diff options
Diffstat (limited to 'drivers')
1835 files changed, 159053 insertions, 51348 deletions
diff --git a/drivers/Makefile b/drivers/Makefile index bc4205d2fc3c..6ee53c7a57a1 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -11,6 +11,7 @@ obj-$(CONFIG_PARISC) += parisc/ obj-$(CONFIG_RAPIDIO) += rapidio/ obj-y += video/ obj-$(CONFIG_ACPI) += acpi/ +obj-$(CONFIG_SFI) += sfi/ # PnP must come after ACPI since it will eventually need to check if acpi # was used and do nothing if so obj-$(CONFIG_PNP) += pnp/ @@ -42,6 +43,8 @@ obj-y += macintosh/ obj-$(CONFIG_IDE) += ide/ obj-$(CONFIG_SCSI) += scsi/ obj-$(CONFIG_ATA) += ata/ +obj-$(CONFIG_MTD) += mtd/ +obj-$(CONFIG_SPI) += spi/ obj-y += net/ obj-$(CONFIG_ATM) += atm/ obj-$(CONFIG_FUSION) += message/ @@ -50,8 +53,6 @@ obj-y += ieee1394/ obj-$(CONFIG_UIO) += uio/ obj-y += cdrom/ obj-y += auxdisplay/ -obj-$(CONFIG_MTD) += mtd/ -obj-$(CONFIG_SPI) += spi/ obj-$(CONFIG_PCCARD) += pcmcia/ obj-$(CONFIG_DIO) += dio/ obj-$(CONFIG_SBUS) += sbus/ diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig index 7ec7d88c5999..93d2c7971df6 100644 --- a/drivers/acpi/Kconfig +++ b/drivers/acpi/Kconfig @@ -60,7 +60,11 @@ config ACPI_PROCFS /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) - + /proc/acpi/processor/*/power (/sys/devices/system/cpu/*/cpuidle/*) + /proc/acpi/processor/*/performance (/sys/devices/system/cpu/*/ + cpufreq/*) + /proc/acpi/processor/*/throttling (/sys/class/thermal/ + cooling_device*/*) This option has no effect on /proc/acpi/ files and functions which do not yet exist in /sys. @@ -82,6 +86,17 @@ config ACPI_PROCFS_POWER Say N to delete power /proc/acpi/ directories that have moved to /sys/ +config ACPI_POWER_METER + tristate "ACPI 4.0 power meter" + depends on HWMON + help + This driver exposes ACPI 4.0 power meters as hardware monitoring + devices. Say Y (or M) if you have a computer with ACPI 4.0 firmware + and a power meter. + + To compile this driver as a module, choose M here: + the module will be called power-meter. + config ACPI_SYSFS_POWER bool "Future power /sys interface" select POWER_SUPPLY @@ -196,6 +211,18 @@ config ACPI_HOTPLUG_CPU select ACPI_CONTAINER default y +config ACPI_PROCESSOR_AGGREGATOR + tristate "Processor Aggregator" + depends on ACPI_PROCESSOR + depends on EXPERIMENTAL + depends on X86 + help + ACPI 4.0 defines processor Aggregator, which enables OS to perform + specific processor configuration and control that applies to all + processors in the platform. Currently only logical processor idling + is defined, which is to reduce power consumption. This driver + supports the new device. + config ACPI_THERMAL tristate "Thermal Zone" depends on ACPI_PROCESSOR diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile index 03a985be3fe3..7702118509a0 100644 --- a/drivers/acpi/Makefile +++ b/drivers/acpi/Makefile @@ -56,8 +56,11 @@ obj-$(CONFIG_ACPI_HOTPLUG_MEMORY) += acpi_memhotplug.o obj-$(CONFIG_ACPI_BATTERY) += battery.o obj-$(CONFIG_ACPI_SBS) += sbshc.o obj-$(CONFIG_ACPI_SBS) += sbs.o +obj-$(CONFIG_ACPI_POWER_METER) += power_meter.o # processor has its own "processor." module_param namespace processor-y := processor_core.o processor_throttling.o processor-y += processor_idle.o processor_thermal.o processor-$(CONFIG_CPU_FREQ) += processor_perflib.o + +obj-$(CONFIG_ACPI_PROCESSOR_AGGREGATOR) += acpi_pad.o diff --git a/drivers/acpi/ac.c b/drivers/acpi/ac.c index 0df8fcb687d6..b6ed60b57b0d 100644 --- a/drivers/acpi/ac.c +++ b/drivers/acpi/ac.c @@ -37,6 +37,8 @@ #include <acpi/acpi_bus.h> #include <acpi/acpi_drivers.h> +#define PREFIX "ACPI: " + #define ACPI_AC_CLASS "ac_adapter" #define ACPI_AC_DEVICE_NAME "AC Adapter" #define ACPI_AC_FILE_STATE "state" @@ -243,6 +245,7 @@ static void acpi_ac_notify(struct acpi_device *device, u32 event) acpi_bus_generate_netlink_event(device->pnp.device_class, dev_name(&device->dev), event, (u32) ac->state); + acpi_notifier_call_chain(device, event, (u32) ac->state); #ifdef CONFIG_ACPI_SYSFS_POWER kobject_uevent(&ac->charger.dev->kobj, KOBJ_CHANGE); #endif diff --git a/drivers/acpi/acpi_memhotplug.c b/drivers/acpi/acpi_memhotplug.c index 9a62224cc278..28ccdbc05ac8 100644 --- a/drivers/acpi/acpi_memhotplug.c +++ b/drivers/acpi/acpi_memhotplug.c @@ -53,7 +53,6 @@ MODULE_LICENSE("GPL"); static int acpi_memory_device_add(struct acpi_device *device); static int acpi_memory_device_remove(struct acpi_device *device, int type); -static int acpi_memory_device_start(struct acpi_device *device); static const struct acpi_device_id memory_device_ids[] = { {ACPI_MEMORY_DEVICE_HID, 0}, @@ -68,7 +67,6 @@ static struct acpi_driver acpi_memory_device_driver = { .ops = { .add = acpi_memory_device_add, .remove = acpi_memory_device_remove, - .start = acpi_memory_device_start, }, }; @@ -431,28 +429,6 @@ static int acpi_memory_device_add(struct acpi_device *device) printk(KERN_DEBUG "%s \n", acpi_device_name(device)); - return result; -} - -static int acpi_memory_device_remove(struct acpi_device *device, int type) -{ - struct acpi_memory_device *mem_device = NULL; - - - if (!device || !acpi_driver_data(device)) - return -EINVAL; - - mem_device = acpi_driver_data(device); - kfree(mem_device); - - return 0; -} - -static int acpi_memory_device_start (struct acpi_device *device) -{ - struct acpi_memory_device *mem_device; - int result = 0; - /* * Early boot code has recognized memory area by EFI/E820. * If DSDT shows these memory devices on boot, hotplug is not necessary @@ -462,8 +438,6 @@ static int acpi_memory_device_start (struct acpi_device *device) if (!acpi_hotmem_initialized) return 0; - mem_device = acpi_driver_data(device); - if (!acpi_memory_check_device(mem_device)) { /* call add_memory func */ result = acpi_memory_enable_device(mem_device); @@ -474,6 +448,20 @@ static int acpi_memory_device_start (struct acpi_device *device) return result; } +static int acpi_memory_device_remove(struct acpi_device *device, int type) +{ + struct acpi_memory_device *mem_device = NULL; + + + if (!device || !acpi_driver_data(device)) + return -EINVAL; + + mem_device = acpi_driver_data(device); + kfree(mem_device); + + return 0; +} + /* * Helper function to check for memory device */ @@ -481,26 +469,23 @@ static acpi_status is_memory_device(acpi_handle handle) { char *hardware_id; acpi_status status; - struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; struct acpi_device_info *info; - - status = acpi_get_object_info(handle, &buffer); + status = acpi_get_object_info(handle, &info); if (ACPI_FAILURE(status)) return status; - info = buffer.pointer; if (!(info->valid & ACPI_VALID_HID)) { - kfree(buffer.pointer); + kfree(info); return AE_ERROR; } - hardware_id = info->hardware_id.value; + hardware_id = info->hardware_id.string; if ((hardware_id == NULL) || (strcmp(hardware_id, ACPI_MEMORY_DEVICE_HID))) status = AE_ERROR; - kfree(buffer.pointer); + kfree(info); return status; } diff --git a/drivers/acpi/acpi_pad.c b/drivers/acpi/acpi_pad.c new file mode 100644 index 000000000000..0d2cdb86158b --- /dev/null +++ b/drivers/acpi/acpi_pad.c @@ -0,0 +1,514 @@ +/* + * acpi_pad.c ACPI Processor Aggregator Driver + * + * Copyright (c) 2009, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <linux/kernel.h> +#include <linux/cpumask.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/types.h> +#include <linux/kthread.h> +#include <linux/freezer.h> +#include <linux/cpu.h> +#include <linux/clockchips.h> +#include <acpi/acpi_bus.h> +#include <acpi/acpi_drivers.h> + +#define ACPI_PROCESSOR_AGGREGATOR_CLASS "processor_aggregator" +#define ACPI_PROCESSOR_AGGREGATOR_DEVICE_NAME "Processor Aggregator" +#define ACPI_PROCESSOR_AGGREGATOR_NOTIFY 0x80 +static DEFINE_MUTEX(isolated_cpus_lock); + +#define MWAIT_SUBSTATE_MASK (0xf) +#define MWAIT_CSTATE_MASK (0xf) +#define MWAIT_SUBSTATE_SIZE (4) +#define CPUID_MWAIT_LEAF (5) +#define CPUID5_ECX_EXTENSIONS_SUPPORTED (0x1) +#define CPUID5_ECX_INTERRUPT_BREAK (0x2) +static unsigned long power_saving_mwait_eax; +static void power_saving_mwait_init(void) +{ + unsigned int eax, ebx, ecx, edx; + unsigned int highest_cstate = 0; + unsigned int highest_subcstate = 0; + int i; + + if (!boot_cpu_has(X86_FEATURE_MWAIT)) + return; + if (boot_cpu_data.cpuid_level < CPUID_MWAIT_LEAF) + return; + + cpuid(CPUID_MWAIT_LEAF, &eax, &ebx, &ecx, &edx); + + if (!(ecx & CPUID5_ECX_EXTENSIONS_SUPPORTED) || + !(ecx & CPUID5_ECX_INTERRUPT_BREAK)) + return; + + edx >>= MWAIT_SUBSTATE_SIZE; + for (i = 0; i < 7 && edx; i++, edx >>= MWAIT_SUBSTATE_SIZE) { + if (edx & MWAIT_SUBSTATE_MASK) { + highest_cstate = i; + highest_subcstate = edx & MWAIT_SUBSTATE_MASK; + } + } + power_saving_mwait_eax = (highest_cstate << MWAIT_SUBSTATE_SIZE) | + (highest_subcstate - 1); + + for_each_online_cpu(i) + clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_ON, &i); + +#if defined(CONFIG_GENERIC_TIME) && defined(CONFIG_X86) + switch (boot_cpu_data.x86_vendor) { + case X86_VENDOR_AMD: + case X86_VENDOR_INTEL: + /* + * AMD Fam10h TSC will tick in all + * C/P/S0/S1 states when this bit is set. + */ + if (boot_cpu_has(X86_FEATURE_NONSTOP_TSC)) + return; + + /*FALL THROUGH*/ + default: + /* TSC could halt in idle, so notify users */ + mark_tsc_unstable("TSC halts in idle"); + } +#endif +} + +static unsigned long cpu_weight[NR_CPUS]; +static int tsk_in_cpu[NR_CPUS] = {[0 ... NR_CPUS-1] = -1}; +static DECLARE_BITMAP(pad_busy_cpus_bits, NR_CPUS); +static void round_robin_cpu(unsigned int tsk_index) +{ + struct cpumask *pad_busy_cpus = to_cpumask(pad_busy_cpus_bits); + cpumask_var_t tmp; + int cpu; + unsigned long min_weight = -1, preferred_cpu; + + if (!alloc_cpumask_var(&tmp, GFP_KERNEL)) + return; + + mutex_lock(&isolated_cpus_lock); + cpumask_clear(tmp); + for_each_cpu(cpu, pad_busy_cpus) + cpumask_or(tmp, tmp, topology_thread_cpumask(cpu)); + cpumask_andnot(tmp, cpu_online_mask, tmp); + /* avoid HT sibilings if possible */ + if (cpumask_empty(tmp)) + cpumask_andnot(tmp, cpu_online_mask, pad_busy_cpus); + if (cpumask_empty(tmp)) { + mutex_unlock(&isolated_cpus_lock); + return; + } + for_each_cpu(cpu, tmp) { + if (cpu_weight[cpu] < min_weight) { + min_weight = cpu_weight[cpu]; + preferred_cpu = cpu; + } + } + + if (tsk_in_cpu[tsk_index] != -1) + cpumask_clear_cpu(tsk_in_cpu[tsk_index], pad_busy_cpus); + tsk_in_cpu[tsk_index] = preferred_cpu; + cpumask_set_cpu(preferred_cpu, pad_busy_cpus); + cpu_weight[preferred_cpu]++; + mutex_unlock(&isolated_cpus_lock); + + set_cpus_allowed_ptr(current, cpumask_of(preferred_cpu)); +} + +static void exit_round_robin(unsigned int tsk_index) +{ + struct cpumask *pad_busy_cpus = to_cpumask(pad_busy_cpus_bits); + cpumask_clear_cpu(tsk_in_cpu[tsk_index], pad_busy_cpus); + tsk_in_cpu[tsk_index] = -1; +} + +static unsigned int idle_pct = 5; /* percentage */ +static unsigned int round_robin_time = 10; /* second */ +static int power_saving_thread(void *data) +{ + struct sched_param param = {.sched_priority = 1}; + int do_sleep; + unsigned int tsk_index = (unsigned long)data; + u64 last_jiffies = 0; + + sched_setscheduler(current, SCHED_RR, ¶m); + + while (!kthread_should_stop()) { + int cpu; + u64 expire_time; + + try_to_freeze(); + + /* round robin to cpus */ + if (last_jiffies + round_robin_time * HZ < jiffies) { + last_jiffies = jiffies; + round_robin_cpu(tsk_index); + } + + do_sleep = 0; + + current_thread_info()->status &= ~TS_POLLING; + /* + * TS_POLLING-cleared state must be visible before we test + * NEED_RESCHED: + */ + smp_mb(); + + expire_time = jiffies + HZ * (100 - idle_pct) / 100; + + while (!need_resched()) { + local_irq_disable(); + cpu = smp_processor_id(); + clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_ENTER, + &cpu); + stop_critical_timings(); + + __monitor((void *)¤t_thread_info()->flags, 0, 0); + smp_mb(); + if (!need_resched()) + __mwait(power_saving_mwait_eax, 1); + + start_critical_timings(); + clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_EXIT, + &cpu); + local_irq_enable(); + + if (jiffies > expire_time) { + do_sleep = 1; + break; + } + } + + current_thread_info()->status |= TS_POLLING; + + /* + * current sched_rt has threshold for rt task running time. + * When a rt task uses 95% CPU time, the rt thread will be + * scheduled out for 5% CPU time to not starve other tasks. But + * the mechanism only works when all CPUs have RT task running, + * as if one CPU hasn't RT task, RT task from other CPUs will + * borrow CPU time from this CPU and cause RT task use > 95% + * CPU time. To make 'avoid staration' work, takes a nap here. + */ + if (do_sleep) + schedule_timeout_killable(HZ * idle_pct / 100); + } + + exit_round_robin(tsk_index); + return 0; +} + +static struct task_struct *ps_tsks[NR_CPUS]; +static unsigned int ps_tsk_num; +static int create_power_saving_task(void) +{ + ps_tsks[ps_tsk_num] = kthread_run(power_saving_thread, + (void *)(unsigned long)ps_tsk_num, + "power_saving/%d", ps_tsk_num); + if (ps_tsks[ps_tsk_num]) { + ps_tsk_num++; + return 0; + } + return -EINVAL; +} + +static void destroy_power_saving_task(void) +{ + if (ps_tsk_num > 0) { + ps_tsk_num--; + kthread_stop(ps_tsks[ps_tsk_num]); + } +} + +static void set_power_saving_task_num(unsigned int num) +{ + if (num > ps_tsk_num) { + while (ps_tsk_num < num) { + if (create_power_saving_task()) + return; + } + } else if (num < ps_tsk_num) { + while (ps_tsk_num > num) + destroy_power_saving_task(); + } +} + +static int acpi_pad_idle_cpus(unsigned int num_cpus) +{ + get_online_cpus(); + + num_cpus = min_t(unsigned int, num_cpus, num_online_cpus()); + set_power_saving_task_num(num_cpus); + + put_online_cpus(); + return 0; +} + +static uint32_t acpi_pad_idle_cpus_num(void) +{ + return ps_tsk_num; +} + +static ssize_t acpi_pad_rrtime_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + unsigned long num; + if (strict_strtoul(buf, 0, &num)) + return -EINVAL; + if (num < 1 || num >= 100) + return -EINVAL; + mutex_lock(&isolated_cpus_lock); + round_robin_time = num; + mutex_unlock(&isolated_cpus_lock); + return count; +} + +static ssize_t acpi_pad_rrtime_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return scnprintf(buf, PAGE_SIZE, "%d", round_robin_time); +} +static DEVICE_ATTR(rrtime, S_IRUGO|S_IWUSR, + acpi_pad_rrtime_show, + acpi_pad_rrtime_store); + +static ssize_t acpi_pad_idlepct_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + unsigned long num; + if (strict_strtoul(buf, 0, &num)) + return -EINVAL; + if (num < 1 || num >= 100) + return -EINVAL; + mutex_lock(&isolated_cpus_lock); + idle_pct = num; + mutex_unlock(&isolated_cpus_lock); + return count; +} + +static ssize_t acpi_pad_idlepct_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return scnprintf(buf, PAGE_SIZE, "%d", idle_pct); +} +static DEVICE_ATTR(idlepct, S_IRUGO|S_IWUSR, + acpi_pad_idlepct_show, + acpi_pad_idlepct_store); + +static ssize_t acpi_pad_idlecpus_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + unsigned long num; + if (strict_strtoul(buf, 0, &num)) + return -EINVAL; + mutex_lock(&isolated_cpus_lock); + acpi_pad_idle_cpus(num); + mutex_unlock(&isolated_cpus_lock); + return count; +} + +static ssize_t acpi_pad_idlecpus_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return cpumask_scnprintf(buf, PAGE_SIZE, + to_cpumask(pad_busy_cpus_bits)); +} +static DEVICE_ATTR(idlecpus, S_IRUGO|S_IWUSR, + acpi_pad_idlecpus_show, + acpi_pad_idlecpus_store); + +static int acpi_pad_add_sysfs(struct acpi_device *device) +{ + int result; + + result = device_create_file(&device->dev, &dev_attr_idlecpus); + if (result) + return -ENODEV; + result = device_create_file(&device->dev, &dev_attr_idlepct); + if (result) { + device_remove_file(&device->dev, &dev_attr_idlecpus); + return -ENODEV; + } + result = device_create_file(&device->dev, &dev_attr_rrtime); + if (result) { + device_remove_file(&device->dev, &dev_attr_idlecpus); + device_remove_file(&device->dev, &dev_attr_idlepct); + return -ENODEV; + } + return 0; +} + +static void acpi_pad_remove_sysfs(struct acpi_device *device) +{ + device_remove_file(&device->dev, &dev_attr_idlecpus); + device_remove_file(&device->dev, &dev_attr_idlepct); + device_remove_file(&device->dev, &dev_attr_rrtime); +} + +/* Query firmware how many CPUs should be idle */ +static int acpi_pad_pur(acpi_handle handle, int *num_cpus) +{ + struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL}; + acpi_status status; + union acpi_object *package; + int rev, num, ret = -EINVAL; + + status = acpi_evaluate_object(handle, "_PUR", NULL, &buffer); + if (ACPI_FAILURE(status)) + return -EINVAL; + package = buffer.pointer; + if (package->type != ACPI_TYPE_PACKAGE || package->package.count != 2) + goto out; + rev = package->package.elements[0].integer.value; + num = package->package.elements[1].integer.value; + if (rev != 1) + goto out; + *num_cpus = num; + ret = 0; +out: + kfree(buffer.pointer); + return ret; +} + +/* Notify firmware how many CPUs are idle */ +static void acpi_pad_ost(acpi_handle handle, int stat, + uint32_t idle_cpus) +{ + union acpi_object params[3] = { + {.type = ACPI_TYPE_INTEGER,}, + {.type = ACPI_TYPE_INTEGER,}, + {.type = ACPI_TYPE_BUFFER,}, + }; + struct acpi_object_list arg_list = {3, params}; + + params[0].integer.value = ACPI_PROCESSOR_AGGREGATOR_NOTIFY; + params[1].integer.value = stat; + params[2].buffer.length = 4; + params[2].buffer.pointer = (void *)&idle_cpus; + acpi_evaluate_object(handle, "_OST", &arg_list, NULL); +} + +static void acpi_pad_handle_notify(acpi_handle handle) +{ + int num_cpus, ret; + uint32_t idle_cpus; + + mutex_lock(&isolated_cpus_lock); + if (acpi_pad_pur(handle, &num_cpus)) { + mutex_unlock(&isolated_cpus_lock); + return; + } + ret = acpi_pad_idle_cpus(num_cpus); + idle_cpus = acpi_pad_idle_cpus_num(); + if (!ret) + acpi_pad_ost(handle, 0, idle_cpus); + else + acpi_pad_ost(handle, 1, 0); + mutex_unlock(&isolated_cpus_lock); +} + +static void acpi_pad_notify(acpi_handle handle, u32 event, + void *data) +{ + struct acpi_device *device = data; + + switch (event) { + case ACPI_PROCESSOR_AGGREGATOR_NOTIFY: + acpi_pad_handle_notify(handle); + acpi_bus_generate_proc_event(device, event, 0); + acpi_bus_generate_netlink_event(device->pnp.device_class, + dev_name(&device->dev), event, 0); + break; + default: + printk(KERN_WARNING"Unsupported event [0x%x]\n", event); + break; + } +} + +static int acpi_pad_add(struct acpi_device *device) +{ + acpi_status status; + + strcpy(acpi_device_name(device), ACPI_PROCESSOR_AGGREGATOR_DEVICE_NAME); + strcpy(acpi_device_class(device), ACPI_PROCESSOR_AGGREGATOR_CLASS); + + if (acpi_pad_add_sysfs(device)) + return -ENODEV; + + status = acpi_install_notify_handler(device->handle, + ACPI_DEVICE_NOTIFY, acpi_pad_notify, device); + if (ACPI_FAILURE(status)) { + acpi_pad_remove_sysfs(device); + return -ENODEV; + } + + return 0; +} + +static int acpi_pad_remove(struct acpi_device *device, + int type) +{ + mutex_lock(&isolated_cpus_lock); + acpi_pad_idle_cpus(0); + mutex_unlock(&isolated_cpus_lock); + + acpi_remove_notify_handler(device->handle, + ACPI_DEVICE_NOTIFY, acpi_pad_notify); + acpi_pad_remove_sysfs(device); + return 0; +} + +static const struct acpi_device_id pad_device_ids[] = { + {"ACPI000C", 0}, + {"", 0}, +}; +MODULE_DEVICE_TABLE(acpi, pad_device_ids); + +static struct acpi_driver acpi_pad_driver = { + .name = "processor_aggregator", + .class = ACPI_PROCESSOR_AGGREGATOR_CLASS, + .ids = pad_device_ids, + .ops = { + .add = acpi_pad_add, + .remove = acpi_pad_remove, + }, +}; + +static int __init acpi_pad_init(void) +{ + power_saving_mwait_init(); + if (power_saving_mwait_eax == 0) + return -EINVAL; + + return acpi_bus_register_driver(&acpi_pad_driver); +} + +static void __exit acpi_pad_exit(void) +{ + acpi_bus_unregister_driver(&acpi_pad_driver); +} + +module_init(acpi_pad_init); +module_exit(acpi_pad_exit); +MODULE_AUTHOR("Shaohua Li<shaohua.li@intel.com>"); +MODULE_DESCRIPTION("ACPI Processor Aggregator Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/acpi/acpica/Makefile b/drivers/acpi/acpica/Makefile index 72ac28da14e3..e7973bc16846 100644 --- a/drivers/acpi/acpica/Makefile +++ b/drivers/acpi/acpica/Makefile @@ -28,7 +28,7 @@ acpi-$(ACPI_FUTURE_USAGE) += hwtimer.o acpi-y += nsaccess.o nsload.o nssearch.o nsxfeval.o \ nsalloc.o nseval.o nsnames.o nsutils.o nsxfname.o \ nsdump.o nsinit.o nsobject.o nswalk.o nsxfobj.o \ - nsparse.o nspredef.o + nsparse.o nspredef.o nsrepair.o acpi-$(ACPI_FUTURE_USAGE) += nsdumpdv.o @@ -44,4 +44,4 @@ acpi-y += tbxface.o tbinstal.o tbutils.o tbfind.o tbfadt.o tbxfroot.o acpi-y += utalloc.o utdebug.o uteval.o utinit.o utmisc.o utxface.o \ utcopy.o utdelete.o utglobal.o utmath.o utobject.o \ - utstate.o utmutex.o utobject.o utresrc.o utlock.o + utstate.o utmutex.o utobject.o utresrc.o utlock.o utids.o diff --git a/drivers/acpi/acpica/acconfig.h b/drivers/acpi/acpica/acconfig.h index e6777fb883d2..a4471e3d3853 100644 --- a/drivers/acpi/acpica/acconfig.h +++ b/drivers/acpi/acpica/acconfig.h @@ -103,9 +103,9 @@ #define ACPI_MAX_REFERENCE_COUNT 0x1000 -/* Size of cached memory mapping for system memory operation region */ +/* Default page size for use in mapping memory for operation regions */ -#define ACPI_SYSMEM_REGION_WINDOW_SIZE 4096 +#define ACPI_DEFAULT_PAGE_SIZE 4096 /* Must be power of 2 */ /* owner_id tracking. 8 entries allows for 255 owner_ids */ @@ -183,7 +183,7 @@ /* Operation regions */ -#define ACPI_NUM_PREDEFINED_REGIONS 8 +#define ACPI_NUM_PREDEFINED_REGIONS 9 #define ACPI_USER_REGION_BEGIN 0x80 /* Maximum space_ids for Operation Regions */ @@ -199,9 +199,15 @@ #define ACPI_RSDP_CHECKSUM_LENGTH 20 #define ACPI_RSDP_XCHECKSUM_LENGTH 36 -/* SMBus bidirectional buffer size */ +/* SMBus and IPMI bidirectional buffer size */ #define ACPI_SMBUS_BUFFER_SIZE 34 +#define ACPI_IPMI_BUFFER_SIZE 66 + +/* _sx_d and _sx_w control methods */ + +#define ACPI_NUM_sx_d_METHODS 4 +#define ACPI_NUM_sx_w_METHODS 5 /****************************************************************************** * diff --git a/drivers/acpi/acpica/acdebug.h b/drivers/acpi/acpica/acdebug.h index 62c59df3b86c..a4fb001d96f1 100644 --- a/drivers/acpi/acpica/acdebug.h +++ b/drivers/acpi/acpica/acdebug.h @@ -154,10 +154,6 @@ void acpi_db_display_argument_object(union acpi_operand_object *obj_desc, struct acpi_walk_state *walk_state); -void acpi_db_check_predefined_names(void); - -void acpi_db_batch_execute(void); - /* * dbexec - debugger control method execution */ diff --git a/drivers/acpi/acpica/acglobal.h b/drivers/acpi/acpica/acglobal.h index 3d87362d17ed..29ba66d5a790 100644 --- a/drivers/acpi/acpica/acglobal.h +++ b/drivers/acpi/acpica/acglobal.h @@ -58,6 +58,10 @@ #define ACPI_INIT_GLOBAL(a,b) a #endif +#ifdef DEFINE_ACPI_GLOBALS + +/* Public globals, available from outside ACPICA subsystem */ + /***************************************************************************** * * Runtime configuration (static defaults that can be overriden at runtime) @@ -78,7 +82,7 @@ * 5) Allow unresolved references (invalid target name) in package objects * 6) Enable warning messages for behavior that is not ACPI spec compliant */ -ACPI_EXTERN u8 ACPI_INIT_GLOBAL(acpi_gbl_enable_interpreter_slack, FALSE); +u8 ACPI_INIT_GLOBAL(acpi_gbl_enable_interpreter_slack, FALSE); /* * Automatically serialize ALL control methods? Default is FALSE, meaning @@ -86,27 +90,36 @@ ACPI_EXTERN u8 ACPI_INIT_GLOBAL(acpi_gbl_enable_interpreter_slack, FALSE); * Only change this if the ASL code is poorly written and cannot handle * reentrancy even though methods are marked "NotSerialized". */ -ACPI_EXTERN u8 ACPI_INIT_GLOBAL(acpi_gbl_all_methods_serialized, FALSE); +u8 ACPI_INIT_GLOBAL(acpi_gbl_all_methods_serialized, FALSE); /* * Create the predefined _OSI method in the namespace? Default is TRUE * because ACPI CA is fully compatible with other ACPI implementations. * Changing this will revert ACPI CA (and machine ASL) to pre-OSI behavior. */ -ACPI_EXTERN u8 ACPI_INIT_GLOBAL(acpi_gbl_create_osi_method, TRUE); +u8 ACPI_INIT_GLOBAL(acpi_gbl_create_osi_method, TRUE); /* * Disable wakeup GPEs during runtime? Default is TRUE because WAKE and * RUNTIME GPEs should never be shared, and WAKE GPEs should typically only * be enabled just before going to sleep. */ -ACPI_EXTERN u8 ACPI_INIT_GLOBAL(acpi_gbl_leave_wake_gpes_disabled, TRUE); +u8 ACPI_INIT_GLOBAL(acpi_gbl_leave_wake_gpes_disabled, TRUE); /* * Optionally use default values for the ACPI register widths. Set this to * TRUE to use the defaults, if an FADT contains incorrect widths/lengths. */ -ACPI_EXTERN u8 ACPI_INIT_GLOBAL(acpi_gbl_use_default_register_widths, TRUE); +u8 ACPI_INIT_GLOBAL(acpi_gbl_use_default_register_widths, TRUE); + +/* acpi_gbl_FADT is a local copy of the FADT, converted to a common format. */ + +struct acpi_table_fadt acpi_gbl_FADT; +u32 acpi_current_gpe_count; +u32 acpi_gbl_trace_flags; +acpi_name acpi_gbl_trace_method_name; + +#endif /***************************************************************************** * @@ -114,11 +127,6 @@ ACPI_EXTERN u8 ACPI_INIT_GLOBAL(acpi_gbl_use_default_register_widths, TRUE); * ****************************************************************************/ -/* Runtime configuration of debug print levels */ - -extern u32 acpi_dbg_level; -extern u32 acpi_dbg_layer; - /* Procedure nesting level for debug output */ extern u32 acpi_gbl_nesting_level; @@ -127,10 +135,8 @@ extern u32 acpi_gbl_nesting_level; ACPI_EXTERN u32 acpi_gbl_original_dbg_level; ACPI_EXTERN u32 acpi_gbl_original_dbg_layer; -ACPI_EXTERN acpi_name acpi_gbl_trace_method_name; ACPI_EXTERN u32 acpi_gbl_trace_dbg_level; ACPI_EXTERN u32 acpi_gbl_trace_dbg_layer; -ACPI_EXTERN u32 acpi_gbl_trace_flags; /***************************************************************************** * @@ -142,10 +148,8 @@ ACPI_EXTERN u32 acpi_gbl_trace_flags; * acpi_gbl_root_table_list is the master list of ACPI tables found in the * RSDT/XSDT. * - * acpi_gbl_FADT is a local copy of the FADT, converted to a common format. */ ACPI_EXTERN struct acpi_internal_rsdt acpi_gbl_root_table_list; -ACPI_EXTERN struct acpi_table_fadt acpi_gbl_FADT; ACPI_EXTERN struct acpi_table_facs *acpi_gbl_FACS; /* These addresses are calculated from the FADT Event Block addresses */ @@ -261,7 +265,8 @@ ACPI_EXTERN u8 acpi_gbl_osi_data; extern u8 acpi_gbl_shutdown; extern u32 acpi_gbl_startup_flags; extern const char *acpi_gbl_sleep_state_names[ACPI_S_STATE_COUNT]; -extern const char *acpi_gbl_highest_dstate_names[4]; +extern const char *acpi_gbl_lowest_dstate_names[ACPI_NUM_sx_w_METHODS]; +extern const char *acpi_gbl_highest_dstate_names[ACPI_NUM_sx_d_METHODS]; extern const struct acpi_opcode_info acpi_gbl_aml_op_info[AML_NUM_OPCODES]; extern const char *acpi_gbl_region_types[ACPI_NUM_PREDEFINED_REGIONS]; @@ -290,6 +295,7 @@ extern char const *acpi_gbl_exception_names_ctrl[]; ACPI_EXTERN struct acpi_namespace_node acpi_gbl_root_node_struct; ACPI_EXTERN struct acpi_namespace_node *acpi_gbl_root_node; ACPI_EXTERN struct acpi_namespace_node *acpi_gbl_fadt_gpe_device; +ACPI_EXTERN union acpi_operand_object *acpi_gbl_module_code_list; extern const u8 acpi_gbl_ns_properties[ACPI_NUM_NS_TYPES]; extern const struct acpi_predefined_names @@ -340,7 +346,6 @@ ACPI_EXTERN struct acpi_fixed_event_handler ACPI_EXTERN struct acpi_gpe_xrupt_info *acpi_gbl_gpe_xrupt_list_head; ACPI_EXTERN struct acpi_gpe_block_info *acpi_gbl_gpe_fadt_blocks[ACPI_MAX_GPE_BLOCKS]; -ACPI_EXTERN u32 acpi_current_gpe_count; /***************************************************************************** * diff --git a/drivers/acpi/acpica/achware.h b/drivers/acpi/acpica/achware.h index 4afa3d8e0efb..36192f142fbb 100644 --- a/drivers/acpi/acpica/achware.h +++ b/drivers/acpi/acpica/achware.h @@ -62,6 +62,14 @@ u32 acpi_hw_get_mode(void); /* * hwregs - ACPI Register I/O */ +acpi_status +acpi_hw_validate_register(struct acpi_generic_address *reg, + u8 max_bit_width, u64 *address); + +acpi_status acpi_hw_read(u32 *value, struct acpi_generic_address *reg); + +acpi_status acpi_hw_write(u32 value, struct acpi_generic_address *reg); + struct acpi_bit_register_info *acpi_hw_get_bit_register_info(u32 register_id); acpi_status acpi_hw_write_pm1_control(u32 pm1a_control, u32 pm1b_control); diff --git a/drivers/acpi/acpica/acinterp.h b/drivers/acpi/acpica/acinterp.h index e8db7a3143a5..5db9f2916f7c 100644 --- a/drivers/acpi/acpica/acinterp.h +++ b/drivers/acpi/acpica/acinterp.h @@ -461,9 +461,9 @@ void acpi_ex_acquire_global_lock(u32 rule); void acpi_ex_release_global_lock(u32 rule); -void acpi_ex_eisa_id_to_string(u32 numeric_id, char *out_string); +void acpi_ex_eisa_id_to_string(char *dest, acpi_integer compressed_id); -void acpi_ex_unsigned_integer_to_string(acpi_integer value, char *out_string); +void acpi_ex_integer_to_string(char *dest, acpi_integer value); /* * exregion - default op_region handlers diff --git a/drivers/acpi/acpica/aclocal.h b/drivers/acpi/acpica/aclocal.h index ee986edfa0da..81e64f478679 100644 --- a/drivers/acpi/acpica/aclocal.h +++ b/drivers/acpi/acpica/aclocal.h @@ -369,6 +369,19 @@ union acpi_predefined_info { struct acpi_package_info3 ret_info3; }; +/* Data block used during object validation */ + +struct acpi_predefined_data { + char *pathname; + const union acpi_predefined_info *predefined; + u32 flags; + u8 node_flags; +}; + +/* Defines for Flags field above */ + +#define ACPI_OBJECT_REPAIRED 1 + /* * Bitmapped return value types * Note: the actual data types must be contiguous, a loop in nspredef.c @@ -885,6 +898,9 @@ struct acpi_bit_register_info { #define ACPI_OSI_WIN_XP_SP2 0x05 #define ACPI_OSI_WINSRV_2003_SP1 0x06 #define ACPI_OSI_WIN_VISTA 0x07 +#define ACPI_OSI_WINSRV_2008 0x08 +#define ACPI_OSI_WIN_VISTA_SP1 0x09 +#define ACPI_OSI_WIN_7 0x0A #define ACPI_ALWAYS_ILLEGAL 0x00 diff --git a/drivers/acpi/acpica/acmacros.h b/drivers/acpi/acpica/acmacros.h index 91ac7d7b4402..3acd9c6760ea 100644 --- a/drivers/acpi/acpica/acmacros.h +++ b/drivers/acpi/acpica/acmacros.h @@ -340,6 +340,7 @@ */ #define ACPI_ERROR_NAMESPACE(s, e) acpi_ns_report_error (AE_INFO, s, e); #define ACPI_ERROR_METHOD(s, n, p, e) acpi_ns_report_method_error (AE_INFO, s, n, p, e); +#define ACPI_WARN_PREDEFINED(plist) acpi_ut_predefined_warning plist #else @@ -347,6 +348,7 @@ #define ACPI_ERROR_NAMESPACE(s, e) #define ACPI_ERROR_METHOD(s, n, p, e) +#define ACPI_WARN_PREDEFINED(plist) #endif /* ACPI_NO_ERROR_MESSAGES */ /* diff --git a/drivers/acpi/acpica/acnamesp.h b/drivers/acpi/acpica/acnamesp.h index 94cdc2b8cb93..09a2764c734b 100644 --- a/drivers/acpi/acpica/acnamesp.h +++ b/drivers/acpi/acpica/acnamesp.h @@ -73,6 +73,14 @@ #define ACPI_NS_WALK_UNLOCK 0x01 #define ACPI_NS_WALK_TEMP_NODES 0x02 +/* Object is not a package element */ + +#define ACPI_NOT_PACKAGE_ELEMENT ACPI_UINT32_MAX + +/* Always emit warning message, not dependent on node flags */ + +#define ACPI_WARN_ALWAYS 0 + /* * nsinit - Namespace initialization */ @@ -144,6 +152,8 @@ struct acpi_namespace_node *acpi_ns_create_node(u32 name); void acpi_ns_delete_node(struct acpi_namespace_node *node); +void acpi_ns_remove_node(struct acpi_namespace_node *node); + void acpi_ns_delete_namespace_subtree(struct acpi_namespace_node *parent_handle); @@ -186,6 +196,8 @@ acpi_ns_dump_objects(acpi_object_type type, */ acpi_status acpi_ns_evaluate(struct acpi_evaluate_info *info); +void acpi_ns_exec_module_code_list(void); + /* * nspredef - Support for predefined/reserved names */ @@ -260,6 +272,19 @@ acpi_ns_get_attached_data(struct acpi_namespace_node *node, acpi_object_handler handler, void **data); /* + * nsrepair - return object repair for predefined methods/objects + */ +acpi_status +acpi_ns_repair_object(struct acpi_predefined_data *data, + u32 expected_btypes, + u32 package_index, + union acpi_operand_object **return_object_ptr); + +acpi_status +acpi_ns_repair_package_list(struct acpi_predefined_data *data, + union acpi_operand_object **obj_desc_ptr); + +/* * nssearch - Namespace searching and entry */ acpi_status diff --git a/drivers/acpi/acpica/acobject.h b/drivers/acpi/acpica/acobject.h index eb6f038b03d9..b39d682a2140 100644 --- a/drivers/acpi/acpica/acobject.h +++ b/drivers/acpi/acpica/acobject.h @@ -98,6 +98,7 @@ #define AOPOBJ_SETUP_COMPLETE 0x10 #define AOPOBJ_SINGLE_DATUM 0x20 #define AOPOBJ_INVALID 0x40 /* Used if host OS won't allow an op_region address */ +#define AOPOBJ_MODULE_LEVEL 0x80 /****************************************************************************** * diff --git a/drivers/acpi/acpica/acparser.h b/drivers/acpi/acpica/acparser.h index 23ee0fbf5619..22881e8ce229 100644 --- a/drivers/acpi/acpica/acparser.h +++ b/drivers/acpi/acpica/acparser.h @@ -62,6 +62,8 @@ #define ACPI_PARSE_DEFERRED_OP 0x0100 #define ACPI_PARSE_DISASSEMBLE 0x0200 +#define ACPI_PARSE_MODULE_LEVEL 0x0400 + /****************************************************************************** * * Parser interfaces diff --git a/drivers/acpi/acpica/acpredef.h b/drivers/acpi/acpica/acpredef.h index 63f656ae3604..57bdaf6ffab1 100644 --- a/drivers/acpi/acpica/acpredef.h +++ b/drivers/acpi/acpica/acpredef.h @@ -64,8 +64,8 @@ * (Used for _PRW) * * - * 2) PTYPE2 packages contain a variable number of sub-packages. Each of the - * different types describe the contents of each of the sub-packages. + * 2) PTYPE2 packages contain a Variable-length number of sub-packages. Each + * of the different types describe the contents of each of the sub-packages. * * ACPI_PTYPE2: Each subpackage contains 1 or 2 object types: * object type @@ -91,6 +91,9 @@ * ACPI_PTYPE2_MIN: Each subpackage has a variable but minimum length * (Used for _HPX) * + * ACPI_PTYPE2_REV_FIXED: Revision at start, each subpackage is Fixed-length + * (Used for _ART, _FPS) + * *****************************************************************************/ enum acpi_return_package_types { @@ -101,9 +104,11 @@ enum acpi_return_package_types { ACPI_PTYPE2_COUNT = 5, ACPI_PTYPE2_PKG_COUNT = 6, ACPI_PTYPE2_FIXED = 7, - ACPI_PTYPE2_MIN = 8 + ACPI_PTYPE2_MIN = 8, + ACPI_PTYPE2_REV_FIXED = 9 }; +#ifdef ACPI_CREATE_PREDEFINED_TABLE /* * Predefined method/object information table. * @@ -136,239 +141,385 @@ enum acpi_return_package_types { * is saved here (rather than in a separate table) in order to minimize the * overall size of the stored data. */ -static const union acpi_predefined_info predefined_names[] = { - {.info = {"_AC0", 0, ACPI_RTYPE_INTEGER}}, - {.info = {"_AC1", 0, ACPI_RTYPE_INTEGER}}, - {.info = {"_AC2", 0, ACPI_RTYPE_INTEGER}}, - {.info = {"_AC3", 0, ACPI_RTYPE_INTEGER}}, - {.info = {"_AC4", 0, ACPI_RTYPE_INTEGER}}, - {.info = {"_AC5", 0, ACPI_RTYPE_INTEGER}}, - {.info = {"_AC6", 0, ACPI_RTYPE_INTEGER}}, - {.info = {"_AC7", 0, ACPI_RTYPE_INTEGER}}, - {.info = {"_AC8", 0, ACPI_RTYPE_INTEGER}}, - {.info = {"_AC9", 0, ACPI_RTYPE_INTEGER}}, - {.info = {"_ADR", 0, ACPI_RTYPE_INTEGER}}, - {.info = {"_AL0", 0, ACPI_RTYPE_PACKAGE}}, {.ret_info = {ACPI_PTYPE1_VAR, ACPI_RTYPE_REFERENCE, 0, 0, 0, 0}}, /* variable (Refs) */ - {.info = {"_AL1", 0, ACPI_RTYPE_PACKAGE}}, {.ret_info = {ACPI_PTYPE1_VAR, ACPI_RTYPE_REFERENCE, 0, 0, 0, 0}}, /* variable (Refs) */ - {.info = {"_AL2", 0, ACPI_RTYPE_PACKAGE}}, {.ret_info = {ACPI_PTYPE1_VAR, ACPI_RTYPE_REFERENCE, 0, 0, 0, 0}}, /* variable (Refs) */ - {.info = {"_AL3", 0, ACPI_RTYPE_PACKAGE}}, {.ret_info = {ACPI_PTYPE1_VAR, ACPI_RTYPE_REFERENCE, 0, 0, 0, 0}}, /* variable (Refs) */ - {.info = {"_AL4", 0, ACPI_RTYPE_PACKAGE}}, {.ret_info = {ACPI_PTYPE1_VAR, ACPI_RTYPE_REFERENCE, 0, 0, 0, 0}}, /* variable (Refs) */ - {.info = {"_AL5", 0, ACPI_RTYPE_PACKAGE}}, {.ret_info = {ACPI_PTYPE1_VAR, ACPI_RTYPE_REFERENCE, 0, 0, 0, 0}}, /* variable (Refs) */ - {.info = {"_AL6", 0, ACPI_RTYPE_PACKAGE}}, {.ret_info = {ACPI_PTYPE1_VAR, ACPI_RTYPE_REFERENCE, 0, 0, 0, 0}}, /* variable (Refs) */ - {.info = {"_AL7", 0, ACPI_RTYPE_PACKAGE}}, {.ret_info = {ACPI_PTYPE1_VAR, ACPI_RTYPE_REFERENCE, 0, 0, 0, 0}}, /* variable (Refs) */ - {.info = {"_AL8", 0, ACPI_RTYPE_PACKAGE}}, {.ret_info = {ACPI_PTYPE1_VAR, ACPI_RTYPE_REFERENCE, 0, 0, 0, 0}}, /* variable (Refs) */ - {.info = {"_AL9", 0, ACPI_RTYPE_PACKAGE}}, {.ret_info = {ACPI_PTYPE1_VAR, ACPI_RTYPE_REFERENCE, 0, 0, 0, 0}}, /* variable (Refs) */ - {.info = {"_ALC", 0, ACPI_RTYPE_INTEGER}}, - {.info = {"_ALI", 0, ACPI_RTYPE_INTEGER}}, - {.info = {"_ALP", 0, ACPI_RTYPE_INTEGER}}, - {.info = {"_ALR", 0, ACPI_RTYPE_PACKAGE}}, {.ret_info = {ACPI_PTYPE2, ACPI_RTYPE_INTEGER, 2, 0, 0, 0}}, /* variable (Pkgs) each 2 (Ints) */ - {.info = {"_ALT", 0, ACPI_RTYPE_INTEGER}}, - {.info = {"_BBN", 0, ACPI_RTYPE_INTEGER}}, - {.info = {"_BCL", 0, ACPI_RTYPE_PACKAGE}}, {.ret_info = {ACPI_PTYPE1_VAR, ACPI_RTYPE_INTEGER, 0, 0, 0, 0}}, /* variable (Ints) */ - {.info = {"_BCM", 1, 0}}, - {.info = {"_BDN", 0, ACPI_RTYPE_INTEGER}}, - {.info = {"_BFS", 1, 0}}, - {.info = {"_BIF", 0, ACPI_RTYPE_PACKAGE}}, {.ret_info = {ACPI_PTYPE1_FIXED, ACPI_RTYPE_INTEGER, - 9, - ACPI_RTYPE_STRING | ACPI_RTYPE_BUFFER, 4, 0}}, /* fixed (9 Int),(4 Str) */ - {.info = {"_BLT", 3, 0}}, - {.info = {"_BMC", 1, 0}}, - {.info = {"_BMD", 0, ACPI_RTYPE_PACKAGE}}, {.ret_info = {ACPI_PTYPE1_FIXED, ACPI_RTYPE_INTEGER, 5, 0, 0, 0}}, /* fixed (5 Int) */ - {.info = {"_BQC", 0, ACPI_RTYPE_INTEGER}}, - {.info = {"_BST", 0, ACPI_RTYPE_PACKAGE}}, {.ret_info = {ACPI_PTYPE1_FIXED, ACPI_RTYPE_INTEGER, 4, 0, 0, 0}}, /* fixed (4 Int) */ - {.info = {"_BTM", 1, ACPI_RTYPE_INTEGER}}, - {.info = {"_BTP", 1, 0}}, - {.info = {"_CBA", 0, ACPI_RTYPE_INTEGER}}, /* see PCI firmware spec 3.0 */ - {.info = {"_CID", 0, - ACPI_RTYPE_INTEGER | ACPI_RTYPE_STRING | ACPI_RTYPE_PACKAGE}}, - {.ret_info = {ACPI_PTYPE1_VAR, ACPI_RTYPE_INTEGER | ACPI_RTYPE_STRING, 0, 0, 0, 0}}, /* variable (Ints/Strs) */ - {.info = {"_CRS", 0, ACPI_RTYPE_BUFFER}}, - {.info = {"_CRT", 0, ACPI_RTYPE_INTEGER}}, - {.info = {"_CSD", 0, ACPI_RTYPE_PACKAGE}}, {.ret_info = {ACPI_PTYPE2_COUNT, ACPI_RTYPE_INTEGER, 0, 0, 0, 0}}, /* variable (1 Int(n), n-1 Int) */ - {.info = {"_CST", 0, ACPI_RTYPE_PACKAGE}}, {.ret_info = {ACPI_PTYPE2_PKG_COUNT, - ACPI_RTYPE_BUFFER, 1, - ACPI_RTYPE_INTEGER, 3, 0}}, /* variable (1 Int(n), n Pkg (1 Buf/3 Int) */ - {.info = {"_DCK", 1, ACPI_RTYPE_INTEGER}}, - {.info = {"_DCS", 0, ACPI_RTYPE_INTEGER}}, - {.info = {"_DDC", 1, ACPI_RTYPE_INTEGER | ACPI_RTYPE_BUFFER}}, - {.info = {"_DDN", 0, ACPI_RTYPE_STRING}}, - {.info = {"_DGS", 0, ACPI_RTYPE_INTEGER}}, - {.info = {"_DIS", 0, 0}}, - {.info = {"_DMA", 0, ACPI_RTYPE_BUFFER}}, - {.info = {"_DOD", 0, ACPI_RTYPE_PACKAGE}}, {.ret_info = {ACPI_PTYPE1_VAR, ACPI_RTYPE_INTEGER, 0, 0, 0, 0}}, /* variable (Ints) */ - {.info = {"_DOS", 1, 0}}, - {.info = {"_DSM", 4, ACPI_RTYPE_ALL}}, /* Must return a type, but it can be of any type */ - {.info = {"_DSS", 1, 0}}, - {.info = {"_DSW", 3, 0}}, - {.info = {"_EC_", 0, ACPI_RTYPE_INTEGER}}, - {.info = {"_EDL", 0, ACPI_RTYPE_PACKAGE}}, {.ret_info = {ACPI_PTYPE1_VAR, ACPI_RTYPE_REFERENCE, 0, 0, 0, 0}}, /* variable (Refs) */ - {.info = {"_EJ0", 1, 0}}, - {.info = {"_EJ1", 1, 0}}, - {.info = {"_EJ2", 1, 0}}, - {.info = {"_EJ3", 1, 0}}, - {.info = {"_EJ4", 1, 0}}, - {.info = {"_EJD", 0, ACPI_RTYPE_STRING}}, - {.info = {"_FDE", 0, ACPI_RTYPE_BUFFER}}, - {.info = {"_FDI", 0, ACPI_RTYPE_PACKAGE}}, {.ret_info = {ACPI_PTYPE1_FIXED, ACPI_RTYPE_INTEGER, 16, 0, 0, 0}}, /* fixed (16 Int) */ - {.info = {"_FDM", 1, 0}}, - {.info = {"_FIX", 0, ACPI_RTYPE_PACKAGE}}, {.ret_info = {ACPI_PTYPE1_VAR, ACPI_RTYPE_INTEGER, 0, 0, 0, 0}}, /* variable (Ints) */ - {.info = {"_GLK", 0, ACPI_RTYPE_INTEGER}}, - {.info = {"_GPD", 0, ACPI_RTYPE_INTEGER}}, - {.info = {"_GPE", 0, ACPI_RTYPE_INTEGER}}, /* _GPE method, not _GPE scope */ - {.info = {"_GSB", 0, ACPI_RTYPE_INTEGER}}, - {.info = {"_GTF", 0, ACPI_RTYPE_BUFFER}}, - {.info = {"_GTM", 0, ACPI_RTYPE_BUFFER}}, - {.info = {"_GTS", 1, 0}}, - {.info = {"_HID", 0, ACPI_RTYPE_INTEGER | ACPI_RTYPE_STRING}}, - {.info = {"_HOT", 0, ACPI_RTYPE_INTEGER}}, - {.info = {"_HPP", 0, ACPI_RTYPE_PACKAGE}}, {.ret_info = {ACPI_PTYPE1_FIXED, ACPI_RTYPE_INTEGER, 4, 0, 0, 0}}, /* fixed (4 Int) */ +static const union acpi_predefined_info predefined_names[] = +{ + {{"_AC0", 0, ACPI_RTYPE_INTEGER}}, + {{"_AC1", 0, ACPI_RTYPE_INTEGER}}, + {{"_AC2", 0, ACPI_RTYPE_INTEGER}}, + {{"_AC3", 0, ACPI_RTYPE_INTEGER}}, + {{"_AC4", 0, ACPI_RTYPE_INTEGER}}, + {{"_AC5", 0, ACPI_RTYPE_INTEGER}}, + {{"_AC6", 0, ACPI_RTYPE_INTEGER}}, + {{"_AC7", 0, ACPI_RTYPE_INTEGER}}, + {{"_AC8", 0, ACPI_RTYPE_INTEGER}}, + {{"_AC9", 0, ACPI_RTYPE_INTEGER}}, + {{"_ADR", 0, ACPI_RTYPE_INTEGER}}, + {{"_AL0", 0, ACPI_RTYPE_PACKAGE}}, /* Variable-length (Refs) */ + {{{ACPI_PTYPE1_VAR, ACPI_RTYPE_REFERENCE, 0,0}, 0,0}}, + + {{"_AL1", 0, ACPI_RTYPE_PACKAGE}}, /* Variable-length (Refs) */ + {{{ACPI_PTYPE1_VAR, ACPI_RTYPE_REFERENCE, 0,0}, 0,0}}, + + {{"_AL2", 0, ACPI_RTYPE_PACKAGE}}, /* Variable-length (Refs) */ + {{{ACPI_PTYPE1_VAR, ACPI_RTYPE_REFERENCE, 0,0}, 0,0}}, + + {{"_AL3", 0, ACPI_RTYPE_PACKAGE}}, /* Variable-length (Refs) */ + {{{ACPI_PTYPE1_VAR, ACPI_RTYPE_REFERENCE, 0,0}, 0,0}}, + + {{"_AL4", 0, ACPI_RTYPE_PACKAGE}}, /* Variable-length (Refs) */ + {{{ACPI_PTYPE1_VAR, ACPI_RTYPE_REFERENCE, 0,0}, 0,0}}, + + {{"_AL5", 0, ACPI_RTYPE_PACKAGE}}, /* Variable-length (Refs) */ + {{{ACPI_PTYPE1_VAR, ACPI_RTYPE_REFERENCE, 0,0}, 0,0}}, + + {{"_AL6", 0, ACPI_RTYPE_PACKAGE}}, /* Variable-length (Refs) */ + {{{ACPI_PTYPE1_VAR, ACPI_RTYPE_REFERENCE, 0,0}, 0,0}}, + + {{"_AL7", 0, ACPI_RTYPE_PACKAGE}}, /* Variable-length (Refs) */ + {{{ACPI_PTYPE1_VAR, ACPI_RTYPE_REFERENCE, 0,0}, 0,0}}, + + {{"_AL8", 0, ACPI_RTYPE_PACKAGE}}, /* Variable-length (Refs) */ + {{{ACPI_PTYPE1_VAR, ACPI_RTYPE_REFERENCE, 0,0}, 0,0}}, + + {{"_AL9", 0, ACPI_RTYPE_PACKAGE}}, /* Variable-length (Refs) */ + {{{ACPI_PTYPE1_VAR, ACPI_RTYPE_REFERENCE, 0,0}, 0,0}}, + + {{"_ALC", 0, ACPI_RTYPE_INTEGER}}, + {{"_ALI", 0, ACPI_RTYPE_INTEGER}}, + {{"_ALP", 0, ACPI_RTYPE_INTEGER}}, + {{"_ALR", 0, ACPI_RTYPE_PACKAGE}}, /* Variable-length (Pkgs) each 2 (Ints) */ + {{{ACPI_PTYPE2, ACPI_RTYPE_INTEGER, 2,0}, 0,0}}, + + {{"_ALT", 0, ACPI_RTYPE_INTEGER}}, + {{"_ART", 0, ACPI_RTYPE_PACKAGE}}, /* Variable-length (1 Int(rev), n Pkg (2 Ref/11 Int) */ + {{{ACPI_PTYPE2_REV_FIXED, ACPI_RTYPE_REFERENCE, 2, ACPI_RTYPE_INTEGER}, + 11, 0}}, + + {{"_BBN", 0, ACPI_RTYPE_INTEGER}}, + {{"_BCL", 0, ACPI_RTYPE_PACKAGE}}, /* Variable-length (Ints) */ + {{{ACPI_PTYPE1_VAR, ACPI_RTYPE_INTEGER, 0,0}, 0,0}}, + + {{"_BCM", 1, 0}}, + {{"_BCT", 1, ACPI_RTYPE_INTEGER}}, + {{"_BDN", 0, ACPI_RTYPE_INTEGER}}, + {{"_BFS", 1, 0}}, + {{"_BIF", 0, ACPI_RTYPE_PACKAGE} }, /* Fixed-length (9 Int),(4 Str/Buf) */ + {{{ACPI_PTYPE1_FIXED, ACPI_RTYPE_INTEGER, 9, + ACPI_RTYPE_STRING | ACPI_RTYPE_BUFFER}, 4, 0} }, + + {{"_BIX", 0, ACPI_RTYPE_PACKAGE}}, /* Fixed-length (16 Int),(4 Str) */ + {{{ACPI_PTYPE1_FIXED, ACPI_RTYPE_INTEGER, 16, ACPI_RTYPE_STRING}, 4, + 0}}, + + {{"_BLT", 3, 0}}, + {{"_BMA", 1, ACPI_RTYPE_INTEGER}}, + {{"_BMC", 1, 0}}, + {{"_BMD", 0, ACPI_RTYPE_PACKAGE}}, /* Fixed-length (5 Int) */ + {{{ACPI_PTYPE1_FIXED, ACPI_RTYPE_INTEGER, 5,0}, 0,0}}, + + {{"_BMS", 1, ACPI_RTYPE_INTEGER}}, + {{"_BQC", 0, ACPI_RTYPE_INTEGER}}, + {{"_BST", 0, ACPI_RTYPE_PACKAGE}}, /* Fixed-length (4 Int) */ + {{{ACPI_PTYPE1_FIXED, ACPI_RTYPE_INTEGER, 4,0}, 0,0}}, + + {{"_BTM", 1, ACPI_RTYPE_INTEGER}}, + {{"_BTP", 1, 0}}, + {{"_CBA", 0, ACPI_RTYPE_INTEGER}}, /* See PCI firmware spec 3.0 */ + {{"_CDM", 0, ACPI_RTYPE_INTEGER}}, + {{"_CID", 0, ACPI_RTYPE_INTEGER | ACPI_RTYPE_STRING | ACPI_RTYPE_PACKAGE}}, /* Variable-length (Ints/Strs) */ + {{{ACPI_PTYPE1_VAR, ACPI_RTYPE_INTEGER | ACPI_RTYPE_STRING, 0,0}, 0,0}}, + + {{"_CRS", 0, ACPI_RTYPE_BUFFER}}, + {{"_CRT", 0, ACPI_RTYPE_INTEGER}}, + {{"_CSD", 0, ACPI_RTYPE_PACKAGE}}, /* Variable-length (1 Int(n), n-1 Int) */ + {{{ACPI_PTYPE2_COUNT, ACPI_RTYPE_INTEGER, 0,0}, 0,0}}, + + {{"_CST", 0, ACPI_RTYPE_PACKAGE}}, /* Variable-length (1 Int(n), n Pkg (1 Buf/3 Int) */ + {{{ACPI_PTYPE2_PKG_COUNT,ACPI_RTYPE_BUFFER, 1, ACPI_RTYPE_INTEGER}, 3,0}}, + + {{"_DCK", 1, ACPI_RTYPE_INTEGER}}, + {{"_DCS", 0, ACPI_RTYPE_INTEGER}}, + {{"_DDC", 1, ACPI_RTYPE_INTEGER | ACPI_RTYPE_BUFFER}}, + {{"_DDN", 0, ACPI_RTYPE_STRING}}, + {{"_DGS", 0, ACPI_RTYPE_INTEGER}}, + {{"_DIS", 0, 0}}, + {{"_DMA", 0, ACPI_RTYPE_BUFFER}}, + {{"_DOD", 0, ACPI_RTYPE_PACKAGE}}, /* Variable-length (Ints) */ + {{{ACPI_PTYPE1_VAR, ACPI_RTYPE_INTEGER, 0,0}, 0,0}}, + + {{"_DOS", 1, 0}}, + {{"_DSM", 4, ACPI_RTYPE_ALL}}, /* Must return a type, but it can be of any type */ + {{"_DSS", 1, 0}}, + {{"_DSW", 3, 0}}, + {{"_DTI", 1, 0}}, + {{"_EC_", 0, ACPI_RTYPE_INTEGER}}, + {{"_EDL", 0, ACPI_RTYPE_PACKAGE}}, /* Variable-length (Refs)*/ + {{{ACPI_PTYPE1_VAR, ACPI_RTYPE_REFERENCE, 0,0}, 0,0}}, + + {{"_EJ0", 1, 0}}, + {{"_EJ1", 1, 0}}, + {{"_EJ2", 1, 0}}, + {{"_EJ3", 1, 0}}, + {{"_EJ4", 1, 0}}, + {{"_EJD", 0, ACPI_RTYPE_STRING}}, + {{"_FDE", 0, ACPI_RTYPE_BUFFER}}, + {{"_FDI", 0, ACPI_RTYPE_PACKAGE}}, /* Fixed-length (16 Int) */ + {{{ACPI_PTYPE1_FIXED, ACPI_RTYPE_INTEGER, 16,0}, 0,0}}, + + {{"_FDM", 1, 0}}, + {{"_FIF", 0, ACPI_RTYPE_PACKAGE}}, /* Fixed-length (4 Int) */ + {{{ACPI_PTYPE1_FIXED, ACPI_RTYPE_INTEGER, 4, 0}, 0, 0}}, + + {{"_FIX", 0, ACPI_RTYPE_PACKAGE}}, /* Variable-length (Ints) */ + {{{ACPI_PTYPE1_VAR, ACPI_RTYPE_INTEGER, 0,0}, 0,0}}, + + {{"_FPS", 0, ACPI_RTYPE_PACKAGE}}, /* Variable-length (1 Int(rev), n Pkg (5 Int) */ + {{{ACPI_PTYPE2_REV_FIXED, ACPI_RTYPE_INTEGER, 5, 0}, 0, 0}}, + + {{"_FSL", 1, 0}}, + {{"_FST", 0, ACPI_RTYPE_PACKAGE}}, /* Fixed-length (3 Int) */ + {{{ACPI_PTYPE1_FIXED, ACPI_RTYPE_INTEGER, 3, 0}, 0, 0}}, + + {{"_GAI", 0, ACPI_RTYPE_INTEGER}}, + {{"_GHL", 0, ACPI_RTYPE_INTEGER}}, + {{"_GLK", 0, ACPI_RTYPE_INTEGER}}, + {{"_GPD", 0, ACPI_RTYPE_INTEGER}}, + {{"_GPE", 0, ACPI_RTYPE_INTEGER}}, /* _GPE method, not _GPE scope */ + {{"_GSB", 0, ACPI_RTYPE_INTEGER}}, + {{"_GTF", 0, ACPI_RTYPE_BUFFER}}, + {{"_GTM", 0, ACPI_RTYPE_BUFFER}}, + {{"_GTS", 1, 0}}, + {{"_HID", 0, ACPI_RTYPE_INTEGER | ACPI_RTYPE_STRING}}, + {{"_HOT", 0, ACPI_RTYPE_INTEGER}}, + {{"_HPP", 0, ACPI_RTYPE_PACKAGE}}, /* Fixed-length (4 Int) */ + {{{ACPI_PTYPE1_FIXED, ACPI_RTYPE_INTEGER, 4,0}, 0,0}}, /* - * For _HPX, a single package is returned, containing a variable number of sub-packages. - * Each sub-package contains a PCI record setting. There are several different type of - * record settings, of different lengths, but all elements of all settings are Integers. + * For _HPX, a single package is returned, containing a Variable-length number + * of sub-packages. Each sub-package contains a PCI record setting. + * There are several different type of record settings, of different + * lengths, but all elements of all settings are Integers. */ - {.info = {"_HPX", 0, ACPI_RTYPE_PACKAGE}}, {.ret_info = {ACPI_PTYPE2_MIN, ACPI_RTYPE_INTEGER, 5, 0, 0, 0}}, /* variable (Pkgs) each (var Ints) */ - {.info = {"_IFT", 0, ACPI_RTYPE_INTEGER}}, /* see IPMI spec */ - {.info = {"_INI", 0, 0}}, - {.info = {"_IRC", 0, 0}}, - {.info = {"_LCK", 1, 0}}, - {.info = {"_LID", 0, ACPI_RTYPE_INTEGER}}, - {.info = {"_MAT", 0, ACPI_RTYPE_BUFFER}}, - {.info = {"_MLS", 0, ACPI_RTYPE_PACKAGE}}, {.ret_info = {ACPI_PTYPE2, ACPI_RTYPE_STRING, 2, 0, 0, 0}}, /* variable (Pkgs) each (2 Str) */ - {.info = {"_MSG", 1, 0}}, - {.info = {"_OFF", 0, 0}}, - {.info = {"_ON_", 0, 0}}, - {.info = {"_OS_", 0, ACPI_RTYPE_STRING}}, - {.info = {"_OSC", 4, ACPI_RTYPE_BUFFER}}, - {.info = {"_OST", 3, 0}}, - {.info = {"_PCL", 0, ACPI_RTYPE_PACKAGE}}, {.ret_info = {ACPI_PTYPE1_VAR, ACPI_RTYPE_REFERENCE, 0, 0, 0, 0}}, /* variable (Refs) */ - {.info = {"_PCT", 0, ACPI_RTYPE_PACKAGE}}, {.ret_info = {ACPI_PTYPE1_FIXED, ACPI_RTYPE_BUFFER, 2, 0, 0, 0}}, /* fixed (2 Buf) */ - {.info = {"_PDC", 1, 0}}, - {.info = {"_PIC", 1, 0}}, - {.info = {"_PLD", 0, ACPI_RTYPE_PACKAGE}}, {.ret_info = {ACPI_PTYPE1_VAR, ACPI_RTYPE_BUFFER, 0, 0, 0, 0}}, /* variable (Bufs) */ - {.info = {"_PPC", 0, ACPI_RTYPE_INTEGER}}, - {.info = {"_PPE", 0, ACPI_RTYPE_INTEGER}}, /* see dig64 spec */ - {.info = {"_PR0", 0, ACPI_RTYPE_PACKAGE}}, {.ret_info = {ACPI_PTYPE1_VAR, ACPI_RTYPE_REFERENCE, 0, 0, 0, 0}}, /* variable (Refs) */ - {.info = {"_PR1", 0, ACPI_RTYPE_PACKAGE}}, {.ret_info = {ACPI_PTYPE1_VAR, ACPI_RTYPE_REFERENCE, 0, 0, 0, 0}}, /* variable (Refs) */ - {.info = {"_PR2", 0, ACPI_RTYPE_PACKAGE}}, {.ret_info = {ACPI_PTYPE1_VAR, ACPI_RTYPE_REFERENCE, 0, 0, 0, 0}}, /* variable (Refs) */ - {.info = {"_PRS", 0, ACPI_RTYPE_BUFFER}}, + {{"_HPX", 0, ACPI_RTYPE_PACKAGE}}, /* Variable-length (Pkgs) each (var Ints) */ + {{{ACPI_PTYPE2_MIN, ACPI_RTYPE_INTEGER, 5,0}, 0,0}}, + + {{"_IFT", 0, ACPI_RTYPE_INTEGER}}, /* See IPMI spec */ + {{"_INI", 0, 0}}, + {{"_IRC", 0, 0}}, + {{"_LCK", 1, 0}}, + {{"_LID", 0, ACPI_RTYPE_INTEGER}}, + {{"_MAT", 0, ACPI_RTYPE_BUFFER}}, + {{"_MBM", 0, ACPI_RTYPE_PACKAGE}}, /* Fixed-length (8 Int) */ + {{{ACPI_PTYPE1_FIXED, ACPI_RTYPE_INTEGER, 8, 0}, 0, 0}}, + + {{"_MLS", 0, ACPI_RTYPE_PACKAGE}}, /* Variable-length (Pkgs) each (2 Str) */ + {{{ACPI_PTYPE2, ACPI_RTYPE_STRING, 2,0}, 0,0}}, + + {{"_MSG", 1, 0}}, + {{"_MSM", 4, ACPI_RTYPE_INTEGER}}, + {{"_NTT", 0, ACPI_RTYPE_INTEGER}}, + {{"_OFF", 0, 0}}, + {{"_ON_", 0, 0}}, + {{"_OS_", 0, ACPI_RTYPE_STRING}}, + {{"_OSC", 4, ACPI_RTYPE_BUFFER}}, + {{"_OST", 3, 0}}, + {{"_PAI", 1, ACPI_RTYPE_INTEGER}}, + {{"_PCL", 0, ACPI_RTYPE_PACKAGE}}, /* Variable-length (Refs) */ + {{{ACPI_PTYPE1_VAR, ACPI_RTYPE_REFERENCE, 0,0}, 0,0}}, + + {{"_PCT", 0, ACPI_RTYPE_PACKAGE}}, /* Fixed-length (2 Buf) */ + {{{ACPI_PTYPE1_FIXED, ACPI_RTYPE_BUFFER, 2,0}, 0,0}}, + + {{"_PDC", 1, 0}}, + {{"_PDL", 0, ACPI_RTYPE_INTEGER}}, + {{"_PIC", 1, 0}}, + {{"_PIF", 0, ACPI_RTYPE_PACKAGE}}, /* Fixed-length (3 Int),(3 Str) */ + {{{ACPI_PTYPE1_FIXED, ACPI_RTYPE_INTEGER, 3, ACPI_RTYPE_STRING}, 3, 0}}, + + {{"_PLD", 0, ACPI_RTYPE_PACKAGE}}, /* Variable-length (Bufs) */ + {{{ACPI_PTYPE1_VAR, ACPI_RTYPE_BUFFER, 0,0}, 0,0}}, + + {{"_PMC", 0, ACPI_RTYPE_PACKAGE}}, /* Fixed-length (11 Int),(3 Str) */ + {{{ACPI_PTYPE1_FIXED, ACPI_RTYPE_INTEGER, 11, ACPI_RTYPE_STRING}, 3, + 0}}, + + {{"_PMD", 0, ACPI_RTYPE_PACKAGE}}, /* Variable-length (Refs) */ + {{{ACPI_PTYPE1_VAR, ACPI_RTYPE_REFERENCE, 0, 0}, 0, 0}}, + + {{"_PMM", 0, ACPI_RTYPE_INTEGER}}, + {{"_PPC", 0, ACPI_RTYPE_INTEGER}}, + {{"_PPE", 0, ACPI_RTYPE_INTEGER}}, /* See dig64 spec */ + {{"_PR0", 0, ACPI_RTYPE_PACKAGE}}, /* Variable-length (Refs) */ + {{{ACPI_PTYPE1_VAR, ACPI_RTYPE_REFERENCE, 0,0}, 0,0}}, + + {{"_PR1", 0, ACPI_RTYPE_PACKAGE}}, /* Variable-length (Refs) */ + {{{ACPI_PTYPE1_VAR, ACPI_RTYPE_REFERENCE, 0,0}, 0,0}}, + + {{"_PR2", 0, ACPI_RTYPE_PACKAGE}}, /* Variable-length (Refs) */ + {{{ACPI_PTYPE1_VAR, ACPI_RTYPE_REFERENCE, 0,0}, 0,0}}, + + {{"_PR3", 0, ACPI_RTYPE_PACKAGE}}, /* Variable-length (Refs) */ + {{{ACPI_PTYPE1_VAR, ACPI_RTYPE_REFERENCE, 0, 0}, 0, 0}}, + + {{"_PRL", 0, ACPI_RTYPE_PACKAGE}}, /* Variable-length (Refs) */ + {{{ACPI_PTYPE1_VAR, ACPI_RTYPE_REFERENCE, 0, 0}, 0, 0}}, + + {{"_PRS", 0, ACPI_RTYPE_BUFFER}}, /* - * For _PRT, many BIOSs reverse the 2nd and 3rd Package elements. This bug is so prevalent that there - * is code in the ACPICA Resource Manager to detect this and switch them back. For now, do not allow - * and issue a warning. To allow this and eliminate the warning, add the ACPI_RTYPE_REFERENCE - * type to the 2nd element (index 1) in the statement below. + * For _PRT, many BIOSs reverse the 3rd and 4th Package elements (Source + * and source_index). This bug is so prevalent that there is code in the + * ACPICA Resource Manager to detect this and switch them back. For now, + * do not allow and issue a warning. To allow this and eliminate the + * warning, add the ACPI_RTYPE_REFERENCE type to the 4th element (index 3) + * in the statement below. */ - {.info = {"_PRT", 0, ACPI_RTYPE_PACKAGE}}, {.ret_info = {ACPI_PTYPE2_FIXED, 4, - ACPI_RTYPE_INTEGER, - ACPI_RTYPE_INTEGER, - ACPI_RTYPE_INTEGER | ACPI_RTYPE_REFERENCE, ACPI_RTYPE_INTEGER}}, /* variable (Pkgs) each (4): Int,Int,Int/Ref,Int */ - - {.info = {"_PRW", 0, ACPI_RTYPE_PACKAGE}}, {.ret_info = {ACPI_PTYPE1_OPTION, 2, - ACPI_RTYPE_INTEGER | - ACPI_RTYPE_PACKAGE, - ACPI_RTYPE_INTEGER, ACPI_RTYPE_REFERENCE, 0}}, /* variable (Pkgs) each: Pkg/Int,Int,[variable Refs] (Pkg is Ref/Int) */ - - {.info = {"_PS0", 0, 0}}, - {.info = {"_PS1", 0, 0}}, - {.info = {"_PS2", 0, 0}}, - {.info = {"_PS3", 0, 0}}, - {.info = {"_PSC", 0, ACPI_RTYPE_INTEGER}}, - {.info = {"_PSD", 0, ACPI_RTYPE_PACKAGE}}, {.ret_info = {ACPI_PTYPE2_COUNT, ACPI_RTYPE_INTEGER, 0, 0, 0, 0}}, /* variable (Pkgs) each (5 Int) with count */ - {.info = {"_PSL", 0, ACPI_RTYPE_PACKAGE}}, {.ret_info = {ACPI_PTYPE1_VAR, ACPI_RTYPE_REFERENCE, 0, 0, 0, 0}}, /* variable (Refs) */ - {.info = {"_PSR", 0, ACPI_RTYPE_INTEGER}}, - {.info = {"_PSS", 0, ACPI_RTYPE_PACKAGE}}, {.ret_info = {ACPI_PTYPE2, ACPI_RTYPE_INTEGER, 6, 0, 0, 0}}, /* variable (Pkgs) each (6 Int) */ - {.info = {"_PSV", 0, ACPI_RTYPE_INTEGER}}, - {.info = {"_PSW", 1, 0}}, - {.info = {"_PTC", 0, ACPI_RTYPE_PACKAGE}}, {.ret_info = {ACPI_PTYPE1_FIXED, ACPI_RTYPE_BUFFER, 2, 0, 0, 0}}, /* fixed (2 Buf) */ - {.info = {"_PTS", 1, 0}}, - {.info = {"_PXM", 0, ACPI_RTYPE_INTEGER}}, - {.info = {"_REG", 2, 0}}, - {.info = {"_REV", 0, ACPI_RTYPE_INTEGER}}, - {.info = {"_RMV", 0, ACPI_RTYPE_INTEGER}}, - {.info = {"_ROM", 2, ACPI_RTYPE_BUFFER}}, - {.info = {"_RTV", 0, ACPI_RTYPE_INTEGER}}, + {{"_PRT", 0, ACPI_RTYPE_PACKAGE}}, /* Variable-length (Pkgs) each (4): Int,Int,Int/Ref,Int */ + {{{ACPI_PTYPE2_FIXED, 4, ACPI_RTYPE_INTEGER,ACPI_RTYPE_INTEGER}, + ACPI_RTYPE_INTEGER | ACPI_RTYPE_REFERENCE, + ACPI_RTYPE_INTEGER}}, + + {{"_PRW", 0, ACPI_RTYPE_PACKAGE}}, /* Variable-length (Pkgs) each: Pkg/Int,Int,[Variable-length Refs] (Pkg is Ref/Int) */ + {{{ACPI_PTYPE1_OPTION, 2, ACPI_RTYPE_INTEGER | ACPI_RTYPE_PACKAGE, + ACPI_RTYPE_INTEGER}, ACPI_RTYPE_REFERENCE,0}}, + + {{"_PS0", 0, 0}}, + {{"_PS1", 0, 0}}, + {{"_PS2", 0, 0}}, + {{"_PS3", 0, 0}}, + {{"_PSC", 0, ACPI_RTYPE_INTEGER}}, + {{"_PSD", 0, ACPI_RTYPE_PACKAGE}}, /* Variable-length (Pkgs) each (5 Int) with count */ + {{{ACPI_PTYPE2_COUNT, ACPI_RTYPE_INTEGER,0,0}, 0,0}}, + + {{"_PSL", 0, ACPI_RTYPE_PACKAGE}}, /* Variable-length (Refs) */ + {{{ACPI_PTYPE1_VAR, ACPI_RTYPE_REFERENCE, 0,0}, 0,0}}, + + {{"_PSR", 0, ACPI_RTYPE_INTEGER}}, + {{"_PSS", 0, ACPI_RTYPE_PACKAGE}}, /* Variable-length (Pkgs) each (6 Int) */ + {{{ACPI_PTYPE2, ACPI_RTYPE_INTEGER, 6,0}, 0,0}}, + + {{"_PSV", 0, ACPI_RTYPE_INTEGER}}, + {{"_PSW", 1, 0}}, + {{"_PTC", 0, ACPI_RTYPE_PACKAGE}}, /* Fixed-length (2 Buf) */ + {{{ACPI_PTYPE1_FIXED, ACPI_RTYPE_BUFFER, 2,0}, 0,0}}, + + {{"_PTP", 2, ACPI_RTYPE_INTEGER}}, + {{"_PTS", 1, 0}}, + {{"_PUR", 0, ACPI_RTYPE_PACKAGE}}, /* Fixed-length (2 Int) */ + {{{ACPI_PTYPE1_FIXED, ACPI_RTYPE_INTEGER, 2, 0}, 0, 0}}, + + {{"_PXM", 0, ACPI_RTYPE_INTEGER}}, + {{"_REG", 2, 0}}, + {{"_REV", 0, ACPI_RTYPE_INTEGER}}, + {{"_RMV", 0, ACPI_RTYPE_INTEGER}}, + {{"_ROM", 2, ACPI_RTYPE_BUFFER}}, + {{"_RTV", 0, ACPI_RTYPE_INTEGER}}, /* - * For _S0_ through _S5_, the ACPI spec defines a return Package containing 1 Integer, - * but most DSDTs have it wrong - 2,3, or 4 integers. Allow this by making the objects "variable length", - * but all elements must be Integers. + * For _S0_ through _S5_, the ACPI spec defines a return Package + * containing 1 Integer, but most DSDTs have it wrong - 2,3, or 4 integers. + * Allow this by making the objects "Variable-length length", but all elements + * must be Integers. */ - {.info = {"_S0_", 0, ACPI_RTYPE_PACKAGE}}, {.ret_info = {ACPI_PTYPE1_VAR, ACPI_RTYPE_INTEGER, 1, 0, 0, 0}}, /* fixed (1 Int) */ - {.info = {"_S1_", 0, ACPI_RTYPE_PACKAGE}}, {.ret_info = {ACPI_PTYPE1_VAR, ACPI_RTYPE_INTEGER, 1, 0, 0, 0}}, /* fixed (1 Int) */ - {.info = {"_S2_", 0, ACPI_RTYPE_PACKAGE}}, {.ret_info = {ACPI_PTYPE1_VAR, ACPI_RTYPE_INTEGER, 1, 0, 0, 0}}, /* fixed (1 Int) */ - {.info = {"_S3_", 0, ACPI_RTYPE_PACKAGE}}, {.ret_info = {ACPI_PTYPE1_VAR, ACPI_RTYPE_INTEGER, 1, 0, 0, 0}}, /* fixed (1 Int) */ - {.info = {"_S4_", 0, ACPI_RTYPE_PACKAGE}}, {.ret_info = {ACPI_PTYPE1_VAR, ACPI_RTYPE_INTEGER, 1, 0, 0, 0}}, /* fixed (1 Int) */ - {.info = {"_S5_", 0, ACPI_RTYPE_PACKAGE}}, {.ret_info = {ACPI_PTYPE1_VAR, ACPI_RTYPE_INTEGER, 1, 0, 0, 0}}, /* fixed (1 Int) */ - - {.info = {"_S1D", 0, ACPI_RTYPE_INTEGER}}, - {.info = {"_S2D", 0, ACPI_RTYPE_INTEGER}}, - {.info = {"_S3D", 0, ACPI_RTYPE_INTEGER}}, - {.info = {"_S4D", 0, ACPI_RTYPE_INTEGER}}, - {.info = {"_S0W", 0, ACPI_RTYPE_INTEGER}}, - {.info = {"_S1W", 0, ACPI_RTYPE_INTEGER}}, - {.info = {"_S2W", 0, ACPI_RTYPE_INTEGER}}, - {.info = {"_S3W", 0, ACPI_RTYPE_INTEGER}}, - {.info = {"_S4W", 0, ACPI_RTYPE_INTEGER}}, - {.info = {"_SBS", 0, ACPI_RTYPE_INTEGER}}, - {.info = {"_SCP", 0x13, 0}}, /* Acpi 1.0 allowed 1 arg. Acpi 3.0 expanded to 3 args. Allow both. */ - /* Note: the 3-arg definition may be removed for ACPI 4.0 */ - {.info = {"_SDD", 1, 0}}, - {.info = {"_SEG", 0, ACPI_RTYPE_INTEGER}}, - {.info = {"_SLI", 0, ACPI_RTYPE_BUFFER}}, - {.info = {"_SPD", 1, ACPI_RTYPE_INTEGER}}, - {.info = {"_SRS", 1, 0}}, - {.info = {"_SRV", 0, ACPI_RTYPE_INTEGER}}, /* see IPMI spec */ - {.info = {"_SST", 1, 0}}, - {.info = {"_STA", 0, ACPI_RTYPE_INTEGER}}, - {.info = {"_STM", 3, 0}}, - {.info = {"_STR", 0, ACPI_RTYPE_BUFFER}}, - {.info = {"_SUN", 0, ACPI_RTYPE_INTEGER}}, - {.info = {"_SWS", 0, ACPI_RTYPE_INTEGER}}, - {.info = {"_TC1", 0, ACPI_RTYPE_INTEGER}}, - {.info = {"_TC2", 0, ACPI_RTYPE_INTEGER}}, - {.info = {"_TMP", 0, ACPI_RTYPE_INTEGER}}, - {.info = {"_TPC", 0, ACPI_RTYPE_INTEGER}}, - {.info = {"_TPT", 1, 0}}, - {.info = {"_TRT", 0, ACPI_RTYPE_PACKAGE}}, {.ret_info = {ACPI_PTYPE2, ACPI_RTYPE_REFERENCE, 2, - ACPI_RTYPE_INTEGER, 6, 0}}, /* variable (Pkgs) each 2_ref/6_int */ - {.info = {"_TSD", 0, ACPI_RTYPE_PACKAGE}}, {.ret_info = {ACPI_PTYPE2_COUNT, ACPI_RTYPE_INTEGER, 5, 0, 0, 0}}, /* variable (Pkgs) each 5_int with count */ - {.info = {"_TSP", 0, ACPI_RTYPE_INTEGER}}, - {.info = {"_TSS", 0, ACPI_RTYPE_PACKAGE}}, {.ret_info = {ACPI_PTYPE2, ACPI_RTYPE_INTEGER, 5, 0, 0, 0}}, /* variable (Pkgs) each 5_int */ - {.info = {"_TST", 0, ACPI_RTYPE_INTEGER}}, - {.info = {"_TTS", 1, 0}}, - {.info = {"_TZD", 0, ACPI_RTYPE_PACKAGE}}, {.ret_info = {ACPI_PTYPE1_VAR, ACPI_RTYPE_REFERENCE, 0, 0, 0, 0}}, /* variable (Refs) */ - {.info = {"_TZM", 0, ACPI_RTYPE_REFERENCE}}, - {.info = {"_TZP", 0, ACPI_RTYPE_INTEGER}}, - {.info = {"_UID", 0, ACPI_RTYPE_INTEGER | ACPI_RTYPE_STRING}}, - {.info = {"_UPC", 0, ACPI_RTYPE_PACKAGE}}, {.ret_info = {ACPI_PTYPE1_FIXED, ACPI_RTYPE_INTEGER, 4, 0, 0, 0}}, /* fixed (4 Int) */ - {.info = {"_UPD", 0, ACPI_RTYPE_INTEGER}}, - {.info = {"_UPP", 0, ACPI_RTYPE_INTEGER}}, - {.info = {"_VPO", 0, ACPI_RTYPE_INTEGER}}, + {{"_S0_", 0, ACPI_RTYPE_PACKAGE}}, /* Fixed-length (1 Int) */ + {{{ACPI_PTYPE1_VAR, ACPI_RTYPE_INTEGER, 1,0}, 0,0}}, + + {{"_S1_", 0, ACPI_RTYPE_PACKAGE}}, /* Fixed-length (1 Int) */ + {{{ACPI_PTYPE1_VAR, ACPI_RTYPE_INTEGER, 1,0}, 0,0}}, + + {{"_S2_", 0, ACPI_RTYPE_PACKAGE}}, /* Fixed-length (1 Int) */ + {{{ACPI_PTYPE1_VAR, ACPI_RTYPE_INTEGER, 1,0}, 0,0}}, + + {{"_S3_", 0, ACPI_RTYPE_PACKAGE}}, /* Fixed-length (1 Int) */ + {{{ACPI_PTYPE1_VAR, ACPI_RTYPE_INTEGER, 1,0}, 0,0}}, + + {{"_S4_", 0, ACPI_RTYPE_PACKAGE}}, /* Fixed-length (1 Int) */ + {{{ACPI_PTYPE1_VAR, ACPI_RTYPE_INTEGER, 1,0}, 0,0}}, + + {{"_S5_", 0, ACPI_RTYPE_PACKAGE}}, /* Fixed-length (1 Int) */ + {{{ACPI_PTYPE1_VAR, ACPI_RTYPE_INTEGER, 1,0}, 0,0}}, + + {{"_S1D", 0, ACPI_RTYPE_INTEGER}}, + {{"_S2D", 0, ACPI_RTYPE_INTEGER}}, + {{"_S3D", 0, ACPI_RTYPE_INTEGER}}, + {{"_S4D", 0, ACPI_RTYPE_INTEGER}}, + {{"_S0W", 0, ACPI_RTYPE_INTEGER}}, + {{"_S1W", 0, ACPI_RTYPE_INTEGER}}, + {{"_S2W", 0, ACPI_RTYPE_INTEGER}}, + {{"_S3W", 0, ACPI_RTYPE_INTEGER}}, + {{"_S4W", 0, ACPI_RTYPE_INTEGER}}, + {{"_SBS", 0, ACPI_RTYPE_INTEGER}}, + {{"_SCP", 0x13, 0}}, /* Acpi 1.0 allowed 1 arg. Acpi 3.0 expanded to 3 args. Allow both. */ + /* Note: the 3-arg definition may be removed for ACPI 4.0 */ + {{"_SDD", 1, 0}}, + {{"_SEG", 0, ACPI_RTYPE_INTEGER}}, + {{"_SHL", 1, ACPI_RTYPE_INTEGER}}, + {{"_SLI", 0, ACPI_RTYPE_BUFFER}}, + {{"_SPD", 1, ACPI_RTYPE_INTEGER}}, + {{"_SRS", 1, 0}}, + {{"_SRV", 0, ACPI_RTYPE_INTEGER}}, /* See IPMI spec */ + {{"_SST", 1, 0}}, + {{"_STA", 0, ACPI_RTYPE_INTEGER}}, + {{"_STM", 3, 0}}, + {{"_STP", 2, ACPI_RTYPE_INTEGER}}, + {{"_STR", 0, ACPI_RTYPE_BUFFER}}, + {{"_STV", 2, ACPI_RTYPE_INTEGER}}, + {{"_SUN", 0, ACPI_RTYPE_INTEGER}}, + {{"_SWS", 0, ACPI_RTYPE_INTEGER}}, + {{"_TC1", 0, ACPI_RTYPE_INTEGER}}, + {{"_TC2", 0, ACPI_RTYPE_INTEGER}}, + {{"_TIP", 1, ACPI_RTYPE_INTEGER}}, + {{"_TIV", 1, ACPI_RTYPE_INTEGER}}, + {{"_TMP", 0, ACPI_RTYPE_INTEGER}}, + {{"_TPC", 0, ACPI_RTYPE_INTEGER}}, + {{"_TPT", 1, 0}}, + {{"_TRT", 0, ACPI_RTYPE_PACKAGE}}, /* Variable-length (Pkgs) each 2_ref/6_int */ + {{{ACPI_PTYPE2, ACPI_RTYPE_REFERENCE, 2, ACPI_RTYPE_INTEGER}, 6, 0}}, + + {{"_TSD", 0, ACPI_RTYPE_PACKAGE}}, /* Variable-length (Pkgs) each 5_int with count */ + {{{ACPI_PTYPE2_COUNT,ACPI_RTYPE_INTEGER, 5,0}, 0,0}}, + + {{"_TSP", 0, ACPI_RTYPE_INTEGER}}, + {{"_TSS", 0, ACPI_RTYPE_PACKAGE}}, /* Variable-length (Pkgs) each 5_int */ + {{{ACPI_PTYPE2, ACPI_RTYPE_INTEGER, 5,0}, 0,0}}, + + {{"_TST", 0, ACPI_RTYPE_INTEGER}}, + {{"_TTS", 1, 0}}, + {{"_TZD", 0, ACPI_RTYPE_PACKAGE}}, /* Variable-length (Refs) */ + {{{ACPI_PTYPE1_VAR, ACPI_RTYPE_REFERENCE, 0,0}, 0,0}}, + + {{"_TZM", 0, ACPI_RTYPE_REFERENCE}}, + {{"_TZP", 0, ACPI_RTYPE_INTEGER}}, + {{"_UID", 0, ACPI_RTYPE_INTEGER | ACPI_RTYPE_STRING}}, + {{"_UPC", 0, ACPI_RTYPE_PACKAGE}}, /* Fixed-length (4 Int) */ + {{{ACPI_PTYPE1_FIXED, ACPI_RTYPE_INTEGER, 4,0}, 0,0}}, + + {{"_UPD", 0, ACPI_RTYPE_INTEGER}}, + {{"_UPP", 0, ACPI_RTYPE_INTEGER}}, + {{"_VPO", 0, ACPI_RTYPE_INTEGER}}, /* Acpi 1.0 defined _WAK with no return value. Later, it was changed to return a package */ - {.info = {"_WAK", 1, ACPI_RTYPE_NONE | ACPI_RTYPE_INTEGER | ACPI_RTYPE_PACKAGE}}, - {.ret_info = {ACPI_PTYPE1_FIXED, ACPI_RTYPE_INTEGER, 2, 0, 0, 0}}, /* fixed (2 Int), but is optional */ - {.ret_info = {0, 0, 0, 0, 0, 0}} /* Table terminator */ + {{"_WAK", 1, ACPI_RTYPE_NONE | ACPI_RTYPE_INTEGER | ACPI_RTYPE_PACKAGE}}, + {{{ACPI_PTYPE1_FIXED, ACPI_RTYPE_INTEGER, 2,0}, 0,0}}, /* Fixed-length (2 Int), but is optional */ + + {{{0,0,0,0}, 0,0}} /* Table terminator */ }; #if 0 /* Not implemented */ -{ -"_WDG", 0, ACPI_RTYPE_BUFFER}, /* MS Extension */ + {{"_WDG", 0, ACPI_RTYPE_BUFFER}}, /* MS Extension */ + {{"_WED", 1, ACPI_RTYPE_PACKAGE}}, /* MS Extension */ -{ -"_WED", 1, ACPI_RTYPE_PACKAGE}, /* MS Extension */ + /* This is an internally implemented control method, no need to check */ + {{"_OSI", 1, ACPI_RTYPE_INTEGER}}, - /* This is an internally implemented control method, no need to check */ -{ -"_OSI", 1, ACPI_RTYPE_INTEGER}, + /* TBD: */ + + _PRT - currently ignore reversed entries. attempt to fix here? + think about possibly fixing package elements like _BIF, etc. +#endif - /* TBD: */ - _PRT - currently ignore reversed entries.attempt to fix here ? - think about code that attempts to fix package elements like _BIF, etc. #endif #endif diff --git a/drivers/acpi/acpica/acutils.h b/drivers/acpi/acpica/acutils.h index 897810ba0ccc..863a264b829e 100644 --- a/drivers/acpi/acpica/acutils.h +++ b/drivers/acpi/acpica/acutils.h @@ -324,26 +324,30 @@ acpi_ut_evaluate_object(struct acpi_namespace_node *prefix_node, acpi_status acpi_ut_evaluate_numeric_object(char *object_name, struct acpi_namespace_node *device_node, - acpi_integer * address); + acpi_integer *value); acpi_status -acpi_ut_execute_HID(struct acpi_namespace_node *device_node, - struct acpica_device_id *hid); +acpi_ut_execute_STA(struct acpi_namespace_node *device_node, u32 *status_flags); acpi_status -acpi_ut_execute_CID(struct acpi_namespace_node *device_node, - struct acpi_compatible_id_list **return_cid_list); +acpi_ut_execute_power_methods(struct acpi_namespace_node *device_node, + const char **method_names, + u8 method_count, u8 *out_values); +/* + * utids - device ID support + */ acpi_status -acpi_ut_execute_STA(struct acpi_namespace_node *device_node, - u32 * status_flags); +acpi_ut_execute_HID(struct acpi_namespace_node *device_node, + struct acpica_device_id **return_id); acpi_status acpi_ut_execute_UID(struct acpi_namespace_node *device_node, - struct acpica_device_id *uid); + struct acpica_device_id **return_id); acpi_status -acpi_ut_execute_sxds(struct acpi_namespace_node *device_node, u8 * highest); +acpi_ut_execute_CID(struct acpi_namespace_node *device_node, + struct acpica_device_id_list **return_cid_list); /* * utlock - reader/writer locks @@ -445,6 +449,8 @@ acpi_ut_short_divide(acpi_integer in_dividend, */ const char *acpi_ut_validate_exception(acpi_status status); +u8 acpi_ut_is_pci_root_bridge(char *id); + u8 acpi_ut_is_aml_table(struct acpi_table_header *table); acpi_status acpi_ut_allocate_owner_id(acpi_owner_id * owner_id); @@ -469,6 +475,12 @@ u8 acpi_ut_valid_acpi_char(char character, u32 position); acpi_status acpi_ut_strtoul64(char *string, u32 base, acpi_integer * ret_integer); +void ACPI_INTERNAL_VAR_XFACE +acpi_ut_predefined_warning(const char *module_name, + u32 line_number, + char *pathname, + u8 node_flags, const char *format, ...); + /* Values for Base above (16=Hex, 10=Decimal) */ #define ACPI_ANY_BASE 0 diff --git a/drivers/acpi/acpica/amlcode.h b/drivers/acpi/acpica/amlcode.h index 067f967eb389..4940249f2524 100644 --- a/drivers/acpi/acpica/amlcode.h +++ b/drivers/acpi/acpica/amlcode.h @@ -404,6 +404,7 @@ typedef enum { REGION_SMBUS, REGION_CMOS, REGION_PCI_BAR, + REGION_IPMI, REGION_DATA_TABLE, /* Internal use only */ REGION_FIXED_HW = 0x7F } AML_REGION_TYPES; diff --git a/drivers/acpi/acpica/dsfield.c b/drivers/acpi/acpica/dsfield.c index 53e27bc5a734..54a225e56a64 100644 --- a/drivers/acpi/acpica/dsfield.c +++ b/drivers/acpi/acpica/dsfield.c @@ -123,9 +123,12 @@ acpi_ds_create_buffer_field(union acpi_parse_object *op, flags = ACPI_NS_NO_UPSEARCH | ACPI_NS_DONT_OPEN_SCOPE | ACPI_NS_ERROR_IF_FOUND; - /* Mark node temporary if we are executing a method */ - - if (walk_state->method_node) { + /* + * Mark node temporary if we are executing a normal control + * method. (Don't mark if this is a module-level code method) + */ + if (walk_state->method_node && + !(walk_state->parse_flags & ACPI_PARSE_MODULE_LEVEL)) { flags |= ACPI_NS_TEMPORARY; } @@ -456,9 +459,12 @@ acpi_ds_init_field_objects(union acpi_parse_object *op, flags = ACPI_NS_NO_UPSEARCH | ACPI_NS_DONT_OPEN_SCOPE | ACPI_NS_ERROR_IF_FOUND; - /* Mark node(s) temporary if we are executing a method */ - - if (walk_state->method_node) { + /* + * Mark node(s) temporary if we are executing a normal control + * method. (Don't mark if this is a module-level code method) + */ + if (walk_state->method_node && + !(walk_state->parse_flags & ACPI_PARSE_MODULE_LEVEL)) { flags |= ACPI_NS_TEMPORARY; } diff --git a/drivers/acpi/acpica/dsmethod.c b/drivers/acpi/acpica/dsmethod.c index 14b8b8ed8023..567a4899a018 100644 --- a/drivers/acpi/acpica/dsmethod.c +++ b/drivers/acpi/acpica/dsmethod.c @@ -578,10 +578,15 @@ acpi_ds_terminate_control_method(union acpi_operand_object *method_desc, } /* - * Delete any namespace objects created anywhere within - * the namespace by the execution of this method + * Delete any namespace objects created anywhere within the + * namespace by the execution of this method. Unless this method + * is a module-level executable code method, in which case we + * want make the objects permanent. */ - acpi_ns_delete_namespace_by_owner(method_desc->method.owner_id); + if (!(method_desc->method.flags & AOPOBJ_MODULE_LEVEL)) { + acpi_ns_delete_namespace_by_owner(method_desc->method. + owner_id); + } } /* Decrement the thread count on the method */ @@ -622,7 +627,9 @@ acpi_ds_terminate_control_method(union acpi_operand_object *method_desc, /* No more threads, we can free the owner_id */ - acpi_ut_release_owner_id(&method_desc->method.owner_id); + if (!(method_desc->method.flags & AOPOBJ_MODULE_LEVEL)) { + acpi_ut_release_owner_id(&method_desc->method.owner_id); + } } return_VOID; diff --git a/drivers/acpi/acpica/dsmthdat.c b/drivers/acpi/acpica/dsmthdat.c index 22b1a3ce2c94..7d077bb2f525 100644 --- a/drivers/acpi/acpica/dsmthdat.c +++ b/drivers/acpi/acpica/dsmthdat.c @@ -433,10 +433,10 @@ acpi_ds_method_data_get_value(u8 type, case ACPI_REFCLASS_LOCAL: - ACPI_ERROR((AE_INFO, - "Uninitialized Local[%d] at node %p", - index, node)); - + /* + * No error message for this case, will be trapped again later to + * detect and ignore cases of Store(local_x,local_x) + */ return_ACPI_STATUS(AE_AML_UNINITIALIZED_LOCAL); default: diff --git a/drivers/acpi/acpica/dsobject.c b/drivers/acpi/acpica/dsobject.c index 02e6caad4a76..507e1f0bbdfd 100644 --- a/drivers/acpi/acpica/dsobject.c +++ b/drivers/acpi/acpica/dsobject.c @@ -482,14 +482,27 @@ acpi_ds_build_internal_package_obj(struct acpi_walk_state *walk_state, if (arg) { /* * num_elements was exhausted, but there are remaining elements in the - * package_list. + * package_list. Truncate the package to num_elements. * * Note: technically, this is an error, from ACPI spec: "It is an error * for NumElements to be less than the number of elements in the - * PackageList". However, for now, we just print an error message and - * no exception is returned. + * PackageList". However, we just print an error message and + * no exception is returned. This provides Windows compatibility. Some + * BIOSs will alter the num_elements on the fly, creating this type + * of ill-formed package object. */ while (arg) { + /* + * We must delete any package elements that were created earlier + * and are not going to be used because of the package truncation. + */ + if (arg->common.node) { + acpi_ut_remove_reference(ACPI_CAST_PTR + (union + acpi_operand_object, + arg->common.node)); + arg->common.node = NULL; + } /* Find out how many elements there really are */ @@ -498,7 +511,7 @@ acpi_ds_build_internal_package_obj(struct acpi_walk_state *walk_state, } ACPI_WARNING((AE_INFO, - "Package List length (%X) larger than NumElements count (%X), truncated\n", + "Package List length (0x%X) larger than NumElements count (0x%X), truncated\n", i, element_count)); } else if (i < element_count) { /* @@ -506,7 +519,7 @@ acpi_ds_build_internal_package_obj(struct acpi_walk_state *walk_state, * Note: this is not an error, the package is padded out with NULLs. */ ACPI_DEBUG_PRINT((ACPI_DB_INFO, - "Package List length (%X) smaller than NumElements count (%X), padded with null elements\n", + "Package List length (0x%X) smaller than NumElements count (0x%X), padded with null elements\n", i, element_count)); } diff --git a/drivers/acpi/acpica/dswload.c b/drivers/acpi/acpica/dswload.c index 3023ceaa8d54..6de3a99d4cd4 100644 --- a/drivers/acpi/acpica/dswload.c +++ b/drivers/acpi/acpica/dswload.c @@ -581,21 +581,6 @@ acpi_ds_load2_begin_op(struct acpi_walk_state *walk_state, if ((!(walk_state->op_info->flags & AML_NSOPCODE) && (walk_state->opcode != AML_INT_NAMEPATH_OP)) || (!(walk_state->op_info->flags & AML_NAMED))) { -#ifdef ACPI_ENABLE_MODULE_LEVEL_CODE - if ((walk_state->op_info->class == AML_CLASS_EXECUTE) || - (walk_state->op_info->class == AML_CLASS_CONTROL)) { - ACPI_DEBUG_PRINT((ACPI_DB_DISPATCH, - "Begin/EXEC: %s (fl %8.8X)\n", - walk_state->op_info->name, - walk_state->op_info->flags)); - - /* Executing a type1 or type2 opcode outside of a method */ - - status = - acpi_ds_exec_begin_op(walk_state, out_op); - return_ACPI_STATUS(status); - } -#endif return_ACPI_STATUS(AE_OK); } @@ -768,7 +753,13 @@ acpi_ds_load2_begin_op(struct acpi_walk_state *walk_state, /* Execution mode, node cannot already exist, node is temporary */ - flags |= (ACPI_NS_ERROR_IF_FOUND | ACPI_NS_TEMPORARY); + flags |= ACPI_NS_ERROR_IF_FOUND; + + if (! + (walk_state-> + parse_flags & ACPI_PARSE_MODULE_LEVEL)) { + flags |= ACPI_NS_TEMPORARY; + } } /* Add new entry or lookup existing entry */ @@ -851,24 +842,6 @@ acpi_status acpi_ds_load2_end_op(struct acpi_walk_state *walk_state) /* Check if opcode had an associated namespace object */ if (!(walk_state->op_info->flags & AML_NSOBJECT)) { -#ifndef ACPI_NO_METHOD_EXECUTION -#ifdef ACPI_ENABLE_MODULE_LEVEL_CODE - /* No namespace object. Executable opcode? */ - - if ((walk_state->op_info->class == AML_CLASS_EXECUTE) || - (walk_state->op_info->class == AML_CLASS_CONTROL)) { - ACPI_DEBUG_PRINT((ACPI_DB_DISPATCH, - "End/EXEC: %s (fl %8.8X)\n", - walk_state->op_info->name, - walk_state->op_info->flags)); - - /* Executing a type1 or type2 opcode outside of a method */ - - status = acpi_ds_exec_end_op(walk_state); - return_ACPI_STATUS(status); - } -#endif -#endif return_ACPI_STATUS(AE_OK); } diff --git a/drivers/acpi/acpica/evgpe.c b/drivers/acpi/acpica/evgpe.c index b9d8ee69ca6c..afacf4416c73 100644 --- a/drivers/acpi/acpica/evgpe.c +++ b/drivers/acpi/acpica/evgpe.c @@ -424,8 +424,8 @@ u32 acpi_ev_gpe_detect(struct acpi_gpe_xrupt_info * gpe_xrupt_list) /* Read the Status Register */ status = - acpi_read(&status_reg, - &gpe_register_info->status_address); + acpi_hw_read(&status_reg, + &gpe_register_info->status_address); if (ACPI_FAILURE(status)) { goto unlock_and_exit; } @@ -433,8 +433,8 @@ u32 acpi_ev_gpe_detect(struct acpi_gpe_xrupt_info * gpe_xrupt_list) /* Read the Enable Register */ status = - acpi_read(&enable_reg, - &gpe_register_info->enable_address); + acpi_hw_read(&enable_reg, + &gpe_register_info->enable_address); if (ACPI_FAILURE(status)) { goto unlock_and_exit; } diff --git a/drivers/acpi/acpica/evgpeblk.c b/drivers/acpi/acpica/evgpeblk.c index 7b3463639422..a60aaa7635f3 100644 --- a/drivers/acpi/acpica/evgpeblk.c +++ b/drivers/acpi/acpica/evgpeblk.c @@ -843,14 +843,14 @@ acpi_ev_create_gpe_info_blocks(struct acpi_gpe_block_info *gpe_block) /* Disable all GPEs within this register */ - status = acpi_write(0x00, &this_register->enable_address); + status = acpi_hw_write(0x00, &this_register->enable_address); if (ACPI_FAILURE(status)) { goto error_exit; } /* Clear any pending GPE events within this register */ - status = acpi_write(0xFF, &this_register->status_address); + status = acpi_hw_write(0xFF, &this_register->status_address); if (ACPI_FAILURE(status)) { goto error_exit; } diff --git a/drivers/acpi/acpica/evrgnini.c b/drivers/acpi/acpica/evrgnini.c index 284a7becbe96..cf29c4953028 100644 --- a/drivers/acpi/acpica/evrgnini.c +++ b/drivers/acpi/acpica/evrgnini.c @@ -50,8 +50,6 @@ ACPI_MODULE_NAME("evrgnini") /* Local prototypes */ -static u8 acpi_ev_match_pci_root_bridge(char *id); - static u8 acpi_ev_is_pci_root_bridge(struct acpi_namespace_node *node); /******************************************************************************* @@ -332,37 +330,6 @@ acpi_ev_pci_config_region_setup(acpi_handle handle, /******************************************************************************* * - * FUNCTION: acpi_ev_match_pci_root_bridge - * - * PARAMETERS: Id - The HID/CID in string format - * - * RETURN: TRUE if the Id is a match for a PCI/PCI-Express Root Bridge - * - * DESCRIPTION: Determine if the input ID is a PCI Root Bridge ID. - * - ******************************************************************************/ - -static u8 acpi_ev_match_pci_root_bridge(char *id) -{ - - /* - * Check if this is a PCI root. - * ACPI 3.0+: check for a PCI Express root also. - */ - if (!(ACPI_STRNCMP(id, - PCI_ROOT_HID_STRING, - sizeof(PCI_ROOT_HID_STRING))) || - !(ACPI_STRNCMP(id, - PCI_EXPRESS_ROOT_HID_STRING, - sizeof(PCI_EXPRESS_ROOT_HID_STRING)))) { - return (TRUE); - } - - return (FALSE); -} - -/******************************************************************************* - * * FUNCTION: acpi_ev_is_pci_root_bridge * * PARAMETERS: Node - Device node being examined @@ -377,9 +344,10 @@ static u8 acpi_ev_match_pci_root_bridge(char *id) static u8 acpi_ev_is_pci_root_bridge(struct acpi_namespace_node *node) { acpi_status status; - struct acpica_device_id hid; - struct acpi_compatible_id_list *cid; + struct acpica_device_id *hid; + struct acpica_device_id_list *cid; u32 i; + u8 match; /* Get the _HID and check for a PCI Root Bridge */ @@ -388,7 +356,10 @@ static u8 acpi_ev_is_pci_root_bridge(struct acpi_namespace_node *node) return (FALSE); } - if (acpi_ev_match_pci_root_bridge(hid.value)) { + match = acpi_ut_is_pci_root_bridge(hid->string); + ACPI_FREE(hid); + + if (match) { return (TRUE); } @@ -402,7 +373,7 @@ static u8 acpi_ev_is_pci_root_bridge(struct acpi_namespace_node *node) /* Check all _CIDs in the returned list */ for (i = 0; i < cid->count; i++) { - if (acpi_ev_match_pci_root_bridge(cid->id[i].value)) { + if (acpi_ut_is_pci_root_bridge(cid->ids[i].string)) { ACPI_FREE(cid); return (TRUE); } diff --git a/drivers/acpi/acpica/exconfig.c b/drivers/acpi/acpica/exconfig.c index 277fd609611a..24afef81af39 100644 --- a/drivers/acpi/acpica/exconfig.c +++ b/drivers/acpi/acpica/exconfig.c @@ -110,8 +110,15 @@ acpi_ex_add_table(u32 table_index, if (ACPI_FAILURE(status)) { acpi_ut_remove_reference(obj_desc); *ddb_handle = NULL; + return_ACPI_STATUS(status); } + /* Execute any module-level code that was found in the table */ + + acpi_ex_exit_interpreter(); + acpi_ns_exec_module_code_list(); + acpi_ex_enter_interpreter(); + return_ACPI_STATUS(status); } diff --git a/drivers/acpi/acpica/exdump.c b/drivers/acpi/acpica/exdump.c index ec524614e708..de3446372ddc 100644 --- a/drivers/acpi/acpica/exdump.c +++ b/drivers/acpi/acpica/exdump.c @@ -418,9 +418,9 @@ acpi_ex_dump_object(union acpi_operand_object *obj_desc, case ACPI_EXD_REFERENCE: acpi_ex_out_string("Class Name", - (char *) - acpi_ut_get_reference_name - (obj_desc)); + ACPI_CAST_PTR(char, + acpi_ut_get_reference_name + (obj_desc))); acpi_ex_dump_reference_obj(obj_desc); break; diff --git a/drivers/acpi/acpica/exfield.c b/drivers/acpi/acpica/exfield.c index 546dcdd86785..0b33d6c887b9 100644 --- a/drivers/acpi/acpica/exfield.c +++ b/drivers/acpi/acpica/exfield.c @@ -72,6 +72,7 @@ acpi_ex_read_data_from_field(struct acpi_walk_state *walk_state, union acpi_operand_object *buffer_desc; acpi_size length; void *buffer; + u32 function; ACPI_FUNCTION_TRACE_PTR(ex_read_data_from_field, obj_desc); @@ -97,13 +98,27 @@ acpi_ex_read_data_from_field(struct acpi_walk_state *walk_state, } } else if ((obj_desc->common.type == ACPI_TYPE_LOCAL_REGION_FIELD) && (obj_desc->field.region_obj->region.space_id == - ACPI_ADR_SPACE_SMBUS)) { + ACPI_ADR_SPACE_SMBUS + || obj_desc->field.region_obj->region.space_id == + ACPI_ADR_SPACE_IPMI)) { /* - * This is an SMBus read. We must create a buffer to hold the data - * and directly access the region handler. + * This is an SMBus or IPMI read. We must create a buffer to hold + * the data and then directly access the region handler. + * + * Note: Smbus protocol value is passed in upper 16-bits of Function */ - buffer_desc = - acpi_ut_create_buffer_object(ACPI_SMBUS_BUFFER_SIZE); + if (obj_desc->field.region_obj->region.space_id == + ACPI_ADR_SPACE_SMBUS) { + length = ACPI_SMBUS_BUFFER_SIZE; + function = + ACPI_READ | (obj_desc->field.attribute << 16); + } else { /* IPMI */ + + length = ACPI_IPMI_BUFFER_SIZE; + function = ACPI_READ; + } + + buffer_desc = acpi_ut_create_buffer_object(length); if (!buffer_desc) { return_ACPI_STATUS(AE_NO_MEMORY); } @@ -112,16 +127,13 @@ acpi_ex_read_data_from_field(struct acpi_walk_state *walk_state, acpi_ex_acquire_global_lock(obj_desc->common_field.field_flags); - /* - * Perform the read. - * Note: Smbus protocol value is passed in upper 16-bits of Function - */ + /* Call the region handler for the read */ + status = acpi_ex_access_region(obj_desc, 0, ACPI_CAST_PTR(acpi_integer, buffer_desc-> buffer.pointer), - ACPI_READ | (obj_desc->field. - attribute << 16)); + function); acpi_ex_release_global_lock(obj_desc->common_field.field_flags); goto exit; } @@ -212,6 +224,7 @@ acpi_ex_write_data_to_field(union acpi_operand_object *source_desc, u32 length; void *buffer; union acpi_operand_object *buffer_desc; + u32 function; ACPI_FUNCTION_TRACE_PTR(ex_write_data_to_field, obj_desc); @@ -234,39 +247,56 @@ acpi_ex_write_data_to_field(union acpi_operand_object *source_desc, } } else if ((obj_desc->common.type == ACPI_TYPE_LOCAL_REGION_FIELD) && (obj_desc->field.region_obj->region.space_id == - ACPI_ADR_SPACE_SMBUS)) { + ACPI_ADR_SPACE_SMBUS + || obj_desc->field.region_obj->region.space_id == + ACPI_ADR_SPACE_IPMI)) { /* - * This is an SMBus write. We will bypass the entire field mechanism - * and handoff the buffer directly to the handler. + * This is an SMBus or IPMI write. We will bypass the entire field + * mechanism and handoff the buffer directly to the handler. For + * these address spaces, the buffer is bi-directional; on a write, + * return data is returned in the same buffer. + * + * Source must be a buffer of sufficient size: + * ACPI_SMBUS_BUFFER_SIZE or ACPI_IPMI_BUFFER_SIZE. * - * Source must be a buffer of sufficient size (ACPI_SMBUS_BUFFER_SIZE). + * Note: SMBus protocol type is passed in upper 16-bits of Function */ if (source_desc->common.type != ACPI_TYPE_BUFFER) { ACPI_ERROR((AE_INFO, - "SMBus write requires Buffer, found type %s", + "SMBus or IPMI write requires Buffer, found type %s", acpi_ut_get_object_type_name(source_desc))); return_ACPI_STATUS(AE_AML_OPERAND_TYPE); } - if (source_desc->buffer.length < ACPI_SMBUS_BUFFER_SIZE) { + if (obj_desc->field.region_obj->region.space_id == + ACPI_ADR_SPACE_SMBUS) { + length = ACPI_SMBUS_BUFFER_SIZE; + function = + ACPI_WRITE | (obj_desc->field.attribute << 16); + } else { /* IPMI */ + + length = ACPI_IPMI_BUFFER_SIZE; + function = ACPI_WRITE; + } + + if (source_desc->buffer.length < length) { ACPI_ERROR((AE_INFO, - "SMBus write requires Buffer of length %X, found length %X", - ACPI_SMBUS_BUFFER_SIZE, - source_desc->buffer.length)); + "SMBus or IPMI write requires Buffer of length %X, found length %X", + length, source_desc->buffer.length)); return_ACPI_STATUS(AE_AML_BUFFER_LIMIT); } - buffer_desc = - acpi_ut_create_buffer_object(ACPI_SMBUS_BUFFER_SIZE); + /* Create the bi-directional buffer */ + + buffer_desc = acpi_ut_create_buffer_object(length); if (!buffer_desc) { return_ACPI_STATUS(AE_NO_MEMORY); } buffer = buffer_desc->buffer.pointer; - ACPI_MEMCPY(buffer, source_desc->buffer.pointer, - ACPI_SMBUS_BUFFER_SIZE); + ACPI_MEMCPY(buffer, source_desc->buffer.pointer, length); /* Lock entire transaction if requested */ @@ -275,12 +305,10 @@ acpi_ex_write_data_to_field(union acpi_operand_object *source_desc, /* * Perform the write (returns status and perhaps data in the * same buffer) - * Note: SMBus protocol type is passed in upper 16-bits of Function. */ status = acpi_ex_access_region(obj_desc, 0, (acpi_integer *) buffer, - ACPI_WRITE | (obj_desc->field. - attribute << 16)); + function); acpi_ex_release_global_lock(obj_desc->common_field.field_flags); *result_desc = buffer_desc; diff --git a/drivers/acpi/acpica/exfldio.c b/drivers/acpi/acpica/exfldio.c index 6687be167f5f..d7b3b418fb45 100644 --- a/drivers/acpi/acpica/exfldio.c +++ b/drivers/acpi/acpica/exfldio.c @@ -120,12 +120,13 @@ acpi_ex_setup_region(union acpi_operand_object *obj_desc, } /* - * Exit now for SMBus address space, it has a non-linear address space + * Exit now for SMBus or IPMI address space, it has a non-linear address space * and the request cannot be directly validated */ - if (rgn_desc->region.space_id == ACPI_ADR_SPACE_SMBUS) { + if (rgn_desc->region.space_id == ACPI_ADR_SPACE_SMBUS || + rgn_desc->region.space_id == ACPI_ADR_SPACE_IPMI) { - /* SMBus has a non-linear address space */ + /* SMBus or IPMI has a non-linear address space */ return_ACPI_STATUS(AE_OK); } diff --git a/drivers/acpi/acpica/exregion.c b/drivers/acpi/acpica/exregion.c index 3a54b737d2da..2bd83ac57c3a 100644 --- a/drivers/acpi/acpica/exregion.c +++ b/drivers/acpi/acpica/exregion.c @@ -77,7 +77,8 @@ acpi_ex_system_memory_space_handler(u32 function, void *logical_addr_ptr = NULL; struct acpi_mem_space_context *mem_info = region_context; u32 length; - acpi_size window_size; + acpi_size map_length; + acpi_size page_boundary_map_length; #ifdef ACPI_MISALIGNMENT_NOT_SUPPORTED u32 remainder; #endif @@ -144,25 +145,39 @@ acpi_ex_system_memory_space_handler(u32 function, } /* - * Don't attempt to map memory beyond the end of the region, and - * constrain the maximum mapping size to something reasonable. + * Attempt to map from the requested address to the end of the region. + * However, we will never map more than one page, nor will we cross + * a page boundary. */ - window_size = (acpi_size) + map_length = (acpi_size) ((mem_info->address + mem_info->length) - address); - if (window_size > ACPI_SYSMEM_REGION_WINDOW_SIZE) { - window_size = ACPI_SYSMEM_REGION_WINDOW_SIZE; + /* + * If mapping the entire remaining portion of the region will cross + * a page boundary, just map up to the page boundary, do not cross. + * On some systems, crossing a page boundary while mapping regions + * can cause warnings if the pages have different attributes + * due to resource management + */ + page_boundary_map_length = + ACPI_ROUND_UP(address, ACPI_DEFAULT_PAGE_SIZE) - address; + + if (!page_boundary_map_length) { + page_boundary_map_length = ACPI_DEFAULT_PAGE_SIZE; + } + + if (map_length > page_boundary_map_length) { + map_length = page_boundary_map_length; } /* Create a new mapping starting at the address given */ - mem_info->mapped_logical_address = - acpi_os_map_memory((acpi_physical_address) address, window_size); + mem_info->mapped_logical_address = acpi_os_map_memory((acpi_physical_address) address, map_length); if (!mem_info->mapped_logical_address) { ACPI_ERROR((AE_INFO, "Could not map memory at %8.8X%8.8X, size %X", ACPI_FORMAT_NATIVE_UINT(address), - (u32) window_size)); + (u32) map_length)); mem_info->mapped_length = 0; return_ACPI_STATUS(AE_NO_MEMORY); } @@ -170,7 +185,7 @@ acpi_ex_system_memory_space_handler(u32 function, /* Save the physical address and mapping size */ mem_info->mapped_physical_address = address; - mem_info->mapped_length = window_size; + mem_info->mapped_length = map_length; } /* diff --git a/drivers/acpi/acpica/exutils.c b/drivers/acpi/acpica/exutils.c index 87730e944132..7d41f99f7052 100644 --- a/drivers/acpi/acpica/exutils.c +++ b/drivers/acpi/acpica/exutils.c @@ -358,50 +358,67 @@ static u32 acpi_ex_digits_needed(acpi_integer value, u32 base) * * FUNCTION: acpi_ex_eisa_id_to_string * - * PARAMETERS: numeric_id - EISA ID to be converted + * PARAMETERS: compressed_id - EISAID to be converted * out_string - Where to put the converted string (8 bytes) * * RETURN: None * - * DESCRIPTION: Convert a numeric EISA ID to string representation + * DESCRIPTION: Convert a numeric EISAID to string representation. Return + * buffer must be large enough to hold the string. The string + * returned is always exactly of length ACPI_EISAID_STRING_SIZE + * (includes null terminator). The EISAID is always 32 bits. * ******************************************************************************/ -void acpi_ex_eisa_id_to_string(u32 numeric_id, char *out_string) +void acpi_ex_eisa_id_to_string(char *out_string, acpi_integer compressed_id) { - u32 eisa_id; + u32 swapped_id; ACPI_FUNCTION_ENTRY(); + /* The EISAID should be a 32-bit integer */ + + if (compressed_id > ACPI_UINT32_MAX) { + ACPI_WARNING((AE_INFO, + "Expected EISAID is larger than 32 bits: 0x%8.8X%8.8X, truncating", + ACPI_FORMAT_UINT64(compressed_id))); + } + /* Swap ID to big-endian to get contiguous bits */ - eisa_id = acpi_ut_dword_byte_swap(numeric_id); + swapped_id = acpi_ut_dword_byte_swap((u32)compressed_id); - out_string[0] = (char)('@' + (((unsigned long)eisa_id >> 26) & 0x1f)); - out_string[1] = (char)('@' + ((eisa_id >> 21) & 0x1f)); - out_string[2] = (char)('@' + ((eisa_id >> 16) & 0x1f)); - out_string[3] = acpi_ut_hex_to_ascii_char((acpi_integer) eisa_id, 12); - out_string[4] = acpi_ut_hex_to_ascii_char((acpi_integer) eisa_id, 8); - out_string[5] = acpi_ut_hex_to_ascii_char((acpi_integer) eisa_id, 4); - out_string[6] = acpi_ut_hex_to_ascii_char((acpi_integer) eisa_id, 0); + /* First 3 bytes are uppercase letters. Next 4 bytes are hexadecimal */ + + out_string[0] = + (char)(0x40 + (((unsigned long)swapped_id >> 26) & 0x1F)); + out_string[1] = (char)(0x40 + ((swapped_id >> 21) & 0x1F)); + out_string[2] = (char)(0x40 + ((swapped_id >> 16) & 0x1F)); + out_string[3] = acpi_ut_hex_to_ascii_char((acpi_integer)swapped_id, 12); + out_string[4] = acpi_ut_hex_to_ascii_char((acpi_integer)swapped_id, 8); + out_string[5] = acpi_ut_hex_to_ascii_char((acpi_integer)swapped_id, 4); + out_string[6] = acpi_ut_hex_to_ascii_char((acpi_integer)swapped_id, 0); out_string[7] = 0; } /******************************************************************************* * - * FUNCTION: acpi_ex_unsigned_integer_to_string + * FUNCTION: acpi_ex_integer_to_string * - * PARAMETERS: Value - Value to be converted - * out_string - Where to put the converted string (8 bytes) + * PARAMETERS: out_string - Where to put the converted string. At least + * 21 bytes are needed to hold the largest + * possible 64-bit integer. + * Value - Value to be converted * * RETURN: None, string * - * DESCRIPTION: Convert a number to string representation. Assumes string - * buffer is large enough to hold the string. + * DESCRIPTION: Convert a 64-bit integer to decimal string representation. + * Assumes string buffer is large enough to hold the string. The + * largest string is (ACPI_MAX64_DECIMAL_DIGITS + 1). * ******************************************************************************/ -void acpi_ex_unsigned_integer_to_string(acpi_integer value, char *out_string) +void acpi_ex_integer_to_string(char *out_string, acpi_integer value) { u32 count; u32 digits_needed; diff --git a/drivers/acpi/acpica/hwgpe.c b/drivers/acpi/acpica/hwgpe.c index d3b7e37c9eed..c28c41b3180b 100644 --- a/drivers/acpi/acpica/hwgpe.c +++ b/drivers/acpi/acpica/hwgpe.c @@ -82,7 +82,7 @@ acpi_status acpi_hw_low_disable_gpe(struct acpi_gpe_event_info *gpe_event_info) /* Get current value of the enable register that contains this GPE */ - status = acpi_read(&enable_mask, &gpe_register_info->enable_address); + status = acpi_hw_read(&enable_mask, &gpe_register_info->enable_address); if (ACPI_FAILURE(status)) { return (status); } @@ -95,7 +95,7 @@ acpi_status acpi_hw_low_disable_gpe(struct acpi_gpe_event_info *gpe_event_info) /* Write the updated enable mask */ - status = acpi_write(enable_mask, &gpe_register_info->enable_address); + status = acpi_hw_write(enable_mask, &gpe_register_info->enable_address); return (status); } @@ -130,8 +130,8 @@ acpi_hw_write_gpe_enable_reg(struct acpi_gpe_event_info * gpe_event_info) /* Write the entire GPE (runtime) enable register */ - status = acpi_write(gpe_register_info->enable_for_run, - &gpe_register_info->enable_address); + status = acpi_hw_write(gpe_register_info->enable_for_run, + &gpe_register_info->enable_address); return (status); } @@ -163,8 +163,8 @@ acpi_status acpi_hw_clear_gpe(struct acpi_gpe_event_info * gpe_event_info) * Write a one to the appropriate bit in the status register to * clear this GPE. */ - status = acpi_write(register_bit, - &gpe_event_info->register_info->status_address); + status = acpi_hw_write(register_bit, + &gpe_event_info->register_info->status_address); return (status); } @@ -222,7 +222,7 @@ acpi_hw_get_gpe_status(struct acpi_gpe_event_info * gpe_event_info, /* GPE currently active (status bit == 1)? */ - status = acpi_read(&in_byte, &gpe_register_info->status_address); + status = acpi_hw_read(&in_byte, &gpe_register_info->status_address); if (ACPI_FAILURE(status)) { goto unlock_and_exit; } @@ -266,8 +266,8 @@ acpi_hw_disable_gpe_block(struct acpi_gpe_xrupt_info *gpe_xrupt_info, /* Disable all GPEs in this register */ status = - acpi_write(0x00, - &gpe_block->register_info[i].enable_address); + acpi_hw_write(0x00, + &gpe_block->register_info[i].enable_address); if (ACPI_FAILURE(status)) { return (status); } @@ -303,8 +303,8 @@ acpi_hw_clear_gpe_block(struct acpi_gpe_xrupt_info *gpe_xrupt_info, /* Clear status on all GPEs in this register */ status = - acpi_write(0xFF, - &gpe_block->register_info[i].status_address); + acpi_hw_write(0xFF, + &gpe_block->register_info[i].status_address); if (ACPI_FAILURE(status)) { return (status); } @@ -345,9 +345,9 @@ acpi_hw_enable_runtime_gpe_block(struct acpi_gpe_xrupt_info *gpe_xrupt_info, /* Enable all "runtime" GPEs in this register */ - status = acpi_write(gpe_block->register_info[i].enable_for_run, - &gpe_block->register_info[i]. - enable_address); + status = + acpi_hw_write(gpe_block->register_info[i].enable_for_run, + &gpe_block->register_info[i].enable_address); if (ACPI_FAILURE(status)) { return (status); } @@ -387,9 +387,9 @@ acpi_hw_enable_wakeup_gpe_block(struct acpi_gpe_xrupt_info *gpe_xrupt_info, /* Enable all "wake" GPEs in this register */ - status = acpi_write(gpe_block->register_info[i].enable_for_wake, - &gpe_block->register_info[i]. - enable_address); + status = + acpi_hw_write(gpe_block->register_info[i].enable_for_wake, + &gpe_block->register_info[i].enable_address); if (ACPI_FAILURE(status)) { return (status); } diff --git a/drivers/acpi/acpica/hwregs.c b/drivers/acpi/acpica/hwregs.c index 23d5505cb1f7..15c9ed2be853 100644 --- a/drivers/acpi/acpica/hwregs.c +++ b/drivers/acpi/acpica/hwregs.c @@ -62,6 +62,184 @@ acpi_hw_write_multiple(u32 value, struct acpi_generic_address *register_a, struct acpi_generic_address *register_b); +/****************************************************************************** + * + * FUNCTION: acpi_hw_validate_register + * + * PARAMETERS: Reg - GAS register structure + * max_bit_width - Max bit_width supported (32 or 64) + * Address - Pointer to where the gas->address + * is returned + * + * RETURN: Status + * + * DESCRIPTION: Validate the contents of a GAS register. Checks the GAS + * pointer, Address, space_id, bit_width, and bit_offset. + * + ******************************************************************************/ + +acpi_status +acpi_hw_validate_register(struct acpi_generic_address *reg, + u8 max_bit_width, u64 *address) +{ + + /* Must have a valid pointer to a GAS structure */ + + if (!reg) { + return (AE_BAD_PARAMETER); + } + + /* + * Copy the target address. This handles possible alignment issues. + * Address must not be null. A null address also indicates an optional + * ACPI register that is not supported, so no error message. + */ + ACPI_MOVE_64_TO_64(address, ®->address); + if (!(*address)) { + return (AE_BAD_ADDRESS); + } + + /* Validate the space_iD */ + + if ((reg->space_id != ACPI_ADR_SPACE_SYSTEM_MEMORY) && + (reg->space_id != ACPI_ADR_SPACE_SYSTEM_IO)) { + ACPI_ERROR((AE_INFO, + "Unsupported address space: 0x%X", reg->space_id)); + return (AE_SUPPORT); + } + + /* Validate the bit_width */ + + if ((reg->bit_width != 8) && + (reg->bit_width != 16) && + (reg->bit_width != 32) && (reg->bit_width != max_bit_width)) { + ACPI_ERROR((AE_INFO, + "Unsupported register bit width: 0x%X", + reg->bit_width)); + return (AE_SUPPORT); + } + + /* Validate the bit_offset. Just a warning for now. */ + + if (reg->bit_offset != 0) { + ACPI_WARNING((AE_INFO, + "Unsupported register bit offset: 0x%X", + reg->bit_offset)); + } + + return (AE_OK); +} + +/****************************************************************************** + * + * FUNCTION: acpi_hw_read + * + * PARAMETERS: Value - Where the value is returned + * Reg - GAS register structure + * + * RETURN: Status + * + * DESCRIPTION: Read from either memory or IO space. This is a 32-bit max + * version of acpi_read, used internally since the overhead of + * 64-bit values is not needed. + * + * LIMITATIONS: <These limitations also apply to acpi_hw_write> + * bit_width must be exactly 8, 16, or 32. + * space_iD must be system_memory or system_iO. + * bit_offset and access_width are currently ignored, as there has + * not been a need to implement these. + * + ******************************************************************************/ + +acpi_status acpi_hw_read(u32 *value, struct acpi_generic_address *reg) +{ + u64 address; + acpi_status status; + + ACPI_FUNCTION_NAME(hw_read); + + /* Validate contents of the GAS register */ + + status = acpi_hw_validate_register(reg, 32, &address); + if (ACPI_FAILURE(status)) { + return (status); + } + + /* Initialize entire 32-bit return value to zero */ + + *value = 0; + + /* + * Two address spaces supported: Memory or IO. PCI_Config is + * not supported here because the GAS structure is insufficient + */ + if (reg->space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY) { + status = acpi_os_read_memory((acpi_physical_address) + address, value, reg->bit_width); + } else { /* ACPI_ADR_SPACE_SYSTEM_IO, validated earlier */ + + status = acpi_hw_read_port((acpi_io_address) + address, value, reg->bit_width); + } + + ACPI_DEBUG_PRINT((ACPI_DB_IO, + "Read: %8.8X width %2d from %8.8X%8.8X (%s)\n", + *value, reg->bit_width, ACPI_FORMAT_UINT64(address), + acpi_ut_get_region_name(reg->space_id))); + + return (status); +} + +/****************************************************************************** + * + * FUNCTION: acpi_hw_write + * + * PARAMETERS: Value - Value to be written + * Reg - GAS register structure + * + * RETURN: Status + * + * DESCRIPTION: Write to either memory or IO space. This is a 32-bit max + * version of acpi_write, used internally since the overhead of + * 64-bit values is not needed. + * + ******************************************************************************/ + +acpi_status acpi_hw_write(u32 value, struct acpi_generic_address *reg) +{ + u64 address; + acpi_status status; + + ACPI_FUNCTION_NAME(hw_write); + + /* Validate contents of the GAS register */ + + status = acpi_hw_validate_register(reg, 32, &address); + if (ACPI_FAILURE(status)) { + return (status); + } + + /* + * Two address spaces supported: Memory or IO. PCI_Config is + * not supported here because the GAS structure is insufficient + */ + if (reg->space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY) { + status = acpi_os_write_memory((acpi_physical_address) + address, value, reg->bit_width); + } else { /* ACPI_ADR_SPACE_SYSTEM_IO, validated earlier */ + + status = acpi_hw_write_port((acpi_io_address) + address, value, reg->bit_width); + } + + ACPI_DEBUG_PRINT((ACPI_DB_IO, + "Wrote: %8.8X width %2d to %8.8X%8.8X (%s)\n", + value, reg->bit_width, ACPI_FORMAT_UINT64(address), + acpi_ut_get_region_name(reg->space_id))); + + return (status); +} + /******************************************************************************* * * FUNCTION: acpi_hw_clear_acpi_status @@ -152,15 +330,16 @@ acpi_status acpi_hw_write_pm1_control(u32 pm1a_control, u32 pm1b_control) ACPI_FUNCTION_TRACE(hw_write_pm1_control); - status = acpi_write(pm1a_control, &acpi_gbl_FADT.xpm1a_control_block); + status = + acpi_hw_write(pm1a_control, &acpi_gbl_FADT.xpm1a_control_block); if (ACPI_FAILURE(status)) { return_ACPI_STATUS(status); } if (acpi_gbl_FADT.xpm1b_control_block.address) { status = - acpi_write(pm1b_control, - &acpi_gbl_FADT.xpm1b_control_block); + acpi_hw_write(pm1b_control, + &acpi_gbl_FADT.xpm1b_control_block); } return_ACPI_STATUS(status); } @@ -218,12 +397,13 @@ acpi_hw_register_read(u32 register_id, u32 * return_value) case ACPI_REGISTER_PM2_CONTROL: /* 8-bit access */ - status = acpi_read(&value, &acpi_gbl_FADT.xpm2_control_block); + status = + acpi_hw_read(&value, &acpi_gbl_FADT.xpm2_control_block); break; case ACPI_REGISTER_PM_TIMER: /* 32-bit access */ - status = acpi_read(&value, &acpi_gbl_FADT.xpm_timer_block); + status = acpi_hw_read(&value, &acpi_gbl_FADT.xpm_timer_block); break; case ACPI_REGISTER_SMI_COMMAND_BLOCK: /* 8-bit access */ @@ -340,7 +520,8 @@ acpi_status acpi_hw_register_write(u32 register_id, u32 value) * as per the ACPI spec. */ status = - acpi_read(&read_value, &acpi_gbl_FADT.xpm2_control_block); + acpi_hw_read(&read_value, + &acpi_gbl_FADT.xpm2_control_block); if (ACPI_FAILURE(status)) { goto exit; } @@ -350,12 +531,13 @@ acpi_status acpi_hw_register_write(u32 register_id, u32 value) ACPI_INSERT_BITS(value, ACPI_PM2_CONTROL_PRESERVED_BITS, read_value); - status = acpi_write(value, &acpi_gbl_FADT.xpm2_control_block); + status = + acpi_hw_write(value, &acpi_gbl_FADT.xpm2_control_block); break; case ACPI_REGISTER_PM_TIMER: /* 32-bit access */ - status = acpi_write(value, &acpi_gbl_FADT.xpm_timer_block); + status = acpi_hw_write(value, &acpi_gbl_FADT.xpm_timer_block); break; case ACPI_REGISTER_SMI_COMMAND_BLOCK: /* 8-bit access */ @@ -401,7 +583,7 @@ acpi_hw_read_multiple(u32 *value, /* The first register is always required */ - status = acpi_read(&value_a, register_a); + status = acpi_hw_read(&value_a, register_a); if (ACPI_FAILURE(status)) { return (status); } @@ -409,7 +591,7 @@ acpi_hw_read_multiple(u32 *value, /* Second register is optional */ if (register_b->address) { - status = acpi_read(&value_b, register_b); + status = acpi_hw_read(&value_b, register_b); if (ACPI_FAILURE(status)) { return (status); } @@ -452,7 +634,7 @@ acpi_hw_write_multiple(u32 value, /* The first register is always required */ - status = acpi_write(value, register_a); + status = acpi_hw_write(value, register_a); if (ACPI_FAILURE(status)) { return (status); } @@ -470,7 +652,7 @@ acpi_hw_write_multiple(u32 value, * and writes have no side effects" */ if (register_b->address) { - status = acpi_write(value, register_b); + status = acpi_hw_write(value, register_b); } return (status); diff --git a/drivers/acpi/acpica/hwtimer.c b/drivers/acpi/acpica/hwtimer.c index b7f522c8f023..6b282e85d039 100644 --- a/drivers/acpi/acpica/hwtimer.c +++ b/drivers/acpi/acpica/hwtimer.c @@ -100,7 +100,7 @@ acpi_status acpi_get_timer(u32 * ticks) } status = - acpi_hw_low_level_read(32, ticks, &acpi_gbl_FADT.xpm_timer_block); + acpi_hw_read(ticks, &acpi_gbl_FADT.xpm_timer_block); return_ACPI_STATUS(status); } diff --git a/drivers/acpi/acpica/hwxface.c b/drivers/acpi/acpica/hwxface.c index 9829979f2bdd..647c7b6e6756 100644 --- a/drivers/acpi/acpica/hwxface.c +++ b/drivers/acpi/acpica/hwxface.c @@ -78,9 +78,22 @@ acpi_status acpi_reset(void) return_ACPI_STATUS(AE_NOT_EXIST); } - /* Write the reset value to the reset register */ + if (reset_reg->space_id == ACPI_ADR_SPACE_SYSTEM_IO) { + /* + * For I/O space, write directly to the OSL. This bypasses the port + * validation mechanism, which may block a valid write to the reset + * register. + */ + status = + acpi_os_write_port((acpi_io_address) reset_reg->address, + acpi_gbl_FADT.reset_value, + reset_reg->bit_width); + } else { + /* Write the reset value to the reset register */ + + status = acpi_hw_write(acpi_gbl_FADT.reset_value, reset_reg); + } - status = acpi_write(acpi_gbl_FADT.reset_value, reset_reg); return_ACPI_STATUS(status); } @@ -97,67 +110,92 @@ ACPI_EXPORT_SYMBOL(acpi_reset) * * DESCRIPTION: Read from either memory or IO space. * + * LIMITATIONS: <These limitations also apply to acpi_write> + * bit_width must be exactly 8, 16, 32, or 64. + * space_iD must be system_memory or system_iO. + * bit_offset and access_width are currently ignored, as there has + * not been a need to implement these. + * ******************************************************************************/ -acpi_status acpi_read(u32 *value, struct acpi_generic_address *reg) +acpi_status acpi_read(u64 *return_value, struct acpi_generic_address *reg) { + u32 value; u32 width; u64 address; acpi_status status; ACPI_FUNCTION_NAME(acpi_read); - /* - * Must have a valid pointer to a GAS structure, and a non-zero address - * within. - */ - if (!reg) { + if (!return_value) { return (AE_BAD_PARAMETER); } - /* Get a local copy of the address. Handles possible alignment issues */ + /* Validate contents of the GAS register. Allow 64-bit transfers */ - ACPI_MOVE_64_TO_64(&address, ®->address); - if (!address) { - return (AE_BAD_ADDRESS); + status = acpi_hw_validate_register(reg, 64, &address); + if (ACPI_FAILURE(status)) { + return (status); } - /* Supported widths are 8/16/32 */ - width = reg->bit_width; - if ((width != 8) && (width != 16) && (width != 32)) { - return (AE_SUPPORT); + if (width == 64) { + width = 32; /* Break into two 32-bit transfers */ } - /* Initialize entire 32-bit return value to zero */ + /* Initialize entire 64-bit return value to zero */ - *value = 0; + *return_value = 0; + value = 0; /* * Two address spaces supported: Memory or IO. PCI_Config is * not supported here because the GAS structure is insufficient */ - switch (reg->space_id) { - case ACPI_ADR_SPACE_SYSTEM_MEMORY: + if (reg->space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY) { + status = acpi_os_read_memory((acpi_physical_address) + address, &value, width); + if (ACPI_FAILURE(status)) { + return (status); + } + *return_value = value; - status = acpi_os_read_memory((acpi_physical_address) address, - value, width); - break; + if (reg->bit_width == 64) { - case ACPI_ADR_SPACE_SYSTEM_IO: + /* Read the top 32 bits */ - status = - acpi_hw_read_port((acpi_io_address) address, value, width); - break; + status = acpi_os_read_memory((acpi_physical_address) + (address + 4), &value, 32); + if (ACPI_FAILURE(status)) { + return (status); + } + *return_value |= ((u64)value << 32); + } + } else { /* ACPI_ADR_SPACE_SYSTEM_IO, validated earlier */ - default: - ACPI_ERROR((AE_INFO, - "Unsupported address space: %X", reg->space_id)); - return (AE_BAD_PARAMETER); + status = acpi_hw_read_port((acpi_io_address) + address, &value, width); + if (ACPI_FAILURE(status)) { + return (status); + } + *return_value = value; + + if (reg->bit_width == 64) { + + /* Read the top 32 bits */ + + status = acpi_hw_read_port((acpi_io_address) + (address + 4), &value, 32); + if (ACPI_FAILURE(status)) { + return (status); + } + *return_value |= ((u64)value << 32); + } } ACPI_DEBUG_PRINT((ACPI_DB_IO, - "Read: %8.8X width %2d from %8.8X%8.8X (%s)\n", - *value, width, ACPI_FORMAT_UINT64(address), + "Read: %8.8X%8.8X width %2d from %8.8X%8.8X (%s)\n", + ACPI_FORMAT_UINT64(*return_value), reg->bit_width, + ACPI_FORMAT_UINT64(address), acpi_ut_get_region_name(reg->space_id))); return (status); @@ -169,7 +207,7 @@ ACPI_EXPORT_SYMBOL(acpi_read) * * FUNCTION: acpi_write * - * PARAMETERS: Value - To be written + * PARAMETERS: Value - Value to be written * Reg - GAS register structure * * RETURN: Status @@ -177,7 +215,7 @@ ACPI_EXPORT_SYMBOL(acpi_read) * DESCRIPTION: Write to either memory or IO space. * ******************************************************************************/ -acpi_status acpi_write(u32 value, struct acpi_generic_address *reg) +acpi_status acpi_write(u64 value, struct acpi_generic_address *reg) { u32 width; u64 address; @@ -185,54 +223,61 @@ acpi_status acpi_write(u32 value, struct acpi_generic_address *reg) ACPI_FUNCTION_NAME(acpi_write); - /* - * Must have a valid pointer to a GAS structure, and a non-zero address - * within. - */ - if (!reg) { - return (AE_BAD_PARAMETER); - } - - /* Get a local copy of the address. Handles possible alignment issues */ + /* Validate contents of the GAS register. Allow 64-bit transfers */ - ACPI_MOVE_64_TO_64(&address, ®->address); - if (!address) { - return (AE_BAD_ADDRESS); + status = acpi_hw_validate_register(reg, 64, &address); + if (ACPI_FAILURE(status)) { + return (status); } - /* Supported widths are 8/16/32 */ - width = reg->bit_width; - if ((width != 8) && (width != 16) && (width != 32)) { - return (AE_SUPPORT); + if (width == 64) { + width = 32; /* Break into two 32-bit transfers */ } /* - * Two address spaces supported: Memory or IO. - * PCI_Config is not supported here because the GAS struct is insufficient + * Two address spaces supported: Memory or IO. PCI_Config is + * not supported here because the GAS structure is insufficient */ - switch (reg->space_id) { - case ACPI_ADR_SPACE_SYSTEM_MEMORY: - - status = acpi_os_write_memory((acpi_physical_address) address, - value, width); - break; + if (reg->space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY) { + status = acpi_os_write_memory((acpi_physical_address) + address, ACPI_LODWORD(value), + width); + if (ACPI_FAILURE(status)) { + return (status); + } - case ACPI_ADR_SPACE_SYSTEM_IO: + if (reg->bit_width == 64) { + status = acpi_os_write_memory((acpi_physical_address) + (address + 4), + ACPI_HIDWORD(value), 32); + if (ACPI_FAILURE(status)) { + return (status); + } + } + } else { /* ACPI_ADR_SPACE_SYSTEM_IO, validated earlier */ - status = acpi_hw_write_port((acpi_io_address) address, value, + status = acpi_hw_write_port((acpi_io_address) + address, ACPI_LODWORD(value), width); - break; + if (ACPI_FAILURE(status)) { + return (status); + } - default: - ACPI_ERROR((AE_INFO, - "Unsupported address space: %X", reg->space_id)); - return (AE_BAD_PARAMETER); + if (reg->bit_width == 64) { + status = acpi_hw_write_port((acpi_io_address) + (address + 4), + ACPI_HIDWORD(value), 32); + if (ACPI_FAILURE(status)) { + return (status); + } + } } ACPI_DEBUG_PRINT((ACPI_DB_IO, - "Wrote: %8.8X width %2d to %8.8X%8.8X (%s)\n", - value, width, ACPI_FORMAT_UINT64(address), + "Wrote: %8.8X%8.8X width %2d to %8.8X%8.8X (%s)\n", + ACPI_FORMAT_UINT64(value), reg->bit_width, + ACPI_FORMAT_UINT64(address), acpi_ut_get_region_name(reg->space_id))); return (status); diff --git a/drivers/acpi/acpica/nsalloc.c b/drivers/acpi/acpica/nsalloc.c index efc971ab7d65..8a58a1b85aa0 100644 --- a/drivers/acpi/acpica/nsalloc.c +++ b/drivers/acpi/acpica/nsalloc.c @@ -96,17 +96,68 @@ struct acpi_namespace_node *acpi_ns_create_node(u32 name) * * RETURN: None * - * DESCRIPTION: Delete a namespace node + * DESCRIPTION: Delete a namespace node. All node deletions must come through + * here. Detaches any attached objects, including any attached + * data. If a handler is associated with attached data, it is + * invoked before the node is deleted. * ******************************************************************************/ void acpi_ns_delete_node(struct acpi_namespace_node *node) { + union acpi_operand_object *obj_desc; + + ACPI_FUNCTION_NAME(ns_delete_node); + + /* Detach an object if there is one */ + + acpi_ns_detach_object(node); + + /* + * Delete an attached data object if present (an object that was created + * and attached via acpi_attach_data). Note: After any normal object is + * detached above, the only possible remaining object is a data object. + */ + obj_desc = node->object; + if (obj_desc && (obj_desc->common.type == ACPI_TYPE_LOCAL_DATA)) { + + /* Invoke the attached data deletion handler if present */ + + if (obj_desc->data.handler) { + obj_desc->data.handler(node, obj_desc->data.pointer); + } + + acpi_ut_remove_reference(obj_desc); + } + + /* Now we can delete the node */ + + (void)acpi_os_release_object(acpi_gbl_namespace_cache, node); + + ACPI_MEM_TRACKING(acpi_gbl_ns_node_list->total_freed++); + ACPI_DEBUG_PRINT((ACPI_DB_ALLOCATIONS, "Node %p, Remaining %X\n", + node, acpi_gbl_current_node_count)); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ns_remove_node + * + * PARAMETERS: Node - Node to be removed/deleted + * + * RETURN: None + * + * DESCRIPTION: Remove (unlink) and delete a namespace node + * + ******************************************************************************/ + +void acpi_ns_remove_node(struct acpi_namespace_node *node) +{ struct acpi_namespace_node *parent_node; struct acpi_namespace_node *prev_node; struct acpi_namespace_node *next_node; - ACPI_FUNCTION_TRACE_PTR(ns_delete_node, node); + ACPI_FUNCTION_TRACE_PTR(ns_remove_node, node); parent_node = acpi_ns_get_parent_node(node); @@ -142,12 +193,9 @@ void acpi_ns_delete_node(struct acpi_namespace_node *node) } } - ACPI_MEM_TRACKING(acpi_gbl_ns_node_list->total_freed++); - - /* Detach an object if there is one, then delete the node */ + /* Delete the node and any attached objects */ - acpi_ns_detach_object(node); - (void)acpi_os_release_object(acpi_gbl_namespace_cache, node); + acpi_ns_delete_node(node); return_VOID; } @@ -273,25 +321,11 @@ void acpi_ns_delete_children(struct acpi_namespace_node *parent_node) parent_node, child_node)); } - /* Now we can free this child object */ - - ACPI_MEM_TRACKING(acpi_gbl_ns_node_list->total_freed++); - - ACPI_DEBUG_PRINT((ACPI_DB_ALLOCATIONS, - "Object %p, Remaining %X\n", child_node, - acpi_gbl_current_node_count)); - - /* Detach an object if there is one, then free the child node */ - - acpi_ns_detach_object(child_node); - - /* Now we can delete the node */ - - (void)acpi_os_release_object(acpi_gbl_namespace_cache, - child_node); - - /* And move on to the next child in the list */ - + /* + * Delete this child node and move on to the next child in the list. + * No need to unlink the node since we are deleting the entire branch. + */ + acpi_ns_delete_node(child_node); child_node = next_node; } while (!(flags & ANOBJ_END_OF_PEER_LIST)); @@ -433,7 +467,7 @@ void acpi_ns_delete_namespace_by_owner(acpi_owner_id owner_id) if (deletion_node) { acpi_ns_delete_children(deletion_node); - acpi_ns_delete_node(deletion_node); + acpi_ns_remove_node(deletion_node); deletion_node = NULL; } diff --git a/drivers/acpi/acpica/nsdumpdv.c b/drivers/acpi/acpica/nsdumpdv.c index 41994fe7fbb8..0fe87f1aef16 100644 --- a/drivers/acpi/acpica/nsdumpdv.c +++ b/drivers/acpi/acpica/nsdumpdv.c @@ -70,7 +70,6 @@ static acpi_status acpi_ns_dump_one_device(acpi_handle obj_handle, u32 level, void *context, void **return_value) { - struct acpi_buffer buffer; struct acpi_device_info *info; acpi_status status; u32 i; @@ -80,17 +79,15 @@ acpi_ns_dump_one_device(acpi_handle obj_handle, status = acpi_ns_dump_one_object(obj_handle, level, context, return_value); - buffer.length = ACPI_ALLOCATE_LOCAL_BUFFER; - status = acpi_get_object_info(obj_handle, &buffer); + status = acpi_get_object_info(obj_handle, &info); if (ACPI_SUCCESS(status)) { - info = buffer.pointer; for (i = 0; i < level; i++) { ACPI_DEBUG_PRINT_RAW((ACPI_DB_TABLES, " ")); } ACPI_DEBUG_PRINT_RAW((ACPI_DB_TABLES, " HID: %s, ADR: %8.8X%8.8X, Status: %X\n", - info->hardware_id.value, + info->hardware_id.string, ACPI_FORMAT_UINT64(info->address), info->current_status)); ACPI_FREE(info); diff --git a/drivers/acpi/acpica/nseval.c b/drivers/acpi/acpica/nseval.c index 8e7dec1176c9..846d1132feb1 100644 --- a/drivers/acpi/acpica/nseval.c +++ b/drivers/acpi/acpica/nseval.c @@ -50,6 +50,11 @@ #define _COMPONENT ACPI_NAMESPACE ACPI_MODULE_NAME("nseval") +/* Local prototypes */ +static void +acpi_ns_exec_module_code(union acpi_operand_object *method_obj, + struct acpi_evaluate_info *info); + /******************************************************************************* * * FUNCTION: acpi_ns_evaluate @@ -76,6 +81,7 @@ ACPI_MODULE_NAME("nseval") * MUTEX: Locks interpreter * ******************************************************************************/ + acpi_status acpi_ns_evaluate(struct acpi_evaluate_info * info) { acpi_status status; @@ -276,3 +282,134 @@ acpi_status acpi_ns_evaluate(struct acpi_evaluate_info * info) */ return_ACPI_STATUS(status); } + +/******************************************************************************* + * + * FUNCTION: acpi_ns_exec_module_code_list + * + * PARAMETERS: None + * + * RETURN: None. Exceptions during method execution are ignored, since + * we cannot abort a table load. + * + * DESCRIPTION: Execute all elements of the global module-level code list. + * Each element is executed as a single control method. + * + ******************************************************************************/ + +void acpi_ns_exec_module_code_list(void) +{ + union acpi_operand_object *prev; + union acpi_operand_object *next; + struct acpi_evaluate_info *info; + u32 method_count = 0; + + ACPI_FUNCTION_TRACE(ns_exec_module_code_list); + + /* Exit now if the list is empty */ + + next = acpi_gbl_module_code_list; + if (!next) { + return_VOID; + } + + /* Allocate the evaluation information block */ + + info = ACPI_ALLOCATE(sizeof(struct acpi_evaluate_info)); + if (!info) { + return_VOID; + } + + /* Walk the list, executing each "method" */ + + while (next) { + prev = next; + next = next->method.mutex; + + /* Clear the link field and execute the method */ + + prev->method.mutex = NULL; + acpi_ns_exec_module_code(prev, info); + method_count++; + + /* Delete the (temporary) method object */ + + acpi_ut_remove_reference(prev); + } + + ACPI_INFO((AE_INFO, + "Executed %u blocks of module-level executable AML code", + method_count)); + + ACPI_FREE(info); + acpi_gbl_module_code_list = NULL; + return_VOID; +} + +/******************************************************************************* + * + * FUNCTION: acpi_ns_exec_module_code + * + * PARAMETERS: method_obj - Object container for the module-level code + * Info - Info block for method evaluation + * + * RETURN: None. Exceptions during method execution are ignored, since + * we cannot abort a table load. + * + * DESCRIPTION: Execute a control method containing a block of module-level + * executable AML code. The control method is temporarily + * installed to the root node, then evaluated. + * + ******************************************************************************/ + +static void +acpi_ns_exec_module_code(union acpi_operand_object *method_obj, + struct acpi_evaluate_info *info) +{ + union acpi_operand_object *root_obj; + acpi_status status; + + ACPI_FUNCTION_TRACE(ns_exec_module_code); + + /* Initialize the evaluation information block */ + + ACPI_MEMSET(info, 0, sizeof(struct acpi_evaluate_info)); + info->prefix_node = acpi_gbl_root_node; + + /* + * Get the currently attached root object. Add a reference, because the + * ref count will be decreased when the method object is installed to + * the root node. + */ + root_obj = acpi_ns_get_attached_object(acpi_gbl_root_node); + acpi_ut_add_reference(root_obj); + + /* Install the method (module-level code) in the root node */ + + status = acpi_ns_attach_object(acpi_gbl_root_node, method_obj, + ACPI_TYPE_METHOD); + if (ACPI_FAILURE(status)) { + goto exit; + } + + /* Execute the root node as a control method */ + + status = acpi_ns_evaluate(info); + + ACPI_DEBUG_PRINT((ACPI_DB_INIT, "Executed module-level code at %p\n", + method_obj->method.aml_start)); + + /* Detach the temporary method object */ + + acpi_ns_detach_object(acpi_gbl_root_node); + + /* Restore the original root object */ + + status = + acpi_ns_attach_object(acpi_gbl_root_node, root_obj, + ACPI_TYPE_DEVICE); + + exit: + acpi_ut_remove_reference(root_obj); + return_VOID; +} diff --git a/drivers/acpi/acpica/nsinit.c b/drivers/acpi/acpica/nsinit.c index 2adfcf329e15..1d5b360eb25b 100644 --- a/drivers/acpi/acpica/nsinit.c +++ b/drivers/acpi/acpica/nsinit.c @@ -170,6 +170,21 @@ acpi_status acpi_ns_initialize_devices(void) goto error_exit; } + /* + * Execute the "global" _INI method that may appear at the root. This + * support is provided for Windows compatibility (Vista+) and is not + * part of the ACPI specification. + */ + info.evaluate_info->prefix_node = acpi_gbl_root_node; + info.evaluate_info->pathname = METHOD_NAME__INI; + info.evaluate_info->parameters = NULL; + info.evaluate_info->flags = ACPI_IGNORE_RETURN_VALUE; + + status = acpi_ns_evaluate(info.evaluate_info); + if (ACPI_SUCCESS(status)) { + info.num_INI++; + } + /* Walk namespace to execute all _INIs on present devices */ status = acpi_ns_walk_namespace(ACPI_TYPE_ANY, ACPI_ROOT_OBJECT, diff --git a/drivers/acpi/acpica/nsload.c b/drivers/acpi/acpica/nsload.c index dcd7a6adbbbc..a7234e60e985 100644 --- a/drivers/acpi/acpica/nsload.c +++ b/drivers/acpi/acpica/nsload.c @@ -270,8 +270,7 @@ static acpi_status acpi_ns_delete_subtree(acpi_handle start_handle) /* Now delete the starting object, and we are done */ - acpi_ns_delete_node(child_handle); - + acpi_ns_remove_node(child_handle); return_ACPI_STATUS(AE_OK); } diff --git a/drivers/acpi/acpica/nspredef.c b/drivers/acpi/acpica/nspredef.c index 7f8e066b12a3..f8427afeebdf 100644 --- a/drivers/acpi/acpica/nspredef.c +++ b/drivers/acpi/acpica/nspredef.c @@ -42,6 +42,8 @@ * POSSIBILITY OF SUCH DAMAGES. */ +#define ACPI_CREATE_PREDEFINED_TABLE + #include <acpi/acpi.h> #include "accommon.h" #include "acnamesp.h" @@ -72,30 +74,31 @@ ACPI_MODULE_NAME("nspredef") ******************************************************************************/ /* Local prototypes */ static acpi_status -acpi_ns_check_package(char *pathname, - union acpi_operand_object **return_object_ptr, - const union acpi_predefined_info *predefined); +acpi_ns_check_package(struct acpi_predefined_data *data, + union acpi_operand_object **return_object_ptr); + +static acpi_status +acpi_ns_check_package_list(struct acpi_predefined_data *data, + const union acpi_predefined_info *package, + union acpi_operand_object **elements, u32 count); static acpi_status -acpi_ns_check_package_elements(char *pathname, +acpi_ns_check_package_elements(struct acpi_predefined_data *data, union acpi_operand_object **elements, u8 type1, u32 count1, u8 type2, u32 count2, u32 start_index); static acpi_status -acpi_ns_check_object_type(char *pathname, +acpi_ns_check_object_type(struct acpi_predefined_data *data, union acpi_operand_object **return_object_ptr, u32 expected_btypes, u32 package_index); static acpi_status -acpi_ns_check_reference(char *pathname, +acpi_ns_check_reference(struct acpi_predefined_data *data, union acpi_operand_object *return_object); -static acpi_status -acpi_ns_repair_object(u32 expected_btypes, - u32 package_index, - union acpi_operand_object **return_object_ptr); +static void acpi_ns_get_expected_types(char *buffer, u32 expected_btypes); /* * Names for the types that can be returned by the predefined objects. @@ -109,13 +112,13 @@ static const char *acpi_rtype_names[] = { "/Reference", }; -#define ACPI_NOT_PACKAGE ACPI_UINT32_MAX - /******************************************************************************* * * FUNCTION: acpi_ns_check_predefined_names * * PARAMETERS: Node - Namespace node for the method/object + * user_param_count - Number of parameters actually passed + * return_status - Status from the object evaluation * return_object_ptr - Pointer to the object returned from the * evaluation of a method or object * @@ -135,12 +138,13 @@ acpi_ns_check_predefined_names(struct acpi_namespace_node *node, acpi_status status = AE_OK; const union acpi_predefined_info *predefined; char *pathname; + struct acpi_predefined_data *data; /* Match the name for this method/object against the predefined list */ predefined = acpi_ns_check_for_predefined_name(node); - /* Get the full pathname to the object, for use in error messages */ + /* Get the full pathname to the object, for use in warning messages */ pathname = acpi_ns_get_external_pathname(node); if (!pathname) { @@ -158,28 +162,17 @@ acpi_ns_check_predefined_names(struct acpi_namespace_node *node, /* If not a predefined name, we cannot validate the return object */ if (!predefined) { - goto exit; - } - - /* If the method failed, we cannot validate the return object */ - - if ((return_status != AE_OK) && (return_status != AE_CTRL_RETURN_VALUE)) { - goto exit; + goto cleanup; } /* - * Only validate the return value on the first successful evaluation of - * the method. This ensures that any warnings will only be emitted during - * the very first evaluation of the method/object. + * If the method failed or did not actually return an object, we cannot + * validate the return object */ - if (node->flags & ANOBJ_EVALUATED) { - goto exit; + if ((return_status != AE_OK) && (return_status != AE_CTRL_RETURN_VALUE)) { + goto cleanup; } - /* Mark the node as having been successfully evaluated */ - - node->flags |= ANOBJ_EVALUATED; - /* * If there is no return value, check if we require a return value for * this predefined name. Either one return value is expected, or none, @@ -190,46 +183,67 @@ acpi_ns_check_predefined_names(struct acpi_namespace_node *node, if (!return_object) { if ((predefined->info.expected_btypes) && (!(predefined->info.expected_btypes & ACPI_RTYPE_NONE))) { - ACPI_ERROR((AE_INFO, - "%s: Missing expected return value", - pathname)); + ACPI_WARN_PREDEFINED((AE_INFO, pathname, + ACPI_WARN_ALWAYS, + "Missing expected return value")); status = AE_AML_NO_RETURN_VALUE; } - goto exit; + goto cleanup; } /* - * We have a return value, but if one wasn't expected, just exit, this is - * not a problem + * 1) We have a return value, but if one wasn't expected, just exit, this is + * not a problem. For example, if the "Implicit Return" feature is + * enabled, methods will always return a value. * - * For example, if the "Implicit Return" feature is enabled, methods will - * always return a value + * 2) If the return value can be of any type, then we cannot perform any + * validation, exit. */ - if (!predefined->info.expected_btypes) { - goto exit; + if ((!predefined->info.expected_btypes) || + (predefined->info.expected_btypes == ACPI_RTYPE_ALL)) { + goto cleanup; } + /* Create the parameter data block for object validation */ + + data = ACPI_ALLOCATE_ZEROED(sizeof(struct acpi_predefined_data)); + if (!data) { + goto cleanup; + } + data->predefined = predefined; + data->node_flags = node->flags; + data->pathname = pathname; + /* * Check that the type of the return object is what is expected for * this predefined name */ - status = acpi_ns_check_object_type(pathname, return_object_ptr, + status = acpi_ns_check_object_type(data, return_object_ptr, predefined->info.expected_btypes, - ACPI_NOT_PACKAGE); + ACPI_NOT_PACKAGE_ELEMENT); if (ACPI_FAILURE(status)) { - goto exit; + goto check_validation_status; } /* For returned Package objects, check the type of all sub-objects */ if (return_object->common.type == ACPI_TYPE_PACKAGE) { - status = - acpi_ns_check_package(pathname, return_object_ptr, - predefined); + status = acpi_ns_check_package(data, return_object_ptr); + } + +check_validation_status: + /* + * If the object validation failed or if we successfully repaired one + * or more objects, mark the parent node to suppress further warning + * messages during the next evaluation of the same method/object. + */ + if (ACPI_FAILURE(status) || (data->flags & ACPI_OBJECT_REPAIRED)) { + node->flags |= ANOBJ_EVALUATED; } + ACPI_FREE(data); - exit: +cleanup: ACPI_FREE(pathname); return (status); } @@ -268,64 +282,58 @@ acpi_ns_check_parameter_count(char *pathname, param_count = node->object->method.param_count; } - /* Argument count check for non-predefined methods/objects */ - if (!predefined) { /* + * Check the parameter count for non-predefined methods/objects. + * * Warning if too few or too many arguments have been passed by the * caller. An incorrect number of arguments may not cause the method * to fail. However, the method will fail if there are too few * arguments and the method attempts to use one of the missing ones. */ if (user_param_count < param_count) { - ACPI_WARNING((AE_INFO, - "%s: Insufficient arguments - needs %d, found %d", - pathname, param_count, user_param_count)); + ACPI_WARN_PREDEFINED((AE_INFO, pathname, + ACPI_WARN_ALWAYS, + "Insufficient arguments - needs %u, found %u", + param_count, user_param_count)); } else if (user_param_count > param_count) { - ACPI_WARNING((AE_INFO, - "%s: Excess arguments - needs %d, found %d", - pathname, param_count, user_param_count)); + ACPI_WARN_PREDEFINED((AE_INFO, pathname, + ACPI_WARN_ALWAYS, + "Excess arguments - needs %u, found %u", + param_count, user_param_count)); } return; } - /* Allow two different legal argument counts (_SCP, etc.) */ - + /* + * Validate the user-supplied parameter count. + * Allow two different legal argument counts (_SCP, etc.) + */ required_params_current = predefined->info.param_count & 0x0F; required_params_old = predefined->info.param_count >> 4; if (user_param_count != ACPI_UINT32_MAX) { - - /* Validate the user-supplied parameter count */ - if ((user_param_count != required_params_current) && (user_param_count != required_params_old)) { - ACPI_WARNING((AE_INFO, - "%s: Parameter count mismatch - " - "caller passed %d, ACPI requires %d", - pathname, user_param_count, - required_params_current)); + ACPI_WARN_PREDEFINED((AE_INFO, pathname, + ACPI_WARN_ALWAYS, + "Parameter count mismatch - " + "caller passed %u, ACPI requires %u", + user_param_count, + required_params_current)); } } /* - * Only validate the argument count on the first successful evaluation of - * the method. This ensures that any warnings will only be emitted during - * the very first evaluation of the method/object. - */ - if (node->flags & ANOBJ_EVALUATED) { - return; - } - - /* * Check that the ASL-defined parameter count is what is expected for - * this predefined name. + * this predefined name (parameter count as defined by the ACPI + * specification) */ if ((param_count != required_params_current) && (param_count != required_params_old)) { - ACPI_WARNING((AE_INFO, - "%s: Parameter count mismatch - ASL declared %d, ACPI requires %d", - pathname, param_count, required_params_current)); + ACPI_WARN_PREDEFINED((AE_INFO, pathname, node->flags, + "Parameter count mismatch - ASL declared %u, ACPI requires %u", + param_count, required_params_current)); } } @@ -358,9 +366,6 @@ const union acpi_predefined_info *acpi_ns_check_for_predefined_name(struct this_name = predefined_names; while (this_name->info.name[0]) { if (ACPI_COMPARE_NAME(node->name.ascii, this_name->info.name)) { - - /* Return pointer to this table entry */ - return (this_name); } @@ -375,17 +380,16 @@ const union acpi_predefined_info *acpi_ns_check_for_predefined_name(struct this_name++; } - return (NULL); + return (NULL); /* Not found */ } /******************************************************************************* * * FUNCTION: acpi_ns_check_package * - * PARAMETERS: Pathname - Full pathname to the node (for error msgs) + * PARAMETERS: Data - Pointer to validation data structure * return_object_ptr - Pointer to the object returned from the * evaluation of a method or object - * Predefined - Pointer to entry in predefined name table * * RETURN: Status * @@ -395,30 +399,26 @@ const union acpi_predefined_info *acpi_ns_check_for_predefined_name(struct ******************************************************************************/ static acpi_status -acpi_ns_check_package(char *pathname, - union acpi_operand_object **return_object_ptr, - const union acpi_predefined_info *predefined) +acpi_ns_check_package(struct acpi_predefined_data *data, + union acpi_operand_object **return_object_ptr) { union acpi_operand_object *return_object = *return_object_ptr; const union acpi_predefined_info *package; - union acpi_operand_object *sub_package; union acpi_operand_object **elements; - union acpi_operand_object **sub_elements; - acpi_status status; + acpi_status status = AE_OK; u32 expected_count; u32 count; u32 i; - u32 j; ACPI_FUNCTION_NAME(ns_check_package); /* The package info for this name is in the next table entry */ - package = predefined + 1; + package = data->predefined + 1; ACPI_DEBUG_PRINT((ACPI_DB_NAMES, "%s Validating return Package of Type %X, Count %X\n", - pathname, package->ret_info.type, + data->pathname, package->ret_info.type, return_object->package.count)); /* Extract package count and elements array */ @@ -429,9 +429,8 @@ acpi_ns_check_package(char *pathname, /* The package must have at least one element, else invalid */ if (!count) { - ACPI_WARNING((AE_INFO, - "%s: Return Package has no elements (empty)", - pathname)); + ACPI_WARN_PREDEFINED((AE_INFO, data->pathname, data->node_flags, + "Return Package has no elements (empty)")); return (AE_AML_OPERAND_VALUE); } @@ -456,15 +455,16 @@ acpi_ns_check_package(char *pathname, if (count < expected_count) { goto package_too_small; } else if (count > expected_count) { - ACPI_WARNING((AE_INFO, - "%s: Return Package is larger than needed - " - "found %u, expected %u", pathname, count, - expected_count)); + ACPI_WARN_PREDEFINED((AE_INFO, data->pathname, + data->node_flags, + "Return Package is larger than needed - " + "found %u, expected %u", count, + expected_count)); } /* Validate all elements of the returned package */ - status = acpi_ns_check_package_elements(pathname, elements, + status = acpi_ns_check_package_elements(data, elements, package->ret_info. object_type1, package->ret_info. @@ -473,9 +473,6 @@ acpi_ns_check_package(char *pathname, object_type2, package->ret_info. count2, 0); - if (ACPI_FAILURE(status)) { - return (status); - } break; case ACPI_PTYPE1_VAR: @@ -485,7 +482,7 @@ acpi_ns_check_package(char *pathname, * elements must be of the same type */ for (i = 0; i < count; i++) { - status = acpi_ns_check_object_type(pathname, elements, + status = acpi_ns_check_object_type(data, elements, package->ret_info. object_type1, i); if (ACPI_FAILURE(status)) { @@ -517,8 +514,7 @@ acpi_ns_check_package(char *pathname, /* These are the required package elements (0, 1, or 2) */ status = - acpi_ns_check_object_type(pathname, - elements, + acpi_ns_check_object_type(data, elements, package-> ret_info3. object_type[i], @@ -530,8 +526,7 @@ acpi_ns_check_package(char *pathname, /* These are the optional package elements */ status = - acpi_ns_check_object_type(pathname, - elements, + acpi_ns_check_object_type(data, elements, package-> ret_info3. tail_object_type, @@ -544,11 +539,30 @@ acpi_ns_check_package(char *pathname, } break; + case ACPI_PTYPE2_REV_FIXED: + + /* First element is the (Integer) revision */ + + status = acpi_ns_check_object_type(data, elements, + ACPI_RTYPE_INTEGER, 0); + if (ACPI_FAILURE(status)) { + return (status); + } + + elements++; + count--; + + /* Examine the sub-packages */ + + status = + acpi_ns_check_package_list(data, package, elements, count); + break; + case ACPI_PTYPE2_PKG_COUNT: /* First element is the (Integer) count of sub-packages to follow */ - status = acpi_ns_check_object_type(pathname, elements, + status = acpi_ns_check_object_type(data, elements, ACPI_RTYPE_INTEGER, 0); if (ACPI_FAILURE(status)) { return (status); @@ -566,9 +580,11 @@ acpi_ns_check_package(char *pathname, count = expected_count; elements++; - /* Now we can walk the sub-packages */ + /* Examine the sub-packages */ - /*lint -fallthrough */ + status = + acpi_ns_check_package_list(data, package, elements, count); + break; case ACPI_PTYPE2: case ACPI_PTYPE2_FIXED: @@ -576,176 +592,240 @@ acpi_ns_check_package(char *pathname, case ACPI_PTYPE2_COUNT: /* - * These types all return a single package that consists of a variable - * number of sub-packages + * These types all return a single Package that consists of a + * variable number of sub-Packages. + * + * First, ensure that the first element is a sub-Package. If not, + * the BIOS may have incorrectly returned the object as a single + * package instead of a Package of Packages (a common error if + * there is only one entry). We may be able to repair this by + * wrapping the returned Package with a new outer Package. */ - for (i = 0; i < count; i++) { - sub_package = *elements; - sub_elements = sub_package->package.elements; + if ((*elements)->common.type != ACPI_TYPE_PACKAGE) { - /* Each sub-object must be of type Package */ + /* Create the new outer package and populate it */ status = - acpi_ns_check_object_type(pathname, &sub_package, - ACPI_RTYPE_PACKAGE, i); + acpi_ns_repair_package_list(data, + return_object_ptr); if (ACPI_FAILURE(status)) { return (status); } - /* Examine the different types of sub-packages */ + /* Update locals to point to the new package (of 1 element) */ - switch (package->ret_info.type) { - case ACPI_PTYPE2: - case ACPI_PTYPE2_PKG_COUNT: + return_object = *return_object_ptr; + elements = return_object->package.elements; + count = 1; + } - /* Each subpackage has a fixed number of elements */ + /* Examine the sub-packages */ - expected_count = - package->ret_info.count1 + - package->ret_info.count2; - if (sub_package->package.count != - expected_count) { - count = sub_package->package.count; - goto package_too_small; - } + status = + acpi_ns_check_package_list(data, package, elements, count); + break; - status = - acpi_ns_check_package_elements(pathname, - sub_elements, - package-> - ret_info. - object_type1, - package-> - ret_info. - count1, - package-> - ret_info. - object_type2, - package-> - ret_info. - count2, 0); - if (ACPI_FAILURE(status)) { - return (status); - } - break; + default: - case ACPI_PTYPE2_FIXED: + /* Should not get here if predefined info table is correct */ - /* Each sub-package has a fixed length */ + ACPI_WARN_PREDEFINED((AE_INFO, data->pathname, data->node_flags, + "Invalid internal return type in table entry: %X", + package->ret_info.type)); - expected_count = package->ret_info2.count; - if (sub_package->package.count < expected_count) { - count = sub_package->package.count; - goto package_too_small; - } + return (AE_AML_INTERNAL); + } - /* Check the type of each sub-package element */ + return (status); - for (j = 0; j < expected_count; j++) { - status = - acpi_ns_check_object_type(pathname, - &sub_elements[j], - package->ret_info2.object_type[j], j); - if (ACPI_FAILURE(status)) { - return (status); - } - } - break; +package_too_small: - case ACPI_PTYPE2_MIN: + /* Error exit for the case with an incorrect package count */ - /* Each sub-package has a variable but minimum length */ + ACPI_WARN_PREDEFINED((AE_INFO, data->pathname, data->node_flags, + "Return Package is too small - found %u elements, expected %u", + count, expected_count)); - expected_count = package->ret_info.count1; - if (sub_package->package.count < expected_count) { - count = sub_package->package.count; - goto package_too_small; - } + return (AE_AML_OPERAND_VALUE); +} - /* Check the type of each sub-package element */ +/******************************************************************************* + * + * FUNCTION: acpi_ns_check_package_list + * + * PARAMETERS: Data - Pointer to validation data structure + * Package - Pointer to package-specific info for method + * Elements - Element list of parent package. All elements + * of this list should be of type Package. + * Count - Count of subpackages + * + * RETURN: Status + * + * DESCRIPTION: Examine a list of subpackages + * + ******************************************************************************/ - status = - acpi_ns_check_package_elements(pathname, - sub_elements, - package-> - ret_info. - object_type1, - sub_package-> - package. - count, 0, 0, - 0); - if (ACPI_FAILURE(status)) { - return (status); - } - break; +static acpi_status +acpi_ns_check_package_list(struct acpi_predefined_data *data, + const union acpi_predefined_info *package, + union acpi_operand_object **elements, u32 count) +{ + union acpi_operand_object *sub_package; + union acpi_operand_object **sub_elements; + acpi_status status; + u32 expected_count; + u32 i; + u32 j; - case ACPI_PTYPE2_COUNT: + /* Validate each sub-Package in the parent Package */ - /* First element is the (Integer) count of elements to follow */ + for (i = 0; i < count; i++) { + sub_package = *elements; + sub_elements = sub_package->package.elements; - status = - acpi_ns_check_object_type(pathname, - sub_elements, - ACPI_RTYPE_INTEGER, - 0); - if (ACPI_FAILURE(status)) { - return (status); - } + /* Each sub-object must be of type Package */ - /* Make sure package is large enough for the Count */ + status = acpi_ns_check_object_type(data, &sub_package, + ACPI_RTYPE_PACKAGE, i); + if (ACPI_FAILURE(status)) { + return (status); + } - expected_count = - (u32) (*sub_elements)->integer.value; - if (sub_package->package.count < expected_count) { - count = sub_package->package.count; - goto package_too_small; - } + /* Examine the different types of expected sub-packages */ + + switch (package->ret_info.type) { + case ACPI_PTYPE2: + case ACPI_PTYPE2_PKG_COUNT: + case ACPI_PTYPE2_REV_FIXED: + + /* Each subpackage has a fixed number of elements */ + + expected_count = + package->ret_info.count1 + package->ret_info.count2; + if (sub_package->package.count < expected_count) { + goto package_too_small; + } + + status = + acpi_ns_check_package_elements(data, sub_elements, + package->ret_info. + object_type1, + package->ret_info. + count1, + package->ret_info. + object_type2, + package->ret_info. + count2, 0); + if (ACPI_FAILURE(status)) { + return (status); + } + break; - /* Check the type of each sub-package element */ + case ACPI_PTYPE2_FIXED: + /* Each sub-package has a fixed length */ + + expected_count = package->ret_info2.count; + if (sub_package->package.count < expected_count) { + goto package_too_small; + } + + /* Check the type of each sub-package element */ + + for (j = 0; j < expected_count; j++) { status = - acpi_ns_check_package_elements(pathname, - (sub_elements - + 1), - package-> - ret_info. - object_type1, - (expected_count - - 1), 0, 0, - 1); + acpi_ns_check_object_type(data, + &sub_elements[j], + package-> + ret_info2. + object_type[j], + j); if (ACPI_FAILURE(status)) { return (status); } - break; + } + break; + + case ACPI_PTYPE2_MIN: - default: - break; + /* Each sub-package has a variable but minimum length */ + + expected_count = package->ret_info.count1; + if (sub_package->package.count < expected_count) { + goto package_too_small; } - elements++; - } - break; + /* Check the type of each sub-package element */ - default: + status = + acpi_ns_check_package_elements(data, sub_elements, + package->ret_info. + object_type1, + sub_package->package. + count, 0, 0, 0); + if (ACPI_FAILURE(status)) { + return (status); + } + break; - /* Should not get here if predefined info table is correct */ + case ACPI_PTYPE2_COUNT: + + /* + * First element is the (Integer) count of elements, including + * the count field. + */ + status = acpi_ns_check_object_type(data, sub_elements, + ACPI_RTYPE_INTEGER, + 0); + if (ACPI_FAILURE(status)) { + return (status); + } - ACPI_WARNING((AE_INFO, - "%s: Invalid internal return type in table entry: %X", - pathname, package->ret_info.type)); + /* + * Make sure package is large enough for the Count and is + * is as large as the minimum size + */ + expected_count = (u32)(*sub_elements)->integer.value; + if (sub_package->package.count < expected_count) { + goto package_too_small; + } + if (sub_package->package.count < + package->ret_info.count1) { + expected_count = package->ret_info.count1; + goto package_too_small; + } - return (AE_AML_INTERNAL); + /* Check the type of each sub-package element */ + + status = + acpi_ns_check_package_elements(data, + (sub_elements + 1), + package->ret_info. + object_type1, + (expected_count - 1), + 0, 0, 1); + if (ACPI_FAILURE(status)) { + return (status); + } + break; + + default: /* Should not get here, type was validated by caller */ + + return (AE_AML_INTERNAL); + } + + elements++; } return (AE_OK); - package_too_small: +package_too_small: - /* Error exit for the case with an incorrect package count */ + /* The sub-package count was smaller than required */ - ACPI_WARNING((AE_INFO, "%s: Return Package is too small - " - "found %u, expected %u", pathname, count, - expected_count)); + ACPI_WARN_PREDEFINED((AE_INFO, data->pathname, data->node_flags, + "Return Sub-Package[%u] is too small - found %u elements, expected %u", + i, sub_package->package.count, expected_count)); return (AE_AML_OPERAND_VALUE); } @@ -754,7 +834,7 @@ acpi_ns_check_package(char *pathname, * * FUNCTION: acpi_ns_check_package_elements * - * PARAMETERS: Pathname - Full pathname to the node (for error msgs) + * PARAMETERS: Data - Pointer to validation data structure * Elements - Pointer to the package elements array * Type1 - Object type for first group * Count1 - Count for first group @@ -770,7 +850,7 @@ acpi_ns_check_package(char *pathname, ******************************************************************************/ static acpi_status -acpi_ns_check_package_elements(char *pathname, +acpi_ns_check_package_elements(struct acpi_predefined_data *data, union acpi_operand_object **elements, u8 type1, u32 count1, @@ -786,7 +866,7 @@ acpi_ns_check_package_elements(char *pathname, * The second group can have a count of zero. */ for (i = 0; i < count1; i++) { - status = acpi_ns_check_object_type(pathname, this_element, + status = acpi_ns_check_object_type(data, this_element, type1, i + start_index); if (ACPI_FAILURE(status)) { return (status); @@ -795,7 +875,7 @@ acpi_ns_check_package_elements(char *pathname, } for (i = 0; i < count2; i++) { - status = acpi_ns_check_object_type(pathname, this_element, + status = acpi_ns_check_object_type(data, this_element, type2, (i + count1 + start_index)); if (ACPI_FAILURE(status)) { @@ -811,12 +891,13 @@ acpi_ns_check_package_elements(char *pathname, * * FUNCTION: acpi_ns_check_object_type * - * PARAMETERS: Pathname - Full pathname to the node (for error msgs) + * PARAMETERS: Data - Pointer to validation data structure * return_object_ptr - Pointer to the object returned from the * evaluation of a method or object * expected_btypes - Bitmap of expected return type(s) * package_index - Index of object within parent package (if - * applicable - ACPI_NOT_PACKAGE otherwise) + * applicable - ACPI_NOT_PACKAGE_ELEMENT + * otherwise) * * RETURN: Status * @@ -826,7 +907,7 @@ acpi_ns_check_package_elements(char *pathname, ******************************************************************************/ static acpi_status -acpi_ns_check_object_type(char *pathname, +acpi_ns_check_object_type(struct acpi_predefined_data *data, union acpi_operand_object **return_object_ptr, u32 expected_btypes, u32 package_index) { @@ -834,9 +915,6 @@ acpi_ns_check_object_type(char *pathname, acpi_status status = AE_OK; u32 return_btype; char type_buffer[48]; /* Room for 5 types */ - u32 this_rtype; - u32 i; - u32 j; /* * If we get a NULL return_object here, it is a NULL package element, @@ -849,10 +927,11 @@ acpi_ns_check_object_type(char *pathname, /* A Namespace node should not get here, but make sure */ if (ACPI_GET_DESCRIPTOR_TYPE(return_object) == ACPI_DESC_TYPE_NAMED) { - ACPI_WARNING((AE_INFO, - "%s: Invalid return type - Found a Namespace node [%4.4s] type %s", - pathname, return_object->node.name.ascii, - acpi_ut_get_type_name(return_object->node.type))); + ACPI_WARN_PREDEFINED((AE_INFO, data->pathname, data->node_flags, + "Invalid return type - Found a Namespace node [%4.4s] type %s", + return_object->node.name.ascii, + acpi_ut_get_type_name(return_object->node. + type))); return (AE_AML_OPERAND_TYPE); } @@ -897,10 +976,11 @@ acpi_ns_check_object_type(char *pathname, /* Type mismatch -- attempt repair of the returned object */ - status = acpi_ns_repair_object(expected_btypes, package_index, + status = acpi_ns_repair_object(data, expected_btypes, + package_index, return_object_ptr); if (ACPI_SUCCESS(status)) { - return (status); + return (AE_OK); /* Repair was successful */ } goto type_error_exit; } @@ -908,7 +988,7 @@ acpi_ns_check_object_type(char *pathname, /* For reference objects, check that the reference type is correct */ if (return_object->common.type == ACPI_TYPE_LOCAL_REFERENCE) { - status = acpi_ns_check_reference(pathname, return_object); + status = acpi_ns_check_reference(data, return_object); } return (status); @@ -917,33 +997,19 @@ acpi_ns_check_object_type(char *pathname, /* Create a string with all expected types for this predefined object */ - j = 1; - type_buffer[0] = 0; - this_rtype = ACPI_RTYPE_INTEGER; - - for (i = 0; i < ACPI_NUM_RTYPES; i++) { - - /* If one of the expected types, concatenate the name of this type */ - - if (expected_btypes & this_rtype) { - ACPI_STRCAT(type_buffer, &acpi_rtype_names[i][j]); - j = 0; /* Use name separator from now on */ - } - this_rtype <<= 1; /* Next Rtype */ - } + acpi_ns_get_expected_types(type_buffer, expected_btypes); - if (package_index == ACPI_NOT_PACKAGE) { - ACPI_WARNING((AE_INFO, - "%s: Return type mismatch - found %s, expected %s", - pathname, - acpi_ut_get_object_type_name(return_object), - type_buffer)); + if (package_index == ACPI_NOT_PACKAGE_ELEMENT) { + ACPI_WARN_PREDEFINED((AE_INFO, data->pathname, data->node_flags, + "Return type mismatch - found %s, expected %s", + acpi_ut_get_object_type_name + (return_object), type_buffer)); } else { - ACPI_WARNING((AE_INFO, - "%s: Return Package type mismatch at index %u - " - "found %s, expected %s", pathname, package_index, - acpi_ut_get_object_type_name(return_object), - type_buffer)); + ACPI_WARN_PREDEFINED((AE_INFO, data->pathname, data->node_flags, + "Return Package type mismatch at index %u - " + "found %s, expected %s", package_index, + acpi_ut_get_object_type_name + (return_object), type_buffer)); } return (AE_AML_OPERAND_TYPE); @@ -953,7 +1019,7 @@ acpi_ns_check_object_type(char *pathname, * * FUNCTION: acpi_ns_check_reference * - * PARAMETERS: Pathname - Full pathname to the node (for error msgs) + * PARAMETERS: Data - Pointer to validation data structure * return_object - Object returned from the evaluation of a * method or object * @@ -966,7 +1032,7 @@ acpi_ns_check_object_type(char *pathname, ******************************************************************************/ static acpi_status -acpi_ns_check_reference(char *pathname, +acpi_ns_check_reference(struct acpi_predefined_data *data, union acpi_operand_object *return_object) { @@ -979,94 +1045,46 @@ acpi_ns_check_reference(char *pathname, return (AE_OK); } - ACPI_WARNING((AE_INFO, - "%s: Return type mismatch - " - "unexpected reference object type [%s] %2.2X", - pathname, acpi_ut_get_reference_name(return_object), - return_object->reference.class)); + ACPI_WARN_PREDEFINED((AE_INFO, data->pathname, data->node_flags, + "Return type mismatch - unexpected reference object type [%s] %2.2X", + acpi_ut_get_reference_name(return_object), + return_object->reference.class)); return (AE_AML_OPERAND_TYPE); } /******************************************************************************* * - * FUNCTION: acpi_ns_repair_object + * FUNCTION: acpi_ns_get_expected_types * - * PARAMETERS: Pathname - Full pathname to the node (for error msgs) - * package_index - Used to determine if target is in a package - * return_object_ptr - Pointer to the object returned from the - * evaluation of a method or object + * PARAMETERS: Buffer - Pointer to where the string is returned + * expected_btypes - Bitmap of expected return type(s) * - * RETURN: Status. AE_OK if repair was successful. + * RETURN: Buffer is populated with type names. * - * DESCRIPTION: Attempt to repair/convert a return object of a type that was - * not expected. + * DESCRIPTION: Translate the expected types bitmap into a string of ascii + * names of expected types, for use in warning messages. * ******************************************************************************/ -static acpi_status -acpi_ns_repair_object(u32 expected_btypes, - u32 package_index, - union acpi_operand_object **return_object_ptr) +static void acpi_ns_get_expected_types(char *buffer, u32 expected_btypes) { - union acpi_operand_object *return_object = *return_object_ptr; - union acpi_operand_object *new_object; - acpi_size length; - - switch (return_object->common.type) { - case ACPI_TYPE_BUFFER: - - if (!(expected_btypes & ACPI_RTYPE_STRING)) { - return (AE_AML_OPERAND_TYPE); - } - - /* - * Have a Buffer, expected a String, convert. Use a to_string - * conversion, no transform performed on the buffer data. The best - * example of this is the _BIF method, where the string data from - * the battery is often (incorrectly) returned as buffer object(s). - */ - length = 0; - while ((length < return_object->buffer.length) && - (return_object->buffer.pointer[length])) { - length++; - } - - /* Allocate a new string object */ - - new_object = acpi_ut_create_string_object(length); - if (!new_object) { - return (AE_NO_MEMORY); - } + u32 this_rtype; + u32 i; + u32 j; - /* - * Copy the raw buffer data with no transform. String is already NULL - * terminated at Length+1. - */ - ACPI_MEMCPY(new_object->string.pointer, - return_object->buffer.pointer, length); + j = 1; + buffer[0] = 0; + this_rtype = ACPI_RTYPE_INTEGER; - /* Install the new return object */ + for (i = 0; i < ACPI_NUM_RTYPES; i++) { - acpi_ut_remove_reference(return_object); - *return_object_ptr = new_object; + /* If one of the expected types, concatenate the name of this type */ - /* - * If the object is a package element, we need to: - * 1. Decrement the reference count of the orignal object, it was - * incremented when building the package - * 2. Increment the reference count of the new object, it will be - * decremented when releasing the package - */ - if (package_index != ACPI_NOT_PACKAGE) { - acpi_ut_remove_reference(return_object); - acpi_ut_add_reference(new_object); + if (expected_btypes & this_rtype) { + ACPI_STRCAT(buffer, &acpi_rtype_names[i][j]); + j = 0; /* Use name separator from now on */ } - return (AE_OK); - - default: - break; + this_rtype <<= 1; /* Next Rtype */ } - - return (AE_AML_OPERAND_TYPE); } diff --git a/drivers/acpi/acpica/nsrepair.c b/drivers/acpi/acpica/nsrepair.c new file mode 100644 index 000000000000..db2b2a99c3a8 --- /dev/null +++ b/drivers/acpi/acpica/nsrepair.c @@ -0,0 +1,203 @@ +/****************************************************************************** + * + * Module Name: nsrepair - Repair for objects returned by predefined methods + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2009, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#include <acpi/acpi.h> +#include "accommon.h" +#include "acnamesp.h" +#include "acpredef.h" + +#define _COMPONENT ACPI_NAMESPACE +ACPI_MODULE_NAME("nsrepair") + +/******************************************************************************* + * + * FUNCTION: acpi_ns_repair_object + * + * PARAMETERS: Data - Pointer to validation data structure + * expected_btypes - Object types expected + * package_index - Index of object within parent package (if + * applicable - ACPI_NOT_PACKAGE_ELEMENT + * otherwise) + * return_object_ptr - Pointer to the object returned from the + * evaluation of a method or object + * + * RETURN: Status. AE_OK if repair was successful. + * + * DESCRIPTION: Attempt to repair/convert a return object of a type that was + * not expected. + * + ******************************************************************************/ +acpi_status +acpi_ns_repair_object(struct acpi_predefined_data *data, + u32 expected_btypes, + u32 package_index, + union acpi_operand_object **return_object_ptr) +{ + union acpi_operand_object *return_object = *return_object_ptr; + union acpi_operand_object *new_object; + acpi_size length; + + switch (return_object->common.type) { + case ACPI_TYPE_BUFFER: + + /* Does the method/object legally return a string? */ + + if (!(expected_btypes & ACPI_RTYPE_STRING)) { + return (AE_AML_OPERAND_TYPE); + } + + /* + * Have a Buffer, expected a String, convert. Use a to_string + * conversion, no transform performed on the buffer data. The best + * example of this is the _BIF method, where the string data from + * the battery is often (incorrectly) returned as buffer object(s). + */ + length = 0; + while ((length < return_object->buffer.length) && + (return_object->buffer.pointer[length])) { + length++; + } + + /* Allocate a new string object */ + + new_object = acpi_ut_create_string_object(length); + if (!new_object) { + return (AE_NO_MEMORY); + } + + /* + * Copy the raw buffer data with no transform. String is already NULL + * terminated at Length+1. + */ + ACPI_MEMCPY(new_object->string.pointer, + return_object->buffer.pointer, length); + + /* + * If the original object is a package element, we need to: + * 1. Set the reference count of the new object to match the + * reference count of the old object. + * 2. Decrement the reference count of the original object. + */ + if (package_index != ACPI_NOT_PACKAGE_ELEMENT) { + new_object->common.reference_count = + return_object->common.reference_count; + + if (return_object->common.reference_count > 1) { + return_object->common.reference_count--; + } + + ACPI_WARN_PREDEFINED((AE_INFO, data->pathname, + data->node_flags, + "Converted Buffer to expected String at index %u", + package_index)); + } else { + ACPI_WARN_PREDEFINED((AE_INFO, data->pathname, + data->node_flags, + "Converted Buffer to expected String")); + } + + /* Delete old object, install the new return object */ + + acpi_ut_remove_reference(return_object); + *return_object_ptr = new_object; + data->flags |= ACPI_OBJECT_REPAIRED; + return (AE_OK); + + default: + break; + } + + return (AE_AML_OPERAND_TYPE); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ns_repair_package_list + * + * PARAMETERS: Data - Pointer to validation data structure + * obj_desc_ptr - Pointer to the object to repair. The new + * package object is returned here, + * overwriting the old object. + * + * RETURN: Status, new object in *obj_desc_ptr + * + * DESCRIPTION: Repair a common problem with objects that are defined to return + * a variable-length Package of Packages. If the variable-length + * is one, some BIOS code mistakenly simply declares a single + * Package instead of a Package with one sub-Package. This + * function attempts to repair this error by wrapping a Package + * object around the original Package, creating the correct + * Package with one sub-Package. + * + * Names that can be repaired in this manner include: + * _ALR, _CSD, _HPX, _MLS, _PRT, _PSS, _TRT, TSS + * + ******************************************************************************/ + +acpi_status +acpi_ns_repair_package_list(struct acpi_predefined_data *data, + union acpi_operand_object **obj_desc_ptr) +{ + union acpi_operand_object *pkg_obj_desc; + + /* + * Create the new outer package and populate it. The new package will + * have a single element, the lone subpackage. + */ + pkg_obj_desc = acpi_ut_create_package_object(1); + if (!pkg_obj_desc) { + return (AE_NO_MEMORY); + } + + pkg_obj_desc->package.elements[0] = *obj_desc_ptr; + + /* Return the new object in the object pointer */ + + *obj_desc_ptr = pkg_obj_desc; + data->flags |= ACPI_OBJECT_REPAIRED; + + ACPI_WARN_PREDEFINED((AE_INFO, data->pathname, data->node_flags, + "Incorrectly formed Package, attempting repair")); + + return (AE_OK); +} diff --git a/drivers/acpi/acpica/nsutils.c b/drivers/acpi/acpica/nsutils.c index 78277ed08339..ea55ab4f9849 100644 --- a/drivers/acpi/acpica/nsutils.c +++ b/drivers/acpi/acpica/nsutils.c @@ -88,7 +88,8 @@ acpi_ns_report_error(const char *module_name, /* There is a non-ascii character in the name */ - ACPI_MOVE_32_TO_32(&bad_name, internal_name); + ACPI_MOVE_32_TO_32(&bad_name, + ACPI_CAST_PTR(u32, internal_name)); acpi_os_printf("[0x%4.4X] (NON-ASCII)", bad_name); } else { /* Convert path to external format */ @@ -836,7 +837,7 @@ acpi_ns_get_node(struct acpi_namespace_node *prefix_node, acpi_status status; char *internal_path; - ACPI_FUNCTION_TRACE_PTR(ns_get_node, pathname); + ACPI_FUNCTION_TRACE_PTR(ns_get_node, ACPI_CAST_PTR(char, pathname)); if (!pathname) { *return_node = prefix_node; diff --git a/drivers/acpi/acpica/nsxfeval.c b/drivers/acpi/acpica/nsxfeval.c index daf4ad37896d..4929dbdbc8f0 100644 --- a/drivers/acpi/acpica/nsxfeval.c +++ b/drivers/acpi/acpica/nsxfeval.c @@ -535,10 +535,11 @@ acpi_ns_get_device_callback(acpi_handle obj_handle, acpi_status status; struct acpi_namespace_node *node; u32 flags; - struct acpica_device_id hid; - struct acpi_compatible_id_list *cid; + struct acpica_device_id *hid; + struct acpica_device_id_list *cid; u32 i; - int found; + u8 found; + int no_match; status = acpi_ut_acquire_mutex(ACPI_MTX_NAMESPACE); if (ACPI_FAILURE(status)) { @@ -582,10 +583,14 @@ acpi_ns_get_device_callback(acpi_handle obj_handle, return (AE_CTRL_DEPTH); } - if (ACPI_STRNCMP(hid.value, info->hid, sizeof(hid.value)) != 0) { - - /* Get the list of Compatible IDs */ + no_match = ACPI_STRCMP(hid->string, info->hid); + ACPI_FREE(hid); + if (no_match) { + /* + * HID does not match, attempt match within the + * list of Compatible IDs (CIDs) + */ status = acpi_ut_execute_CID(node, &cid); if (status == AE_NOT_FOUND) { return (AE_OK); @@ -597,10 +602,8 @@ acpi_ns_get_device_callback(acpi_handle obj_handle, found = 0; for (i = 0; i < cid->count; i++) { - if (ACPI_STRNCMP(cid->id[i].value, info->hid, - sizeof(struct - acpi_compatible_id)) == - 0) { + if (ACPI_STRCMP(cid->ids[i].string, info->hid) + == 0) { found = 1; break; } diff --git a/drivers/acpi/acpica/nsxfname.c b/drivers/acpi/acpica/nsxfname.c index f23593d6add4..ddc84af6336e 100644 --- a/drivers/acpi/acpica/nsxfname.c +++ b/drivers/acpi/acpica/nsxfname.c @@ -51,6 +51,11 @@ #define _COMPONENT ACPI_NAMESPACE ACPI_MODULE_NAME("nsxfname") +/* Local prototypes */ +static char *acpi_ns_copy_device_id(struct acpica_device_id *dest, + struct acpica_device_id *source, + char *string_area); + /****************************************************************************** * * FUNCTION: acpi_get_handle @@ -68,6 +73,7 @@ ACPI_MODULE_NAME("nsxfname") * namespace handle. * ******************************************************************************/ + acpi_status acpi_get_handle(acpi_handle parent, acpi_string pathname, acpi_handle * ret_handle) @@ -210,10 +216,38 @@ ACPI_EXPORT_SYMBOL(acpi_get_name) /****************************************************************************** * + * FUNCTION: acpi_ns_copy_device_id + * + * PARAMETERS: Dest - Pointer to the destination DEVICE_ID + * Source - Pointer to the source DEVICE_ID + * string_area - Pointer to where to copy the dest string + * + * RETURN: Pointer to the next string area + * + * DESCRIPTION: Copy a single DEVICE_ID, including the string data. + * + ******************************************************************************/ +static char *acpi_ns_copy_device_id(struct acpica_device_id *dest, + struct acpica_device_id *source, + char *string_area) +{ + /* Create the destination DEVICE_ID */ + + dest->string = string_area; + dest->length = source->length; + + /* Copy actual string and return a pointer to the next string area */ + + ACPI_MEMCPY(string_area, source->string, source->length); + return (string_area + source->length); +} + +/****************************************************************************** + * * FUNCTION: acpi_get_object_info * - * PARAMETERS: Handle - Object Handle - * Buffer - Where the info is returned + * PARAMETERS: Handle - Object Handle + * return_buffer - Where the info is returned * * RETURN: Status * @@ -221,33 +255,37 @@ ACPI_EXPORT_SYMBOL(acpi_get_name) * namespace node and possibly by running several standard * control methods (Such as in the case of a device.) * + * For Device and Processor objects, run the Device _HID, _UID, _CID, _STA, + * _ADR, _sx_w, and _sx_d methods. + * + * Note: Allocates the return buffer, must be freed by the caller. + * ******************************************************************************/ + acpi_status -acpi_get_object_info(acpi_handle handle, struct acpi_buffer * buffer) +acpi_get_object_info(acpi_handle handle, + struct acpi_device_info **return_buffer) { - acpi_status status; struct acpi_namespace_node *node; struct acpi_device_info *info; - struct acpi_device_info *return_info; - struct acpi_compatible_id_list *cid_list = NULL; - acpi_size size; + struct acpica_device_id_list *cid_list = NULL; + struct acpica_device_id *hid = NULL; + struct acpica_device_id *uid = NULL; + char *next_id_string; + acpi_object_type type; + acpi_name name; + u8 param_count = 0; + u8 valid = 0; + u32 info_size; + u32 i; + acpi_status status; /* Parameter validation */ - if (!handle || !buffer) { + if (!handle || !return_buffer) { return (AE_BAD_PARAMETER); } - status = acpi_ut_validate_buffer(buffer); - if (ACPI_FAILURE(status)) { - return (status); - } - - info = ACPI_ALLOCATE_ZEROED(sizeof(struct acpi_device_info)); - if (!info) { - return (AE_NO_MEMORY); - } - status = acpi_ut_acquire_mutex(ACPI_MTX_NAMESPACE); if (ACPI_FAILURE(status)) { goto cleanup; @@ -256,66 +294,91 @@ acpi_get_object_info(acpi_handle handle, struct acpi_buffer * buffer) node = acpi_ns_map_handle_to_node(handle); if (!node) { (void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE); - status = AE_BAD_PARAMETER; - goto cleanup; + return (AE_BAD_PARAMETER); } - /* Init return structure */ - - size = sizeof(struct acpi_device_info); + /* Get the namespace node data while the namespace is locked */ - info->type = node->type; - info->name = node->name.integer; - info->valid = 0; + info_size = sizeof(struct acpi_device_info); + type = node->type; + name = node->name.integer; if (node->type == ACPI_TYPE_METHOD) { - info->param_count = node->object->method.param_count; + param_count = node->object->method.param_count; } status = acpi_ut_release_mutex(ACPI_MTX_NAMESPACE); if (ACPI_FAILURE(status)) { - goto cleanup; + return (status); } - /* If not a device, we are all done */ - - if (info->type == ACPI_TYPE_DEVICE) { + if ((type == ACPI_TYPE_DEVICE) || (type == ACPI_TYPE_PROCESSOR)) { /* - * Get extra info for ACPI Devices objects only: - * Run the Device _HID, _UID, _CID, _STA, _ADR and _sx_d methods. + * Get extra info for ACPI Device/Processor objects only: + * Run the Device _HID, _UID, and _CID methods. * * Note: none of these methods are required, so they may or may - * not be present for this device. The Info->Valid bitfield is used - * to indicate which methods were found and ran successfully. + * not be present for this device. The Info->Valid bitfield is used + * to indicate which methods were found and run successfully. */ /* Execute the Device._HID method */ - status = acpi_ut_execute_HID(node, &info->hardware_id); + status = acpi_ut_execute_HID(node, &hid); if (ACPI_SUCCESS(status)) { - info->valid |= ACPI_VALID_HID; + info_size += hid->length; + valid |= ACPI_VALID_HID; } /* Execute the Device._UID method */ - status = acpi_ut_execute_UID(node, &info->unique_id); + status = acpi_ut_execute_UID(node, &uid); if (ACPI_SUCCESS(status)) { - info->valid |= ACPI_VALID_UID; + info_size += uid->length; + valid |= ACPI_VALID_UID; } /* Execute the Device._CID method */ status = acpi_ut_execute_CID(node, &cid_list); if (ACPI_SUCCESS(status)) { - size += cid_list->size; - info->valid |= ACPI_VALID_CID; + + /* Add size of CID strings and CID pointer array */ + + info_size += + (cid_list->list_size - + sizeof(struct acpica_device_id_list)); + valid |= ACPI_VALID_CID; } + } + + /* + * Now that we have the variable-length data, we can allocate the + * return buffer + */ + info = ACPI_ALLOCATE_ZEROED(info_size); + if (!info) { + status = AE_NO_MEMORY; + goto cleanup; + } + + /* Get the fixed-length data */ + + if ((type == ACPI_TYPE_DEVICE) || (type == ACPI_TYPE_PROCESSOR)) { + /* + * Get extra info for ACPI Device/Processor objects only: + * Run the _STA, _ADR and, sx_w, and _sx_d methods. + * + * Note: none of these methods are required, so they may or may + * not be present for this device. The Info->Valid bitfield is used + * to indicate which methods were found and run successfully. + */ /* Execute the Device._STA method */ status = acpi_ut_execute_STA(node, &info->current_status); if (ACPI_SUCCESS(status)) { - info->valid |= ACPI_VALID_STA; + valid |= ACPI_VALID_STA; } /* Execute the Device._ADR method */ @@ -323,36 +386,100 @@ acpi_get_object_info(acpi_handle handle, struct acpi_buffer * buffer) status = acpi_ut_evaluate_numeric_object(METHOD_NAME__ADR, node, &info->address); if (ACPI_SUCCESS(status)) { - info->valid |= ACPI_VALID_ADR; + valid |= ACPI_VALID_ADR; + } + + /* Execute the Device._sx_w methods */ + + status = acpi_ut_execute_power_methods(node, + acpi_gbl_lowest_dstate_names, + ACPI_NUM_sx_w_METHODS, + info->lowest_dstates); + if (ACPI_SUCCESS(status)) { + valid |= ACPI_VALID_SXWS; } /* Execute the Device._sx_d methods */ - status = acpi_ut_execute_sxds(node, info->highest_dstates); + status = acpi_ut_execute_power_methods(node, + acpi_gbl_highest_dstate_names, + ACPI_NUM_sx_d_METHODS, + info->highest_dstates); if (ACPI_SUCCESS(status)) { - info->valid |= ACPI_VALID_SXDS; + valid |= ACPI_VALID_SXDS; } } - /* Validate/Allocate/Clear caller buffer */ + /* + * Create a pointer to the string area of the return buffer. + * Point to the end of the base struct acpi_device_info structure. + */ + next_id_string = ACPI_CAST_PTR(char, info->compatible_id_list.ids); + if (cid_list) { - status = acpi_ut_initialize_buffer(buffer, size); - if (ACPI_FAILURE(status)) { - goto cleanup; + /* Point past the CID DEVICE_ID array */ + + next_id_string += + ((acpi_size) cid_list->count * + sizeof(struct acpica_device_id)); } - /* Populate the return buffer */ + /* + * Copy the HID, UID, and CIDs to the return buffer. The variable-length + * strings are copied to the reserved area at the end of the buffer. + * + * For HID and CID, check if the ID is a PCI Root Bridge. + */ + if (hid) { + next_id_string = acpi_ns_copy_device_id(&info->hardware_id, + hid, next_id_string); + + if (acpi_ut_is_pci_root_bridge(hid->string)) { + info->flags |= ACPI_PCI_ROOT_BRIDGE; + } + } - return_info = buffer->pointer; - ACPI_MEMCPY(return_info, info, sizeof(struct acpi_device_info)); + if (uid) { + next_id_string = acpi_ns_copy_device_id(&info->unique_id, + uid, next_id_string); + } if (cid_list) { - ACPI_MEMCPY(&return_info->compatibility_id, cid_list, - cid_list->size); + info->compatible_id_list.count = cid_list->count; + info->compatible_id_list.list_size = cid_list->list_size; + + /* Copy each CID */ + + for (i = 0; i < cid_list->count; i++) { + next_id_string = + acpi_ns_copy_device_id(&info->compatible_id_list. + ids[i], &cid_list->ids[i], + next_id_string); + + if (acpi_ut_is_pci_root_bridge(cid_list->ids[i].string)) { + info->flags |= ACPI_PCI_ROOT_BRIDGE; + } + } } + /* Copy the fixed-length data */ + + info->info_size = info_size; + info->type = type; + info->name = name; + info->param_count = param_count; + info->valid = valid; + + *return_buffer = info; + status = AE_OK; + cleanup: - ACPI_FREE(info); + if (hid) { + ACPI_FREE(hid); + } + if (uid) { + ACPI_FREE(uid); + } if (cid_list) { ACPI_FREE(cid_list); } diff --git a/drivers/acpi/acpica/psloop.c b/drivers/acpi/acpica/psloop.c index c5f6ce19a401..cd7995b3aed4 100644 --- a/drivers/acpi/acpica/psloop.c +++ b/drivers/acpi/acpica/psloop.c @@ -86,6 +86,9 @@ static acpi_status acpi_ps_complete_final_op(struct acpi_walk_state *walk_state, union acpi_parse_object *op, acpi_status status); +static void +acpi_ps_link_module_code(u8 *aml_start, u32 aml_length, acpi_owner_id owner_id); + /******************************************************************************* * * FUNCTION: acpi_ps_get_aml_opcode @@ -390,6 +393,7 @@ acpi_ps_get_arguments(struct acpi_walk_state *walk_state, { acpi_status status = AE_OK; union acpi_parse_object *arg = NULL; + const struct acpi_opcode_info *op_info; ACPI_FUNCTION_TRACE_PTR(ps_get_arguments, walk_state); @@ -449,13 +453,11 @@ acpi_ps_get_arguments(struct acpi_walk_state *walk_state, INCREMENT_ARG_LIST(walk_state->arg_types); } - /* Special processing for certain opcodes */ - - /* TBD (remove): Temporary mechanism to disable this code if needed */ - -#ifdef ACPI_ENABLE_MODULE_LEVEL_CODE - - if ((walk_state->pass_number <= ACPI_IMODE_LOAD_PASS1) && + /* + * Handle executable code at "module-level". This refers to + * executable opcodes that appear outside of any control method. + */ + if ((walk_state->pass_number <= ACPI_IMODE_LOAD_PASS2) && ((walk_state->parse_flags & ACPI_PARSE_DISASSEMBLE) == 0)) { /* * We want to skip If/Else/While constructs during Pass1 because we @@ -469,6 +471,23 @@ acpi_ps_get_arguments(struct acpi_walk_state *walk_state, case AML_ELSE_OP: case AML_WHILE_OP: + /* + * Currently supported module-level opcodes are: + * IF/ELSE/WHILE. These appear to be the most common, + * and easiest to support since they open an AML + * package. + */ + if (walk_state->pass_number == + ACPI_IMODE_LOAD_PASS1) { + acpi_ps_link_module_code(aml_op_start, + walk_state-> + parser_state. + pkg_end - + aml_op_start, + walk_state-> + owner_id); + } + ACPI_DEBUG_PRINT((ACPI_DB_PARSE, "Pass1: Skipping an If/Else/While body\n")); @@ -480,10 +499,34 @@ acpi_ps_get_arguments(struct acpi_walk_state *walk_state, break; default: + /* + * Check for an unsupported executable opcode at module + * level. We must be in PASS1, the parent must be a SCOPE, + * The opcode class must be EXECUTE, and the opcode must + * not be an argument to another opcode. + */ + if ((walk_state->pass_number == + ACPI_IMODE_LOAD_PASS1) + && (op->common.parent->common.aml_opcode == + AML_SCOPE_OP)) { + op_info = + acpi_ps_get_opcode_info(op->common. + aml_opcode); + if ((op_info->class == + AML_CLASS_EXECUTE) && (!arg)) { + ACPI_WARNING((AE_INFO, + "Detected an unsupported executable opcode " + "at module-level: [0x%.4X] at table offset 0x%.4X", + op->common.aml_opcode, + (u32)((aml_op_start - walk_state->parser_state.aml_start) + + sizeof(struct acpi_table_header)))); + } + } break; } } -#endif + + /* Special processing for certain opcodes */ switch (op->common.aml_opcode) { case AML_METHOD_OP: @@ -553,6 +596,66 @@ acpi_ps_get_arguments(struct acpi_walk_state *walk_state, /******************************************************************************* * + * FUNCTION: acpi_ps_link_module_code + * + * PARAMETERS: aml_start - Pointer to the AML + * aml_length - Length of executable AML + * owner_id - owner_id of module level code + * + * RETURN: None. + * + * DESCRIPTION: Wrap the module-level code with a method object and link the + * object to the global list. Note, the mutex field of the method + * object is used to link multiple module-level code objects. + * + ******************************************************************************/ + +static void +acpi_ps_link_module_code(u8 *aml_start, u32 aml_length, acpi_owner_id owner_id) +{ + union acpi_operand_object *prev; + union acpi_operand_object *next; + union acpi_operand_object *method_obj; + + /* Get the tail of the list */ + + prev = next = acpi_gbl_module_code_list; + while (next) { + prev = next; + next = next->method.mutex; + } + + /* + * Insert the module level code into the list. Merge it if it is + * adjacent to the previous element. + */ + if (!prev || + ((prev->method.aml_start + prev->method.aml_length) != aml_start)) { + + /* Create, initialize, and link a new temporary method object */ + + method_obj = acpi_ut_create_internal_object(ACPI_TYPE_METHOD); + if (!method_obj) { + return; + } + + method_obj->method.aml_start = aml_start; + method_obj->method.aml_length = aml_length; + method_obj->method.owner_id = owner_id; + method_obj->method.flags |= AOPOBJ_MODULE_LEVEL; + + if (!prev) { + acpi_gbl_module_code_list = method_obj; + } else { + prev->method.mutex = method_obj; + } + } else { + prev->method.aml_length += aml_length; + } +} + +/******************************************************************************* + * * FUNCTION: acpi_ps_complete_op * * PARAMETERS: walk_state - Current state diff --git a/drivers/acpi/acpica/psxface.c b/drivers/acpi/acpica/psxface.c index ff06032c0f06..dd9731c29a79 100644 --- a/drivers/acpi/acpica/psxface.c +++ b/drivers/acpi/acpica/psxface.c @@ -280,6 +280,10 @@ acpi_status acpi_ps_execute_method(struct acpi_evaluate_info *info) goto cleanup; } + if (info->obj_desc->method.flags & AOPOBJ_MODULE_LEVEL) { + walk_state->parse_flags |= ACPI_PARSE_MODULE_LEVEL; + } + /* Invoke an internal method if necessary */ if (info->obj_desc->method.method_flags & AML_METHOD_INTERNAL_ONLY) { diff --git a/drivers/acpi/acpica/tbutils.c b/drivers/acpi/acpica/tbutils.c index ef7d2c2d8f0b..1f15497f00d1 100644 --- a/drivers/acpi/acpica/tbutils.c +++ b/drivers/acpi/acpica/tbutils.c @@ -49,6 +49,12 @@ ACPI_MODULE_NAME("tbutils") /* Local prototypes */ +static void acpi_tb_fix_string(char *string, acpi_size length); + +static void +acpi_tb_cleanup_table_header(struct acpi_table_header *out_header, + struct acpi_table_header *header); + static acpi_physical_address acpi_tb_get_root_table_entry(u8 *table_entry, u32 table_entry_size); @@ -161,6 +167,59 @@ u8 acpi_tb_tables_loaded(void) /******************************************************************************* * + * FUNCTION: acpi_tb_fix_string + * + * PARAMETERS: String - String to be repaired + * Length - Maximum length + * + * RETURN: None + * + * DESCRIPTION: Replace every non-printable or non-ascii byte in the string + * with a question mark '?'. + * + ******************************************************************************/ + +static void acpi_tb_fix_string(char *string, acpi_size length) +{ + + while (length && *string) { + if (!ACPI_IS_PRINT(*string)) { + *string = '?'; + } + string++; + length--; + } +} + +/******************************************************************************* + * + * FUNCTION: acpi_tb_cleanup_table_header + * + * PARAMETERS: out_header - Where the cleaned header is returned + * Header - Input ACPI table header + * + * RETURN: Returns the cleaned header in out_header + * + * DESCRIPTION: Copy the table header and ensure that all "string" fields in + * the header consist of printable characters. + * + ******************************************************************************/ + +static void +acpi_tb_cleanup_table_header(struct acpi_table_header *out_header, + struct acpi_table_header *header) +{ + + ACPI_MEMCPY(out_header, header, sizeof(struct acpi_table_header)); + + acpi_tb_fix_string(out_header->signature, ACPI_NAME_SIZE); + acpi_tb_fix_string(out_header->oem_id, ACPI_OEM_ID_SIZE); + acpi_tb_fix_string(out_header->oem_table_id, ACPI_OEM_TABLE_ID_SIZE); + acpi_tb_fix_string(out_header->asl_compiler_id, ACPI_NAME_SIZE); +} + +/******************************************************************************* + * * FUNCTION: acpi_tb_print_table_header * * PARAMETERS: Address - Table physical address @@ -176,6 +235,7 @@ void acpi_tb_print_table_header(acpi_physical_address address, struct acpi_table_header *header) { + struct acpi_table_header local_header; /* * The reason that the Address is cast to a void pointer is so that we @@ -192,6 +252,11 @@ acpi_tb_print_table_header(acpi_physical_address address, /* RSDP has no common fields */ + ACPI_MEMCPY(local_header.oem_id, + ACPI_CAST_PTR(struct acpi_table_rsdp, + header)->oem_id, ACPI_OEM_ID_SIZE); + acpi_tb_fix_string(local_header.oem_id, ACPI_OEM_ID_SIZE); + ACPI_INFO((AE_INFO, "RSDP %p %05X (v%.2d %6.6s)", ACPI_CAST_PTR (void, address), (ACPI_CAST_PTR(struct acpi_table_rsdp, header)-> @@ -200,18 +265,21 @@ acpi_tb_print_table_header(acpi_physical_address address, header)->length : 20, ACPI_CAST_PTR(struct acpi_table_rsdp, header)->revision, - ACPI_CAST_PTR(struct acpi_table_rsdp, - header)->oem_id)); + local_header.oem_id)); } else { /* Standard ACPI table with full common header */ + acpi_tb_cleanup_table_header(&local_header, header); + ACPI_INFO((AE_INFO, "%4.4s %p %05X (v%.2d %6.6s %8.8s %08X %4.4s %08X)", - header->signature, ACPI_CAST_PTR (void, address), - header->length, header->revision, header->oem_id, - header->oem_table_id, header->oem_revision, - header->asl_compiler_id, - header->asl_compiler_revision)); + local_header.signature, ACPI_CAST_PTR(void, address), + local_header.length, local_header.revision, + local_header.oem_id, local_header.oem_table_id, + local_header.oem_revision, + local_header.asl_compiler_id, + local_header.asl_compiler_revision)); + } } diff --git a/drivers/acpi/acpica/utdelete.c b/drivers/acpi/acpica/utdelete.c index bc1710315088..96e26e70c63d 100644 --- a/drivers/acpi/acpica/utdelete.c +++ b/drivers/acpi/acpica/utdelete.c @@ -215,6 +215,12 @@ static void acpi_ut_delete_internal_obj(union acpi_operand_object *object) ACPI_DEBUG_PRINT((ACPI_DB_ALLOCATIONS, "***** Region %p\n", object)); + /* Invalidate the region address/length via the host OS */ + + acpi_os_invalidate_address(object->region.space_id, + object->region.address, + (acpi_size) object->region.length); + second_desc = acpi_ns_get_secondary_object(object); if (second_desc) { /* diff --git a/drivers/acpi/acpica/uteval.c b/drivers/acpi/acpica/uteval.c index 006b16c26017..5d54e36ab453 100644 --- a/drivers/acpi/acpica/uteval.c +++ b/drivers/acpi/acpica/uteval.c @@ -44,19 +44,10 @@ #include <acpi/acpi.h> #include "accommon.h" #include "acnamesp.h" -#include "acinterp.h" #define _COMPONENT ACPI_UTILITIES ACPI_MODULE_NAME("uteval") -/* Local prototypes */ -static void -acpi_ut_copy_id_string(char *destination, char *source, acpi_size max_length); - -static acpi_status -acpi_ut_translate_one_cid(union acpi_operand_object *obj_desc, - struct acpi_compatible_id *one_cid); - /* * Strings supported by the _OSI predefined (internal) method. * @@ -78,6 +69,9 @@ static struct acpi_interface_info acpi_interfaces_supported[] = { {"Windows 2001 SP2", ACPI_OSI_WIN_XP_SP2}, /* Windows XP SP2 */ {"Windows 2001.1 SP1", ACPI_OSI_WINSRV_2003_SP1}, /* Windows Server 2003 SP1 - Added 03/2006 */ {"Windows 2006", ACPI_OSI_WIN_VISTA}, /* Windows Vista - Added 03/2006 */ + {"Windows 2006.1", ACPI_OSI_WINSRV_2008}, /* Windows Server 2008 - Added 09/2009 */ + {"Windows 2006 SP1", ACPI_OSI_WIN_VISTA_SP1}, /* Windows Vista SP1 - Added 09/2009 */ + {"Windows 2009", ACPI_OSI_WIN_7}, /* Windows 7 and Server 2008 R2 - Added 09/2009 */ /* Feature Group Strings */ @@ -213,7 +207,7 @@ acpi_status acpi_osi_invalidate(char *interface) * RETURN: Status * * DESCRIPTION: Evaluates a namespace object and verifies the type of the - * return object. Common code that simplifies accessing objects + * return object. Common code that simplifies accessing objects * that have required return objects of fixed types. * * NOTE: Internal function, no parameter validation @@ -298,7 +292,7 @@ acpi_ut_evaluate_object(struct acpi_namespace_node *prefix_node, if ((acpi_gbl_enable_interpreter_slack) && (!expected_return_btypes)) { /* - * We received a return object, but one was not expected. This can + * We received a return object, but one was not expected. This can * happen frequently if the "implicit return" feature is enabled. * Just delete the return object and return AE_OK. */ @@ -340,12 +334,12 @@ acpi_ut_evaluate_object(struct acpi_namespace_node *prefix_node, * * PARAMETERS: object_name - Object name to be evaluated * device_node - Node for the device - * Address - Where the value is returned + * Value - Where the value is returned * * RETURN: Status * * DESCRIPTION: Evaluates a numeric namespace object for a selected device - * and stores result in *Address. + * and stores result in *Value. * * NOTE: Internal function, no parameter validation * @@ -354,7 +348,7 @@ acpi_ut_evaluate_object(struct acpi_namespace_node *prefix_node, acpi_status acpi_ut_evaluate_numeric_object(char *object_name, struct acpi_namespace_node *device_node, - acpi_integer * address) + acpi_integer *value) { union acpi_operand_object *obj_desc; acpi_status status; @@ -369,295 +363,7 @@ acpi_ut_evaluate_numeric_object(char *object_name, /* Get the returned Integer */ - *address = obj_desc->integer.value; - - /* On exit, we must delete the return object */ - - acpi_ut_remove_reference(obj_desc); - return_ACPI_STATUS(status); -} - -/******************************************************************************* - * - * FUNCTION: acpi_ut_copy_id_string - * - * PARAMETERS: Destination - Where to copy the string - * Source - Source string - * max_length - Length of the destination buffer - * - * RETURN: None - * - * DESCRIPTION: Copies an ID string for the _HID, _CID, and _UID methods. - * Performs removal of a leading asterisk if present -- workaround - * for a known issue on a bunch of machines. - * - ******************************************************************************/ - -static void -acpi_ut_copy_id_string(char *destination, char *source, acpi_size max_length) -{ - - /* - * Workaround for ID strings that have a leading asterisk. This construct - * is not allowed by the ACPI specification (ID strings must be - * alphanumeric), but enough existing machines have this embedded in their - * ID strings that the following code is useful. - */ - if (*source == '*') { - source++; - } - - /* Do the actual copy */ - - ACPI_STRNCPY(destination, source, max_length); -} - -/******************************************************************************* - * - * FUNCTION: acpi_ut_execute_HID - * - * PARAMETERS: device_node - Node for the device - * Hid - Where the HID is returned - * - * RETURN: Status - * - * DESCRIPTION: Executes the _HID control method that returns the hardware - * ID of the device. - * - * NOTE: Internal function, no parameter validation - * - ******************************************************************************/ - -acpi_status -acpi_ut_execute_HID(struct acpi_namespace_node *device_node, - struct acpica_device_id *hid) -{ - union acpi_operand_object *obj_desc; - acpi_status status; - - ACPI_FUNCTION_TRACE(ut_execute_HID); - - status = acpi_ut_evaluate_object(device_node, METHOD_NAME__HID, - ACPI_BTYPE_INTEGER | ACPI_BTYPE_STRING, - &obj_desc); - if (ACPI_FAILURE(status)) { - return_ACPI_STATUS(status); - } - - if (obj_desc->common.type == ACPI_TYPE_INTEGER) { - - /* Convert the Numeric HID to string */ - - acpi_ex_eisa_id_to_string((u32) obj_desc->integer.value, - hid->value); - } else { - /* Copy the String HID from the returned object */ - - acpi_ut_copy_id_string(hid->value, obj_desc->string.pointer, - sizeof(hid->value)); - } - - /* On exit, we must delete the return object */ - - acpi_ut_remove_reference(obj_desc); - return_ACPI_STATUS(status); -} - -/******************************************************************************* - * - * FUNCTION: acpi_ut_translate_one_cid - * - * PARAMETERS: obj_desc - _CID object, must be integer or string - * one_cid - Where the CID string is returned - * - * RETURN: Status - * - * DESCRIPTION: Return a numeric or string _CID value as a string. - * (Compatible ID) - * - * NOTE: Assumes a maximum _CID string length of - * ACPI_MAX_CID_LENGTH. - * - ******************************************************************************/ - -static acpi_status -acpi_ut_translate_one_cid(union acpi_operand_object *obj_desc, - struct acpi_compatible_id *one_cid) -{ - - switch (obj_desc->common.type) { - case ACPI_TYPE_INTEGER: - - /* Convert the Numeric CID to string */ - - acpi_ex_eisa_id_to_string((u32) obj_desc->integer.value, - one_cid->value); - return (AE_OK); - - case ACPI_TYPE_STRING: - - if (obj_desc->string.length > ACPI_MAX_CID_LENGTH) { - return (AE_AML_STRING_LIMIT); - } - - /* Copy the String CID from the returned object */ - - acpi_ut_copy_id_string(one_cid->value, obj_desc->string.pointer, - ACPI_MAX_CID_LENGTH); - return (AE_OK); - - default: - - return (AE_TYPE); - } -} - -/******************************************************************************* - * - * FUNCTION: acpi_ut_execute_CID - * - * PARAMETERS: device_node - Node for the device - * return_cid_list - Where the CID list is returned - * - * RETURN: Status - * - * DESCRIPTION: Executes the _CID control method that returns one or more - * compatible hardware IDs for the device. - * - * NOTE: Internal function, no parameter validation - * - ******************************************************************************/ - -acpi_status -acpi_ut_execute_CID(struct acpi_namespace_node * device_node, - struct acpi_compatible_id_list ** return_cid_list) -{ - union acpi_operand_object *obj_desc; - acpi_status status; - u32 count; - u32 size; - struct acpi_compatible_id_list *cid_list; - u32 i; - - ACPI_FUNCTION_TRACE(ut_execute_CID); - - /* Evaluate the _CID method for this device */ - - status = acpi_ut_evaluate_object(device_node, METHOD_NAME__CID, - ACPI_BTYPE_INTEGER | ACPI_BTYPE_STRING - | ACPI_BTYPE_PACKAGE, &obj_desc); - if (ACPI_FAILURE(status)) { - return_ACPI_STATUS(status); - } - - /* Get the number of _CIDs returned */ - - count = 1; - if (obj_desc->common.type == ACPI_TYPE_PACKAGE) { - count = obj_desc->package.count; - } - - /* Allocate a worst-case buffer for the _CIDs */ - - size = (((count - 1) * sizeof(struct acpi_compatible_id)) + - sizeof(struct acpi_compatible_id_list)); - - cid_list = ACPI_ALLOCATE_ZEROED((acpi_size) size); - if (!cid_list) { - return_ACPI_STATUS(AE_NO_MEMORY); - } - - /* Init CID list */ - - cid_list->count = count; - cid_list->size = size; - - /* - * A _CID can return either a single compatible ID or a package of - * compatible IDs. Each compatible ID can be one of the following: - * 1) Integer (32 bit compressed EISA ID) or - * 2) String (PCI ID format, e.g. "PCI\VEN_vvvv&DEV_dddd&SUBSYS_ssssssss") - */ - - /* The _CID object can be either a single CID or a package (list) of CIDs */ - - if (obj_desc->common.type == ACPI_TYPE_PACKAGE) { - - /* Translate each package element */ - - for (i = 0; i < count; i++) { - status = - acpi_ut_translate_one_cid(obj_desc->package. - elements[i], - &cid_list->id[i]); - if (ACPI_FAILURE(status)) { - break; - } - } - } else { - /* Only one CID, translate to a string */ - - status = acpi_ut_translate_one_cid(obj_desc, cid_list->id); - } - - /* Cleanup on error */ - - if (ACPI_FAILURE(status)) { - ACPI_FREE(cid_list); - } else { - *return_cid_list = cid_list; - } - - /* On exit, we must delete the _CID return object */ - - acpi_ut_remove_reference(obj_desc); - return_ACPI_STATUS(status); -} - -/******************************************************************************* - * - * FUNCTION: acpi_ut_execute_UID - * - * PARAMETERS: device_node - Node for the device - * Uid - Where the UID is returned - * - * RETURN: Status - * - * DESCRIPTION: Executes the _UID control method that returns the hardware - * ID of the device. - * - * NOTE: Internal function, no parameter validation - * - ******************************************************************************/ - -acpi_status -acpi_ut_execute_UID(struct acpi_namespace_node *device_node, - struct acpica_device_id *uid) -{ - union acpi_operand_object *obj_desc; - acpi_status status; - - ACPI_FUNCTION_TRACE(ut_execute_UID); - - status = acpi_ut_evaluate_object(device_node, METHOD_NAME__UID, - ACPI_BTYPE_INTEGER | ACPI_BTYPE_STRING, - &obj_desc); - if (ACPI_FAILURE(status)) { - return_ACPI_STATUS(status); - } - - if (obj_desc->common.type == ACPI_TYPE_INTEGER) { - - /* Convert the Numeric UID to string */ - - acpi_ex_unsigned_integer_to_string(obj_desc->integer.value, - uid->value); - } else { - /* Copy the String UID from the returned object */ - - acpi_ut_copy_id_string(uid->value, obj_desc->string.pointer, - sizeof(uid->value)); - } + *value = obj_desc->integer.value; /* On exit, we must delete the return object */ @@ -716,60 +422,64 @@ acpi_ut_execute_STA(struct acpi_namespace_node *device_node, u32 * flags) /******************************************************************************* * - * FUNCTION: acpi_ut_execute_Sxds + * FUNCTION: acpi_ut_execute_power_methods * * PARAMETERS: device_node - Node for the device - * Flags - Where the status flags are returned + * method_names - Array of power method names + * method_count - Number of methods to execute + * out_values - Where the power method values are returned * - * RETURN: Status + * RETURN: Status, out_values * - * DESCRIPTION: Executes _STA for selected device and stores results in - * *Flags. + * DESCRIPTION: Executes the specified power methods for the device and returns + * the result(s). * * NOTE: Internal function, no parameter validation * - ******************************************************************************/ +******************************************************************************/ acpi_status -acpi_ut_execute_sxds(struct acpi_namespace_node *device_node, u8 * highest) +acpi_ut_execute_power_methods(struct acpi_namespace_node *device_node, + const char **method_names, + u8 method_count, u8 *out_values) { union acpi_operand_object *obj_desc; acpi_status status; + acpi_status final_status = AE_NOT_FOUND; u32 i; - ACPI_FUNCTION_TRACE(ut_execute_sxds); + ACPI_FUNCTION_TRACE(ut_execute_power_methods); - for (i = 0; i < 4; i++) { - highest[i] = 0xFF; + for (i = 0; i < method_count; i++) { + /* + * Execute the power method (_sx_d or _sx_w). The only allowable + * return type is an Integer. + */ status = acpi_ut_evaluate_object(device_node, ACPI_CAST_PTR(char, - acpi_gbl_highest_dstate_names - [i]), + method_names[i]), ACPI_BTYPE_INTEGER, &obj_desc); - if (ACPI_FAILURE(status)) { - if (status != AE_NOT_FOUND) { - ACPI_DEBUG_PRINT((ACPI_DB_EXEC, - "%s on Device %4.4s, %s\n", - ACPI_CAST_PTR(char, - acpi_gbl_highest_dstate_names - [i]), - acpi_ut_get_node_name - (device_node), - acpi_format_exception - (status))); - - return_ACPI_STATUS(status); - } - } else { - /* Extract the Dstate value */ - - highest[i] = (u8) obj_desc->integer.value; + if (ACPI_SUCCESS(status)) { + out_values[i] = (u8)obj_desc->integer.value; /* Delete the return object */ acpi_ut_remove_reference(obj_desc); + final_status = AE_OK; /* At least one value is valid */ + continue; } + + out_values[i] = ACPI_UINT8_MAX; + if (status == AE_NOT_FOUND) { + continue; /* Ignore if not found */ + } + + ACPI_DEBUG_PRINT((ACPI_DB_EXEC, + "Failed %s on Device %4.4s, %s\n", + ACPI_CAST_PTR(char, method_names[i]), + acpi_ut_get_node_name(device_node), + acpi_format_exception(status))); } - return_ACPI_STATUS(AE_OK); + return_ACPI_STATUS(final_status); } diff --git a/drivers/acpi/acpica/utglobal.c b/drivers/acpi/acpica/utglobal.c index 59e46f257c02..3f2c68f4e959 100644 --- a/drivers/acpi/acpica/utglobal.c +++ b/drivers/acpi/acpica/utglobal.c @@ -90,7 +90,15 @@ const char *acpi_gbl_sleep_state_names[ACPI_S_STATE_COUNT] = { "\\_S5_" }; -const char *acpi_gbl_highest_dstate_names[4] = { +const char *acpi_gbl_lowest_dstate_names[ACPI_NUM_sx_w_METHODS] = { + "_S0W", + "_S1W", + "_S2W", + "_S3W", + "_S4W" +}; + +const char *acpi_gbl_highest_dstate_names[ACPI_NUM_sx_d_METHODS] = { "_S1D", "_S2D", "_S3D", @@ -351,6 +359,7 @@ const char *acpi_gbl_region_types[ACPI_NUM_PREDEFINED_REGIONS] = { "SMBus", "SystemCMOS", "PCIBARTarget", + "IPMI", "DataTable" }; @@ -798,6 +807,7 @@ acpi_status acpi_ut_init_globals(void) /* Namespace */ + acpi_gbl_module_code_list = NULL; acpi_gbl_root_node = NULL; acpi_gbl_root_node_struct.name.integer = ACPI_ROOT_NAME; acpi_gbl_root_node_struct.descriptor_type = ACPI_DESC_TYPE_NAMED; diff --git a/drivers/acpi/acpica/utids.c b/drivers/acpi/acpica/utids.c new file mode 100644 index 000000000000..52eaae404554 --- /dev/null +++ b/drivers/acpi/acpica/utids.c @@ -0,0 +1,382 @@ +/****************************************************************************** + * + * Module Name: utids - support for device IDs - HID, UID, CID + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2009, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#include <acpi/acpi.h> +#include "accommon.h" +#include "acinterp.h" + +#define _COMPONENT ACPI_UTILITIES +ACPI_MODULE_NAME("utids") + +/* Local prototypes */ +static void acpi_ut_copy_id_string(char *destination, char *source); + +/******************************************************************************* + * + * FUNCTION: acpi_ut_copy_id_string + * + * PARAMETERS: Destination - Where to copy the string + * Source - Source string + * + * RETURN: None + * + * DESCRIPTION: Copies an ID string for the _HID, _CID, and _UID methods. + * Performs removal of a leading asterisk if present -- workaround + * for a known issue on a bunch of machines. + * + ******************************************************************************/ + +static void acpi_ut_copy_id_string(char *destination, char *source) +{ + + /* + * Workaround for ID strings that have a leading asterisk. This construct + * is not allowed by the ACPI specification (ID strings must be + * alphanumeric), but enough existing machines have this embedded in their + * ID strings that the following code is useful. + */ + if (*source == '*') { + source++; + } + + /* Do the actual copy */ + + ACPI_STRCPY(destination, source); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ut_execute_HID + * + * PARAMETERS: device_node - Node for the device + * return_id - Where the string HID is returned + * + * RETURN: Status + * + * DESCRIPTION: Executes the _HID control method that returns the hardware + * ID of the device. The HID is either an 32-bit encoded EISAID + * Integer or a String. A string is always returned. An EISAID + * is converted to a string. + * + * NOTE: Internal function, no parameter validation + * + ******************************************************************************/ + +acpi_status +acpi_ut_execute_HID(struct acpi_namespace_node *device_node, + struct acpica_device_id **return_id) +{ + union acpi_operand_object *obj_desc; + struct acpica_device_id *hid; + u32 length; + acpi_status status; + + ACPI_FUNCTION_TRACE(ut_execute_HID); + + status = acpi_ut_evaluate_object(device_node, METHOD_NAME__HID, + ACPI_BTYPE_INTEGER | ACPI_BTYPE_STRING, + &obj_desc); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + /* Get the size of the String to be returned, includes null terminator */ + + if (obj_desc->common.type == ACPI_TYPE_INTEGER) { + length = ACPI_EISAID_STRING_SIZE; + } else { + length = obj_desc->string.length + 1; + } + + /* Allocate a buffer for the HID */ + + hid = + ACPI_ALLOCATE_ZEROED(sizeof(struct acpica_device_id) + + (acpi_size) length); + if (!hid) { + status = AE_NO_MEMORY; + goto cleanup; + } + + /* Area for the string starts after DEVICE_ID struct */ + + hid->string = ACPI_ADD_PTR(char, hid, sizeof(struct acpica_device_id)); + + /* Convert EISAID to a string or simply copy existing string */ + + if (obj_desc->common.type == ACPI_TYPE_INTEGER) { + acpi_ex_eisa_id_to_string(hid->string, obj_desc->integer.value); + } else { + acpi_ut_copy_id_string(hid->string, obj_desc->string.pointer); + } + + hid->length = length; + *return_id = hid; + +cleanup: + + /* On exit, we must delete the return object */ + + acpi_ut_remove_reference(obj_desc); + return_ACPI_STATUS(status); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ut_execute_UID + * + * PARAMETERS: device_node - Node for the device + * return_id - Where the string UID is returned + * + * RETURN: Status + * + * DESCRIPTION: Executes the _UID control method that returns the unique + * ID of the device. The UID is either a 64-bit Integer (NOT an + * EISAID) or a string. Always returns a string. A 64-bit integer + * is converted to a decimal string. + * + * NOTE: Internal function, no parameter validation + * + ******************************************************************************/ + +acpi_status +acpi_ut_execute_UID(struct acpi_namespace_node *device_node, + struct acpica_device_id **return_id) +{ + union acpi_operand_object *obj_desc; + struct acpica_device_id *uid; + u32 length; + acpi_status status; + + ACPI_FUNCTION_TRACE(ut_execute_UID); + + status = acpi_ut_evaluate_object(device_node, METHOD_NAME__UID, + ACPI_BTYPE_INTEGER | ACPI_BTYPE_STRING, + &obj_desc); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + /* Get the size of the String to be returned, includes null terminator */ + + if (obj_desc->common.type == ACPI_TYPE_INTEGER) { + length = ACPI_MAX64_DECIMAL_DIGITS + 1; + } else { + length = obj_desc->string.length + 1; + } + + /* Allocate a buffer for the UID */ + + uid = + ACPI_ALLOCATE_ZEROED(sizeof(struct acpica_device_id) + + (acpi_size) length); + if (!uid) { + status = AE_NO_MEMORY; + goto cleanup; + } + + /* Area for the string starts after DEVICE_ID struct */ + + uid->string = ACPI_ADD_PTR(char, uid, sizeof(struct acpica_device_id)); + + /* Convert an Integer to string, or just copy an existing string */ + + if (obj_desc->common.type == ACPI_TYPE_INTEGER) { + acpi_ex_integer_to_string(uid->string, obj_desc->integer.value); + } else { + acpi_ut_copy_id_string(uid->string, obj_desc->string.pointer); + } + + uid->length = length; + *return_id = uid; + +cleanup: + + /* On exit, we must delete the return object */ + + acpi_ut_remove_reference(obj_desc); + return_ACPI_STATUS(status); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ut_execute_CID + * + * PARAMETERS: device_node - Node for the device + * return_cid_list - Where the CID list is returned + * + * RETURN: Status, list of CID strings + * + * DESCRIPTION: Executes the _CID control method that returns one or more + * compatible hardware IDs for the device. + * + * NOTE: Internal function, no parameter validation + * + * A _CID method can return either a single compatible ID or a package of + * compatible IDs. Each compatible ID can be one of the following: + * 1) Integer (32 bit compressed EISA ID) or + * 2) String (PCI ID format, e.g. "PCI\VEN_vvvv&DEV_dddd&SUBSYS_ssssssss") + * + * The Integer CIDs are converted to string format by this function. + * + ******************************************************************************/ + +acpi_status +acpi_ut_execute_CID(struct acpi_namespace_node *device_node, + struct acpica_device_id_list **return_cid_list) +{ + union acpi_operand_object **cid_objects; + union acpi_operand_object *obj_desc; + struct acpica_device_id_list *cid_list; + char *next_id_string; + u32 string_area_size; + u32 length; + u32 cid_list_size; + acpi_status status; + u32 count; + u32 i; + + ACPI_FUNCTION_TRACE(ut_execute_CID); + + /* Evaluate the _CID method for this device */ + + status = acpi_ut_evaluate_object(device_node, METHOD_NAME__CID, + ACPI_BTYPE_INTEGER | ACPI_BTYPE_STRING + | ACPI_BTYPE_PACKAGE, &obj_desc); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + /* + * Get the count and size of the returned _CIDs. _CID can return either + * a Package of Integers/Strings or a single Integer or String. + * Note: This section also validates that all CID elements are of the + * correct type (Integer or String). + */ + if (obj_desc->common.type == ACPI_TYPE_PACKAGE) { + count = obj_desc->package.count; + cid_objects = obj_desc->package.elements; + } else { /* Single Integer or String CID */ + + count = 1; + cid_objects = &obj_desc; + } + + string_area_size = 0; + for (i = 0; i < count; i++) { + + /* String lengths include null terminator */ + + switch (cid_objects[i]->common.type) { + case ACPI_TYPE_INTEGER: + string_area_size += ACPI_EISAID_STRING_SIZE; + break; + + case ACPI_TYPE_STRING: + string_area_size += cid_objects[i]->string.length + 1; + break; + + default: + status = AE_TYPE; + goto cleanup; + } + } + + /* + * Now that we know the length of the CIDs, allocate return buffer: + * 1) Size of the base structure + + * 2) Size of the CID DEVICE_ID array + + * 3) Size of the actual CID strings + */ + cid_list_size = sizeof(struct acpica_device_id_list) + + ((count - 1) * sizeof(struct acpica_device_id)) + string_area_size; + + cid_list = ACPI_ALLOCATE_ZEROED(cid_list_size); + if (!cid_list) { + status = AE_NO_MEMORY; + goto cleanup; + } + + /* Area for CID strings starts after the CID DEVICE_ID array */ + + next_id_string = ACPI_CAST_PTR(char, cid_list->ids) + + ((acpi_size) count * sizeof(struct acpica_device_id)); + + /* Copy/convert the CIDs to the return buffer */ + + for (i = 0; i < count; i++) { + if (cid_objects[i]->common.type == ACPI_TYPE_INTEGER) { + + /* Convert the Integer (EISAID) CID to a string */ + + acpi_ex_eisa_id_to_string(next_id_string, + cid_objects[i]->integer. + value); + length = ACPI_EISAID_STRING_SIZE; + } else { /* ACPI_TYPE_STRING */ + + /* Copy the String CID from the returned object */ + + acpi_ut_copy_id_string(next_id_string, + cid_objects[i]->string.pointer); + length = cid_objects[i]->string.length + 1; + } + + cid_list->ids[i].string = next_id_string; + cid_list->ids[i].length = length; + next_id_string += length; + } + + /* Finish the CID list */ + + cid_list->count = count; + cid_list->list_size = cid_list_size; + *return_cid_list = cid_list; + +cleanup: + + /* On exit, we must delete the _CID return object */ + + acpi_ut_remove_reference(obj_desc); + return_ACPI_STATUS(status); +} diff --git a/drivers/acpi/acpica/utinit.c b/drivers/acpi/acpica/utinit.c index a54ca84eb362..9d0919ebf7b0 100644 --- a/drivers/acpi/acpica/utinit.c +++ b/drivers/acpi/acpica/utinit.c @@ -99,33 +99,19 @@ static void acpi_ut_terminate(void) * * FUNCTION: acpi_ut_subsystem_shutdown * - * PARAMETERS: none + * PARAMETERS: None * - * RETURN: none + * RETURN: None * - * DESCRIPTION: Shutdown the various subsystems. Don't delete the mutex - * objects here -- because the AML debugger may be still running. + * DESCRIPTION: Shutdown the various components. Do not delete the mutex + * objects here, because the AML debugger may be still running. * ******************************************************************************/ void acpi_ut_subsystem_shutdown(void) { - ACPI_FUNCTION_TRACE(ut_subsystem_shutdown); - /* Just exit if subsystem is already shutdown */ - - if (acpi_gbl_shutdown) { - ACPI_ERROR((AE_INFO, "ACPI Subsystem is already terminated")); - return_VOID; - } - - /* Subsystem appears active, go ahead and shut it down */ - - acpi_gbl_shutdown = TRUE; - acpi_gbl_startup_flags = 0; - ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Shutting down ACPI Subsystem\n")); - #ifndef ACPI_ASL_COMPILER /* Close the acpi_event Handling */ diff --git a/drivers/acpi/acpica/utmisc.c b/drivers/acpi/acpica/utmisc.c index fbe782348b0b..61f6315fce9f 100644 --- a/drivers/acpi/acpica/utmisc.c +++ b/drivers/acpi/acpica/utmisc.c @@ -50,6 +50,11 @@ #define _COMPONENT ACPI_UTILITIES ACPI_MODULE_NAME("utmisc") +/* + * Common suffix for messages + */ +#define ACPI_COMMON_MSG_SUFFIX \ + acpi_os_printf(" (%8.8X/%s-%u)\n", ACPI_CA_VERSION, module_name, line_number) /******************************************************************************* * * FUNCTION: acpi_ut_validate_exception @@ -120,6 +125,34 @@ const char *acpi_ut_validate_exception(acpi_status status) /******************************************************************************* * + * FUNCTION: acpi_ut_is_pci_root_bridge + * + * PARAMETERS: Id - The HID/CID in string format + * + * RETURN: TRUE if the Id is a match for a PCI/PCI-Express Root Bridge + * + * DESCRIPTION: Determine if the input ID is a PCI Root Bridge ID. + * + ******************************************************************************/ + +u8 acpi_ut_is_pci_root_bridge(char *id) +{ + + /* + * Check if this is a PCI root bridge. + * ACPI 3.0+: check for a PCI Express root also. + */ + if (!(ACPI_STRCMP(id, + PCI_ROOT_HID_STRING)) || + !(ACPI_STRCMP(id, PCI_EXPRESS_ROOT_HID_STRING))) { + return (TRUE); + } + + return (FALSE); +} + +/******************************************************************************* + * * FUNCTION: acpi_ut_is_aml_table * * PARAMETERS: Table - An ACPI table @@ -1037,8 +1070,7 @@ acpi_error(const char *module_name, u32 line_number, const char *format, ...) va_start(args, format); acpi_os_vprintf(format, args); - acpi_os_printf(" %8.8X %s-%u\n", ACPI_CA_VERSION, module_name, - line_number); + ACPI_COMMON_MSG_SUFFIX; va_end(args); } @@ -1052,8 +1084,7 @@ acpi_exception(const char *module_name, va_start(args, format); acpi_os_vprintf(format, args); - acpi_os_printf(" %8.8X %s-%u\n", ACPI_CA_VERSION, module_name, - line_number); + ACPI_COMMON_MSG_SUFFIX; va_end(args); } @@ -1066,8 +1097,7 @@ acpi_warning(const char *module_name, u32 line_number, const char *format, ...) va_start(args, format); acpi_os_vprintf(format, args); - acpi_os_printf(" %8.8X %s-%u\n", ACPI_CA_VERSION, module_name, - line_number); + ACPI_COMMON_MSG_SUFFIX; va_end(args); } @@ -1088,3 +1118,46 @@ ACPI_EXPORT_SYMBOL(acpi_error) ACPI_EXPORT_SYMBOL(acpi_exception) ACPI_EXPORT_SYMBOL(acpi_warning) ACPI_EXPORT_SYMBOL(acpi_info) + +/******************************************************************************* + * + * FUNCTION: acpi_ut_predefined_warning + * + * PARAMETERS: module_name - Caller's module name (for error output) + * line_number - Caller's line number (for error output) + * Pathname - Full pathname to the node + * node_flags - From Namespace node for the method/object + * Format - Printf format string + additional args + * + * RETURN: None + * + * DESCRIPTION: Warnings for the predefined validation module. Messages are + * only emitted the first time a problem with a particular + * method/object is detected. This prevents a flood of error + * messages for methods that are repeatedly evaluated. + * +******************************************************************************/ + +void ACPI_INTERNAL_VAR_XFACE +acpi_ut_predefined_warning(const char *module_name, + u32 line_number, + char *pathname, + u8 node_flags, const char *format, ...) +{ + va_list args; + + /* + * Warning messages for this method/object will be disabled after the + * first time a validation fails or an object is successfully repaired. + */ + if (node_flags & ANOBJ_EVALUATED) { + return; + } + + acpi_os_printf("ACPI Warning for %s: ", pathname); + + va_start(args, format); + acpi_os_vprintf(format, args); + ACPI_COMMON_MSG_SUFFIX; + va_end(args); +} diff --git a/drivers/acpi/acpica/utxface.c b/drivers/acpi/acpica/utxface.c index 078a22728c6b..b1f5f680bc78 100644 --- a/drivers/acpi/acpica/utxface.c +++ b/drivers/acpi/acpica/utxface.c @@ -251,6 +251,16 @@ acpi_status acpi_initialize_objects(u32 flags) } /* + * Execute any module-level code that was detected during the table load + * phase. Although illegal since ACPI 2.0, there are many machines that + * contain this type of code. Each block of detected executable AML code + * outside of any control method is wrapped with a temporary control + * method object and placed on a global list. The methods on this list + * are executed below. + */ + acpi_ns_exec_module_code_list(); + + /* * Initialize the objects that remain uninitialized. This runs the * executable AML that may be part of the declaration of these objects: * operation_regions, buffer_fields, Buffers, and Packages. @@ -318,7 +328,7 @@ ACPI_EXPORT_SYMBOL(acpi_initialize_objects) * * RETURN: Status * - * DESCRIPTION: Shutdown the ACPI subsystem. Release all resources. + * DESCRIPTION: Shutdown the ACPICA subsystem and release all resources. * ******************************************************************************/ acpi_status acpi_terminate(void) @@ -327,6 +337,19 @@ acpi_status acpi_terminate(void) ACPI_FUNCTION_TRACE(acpi_terminate); + /* Just exit if subsystem is already shutdown */ + + if (acpi_gbl_shutdown) { + ACPI_ERROR((AE_INFO, "ACPI Subsystem is already terminated")); + return_ACPI_STATUS(AE_OK); + } + + /* Subsystem appears active, go ahead and shut it down */ + + acpi_gbl_shutdown = TRUE; + acpi_gbl_startup_flags = 0; + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Shutting down ACPI Subsystem\n")); + /* Terminate the AML Debugger if present */ ACPI_DEBUGGER_EXEC(acpi_gbl_db_terminate_threads = TRUE); @@ -353,6 +376,7 @@ acpi_status acpi_terminate(void) } ACPI_EXPORT_SYMBOL(acpi_terminate) + #ifndef ACPI_ASL_COMPILER #ifdef ACPI_FUTURE_USAGE /******************************************************************************* diff --git a/drivers/acpi/battery.c b/drivers/acpi/battery.c index 58b4517ce712..3f4602b8f287 100644 --- a/drivers/acpi/battery.c +++ b/drivers/acpi/battery.c @@ -31,6 +31,7 @@ #include <linux/types.h> #include <linux/jiffies.h> #include <linux/async.h> +#include <linux/dmi.h> #ifdef CONFIG_ACPI_PROCFS_POWER #include <linux/proc_fs.h> @@ -45,6 +46,8 @@ #include <linux/power_supply.h> #endif +#define PREFIX "ACPI: " + #define ACPI_BATTERY_VALUE_UNKNOWN 0xFFFFFFFF #define ACPI_BATTERY_CLASS "battery" @@ -85,6 +88,10 @@ static const struct acpi_device_id battery_device_ids[] = { MODULE_DEVICE_TABLE(acpi, battery_device_ids); +/* For buggy DSDTs that report negative 16-bit values for either charging + * or discharging current and/or report 0 as 65536 due to bad math. + */ +#define QUIRK_SIGNED16_CURRENT 0x0001 struct acpi_battery { struct mutex lock; @@ -112,6 +119,7 @@ struct acpi_battery { int state; int power_unit; u8 alarm_present; + long quirks; }; #define to_acpi_battery(x) container_of(x, struct acpi_battery, bat); @@ -390,6 +398,11 @@ static int acpi_battery_get_state(struct acpi_battery *battery) state_offsets, ARRAY_SIZE(state_offsets)); battery->update_time = jiffies; kfree(buffer.pointer); + + if ((battery->quirks & QUIRK_SIGNED16_CURRENT) && + battery->rate_now != -1) + battery->rate_now = abs((s16)battery->rate_now); + return result; } @@ -495,6 +508,14 @@ static void sysfs_remove_battery(struct acpi_battery *battery) } #endif +static void acpi_battery_quirks(struct acpi_battery *battery) +{ + battery->quirks = 0; + if (dmi_name_in_vendors("Acer") && battery->power_unit) { + battery->quirks |= QUIRK_SIGNED16_CURRENT; + } +} + static int acpi_battery_update(struct acpi_battery *battery) { int result, old_present = acpi_battery_present(battery); @@ -513,6 +534,7 @@ static int acpi_battery_update(struct acpi_battery *battery) result = acpi_battery_get_info(battery); if (result) return result; + acpi_battery_quirks(battery); acpi_battery_init_alarm(battery); } #ifdef CONFIG_ACPI_SYSFS_POWER diff --git a/drivers/acpi/blacklist.c b/drivers/acpi/blacklist.c index 0c4ca4d318b3..23e5a0519af5 100644 --- a/drivers/acpi/blacklist.c +++ b/drivers/acpi/blacklist.c @@ -34,6 +34,8 @@ #include <acpi/acpi_bus.h> #include <linux/dmi.h> +#include "internal.h" + enum acpi_blacklist_predicates { all_versions, less_than_or_equal, @@ -222,6 +224,7 @@ static struct dmi_system_id acpi_osi_dmi_table[] __initdata = { * _OSI(Linux) helps sound * DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad R61"), * DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad T61"), + * T400, T500 * _OSI(Linux) has Linux specific hooks * DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad X61"), * _OSI(Linux) is a NOP: @@ -252,6 +255,22 @@ static struct dmi_system_id acpi_osi_dmi_table[] __initdata = { DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad X61"), }, }, + { + .callback = dmi_enable_osi_linux, + .ident = "Lenovo ThinkPad T400", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad T400"), + }, + }, + { + .callback = dmi_enable_osi_linux, + .ident = "Lenovo ThinkPad T500", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad T500"), + }, + }, {} }; diff --git a/drivers/acpi/bus.c b/drivers/acpi/bus.c index 2876fc70c3a9..741191524353 100644 --- a/drivers/acpi/bus.c +++ b/drivers/acpi/bus.c @@ -38,6 +38,7 @@ #include <linux/pci.h> #include <acpi/acpi_bus.h> #include <acpi/acpi_drivers.h> +#include <linux/dmi.h> #include "internal.h" @@ -93,36 +94,33 @@ int acpi_bus_get_device(acpi_handle handle, struct acpi_device **device) EXPORT_SYMBOL(acpi_bus_get_device); -int acpi_bus_get_status(struct acpi_device *device) +acpi_status acpi_bus_get_status_handle(acpi_handle handle, + unsigned long long *sta) { - acpi_status status = AE_OK; - unsigned long long sta = 0; - + acpi_status status; - if (!device) - return -EINVAL; + status = acpi_evaluate_integer(handle, "_STA", NULL, sta); + if (ACPI_SUCCESS(status)) + return AE_OK; - /* - * Evaluate _STA if present. - */ - if (device->flags.dynamic_status) { - status = - acpi_evaluate_integer(device->handle, "_STA", NULL, &sta); - if (ACPI_FAILURE(status)) - return -ENODEV; - STRUCT_TO_INT(device->status) = (int)sta; + if (status == AE_NOT_FOUND) { + *sta = ACPI_STA_DEVICE_PRESENT | ACPI_STA_DEVICE_ENABLED | + ACPI_STA_DEVICE_UI | ACPI_STA_DEVICE_FUNCTIONING; + return AE_OK; } + return status; +} - /* - * According to ACPI spec some device can be present and functional - * even if the parent is not present but functional. - * In such conditions the child device should not inherit the status - * from the parent. - */ - else - STRUCT_TO_INT(device->status) = - ACPI_STA_DEVICE_PRESENT | ACPI_STA_DEVICE_ENABLED | - ACPI_STA_DEVICE_UI | ACPI_STA_DEVICE_FUNCTIONING; +int acpi_bus_get_status(struct acpi_device *device) +{ + acpi_status status; + unsigned long long sta; + + status = acpi_bus_get_status_handle(device->handle, &sta); + if (ACPI_FAILURE(status)) + return -ENODEV; + + STRUCT_TO_INT(device->status) = (int) sta; if (device->status.functional && !device->status.present) { ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Device [%s] status [%08x]: " @@ -134,14 +132,12 @@ int acpi_bus_get_status(struct acpi_device *device) ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Device [%s] status [%08x]\n", device->pnp.bus_id, (u32) STRUCT_TO_INT(device->status))); - return 0; } - EXPORT_SYMBOL(acpi_bus_get_status); void acpi_bus_private_data_handler(acpi_handle handle, - u32 function, void *context) + void *context) { return; } diff --git a/drivers/acpi/button.c b/drivers/acpi/button.c index 9195deba9d94..0c9c6a9a002c 100644 --- a/drivers/acpi/button.c +++ b/drivers/acpi/button.c @@ -33,6 +33,8 @@ #include <acpi/acpi_bus.h> #include <acpi/acpi_drivers.h> +#define PREFIX "ACPI: " + #define ACPI_BUTTON_CLASS "button" #define ACPI_BUTTON_FILE_INFO "info" #define ACPI_BUTTON_FILE_STATE "state" @@ -113,6 +115,9 @@ static const struct file_operations acpi_button_state_fops = { .release = single_release, }; +static BLOCKING_NOTIFIER_HEAD(acpi_lid_notifier); +static struct acpi_device *lid_device; + /* -------------------------------------------------------------------------- FS Interface (/proc) -------------------------------------------------------------------------- */ @@ -229,11 +234,41 @@ static int acpi_button_remove_fs(struct acpi_device *device) /* -------------------------------------------------------------------------- Driver Interface -------------------------------------------------------------------------- */ +int acpi_lid_notifier_register(struct notifier_block *nb) +{ + return blocking_notifier_chain_register(&acpi_lid_notifier, nb); +} +EXPORT_SYMBOL(acpi_lid_notifier_register); + +int acpi_lid_notifier_unregister(struct notifier_block *nb) +{ + return blocking_notifier_chain_unregister(&acpi_lid_notifier, nb); +} +EXPORT_SYMBOL(acpi_lid_notifier_unregister); + +int acpi_lid_open(void) +{ + acpi_status status; + unsigned long long state; + + if (!lid_device) + return -ENODEV; + + status = acpi_evaluate_integer(lid_device->handle, "_LID", NULL, + &state); + if (ACPI_FAILURE(status)) + return -ENODEV; + + return !!state; +} +EXPORT_SYMBOL(acpi_lid_open); + static int acpi_lid_send_state(struct acpi_device *device) { struct acpi_button *button = acpi_driver_data(device); unsigned long long state; acpi_status status; + int ret; status = acpi_evaluate_integer(device->handle, "_LID", NULL, &state); if (ACPI_FAILURE(status)) @@ -242,7 +277,12 @@ static int acpi_lid_send_state(struct acpi_device *device) /* input layer checks if event is redundant */ input_report_switch(button->input, SW_LID, !state); input_sync(button->input); - return 0; + + ret = blocking_notifier_call_chain(&acpi_lid_notifier, state, device); + if (ret == NOTIFY_DONE) + ret = blocking_notifier_call_chain(&acpi_lid_notifier, state, + device); + return ret; } static void acpi_button_notify(struct acpi_device *device, u32 event) @@ -364,8 +404,14 @@ static int acpi_button_add(struct acpi_device *device) error = input_register_device(input); if (error) goto err_remove_fs; - if (button->type == ACPI_BUTTON_TYPE_LID) + if (button->type == ACPI_BUTTON_TYPE_LID) { acpi_lid_send_state(device); + /* + * This assumes there's only one lid device, or if there are + * more we only care about the last one... + */ + lid_device = device; + } if (device->wakeup.flags.valid) { /* Button's GPE is run-wake GPE */ diff --git a/drivers/acpi/cm_sbs.c b/drivers/acpi/cm_sbs.c index 332fe4b21708..6c9ee68e46fb 100644 --- a/drivers/acpi/cm_sbs.c +++ b/drivers/acpi/cm_sbs.c @@ -28,6 +28,8 @@ #include <acpi/acpi_bus.h> #include <acpi/acpi_drivers.h> +#define PREFIX "ACPI: " + ACPI_MODULE_NAME("cm_sbs"); #define ACPI_AC_CLASS "ac_adapter" #define ACPI_BATTERY_CLASS "battery" diff --git a/drivers/acpi/container.c b/drivers/acpi/container.c index fe0cdf83641a..642bb305cb65 100644 --- a/drivers/acpi/container.c +++ b/drivers/acpi/container.c @@ -35,6 +35,8 @@ #include <acpi/acpi_drivers.h> #include <acpi/container.h> +#define PREFIX "ACPI: " + #define ACPI_CONTAINER_DEVICE_NAME "ACPI container device" #define ACPI_CONTAINER_CLASS "container" @@ -200,20 +202,17 @@ container_walk_namespace_cb(acpi_handle handle, u32 lvl, void *context, void **rv) { char *hid = NULL; - struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; struct acpi_device_info *info; acpi_status status; int *action = context; - - status = acpi_get_object_info(handle, &buffer); - if (ACPI_FAILURE(status) || !buffer.pointer) { + status = acpi_get_object_info(handle, &info); + if (ACPI_FAILURE(status)) { return AE_OK; } - info = buffer.pointer; if (info->valid & ACPI_VALID_HID) - hid = info->hardware_id.value; + hid = info->hardware_id.string; if (hid == NULL) { goto end; @@ -240,7 +239,7 @@ container_walk_namespace_cb(acpi_handle handle, } end: - kfree(buffer.pointer); + kfree(info); return AE_OK; } diff --git a/drivers/acpi/debug.c b/drivers/acpi/debug.c index a8287be0870e..8a690c3b8e23 100644 --- a/drivers/acpi/debug.c +++ b/drivers/acpi/debug.c @@ -3,6 +3,7 @@ */ #include <linux/proc_fs.h> +#include <linux/seq_file.h> #include <linux/init.h> #include <linux/module.h> #include <linux/kernel.h> @@ -201,72 +202,54 @@ module_param_call(trace_state, param_set_trace_state, param_get_trace_state, #define ACPI_SYSTEM_FILE_DEBUG_LAYER "debug_layer" #define ACPI_SYSTEM_FILE_DEBUG_LEVEL "debug_level" -static int -acpi_system_read_debug(char *page, - char **start, off_t off, int count, int *eof, void *data) +static int acpi_system_debug_proc_show(struct seq_file *m, void *v) { - char *p = page; - int size = 0; unsigned int i; - if (off != 0) - goto end; + seq_printf(m, "%-25s\tHex SET\n", "Description"); - p += sprintf(p, "%-25s\tHex SET\n", "Description"); - - switch ((unsigned long)data) { + switch ((unsigned long)m->private) { case 0: for (i = 0; i < ARRAY_SIZE(acpi_debug_layers); i++) { - p += sprintf(p, "%-25s\t0x%08lX [%c]\n", + seq_printf(m, "%-25s\t0x%08lX [%c]\n", acpi_debug_layers[i].name, acpi_debug_layers[i].value, (acpi_dbg_layer & acpi_debug_layers[i]. value) ? '*' : ' '); } - p += sprintf(p, "%-25s\t0x%08X [%c]\n", "ACPI_ALL_DRIVERS", + seq_printf(m, "%-25s\t0x%08X [%c]\n", "ACPI_ALL_DRIVERS", ACPI_ALL_DRIVERS, (acpi_dbg_layer & ACPI_ALL_DRIVERS) == ACPI_ALL_DRIVERS ? '*' : (acpi_dbg_layer & ACPI_ALL_DRIVERS) == 0 ? ' ' : '-'); - p += sprintf(p, + seq_printf(m, "--\ndebug_layer = 0x%08X (* = enabled, - = partial)\n", acpi_dbg_layer); break; case 1: for (i = 0; i < ARRAY_SIZE(acpi_debug_levels); i++) { - p += sprintf(p, "%-25s\t0x%08lX [%c]\n", + seq_printf(m, "%-25s\t0x%08lX [%c]\n", acpi_debug_levels[i].name, acpi_debug_levels[i].value, (acpi_dbg_level & acpi_debug_levels[i]. value) ? '*' : ' '); } - p += sprintf(p, "--\ndebug_level = 0x%08X (* = enabled)\n", + seq_printf(m, "--\ndebug_level = 0x%08X (* = enabled)\n", acpi_dbg_level); break; - default: - p += sprintf(p, "Invalid debug option\n"); - break; } + return 0; +} - end: - size = (p - page); - if (size <= off + count) - *eof = 1; - *start = page + off; - size -= off; - if (size > count) - size = count; - if (size < 0) - size = 0; - - return size; +static int acpi_system_debug_proc_open(struct inode *inode, struct file *file) +{ + return single_open(file, acpi_system_debug_proc_show, PDE(inode)->data); } -static int -acpi_system_write_debug(struct file *file, +static ssize_t acpi_system_debug_proc_write(struct file *file, const char __user * buffer, - unsigned long count, void *data) + size_t count, loff_t *pos) { char debug_string[12] = { '\0' }; @@ -279,7 +262,7 @@ acpi_system_write_debug(struct file *file, debug_string[count] = '\0'; - switch ((unsigned long)data) { + switch ((unsigned long)PDE(file->f_path.dentry->d_inode)->data) { case 0: acpi_dbg_layer = simple_strtoul(debug_string, NULL, 0); break; @@ -292,6 +275,15 @@ acpi_system_write_debug(struct file *file, return count; } + +static const struct file_operations acpi_system_debug_proc_fops = { + .owner = THIS_MODULE, + .open = acpi_system_debug_proc_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, + .write = acpi_system_debug_proc_write, +}; #endif int __init acpi_debug_init(void) @@ -303,24 +295,18 @@ int __init acpi_debug_init(void) /* 'debug_layer' [R/W] */ name = ACPI_SYSTEM_FILE_DEBUG_LAYER; - entry = - create_proc_read_entry(name, S_IFREG | S_IRUGO | S_IWUSR, - acpi_root_dir, acpi_system_read_debug, - (void *)0); - if (entry) - entry->write_proc = acpi_system_write_debug; - else + entry = proc_create_data(name, S_IFREG | S_IRUGO | S_IWUSR, + acpi_root_dir, &acpi_system_debug_proc_fops, + (void *)0); + if (!entry) goto Error; /* 'debug_level' [R/W] */ name = ACPI_SYSTEM_FILE_DEBUG_LEVEL; - entry = - create_proc_read_entry(name, S_IFREG | S_IRUGO | S_IWUSR, - acpi_root_dir, acpi_system_read_debug, - (void *)1); - if (entry) - entry->write_proc = acpi_system_write_debug; - else + entry = proc_create_data(name, S_IFREG | S_IRUGO | S_IWUSR, + acpi_root_dir, &acpi_system_debug_proc_fops, + (void *)1); + if (!entry) goto Error; Done: diff --git a/drivers/acpi/dock.c b/drivers/acpi/dock.c index efb959d6c8a9..7338b6a3e049 100644 --- a/drivers/acpi/dock.c +++ b/drivers/acpi/dock.c @@ -33,6 +33,8 @@ #include <acpi/acpi_bus.h> #include <acpi/acpi_drivers.h> +#define PREFIX "ACPI: " + #define ACPI_DOCK_DRIVER_DESCRIPTION "ACPI Dock Station Driver" ACPI_MODULE_NAME("dock"); @@ -65,7 +67,7 @@ struct dock_station { struct list_head dependent_devices; struct list_head hotplug_devices; - struct list_head sibiling; + struct list_head sibling; struct platform_device *dock_device; }; static LIST_HEAD(dock_stations); @@ -231,18 +233,16 @@ static int is_ata(acpi_handle handle) static int is_battery(acpi_handle handle) { struct acpi_device_info *info; - struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL}; int ret = 1; - if (!ACPI_SUCCESS(acpi_get_object_info(handle, &buffer))) + if (!ACPI_SUCCESS(acpi_get_object_info(handle, &info))) return 0; - info = buffer.pointer; if (!(info->valid & ACPI_VALID_HID)) ret = 0; else - ret = !strcmp("PNP0C0A", info->hardware_id.value); + ret = !strcmp("PNP0C0A", info->hardware_id.string); - kfree(buffer.pointer); + kfree(info); return ret; } @@ -275,7 +275,7 @@ int is_dock_device(acpi_handle handle) if (is_dock(handle)) return 1; - list_for_each_entry(dock_station, &dock_stations, sibiling) { + list_for_each_entry(dock_station, &dock_stations, sibling) { if (find_dock_dependent_device(dock_station, handle)) return 1; } @@ -619,7 +619,7 @@ register_hotplug_dock_device(acpi_handle handle, struct acpi_dock_ops *ops, * make sure this handle is for a device dependent on the dock, * this would include the dock station itself */ - list_for_each_entry(dock_station, &dock_stations, sibiling) { + list_for_each_entry(dock_station, &dock_stations, sibling) { /* * An ATA bay can be in a dock and itself can be ejected * seperately, so there are two 'dock stations' which need the @@ -651,7 +651,7 @@ void unregister_hotplug_dock_device(acpi_handle handle) if (!dock_station_count) return; - list_for_each_entry(dock_station, &dock_stations, sibiling) { + list_for_each_entry(dock_station, &dock_stations, sibling) { dd = find_dock_dependent_device(dock_station, handle); if (dd) dock_del_hotplug_device(dock_station, dd); @@ -787,7 +787,7 @@ static int acpi_dock_notifier_call(struct notifier_block *this, if (event != ACPI_NOTIFY_BUS_CHECK && event != ACPI_NOTIFY_DEVICE_CHECK && event != ACPI_NOTIFY_EJECT_REQUEST) return 0; - list_for_each_entry(dock_station, &dock_stations, sibiling) { + list_for_each_entry(dock_station, &dock_stations, sibling) { if (dock_station->handle == handle) { struct dock_data *dock_data; @@ -958,7 +958,7 @@ static int dock_add(acpi_handle handle) dock_station->last_dock_time = jiffies - HZ; INIT_LIST_HEAD(&dock_station->dependent_devices); INIT_LIST_HEAD(&dock_station->hotplug_devices); - INIT_LIST_HEAD(&dock_station->sibiling); + INIT_LIST_HEAD(&dock_station->sibling); spin_lock_init(&dock_station->dd_lock); mutex_init(&dock_station->hp_lock); ATOMIC_INIT_NOTIFIER_HEAD(&dock_notifier_list); @@ -1044,7 +1044,7 @@ static int dock_add(acpi_handle handle) add_dock_dependent_device(dock_station, dd); dock_station_count++; - list_add(&dock_station->sibiling, &dock_stations); + list_add(&dock_station->sibling, &dock_stations); return 0; dock_add_err_unregister: @@ -1149,7 +1149,7 @@ static void __exit dock_exit(void) struct dock_station *tmp; unregister_acpi_bus_notifier(&dock_acpi_notifier); - list_for_each_entry_safe(dock_station, tmp, &dock_stations, sibiling) + list_for_each_entry_safe(dock_station, tmp, &dock_stations, sibling) dock_remove(dock_station); } diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c index 391f331674c7..baef28c1e630 100644 --- a/drivers/acpi/ec.c +++ b/drivers/acpi/ec.c @@ -42,12 +42,12 @@ #include <asm/io.h> #include <acpi/acpi_bus.h> #include <acpi/acpi_drivers.h> +#include <linux/dmi.h> #define ACPI_EC_CLASS "embedded_controller" #define ACPI_EC_DEVICE_NAME "Embedded Controller" #define ACPI_EC_FILE_INFO "info" -#undef PREFIX #define PREFIX "ACPI: EC: " /* EC status register */ @@ -68,15 +68,13 @@ enum ec_command { #define ACPI_EC_DELAY 500 /* Wait 500ms max. during EC ops */ #define ACPI_EC_UDELAY_GLK 1000 /* Wait 1ms max. to get global lock */ #define ACPI_EC_CDELAY 10 /* Wait 10us before polling EC */ +#define ACPI_EC_MSI_UDELAY 550 /* Wait 550us for MSI EC */ #define ACPI_EC_STORM_THRESHOLD 8 /* number of false interrupts per one transaction */ enum { EC_FLAGS_QUERY_PENDING, /* Query is pending */ - EC_FLAGS_GPE_MODE, /* Expect GPE to be sent - * for status change */ - EC_FLAGS_NO_GPE, /* Don't use GPE mode */ EC_FLAGS_GPE_STORM, /* GPE storm detected */ EC_FLAGS_HANDLERS_INSTALLED /* Handlers for GPE and * OpReg are installed */ @@ -121,6 +119,8 @@ static struct acpi_ec { } *boot_ec, *first_ec; static int EC_FLAGS_MSI; /* Out-of-spec MSI controller */ +static int EC_FLAGS_VALIDATE_ECDT; /* ASUStec ECDTs need to be validated */ +static int EC_FLAGS_SKIP_DSDT_SCAN; /* Not all BIOS survive early DSDT scan */ /* -------------------------------------------------------------------------- Transaction Management @@ -170,7 +170,7 @@ static void start_transaction(struct acpi_ec *ec) acpi_ec_write_cmd(ec, ec->curr->command); } -static void gpe_transaction(struct acpi_ec *ec, u8 status) +static void advance_transaction(struct acpi_ec *ec, u8 status) { unsigned long flags; spin_lock_irqsave(&ec->curr_lock, flags); @@ -201,29 +201,6 @@ unlock: spin_unlock_irqrestore(&ec->curr_lock, flags); } -static int acpi_ec_wait(struct acpi_ec *ec) -{ - if (wait_event_timeout(ec->wait, ec_transaction_done(ec), - msecs_to_jiffies(ACPI_EC_DELAY))) - return 0; - /* try restart command if we get any false interrupts */ - if (ec->curr->irq_count && - (acpi_ec_read_status(ec) & ACPI_EC_FLAG_IBF) == 0) { - pr_debug(PREFIX "controller reset, restart transaction\n"); - start_transaction(ec); - if (wait_event_timeout(ec->wait, ec_transaction_done(ec), - msecs_to_jiffies(ACPI_EC_DELAY))) - return 0; - } - /* missing GPEs, switch back to poll mode */ - if (printk_ratelimit()) - pr_info(PREFIX "missing confirmations, " - "switch off interrupt mode.\n"); - set_bit(EC_FLAGS_NO_GPE, &ec->flags); - clear_bit(EC_FLAGS_GPE_MODE, &ec->flags); - return 1; -} - static void acpi_ec_gpe_query(void *ec_cxt); static int ec_check_sci(struct acpi_ec *ec, u8 state) @@ -236,43 +213,49 @@ static int ec_check_sci(struct acpi_ec *ec, u8 state) return 0; } -static void ec_delay(void) -{ - /* EC in MSI notebooks don't tolerate delays other than 550 usec */ - if (EC_FLAGS_MSI) - udelay(ACPI_EC_DELAY); - else - /* Use shortest sleep available */ - msleep(1); -} - static int ec_poll(struct acpi_ec *ec) { - unsigned long delay = jiffies + msecs_to_jiffies(ACPI_EC_DELAY); - udelay(ACPI_EC_CDELAY); - while (time_before(jiffies, delay)) { - gpe_transaction(ec, acpi_ec_read_status(ec)); - ec_delay(); - if (ec_transaction_done(ec)) - return 0; + unsigned long flags; + int repeat = 2; /* number of command restarts */ + while (repeat--) { + unsigned long delay = jiffies + + msecs_to_jiffies(ACPI_EC_DELAY); + do { + /* don't sleep with disabled interrupts */ + if (EC_FLAGS_MSI || irqs_disabled()) { + udelay(ACPI_EC_MSI_UDELAY); + if (ec_transaction_done(ec)) + return 0; + } else { + if (wait_event_timeout(ec->wait, + ec_transaction_done(ec), + msecs_to_jiffies(1))) + return 0; + } + advance_transaction(ec, acpi_ec_read_status(ec)); + } while (time_before(jiffies, delay)); + if (acpi_ec_read_status(ec) & ACPI_EC_FLAG_IBF) + break; + pr_debug(PREFIX "controller reset, restart transaction\n"); + spin_lock_irqsave(&ec->curr_lock, flags); + start_transaction(ec); + spin_unlock_irqrestore(&ec->curr_lock, flags); } return -ETIME; } static int acpi_ec_transaction_unlocked(struct acpi_ec *ec, - struct transaction *t, - int force_poll) + struct transaction *t) { unsigned long tmp; int ret = 0; pr_debug(PREFIX "transaction start\n"); /* disable GPE during transaction if storm is detected */ if (test_bit(EC_FLAGS_GPE_STORM, &ec->flags)) { - clear_bit(EC_FLAGS_GPE_MODE, &ec->flags); acpi_disable_gpe(NULL, ec->gpe); } if (EC_FLAGS_MSI) - udelay(ACPI_EC_DELAY); + udelay(ACPI_EC_MSI_UDELAY); /* start transaction */ spin_lock_irqsave(&ec->curr_lock, tmp); /* following two actions should be kept atomic */ @@ -281,11 +264,7 @@ static int acpi_ec_transaction_unlocked(struct acpi_ec *ec, if (ec->curr->command == ACPI_EC_COMMAND_QUERY) clear_bit(EC_FLAGS_QUERY_PENDING, &ec->flags); spin_unlock_irqrestore(&ec->curr_lock, tmp); - /* if we selected poll mode or failed in GPE-mode do a poll loop */ - if (force_poll || - !test_bit(EC_FLAGS_GPE_MODE, &ec->flags) || - acpi_ec_wait(ec)) - ret = ec_poll(ec); + ret = ec_poll(ec); pr_debug(PREFIX "transaction end\n"); spin_lock_irqsave(&ec->curr_lock, tmp); ec->curr = NULL; @@ -295,8 +274,7 @@ static int acpi_ec_transaction_unlocked(struct acpi_ec *ec, ec_check_sci(ec, acpi_ec_read_status(ec)); /* it is safe to enable GPE outside of transaction */ acpi_enable_gpe(NULL, ec->gpe); - } else if (test_bit(EC_FLAGS_GPE_MODE, &ec->flags) && - t->irq_count > ACPI_EC_STORM_THRESHOLD) { + } else if (t->irq_count > ACPI_EC_STORM_THRESHOLD) { pr_info(PREFIX "GPE storm detected, " "transactions will use polling mode\n"); set_bit(EC_FLAGS_GPE_STORM, &ec->flags); @@ -314,16 +292,14 @@ static int ec_wait_ibf0(struct acpi_ec *ec) { unsigned long delay = jiffies + msecs_to_jiffies(ACPI_EC_DELAY); /* interrupt wait manually if GPE mode is not active */ - unsigned long timeout = test_bit(EC_FLAGS_GPE_MODE, &ec->flags) ? - msecs_to_jiffies(ACPI_EC_DELAY) : msecs_to_jiffies(1); while (time_before(jiffies, delay)) - if (wait_event_timeout(ec->wait, ec_check_ibf0(ec), timeout)) + if (wait_event_timeout(ec->wait, ec_check_ibf0(ec), + msecs_to_jiffies(1))) return 0; return -ETIME; } -static int acpi_ec_transaction(struct acpi_ec *ec, struct transaction *t, - int force_poll) +static int acpi_ec_transaction(struct acpi_ec *ec, struct transaction *t) { int status; u32 glk; @@ -345,7 +321,7 @@ static int acpi_ec_transaction(struct acpi_ec *ec, struct transaction *t, status = -ETIME; goto end; } - status = acpi_ec_transaction_unlocked(ec, t, force_poll); + status = acpi_ec_transaction_unlocked(ec, t); end: if (ec->global_lock) acpi_release_global_lock(glk); @@ -354,10 +330,6 @@ unlock: return status; } -/* - * Note: samsung nv5000 doesn't work with ec burst mode. - * http://bugzilla.kernel.org/show_bug.cgi?id=4980 - */ static int acpi_ec_burst_enable(struct acpi_ec *ec) { u8 d; @@ -365,7 +337,7 @@ static int acpi_ec_burst_enable(struct acpi_ec *ec) .wdata = NULL, .rdata = &d, .wlen = 0, .rlen = 1}; - return acpi_ec_transaction(ec, &t, 0); + return acpi_ec_transaction(ec, &t); } static int acpi_ec_burst_disable(struct acpi_ec *ec) @@ -375,7 +347,7 @@ static int acpi_ec_burst_disable(struct acpi_ec *ec) .wlen = 0, .rlen = 0}; return (acpi_ec_read_status(ec) & ACPI_EC_FLAG_BURST) ? - acpi_ec_transaction(ec, &t, 0) : 0; + acpi_ec_transaction(ec, &t) : 0; } static int acpi_ec_read(struct acpi_ec *ec, u8 address, u8 * data) @@ -386,7 +358,7 @@ static int acpi_ec_read(struct acpi_ec *ec, u8 address, u8 * data) .wdata = &address, .rdata = &d, .wlen = 1, .rlen = 1}; - result = acpi_ec_transaction(ec, &t, 0); + result = acpi_ec_transaction(ec, &t); *data = d; return result; } @@ -398,7 +370,7 @@ static int acpi_ec_write(struct acpi_ec *ec, u8 address, u8 data) .wdata = wdata, .rdata = NULL, .wlen = 2, .rlen = 0}; - return acpi_ec_transaction(ec, &t, 0); + return acpi_ec_transaction(ec, &t); } /* @@ -466,7 +438,7 @@ int ec_transaction(u8 command, if (!first_ec) return -ENODEV; - return acpi_ec_transaction(first_ec, &t, force_poll); + return acpi_ec_transaction(first_ec, &t); } EXPORT_SYMBOL(ec_transaction); @@ -487,7 +459,7 @@ static int acpi_ec_query(struct acpi_ec *ec, u8 * data) * bit to be cleared (and thus clearing the interrupt source). */ - result = acpi_ec_transaction(ec, &t, 0); + result = acpi_ec_transaction(ec, &t); if (result) return result; @@ -570,28 +542,10 @@ static u32 acpi_ec_gpe_handler(void *data) pr_debug(PREFIX "~~~> interrupt\n"); status = acpi_ec_read_status(ec); - if (test_bit(EC_FLAGS_GPE_MODE, &ec->flags)) { - gpe_transaction(ec, status); - if (ec_transaction_done(ec) && - (status & ACPI_EC_FLAG_IBF) == 0) - wake_up(&ec->wait); - } - + advance_transaction(ec, status); + if (ec_transaction_done(ec) && (status & ACPI_EC_FLAG_IBF) == 0) + wake_up(&ec->wait); ec_check_sci(ec, status); - if (!test_bit(EC_FLAGS_GPE_MODE, &ec->flags) && - !test_bit(EC_FLAGS_NO_GPE, &ec->flags)) { - /* this is non-query, must be confirmation */ - if (!test_bit(EC_FLAGS_GPE_STORM, &ec->flags)) { - if (printk_ratelimit()) - pr_info(PREFIX "non-query interrupt received," - " switching to interrupt mode\n"); - } else { - /* hush, STORM switches the mode every transaction */ - pr_debug(PREFIX "non-query interrupt received," - " switching to interrupt mode\n"); - } - set_bit(EC_FLAGS_GPE_MODE, &ec->flags); - } return ACPI_INTERRUPT_HANDLED; } @@ -617,7 +571,8 @@ acpi_ec_space_handler(u32 function, acpi_physical_address address, if (bits != 8 && acpi_strict) return AE_BAD_PARAMETER; - acpi_ec_burst_enable(ec); + if (EC_FLAGS_MSI) + acpi_ec_burst_enable(ec); if (function == ACPI_READ) { result = acpi_ec_read(ec, address, &temp); @@ -638,7 +593,8 @@ acpi_ec_space_handler(u32 function, acpi_physical_address address, } } - acpi_ec_burst_disable(ec); + if (EC_FLAGS_MSI) + acpi_ec_burst_disable(ec); switch (result) { case -EINVAL: @@ -788,6 +744,42 @@ ec_parse_device(acpi_handle handle, u32 Level, void *context, void **retval) return AE_CTRL_TERMINATE; } +static int ec_install_handlers(struct acpi_ec *ec) +{ + acpi_status status; + if (test_bit(EC_FLAGS_HANDLERS_INSTALLED, &ec->flags)) + return 0; + status = acpi_install_gpe_handler(NULL, ec->gpe, + ACPI_GPE_EDGE_TRIGGERED, + &acpi_ec_gpe_handler, ec); + if (ACPI_FAILURE(status)) + return -ENODEV; + acpi_set_gpe_type(NULL, ec->gpe, ACPI_GPE_TYPE_RUNTIME); + acpi_enable_gpe(NULL, ec->gpe); + status = acpi_install_address_space_handler(ec->handle, + ACPI_ADR_SPACE_EC, + &acpi_ec_space_handler, + NULL, ec); + if (ACPI_FAILURE(status)) { + if (status == AE_NOT_FOUND) { + /* + * Maybe OS fails in evaluating the _REG object. + * The AE_NOT_FOUND error will be ignored and OS + * continue to initialize EC. + */ + printk(KERN_ERR "Fail in evaluating the _REG object" + " of EC device. Broken bios is suspected.\n"); + } else { + acpi_remove_gpe_handler(NULL, ec->gpe, + &acpi_ec_gpe_handler); + return -ENODEV; + } + } + + set_bit(EC_FLAGS_HANDLERS_INSTALLED, &ec->flags); + return 0; +} + static void ec_remove_handlers(struct acpi_ec *ec) { if (ACPI_FAILURE(acpi_remove_address_space_handler(ec->handle, @@ -802,9 +794,8 @@ static void ec_remove_handlers(struct acpi_ec *ec) static int acpi_ec_add(struct acpi_device *device) { struct acpi_ec *ec = NULL; + int ret; - if (!device) - return -EINVAL; strcpy(acpi_device_name(device), ACPI_EC_DEVICE_NAME); strcpy(acpi_device_class(device), ACPI_EC_CLASS); @@ -837,9 +828,12 @@ static int acpi_ec_add(struct acpi_device *device) acpi_ec_add_fs(device); pr_info(PREFIX "GPE = 0x%lx, I/O: command/status = 0x%lx, data = 0x%lx\n", ec->gpe, ec->command_addr, ec->data_addr); - pr_info(PREFIX "driver started in %s mode\n", - (test_bit(EC_FLAGS_GPE_MODE, &ec->flags))?"interrupt":"poll"); - return 0; + + ret = ec_install_handlers(ec); + + /* EC is fully operational, allow queries */ + clear_bit(EC_FLAGS_QUERY_PENDING, &ec->flags); + return ret; } static int acpi_ec_remove(struct acpi_device *device, int type) @@ -851,6 +845,7 @@ static int acpi_ec_remove(struct acpi_device *device, int type) return -EINVAL; ec = acpi_driver_data(device); + ec_remove_handlers(ec); mutex_lock(&ec->lock); list_for_each_entry_safe(handler, tmp, &ec->list, node) { list_del(&handler->node); @@ -888,91 +883,60 @@ ec_parse_io_ports(struct acpi_resource *resource, void *context) return AE_OK; } -static int ec_install_handlers(struct acpi_ec *ec) +int __init acpi_boot_ec_enable(void) { - acpi_status status; - if (test_bit(EC_FLAGS_HANDLERS_INSTALLED, &ec->flags)) + if (!boot_ec || test_bit(EC_FLAGS_HANDLERS_INSTALLED, &boot_ec->flags)) + return 0; + if (!ec_install_handlers(boot_ec)) { + first_ec = boot_ec; return 0; - status = acpi_install_gpe_handler(NULL, ec->gpe, - ACPI_GPE_EDGE_TRIGGERED, - &acpi_ec_gpe_handler, ec); - if (ACPI_FAILURE(status)) - return -ENODEV; - acpi_set_gpe_type(NULL, ec->gpe, ACPI_GPE_TYPE_RUNTIME); - acpi_enable_gpe(NULL, ec->gpe); - status = acpi_install_address_space_handler(ec->handle, - ACPI_ADR_SPACE_EC, - &acpi_ec_space_handler, - NULL, ec); - if (ACPI_FAILURE(status)) { - if (status == AE_NOT_FOUND) { - /* - * Maybe OS fails in evaluating the _REG object. - * The AE_NOT_FOUND error will be ignored and OS - * continue to initialize EC. - */ - printk(KERN_ERR "Fail in evaluating the _REG object" - " of EC device. Broken bios is suspected.\n"); - } else { - acpi_remove_gpe_handler(NULL, ec->gpe, - &acpi_ec_gpe_handler); - return -ENODEV; - } } - - set_bit(EC_FLAGS_HANDLERS_INSTALLED, &ec->flags); - return 0; + return -EFAULT; } -static int acpi_ec_start(struct acpi_device *device) -{ - struct acpi_ec *ec; - int ret = 0; - - if (!device) - return -EINVAL; - - ec = acpi_driver_data(device); - - if (!ec) - return -EINVAL; - - ret = ec_install_handlers(ec); +static const struct acpi_device_id ec_device_ids[] = { + {"PNP0C09", 0}, + {"", 0}, +}; - /* EC is fully operational, allow queries */ - clear_bit(EC_FLAGS_QUERY_PENDING, &ec->flags); - return ret; +/* Some BIOS do not survive early DSDT scan, skip it */ +static int ec_skip_dsdt_scan(const struct dmi_system_id *id) +{ + EC_FLAGS_SKIP_DSDT_SCAN = 1; + return 0; } -static int acpi_ec_stop(struct acpi_device *device, int type) +/* ASUStek often supplies us with broken ECDT, validate it */ +static int ec_validate_ecdt(const struct dmi_system_id *id) { - struct acpi_ec *ec; - if (!device) - return -EINVAL; - ec = acpi_driver_data(device); - if (!ec) - return -EINVAL; - ec_remove_handlers(ec); - + EC_FLAGS_VALIDATE_ECDT = 1; return 0; } -int __init acpi_boot_ec_enable(void) +/* MSI EC needs special treatment, enable it */ +static int ec_flag_msi(const struct dmi_system_id *id) { - if (!boot_ec || test_bit(EC_FLAGS_HANDLERS_INSTALLED, &boot_ec->flags)) - return 0; - if (!ec_install_handlers(boot_ec)) { - first_ec = boot_ec; - return 0; - } - return -EFAULT; + EC_FLAGS_MSI = 1; + EC_FLAGS_VALIDATE_ECDT = 1; + return 0; } -static const struct acpi_device_id ec_device_ids[] = { - {"PNP0C09", 0}, - {"", 0}, +static struct dmi_system_id __initdata ec_dmi_table[] = { + { + ec_skip_dsdt_scan, "Compal JFL92", { + DMI_MATCH(DMI_BIOS_VENDOR, "COMPAL"), + DMI_MATCH(DMI_BOARD_NAME, "JFL92") }, NULL}, + { + ec_flag_msi, "MSI hardware", { + DMI_MATCH(DMI_BIOS_VENDOR, "Micro-Star"), + DMI_MATCH(DMI_CHASSIS_VENDOR, "MICRO-Star") }, NULL}, + { + ec_validate_ecdt, "ASUS hardware", { + DMI_MATCH(DMI_BIOS_VENDOR, "ASUS") }, NULL}, + {}, }; + int __init acpi_ec_ecdt_probe(void) { acpi_status status; @@ -985,11 +949,7 @@ int __init acpi_ec_ecdt_probe(void) /* * Generate a boot ec context */ - if (dmi_name_in_vendors("Micro-Star") || - dmi_name_in_vendors("Notebook")) { - pr_info(PREFIX "Enabling special treatment for EC from MSI.\n"); - EC_FLAGS_MSI = 1; - } + dmi_check_system(ec_dmi_table); status = acpi_get_table(ACPI_SIG_ECDT, 1, (struct acpi_table_header **)&ecdt_ptr); if (ACPI_SUCCESS(status)) { @@ -1000,7 +960,7 @@ int __init acpi_ec_ecdt_probe(void) boot_ec->handle = ACPI_ROOT_OBJECT; acpi_get_handle(ACPI_ROOT_OBJECT, ecdt_ptr->id, &boot_ec->handle); /* Don't trust ECDT, which comes from ASUSTek */ - if (!dmi_name_in_vendors("ASUS") && EC_FLAGS_MSI == 0) + if (!EC_FLAGS_VALIDATE_ECDT) goto install; saved_ec = kmalloc(sizeof(struct acpi_ec), GFP_KERNEL); if (!saved_ec) @@ -1008,6 +968,10 @@ int __init acpi_ec_ecdt_probe(void) memcpy(saved_ec, boot_ec, sizeof(struct acpi_ec)); /* fall through */ } + + if (EC_FLAGS_SKIP_DSDT_SCAN) + return -ENODEV; + /* This workaround is needed only on some broken machines, * which require early EC, but fail to provide ECDT */ printk(KERN_DEBUG PREFIX "Look up EC in DSDT\n"); @@ -1054,8 +1018,6 @@ static int acpi_ec_suspend(struct acpi_device *device, pm_message_t state) { struct acpi_ec *ec = acpi_driver_data(device); /* Stop using GPE */ - set_bit(EC_FLAGS_NO_GPE, &ec->flags); - clear_bit(EC_FLAGS_GPE_MODE, &ec->flags); acpi_disable_gpe(NULL, ec->gpe); return 0; } @@ -1064,8 +1026,6 @@ static int acpi_ec_resume(struct acpi_device *device) { struct acpi_ec *ec = acpi_driver_data(device); /* Enable use of GPE back */ - clear_bit(EC_FLAGS_NO_GPE, &ec->flags); - set_bit(EC_FLAGS_GPE_MODE, &ec->flags); acpi_enable_gpe(NULL, ec->gpe); return 0; } @@ -1077,8 +1037,6 @@ static struct acpi_driver acpi_ec_driver = { .ops = { .add = acpi_ec_add, .remove = acpi_ec_remove, - .start = acpi_ec_start, - .stop = acpi_ec_stop, .suspend = acpi_ec_suspend, .resume = acpi_ec_resume, }, diff --git a/drivers/acpi/event.c b/drivers/acpi/event.c index aeb7e5fb4a04..c511071bfd79 100644 --- a/drivers/acpi/event.c +++ b/drivers/acpi/event.c @@ -14,6 +14,8 @@ #include <net/netlink.h> #include <net/genetlink.h> +#include "internal.h" + #define _COMPONENT ACPI_SYSTEM_COMPONENT ACPI_MODULE_NAME("event"); diff --git a/drivers/acpi/fan.c b/drivers/acpi/fan.c index 53698ea08371..f419849a0d3f 100644 --- a/drivers/acpi/fan.c +++ b/drivers/acpi/fan.c @@ -34,6 +34,8 @@ #include <acpi/acpi_bus.h> #include <acpi/acpi_drivers.h> +#define PREFIX "ACPI: " + #define ACPI_FAN_CLASS "fan" #define ACPI_FAN_FILE_STATE "state" diff --git a/drivers/acpi/glue.c b/drivers/acpi/glue.c index a8a5c29958c8..c6645f26224b 100644 --- a/drivers/acpi/glue.c +++ b/drivers/acpi/glue.c @@ -12,6 +12,8 @@ #include <linux/rwsem.h> #include <linux/acpi.h> +#include "internal.h" + #define ACPI_GLUE_DEBUG 0 #if ACPI_GLUE_DEBUG #define DBG(x...) printk(PREFIX x) @@ -93,15 +95,13 @@ do_acpi_find_child(acpi_handle handle, u32 lvl, void *context, void **rv) { acpi_status status; struct acpi_device_info *info; - struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; struct acpi_find_child *find = context; - status = acpi_get_object_info(handle, &buffer); + status = acpi_get_object_info(handle, &info); if (ACPI_SUCCESS(status)) { - info = buffer.pointer; if (info->address == find->address) find->handle = handle; - kfree(buffer.pointer); + kfree(info); } return AE_OK; } @@ -121,7 +121,7 @@ EXPORT_SYMBOL(acpi_get_child); /* Link ACPI devices with physical devices */ static void acpi_glue_data_handler(acpi_handle handle, - u32 function, void *context) + void *context) { /* we provide an empty handler */ } diff --git a/drivers/acpi/internal.h b/drivers/acpi/internal.h index 11a69b53004e..074cf8682d52 100644 --- a/drivers/acpi/internal.h +++ b/drivers/acpi/internal.h @@ -1,4 +1,24 @@ -/* For use by Linux/ACPI infrastructure, not drivers */ +/* + * acpi/internal.h + * For use by Linux/ACPI infrastructure, not drivers + * + * Copyright (c) 2009, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#define PREFIX "ACPI: " int init_acpi_device_notify(void); int acpi_scan_init(void); diff --git a/drivers/acpi/numa.c b/drivers/acpi/numa.c index d440ccd27d91..202dd0c976a3 100644 --- a/drivers/acpi/numa.c +++ b/drivers/acpi/numa.c @@ -30,6 +30,8 @@ #include <linux/acpi.h> #include <acpi/acpi_bus.h> +#define PREFIX "ACPI: " + #define ACPI_NUMA 0x80000000 #define _COMPONENT ACPI_NUMA ACPI_MODULE_NAME("numa"); diff --git a/drivers/acpi/osl.c b/drivers/acpi/osl.c index 5691f165a952..7c1c59ea9ec6 100644 --- a/drivers/acpi/osl.c +++ b/drivers/acpi/osl.c @@ -58,6 +58,7 @@ struct acpi_os_dpc { acpi_osd_exec_callback function; void *context; struct work_struct work; + int wait; }; #ifdef CONFIG_ACPI_CUSTOM_DSDT @@ -88,6 +89,7 @@ struct acpi_res_list { char name[5]; /* only can have a length of 4 chars, make use of this one instead of res->name, no need to kalloc then */ struct list_head resource_list; + int count; }; static LIST_HEAD(resource_list_head); @@ -191,7 +193,7 @@ acpi_status __init acpi_os_initialize(void) static void bind_to_cpu0(struct work_struct *work) { - set_cpus_allowed(current, cpumask_of_cpu(0)); + set_cpus_allowed_ptr(current, cpumask_of(0)); kfree(work); } @@ -697,31 +699,12 @@ void acpi_os_derive_pci_id(acpi_handle rhandle, /* upper bound */ static void acpi_os_execute_deferred(struct work_struct *work) { struct acpi_os_dpc *dpc = container_of(work, struct acpi_os_dpc, work); - if (!dpc) { - printk(KERN_ERR PREFIX "Invalid (NULL) context\n"); - return; - } - - dpc->function(dpc->context); - kfree(dpc); - - return; -} - -static void acpi_os_execute_hp_deferred(struct work_struct *work) -{ - struct acpi_os_dpc *dpc = container_of(work, struct acpi_os_dpc, work); - if (!dpc) { - printk(KERN_ERR PREFIX "Invalid (NULL) context\n"); - return; - } - acpi_os_wait_events_complete(NULL); + if (dpc->wait) + acpi_os_wait_events_complete(NULL); dpc->function(dpc->context); kfree(dpc); - - return; } /******************************************************************************* @@ -745,15 +728,11 @@ static acpi_status __acpi_os_execute(acpi_execute_type type, acpi_status status = AE_OK; struct acpi_os_dpc *dpc; struct workqueue_struct *queue; - work_func_t func; int ret; ACPI_DEBUG_PRINT((ACPI_DB_EXEC, "Scheduling function [%p(%p)] for deferred execution.\n", function, context)); - if (!function) - return AE_BAD_PARAMETER; - /* * Allocate/initialize DPC structure. Note that this memory will be * freed by the callee. The kernel handles the work_struct list in a @@ -778,8 +757,8 @@ static acpi_status __acpi_os_execute(acpi_execute_type type, */ queue = hp ? kacpi_hotplug_wq : (type == OSL_NOTIFY_HANDLER ? kacpi_notify_wq : kacpid_wq); - func = hp ? acpi_os_execute_hp_deferred : acpi_os_execute_deferred; - INIT_WORK(&dpc->work, func); + dpc->wait = hp ? 1 : 0; + INIT_WORK(&dpc->work, acpi_os_execute_deferred); ret = queue_work(queue, &dpc->work); if (!ret) { @@ -1182,7 +1161,13 @@ int acpi_check_resource_conflict(struct resource *res) res_list_elem->name, (long long) res_list_elem->start, (long long) res_list_elem->end); - printk(KERN_INFO "ACPI: Device needs an ACPI driver\n"); + if (acpi_enforce_resources == ENFORCE_RESOURCES_LAX) + printk(KERN_NOTICE "ACPI: This conflict may" + " cause random problems and system" + " instability\n"); + printk(KERN_INFO "ACPI: If an ACPI driver is available" + " for this device, you should use it instead of" + " the native driver\n"); } if (acpi_enforce_resources == ENFORCE_RESOURCES_STRICT) return -EBUSY; @@ -1358,6 +1343,89 @@ acpi_os_validate_interface (char *interface) return AE_SUPPORT; } +static inline int acpi_res_list_add(struct acpi_res_list *res) +{ + struct acpi_res_list *res_list_elem; + + list_for_each_entry(res_list_elem, &resource_list_head, + resource_list) { + + if (res->resource_type == res_list_elem->resource_type && + res->start == res_list_elem->start && + res->end == res_list_elem->end) { + + /* + * The Region(addr,len) already exist in the list, + * just increase the count + */ + + res_list_elem->count++; + return 0; + } + } + + res->count = 1; + list_add(&res->resource_list, &resource_list_head); + return 1; +} + +static inline void acpi_res_list_del(struct acpi_res_list *res) +{ + struct acpi_res_list *res_list_elem; + + list_for_each_entry(res_list_elem, &resource_list_head, + resource_list) { + + if (res->resource_type == res_list_elem->resource_type && + res->start == res_list_elem->start && + res->end == res_list_elem->end) { + + /* + * If the res count is decreased to 0, + * remove and free it + */ + + if (--res_list_elem->count == 0) { + list_del(&res_list_elem->resource_list); + kfree(res_list_elem); + } + return; + } + } +} + +acpi_status +acpi_os_invalidate_address( + u8 space_id, + acpi_physical_address address, + acpi_size length) +{ + struct acpi_res_list res; + + switch (space_id) { + case ACPI_ADR_SPACE_SYSTEM_IO: + case ACPI_ADR_SPACE_SYSTEM_MEMORY: + /* Only interference checks against SystemIO and SytemMemory + are needed */ + res.start = address; + res.end = address + length - 1; + res.resource_type = space_id; + spin_lock(&acpi_res_lock); + acpi_res_list_del(&res); + spin_unlock(&acpi_res_lock); + break; + case ACPI_ADR_SPACE_PCI_CONFIG: + case ACPI_ADR_SPACE_EC: + case ACPI_ADR_SPACE_SMBUS: + case ACPI_ADR_SPACE_CMOS: + case ACPI_ADR_SPACE_PCI_BAR_TARGET: + case ACPI_ADR_SPACE_DATA_TABLE: + case ACPI_ADR_SPACE_FIXED_HARDWARE: + break; + } + return AE_OK; +} + /****************************************************************************** * * FUNCTION: acpi_os_validate_address @@ -1382,6 +1450,7 @@ acpi_os_validate_address ( char *name) { struct acpi_res_list *res; + int added; if (acpi_enforce_resources == ENFORCE_RESOURCES_NO) return AE_OK; @@ -1399,14 +1468,17 @@ acpi_os_validate_address ( res->end = address + length - 1; res->resource_type = space_id; spin_lock(&acpi_res_lock); - list_add(&res->resource_list, &resource_list_head); + added = acpi_res_list_add(res); spin_unlock(&acpi_res_lock); - pr_debug("Added %s resource: start: 0x%llx, end: 0x%llx, " - "name: %s\n", (space_id == ACPI_ADR_SPACE_SYSTEM_IO) + pr_debug("%s %s resource: start: 0x%llx, end: 0x%llx, " + "name: %s\n", added ? "Added" : "Already exist", + (space_id == ACPI_ADR_SPACE_SYSTEM_IO) ? "SystemIO" : "System Memory", (unsigned long long)res->start, (unsigned long long)res->end, res->name); + if (!added) + kfree(res); break; case ACPI_ADR_SPACE_PCI_CONFIG: case ACPI_ADR_SPACE_EC: diff --git a/drivers/acpi/pci_irq.c b/drivers/acpi/pci_irq.c index b794eb88ab90..843699ed93f2 100644 --- a/drivers/acpi/pci_irq.c +++ b/drivers/acpi/pci_irq.c @@ -40,6 +40,8 @@ #include <acpi/acpi_bus.h> #include <acpi/acpi_drivers.h> +#define PREFIX "ACPI: " + #define _COMPONENT ACPI_PCI_COMPONENT ACPI_MODULE_NAME("pci_irq"); diff --git a/drivers/acpi/pci_link.c b/drivers/acpi/pci_link.c index 16e0f9d3d17c..394ae89409c2 100644 --- a/drivers/acpi/pci_link.c +++ b/drivers/acpi/pci_link.c @@ -43,6 +43,8 @@ #include <acpi/acpi_bus.h> #include <acpi/acpi_drivers.h> +#define PREFIX "ACPI: " + #define _COMPONENT ACPI_PCI_COMPONENT ACPI_MODULE_NAME("pci_link"); #define ACPI_PCI_LINK_CLASS "pci_irq_routing" diff --git a/drivers/acpi/pci_root.c b/drivers/acpi/pci_root.c index 31b961c2f22f..1af808171d46 100644 --- a/drivers/acpi/pci_root.c +++ b/drivers/acpi/pci_root.c @@ -36,6 +36,8 @@ #include <acpi/acpi_bus.h> #include <acpi/acpi_drivers.h> +#define PREFIX "ACPI: " + #define _COMPONENT ACPI_PCI_COMPONENT ACPI_MODULE_NAME("pci_root"); #define ACPI_PCI_ROOT_CLASS "pci_bridge" @@ -387,6 +389,17 @@ struct pci_dev *acpi_get_pci_dev(acpi_handle handle) pbus = pdev->subordinate; pci_dev_put(pdev); + + /* + * This function may be called for a non-PCI device that has a + * PCI parent (eg. a disk under a PCI SATA controller). In that + * case pdev->subordinate will be NULL for the parent. + */ + if (!pbus) { + dev_dbg(&pdev->dev, "Not a PCI-to-PCI bridge\n"); + pdev = NULL; + break; + } } out: list_for_each_entry_safe(node, tmp, &device_list, node) diff --git a/drivers/acpi/pci_slot.c b/drivers/acpi/pci_slot.c index 12158e0d009b..45da2bae36c8 100644 --- a/drivers/acpi/pci_slot.c +++ b/drivers/acpi/pci_slot.c @@ -31,6 +31,7 @@ #include <linux/acpi.h> #include <acpi/acpi_bus.h> #include <acpi/acpi_drivers.h> +#include <linux/dmi.h> static int debug; static int check_sta_before_sun; @@ -57,7 +58,7 @@ ACPI_MODULE_NAME("pci_slot"); MY_NAME , ## arg); \ } while (0) -#define SLOT_NAME_SIZE 20 /* Inspired by #define in acpiphp.h */ +#define SLOT_NAME_SIZE 21 /* Inspired by #define in acpiphp.h */ struct acpi_pci_slot { acpi_handle root_handle; /* handle of the root bridge */ @@ -149,7 +150,7 @@ register_slot(acpi_handle handle, u32 lvl, void *context, void **rv) return AE_OK; } - snprintf(name, sizeof(name), "%u", (u32)sun); + snprintf(name, sizeof(name), "%llu", sun); pci_slot = pci_create_slot(pci_bus, device, name, NULL); if (IS_ERR(pci_slot)) { err("pci_create_slot returned %ld\n", PTR_ERR(pci_slot)); diff --git a/drivers/acpi/power.c b/drivers/acpi/power.c index 5a09bf392ec1..22b297916519 100644 --- a/drivers/acpi/power.c +++ b/drivers/acpi/power.c @@ -43,9 +43,10 @@ #include <linux/seq_file.h> #include <acpi/acpi_bus.h> #include <acpi/acpi_drivers.h> - #include "sleep.h" +#define PREFIX "ACPI: " + #define _COMPONENT ACPI_POWER_COMPONENT ACPI_MODULE_NAME("power"); #define ACPI_POWER_CLASS "power_resource" diff --git a/drivers/acpi/power_meter.c b/drivers/acpi/power_meter.c new file mode 100644 index 000000000000..2ef7030a0c28 --- /dev/null +++ b/drivers/acpi/power_meter.c @@ -0,0 +1,1022 @@ +/* + * A hwmon driver for ACPI 4.0 power meters + * Copyright (C) 2009 IBM + * + * Author: Darrick J. Wong <djwong@us.ibm.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <linux/module.h> +#include <linux/hwmon.h> +#include <linux/hwmon-sysfs.h> +#include <linux/jiffies.h> +#include <linux/mutex.h> +#include <linux/dmi.h> +#include <linux/kdev_t.h> +#include <linux/sched.h> +#include <linux/time.h> +#include <acpi/acpi_drivers.h> +#include <acpi/acpi_bus.h> + +#define ACPI_POWER_METER_NAME "power_meter" +ACPI_MODULE_NAME(ACPI_POWER_METER_NAME); +#define ACPI_POWER_METER_DEVICE_NAME "Power Meter" +#define ACPI_POWER_METER_CLASS "power_meter_resource" + +#define NUM_SENSORS 17 + +#define POWER_METER_CAN_MEASURE (1 << 0) +#define POWER_METER_CAN_TRIP (1 << 1) +#define POWER_METER_CAN_CAP (1 << 2) +#define POWER_METER_CAN_NOTIFY (1 << 3) +#define POWER_METER_IS_BATTERY (1 << 8) +#define UNKNOWN_HYSTERESIS 0xFFFFFFFF + +#define METER_NOTIFY_CONFIG 0x80 +#define METER_NOTIFY_TRIP 0x81 +#define METER_NOTIFY_CAP 0x82 +#define METER_NOTIFY_CAPPING 0x83 +#define METER_NOTIFY_INTERVAL 0x84 + +#define POWER_AVERAGE_NAME "power1_average" +#define POWER_CAP_NAME "power1_cap" +#define POWER_AVG_INTERVAL_NAME "power1_average_interval" +#define POWER_ALARM_NAME "power1_alarm" + +static int cap_in_hardware; +static int force_cap_on; + +static int can_cap_in_hardware(void) +{ + return force_cap_on || cap_in_hardware; +} + +static struct acpi_device_id power_meter_ids[] = { + {"ACPI000D", 0}, + {"", 0}, +}; +MODULE_DEVICE_TABLE(acpi, power_meter_ids); + +struct acpi_power_meter_capabilities { + acpi_integer flags; + acpi_integer units; + acpi_integer type; + acpi_integer accuracy; + acpi_integer sampling_time; + acpi_integer min_avg_interval; + acpi_integer max_avg_interval; + acpi_integer hysteresis; + acpi_integer configurable_cap; + acpi_integer min_cap; + acpi_integer max_cap; +}; + +struct acpi_power_meter_resource { + struct acpi_device *acpi_dev; + acpi_bus_id name; + struct mutex lock; + struct device *hwmon_dev; + struct acpi_power_meter_capabilities caps; + acpi_string model_number; + acpi_string serial_number; + acpi_string oem_info; + acpi_integer power; + acpi_integer cap; + acpi_integer avg_interval; + int sensors_valid; + unsigned long sensors_last_updated; + struct sensor_device_attribute sensors[NUM_SENSORS]; + int num_sensors; + int trip[2]; + int num_domain_devices; + struct acpi_device **domain_devices; + struct kobject *holders_dir; +}; + +struct ro_sensor_template { + char *label; + ssize_t (*show)(struct device *dev, + struct device_attribute *devattr, + char *buf); + int index; +}; + +struct rw_sensor_template { + char *label; + ssize_t (*show)(struct device *dev, + struct device_attribute *devattr, + char *buf); + ssize_t (*set)(struct device *dev, + struct device_attribute *devattr, + const char *buf, size_t count); + int index; +}; + +/* Averaging interval */ +static int update_avg_interval(struct acpi_power_meter_resource *resource) +{ + unsigned long long data; + acpi_status status; + + status = acpi_evaluate_integer(resource->acpi_dev->handle, "_GAI", + NULL, &data); + if (ACPI_FAILURE(status)) { + ACPI_EXCEPTION((AE_INFO, status, "Evaluating _GAI")); + return -ENODEV; + } + + resource->avg_interval = data; + return 0; +} + +static ssize_t show_avg_interval(struct device *dev, + struct device_attribute *devattr, + char *buf) +{ + struct acpi_device *acpi_dev = to_acpi_device(dev); + struct acpi_power_meter_resource *resource = acpi_dev->driver_data; + + mutex_lock(&resource->lock); + update_avg_interval(resource); + mutex_unlock(&resource->lock); + + return sprintf(buf, "%llu\n", resource->avg_interval); +} + +static ssize_t set_avg_interval(struct device *dev, + struct device_attribute *devattr, + const char *buf, size_t count) +{ + struct acpi_device *acpi_dev = to_acpi_device(dev); + struct acpi_power_meter_resource *resource = acpi_dev->driver_data; + union acpi_object arg0 = { ACPI_TYPE_INTEGER }; + struct acpi_object_list args = { 1, &arg0 }; + int res; + unsigned long temp; + unsigned long long data; + acpi_status status; + + res = strict_strtoul(buf, 10, &temp); + if (res) + return res; + + if (temp > resource->caps.max_avg_interval || + temp < resource->caps.min_avg_interval) + return -EINVAL; + arg0.integer.value = temp; + + mutex_lock(&resource->lock); + status = acpi_evaluate_integer(resource->acpi_dev->handle, "_PAI", + &args, &data); + if (!ACPI_FAILURE(status)) + resource->avg_interval = temp; + mutex_unlock(&resource->lock); + + if (ACPI_FAILURE(status)) { + ACPI_EXCEPTION((AE_INFO, status, "Evaluating _PAI")); + return -EINVAL; + } + + /* _PAI returns 0 on success, nonzero otherwise */ + if (data) + return -EINVAL; + + return count; +} + +/* Cap functions */ +static int update_cap(struct acpi_power_meter_resource *resource) +{ + unsigned long long data; + acpi_status status; + + status = acpi_evaluate_integer(resource->acpi_dev->handle, "_GHL", + NULL, &data); + if (ACPI_FAILURE(status)) { + ACPI_EXCEPTION((AE_INFO, status, "Evaluating _GHL")); + return -ENODEV; + } + + resource->cap = data; + return 0; +} + +static ssize_t show_cap(struct device *dev, + struct device_attribute *devattr, + char *buf) +{ + struct acpi_device *acpi_dev = to_acpi_device(dev); + struct acpi_power_meter_resource *resource = acpi_dev->driver_data; + + mutex_lock(&resource->lock); + update_cap(resource); + mutex_unlock(&resource->lock); + + return sprintf(buf, "%llu\n", resource->cap * 1000); +} + +static ssize_t set_cap(struct device *dev, struct device_attribute *devattr, + const char *buf, size_t count) +{ + struct acpi_device *acpi_dev = to_acpi_device(dev); + struct acpi_power_meter_resource *resource = acpi_dev->driver_data; + union acpi_object arg0 = { ACPI_TYPE_INTEGER }; + struct acpi_object_list args = { 1, &arg0 }; + int res; + unsigned long temp; + unsigned long long data; + acpi_status status; + + res = strict_strtoul(buf, 10, &temp); + if (res) + return res; + + temp /= 1000; + if (temp > resource->caps.max_cap || temp < resource->caps.min_cap) + return -EINVAL; + arg0.integer.value = temp; + + mutex_lock(&resource->lock); + status = acpi_evaluate_integer(resource->acpi_dev->handle, "_SHL", + &args, &data); + if (!ACPI_FAILURE(status)) + resource->cap = temp; + mutex_unlock(&resource->lock); + + if (ACPI_FAILURE(status)) { + ACPI_EXCEPTION((AE_INFO, status, "Evaluating _SHL")); + return -EINVAL; + } + + /* _SHL returns 0 on success, nonzero otherwise */ + if (data) + return -EINVAL; + + return count; +} + +/* Power meter trip points */ +static int set_acpi_trip(struct acpi_power_meter_resource *resource) +{ + union acpi_object arg_objs[] = { + {ACPI_TYPE_INTEGER}, + {ACPI_TYPE_INTEGER} + }; + struct acpi_object_list args = { 2, arg_objs }; + unsigned long long data; + acpi_status status; + + /* Both trip levels must be set */ + if (resource->trip[0] < 0 || resource->trip[1] < 0) + return 0; + + /* This driver stores min, max; ACPI wants max, min. */ + arg_objs[0].integer.value = resource->trip[1]; + arg_objs[1].integer.value = resource->trip[0]; + + status = acpi_evaluate_integer(resource->acpi_dev->handle, "_PTP", + &args, &data); + if (ACPI_FAILURE(status)) { + ACPI_EXCEPTION((AE_INFO, status, "Evaluating _PTP")); + return -EINVAL; + } + + /* _PTP returns 0 on success, nonzero otherwise */ + if (data) + return -EINVAL; + + return 0; +} + +static ssize_t set_trip(struct device *dev, struct device_attribute *devattr, + const char *buf, size_t count) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct acpi_device *acpi_dev = to_acpi_device(dev); + struct acpi_power_meter_resource *resource = acpi_dev->driver_data; + int res; + unsigned long temp; + + res = strict_strtoul(buf, 10, &temp); + if (res) + return res; + + temp /= 1000; + if (temp < 0) + return -EINVAL; + + mutex_lock(&resource->lock); + resource->trip[attr->index - 7] = temp; + res = set_acpi_trip(resource); + mutex_unlock(&resource->lock); + + if (res) + return res; + + return count; +} + +/* Power meter */ +static int update_meter(struct acpi_power_meter_resource *resource) +{ + unsigned long long data; + acpi_status status; + unsigned long local_jiffies = jiffies; + + if (time_before(local_jiffies, resource->sensors_last_updated + + msecs_to_jiffies(resource->caps.sampling_time)) && + resource->sensors_valid) + return 0; + + status = acpi_evaluate_integer(resource->acpi_dev->handle, "_PMM", + NULL, &data); + if (ACPI_FAILURE(status)) { + ACPI_EXCEPTION((AE_INFO, status, "Evaluating _PMM")); + return -ENODEV; + } + + resource->power = data; + resource->sensors_valid = 1; + resource->sensors_last_updated = jiffies; + return 0; +} + +static ssize_t show_power(struct device *dev, + struct device_attribute *devattr, + char *buf) +{ + struct acpi_device *acpi_dev = to_acpi_device(dev); + struct acpi_power_meter_resource *resource = acpi_dev->driver_data; + + mutex_lock(&resource->lock); + update_meter(resource); + mutex_unlock(&resource->lock); + + return sprintf(buf, "%llu\n", resource->power * 1000); +} + +/* Miscellaneous */ +static ssize_t show_str(struct device *dev, + struct device_attribute *devattr, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct acpi_device *acpi_dev = to_acpi_device(dev); + struct acpi_power_meter_resource *resource = acpi_dev->driver_data; + acpi_string val; + + switch (attr->index) { + case 0: + val = resource->model_number; + break; + case 1: + val = resource->serial_number; + break; + case 2: + val = resource->oem_info; + break; + default: + BUG(); + } + + return sprintf(buf, "%s\n", val); +} + +static ssize_t show_val(struct device *dev, + struct device_attribute *devattr, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct acpi_device *acpi_dev = to_acpi_device(dev); + struct acpi_power_meter_resource *resource = acpi_dev->driver_data; + acpi_integer val = 0; + + switch (attr->index) { + case 0: + val = resource->caps.min_avg_interval; + break; + case 1: + val = resource->caps.max_avg_interval; + break; + case 2: + val = resource->caps.min_cap * 1000; + break; + case 3: + val = resource->caps.max_cap * 1000; + break; + case 4: + if (resource->caps.hysteresis == UNKNOWN_HYSTERESIS) + return sprintf(buf, "unknown\n"); + + val = resource->caps.hysteresis * 1000; + break; + case 5: + if (resource->caps.flags & POWER_METER_IS_BATTERY) + val = 1; + else + val = 0; + break; + case 6: + if (resource->power > resource->cap) + val = 1; + else + val = 0; + break; + case 7: + case 8: + if (resource->trip[attr->index - 7] < 0) + return sprintf(buf, "unknown\n"); + + val = resource->trip[attr->index - 7] * 1000; + break; + default: + BUG(); + } + + return sprintf(buf, "%llu\n", val); +} + +static ssize_t show_accuracy(struct device *dev, + struct device_attribute *devattr, + char *buf) +{ + struct acpi_device *acpi_dev = to_acpi_device(dev); + struct acpi_power_meter_resource *resource = acpi_dev->driver_data; + unsigned int acc = resource->caps.accuracy; + + return sprintf(buf, "%u.%u%%\n", acc / 1000, acc % 1000); +} + +static ssize_t show_name(struct device *dev, + struct device_attribute *devattr, + char *buf) +{ + return sprintf(buf, "%s\n", ACPI_POWER_METER_NAME); +} + +/* Sensor descriptions. If you add a sensor, update NUM_SENSORS above! */ +static struct ro_sensor_template meter_ro_attrs[] = { +{POWER_AVERAGE_NAME, show_power, 0}, +{"power1_accuracy", show_accuracy, 0}, +{"power1_average_interval_min", show_val, 0}, +{"power1_average_interval_max", show_val, 1}, +{"power1_is_battery", show_val, 5}, +{NULL, NULL, 0}, +}; + +static struct rw_sensor_template meter_rw_attrs[] = { +{POWER_AVG_INTERVAL_NAME, show_avg_interval, set_avg_interval, 0}, +{NULL, NULL, NULL, 0}, +}; + +static struct ro_sensor_template misc_cap_attrs[] = { +{"power1_cap_min", show_val, 2}, +{"power1_cap_max", show_val, 3}, +{"power1_cap_hyst", show_val, 4}, +{POWER_ALARM_NAME, show_val, 6}, +{NULL, NULL, 0}, +}; + +static struct ro_sensor_template ro_cap_attrs[] = { +{POWER_CAP_NAME, show_cap, 0}, +{NULL, NULL, 0}, +}; + +static struct rw_sensor_template rw_cap_attrs[] = { +{POWER_CAP_NAME, show_cap, set_cap, 0}, +{NULL, NULL, NULL, 0}, +}; + +static struct rw_sensor_template trip_attrs[] = { +{"power1_average_min", show_val, set_trip, 7}, +{"power1_average_max", show_val, set_trip, 8}, +{NULL, NULL, NULL, 0}, +}; + +static struct ro_sensor_template misc_attrs[] = { +{"name", show_name, 0}, +{"power1_model_number", show_str, 0}, +{"power1_oem_info", show_str, 2}, +{"power1_serial_number", show_str, 1}, +{NULL, NULL, 0}, +}; + +/* Read power domain data */ +static void remove_domain_devices(struct acpi_power_meter_resource *resource) +{ + int i; + + if (!resource->num_domain_devices) + return; + + for (i = 0; i < resource->num_domain_devices; i++) { + struct acpi_device *obj = resource->domain_devices[i]; + if (!obj) + continue; + + sysfs_remove_link(resource->holders_dir, + kobject_name(&obj->dev.kobj)); + put_device(&obj->dev); + } + + kfree(resource->domain_devices); + kobject_put(resource->holders_dir); +} + +static int read_domain_devices(struct acpi_power_meter_resource *resource) +{ + int res = 0; + int i; + struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; + union acpi_object *pss; + acpi_status status; + + status = acpi_evaluate_object(resource->acpi_dev->handle, "_PMD", NULL, + &buffer); + if (ACPI_FAILURE(status)) { + ACPI_EXCEPTION((AE_INFO, status, "Evaluating _PMD")); + return -ENODEV; + } + + pss = buffer.pointer; + if (!pss || + pss->type != ACPI_TYPE_PACKAGE) { + dev_err(&resource->acpi_dev->dev, ACPI_POWER_METER_NAME + "Invalid _PMD data\n"); + res = -EFAULT; + goto end; + } + + if (!pss->package.count) + goto end; + + resource->domain_devices = kzalloc(sizeof(struct acpi_device *) * + pss->package.count, GFP_KERNEL); + if (!resource->domain_devices) { + res = -ENOMEM; + goto end; + } + + resource->holders_dir = kobject_create_and_add("measures", + &resource->acpi_dev->dev.kobj); + if (!resource->holders_dir) { + res = -ENOMEM; + goto exit_free; + } + + resource->num_domain_devices = pss->package.count; + + for (i = 0; i < pss->package.count; i++) { + struct acpi_device *obj; + union acpi_object *element = &(pss->package.elements[i]); + + /* Refuse non-references */ + if (element->type != ACPI_TYPE_LOCAL_REFERENCE) + continue; + + /* Create a symlink to domain objects */ + resource->domain_devices[i] = NULL; + status = acpi_bus_get_device(element->reference.handle, + &resource->domain_devices[i]); + if (ACPI_FAILURE(status)) + continue; + + obj = resource->domain_devices[i]; + get_device(&obj->dev); + + res = sysfs_create_link(resource->holders_dir, &obj->dev.kobj, + kobject_name(&obj->dev.kobj)); + if (res) { + put_device(&obj->dev); + resource->domain_devices[i] = NULL; + } + } + + res = 0; + goto end; + +exit_free: + kfree(resource->domain_devices); +end: + kfree(buffer.pointer); + return res; +} + +/* Registration and deregistration */ +static int register_ro_attrs(struct acpi_power_meter_resource *resource, + struct ro_sensor_template *ro) +{ + struct device *dev = &resource->acpi_dev->dev; + struct sensor_device_attribute *sensors = + &resource->sensors[resource->num_sensors]; + int res = 0; + + while (ro->label) { + sensors->dev_attr.attr.name = ro->label; + sensors->dev_attr.attr.mode = S_IRUGO; + sensors->dev_attr.show = ro->show; + sensors->index = ro->index; + + res = device_create_file(dev, &sensors->dev_attr); + if (res) { + sensors->dev_attr.attr.name = NULL; + goto error; + } + sensors++; + resource->num_sensors++; + ro++; + } + +error: + return res; +} + +static int register_rw_attrs(struct acpi_power_meter_resource *resource, + struct rw_sensor_template *rw) +{ + struct device *dev = &resource->acpi_dev->dev; + struct sensor_device_attribute *sensors = + &resource->sensors[resource->num_sensors]; + int res = 0; + + while (rw->label) { + sensors->dev_attr.attr.name = rw->label; + sensors->dev_attr.attr.mode = S_IRUGO | S_IWUSR; + sensors->dev_attr.show = rw->show; + sensors->dev_attr.store = rw->set; + sensors->index = rw->index; + + res = device_create_file(dev, &sensors->dev_attr); + if (res) { + sensors->dev_attr.attr.name = NULL; + goto error; + } + sensors++; + resource->num_sensors++; + rw++; + } + +error: + return res; +} + +static void remove_attrs(struct acpi_power_meter_resource *resource) +{ + int i; + + for (i = 0; i < resource->num_sensors; i++) { + if (!resource->sensors[i].dev_attr.attr.name) + continue; + device_remove_file(&resource->acpi_dev->dev, + &resource->sensors[i].dev_attr); + } + + remove_domain_devices(resource); + + resource->num_sensors = 0; +} + +static int setup_attrs(struct acpi_power_meter_resource *resource) +{ + int res = 0; + + res = read_domain_devices(resource); + if (res) + return res; + + if (resource->caps.flags & POWER_METER_CAN_MEASURE) { + res = register_ro_attrs(resource, meter_ro_attrs); + if (res) + goto error; + res = register_rw_attrs(resource, meter_rw_attrs); + if (res) + goto error; + } + + if (resource->caps.flags & POWER_METER_CAN_CAP) { + if (!can_cap_in_hardware()) { + dev_err(&resource->acpi_dev->dev, + "Ignoring unsafe software power cap!\n"); + goto skip_unsafe_cap; + } + + if (resource->caps.configurable_cap) { + res = register_rw_attrs(resource, rw_cap_attrs); + if (res) + goto error; + } else { + res = register_ro_attrs(resource, ro_cap_attrs); + if (res) + goto error; + } + res = register_ro_attrs(resource, misc_cap_attrs); + if (res) + goto error; + } +skip_unsafe_cap: + + if (resource->caps.flags & POWER_METER_CAN_TRIP) { + res = register_rw_attrs(resource, trip_attrs); + if (res) + goto error; + } + + res = register_ro_attrs(resource, misc_attrs); + if (res) + goto error; + + return res; +error: + remove_domain_devices(resource); + remove_attrs(resource); + return res; +} + +static void free_capabilities(struct acpi_power_meter_resource *resource) +{ + acpi_string *str; + int i; + + str = &resource->model_number; + for (i = 0; i < 3; i++, str++) + kfree(*str); +} + +static int read_capabilities(struct acpi_power_meter_resource *resource) +{ + int res = 0; + int i; + struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; + struct acpi_buffer state = { 0, NULL }; + struct acpi_buffer format = { sizeof("NNNNNNNNNNN"), "NNNNNNNNNNN" }; + union acpi_object *pss; + acpi_string *str; + acpi_status status; + + status = acpi_evaluate_object(resource->acpi_dev->handle, "_PMC", NULL, + &buffer); + if (ACPI_FAILURE(status)) { + ACPI_EXCEPTION((AE_INFO, status, "Evaluating _PMC")); + return -ENODEV; + } + + pss = buffer.pointer; + if (!pss || + pss->type != ACPI_TYPE_PACKAGE || + pss->package.count != 14) { + dev_err(&resource->acpi_dev->dev, ACPI_POWER_METER_NAME + "Invalid _PMC data\n"); + res = -EFAULT; + goto end; + } + + /* Grab all the integer data at once */ + state.length = sizeof(struct acpi_power_meter_capabilities); + state.pointer = &resource->caps; + + status = acpi_extract_package(pss, &format, &state); + if (ACPI_FAILURE(status)) { + ACPI_EXCEPTION((AE_INFO, status, "Invalid data")); + res = -EFAULT; + goto end; + } + + if (resource->caps.units) { + dev_err(&resource->acpi_dev->dev, ACPI_POWER_METER_NAME + "Unknown units %llu.\n", + resource->caps.units); + res = -EINVAL; + goto end; + } + + /* Grab the string data */ + str = &resource->model_number; + + for (i = 11; i < 14; i++) { + union acpi_object *element = &(pss->package.elements[i]); + + if (element->type != ACPI_TYPE_STRING) { + res = -EINVAL; + goto error; + } + + *str = kzalloc(sizeof(u8) * (element->string.length + 1), + GFP_KERNEL); + if (!*str) { + res = -ENOMEM; + goto error; + } + + strncpy(*str, element->string.pointer, element->string.length); + str++; + } + + dev_info(&resource->acpi_dev->dev, "Found ACPI power meter.\n"); + goto end; +error: + str = &resource->model_number; + for (i = 0; i < 3; i++, str++) + kfree(*str); +end: + kfree(buffer.pointer); + return res; +} + +/* Handle ACPI event notifications */ +static void acpi_power_meter_notify(struct acpi_device *device, u32 event) +{ + struct acpi_power_meter_resource *resource; + int res; + + if (!device || !acpi_driver_data(device)) + return; + + resource = acpi_driver_data(device); + + mutex_lock(&resource->lock); + switch (event) { + case METER_NOTIFY_CONFIG: + free_capabilities(resource); + res = read_capabilities(resource); + if (res) + break; + + remove_attrs(resource); + setup_attrs(resource); + break; + case METER_NOTIFY_TRIP: + sysfs_notify(&device->dev.kobj, NULL, POWER_AVERAGE_NAME); + update_meter(resource); + break; + case METER_NOTIFY_CAP: + sysfs_notify(&device->dev.kobj, NULL, POWER_CAP_NAME); + update_cap(resource); + break; + case METER_NOTIFY_INTERVAL: + sysfs_notify(&device->dev.kobj, NULL, POWER_AVG_INTERVAL_NAME); + update_avg_interval(resource); + break; + case METER_NOTIFY_CAPPING: + sysfs_notify(&device->dev.kobj, NULL, POWER_ALARM_NAME); + dev_info(&device->dev, "Capping in progress.\n"); + break; + default: + BUG(); + } + mutex_unlock(&resource->lock); + + acpi_bus_generate_netlink_event(ACPI_POWER_METER_CLASS, + dev_name(&device->dev), event, 0); +} + +static int acpi_power_meter_add(struct acpi_device *device) +{ + int res; + struct acpi_power_meter_resource *resource; + + if (!device) + return -EINVAL; + + resource = kzalloc(sizeof(struct acpi_power_meter_resource), + GFP_KERNEL); + if (!resource) + return -ENOMEM; + + resource->sensors_valid = 0; + resource->acpi_dev = device; + mutex_init(&resource->lock); + strcpy(acpi_device_name(device), ACPI_POWER_METER_DEVICE_NAME); + strcpy(acpi_device_class(device), ACPI_POWER_METER_CLASS); + device->driver_data = resource; + + free_capabilities(resource); + res = read_capabilities(resource); + if (res) + goto exit_free; + + resource->trip[0] = resource->trip[1] = -1; + + res = setup_attrs(resource); + if (res) + goto exit_free; + + resource->hwmon_dev = hwmon_device_register(&device->dev); + if (IS_ERR(resource->hwmon_dev)) { + res = PTR_ERR(resource->hwmon_dev); + goto exit_remove; + } + + res = 0; + goto exit; + +exit_remove: + remove_attrs(resource); +exit_free: + kfree(resource); +exit: + return res; +} + +static int acpi_power_meter_remove(struct acpi_device *device, int type) +{ + struct acpi_power_meter_resource *resource; + + if (!device || !acpi_driver_data(device)) + return -EINVAL; + + resource = acpi_driver_data(device); + hwmon_device_unregister(resource->hwmon_dev); + + free_capabilities(resource); + remove_attrs(resource); + + kfree(resource); + return 0; +} + +static int acpi_power_meter_resume(struct acpi_device *device) +{ + struct acpi_power_meter_resource *resource; + + if (!device || !acpi_driver_data(device)) + return -EINVAL; + + resource = acpi_driver_data(device); + free_capabilities(resource); + read_capabilities(resource); + + return 0; +} + +static struct acpi_driver acpi_power_meter_driver = { + .name = "power_meter", + .class = ACPI_POWER_METER_CLASS, + .ids = power_meter_ids, + .ops = { + .add = acpi_power_meter_add, + .remove = acpi_power_meter_remove, + .resume = acpi_power_meter_resume, + .notify = acpi_power_meter_notify, + }, +}; + +/* Module init/exit routines */ +static int __init enable_cap_knobs(const struct dmi_system_id *d) +{ + cap_in_hardware = 1; + return 0; +} + +static struct dmi_system_id __initdata pm_dmi_table[] = { + { + enable_cap_knobs, "IBM Active Energy Manager", + { + DMI_MATCH(DMI_SYS_VENDOR, "IBM") + }, + }, + {} +}; + +static int __init acpi_power_meter_init(void) +{ + int result; + + if (acpi_disabled) + return -ENODEV; + + dmi_check_system(pm_dmi_table); + + result = acpi_bus_register_driver(&acpi_power_meter_driver); + if (result < 0) + return -ENODEV; + + return 0; +} + +static void __exit acpi_power_meter_exit(void) +{ + acpi_bus_unregister_driver(&acpi_power_meter_driver); +} + +MODULE_AUTHOR("Darrick J. Wong <djwong@us.ibm.com>"); +MODULE_DESCRIPTION("ACPI 4.0 power meter driver"); +MODULE_LICENSE("GPL"); + +module_param(force_cap_on, bool, 0644); +MODULE_PARM_DESC(force_cap_on, "Enable power cap even it is unsafe to do so."); + +module_init(acpi_power_meter_init); +module_exit(acpi_power_meter_exit); diff --git a/drivers/acpi/proc.c b/drivers/acpi/proc.c index d0d550d22a6d..d0d25e2e1ced 100644 --- a/drivers/acpi/proc.c +++ b/drivers/acpi/proc.c @@ -393,11 +393,13 @@ acpi_system_write_wakeup_device(struct file *file, struct list_head *node, *next; char strbuf[5]; char str[5] = ""; - int len = count; + unsigned int len = count; struct acpi_device *found_dev = NULL; if (len > 4) len = 4; + if (len < 0) + return -EFAULT; if (copy_from_user(strbuf, buffer, len)) return -EFAULT; diff --git a/drivers/acpi/processor_core.c b/drivers/acpi/processor_core.c index 2cc4b3033872..ec742a4e5635 100644 --- a/drivers/acpi/processor_core.c +++ b/drivers/acpi/processor_core.c @@ -59,6 +59,8 @@ #include <acpi/acpi_drivers.h> #include <acpi/processor.h> +#define PREFIX "ACPI: " + #define ACPI_PROCESSOR_CLASS "processor" #define ACPI_PROCESSOR_DEVICE_NAME "Processor" #define ACPI_PROCESSOR_FILE_INFO "info" @@ -79,9 +81,10 @@ MODULE_DESCRIPTION("ACPI Processor Driver"); MODULE_LICENSE("GPL"); static int acpi_processor_add(struct acpi_device *device); -static int acpi_processor_start(struct acpi_device *device); static int acpi_processor_remove(struct acpi_device *device, int type); +#ifdef CONFIG_ACPI_PROCFS static int acpi_processor_info_open_fs(struct inode *inode, struct file *file); +#endif static void acpi_processor_notify(struct acpi_device *device, u32 event); static acpi_status acpi_processor_hotadd_init(acpi_handle handle, int *p_cpu); static int acpi_processor_handle_eject(struct acpi_processor *pr); @@ -101,7 +104,6 @@ static struct acpi_driver acpi_processor_driver = { .ops = { .add = acpi_processor_add, .remove = acpi_processor_remove, - .start = acpi_processor_start, .suspend = acpi_processor_suspend, .resume = acpi_processor_resume, .notify = acpi_processor_notify, @@ -110,7 +112,7 @@ static struct acpi_driver acpi_processor_driver = { #define INSTALL_NOTIFY_HANDLER 1 #define UNINSTALL_NOTIFY_HANDLER 2 - +#ifdef CONFIG_ACPI_PROCFS static const struct file_operations acpi_processor_info_fops = { .owner = THIS_MODULE, .open = acpi_processor_info_open_fs, @@ -118,6 +120,7 @@ static const struct file_operations acpi_processor_info_fops = { .llseek = seq_lseek, .release = single_release, }; +#endif DEFINE_PER_CPU(struct acpi_processor *, processors); struct acpi_processor_errata errata __read_mostly; @@ -316,6 +319,7 @@ static int acpi_processor_set_pdc(struct acpi_processor *pr) FS Interface (/proc) -------------------------------------------------------------------------- */ +#ifdef CONFIG_ACPI_PROCFS static struct proc_dir_entry *acpi_processor_dir = NULL; static int acpi_processor_info_seq_show(struct seq_file *seq, void *offset) @@ -388,7 +392,6 @@ static int acpi_processor_add_fs(struct acpi_device *device) return -EIO; return 0; } - static int acpi_processor_remove_fs(struct acpi_device *device) { @@ -405,6 +408,16 @@ static int acpi_processor_remove_fs(struct acpi_device *device) return 0; } +#else +static inline int acpi_processor_add_fs(struct acpi_device *device) +{ + return 0; +} +static inline int acpi_processor_remove_fs(struct acpi_device *device) +{ + return 0; +} +#endif /* Use the acpiid in MADT to map cpus in case of SMP */ @@ -698,92 +711,6 @@ static int acpi_processor_get_info(struct acpi_device *device) static DEFINE_PER_CPU(void *, processor_device_array); -static int __cpuinit acpi_processor_start(struct acpi_device *device) -{ - int result = 0; - struct acpi_processor *pr; - struct sys_device *sysdev; - - pr = acpi_driver_data(device); - - result = acpi_processor_get_info(device); - if (result) { - /* Processor is physically not present */ - return 0; - } - - BUG_ON((pr->id >= nr_cpu_ids) || (pr->id < 0)); - - /* - * Buggy BIOS check - * ACPI id of processors can be reported wrongly by the BIOS. - * Don't trust it blindly - */ - if (per_cpu(processor_device_array, pr->id) != NULL && - per_cpu(processor_device_array, pr->id) != device) { - printk(KERN_WARNING "BIOS reported wrong ACPI id " - "for the processor\n"); - return -ENODEV; - } - per_cpu(processor_device_array, pr->id) = device; - - per_cpu(processors, pr->id) = pr; - - result = acpi_processor_add_fs(device); - if (result) - goto end; - - sysdev = get_cpu_sysdev(pr->id); - if (sysfs_create_link(&device->dev.kobj, &sysdev->kobj, "sysdev")) - return -EFAULT; - - /* _PDC call should be done before doing anything else (if reqd.). */ - arch_acpi_processor_init_pdc(pr); - acpi_processor_set_pdc(pr); - arch_acpi_processor_cleanup_pdc(pr); - -#ifdef CONFIG_CPU_FREQ - acpi_processor_ppc_has_changed(pr); -#endif - acpi_processor_get_throttling_info(pr); - acpi_processor_get_limit_info(pr); - - - acpi_processor_power_init(pr, device); - - pr->cdev = thermal_cooling_device_register("Processor", device, - &processor_cooling_ops); - if (IS_ERR(pr->cdev)) { - result = PTR_ERR(pr->cdev); - goto end; - } - - dev_info(&device->dev, "registered as cooling_device%d\n", - pr->cdev->id); - - result = sysfs_create_link(&device->dev.kobj, - &pr->cdev->device.kobj, - "thermal_cooling"); - if (result) - printk(KERN_ERR PREFIX "Create sysfs link\n"); - result = sysfs_create_link(&pr->cdev->device.kobj, - &device->dev.kobj, - "device"); - if (result) - printk(KERN_ERR PREFIX "Create sysfs link\n"); - - if (pr->flags.throttling) { - printk(KERN_INFO PREFIX "%s [%s] (supports", - acpi_device_name(device), acpi_device_bid(device)); - printk(" %d throttling states", pr->throttling.state_count); - printk(")\n"); - } - - end: - - return result; -} - static void acpi_processor_notify(struct acpi_device *device, u32 event) { struct acpi_processor *pr = acpi_driver_data(device); @@ -843,13 +770,11 @@ static struct notifier_block acpi_cpu_notifier = .notifier_call = acpi_cpu_soft_notify, }; -static int acpi_processor_add(struct acpi_device *device) +static int __cpuinit acpi_processor_add(struct acpi_device *device) { struct acpi_processor *pr = NULL; - - - if (!device) - return -EINVAL; + int result = 0; + struct sys_device *sysdev; pr = kzalloc(sizeof(struct acpi_processor), GFP_KERNEL); if (!pr) @@ -865,7 +790,93 @@ static int acpi_processor_add(struct acpi_device *device) strcpy(acpi_device_class(device), ACPI_PROCESSOR_CLASS); device->driver_data = pr; + result = acpi_processor_get_info(device); + if (result) { + /* Processor is physically not present */ + return 0; + } + + BUG_ON((pr->id >= nr_cpu_ids) || (pr->id < 0)); + + /* + * Buggy BIOS check + * ACPI id of processors can be reported wrongly by the BIOS. + * Don't trust it blindly + */ + if (per_cpu(processor_device_array, pr->id) != NULL && + per_cpu(processor_device_array, pr->id) != device) { + printk(KERN_WARNING "BIOS reported wrong ACPI id " + "for the processor\n"); + result = -ENODEV; + goto err_free_cpumask; + } + per_cpu(processor_device_array, pr->id) = device; + + per_cpu(processors, pr->id) = pr; + + result = acpi_processor_add_fs(device); + if (result) + goto err_free_cpumask; + + sysdev = get_cpu_sysdev(pr->id); + if (sysfs_create_link(&device->dev.kobj, &sysdev->kobj, "sysdev")) { + result = -EFAULT; + goto err_remove_fs; + } + + /* _PDC call should be done before doing anything else (if reqd.). */ + arch_acpi_processor_init_pdc(pr); + acpi_processor_set_pdc(pr); + arch_acpi_processor_cleanup_pdc(pr); + +#ifdef CONFIG_CPU_FREQ + acpi_processor_ppc_has_changed(pr); +#endif + acpi_processor_get_throttling_info(pr); + acpi_processor_get_limit_info(pr); + + + acpi_processor_power_init(pr, device); + + pr->cdev = thermal_cooling_device_register("Processor", device, + &processor_cooling_ops); + if (IS_ERR(pr->cdev)) { + result = PTR_ERR(pr->cdev); + goto err_power_exit; + } + + dev_info(&device->dev, "registered as cooling_device%d\n", + pr->cdev->id); + + result = sysfs_create_link(&device->dev.kobj, + &pr->cdev->device.kobj, + "thermal_cooling"); + if (result) { + printk(KERN_ERR PREFIX "Create sysfs link\n"); + goto err_thermal_unregister; + } + result = sysfs_create_link(&pr->cdev->device.kobj, + &device->dev.kobj, + "device"); + if (result) { + printk(KERN_ERR PREFIX "Create sysfs link\n"); + goto err_remove_sysfs; + } + return 0; + +err_remove_sysfs: + sysfs_remove_link(&device->dev.kobj, "thermal_cooling"); +err_thermal_unregister: + thermal_cooling_device_unregister(pr->cdev); +err_power_exit: + acpi_processor_power_exit(pr, device); +err_remove_fs: + acpi_processor_remove_fs(device); +err_free_cpumask: + free_cpumask_var(pr->throttling.shared_cpu_map); + + return result; } static int acpi_processor_remove(struct acpi_device *device, int type) @@ -942,7 +953,6 @@ int acpi_processor_device_add(acpi_handle handle, struct acpi_device **device) { acpi_handle phandle; struct acpi_device *pdev; - struct acpi_processor *pr; if (acpi_get_parent(handle, &phandle)) { @@ -957,15 +967,6 @@ int acpi_processor_device_add(acpi_handle handle, struct acpi_device **device) return -ENODEV; } - acpi_bus_start(*device); - - pr = acpi_driver_data(*device); - if (!pr) - return -ENODEV; - - if ((pr->id >= 0) && (pr->id < nr_cpu_ids)) { - kobject_uevent(&(*device)->dev.kobj, KOBJ_ONLINE); - } return 0; } @@ -995,25 +996,6 @@ static void __ref acpi_processor_hotplug_notify(acpi_handle handle, "Unable to add the device\n"); break; } - - pr = acpi_driver_data(device); - if (!pr) { - printk(KERN_ERR PREFIX "Driver data is NULL\n"); - break; - } - - if (pr->id >= 0 && (pr->id < nr_cpu_ids)) { - kobject_uevent(&device->dev.kobj, KOBJ_OFFLINE); - break; - } - - result = acpi_processor_start(device); - if ((!result) && ((pr->id >= 0) && (pr->id < nr_cpu_ids))) { - kobject_uevent(&device->dev.kobj, KOBJ_ONLINE); - } else { - printk(KERN_ERR PREFIX "Device [%s] failed to start\n", - acpi_device_bid(device)); - } break; case ACPI_NOTIFY_EJECT_REQUEST: ACPI_DEBUG_PRINT((ACPI_DB_INFO, @@ -1030,9 +1012,6 @@ static void __ref acpi_processor_hotplug_notify(acpi_handle handle, "Driver data is NULL, dropping EJECT\n"); return; } - - if ((pr->id < nr_cpu_ids) && (cpu_present(pr->id))) - kobject_uevent(&device->dev.kobj, KOBJ_OFFLINE); break; default: ACPI_DEBUG_PRINT((ACPI_DB_INFO, @@ -1161,11 +1140,11 @@ static int __init acpi_processor_init(void) (struct acpi_table_header **)&madt))) madt = NULL; #endif - +#ifdef CONFIG_ACPI_PROCFS acpi_processor_dir = proc_mkdir(ACPI_PROCESSOR_CLASS, acpi_root_dir); if (!acpi_processor_dir) return -ENOMEM; - +#endif /* * Check whether the system is DMI table. If yes, OSPM * should not use mwait for CPU-states. @@ -1193,7 +1172,9 @@ out_cpuidle: cpuidle_unregister_driver(&acpi_idle_driver); out_proc: +#ifdef CONFIG_ACPI_PROCFS remove_proc_entry(ACPI_PROCESSOR_CLASS, acpi_root_dir); +#endif return result; } @@ -1213,7 +1194,9 @@ static void __exit acpi_processor_exit(void) cpuidle_unregister_driver(&acpi_idle_driver); +#ifdef CONFIG_ACPI_PROCFS remove_proc_entry(ACPI_PROCESSOR_CLASS, acpi_root_dir); +#endif return; } diff --git a/drivers/acpi/processor_idle.c b/drivers/acpi/processor_idle.c index 66393d5c4c7c..bbd066e7f854 100644 --- a/drivers/acpi/processor_idle.c +++ b/drivers/acpi/processor_idle.c @@ -60,6 +60,8 @@ #include <acpi/processor.h> #include <asm/processor.h> +#define PREFIX "ACPI: " + #define ACPI_PROCESSOR_CLASS "processor" #define _COMPONENT ACPI_PROCESSOR_COMPONENT ACPI_MODULE_NAME("processor_idle"); @@ -680,6 +682,7 @@ static int acpi_processor_get_power_info(struct acpi_processor *pr) return 0; } +#ifdef CONFIG_ACPI_PROCFS static int acpi_processor_power_seq_show(struct seq_file *seq, void *offset) { struct acpi_processor *pr = seq->private; @@ -759,7 +762,7 @@ static const struct file_operations acpi_processor_power_fops = { .llseek = seq_lseek, .release = single_release, }; - +#endif /** * acpi_idle_bm_check - checks if bus master activity was detected @@ -1160,8 +1163,9 @@ int __cpuinit acpi_processor_power_init(struct acpi_processor *pr, { acpi_status status = 0; static int first_run; +#ifdef CONFIG_ACPI_PROCFS struct proc_dir_entry *entry = NULL; - unsigned int i; +#endif if (boot_option_idle_override) return 0; @@ -1209,15 +1213,8 @@ int __cpuinit acpi_processor_power_init(struct acpi_processor *pr, acpi_processor_setup_cpuidle(pr); if (cpuidle_register_device(&pr->power.dev)) return -EIO; - - printk(KERN_INFO PREFIX "CPU%d (power states:", pr->id); - for (i = 1; i <= pr->power.count; i++) - if (pr->power.states[i].valid) - printk(" C%d[C%d]", i, - pr->power.states[i].type); - printk(")\n"); } - +#ifdef CONFIG_ACPI_PROCFS /* 'power' [R] */ entry = proc_create_data(ACPI_PROCESSOR_FILE_POWER, S_IRUGO, acpi_device_dir(device), @@ -1225,6 +1222,7 @@ int __cpuinit acpi_processor_power_init(struct acpi_processor *pr, acpi_driver_data(device)); if (!entry) return -EIO; +#endif return 0; } @@ -1237,9 +1235,11 @@ int acpi_processor_power_exit(struct acpi_processor *pr, cpuidle_unregister_device(&pr->power.dev); pr->flags.power_setup_done = 0; +#ifdef CONFIG_ACPI_PROCFS if (acpi_device_dir(device)) remove_proc_entry(ACPI_PROCESSOR_FILE_POWER, acpi_device_dir(device)); +#endif return 0; } diff --git a/drivers/acpi/processor_perflib.c b/drivers/acpi/processor_perflib.c index 60e543d3234e..8ba0ed0b9ddb 100644 --- a/drivers/acpi/processor_perflib.c +++ b/drivers/acpi/processor_perflib.c @@ -39,6 +39,8 @@ #include <acpi/acpi_drivers.h> #include <acpi/processor.h> +#define PREFIX "ACPI: " + #define ACPI_PROCESSOR_CLASS "processor" #define ACPI_PROCESSOR_FILE_PERFORMANCE "performance" #define _COMPONENT ACPI_PROCESSOR_COMPONENT @@ -509,7 +511,7 @@ int acpi_processor_preregister_performance( struct acpi_processor *match_pr; struct acpi_psd_package *match_pdomain; - if (!alloc_cpumask_var(&covered_cpus, GFP_KERNEL)) + if (!zalloc_cpumask_var(&covered_cpus, GFP_KERNEL)) return -ENOMEM; mutex_lock(&performance_mutex); @@ -556,7 +558,6 @@ int acpi_processor_preregister_performance( * Now that we have _PSD data from all CPUs, lets setup P-state * domain info. */ - cpumask_clear(covered_cpus); for_each_possible_cpu(i) { pr = per_cpu(processors, i); if (!pr) diff --git a/drivers/acpi/processor_thermal.c b/drivers/acpi/processor_thermal.c index 31adda1099e0..140c5c5b423c 100644 --- a/drivers/acpi/processor_thermal.c +++ b/drivers/acpi/processor_thermal.c @@ -40,6 +40,8 @@ #include <acpi/processor.h> #include <acpi/acpi_drivers.h> +#define PREFIX "ACPI: " + #define ACPI_PROCESSOR_CLASS "processor" #define _COMPONENT ACPI_PROCESSOR_COMPONENT ACPI_MODULE_NAME("processor_thermal"); @@ -438,7 +440,7 @@ struct thermal_cooling_device_ops processor_cooling_ops = { }; /* /proc interface */ - +#ifdef CONFIG_ACPI_PROCFS static int acpi_processor_limit_seq_show(struct seq_file *seq, void *offset) { struct acpi_processor *pr = (struct acpi_processor *)seq->private; @@ -517,3 +519,4 @@ const struct file_operations acpi_processor_limit_fops = { .llseek = seq_lseek, .release = single_release, }; +#endif diff --git a/drivers/acpi/processor_throttling.c b/drivers/acpi/processor_throttling.c index ae39797aab55..1c5d7a8b2fdf 100644 --- a/drivers/acpi/processor_throttling.c +++ b/drivers/acpi/processor_throttling.c @@ -41,6 +41,8 @@ #include <acpi/acpi_drivers.h> #include <acpi/processor.h> +#define PREFIX "ACPI: " + #define ACPI_PROCESSOR_CLASS "processor" #define _COMPONENT ACPI_PROCESSOR_COMPONENT ACPI_MODULE_NAME("processor_throttling"); @@ -75,7 +77,7 @@ static int acpi_processor_update_tsd_coord(void) struct acpi_tsd_package *pdomain, *match_pdomain; struct acpi_processor_throttling *pthrottling, *match_pthrottling; - if (!alloc_cpumask_var(&covered_cpus, GFP_KERNEL)) + if (!zalloc_cpumask_var(&covered_cpus, GFP_KERNEL)) return -ENOMEM; /* @@ -103,7 +105,6 @@ static int acpi_processor_update_tsd_coord(void) if (retval) goto err_ret; - cpumask_clear(covered_cpus); for_each_possible_cpu(i) { pr = per_cpu(processors, i); if (!pr) @@ -1132,15 +1133,15 @@ int acpi_processor_get_throttling_info(struct acpi_processor *pr) int result = 0; struct acpi_processor_throttling *pthrottling; + if (!pr) + return -EINVAL; + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "pblk_address[0x%08x] duty_offset[%d] duty_width[%d]\n", pr->throttling.address, pr->throttling.duty_offset, pr->throttling.duty_width)); - if (!pr) - return -EINVAL; - /* * Evaluate _PTC, _TSS and _TPC * They must all be present or none of them can be used. @@ -1216,7 +1217,7 @@ int acpi_processor_get_throttling_info(struct acpi_processor *pr) } /* proc interface */ - +#ifdef CONFIG_ACPI_PROCFS static int acpi_processor_throttling_seq_show(struct seq_file *seq, void *offset) { @@ -1324,3 +1325,4 @@ const struct file_operations acpi_processor_throttling_fops = { .llseek = seq_lseek, .release = single_release, }; +#endif diff --git a/drivers/acpi/sbs.c b/drivers/acpi/sbs.c index 4b214b74ebaa..52b9db8afc20 100644 --- a/drivers/acpi/sbs.c +++ b/drivers/acpi/sbs.c @@ -46,6 +46,8 @@ #include "sbshc.h" +#define PREFIX "ACPI: " + #define ACPI_SBS_CLASS "sbs" #define ACPI_AC_CLASS "ac_adapter" #define ACPI_BATTERY_CLASS "battery" diff --git a/drivers/acpi/sbshc.c b/drivers/acpi/sbshc.c index 0619734895b2..d9339806df45 100644 --- a/drivers/acpi/sbshc.c +++ b/drivers/acpi/sbshc.c @@ -15,6 +15,8 @@ #include <linux/interrupt.h> #include "sbshc.h" +#define PREFIX "ACPI: " + #define ACPI_SMB_HC_CLASS "smbus_host_controller" #define ACPI_SMB_HC_DEVICE_NAME "ACPI SMBus HC" diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index 318b1ea7a5bf..14a7481c97d7 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -22,6 +22,8 @@ extern struct acpi_device *acpi_root; #define ACPI_BUS_HID "LNXSYBUS" #define ACPI_BUS_DEVICE_NAME "System Bus" +#define ACPI_IS_ROOT_DEVICE(device) (!(device)->parent) + static LIST_HEAD(acpi_device_list); static LIST_HEAD(acpi_bus_id_list); DEFINE_MUTEX(acpi_device_lock); @@ -43,40 +45,19 @@ static int create_modalias(struct acpi_device *acpi_dev, char *modalias, { int len; int count; - - if (!acpi_dev->flags.hardware_id && !acpi_dev->flags.compatible_ids) - return -ENODEV; + struct acpi_hardware_id *id; len = snprintf(modalias, size, "acpi:"); size -= len; - if (acpi_dev->flags.hardware_id) { - count = snprintf(&modalias[len], size, "%s:", - acpi_dev->pnp.hardware_id); + list_for_each_entry(id, &acpi_dev->pnp.ids, list) { + count = snprintf(&modalias[len], size, "%s:", id->id); if (count < 0 || count >= size) return -EINVAL; len += count; size -= count; } - if (acpi_dev->flags.compatible_ids) { - struct acpi_compatible_id_list *cid_list; - int i; - - cid_list = acpi_dev->pnp.cid_list; - for (i = 0; i < cid_list->count; i++) { - count = snprintf(&modalias[len], size, "%s:", - cid_list->id[i].value); - if (count < 0 || count >= size) { - printk(KERN_ERR PREFIX "%s cid[%i] exceeds event buffer size", - acpi_dev->pnp.device_name, i); - break; - } - len += count; - size -= count; - } - } - modalias[len] = '\0'; return len; } @@ -183,7 +164,7 @@ static ssize_t acpi_device_hid_show(struct device *dev, struct device_attribute *attr, char *buf) { struct acpi_device *acpi_dev = to_acpi_device(dev); - return sprintf(buf, "%s\n", acpi_dev->pnp.hardware_id); + return sprintf(buf, "%s\n", acpi_device_hid(acpi_dev)); } static DEVICE_ATTR(hid, 0444, acpi_device_hid_show, NULL); @@ -219,17 +200,13 @@ static int acpi_device_setup_files(struct acpi_device *dev) goto end; } - if (dev->flags.hardware_id) { - result = device_create_file(&dev->dev, &dev_attr_hid); - if (result) - goto end; - } + result = device_create_file(&dev->dev, &dev_attr_hid); + if (result) + goto end; - if (dev->flags.hardware_id || dev->flags.compatible_ids) { - result = device_create_file(&dev->dev, &dev_attr_modalias); - if (result) - goto end; - } + result = device_create_file(&dev->dev, &dev_attr_modalias); + if (result) + goto end; /* * If device has _EJ0, 'eject' file is created that is used to trigger @@ -255,11 +232,8 @@ static void acpi_device_remove_files(struct acpi_device *dev) if (ACPI_SUCCESS(status)) device_remove_file(&dev->dev, &dev_attr_eject); - if (dev->flags.hardware_id || dev->flags.compatible_ids) - device_remove_file(&dev->dev, &dev_attr_modalias); - - if (dev->flags.hardware_id) - device_remove_file(&dev->dev, &dev_attr_hid); + device_remove_file(&dev->dev, &dev_attr_modalias); + device_remove_file(&dev->dev, &dev_attr_hid); if (dev->handle) device_remove_file(&dev->dev, &dev_attr_path); } @@ -271,6 +245,7 @@ int acpi_match_device_ids(struct acpi_device *device, const struct acpi_device_id *ids) { const struct acpi_device_id *id; + struct acpi_hardware_id *hwid; /* * If the device is not present, it is unnecessary to load device @@ -279,36 +254,30 @@ int acpi_match_device_ids(struct acpi_device *device, if (!device->status.present) return -ENODEV; - if (device->flags.hardware_id) { - for (id = ids; id->id[0]; id++) { - if (!strcmp((char*)id->id, device->pnp.hardware_id)) + for (id = ids; id->id[0]; id++) + list_for_each_entry(hwid, &device->pnp.ids, list) + if (!strcmp((char *) id->id, hwid->id)) return 0; - } - } - - if (device->flags.compatible_ids) { - struct acpi_compatible_id_list *cid_list = device->pnp.cid_list; - int i; - - for (id = ids; id->id[0]; id++) { - /* compare multiple _CID entries against driver ids */ - for (i = 0; i < cid_list->count; i++) { - if (!strcmp((char*)id->id, - cid_list->id[i].value)) - return 0; - } - } - } return -ENOENT; } EXPORT_SYMBOL(acpi_match_device_ids); +static void acpi_free_ids(struct acpi_device *device) +{ + struct acpi_hardware_id *id, *tmp; + + list_for_each_entry_safe(id, tmp, &device->pnp.ids, list) { + kfree(id->id); + kfree(id); + } +} + static void acpi_device_release(struct device *dev) { struct acpi_device *acpi_dev = to_acpi_device(dev); - kfree(acpi_dev->pnp.cid_list); + acpi_free_ids(acpi_dev); kfree(acpi_dev); } @@ -366,22 +335,21 @@ static acpi_status acpi_device_notify_fixed(void *data) { struct acpi_device *device = data; - acpi_device_notify(device->handle, ACPI_FIXED_HARDWARE_EVENT, device); + /* Fixed hardware devices have no handles */ + acpi_device_notify(NULL, ACPI_FIXED_HARDWARE_EVENT, device); return AE_OK; } static int acpi_device_install_notify_handler(struct acpi_device *device) { acpi_status status; - char *hid; - hid = acpi_device_hid(device); - if (!strcmp(hid, ACPI_BUTTON_HID_POWERF)) + if (device->device_type == ACPI_BUS_TYPE_POWER_BUTTON) status = acpi_install_fixed_event_handler(ACPI_EVENT_POWER_BUTTON, acpi_device_notify_fixed, device); - else if (!strcmp(hid, ACPI_BUTTON_HID_SLEEPF)) + else if (device->device_type == ACPI_BUS_TYPE_SLEEP_BUTTON) status = acpi_install_fixed_event_handler(ACPI_EVENT_SLEEP_BUTTON, acpi_device_notify_fixed, @@ -399,10 +367,10 @@ static int acpi_device_install_notify_handler(struct acpi_device *device) static void acpi_device_remove_notify_handler(struct acpi_device *device) { - if (!strcmp(acpi_device_hid(device), ACPI_BUTTON_HID_POWERF)) + if (device->device_type == ACPI_BUS_TYPE_POWER_BUTTON) acpi_remove_fixed_event_handler(ACPI_EVENT_POWER_BUTTON, acpi_device_notify_fixed); - else if (!strcmp(acpi_device_hid(device), ACPI_BUTTON_HID_SLEEPF)) + else if (device->device_type == ACPI_BUS_TYPE_SLEEP_BUTTON) acpi_remove_fixed_event_handler(ACPI_EVENT_SLEEP_BUTTON, acpi_device_notify_fixed); else @@ -426,9 +394,6 @@ static int acpi_device_probe(struct device * dev) if (acpi_drv->ops.notify) { ret = acpi_device_install_notify_handler(acpi_dev); if (ret) { - if (acpi_drv->ops.stop) - acpi_drv->ops.stop(acpi_dev, - acpi_dev->removal_type); if (acpi_drv->ops.remove) acpi_drv->ops.remove(acpi_dev, acpi_dev->removal_type); @@ -452,8 +417,6 @@ static int acpi_device_remove(struct device * dev) if (acpi_drv) { if (acpi_drv->ops.notify) acpi_device_remove_notify_handler(acpi_dev); - if (acpi_drv->ops.stop) - acpi_drv->ops.stop(acpi_dev, acpi_dev->removal_type); if (acpi_drv->ops.remove) acpi_drv->ops.remove(acpi_dev, acpi_dev->removal_type); } @@ -474,12 +437,12 @@ struct bus_type acpi_bus_type = { .uevent = acpi_device_uevent, }; -static int acpi_device_register(struct acpi_device *device, - struct acpi_device *parent) +static int acpi_device_register(struct acpi_device *device) { int result; struct acpi_device_bus_id *acpi_device_bus_id, *new_bus_id; int found = 0; + /* * Linkage * ------- @@ -501,8 +464,9 @@ static int acpi_device_register(struct acpi_device *device, * If failed, create one and link it into acpi_bus_id_list */ list_for_each_entry(acpi_device_bus_id, &acpi_bus_id_list, node) { - if(!strcmp(acpi_device_bus_id->bus_id, device->flags.hardware_id? device->pnp.hardware_id : "device")) { - acpi_device_bus_id->instance_no ++; + if (!strcmp(acpi_device_bus_id->bus_id, + acpi_device_hid(device))) { + acpi_device_bus_id->instance_no++; found = 1; kfree(new_bus_id); break; @@ -510,7 +474,7 @@ static int acpi_device_register(struct acpi_device *device, } if (!found) { acpi_device_bus_id = new_bus_id; - strcpy(acpi_device_bus_id->bus_id, device->flags.hardware_id ? device->pnp.hardware_id : "device"); + strcpy(acpi_device_bus_id->bus_id, acpi_device_hid(device)); acpi_device_bus_id->instance_no = 0; list_add_tail(&acpi_device_bus_id->node, &acpi_bus_id_list); } @@ -524,7 +488,7 @@ static int acpi_device_register(struct acpi_device *device, mutex_unlock(&acpi_device_lock); if (device->parent) - device->dev.parent = &parent->dev; + device->dev.parent = &device->parent->dev; device->dev.bus = &acpi_bus_type; device->dev.release = &acpi_device_release; result = device_register(&device->dev); @@ -664,6 +628,33 @@ EXPORT_SYMBOL(acpi_bus_unregister_driver); /* -------------------------------------------------------------------------- Device Enumeration -------------------------------------------------------------------------- */ +static struct acpi_device *acpi_bus_get_parent(acpi_handle handle) +{ + acpi_status status; + int ret; + struct acpi_device *device; + + /* + * Fixed hardware devices do not appear in the namespace and do not + * have handles, but we fabricate acpi_devices for them, so we have + * to deal with them specially. + */ + if (handle == NULL) + return acpi_root; + + do { + status = acpi_get_parent(handle, &handle); + if (status == AE_NULL_ENTRY) + return NULL; + if (ACPI_FAILURE(status)) + return acpi_root; + + ret = acpi_bus_get_device(handle, &device); + if (ret == 0) + return device; + } while (1); +} + acpi_status acpi_bus_get_ejd(acpi_handle handle, acpi_handle *ejd) { @@ -687,7 +678,7 @@ acpi_bus_get_ejd(acpi_handle handle, acpi_handle *ejd) } EXPORT_SYMBOL_GPL(acpi_bus_get_ejd); -void acpi_bus_data_handler(acpi_handle handle, u32 function, void *context) +void acpi_bus_data_handler(acpi_handle handle, void *context) { /* TBD */ @@ -876,11 +867,6 @@ static int acpi_bus_get_flags(struct acpi_device *device) if (ACPI_SUCCESS(status)) device->flags.dynamic_status = 1; - /* Presence of _CID indicates 'compatible_ids' */ - status = acpi_get_handle(device->handle, "_CID", &temp); - if (ACPI_SUCCESS(status)) - device->flags.compatible_ids = 1; - /* Presence of _RMV indicates 'removable' */ status = acpi_get_handle(device->handle, "_RMV", &temp); if (ACPI_SUCCESS(status)) @@ -918,8 +904,7 @@ static int acpi_bus_get_flags(struct acpi_device *device) return 0; } -static void acpi_device_get_busid(struct acpi_device *device, - acpi_handle handle, int type) +static void acpi_device_get_busid(struct acpi_device *device) { char bus_id[5] = { '?', 0 }; struct acpi_buffer buffer = { sizeof(bus_id), bus_id }; @@ -931,10 +916,12 @@ static void acpi_device_get_busid(struct acpi_device *device, * The device's Bus ID is simply the object name. * TBD: Shouldn't this value be unique (within the ACPI namespace)? */ - switch (type) { - case ACPI_BUS_TYPE_SYSTEM: + if (ACPI_IS_ROOT_DEVICE(device)) { strcpy(device->pnp.bus_id, "ACPI"); - break; + return; + } + + switch (device->device_type) { case ACPI_BUS_TYPE_POWER_BUTTON: strcpy(device->pnp.bus_id, "PWRF"); break; @@ -942,7 +929,7 @@ static void acpi_device_get_busid(struct acpi_device *device, strcpy(device->pnp.bus_id, "SLPF"); break; default: - acpi_get_name(handle, ACPI_SINGLE_NAME, &buffer); + acpi_get_name(device->handle, ACPI_SINGLE_NAME, &buffer); /* Clean up trailing underscores (if any) */ for (i = 3; i > 1; i--) { if (bus_id[i] == '_') @@ -1000,157 +987,134 @@ static int acpi_dock_match(struct acpi_device *device) return acpi_get_handle(device->handle, "_DCK", &tmp); } -static void acpi_device_set_id(struct acpi_device *device, - struct acpi_device *parent, acpi_handle handle, - int type) +char *acpi_device_hid(struct acpi_device *device) +{ + struct acpi_hardware_id *hid; + + hid = list_first_entry(&device->pnp.ids, struct acpi_hardware_id, list); + return hid->id; +} +EXPORT_SYMBOL(acpi_device_hid); + +static void acpi_add_id(struct acpi_device *device, const char *dev_id) +{ + struct acpi_hardware_id *id; + + id = kmalloc(sizeof(*id), GFP_KERNEL); + if (!id) + return; + + id->id = kmalloc(strlen(dev_id) + 1, GFP_KERNEL); + if (!id->id) { + kfree(id); + return; + } + + strcpy(id->id, dev_id); + list_add_tail(&id->list, &device->pnp.ids); +} + +static void acpi_device_set_id(struct acpi_device *device) { - struct acpi_device_info *info; - struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; - char *hid = NULL; - char *uid = NULL; - struct acpi_compatible_id_list *cid_list = NULL; - const char *cid_add = NULL; acpi_status status; + struct acpi_device_info *info; + struct acpica_device_id_list *cid_list; + int i; - switch (type) { + switch (device->device_type) { case ACPI_BUS_TYPE_DEVICE: - status = acpi_get_object_info(handle, &buffer); + if (ACPI_IS_ROOT_DEVICE(device)) { + acpi_add_id(device, ACPI_SYSTEM_HID); + break; + } else if (ACPI_IS_ROOT_DEVICE(device->parent)) { + /* \_SB_, the only root-level namespace device */ + acpi_add_id(device, ACPI_BUS_HID); + strcpy(device->pnp.device_name, ACPI_BUS_DEVICE_NAME); + strcpy(device->pnp.device_class, ACPI_BUS_CLASS); + break; + } + + status = acpi_get_object_info(device->handle, &info); if (ACPI_FAILURE(status)) { printk(KERN_ERR PREFIX "%s: Error reading device info\n", __func__); return; } - info = buffer.pointer; if (info->valid & ACPI_VALID_HID) - hid = info->hardware_id.value; - if (info->valid & ACPI_VALID_UID) - uid = info->unique_id.value; - if (info->valid & ACPI_VALID_CID) - cid_list = &info->compatibility_id; + acpi_add_id(device, info->hardware_id.string); + if (info->valid & ACPI_VALID_CID) { + cid_list = &info->compatible_id_list; + for (i = 0; i < cid_list->count; i++) + acpi_add_id(device, cid_list->ids[i].string); + } if (info->valid & ACPI_VALID_ADR) { device->pnp.bus_address = info->address; device->flags.bus_address = 1; } - /* If we have a video/bay/dock device, add our selfdefined - HID to the CID list. Like that the video/bay/dock drivers - will get autoloaded and the device might still match - against another driver. - */ + kfree(info); + + /* + * Some devices don't reliably have _HIDs & _CIDs, so add + * synthetic HIDs to make sure drivers can find them. + */ if (acpi_is_video_device(device)) - cid_add = ACPI_VIDEO_HID; + acpi_add_id(device, ACPI_VIDEO_HID); else if (ACPI_SUCCESS(acpi_bay_match(device))) - cid_add = ACPI_BAY_HID; + acpi_add_id(device, ACPI_BAY_HID); else if (ACPI_SUCCESS(acpi_dock_match(device))) - cid_add = ACPI_DOCK_HID; + acpi_add_id(device, ACPI_DOCK_HID); break; case ACPI_BUS_TYPE_POWER: - hid = ACPI_POWER_HID; + acpi_add_id(device, ACPI_POWER_HID); break; case ACPI_BUS_TYPE_PROCESSOR: - hid = ACPI_PROCESSOR_OBJECT_HID; - break; - case ACPI_BUS_TYPE_SYSTEM: - hid = ACPI_SYSTEM_HID; + acpi_add_id(device, ACPI_PROCESSOR_OBJECT_HID); break; case ACPI_BUS_TYPE_THERMAL: - hid = ACPI_THERMAL_HID; + acpi_add_id(device, ACPI_THERMAL_HID); break; case ACPI_BUS_TYPE_POWER_BUTTON: - hid = ACPI_BUTTON_HID_POWERF; + acpi_add_id(device, ACPI_BUTTON_HID_POWERF); break; case ACPI_BUS_TYPE_SLEEP_BUTTON: - hid = ACPI_BUTTON_HID_SLEEPF; + acpi_add_id(device, ACPI_BUTTON_HID_SLEEPF); break; } /* - * \_SB - * ---- - * Fix for the system root bus device -- the only root-level device. + * We build acpi_devices for some objects that don't have _HID or _CID, + * e.g., PCI bridges and slots. Drivers can't bind to these objects, + * but we do use them indirectly by traversing the acpi_device tree. + * This generic ID isn't useful for driver binding, but it provides + * the useful property that "every acpi_device has an ID." */ - if (((acpi_handle)parent == ACPI_ROOT_OBJECT) && (type == ACPI_BUS_TYPE_DEVICE)) { - hid = ACPI_BUS_HID; - strcpy(device->pnp.device_name, ACPI_BUS_DEVICE_NAME); - strcpy(device->pnp.device_class, ACPI_BUS_CLASS); - } - - if (hid) { - strcpy(device->pnp.hardware_id, hid); - device->flags.hardware_id = 1; - } - if (uid) { - strcpy(device->pnp.unique_id, uid); - device->flags.unique_id = 1; - } - if (cid_list || cid_add) { - struct acpi_compatible_id_list *list; - int size = 0; - int count = 0; - - if (cid_list) { - size = cid_list->size; - } else if (cid_add) { - size = sizeof(struct acpi_compatible_id_list); - cid_list = ACPI_ALLOCATE_ZEROED((acpi_size) size); - if (!cid_list) { - printk(KERN_ERR "Memory allocation error\n"); - kfree(buffer.pointer); - return; - } else { - cid_list->count = 0; - cid_list->size = size; - } - } - if (cid_add) - size += sizeof(struct acpi_compatible_id); - list = kmalloc(size, GFP_KERNEL); - - if (list) { - if (cid_list) { - memcpy(list, cid_list, cid_list->size); - count = cid_list->count; - } - if (cid_add) { - strncpy(list->id[count].value, cid_add, - ACPI_MAX_CID_LENGTH); - count++; - device->flags.compatible_ids = 1; - } - list->size = size; - list->count = count; - device->pnp.cid_list = list; - } else - printk(KERN_ERR PREFIX "Memory allocation error\n"); - } - - kfree(buffer.pointer); + if (list_empty(&device->pnp.ids)) + acpi_add_id(device, "device"); } -static int acpi_device_set_context(struct acpi_device *device, int type) +static int acpi_device_set_context(struct acpi_device *device) { - acpi_status status = AE_OK; - int result = 0; + acpi_status status; + /* * Context * ------- * Attach this 'struct acpi_device' to the ACPI object. This makes - * resolutions from handle->device very efficient. Note that we need - * to be careful with fixed-feature devices as they all attach to the - * root object. + * resolutions from handle->device very efficient. Fixed hardware + * devices have no handles, so we skip them. */ - if (type != ACPI_BUS_TYPE_POWER_BUTTON && - type != ACPI_BUS_TYPE_SLEEP_BUTTON) { - status = acpi_attach_data(device->handle, - acpi_bus_data_handler, device); + if (!device->handle) + return 0; - if (ACPI_FAILURE(status)) { - printk(KERN_ERR PREFIX "Error attaching device data\n"); - result = -ENODEV; - } - } - return result; + status = acpi_attach_data(device->handle, + acpi_bus_data_handler, device); + if (ACPI_SUCCESS(status)) + return 0; + + printk(KERN_ERR PREFIX "Error attaching device data\n"); + return -ENODEV; } static int acpi_bus_remove(struct acpi_device *dev, int rmdevice) @@ -1176,17 +1140,14 @@ static int acpi_bus_remove(struct acpi_device *dev, int rmdevice) return 0; } -static int -acpi_add_single_object(struct acpi_device **child, - struct acpi_device *parent, acpi_handle handle, int type, - struct acpi_bus_ops *ops) +static int acpi_add_single_object(struct acpi_device **child, + acpi_handle handle, int type, + unsigned long long sta, + struct acpi_bus_ops *ops) { - int result = 0; - struct acpi_device *device = NULL; - - - if (!child) - return -EINVAL; + int result; + struct acpi_device *device; + struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; device = kzalloc(sizeof(struct acpi_device), GFP_KERNEL); if (!device) { @@ -1194,85 +1155,31 @@ acpi_add_single_object(struct acpi_device **child, return -ENOMEM; } + INIT_LIST_HEAD(&device->pnp.ids); + device->device_type = type; device->handle = handle; - device->parent = parent; + device->parent = acpi_bus_get_parent(handle); device->bus_ops = *ops; /* workround for not call .start */ + STRUCT_TO_INT(device->status) = sta; - - acpi_device_get_busid(device, handle, type); + acpi_device_get_busid(device); /* * Flags * ----- - * Get prior to calling acpi_bus_get_status() so we know whether - * or not _STA is present. Note that we only look for object - * handles -- cannot evaluate objects until we know the device is - * present and properly initialized. + * Note that we only look for object handles -- cannot evaluate objects + * until we know the device is present and properly initialized. */ result = acpi_bus_get_flags(device); if (result) goto end; /* - * Status - * ------ - * See if the device is present. We always assume that non-Device - * and non-Processor objects (e.g. thermal zones, power resources, - * etc.) are present, functioning, etc. (at least when parent object - * is present). Note that _STA has a different meaning for some - * objects (e.g. power resources) so we need to be careful how we use - * it. - */ - switch (type) { - case ACPI_BUS_TYPE_PROCESSOR: - case ACPI_BUS_TYPE_DEVICE: - result = acpi_bus_get_status(device); - if (ACPI_FAILURE(result)) { - result = -ENODEV; - goto end; - } - /* - * When the device is neither present nor functional, the - * device should not be added to Linux ACPI device tree. - * When the status of the device is not present but functinal, - * it should be added to Linux ACPI tree. For example : bay - * device , dock device. - * In such conditions it is unncessary to check whether it is - * bay device or dock device. - */ - if (!device->status.present && !device->status.functional) { - result = -ENODEV; - goto end; - } - break; - default: - STRUCT_TO_INT(device->status) = - ACPI_STA_DEVICE_PRESENT | ACPI_STA_DEVICE_ENABLED | - ACPI_STA_DEVICE_UI | ACPI_STA_DEVICE_FUNCTIONING; - break; - } - - /* * Initialize Device * ----------------- * TBD: Synch with Core's enumeration/initialization process. */ - - /* - * Hardware ID, Unique ID, & Bus Address - * ------------------------------------- - */ - acpi_device_set_id(device, parent, handle, type); - - /* - * The ACPI device is attached to acpi handle before getting - * the power/wakeup/peformance flags. Otherwise OS can't get - * the corresponding ACPI device by the acpi handle in the course - * of getting the power/wakeup/performance flags. - */ - result = acpi_device_set_context(device, type); - if (result) - goto end; + acpi_device_set_id(device); /* * Power Management @@ -1304,8 +1211,10 @@ acpi_add_single_object(struct acpi_device **child, goto end; } + if ((result = acpi_device_set_context(device))) + goto end; - result = acpi_device_register(device, parent); + result = acpi_device_register(device); /* * Bind _ADR-Based Devices when hot add @@ -1316,130 +1225,117 @@ acpi_add_single_object(struct acpi_device **child, } end: - if (!result) + if (!result) { + acpi_get_name(handle, ACPI_FULL_PATHNAME, &buffer); + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "Adding %s [%s] parent %s\n", dev_name(&device->dev), + (char *) buffer.pointer, + device->parent ? dev_name(&device->parent->dev) : + "(null)")); + kfree(buffer.pointer); *child = device; - else { - kfree(device->pnp.cid_list); - kfree(device); - } + } else + acpi_device_release(&device->dev); return result; } -static int acpi_bus_scan(struct acpi_device *start, struct acpi_bus_ops *ops) -{ - acpi_status status = AE_OK; - struct acpi_device *parent = NULL; - struct acpi_device *child = NULL; - acpi_handle phandle = NULL; - acpi_handle chandle = NULL; - acpi_object_type type = 0; - u32 level = 1; +#define ACPI_STA_DEFAULT (ACPI_STA_DEVICE_PRESENT | ACPI_STA_DEVICE_ENABLED | \ + ACPI_STA_DEVICE_UI | ACPI_STA_DEVICE_FUNCTIONING) +static int acpi_bus_type_and_status(acpi_handle handle, int *type, + unsigned long long *sta) +{ + acpi_status status; + acpi_object_type acpi_type; - if (!start) - return -EINVAL; - - parent = start; - phandle = start->handle; + status = acpi_get_type(handle, &acpi_type); + if (ACPI_FAILURE(status)) + return -ENODEV; - /* - * Parse through the ACPI namespace, identify all 'devices', and - * create a new 'struct acpi_device' for each. - */ - while ((level > 0) && parent) { + switch (acpi_type) { + case ACPI_TYPE_ANY: /* for ACPI_ROOT_OBJECT */ + case ACPI_TYPE_DEVICE: + *type = ACPI_BUS_TYPE_DEVICE; + status = acpi_bus_get_status_handle(handle, sta); + if (ACPI_FAILURE(status)) + return -ENODEV; + break; + case ACPI_TYPE_PROCESSOR: + *type = ACPI_BUS_TYPE_PROCESSOR; + status = acpi_bus_get_status_handle(handle, sta); + if (ACPI_FAILURE(status)) + return -ENODEV; + break; + case ACPI_TYPE_THERMAL: + *type = ACPI_BUS_TYPE_THERMAL; + *sta = ACPI_STA_DEFAULT; + break; + case ACPI_TYPE_POWER: + *type = ACPI_BUS_TYPE_POWER; + *sta = ACPI_STA_DEFAULT; + break; + default: + return -ENODEV; + } - status = acpi_get_next_object(ACPI_TYPE_ANY, phandle, - chandle, &chandle); + return 0; +} - /* - * If this scope is exhausted then move our way back up. - */ - if (ACPI_FAILURE(status)) { - level--; - chandle = phandle; - acpi_get_parent(phandle, &phandle); - if (parent->parent) - parent = parent->parent; - continue; - } +static acpi_status acpi_bus_check_add(acpi_handle handle, u32 lvl, + void *context, void **return_value) +{ + struct acpi_bus_ops *ops = context; + int type; + unsigned long long sta; + struct acpi_device *device; + acpi_status status; + int result; - status = acpi_get_type(chandle, &type); - if (ACPI_FAILURE(status)) - continue; + result = acpi_bus_type_and_status(handle, &type, &sta); + if (result) + return AE_OK; - /* - * If this is a scope object then parse it (depth-first). - */ - if (type == ACPI_TYPE_LOCAL_SCOPE) { - level++; - phandle = chandle; - chandle = NULL; - continue; - } + if (!(sta & ACPI_STA_DEVICE_PRESENT) && + !(sta & ACPI_STA_DEVICE_FUNCTIONING)) + return AE_CTRL_DEPTH; - /* - * We're only interested in objects that we consider 'devices'. - */ - switch (type) { - case ACPI_TYPE_DEVICE: - type = ACPI_BUS_TYPE_DEVICE; - break; - case ACPI_TYPE_PROCESSOR: - type = ACPI_BUS_TYPE_PROCESSOR; - break; - case ACPI_TYPE_THERMAL: - type = ACPI_BUS_TYPE_THERMAL; - break; - case ACPI_TYPE_POWER: - type = ACPI_BUS_TYPE_POWER; - break; - default: - continue; - } + /* + * We may already have an acpi_device from a previous enumeration. If + * so, we needn't add it again, but we may still have to start it. + */ + device = NULL; + acpi_bus_get_device(handle, &device); + if (ops->acpi_op_add && !device) + acpi_add_single_object(&device, handle, type, sta, ops); - if (ops->acpi_op_add) - status = acpi_add_single_object(&child, parent, - chandle, type, ops); - else - status = acpi_bus_get_device(chandle, &child); + if (!device) + return AE_CTRL_DEPTH; + if (ops->acpi_op_start && !(ops->acpi_op_add)) { + status = acpi_start_single_object(device); if (ACPI_FAILURE(status)) - continue; + return AE_CTRL_DEPTH; + } - if (ops->acpi_op_start && !(ops->acpi_op_add)) { - status = acpi_start_single_object(child); - if (ACPI_FAILURE(status)) - continue; - } + if (!*return_value) + *return_value = device; + return AE_OK; +} - /* - * If the device is present, enabled, and functioning then - * parse its scope (depth-first). Note that we need to - * represent absent devices to facilitate PnP notifications - * -- but only the subtree head (not all of its children, - * which will be enumerated when the parent is inserted). - * - * TBD: Need notifications and other detection mechanisms - * in place before we can fully implement this. - */ - /* - * When the device is not present but functional, it is also - * necessary to scan the children of this device. - */ - if (child->status.present || (!child->status.present && - child->status.functional)) { - status = acpi_get_next_object(ACPI_TYPE_ANY, chandle, - NULL, NULL); - if (ACPI_SUCCESS(status)) { - level++; - phandle = chandle; - chandle = NULL; - parent = child; - } - } - } +static int acpi_bus_scan(acpi_handle handle, struct acpi_bus_ops *ops, + struct acpi_device **child) +{ + acpi_status status; + void *device = NULL; + + status = acpi_bus_check_add(handle, 0, ops, &device); + if (ACPI_SUCCESS(status)) + acpi_walk_namespace(ACPI_TYPE_ANY, handle, ACPI_UINT32_MAX, + acpi_bus_check_add, ops, &device); + if (child) + *child = device; return 0; } @@ -1447,36 +1343,25 @@ int acpi_bus_add(struct acpi_device **child, struct acpi_device *parent, acpi_handle handle, int type) { - int result; struct acpi_bus_ops ops; memset(&ops, 0, sizeof(ops)); ops.acpi_op_add = 1; - result = acpi_add_single_object(child, parent, handle, type, &ops); - if (!result) - result = acpi_bus_scan(*child, &ops); - - return result; + acpi_bus_scan(handle, &ops, child); + return 0; } EXPORT_SYMBOL(acpi_bus_add); int acpi_bus_start(struct acpi_device *device) { - int result; struct acpi_bus_ops ops; + memset(&ops, 0, sizeof(ops)); + ops.acpi_op_start = 1; - if (!device) - return -EINVAL; - - result = acpi_start_single_object(device); - if (!result) { - memset(&ops, 0, sizeof(ops)); - ops.acpi_op_start = 1; - result = acpi_bus_scan(device, &ops); - } - return result; + acpi_bus_scan(device->handle, &ops, NULL); + return 0; } EXPORT_SYMBOL(acpi_bus_start); @@ -1535,15 +1420,12 @@ int acpi_bus_trim(struct acpi_device *start, int rmdevice) } EXPORT_SYMBOL_GPL(acpi_bus_trim); -static int acpi_bus_scan_fixed(struct acpi_device *root) +static int acpi_bus_scan_fixed(void) { int result = 0; struct acpi_device *device = NULL; struct acpi_bus_ops ops; - if (!root) - return -ENODEV; - memset(&ops, 0, sizeof(ops)); ops.acpi_op_add = 1; ops.acpi_op_start = 1; @@ -1552,16 +1434,16 @@ static int acpi_bus_scan_fixed(struct acpi_device *root) * Enumerate all fixed-feature devices. */ if ((acpi_gbl_FADT.flags & ACPI_FADT_POWER_BUTTON) == 0) { - result = acpi_add_single_object(&device, acpi_root, - NULL, + result = acpi_add_single_object(&device, NULL, ACPI_BUS_TYPE_POWER_BUTTON, + ACPI_STA_DEFAULT, &ops); } if ((acpi_gbl_FADT.flags & ACPI_FADT_SLEEP_BUTTON) == 0) { - result = acpi_add_single_object(&device, acpi_root, - NULL, + result = acpi_add_single_object(&device, NULL, ACPI_BUS_TYPE_SLEEP_BUTTON, + ACPI_STA_DEFAULT, &ops); } @@ -1584,24 +1466,15 @@ int __init acpi_scan_init(void) } /* - * Create the root device in the bus's device tree - */ - result = acpi_add_single_object(&acpi_root, NULL, ACPI_ROOT_OBJECT, - ACPI_BUS_TYPE_SYSTEM, &ops); - if (result) - goto Done; - - /* * Enumerate devices in the ACPI namespace. */ - result = acpi_bus_scan_fixed(acpi_root); + result = acpi_bus_scan(ACPI_ROOT_OBJECT, &ops, &acpi_root); if (!result) - result = acpi_bus_scan(acpi_root, &ops); + result = acpi_bus_scan_fixed(); if (result) acpi_device_unregister(acpi_root, ACPI_BUS_REMOVAL_NORMAL); -Done: return result; } diff --git a/drivers/acpi/sleep.c b/drivers/acpi/sleep.c index feece693d773..5f2c379ab7bf 100644 --- a/drivers/acpi/sleep.c +++ b/drivers/acpi/sleep.c @@ -405,6 +405,46 @@ static struct dmi_system_id __initdata acpisleep_dmi_table[] = { }, }, { + .callback = init_set_sci_en_on_resume, + .ident = "Hewlett-Packard HP Pavilion dv3 Notebook PC", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"), + DMI_MATCH(DMI_PRODUCT_NAME, "HP Pavilion dv3 Notebook PC"), + }, + }, + { + .callback = init_set_sci_en_on_resume, + .ident = "Hewlett-Packard Pavilion dv4", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"), + DMI_MATCH(DMI_PRODUCT_NAME, "HP Pavilion dv4"), + }, + }, + { + .callback = init_set_sci_en_on_resume, + .ident = "Hewlett-Packard Pavilion dv7", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"), + DMI_MATCH(DMI_PRODUCT_NAME, "HP Pavilion dv7"), + }, + }, + { + .callback = init_set_sci_en_on_resume, + .ident = "Hewlett-Packard Compaq Presario C700 Notebook PC", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"), + DMI_MATCH(DMI_PRODUCT_NAME, "Compaq Presario C700 Notebook PC"), + }, + }, + { + .callback = init_set_sci_en_on_resume, + .ident = "Hewlett-Packard Compaq Presario CQ40 Notebook PC", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"), + DMI_MATCH(DMI_PRODUCT_NAME, "Compaq Presario CQ40 Notebook PC"), + }, + }, + { .callback = init_old_suspend_ordering, .ident = "Panasonic CF51-2L", .matches = { diff --git a/drivers/acpi/system.c b/drivers/acpi/system.c index 9c61ab2177cf..d11282975f35 100644 --- a/drivers/acpi/system.c +++ b/drivers/acpi/system.c @@ -31,6 +31,8 @@ #include <acpi/acpi_drivers.h> +#define PREFIX "ACPI: " + #define _COMPONENT ACPI_SYSTEM_COMPONENT ACPI_MODULE_NAME("system"); diff --git a/drivers/acpi/tables.c b/drivers/acpi/tables.c index 646d39c031ca..f336bca7c450 100644 --- a/drivers/acpi/tables.c +++ b/drivers/acpi/tables.c @@ -213,6 +213,9 @@ acpi_table_parse_entries(char *id, unsigned long table_end; acpi_size tbl_size; + if (acpi_disabled) + return -ENODEV; + if (!handler) return -EINVAL; @@ -277,6 +280,9 @@ int __init acpi_table_parse(char *id, acpi_table_handler handler) struct acpi_table_header *table = NULL; acpi_size tbl_size; + if (acpi_disabled) + return -ENODEV; + if (!handler) return -EINVAL; diff --git a/drivers/acpi/thermal.c b/drivers/acpi/thermal.c index 564ea1424288..65f67815902a 100644 --- a/drivers/acpi/thermal.c +++ b/drivers/acpi/thermal.c @@ -47,6 +47,8 @@ #include <acpi/acpi_bus.h> #include <acpi/acpi_drivers.h> +#define PREFIX "ACPI: " + #define ACPI_THERMAL_CLASS "thermal_zone" #define ACPI_THERMAL_DEVICE_NAME "Thermal Zone" #define ACPI_THERMAL_FILE_STATE "state" diff --git a/drivers/acpi/utils.c b/drivers/acpi/utils.c index f844941089bb..811fec10462b 100644 --- a/drivers/acpi/utils.c +++ b/drivers/acpi/utils.c @@ -30,6 +30,8 @@ #include <acpi/acpi_bus.h> #include <acpi/acpi_drivers.h> +#include "internal.h" + #define _COMPONENT ACPI_BUS_COMPONENT ACPI_MODULE_NAME("utils"); diff --git a/drivers/acpi/video.c b/drivers/acpi/video.c index 60ea984c84a0..05dff631591c 100644 --- a/drivers/acpi/video.c +++ b/drivers/acpi/video.c @@ -40,10 +40,12 @@ #include <linux/pci.h> #include <linux/pci_ids.h> #include <asm/uaccess.h> - +#include <linux/dmi.h> #include <acpi/acpi_bus.h> #include <acpi/acpi_drivers.h> +#define PREFIX "ACPI: " + #define ACPI_VIDEO_CLASS "video" #define ACPI_VIDEO_BUS_NAME "Video Bus" #define ACPI_VIDEO_DEVICE_NAME "Video Device" @@ -198,7 +200,7 @@ struct acpi_video_device { struct acpi_device *dev; struct acpi_video_device_brightness *brightness; struct backlight_device *backlight; - struct thermal_cooling_device *cdev; + struct thermal_cooling_device *cooling_dev; struct output_device *output_dev; }; @@ -283,7 +285,7 @@ static int acpi_video_device_brightness_open_fs(struct inode *inode, struct file *file); static ssize_t acpi_video_device_write_brightness(struct file *file, const char __user *buffer, size_t count, loff_t *data); -static struct file_operations acpi_video_device_brightness_fops = { +static const struct file_operations acpi_video_device_brightness_fops = { .owner = THIS_MODULE, .open = acpi_video_device_brightness_open_fs, .read = seq_read, @@ -387,20 +389,20 @@ static struct output_properties acpi_output_properties = { /* thermal cooling device callbacks */ -static int video_get_max_state(struct thermal_cooling_device *cdev, unsigned +static int video_get_max_state(struct thermal_cooling_device *cooling_dev, unsigned long *state) { - struct acpi_device *device = cdev->devdata; + struct acpi_device *device = cooling_dev->devdata; struct acpi_video_device *video = acpi_driver_data(device); *state = video->brightness->count - 3; return 0; } -static int video_get_cur_state(struct thermal_cooling_device *cdev, unsigned +static int video_get_cur_state(struct thermal_cooling_device *cooling_dev, unsigned long *state) { - struct acpi_device *device = cdev->devdata; + struct acpi_device *device = cooling_dev->devdata; struct acpi_video_device *video = acpi_driver_data(device); unsigned long long level; int offset; @@ -417,9 +419,9 @@ static int video_get_cur_state(struct thermal_cooling_device *cdev, unsigned } static int -video_set_cur_state(struct thermal_cooling_device *cdev, unsigned long state) +video_set_cur_state(struct thermal_cooling_device *cooling_dev, unsigned long state) { - struct acpi_device *device = cdev->devdata; + struct acpi_device *device = cooling_dev->devdata; struct acpi_video_device *video = acpi_driver_data(device); int level; @@ -603,6 +605,7 @@ acpi_video_device_lcd_get_level_current(struct acpi_video_device *device, unsigned long long *level) { acpi_status status = AE_OK; + int i; if (device->cap._BQC || device->cap._BCQ) { char *buf = device->cap._BQC ? "_BQC" : "_BCQ"; @@ -618,8 +621,15 @@ acpi_video_device_lcd_get_level_current(struct acpi_video_device *device, } *level += bqc_offset_aml_bug_workaround; - device->brightness->curr = *level; - return 0; + for (i = 2; i < device->brightness->count; i++) + if (device->brightness->levels[i] == *level) { + device->brightness->curr = *level; + return 0; + } + /* BQC returned an invalid level. Stop using it. */ + ACPI_WARNING((AE_INFO, "%s returned an invalid level", + buf)); + device->cap._BQC = device->cap._BCQ = 0; } else { /* Fixme: * should we return an error or ignore this failure? @@ -870,7 +880,7 @@ acpi_video_init_brightness(struct acpi_video_device *device) br->flags._BCM_use_index = br->flags._BCL_use_index; /* _BQC uses INDEX while _BCL uses VALUE in some laptops */ - br->curr = level_old = max_level; + br->curr = level = max_level; if (!device->cap._BQC) goto set_level; @@ -892,15 +902,25 @@ acpi_video_init_brightness(struct acpi_video_device *device) br->flags._BQC_use_index = (level == max_level ? 0 : 1); - if (!br->flags._BQC_use_index) + if (!br->flags._BQC_use_index) { + /* + * Set the backlight to the initial state. + * On some buggy laptops, _BQC returns an uninitialized value + * when invoked for the first time, i.e. level_old is invalid. + * set the backlight to max_level in this case + */ + for (i = 2; i < br->count; i++) + if (level_old == br->levels[i]) + level = level_old; goto set_level; + } if (br->flags._BCL_reversed) level_old = (br->count - 1) - level_old; - level_old = br->levels[level_old]; + level = br->levels[level_old]; set_level: - result = acpi_video_device_lcd_set_level(device, level_old); + result = acpi_video_device_lcd_set_level(device, level); if (result) goto out_free_levels; @@ -934,9 +954,6 @@ static void acpi_video_device_find_cap(struct acpi_video_device *device) { acpi_handle h_dummy1; - - memset(&device->cap, 0, sizeof(device->cap)); - if (ACPI_SUCCESS(acpi_get_handle(device->dev->handle, "_ADR", &h_dummy1))) { device->cap._ADR = 1; } @@ -990,19 +1007,29 @@ static void acpi_video_device_find_cap(struct acpi_video_device *device) if (result) printk(KERN_ERR PREFIX "Create sysfs link\n"); - device->cdev = thermal_cooling_device_register("LCD", + device->cooling_dev = thermal_cooling_device_register("LCD", device->dev, &video_cooling_ops); - if (IS_ERR(device->cdev)) + if (IS_ERR(device->cooling_dev)) { + /* + * Set cooling_dev to NULL so we don't crash trying to + * free it. + * Also, why the hell we are returning early and + * not attempt to register video output if cooling + * device registration failed? + * -- dtor + */ + device->cooling_dev = NULL; return; + } dev_info(&device->dev->dev, "registered as cooling_device%d\n", - device->cdev->id); + device->cooling_dev->id); result = sysfs_create_link(&device->dev->dev.kobj, - &device->cdev->device.kobj, + &device->cooling_dev->device.kobj, "thermal_cooling"); if (result) printk(KERN_ERR PREFIX "Create sysfs link\n"); - result = sysfs_create_link(&device->cdev->device.kobj, + result = sysfs_create_link(&device->cooling_dev->device.kobj, &device->dev->dev.kobj, "device"); if (result) printk(KERN_ERR PREFIX "Create sysfs link\n"); @@ -1039,7 +1066,6 @@ static void acpi_video_bus_find_cap(struct acpi_video_bus *video) { acpi_handle h_dummy1; - memset(&video->cap, 0, sizeof(video->cap)); if (ACPI_SUCCESS(acpi_get_handle(video->device->handle, "_DOS", &h_dummy1))) { video->cap._DOS = 1; } @@ -1083,7 +1109,12 @@ static int acpi_video_bus_check(struct acpi_video_bus *video) */ /* Does this device support video switching? */ - if (video->cap._DOS) { + if (video->cap._DOS || video->cap._DOD) { + if (!video->cap._DOS) { + printk(KERN_WARNING FW_BUG + "ACPI(%s) defines _DOD but not _DOS\n", + acpi_device_bid(video->device)); + } video->flags.multihead = 1; status = 0; } @@ -1192,7 +1223,7 @@ acpi_video_device_write_state(struct file *file, u32 state = 0; - if (!dev || count + 1 > sizeof str) + if (!dev || count >= sizeof(str)) return -EINVAL; if (copy_from_user(str, buffer, count)) @@ -1249,7 +1280,7 @@ acpi_video_device_write_brightness(struct file *file, int i; - if (!dev || !dev->brightness || count + 1 > sizeof str) + if (!dev || !dev->brightness || count >= sizeof(str)) return -EINVAL; if (copy_from_user(str, buffer, count)) @@ -1531,7 +1562,7 @@ acpi_video_bus_write_POST(struct file *file, unsigned long long opt, options; - if (!video || count + 1 > sizeof str) + if (!video || count >= sizeof(str)) return -EINVAL; status = acpi_video_bus_POST_options(video, &options); @@ -1571,7 +1602,7 @@ acpi_video_bus_write_DOS(struct file *file, unsigned long opt; - if (!video || count + 1 > sizeof str) + if (!video || count >= sizeof(str)) return -EINVAL; if (copy_from_user(str, buffer, count)) @@ -1960,6 +1991,10 @@ acpi_video_switch_brightness(struct acpi_video_device *device, int event) result = acpi_video_device_lcd_set_level(device, level_next); + if (!result) + backlight_force_update(device->backlight, + BACKLIGHT_UPDATE_HOTKEY); + out: if (result) printk(KERN_ERR PREFIX "Failed to switch the brightness\n"); @@ -2009,13 +2044,13 @@ static int acpi_video_bus_put_one_device(struct acpi_video_device *device) backlight_device_unregister(device->backlight); device->backlight = NULL; } - if (device->cdev) { + if (device->cooling_dev) { sysfs_remove_link(&device->dev->dev.kobj, "thermal_cooling"); - sysfs_remove_link(&device->cdev->device.kobj, + sysfs_remove_link(&device->cooling_dev->device.kobj, "device"); - thermal_cooling_device_unregister(device->cdev); - device->cdev = NULL; + thermal_cooling_device_unregister(device->cooling_dev); + device->cooling_dev = NULL; } video_output_unregister(device->output_dev); diff --git a/drivers/acpi/video_detect.c b/drivers/acpi/video_detect.c index 7cd2b63435ea..575593a8b4e6 100644 --- a/drivers/acpi/video_detect.c +++ b/drivers/acpi/video_detect.c @@ -38,6 +38,8 @@ #include <linux/dmi.h> #include <linux/pci.h> +#define PREFIX "ACPI: " + ACPI_MODULE_NAME("video"); #define _COMPONENT ACPI_VIDEO_COMPONENT @@ -82,7 +84,7 @@ long acpi_is_video_device(struct acpi_device *device) return 0; /* Does this device able to support video switching ? */ - if (ACPI_SUCCESS(acpi_get_handle(device->handle, "_DOD", &h_dummy)) && + if (ACPI_SUCCESS(acpi_get_handle(device->handle, "_DOD", &h_dummy)) || ACPI_SUCCESS(acpi_get_handle(device->handle, "_DOS", &h_dummy))) video_caps |= ACPI_VIDEO_OUTPUT_SWITCHING; diff --git a/drivers/ata/ahci.c b/drivers/ata/ahci.c index acd1162712b1..a3241a1a710b 100644 --- a/drivers/ata/ahci.c +++ b/drivers/ata/ahci.c @@ -122,6 +122,7 @@ enum { HOST_VERSION = 0x10, /* AHCI spec. version compliancy */ HOST_EM_LOC = 0x1c, /* Enclosure Management location */ HOST_EM_CTL = 0x20, /* Enclosure Management Control */ + HOST_CAP2 = 0x24, /* host capabilities, extended */ /* HOST_CTL bits */ HOST_RESET = (1 << 0), /* reset controller; self-clear */ @@ -129,16 +130,29 @@ enum { HOST_AHCI_EN = (1 << 31), /* AHCI enabled */ /* HOST_CAP bits */ + HOST_CAP_SXS = (1 << 5), /* Supports External SATA */ HOST_CAP_EMS = (1 << 6), /* Enclosure Management support */ - HOST_CAP_SSC = (1 << 14), /* Slumber capable */ + HOST_CAP_CCC = (1 << 7), /* Command Completion Coalescing */ + HOST_CAP_PART = (1 << 13), /* Partial state capable */ + HOST_CAP_SSC = (1 << 14), /* Slumber state capable */ + HOST_CAP_PIO_MULTI = (1 << 15), /* PIO multiple DRQ support */ + HOST_CAP_FBS = (1 << 16), /* FIS-based switching support */ HOST_CAP_PMP = (1 << 17), /* Port Multiplier support */ + HOST_CAP_ONLY = (1 << 18), /* Supports AHCI mode only */ HOST_CAP_CLO = (1 << 24), /* Command List Override support */ + HOST_CAP_LED = (1 << 25), /* Supports activity LED */ HOST_CAP_ALPM = (1 << 26), /* Aggressive Link PM support */ HOST_CAP_SSS = (1 << 27), /* Staggered Spin-up */ + HOST_CAP_MPS = (1 << 28), /* Mechanical presence switch */ HOST_CAP_SNTF = (1 << 29), /* SNotification register */ HOST_CAP_NCQ = (1 << 30), /* Native Command Queueing */ HOST_CAP_64 = (1 << 31), /* PCI DAC (64-bit DMA) support */ + /* HOST_CAP2 bits */ + HOST_CAP2_BOH = (1 << 0), /* BIOS/OS handoff supported */ + HOST_CAP2_NVMHCI = (1 << 1), /* NVMHCI supported */ + HOST_CAP2_APST = (1 << 2), /* Automatic partial to slumber */ + /* registers for each SATA port */ PORT_LST_ADDR = 0x00, /* command list DMA addr */ PORT_LST_ADDR_HI = 0x04, /* command list DMA addr hi */ @@ -267,8 +281,10 @@ struct ahci_em_priv { struct ahci_host_priv { unsigned int flags; /* AHCI_HFLAG_* */ u32 cap; /* cap to use */ + u32 cap2; /* cap2 to use */ u32 port_map; /* port map to use */ u32 saved_cap; /* saved initial cap */ + u32 saved_cap2; /* saved initial cap2 */ u32 saved_port_map; /* saved initial port_map */ u32 em_loc; /* enclosure management location */ }; @@ -331,12 +347,15 @@ static void ahci_init_sw_activity(struct ata_link *link); static ssize_t ahci_show_host_caps(struct device *dev, struct device_attribute *attr, char *buf); +static ssize_t ahci_show_host_cap2(struct device *dev, + struct device_attribute *attr, char *buf); static ssize_t ahci_show_host_version(struct device *dev, struct device_attribute *attr, char *buf); static ssize_t ahci_show_port_cmd(struct device *dev, struct device_attribute *attr, char *buf); DEVICE_ATTR(ahci_host_caps, S_IRUGO, ahci_show_host_caps, NULL); +DEVICE_ATTR(ahci_host_cap2, S_IRUGO, ahci_show_host_cap2, NULL); DEVICE_ATTR(ahci_host_version, S_IRUGO, ahci_show_host_version, NULL); DEVICE_ATTR(ahci_port_cmd, S_IRUGO, ahci_show_port_cmd, NULL); @@ -345,6 +364,7 @@ static struct device_attribute *ahci_shost_attrs[] = { &dev_attr_em_message_type, &dev_attr_em_message, &dev_attr_ahci_host_caps, + &dev_attr_ahci_host_cap2, &dev_attr_ahci_host_version, &dev_attr_ahci_port_cmd, NULL @@ -447,7 +467,8 @@ static const struct ata_port_info ahci_port_info[] = { [board_ahci_sb600] = { AHCI_HFLAGS (AHCI_HFLAG_IGN_SERR_INTERNAL | - AHCI_HFLAG_NO_MSI | AHCI_HFLAG_SECT255), + AHCI_HFLAG_NO_MSI | AHCI_HFLAG_SECT255 | + AHCI_HFLAG_32BIT_ONLY), .flags = AHCI_FLAG_COMMON, .pio_mask = ATA_PIO4, .udma_mask = ATA_UDMA6, @@ -554,7 +575,7 @@ static const struct pci_device_id ahci_pci_tbl[] = { { PCI_VDEVICE(ATI, 0x4395), board_ahci_sb700 }, /* ATI SB700/800 */ /* AMD */ - { PCI_VDEVICE(AMD, 0x7800), board_ahci }, /* AMD SB900 */ + { PCI_VDEVICE(AMD, 0x7800), board_ahci }, /* AMD Hudson-2 */ /* AMD is using RAID class only for ahci controllers */ { PCI_VENDOR_ID_AMD, PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_STORAGE_RAID << 8, 0xffffff, board_ahci }, @@ -584,6 +605,7 @@ static const struct pci_device_id ahci_pci_tbl[] = { { PCI_VDEVICE(NVIDIA, 0x0559), board_ahci_yesncq }, /* MCP67 */ { PCI_VDEVICE(NVIDIA, 0x055a), board_ahci_yesncq }, /* MCP67 */ { PCI_VDEVICE(NVIDIA, 0x055b), board_ahci_yesncq }, /* MCP67 */ + { PCI_VDEVICE(NVIDIA, 0x0580), board_ahci_yesncq }, /* Linux ID */ { PCI_VDEVICE(NVIDIA, 0x07f0), board_ahci_yesncq }, /* MCP73 */ { PCI_VDEVICE(NVIDIA, 0x07f1), board_ahci_yesncq }, /* MCP73 */ { PCI_VDEVICE(NVIDIA, 0x07f2), board_ahci_yesncq }, /* MCP73 */ @@ -732,6 +754,16 @@ static ssize_t ahci_show_host_caps(struct device *dev, return sprintf(buf, "%x\n", hpriv->cap); } +static ssize_t ahci_show_host_cap2(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct Scsi_Host *shost = class_to_shost(dev); + struct ata_port *ap = ata_shost_to_port(shost); + struct ahci_host_priv *hpriv = ap->host->private_data; + + return sprintf(buf, "%x\n", hpriv->cap2); +} + static ssize_t ahci_show_host_version(struct device *dev, struct device_attribute *attr, char *buf) { @@ -771,7 +803,7 @@ static void ahci_save_initial_config(struct pci_dev *pdev, struct ahci_host_priv *hpriv) { void __iomem *mmio = pcim_iomap_table(pdev)[AHCI_PCI_BAR]; - u32 cap, port_map; + u32 cap, cap2, vers, port_map; int i; int mv; @@ -784,6 +816,14 @@ static void ahci_save_initial_config(struct pci_dev *pdev, hpriv->saved_cap = cap = readl(mmio + HOST_CAP); hpriv->saved_port_map = port_map = readl(mmio + HOST_PORTS_IMPL); + /* CAP2 register is only defined for AHCI 1.2 and later */ + vers = readl(mmio + HOST_VERSION); + if ((vers >> 16) > 1 || + ((vers >> 16) == 1 && (vers & 0xFFFF) >= 0x200)) + hpriv->saved_cap2 = cap2 = readl(mmio + HOST_CAP2); + else + hpriv->saved_cap2 = cap2 = 0; + /* some chips have errata preventing 64bit use */ if ((cap & HOST_CAP_64) && (hpriv->flags & AHCI_HFLAG_32BIT_ONLY)) { dev_printk(KERN_INFO, &pdev->dev, @@ -869,6 +909,7 @@ static void ahci_save_initial_config(struct pci_dev *pdev, /* record values to use during operation */ hpriv->cap = cap; + hpriv->cap2 = cap2; hpriv->port_map = port_map; } @@ -887,6 +928,8 @@ static void ahci_restore_initial_config(struct ata_host *host) void __iomem *mmio = host->iomap[AHCI_PCI_BAR]; writel(hpriv->saved_cap, mmio + HOST_CAP); + if (hpriv->saved_cap2) + writel(hpriv->saved_cap2, mmio + HOST_CAP2); writel(hpriv->saved_port_map, mmio + HOST_PORTS_IMPL); (void) readl(mmio + HOST_PORTS_IMPL); /* flush */ } @@ -2534,13 +2577,14 @@ static void ahci_print_info(struct ata_host *host) struct ahci_host_priv *hpriv = host->private_data; struct pci_dev *pdev = to_pci_dev(host->dev); void __iomem *mmio = host->iomap[AHCI_PCI_BAR]; - u32 vers, cap, impl, speed; + u32 vers, cap, cap2, impl, speed; const char *speed_s; u16 cc; const char *scc_s; vers = readl(mmio + HOST_VERSION); cap = hpriv->cap; + cap2 = hpriv->cap2; impl = hpriv->port_map; speed = (cap >> 20) & 0xf; @@ -2583,25 +2627,29 @@ static void ahci_print_info(struct ata_host *host) "flags: " "%s%s%s%s%s%s%s" "%s%s%s%s%s%s%s" - "%s\n" + "%s%s%s%s%s%s\n" , - cap & (1 << 31) ? "64bit " : "", - cap & (1 << 30) ? "ncq " : "", - cap & (1 << 29) ? "sntf " : "", - cap & (1 << 28) ? "ilck " : "", - cap & (1 << 27) ? "stag " : "", - cap & (1 << 26) ? "pm " : "", - cap & (1 << 25) ? "led " : "", - - cap & (1 << 24) ? "clo " : "", - cap & (1 << 19) ? "nz " : "", - cap & (1 << 18) ? "only " : "", - cap & (1 << 17) ? "pmp " : "", - cap & (1 << 15) ? "pio " : "", - cap & (1 << 14) ? "slum " : "", - cap & (1 << 13) ? "part " : "", - cap & (1 << 6) ? "ems ": "" + cap & HOST_CAP_64 ? "64bit " : "", + cap & HOST_CAP_NCQ ? "ncq " : "", + cap & HOST_CAP_SNTF ? "sntf " : "", + cap & HOST_CAP_MPS ? "ilck " : "", + cap & HOST_CAP_SSS ? "stag " : "", + cap & HOST_CAP_ALPM ? "pm " : "", + cap & HOST_CAP_LED ? "led " : "", + cap & HOST_CAP_CLO ? "clo " : "", + cap & HOST_CAP_ONLY ? "only " : "", + cap & HOST_CAP_PMP ? "pmp " : "", + cap & HOST_CAP_FBS ? "fbs " : "", + cap & HOST_CAP_PIO_MULTI ? "pio " : "", + cap & HOST_CAP_SSC ? "slum " : "", + cap & HOST_CAP_PART ? "part " : "", + cap & HOST_CAP_CCC ? "ccc " : "", + cap & HOST_CAP_EMS ? "ems " : "", + cap & HOST_CAP_SXS ? "sxs " : "", + cap2 & HOST_CAP2_APST ? "apst " : "", + cap2 & HOST_CAP2_NVMHCI ? "nvmp " : "", + cap2 & HOST_CAP2_BOH ? "boh " : "" ); } @@ -2650,17 +2698,15 @@ static void ahci_p5wdh_workaround(struct ata_host *host) } } -/* - * SB600 ahci controller on certain boards can't do 64bit DMA with - * older BIOS. - */ -static bool ahci_sb600_32bit_only(struct pci_dev *pdev) +/* only some SB600 ahci controllers can do 64bit DMA */ +static bool ahci_sb600_enable_64bit(struct pci_dev *pdev) { static const struct dmi_system_id sysids[] = { /* * The oldest version known to be broken is 0901 and * working is 1501 which was released on 2007-10-26. - * Force 32bit DMA on anything older than 1501. + * Enable 64bit DMA on 1501 and anything newer. + * * Please read bko#9412 for more info. */ { @@ -2673,46 +2719,57 @@ static bool ahci_sb600_32bit_only(struct pci_dev *pdev) .driver_data = "20071026", /* yyyymmdd */ }, /* - * It's yet unknown whether more recent BIOS fixes the - * problem. Blacklist the whole board for the time - * being. Please read the following thread for more - * info. + * All BIOS versions for the MSI K9A2 Platinum (MS-7376) + * support 64bit DMA. * - * http://thread.gmane.org/gmane.linux.ide/42326 + * BIOS versions earlier than 1.5 had the Manufacturer DMI + * fields as "MICRO-STAR INTERANTIONAL CO.,LTD". + * This spelling mistake was fixed in BIOS version 1.5, so + * 1.5 and later have the Manufacturer as + * "MICRO-STAR INTERNATIONAL CO.,LTD". + * So try to match on DMI_BOARD_VENDOR of "MICRO-STAR INTER". + * + * BIOS versions earlier than 1.9 had a Board Product Name + * DMI field of "MS-7376". This was changed to be + * "K9A2 Platinum (MS-7376)" in version 1.9, but we can still + * match on DMI_BOARD_NAME of "MS-7376". */ { - .ident = "Gigabyte GA-MA69VM-S2", + .ident = "MSI K9A2 Platinum", .matches = { DMI_MATCH(DMI_BOARD_VENDOR, - "Gigabyte Technology Co., Ltd."), - DMI_MATCH(DMI_BOARD_NAME, "GA-MA69VM-S2"), + "MICRO-STAR INTER"), + DMI_MATCH(DMI_BOARD_NAME, "MS-7376"), }, }, { } }; const struct dmi_system_id *match; + int year, month, date; + char buf[9]; match = dmi_first_match(sysids); if (pdev->bus->number != 0 || pdev->devfn != PCI_DEVFN(0x12, 0) || !match) return false; - if (match->driver_data) { - int year, month, date; - char buf[9]; - - dmi_get_date(DMI_BIOS_DATE, &year, &month, &date); - snprintf(buf, sizeof(buf), "%04d%02d%02d", year, month, date); + if (!match->driver_data) + goto enable_64bit; - if (strcmp(buf, match->driver_data) >= 0) - return false; + dmi_get_date(DMI_BIOS_DATE, &year, &month, &date); + snprintf(buf, sizeof(buf), "%04d%02d%02d", year, month, date); + if (strcmp(buf, match->driver_data) >= 0) + goto enable_64bit; + else { dev_printk(KERN_WARNING, &pdev->dev, "%s: BIOS too old, " "forcing 32bit DMA, update BIOS\n", match->ident); - } else - dev_printk(KERN_WARNING, &pdev->dev, "%s: this board can't " - "do 64bit DMA, forcing 32bit\n", match->ident); + return false; + } +enable_64bit: + dev_printk(KERN_WARNING, &pdev->dev, "%s: enabling 64bit DMA\n", + match->ident); return true; } @@ -2858,6 +2915,55 @@ static bool ahci_broken_online(struct pci_dev *pdev) return pdev->bus->number == (val >> 8) && pdev->devfn == (val & 0xff); } +#ifdef CONFIG_ATA_ACPI +static void ahci_gtf_filter_workaround(struct ata_host *host) +{ + static const struct dmi_system_id sysids[] = { + /* + * Aspire 3810T issues a bunch of SATA enable commands + * via _GTF including an invalid one and one which is + * rejected by the device. Among the successful ones + * is FPDMA non-zero offset enable which when enabled + * only on the drive side leads to NCQ command + * failures. Filter it out. + */ + { + .ident = "Aspire 3810T", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Acer"), + DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 3810T"), + }, + .driver_data = (void *)ATA_ACPI_FILTER_FPDMA_OFFSET, + }, + { } + }; + const struct dmi_system_id *dmi = dmi_first_match(sysids); + unsigned int filter; + int i; + + if (!dmi) + return; + + filter = (unsigned long)dmi->driver_data; + dev_printk(KERN_INFO, host->dev, + "applying extra ACPI _GTF filter 0x%x for %s\n", + filter, dmi->ident); + + for (i = 0; i < host->n_ports; i++) { + struct ata_port *ap = host->ports[i]; + struct ata_link *link; + struct ata_device *dev; + + ata_for_each_link(link, ap, EDGE) + ata_for_each_dev(dev, link, ALL) + dev->gtf_filter |= filter; + } +} +#else +static inline void ahci_gtf_filter_workaround(struct ata_host *host) +{} +#endif + static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) { static int printed_version; @@ -2926,9 +3032,9 @@ static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) if (board_id == board_ahci_sb700 && pdev->revision >= 0x40) hpriv->flags &= ~AHCI_HFLAG_IGN_SERR_INTERNAL; - /* apply sb600 32bit only quirk */ - if (ahci_sb600_32bit_only(pdev)) - hpriv->flags |= AHCI_HFLAG_32BIT_ONLY; + /* only some SB600s can do 64bit DMA */ + if (ahci_sb600_enable_64bit(pdev)) + hpriv->flags &= ~AHCI_HFLAG_32BIT_ONLY; if ((hpriv->flags & AHCI_HFLAG_NO_MSI) || pci_enable_msi(pdev)) pci_intx(pdev, 1); @@ -3023,6 +3129,9 @@ static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) /* apply workaround for ASUS P5W DH Deluxe mainboard */ ahci_p5wdh_workaround(host); + /* apply gtf filter quirk */ + ahci_gtf_filter_workaround(host); + /* initialize adapter */ rc = ahci_configure_dma_masks(pdev, hpriv->cap & HOST_CAP_64); if (rc) diff --git a/drivers/ata/libata-acpi.c b/drivers/ata/libata-acpi.c index 01964b6e6f6b..b0882cddfd4c 100644 --- a/drivers/ata/libata-acpi.c +++ b/drivers/ata/libata-acpi.c @@ -20,19 +20,9 @@ #include <acpi/acpi_bus.h> -enum { - ATA_ACPI_FILTER_SETXFER = 1 << 0, - ATA_ACPI_FILTER_LOCK = 1 << 1, - ATA_ACPI_FILTER_DIPM = 1 << 2, - - ATA_ACPI_FILTER_DEFAULT = ATA_ACPI_FILTER_SETXFER | - ATA_ACPI_FILTER_LOCK | - ATA_ACPI_FILTER_DIPM, -}; - -static unsigned int ata_acpi_gtf_filter = ATA_ACPI_FILTER_DEFAULT; +unsigned int ata_acpi_gtf_filter = ATA_ACPI_FILTER_DEFAULT; module_param_named(acpi_gtf_filter, ata_acpi_gtf_filter, int, 0644); -MODULE_PARM_DESC(acpi_gtf_filter, "filter mask for ACPI _GTF commands, set to filter out (0x1=set xfermode, 0x2=lock/freeze lock, 0x4=DIPM)"); +MODULE_PARM_DESC(acpi_gtf_filter, "filter mask for ACPI _GTF commands, set to filter out (0x1=set xfermode, 0x2=lock/freeze lock, 0x4=DIPM, 0x8=FPDMA non-zero offset, 0x10=FPDMA DMA Setup FIS auto-activate)"); #define NO_PORT_MULT 0xffff #define SATA_ADR(root, pmp) (((root) << 16) | (pmp)) @@ -613,10 +603,11 @@ static void ata_acpi_gtf_to_tf(struct ata_device *dev, tf->command = gtf->tf[6]; /* 0x1f7 */ } -static int ata_acpi_filter_tf(const struct ata_taskfile *tf, +static int ata_acpi_filter_tf(struct ata_device *dev, + const struct ata_taskfile *tf, const struct ata_taskfile *ptf) { - if (ata_acpi_gtf_filter & ATA_ACPI_FILTER_SETXFER) { + if (dev->gtf_filter & ATA_ACPI_FILTER_SETXFER) { /* libata doesn't use ACPI to configure transfer mode. * It will only confuse device configuration. Skip. */ @@ -625,7 +616,7 @@ static int ata_acpi_filter_tf(const struct ata_taskfile *tf, return 1; } - if (ata_acpi_gtf_filter & ATA_ACPI_FILTER_LOCK) { + if (dev->gtf_filter & ATA_ACPI_FILTER_LOCK) { /* BIOS writers, sorry but we don't wanna lock * features unless the user explicitly said so. */ @@ -647,12 +638,23 @@ static int ata_acpi_filter_tf(const struct ata_taskfile *tf, return 1; } - if (ata_acpi_gtf_filter & ATA_ACPI_FILTER_DIPM) { + if (tf->command == ATA_CMD_SET_FEATURES && + tf->feature == SETFEATURES_SATA_ENABLE) { /* inhibit enabling DIPM */ - if (tf->command == ATA_CMD_SET_FEATURES && - tf->feature == SETFEATURES_SATA_ENABLE && + if (dev->gtf_filter & ATA_ACPI_FILTER_DIPM && tf->nsect == SATA_DIPM) return 1; + + /* inhibit FPDMA non-zero offset */ + if (dev->gtf_filter & ATA_ACPI_FILTER_FPDMA_OFFSET && + (tf->nsect == SATA_FPDMA_OFFSET || + tf->nsect == SATA_FPDMA_IN_ORDER)) + return 1; + + /* inhibit FPDMA auto activation */ + if (dev->gtf_filter & ATA_ACPI_FILTER_FPDMA_AA && + tf->nsect == SATA_FPDMA_AA) + return 1; } return 0; @@ -704,7 +706,7 @@ static int ata_acpi_run_tf(struct ata_device *dev, pptf = &ptf; } - if (!ata_acpi_filter_tf(&tf, pptf)) { + if (!ata_acpi_filter_tf(dev, &tf, pptf)) { rtf = tf; err_mask = ata_exec_internal(dev, &rtf, NULL, DMA_NONE, NULL, 0, 0); diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c index 0ddaf43d68c6..dc72690ed5db 100644 --- a/drivers/ata/libata-core.c +++ b/drivers/ata/libata-core.c @@ -4919,10 +4919,11 @@ struct ata_queued_cmd *ata_qc_new_init(struct ata_device *dev) */ void ata_qc_free(struct ata_queued_cmd *qc) { - struct ata_port *ap = qc->ap; + struct ata_port *ap; unsigned int tag; WARN_ON_ONCE(qc == NULL); /* ata_qc_from_tag _might_ return NULL */ + ap = qc->ap; qc->flags = 0; tag = qc->tag; @@ -4934,11 +4935,13 @@ void ata_qc_free(struct ata_queued_cmd *qc) void __ata_qc_complete(struct ata_queued_cmd *qc) { - struct ata_port *ap = qc->ap; - struct ata_link *link = qc->dev->link; + struct ata_port *ap; + struct ata_link *link; WARN_ON_ONCE(qc == NULL); /* ata_qc_from_tag _might_ return NULL */ WARN_ON_ONCE(!(qc->flags & ATA_QCFLAG_ACTIVE)); + ap = qc->ap; + link = qc->dev->link; if (likely(qc->flags & ATA_QCFLAG_DMAMAP)) ata_sg_clean(qc); @@ -5028,12 +5031,14 @@ void ata_qc_complete(struct ata_queued_cmd *qc) qc->flags |= ATA_QCFLAG_FAILED; if (unlikely(qc->flags & ATA_QCFLAG_FAILED)) { - if (!ata_tag_internal(qc->tag)) { - /* always fill result TF for failed qc */ - fill_result_tf(qc); + /* always fill result TF for failed qc */ + fill_result_tf(qc); + + if (!ata_tag_internal(qc->tag)) ata_qc_schedule_eh(qc); - return; - } + else + __ata_qc_complete(qc); + return; } WARN_ON_ONCE(ap->pflags & ATA_PFLAG_FROZEN); @@ -5591,6 +5596,9 @@ void ata_link_init(struct ata_port *ap, struct ata_link *link, int pmp) dev->link = link; dev->devno = dev - link->device; +#ifdef CONFIG_ATA_ACPI + dev->gtf_filter = ata_acpi_gtf_filter; +#endif ata_dev_init(dev); } } diff --git a/drivers/ata/libata-eh.c b/drivers/ata/libata-eh.c index a04488f0de88..bba2ae5df1c2 100644 --- a/drivers/ata/libata-eh.c +++ b/drivers/ata/libata-eh.c @@ -2667,14 +2667,14 @@ int ata_eh_reset(struct ata_link *link, int classify, dev->pio_mode = XFER_PIO_0; dev->flags &= ~ATA_DFLAG_SLEEPING; - if (!ata_phys_link_offline(ata_dev_phys_link(dev))) { - /* apply class override */ - if (lflags & ATA_LFLAG_ASSUME_ATA) - classes[dev->devno] = ATA_DEV_ATA; - else if (lflags & ATA_LFLAG_ASSUME_SEMB) - classes[dev->devno] = ATA_DEV_SEMB_UNSUP; - } else - classes[dev->devno] = ATA_DEV_NONE; + if (ata_phys_link_offline(ata_dev_phys_link(dev))) + continue; + + /* apply class override */ + if (lflags & ATA_LFLAG_ASSUME_ATA) + classes[dev->devno] = ATA_DEV_ATA; + else if (lflags & ATA_LFLAG_ASSUME_SEMB) + classes[dev->devno] = ATA_DEV_SEMB_UNSUP; } /* record current link speed */ @@ -2713,34 +2713,48 @@ int ata_eh_reset(struct ata_link *link, int classify, ap->pflags &= ~ATA_PFLAG_EH_PENDING; spin_unlock_irqrestore(link->ap->lock, flags); - /* Make sure onlineness and classification result correspond. + /* + * Make sure onlineness and classification result correspond. * Hotplug could have happened during reset and some * controllers fail to wait while a drive is spinning up after * being hotplugged causing misdetection. By cross checking - * link onlineness and classification result, those conditions - * can be reliably detected and retried. + * link on/offlineness and classification result, those + * conditions can be reliably detected and retried. */ nr_unknown = 0; ata_for_each_dev(dev, link, ALL) { - /* convert all ATA_DEV_UNKNOWN to ATA_DEV_NONE */ - if (classes[dev->devno] == ATA_DEV_UNKNOWN) { - classes[dev->devno] = ATA_DEV_NONE; - if (ata_phys_link_online(ata_dev_phys_link(dev))) + if (ata_phys_link_online(ata_dev_phys_link(dev))) { + if (classes[dev->devno] == ATA_DEV_UNKNOWN) { + ata_dev_printk(dev, KERN_DEBUG, "link online " + "but device misclassifed\n"); + classes[dev->devno] = ATA_DEV_NONE; nr_unknown++; + } + } else if (ata_phys_link_offline(ata_dev_phys_link(dev))) { + if (ata_class_enabled(classes[dev->devno])) + ata_dev_printk(dev, KERN_DEBUG, "link offline, " + "clearing class %d to NONE\n", + classes[dev->devno]); + classes[dev->devno] = ATA_DEV_NONE; + } else if (classes[dev->devno] == ATA_DEV_UNKNOWN) { + ata_dev_printk(dev, KERN_DEBUG, "link status unknown, " + "clearing UNKNOWN to NONE\n"); + classes[dev->devno] = ATA_DEV_NONE; } } if (classify && nr_unknown) { if (try < max_tries) { ata_link_printk(link, KERN_WARNING, "link online but " - "device misclassified, retrying\n"); + "%d devices misclassified, retrying\n", + nr_unknown); failed_link = link; rc = -EAGAIN; goto fail; } ata_link_printk(link, KERN_WARNING, - "link online but device misclassified, " - "device detection might fail\n"); + "link online but %d devices misclassified, " + "device detection might fail\n", nr_unknown); } /* reset successful, schedule revalidation */ @@ -2967,12 +2981,14 @@ static int ata_eh_revalidate_and_attach(struct ata_link *link, * device detection messages backwards. */ ata_for_each_dev(dev, link, ALL) { - if (!(new_mask & (1 << dev->devno)) || - dev->class == ATA_DEV_PMP) + if (!(new_mask & (1 << dev->devno))) continue; dev->class = ehc->classes[dev->devno]; + if (dev->class == ATA_DEV_PMP) + continue; + ehc->i.flags |= ATA_EHI_PRINTINFO; rc = ata_dev_configure(dev); ehc->i.flags &= ~ATA_EHI_PRINTINFO; diff --git a/drivers/ata/libata.h b/drivers/ata/libata.h index be8e2628f82c..823e63096362 100644 --- a/drivers/ata/libata.h +++ b/drivers/ata/libata.h @@ -118,6 +118,8 @@ extern void ata_lpm_schedule(struct ata_port *ap, enum link_pm); /* libata-acpi.c */ #ifdef CONFIG_ATA_ACPI +extern unsigned int ata_acpi_gtf_filter; + extern void ata_acpi_associate_sata_port(struct ata_port *ap); extern void ata_acpi_associate(struct ata_host *host); extern void ata_acpi_dissociate(struct ata_host *host); diff --git a/drivers/ata/pata_ali.c b/drivers/ata/pata_ali.c index fc9c5d6d7d80..1432dc9d0ab8 100644 --- a/drivers/ata/pata_ali.c +++ b/drivers/ata/pata_ali.c @@ -290,7 +290,7 @@ static void ali_warn_atapi_dma(struct ata_device *adev) if (print_info && adev->class == ATA_DEV_ATAPI && !ali_atapi_dma) { ata_dev_printk(adev, KERN_WARNING, - "WARNING: ATAPI DMA disabled for reliablity issues. It can be enabled\n"); + "WARNING: ATAPI DMA disabled for reliability issues. It can be enabled\n"); ata_dev_printk(adev, KERN_WARNING, "WARNING: via pata_ali.atapi_dma modparam or corresponding sysfs node.\n"); } diff --git a/drivers/ata/pata_atiixp.c b/drivers/ata/pata_atiixp.c index aa4b3f6ae771..ae4454d4e955 100644 --- a/drivers/ata/pata_atiixp.c +++ b/drivers/ata/pata_atiixp.c @@ -246,7 +246,7 @@ static const struct pci_device_id atiixp[] = { { PCI_VDEVICE(ATI, PCI_DEVICE_ID_ATI_IXP400_IDE), }, { PCI_VDEVICE(ATI, PCI_DEVICE_ID_ATI_IXP600_IDE), }, { PCI_VDEVICE(ATI, PCI_DEVICE_ID_ATI_IXP700_IDE), }, - { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_SB900_IDE), }, + { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_HUDSON2_IDE), }, { }, }; diff --git a/drivers/ata/pata_atp867x.c b/drivers/ata/pata_atp867x.c index 7990de925d2e..6fe7ded40c6a 100644 --- a/drivers/ata/pata_atp867x.c +++ b/drivers/ata/pata_atp867x.c @@ -118,20 +118,13 @@ struct atp867x_priv { int pci66mhz; }; -static inline u8 atp867x_speed_to_mode(u8 speed) -{ - return speed - XFER_UDMA_0 + 1; -} - static void atp867x_set_dmamode(struct ata_port *ap, struct ata_device *adev) { struct pci_dev *pdev = to_pci_dev(ap->host->dev); struct atp867x_priv *dp = ap->private_data; u8 speed = adev->dma_mode; u8 b; - u8 mode; - - mode = atp867x_speed_to_mode(speed); + u8 mode = speed - XFER_UDMA_0 + 1; /* * Doc 6.6.9: decrease the udma mode value by 1 for safer UDMA speed @@ -156,25 +149,38 @@ static void atp867x_set_dmamode(struct ata_port *ap, struct ata_device *adev) iowrite8(b, dp->dma_mode); } -static int atp867x_get_active_clocks_shifted(unsigned int clk) +static int atp867x_get_active_clocks_shifted(struct ata_port *ap, + unsigned int clk) { + struct atp867x_priv *dp = ap->private_data; unsigned char clocks = clk; + /* + * Doc 6.6.9: increase the clock value by 1 for safer PIO speed + * on 66MHz bus + */ + if (dp->pci66mhz) + clocks++; + switch (clocks) { case 0: clocks = 1; break; - case 1 ... 7: - break; - case 8 ... 12: - clocks = 7; + case 1 ... 6: break; default: printk(KERN_WARNING "ATP867X: active %dclk is invalid. " - "Using default 8clk.\n", clk); - clocks = 0; /* 8 clk */ + "Using 12clk.\n", clk); + case 9 ... 12: + clocks = 7; /* 12 clk */ break; + case 7: + case 8: /* default 8 clk */ + clocks = 0; + goto active_clock_shift_done; } + +active_clock_shift_done: return clocks << ATP867X_IO_PIOSPD_ACTIVE_SHIFT; } @@ -188,20 +194,20 @@ static int atp867x_get_recover_clocks_shifted(unsigned int clk) break; case 1 ... 11: break; - case 12: - clocks = 0; - break; - case 13: case 14: - --clocks; + case 13: + case 14: + --clocks; /* by the spec */ break; case 15: break; default: printk(KERN_WARNING "ATP867X: recover %dclk is invalid. " - "Using default 15clk.\n", clk); - clocks = 0; /* 12 clk */ + "Using default 12clk.\n", clk); + case 12: /* default 12 clk */ + clocks = 0; break; } + return clocks << ATP867X_IO_PIOSPD_RECOVER_SHIFT; } @@ -230,25 +236,38 @@ static void atp867x_set_piomode(struct ata_port *ap, struct ata_device *adev) b = (b & ~ATP867X_IO_DMAMODE_MSTR_MASK); iowrite8(b, dp->dma_mode); - b = atp867x_get_active_clocks_shifted(t.active) | - atp867x_get_recover_clocks_shifted(t.recover); - if (dp->pci66mhz) - b += 0x10; + b = atp867x_get_active_clocks_shifted(ap, t.active) | + atp867x_get_recover_clocks_shifted(t.recover); if (adev->devno & 1) iowrite8(b, dp->slave_piospd); else iowrite8(b, dp->mstr_piospd); - /* - * use the same value for comand timing as for PIO timimg - */ + b = atp867x_get_active_clocks_shifted(ap, t.act8b) | + atp867x_get_recover_clocks_shifted(t.rec8b); + iowrite8(b, dp->eightb_piospd); } +static int atp867x_cable_override(struct pci_dev *pdev) +{ + if (pdev->subsystem_vendor == PCI_VENDOR_ID_ARTOP && + (pdev->subsystem_device == PCI_DEVICE_ID_ARTOP_ATP867A || + pdev->subsystem_device == PCI_DEVICE_ID_ARTOP_ATP867B)) { + return 1; + } + return 0; +} + static int atp867x_cable_detect(struct ata_port *ap) { - return ATA_CBL_PATA40_SHORT; + struct pci_dev *pdev = to_pci_dev(ap->host->dev); + + if (atp867x_cable_override(pdev)) + return ATA_CBL_PATA40_SHORT; + + return ATA_CBL_PATA_UNK; } static struct scsi_host_template atp867x_sht = { @@ -471,7 +490,6 @@ static int atp867x_init_one(struct pci_dev *pdev, static const struct ata_port_info info_867x = { .flags = ATA_FLAG_SLAVE_POSS, .pio_mask = ATA_PIO4, - .mwdma_mask = ATA_MWDMA2, .udma_mask = ATA_UDMA6, .port_ops = &atp867x_ops, }; @@ -515,6 +533,23 @@ err_out: return rc; } +#ifdef CONFIG_PM +static int atp867x_reinit_one(struct pci_dev *pdev) +{ + struct ata_host *host = dev_get_drvdata(&pdev->dev); + int rc; + + rc = ata_pci_device_do_resume(pdev); + if (rc) + return rc; + + atp867x_fixup(host); + + ata_host_resume(host); + return 0; +} +#endif + static struct pci_device_id atp867x_pci_tbl[] = { { PCI_VDEVICE(ARTOP, PCI_DEVICE_ID_ARTOP_ATP867A), 0 }, { PCI_VDEVICE(ARTOP, PCI_DEVICE_ID_ARTOP_ATP867B), 0 }, @@ -526,6 +561,10 @@ static struct pci_driver atp867x_driver = { .id_table = atp867x_pci_tbl, .probe = atp867x_init_one, .remove = ata_pci_remove_one, +#ifdef CONFIG_PM + .suspend = ata_pci_device_suspend, + .resume = atp867x_reinit_one, +#endif }; static int __init atp867x_init(void) diff --git a/drivers/ata/pata_sc1200.c b/drivers/ata/pata_sc1200.c index f49814d6fd2e..3bbed8322ecf 100644 --- a/drivers/ata/pata_sc1200.c +++ b/drivers/ata/pata_sc1200.c @@ -235,8 +235,7 @@ static int sc1200_init_one(struct pci_dev *dev, const struct pci_device_id *id) .udma_mask = ATA_UDMA2, .port_ops = &sc1200_port_ops }; - /* Can't enable port 2 yet, see top comments */ - const struct ata_port_info *ppi[] = { &info, }; + const struct ata_port_info *ppi[] = { &info, NULL }; return ata_pci_sff_init_one(dev, ppi, &sc1200_sht, NULL); } diff --git a/drivers/ata/pata_via.c b/drivers/ata/pata_via.c index 45657cacec43..88984b803d6d 100644 --- a/drivers/ata/pata_via.c +++ b/drivers/ata/pata_via.c @@ -111,7 +111,7 @@ static const struct via_isa_bridge { { "vt8251", PCI_DEVICE_ID_VIA_8251, 0x00, 0x2f, VIA_UDMA_133 | VIA_BAD_AST }, { "cx700", PCI_DEVICE_ID_VIA_CX700, 0x00, 0x2f, VIA_UDMA_133 | VIA_BAD_AST | VIA_SATA_PATA }, { "vt6410", PCI_DEVICE_ID_VIA_6410, 0x00, 0x2f, VIA_UDMA_133 | VIA_BAD_AST | VIA_NO_ENABLES }, - { "vt6415", PCI_DEVICE_ID_VIA_6415, 0x00, 0x2f, VIA_UDMA_133 | VIA_BAD_AST | VIA_NO_ENABLES }, + { "vt6415", PCI_DEVICE_ID_VIA_6415, 0x00, 0xff, VIA_UDMA_133 | VIA_BAD_AST | VIA_NO_ENABLES }, { "vt8237a", PCI_DEVICE_ID_VIA_8237A, 0x00, 0x2f, VIA_UDMA_133 | VIA_BAD_AST }, { "vt8237", PCI_DEVICE_ID_VIA_8237, 0x00, 0x2f, VIA_UDMA_133 | VIA_BAD_AST }, { "vt8235", PCI_DEVICE_ID_VIA_8235, 0x00, 0x2f, VIA_UDMA_133 | VIA_BAD_AST }, diff --git a/drivers/ata/sata_fsl.c b/drivers/ata/sata_fsl.c index d344db42a002..172b57e6543f 100644 --- a/drivers/ata/sata_fsl.c +++ b/drivers/ata/sata_fsl.c @@ -707,34 +707,17 @@ static unsigned int sata_fsl_dev_classify(struct ata_port *ap) return ata_dev_classify(&tf); } -static int sata_fsl_prereset(struct ata_link *link, unsigned long deadline) -{ - /* FIXME: Never skip softreset, sata_fsl_softreset() is - * combination of soft and hard resets. sata_fsl_softreset() - * needs to be splitted into soft and hard resets. - */ - return 0; -} - -static int sata_fsl_softreset(struct ata_link *link, unsigned int *class, +static int sata_fsl_hardreset(struct ata_link *link, unsigned int *class, unsigned long deadline) { struct ata_port *ap = link->ap; - struct sata_fsl_port_priv *pp = ap->private_data; struct sata_fsl_host_priv *host_priv = ap->host->private_data; void __iomem *hcr_base = host_priv->hcr_base; - int pmp = sata_srst_pmp(link); u32 temp; - struct ata_taskfile tf; - u8 *cfis; - u32 Serror; int i = 0; unsigned long start_jiffies; - DPRINTK("in xx_softreset\n"); - - if (pmp != SATA_PMP_CTRL_PORT) - goto issue_srst; + DPRINTK("in xx_hardreset\n"); try_offline_again: /* @@ -749,7 +732,7 @@ try_offline_again: if (temp & ONLINE) { ata_port_printk(ap, KERN_ERR, - "Softreset failed, not off-lined %d\n", i); + "Hardreset failed, not off-lined %d\n", i); /* * Try to offline controller atleast twice @@ -761,7 +744,7 @@ try_offline_again: goto try_offline_again; } - DPRINTK("softreset, controller off-lined\n"); + DPRINTK("hardreset, controller off-lined\n"); VPRINTK("HStatus = 0x%x\n", ioread32(hcr_base + HSTATUS)); VPRINTK("HControl = 0x%x\n", ioread32(hcr_base + HCONTROL)); @@ -786,11 +769,11 @@ try_offline_again: if (!(temp & ONLINE)) { ata_port_printk(ap, KERN_ERR, - "Softreset failed, not on-lined\n"); + "Hardreset failed, not on-lined\n"); goto err; } - DPRINTK("softreset, controller off-lined & on-lined\n"); + DPRINTK("hardreset, controller off-lined & on-lined\n"); VPRINTK("HStatus = 0x%x\n", ioread32(hcr_base + HSTATUS)); VPRINTK("HControl = 0x%x\n", ioread32(hcr_base + HCONTROL)); @@ -806,7 +789,7 @@ try_offline_again: "No Device OR PHYRDY change,Hstatus = 0x%x\n", ioread32(hcr_base + HSTATUS)); *class = ATA_DEV_NONE; - goto out; + return 0; } /* @@ -819,11 +802,44 @@ try_offline_again: if ((temp & 0xFF) != 0x18) { ata_port_printk(ap, KERN_WARNING, "No Signature Update\n"); *class = ATA_DEV_NONE; - goto out; + goto do_followup_srst; } else { ata_port_printk(ap, KERN_INFO, "Signature Update detected @ %d msecs\n", jiffies_to_msecs(jiffies - start_jiffies)); + *class = sata_fsl_dev_classify(ap); + return 0; + } + +do_followup_srst: + /* + * request libATA to perform follow-up softreset + */ + return -EAGAIN; + +err: + return -EIO; +} + +static int sata_fsl_softreset(struct ata_link *link, unsigned int *class, + unsigned long deadline) +{ + struct ata_port *ap = link->ap; + struct sata_fsl_port_priv *pp = ap->private_data; + struct sata_fsl_host_priv *host_priv = ap->host->private_data; + void __iomem *hcr_base = host_priv->hcr_base; + int pmp = sata_srst_pmp(link); + u32 temp; + struct ata_taskfile tf; + u8 *cfis; + u32 Serror; + + DPRINTK("in xx_softreset\n"); + + if (ata_link_offline(link)) { + DPRINTK("PHY reports no device\n"); + *class = ATA_DEV_NONE; + return 0; } /* @@ -834,7 +850,6 @@ try_offline_again: * reached here, we can send a command to the target device */ -issue_srst: DPRINTK("Sending SRST/device reset\n"); ata_tf_init(link->device, &tf); @@ -860,6 +875,8 @@ issue_srst: ioread32(CA + hcr_base), ioread32(CC + hcr_base)); iowrite32(0xFFFF, CC + hcr_base); + if (pmp != SATA_PMP_CTRL_PORT) + iowrite32(pmp, CQPMP + hcr_base); iowrite32(1, CQ + hcr_base); temp = ata_wait_register(CQ + hcr_base, 0x1, 0x1, 1, 5000); @@ -926,7 +943,6 @@ issue_srst: VPRINTK("cereg = 0x%x\n", ioread32(hcr_base + CE)); } -out: return 0; err: @@ -988,18 +1004,6 @@ static void sata_fsl_error_intr(struct ata_port *ap) ehi->err_mask |= AC_ERR_ATA_BUS; ehi->action |= ATA_EH_SOFTRESET; - /* - * Ignore serror in case of fatal errors as we always want - * to do a soft-reset of the FSL SATA controller. Analyzing - * serror may cause libata to schedule a hard-reset action, - * and hard-reset currently does not do controller - * offline/online, causing command timeouts and leads to an - * un-recoverable state, hence make libATA ignore - * autopsy in case of fatal errors. - */ - - ehi->flags |= ATA_EHI_NO_AUTOPSY; - freeze = 1; } @@ -1267,8 +1271,8 @@ static struct ata_port_operations sata_fsl_ops = { .freeze = sata_fsl_freeze, .thaw = sata_fsl_thaw, - .prereset = sata_fsl_prereset, .softreset = sata_fsl_softreset, + .hardreset = sata_fsl_hardreset, .pmp_softreset = sata_fsl_softreset, .error_handler = sata_fsl_error_handler, .post_internal_cmd = sata_fsl_post_internal_cmd, diff --git a/drivers/ata/sata_mv.c b/drivers/ata/sata_mv.c index 17f9ff9067a2..6f5093b7c8c5 100644 --- a/drivers/ata/sata_mv.c +++ b/drivers/ata/sata_mv.c @@ -1382,6 +1382,25 @@ static int mv_qc_defer(struct ata_queued_cmd *qc) */ if (pp->pp_flags & MV_PP_FLAG_DELAYED_EH) return ATA_DEFER_PORT; + + /* PIO commands need exclusive link: no other commands [DMA or PIO] + * can run concurrently. + * set excl_link when we want to send a PIO command in DMA mode + * or a non-NCQ command in NCQ mode. + * When we receive a command from that link, and there are no + * outstanding commands, mark a flag to clear excl_link and let + * the command go through. + */ + if (unlikely(ap->excl_link)) { + if (link == ap->excl_link) { + if (ap->nr_active_links) + return ATA_DEFER_PORT; + qc->flags |= ATA_QCFLAG_CLEAR_EXCL; + return 0; + } else + return ATA_DEFER_PORT; + } + /* * If the port is completely idle, then allow the new qc. */ @@ -1395,8 +1414,14 @@ static int mv_qc_defer(struct ata_queued_cmd *qc) * doesn't allow it. */ if ((pp->pp_flags & MV_PP_FLAG_EDMA_EN) && - (pp->pp_flags & MV_PP_FLAG_NCQ_EN) && ata_is_ncq(qc->tf.protocol)) - return 0; + (pp->pp_flags & MV_PP_FLAG_NCQ_EN)) { + if (ata_is_ncq(qc->tf.protocol)) + return 0; + else { + ap->excl_link = link; + return ATA_DEFER_PORT; + } + } return ATA_DEFER_PORT; } diff --git a/drivers/ata/sata_nv.c b/drivers/ata/sata_nv.c index 86a40582999c..1eb4e020eb5c 100644 --- a/drivers/ata/sata_nv.c +++ b/drivers/ata/sata_nv.c @@ -1594,9 +1594,21 @@ static int nv_hardreset(struct ata_link *link, unsigned int *class, !ata_dev_enabled(link->device)) sata_link_hardreset(link, sata_deb_timing_hotplug, deadline, NULL, NULL); - else if (!(ehc->i.flags & ATA_EHI_QUIET)) - ata_link_printk(link, KERN_INFO, - "nv: skipping hardreset on occupied port\n"); + else { + const unsigned long *timing = sata_ehc_deb_timing(ehc); + int rc; + + if (!(ehc->i.flags & ATA_EHI_QUIET)) + ata_link_printk(link, KERN_INFO, "nv: skipping " + "hardreset on occupied port\n"); + + /* make sure the link is online */ + rc = sata_link_resume(link, timing, deadline); + /* whine about phy resume failure but proceed */ + if (rc && rc != -EOPNOTSUPP) + ata_link_printk(link, KERN_WARNING, "failed to resume " + "link (errno=%d)\n", rc); + } /* device signature acquisition is unreliable */ return -EAGAIN; diff --git a/drivers/ata/sata_via.c b/drivers/ata/sata_via.c index bdd43c7f432e..02efd9a83d26 100644 --- a/drivers/ata/sata_via.c +++ b/drivers/ata/sata_via.c @@ -93,7 +93,6 @@ static const struct pci_device_id svia_pci_tbl[] = { { PCI_VDEVICE(VIA, 0x7372), vt6420 }, { PCI_VDEVICE(VIA, 0x5287), vt8251 }, /* 2 sata chnls (Master/Slave) */ { PCI_VDEVICE(VIA, 0x9000), vt8251 }, - { PCI_VDEVICE(VIA, 0x9040), vt8251 }, { } /* terminate list */ }; diff --git a/drivers/atm/ambassador.c b/drivers/atm/ambassador.c index 703364b52170..66e181345b3a 100644 --- a/drivers/atm/ambassador.c +++ b/drivers/atm/ambassador.c @@ -1306,14 +1306,6 @@ static void amb_close (struct atm_vcc * atm_vcc) { return; } -/********** Set socket options for a VC **********/ - -// int amb_getsockopt (struct atm_vcc * atm_vcc, int level, int optname, void * optval, int optlen); - -/********** Set socket options for a VC **********/ - -// int amb_setsockopt (struct atm_vcc * atm_vcc, int level, int optname, void * optval, int optlen); - /********** Send **********/ static int amb_send (struct atm_vcc * atm_vcc, struct sk_buff * skb) { diff --git a/drivers/atm/eni.c b/drivers/atm/eni.c index 5503bfc8e132..0c3026145443 100644 --- a/drivers/atm/eni.c +++ b/drivers/atm/eni.c @@ -2031,7 +2031,7 @@ static int eni_getsockopt(struct atm_vcc *vcc,int level,int optname, static int eni_setsockopt(struct atm_vcc *vcc,int level,int optname, - void __user *optval,int optlen) + void __user *optval,unsigned int optlen) { return -EINVAL; } diff --git a/drivers/atm/firestream.c b/drivers/atm/firestream.c index b119640e1ee9..cd5049af47a9 100644 --- a/drivers/atm/firestream.c +++ b/drivers/atm/firestream.c @@ -1244,7 +1244,7 @@ static int fs_getsockopt(struct atm_vcc *vcc,int level,int optname, static int fs_setsockopt(struct atm_vcc *vcc,int level,int optname, - void __user *optval,int optlen) + void __user *optval,unsigned int optlen) { func_enter (); func_exit (); diff --git a/drivers/atm/fore200e.c b/drivers/atm/fore200e.c index 10f000dbe448..f766cc46b4c4 100644 --- a/drivers/atm/fore200e.c +++ b/drivers/atm/fore200e.c @@ -1795,7 +1795,7 @@ fore200e_getsockopt(struct atm_vcc* vcc, int level, int optname, void __user *op static int -fore200e_setsockopt(struct atm_vcc* vcc, int level, int optname, void __user *optval, int optlen) +fore200e_setsockopt(struct atm_vcc* vcc, int level, int optname, void __user *optval, unsigned int optlen) { /* struct fore200e* fore200e = FORE200E_DEV(vcc->dev); */ diff --git a/drivers/atm/he.c b/drivers/atm/he.c index 2de64065aa1b..70667033a568 100644 --- a/drivers/atm/he.c +++ b/drivers/atm/he.c @@ -790,11 +790,15 @@ he_init_group(struct he_dev *he_dev, int group) he_dev->rbps_base = pci_alloc_consistent(he_dev->pci_dev, CONFIG_RBPS_SIZE * sizeof(struct he_rbp), &he_dev->rbps_phys); if (he_dev->rbps_base == NULL) { - hprintk("failed to alloc rbps\n"); - return -ENOMEM; + hprintk("failed to alloc rbps_base\n"); + goto out_destroy_rbps_pool; } memset(he_dev->rbps_base, 0, CONFIG_RBPS_SIZE * sizeof(struct he_rbp)); he_dev->rbps_virt = kmalloc(CONFIG_RBPS_SIZE * sizeof(struct he_virt), GFP_KERNEL); + if (he_dev->rbps_virt == NULL) { + hprintk("failed to alloc rbps_virt\n"); + goto out_free_rbps_base; + } for (i = 0; i < CONFIG_RBPS_SIZE; ++i) { dma_addr_t dma_handle; @@ -802,7 +806,7 @@ he_init_group(struct he_dev *he_dev, int group) cpuaddr = pci_pool_alloc(he_dev->rbps_pool, GFP_KERNEL|GFP_DMA, &dma_handle); if (cpuaddr == NULL) - return -ENOMEM; + goto out_free_rbps_virt; he_dev->rbps_virt[i].virt = cpuaddr; he_dev->rbps_base[i].status = RBP_LOANED | RBP_SMALLBUF | (i << RBP_INDEX_OFF); @@ -827,17 +831,21 @@ he_init_group(struct he_dev *he_dev, int group) CONFIG_RBPL_BUFSIZE, 8, 0); if (he_dev->rbpl_pool == NULL) { hprintk("unable to create rbpl pool\n"); - return -ENOMEM; + goto out_free_rbps_virt; } he_dev->rbpl_base = pci_alloc_consistent(he_dev->pci_dev, CONFIG_RBPL_SIZE * sizeof(struct he_rbp), &he_dev->rbpl_phys); if (he_dev->rbpl_base == NULL) { - hprintk("failed to alloc rbpl\n"); - return -ENOMEM; + hprintk("failed to alloc rbpl_base\n"); + goto out_destroy_rbpl_pool; } memset(he_dev->rbpl_base, 0, CONFIG_RBPL_SIZE * sizeof(struct he_rbp)); he_dev->rbpl_virt = kmalloc(CONFIG_RBPL_SIZE * sizeof(struct he_virt), GFP_KERNEL); + if (he_dev->rbpl_virt == NULL) { + hprintk("failed to alloc rbpl_virt\n"); + goto out_free_rbpl_base; + } for (i = 0; i < CONFIG_RBPL_SIZE; ++i) { dma_addr_t dma_handle; @@ -845,7 +853,7 @@ he_init_group(struct he_dev *he_dev, int group) cpuaddr = pci_pool_alloc(he_dev->rbpl_pool, GFP_KERNEL|GFP_DMA, &dma_handle); if (cpuaddr == NULL) - return -ENOMEM; + goto out_free_rbpl_virt; he_dev->rbpl_virt[i].virt = cpuaddr; he_dev->rbpl_base[i].status = RBP_LOANED | (i << RBP_INDEX_OFF); @@ -870,7 +878,7 @@ he_init_group(struct he_dev *he_dev, int group) CONFIG_RBRQ_SIZE * sizeof(struct he_rbrq), &he_dev->rbrq_phys); if (he_dev->rbrq_base == NULL) { hprintk("failed to allocate rbrq\n"); - return -ENOMEM; + goto out_free_rbpl_virt; } memset(he_dev->rbrq_base, 0, CONFIG_RBRQ_SIZE * sizeof(struct he_rbrq)); @@ -894,7 +902,7 @@ he_init_group(struct he_dev *he_dev, int group) CONFIG_TBRQ_SIZE * sizeof(struct he_tbrq), &he_dev->tbrq_phys); if (he_dev->tbrq_base == NULL) { hprintk("failed to allocate tbrq\n"); - return -ENOMEM; + goto out_free_rbpq_base; } memset(he_dev->tbrq_base, 0, CONFIG_TBRQ_SIZE * sizeof(struct he_tbrq)); @@ -906,6 +914,39 @@ he_init_group(struct he_dev *he_dev, int group) he_writel(he_dev, CONFIG_TBRQ_THRESH, G0_TBRQ_THRESH + (group * 16)); return 0; + +out_free_rbpq_base: + pci_free_consistent(he_dev->pci_dev, CONFIG_RBRQ_SIZE * + sizeof(struct he_rbrq), he_dev->rbrq_base, + he_dev->rbrq_phys); + i = CONFIG_RBPL_SIZE; +out_free_rbpl_virt: + while (i--) + pci_pool_free(he_dev->rbpl_pool, he_dev->rbpl_virt[i].virt, + he_dev->rbpl_base[i].phys); + kfree(he_dev->rbpl_virt); + +out_free_rbpl_base: + pci_free_consistent(he_dev->pci_dev, CONFIG_RBPL_SIZE * + sizeof(struct he_rbp), he_dev->rbpl_base, + he_dev->rbpl_phys); +out_destroy_rbpl_pool: + pci_pool_destroy(he_dev->rbpl_pool); + + i = CONFIG_RBPS_SIZE; +out_free_rbps_virt: + while (i--) + pci_pool_free(he_dev->rbps_pool, he_dev->rbps_virt[i].virt, + he_dev->rbps_base[i].phys); + kfree(he_dev->rbps_virt); + +out_free_rbps_base: + pci_free_consistent(he_dev->pci_dev, CONFIG_RBPS_SIZE * + sizeof(struct he_rbp), he_dev->rbps_base, + he_dev->rbps_phys); +out_destroy_rbps_pool: + pci_pool_destroy(he_dev->rbps_pool); + return -ENOMEM; } static int __devinit diff --git a/drivers/atm/horizon.c b/drivers/atm/horizon.c index 01ce241dbeae..4e49021e67ee 100644 --- a/drivers/atm/horizon.c +++ b/drivers/atm/horizon.c @@ -2590,7 +2590,7 @@ static int hrz_getsockopt (struct atm_vcc * atm_vcc, int level, int optname, } static int hrz_setsockopt (struct atm_vcc * atm_vcc, int level, int optname, - void *optval, int optlen) { + void *optval, unsigned int optlen) { hrz_dev * dev = HRZ_DEV(atm_vcc->dev); PRINTD (DBG_FLOW|DBG_VCC, "hrz_setsockopt"); switch (level) { diff --git a/drivers/atm/iphase.c b/drivers/atm/iphase.c index 78c9736c3579..b2c1b37ab2e4 100644 --- a/drivers/atm/iphase.c +++ b/drivers/atm/iphase.c @@ -2862,7 +2862,7 @@ static int ia_getsockopt(struct atm_vcc *vcc, int level, int optname, } static int ia_setsockopt(struct atm_vcc *vcc, int level, int optname, - void __user *optval, int optlen) + void __user *optval, unsigned int optlen) { IF_EVENT(printk(">ia_setsockopt\n");) return -EINVAL; diff --git a/drivers/atm/solos-attrlist.c b/drivers/atm/solos-attrlist.c index efa2808dd94d..1a9332e4efe0 100644 --- a/drivers/atm/solos-attrlist.c +++ b/drivers/atm/solos-attrlist.c @@ -25,6 +25,10 @@ SOLOS_ATTR_RO(RSCorrectedErrorsUp) SOLOS_ATTR_RO(RSUnCorrectedErrorsUp) SOLOS_ATTR_RO(InterleaveRDn) SOLOS_ATTR_RO(InterleaveRUp) +SOLOS_ATTR_RO(BisRDn) +SOLOS_ATTR_RO(BisRUp) +SOLOS_ATTR_RO(INPdown) +SOLOS_ATTR_RO(INPup) SOLOS_ATTR_RO(ShowtimeStart) SOLOS_ATTR_RO(ATURVendor) SOLOS_ATTR_RO(ATUCCountry) @@ -62,6 +66,13 @@ SOLOS_ATTR_RW(Defaults) SOLOS_ATTR_RW(LineMode) SOLOS_ATTR_RW(Profile) SOLOS_ATTR_RW(DetectNoise) +SOLOS_ATTR_RW(BisAForceSNRMarginDn) +SOLOS_ATTR_RW(BisMForceSNRMarginDn) +SOLOS_ATTR_RW(BisAMaxMargin) +SOLOS_ATTR_RW(BisMMaxMargin) +SOLOS_ATTR_RW(AnnexAForceSNRMarginDn) +SOLOS_ATTR_RW(AnnexAMaxMargin) +SOLOS_ATTR_RW(AnnexMMaxMargin) SOLOS_ATTR_RO(SupportedAnnexes) SOLOS_ATTR_RO(Status) SOLOS_ATTR_RO(TotalStart) diff --git a/drivers/atm/solos-pci.c b/drivers/atm/solos-pci.c index 307321b32cb3..c5f5186d62a3 100644 --- a/drivers/atm/solos-pci.c +++ b/drivers/atm/solos-pci.c @@ -59,21 +59,29 @@ #define RX_DMA_ADDR(port) (0x30 + (4 * (port))) #define DATA_RAM_SIZE 32768 -#define BUF_SIZE 4096 +#define BUF_SIZE 2048 +#define OLD_BUF_SIZE 4096 /* For FPGA versions <= 2*/ #define FPGA_PAGE 528 /* FPGA flash page size*/ #define SOLOS_PAGE 512 /* Solos flash page size*/ #define FPGA_BLOCK (FPGA_PAGE * 8) /* FPGA flash block size*/ #define SOLOS_BLOCK (SOLOS_PAGE * 8) /* Solos flash block size*/ -#define RX_BUF(card, nr) ((card->buffers) + (nr)*BUF_SIZE*2) -#define TX_BUF(card, nr) ((card->buffers) + (nr)*BUF_SIZE*2 + BUF_SIZE) +#define RX_BUF(card, nr) ((card->buffers) + (nr)*(card->buffer_size)*2) +#define TX_BUF(card, nr) ((card->buffers) + (nr)*(card->buffer_size)*2 + (card->buffer_size)) +#define FLASH_BUF ((card->buffers) + 4*(card->buffer_size)*2) #define RX_DMA_SIZE 2048 +#define FPGA_VERSION(a,b) (((a) << 8) + (b)) +#define LEGACY_BUFFERS 2 +#define DMA_SUPPORTED 4 + static int reset = 0; static int atmdebug = 0; static int firmware_upgrade = 0; static int fpga_upgrade = 0; +static int db_firmware_upgrade = 0; +static int db_fpga_upgrade = 0; struct pkt_hdr { __le16 size; @@ -116,6 +124,8 @@ struct solos_card { wait_queue_head_t param_wq; wait_queue_head_t fw_wq; int using_dma; + int fpga_version; + int buffer_size; }; @@ -136,10 +146,14 @@ MODULE_PARM_DESC(reset, "Reset Solos chips on startup"); MODULE_PARM_DESC(atmdebug, "Print ATM data"); MODULE_PARM_DESC(firmware_upgrade, "Initiate Solos firmware upgrade"); MODULE_PARM_DESC(fpga_upgrade, "Initiate FPGA upgrade"); +MODULE_PARM_DESC(db_firmware_upgrade, "Initiate daughter board Solos firmware upgrade"); +MODULE_PARM_DESC(db_fpga_upgrade, "Initiate daughter board FPGA upgrade"); module_param(reset, int, 0444); module_param(atmdebug, int, 0644); module_param(firmware_upgrade, int, 0444); module_param(fpga_upgrade, int, 0444); +module_param(db_firmware_upgrade, int, 0444); +module_param(db_fpga_upgrade, int, 0444); static void fpga_queue(struct solos_card *card, int port, struct sk_buff *skb, struct atm_vcc *vcc); @@ -517,10 +531,32 @@ static int flash_upgrade(struct solos_card *card, int chip) if (chip == 0) { fw_name = "solos-FPGA.bin"; blocksize = FPGA_BLOCK; - } else { + } + + if (chip == 1) { fw_name = "solos-Firmware.bin"; blocksize = SOLOS_BLOCK; } + + if (chip == 2){ + if (card->fpga_version > LEGACY_BUFFERS){ + fw_name = "solos-db-FPGA.bin"; + blocksize = FPGA_BLOCK; + } else { + dev_info(&card->dev->dev, "FPGA version doesn't support daughter board upgrades\n"); + return -EPERM; + } + } + + if (chip == 3){ + if (card->fpga_version > LEGACY_BUFFERS){ + fw_name = "solos-Firmware.bin"; + blocksize = SOLOS_BLOCK; + } else { + dev_info(&card->dev->dev, "FPGA version doesn't support daughter board upgrades\n"); + return -EPERM; + } + } if (request_firmware(&fw, fw_name, &card->dev->dev)) return -ENOENT; @@ -536,8 +572,10 @@ static int flash_upgrade(struct solos_card *card, int chip) data32 = ioread32(card->config_regs + FPGA_MODE); /* Set mode to Chip Erase */ - dev_info(&card->dev->dev, "Set FPGA Flash mode to %s Chip Erase\n", - chip?"Solos":"FPGA"); + if(chip == 0 || chip == 2) + dev_info(&card->dev->dev, "Set FPGA Flash mode to FPGA Chip Erase\n"); + if(chip == 1 || chip == 3) + dev_info(&card->dev->dev, "Set FPGA Flash mode to Solos Chip Erase\n"); iowrite32((chip * 2), card->config_regs + FLASH_MODE); @@ -557,7 +595,10 @@ static int flash_upgrade(struct solos_card *card, int chip) /* Copy block to buffer, swapping each 16 bits */ for(i = 0; i < blocksize; i += 4) { uint32_t word = swahb32p((uint32_t *)(fw->data + offset + i)); - iowrite32(word, RX_BUF(card, 3) + i); + if(card->fpga_version > LEGACY_BUFFERS) + iowrite32(word, FLASH_BUF + i); + else + iowrite32(word, RX_BUF(card, 3) + i); } /* Specify block number and then trigger flash write */ @@ -630,6 +671,10 @@ void solos_bh(unsigned long card_arg) memcpy_fromio(header, RX_BUF(card, port), sizeof(*header)); size = le16_to_cpu(header->size); + if (size > (card->buffer_size - sizeof(*header))){ + dev_warn(&card->dev->dev, "Invalid buffer size\n"); + continue; + } skb = alloc_skb(size + 1, GFP_ATOMIC); if (!skb) { @@ -1094,12 +1139,18 @@ static int fpga_probe(struct pci_dev *dev, const struct pci_device_id *id) fpga_ver = (data32 & 0x0000FFFF); major_ver = ((data32 & 0xFF000000) >> 24); minor_ver = ((data32 & 0x00FF0000) >> 16); + card->fpga_version = FPGA_VERSION(major_ver,minor_ver); + if (card->fpga_version > LEGACY_BUFFERS) + card->buffer_size = BUF_SIZE; + else + card->buffer_size = OLD_BUF_SIZE; dev_info(&dev->dev, "Solos FPGA Version %d.%02d svn-%d\n", major_ver, minor_ver, fpga_ver); - if (0 && fpga_ver > 27) + if (card->fpga_version >= DMA_SUPPORTED){ card->using_dma = 1; - else { + } else { + card->using_dma = 0; /* Set RX empty flag for all ports */ iowrite32(0xF0, card->config_regs + FLAGS_ADDR); } @@ -1131,6 +1182,12 @@ static int fpga_probe(struct pci_dev *dev, const struct pci_device_id *id) if (firmware_upgrade) flash_upgrade(card, 1); + if (db_fpga_upgrade) + flash_upgrade(card, 2); + + if (db_firmware_upgrade) + flash_upgrade(card, 3); + err = atm_init(card); if (err) goto out_free_irq; diff --git a/drivers/atm/zatm.c b/drivers/atm/zatm.c index 752b1ba81f7e..2e9635be048c 100644 --- a/drivers/atm/zatm.c +++ b/drivers/atm/zatm.c @@ -1517,7 +1517,7 @@ static int zatm_getsockopt(struct atm_vcc *vcc,int level,int optname, static int zatm_setsockopt(struct atm_vcc *vcc,int level,int optname, - void __user *optval,int optlen) + void __user *optval,unsigned int optlen) { return -EINVAL; } diff --git a/drivers/base/bus.c b/drivers/base/bus.c index 973bf2ad4e0d..63c143e54a57 100644 --- a/drivers/base/bus.c +++ b/drivers/base/bus.c @@ -689,15 +689,19 @@ int bus_add_driver(struct device_driver *drv) printk(KERN_ERR "%s: driver_add_attrs(%s) failed\n", __func__, drv->name); } - error = add_bind_files(drv); - if (error) { - /* Ditto */ - printk(KERN_ERR "%s: add_bind_files(%s) failed\n", - __func__, drv->name); + + if (!drv->suppress_bind_attrs) { + error = add_bind_files(drv); + if (error) { + /* Ditto */ + printk(KERN_ERR "%s: add_bind_files(%s) failed\n", + __func__, drv->name); + } } kobject_uevent(&priv->kobj, KOBJ_ADD); return 0; + out_unregister: kfree(drv->p); drv->p = NULL; @@ -720,7 +724,8 @@ void bus_remove_driver(struct device_driver *drv) if (!drv->bus) return; - remove_bind_files(drv); + if (!drv->suppress_bind_attrs) + remove_bind_files(drv); driver_remove_attrs(drv->bus, drv); driver_remove_file(drv, &driver_attr_uevent); klist_remove(&drv->p->knode_bus); diff --git a/drivers/base/driver.c b/drivers/base/driver.c index ed2ebd3c287d..f367885a7646 100644 --- a/drivers/base/driver.c +++ b/drivers/base/driver.c @@ -236,7 +236,7 @@ int driver_register(struct device_driver *drv) put_driver(other); printk(KERN_ERR "Error: Driver '%s' is already registered, " "aborting...\n", drv->name); - return -EEXIST; + return -EBUSY; } ret = bus_add_driver(drv); diff --git a/drivers/base/platform.c b/drivers/base/platform.c index ed156a13aa40..4fa954b07ac4 100644 --- a/drivers/base/platform.c +++ b/drivers/base/platform.c @@ -521,11 +521,15 @@ int __init_or_module platform_driver_probe(struct platform_driver *drv, { int retval, code; + /* make sure driver won't have bind/unbind attributes */ + drv->driver.suppress_bind_attrs = true; + /* temporary section violation during probe() */ drv->probe = probe; retval = code = platform_driver_register(drv); - /* Fixup that section violation, being paranoid about code scanning + /* + * Fixup that section violation, being paranoid about code scanning * the list of drivers in order to probe new devices. Check to see * if the probe was successful, and make sure any forced probes of * new devices fail. diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c index e0dc4071e088..8aa2443182d5 100644 --- a/drivers/base/power/main.c +++ b/drivers/base/power/main.c @@ -511,6 +511,7 @@ static void dpm_complete(pm_message_t state) INIT_LIST_HEAD(&list); mutex_lock(&dpm_list_mtx); + transition_started = false; while (!list_empty(&dpm_list)) { struct device *dev = to_device(dpm_list.prev); diff --git a/drivers/base/power/runtime.c b/drivers/base/power/runtime.c index 38556f6cc22d..846d89e3d122 100644 --- a/drivers/base/power/runtime.c +++ b/drivers/base/power/runtime.c @@ -51,8 +51,6 @@ static int __pm_runtime_idle(struct device *dev) { int retval = 0; - dev_dbg(dev, "__pm_runtime_idle()!\n"); - if (dev->power.runtime_error) retval = -EINVAL; else if (dev->power.idle_notification) @@ -93,8 +91,6 @@ static int __pm_runtime_idle(struct device *dev) wake_up_all(&dev->power.wait_queue); out: - dev_dbg(dev, "__pm_runtime_idle() returns %d!\n", retval); - return retval; } @@ -332,11 +328,11 @@ int __pm_runtime_resume(struct device *dev, bool from_wq) * necessary. */ parent = dev->parent; - spin_unlock_irq(&dev->power.lock); + spin_unlock(&dev->power.lock); pm_runtime_get_noresume(parent); - spin_lock_irq(&parent->power.lock); + spin_lock(&parent->power.lock); /* * We can resume if the parent's run-time PM is disabled or it * is set to ignore children. @@ -347,9 +343,9 @@ int __pm_runtime_resume(struct device *dev, bool from_wq) if (parent->power.runtime_status != RPM_ACTIVE) retval = -EBUSY; } - spin_unlock_irq(&parent->power.lock); + spin_unlock(&parent->power.lock); - spin_lock_irq(&dev->power.lock); + spin_lock(&dev->power.lock); if (retval) goto out; goto repeat; @@ -781,7 +777,7 @@ int __pm_runtime_set_status(struct device *dev, unsigned int status) } if (parent) { - spin_lock_irq(&parent->power.lock); + spin_lock(&parent->power.lock); /* * It is invalid to put an active child under a parent that is @@ -797,7 +793,7 @@ int __pm_runtime_set_status(struct device *dev, unsigned int status) atomic_inc(&parent->power.child_count); } - spin_unlock_irq(&parent->power.lock); + spin_unlock(&parent->power.lock); if (error) goto out; diff --git a/drivers/block/DAC960.c b/drivers/block/DAC960.c index c77b6f3c28ea..eb4fa1943944 100644 --- a/drivers/block/DAC960.c +++ b/drivers/block/DAC960.c @@ -38,6 +38,7 @@ #include <linux/slab.h> #include <linux/smp_lock.h> #include <linux/proc_fs.h> +#include <linux/seq_file.h> #include <linux/reboot.h> #include <linux/spinlock.h> #include <linux/timer.h> @@ -6422,16 +6423,10 @@ static bool DAC960_V2_ExecuteUserCommand(DAC960_Controller_T *Controller, return true; } - -/* - DAC960_ProcReadStatus implements reading /proc/rd/status. -*/ - -static int DAC960_ProcReadStatus(char *Page, char **Start, off_t Offset, - int Count, int *EOF, void *Data) +static int dac960_proc_show(struct seq_file *m, void *v) { unsigned char *StatusMessage = "OK\n"; - int ControllerNumber, BytesAvailable; + int ControllerNumber; for (ControllerNumber = 0; ControllerNumber < DAC960_ControllerCount; ControllerNumber++) @@ -6444,52 +6439,49 @@ static int DAC960_ProcReadStatus(char *Page, char **Start, off_t Offset, break; } } - BytesAvailable = strlen(StatusMessage) - Offset; - if (Count >= BytesAvailable) - { - Count = BytesAvailable; - *EOF = true; - } - if (Count <= 0) return 0; - *Start = Page; - memcpy(Page, &StatusMessage[Offset], Count); - return Count; + seq_puts(m, StatusMessage); + return 0; } +static int dac960_proc_open(struct inode *inode, struct file *file) +{ + return single_open(file, dac960_proc_show, NULL); +} -/* - DAC960_ProcReadInitialStatus implements reading /proc/rd/cN/initial_status. -*/ +static const struct file_operations dac960_proc_fops = { + .owner = THIS_MODULE, + .open = dac960_proc_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; -static int DAC960_ProcReadInitialStatus(char *Page, char **Start, off_t Offset, - int Count, int *EOF, void *Data) +static int dac960_initial_status_proc_show(struct seq_file *m, void *v) { - DAC960_Controller_T *Controller = (DAC960_Controller_T *) Data; - int BytesAvailable = Controller->InitialStatusLength - Offset; - if (Count >= BytesAvailable) - { - Count = BytesAvailable; - *EOF = true; - } - if (Count <= 0) return 0; - *Start = Page; - memcpy(Page, &Controller->CombinedStatusBuffer[Offset], Count); - return Count; + DAC960_Controller_T *Controller = (DAC960_Controller_T *)m->private; + seq_printf(m, "%.*s", Controller->InitialStatusLength, Controller->CombinedStatusBuffer); + return 0; } +static int dac960_initial_status_proc_open(struct inode *inode, struct file *file) +{ + return single_open(file, dac960_initial_status_proc_show, PDE(inode)->data); +} -/* - DAC960_ProcReadCurrentStatus implements reading /proc/rd/cN/current_status. -*/ +static const struct file_operations dac960_initial_status_proc_fops = { + .owner = THIS_MODULE, + .open = dac960_initial_status_proc_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; -static int DAC960_ProcReadCurrentStatus(char *Page, char **Start, off_t Offset, - int Count, int *EOF, void *Data) +static int dac960_current_status_proc_show(struct seq_file *m, void *v) { - DAC960_Controller_T *Controller = (DAC960_Controller_T *) Data; + DAC960_Controller_T *Controller = (DAC960_Controller_T *) m->private; unsigned char *StatusMessage = "No Rebuild or Consistency Check in Progress\n"; int ProgressMessageLength = strlen(StatusMessage); - int BytesAvailable; if (jiffies != Controller->LastCurrentStatusTime) { Controller->CurrentStatusLength = 0; @@ -6513,56 +6505,48 @@ static int DAC960_ProcReadCurrentStatus(char *Page, char **Start, off_t Offset, } Controller->LastCurrentStatusTime = jiffies; } - BytesAvailable = Controller->CurrentStatusLength - Offset; - if (Count >= BytesAvailable) - { - Count = BytesAvailable; - *EOF = true; - } - if (Count <= 0) return 0; - *Start = Page; - memcpy(Page, &Controller->CurrentStatusBuffer[Offset], Count); - return Count; + seq_printf(m, "%.*s", Controller->CurrentStatusLength, Controller->CurrentStatusBuffer); + return 0; } +static int dac960_current_status_proc_open(struct inode *inode, struct file *file) +{ + return single_open(file, dac960_current_status_proc_show, PDE(inode)->data); +} -/* - DAC960_ProcReadUserCommand implements reading /proc/rd/cN/user_command. -*/ +static const struct file_operations dac960_current_status_proc_fops = { + .owner = THIS_MODULE, + .open = dac960_current_status_proc_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; -static int DAC960_ProcReadUserCommand(char *Page, char **Start, off_t Offset, - int Count, int *EOF, void *Data) +static int dac960_user_command_proc_show(struct seq_file *m, void *v) { - DAC960_Controller_T *Controller = (DAC960_Controller_T *) Data; - int BytesAvailable = Controller->UserStatusLength - Offset; - if (Count >= BytesAvailable) - { - Count = BytesAvailable; - *EOF = true; - } - if (Count <= 0) return 0; - *Start = Page; - memcpy(Page, &Controller->UserStatusBuffer[Offset], Count); - return Count; -} + DAC960_Controller_T *Controller = (DAC960_Controller_T *)m->private; + seq_printf(m, "%.*s", Controller->UserStatusLength, Controller->UserStatusBuffer); + return 0; +} -/* - DAC960_ProcWriteUserCommand implements writing /proc/rd/cN/user_command. -*/ +static int dac960_user_command_proc_open(struct inode *inode, struct file *file) +{ + return single_open(file, dac960_user_command_proc_show, PDE(inode)->data); +} -static int DAC960_ProcWriteUserCommand(struct file *file, +static ssize_t dac960_user_command_proc_write(struct file *file, const char __user *Buffer, - unsigned long Count, void *Data) + size_t Count, loff_t *pos) { - DAC960_Controller_T *Controller = (DAC960_Controller_T *) Data; + DAC960_Controller_T *Controller = (DAC960_Controller_T *) PDE(file->f_path.dentry->d_inode)->data; unsigned char CommandBuffer[80]; int Length; if (Count > sizeof(CommandBuffer)-1) return -EINVAL; if (copy_from_user(CommandBuffer, Buffer, Count)) return -EFAULT; CommandBuffer[Count] = '\0'; Length = strlen(CommandBuffer); - if (CommandBuffer[Length-1] == '\n') + if (Length > 0 && CommandBuffer[Length-1] == '\n') CommandBuffer[--Length] = '\0'; if (Controller->FirmwareType == DAC960_V1_Controller) return (DAC960_V1_ExecuteUserCommand(Controller, CommandBuffer) @@ -6572,6 +6556,14 @@ static int DAC960_ProcWriteUserCommand(struct file *file, ? Count : -EBUSY); } +static const struct file_operations dac960_user_command_proc_fops = { + .owner = THIS_MODULE, + .open = dac960_user_command_proc_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, + .write = dac960_user_command_proc_write, +}; /* DAC960_CreateProcEntries creates the /proc/rd/... entries for the @@ -6586,23 +6578,17 @@ static void DAC960_CreateProcEntries(DAC960_Controller_T *Controller) if (DAC960_ProcDirectoryEntry == NULL) { DAC960_ProcDirectoryEntry = proc_mkdir("rd", NULL); - StatusProcEntry = create_proc_read_entry("status", 0, + StatusProcEntry = proc_create("status", 0, DAC960_ProcDirectoryEntry, - DAC960_ProcReadStatus, NULL); + &dac960_proc_fops); } sprintf(Controller->ControllerName, "c%d", Controller->ControllerNumber); ControllerProcEntry = proc_mkdir(Controller->ControllerName, DAC960_ProcDirectoryEntry); - create_proc_read_entry("initial_status", 0, ControllerProcEntry, - DAC960_ProcReadInitialStatus, Controller); - create_proc_read_entry("current_status", 0, ControllerProcEntry, - DAC960_ProcReadCurrentStatus, Controller); - UserCommandProcEntry = - create_proc_read_entry("user_command", S_IWUSR | S_IRUSR, - ControllerProcEntry, DAC960_ProcReadUserCommand, - Controller); - UserCommandProcEntry->write_proc = DAC960_ProcWriteUserCommand; + proc_create_data("initial_status", 0, ControllerProcEntry, &dac960_initial_status_proc_fops, Controller); + proc_create_data("current_status", 0, ControllerProcEntry, &dac960_current_status_proc_fops, Controller); + UserCommandProcEntry = proc_create_data("user_command", S_IWUSR | S_IRUSR, ControllerProcEntry, &dac960_user_command_proc_fops, Controller); Controller->ControllerProcEntry = ControllerProcEntry; } diff --git a/drivers/block/cciss.c b/drivers/block/cciss.c index 4f19105f755c..92b126394fa1 100644 --- a/drivers/block/cciss.c +++ b/drivers/block/cciss.c @@ -36,9 +36,11 @@ #include <linux/proc_fs.h> #include <linux/seq_file.h> #include <linux/init.h> +#include <linux/jiffies.h> #include <linux/hdreg.h> #include <linux/spinlock.h> #include <linux/compat.h> +#include <linux/mutex.h> #include <asm/uaccess.h> #include <asm/io.h> @@ -66,6 +68,12 @@ MODULE_SUPPORTED_DEVICE("HP SA5i SA5i+ SA532 SA5300 SA5312 SA641 SA642 SA6400" MODULE_VERSION("3.6.20"); MODULE_LICENSE("GPL"); +static int cciss_allow_hpsa; +module_param(cciss_allow_hpsa, int, S_IRUGO|S_IWUSR); +MODULE_PARM_DESC(cciss_allow_hpsa, + "Prevent cciss driver from accessing hardware known to be " + " supported by the hpsa driver"); + #include "cciss_cmd.h" #include "cciss.h" #include <linux/cciss_ioctl.h> @@ -99,8 +107,6 @@ static const struct pci_device_id cciss_pci_device_id[] = { {PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_CISSE, 0x103C, 0x3249}, {PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_CISSE, 0x103C, 0x324A}, {PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_CISSE, 0x103C, 0x324B}, - {PCI_VENDOR_ID_HP, PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, - PCI_CLASS_STORAGE_RAID << 8, 0xffff << 8, 0}, {0,} }; @@ -121,8 +127,6 @@ static struct board_type products[] = { {0x409D0E11, "Smart Array 6400 EM", &SA5_access}, {0x40910E11, "Smart Array 6i", &SA5_access}, {0x3225103C, "Smart Array P600", &SA5_access}, - {0x3223103C, "Smart Array P800", &SA5_access}, - {0x3234103C, "Smart Array P400", &SA5_access}, {0x3235103C, "Smart Array P400i", &SA5_access}, {0x3211103C, "Smart Array E200i", &SA5_access}, {0x3212103C, "Smart Array E200", &SA5_access}, @@ -130,6 +134,10 @@ static struct board_type products[] = { {0x3214103C, "Smart Array E200i", &SA5_access}, {0x3215103C, "Smart Array E200i", &SA5_access}, {0x3237103C, "Smart Array E500", &SA5_access}, +/* controllers below this line are also supported by the hpsa driver. */ +#define HPSA_BOUNDARY 0x3223103C + {0x3223103C, "Smart Array P800", &SA5_access}, + {0x3234103C, "Smart Array P400", &SA5_access}, {0x323D103C, "Smart Array P700m", &SA5_access}, {0x3241103C, "Smart Array P212", &SA5_access}, {0x3243103C, "Smart Array P410", &SA5_access}, @@ -138,7 +146,6 @@ static struct board_type products[] = { {0x3249103C, "Smart Array P812", &SA5_access}, {0x324A103C, "Smart Array P712m", &SA5_access}, {0x324B103C, "Smart Array P711m", &SA5_access}, - {0xFFFF103C, "Unknown Smart Array", &SA5_access}, }; /* How long to wait (in milliseconds) for board to go into simple mode */ @@ -155,6 +162,10 @@ static struct board_type products[] = { static ctlr_info_t *hba[MAX_CTLR]; +static struct task_struct *cciss_scan_thread; +static DEFINE_MUTEX(scan_mutex); +static LIST_HEAD(scan_q); + static void do_cciss_request(struct request_queue *q); static irqreturn_t do_cciss_intr(int irq, void *dev_id); static int cciss_open(struct block_device *bdev, fmode_t mode); @@ -164,9 +175,9 @@ static int cciss_ioctl(struct block_device *bdev, fmode_t mode, static int cciss_getgeo(struct block_device *bdev, struct hd_geometry *geo); static int cciss_revalidate(struct gendisk *disk); -static int rebuild_lun_table(ctlr_info_t *h, int first_time); +static int rebuild_lun_table(ctlr_info_t *h, int first_time, int via_ioctl); static int deregister_disk(ctlr_info_t *h, int drv_index, - int clear_all); + int clear_all, int via_ioctl); static void cciss_read_capacity(int ctlr, int logvol, int withirq, sector_t *total_size, unsigned int *block_size); @@ -189,8 +200,13 @@ static int sendcmd_withirq_core(ctlr_info_t *h, CommandList_struct *c, static int process_sendcmd_error(ctlr_info_t *h, CommandList_struct *c); static void fail_all_cmds(unsigned long ctlr); +static int add_to_scan_list(struct ctlr_info *h); static int scan_thread(void *data); static int check_for_unit_attention(ctlr_info_t *h, CommandList_struct *c); +static void cciss_hba_release(struct device *dev); +static void cciss_device_release(struct device *dev); +static void cciss_free_gendisk(ctlr_info_t *h, int drv_index); +static void cciss_free_drive_info(ctlr_info_t *h, int drv_index); #ifdef CONFIG_PROC_FS static void cciss_procinit(int i); @@ -245,7 +261,10 @@ static inline void removeQ(CommandList_struct *c) #include "cciss_scsi.c" /* For SCSI tape support */ -#define RAID_UNKNOWN 6 +static const char *raid_label[] = { "0", "4", "1(1+0)", "5", "5+1", "ADG", + "UNKNOWN" +}; +#define RAID_UNKNOWN (sizeof(raid_label) / sizeof(raid_label[0])-1) #ifdef CONFIG_PROC_FS @@ -255,9 +274,6 @@ static inline void removeQ(CommandList_struct *c) #define ENG_GIG 1000000000 #define ENG_GIG_FACTOR (ENG_GIG/512) #define ENGAGE_SCSI "engage scsi" -static const char *raid_label[] = { "0", "4", "1(1+0)", "5", "5+1", "ADG", - "UNKNOWN" -}; static struct proc_dir_entry *proc_cciss; @@ -318,7 +334,7 @@ static int cciss_seq_show(struct seq_file *seq, void *v) ctlr_info_t *h = seq->private; unsigned ctlr = h->ctlr; loff_t *pos = v; - drive_info_struct *drv = &h->drv[*pos]; + drive_info_struct *drv = h->drv[*pos]; if (*pos > h->highest_lun) return 0; @@ -331,7 +347,7 @@ static int cciss_seq_show(struct seq_file *seq, void *v) vol_sz_frac *= 100; sector_div(vol_sz_frac, ENG_GIG_FACTOR); - if (drv->raid_level > 5) + if (drv->raid_level < 0 || drv->raid_level > RAID_UNKNOWN) drv->raid_level = RAID_UNKNOWN; seq_printf(seq, "cciss/c%dd%d:" "\t%4u.%02uGB\tRAID %s\n", @@ -363,7 +379,7 @@ static void cciss_seq_stop(struct seq_file *seq, void *v) h->busy_configuring = 0; } -static struct seq_operations cciss_seq_ops = { +static const struct seq_operations cciss_seq_ops = { .start = cciss_seq_start, .show = cciss_seq_show, .next = cciss_seq_next, @@ -426,7 +442,7 @@ out: return err; } -static struct file_operations cciss_proc_fops = { +static const struct file_operations cciss_proc_fops = { .owner = THIS_MODULE, .open = cciss_seq_open, .read = seq_read, @@ -454,9 +470,19 @@ static void __devinit cciss_procinit(int i) #define to_hba(n) container_of(n, struct ctlr_info, dev) #define to_drv(n) container_of(n, drive_info_struct, dev) -static struct device_type cciss_host_type = { - .name = "cciss_host", -}; +static ssize_t host_store_rescan(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct ctlr_info *h = to_hba(dev); + + add_to_scan_list(h); + wake_up_process(cciss_scan_thread); + wait_for_completion_interruptible(&h->scan_wait); + + return count; +} +static DEVICE_ATTR(rescan, S_IWUSR, NULL, host_store_rescan); static ssize_t dev_show_unique_id(struct device *dev, struct device_attribute *attr, @@ -486,7 +512,7 @@ static ssize_t dev_show_unique_id(struct device *dev, sn[8], sn[9], sn[10], sn[11], sn[12], sn[13], sn[14], sn[15]); } -DEVICE_ATTR(unique_id, S_IRUGO, dev_show_unique_id, NULL); +static DEVICE_ATTR(unique_id, S_IRUGO, dev_show_unique_id, NULL); static ssize_t dev_show_vendor(struct device *dev, struct device_attribute *attr, @@ -510,7 +536,7 @@ static ssize_t dev_show_vendor(struct device *dev, else return snprintf(buf, sizeof(vendor) + 1, "%s\n", drv->vendor); } -DEVICE_ATTR(vendor, S_IRUGO, dev_show_vendor, NULL); +static DEVICE_ATTR(vendor, S_IRUGO, dev_show_vendor, NULL); static ssize_t dev_show_model(struct device *dev, struct device_attribute *attr, @@ -534,7 +560,7 @@ static ssize_t dev_show_model(struct device *dev, else return snprintf(buf, sizeof(model) + 1, "%s\n", drv->model); } -DEVICE_ATTR(model, S_IRUGO, dev_show_model, NULL); +static DEVICE_ATTR(model, S_IRUGO, dev_show_model, NULL); static ssize_t dev_show_rev(struct device *dev, struct device_attribute *attr, @@ -558,13 +584,103 @@ static ssize_t dev_show_rev(struct device *dev, else return snprintf(buf, sizeof(rev) + 1, "%s\n", drv->rev); } -DEVICE_ATTR(rev, S_IRUGO, dev_show_rev, NULL); +static DEVICE_ATTR(rev, S_IRUGO, dev_show_rev, NULL); + +static ssize_t cciss_show_lunid(struct device *dev, + struct device_attribute *attr, char *buf) +{ + drive_info_struct *drv = to_drv(dev); + struct ctlr_info *h = to_hba(drv->dev.parent); + unsigned long flags; + unsigned char lunid[8]; + + spin_lock_irqsave(CCISS_LOCK(h->ctlr), flags); + if (h->busy_configuring) { + spin_unlock_irqrestore(CCISS_LOCK(h->ctlr), flags); + return -EBUSY; + } + if (!drv->heads) { + spin_unlock_irqrestore(CCISS_LOCK(h->ctlr), flags); + return -ENOTTY; + } + memcpy(lunid, drv->LunID, sizeof(lunid)); + spin_unlock_irqrestore(CCISS_LOCK(h->ctlr), flags); + return snprintf(buf, 20, "0x%02x%02x%02x%02x%02x%02x%02x%02x\n", + lunid[0], lunid[1], lunid[2], lunid[3], + lunid[4], lunid[5], lunid[6], lunid[7]); +} +static DEVICE_ATTR(lunid, S_IRUGO, cciss_show_lunid, NULL); + +static ssize_t cciss_show_raid_level(struct device *dev, + struct device_attribute *attr, char *buf) +{ + drive_info_struct *drv = to_drv(dev); + struct ctlr_info *h = to_hba(drv->dev.parent); + int raid; + unsigned long flags; + + spin_lock_irqsave(CCISS_LOCK(h->ctlr), flags); + if (h->busy_configuring) { + spin_unlock_irqrestore(CCISS_LOCK(h->ctlr), flags); + return -EBUSY; + } + raid = drv->raid_level; + spin_unlock_irqrestore(CCISS_LOCK(h->ctlr), flags); + if (raid < 0 || raid > RAID_UNKNOWN) + raid = RAID_UNKNOWN; + + return snprintf(buf, strlen(raid_label[raid]) + 7, "RAID %s\n", + raid_label[raid]); +} +static DEVICE_ATTR(raid_level, S_IRUGO, cciss_show_raid_level, NULL); + +static ssize_t cciss_show_usage_count(struct device *dev, + struct device_attribute *attr, char *buf) +{ + drive_info_struct *drv = to_drv(dev); + struct ctlr_info *h = to_hba(drv->dev.parent); + unsigned long flags; + int count; + + spin_lock_irqsave(CCISS_LOCK(h->ctlr), flags); + if (h->busy_configuring) { + spin_unlock_irqrestore(CCISS_LOCK(h->ctlr), flags); + return -EBUSY; + } + count = drv->usage_count; + spin_unlock_irqrestore(CCISS_LOCK(h->ctlr), flags); + return snprintf(buf, 20, "%d\n", count); +} +static DEVICE_ATTR(usage_count, S_IRUGO, cciss_show_usage_count, NULL); + +static struct attribute *cciss_host_attrs[] = { + &dev_attr_rescan.attr, + NULL +}; + +static struct attribute_group cciss_host_attr_group = { + .attrs = cciss_host_attrs, +}; + +static const struct attribute_group *cciss_host_attr_groups[] = { + &cciss_host_attr_group, + NULL +}; + +static struct device_type cciss_host_type = { + .name = "cciss_host", + .groups = cciss_host_attr_groups, + .release = cciss_hba_release, +}; static struct attribute *cciss_dev_attrs[] = { &dev_attr_unique_id.attr, &dev_attr_model.attr, &dev_attr_vendor.attr, &dev_attr_rev.attr, + &dev_attr_lunid.attr, + &dev_attr_raid_level.attr, + &dev_attr_usage_count.attr, NULL }; @@ -580,12 +696,24 @@ static const struct attribute_group *cciss_dev_attr_groups[] = { static struct device_type cciss_dev_type = { .name = "cciss_device", .groups = cciss_dev_attr_groups, + .release = cciss_device_release, }; static struct bus_type cciss_bus_type = { .name = "cciss", }; +/* + * cciss_hba_release is called when the reference count + * of h->dev goes to zero. + */ +static void cciss_hba_release(struct device *dev) +{ + /* + * nothing to do, but need this to avoid a warning + * about not having a release handler from lib/kref.c. + */ +} /* * Initialize sysfs entry for each controller. This sets up and registers @@ -609,6 +737,16 @@ static int cciss_create_hba_sysfs_entry(struct ctlr_info *h) static void cciss_destroy_hba_sysfs_entry(struct ctlr_info *h) { device_del(&h->dev); + put_device(&h->dev); /* final put. */ +} + +/* cciss_device_release is called when the reference count + * of h->drv[x]dev goes to zero. + */ +static void cciss_device_release(struct device *dev) +{ + drive_info_struct *drv = to_drv(dev); + kfree(drv); } /* @@ -617,24 +755,39 @@ static void cciss_destroy_hba_sysfs_entry(struct ctlr_info *h) * /sys/bus/pci/devices/<dev/ccis#/. We also create a link from * /sys/block/cciss!c#d# to this entry. */ -static int cciss_create_ld_sysfs_entry(struct ctlr_info *h, - drive_info_struct *drv, +static long cciss_create_ld_sysfs_entry(struct ctlr_info *h, int drv_index) { - device_initialize(&drv->dev); - drv->dev.type = &cciss_dev_type; - drv->dev.bus = &cciss_bus_type; - dev_set_name(&drv->dev, "c%dd%d", h->ctlr, drv_index); - drv->dev.parent = &h->dev; - return device_add(&drv->dev); + struct device *dev; + + if (h->drv[drv_index]->device_initialized) + return 0; + + dev = &h->drv[drv_index]->dev; + device_initialize(dev); + dev->type = &cciss_dev_type; + dev->bus = &cciss_bus_type; + dev_set_name(dev, "c%dd%d", h->ctlr, drv_index); + dev->parent = &h->dev; + h->drv[drv_index]->device_initialized = 1; + return device_add(dev); } /* * Remove sysfs entries for a logical drive. */ -static void cciss_destroy_ld_sysfs_entry(drive_info_struct *drv) +static void cciss_destroy_ld_sysfs_entry(struct ctlr_info *h, int drv_index, + int ctlr_exiting) { - device_del(&drv->dev); + struct device *dev = &h->drv[drv_index]->dev; + + /* special case for c*d0, we only destroy it on controller exit */ + if (drv_index == 0 && !ctlr_exiting) + return; + + device_del(dev); + put_device(dev); /* the "final" put. */ + h->drv[drv_index] = NULL; } /* @@ -751,7 +904,7 @@ static int cciss_open(struct block_device *bdev, fmode_t mode) printk(KERN_DEBUG "cciss_open %s\n", bdev->bd_disk->disk_name); #endif /* CCISS_DEBUG */ - if (host->busy_initializing || drv->busy_configuring) + if (drv->busy_configuring) return -EBUSY; /* * Root is allowed to open raw volume zero even if it's not configured @@ -767,7 +920,8 @@ static int cciss_open(struct block_device *bdev, fmode_t mode) if (MINOR(bdev->bd_dev) & 0x0f) { return -ENXIO; /* if it is, make sure we have a LUN ID */ - } else if (drv->LunID == 0) { + } else if (memcmp(drv->LunID, CTLR_LUNID, + sizeof(drv->LunID))) { return -ENXIO; } } @@ -1132,12 +1286,13 @@ static int cciss_ioctl(struct block_device *bdev, fmode_t mode, case CCISS_DEREGDISK: case CCISS_REGNEWD: case CCISS_REVALIDVOLS: - return rebuild_lun_table(host, 0); + return rebuild_lun_table(host, 0, 1); case CCISS_GETLUNINFO:{ LogvolInfo_struct luninfo; - luninfo.LunID = drv->LunID; + memcpy(&luninfo.LunID, drv->LunID, + sizeof(luninfo.LunID)); luninfo.num_opens = drv->usage_count; luninfo.num_parts = 0; if (copy_to_user(argp, &luninfo, @@ -1475,7 +1630,10 @@ static void cciss_check_queues(ctlr_info_t *h) /* make sure the disk has been added and the drive is real * because this can be called from the middle of init_one. */ - if (!(h->drv[curr_queue].queue) || !(h->drv[curr_queue].heads)) + if (!h->drv[curr_queue]) + continue; + if (!(h->drv[curr_queue]->queue) || + !(h->drv[curr_queue]->heads)) continue; blk_start_queue(h->gendisk[curr_queue]->queue); @@ -1532,13 +1690,11 @@ static void cciss_softirq_done(struct request *rq) spin_unlock_irqrestore(&h->lock, flags); } -static void log_unit_to_scsi3addr(ctlr_info_t *h, unsigned char scsi3addr[], - uint32_t log_unit) +static inline void log_unit_to_scsi3addr(ctlr_info_t *h, + unsigned char scsi3addr[], uint32_t log_unit) { - log_unit = h->drv[log_unit].LunID & 0x03fff; - memset(&scsi3addr[4], 0, 4); - memcpy(&scsi3addr[0], &log_unit, 4); - scsi3addr[3] |= 0x40; + memcpy(scsi3addr, h->drv[log_unit]->LunID, + sizeof(h->drv[log_unit]->LunID)); } /* This function gets the SCSI vendor, model, and revision of a logical drive @@ -1615,16 +1771,23 @@ static void cciss_get_serial_no(int ctlr, int logvol, int withirq, return; } -static void cciss_add_disk(ctlr_info_t *h, struct gendisk *disk, +/* + * cciss_add_disk sets up the block device queue for a logical drive + */ +static int cciss_add_disk(ctlr_info_t *h, struct gendisk *disk, int drv_index) { disk->queue = blk_init_queue(do_cciss_request, &h->lock); + if (!disk->queue) + goto init_queue_failure; sprintf(disk->disk_name, "cciss/c%dd%d", h->ctlr, drv_index); disk->major = h->major; disk->first_minor = drv_index << NWD_SHIFT; disk->fops = &cciss_fops; - disk->private_data = &h->drv[drv_index]; - disk->driverfs_dev = &h->drv[drv_index].dev; + if (cciss_create_ld_sysfs_entry(h, drv_index)) + goto cleanup_queue; + disk->private_data = h->drv[drv_index]; + disk->driverfs_dev = &h->drv[drv_index]->dev; /* Set up queue information */ blk_queue_bounce_limit(disk->queue, h->pdev->dma_mask); @@ -1642,14 +1805,21 @@ static void cciss_add_disk(ctlr_info_t *h, struct gendisk *disk, disk->queue->queuedata = h; blk_queue_logical_block_size(disk->queue, - h->drv[drv_index].block_size); + h->drv[drv_index]->block_size); /* Make sure all queue data is written out before */ - /* setting h->drv[drv_index].queue, as setting this */ + /* setting h->drv[drv_index]->queue, as setting this */ /* allows the interrupt handler to start the queue */ wmb(); - h->drv[drv_index].queue = disk->queue; + h->drv[drv_index]->queue = disk->queue; add_disk(disk); + return 0; + +cleanup_queue: + blk_cleanup_queue(disk->queue); + disk->queue = NULL; +init_queue_failure: + return -1; } /* This function will check the usage_count of the drive to be updated/added. @@ -1662,7 +1832,8 @@ static void cciss_add_disk(ctlr_info_t *h, struct gendisk *disk, * is also the controller node. Any changes to disk 0 will show up on * the next reboot. */ -static void cciss_update_drive_info(int ctlr, int drv_index, int first_time) +static void cciss_update_drive_info(int ctlr, int drv_index, int first_time, + int via_ioctl) { ctlr_info_t *h = hba[ctlr]; struct gendisk *disk; @@ -1672,21 +1843,13 @@ static void cciss_update_drive_info(int ctlr, int drv_index, int first_time) unsigned long flags = 0; int ret = 0; drive_info_struct *drvinfo; - int was_only_controller_node; /* Get information about the disk and modify the driver structure */ inq_buff = kmalloc(sizeof(InquiryData_struct), GFP_KERNEL); - drvinfo = kmalloc(sizeof(*drvinfo), GFP_KERNEL); + drvinfo = kzalloc(sizeof(*drvinfo), GFP_KERNEL); if (inq_buff == NULL || drvinfo == NULL) goto mem_msg; - /* See if we're trying to update the "controller node" - * this will happen the when the first logical drive gets - * created by ACU. - */ - was_only_controller_node = (drv_index == 0 && - h->drv[0].raid_level == -1); - /* testing to see if 16-byte CDBs are already being used */ if (h->cciss_read == CCISS_READ_16) { cciss_read_capacity_16(h->ctlr, drv_index, 1, @@ -1719,16 +1882,19 @@ static void cciss_update_drive_info(int ctlr, int drv_index, int first_time) drvinfo->model, drvinfo->rev); cciss_get_serial_no(ctlr, drv_index, 1, drvinfo->serial_no, sizeof(drvinfo->serial_no)); + /* Save the lunid in case we deregister the disk, below. */ + memcpy(drvinfo->LunID, h->drv[drv_index]->LunID, + sizeof(drvinfo->LunID)); /* Is it the same disk we already know, and nothing's changed? */ - if (h->drv[drv_index].raid_level != -1 && + if (h->drv[drv_index]->raid_level != -1 && ((memcmp(drvinfo->serial_no, - h->drv[drv_index].serial_no, 16) == 0) && - drvinfo->block_size == h->drv[drv_index].block_size && - drvinfo->nr_blocks == h->drv[drv_index].nr_blocks && - drvinfo->heads == h->drv[drv_index].heads && - drvinfo->sectors == h->drv[drv_index].sectors && - drvinfo->cylinders == h->drv[drv_index].cylinders)) + h->drv[drv_index]->serial_no, 16) == 0) && + drvinfo->block_size == h->drv[drv_index]->block_size && + drvinfo->nr_blocks == h->drv[drv_index]->nr_blocks && + drvinfo->heads == h->drv[drv_index]->heads && + drvinfo->sectors == h->drv[drv_index]->sectors && + drvinfo->cylinders == h->drv[drv_index]->cylinders)) /* The disk is unchanged, nothing to update */ goto freeret; @@ -1738,18 +1904,17 @@ static void cciss_update_drive_info(int ctlr, int drv_index, int first_time) * If the disk already exists then deregister it before proceeding * (unless it's the first disk (for the controller node). */ - if (h->drv[drv_index].raid_level != -1 && drv_index != 0) { + if (h->drv[drv_index]->raid_level != -1 && drv_index != 0) { printk(KERN_WARNING "disk %d has changed.\n", drv_index); spin_lock_irqsave(CCISS_LOCK(h->ctlr), flags); - h->drv[drv_index].busy_configuring = 1; + h->drv[drv_index]->busy_configuring = 1; spin_unlock_irqrestore(CCISS_LOCK(h->ctlr), flags); - /* deregister_disk sets h->drv[drv_index].queue = NULL + /* deregister_disk sets h->drv[drv_index]->queue = NULL * which keeps the interrupt handler from starting * the queue. */ - ret = deregister_disk(h, drv_index, 0); - h->drv[drv_index].busy_configuring = 0; + ret = deregister_disk(h, drv_index, 0, via_ioctl); } /* If the disk is in use return */ @@ -1757,22 +1922,31 @@ static void cciss_update_drive_info(int ctlr, int drv_index, int first_time) goto freeret; /* Save the new information from cciss_geometry_inquiry - * and serial number inquiry. + * and serial number inquiry. If the disk was deregistered + * above, then h->drv[drv_index] will be NULL. */ - h->drv[drv_index].block_size = drvinfo->block_size; - h->drv[drv_index].nr_blocks = drvinfo->nr_blocks; - h->drv[drv_index].heads = drvinfo->heads; - h->drv[drv_index].sectors = drvinfo->sectors; - h->drv[drv_index].cylinders = drvinfo->cylinders; - h->drv[drv_index].raid_level = drvinfo->raid_level; - memcpy(h->drv[drv_index].serial_no, drvinfo->serial_no, 16); - memcpy(h->drv[drv_index].vendor, drvinfo->vendor, VENDOR_LEN + 1); - memcpy(h->drv[drv_index].model, drvinfo->model, MODEL_LEN + 1); - memcpy(h->drv[drv_index].rev, drvinfo->rev, REV_LEN + 1); + if (h->drv[drv_index] == NULL) { + drvinfo->device_initialized = 0; + h->drv[drv_index] = drvinfo; + drvinfo = NULL; /* so it won't be freed below. */ + } else { + /* special case for cxd0 */ + h->drv[drv_index]->block_size = drvinfo->block_size; + h->drv[drv_index]->nr_blocks = drvinfo->nr_blocks; + h->drv[drv_index]->heads = drvinfo->heads; + h->drv[drv_index]->sectors = drvinfo->sectors; + h->drv[drv_index]->cylinders = drvinfo->cylinders; + h->drv[drv_index]->raid_level = drvinfo->raid_level; + memcpy(h->drv[drv_index]->serial_no, drvinfo->serial_no, 16); + memcpy(h->drv[drv_index]->vendor, drvinfo->vendor, + VENDOR_LEN + 1); + memcpy(h->drv[drv_index]->model, drvinfo->model, MODEL_LEN + 1); + memcpy(h->drv[drv_index]->rev, drvinfo->rev, REV_LEN + 1); + } ++h->num_luns; disk = h->gendisk[drv_index]; - set_capacity(disk, h->drv[drv_index].nr_blocks); + set_capacity(disk, h->drv[drv_index]->nr_blocks); /* If it's not disk 0 (drv_index != 0) * or if it was disk 0, but there was previously @@ -1780,8 +1954,15 @@ static void cciss_update_drive_info(int ctlr, int drv_index, int first_time) * (raid_leve == -1) then we want to update the * logical drive's information. */ - if (drv_index || first_time) - cciss_add_disk(h, disk, drv_index); + if (drv_index || first_time) { + if (cciss_add_disk(h, disk, drv_index) != 0) { + cciss_free_gendisk(h, drv_index); + cciss_free_drive_info(h, drv_index); + printk(KERN_WARNING "cciss:%d could not update " + "disk %d\n", h->ctlr, drv_index); + --h->num_luns; + } + } freeret: kfree(inq_buff); @@ -1793,28 +1974,70 @@ mem_msg: } /* This function will find the first index of the controllers drive array - * that has a -1 for the raid_level and will return that index. This is - * where new drives will be added. If the index to be returned is greater - * than the highest_lun index for the controller then highest_lun is set - * to this new index. If there are no available indexes then -1 is returned. - * "controller_node" is used to know if this is a real logical drive, or just - * the controller node, which determines if this counts towards highest_lun. + * that has a null drv pointer and allocate the drive info struct and + * will return that index This is where new drives will be added. + * If the index to be returned is greater than the highest_lun index for + * the controller then highest_lun is set * to this new index. + * If there are no available indexes or if tha allocation fails, then -1 + * is returned. * "controller_node" is used to know if this is a real + * logical drive, or just the controller node, which determines if this + * counts towards highest_lun. */ -static int cciss_find_free_drive_index(int ctlr, int controller_node) +static int cciss_alloc_drive_info(ctlr_info_t *h, int controller_node) { int i; + drive_info_struct *drv; + /* Search for an empty slot for our drive info */ for (i = 0; i < CISS_MAX_LUN; i++) { - if (hba[ctlr]->drv[i].raid_level == -1) { - if (i > hba[ctlr]->highest_lun) - if (!controller_node) - hba[ctlr]->highest_lun = i; + + /* if not cxd0 case, and it's occupied, skip it. */ + if (h->drv[i] && i != 0) + continue; + /* + * If it's cxd0 case, and drv is alloc'ed already, and a + * disk is configured there, skip it. + */ + if (i == 0 && h->drv[i] && h->drv[i]->raid_level != -1) + continue; + + /* + * We've found an empty slot. Update highest_lun + * provided this isn't just the fake cxd0 controller node. + */ + if (i > h->highest_lun && !controller_node) + h->highest_lun = i; + + /* If adding a real disk at cxd0, and it's already alloc'ed */ + if (i == 0 && h->drv[i] != NULL) return i; - } + + /* + * Found an empty slot, not already alloc'ed. Allocate it. + * Mark it with raid_level == -1, so we know it's new later on. + */ + drv = kzalloc(sizeof(*drv), GFP_KERNEL); + if (!drv) + return -1; + drv->raid_level = -1; /* so we know it's new */ + h->drv[i] = drv; + return i; } return -1; } +static void cciss_free_drive_info(ctlr_info_t *h, int drv_index) +{ + kfree(h->drv[drv_index]); + h->drv[drv_index] = NULL; +} + +static void cciss_free_gendisk(ctlr_info_t *h, int drv_index) +{ + put_disk(h->gendisk[drv_index]); + h->gendisk[drv_index] = NULL; +} + /* cciss_add_gendisk finds a free hba[]->drv structure * and allocates a gendisk if needed, and sets the lunid * in the drvinfo structure. It returns the index into @@ -1824,13 +2047,15 @@ static int cciss_find_free_drive_index(int ctlr, int controller_node) * a means to talk to the controller in case no logical * drives have yet been configured. */ -static int cciss_add_gendisk(ctlr_info_t *h, __u32 lunid, int controller_node) +static int cciss_add_gendisk(ctlr_info_t *h, unsigned char lunid[], + int controller_node) { int drv_index; - drv_index = cciss_find_free_drive_index(h->ctlr, controller_node); + drv_index = cciss_alloc_drive_info(h, controller_node); if (drv_index == -1) return -1; + /*Check if the gendisk needs to be allocated */ if (!h->gendisk[drv_index]) { h->gendisk[drv_index] = @@ -1839,23 +2064,24 @@ static int cciss_add_gendisk(ctlr_info_t *h, __u32 lunid, int controller_node) printk(KERN_ERR "cciss%d: could not " "allocate a new disk %d\n", h->ctlr, drv_index); - return -1; + goto err_free_drive_info; } } - h->drv[drv_index].LunID = lunid; - if (cciss_create_ld_sysfs_entry(h, &h->drv[drv_index], drv_index)) + memcpy(h->drv[drv_index]->LunID, lunid, + sizeof(h->drv[drv_index]->LunID)); + if (cciss_create_ld_sysfs_entry(h, drv_index)) goto err_free_disk; - /* Don't need to mark this busy because nobody */ /* else knows about this disk yet to contend */ /* for access to it. */ - h->drv[drv_index].busy_configuring = 0; + h->drv[drv_index]->busy_configuring = 0; wmb(); return drv_index; err_free_disk: - put_disk(h->gendisk[drv_index]); - h->gendisk[drv_index] = NULL; + cciss_free_gendisk(h, drv_index); +err_free_drive_info: + cciss_free_drive_info(h, drv_index); return -1; } @@ -1872,21 +2098,25 @@ static void cciss_add_controller_node(ctlr_info_t *h) if (h->gendisk[0] != NULL) /* already did this? Then bail. */ return; - drv_index = cciss_add_gendisk(h, 0, 1); - if (drv_index == -1) { - printk(KERN_WARNING "cciss%d: could not " - "add disk 0.\n", h->ctlr); - return; - } - h->drv[drv_index].block_size = 512; - h->drv[drv_index].nr_blocks = 0; - h->drv[drv_index].heads = 0; - h->drv[drv_index].sectors = 0; - h->drv[drv_index].cylinders = 0; - h->drv[drv_index].raid_level = -1; - memset(h->drv[drv_index].serial_no, 0, 16); + drv_index = cciss_add_gendisk(h, CTLR_LUNID, 1); + if (drv_index == -1) + goto error; + h->drv[drv_index]->block_size = 512; + h->drv[drv_index]->nr_blocks = 0; + h->drv[drv_index]->heads = 0; + h->drv[drv_index]->sectors = 0; + h->drv[drv_index]->cylinders = 0; + h->drv[drv_index]->raid_level = -1; + memset(h->drv[drv_index]->serial_no, 0, 16); disk = h->gendisk[drv_index]; - cciss_add_disk(h, disk, drv_index); + if (cciss_add_disk(h, disk, drv_index) == 0) + return; + cciss_free_gendisk(h, drv_index); + cciss_free_drive_info(h, drv_index); +error: + printk(KERN_WARNING "cciss%d: could not " + "add disk 0.\n", h->ctlr); + return; } /* This function will add and remove logical drives from the Logical @@ -1897,7 +2127,8 @@ static void cciss_add_controller_node(ctlr_info_t *h) * INPUT * h = The controller to perform the operations on */ -static int rebuild_lun_table(ctlr_info_t *h, int first_time) +static int rebuild_lun_table(ctlr_info_t *h, int first_time, + int via_ioctl) { int ctlr = h->ctlr; int num_luns; @@ -1907,7 +2138,7 @@ static int rebuild_lun_table(ctlr_info_t *h, int first_time) int i; int drv_found; int drv_index = 0; - __u32 lunid = 0; + unsigned char lunid[8] = CTLR_LUNID; unsigned long flags; if (!capable(CAP_SYS_RAWIO)) @@ -1960,13 +2191,13 @@ static int rebuild_lun_table(ctlr_info_t *h, int first_time) drv_found = 0; /* skip holes in the array from already deleted drives */ - if (h->drv[i].raid_level == -1) + if (h->drv[i] == NULL) continue; for (j = 0; j < num_luns; j++) { - memcpy(&lunid, &ld_buff->LUN[j][0], 4); - lunid = le32_to_cpu(lunid); - if (h->drv[i].LunID == lunid) { + memcpy(lunid, &ld_buff->LUN[j][0], sizeof(lunid)); + if (memcmp(h->drv[i]->LunID, lunid, + sizeof(lunid)) == 0) { drv_found = 1; break; } @@ -1974,11 +2205,11 @@ static int rebuild_lun_table(ctlr_info_t *h, int first_time) if (!drv_found) { /* Deregister it from the OS, it's gone. */ spin_lock_irqsave(CCISS_LOCK(h->ctlr), flags); - h->drv[i].busy_configuring = 1; + h->drv[i]->busy_configuring = 1; spin_unlock_irqrestore(CCISS_LOCK(h->ctlr), flags); - return_code = deregister_disk(h, i, 1); - cciss_destroy_ld_sysfs_entry(&h->drv[i]); - h->drv[i].busy_configuring = 0; + return_code = deregister_disk(h, i, 1, via_ioctl); + if (h->drv[i] != NULL) + h->drv[i]->busy_configuring = 0; } } @@ -1992,17 +2223,16 @@ static int rebuild_lun_table(ctlr_info_t *h, int first_time) drv_found = 0; - memcpy(&lunid, &ld_buff->LUN[i][0], 4); - lunid = le32_to_cpu(lunid); - + memcpy(lunid, &ld_buff->LUN[i][0], sizeof(lunid)); /* Find if the LUN is already in the drive array * of the driver. If so then update its info * if not in use. If it does not exist then find * the first free index and add it. */ for (j = 0; j <= h->highest_lun; j++) { - if (h->drv[j].raid_level != -1 && - h->drv[j].LunID == lunid) { + if (h->drv[j] != NULL && + memcmp(h->drv[j]->LunID, lunid, + sizeof(h->drv[j]->LunID)) == 0) { drv_index = j; drv_found = 1; break; @@ -2015,7 +2245,8 @@ static int rebuild_lun_table(ctlr_info_t *h, int first_time) if (drv_index == -1) goto freeret; } - cciss_update_drive_info(ctlr, drv_index, first_time); + cciss_update_drive_info(ctlr, drv_index, first_time, + via_ioctl); } /* end for */ freeret: @@ -2032,6 +2263,25 @@ mem_msg: goto freeret; } +static void cciss_clear_drive_info(drive_info_struct *drive_info) +{ + /* zero out the disk size info */ + drive_info->nr_blocks = 0; + drive_info->block_size = 0; + drive_info->heads = 0; + drive_info->sectors = 0; + drive_info->cylinders = 0; + drive_info->raid_level = -1; + memset(drive_info->serial_no, 0, sizeof(drive_info->serial_no)); + memset(drive_info->model, 0, sizeof(drive_info->model)); + memset(drive_info->rev, 0, sizeof(drive_info->rev)); + memset(drive_info->vendor, 0, sizeof(drive_info->vendor)); + /* + * don't clear the LUNID though, we need to remember which + * one this one is. + */ +} + /* This function will deregister the disk and it's queue from the * kernel. It must be called with the controller lock held and the * drv structures busy_configuring flag set. It's parameters are: @@ -2046,43 +2296,48 @@ mem_msg: * the disk in preparation for re-adding it. In this case * the highest_lun should be left unchanged and the LunID * should not be cleared. + * via_ioctl + * This indicates whether we've reached this path via ioctl. + * This affects the maximum usage count allowed for c0d0 to be messed with. + * If this path is reached via ioctl(), then the max_usage_count will + * be 1, as the process calling ioctl() has got to have the device open. + * If we get here via sysfs, then the max usage count will be zero. */ static int deregister_disk(ctlr_info_t *h, int drv_index, - int clear_all) + int clear_all, int via_ioctl) { int i; struct gendisk *disk; drive_info_struct *drv; + int recalculate_highest_lun; if (!capable(CAP_SYS_RAWIO)) return -EPERM; - drv = &h->drv[drv_index]; + drv = h->drv[drv_index]; disk = h->gendisk[drv_index]; /* make sure logical volume is NOT is use */ if (clear_all || (h->gendisk[0] == disk)) { - if (drv->usage_count > 1) + if (drv->usage_count > via_ioctl) return -EBUSY; } else if (drv->usage_count > 0) return -EBUSY; + recalculate_highest_lun = (drv == h->drv[h->highest_lun]); + /* invalidate the devices and deregister the disk. If it is disk * zero do not deregister it but just zero out it's values. This * allows us to delete disk zero but keep the controller registered. */ if (h->gendisk[0] != disk) { struct request_queue *q = disk->queue; - if (disk->flags & GENHD_FL_UP) + if (disk->flags & GENHD_FL_UP) { + cciss_destroy_ld_sysfs_entry(h, drv_index, 0); del_gendisk(disk); - if (q) { - blk_cleanup_queue(q); - /* Set drv->queue to NULL so that we do not try - * to call blk_start_queue on this queue in the - * interrupt handler - */ - drv->queue = NULL; } + if (q) + blk_cleanup_queue(q); /* If clear_all is set then we are deleting the logical * drive, not just refreshing its info. For drives * other than disk 0 we will call put_disk. We do not @@ -2105,34 +2360,20 @@ static int deregister_disk(ctlr_info_t *h, int drv_index, } } else { set_capacity(disk, 0); + cciss_clear_drive_info(drv); } --h->num_luns; - /* zero out the disk size info */ - drv->nr_blocks = 0; - drv->block_size = 0; - drv->heads = 0; - drv->sectors = 0; - drv->cylinders = 0; - drv->raid_level = -1; /* This can be used as a flag variable to - * indicate that this element of the drive - * array is free. - */ - - if (clear_all) { - /* check to see if it was the last disk */ - if (drv == h->drv + h->highest_lun) { - /* if so, find the new hightest lun */ - int i, newhighest = -1; - for (i = 0; i <= h->highest_lun; i++) { - /* if the disk has size > 0, it is available */ - if (h->drv[i].heads) - newhighest = i; - } - h->highest_lun = newhighest; - } - drv->LunID = 0; + /* if it was the last disk, find the new hightest lun */ + if (clear_all && recalculate_highest_lun) { + int i, newhighest = -1; + for (i = 0; i <= h->highest_lun; i++) { + /* if the disk has size > 0, it is available */ + if (h->drv[i] && h->drv[i]->heads) + newhighest = i; + } + h->highest_lun = newhighest; } return 0; } @@ -2479,8 +2720,6 @@ static void cciss_geometry_inquiry(int ctlr, int logvol, } else { /* Get geometry failed */ printk(KERN_WARNING "cciss: reading geometry failed\n"); } - printk(KERN_INFO " heads=%d, sectors=%d, cylinders=%d\n\n", - drv->heads, drv->sectors, drv->cylinders); } static void @@ -2514,9 +2753,6 @@ cciss_read_capacity(int ctlr, int logvol, int withirq, sector_t *total_size, *total_size = 0; *block_size = BLOCK_SIZE; } - if (*total_size != 0) - printk(KERN_INFO " blocks= %llu block_size= %d\n", - (unsigned long long)*total_size+1, *block_size); kfree(buf); } @@ -2568,7 +2804,8 @@ static int cciss_revalidate(struct gendisk *disk) InquiryData_struct *inq_buff = NULL; for (logvol = 0; logvol < CISS_MAX_LUN; logvol++) { - if (h->drv[logvol].LunID == drv->LunID) { + if (memcmp(h->drv[logvol]->LunID, drv->LunID, + sizeof(drv->LunID)) == 0) { FOUND = 1; break; } @@ -3053,8 +3290,7 @@ static void do_cciss_request(struct request_queue *q) /* The first 2 bits are reserved for controller error reporting. */ c->Header.Tag.lower = (c->cmdindex << 3); c->Header.Tag.lower |= 0x04; /* flag for direct lookup. */ - c->Header.LUN.LogDev.VolId = drv->LunID; - c->Header.LUN.LogDev.Mode = 1; + memcpy(&c->Header.LUN, drv->LunID, sizeof(drv->LunID)); c->Request.CDBLen = 10; // 12 byte commands not in FW yet; c->Request.Type.Type = TYPE_CMD; // It is a command. c->Request.Type.Attribute = ATTR_SIMPLE; @@ -3232,20 +3468,121 @@ static irqreturn_t do_cciss_intr(int irq, void *dev_id) return IRQ_HANDLED; } +/** + * add_to_scan_list() - add controller to rescan queue + * @h: Pointer to the controller. + * + * Adds the controller to the rescan queue if not already on the queue. + * + * returns 1 if added to the queue, 0 if skipped (could be on the + * queue already, or the controller could be initializing or shutting + * down). + **/ +static int add_to_scan_list(struct ctlr_info *h) +{ + struct ctlr_info *test_h; + int found = 0; + int ret = 0; + + if (h->busy_initializing) + return 0; + + if (!mutex_trylock(&h->busy_shutting_down)) + return 0; + + mutex_lock(&scan_mutex); + list_for_each_entry(test_h, &scan_q, scan_list) { + if (test_h == h) { + found = 1; + break; + } + } + if (!found && !h->busy_scanning) { + INIT_COMPLETION(h->scan_wait); + list_add_tail(&h->scan_list, &scan_q); + ret = 1; + } + mutex_unlock(&scan_mutex); + mutex_unlock(&h->busy_shutting_down); + + return ret; +} + +/** + * remove_from_scan_list() - remove controller from rescan queue + * @h: Pointer to the controller. + * + * Removes the controller from the rescan queue if present. Blocks if + * the controller is currently conducting a rescan. + **/ +static void remove_from_scan_list(struct ctlr_info *h) +{ + struct ctlr_info *test_h, *tmp_h; + int scanning = 0; + + mutex_lock(&scan_mutex); + list_for_each_entry_safe(test_h, tmp_h, &scan_q, scan_list) { + if (test_h == h) { + list_del(&h->scan_list); + complete_all(&h->scan_wait); + mutex_unlock(&scan_mutex); + return; + } + } + if (&h->busy_scanning) + scanning = 0; + mutex_unlock(&scan_mutex); + + if (scanning) + wait_for_completion(&h->scan_wait); +} + +/** + * scan_thread() - kernel thread used to rescan controllers + * @data: Ignored. + * + * A kernel thread used scan for drive topology changes on + * controllers. The thread processes only one controller at a time + * using a queue. Controllers are added to the queue using + * add_to_scan_list() and removed from the queue either after done + * processing or using remove_from_scan_list(). + * + * returns 0. + **/ static int scan_thread(void *data) { - ctlr_info_t *h = data; - int rc; - DECLARE_COMPLETION_ONSTACK(wait); - h->rescan_wait = &wait; + struct ctlr_info *h; - for (;;) { - rc = wait_for_completion_interruptible(&wait); + while (1) { + set_current_state(TASK_INTERRUPTIBLE); + schedule(); if (kthread_should_stop()) break; - if (!rc) - rebuild_lun_table(h, 0); + + while (1) { + mutex_lock(&scan_mutex); + if (list_empty(&scan_q)) { + mutex_unlock(&scan_mutex); + break; + } + + h = list_entry(scan_q.next, + struct ctlr_info, + scan_list); + list_del(&h->scan_list); + h->busy_scanning = 1; + mutex_unlock(&scan_mutex); + + if (h) { + rebuild_lun_table(h, 0, 0); + complete_all(&h->scan_wait); + mutex_lock(&scan_mutex); + h->busy_scanning = 0; + mutex_unlock(&scan_mutex); + } + } } + return 0; } @@ -3268,8 +3605,8 @@ static int check_for_unit_attention(ctlr_info_t *h, CommandList_struct *c) case REPORT_LUNS_CHANGED: printk(KERN_WARNING "cciss%d: report LUN data " "changed\n", h->ctlr); - if (h->rescan_wait) - complete(h->rescan_wait); + add_to_scan_list(h); + wake_up_process(cciss_scan_thread); return 1; break; case POWER_OR_RESET: @@ -3422,7 +3759,27 @@ static int __devinit cciss_pci_init(ctlr_info_t *c, struct pci_dev *pdev) __u64 cfg_offset; __u32 cfg_base_addr; __u64 cfg_base_addr_index; - int i, err; + int i, prod_index, err; + + subsystem_vendor_id = pdev->subsystem_vendor; + subsystem_device_id = pdev->subsystem_device; + board_id = (((__u32) (subsystem_device_id << 16) & 0xffff0000) | + subsystem_vendor_id); + + for (i = 0; i < ARRAY_SIZE(products); i++) { + /* Stand aside for hpsa driver on request */ + if (cciss_allow_hpsa && products[i].board_id == HPSA_BOUNDARY) + return -ENODEV; + if (board_id == products[i].board_id) + break; + } + prod_index = i; + if (prod_index == ARRAY_SIZE(products)) { + dev_warn(&pdev->dev, + "unrecognized board ID: 0x%08lx, ignoring.\n", + (unsigned long) board_id); + return -ENODEV; + } /* check to see if controller has been disabled */ /* BEFORE trying to enable it */ @@ -3446,11 +3803,6 @@ static int __devinit cciss_pci_init(ctlr_info_t *c, struct pci_dev *pdev) return err; } - subsystem_vendor_id = pdev->subsystem_vendor; - subsystem_device_id = pdev->subsystem_device; - board_id = (((__u32) (subsystem_device_id << 16) & 0xffff0000) | - subsystem_vendor_id); - #ifdef CCISS_DEBUG printk("command = %x\n", command); printk("irq = %x\n", pdev->irq); @@ -3489,7 +3841,7 @@ static int __devinit cciss_pci_init(ctlr_info_t *c, struct pci_dev *pdev) if (scratchpad == CCISS_FIRMWARE_READY) break; set_current_state(TASK_INTERRUPTIBLE); - schedule_timeout(HZ / 10); /* wait 100ms */ + schedule_timeout(msecs_to_jiffies(100)); /* wait 100ms */ } if (scratchpad != CCISS_FIRMWARE_READY) { printk(KERN_WARNING "cciss: Board not ready. Timed out.\n"); @@ -3536,14 +3888,9 @@ static int __devinit cciss_pci_init(ctlr_info_t *c, struct pci_dev *pdev) * leave a little room for ioctl calls. */ c->max_commands = readl(&(c->cfgtable->CmdsOutMax)); - for (i = 0; i < ARRAY_SIZE(products); i++) { - if (board_id == products[i].board_id) { - c->product_name = products[i].product_name; - c->access = *(products[i].access); - c->nr_cmds = c->max_commands - 4; - break; - } - } + c->product_name = products[prod_index].product_name; + c->access = *(products[prod_index].access); + c->nr_cmds = c->max_commands - 4; if ((readb(&c->cfgtable->Signature[0]) != 'C') || (readb(&c->cfgtable->Signature[1]) != 'I') || (readb(&c->cfgtable->Signature[2]) != 'S') || @@ -3552,27 +3899,6 @@ static int __devinit cciss_pci_init(ctlr_info_t *c, struct pci_dev *pdev) err = -ENODEV; goto err_out_free_res; } - /* We didn't find the controller in our list. We know the - * signature is valid. If it's an HP device let's try to - * bind to the device and fire it up. Otherwise we bail. - */ - if (i == ARRAY_SIZE(products)) { - if (subsystem_vendor_id == PCI_VENDOR_ID_HP) { - c->product_name = products[i-1].product_name; - c->access = *(products[i-1].access); - c->nr_cmds = c->max_commands - 4; - printk(KERN_WARNING "cciss: This is an unknown " - "Smart Array controller.\n" - "cciss: Please update to the latest driver " - "available from www.hp.com.\n"); - } else { - printk(KERN_WARNING "cciss: Sorry, I don't know how" - " to access the Smart Array controller %08lx\n" - , (unsigned long)board_id); - err = -ENODEV; - goto err_out_free_res; - } - } #ifdef CONFIG_X86 { /* Need to enable prefetch in the SCSI core for 6400 in x86 */ @@ -3615,7 +3941,7 @@ static int __devinit cciss_pci_init(ctlr_info_t *c, struct pci_dev *pdev) break; /* delay and try again */ set_current_state(TASK_INTERRUPTIBLE); - schedule_timeout(10); + schedule_timeout(msecs_to_jiffies(1)); } #ifdef CCISS_DEBUG @@ -3669,15 +3995,16 @@ Enomem: return -1; } -static void free_hba(int i) +static void free_hba(int n) { - ctlr_info_t *p = hba[i]; - int n; + ctlr_info_t *h = hba[n]; + int i; - hba[i] = NULL; - for (n = 0; n < CISS_MAX_LUN; n++) - put_disk(p->gendisk[n]); - kfree(p); + hba[n] = NULL; + for (i = 0; i < h->highest_lun + 1; i++) + if (h->gendisk[i] != NULL) + put_disk(h->gendisk[i]); + kfree(h); } /* Send a message CDB to the firmware. */ @@ -3918,14 +4245,17 @@ static int __devinit cciss_init_one(struct pci_dev *pdev, hba[i]->busy_initializing = 1; INIT_HLIST_HEAD(&hba[i]->cmpQ); INIT_HLIST_HEAD(&hba[i]->reqQ); + mutex_init(&hba[i]->busy_shutting_down); if (cciss_pci_init(hba[i], pdev) != 0) - goto clean0; + goto clean_no_release_regions; sprintf(hba[i]->devname, "cciss%d", i); hba[i]->ctlr = i; hba[i]->pdev = pdev; + init_completion(&hba[i]->scan_wait); + if (cciss_create_hba_sysfs_entry(hba[i])) goto clean0; @@ -4001,8 +4331,7 @@ static int __devinit cciss_init_one(struct pci_dev *pdev, hba[i]->num_luns = 0; hba[i]->highest_lun = -1; for (j = 0; j < CISS_MAX_LUN; j++) { - hba[i]->drv[j].raid_level = -1; - hba[i]->drv[j].queue = NULL; + hba[i]->drv[j] = NULL; hba[i]->gendisk[j] = NULL; } @@ -4035,14 +4364,8 @@ static int __devinit cciss_init_one(struct pci_dev *pdev, hba[i]->cciss_max_sectors = 2048; + rebuild_lun_table(hba[i], 1, 0); hba[i]->busy_initializing = 0; - - rebuild_lun_table(hba[i], 1); - hba[i]->cciss_scan_thread = kthread_run(scan_thread, hba[i], - "cciss_scan%02d", i); - if (IS_ERR(hba[i]->cciss_scan_thread)) - return PTR_ERR(hba[i]->cciss_scan_thread); - return 1; clean4: @@ -4062,18 +4385,14 @@ clean2: clean1: cciss_destroy_hba_sysfs_entry(hba[i]); clean0: + pci_release_regions(pdev); +clean_no_release_regions: hba[i]->busy_initializing = 0; - /* cleanup any queues that may have been initialized */ - for (j=0; j <= hba[i]->highest_lun; j++){ - drive_info_struct *drv = &(hba[i]->drv[j]); - if (drv->queue) - blk_cleanup_queue(drv->queue); - } + /* * Deliberately omit pci_disable_device(): it does something nasty to * Smart Array controllers that pci_enable_device does not undo */ - pci_release_regions(pdev); pci_set_drvdata(pdev, NULL); free_hba(i); return -1; @@ -4125,8 +4444,9 @@ static void __devexit cciss_remove_one(struct pci_dev *pdev) return; } - kthread_stop(hba[i]->cciss_scan_thread); + mutex_lock(&hba[i]->busy_shutting_down); + remove_from_scan_list(hba[i]); remove_proc_entry(hba[i]->devname, proc_cciss); unregister_blkdev(hba[i]->major, hba[i]->devname); @@ -4136,8 +4456,10 @@ static void __devexit cciss_remove_one(struct pci_dev *pdev) if (disk) { struct request_queue *q = disk->queue; - if (disk->flags & GENHD_FL_UP) + if (disk->flags & GENHD_FL_UP) { + cciss_destroy_ld_sysfs_entry(hba[i], j, 1); del_gendisk(disk); + } if (q) blk_cleanup_queue(q); } @@ -4170,6 +4492,7 @@ static void __devexit cciss_remove_one(struct pci_dev *pdev) pci_release_regions(pdev); pci_set_drvdata(pdev, NULL); cciss_destroy_hba_sysfs_entry(hba[i]); + mutex_unlock(&hba[i]->busy_shutting_down); free_hba(i); } @@ -4202,15 +4525,25 @@ static int __init cciss_init(void) if (err) return err; + /* Start the scan thread */ + cciss_scan_thread = kthread_run(scan_thread, NULL, "cciss_scan"); + if (IS_ERR(cciss_scan_thread)) { + err = PTR_ERR(cciss_scan_thread); + goto err_bus_unregister; + } + /* Register for our PCI devices */ err = pci_register_driver(&cciss_pci_driver); if (err) - goto err_bus_register; + goto err_thread_stop; - return 0; + return err; -err_bus_register: +err_thread_stop: + kthread_stop(cciss_scan_thread); +err_bus_unregister: bus_unregister(&cciss_bus_type); + return err; } @@ -4227,6 +4560,7 @@ static void __exit cciss_cleanup(void) cciss_remove_one(hba[i]->pdev); } } + kthread_stop(cciss_scan_thread); remove_proc_entry("driver/cciss", NULL); bus_unregister(&cciss_bus_type); } diff --git a/drivers/block/cciss.h b/drivers/block/cciss.h index 06a5db25b298..31524cf42c77 100644 --- a/drivers/block/cciss.h +++ b/drivers/block/cciss.h @@ -2,6 +2,7 @@ #define CCISS_H #include <linux/genhd.h> +#include <linux/mutex.h> #include "cciss_cmd.h" @@ -29,7 +30,7 @@ struct access_method { }; typedef struct _drive_info_struct { - __u32 LunID; + unsigned char LunID[8]; int usage_count; struct request_queue *queue; sector_t nr_blocks; @@ -51,6 +52,7 @@ typedef struct _drive_info_struct char vendor[VENDOR_LEN + 1]; /* SCSI vendor string */ char model[MODEL_LEN + 1]; /* SCSI model string */ char rev[REV_LEN + 1]; /* SCSI revision string */ + char device_initialized; /* indicates whether dev is initialized */ } drive_info_struct; struct ctlr_info @@ -86,7 +88,7 @@ struct ctlr_info BYTE cciss_read_capacity; // information about each logical volume - drive_info_struct drv[CISS_MAX_LUN]; + drive_info_struct *drv[CISS_MAX_LUN]; struct access_method access; @@ -108,6 +110,8 @@ struct ctlr_info int nr_frees; int busy_configuring; int busy_initializing; + int busy_scanning; + struct mutex busy_shutting_down; /* This element holds the zero based queue number of the last * queue to be started. It is used for fairness. @@ -122,8 +126,8 @@ struct ctlr_info /* and saved for later processing */ #endif unsigned char alive; - struct completion *rescan_wait; - struct task_struct *cciss_scan_thread; + struct list_head scan_list; + struct completion scan_wait; struct device dev; }; diff --git a/drivers/block/cpqarray.c b/drivers/block/cpqarray.c index b82d438e2607..6422651ec364 100644 --- a/drivers/block/cpqarray.c +++ b/drivers/block/cpqarray.c @@ -32,6 +32,7 @@ #include <linux/blkpg.h> #include <linux/timer.h> #include <linux/proc_fs.h> +#include <linux/seq_file.h> #include <linux/init.h> #include <linux/hdreg.h> #include <linux/spinlock.h> @@ -177,7 +178,6 @@ static int cpqarray_register_ctlr(int ctlr, struct pci_dev *pdev); #ifdef CONFIG_PROC_FS static void ida_procinit(int i); -static int ida_proc_get_info(char *buffer, char **start, off_t offset, int length, int *eof, void *data); #else static void ida_procinit(int i) {} #endif @@ -206,6 +206,7 @@ static const struct block_device_operations ida_fops = { #ifdef CONFIG_PROC_FS static struct proc_dir_entry *proc_array; +static const struct file_operations ida_proc_fops; /* * Get us a file in /proc/array that says something about each controller. @@ -218,19 +219,16 @@ static void __init ida_procinit(int i) if (!proc_array) return; } - create_proc_read_entry(hba[i]->devname, 0, proc_array, - ida_proc_get_info, hba[i]); + proc_create_data(hba[i]->devname, 0, proc_array, &ida_proc_fops, hba[i]); } /* * Report information about this controller. */ -static int ida_proc_get_info(char *buffer, char **start, off_t offset, int length, int *eof, void *data) +static int ida_proc_show(struct seq_file *m, void *v) { - off_t pos = 0; - off_t len = 0; - int size, i, ctlr; - ctlr_info_t *h = (ctlr_info_t*)data; + int i, ctlr; + ctlr_info_t *h = (ctlr_info_t*)m->private; drv_info_t *drv; #ifdef CPQ_PROC_PRINT_QUEUES cmdlist_t *c; @@ -238,7 +236,7 @@ static int ida_proc_get_info(char *buffer, char **start, off_t offset, int lengt #endif ctlr = h->ctlr; - size = sprintf(buffer, "%s: Compaq %s Controller\n" + seq_printf(m, "%s: Compaq %s Controller\n" " Board ID: 0x%08lx\n" " Firmware Revision: %c%c%c%c\n" " Controller Sig: 0x%08lx\n" @@ -258,55 +256,54 @@ static int ida_proc_get_info(char *buffer, char **start, off_t offset, int lengt h->log_drives, h->phys_drives, h->Qdepth, h->maxQsinceinit); - pos += size; len += size; - - size = sprintf(buffer+len, "Logical Drive Info:\n"); - pos += size; len += size; + seq_puts(m, "Logical Drive Info:\n"); for(i=0; i<h->log_drives; i++) { drv = &h->drv[i]; - size = sprintf(buffer+len, "ida/c%dd%d: blksz=%d nr_blks=%d\n", + seq_printf(m, "ida/c%dd%d: blksz=%d nr_blks=%d\n", ctlr, i, drv->blk_size, drv->nr_blks); - pos += size; len += size; } #ifdef CPQ_PROC_PRINT_QUEUES spin_lock_irqsave(IDA_LOCK(h->ctlr), flags); - size = sprintf(buffer+len, "\nCurrent Queues:\n"); - pos += size; len += size; + seq_puts(m, "\nCurrent Queues:\n"); c = h->reqQ; - size = sprintf(buffer+len, "reqQ = %p", c); pos += size; len += size; + seq_printf(m, "reqQ = %p", c); if (c) c=c->next; while(c && c != h->reqQ) { - size = sprintf(buffer+len, "->%p", c); - pos += size; len += size; + seq_printf(m, "->%p", c); c=c->next; } c = h->cmpQ; - size = sprintf(buffer+len, "\ncmpQ = %p", c); pos += size; len += size; + seq_printf(m, "\ncmpQ = %p", c); if (c) c=c->next; while(c && c != h->cmpQ) { - size = sprintf(buffer+len, "->%p", c); - pos += size; len += size; + seq_printf(m, "->%p", c); c=c->next; } - size = sprintf(buffer+len, "\n"); pos += size; len += size; + seq_putc(m, '\n'); spin_unlock_irqrestore(IDA_LOCK(h->ctlr), flags); #endif - size = sprintf(buffer+len, "nr_allocs = %d\nnr_frees = %d\n", + seq_printf(m, "nr_allocs = %d\nnr_frees = %d\n", h->nr_allocs, h->nr_frees); - pos += size; len += size; - - *eof = 1; - *start = buffer+offset; - len -= offset; - if (len>length) - len = length; - return len; + return 0; +} + +static int ida_proc_open(struct inode *inode, struct file *file) +{ + return single_open(file, ida_proc_show, PDE(inode)->data); } + +static const struct file_operations ida_proc_fops = { + .owner = THIS_MODULE, + .open = ida_proc_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; #endif /* CONFIG_PROC_FS */ module_param_array(eisa, int, NULL, 0); diff --git a/drivers/block/loop.c b/drivers/block/loop.c index edda9ea7c626..bd112c8c7bcd 100644 --- a/drivers/block/loop.c +++ b/drivers/block/loop.c @@ -949,7 +949,7 @@ static int loop_clr_fd(struct loop_device *lo, struct block_device *bdev) lo->lo_state = Lo_unbound; /* This is safe: open() is still holding a reference. */ module_put(THIS_MODULE); - if (max_part > 0) + if (max_part > 0 && bdev) ioctl_by_bdev(bdev, BLKRRPART, 0); mutex_unlock(&lo->lo_ctl_mutex); /* diff --git a/drivers/block/virtio_blk.c b/drivers/block/virtio_blk.c index aa89fe45237d..51042f0ba7e1 100644 --- a/drivers/block/virtio_blk.c +++ b/drivers/block/virtio_blk.c @@ -91,15 +91,26 @@ static bool do_req(struct request_queue *q, struct virtio_blk *vblk, return false; vbr->req = req; - if (blk_fs_request(vbr->req)) { + switch (req->cmd_type) { + case REQ_TYPE_FS: vbr->out_hdr.type = 0; vbr->out_hdr.sector = blk_rq_pos(vbr->req); vbr->out_hdr.ioprio = req_get_ioprio(vbr->req); - } else if (blk_pc_request(vbr->req)) { + break; + case REQ_TYPE_BLOCK_PC: vbr->out_hdr.type = VIRTIO_BLK_T_SCSI_CMD; vbr->out_hdr.sector = 0; vbr->out_hdr.ioprio = req_get_ioprio(vbr->req); - } else { + break; + case REQ_TYPE_LINUX_BLOCK: + if (req->cmd[0] == REQ_LB_OP_FLUSH) { + vbr->out_hdr.type = VIRTIO_BLK_T_FLUSH; + vbr->out_hdr.sector = 0; + vbr->out_hdr.ioprio = req_get_ioprio(vbr->req); + break; + } + /*FALLTHRU*/ + default: /* We don't put anything else in the queue. */ BUG(); } @@ -139,7 +150,7 @@ static bool do_req(struct request_queue *q, struct virtio_blk *vblk, } } - if (vblk->vq->vq_ops->add_buf(vblk->vq, vblk->sg, out, in, vbr)) { + if (vblk->vq->vq_ops->add_buf(vblk->vq, vblk->sg, out, in, vbr) < 0) { mempool_free(vbr, vblk->pool); return false; } @@ -171,32 +182,10 @@ static void do_virtblk_request(struct request_queue *q) vblk->vq->vq_ops->kick(vblk->vq); } -/* return ATA identify data - */ -static int virtblk_identify(struct gendisk *disk, void *argp) +static void virtblk_prepare_flush(struct request_queue *q, struct request *req) { - struct virtio_blk *vblk = disk->private_data; - void *opaque; - int err = -ENOMEM; - - opaque = kmalloc(VIRTIO_BLK_ID_BYTES, GFP_KERNEL); - if (!opaque) - goto out; - - err = virtio_config_buf(vblk->vdev, VIRTIO_BLK_F_IDENTIFY, - offsetof(struct virtio_blk_config, identify), opaque, - VIRTIO_BLK_ID_BYTES); - - if (err) - goto out_kfree; - - if (copy_to_user(argp, opaque, VIRTIO_BLK_ID_BYTES)) - err = -EFAULT; - -out_kfree: - kfree(opaque); -out: - return err; + req->cmd_type = REQ_TYPE_LINUX_BLOCK; + req->cmd[0] = REQ_LB_OP_FLUSH; } static int virtblk_ioctl(struct block_device *bdev, fmode_t mode, @@ -204,10 +193,6 @@ static int virtblk_ioctl(struct block_device *bdev, fmode_t mode, { struct gendisk *disk = bdev->bd_disk; struct virtio_blk *vblk = disk->private_data; - void __user *argp = (void __user *)data; - - if (cmd == HDIO_GET_IDENTITY) - return virtblk_identify(disk, argp); /* * Only allow the generic SCSI ioctls if the host can support it. @@ -215,7 +200,8 @@ static int virtblk_ioctl(struct block_device *bdev, fmode_t mode, if (!virtio_has_feature(vblk->vdev, VIRTIO_BLK_F_SCSI)) return -ENOTTY; - return scsi_cmd_ioctl(disk->queue, disk, mode, cmd, argp); + return scsi_cmd_ioctl(disk->queue, disk, mode, cmd, + (void __user *)data); } /* We provide getgeo only to please some old bootloader/partitioning tools */ @@ -314,7 +300,6 @@ static int __devinit virtblk_probe(struct virtio_device *vdev) } vblk->disk->queue->queuedata = vblk; - queue_flag_set_unlocked(QUEUE_FLAG_VIRT, vblk->disk->queue); if (index < 26) { sprintf(vblk->disk->disk_name, "vd%c", 'a' + index % 26); @@ -337,7 +322,10 @@ static int __devinit virtblk_probe(struct virtio_device *vdev) index++; /* If barriers are supported, tell block layer that queue is ordered */ - if (virtio_has_feature(vdev, VIRTIO_BLK_F_BARRIER)) + if (virtio_has_feature(vdev, VIRTIO_BLK_F_FLUSH)) + blk_queue_ordered(vblk->disk->queue, QUEUE_ORDERED_DRAIN_FLUSH, + virtblk_prepare_flush); + else if (virtio_has_feature(vdev, VIRTIO_BLK_F_BARRIER)) blk_queue_ordered(vblk->disk->queue, QUEUE_ORDERED_TAG, NULL); /* If disk is read-only in the host, the guest should obey */ @@ -424,7 +412,7 @@ static struct virtio_device_id id_table[] = { static unsigned int features[] = { VIRTIO_BLK_F_BARRIER, VIRTIO_BLK_F_SEG_MAX, VIRTIO_BLK_F_SIZE_MAX, VIRTIO_BLK_F_GEOMETRY, VIRTIO_BLK_F_RO, VIRTIO_BLK_F_BLK_SIZE, - VIRTIO_BLK_F_SCSI, VIRTIO_BLK_F_IDENTIFY + VIRTIO_BLK_F_SCSI, VIRTIO_BLK_F_FLUSH }; /* diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c index 7ba91aa3fe8b..44bc8bbabf54 100644 --- a/drivers/bluetooth/btusb.c +++ b/drivers/bluetooth/btusb.c @@ -591,6 +591,7 @@ static int btusb_close(struct hci_dev *hdev) return 0; cancel_work_sync(&data->work); + cancel_work_sync(&data->waker); clear_bit(BTUSB_ISOC_RUNNING, &data->flags); clear_bit(BTUSB_BULK_RUNNING, &data->flags); @@ -599,11 +600,13 @@ static int btusb_close(struct hci_dev *hdev) btusb_stop_traffic(data); err = usb_autopm_get_interface(data->intf); if (err < 0) - return 0; + goto failed; data->intf->needs_remote_wakeup = 0; usb_autopm_put_interface(data->intf); +failed: + usb_scuttle_anchored_urbs(&data->deferred); return 0; } diff --git a/drivers/cdrom/cdrom.c b/drivers/cdrom/cdrom.c index 71d1b9bab70b..614da5b8613a 100644 --- a/drivers/cdrom/cdrom.c +++ b/drivers/cdrom/cdrom.c @@ -3412,7 +3412,7 @@ static int cdrom_print_info(const char *header, int val, char *info, return 0; } -static int cdrom_sysctl_info(ctl_table *ctl, int write, struct file * filp, +static int cdrom_sysctl_info(ctl_table *ctl, int write, void __user *buffer, size_t *lenp, loff_t *ppos) { int pos; @@ -3489,7 +3489,7 @@ static int cdrom_sysctl_info(ctl_table *ctl, int write, struct file * filp, goto done; doit: mutex_unlock(&cdrom_mutex); - return proc_dostring(ctl, write, filp, buffer, lenp, ppos); + return proc_dostring(ctl, write, buffer, lenp, ppos); done: printk(KERN_INFO "cdrom: info buffer too small\n"); goto doit; @@ -3525,12 +3525,12 @@ static void cdrom_update_settings(void) mutex_unlock(&cdrom_mutex); } -static int cdrom_sysctl_handler(ctl_table *ctl, int write, struct file * filp, +static int cdrom_sysctl_handler(ctl_table *ctl, int write, void __user *buffer, size_t *lenp, loff_t *ppos) { int ret; - ret = proc_dointvec(ctl, write, filp, buffer, lenp, ppos); + ret = proc_dointvec(ctl, write, buffer, lenp, ppos); if (write) { diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig index 6a06913b01d3..6aad99ec4e0f 100644 --- a/drivers/char/Kconfig +++ b/drivers/char/Kconfig @@ -323,7 +323,7 @@ config SPECIALIX config SX tristate "Specialix SX (and SI) card support" - depends on SERIAL_NONSTANDARD && (PCI || EISA || ISA) + depends on SERIAL_NONSTANDARD && (PCI || EISA || ISA) && BROKEN help This is a driver for the SX and SI multiport serial cards. Please read the file <file:Documentation/serial/sx.txt> for details. @@ -334,7 +334,7 @@ config SX config RIO tristate "Specialix RIO system support" - depends on SERIAL_NONSTANDARD + depends on SERIAL_NONSTANDARD && BROKEN help This is a driver for the Specialix RIO, a smart serial card which drives an outboard box that can support up to 128 ports. Product @@ -395,7 +395,7 @@ config NOZOMI config A2232 tristate "Commodore A2232 serial support (EXPERIMENTAL)" - depends on EXPERIMENTAL && ZORRO && BROKEN_ON_SMP + depends on EXPERIMENTAL && ZORRO && BROKEN ---help--- This option supports the 2232 7-port serial card shipped with the Amiga 2000 and other Zorro-bus machines, dating from 1989. At @@ -1087,6 +1087,14 @@ config MMTIMER The mmtimer device allows direct userspace access to the Altix system timer. +config UV_MMTIMER + tristate "UV_MMTIMER Memory mapped RTC for SGI UV" + depends on X86_UV + default m + help + The uv_mmtimer device allows direct userspace access to the + UV system timer. + source "drivers/char/tpm/Kconfig" config TELCLOCK diff --git a/drivers/char/Makefile b/drivers/char/Makefile index 66f779ad4f4c..19a79dd79eee 100644 --- a/drivers/char/Makefile +++ b/drivers/char/Makefile @@ -58,6 +58,7 @@ obj-$(CONFIG_RAW_DRIVER) += raw.o obj-$(CONFIG_SGI_SNSC) += snsc.o snsc_event.o obj-$(CONFIG_MSPEC) += mspec.o obj-$(CONFIG_MMTIMER) += mmtimer.o +obj-$(CONFIG_UV_MMTIMER) += uv_mmtimer.o obj-$(CONFIG_VIOTAPE) += viotape.o obj-$(CONFIG_HVCS) += hvcs.o obj-$(CONFIG_IBM_BSR) += bsr.o diff --git a/drivers/char/agp/agp.h b/drivers/char/agp/agp.h index d6f36c004d9b..870f12cfed93 100644 --- a/drivers/char/agp/agp.h +++ b/drivers/char/agp/agp.h @@ -131,7 +131,7 @@ struct agp_bridge_driver { struct agp_bridge_data { const struct agp_version *version; const struct agp_bridge_driver *driver; - struct vm_operations_struct *vm_ops; + const struct vm_operations_struct *vm_ops; void *previous_size; void *current_size; void *dev_private_data; diff --git a/drivers/char/agp/alpha-agp.c b/drivers/char/agp/alpha-agp.c index 5ea4da8e9954..dd84af4d4f7e 100644 --- a/drivers/char/agp/alpha-agp.c +++ b/drivers/char/agp/alpha-agp.c @@ -40,7 +40,7 @@ static struct aper_size_info_fixed alpha_core_agp_sizes[] = { 0, 0, 0 }, /* filled in by alpha_core_agp_setup */ }; -struct vm_operations_struct alpha_core_agp_vm_ops = { +static const struct vm_operations_struct alpha_core_agp_vm_ops = { .fault = alpha_core_agp_vm_fault, }; diff --git a/drivers/char/agp/hp-agp.c b/drivers/char/agp/hp-agp.c index 501e293e5ad0..9047b2714653 100644 --- a/drivers/char/agp/hp-agp.c +++ b/drivers/char/agp/hp-agp.c @@ -476,7 +476,6 @@ zx1_gart_probe (acpi_handle obj, u32 depth, void *context, void **ret) { acpi_handle handle, parent; acpi_status status; - struct acpi_buffer buffer; struct acpi_device_info *info; u64 lba_hpa, sba_hpa, length; int match; @@ -488,13 +487,11 @@ zx1_gart_probe (acpi_handle obj, u32 depth, void *context, void **ret) /* Look for an enclosing IOC scope and find its CSR space */ handle = obj; do { - buffer.length = ACPI_ALLOCATE_LOCAL_BUFFER; - status = acpi_get_object_info(handle, &buffer); + status = acpi_get_object_info(handle, &info); if (ACPI_SUCCESS(status)) { /* TBD check _CID also */ - info = buffer.pointer; - info->hardware_id.value[sizeof(info->hardware_id)-1] = '\0'; - match = (strcmp(info->hardware_id.value, "HWP0001") == 0); + info->hardware_id.string[sizeof(info->hardware_id.length)-1] = '\0'; + match = (strcmp(info->hardware_id.string, "HWP0001") == 0); kfree(info); if (match) { status = hp_acpi_csr_space(handle, &sba_hpa, &length); diff --git a/drivers/char/agp/intel-agp.c b/drivers/char/agp/intel-agp.c index 1540e693d91e..3cb56a049e24 100644 --- a/drivers/char/agp/intel-agp.c +++ b/drivers/char/agp/intel-agp.c @@ -46,6 +46,8 @@ #define PCI_DEVICE_ID_INTEL_Q35_IG 0x29B2 #define PCI_DEVICE_ID_INTEL_Q33_HB 0x29D0 #define PCI_DEVICE_ID_INTEL_Q33_IG 0x29D2 +#define PCI_DEVICE_ID_INTEL_B43_HB 0x2E40 +#define PCI_DEVICE_ID_INTEL_B43_IG 0x2E42 #define PCI_DEVICE_ID_INTEL_GM45_HB 0x2A40 #define PCI_DEVICE_ID_INTEL_GM45_IG 0x2A42 #define PCI_DEVICE_ID_INTEL_IGD_E_HB 0x2E00 @@ -60,6 +62,7 @@ #define PCI_DEVICE_ID_INTEL_IGDNG_D_IG 0x0042 #define PCI_DEVICE_ID_INTEL_IGDNG_M_HB 0x0044 #define PCI_DEVICE_ID_INTEL_IGDNG_MA_HB 0x0062 +#define PCI_DEVICE_ID_INTEL_IGDNG_MC2_HB 0x006a #define PCI_DEVICE_ID_INTEL_IGDNG_M_IG 0x0046 /* cover 915 and 945 variants */ @@ -91,9 +94,11 @@ agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_G45_HB || \ agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_GM45_HB || \ agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_G41_HB || \ + agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_B43_HB || \ agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_IGDNG_D_HB || \ agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_IGDNG_M_HB || \ - agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_IGDNG_MA_HB) + agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_IGDNG_MA_HB || \ + agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_IGDNG_MC2_HB) extern int agp_memory_reserved; @@ -804,23 +809,39 @@ static void intel_i830_setup_flush(void) if (!intel_private.i8xx_page) return; - /* make page uncached */ - map_page_into_agp(intel_private.i8xx_page); - intel_private.i8xx_flush_page = kmap(intel_private.i8xx_page); if (!intel_private.i8xx_flush_page) intel_i830_fini_flush(); } +static void +do_wbinvd(void *null) +{ + wbinvd(); +} + +/* The chipset_flush interface needs to get data that has already been + * flushed out of the CPU all the way out to main memory, because the GPU + * doesn't snoop those buffers. + * + * The 8xx series doesn't have the same lovely interface for flushing the + * chipset write buffers that the later chips do. According to the 865 + * specs, it's 64 octwords, or 1KB. So, to get those previous things in + * that buffer out, we just fill 1KB and clflush it out, on the assumption + * that it'll push whatever was in there out. It appears to work. + */ static void intel_i830_chipset_flush(struct agp_bridge_data *bridge) { unsigned int *pg = intel_private.i8xx_flush_page; - int i; - for (i = 0; i < 256; i += 2) - *(pg + i) = i; + memset(pg, 0, 1024); - wmb(); + if (cpu_has_clflush) { + clflush_cache_range(pg, 1024); + } else { + if (on_each_cpu(do_wbinvd, NULL, 1) != 0) + printk(KERN_ERR "Timed out waiting for cache flush.\n"); + } } /* The intel i830 automatically initializes the agp aperture during POST. @@ -1142,12 +1163,6 @@ static int intel_i915_configure(void) intel_i9xx_setup_flush(); -#ifdef USE_PCI_DMA_API - if (pci_set_dma_mask(intel_private.pcidev, DMA_BIT_MASK(36))) - dev_err(&intel_private.pcidev->dev, - "set gfx device dma mask 36bit failed!\n"); -#endif - return 0; } @@ -1341,9 +1356,11 @@ static void intel_i965_get_gtt_range(int *gtt_offset, int *gtt_size) case PCI_DEVICE_ID_INTEL_Q45_HB: case PCI_DEVICE_ID_INTEL_G45_HB: case PCI_DEVICE_ID_INTEL_G41_HB: + case PCI_DEVICE_ID_INTEL_B43_HB: case PCI_DEVICE_ID_INTEL_IGDNG_D_HB: case PCI_DEVICE_ID_INTEL_IGDNG_M_HB: case PCI_DEVICE_ID_INTEL_IGDNG_MA_HB: + case PCI_DEVICE_ID_INTEL_IGDNG_MC2_HB: *gtt_offset = *gtt_size = MB(2); break; default: @@ -2335,6 +2352,8 @@ static const struct intel_driver_description { "Q45/Q43", NULL, &intel_i965_driver }, { PCI_DEVICE_ID_INTEL_G45_HB, PCI_DEVICE_ID_INTEL_G45_IG, 0, "G45/G43", NULL, &intel_i965_driver }, + { PCI_DEVICE_ID_INTEL_B43_HB, PCI_DEVICE_ID_INTEL_B43_IG, 0, + "B43", NULL, &intel_i965_driver }, { PCI_DEVICE_ID_INTEL_G41_HB, PCI_DEVICE_ID_INTEL_G41_IG, 0, "G41", NULL, &intel_i965_driver }, { PCI_DEVICE_ID_INTEL_IGDNG_D_HB, PCI_DEVICE_ID_INTEL_IGDNG_D_IG, 0, @@ -2343,6 +2362,8 @@ static const struct intel_driver_description { "IGDNG/M", NULL, &intel_i965_driver }, { PCI_DEVICE_ID_INTEL_IGDNG_MA_HB, PCI_DEVICE_ID_INTEL_IGDNG_M_IG, 0, "IGDNG/MA", NULL, &intel_i965_driver }, + { PCI_DEVICE_ID_INTEL_IGDNG_MC2_HB, PCI_DEVICE_ID_INTEL_IGDNG_M_IG, 0, + "IGDNG/MC2", NULL, &intel_i965_driver }, { 0, 0, 0, NULL, NULL, NULL } }; @@ -2434,6 +2455,11 @@ static int __devinit agp_intel_probe(struct pci_dev *pdev, &bridge->mode); } + if (bridge->driver->mask_memory == intel_i965_mask_memory) + if (pci_set_dma_mask(intel_private.pcidev, DMA_BIT_MASK(36))) + dev_err(&intel_private.pcidev->dev, + "set gfx device dma mask 36bit failed!\n"); + pci_set_drvdata(pdev, bridge); return agp_add_bridge(bridge); } @@ -2535,9 +2561,11 @@ static struct pci_device_id agp_intel_pci_table[] = { ID(PCI_DEVICE_ID_INTEL_Q45_HB), ID(PCI_DEVICE_ID_INTEL_G45_HB), ID(PCI_DEVICE_ID_INTEL_G41_HB), + ID(PCI_DEVICE_ID_INTEL_B43_HB), ID(PCI_DEVICE_ID_INTEL_IGDNG_D_HB), ID(PCI_DEVICE_ID_INTEL_IGDNG_M_HB), ID(PCI_DEVICE_ID_INTEL_IGDNG_MA_HB), + ID(PCI_DEVICE_ID_INTEL_IGDNG_MC2_HB), { } }; diff --git a/drivers/char/agp/parisc-agp.c b/drivers/char/agp/parisc-agp.c index 60ab75104da9..1c129211302d 100644 --- a/drivers/char/agp/parisc-agp.c +++ b/drivers/char/agp/parisc-agp.c @@ -217,7 +217,7 @@ static const struct agp_bridge_driver parisc_agp_driver = { .configure = parisc_agp_configure, .fetch_size = parisc_agp_fetch_size, .tlb_flush = parisc_agp_tlbflush, - .mask_memory = parisc_agp_page_mask_memory, + .mask_memory = parisc_agp_mask_memory, .masks = parisc_agp_masks, .agp_enable = parisc_agp_enable, .cache_flush = global_cache_flush, diff --git a/drivers/char/apm-emulation.c b/drivers/char/apm-emulation.c index aaca40283be9..4f568cb9af3f 100644 --- a/drivers/char/apm-emulation.c +++ b/drivers/char/apm-emulation.c @@ -393,7 +393,7 @@ static int apm_open(struct inode * inode, struct file * filp) return as ? 0 : -ENOMEM; } -static struct file_operations apm_bios_fops = { +static const struct file_operations apm_bios_fops = { .owner = THIS_MODULE, .read = apm_read, .poll = apm_poll, diff --git a/drivers/char/applicom.c b/drivers/char/applicom.c index 73a0765344b6..fe2cb2f5db17 100644 --- a/drivers/char/applicom.c +++ b/drivers/char/applicom.c @@ -23,6 +23,7 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/interrupt.h> +#include <linux/sched.h> #include <linux/slab.h> #include <linux/errno.h> #include <linux/miscdevice.h> diff --git a/drivers/char/bfin-otp.c b/drivers/char/bfin-otp.c index 0a01329451e4..836d4f0a876f 100644 --- a/drivers/char/bfin-otp.c +++ b/drivers/char/bfin-otp.c @@ -1,8 +1,7 @@ /* * Blackfin On-Chip OTP Memory Interface - * Supports BF52x/BF54x * - * Copyright 2007-2008 Analog Devices Inc. + * Copyright 2007-2009 Analog Devices Inc. * * Enter bugs at http://blackfin.uclinux.org/ * @@ -17,8 +16,10 @@ #include <linux/module.h> #include <linux/mutex.h> #include <linux/types.h> +#include <mtd/mtd-abi.h> #include <asm/blackfin.h> +#include <asm/bfrom.h> #include <asm/uaccess.h> #define stamp(fmt, args...) pr_debug("%s:%i: " fmt "\n", __func__, __LINE__, ## args) @@ -30,39 +31,6 @@ static DEFINE_MUTEX(bfin_otp_lock); -/* OTP Boot ROM functions */ -#define _BOOTROM_OTP_COMMAND 0xEF000018 -#define _BOOTROM_OTP_READ 0xEF00001A -#define _BOOTROM_OTP_WRITE 0xEF00001C - -static u32 (* const otp_command)(u32 command, u32 value) = (void *)_BOOTROM_OTP_COMMAND; -static u32 (* const otp_read)(u32 page, u32 flags, u64 *page_content) = (void *)_BOOTROM_OTP_READ; -static u32 (* const otp_write)(u32 page, u32 flags, u64 *page_content) = (void *)_BOOTROM_OTP_WRITE; - -/* otp_command(): defines for "command" */ -#define OTP_INIT 0x00000001 -#define OTP_CLOSE 0x00000002 - -/* otp_{read,write}(): defines for "flags" */ -#define OTP_LOWER_HALF 0x00000000 /* select upper/lower 64-bit half (bit 0) */ -#define OTP_UPPER_HALF 0x00000001 -#define OTP_NO_ECC 0x00000010 /* do not use ECC */ -#define OTP_LOCK 0x00000020 /* sets page protection bit for page */ -#define OTP_ACCESS_READ 0x00001000 -#define OTP_ACCESS_READWRITE 0x00002000 - -/* Return values for all functions */ -#define OTP_SUCCESS 0x00000000 -#define OTP_MASTER_ERROR 0x001 -#define OTP_WRITE_ERROR 0x003 -#define OTP_READ_ERROR 0x005 -#define OTP_ACC_VIO_ERROR 0x009 -#define OTP_DATA_MULT_ERROR 0x011 -#define OTP_ECC_MULT_ERROR 0x021 -#define OTP_PREV_WR_ERROR 0x041 -#define OTP_DATA_SB_WARN 0x100 -#define OTP_ECC_SB_WARN 0x200 - /** * bfin_otp_read - Read OTP pages * @@ -86,9 +54,11 @@ static ssize_t bfin_otp_read(struct file *file, char __user *buff, size_t count, page = *pos / (sizeof(u64) * 2); while (bytes_done < count) { flags = (*pos % (sizeof(u64) * 2) ? OTP_UPPER_HALF : OTP_LOWER_HALF); - stamp("processing page %i (%s)", page, (flags == OTP_UPPER_HALF ? "upper" : "lower")); - ret = otp_read(page, flags, &content); + stamp("processing page %i (0x%x:%s)", page, flags, + (flags & OTP_UPPER_HALF ? "upper" : "lower")); + ret = bfrom_OtpRead(page, flags, &content); if (ret & OTP_MASTER_ERROR) { + stamp("error from otp: 0x%x", ret); bytes_done = -EIO; break; } @@ -96,7 +66,7 @@ static ssize_t bfin_otp_read(struct file *file, char __user *buff, size_t count, bytes_done = -EFAULT; break; } - if (flags == OTP_UPPER_HALF) + if (flags & OTP_UPPER_HALF) ++page; bytes_done += sizeof(content); *pos += sizeof(content); @@ -108,14 +78,53 @@ static ssize_t bfin_otp_read(struct file *file, char __user *buff, size_t count, } #ifdef CONFIG_BFIN_OTP_WRITE_ENABLE +static bool allow_writes; + +/** + * bfin_otp_init_timing - setup OTP timing parameters + * + * Required before doing any write operation. Algorithms from HRM. + */ +static u32 bfin_otp_init_timing(void) +{ + u32 tp1, tp2, tp3, timing; + + tp1 = get_sclk() / 1000000; + tp2 = (2 * get_sclk() / 10000000) << 8; + tp3 = (0x1401) << 15; + timing = tp1 | tp2 | tp3; + if (bfrom_OtpCommand(OTP_INIT, timing)) + return 0; + + return timing; +} + +/** + * bfin_otp_deinit_timing - set timings to only allow reads + * + * Should be called after all writes are done. + */ +static void bfin_otp_deinit_timing(u32 timing) +{ + /* mask bits [31:15] so that any attempts to write fail */ + bfrom_OtpCommand(OTP_CLOSE, 0); + bfrom_OtpCommand(OTP_INIT, timing & ~(-1 << 15)); + bfrom_OtpCommand(OTP_CLOSE, 0); +} + /** - * bfin_otp_write - Write OTP pages + * bfin_otp_write - write OTP pages * * All writes must be in half page chunks (half page == 64 bits). */ static ssize_t bfin_otp_write(struct file *filp, const char __user *buff, size_t count, loff_t *pos) { - stampit(); + ssize_t bytes_done; + u32 timing, page, base_flags, flags, ret; + u64 content; + + if (!allow_writes) + return -EACCES; if (count % sizeof(u64)) return -EMSGSIZE; @@ -123,20 +132,96 @@ static ssize_t bfin_otp_write(struct file *filp, const char __user *buff, size_t if (mutex_lock_interruptible(&bfin_otp_lock)) return -ERESTARTSYS; - /* need otp_init() documentation before this can be implemented */ + stampit(); + + timing = bfin_otp_init_timing(); + if (timing == 0) { + mutex_unlock(&bfin_otp_lock); + return -EIO; + } + + base_flags = OTP_CHECK_FOR_PREV_WRITE; + + bytes_done = 0; + page = *pos / (sizeof(u64) * 2); + while (bytes_done < count) { + flags = base_flags | (*pos % (sizeof(u64) * 2) ? OTP_UPPER_HALF : OTP_LOWER_HALF); + stamp("processing page %i (0x%x:%s) from %p", page, flags, + (flags & OTP_UPPER_HALF ? "upper" : "lower"), buff + bytes_done); + if (copy_from_user(&content, buff + bytes_done, sizeof(content))) { + bytes_done = -EFAULT; + break; + } + ret = bfrom_OtpWrite(page, flags, &content); + if (ret & OTP_MASTER_ERROR) { + stamp("error from otp: 0x%x", ret); + bytes_done = -EIO; + break; + } + if (flags & OTP_UPPER_HALF) + ++page; + bytes_done += sizeof(content); + *pos += sizeof(content); + } + + bfin_otp_deinit_timing(timing); mutex_unlock(&bfin_otp_lock); + return bytes_done; +} + +static long bfin_otp_ioctl(struct file *filp, unsigned cmd, unsigned long arg) +{ + stampit(); + + switch (cmd) { + case OTPLOCK: { + u32 timing; + int ret = -EIO; + + if (!allow_writes) + return -EACCES; + + if (mutex_lock_interruptible(&bfin_otp_lock)) + return -ERESTARTSYS; + + timing = bfin_otp_init_timing(); + if (timing) { + u32 otp_result = bfrom_OtpWrite(arg, OTP_LOCK, NULL); + stamp("locking page %lu resulted in 0x%x", arg, otp_result); + if (!(otp_result & OTP_MASTER_ERROR)) + ret = 0; + + bfin_otp_deinit_timing(timing); + } + + mutex_unlock(&bfin_otp_lock); + + return ret; + } + + case MEMLOCK: + allow_writes = false; + return 0; + + case MEMUNLOCK: + allow_writes = true; + return 0; + } + return -EINVAL; } #else # define bfin_otp_write NULL +# define bfin_otp_ioctl NULL #endif -static struct file_operations bfin_otp_fops = { - .owner = THIS_MODULE, - .read = bfin_otp_read, - .write = bfin_otp_write, +static const struct file_operations bfin_otp_fops = { + .owner = THIS_MODULE, + .unlocked_ioctl = bfin_otp_ioctl, + .read = bfin_otp_read, + .write = bfin_otp_write, }; static struct miscdevice bfin_otp_misc_device = { diff --git a/drivers/char/cyclades.c b/drivers/char/cyclades.c index df5038bbcbc2..4254457d3911 100644 --- a/drivers/char/cyclades.c +++ b/drivers/char/cyclades.c @@ -3354,7 +3354,7 @@ static int __init cy_detect_isa(void) continue; } #ifdef MODULE - if (isparam && irq[i]) + if (isparam && i < NR_CARDS && irq[i]) cy_isa_irq = irq[i]; else #endif diff --git a/drivers/char/dtlk.c b/drivers/char/dtlk.c index 52e06589821d..045c930e6320 100644 --- a/drivers/char/dtlk.c +++ b/drivers/char/dtlk.c @@ -56,6 +56,7 @@ #include <linux/errno.h> /* for -EBUSY */ #include <linux/ioport.h> /* for request_region */ #include <linux/delay.h> /* for loops_per_jiffy */ +#include <linux/sched.h> #include <linux/smp_lock.h> /* cycle_kernel_lock() */ #include <asm/io.h> /* for inb_p, outb_p, inb, outb, etc. */ #include <asm/uaccess.h> /* for get_user, etc. */ diff --git a/drivers/char/epca.c b/drivers/char/epca.c index 9d589e3144de..dde5134713e2 100644 --- a/drivers/char/epca.c +++ b/drivers/char/epca.c @@ -30,6 +30,7 @@ #include <linux/kernel.h> #include <linux/types.h> #include <linux/init.h> +#include <linux/sched.h> #include <linux/serial.h> #include <linux/delay.h> #include <linux/ctype.h> diff --git a/drivers/char/generic_serial.c b/drivers/char/generic_serial.c index 9e4e569dc00d..d400cbd280f2 100644 --- a/drivers/char/generic_serial.c +++ b/drivers/char/generic_serial.c @@ -22,6 +22,7 @@ #include <linux/module.h> #include <linux/kernel.h> #include <linux/tty.h> +#include <linux/sched.h> #include <linux/serial.h> #include <linux/mm.h> #include <linux/generic_serial.h> diff --git a/drivers/char/genrtc.c b/drivers/char/genrtc.c index aac0985a572b..31e7c91c2d9d 100644 --- a/drivers/char/genrtc.c +++ b/drivers/char/genrtc.c @@ -43,6 +43,7 @@ #define RTC_VERSION "1.07" #include <linux/module.h> +#include <linux/sched.h> #include <linux/errno.h> #include <linux/miscdevice.h> #include <linux/fcntl.h> diff --git a/drivers/char/hpet.c b/drivers/char/hpet.c index 4a9f3492b921..70a770ac0138 100644 --- a/drivers/char/hpet.c +++ b/drivers/char/hpet.c @@ -166,9 +166,8 @@ static irqreturn_t hpet_interrupt(int irq, void *data) unsigned long m, t; t = devp->hd_ireqfreq; - m = read_counter(&devp->hd_hpet->hpet_mc); - write_counter(t + m + devp->hd_hpets->hp_delta, - &devp->hd_timer->hpet_compare); + m = read_counter(&devp->hd_timer->hpet_compare); + write_counter(t + m, &devp->hd_timer->hpet_compare); } if (devp->hd_flags & HPET_SHARED_IRQ) @@ -504,21 +503,25 @@ static int hpet_ioctl_ieon(struct hpet_dev *devp) g = v | Tn_32MODE_CNF_MASK | Tn_INT_ENB_CNF_MASK; if (devp->hd_flags & HPET_PERIODIC) { - write_counter(t, &timer->hpet_compare); g |= Tn_TYPE_CNF_MASK; - v |= Tn_TYPE_CNF_MASK; - writeq(v, &timer->hpet_config); - v |= Tn_VAL_SET_CNF_MASK; + v |= Tn_TYPE_CNF_MASK | Tn_VAL_SET_CNF_MASK; writeq(v, &timer->hpet_config); local_irq_save(flags); - /* NOTE: what we modify here is a hidden accumulator + /* + * NOTE: First we modify the hidden accumulator * register supported by periodic-capable comparators. * We never want to modify the (single) counter; that - * would affect all the comparators. + * would affect all the comparators. The value written + * is the counter value when the first interrupt is due. */ m = read_counter(&hpet->hpet_mc); write_counter(t + m + hpetp->hp_delta, &timer->hpet_compare); + /* + * Then we modify the comparator, indicating the period + * for subsequent interrupt. + */ + write_counter(t, &timer->hpet_compare); } else { local_irq_save(flags); m = read_counter(&hpet->hpet_mc); diff --git a/drivers/char/hvc_console.c b/drivers/char/hvc_console.c index 25ce15bb1c08..a632f25f144a 100644 --- a/drivers/char/hvc_console.c +++ b/drivers/char/hvc_console.c @@ -678,7 +678,7 @@ int hvc_poll(struct hvc_struct *hp) EXPORT_SYMBOL_GPL(hvc_poll); /** - * hvc_resize() - Update terminal window size information. + * __hvc_resize() - Update terminal window size information. * @hp: HVC console pointer * @ws: Terminal window size structure * @@ -687,12 +687,12 @@ EXPORT_SYMBOL_GPL(hvc_poll); * * Locking: Locking free; the function MUST be called holding hp->lock */ -void hvc_resize(struct hvc_struct *hp, struct winsize ws) +void __hvc_resize(struct hvc_struct *hp, struct winsize ws) { hp->ws = ws; schedule_work(&hp->tty_resize); } -EXPORT_SYMBOL_GPL(hvc_resize); +EXPORT_SYMBOL_GPL(__hvc_resize); /* * This kthread is either polling or interrupt driven. This is determined by diff --git a/drivers/char/hvc_console.h b/drivers/char/hvc_console.h index 3c85d78c975c..10950ca706d8 100644 --- a/drivers/char/hvc_console.h +++ b/drivers/char/hvc_console.h @@ -28,6 +28,7 @@ #define HVC_CONSOLE_H #include <linux/kref.h> #include <linux/tty.h> +#include <linux/spinlock.h> /* * This is the max number of console adapters that can/will be found as @@ -88,7 +89,16 @@ int hvc_poll(struct hvc_struct *hp); void hvc_kick(void); /* Resize hvc tty terminal window */ -extern void hvc_resize(struct hvc_struct *hp, struct winsize ws); +extern void __hvc_resize(struct hvc_struct *hp, struct winsize ws); + +static inline void hvc_resize(struct hvc_struct *hp, struct winsize ws) +{ + unsigned long flags; + + spin_lock_irqsave(&hp->lock, flags); + __hvc_resize(hp, ws); + spin_unlock_irqrestore(&hp->lock, flags); +} /* default notifier for irq based notification */ extern int notifier_add_irq(struct hvc_struct *hp, int data); diff --git a/drivers/char/hvc_iucv.c b/drivers/char/hvc_iucv.c index 0ecac7e532f6..b8a5d654d3d0 100644 --- a/drivers/char/hvc_iucv.c +++ b/drivers/char/hvc_iucv.c @@ -273,7 +273,9 @@ static int hvc_iucv_write(struct hvc_iucv_private *priv, case MSG_TYPE_WINSIZE: if (rb->mbuf->datalen != sizeof(struct winsize)) break; - hvc_resize(priv->hvc, *((struct winsize *) rb->mbuf->data)); + /* The caller must ensure that the hvc is locked, which + * is the case when called from hvc_iucv_get_chars() */ + __hvc_resize(priv->hvc, *((struct winsize *) rb->mbuf->data)); break; case MSG_TYPE_ERROR: /* ignored ... */ diff --git a/drivers/char/hvc_xen.c b/drivers/char/hvc_xen.c index eba999f8598d..a6ee32b599a8 100644 --- a/drivers/char/hvc_xen.c +++ b/drivers/char/hvc_xen.c @@ -55,7 +55,7 @@ static inline void notify_daemon(void) notify_remote_via_evtchn(xen_start_info->console.domU.evtchn); } -static int write_console(uint32_t vtermno, const char *data, int len) +static int __write_console(const char *data, int len) { struct xencons_interface *intf = xencons_interface(); XENCONS_RING_IDX cons, prod; @@ -76,6 +76,29 @@ static int write_console(uint32_t vtermno, const char *data, int len) return sent; } +static int write_console(uint32_t vtermno, const char *data, int len) +{ + int ret = len; + + /* + * Make sure the whole buffer is emitted, polling if + * necessary. We don't ever want to rely on the hvc daemon + * because the most interesting console output is when the + * kernel is crippled. + */ + while (len) { + int sent = __write_console(data, len); + + data += sent; + len -= sent; + + if (unlikely(len)) + HYPERVISOR_sched_op(SCHEDOP_yield, NULL); + } + + return ret; +} + static int read_console(uint32_t vtermno, char *buf, int len) { struct xencons_interface *intf = xencons_interface(); diff --git a/drivers/char/hw_random/omap-rng.c b/drivers/char/hw_random/omap-rng.c index 00dd3de1be51..06aad0831c73 100644 --- a/drivers/char/hw_random/omap-rng.c +++ b/drivers/char/hw_random/omap-rng.c @@ -116,7 +116,7 @@ static int __devinit omap_rng_probe(struct platform_device *pdev) if (!res) return -ENOENT; - mem = request_mem_region(res->start, res->end - res->start + 1, + mem = request_mem_region(res->start, resource_size(res), pdev->name); if (mem == NULL) { ret = -EBUSY; @@ -124,7 +124,7 @@ static int __devinit omap_rng_probe(struct platform_device *pdev) } dev_set_drvdata(&pdev->dev, mem); - rng_base = ioremap(res->start, res->end - res->start + 1); + rng_base = ioremap(res->start, resource_size(res)); if (!rng_base) { ret = -ENOMEM; goto err_ioremap; diff --git a/drivers/char/hw_random/virtio-rng.c b/drivers/char/hw_random/virtio-rng.c index 32216b623248..915157fcff98 100644 --- a/drivers/char/hw_random/virtio-rng.c +++ b/drivers/char/hw_random/virtio-rng.c @@ -51,7 +51,7 @@ static void register_buffer(void) sg_init_one(&sg, random_data+data_left, RANDOM_DATA_SIZE-data_left); /* There should always be room for one buffer. */ - if (vq->vq_ops->add_buf(vq, &sg, 0, 1, random_data) != 0) + if (vq->vq_ops->add_buf(vq, &sg, 0, 1, random_data) < 0) BUG(); vq->vq_ops->kick(vq); } @@ -116,7 +116,7 @@ static int virtrng_probe(struct virtio_device *vdev) return 0; } -static void virtrng_remove(struct virtio_device *vdev) +static void __devexit virtrng_remove(struct virtio_device *vdev) { vdev->config->reset(vdev); hwrng_unregister(&virtio_hwrng); diff --git a/drivers/char/ipmi/ipmi_devintf.c b/drivers/char/ipmi/ipmi_devintf.c index 41fc11dc921c..65545de3dbf4 100644 --- a/drivers/char/ipmi/ipmi_devintf.c +++ b/drivers/char/ipmi/ipmi_devintf.c @@ -36,6 +36,7 @@ #include <linux/errno.h> #include <asm/system.h> #include <linux/poll.h> +#include <linux/sched.h> #include <linux/spinlock.h> #include <linux/slab.h> #include <linux/ipmi.h> diff --git a/drivers/char/ipmi/ipmi_msghandler.c b/drivers/char/ipmi/ipmi_msghandler.c index 09050797c76a..ec5e3f8df648 100644 --- a/drivers/char/ipmi/ipmi_msghandler.c +++ b/drivers/char/ipmi/ipmi_msghandler.c @@ -35,6 +35,7 @@ #include <linux/errno.h> #include <asm/system.h> #include <linux/poll.h> +#include <linux/sched.h> #include <linux/spinlock.h> #include <linux/mutex.h> #include <linux/slab.h> diff --git a/drivers/char/istallion.c b/drivers/char/istallion.c index ab2f3349c5c4..402838f4083e 100644 --- a/drivers/char/istallion.c +++ b/drivers/char/istallion.c @@ -19,6 +19,7 @@ /*****************************************************************************/ #include <linux/module.h> +#include <linux/sched.h> #include <linux/slab.h> #include <linux/smp_lock.h> #include <linux/interrupt.h> diff --git a/drivers/char/keyboard.c b/drivers/char/keyboard.c index 737be953cc58..950837cf9e9c 100644 --- a/drivers/char/keyboard.c +++ b/drivers/char/keyboard.c @@ -1249,7 +1249,7 @@ static void kbd_keycode(unsigned int keycode, int down, int hw_raw) if (keycode >= NR_KEYS) if (keycode >= KEY_BRL_DOT1 && keycode <= KEY_BRL_DOT8) - keysym = K(KT_BRL, keycode - KEY_BRL_DOT1 + 1); + keysym = U(K(KT_BRL, keycode - KEY_BRL_DOT1 + 1)); else return; else diff --git a/drivers/char/mem.c b/drivers/char/mem.c index 0aede1d6a9ea..a074fceb67d3 100644 --- a/drivers/char/mem.c +++ b/drivers/char/mem.c @@ -301,7 +301,7 @@ static inline int private_mapping_ok(struct vm_area_struct *vma) } #endif -static struct vm_operations_struct mmap_mem_ops = { +static const struct vm_operations_struct mmap_mem_ops = { #ifdef CONFIG_HAVE_IOREMAP_PROT .access = generic_access_phys #endif @@ -690,7 +690,7 @@ static ssize_t read_zero(struct file * file, char __user * buf, if (chunk > PAGE_SIZE) chunk = PAGE_SIZE; /* Just for latency reasons */ - unwritten = clear_user(buf, chunk); + unwritten = __clear_user(buf, chunk); written += chunk - unwritten; if (unwritten) break; diff --git a/drivers/char/misc.c b/drivers/char/misc.c index 1ee27cc23426..07fa612a58d5 100644 --- a/drivers/char/misc.c +++ b/drivers/char/misc.c @@ -91,7 +91,7 @@ static int misc_seq_show(struct seq_file *seq, void *v) } -static struct seq_operations misc_seq_ops = { +static const struct seq_operations misc_seq_ops = { .start = misc_seq_start, .next = misc_seq_next, .stop = misc_seq_stop, diff --git a/drivers/char/mspec.c b/drivers/char/mspec.c index 30f095a8c2d4..1997270bb6f4 100644 --- a/drivers/char/mspec.c +++ b/drivers/char/mspec.c @@ -239,7 +239,7 @@ mspec_fault(struct vm_area_struct *vma, struct vm_fault *vmf) return VM_FAULT_NOPAGE; } -static struct vm_operations_struct mspec_vm_ops = { +static const struct vm_operations_struct mspec_vm_ops = { .open = mspec_open, .close = mspec_close, .fault = mspec_fault, diff --git a/drivers/char/mwave/mwavedd.c b/drivers/char/mwave/mwavedd.c index 94ad2c3bfc4a..a4ec50c95072 100644 --- a/drivers/char/mwave/mwavedd.c +++ b/drivers/char/mwave/mwavedd.c @@ -281,12 +281,6 @@ static long mwave_ioctl(struct file *file, unsigned int iocmd, case IOCTL_MW_REGISTER_IPC: { unsigned int ipcnum = (unsigned int) ioarg; - PRINTK_3(TRACE_MWAVE, - "mwavedd::mwave_ioctl IOCTL_MW_REGISTER_IPC" - " ipcnum %x entry usIntCount %x\n", - ipcnum, - pDrvData->IPCs[ipcnum].usIntCount); - if (ipcnum >= ARRAY_SIZE(pDrvData->IPCs)) { PRINTK_ERROR(KERN_ERR_MWAVE "mwavedd::mwave_ioctl:" @@ -295,6 +289,12 @@ static long mwave_ioctl(struct file *file, unsigned int iocmd, ipcnum); return -EINVAL; } + PRINTK_3(TRACE_MWAVE, + "mwavedd::mwave_ioctl IOCTL_MW_REGISTER_IPC" + " ipcnum %x entry usIntCount %x\n", + ipcnum, + pDrvData->IPCs[ipcnum].usIntCount); + lock_kernel(); pDrvData->IPCs[ipcnum].bIsHere = FALSE; pDrvData->IPCs[ipcnum].bIsEnabled = TRUE; @@ -310,11 +310,6 @@ static long mwave_ioctl(struct file *file, unsigned int iocmd, case IOCTL_MW_GET_IPC: { unsigned int ipcnum = (unsigned int) ioarg; - PRINTK_3(TRACE_MWAVE, - "mwavedd::mwave_ioctl IOCTL_MW_GET_IPC" - " ipcnum %x, usIntCount %x\n", - ipcnum, - pDrvData->IPCs[ipcnum].usIntCount); if (ipcnum >= ARRAY_SIZE(pDrvData->IPCs)) { PRINTK_ERROR(KERN_ERR_MWAVE "mwavedd::mwave_ioctl:" @@ -322,6 +317,11 @@ static long mwave_ioctl(struct file *file, unsigned int iocmd, " Invalid ipcnum %x\n", ipcnum); return -EINVAL; } + PRINTK_3(TRACE_MWAVE, + "mwavedd::mwave_ioctl IOCTL_MW_GET_IPC" + " ipcnum %x, usIntCount %x\n", + ipcnum, + pDrvData->IPCs[ipcnum].usIntCount); lock_kernel(); if (pDrvData->IPCs[ipcnum].bIsEnabled == TRUE) { diff --git a/drivers/char/nozomi.c b/drivers/char/nozomi.c index ec58d8c387ff..d3400b20444f 100644 --- a/drivers/char/nozomi.c +++ b/drivers/char/nozomi.c @@ -48,6 +48,7 @@ #include <linux/tty.h> #include <linux/tty_driver.h> #include <linux/tty_flip.h> +#include <linux/sched.h> #include <linux/serial.h> #include <linux/interrupt.h> #include <linux/kmod.h> diff --git a/drivers/char/pty.c b/drivers/char/pty.c index 53761cefa915..62f282e67638 100644 --- a/drivers/char/pty.c +++ b/drivers/char/pty.c @@ -18,6 +18,7 @@ #include <linux/tty.h> #include <linux/tty_flip.h> #include <linux/fcntl.h> +#include <linux/sched.h> #include <linux/string.h> #include <linux/major.h> #include <linux/mm.h> @@ -261,6 +262,9 @@ done: return 0; } +/* Traditional BSD devices */ +#ifdef CONFIG_LEGACY_PTYS + static int pty_install(struct tty_driver *driver, struct tty_struct *tty) { struct tty_struct *o_tty; @@ -310,24 +314,6 @@ free_mem_out: return -ENOMEM; } - -static const struct tty_operations pty_ops = { - .install = pty_install, - .open = pty_open, - .close = pty_close, - .write = pty_write, - .write_room = pty_write_room, - .flush_buffer = pty_flush_buffer, - .chars_in_buffer = pty_chars_in_buffer, - .unthrottle = pty_unthrottle, - .set_termios = pty_set_termios, - .resize = pty_resize -}; - -/* Traditional BSD devices */ -#ifdef CONFIG_LEGACY_PTYS -static struct tty_driver *pty_driver, *pty_slave_driver; - static int pty_bsd_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg) { @@ -341,7 +327,12 @@ static int pty_bsd_ioctl(struct tty_struct *tty, struct file *file, static int legacy_count = CONFIG_LEGACY_PTY_COUNT; module_param(legacy_count, int, 0); -static const struct tty_operations pty_ops_bsd = { +/* + * The master side of a pty can do TIOCSPTLCK and thus + * has pty_bsd_ioctl. + */ +static const struct tty_operations master_pty_ops_bsd = { + .install = pty_install, .open = pty_open, .close = pty_close, .write = pty_write, @@ -354,8 +345,23 @@ static const struct tty_operations pty_ops_bsd = { .resize = pty_resize }; +static const struct tty_operations slave_pty_ops_bsd = { + .install = pty_install, + .open = pty_open, + .close = pty_close, + .write = pty_write, + .write_room = pty_write_room, + .flush_buffer = pty_flush_buffer, + .chars_in_buffer = pty_chars_in_buffer, + .unthrottle = pty_unthrottle, + .set_termios = pty_set_termios, + .resize = pty_resize +}; + static void __init legacy_pty_init(void) { + struct tty_driver *pty_driver, *pty_slave_driver; + if (legacy_count <= 0) return; @@ -383,7 +389,7 @@ static void __init legacy_pty_init(void) pty_driver->init_termios.c_ospeed = 38400; pty_driver->flags = TTY_DRIVER_RESET_TERMIOS | TTY_DRIVER_REAL_RAW; pty_driver->other = pty_slave_driver; - tty_set_operations(pty_driver, &pty_ops); + tty_set_operations(pty_driver, &master_pty_ops_bsd); pty_slave_driver->owner = THIS_MODULE; pty_slave_driver->driver_name = "pty_slave"; @@ -399,7 +405,7 @@ static void __init legacy_pty_init(void) pty_slave_driver->flags = TTY_DRIVER_RESET_TERMIOS | TTY_DRIVER_REAL_RAW; pty_slave_driver->other = pty_driver; - tty_set_operations(pty_slave_driver, &pty_ops); + tty_set_operations(pty_slave_driver, &slave_pty_ops_bsd); if (tty_register_driver(pty_driver)) panic("Couldn't register pty driver"); diff --git a/drivers/char/random.c b/drivers/char/random.c index d8a9255e1a3f..04b505e5a5e2 100644 --- a/drivers/char/random.c +++ b/drivers/char/random.c @@ -1231,7 +1231,7 @@ static char sysctl_bootid[16]; * as an ASCII string in the standard UUID format. If accesses via the * sysctl system call, it is returned as 16 bytes of binary data. */ -static int proc_do_uuid(ctl_table *table, int write, struct file *filp, +static int proc_do_uuid(ctl_table *table, int write, void __user *buffer, size_t *lenp, loff_t *ppos) { ctl_table fake_table; @@ -1254,7 +1254,7 @@ static int proc_do_uuid(ctl_table *table, int write, struct file *filp, fake_table.data = buf; fake_table.maxlen = sizeof(buf); - return proc_dostring(&fake_table, write, filp, buffer, lenp, ppos); + return proc_dostring(&fake_table, write, buffer, lenp, ppos); } static int uuid_strategy(ctl_table *table, diff --git a/drivers/char/rio/riocmd.c b/drivers/char/rio/riocmd.c index 01f2654d5a2e..f121357e5af0 100644 --- a/drivers/char/rio/riocmd.c +++ b/drivers/char/rio/riocmd.c @@ -32,6 +32,7 @@ */ #include <linux/module.h> +#include <linux/sched.h> #include <linux/slab.h> #include <linux/errno.h> #include <linux/tty.h> diff --git a/drivers/char/rio/rioctrl.c b/drivers/char/rio/rioctrl.c index eecee0f576d2..780506326a73 100644 --- a/drivers/char/rio/rioctrl.c +++ b/drivers/char/rio/rioctrl.c @@ -31,6 +31,7 @@ */ #include <linux/module.h> +#include <linux/sched.h> #include <linux/slab.h> #include <linux/errno.h> #include <asm/io.h> @@ -873,7 +874,7 @@ int riocontrol(struct rio_info *p, dev_t dev, int cmd, unsigned long arg, int su /* ** It is important that the product code is an unsigned object! */ - if (DownLoad.ProductCode > MAX_PRODUCT) { + if (DownLoad.ProductCode >= MAX_PRODUCT) { rio_dprintk(RIO_DEBUG_CTRL, "RIO_DOWNLOAD: Bad product code %d passed\n", DownLoad.ProductCode); p->RIOError.Error = NO_SUCH_PRODUCT; return -ENXIO; diff --git a/drivers/char/rio/riotty.c b/drivers/char/rio/riotty.c index 2fb49e89b324..47fab7c33073 100644 --- a/drivers/char/rio/riotty.c +++ b/drivers/char/rio/riotty.c @@ -33,6 +33,7 @@ #define __EXPLICIT_DEF_H__ #include <linux/module.h> +#include <linux/sched.h> #include <linux/slab.h> #include <linux/errno.h> #include <linux/tty.h> diff --git a/drivers/char/rtc.c b/drivers/char/rtc.c index e0d0f8b2696b..bc4ab3e54550 100644 --- a/drivers/char/rtc.c +++ b/drivers/char/rtc.c @@ -74,6 +74,7 @@ #include <linux/proc_fs.h> #include <linux/seq_file.h> #include <linux/spinlock.h> +#include <linux/sched.h> #include <linux/sysctl.h> #include <linux/wait.h> #include <linux/bcd.h> diff --git a/drivers/char/ser_a2232.c b/drivers/char/ser_a2232.c index 33a2b531802e..9610861d1f5f 100644 --- a/drivers/char/ser_a2232.c +++ b/drivers/char/ser_a2232.c @@ -89,6 +89,7 @@ #include <linux/interrupt.h> #include <linux/kernel.h> #include <linux/errno.h> +#include <linux/sched.h> #include <linux/tty.h> #include <asm/setup.h> diff --git a/drivers/char/serial167.c b/drivers/char/serial167.c index 5942a9d674c0..452370af95de 100644 --- a/drivers/char/serial167.c +++ b/drivers/char/serial167.c @@ -220,8 +220,7 @@ static inline int serial_paranoia_check(struct cyclades_port *info, char *name, return 1; } - if ((long)info < (long)(&cy_port[0]) - || (long)(&cy_port[NR_PORTS]) < (long)info) { + if (info < &cy_port[0] || info >= &cy_port[NR_PORTS]) { printk("Warning: cyclades_port out of range for (%s) in %s\n", name, routine); return 1; @@ -520,15 +519,13 @@ static irqreturn_t cd2401_tx_interrupt(int irq, void *dev_id) panic("TxInt on debug port!!!"); } #endif - - info = &cy_port[channel]; - /* validate the port number (as configured and open) */ if ((channel < 0) || (NR_PORTS <= channel)) { base_addr[CyIER] &= ~(CyTxMpty | CyTxRdy); base_addr[CyTEOIR] = CyNOTRANS; return IRQ_HANDLED; } + info = &cy_port[channel]; info->last_active = jiffies; if (info->tty == 0) { base_addr[CyIER] &= ~(CyTxMpty | CyTxRdy); diff --git a/drivers/char/sonypi.c b/drivers/char/sonypi.c index fd3dced97776..8c262aaf7c26 100644 --- a/drivers/char/sonypi.c +++ b/drivers/char/sonypi.c @@ -36,6 +36,7 @@ */ #include <linux/module.h> +#include <linux/sched.h> #include <linux/input.h> #include <linux/pci.h> #include <linux/init.h> diff --git a/drivers/char/stallion.c b/drivers/char/stallion.c index 53e504f41b20..db6dcfa35ba0 100644 --- a/drivers/char/stallion.c +++ b/drivers/char/stallion.c @@ -27,6 +27,7 @@ /*****************************************************************************/ #include <linux/module.h> +#include <linux/sched.h> #include <linux/slab.h> #include <linux/interrupt.h> #include <linux/tty.h> diff --git a/drivers/char/tlclk.c b/drivers/char/tlclk.c index 8f2284be68e1..80ea6bcfffdc 100644 --- a/drivers/char/tlclk.c +++ b/drivers/char/tlclk.c @@ -32,6 +32,7 @@ #include <linux/kernel.h> /* printk() */ #include <linux/fs.h> /* everything... */ #include <linux/errno.h> /* error codes */ +#include <linux/sched.h> #include <linux/slab.h> #include <linux/ioport.h> #include <linux/interrupt.h> diff --git a/drivers/char/tpm/tpm.c b/drivers/char/tpm/tpm.c index b0603b2e5684..47c2d2763456 100644 --- a/drivers/char/tpm/tpm.c +++ b/drivers/char/tpm/tpm.c @@ -696,8 +696,7 @@ int __tpm_pcr_read(struct tpm_chip *chip, int pcr_idx, u8 *res_buf) cmd.header.in = pcrread_header; cmd.params.pcrread_in.pcr_idx = cpu_to_be32(pcr_idx); - BUILD_BUG_ON(cmd.header.in.length > READ_PCR_RESULT_SIZE); - rc = transmit_cmd(chip, &cmd, cmd.header.in.length, + rc = transmit_cmd(chip, &cmd, READ_PCR_RESULT_SIZE, "attempting to read a pcr value"); if (rc == 0) @@ -742,7 +741,7 @@ EXPORT_SYMBOL_GPL(tpm_pcr_read); * the module usage count. */ #define TPM_ORD_PCR_EXTEND cpu_to_be32(20) -#define EXTEND_PCR_SIZE 34 +#define EXTEND_PCR_RESULT_SIZE 34 static struct tpm_input_header pcrextend_header = { .tag = TPM_TAG_RQU_COMMAND, .length = cpu_to_be32(34), @@ -760,10 +759,9 @@ int tpm_pcr_extend(u32 chip_num, int pcr_idx, const u8 *hash) return -ENODEV; cmd.header.in = pcrextend_header; - BUILD_BUG_ON(be32_to_cpu(cmd.header.in.length) > EXTEND_PCR_SIZE); cmd.params.pcrextend_in.pcr_idx = cpu_to_be32(pcr_idx); memcpy(cmd.params.pcrextend_in.hash, hash, TPM_DIGEST_SIZE); - rc = transmit_cmd(chip, &cmd, cmd.header.in.length, + rc = transmit_cmd(chip, &cmd, EXTEND_PCR_RESULT_SIZE, "attempting extend a PCR value"); module_put(chip->dev->driver->owner); diff --git a/drivers/char/tpm/tpm_bios.c b/drivers/char/tpm/tpm_bios.c index 0c2f55a38b95..bf2170fb1cdd 100644 --- a/drivers/char/tpm/tpm_bios.c +++ b/drivers/char/tpm/tpm_bios.c @@ -343,14 +343,14 @@ static int tpm_ascii_bios_measurements_show(struct seq_file *m, void *v) return 0; } -static struct seq_operations tpm_ascii_b_measurments_seqops = { +static const struct seq_operations tpm_ascii_b_measurments_seqops = { .start = tpm_bios_measurements_start, .next = tpm_bios_measurements_next, .stop = tpm_bios_measurements_stop, .show = tpm_ascii_bios_measurements_show, }; -static struct seq_operations tpm_binary_b_measurments_seqops = { +static const struct seq_operations tpm_binary_b_measurments_seqops = { .start = tpm_bios_measurements_start, .next = tpm_bios_measurements_next, .stop = tpm_bios_measurements_stop, diff --git a/drivers/char/tty_buffer.c b/drivers/char/tty_buffer.c index 3108991c5c8b..66fa4e10d76b 100644 --- a/drivers/char/tty_buffer.c +++ b/drivers/char/tty_buffer.c @@ -402,28 +402,26 @@ static void flush_to_ldisc(struct work_struct *work) container_of(work, struct tty_struct, buf.work.work); unsigned long flags; struct tty_ldisc *disc; - struct tty_buffer *tbuf, *head; - char *char_buf; - unsigned char *flag_buf; disc = tty_ldisc_ref(tty); if (disc == NULL) /* !TTY_LDISC */ return; spin_lock_irqsave(&tty->buf.lock, flags); - /* So we know a flush is running */ - set_bit(TTY_FLUSHING, &tty->flags); - head = tty->buf.head; - if (head != NULL) { - tty->buf.head = NULL; - for (;;) { - int count = head->commit - head->read; + + if (!test_and_set_bit(TTY_FLUSHING, &tty->flags)) { + struct tty_buffer *head; + while ((head = tty->buf.head) != NULL) { + int count; + char *char_buf; + unsigned char *flag_buf; + + count = head->commit - head->read; if (!count) { if (head->next == NULL) break; - tbuf = head; - head = head->next; - tty_buffer_free(tty, tbuf); + tty->buf.head = head->next; + tty_buffer_free(tty, head); continue; } /* Ldisc or user is trying to flush the buffers @@ -445,9 +443,9 @@ static void flush_to_ldisc(struct work_struct *work) flag_buf, count); spin_lock_irqsave(&tty->buf.lock, flags); } - /* Restore the queue head */ - tty->buf.head = head; + clear_bit(TTY_FLUSHING, &tty->flags); } + /* We may have a deferred request to flush the input buffer, if so pull the chain under the lock and empty the queue */ if (test_bit(TTY_FLUSHPENDING, &tty->flags)) { @@ -455,7 +453,6 @@ static void flush_to_ldisc(struct work_struct *work) clear_bit(TTY_FLUSHPENDING, &tty->flags); wake_up(&tty->read_wait); } - clear_bit(TTY_FLUSHING, &tty->flags); spin_unlock_irqrestore(&tty->buf.lock, flags); tty_ldisc_deref(disc); @@ -471,7 +468,7 @@ static void flush_to_ldisc(struct work_struct *work) */ void tty_flush_to_ldisc(struct tty_struct *tty) { - flush_to_ldisc(&tty->buf.work.work); + flush_delayed_work(&tty->buf.work); } /** diff --git a/drivers/char/tty_io.c b/drivers/char/tty_io.c index ea18a129b0b5..59499ee0fe6a 100644 --- a/drivers/char/tty_io.c +++ b/drivers/char/tty_io.c @@ -1389,7 +1389,7 @@ EXPORT_SYMBOL(tty_shutdown); * of ttys that the driver keeps. * * This method gets called from a work queue so that the driver private - * shutdown ops can sleep (needed for USB at least) + * cleanup ops can sleep (needed for USB at least) */ static void release_one_tty(struct work_struct *work) { @@ -1397,10 +1397,9 @@ static void release_one_tty(struct work_struct *work) container_of(work, struct tty_struct, hangup_work); struct tty_driver *driver = tty->driver; - if (tty->ops->shutdown) - tty->ops->shutdown(tty); - else - tty_shutdown(tty); + if (tty->ops->cleanup) + tty->ops->cleanup(tty); + tty->magic = 0; tty_driver_kref_put(driver); module_put(driver->owner); @@ -1415,6 +1414,12 @@ static void release_one_tty(struct work_struct *work) static void queue_release_one_tty(struct kref *kref) { struct tty_struct *tty = container_of(kref, struct tty_struct, kref); + + if (tty->ops->shutdown) + tty->ops->shutdown(tty); + else + tty_shutdown(tty); + /* The hangup queue is now free so we can reuse it rather than waste a chunk of memory for each port */ INIT_WORK(&tty->hangup_work, release_one_tty); diff --git a/drivers/char/tty_ldisc.c b/drivers/char/tty_ldisc.c index aafdbaebc16a..feb55075819b 100644 --- a/drivers/char/tty_ldisc.c +++ b/drivers/char/tty_ldisc.c @@ -518,7 +518,7 @@ static void tty_ldisc_restore(struct tty_struct *tty, struct tty_ldisc *old) static int tty_ldisc_halt(struct tty_struct *tty) { clear_bit(TTY_LDISC, &tty->flags); - return cancel_delayed_work(&tty->buf.work); + return cancel_delayed_work_sync(&tty->buf.work); } /** @@ -756,12 +756,9 @@ void tty_ldisc_hangup(struct tty_struct *tty) * N_TTY. */ if (tty->driver->flags & TTY_DRIVER_RESET_TERMIOS) { - /* Make sure the old ldisc is quiescent */ - tty_ldisc_halt(tty); - flush_scheduled_work(); - /* Avoid racing set_ldisc or tty_ldisc_release */ mutex_lock(&tty->ldisc_mutex); + tty_ldisc_halt(tty); if (tty->ldisc) { /* Not yet closed */ /* Switch back to N_TTY */ tty_ldisc_reinit(tty); diff --git a/drivers/char/tty_port.c b/drivers/char/tty_port.c index a4bbb28f10be..2e8552dc5eda 100644 --- a/drivers/char/tty_port.c +++ b/drivers/char/tty_port.c @@ -221,6 +221,9 @@ int tty_port_block_til_ready(struct tty_port *port, the port has just hung up or is in another error state */ if ((filp->f_flags & O_NONBLOCK) || (tty->flags & (1 << TTY_IO_ERROR))) { + /* Indicate we are open */ + if (tty->termios->c_cflag & CBAUD) + tty_port_raise_dtr_rts(port); port->flags |= ASYNC_NORMAL_ACTIVE; return 0; } diff --git a/drivers/char/uv_mmtimer.c b/drivers/char/uv_mmtimer.c new file mode 100644 index 000000000000..867b67be9f0a --- /dev/null +++ b/drivers/char/uv_mmtimer.c @@ -0,0 +1,216 @@ +/* + * Timer device implementation for SGI UV platform. + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Copyright (c) 2009 Silicon Graphics, Inc. All rights reserved. + * + */ + +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/ioctl.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/errno.h> +#include <linux/mm.h> +#include <linux/fs.h> +#include <linux/mmtimer.h> +#include <linux/miscdevice.h> +#include <linux/posix-timers.h> +#include <linux/interrupt.h> +#include <linux/time.h> +#include <linux/math64.h> +#include <linux/smp_lock.h> + +#include <asm/genapic.h> +#include <asm/uv/uv_hub.h> +#include <asm/uv/bios.h> +#include <asm/uv/uv.h> + +MODULE_AUTHOR("Dimitri Sivanich <sivanich@sgi.com>"); +MODULE_DESCRIPTION("SGI UV Memory Mapped RTC Timer"); +MODULE_LICENSE("GPL"); + +/* name of the device, usually in /dev */ +#define UV_MMTIMER_NAME "mmtimer" +#define UV_MMTIMER_DESC "SGI UV Memory Mapped RTC Timer" +#define UV_MMTIMER_VERSION "1.0" + +static long uv_mmtimer_ioctl(struct file *file, unsigned int cmd, + unsigned long arg); +static int uv_mmtimer_mmap(struct file *file, struct vm_area_struct *vma); + +/* + * Period in femtoseconds (10^-15 s) + */ +static unsigned long uv_mmtimer_femtoperiod; + +static const struct file_operations uv_mmtimer_fops = { + .owner = THIS_MODULE, + .mmap = uv_mmtimer_mmap, + .unlocked_ioctl = uv_mmtimer_ioctl, +}; + +/** + * uv_mmtimer_ioctl - ioctl interface for /dev/uv_mmtimer + * @file: file structure for the device + * @cmd: command to execute + * @arg: optional argument to command + * + * Executes the command specified by @cmd. Returns 0 for success, < 0 for + * failure. + * + * Valid commands: + * + * %MMTIMER_GETOFFSET - Should return the offset (relative to the start + * of the page where the registers are mapped) for the counter in question. + * + * %MMTIMER_GETRES - Returns the resolution of the clock in femto (10^-15) + * seconds + * + * %MMTIMER_GETFREQ - Copies the frequency of the clock in Hz to the address + * specified by @arg + * + * %MMTIMER_GETBITS - Returns the number of bits in the clock's counter + * + * %MMTIMER_MMAPAVAIL - Returns 1 if registers can be mmap'd into userspace + * + * %MMTIMER_GETCOUNTER - Gets the current value in the counter and places it + * in the address specified by @arg. + */ +static long uv_mmtimer_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + int ret = 0; + + switch (cmd) { + case MMTIMER_GETOFFSET: /* offset of the counter */ + /* + * UV RTC register is on its own page + */ + if (PAGE_SIZE <= (1 << 16)) + ret = ((UV_LOCAL_MMR_BASE | UVH_RTC) & (PAGE_SIZE-1)) + / 8; + else + ret = -ENOSYS; + break; + + case MMTIMER_GETRES: /* resolution of the clock in 10^-15 s */ + if (copy_to_user((unsigned long __user *)arg, + &uv_mmtimer_femtoperiod, sizeof(unsigned long))) + ret = -EFAULT; + break; + + case MMTIMER_GETFREQ: /* frequency in Hz */ + if (copy_to_user((unsigned long __user *)arg, + &sn_rtc_cycles_per_second, + sizeof(unsigned long))) + ret = -EFAULT; + break; + + case MMTIMER_GETBITS: /* number of bits in the clock */ + ret = hweight64(UVH_RTC_REAL_TIME_CLOCK_MASK); + break; + + case MMTIMER_MMAPAVAIL: /* can we mmap the clock into userspace? */ + ret = (PAGE_SIZE <= (1 << 16)) ? 1 : 0; + break; + + case MMTIMER_GETCOUNTER: + if (copy_to_user((unsigned long __user *)arg, + (unsigned long *)uv_local_mmr_address(UVH_RTC), + sizeof(unsigned long))) + ret = -EFAULT; + break; + default: + ret = -ENOTTY; + break; + } + return ret; +} + +/** + * uv_mmtimer_mmap - maps the clock's registers into userspace + * @file: file structure for the device + * @vma: VMA to map the registers into + * + * Calls remap_pfn_range() to map the clock's registers into + * the calling process' address space. + */ +static int uv_mmtimer_mmap(struct file *file, struct vm_area_struct *vma) +{ + unsigned long uv_mmtimer_addr; + + if (vma->vm_end - vma->vm_start != PAGE_SIZE) + return -EINVAL; + + if (vma->vm_flags & VM_WRITE) + return -EPERM; + + if (PAGE_SIZE > (1 << 16)) + return -ENOSYS; + + vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); + + uv_mmtimer_addr = UV_LOCAL_MMR_BASE | UVH_RTC; + uv_mmtimer_addr &= ~(PAGE_SIZE - 1); + uv_mmtimer_addr &= 0xfffffffffffffffUL; + + if (remap_pfn_range(vma, vma->vm_start, uv_mmtimer_addr >> PAGE_SHIFT, + PAGE_SIZE, vma->vm_page_prot)) { + printk(KERN_ERR "remap_pfn_range failed in uv_mmtimer_mmap\n"); + return -EAGAIN; + } + + return 0; +} + +static struct miscdevice uv_mmtimer_miscdev = { + MISC_DYNAMIC_MINOR, + UV_MMTIMER_NAME, + &uv_mmtimer_fops +}; + + +/** + * uv_mmtimer_init - device initialization routine + * + * Does initial setup for the uv_mmtimer device. + */ +static int __init uv_mmtimer_init(void) +{ + if (!is_uv_system()) { + printk(KERN_ERR "%s: Hardware unsupported\n", UV_MMTIMER_NAME); + return -1; + } + + /* + * Sanity check the cycles/sec variable + */ + if (sn_rtc_cycles_per_second < 100000) { + printk(KERN_ERR "%s: unable to determine clock frequency\n", + UV_MMTIMER_NAME); + return -1; + } + + uv_mmtimer_femtoperiod = ((unsigned long)1E15 + + sn_rtc_cycles_per_second / 2) / + sn_rtc_cycles_per_second; + + if (misc_register(&uv_mmtimer_miscdev)) { + printk(KERN_ERR "%s: failed to register device\n", + UV_MMTIMER_NAME); + return -1; + } + + printk(KERN_INFO "%s: v%s, %ld MHz\n", UV_MMTIMER_DESC, + UV_MMTIMER_VERSION, + sn_rtc_cycles_per_second/(unsigned long)1E6); + + return 0; +} + +module_init(uv_mmtimer_init); diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c index c74dacfa6795..a035ae39a359 100644 --- a/drivers/char/virtio_console.c +++ b/drivers/char/virtio_console.c @@ -65,7 +65,7 @@ static int put_chars(u32 vtermno, const char *buf, int count) /* add_buf wants a token to identify this buffer: we hand it any * non-NULL pointer, since there's only ever one buffer. */ - if (out_vq->vq_ops->add_buf(out_vq, sg, 1, 0, (void *)1) == 0) { + if (out_vq->vq_ops->add_buf(out_vq, sg, 1, 0, (void *)1) >= 0) { /* Tell Host to go! */ out_vq->vq_ops->kick(out_vq); /* Chill out until it's done with the buffer. */ @@ -85,7 +85,7 @@ static void add_inbuf(void) sg_init_one(sg, inbuf, PAGE_SIZE); /* We should always be able to add one buffer to an empty queue. */ - if (in_vq->vq_ops->add_buf(in_vq, sg, 0, 1, inbuf) != 0) + if (in_vq->vq_ops->add_buf(in_vq, sg, 0, 1, inbuf) < 0) BUG(); in_vq->vq_ops->kick(in_vq); } diff --git a/drivers/char/vt_ioctl.c b/drivers/char/vt_ioctl.c index 29c651ab0d78..6aa10284104a 100644 --- a/drivers/char/vt_ioctl.c +++ b/drivers/char/vt_ioctl.c @@ -103,8 +103,8 @@ void vt_event_post(unsigned int event, unsigned int old, unsigned int new) ve->event.event = event; /* kernel view is consoles 0..n-1, user space view is console 1..n with 0 meaning current, so we must bias */ - ve->event.old = old + 1; - ve->event.new = new + 1; + ve->event.oldev = old + 1; + ve->event.newev = new + 1; wake = 1; ve->done = 1; } @@ -186,7 +186,7 @@ int vt_waitactive(int n) vt_event_wait(&vw); if (vw.done == 0) return -EINTR; - } while (vw.event.new != n); + } while (vw.event.newev != n); return 0; } @@ -981,8 +981,10 @@ int vt_ioctl(struct tty_struct *tty, struct file * file, goto eperm; if (copy_from_user(&vsa, (struct vt_setactivate __user *)arg, - sizeof(struct vt_setactivate))) - return -EFAULT; + sizeof(struct vt_setactivate))) { + ret = -EFAULT; + goto out; + } if (vsa.console == 0 || vsa.console > MAX_NR_CONSOLES) ret = -ENXIO; else { @@ -1530,7 +1532,7 @@ long vt_compat_ioctl(struct tty_struct *tty, struct file * file, case PIO_UNIMAP: case GIO_UNIMAP: - ret = do_unimap_ioctl(cmd, up, perm, vc); + ret = compat_unimap_ioctl(cmd, up, perm, vc); break; /* diff --git a/drivers/char/xilinx_hwicap/xilinx_hwicap.c b/drivers/char/xilinx_hwicap/xilinx_hwicap.c index f40ab699860f..4846d50199f3 100644 --- a/drivers/char/xilinx_hwicap/xilinx_hwicap.c +++ b/drivers/char/xilinx_hwicap/xilinx_hwicap.c @@ -559,7 +559,7 @@ static int hwicap_release(struct inode *inode, struct file *file) return status; } -static struct file_operations hwicap_fops = { +static const struct file_operations hwicap_fops = { .owner = THIS_MODULE, .write = hwicap_write, .read = hwicap_read, diff --git a/drivers/connector/cn_proc.c b/drivers/connector/cn_proc.c index 85e5dc0431fe..60697909ebdb 100644 --- a/drivers/connector/cn_proc.c +++ b/drivers/connector/cn_proc.c @@ -139,6 +139,31 @@ void proc_id_connector(struct task_struct *task, int which_id) cn_netlink_send(msg, CN_IDX_PROC, GFP_KERNEL); } +void proc_sid_connector(struct task_struct *task) +{ + struct cn_msg *msg; + struct proc_event *ev; + struct timespec ts; + __u8 buffer[CN_PROC_MSG_SIZE]; + + if (atomic_read(&proc_event_num_listeners) < 1) + return; + + msg = (struct cn_msg *)buffer; + ev = (struct proc_event *)msg->data; + get_seq(&msg->seq, &ev->cpu); + ktime_get_ts(&ts); /* get high res monotonic timestamp */ + put_unaligned(timespec_to_ns(&ts), (__u64 *)&ev->timestamp_ns); + ev->what = PROC_EVENT_SID; + ev->event_data.sid.process_pid = task->pid; + ev->event_data.sid.process_tgid = task->tgid; + + memcpy(&msg->id, &cn_proc_event_id, sizeof(msg->id)); + msg->ack = 0; /* not used */ + msg->len = sizeof(*ev); + cn_netlink_send(msg, CN_IDX_PROC, GFP_KERNEL); +} + void proc_exit_connector(struct task_struct *task) { struct cn_msg *msg; @@ -202,7 +227,8 @@ static void cn_proc_ack(int err, int rcvd_seq, int rcvd_ack) * cn_proc_mcast_ctl * @data: message sent from userspace via the connector */ -static void cn_proc_mcast_ctl(struct cn_msg *msg) +static void cn_proc_mcast_ctl(struct cn_msg *msg, + struct netlink_skb_parms *nsp) { enum proc_cn_mcast_op *mc_op = NULL; int err = 0; diff --git a/drivers/connector/cn_queue.c b/drivers/connector/cn_queue.c index 4a1dfe1f4ba9..210338ea222f 100644 --- a/drivers/connector/cn_queue.c +++ b/drivers/connector/cn_queue.c @@ -78,18 +78,20 @@ void cn_queue_wrapper(struct work_struct *work) struct cn_callback_entry *cbq = container_of(work, struct cn_callback_entry, work); struct cn_callback_data *d = &cbq->data; + struct cn_msg *msg = NLMSG_DATA(nlmsg_hdr(d->skb)); + struct netlink_skb_parms *nsp = &NETLINK_CB(d->skb); - d->callback(d->callback_priv); + d->callback(msg, nsp); - d->destruct_data(d->ddata); - d->ddata = NULL; + kfree_skb(d->skb); + d->skb = NULL; kfree(d->free); } static struct cn_callback_entry * cn_queue_alloc_callback_entry(char *name, struct cb_id *id, - void (*callback)(struct cn_msg *)) + void (*callback)(struct cn_msg *, struct netlink_skb_parms *)) { struct cn_callback_entry *cbq; @@ -123,7 +125,7 @@ int cn_cb_equal(struct cb_id *i1, struct cb_id *i2) } int cn_queue_add_callback(struct cn_queue_dev *dev, char *name, struct cb_id *id, - void (*callback)(struct cn_msg *)) + void (*callback)(struct cn_msg *, struct netlink_skb_parms *)) { struct cn_callback_entry *cbq, *__cbq; int found = 0; diff --git a/drivers/connector/connector.c b/drivers/connector/connector.c index 74f52af79563..f06024668f99 100644 --- a/drivers/connector/connector.c +++ b/drivers/connector/connector.c @@ -129,21 +129,19 @@ EXPORT_SYMBOL_GPL(cn_netlink_send); /* * Callback helper - queues work and setup destructor for given data. */ -static int cn_call_callback(struct cn_msg *msg, void (*destruct_data)(void *), void *data) +static int cn_call_callback(struct sk_buff *skb) { struct cn_callback_entry *__cbq, *__new_cbq; struct cn_dev *dev = &cdev; + struct cn_msg *msg = NLMSG_DATA(nlmsg_hdr(skb)); int err = -ENODEV; spin_lock_bh(&dev->cbdev->queue_lock); list_for_each_entry(__cbq, &dev->cbdev->queue_list, callback_entry) { if (cn_cb_equal(&__cbq->id.id, &msg->id)) { if (likely(!work_pending(&__cbq->work) && - __cbq->data.ddata == NULL)) { - __cbq->data.callback_priv = msg; - - __cbq->data.ddata = data; - __cbq->data.destruct_data = destruct_data; + __cbq->data.skb == NULL)) { + __cbq->data.skb = skb; if (queue_cn_work(__cbq, &__cbq->work)) err = 0; @@ -156,10 +154,8 @@ static int cn_call_callback(struct cn_msg *msg, void (*destruct_data)(void *), v __new_cbq = kzalloc(sizeof(struct cn_callback_entry), GFP_ATOMIC); if (__new_cbq) { d = &__new_cbq->data; - d->callback_priv = msg; + d->skb = skb; d->callback = __cbq->data.callback; - d->ddata = data; - d->destruct_data = destruct_data; d->free = __new_cbq; __new_cbq->pdev = __cbq->pdev; @@ -191,7 +187,6 @@ static int cn_call_callback(struct cn_msg *msg, void (*destruct_data)(void *), v */ static void cn_rx_skb(struct sk_buff *__skb) { - struct cn_msg *msg; struct nlmsghdr *nlh; int err; struct sk_buff *skb; @@ -208,8 +203,7 @@ static void cn_rx_skb(struct sk_buff *__skb) return; } - msg = NLMSG_DATA(nlh); - err = cn_call_callback(msg, (void (*)(void *))kfree_skb, skb); + err = cn_call_callback(skb); if (err < 0) kfree_skb(skb); } @@ -270,7 +264,7 @@ static void cn_notify(struct cb_id *id, u32 notify_event) * May sleep. */ int cn_add_callback(struct cb_id *id, char *name, - void (*callback)(struct cn_msg *)) + void (*callback)(struct cn_msg *, struct netlink_skb_parms *)) { int err; struct cn_dev *dev = &cdev; @@ -352,7 +346,7 @@ static int cn_ctl_msg_equals(struct cn_ctl_msg *m1, struct cn_ctl_msg *m2) * * Used for notification of a request's processing. */ -static void cn_callback(struct cn_msg *msg) +static void cn_callback(struct cn_msg *msg, struct netlink_skb_parms *nsp) { struct cn_ctl_msg *ctl; struct cn_ctl_entry *ent; diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c index 3938c7817095..ff57c40e9b8b 100644 --- a/drivers/cpufreq/cpufreq.c +++ b/drivers/cpufreq/cpufreq.c @@ -41,7 +41,7 @@ static struct cpufreq_driver *cpufreq_driver; static DEFINE_PER_CPU(struct cpufreq_policy *, cpufreq_cpu_data); #ifdef CONFIG_HOTPLUG_CPU /* This one keeps track of the previously set governor of a removed CPU */ -static DEFINE_PER_CPU(struct cpufreq_governor *, cpufreq_cpu_governor); +static DEFINE_PER_CPU(char[CPUFREQ_NAME_LEN], cpufreq_cpu_governor); #endif static DEFINE_SPINLOCK(cpufreq_driver_lock); @@ -774,10 +774,12 @@ int cpufreq_add_dev_policy(unsigned int cpu, struct cpufreq_policy *policy, #ifdef CONFIG_SMP unsigned long flags; unsigned int j; - #ifdef CONFIG_HOTPLUG_CPU - if (per_cpu(cpufreq_cpu_governor, cpu)) { - policy->governor = per_cpu(cpufreq_cpu_governor, cpu); + struct cpufreq_governor *gov; + + gov = __find_governor(per_cpu(cpufreq_cpu_governor, cpu)); + if (gov) { + policy->governor = gov; dprintk("Restoring governor %s for cpu %d\n", policy->governor->name, cpu); } @@ -949,10 +951,13 @@ err_out_kobj_put: static int cpufreq_add_dev(struct sys_device *sys_dev) { unsigned int cpu = sys_dev->id; - int ret = 0; + int ret = 0, found = 0; struct cpufreq_policy *policy; unsigned long flags; unsigned int j; +#ifdef CONFIG_HOTPLUG_CPU + int sibling; +#endif if (cpu_is_offline(cpu)) return 0; @@ -999,7 +1004,19 @@ static int cpufreq_add_dev(struct sys_device *sys_dev) INIT_WORK(&policy->update, handle_update); /* Set governor before ->init, so that driver could check it */ - policy->governor = CPUFREQ_DEFAULT_GOVERNOR; +#ifdef CONFIG_HOTPLUG_CPU + for_each_online_cpu(sibling) { + struct cpufreq_policy *cp = per_cpu(cpufreq_cpu_data, sibling); + if (cp && cp->governor && + (cpumask_test_cpu(cpu, cp->related_cpus))) { + policy->governor = cp->governor; + found = 1; + break; + } + } +#endif + if (!found) + policy->governor = CPUFREQ_DEFAULT_GOVERNOR; /* call driver. From then on the cpufreq must be able * to accept all calls to ->verify and ->setpolicy for this CPU */ @@ -1111,7 +1128,8 @@ static int __cpufreq_remove_dev(struct sys_device *sys_dev) #ifdef CONFIG_SMP #ifdef CONFIG_HOTPLUG_CPU - per_cpu(cpufreq_cpu_governor, cpu) = data->governor; + strncpy(per_cpu(cpufreq_cpu_governor, cpu), data->governor->name, + CPUFREQ_NAME_LEN); #endif /* if we have other CPUs still registered, we need to unlink them, @@ -1135,7 +1153,8 @@ static int __cpufreq_remove_dev(struct sys_device *sys_dev) continue; dprintk("removing link for cpu %u\n", j); #ifdef CONFIG_HOTPLUG_CPU - per_cpu(cpufreq_cpu_governor, j) = data->governor; + strncpy(per_cpu(cpufreq_cpu_governor, j), + data->governor->name, CPUFREQ_NAME_LEN); #endif cpu_sys_dev = get_cpu_sysdev(j); sysfs_remove_link(&cpu_sys_dev->kobj, "cpufreq"); @@ -1606,9 +1625,22 @@ EXPORT_SYMBOL_GPL(cpufreq_register_governor); void cpufreq_unregister_governor(struct cpufreq_governor *governor) { +#ifdef CONFIG_HOTPLUG_CPU + int cpu; +#endif + if (!governor) return; +#ifdef CONFIG_HOTPLUG_CPU + for_each_present_cpu(cpu) { + if (cpu_online(cpu)) + continue; + if (!strcmp(per_cpu(cpufreq_cpu_governor, cpu), governor->name)) + strcpy(per_cpu(cpufreq_cpu_governor, cpu), "\0"); + } +#endif + mutex_lock(&cpufreq_governor_mutex); list_del(&governor->governor_list); mutex_unlock(&cpufreq_governor_mutex); diff --git a/drivers/cpufreq/cpufreq_conservative.c b/drivers/cpufreq/cpufreq_conservative.c index bc33ddc9c97c..c7b081b839ff 100644 --- a/drivers/cpufreq/cpufreq_conservative.c +++ b/drivers/cpufreq/cpufreq_conservative.c @@ -116,9 +116,9 @@ static inline cputime64_t get_cpu_idle_time_jiffy(unsigned int cpu, idle_time = cputime64_sub(cur_wall_time, busy_time); if (wall) - *wall = cur_wall_time; + *wall = (cputime64_t)jiffies_to_usecs(cur_wall_time); - return idle_time; + return (cputime64_t)jiffies_to_usecs(idle_time);; } static inline cputime64_t get_cpu_idle_time(unsigned int cpu, cputime64_t *wall) diff --git a/drivers/cpufreq/cpufreq_ondemand.c b/drivers/cpufreq/cpufreq_ondemand.c index 071699de50ee..4b34ade2332b 100644 --- a/drivers/cpufreq/cpufreq_ondemand.c +++ b/drivers/cpufreq/cpufreq_ondemand.c @@ -133,9 +133,9 @@ static inline cputime64_t get_cpu_idle_time_jiffy(unsigned int cpu, idle_time = cputime64_sub(cur_wall_time, busy_time); if (wall) - *wall = cur_wall_time; + *wall = (cputime64_t)jiffies_to_usecs(cur_wall_time); - return idle_time; + return (cputime64_t)jiffies_to_usecs(idle_time); } static inline cputime64_t get_cpu_idle_time(unsigned int cpu, cputime64_t *wall) diff --git a/drivers/cpuidle/cpuidle.c b/drivers/cpuidle/cpuidle.c index ad41f19b8e3f..12fdd3987a36 100644 --- a/drivers/cpuidle/cpuidle.c +++ b/drivers/cpuidle/cpuidle.c @@ -76,8 +76,11 @@ static void cpuidle_idle_call(void) #endif /* ask the governor for the next state */ next_state = cpuidle_curr_governor->select(dev); - if (need_resched()) + if (need_resched()) { + local_irq_enable(); return; + } + target_state = &dev->states[next_state]; /* enter the state and update stats */ diff --git a/drivers/crypto/padlock-sha.c b/drivers/crypto/padlock-sha.c index 76cb6b345e7b..0af80577dc7b 100644 --- a/drivers/crypto/padlock-sha.c +++ b/drivers/crypto/padlock-sha.c @@ -24,6 +24,12 @@ #include <asm/i387.h> #include "padlock.h" +#ifdef CONFIG_64BIT +#define STACK_ALIGN 16 +#else +#define STACK_ALIGN 4 +#endif + struct padlock_sha_desc { struct shash_desc fallback; }; @@ -64,7 +70,9 @@ static int padlock_sha1_finup(struct shash_desc *desc, const u8 *in, /* We can't store directly to *out as it may be unaligned. */ /* BTW Don't reduce the buffer size below 128 Bytes! * PadLock microcode needs it that big. */ - char result[128] __attribute__ ((aligned(PADLOCK_ALIGNMENT))); + char buf[128 + PADLOCK_ALIGNMENT - STACK_ALIGN] __attribute__ + ((aligned(STACK_ALIGN))); + char *result = PTR_ALIGN(&buf[0], PADLOCK_ALIGNMENT); struct padlock_sha_desc *dctx = shash_desc_ctx(desc); struct sha1_state state; unsigned int space; @@ -128,7 +136,9 @@ static int padlock_sha256_finup(struct shash_desc *desc, const u8 *in, /* We can't store directly to *out as it may be unaligned. */ /* BTW Don't reduce the buffer size below 128 Bytes! * PadLock microcode needs it that big. */ - char result[128] __attribute__ ((aligned(PADLOCK_ALIGNMENT))); + char buf[128 + PADLOCK_ALIGNMENT - STACK_ALIGN] __attribute__ + ((aligned(STACK_ALIGN))); + char *result = PTR_ALIGN(&buf[0], PADLOCK_ALIGNMENT); struct padlock_sha_desc *dctx = shash_desc_ctx(desc); struct sha256_state state; unsigned int space; diff --git a/drivers/dca/dca-core.c b/drivers/dca/dca-core.c index 25b743abfb59..52e6bb70a490 100644 --- a/drivers/dca/dca-core.c +++ b/drivers/dca/dca-core.c @@ -28,7 +28,7 @@ #include <linux/device.h> #include <linux/dca.h> -#define DCA_VERSION "1.8" +#define DCA_VERSION "1.12.1" MODULE_VERSION(DCA_VERSION); MODULE_LICENSE("GPL"); @@ -36,20 +36,92 @@ MODULE_AUTHOR("Intel Corporation"); static DEFINE_SPINLOCK(dca_lock); -static LIST_HEAD(dca_providers); +static LIST_HEAD(dca_domains); -static struct dca_provider *dca_find_provider_by_dev(struct device *dev) +static struct pci_bus *dca_pci_rc_from_dev(struct device *dev) { - struct dca_provider *dca, *ret = NULL; + struct pci_dev *pdev = to_pci_dev(dev); + struct pci_bus *bus = pdev->bus; - list_for_each_entry(dca, &dca_providers, node) { - if ((!dev) || (dca->ops->dev_managed(dca, dev))) { - ret = dca; - break; - } + while (bus->parent) + bus = bus->parent; + + return bus; +} + +static struct dca_domain *dca_allocate_domain(struct pci_bus *rc) +{ + struct dca_domain *domain; + + domain = kzalloc(sizeof(*domain), GFP_NOWAIT); + if (!domain) + return NULL; + + INIT_LIST_HEAD(&domain->dca_providers); + domain->pci_rc = rc; + + return domain; +} + +static void dca_free_domain(struct dca_domain *domain) +{ + list_del(&domain->node); + kfree(domain); +} + +static struct dca_domain *dca_find_domain(struct pci_bus *rc) +{ + struct dca_domain *domain; + + list_for_each_entry(domain, &dca_domains, node) + if (domain->pci_rc == rc) + return domain; + + return NULL; +} + +static struct dca_domain *dca_get_domain(struct device *dev) +{ + struct pci_bus *rc; + struct dca_domain *domain; + + rc = dca_pci_rc_from_dev(dev); + domain = dca_find_domain(rc); + + if (!domain) { + domain = dca_allocate_domain(rc); + if (domain) + list_add(&domain->node, &dca_domains); + } + + return domain; +} + +static struct dca_provider *dca_find_provider_by_dev(struct device *dev) +{ + struct dca_provider *dca; + struct pci_bus *rc; + struct dca_domain *domain; + + if (dev) { + rc = dca_pci_rc_from_dev(dev); + domain = dca_find_domain(rc); + if (!domain) + return NULL; + } else { + if (!list_empty(&dca_domains)) + domain = list_first_entry(&dca_domains, + struct dca_domain, + node); + else + return NULL; } - return ret; + list_for_each_entry(dca, &domain->dca_providers, node) + if ((!dev) || (dca->ops->dev_managed(dca, dev))) + return dca; + + return NULL; } /** @@ -61,6 +133,8 @@ int dca_add_requester(struct device *dev) struct dca_provider *dca; int err, slot = -ENODEV; unsigned long flags; + struct pci_bus *pci_rc; + struct dca_domain *domain; if (!dev) return -EFAULT; @@ -74,7 +148,14 @@ int dca_add_requester(struct device *dev) return -EEXIST; } - list_for_each_entry(dca, &dca_providers, node) { + pci_rc = dca_pci_rc_from_dev(dev); + domain = dca_find_domain(pci_rc); + if (!domain) { + spin_unlock_irqrestore(&dca_lock, flags); + return -ENODEV; + } + + list_for_each_entry(dca, &domain->dca_providers, node) { slot = dca->ops->add_requester(dca, dev); if (slot >= 0) break; @@ -222,13 +303,19 @@ int register_dca_provider(struct dca_provider *dca, struct device *dev) { int err; unsigned long flags; + struct dca_domain *domain; err = dca_sysfs_add_provider(dca, dev); if (err) return err; spin_lock_irqsave(&dca_lock, flags); - list_add(&dca->node, &dca_providers); + domain = dca_get_domain(dev); + if (!domain) { + spin_unlock_irqrestore(&dca_lock, flags); + return -ENODEV; + } + list_add(&dca->node, &domain->dca_providers); spin_unlock_irqrestore(&dca_lock, flags); blocking_notifier_call_chain(&dca_provider_chain, @@ -241,15 +328,24 @@ EXPORT_SYMBOL_GPL(register_dca_provider); * unregister_dca_provider - remove a dca provider * @dca - struct created by alloc_dca_provider() */ -void unregister_dca_provider(struct dca_provider *dca) +void unregister_dca_provider(struct dca_provider *dca, struct device *dev) { unsigned long flags; + struct pci_bus *pci_rc; + struct dca_domain *domain; blocking_notifier_call_chain(&dca_provider_chain, DCA_PROVIDER_REMOVE, NULL); spin_lock_irqsave(&dca_lock, flags); + list_del(&dca->node); + + pci_rc = dca_pci_rc_from_dev(dev); + domain = dca_find_domain(pci_rc); + if (list_empty(&domain->dca_providers)) + dca_free_domain(domain); + spin_unlock_irqrestore(&dca_lock, flags); dca_sysfs_remove_provider(dca); @@ -276,7 +372,7 @@ EXPORT_SYMBOL_GPL(dca_unregister_notify); static int __init dca_init(void) { - printk(KERN_ERR "dca service started, version %s\n", DCA_VERSION); + pr_info("dca service started, version %s\n", DCA_VERSION); return dca_sysfs_init(); } diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig index 81e1020fb514..b401dadad4a8 100644 --- a/drivers/dma/Kconfig +++ b/drivers/dma/Kconfig @@ -17,11 +17,17 @@ if DMADEVICES comment "DMA Devices" +config ASYNC_TX_DISABLE_CHANNEL_SWITCH + bool + config INTEL_IOATDMA tristate "Intel I/OAT DMA support" depends on PCI && X86 select DMA_ENGINE select DCA + select ASYNC_TX_DISABLE_CHANNEL_SWITCH + select ASYNC_TX_DISABLE_PQ_VAL_DMA + select ASYNC_TX_DISABLE_XOR_VAL_DMA help Enable support for the Intel(R) I/OAT DMA engine present in recent Intel Xeon chipsets. @@ -97,6 +103,14 @@ config TXX9_DMAC Support the TXx9 SoC internal DMA controller. This can be integrated in chips such as the Toshiba TX4927/38/39. +config SH_DMAE + tristate "Renesas SuperH DMAC support" + depends on SUPERH && SH_DMA + depends on !SH_DMA_API + select DMA_ENGINE + help + Enable support for the Renesas SuperH DMA controllers. + config DMA_ENGINE bool @@ -116,7 +130,7 @@ config NET_DMA config ASYNC_TX_DMA bool "Async_tx: Offload support for the async_tx api" - depends on DMA_ENGINE && !HIGHMEM64G + depends on DMA_ENGINE help This allows the async_tx api to take advantage of offload engines for memcpy, memset, xor, and raid6 p+q operations. If your platform has diff --git a/drivers/dma/Makefile b/drivers/dma/Makefile index 40e1e0083571..eca71ba78ae9 100644 --- a/drivers/dma/Makefile +++ b/drivers/dma/Makefile @@ -1,8 +1,7 @@ obj-$(CONFIG_DMA_ENGINE) += dmaengine.o obj-$(CONFIG_NET_DMA) += iovlock.o obj-$(CONFIG_DMATEST) += dmatest.o -obj-$(CONFIG_INTEL_IOATDMA) += ioatdma.o -ioatdma-objs := ioat.o ioat_dma.o ioat_dca.o +obj-$(CONFIG_INTEL_IOATDMA) += ioat/ obj-$(CONFIG_INTEL_IOP_ADMA) += iop-adma.o obj-$(CONFIG_FSL_DMA) += fsldma.o obj-$(CONFIG_MV_XOR) += mv_xor.o @@ -10,3 +9,4 @@ obj-$(CONFIG_DW_DMAC) += dw_dmac.o obj-$(CONFIG_AT_HDMAC) += at_hdmac.o obj-$(CONFIG_MX3_IPU) += ipu/ obj-$(CONFIG_TXX9_DMAC) += txx9dmac.o +obj-$(CONFIG_SH_DMAE) += shdma.o diff --git a/drivers/dma/at_hdmac.c b/drivers/dma/at_hdmac.c index c8522e6f1ad2..7585c4164bd5 100644 --- a/drivers/dma/at_hdmac.c +++ b/drivers/dma/at_hdmac.c @@ -87,6 +87,7 @@ static struct at_desc *atc_alloc_descriptor(struct dma_chan *chan, desc = dma_pool_alloc(atdma->dma_desc_pool, gfp_flags, &phys); if (desc) { memset(desc, 0, sizeof(struct at_desc)); + INIT_LIST_HEAD(&desc->tx_list); dma_async_tx_descriptor_init(&desc->txd, chan); /* txd.flags will be overwritten in prep functions */ desc->txd.flags = DMA_CTRL_ACK; @@ -150,11 +151,11 @@ static void atc_desc_put(struct at_dma_chan *atchan, struct at_desc *desc) struct at_desc *child; spin_lock_bh(&atchan->lock); - list_for_each_entry(child, &desc->txd.tx_list, desc_node) + list_for_each_entry(child, &desc->tx_list, desc_node) dev_vdbg(chan2dev(&atchan->chan_common), "moving child desc %p to freelist\n", child); - list_splice_init(&desc->txd.tx_list, &atchan->free_list); + list_splice_init(&desc->tx_list, &atchan->free_list); dev_vdbg(chan2dev(&atchan->chan_common), "moving desc %p to freelist\n", desc); list_add(&desc->desc_node, &atchan->free_list); @@ -247,30 +248,33 @@ atc_chain_complete(struct at_dma_chan *atchan, struct at_desc *desc) param = txd->callback_param; /* move children to free_list */ - list_splice_init(&txd->tx_list, &atchan->free_list); + list_splice_init(&desc->tx_list, &atchan->free_list); /* move myself to free_list */ list_move(&desc->desc_node, &atchan->free_list); /* unmap dma addresses */ - if (!(txd->flags & DMA_COMPL_SKIP_DEST_UNMAP)) { - if (txd->flags & DMA_COMPL_DEST_UNMAP_SINGLE) - dma_unmap_single(chan2parent(&atchan->chan_common), - desc->lli.daddr, - desc->len, DMA_FROM_DEVICE); - else - dma_unmap_page(chan2parent(&atchan->chan_common), - desc->lli.daddr, - desc->len, DMA_FROM_DEVICE); - } - if (!(txd->flags & DMA_COMPL_SKIP_SRC_UNMAP)) { - if (txd->flags & DMA_COMPL_SRC_UNMAP_SINGLE) - dma_unmap_single(chan2parent(&atchan->chan_common), - desc->lli.saddr, - desc->len, DMA_TO_DEVICE); - else - dma_unmap_page(chan2parent(&atchan->chan_common), - desc->lli.saddr, - desc->len, DMA_TO_DEVICE); + if (!atchan->chan_common.private) { + struct device *parent = chan2parent(&atchan->chan_common); + if (!(txd->flags & DMA_COMPL_SKIP_DEST_UNMAP)) { + if (txd->flags & DMA_COMPL_DEST_UNMAP_SINGLE) + dma_unmap_single(parent, + desc->lli.daddr, + desc->len, DMA_FROM_DEVICE); + else + dma_unmap_page(parent, + desc->lli.daddr, + desc->len, DMA_FROM_DEVICE); + } + if (!(txd->flags & DMA_COMPL_SKIP_SRC_UNMAP)) { + if (txd->flags & DMA_COMPL_SRC_UNMAP_SINGLE) + dma_unmap_single(parent, + desc->lli.saddr, + desc->len, DMA_TO_DEVICE); + else + dma_unmap_page(parent, + desc->lli.saddr, + desc->len, DMA_TO_DEVICE); + } } /* @@ -334,7 +338,7 @@ static void atc_cleanup_descriptors(struct at_dma_chan *atchan) /* This one is currently in progress */ return; - list_for_each_entry(child, &desc->txd.tx_list, desc_node) + list_for_each_entry(child, &desc->tx_list, desc_node) if (!(child->lli.ctrla & ATC_DONE)) /* Currently in progress */ return; @@ -407,7 +411,7 @@ static void atc_handle_error(struct at_dma_chan *atchan) dev_crit(chan2dev(&atchan->chan_common), " cookie: %d\n", bad_desc->txd.cookie); atc_dump_lli(atchan, &bad_desc->lli); - list_for_each_entry(child, &bad_desc->txd.tx_list, desc_node) + list_for_each_entry(child, &bad_desc->tx_list, desc_node) atc_dump_lli(atchan, &child->lli); /* Pretend the descriptor completed successfully */ @@ -587,7 +591,7 @@ atc_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dest, dma_addr_t src, prev->lli.dscr = desc->txd.phys; /* insert the link descriptor to the LD ring */ list_add_tail(&desc->desc_node, - &first->txd.tx_list); + &first->tx_list); } prev = desc; } @@ -646,8 +650,6 @@ atc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, reg_width = atslave->reg_width; - sg_len = dma_map_sg(chan2parent(chan), sgl, sg_len, direction); - ctrla = ATC_DEFAULT_CTRLA | atslave->ctrla; ctrlb = ATC_DEFAULT_CTRLB | ATC_IEN; @@ -687,7 +689,7 @@ atc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, prev->lli.dscr = desc->txd.phys; /* insert the link descriptor to the LD ring */ list_add_tail(&desc->desc_node, - &first->txd.tx_list); + &first->tx_list); } prev = desc; total_len += len; @@ -729,7 +731,7 @@ atc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, prev->lli.dscr = desc->txd.phys; /* insert the link descriptor to the LD ring */ list_add_tail(&desc->desc_node, - &first->txd.tx_list); + &first->tx_list); } prev = desc; total_len += len; diff --git a/drivers/dma/at_hdmac_regs.h b/drivers/dma/at_hdmac_regs.h index 4c972afc49ec..495457e3dc4b 100644 --- a/drivers/dma/at_hdmac_regs.h +++ b/drivers/dma/at_hdmac_regs.h @@ -165,6 +165,7 @@ struct at_desc { struct at_lli lli; /* THEN values for driver housekeeping */ + struct list_head tx_list; struct dma_async_tx_descriptor txd; struct list_head desc_node; size_t len; diff --git a/drivers/dma/dmaengine.c b/drivers/dma/dmaengine.c index 5a87384ea4ff..8f99354082ce 100644 --- a/drivers/dma/dmaengine.c +++ b/drivers/dma/dmaengine.c @@ -608,6 +608,50 @@ void dmaengine_put(void) } EXPORT_SYMBOL(dmaengine_put); +static bool device_has_all_tx_types(struct dma_device *device) +{ + /* A device that satisfies this test has channels that will never cause + * an async_tx channel switch event as all possible operation types can + * be handled. + */ + #ifdef CONFIG_ASYNC_TX_DMA + if (!dma_has_cap(DMA_INTERRUPT, device->cap_mask)) + return false; + #endif + + #if defined(CONFIG_ASYNC_MEMCPY) || defined(CONFIG_ASYNC_MEMCPY_MODULE) + if (!dma_has_cap(DMA_MEMCPY, device->cap_mask)) + return false; + #endif + + #if defined(CONFIG_ASYNC_MEMSET) || defined(CONFIG_ASYNC_MEMSET_MODULE) + if (!dma_has_cap(DMA_MEMSET, device->cap_mask)) + return false; + #endif + + #if defined(CONFIG_ASYNC_XOR) || defined(CONFIG_ASYNC_XOR_MODULE) + if (!dma_has_cap(DMA_XOR, device->cap_mask)) + return false; + + #ifndef CONFIG_ASYNC_TX_DISABLE_XOR_VAL_DMA + if (!dma_has_cap(DMA_XOR_VAL, device->cap_mask)) + return false; + #endif + #endif + + #if defined(CONFIG_ASYNC_PQ) || defined(CONFIG_ASYNC_PQ_MODULE) + if (!dma_has_cap(DMA_PQ, device->cap_mask)) + return false; + + #ifndef CONFIG_ASYNC_TX_DISABLE_PQ_VAL_DMA + if (!dma_has_cap(DMA_PQ_VAL, device->cap_mask)) + return false; + #endif + #endif + + return true; +} + static int get_dma_id(struct dma_device *device) { int rc; @@ -644,8 +688,12 @@ int dma_async_device_register(struct dma_device *device) !device->device_prep_dma_memcpy); BUG_ON(dma_has_cap(DMA_XOR, device->cap_mask) && !device->device_prep_dma_xor); - BUG_ON(dma_has_cap(DMA_ZERO_SUM, device->cap_mask) && - !device->device_prep_dma_zero_sum); + BUG_ON(dma_has_cap(DMA_XOR_VAL, device->cap_mask) && + !device->device_prep_dma_xor_val); + BUG_ON(dma_has_cap(DMA_PQ, device->cap_mask) && + !device->device_prep_dma_pq); + BUG_ON(dma_has_cap(DMA_PQ_VAL, device->cap_mask) && + !device->device_prep_dma_pq_val); BUG_ON(dma_has_cap(DMA_MEMSET, device->cap_mask) && !device->device_prep_dma_memset); BUG_ON(dma_has_cap(DMA_INTERRUPT, device->cap_mask) && @@ -661,6 +709,12 @@ int dma_async_device_register(struct dma_device *device) BUG_ON(!device->device_issue_pending); BUG_ON(!device->dev); + /* note: this only matters in the + * CONFIG_ASYNC_TX_DISABLE_CHANNEL_SWITCH=y case + */ + if (device_has_all_tx_types(device)) + dma_cap_set(DMA_ASYNC_TX, device->cap_mask); + idr_ref = kmalloc(sizeof(*idr_ref), GFP_KERNEL); if (!idr_ref) return -ENOMEM; @@ -933,55 +987,29 @@ void dma_async_tx_descriptor_init(struct dma_async_tx_descriptor *tx, { tx->chan = chan; spin_lock_init(&tx->lock); - INIT_LIST_HEAD(&tx->tx_list); } EXPORT_SYMBOL(dma_async_tx_descriptor_init); /* dma_wait_for_async_tx - spin wait for a transaction to complete * @tx: in-flight transaction to wait on - * - * This routine assumes that tx was obtained from a call to async_memcpy, - * async_xor, async_memset, etc which ensures that tx is "in-flight" (prepped - * and submitted). Walking the parent chain is only meant to cover for DMA - * drivers that do not implement the DMA_INTERRUPT capability and may race with - * the driver's descriptor cleanup routine. */ enum dma_status dma_wait_for_async_tx(struct dma_async_tx_descriptor *tx) { - enum dma_status status; - struct dma_async_tx_descriptor *iter; - struct dma_async_tx_descriptor *parent; + unsigned long dma_sync_wait_timeout = jiffies + msecs_to_jiffies(5000); if (!tx) return DMA_SUCCESS; - WARN_ONCE(tx->parent, "%s: speculatively walking dependency chain for" - " %s\n", __func__, dma_chan_name(tx->chan)); - - /* poll through the dependency chain, return when tx is complete */ - do { - iter = tx; - - /* find the root of the unsubmitted dependency chain */ - do { - parent = iter->parent; - if (!parent) - break; - else - iter = parent; - } while (parent); - - /* there is a small window for ->parent == NULL and - * ->cookie == -EBUSY - */ - while (iter->cookie == -EBUSY) - cpu_relax(); - - status = dma_sync_wait(iter->chan, iter->cookie); - } while (status == DMA_IN_PROGRESS || (iter != tx)); - - return status; + while (tx->cookie == -EBUSY) { + if (time_after_eq(jiffies, dma_sync_wait_timeout)) { + pr_err("%s timeout waiting for descriptor submission\n", + __func__); + return DMA_ERROR; + } + cpu_relax(); + } + return dma_sync_wait(tx->chan, tx->cookie); } EXPORT_SYMBOL_GPL(dma_wait_for_async_tx); diff --git a/drivers/dma/dmatest.c b/drivers/dma/dmatest.c index d93017fc7872..a32a4cf7b1e0 100644 --- a/drivers/dma/dmatest.c +++ b/drivers/dma/dmatest.c @@ -48,6 +48,11 @@ module_param(xor_sources, uint, S_IRUGO); MODULE_PARM_DESC(xor_sources, "Number of xor source buffers (default: 3)"); +static unsigned int pq_sources = 3; +module_param(pq_sources, uint, S_IRUGO); +MODULE_PARM_DESC(pq_sources, + "Number of p+q source buffers (default: 3)"); + /* * Initialization patterns. All bytes in the source buffer has bit 7 * set, all bytes in the destination buffer has bit 7 cleared. @@ -232,6 +237,7 @@ static int dmatest_func(void *data) dma_cookie_t cookie; enum dma_status status; enum dma_ctrl_flags flags; + u8 pq_coefs[pq_sources]; int ret; int src_cnt; int dst_cnt; @@ -248,6 +254,11 @@ static int dmatest_func(void *data) else if (thread->type == DMA_XOR) { src_cnt = xor_sources | 1; /* force odd to ensure dst = src */ dst_cnt = 1; + } else if (thread->type == DMA_PQ) { + src_cnt = pq_sources | 1; /* force odd to ensure dst = src */ + dst_cnt = 2; + for (i = 0; i < pq_sources; i++) + pq_coefs[i] = 1; } else goto err_srcs; @@ -283,6 +294,7 @@ static int dmatest_func(void *data) dma_addr_t dma_dsts[dst_cnt]; struct completion cmp; unsigned long tmo = msecs_to_jiffies(3000); + u8 align = 0; total_tests++; @@ -290,6 +302,18 @@ static int dmatest_func(void *data) src_off = dmatest_random() % (test_buf_size - len + 1); dst_off = dmatest_random() % (test_buf_size - len + 1); + /* honor alignment restrictions */ + if (thread->type == DMA_MEMCPY) + align = dev->copy_align; + else if (thread->type == DMA_XOR) + align = dev->xor_align; + else if (thread->type == DMA_PQ) + align = dev->pq_align; + + len = (len >> align) << align; + src_off = (src_off >> align) << align; + dst_off = (dst_off >> align) << align; + dmatest_init_srcs(thread->srcs, src_off, len); dmatest_init_dsts(thread->dsts, dst_off, len); @@ -306,6 +330,7 @@ static int dmatest_func(void *data) DMA_BIDIRECTIONAL); } + if (thread->type == DMA_MEMCPY) tx = dev->device_prep_dma_memcpy(chan, dma_dsts[0] + dst_off, @@ -316,6 +341,15 @@ static int dmatest_func(void *data) dma_dsts[0] + dst_off, dma_srcs, xor_sources, len, flags); + else if (thread->type == DMA_PQ) { + dma_addr_t dma_pq[dst_cnt]; + + for (i = 0; i < dst_cnt; i++) + dma_pq[i] = dma_dsts[i] + dst_off; + tx = dev->device_prep_dma_pq(chan, dma_pq, dma_srcs, + pq_sources, pq_coefs, + len, flags); + } if (!tx) { for (i = 0; i < src_cnt; i++) @@ -459,6 +493,8 @@ static int dmatest_add_threads(struct dmatest_chan *dtc, enum dma_transaction_ty op = "copy"; else if (type == DMA_XOR) op = "xor"; + else if (type == DMA_PQ) + op = "pq"; else return -EINVAL; @@ -514,6 +550,10 @@ static int dmatest_add_channel(struct dma_chan *chan) cnt = dmatest_add_threads(dtc, DMA_XOR); thread_count += cnt > 0 ? cnt : 0; } + if (dma_has_cap(DMA_PQ, dma_dev->cap_mask)) { + cnt = dmatest_add_threads(dtc, DMA_PQ); + thread_count += cnt > 0 ?: 0; + } pr_info("dmatest: Started %u threads using %s\n", thread_count, dma_chan_name(chan)); diff --git a/drivers/dma/dw_dmac.c b/drivers/dma/dw_dmac.c index 933c143b6a74..2eea823516a7 100644 --- a/drivers/dma/dw_dmac.c +++ b/drivers/dma/dw_dmac.c @@ -116,7 +116,7 @@ static void dwc_sync_desc_for_cpu(struct dw_dma_chan *dwc, struct dw_desc *desc) { struct dw_desc *child; - list_for_each_entry(child, &desc->txd.tx_list, desc_node) + list_for_each_entry(child, &desc->tx_list, desc_node) dma_sync_single_for_cpu(chan2parent(&dwc->chan), child->txd.phys, sizeof(child->lli), DMA_TO_DEVICE); @@ -137,11 +137,11 @@ static void dwc_desc_put(struct dw_dma_chan *dwc, struct dw_desc *desc) dwc_sync_desc_for_cpu(dwc, desc); spin_lock_bh(&dwc->lock); - list_for_each_entry(child, &desc->txd.tx_list, desc_node) + list_for_each_entry(child, &desc->tx_list, desc_node) dev_vdbg(chan2dev(&dwc->chan), "moving child desc %p to freelist\n", child); - list_splice_init(&desc->txd.tx_list, &dwc->free_list); + list_splice_init(&desc->tx_list, &dwc->free_list); dev_vdbg(chan2dev(&dwc->chan), "moving desc %p to freelist\n", desc); list_add(&desc->desc_node, &dwc->free_list); spin_unlock_bh(&dwc->lock); @@ -209,19 +209,28 @@ dwc_descriptor_complete(struct dw_dma_chan *dwc, struct dw_desc *desc) param = txd->callback_param; dwc_sync_desc_for_cpu(dwc, desc); - list_splice_init(&txd->tx_list, &dwc->free_list); + list_splice_init(&desc->tx_list, &dwc->free_list); list_move(&desc->desc_node, &dwc->free_list); - /* - * We use dma_unmap_page() regardless of how the buffers were - * mapped before they were submitted... - */ - if (!(txd->flags & DMA_COMPL_SKIP_DEST_UNMAP)) - dma_unmap_page(chan2parent(&dwc->chan), desc->lli.dar, - desc->len, DMA_FROM_DEVICE); - if (!(txd->flags & DMA_COMPL_SKIP_SRC_UNMAP)) - dma_unmap_page(chan2parent(&dwc->chan), desc->lli.sar, - desc->len, DMA_TO_DEVICE); + if (!dwc->chan.private) { + struct device *parent = chan2parent(&dwc->chan); + if (!(txd->flags & DMA_COMPL_SKIP_DEST_UNMAP)) { + if (txd->flags & DMA_COMPL_DEST_UNMAP_SINGLE) + dma_unmap_single(parent, desc->lli.dar, + desc->len, DMA_FROM_DEVICE); + else + dma_unmap_page(parent, desc->lli.dar, + desc->len, DMA_FROM_DEVICE); + } + if (!(txd->flags & DMA_COMPL_SKIP_SRC_UNMAP)) { + if (txd->flags & DMA_COMPL_SRC_UNMAP_SINGLE) + dma_unmap_single(parent, desc->lli.sar, + desc->len, DMA_TO_DEVICE); + else + dma_unmap_page(parent, desc->lli.sar, + desc->len, DMA_TO_DEVICE); + } + } /* * The API requires that no submissions are done from a @@ -289,7 +298,7 @@ static void dwc_scan_descriptors(struct dw_dma *dw, struct dw_dma_chan *dwc) /* This one is currently in progress */ return; - list_for_each_entry(child, &desc->txd.tx_list, desc_node) + list_for_each_entry(child, &desc->tx_list, desc_node) if (child->lli.llp == llp) /* Currently in progress */ return; @@ -356,7 +365,7 @@ static void dwc_handle_error(struct dw_dma *dw, struct dw_dma_chan *dwc) dev_printk(KERN_CRIT, chan2dev(&dwc->chan), " cookie: %d\n", bad_desc->txd.cookie); dwc_dump_lli(dwc, &bad_desc->lli); - list_for_each_entry(child, &bad_desc->txd.tx_list, desc_node) + list_for_each_entry(child, &bad_desc->tx_list, desc_node) dwc_dump_lli(dwc, &child->lli); /* Pretend the descriptor completed successfully */ @@ -608,7 +617,7 @@ dwc_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dest, dma_addr_t src, prev->txd.phys, sizeof(prev->lli), DMA_TO_DEVICE); list_add_tail(&desc->desc_node, - &first->txd.tx_list); + &first->tx_list); } prev = desc; } @@ -658,8 +667,6 @@ dwc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, reg_width = dws->reg_width; prev = first = NULL; - sg_len = dma_map_sg(chan2parent(chan), sgl, sg_len, direction); - switch (direction) { case DMA_TO_DEVICE: ctllo = (DWC_DEFAULT_CTLLO @@ -700,7 +707,7 @@ dwc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, sizeof(prev->lli), DMA_TO_DEVICE); list_add_tail(&desc->desc_node, - &first->txd.tx_list); + &first->tx_list); } prev = desc; total_len += len; @@ -746,7 +753,7 @@ dwc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, sizeof(prev->lli), DMA_TO_DEVICE); list_add_tail(&desc->desc_node, - &first->txd.tx_list); + &first->tx_list); } prev = desc; total_len += len; @@ -902,6 +909,7 @@ static int dwc_alloc_chan_resources(struct dma_chan *chan) break; } + INIT_LIST_HEAD(&desc->tx_list); dma_async_tx_descriptor_init(&desc->txd, chan); desc->txd.tx_submit = dwc_tx_submit; desc->txd.flags = DMA_CTRL_ACK; diff --git a/drivers/dma/dw_dmac_regs.h b/drivers/dma/dw_dmac_regs.h index 13a580767031..d9a939f67f46 100644 --- a/drivers/dma/dw_dmac_regs.h +++ b/drivers/dma/dw_dmac_regs.h @@ -217,6 +217,7 @@ struct dw_desc { /* THEN values for driver housekeeping */ struct list_head desc_node; + struct list_head tx_list; struct dma_async_tx_descriptor txd; size_t len; }; diff --git a/drivers/dma/fsldma.c b/drivers/dma/fsldma.c index ef87a8984145..296f9e747fac 100644 --- a/drivers/dma/fsldma.c +++ b/drivers/dma/fsldma.c @@ -34,6 +34,7 @@ #include <linux/dmapool.h> #include <linux/of_platform.h> +#include <asm/fsldma.h> #include "fsldma.h" static void dma_init(struct fsl_dma_chan *fsl_chan) @@ -280,28 +281,40 @@ static void fsl_chan_set_dest_loop_size(struct fsl_dma_chan *fsl_chan, int size) } /** - * fsl_chan_toggle_ext_pause - Toggle channel external pause status + * fsl_chan_set_request_count - Set DMA Request Count for external control * @fsl_chan : Freescale DMA channel - * @size : Pause control size, 0 for disable external pause control. - * The maximum is 1024. + * @size : Number of bytes to transfer in a single request + * + * The Freescale DMA channel can be controlled by the external signal DREQ#. + * The DMA request count is how many bytes are allowed to transfer before + * pausing the channel, after which a new assertion of DREQ# resumes channel + * operation. * - * The Freescale DMA channel can be controlled by the external - * signal DREQ#. The pause control size is how many bytes are allowed - * to transfer before pausing the channel, after which a new assertion - * of DREQ# resumes channel operation. + * A size of 0 disables external pause control. The maximum size is 1024. */ -static void fsl_chan_toggle_ext_pause(struct fsl_dma_chan *fsl_chan, int size) +static void fsl_chan_set_request_count(struct fsl_dma_chan *fsl_chan, int size) { - if (size > 1024) - return; + BUG_ON(size > 1024); + DMA_OUT(fsl_chan, &fsl_chan->reg_base->mr, + DMA_IN(fsl_chan, &fsl_chan->reg_base->mr, 32) + | ((__ilog2(size) << 24) & 0x0f000000), + 32); +} - if (size) { - DMA_OUT(fsl_chan, &fsl_chan->reg_base->mr, - DMA_IN(fsl_chan, &fsl_chan->reg_base->mr, 32) - | ((__ilog2(size) << 24) & 0x0f000000), - 32); +/** + * fsl_chan_toggle_ext_pause - Toggle channel external pause status + * @fsl_chan : Freescale DMA channel + * @enable : 0 is disabled, 1 is enabled. + * + * The Freescale DMA channel can be controlled by the external signal DREQ#. + * The DMA Request Count feature should be used in addition to this feature + * to set the number of bytes to transfer before pausing the channel. + */ +static void fsl_chan_toggle_ext_pause(struct fsl_dma_chan *fsl_chan, int enable) +{ + if (enable) fsl_chan->feature |= FSL_DMA_CHAN_PAUSE_EXT; - } else + else fsl_chan->feature &= ~FSL_DMA_CHAN_PAUSE_EXT; } @@ -326,7 +339,8 @@ static void fsl_chan_toggle_ext_start(struct fsl_dma_chan *fsl_chan, int enable) static dma_cookie_t fsl_dma_tx_submit(struct dma_async_tx_descriptor *tx) { struct fsl_dma_chan *fsl_chan = to_fsl_chan(tx->chan); - struct fsl_desc_sw *desc; + struct fsl_desc_sw *desc = tx_to_fsl_desc(tx); + struct fsl_desc_sw *child; unsigned long flags; dma_cookie_t cookie; @@ -334,7 +348,7 @@ static dma_cookie_t fsl_dma_tx_submit(struct dma_async_tx_descriptor *tx) spin_lock_irqsave(&fsl_chan->desc_lock, flags); cookie = fsl_chan->common.cookie; - list_for_each_entry(desc, &tx->tx_list, node) { + list_for_each_entry(child, &desc->tx_list, node) { cookie++; if (cookie < 0) cookie = 1; @@ -343,8 +357,8 @@ static dma_cookie_t fsl_dma_tx_submit(struct dma_async_tx_descriptor *tx) } fsl_chan->common.cookie = cookie; - append_ld_queue(fsl_chan, tx_to_fsl_desc(tx)); - list_splice_init(&tx->tx_list, fsl_chan->ld_queue.prev); + append_ld_queue(fsl_chan, desc); + list_splice_init(&desc->tx_list, fsl_chan->ld_queue.prev); spin_unlock_irqrestore(&fsl_chan->desc_lock, flags); @@ -366,6 +380,7 @@ static struct fsl_desc_sw *fsl_dma_alloc_descriptor( desc_sw = dma_pool_alloc(fsl_chan->desc_pool, GFP_ATOMIC, &pdesc); if (desc_sw) { memset(desc_sw, 0, sizeof(struct fsl_desc_sw)); + INIT_LIST_HEAD(&desc_sw->tx_list); dma_async_tx_descriptor_init(&desc_sw->async_tx, &fsl_chan->common); desc_sw->async_tx.tx_submit = fsl_dma_tx_submit; @@ -455,7 +470,7 @@ fsl_dma_prep_interrupt(struct dma_chan *chan, unsigned long flags) new->async_tx.flags = flags; /* Insert the link descriptor to the LD ring */ - list_add_tail(&new->node, &new->async_tx.tx_list); + list_add_tail(&new->node, &new->tx_list); /* Set End-of-link to the last link descriptor of new list*/ set_ld_eol(fsl_chan, new); @@ -513,7 +528,7 @@ static struct dma_async_tx_descriptor *fsl_dma_prep_memcpy( dma_dest += copy; /* Insert the link descriptor to the LD ring */ - list_add_tail(&new->node, &first->async_tx.tx_list); + list_add_tail(&new->node, &first->tx_list); } while (len); new->async_tx.flags = flags; /* client is in control of this ack */ @@ -528,7 +543,7 @@ fail: if (!first) return NULL; - list = &first->async_tx.tx_list; + list = &first->tx_list; list_for_each_entry_safe_reverse(new, prev, list, node) { list_del(&new->node); dma_pool_free(fsl_chan->desc_pool, new, new->async_tx.phys); @@ -538,6 +553,229 @@ fail: } /** + * fsl_dma_prep_slave_sg - prepare descriptors for a DMA_SLAVE transaction + * @chan: DMA channel + * @sgl: scatterlist to transfer to/from + * @sg_len: number of entries in @scatterlist + * @direction: DMA direction + * @flags: DMAEngine flags + * + * Prepare a set of descriptors for a DMA_SLAVE transaction. Following the + * DMA_SLAVE API, this gets the device-specific information from the + * chan->private variable. + */ +static struct dma_async_tx_descriptor *fsl_dma_prep_slave_sg( + struct dma_chan *chan, struct scatterlist *sgl, unsigned int sg_len, + enum dma_data_direction direction, unsigned long flags) +{ + struct fsl_dma_chan *fsl_chan; + struct fsl_desc_sw *first = NULL, *prev = NULL, *new = NULL; + struct fsl_dma_slave *slave; + struct list_head *tx_list; + size_t copy; + + int i; + struct scatterlist *sg; + size_t sg_used; + size_t hw_used; + struct fsl_dma_hw_addr *hw; + dma_addr_t dma_dst, dma_src; + + if (!chan) + return NULL; + + if (!chan->private) + return NULL; + + fsl_chan = to_fsl_chan(chan); + slave = chan->private; + + if (list_empty(&slave->addresses)) + return NULL; + + hw = list_first_entry(&slave->addresses, struct fsl_dma_hw_addr, entry); + hw_used = 0; + + /* + * Build the hardware transaction to copy from the scatterlist to + * the hardware, or from the hardware to the scatterlist + * + * If you are copying from the hardware to the scatterlist and it + * takes two hardware entries to fill an entire page, then both + * hardware entries will be coalesced into the same page + * + * If you are copying from the scatterlist to the hardware and a + * single page can fill two hardware entries, then the data will + * be read out of the page into the first hardware entry, and so on + */ + for_each_sg(sgl, sg, sg_len, i) { + sg_used = 0; + + /* Loop until the entire scatterlist entry is used */ + while (sg_used < sg_dma_len(sg)) { + + /* + * If we've used up the current hardware address/length + * pair, we need to load a new one + * + * This is done in a while loop so that descriptors with + * length == 0 will be skipped + */ + while (hw_used >= hw->length) { + + /* + * If the current hardware entry is the last + * entry in the list, we're finished + */ + if (list_is_last(&hw->entry, &slave->addresses)) + goto finished; + + /* Get the next hardware address/length pair */ + hw = list_entry(hw->entry.next, + struct fsl_dma_hw_addr, entry); + hw_used = 0; + } + + /* Allocate the link descriptor from DMA pool */ + new = fsl_dma_alloc_descriptor(fsl_chan); + if (!new) { + dev_err(fsl_chan->dev, "No free memory for " + "link descriptor\n"); + goto fail; + } +#ifdef FSL_DMA_LD_DEBUG + dev_dbg(fsl_chan->dev, "new link desc alloc %p\n", new); +#endif + + /* + * Calculate the maximum number of bytes to transfer, + * making sure it is less than the DMA controller limit + */ + copy = min_t(size_t, sg_dma_len(sg) - sg_used, + hw->length - hw_used); + copy = min_t(size_t, copy, FSL_DMA_BCR_MAX_CNT); + + /* + * DMA_FROM_DEVICE + * from the hardware to the scatterlist + * + * DMA_TO_DEVICE + * from the scatterlist to the hardware + */ + if (direction == DMA_FROM_DEVICE) { + dma_src = hw->address + hw_used; + dma_dst = sg_dma_address(sg) + sg_used; + } else { + dma_src = sg_dma_address(sg) + sg_used; + dma_dst = hw->address + hw_used; + } + + /* Fill in the descriptor */ + set_desc_cnt(fsl_chan, &new->hw, copy); + set_desc_src(fsl_chan, &new->hw, dma_src); + set_desc_dest(fsl_chan, &new->hw, dma_dst); + + /* + * If this is not the first descriptor, chain the + * current descriptor after the previous descriptor + */ + if (!first) { + first = new; + } else { + set_desc_next(fsl_chan, &prev->hw, + new->async_tx.phys); + } + + new->async_tx.cookie = 0; + async_tx_ack(&new->async_tx); + + prev = new; + sg_used += copy; + hw_used += copy; + + /* Insert the link descriptor into the LD ring */ + list_add_tail(&new->node, &first->tx_list); + } + } + +finished: + + /* All of the hardware address/length pairs had length == 0 */ + if (!first || !new) + return NULL; + + new->async_tx.flags = flags; + new->async_tx.cookie = -EBUSY; + + /* Set End-of-link to the last link descriptor of new list */ + set_ld_eol(fsl_chan, new); + + /* Enable extra controller features */ + if (fsl_chan->set_src_loop_size) + fsl_chan->set_src_loop_size(fsl_chan, slave->src_loop_size); + + if (fsl_chan->set_dest_loop_size) + fsl_chan->set_dest_loop_size(fsl_chan, slave->dst_loop_size); + + if (fsl_chan->toggle_ext_start) + fsl_chan->toggle_ext_start(fsl_chan, slave->external_start); + + if (fsl_chan->toggle_ext_pause) + fsl_chan->toggle_ext_pause(fsl_chan, slave->external_pause); + + if (fsl_chan->set_request_count) + fsl_chan->set_request_count(fsl_chan, slave->request_count); + + return &first->async_tx; + +fail: + /* If first was not set, then we failed to allocate the very first + * descriptor, and we're done */ + if (!first) + return NULL; + + /* + * First is set, so all of the descriptors we allocated have been added + * to first->tx_list, INCLUDING "first" itself. Therefore we + * must traverse the list backwards freeing each descriptor in turn + * + * We're re-using variables for the loop, oh well + */ + tx_list = &first->tx_list; + list_for_each_entry_safe_reverse(new, prev, tx_list, node) { + list_del_init(&new->node); + dma_pool_free(fsl_chan->desc_pool, new, new->async_tx.phys); + } + + return NULL; +} + +static void fsl_dma_device_terminate_all(struct dma_chan *chan) +{ + struct fsl_dma_chan *fsl_chan; + struct fsl_desc_sw *desc, *tmp; + unsigned long flags; + + if (!chan) + return; + + fsl_chan = to_fsl_chan(chan); + + /* Halt the DMA engine */ + dma_halt(fsl_chan); + + spin_lock_irqsave(&fsl_chan->desc_lock, flags); + + /* Remove and free all of the descriptors in the LD queue */ + list_for_each_entry_safe(desc, tmp, &fsl_chan->ld_queue, node) { + list_del(&desc->node); + dma_pool_free(fsl_chan->desc_pool, desc, desc->async_tx.phys); + } + + spin_unlock_irqrestore(&fsl_chan->desc_lock, flags); +} + +/** * fsl_dma_update_completed_cookie - Update the completed cookie. * @fsl_chan : Freescale DMA channel */ @@ -883,6 +1121,7 @@ static int __devinit fsl_dma_chan_probe(struct fsl_dma_device *fdev, new_fsl_chan->toggle_ext_start = fsl_chan_toggle_ext_start; new_fsl_chan->set_src_loop_size = fsl_chan_set_src_loop_size; new_fsl_chan->set_dest_loop_size = fsl_chan_set_dest_loop_size; + new_fsl_chan->set_request_count = fsl_chan_set_request_count; } spin_lock_init(&new_fsl_chan->desc_lock); @@ -962,12 +1201,15 @@ static int __devinit of_fsl_dma_probe(struct of_device *dev, dma_cap_set(DMA_MEMCPY, fdev->common.cap_mask); dma_cap_set(DMA_INTERRUPT, fdev->common.cap_mask); + dma_cap_set(DMA_SLAVE, fdev->common.cap_mask); fdev->common.device_alloc_chan_resources = fsl_dma_alloc_chan_resources; fdev->common.device_free_chan_resources = fsl_dma_free_chan_resources; fdev->common.device_prep_dma_interrupt = fsl_dma_prep_interrupt; fdev->common.device_prep_dma_memcpy = fsl_dma_prep_memcpy; fdev->common.device_is_tx_complete = fsl_dma_is_complete; fdev->common.device_issue_pending = fsl_dma_memcpy_issue_pending; + fdev->common.device_prep_slave_sg = fsl_dma_prep_slave_sg; + fdev->common.device_terminate_all = fsl_dma_device_terminate_all; fdev->common.dev = &dev->dev; fdev->irq = irq_of_parse_and_map(dev->node, 0); diff --git a/drivers/dma/fsldma.h b/drivers/dma/fsldma.h index dc7f26865797..0df14cbb8ca3 100644 --- a/drivers/dma/fsldma.h +++ b/drivers/dma/fsldma.h @@ -90,6 +90,7 @@ struct fsl_dma_ld_hw { struct fsl_desc_sw { struct fsl_dma_ld_hw hw; struct list_head node; + struct list_head tx_list; struct dma_async_tx_descriptor async_tx; struct list_head *ld; void *priv; @@ -143,10 +144,11 @@ struct fsl_dma_chan { struct tasklet_struct tasklet; u32 feature; - void (*toggle_ext_pause)(struct fsl_dma_chan *fsl_chan, int size); + void (*toggle_ext_pause)(struct fsl_dma_chan *fsl_chan, int enable); void (*toggle_ext_start)(struct fsl_dma_chan *fsl_chan, int enable); void (*set_src_loop_size)(struct fsl_dma_chan *fsl_chan, int size); void (*set_dest_loop_size)(struct fsl_dma_chan *fsl_chan, int size); + void (*set_request_count)(struct fsl_dma_chan *fsl_chan, int size); }; #define to_fsl_chan(chan) container_of(chan, struct fsl_dma_chan, common) diff --git a/drivers/dma/ioat.c b/drivers/dma/ioat.c deleted file mode 100644 index 2225bb6ba3d1..000000000000 --- a/drivers/dma/ioat.c +++ /dev/null @@ -1,202 +0,0 @@ -/* - * Intel I/OAT DMA Linux driver - * Copyright(c) 2007 - 2009 Intel Corporation. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. - * - * The full GNU General Public License is included in this distribution in - * the file called "COPYING". - * - */ - -/* - * This driver supports an Intel I/OAT DMA engine, which does asynchronous - * copy operations. - */ - -#include <linux/init.h> -#include <linux/module.h> -#include <linux/pci.h> -#include <linux/interrupt.h> -#include <linux/dca.h> -#include "ioatdma.h" -#include "ioatdma_registers.h" -#include "ioatdma_hw.h" - -MODULE_VERSION(IOAT_DMA_VERSION); -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Intel Corporation"); - -static struct pci_device_id ioat_pci_tbl[] = { - /* I/OAT v1 platforms */ - { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_IOAT) }, - { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_IOAT_CNB) }, - { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_IOAT_SCNB) }, - { PCI_DEVICE(PCI_VENDOR_ID_UNISYS, PCI_DEVICE_ID_UNISYS_DMA_DIRECTOR) }, - - /* I/OAT v2 platforms */ - { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_IOAT_SNB) }, - - /* I/OAT v3 platforms */ - { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_IOAT_TBG0) }, - { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_IOAT_TBG1) }, - { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_IOAT_TBG2) }, - { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_IOAT_TBG3) }, - { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_IOAT_TBG4) }, - { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_IOAT_TBG5) }, - { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_IOAT_TBG6) }, - { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_IOAT_TBG7) }, - { 0, } -}; - -struct ioat_device { - struct pci_dev *pdev; - void __iomem *iobase; - struct ioatdma_device *dma; - struct dca_provider *dca; -}; - -static int __devinit ioat_probe(struct pci_dev *pdev, - const struct pci_device_id *id); -static void __devexit ioat_remove(struct pci_dev *pdev); - -static int ioat_dca_enabled = 1; -module_param(ioat_dca_enabled, int, 0644); -MODULE_PARM_DESC(ioat_dca_enabled, "control support of dca service (default: 1)"); - -static struct pci_driver ioat_pci_driver = { - .name = "ioatdma", - .id_table = ioat_pci_tbl, - .probe = ioat_probe, - .remove = __devexit_p(ioat_remove), -}; - -static int __devinit ioat_probe(struct pci_dev *pdev, - const struct pci_device_id *id) -{ - void __iomem *iobase; - struct ioat_device *device; - unsigned long mmio_start, mmio_len; - int err; - - err = pci_enable_device(pdev); - if (err) - goto err_enable_device; - - err = pci_request_regions(pdev, ioat_pci_driver.name); - if (err) - goto err_request_regions; - - err = pci_set_dma_mask(pdev, DMA_BIT_MASK(64)); - if (err) - err = pci_set_dma_mask(pdev, DMA_BIT_MASK(32)); - if (err) - goto err_set_dma_mask; - - err = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(64)); - if (err) - err = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32)); - if (err) - goto err_set_dma_mask; - - mmio_start = pci_resource_start(pdev, 0); - mmio_len = pci_resource_len(pdev, 0); - iobase = ioremap(mmio_start, mmio_len); - if (!iobase) { - err = -ENOMEM; - goto err_ioremap; - } - - device = kzalloc(sizeof(*device), GFP_KERNEL); - if (!device) { - err = -ENOMEM; - goto err_kzalloc; - } - device->pdev = pdev; - pci_set_drvdata(pdev, device); - device->iobase = iobase; - - pci_set_master(pdev); - - switch (readb(iobase + IOAT_VER_OFFSET)) { - case IOAT_VER_1_2: - device->dma = ioat_dma_probe(pdev, iobase); - if (device->dma && ioat_dca_enabled) - device->dca = ioat_dca_init(pdev, iobase); - break; - case IOAT_VER_2_0: - device->dma = ioat_dma_probe(pdev, iobase); - if (device->dma && ioat_dca_enabled) - device->dca = ioat2_dca_init(pdev, iobase); - break; - case IOAT_VER_3_0: - device->dma = ioat_dma_probe(pdev, iobase); - if (device->dma && ioat_dca_enabled) - device->dca = ioat3_dca_init(pdev, iobase); - break; - default: - err = -ENODEV; - break; - } - if (!device->dma) - err = -ENODEV; - - if (err) - goto err_version; - - return 0; - -err_version: - kfree(device); -err_kzalloc: - iounmap(iobase); -err_ioremap: -err_set_dma_mask: - pci_release_regions(pdev); - pci_disable_device(pdev); -err_request_regions: -err_enable_device: - return err; -} - -static void __devexit ioat_remove(struct pci_dev *pdev) -{ - struct ioat_device *device = pci_get_drvdata(pdev); - - dev_err(&pdev->dev, "Removing dma and dca services\n"); - if (device->dca) { - unregister_dca_provider(device->dca); - free_dca_provider(device->dca); - device->dca = NULL; - } - - if (device->dma) { - ioat_dma_remove(device->dma); - device->dma = NULL; - } - - kfree(device); -} - -static int __init ioat_init_module(void) -{ - return pci_register_driver(&ioat_pci_driver); -} -module_init(ioat_init_module); - -static void __exit ioat_exit_module(void) -{ - pci_unregister_driver(&ioat_pci_driver); -} -module_exit(ioat_exit_module); diff --git a/drivers/dma/ioat/Makefile b/drivers/dma/ioat/Makefile new file mode 100644 index 000000000000..8997d3fb9051 --- /dev/null +++ b/drivers/dma/ioat/Makefile @@ -0,0 +1,2 @@ +obj-$(CONFIG_INTEL_IOATDMA) += ioatdma.o +ioatdma-objs := pci.o dma.o dma_v2.o dma_v3.o dca.o diff --git a/drivers/dma/ioat_dca.c b/drivers/dma/ioat/dca.c index c012a1e15043..abd9038e06b1 100644 --- a/drivers/dma/ioat_dca.c +++ b/drivers/dma/ioat/dca.c @@ -33,8 +33,8 @@ #define cpu_physical_id(cpu) (cpuid_ebx(1) >> 24) #endif -#include "ioatdma.h" -#include "ioatdma_registers.h" +#include "dma.h" +#include "registers.h" /* * Bit 7 of a tag map entry is the "valid" bit, if it is set then bits 0:6 @@ -98,17 +98,17 @@ static int dca_enabled_in_bios(struct pci_dev *pdev) cpuid_level_9 = cpuid_eax(9); res = test_bit(0, &cpuid_level_9); if (!res) - dev_err(&pdev->dev, "DCA is disabled in BIOS\n"); + dev_dbg(&pdev->dev, "DCA is disabled in BIOS\n"); return res; } -static int system_has_dca_enabled(struct pci_dev *pdev) +int system_has_dca_enabled(struct pci_dev *pdev) { if (boot_cpu_has(X86_FEATURE_DCA)) return dca_enabled_in_bios(pdev); - dev_err(&pdev->dev, "boot cpu doesn't have X86_FEATURE_DCA\n"); + dev_dbg(&pdev->dev, "boot cpu doesn't have X86_FEATURE_DCA\n"); return 0; } @@ -242,7 +242,8 @@ static struct dca_ops ioat_dca_ops = { }; -struct dca_provider *ioat_dca_init(struct pci_dev *pdev, void __iomem *iobase) +struct dca_provider * __devinit +ioat_dca_init(struct pci_dev *pdev, void __iomem *iobase) { struct dca_provider *dca; struct ioat_dca_priv *ioatdca; @@ -407,7 +408,8 @@ static int ioat2_dca_count_dca_slots(void __iomem *iobase, u16 dca_offset) return slots; } -struct dca_provider *ioat2_dca_init(struct pci_dev *pdev, void __iomem *iobase) +struct dca_provider * __devinit +ioat2_dca_init(struct pci_dev *pdev, void __iomem *iobase) { struct dca_provider *dca; struct ioat_dca_priv *ioatdca; @@ -602,7 +604,8 @@ static int ioat3_dca_count_dca_slots(void *iobase, u16 dca_offset) return slots; } -struct dca_provider *ioat3_dca_init(struct pci_dev *pdev, void __iomem *iobase) +struct dca_provider * __devinit +ioat3_dca_init(struct pci_dev *pdev, void __iomem *iobase) { struct dca_provider *dca; struct ioat_dca_priv *ioatdca; diff --git a/drivers/dma/ioat/dma.c b/drivers/dma/ioat/dma.c new file mode 100644 index 000000000000..c524d36d3c2e --- /dev/null +++ b/drivers/dma/ioat/dma.c @@ -0,0 +1,1238 @@ +/* + * Intel I/OAT DMA Linux driver + * Copyright(c) 2004 - 2009 Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + * + * The full GNU General Public License is included in this distribution in + * the file called "COPYING". + * + */ + +/* + * This driver supports an Intel I/OAT DMA engine, which does asynchronous + * copy operations. + */ + +#include <linux/init.h> +#include <linux/module.h> +#include <linux/pci.h> +#include <linux/interrupt.h> +#include <linux/dmaengine.h> +#include <linux/delay.h> +#include <linux/dma-mapping.h> +#include <linux/workqueue.h> +#include <linux/i7300_idle.h> +#include "dma.h" +#include "registers.h" +#include "hw.h" + +int ioat_pending_level = 4; +module_param(ioat_pending_level, int, 0644); +MODULE_PARM_DESC(ioat_pending_level, + "high-water mark for pushing ioat descriptors (default: 4)"); + +/* internal functions */ +static void ioat1_cleanup(struct ioat_dma_chan *ioat); +static void ioat1_dma_start_null_desc(struct ioat_dma_chan *ioat); + +/** + * ioat_dma_do_interrupt - handler used for single vector interrupt mode + * @irq: interrupt id + * @data: interrupt data + */ +static irqreturn_t ioat_dma_do_interrupt(int irq, void *data) +{ + struct ioatdma_device *instance = data; + struct ioat_chan_common *chan; + unsigned long attnstatus; + int bit; + u8 intrctrl; + + intrctrl = readb(instance->reg_base + IOAT_INTRCTRL_OFFSET); + + if (!(intrctrl & IOAT_INTRCTRL_MASTER_INT_EN)) + return IRQ_NONE; + + if (!(intrctrl & IOAT_INTRCTRL_INT_STATUS)) { + writeb(intrctrl, instance->reg_base + IOAT_INTRCTRL_OFFSET); + return IRQ_NONE; + } + + attnstatus = readl(instance->reg_base + IOAT_ATTNSTATUS_OFFSET); + for_each_bit(bit, &attnstatus, BITS_PER_LONG) { + chan = ioat_chan_by_index(instance, bit); + tasklet_schedule(&chan->cleanup_task); + } + + writeb(intrctrl, instance->reg_base + IOAT_INTRCTRL_OFFSET); + return IRQ_HANDLED; +} + +/** + * ioat_dma_do_interrupt_msix - handler used for vector-per-channel interrupt mode + * @irq: interrupt id + * @data: interrupt data + */ +static irqreturn_t ioat_dma_do_interrupt_msix(int irq, void *data) +{ + struct ioat_chan_common *chan = data; + + tasklet_schedule(&chan->cleanup_task); + + return IRQ_HANDLED; +} + +static void ioat1_cleanup_tasklet(unsigned long data); + +/* common channel initialization */ +void ioat_init_channel(struct ioatdma_device *device, + struct ioat_chan_common *chan, int idx, + void (*timer_fn)(unsigned long), + void (*tasklet)(unsigned long), + unsigned long ioat) +{ + struct dma_device *dma = &device->common; + + chan->device = device; + chan->reg_base = device->reg_base + (0x80 * (idx + 1)); + spin_lock_init(&chan->cleanup_lock); + chan->common.device = dma; + list_add_tail(&chan->common.device_node, &dma->channels); + device->idx[idx] = chan; + init_timer(&chan->timer); + chan->timer.function = timer_fn; + chan->timer.data = ioat; + tasklet_init(&chan->cleanup_task, tasklet, ioat); + tasklet_disable(&chan->cleanup_task); +} + +static void ioat1_timer_event(unsigned long data); + +/** + * ioat1_dma_enumerate_channels - find and initialize the device's channels + * @device: the device to be enumerated + */ +static int ioat1_enumerate_channels(struct ioatdma_device *device) +{ + u8 xfercap_scale; + u32 xfercap; + int i; + struct ioat_dma_chan *ioat; + struct device *dev = &device->pdev->dev; + struct dma_device *dma = &device->common; + + INIT_LIST_HEAD(&dma->channels); + dma->chancnt = readb(device->reg_base + IOAT_CHANCNT_OFFSET); + dma->chancnt &= 0x1f; /* bits [4:0] valid */ + if (dma->chancnt > ARRAY_SIZE(device->idx)) { + dev_warn(dev, "(%d) exceeds max supported channels (%zu)\n", + dma->chancnt, ARRAY_SIZE(device->idx)); + dma->chancnt = ARRAY_SIZE(device->idx); + } + xfercap_scale = readb(device->reg_base + IOAT_XFERCAP_OFFSET); + xfercap_scale &= 0x1f; /* bits [4:0] valid */ + xfercap = (xfercap_scale == 0 ? -1 : (1UL << xfercap_scale)); + dev_dbg(dev, "%s: xfercap = %d\n", __func__, xfercap); + +#ifdef CONFIG_I7300_IDLE_IOAT_CHANNEL + if (i7300_idle_platform_probe(NULL, NULL, 1) == 0) + dma->chancnt--; +#endif + for (i = 0; i < dma->chancnt; i++) { + ioat = devm_kzalloc(dev, sizeof(*ioat), GFP_KERNEL); + if (!ioat) + break; + + ioat_init_channel(device, &ioat->base, i, + ioat1_timer_event, + ioat1_cleanup_tasklet, + (unsigned long) ioat); + ioat->xfercap = xfercap; + spin_lock_init(&ioat->desc_lock); + INIT_LIST_HEAD(&ioat->free_desc); + INIT_LIST_HEAD(&ioat->used_desc); + } + dma->chancnt = i; + return i; +} + +/** + * ioat_dma_memcpy_issue_pending - push potentially unrecognized appended + * descriptors to hw + * @chan: DMA channel handle + */ +static inline void +__ioat1_dma_memcpy_issue_pending(struct ioat_dma_chan *ioat) +{ + void __iomem *reg_base = ioat->base.reg_base; + + dev_dbg(to_dev(&ioat->base), "%s: pending: %d\n", + __func__, ioat->pending); + ioat->pending = 0; + writeb(IOAT_CHANCMD_APPEND, reg_base + IOAT1_CHANCMD_OFFSET); +} + +static void ioat1_dma_memcpy_issue_pending(struct dma_chan *chan) +{ + struct ioat_dma_chan *ioat = to_ioat_chan(chan); + + if (ioat->pending > 0) { + spin_lock_bh(&ioat->desc_lock); + __ioat1_dma_memcpy_issue_pending(ioat); + spin_unlock_bh(&ioat->desc_lock); + } +} + +/** + * ioat1_reset_channel - restart a channel + * @ioat: IOAT DMA channel handle + */ +static void ioat1_reset_channel(struct ioat_dma_chan *ioat) +{ + struct ioat_chan_common *chan = &ioat->base; + void __iomem *reg_base = chan->reg_base; + u32 chansts, chanerr; + + dev_warn(to_dev(chan), "reset\n"); + chanerr = readl(reg_base + IOAT_CHANERR_OFFSET); + chansts = *chan->completion & IOAT_CHANSTS_STATUS; + if (chanerr) { + dev_err(to_dev(chan), + "chan%d, CHANSTS = 0x%08x CHANERR = 0x%04x, clearing\n", + chan_num(chan), chansts, chanerr); + writel(chanerr, reg_base + IOAT_CHANERR_OFFSET); + } + + /* + * whack it upside the head with a reset + * and wait for things to settle out. + * force the pending count to a really big negative + * to make sure no one forces an issue_pending + * while we're waiting. + */ + + ioat->pending = INT_MIN; + writeb(IOAT_CHANCMD_RESET, + reg_base + IOAT_CHANCMD_OFFSET(chan->device->version)); + set_bit(IOAT_RESET_PENDING, &chan->state); + mod_timer(&chan->timer, jiffies + RESET_DELAY); +} + +static dma_cookie_t ioat1_tx_submit(struct dma_async_tx_descriptor *tx) +{ + struct dma_chan *c = tx->chan; + struct ioat_dma_chan *ioat = to_ioat_chan(c); + struct ioat_desc_sw *desc = tx_to_ioat_desc(tx); + struct ioat_chan_common *chan = &ioat->base; + struct ioat_desc_sw *first; + struct ioat_desc_sw *chain_tail; + dma_cookie_t cookie; + + spin_lock_bh(&ioat->desc_lock); + /* cookie incr and addition to used_list must be atomic */ + cookie = c->cookie; + cookie++; + if (cookie < 0) + cookie = 1; + c->cookie = cookie; + tx->cookie = cookie; + dev_dbg(to_dev(&ioat->base), "%s: cookie: %d\n", __func__, cookie); + + /* write address into NextDescriptor field of last desc in chain */ + first = to_ioat_desc(desc->tx_list.next); + chain_tail = to_ioat_desc(ioat->used_desc.prev); + /* make descriptor updates globally visible before chaining */ + wmb(); + chain_tail->hw->next = first->txd.phys; + list_splice_tail_init(&desc->tx_list, &ioat->used_desc); + dump_desc_dbg(ioat, chain_tail); + dump_desc_dbg(ioat, first); + + if (!test_and_set_bit(IOAT_COMPLETION_PENDING, &chan->state)) + mod_timer(&chan->timer, jiffies + COMPLETION_TIMEOUT); + + ioat->active += desc->hw->tx_cnt; + ioat->pending += desc->hw->tx_cnt; + if (ioat->pending >= ioat_pending_level) + __ioat1_dma_memcpy_issue_pending(ioat); + spin_unlock_bh(&ioat->desc_lock); + + return cookie; +} + +/** + * ioat_dma_alloc_descriptor - allocate and return a sw and hw descriptor pair + * @ioat: the channel supplying the memory pool for the descriptors + * @flags: allocation flags + */ +static struct ioat_desc_sw * +ioat_dma_alloc_descriptor(struct ioat_dma_chan *ioat, gfp_t flags) +{ + struct ioat_dma_descriptor *desc; + struct ioat_desc_sw *desc_sw; + struct ioatdma_device *ioatdma_device; + dma_addr_t phys; + + ioatdma_device = ioat->base.device; + desc = pci_pool_alloc(ioatdma_device->dma_pool, flags, &phys); + if (unlikely(!desc)) + return NULL; + + desc_sw = kzalloc(sizeof(*desc_sw), flags); + if (unlikely(!desc_sw)) { + pci_pool_free(ioatdma_device->dma_pool, desc, phys); + return NULL; + } + + memset(desc, 0, sizeof(*desc)); + + INIT_LIST_HEAD(&desc_sw->tx_list); + dma_async_tx_descriptor_init(&desc_sw->txd, &ioat->base.common); + desc_sw->txd.tx_submit = ioat1_tx_submit; + desc_sw->hw = desc; + desc_sw->txd.phys = phys; + set_desc_id(desc_sw, -1); + + return desc_sw; +} + +static int ioat_initial_desc_count = 256; +module_param(ioat_initial_desc_count, int, 0644); +MODULE_PARM_DESC(ioat_initial_desc_count, + "ioat1: initial descriptors per channel (default: 256)"); +/** + * ioat1_dma_alloc_chan_resources - returns the number of allocated descriptors + * @chan: the channel to be filled out + */ +static int ioat1_dma_alloc_chan_resources(struct dma_chan *c) +{ + struct ioat_dma_chan *ioat = to_ioat_chan(c); + struct ioat_chan_common *chan = &ioat->base; + struct ioat_desc_sw *desc; + u32 chanerr; + int i; + LIST_HEAD(tmp_list); + + /* have we already been set up? */ + if (!list_empty(&ioat->free_desc)) + return ioat->desccount; + + /* Setup register to interrupt and write completion status on error */ + writew(IOAT_CHANCTRL_RUN, chan->reg_base + IOAT_CHANCTRL_OFFSET); + + chanerr = readl(chan->reg_base + IOAT_CHANERR_OFFSET); + if (chanerr) { + dev_err(to_dev(chan), "CHANERR = %x, clearing\n", chanerr); + writel(chanerr, chan->reg_base + IOAT_CHANERR_OFFSET); + } + + /* Allocate descriptors */ + for (i = 0; i < ioat_initial_desc_count; i++) { + desc = ioat_dma_alloc_descriptor(ioat, GFP_KERNEL); + if (!desc) { + dev_err(to_dev(chan), "Only %d initial descriptors\n", i); + break; + } + set_desc_id(desc, i); + list_add_tail(&desc->node, &tmp_list); + } + spin_lock_bh(&ioat->desc_lock); + ioat->desccount = i; + list_splice(&tmp_list, &ioat->free_desc); + spin_unlock_bh(&ioat->desc_lock); + + /* allocate a completion writeback area */ + /* doing 2 32bit writes to mmio since 1 64b write doesn't work */ + chan->completion = pci_pool_alloc(chan->device->completion_pool, + GFP_KERNEL, &chan->completion_dma); + memset(chan->completion, 0, sizeof(*chan->completion)); + writel(((u64) chan->completion_dma) & 0x00000000FFFFFFFF, + chan->reg_base + IOAT_CHANCMP_OFFSET_LOW); + writel(((u64) chan->completion_dma) >> 32, + chan->reg_base + IOAT_CHANCMP_OFFSET_HIGH); + + tasklet_enable(&chan->cleanup_task); + ioat1_dma_start_null_desc(ioat); /* give chain to dma device */ + dev_dbg(to_dev(chan), "%s: allocated %d descriptors\n", + __func__, ioat->desccount); + return ioat->desccount; +} + +/** + * ioat1_dma_free_chan_resources - release all the descriptors + * @chan: the channel to be cleaned + */ +static void ioat1_dma_free_chan_resources(struct dma_chan *c) +{ + struct ioat_dma_chan *ioat = to_ioat_chan(c); + struct ioat_chan_common *chan = &ioat->base; + struct ioatdma_device *ioatdma_device = chan->device; + struct ioat_desc_sw *desc, *_desc; + int in_use_descs = 0; + + /* Before freeing channel resources first check + * if they have been previously allocated for this channel. + */ + if (ioat->desccount == 0) + return; + + tasklet_disable(&chan->cleanup_task); + del_timer_sync(&chan->timer); + ioat1_cleanup(ioat); + + /* Delay 100ms after reset to allow internal DMA logic to quiesce + * before removing DMA descriptor resources. + */ + writeb(IOAT_CHANCMD_RESET, + chan->reg_base + IOAT_CHANCMD_OFFSET(chan->device->version)); + mdelay(100); + + spin_lock_bh(&ioat->desc_lock); + list_for_each_entry_safe(desc, _desc, &ioat->used_desc, node) { + dev_dbg(to_dev(chan), "%s: freeing %d from used list\n", + __func__, desc_id(desc)); + dump_desc_dbg(ioat, desc); + in_use_descs++; + list_del(&desc->node); + pci_pool_free(ioatdma_device->dma_pool, desc->hw, + desc->txd.phys); + kfree(desc); + } + list_for_each_entry_safe(desc, _desc, + &ioat->free_desc, node) { + list_del(&desc->node); + pci_pool_free(ioatdma_device->dma_pool, desc->hw, + desc->txd.phys); + kfree(desc); + } + spin_unlock_bh(&ioat->desc_lock); + + pci_pool_free(ioatdma_device->completion_pool, + chan->completion, + chan->completion_dma); + + /* one is ok since we left it on there on purpose */ + if (in_use_descs > 1) + dev_err(to_dev(chan), "Freeing %d in use descriptors!\n", + in_use_descs - 1); + + chan->last_completion = 0; + chan->completion_dma = 0; + ioat->pending = 0; + ioat->desccount = 0; +} + +/** + * ioat1_dma_get_next_descriptor - return the next available descriptor + * @ioat: IOAT DMA channel handle + * + * Gets the next descriptor from the chain, and must be called with the + * channel's desc_lock held. Allocates more descriptors if the channel + * has run out. + */ +static struct ioat_desc_sw * +ioat1_dma_get_next_descriptor(struct ioat_dma_chan *ioat) +{ + struct ioat_desc_sw *new; + + if (!list_empty(&ioat->free_desc)) { + new = to_ioat_desc(ioat->free_desc.next); + list_del(&new->node); + } else { + /* try to get another desc */ + new = ioat_dma_alloc_descriptor(ioat, GFP_ATOMIC); + if (!new) { + dev_err(to_dev(&ioat->base), "alloc failed\n"); + return NULL; + } + } + dev_dbg(to_dev(&ioat->base), "%s: allocated: %d\n", + __func__, desc_id(new)); + prefetch(new->hw); + return new; +} + +static struct dma_async_tx_descriptor * +ioat1_dma_prep_memcpy(struct dma_chan *c, dma_addr_t dma_dest, + dma_addr_t dma_src, size_t len, unsigned long flags) +{ + struct ioat_dma_chan *ioat = to_ioat_chan(c); + struct ioat_desc_sw *desc; + size_t copy; + LIST_HEAD(chain); + dma_addr_t src = dma_src; + dma_addr_t dest = dma_dest; + size_t total_len = len; + struct ioat_dma_descriptor *hw = NULL; + int tx_cnt = 0; + + spin_lock_bh(&ioat->desc_lock); + desc = ioat1_dma_get_next_descriptor(ioat); + do { + if (!desc) + break; + + tx_cnt++; + copy = min_t(size_t, len, ioat->xfercap); + + hw = desc->hw; + hw->size = copy; + hw->ctl = 0; + hw->src_addr = src; + hw->dst_addr = dest; + + list_add_tail(&desc->node, &chain); + + len -= copy; + dest += copy; + src += copy; + if (len) { + struct ioat_desc_sw *next; + + async_tx_ack(&desc->txd); + next = ioat1_dma_get_next_descriptor(ioat); + hw->next = next ? next->txd.phys : 0; + dump_desc_dbg(ioat, desc); + desc = next; + } else + hw->next = 0; + } while (len); + + if (!desc) { + struct ioat_chan_common *chan = &ioat->base; + + dev_err(to_dev(chan), + "chan%d - get_next_desc failed\n", chan_num(chan)); + list_splice(&chain, &ioat->free_desc); + spin_unlock_bh(&ioat->desc_lock); + return NULL; + } + spin_unlock_bh(&ioat->desc_lock); + + desc->txd.flags = flags; + desc->len = total_len; + list_splice(&chain, &desc->tx_list); + hw->ctl_f.int_en = !!(flags & DMA_PREP_INTERRUPT); + hw->ctl_f.compl_write = 1; + hw->tx_cnt = tx_cnt; + dump_desc_dbg(ioat, desc); + + return &desc->txd; +} + +static void ioat1_cleanup_tasklet(unsigned long data) +{ + struct ioat_dma_chan *chan = (void *)data; + + ioat1_cleanup(chan); + writew(IOAT_CHANCTRL_RUN, chan->base.reg_base + IOAT_CHANCTRL_OFFSET); +} + +void ioat_dma_unmap(struct ioat_chan_common *chan, enum dma_ctrl_flags flags, + size_t len, struct ioat_dma_descriptor *hw) +{ + struct pci_dev *pdev = chan->device->pdev; + size_t offset = len - hw->size; + + if (!(flags & DMA_COMPL_SKIP_DEST_UNMAP)) + ioat_unmap(pdev, hw->dst_addr - offset, len, + PCI_DMA_FROMDEVICE, flags, 1); + + if (!(flags & DMA_COMPL_SKIP_SRC_UNMAP)) + ioat_unmap(pdev, hw->src_addr - offset, len, + PCI_DMA_TODEVICE, flags, 0); +} + +unsigned long ioat_get_current_completion(struct ioat_chan_common *chan) +{ + unsigned long phys_complete; + u64 completion; + + completion = *chan->completion; + phys_complete = ioat_chansts_to_addr(completion); + + dev_dbg(to_dev(chan), "%s: phys_complete: %#llx\n", __func__, + (unsigned long long) phys_complete); + + if (is_ioat_halted(completion)) { + u32 chanerr = readl(chan->reg_base + IOAT_CHANERR_OFFSET); + dev_err(to_dev(chan), "Channel halted, chanerr = %x\n", + chanerr); + + /* TODO do something to salvage the situation */ + } + + return phys_complete; +} + +bool ioat_cleanup_preamble(struct ioat_chan_common *chan, + unsigned long *phys_complete) +{ + *phys_complete = ioat_get_current_completion(chan); + if (*phys_complete == chan->last_completion) + return false; + clear_bit(IOAT_COMPLETION_ACK, &chan->state); + mod_timer(&chan->timer, jiffies + COMPLETION_TIMEOUT); + + return true; +} + +static void __cleanup(struct ioat_dma_chan *ioat, unsigned long phys_complete) +{ + struct ioat_chan_common *chan = &ioat->base; + struct list_head *_desc, *n; + struct dma_async_tx_descriptor *tx; + + dev_dbg(to_dev(chan), "%s: phys_complete: %lx\n", + __func__, phys_complete); + list_for_each_safe(_desc, n, &ioat->used_desc) { + struct ioat_desc_sw *desc; + + prefetch(n); + desc = list_entry(_desc, typeof(*desc), node); + tx = &desc->txd; + /* + * Incoming DMA requests may use multiple descriptors, + * due to exceeding xfercap, perhaps. If so, only the + * last one will have a cookie, and require unmapping. + */ + dump_desc_dbg(ioat, desc); + if (tx->cookie) { + chan->completed_cookie = tx->cookie; + tx->cookie = 0; + ioat_dma_unmap(chan, tx->flags, desc->len, desc->hw); + ioat->active -= desc->hw->tx_cnt; + if (tx->callback) { + tx->callback(tx->callback_param); + tx->callback = NULL; + } + } + + if (tx->phys != phys_complete) { + /* + * a completed entry, but not the last, so clean + * up if the client is done with the descriptor + */ + if (async_tx_test_ack(tx)) + list_move_tail(&desc->node, &ioat->free_desc); + } else { + /* + * last used desc. Do not remove, so we can + * append from it. + */ + + /* if nothing else is pending, cancel the + * completion timeout + */ + if (n == &ioat->used_desc) { + dev_dbg(to_dev(chan), + "%s cancel completion timeout\n", + __func__); + clear_bit(IOAT_COMPLETION_PENDING, &chan->state); + } + + /* TODO check status bits? */ + break; + } + } + + chan->last_completion = phys_complete; +} + +/** + * ioat1_cleanup - cleanup up finished descriptors + * @chan: ioat channel to be cleaned up + * + * To prevent lock contention we defer cleanup when the locks are + * contended with a terminal timeout that forces cleanup and catches + * completion notification errors. + */ +static void ioat1_cleanup(struct ioat_dma_chan *ioat) +{ + struct ioat_chan_common *chan = &ioat->base; + unsigned long phys_complete; + + prefetch(chan->completion); + + if (!spin_trylock_bh(&chan->cleanup_lock)) + return; + + if (!ioat_cleanup_preamble(chan, &phys_complete)) { + spin_unlock_bh(&chan->cleanup_lock); + return; + } + + if (!spin_trylock_bh(&ioat->desc_lock)) { + spin_unlock_bh(&chan->cleanup_lock); + return; + } + + __cleanup(ioat, phys_complete); + + spin_unlock_bh(&ioat->desc_lock); + spin_unlock_bh(&chan->cleanup_lock); +} + +static void ioat1_timer_event(unsigned long data) +{ + struct ioat_dma_chan *ioat = (void *) data; + struct ioat_chan_common *chan = &ioat->base; + + dev_dbg(to_dev(chan), "%s: state: %lx\n", __func__, chan->state); + + spin_lock_bh(&chan->cleanup_lock); + if (test_and_clear_bit(IOAT_RESET_PENDING, &chan->state)) { + struct ioat_desc_sw *desc; + + spin_lock_bh(&ioat->desc_lock); + + /* restart active descriptors */ + desc = to_ioat_desc(ioat->used_desc.prev); + ioat_set_chainaddr(ioat, desc->txd.phys); + ioat_start(chan); + + ioat->pending = 0; + set_bit(IOAT_COMPLETION_PENDING, &chan->state); + mod_timer(&chan->timer, jiffies + COMPLETION_TIMEOUT); + spin_unlock_bh(&ioat->desc_lock); + } else if (test_bit(IOAT_COMPLETION_PENDING, &chan->state)) { + unsigned long phys_complete; + + spin_lock_bh(&ioat->desc_lock); + /* if we haven't made progress and we have already + * acknowledged a pending completion once, then be more + * forceful with a restart + */ + if (ioat_cleanup_preamble(chan, &phys_complete)) + __cleanup(ioat, phys_complete); + else if (test_bit(IOAT_COMPLETION_ACK, &chan->state)) + ioat1_reset_channel(ioat); + else { + u64 status = ioat_chansts(chan); + + /* manually update the last completion address */ + if (ioat_chansts_to_addr(status) != 0) + *chan->completion = status; + + set_bit(IOAT_COMPLETION_ACK, &chan->state); + mod_timer(&chan->timer, jiffies + COMPLETION_TIMEOUT); + } + spin_unlock_bh(&ioat->desc_lock); + } + spin_unlock_bh(&chan->cleanup_lock); +} + +static enum dma_status +ioat1_dma_is_complete(struct dma_chan *c, dma_cookie_t cookie, + dma_cookie_t *done, dma_cookie_t *used) +{ + struct ioat_dma_chan *ioat = to_ioat_chan(c); + + if (ioat_is_complete(c, cookie, done, used) == DMA_SUCCESS) + return DMA_SUCCESS; + + ioat1_cleanup(ioat); + + return ioat_is_complete(c, cookie, done, used); +} + +static void ioat1_dma_start_null_desc(struct ioat_dma_chan *ioat) +{ + struct ioat_chan_common *chan = &ioat->base; + struct ioat_desc_sw *desc; + struct ioat_dma_descriptor *hw; + + spin_lock_bh(&ioat->desc_lock); + + desc = ioat1_dma_get_next_descriptor(ioat); + + if (!desc) { + dev_err(to_dev(chan), + "Unable to start null desc - get next desc failed\n"); + spin_unlock_bh(&ioat->desc_lock); + return; + } + + hw = desc->hw; + hw->ctl = 0; + hw->ctl_f.null = 1; + hw->ctl_f.int_en = 1; + hw->ctl_f.compl_write = 1; + /* set size to non-zero value (channel returns error when size is 0) */ + hw->size = NULL_DESC_BUFFER_SIZE; + hw->src_addr = 0; + hw->dst_addr = 0; + async_tx_ack(&desc->txd); + hw->next = 0; + list_add_tail(&desc->node, &ioat->used_desc); + dump_desc_dbg(ioat, desc); + + ioat_set_chainaddr(ioat, desc->txd.phys); + ioat_start(chan); + spin_unlock_bh(&ioat->desc_lock); +} + +/* + * Perform a IOAT transaction to verify the HW works. + */ +#define IOAT_TEST_SIZE 2000 + +static void __devinit ioat_dma_test_callback(void *dma_async_param) +{ + struct completion *cmp = dma_async_param; + + complete(cmp); +} + +/** + * ioat_dma_self_test - Perform a IOAT transaction to verify the HW works. + * @device: device to be tested + */ +int __devinit ioat_dma_self_test(struct ioatdma_device *device) +{ + int i; + u8 *src; + u8 *dest; + struct dma_device *dma = &device->common; + struct device *dev = &device->pdev->dev; + struct dma_chan *dma_chan; + struct dma_async_tx_descriptor *tx; + dma_addr_t dma_dest, dma_src; + dma_cookie_t cookie; + int err = 0; + struct completion cmp; + unsigned long tmo; + unsigned long flags; + + src = kzalloc(sizeof(u8) * IOAT_TEST_SIZE, GFP_KERNEL); + if (!src) + return -ENOMEM; + dest = kzalloc(sizeof(u8) * IOAT_TEST_SIZE, GFP_KERNEL); + if (!dest) { + kfree(src); + return -ENOMEM; + } + + /* Fill in src buffer */ + for (i = 0; i < IOAT_TEST_SIZE; i++) + src[i] = (u8)i; + + /* Start copy, using first DMA channel */ + dma_chan = container_of(dma->channels.next, struct dma_chan, + device_node); + if (dma->device_alloc_chan_resources(dma_chan) < 1) { + dev_err(dev, "selftest cannot allocate chan resource\n"); + err = -ENODEV; + goto out; + } + + dma_src = dma_map_single(dev, src, IOAT_TEST_SIZE, DMA_TO_DEVICE); + dma_dest = dma_map_single(dev, dest, IOAT_TEST_SIZE, DMA_FROM_DEVICE); + flags = DMA_COMPL_SRC_UNMAP_SINGLE | DMA_COMPL_DEST_UNMAP_SINGLE | + DMA_PREP_INTERRUPT; + tx = device->common.device_prep_dma_memcpy(dma_chan, dma_dest, dma_src, + IOAT_TEST_SIZE, flags); + if (!tx) { + dev_err(dev, "Self-test prep failed, disabling\n"); + err = -ENODEV; + goto free_resources; + } + + async_tx_ack(tx); + init_completion(&cmp); + tx->callback = ioat_dma_test_callback; + tx->callback_param = &cmp; + cookie = tx->tx_submit(tx); + if (cookie < 0) { + dev_err(dev, "Self-test setup failed, disabling\n"); + err = -ENODEV; + goto free_resources; + } + dma->device_issue_pending(dma_chan); + + tmo = wait_for_completion_timeout(&cmp, msecs_to_jiffies(3000)); + + if (tmo == 0 || + dma->device_is_tx_complete(dma_chan, cookie, NULL, NULL) + != DMA_SUCCESS) { + dev_err(dev, "Self-test copy timed out, disabling\n"); + err = -ENODEV; + goto free_resources; + } + if (memcmp(src, dest, IOAT_TEST_SIZE)) { + dev_err(dev, "Self-test copy failed compare, disabling\n"); + err = -ENODEV; + goto free_resources; + } + +free_resources: + dma->device_free_chan_resources(dma_chan); +out: + kfree(src); + kfree(dest); + return err; +} + +static char ioat_interrupt_style[32] = "msix"; +module_param_string(ioat_interrupt_style, ioat_interrupt_style, + sizeof(ioat_interrupt_style), 0644); +MODULE_PARM_DESC(ioat_interrupt_style, + "set ioat interrupt style: msix (default), " + "msix-single-vector, msi, intx)"); + +/** + * ioat_dma_setup_interrupts - setup interrupt handler + * @device: ioat device + */ +static int ioat_dma_setup_interrupts(struct ioatdma_device *device) +{ + struct ioat_chan_common *chan; + struct pci_dev *pdev = device->pdev; + struct device *dev = &pdev->dev; + struct msix_entry *msix; + int i, j, msixcnt; + int err = -EINVAL; + u8 intrctrl = 0; + + if (!strcmp(ioat_interrupt_style, "msix")) + goto msix; + if (!strcmp(ioat_interrupt_style, "msix-single-vector")) + goto msix_single_vector; + if (!strcmp(ioat_interrupt_style, "msi")) + goto msi; + if (!strcmp(ioat_interrupt_style, "intx")) + goto intx; + dev_err(dev, "invalid ioat_interrupt_style %s\n", ioat_interrupt_style); + goto err_no_irq; + +msix: + /* The number of MSI-X vectors should equal the number of channels */ + msixcnt = device->common.chancnt; + for (i = 0; i < msixcnt; i++) + device->msix_entries[i].entry = i; + + err = pci_enable_msix(pdev, device->msix_entries, msixcnt); + if (err < 0) + goto msi; + if (err > 0) + goto msix_single_vector; + + for (i = 0; i < msixcnt; i++) { + msix = &device->msix_entries[i]; + chan = ioat_chan_by_index(device, i); + err = devm_request_irq(dev, msix->vector, + ioat_dma_do_interrupt_msix, 0, + "ioat-msix", chan); + if (err) { + for (j = 0; j < i; j++) { + msix = &device->msix_entries[j]; + chan = ioat_chan_by_index(device, j); + devm_free_irq(dev, msix->vector, chan); + } + goto msix_single_vector; + } + } + intrctrl |= IOAT_INTRCTRL_MSIX_VECTOR_CONTROL; + goto done; + +msix_single_vector: + msix = &device->msix_entries[0]; + msix->entry = 0; + err = pci_enable_msix(pdev, device->msix_entries, 1); + if (err) + goto msi; + + err = devm_request_irq(dev, msix->vector, ioat_dma_do_interrupt, 0, + "ioat-msix", device); + if (err) { + pci_disable_msix(pdev); + goto msi; + } + goto done; + +msi: + err = pci_enable_msi(pdev); + if (err) + goto intx; + + err = devm_request_irq(dev, pdev->irq, ioat_dma_do_interrupt, 0, + "ioat-msi", device); + if (err) { + pci_disable_msi(pdev); + goto intx; + } + goto done; + +intx: + err = devm_request_irq(dev, pdev->irq, ioat_dma_do_interrupt, + IRQF_SHARED, "ioat-intx", device); + if (err) + goto err_no_irq; + +done: + if (device->intr_quirk) + device->intr_quirk(device); + intrctrl |= IOAT_INTRCTRL_MASTER_INT_EN; + writeb(intrctrl, device->reg_base + IOAT_INTRCTRL_OFFSET); + return 0; + +err_no_irq: + /* Disable all interrupt generation */ + writeb(0, device->reg_base + IOAT_INTRCTRL_OFFSET); + dev_err(dev, "no usable interrupts\n"); + return err; +} + +static void ioat_disable_interrupts(struct ioatdma_device *device) +{ + /* Disable all interrupt generation */ + writeb(0, device->reg_base + IOAT_INTRCTRL_OFFSET); +} + +int __devinit ioat_probe(struct ioatdma_device *device) +{ + int err = -ENODEV; + struct dma_device *dma = &device->common; + struct pci_dev *pdev = device->pdev; + struct device *dev = &pdev->dev; + + /* DMA coherent memory pool for DMA descriptor allocations */ + device->dma_pool = pci_pool_create("dma_desc_pool", pdev, + sizeof(struct ioat_dma_descriptor), + 64, 0); + if (!device->dma_pool) { + err = -ENOMEM; + goto err_dma_pool; + } + + device->completion_pool = pci_pool_create("completion_pool", pdev, + sizeof(u64), SMP_CACHE_BYTES, + SMP_CACHE_BYTES); + + if (!device->completion_pool) { + err = -ENOMEM; + goto err_completion_pool; + } + + device->enumerate_channels(device); + + dma_cap_set(DMA_MEMCPY, dma->cap_mask); + dma->dev = &pdev->dev; + + if (!dma->chancnt) { + dev_err(dev, "zero channels detected\n"); + goto err_setup_interrupts; + } + + err = ioat_dma_setup_interrupts(device); + if (err) + goto err_setup_interrupts; + + err = device->self_test(device); + if (err) + goto err_self_test; + + return 0; + +err_self_test: + ioat_disable_interrupts(device); +err_setup_interrupts: + pci_pool_destroy(device->completion_pool); +err_completion_pool: + pci_pool_destroy(device->dma_pool); +err_dma_pool: + return err; +} + +int __devinit ioat_register(struct ioatdma_device *device) +{ + int err = dma_async_device_register(&device->common); + + if (err) { + ioat_disable_interrupts(device); + pci_pool_destroy(device->completion_pool); + pci_pool_destroy(device->dma_pool); + } + + return err; +} + +/* ioat1_intr_quirk - fix up dma ctrl register to enable / disable msi */ +static void ioat1_intr_quirk(struct ioatdma_device *device) +{ + struct pci_dev *pdev = device->pdev; + u32 dmactrl; + + pci_read_config_dword(pdev, IOAT_PCI_DMACTRL_OFFSET, &dmactrl); + if (pdev->msi_enabled) + dmactrl |= IOAT_PCI_DMACTRL_MSI_EN; + else + dmactrl &= ~IOAT_PCI_DMACTRL_MSI_EN; + pci_write_config_dword(pdev, IOAT_PCI_DMACTRL_OFFSET, dmactrl); +} + +static ssize_t ring_size_show(struct dma_chan *c, char *page) +{ + struct ioat_dma_chan *ioat = to_ioat_chan(c); + + return sprintf(page, "%d\n", ioat->desccount); +} +static struct ioat_sysfs_entry ring_size_attr = __ATTR_RO(ring_size); + +static ssize_t ring_active_show(struct dma_chan *c, char *page) +{ + struct ioat_dma_chan *ioat = to_ioat_chan(c); + + return sprintf(page, "%d\n", ioat->active); +} +static struct ioat_sysfs_entry ring_active_attr = __ATTR_RO(ring_active); + +static ssize_t cap_show(struct dma_chan *c, char *page) +{ + struct dma_device *dma = c->device; + + return sprintf(page, "copy%s%s%s%s%s%s\n", + dma_has_cap(DMA_PQ, dma->cap_mask) ? " pq" : "", + dma_has_cap(DMA_PQ_VAL, dma->cap_mask) ? " pq_val" : "", + dma_has_cap(DMA_XOR, dma->cap_mask) ? " xor" : "", + dma_has_cap(DMA_XOR_VAL, dma->cap_mask) ? " xor_val" : "", + dma_has_cap(DMA_MEMSET, dma->cap_mask) ? " fill" : "", + dma_has_cap(DMA_INTERRUPT, dma->cap_mask) ? " intr" : ""); + +} +struct ioat_sysfs_entry ioat_cap_attr = __ATTR_RO(cap); + +static ssize_t version_show(struct dma_chan *c, char *page) +{ + struct dma_device *dma = c->device; + struct ioatdma_device *device = to_ioatdma_device(dma); + + return sprintf(page, "%d.%d\n", + device->version >> 4, device->version & 0xf); +} +struct ioat_sysfs_entry ioat_version_attr = __ATTR_RO(version); + +static struct attribute *ioat1_attrs[] = { + &ring_size_attr.attr, + &ring_active_attr.attr, + &ioat_cap_attr.attr, + &ioat_version_attr.attr, + NULL, +}; + +static ssize_t +ioat_attr_show(struct kobject *kobj, struct attribute *attr, char *page) +{ + struct ioat_sysfs_entry *entry; + struct ioat_chan_common *chan; + + entry = container_of(attr, struct ioat_sysfs_entry, attr); + chan = container_of(kobj, struct ioat_chan_common, kobj); + + if (!entry->show) + return -EIO; + return entry->show(&chan->common, page); +} + +struct sysfs_ops ioat_sysfs_ops = { + .show = ioat_attr_show, +}; + +static struct kobj_type ioat1_ktype = { + .sysfs_ops = &ioat_sysfs_ops, + .default_attrs = ioat1_attrs, +}; + +void ioat_kobject_add(struct ioatdma_device *device, struct kobj_type *type) +{ + struct dma_device *dma = &device->common; + struct dma_chan *c; + + list_for_each_entry(c, &dma->channels, device_node) { + struct ioat_chan_common *chan = to_chan_common(c); + struct kobject *parent = &c->dev->device.kobj; + int err; + + err = kobject_init_and_add(&chan->kobj, type, parent, "quickdata"); + if (err) { + dev_warn(to_dev(chan), + "sysfs init error (%d), continuing...\n", err); + kobject_put(&chan->kobj); + set_bit(IOAT_KOBJ_INIT_FAIL, &chan->state); + } + } +} + +void ioat_kobject_del(struct ioatdma_device *device) +{ + struct dma_device *dma = &device->common; + struct dma_chan *c; + + list_for_each_entry(c, &dma->channels, device_node) { + struct ioat_chan_common *chan = to_chan_common(c); + + if (!test_bit(IOAT_KOBJ_INIT_FAIL, &chan->state)) { + kobject_del(&chan->kobj); + kobject_put(&chan->kobj); + } + } +} + +int __devinit ioat1_dma_probe(struct ioatdma_device *device, int dca) +{ + struct pci_dev *pdev = device->pdev; + struct dma_device *dma; + int err; + + device->intr_quirk = ioat1_intr_quirk; + device->enumerate_channels = ioat1_enumerate_channels; + device->self_test = ioat_dma_self_test; + dma = &device->common; + dma->device_prep_dma_memcpy = ioat1_dma_prep_memcpy; + dma->device_issue_pending = ioat1_dma_memcpy_issue_pending; + dma->device_alloc_chan_resources = ioat1_dma_alloc_chan_resources; + dma->device_free_chan_resources = ioat1_dma_free_chan_resources; + dma->device_is_tx_complete = ioat1_dma_is_complete; + + err = ioat_probe(device); + if (err) + return err; + ioat_set_tcp_copy_break(4096); + err = ioat_register(device); + if (err) + return err; + ioat_kobject_add(device, &ioat1_ktype); + + if (dca) + device->dca = ioat_dca_init(pdev, device->reg_base); + + return err; +} + +void __devexit ioat_dma_remove(struct ioatdma_device *device) +{ + struct dma_device *dma = &device->common; + + ioat_disable_interrupts(device); + + ioat_kobject_del(device); + + dma_async_device_unregister(dma); + + pci_pool_destroy(device->dma_pool); + pci_pool_destroy(device->completion_pool); + + INIT_LIST_HEAD(&dma->channels); +} diff --git a/drivers/dma/ioat/dma.h b/drivers/dma/ioat/dma.h new file mode 100644 index 000000000000..45edde996480 --- /dev/null +++ b/drivers/dma/ioat/dma.h @@ -0,0 +1,335 @@ +/* + * Copyright(c) 2004 - 2009 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 59 + * Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * The full GNU General Public License is included in this distribution in the + * file called COPYING. + */ +#ifndef IOATDMA_H +#define IOATDMA_H + +#include <linux/dmaengine.h> +#include "hw.h" +#include "registers.h" +#include <linux/init.h> +#include <linux/dmapool.h> +#include <linux/cache.h> +#include <linux/pci_ids.h> +#include <net/tcp.h> + +#define IOAT_DMA_VERSION "4.00" + +#define IOAT_LOW_COMPLETION_MASK 0xffffffc0 +#define IOAT_DMA_DCA_ANY_CPU ~0 + +#define to_ioatdma_device(dev) container_of(dev, struct ioatdma_device, common) +#define to_ioat_desc(lh) container_of(lh, struct ioat_desc_sw, node) +#define tx_to_ioat_desc(tx) container_of(tx, struct ioat_desc_sw, txd) +#define to_dev(ioat_chan) (&(ioat_chan)->device->pdev->dev) + +#define chan_num(ch) ((int)((ch)->reg_base - (ch)->device->reg_base) / 0x80) + +/* + * workaround for IOAT ver.3.0 null descriptor issue + * (channel returns error when size is 0) + */ +#define NULL_DESC_BUFFER_SIZE 1 + +/** + * struct ioatdma_device - internal representation of a IOAT device + * @pdev: PCI-Express device + * @reg_base: MMIO register space base address + * @dma_pool: for allocating DMA descriptors + * @common: embedded struct dma_device + * @version: version of ioatdma device + * @msix_entries: irq handlers + * @idx: per channel data + * @dca: direct cache access context + * @intr_quirk: interrupt setup quirk (for ioat_v1 devices) + * @enumerate_channels: hw version specific channel enumeration + * @cleanup_tasklet: select between the v2 and v3 cleanup routines + * @timer_fn: select between the v2 and v3 timer watchdog routines + * @self_test: hardware version specific self test for each supported op type + * + * Note: the v3 cleanup routine supports raid operations + */ +struct ioatdma_device { + struct pci_dev *pdev; + void __iomem *reg_base; + struct pci_pool *dma_pool; + struct pci_pool *completion_pool; + struct dma_device common; + u8 version; + struct msix_entry msix_entries[4]; + struct ioat_chan_common *idx[4]; + struct dca_provider *dca; + void (*intr_quirk)(struct ioatdma_device *device); + int (*enumerate_channels)(struct ioatdma_device *device); + void (*cleanup_tasklet)(unsigned long data); + void (*timer_fn)(unsigned long data); + int (*self_test)(struct ioatdma_device *device); +}; + +struct ioat_chan_common { + struct dma_chan common; + void __iomem *reg_base; + unsigned long last_completion; + spinlock_t cleanup_lock; + dma_cookie_t completed_cookie; + unsigned long state; + #define IOAT_COMPLETION_PENDING 0 + #define IOAT_COMPLETION_ACK 1 + #define IOAT_RESET_PENDING 2 + #define IOAT_KOBJ_INIT_FAIL 3 + struct timer_list timer; + #define COMPLETION_TIMEOUT msecs_to_jiffies(100) + #define IDLE_TIMEOUT msecs_to_jiffies(2000) + #define RESET_DELAY msecs_to_jiffies(100) + struct ioatdma_device *device; + dma_addr_t completion_dma; + u64 *completion; + struct tasklet_struct cleanup_task; + struct kobject kobj; +}; + +struct ioat_sysfs_entry { + struct attribute attr; + ssize_t (*show)(struct dma_chan *, char *); +}; + +/** + * struct ioat_dma_chan - internal representation of a DMA channel + */ +struct ioat_dma_chan { + struct ioat_chan_common base; + + size_t xfercap; /* XFERCAP register value expanded out */ + + spinlock_t desc_lock; + struct list_head free_desc; + struct list_head used_desc; + + int pending; + u16 desccount; + u16 active; +}; + +static inline struct ioat_chan_common *to_chan_common(struct dma_chan *c) +{ + return container_of(c, struct ioat_chan_common, common); +} + +static inline struct ioat_dma_chan *to_ioat_chan(struct dma_chan *c) +{ + struct ioat_chan_common *chan = to_chan_common(c); + + return container_of(chan, struct ioat_dma_chan, base); +} + +/** + * ioat_is_complete - poll the status of an ioat transaction + * @c: channel handle + * @cookie: transaction identifier + * @done: if set, updated with last completed transaction + * @used: if set, updated with last used transaction + */ +static inline enum dma_status +ioat_is_complete(struct dma_chan *c, dma_cookie_t cookie, + dma_cookie_t *done, dma_cookie_t *used) +{ + struct ioat_chan_common *chan = to_chan_common(c); + dma_cookie_t last_used; + dma_cookie_t last_complete; + + last_used = c->cookie; + last_complete = chan->completed_cookie; + + if (done) + *done = last_complete; + if (used) + *used = last_used; + + return dma_async_is_complete(cookie, last_complete, last_used); +} + +/* wrapper around hardware descriptor format + additional software fields */ + +/** + * struct ioat_desc_sw - wrapper around hardware descriptor + * @hw: hardware DMA descriptor (for memcpy) + * @node: this descriptor will either be on the free list, + * or attached to a transaction list (tx_list) + * @txd: the generic software descriptor for all engines + * @id: identifier for debug + */ +struct ioat_desc_sw { + struct ioat_dma_descriptor *hw; + struct list_head node; + size_t len; + struct list_head tx_list; + struct dma_async_tx_descriptor txd; + #ifdef DEBUG + int id; + #endif +}; + +#ifdef DEBUG +#define set_desc_id(desc, i) ((desc)->id = (i)) +#define desc_id(desc) ((desc)->id) +#else +#define set_desc_id(desc, i) +#define desc_id(desc) (0) +#endif + +static inline void +__dump_desc_dbg(struct ioat_chan_common *chan, struct ioat_dma_descriptor *hw, + struct dma_async_tx_descriptor *tx, int id) +{ + struct device *dev = to_dev(chan); + + dev_dbg(dev, "desc[%d]: (%#llx->%#llx) cookie: %d flags: %#x" + " ctl: %#x (op: %d int_en: %d compl: %d)\n", id, + (unsigned long long) tx->phys, + (unsigned long long) hw->next, tx->cookie, tx->flags, + hw->ctl, hw->ctl_f.op, hw->ctl_f.int_en, hw->ctl_f.compl_write); +} + +#define dump_desc_dbg(c, d) \ + ({ if (d) __dump_desc_dbg(&c->base, d->hw, &d->txd, desc_id(d)); 0; }) + +static inline void ioat_set_tcp_copy_break(unsigned long copybreak) +{ + #ifdef CONFIG_NET_DMA + sysctl_tcp_dma_copybreak = copybreak; + #endif +} + +static inline struct ioat_chan_common * +ioat_chan_by_index(struct ioatdma_device *device, int index) +{ + return device->idx[index]; +} + +static inline u64 ioat_chansts(struct ioat_chan_common *chan) +{ + u8 ver = chan->device->version; + u64 status; + u32 status_lo; + + /* We need to read the low address first as this causes the + * chipset to latch the upper bits for the subsequent read + */ + status_lo = readl(chan->reg_base + IOAT_CHANSTS_OFFSET_LOW(ver)); + status = readl(chan->reg_base + IOAT_CHANSTS_OFFSET_HIGH(ver)); + status <<= 32; + status |= status_lo; + + return status; +} + +static inline void ioat_start(struct ioat_chan_common *chan) +{ + u8 ver = chan->device->version; + + writeb(IOAT_CHANCMD_START, chan->reg_base + IOAT_CHANCMD_OFFSET(ver)); +} + +static inline u64 ioat_chansts_to_addr(u64 status) +{ + return status & IOAT_CHANSTS_COMPLETED_DESCRIPTOR_ADDR; +} + +static inline u32 ioat_chanerr(struct ioat_chan_common *chan) +{ + return readl(chan->reg_base + IOAT_CHANERR_OFFSET); +} + +static inline void ioat_suspend(struct ioat_chan_common *chan) +{ + u8 ver = chan->device->version; + + writeb(IOAT_CHANCMD_SUSPEND, chan->reg_base + IOAT_CHANCMD_OFFSET(ver)); +} + +static inline void ioat_set_chainaddr(struct ioat_dma_chan *ioat, u64 addr) +{ + struct ioat_chan_common *chan = &ioat->base; + + writel(addr & 0x00000000FFFFFFFF, + chan->reg_base + IOAT1_CHAINADDR_OFFSET_LOW); + writel(addr >> 32, + chan->reg_base + IOAT1_CHAINADDR_OFFSET_HIGH); +} + +static inline bool is_ioat_active(unsigned long status) +{ + return ((status & IOAT_CHANSTS_STATUS) == IOAT_CHANSTS_ACTIVE); +} + +static inline bool is_ioat_idle(unsigned long status) +{ + return ((status & IOAT_CHANSTS_STATUS) == IOAT_CHANSTS_DONE); +} + +static inline bool is_ioat_halted(unsigned long status) +{ + return ((status & IOAT_CHANSTS_STATUS) == IOAT_CHANSTS_HALTED); +} + +static inline bool is_ioat_suspended(unsigned long status) +{ + return ((status & IOAT_CHANSTS_STATUS) == IOAT_CHANSTS_SUSPENDED); +} + +/* channel was fatally programmed */ +static inline bool is_ioat_bug(unsigned long err) +{ + return !!err; +} + +static inline void ioat_unmap(struct pci_dev *pdev, dma_addr_t addr, size_t len, + int direction, enum dma_ctrl_flags flags, bool dst) +{ + if ((dst && (flags & DMA_COMPL_DEST_UNMAP_SINGLE)) || + (!dst && (flags & DMA_COMPL_SRC_UNMAP_SINGLE))) + pci_unmap_single(pdev, addr, len, direction); + else + pci_unmap_page(pdev, addr, len, direction); +} + +int __devinit ioat_probe(struct ioatdma_device *device); +int __devinit ioat_register(struct ioatdma_device *device); +int __devinit ioat1_dma_probe(struct ioatdma_device *dev, int dca); +int __devinit ioat_dma_self_test(struct ioatdma_device *device); +void __devexit ioat_dma_remove(struct ioatdma_device *device); +struct dca_provider * __devinit ioat_dca_init(struct pci_dev *pdev, + void __iomem *iobase); +unsigned long ioat_get_current_completion(struct ioat_chan_common *chan); +void ioat_init_channel(struct ioatdma_device *device, + struct ioat_chan_common *chan, int idx, + void (*timer_fn)(unsigned long), + void (*tasklet)(unsigned long), + unsigned long ioat); +void ioat_dma_unmap(struct ioat_chan_common *chan, enum dma_ctrl_flags flags, + size_t len, struct ioat_dma_descriptor *hw); +bool ioat_cleanup_preamble(struct ioat_chan_common *chan, + unsigned long *phys_complete); +void ioat_kobject_add(struct ioatdma_device *device, struct kobj_type *type); +void ioat_kobject_del(struct ioatdma_device *device); +extern struct sysfs_ops ioat_sysfs_ops; +extern struct ioat_sysfs_entry ioat_version_attr; +extern struct ioat_sysfs_entry ioat_cap_attr; +#endif /* IOATDMA_H */ diff --git a/drivers/dma/ioat/dma_v2.c b/drivers/dma/ioat/dma_v2.c new file mode 100644 index 000000000000..8f1f7f05deaa --- /dev/null +++ b/drivers/dma/ioat/dma_v2.c @@ -0,0 +1,873 @@ +/* + * Intel I/OAT DMA Linux driver + * Copyright(c) 2004 - 2009 Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + * + * The full GNU General Public License is included in this distribution in + * the file called "COPYING". + * + */ + +/* + * This driver supports an Intel I/OAT DMA engine (versions >= 2), which + * does asynchronous data movement and checksumming operations. + */ + +#include <linux/init.h> +#include <linux/module.h> +#include <linux/pci.h> +#include <linux/interrupt.h> +#include <linux/dmaengine.h> +#include <linux/delay.h> +#include <linux/dma-mapping.h> +#include <linux/workqueue.h> +#include <linux/i7300_idle.h> +#include "dma.h" +#include "dma_v2.h" +#include "registers.h" +#include "hw.h" + +int ioat_ring_alloc_order = 8; +module_param(ioat_ring_alloc_order, int, 0644); +MODULE_PARM_DESC(ioat_ring_alloc_order, + "ioat2+: allocate 2^n descriptors per channel" + " (default: 8 max: 16)"); +static int ioat_ring_max_alloc_order = IOAT_MAX_ORDER; +module_param(ioat_ring_max_alloc_order, int, 0644); +MODULE_PARM_DESC(ioat_ring_max_alloc_order, + "ioat2+: upper limit for ring size (default: 16)"); + +void __ioat2_issue_pending(struct ioat2_dma_chan *ioat) +{ + void * __iomem reg_base = ioat->base.reg_base; + + ioat->pending = 0; + ioat->dmacount += ioat2_ring_pending(ioat); + ioat->issued = ioat->head; + /* make descriptor updates globally visible before notifying channel */ + wmb(); + writew(ioat->dmacount, reg_base + IOAT_CHAN_DMACOUNT_OFFSET); + dev_dbg(to_dev(&ioat->base), + "%s: head: %#x tail: %#x issued: %#x count: %#x\n", + __func__, ioat->head, ioat->tail, ioat->issued, ioat->dmacount); +} + +void ioat2_issue_pending(struct dma_chan *chan) +{ + struct ioat2_dma_chan *ioat = to_ioat2_chan(chan); + + spin_lock_bh(&ioat->ring_lock); + if (ioat->pending == 1) + __ioat2_issue_pending(ioat); + spin_unlock_bh(&ioat->ring_lock); +} + +/** + * ioat2_update_pending - log pending descriptors + * @ioat: ioat2+ channel + * + * set pending to '1' unless pending is already set to '2', pending == 2 + * indicates that submission is temporarily blocked due to an in-flight + * reset. If we are already above the ioat_pending_level threshold then + * just issue pending. + * + * called with ring_lock held + */ +static void ioat2_update_pending(struct ioat2_dma_chan *ioat) +{ + if (unlikely(ioat->pending == 2)) + return; + else if (ioat2_ring_pending(ioat) > ioat_pending_level) + __ioat2_issue_pending(ioat); + else + ioat->pending = 1; +} + +static void __ioat2_start_null_desc(struct ioat2_dma_chan *ioat) +{ + struct ioat_ring_ent *desc; + struct ioat_dma_descriptor *hw; + int idx; + + if (ioat2_ring_space(ioat) < 1) { + dev_err(to_dev(&ioat->base), + "Unable to start null desc - ring full\n"); + return; + } + + dev_dbg(to_dev(&ioat->base), "%s: head: %#x tail: %#x issued: %#x\n", + __func__, ioat->head, ioat->tail, ioat->issued); + idx = ioat2_desc_alloc(ioat, 1); + desc = ioat2_get_ring_ent(ioat, idx); + + hw = desc->hw; + hw->ctl = 0; + hw->ctl_f.null = 1; + hw->ctl_f.int_en = 1; + hw->ctl_f.compl_write = 1; + /* set size to non-zero value (channel returns error when size is 0) */ + hw->size = NULL_DESC_BUFFER_SIZE; + hw->src_addr = 0; + hw->dst_addr = 0; + async_tx_ack(&desc->txd); + ioat2_set_chainaddr(ioat, desc->txd.phys); + dump_desc_dbg(ioat, desc); + __ioat2_issue_pending(ioat); +} + +static void ioat2_start_null_desc(struct ioat2_dma_chan *ioat) +{ + spin_lock_bh(&ioat->ring_lock); + __ioat2_start_null_desc(ioat); + spin_unlock_bh(&ioat->ring_lock); +} + +static void __cleanup(struct ioat2_dma_chan *ioat, unsigned long phys_complete) +{ + struct ioat_chan_common *chan = &ioat->base; + struct dma_async_tx_descriptor *tx; + struct ioat_ring_ent *desc; + bool seen_current = false; + u16 active; + int i; + + dev_dbg(to_dev(chan), "%s: head: %#x tail: %#x issued: %#x\n", + __func__, ioat->head, ioat->tail, ioat->issued); + + active = ioat2_ring_active(ioat); + for (i = 0; i < active && !seen_current; i++) { + prefetch(ioat2_get_ring_ent(ioat, ioat->tail + i + 1)); + desc = ioat2_get_ring_ent(ioat, ioat->tail + i); + tx = &desc->txd; + dump_desc_dbg(ioat, desc); + if (tx->cookie) { + ioat_dma_unmap(chan, tx->flags, desc->len, desc->hw); + chan->completed_cookie = tx->cookie; + tx->cookie = 0; + if (tx->callback) { + tx->callback(tx->callback_param); + tx->callback = NULL; + } + } + + if (tx->phys == phys_complete) + seen_current = true; + } + ioat->tail += i; + BUG_ON(!seen_current); /* no active descs have written a completion? */ + + chan->last_completion = phys_complete; + if (ioat->head == ioat->tail) { + dev_dbg(to_dev(chan), "%s: cancel completion timeout\n", + __func__); + clear_bit(IOAT_COMPLETION_PENDING, &chan->state); + mod_timer(&chan->timer, jiffies + IDLE_TIMEOUT); + } +} + +/** + * ioat2_cleanup - clean finished descriptors (advance tail pointer) + * @chan: ioat channel to be cleaned up + */ +static void ioat2_cleanup(struct ioat2_dma_chan *ioat) +{ + struct ioat_chan_common *chan = &ioat->base; + unsigned long phys_complete; + + prefetch(chan->completion); + + if (!spin_trylock_bh(&chan->cleanup_lock)) + return; + + if (!ioat_cleanup_preamble(chan, &phys_complete)) { + spin_unlock_bh(&chan->cleanup_lock); + return; + } + + if (!spin_trylock_bh(&ioat->ring_lock)) { + spin_unlock_bh(&chan->cleanup_lock); + return; + } + + __cleanup(ioat, phys_complete); + + spin_unlock_bh(&ioat->ring_lock); + spin_unlock_bh(&chan->cleanup_lock); +} + +void ioat2_cleanup_tasklet(unsigned long data) +{ + struct ioat2_dma_chan *ioat = (void *) data; + + ioat2_cleanup(ioat); + writew(IOAT_CHANCTRL_RUN, ioat->base.reg_base + IOAT_CHANCTRL_OFFSET); +} + +void __ioat2_restart_chan(struct ioat2_dma_chan *ioat) +{ + struct ioat_chan_common *chan = &ioat->base; + + /* set the tail to be re-issued */ + ioat->issued = ioat->tail; + ioat->dmacount = 0; + set_bit(IOAT_COMPLETION_PENDING, &chan->state); + mod_timer(&chan->timer, jiffies + COMPLETION_TIMEOUT); + + dev_dbg(to_dev(chan), + "%s: head: %#x tail: %#x issued: %#x count: %#x\n", + __func__, ioat->head, ioat->tail, ioat->issued, ioat->dmacount); + + if (ioat2_ring_pending(ioat)) { + struct ioat_ring_ent *desc; + + desc = ioat2_get_ring_ent(ioat, ioat->tail); + ioat2_set_chainaddr(ioat, desc->txd.phys); + __ioat2_issue_pending(ioat); + } else + __ioat2_start_null_desc(ioat); +} + +static void ioat2_restart_channel(struct ioat2_dma_chan *ioat) +{ + struct ioat_chan_common *chan = &ioat->base; + unsigned long phys_complete; + u32 status; + + status = ioat_chansts(chan); + if (is_ioat_active(status) || is_ioat_idle(status)) + ioat_suspend(chan); + while (is_ioat_active(status) || is_ioat_idle(status)) { + status = ioat_chansts(chan); + cpu_relax(); + } + + if (ioat_cleanup_preamble(chan, &phys_complete)) + __cleanup(ioat, phys_complete); + + __ioat2_restart_chan(ioat); +} + +void ioat2_timer_event(unsigned long data) +{ + struct ioat2_dma_chan *ioat = (void *) data; + struct ioat_chan_common *chan = &ioat->base; + + spin_lock_bh(&chan->cleanup_lock); + if (test_bit(IOAT_COMPLETION_PENDING, &chan->state)) { + unsigned long phys_complete; + u64 status; + + spin_lock_bh(&ioat->ring_lock); + status = ioat_chansts(chan); + + /* when halted due to errors check for channel + * programming errors before advancing the completion state + */ + if (is_ioat_halted(status)) { + u32 chanerr; + + chanerr = readl(chan->reg_base + IOAT_CHANERR_OFFSET); + dev_err(to_dev(chan), "%s: Channel halted (%x)\n", + __func__, chanerr); + BUG_ON(is_ioat_bug(chanerr)); + } + + /* if we haven't made progress and we have already + * acknowledged a pending completion once, then be more + * forceful with a restart + */ + if (ioat_cleanup_preamble(chan, &phys_complete)) + __cleanup(ioat, phys_complete); + else if (test_bit(IOAT_COMPLETION_ACK, &chan->state)) + ioat2_restart_channel(ioat); + else { + set_bit(IOAT_COMPLETION_ACK, &chan->state); + mod_timer(&chan->timer, jiffies + COMPLETION_TIMEOUT); + } + spin_unlock_bh(&ioat->ring_lock); + } else { + u16 active; + + /* if the ring is idle, empty, and oversized try to step + * down the size + */ + spin_lock_bh(&ioat->ring_lock); + active = ioat2_ring_active(ioat); + if (active == 0 && ioat->alloc_order > ioat_get_alloc_order()) + reshape_ring(ioat, ioat->alloc_order-1); + spin_unlock_bh(&ioat->ring_lock); + + /* keep shrinking until we get back to our minimum + * default size + */ + if (ioat->alloc_order > ioat_get_alloc_order()) + mod_timer(&chan->timer, jiffies + IDLE_TIMEOUT); + } + spin_unlock_bh(&chan->cleanup_lock); +} + +/** + * ioat2_enumerate_channels - find and initialize the device's channels + * @device: the device to be enumerated + */ +int ioat2_enumerate_channels(struct ioatdma_device *device) +{ + struct ioat2_dma_chan *ioat; + struct device *dev = &device->pdev->dev; + struct dma_device *dma = &device->common; + u8 xfercap_log; + int i; + + INIT_LIST_HEAD(&dma->channels); + dma->chancnt = readb(device->reg_base + IOAT_CHANCNT_OFFSET); + dma->chancnt &= 0x1f; /* bits [4:0] valid */ + if (dma->chancnt > ARRAY_SIZE(device->idx)) { + dev_warn(dev, "(%d) exceeds max supported channels (%zu)\n", + dma->chancnt, ARRAY_SIZE(device->idx)); + dma->chancnt = ARRAY_SIZE(device->idx); + } + xfercap_log = readb(device->reg_base + IOAT_XFERCAP_OFFSET); + xfercap_log &= 0x1f; /* bits [4:0] valid */ + if (xfercap_log == 0) + return 0; + dev_dbg(dev, "%s: xfercap = %d\n", __func__, 1 << xfercap_log); + + /* FIXME which i/oat version is i7300? */ +#ifdef CONFIG_I7300_IDLE_IOAT_CHANNEL + if (i7300_idle_platform_probe(NULL, NULL, 1) == 0) + dma->chancnt--; +#endif + for (i = 0; i < dma->chancnt; i++) { + ioat = devm_kzalloc(dev, sizeof(*ioat), GFP_KERNEL); + if (!ioat) + break; + + ioat_init_channel(device, &ioat->base, i, + device->timer_fn, + device->cleanup_tasklet, + (unsigned long) ioat); + ioat->xfercap_log = xfercap_log; + spin_lock_init(&ioat->ring_lock); + } + dma->chancnt = i; + return i; +} + +static dma_cookie_t ioat2_tx_submit_unlock(struct dma_async_tx_descriptor *tx) +{ + struct dma_chan *c = tx->chan; + struct ioat2_dma_chan *ioat = to_ioat2_chan(c); + struct ioat_chan_common *chan = &ioat->base; + dma_cookie_t cookie = c->cookie; + + cookie++; + if (cookie < 0) + cookie = 1; + tx->cookie = cookie; + c->cookie = cookie; + dev_dbg(to_dev(&ioat->base), "%s: cookie: %d\n", __func__, cookie); + + if (!test_and_set_bit(IOAT_COMPLETION_PENDING, &chan->state)) + mod_timer(&chan->timer, jiffies + COMPLETION_TIMEOUT); + ioat2_update_pending(ioat); + spin_unlock_bh(&ioat->ring_lock); + + return cookie; +} + +static struct ioat_ring_ent *ioat2_alloc_ring_ent(struct dma_chan *chan, gfp_t flags) +{ + struct ioat_dma_descriptor *hw; + struct ioat_ring_ent *desc; + struct ioatdma_device *dma; + dma_addr_t phys; + + dma = to_ioatdma_device(chan->device); + hw = pci_pool_alloc(dma->dma_pool, flags, &phys); + if (!hw) + return NULL; + memset(hw, 0, sizeof(*hw)); + + desc = kmem_cache_alloc(ioat2_cache, flags); + if (!desc) { + pci_pool_free(dma->dma_pool, hw, phys); + return NULL; + } + memset(desc, 0, sizeof(*desc)); + + dma_async_tx_descriptor_init(&desc->txd, chan); + desc->txd.tx_submit = ioat2_tx_submit_unlock; + desc->hw = hw; + desc->txd.phys = phys; + return desc; +} + +static void ioat2_free_ring_ent(struct ioat_ring_ent *desc, struct dma_chan *chan) +{ + struct ioatdma_device *dma; + + dma = to_ioatdma_device(chan->device); + pci_pool_free(dma->dma_pool, desc->hw, desc->txd.phys); + kmem_cache_free(ioat2_cache, desc); +} + +static struct ioat_ring_ent **ioat2_alloc_ring(struct dma_chan *c, int order, gfp_t flags) +{ + struct ioat_ring_ent **ring; + int descs = 1 << order; + int i; + + if (order > ioat_get_max_alloc_order()) + return NULL; + + /* allocate the array to hold the software ring */ + ring = kcalloc(descs, sizeof(*ring), flags); + if (!ring) + return NULL; + for (i = 0; i < descs; i++) { + ring[i] = ioat2_alloc_ring_ent(c, flags); + if (!ring[i]) { + while (i--) + ioat2_free_ring_ent(ring[i], c); + kfree(ring); + return NULL; + } + set_desc_id(ring[i], i); + } + + /* link descs */ + for (i = 0; i < descs-1; i++) { + struct ioat_ring_ent *next = ring[i+1]; + struct ioat_dma_descriptor *hw = ring[i]->hw; + + hw->next = next->txd.phys; + } + ring[i]->hw->next = ring[0]->txd.phys; + + return ring; +} + +/* ioat2_alloc_chan_resources - allocate/initialize ioat2 descriptor ring + * @chan: channel to be initialized + */ +int ioat2_alloc_chan_resources(struct dma_chan *c) +{ + struct ioat2_dma_chan *ioat = to_ioat2_chan(c); + struct ioat_chan_common *chan = &ioat->base; + struct ioat_ring_ent **ring; + u32 chanerr; + int order; + + /* have we already been set up? */ + if (ioat->ring) + return 1 << ioat->alloc_order; + + /* Setup register to interrupt and write completion status on error */ + writew(IOAT_CHANCTRL_RUN, chan->reg_base + IOAT_CHANCTRL_OFFSET); + + chanerr = readl(chan->reg_base + IOAT_CHANERR_OFFSET); + if (chanerr) { + dev_err(to_dev(chan), "CHANERR = %x, clearing\n", chanerr); + writel(chanerr, chan->reg_base + IOAT_CHANERR_OFFSET); + } + + /* allocate a completion writeback area */ + /* doing 2 32bit writes to mmio since 1 64b write doesn't work */ + chan->completion = pci_pool_alloc(chan->device->completion_pool, + GFP_KERNEL, &chan->completion_dma); + if (!chan->completion) + return -ENOMEM; + + memset(chan->completion, 0, sizeof(*chan->completion)); + writel(((u64) chan->completion_dma) & 0x00000000FFFFFFFF, + chan->reg_base + IOAT_CHANCMP_OFFSET_LOW); + writel(((u64) chan->completion_dma) >> 32, + chan->reg_base + IOAT_CHANCMP_OFFSET_HIGH); + + order = ioat_get_alloc_order(); + ring = ioat2_alloc_ring(c, order, GFP_KERNEL); + if (!ring) + return -ENOMEM; + + spin_lock_bh(&ioat->ring_lock); + ioat->ring = ring; + ioat->head = 0; + ioat->issued = 0; + ioat->tail = 0; + ioat->pending = 0; + ioat->alloc_order = order; + spin_unlock_bh(&ioat->ring_lock); + + tasklet_enable(&chan->cleanup_task); + ioat2_start_null_desc(ioat); + + return 1 << ioat->alloc_order; +} + +bool reshape_ring(struct ioat2_dma_chan *ioat, int order) +{ + /* reshape differs from normal ring allocation in that we want + * to allocate a new software ring while only + * extending/truncating the hardware ring + */ + struct ioat_chan_common *chan = &ioat->base; + struct dma_chan *c = &chan->common; + const u16 curr_size = ioat2_ring_mask(ioat) + 1; + const u16 active = ioat2_ring_active(ioat); + const u16 new_size = 1 << order; + struct ioat_ring_ent **ring; + u16 i; + + if (order > ioat_get_max_alloc_order()) + return false; + + /* double check that we have at least 1 free descriptor */ + if (active == curr_size) + return false; + + /* when shrinking, verify that we can hold the current active + * set in the new ring + */ + if (active >= new_size) + return false; + + /* allocate the array to hold the software ring */ + ring = kcalloc(new_size, sizeof(*ring), GFP_NOWAIT); + if (!ring) + return false; + + /* allocate/trim descriptors as needed */ + if (new_size > curr_size) { + /* copy current descriptors to the new ring */ + for (i = 0; i < curr_size; i++) { + u16 curr_idx = (ioat->tail+i) & (curr_size-1); + u16 new_idx = (ioat->tail+i) & (new_size-1); + + ring[new_idx] = ioat->ring[curr_idx]; + set_desc_id(ring[new_idx], new_idx); + } + + /* add new descriptors to the ring */ + for (i = curr_size; i < new_size; i++) { + u16 new_idx = (ioat->tail+i) & (new_size-1); + + ring[new_idx] = ioat2_alloc_ring_ent(c, GFP_NOWAIT); + if (!ring[new_idx]) { + while (i--) { + u16 new_idx = (ioat->tail+i) & (new_size-1); + + ioat2_free_ring_ent(ring[new_idx], c); + } + kfree(ring); + return false; + } + set_desc_id(ring[new_idx], new_idx); + } + + /* hw link new descriptors */ + for (i = curr_size-1; i < new_size; i++) { + u16 new_idx = (ioat->tail+i) & (new_size-1); + struct ioat_ring_ent *next = ring[(new_idx+1) & (new_size-1)]; + struct ioat_dma_descriptor *hw = ring[new_idx]->hw; + + hw->next = next->txd.phys; + } + } else { + struct ioat_dma_descriptor *hw; + struct ioat_ring_ent *next; + + /* copy current descriptors to the new ring, dropping the + * removed descriptors + */ + for (i = 0; i < new_size; i++) { + u16 curr_idx = (ioat->tail+i) & (curr_size-1); + u16 new_idx = (ioat->tail+i) & (new_size-1); + + ring[new_idx] = ioat->ring[curr_idx]; + set_desc_id(ring[new_idx], new_idx); + } + + /* free deleted descriptors */ + for (i = new_size; i < curr_size; i++) { + struct ioat_ring_ent *ent; + + ent = ioat2_get_ring_ent(ioat, ioat->tail+i); + ioat2_free_ring_ent(ent, c); + } + + /* fix up hardware ring */ + hw = ring[(ioat->tail+new_size-1) & (new_size-1)]->hw; + next = ring[(ioat->tail+new_size) & (new_size-1)]; + hw->next = next->txd.phys; + } + + dev_dbg(to_dev(chan), "%s: allocated %d descriptors\n", + __func__, new_size); + + kfree(ioat->ring); + ioat->ring = ring; + ioat->alloc_order = order; + + return true; +} + +/** + * ioat2_alloc_and_lock - common descriptor alloc boilerplate for ioat2,3 ops + * @idx: gets starting descriptor index on successful allocation + * @ioat: ioat2,3 channel (ring) to operate on + * @num_descs: allocation length + */ +int ioat2_alloc_and_lock(u16 *idx, struct ioat2_dma_chan *ioat, int num_descs) +{ + struct ioat_chan_common *chan = &ioat->base; + + spin_lock_bh(&ioat->ring_lock); + /* never allow the last descriptor to be consumed, we need at + * least one free at all times to allow for on-the-fly ring + * resizing. + */ + while (unlikely(ioat2_ring_space(ioat) <= num_descs)) { + if (reshape_ring(ioat, ioat->alloc_order + 1) && + ioat2_ring_space(ioat) > num_descs) + break; + + if (printk_ratelimit()) + dev_dbg(to_dev(chan), + "%s: ring full! num_descs: %d (%x:%x:%x)\n", + __func__, num_descs, ioat->head, ioat->tail, + ioat->issued); + spin_unlock_bh(&ioat->ring_lock); + + /* progress reclaim in the allocation failure case we + * may be called under bh_disabled so we need to trigger + * the timer event directly + */ + spin_lock_bh(&chan->cleanup_lock); + if (jiffies > chan->timer.expires && + timer_pending(&chan->timer)) { + struct ioatdma_device *device = chan->device; + + mod_timer(&chan->timer, jiffies + COMPLETION_TIMEOUT); + spin_unlock_bh(&chan->cleanup_lock); + device->timer_fn((unsigned long) ioat); + } else + spin_unlock_bh(&chan->cleanup_lock); + return -ENOMEM; + } + + dev_dbg(to_dev(chan), "%s: num_descs: %d (%x:%x:%x)\n", + __func__, num_descs, ioat->head, ioat->tail, ioat->issued); + + *idx = ioat2_desc_alloc(ioat, num_descs); + return 0; /* with ioat->ring_lock held */ +} + +struct dma_async_tx_descriptor * +ioat2_dma_prep_memcpy_lock(struct dma_chan *c, dma_addr_t dma_dest, + dma_addr_t dma_src, size_t len, unsigned long flags) +{ + struct ioat2_dma_chan *ioat = to_ioat2_chan(c); + struct ioat_dma_descriptor *hw; + struct ioat_ring_ent *desc; + dma_addr_t dst = dma_dest; + dma_addr_t src = dma_src; + size_t total_len = len; + int num_descs; + u16 idx; + int i; + + num_descs = ioat2_xferlen_to_descs(ioat, len); + if (likely(num_descs) && + ioat2_alloc_and_lock(&idx, ioat, num_descs) == 0) + /* pass */; + else + return NULL; + i = 0; + do { + size_t copy = min_t(size_t, len, 1 << ioat->xfercap_log); + + desc = ioat2_get_ring_ent(ioat, idx + i); + hw = desc->hw; + + hw->size = copy; + hw->ctl = 0; + hw->src_addr = src; + hw->dst_addr = dst; + + len -= copy; + dst += copy; + src += copy; + dump_desc_dbg(ioat, desc); + } while (++i < num_descs); + + desc->txd.flags = flags; + desc->len = total_len; + hw->ctl_f.int_en = !!(flags & DMA_PREP_INTERRUPT); + hw->ctl_f.fence = !!(flags & DMA_PREP_FENCE); + hw->ctl_f.compl_write = 1; + dump_desc_dbg(ioat, desc); + /* we leave the channel locked to ensure in order submission */ + + return &desc->txd; +} + +/** + * ioat2_free_chan_resources - release all the descriptors + * @chan: the channel to be cleaned + */ +void ioat2_free_chan_resources(struct dma_chan *c) +{ + struct ioat2_dma_chan *ioat = to_ioat2_chan(c); + struct ioat_chan_common *chan = &ioat->base; + struct ioatdma_device *device = chan->device; + struct ioat_ring_ent *desc; + const u16 total_descs = 1 << ioat->alloc_order; + int descs; + int i; + + /* Before freeing channel resources first check + * if they have been previously allocated for this channel. + */ + if (!ioat->ring) + return; + + tasklet_disable(&chan->cleanup_task); + del_timer_sync(&chan->timer); + device->cleanup_tasklet((unsigned long) ioat); + + /* Delay 100ms after reset to allow internal DMA logic to quiesce + * before removing DMA descriptor resources. + */ + writeb(IOAT_CHANCMD_RESET, + chan->reg_base + IOAT_CHANCMD_OFFSET(chan->device->version)); + mdelay(100); + + spin_lock_bh(&ioat->ring_lock); + descs = ioat2_ring_space(ioat); + dev_dbg(to_dev(chan), "freeing %d idle descriptors\n", descs); + for (i = 0; i < descs; i++) { + desc = ioat2_get_ring_ent(ioat, ioat->head + i); + ioat2_free_ring_ent(desc, c); + } + + if (descs < total_descs) + dev_err(to_dev(chan), "Freeing %d in use descriptors!\n", + total_descs - descs); + + for (i = 0; i < total_descs - descs; i++) { + desc = ioat2_get_ring_ent(ioat, ioat->tail + i); + dump_desc_dbg(ioat, desc); + ioat2_free_ring_ent(desc, c); + } + + kfree(ioat->ring); + ioat->ring = NULL; + ioat->alloc_order = 0; + pci_pool_free(device->completion_pool, chan->completion, + chan->completion_dma); + spin_unlock_bh(&ioat->ring_lock); + + chan->last_completion = 0; + chan->completion_dma = 0; + ioat->pending = 0; + ioat->dmacount = 0; +} + +enum dma_status +ioat2_is_complete(struct dma_chan *c, dma_cookie_t cookie, + dma_cookie_t *done, dma_cookie_t *used) +{ + struct ioat2_dma_chan *ioat = to_ioat2_chan(c); + struct ioatdma_device *device = ioat->base.device; + + if (ioat_is_complete(c, cookie, done, used) == DMA_SUCCESS) + return DMA_SUCCESS; + + device->cleanup_tasklet((unsigned long) ioat); + + return ioat_is_complete(c, cookie, done, used); +} + +static ssize_t ring_size_show(struct dma_chan *c, char *page) +{ + struct ioat2_dma_chan *ioat = to_ioat2_chan(c); + + return sprintf(page, "%d\n", (1 << ioat->alloc_order) & ~1); +} +static struct ioat_sysfs_entry ring_size_attr = __ATTR_RO(ring_size); + +static ssize_t ring_active_show(struct dma_chan *c, char *page) +{ + struct ioat2_dma_chan *ioat = to_ioat2_chan(c); + + /* ...taken outside the lock, no need to be precise */ + return sprintf(page, "%d\n", ioat2_ring_active(ioat)); +} +static struct ioat_sysfs_entry ring_active_attr = __ATTR_RO(ring_active); + +static struct attribute *ioat2_attrs[] = { + &ring_size_attr.attr, + &ring_active_attr.attr, + &ioat_cap_attr.attr, + &ioat_version_attr.attr, + NULL, +}; + +struct kobj_type ioat2_ktype = { + .sysfs_ops = &ioat_sysfs_ops, + .default_attrs = ioat2_attrs, +}; + +int __devinit ioat2_dma_probe(struct ioatdma_device *device, int dca) +{ + struct pci_dev *pdev = device->pdev; + struct dma_device *dma; + struct dma_chan *c; + struct ioat_chan_common *chan; + int err; + + device->enumerate_channels = ioat2_enumerate_channels; + device->cleanup_tasklet = ioat2_cleanup_tasklet; + device->timer_fn = ioat2_timer_event; + device->self_test = ioat_dma_self_test; + dma = &device->common; + dma->device_prep_dma_memcpy = ioat2_dma_prep_memcpy_lock; + dma->device_issue_pending = ioat2_issue_pending; + dma->device_alloc_chan_resources = ioat2_alloc_chan_resources; + dma->device_free_chan_resources = ioat2_free_chan_resources; + dma->device_is_tx_complete = ioat2_is_complete; + + err = ioat_probe(device); + if (err) + return err; + ioat_set_tcp_copy_break(2048); + + list_for_each_entry(c, &dma->channels, device_node) { + chan = to_chan_common(c); + writel(IOAT_DCACTRL_CMPL_WRITE_ENABLE | IOAT_DMA_DCA_ANY_CPU, + chan->reg_base + IOAT_DCACTRL_OFFSET); + } + + err = ioat_register(device); + if (err) + return err; + + ioat_kobject_add(device, &ioat2_ktype); + + if (dca) + device->dca = ioat2_dca_init(pdev, device->reg_base); + + return err; +} diff --git a/drivers/dma/ioat/dma_v2.h b/drivers/dma/ioat/dma_v2.h new file mode 100644 index 000000000000..1d849ef74d5f --- /dev/null +++ b/drivers/dma/ioat/dma_v2.h @@ -0,0 +1,190 @@ +/* + * Copyright(c) 2004 - 2009 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 59 + * Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * The full GNU General Public License is included in this distribution in the + * file called COPYING. + */ +#ifndef IOATDMA_V2_H +#define IOATDMA_V2_H + +#include <linux/dmaengine.h> +#include "dma.h" +#include "hw.h" + + +extern int ioat_pending_level; +extern int ioat_ring_alloc_order; + +/* + * workaround for IOAT ver.3.0 null descriptor issue + * (channel returns error when size is 0) + */ +#define NULL_DESC_BUFFER_SIZE 1 + +#define IOAT_MAX_ORDER 16 +#define ioat_get_alloc_order() \ + (min(ioat_ring_alloc_order, IOAT_MAX_ORDER)) +#define ioat_get_max_alloc_order() \ + (min(ioat_ring_max_alloc_order, IOAT_MAX_ORDER)) + +/* struct ioat2_dma_chan - ioat v2 / v3 channel attributes + * @base: common ioat channel parameters + * @xfercap_log; log2 of channel max transfer length (for fast division) + * @head: allocated index + * @issued: hardware notification point + * @tail: cleanup index + * @pending: lock free indicator for issued != head + * @dmacount: identical to 'head' except for occasionally resetting to zero + * @alloc_order: log2 of the number of allocated descriptors + * @ring: software ring buffer implementation of hardware ring + * @ring_lock: protects ring attributes + */ +struct ioat2_dma_chan { + struct ioat_chan_common base; + size_t xfercap_log; + u16 head; + u16 issued; + u16 tail; + u16 dmacount; + u16 alloc_order; + int pending; + struct ioat_ring_ent **ring; + spinlock_t ring_lock; +}; + +static inline struct ioat2_dma_chan *to_ioat2_chan(struct dma_chan *c) +{ + struct ioat_chan_common *chan = to_chan_common(c); + + return container_of(chan, struct ioat2_dma_chan, base); +} + +static inline u16 ioat2_ring_mask(struct ioat2_dma_chan *ioat) +{ + return (1 << ioat->alloc_order) - 1; +} + +/* count of descriptors in flight with the engine */ +static inline u16 ioat2_ring_active(struct ioat2_dma_chan *ioat) +{ + return (ioat->head - ioat->tail) & ioat2_ring_mask(ioat); +} + +/* count of descriptors pending submission to hardware */ +static inline u16 ioat2_ring_pending(struct ioat2_dma_chan *ioat) +{ + return (ioat->head - ioat->issued) & ioat2_ring_mask(ioat); +} + +static inline u16 ioat2_ring_space(struct ioat2_dma_chan *ioat) +{ + u16 num_descs = ioat2_ring_mask(ioat) + 1; + u16 active = ioat2_ring_active(ioat); + + BUG_ON(active > num_descs); + + return num_descs - active; +} + +/* assumes caller already checked space */ +static inline u16 ioat2_desc_alloc(struct ioat2_dma_chan *ioat, u16 len) +{ + ioat->head += len; + return ioat->head - len; +} + +static inline u16 ioat2_xferlen_to_descs(struct ioat2_dma_chan *ioat, size_t len) +{ + u16 num_descs = len >> ioat->xfercap_log; + + num_descs += !!(len & ((1 << ioat->xfercap_log) - 1)); + return num_descs; +} + +/** + * struct ioat_ring_ent - wrapper around hardware descriptor + * @hw: hardware DMA descriptor (for memcpy) + * @fill: hardware fill descriptor + * @xor: hardware xor descriptor + * @xor_ex: hardware xor extension descriptor + * @pq: hardware pq descriptor + * @pq_ex: hardware pq extension descriptor + * @pqu: hardware pq update descriptor + * @raw: hardware raw (un-typed) descriptor + * @txd: the generic software descriptor for all engines + * @len: total transaction length for unmap + * @result: asynchronous result of validate operations + * @id: identifier for debug + */ + +struct ioat_ring_ent { + union { + struct ioat_dma_descriptor *hw; + struct ioat_fill_descriptor *fill; + struct ioat_xor_descriptor *xor; + struct ioat_xor_ext_descriptor *xor_ex; + struct ioat_pq_descriptor *pq; + struct ioat_pq_ext_descriptor *pq_ex; + struct ioat_pq_update_descriptor *pqu; + struct ioat_raw_descriptor *raw; + }; + size_t len; + struct dma_async_tx_descriptor txd; + enum sum_check_flags *result; + #ifdef DEBUG + int id; + #endif +}; + +static inline struct ioat_ring_ent * +ioat2_get_ring_ent(struct ioat2_dma_chan *ioat, u16 idx) +{ + return ioat->ring[idx & ioat2_ring_mask(ioat)]; +} + +static inline void ioat2_set_chainaddr(struct ioat2_dma_chan *ioat, u64 addr) +{ + struct ioat_chan_common *chan = &ioat->base; + + writel(addr & 0x00000000FFFFFFFF, + chan->reg_base + IOAT2_CHAINADDR_OFFSET_LOW); + writel(addr >> 32, + chan->reg_base + IOAT2_CHAINADDR_OFFSET_HIGH); +} + +int __devinit ioat2_dma_probe(struct ioatdma_device *dev, int dca); +int __devinit ioat3_dma_probe(struct ioatdma_device *dev, int dca); +struct dca_provider * __devinit ioat2_dca_init(struct pci_dev *pdev, void __iomem *iobase); +struct dca_provider * __devinit ioat3_dca_init(struct pci_dev *pdev, void __iomem *iobase); +int ioat2_alloc_and_lock(u16 *idx, struct ioat2_dma_chan *ioat, int num_descs); +int ioat2_enumerate_channels(struct ioatdma_device *device); +struct dma_async_tx_descriptor * +ioat2_dma_prep_memcpy_lock(struct dma_chan *c, dma_addr_t dma_dest, + dma_addr_t dma_src, size_t len, unsigned long flags); +void ioat2_issue_pending(struct dma_chan *chan); +int ioat2_alloc_chan_resources(struct dma_chan *c); +void ioat2_free_chan_resources(struct dma_chan *c); +enum dma_status ioat2_is_complete(struct dma_chan *c, dma_cookie_t cookie, + dma_cookie_t *done, dma_cookie_t *used); +void __ioat2_restart_chan(struct ioat2_dma_chan *ioat); +bool reshape_ring(struct ioat2_dma_chan *ioat, int order); +void __ioat2_issue_pending(struct ioat2_dma_chan *ioat); +void ioat2_cleanup_tasklet(unsigned long data); +void ioat2_timer_event(unsigned long data); +extern struct kobj_type ioat2_ktype; +extern struct kmem_cache *ioat2_cache; +#endif /* IOATDMA_V2_H */ diff --git a/drivers/dma/ioat/dma_v3.c b/drivers/dma/ioat/dma_v3.c new file mode 100644 index 000000000000..42f6f10fb0cc --- /dev/null +++ b/drivers/dma/ioat/dma_v3.c @@ -0,0 +1,1253 @@ +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2004 - 2009 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + * + * The full GNU General Public License is included in this distribution in + * the file called "COPYING". + * + * BSD LICENSE + * + * Copyright(c) 2004-2009 Intel Corporation. All rights reserved. + * + * 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. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * Support routines for v3+ hardware + */ + +#include <linux/pci.h> +#include <linux/dmaengine.h> +#include <linux/dma-mapping.h> +#include "registers.h" +#include "hw.h" +#include "dma.h" +#include "dma_v2.h" + +/* ioat hardware assumes at least two sources for raid operations */ +#define src_cnt_to_sw(x) ((x) + 2) +#define src_cnt_to_hw(x) ((x) - 2) + +/* provide a lookup table for setting the source address in the base or + * extended descriptor of an xor or pq descriptor + */ +static const u8 xor_idx_to_desc __read_mostly = 0xd0; +static const u8 xor_idx_to_field[] __read_mostly = { 1, 4, 5, 6, 7, 0, 1, 2 }; +static const u8 pq_idx_to_desc __read_mostly = 0xf8; +static const u8 pq_idx_to_field[] __read_mostly = { 1, 4, 5, 0, 1, 2, 4, 5 }; + +static dma_addr_t xor_get_src(struct ioat_raw_descriptor *descs[2], int idx) +{ + struct ioat_raw_descriptor *raw = descs[xor_idx_to_desc >> idx & 1]; + + return raw->field[xor_idx_to_field[idx]]; +} + +static void xor_set_src(struct ioat_raw_descriptor *descs[2], + dma_addr_t addr, u32 offset, int idx) +{ + struct ioat_raw_descriptor *raw = descs[xor_idx_to_desc >> idx & 1]; + + raw->field[xor_idx_to_field[idx]] = addr + offset; +} + +static dma_addr_t pq_get_src(struct ioat_raw_descriptor *descs[2], int idx) +{ + struct ioat_raw_descriptor *raw = descs[pq_idx_to_desc >> idx & 1]; + + return raw->field[pq_idx_to_field[idx]]; +} + +static void pq_set_src(struct ioat_raw_descriptor *descs[2], + dma_addr_t addr, u32 offset, u8 coef, int idx) +{ + struct ioat_pq_descriptor *pq = (struct ioat_pq_descriptor *) descs[0]; + struct ioat_raw_descriptor *raw = descs[pq_idx_to_desc >> idx & 1]; + + raw->field[pq_idx_to_field[idx]] = addr + offset; + pq->coef[idx] = coef; +} + +static void ioat3_dma_unmap(struct ioat2_dma_chan *ioat, + struct ioat_ring_ent *desc, int idx) +{ + struct ioat_chan_common *chan = &ioat->base; + struct pci_dev *pdev = chan->device->pdev; + size_t len = desc->len; + size_t offset = len - desc->hw->size; + struct dma_async_tx_descriptor *tx = &desc->txd; + enum dma_ctrl_flags flags = tx->flags; + + switch (desc->hw->ctl_f.op) { + case IOAT_OP_COPY: + if (!desc->hw->ctl_f.null) /* skip 'interrupt' ops */ + ioat_dma_unmap(chan, flags, len, desc->hw); + break; + case IOAT_OP_FILL: { + struct ioat_fill_descriptor *hw = desc->fill; + + if (!(flags & DMA_COMPL_SKIP_DEST_UNMAP)) + ioat_unmap(pdev, hw->dst_addr - offset, len, + PCI_DMA_FROMDEVICE, flags, 1); + break; + } + case IOAT_OP_XOR_VAL: + case IOAT_OP_XOR: { + struct ioat_xor_descriptor *xor = desc->xor; + struct ioat_ring_ent *ext; + struct ioat_xor_ext_descriptor *xor_ex = NULL; + int src_cnt = src_cnt_to_sw(xor->ctl_f.src_cnt); + struct ioat_raw_descriptor *descs[2]; + int i; + + if (src_cnt > 5) { + ext = ioat2_get_ring_ent(ioat, idx + 1); + xor_ex = ext->xor_ex; + } + + if (!(flags & DMA_COMPL_SKIP_SRC_UNMAP)) { + descs[0] = (struct ioat_raw_descriptor *) xor; + descs[1] = (struct ioat_raw_descriptor *) xor_ex; + for (i = 0; i < src_cnt; i++) { + dma_addr_t src = xor_get_src(descs, i); + + ioat_unmap(pdev, src - offset, len, + PCI_DMA_TODEVICE, flags, 0); + } + + /* dest is a source in xor validate operations */ + if (xor->ctl_f.op == IOAT_OP_XOR_VAL) { + ioat_unmap(pdev, xor->dst_addr - offset, len, + PCI_DMA_TODEVICE, flags, 1); + break; + } + } + + if (!(flags & DMA_COMPL_SKIP_DEST_UNMAP)) + ioat_unmap(pdev, xor->dst_addr - offset, len, + PCI_DMA_FROMDEVICE, flags, 1); + break; + } + case IOAT_OP_PQ_VAL: + case IOAT_OP_PQ: { + struct ioat_pq_descriptor *pq = desc->pq; + struct ioat_ring_ent *ext; + struct ioat_pq_ext_descriptor *pq_ex = NULL; + int src_cnt = src_cnt_to_sw(pq->ctl_f.src_cnt); + struct ioat_raw_descriptor *descs[2]; + int i; + + if (src_cnt > 3) { + ext = ioat2_get_ring_ent(ioat, idx + 1); + pq_ex = ext->pq_ex; + } + + /* in the 'continue' case don't unmap the dests as sources */ + if (dmaf_p_disabled_continue(flags)) + src_cnt--; + else if (dmaf_continue(flags)) + src_cnt -= 3; + + if (!(flags & DMA_COMPL_SKIP_SRC_UNMAP)) { + descs[0] = (struct ioat_raw_descriptor *) pq; + descs[1] = (struct ioat_raw_descriptor *) pq_ex; + for (i = 0; i < src_cnt; i++) { + dma_addr_t src = pq_get_src(descs, i); + + ioat_unmap(pdev, src - offset, len, + PCI_DMA_TODEVICE, flags, 0); + } + + /* the dests are sources in pq validate operations */ + if (pq->ctl_f.op == IOAT_OP_XOR_VAL) { + if (!(flags & DMA_PREP_PQ_DISABLE_P)) + ioat_unmap(pdev, pq->p_addr - offset, + len, PCI_DMA_TODEVICE, flags, 0); + if (!(flags & DMA_PREP_PQ_DISABLE_Q)) + ioat_unmap(pdev, pq->q_addr - offset, + len, PCI_DMA_TODEVICE, flags, 0); + break; + } + } + + if (!(flags & DMA_COMPL_SKIP_DEST_UNMAP)) { + if (!(flags & DMA_PREP_PQ_DISABLE_P)) + ioat_unmap(pdev, pq->p_addr - offset, len, + PCI_DMA_BIDIRECTIONAL, flags, 1); + if (!(flags & DMA_PREP_PQ_DISABLE_Q)) + ioat_unmap(pdev, pq->q_addr - offset, len, + PCI_DMA_BIDIRECTIONAL, flags, 1); + } + break; + } + default: + dev_err(&pdev->dev, "%s: unknown op type: %#x\n", + __func__, desc->hw->ctl_f.op); + } +} + +static bool desc_has_ext(struct ioat_ring_ent *desc) +{ + struct ioat_dma_descriptor *hw = desc->hw; + + if (hw->ctl_f.op == IOAT_OP_XOR || + hw->ctl_f.op == IOAT_OP_XOR_VAL) { + struct ioat_xor_descriptor *xor = desc->xor; + + if (src_cnt_to_sw(xor->ctl_f.src_cnt) > 5) + return true; + } else if (hw->ctl_f.op == IOAT_OP_PQ || + hw->ctl_f.op == IOAT_OP_PQ_VAL) { + struct ioat_pq_descriptor *pq = desc->pq; + + if (src_cnt_to_sw(pq->ctl_f.src_cnt) > 3) + return true; + } + + return false; +} + +/** + * __cleanup - reclaim used descriptors + * @ioat: channel (ring) to clean + * + * The difference from the dma_v2.c __cleanup() is that this routine + * handles extended descriptors and dma-unmapping raid operations. + */ +static void __cleanup(struct ioat2_dma_chan *ioat, unsigned long phys_complete) +{ + struct ioat_chan_common *chan = &ioat->base; + struct ioat_ring_ent *desc; + bool seen_current = false; + u16 active; + int i; + + dev_dbg(to_dev(chan), "%s: head: %#x tail: %#x issued: %#x\n", + __func__, ioat->head, ioat->tail, ioat->issued); + + active = ioat2_ring_active(ioat); + for (i = 0; i < active && !seen_current; i++) { + struct dma_async_tx_descriptor *tx; + + prefetch(ioat2_get_ring_ent(ioat, ioat->tail + i + 1)); + desc = ioat2_get_ring_ent(ioat, ioat->tail + i); + dump_desc_dbg(ioat, desc); + tx = &desc->txd; + if (tx->cookie) { + chan->completed_cookie = tx->cookie; + ioat3_dma_unmap(ioat, desc, ioat->tail + i); + tx->cookie = 0; + if (tx->callback) { + tx->callback(tx->callback_param); + tx->callback = NULL; + } + } + + if (tx->phys == phys_complete) + seen_current = true; + + /* skip extended descriptors */ + if (desc_has_ext(desc)) { + BUG_ON(i + 1 >= active); + i++; + } + } + ioat->tail += i; + BUG_ON(!seen_current); /* no active descs have written a completion? */ + chan->last_completion = phys_complete; + if (ioat->head == ioat->tail) { + dev_dbg(to_dev(chan), "%s: cancel completion timeout\n", + __func__); + clear_bit(IOAT_COMPLETION_PENDING, &chan->state); + mod_timer(&chan->timer, jiffies + IDLE_TIMEOUT); + } +} + +static void ioat3_cleanup(struct ioat2_dma_chan *ioat) +{ + struct ioat_chan_common *chan = &ioat->base; + unsigned long phys_complete; + + prefetch(chan->completion); + + if (!spin_trylock_bh(&chan->cleanup_lock)) + return; + + if (!ioat_cleanup_preamble(chan, &phys_complete)) { + spin_unlock_bh(&chan->cleanup_lock); + return; + } + + if (!spin_trylock_bh(&ioat->ring_lock)) { + spin_unlock_bh(&chan->cleanup_lock); + return; + } + + __cleanup(ioat, phys_complete); + + spin_unlock_bh(&ioat->ring_lock); + spin_unlock_bh(&chan->cleanup_lock); +} + +static void ioat3_cleanup_tasklet(unsigned long data) +{ + struct ioat2_dma_chan *ioat = (void *) data; + + ioat3_cleanup(ioat); + writew(IOAT_CHANCTRL_RUN | IOAT3_CHANCTRL_COMPL_DCA_EN, + ioat->base.reg_base + IOAT_CHANCTRL_OFFSET); +} + +static void ioat3_restart_channel(struct ioat2_dma_chan *ioat) +{ + struct ioat_chan_common *chan = &ioat->base; + unsigned long phys_complete; + u32 status; + + status = ioat_chansts(chan); + if (is_ioat_active(status) || is_ioat_idle(status)) + ioat_suspend(chan); + while (is_ioat_active(status) || is_ioat_idle(status)) { + status = ioat_chansts(chan); + cpu_relax(); + } + + if (ioat_cleanup_preamble(chan, &phys_complete)) + __cleanup(ioat, phys_complete); + + __ioat2_restart_chan(ioat); +} + +static void ioat3_timer_event(unsigned long data) +{ + struct ioat2_dma_chan *ioat = (void *) data; + struct ioat_chan_common *chan = &ioat->base; + + spin_lock_bh(&chan->cleanup_lock); + if (test_bit(IOAT_COMPLETION_PENDING, &chan->state)) { + unsigned long phys_complete; + u64 status; + + spin_lock_bh(&ioat->ring_lock); + status = ioat_chansts(chan); + + /* when halted due to errors check for channel + * programming errors before advancing the completion state + */ + if (is_ioat_halted(status)) { + u32 chanerr; + + chanerr = readl(chan->reg_base + IOAT_CHANERR_OFFSET); + dev_err(to_dev(chan), "%s: Channel halted (%x)\n", + __func__, chanerr); + BUG_ON(is_ioat_bug(chanerr)); + } + + /* if we haven't made progress and we have already + * acknowledged a pending completion once, then be more + * forceful with a restart + */ + if (ioat_cleanup_preamble(chan, &phys_complete)) + __cleanup(ioat, phys_complete); + else if (test_bit(IOAT_COMPLETION_ACK, &chan->state)) + ioat3_restart_channel(ioat); + else { + set_bit(IOAT_COMPLETION_ACK, &chan->state); + mod_timer(&chan->timer, jiffies + COMPLETION_TIMEOUT); + } + spin_unlock_bh(&ioat->ring_lock); + } else { + u16 active; + + /* if the ring is idle, empty, and oversized try to step + * down the size + */ + spin_lock_bh(&ioat->ring_lock); + active = ioat2_ring_active(ioat); + if (active == 0 && ioat->alloc_order > ioat_get_alloc_order()) + reshape_ring(ioat, ioat->alloc_order-1); + spin_unlock_bh(&ioat->ring_lock); + + /* keep shrinking until we get back to our minimum + * default size + */ + if (ioat->alloc_order > ioat_get_alloc_order()) + mod_timer(&chan->timer, jiffies + IDLE_TIMEOUT); + } + spin_unlock_bh(&chan->cleanup_lock); +} + +static enum dma_status +ioat3_is_complete(struct dma_chan *c, dma_cookie_t cookie, + dma_cookie_t *done, dma_cookie_t *used) +{ + struct ioat2_dma_chan *ioat = to_ioat2_chan(c); + + if (ioat_is_complete(c, cookie, done, used) == DMA_SUCCESS) + return DMA_SUCCESS; + + ioat3_cleanup(ioat); + + return ioat_is_complete(c, cookie, done, used); +} + +static struct dma_async_tx_descriptor * +ioat3_prep_memset_lock(struct dma_chan *c, dma_addr_t dest, int value, + size_t len, unsigned long flags) +{ + struct ioat2_dma_chan *ioat = to_ioat2_chan(c); + struct ioat_ring_ent *desc; + size_t total_len = len; + struct ioat_fill_descriptor *fill; + int num_descs; + u64 src_data = (0x0101010101010101ULL) * (value & 0xff); + u16 idx; + int i; + + num_descs = ioat2_xferlen_to_descs(ioat, len); + if (likely(num_descs) && + ioat2_alloc_and_lock(&idx, ioat, num_descs) == 0) + /* pass */; + else + return NULL; + i = 0; + do { + size_t xfer_size = min_t(size_t, len, 1 << ioat->xfercap_log); + + desc = ioat2_get_ring_ent(ioat, idx + i); + fill = desc->fill; + + fill->size = xfer_size; + fill->src_data = src_data; + fill->dst_addr = dest; + fill->ctl = 0; + fill->ctl_f.op = IOAT_OP_FILL; + + len -= xfer_size; + dest += xfer_size; + dump_desc_dbg(ioat, desc); + } while (++i < num_descs); + + desc->txd.flags = flags; + desc->len = total_len; + fill->ctl_f.int_en = !!(flags & DMA_PREP_INTERRUPT); + fill->ctl_f.fence = !!(flags & DMA_PREP_FENCE); + fill->ctl_f.compl_write = 1; + dump_desc_dbg(ioat, desc); + + /* we leave the channel locked to ensure in order submission */ + return &desc->txd; +} + +static struct dma_async_tx_descriptor * +__ioat3_prep_xor_lock(struct dma_chan *c, enum sum_check_flags *result, + dma_addr_t dest, dma_addr_t *src, unsigned int src_cnt, + size_t len, unsigned long flags) +{ + struct ioat2_dma_chan *ioat = to_ioat2_chan(c); + struct ioat_ring_ent *compl_desc; + struct ioat_ring_ent *desc; + struct ioat_ring_ent *ext; + size_t total_len = len; + struct ioat_xor_descriptor *xor; + struct ioat_xor_ext_descriptor *xor_ex = NULL; + struct ioat_dma_descriptor *hw; + u32 offset = 0; + int num_descs; + int with_ext; + int i; + u16 idx; + u8 op = result ? IOAT_OP_XOR_VAL : IOAT_OP_XOR; + + BUG_ON(src_cnt < 2); + + num_descs = ioat2_xferlen_to_descs(ioat, len); + /* we need 2x the number of descriptors to cover greater than 5 + * sources + */ + if (src_cnt > 5) { + with_ext = 1; + num_descs *= 2; + } else + with_ext = 0; + + /* completion writes from the raid engine may pass completion + * writes from the legacy engine, so we need one extra null + * (legacy) descriptor to ensure all completion writes arrive in + * order. + */ + if (likely(num_descs) && + ioat2_alloc_and_lock(&idx, ioat, num_descs+1) == 0) + /* pass */; + else + return NULL; + i = 0; + do { + struct ioat_raw_descriptor *descs[2]; + size_t xfer_size = min_t(size_t, len, 1 << ioat->xfercap_log); + int s; + + desc = ioat2_get_ring_ent(ioat, idx + i); + xor = desc->xor; + + /* save a branch by unconditionally retrieving the + * extended descriptor xor_set_src() knows to not write + * to it in the single descriptor case + */ + ext = ioat2_get_ring_ent(ioat, idx + i + 1); + xor_ex = ext->xor_ex; + + descs[0] = (struct ioat_raw_descriptor *) xor; + descs[1] = (struct ioat_raw_descriptor *) xor_ex; + for (s = 0; s < src_cnt; s++) + xor_set_src(descs, src[s], offset, s); + xor->size = xfer_size; + xor->dst_addr = dest + offset; + xor->ctl = 0; + xor->ctl_f.op = op; + xor->ctl_f.src_cnt = src_cnt_to_hw(src_cnt); + + len -= xfer_size; + offset += xfer_size; + dump_desc_dbg(ioat, desc); + } while ((i += 1 + with_ext) < num_descs); + + /* last xor descriptor carries the unmap parameters and fence bit */ + desc->txd.flags = flags; + desc->len = total_len; + if (result) + desc->result = result; + xor->ctl_f.fence = !!(flags & DMA_PREP_FENCE); + + /* completion descriptor carries interrupt bit */ + compl_desc = ioat2_get_ring_ent(ioat, idx + i); + compl_desc->txd.flags = flags & DMA_PREP_INTERRUPT; + hw = compl_desc->hw; + hw->ctl = 0; + hw->ctl_f.null = 1; + hw->ctl_f.int_en = !!(flags & DMA_PREP_INTERRUPT); + hw->ctl_f.compl_write = 1; + hw->size = NULL_DESC_BUFFER_SIZE; + dump_desc_dbg(ioat, compl_desc); + + /* we leave the channel locked to ensure in order submission */ + return &compl_desc->txd; +} + +static struct dma_async_tx_descriptor * +ioat3_prep_xor(struct dma_chan *chan, dma_addr_t dest, dma_addr_t *src, + unsigned int src_cnt, size_t len, unsigned long flags) +{ + return __ioat3_prep_xor_lock(chan, NULL, dest, src, src_cnt, len, flags); +} + +struct dma_async_tx_descriptor * +ioat3_prep_xor_val(struct dma_chan *chan, dma_addr_t *src, + unsigned int src_cnt, size_t len, + enum sum_check_flags *result, unsigned long flags) +{ + /* the cleanup routine only sets bits on validate failure, it + * does not clear bits on validate success... so clear it here + */ + *result = 0; + + return __ioat3_prep_xor_lock(chan, result, src[0], &src[1], + src_cnt - 1, len, flags); +} + +static void +dump_pq_desc_dbg(struct ioat2_dma_chan *ioat, struct ioat_ring_ent *desc, struct ioat_ring_ent *ext) +{ + struct device *dev = to_dev(&ioat->base); + struct ioat_pq_descriptor *pq = desc->pq; + struct ioat_pq_ext_descriptor *pq_ex = ext ? ext->pq_ex : NULL; + struct ioat_raw_descriptor *descs[] = { (void *) pq, (void *) pq_ex }; + int src_cnt = src_cnt_to_sw(pq->ctl_f.src_cnt); + int i; + + dev_dbg(dev, "desc[%d]: (%#llx->%#llx) flags: %#x" + " sz: %#x ctl: %#x (op: %d int: %d compl: %d pq: '%s%s' src_cnt: %d)\n", + desc_id(desc), (unsigned long long) desc->txd.phys, + (unsigned long long) (pq_ex ? pq_ex->next : pq->next), + desc->txd.flags, pq->size, pq->ctl, pq->ctl_f.op, pq->ctl_f.int_en, + pq->ctl_f.compl_write, + pq->ctl_f.p_disable ? "" : "p", pq->ctl_f.q_disable ? "" : "q", + pq->ctl_f.src_cnt); + for (i = 0; i < src_cnt; i++) + dev_dbg(dev, "\tsrc[%d]: %#llx coef: %#x\n", i, + (unsigned long long) pq_get_src(descs, i), pq->coef[i]); + dev_dbg(dev, "\tP: %#llx\n", pq->p_addr); + dev_dbg(dev, "\tQ: %#llx\n", pq->q_addr); +} + +static struct dma_async_tx_descriptor * +__ioat3_prep_pq_lock(struct dma_chan *c, enum sum_check_flags *result, + const dma_addr_t *dst, const dma_addr_t *src, + unsigned int src_cnt, const unsigned char *scf, + size_t len, unsigned long flags) +{ + struct ioat2_dma_chan *ioat = to_ioat2_chan(c); + struct ioat_chan_common *chan = &ioat->base; + struct ioat_ring_ent *compl_desc; + struct ioat_ring_ent *desc; + struct ioat_ring_ent *ext; + size_t total_len = len; + struct ioat_pq_descriptor *pq; + struct ioat_pq_ext_descriptor *pq_ex = NULL; + struct ioat_dma_descriptor *hw; + u32 offset = 0; + int num_descs; + int with_ext; + int i, s; + u16 idx; + u8 op = result ? IOAT_OP_PQ_VAL : IOAT_OP_PQ; + + dev_dbg(to_dev(chan), "%s\n", __func__); + /* the engine requires at least two sources (we provide + * at least 1 implied source in the DMA_PREP_CONTINUE case) + */ + BUG_ON(src_cnt + dmaf_continue(flags) < 2); + + num_descs = ioat2_xferlen_to_descs(ioat, len); + /* we need 2x the number of descriptors to cover greater than 3 + * sources + */ + if (src_cnt > 3 || flags & DMA_PREP_CONTINUE) { + with_ext = 1; + num_descs *= 2; + } else + with_ext = 0; + + /* completion writes from the raid engine may pass completion + * writes from the legacy engine, so we need one extra null + * (legacy) descriptor to ensure all completion writes arrive in + * order. + */ + if (likely(num_descs) && + ioat2_alloc_and_lock(&idx, ioat, num_descs+1) == 0) + /* pass */; + else + return NULL; + i = 0; + do { + struct ioat_raw_descriptor *descs[2]; + size_t xfer_size = min_t(size_t, len, 1 << ioat->xfercap_log); + + desc = ioat2_get_ring_ent(ioat, idx + i); + pq = desc->pq; + + /* save a branch by unconditionally retrieving the + * extended descriptor pq_set_src() knows to not write + * to it in the single descriptor case + */ + ext = ioat2_get_ring_ent(ioat, idx + i + with_ext); + pq_ex = ext->pq_ex; + + descs[0] = (struct ioat_raw_descriptor *) pq; + descs[1] = (struct ioat_raw_descriptor *) pq_ex; + + for (s = 0; s < src_cnt; s++) + pq_set_src(descs, src[s], offset, scf[s], s); + + /* see the comment for dma_maxpq in include/linux/dmaengine.h */ + if (dmaf_p_disabled_continue(flags)) + pq_set_src(descs, dst[1], offset, 1, s++); + else if (dmaf_continue(flags)) { + pq_set_src(descs, dst[0], offset, 0, s++); + pq_set_src(descs, dst[1], offset, 1, s++); + pq_set_src(descs, dst[1], offset, 0, s++); + } + pq->size = xfer_size; + pq->p_addr = dst[0] + offset; + pq->q_addr = dst[1] + offset; + pq->ctl = 0; + pq->ctl_f.op = op; + pq->ctl_f.src_cnt = src_cnt_to_hw(s); + pq->ctl_f.p_disable = !!(flags & DMA_PREP_PQ_DISABLE_P); + pq->ctl_f.q_disable = !!(flags & DMA_PREP_PQ_DISABLE_Q); + + len -= xfer_size; + offset += xfer_size; + } while ((i += 1 + with_ext) < num_descs); + + /* last pq descriptor carries the unmap parameters and fence bit */ + desc->txd.flags = flags; + desc->len = total_len; + if (result) + desc->result = result; + pq->ctl_f.fence = !!(flags & DMA_PREP_FENCE); + dump_pq_desc_dbg(ioat, desc, ext); + + /* completion descriptor carries interrupt bit */ + compl_desc = ioat2_get_ring_ent(ioat, idx + i); + compl_desc->txd.flags = flags & DMA_PREP_INTERRUPT; + hw = compl_desc->hw; + hw->ctl = 0; + hw->ctl_f.null = 1; + hw->ctl_f.int_en = !!(flags & DMA_PREP_INTERRUPT); + hw->ctl_f.compl_write = 1; + hw->size = NULL_DESC_BUFFER_SIZE; + dump_desc_dbg(ioat, compl_desc); + + /* we leave the channel locked to ensure in order submission */ + return &compl_desc->txd; +} + +static struct dma_async_tx_descriptor * +ioat3_prep_pq(struct dma_chan *chan, dma_addr_t *dst, dma_addr_t *src, + unsigned int src_cnt, const unsigned char *scf, size_t len, + unsigned long flags) +{ + /* specify valid address for disabled result */ + if (flags & DMA_PREP_PQ_DISABLE_P) + dst[0] = dst[1]; + if (flags & DMA_PREP_PQ_DISABLE_Q) + dst[1] = dst[0]; + + /* handle the single source multiply case from the raid6 + * recovery path + */ + if ((flags & DMA_PREP_PQ_DISABLE_P) && src_cnt == 1) { + dma_addr_t single_source[2]; + unsigned char single_source_coef[2]; + + BUG_ON(flags & DMA_PREP_PQ_DISABLE_Q); + single_source[0] = src[0]; + single_source[1] = src[0]; + single_source_coef[0] = scf[0]; + single_source_coef[1] = 0; + + return __ioat3_prep_pq_lock(chan, NULL, dst, single_source, 2, + single_source_coef, len, flags); + } else + return __ioat3_prep_pq_lock(chan, NULL, dst, src, src_cnt, scf, + len, flags); +} + +struct dma_async_tx_descriptor * +ioat3_prep_pq_val(struct dma_chan *chan, dma_addr_t *pq, dma_addr_t *src, + unsigned int src_cnt, const unsigned char *scf, size_t len, + enum sum_check_flags *pqres, unsigned long flags) +{ + /* specify valid address for disabled result */ + if (flags & DMA_PREP_PQ_DISABLE_P) + pq[0] = pq[1]; + if (flags & DMA_PREP_PQ_DISABLE_Q) + pq[1] = pq[0]; + + /* the cleanup routine only sets bits on validate failure, it + * does not clear bits on validate success... so clear it here + */ + *pqres = 0; + + return __ioat3_prep_pq_lock(chan, pqres, pq, src, src_cnt, scf, len, + flags); +} + +static struct dma_async_tx_descriptor * +ioat3_prep_pqxor(struct dma_chan *chan, dma_addr_t dst, dma_addr_t *src, + unsigned int src_cnt, size_t len, unsigned long flags) +{ + unsigned char scf[src_cnt]; + dma_addr_t pq[2]; + + memset(scf, 0, src_cnt); + pq[0] = dst; + flags |= DMA_PREP_PQ_DISABLE_Q; + pq[1] = dst; /* specify valid address for disabled result */ + + return __ioat3_prep_pq_lock(chan, NULL, pq, src, src_cnt, scf, len, + flags); +} + +struct dma_async_tx_descriptor * +ioat3_prep_pqxor_val(struct dma_chan *chan, dma_addr_t *src, + unsigned int src_cnt, size_t len, + enum sum_check_flags *result, unsigned long flags) +{ + unsigned char scf[src_cnt]; + dma_addr_t pq[2]; + + /* the cleanup routine only sets bits on validate failure, it + * does not clear bits on validate success... so clear it here + */ + *result = 0; + + memset(scf, 0, src_cnt); + pq[0] = src[0]; + flags |= DMA_PREP_PQ_DISABLE_Q; + pq[1] = pq[0]; /* specify valid address for disabled result */ + + return __ioat3_prep_pq_lock(chan, result, pq, &src[1], src_cnt - 1, scf, + len, flags); +} + +static struct dma_async_tx_descriptor * +ioat3_prep_interrupt_lock(struct dma_chan *c, unsigned long flags) +{ + struct ioat2_dma_chan *ioat = to_ioat2_chan(c); + struct ioat_ring_ent *desc; + struct ioat_dma_descriptor *hw; + u16 idx; + + if (ioat2_alloc_and_lock(&idx, ioat, 1) == 0) + desc = ioat2_get_ring_ent(ioat, idx); + else + return NULL; + + hw = desc->hw; + hw->ctl = 0; + hw->ctl_f.null = 1; + hw->ctl_f.int_en = 1; + hw->ctl_f.fence = !!(flags & DMA_PREP_FENCE); + hw->ctl_f.compl_write = 1; + hw->size = NULL_DESC_BUFFER_SIZE; + hw->src_addr = 0; + hw->dst_addr = 0; + + desc->txd.flags = flags; + desc->len = 1; + + dump_desc_dbg(ioat, desc); + + /* we leave the channel locked to ensure in order submission */ + return &desc->txd; +} + +static void __devinit ioat3_dma_test_callback(void *dma_async_param) +{ + struct completion *cmp = dma_async_param; + + complete(cmp); +} + +#define IOAT_NUM_SRC_TEST 6 /* must be <= 8 */ +static int __devinit ioat_xor_val_self_test(struct ioatdma_device *device) +{ + int i, src_idx; + struct page *dest; + struct page *xor_srcs[IOAT_NUM_SRC_TEST]; + struct page *xor_val_srcs[IOAT_NUM_SRC_TEST + 1]; + dma_addr_t dma_srcs[IOAT_NUM_SRC_TEST + 1]; + dma_addr_t dma_addr, dest_dma; + struct dma_async_tx_descriptor *tx; + struct dma_chan *dma_chan; + dma_cookie_t cookie; + u8 cmp_byte = 0; + u32 cmp_word; + u32 xor_val_result; + int err = 0; + struct completion cmp; + unsigned long tmo; + struct device *dev = &device->pdev->dev; + struct dma_device *dma = &device->common; + + dev_dbg(dev, "%s\n", __func__); + + if (!dma_has_cap(DMA_XOR, dma->cap_mask)) + return 0; + + for (src_idx = 0; src_idx < IOAT_NUM_SRC_TEST; src_idx++) { + xor_srcs[src_idx] = alloc_page(GFP_KERNEL); + if (!xor_srcs[src_idx]) { + while (src_idx--) + __free_page(xor_srcs[src_idx]); + return -ENOMEM; + } + } + + dest = alloc_page(GFP_KERNEL); + if (!dest) { + while (src_idx--) + __free_page(xor_srcs[src_idx]); + return -ENOMEM; + } + + /* Fill in src buffers */ + for (src_idx = 0; src_idx < IOAT_NUM_SRC_TEST; src_idx++) { + u8 *ptr = page_address(xor_srcs[src_idx]); + for (i = 0; i < PAGE_SIZE; i++) + ptr[i] = (1 << src_idx); + } + + for (src_idx = 0; src_idx < IOAT_NUM_SRC_TEST; src_idx++) + cmp_byte ^= (u8) (1 << src_idx); + + cmp_word = (cmp_byte << 24) | (cmp_byte << 16) | + (cmp_byte << 8) | cmp_byte; + + memset(page_address(dest), 0, PAGE_SIZE); + + dma_chan = container_of(dma->channels.next, struct dma_chan, + device_node); + if (dma->device_alloc_chan_resources(dma_chan) < 1) { + err = -ENODEV; + goto out; + } + + /* test xor */ + dest_dma = dma_map_page(dev, dest, 0, PAGE_SIZE, DMA_FROM_DEVICE); + for (i = 0; i < IOAT_NUM_SRC_TEST; i++) + dma_srcs[i] = dma_map_page(dev, xor_srcs[i], 0, PAGE_SIZE, + DMA_TO_DEVICE); + tx = dma->device_prep_dma_xor(dma_chan, dest_dma, dma_srcs, + IOAT_NUM_SRC_TEST, PAGE_SIZE, + DMA_PREP_INTERRUPT); + + if (!tx) { + dev_err(dev, "Self-test xor prep failed\n"); + err = -ENODEV; + goto free_resources; + } + + async_tx_ack(tx); + init_completion(&cmp); + tx->callback = ioat3_dma_test_callback; + tx->callback_param = &cmp; + cookie = tx->tx_submit(tx); + if (cookie < 0) { + dev_err(dev, "Self-test xor setup failed\n"); + err = -ENODEV; + goto free_resources; + } + dma->device_issue_pending(dma_chan); + + tmo = wait_for_completion_timeout(&cmp, msecs_to_jiffies(3000)); + + if (dma->device_is_tx_complete(dma_chan, cookie, NULL, NULL) != DMA_SUCCESS) { + dev_err(dev, "Self-test xor timed out\n"); + err = -ENODEV; + goto free_resources; + } + + dma_sync_single_for_cpu(dev, dest_dma, PAGE_SIZE, DMA_FROM_DEVICE); + for (i = 0; i < (PAGE_SIZE / sizeof(u32)); i++) { + u32 *ptr = page_address(dest); + if (ptr[i] != cmp_word) { + dev_err(dev, "Self-test xor failed compare\n"); + err = -ENODEV; + goto free_resources; + } + } + dma_sync_single_for_device(dev, dest_dma, PAGE_SIZE, DMA_TO_DEVICE); + + /* skip validate if the capability is not present */ + if (!dma_has_cap(DMA_XOR_VAL, dma_chan->device->cap_mask)) + goto free_resources; + + /* validate the sources with the destintation page */ + for (i = 0; i < IOAT_NUM_SRC_TEST; i++) + xor_val_srcs[i] = xor_srcs[i]; + xor_val_srcs[i] = dest; + + xor_val_result = 1; + + for (i = 0; i < IOAT_NUM_SRC_TEST + 1; i++) + dma_srcs[i] = dma_map_page(dev, xor_val_srcs[i], 0, PAGE_SIZE, + DMA_TO_DEVICE); + tx = dma->device_prep_dma_xor_val(dma_chan, dma_srcs, + IOAT_NUM_SRC_TEST + 1, PAGE_SIZE, + &xor_val_result, DMA_PREP_INTERRUPT); + if (!tx) { + dev_err(dev, "Self-test zero prep failed\n"); + err = -ENODEV; + goto free_resources; + } + + async_tx_ack(tx); + init_completion(&cmp); + tx->callback = ioat3_dma_test_callback; + tx->callback_param = &cmp; + cookie = tx->tx_submit(tx); + if (cookie < 0) { + dev_err(dev, "Self-test zero setup failed\n"); + err = -ENODEV; + goto free_resources; + } + dma->device_issue_pending(dma_chan); + + tmo = wait_for_completion_timeout(&cmp, msecs_to_jiffies(3000)); + + if (dma->device_is_tx_complete(dma_chan, cookie, NULL, NULL) != DMA_SUCCESS) { + dev_err(dev, "Self-test validate timed out\n"); + err = -ENODEV; + goto free_resources; + } + + if (xor_val_result != 0) { + dev_err(dev, "Self-test validate failed compare\n"); + err = -ENODEV; + goto free_resources; + } + + /* skip memset if the capability is not present */ + if (!dma_has_cap(DMA_MEMSET, dma_chan->device->cap_mask)) + goto free_resources; + + /* test memset */ + dma_addr = dma_map_page(dev, dest, 0, + PAGE_SIZE, DMA_FROM_DEVICE); + tx = dma->device_prep_dma_memset(dma_chan, dma_addr, 0, PAGE_SIZE, + DMA_PREP_INTERRUPT); + if (!tx) { + dev_err(dev, "Self-test memset prep failed\n"); + err = -ENODEV; + goto free_resources; + } + + async_tx_ack(tx); + init_completion(&cmp); + tx->callback = ioat3_dma_test_callback; + tx->callback_param = &cmp; + cookie = tx->tx_submit(tx); + if (cookie < 0) { + dev_err(dev, "Self-test memset setup failed\n"); + err = -ENODEV; + goto free_resources; + } + dma->device_issue_pending(dma_chan); + + tmo = wait_for_completion_timeout(&cmp, msecs_to_jiffies(3000)); + + if (dma->device_is_tx_complete(dma_chan, cookie, NULL, NULL) != DMA_SUCCESS) { + dev_err(dev, "Self-test memset timed out\n"); + err = -ENODEV; + goto free_resources; + } + + for (i = 0; i < PAGE_SIZE/sizeof(u32); i++) { + u32 *ptr = page_address(dest); + if (ptr[i]) { + dev_err(dev, "Self-test memset failed compare\n"); + err = -ENODEV; + goto free_resources; + } + } + + /* test for non-zero parity sum */ + xor_val_result = 0; + for (i = 0; i < IOAT_NUM_SRC_TEST + 1; i++) + dma_srcs[i] = dma_map_page(dev, xor_val_srcs[i], 0, PAGE_SIZE, + DMA_TO_DEVICE); + tx = dma->device_prep_dma_xor_val(dma_chan, dma_srcs, + IOAT_NUM_SRC_TEST + 1, PAGE_SIZE, + &xor_val_result, DMA_PREP_INTERRUPT); + if (!tx) { + dev_err(dev, "Self-test 2nd zero prep failed\n"); + err = -ENODEV; + goto free_resources; + } + + async_tx_ack(tx); + init_completion(&cmp); + tx->callback = ioat3_dma_test_callback; + tx->callback_param = &cmp; + cookie = tx->tx_submit(tx); + if (cookie < 0) { + dev_err(dev, "Self-test 2nd zero setup failed\n"); + err = -ENODEV; + goto free_resources; + } + dma->device_issue_pending(dma_chan); + + tmo = wait_for_completion_timeout(&cmp, msecs_to_jiffies(3000)); + + if (dma->device_is_tx_complete(dma_chan, cookie, NULL, NULL) != DMA_SUCCESS) { + dev_err(dev, "Self-test 2nd validate timed out\n"); + err = -ENODEV; + goto free_resources; + } + + if (xor_val_result != SUM_CHECK_P_RESULT) { + dev_err(dev, "Self-test validate failed compare\n"); + err = -ENODEV; + goto free_resources; + } + +free_resources: + dma->device_free_chan_resources(dma_chan); +out: + src_idx = IOAT_NUM_SRC_TEST; + while (src_idx--) + __free_page(xor_srcs[src_idx]); + __free_page(dest); + return err; +} + +static int __devinit ioat3_dma_self_test(struct ioatdma_device *device) +{ + int rc = ioat_dma_self_test(device); + + if (rc) + return rc; + + rc = ioat_xor_val_self_test(device); + if (rc) + return rc; + + return 0; +} + +int __devinit ioat3_dma_probe(struct ioatdma_device *device, int dca) +{ + struct pci_dev *pdev = device->pdev; + int dca_en = system_has_dca_enabled(pdev); + struct dma_device *dma; + struct dma_chan *c; + struct ioat_chan_common *chan; + bool is_raid_device = false; + int err; + u16 dev_id; + u32 cap; + + device->enumerate_channels = ioat2_enumerate_channels; + device->self_test = ioat3_dma_self_test; + dma = &device->common; + dma->device_prep_dma_memcpy = ioat2_dma_prep_memcpy_lock; + dma->device_issue_pending = ioat2_issue_pending; + dma->device_alloc_chan_resources = ioat2_alloc_chan_resources; + dma->device_free_chan_resources = ioat2_free_chan_resources; + + dma_cap_set(DMA_INTERRUPT, dma->cap_mask); + dma->device_prep_dma_interrupt = ioat3_prep_interrupt_lock; + + cap = readl(device->reg_base + IOAT_DMA_CAP_OFFSET); + + /* dca is incompatible with raid operations */ + if (dca_en && (cap & (IOAT_CAP_XOR|IOAT_CAP_PQ))) + cap &= ~(IOAT_CAP_XOR|IOAT_CAP_PQ); + + if (cap & IOAT_CAP_XOR) { + is_raid_device = true; + dma->max_xor = 8; + dma->xor_align = 2; + + dma_cap_set(DMA_XOR, dma->cap_mask); + dma->device_prep_dma_xor = ioat3_prep_xor; + + dma_cap_set(DMA_XOR_VAL, dma->cap_mask); + dma->device_prep_dma_xor_val = ioat3_prep_xor_val; + } + if (cap & IOAT_CAP_PQ) { + is_raid_device = true; + dma_set_maxpq(dma, 8, 0); + dma->pq_align = 2; + + dma_cap_set(DMA_PQ, dma->cap_mask); + dma->device_prep_dma_pq = ioat3_prep_pq; + + dma_cap_set(DMA_PQ_VAL, dma->cap_mask); + dma->device_prep_dma_pq_val = ioat3_prep_pq_val; + + if (!(cap & IOAT_CAP_XOR)) { + dma->max_xor = 8; + dma->xor_align = 2; + + dma_cap_set(DMA_XOR, dma->cap_mask); + dma->device_prep_dma_xor = ioat3_prep_pqxor; + + dma_cap_set(DMA_XOR_VAL, dma->cap_mask); + dma->device_prep_dma_xor_val = ioat3_prep_pqxor_val; + } + } + if (is_raid_device && (cap & IOAT_CAP_FILL_BLOCK)) { + dma_cap_set(DMA_MEMSET, dma->cap_mask); + dma->device_prep_dma_memset = ioat3_prep_memset_lock; + } + + + if (is_raid_device) { + dma->device_is_tx_complete = ioat3_is_complete; + device->cleanup_tasklet = ioat3_cleanup_tasklet; + device->timer_fn = ioat3_timer_event; + } else { + dma->device_is_tx_complete = ioat2_is_complete; + device->cleanup_tasklet = ioat2_cleanup_tasklet; + device->timer_fn = ioat2_timer_event; + } + + #ifdef CONFIG_ASYNC_TX_DISABLE_PQ_VAL_DMA + dma_cap_clear(DMA_PQ_VAL, dma->cap_mask); + dma->device_prep_dma_pq_val = NULL; + #endif + + #ifdef CONFIG_ASYNC_TX_DISABLE_XOR_VAL_DMA + dma_cap_clear(DMA_XOR_VAL, dma->cap_mask); + dma->device_prep_dma_xor_val = NULL; + #endif + + /* -= IOAT ver.3 workarounds =- */ + /* Write CHANERRMSK_INT with 3E07h to mask out the errors + * that can cause stability issues for IOAT ver.3 + */ + pci_write_config_dword(pdev, IOAT_PCI_CHANERRMASK_INT_OFFSET, 0x3e07); + + /* Clear DMAUNCERRSTS Cfg-Reg Parity Error status bit + * (workaround for spurious config parity error after restart) + */ + pci_read_config_word(pdev, IOAT_PCI_DEVICE_ID_OFFSET, &dev_id); + if (dev_id == PCI_DEVICE_ID_INTEL_IOAT_TBG0) + pci_write_config_dword(pdev, IOAT_PCI_DMAUNCERRSTS_OFFSET, 0x10); + + err = ioat_probe(device); + if (err) + return err; + ioat_set_tcp_copy_break(262144); + + list_for_each_entry(c, &dma->channels, device_node) { + chan = to_chan_common(c); + writel(IOAT_DMA_DCA_ANY_CPU, + chan->reg_base + IOAT_DCACTRL_OFFSET); + } + + err = ioat_register(device); + if (err) + return err; + + ioat_kobject_add(device, &ioat2_ktype); + + if (dca) + device->dca = ioat3_dca_init(pdev, device->reg_base); + + return 0; +} diff --git a/drivers/dma/ioat/hw.h b/drivers/dma/ioat/hw.h new file mode 100644 index 000000000000..60e675455b6a --- /dev/null +++ b/drivers/dma/ioat/hw.h @@ -0,0 +1,217 @@ +/* + * Copyright(c) 2004 - 2009 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 59 + * Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * The full GNU General Public License is included in this distribution in the + * file called COPYING. + */ +#ifndef _IOAT_HW_H_ +#define _IOAT_HW_H_ + +/* PCI Configuration Space Values */ +#define IOAT_PCI_VID 0x8086 +#define IOAT_MMIO_BAR 0 + +/* CB device ID's */ +#define IOAT_PCI_DID_5000 0x1A38 +#define IOAT_PCI_DID_CNB 0x360B +#define IOAT_PCI_DID_SCNB 0x65FF +#define IOAT_PCI_DID_SNB 0x402F + +#define IOAT_PCI_RID 0x00 +#define IOAT_PCI_SVID 0x8086 +#define IOAT_PCI_SID 0x8086 +#define IOAT_VER_1_2 0x12 /* Version 1.2 */ +#define IOAT_VER_2_0 0x20 /* Version 2.0 */ +#define IOAT_VER_3_0 0x30 /* Version 3.0 */ +#define IOAT_VER_3_2 0x32 /* Version 3.2 */ + +int system_has_dca_enabled(struct pci_dev *pdev); + +struct ioat_dma_descriptor { + uint32_t size; + union { + uint32_t ctl; + struct { + unsigned int int_en:1; + unsigned int src_snoop_dis:1; + unsigned int dest_snoop_dis:1; + unsigned int compl_write:1; + unsigned int fence:1; + unsigned int null:1; + unsigned int src_brk:1; + unsigned int dest_brk:1; + unsigned int bundle:1; + unsigned int dest_dca:1; + unsigned int hint:1; + unsigned int rsvd2:13; + #define IOAT_OP_COPY 0x00 + unsigned int op:8; + } ctl_f; + }; + uint64_t src_addr; + uint64_t dst_addr; + uint64_t next; + uint64_t rsv1; + uint64_t rsv2; + /* store some driver data in an unused portion of the descriptor */ + union { + uint64_t user1; + uint64_t tx_cnt; + }; + uint64_t user2; +}; + +struct ioat_fill_descriptor { + uint32_t size; + union { + uint32_t ctl; + struct { + unsigned int int_en:1; + unsigned int rsvd:1; + unsigned int dest_snoop_dis:1; + unsigned int compl_write:1; + unsigned int fence:1; + unsigned int rsvd2:2; + unsigned int dest_brk:1; + unsigned int bundle:1; + unsigned int rsvd4:15; + #define IOAT_OP_FILL 0x01 + unsigned int op:8; + } ctl_f; + }; + uint64_t src_data; + uint64_t dst_addr; + uint64_t next; + uint64_t rsv1; + uint64_t next_dst_addr; + uint64_t user1; + uint64_t user2; +}; + +struct ioat_xor_descriptor { + uint32_t size; + union { + uint32_t ctl; + struct { + unsigned int int_en:1; + unsigned int src_snoop_dis:1; + unsigned int dest_snoop_dis:1; + unsigned int compl_write:1; + unsigned int fence:1; + unsigned int src_cnt:3; + unsigned int bundle:1; + unsigned int dest_dca:1; + unsigned int hint:1; + unsigned int rsvd:13; + #define IOAT_OP_XOR 0x87 + #define IOAT_OP_XOR_VAL 0x88 + unsigned int op:8; + } ctl_f; + }; + uint64_t src_addr; + uint64_t dst_addr; + uint64_t next; + uint64_t src_addr2; + uint64_t src_addr3; + uint64_t src_addr4; + uint64_t src_addr5; +}; + +struct ioat_xor_ext_descriptor { + uint64_t src_addr6; + uint64_t src_addr7; + uint64_t src_addr8; + uint64_t next; + uint64_t rsvd[4]; +}; + +struct ioat_pq_descriptor { + uint32_t size; + union { + uint32_t ctl; + struct { + unsigned int int_en:1; + unsigned int src_snoop_dis:1; + unsigned int dest_snoop_dis:1; + unsigned int compl_write:1; + unsigned int fence:1; + unsigned int src_cnt:3; + unsigned int bundle:1; + unsigned int dest_dca:1; + unsigned int hint:1; + unsigned int p_disable:1; + unsigned int q_disable:1; + unsigned int rsvd:11; + #define IOAT_OP_PQ 0x89 + #define IOAT_OP_PQ_VAL 0x8a + unsigned int op:8; + } ctl_f; + }; + uint64_t src_addr; + uint64_t p_addr; + uint64_t next; + uint64_t src_addr2; + uint64_t src_addr3; + uint8_t coef[8]; + uint64_t q_addr; +}; + +struct ioat_pq_ext_descriptor { + uint64_t src_addr4; + uint64_t src_addr5; + uint64_t src_addr6; + uint64_t next; + uint64_t src_addr7; + uint64_t src_addr8; + uint64_t rsvd[2]; +}; + +struct ioat_pq_update_descriptor { + uint32_t size; + union { + uint32_t ctl; + struct { + unsigned int int_en:1; + unsigned int src_snoop_dis:1; + unsigned int dest_snoop_dis:1; + unsigned int compl_write:1; + unsigned int fence:1; + unsigned int src_cnt:3; + unsigned int bundle:1; + unsigned int dest_dca:1; + unsigned int hint:1; + unsigned int p_disable:1; + unsigned int q_disable:1; + unsigned int rsvd:3; + unsigned int coef:8; + #define IOAT_OP_PQ_UP 0x8b + unsigned int op:8; + } ctl_f; + }; + uint64_t src_addr; + uint64_t p_addr; + uint64_t next; + uint64_t src_addr2; + uint64_t p_src; + uint64_t q_src; + uint64_t q_addr; +}; + +struct ioat_raw_descriptor { + uint64_t field[8]; +}; +#endif diff --git a/drivers/dma/ioat/pci.c b/drivers/dma/ioat/pci.c new file mode 100644 index 000000000000..d545fae30f37 --- /dev/null +++ b/drivers/dma/ioat/pci.c @@ -0,0 +1,210 @@ +/* + * Intel I/OAT DMA Linux driver + * Copyright(c) 2007 - 2009 Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + * + * The full GNU General Public License is included in this distribution in + * the file called "COPYING". + * + */ + +/* + * This driver supports an Intel I/OAT DMA engine, which does asynchronous + * copy operations. + */ + +#include <linux/init.h> +#include <linux/module.h> +#include <linux/pci.h> +#include <linux/interrupt.h> +#include <linux/dca.h> +#include "dma.h" +#include "dma_v2.h" +#include "registers.h" +#include "hw.h" + +MODULE_VERSION(IOAT_DMA_VERSION); +MODULE_LICENSE("Dual BSD/GPL"); +MODULE_AUTHOR("Intel Corporation"); + +static struct pci_device_id ioat_pci_tbl[] = { + /* I/OAT v1 platforms */ + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IOAT) }, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IOAT_CNB) }, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IOAT_SCNB) }, + { PCI_VDEVICE(UNISYS, PCI_DEVICE_ID_UNISYS_DMA_DIRECTOR) }, + + /* I/OAT v2 platforms */ + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IOAT_SNB) }, + + /* I/OAT v3 platforms */ + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IOAT_TBG0) }, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IOAT_TBG1) }, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IOAT_TBG2) }, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IOAT_TBG3) }, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IOAT_TBG4) }, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IOAT_TBG5) }, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IOAT_TBG6) }, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IOAT_TBG7) }, + + /* I/OAT v3.2 platforms */ + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IOAT_JSF0) }, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IOAT_JSF1) }, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IOAT_JSF2) }, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IOAT_JSF3) }, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IOAT_JSF4) }, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IOAT_JSF5) }, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IOAT_JSF6) }, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IOAT_JSF7) }, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IOAT_JSF8) }, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IOAT_JSF9) }, + + { 0, } +}; +MODULE_DEVICE_TABLE(pci, ioat_pci_tbl); + +static int __devinit ioat_pci_probe(struct pci_dev *pdev, + const struct pci_device_id *id); +static void __devexit ioat_remove(struct pci_dev *pdev); + +static int ioat_dca_enabled = 1; +module_param(ioat_dca_enabled, int, 0644); +MODULE_PARM_DESC(ioat_dca_enabled, "control support of dca service (default: 1)"); + +struct kmem_cache *ioat2_cache; + +#define DRV_NAME "ioatdma" + +static struct pci_driver ioat_pci_driver = { + .name = DRV_NAME, + .id_table = ioat_pci_tbl, + .probe = ioat_pci_probe, + .remove = __devexit_p(ioat_remove), +}; + +static struct ioatdma_device * +alloc_ioatdma(struct pci_dev *pdev, void __iomem *iobase) +{ + struct device *dev = &pdev->dev; + struct ioatdma_device *d = devm_kzalloc(dev, sizeof(*d), GFP_KERNEL); + + if (!d) + return NULL; + d->pdev = pdev; + d->reg_base = iobase; + return d; +} + +static int __devinit ioat_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) +{ + void __iomem * const *iomap; + struct device *dev = &pdev->dev; + struct ioatdma_device *device; + int err; + + err = pcim_enable_device(pdev); + if (err) + return err; + + err = pcim_iomap_regions(pdev, 1 << IOAT_MMIO_BAR, DRV_NAME); + if (err) + return err; + iomap = pcim_iomap_table(pdev); + if (!iomap) + return -ENOMEM; + + err = pci_set_dma_mask(pdev, DMA_BIT_MASK(64)); + if (err) + err = pci_set_dma_mask(pdev, DMA_BIT_MASK(32)); + if (err) + return err; + + err = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(64)); + if (err) + err = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32)); + if (err) + return err; + + device = devm_kzalloc(dev, sizeof(*device), GFP_KERNEL); + if (!device) + return -ENOMEM; + + pci_set_master(pdev); + + device = alloc_ioatdma(pdev, iomap[IOAT_MMIO_BAR]); + if (!device) + return -ENOMEM; + pci_set_drvdata(pdev, device); + + device->version = readb(device->reg_base + IOAT_VER_OFFSET); + if (device->version == IOAT_VER_1_2) + err = ioat1_dma_probe(device, ioat_dca_enabled); + else if (device->version == IOAT_VER_2_0) + err = ioat2_dma_probe(device, ioat_dca_enabled); + else if (device->version >= IOAT_VER_3_0) + err = ioat3_dma_probe(device, ioat_dca_enabled); + else + return -ENODEV; + + if (err) { + dev_err(dev, "Intel(R) I/OAT DMA Engine init failed\n"); + return -ENODEV; + } + + return 0; +} + +static void __devexit ioat_remove(struct pci_dev *pdev) +{ + struct ioatdma_device *device = pci_get_drvdata(pdev); + + if (!device) + return; + + dev_err(&pdev->dev, "Removing dma and dca services\n"); + if (device->dca) { + unregister_dca_provider(device->dca, &pdev->dev); + free_dca_provider(device->dca); + device->dca = NULL; + } + ioat_dma_remove(device); +} + +static int __init ioat_init_module(void) +{ + int err; + + pr_info("%s: Intel(R) QuickData Technology Driver %s\n", + DRV_NAME, IOAT_DMA_VERSION); + + ioat2_cache = kmem_cache_create("ioat2", sizeof(struct ioat_ring_ent), + 0, SLAB_HWCACHE_ALIGN, NULL); + if (!ioat2_cache) + return -ENOMEM; + + err = pci_register_driver(&ioat_pci_driver); + if (err) + kmem_cache_destroy(ioat2_cache); + + return err; +} +module_init(ioat_init_module); + +static void __exit ioat_exit_module(void) +{ + pci_unregister_driver(&ioat_pci_driver); + kmem_cache_destroy(ioat2_cache); +} +module_exit(ioat_exit_module); diff --git a/drivers/dma/ioatdma_registers.h b/drivers/dma/ioat/registers.h index 49bc277424f8..f015ec196700 100644 --- a/drivers/dma/ioatdma_registers.h +++ b/drivers/dma/ioat/registers.h @@ -64,18 +64,35 @@ #define IOAT_DEVICE_STATUS_OFFSET 0x0E /* 16-bit */ #define IOAT_DEVICE_STATUS_DEGRADED_MODE 0x0001 +#define IOAT_DEVICE_MMIO_RESTRICTED 0x0002 +#define IOAT_DEVICE_MEMORY_BYPASS 0x0004 +#define IOAT_DEVICE_ADDRESS_REMAPPING 0x0008 + +#define IOAT_DMA_CAP_OFFSET 0x10 /* 32-bit */ +#define IOAT_CAP_PAGE_BREAK 0x00000001 +#define IOAT_CAP_CRC 0x00000002 +#define IOAT_CAP_SKIP_MARKER 0x00000004 +#define IOAT_CAP_DCA 0x00000010 +#define IOAT_CAP_CRC_MOVE 0x00000020 +#define IOAT_CAP_FILL_BLOCK 0x00000040 +#define IOAT_CAP_APIC 0x00000080 +#define IOAT_CAP_XOR 0x00000100 +#define IOAT_CAP_PQ 0x00000200 #define IOAT_CHANNEL_MMIO_SIZE 0x80 /* Each Channel MMIO space is this size */ /* DMA Channel Registers */ #define IOAT_CHANCTRL_OFFSET 0x00 /* 16-bit Channel Control Register */ #define IOAT_CHANCTRL_CHANNEL_PRIORITY_MASK 0xF000 +#define IOAT3_CHANCTRL_COMPL_DCA_EN 0x0200 #define IOAT_CHANCTRL_CHANNEL_IN_USE 0x0100 #define IOAT_CHANCTRL_DESCRIPTOR_ADDR_SNOOP_CONTROL 0x0020 #define IOAT_CHANCTRL_ERR_INT_EN 0x0010 #define IOAT_CHANCTRL_ANY_ERR_ABORT_EN 0x0008 #define IOAT_CHANCTRL_ERR_COMPLETION_EN 0x0004 -#define IOAT_CHANCTRL_INT_DISABLE 0x0001 +#define IOAT_CHANCTRL_INT_REARM 0x0001 +#define IOAT_CHANCTRL_RUN (IOAT_CHANCTRL_INT_REARM |\ + IOAT_CHANCTRL_ANY_ERR_ABORT_EN) #define IOAT_DMA_COMP_OFFSET 0x02 /* 16-bit DMA channel compatibility */ #define IOAT_DMA_COMP_V1 0x0001 /* Compatibility with DMA version 1 */ @@ -94,14 +111,14 @@ #define IOAT2_CHANSTS_OFFSET_HIGH 0x0C #define IOAT_CHANSTS_OFFSET_HIGH(ver) ((ver) < IOAT_VER_2_0 \ ? IOAT1_CHANSTS_OFFSET_HIGH : IOAT2_CHANSTS_OFFSET_HIGH) -#define IOAT_CHANSTS_COMPLETED_DESCRIPTOR_ADDR ~0x3F -#define IOAT_CHANSTS_SOFT_ERR 0x0000000000000010 -#define IOAT_CHANSTS_UNAFFILIATED_ERR 0x0000000000000008 -#define IOAT_CHANSTS_DMA_TRANSFER_STATUS 0x0000000000000007 -#define IOAT_CHANSTS_DMA_TRANSFER_STATUS_ACTIVE 0x0 -#define IOAT_CHANSTS_DMA_TRANSFER_STATUS_DONE 0x1 -#define IOAT_CHANSTS_DMA_TRANSFER_STATUS_SUSPENDED 0x2 -#define IOAT_CHANSTS_DMA_TRANSFER_STATUS_HALTED 0x3 +#define IOAT_CHANSTS_COMPLETED_DESCRIPTOR_ADDR (~0x3fULL) +#define IOAT_CHANSTS_SOFT_ERR 0x10ULL +#define IOAT_CHANSTS_UNAFFILIATED_ERR 0x8ULL +#define IOAT_CHANSTS_STATUS 0x7ULL +#define IOAT_CHANSTS_ACTIVE 0x0 +#define IOAT_CHANSTS_DONE 0x1 +#define IOAT_CHANSTS_SUSPENDED 0x2 +#define IOAT_CHANSTS_HALTED 0x3 @@ -204,22 +221,27 @@ #define IOAT_CDAR_OFFSET_HIGH 0x24 #define IOAT_CHANERR_OFFSET 0x28 /* 32-bit Channel Error Register */ -#define IOAT_CHANERR_DMA_TRANSFER_SRC_ADDR_ERR 0x0001 -#define IOAT_CHANERR_DMA_TRANSFER_DEST_ADDR_ERR 0x0002 -#define IOAT_CHANERR_NEXT_DESCRIPTOR_ADDR_ERR 0x0004 -#define IOAT_CHANERR_NEXT_DESCRIPTOR_ALIGNMENT_ERR 0x0008 +#define IOAT_CHANERR_SRC_ADDR_ERR 0x0001 +#define IOAT_CHANERR_DEST_ADDR_ERR 0x0002 +#define IOAT_CHANERR_NEXT_ADDR_ERR 0x0004 +#define IOAT_CHANERR_NEXT_DESC_ALIGN_ERR 0x0008 #define IOAT_CHANERR_CHAIN_ADDR_VALUE_ERR 0x0010 #define IOAT_CHANERR_CHANCMD_ERR 0x0020 #define IOAT_CHANERR_CHIPSET_UNCORRECTABLE_DATA_INTEGRITY_ERR 0x0040 #define IOAT_CHANERR_DMA_UNCORRECTABLE_DATA_INTEGRITY_ERR 0x0080 #define IOAT_CHANERR_READ_DATA_ERR 0x0100 #define IOAT_CHANERR_WRITE_DATA_ERR 0x0200 -#define IOAT_CHANERR_DESCRIPTOR_CONTROL_ERR 0x0400 -#define IOAT_CHANERR_DESCRIPTOR_LENGTH_ERR 0x0800 +#define IOAT_CHANERR_CONTROL_ERR 0x0400 +#define IOAT_CHANERR_LENGTH_ERR 0x0800 #define IOAT_CHANERR_COMPLETION_ADDR_ERR 0x1000 #define IOAT_CHANERR_INT_CONFIGURATION_ERR 0x2000 #define IOAT_CHANERR_SOFT_ERR 0x4000 #define IOAT_CHANERR_UNAFFILIATED_ERR 0x8000 +#define IOAT_CHANERR_XOR_P_OR_CRC_ERR 0x10000 +#define IOAT_CHANERR_XOR_Q_ERR 0x20000 +#define IOAT_CHANERR_DESCRIPTOR_COUNT_ERR 0x40000 + +#define IOAT_CHANERR_HANDLE_MASK (IOAT_CHANERR_XOR_P_OR_CRC_ERR | IOAT_CHANERR_XOR_Q_ERR) #define IOAT_CHANERR_MASK_OFFSET 0x2C /* 32-bit Channel Error Register */ diff --git a/drivers/dma/ioat_dma.c b/drivers/dma/ioat_dma.c deleted file mode 100644 index a600fc0f7962..000000000000 --- a/drivers/dma/ioat_dma.c +++ /dev/null @@ -1,1741 +0,0 @@ -/* - * Intel I/OAT DMA Linux driver - * Copyright(c) 2004 - 2009 Intel Corporation. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. - * - * The full GNU General Public License is included in this distribution in - * the file called "COPYING". - * - */ - -/* - * This driver supports an Intel I/OAT DMA engine, which does asynchronous - * copy operations. - */ - -#include <linux/init.h> -#include <linux/module.h> -#include <linux/pci.h> -#include <linux/interrupt.h> -#include <linux/dmaengine.h> -#include <linux/delay.h> -#include <linux/dma-mapping.h> -#include <linux/workqueue.h> -#include <linux/i7300_idle.h> -#include "ioatdma.h" -#include "ioatdma_registers.h" -#include "ioatdma_hw.h" - -#define to_ioat_chan(chan) container_of(chan, struct ioat_dma_chan, common) -#define to_ioatdma_device(dev) container_of(dev, struct ioatdma_device, common) -#define to_ioat_desc(lh) container_of(lh, struct ioat_desc_sw, node) -#define tx_to_ioat_desc(tx) container_of(tx, struct ioat_desc_sw, async_tx) - -#define chan_num(ch) ((int)((ch)->reg_base - (ch)->device->reg_base) / 0x80) -static int ioat_pending_level = 4; -module_param(ioat_pending_level, int, 0644); -MODULE_PARM_DESC(ioat_pending_level, - "high-water mark for pushing ioat descriptors (default: 4)"); - -#define RESET_DELAY msecs_to_jiffies(100) -#define WATCHDOG_DELAY round_jiffies(msecs_to_jiffies(2000)) -static void ioat_dma_chan_reset_part2(struct work_struct *work); -static void ioat_dma_chan_watchdog(struct work_struct *work); - -/* - * workaround for IOAT ver.3.0 null descriptor issue - * (channel returns error when size is 0) - */ -#define NULL_DESC_BUFFER_SIZE 1 - -/* internal functions */ -static void ioat_dma_start_null_desc(struct ioat_dma_chan *ioat_chan); -static void ioat_dma_memcpy_cleanup(struct ioat_dma_chan *ioat_chan); - -static struct ioat_desc_sw * -ioat1_dma_get_next_descriptor(struct ioat_dma_chan *ioat_chan); -static struct ioat_desc_sw * -ioat2_dma_get_next_descriptor(struct ioat_dma_chan *ioat_chan); - -static inline struct ioat_dma_chan *ioat_lookup_chan_by_index( - struct ioatdma_device *device, - int index) -{ - return device->idx[index]; -} - -/** - * ioat_dma_do_interrupt - handler used for single vector interrupt mode - * @irq: interrupt id - * @data: interrupt data - */ -static irqreturn_t ioat_dma_do_interrupt(int irq, void *data) -{ - struct ioatdma_device *instance = data; - struct ioat_dma_chan *ioat_chan; - unsigned long attnstatus; - int bit; - u8 intrctrl; - - intrctrl = readb(instance->reg_base + IOAT_INTRCTRL_OFFSET); - - if (!(intrctrl & IOAT_INTRCTRL_MASTER_INT_EN)) - return IRQ_NONE; - - if (!(intrctrl & IOAT_INTRCTRL_INT_STATUS)) { - writeb(intrctrl, instance->reg_base + IOAT_INTRCTRL_OFFSET); - return IRQ_NONE; - } - - attnstatus = readl(instance->reg_base + IOAT_ATTNSTATUS_OFFSET); - for_each_bit(bit, &attnstatus, BITS_PER_LONG) { - ioat_chan = ioat_lookup_chan_by_index(instance, bit); - tasklet_schedule(&ioat_chan->cleanup_task); - } - - writeb(intrctrl, instance->reg_base + IOAT_INTRCTRL_OFFSET); - return IRQ_HANDLED; -} - -/** - * ioat_dma_do_interrupt_msix - handler used for vector-per-channel interrupt mode - * @irq: interrupt id - * @data: interrupt data - */ -static irqreturn_t ioat_dma_do_interrupt_msix(int irq, void *data) -{ - struct ioat_dma_chan *ioat_chan = data; - - tasklet_schedule(&ioat_chan->cleanup_task); - - return IRQ_HANDLED; -} - -static void ioat_dma_cleanup_tasklet(unsigned long data); - -/** - * ioat_dma_enumerate_channels - find and initialize the device's channels - * @device: the device to be enumerated - */ -static int ioat_dma_enumerate_channels(struct ioatdma_device *device) -{ - u8 xfercap_scale; - u32 xfercap; - int i; - struct ioat_dma_chan *ioat_chan; - - /* - * IOAT ver.3 workarounds - */ - if (device->version == IOAT_VER_3_0) { - u32 chan_err_mask; - u16 dev_id; - u32 dmauncerrsts; - - /* - * Write CHANERRMSK_INT with 3E07h to mask out the errors - * that can cause stability issues for IOAT ver.3 - */ - chan_err_mask = 0x3E07; - pci_write_config_dword(device->pdev, - IOAT_PCI_CHANERRMASK_INT_OFFSET, - chan_err_mask); - - /* - * Clear DMAUNCERRSTS Cfg-Reg Parity Error status bit - * (workaround for spurious config parity error after restart) - */ - pci_read_config_word(device->pdev, - IOAT_PCI_DEVICE_ID_OFFSET, - &dev_id); - if (dev_id == PCI_DEVICE_ID_INTEL_IOAT_TBG0) { - dmauncerrsts = 0x10; - pci_write_config_dword(device->pdev, - IOAT_PCI_DMAUNCERRSTS_OFFSET, - dmauncerrsts); - } - } - - device->common.chancnt = readb(device->reg_base + IOAT_CHANCNT_OFFSET); - xfercap_scale = readb(device->reg_base + IOAT_XFERCAP_OFFSET); - xfercap = (xfercap_scale == 0 ? -1 : (1UL << xfercap_scale)); - -#ifdef CONFIG_I7300_IDLE_IOAT_CHANNEL - if (i7300_idle_platform_probe(NULL, NULL, 1) == 0) { - device->common.chancnt--; - } -#endif - for (i = 0; i < device->common.chancnt; i++) { - ioat_chan = kzalloc(sizeof(*ioat_chan), GFP_KERNEL); - if (!ioat_chan) { - device->common.chancnt = i; - break; - } - - ioat_chan->device = device; - ioat_chan->reg_base = device->reg_base + (0x80 * (i + 1)); - ioat_chan->xfercap = xfercap; - ioat_chan->desccount = 0; - INIT_DELAYED_WORK(&ioat_chan->work, ioat_dma_chan_reset_part2); - if (ioat_chan->device->version == IOAT_VER_2_0) - writel(IOAT_DCACTRL_CMPL_WRITE_ENABLE | - IOAT_DMA_DCA_ANY_CPU, - ioat_chan->reg_base + IOAT_DCACTRL_OFFSET); - else if (ioat_chan->device->version == IOAT_VER_3_0) - writel(IOAT_DMA_DCA_ANY_CPU, - ioat_chan->reg_base + IOAT_DCACTRL_OFFSET); - spin_lock_init(&ioat_chan->cleanup_lock); - spin_lock_init(&ioat_chan->desc_lock); - INIT_LIST_HEAD(&ioat_chan->free_desc); - INIT_LIST_HEAD(&ioat_chan->used_desc); - /* This should be made common somewhere in dmaengine.c */ - ioat_chan->common.device = &device->common; - list_add_tail(&ioat_chan->common.device_node, - &device->common.channels); - device->idx[i] = ioat_chan; - tasklet_init(&ioat_chan->cleanup_task, - ioat_dma_cleanup_tasklet, - (unsigned long) ioat_chan); - tasklet_disable(&ioat_chan->cleanup_task); - } - return device->common.chancnt; -} - -/** - * ioat_dma_memcpy_issue_pending - push potentially unrecognized appended - * descriptors to hw - * @chan: DMA channel handle - */ -static inline void __ioat1_dma_memcpy_issue_pending( - struct ioat_dma_chan *ioat_chan) -{ - ioat_chan->pending = 0; - writeb(IOAT_CHANCMD_APPEND, ioat_chan->reg_base + IOAT1_CHANCMD_OFFSET); -} - -static void ioat1_dma_memcpy_issue_pending(struct dma_chan *chan) -{ - struct ioat_dma_chan *ioat_chan = to_ioat_chan(chan); - - if (ioat_chan->pending > 0) { - spin_lock_bh(&ioat_chan->desc_lock); - __ioat1_dma_memcpy_issue_pending(ioat_chan); - spin_unlock_bh(&ioat_chan->desc_lock); - } -} - -static inline void __ioat2_dma_memcpy_issue_pending( - struct ioat_dma_chan *ioat_chan) -{ - ioat_chan->pending = 0; - writew(ioat_chan->dmacount, - ioat_chan->reg_base + IOAT_CHAN_DMACOUNT_OFFSET); -} - -static void ioat2_dma_memcpy_issue_pending(struct dma_chan *chan) -{ - struct ioat_dma_chan *ioat_chan = to_ioat_chan(chan); - - if (ioat_chan->pending > 0) { - spin_lock_bh(&ioat_chan->desc_lock); - __ioat2_dma_memcpy_issue_pending(ioat_chan); - spin_unlock_bh(&ioat_chan->desc_lock); - } -} - - -/** - * ioat_dma_chan_reset_part2 - reinit the channel after a reset - */ -static void ioat_dma_chan_reset_part2(struct work_struct *work) -{ - struct ioat_dma_chan *ioat_chan = - container_of(work, struct ioat_dma_chan, work.work); - struct ioat_desc_sw *desc; - - spin_lock_bh(&ioat_chan->cleanup_lock); - spin_lock_bh(&ioat_chan->desc_lock); - - ioat_chan->completion_virt->low = 0; - ioat_chan->completion_virt->high = 0; - ioat_chan->pending = 0; - - /* - * count the descriptors waiting, and be sure to do it - * right for both the CB1 line and the CB2 ring - */ - ioat_chan->dmacount = 0; - if (ioat_chan->used_desc.prev) { - desc = to_ioat_desc(ioat_chan->used_desc.prev); - do { - ioat_chan->dmacount++; - desc = to_ioat_desc(desc->node.next); - } while (&desc->node != ioat_chan->used_desc.next); - } - - /* - * write the new starting descriptor address - * this puts channel engine into ARMED state - */ - desc = to_ioat_desc(ioat_chan->used_desc.prev); - switch (ioat_chan->device->version) { - case IOAT_VER_1_2: - writel(((u64) desc->async_tx.phys) & 0x00000000FFFFFFFF, - ioat_chan->reg_base + IOAT1_CHAINADDR_OFFSET_LOW); - writel(((u64) desc->async_tx.phys) >> 32, - ioat_chan->reg_base + IOAT1_CHAINADDR_OFFSET_HIGH); - - writeb(IOAT_CHANCMD_START, ioat_chan->reg_base - + IOAT_CHANCMD_OFFSET(ioat_chan->device->version)); - break; - case IOAT_VER_2_0: - writel(((u64) desc->async_tx.phys) & 0x00000000FFFFFFFF, - ioat_chan->reg_base + IOAT2_CHAINADDR_OFFSET_LOW); - writel(((u64) desc->async_tx.phys) >> 32, - ioat_chan->reg_base + IOAT2_CHAINADDR_OFFSET_HIGH); - - /* tell the engine to go with what's left to be done */ - writew(ioat_chan->dmacount, - ioat_chan->reg_base + IOAT_CHAN_DMACOUNT_OFFSET); - - break; - } - dev_err(&ioat_chan->device->pdev->dev, - "chan%d reset - %d descs waiting, %d total desc\n", - chan_num(ioat_chan), ioat_chan->dmacount, ioat_chan->desccount); - - spin_unlock_bh(&ioat_chan->desc_lock); - spin_unlock_bh(&ioat_chan->cleanup_lock); -} - -/** - * ioat_dma_reset_channel - restart a channel - * @ioat_chan: IOAT DMA channel handle - */ -static void ioat_dma_reset_channel(struct ioat_dma_chan *ioat_chan) -{ - u32 chansts, chanerr; - - if (!ioat_chan->used_desc.prev) - return; - - chanerr = readl(ioat_chan->reg_base + IOAT_CHANERR_OFFSET); - chansts = (ioat_chan->completion_virt->low - & IOAT_CHANSTS_DMA_TRANSFER_STATUS); - if (chanerr) { - dev_err(&ioat_chan->device->pdev->dev, - "chan%d, CHANSTS = 0x%08x CHANERR = 0x%04x, clearing\n", - chan_num(ioat_chan), chansts, chanerr); - writel(chanerr, ioat_chan->reg_base + IOAT_CHANERR_OFFSET); - } - - /* - * whack it upside the head with a reset - * and wait for things to settle out. - * force the pending count to a really big negative - * to make sure no one forces an issue_pending - * while we're waiting. - */ - - spin_lock_bh(&ioat_chan->desc_lock); - ioat_chan->pending = INT_MIN; - writeb(IOAT_CHANCMD_RESET, - ioat_chan->reg_base - + IOAT_CHANCMD_OFFSET(ioat_chan->device->version)); - spin_unlock_bh(&ioat_chan->desc_lock); - - /* schedule the 2nd half instead of sleeping a long time */ - schedule_delayed_work(&ioat_chan->work, RESET_DELAY); -} - -/** - * ioat_dma_chan_watchdog - watch for stuck channels - */ -static void ioat_dma_chan_watchdog(struct work_struct *work) -{ - struct ioatdma_device *device = - container_of(work, struct ioatdma_device, work.work); - struct ioat_dma_chan *ioat_chan; - int i; - - union { - u64 full; - struct { - u32 low; - u32 high; - }; - } completion_hw; - unsigned long compl_desc_addr_hw; - - for (i = 0; i < device->common.chancnt; i++) { - ioat_chan = ioat_lookup_chan_by_index(device, i); - - if (ioat_chan->device->version == IOAT_VER_1_2 - /* have we started processing anything yet */ - && ioat_chan->last_completion - /* have we completed any since last watchdog cycle? */ - && (ioat_chan->last_completion == - ioat_chan->watchdog_completion) - /* has TCP stuck on one cookie since last watchdog? */ - && (ioat_chan->watchdog_tcp_cookie == - ioat_chan->watchdog_last_tcp_cookie) - && (ioat_chan->watchdog_tcp_cookie != - ioat_chan->completed_cookie) - /* is there something in the chain to be processed? */ - /* CB1 chain always has at least the last one processed */ - && (ioat_chan->used_desc.prev != ioat_chan->used_desc.next) - && ioat_chan->pending == 0) { - - /* - * check CHANSTS register for completed - * descriptor address. - * if it is different than completion writeback, - * it is not zero - * and it has changed since the last watchdog - * we can assume that channel - * is still working correctly - * and the problem is in completion writeback. - * update completion writeback - * with actual CHANSTS value - * else - * try resetting the channel - */ - - completion_hw.low = readl(ioat_chan->reg_base + - IOAT_CHANSTS_OFFSET_LOW(ioat_chan->device->version)); - completion_hw.high = readl(ioat_chan->reg_base + - IOAT_CHANSTS_OFFSET_HIGH(ioat_chan->device->version)); -#if (BITS_PER_LONG == 64) - compl_desc_addr_hw = - completion_hw.full - & IOAT_CHANSTS_COMPLETED_DESCRIPTOR_ADDR; -#else - compl_desc_addr_hw = - completion_hw.low & IOAT_LOW_COMPLETION_MASK; -#endif - - if ((compl_desc_addr_hw != 0) - && (compl_desc_addr_hw != ioat_chan->watchdog_completion) - && (compl_desc_addr_hw != ioat_chan->last_compl_desc_addr_hw)) { - ioat_chan->last_compl_desc_addr_hw = compl_desc_addr_hw; - ioat_chan->completion_virt->low = completion_hw.low; - ioat_chan->completion_virt->high = completion_hw.high; - } else { - ioat_dma_reset_channel(ioat_chan); - ioat_chan->watchdog_completion = 0; - ioat_chan->last_compl_desc_addr_hw = 0; - } - - /* - * for version 2.0 if there are descriptors yet to be processed - * and the last completed hasn't changed since the last watchdog - * if they haven't hit the pending level - * issue the pending to push them through - * else - * try resetting the channel - */ - } else if (ioat_chan->device->version == IOAT_VER_2_0 - && ioat_chan->used_desc.prev - && ioat_chan->last_completion - && ioat_chan->last_completion == ioat_chan->watchdog_completion) { - - if (ioat_chan->pending < ioat_pending_level) - ioat2_dma_memcpy_issue_pending(&ioat_chan->common); - else { - ioat_dma_reset_channel(ioat_chan); - ioat_chan->watchdog_completion = 0; - } - } else { - ioat_chan->last_compl_desc_addr_hw = 0; - ioat_chan->watchdog_completion - = ioat_chan->last_completion; - } - - ioat_chan->watchdog_last_tcp_cookie = - ioat_chan->watchdog_tcp_cookie; - } - - schedule_delayed_work(&device->work, WATCHDOG_DELAY); -} - -static dma_cookie_t ioat1_tx_submit(struct dma_async_tx_descriptor *tx) -{ - struct ioat_dma_chan *ioat_chan = to_ioat_chan(tx->chan); - struct ioat_desc_sw *first = tx_to_ioat_desc(tx); - struct ioat_desc_sw *prev, *new; - struct ioat_dma_descriptor *hw; - dma_cookie_t cookie; - LIST_HEAD(new_chain); - u32 copy; - size_t len; - dma_addr_t src, dst; - unsigned long orig_flags; - unsigned int desc_count = 0; - - /* src and dest and len are stored in the initial descriptor */ - len = first->len; - src = first->src; - dst = first->dst; - orig_flags = first->async_tx.flags; - new = first; - - spin_lock_bh(&ioat_chan->desc_lock); - prev = to_ioat_desc(ioat_chan->used_desc.prev); - prefetch(prev->hw); - do { - copy = min_t(size_t, len, ioat_chan->xfercap); - - async_tx_ack(&new->async_tx); - - hw = new->hw; - hw->size = copy; - hw->ctl = 0; - hw->src_addr = src; - hw->dst_addr = dst; - hw->next = 0; - - /* chain together the physical address list for the HW */ - wmb(); - prev->hw->next = (u64) new->async_tx.phys; - - len -= copy; - dst += copy; - src += copy; - - list_add_tail(&new->node, &new_chain); - desc_count++; - prev = new; - } while (len && (new = ioat1_dma_get_next_descriptor(ioat_chan))); - - if (!new) { - dev_err(&ioat_chan->device->pdev->dev, - "tx submit failed\n"); - spin_unlock_bh(&ioat_chan->desc_lock); - return -ENOMEM; - } - - hw->ctl = IOAT_DMA_DESCRIPTOR_CTL_CP_STS; - if (first->async_tx.callback) { - hw->ctl |= IOAT_DMA_DESCRIPTOR_CTL_INT_GN; - if (first != new) { - /* move callback into to last desc */ - new->async_tx.callback = first->async_tx.callback; - new->async_tx.callback_param - = first->async_tx.callback_param; - first->async_tx.callback = NULL; - first->async_tx.callback_param = NULL; - } - } - - new->tx_cnt = desc_count; - new->async_tx.flags = orig_flags; /* client is in control of this ack */ - - /* store the original values for use in later cleanup */ - if (new != first) { - new->src = first->src; - new->dst = first->dst; - new->len = first->len; - } - - /* cookie incr and addition to used_list must be atomic */ - cookie = ioat_chan->common.cookie; - cookie++; - if (cookie < 0) - cookie = 1; - ioat_chan->common.cookie = new->async_tx.cookie = cookie; - - /* write address into NextDescriptor field of last desc in chain */ - to_ioat_desc(ioat_chan->used_desc.prev)->hw->next = - first->async_tx.phys; - list_splice_tail(&new_chain, &ioat_chan->used_desc); - - ioat_chan->dmacount += desc_count; - ioat_chan->pending += desc_count; - if (ioat_chan->pending >= ioat_pending_level) - __ioat1_dma_memcpy_issue_pending(ioat_chan); - spin_unlock_bh(&ioat_chan->desc_lock); - - return cookie; -} - -static dma_cookie_t ioat2_tx_submit(struct dma_async_tx_descriptor *tx) -{ - struct ioat_dma_chan *ioat_chan = to_ioat_chan(tx->chan); - struct ioat_desc_sw *first = tx_to_ioat_desc(tx); - struct ioat_desc_sw *new; - struct ioat_dma_descriptor *hw; - dma_cookie_t cookie; - u32 copy; - size_t len; - dma_addr_t src, dst; - unsigned long orig_flags; - unsigned int desc_count = 0; - - /* src and dest and len are stored in the initial descriptor */ - len = first->len; - src = first->src; - dst = first->dst; - orig_flags = first->async_tx.flags; - new = first; - - /* - * ioat_chan->desc_lock is still in force in version 2 path - * it gets unlocked at end of this function - */ - do { - copy = min_t(size_t, len, ioat_chan->xfercap); - - async_tx_ack(&new->async_tx); - - hw = new->hw; - hw->size = copy; - hw->ctl = 0; - hw->src_addr = src; - hw->dst_addr = dst; - - len -= copy; - dst += copy; - src += copy; - desc_count++; - } while (len && (new = ioat2_dma_get_next_descriptor(ioat_chan))); - - if (!new) { - dev_err(&ioat_chan->device->pdev->dev, - "tx submit failed\n"); - spin_unlock_bh(&ioat_chan->desc_lock); - return -ENOMEM; - } - - hw->ctl |= IOAT_DMA_DESCRIPTOR_CTL_CP_STS; - if (first->async_tx.callback) { - hw->ctl |= IOAT_DMA_DESCRIPTOR_CTL_INT_GN; - if (first != new) { - /* move callback into to last desc */ - new->async_tx.callback = first->async_tx.callback; - new->async_tx.callback_param - = first->async_tx.callback_param; - first->async_tx.callback = NULL; - first->async_tx.callback_param = NULL; - } - } - - new->tx_cnt = desc_count; - new->async_tx.flags = orig_flags; /* client is in control of this ack */ - - /* store the original values for use in later cleanup */ - if (new != first) { - new->src = first->src; - new->dst = first->dst; - new->len = first->len; - } - - /* cookie incr and addition to used_list must be atomic */ - cookie = ioat_chan->common.cookie; - cookie++; - if (cookie < 0) - cookie = 1; - ioat_chan->common.cookie = new->async_tx.cookie = cookie; - - ioat_chan->dmacount += desc_count; - ioat_chan->pending += desc_count; - if (ioat_chan->pending >= ioat_pending_level) - __ioat2_dma_memcpy_issue_pending(ioat_chan); - spin_unlock_bh(&ioat_chan->desc_lock); - - return cookie; -} - -/** - * ioat_dma_alloc_descriptor - allocate and return a sw and hw descriptor pair - * @ioat_chan: the channel supplying the memory pool for the descriptors - * @flags: allocation flags - */ -static struct ioat_desc_sw *ioat_dma_alloc_descriptor( - struct ioat_dma_chan *ioat_chan, - gfp_t flags) -{ - struct ioat_dma_descriptor *desc; - struct ioat_desc_sw *desc_sw; - struct ioatdma_device *ioatdma_device; - dma_addr_t phys; - - ioatdma_device = to_ioatdma_device(ioat_chan->common.device); - desc = pci_pool_alloc(ioatdma_device->dma_pool, flags, &phys); - if (unlikely(!desc)) - return NULL; - - desc_sw = kzalloc(sizeof(*desc_sw), flags); - if (unlikely(!desc_sw)) { - pci_pool_free(ioatdma_device->dma_pool, desc, phys); - return NULL; - } - - memset(desc, 0, sizeof(*desc)); - dma_async_tx_descriptor_init(&desc_sw->async_tx, &ioat_chan->common); - switch (ioat_chan->device->version) { - case IOAT_VER_1_2: - desc_sw->async_tx.tx_submit = ioat1_tx_submit; - break; - case IOAT_VER_2_0: - case IOAT_VER_3_0: - desc_sw->async_tx.tx_submit = ioat2_tx_submit; - break; - } - - desc_sw->hw = desc; - desc_sw->async_tx.phys = phys; - - return desc_sw; -} - -static int ioat_initial_desc_count = 256; -module_param(ioat_initial_desc_count, int, 0644); -MODULE_PARM_DESC(ioat_initial_desc_count, - "initial descriptors per channel (default: 256)"); - -/** - * ioat2_dma_massage_chan_desc - link the descriptors into a circle - * @ioat_chan: the channel to be massaged - */ -static void ioat2_dma_massage_chan_desc(struct ioat_dma_chan *ioat_chan) -{ - struct ioat_desc_sw *desc, *_desc; - - /* setup used_desc */ - ioat_chan->used_desc.next = ioat_chan->free_desc.next; - ioat_chan->used_desc.prev = NULL; - - /* pull free_desc out of the circle so that every node is a hw - * descriptor, but leave it pointing to the list - */ - ioat_chan->free_desc.prev->next = ioat_chan->free_desc.next; - ioat_chan->free_desc.next->prev = ioat_chan->free_desc.prev; - - /* circle link the hw descriptors */ - desc = to_ioat_desc(ioat_chan->free_desc.next); - desc->hw->next = to_ioat_desc(desc->node.next)->async_tx.phys; - list_for_each_entry_safe(desc, _desc, ioat_chan->free_desc.next, node) { - desc->hw->next = to_ioat_desc(desc->node.next)->async_tx.phys; - } -} - -/** - * ioat_dma_alloc_chan_resources - returns the number of allocated descriptors - * @chan: the channel to be filled out - */ -static int ioat_dma_alloc_chan_resources(struct dma_chan *chan) -{ - struct ioat_dma_chan *ioat_chan = to_ioat_chan(chan); - struct ioat_desc_sw *desc; - u16 chanctrl; - u32 chanerr; - int i; - LIST_HEAD(tmp_list); - - /* have we already been set up? */ - if (!list_empty(&ioat_chan->free_desc)) - return ioat_chan->desccount; - - /* Setup register to interrupt and write completion status on error */ - chanctrl = IOAT_CHANCTRL_ERR_INT_EN | - IOAT_CHANCTRL_ANY_ERR_ABORT_EN | - IOAT_CHANCTRL_ERR_COMPLETION_EN; - writew(chanctrl, ioat_chan->reg_base + IOAT_CHANCTRL_OFFSET); - - chanerr = readl(ioat_chan->reg_base + IOAT_CHANERR_OFFSET); - if (chanerr) { - dev_err(&ioat_chan->device->pdev->dev, - "CHANERR = %x, clearing\n", chanerr); - writel(chanerr, ioat_chan->reg_base + IOAT_CHANERR_OFFSET); - } - - /* Allocate descriptors */ - for (i = 0; i < ioat_initial_desc_count; i++) { - desc = ioat_dma_alloc_descriptor(ioat_chan, GFP_KERNEL); - if (!desc) { - dev_err(&ioat_chan->device->pdev->dev, - "Only %d initial descriptors\n", i); - break; - } - list_add_tail(&desc->node, &tmp_list); - } - spin_lock_bh(&ioat_chan->desc_lock); - ioat_chan->desccount = i; - list_splice(&tmp_list, &ioat_chan->free_desc); - if (ioat_chan->device->version != IOAT_VER_1_2) - ioat2_dma_massage_chan_desc(ioat_chan); - spin_unlock_bh(&ioat_chan->desc_lock); - - /* allocate a completion writeback area */ - /* doing 2 32bit writes to mmio since 1 64b write doesn't work */ - ioat_chan->completion_virt = - pci_pool_alloc(ioat_chan->device->completion_pool, - GFP_KERNEL, - &ioat_chan->completion_addr); - memset(ioat_chan->completion_virt, 0, - sizeof(*ioat_chan->completion_virt)); - writel(((u64) ioat_chan->completion_addr) & 0x00000000FFFFFFFF, - ioat_chan->reg_base + IOAT_CHANCMP_OFFSET_LOW); - writel(((u64) ioat_chan->completion_addr) >> 32, - ioat_chan->reg_base + IOAT_CHANCMP_OFFSET_HIGH); - - tasklet_enable(&ioat_chan->cleanup_task); - ioat_dma_start_null_desc(ioat_chan); /* give chain to dma device */ - return ioat_chan->desccount; -} - -/** - * ioat_dma_free_chan_resources - release all the descriptors - * @chan: the channel to be cleaned - */ -static void ioat_dma_free_chan_resources(struct dma_chan *chan) -{ - struct ioat_dma_chan *ioat_chan = to_ioat_chan(chan); - struct ioatdma_device *ioatdma_device = to_ioatdma_device(chan->device); - struct ioat_desc_sw *desc, *_desc; - int in_use_descs = 0; - - /* Before freeing channel resources first check - * if they have been previously allocated for this channel. - */ - if (ioat_chan->desccount == 0) - return; - - tasklet_disable(&ioat_chan->cleanup_task); - ioat_dma_memcpy_cleanup(ioat_chan); - - /* Delay 100ms after reset to allow internal DMA logic to quiesce - * before removing DMA descriptor resources. - */ - writeb(IOAT_CHANCMD_RESET, - ioat_chan->reg_base - + IOAT_CHANCMD_OFFSET(ioat_chan->device->version)); - mdelay(100); - - spin_lock_bh(&ioat_chan->desc_lock); - switch (ioat_chan->device->version) { - case IOAT_VER_1_2: - list_for_each_entry_safe(desc, _desc, - &ioat_chan->used_desc, node) { - in_use_descs++; - list_del(&desc->node); - pci_pool_free(ioatdma_device->dma_pool, desc->hw, - desc->async_tx.phys); - kfree(desc); - } - list_for_each_entry_safe(desc, _desc, - &ioat_chan->free_desc, node) { - list_del(&desc->node); - pci_pool_free(ioatdma_device->dma_pool, desc->hw, - desc->async_tx.phys); - kfree(desc); - } - break; - case IOAT_VER_2_0: - case IOAT_VER_3_0: - list_for_each_entry_safe(desc, _desc, - ioat_chan->free_desc.next, node) { - list_del(&desc->node); - pci_pool_free(ioatdma_device->dma_pool, desc->hw, - desc->async_tx.phys); - kfree(desc); - } - desc = to_ioat_desc(ioat_chan->free_desc.next); - pci_pool_free(ioatdma_device->dma_pool, desc->hw, - desc->async_tx.phys); - kfree(desc); - INIT_LIST_HEAD(&ioat_chan->free_desc); - INIT_LIST_HEAD(&ioat_chan->used_desc); - break; - } - spin_unlock_bh(&ioat_chan->desc_lock); - - pci_pool_free(ioatdma_device->completion_pool, - ioat_chan->completion_virt, - ioat_chan->completion_addr); - - /* one is ok since we left it on there on purpose */ - if (in_use_descs > 1) - dev_err(&ioat_chan->device->pdev->dev, - "Freeing %d in use descriptors!\n", - in_use_descs - 1); - - ioat_chan->last_completion = ioat_chan->completion_addr = 0; - ioat_chan->pending = 0; - ioat_chan->dmacount = 0; - ioat_chan->desccount = 0; - ioat_chan->watchdog_completion = 0; - ioat_chan->last_compl_desc_addr_hw = 0; - ioat_chan->watchdog_tcp_cookie = - ioat_chan->watchdog_last_tcp_cookie = 0; -} - -/** - * ioat_dma_get_next_descriptor - return the next available descriptor - * @ioat_chan: IOAT DMA channel handle - * - * Gets the next descriptor from the chain, and must be called with the - * channel's desc_lock held. Allocates more descriptors if the channel - * has run out. - */ -static struct ioat_desc_sw * -ioat1_dma_get_next_descriptor(struct ioat_dma_chan *ioat_chan) -{ - struct ioat_desc_sw *new; - - if (!list_empty(&ioat_chan->free_desc)) { - new = to_ioat_desc(ioat_chan->free_desc.next); - list_del(&new->node); - } else { - /* try to get another desc */ - new = ioat_dma_alloc_descriptor(ioat_chan, GFP_ATOMIC); - if (!new) { - dev_err(&ioat_chan->device->pdev->dev, - "alloc failed\n"); - return NULL; - } - } - - prefetch(new->hw); - return new; -} - -static struct ioat_desc_sw * -ioat2_dma_get_next_descriptor(struct ioat_dma_chan *ioat_chan) -{ - struct ioat_desc_sw *new; - - /* - * used.prev points to where to start processing - * used.next points to next free descriptor - * if used.prev == NULL, there are none waiting to be processed - * if used.next == used.prev.prev, there is only one free descriptor, - * and we need to use it to as a noop descriptor before - * linking in a new set of descriptors, since the device - * has probably already read the pointer to it - */ - if (ioat_chan->used_desc.prev && - ioat_chan->used_desc.next == ioat_chan->used_desc.prev->prev) { - - struct ioat_desc_sw *desc; - struct ioat_desc_sw *noop_desc; - int i; - - /* set up the noop descriptor */ - noop_desc = to_ioat_desc(ioat_chan->used_desc.next); - /* set size to non-zero value (channel returns error when size is 0) */ - noop_desc->hw->size = NULL_DESC_BUFFER_SIZE; - noop_desc->hw->ctl = IOAT_DMA_DESCRIPTOR_NUL; - noop_desc->hw->src_addr = 0; - noop_desc->hw->dst_addr = 0; - - ioat_chan->used_desc.next = ioat_chan->used_desc.next->next; - ioat_chan->pending++; - ioat_chan->dmacount++; - - /* try to get a few more descriptors */ - for (i = 16; i; i--) { - desc = ioat_dma_alloc_descriptor(ioat_chan, GFP_ATOMIC); - if (!desc) { - dev_err(&ioat_chan->device->pdev->dev, - "alloc failed\n"); - break; - } - list_add_tail(&desc->node, ioat_chan->used_desc.next); - - desc->hw->next - = to_ioat_desc(desc->node.next)->async_tx.phys; - to_ioat_desc(desc->node.prev)->hw->next - = desc->async_tx.phys; - ioat_chan->desccount++; - } - - ioat_chan->used_desc.next = noop_desc->node.next; - } - new = to_ioat_desc(ioat_chan->used_desc.next); - prefetch(new); - ioat_chan->used_desc.next = new->node.next; - - if (ioat_chan->used_desc.prev == NULL) - ioat_chan->used_desc.prev = &new->node; - - prefetch(new->hw); - return new; -} - -static struct ioat_desc_sw *ioat_dma_get_next_descriptor( - struct ioat_dma_chan *ioat_chan) -{ - if (!ioat_chan) - return NULL; - - switch (ioat_chan->device->version) { - case IOAT_VER_1_2: - return ioat1_dma_get_next_descriptor(ioat_chan); - case IOAT_VER_2_0: - case IOAT_VER_3_0: - return ioat2_dma_get_next_descriptor(ioat_chan); - } - return NULL; -} - -static struct dma_async_tx_descriptor *ioat1_dma_prep_memcpy( - struct dma_chan *chan, - dma_addr_t dma_dest, - dma_addr_t dma_src, - size_t len, - unsigned long flags) -{ - struct ioat_dma_chan *ioat_chan = to_ioat_chan(chan); - struct ioat_desc_sw *new; - - spin_lock_bh(&ioat_chan->desc_lock); - new = ioat_dma_get_next_descriptor(ioat_chan); - spin_unlock_bh(&ioat_chan->desc_lock); - - if (new) { - new->len = len; - new->dst = dma_dest; - new->src = dma_src; - new->async_tx.flags = flags; - return &new->async_tx; - } else { - dev_err(&ioat_chan->device->pdev->dev, - "chan%d - get_next_desc failed: %d descs waiting, %d total desc\n", - chan_num(ioat_chan), ioat_chan->dmacount, ioat_chan->desccount); - return NULL; - } -} - -static struct dma_async_tx_descriptor *ioat2_dma_prep_memcpy( - struct dma_chan *chan, - dma_addr_t dma_dest, - dma_addr_t dma_src, - size_t len, - unsigned long flags) -{ - struct ioat_dma_chan *ioat_chan = to_ioat_chan(chan); - struct ioat_desc_sw *new; - - spin_lock_bh(&ioat_chan->desc_lock); - new = ioat2_dma_get_next_descriptor(ioat_chan); - - /* - * leave ioat_chan->desc_lock set in ioat 2 path - * it will get unlocked at end of tx_submit - */ - - if (new) { - new->len = len; - new->dst = dma_dest; - new->src = dma_src; - new->async_tx.flags = flags; - return &new->async_tx; - } else { - spin_unlock_bh(&ioat_chan->desc_lock); - dev_err(&ioat_chan->device->pdev->dev, - "chan%d - get_next_desc failed: %d descs waiting, %d total desc\n", - chan_num(ioat_chan), ioat_chan->dmacount, ioat_chan->desccount); - return NULL; - } -} - -static void ioat_dma_cleanup_tasklet(unsigned long data) -{ - struct ioat_dma_chan *chan = (void *)data; - ioat_dma_memcpy_cleanup(chan); - writew(IOAT_CHANCTRL_INT_DISABLE, - chan->reg_base + IOAT_CHANCTRL_OFFSET); -} - -static void -ioat_dma_unmap(struct ioat_dma_chan *ioat_chan, struct ioat_desc_sw *desc) -{ - if (!(desc->async_tx.flags & DMA_COMPL_SKIP_DEST_UNMAP)) { - if (desc->async_tx.flags & DMA_COMPL_DEST_UNMAP_SINGLE) - pci_unmap_single(ioat_chan->device->pdev, - pci_unmap_addr(desc, dst), - pci_unmap_len(desc, len), - PCI_DMA_FROMDEVICE); - else - pci_unmap_page(ioat_chan->device->pdev, - pci_unmap_addr(desc, dst), - pci_unmap_len(desc, len), - PCI_DMA_FROMDEVICE); - } - - if (!(desc->async_tx.flags & DMA_COMPL_SKIP_SRC_UNMAP)) { - if (desc->async_tx.flags & DMA_COMPL_SRC_UNMAP_SINGLE) - pci_unmap_single(ioat_chan->device->pdev, - pci_unmap_addr(desc, src), - pci_unmap_len(desc, len), - PCI_DMA_TODEVICE); - else - pci_unmap_page(ioat_chan->device->pdev, - pci_unmap_addr(desc, src), - pci_unmap_len(desc, len), - PCI_DMA_TODEVICE); - } -} - -/** - * ioat_dma_memcpy_cleanup - cleanup up finished descriptors - * @chan: ioat channel to be cleaned up - */ -static void ioat_dma_memcpy_cleanup(struct ioat_dma_chan *ioat_chan) -{ - unsigned long phys_complete; - struct ioat_desc_sw *desc, *_desc; - dma_cookie_t cookie = 0; - unsigned long desc_phys; - struct ioat_desc_sw *latest_desc; - - prefetch(ioat_chan->completion_virt); - - if (!spin_trylock_bh(&ioat_chan->cleanup_lock)) - return; - - /* The completion writeback can happen at any time, - so reads by the driver need to be atomic operations - The descriptor physical addresses are limited to 32-bits - when the CPU can only do a 32-bit mov */ - -#if (BITS_PER_LONG == 64) - phys_complete = - ioat_chan->completion_virt->full - & IOAT_CHANSTS_COMPLETED_DESCRIPTOR_ADDR; -#else - phys_complete = - ioat_chan->completion_virt->low & IOAT_LOW_COMPLETION_MASK; -#endif - - if ((ioat_chan->completion_virt->full - & IOAT_CHANSTS_DMA_TRANSFER_STATUS) == - IOAT_CHANSTS_DMA_TRANSFER_STATUS_HALTED) { - dev_err(&ioat_chan->device->pdev->dev, - "Channel halted, chanerr = %x\n", - readl(ioat_chan->reg_base + IOAT_CHANERR_OFFSET)); - - /* TODO do something to salvage the situation */ - } - - if (phys_complete == ioat_chan->last_completion) { - spin_unlock_bh(&ioat_chan->cleanup_lock); - /* - * perhaps we're stuck so hard that the watchdog can't go off? - * try to catch it after 2 seconds - */ - if (ioat_chan->device->version != IOAT_VER_3_0) { - if (time_after(jiffies, - ioat_chan->last_completion_time + HZ*WATCHDOG_DELAY)) { - ioat_dma_chan_watchdog(&(ioat_chan->device->work.work)); - ioat_chan->last_completion_time = jiffies; - } - } - return; - } - ioat_chan->last_completion_time = jiffies; - - cookie = 0; - if (!spin_trylock_bh(&ioat_chan->desc_lock)) { - spin_unlock_bh(&ioat_chan->cleanup_lock); - return; - } - - switch (ioat_chan->device->version) { - case IOAT_VER_1_2: - list_for_each_entry_safe(desc, _desc, - &ioat_chan->used_desc, node) { - - /* - * Incoming DMA requests may use multiple descriptors, - * due to exceeding xfercap, perhaps. If so, only the - * last one will have a cookie, and require unmapping. - */ - if (desc->async_tx.cookie) { - cookie = desc->async_tx.cookie; - ioat_dma_unmap(ioat_chan, desc); - if (desc->async_tx.callback) { - desc->async_tx.callback(desc->async_tx.callback_param); - desc->async_tx.callback = NULL; - } - } - - if (desc->async_tx.phys != phys_complete) { - /* - * a completed entry, but not the last, so clean - * up if the client is done with the descriptor - */ - if (async_tx_test_ack(&desc->async_tx)) { - list_move_tail(&desc->node, - &ioat_chan->free_desc); - } else - desc->async_tx.cookie = 0; - } else { - /* - * last used desc. Do not remove, so we can - * append from it, but don't look at it next - * time, either - */ - desc->async_tx.cookie = 0; - - /* TODO check status bits? */ - break; - } - } - break; - case IOAT_VER_2_0: - case IOAT_VER_3_0: - /* has some other thread has already cleaned up? */ - if (ioat_chan->used_desc.prev == NULL) - break; - - /* work backwards to find latest finished desc */ - desc = to_ioat_desc(ioat_chan->used_desc.next); - latest_desc = NULL; - do { - desc = to_ioat_desc(desc->node.prev); - desc_phys = (unsigned long)desc->async_tx.phys - & IOAT_CHANSTS_COMPLETED_DESCRIPTOR_ADDR; - if (desc_phys == phys_complete) { - latest_desc = desc; - break; - } - } while (&desc->node != ioat_chan->used_desc.prev); - - if (latest_desc != NULL) { - - /* work forwards to clear finished descriptors */ - for (desc = to_ioat_desc(ioat_chan->used_desc.prev); - &desc->node != latest_desc->node.next && - &desc->node != ioat_chan->used_desc.next; - desc = to_ioat_desc(desc->node.next)) { - if (desc->async_tx.cookie) { - cookie = desc->async_tx.cookie; - desc->async_tx.cookie = 0; - ioat_dma_unmap(ioat_chan, desc); - if (desc->async_tx.callback) { - desc->async_tx.callback(desc->async_tx.callback_param); - desc->async_tx.callback = NULL; - } - } - } - - /* move used.prev up beyond those that are finished */ - if (&desc->node == ioat_chan->used_desc.next) - ioat_chan->used_desc.prev = NULL; - else - ioat_chan->used_desc.prev = &desc->node; - } - break; - } - - spin_unlock_bh(&ioat_chan->desc_lock); - - ioat_chan->last_completion = phys_complete; - if (cookie != 0) - ioat_chan->completed_cookie = cookie; - - spin_unlock_bh(&ioat_chan->cleanup_lock); -} - -/** - * ioat_dma_is_complete - poll the status of a IOAT DMA transaction - * @chan: IOAT DMA channel handle - * @cookie: DMA transaction identifier - * @done: if not %NULL, updated with last completed transaction - * @used: if not %NULL, updated with last used transaction - */ -static enum dma_status ioat_dma_is_complete(struct dma_chan *chan, - dma_cookie_t cookie, - dma_cookie_t *done, - dma_cookie_t *used) -{ - struct ioat_dma_chan *ioat_chan = to_ioat_chan(chan); - dma_cookie_t last_used; - dma_cookie_t last_complete; - enum dma_status ret; - - last_used = chan->cookie; - last_complete = ioat_chan->completed_cookie; - ioat_chan->watchdog_tcp_cookie = cookie; - - if (done) - *done = last_complete; - if (used) - *used = last_used; - - ret = dma_async_is_complete(cookie, last_complete, last_used); - if (ret == DMA_SUCCESS) - return ret; - - ioat_dma_memcpy_cleanup(ioat_chan); - - last_used = chan->cookie; - last_complete = ioat_chan->completed_cookie; - - if (done) - *done = last_complete; - if (used) - *used = last_used; - - return dma_async_is_complete(cookie, last_complete, last_used); -} - -static void ioat_dma_start_null_desc(struct ioat_dma_chan *ioat_chan) -{ - struct ioat_desc_sw *desc; - - spin_lock_bh(&ioat_chan->desc_lock); - - desc = ioat_dma_get_next_descriptor(ioat_chan); - - if (!desc) { - dev_err(&ioat_chan->device->pdev->dev, - "Unable to start null desc - get next desc failed\n"); - spin_unlock_bh(&ioat_chan->desc_lock); - return; - } - - desc->hw->ctl = IOAT_DMA_DESCRIPTOR_NUL - | IOAT_DMA_DESCRIPTOR_CTL_INT_GN - | IOAT_DMA_DESCRIPTOR_CTL_CP_STS; - /* set size to non-zero value (channel returns error when size is 0) */ - desc->hw->size = NULL_DESC_BUFFER_SIZE; - desc->hw->src_addr = 0; - desc->hw->dst_addr = 0; - async_tx_ack(&desc->async_tx); - switch (ioat_chan->device->version) { - case IOAT_VER_1_2: - desc->hw->next = 0; - list_add_tail(&desc->node, &ioat_chan->used_desc); - - writel(((u64) desc->async_tx.phys) & 0x00000000FFFFFFFF, - ioat_chan->reg_base + IOAT1_CHAINADDR_OFFSET_LOW); - writel(((u64) desc->async_tx.phys) >> 32, - ioat_chan->reg_base + IOAT1_CHAINADDR_OFFSET_HIGH); - - writeb(IOAT_CHANCMD_START, ioat_chan->reg_base - + IOAT_CHANCMD_OFFSET(ioat_chan->device->version)); - break; - case IOAT_VER_2_0: - case IOAT_VER_3_0: - writel(((u64) desc->async_tx.phys) & 0x00000000FFFFFFFF, - ioat_chan->reg_base + IOAT2_CHAINADDR_OFFSET_LOW); - writel(((u64) desc->async_tx.phys) >> 32, - ioat_chan->reg_base + IOAT2_CHAINADDR_OFFSET_HIGH); - - ioat_chan->dmacount++; - __ioat2_dma_memcpy_issue_pending(ioat_chan); - break; - } - spin_unlock_bh(&ioat_chan->desc_lock); -} - -/* - * Perform a IOAT transaction to verify the HW works. - */ -#define IOAT_TEST_SIZE 2000 - -static void ioat_dma_test_callback(void *dma_async_param) -{ - struct completion *cmp = dma_async_param; - - complete(cmp); -} - -/** - * ioat_dma_self_test - Perform a IOAT transaction to verify the HW works. - * @device: device to be tested - */ -static int ioat_dma_self_test(struct ioatdma_device *device) -{ - int i; - u8 *src; - u8 *dest; - struct dma_chan *dma_chan; - struct dma_async_tx_descriptor *tx; - dma_addr_t dma_dest, dma_src; - dma_cookie_t cookie; - int err = 0; - struct completion cmp; - unsigned long tmo; - unsigned long flags; - - src = kzalloc(sizeof(u8) * IOAT_TEST_SIZE, GFP_KERNEL); - if (!src) - return -ENOMEM; - dest = kzalloc(sizeof(u8) * IOAT_TEST_SIZE, GFP_KERNEL); - if (!dest) { - kfree(src); - return -ENOMEM; - } - - /* Fill in src buffer */ - for (i = 0; i < IOAT_TEST_SIZE; i++) - src[i] = (u8)i; - - /* Start copy, using first DMA channel */ - dma_chan = container_of(device->common.channels.next, - struct dma_chan, - device_node); - if (device->common.device_alloc_chan_resources(dma_chan) < 1) { - dev_err(&device->pdev->dev, - "selftest cannot allocate chan resource\n"); - err = -ENODEV; - goto out; - } - - dma_src = dma_map_single(dma_chan->device->dev, src, IOAT_TEST_SIZE, - DMA_TO_DEVICE); - dma_dest = dma_map_single(dma_chan->device->dev, dest, IOAT_TEST_SIZE, - DMA_FROM_DEVICE); - flags = DMA_COMPL_SRC_UNMAP_SINGLE | DMA_COMPL_DEST_UNMAP_SINGLE; - tx = device->common.device_prep_dma_memcpy(dma_chan, dma_dest, dma_src, - IOAT_TEST_SIZE, flags); - if (!tx) { - dev_err(&device->pdev->dev, - "Self-test prep failed, disabling\n"); - err = -ENODEV; - goto free_resources; - } - - async_tx_ack(tx); - init_completion(&cmp); - tx->callback = ioat_dma_test_callback; - tx->callback_param = &cmp; - cookie = tx->tx_submit(tx); - if (cookie < 0) { - dev_err(&device->pdev->dev, - "Self-test setup failed, disabling\n"); - err = -ENODEV; - goto free_resources; - } - device->common.device_issue_pending(dma_chan); - - tmo = wait_for_completion_timeout(&cmp, msecs_to_jiffies(3000)); - - if (tmo == 0 || - device->common.device_is_tx_complete(dma_chan, cookie, NULL, NULL) - != DMA_SUCCESS) { - dev_err(&device->pdev->dev, - "Self-test copy timed out, disabling\n"); - err = -ENODEV; - goto free_resources; - } - if (memcmp(src, dest, IOAT_TEST_SIZE)) { - dev_err(&device->pdev->dev, - "Self-test copy failed compare, disabling\n"); - err = -ENODEV; - goto free_resources; - } - -free_resources: - device->common.device_free_chan_resources(dma_chan); -out: - kfree(src); - kfree(dest); - return err; -} - -static char ioat_interrupt_style[32] = "msix"; -module_param_string(ioat_interrupt_style, ioat_interrupt_style, - sizeof(ioat_interrupt_style), 0644); -MODULE_PARM_DESC(ioat_interrupt_style, - "set ioat interrupt style: msix (default), " - "msix-single-vector, msi, intx)"); - -/** - * ioat_dma_setup_interrupts - setup interrupt handler - * @device: ioat device - */ -static int ioat_dma_setup_interrupts(struct ioatdma_device *device) -{ - struct ioat_dma_chan *ioat_chan; - int err, i, j, msixcnt; - u8 intrctrl = 0; - - if (!strcmp(ioat_interrupt_style, "msix")) - goto msix; - if (!strcmp(ioat_interrupt_style, "msix-single-vector")) - goto msix_single_vector; - if (!strcmp(ioat_interrupt_style, "msi")) - goto msi; - if (!strcmp(ioat_interrupt_style, "intx")) - goto intx; - dev_err(&device->pdev->dev, "invalid ioat_interrupt_style %s\n", - ioat_interrupt_style); - goto err_no_irq; - -msix: - /* The number of MSI-X vectors should equal the number of channels */ - msixcnt = device->common.chancnt; - for (i = 0; i < msixcnt; i++) - device->msix_entries[i].entry = i; - - err = pci_enable_msix(device->pdev, device->msix_entries, msixcnt); - if (err < 0) - goto msi; - if (err > 0) - goto msix_single_vector; - - for (i = 0; i < msixcnt; i++) { - ioat_chan = ioat_lookup_chan_by_index(device, i); - err = request_irq(device->msix_entries[i].vector, - ioat_dma_do_interrupt_msix, - 0, "ioat-msix", ioat_chan); - if (err) { - for (j = 0; j < i; j++) { - ioat_chan = - ioat_lookup_chan_by_index(device, j); - free_irq(device->msix_entries[j].vector, - ioat_chan); - } - goto msix_single_vector; - } - } - intrctrl |= IOAT_INTRCTRL_MSIX_VECTOR_CONTROL; - device->irq_mode = msix_multi_vector; - goto done; - -msix_single_vector: - device->msix_entries[0].entry = 0; - err = pci_enable_msix(device->pdev, device->msix_entries, 1); - if (err) - goto msi; - - err = request_irq(device->msix_entries[0].vector, ioat_dma_do_interrupt, - 0, "ioat-msix", device); - if (err) { - pci_disable_msix(device->pdev); - goto msi; - } - device->irq_mode = msix_single_vector; - goto done; - -msi: - err = pci_enable_msi(device->pdev); - if (err) - goto intx; - - err = request_irq(device->pdev->irq, ioat_dma_do_interrupt, - 0, "ioat-msi", device); - if (err) { - pci_disable_msi(device->pdev); - goto intx; - } - /* - * CB 1.2 devices need a bit set in configuration space to enable MSI - */ - if (device->version == IOAT_VER_1_2) { - u32 dmactrl; - pci_read_config_dword(device->pdev, - IOAT_PCI_DMACTRL_OFFSET, &dmactrl); - dmactrl |= IOAT_PCI_DMACTRL_MSI_EN; - pci_write_config_dword(device->pdev, - IOAT_PCI_DMACTRL_OFFSET, dmactrl); - } - device->irq_mode = msi; - goto done; - -intx: - err = request_irq(device->pdev->irq, ioat_dma_do_interrupt, - IRQF_SHARED, "ioat-intx", device); - if (err) - goto err_no_irq; - device->irq_mode = intx; - -done: - intrctrl |= IOAT_INTRCTRL_MASTER_INT_EN; - writeb(intrctrl, device->reg_base + IOAT_INTRCTRL_OFFSET); - return 0; - -err_no_irq: - /* Disable all interrupt generation */ - writeb(0, device->reg_base + IOAT_INTRCTRL_OFFSET); - dev_err(&device->pdev->dev, "no usable interrupts\n"); - device->irq_mode = none; - return -1; -} - -/** - * ioat_dma_remove_interrupts - remove whatever interrupts were set - * @device: ioat device - */ -static void ioat_dma_remove_interrupts(struct ioatdma_device *device) -{ - struct ioat_dma_chan *ioat_chan; - int i; - - /* Disable all interrupt generation */ - writeb(0, device->reg_base + IOAT_INTRCTRL_OFFSET); - - switch (device->irq_mode) { - case msix_multi_vector: - for (i = 0; i < device->common.chancnt; i++) { - ioat_chan = ioat_lookup_chan_by_index(device, i); - free_irq(device->msix_entries[i].vector, ioat_chan); - } - pci_disable_msix(device->pdev); - break; - case msix_single_vector: - free_irq(device->msix_entries[0].vector, device); - pci_disable_msix(device->pdev); - break; - case msi: - free_irq(device->pdev->irq, device); - pci_disable_msi(device->pdev); - break; - case intx: - free_irq(device->pdev->irq, device); - break; - case none: - dev_warn(&device->pdev->dev, - "call to %s without interrupts setup\n", __func__); - } - device->irq_mode = none; -} - -struct ioatdma_device *ioat_dma_probe(struct pci_dev *pdev, - void __iomem *iobase) -{ - int err; - struct ioatdma_device *device; - - device = kzalloc(sizeof(*device), GFP_KERNEL); - if (!device) { - err = -ENOMEM; - goto err_kzalloc; - } - device->pdev = pdev; - device->reg_base = iobase; - device->version = readb(device->reg_base + IOAT_VER_OFFSET); - - /* DMA coherent memory pool for DMA descriptor allocations */ - device->dma_pool = pci_pool_create("dma_desc_pool", pdev, - sizeof(struct ioat_dma_descriptor), - 64, 0); - if (!device->dma_pool) { - err = -ENOMEM; - goto err_dma_pool; - } - - device->completion_pool = pci_pool_create("completion_pool", pdev, - sizeof(u64), SMP_CACHE_BYTES, - SMP_CACHE_BYTES); - if (!device->completion_pool) { - err = -ENOMEM; - goto err_completion_pool; - } - - INIT_LIST_HEAD(&device->common.channels); - ioat_dma_enumerate_channels(device); - - device->common.device_alloc_chan_resources = - ioat_dma_alloc_chan_resources; - device->common.device_free_chan_resources = - ioat_dma_free_chan_resources; - device->common.dev = &pdev->dev; - - dma_cap_set(DMA_MEMCPY, device->common.cap_mask); - device->common.device_is_tx_complete = ioat_dma_is_complete; - switch (device->version) { - case IOAT_VER_1_2: - device->common.device_prep_dma_memcpy = ioat1_dma_prep_memcpy; - device->common.device_issue_pending = - ioat1_dma_memcpy_issue_pending; - break; - case IOAT_VER_2_0: - case IOAT_VER_3_0: - device->common.device_prep_dma_memcpy = ioat2_dma_prep_memcpy; - device->common.device_issue_pending = - ioat2_dma_memcpy_issue_pending; - break; - } - - dev_err(&device->pdev->dev, - "Intel(R) I/OAT DMA Engine found," - " %d channels, device version 0x%02x, driver version %s\n", - device->common.chancnt, device->version, IOAT_DMA_VERSION); - - if (!device->common.chancnt) { - dev_err(&device->pdev->dev, - "Intel(R) I/OAT DMA Engine problem found: " - "zero channels detected\n"); - goto err_setup_interrupts; - } - - err = ioat_dma_setup_interrupts(device); - if (err) - goto err_setup_interrupts; - - err = ioat_dma_self_test(device); - if (err) - goto err_self_test; - - ioat_set_tcp_copy_break(device); - - dma_async_device_register(&device->common); - - if (device->version != IOAT_VER_3_0) { - INIT_DELAYED_WORK(&device->work, ioat_dma_chan_watchdog); - schedule_delayed_work(&device->work, - WATCHDOG_DELAY); - } - - return device; - -err_self_test: - ioat_dma_remove_interrupts(device); -err_setup_interrupts: - pci_pool_destroy(device->completion_pool); -err_completion_pool: - pci_pool_destroy(device->dma_pool); -err_dma_pool: - kfree(device); -err_kzalloc: - dev_err(&pdev->dev, - "Intel(R) I/OAT DMA Engine initialization failed\n"); - return NULL; -} - -void ioat_dma_remove(struct ioatdma_device *device) -{ - struct dma_chan *chan, *_chan; - struct ioat_dma_chan *ioat_chan; - - if (device->version != IOAT_VER_3_0) - cancel_delayed_work(&device->work); - - ioat_dma_remove_interrupts(device); - - dma_async_device_unregister(&device->common); - - pci_pool_destroy(device->dma_pool); - pci_pool_destroy(device->completion_pool); - - iounmap(device->reg_base); - pci_release_regions(device->pdev); - pci_disable_device(device->pdev); - - list_for_each_entry_safe(chan, _chan, - &device->common.channels, device_node) { - ioat_chan = to_ioat_chan(chan); - list_del(&chan->device_node); - kfree(ioat_chan); - } - kfree(device); -} - diff --git a/drivers/dma/ioatdma.h b/drivers/dma/ioatdma.h deleted file mode 100644 index a52ff4bd4601..000000000000 --- a/drivers/dma/ioatdma.h +++ /dev/null @@ -1,165 +0,0 @@ -/* - * Copyright(c) 2004 - 2009 Intel Corporation. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the Free - * Software Foundation; either version 2 of the License, or (at your option) - * any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., 59 - * Temple Place - Suite 330, Boston, MA 02111-1307, USA. - * - * The full GNU General Public License is included in this distribution in the - * file called COPYING. - */ -#ifndef IOATDMA_H -#define IOATDMA_H - -#include <linux/dmaengine.h> -#include "ioatdma_hw.h" -#include <linux/init.h> -#include <linux/dmapool.h> -#include <linux/cache.h> -#include <linux/pci_ids.h> -#include <net/tcp.h> - -#define IOAT_DMA_VERSION "3.64" - -enum ioat_interrupt { - none = 0, - msix_multi_vector = 1, - msix_single_vector = 2, - msi = 3, - intx = 4, -}; - -#define IOAT_LOW_COMPLETION_MASK 0xffffffc0 -#define IOAT_DMA_DCA_ANY_CPU ~0 -#define IOAT_WATCHDOG_PERIOD (2 * HZ) - - -/** - * struct ioatdma_device - internal representation of a IOAT device - * @pdev: PCI-Express device - * @reg_base: MMIO register space base address - * @dma_pool: for allocating DMA descriptors - * @common: embedded struct dma_device - * @version: version of ioatdma device - * @irq_mode: which style irq to use - * @msix_entries: irq handlers - * @idx: per channel data - */ - -struct ioatdma_device { - struct pci_dev *pdev; - void __iomem *reg_base; - struct pci_pool *dma_pool; - struct pci_pool *completion_pool; - struct dma_device common; - u8 version; - enum ioat_interrupt irq_mode; - struct delayed_work work; - struct msix_entry msix_entries[4]; - struct ioat_dma_chan *idx[4]; -}; - -/** - * struct ioat_dma_chan - internal representation of a DMA channel - */ -struct ioat_dma_chan { - - void __iomem *reg_base; - - dma_cookie_t completed_cookie; - unsigned long last_completion; - unsigned long last_completion_time; - - size_t xfercap; /* XFERCAP register value expanded out */ - - spinlock_t cleanup_lock; - spinlock_t desc_lock; - struct list_head free_desc; - struct list_head used_desc; - unsigned long watchdog_completion; - int watchdog_tcp_cookie; - u32 watchdog_last_tcp_cookie; - struct delayed_work work; - - int pending; - int dmacount; - int desccount; - - struct ioatdma_device *device; - struct dma_chan common; - - dma_addr_t completion_addr; - union { - u64 full; /* HW completion writeback */ - struct { - u32 low; - u32 high; - }; - } *completion_virt; - unsigned long last_compl_desc_addr_hw; - struct tasklet_struct cleanup_task; -}; - -/* wrapper around hardware descriptor format + additional software fields */ - -/** - * struct ioat_desc_sw - wrapper around hardware descriptor - * @hw: hardware DMA descriptor - * @node: this descriptor will either be on the free list, - * or attached to a transaction list (async_tx.tx_list) - * @tx_cnt: number of descriptors required to complete the transaction - * @async_tx: the generic software descriptor for all engines - */ -struct ioat_desc_sw { - struct ioat_dma_descriptor *hw; - struct list_head node; - int tx_cnt; - size_t len; - dma_addr_t src; - dma_addr_t dst; - struct dma_async_tx_descriptor async_tx; -}; - -static inline void ioat_set_tcp_copy_break(struct ioatdma_device *dev) -{ - #ifdef CONFIG_NET_DMA - switch (dev->version) { - case IOAT_VER_1_2: - sysctl_tcp_dma_copybreak = 4096; - break; - case IOAT_VER_2_0: - sysctl_tcp_dma_copybreak = 2048; - break; - case IOAT_VER_3_0: - sysctl_tcp_dma_copybreak = 262144; - break; - } - #endif -} - -#if defined(CONFIG_INTEL_IOATDMA) || defined(CONFIG_INTEL_IOATDMA_MODULE) -struct ioatdma_device *ioat_dma_probe(struct pci_dev *pdev, - void __iomem *iobase); -void ioat_dma_remove(struct ioatdma_device *device); -struct dca_provider *ioat_dca_init(struct pci_dev *pdev, void __iomem *iobase); -struct dca_provider *ioat2_dca_init(struct pci_dev *pdev, void __iomem *iobase); -struct dca_provider *ioat3_dca_init(struct pci_dev *pdev, void __iomem *iobase); -#else -#define ioat_dma_probe(pdev, iobase) NULL -#define ioat_dma_remove(device) do { } while (0) -#define ioat_dca_init(pdev, iobase) NULL -#define ioat2_dca_init(pdev, iobase) NULL -#define ioat3_dca_init(pdev, iobase) NULL -#endif - -#endif /* IOATDMA_H */ diff --git a/drivers/dma/ioatdma_hw.h b/drivers/dma/ioatdma_hw.h deleted file mode 100644 index afa57eef86c9..000000000000 --- a/drivers/dma/ioatdma_hw.h +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright(c) 2004 - 2009 Intel Corporation. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the Free - * Software Foundation; either version 2 of the License, or (at your option) - * any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., 59 - * Temple Place - Suite 330, Boston, MA 02111-1307, USA. - * - * The full GNU General Public License is included in this distribution in the - * file called COPYING. - */ -#ifndef _IOAT_HW_H_ -#define _IOAT_HW_H_ - -/* PCI Configuration Space Values */ -#define IOAT_PCI_VID 0x8086 - -/* CB device ID's */ -#define IOAT_PCI_DID_5000 0x1A38 -#define IOAT_PCI_DID_CNB 0x360B -#define IOAT_PCI_DID_SCNB 0x65FF -#define IOAT_PCI_DID_SNB 0x402F - -#define IOAT_PCI_RID 0x00 -#define IOAT_PCI_SVID 0x8086 -#define IOAT_PCI_SID 0x8086 -#define IOAT_VER_1_2 0x12 /* Version 1.2 */ -#define IOAT_VER_2_0 0x20 /* Version 2.0 */ -#define IOAT_VER_3_0 0x30 /* Version 3.0 */ - -struct ioat_dma_descriptor { - uint32_t size; - uint32_t ctl; - uint64_t src_addr; - uint64_t dst_addr; - uint64_t next; - uint64_t rsv1; - uint64_t rsv2; - uint64_t user1; - uint64_t user2; -}; - -#define IOAT_DMA_DESCRIPTOR_CTL_INT_GN 0x00000001 -#define IOAT_DMA_DESCRIPTOR_CTL_SRC_SN 0x00000002 -#define IOAT_DMA_DESCRIPTOR_CTL_DST_SN 0x00000004 -#define IOAT_DMA_DESCRIPTOR_CTL_CP_STS 0x00000008 -#define IOAT_DMA_DESCRIPTOR_CTL_FRAME 0x00000010 -#define IOAT_DMA_DESCRIPTOR_NUL 0x00000020 -#define IOAT_DMA_DESCRIPTOR_CTL_SP_BRK 0x00000040 -#define IOAT_DMA_DESCRIPTOR_CTL_DP_BRK 0x00000080 -#define IOAT_DMA_DESCRIPTOR_CTL_BNDL 0x00000100 -#define IOAT_DMA_DESCRIPTOR_CTL_DCA 0x00000200 -#define IOAT_DMA_DESCRIPTOR_CTL_BUFHINT 0x00000400 - -#define IOAT_DMA_DESCRIPTOR_CTL_OPCODE_CONTEXT 0xFF000000 -#define IOAT_DMA_DESCRIPTOR_CTL_OPCODE_DMA 0x00000000 - -#define IOAT_DMA_DESCRIPTOR_CTL_CONTEXT_DCA 0x00000001 -#define IOAT_DMA_DESCRIPTOR_CTL_OPCODE_MASK 0xFF000000 - -#endif diff --git a/drivers/dma/iop-adma.c b/drivers/dma/iop-adma.c index 2f052265122f..645ca8d54ec4 100644 --- a/drivers/dma/iop-adma.c +++ b/drivers/dma/iop-adma.c @@ -31,6 +31,7 @@ #include <linux/platform_device.h> #include <linux/memory.h> #include <linux/ioport.h> +#include <linux/raid/pq.h> #include <mach/adma.h> @@ -57,65 +58,110 @@ static void iop_adma_free_slots(struct iop_adma_desc_slot *slot) } } +static void +iop_desc_unmap(struct iop_adma_chan *iop_chan, struct iop_adma_desc_slot *desc) +{ + struct dma_async_tx_descriptor *tx = &desc->async_tx; + struct iop_adma_desc_slot *unmap = desc->group_head; + struct device *dev = &iop_chan->device->pdev->dev; + u32 len = unmap->unmap_len; + enum dma_ctrl_flags flags = tx->flags; + u32 src_cnt; + dma_addr_t addr; + dma_addr_t dest; + + src_cnt = unmap->unmap_src_cnt; + dest = iop_desc_get_dest_addr(unmap, iop_chan); + if (!(flags & DMA_COMPL_SKIP_DEST_UNMAP)) { + enum dma_data_direction dir; + + if (src_cnt > 1) /* is xor? */ + dir = DMA_BIDIRECTIONAL; + else + dir = DMA_FROM_DEVICE; + + dma_unmap_page(dev, dest, len, dir); + } + + if (!(flags & DMA_COMPL_SKIP_SRC_UNMAP)) { + while (src_cnt--) { + addr = iop_desc_get_src_addr(unmap, iop_chan, src_cnt); + if (addr == dest) + continue; + dma_unmap_page(dev, addr, len, DMA_TO_DEVICE); + } + } + desc->group_head = NULL; +} + +static void +iop_desc_unmap_pq(struct iop_adma_chan *iop_chan, struct iop_adma_desc_slot *desc) +{ + struct dma_async_tx_descriptor *tx = &desc->async_tx; + struct iop_adma_desc_slot *unmap = desc->group_head; + struct device *dev = &iop_chan->device->pdev->dev; + u32 len = unmap->unmap_len; + enum dma_ctrl_flags flags = tx->flags; + u32 src_cnt = unmap->unmap_src_cnt; + dma_addr_t pdest = iop_desc_get_dest_addr(unmap, iop_chan); + dma_addr_t qdest = iop_desc_get_qdest_addr(unmap, iop_chan); + int i; + + if (tx->flags & DMA_PREP_CONTINUE) + src_cnt -= 3; + + if (!(flags & DMA_COMPL_SKIP_DEST_UNMAP) && !desc->pq_check_result) { + dma_unmap_page(dev, pdest, len, DMA_BIDIRECTIONAL); + dma_unmap_page(dev, qdest, len, DMA_BIDIRECTIONAL); + } + + if (!(flags & DMA_COMPL_SKIP_SRC_UNMAP)) { + dma_addr_t addr; + + for (i = 0; i < src_cnt; i++) { + addr = iop_desc_get_src_addr(unmap, iop_chan, i); + dma_unmap_page(dev, addr, len, DMA_TO_DEVICE); + } + if (desc->pq_check_result) { + dma_unmap_page(dev, pdest, len, DMA_TO_DEVICE); + dma_unmap_page(dev, qdest, len, DMA_TO_DEVICE); + } + } + + desc->group_head = NULL; +} + + static dma_cookie_t iop_adma_run_tx_complete_actions(struct iop_adma_desc_slot *desc, struct iop_adma_chan *iop_chan, dma_cookie_t cookie) { - BUG_ON(desc->async_tx.cookie < 0); - if (desc->async_tx.cookie > 0) { - cookie = desc->async_tx.cookie; - desc->async_tx.cookie = 0; + struct dma_async_tx_descriptor *tx = &desc->async_tx; + + BUG_ON(tx->cookie < 0); + if (tx->cookie > 0) { + cookie = tx->cookie; + tx->cookie = 0; /* call the callback (must not sleep or submit new * operations to this channel) */ - if (desc->async_tx.callback) - desc->async_tx.callback( - desc->async_tx.callback_param); + if (tx->callback) + tx->callback(tx->callback_param); /* unmap dma addresses * (unmap_single vs unmap_page?) */ if (desc->group_head && desc->unmap_len) { - struct iop_adma_desc_slot *unmap = desc->group_head; - struct device *dev = - &iop_chan->device->pdev->dev; - u32 len = unmap->unmap_len; - enum dma_ctrl_flags flags = desc->async_tx.flags; - u32 src_cnt; - dma_addr_t addr; - dma_addr_t dest; - - src_cnt = unmap->unmap_src_cnt; - dest = iop_desc_get_dest_addr(unmap, iop_chan); - if (!(flags & DMA_COMPL_SKIP_DEST_UNMAP)) { - enum dma_data_direction dir; - - if (src_cnt > 1) /* is xor? */ - dir = DMA_BIDIRECTIONAL; - else - dir = DMA_FROM_DEVICE; - - dma_unmap_page(dev, dest, len, dir); - } - - if (!(flags & DMA_COMPL_SKIP_SRC_UNMAP)) { - while (src_cnt--) { - addr = iop_desc_get_src_addr(unmap, - iop_chan, - src_cnt); - if (addr == dest) - continue; - dma_unmap_page(dev, addr, len, - DMA_TO_DEVICE); - } - } - desc->group_head = NULL; + if (iop_desc_is_pq(desc)) + iop_desc_unmap_pq(iop_chan, desc); + else + iop_desc_unmap(iop_chan, desc); } } /* run dependent operations */ - dma_run_dependencies(&desc->async_tx); + dma_run_dependencies(tx); return cookie; } @@ -287,7 +333,12 @@ static void iop_adma_tasklet(unsigned long data) { struct iop_adma_chan *iop_chan = (struct iop_adma_chan *) data; - spin_lock(&iop_chan->lock); + /* lockdep will flag depedency submissions as potentially + * recursive locking, this is not the case as a dependency + * submission will never recurse a channels submit routine. + * There are checks in async_tx.c to prevent this. + */ + spin_lock_nested(&iop_chan->lock, SINGLE_DEPTH_NESTING); __iop_adma_slot_cleanup(iop_chan); spin_unlock(&iop_chan->lock); } @@ -370,7 +421,7 @@ retry: } alloc_tail->group_head = alloc_start; alloc_tail->async_tx.cookie = -EBUSY; - list_splice(&chain, &alloc_tail->async_tx.tx_list); + list_splice(&chain, &alloc_tail->tx_list); iop_chan->last_used = last_used; iop_desc_clear_next_desc(alloc_start); iop_desc_clear_next_desc(alloc_tail); @@ -429,7 +480,7 @@ iop_adma_tx_submit(struct dma_async_tx_descriptor *tx) old_chain_tail = list_entry(iop_chan->chain.prev, struct iop_adma_desc_slot, chain_node); - list_splice_init(&sw_desc->async_tx.tx_list, + list_splice_init(&sw_desc->tx_list, &old_chain_tail->chain_node); /* fix up the hardware chain */ @@ -496,6 +547,7 @@ static int iop_adma_alloc_chan_resources(struct dma_chan *chan) dma_async_tx_descriptor_init(&slot->async_tx, chan); slot->async_tx.tx_submit = iop_adma_tx_submit; + INIT_LIST_HEAD(&slot->tx_list); INIT_LIST_HEAD(&slot->chain_node); INIT_LIST_HEAD(&slot->slot_node); hw_desc = (char *) iop_chan->device->dma_desc_pool; @@ -660,9 +712,9 @@ iop_adma_prep_dma_xor(struct dma_chan *chan, dma_addr_t dma_dest, } static struct dma_async_tx_descriptor * -iop_adma_prep_dma_zero_sum(struct dma_chan *chan, dma_addr_t *dma_src, - unsigned int src_cnt, size_t len, u32 *result, - unsigned long flags) +iop_adma_prep_dma_xor_val(struct dma_chan *chan, dma_addr_t *dma_src, + unsigned int src_cnt, size_t len, u32 *result, + unsigned long flags) { struct iop_adma_chan *iop_chan = to_iop_adma_chan(chan); struct iop_adma_desc_slot *sw_desc, *grp_start; @@ -696,6 +748,118 @@ iop_adma_prep_dma_zero_sum(struct dma_chan *chan, dma_addr_t *dma_src, return sw_desc ? &sw_desc->async_tx : NULL; } +static struct dma_async_tx_descriptor * +iop_adma_prep_dma_pq(struct dma_chan *chan, dma_addr_t *dst, dma_addr_t *src, + unsigned int src_cnt, const unsigned char *scf, size_t len, + unsigned long flags) +{ + struct iop_adma_chan *iop_chan = to_iop_adma_chan(chan); + struct iop_adma_desc_slot *sw_desc, *g; + int slot_cnt, slots_per_op; + int continue_srcs; + + if (unlikely(!len)) + return NULL; + BUG_ON(len > IOP_ADMA_XOR_MAX_BYTE_COUNT); + + dev_dbg(iop_chan->device->common.dev, + "%s src_cnt: %d len: %u flags: %lx\n", + __func__, src_cnt, len, flags); + + if (dmaf_p_disabled_continue(flags)) + continue_srcs = 1+src_cnt; + else if (dmaf_continue(flags)) + continue_srcs = 3+src_cnt; + else + continue_srcs = 0+src_cnt; + + spin_lock_bh(&iop_chan->lock); + slot_cnt = iop_chan_pq_slot_count(len, continue_srcs, &slots_per_op); + sw_desc = iop_adma_alloc_slots(iop_chan, slot_cnt, slots_per_op); + if (sw_desc) { + int i; + + g = sw_desc->group_head; + iop_desc_set_byte_count(g, iop_chan, len); + + /* even if P is disabled its destination address (bits + * [3:0]) must match Q. It is ok if P points to an + * invalid address, it won't be written. + */ + if (flags & DMA_PREP_PQ_DISABLE_P) + dst[0] = dst[1] & 0x7; + + iop_desc_set_pq_addr(g, dst); + sw_desc->unmap_src_cnt = src_cnt; + sw_desc->unmap_len = len; + sw_desc->async_tx.flags = flags; + for (i = 0; i < src_cnt; i++) + iop_desc_set_pq_src_addr(g, i, src[i], scf[i]); + + /* if we are continuing a previous operation factor in + * the old p and q values, see the comment for dma_maxpq + * in include/linux/dmaengine.h + */ + if (dmaf_p_disabled_continue(flags)) + iop_desc_set_pq_src_addr(g, i++, dst[1], 1); + else if (dmaf_continue(flags)) { + iop_desc_set_pq_src_addr(g, i++, dst[0], 0); + iop_desc_set_pq_src_addr(g, i++, dst[1], 1); + iop_desc_set_pq_src_addr(g, i++, dst[1], 0); + } + iop_desc_init_pq(g, i, flags); + } + spin_unlock_bh(&iop_chan->lock); + + return sw_desc ? &sw_desc->async_tx : NULL; +} + +static struct dma_async_tx_descriptor * +iop_adma_prep_dma_pq_val(struct dma_chan *chan, dma_addr_t *pq, dma_addr_t *src, + unsigned int src_cnt, const unsigned char *scf, + size_t len, enum sum_check_flags *pqres, + unsigned long flags) +{ + struct iop_adma_chan *iop_chan = to_iop_adma_chan(chan); + struct iop_adma_desc_slot *sw_desc, *g; + int slot_cnt, slots_per_op; + + if (unlikely(!len)) + return NULL; + BUG_ON(len > IOP_ADMA_XOR_MAX_BYTE_COUNT); + + dev_dbg(iop_chan->device->common.dev, "%s src_cnt: %d len: %u\n", + __func__, src_cnt, len); + + spin_lock_bh(&iop_chan->lock); + slot_cnt = iop_chan_pq_zero_sum_slot_count(len, src_cnt + 2, &slots_per_op); + sw_desc = iop_adma_alloc_slots(iop_chan, slot_cnt, slots_per_op); + if (sw_desc) { + /* for validate operations p and q are tagged onto the + * end of the source list + */ + int pq_idx = src_cnt; + + g = sw_desc->group_head; + iop_desc_init_pq_zero_sum(g, src_cnt+2, flags); + iop_desc_set_pq_zero_sum_byte_count(g, len); + g->pq_check_result = pqres; + pr_debug("\t%s: g->pq_check_result: %p\n", + __func__, g->pq_check_result); + sw_desc->unmap_src_cnt = src_cnt+2; + sw_desc->unmap_len = len; + sw_desc->async_tx.flags = flags; + while (src_cnt--) + iop_desc_set_pq_zero_sum_src_addr(g, src_cnt, + src[src_cnt], + scf[src_cnt]); + iop_desc_set_pq_zero_sum_addr(g, pq_idx, src); + } + spin_unlock_bh(&iop_chan->lock); + + return sw_desc ? &sw_desc->async_tx : NULL; +} + static void iop_adma_free_chan_resources(struct dma_chan *chan) { struct iop_adma_chan *iop_chan = to_iop_adma_chan(chan); @@ -906,7 +1070,7 @@ out: #define IOP_ADMA_NUM_SRC_TEST 4 /* must be <= 15 */ static int __devinit -iop_adma_xor_zero_sum_self_test(struct iop_adma_device *device) +iop_adma_xor_val_self_test(struct iop_adma_device *device) { int i, src_idx; struct page *dest; @@ -1002,7 +1166,7 @@ iop_adma_xor_zero_sum_self_test(struct iop_adma_device *device) PAGE_SIZE, DMA_TO_DEVICE); /* skip zero sum if the capability is not present */ - if (!dma_has_cap(DMA_ZERO_SUM, dma_chan->device->cap_mask)) + if (!dma_has_cap(DMA_XOR_VAL, dma_chan->device->cap_mask)) goto free_resources; /* zero sum the sources with the destintation page */ @@ -1016,10 +1180,10 @@ iop_adma_xor_zero_sum_self_test(struct iop_adma_device *device) dma_srcs[i] = dma_map_page(dma_chan->device->dev, zero_sum_srcs[i], 0, PAGE_SIZE, DMA_TO_DEVICE); - tx = iop_adma_prep_dma_zero_sum(dma_chan, dma_srcs, - IOP_ADMA_NUM_SRC_TEST + 1, PAGE_SIZE, - &zero_sum_result, - DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + tx = iop_adma_prep_dma_xor_val(dma_chan, dma_srcs, + IOP_ADMA_NUM_SRC_TEST + 1, PAGE_SIZE, + &zero_sum_result, + DMA_PREP_INTERRUPT | DMA_CTRL_ACK); cookie = iop_adma_tx_submit(tx); iop_adma_issue_pending(dma_chan); @@ -1072,10 +1236,10 @@ iop_adma_xor_zero_sum_self_test(struct iop_adma_device *device) dma_srcs[i] = dma_map_page(dma_chan->device->dev, zero_sum_srcs[i], 0, PAGE_SIZE, DMA_TO_DEVICE); - tx = iop_adma_prep_dma_zero_sum(dma_chan, dma_srcs, - IOP_ADMA_NUM_SRC_TEST + 1, PAGE_SIZE, - &zero_sum_result, - DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + tx = iop_adma_prep_dma_xor_val(dma_chan, dma_srcs, + IOP_ADMA_NUM_SRC_TEST + 1, PAGE_SIZE, + &zero_sum_result, + DMA_PREP_INTERRUPT | DMA_CTRL_ACK); cookie = iop_adma_tx_submit(tx); iop_adma_issue_pending(dma_chan); @@ -1105,6 +1269,170 @@ out: return err; } +#ifdef CONFIG_MD_RAID6_PQ +static int __devinit +iop_adma_pq_zero_sum_self_test(struct iop_adma_device *device) +{ + /* combined sources, software pq results, and extra hw pq results */ + struct page *pq[IOP_ADMA_NUM_SRC_TEST+2+2]; + /* ptr to the extra hw pq buffers defined above */ + struct page **pq_hw = &pq[IOP_ADMA_NUM_SRC_TEST+2]; + /* address conversion buffers (dma_map / page_address) */ + void *pq_sw[IOP_ADMA_NUM_SRC_TEST+2]; + dma_addr_t pq_src[IOP_ADMA_NUM_SRC_TEST]; + dma_addr_t pq_dest[2]; + + int i; + struct dma_async_tx_descriptor *tx; + struct dma_chan *dma_chan; + dma_cookie_t cookie; + u32 zero_sum_result; + int err = 0; + struct device *dev; + + dev_dbg(device->common.dev, "%s\n", __func__); + + for (i = 0; i < ARRAY_SIZE(pq); i++) { + pq[i] = alloc_page(GFP_KERNEL); + if (!pq[i]) { + while (i--) + __free_page(pq[i]); + return -ENOMEM; + } + } + + /* Fill in src buffers */ + for (i = 0; i < IOP_ADMA_NUM_SRC_TEST; i++) { + pq_sw[i] = page_address(pq[i]); + memset(pq_sw[i], 0x11111111 * (1<<i), PAGE_SIZE); + } + pq_sw[i] = page_address(pq[i]); + pq_sw[i+1] = page_address(pq[i+1]); + + dma_chan = container_of(device->common.channels.next, + struct dma_chan, + device_node); + if (iop_adma_alloc_chan_resources(dma_chan) < 1) { + err = -ENODEV; + goto out; + } + + dev = dma_chan->device->dev; + + /* initialize the dests */ + memset(page_address(pq_hw[0]), 0 , PAGE_SIZE); + memset(page_address(pq_hw[1]), 0 , PAGE_SIZE); + + /* test pq */ + pq_dest[0] = dma_map_page(dev, pq_hw[0], 0, PAGE_SIZE, DMA_FROM_DEVICE); + pq_dest[1] = dma_map_page(dev, pq_hw[1], 0, PAGE_SIZE, DMA_FROM_DEVICE); + for (i = 0; i < IOP_ADMA_NUM_SRC_TEST; i++) + pq_src[i] = dma_map_page(dev, pq[i], 0, PAGE_SIZE, + DMA_TO_DEVICE); + + tx = iop_adma_prep_dma_pq(dma_chan, pq_dest, pq_src, + IOP_ADMA_NUM_SRC_TEST, (u8 *)raid6_gfexp, + PAGE_SIZE, + DMA_PREP_INTERRUPT | + DMA_CTRL_ACK); + + cookie = iop_adma_tx_submit(tx); + iop_adma_issue_pending(dma_chan); + msleep(8); + + if (iop_adma_is_complete(dma_chan, cookie, NULL, NULL) != + DMA_SUCCESS) { + dev_err(dev, "Self-test pq timed out, disabling\n"); + err = -ENODEV; + goto free_resources; + } + + raid6_call.gen_syndrome(IOP_ADMA_NUM_SRC_TEST+2, PAGE_SIZE, pq_sw); + + if (memcmp(pq_sw[IOP_ADMA_NUM_SRC_TEST], + page_address(pq_hw[0]), PAGE_SIZE) != 0) { + dev_err(dev, "Self-test p failed compare, disabling\n"); + err = -ENODEV; + goto free_resources; + } + if (memcmp(pq_sw[IOP_ADMA_NUM_SRC_TEST+1], + page_address(pq_hw[1]), PAGE_SIZE) != 0) { + dev_err(dev, "Self-test q failed compare, disabling\n"); + err = -ENODEV; + goto free_resources; + } + + /* test correct zero sum using the software generated pq values */ + for (i = 0; i < IOP_ADMA_NUM_SRC_TEST + 2; i++) + pq_src[i] = dma_map_page(dev, pq[i], 0, PAGE_SIZE, + DMA_TO_DEVICE); + + zero_sum_result = ~0; + tx = iop_adma_prep_dma_pq_val(dma_chan, &pq_src[IOP_ADMA_NUM_SRC_TEST], + pq_src, IOP_ADMA_NUM_SRC_TEST, + raid6_gfexp, PAGE_SIZE, &zero_sum_result, + DMA_PREP_INTERRUPT|DMA_CTRL_ACK); + + cookie = iop_adma_tx_submit(tx); + iop_adma_issue_pending(dma_chan); + msleep(8); + + if (iop_adma_is_complete(dma_chan, cookie, NULL, NULL) != + DMA_SUCCESS) { + dev_err(dev, "Self-test pq-zero-sum timed out, disabling\n"); + err = -ENODEV; + goto free_resources; + } + + if (zero_sum_result != 0) { + dev_err(dev, "Self-test pq-zero-sum failed to validate: %x\n", + zero_sum_result); + err = -ENODEV; + goto free_resources; + } + + /* test incorrect zero sum */ + i = IOP_ADMA_NUM_SRC_TEST; + memset(pq_sw[i] + 100, 0, 100); + memset(pq_sw[i+1] + 200, 0, 200); + for (i = 0; i < IOP_ADMA_NUM_SRC_TEST + 2; i++) + pq_src[i] = dma_map_page(dev, pq[i], 0, PAGE_SIZE, + DMA_TO_DEVICE); + + zero_sum_result = 0; + tx = iop_adma_prep_dma_pq_val(dma_chan, &pq_src[IOP_ADMA_NUM_SRC_TEST], + pq_src, IOP_ADMA_NUM_SRC_TEST, + raid6_gfexp, PAGE_SIZE, &zero_sum_result, + DMA_PREP_INTERRUPT|DMA_CTRL_ACK); + + cookie = iop_adma_tx_submit(tx); + iop_adma_issue_pending(dma_chan); + msleep(8); + + if (iop_adma_is_complete(dma_chan, cookie, NULL, NULL) != + DMA_SUCCESS) { + dev_err(dev, "Self-test !pq-zero-sum timed out, disabling\n"); + err = -ENODEV; + goto free_resources; + } + + if (zero_sum_result != (SUM_CHECK_P_RESULT | SUM_CHECK_Q_RESULT)) { + dev_err(dev, "Self-test !pq-zero-sum failed to validate: %x\n", + zero_sum_result); + err = -ENODEV; + goto free_resources; + } + +free_resources: + iop_adma_free_chan_resources(dma_chan); +out: + i = ARRAY_SIZE(pq); + while (i--) + __free_page(pq[i]); + return err; +} +#endif + static int __devexit iop_adma_remove(struct platform_device *dev) { struct iop_adma_device *device = platform_get_drvdata(dev); @@ -1192,9 +1520,16 @@ static int __devinit iop_adma_probe(struct platform_device *pdev) dma_dev->max_xor = iop_adma_get_max_xor(); dma_dev->device_prep_dma_xor = iop_adma_prep_dma_xor; } - if (dma_has_cap(DMA_ZERO_SUM, dma_dev->cap_mask)) - dma_dev->device_prep_dma_zero_sum = - iop_adma_prep_dma_zero_sum; + if (dma_has_cap(DMA_XOR_VAL, dma_dev->cap_mask)) + dma_dev->device_prep_dma_xor_val = + iop_adma_prep_dma_xor_val; + if (dma_has_cap(DMA_PQ, dma_dev->cap_mask)) { + dma_set_maxpq(dma_dev, iop_adma_get_max_pq(), 0); + dma_dev->device_prep_dma_pq = iop_adma_prep_dma_pq; + } + if (dma_has_cap(DMA_PQ_VAL, dma_dev->cap_mask)) + dma_dev->device_prep_dma_pq_val = + iop_adma_prep_dma_pq_val; if (dma_has_cap(DMA_INTERRUPT, dma_dev->cap_mask)) dma_dev->device_prep_dma_interrupt = iop_adma_prep_dma_interrupt; @@ -1248,23 +1583,35 @@ static int __devinit iop_adma_probe(struct platform_device *pdev) } if (dma_has_cap(DMA_XOR, dma_dev->cap_mask) || - dma_has_cap(DMA_MEMSET, dma_dev->cap_mask)) { - ret = iop_adma_xor_zero_sum_self_test(adev); + dma_has_cap(DMA_MEMSET, dma_dev->cap_mask)) { + ret = iop_adma_xor_val_self_test(adev); dev_dbg(&pdev->dev, "xor self test returned %d\n", ret); if (ret) goto err_free_iop_chan; } + if (dma_has_cap(DMA_PQ, dma_dev->cap_mask) && + dma_has_cap(DMA_PQ_VAL, dma_dev->cap_mask)) { + #ifdef CONFIG_MD_RAID6_PQ + ret = iop_adma_pq_zero_sum_self_test(adev); + dev_dbg(&pdev->dev, "pq self test returned %d\n", ret); + #else + /* can not test raid6, so do not publish capability */ + dma_cap_clear(DMA_PQ, dma_dev->cap_mask); + dma_cap_clear(DMA_PQ_VAL, dma_dev->cap_mask); + ret = 0; + #endif + if (ret) + goto err_free_iop_chan; + } + dev_printk(KERN_INFO, &pdev->dev, "Intel(R) IOP: " - "( %s%s%s%s%s%s%s%s%s%s)\n", - dma_has_cap(DMA_PQ_XOR, dma_dev->cap_mask) ? "pq_xor " : "", - dma_has_cap(DMA_PQ_UPDATE, dma_dev->cap_mask) ? "pq_update " : "", - dma_has_cap(DMA_PQ_ZERO_SUM, dma_dev->cap_mask) ? "pq_zero_sum " : "", + "( %s%s%s%s%s%s%s)\n", + dma_has_cap(DMA_PQ, dma_dev->cap_mask) ? "pq " : "", + dma_has_cap(DMA_PQ_VAL, dma_dev->cap_mask) ? "pq_val " : "", dma_has_cap(DMA_XOR, dma_dev->cap_mask) ? "xor " : "", - dma_has_cap(DMA_DUAL_XOR, dma_dev->cap_mask) ? "dual_xor " : "", - dma_has_cap(DMA_ZERO_SUM, dma_dev->cap_mask) ? "xor_zero_sum " : "", + dma_has_cap(DMA_XOR_VAL, dma_dev->cap_mask) ? "xor_val " : "", dma_has_cap(DMA_MEMSET, dma_dev->cap_mask) ? "fill " : "", - dma_has_cap(DMA_MEMCPY_CRC32C, dma_dev->cap_mask) ? "cpy+crc " : "", dma_has_cap(DMA_MEMCPY, dma_dev->cap_mask) ? "cpy " : "", dma_has_cap(DMA_INTERRUPT, dma_dev->cap_mask) ? "intr " : ""); @@ -1296,7 +1643,7 @@ static void iop_chan_start_null_memcpy(struct iop_adma_chan *iop_chan) if (sw_desc) { grp_start = sw_desc->group_head; - list_splice_init(&sw_desc->async_tx.tx_list, &iop_chan->chain); + list_splice_init(&sw_desc->tx_list, &iop_chan->chain); async_tx_ack(&sw_desc->async_tx); iop_desc_init_memcpy(grp_start, 0); iop_desc_set_byte_count(grp_start, iop_chan, 0); @@ -1352,7 +1699,7 @@ static void iop_chan_start_null_xor(struct iop_adma_chan *iop_chan) sw_desc = iop_adma_alloc_slots(iop_chan, slot_cnt, slots_per_op); if (sw_desc) { grp_start = sw_desc->group_head; - list_splice_init(&sw_desc->async_tx.tx_list, &iop_chan->chain); + list_splice_init(&sw_desc->tx_list, &iop_chan->chain); async_tx_ack(&sw_desc->async_tx); iop_desc_init_null_xor(grp_start, 2, 0); iop_desc_set_byte_count(grp_start, iop_chan, 0); diff --git a/drivers/dma/iovlock.c b/drivers/dma/iovlock.c index 9f6fe46a9b87..c0a272c73682 100644 --- a/drivers/dma/iovlock.c +++ b/drivers/dma/iovlock.c @@ -183,6 +183,11 @@ dma_cookie_t dma_memcpy_to_iovec(struct dma_chan *chan, struct iovec *iov, iov_byte_offset, kdata, copy); + /* poll for a descriptor slot */ + if (unlikely(dma_cookie < 0)) { + dma_async_issue_pending(chan); + continue; + } len -= copy; iov[iovec_idx].iov_len -= copy; @@ -248,6 +253,11 @@ dma_cookie_t dma_memcpy_pg_to_iovec(struct dma_chan *chan, struct iovec *iov, page, offset, copy); + /* poll for a descriptor slot */ + if (unlikely(dma_cookie < 0)) { + dma_async_issue_pending(chan); + continue; + } len -= copy; iov[iovec_idx].iov_len -= copy; diff --git a/drivers/dma/mv_xor.c b/drivers/dma/mv_xor.c index 3f23eabe09f2..466ab10c1ff1 100644 --- a/drivers/dma/mv_xor.c +++ b/drivers/dma/mv_xor.c @@ -517,7 +517,7 @@ retry: } alloc_tail->group_head = alloc_start; alloc_tail->async_tx.cookie = -EBUSY; - list_splice(&chain, &alloc_tail->async_tx.tx_list); + list_splice(&chain, &alloc_tail->tx_list); mv_chan->last_used = last_used; mv_desc_clear_next_desc(alloc_start); mv_desc_clear_next_desc(alloc_tail); @@ -565,14 +565,14 @@ mv_xor_tx_submit(struct dma_async_tx_descriptor *tx) cookie = mv_desc_assign_cookie(mv_chan, sw_desc); if (list_empty(&mv_chan->chain)) - list_splice_init(&sw_desc->async_tx.tx_list, &mv_chan->chain); + list_splice_init(&sw_desc->tx_list, &mv_chan->chain); else { new_hw_chain = 0; old_chain_tail = list_entry(mv_chan->chain.prev, struct mv_xor_desc_slot, chain_node); - list_splice_init(&grp_start->async_tx.tx_list, + list_splice_init(&grp_start->tx_list, &old_chain_tail->chain_node); if (!mv_can_chain(grp_start)) @@ -632,6 +632,7 @@ static int mv_xor_alloc_chan_resources(struct dma_chan *chan) slot->async_tx.tx_submit = mv_xor_tx_submit; INIT_LIST_HEAD(&slot->chain_node); INIT_LIST_HEAD(&slot->slot_node); + INIT_LIST_HEAD(&slot->tx_list); hw_desc = (char *) mv_chan->device->dma_desc_pool; slot->async_tx.phys = (dma_addr_t) &hw_desc[idx * MV_XOR_SLOT_SIZE]; diff --git a/drivers/dma/mv_xor.h b/drivers/dma/mv_xor.h index 06cafe1ef521..977b592e976b 100644 --- a/drivers/dma/mv_xor.h +++ b/drivers/dma/mv_xor.h @@ -126,9 +126,8 @@ struct mv_xor_chan { * @idx: pool index * @unmap_src_cnt: number of xor sources * @unmap_len: transaction bytecount + * @tx_list: list of slots that make up a multi-descriptor transaction * @async_tx: support for the async_tx api - * @group_list: list of slots that make up a multi-descriptor transaction - * for example transfer lengths larger than the supported hw max * @xor_check_result: result of zero sum * @crc32_result: result crc calculation */ @@ -145,6 +144,7 @@ struct mv_xor_desc_slot { u16 unmap_src_cnt; u32 value; size_t unmap_len; + struct list_head tx_list; struct dma_async_tx_descriptor async_tx; union { u32 *xor_check_result; diff --git a/drivers/dma/shdma.c b/drivers/dma/shdma.c new file mode 100644 index 000000000000..034ecf0ace03 --- /dev/null +++ b/drivers/dma/shdma.c @@ -0,0 +1,784 @@ +/* + * Renesas SuperH DMA Engine support + * + * base is drivers/dma/flsdma.c + * + * Copyright (C) 2009 Nobuhiro Iwamatsu <iwamatsu.nobuhiro@renesas.com> + * Copyright (C) 2009 Renesas Solutions, Inc. All rights reserved. + * Copyright (C) 2007 Freescale Semiconductor, Inc. All rights reserved. + * + * This 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. + * + * - DMA of SuperH does not have Hardware DMA chain mode. + * - MAX DMA size is 16MB. + * + */ + +#include <linux/init.h> +#include <linux/module.h> +#include <linux/interrupt.h> +#include <linux/dmaengine.h> +#include <linux/delay.h> +#include <linux/dma-mapping.h> +#include <linux/dmapool.h> +#include <linux/platform_device.h> +#include <cpu/dma.h> +#include <asm/dma-sh.h> +#include "shdma.h" + +/* DMA descriptor control */ +#define DESC_LAST (-1) +#define DESC_COMP (1) +#define DESC_NCOMP (0) + +#define NR_DESCS_PER_CHANNEL 32 +/* + * Define the default configuration for dual address memory-memory transfer. + * The 0x400 value represents auto-request, external->external. + * + * And this driver set 4byte burst mode. + * If you want to change mode, you need to change RS_DEFAULT of value. + * (ex 1byte burst mode -> (RS_DUAL & ~TS_32) + */ +#define RS_DEFAULT (RS_DUAL) + +#define SH_DMAC_CHAN_BASE(id) (dma_base_addr[id]) +static void sh_dmae_writel(struct sh_dmae_chan *sh_dc, u32 data, u32 reg) +{ + ctrl_outl(data, (SH_DMAC_CHAN_BASE(sh_dc->id) + reg)); +} + +static u32 sh_dmae_readl(struct sh_dmae_chan *sh_dc, u32 reg) +{ + return ctrl_inl((SH_DMAC_CHAN_BASE(sh_dc->id) + reg)); +} + +static void dmae_init(struct sh_dmae_chan *sh_chan) +{ + u32 chcr = RS_DEFAULT; /* default is DUAL mode */ + sh_dmae_writel(sh_chan, chcr, CHCR); +} + +/* + * Reset DMA controller + * + * SH7780 has two DMAOR register + */ +static void sh_dmae_ctl_stop(int id) +{ + unsigned short dmaor = dmaor_read_reg(id); + + dmaor &= ~(DMAOR_NMIF | DMAOR_AE); + dmaor_write_reg(id, dmaor); +} + +static int sh_dmae_rst(int id) +{ + unsigned short dmaor; + + sh_dmae_ctl_stop(id); + dmaor = (dmaor_read_reg(id)|DMAOR_INIT); + + dmaor_write_reg(id, dmaor); + if ((dmaor_read_reg(id) & (DMAOR_AE | DMAOR_NMIF))) { + pr_warning(KERN_ERR "dma-sh: Can't initialize DMAOR.\n"); + return -EINVAL; + } + return 0; +} + +static int dmae_is_idle(struct sh_dmae_chan *sh_chan) +{ + u32 chcr = sh_dmae_readl(sh_chan, CHCR); + if (chcr & CHCR_DE) { + if (!(chcr & CHCR_TE)) + return -EBUSY; /* working */ + } + return 0; /* waiting */ +} + +static inline unsigned int calc_xmit_shift(struct sh_dmae_chan *sh_chan) +{ + u32 chcr = sh_dmae_readl(sh_chan, CHCR); + return ts_shift[(chcr & CHCR_TS_MASK) >> CHCR_TS_SHIFT]; +} + +static void dmae_set_reg(struct sh_dmae_chan *sh_chan, struct sh_dmae_regs hw) +{ + sh_dmae_writel(sh_chan, hw.sar, SAR); + sh_dmae_writel(sh_chan, hw.dar, DAR); + sh_dmae_writel(sh_chan, + (hw.tcr >> calc_xmit_shift(sh_chan)), TCR); +} + +static void dmae_start(struct sh_dmae_chan *sh_chan) +{ + u32 chcr = sh_dmae_readl(sh_chan, CHCR); + + chcr |= (CHCR_DE|CHCR_IE); + sh_dmae_writel(sh_chan, chcr, CHCR); +} + +static void dmae_halt(struct sh_dmae_chan *sh_chan) +{ + u32 chcr = sh_dmae_readl(sh_chan, CHCR); + + chcr &= ~(CHCR_DE | CHCR_TE | CHCR_IE); + sh_dmae_writel(sh_chan, chcr, CHCR); +} + +static int dmae_set_chcr(struct sh_dmae_chan *sh_chan, u32 val) +{ + int ret = dmae_is_idle(sh_chan); + /* When DMA was working, can not set data to CHCR */ + if (ret) + return ret; + + sh_dmae_writel(sh_chan, val, CHCR); + return 0; +} + +#define DMARS1_ADDR 0x04 +#define DMARS2_ADDR 0x08 +#define DMARS_SHIFT 8 +#define DMARS_CHAN_MSK 0x01 +static int dmae_set_dmars(struct sh_dmae_chan *sh_chan, u16 val) +{ + u32 addr; + int shift = 0; + int ret = dmae_is_idle(sh_chan); + if (ret) + return ret; + + if (sh_chan->id & DMARS_CHAN_MSK) + shift = DMARS_SHIFT; + + switch (sh_chan->id) { + /* DMARS0 */ + case 0: + case 1: + addr = SH_DMARS_BASE; + break; + /* DMARS1 */ + case 2: + case 3: + addr = (SH_DMARS_BASE + DMARS1_ADDR); + break; + /* DMARS2 */ + case 4: + case 5: + addr = (SH_DMARS_BASE + DMARS2_ADDR); + break; + default: + return -EINVAL; + } + + ctrl_outw((val << shift) | + (ctrl_inw(addr) & (shift ? 0xFF00 : 0x00FF)), + addr); + + return 0; +} + +static dma_cookie_t sh_dmae_tx_submit(struct dma_async_tx_descriptor *tx) +{ + struct sh_desc *desc = tx_to_sh_desc(tx); + struct sh_dmae_chan *sh_chan = to_sh_chan(tx->chan); + dma_cookie_t cookie; + + spin_lock_bh(&sh_chan->desc_lock); + + cookie = sh_chan->common.cookie; + cookie++; + if (cookie < 0) + cookie = 1; + + /* If desc only in the case of 1 */ + if (desc->async_tx.cookie != -EBUSY) + desc->async_tx.cookie = cookie; + sh_chan->common.cookie = desc->async_tx.cookie; + + list_splice_init(&desc->tx_list, sh_chan->ld_queue.prev); + + spin_unlock_bh(&sh_chan->desc_lock); + + return cookie; +} + +static struct sh_desc *sh_dmae_get_desc(struct sh_dmae_chan *sh_chan) +{ + struct sh_desc *desc, *_desc, *ret = NULL; + + spin_lock_bh(&sh_chan->desc_lock); + list_for_each_entry_safe(desc, _desc, &sh_chan->ld_free, node) { + if (async_tx_test_ack(&desc->async_tx)) { + list_del(&desc->node); + ret = desc; + break; + } + } + spin_unlock_bh(&sh_chan->desc_lock); + + return ret; +} + +static void sh_dmae_put_desc(struct sh_dmae_chan *sh_chan, struct sh_desc *desc) +{ + if (desc) { + spin_lock_bh(&sh_chan->desc_lock); + + list_splice_init(&desc->tx_list, &sh_chan->ld_free); + list_add(&desc->node, &sh_chan->ld_free); + + spin_unlock_bh(&sh_chan->desc_lock); + } +} + +static int sh_dmae_alloc_chan_resources(struct dma_chan *chan) +{ + struct sh_dmae_chan *sh_chan = to_sh_chan(chan); + struct sh_desc *desc; + + spin_lock_bh(&sh_chan->desc_lock); + while (sh_chan->descs_allocated < NR_DESCS_PER_CHANNEL) { + spin_unlock_bh(&sh_chan->desc_lock); + desc = kzalloc(sizeof(struct sh_desc), GFP_KERNEL); + if (!desc) { + spin_lock_bh(&sh_chan->desc_lock); + break; + } + dma_async_tx_descriptor_init(&desc->async_tx, + &sh_chan->common); + desc->async_tx.tx_submit = sh_dmae_tx_submit; + desc->async_tx.flags = DMA_CTRL_ACK; + INIT_LIST_HEAD(&desc->tx_list); + sh_dmae_put_desc(sh_chan, desc); + + spin_lock_bh(&sh_chan->desc_lock); + sh_chan->descs_allocated++; + } + spin_unlock_bh(&sh_chan->desc_lock); + + return sh_chan->descs_allocated; +} + +/* + * sh_dma_free_chan_resources - Free all resources of the channel. + */ +static void sh_dmae_free_chan_resources(struct dma_chan *chan) +{ + struct sh_dmae_chan *sh_chan = to_sh_chan(chan); + struct sh_desc *desc, *_desc; + LIST_HEAD(list); + + BUG_ON(!list_empty(&sh_chan->ld_queue)); + spin_lock_bh(&sh_chan->desc_lock); + + list_splice_init(&sh_chan->ld_free, &list); + sh_chan->descs_allocated = 0; + + spin_unlock_bh(&sh_chan->desc_lock); + + list_for_each_entry_safe(desc, _desc, &list, node) + kfree(desc); +} + +static struct dma_async_tx_descriptor *sh_dmae_prep_memcpy( + struct dma_chan *chan, dma_addr_t dma_dest, dma_addr_t dma_src, + size_t len, unsigned long flags) +{ + struct sh_dmae_chan *sh_chan; + struct sh_desc *first = NULL, *prev = NULL, *new; + size_t copy_size; + + if (!chan) + return NULL; + + if (!len) + return NULL; + + sh_chan = to_sh_chan(chan); + + do { + /* Allocate the link descriptor from DMA pool */ + new = sh_dmae_get_desc(sh_chan); + if (!new) { + dev_err(sh_chan->dev, + "No free memory for link descriptor\n"); + goto err_get_desc; + } + + copy_size = min(len, (size_t)SH_DMA_TCR_MAX); + + new->hw.sar = dma_src; + new->hw.dar = dma_dest; + new->hw.tcr = copy_size; + if (!first) + first = new; + + new->mark = DESC_NCOMP; + async_tx_ack(&new->async_tx); + + prev = new; + len -= copy_size; + dma_src += copy_size; + dma_dest += copy_size; + /* Insert the link descriptor to the LD ring */ + list_add_tail(&new->node, &first->tx_list); + } while (len); + + new->async_tx.flags = flags; /* client is in control of this ack */ + new->async_tx.cookie = -EBUSY; /* Last desc */ + + return &first->async_tx; + +err_get_desc: + sh_dmae_put_desc(sh_chan, first); + return NULL; + +} + +/* + * sh_chan_ld_cleanup - Clean up link descriptors + * + * This function clean up the ld_queue of DMA channel. + */ +static void sh_dmae_chan_ld_cleanup(struct sh_dmae_chan *sh_chan) +{ + struct sh_desc *desc, *_desc; + + spin_lock_bh(&sh_chan->desc_lock); + list_for_each_entry_safe(desc, _desc, &sh_chan->ld_queue, node) { + dma_async_tx_callback callback; + void *callback_param; + + /* non send data */ + if (desc->mark == DESC_NCOMP) + break; + + /* send data sesc */ + callback = desc->async_tx.callback; + callback_param = desc->async_tx.callback_param; + + /* Remove from ld_queue list */ + list_splice_init(&desc->tx_list, &sh_chan->ld_free); + + dev_dbg(sh_chan->dev, "link descriptor %p will be recycle.\n", + desc); + + list_move(&desc->node, &sh_chan->ld_free); + /* Run the link descriptor callback function */ + if (callback) { + spin_unlock_bh(&sh_chan->desc_lock); + dev_dbg(sh_chan->dev, "link descriptor %p callback\n", + desc); + callback(callback_param); + spin_lock_bh(&sh_chan->desc_lock); + } + } + spin_unlock_bh(&sh_chan->desc_lock); +} + +static void sh_chan_xfer_ld_queue(struct sh_dmae_chan *sh_chan) +{ + struct list_head *ld_node; + struct sh_dmae_regs hw; + + /* DMA work check */ + if (dmae_is_idle(sh_chan)) + return; + + /* Find the first un-transfer desciptor */ + for (ld_node = sh_chan->ld_queue.next; + (ld_node != &sh_chan->ld_queue) + && (to_sh_desc(ld_node)->mark == DESC_COMP); + ld_node = ld_node->next) + cpu_relax(); + + if (ld_node != &sh_chan->ld_queue) { + /* Get the ld start address from ld_queue */ + hw = to_sh_desc(ld_node)->hw; + dmae_set_reg(sh_chan, hw); + dmae_start(sh_chan); + } +} + +static void sh_dmae_memcpy_issue_pending(struct dma_chan *chan) +{ + struct sh_dmae_chan *sh_chan = to_sh_chan(chan); + sh_chan_xfer_ld_queue(sh_chan); +} + +static enum dma_status sh_dmae_is_complete(struct dma_chan *chan, + dma_cookie_t cookie, + dma_cookie_t *done, + dma_cookie_t *used) +{ + struct sh_dmae_chan *sh_chan = to_sh_chan(chan); + dma_cookie_t last_used; + dma_cookie_t last_complete; + + sh_dmae_chan_ld_cleanup(sh_chan); + + last_used = chan->cookie; + last_complete = sh_chan->completed_cookie; + if (last_complete == -EBUSY) + last_complete = last_used; + + if (done) + *done = last_complete; + + if (used) + *used = last_used; + + return dma_async_is_complete(cookie, last_complete, last_used); +} + +static irqreturn_t sh_dmae_interrupt(int irq, void *data) +{ + irqreturn_t ret = IRQ_NONE; + struct sh_dmae_chan *sh_chan = (struct sh_dmae_chan *)data; + u32 chcr = sh_dmae_readl(sh_chan, CHCR); + + if (chcr & CHCR_TE) { + /* DMA stop */ + dmae_halt(sh_chan); + + ret = IRQ_HANDLED; + tasklet_schedule(&sh_chan->tasklet); + } + + return ret; +} + +#if defined(CONFIG_CPU_SH4) +static irqreturn_t sh_dmae_err(int irq, void *data) +{ + int err = 0; + struct sh_dmae_device *shdev = (struct sh_dmae_device *)data; + + /* IRQ Multi */ + if (shdev->pdata.mode & SHDMA_MIX_IRQ) { + int cnt = 0; + switch (irq) { +#if defined(DMTE6_IRQ) && defined(DMAE1_IRQ) + case DMTE6_IRQ: + cnt++; +#endif + case DMTE0_IRQ: + if (dmaor_read_reg(cnt) & (DMAOR_NMIF | DMAOR_AE)) { + disable_irq(irq); + return IRQ_HANDLED; + } + default: + return IRQ_NONE; + } + } else { + /* reset dma controller */ + err = sh_dmae_rst(0); + if (err) + return err; + if (shdev->pdata.mode & SHDMA_DMAOR1) { + err = sh_dmae_rst(1); + if (err) + return err; + } + disable_irq(irq); + return IRQ_HANDLED; + } +} +#endif + +static void dmae_do_tasklet(unsigned long data) +{ + struct sh_dmae_chan *sh_chan = (struct sh_dmae_chan *)data; + struct sh_desc *desc, *_desc, *cur_desc = NULL; + u32 sar_buf = sh_dmae_readl(sh_chan, SAR); + list_for_each_entry_safe(desc, _desc, + &sh_chan->ld_queue, node) { + if ((desc->hw.sar + desc->hw.tcr) == sar_buf) { + cur_desc = desc; + break; + } + } + + if (cur_desc) { + switch (cur_desc->async_tx.cookie) { + case 0: /* other desc data */ + break; + case -EBUSY: /* last desc */ + sh_chan->completed_cookie = + cur_desc->async_tx.cookie; + break; + default: /* first desc ( 0 < )*/ + sh_chan->completed_cookie = + cur_desc->async_tx.cookie - 1; + break; + } + cur_desc->mark = DESC_COMP; + } + /* Next desc */ + sh_chan_xfer_ld_queue(sh_chan); + sh_dmae_chan_ld_cleanup(sh_chan); +} + +static unsigned int get_dmae_irq(unsigned int id) +{ + unsigned int irq = 0; + if (id < ARRAY_SIZE(dmte_irq_map)) + irq = dmte_irq_map[id]; + return irq; +} + +static int __devinit sh_dmae_chan_probe(struct sh_dmae_device *shdev, int id) +{ + int err; + unsigned int irq = get_dmae_irq(id); + unsigned long irqflags = IRQF_DISABLED; + struct sh_dmae_chan *new_sh_chan; + + /* alloc channel */ + new_sh_chan = kzalloc(sizeof(struct sh_dmae_chan), GFP_KERNEL); + if (!new_sh_chan) { + dev_err(shdev->common.dev, "No free memory for allocating " + "dma channels!\n"); + return -ENOMEM; + } + + new_sh_chan->dev = shdev->common.dev; + new_sh_chan->id = id; + + /* Init DMA tasklet */ + tasklet_init(&new_sh_chan->tasklet, dmae_do_tasklet, + (unsigned long)new_sh_chan); + + /* Init the channel */ + dmae_init(new_sh_chan); + + spin_lock_init(&new_sh_chan->desc_lock); + + /* Init descripter manage list */ + INIT_LIST_HEAD(&new_sh_chan->ld_queue); + INIT_LIST_HEAD(&new_sh_chan->ld_free); + + /* copy struct dma_device */ + new_sh_chan->common.device = &shdev->common; + + /* Add the channel to DMA device channel list */ + list_add_tail(&new_sh_chan->common.device_node, + &shdev->common.channels); + shdev->common.chancnt++; + + if (shdev->pdata.mode & SHDMA_MIX_IRQ) { + irqflags = IRQF_SHARED; +#if defined(DMTE6_IRQ) + if (irq >= DMTE6_IRQ) + irq = DMTE6_IRQ; + else +#endif + irq = DMTE0_IRQ; + } + + snprintf(new_sh_chan->dev_id, sizeof(new_sh_chan->dev_id), + "sh-dmae%d", new_sh_chan->id); + + /* set up channel irq */ + err = request_irq(irq, &sh_dmae_interrupt, + irqflags, new_sh_chan->dev_id, new_sh_chan); + if (err) { + dev_err(shdev->common.dev, "DMA channel %d request_irq error " + "with return %d\n", id, err); + goto err_no_irq; + } + + /* CHCR register control function */ + new_sh_chan->set_chcr = dmae_set_chcr; + /* DMARS register control function */ + new_sh_chan->set_dmars = dmae_set_dmars; + + shdev->chan[id] = new_sh_chan; + return 0; + +err_no_irq: + /* remove from dmaengine device node */ + list_del(&new_sh_chan->common.device_node); + kfree(new_sh_chan); + return err; +} + +static void sh_dmae_chan_remove(struct sh_dmae_device *shdev) +{ + int i; + + for (i = shdev->common.chancnt - 1 ; i >= 0 ; i--) { + if (shdev->chan[i]) { + struct sh_dmae_chan *shchan = shdev->chan[i]; + if (!(shdev->pdata.mode & SHDMA_MIX_IRQ)) + free_irq(dmte_irq_map[i], shchan); + + list_del(&shchan->common.device_node); + kfree(shchan); + shdev->chan[i] = NULL; + } + } + shdev->common.chancnt = 0; +} + +static int __init sh_dmae_probe(struct platform_device *pdev) +{ + int err = 0, cnt, ecnt; + unsigned long irqflags = IRQF_DISABLED; +#if defined(CONFIG_CPU_SH4) + int eirq[] = { DMAE0_IRQ, +#if defined(DMAE1_IRQ) + DMAE1_IRQ +#endif + }; +#endif + struct sh_dmae_device *shdev; + + /* get platform data */ + if (!pdev->dev.platform_data) + return -ENODEV; + + shdev = kzalloc(sizeof(struct sh_dmae_device), GFP_KERNEL); + if (!shdev) { + dev_err(&pdev->dev, "No enough memory\n"); + return -ENOMEM; + } + + /* platform data */ + memcpy(&shdev->pdata, pdev->dev.platform_data, + sizeof(struct sh_dmae_pdata)); + + /* reset dma controller */ + err = sh_dmae_rst(0); + if (err) + goto rst_err; + + /* SH7780/85/23 has DMAOR1 */ + if (shdev->pdata.mode & SHDMA_DMAOR1) { + err = sh_dmae_rst(1); + if (err) + goto rst_err; + } + + INIT_LIST_HEAD(&shdev->common.channels); + + dma_cap_set(DMA_MEMCPY, shdev->common.cap_mask); + shdev->common.device_alloc_chan_resources + = sh_dmae_alloc_chan_resources; + shdev->common.device_free_chan_resources = sh_dmae_free_chan_resources; + shdev->common.device_prep_dma_memcpy = sh_dmae_prep_memcpy; + shdev->common.device_is_tx_complete = sh_dmae_is_complete; + shdev->common.device_issue_pending = sh_dmae_memcpy_issue_pending; + shdev->common.dev = &pdev->dev; + +#if defined(CONFIG_CPU_SH4) + /* Non Mix IRQ mode SH7722/SH7730 etc... */ + if (shdev->pdata.mode & SHDMA_MIX_IRQ) { + irqflags = IRQF_SHARED; + eirq[0] = DMTE0_IRQ; +#if defined(DMTE6_IRQ) && defined(DMAE1_IRQ) + eirq[1] = DMTE6_IRQ; +#endif + } + + for (ecnt = 0 ; ecnt < ARRAY_SIZE(eirq); ecnt++) { + err = request_irq(eirq[ecnt], sh_dmae_err, + irqflags, "DMAC Address Error", shdev); + if (err) { + dev_err(&pdev->dev, "DMA device request_irq" + "error (irq %d) with return %d\n", + eirq[ecnt], err); + goto eirq_err; + } + } +#endif /* CONFIG_CPU_SH4 */ + + /* Create DMA Channel */ + for (cnt = 0 ; cnt < MAX_DMA_CHANNELS ; cnt++) { + err = sh_dmae_chan_probe(shdev, cnt); + if (err) + goto chan_probe_err; + } + + platform_set_drvdata(pdev, shdev); + dma_async_device_register(&shdev->common); + + return err; + +chan_probe_err: + sh_dmae_chan_remove(shdev); + +eirq_err: + for (ecnt-- ; ecnt >= 0; ecnt--) + free_irq(eirq[ecnt], shdev); + +rst_err: + kfree(shdev); + + return err; +} + +static int __exit sh_dmae_remove(struct platform_device *pdev) +{ + struct sh_dmae_device *shdev = platform_get_drvdata(pdev); + + dma_async_device_unregister(&shdev->common); + + if (shdev->pdata.mode & SHDMA_MIX_IRQ) { + free_irq(DMTE0_IRQ, shdev); +#if defined(DMTE6_IRQ) + free_irq(DMTE6_IRQ, shdev); +#endif + } + + /* channel data remove */ + sh_dmae_chan_remove(shdev); + + if (!(shdev->pdata.mode & SHDMA_MIX_IRQ)) { + free_irq(DMAE0_IRQ, shdev); +#if defined(DMAE1_IRQ) + free_irq(DMAE1_IRQ, shdev); +#endif + } + kfree(shdev); + + return 0; +} + +static void sh_dmae_shutdown(struct platform_device *pdev) +{ + struct sh_dmae_device *shdev = platform_get_drvdata(pdev); + sh_dmae_ctl_stop(0); + if (shdev->pdata.mode & SHDMA_DMAOR1) + sh_dmae_ctl_stop(1); +} + +static struct platform_driver sh_dmae_driver = { + .remove = __exit_p(sh_dmae_remove), + .shutdown = sh_dmae_shutdown, + .driver = { + .name = "sh-dma-engine", + }, +}; + +static int __init sh_dmae_init(void) +{ + return platform_driver_probe(&sh_dmae_driver, sh_dmae_probe); +} +module_init(sh_dmae_init); + +static void __exit sh_dmae_exit(void) +{ + platform_driver_unregister(&sh_dmae_driver); +} +module_exit(sh_dmae_exit); + +MODULE_AUTHOR("Nobuhiro Iwamatsu <iwamatsu.nobuhiro@renesas.com>"); +MODULE_DESCRIPTION("Renesas SH DMA Engine driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/dma/shdma.h b/drivers/dma/shdma.h new file mode 100644 index 000000000000..2b4bc15a2c0a --- /dev/null +++ b/drivers/dma/shdma.h @@ -0,0 +1,64 @@ +/* + * Renesas SuperH DMA Engine support + * + * Copyright (C) 2009 Nobuhiro Iwamatsu <iwamatsu.nobuhiro@renesas.com> + * Copyright (C) 2009 Renesas Solutions, Inc. All rights reserved. + * + * This 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. + * + */ +#ifndef __DMA_SHDMA_H +#define __DMA_SHDMA_H + +#include <linux/device.h> +#include <linux/dmapool.h> +#include <linux/dmaengine.h> + +#define SH_DMA_TCR_MAX 0x00FFFFFF /* 16MB */ + +struct sh_dmae_regs { + u32 sar; /* SAR / source address */ + u32 dar; /* DAR / destination address */ + u32 tcr; /* TCR / transfer count */ +}; + +struct sh_desc { + struct list_head tx_list; + struct sh_dmae_regs hw; + struct list_head node; + struct dma_async_tx_descriptor async_tx; + int mark; +}; + +struct sh_dmae_chan { + dma_cookie_t completed_cookie; /* The maximum cookie completed */ + spinlock_t desc_lock; /* Descriptor operation lock */ + struct list_head ld_queue; /* Link descriptors queue */ + struct list_head ld_free; /* Link descriptors free */ + struct dma_chan common; /* DMA common channel */ + struct device *dev; /* Channel device */ + struct tasklet_struct tasklet; /* Tasklet */ + int descs_allocated; /* desc count */ + int id; /* Raw id of this channel */ + char dev_id[16]; /* unique name per DMAC of channel */ + + /* Set chcr */ + int (*set_chcr)(struct sh_dmae_chan *sh_chan, u32 regs); + /* Set DMA resource */ + int (*set_dmars)(struct sh_dmae_chan *sh_chan, u16 res); +}; + +struct sh_dmae_device { + struct dma_device common; + struct sh_dmae_chan *chan[MAX_DMA_CHANNELS]; + struct sh_dmae_pdata pdata; +}; + +#define to_sh_chan(chan) container_of(chan, struct sh_dmae_chan, common) +#define to_sh_desc(lh) container_of(lh, struct sh_desc, node) +#define tx_to_sh_desc(tx) container_of(tx, struct sh_desc, async_tx) + +#endif /* __DMA_SHDMA_H */ diff --git a/drivers/dma/txx9dmac.c b/drivers/dma/txx9dmac.c index 7837930146a4..fb6bb64e8861 100644 --- a/drivers/dma/txx9dmac.c +++ b/drivers/dma/txx9dmac.c @@ -180,9 +180,8 @@ static struct txx9dmac_desc *txx9dmac_first_queued(struct txx9dmac_chan *dc) static struct txx9dmac_desc *txx9dmac_last_child(struct txx9dmac_desc *desc) { - if (!list_empty(&desc->txd.tx_list)) - desc = list_entry(desc->txd.tx_list.prev, - struct txx9dmac_desc, desc_node); + if (!list_empty(&desc->tx_list)) + desc = list_entry(desc->tx_list.prev, typeof(*desc), desc_node); return desc; } @@ -197,6 +196,7 @@ static struct txx9dmac_desc *txx9dmac_desc_alloc(struct txx9dmac_chan *dc, desc = kzalloc(sizeof(*desc), flags); if (!desc) return NULL; + INIT_LIST_HEAD(&desc->tx_list); dma_async_tx_descriptor_init(&desc->txd, &dc->chan); desc->txd.tx_submit = txx9dmac_tx_submit; /* txd.flags will be overwritten in prep funcs */ @@ -245,7 +245,7 @@ static void txx9dmac_sync_desc_for_cpu(struct txx9dmac_chan *dc, struct txx9dmac_dev *ddev = dc->ddev; struct txx9dmac_desc *child; - list_for_each_entry(child, &desc->txd.tx_list, desc_node) + list_for_each_entry(child, &desc->tx_list, desc_node) dma_sync_single_for_cpu(chan2parent(&dc->chan), child->txd.phys, ddev->descsize, DMA_TO_DEVICE); @@ -267,11 +267,11 @@ static void txx9dmac_desc_put(struct txx9dmac_chan *dc, txx9dmac_sync_desc_for_cpu(dc, desc); spin_lock_bh(&dc->lock); - list_for_each_entry(child, &desc->txd.tx_list, desc_node) + list_for_each_entry(child, &desc->tx_list, desc_node) dev_vdbg(chan2dev(&dc->chan), "moving child desc %p to freelist\n", child); - list_splice_init(&desc->txd.tx_list, &dc->free_list); + list_splice_init(&desc->tx_list, &dc->free_list); dev_vdbg(chan2dev(&dc->chan), "moving desc %p to freelist\n", desc); list_add(&desc->desc_node, &dc->free_list); @@ -429,7 +429,7 @@ txx9dmac_descriptor_complete(struct txx9dmac_chan *dc, param = txd->callback_param; txx9dmac_sync_desc_for_cpu(dc, desc); - list_splice_init(&txd->tx_list, &dc->free_list); + list_splice_init(&desc->tx_list, &dc->free_list); list_move(&desc->desc_node, &dc->free_list); if (!ds) { @@ -571,7 +571,7 @@ static void txx9dmac_handle_error(struct txx9dmac_chan *dc, u32 csr) "Bad descriptor submitted for DMA! (cookie: %d)\n", bad_desc->txd.cookie); txx9dmac_dump_desc(dc, &bad_desc->hwdesc); - list_for_each_entry(child, &bad_desc->txd.tx_list, desc_node) + list_for_each_entry(child, &bad_desc->tx_list, desc_node) txx9dmac_dump_desc(dc, &child->hwdesc); /* Pretend the descriptor completed successfully */ txx9dmac_descriptor_complete(dc, bad_desc); @@ -613,7 +613,7 @@ static void txx9dmac_scan_descriptors(struct txx9dmac_chan *dc) return; } - list_for_each_entry(child, &desc->txd.tx_list, desc_node) + list_for_each_entry(child, &desc->tx_list, desc_node) if (desc_read_CHAR(dc, child) == chain) { /* Currently in progress */ if (csr & TXX9_DMA_CSR_ABCHC) @@ -823,8 +823,7 @@ txx9dmac_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dest, dma_addr_t src, dma_sync_single_for_device(chan2parent(&dc->chan), prev->txd.phys, ddev->descsize, DMA_TO_DEVICE); - list_add_tail(&desc->desc_node, - &first->txd.tx_list); + list_add_tail(&desc->desc_node, &first->tx_list); } prev = desc; } @@ -919,8 +918,7 @@ txx9dmac_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, prev->txd.phys, ddev->descsize, DMA_TO_DEVICE); - list_add_tail(&desc->desc_node, - &first->txd.tx_list); + list_add_tail(&desc->desc_node, &first->tx_list); } prev = desc; } diff --git a/drivers/dma/txx9dmac.h b/drivers/dma/txx9dmac.h index c907ff01d276..365d42366b9f 100644 --- a/drivers/dma/txx9dmac.h +++ b/drivers/dma/txx9dmac.h @@ -231,6 +231,7 @@ struct txx9dmac_desc { /* THEN values for driver housekeeping */ struct list_head desc_node ____cacheline_aligned; + struct list_head tx_list; struct dma_async_tx_descriptor txd; size_t len; }; diff --git a/drivers/edac/Kconfig b/drivers/edac/Kconfig index a3ca18e2d7cf..55c9c59b3f71 100644 --- a/drivers/edac/Kconfig +++ b/drivers/edac/Kconfig @@ -47,6 +47,18 @@ config EDAC_DEBUG_VERBOSE Source file name and line number where debugging message printed will be added to debugging message. + config EDAC_DECODE_MCE + tristate "Decode MCEs in human-readable form (only on AMD for now)" + depends on CPU_SUP_AMD && X86_MCE + default y + ---help--- + Enable this option if you want to decode Machine Check Exceptions + occuring on your machine in human-readable form. + + You should definitely say Y here in case you want to decode MCEs + which occur really early upon boot, before the module infrastructure + has been initialized. + config EDAC_MM_EDAC tristate "Main Memory EDAC (Error Detection And Correction) reporting" help @@ -59,7 +71,7 @@ config EDAC_MM_EDAC config EDAC_AMD64 tristate "AMD64 (Opteron, Athlon64) K8, F10h, F11h" - depends on EDAC_MM_EDAC && K8_NB && X86_64 && PCI && CPU_SUP_AMD + depends on EDAC_MM_EDAC && K8_NB && X86_64 && PCI && EDAC_DECODE_MCE help Support for error detection and correction on the AMD 64 Families of Memory Controllers (K8, F10h and F11h) @@ -133,6 +145,13 @@ config EDAC_I3000 Support for error detection and correction on the Intel 3000 and 3010 server chipsets. +config EDAC_I3200 + tristate "Intel 3200" + depends on EDAC_MM_EDAC && PCI && X86 && EXPERIMENTAL + help + Support for error detection and correction on the Intel + 3200 and 3210 server chipsets. + config EDAC_X38 tristate "Intel X38" depends on EDAC_MM_EDAC && PCI && X86 @@ -176,11 +195,11 @@ config EDAC_I5100 San Clemente MCH. config EDAC_MPC85XX - tristate "Freescale MPC85xx" - depends on EDAC_MM_EDAC && FSL_SOC && MPC85xx + tristate "Freescale MPC83xx / MPC85xx" + depends on EDAC_MM_EDAC && FSL_SOC && (PPC_83xx || MPC85xx) help Support for error detection and correction on the Freescale - MPC8560, MPC8540, MPC8548 + MPC8349, MPC8560, MPC8540, MPC8548 config EDAC_MV64X60 tristate "Marvell MV64x60" diff --git a/drivers/edac/Makefile b/drivers/edac/Makefile index cfa033ce53a7..bc5dc232a0fb 100644 --- a/drivers/edac/Makefile +++ b/drivers/edac/Makefile @@ -6,7 +6,6 @@ # GNU General Public License. # - obj-$(CONFIG_EDAC) := edac_stub.o obj-$(CONFIG_EDAC_MM_EDAC) += edac_core.o @@ -17,9 +16,7 @@ ifdef CONFIG_PCI edac_core-objs += edac_pci.o edac_pci_sysfs.o endif -ifdef CONFIG_CPU_SUP_AMD -edac_core-objs += edac_mce_amd.o -endif +obj-$(CONFIG_EDAC_DECODE_MCE) += edac_mce_amd.o obj-$(CONFIG_EDAC_AMD76X) += amd76x_edac.o obj-$(CONFIG_EDAC_CPC925) += cpc925_edac.o @@ -32,6 +29,7 @@ obj-$(CONFIG_EDAC_I82443BXGX) += i82443bxgx_edac.o obj-$(CONFIG_EDAC_I82875P) += i82875p_edac.o obj-$(CONFIG_EDAC_I82975X) += i82975x_edac.o obj-$(CONFIG_EDAC_I3000) += i3000_edac.o +obj-$(CONFIG_EDAC_I3200) += i3200_edac.o obj-$(CONFIG_EDAC_X38) += x38_edac.o obj-$(CONFIG_EDAC_I82860) += i82860_edac.o obj-$(CONFIG_EDAC_R82600) += r82600_edac.o @@ -49,3 +47,4 @@ obj-$(CONFIG_EDAC_CELL) += cell_edac.o obj-$(CONFIG_EDAC_PPC4XX) += ppc4xx_edac.o obj-$(CONFIG_EDAC_AMD8111) += amd8111_edac.o obj-$(CONFIG_EDAC_AMD8131) += amd8131_edac.o + diff --git a/drivers/edac/amd64_edac.c b/drivers/edac/amd64_edac.c index 4e551e63b6dc..a38831c82649 100644 --- a/drivers/edac/amd64_edac.c +++ b/drivers/edac/amd64_edac.c @@ -15,8 +15,8 @@ module_param(ecc_enable_override, int, 0644); /* Lookup table for all possible MC control instances */ struct amd64_pvt; -static struct mem_ctl_info *mci_lookup[MAX_NUMNODES]; -static struct amd64_pvt *pvt_lookup[MAX_NUMNODES]; +static struct mem_ctl_info *mci_lookup[EDAC_MAX_NUMNODES]; +static struct amd64_pvt *pvt_lookup[EDAC_MAX_NUMNODES]; /* * See F2x80 for K8 and F2x[1,0]80 for Fam10 and later. The table below is only @@ -189,7 +189,10 @@ static int amd64_get_scrub_rate(struct mem_ctl_info *mci, u32 *bw) /* Map from a CSROW entry to the mask entry that operates on it */ static inline u32 amd64_map_to_dcs_mask(struct amd64_pvt *pvt, int csrow) { - return csrow >> (pvt->num_dcsm >> 3); + if (boot_cpu_data.x86 == 0xf && pvt->ext_model < OPTERON_CPU_REV_F) + return csrow; + else + return csrow >> 1; } /* return the 'base' address the i'th CS entry of the 'dct' DRAM controller */ @@ -279,29 +282,26 @@ static struct mem_ctl_info *find_mc_by_sys_addr(struct mem_ctl_info *mci, intlv_en = pvt->dram_IntlvEn[0]; if (intlv_en == 0) { - for (node_id = 0; ; ) { + for (node_id = 0; node_id < DRAM_REG_COUNT; node_id++) { if (amd64_base_limit_match(pvt, sys_addr, node_id)) - break; - - if (++node_id >= DRAM_REG_COUNT) - goto err_no_match; + goto found; } - goto found; + goto err_no_match; } - if (unlikely((intlv_en != (0x01 << 8)) && - (intlv_en != (0x03 << 8)) && - (intlv_en != (0x07 << 8)))) { + if (unlikely((intlv_en != 0x01) && + (intlv_en != 0x03) && + (intlv_en != 0x07))) { amd64_printk(KERN_WARNING, "junk value of 0x%x extracted from " "IntlvEn field of DRAM Base Register for node 0: " - "This probably indicates a BIOS bug.\n", intlv_en); + "this probably indicates a BIOS bug.\n", intlv_en); return NULL; } bits = (((u32) sys_addr) >> 12) & intlv_en; for (node_id = 0; ; ) { - if ((pvt->dram_limit[node_id] & intlv_en) == bits) + if ((pvt->dram_IntlvSel[node_id] & intlv_en) == bits) break; /* intlv_sel field matches */ if (++node_id >= DRAM_REG_COUNT) @@ -311,10 +311,10 @@ static struct mem_ctl_info *find_mc_by_sys_addr(struct mem_ctl_info *mci, /* sanity test for sys_addr */ if (unlikely(!amd64_base_limit_match(pvt, sys_addr, node_id))) { amd64_printk(KERN_WARNING, - "%s(): sys_addr 0x%lx falls outside base/limit " - "address range for node %d with node interleaving " - "enabled.\n", __func__, (unsigned long)sys_addr, - node_id); + "%s(): sys_addr 0x%llx falls outside base/limit " + "address range for node %d with node interleaving " + "enabled.\n", + __func__, sys_addr, node_id); return NULL; } @@ -377,7 +377,7 @@ static int input_addr_to_csrow(struct mem_ctl_info *mci, u64 input_addr) * base/mask register pair, test the condition shown near the start of * section 3.5.4 (p. 84, BKDG #26094, K8, revA-E). */ - for (csrow = 0; csrow < CHIPSELECT_COUNT; csrow++) { + for (csrow = 0; csrow < pvt->cs_count; csrow++) { /* This DRAM chip select is disabled on this node */ if ((pvt->dcsb0[csrow] & K8_DCSB_CS_ENABLE) == 0) @@ -734,7 +734,7 @@ static void find_csrow_limits(struct mem_ctl_info *mci, int csrow, u64 base, mask; pvt = mci->pvt_info; - BUG_ON((csrow < 0) || (csrow >= CHIPSELECT_COUNT)); + BUG_ON((csrow < 0) || (csrow >= pvt->cs_count)); base = base_from_dct_base(pvt, csrow); mask = mask_from_dct_mask(pvt, csrow); @@ -962,35 +962,27 @@ err_reg: */ static void amd64_set_dct_base_and_mask(struct amd64_pvt *pvt) { - if (pvt->ext_model >= OPTERON_CPU_REV_F) { + + if (boot_cpu_data.x86 == 0xf && pvt->ext_model < OPTERON_CPU_REV_F) { + pvt->dcsb_base = REV_E_DCSB_BASE_BITS; + pvt->dcsm_mask = REV_E_DCSM_MASK_BITS; + pvt->dcs_mask_notused = REV_E_DCS_NOTUSED_BITS; + pvt->dcs_shift = REV_E_DCS_SHIFT; + pvt->cs_count = 8; + pvt->num_dcsm = 8; + } else { pvt->dcsb_base = REV_F_F1Xh_DCSB_BASE_BITS; pvt->dcsm_mask = REV_F_F1Xh_DCSM_MASK_BITS; pvt->dcs_mask_notused = REV_F_F1Xh_DCS_NOTUSED_BITS; pvt->dcs_shift = REV_F_F1Xh_DCS_SHIFT; - switch (boot_cpu_data.x86) { - case 0xf: - pvt->num_dcsm = REV_F_DCSM_COUNT; - break; - - case 0x10: - pvt->num_dcsm = F10_DCSM_COUNT; - break; - - case 0x11: - pvt->num_dcsm = F11_DCSM_COUNT; - break; - - default: - amd64_printk(KERN_ERR, "Unsupported family!\n"); - break; + if (boot_cpu_data.x86 == 0x11) { + pvt->cs_count = 4; + pvt->num_dcsm = 2; + } else { + pvt->cs_count = 8; + pvt->num_dcsm = 4; } - } else { - pvt->dcsb_base = REV_E_DCSB_BASE_BITS; - pvt->dcsm_mask = REV_E_DCSM_MASK_BITS; - pvt->dcs_mask_notused = REV_E_DCS_NOTUSED_BITS; - pvt->dcs_shift = REV_E_DCS_SHIFT; - pvt->num_dcsm = REV_E_DCSM_COUNT; } } @@ -1003,7 +995,7 @@ static void amd64_read_dct_base_mask(struct amd64_pvt *pvt) amd64_set_dct_base_and_mask(pvt); - for (cs = 0; cs < CHIPSELECT_COUNT; cs++) { + for (cs = 0; cs < pvt->cs_count; cs++) { reg = K8_DCSB0 + (cs * 4); err = pci_read_config_dword(pvt->dram_f2_ctl, reg, &pvt->dcsb0[cs]); @@ -1193,7 +1185,7 @@ static void k8_map_sysaddr_to_csrow(struct mem_ctl_info *mci, * different from the node that detected the error. */ src_mci = find_mc_by_sys_addr(mci, SystemAddress); - if (src_mci) { + if (!src_mci) { amd64_mc_printk(mci, KERN_ERR, "failed to map error address 0x%lx to a node\n", (unsigned long)SystemAddress); @@ -1376,8 +1368,8 @@ static void f10_read_dram_base_limit(struct amd64_pvt *pvt, int dram) pvt->dram_IntlvEn[dram] = (low_base >> 8) & 0x7; - pvt->dram_base[dram] = (((((u64) high_base & 0x000000FF) << 32) | - ((u64) low_base & 0xFFFF0000))) << 8; + pvt->dram_base[dram] = (((u64)high_base & 0x000000FF) << 40) | + (((u64)low_base & 0xFFFF0000) << 8); low_offset = K8_DRAM_LIMIT_LOW + (dram << 3); high_offset = F10_DRAM_LIMIT_HIGH + (dram << 3); @@ -1398,9 +1390,9 @@ static void f10_read_dram_base_limit(struct amd64_pvt *pvt, int dram) * Extract address values and form a LIMIT address. Limit is the HIGHEST * memory location of the region, so low 24 bits need to be all ones. */ - low_limit |= 0x0000FFFF; - pvt->dram_limit[dram] = - ((((u64) high_limit << 32) + (u64) low_limit) << 8) | (0xFF); + pvt->dram_limit[dram] = (((u64)high_limit & 0x000000FF) << 40) | + (((u64) low_limit & 0xFFFF0000) << 8) | + 0x00FFFFFF; } static void f10_read_dram_ctl_register(struct amd64_pvt *pvt) @@ -1566,7 +1558,7 @@ static int f10_lookup_addr_in_dct(u32 in_addr, u32 nid, u32 cs) debugf1("InputAddr=0x%x channelselect=%d\n", in_addr, cs); - for (csrow = 0; csrow < CHIPSELECT_COUNT; csrow++) { + for (csrow = 0; csrow < pvt->cs_count; csrow++) { cs_base = amd64_get_dct_base(pvt, cs, csrow); if (!(cs_base & K8_DCSB_CS_ENABLE)) @@ -2262,7 +2254,7 @@ static inline void __amd64_decode_bus_error(struct mem_ctl_info *mci, { u32 ec = ERROR_CODE(info->nbsl); u32 xec = EXT_ERROR_CODE(info->nbsl); - int ecc_type = info->nbsh & (0x3 << 13); + int ecc_type = (info->nbsh >> 13) & 0x3; /* Bail early out if this was an 'observed' error */ if (PP(ec) == K8_NBSL_PP_OBS) @@ -2497,7 +2489,7 @@ err_reg: * NOTE: CPU Revision Dependent code * * Input: - * @csrow_nr ChipSelect Row Number (0..CHIPSELECT_COUNT-1) + * @csrow_nr ChipSelect Row Number (0..pvt->cs_count-1) * k8 private pointer to --> * DRAM Bank Address mapping register * node_id @@ -2577,7 +2569,7 @@ static int amd64_init_csrows(struct mem_ctl_info *mci) (pvt->nbcfg & K8_NBCFG_ECC_ENABLE) ? "Enabled" : "Disabled" ); - for (i = 0; i < CHIPSELECT_COUNT; i++) { + for (i = 0; i < pvt->cs_count; i++) { csrow = &mci->csrows[i]; if ((pvt->dcsb0[i] & K8_DCSB_CS_ENABLE) == 0) { @@ -2988,7 +2980,7 @@ static int amd64_init_2nd_stage(struct amd64_pvt *pvt) goto err_exit; ret = -ENOMEM; - mci = edac_mc_alloc(0, CHIPSELECT_COUNT, pvt->channel_count, node_id); + mci = edac_mc_alloc(0, pvt->cs_count, pvt->channel_count, node_id); if (!mci) goto err_exit; @@ -3171,7 +3163,7 @@ static int __init amd64_edac_init(void) opstate_init(); if (cache_k8_northbridges() < 0) - goto err_exit; + return err; err = pci_register_driver(&amd64_pci_driver); if (err) @@ -3197,8 +3189,6 @@ static int __init amd64_edac_init(void) err_2nd_stage: debugf0("2nd stage failed\n"); - -err_exit: pci_unregister_driver(&amd64_pci_driver); return err; diff --git a/drivers/edac/amd64_edac.h b/drivers/edac/amd64_edac.h index 8ea07e2715dc..c6f359a85207 100644 --- a/drivers/edac/amd64_edac.h +++ b/drivers/edac/amd64_edac.h @@ -132,6 +132,8 @@ #define EDAC_AMD64_VERSION " Ver: 3.2.0 " __DATE__ #define EDAC_MOD_STR "amd64_edac" +#define EDAC_MAX_NUMNODES 8 + /* Extended Model from CPUID, for CPU Revision numbers */ #define OPTERON_CPU_LE_REV_C 0 #define OPTERON_CPU_REV_D 1 @@ -142,7 +144,7 @@ #define OPTERON_CPU_REV_FA 5 /* Hardware limit on ChipSelect rows per MC and processors per system */ -#define CHIPSELECT_COUNT 8 +#define MAX_CS_COUNT 8 #define DRAM_REG_COUNT 8 @@ -193,7 +195,6 @@ */ #define REV_E_DCSB_BASE_BITS (0xFFE0FE00ULL) #define REV_E_DCS_SHIFT 4 -#define REV_E_DCSM_COUNT 8 #define REV_F_F1Xh_DCSB_BASE_BITS (0x1FF83FE0ULL) #define REV_F_F1Xh_DCS_SHIFT 8 @@ -204,9 +205,6 @@ */ #define REV_F_DCSB_BASE_BITS (0x1FF83FE0ULL) #define REV_F_DCS_SHIFT 8 -#define REV_F_DCSM_COUNT 4 -#define F10_DCSM_COUNT 4 -#define F11_DCSM_COUNT 2 /* DRAM CS Mask Registers */ #define K8_DCSM0 0x60 @@ -374,13 +372,11 @@ enum { #define SET_NB_DRAM_INJECTION_WRITE(word, bits) \ (BIT(((word) & 0xF) + 20) | \ - BIT(17) | \ - ((bits) & 0xF)) + BIT(17) | bits) #define SET_NB_DRAM_INJECTION_READ(word, bits) \ (BIT(((word) & 0xF) + 20) | \ - BIT(16) | \ - ((bits) & 0xF)) + BIT(16) | bits) #define K8_NBCAP 0xE8 #define K8_NBCAP_CORES (BIT(12)|BIT(13)) @@ -445,12 +441,12 @@ struct amd64_pvt { u32 dbam1; /* DRAM Base Address Mapping reg for DCT1 */ /* DRAM CS Base Address Registers F2x[1,0][5C:40] */ - u32 dcsb0[CHIPSELECT_COUNT]; - u32 dcsb1[CHIPSELECT_COUNT]; + u32 dcsb0[MAX_CS_COUNT]; + u32 dcsb1[MAX_CS_COUNT]; /* DRAM CS Mask Registers F2x[1,0][6C:60] */ - u32 dcsm0[CHIPSELECT_COUNT]; - u32 dcsm1[CHIPSELECT_COUNT]; + u32 dcsm0[MAX_CS_COUNT]; + u32 dcsm1[MAX_CS_COUNT]; /* * Decoded parts of DRAM BASE and LIMIT Registers @@ -470,6 +466,7 @@ struct amd64_pvt { */ u32 dcsb_base; /* DCSB base bits */ u32 dcsm_mask; /* DCSM mask bits */ + u32 cs_count; /* num chip selects (== num DCSB registers) */ u32 num_dcsm; /* Number of DCSM registers */ u32 dcs_mask_notused; /* DCSM notused mask bits */ u32 dcs_shift; /* DCSB and DCSM shift value */ diff --git a/drivers/edac/amd64_edac_inj.c b/drivers/edac/amd64_edac_inj.c index d3675b76b3a7..29f1f7a612d9 100644 --- a/drivers/edac/amd64_edac_inj.c +++ b/drivers/edac/amd64_edac_inj.c @@ -1,5 +1,11 @@ #include "amd64_edac.h" +static ssize_t amd64_inject_section_show(struct mem_ctl_info *mci, char *buf) +{ + struct amd64_pvt *pvt = mci->pvt_info; + return sprintf(buf, "0x%x\n", pvt->injection.section); +} + /* * store error injection section value which refers to one of 4 16-byte sections * within a 64-byte cacheline @@ -15,12 +21,26 @@ static ssize_t amd64_inject_section_store(struct mem_ctl_info *mci, ret = strict_strtoul(data, 10, &value); if (ret != -EINVAL) { + + if (value > 3) { + amd64_printk(KERN_WARNING, + "%s: invalid section 0x%lx\n", + __func__, value); + return -EINVAL; + } + pvt->injection.section = (u32) value; return count; } return ret; } +static ssize_t amd64_inject_word_show(struct mem_ctl_info *mci, char *buf) +{ + struct amd64_pvt *pvt = mci->pvt_info; + return sprintf(buf, "0x%x\n", pvt->injection.word); +} + /* * store error injection word value which refers to one of 9 16-bit word of the * 16-byte (128-bit + ECC bits) section @@ -37,14 +57,25 @@ static ssize_t amd64_inject_word_store(struct mem_ctl_info *mci, ret = strict_strtoul(data, 10, &value); if (ret != -EINVAL) { - value = (value <= 8) ? value : 0; - pvt->injection.word = (u32) value; + if (value > 8) { + amd64_printk(KERN_WARNING, + "%s: invalid word 0x%lx\n", + __func__, value); + return -EINVAL; + } + pvt->injection.word = (u32) value; return count; } return ret; } +static ssize_t amd64_inject_ecc_vector_show(struct mem_ctl_info *mci, char *buf) +{ + struct amd64_pvt *pvt = mci->pvt_info; + return sprintf(buf, "0x%x\n", pvt->injection.bit_map); +} + /* * store 16 bit error injection vector which enables injecting errors to the * corresponding bit within the error injection word above. When used during a @@ -60,8 +91,14 @@ static ssize_t amd64_inject_ecc_vector_store(struct mem_ctl_info *mci, ret = strict_strtoul(data, 16, &value); if (ret != -EINVAL) { - pvt->injection.bit_map = (u32) value & 0xFFFF; + if (value & 0xFFFF0000) { + amd64_printk(KERN_WARNING, + "%s: invalid EccVector: 0x%lx\n", + __func__, value); + return -EINVAL; + } + pvt->injection.bit_map = (u32) value; return count; } return ret; @@ -147,7 +184,7 @@ struct mcidev_sysfs_attribute amd64_inj_attrs[] = { .name = "inject_section", .mode = (S_IRUGO | S_IWUSR) }, - .show = NULL, + .show = amd64_inject_section_show, .store = amd64_inject_section_store, }, { @@ -155,7 +192,7 @@ struct mcidev_sysfs_attribute amd64_inj_attrs[] = { .name = "inject_word", .mode = (S_IRUGO | S_IWUSR) }, - .show = NULL, + .show = amd64_inject_word_show, .store = amd64_inject_word_store, }, { @@ -163,7 +200,7 @@ struct mcidev_sysfs_attribute amd64_inj_attrs[] = { .name = "inject_ecc_vector", .mode = (S_IRUGO | S_IWUSR) }, - .show = NULL, + .show = amd64_inject_ecc_vector_show, .store = amd64_inject_ecc_vector_store, }, { diff --git a/drivers/edac/cpc925_edac.c b/drivers/edac/cpc925_edac.c index 8c54196b5aba..3d50274f1348 100644 --- a/drivers/edac/cpc925_edac.c +++ b/drivers/edac/cpc925_edac.c @@ -885,14 +885,14 @@ static int __devinit cpc925_probe(struct platform_device *pdev) if (!devm_request_mem_region(&pdev->dev, r->start, - r->end - r->start + 1, + resource_size(r), pdev->name)) { cpc925_printk(KERN_ERR, "Unable to request mem region\n"); res = -EBUSY; goto err1; } - vbase = devm_ioremap(&pdev->dev, r->start, r->end - r->start + 1); + vbase = devm_ioremap(&pdev->dev, r->start, resource_size(r)); if (!vbase) { cpc925_printk(KERN_ERR, "Unable to ioremap device\n"); res = -ENOMEM; @@ -953,7 +953,7 @@ err3: cpc925_mc_exit(mci); edac_mc_free(mci); err2: - devm_release_mem_region(&pdev->dev, r->start, r->end-r->start+1); + devm_release_mem_region(&pdev->dev, r->start, resource_size(r)); err1: devres_release_group(&pdev->dev, cpc925_probe); out: diff --git a/drivers/edac/edac_device.c b/drivers/edac/edac_device.c index b02a6a69a8f0..d5e13c94714f 100644 --- a/drivers/edac/edac_device.c +++ b/drivers/edac/edac_device.c @@ -356,7 +356,6 @@ static void complete_edac_device_list_del(struct rcu_head *head) edac_dev = container_of(head, struct edac_device_ctl_info, rcu); INIT_LIST_HEAD(&edac_dev->link); - complete(&edac_dev->removal_complete); } /* @@ -369,10 +368,8 @@ static void del_edac_device_from_global_list(struct edac_device_ctl_info *edac_device) { list_del_rcu(&edac_device->link); - - init_completion(&edac_device->removal_complete); call_rcu(&edac_device->rcu, complete_edac_device_list_del); - wait_for_completion(&edac_device->removal_complete); + rcu_barrier(); } /* diff --git a/drivers/edac/edac_mc.c b/drivers/edac/edac_mc.c index 335b7ebdb11c..b629c41756f0 100644 --- a/drivers/edac/edac_mc.c +++ b/drivers/edac/edac_mc.c @@ -418,16 +418,14 @@ static void complete_mc_list_del(struct rcu_head *head) mci = container_of(head, struct mem_ctl_info, rcu); INIT_LIST_HEAD(&mci->link); - complete(&mci->complete); } static void del_mc_from_global_list(struct mem_ctl_info *mci) { atomic_dec(&edac_handlers); list_del_rcu(&mci->link); - init_completion(&mci->complete); call_rcu(&mci->rcu, complete_mc_list_del); - wait_for_completion(&mci->complete); + rcu_barrier(); } /** diff --git a/drivers/edac/edac_mce_amd.c b/drivers/edac/edac_mce_amd.c index 0c21c370c9dd..713ed7d37247 100644 --- a/drivers/edac/edac_mce_amd.c +++ b/drivers/edac/edac_mce_amd.c @@ -3,6 +3,7 @@ static bool report_gart_errors; static void (*nb_bus_decoder)(int node_id, struct err_regs *regs); +static void (*orig_mce_callback)(struct mce *m); void amd_report_gart_errors(bool v) { @@ -362,7 +363,7 @@ static inline void amd_decode_err_code(unsigned int ec) pr_warning("Huh? Unknown MCE error 0x%x\n", ec); } -void decode_mce(struct mce *m) +static void amd_decode_mce(struct mce *m) { struct err_regs regs; int node, ecc; @@ -420,3 +421,32 @@ void decode_mce(struct mce *m) amd_decode_err_code(m->status & 0xffff); } + +static int __init mce_amd_init(void) +{ + /* + * We can decode MCEs for Opteron and later CPUs: + */ + if ((boot_cpu_data.x86_vendor == X86_VENDOR_AMD) && + (boot_cpu_data.x86 >= 0xf)) { + /* safe the default decode mce callback */ + orig_mce_callback = x86_mce_decode_callback; + + x86_mce_decode_callback = amd_decode_mce; + } + + return 0; +} +early_initcall(mce_amd_init); + +#ifdef MODULE +static void __exit mce_amd_exit(void) +{ + x86_mce_decode_callback = orig_mce_callback; +} + +MODULE_DESCRIPTION("AMD MCE decoder"); +MODULE_ALIAS("edac-mce-amd"); +MODULE_LICENSE("GPL"); +module_exit(mce_amd_exit); +#endif diff --git a/drivers/edac/edac_pci.c b/drivers/edac/edac_pci.c index 30b585b1d60b..efb5d5650783 100644 --- a/drivers/edac/edac_pci.c +++ b/drivers/edac/edac_pci.c @@ -174,7 +174,6 @@ static void complete_edac_pci_list_del(struct rcu_head *head) pci = container_of(head, struct edac_pci_ctl_info, rcu); INIT_LIST_HEAD(&pci->link); - complete(&pci->complete); } /* @@ -185,9 +184,8 @@ static void complete_edac_pci_list_del(struct rcu_head *head) static void del_edac_pci_from_global_list(struct edac_pci_ctl_info *pci) { list_del_rcu(&pci->link); - init_completion(&pci->complete); call_rcu(&pci->rcu, complete_edac_pci_list_del); - wait_for_completion(&pci->complete); + rcu_barrier(); } #if 0 diff --git a/drivers/edac/i3200_edac.c b/drivers/edac/i3200_edac.c new file mode 100644 index 000000000000..fde4db91c4d2 --- /dev/null +++ b/drivers/edac/i3200_edac.c @@ -0,0 +1,527 @@ +/* + * Intel 3200/3210 Memory Controller kernel module + * Copyright (C) 2008-2009 Akamai Technologies, Inc. + * Portions by Hitoshi Mitake <h.mitake@gmail.com>. + * + * This file may be distributed under the terms of the + * GNU General Public License. + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/pci.h> +#include <linux/pci_ids.h> +#include <linux/slab.h> +#include <linux/edac.h> +#include <linux/io.h> +#include "edac_core.h" + +#define I3200_REVISION "1.1" + +#define EDAC_MOD_STR "i3200_edac" + +#define PCI_DEVICE_ID_INTEL_3200_HB 0x29f0 + +#define I3200_RANKS 8 +#define I3200_RANKS_PER_CHANNEL 4 +#define I3200_CHANNELS 2 + +/* Intel 3200 register addresses - device 0 function 0 - DRAM Controller */ + +#define I3200_MCHBAR_LOW 0x48 /* MCH Memory Mapped Register BAR */ +#define I3200_MCHBAR_HIGH 0x4c +#define I3200_MCHBAR_MASK 0xfffffc000ULL /* bits 35:14 */ +#define I3200_MMR_WINDOW_SIZE 16384 + +#define I3200_TOM 0xa0 /* Top of Memory (16b) + * + * 15:10 reserved + * 9:0 total populated physical memory + */ +#define I3200_TOM_MASK 0x3ff /* bits 9:0 */ +#define I3200_TOM_SHIFT 26 /* 64MiB grain */ + +#define I3200_ERRSTS 0xc8 /* Error Status Register (16b) + * + * 15 reserved + * 14 Isochronous TBWRR Run Behind FIFO Full + * (ITCV) + * 13 Isochronous TBWRR Run Behind FIFO Put + * (ITSTV) + * 12 reserved + * 11 MCH Thermal Sensor Event + * for SMI/SCI/SERR (GTSE) + * 10 reserved + * 9 LOCK to non-DRAM Memory Flag (LCKF) + * 8 reserved + * 7 DRAM Throttle Flag (DTF) + * 6:2 reserved + * 1 Multi-bit DRAM ECC Error Flag (DMERR) + * 0 Single-bit DRAM ECC Error Flag (DSERR) + */ +#define I3200_ERRSTS_UE 0x0002 +#define I3200_ERRSTS_CE 0x0001 +#define I3200_ERRSTS_BITS (I3200_ERRSTS_UE | I3200_ERRSTS_CE) + + +/* Intel MMIO register space - device 0 function 0 - MMR space */ + +#define I3200_C0DRB 0x200 /* Channel 0 DRAM Rank Boundary (16b x 4) + * + * 15:10 reserved + * 9:0 Channel 0 DRAM Rank Boundary Address + */ +#define I3200_C1DRB 0x600 /* Channel 1 DRAM Rank Boundary (16b x 4) */ +#define I3200_DRB_MASK 0x3ff /* bits 9:0 */ +#define I3200_DRB_SHIFT 26 /* 64MiB grain */ + +#define I3200_C0ECCERRLOG 0x280 /* Channel 0 ECC Error Log (64b) + * + * 63:48 Error Column Address (ERRCOL) + * 47:32 Error Row Address (ERRROW) + * 31:29 Error Bank Address (ERRBANK) + * 28:27 Error Rank Address (ERRRANK) + * 26:24 reserved + * 23:16 Error Syndrome (ERRSYND) + * 15: 2 reserved + * 1 Multiple Bit Error Status (MERRSTS) + * 0 Correctable Error Status (CERRSTS) + */ +#define I3200_C1ECCERRLOG 0x680 /* Chan 1 ECC Error Log (64b) */ +#define I3200_ECCERRLOG_CE 0x1 +#define I3200_ECCERRLOG_UE 0x2 +#define I3200_ECCERRLOG_RANK_BITS 0x18000000 +#define I3200_ECCERRLOG_RANK_SHIFT 27 +#define I3200_ECCERRLOG_SYNDROME_BITS 0xff0000 +#define I3200_ECCERRLOG_SYNDROME_SHIFT 16 +#define I3200_CAPID0 0xe0 /* P.95 of spec for details */ + +struct i3200_priv { + void __iomem *window; +}; + +static int nr_channels; + +static int how_many_channels(struct pci_dev *pdev) +{ + unsigned char capid0_8b; /* 8th byte of CAPID0 */ + + pci_read_config_byte(pdev, I3200_CAPID0 + 8, &capid0_8b); + if (capid0_8b & 0x20) { /* check DCD: Dual Channel Disable */ + debugf0("In single channel mode.\n"); + return 1; + } else { + debugf0("In dual channel mode.\n"); + return 2; + } +} + +static unsigned long eccerrlog_syndrome(u64 log) +{ + return (log & I3200_ECCERRLOG_SYNDROME_BITS) >> + I3200_ECCERRLOG_SYNDROME_SHIFT; +} + +static int eccerrlog_row(int channel, u64 log) +{ + u64 rank = ((log & I3200_ECCERRLOG_RANK_BITS) >> + I3200_ECCERRLOG_RANK_SHIFT); + return rank | (channel * I3200_RANKS_PER_CHANNEL); +} + +enum i3200_chips { + I3200 = 0, +}; + +struct i3200_dev_info { + const char *ctl_name; +}; + +struct i3200_error_info { + u16 errsts; + u16 errsts2; + u64 eccerrlog[I3200_CHANNELS]; +}; + +static const struct i3200_dev_info i3200_devs[] = { + [I3200] = { + .ctl_name = "i3200" + }, +}; + +static struct pci_dev *mci_pdev; +static int i3200_registered = 1; + + +static void i3200_clear_error_info(struct mem_ctl_info *mci) +{ + struct pci_dev *pdev; + + pdev = to_pci_dev(mci->dev); + + /* + * Clear any error bits. + * (Yes, we really clear bits by writing 1 to them.) + */ + pci_write_bits16(pdev, I3200_ERRSTS, I3200_ERRSTS_BITS, + I3200_ERRSTS_BITS); +} + +static void i3200_get_and_clear_error_info(struct mem_ctl_info *mci, + struct i3200_error_info *info) +{ + struct pci_dev *pdev; + struct i3200_priv *priv = mci->pvt_info; + void __iomem *window = priv->window; + + pdev = to_pci_dev(mci->dev); + + /* + * This is a mess because there is no atomic way to read all the + * registers at once and the registers can transition from CE being + * overwritten by UE. + */ + pci_read_config_word(pdev, I3200_ERRSTS, &info->errsts); + if (!(info->errsts & I3200_ERRSTS_BITS)) + return; + + info->eccerrlog[0] = readq(window + I3200_C0ECCERRLOG); + if (nr_channels == 2) + info->eccerrlog[1] = readq(window + I3200_C1ECCERRLOG); + + pci_read_config_word(pdev, I3200_ERRSTS, &info->errsts2); + + /* + * If the error is the same for both reads then the first set + * of reads is valid. If there is a change then there is a CE + * with no info and the second set of reads is valid and + * should be UE info. + */ + if ((info->errsts ^ info->errsts2) & I3200_ERRSTS_BITS) { + info->eccerrlog[0] = readq(window + I3200_C0ECCERRLOG); + if (nr_channels == 2) + info->eccerrlog[1] = readq(window + I3200_C1ECCERRLOG); + } + + i3200_clear_error_info(mci); +} + +static void i3200_process_error_info(struct mem_ctl_info *mci, + struct i3200_error_info *info) +{ + int channel; + u64 log; + + if (!(info->errsts & I3200_ERRSTS_BITS)) + return; + + if ((info->errsts ^ info->errsts2) & I3200_ERRSTS_BITS) { + edac_mc_handle_ce_no_info(mci, "UE overwrote CE"); + info->errsts = info->errsts2; + } + + for (channel = 0; channel < nr_channels; channel++) { + log = info->eccerrlog[channel]; + if (log & I3200_ECCERRLOG_UE) { + edac_mc_handle_ue(mci, 0, 0, + eccerrlog_row(channel, log), + "i3200 UE"); + } else if (log & I3200_ECCERRLOG_CE) { + edac_mc_handle_ce(mci, 0, 0, + eccerrlog_syndrome(log), + eccerrlog_row(channel, log), 0, + "i3200 CE"); + } + } +} + +static void i3200_check(struct mem_ctl_info *mci) +{ + struct i3200_error_info info; + + debugf1("MC%d: %s()\n", mci->mc_idx, __func__); + i3200_get_and_clear_error_info(mci, &info); + i3200_process_error_info(mci, &info); +} + + +void __iomem *i3200_map_mchbar(struct pci_dev *pdev) +{ + union { + u64 mchbar; + struct { + u32 mchbar_low; + u32 mchbar_high; + }; + } u; + void __iomem *window; + + pci_read_config_dword(pdev, I3200_MCHBAR_LOW, &u.mchbar_low); + pci_read_config_dword(pdev, I3200_MCHBAR_HIGH, &u.mchbar_high); + u.mchbar &= I3200_MCHBAR_MASK; + + if (u.mchbar != (resource_size_t)u.mchbar) { + printk(KERN_ERR + "i3200: mmio space beyond accessible range (0x%llx)\n", + (unsigned long long)u.mchbar); + return NULL; + } + + window = ioremap_nocache(u.mchbar, I3200_MMR_WINDOW_SIZE); + if (!window) + printk(KERN_ERR "i3200: cannot map mmio space at 0x%llx\n", + (unsigned long long)u.mchbar); + + return window; +} + + +static void i3200_get_drbs(void __iomem *window, + u16 drbs[I3200_CHANNELS][I3200_RANKS_PER_CHANNEL]) +{ + int i; + + for (i = 0; i < I3200_RANKS_PER_CHANNEL; i++) { + drbs[0][i] = readw(window + I3200_C0DRB + 2*i) & I3200_DRB_MASK; + drbs[1][i] = readw(window + I3200_C1DRB + 2*i) & I3200_DRB_MASK; + } +} + +static bool i3200_is_stacked(struct pci_dev *pdev, + u16 drbs[I3200_CHANNELS][I3200_RANKS_PER_CHANNEL]) +{ + u16 tom; + + pci_read_config_word(pdev, I3200_TOM, &tom); + tom &= I3200_TOM_MASK; + + return drbs[I3200_CHANNELS - 1][I3200_RANKS_PER_CHANNEL - 1] == tom; +} + +static unsigned long drb_to_nr_pages( + u16 drbs[I3200_CHANNELS][I3200_RANKS_PER_CHANNEL], bool stacked, + int channel, int rank) +{ + int n; + + n = drbs[channel][rank]; + if (rank > 0) + n -= drbs[channel][rank - 1]; + if (stacked && (channel == 1) && + drbs[channel][rank] == drbs[channel][I3200_RANKS_PER_CHANNEL - 1]) + n -= drbs[0][I3200_RANKS_PER_CHANNEL - 1]; + + n <<= (I3200_DRB_SHIFT - PAGE_SHIFT); + return n; +} + +static int i3200_probe1(struct pci_dev *pdev, int dev_idx) +{ + int rc; + int i; + struct mem_ctl_info *mci = NULL; + unsigned long last_page; + u16 drbs[I3200_CHANNELS][I3200_RANKS_PER_CHANNEL]; + bool stacked; + void __iomem *window; + struct i3200_priv *priv; + + debugf0("MC: %s()\n", __func__); + + window = i3200_map_mchbar(pdev); + if (!window) + return -ENODEV; + + i3200_get_drbs(window, drbs); + nr_channels = how_many_channels(pdev); + + mci = edac_mc_alloc(sizeof(struct i3200_priv), I3200_RANKS, + nr_channels, 0); + if (!mci) + return -ENOMEM; + + debugf3("MC: %s(): init mci\n", __func__); + + mci->dev = &pdev->dev; + mci->mtype_cap = MEM_FLAG_DDR2; + + mci->edac_ctl_cap = EDAC_FLAG_SECDED; + mci->edac_cap = EDAC_FLAG_SECDED; + + mci->mod_name = EDAC_MOD_STR; + mci->mod_ver = I3200_REVISION; + mci->ctl_name = i3200_devs[dev_idx].ctl_name; + mci->dev_name = pci_name(pdev); + mci->edac_check = i3200_check; + mci->ctl_page_to_phys = NULL; + priv = mci->pvt_info; + priv->window = window; + + stacked = i3200_is_stacked(pdev, drbs); + + /* + * The dram rank boundary (DRB) reg values are boundary addresses + * for each DRAM rank with a granularity of 64MB. DRB regs are + * cumulative; the last one will contain the total memory + * contained in all ranks. + */ + last_page = -1UL; + for (i = 0; i < mci->nr_csrows; i++) { + unsigned long nr_pages; + struct csrow_info *csrow = &mci->csrows[i]; + + nr_pages = drb_to_nr_pages(drbs, stacked, + i / I3200_RANKS_PER_CHANNEL, + i % I3200_RANKS_PER_CHANNEL); + + if (nr_pages == 0) { + csrow->mtype = MEM_EMPTY; + continue; + } + + csrow->first_page = last_page + 1; + last_page += nr_pages; + csrow->last_page = last_page; + csrow->nr_pages = nr_pages; + + csrow->grain = nr_pages << PAGE_SHIFT; + csrow->mtype = MEM_DDR2; + csrow->dtype = DEV_UNKNOWN; + csrow->edac_mode = EDAC_UNKNOWN; + } + + i3200_clear_error_info(mci); + + rc = -ENODEV; + if (edac_mc_add_mc(mci)) { + debugf3("MC: %s(): failed edac_mc_add_mc()\n", __func__); + goto fail; + } + + /* get this far and it's successful */ + debugf3("MC: %s(): success\n", __func__); + return 0; + +fail: + iounmap(window); + if (mci) + edac_mc_free(mci); + + return rc; +} + +static int __devinit i3200_init_one(struct pci_dev *pdev, + const struct pci_device_id *ent) +{ + int rc; + + debugf0("MC: %s()\n", __func__); + + if (pci_enable_device(pdev) < 0) + return -EIO; + + rc = i3200_probe1(pdev, ent->driver_data); + if (!mci_pdev) + mci_pdev = pci_dev_get(pdev); + + return rc; +} + +static void __devexit i3200_remove_one(struct pci_dev *pdev) +{ + struct mem_ctl_info *mci; + struct i3200_priv *priv; + + debugf0("%s()\n", __func__); + + mci = edac_mc_del_mc(&pdev->dev); + if (!mci) + return; + + priv = mci->pvt_info; + iounmap(priv->window); + + edac_mc_free(mci); +} + +static const struct pci_device_id i3200_pci_tbl[] __devinitdata = { + { + PCI_VEND_DEV(INTEL, 3200_HB), PCI_ANY_ID, PCI_ANY_ID, 0, 0, + I3200}, + { + 0, + } /* 0 terminated list. */ +}; + +MODULE_DEVICE_TABLE(pci, i3200_pci_tbl); + +static struct pci_driver i3200_driver = { + .name = EDAC_MOD_STR, + .probe = i3200_init_one, + .remove = __devexit_p(i3200_remove_one), + .id_table = i3200_pci_tbl, +}; + +static int __init i3200_init(void) +{ + int pci_rc; + + debugf3("MC: %s()\n", __func__); + + /* Ensure that the OPSTATE is set correctly for POLL or NMI */ + opstate_init(); + + pci_rc = pci_register_driver(&i3200_driver); + if (pci_rc < 0) + goto fail0; + + if (!mci_pdev) { + i3200_registered = 0; + mci_pdev = pci_get_device(PCI_VENDOR_ID_INTEL, + PCI_DEVICE_ID_INTEL_3200_HB, NULL); + if (!mci_pdev) { + debugf0("i3200 pci_get_device fail\n"); + pci_rc = -ENODEV; + goto fail1; + } + + pci_rc = i3200_init_one(mci_pdev, i3200_pci_tbl); + if (pci_rc < 0) { + debugf0("i3200 init fail\n"); + pci_rc = -ENODEV; + goto fail1; + } + } + + return 0; + +fail1: + pci_unregister_driver(&i3200_driver); + +fail0: + if (mci_pdev) + pci_dev_put(mci_pdev); + + return pci_rc; +} + +static void __exit i3200_exit(void) +{ + debugf3("MC: %s()\n", __func__); + + pci_unregister_driver(&i3200_driver); + if (!i3200_registered) { + i3200_remove_one(mci_pdev); + pci_dev_put(mci_pdev); + } +} + +module_init(i3200_init); +module_exit(i3200_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Akamai Technologies, Inc."); +MODULE_DESCRIPTION("MC support for Intel 3200 memory hub controllers"); + +module_param(edac_op_state, int, 0444); +MODULE_PARM_DESC(edac_op_state, "EDAC Error Reporting state: 0=Poll,1=NMI"); diff --git a/drivers/edac/i5000_edac.c b/drivers/edac/i5000_edac.c index d335086f4a26..77a9579d7167 100644 --- a/drivers/edac/i5000_edac.c +++ b/drivers/edac/i5000_edac.c @@ -1173,7 +1173,7 @@ static void i5000_get_mc_regs(struct mem_ctl_info *mci) pci_read_config_word(pvt->branch_1, where, &pvt->b1_mtr[slot_row]); debugf2("MTR%d where=0x%x B1 value=0x%x\n", slot_row, - where, pvt->b0_mtr[slot_row]); + where, pvt->b1_mtr[slot_row]); } else { pvt->b1_mtr[slot_row] = 0; } @@ -1232,7 +1232,7 @@ static int i5000_init_csrows(struct mem_ctl_info *mci) struct csrow_info *p_csrow; int empty, channel_count; int max_csrows; - int mtr; + int mtr, mtr1; int csrow_megs; int channel; int csrow; @@ -1251,9 +1251,10 @@ static int i5000_init_csrows(struct mem_ctl_info *mci) /* use branch 0 for the basis */ mtr = pvt->b0_mtr[csrow >> 1]; + mtr1 = pvt->b1_mtr[csrow >> 1]; /* if no DIMMS on this row, continue */ - if (!MTR_DIMMS_PRESENT(mtr)) + if (!MTR_DIMMS_PRESENT(mtr) && !MTR_DIMMS_PRESENT(mtr1)) continue; /* FAKE OUT VALUES, FIXME */ diff --git a/drivers/edac/i5400_edac.c b/drivers/edac/i5400_edac.c index b08b6d8e2dc7..f99d10655ed4 100644 --- a/drivers/edac/i5400_edac.c +++ b/drivers/edac/i5400_edac.c @@ -46,9 +46,10 @@ /* Limits for i5400 */ #define NUM_MTRS_PER_BRANCH 4 #define CHANNELS_PER_BRANCH 2 +#define MAX_DIMMS_PER_CHANNEL NUM_MTRS_PER_BRANCH #define MAX_CHANNELS 4 -#define MAX_DIMMS (MAX_CHANNELS * 4) /* Up to 4 DIMM's per channel */ -#define MAX_CSROWS (MAX_DIMMS * 2) /* max possible csrows per channel */ +/* max possible csrows per channel */ +#define MAX_CSROWS (MAX_DIMMS_PER_CHANNEL) /* Device 16, * Function 0: System Address @@ -331,7 +332,6 @@ static const struct i5400_dev_info i5400_devs[] = { struct i5400_dimm_info { int megabytes; /* size, 0 means not present */ - int dual_rank; }; /* driver private data structure */ @@ -849,11 +849,9 @@ static int determine_mtr(struct i5400_pvt *pvt, int csrow, int channel) int n; /* There is one MTR for each slot pair of FB-DIMMs, - Each slot may have one or two ranks (2 csrows), Each slot pair may be at branch 0 or branch 1. - So, csrow should be divided by eight */ - n = csrow >> 3; + n = csrow; if (n >= NUM_MTRS_PER_BRANCH) { debugf0("ERROR: trying to access an invalid csrow: %d\n", @@ -905,25 +903,22 @@ static void handle_channel(struct i5400_pvt *pvt, int csrow, int channel, amb_present_reg = determine_amb_present_reg(pvt, channel); /* Determine if there is a DIMM present in this DIMM slot */ - if (amb_present_reg & (1 << (csrow >> 1))) { - dinfo->dual_rank = MTR_DIMM_RANK(mtr); - - if (!((dinfo->dual_rank == 0) && - ((csrow & 0x1) == 0x1))) { - /* Start with the number of bits for a Bank - * on the DRAM */ - addrBits = MTR_DRAM_BANKS_ADDR_BITS(mtr); - /* Add thenumber of ROW bits */ - addrBits += MTR_DIMM_ROWS_ADDR_BITS(mtr); - /* add the number of COLUMN bits */ - addrBits += MTR_DIMM_COLS_ADDR_BITS(mtr); - - addrBits += 6; /* add 64 bits per DIMM */ - addrBits -= 20; /* divide by 2^^20 */ - addrBits -= 3; /* 8 bits per bytes */ - - dinfo->megabytes = 1 << addrBits; - } + if (amb_present_reg & (1 << csrow)) { + /* Start with the number of bits for a Bank + * on the DRAM */ + addrBits = MTR_DRAM_BANKS_ADDR_BITS(mtr); + /* Add thenumber of ROW bits */ + addrBits += MTR_DIMM_ROWS_ADDR_BITS(mtr); + /* add the number of COLUMN bits */ + addrBits += MTR_DIMM_COLS_ADDR_BITS(mtr); + /* add the number of RANK bits */ + addrBits += MTR_DIMM_RANK(mtr); + + addrBits += 6; /* add 64 bits per DIMM */ + addrBits -= 20; /* divide by 2^^20 */ + addrBits -= 3; /* 8 bits per bytes */ + + dinfo->megabytes = 1 << addrBits; } } } @@ -951,12 +946,12 @@ static void calculate_dimm_size(struct i5400_pvt *pvt) return; } - /* Scan all the actual CSROWS (which is # of DIMMS * 2) + /* Scan all the actual CSROWS * and calculate the information for each DIMM * Start with the highest csrow first, to display it first * and work toward the 0th csrow */ - max_csrows = pvt->maxdimmperch * 2; + max_csrows = pvt->maxdimmperch; for (csrow = max_csrows - 1; csrow >= 0; csrow--) { /* on an odd csrow, first output a 'boundary' marker, @@ -1064,7 +1059,7 @@ static void i5400_get_mc_regs(struct mem_ctl_info *mci) /* Get the set of MTR[0-3] regs by each branch */ for (slot_row = 0; slot_row < NUM_MTRS_PER_BRANCH; slot_row++) { - int where = MTR0 + (slot_row * sizeof(u32)); + int where = MTR0 + (slot_row * sizeof(u16)); /* Branch 0 set of MTR registers */ pci_read_config_word(pvt->branch_0, where, @@ -1146,7 +1141,7 @@ static int i5400_init_csrows(struct mem_ctl_info *mci) pvt = mci->pvt_info; channel_count = pvt->maxch; - max_csrows = pvt->maxdimmperch * 2; + max_csrows = pvt->maxdimmperch; empty = 1; /* Assume NO memory */ @@ -1215,28 +1210,6 @@ static void i5400_enable_error_reporting(struct mem_ctl_info *mci) } /* - * i5400_get_dimm_and_channel_counts(pdev, &num_csrows, &num_channels) - * - * ask the device how many channels are present and how many CSROWS - * as well - */ -static void i5400_get_dimm_and_channel_counts(struct pci_dev *pdev, - int *num_dimms_per_channel, - int *num_channels) -{ - u8 value; - - /* Need to retrieve just how many channels and dimms per channel are - * supported on this memory controller - */ - pci_read_config_byte(pdev, MAXDIMMPERCH, &value); - *num_dimms_per_channel = (int)value * 2; - - pci_read_config_byte(pdev, MAXCH, &value); - *num_channels = (int)value; -} - -/* * i5400_probe1 Probe for ONE instance of device to see if it is * present. * return: @@ -1263,22 +1236,16 @@ static int i5400_probe1(struct pci_dev *pdev, int dev_idx) if (PCI_FUNC(pdev->devfn) != 0) return -ENODEV; - /* Ask the devices for the number of CSROWS and CHANNELS so - * that we can calculate the memory resources, etc - * - * The Chipset will report what it can handle which will be greater - * or equal to what the motherboard manufacturer will implement. - * - * As we don't have a motherboard identification routine to determine + /* As we don't have a motherboard identification routine to determine * actual number of slots/dimms per channel, we thus utilize the * resource as specified by the chipset. Thus, we might have * have more DIMMs per channel than actually on the mobo, but this * allows the driver to support upto the chipset max, without * some fancy mobo determination. */ - i5400_get_dimm_and_channel_counts(pdev, &num_dimms_per_channel, - &num_channels); - num_csrows = num_dimms_per_channel * 2; + num_dimms_per_channel = MAX_DIMMS_PER_CHANNEL; + num_channels = MAX_CHANNELS; + num_csrows = num_dimms_per_channel; debugf0("MC: %s(): Number of - Channels= %d DIMMS= %d CSROWS= %d\n", __func__, num_channels, num_dimms_per_channel, num_csrows); diff --git a/drivers/edac/mpc85xx_edac.c b/drivers/edac/mpc85xx_edac.c index 3f2ccfc6407c..cf27402af97b 100644 --- a/drivers/edac/mpc85xx_edac.c +++ b/drivers/edac/mpc85xx_edac.c @@ -26,7 +26,9 @@ #include "mpc85xx_edac.h" static int edac_dev_idx; +#ifdef CONFIG_PCI static int edac_pci_idx; +#endif static int edac_mc_idx; static u32 orig_ddr_err_disable; @@ -41,7 +43,9 @@ static u32 orig_pci_err_en; #endif static u32 orig_l2_err_disable; +#ifdef CONFIG_MPC85xx static u32 orig_hid1[2]; +#endif /************************ MC SYSFS parts ***********************************/ @@ -646,6 +650,7 @@ static struct of_device_id mpc85xx_l2_err_of_match[] = { { .compatible = "fsl,mpc8560-l2-cache-controller", }, { .compatible = "fsl,mpc8568-l2-cache-controller", }, { .compatible = "fsl,mpc8572-l2-cache-controller", }, + { .compatible = "fsl,p2020-l2-cache-controller", }, {}, }; @@ -788,19 +793,20 @@ static void __devinit mpc85xx_init_csrows(struct mem_ctl_info *mci) csrow = &mci->csrows[index]; cs_bnds = in_be32(pdata->mc_vbase + MPC85XX_MC_CS_BNDS_0 + (index * MPC85XX_MC_CS_BNDS_OFS)); - start = (cs_bnds & 0xfff0000) << 4; - end = ((cs_bnds & 0xfff) << 20); - if (start) - start |= 0xfffff; - if (end) - end |= 0xfffff; + + start = (cs_bnds & 0xffff0000) >> 16; + end = (cs_bnds & 0x0000ffff); if (start == end) continue; /* not populated */ + start <<= (24 - PAGE_SHIFT); + end <<= (24 - PAGE_SHIFT); + end |= (1 << (24 - PAGE_SHIFT)) - 1; + csrow->first_page = start >> PAGE_SHIFT; csrow->last_page = end >> PAGE_SHIFT; - csrow->nr_pages = csrow->last_page + 1 - csrow->first_page; + csrow->nr_pages = end + 1 - start; csrow->grain = 8; csrow->mtype = mtype; csrow->dtype = DEV_UNKNOWN; @@ -984,6 +990,8 @@ static struct of_device_id mpc85xx_mc_err_of_match[] = { { .compatible = "fsl,mpc8560-memory-controller", }, { .compatible = "fsl,mpc8568-memory-controller", }, { .compatible = "fsl,mpc8572-memory-controller", }, + { .compatible = "fsl,mpc8349-memory-controller", }, + { .compatible = "fsl,p2020-memory-controller", }, {}, }; @@ -999,13 +1007,13 @@ static struct of_platform_driver mpc85xx_mc_err_driver = { }, }; - +#ifdef CONFIG_MPC85xx static void __init mpc85xx_mc_clear_rfxe(void *data) { orig_hid1[smp_processor_id()] = mfspr(SPRN_HID1); mtspr(SPRN_HID1, (orig_hid1[smp_processor_id()] & ~0x20000)); } - +#endif static int __init mpc85xx_mc_init(void) { @@ -1038,26 +1046,32 @@ static int __init mpc85xx_mc_init(void) printk(KERN_WARNING EDAC_MOD_STR "PCI fails to register\n"); #endif +#ifdef CONFIG_MPC85xx /* * need to clear HID1[RFXE] to disable machine check int * so we can catch it */ if (edac_op_state == EDAC_OPSTATE_INT) on_each_cpu(mpc85xx_mc_clear_rfxe, NULL, 0); +#endif return 0; } module_init(mpc85xx_mc_init); +#ifdef CONFIG_MPC85xx static void __exit mpc85xx_mc_restore_hid1(void *data) { mtspr(SPRN_HID1, orig_hid1[smp_processor_id()]); } +#endif static void __exit mpc85xx_mc_exit(void) { +#ifdef CONFIG_MPC85xx on_each_cpu(mpc85xx_mc_restore_hid1, NULL, 0); +#endif #ifdef CONFIG_PCI of_unregister_platform_driver(&mpc85xx_pci_err_driver); #endif diff --git a/drivers/edac/mv64x60_edac.c b/drivers/edac/mv64x60_edac.c index 5131aaae8e03..a6b9fec13a74 100644 --- a/drivers/edac/mv64x60_edac.c +++ b/drivers/edac/mv64x60_edac.c @@ -90,7 +90,7 @@ static int __init mv64x60_pci_fixup(struct platform_device *pdev) return -ENOENT; } - pci_serr = ioremap(r->start, r->end - r->start + 1); + pci_serr = ioremap(r->start, resource_size(r)); if (!pci_serr) return -ENOMEM; @@ -140,7 +140,7 @@ static int __devinit mv64x60_pci_err_probe(struct platform_device *pdev) if (!devm_request_mem_region(&pdev->dev, r->start, - r->end - r->start + 1, + resource_size(r), pdata->name)) { printk(KERN_ERR "%s: Error while requesting mem region\n", __func__); @@ -150,7 +150,7 @@ static int __devinit mv64x60_pci_err_probe(struct platform_device *pdev) pdata->pci_vbase = devm_ioremap(&pdev->dev, r->start, - r->end - r->start + 1); + resource_size(r)); if (!pdata->pci_vbase) { printk(KERN_ERR "%s: Unable to setup PCI err regs\n", __func__); res = -ENOMEM; @@ -306,7 +306,7 @@ static int __devinit mv64x60_sram_err_probe(struct platform_device *pdev) if (!devm_request_mem_region(&pdev->dev, r->start, - r->end - r->start + 1, + resource_size(r), pdata->name)) { printk(KERN_ERR "%s: Error while request mem region\n", __func__); @@ -316,7 +316,7 @@ static int __devinit mv64x60_sram_err_probe(struct platform_device *pdev) pdata->sram_vbase = devm_ioremap(&pdev->dev, r->start, - r->end - r->start + 1); + resource_size(r)); if (!pdata->sram_vbase) { printk(KERN_ERR "%s: Unable to setup SRAM err regs\n", __func__); @@ -474,7 +474,7 @@ static int __devinit mv64x60_cpu_err_probe(struct platform_device *pdev) if (!devm_request_mem_region(&pdev->dev, r->start, - r->end - r->start + 1, + resource_size(r), pdata->name)) { printk(KERN_ERR "%s: Error while requesting mem region\n", __func__); @@ -484,7 +484,7 @@ static int __devinit mv64x60_cpu_err_probe(struct platform_device *pdev) pdata->cpu_vbase[0] = devm_ioremap(&pdev->dev, r->start, - r->end - r->start + 1); + resource_size(r)); if (!pdata->cpu_vbase[0]) { printk(KERN_ERR "%s: Unable to setup CPU err regs\n", __func__); res = -ENOMEM; @@ -501,7 +501,7 @@ static int __devinit mv64x60_cpu_err_probe(struct platform_device *pdev) if (!devm_request_mem_region(&pdev->dev, r->start, - r->end - r->start + 1, + resource_size(r), pdata->name)) { printk(KERN_ERR "%s: Error while requesting mem region\n", __func__); @@ -511,7 +511,7 @@ static int __devinit mv64x60_cpu_err_probe(struct platform_device *pdev) pdata->cpu_vbase[1] = devm_ioremap(&pdev->dev, r->start, - r->end - r->start + 1); + resource_size(r)); if (!pdata->cpu_vbase[1]) { printk(KERN_ERR "%s: Unable to setup CPU err regs\n", __func__); res = -ENOMEM; @@ -726,7 +726,7 @@ static int __devinit mv64x60_mc_err_probe(struct platform_device *pdev) if (!devm_request_mem_region(&pdev->dev, r->start, - r->end - r->start + 1, + resource_size(r), pdata->name)) { printk(KERN_ERR "%s: Error while requesting mem region\n", __func__); @@ -736,7 +736,7 @@ static int __devinit mv64x60_mc_err_probe(struct platform_device *pdev) pdata->mc_vbase = devm_ioremap(&pdev->dev, r->start, - r->end - r->start + 1); + resource_size(r)); if (!pdata->mc_vbase) { printk(KERN_ERR "%s: Unable to setup MC err regs\n", __func__); res = -ENOMEM; diff --git a/drivers/firewire/core-card.c b/drivers/firewire/core-card.c index f74edae5cb4c..e4864e894e4f 100644 --- a/drivers/firewire/core-card.c +++ b/drivers/firewire/core-card.c @@ -444,16 +444,13 @@ int fw_card_add(struct fw_card *card, card->guid = guid; mutex_lock(&card_mutex); - config_rom = generate_config_rom(card, &length); - list_add_tail(&card->link, &card_list); - mutex_unlock(&card_mutex); + config_rom = generate_config_rom(card, &length); ret = card->driver->enable(card, config_rom, length); - if (ret < 0) { - mutex_lock(&card_mutex); - list_del(&card->link); - mutex_unlock(&card_mutex); - } + if (ret == 0) + list_add_tail(&card->link, &card_list); + + mutex_unlock(&card_mutex); return ret; } diff --git a/drivers/firewire/core-cdev.c b/drivers/firewire/core-cdev.c index ced186d7e9a9..5089331544ed 100644 --- a/drivers/firewire/core-cdev.c +++ b/drivers/firewire/core-cdev.c @@ -33,6 +33,7 @@ #include <linux/mutex.h> #include <linux/poll.h> #include <linux/preempt.h> +#include <linux/sched.h> #include <linux/spinlock.h> #include <linux/time.h> #include <linux/uaccess.h> diff --git a/drivers/firewire/core-transaction.c b/drivers/firewire/core-transaction.c index 479b22f5a1eb..da628c72a462 100644 --- a/drivers/firewire/core-transaction.c +++ b/drivers/firewire/core-transaction.c @@ -834,7 +834,7 @@ static void handle_topology_map(struct fw_card *card, struct fw_request *request } static struct fw_address_handler topology_map = { - .length = 0x200, + .length = 0x400, .address_callback = handle_topology_map, }; diff --git a/drivers/firewire/core.h b/drivers/firewire/core.h index 6052816be353..7ff6e7585152 100644 --- a/drivers/firewire/core.h +++ b/drivers/firewire/core.h @@ -96,6 +96,20 @@ int fw_core_initiate_bus_reset(struct fw_card *card, int short_reset); int fw_compute_block_crc(u32 *block); void fw_schedule_bm_work(struct fw_card *card, unsigned long delay); +static inline struct fw_card *fw_card_get(struct fw_card *card) +{ + kref_get(&card->kref); + + return card; +} + +void fw_card_release(struct kref *kref); + +static inline void fw_card_put(struct fw_card *card) +{ + kref_put(&card->kref, fw_card_release); +} + /* -cdev */ diff --git a/drivers/firewire/ohci.c b/drivers/firewire/ohci.c index 76b321bb73f9..94260aa76aa3 100644 --- a/drivers/firewire/ohci.c +++ b/drivers/firewire/ohci.c @@ -275,7 +275,7 @@ static void log_irqs(u32 evt) !(evt & OHCI1394_busReset)) return; - fw_notify("IRQ %08x%s%s%s%s%s%s%s%s%s%s%s%s%s\n", evt, + fw_notify("IRQ %08x%s%s%s%s%s%s%s%s%s%s%s%s%s%s\n", evt, evt & OHCI1394_selfIDComplete ? " selfID" : "", evt & OHCI1394_RQPkt ? " AR_req" : "", evt & OHCI1394_RSPkt ? " AR_resp" : "", @@ -286,6 +286,7 @@ static void log_irqs(u32 evt) evt & OHCI1394_postedWriteErr ? " postedWriteErr" : "", evt & OHCI1394_cycleTooLong ? " cycleTooLong" : "", evt & OHCI1394_cycle64Seconds ? " cycle64Seconds" : "", + evt & OHCI1394_cycleInconsistent ? " cycleInconsistent" : "", evt & OHCI1394_regAccessFail ? " regAccessFail" : "", evt & OHCI1394_busReset ? " busReset" : "", evt & ~(OHCI1394_selfIDComplete | OHCI1394_RQPkt | @@ -293,6 +294,7 @@ static void log_irqs(u32 evt) OHCI1394_respTxComplete | OHCI1394_isochRx | OHCI1394_isochTx | OHCI1394_postedWriteErr | OHCI1394_cycleTooLong | OHCI1394_cycle64Seconds | + OHCI1394_cycleInconsistent | OHCI1394_regAccessFail | OHCI1394_busReset) ? " ?" : ""); } @@ -1279,8 +1281,8 @@ static void bus_reset_tasklet(unsigned long data) * the inverted quadlets and a header quadlet, we shift one * bit extra to get the actual number of self IDs. */ - self_id_count = (reg >> 3) & 0x3ff; - if (self_id_count == 0) { + self_id_count = (reg >> 3) & 0xff; + if (self_id_count == 0 || self_id_count > 252) { fw_notify("inconsistent self IDs\n"); return; } @@ -1439,6 +1441,17 @@ static irqreturn_t irq_handler(int irq, void *data) OHCI1394_LinkControl_cycleMaster); } + if (unlikely(event & OHCI1394_cycleInconsistent)) { + /* + * We need to clear this event bit in order to make + * cycleMatch isochronous I/O work. In theory we should + * stop active cycleMatch iso contexts now and restart + * them at least two cycles later. (FIXME?) + */ + if (printk_ratelimit()) + fw_notify("isochronous cycle inconsistent\n"); + } + if (event & OHCI1394_cycle64Seconds) { cycle_time = reg_read(ohci, OHCI1394_IsochronousCycleTimer); if ((cycle_time & 0x80000000) == 0) @@ -1528,6 +1541,7 @@ static int ohci_enable(struct fw_card *card, u32 *config_rom, size_t length) OHCI1394_reqTxComplete | OHCI1394_respTxComplete | OHCI1394_isochRx | OHCI1394_isochTx | OHCI1394_postedWriteErr | OHCI1394_cycleTooLong | + OHCI1394_cycleInconsistent | OHCI1394_cycle64Seconds | OHCI1394_regAccessFail | OHCI1394_masterIntEnable); if (param_debug & OHCI_PARAM_DEBUG_BUSRESETS) @@ -1890,15 +1904,30 @@ static int handle_it_packet(struct context *context, { struct iso_context *ctx = container_of(context, struct iso_context, context); + int i; + struct descriptor *pd; - if (last->transfer_status == 0) - /* This descriptor isn't done yet, stop iteration. */ + for (pd = d; pd <= last; pd++) + if (pd->transfer_status) + break; + if (pd > last) + /* Descriptor(s) not done yet, stop iteration */ return 0; - if (le16_to_cpu(last->control) & DESCRIPTOR_IRQ_ALWAYS) + i = ctx->header_length; + if (i + 4 < PAGE_SIZE) { + /* Present this value as big-endian to match the receive code */ + *(__be32 *)(ctx->header + i) = cpu_to_be32( + ((u32)le16_to_cpu(pd->transfer_status) << 16) | + le16_to_cpu(pd->res_count)); + ctx->header_length += 4; + } + if (le16_to_cpu(last->control) & DESCRIPTOR_IRQ_ALWAYS) { ctx->base.callback(&ctx->base, le16_to_cpu(last->res_count), - 0, NULL, ctx->base.callback_data); - + ctx->header_length, ctx->header, + ctx->base.callback_data); + ctx->header_length = 0; + } return 1; } diff --git a/drivers/firewire/sbp2.c b/drivers/firewire/sbp2.c index e5df822a8130..98dbbda3ad41 100644 --- a/drivers/firewire/sbp2.c +++ b/drivers/firewire/sbp2.c @@ -188,14 +188,7 @@ static struct fw_device *target_device(struct sbp2_target *tgt) /* Impossible login_id, to detect logout attempt before successful login */ #define INVALID_LOGIN_ID 0x10000 -/* - * Per section 7.4.8 of the SBP-2 spec, a mgt_ORB_timeout value can be - * provided in the config rom. Most devices do provide a value, which - * we'll use for login management orbs, but with some sane limits. - */ -#define SBP2_MIN_LOGIN_ORB_TIMEOUT 5000U /* Timeout in ms */ -#define SBP2_MAX_LOGIN_ORB_TIMEOUT 40000U /* Timeout in ms */ -#define SBP2_ORB_TIMEOUT 2000U /* Timeout in ms */ +#define SBP2_ORB_TIMEOUT 2000U /* Timeout in ms */ #define SBP2_ORB_NULL 0x80000000 #define SBP2_RETRY_LIMIT 0xf /* 15 retries */ #define SBP2_CYCLE_LIMIT (0xc8 << 12) /* 200 125us cycles */ @@ -354,8 +347,7 @@ static const struct { /* DViCO Momobay FX-3A with TSB42AA9A bridge */ { .firmware_revision = 0x002800, .model = 0x000000, - .workarounds = SBP2_WORKAROUND_DELAY_INQUIRY | - SBP2_WORKAROUND_POWER_CONDITION, + .workarounds = SBP2_WORKAROUND_POWER_CONDITION, }, /* Initio bridges, actually only needed for some older ones */ { .firmware_revision = 0x000200, @@ -425,19 +417,20 @@ static void sbp2_status_write(struct fw_card *card, struct fw_request *request, struct sbp2_logical_unit *lu = callback_data; struct sbp2_orb *orb; struct sbp2_status status; - size_t header_size; unsigned long flags; if (tcode != TCODE_WRITE_BLOCK_REQUEST || - length == 0 || length > sizeof(status)) { + length < 8 || length > sizeof(status)) { fw_send_response(card, request, RCODE_TYPE_ERROR); return; } - header_size = min(length, 2 * sizeof(u32)); - fw_memcpy_from_be32(&status, payload, header_size); - if (length > header_size) - memcpy(status.data, payload + 8, length - header_size); + status.status = be32_to_cpup(payload); + status.orb_low = be32_to_cpup(payload + 4); + memset(status.data, 0, sizeof(status.data)); + if (length > 8) + memcpy(status.data, payload + 8, length - 8); + if (STATUS_GET_SOURCE(status) == 2 || STATUS_GET_SOURCE(status) == 3) { fw_notify("non-orb related status write, not handled\n"); fw_send_response(card, request, RCODE_COMPLETE); @@ -1034,7 +1027,6 @@ static int sbp2_scan_unit_dir(struct sbp2_target *tgt, u32 *directory, { struct fw_csr_iterator ci; int key, value; - unsigned int timeout; fw_csr_iterator_init(&ci, directory); while (fw_csr_iterator_next(&ci, &key, &value)) { @@ -1059,17 +1051,7 @@ static int sbp2_scan_unit_dir(struct sbp2_target *tgt, u32 *directory, case SBP2_CSR_UNIT_CHARACTERISTICS: /* the timeout value is stored in 500ms units */ - timeout = ((unsigned int) value >> 8 & 0xff) * 500; - timeout = max(timeout, SBP2_MIN_LOGIN_ORB_TIMEOUT); - tgt->mgt_orb_timeout = - min(timeout, SBP2_MAX_LOGIN_ORB_TIMEOUT); - - if (timeout > tgt->mgt_orb_timeout) - fw_notify("%s: config rom contains %ds " - "management ORB timeout, limiting " - "to %ds\n", tgt->bus_id, - timeout / 1000, - tgt->mgt_orb_timeout / 1000); + tgt->mgt_orb_timeout = (value >> 8 & 0xff) * 500; break; case SBP2_CSR_LOGICAL_UNIT_NUMBER: @@ -1087,6 +1069,22 @@ static int sbp2_scan_unit_dir(struct sbp2_target *tgt, u32 *directory, return 0; } +/* + * Per section 7.4.8 of the SBP-2 spec, a mgt_ORB_timeout value can be + * provided in the config rom. Most devices do provide a value, which + * we'll use for login management orbs, but with some sane limits. + */ +static void sbp2_clamp_management_orb_timeout(struct sbp2_target *tgt) +{ + unsigned int timeout = tgt->mgt_orb_timeout; + + if (timeout > 40000) + fw_notify("%s: %ds mgt_ORB_timeout limited to 40s\n", + tgt->bus_id, timeout / 1000); + + tgt->mgt_orb_timeout = clamp_val(timeout, 5000, 40000); +} + static void sbp2_init_workarounds(struct sbp2_target *tgt, u32 model, u32 firmware_revision) { @@ -1171,6 +1169,7 @@ static int sbp2_probe(struct device *dev) &firmware_revision) < 0) goto fail_tgt_put; + sbp2_clamp_management_orb_timeout(tgt); sbp2_init_workarounds(tgt, model, firmware_revision); /* diff --git a/drivers/firmware/iscsi_ibft.c b/drivers/firmware/iscsi_ibft.c index 420a96e7f2db..051d1ebbd287 100644 --- a/drivers/firmware/iscsi_ibft.c +++ b/drivers/firmware/iscsi_ibft.c @@ -939,7 +939,7 @@ static int __init ibft_init(void) if (ibft_addr) { printk(KERN_INFO "iBFT detected at 0x%llx.\n", - (u64)virt_to_phys((void *)ibft_addr)); + (u64)isa_virt_to_bus(ibft_addr)); rc = ibft_check_device(); if (rc) diff --git a/drivers/firmware/iscsi_ibft_find.c b/drivers/firmware/iscsi_ibft_find.c index d53fbbfefa3e..dfb15c06c88f 100644 --- a/drivers/firmware/iscsi_ibft_find.c +++ b/drivers/firmware/iscsi_ibft_find.c @@ -65,10 +65,10 @@ void __init reserve_ibft_region(void) * so skip that area */ if (pos == VGA_MEM) pos += VGA_SIZE; - virt = phys_to_virt(pos); + virt = isa_bus_to_virt(pos); if (memcmp(virt, IBFT_SIGN, IBFT_SIGN_LEN) == 0) { unsigned long *addr = - (unsigned long *)phys_to_virt(pos + 4); + (unsigned long *)isa_bus_to_virt(pos + 4); len = *addr; /* if the length of the table extends past 1M, * the table cannot be valid. */ diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 6b4c484a699a..2ad0128c63c6 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -162,6 +162,16 @@ config GPIO_WM831X Say yes here to access the GPIO signals of WM831x power management chips from Wolfson Microelectronics. +config GPIO_ADP5520 + tristate "GPIO Support for ADP5520 PMIC" + depends on PMIC_ADP5520 + help + This option enables support for on-chip GPIO found + on Analog Devices ADP5520 PMICs. + + To compile this driver as a module, choose M here: the module will + be called adp5520-gpio. + comment "PCI GPIO expanders:" config GPIO_BT8XX @@ -180,6 +190,12 @@ config GPIO_BT8XX If unsure, say N. +config GPIO_LANGWELL + bool "Intel Moorestown Platform Langwell GPIO support" + depends on PCI + help + Say Y here to support Intel Moorestown platform GPIO. + comment "SPI GPIO expanders:" config GPIO_MAX7301 @@ -195,4 +211,23 @@ config GPIO_MCP23S08 SPI driver for Microchip MCP23S08 I/O expander. This provides a GPIO interface supporting inputs and outputs. +config GPIO_MC33880 + tristate "Freescale MC33880 high-side/low-side switch" + depends on SPI_MASTER + help + SPI driver for Freescale MC33880 high-side/low-side switch. + This provides GPIO interface supporting inputs and outputs. + +comment "AC97 GPIO expanders:" + +config GPIO_UCB1400 + bool "Philips UCB1400 GPIO" + depends on UCB1400_CORE + help + This enables support for the Philips UCB1400 GPIO pins. + The UCB1400 is an AC97 audio codec. + + To compile this driver as a module, choose M here: the + module will be called ucb1400_gpio. + endif diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index ea7c745f26a8..00a532c9a1e2 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -4,13 +4,17 @@ ccflags-$(CONFIG_DEBUG_GPIO) += -DDEBUG obj-$(CONFIG_GPIOLIB) += gpiolib.o +obj-$(CONFIG_GPIO_ADP5520) += adp5520-gpio.o +obj-$(CONFIG_GPIO_LANGWELL) += langwell_gpio.o obj-$(CONFIG_GPIO_MAX7301) += max7301.o obj-$(CONFIG_GPIO_MAX732X) += max732x.o +obj-$(CONFIG_GPIO_MC33880) += mc33880.o obj-$(CONFIG_GPIO_MCP23S08) += mcp23s08.o obj-$(CONFIG_GPIO_PCA953X) += pca953x.o obj-$(CONFIG_GPIO_PCF857X) += pcf857x.o obj-$(CONFIG_GPIO_PL061) += pl061.o obj-$(CONFIG_GPIO_TWL4030) += twl4030-gpio.o +obj-$(CONFIG_GPIO_UCB1400) += ucb1400_gpio.o obj-$(CONFIG_GPIO_XILINX) += xilinx_gpio.o obj-$(CONFIG_GPIO_BT8XX) += bt8xxgpio.o obj-$(CONFIG_GPIO_VR41XX) += vr41xx_giu.o diff --git a/drivers/gpio/adp5520-gpio.c b/drivers/gpio/adp5520-gpio.c new file mode 100644 index 000000000000..ad05bbc7ffd5 --- /dev/null +++ b/drivers/gpio/adp5520-gpio.c @@ -0,0 +1,206 @@ +/* + * GPIO driver for Analog Devices ADP5520 MFD PMICs + * + * Copyright 2009 Analog Devices Inc. + * + * Licensed under the GPL-2 or later. + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/platform_device.h> +#include <linux/mfd/adp5520.h> + +#include <linux/gpio.h> + +struct adp5520_gpio { + struct device *master; + struct gpio_chip gpio_chip; + unsigned char lut[ADP5520_MAXGPIOS]; + unsigned long output; +}; + +static int adp5520_gpio_get_value(struct gpio_chip *chip, unsigned off) +{ + struct adp5520_gpio *dev; + uint8_t reg_val; + + dev = container_of(chip, struct adp5520_gpio, gpio_chip); + + /* + * There are dedicated registers for GPIO IN/OUT. + * Make sure we return the right value, even when configured as output + */ + + if (test_bit(off, &dev->output)) + adp5520_read(dev->master, GPIO_OUT, ®_val); + else + adp5520_read(dev->master, GPIO_IN, ®_val); + + return !!(reg_val & dev->lut[off]); +} + +static void adp5520_gpio_set_value(struct gpio_chip *chip, + unsigned off, int val) +{ + struct adp5520_gpio *dev; + dev = container_of(chip, struct adp5520_gpio, gpio_chip); + + if (val) + adp5520_set_bits(dev->master, GPIO_OUT, dev->lut[off]); + else + adp5520_clr_bits(dev->master, GPIO_OUT, dev->lut[off]); +} + +static int adp5520_gpio_direction_input(struct gpio_chip *chip, unsigned off) +{ + struct adp5520_gpio *dev; + dev = container_of(chip, struct adp5520_gpio, gpio_chip); + + clear_bit(off, &dev->output); + + return adp5520_clr_bits(dev->master, GPIO_CFG_2, dev->lut[off]); +} + +static int adp5520_gpio_direction_output(struct gpio_chip *chip, + unsigned off, int val) +{ + struct adp5520_gpio *dev; + int ret = 0; + dev = container_of(chip, struct adp5520_gpio, gpio_chip); + + set_bit(off, &dev->output); + + if (val) + ret |= adp5520_set_bits(dev->master, GPIO_OUT, dev->lut[off]); + else + ret |= adp5520_clr_bits(dev->master, GPIO_OUT, dev->lut[off]); + + ret |= adp5520_set_bits(dev->master, GPIO_CFG_2, dev->lut[off]); + + return ret; +} + +static int __devinit adp5520_gpio_probe(struct platform_device *pdev) +{ + struct adp5520_gpio_platfrom_data *pdata = pdev->dev.platform_data; + struct adp5520_gpio *dev; + struct gpio_chip *gc; + int ret, i, gpios; + unsigned char ctl_mask = 0; + + if (pdata == NULL) { + dev_err(&pdev->dev, "missing platform data\n"); + return -ENODEV; + } + + if (pdev->id != ID_ADP5520) { + dev_err(&pdev->dev, "only ADP5520 supports GPIO\n"); + return -ENODEV; + } + + dev = kzalloc(sizeof(*dev), GFP_KERNEL); + if (dev == NULL) { + dev_err(&pdev->dev, "failed to alloc memory\n"); + return -ENOMEM; + } + + dev->master = pdev->dev.parent; + + for (gpios = 0, i = 0; i < ADP5520_MAXGPIOS; i++) + if (pdata->gpio_en_mask & (1 << i)) + dev->lut[gpios++] = 1 << i; + + if (gpios < 1) { + ret = -EINVAL; + goto err; + } + + gc = &dev->gpio_chip; + gc->direction_input = adp5520_gpio_direction_input; + gc->direction_output = adp5520_gpio_direction_output; + gc->get = adp5520_gpio_get_value; + gc->set = adp5520_gpio_set_value; + gc->can_sleep = 1; + + gc->base = pdata->gpio_start; + gc->ngpio = gpios; + gc->label = pdev->name; + gc->owner = THIS_MODULE; + + ret = adp5520_clr_bits(dev->master, GPIO_CFG_1, + pdata->gpio_en_mask); + + if (pdata->gpio_en_mask & GPIO_C3) + ctl_mask |= C3_MODE; + + if (pdata->gpio_en_mask & GPIO_R3) + ctl_mask |= R3_MODE; + + if (ctl_mask) + ret = adp5520_set_bits(dev->master, LED_CONTROL, + ctl_mask); + + ret |= adp5520_set_bits(dev->master, GPIO_PULLUP, + pdata->gpio_pullup_mask); + + if (ret) { + dev_err(&pdev->dev, "failed to write\n"); + goto err; + } + + ret = gpiochip_add(&dev->gpio_chip); + if (ret) + goto err; + + platform_set_drvdata(pdev, dev); + return 0; + +err: + kfree(dev); + return ret; +} + +static int __devexit adp5520_gpio_remove(struct platform_device *pdev) +{ + struct adp5520_gpio *dev; + int ret; + + dev = platform_get_drvdata(pdev); + ret = gpiochip_remove(&dev->gpio_chip); + if (ret) { + dev_err(&pdev->dev, "%s failed, %d\n", + "gpiochip_remove()", ret); + return ret; + } + + kfree(dev); + return 0; +} + +static struct platform_driver adp5520_gpio_driver = { + .driver = { + .name = "adp5520-gpio", + .owner = THIS_MODULE, + }, + .probe = adp5520_gpio_probe, + .remove = __devexit_p(adp5520_gpio_remove), +}; + +static int __init adp5520_gpio_init(void) +{ + return platform_driver_register(&adp5520_gpio_driver); +} +module_init(adp5520_gpio_init); + +static void __exit adp5520_gpio_exit(void) +{ + platform_driver_unregister(&adp5520_gpio_driver); +} +module_exit(adp5520_gpio_exit); + +MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>"); +MODULE_DESCRIPTION("GPIO ADP5520 Driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:adp5520-gpio"); diff --git a/drivers/gpio/bt8xxgpio.c b/drivers/gpio/bt8xxgpio.c index 55904140213b..2559f2289409 100644 --- a/drivers/gpio/bt8xxgpio.c +++ b/drivers/gpio/bt8xxgpio.c @@ -46,8 +46,7 @@ #include <linux/module.h> #include <linux/pci.h> #include <linux/spinlock.h> - -#include <asm/gpio.h> +#include <linux/gpio.h> /* Steal the hardware definitions from the bttv driver. */ #include "../media/video/bt8xx/bt848.h" diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 51a8d4103be5..50de0f5750d8 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -1,5 +1,6 @@ #include <linux/kernel.h> #include <linux/module.h> +#include <linux/interrupt.h> #include <linux/irq.h> #include <linux/spinlock.h> #include <linux/device.h> @@ -7,6 +8,7 @@ #include <linux/debugfs.h> #include <linux/seq_file.h> #include <linux/gpio.h> +#include <linux/idr.h> /* Optional implementation infrastructure for GPIO interfaces. @@ -49,6 +51,13 @@ struct gpio_desc { #define FLAG_RESERVED 2 #define FLAG_EXPORT 3 /* protected by sysfs_lock */ #define FLAG_SYSFS 4 /* exported via /sys/class/gpio/control */ +#define FLAG_TRIG_FALL 5 /* trigger on falling edge */ +#define FLAG_TRIG_RISE 6 /* trigger on rising edge */ + +#define PDESC_ID_SHIFT 16 /* add new flags before this one */ + +#define GPIO_FLAGS_MASK ((1 << PDESC_ID_SHIFT) - 1) +#define GPIO_TRIGGER_MASK (BIT(FLAG_TRIG_FALL) | BIT(FLAG_TRIG_RISE)) #ifdef CONFIG_DEBUG_FS const char *label; @@ -56,6 +65,15 @@ struct gpio_desc { }; static struct gpio_desc gpio_desc[ARCH_NR_GPIOS]; +#ifdef CONFIG_GPIO_SYSFS +struct poll_desc { + struct work_struct work; + struct sysfs_dirent *value_sd; +}; + +static struct idr pdesc_idr; +#endif + static inline void desc_set_label(struct gpio_desc *d, const char *label) { #ifdef CONFIG_DEBUG_FS @@ -188,10 +206,10 @@ static DEFINE_MUTEX(sysfs_lock); * /value * * always readable, subject to hardware behavior * * may be writable, as zero/nonzero - * - * REVISIT there will likely be an attribute for configuring async - * notifications, e.g. to specify polling interval or IRQ trigger type - * that would for example trigger a poll() on the "value". + * /edge + * * configures behavior of poll(2) on /value + * * available only if pin can generate IRQs on input + * * is read/write as "none", "falling", "rising", or "both" */ static ssize_t gpio_direction_show(struct device *dev, @@ -288,6 +306,175 @@ static ssize_t gpio_value_store(struct device *dev, static /*const*/ DEVICE_ATTR(value, 0644, gpio_value_show, gpio_value_store); +static irqreturn_t gpio_sysfs_irq(int irq, void *priv) +{ + struct work_struct *work = priv; + + schedule_work(work); + return IRQ_HANDLED; +} + +static void gpio_notify_sysfs(struct work_struct *work) +{ + struct poll_desc *pdesc; + + pdesc = container_of(work, struct poll_desc, work); + sysfs_notify_dirent(pdesc->value_sd); +} + +static int gpio_setup_irq(struct gpio_desc *desc, struct device *dev, + unsigned long gpio_flags) +{ + struct poll_desc *pdesc; + unsigned long irq_flags; + int ret, irq, id; + + if ((desc->flags & GPIO_TRIGGER_MASK) == gpio_flags) + return 0; + + irq = gpio_to_irq(desc - gpio_desc); + if (irq < 0) + return -EIO; + + id = desc->flags >> PDESC_ID_SHIFT; + pdesc = idr_find(&pdesc_idr, id); + if (pdesc) { + free_irq(irq, &pdesc->work); + cancel_work_sync(&pdesc->work); + } + + desc->flags &= ~GPIO_TRIGGER_MASK; + + if (!gpio_flags) { + ret = 0; + goto free_sd; + } + + irq_flags = IRQF_SHARED; + if (test_bit(FLAG_TRIG_FALL, &gpio_flags)) + irq_flags |= IRQF_TRIGGER_FALLING; + if (test_bit(FLAG_TRIG_RISE, &gpio_flags)) + irq_flags |= IRQF_TRIGGER_RISING; + + if (!pdesc) { + pdesc = kmalloc(sizeof(*pdesc), GFP_KERNEL); + if (!pdesc) { + ret = -ENOMEM; + goto err_out; + } + + do { + ret = -ENOMEM; + if (idr_pre_get(&pdesc_idr, GFP_KERNEL)) + ret = idr_get_new_above(&pdesc_idr, + pdesc, 1, &id); + } while (ret == -EAGAIN); + + if (ret) + goto free_mem; + + desc->flags &= GPIO_FLAGS_MASK; + desc->flags |= (unsigned long)id << PDESC_ID_SHIFT; + + if (desc->flags >> PDESC_ID_SHIFT != id) { + ret = -ERANGE; + goto free_id; + } + + pdesc->value_sd = sysfs_get_dirent(dev->kobj.sd, "value"); + if (!pdesc->value_sd) { + ret = -ENODEV; + goto free_id; + } + INIT_WORK(&pdesc->work, gpio_notify_sysfs); + } + + ret = request_irq(irq, gpio_sysfs_irq, irq_flags, + "gpiolib", &pdesc->work); + if (ret) + goto free_sd; + + desc->flags |= gpio_flags; + return 0; + +free_sd: + sysfs_put(pdesc->value_sd); +free_id: + idr_remove(&pdesc_idr, id); + desc->flags &= GPIO_FLAGS_MASK; +free_mem: + kfree(pdesc); +err_out: + return ret; +} + +static const struct { + const char *name; + unsigned long flags; +} trigger_types[] = { + { "none", 0 }, + { "falling", BIT(FLAG_TRIG_FALL) }, + { "rising", BIT(FLAG_TRIG_RISE) }, + { "both", BIT(FLAG_TRIG_FALL) | BIT(FLAG_TRIG_RISE) }, +}; + +static ssize_t gpio_edge_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + const struct gpio_desc *desc = dev_get_drvdata(dev); + ssize_t status; + + mutex_lock(&sysfs_lock); + + if (!test_bit(FLAG_EXPORT, &desc->flags)) + status = -EIO; + else { + int i; + + status = 0; + for (i = 0; i < ARRAY_SIZE(trigger_types); i++) + if ((desc->flags & GPIO_TRIGGER_MASK) + == trigger_types[i].flags) { + status = sprintf(buf, "%s\n", + trigger_types[i].name); + break; + } + } + + mutex_unlock(&sysfs_lock); + return status; +} + +static ssize_t gpio_edge_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + struct gpio_desc *desc = dev_get_drvdata(dev); + ssize_t status; + int i; + + for (i = 0; i < ARRAY_SIZE(trigger_types); i++) + if (sysfs_streq(trigger_types[i].name, buf)) + goto found; + return -EINVAL; + +found: + mutex_lock(&sysfs_lock); + + if (!test_bit(FLAG_EXPORT, &desc->flags)) + status = -EIO; + else { + status = gpio_setup_irq(desc, dev, trigger_types[i].flags); + if (!status) + status = size; + } + + mutex_unlock(&sysfs_lock); + + return status; +} + +static DEVICE_ATTR(edge, 0644, gpio_edge_show, gpio_edge_store); + static const struct attribute *gpio_attrs[] = { &dev_attr_direction.attr, &dev_attr_value.attr, @@ -473,18 +660,26 @@ int gpio_export(unsigned gpio, bool direction_may_change) struct device *dev; dev = device_create(&gpio_class, desc->chip->dev, MKDEV(0, 0), - desc, ioname ? ioname : "gpio%d", gpio); - if (dev) { + desc, ioname ? ioname : "gpio%d", gpio); + if (!IS_ERR(dev)) { if (direction_may_change) status = sysfs_create_group(&dev->kobj, &gpio_attr_group); else status = device_create_file(dev, &dev_attr_value); + + if (!status && gpio_to_irq(gpio) >= 0 + && (direction_may_change + || !test_bit(FLAG_IS_OUT, + &desc->flags))) + status = device_create_file(dev, + &dev_attr_edge); + if (status != 0) device_unregister(dev); } else - status = -ENODEV; + status = PTR_ERR(dev); if (status == 0) set_bit(FLAG_EXPORT, &desc->flags); } @@ -505,6 +700,51 @@ static int match_export(struct device *dev, void *data) } /** + * gpio_export_link - create a sysfs link to an exported GPIO node + * @dev: device under which to create symlink + * @name: name of the symlink + * @gpio: gpio to create symlink to, already exported + * + * Set up a symlink from /sys/.../dev/name to /sys/class/gpio/gpioN + * node. Caller is responsible for unlinking. + * + * Returns zero on success, else an error. + */ +int gpio_export_link(struct device *dev, const char *name, unsigned gpio) +{ + struct gpio_desc *desc; + int status = -EINVAL; + + if (!gpio_is_valid(gpio)) + goto done; + + mutex_lock(&sysfs_lock); + + desc = &gpio_desc[gpio]; + + if (test_bit(FLAG_EXPORT, &desc->flags)) { + struct device *tdev; + + tdev = class_find_device(&gpio_class, NULL, desc, match_export); + if (tdev != NULL) { + status = sysfs_create_link(&dev->kobj, &tdev->kobj, + name); + } else { + status = -ENODEV; + } + } + + mutex_unlock(&sysfs_lock); + +done: + if (status) + pr_debug("%s: gpio%d status %d\n", __func__, gpio, status); + + return status; +} +EXPORT_SYMBOL_GPL(gpio_export_link); + +/** * gpio_unexport - reverse effect of gpio_export() * @gpio: gpio to make unavailable * @@ -527,6 +767,7 @@ void gpio_unexport(unsigned gpio) dev = class_find_device(&gpio_class, NULL, desc, match_export); if (dev) { + gpio_setup_irq(desc, dev, 0); clear_bit(FLAG_EXPORT, &desc->flags); put_device(dev); device_unregister(dev); @@ -559,11 +800,11 @@ static int gpiochip_export(struct gpio_chip *chip) mutex_lock(&sysfs_lock); dev = device_create(&gpio_class, chip->dev, MKDEV(0, 0), chip, "gpiochip%d", chip->base); - if (dev) { + if (!IS_ERR(dev)) { status = sysfs_create_group(&dev->kobj, &gpiochip_attr_group); } else - status = -ENODEV; + status = PTR_ERR(dev); chip->exported = (status == 0); mutex_unlock(&sysfs_lock); @@ -611,6 +852,8 @@ static int __init gpiolib_sysfs_init(void) unsigned long flags; unsigned gpio; + idr_init(&pdesc_idr); + status = class_register(&gpio_class); if (status < 0) return status; @@ -1244,7 +1487,7 @@ static int gpiolib_open(struct inode *inode, struct file *file) return single_open(file, gpiolib_show, NULL); } -static struct file_operations gpiolib_operations = { +static const struct file_operations gpiolib_operations = { .open = gpiolib_open, .read = seq_read, .llseek = seq_lseek, diff --git a/drivers/gpio/langwell_gpio.c b/drivers/gpio/langwell_gpio.c new file mode 100644 index 000000000000..5711ce5353c6 --- /dev/null +++ b/drivers/gpio/langwell_gpio.c @@ -0,0 +1,297 @@ +/* langwell_gpio.c Moorestown platform Langwell chip GPIO driver + * Copyright (c) 2008 - 2009, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/* Supports: + * Moorestown platform Langwell chip. + */ + +#include <linux/module.h> +#include <linux/pci.h> +#include <linux/kernel.h> +#include <linux/delay.h> +#include <linux/stddef.h> +#include <linux/interrupt.h> +#include <linux/init.h> +#include <linux/irq.h> +#include <linux/io.h> +#include <linux/gpio.h> + +struct lnw_gpio_register { + u32 GPLR[2]; + u32 GPDR[2]; + u32 GPSR[2]; + u32 GPCR[2]; + u32 GRER[2]; + u32 GFER[2]; + u32 GEDR[2]; +}; + +struct lnw_gpio { + struct gpio_chip chip; + struct lnw_gpio_register *reg_base; + spinlock_t lock; + unsigned irq_base; +}; + +static int lnw_gpio_get(struct gpio_chip *chip, unsigned offset) +{ + struct lnw_gpio *lnw = container_of(chip, struct lnw_gpio, chip); + u8 reg = offset / 32; + void __iomem *gplr; + + gplr = (void __iomem *)(&lnw->reg_base->GPLR[reg]); + return readl(gplr) & BIT(offset % 32); +} + +static void lnw_gpio_set(struct gpio_chip *chip, unsigned offset, int value) +{ + struct lnw_gpio *lnw = container_of(chip, struct lnw_gpio, chip); + u8 reg = offset / 32; + void __iomem *gpsr, *gpcr; + + if (value) { + gpsr = (void __iomem *)(&lnw->reg_base->GPSR[reg]); + writel(BIT(offset % 32), gpsr); + } else { + gpcr = (void __iomem *)(&lnw->reg_base->GPCR[reg]); + writel(BIT(offset % 32), gpcr); + } +} + +static int lnw_gpio_direction_input(struct gpio_chip *chip, unsigned offset) +{ + struct lnw_gpio *lnw = container_of(chip, struct lnw_gpio, chip); + u8 reg = offset / 32; + u32 value; + unsigned long flags; + void __iomem *gpdr; + + gpdr = (void __iomem *)(&lnw->reg_base->GPDR[reg]); + spin_lock_irqsave(&lnw->lock, flags); + value = readl(gpdr); + value &= ~BIT(offset % 32); + writel(value, gpdr); + spin_unlock_irqrestore(&lnw->lock, flags); + return 0; +} + +static int lnw_gpio_direction_output(struct gpio_chip *chip, + unsigned offset, int value) +{ + struct lnw_gpio *lnw = container_of(chip, struct lnw_gpio, chip); + u8 reg = offset / 32; + unsigned long flags; + void __iomem *gpdr; + + lnw_gpio_set(chip, offset, value); + gpdr = (void __iomem *)(&lnw->reg_base->GPDR[reg]); + spin_lock_irqsave(&lnw->lock, flags); + value = readl(gpdr); + value |= BIT(offset % 32);; + writel(value, gpdr); + spin_unlock_irqrestore(&lnw->lock, flags); + return 0; +} + +static int lnw_gpio_to_irq(struct gpio_chip *chip, unsigned offset) +{ + struct lnw_gpio *lnw = container_of(chip, struct lnw_gpio, chip); + return lnw->irq_base + offset; +} + +static int lnw_irq_type(unsigned irq, unsigned type) +{ + struct lnw_gpio *lnw = get_irq_chip_data(irq); + u32 gpio = irq - lnw->irq_base; + u8 reg = gpio / 32; + unsigned long flags; + u32 value; + void __iomem *grer = (void __iomem *)(&lnw->reg_base->GRER[reg]); + void __iomem *gfer = (void __iomem *)(&lnw->reg_base->GFER[reg]); + + if (gpio < 0 || gpio > lnw->chip.ngpio) + return -EINVAL; + spin_lock_irqsave(&lnw->lock, flags); + if (type & IRQ_TYPE_EDGE_RISING) + value = readl(grer) | BIT(gpio % 32); + else + value = readl(grer) & (~BIT(gpio % 32)); + writel(value, grer); + + if (type & IRQ_TYPE_EDGE_FALLING) + value = readl(gfer) | BIT(gpio % 32); + else + value = readl(gfer) & (~BIT(gpio % 32)); + writel(value, gfer); + spin_unlock_irqrestore(&lnw->lock, flags); + + return 0; +}; + +static void lnw_irq_unmask(unsigned irq) +{ + struct lnw_gpio *lnw = get_irq_chip_data(irq); + u32 gpio = irq - lnw->irq_base; + u8 reg = gpio / 32; + void __iomem *gedr; + + gedr = (void __iomem *)(&lnw->reg_base->GEDR[reg]); + writel(BIT(gpio % 32), gedr); +}; + +static void lnw_irq_mask(unsigned irq) +{ +}; + +static struct irq_chip lnw_irqchip = { + .name = "LNW-GPIO", + .mask = lnw_irq_mask, + .unmask = lnw_irq_unmask, + .set_type = lnw_irq_type, +}; + +static struct pci_device_id lnw_gpio_ids[] = { + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x080f) }, + { 0, } +}; +MODULE_DEVICE_TABLE(pci, lnw_gpio_ids); + +static void lnw_irq_handler(unsigned irq, struct irq_desc *desc) +{ + struct lnw_gpio *lnw = (struct lnw_gpio *)get_irq_data(irq); + u32 reg, gpio; + void __iomem *gedr; + u32 gedr_v; + + /* check GPIO controller to check which pin triggered the interrupt */ + for (reg = 0; reg < lnw->chip.ngpio / 32; reg++) { + gedr = (void __iomem *)(&lnw->reg_base->GEDR[reg]); + gedr_v = readl(gedr); + if (!gedr_v) + continue; + for (gpio = reg*32; gpio < reg*32+32; gpio++) { + gedr_v = readl(gedr); + if (gedr_v & BIT(gpio % 32)) { + pr_debug("pin %d triggered\n", gpio); + generic_handle_irq(lnw->irq_base + gpio); + } + } + /* clear the edge detect status bit */ + writel(gedr_v, gedr); + } + desc->chip->eoi(irq); +} + +static int __devinit lnw_gpio_probe(struct pci_dev *pdev, + const struct pci_device_id *id) +{ + void *base; + int i; + resource_size_t start, len; + struct lnw_gpio *lnw; + u32 irq_base; + u32 gpio_base; + int retval = 0; + + retval = pci_enable_device(pdev); + if (retval) + goto done; + + retval = pci_request_regions(pdev, "langwell_gpio"); + if (retval) { + dev_err(&pdev->dev, "error requesting resources\n"); + goto err2; + } + /* get the irq_base from bar1 */ + start = pci_resource_start(pdev, 1); + len = pci_resource_len(pdev, 1); + base = ioremap_nocache(start, len); + if (!base) { + dev_err(&pdev->dev, "error mapping bar1\n"); + goto err3; + } + irq_base = *(u32 *)base; + gpio_base = *((u32 *)base + 1); + /* release the IO mapping, since we already get the info from bar1 */ + iounmap(base); + /* get the register base from bar0 */ + start = pci_resource_start(pdev, 0); + len = pci_resource_len(pdev, 0); + base = ioremap_nocache(start, len); + if (!base) { + dev_err(&pdev->dev, "error mapping bar0\n"); + retval = -EFAULT; + goto err3; + } + + lnw = kzalloc(sizeof(struct lnw_gpio), GFP_KERNEL); + if (!lnw) { + dev_err(&pdev->dev, "can't allocate langwell_gpio chip data\n"); + retval = -ENOMEM; + goto err4; + } + lnw->reg_base = base; + lnw->irq_base = irq_base; + lnw->chip.label = dev_name(&pdev->dev); + lnw->chip.direction_input = lnw_gpio_direction_input; + lnw->chip.direction_output = lnw_gpio_direction_output; + lnw->chip.get = lnw_gpio_get; + lnw->chip.set = lnw_gpio_set; + lnw->chip.to_irq = lnw_gpio_to_irq; + lnw->chip.base = gpio_base; + lnw->chip.ngpio = 64; + lnw->chip.can_sleep = 0; + pci_set_drvdata(pdev, lnw); + retval = gpiochip_add(&lnw->chip); + if (retval) { + dev_err(&pdev->dev, "langwell gpiochip_add error %d\n", retval); + goto err5; + } + set_irq_data(pdev->irq, lnw); + set_irq_chained_handler(pdev->irq, lnw_irq_handler); + for (i = 0; i < lnw->chip.ngpio; i++) { + set_irq_chip_and_handler_name(i + lnw->irq_base, &lnw_irqchip, + handle_simple_irq, "demux"); + set_irq_chip_data(i + lnw->irq_base, lnw); + } + + spin_lock_init(&lnw->lock); + goto done; +err5: + kfree(lnw); +err4: + iounmap(base); +err3: + pci_release_regions(pdev); +err2: + pci_disable_device(pdev); +done: + return retval; +} + +static struct pci_driver lnw_gpio_driver = { + .name = "langwell_gpio", + .id_table = lnw_gpio_ids, + .probe = lnw_gpio_probe, +}; + +static int __init lnw_gpio_init(void) +{ + return pci_register_driver(&lnw_gpio_driver); +} + +device_initcall(lnw_gpio_init); diff --git a/drivers/gpio/max7301.c b/drivers/gpio/max7301.c index 7b82eaae2621..480956f1ca50 100644 --- a/drivers/gpio/max7301.c +++ b/drivers/gpio/max7301.c @@ -339,3 +339,4 @@ module_exit(max7301_exit); MODULE_AUTHOR("Juergen Beisert"); MODULE_LICENSE("GPL v2"); MODULE_DESCRIPTION("MAX7301 SPI based GPIO-Expander"); +MODULE_ALIAS("spi:" DRIVER_NAME); diff --git a/drivers/gpio/mc33880.c b/drivers/gpio/mc33880.c new file mode 100644 index 000000000000..e7d01bd8fdb3 --- /dev/null +++ b/drivers/gpio/mc33880.c @@ -0,0 +1,196 @@ +/* + * mc33880.c MC33880 high-side/low-side switch GPIO driver + * Copyright (c) 2009 Intel Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/* Supports: + * Freescale MC33880 high-side/low-side switch + */ + +#include <linux/init.h> +#include <linux/mutex.h> +#include <linux/spi/spi.h> +#include <linux/spi/mc33880.h> +#include <linux/gpio.h> + +#define DRIVER_NAME "mc33880" + +/* + * Pin configurations, see MAX7301 datasheet page 6 + */ +#define PIN_CONFIG_MASK 0x03 +#define PIN_CONFIG_IN_PULLUP 0x03 +#define PIN_CONFIG_IN_WO_PULLUP 0x02 +#define PIN_CONFIG_OUT 0x01 + +#define PIN_NUMBER 8 + + +/* + * Some registers must be read back to modify. + * To save time we cache them here in memory + */ +struct mc33880 { + struct mutex lock; /* protect from simultanous accesses */ + u8 port_config; + struct gpio_chip chip; + struct spi_device *spi; +}; + +static int mc33880_write_config(struct mc33880 *mc) +{ + return spi_write(mc->spi, &mc->port_config, sizeof(mc->port_config)); +} + + +static int __mc33880_set(struct mc33880 *mc, unsigned offset, int value) +{ + if (value) + mc->port_config |= 1 << offset; + else + mc->port_config &= ~(1 << offset); + + return mc33880_write_config(mc); +} + + +static void mc33880_set(struct gpio_chip *chip, unsigned offset, int value) +{ + struct mc33880 *mc = container_of(chip, struct mc33880, chip); + + mutex_lock(&mc->lock); + + __mc33880_set(mc, offset, value); + + mutex_unlock(&mc->lock); +} + +static int __devinit mc33880_probe(struct spi_device *spi) +{ + struct mc33880 *mc; + struct mc33880_platform_data *pdata; + int ret; + + pdata = spi->dev.platform_data; + if (!pdata || !pdata->base) { + dev_dbg(&spi->dev, "incorrect or missing platform data\n"); + return -EINVAL; + } + + /* + * bits_per_word cannot be configured in platform data + */ + spi->bits_per_word = 8; + + ret = spi_setup(spi); + if (ret < 0) + return ret; + + mc = kzalloc(sizeof(struct mc33880), GFP_KERNEL); + if (!mc) + return -ENOMEM; + + mutex_init(&mc->lock); + + dev_set_drvdata(&spi->dev, mc); + + mc->spi = spi; + + mc->chip.label = DRIVER_NAME, + mc->chip.set = mc33880_set; + mc->chip.base = pdata->base; + mc->chip.ngpio = PIN_NUMBER; + mc->chip.can_sleep = 1; + mc->chip.dev = &spi->dev; + mc->chip.owner = THIS_MODULE; + + mc->port_config = 0x00; + /* write twice, because during initialisation the first setting + * is just for testing SPI communication, and the second is the + * "real" configuration + */ + ret = mc33880_write_config(mc); + mc->port_config = 0x00; + if (!ret) + ret = mc33880_write_config(mc); + + if (ret) { + printk(KERN_ERR "Failed writing to " DRIVER_NAME ": %d\n", ret); + goto exit_destroy; + } + + ret = gpiochip_add(&mc->chip); + if (ret) + goto exit_destroy; + + return ret; + +exit_destroy: + dev_set_drvdata(&spi->dev, NULL); + mutex_destroy(&mc->lock); + kfree(mc); + return ret; +} + +static int mc33880_remove(struct spi_device *spi) +{ + struct mc33880 *mc; + int ret; + + mc = dev_get_drvdata(&spi->dev); + if (mc == NULL) + return -ENODEV; + + dev_set_drvdata(&spi->dev, NULL); + + ret = gpiochip_remove(&mc->chip); + if (!ret) { + mutex_destroy(&mc->lock); + kfree(mc); + } else + dev_err(&spi->dev, "Failed to remove the GPIO controller: %d\n", + ret); + + return ret; +} + +static struct spi_driver mc33880_driver = { + .driver = { + .name = DRIVER_NAME, + .owner = THIS_MODULE, + }, + .probe = mc33880_probe, + .remove = __devexit_p(mc33880_remove), +}; + +static int __init mc33880_init(void) +{ + return spi_register_driver(&mc33880_driver); +} +/* register after spi postcore initcall and before + * subsys initcalls that may rely on these GPIOs + */ +subsys_initcall(mc33880_init); + +static void __exit mc33880_exit(void) +{ + spi_unregister_driver(&mc33880_driver); +} +module_exit(mc33880_exit); + +MODULE_AUTHOR("Mocean Laboratories <info@mocean-labs.com>"); +MODULE_LICENSE("GPL v2"); + diff --git a/drivers/gpio/mcp23s08.c b/drivers/gpio/mcp23s08.c index f6fae0e50e65..cd651ec8d034 100644 --- a/drivers/gpio/mcp23s08.c +++ b/drivers/gpio/mcp23s08.c @@ -6,12 +6,10 @@ #include <linux/device.h> #include <linux/workqueue.h> #include <linux/mutex.h> - +#include <linux/gpio.h> #include <linux/spi/spi.h> #include <linux/spi/mcp23s08.h> -#include <asm/gpio.h> - /* Registers are all 8 bits wide. * @@ -433,3 +431,4 @@ static void __exit mcp23s08_exit(void) module_exit(mcp23s08_exit); MODULE_LICENSE("GPL"); +MODULE_ALIAS("spi:mcp23s08"); diff --git a/drivers/gpio/pca953x.c b/drivers/gpio/pca953x.c index cdb6574d25a6..6a2fb3fbb3d9 100644 --- a/drivers/gpio/pca953x.c +++ b/drivers/gpio/pca953x.c @@ -13,6 +13,7 @@ #include <linux/module.h> #include <linux/init.h> +#include <linux/gpio.h> #include <linux/i2c.h> #include <linux/i2c/pca953x.h> #ifdef CONFIG_OF_GPIO @@ -20,8 +21,6 @@ #include <linux/of_gpio.h> #endif -#include <asm/gpio.h> - #define PCA953X_INPUT 0 #define PCA953X_OUTPUT 1 #define PCA953X_INVERT 2 @@ -40,6 +39,7 @@ static const struct i2c_device_id pca953x_id[] = { { "pca9557", 8, }, { "max7310", 8, }, + { "max7315", 8, }, { "pca6107", 8, }, { "tca6408", 8, }, { "tca6416", 16, }, diff --git a/drivers/gpio/pcf857x.c b/drivers/gpio/pcf857x.c index 9525724be731..29f19ce3e80f 100644 --- a/drivers/gpio/pcf857x.c +++ b/drivers/gpio/pcf857x.c @@ -20,14 +20,14 @@ #include <linux/kernel.h> #include <linux/slab.h> +#include <linux/gpio.h> #include <linux/i2c.h> #include <linux/i2c/pcf857x.h> -#include <asm/gpio.h> - static const struct i2c_device_id pcf857x_id[] = { { "pcf8574", 8 }, + { "pcf8574a", 8 }, { "pca8574", 8 }, { "pca9670", 8 }, { "pca9672", 8 }, diff --git a/drivers/gpio/twl4030-gpio.c b/drivers/gpio/twl4030-gpio.c index afad14792141..49384a7c5492 100644 --- a/drivers/gpio/twl4030-gpio.c +++ b/drivers/gpio/twl4030-gpio.c @@ -460,7 +460,8 @@ no_irqs: return ret; } -static int __devexit gpio_twl4030_remove(struct platform_device *pdev) +/* Cannot use __devexit as gpio_twl4030_probe() calls us */ +static int gpio_twl4030_remove(struct platform_device *pdev) { struct twl4030_gpio_platform_data *pdata = pdev->dev.platform_data; int status; @@ -493,7 +494,7 @@ static struct platform_driver gpio_twl4030_driver = { .driver.name = "twl4030_gpio", .driver.owner = THIS_MODULE, .probe = gpio_twl4030_probe, - .remove = __devexit_p(gpio_twl4030_remove), + .remove = gpio_twl4030_remove, }; static int __init gpio_twl4030_init(void) diff --git a/drivers/gpio/ucb1400_gpio.c b/drivers/gpio/ucb1400_gpio.c new file mode 100644 index 000000000000..50e6bd1392ce --- /dev/null +++ b/drivers/gpio/ucb1400_gpio.c @@ -0,0 +1,125 @@ +/* + * Philips UCB1400 GPIO driver + * + * Author: Marek Vasut <marek.vasut@gmail.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include <linux/module.h> +#include <linux/ucb1400.h> + +struct ucb1400_gpio_data *ucbdata; + +static int ucb1400_gpio_dir_in(struct gpio_chip *gc, unsigned off) +{ + struct ucb1400_gpio *gpio; + gpio = container_of(gc, struct ucb1400_gpio, gc); + ucb1400_gpio_set_direction(gpio->ac97, off, 0); + return 0; +} + +static int ucb1400_gpio_dir_out(struct gpio_chip *gc, unsigned off, int val) +{ + struct ucb1400_gpio *gpio; + gpio = container_of(gc, struct ucb1400_gpio, gc); + ucb1400_gpio_set_direction(gpio->ac97, off, 1); + ucb1400_gpio_set_value(gpio->ac97, off, val); + return 0; +} + +static int ucb1400_gpio_get(struct gpio_chip *gc, unsigned off) +{ + struct ucb1400_gpio *gpio; + gpio = container_of(gc, struct ucb1400_gpio, gc); + return ucb1400_gpio_get_value(gpio->ac97, off); +} + +static void ucb1400_gpio_set(struct gpio_chip *gc, unsigned off, int val) +{ + struct ucb1400_gpio *gpio; + gpio = container_of(gc, struct ucb1400_gpio, gc); + ucb1400_gpio_set_value(gpio->ac97, off, val); +} + +static int ucb1400_gpio_probe(struct platform_device *dev) +{ + struct ucb1400_gpio *ucb = dev->dev.platform_data; + int err = 0; + + if (!(ucbdata && ucbdata->gpio_offset)) { + err = -EINVAL; + goto err; + } + + platform_set_drvdata(dev, ucb); + + ucb->gc.label = "ucb1400_gpio"; + ucb->gc.base = ucbdata->gpio_offset; + ucb->gc.ngpio = 10; + ucb->gc.owner = THIS_MODULE; + + ucb->gc.direction_input = ucb1400_gpio_dir_in; + ucb->gc.direction_output = ucb1400_gpio_dir_out; + ucb->gc.get = ucb1400_gpio_get; + ucb->gc.set = ucb1400_gpio_set; + ucb->gc.can_sleep = 1; + + err = gpiochip_add(&ucb->gc); + if (err) + goto err; + + if (ucbdata && ucbdata->gpio_setup) + err = ucbdata->gpio_setup(&dev->dev, ucb->gc.ngpio); + +err: + return err; + +} + +static int ucb1400_gpio_remove(struct platform_device *dev) +{ + int err = 0; + struct ucb1400_gpio *ucb = platform_get_drvdata(dev); + + if (ucbdata && ucbdata->gpio_teardown) { + err = ucbdata->gpio_teardown(&dev->dev, ucb->gc.ngpio); + if (err) + return err; + } + + err = gpiochip_remove(&ucb->gc); + return err; +} + +static struct platform_driver ucb1400_gpio_driver = { + .probe = ucb1400_gpio_probe, + .remove = ucb1400_gpio_remove, + .driver = { + .name = "ucb1400_gpio" + }, +}; + +static int __init ucb1400_gpio_init(void) +{ + return platform_driver_register(&ucb1400_gpio_driver); +} + +static void __exit ucb1400_gpio_exit(void) +{ + platform_driver_unregister(&ucb1400_gpio_driver); +} + +void __init ucb1400_gpio_set_data(struct ucb1400_gpio_data *data) +{ + ucbdata = data; +} + +module_init(ucb1400_gpio_init); +module_exit(ucb1400_gpio_exit); + +MODULE_DESCRIPTION("Philips UCB1400 GPIO driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig index e4d971c8b9d0..96eddd17e050 100644 --- a/drivers/gpu/drm/Kconfig +++ b/drivers/gpu/drm/Kconfig @@ -92,6 +92,7 @@ config DRM_I830 config DRM_I915 tristate "i915 driver" depends on AGP_INTEL + select SHMEM select DRM_KMS_HELPER select FB_CFB_FILLRECT select FB_CFB_COPYAREA @@ -102,6 +103,7 @@ config DRM_I915 select BACKLIGHT_CLASS_DEVICE if ACPI select INPUT if ACPI select ACPI_VIDEO if ACPI + select ACPI_BUTTON if ACPI help Choose this option if you have a system that has Intel 830M, 845G, 852GM, 855GM 865G or 915G integrated graphics. If M is selected, the diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index ba728ad77f2a..5cae0b3eee9b 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -482,6 +482,7 @@ void drm_connector_cleanup(struct drm_connector *connector) list_for_each_entry_safe(mode, t, &connector->user_modes, head) drm_mode_remove(connector, mode); + kfree(connector->fb_helper_private); mutex_lock(&dev->mode_config.mutex); drm_mode_object_put(dev, &connector->base); list_del(&connector->head); @@ -1555,8 +1556,6 @@ int drm_mode_cursor_ioctl(struct drm_device *dev, struct drm_crtc *crtc; int ret = 0; - DRM_DEBUG_KMS("\n"); - if (!req->flags) { DRM_ERROR("no operation set\n"); return -EINVAL; diff --git a/drivers/gpu/drm/drm_crtc_helper.c b/drivers/gpu/drm/drm_crtc_helper.c index fe8697447f32..bbfd110a7168 100644 --- a/drivers/gpu/drm/drm_crtc_helper.c +++ b/drivers/gpu/drm/drm_crtc_helper.c @@ -32,6 +32,7 @@ #include "drmP.h" #include "drm_crtc.h" #include "drm_crtc_helper.h" +#include "drm_fb_helper.h" static void drm_mode_validate_flag(struct drm_connector *connector, int flags) @@ -90,7 +91,15 @@ int drm_helper_probe_single_connector_modes(struct drm_connector *connector, list_for_each_entry_safe(mode, t, &connector->modes, head) mode->status = MODE_UNVERIFIED; - connector->status = connector->funcs->detect(connector); + if (connector->force) { + if (connector->force == DRM_FORCE_ON) + connector->status = connector_status_connected; + else + connector->status = connector_status_disconnected; + if (connector->funcs->force) + connector->funcs->force(connector); + } else + connector->status = connector->funcs->detect(connector); if (connector->status == connector_status_disconnected) { DRM_DEBUG_KMS("%s is disconnected\n", @@ -267,6 +276,66 @@ static struct drm_display_mode *drm_has_preferred_mode(struct drm_connector *con return NULL; } +static bool drm_has_cmdline_mode(struct drm_connector *connector) +{ + struct drm_fb_helper_connector *fb_help_conn = connector->fb_helper_private; + struct drm_fb_helper_cmdline_mode *cmdline_mode; + + if (!fb_help_conn) + return false; + + cmdline_mode = &fb_help_conn->cmdline_mode; + return cmdline_mode->specified; +} + +static struct drm_display_mode *drm_pick_cmdline_mode(struct drm_connector *connector, int width, int height) +{ + struct drm_fb_helper_connector *fb_help_conn = connector->fb_helper_private; + struct drm_fb_helper_cmdline_mode *cmdline_mode; + struct drm_display_mode *mode = NULL; + + if (!fb_help_conn) + return mode; + + cmdline_mode = &fb_help_conn->cmdline_mode; + if (cmdline_mode->specified == false) + return mode; + + /* attempt to find a matching mode in the list of modes + * we have gotten so far, if not add a CVT mode that conforms + */ + if (cmdline_mode->rb || cmdline_mode->margins) + goto create_mode; + + list_for_each_entry(mode, &connector->modes, head) { + /* check width/height */ + if (mode->hdisplay != cmdline_mode->xres || + mode->vdisplay != cmdline_mode->yres) + continue; + + if (cmdline_mode->refresh_specified) { + if (mode->vrefresh != cmdline_mode->refresh) + continue; + } + + if (cmdline_mode->interlace) { + if (!(mode->flags & DRM_MODE_FLAG_INTERLACE)) + continue; + } + return mode; + } + +create_mode: + mode = drm_cvt_mode(connector->dev, cmdline_mode->xres, + cmdline_mode->yres, + cmdline_mode->refresh_specified ? cmdline_mode->refresh : 60, + cmdline_mode->rb, cmdline_mode->interlace, + cmdline_mode->margins); + drm_mode_set_crtcinfo(mode, CRTC_INTERLACE_HALVE_V); + list_add(&mode->head, &connector->modes); + return mode; +} + static bool drm_connector_enabled(struct drm_connector *connector, bool strict) { bool enable; @@ -317,10 +386,16 @@ static bool drm_target_preferred(struct drm_device *dev, continue; } - DRM_DEBUG_KMS("looking for preferred mode on connector %d\n", - connector->base.id); + DRM_DEBUG_KMS("looking for cmdline mode on connector %d\n", + connector->base.id); - modes[i] = drm_has_preferred_mode(connector, width, height); + /* got for command line mode first */ + modes[i] = drm_pick_cmdline_mode(connector, width, height); + if (!modes[i]) { + DRM_DEBUG_KMS("looking for preferred mode on connector %d\n", + connector->base.id); + modes[i] = drm_has_preferred_mode(connector, width, height); + } /* No preferred modes, pick one off the list */ if (!modes[i] && !list_empty(&connector->modes)) { list_for_each_entry(modes[i], &connector->modes, head) @@ -369,6 +444,8 @@ static int drm_pick_crtcs(struct drm_device *dev, my_score = 1; if (connector->status == connector_status_connected) my_score++; + if (drm_has_cmdline_mode(connector)) + my_score++; if (drm_has_preferred_mode(connector, width, height)) my_score++; @@ -943,6 +1020,8 @@ bool drm_helper_initial_config(struct drm_device *dev) { int count = 0; + drm_fb_helper_parse_command_line(dev); + count = drm_helper_probe_connector_modes(dev, dev->mode_config.max_width, dev->mode_config.max_height); @@ -950,7 +1029,7 @@ bool drm_helper_initial_config(struct drm_device *dev) /* * we shouldn't end up with no modes here. */ - WARN(!count, "Connected connector with 0 modes\n"); + WARN(!count, "No connectors reported connected with modes\n"); drm_setup_crtcs(dev); diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c index 90d76bacff17..b54ba63d506e 100644 --- a/drivers/gpu/drm/drm_edid.c +++ b/drivers/gpu/drm/drm_edid.c @@ -109,7 +109,9 @@ static struct edid_quirk { /* Valid EDID header has these bytes */ -static u8 edid_header[] = { 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00 }; +static const u8 edid_header[] = { + 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00 +}; /** * edid_is_valid - sanity check EDID data @@ -500,6 +502,19 @@ static struct drm_display_mode *drm_find_dmt(struct drm_device *dev, } return mode; } + +/* + * 0 is reserved. The spec says 0x01 fill for unused timings. Some old + * monitors fill with ascii space (0x20) instead. + */ +static int +bad_std_timing(u8 a, u8 b) +{ + return (a == 0x00 && b == 0x00) || + (a == 0x01 && b == 0x01) || + (a == 0x20 && b == 0x20); +} + /** * drm_mode_std - convert standard mode info (width, height, refresh) into mode * @t: standard timing params @@ -513,6 +528,7 @@ static struct drm_display_mode *drm_find_dmt(struct drm_device *dev, */ struct drm_display_mode *drm_mode_std(struct drm_device *dev, struct std_timing *t, + int revision, int timing_level) { struct drm_display_mode *mode; @@ -523,14 +539,20 @@ struct drm_display_mode *drm_mode_std(struct drm_device *dev, unsigned vfreq = (t->vfreq_aspect & EDID_TIMING_VFREQ_MASK) >> EDID_TIMING_VFREQ_SHIFT; + if (bad_std_timing(t->hsize, t->vfreq_aspect)) + return NULL; + /* According to the EDID spec, the hdisplay = hsize * 8 + 248 */ hsize = t->hsize * 8 + 248; /* vrefresh_rate = vfreq + 60 */ vrefresh_rate = vfreq + 60; /* the vdisplay is calculated based on the aspect ratio */ - if (aspect_ratio == 0) - vsize = (hsize * 10) / 16; - else if (aspect_ratio == 1) + if (aspect_ratio == 0) { + if (revision < 3) + vsize = hsize; + else + vsize = (hsize * 10) / 16; + } else if (aspect_ratio == 1) vsize = (hsize * 3) / 4; else if (aspect_ratio == 2) vsize = (hsize * 4) / 5; @@ -538,7 +560,8 @@ struct drm_display_mode *drm_mode_std(struct drm_device *dev, vsize = (hsize * 9) / 16; /* HDTV hack */ if (hsize == 1360 && vsize == 765 && vrefresh_rate == 60) { - mode = drm_cvt_mode(dev, hsize, vsize, vrefresh_rate, 0, 0); + mode = drm_cvt_mode(dev, hsize, vsize, vrefresh_rate, 0, 0, + false); mode->hdisplay = 1366; mode->vsync_start = mode->vsync_start - 1; mode->vsync_end = mode->vsync_end - 1; @@ -557,7 +580,8 @@ struct drm_display_mode *drm_mode_std(struct drm_device *dev, mode = drm_gtf_mode(dev, hsize, vsize, vrefresh_rate, 0, 0); break; case LEVEL_CVT: - mode = drm_cvt_mode(dev, hsize, vsize, vrefresh_rate, 0, 0); + mode = drm_cvt_mode(dev, hsize, vsize, vrefresh_rate, 0, 0, + false); break; } return mode; @@ -602,6 +626,12 @@ static struct drm_display_mode *drm_mode_detailed(struct drm_device *dev, return NULL; } + /* it is incorrect if hsync/vsync width is zero */ + if (!hsync_pulse_width || !vsync_pulse_width) { + DRM_DEBUG_KMS("Incorrect Detailed timing. " + "Wrong Hsync/Vsync pulse width\n"); + return NULL; + } mode = drm_mode_create(dev); if (!mode) return NULL; @@ -623,6 +653,21 @@ static struct drm_display_mode *drm_mode_detailed(struct drm_device *dev, mode->vsync_end = mode->vsync_start + vsync_pulse_width; mode->vtotal = mode->vdisplay + vblank; + /* perform the basic check for the detailed timing */ + if (mode->hsync_end > mode->htotal || + mode->vsync_end > mode->vtotal) { + drm_mode_destroy(dev, mode); + DRM_DEBUG_KMS("Incorrect detailed timing. " + "Sync is beyond the blank.\n"); + return NULL; + } + + /* Some EDIDs have bogus h/vtotal values */ + if (mode->hsync_end > mode->htotal) + mode->htotal = mode->hsync_end + 1; + if (mode->vsync_end > mode->vtotal) + mode->vtotal = mode->vsync_end + 1; + drm_mode_set_name(mode); if (pt->misc & DRM_EDID_PT_INTERLACED) @@ -779,7 +824,7 @@ static int add_standard_modes(struct drm_connector *connector, struct edid *edid continue; newmode = drm_mode_std(dev, &edid->standard_timings[i], - timing_level); + edid->revision, timing_level); if (newmode) { drm_mode_probed_add(connector, newmode); modes++; @@ -829,13 +874,13 @@ static int add_detailed_info(struct drm_connector *connector, case EDID_DETAIL_MONITOR_CPDATA: break; case EDID_DETAIL_STD_MODES: - /* Five modes per detailed section */ - for (j = 0; j < 5; i++) { + for (j = 0; j < 6; i++) { struct std_timing *std; struct drm_display_mode *newmode; std = &data->data.timings[j]; newmode = drm_mode_std(dev, std, + edid->revision, timing_level); if (newmode) { drm_mode_probed_add(connector, newmode); @@ -964,7 +1009,9 @@ static int add_detailed_info_eedid(struct drm_connector *connector, struct drm_display_mode *newmode; std = &data->data.timings[j]; - newmode = drm_mode_std(dev, std, timing_level); + newmode = drm_mode_std(dev, std, + edid->revision, + timing_level); if (newmode) { drm_mode_probed_add(connector, newmode); modes++; diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c index 2c4671314884..65ef011fa8ba 100644 --- a/drivers/gpu/drm/drm_fb_helper.c +++ b/drivers/gpu/drm/drm_fb_helper.c @@ -40,6 +40,199 @@ MODULE_LICENSE("GPL and additional rights"); static LIST_HEAD(kernel_fb_helper_list); +int drm_fb_helper_add_connector(struct drm_connector *connector) +{ + connector->fb_helper_private = kzalloc(sizeof(struct drm_fb_helper_connector), GFP_KERNEL); + if (!connector->fb_helper_private) + return -ENOMEM; + + return 0; +} +EXPORT_SYMBOL(drm_fb_helper_add_connector); + +static int my_atoi(const char *name) +{ + int val = 0; + + for (;; name++) { + switch (*name) { + case '0' ... '9': + val = 10*val+(*name-'0'); + break; + default: + return val; + } + } +} + +/** + * drm_fb_helper_connector_parse_command_line - parse command line for connector + * @connector - connector to parse line for + * @mode_option - per connector mode option + * + * This parses the connector specific then generic command lines for + * modes and options to configure the connector. + * + * This uses the same parameters as the fb modedb.c, except for extra + * <xres>x<yres>[M][R][-<bpp>][@<refresh>][i][m][eDd] + * + * enable/enable Digital/disable bit at the end + */ +static bool drm_fb_helper_connector_parse_command_line(struct drm_connector *connector, + const char *mode_option) +{ + const char *name; + unsigned int namelen; + int res_specified = 0, bpp_specified = 0, refresh_specified = 0; + unsigned int xres = 0, yres = 0, bpp = 32, refresh = 0; + int yres_specified = 0, cvt = 0, rb = 0, interlace = 0, margins = 0; + int i; + enum drm_connector_force force = DRM_FORCE_UNSPECIFIED; + struct drm_fb_helper_connector *fb_help_conn = connector->fb_helper_private; + struct drm_fb_helper_cmdline_mode *cmdline_mode; + + if (!fb_help_conn) + return false; + + cmdline_mode = &fb_help_conn->cmdline_mode; + if (!mode_option) + mode_option = fb_mode_option; + + if (!mode_option) { + cmdline_mode->specified = false; + return false; + } + + name = mode_option; + namelen = strlen(name); + for (i = namelen-1; i >= 0; i--) { + switch (name[i]) { + case '@': + namelen = i; + if (!refresh_specified && !bpp_specified && + !yres_specified) { + refresh = my_atoi(&name[i+1]); + refresh_specified = 1; + if (cvt || rb) + cvt = 0; + } else + goto done; + break; + case '-': + namelen = i; + if (!bpp_specified && !yres_specified) { + bpp = my_atoi(&name[i+1]); + bpp_specified = 1; + if (cvt || rb) + cvt = 0; + } else + goto done; + break; + case 'x': + if (!yres_specified) { + yres = my_atoi(&name[i+1]); + yres_specified = 1; + } else + goto done; + case '0' ... '9': + break; + case 'M': + if (!yres_specified) + cvt = 1; + break; + case 'R': + if (!cvt) + rb = 1; + break; + case 'm': + if (!cvt) + margins = 1; + break; + case 'i': + if (!cvt) + interlace = 1; + break; + case 'e': + force = DRM_FORCE_ON; + break; + case 'D': + if ((connector->connector_type != DRM_MODE_CONNECTOR_DVII) || + (connector->connector_type != DRM_MODE_CONNECTOR_HDMIB)) + force = DRM_FORCE_ON; + else + force = DRM_FORCE_ON_DIGITAL; + break; + case 'd': + force = DRM_FORCE_OFF; + break; + default: + goto done; + } + } + if (i < 0 && yres_specified) { + xres = my_atoi(name); + res_specified = 1; + } +done: + + DRM_DEBUG_KMS("cmdline mode for connector %s %dx%d@%dHz%s%s%s\n", + drm_get_connector_name(connector), xres, yres, + (refresh) ? refresh : 60, (rb) ? " reduced blanking" : + "", (margins) ? " with margins" : "", (interlace) ? + " interlaced" : ""); + + if (force) { + const char *s; + switch (force) { + case DRM_FORCE_OFF: s = "OFF"; break; + case DRM_FORCE_ON_DIGITAL: s = "ON - dig"; break; + default: + case DRM_FORCE_ON: s = "ON"; break; + } + + DRM_INFO("forcing %s connector %s\n", + drm_get_connector_name(connector), s); + connector->force = force; + } + + if (res_specified) { + cmdline_mode->specified = true; + cmdline_mode->xres = xres; + cmdline_mode->yres = yres; + } + + if (refresh_specified) { + cmdline_mode->refresh_specified = true; + cmdline_mode->refresh = refresh; + } + + if (bpp_specified) { + cmdline_mode->bpp_specified = true; + cmdline_mode->bpp = bpp; + } + cmdline_mode->rb = rb ? true : false; + cmdline_mode->cvt = cvt ? true : false; + cmdline_mode->interlace = interlace ? true : false; + + return true; +} + +int drm_fb_helper_parse_command_line(struct drm_device *dev) +{ + struct drm_connector *connector; + + list_for_each_entry(connector, &dev->mode_config.connector_list, head) { + char *option = NULL; + + /* do something on return - turn off connector maybe */ + if (fb_get_options(drm_get_connector_name(connector), &option)) + continue; + + drm_fb_helper_connector_parse_command_line(connector, option); + } + return 0; +} + bool drm_fb_helper_force_kernel_mode(void) { int i = 0; @@ -87,6 +280,7 @@ void drm_fb_helper_restore(void) } EXPORT_SYMBOL(drm_fb_helper_restore); +#ifdef CONFIG_MAGIC_SYSRQ static void drm_fb_helper_restore_work_fn(struct work_struct *ignored) { drm_fb_helper_restore(); @@ -103,6 +297,7 @@ static struct sysrq_key_op sysrq_drm_fb_helper_restore_op = { .help_msg = "force-fb(V)", .action_msg = "Restore framebuffer console", }; +#endif static void drm_fb_helper_on(struct fb_info *info) { @@ -259,6 +454,109 @@ out_free: } EXPORT_SYMBOL(drm_fb_helper_init_crtc_count); +static int setcolreg(struct drm_crtc *crtc, u16 red, u16 green, + u16 blue, u16 regno, struct fb_info *info) +{ + struct drm_fb_helper *fb_helper = info->par; + struct drm_framebuffer *fb = fb_helper->fb; + int pindex; + + if (info->fix.visual == FB_VISUAL_TRUECOLOR) { + u32 *palette; + u32 value; + /* place color in psuedopalette */ + if (regno > 16) + return -EINVAL; + palette = (u32 *)info->pseudo_palette; + red >>= (16 - info->var.red.length); + green >>= (16 - info->var.green.length); + blue >>= (16 - info->var.blue.length); + value = (red << info->var.red.offset) | + (green << info->var.green.offset) | + (blue << info->var.blue.offset); + palette[regno] = value; + return 0; + } + + pindex = regno; + + if (fb->bits_per_pixel == 16) { + pindex = regno << 3; + + if (fb->depth == 16 && regno > 63) + return -EINVAL; + if (fb->depth == 15 && regno > 31) + return -EINVAL; + + if (fb->depth == 16) { + u16 r, g, b; + int i; + if (regno < 32) { + for (i = 0; i < 8; i++) + fb_helper->funcs->gamma_set(crtc, red, + green, blue, pindex + i); + } + + fb_helper->funcs->gamma_get(crtc, &r, + &g, &b, + pindex >> 1); + + for (i = 0; i < 4; i++) + fb_helper->funcs->gamma_set(crtc, r, + green, b, + (pindex >> 1) + i); + } + } + + if (fb->depth != 16) + fb_helper->funcs->gamma_set(crtc, red, green, blue, pindex); + return 0; +} + +int drm_fb_helper_setcmap(struct fb_cmap *cmap, struct fb_info *info) +{ + struct drm_fb_helper *fb_helper = info->par; + struct drm_device *dev = fb_helper->dev; + u16 *red, *green, *blue, *transp; + struct drm_crtc *crtc; + int i, rc = 0; + int start; + + list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { + struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private; + for (i = 0; i < fb_helper->crtc_count; i++) { + if (crtc->base.id == fb_helper->crtc_info[i].crtc_id) + break; + } + if (i == fb_helper->crtc_count) + continue; + + red = cmap->red; + green = cmap->green; + blue = cmap->blue; + transp = cmap->transp; + start = cmap->start; + + for (i = 0; i < cmap->len; i++) { + u16 hred, hgreen, hblue, htransp = 0xffff; + + hred = *red++; + hgreen = *green++; + hblue = *blue++; + + if (transp) + htransp = *transp++; + + rc = setcolreg(crtc, hred, hgreen, hblue, start++, info); + if (rc) + return rc; + } + crtc_funcs->load_lut(crtc); + } + return rc; +} +EXPORT_SYMBOL(drm_fb_helper_setcmap); + int drm_fb_helper_setcolreg(unsigned regno, unsigned red, unsigned green, @@ -270,10 +568,13 @@ int drm_fb_helper_setcolreg(unsigned regno, struct drm_device *dev = fb_helper->dev; struct drm_crtc *crtc; int i; + int ret; - list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { - struct drm_framebuffer *fb = fb_helper->fb; + if (regno > 255) + return 1; + list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { + struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private; for (i = 0; i < fb_helper->crtc_count; i++) { if (crtc->base.id == fb_helper->crtc_info[i].crtc_id) break; @@ -281,35 +582,11 @@ int drm_fb_helper_setcolreg(unsigned regno, if (i == fb_helper->crtc_count) continue; - if (regno > 255) - return 1; - - if (fb->depth == 8) { - fb_helper->funcs->gamma_set(crtc, red, green, blue, regno); - return 0; - } + ret = setcolreg(crtc, red, green, blue, regno, info); + if (ret) + return ret; - if (regno < 16) { - switch (fb->depth) { - case 15: - fb->pseudo_palette[regno] = ((red & 0xf800) >> 1) | - ((green & 0xf800) >> 6) | - ((blue & 0xf800) >> 11); - break; - case 16: - fb->pseudo_palette[regno] = (red & 0xf800) | - ((green & 0xfc00) >> 5) | - ((blue & 0xf800) >> 11); - break; - case 24: - case 32: - fb->pseudo_palette[regno] = - (((red >> 8) & 0xff) << info->var.red.offset) | - (((green >> 8) & 0xff) << info->var.green.offset) | - (((blue >> 8) & 0xff) << info->var.blue.offset); - break; - } - } + crtc_funcs->load_lut(crtc); } return 0; } @@ -322,7 +599,7 @@ int drm_fb_helper_check_var(struct fb_var_screeninfo *var, struct drm_framebuffer *fb = fb_helper->fb; int depth; - if (var->pixclock == -1 || !var->pixclock) + if (var->pixclock != 0) return -EINVAL; /* Need to resize the fb object !!! */ @@ -414,7 +691,7 @@ int drm_fb_helper_set_par(struct fb_info *info) int ret; int i; - if (var->pixclock != -1) { + if (var->pixclock != 0) { DRM_ERROR("PIXEL CLCOK SET\n"); return -EINVAL; } @@ -430,7 +707,7 @@ int drm_fb_helper_set_par(struct fb_info *info) if (crtc->fb == fb_helper->crtc_info[i].mode_set.fb) { mutex_lock(&dev->mode_config.mutex); - ret = crtc->funcs->set_config(&fb_helper->crtc_info->mode_set); + ret = crtc->funcs->set_config(&fb_helper->crtc_info[i].mode_set); mutex_unlock(&dev->mode_config.mutex); if (ret) return ret; @@ -479,11 +756,14 @@ int drm_fb_helper_pan_display(struct fb_var_screeninfo *var, EXPORT_SYMBOL(drm_fb_helper_pan_display); int drm_fb_helper_single_fb_probe(struct drm_device *dev, + int preferred_bpp, int (*fb_create)(struct drm_device *dev, uint32_t fb_width, uint32_t fb_height, uint32_t surface_width, uint32_t surface_height, + uint32_t surface_depth, + uint32_t surface_bpp, struct drm_framebuffer **fb_ptr)) { struct drm_crtc *crtc; @@ -497,8 +777,48 @@ int drm_fb_helper_single_fb_probe(struct drm_device *dev, struct drm_framebuffer *fb; struct drm_mode_set *modeset = NULL; struct drm_fb_helper *fb_helper; + uint32_t surface_depth = 24, surface_bpp = 32; + /* if driver picks 8 or 16 by default use that + for both depth/bpp */ + if (preferred_bpp != surface_bpp) { + surface_depth = surface_bpp = preferred_bpp; + } /* first up get a count of crtcs now in use and new min/maxes width/heights */ + list_for_each_entry(connector, &dev->mode_config.connector_list, head) { + struct drm_fb_helper_connector *fb_help_conn = connector->fb_helper_private; + + struct drm_fb_helper_cmdline_mode *cmdline_mode; + + if (!fb_help_conn) + continue; + + cmdline_mode = &fb_help_conn->cmdline_mode; + + if (cmdline_mode->bpp_specified) { + switch (cmdline_mode->bpp) { + case 8: + surface_depth = surface_bpp = 8; + break; + case 15: + surface_depth = 15; + surface_bpp = 16; + break; + case 16: + surface_depth = surface_bpp = 16; + break; + case 24: + surface_depth = surface_bpp = 24; + break; + case 32: + surface_depth = 24; + surface_bpp = 32; + break; + } + break; + } + } + list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { if (drm_helper_crtc_in_use(crtc)) { if (crtc->desired_mode) { @@ -527,7 +847,8 @@ int drm_fb_helper_single_fb_probe(struct drm_device *dev, /* do we have an fb already? */ if (list_empty(&dev->mode_config.fb_kernel_list)) { ret = (*fb_create)(dev, fb_width, fb_height, surface_width, - surface_height, &fb); + surface_height, surface_depth, surface_bpp, + &fb); if (ret) return -EINVAL; new_fb = 1; @@ -583,7 +904,7 @@ int drm_fb_helper_single_fb_probe(struct drm_device *dev, fb_helper->fb = fb; if (new_fb) { - info->var.pixclock = -1; + info->var.pixclock = 0; if (register_framebuffer(info) < 0) return -EINVAL; } else { @@ -618,10 +939,12 @@ void drm_fb_helper_free(struct drm_fb_helper *helper) } EXPORT_SYMBOL(drm_fb_helper_free); -void drm_fb_helper_fill_fix(struct fb_info *info, uint32_t pitch) +void drm_fb_helper_fill_fix(struct fb_info *info, uint32_t pitch, + uint32_t depth) { info->fix.type = FB_TYPE_PACKED_PIXELS; - info->fix.visual = FB_VISUAL_TRUECOLOR; + info->fix.visual = depth == 8 ? FB_VISUAL_PSEUDOCOLOR : + FB_VISUAL_TRUECOLOR; info->fix.type_aux = 0; info->fix.xpanstep = 1; /* doing it in hw */ info->fix.ypanstep = 1; /* doing it in hw */ diff --git a/drivers/gpu/drm/drm_gem.c b/drivers/gpu/drm/drm_gem.c index 230c9ffdd5e9..e9dbb481c469 100644 --- a/drivers/gpu/drm/drm_gem.c +++ b/drivers/gpu/drm/drm_gem.c @@ -142,6 +142,19 @@ drm_gem_object_alloc(struct drm_device *dev, size_t size) if (IS_ERR(obj->filp)) goto free; + /* Basically we want to disable the OOM killer and handle ENOMEM + * ourselves by sacrificing pages from cached buffers. + * XXX shmem_file_[gs]et_gfp_mask() + */ + mapping_set_gfp_mask(obj->filp->f_path.dentry->d_inode->i_mapping, + GFP_HIGHUSER | + __GFP_COLD | + __GFP_FS | + __GFP_RECLAIMABLE | + __GFP_NORETRY | + __GFP_NOWARN | + __GFP_NOMEMALLOC); + kref_init(&obj->refcount); kref_init(&obj->handlecount); obj->size = size; @@ -539,7 +552,7 @@ int drm_gem_mmap(struct file *filp, struct vm_area_struct *vma) vma->vm_flags |= VM_RESERVED | VM_IO | VM_PFNMAP | VM_DONTEXPAND; vma->vm_ops = obj->dev->driver->gem_vm_ops; vma->vm_private_data = map->handle; - vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot); + vma->vm_page_prot = pgprot_writecombine(vm_get_page_prot(vma->vm_flags)); /* Take a ref for this mapping of the object, so that the fault * handler can dereference the mmap offset's pointer to the object. diff --git a/drivers/gpu/drm/drm_mm.c b/drivers/gpu/drm/drm_mm.c index c861d80fd779..97dc5a4f0de4 100644 --- a/drivers/gpu/drm/drm_mm.c +++ b/drivers/gpu/drm/drm_mm.c @@ -103,6 +103,11 @@ static struct drm_mm_node *drm_mm_kmalloc(struct drm_mm *mm, int atomic) return child; } +/* drm_mm_pre_get() - pre allocate drm_mm_node structure + * drm_mm: memory manager struct we are pre-allocating for + * + * Returns 0 on success or -ENOMEM if allocation fails. + */ int drm_mm_pre_get(struct drm_mm *mm) { struct drm_mm_node *node; @@ -253,12 +258,14 @@ void drm_mm_put_block(struct drm_mm_node *cur) prev_node->size += next_node->size; list_del(&next_node->ml_entry); list_del(&next_node->fl_entry); + spin_lock(&mm->unused_lock); if (mm->num_unused < MM_UNUSED_TARGET) { list_add(&next_node->fl_entry, &mm->unused_nodes); ++mm->num_unused; } else kfree(next_node); + spin_unlock(&mm->unused_lock); } else { next_node->size += cur->size; next_node->start = cur->start; @@ -271,11 +278,13 @@ void drm_mm_put_block(struct drm_mm_node *cur) list_add(&cur->fl_entry, &mm->fl_entry); } else { list_del(&cur->ml_entry); + spin_lock(&mm->unused_lock); if (mm->num_unused < MM_UNUSED_TARGET) { list_add(&cur->fl_entry, &mm->unused_nodes); ++mm->num_unused; } else kfree(cur); + spin_unlock(&mm->unused_lock); } } diff --git a/drivers/gpu/drm/drm_modes.c b/drivers/gpu/drm/drm_modes.c index 49404ce1666e..51f677215f1d 100644 --- a/drivers/gpu/drm/drm_modes.c +++ b/drivers/gpu/drm/drm_modes.c @@ -88,7 +88,7 @@ EXPORT_SYMBOL(drm_mode_debug_printmodeline); #define HV_FACTOR 1000 struct drm_display_mode *drm_cvt_mode(struct drm_device *dev, int hdisplay, int vdisplay, int vrefresh, - bool reduced, bool interlaced) + bool reduced, bool interlaced, bool margins) { /* 1) top/bottom margin size (% of height) - default: 1.8, */ #define CVT_MARGIN_PERCENTAGE 18 @@ -101,7 +101,6 @@ struct drm_display_mode *drm_cvt_mode(struct drm_device *dev, int hdisplay, /* Pixel Clock step (kHz) */ #define CVT_CLOCK_STEP 250 struct drm_display_mode *drm_mode; - bool margins = false; unsigned int vfieldrate, hperiod; int hdisplay_rnd, hmargin, vdisplay_rnd, vmargin, vsync; int interlace; diff --git a/drivers/gpu/drm/drm_vm.c b/drivers/gpu/drm/drm_vm.c index 7e1fbe5d4779..4ac900f4647f 100644 --- a/drivers/gpu/drm/drm_vm.c +++ b/drivers/gpu/drm/drm_vm.c @@ -369,28 +369,28 @@ static int drm_vm_sg_fault(struct vm_area_struct *vma, struct vm_fault *vmf) } /** AGP virtual memory operations */ -static struct vm_operations_struct drm_vm_ops = { +static const struct vm_operations_struct drm_vm_ops = { .fault = drm_vm_fault, .open = drm_vm_open, .close = drm_vm_close, }; /** Shared virtual memory operations */ -static struct vm_operations_struct drm_vm_shm_ops = { +static const struct vm_operations_struct drm_vm_shm_ops = { .fault = drm_vm_shm_fault, .open = drm_vm_open, .close = drm_vm_shm_close, }; /** DMA virtual memory operations */ -static struct vm_operations_struct drm_vm_dma_ops = { +static const struct vm_operations_struct drm_vm_dma_ops = { .fault = drm_vm_dma_fault, .open = drm_vm_open, .close = drm_vm_close, }; /** Scatter-gather virtual memory operations */ -static struct vm_operations_struct drm_vm_sg_ops = { +static const struct vm_operations_struct drm_vm_sg_ops = { .fault = drm_vm_sg_fault, .open = drm_vm_open, .close = drm_vm_close, diff --git a/drivers/gpu/drm/i915/Makefile b/drivers/gpu/drm/i915/Makefile index 5269dfa5f620..fa7b9be096bc 100644 --- a/drivers/gpu/drm/i915/Makefile +++ b/drivers/gpu/drm/i915/Makefile @@ -9,6 +9,7 @@ i915-y := i915_drv.o i915_dma.o i915_irq.o i915_mem.o \ i915_gem.o \ i915_gem_debug.o \ i915_gem_tiling.o \ + i915_trace_points.o \ intel_display.o \ intel_crt.o \ intel_lvds.o \ diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c index 1e3bdcee863c..26bf0552b3cb 100644 --- a/drivers/gpu/drm/i915/i915_debugfs.c +++ b/drivers/gpu/drm/i915/i915_debugfs.c @@ -96,11 +96,13 @@ static int i915_gem_object_list_info(struct seq_file *m, void *data) { struct drm_gem_object *obj = obj_priv->obj; - seq_printf(m, " %p: %s %08x %08x %d", + seq_printf(m, " %p: %s %8zd %08x %08x %d %s", obj, get_pin_flag(obj_priv), + obj->size, obj->read_domains, obj->write_domain, - obj_priv->last_rendering_seqno); + obj_priv->last_rendering_seqno, + obj_priv->dirty ? "dirty" : ""); if (obj->name) seq_printf(m, " (name: %d)", obj->name); @@ -265,10 +267,10 @@ static void i915_dump_pages(struct seq_file *m, struct page **pages, int page_co uint32_t *mem; for (page = 0; page < page_count; page++) { - mem = kmap(pages[page]); + mem = kmap_atomic(pages[page], KM_USER0); for (i = 0; i < PAGE_SIZE; i += 4) seq_printf(m, "%08x : %08x\n", i, mem[i / 4]); - kunmap(pages[page]); + kunmap_atomic(pages[page], KM_USER0); } } diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c index 5a49a1867b35..e5b138be45fa 100644 --- a/drivers/gpu/drm/i915/i915_dma.c +++ b/drivers/gpu/drm/i915/i915_dma.c @@ -33,6 +33,7 @@ #include "intel_drv.h" #include "i915_drm.h" #include "i915_drv.h" +#include "i915_trace.h" #include <linux/vgaarb.h> /* Really want an OS-independent resettable timer. Would like to have @@ -50,14 +51,18 @@ int i915_wait_ring(struct drm_device * dev, int n, const char *caller) u32 last_head = I915_READ(PRB0_HEAD) & HEAD_ADDR; int i; + trace_i915_ring_wait_begin (dev); + for (i = 0; i < 100000; i++) { ring->head = I915_READ(PRB0_HEAD) & HEAD_ADDR; acthd = I915_READ(acthd_reg); ring->space = ring->head - (ring->tail + 8); if (ring->space < 0) ring->space += ring->Size; - if (ring->space >= n) + if (ring->space >= n) { + trace_i915_ring_wait_end (dev); return 0; + } if (dev->primary->master) { struct drm_i915_master_private *master_priv = dev->primary->master->driver_priv; @@ -77,6 +82,7 @@ int i915_wait_ring(struct drm_device * dev, int n, const char *caller) } + trace_i915_ring_wait_end (dev); return -EBUSY; } @@ -922,7 +928,8 @@ static int i915_get_bridge_dev(struct drm_device *dev) * how much was set aside so we can use it for our own purposes. */ static int i915_probe_agp(struct drm_device *dev, uint32_t *aperture_size, - uint32_t *preallocated_size) + uint32_t *preallocated_size, + uint32_t *start) { struct drm_i915_private *dev_priv = dev->dev_private; u16 tmp = 0; @@ -1009,10 +1016,159 @@ static int i915_probe_agp(struct drm_device *dev, uint32_t *aperture_size, return -1; } *preallocated_size = stolen - overhead; + *start = overhead; return 0; } +#define PTE_ADDRESS_MASK 0xfffff000 +#define PTE_ADDRESS_MASK_HIGH 0x000000f0 /* i915+ */ +#define PTE_MAPPING_TYPE_UNCACHED (0 << 1) +#define PTE_MAPPING_TYPE_DCACHE (1 << 1) /* i830 only */ +#define PTE_MAPPING_TYPE_CACHED (3 << 1) +#define PTE_MAPPING_TYPE_MASK (3 << 1) +#define PTE_VALID (1 << 0) + +/** + * i915_gtt_to_phys - take a GTT address and turn it into a physical one + * @dev: drm device + * @gtt_addr: address to translate + * + * Some chip functions require allocations from stolen space but need the + * physical address of the memory in question. We use this routine + * to get a physical address suitable for register programming from a given + * GTT address. + */ +static unsigned long i915_gtt_to_phys(struct drm_device *dev, + unsigned long gtt_addr) +{ + unsigned long *gtt; + unsigned long entry, phys; + int gtt_bar = IS_I9XX(dev) ? 0 : 1; + int gtt_offset, gtt_size; + + if (IS_I965G(dev)) { + if (IS_G4X(dev) || IS_IGDNG(dev)) { + gtt_offset = 2*1024*1024; + gtt_size = 2*1024*1024; + } else { + gtt_offset = 512*1024; + gtt_size = 512*1024; + } + } else { + gtt_bar = 3; + gtt_offset = 0; + gtt_size = pci_resource_len(dev->pdev, gtt_bar); + } + + gtt = ioremap_wc(pci_resource_start(dev->pdev, gtt_bar) + gtt_offset, + gtt_size); + if (!gtt) { + DRM_ERROR("ioremap of GTT failed\n"); + return 0; + } + + entry = *(volatile u32 *)(gtt + (gtt_addr / 1024)); + + DRM_DEBUG("GTT addr: 0x%08lx, PTE: 0x%08lx\n", gtt_addr, entry); + + /* Mask out these reserved bits on this hardware. */ + if (!IS_I9XX(dev) || IS_I915G(dev) || IS_I915GM(dev) || + IS_I945G(dev) || IS_I945GM(dev)) { + entry &= ~PTE_ADDRESS_MASK_HIGH; + } + + /* If it's not a mapping type we know, then bail. */ + if ((entry & PTE_MAPPING_TYPE_MASK) != PTE_MAPPING_TYPE_UNCACHED && + (entry & PTE_MAPPING_TYPE_MASK) != PTE_MAPPING_TYPE_CACHED) { + iounmap(gtt); + return 0; + } + + if (!(entry & PTE_VALID)) { + DRM_ERROR("bad GTT entry in stolen space\n"); + iounmap(gtt); + return 0; + } + + iounmap(gtt); + + phys =(entry & PTE_ADDRESS_MASK) | + ((uint64_t)(entry & PTE_ADDRESS_MASK_HIGH) << (32 - 4)); + + DRM_DEBUG("GTT addr: 0x%08lx, phys addr: 0x%08lx\n", gtt_addr, phys); + + return phys; +} + +static void i915_warn_stolen(struct drm_device *dev) +{ + DRM_ERROR("not enough stolen space for compressed buffer, disabling\n"); + DRM_ERROR("hint: you may be able to increase stolen memory size in the BIOS to avoid this\n"); +} + +static void i915_setup_compression(struct drm_device *dev, int size) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + struct drm_mm_node *compressed_fb, *compressed_llb; + unsigned long cfb_base, ll_base; + + /* Leave 1M for line length buffer & misc. */ + compressed_fb = drm_mm_search_free(&dev_priv->vram, size, 4096, 0); + if (!compressed_fb) { + i915_warn_stolen(dev); + return; + } + + compressed_fb = drm_mm_get_block(compressed_fb, size, 4096); + if (!compressed_fb) { + i915_warn_stolen(dev); + return; + } + + cfb_base = i915_gtt_to_phys(dev, compressed_fb->start); + if (!cfb_base) { + DRM_ERROR("failed to get stolen phys addr, disabling FBC\n"); + drm_mm_put_block(compressed_fb); + } + + if (!IS_GM45(dev)) { + compressed_llb = drm_mm_search_free(&dev_priv->vram, 4096, + 4096, 0); + if (!compressed_llb) { + i915_warn_stolen(dev); + return; + } + + compressed_llb = drm_mm_get_block(compressed_llb, 4096, 4096); + if (!compressed_llb) { + i915_warn_stolen(dev); + return; + } + + ll_base = i915_gtt_to_phys(dev, compressed_llb->start); + if (!ll_base) { + DRM_ERROR("failed to get stolen phys addr, disabling FBC\n"); + drm_mm_put_block(compressed_fb); + drm_mm_put_block(compressed_llb); + } + } + + dev_priv->cfb_size = size; + + if (IS_GM45(dev)) { + g4x_disable_fbc(dev); + I915_WRITE(DPFC_CB_BASE, compressed_fb->start); + } else { + i8xx_disable_fbc(dev); + I915_WRITE(FBC_CFB_BASE, cfb_base); + I915_WRITE(FBC_LL_BASE, ll_base); + } + + DRM_DEBUG("FBC base 0x%08lx, ll base 0x%08lx, size %dM\n", cfb_base, + ll_base, size >> 20); +} + /* true = enable decode, false = disable decoder */ static unsigned int i915_vga_set_decode(void *cookie, bool state) { @@ -1027,6 +1183,7 @@ static unsigned int i915_vga_set_decode(void *cookie, bool state) } static int i915_load_modeset_init(struct drm_device *dev, + unsigned long prealloc_start, unsigned long prealloc_size, unsigned long agp_size) { @@ -1047,6 +1204,10 @@ static int i915_load_modeset_init(struct drm_device *dev, /* Basic memrange allocator for stolen space (aka vram) */ drm_mm_init(&dev_priv->vram, 0, prealloc_size); + DRM_INFO("set up %ldM of stolen space\n", prealloc_size / (1024*1024)); + + /* We're off and running w/KMS */ + dev_priv->mm.suspended = 0; /* Let GEM Manage from end of prealloc space to end of aperture. * @@ -1059,10 +1220,24 @@ static int i915_load_modeset_init(struct drm_device *dev, */ i915_gem_do_init(dev, prealloc_size, agp_size - 4096); + mutex_lock(&dev->struct_mutex); ret = i915_gem_init_ringbuffer(dev); + mutex_unlock(&dev->struct_mutex); if (ret) goto out; + /* Try to set up FBC with a reasonable compressed buffer size */ + if (I915_HAS_FBC(dev) && i915_powersave) { + int cfb_size; + + /* Try to get an 8M buffer... */ + if (prealloc_size > (9*1024*1024)) + cfb_size = 8*1024*1024; + else /* fall back to 7/8 of the stolen space */ + cfb_size = prealloc_size * 7 / 8; + i915_setup_compression(dev, cfb_size); + } + /* Allow hardware batchbuffers unless told otherwise. */ dev_priv->allow_batchbuffer = 1; @@ -1180,7 +1355,7 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags) struct drm_i915_private *dev_priv = dev->dev_private; resource_size_t base, size; int ret = 0, mmio_bar = IS_I9XX(dev) ? 0 : 1; - uint32_t agp_size, prealloc_size; + uint32_t agp_size, prealloc_size, prealloc_start; /* i915 has 4 more counters */ dev->counters += 4; @@ -1234,7 +1409,7 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags) "performance may suffer.\n"); } - ret = i915_probe_agp(dev, &agp_size, &prealloc_size); + ret = i915_probe_agp(dev, &agp_size, &prealloc_size, &prealloc_start); if (ret) goto out_iomapfree; @@ -1292,6 +1467,7 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags) spin_lock_init(&dev_priv->user_irq_lock); spin_lock_init(&dev_priv->error_lock); dev_priv->user_irq_refcount = 0; + dev_priv->trace_irq_seqno = 0; ret = drm_vblank_init(dev, I915_NUM_PIPE); @@ -1300,8 +1476,12 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags) return ret; } + /* Start out suspended */ + dev_priv->mm.suspended = 1; + if (drm_core_check_feature(dev, DRIVER_MODESET)) { - ret = i915_load_modeset_init(dev, prealloc_size, agp_size); + ret = i915_load_modeset_init(dev, prealloc_start, + prealloc_size, agp_size); if (ret < 0) { DRM_ERROR("failed to init modeset\n"); goto out_workqueue_free; @@ -1313,6 +1493,8 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags) if (!IS_IGDNG(dev)) intel_opregion_init(dev, 0); + setup_timer(&dev_priv->hangcheck_timer, i915_hangcheck_elapsed, + (unsigned long) dev); return 0; out_workqueue_free: @@ -1333,6 +1515,7 @@ int i915_driver_unload(struct drm_device *dev) struct drm_i915_private *dev_priv = dev->dev_private; destroy_workqueue(dev_priv->wq); + del_timer_sync(&dev_priv->hangcheck_timer); io_mapping_free(dev_priv->mm.gtt_mapping); if (dev_priv->mm.gtt_mtrr >= 0) { @@ -1472,6 +1655,7 @@ struct drm_ioctl_desc i915_ioctls[] = { DRM_IOCTL_DEF(DRM_I915_GEM_GET_TILING, i915_gem_get_tiling, 0), DRM_IOCTL_DEF(DRM_I915_GEM_GET_APERTURE, i915_gem_get_aperture_ioctl, 0), DRM_IOCTL_DEF(DRM_I915_GET_PIPE_FROM_CRTC_ID, intel_get_pipe_from_crtc_id, 0), + DRM_IOCTL_DEF(DRM_I915_GEM_MADVISE, i915_gem_madvise_ioctl, 0), }; int i915_max_ioctl = DRM_ARRAY_SIZE(i915_ioctls); diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c index dbe568c9327b..7f436ec075f6 100644 --- a/drivers/gpu/drm/i915/i915_drv.c +++ b/drivers/gpu/drm/i915/i915_drv.c @@ -89,6 +89,9 @@ static int i915_suspend(struct drm_device *dev, pm_message_t state) pci_set_power_state(dev->pdev, PCI_D3hot); } + /* Modeset on resume, not lid events */ + dev_priv->modeset_on_lid = 0; + return 0; } @@ -97,8 +100,6 @@ static int i915_resume(struct drm_device *dev) struct drm_i915_private *dev_priv = dev->dev_private; int ret = 0; - pci_set_power_state(dev->pdev, PCI_D0); - pci_restore_state(dev->pdev); if (pci_enable_device(dev->pdev)) return -1; pci_set_master(dev->pdev); @@ -124,9 +125,135 @@ static int i915_resume(struct drm_device *dev) drm_helper_resume_force_mode(dev); } + dev_priv->modeset_on_lid = 0; + return ret; } +/** + * i965_reset - reset chip after a hang + * @dev: drm device to reset + * @flags: reset domains + * + * Reset the chip. Useful if a hang is detected. Returns zero on successful + * reset or otherwise an error code. + * + * Procedure is fairly simple: + * - reset the chip using the reset reg + * - re-init context state + * - re-init hardware status page + * - re-init ring buffer + * - re-init interrupt state + * - re-init display + */ +int i965_reset(struct drm_device *dev, u8 flags) +{ + drm_i915_private_t *dev_priv = dev->dev_private; + unsigned long timeout; + u8 gdrst; + /* + * We really should only reset the display subsystem if we actually + * need to + */ + bool need_display = true; + + mutex_lock(&dev->struct_mutex); + + /* + * Clear request list + */ + i915_gem_retire_requests(dev); + + if (need_display) + i915_save_display(dev); + + if (IS_I965G(dev) || IS_G4X(dev)) { + /* + * Set the domains we want to reset, then the reset bit (bit 0). + * Clear the reset bit after a while and wait for hardware status + * bit (bit 1) to be set + */ + pci_read_config_byte(dev->pdev, GDRST, &gdrst); + pci_write_config_byte(dev->pdev, GDRST, gdrst | flags | ((flags == GDRST_FULL) ? 0x1 : 0x0)); + udelay(50); + pci_write_config_byte(dev->pdev, GDRST, gdrst & 0xfe); + + /* ...we don't want to loop forever though, 500ms should be plenty */ + timeout = jiffies + msecs_to_jiffies(500); + do { + udelay(100); + pci_read_config_byte(dev->pdev, GDRST, &gdrst); + } while ((gdrst & 0x1) && time_after(timeout, jiffies)); + + if (gdrst & 0x1) { + WARN(true, "i915: Failed to reset chip\n"); + mutex_unlock(&dev->struct_mutex); + return -EIO; + } + } else { + DRM_ERROR("Error occurred. Don't know how to reset this chip.\n"); + return -ENODEV; + } + + /* Ok, now get things going again... */ + + /* + * Everything depends on having the GTT running, so we need to start + * there. Fortunately we don't need to do this unless we reset the + * chip at a PCI level. + * + * Next we need to restore the context, but we don't use those + * yet either... + * + * Ring buffer needs to be re-initialized in the KMS case, or if X + * was running at the time of the reset (i.e. we weren't VT + * switched away). + */ + if (drm_core_check_feature(dev, DRIVER_MODESET) || + !dev_priv->mm.suspended) { + drm_i915_ring_buffer_t *ring = &dev_priv->ring; + struct drm_gem_object *obj = ring->ring_obj; + struct drm_i915_gem_object *obj_priv = obj->driver_private; + dev_priv->mm.suspended = 0; + + /* Stop the ring if it's running. */ + I915_WRITE(PRB0_CTL, 0); + I915_WRITE(PRB0_TAIL, 0); + I915_WRITE(PRB0_HEAD, 0); + + /* Initialize the ring. */ + I915_WRITE(PRB0_START, obj_priv->gtt_offset); + I915_WRITE(PRB0_CTL, + ((obj->size - 4096) & RING_NR_PAGES) | + RING_NO_REPORT | + RING_VALID); + if (!drm_core_check_feature(dev, DRIVER_MODESET)) + i915_kernel_lost_context(dev); + else { + ring->head = I915_READ(PRB0_HEAD) & HEAD_ADDR; + ring->tail = I915_READ(PRB0_TAIL) & TAIL_ADDR; + ring->space = ring->head - (ring->tail + 8); + if (ring->space < 0) + ring->space += ring->Size; + } + + mutex_unlock(&dev->struct_mutex); + drm_irq_uninstall(dev); + drm_irq_install(dev); + mutex_lock(&dev->struct_mutex); + } + + /* + * Display needs restore too... + */ + if (need_display) + i915_restore_display(dev); + + mutex_unlock(&dev->struct_mutex); + return 0; +} + + static int __devinit i915_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) { @@ -234,6 +361,8 @@ static int __init i915_init(void) { driver.num_ioctls = i915_max_ioctl; + i915_gem_shrinker_init(); + /* * If CONFIG_DRM_I915_KMS is set, default to KMS unless * explicitly disabled with the module pararmeter. @@ -260,6 +389,7 @@ static int __init i915_init(void) static void __exit i915_exit(void) { + i915_gem_shrinker_exit(); drm_exit(&driver); } diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index a0632f8e76ac..a725f6591192 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -48,6 +48,11 @@ enum pipe { PIPE_B, }; +enum plane { + PLANE_A = 0, + PLANE_B, +}; + #define I915_NUM_PIPE 2 /* Interface history: @@ -148,6 +153,23 @@ struct drm_i915_error_state { struct timeval time; }; +struct drm_i915_display_funcs { + void (*dpms)(struct drm_crtc *crtc, int mode); + bool (*fbc_enabled)(struct drm_crtc *crtc); + void (*enable_fbc)(struct drm_crtc *crtc, unsigned long interval); + void (*disable_fbc)(struct drm_device *dev); + int (*get_display_clock_speed)(struct drm_device *dev); + int (*get_fifo_size)(struct drm_device *dev, int plane); + void (*update_wm)(struct drm_device *dev, int planea_clock, + int planeb_clock, int sr_hdisplay, int pixel_size); + /* clock updates for mode set */ + /* cursor updates */ + /* render clock increase/decrease */ + /* display clock increase/decrease */ + /* pll clock increase/decrease */ + /* clock gating init */ +}; + typedef struct drm_i915_private { struct drm_device *dev; @@ -180,6 +202,7 @@ typedef struct drm_i915_private { spinlock_t user_irq_lock; /** Refcount for i915_user_irq_get() versus i915_user_irq_put(). */ int user_irq_refcount; + u32 trace_irq_seqno; /** Cached value of IMR to avoid reads in updating the bitfield */ u32 irq_mask_reg; u32 pipestat[2]; @@ -198,10 +221,21 @@ typedef struct drm_i915_private { unsigned int sr01, adpa, ppcr, dvob, dvoc, lvds; int vblank_pipe; + /* For hangcheck timer */ +#define DRM_I915_HANGCHECK_PERIOD 75 /* in jiffies */ + struct timer_list hangcheck_timer; + int hangcheck_count; + uint32_t last_acthd; + bool cursor_needs_physical; struct drm_mm vram; + unsigned long cfb_size; + unsigned long cfb_pitch; + int cfb_fence; + int cfb_plane; + int irq_enabled; struct intel_opregion opregion; @@ -222,6 +256,8 @@ typedef struct drm_i915_private { unsigned int edp_support:1; int lvds_ssc_freq; + struct notifier_block lid_notifier; + int crt_ddc_bus; /* -1 = unknown, else GPIO to use for CRT DDC */ struct drm_i915_fence_reg fence_regs[16]; /* assume 965 */ int fence_reg_start; /* 4 if userland hasn't ioctl'd us yet */ @@ -234,7 +270,11 @@ typedef struct drm_i915_private { struct work_struct error_work; struct workqueue_struct *wq; + /* Display functions */ + struct drm_i915_display_funcs display; + /* Register state */ + bool modeset_on_lid; u8 saveLBB; u32 saveDSPACNTR; u32 saveDSPBCNTR; @@ -256,6 +296,13 @@ typedef struct drm_i915_private { u32 saveVBLANK_A; u32 saveVSYNC_A; u32 saveBCLRPAT_A; + u32 saveTRANSACONF; + u32 saveTRANS_HTOTAL_A; + u32 saveTRANS_HBLANK_A; + u32 saveTRANS_HSYNC_A; + u32 saveTRANS_VTOTAL_A; + u32 saveTRANS_VBLANK_A; + u32 saveTRANS_VSYNC_A; u32 savePIPEASTAT; u32 saveDSPASTRIDE; u32 saveDSPASIZE; @@ -264,8 +311,11 @@ typedef struct drm_i915_private { u32 saveDSPASURF; u32 saveDSPATILEOFF; u32 savePFIT_PGM_RATIOS; + u32 saveBLC_HIST_CTL; u32 saveBLC_PWM_CTL; u32 saveBLC_PWM_CTL2; + u32 saveBLC_CPU_PWM_CTL; + u32 saveBLC_CPU_PWM_CTL2; u32 saveFPB0; u32 saveFPB1; u32 saveDPLL_B; @@ -277,6 +327,13 @@ typedef struct drm_i915_private { u32 saveVBLANK_B; u32 saveVSYNC_B; u32 saveBCLRPAT_B; + u32 saveTRANSBCONF; + u32 saveTRANS_HTOTAL_B; + u32 saveTRANS_HBLANK_B; + u32 saveTRANS_HSYNC_B; + u32 saveTRANS_VTOTAL_B; + u32 saveTRANS_VBLANK_B; + u32 saveTRANS_VSYNC_B; u32 savePIPEBSTAT; u32 saveDSPBSTRIDE; u32 saveDSPBSIZE; @@ -302,6 +359,7 @@ typedef struct drm_i915_private { u32 savePFIT_CONTROL; u32 save_palette_a[256]; u32 save_palette_b[256]; + u32 saveDPFC_CB_BASE; u32 saveFBC_CFB_BASE; u32 saveFBC_LL_BASE; u32 saveFBC_CONTROL; @@ -309,6 +367,12 @@ typedef struct drm_i915_private { u32 saveIER; u32 saveIIR; u32 saveIMR; + u32 saveDEIER; + u32 saveDEIMR; + u32 saveGTIER; + u32 saveGTIMR; + u32 saveFDI_RXA_IMR; + u32 saveFDI_RXB_IMR; u32 saveCACHE_MODE_0; u32 saveD_STATE; u32 saveDSPCLK_GATE_D; @@ -342,6 +406,26 @@ typedef struct drm_i915_private { u32 savePIPEB_DP_LINK_M; u32 savePIPEA_DP_LINK_N; u32 savePIPEB_DP_LINK_N; + u32 saveFDI_RXA_CTL; + u32 saveFDI_TXA_CTL; + u32 saveFDI_RXB_CTL; + u32 saveFDI_TXB_CTL; + u32 savePFA_CTL_1; + u32 savePFB_CTL_1; + u32 savePFA_WIN_SZ; + u32 savePFB_WIN_SZ; + u32 savePFA_WIN_POS; + u32 savePFB_WIN_POS; + u32 savePCH_DREF_CONTROL; + u32 saveDISP_ARB_CTL; + u32 savePIPEA_DATA_M1; + u32 savePIPEA_DATA_N1; + u32 savePIPEA_LINK_M1; + u32 savePIPEA_LINK_N1; + u32 savePIPEB_DATA_M1; + u32 savePIPEB_DATA_N1; + u32 savePIPEB_LINK_M1; + u32 savePIPEB_LINK_N1; struct { struct drm_mm gtt_space; @@ -350,6 +434,15 @@ typedef struct drm_i915_private { int gtt_mtrr; /** + * Membership on list of all loaded devices, used to evict + * inactive buffers under memory pressure. + * + * Modifications should only be done whilst holding the + * shrink_list_lock spinlock. + */ + struct list_head shrink_list; + + /** * List of objects currently involved in rendering from the * ringbuffer. * @@ -432,7 +525,7 @@ typedef struct drm_i915_private { * It prevents command submission from occuring and makes * every pending request fail */ - int wedged; + atomic_t wedged; /** Bit 6 swizzling required for X tiling */ uint32_t bit_6_swizzle_x; @@ -443,6 +536,8 @@ typedef struct drm_i915_private { struct drm_i915_gem_phys_object *phys_objs[I915_MAX_PHYS_OBJECT]; } mm; struct sdvo_device_mapping sdvo_mappings[2]; + /* indicate whether the LVDS_BORDER should be enabled or not */ + unsigned int lvds_border_bits; /* Reclocking support */ bool render_reclock_avail; @@ -491,10 +586,7 @@ struct drm_i915_gem_object { * This is the same as gtt_space->start */ uint32_t gtt_offset; - /** - * Required alignment for the object - */ - uint32_t gtt_alignment; + /** * Fake offset for use by mmap(2) */ @@ -541,6 +633,11 @@ struct drm_i915_gem_object { * in an execbuffer object list. */ int in_execbuffer; + + /** + * Advice: are the backing pages purgeable? + */ + int madv; }; /** @@ -585,6 +682,8 @@ extern int i915_max_ioctl; extern unsigned int i915_fbpercrtc; extern unsigned int i915_powersave; +extern void i915_save_display(struct drm_device *dev); +extern void i915_restore_display(struct drm_device *dev); extern int i915_master_create(struct drm_device *dev, struct drm_master *master); extern void i915_master_destroy(struct drm_device *dev, struct drm_master *master); @@ -604,13 +703,16 @@ extern long i915_compat_ioctl(struct file *filp, unsigned int cmd, extern int i915_emit_box(struct drm_device *dev, struct drm_clip_rect *boxes, int i, int DR1, int DR4); +extern int i965_reset(struct drm_device *dev, u8 flags); /* i915_irq.c */ +void i915_hangcheck_elapsed(unsigned long data); extern int i915_irq_emit(struct drm_device *dev, void *data, struct drm_file *file_priv); extern int i915_irq_wait(struct drm_device *dev, void *data, struct drm_file *file_priv); void i915_user_irq_get(struct drm_device *dev); +void i915_trace_irq_get(struct drm_device *dev, u32 seqno); void i915_user_irq_put(struct drm_device *dev); extern void i915_enable_interrupt (struct drm_device *dev); @@ -676,6 +778,8 @@ int i915_gem_busy_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); int i915_gem_throttle_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); +int i915_gem_madvise_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv); int i915_gem_entervt_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); int i915_gem_leavevt_ioctl(struct drm_device *dev, void *data, @@ -695,6 +799,7 @@ int i915_gem_object_unbind(struct drm_gem_object *obj); void i915_gem_release_mmap(struct drm_gem_object *obj); void i915_gem_lastclose(struct drm_device *dev); uint32_t i915_get_gem_seqno(struct drm_device *dev); +bool i915_seqno_passed(uint32_t seq1, uint32_t seq2); int i915_gem_object_get_fence_reg(struct drm_gem_object *obj); int i915_gem_object_put_fence_reg(struct drm_gem_object *obj); void i915_gem_retire_requests(struct drm_device *dev); @@ -720,6 +825,9 @@ int i915_gem_object_get_pages(struct drm_gem_object *obj); void i915_gem_object_put_pages(struct drm_gem_object *obj); void i915_gem_release(struct drm_device * dev, struct drm_file *file_priv); +void i915_gem_shrinker_init(void); +void i915_gem_shrinker_exit(void); + /* i915_gem_tiling.c */ void i915_gem_detect_bit_6_swizzle(struct drm_device *dev); void i915_gem_object_do_bit_17_swizzle(struct drm_gem_object *obj); @@ -767,6 +875,8 @@ static inline void opregion_enable_asle(struct drm_device *dev) { return; } extern void intel_modeset_init(struct drm_device *dev); extern void intel_modeset_cleanup(struct drm_device *dev); extern int intel_modeset_vga_set_state(struct drm_device *dev, bool state); +extern void i8xx_disable_fbc(struct drm_device *dev); +extern void g4x_disable_fbc(struct drm_device *dev); /** * Lock test for when it's just for synchronization of ring access. @@ -864,6 +974,7 @@ extern int i915_wait_ring(struct drm_device * dev, int n, const char *caller); (dev)->pci_device == 0x2E12 || \ (dev)->pci_device == 0x2E22 || \ (dev)->pci_device == 0x2E32 || \ + (dev)->pci_device == 0x2E42 || \ (dev)->pci_device == 0x0042 || \ (dev)->pci_device == 0x0046) @@ -876,6 +987,7 @@ extern int i915_wait_ring(struct drm_device * dev, int n, const char *caller); (dev)->pci_device == 0x2E12 || \ (dev)->pci_device == 0x2E22 || \ (dev)->pci_device == 0x2E32 || \ + (dev)->pci_device == 0x2E42 || \ IS_GM45(dev)) #define IS_IGDG(dev) ((dev)->pci_device == 0xa001) @@ -909,12 +1021,16 @@ extern int i915_wait_ring(struct drm_device * dev, int n, const char *caller); #define SUPPORTS_INTEGRATED_HDMI(dev) (IS_G4X(dev) || IS_IGDNG(dev)) #define SUPPORTS_INTEGRATED_DP(dev) (IS_G4X(dev) || IS_IGDNG(dev)) #define SUPPORTS_EDP(dev) (IS_IGDNG_M(dev)) -#define I915_HAS_HOTPLUG(dev) (IS_I945G(dev) || IS_I945GM(dev) || IS_I965G(dev)) +#define I915_HAS_HOTPLUG(dev) (IS_I945G(dev) || IS_I945GM(dev) || IS_G33(dev) || IS_I965G(dev)) /* dsparb controlled by hw only */ #define DSPARB_HWCONTROL(dev) (IS_G4X(dev) || IS_IGDNG(dev)) #define HAS_FW_BLC(dev) (IS_I9XX(dev) || IS_G4X(dev) || IS_IGDNG(dev)) #define HAS_PIPE_CXSR(dev) (IS_G4X(dev) || IS_IGDNG(dev)) +#define I915_HAS_FBC(dev) (IS_MOBILE(dev) && \ + (IS_I9XX(dev) || IS_GM45(dev)) && \ + !IS_IGD(dev) && \ + !IS_IGDNG(dev)) #define PRIMARY_RINGBUFFER_SIZE (128*1024) diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index c67317112f4a..abfc27b0c2ea 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -29,6 +29,7 @@ #include "drm.h" #include "i915_drm.h" #include "i915_drv.h" +#include "i915_trace.h" #include "intel_drv.h" #include <linux/swap.h> #include <linux/pci.h> @@ -48,11 +49,15 @@ static int i915_gem_object_wait_rendering(struct drm_gem_object *obj); static int i915_gem_object_bind_to_gtt(struct drm_gem_object *obj, unsigned alignment); static void i915_gem_clear_fence_reg(struct drm_gem_object *obj); -static int i915_gem_evict_something(struct drm_device *dev); +static int i915_gem_evict_something(struct drm_device *dev, int min_size); +static int i915_gem_evict_from_inactive_list(struct drm_device *dev); static int i915_gem_phys_pwrite(struct drm_device *dev, struct drm_gem_object *obj, struct drm_i915_gem_pwrite *args, struct drm_file *file_priv); +static LIST_HEAD(shrink_list); +static DEFINE_SPINLOCK(shrink_list_lock); + int i915_gem_do_init(struct drm_device *dev, unsigned long start, unsigned long end) { @@ -316,6 +321,45 @@ fail_unlock: return ret; } +static inline gfp_t +i915_gem_object_get_page_gfp_mask (struct drm_gem_object *obj) +{ + return mapping_gfp_mask(obj->filp->f_path.dentry->d_inode->i_mapping); +} + +static inline void +i915_gem_object_set_page_gfp_mask (struct drm_gem_object *obj, gfp_t gfp) +{ + mapping_set_gfp_mask(obj->filp->f_path.dentry->d_inode->i_mapping, gfp); +} + +static int +i915_gem_object_get_pages_or_evict(struct drm_gem_object *obj) +{ + int ret; + + ret = i915_gem_object_get_pages(obj); + + /* If we've insufficient memory to map in the pages, attempt + * to make some space by throwing out some old buffers. + */ + if (ret == -ENOMEM) { + struct drm_device *dev = obj->dev; + gfp_t gfp; + + ret = i915_gem_evict_something(dev, obj->size); + if (ret) + return ret; + + gfp = i915_gem_object_get_page_gfp_mask(obj); + i915_gem_object_set_page_gfp_mask(obj, gfp & ~__GFP_NORETRY); + ret = i915_gem_object_get_pages(obj); + i915_gem_object_set_page_gfp_mask (obj, gfp); + } + + return ret; +} + /** * This is the fallback shmem pread path, which allocates temporary storage * in kernel space to copy_to_user into outside of the struct_mutex, so we @@ -367,8 +411,8 @@ i915_gem_shmem_pread_slow(struct drm_device *dev, struct drm_gem_object *obj, mutex_lock(&dev->struct_mutex); - ret = i915_gem_object_get_pages(obj); - if (ret != 0) + ret = i915_gem_object_get_pages_or_evict(obj); + if (ret) goto fail_unlock; ret = i915_gem_object_set_cpu_read_domain_range(obj, args->offset, @@ -842,8 +886,8 @@ i915_gem_shmem_pwrite_slow(struct drm_device *dev, struct drm_gem_object *obj, mutex_lock(&dev->struct_mutex); - ret = i915_gem_object_get_pages(obj); - if (ret != 0) + ret = i915_gem_object_get_pages_or_evict(obj); + if (ret) goto fail_unlock; ret = i915_gem_object_set_to_cpu_domain(obj, 1); @@ -1155,28 +1199,22 @@ int i915_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf) /* Now bind it into the GTT if needed */ mutex_lock(&dev->struct_mutex); if (!obj_priv->gtt_space) { - ret = i915_gem_object_bind_to_gtt(obj, obj_priv->gtt_alignment); - if (ret) { - mutex_unlock(&dev->struct_mutex); - return VM_FAULT_SIGBUS; - } - - ret = i915_gem_object_set_to_gtt_domain(obj, write); - if (ret) { - mutex_unlock(&dev->struct_mutex); - return VM_FAULT_SIGBUS; - } + ret = i915_gem_object_bind_to_gtt(obj, 0); + if (ret) + goto unlock; list_add_tail(&obj_priv->list, &dev_priv->mm.inactive_list); + + ret = i915_gem_object_set_to_gtt_domain(obj, write); + if (ret) + goto unlock; } /* Need a new fence register? */ if (obj_priv->tiling_mode != I915_TILING_NONE) { ret = i915_gem_object_get_fence_reg(obj); - if (ret) { - mutex_unlock(&dev->struct_mutex); - return VM_FAULT_SIGBUS; - } + if (ret) + goto unlock; } pfn = ((dev->agp->base + obj_priv->gtt_offset) >> PAGE_SHIFT) + @@ -1184,18 +1222,18 @@ int i915_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf) /* Finally, remap it using the new GTT offset */ ret = vm_insert_pfn(vma, (unsigned long)vmf->virtual_address, pfn); - +unlock: mutex_unlock(&dev->struct_mutex); switch (ret) { + case 0: + case -ERESTARTSYS: + return VM_FAULT_NOPAGE; case -ENOMEM: case -EAGAIN: return VM_FAULT_OOM; - case -EFAULT: - case -EINVAL: - return VM_FAULT_SIGBUS; default: - return VM_FAULT_NOPAGE; + return VM_FAULT_SIGBUS; } } @@ -1388,6 +1426,14 @@ i915_gem_mmap_gtt_ioctl(struct drm_device *dev, void *data, obj_priv = obj->driver_private; + if (obj_priv->madv != I915_MADV_WILLNEED) { + DRM_ERROR("Attempting to mmap a purgeable buffer\n"); + drm_gem_object_unreference(obj); + mutex_unlock(&dev->struct_mutex); + return -EINVAL; + } + + if (!obj_priv->mmap_offset) { ret = i915_gem_create_mmap_offset(obj); if (ret) { @@ -1399,22 +1445,12 @@ i915_gem_mmap_gtt_ioctl(struct drm_device *dev, void *data, args->offset = obj_priv->mmap_offset; - obj_priv->gtt_alignment = i915_gem_get_gtt_alignment(obj); - - /* Make sure the alignment is correct for fence regs etc */ - if (obj_priv->agp_mem && - (obj_priv->gtt_offset & (obj_priv->gtt_alignment - 1))) { - drm_gem_object_unreference(obj); - mutex_unlock(&dev->struct_mutex); - return -EINVAL; - } - /* * Pull it into the GTT so that we have a page list (makes the * initial fault faster and any subsequent flushing possible). */ if (!obj_priv->agp_mem) { - ret = i915_gem_object_bind_to_gtt(obj, obj_priv->gtt_alignment); + ret = i915_gem_object_bind_to_gtt(obj, 0); if (ret) { drm_gem_object_unreference(obj); mutex_unlock(&dev->struct_mutex); @@ -1437,6 +1473,7 @@ i915_gem_object_put_pages(struct drm_gem_object *obj) int i; BUG_ON(obj_priv->pages_refcount == 0); + BUG_ON(obj_priv->madv == __I915_MADV_PURGED); if (--obj_priv->pages_refcount != 0) return; @@ -1444,13 +1481,21 @@ i915_gem_object_put_pages(struct drm_gem_object *obj) if (obj_priv->tiling_mode != I915_TILING_NONE) i915_gem_object_save_bit_17_swizzle(obj); - for (i = 0; i < page_count; i++) - if (obj_priv->pages[i] != NULL) { - if (obj_priv->dirty) - set_page_dirty(obj_priv->pages[i]); + if (obj_priv->madv == I915_MADV_DONTNEED) + obj_priv->dirty = 0; + + for (i = 0; i < page_count; i++) { + if (obj_priv->pages[i] == NULL) + break; + + if (obj_priv->dirty) + set_page_dirty(obj_priv->pages[i]); + + if (obj_priv->madv == I915_MADV_WILLNEED) mark_page_accessed(obj_priv->pages[i]); - page_cache_release(obj_priv->pages[i]); - } + + page_cache_release(obj_priv->pages[i]); + } obj_priv->dirty = 0; drm_free_large(obj_priv->pages); @@ -1489,6 +1534,26 @@ i915_gem_object_move_to_flushing(struct drm_gem_object *obj) obj_priv->last_rendering_seqno = 0; } +/* Immediately discard the backing storage */ +static void +i915_gem_object_truncate(struct drm_gem_object *obj) +{ + struct drm_i915_gem_object *obj_priv = obj->driver_private; + struct inode *inode; + + inode = obj->filp->f_path.dentry->d_inode; + if (inode->i_op->truncate) + inode->i_op->truncate (inode); + + obj_priv->madv = __I915_MADV_PURGED; +} + +static inline int +i915_gem_object_is_purgeable(struct drm_i915_gem_object *obj_priv) +{ + return obj_priv->madv == I915_MADV_DONTNEED; +} + static void i915_gem_object_move_to_inactive(struct drm_gem_object *obj) { @@ -1577,15 +1642,24 @@ i915_add_request(struct drm_device *dev, struct drm_file *file_priv, if ((obj->write_domain & flush_domains) == obj->write_domain) { + uint32_t old_write_domain = obj->write_domain; + obj->write_domain = 0; i915_gem_object_move_to_active(obj, seqno); + + trace_i915_gem_object_change_domain(obj, + obj->read_domains, + old_write_domain); } } } - if (was_empty && !dev_priv->mm.suspended) - queue_delayed_work(dev_priv->wq, &dev_priv->mm.retire_work, HZ); + if (!dev_priv->mm.suspended) { + mod_timer(&dev_priv->hangcheck_timer, jiffies + DRM_I915_HANGCHECK_PERIOD); + if (was_empty) + queue_delayed_work(dev_priv->wq, &dev_priv->mm.retire_work, HZ); + } return seqno; } @@ -1623,6 +1697,8 @@ i915_gem_retire_request(struct drm_device *dev, { drm_i915_private_t *dev_priv = dev->dev_private; + trace_i915_gem_request_retire(dev, request->seqno); + /* Move any buffers on the active list that are no longer referenced * by the ringbuffer to the flushing/inactive lists as appropriate. */ @@ -1671,7 +1747,7 @@ out: /** * Returns true if seq1 is later than seq2. */ -static int +bool i915_seqno_passed(uint32_t seq1, uint32_t seq2) { return (int32_t)(seq1 - seq2) >= 0; @@ -1694,7 +1770,7 @@ i915_gem_retire_requests(struct drm_device *dev) drm_i915_private_t *dev_priv = dev->dev_private; uint32_t seqno; - if (!dev_priv->hw_status_page) + if (!dev_priv->hw_status_page || list_empty(&dev_priv->mm.request_list)) return; seqno = i915_get_gem_seqno(dev); @@ -1709,7 +1785,7 @@ i915_gem_retire_requests(struct drm_device *dev) retiring_seqno = request->seqno; if (i915_seqno_passed(seqno, retiring_seqno) || - dev_priv->mm.wedged) { + atomic_read(&dev_priv->mm.wedged)) { i915_gem_retire_request(dev, request); list_del(&request->list); @@ -1718,6 +1794,12 @@ i915_gem_retire_requests(struct drm_device *dev) } else break; } + + if (unlikely (dev_priv->trace_irq_seqno && + i915_seqno_passed(dev_priv->trace_irq_seqno, seqno))) { + i915_user_irq_put(dev); + dev_priv->trace_irq_seqno = 0; + } } void @@ -1751,6 +1833,9 @@ i915_wait_request(struct drm_device *dev, uint32_t seqno) BUG_ON(seqno == 0); + if (atomic_read(&dev_priv->mm.wedged)) + return -EIO; + if (!i915_seqno_passed(i915_get_gem_seqno(dev), seqno)) { if (IS_IGDNG(dev)) ier = I915_READ(DEIER) | I915_READ(GTIER); @@ -1763,16 +1848,20 @@ i915_wait_request(struct drm_device *dev, uint32_t seqno) i915_driver_irq_postinstall(dev); } + trace_i915_gem_request_wait_begin(dev, seqno); + dev_priv->mm.waiting_gem_seqno = seqno; i915_user_irq_get(dev); ret = wait_event_interruptible(dev_priv->irq_queue, i915_seqno_passed(i915_get_gem_seqno(dev), seqno) || - dev_priv->mm.wedged); + atomic_read(&dev_priv->mm.wedged)); i915_user_irq_put(dev); dev_priv->mm.waiting_gem_seqno = 0; + + trace_i915_gem_request_wait_end(dev, seqno); } - if (dev_priv->mm.wedged) + if (atomic_read(&dev_priv->mm.wedged)) ret = -EIO; if (ret && ret != -ERESTARTSYS) @@ -1803,6 +1892,8 @@ i915_gem_flush(struct drm_device *dev, DRM_INFO("%s: invalidate %08x flush %08x\n", __func__, invalidate_domains, flush_domains); #endif + trace_i915_gem_request_flush(dev, dev_priv->mm.next_gem_seqno, + invalidate_domains, flush_domains); if (flush_domains & I915_GEM_DOMAIN_CPU) drm_agp_chipset_flush(dev); @@ -1915,6 +2006,12 @@ i915_gem_object_unbind(struct drm_gem_object *obj) return -EINVAL; } + /* blow away mappings if mapped through GTT */ + i915_gem_release_mmap(obj); + + if (obj_priv->fence_reg != I915_FENCE_REG_NONE) + i915_gem_clear_fence_reg(obj); + /* Move the object to the CPU domain to ensure that * any possible CPU writes while it's not in the GTT * are flushed when we go to remap it. This will @@ -1928,21 +2025,16 @@ i915_gem_object_unbind(struct drm_gem_object *obj) return ret; } + BUG_ON(obj_priv->active); + if (obj_priv->agp_mem != NULL) { drm_unbind_agp(obj_priv->agp_mem); drm_free_agp(obj_priv->agp_mem, obj->size / PAGE_SIZE); obj_priv->agp_mem = NULL; } - BUG_ON(obj_priv->active); - - /* blow away mappings if mapped through GTT */ - i915_gem_release_mmap(obj); - - if (obj_priv->fence_reg != I915_FENCE_REG_NONE) - i915_gem_clear_fence_reg(obj); - i915_gem_object_put_pages(obj); + BUG_ON(obj_priv->pages_refcount); if (obj_priv->gtt_space) { atomic_dec(&dev->gtt_count); @@ -1956,40 +2048,113 @@ i915_gem_object_unbind(struct drm_gem_object *obj) if (!list_empty(&obj_priv->list)) list_del_init(&obj_priv->list); + if (i915_gem_object_is_purgeable(obj_priv)) + i915_gem_object_truncate(obj); + + trace_i915_gem_object_unbind(obj); + + return 0; +} + +static struct drm_gem_object * +i915_gem_find_inactive_object(struct drm_device *dev, int min_size) +{ + drm_i915_private_t *dev_priv = dev->dev_private; + struct drm_i915_gem_object *obj_priv; + struct drm_gem_object *best = NULL; + struct drm_gem_object *first = NULL; + + /* Try to find the smallest clean object */ + list_for_each_entry(obj_priv, &dev_priv->mm.inactive_list, list) { + struct drm_gem_object *obj = obj_priv->obj; + if (obj->size >= min_size) { + if ((!obj_priv->dirty || + i915_gem_object_is_purgeable(obj_priv)) && + (!best || obj->size < best->size)) { + best = obj; + if (best->size == min_size) + return best; + } + if (!first) + first = obj; + } + } + + return best ? best : first; +} + +static int +i915_gem_evict_everything(struct drm_device *dev) +{ + drm_i915_private_t *dev_priv = dev->dev_private; + uint32_t seqno; + int ret; + bool lists_empty; + + spin_lock(&dev_priv->mm.active_list_lock); + lists_empty = (list_empty(&dev_priv->mm.inactive_list) && + list_empty(&dev_priv->mm.flushing_list) && + list_empty(&dev_priv->mm.active_list)); + spin_unlock(&dev_priv->mm.active_list_lock); + + if (lists_empty) + return -ENOSPC; + + /* Flush everything (on to the inactive lists) and evict */ + i915_gem_flush(dev, I915_GEM_GPU_DOMAINS, I915_GEM_GPU_DOMAINS); + seqno = i915_add_request(dev, NULL, I915_GEM_GPU_DOMAINS); + if (seqno == 0) + return -ENOMEM; + + ret = i915_wait_request(dev, seqno); + if (ret) + return ret; + + ret = i915_gem_evict_from_inactive_list(dev); + if (ret) + return ret; + + spin_lock(&dev_priv->mm.active_list_lock); + lists_empty = (list_empty(&dev_priv->mm.inactive_list) && + list_empty(&dev_priv->mm.flushing_list) && + list_empty(&dev_priv->mm.active_list)); + spin_unlock(&dev_priv->mm.active_list_lock); + BUG_ON(!lists_empty); + return 0; } static int -i915_gem_evict_something(struct drm_device *dev) +i915_gem_evict_something(struct drm_device *dev, int min_size) { drm_i915_private_t *dev_priv = dev->dev_private; struct drm_gem_object *obj; - struct drm_i915_gem_object *obj_priv; - int ret = 0; + int ret; for (;;) { + i915_gem_retire_requests(dev); + /* If there's an inactive buffer available now, grab it * and be done. */ - if (!list_empty(&dev_priv->mm.inactive_list)) { - obj_priv = list_first_entry(&dev_priv->mm.inactive_list, - struct drm_i915_gem_object, - list); - obj = obj_priv->obj; - BUG_ON(obj_priv->pin_count != 0); + obj = i915_gem_find_inactive_object(dev, min_size); + if (obj) { + struct drm_i915_gem_object *obj_priv; + #if WATCH_LRU DRM_INFO("%s: evicting %p\n", __func__, obj); #endif + obj_priv = obj->driver_private; + BUG_ON(obj_priv->pin_count != 0); BUG_ON(obj_priv->active); /* Wait on the rendering and unbind the buffer. */ - ret = i915_gem_object_unbind(obj); - break; + return i915_gem_object_unbind(obj); } /* If we didn't get anything, but the ring is still processing - * things, wait for one of those things to finish and hopefully - * leave us a buffer to evict. + * things, wait for the next to finish and hopefully leave us + * a buffer to evict. */ if (!list_empty(&dev_priv->mm.request_list)) { struct drm_i915_gem_request *request; @@ -2000,16 +2165,9 @@ i915_gem_evict_something(struct drm_device *dev) ret = i915_wait_request(dev, request->seqno); if (ret) - break; + return ret; - /* if waiting caused an object to become inactive, - * then loop around and wait for it. Otherwise, we - * assume that waiting freed and unbound something, - * so there should now be some space in the GTT - */ - if (!list_empty(&dev_priv->mm.inactive_list)) - continue; - break; + continue; } /* If we didn't have anything on the request list but there @@ -2018,46 +2176,44 @@ i915_gem_evict_something(struct drm_device *dev) * will get moved to inactive. */ if (!list_empty(&dev_priv->mm.flushing_list)) { - obj_priv = list_first_entry(&dev_priv->mm.flushing_list, - struct drm_i915_gem_object, - list); - obj = obj_priv->obj; + struct drm_i915_gem_object *obj_priv; - i915_gem_flush(dev, - obj->write_domain, - obj->write_domain); - i915_add_request(dev, NULL, obj->write_domain); + /* Find an object that we can immediately reuse */ + list_for_each_entry(obj_priv, &dev_priv->mm.flushing_list, list) { + obj = obj_priv->obj; + if (obj->size >= min_size) + break; - obj = NULL; - continue; + obj = NULL; + } + + if (obj != NULL) { + uint32_t seqno; + + i915_gem_flush(dev, + obj->write_domain, + obj->write_domain); + seqno = i915_add_request(dev, NULL, obj->write_domain); + if (seqno == 0) + return -ENOMEM; + + ret = i915_wait_request(dev, seqno); + if (ret) + return ret; + + continue; + } } - DRM_ERROR("inactive empty %d request empty %d " - "flushing empty %d\n", - list_empty(&dev_priv->mm.inactive_list), - list_empty(&dev_priv->mm.request_list), - list_empty(&dev_priv->mm.flushing_list)); - /* If we didn't do any of the above, there's nothing to be done - * and we just can't fit it in. + /* If we didn't do any of the above, there's no single buffer + * large enough to swap out for the new one, so just evict + * everything and start again. (This should be rare.) */ - return -ENOSPC; + if (!list_empty (&dev_priv->mm.inactive_list)) + return i915_gem_evict_from_inactive_list(dev); + else + return i915_gem_evict_everything(dev); } - return ret; -} - -static int -i915_gem_evict_everything(struct drm_device *dev) -{ - int ret; - - for (;;) { - ret = i915_gem_evict_something(dev); - if (ret != 0) - break; - } - if (ret == -ENOSPC) - return 0; - return ret; } int @@ -2080,7 +2236,6 @@ i915_gem_object_get_pages(struct drm_gem_object *obj) BUG_ON(obj_priv->pages != NULL); obj_priv->pages = drm_calloc_large(page_count, sizeof(struct page *)); if (obj_priv->pages == NULL) { - DRM_ERROR("Faled to allocate page list\n"); obj_priv->pages_refcount--; return -ENOMEM; } @@ -2091,7 +2246,6 @@ i915_gem_object_get_pages(struct drm_gem_object *obj) page = read_mapping_page(mapping, i, NULL); if (IS_ERR(page)) { ret = PTR_ERR(page); - DRM_ERROR("read_mapping_page failed: %d\n", ret); i915_gem_object_put_pages(obj); return ret; } @@ -2328,6 +2482,8 @@ i915_gem_object_get_fence_reg(struct drm_gem_object *obj) else i830_write_fence_reg(reg); + trace_i915_gem_object_get_fence(obj, i, obj_priv->tiling_mode); + return 0; } @@ -2410,10 +2566,17 @@ i915_gem_object_bind_to_gtt(struct drm_gem_object *obj, unsigned alignment) drm_i915_private_t *dev_priv = dev->dev_private; struct drm_i915_gem_object *obj_priv = obj->driver_private; struct drm_mm_node *free_space; - int page_count, ret; + bool retry_alloc = false; + int ret; if (dev_priv->mm.suspended) return -EBUSY; + + if (obj_priv->madv != I915_MADV_WILLNEED) { + DRM_ERROR("Attempting to bind a purgeable object\n"); + return -EINVAL; + } + if (alignment == 0) alignment = i915_gem_get_gtt_alignment(obj); if (alignment & (i915_gem_get_gtt_alignment(obj) - 1)) { @@ -2433,30 +2596,16 @@ i915_gem_object_bind_to_gtt(struct drm_gem_object *obj, unsigned alignment) } } if (obj_priv->gtt_space == NULL) { - bool lists_empty; - /* If the gtt is empty and we're still having trouble * fitting our object in, we're out of memory. */ #if WATCH_LRU DRM_INFO("%s: GTT full, evicting something\n", __func__); #endif - spin_lock(&dev_priv->mm.active_list_lock); - lists_empty = (list_empty(&dev_priv->mm.inactive_list) && - list_empty(&dev_priv->mm.flushing_list) && - list_empty(&dev_priv->mm.active_list)); - spin_unlock(&dev_priv->mm.active_list_lock); - if (lists_empty) { - DRM_ERROR("GTT full, but LRU list empty\n"); - return -ENOSPC; - } - - ret = i915_gem_evict_something(dev); - if (ret != 0) { - if (ret != -ERESTARTSYS) - DRM_ERROR("Failed to evict a buffer %d\n", ret); + ret = i915_gem_evict_something(dev, obj->size); + if (ret) return ret; - } + goto search_free; } @@ -2464,27 +2613,56 @@ i915_gem_object_bind_to_gtt(struct drm_gem_object *obj, unsigned alignment) DRM_INFO("Binding object of size %zd at 0x%08x\n", obj->size, obj_priv->gtt_offset); #endif + if (retry_alloc) { + i915_gem_object_set_page_gfp_mask (obj, + i915_gem_object_get_page_gfp_mask (obj) & ~__GFP_NORETRY); + } ret = i915_gem_object_get_pages(obj); + if (retry_alloc) { + i915_gem_object_set_page_gfp_mask (obj, + i915_gem_object_get_page_gfp_mask (obj) | __GFP_NORETRY); + } if (ret) { drm_mm_put_block(obj_priv->gtt_space); obj_priv->gtt_space = NULL; + + if (ret == -ENOMEM) { + /* first try to clear up some space from the GTT */ + ret = i915_gem_evict_something(dev, obj->size); + if (ret) { + /* now try to shrink everyone else */ + if (! retry_alloc) { + retry_alloc = true; + goto search_free; + } + + return ret; + } + + goto search_free; + } + return ret; } - page_count = obj->size / PAGE_SIZE; /* Create an AGP memory structure pointing at our pages, and bind it * into the GTT. */ obj_priv->agp_mem = drm_agp_bind_pages(dev, obj_priv->pages, - page_count, + obj->size >> PAGE_SHIFT, obj_priv->gtt_offset, obj_priv->agp_type); if (obj_priv->agp_mem == NULL) { i915_gem_object_put_pages(obj); drm_mm_put_block(obj_priv->gtt_space); obj_priv->gtt_space = NULL; - return -ENOMEM; + + ret = i915_gem_evict_something(dev, obj->size); + if (ret) + return ret; + + goto search_free; } atomic_inc(&dev->gtt_count); atomic_add(obj->size, &dev->gtt_memory); @@ -2496,6 +2674,8 @@ i915_gem_object_bind_to_gtt(struct drm_gem_object *obj, unsigned alignment) BUG_ON(obj->read_domains & I915_GEM_GPU_DOMAINS); BUG_ON(obj->write_domain & I915_GEM_GPU_DOMAINS); + trace_i915_gem_object_bind(obj, obj_priv->gtt_offset); + return 0; } @@ -2511,15 +2691,7 @@ i915_gem_clflush_object(struct drm_gem_object *obj) if (obj_priv->pages == NULL) return; - /* XXX: The 865 in particular appears to be weird in how it handles - * cache flushing. We haven't figured it out, but the - * clflush+agp_chipset_flush doesn't appear to successfully get the - * data visible to the PGU, while wbinvd + agp_chipset_flush does. - */ - if (IS_I865G(obj->dev)) { - wbinvd(); - return; - } + trace_i915_gem_object_clflush(obj); drm_clflush_pages(obj_priv->pages, obj->size / PAGE_SIZE); } @@ -2530,21 +2702,29 @@ i915_gem_object_flush_gpu_write_domain(struct drm_gem_object *obj) { struct drm_device *dev = obj->dev; uint32_t seqno; + uint32_t old_write_domain; if ((obj->write_domain & I915_GEM_GPU_DOMAINS) == 0) return; /* Queue the GPU write cache flushing we need. */ + old_write_domain = obj->write_domain; i915_gem_flush(dev, 0, obj->write_domain); seqno = i915_add_request(dev, NULL, obj->write_domain); obj->write_domain = 0; i915_gem_object_move_to_active(obj, seqno); + + trace_i915_gem_object_change_domain(obj, + obj->read_domains, + old_write_domain); } /** Flushes the GTT write domain for the object if it's dirty. */ static void i915_gem_object_flush_gtt_write_domain(struct drm_gem_object *obj) { + uint32_t old_write_domain; + if (obj->write_domain != I915_GEM_DOMAIN_GTT) return; @@ -2552,7 +2732,12 @@ i915_gem_object_flush_gtt_write_domain(struct drm_gem_object *obj) * to it immediately go to main memory as far as we know, so there's * no chipset flush. It also doesn't land in render cache. */ + old_write_domain = obj->write_domain; obj->write_domain = 0; + + trace_i915_gem_object_change_domain(obj, + obj->read_domains, + old_write_domain); } /** Flushes the CPU write domain for the object if it's dirty. */ @@ -2560,13 +2745,19 @@ static void i915_gem_object_flush_cpu_write_domain(struct drm_gem_object *obj) { struct drm_device *dev = obj->dev; + uint32_t old_write_domain; if (obj->write_domain != I915_GEM_DOMAIN_CPU) return; i915_gem_clflush_object(obj); drm_agp_chipset_flush(dev); + old_write_domain = obj->write_domain; obj->write_domain = 0; + + trace_i915_gem_object_change_domain(obj, + obj->read_domains, + old_write_domain); } /** @@ -2579,6 +2770,7 @@ int i915_gem_object_set_to_gtt_domain(struct drm_gem_object *obj, int write) { struct drm_i915_gem_object *obj_priv = obj->driver_private; + uint32_t old_write_domain, old_read_domains; int ret; /* Not valid to be called on unbound objects. */ @@ -2591,6 +2783,9 @@ i915_gem_object_set_to_gtt_domain(struct drm_gem_object *obj, int write) if (ret != 0) return ret; + old_write_domain = obj->write_domain; + old_read_domains = obj->read_domains; + /* If we're writing through the GTT domain, then CPU and GPU caches * will need to be invalidated at next use. */ @@ -2609,6 +2804,10 @@ i915_gem_object_set_to_gtt_domain(struct drm_gem_object *obj, int write) obj_priv->dirty = 1; } + trace_i915_gem_object_change_domain(obj, + old_read_domains, + old_write_domain); + return 0; } @@ -2621,6 +2820,7 @@ i915_gem_object_set_to_gtt_domain(struct drm_gem_object *obj, int write) static int i915_gem_object_set_to_cpu_domain(struct drm_gem_object *obj, int write) { + uint32_t old_write_domain, old_read_domains; int ret; i915_gem_object_flush_gpu_write_domain(obj); @@ -2636,6 +2836,9 @@ i915_gem_object_set_to_cpu_domain(struct drm_gem_object *obj, int write) */ i915_gem_object_set_to_full_cpu_read_domain(obj); + old_write_domain = obj->write_domain; + old_read_domains = obj->read_domains; + /* Flush the CPU cache if it's still invalid. */ if ((obj->read_domains & I915_GEM_DOMAIN_CPU) == 0) { i915_gem_clflush_object(obj); @@ -2656,6 +2859,10 @@ i915_gem_object_set_to_cpu_domain(struct drm_gem_object *obj, int write) obj->write_domain = I915_GEM_DOMAIN_CPU; } + trace_i915_gem_object_change_domain(obj, + old_read_domains, + old_write_domain); + return 0; } @@ -2777,6 +2984,7 @@ i915_gem_object_set_to_gpu_domain(struct drm_gem_object *obj) struct drm_i915_gem_object *obj_priv = obj->driver_private; uint32_t invalidate_domains = 0; uint32_t flush_domains = 0; + uint32_t old_read_domains; BUG_ON(obj->pending_read_domains & I915_GEM_DOMAIN_CPU); BUG_ON(obj->pending_write_domain == I915_GEM_DOMAIN_CPU); @@ -2823,6 +3031,8 @@ i915_gem_object_set_to_gpu_domain(struct drm_gem_object *obj) i915_gem_clflush_object(obj); } + old_read_domains = obj->read_domains; + /* The actual obj->write_domain will be updated with * pending_write_domain after we emit the accumulated flush for all * of our domain changes in execbuffers (which clears objects' @@ -2841,6 +3051,10 @@ i915_gem_object_set_to_gpu_domain(struct drm_gem_object *obj) obj->read_domains, obj->write_domain, dev->invalidate_domains, dev->flush_domains); #endif + + trace_i915_gem_object_change_domain(obj, + old_read_domains, + obj->write_domain); } /** @@ -2893,6 +3107,7 @@ i915_gem_object_set_cpu_read_domain_range(struct drm_gem_object *obj, uint64_t offset, uint64_t size) { struct drm_i915_gem_object *obj_priv = obj->driver_private; + uint32_t old_read_domains; int i, ret; if (offset == 0 && size == obj->size) @@ -2939,8 +3154,13 @@ i915_gem_object_set_cpu_read_domain_range(struct drm_gem_object *obj, */ BUG_ON((obj->write_domain & ~I915_GEM_DOMAIN_CPU) != 0); + old_read_domains = obj->read_domains; obj->read_domains |= I915_GEM_DOMAIN_CPU; + trace_i915_gem_object_change_domain(obj, + old_read_domains, + obj->write_domain); + return 0; } @@ -2984,6 +3204,21 @@ i915_gem_object_pin_and_relocate(struct drm_gem_object *obj, } target_obj_priv = target_obj->driver_private; +#if WATCH_RELOC + DRM_INFO("%s: obj %p offset %08x target %d " + "read %08x write %08x gtt %08x " + "presumed %08x delta %08x\n", + __func__, + obj, + (int) reloc->offset, + (int) reloc->target_handle, + (int) reloc->read_domains, + (int) reloc->write_domain, + (int) target_obj_priv->gtt_offset, + (int) reloc->presumed_offset, + reloc->delta); +#endif + /* The target buffer should have appeared before us in the * exec_object list, so it should have a GTT space bound by now. */ @@ -2995,25 +3230,7 @@ i915_gem_object_pin_and_relocate(struct drm_gem_object *obj, return -EINVAL; } - if (reloc->offset > obj->size - 4) { - DRM_ERROR("Relocation beyond object bounds: " - "obj %p target %d offset %d size %d.\n", - obj, reloc->target_handle, - (int) reloc->offset, (int) obj->size); - drm_gem_object_unreference(target_obj); - i915_gem_object_unpin(obj); - return -EINVAL; - } - if (reloc->offset & 3) { - DRM_ERROR("Relocation not 4-byte aligned: " - "obj %p target %d offset %d.\n", - obj, reloc->target_handle, - (int) reloc->offset); - drm_gem_object_unreference(target_obj); - i915_gem_object_unpin(obj); - return -EINVAL; - } - + /* Validate that the target is in a valid r/w GPU domain */ if (reloc->write_domain & I915_GEM_DOMAIN_CPU || reloc->read_domains & I915_GEM_DOMAIN_CPU) { DRM_ERROR("reloc with read/write CPU domains: " @@ -3027,7 +3244,6 @@ i915_gem_object_pin_and_relocate(struct drm_gem_object *obj, i915_gem_object_unpin(obj); return -EINVAL; } - if (reloc->write_domain && target_obj->pending_write_domain && reloc->write_domain != target_obj->pending_write_domain) { DRM_ERROR("Write domain conflict: " @@ -3042,21 +3258,6 @@ i915_gem_object_pin_and_relocate(struct drm_gem_object *obj, return -EINVAL; } -#if WATCH_RELOC - DRM_INFO("%s: obj %p offset %08x target %d " - "read %08x write %08x gtt %08x " - "presumed %08x delta %08x\n", - __func__, - obj, - (int) reloc->offset, - (int) reloc->target_handle, - (int) reloc->read_domains, - (int) reloc->write_domain, - (int) target_obj_priv->gtt_offset, - (int) reloc->presumed_offset, - reloc->delta); -#endif - target_obj->pending_read_domains |= reloc->read_domains; target_obj->pending_write_domain |= reloc->write_domain; @@ -3068,6 +3269,37 @@ i915_gem_object_pin_and_relocate(struct drm_gem_object *obj, continue; } + /* Check that the relocation address is valid... */ + if (reloc->offset > obj->size - 4) { + DRM_ERROR("Relocation beyond object bounds: " + "obj %p target %d offset %d size %d.\n", + obj, reloc->target_handle, + (int) reloc->offset, (int) obj->size); + drm_gem_object_unreference(target_obj); + i915_gem_object_unpin(obj); + return -EINVAL; + } + if (reloc->offset & 3) { + DRM_ERROR("Relocation not 4-byte aligned: " + "obj %p target %d offset %d.\n", + obj, reloc->target_handle, + (int) reloc->offset); + drm_gem_object_unreference(target_obj); + i915_gem_object_unpin(obj); + return -EINVAL; + } + + /* and points to somewhere within the target object. */ + if (reloc->delta >= target_obj->size) { + DRM_ERROR("Relocation beyond target object bounds: " + "obj %p target %d delta %d size %d.\n", + obj, reloc->target_handle, + (int) reloc->delta, (int) target_obj->size); + drm_gem_object_unreference(target_obj); + i915_gem_object_unpin(obj); + return -EINVAL; + } + ret = i915_gem_object_set_to_gtt_domain(obj, 1); if (ret != 0) { drm_gem_object_unreference(target_obj); @@ -3126,6 +3358,8 @@ i915_dispatch_gem_execbuffer(struct drm_device *dev, exec_start = (uint32_t) exec_offset + exec->batch_start_offset; exec_len = (uint32_t) exec->batch_len; + trace_i915_gem_request_submit(dev, dev_priv->mm.next_gem_seqno + 1); + count = nbox ? nbox : 1; for (i = 0; i < count; i++) { @@ -3363,7 +3597,7 @@ i915_gem_execbuffer(struct drm_device *dev, void *data, i915_verify_inactive(dev, __FILE__, __LINE__); - if (dev_priv->mm.wedged) { + if (atomic_read(&dev_priv->mm.wedged)) { DRM_ERROR("Execbuf while wedged\n"); mutex_unlock(&dev->struct_mutex); ret = -EIO; @@ -3421,8 +3655,23 @@ i915_gem_execbuffer(struct drm_device *dev, void *data, /* error other than GTT full, or we've already tried again */ if (ret != -ENOSPC || pin_tries >= 1) { - if (ret != -ERESTARTSYS) - DRM_ERROR("Failed to pin buffers %d\n", ret); + if (ret != -ERESTARTSYS) { + unsigned long long total_size = 0; + for (i = 0; i < args->buffer_count; i++) + total_size += object_list[i]->size; + DRM_ERROR("Failed to pin buffer %d of %d, total %llu bytes: %d\n", + pinned+1, args->buffer_count, + total_size, ret); + DRM_ERROR("%d objects [%d pinned], " + "%d object bytes [%d pinned], " + "%d/%d gtt bytes\n", + atomic_read(&dev->object_count), + atomic_read(&dev->pin_count), + atomic_read(&dev->object_memory), + atomic_read(&dev->pin_memory), + atomic_read(&dev->gtt_memory), + dev->gtt_total); + } goto err; } @@ -3433,7 +3682,7 @@ i915_gem_execbuffer(struct drm_device *dev, void *data, /* evict everyone we can from the aperture */ ret = i915_gem_evict_everything(dev); - if (ret) + if (ret && ret != -ENOSPC) goto err; } @@ -3489,8 +3738,12 @@ i915_gem_execbuffer(struct drm_device *dev, void *data, for (i = 0; i < args->buffer_count; i++) { struct drm_gem_object *obj = object_list[i]; + uint32_t old_write_domain = obj->write_domain; obj->write_domain = obj->pending_write_domain; + trace_i915_gem_object_change_domain(obj, + obj->read_domains, + old_write_domain); } i915_verify_inactive(dev, __FILE__, __LINE__); @@ -3607,11 +3860,8 @@ i915_gem_object_pin(struct drm_gem_object *obj, uint32_t alignment) i915_verify_inactive(dev, __FILE__, __LINE__); if (obj_priv->gtt_space == NULL) { ret = i915_gem_object_bind_to_gtt(obj, alignment); - if (ret != 0) { - if (ret != -EBUSY && ret != -ERESTARTSYS) - DRM_ERROR("Failure to bind: %d\n", ret); + if (ret) return ret; - } } /* * Pre-965 chips need a fence register set up in order to @@ -3691,6 +3941,13 @@ i915_gem_pin_ioctl(struct drm_device *dev, void *data, } obj_priv = obj->driver_private; + if (obj_priv->madv != I915_MADV_WILLNEED) { + DRM_ERROR("Attempting to pin a purgeable buffer\n"); + drm_gem_object_unreference(obj); + mutex_unlock(&dev->struct_mutex); + return -EINVAL; + } + if (obj_priv->pin_filp != NULL && obj_priv->pin_filp != file_priv) { DRM_ERROR("Already pinned in i915_gem_pin_ioctl(): %d\n", args->handle); @@ -3803,6 +4060,56 @@ i915_gem_throttle_ioctl(struct drm_device *dev, void *data, return i915_gem_ring_throttle(dev, file_priv); } +int +i915_gem_madvise_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + struct drm_i915_gem_madvise *args = data; + struct drm_gem_object *obj; + struct drm_i915_gem_object *obj_priv; + + switch (args->madv) { + case I915_MADV_DONTNEED: + case I915_MADV_WILLNEED: + break; + default: + return -EINVAL; + } + + obj = drm_gem_object_lookup(dev, file_priv, args->handle); + if (obj == NULL) { + DRM_ERROR("Bad handle in i915_gem_madvise_ioctl(): %d\n", + args->handle); + return -EBADF; + } + + mutex_lock(&dev->struct_mutex); + obj_priv = obj->driver_private; + + if (obj_priv->pin_count) { + drm_gem_object_unreference(obj); + mutex_unlock(&dev->struct_mutex); + + DRM_ERROR("Attempted i915_gem_madvise_ioctl() on a pinned object\n"); + return -EINVAL; + } + + if (obj_priv->madv != __I915_MADV_PURGED) + obj_priv->madv = args->madv; + + /* if the object is no longer bound, discard its backing storage */ + if (i915_gem_object_is_purgeable(obj_priv) && + obj_priv->gtt_space == NULL) + i915_gem_object_truncate(obj); + + args->retained = obj_priv->madv != __I915_MADV_PURGED; + + drm_gem_object_unreference(obj); + mutex_unlock(&dev->struct_mutex); + + return 0; +} + int i915_gem_init_object(struct drm_gem_object *obj) { struct drm_i915_gem_object *obj_priv; @@ -3827,6 +4134,9 @@ int i915_gem_init_object(struct drm_gem_object *obj) obj_priv->fence_reg = I915_FENCE_REG_NONE; INIT_LIST_HEAD(&obj_priv->list); INIT_LIST_HEAD(&obj_priv->fence_list); + obj_priv->madv = I915_MADV_WILLNEED; + + trace_i915_gem_object_create(obj); return 0; } @@ -3836,6 +4146,8 @@ void i915_gem_free_object(struct drm_gem_object *obj) struct drm_device *dev = obj->dev; struct drm_i915_gem_object *obj_priv = obj->driver_private; + trace_i915_gem_object_destroy(obj); + while (obj_priv->pin_count > 0) i915_gem_object_unpin(obj); @@ -3844,43 +4156,35 @@ void i915_gem_free_object(struct drm_gem_object *obj) i915_gem_object_unbind(obj); - i915_gem_free_mmap_offset(obj); + if (obj_priv->mmap_offset) + i915_gem_free_mmap_offset(obj); kfree(obj_priv->page_cpu_valid); kfree(obj_priv->bit_17); kfree(obj->driver_private); } -/** Unbinds all objects that are on the given buffer list. */ +/** Unbinds all inactive objects. */ static int -i915_gem_evict_from_list(struct drm_device *dev, struct list_head *head) +i915_gem_evict_from_inactive_list(struct drm_device *dev) { - struct drm_gem_object *obj; - struct drm_i915_gem_object *obj_priv; - int ret; + drm_i915_private_t *dev_priv = dev->dev_private; - while (!list_empty(head)) { - obj_priv = list_first_entry(head, - struct drm_i915_gem_object, - list); - obj = obj_priv->obj; + while (!list_empty(&dev_priv->mm.inactive_list)) { + struct drm_gem_object *obj; + int ret; - if (obj_priv->pin_count != 0) { - DRM_ERROR("Pinned object in unbind list\n"); - mutex_unlock(&dev->struct_mutex); - return -EINVAL; - } + obj = list_first_entry(&dev_priv->mm.inactive_list, + struct drm_i915_gem_object, + list)->obj; ret = i915_gem_object_unbind(obj); if (ret != 0) { - DRM_ERROR("Error unbinding object in LeaveVT: %d\n", - ret); - mutex_unlock(&dev->struct_mutex); + DRM_ERROR("Error unbinding object: %d\n", ret); return ret; } } - return 0; } @@ -3902,6 +4206,7 @@ i915_gem_idle(struct drm_device *dev) * We need to replace this with a semaphore, or something. */ dev_priv->mm.suspended = 1; + del_timer(&dev_priv->hangcheck_timer); /* Cancel the retire work handler, wait for it to finish if running */ @@ -3931,7 +4236,7 @@ i915_gem_idle(struct drm_device *dev) if (last_seqno == cur_seqno) { if (stuck++ > 100) { DRM_ERROR("hardware wedged\n"); - dev_priv->mm.wedged = 1; + atomic_set(&dev_priv->mm.wedged, 1); DRM_WAKEUP(&dev_priv->irq_queue); break; } @@ -3944,7 +4249,7 @@ i915_gem_idle(struct drm_device *dev) i915_gem_retire_requests(dev); spin_lock(&dev_priv->mm.active_list_lock); - if (!dev_priv->mm.wedged) { + if (!atomic_read(&dev_priv->mm.wedged)) { /* Active and flushing should now be empty as we've * waited for a sequence higher than any pending execbuffer */ @@ -3962,29 +4267,41 @@ i915_gem_idle(struct drm_device *dev) * the GPU domains and just stuff them onto inactive. */ while (!list_empty(&dev_priv->mm.active_list)) { - struct drm_i915_gem_object *obj_priv; + struct drm_gem_object *obj; + uint32_t old_write_domain; - obj_priv = list_first_entry(&dev_priv->mm.active_list, - struct drm_i915_gem_object, - list); - obj_priv->obj->write_domain &= ~I915_GEM_GPU_DOMAINS; - i915_gem_object_move_to_inactive(obj_priv->obj); + obj = list_first_entry(&dev_priv->mm.active_list, + struct drm_i915_gem_object, + list)->obj; + old_write_domain = obj->write_domain; + obj->write_domain &= ~I915_GEM_GPU_DOMAINS; + i915_gem_object_move_to_inactive(obj); + + trace_i915_gem_object_change_domain(obj, + obj->read_domains, + old_write_domain); } spin_unlock(&dev_priv->mm.active_list_lock); while (!list_empty(&dev_priv->mm.flushing_list)) { - struct drm_i915_gem_object *obj_priv; + struct drm_gem_object *obj; + uint32_t old_write_domain; - obj_priv = list_first_entry(&dev_priv->mm.flushing_list, - struct drm_i915_gem_object, - list); - obj_priv->obj->write_domain &= ~I915_GEM_GPU_DOMAINS; - i915_gem_object_move_to_inactive(obj_priv->obj); + obj = list_first_entry(&dev_priv->mm.flushing_list, + struct drm_i915_gem_object, + list)->obj; + old_write_domain = obj->write_domain; + obj->write_domain &= ~I915_GEM_GPU_DOMAINS; + i915_gem_object_move_to_inactive(obj); + + trace_i915_gem_object_change_domain(obj, + obj->read_domains, + old_write_domain); } /* Move all inactive buffers out of the GTT. */ - ret = i915_gem_evict_from_list(dev, &dev_priv->mm.inactive_list); + ret = i915_gem_evict_from_inactive_list(dev); WARN_ON(!list_empty(&dev_priv->mm.inactive_list)); if (ret) { mutex_unlock(&dev->struct_mutex); @@ -4206,9 +4523,9 @@ i915_gem_entervt_ioctl(struct drm_device *dev, void *data, if (drm_core_check_feature(dev, DRIVER_MODESET)) return 0; - if (dev_priv->mm.wedged) { + if (atomic_read(&dev_priv->mm.wedged)) { DRM_ERROR("Reenabling wedged hardware, good luck\n"); - dev_priv->mm.wedged = 0; + atomic_set(&dev_priv->mm.wedged, 0); } mutex_lock(&dev->struct_mutex); @@ -4274,6 +4591,10 @@ i915_gem_load(struct drm_device *dev) i915_gem_retire_work_handler); dev_priv->mm.next_gem_seqno = 1; + spin_lock(&shrink_list_lock); + list_add(&dev_priv->mm.shrink_list, &shrink_list); + spin_unlock(&shrink_list_lock); + /* Old X drivers will take 0-2 for front, back, depth buffers */ dev_priv->fence_reg_start = 3; @@ -4491,3 +4812,116 @@ void i915_gem_release(struct drm_device * dev, struct drm_file *file_priv) list_del_init(i915_file_priv->mm.request_list.next); mutex_unlock(&dev->struct_mutex); } + +static int +i915_gem_shrink(int nr_to_scan, gfp_t gfp_mask) +{ + drm_i915_private_t *dev_priv, *next_dev; + struct drm_i915_gem_object *obj_priv, *next_obj; + int cnt = 0; + int would_deadlock = 1; + + /* "fast-path" to count number of available objects */ + if (nr_to_scan == 0) { + spin_lock(&shrink_list_lock); + list_for_each_entry(dev_priv, &shrink_list, mm.shrink_list) { + struct drm_device *dev = dev_priv->dev; + + if (mutex_trylock(&dev->struct_mutex)) { + list_for_each_entry(obj_priv, + &dev_priv->mm.inactive_list, + list) + cnt++; + mutex_unlock(&dev->struct_mutex); + } + } + spin_unlock(&shrink_list_lock); + + return (cnt / 100) * sysctl_vfs_cache_pressure; + } + + spin_lock(&shrink_list_lock); + + /* first scan for clean buffers */ + list_for_each_entry_safe(dev_priv, next_dev, + &shrink_list, mm.shrink_list) { + struct drm_device *dev = dev_priv->dev; + + if (! mutex_trylock(&dev->struct_mutex)) + continue; + + spin_unlock(&shrink_list_lock); + + i915_gem_retire_requests(dev); + + list_for_each_entry_safe(obj_priv, next_obj, + &dev_priv->mm.inactive_list, + list) { + if (i915_gem_object_is_purgeable(obj_priv)) { + i915_gem_object_unbind(obj_priv->obj); + if (--nr_to_scan <= 0) + break; + } + } + + spin_lock(&shrink_list_lock); + mutex_unlock(&dev->struct_mutex); + + would_deadlock = 0; + + if (nr_to_scan <= 0) + break; + } + + /* second pass, evict/count anything still on the inactive list */ + list_for_each_entry_safe(dev_priv, next_dev, + &shrink_list, mm.shrink_list) { + struct drm_device *dev = dev_priv->dev; + + if (! mutex_trylock(&dev->struct_mutex)) + continue; + + spin_unlock(&shrink_list_lock); + + list_for_each_entry_safe(obj_priv, next_obj, + &dev_priv->mm.inactive_list, + list) { + if (nr_to_scan > 0) { + i915_gem_object_unbind(obj_priv->obj); + nr_to_scan--; + } else + cnt++; + } + + spin_lock(&shrink_list_lock); + mutex_unlock(&dev->struct_mutex); + + would_deadlock = 0; + } + + spin_unlock(&shrink_list_lock); + + if (would_deadlock) + return -1; + else if (cnt > 0) + return (cnt / 100) * sysctl_vfs_cache_pressure; + else + return 0; +} + +static struct shrinker shrinker = { + .shrink = i915_gem_shrink, + .seeks = DEFAULT_SEEKS, +}; + +__init void +i915_gem_shrinker_init(void) +{ + register_shrinker(&shrinker); +} + +__exit void +i915_gem_shrinker_exit(void) +{ + unregister_shrinker(&shrinker); +} diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 6c89f2ff2495..aa7fd82aa6eb 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -31,6 +31,7 @@ #include "drm.h" #include "i915_drm.h" #include "i915_drv.h" +#include "i915_trace.h" #include "intel_drv.h" #define MAX_NOPID ((u32)~0) @@ -253,10 +254,15 @@ irqreturn_t igdng_irq_handler(struct drm_device *dev) { drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; int ret = IRQ_NONE; - u32 de_iir, gt_iir; + u32 de_iir, gt_iir, de_ier; u32 new_de_iir, new_gt_iir; struct drm_i915_master_private *master_priv; + /* disable master interrupt before clearing iir */ + de_ier = I915_READ(DEIER); + I915_WRITE(DEIER, de_ier & ~DE_MASTER_IRQ_CONTROL); + (void)I915_READ(DEIER); + de_iir = I915_READ(DEIIR); gt_iir = I915_READ(GTIIR); @@ -279,7 +285,9 @@ irqreturn_t igdng_irq_handler(struct drm_device *dev) } if (gt_iir & GT_USER_INTERRUPT) { - dev_priv->mm.irq_gem_seqno = i915_get_gem_seqno(dev); + u32 seqno = i915_get_gem_seqno(dev); + dev_priv->mm.irq_gem_seqno = seqno; + trace_i915_gem_request_complete(dev, seqno); DRM_WAKEUP(&dev_priv->irq_queue); } @@ -287,6 +295,9 @@ irqreturn_t igdng_irq_handler(struct drm_device *dev) gt_iir = new_gt_iir; } + I915_WRITE(DEIER, de_ier); + (void)I915_READ(DEIER); + return ret; } @@ -302,12 +313,25 @@ static void i915_error_work_func(struct work_struct *work) drm_i915_private_t *dev_priv = container_of(work, drm_i915_private_t, error_work); struct drm_device *dev = dev_priv->dev; - char *event_string = "ERROR=1"; - char *envp[] = { event_string, NULL }; + char *error_event[] = { "ERROR=1", NULL }; + char *reset_event[] = { "RESET=1", NULL }; + char *reset_done_event[] = { "ERROR=0", NULL }; DRM_DEBUG("generating error event\n"); - - kobject_uevent_env(&dev->primary->kdev.kobj, KOBJ_CHANGE, envp); + kobject_uevent_env(&dev->primary->kdev.kobj, KOBJ_CHANGE, error_event); + + if (atomic_read(&dev_priv->mm.wedged)) { + if (IS_I965G(dev)) { + DRM_DEBUG("resetting chip\n"); + kobject_uevent_env(&dev->primary->kdev.kobj, KOBJ_CHANGE, reset_event); + if (!i965_reset(dev, GDRST_RENDER)) { + atomic_set(&dev_priv->mm.wedged, 0); + kobject_uevent_env(&dev->primary->kdev.kobj, KOBJ_CHANGE, reset_done_event); + } + } else { + printk("reboot required\n"); + } + } } /** @@ -372,7 +396,7 @@ out: * so userspace knows something bad happened (should trigger collection * of a ring dump etc.). */ -static void i915_handle_error(struct drm_device *dev) +static void i915_handle_error(struct drm_device *dev, bool wedged) { struct drm_i915_private *dev_priv = dev->dev_private; u32 eir = I915_READ(EIR); @@ -482,6 +506,16 @@ static void i915_handle_error(struct drm_device *dev) I915_WRITE(IIR, I915_RENDER_COMMAND_PARSER_ERROR_INTERRUPT); } + if (wedged) { + atomic_set(&dev_priv->mm.wedged, 1); + + /* + * Wakeup waiting processes so they don't hang + */ + printk("i915: Waking up sleeping processes\n"); + DRM_WAKEUP(&dev_priv->irq_queue); + } + queue_work(dev_priv->wq, &dev_priv->error_work); } @@ -527,7 +561,7 @@ irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS) pipeb_stats = I915_READ(PIPEBSTAT); if (iir & I915_RENDER_COMMAND_PARSER_ERROR_INTERRUPT) - i915_handle_error(dev); + i915_handle_error(dev, false); /* * Clear the PIPE(A|B)STAT regs before the IIR @@ -599,8 +633,12 @@ irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS) } if (iir & I915_USER_INTERRUPT) { - dev_priv->mm.irq_gem_seqno = i915_get_gem_seqno(dev); + u32 seqno = i915_get_gem_seqno(dev); + dev_priv->mm.irq_gem_seqno = seqno; + trace_i915_gem_request_complete(dev, seqno); DRM_WAKEUP(&dev_priv->irq_queue); + dev_priv->hangcheck_count = 0; + mod_timer(&dev_priv->hangcheck_timer, jiffies + DRM_I915_HANGCHECK_PERIOD); } if (pipea_stats & vblank_status) { @@ -695,6 +733,16 @@ void i915_user_irq_put(struct drm_device *dev) spin_unlock_irqrestore(&dev_priv->user_irq_lock, irqflags); } +void i915_trace_irq_get(struct drm_device *dev, u32 seqno) +{ + drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; + + if (dev_priv->trace_irq_seqno == 0) + i915_user_irq_get(dev); + + dev_priv->trace_irq_seqno = seqno; +} + static int i915_wait_irq(struct drm_device * dev, int irq_nr) { drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; @@ -880,6 +928,52 @@ int i915_vblank_swap(struct drm_device *dev, void *data, return -EINVAL; } +struct drm_i915_gem_request *i915_get_tail_request(struct drm_device *dev) { + drm_i915_private_t *dev_priv = dev->dev_private; + return list_entry(dev_priv->mm.request_list.prev, struct drm_i915_gem_request, list); +} + +/** + * This is called when the chip hasn't reported back with completed + * batchbuffers in a long time. The first time this is called we simply record + * ACTHD. If ACTHD hasn't changed by the time the hangcheck timer elapses + * again, we assume the chip is wedged and try to fix it. + */ +void i915_hangcheck_elapsed(unsigned long data) +{ + struct drm_device *dev = (struct drm_device *)data; + drm_i915_private_t *dev_priv = dev->dev_private; + uint32_t acthd; + + if (!IS_I965G(dev)) + acthd = I915_READ(ACTHD); + else + acthd = I915_READ(ACTHD_I965); + + /* If all work is done then ACTHD clearly hasn't advanced. */ + if (list_empty(&dev_priv->mm.request_list) || + i915_seqno_passed(i915_get_gem_seqno(dev), i915_get_tail_request(dev)->seqno)) { + dev_priv->hangcheck_count = 0; + return; + } + + if (dev_priv->last_acthd == acthd && dev_priv->hangcheck_count > 0) { + DRM_ERROR("Hangcheck timer elapsed... GPU hung\n"); + i915_handle_error(dev, true); + return; + } + + /* Reset timer case chip hangs without another request being added */ + mod_timer(&dev_priv->hangcheck_timer, jiffies + DRM_I915_HANGCHECK_PERIOD); + + if (acthd != dev_priv->last_acthd) + dev_priv->hangcheck_count = 0; + else + dev_priv->hangcheck_count++; + + dev_priv->last_acthd = acthd; +} + /* drm_dma.h hooks */ static void igdng_irq_preinstall(struct drm_device *dev) diff --git a/drivers/gpu/drm/i915/i915_opregion.c b/drivers/gpu/drm/i915/i915_opregion.c index e4b4e8898e39..2d5193556d3f 100644 --- a/drivers/gpu/drm/i915/i915_opregion.c +++ b/drivers/gpu/drm/i915/i915_opregion.c @@ -148,6 +148,7 @@ static u32 asle_set_backlight(struct drm_device *dev, u32 bclp) struct drm_i915_private *dev_priv = dev->dev_private; struct opregion_asle *asle = dev_priv->opregion.asle; u32 blc_pwm_ctl, blc_pwm_ctl2; + u32 max_backlight, level, shift; if (!(bclp & ASLE_BCLP_VALID)) return ASLE_BACKLIGHT_FAIL; @@ -157,14 +158,25 @@ static u32 asle_set_backlight(struct drm_device *dev, u32 bclp) return ASLE_BACKLIGHT_FAIL; blc_pwm_ctl = I915_READ(BLC_PWM_CTL); - blc_pwm_ctl &= ~BACKLIGHT_DUTY_CYCLE_MASK; blc_pwm_ctl2 = I915_READ(BLC_PWM_CTL2); - if (blc_pwm_ctl2 & BLM_COMBINATION_MODE) + if (IS_I965G(dev) && (blc_pwm_ctl2 & BLM_COMBINATION_MODE)) pci_write_config_dword(dev->pdev, PCI_LBPC, bclp); - else - I915_WRITE(BLC_PWM_CTL, blc_pwm_ctl | ((bclp * 0x101)-1)); - + else { + if (IS_IGD(dev)) { + blc_pwm_ctl &= ~(BACKLIGHT_DUTY_CYCLE_MASK - 1); + max_backlight = (blc_pwm_ctl & BACKLIGHT_MODULATION_FREQ_MASK) >> + BACKLIGHT_MODULATION_FREQ_SHIFT; + shift = BACKLIGHT_DUTY_CYCLE_SHIFT + 1; + } else { + blc_pwm_ctl &= ~BACKLIGHT_DUTY_CYCLE_MASK; + max_backlight = ((blc_pwm_ctl & BACKLIGHT_MODULATION_FREQ_MASK) >> + BACKLIGHT_MODULATION_FREQ_SHIFT) * 2; + shift = BACKLIGHT_DUTY_CYCLE_SHIFT; + } + level = (bclp * max_backlight) / 255; + I915_WRITE(BLC_PWM_CTL, blc_pwm_ctl | (level << shift)); + } asle->cblv = (bclp*0x64)/0xff | ASLE_CBLV_VALID; return 0; diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index 3f7963553464..1687edf68795 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -86,6 +86,10 @@ #define I915_GC_RENDER_CLOCK_200_MHZ (1 << 0) #define I915_GC_RENDER_CLOCK_333_MHZ (4 << 0) #define LBB 0xf4 +#define GDRST 0xc0 +#define GDRST_FULL (0<<2) +#define GDRST_RENDER (1<<2) +#define GDRST_MEDIA (3<<2) /* VGA stuff */ @@ -344,9 +348,37 @@ #define FBC_CTL_PLANEA (0<<0) #define FBC_CTL_PLANEB (1<<0) #define FBC_FENCE_OFF 0x0321b +#define FBC_TAG 0x03300 #define FBC_LL_SIZE (1536) +/* Framebuffer compression for GM45+ */ +#define DPFC_CB_BASE 0x3200 +#define DPFC_CONTROL 0x3208 +#define DPFC_CTL_EN (1<<31) +#define DPFC_CTL_PLANEA (0<<30) +#define DPFC_CTL_PLANEB (1<<30) +#define DPFC_CTL_FENCE_EN (1<<29) +#define DPFC_SR_EN (1<<10) +#define DPFC_CTL_LIMIT_1X (0<<6) +#define DPFC_CTL_LIMIT_2X (1<<6) +#define DPFC_CTL_LIMIT_4X (2<<6) +#define DPFC_RECOMP_CTL 0x320c +#define DPFC_RECOMP_STALL_EN (1<<27) +#define DPFC_RECOMP_STALL_WM_SHIFT (16) +#define DPFC_RECOMP_STALL_WM_MASK (0x07ff0000) +#define DPFC_RECOMP_TIMER_COUNT_SHIFT (0) +#define DPFC_RECOMP_TIMER_COUNT_MASK (0x0000003f) +#define DPFC_STATUS 0x3210 +#define DPFC_INVAL_SEG_SHIFT (16) +#define DPFC_INVAL_SEG_MASK (0x07ff0000) +#define DPFC_COMP_SEG_SHIFT (0) +#define DPFC_COMP_SEG_MASK (0x000003ff) +#define DPFC_STATUS2 0x3214 +#define DPFC_FENCE_YOFF 0x3218 +#define DPFC_CHICKEN 0x3224 +#define DPFC_HT_MODIFY (1<<31) + /* * GPIO regs */ @@ -936,6 +968,8 @@ #define LVDS_PORT_EN (1 << 31) /* Selects pipe B for LVDS data. Must be set on pre-965. */ #define LVDS_PIPEB_SELECT (1 << 30) +/* Enable border for unscaled (or aspect-scaled) display */ +#define LVDS_BORDER_ENABLE (1 << 15) /* * Enables the A0-A2 data pairs and CLKA, containing 18 bits of color data per * pixel. @@ -1046,6 +1080,8 @@ #define BACKLIGHT_DUTY_CYCLE_SHIFT (0) #define BACKLIGHT_DUTY_CYCLE_MASK (0xffff) +#define BLC_HIST_CTL 0x61260 + /* TV port control */ #define TV_CTL 0x68000 /** Enables the TV encoder */ @@ -1748,6 +1784,11 @@ #define PIPE_START_VBLANK_INTERRUPT_STATUS (1UL<<2) /* 965 or later */ #define PIPE_VBLANK_INTERRUPT_STATUS (1UL<<1) #define PIPE_OVERLAY_UPDATED_STATUS (1UL<<0) +#define PIPE_BPC_MASK (7 << 5) /* Ironlake */ +#define PIPE_8BPC (0 << 5) +#define PIPE_10BPC (1 << 5) +#define PIPE_6BPC (2 << 5) +#define PIPE_12BPC (3 << 5) #define DSPARB 0x70030 #define DSPARB_CSTART_MASK (0x7f << 7) @@ -1758,17 +1799,29 @@ #define DSPARB_AEND_SHIFT 0 #define DSPFW1 0x70034 +#define DSPFW_SR_SHIFT 23 +#define DSPFW_CURSORB_SHIFT 16 +#define DSPFW_PLANEB_SHIFT 8 #define DSPFW2 0x70038 +#define DSPFW_CURSORA_MASK 0x00003f00 +#define DSPFW_CURSORA_SHIFT 16 #define DSPFW3 0x7003c +#define DSPFW_HPLL_SR_EN (1<<31) +#define DSPFW_CURSOR_SR_SHIFT 24 #define IGD_SELF_REFRESH_EN (1<<30) /* FIFO watermark sizes etc */ +#define G4X_FIFO_LINE_SIZE 64 #define I915_FIFO_LINE_SIZE 64 #define I830_FIFO_LINE_SIZE 32 + +#define G4X_FIFO_SIZE 127 #define I945_FIFO_SIZE 127 /* 945 & 965 */ #define I915_FIFO_SIZE 95 #define I855GM_FIFO_SIZE 127 /* In cachelines */ #define I830_FIFO_SIZE 95 + +#define G4X_MAX_WM 0x3f #define I915_MAX_WM 0x3f #define IGD_DISPLAY_FIFO 512 /* in 64byte unit */ @@ -1998,8 +2051,15 @@ #define PFA_CTL_1 0x68080 #define PFB_CTL_1 0x68880 #define PF_ENABLE (1<<31) +#define PF_FILTER_MASK (3<<23) +#define PF_FILTER_PROGRAMMED (0<<23) +#define PF_FILTER_MED_3x3 (1<<23) +#define PF_FILTER_EDGE_ENHANCE (2<<23) +#define PF_FILTER_EDGE_SOFTEN (3<<23) #define PFA_WIN_SZ 0x68074 #define PFB_WIN_SZ 0x68874 +#define PFA_WIN_POS 0x68070 +#define PFB_WIN_POS 0x68870 /* legacy palette */ #define LGC_PALETTE_A 0x4a000 @@ -2115,11 +2175,11 @@ #define DREF_CPU_SOURCE_OUTPUT_MASK (3<<13) #define DREF_SSC_SOURCE_DISABLE (0<<11) #define DREF_SSC_SOURCE_ENABLE (2<<11) -#define DREF_SSC_SOURCE_MASK (2<<11) +#define DREF_SSC_SOURCE_MASK (3<<11) #define DREF_NONSPREAD_SOURCE_DISABLE (0<<9) #define DREF_NONSPREAD_CK505_ENABLE (1<<9) #define DREF_NONSPREAD_SOURCE_ENABLE (2<<9) -#define DREF_NONSPREAD_SOURCE_MASK (2<<9) +#define DREF_NONSPREAD_SOURCE_MASK (3<<9) #define DREF_SUPERSPREAD_SOURCE_DISABLE (0<<7) #define DREF_SUPERSPREAD_SOURCE_ENABLE (2<<7) #define DREF_SSC4_DOWNSPREAD (0<<6) diff --git a/drivers/gpu/drm/i915/i915_suspend.c b/drivers/gpu/drm/i915/i915_suspend.c index 20d4d19f5568..6eec8171a44e 100644 --- a/drivers/gpu/drm/i915/i915_suspend.c +++ b/drivers/gpu/drm/i915/i915_suspend.c @@ -32,11 +32,15 @@ static bool i915_pipe_enabled(struct drm_device *dev, enum pipe pipe) { struct drm_i915_private *dev_priv = dev->dev_private; + u32 dpll_reg; - if (pipe == PIPE_A) - return (I915_READ(DPLL_A) & DPLL_VCO_ENABLE); - else - return (I915_READ(DPLL_B) & DPLL_VCO_ENABLE); + if (IS_IGDNG(dev)) { + dpll_reg = (pipe == PIPE_A) ? PCH_DPLL_A: PCH_DPLL_B; + } else { + dpll_reg = (pipe == PIPE_A) ? DPLL_A: DPLL_B; + } + + return (I915_READ(dpll_reg) & DPLL_VCO_ENABLE); } static void i915_save_palette(struct drm_device *dev, enum pipe pipe) @@ -49,6 +53,9 @@ static void i915_save_palette(struct drm_device *dev, enum pipe pipe) if (!i915_pipe_enabled(dev, pipe)) return; + if (IS_IGDNG(dev)) + reg = (pipe == PIPE_A) ? LGC_PALETTE_A : LGC_PALETTE_B; + if (pipe == PIPE_A) array = dev_priv->save_palette_a; else @@ -68,6 +75,9 @@ static void i915_restore_palette(struct drm_device *dev, enum pipe pipe) if (!i915_pipe_enabled(dev, pipe)) return; + if (IS_IGDNG(dev)) + reg = (pipe == PIPE_A) ? LGC_PALETTE_A : LGC_PALETTE_B; + if (pipe == PIPE_A) array = dev_priv->save_palette_a; else @@ -228,13 +238,25 @@ static void i915_save_modeset_reg(struct drm_device *dev) if (drm_core_check_feature(dev, DRIVER_MODESET)) return; + + if (IS_IGDNG(dev)) { + dev_priv->savePCH_DREF_CONTROL = I915_READ(PCH_DREF_CONTROL); + dev_priv->saveDISP_ARB_CTL = I915_READ(DISP_ARB_CTL); + } + /* Pipe & plane A info */ dev_priv->savePIPEACONF = I915_READ(PIPEACONF); dev_priv->savePIPEASRC = I915_READ(PIPEASRC); - dev_priv->saveFPA0 = I915_READ(FPA0); - dev_priv->saveFPA1 = I915_READ(FPA1); - dev_priv->saveDPLL_A = I915_READ(DPLL_A); - if (IS_I965G(dev)) + if (IS_IGDNG(dev)) { + dev_priv->saveFPA0 = I915_READ(PCH_FPA0); + dev_priv->saveFPA1 = I915_READ(PCH_FPA1); + dev_priv->saveDPLL_A = I915_READ(PCH_DPLL_A); + } else { + dev_priv->saveFPA0 = I915_READ(FPA0); + dev_priv->saveFPA1 = I915_READ(FPA1); + dev_priv->saveDPLL_A = I915_READ(DPLL_A); + } + if (IS_I965G(dev) && !IS_IGDNG(dev)) dev_priv->saveDPLL_A_MD = I915_READ(DPLL_A_MD); dev_priv->saveHTOTAL_A = I915_READ(HTOTAL_A); dev_priv->saveHBLANK_A = I915_READ(HBLANK_A); @@ -242,7 +264,30 @@ static void i915_save_modeset_reg(struct drm_device *dev) dev_priv->saveVTOTAL_A = I915_READ(VTOTAL_A); dev_priv->saveVBLANK_A = I915_READ(VBLANK_A); dev_priv->saveVSYNC_A = I915_READ(VSYNC_A); - dev_priv->saveBCLRPAT_A = I915_READ(BCLRPAT_A); + if (!IS_IGDNG(dev)) + dev_priv->saveBCLRPAT_A = I915_READ(BCLRPAT_A); + + if (IS_IGDNG(dev)) { + dev_priv->savePIPEA_DATA_M1 = I915_READ(PIPEA_DATA_M1); + dev_priv->savePIPEA_DATA_N1 = I915_READ(PIPEA_DATA_N1); + dev_priv->savePIPEA_LINK_M1 = I915_READ(PIPEA_LINK_M1); + dev_priv->savePIPEA_LINK_N1 = I915_READ(PIPEA_LINK_N1); + + dev_priv->saveFDI_TXA_CTL = I915_READ(FDI_TXA_CTL); + dev_priv->saveFDI_RXA_CTL = I915_READ(FDI_RXA_CTL); + + dev_priv->savePFA_CTL_1 = I915_READ(PFA_CTL_1); + dev_priv->savePFA_WIN_SZ = I915_READ(PFA_WIN_SZ); + dev_priv->savePFA_WIN_POS = I915_READ(PFA_WIN_POS); + + dev_priv->saveTRANSACONF = I915_READ(TRANSACONF); + dev_priv->saveTRANS_HTOTAL_A = I915_READ(TRANS_HTOTAL_A); + dev_priv->saveTRANS_HBLANK_A = I915_READ(TRANS_HBLANK_A); + dev_priv->saveTRANS_HSYNC_A = I915_READ(TRANS_HSYNC_A); + dev_priv->saveTRANS_VTOTAL_A = I915_READ(TRANS_VTOTAL_A); + dev_priv->saveTRANS_VBLANK_A = I915_READ(TRANS_VBLANK_A); + dev_priv->saveTRANS_VSYNC_A = I915_READ(TRANS_VSYNC_A); + } dev_priv->saveDSPACNTR = I915_READ(DSPACNTR); dev_priv->saveDSPASTRIDE = I915_READ(DSPASTRIDE); @@ -259,10 +304,16 @@ static void i915_save_modeset_reg(struct drm_device *dev) /* Pipe & plane B info */ dev_priv->savePIPEBCONF = I915_READ(PIPEBCONF); dev_priv->savePIPEBSRC = I915_READ(PIPEBSRC); - dev_priv->saveFPB0 = I915_READ(FPB0); - dev_priv->saveFPB1 = I915_READ(FPB1); - dev_priv->saveDPLL_B = I915_READ(DPLL_B); - if (IS_I965G(dev)) + if (IS_IGDNG(dev)) { + dev_priv->saveFPB0 = I915_READ(PCH_FPB0); + dev_priv->saveFPB1 = I915_READ(PCH_FPB1); + dev_priv->saveDPLL_B = I915_READ(PCH_DPLL_B); + } else { + dev_priv->saveFPB0 = I915_READ(FPB0); + dev_priv->saveFPB1 = I915_READ(FPB1); + dev_priv->saveDPLL_B = I915_READ(DPLL_B); + } + if (IS_I965G(dev) && !IS_IGDNG(dev)) dev_priv->saveDPLL_B_MD = I915_READ(DPLL_B_MD); dev_priv->saveHTOTAL_B = I915_READ(HTOTAL_B); dev_priv->saveHBLANK_B = I915_READ(HBLANK_B); @@ -270,7 +321,30 @@ static void i915_save_modeset_reg(struct drm_device *dev) dev_priv->saveVTOTAL_B = I915_READ(VTOTAL_B); dev_priv->saveVBLANK_B = I915_READ(VBLANK_B); dev_priv->saveVSYNC_B = I915_READ(VSYNC_B); - dev_priv->saveBCLRPAT_A = I915_READ(BCLRPAT_A); + if (!IS_IGDNG(dev)) + dev_priv->saveBCLRPAT_B = I915_READ(BCLRPAT_B); + + if (IS_IGDNG(dev)) { + dev_priv->savePIPEB_DATA_M1 = I915_READ(PIPEB_DATA_M1); + dev_priv->savePIPEB_DATA_N1 = I915_READ(PIPEB_DATA_N1); + dev_priv->savePIPEB_LINK_M1 = I915_READ(PIPEB_LINK_M1); + dev_priv->savePIPEB_LINK_N1 = I915_READ(PIPEB_LINK_N1); + + dev_priv->saveFDI_TXB_CTL = I915_READ(FDI_TXB_CTL); + dev_priv->saveFDI_RXB_CTL = I915_READ(FDI_RXB_CTL); + + dev_priv->savePFB_CTL_1 = I915_READ(PFB_CTL_1); + dev_priv->savePFB_WIN_SZ = I915_READ(PFB_WIN_SZ); + dev_priv->savePFB_WIN_POS = I915_READ(PFB_WIN_POS); + + dev_priv->saveTRANSBCONF = I915_READ(TRANSBCONF); + dev_priv->saveTRANS_HTOTAL_B = I915_READ(TRANS_HTOTAL_B); + dev_priv->saveTRANS_HBLANK_B = I915_READ(TRANS_HBLANK_B); + dev_priv->saveTRANS_HSYNC_B = I915_READ(TRANS_HSYNC_B); + dev_priv->saveTRANS_VTOTAL_B = I915_READ(TRANS_VTOTAL_B); + dev_priv->saveTRANS_VBLANK_B = I915_READ(TRANS_VBLANK_B); + dev_priv->saveTRANS_VSYNC_B = I915_READ(TRANS_VSYNC_B); + } dev_priv->saveDSPBCNTR = I915_READ(DSPBCNTR); dev_priv->saveDSPBSTRIDE = I915_READ(DSPBSTRIDE); @@ -285,26 +359,50 @@ static void i915_save_modeset_reg(struct drm_device *dev) dev_priv->savePIPEBSTAT = I915_READ(PIPEBSTAT); return; } + static void i915_restore_modeset_reg(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; + int dpll_a_reg, fpa0_reg, fpa1_reg; + int dpll_b_reg, fpb0_reg, fpb1_reg; if (drm_core_check_feature(dev, DRIVER_MODESET)) return; + if (IS_IGDNG(dev)) { + dpll_a_reg = PCH_DPLL_A; + dpll_b_reg = PCH_DPLL_B; + fpa0_reg = PCH_FPA0; + fpb0_reg = PCH_FPB0; + fpa1_reg = PCH_FPA1; + fpb1_reg = PCH_FPB1; + } else { + dpll_a_reg = DPLL_A; + dpll_b_reg = DPLL_B; + fpa0_reg = FPA0; + fpb0_reg = FPB0; + fpa1_reg = FPA1; + fpb1_reg = FPB1; + } + + if (IS_IGDNG(dev)) { + I915_WRITE(PCH_DREF_CONTROL, dev_priv->savePCH_DREF_CONTROL); + I915_WRITE(DISP_ARB_CTL, dev_priv->saveDISP_ARB_CTL); + } + /* Pipe & plane A info */ /* Prime the clock */ if (dev_priv->saveDPLL_A & DPLL_VCO_ENABLE) { - I915_WRITE(DPLL_A, dev_priv->saveDPLL_A & + I915_WRITE(dpll_a_reg, dev_priv->saveDPLL_A & ~DPLL_VCO_ENABLE); DRM_UDELAY(150); } - I915_WRITE(FPA0, dev_priv->saveFPA0); - I915_WRITE(FPA1, dev_priv->saveFPA1); + I915_WRITE(fpa0_reg, dev_priv->saveFPA0); + I915_WRITE(fpa1_reg, dev_priv->saveFPA1); /* Actually enable it */ - I915_WRITE(DPLL_A, dev_priv->saveDPLL_A); + I915_WRITE(dpll_a_reg, dev_priv->saveDPLL_A); DRM_UDELAY(150); - if (IS_I965G(dev)) + if (IS_I965G(dev) && !IS_IGDNG(dev)) I915_WRITE(DPLL_A_MD, dev_priv->saveDPLL_A_MD); DRM_UDELAY(150); @@ -315,7 +413,30 @@ static void i915_restore_modeset_reg(struct drm_device *dev) I915_WRITE(VTOTAL_A, dev_priv->saveVTOTAL_A); I915_WRITE(VBLANK_A, dev_priv->saveVBLANK_A); I915_WRITE(VSYNC_A, dev_priv->saveVSYNC_A); - I915_WRITE(BCLRPAT_A, dev_priv->saveBCLRPAT_A); + if (!IS_IGDNG(dev)) + I915_WRITE(BCLRPAT_A, dev_priv->saveBCLRPAT_A); + + if (IS_IGDNG(dev)) { + I915_WRITE(PIPEA_DATA_M1, dev_priv->savePIPEA_DATA_M1); + I915_WRITE(PIPEA_DATA_N1, dev_priv->savePIPEA_DATA_N1); + I915_WRITE(PIPEA_LINK_M1, dev_priv->savePIPEA_LINK_M1); + I915_WRITE(PIPEA_LINK_N1, dev_priv->savePIPEA_LINK_N1); + + I915_WRITE(FDI_RXA_CTL, dev_priv->saveFDI_RXA_CTL); + I915_WRITE(FDI_TXA_CTL, dev_priv->saveFDI_TXA_CTL); + + I915_WRITE(PFA_CTL_1, dev_priv->savePFA_CTL_1); + I915_WRITE(PFA_WIN_SZ, dev_priv->savePFA_WIN_SZ); + I915_WRITE(PFA_WIN_POS, dev_priv->savePFA_WIN_POS); + + I915_WRITE(TRANSACONF, dev_priv->saveTRANSACONF); + I915_WRITE(TRANS_HTOTAL_A, dev_priv->saveTRANS_HTOTAL_A); + I915_WRITE(TRANS_HBLANK_A, dev_priv->saveTRANS_HBLANK_A); + I915_WRITE(TRANS_HSYNC_A, dev_priv->saveTRANS_HSYNC_A); + I915_WRITE(TRANS_VTOTAL_A, dev_priv->saveTRANS_VTOTAL_A); + I915_WRITE(TRANS_VBLANK_A, dev_priv->saveTRANS_VBLANK_A); + I915_WRITE(TRANS_VSYNC_A, dev_priv->saveTRANS_VSYNC_A); + } /* Restore plane info */ I915_WRITE(DSPASIZE, dev_priv->saveDSPASIZE); @@ -337,16 +458,16 @@ static void i915_restore_modeset_reg(struct drm_device *dev) /* Pipe & plane B info */ if (dev_priv->saveDPLL_B & DPLL_VCO_ENABLE) { - I915_WRITE(DPLL_B, dev_priv->saveDPLL_B & + I915_WRITE(dpll_b_reg, dev_priv->saveDPLL_B & ~DPLL_VCO_ENABLE); DRM_UDELAY(150); } - I915_WRITE(FPB0, dev_priv->saveFPB0); - I915_WRITE(FPB1, dev_priv->saveFPB1); + I915_WRITE(fpb0_reg, dev_priv->saveFPB0); + I915_WRITE(fpb1_reg, dev_priv->saveFPB1); /* Actually enable it */ - I915_WRITE(DPLL_B, dev_priv->saveDPLL_B); + I915_WRITE(dpll_b_reg, dev_priv->saveDPLL_B); DRM_UDELAY(150); - if (IS_I965G(dev)) + if (IS_I965G(dev) && !IS_IGDNG(dev)) I915_WRITE(DPLL_B_MD, dev_priv->saveDPLL_B_MD); DRM_UDELAY(150); @@ -357,7 +478,30 @@ static void i915_restore_modeset_reg(struct drm_device *dev) I915_WRITE(VTOTAL_B, dev_priv->saveVTOTAL_B); I915_WRITE(VBLANK_B, dev_priv->saveVBLANK_B); I915_WRITE(VSYNC_B, dev_priv->saveVSYNC_B); - I915_WRITE(BCLRPAT_B, dev_priv->saveBCLRPAT_B); + if (!IS_IGDNG(dev)) + I915_WRITE(BCLRPAT_B, dev_priv->saveBCLRPAT_B); + + if (IS_IGDNG(dev)) { + I915_WRITE(PIPEB_DATA_M1, dev_priv->savePIPEB_DATA_M1); + I915_WRITE(PIPEB_DATA_N1, dev_priv->savePIPEB_DATA_N1); + I915_WRITE(PIPEB_LINK_M1, dev_priv->savePIPEB_LINK_M1); + I915_WRITE(PIPEB_LINK_N1, dev_priv->savePIPEB_LINK_N1); + + I915_WRITE(FDI_RXB_CTL, dev_priv->saveFDI_RXB_CTL); + I915_WRITE(FDI_TXB_CTL, dev_priv->saveFDI_TXB_CTL); + + I915_WRITE(PFB_CTL_1, dev_priv->savePFB_CTL_1); + I915_WRITE(PFB_WIN_SZ, dev_priv->savePFB_WIN_SZ); + I915_WRITE(PFB_WIN_POS, dev_priv->savePFB_WIN_POS); + + I915_WRITE(TRANSBCONF, dev_priv->saveTRANSBCONF); + I915_WRITE(TRANS_HTOTAL_B, dev_priv->saveTRANS_HTOTAL_B); + I915_WRITE(TRANS_HBLANK_B, dev_priv->saveTRANS_HBLANK_B); + I915_WRITE(TRANS_HSYNC_B, dev_priv->saveTRANS_HSYNC_B); + I915_WRITE(TRANS_VTOTAL_B, dev_priv->saveTRANS_VTOTAL_B); + I915_WRITE(TRANS_VBLANK_B, dev_priv->saveTRANS_VBLANK_B); + I915_WRITE(TRANS_VSYNC_B, dev_priv->saveTRANS_VSYNC_B); + } /* Restore plane info */ I915_WRITE(DSPBSIZE, dev_priv->saveDSPBSIZE); @@ -379,19 +523,10 @@ static void i915_restore_modeset_reg(struct drm_device *dev) return; } -int i915_save_state(struct drm_device *dev) + +void i915_save_display(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; - int i; - - pci_read_config_byte(dev->pdev, LBB, &dev_priv->saveLBB); - - /* Render Standby */ - if (IS_I965G(dev) && IS_MOBILE(dev)) - dev_priv->saveRENDERSTANDBY = I915_READ(MCHBAR_RENDER_STANDBY); - - /* Hardware status page */ - dev_priv->saveHWS = I915_READ(HWS_PGA); /* Display arbitration control */ dev_priv->saveDSPARB = I915_READ(DSPARB); @@ -399,6 +534,7 @@ int i915_save_state(struct drm_device *dev) /* This is only meaningful in non-KMS mode */ /* Don't save them in KMS mode */ i915_save_modeset_reg(dev); + /* Cursor state */ dev_priv->saveCURACNTR = I915_READ(CURACNTR); dev_priv->saveCURAPOS = I915_READ(CURAPOS); @@ -410,21 +546,43 @@ int i915_save_state(struct drm_device *dev) dev_priv->saveCURSIZE = I915_READ(CURSIZE); /* CRT state */ - dev_priv->saveADPA = I915_READ(ADPA); + if (IS_IGDNG(dev)) { + dev_priv->saveADPA = I915_READ(PCH_ADPA); + } else { + dev_priv->saveADPA = I915_READ(ADPA); + } /* LVDS state */ - dev_priv->savePP_CONTROL = I915_READ(PP_CONTROL); - dev_priv->savePFIT_PGM_RATIOS = I915_READ(PFIT_PGM_RATIOS); - dev_priv->saveBLC_PWM_CTL = I915_READ(BLC_PWM_CTL); - if (IS_I965G(dev)) - dev_priv->saveBLC_PWM_CTL2 = I915_READ(BLC_PWM_CTL2); - if (IS_MOBILE(dev) && !IS_I830(dev)) - dev_priv->saveLVDS = I915_READ(LVDS); - if (!IS_I830(dev) && !IS_845G(dev)) + if (IS_IGDNG(dev)) { + dev_priv->savePP_CONTROL = I915_READ(PCH_PP_CONTROL); + dev_priv->saveBLC_PWM_CTL = I915_READ(BLC_PWM_PCH_CTL1); + dev_priv->saveBLC_PWM_CTL2 = I915_READ(BLC_PWM_PCH_CTL2); + dev_priv->saveBLC_CPU_PWM_CTL = I915_READ(BLC_PWM_CPU_CTL); + dev_priv->saveBLC_CPU_PWM_CTL2 = I915_READ(BLC_PWM_CPU_CTL2); + dev_priv->saveLVDS = I915_READ(PCH_LVDS); + } else { + dev_priv->savePP_CONTROL = I915_READ(PP_CONTROL); + dev_priv->savePFIT_PGM_RATIOS = I915_READ(PFIT_PGM_RATIOS); + dev_priv->saveBLC_PWM_CTL = I915_READ(BLC_PWM_CTL); + dev_priv->saveBLC_HIST_CTL = I915_READ(BLC_HIST_CTL); + if (IS_I965G(dev)) + dev_priv->saveBLC_PWM_CTL2 = I915_READ(BLC_PWM_CTL2); + if (IS_MOBILE(dev) && !IS_I830(dev)) + dev_priv->saveLVDS = I915_READ(LVDS); + } + + if (!IS_I830(dev) && !IS_845G(dev) && !IS_IGDNG(dev)) dev_priv->savePFIT_CONTROL = I915_READ(PFIT_CONTROL); - dev_priv->savePP_ON_DELAYS = I915_READ(PP_ON_DELAYS); - dev_priv->savePP_OFF_DELAYS = I915_READ(PP_OFF_DELAYS); - dev_priv->savePP_DIVISOR = I915_READ(PP_DIVISOR); + + if (IS_IGDNG(dev)) { + dev_priv->savePP_ON_DELAYS = I915_READ(PCH_PP_ON_DELAYS); + dev_priv->savePP_OFF_DELAYS = I915_READ(PCH_PP_OFF_DELAYS); + dev_priv->savePP_DIVISOR = I915_READ(PCH_PP_DIVISOR); + } else { + dev_priv->savePP_ON_DELAYS = I915_READ(PP_ON_DELAYS); + dev_priv->savePP_OFF_DELAYS = I915_READ(PP_OFF_DELAYS); + dev_priv->savePP_DIVISOR = I915_READ(PP_DIVISOR); + } /* Display Port state */ if (SUPPORTS_INTEGRATED_DP(dev)) { @@ -443,25 +601,162 @@ int i915_save_state(struct drm_device *dev) /* FIXME: save TV & SDVO state */ /* FBC state */ - dev_priv->saveFBC_CFB_BASE = I915_READ(FBC_CFB_BASE); - dev_priv->saveFBC_LL_BASE = I915_READ(FBC_LL_BASE); - dev_priv->saveFBC_CONTROL2 = I915_READ(FBC_CONTROL2); - dev_priv->saveFBC_CONTROL = I915_READ(FBC_CONTROL); - - /* Interrupt state */ - dev_priv->saveIIR = I915_READ(IIR); - dev_priv->saveIER = I915_READ(IER); - dev_priv->saveIMR = I915_READ(IMR); + if (IS_GM45(dev)) { + dev_priv->saveDPFC_CB_BASE = I915_READ(DPFC_CB_BASE); + } else { + dev_priv->saveFBC_CFB_BASE = I915_READ(FBC_CFB_BASE); + dev_priv->saveFBC_LL_BASE = I915_READ(FBC_LL_BASE); + dev_priv->saveFBC_CONTROL2 = I915_READ(FBC_CONTROL2); + dev_priv->saveFBC_CONTROL = I915_READ(FBC_CONTROL); + } /* VGA state */ dev_priv->saveVGA0 = I915_READ(VGA0); dev_priv->saveVGA1 = I915_READ(VGA1); dev_priv->saveVGA_PD = I915_READ(VGA_PD); - dev_priv->saveVGACNTRL = I915_READ(VGACNTRL); + if (IS_IGDNG(dev)) + dev_priv->saveVGACNTRL = I915_READ(CPU_VGACNTRL); + else + dev_priv->saveVGACNTRL = I915_READ(VGACNTRL); + + i915_save_vga(dev); +} + +void i915_restore_display(struct drm_device *dev) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + + /* Display arbitration */ + I915_WRITE(DSPARB, dev_priv->saveDSPARB); + + /* Display port ratios (must be done before clock is set) */ + if (SUPPORTS_INTEGRATED_DP(dev)) { + I915_WRITE(PIPEA_GMCH_DATA_M, dev_priv->savePIPEA_GMCH_DATA_M); + I915_WRITE(PIPEB_GMCH_DATA_M, dev_priv->savePIPEB_GMCH_DATA_M); + I915_WRITE(PIPEA_GMCH_DATA_N, dev_priv->savePIPEA_GMCH_DATA_N); + I915_WRITE(PIPEB_GMCH_DATA_N, dev_priv->savePIPEB_GMCH_DATA_N); + I915_WRITE(PIPEA_DP_LINK_M, dev_priv->savePIPEA_DP_LINK_M); + I915_WRITE(PIPEB_DP_LINK_M, dev_priv->savePIPEB_DP_LINK_M); + I915_WRITE(PIPEA_DP_LINK_N, dev_priv->savePIPEA_DP_LINK_N); + I915_WRITE(PIPEB_DP_LINK_N, dev_priv->savePIPEB_DP_LINK_N); + } + + /* This is only meaningful in non-KMS mode */ + /* Don't restore them in KMS mode */ + i915_restore_modeset_reg(dev); + + /* Cursor state */ + I915_WRITE(CURAPOS, dev_priv->saveCURAPOS); + I915_WRITE(CURACNTR, dev_priv->saveCURACNTR); + I915_WRITE(CURABASE, dev_priv->saveCURABASE); + I915_WRITE(CURBPOS, dev_priv->saveCURBPOS); + I915_WRITE(CURBCNTR, dev_priv->saveCURBCNTR); + I915_WRITE(CURBBASE, dev_priv->saveCURBBASE); + if (!IS_I9XX(dev)) + I915_WRITE(CURSIZE, dev_priv->saveCURSIZE); + + /* CRT state */ + if (IS_IGDNG(dev)) + I915_WRITE(PCH_ADPA, dev_priv->saveADPA); + else + I915_WRITE(ADPA, dev_priv->saveADPA); + + /* LVDS state */ + if (IS_I965G(dev) && !IS_IGDNG(dev)) + I915_WRITE(BLC_PWM_CTL2, dev_priv->saveBLC_PWM_CTL2); + + if (IS_IGDNG(dev)) { + I915_WRITE(PCH_LVDS, dev_priv->saveLVDS); + } else if (IS_MOBILE(dev) && !IS_I830(dev)) + I915_WRITE(LVDS, dev_priv->saveLVDS); + + if (!IS_I830(dev) && !IS_845G(dev) && !IS_IGDNG(dev)) + I915_WRITE(PFIT_CONTROL, dev_priv->savePFIT_CONTROL); + + if (IS_IGDNG(dev)) { + I915_WRITE(BLC_PWM_PCH_CTL1, dev_priv->saveBLC_PWM_CTL); + I915_WRITE(BLC_PWM_PCH_CTL2, dev_priv->saveBLC_PWM_CTL2); + I915_WRITE(BLC_PWM_CPU_CTL, dev_priv->saveBLC_CPU_PWM_CTL); + I915_WRITE(BLC_PWM_CPU_CTL2, dev_priv->saveBLC_CPU_PWM_CTL2); + I915_WRITE(PCH_PP_ON_DELAYS, dev_priv->savePP_ON_DELAYS); + I915_WRITE(PCH_PP_OFF_DELAYS, dev_priv->savePP_OFF_DELAYS); + I915_WRITE(PCH_PP_DIVISOR, dev_priv->savePP_DIVISOR); + I915_WRITE(PCH_PP_CONTROL, dev_priv->savePP_CONTROL); + } else { + I915_WRITE(PFIT_PGM_RATIOS, dev_priv->savePFIT_PGM_RATIOS); + I915_WRITE(BLC_PWM_CTL, dev_priv->saveBLC_PWM_CTL); + I915_WRITE(BLC_HIST_CTL, dev_priv->saveBLC_HIST_CTL); + I915_WRITE(PP_ON_DELAYS, dev_priv->savePP_ON_DELAYS); + I915_WRITE(PP_OFF_DELAYS, dev_priv->savePP_OFF_DELAYS); + I915_WRITE(PP_DIVISOR, dev_priv->savePP_DIVISOR); + I915_WRITE(PP_CONTROL, dev_priv->savePP_CONTROL); + } + + /* Display Port state */ + if (SUPPORTS_INTEGRATED_DP(dev)) { + I915_WRITE(DP_B, dev_priv->saveDP_B); + I915_WRITE(DP_C, dev_priv->saveDP_C); + I915_WRITE(DP_D, dev_priv->saveDP_D); + } + /* FIXME: restore TV & SDVO state */ + + /* FBC info */ + if (IS_GM45(dev)) { + g4x_disable_fbc(dev); + I915_WRITE(DPFC_CB_BASE, dev_priv->saveDPFC_CB_BASE); + } else { + i8xx_disable_fbc(dev); + I915_WRITE(FBC_CFB_BASE, dev_priv->saveFBC_CFB_BASE); + I915_WRITE(FBC_LL_BASE, dev_priv->saveFBC_LL_BASE); + I915_WRITE(FBC_CONTROL2, dev_priv->saveFBC_CONTROL2); + I915_WRITE(FBC_CONTROL, dev_priv->saveFBC_CONTROL); + } + + /* VGA state */ + if (IS_IGDNG(dev)) + I915_WRITE(CPU_VGACNTRL, dev_priv->saveVGACNTRL); + else + I915_WRITE(VGACNTRL, dev_priv->saveVGACNTRL); + I915_WRITE(VGA0, dev_priv->saveVGA0); + I915_WRITE(VGA1, dev_priv->saveVGA1); + I915_WRITE(VGA_PD, dev_priv->saveVGA_PD); + DRM_UDELAY(150); + + i915_restore_vga(dev); +} + +int i915_save_state(struct drm_device *dev) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + int i; + + pci_read_config_byte(dev->pdev, LBB, &dev_priv->saveLBB); + + /* Render Standby */ + if (IS_I965G(dev) && IS_MOBILE(dev)) + dev_priv->saveRENDERSTANDBY = I915_READ(MCHBAR_RENDER_STANDBY); + + /* Hardware status page */ + dev_priv->saveHWS = I915_READ(HWS_PGA); + + i915_save_display(dev); + + /* Interrupt state */ + if (IS_IGDNG(dev)) { + dev_priv->saveDEIER = I915_READ(DEIER); + dev_priv->saveDEIMR = I915_READ(DEIMR); + dev_priv->saveGTIER = I915_READ(GTIER); + dev_priv->saveGTIMR = I915_READ(GTIMR); + dev_priv->saveFDI_RXA_IMR = I915_READ(FDI_RXA_IMR); + dev_priv->saveFDI_RXB_IMR = I915_READ(FDI_RXB_IMR); + } else { + dev_priv->saveIER = I915_READ(IER); + dev_priv->saveIMR = I915_READ(IMR); + } /* Clock gating state */ dev_priv->saveD_STATE = I915_READ(D_STATE); - dev_priv->saveDSPCLK_GATE_D = I915_READ(DSPCLK_GATE_D); + dev_priv->saveDSPCLK_GATE_D = I915_READ(DSPCLK_GATE_D); /* Not sure about this */ /* Cache mode state */ dev_priv->saveCACHE_MODE_0 = I915_READ(CACHE_MODE_0); @@ -489,7 +784,6 @@ int i915_save_state(struct drm_device *dev) for (i = 0; i < 8; i++) dev_priv->saveFENCE[i+8] = I915_READ(FENCE_REG_945_8 + (i * 4)); } - i915_save_vga(dev); return 0; } @@ -508,9 +802,6 @@ int i915_restore_state(struct drm_device *dev) /* Hardware status page */ I915_WRITE(HWS_PGA, dev_priv->saveHWS); - /* Display arbitration */ - I915_WRITE(DSPARB, dev_priv->saveDSPARB); - /* Fences */ if (IS_I965G(dev)) { for (i = 0; i < 16; i++) @@ -522,69 +813,21 @@ int i915_restore_state(struct drm_device *dev) for (i = 0; i < 8; i++) I915_WRITE(FENCE_REG_945_8 + (i * 4), dev_priv->saveFENCE[i+8]); } - - /* Display port ratios (must be done before clock is set) */ - if (SUPPORTS_INTEGRATED_DP(dev)) { - I915_WRITE(PIPEA_GMCH_DATA_M, dev_priv->savePIPEA_GMCH_DATA_M); - I915_WRITE(PIPEB_GMCH_DATA_M, dev_priv->savePIPEB_GMCH_DATA_M); - I915_WRITE(PIPEA_GMCH_DATA_N, dev_priv->savePIPEA_GMCH_DATA_N); - I915_WRITE(PIPEB_GMCH_DATA_N, dev_priv->savePIPEB_GMCH_DATA_N); - I915_WRITE(PIPEA_DP_LINK_M, dev_priv->savePIPEA_DP_LINK_M); - I915_WRITE(PIPEB_DP_LINK_M, dev_priv->savePIPEB_DP_LINK_M); - I915_WRITE(PIPEA_DP_LINK_N, dev_priv->savePIPEA_DP_LINK_N); - I915_WRITE(PIPEB_DP_LINK_N, dev_priv->savePIPEB_DP_LINK_N); - } - /* This is only meaningful in non-KMS mode */ - /* Don't restore them in KMS mode */ - i915_restore_modeset_reg(dev); - /* Cursor state */ - I915_WRITE(CURAPOS, dev_priv->saveCURAPOS); - I915_WRITE(CURACNTR, dev_priv->saveCURACNTR); - I915_WRITE(CURABASE, dev_priv->saveCURABASE); - I915_WRITE(CURBPOS, dev_priv->saveCURBPOS); - I915_WRITE(CURBCNTR, dev_priv->saveCURBCNTR); - I915_WRITE(CURBBASE, dev_priv->saveCURBBASE); - if (!IS_I9XX(dev)) - I915_WRITE(CURSIZE, dev_priv->saveCURSIZE); - /* CRT state */ - I915_WRITE(ADPA, dev_priv->saveADPA); + i915_restore_display(dev); - /* LVDS state */ - if (IS_I965G(dev)) - I915_WRITE(BLC_PWM_CTL2, dev_priv->saveBLC_PWM_CTL2); - if (IS_MOBILE(dev) && !IS_I830(dev)) - I915_WRITE(LVDS, dev_priv->saveLVDS); - if (!IS_I830(dev) && !IS_845G(dev)) - I915_WRITE(PFIT_CONTROL, dev_priv->savePFIT_CONTROL); - - I915_WRITE(PFIT_PGM_RATIOS, dev_priv->savePFIT_PGM_RATIOS); - I915_WRITE(BLC_PWM_CTL, dev_priv->saveBLC_PWM_CTL); - I915_WRITE(PP_ON_DELAYS, dev_priv->savePP_ON_DELAYS); - I915_WRITE(PP_OFF_DELAYS, dev_priv->savePP_OFF_DELAYS); - I915_WRITE(PP_DIVISOR, dev_priv->savePP_DIVISOR); - I915_WRITE(PP_CONTROL, dev_priv->savePP_CONTROL); - - /* Display Port state */ - if (SUPPORTS_INTEGRATED_DP(dev)) { - I915_WRITE(DP_B, dev_priv->saveDP_B); - I915_WRITE(DP_C, dev_priv->saveDP_C); - I915_WRITE(DP_D, dev_priv->saveDP_D); + /* Interrupt state */ + if (IS_IGDNG(dev)) { + I915_WRITE(DEIER, dev_priv->saveDEIER); + I915_WRITE(DEIMR, dev_priv->saveDEIMR); + I915_WRITE(GTIER, dev_priv->saveGTIER); + I915_WRITE(GTIMR, dev_priv->saveGTIMR); + I915_WRITE(FDI_RXA_IMR, dev_priv->saveFDI_RXA_IMR); + I915_WRITE(FDI_RXB_IMR, dev_priv->saveFDI_RXB_IMR); + } else { + I915_WRITE (IER, dev_priv->saveIER); + I915_WRITE (IMR, dev_priv->saveIMR); } - /* FIXME: restore TV & SDVO state */ - - /* FBC info */ - I915_WRITE(FBC_CFB_BASE, dev_priv->saveFBC_CFB_BASE); - I915_WRITE(FBC_LL_BASE, dev_priv->saveFBC_LL_BASE); - I915_WRITE(FBC_CONTROL2, dev_priv->saveFBC_CONTROL2); - I915_WRITE(FBC_CONTROL, dev_priv->saveFBC_CONTROL); - - /* VGA state */ - I915_WRITE(VGACNTRL, dev_priv->saveVGACNTRL); - I915_WRITE(VGA0, dev_priv->saveVGA0); - I915_WRITE(VGA1, dev_priv->saveVGA1); - I915_WRITE(VGA_PD, dev_priv->saveVGA_PD); - DRM_UDELAY(150); /* Clock gating state */ I915_WRITE (D_STATE, dev_priv->saveD_STATE); @@ -603,8 +846,6 @@ int i915_restore_state(struct drm_device *dev) for (i = 0; i < 3; i++) I915_WRITE(SWF30 + (i << 2), dev_priv->saveSWF2[i]); - i915_restore_vga(dev); - return 0; } diff --git a/drivers/gpu/drm/i915/i915_trace.h b/drivers/gpu/drm/i915/i915_trace.h new file mode 100644 index 000000000000..01840d9bc38f --- /dev/null +++ b/drivers/gpu/drm/i915/i915_trace.h @@ -0,0 +1,316 @@ +#if !defined(_I915_TRACE_H_) || defined(TRACE_HEADER_MULTI_READ) +#define _I915_TRACE_H_ + +#include <linux/stringify.h> +#include <linux/types.h> +#include <linux/tracepoint.h> + +#include <drm/drmP.h> + +#undef TRACE_SYSTEM +#define TRACE_SYSTEM i915 +#define TRACE_SYSTEM_STRING __stringify(TRACE_SYSTEM) +#define TRACE_INCLUDE_FILE i915_trace + +/* object tracking */ + +TRACE_EVENT(i915_gem_object_create, + + TP_PROTO(struct drm_gem_object *obj), + + TP_ARGS(obj), + + TP_STRUCT__entry( + __field(struct drm_gem_object *, obj) + __field(u32, size) + ), + + TP_fast_assign( + __entry->obj = obj; + __entry->size = obj->size; + ), + + TP_printk("obj=%p, size=%u", __entry->obj, __entry->size) +); + +TRACE_EVENT(i915_gem_object_bind, + + TP_PROTO(struct drm_gem_object *obj, u32 gtt_offset), + + TP_ARGS(obj, gtt_offset), + + TP_STRUCT__entry( + __field(struct drm_gem_object *, obj) + __field(u32, gtt_offset) + ), + + TP_fast_assign( + __entry->obj = obj; + __entry->gtt_offset = gtt_offset; + ), + + TP_printk("obj=%p, gtt_offset=%08x", + __entry->obj, __entry->gtt_offset) +); + +TRACE_EVENT(i915_gem_object_clflush, + + TP_PROTO(struct drm_gem_object *obj), + + TP_ARGS(obj), + + TP_STRUCT__entry( + __field(struct drm_gem_object *, obj) + ), + + TP_fast_assign( + __entry->obj = obj; + ), + + TP_printk("obj=%p", __entry->obj) +); + +TRACE_EVENT(i915_gem_object_change_domain, + + TP_PROTO(struct drm_gem_object *obj, uint32_t old_read_domains, uint32_t old_write_domain), + + TP_ARGS(obj, old_read_domains, old_write_domain), + + TP_STRUCT__entry( + __field(struct drm_gem_object *, obj) + __field(u32, read_domains) + __field(u32, write_domain) + ), + + TP_fast_assign( + __entry->obj = obj; + __entry->read_domains = obj->read_domains | (old_read_domains << 16); + __entry->write_domain = obj->write_domain | (old_write_domain << 16); + ), + + TP_printk("obj=%p, read=%04x, write=%04x", + __entry->obj, + __entry->read_domains, __entry->write_domain) +); + +TRACE_EVENT(i915_gem_object_get_fence, + + TP_PROTO(struct drm_gem_object *obj, int fence, int tiling_mode), + + TP_ARGS(obj, fence, tiling_mode), + + TP_STRUCT__entry( + __field(struct drm_gem_object *, obj) + __field(int, fence) + __field(int, tiling_mode) + ), + + TP_fast_assign( + __entry->obj = obj; + __entry->fence = fence; + __entry->tiling_mode = tiling_mode; + ), + + TP_printk("obj=%p, fence=%d, tiling=%d", + __entry->obj, __entry->fence, __entry->tiling_mode) +); + +TRACE_EVENT(i915_gem_object_unbind, + + TP_PROTO(struct drm_gem_object *obj), + + TP_ARGS(obj), + + TP_STRUCT__entry( + __field(struct drm_gem_object *, obj) + ), + + TP_fast_assign( + __entry->obj = obj; + ), + + TP_printk("obj=%p", __entry->obj) +); + +TRACE_EVENT(i915_gem_object_destroy, + + TP_PROTO(struct drm_gem_object *obj), + + TP_ARGS(obj), + + TP_STRUCT__entry( + __field(struct drm_gem_object *, obj) + ), + + TP_fast_assign( + __entry->obj = obj; + ), + + TP_printk("obj=%p", __entry->obj) +); + +/* batch tracing */ + +TRACE_EVENT(i915_gem_request_submit, + + TP_PROTO(struct drm_device *dev, u32 seqno), + + TP_ARGS(dev, seqno), + + TP_STRUCT__entry( + __field(u32, dev) + __field(u32, seqno) + ), + + TP_fast_assign( + __entry->dev = dev->primary->index; + __entry->seqno = seqno; + i915_trace_irq_get(dev, seqno); + ), + + TP_printk("dev=%u, seqno=%u", __entry->dev, __entry->seqno) +); + +TRACE_EVENT(i915_gem_request_flush, + + TP_PROTO(struct drm_device *dev, u32 seqno, + u32 flush_domains, u32 invalidate_domains), + + TP_ARGS(dev, seqno, flush_domains, invalidate_domains), + + TP_STRUCT__entry( + __field(u32, dev) + __field(u32, seqno) + __field(u32, flush_domains) + __field(u32, invalidate_domains) + ), + + TP_fast_assign( + __entry->dev = dev->primary->index; + __entry->seqno = seqno; + __entry->flush_domains = flush_domains; + __entry->invalidate_domains = invalidate_domains; + ), + + TP_printk("dev=%u, seqno=%u, flush=%04x, invalidate=%04x", + __entry->dev, __entry->seqno, + __entry->flush_domains, __entry->invalidate_domains) +); + + +TRACE_EVENT(i915_gem_request_complete, + + TP_PROTO(struct drm_device *dev, u32 seqno), + + TP_ARGS(dev, seqno), + + TP_STRUCT__entry( + __field(u32, dev) + __field(u32, seqno) + ), + + TP_fast_assign( + __entry->dev = dev->primary->index; + __entry->seqno = seqno; + ), + + TP_printk("dev=%u, seqno=%u", __entry->dev, __entry->seqno) +); + +TRACE_EVENT(i915_gem_request_retire, + + TP_PROTO(struct drm_device *dev, u32 seqno), + + TP_ARGS(dev, seqno), + + TP_STRUCT__entry( + __field(u32, dev) + __field(u32, seqno) + ), + + TP_fast_assign( + __entry->dev = dev->primary->index; + __entry->seqno = seqno; + ), + + TP_printk("dev=%u, seqno=%u", __entry->dev, __entry->seqno) +); + +TRACE_EVENT(i915_gem_request_wait_begin, + + TP_PROTO(struct drm_device *dev, u32 seqno), + + TP_ARGS(dev, seqno), + + TP_STRUCT__entry( + __field(u32, dev) + __field(u32, seqno) + ), + + TP_fast_assign( + __entry->dev = dev->primary->index; + __entry->seqno = seqno; + ), + + TP_printk("dev=%u, seqno=%u", __entry->dev, __entry->seqno) +); + +TRACE_EVENT(i915_gem_request_wait_end, + + TP_PROTO(struct drm_device *dev, u32 seqno), + + TP_ARGS(dev, seqno), + + TP_STRUCT__entry( + __field(u32, dev) + __field(u32, seqno) + ), + + TP_fast_assign( + __entry->dev = dev->primary->index; + __entry->seqno = seqno; + ), + + TP_printk("dev=%u, seqno=%u", __entry->dev, __entry->seqno) +); + +TRACE_EVENT(i915_ring_wait_begin, + + TP_PROTO(struct drm_device *dev), + + TP_ARGS(dev), + + TP_STRUCT__entry( + __field(u32, dev) + ), + + TP_fast_assign( + __entry->dev = dev->primary->index; + ), + + TP_printk("dev=%u", __entry->dev) +); + +TRACE_EVENT(i915_ring_wait_end, + + TP_PROTO(struct drm_device *dev), + + TP_ARGS(dev), + + TP_STRUCT__entry( + __field(u32, dev) + ), + + TP_fast_assign( + __entry->dev = dev->primary->index; + ), + + TP_printk("dev=%u", __entry->dev) +); + +#endif /* _I915_TRACE_H_ */ + +/* This part must be outside protection */ +#undef TRACE_INCLUDE_PATH +#define TRACE_INCLUDE_PATH ../../drivers/gpu/drm/i915 +#include <trace/define_trace.h> diff --git a/drivers/gpu/drm/i915/i915_trace_points.c b/drivers/gpu/drm/i915/i915_trace_points.c new file mode 100644 index 000000000000..ead876eb6ea0 --- /dev/null +++ b/drivers/gpu/drm/i915/i915_trace_points.c @@ -0,0 +1,11 @@ +/* + * Copyright © 2009 Intel Corporation + * + * Authors: + * Chris Wilson <chris@chris-wilson.co.uk> + */ + +#include "i915_drv.h" + +#define CREATE_TRACE_POINTS +#include "i915_trace.h" diff --git a/drivers/gpu/drm/i915/intel_bios.c b/drivers/gpu/drm/i915/intel_bios.c index 1e28c1652fd0..96cd256e60e6 100644 --- a/drivers/gpu/drm/i915/intel_bios.c +++ b/drivers/gpu/drm/i915/intel_bios.c @@ -217,6 +217,9 @@ parse_general_features(struct drm_i915_private *dev_priv, if (IS_I85X(dev_priv->dev)) dev_priv->lvds_ssc_freq = general->ssc_freq ? 66 : 48; + else if (IS_IGDNG(dev_priv->dev)) + dev_priv->lvds_ssc_freq = + general->ssc_freq ? 100 : 120; else dev_priv->lvds_ssc_freq = general->ssc_freq ? 100 : 96; @@ -348,20 +351,18 @@ parse_driver_features(struct drm_i915_private *dev_priv, struct drm_device *dev = dev_priv->dev; struct bdb_driver_features *driver; - /* set default for chips without eDP */ - if (!SUPPORTS_EDP(dev)) { - dev_priv->edp_support = 0; - return; - } - driver = find_section(bdb, BDB_DRIVER_FEATURES); if (!driver) return; - if (driver->lvds_config == BDB_DRIVER_FEATURE_EDP) + if (driver && SUPPORTS_EDP(dev) && + driver->lvds_config == BDB_DRIVER_FEATURE_EDP) { dev_priv->edp_support = 1; + } else { + dev_priv->edp_support = 0; + } - if (driver->dual_frequency) + if (driver && driver->dual_frequency) dev_priv->render_reclock_avail = true; } diff --git a/drivers/gpu/drm/i915/intel_crt.c b/drivers/gpu/drm/i915/intel_crt.c index 88814fa2dfd2..e5051446c48e 100644 --- a/drivers/gpu/drm/i915/intel_crt.c +++ b/drivers/gpu/drm/i915/intel_crt.c @@ -179,13 +179,10 @@ static bool intel_igdng_crt_detect_hotplug(struct drm_connector *connector) { struct drm_device *dev = connector->dev; struct drm_i915_private *dev_priv = dev->dev_private; - u32 adpa, temp; + u32 adpa; bool ret; - temp = adpa = I915_READ(PCH_ADPA); - - adpa &= ~ADPA_DAC_ENABLE; - I915_WRITE(PCH_ADPA, adpa); + adpa = I915_READ(PCH_ADPA); adpa &= ~ADPA_CRT_HOTPLUG_MASK; @@ -212,8 +209,6 @@ static bool intel_igdng_crt_detect_hotplug(struct drm_connector *connector) else ret = false; - /* restore origin register */ - I915_WRITE(PCH_ADPA, temp); return ret; } @@ -267,8 +262,8 @@ static bool intel_crt_detect_hotplug(struct drm_connector *connector) } while (time_after(timeout, jiffies)); } - if ((I915_READ(PORT_HOTPLUG_STAT) & CRT_HOTPLUG_MONITOR_MASK) == - CRT_HOTPLUG_MONITOR_COLOR) + if ((I915_READ(PORT_HOTPLUG_STAT) & CRT_HOTPLUG_MONITOR_MASK) != + CRT_HOTPLUG_MONITOR_NONE) return true; return false; diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 0227b1652906..099f420de57a 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -24,6 +24,8 @@ * Eric Anholt <eric@anholt.net> */ +#include <linux/module.h> +#include <linux/input.h> #include <linux/i2c.h> #include <linux/kernel.h> #include "drmP.h" @@ -861,10 +863,8 @@ intel_igdng_find_best_PLL(const intel_limit_t *limit, struct drm_crtc *crtc, struct drm_device *dev = crtc->dev; struct drm_i915_private *dev_priv = dev->dev_private; intel_clock_t clock; - int max_n; - bool found; int err_most = 47; - found = false; + int err_min = 10000; /* eDP has only 2 clock choice, no n/m/p setting */ if (HAS_eDP) @@ -875,7 +875,7 @@ intel_igdng_find_best_PLL(const intel_limit_t *limit, struct drm_crtc *crtc, refclk, best_clock); if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS)) { - if ((I915_READ(LVDS) & LVDS_CLKB_POWER_MASK) == + if ((I915_READ(PCH_LVDS) & LVDS_CLKB_POWER_MASK) == LVDS_CLKB_POWER_UP) clock.p2 = limit->p2.p2_fast; else @@ -888,10 +888,9 @@ intel_igdng_find_best_PLL(const intel_limit_t *limit, struct drm_crtc *crtc, } memset(best_clock, 0, sizeof(*best_clock)); - max_n = limit->n.max; for (clock.p1 = limit->p1.max; clock.p1 >= limit->p1.min; clock.p1--) { /* based on hardware requriment prefer smaller n to precision */ - for (clock.n = limit->n.min; clock.n <= max_n; clock.n++) { + for (clock.n = limit->n.min; clock.n <= limit->n.max; clock.n++) { /* based on hardware requirment prefere larger m1,m2 */ for (clock.m1 = limit->m1.max; clock.m1 >= limit->m1.min; clock.m1--) { @@ -905,18 +904,18 @@ intel_igdng_find_best_PLL(const intel_limit_t *limit, struct drm_crtc *crtc, this_err = abs((10000 - (target*10000/clock.dot))); if (this_err < err_most) { *best_clock = clock; - err_most = this_err; - max_n = clock.n; - found = true; /* found on first matching */ goto out; + } else if (this_err < err_min) { + *best_clock = clock; + err_min = this_err; } } } } } out: - return found; + return true; } /* DisplayPort has only two frequencies, 162MHz and 270MHz */ @@ -941,6 +940,7 @@ intel_find_pll_g4x_dp(const intel_limit_t *limit, struct drm_crtc *crtc, clock.m = 5 * (clock.m1 + 2) + (clock.m2 + 2); clock.p = (clock.p1 * clock.p2); clock.dot = 96000 * clock.m / (clock.n + 2) / clock.p; + clock.vco = 0; memcpy(best_clock, &clock, sizeof(intel_clock_t)); return true; } @@ -952,6 +952,241 @@ intel_wait_for_vblank(struct drm_device *dev) mdelay(20); } +/* Parameters have changed, update FBC info */ +static void i8xx_enable_fbc(struct drm_crtc *crtc, unsigned long interval) +{ + struct drm_device *dev = crtc->dev; + struct drm_i915_private *dev_priv = dev->dev_private; + struct drm_framebuffer *fb = crtc->fb; + struct intel_framebuffer *intel_fb = to_intel_framebuffer(fb); + struct drm_i915_gem_object *obj_priv = intel_fb->obj->driver_private; + struct intel_crtc *intel_crtc = to_intel_crtc(crtc); + int plane, i; + u32 fbc_ctl, fbc_ctl2; + + dev_priv->cfb_pitch = dev_priv->cfb_size / FBC_LL_SIZE; + + if (fb->pitch < dev_priv->cfb_pitch) + dev_priv->cfb_pitch = fb->pitch; + + /* FBC_CTL wants 64B units */ + dev_priv->cfb_pitch = (dev_priv->cfb_pitch / 64) - 1; + dev_priv->cfb_fence = obj_priv->fence_reg; + dev_priv->cfb_plane = intel_crtc->plane; + plane = dev_priv->cfb_plane == 0 ? FBC_CTL_PLANEA : FBC_CTL_PLANEB; + + /* Clear old tags */ + for (i = 0; i < (FBC_LL_SIZE / 32) + 1; i++) + I915_WRITE(FBC_TAG + (i * 4), 0); + + /* Set it up... */ + fbc_ctl2 = FBC_CTL_FENCE_DBL | FBC_CTL_IDLE_IMM | plane; + if (obj_priv->tiling_mode != I915_TILING_NONE) + fbc_ctl2 |= FBC_CTL_CPU_FENCE; + I915_WRITE(FBC_CONTROL2, fbc_ctl2); + I915_WRITE(FBC_FENCE_OFF, crtc->y); + + /* enable it... */ + fbc_ctl = FBC_CTL_EN | FBC_CTL_PERIODIC; + fbc_ctl |= (dev_priv->cfb_pitch & 0xff) << FBC_CTL_STRIDE_SHIFT; + fbc_ctl |= (interval & 0x2fff) << FBC_CTL_INTERVAL_SHIFT; + if (obj_priv->tiling_mode != I915_TILING_NONE) + fbc_ctl |= dev_priv->cfb_fence; + I915_WRITE(FBC_CONTROL, fbc_ctl); + + DRM_DEBUG("enabled FBC, pitch %ld, yoff %d, plane %d, ", + dev_priv->cfb_pitch, crtc->y, dev_priv->cfb_plane); +} + +void i8xx_disable_fbc(struct drm_device *dev) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + u32 fbc_ctl; + + if (!I915_HAS_FBC(dev)) + return; + + /* Disable compression */ + fbc_ctl = I915_READ(FBC_CONTROL); + fbc_ctl &= ~FBC_CTL_EN; + I915_WRITE(FBC_CONTROL, fbc_ctl); + + /* Wait for compressing bit to clear */ + while (I915_READ(FBC_STATUS) & FBC_STAT_COMPRESSING) + ; /* nothing */ + + intel_wait_for_vblank(dev); + + DRM_DEBUG("disabled FBC\n"); +} + +static bool i8xx_fbc_enabled(struct drm_crtc *crtc) +{ + struct drm_device *dev = crtc->dev; + struct drm_i915_private *dev_priv = dev->dev_private; + + return I915_READ(FBC_CONTROL) & FBC_CTL_EN; +} + +static void g4x_enable_fbc(struct drm_crtc *crtc, unsigned long interval) +{ + struct drm_device *dev = crtc->dev; + struct drm_i915_private *dev_priv = dev->dev_private; + struct drm_framebuffer *fb = crtc->fb; + struct intel_framebuffer *intel_fb = to_intel_framebuffer(fb); + struct drm_i915_gem_object *obj_priv = intel_fb->obj->driver_private; + struct intel_crtc *intel_crtc = to_intel_crtc(crtc); + int plane = (intel_crtc->plane == 0 ? DPFC_CTL_PLANEA : + DPFC_CTL_PLANEB); + unsigned long stall_watermark = 200; + u32 dpfc_ctl; + + dev_priv->cfb_pitch = (dev_priv->cfb_pitch / 64) - 1; + dev_priv->cfb_fence = obj_priv->fence_reg; + dev_priv->cfb_plane = intel_crtc->plane; + + dpfc_ctl = plane | DPFC_SR_EN | DPFC_CTL_LIMIT_1X; + if (obj_priv->tiling_mode != I915_TILING_NONE) { + dpfc_ctl |= DPFC_CTL_FENCE_EN | dev_priv->cfb_fence; + I915_WRITE(DPFC_CHICKEN, DPFC_HT_MODIFY); + } else { + I915_WRITE(DPFC_CHICKEN, ~DPFC_HT_MODIFY); + } + + I915_WRITE(DPFC_CONTROL, dpfc_ctl); + I915_WRITE(DPFC_RECOMP_CTL, DPFC_RECOMP_STALL_EN | + (stall_watermark << DPFC_RECOMP_STALL_WM_SHIFT) | + (interval << DPFC_RECOMP_TIMER_COUNT_SHIFT)); + I915_WRITE(DPFC_FENCE_YOFF, crtc->y); + + /* enable it... */ + I915_WRITE(DPFC_CONTROL, I915_READ(DPFC_CONTROL) | DPFC_CTL_EN); + + DRM_DEBUG("enabled fbc on plane %d\n", intel_crtc->plane); +} + +void g4x_disable_fbc(struct drm_device *dev) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + u32 dpfc_ctl; + + /* Disable compression */ + dpfc_ctl = I915_READ(DPFC_CONTROL); + dpfc_ctl &= ~DPFC_CTL_EN; + I915_WRITE(DPFC_CONTROL, dpfc_ctl); + intel_wait_for_vblank(dev); + + DRM_DEBUG("disabled FBC\n"); +} + +static bool g4x_fbc_enabled(struct drm_crtc *crtc) +{ + struct drm_device *dev = crtc->dev; + struct drm_i915_private *dev_priv = dev->dev_private; + + return I915_READ(DPFC_CONTROL) & DPFC_CTL_EN; +} + +/** + * intel_update_fbc - enable/disable FBC as needed + * @crtc: CRTC to point the compressor at + * @mode: mode in use + * + * Set up the framebuffer compression hardware at mode set time. We + * enable it if possible: + * - plane A only (on pre-965) + * - no pixel mulitply/line duplication + * - no alpha buffer discard + * - no dual wide + * - framebuffer <= 2048 in width, 1536 in height + * + * We can't assume that any compression will take place (worst case), + * so the compressed buffer has to be the same size as the uncompressed + * one. It also must reside (along with the line length buffer) in + * stolen memory. + * + * We need to enable/disable FBC on a global basis. + */ +static void intel_update_fbc(struct drm_crtc *crtc, + struct drm_display_mode *mode) +{ + struct drm_device *dev = crtc->dev; + struct drm_i915_private *dev_priv = dev->dev_private; + struct drm_framebuffer *fb = crtc->fb; + struct intel_framebuffer *intel_fb; + struct drm_i915_gem_object *obj_priv; + struct intel_crtc *intel_crtc = to_intel_crtc(crtc); + int plane = intel_crtc->plane; + + if (!i915_powersave) + return; + + if (!dev_priv->display.fbc_enabled || + !dev_priv->display.enable_fbc || + !dev_priv->display.disable_fbc) + return; + + if (!crtc->fb) + return; + + intel_fb = to_intel_framebuffer(fb); + obj_priv = intel_fb->obj->driver_private; + + /* + * If FBC is already on, we just have to verify that we can + * keep it that way... + * Need to disable if: + * - changing FBC params (stride, fence, mode) + * - new fb is too large to fit in compressed buffer + * - going to an unsupported config (interlace, pixel multiply, etc.) + */ + if (intel_fb->obj->size > dev_priv->cfb_size) { + DRM_DEBUG("framebuffer too large, disabling compression\n"); + goto out_disable; + } + if ((mode->flags & DRM_MODE_FLAG_INTERLACE) || + (mode->flags & DRM_MODE_FLAG_DBLSCAN)) { + DRM_DEBUG("mode incompatible with compression, disabling\n"); + goto out_disable; + } + if ((mode->hdisplay > 2048) || + (mode->vdisplay > 1536)) { + DRM_DEBUG("mode too large for compression, disabling\n"); + goto out_disable; + } + if ((IS_I915GM(dev) || IS_I945GM(dev)) && plane != 0) { + DRM_DEBUG("plane not 0, disabling compression\n"); + goto out_disable; + } + if (obj_priv->tiling_mode != I915_TILING_X) { + DRM_DEBUG("framebuffer not tiled, disabling compression\n"); + goto out_disable; + } + + if (dev_priv->display.fbc_enabled(crtc)) { + /* We can re-enable it in this case, but need to update pitch */ + if (fb->pitch > dev_priv->cfb_pitch) + dev_priv->display.disable_fbc(dev); + if (obj_priv->fence_reg != dev_priv->cfb_fence) + dev_priv->display.disable_fbc(dev); + if (plane != dev_priv->cfb_plane) + dev_priv->display.disable_fbc(dev); + } + + if (!dev_priv->display.fbc_enabled(crtc)) { + /* Now try to turn it back on if possible */ + dev_priv->display.enable_fbc(crtc, 500); + } + + return; + +out_disable: + DRM_DEBUG("unsupported config, disabling FBC\n"); + /* Multiple disables should be harmless */ + if (dev_priv->display.fbc_enabled(crtc)) + dev_priv->display.disable_fbc(dev); +} + static int intel_pipe_set_base(struct drm_crtc *crtc, int x, int y, struct drm_framebuffer *old_fb) @@ -964,12 +1199,13 @@ intel_pipe_set_base(struct drm_crtc *crtc, int x, int y, struct drm_i915_gem_object *obj_priv; struct drm_gem_object *obj; int pipe = intel_crtc->pipe; + int plane = intel_crtc->plane; unsigned long Start, Offset; - int dspbase = (pipe == 0 ? DSPAADDR : DSPBADDR); - int dspsurf = (pipe == 0 ? DSPASURF : DSPBSURF); - int dspstride = (pipe == 0) ? DSPASTRIDE : DSPBSTRIDE; - int dsptileoff = (pipe == 0 ? DSPATILEOFF : DSPBTILEOFF); - int dspcntr_reg = (pipe == 0) ? DSPACNTR : DSPBCNTR; + int dspbase = (plane == 0 ? DSPAADDR : DSPBADDR); + int dspsurf = (plane == 0 ? DSPASURF : DSPBSURF); + int dspstride = (plane == 0) ? DSPASTRIDE : DSPBSTRIDE; + int dsptileoff = (plane == 0 ? DSPATILEOFF : DSPBTILEOFF); + int dspcntr_reg = (plane == 0) ? DSPACNTR : DSPBCNTR; u32 dspcntr, alignment; int ret; @@ -979,12 +1215,12 @@ intel_pipe_set_base(struct drm_crtc *crtc, int x, int y, return 0; } - switch (pipe) { + switch (plane) { case 0: case 1: break; default: - DRM_ERROR("Can't update pipe %d in SAREA\n", pipe); + DRM_ERROR("Can't update plane %d in SAREA\n", plane); return -EINVAL; } @@ -1022,9 +1258,11 @@ intel_pipe_set_base(struct drm_crtc *crtc, int x, int y, return ret; } - /* Pre-i965 needs to install a fence for tiled scan-out */ - if (!IS_I965G(dev) && - obj_priv->fence_reg == I915_FENCE_REG_NONE && + /* Install a fence for tiled scan-out. Pre-i965 always needs a fence, + * whereas 965+ only requires a fence if using framebuffer compression. + * For simplicity, we always install a fence as the cost is not that onerous. + */ + if (obj_priv->fence_reg == I915_FENCE_REG_NONE && obj_priv->tiling_mode != I915_TILING_NONE) { ret = i915_gem_object_get_fence_reg(obj); if (ret != 0) { @@ -1086,6 +1324,9 @@ intel_pipe_set_base(struct drm_crtc *crtc, int x, int y, I915_READ(dspbase); } + if ((IS_I965G(dev) || plane == 0)) + intel_update_fbc(crtc, &crtc->mode); + intel_wait_for_vblank(dev); if (old_fb) { @@ -1217,6 +1458,7 @@ static void igdng_crtc_dpms(struct drm_crtc *crtc, int mode) int transconf_reg = (pipe == 0) ? TRANSACONF : TRANSBCONF; int pf_ctl_reg = (pipe == 0) ? PFA_CTL_1 : PFB_CTL_1; int pf_win_size = (pipe == 0) ? PFA_WIN_SZ : PFB_WIN_SZ; + int pf_win_pos = (pipe == 0) ? PFA_WIN_POS : PFB_WIN_POS; int cpu_htot_reg = (pipe == 0) ? HTOTAL_A : HTOTAL_B; int cpu_hblank_reg = (pipe == 0) ? HBLANK_A : HBLANK_B; int cpu_hsync_reg = (pipe == 0) ? HSYNC_A : HSYNC_B; @@ -1268,6 +1510,19 @@ static void igdng_crtc_dpms(struct drm_crtc *crtc, int mode) } } + /* Enable panel fitting for LVDS */ + if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS)) { + temp = I915_READ(pf_ctl_reg); + I915_WRITE(pf_ctl_reg, temp | PF_ENABLE | PF_FILTER_MED_3x3); + + /* currently full aspect */ + I915_WRITE(pf_win_pos, 0); + + I915_WRITE(pf_win_size, + (dev_priv->panel_fixed_mode->hdisplay << 16) | + (dev_priv->panel_fixed_mode->vdisplay)); + } + /* Enable CPU pipe */ temp = I915_READ(pipeconf_reg); if ((temp & PIPEACONF_ENABLE) == 0) { @@ -1532,9 +1787,10 @@ static void i9xx_crtc_dpms(struct drm_crtc *crtc, int mode) struct drm_i915_private *dev_priv = dev->dev_private; struct intel_crtc *intel_crtc = to_intel_crtc(crtc); int pipe = intel_crtc->pipe; + int plane = intel_crtc->plane; int dpll_reg = (pipe == 0) ? DPLL_A : DPLL_B; - int dspcntr_reg = (pipe == 0) ? DSPACNTR : DSPBCNTR; - int dspbase_reg = (pipe == 0) ? DSPAADDR : DSPBADDR; + int dspcntr_reg = (plane == 0) ? DSPACNTR : DSPBCNTR; + int dspbase_reg = (plane == 0) ? DSPAADDR : DSPBADDR; int pipeconf_reg = (pipe == 0) ? PIPEACONF : PIPEBCONF; u32 temp; @@ -1545,6 +1801,8 @@ static void i9xx_crtc_dpms(struct drm_crtc *crtc, int mode) case DRM_MODE_DPMS_ON: case DRM_MODE_DPMS_STANDBY: case DRM_MODE_DPMS_SUSPEND: + intel_update_watermarks(dev); + /* Enable the DPLL */ temp = I915_READ(dpll_reg); if ((temp & DPLL_VCO_ENABLE) == 0) { @@ -1577,15 +1835,21 @@ static void i9xx_crtc_dpms(struct drm_crtc *crtc, int mode) intel_crtc_load_lut(crtc); + if ((IS_I965G(dev) || plane == 0)) + intel_update_fbc(crtc, &crtc->mode); + /* Give the overlay scaler a chance to enable if it's on this pipe */ //intel_crtc_dpms_video(crtc, true); TODO - intel_update_watermarks(dev); break; case DRM_MODE_DPMS_OFF: intel_update_watermarks(dev); /* Give the overlay scaler a chance to disable if it's on this pipe */ //intel_crtc_dpms_video(crtc, FALSE); TODO + if (dev_priv->cfb_plane == plane && + dev_priv->display.disable_fbc) + dev_priv->display.disable_fbc(dev); + /* Disable the VGA plane that we never use */ i915_disable_vga(dev); @@ -1634,15 +1898,13 @@ static void i9xx_crtc_dpms(struct drm_crtc *crtc, int mode) static void intel_crtc_dpms(struct drm_crtc *crtc, int mode) { struct drm_device *dev = crtc->dev; + struct drm_i915_private *dev_priv = dev->dev_private; struct drm_i915_master_private *master_priv; struct intel_crtc *intel_crtc = to_intel_crtc(crtc); int pipe = intel_crtc->pipe; bool enabled; - if (IS_IGDNG(dev)) - igdng_crtc_dpms(crtc, mode); - else - i9xx_crtc_dpms(crtc, mode); + dev_priv->display.dpms(crtc, mode); intel_crtc->dpms_mode = mode; @@ -1709,56 +1971,68 @@ static bool intel_crtc_mode_fixup(struct drm_crtc *crtc, return true; } +static int i945_get_display_clock_speed(struct drm_device *dev) +{ + return 400000; +} -/** Returns the core display clock speed for i830 - i945 */ -static int intel_get_core_clock_speed(struct drm_device *dev) +static int i915_get_display_clock_speed(struct drm_device *dev) { + return 333000; +} - /* Core clock values taken from the published datasheets. - * The 830 may go up to 166 Mhz, which we should check. - */ - if (IS_I945G(dev)) - return 400000; - else if (IS_I915G(dev)) - return 333000; - else if (IS_I945GM(dev) || IS_845G(dev) || IS_IGDGM(dev)) - return 200000; - else if (IS_I915GM(dev)) { - u16 gcfgc = 0; +static int i9xx_misc_get_display_clock_speed(struct drm_device *dev) +{ + return 200000; +} - pci_read_config_word(dev->pdev, GCFGC, &gcfgc); +static int i915gm_get_display_clock_speed(struct drm_device *dev) +{ + u16 gcfgc = 0; - if (gcfgc & GC_LOW_FREQUENCY_ENABLE) - return 133000; - else { - switch (gcfgc & GC_DISPLAY_CLOCK_MASK) { - case GC_DISPLAY_CLOCK_333_MHZ: - return 333000; - default: - case GC_DISPLAY_CLOCK_190_200_MHZ: - return 190000; - } - } - } else if (IS_I865G(dev)) - return 266000; - else if (IS_I855(dev)) { - u16 hpllcc = 0; - /* Assume that the hardware is in the high speed state. This - * should be the default. - */ - switch (hpllcc & GC_CLOCK_CONTROL_MASK) { - case GC_CLOCK_133_200: - case GC_CLOCK_100_200: - return 200000; - case GC_CLOCK_166_250: - return 250000; - case GC_CLOCK_100_133: - return 133000; + pci_read_config_word(dev->pdev, GCFGC, &gcfgc); + + if (gcfgc & GC_LOW_FREQUENCY_ENABLE) + return 133000; + else { + switch (gcfgc & GC_DISPLAY_CLOCK_MASK) { + case GC_DISPLAY_CLOCK_333_MHZ: + return 333000; + default: + case GC_DISPLAY_CLOCK_190_200_MHZ: + return 190000; } - } else /* 852, 830 */ + } +} + +static int i865_get_display_clock_speed(struct drm_device *dev) +{ + return 266000; +} + +static int i855_get_display_clock_speed(struct drm_device *dev) +{ + u16 hpllcc = 0; + /* Assume that the hardware is in the high speed state. This + * should be the default. + */ + switch (hpllcc & GC_CLOCK_CONTROL_MASK) { + case GC_CLOCK_133_200: + case GC_CLOCK_100_200: + return 200000; + case GC_CLOCK_166_250: + return 250000; + case GC_CLOCK_100_133: return 133000; + } - return 0; /* Silence gcc warning */ + /* Shouldn't happen */ + return 0; +} + +static int i830_get_display_clock_speed(struct drm_device *dev) +{ + return 133000; } /** @@ -1809,7 +2083,7 @@ fdi_reduce_ratio(u32 *num, u32 *den) #define LINK_N 0x80000 static void -igdng_compute_m_n(int bytes_per_pixel, int nlanes, +igdng_compute_m_n(int bits_per_pixel, int nlanes, int pixel_clock, int link_clock, struct fdi_m_n *m_n) { @@ -1819,7 +2093,8 @@ igdng_compute_m_n(int bytes_per_pixel, int nlanes, temp = (u64) DATA_N * pixel_clock; temp = div_u64(temp, link_clock); - m_n->gmch_m = div_u64(temp * bytes_per_pixel, nlanes); + m_n->gmch_m = div_u64(temp * bits_per_pixel, nlanes); + m_n->gmch_m >>= 3; /* convert to bytes_per_pixel */ m_n->gmch_n = DATA_N; fdi_reduce_ratio(&m_n->gmch_m, &m_n->gmch_n); @@ -1867,6 +2142,13 @@ static struct intel_watermark_params igd_cursor_hplloff_wm = { IGD_CURSOR_GUARD_WM, IGD_FIFO_LINE_SIZE }; +static struct intel_watermark_params g4x_wm_info = { + G4X_FIFO_SIZE, + G4X_MAX_WM, + G4X_MAX_WM, + 2, + G4X_FIFO_LINE_SIZE, +}; static struct intel_watermark_params i945_wm_info = { I945_FIFO_SIZE, I915_MAX_WM, @@ -1921,7 +2203,14 @@ static unsigned long intel_calculate_wm(unsigned long clock_in_khz, { long entries_required, wm_size; - entries_required = (clock_in_khz * pixel_size * latency_ns) / 1000000; + /* + * Note: we need to make sure we don't overflow for various clock & + * latency values. + * clocks go from a few thousand to several hundred thousand. + * latency is usually a few thousand + */ + entries_required = ((clock_in_khz / 1000) * pixel_size * latency_ns) / + 1000; entries_required /= wm->cacheline_size; DRM_DEBUG("FIFO entries required for mode: %d\n", entries_required); @@ -1986,14 +2275,13 @@ static struct cxsr_latency *intel_get_cxsr_latency(int is_desktop, int fsb, for (i = 0; i < ARRAY_SIZE(cxsr_latency_table); i++) { latency = &cxsr_latency_table[i]; if (is_desktop == latency->is_desktop && - fsb == latency->fsb_freq && mem == latency->mem_freq) - break; + fsb == latency->fsb_freq && mem == latency->mem_freq) + return latency; } - if (i >= ARRAY_SIZE(cxsr_latency_table)) { - DRM_DEBUG("Unknown FSB/MEM found, disable CxSR\n"); - return NULL; - } - return latency; + + DRM_DEBUG("Unknown FSB/MEM found, disable CxSR\n"); + + return NULL; } static void igd_disable_cxsr(struct drm_device *dev) @@ -2084,32 +2372,17 @@ static void igd_enable_cxsr(struct drm_device *dev, unsigned long clock, */ const static int latency_ns = 5000; -static int intel_get_fifo_size(struct drm_device *dev, int plane) +static int i9xx_get_fifo_size(struct drm_device *dev, int plane) { struct drm_i915_private *dev_priv = dev->dev_private; uint32_t dsparb = I915_READ(DSPARB); int size; - if (IS_I9XX(dev)) { - if (plane == 0) - size = dsparb & 0x7f; - else - size = ((dsparb >> DSPARB_CSTART_SHIFT) & 0x7f) - - (dsparb & 0x7f); - } else if (IS_I85X(dev)) { - if (plane == 0) - size = dsparb & 0x1ff; - else - size = ((dsparb >> DSPARB_BEND_SHIFT) & 0x1ff) - - (dsparb & 0x1ff); - size >>= 1; /* Convert to cachelines */ - } else if (IS_845G(dev)) { + if (plane == 0) size = dsparb & 0x7f; - size >>= 2; /* Convert to cachelines */ - } else { - size = dsparb & 0x7f; - size >>= 1; /* Convert to cachelines */ - } + else + size = ((dsparb >> DSPARB_CSTART_SHIFT) & 0x7f) - + (dsparb & 0x7f); DRM_DEBUG("FIFO size - (0x%08x) %s: %d\n", dsparb, plane ? "B" : "A", size); @@ -2117,19 +2390,127 @@ static int intel_get_fifo_size(struct drm_device *dev, int plane) return size; } -static void g4x_update_wm(struct drm_device *dev) +static int i85x_get_fifo_size(struct drm_device *dev, int plane) { struct drm_i915_private *dev_priv = dev->dev_private; - u32 fw_blc_self = I915_READ(FW_BLC_SELF); + uint32_t dsparb = I915_READ(DSPARB); + int size; - if (i915_powersave) - fw_blc_self |= FW_BLC_SELF_EN; + if (plane == 0) + size = dsparb & 0x1ff; else - fw_blc_self &= ~FW_BLC_SELF_EN; - I915_WRITE(FW_BLC_SELF, fw_blc_self); + size = ((dsparb >> DSPARB_BEND_SHIFT) & 0x1ff) - + (dsparb & 0x1ff); + size >>= 1; /* Convert to cachelines */ + + DRM_DEBUG("FIFO size - (0x%08x) %s: %d\n", dsparb, plane ? "B" : "A", + size); + + return size; +} + +static int i845_get_fifo_size(struct drm_device *dev, int plane) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + uint32_t dsparb = I915_READ(DSPARB); + int size; + + size = dsparb & 0x7f; + size >>= 2; /* Convert to cachelines */ + + DRM_DEBUG("FIFO size - (0x%08x) %s: %d\n", dsparb, plane ? "B" : "A", + size); + + return size; +} + +static int i830_get_fifo_size(struct drm_device *dev, int plane) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + uint32_t dsparb = I915_READ(DSPARB); + int size; + + size = dsparb & 0x7f; + size >>= 1; /* Convert to cachelines */ + + DRM_DEBUG("FIFO size - (0x%08x) %s: %d\n", dsparb, plane ? "B" : "A", + size); + + return size; +} + +static void g4x_update_wm(struct drm_device *dev, int planea_clock, + int planeb_clock, int sr_hdisplay, int pixel_size) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + int total_size, cacheline_size; + int planea_wm, planeb_wm, cursora_wm, cursorb_wm, cursor_sr; + struct intel_watermark_params planea_params, planeb_params; + unsigned long line_time_us; + int sr_clock, sr_entries = 0, entries_required; + + /* Create copies of the base settings for each pipe */ + planea_params = planeb_params = g4x_wm_info; + + /* Grab a couple of global values before we overwrite them */ + total_size = planea_params.fifo_size; + cacheline_size = planea_params.cacheline_size; + + /* + * Note: we need to make sure we don't overflow for various clock & + * latency values. + * clocks go from a few thousand to several hundred thousand. + * latency is usually a few thousand + */ + entries_required = ((planea_clock / 1000) * pixel_size * latency_ns) / + 1000; + entries_required /= G4X_FIFO_LINE_SIZE; + planea_wm = entries_required + planea_params.guard_size; + + entries_required = ((planeb_clock / 1000) * pixel_size * latency_ns) / + 1000; + entries_required /= G4X_FIFO_LINE_SIZE; + planeb_wm = entries_required + planeb_params.guard_size; + + cursora_wm = cursorb_wm = 16; + cursor_sr = 32; + + DRM_DEBUG("FIFO watermarks - A: %d, B: %d\n", planea_wm, planeb_wm); + + /* Calc sr entries for one plane configs */ + if (sr_hdisplay && (!planea_clock || !planeb_clock)) { + /* self-refresh has much higher latency */ + const static int sr_latency_ns = 12000; + + sr_clock = planea_clock ? planea_clock : planeb_clock; + line_time_us = ((sr_hdisplay * 1000) / sr_clock); + + /* Use ns/us then divide to preserve precision */ + sr_entries = (((sr_latency_ns / line_time_us) + 1) * + pixel_size * sr_hdisplay) / 1000; + sr_entries = roundup(sr_entries / cacheline_size, 1); + DRM_DEBUG("self-refresh entries: %d\n", sr_entries); + I915_WRITE(FW_BLC_SELF, FW_BLC_SELF_EN); + } + + DRM_DEBUG("Setting FIFO watermarks - A: %d, B: %d, SR %d\n", + planea_wm, planeb_wm, sr_entries); + + planea_wm &= 0x3f; + planeb_wm &= 0x3f; + + I915_WRITE(DSPFW1, (sr_entries << DSPFW_SR_SHIFT) | + (cursorb_wm << DSPFW_CURSORB_SHIFT) | + (planeb_wm << DSPFW_PLANEB_SHIFT) | planea_wm); + I915_WRITE(DSPFW2, (I915_READ(DSPFW2) & DSPFW_CURSORA_MASK) | + (cursora_wm << DSPFW_CURSORA_SHIFT)); + /* HPLL off in SR has some issues on G4x... disable it */ + I915_WRITE(DSPFW3, (I915_READ(DSPFW3) & ~DSPFW_HPLL_SR_EN) | + (cursor_sr << DSPFW_CURSOR_SR_SHIFT)); } -static void i965_update_wm(struct drm_device *dev) +static void i965_update_wm(struct drm_device *dev, int unused, int unused2, + int unused3, int unused4) { struct drm_i915_private *dev_priv = dev->dev_private; @@ -2165,8 +2546,8 @@ static void i9xx_update_wm(struct drm_device *dev, int planea_clock, cacheline_size = planea_params.cacheline_size; /* Update per-plane FIFO sizes */ - planea_params.fifo_size = intel_get_fifo_size(dev, 0); - planeb_params.fifo_size = intel_get_fifo_size(dev, 1); + planea_params.fifo_size = dev_priv->display.get_fifo_size(dev, 0); + planeb_params.fifo_size = dev_priv->display.get_fifo_size(dev, 1); planea_wm = intel_calculate_wm(planea_clock, &planea_params, pixel_size, latency_ns); @@ -2213,14 +2594,14 @@ static void i9xx_update_wm(struct drm_device *dev, int planea_clock, I915_WRITE(FW_BLC2, fwater_hi); } -static void i830_update_wm(struct drm_device *dev, int planea_clock, - int pixel_size) +static void i830_update_wm(struct drm_device *dev, int planea_clock, int unused, + int unused2, int pixel_size) { struct drm_i915_private *dev_priv = dev->dev_private; uint32_t fwater_lo = I915_READ(FW_BLC) & ~0xfff; int planea_wm; - i830_wm_info.fifo_size = intel_get_fifo_size(dev, 0); + i830_wm_info.fifo_size = dev_priv->display.get_fifo_size(dev, 0); planea_wm = intel_calculate_wm(planea_clock, &i830_wm_info, pixel_size, latency_ns); @@ -2264,12 +2645,16 @@ static void i830_update_wm(struct drm_device *dev, int planea_clock, */ static void intel_update_watermarks(struct drm_device *dev) { + struct drm_i915_private *dev_priv = dev->dev_private; struct drm_crtc *crtc; struct intel_crtc *intel_crtc; int sr_hdisplay = 0; unsigned long planea_clock = 0, planeb_clock = 0, sr_clock = 0; int enabled = 0, pixel_size = 0; + if (!dev_priv->display.update_wm) + return; + /* Get the clock config from both planes */ list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { intel_crtc = to_intel_crtc(crtc); @@ -2302,15 +2687,8 @@ static void intel_update_watermarks(struct drm_device *dev) else if (IS_IGD(dev)) igd_disable_cxsr(dev); - if (IS_G4X(dev)) - g4x_update_wm(dev); - else if (IS_I965G(dev)) - i965_update_wm(dev); - else if (IS_I9XX(dev) || IS_MOBILE(dev)) - i9xx_update_wm(dev, planea_clock, planeb_clock, sr_hdisplay, - pixel_size); - else - i830_update_wm(dev, planea_clock, pixel_size); + dev_priv->display.update_wm(dev, planea_clock, planeb_clock, + sr_hdisplay, pixel_size); } static int intel_crtc_mode_set(struct drm_crtc *crtc, @@ -2323,10 +2701,11 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc, struct drm_i915_private *dev_priv = dev->dev_private; struct intel_crtc *intel_crtc = to_intel_crtc(crtc); int pipe = intel_crtc->pipe; + int plane = intel_crtc->plane; int fp_reg = (pipe == 0) ? FPA0 : FPB0; int dpll_reg = (pipe == 0) ? DPLL_A : DPLL_B; int dpll_md_reg = (intel_crtc->pipe == 0) ? DPLL_A_MD : DPLL_B_MD; - int dspcntr_reg = (pipe == 0) ? DSPACNTR : DSPBCNTR; + int dspcntr_reg = (plane == 0) ? DSPACNTR : DSPBCNTR; int pipeconf_reg = (pipe == 0) ? PIPEACONF : PIPEBCONF; int htot_reg = (pipe == 0) ? HTOTAL_A : HTOTAL_B; int hblank_reg = (pipe == 0) ? HBLANK_A : HBLANK_B; @@ -2334,8 +2713,8 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc, int vtot_reg = (pipe == 0) ? VTOTAL_A : VTOTAL_B; int vblank_reg = (pipe == 0) ? VBLANK_A : VBLANK_B; int vsync_reg = (pipe == 0) ? VSYNC_A : VSYNC_B; - int dspsize_reg = (pipe == 0) ? DSPASIZE : DSPBSIZE; - int dsppos_reg = (pipe == 0) ? DSPAPOS : DSPBPOS; + int dspsize_reg = (plane == 0) ? DSPASIZE : DSPBSIZE; + int dsppos_reg = (plane == 0) ? DSPAPOS : DSPBPOS; int pipesrc_reg = (pipe == 0) ? PIPEASRC : PIPEBSRC; int refclk, num_outputs = 0; intel_clock_t clock, reduced_clock; @@ -2453,7 +2832,7 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc, /* FDI link */ if (IS_IGDNG(dev)) { - int lane, link_bw; + int lane, link_bw, bpp; /* eDP doesn't require FDI link, so just set DP M/N according to current link config */ if (is_edp) { @@ -2472,10 +2851,72 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc, lane = 4; link_bw = 270000; } - igdng_compute_m_n(3, lane, target_clock, + + /* determine panel color depth */ + temp = I915_READ(pipeconf_reg); + + switch (temp & PIPE_BPC_MASK) { + case PIPE_8BPC: + bpp = 24; + break; + case PIPE_10BPC: + bpp = 30; + break; + case PIPE_6BPC: + bpp = 18; + break; + case PIPE_12BPC: + bpp = 36; + break; + default: + DRM_ERROR("unknown pipe bpc value\n"); + bpp = 24; + } + + igdng_compute_m_n(bpp, lane, target_clock, link_bw, &m_n); } + /* Ironlake: try to setup display ref clock before DPLL + * enabling. This is only under driver's control after + * PCH B stepping, previous chipset stepping should be + * ignoring this setting. + */ + if (IS_IGDNG(dev)) { + temp = I915_READ(PCH_DREF_CONTROL); + /* Always enable nonspread source */ + temp &= ~DREF_NONSPREAD_SOURCE_MASK; + temp |= DREF_NONSPREAD_SOURCE_ENABLE; + I915_WRITE(PCH_DREF_CONTROL, temp); + POSTING_READ(PCH_DREF_CONTROL); + + temp &= ~DREF_SSC_SOURCE_MASK; + temp |= DREF_SSC_SOURCE_ENABLE; + I915_WRITE(PCH_DREF_CONTROL, temp); + POSTING_READ(PCH_DREF_CONTROL); + + udelay(200); + + if (is_edp) { + if (dev_priv->lvds_use_ssc) { + temp |= DREF_SSC1_ENABLE; + I915_WRITE(PCH_DREF_CONTROL, temp); + POSTING_READ(PCH_DREF_CONTROL); + + udelay(200); + + temp &= ~DREF_CPU_SOURCE_OUTPUT_MASK; + temp |= DREF_CPU_SOURCE_OUTPUT_DOWNSPREAD; + I915_WRITE(PCH_DREF_CONTROL, temp); + POSTING_READ(PCH_DREF_CONTROL); + } else { + temp |= DREF_CPU_SOURCE_OUTPUT_NONSPREAD; + I915_WRITE(PCH_DREF_CONTROL, temp); + POSTING_READ(PCH_DREF_CONTROL); + } + } + } + if (IS_IGD(dev)) { fp = (1 << clock.n) << 16 | clock.m1 << 8 | clock.m2; if (has_reduced_clock) @@ -2568,7 +3009,7 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc, enable color space conversion */ if (!IS_IGDNG(dev)) { if (pipe == 0) - dspcntr |= DISPPLANE_SEL_PIPE_A; + dspcntr &= ~DISPPLANE_SEL_PIPE_MASK; else dspcntr |= DISPPLANE_SEL_PIPE_B; } @@ -2580,7 +3021,8 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc, * XXX: No double-wide on 915GM pipe B. Is that the only reason for the * pipe == 0 check? */ - if (mode->clock > intel_get_core_clock_speed(dev) * 9 / 10) + if (mode->clock > + dev_priv->display.get_display_clock_speed(dev) * 9 / 10) pipeconf |= PIPEACONF_DOUBLE_WIDE; else pipeconf &= ~PIPEACONF_DOUBLE_WIDE; @@ -2625,6 +3067,8 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc, lvds = I915_READ(lvds_reg); lvds |= LVDS_PORT_EN | LVDS_A0A2_CLKA_POWER_UP | LVDS_PIPEB_SELECT; + /* set the corresponsding LVDS_BORDER bit */ + lvds |= dev_priv->lvds_border_bits; /* Set the B0-B3 data pairs corresponding to whether we're going to * set the DPLLs for dual-channel mode or not. */ @@ -2652,9 +3096,12 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc, udelay(150); if (IS_I965G(dev) && !IS_IGDNG(dev)) { - sdvo_pixel_multiply = adjusted_mode->clock / mode->clock; - I915_WRITE(dpll_md_reg, (0 << DPLL_MD_UDI_DIVIDER_SHIFT) | + if (is_sdvo) { + sdvo_pixel_multiply = adjusted_mode->clock / mode->clock; + I915_WRITE(dpll_md_reg, (0 << DPLL_MD_UDI_DIVIDER_SHIFT) | ((sdvo_pixel_multiply - 1) << DPLL_MD_UDI_MULTIPLIER_SHIFT)); + } else + I915_WRITE(dpll_md_reg, 0); } else { /* write it again -- the BIOS does, after all */ I915_WRITE(dpll_reg, dpll); @@ -2734,6 +3181,9 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc, /* Flush the plane changes */ ret = intel_pipe_set_base(crtc, x, y, old_fb); + if ((IS_I965G(dev) || plane == 0)) + intel_update_fbc(crtc, &crtc->mode); + intel_update_watermarks(dev); drm_vblank_post_modeset(dev, pipe); @@ -2863,6 +3313,7 @@ static int intel_crtc_cursor_set(struct drm_crtc *crtc, i915_gem_object_unpin(intel_crtc->cursor_bo); drm_gem_object_unreference(intel_crtc->cursor_bo); } + mutex_unlock(&dev->struct_mutex); intel_crtc->cursor_addr = addr; @@ -2922,6 +3373,16 @@ void intel_crtc_fb_gamma_set(struct drm_crtc *crtc, u16 red, u16 green, intel_crtc->lut_b[regno] = blue >> 8; } +void intel_crtc_fb_gamma_get(struct drm_crtc *crtc, u16 *red, u16 *green, + u16 *blue, int regno) +{ + struct intel_crtc *intel_crtc = to_intel_crtc(crtc); + + *red = intel_crtc->lut_r[regno] << 8; + *green = intel_crtc->lut_g[regno] << 8; + *blue = intel_crtc->lut_b[regno] << 8; +} + static void intel_crtc_gamma_set(struct drm_crtc *crtc, u16 *red, u16 *green, u16 *blue, uint32_t size) { @@ -3513,6 +3974,7 @@ static const struct drm_crtc_helper_funcs intel_helper_funcs = { .mode_set_base = intel_pipe_set_base, .prepare = intel_crtc_prepare, .commit = intel_crtc_commit, + .load_lut = intel_crtc_load_lut, }; static const struct drm_crtc_funcs intel_crtc_funcs = { @@ -3544,6 +4006,14 @@ static void intel_crtc_init(struct drm_device *dev, int pipe) intel_crtc->lut_b[i] = i; } + /* Swap pipes & planes for FBC on pre-965 */ + intel_crtc->pipe = pipe; + intel_crtc->plane = pipe; + if (IS_MOBILE(dev) && (IS_I9XX(dev) && !IS_I965G(dev))) { + DRM_DEBUG("swapping pipes & planes for FBC\n"); + intel_crtc->plane = ((pipe == 0) ? 1 : 0); + } + intel_crtc->cursor_addr = 0; intel_crtc->dpms_mode = DRM_MODE_DPMS_OFF; drm_crtc_helper_add(&intel_crtc->base, &intel_helper_funcs); @@ -3787,7 +4257,9 @@ void intel_init_clock_gating(struct drm_device *dev) * Disable clock gating reported to work incorrectly according to the * specs, but enable as much else as we can. */ - if (IS_G4X(dev)) { + if (IS_IGDNG(dev)) { + return; + } else if (IS_G4X(dev)) { uint32_t dspclk_gate; I915_WRITE(RENCLK_GATE_D1, 0); I915_WRITE(RENCLK_GATE_D2, VF_UNIT_CLOCK_GATE_DISABLE | @@ -3826,6 +4298,75 @@ void intel_init_clock_gating(struct drm_device *dev) } } +/* Set up chip specific display functions */ +static void intel_init_display(struct drm_device *dev) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + + /* We always want a DPMS function */ + if (IS_IGDNG(dev)) + dev_priv->display.dpms = igdng_crtc_dpms; + else + dev_priv->display.dpms = i9xx_crtc_dpms; + + /* Only mobile has FBC, leave pointers NULL for other chips */ + if (IS_MOBILE(dev)) { + if (IS_GM45(dev)) { + dev_priv->display.fbc_enabled = g4x_fbc_enabled; + dev_priv->display.enable_fbc = g4x_enable_fbc; + dev_priv->display.disable_fbc = g4x_disable_fbc; + } else if (IS_I965GM(dev) || IS_I945GM(dev) || IS_I915GM(dev)) { + dev_priv->display.fbc_enabled = i8xx_fbc_enabled; + dev_priv->display.enable_fbc = i8xx_enable_fbc; + dev_priv->display.disable_fbc = i8xx_disable_fbc; + } + /* 855GM needs testing */ + } + + /* Returns the core display clock speed */ + if (IS_I945G(dev)) + dev_priv->display.get_display_clock_speed = + i945_get_display_clock_speed; + else if (IS_I915G(dev)) + dev_priv->display.get_display_clock_speed = + i915_get_display_clock_speed; + else if (IS_I945GM(dev) || IS_845G(dev) || IS_IGDGM(dev)) + dev_priv->display.get_display_clock_speed = + i9xx_misc_get_display_clock_speed; + else if (IS_I915GM(dev)) + dev_priv->display.get_display_clock_speed = + i915gm_get_display_clock_speed; + else if (IS_I865G(dev)) + dev_priv->display.get_display_clock_speed = + i865_get_display_clock_speed; + else if (IS_I855(dev)) + dev_priv->display.get_display_clock_speed = + i855_get_display_clock_speed; + else /* 852, 830 */ + dev_priv->display.get_display_clock_speed = + i830_get_display_clock_speed; + + /* For FIFO watermark updates */ + if (IS_IGDNG(dev)) + dev_priv->display.update_wm = NULL; + else if (IS_G4X(dev)) + dev_priv->display.update_wm = g4x_update_wm; + else if (IS_I965G(dev)) + dev_priv->display.update_wm = i965_update_wm; + else if (IS_I9XX(dev) || IS_MOBILE(dev)) { + dev_priv->display.update_wm = i9xx_update_wm; + dev_priv->display.get_fifo_size = i9xx_get_fifo_size; + } else { + if (IS_I85X(dev)) + dev_priv->display.get_fifo_size = i85x_get_fifo_size; + else if (IS_845G(dev)) + dev_priv->display.get_fifo_size = i845_get_fifo_size; + else + dev_priv->display.get_fifo_size = i830_get_fifo_size; + dev_priv->display.update_wm = i830_update_wm; + } +} + void intel_modeset_init(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; @@ -3839,6 +4380,8 @@ void intel_modeset_init(struct drm_device *dev) dev->mode_config.funcs = (void *)&intel_mode_funcs; + intel_init_display(dev); + if (IS_I965G(dev)) { dev->mode_config.max_width = 8192; dev->mode_config.max_height = 8192; @@ -3904,6 +4447,9 @@ void intel_modeset_cleanup(struct drm_device *dev) mutex_unlock(&dev->struct_mutex); + if (dev_priv->display.disable_fbc) + dev_priv->display.disable_fbc(dev); + drm_mode_config_cleanup(dev); } diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index f4856a510476..d83447557f9b 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -400,7 +400,7 @@ intel_dp_i2c_init(struct intel_output *intel_output, const char *name) { struct intel_dp_priv *dp_priv = intel_output->dev_priv; - DRM_ERROR("i2c_init %s\n", name); + DRM_DEBUG_KMS("i2c_init %s\n", name); dp_priv->algo.running = false; dp_priv->algo.address = 0; dp_priv->algo.aux_ch = intel_dp_i2c_aux_ch; diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index 3ebbbabfe59b..ef61fe9507e2 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -28,6 +28,7 @@ #include <linux/i2c.h> #include <linux/i2c-id.h> #include <linux/i2c-algo-bit.h> +#include "i915_drv.h" #include "drm_crtc.h" #include "drm_crtc_helper.h" @@ -111,8 +112,8 @@ struct intel_output { struct intel_crtc { struct drm_crtc base; - int pipe; - int plane; + enum pipe pipe; + enum plane plane; struct drm_gem_object *cursor_bo; uint32_t cursor_addr; u8 lut_r[256], lut_g[256], lut_b[256]; @@ -174,6 +175,8 @@ extern int intelfb_resize(struct drm_device *dev, struct drm_crtc *crtc); extern void intelfb_restore(void); extern void intel_crtc_fb_gamma_set(struct drm_crtc *crtc, u16 red, u16 green, u16 blue, int regno); +extern void intel_crtc_fb_gamma_get(struct drm_crtc *crtc, u16 *red, u16 *green, + u16 *blue, int regno); extern int intel_framebuffer_create(struct drm_device *dev, struct drm_mode_fb_cmd *mode_cmd, diff --git a/drivers/gpu/drm/i915/intel_fb.c b/drivers/gpu/drm/i915/intel_fb.c index 7ba4a232a97f..2b0fe54cd92c 100644 --- a/drivers/gpu/drm/i915/intel_fb.c +++ b/drivers/gpu/drm/i915/intel_fb.c @@ -60,10 +60,12 @@ static struct fb_ops intelfb_ops = { .fb_imageblit = cfb_imageblit, .fb_pan_display = drm_fb_helper_pan_display, .fb_blank = drm_fb_helper_blank, + .fb_setcmap = drm_fb_helper_setcmap, }; static struct drm_fb_helper_funcs intel_fb_helper_funcs = { .gamma_set = intel_crtc_fb_gamma_set, + .gamma_get = intel_crtc_fb_gamma_get, }; @@ -110,6 +112,7 @@ EXPORT_SYMBOL(intelfb_resize); static int intelfb_create(struct drm_device *dev, uint32_t fb_width, uint32_t fb_height, uint32_t surface_width, uint32_t surface_height, + uint32_t surface_depth, uint32_t surface_bpp, struct drm_framebuffer **fb_p) { struct fb_info *info; @@ -122,12 +125,16 @@ static int intelfb_create(struct drm_device *dev, uint32_t fb_width, struct device *device = &dev->pdev->dev; int size, ret, mmio_bar = IS_I9XX(dev) ? 0 : 1; + /* we don't do packed 24bpp */ + if (surface_bpp == 24) + surface_bpp = 32; + mode_cmd.width = surface_width; mode_cmd.height = surface_height; - mode_cmd.bpp = 32; + mode_cmd.bpp = surface_bpp; mode_cmd.pitch = ALIGN(mode_cmd.width * ((mode_cmd.bpp + 1) / 8), 64); - mode_cmd.depth = 24; + mode_cmd.depth = surface_depth; size = mode_cmd.pitch * mode_cmd.height; size = ALIGN(size, PAGE_SIZE); @@ -205,7 +212,7 @@ static int intelfb_create(struct drm_device *dev, uint32_t fb_width, // memset(info->screen_base, 0, size); - drm_fb_helper_fill_fix(info, fb->pitch); + drm_fb_helper_fill_fix(info, fb->pitch, fb->depth); drm_fb_helper_fill_var(info, fb, fb_width, fb_height); /* FIXME: we really shouldn't expose mmio space at all */ @@ -243,7 +250,7 @@ int intelfb_probe(struct drm_device *dev) int ret; DRM_DEBUG("\n"); - ret = drm_fb_helper_single_fb_probe(dev, intelfb_create); + ret = drm_fb_helper_single_fb_probe(dev, 32, intelfb_create); return ret; } EXPORT_SYMBOL(intelfb_probe); diff --git a/drivers/gpu/drm/i915/intel_hdmi.c b/drivers/gpu/drm/i915/intel_hdmi.c index fa304e136010..c33451aec1bd 100644 --- a/drivers/gpu/drm/i915/intel_hdmi.c +++ b/drivers/gpu/drm/i915/intel_hdmi.c @@ -77,14 +77,32 @@ static void intel_hdmi_dpms(struct drm_encoder *encoder, int mode) struct intel_hdmi_priv *hdmi_priv = intel_output->dev_priv; u32 temp; - if (mode != DRM_MODE_DPMS_ON) { - temp = I915_READ(hdmi_priv->sdvox_reg); + temp = I915_READ(hdmi_priv->sdvox_reg); + + /* HW workaround, need to toggle enable bit off and on for 12bpc, but + * we do this anyway which shows more stable in testing. + */ + if (IS_IGDNG(dev)) { I915_WRITE(hdmi_priv->sdvox_reg, temp & ~SDVO_ENABLE); + POSTING_READ(hdmi_priv->sdvox_reg); + } + + if (mode != DRM_MODE_DPMS_ON) { + temp &= ~SDVO_ENABLE; } else { - temp = I915_READ(hdmi_priv->sdvox_reg); - I915_WRITE(hdmi_priv->sdvox_reg, temp | SDVO_ENABLE); + temp |= SDVO_ENABLE; } + + I915_WRITE(hdmi_priv->sdvox_reg, temp); POSTING_READ(hdmi_priv->sdvox_reg); + + /* HW workaround, need to write this twice for issue that may result + * in first write getting masked. + */ + if (IS_IGDNG(dev)) { + I915_WRITE(hdmi_priv->sdvox_reg, temp); + POSTING_READ(hdmi_priv->sdvox_reg); + } } static void intel_hdmi_save(struct drm_connector *connector) @@ -223,7 +241,7 @@ void intel_hdmi_init(struct drm_device *dev, int sdvox_reg) connector = &intel_output->base; drm_connector_init(dev, connector, &intel_hdmi_connector_funcs, - DRM_MODE_CONNECTOR_DVID); + DRM_MODE_CONNECTOR_HDMIA); drm_connector_helper_add(connector, &intel_hdmi_connector_helper_funcs); intel_output->type = INTEL_OUTPUT_HDMI; diff --git a/drivers/gpu/drm/i915/intel_lvds.c b/drivers/gpu/drm/i915/intel_lvds.c index dafc0da1c256..05598ae10c4b 100644 --- a/drivers/gpu/drm/i915/intel_lvds.c +++ b/drivers/gpu/drm/i915/intel_lvds.c @@ -27,6 +27,7 @@ * Jesse Barnes <jesse.barnes@intel.com> */ +#include <acpi/button.h> #include <linux/dmi.h> #include <linux/i2c.h> #include "drmP.h" @@ -295,6 +296,10 @@ static bool intel_lvds_mode_fixup(struct drm_encoder *encoder, goto out; } + /* full screen scale for now */ + if (IS_IGDNG(dev)) + goto out; + /* 965+ wants fuzzy fitting */ if (IS_I965G(dev)) pfit_control |= (intel_crtc->pipe << PFIT_PIPE_SHIFT) | @@ -322,8 +327,10 @@ static bool intel_lvds_mode_fixup(struct drm_encoder *encoder, * to register description and PRM. * Change the value here to see the borders for debugging */ - I915_WRITE(BCLRPAT_A, 0); - I915_WRITE(BCLRPAT_B, 0); + if (!IS_IGDNG(dev)) { + I915_WRITE(BCLRPAT_A, 0); + I915_WRITE(BCLRPAT_B, 0); + } switch (lvds_priv->fitting_mode) { case DRM_MODE_SCALE_CENTER: @@ -373,7 +380,7 @@ static bool intel_lvds_mode_fixup(struct drm_encoder *encoder, adjusted_mode->crtc_vblank_start + vsync_pos; /* keep the vsync width constant */ adjusted_mode->crtc_vsync_end = - adjusted_mode->crtc_vblank_start + vsync_width; + adjusted_mode->crtc_vsync_start + vsync_width; border = 1; break; case DRM_MODE_SCALE_ASPECT: @@ -519,6 +526,14 @@ out: lvds_priv->pfit_control = pfit_control; lvds_priv->pfit_pgm_ratios = pfit_pgm_ratios; /* + * When there exists the border, it means that the LVDS_BORDR + * should be enabled. + */ + if (border) + dev_priv->lvds_border_bits |= LVDS_BORDER_ENABLE; + else + dev_priv->lvds_border_bits &= ~(LVDS_BORDER_ENABLE); + /* * XXX: It would be nice to support lower refresh rates on the * panels to reduce power consumption, and perhaps match the * user's requested refresh rate. @@ -572,7 +587,6 @@ static void intel_lvds_mode_set(struct drm_encoder *encoder, * settings. */ - /* No panel fitting yet, fixme */ if (IS_IGDNG(dev)) return; @@ -585,15 +599,33 @@ static void intel_lvds_mode_set(struct drm_encoder *encoder, I915_WRITE(PFIT_CONTROL, lvds_priv->pfit_control); } +/* Some lid devices report incorrect lid status, assume they're connected */ +static const struct dmi_system_id bad_lid_status[] = { + { + .ident = "Aspire One", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Acer"), + DMI_MATCH(DMI_PRODUCT_NAME, "Aspire one"), + }, + }, + { } +}; + /** * Detect the LVDS connection. * - * This always returns CONNECTOR_STATUS_CONNECTED. This connector should only have - * been set up if the LVDS was actually connected anyway. + * Since LVDS doesn't have hotlug, we use the lid as a proxy. Open means + * connected and closed means disconnected. We also send hotplug events as + * needed, using lid status notification from the input layer. */ static enum drm_connector_status intel_lvds_detect(struct drm_connector *connector) { - return connector_status_connected; + enum drm_connector_status status = connector_status_connected; + + if (!acpi_lid_open() && !dmi_check_system(bad_lid_status)) + status = connector_status_disconnected; + + return status; } /** @@ -632,6 +664,39 @@ static int intel_lvds_get_modes(struct drm_connector *connector) return 0; } +/* + * Lid events. Note the use of 'modeset_on_lid': + * - we set it on lid close, and reset it on open + * - we use it as a "only once" bit (ie we ignore + * duplicate events where it was already properly + * set/reset) + * - the suspend/resume paths will also set it to + * zero, since they restore the mode ("lid open"). + */ +static int intel_lid_notify(struct notifier_block *nb, unsigned long val, + void *unused) +{ + struct drm_i915_private *dev_priv = + container_of(nb, struct drm_i915_private, lid_notifier); + struct drm_device *dev = dev_priv->dev; + + if (!acpi_lid_open()) { + dev_priv->modeset_on_lid = 1; + return NOTIFY_OK; + } + + if (!dev_priv->modeset_on_lid) + return NOTIFY_OK; + + dev_priv->modeset_on_lid = 0; + + mutex_lock(&dev->mode_config.mutex); + drm_helper_resume_force_mode(dev); + mutex_unlock(&dev->mode_config.mutex); + + return NOTIFY_OK; +} + /** * intel_lvds_destroy - unregister and free LVDS structures * @connector: connector to free @@ -641,10 +706,14 @@ static int intel_lvds_get_modes(struct drm_connector *connector) */ static void intel_lvds_destroy(struct drm_connector *connector) { + struct drm_device *dev = connector->dev; struct intel_output *intel_output = to_intel_output(connector); + struct drm_i915_private *dev_priv = dev->dev_private; if (intel_output->ddc_bus) intel_i2c_destroy(intel_output->ddc_bus); + if (dev_priv->lid_notifier.notifier_call) + acpi_lid_notifier_unregister(&dev_priv->lid_notifier); drm_sysfs_connector_remove(connector); drm_connector_cleanup(connector); kfree(connector); @@ -1011,6 +1080,11 @@ out: pwm |= PWM_PCH_ENABLE; I915_WRITE(BLC_PWM_PCH_CTL1, pwm); } + dev_priv->lid_notifier.notifier_call = intel_lid_notify; + if (acpi_lid_notifier_register(&dev_priv->lid_notifier)) { + DRM_DEBUG("lid notifier registration failed\n"); + dev_priv->lid_notifier.notifier_call = NULL; + } drm_sysfs_connector_add(connector); return; diff --git a/drivers/gpu/drm/i915/intel_sdvo.c b/drivers/gpu/drm/i915/intel_sdvo.c index 0bf28efcf2c1..083bec2e50f9 100644 --- a/drivers/gpu/drm/i915/intel_sdvo.c +++ b/drivers/gpu/drm/i915/intel_sdvo.c @@ -135,6 +135,30 @@ struct intel_sdvo_priv { struct intel_sdvo_dtd save_input_dtd_1, save_input_dtd_2; struct intel_sdvo_dtd save_output_dtd[16]; u32 save_SDVOX; + /* add the property for the SDVO-TV */ + struct drm_property *left_property; + struct drm_property *right_property; + struct drm_property *top_property; + struct drm_property *bottom_property; + struct drm_property *hpos_property; + struct drm_property *vpos_property; + + /* add the property for the SDVO-TV/LVDS */ + struct drm_property *brightness_property; + struct drm_property *contrast_property; + struct drm_property *saturation_property; + struct drm_property *hue_property; + + /* Add variable to record current setting for the above property */ + u32 left_margin, right_margin, top_margin, bottom_margin; + /* this is to get the range of margin.*/ + u32 max_hscan, max_vscan; + u32 max_hpos, cur_hpos; + u32 max_vpos, cur_vpos; + u32 cur_brightness, max_brightness; + u32 cur_contrast, max_contrast; + u32 cur_saturation, max_saturation; + u32 cur_hue, max_hue; }; static bool @@ -281,6 +305,31 @@ static const struct _sdvo_cmd_name { SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_SDTV_RESOLUTION_SUPPORT), SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_SCALED_HDTV_RESOLUTION_SUPPORT), SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_SUPPORTED_ENHANCEMENTS), + /* Add the op code for SDVO enhancements */ + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_MAX_POSITION_H), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_POSITION_H), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_POSITION_H), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_MAX_POSITION_V), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_POSITION_V), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_POSITION_V), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_MAX_SATURATION), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_SATURATION), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_SATURATION), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_MAX_HUE), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_HUE), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_HUE), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_MAX_CONTRAST), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_CONTRAST), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_CONTRAST), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_MAX_BRIGHTNESS), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_BRIGHTNESS), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_BRIGHTNESS), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_MAX_OVERSCAN_H), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_OVERSCAN_H), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_OVERSCAN_H), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_MAX_OVERSCAN_V), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_OVERSCAN_V), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_OVERSCAN_V), /* HDMI op code */ SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_SUPP_ENCODE), SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_ENCODE), @@ -981,7 +1030,7 @@ static void intel_sdvo_set_tv_format(struct intel_output *output) status = intel_sdvo_read_response(output, NULL, 0); if (status != SDVO_CMD_STATUS_SUCCESS) - DRM_DEBUG("%s: Failed to set TV format\n", + DRM_DEBUG_KMS("%s: Failed to set TV format\n", SDVO_NAME(sdvo_priv)); } @@ -1792,6 +1841,45 @@ static int intel_sdvo_get_modes(struct drm_connector *connector) return 1; } +static +void intel_sdvo_destroy_enhance_property(struct drm_connector *connector) +{ + struct intel_output *intel_output = to_intel_output(connector); + struct intel_sdvo_priv *sdvo_priv = intel_output->dev_priv; + struct drm_device *dev = connector->dev; + + if (sdvo_priv->is_tv) { + if (sdvo_priv->left_property) + drm_property_destroy(dev, sdvo_priv->left_property); + if (sdvo_priv->right_property) + drm_property_destroy(dev, sdvo_priv->right_property); + if (sdvo_priv->top_property) + drm_property_destroy(dev, sdvo_priv->top_property); + if (sdvo_priv->bottom_property) + drm_property_destroy(dev, sdvo_priv->bottom_property); + if (sdvo_priv->hpos_property) + drm_property_destroy(dev, sdvo_priv->hpos_property); + if (sdvo_priv->vpos_property) + drm_property_destroy(dev, sdvo_priv->vpos_property); + } + if (sdvo_priv->is_tv) { + if (sdvo_priv->saturation_property) + drm_property_destroy(dev, + sdvo_priv->saturation_property); + if (sdvo_priv->contrast_property) + drm_property_destroy(dev, + sdvo_priv->contrast_property); + if (sdvo_priv->hue_property) + drm_property_destroy(dev, sdvo_priv->hue_property); + } + if (sdvo_priv->is_tv || sdvo_priv->is_lvds) { + if (sdvo_priv->brightness_property) + drm_property_destroy(dev, + sdvo_priv->brightness_property); + } + return; +} + static void intel_sdvo_destroy(struct drm_connector *connector) { struct intel_output *intel_output = to_intel_output(connector); @@ -1812,6 +1900,9 @@ static void intel_sdvo_destroy(struct drm_connector *connector) drm_property_destroy(connector->dev, sdvo_priv->tv_format_property); + if (sdvo_priv->is_tv || sdvo_priv->is_lvds) + intel_sdvo_destroy_enhance_property(connector); + drm_sysfs_connector_remove(connector); drm_connector_cleanup(connector); @@ -1829,6 +1920,8 @@ intel_sdvo_set_property(struct drm_connector *connector, struct drm_crtc *crtc = encoder->crtc; int ret = 0; bool changed = false; + uint8_t cmd, status; + uint16_t temp_value; ret = drm_connector_property_set_value(connector, property, val); if (ret < 0) @@ -1845,11 +1938,102 @@ intel_sdvo_set_property(struct drm_connector *connector, sdvo_priv->tv_format_name = sdvo_priv->tv_format_supported[val]; changed = true; - } else { - ret = -EINVAL; - goto out; } + if (sdvo_priv->is_tv || sdvo_priv->is_lvds) { + cmd = 0; + temp_value = val; + if (sdvo_priv->left_property == property) { + drm_connector_property_set_value(connector, + sdvo_priv->right_property, val); + if (sdvo_priv->left_margin == temp_value) + goto out; + + sdvo_priv->left_margin = temp_value; + sdvo_priv->right_margin = temp_value; + temp_value = sdvo_priv->max_hscan - + sdvo_priv->left_margin; + cmd = SDVO_CMD_SET_OVERSCAN_H; + } else if (sdvo_priv->right_property == property) { + drm_connector_property_set_value(connector, + sdvo_priv->left_property, val); + if (sdvo_priv->right_margin == temp_value) + goto out; + + sdvo_priv->left_margin = temp_value; + sdvo_priv->right_margin = temp_value; + temp_value = sdvo_priv->max_hscan - + sdvo_priv->left_margin; + cmd = SDVO_CMD_SET_OVERSCAN_H; + } else if (sdvo_priv->top_property == property) { + drm_connector_property_set_value(connector, + sdvo_priv->bottom_property, val); + if (sdvo_priv->top_margin == temp_value) + goto out; + + sdvo_priv->top_margin = temp_value; + sdvo_priv->bottom_margin = temp_value; + temp_value = sdvo_priv->max_vscan - + sdvo_priv->top_margin; + cmd = SDVO_CMD_SET_OVERSCAN_V; + } else if (sdvo_priv->bottom_property == property) { + drm_connector_property_set_value(connector, + sdvo_priv->top_property, val); + if (sdvo_priv->bottom_margin == temp_value) + goto out; + sdvo_priv->top_margin = temp_value; + sdvo_priv->bottom_margin = temp_value; + temp_value = sdvo_priv->max_vscan - + sdvo_priv->top_margin; + cmd = SDVO_CMD_SET_OVERSCAN_V; + } else if (sdvo_priv->hpos_property == property) { + if (sdvo_priv->cur_hpos == temp_value) + goto out; + + cmd = SDVO_CMD_SET_POSITION_H; + sdvo_priv->cur_hpos = temp_value; + } else if (sdvo_priv->vpos_property == property) { + if (sdvo_priv->cur_vpos == temp_value) + goto out; + + cmd = SDVO_CMD_SET_POSITION_V; + sdvo_priv->cur_vpos = temp_value; + } else if (sdvo_priv->saturation_property == property) { + if (sdvo_priv->cur_saturation == temp_value) + goto out; + + cmd = SDVO_CMD_SET_SATURATION; + sdvo_priv->cur_saturation = temp_value; + } else if (sdvo_priv->contrast_property == property) { + if (sdvo_priv->cur_contrast == temp_value) + goto out; + + cmd = SDVO_CMD_SET_CONTRAST; + sdvo_priv->cur_contrast = temp_value; + } else if (sdvo_priv->hue_property == property) { + if (sdvo_priv->cur_hue == temp_value) + goto out; + + cmd = SDVO_CMD_SET_HUE; + sdvo_priv->cur_hue = temp_value; + } else if (sdvo_priv->brightness_property == property) { + if (sdvo_priv->cur_brightness == temp_value) + goto out; + + cmd = SDVO_CMD_SET_BRIGHTNESS; + sdvo_priv->cur_brightness = temp_value; + } + if (cmd) { + intel_sdvo_write_cmd(intel_output, cmd, &temp_value, 2); + status = intel_sdvo_read_response(intel_output, + NULL, 0); + if (status != SDVO_CMD_STATUS_SUCCESS) { + DRM_DEBUG_KMS("Incorrect SDVO command \n"); + return -EINVAL; + } + changed = true; + } + } if (changed && crtc) drm_crtc_helper_set_mode(crtc, &crtc->mode, crtc->x, crtc->y, crtc->fb); @@ -2090,6 +2274,8 @@ intel_sdvo_output_setup(struct intel_output *intel_output, uint16_t flags) sdvo_priv->controlled_output = SDVO_OUTPUT_RGB1; encoder->encoder_type = DRM_MODE_ENCODER_DAC; connector->connector_type = DRM_MODE_CONNECTOR_VGA; + intel_output->clone_mask = (1 << INTEL_SDVO_NON_TV_CLONE_BIT) | + (1 << INTEL_ANALOG_CLONE_BIT); } else if (flags & SDVO_OUTPUT_LVDS0) { sdvo_priv->controlled_output = SDVO_OUTPUT_LVDS0; @@ -2176,6 +2362,310 @@ static void intel_sdvo_tv_create_property(struct drm_connector *connector) } +static void intel_sdvo_create_enhance_property(struct drm_connector *connector) +{ + struct intel_output *intel_output = to_intel_output(connector); + struct intel_sdvo_priv *sdvo_priv = intel_output->dev_priv; + struct intel_sdvo_enhancements_reply sdvo_data; + struct drm_device *dev = connector->dev; + uint8_t status; + uint16_t response, data_value[2]; + + intel_sdvo_write_cmd(intel_output, SDVO_CMD_GET_SUPPORTED_ENHANCEMENTS, + NULL, 0); + status = intel_sdvo_read_response(intel_output, &sdvo_data, + sizeof(sdvo_data)); + if (status != SDVO_CMD_STATUS_SUCCESS) { + DRM_DEBUG_KMS(" incorrect response is returned\n"); + return; + } + response = *((uint16_t *)&sdvo_data); + if (!response) { + DRM_DEBUG_KMS("No enhancement is supported\n"); + return; + } + if (sdvo_priv->is_tv) { + /* when horizontal overscan is supported, Add the left/right + * property + */ + if (sdvo_data.overscan_h) { + intel_sdvo_write_cmd(intel_output, + SDVO_CMD_GET_MAX_OVERSCAN_H, NULL, 0); + status = intel_sdvo_read_response(intel_output, + &data_value, 4); + if (status != SDVO_CMD_STATUS_SUCCESS) { + DRM_DEBUG_KMS("Incorrect SDVO max " + "h_overscan\n"); + return; + } + intel_sdvo_write_cmd(intel_output, + SDVO_CMD_GET_OVERSCAN_H, NULL, 0); + status = intel_sdvo_read_response(intel_output, + &response, 2); + if (status != SDVO_CMD_STATUS_SUCCESS) { + DRM_DEBUG_KMS("Incorrect SDVO h_overscan\n"); + return; + } + sdvo_priv->max_hscan = data_value[0]; + sdvo_priv->left_margin = data_value[0] - response; + sdvo_priv->right_margin = sdvo_priv->left_margin; + sdvo_priv->left_property = + drm_property_create(dev, DRM_MODE_PROP_RANGE, + "left_margin", 2); + sdvo_priv->left_property->values[0] = 0; + sdvo_priv->left_property->values[1] = data_value[0]; + drm_connector_attach_property(connector, + sdvo_priv->left_property, + sdvo_priv->left_margin); + sdvo_priv->right_property = + drm_property_create(dev, DRM_MODE_PROP_RANGE, + "right_margin", 2); + sdvo_priv->right_property->values[0] = 0; + sdvo_priv->right_property->values[1] = data_value[0]; + drm_connector_attach_property(connector, + sdvo_priv->right_property, + sdvo_priv->right_margin); + DRM_DEBUG_KMS("h_overscan: max %d, " + "default %d, current %d\n", + data_value[0], data_value[1], response); + } + if (sdvo_data.overscan_v) { + intel_sdvo_write_cmd(intel_output, + SDVO_CMD_GET_MAX_OVERSCAN_V, NULL, 0); + status = intel_sdvo_read_response(intel_output, + &data_value, 4); + if (status != SDVO_CMD_STATUS_SUCCESS) { + DRM_DEBUG_KMS("Incorrect SDVO max " + "v_overscan\n"); + return; + } + intel_sdvo_write_cmd(intel_output, + SDVO_CMD_GET_OVERSCAN_V, NULL, 0); + status = intel_sdvo_read_response(intel_output, + &response, 2); + if (status != SDVO_CMD_STATUS_SUCCESS) { + DRM_DEBUG_KMS("Incorrect SDVO v_overscan\n"); + return; + } + sdvo_priv->max_vscan = data_value[0]; + sdvo_priv->top_margin = data_value[0] - response; + sdvo_priv->bottom_margin = sdvo_priv->top_margin; + sdvo_priv->top_property = + drm_property_create(dev, DRM_MODE_PROP_RANGE, + "top_margin", 2); + sdvo_priv->top_property->values[0] = 0; + sdvo_priv->top_property->values[1] = data_value[0]; + drm_connector_attach_property(connector, + sdvo_priv->top_property, + sdvo_priv->top_margin); + sdvo_priv->bottom_property = + drm_property_create(dev, DRM_MODE_PROP_RANGE, + "bottom_margin", 2); + sdvo_priv->bottom_property->values[0] = 0; + sdvo_priv->bottom_property->values[1] = data_value[0]; + drm_connector_attach_property(connector, + sdvo_priv->bottom_property, + sdvo_priv->bottom_margin); + DRM_DEBUG_KMS("v_overscan: max %d, " + "default %d, current %d\n", + data_value[0], data_value[1], response); + } + if (sdvo_data.position_h) { + intel_sdvo_write_cmd(intel_output, + SDVO_CMD_GET_MAX_POSITION_H, NULL, 0); + status = intel_sdvo_read_response(intel_output, + &data_value, 4); + if (status != SDVO_CMD_STATUS_SUCCESS) { + DRM_DEBUG_KMS("Incorrect SDVO Max h_pos\n"); + return; + } + intel_sdvo_write_cmd(intel_output, + SDVO_CMD_GET_POSITION_H, NULL, 0); + status = intel_sdvo_read_response(intel_output, + &response, 2); + if (status != SDVO_CMD_STATUS_SUCCESS) { + DRM_DEBUG_KMS("Incorrect SDVO get h_postion\n"); + return; + } + sdvo_priv->max_hpos = data_value[0]; + sdvo_priv->cur_hpos = response; + sdvo_priv->hpos_property = + drm_property_create(dev, DRM_MODE_PROP_RANGE, + "hpos", 2); + sdvo_priv->hpos_property->values[0] = 0; + sdvo_priv->hpos_property->values[1] = data_value[0]; + drm_connector_attach_property(connector, + sdvo_priv->hpos_property, + sdvo_priv->cur_hpos); + DRM_DEBUG_KMS("h_position: max %d, " + "default %d, current %d\n", + data_value[0], data_value[1], response); + } + if (sdvo_data.position_v) { + intel_sdvo_write_cmd(intel_output, + SDVO_CMD_GET_MAX_POSITION_V, NULL, 0); + status = intel_sdvo_read_response(intel_output, + &data_value, 4); + if (status != SDVO_CMD_STATUS_SUCCESS) { + DRM_DEBUG_KMS("Incorrect SDVO Max v_pos\n"); + return; + } + intel_sdvo_write_cmd(intel_output, + SDVO_CMD_GET_POSITION_V, NULL, 0); + status = intel_sdvo_read_response(intel_output, + &response, 2); + if (status != SDVO_CMD_STATUS_SUCCESS) { + DRM_DEBUG_KMS("Incorrect SDVO get v_postion\n"); + return; + } + sdvo_priv->max_vpos = data_value[0]; + sdvo_priv->cur_vpos = response; + sdvo_priv->vpos_property = + drm_property_create(dev, DRM_MODE_PROP_RANGE, + "vpos", 2); + sdvo_priv->vpos_property->values[0] = 0; + sdvo_priv->vpos_property->values[1] = data_value[0]; + drm_connector_attach_property(connector, + sdvo_priv->vpos_property, + sdvo_priv->cur_vpos); + DRM_DEBUG_KMS("v_position: max %d, " + "default %d, current %d\n", + data_value[0], data_value[1], response); + } + } + if (sdvo_priv->is_tv) { + if (sdvo_data.saturation) { + intel_sdvo_write_cmd(intel_output, + SDVO_CMD_GET_MAX_SATURATION, NULL, 0); + status = intel_sdvo_read_response(intel_output, + &data_value, 4); + if (status != SDVO_CMD_STATUS_SUCCESS) { + DRM_DEBUG_KMS("Incorrect SDVO Max sat\n"); + return; + } + intel_sdvo_write_cmd(intel_output, + SDVO_CMD_GET_SATURATION, NULL, 0); + status = intel_sdvo_read_response(intel_output, + &response, 2); + if (status != SDVO_CMD_STATUS_SUCCESS) { + DRM_DEBUG_KMS("Incorrect SDVO get sat\n"); + return; + } + sdvo_priv->max_saturation = data_value[0]; + sdvo_priv->cur_saturation = response; + sdvo_priv->saturation_property = + drm_property_create(dev, DRM_MODE_PROP_RANGE, + "saturation", 2); + sdvo_priv->saturation_property->values[0] = 0; + sdvo_priv->saturation_property->values[1] = + data_value[0]; + drm_connector_attach_property(connector, + sdvo_priv->saturation_property, + sdvo_priv->cur_saturation); + DRM_DEBUG_KMS("saturation: max %d, " + "default %d, current %d\n", + data_value[0], data_value[1], response); + } + if (sdvo_data.contrast) { + intel_sdvo_write_cmd(intel_output, + SDVO_CMD_GET_MAX_CONTRAST, NULL, 0); + status = intel_sdvo_read_response(intel_output, + &data_value, 4); + if (status != SDVO_CMD_STATUS_SUCCESS) { + DRM_DEBUG_KMS("Incorrect SDVO Max contrast\n"); + return; + } + intel_sdvo_write_cmd(intel_output, + SDVO_CMD_GET_CONTRAST, NULL, 0); + status = intel_sdvo_read_response(intel_output, + &response, 2); + if (status != SDVO_CMD_STATUS_SUCCESS) { + DRM_DEBUG_KMS("Incorrect SDVO get contrast\n"); + return; + } + sdvo_priv->max_contrast = data_value[0]; + sdvo_priv->cur_contrast = response; + sdvo_priv->contrast_property = + drm_property_create(dev, DRM_MODE_PROP_RANGE, + "contrast", 2); + sdvo_priv->contrast_property->values[0] = 0; + sdvo_priv->contrast_property->values[1] = data_value[0]; + drm_connector_attach_property(connector, + sdvo_priv->contrast_property, + sdvo_priv->cur_contrast); + DRM_DEBUG_KMS("contrast: max %d, " + "default %d, current %d\n", + data_value[0], data_value[1], response); + } + if (sdvo_data.hue) { + intel_sdvo_write_cmd(intel_output, + SDVO_CMD_GET_MAX_HUE, NULL, 0); + status = intel_sdvo_read_response(intel_output, + &data_value, 4); + if (status != SDVO_CMD_STATUS_SUCCESS) { + DRM_DEBUG_KMS("Incorrect SDVO Max hue\n"); + return; + } + intel_sdvo_write_cmd(intel_output, + SDVO_CMD_GET_HUE, NULL, 0); + status = intel_sdvo_read_response(intel_output, + &response, 2); + if (status != SDVO_CMD_STATUS_SUCCESS) { + DRM_DEBUG_KMS("Incorrect SDVO get hue\n"); + return; + } + sdvo_priv->max_hue = data_value[0]; + sdvo_priv->cur_hue = response; + sdvo_priv->hue_property = + drm_property_create(dev, DRM_MODE_PROP_RANGE, + "hue", 2); + sdvo_priv->hue_property->values[0] = 0; + sdvo_priv->hue_property->values[1] = + data_value[0]; + drm_connector_attach_property(connector, + sdvo_priv->hue_property, + sdvo_priv->cur_hue); + DRM_DEBUG_KMS("hue: max %d, default %d, current %d\n", + data_value[0], data_value[1], response); + } + } + if (sdvo_priv->is_tv || sdvo_priv->is_lvds) { + if (sdvo_data.brightness) { + intel_sdvo_write_cmd(intel_output, + SDVO_CMD_GET_MAX_BRIGHTNESS, NULL, 0); + status = intel_sdvo_read_response(intel_output, + &data_value, 4); + if (status != SDVO_CMD_STATUS_SUCCESS) { + DRM_DEBUG_KMS("Incorrect SDVO Max bright\n"); + return; + } + intel_sdvo_write_cmd(intel_output, + SDVO_CMD_GET_BRIGHTNESS, NULL, 0); + status = intel_sdvo_read_response(intel_output, + &response, 2); + if (status != SDVO_CMD_STATUS_SUCCESS) { + DRM_DEBUG_KMS("Incorrect SDVO get brigh\n"); + return; + } + sdvo_priv->max_brightness = data_value[0]; + sdvo_priv->cur_brightness = response; + sdvo_priv->brightness_property = + drm_property_create(dev, DRM_MODE_PROP_RANGE, + "brightness", 2); + sdvo_priv->brightness_property->values[0] = 0; + sdvo_priv->brightness_property->values[1] = + data_value[0]; + drm_connector_attach_property(connector, + sdvo_priv->brightness_property, + sdvo_priv->cur_brightness); + DRM_DEBUG_KMS("brightness: max %d, " + "default %d, current %d\n", + data_value[0], data_value[1], response); + } + } + return; +} + bool intel_sdvo_init(struct drm_device *dev, int output_device) { struct drm_connector *connector; @@ -2264,6 +2754,10 @@ bool intel_sdvo_init(struct drm_device *dev, int output_device) drm_mode_connector_attach_encoder(&intel_output->base, &intel_output->enc); if (sdvo_priv->is_tv) intel_sdvo_tv_create_property(connector); + + if (sdvo_priv->is_tv || sdvo_priv->is_lvds) + intel_sdvo_create_enhance_property(connector); + drm_sysfs_connector_add(connector); intel_sdvo_select_ddc_bus(sdvo_priv); diff --git a/drivers/gpu/drm/i915/intel_tv.c b/drivers/gpu/drm/i915/intel_tv.c index c64eab493fb0..9ca917931afb 100644 --- a/drivers/gpu/drm/i915/intel_tv.c +++ b/drivers/gpu/drm/i915/intel_tv.c @@ -1082,7 +1082,8 @@ intel_tv_mode_valid(struct drm_connector *connector, struct drm_display_mode *mo const struct tv_mode *tv_mode = intel_tv_mode_find(intel_output); /* Ensure TV refresh is close to desired refresh */ - if (tv_mode && abs(tv_mode->refresh - drm_mode_vrefresh(mode)) < 10) + if (tv_mode && abs(tv_mode->refresh - drm_mode_vrefresh(mode) * 1000) + < 1000) return MODE_OK; return MODE_CLOCK_RANGE; } diff --git a/drivers/gpu/drm/radeon/.gitignore b/drivers/gpu/drm/radeon/.gitignore new file mode 100644 index 000000000000..403eb3a5891f --- /dev/null +++ b/drivers/gpu/drm/radeon/.gitignore @@ -0,0 +1,3 @@ +mkregtable +*_reg_safe.h + diff --git a/drivers/gpu/drm/radeon/Makefile b/drivers/gpu/drm/radeon/Makefile index 09a28923f46e..b5713eedd6e1 100644 --- a/drivers/gpu/drm/radeon/Makefile +++ b/drivers/gpu/drm/radeon/Makefile @@ -49,7 +49,7 @@ radeon-y += radeon_device.o radeon_kms.o \ radeon_cs.o radeon_bios.o radeon_benchmark.o r100.o r300.o r420.o \ rs400.o rs600.o rs690.o rv515.o r520.o r600.o rv770.o radeon_test.o \ r200.o radeon_legacy_tv.o r600_cs.o r600_blit.o r600_blit_shaders.o \ - r600_blit_kms.o + r600_blit_kms.o radeon_pm.o radeon-$(CONFIG_COMPAT) += radeon_ioc32.o diff --git a/drivers/gpu/drm/radeon/atom.c b/drivers/gpu/drm/radeon/atom.c index 901befe03da2..d67c42555ab9 100644 --- a/drivers/gpu/drm/radeon/atom.c +++ b/drivers/gpu/drm/radeon/atom.c @@ -107,6 +107,7 @@ static uint32_t atom_iio_execute(struct atom_context *ctx, int base, base += 3; break; case ATOM_IIO_WRITE: + (void)ctx->card->reg_read(ctx->card, CU16(base + 1)); ctx->card->reg_write(ctx->card, CU16(base + 1), temp); base += 3; break; diff --git a/drivers/gpu/drm/radeon/atombios.h b/drivers/gpu/drm/radeon/atombios.h index 5d402086bc47..c11ddddfb3b6 100644 --- a/drivers/gpu/drm/radeon/atombios.h +++ b/drivers/gpu/drm/radeon/atombios.h @@ -2314,7 +2314,7 @@ typedef struct _ATOM_SPREAD_SPECTRUM_ASSIGNMENT { UCHAR ucSS_Step; UCHAR ucSS_Delay; UCHAR ucSS_Id; - UCHAR ucRecommandedRef_Div; + UCHAR ucRecommendedRef_Div; UCHAR ucSS_Range; /* it was reserved for V11 */ } ATOM_SPREAD_SPECTRUM_ASSIGNMENT; diff --git a/drivers/gpu/drm/radeon/atombios_crtc.c b/drivers/gpu/drm/radeon/atombios_crtc.c index 6a015929deee..c15287a590ff 100644 --- a/drivers/gpu/drm/radeon/atombios_crtc.c +++ b/drivers/gpu/drm/radeon/atombios_crtc.c @@ -31,10 +31,6 @@ #include "atom.h" #include "atom-bits.h" -/* evil but including atombios.h is much worse */ -bool radeon_atom_get_tv_timings(struct radeon_device *rdev, int index, - SET_CRTC_TIMING_PARAMETERS_PS_ALLOCATION *crtc_timing, - int32_t *pixel_clock); static void atombios_overscan_setup(struct drm_crtc *crtc, struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode) @@ -248,18 +244,18 @@ void atombios_crtc_dpms(struct drm_crtc *crtc, int mode) switch (mode) { case DRM_MODE_DPMS_ON: + atombios_enable_crtc(crtc, 1); if (ASIC_IS_DCE3(rdev)) atombios_enable_crtc_memreq(crtc, 1); - atombios_enable_crtc(crtc, 1); atombios_blank_crtc(crtc, 0); break; case DRM_MODE_DPMS_STANDBY: case DRM_MODE_DPMS_SUSPEND: case DRM_MODE_DPMS_OFF: atombios_blank_crtc(crtc, 1); - atombios_enable_crtc(crtc, 0); if (ASIC_IS_DCE3(rdev)) atombios_enable_crtc_memreq(crtc, 0); + atombios_enable_crtc(crtc, 0); break; } @@ -270,59 +266,147 @@ void atombios_crtc_dpms(struct drm_crtc *crtc, int mode) static void atombios_set_crtc_dtd_timing(struct drm_crtc *crtc, - SET_CRTC_USING_DTD_TIMING_PARAMETERS * crtc_param) + struct drm_display_mode *mode) { + struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); struct drm_device *dev = crtc->dev; struct radeon_device *rdev = dev->dev_private; - SET_CRTC_USING_DTD_TIMING_PARAMETERS conv_param; + SET_CRTC_USING_DTD_TIMING_PARAMETERS args; int index = GetIndexIntoMasterTable(COMMAND, SetCRTC_UsingDTDTiming); + u16 misc = 0; - conv_param.usH_Size = cpu_to_le16(crtc_param->usH_Size); - conv_param.usH_Blanking_Time = - cpu_to_le16(crtc_param->usH_Blanking_Time); - conv_param.usV_Size = cpu_to_le16(crtc_param->usV_Size); - conv_param.usV_Blanking_Time = - cpu_to_le16(crtc_param->usV_Blanking_Time); - conv_param.usH_SyncOffset = cpu_to_le16(crtc_param->usH_SyncOffset); - conv_param.usH_SyncWidth = cpu_to_le16(crtc_param->usH_SyncWidth); - conv_param.usV_SyncOffset = cpu_to_le16(crtc_param->usV_SyncOffset); - conv_param.usV_SyncWidth = cpu_to_le16(crtc_param->usV_SyncWidth); - conv_param.susModeMiscInfo.usAccess = - cpu_to_le16(crtc_param->susModeMiscInfo.usAccess); - conv_param.ucCRTC = crtc_param->ucCRTC; + memset(&args, 0, sizeof(args)); + args.usH_Size = cpu_to_le16(mode->crtc_hdisplay); + args.usH_Blanking_Time = + cpu_to_le16(mode->crtc_hblank_end - mode->crtc_hdisplay); + args.usV_Size = cpu_to_le16(mode->crtc_vdisplay); + args.usV_Blanking_Time = + cpu_to_le16(mode->crtc_vblank_end - mode->crtc_vdisplay); + args.usH_SyncOffset = + cpu_to_le16(mode->crtc_hsync_start - mode->crtc_hdisplay); + args.usH_SyncWidth = + cpu_to_le16(mode->crtc_hsync_end - mode->crtc_hsync_start); + args.usV_SyncOffset = + cpu_to_le16(mode->crtc_vsync_start - mode->crtc_vdisplay); + args.usV_SyncWidth = + cpu_to_le16(mode->crtc_vsync_end - mode->crtc_vsync_start); + /*args.ucH_Border = mode->hborder;*/ + /*args.ucV_Border = mode->vborder;*/ + + if (mode->flags & DRM_MODE_FLAG_NVSYNC) + misc |= ATOM_VSYNC_POLARITY; + if (mode->flags & DRM_MODE_FLAG_NHSYNC) + misc |= ATOM_HSYNC_POLARITY; + if (mode->flags & DRM_MODE_FLAG_CSYNC) + misc |= ATOM_COMPOSITESYNC; + if (mode->flags & DRM_MODE_FLAG_INTERLACE) + misc |= ATOM_INTERLACE; + if (mode->flags & DRM_MODE_FLAG_DBLSCAN) + misc |= ATOM_DOUBLE_CLOCK_MODE; + + args.susModeMiscInfo.usAccess = cpu_to_le16(misc); + args.ucCRTC = radeon_crtc->crtc_id; printk("executing set crtc dtd timing\n"); - atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&conv_param); + atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args); } -void atombios_crtc_set_timing(struct drm_crtc *crtc, - SET_CRTC_TIMING_PARAMETERS_PS_ALLOCATION * - crtc_param) +static void atombios_crtc_set_timing(struct drm_crtc *crtc, + struct drm_display_mode *mode) { + struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); struct drm_device *dev = crtc->dev; struct radeon_device *rdev = dev->dev_private; - SET_CRTC_TIMING_PARAMETERS_PS_ALLOCATION conv_param; + SET_CRTC_TIMING_PARAMETERS_PS_ALLOCATION args; int index = GetIndexIntoMasterTable(COMMAND, SetCRTC_Timing); + u16 misc = 0; - conv_param.usH_Total = cpu_to_le16(crtc_param->usH_Total); - conv_param.usH_Disp = cpu_to_le16(crtc_param->usH_Disp); - conv_param.usH_SyncStart = cpu_to_le16(crtc_param->usH_SyncStart); - conv_param.usH_SyncWidth = cpu_to_le16(crtc_param->usH_SyncWidth); - conv_param.usV_Total = cpu_to_le16(crtc_param->usV_Total); - conv_param.usV_Disp = cpu_to_le16(crtc_param->usV_Disp); - conv_param.usV_SyncStart = cpu_to_le16(crtc_param->usV_SyncStart); - conv_param.usV_SyncWidth = cpu_to_le16(crtc_param->usV_SyncWidth); - conv_param.susModeMiscInfo.usAccess = - cpu_to_le16(crtc_param->susModeMiscInfo.usAccess); - conv_param.ucCRTC = crtc_param->ucCRTC; - conv_param.ucOverscanRight = crtc_param->ucOverscanRight; - conv_param.ucOverscanLeft = crtc_param->ucOverscanLeft; - conv_param.ucOverscanBottom = crtc_param->ucOverscanBottom; - conv_param.ucOverscanTop = crtc_param->ucOverscanTop; - conv_param.ucReserved = crtc_param->ucReserved; + memset(&args, 0, sizeof(args)); + args.usH_Total = cpu_to_le16(mode->crtc_htotal); + args.usH_Disp = cpu_to_le16(mode->crtc_hdisplay); + args.usH_SyncStart = cpu_to_le16(mode->crtc_hsync_start); + args.usH_SyncWidth = + cpu_to_le16(mode->crtc_hsync_end - mode->crtc_hsync_start); + args.usV_Total = cpu_to_le16(mode->crtc_vtotal); + args.usV_Disp = cpu_to_le16(mode->crtc_vdisplay); + args.usV_SyncStart = cpu_to_le16(mode->crtc_vsync_start); + args.usV_SyncWidth = + cpu_to_le16(mode->crtc_vsync_end - mode->crtc_vsync_start); + + if (mode->flags & DRM_MODE_FLAG_NVSYNC) + misc |= ATOM_VSYNC_POLARITY; + if (mode->flags & DRM_MODE_FLAG_NHSYNC) + misc |= ATOM_HSYNC_POLARITY; + if (mode->flags & DRM_MODE_FLAG_CSYNC) + misc |= ATOM_COMPOSITESYNC; + if (mode->flags & DRM_MODE_FLAG_INTERLACE) + misc |= ATOM_INTERLACE; + if (mode->flags & DRM_MODE_FLAG_DBLSCAN) + misc |= ATOM_DOUBLE_CLOCK_MODE; + + args.susModeMiscInfo.usAccess = cpu_to_le16(misc); + args.ucCRTC = radeon_crtc->crtc_id; printk("executing set crtc timing\n"); - atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&conv_param); + atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args); +} + +static void atombios_set_ss(struct drm_crtc *crtc, int enable) +{ + struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); + struct drm_device *dev = crtc->dev; + struct radeon_device *rdev = dev->dev_private; + struct drm_encoder *encoder = NULL; + struct radeon_encoder *radeon_encoder = NULL; + struct radeon_encoder_atom_dig *dig = NULL; + int index = GetIndexIntoMasterTable(COMMAND, EnableSpreadSpectrumOnPPLL); + ENABLE_SPREAD_SPECTRUM_ON_PPLL_PS_ALLOCATION args; + ENABLE_LVDS_SS_PARAMETERS legacy_args; + uint16_t percentage = 0; + uint8_t type = 0, step = 0, delay = 0, range = 0; + + list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { + if (encoder->crtc == crtc) { + radeon_encoder = to_radeon_encoder(encoder); + /* only enable spread spectrum on LVDS */ + if (radeon_encoder->devices & (ATOM_DEVICE_LCD_SUPPORT)) { + dig = radeon_encoder->enc_priv; + if (dig && dig->ss) { + percentage = dig->ss->percentage; + type = dig->ss->type; + step = dig->ss->step; + delay = dig->ss->delay; + range = dig->ss->range; + } else if (enable) + return; + } else if (enable) + return; + break; + } + } + + if (!radeon_encoder) + return; + + if (ASIC_IS_AVIVO(rdev)) { + memset(&args, 0, sizeof(args)); + args.usSpreadSpectrumPercentage = cpu_to_le16(percentage); + args.ucSpreadSpectrumType = type; + args.ucSpreadSpectrumStep = step; + args.ucSpreadSpectrumDelay = delay; + args.ucSpreadSpectrumRange = range; + args.ucPpll = radeon_crtc->crtc_id ? ATOM_PPLL2 : ATOM_PPLL1; + args.ucEnable = enable; + atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args); + } else { + memset(&legacy_args, 0, sizeof(legacy_args)); + legacy_args.usSpreadSpectrumPercentage = cpu_to_le16(percentage); + legacy_args.ucSpreadSpectrumType = type; + legacy_args.ucSpreadSpectrumStepSize_Delay = (step & 3) << 2; + legacy_args.ucSpreadSpectrumStepSize_Delay |= (delay & 7) << 4; + legacy_args.ucEnable = enable; + atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&legacy_args); + } } void atombios_crtc_set_pll(struct drm_crtc *crtc, struct drm_display_mode *mode) @@ -333,12 +417,13 @@ void atombios_crtc_set_pll(struct drm_crtc *crtc, struct drm_display_mode *mode) struct drm_encoder *encoder = NULL; struct radeon_encoder *radeon_encoder = NULL; uint8_t frev, crev; - int index = GetIndexIntoMasterTable(COMMAND, SetPixelClock); + int index; SET_PIXEL_CLOCK_PS_ALLOCATION args; PIXEL_CLOCK_PARAMETERS *spc1_ptr; PIXEL_CLOCK_PARAMETERS_V2 *spc2_ptr; PIXEL_CLOCK_PARAMETERS_V3 *spc3_ptr; - uint32_t sclock = mode->clock; + uint32_t pll_clock = mode->clock; + uint32_t adjusted_clock; uint32_t ref_div = 0, fb_div = 0, frac_fb_div = 0, post_div = 0; struct radeon_pll *pll; int pll_flags = 0; @@ -346,8 +431,6 @@ void atombios_crtc_set_pll(struct drm_crtc *crtc, struct drm_display_mode *mode) memset(&args, 0, sizeof(args)); if (ASIC_IS_AVIVO(rdev)) { - uint32_t ss_cntl; - if ((rdev->family == CHIP_RS600) || (rdev->family == CHIP_RS690) || (rdev->family == CHIP_RS740)) @@ -358,15 +441,6 @@ void atombios_crtc_set_pll(struct drm_crtc *crtc, struct drm_display_mode *mode) pll_flags |= RADEON_PLL_PREFER_HIGH_FB_DIV; else pll_flags |= RADEON_PLL_PREFER_LOW_REF_DIV; - - /* disable spread spectrum clocking for now -- thanks Hedy Lamarr */ - if (radeon_crtc->crtc_id == 0) { - ss_cntl = RREG32(AVIVO_P1PLL_INT_SS_CNTL); - WREG32(AVIVO_P1PLL_INT_SS_CNTL, ss_cntl & ~1); - } else { - ss_cntl = RREG32(AVIVO_P2PLL_INT_SS_CNTL); - WREG32(AVIVO_P2PLL_INT_SS_CNTL, ss_cntl & ~1); - } } else { pll_flags |= RADEON_PLL_LEGACY; @@ -393,14 +467,43 @@ void atombios_crtc_set_pll(struct drm_crtc *crtc, struct drm_display_mode *mode) } } + /* DCE3+ has an AdjustDisplayPll that will adjust the pixel clock + * accordingly based on the encoder/transmitter to work around + * special hw requirements. + */ + if (ASIC_IS_DCE3(rdev)) { + ADJUST_DISPLAY_PLL_PS_ALLOCATION adjust_pll_args; + + if (!encoder) + return; + + memset(&adjust_pll_args, 0, sizeof(adjust_pll_args)); + adjust_pll_args.usPixelClock = cpu_to_le16(mode->clock / 10); + adjust_pll_args.ucTransmitterID = radeon_encoder->encoder_id; + adjust_pll_args.ucEncodeMode = atombios_get_encoder_mode(encoder); + + index = GetIndexIntoMasterTable(COMMAND, AdjustDisplayPll); + atom_execute_table(rdev->mode_info.atom_context, + index, (uint32_t *)&adjust_pll_args); + adjusted_clock = le16_to_cpu(adjust_pll_args.usPixelClock) * 10; + } else { + /* DVO wants 2x pixel clock if the DVO chip is in 12 bit mode */ + if (ASIC_IS_AVIVO(rdev) && + (radeon_encoder->encoder_id == ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DVO1)) + adjusted_clock = mode->clock * 2; + else + adjusted_clock = mode->clock; + } + if (radeon_crtc->crtc_id == 0) pll = &rdev->clock.p1pll; else pll = &rdev->clock.p2pll; - radeon_compute_pll(pll, mode->clock, &sclock, &fb_div, &frac_fb_div, + radeon_compute_pll(pll, adjusted_clock, &pll_clock, &fb_div, &frac_fb_div, &ref_div, &post_div, pll_flags); + index = GetIndexIntoMasterTable(COMMAND, SetPixelClock); atom_parse_cmd_header(rdev->mode_info.atom_context, index, &frev, &crev); @@ -409,7 +512,7 @@ void atombios_crtc_set_pll(struct drm_crtc *crtc, struct drm_display_mode *mode) switch (crev) { case 1: spc1_ptr = (PIXEL_CLOCK_PARAMETERS *) & args.sPCLKInput; - spc1_ptr->usPixelClock = cpu_to_le16(sclock); + spc1_ptr->usPixelClock = cpu_to_le16(mode->clock / 10); spc1_ptr->usRefDiv = cpu_to_le16(ref_div); spc1_ptr->usFbDiv = cpu_to_le16(fb_div); spc1_ptr->ucFracFbDiv = frac_fb_div; @@ -422,7 +525,7 @@ void atombios_crtc_set_pll(struct drm_crtc *crtc, struct drm_display_mode *mode) case 2: spc2_ptr = (PIXEL_CLOCK_PARAMETERS_V2 *) & args.sPCLKInput; - spc2_ptr->usPixelClock = cpu_to_le16(sclock); + spc2_ptr->usPixelClock = cpu_to_le16(mode->clock / 10); spc2_ptr->usRefDiv = cpu_to_le16(ref_div); spc2_ptr->usFbDiv = cpu_to_le16(fb_div); spc2_ptr->ucFracFbDiv = frac_fb_div; @@ -437,7 +540,7 @@ void atombios_crtc_set_pll(struct drm_crtc *crtc, struct drm_display_mode *mode) return; spc3_ptr = (PIXEL_CLOCK_PARAMETERS_V3 *) & args.sPCLKInput; - spc3_ptr->usPixelClock = cpu_to_le16(sclock); + spc3_ptr->usPixelClock = cpu_to_le16(mode->clock / 10); spc3_ptr->usRefDiv = cpu_to_le16(ref_div); spc3_ptr->usFbDiv = cpu_to_le16(fb_div); spc3_ptr->ucFracFbDiv = frac_fb_div; @@ -527,6 +630,16 @@ int atombios_crtc_set_base(struct drm_crtc *crtc, int x, int y, WREG32(AVIVO_D1VGA_CONTROL, 0); else WREG32(AVIVO_D2VGA_CONTROL, 0); + + if (rdev->family >= CHIP_RV770) { + if (radeon_crtc->crtc_id) { + WREG32(R700_D2GRPH_PRIMARY_SURFACE_ADDRESS_HIGH, 0); + WREG32(R700_D2GRPH_SECONDARY_SURFACE_ADDRESS_HIGH, 0); + } else { + WREG32(R700_D1GRPH_PRIMARY_SURFACE_ADDRESS_HIGH, 0); + WREG32(R700_D1GRPH_SECONDARY_SURFACE_ADDRESS_HIGH, 0); + } + } WREG32(AVIVO_D1GRPH_PRIMARY_SURFACE_ADDRESS + radeon_crtc->crtc_offset, (u32) fb_location); WREG32(AVIVO_D1GRPH_SECONDARY_SURFACE_ADDRESS + @@ -563,6 +676,10 @@ int atombios_crtc_set_base(struct drm_crtc *crtc, int x, int y, radeon_fb = to_radeon_framebuffer(old_fb); radeon_gem_object_unpin(radeon_fb->obj); } + + /* Bytes per pixel may have changed */ + radeon_bandwidth_update(rdev); + return 0; } @@ -574,134 +691,24 @@ int atombios_crtc_mode_set(struct drm_crtc *crtc, struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); struct drm_device *dev = crtc->dev; struct radeon_device *rdev = dev->dev_private; - struct drm_encoder *encoder; - SET_CRTC_TIMING_PARAMETERS_PS_ALLOCATION crtc_timing; - int need_tv_timings = 0; - bool ret; /* TODO color tiling */ - memset(&crtc_timing, 0, sizeof(crtc_timing)); - - list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { - /* find tv std */ - if (encoder->crtc == crtc) { - struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); - - if (radeon_encoder->active_device & ATOM_DEVICE_TV_SUPPORT) { - struct radeon_encoder_atom_dac *tv_dac = radeon_encoder->enc_priv; - if (tv_dac) { - if (tv_dac->tv_std == TV_STD_NTSC || - tv_dac->tv_std == TV_STD_NTSC_J || - tv_dac->tv_std == TV_STD_PAL_M) - need_tv_timings = 1; - else - need_tv_timings = 2; - break; - } - } - } - } - - crtc_timing.ucCRTC = radeon_crtc->crtc_id; - if (need_tv_timings) { - ret = radeon_atom_get_tv_timings(rdev, need_tv_timings - 1, - &crtc_timing, &adjusted_mode->clock); - if (ret == false) - need_tv_timings = 0; - } - - if (!need_tv_timings) { - crtc_timing.usH_Total = adjusted_mode->crtc_htotal; - crtc_timing.usH_Disp = adjusted_mode->crtc_hdisplay; - crtc_timing.usH_SyncStart = adjusted_mode->crtc_hsync_start; - crtc_timing.usH_SyncWidth = - adjusted_mode->crtc_hsync_end - adjusted_mode->crtc_hsync_start; - - crtc_timing.usV_Total = adjusted_mode->crtc_vtotal; - crtc_timing.usV_Disp = adjusted_mode->crtc_vdisplay; - crtc_timing.usV_SyncStart = adjusted_mode->crtc_vsync_start; - crtc_timing.usV_SyncWidth = - adjusted_mode->crtc_vsync_end - adjusted_mode->crtc_vsync_start; - - if (adjusted_mode->flags & DRM_MODE_FLAG_NVSYNC) - crtc_timing.susModeMiscInfo.usAccess |= ATOM_VSYNC_POLARITY; - - if (adjusted_mode->flags & DRM_MODE_FLAG_NHSYNC) - crtc_timing.susModeMiscInfo.usAccess |= ATOM_HSYNC_POLARITY; - - if (adjusted_mode->flags & DRM_MODE_FLAG_CSYNC) - crtc_timing.susModeMiscInfo.usAccess |= ATOM_COMPOSITESYNC; - - if (adjusted_mode->flags & DRM_MODE_FLAG_INTERLACE) - crtc_timing.susModeMiscInfo.usAccess |= ATOM_INTERLACE; - - if (adjusted_mode->flags & DRM_MODE_FLAG_DBLSCAN) - crtc_timing.susModeMiscInfo.usAccess |= ATOM_DOUBLE_CLOCK_MODE; - } + atombios_set_ss(crtc, 0); atombios_crtc_set_pll(crtc, adjusted_mode); - atombios_crtc_set_timing(crtc, &crtc_timing); + atombios_set_ss(crtc, 1); + atombios_crtc_set_timing(crtc, adjusted_mode); if (ASIC_IS_AVIVO(rdev)) atombios_crtc_set_base(crtc, x, y, old_fb); else { - if (radeon_crtc->crtc_id == 0) { - SET_CRTC_USING_DTD_TIMING_PARAMETERS crtc_dtd_timing; - memset(&crtc_dtd_timing, 0, sizeof(crtc_dtd_timing)); - - /* setup FP shadow regs on R4xx */ - crtc_dtd_timing.ucCRTC = radeon_crtc->crtc_id; - crtc_dtd_timing.usH_Size = adjusted_mode->crtc_hdisplay; - crtc_dtd_timing.usV_Size = adjusted_mode->crtc_vdisplay; - crtc_dtd_timing.usH_Blanking_Time = - adjusted_mode->crtc_hblank_end - - adjusted_mode->crtc_hdisplay; - crtc_dtd_timing.usV_Blanking_Time = - adjusted_mode->crtc_vblank_end - - adjusted_mode->crtc_vdisplay; - crtc_dtd_timing.usH_SyncOffset = - adjusted_mode->crtc_hsync_start - - adjusted_mode->crtc_hdisplay; - crtc_dtd_timing.usV_SyncOffset = - adjusted_mode->crtc_vsync_start - - adjusted_mode->crtc_vdisplay; - crtc_dtd_timing.usH_SyncWidth = - adjusted_mode->crtc_hsync_end - - adjusted_mode->crtc_hsync_start; - crtc_dtd_timing.usV_SyncWidth = - adjusted_mode->crtc_vsync_end - - adjusted_mode->crtc_vsync_start; - /* crtc_dtd_timing.ucH_Border = adjusted_mode->crtc_hborder; */ - /* crtc_dtd_timing.ucV_Border = adjusted_mode->crtc_vborder; */ - - if (adjusted_mode->flags & DRM_MODE_FLAG_NVSYNC) - crtc_dtd_timing.susModeMiscInfo.usAccess |= - ATOM_VSYNC_POLARITY; - - if (adjusted_mode->flags & DRM_MODE_FLAG_NHSYNC) - crtc_dtd_timing.susModeMiscInfo.usAccess |= - ATOM_HSYNC_POLARITY; - - if (adjusted_mode->flags & DRM_MODE_FLAG_CSYNC) - crtc_dtd_timing.susModeMiscInfo.usAccess |= - ATOM_COMPOSITESYNC; - - if (adjusted_mode->flags & DRM_MODE_FLAG_INTERLACE) - crtc_dtd_timing.susModeMiscInfo.usAccess |= - ATOM_INTERLACE; - - if (adjusted_mode->flags & DRM_MODE_FLAG_DBLSCAN) - crtc_dtd_timing.susModeMiscInfo.usAccess |= - ATOM_DOUBLE_CLOCK_MODE; - - atombios_set_crtc_dtd_timing(crtc, &crtc_dtd_timing); - } + if (radeon_crtc->crtc_id == 0) + atombios_set_crtc_dtd_timing(crtc, adjusted_mode); radeon_crtc_set_base(crtc, x, y, old_fb); radeon_legacy_atom_set_surface(crtc); } atombios_overscan_setup(crtc, mode, adjusted_mode); atombios_scaler_setup(crtc); - radeon_bandwidth_update(rdev); return 0; } @@ -733,6 +740,7 @@ static const struct drm_crtc_helper_funcs atombios_helper_funcs = { .mode_set_base = atombios_crtc_set_base, .prepare = atombios_crtc_prepare, .commit = atombios_crtc_commit, + .load_lut = radeon_crtc_load_lut, }; void radeon_atombios_init_crtc(struct drm_device *dev, diff --git a/drivers/gpu/drm/radeon/avivod.h b/drivers/gpu/drm/radeon/avivod.h index e2b92c445bab..d4e6e6e4a938 100644 --- a/drivers/gpu/drm/radeon/avivod.h +++ b/drivers/gpu/drm/radeon/avivod.h @@ -57,13 +57,4 @@ #define VGA_RENDER_CONTROL 0x0300 #define VGA_VSTATUS_CNTL_MASK 0x00030000 -/* AVIVO disable VGA rendering */ -static inline void radeon_avivo_vga_render_disable(struct radeon_device *rdev) -{ - u32 vga_render; - vga_render = RREG32(VGA_RENDER_CONTROL); - vga_render &= ~VGA_VSTATUS_CNTL_MASK; - WREG32(VGA_RENDER_CONTROL, vga_render); -} - #endif diff --git a/drivers/gpu/drm/radeon/mkregtable.c b/drivers/gpu/drm/radeon/mkregtable.c index fb211e585dea..0d79577c1576 100644 --- a/drivers/gpu/drm/radeon/mkregtable.c +++ b/drivers/gpu/drm/radeon/mkregtable.c @@ -561,7 +561,7 @@ struct table { char *gpu_prefix; }; -struct offset *offset_new(unsigned o) +static struct offset *offset_new(unsigned o) { struct offset *offset; @@ -573,12 +573,12 @@ struct offset *offset_new(unsigned o) return offset; } -void table_offset_add(struct table *t, struct offset *offset) +static void table_offset_add(struct table *t, struct offset *offset) { list_add_tail(&offset->list, &t->offsets); } -void table_init(struct table *t) +static void table_init(struct table *t) { INIT_LIST_HEAD(&t->offsets); t->offset_max = 0; @@ -586,7 +586,7 @@ void table_init(struct table *t) t->table = NULL; } -void table_print(struct table *t) +static void table_print(struct table *t) { unsigned nlloop, i, j, n, c, id; @@ -611,7 +611,7 @@ void table_print(struct table *t) printf("};\n"); } -int table_build(struct table *t) +static int table_build(struct table *t) { struct offset *offset; unsigned i, m; @@ -631,7 +631,7 @@ int table_build(struct table *t) } static char gpu_name[10]; -int parser_auth(struct table *t, const char *filename) +static int parser_auth(struct table *t, const char *filename) { FILE *file; regex_t mask_rex; diff --git a/drivers/gpu/drm/radeon/r100.c b/drivers/gpu/drm/radeon/r100.c index be51c5f7d0f6..c9e93eabcf16 100644 --- a/drivers/gpu/drm/radeon/r100.c +++ b/drivers/gpu/drm/radeon/r100.c @@ -32,6 +32,9 @@ #include "radeon_reg.h" #include "radeon.h" #include "r100d.h" +#include "rs100d.h" +#include "rv200d.h" +#include "rv250d.h" #include <linux/firmware.h> #include <linux/platform_device.h> @@ -60,18 +63,7 @@ MODULE_FIRMWARE(FIRMWARE_R520); /* This files gather functions specifics to: * r100,rv100,rs100,rv200,rs200,r200,rv250,rs300,rv280 - * - * Some of these functions might be used by newer ASICs. */ -int r200_init(struct radeon_device *rdev); -void r100_hdp_reset(struct radeon_device *rdev); -void r100_gpu_init(struct radeon_device *rdev); -int r100_gui_wait_for_idle(struct radeon_device *rdev); -int r100_mc_wait_for_idle(struct radeon_device *rdev); -void r100_gpu_wait_for_vsync(struct radeon_device *rdev); -void r100_gpu_wait_for_vsync2(struct radeon_device *rdev); -int r100_debugfs_mc_info_init(struct radeon_device *rdev); - /* * PCI GART @@ -152,136 +144,6 @@ void r100_pci_gart_fini(struct radeon_device *rdev) radeon_gart_fini(rdev); } - -/* - * MC - */ -void r100_mc_disable_clients(struct radeon_device *rdev) -{ - uint32_t ov0_scale_cntl, crtc_ext_cntl, crtc_gen_cntl, crtc2_gen_cntl; - - /* FIXME: is this function correct for rs100,rs200,rs300 ? */ - if (r100_gui_wait_for_idle(rdev)) { - printk(KERN_WARNING "Failed to wait GUI idle while " - "programming pipes. Bad things might happen.\n"); - } - - /* stop display and memory access */ - ov0_scale_cntl = RREG32(RADEON_OV0_SCALE_CNTL); - WREG32(RADEON_OV0_SCALE_CNTL, ov0_scale_cntl & ~RADEON_SCALER_ENABLE); - crtc_ext_cntl = RREG32(RADEON_CRTC_EXT_CNTL); - WREG32(RADEON_CRTC_EXT_CNTL, crtc_ext_cntl | RADEON_CRTC_DISPLAY_DIS); - crtc_gen_cntl = RREG32(RADEON_CRTC_GEN_CNTL); - - r100_gpu_wait_for_vsync(rdev); - - WREG32(RADEON_CRTC_GEN_CNTL, - (crtc_gen_cntl & ~(RADEON_CRTC_CUR_EN | RADEON_CRTC_ICON_EN)) | - RADEON_CRTC_DISP_REQ_EN_B | RADEON_CRTC_EXT_DISP_EN); - - if (!(rdev->flags & RADEON_SINGLE_CRTC)) { - crtc2_gen_cntl = RREG32(RADEON_CRTC2_GEN_CNTL); - - r100_gpu_wait_for_vsync2(rdev); - WREG32(RADEON_CRTC2_GEN_CNTL, - (crtc2_gen_cntl & - ~(RADEON_CRTC2_CUR_EN | RADEON_CRTC2_ICON_EN)) | - RADEON_CRTC2_DISP_REQ_EN_B); - } - - udelay(500); -} - -void r100_mc_setup(struct radeon_device *rdev) -{ - uint32_t tmp; - int r; - - r = r100_debugfs_mc_info_init(rdev); - if (r) { - DRM_ERROR("Failed to register debugfs file for R100 MC !\n"); - } - /* Write VRAM size in case we are limiting it */ - WREG32(RADEON_CONFIG_MEMSIZE, rdev->mc.real_vram_size); - /* Novell bug 204882 for RN50/M6/M7 with 8/16/32MB VRAM, - * if the aperture is 64MB but we have 32MB VRAM - * we report only 32MB VRAM but we have to set MC_FB_LOCATION - * to 64MB, otherwise the gpu accidentially dies */ - tmp = rdev->mc.vram_location + rdev->mc.mc_vram_size - 1; - tmp = REG_SET(RADEON_MC_FB_TOP, tmp >> 16); - tmp |= REG_SET(RADEON_MC_FB_START, rdev->mc.vram_location >> 16); - WREG32(RADEON_MC_FB_LOCATION, tmp); - - /* Enable bus mastering */ - tmp = RREG32(RADEON_BUS_CNTL) & ~RADEON_BUS_MASTER_DIS; - WREG32(RADEON_BUS_CNTL, tmp); - - if (rdev->flags & RADEON_IS_AGP) { - tmp = rdev->mc.gtt_location + rdev->mc.gtt_size - 1; - tmp = REG_SET(RADEON_MC_AGP_TOP, tmp >> 16); - tmp |= REG_SET(RADEON_MC_AGP_START, rdev->mc.gtt_location >> 16); - WREG32(RADEON_MC_AGP_LOCATION, tmp); - WREG32(RADEON_AGP_BASE, rdev->mc.agp_base); - } else { - WREG32(RADEON_MC_AGP_LOCATION, 0x0FFFFFFF); - WREG32(RADEON_AGP_BASE, 0); - } - - tmp = RREG32(RADEON_HOST_PATH_CNTL) & RADEON_HDP_APER_CNTL; - tmp |= (7 << 28); - WREG32(RADEON_HOST_PATH_CNTL, tmp | RADEON_HDP_SOFT_RESET | RADEON_HDP_READ_BUFFER_INVALIDATE); - (void)RREG32(RADEON_HOST_PATH_CNTL); - WREG32(RADEON_HOST_PATH_CNTL, tmp); - (void)RREG32(RADEON_HOST_PATH_CNTL); -} - -int r100_mc_init(struct radeon_device *rdev) -{ - int r; - - if (r100_debugfs_rbbm_init(rdev)) { - DRM_ERROR("Failed to register debugfs file for RBBM !\n"); - } - - r100_gpu_init(rdev); - /* Disable gart which also disable out of gart access */ - r100_pci_gart_disable(rdev); - - /* Setup GPU memory space */ - rdev->mc.gtt_location = 0xFFFFFFFFUL; - if (rdev->flags & RADEON_IS_AGP) { - r = radeon_agp_init(rdev); - if (r) { - printk(KERN_WARNING "[drm] Disabling AGP\n"); - rdev->flags &= ~RADEON_IS_AGP; - rdev->mc.gtt_size = radeon_gart_size * 1024 * 1024; - } else { - rdev->mc.gtt_location = rdev->mc.agp_base; - } - } - r = radeon_mc_setup(rdev); - if (r) { - return r; - } - - r100_mc_disable_clients(rdev); - if (r100_mc_wait_for_idle(rdev)) { - printk(KERN_WARNING "Failed to wait MC idle while " - "programming pipes. Bad things might happen.\n"); - } - - r100_mc_setup(rdev); - return 0; -} - -void r100_mc_fini(struct radeon_device *rdev) -{ -} - - -/* - * Interrupts - */ int r100_irq_set(struct radeon_device *rdev) { uint32_t tmp = 0; @@ -324,7 +186,7 @@ static inline uint32_t r100_irq_ack(struct radeon_device *rdev) int r100_irq_process(struct radeon_device *rdev) { - uint32_t status; + uint32_t status, msi_rearm; status = r100_irq_ack(rdev); if (!status) { @@ -347,6 +209,21 @@ int r100_irq_process(struct radeon_device *rdev) } status = r100_irq_ack(rdev); } + if (rdev->msi_enabled) { + switch (rdev->family) { + case CHIP_RS400: + case CHIP_RS480: + msi_rearm = RREG32(RADEON_AIC_CNTL) & ~RS400_MSI_REARM; + WREG32(RADEON_AIC_CNTL, msi_rearm); + WREG32(RADEON_AIC_CNTL, msi_rearm | RS400_MSI_REARM); + break; + default: + msi_rearm = RREG32(RADEON_MSI_REARM_EN) & ~RV370_MSI_REARM_EN; + WREG32(RADEON_MSI_REARM_EN, msi_rearm); + WREG32(RADEON_MSI_REARM_EN, msi_rearm | RV370_MSI_REARM_EN); + break; + } + } return IRQ_HANDLED; } @@ -358,10 +235,6 @@ u32 r100_get_vblank_counter(struct radeon_device *rdev, int crtc) return RREG32(RADEON_CRTC2_CRNT_FRAME); } - -/* - * Fence emission - */ void r100_fence_ring_emit(struct radeon_device *rdev, struct radeon_fence *fence) { @@ -377,16 +250,12 @@ void r100_fence_ring_emit(struct radeon_device *rdev, radeon_ring_write(rdev, RADEON_SW_INT_FIRE); } - -/* - * Writeback - */ int r100_wb_init(struct radeon_device *rdev) { int r; if (rdev->wb.wb_obj == NULL) { - r = radeon_object_create(rdev, NULL, 4096, + r = radeon_object_create(rdev, NULL, RADEON_GPU_PAGE_SIZE, true, RADEON_GEM_DOMAIN_GTT, false, &rdev->wb.wb_obj); @@ -504,10 +373,6 @@ int r100_copy_blit(struct radeon_device *rdev, return r; } - -/* - * CP - */ static int r100_cp_wait_for_idle(struct radeon_device *rdev) { unsigned i; @@ -612,6 +477,7 @@ static int r100_cp_init_microcode(struct radeon_device *rdev) } return err; } + static void r100_cp_load_microcode(struct radeon_device *rdev) { const __be32 *fw_data; @@ -712,19 +578,19 @@ int r100_cp_init(struct radeon_device *rdev, unsigned ring_size) indirect1_start = 16; /* cp setup */ WREG32(0x718, pre_write_timer | (pre_write_limit << 28)); - WREG32(RADEON_CP_RB_CNTL, -#ifdef __BIG_ENDIAN - RADEON_BUF_SWAP_32BIT | -#endif - REG_SET(RADEON_RB_BUFSZ, rb_bufsz) | + tmp = (REG_SET(RADEON_RB_BUFSZ, rb_bufsz) | REG_SET(RADEON_RB_BLKSZ, rb_blksz) | REG_SET(RADEON_MAX_FETCH, max_fetch) | RADEON_RB_NO_UPDATE); +#ifdef __BIG_ENDIAN + tmp |= RADEON_BUF_SWAP_32BIT; +#endif + WREG32(RADEON_CP_RB_CNTL, tmp); + /* Set ring address */ DRM_INFO("radeon: ring at 0x%016lX\n", (unsigned long)rdev->cp.gpu_addr); WREG32(RADEON_CP_RB_BASE, rdev->cp.gpu_addr); /* Force read & write ptr to 0 */ - tmp = RREG32(RADEON_CP_RB_CNTL); WREG32(RADEON_CP_RB_CNTL, tmp | RADEON_RB_RPTR_WR_ENA); WREG32(RADEON_CP_RB_RPTR_WR, 0); WREG32(RADEON_CP_RB_WPTR, 0); @@ -863,13 +729,11 @@ int r100_cs_parse_packet0(struct radeon_cs_parser *p, void r100_cs_dump_packet(struct radeon_cs_parser *p, struct radeon_cs_packet *pkt) { - struct radeon_cs_chunk *ib_chunk; volatile uint32_t *ib; unsigned i; unsigned idx; ib = p->ib->ptr; - ib_chunk = &p->chunks[p->chunk_ib_idx]; idx = pkt->idx; for (i = 0; i <= (pkt->count + 1); i++, idx++) { DRM_INFO("ib[%d]=0x%08X\n", idx, ib[idx]); @@ -896,7 +760,7 @@ int r100_cs_packet_parse(struct radeon_cs_parser *p, idx, ib_chunk->length_dw); return -EINVAL; } - header = ib_chunk->kdata[idx]; + header = radeon_get_ib_value(p, idx); pkt->idx = idx; pkt->type = CP_PACKET_GET_TYPE(header); pkt->count = CP_PACKET_GET_COUNT(header); @@ -939,7 +803,6 @@ int r100_cs_packet_parse(struct radeon_cs_parser *p, */ int r100_cs_packet_parse_vline(struct radeon_cs_parser *p) { - struct radeon_cs_chunk *ib_chunk; struct drm_mode_object *obj; struct drm_crtc *crtc; struct radeon_crtc *radeon_crtc; @@ -947,8 +810,9 @@ int r100_cs_packet_parse_vline(struct radeon_cs_parser *p) int crtc_id; int r; uint32_t header, h_idx, reg; + volatile uint32_t *ib; - ib_chunk = &p->chunks[p->chunk_ib_idx]; + ib = p->ib->ptr; /* parse the wait until */ r = r100_cs_packet_parse(p, &waitreloc, p->idx); @@ -963,24 +827,24 @@ int r100_cs_packet_parse_vline(struct radeon_cs_parser *p) return r; } - if (ib_chunk->kdata[waitreloc.idx + 1] != RADEON_WAIT_CRTC_VLINE) { + if (radeon_get_ib_value(p, waitreloc.idx + 1) != RADEON_WAIT_CRTC_VLINE) { DRM_ERROR("vline wait had illegal wait until\n"); r = -EINVAL; return r; } /* jump over the NOP */ - r = r100_cs_packet_parse(p, &p3reloc, p->idx); + r = r100_cs_packet_parse(p, &p3reloc, p->idx + waitreloc.count + 2); if (r) return r; h_idx = p->idx - 2; - p->idx += waitreloc.count; - p->idx += p3reloc.count; + p->idx += waitreloc.count + 2; + p->idx += p3reloc.count + 2; - header = ib_chunk->kdata[h_idx]; - crtc_id = ib_chunk->kdata[h_idx + 5]; - reg = ib_chunk->kdata[h_idx] >> 2; + header = radeon_get_ib_value(p, h_idx); + crtc_id = radeon_get_ib_value(p, h_idx + 5); + reg = CP_PACKET0_GET_REG(header); mutex_lock(&p->rdev->ddev->mode_config.mutex); obj = drm_mode_object_find(p->rdev->ddev, crtc_id, DRM_MODE_OBJECT_CRTC); if (!obj) { @@ -994,16 +858,16 @@ int r100_cs_packet_parse_vline(struct radeon_cs_parser *p) if (!crtc->enabled) { /* if the CRTC isn't enabled - we need to nop out the wait until */ - ib_chunk->kdata[h_idx + 2] = PACKET2(0); - ib_chunk->kdata[h_idx + 3] = PACKET2(0); + ib[h_idx + 2] = PACKET2(0); + ib[h_idx + 3] = PACKET2(0); } else if (crtc_id == 1) { switch (reg) { case AVIVO_D1MODE_VLINE_START_END: - header &= R300_CP_PACKET0_REG_MASK; + header &= ~R300_CP_PACKET0_REG_MASK; header |= AVIVO_D2MODE_VLINE_START_END >> 2; break; case RADEON_CRTC_GUI_TRIG_VLINE: - header &= R300_CP_PACKET0_REG_MASK; + header &= ~R300_CP_PACKET0_REG_MASK; header |= RADEON_CRTC2_GUI_TRIG_VLINE >> 2; break; default: @@ -1011,8 +875,8 @@ int r100_cs_packet_parse_vline(struct radeon_cs_parser *p) r = -EINVAL; goto out; } - ib_chunk->kdata[h_idx] = header; - ib_chunk->kdata[h_idx + 3] |= RADEON_ENG_DISPLAY_SELECT_CRTC1; + ib[h_idx] = header; + ib[h_idx + 3] |= RADEON_ENG_DISPLAY_SELECT_CRTC1; } out: mutex_unlock(&p->rdev->ddev->mode_config.mutex); @@ -1033,7 +897,6 @@ out: int r100_cs_packet_next_reloc(struct radeon_cs_parser *p, struct radeon_cs_reloc **cs_reloc) { - struct radeon_cs_chunk *ib_chunk; struct radeon_cs_chunk *relocs_chunk; struct radeon_cs_packet p3reloc; unsigned idx; @@ -1044,7 +907,6 @@ int r100_cs_packet_next_reloc(struct radeon_cs_parser *p, return -EINVAL; } *cs_reloc = NULL; - ib_chunk = &p->chunks[p->chunk_ib_idx]; relocs_chunk = &p->chunks[p->chunk_relocs_idx]; r = r100_cs_packet_parse(p, &p3reloc, p->idx); if (r) { @@ -1057,7 +919,7 @@ int r100_cs_packet_next_reloc(struct radeon_cs_parser *p, r100_cs_dump_packet(p, &p3reloc); return -EINVAL; } - idx = ib_chunk->kdata[p3reloc.idx + 1]; + idx = radeon_get_ib_value(p, p3reloc.idx + 1); if (idx >= relocs_chunk->length_dw) { DRM_ERROR("Relocs at %d after relocations chunk end %d !\n", idx, relocs_chunk->length_dw); @@ -1126,7 +988,6 @@ static int r100_packet0_check(struct radeon_cs_parser *p, struct radeon_cs_packet *pkt, unsigned idx, unsigned reg) { - struct radeon_cs_chunk *ib_chunk; struct radeon_cs_reloc *reloc; struct r100_cs_track *track; volatile uint32_t *ib; @@ -1134,11 +995,13 @@ static int r100_packet0_check(struct radeon_cs_parser *p, int r; int i, face; u32 tile_flags = 0; + u32 idx_value; ib = p->ib->ptr; - ib_chunk = &p->chunks[p->chunk_ib_idx]; track = (struct r100_cs_track *)p->track; + idx_value = radeon_get_ib_value(p, idx); + switch (reg) { case RADEON_CRTC_GUI_TRIG_VLINE: r = r100_cs_packet_parse_vline(p); @@ -1166,8 +1029,8 @@ static int r100_packet0_check(struct radeon_cs_parser *p, return r; } track->zb.robj = reloc->robj; - track->zb.offset = ib_chunk->kdata[idx]; - ib[idx] = ib_chunk->kdata[idx] + ((u32)reloc->lobj.gpu_offset); + track->zb.offset = idx_value; + ib[idx] = idx_value + ((u32)reloc->lobj.gpu_offset); break; case RADEON_RB3D_COLOROFFSET: r = r100_cs_packet_next_reloc(p, &reloc); @@ -1178,8 +1041,8 @@ static int r100_packet0_check(struct radeon_cs_parser *p, return r; } track->cb[0].robj = reloc->robj; - track->cb[0].offset = ib_chunk->kdata[idx]; - ib[idx] = ib_chunk->kdata[idx] + ((u32)reloc->lobj.gpu_offset); + track->cb[0].offset = idx_value; + ib[idx] = idx_value + ((u32)reloc->lobj.gpu_offset); break; case RADEON_PP_TXOFFSET_0: case RADEON_PP_TXOFFSET_1: @@ -1192,7 +1055,7 @@ static int r100_packet0_check(struct radeon_cs_parser *p, r100_cs_dump_packet(p, pkt); return r; } - ib[idx] = ib_chunk->kdata[idx] + ((u32)reloc->lobj.gpu_offset); + ib[idx] = idx_value + ((u32)reloc->lobj.gpu_offset); track->textures[i].robj = reloc->robj; break; case RADEON_PP_CUBIC_OFFSET_T0_0: @@ -1208,8 +1071,8 @@ static int r100_packet0_check(struct radeon_cs_parser *p, r100_cs_dump_packet(p, pkt); return r; } - track->textures[0].cube_info[i].offset = ib_chunk->kdata[idx]; - ib[idx] = ib_chunk->kdata[idx] + ((u32)reloc->lobj.gpu_offset); + track->textures[0].cube_info[i].offset = idx_value; + ib[idx] = idx_value + ((u32)reloc->lobj.gpu_offset); track->textures[0].cube_info[i].robj = reloc->robj; break; case RADEON_PP_CUBIC_OFFSET_T1_0: @@ -1225,8 +1088,8 @@ static int r100_packet0_check(struct radeon_cs_parser *p, r100_cs_dump_packet(p, pkt); return r; } - track->textures[1].cube_info[i].offset = ib_chunk->kdata[idx]; - ib[idx] = ib_chunk->kdata[idx] + ((u32)reloc->lobj.gpu_offset); + track->textures[1].cube_info[i].offset = idx_value; + ib[idx] = idx_value + ((u32)reloc->lobj.gpu_offset); track->textures[1].cube_info[i].robj = reloc->robj; break; case RADEON_PP_CUBIC_OFFSET_T2_0: @@ -1242,12 +1105,12 @@ static int r100_packet0_check(struct radeon_cs_parser *p, r100_cs_dump_packet(p, pkt); return r; } - track->textures[2].cube_info[i].offset = ib_chunk->kdata[idx]; - ib[idx] = ib_chunk->kdata[idx] + ((u32)reloc->lobj.gpu_offset); + track->textures[2].cube_info[i].offset = idx_value; + ib[idx] = idx_value + ((u32)reloc->lobj.gpu_offset); track->textures[2].cube_info[i].robj = reloc->robj; break; case RADEON_RE_WIDTH_HEIGHT: - track->maxy = ((ib_chunk->kdata[idx] >> 16) & 0x7FF); + track->maxy = ((idx_value >> 16) & 0x7FF); break; case RADEON_RB3D_COLORPITCH: r = r100_cs_packet_next_reloc(p, &reloc); @@ -1263,17 +1126,17 @@ static int r100_packet0_check(struct radeon_cs_parser *p, if (reloc->lobj.tiling_flags & RADEON_TILING_MICRO) tile_flags |= RADEON_COLOR_MICROTILE_ENABLE; - tmp = ib_chunk->kdata[idx] & ~(0x7 << 16); + tmp = idx_value & ~(0x7 << 16); tmp |= tile_flags; ib[idx] = tmp; - track->cb[0].pitch = ib_chunk->kdata[idx] & RADEON_COLORPITCH_MASK; + track->cb[0].pitch = idx_value & RADEON_COLORPITCH_MASK; break; case RADEON_RB3D_DEPTHPITCH: - track->zb.pitch = ib_chunk->kdata[idx] & RADEON_DEPTHPITCH_MASK; + track->zb.pitch = idx_value & RADEON_DEPTHPITCH_MASK; break; case RADEON_RB3D_CNTL: - switch ((ib_chunk->kdata[idx] >> RADEON_RB3D_COLOR_FORMAT_SHIFT) & 0x1f) { + switch ((idx_value >> RADEON_RB3D_COLOR_FORMAT_SHIFT) & 0x1f) { case 7: case 8: case 9: @@ -1291,13 +1154,13 @@ static int r100_packet0_check(struct radeon_cs_parser *p, break; default: DRM_ERROR("Invalid color buffer format (%d) !\n", - ((ib_chunk->kdata[idx] >> RADEON_RB3D_COLOR_FORMAT_SHIFT) & 0x1f)); + ((idx_value >> RADEON_RB3D_COLOR_FORMAT_SHIFT) & 0x1f)); return -EINVAL; } - track->z_enabled = !!(ib_chunk->kdata[idx] & RADEON_Z_ENABLE); + track->z_enabled = !!(idx_value & RADEON_Z_ENABLE); break; case RADEON_RB3D_ZSTENCILCNTL: - switch (ib_chunk->kdata[idx] & 0xf) { + switch (idx_value & 0xf) { case 0: track->zb.cpp = 2; break; @@ -1321,44 +1184,44 @@ static int r100_packet0_check(struct radeon_cs_parser *p, r100_cs_dump_packet(p, pkt); return r; } - ib[idx] = ib_chunk->kdata[idx] + ((u32)reloc->lobj.gpu_offset); + ib[idx] = idx_value + ((u32)reloc->lobj.gpu_offset); break; case RADEON_PP_CNTL: { - uint32_t temp = ib_chunk->kdata[idx] >> 4; + uint32_t temp = idx_value >> 4; for (i = 0; i < track->num_texture; i++) track->textures[i].enabled = !!(temp & (1 << i)); } break; case RADEON_SE_VF_CNTL: - track->vap_vf_cntl = ib_chunk->kdata[idx]; + track->vap_vf_cntl = idx_value; break; case RADEON_SE_VTX_FMT: - track->vtx_size = r100_get_vtx_size(ib_chunk->kdata[idx]); + track->vtx_size = r100_get_vtx_size(idx_value); break; case RADEON_PP_TEX_SIZE_0: case RADEON_PP_TEX_SIZE_1: case RADEON_PP_TEX_SIZE_2: i = (reg - RADEON_PP_TEX_SIZE_0) / 8; - track->textures[i].width = (ib_chunk->kdata[idx] & RADEON_TEX_USIZE_MASK) + 1; - track->textures[i].height = ((ib_chunk->kdata[idx] & RADEON_TEX_VSIZE_MASK) >> RADEON_TEX_VSIZE_SHIFT) + 1; + track->textures[i].width = (idx_value & RADEON_TEX_USIZE_MASK) + 1; + track->textures[i].height = ((idx_value & RADEON_TEX_VSIZE_MASK) >> RADEON_TEX_VSIZE_SHIFT) + 1; break; case RADEON_PP_TEX_PITCH_0: case RADEON_PP_TEX_PITCH_1: case RADEON_PP_TEX_PITCH_2: i = (reg - RADEON_PP_TEX_PITCH_0) / 8; - track->textures[i].pitch = ib_chunk->kdata[idx] + 32; + track->textures[i].pitch = idx_value + 32; break; case RADEON_PP_TXFILTER_0: case RADEON_PP_TXFILTER_1: case RADEON_PP_TXFILTER_2: i = (reg - RADEON_PP_TXFILTER_0) / 24; - track->textures[i].num_levels = ((ib_chunk->kdata[idx] & RADEON_MAX_MIP_LEVEL_MASK) + track->textures[i].num_levels = ((idx_value & RADEON_MAX_MIP_LEVEL_MASK) >> RADEON_MAX_MIP_LEVEL_SHIFT); - tmp = (ib_chunk->kdata[idx] >> 23) & 0x7; + tmp = (idx_value >> 23) & 0x7; if (tmp == 2 || tmp == 6) track->textures[i].roundup_w = false; - tmp = (ib_chunk->kdata[idx] >> 27) & 0x7; + tmp = (idx_value >> 27) & 0x7; if (tmp == 2 || tmp == 6) track->textures[i].roundup_h = false; break; @@ -1366,16 +1229,16 @@ static int r100_packet0_check(struct radeon_cs_parser *p, case RADEON_PP_TXFORMAT_1: case RADEON_PP_TXFORMAT_2: i = (reg - RADEON_PP_TXFORMAT_0) / 24; - if (ib_chunk->kdata[idx] & RADEON_TXFORMAT_NON_POWER2) { + if (idx_value & RADEON_TXFORMAT_NON_POWER2) { track->textures[i].use_pitch = 1; } else { track->textures[i].use_pitch = 0; - track->textures[i].width = 1 << ((ib_chunk->kdata[idx] >> RADEON_TXFORMAT_WIDTH_SHIFT) & RADEON_TXFORMAT_WIDTH_MASK); - track->textures[i].height = 1 << ((ib_chunk->kdata[idx] >> RADEON_TXFORMAT_HEIGHT_SHIFT) & RADEON_TXFORMAT_HEIGHT_MASK); + track->textures[i].width = 1 << ((idx_value >> RADEON_TXFORMAT_WIDTH_SHIFT) & RADEON_TXFORMAT_WIDTH_MASK); + track->textures[i].height = 1 << ((idx_value >> RADEON_TXFORMAT_HEIGHT_SHIFT) & RADEON_TXFORMAT_HEIGHT_MASK); } - if (ib_chunk->kdata[idx] & RADEON_TXFORMAT_CUBIC_MAP_ENABLE) + if (idx_value & RADEON_TXFORMAT_CUBIC_MAP_ENABLE) track->textures[i].tex_coord_type = 2; - switch ((ib_chunk->kdata[idx] & RADEON_TXFORMAT_FORMAT_MASK)) { + switch ((idx_value & RADEON_TXFORMAT_FORMAT_MASK)) { case RADEON_TXFORMAT_I8: case RADEON_TXFORMAT_RGB332: case RADEON_TXFORMAT_Y8: @@ -1402,13 +1265,13 @@ static int r100_packet0_check(struct radeon_cs_parser *p, track->textures[i].cpp = 4; break; } - track->textures[i].cube_info[4].width = 1 << ((ib_chunk->kdata[idx] >> 16) & 0xf); - track->textures[i].cube_info[4].height = 1 << ((ib_chunk->kdata[idx] >> 20) & 0xf); + track->textures[i].cube_info[4].width = 1 << ((idx_value >> 16) & 0xf); + track->textures[i].cube_info[4].height = 1 << ((idx_value >> 20) & 0xf); break; case RADEON_PP_CUBIC_FACES_0: case RADEON_PP_CUBIC_FACES_1: case RADEON_PP_CUBIC_FACES_2: - tmp = ib_chunk->kdata[idx]; + tmp = idx_value; i = (reg - RADEON_PP_CUBIC_FACES_0) / 4; for (face = 0; face < 4; face++) { track->textures[i].cube_info[face].width = 1 << ((tmp >> (face * 8)) & 0xf); @@ -1427,15 +1290,14 @@ int r100_cs_track_check_pkt3_indx_buffer(struct radeon_cs_parser *p, struct radeon_cs_packet *pkt, struct radeon_object *robj) { - struct radeon_cs_chunk *ib_chunk; unsigned idx; - - ib_chunk = &p->chunks[p->chunk_ib_idx]; + u32 value; idx = pkt->idx + 1; - if ((ib_chunk->kdata[idx+2] + 1) > radeon_object_size(robj)) { + value = radeon_get_ib_value(p, idx + 2); + if ((value + 1) > radeon_object_size(robj)) { DRM_ERROR("[drm] Buffer too small for PACKET3 INDX_BUFFER " "(need %u have %lu) !\n", - ib_chunk->kdata[idx+2] + 1, + value + 1, radeon_object_size(robj)); return -EINVAL; } @@ -1445,59 +1307,20 @@ int r100_cs_track_check_pkt3_indx_buffer(struct radeon_cs_parser *p, static int r100_packet3_check(struct radeon_cs_parser *p, struct radeon_cs_packet *pkt) { - struct radeon_cs_chunk *ib_chunk; struct radeon_cs_reloc *reloc; struct r100_cs_track *track; unsigned idx; - unsigned i, c; volatile uint32_t *ib; int r; ib = p->ib->ptr; - ib_chunk = &p->chunks[p->chunk_ib_idx]; idx = pkt->idx + 1; track = (struct r100_cs_track *)p->track; switch (pkt->opcode) { case PACKET3_3D_LOAD_VBPNTR: - c = ib_chunk->kdata[idx++]; - track->num_arrays = c; - for (i = 0; i < (c - 1); i += 2, idx += 3) { - r = r100_cs_packet_next_reloc(p, &reloc); - if (r) { - DRM_ERROR("No reloc for packet3 %d\n", - pkt->opcode); - r100_cs_dump_packet(p, pkt); - return r; - } - ib[idx+1] = ib_chunk->kdata[idx+1] + ((u32)reloc->lobj.gpu_offset); - track->arrays[i + 0].robj = reloc->robj; - track->arrays[i + 0].esize = ib_chunk->kdata[idx] >> 8; - track->arrays[i + 0].esize &= 0x7F; - r = r100_cs_packet_next_reloc(p, &reloc); - if (r) { - DRM_ERROR("No reloc for packet3 %d\n", - pkt->opcode); - r100_cs_dump_packet(p, pkt); - return r; - } - ib[idx+2] = ib_chunk->kdata[idx+2] + ((u32)reloc->lobj.gpu_offset); - track->arrays[i + 1].robj = reloc->robj; - track->arrays[i + 1].esize = ib_chunk->kdata[idx] >> 24; - track->arrays[i + 1].esize &= 0x7F; - } - if (c & 1) { - r = r100_cs_packet_next_reloc(p, &reloc); - if (r) { - DRM_ERROR("No reloc for packet3 %d\n", - pkt->opcode); - r100_cs_dump_packet(p, pkt); - return r; - } - ib[idx+1] = ib_chunk->kdata[idx+1] + ((u32)reloc->lobj.gpu_offset); - track->arrays[i + 0].robj = reloc->robj; - track->arrays[i + 0].esize = ib_chunk->kdata[idx] >> 8; - track->arrays[i + 0].esize &= 0x7F; - } + r = r100_packet3_load_vbpntr(p, pkt, idx); + if (r) + return r; break; case PACKET3_INDX_BUFFER: r = r100_cs_packet_next_reloc(p, &reloc); @@ -1506,7 +1329,7 @@ static int r100_packet3_check(struct radeon_cs_parser *p, r100_cs_dump_packet(p, pkt); return r; } - ib[idx+1] = ib_chunk->kdata[idx+1] + ((u32)reloc->lobj.gpu_offset); + ib[idx+1] = radeon_get_ib_value(p, idx+1) + ((u32)reloc->lobj.gpu_offset); r = r100_cs_track_check_pkt3_indx_buffer(p, pkt, reloc->robj); if (r) { return r; @@ -1520,27 +1343,27 @@ static int r100_packet3_check(struct radeon_cs_parser *p, r100_cs_dump_packet(p, pkt); return r; } - ib[idx] = ib_chunk->kdata[idx] + ((u32)reloc->lobj.gpu_offset); + ib[idx] = radeon_get_ib_value(p, idx) + ((u32)reloc->lobj.gpu_offset); track->num_arrays = 1; - track->vtx_size = r100_get_vtx_size(ib_chunk->kdata[idx+2]); + track->vtx_size = r100_get_vtx_size(radeon_get_ib_value(p, idx + 2)); track->arrays[0].robj = reloc->robj; track->arrays[0].esize = track->vtx_size; - track->max_indx = ib_chunk->kdata[idx+1]; + track->max_indx = radeon_get_ib_value(p, idx+1); - track->vap_vf_cntl = ib_chunk->kdata[idx+3]; + track->vap_vf_cntl = radeon_get_ib_value(p, idx+3); track->immd_dwords = pkt->count - 1; r = r100_cs_track_check(p->rdev, track); if (r) return r; break; case PACKET3_3D_DRAW_IMMD: - if (((ib_chunk->kdata[idx+1] >> 4) & 0x3) != 3) { + if (((radeon_get_ib_value(p, idx + 1) >> 4) & 0x3) != 3) { DRM_ERROR("PRIM_WALK must be 3 for IMMD draw\n"); return -EINVAL; } - track->vap_vf_cntl = ib_chunk->kdata[idx+1]; + track->vap_vf_cntl = radeon_get_ib_value(p, idx + 1); track->immd_dwords = pkt->count - 1; r = r100_cs_track_check(p->rdev, track); if (r) @@ -1548,11 +1371,11 @@ static int r100_packet3_check(struct radeon_cs_parser *p, break; /* triggers drawing using in-packet vertex data */ case PACKET3_3D_DRAW_IMMD_2: - if (((ib_chunk->kdata[idx] >> 4) & 0x3) != 3) { + if (((radeon_get_ib_value(p, idx) >> 4) & 0x3) != 3) { DRM_ERROR("PRIM_WALK must be 3 for IMMD draw\n"); return -EINVAL; } - track->vap_vf_cntl = ib_chunk->kdata[idx]; + track->vap_vf_cntl = radeon_get_ib_value(p, idx); track->immd_dwords = pkt->count; r = r100_cs_track_check(p->rdev, track); if (r) @@ -1560,28 +1383,28 @@ static int r100_packet3_check(struct radeon_cs_parser *p, break; /* triggers drawing using in-packet vertex data */ case PACKET3_3D_DRAW_VBUF_2: - track->vap_vf_cntl = ib_chunk->kdata[idx]; + track->vap_vf_cntl = radeon_get_ib_value(p, idx); r = r100_cs_track_check(p->rdev, track); if (r) return r; break; /* triggers drawing of vertex buffers setup elsewhere */ case PACKET3_3D_DRAW_INDX_2: - track->vap_vf_cntl = ib_chunk->kdata[idx]; + track->vap_vf_cntl = radeon_get_ib_value(p, idx); r = r100_cs_track_check(p->rdev, track); if (r) return r; break; /* triggers drawing using indices to vertex buffer */ case PACKET3_3D_DRAW_VBUF: - track->vap_vf_cntl = ib_chunk->kdata[idx + 1]; + track->vap_vf_cntl = radeon_get_ib_value(p, idx + 1); r = r100_cs_track_check(p->rdev, track); if (r) return r; break; /* triggers drawing of vertex buffers setup elsewhere */ case PACKET3_3D_DRAW_INDX: - track->vap_vf_cntl = ib_chunk->kdata[idx + 1]; + track->vap_vf_cntl = radeon_get_ib_value(p, idx + 1); r = r100_cs_track_check(p->rdev, track); if (r) return r; @@ -2033,7 +1856,7 @@ void r100_pll_wreg(struct radeon_device *rdev, uint32_t reg, uint32_t v) r100_pll_errata_after_data(rdev); } -int r100_init(struct radeon_device *rdev) +void r100_set_safe_registers(struct radeon_device *rdev) { if (ASIC_IS_RN50(rdev)) { rdev->config.r100.reg_safe_bm = rn50_reg_safe_bm; @@ -2042,9 +1865,8 @@ int r100_init(struct radeon_device *rdev) rdev->config.r100.reg_safe_bm = r100_reg_safe_bm; rdev->config.r100.reg_safe_bm_size = ARRAY_SIZE(r100_reg_safe_bm); } else { - return r200_init(rdev); + r200_set_safe_registers(rdev); } - return 0; } /* @@ -2342,9 +2164,11 @@ void r100_bandwidth_update(struct radeon_device *rdev) mode1 = &rdev->mode_info.crtcs[0]->base.mode; pixel_bytes1 = rdev->mode_info.crtcs[0]->base.fb->bits_per_pixel / 8; } - if (rdev->mode_info.crtcs[1]->base.enabled) { - mode2 = &rdev->mode_info.crtcs[1]->base.mode; - pixel_bytes2 = rdev->mode_info.crtcs[1]->base.fb->bits_per_pixel / 8; + if (!(rdev->flags & RADEON_SINGLE_CRTC)) { + if (rdev->mode_info.crtcs[1]->base.enabled) { + mode2 = &rdev->mode_info.crtcs[1]->base.mode; + pixel_bytes2 = rdev->mode_info.crtcs[1]->base.fb->bits_per_pixel / 8; + } } min_mem_eff.full = rfixed_const_8(0); @@ -2555,7 +2379,7 @@ void r100_bandwidth_update(struct radeon_device *rdev) /* Find the total latency for the display data. */ - disp_latency_overhead.full = rfixed_const(80); + disp_latency_overhead.full = rfixed_const(8); disp_latency_overhead.full = rfixed_div(disp_latency_overhead, sclk_ff); mc_latency_mclk.full += disp_latency_overhead.full + cur_latency_mclk.full; mc_latency_sclk.full += disp_latency_overhead.full + cur_latency_sclk.full; @@ -2753,8 +2577,11 @@ void r100_bandwidth_update(struct radeon_device *rdev) static inline void r100_cs_track_texture_print(struct r100_cs_track_texture *t) { DRM_ERROR("pitch %d\n", t->pitch); + DRM_ERROR("use_pitch %d\n", t->use_pitch); DRM_ERROR("width %d\n", t->width); + DRM_ERROR("width_11 %d\n", t->width_11); DRM_ERROR("height %d\n", t->height); + DRM_ERROR("height_11 %d\n", t->height_11); DRM_ERROR("num levels %d\n", t->num_levels); DRM_ERROR("depth %d\n", t->txdepth); DRM_ERROR("bpp %d\n", t->cpp); @@ -2814,15 +2641,17 @@ static int r100_cs_track_texture_check(struct radeon_device *rdev, else w = track->textures[u].pitch / (1 << i); } else { - w = track->textures[u].width / (1 << i); + w = track->textures[u].width; if (rdev->family >= CHIP_RV515) w |= track->textures[u].width_11; + w = w / (1 << i); if (track->textures[u].roundup_w) w = roundup_pow_of_two(w); } - h = track->textures[u].height / (1 << i); + h = track->textures[u].height; if (rdev->family >= CHIP_RV515) h |= track->textures[u].height_11; + h = h / (1 << i); if (track->textures[u].roundup_h) h = roundup_pow_of_two(h); size += w * h; @@ -3157,7 +2986,7 @@ void r100_mc_stop(struct radeon_device *rdev, struct r100_mc_save *save) WREG32(R_000740_CP_CSQ_CNTL, 0); /* Save few CRTC registers */ - save->GENMO_WT = RREG32(R_0003C0_GENMO_WT); + save->GENMO_WT = RREG8(R_0003C2_GENMO_WT); save->CRTC_EXT_CNTL = RREG32(R_000054_CRTC_EXT_CNTL); save->CRTC_GEN_CNTL = RREG32(R_000050_CRTC_GEN_CNTL); save->CUR_OFFSET = RREG32(R_000260_CUR_OFFSET); @@ -3167,7 +2996,7 @@ void r100_mc_stop(struct radeon_device *rdev, struct r100_mc_save *save) } /* Disable VGA aperture access */ - WREG32(R_0003C0_GENMO_WT, C_0003C0_VGA_RAM_EN & save->GENMO_WT); + WREG8(R_0003C2_GENMO_WT, C_0003C2_VGA_RAM_EN & save->GENMO_WT); /* Disable cursor, overlay, crtc */ WREG32(R_000260_CUR_OFFSET, save->CUR_OFFSET | S_000260_CUR_LOCK(1)); WREG32(R_000054_CRTC_EXT_CNTL, save->CRTC_EXT_CNTL | @@ -3199,10 +3028,264 @@ void r100_mc_resume(struct radeon_device *rdev, struct r100_mc_save *save) rdev->mc.vram_location); } /* Restore CRTC registers */ - WREG32(R_0003C0_GENMO_WT, save->GENMO_WT); + WREG8(R_0003C2_GENMO_WT, save->GENMO_WT); WREG32(R_000054_CRTC_EXT_CNTL, save->CRTC_EXT_CNTL); WREG32(R_000050_CRTC_GEN_CNTL, save->CRTC_GEN_CNTL); if (!(rdev->flags & RADEON_SINGLE_CRTC)) { WREG32(R_0003F8_CRTC2_GEN_CNTL, save->CRTC2_GEN_CNTL); } } + +void r100_vga_render_disable(struct radeon_device *rdev) +{ + u32 tmp; + + tmp = RREG8(R_0003C2_GENMO_WT); + WREG8(R_0003C2_GENMO_WT, C_0003C2_VGA_RAM_EN & tmp); +} + +static void r100_debugfs(struct radeon_device *rdev) +{ + int r; + + r = r100_debugfs_mc_info_init(rdev); + if (r) + dev_warn(rdev->dev, "Failed to create r100_mc debugfs file.\n"); +} + +static void r100_mc_program(struct radeon_device *rdev) +{ + struct r100_mc_save save; + + /* Stops all mc clients */ + r100_mc_stop(rdev, &save); + if (rdev->flags & RADEON_IS_AGP) { + WREG32(R_00014C_MC_AGP_LOCATION, + S_00014C_MC_AGP_START(rdev->mc.gtt_start >> 16) | + S_00014C_MC_AGP_TOP(rdev->mc.gtt_end >> 16)); + WREG32(R_000170_AGP_BASE, lower_32_bits(rdev->mc.agp_base)); + if (rdev->family > CHIP_RV200) + WREG32(R_00015C_AGP_BASE_2, + upper_32_bits(rdev->mc.agp_base) & 0xff); + } else { + WREG32(R_00014C_MC_AGP_LOCATION, 0x0FFFFFFF); + WREG32(R_000170_AGP_BASE, 0); + if (rdev->family > CHIP_RV200) + WREG32(R_00015C_AGP_BASE_2, 0); + } + /* Wait for mc idle */ + if (r100_mc_wait_for_idle(rdev)) + dev_warn(rdev->dev, "Wait for MC idle timeout.\n"); + /* Program MC, should be a 32bits limited address space */ + WREG32(R_000148_MC_FB_LOCATION, + S_000148_MC_FB_START(rdev->mc.vram_start >> 16) | + S_000148_MC_FB_TOP(rdev->mc.vram_end >> 16)); + r100_mc_resume(rdev, &save); +} + +void r100_clock_startup(struct radeon_device *rdev) +{ + u32 tmp; + + if (radeon_dynclks != -1 && radeon_dynclks) + radeon_legacy_set_clock_gating(rdev, 1); + /* We need to force on some of the block */ + tmp = RREG32_PLL(R_00000D_SCLK_CNTL); + tmp |= S_00000D_FORCE_CP(1) | S_00000D_FORCE_VIP(1); + if ((rdev->family == CHIP_RV250) || (rdev->family == CHIP_RV280)) + tmp |= S_00000D_FORCE_DISP1(1) | S_00000D_FORCE_DISP2(1); + WREG32_PLL(R_00000D_SCLK_CNTL, tmp); +} + +static int r100_startup(struct radeon_device *rdev) +{ + int r; + + r100_mc_program(rdev); + /* Resume clock */ + r100_clock_startup(rdev); + /* Initialize GPU configuration (# pipes, ...) */ + r100_gpu_init(rdev); + /* Initialize GART (initialize after TTM so we can allocate + * memory through TTM but finalize after TTM) */ + if (rdev->flags & RADEON_IS_PCI) { + r = r100_pci_gart_enable(rdev); + if (r) + return r; + } + /* Enable IRQ */ + rdev->irq.sw_int = true; + r100_irq_set(rdev); + /* 1M ring buffer */ + r = r100_cp_init(rdev, 1024 * 1024); + if (r) { + dev_err(rdev->dev, "failled initializing CP (%d).\n", r); + return r; + } + r = r100_wb_init(rdev); + if (r) + dev_err(rdev->dev, "failled initializing WB (%d).\n", r); + r = r100_ib_init(rdev); + if (r) { + dev_err(rdev->dev, "failled initializing IB (%d).\n", r); + return r; + } + return 0; +} + +int r100_resume(struct radeon_device *rdev) +{ + /* Make sur GART are not working */ + if (rdev->flags & RADEON_IS_PCI) + r100_pci_gart_disable(rdev); + /* Resume clock before doing reset */ + r100_clock_startup(rdev); + /* Reset gpu before posting otherwise ATOM will enter infinite loop */ + if (radeon_gpu_reset(rdev)) { + dev_warn(rdev->dev, "GPU reset failed ! (0xE40=0x%08X, 0x7C0=0x%08X)\n", + RREG32(R_000E40_RBBM_STATUS), + RREG32(R_0007C0_CP_STAT)); + } + /* post */ + radeon_combios_asic_init(rdev->ddev); + /* Resume clock after posting */ + r100_clock_startup(rdev); + return r100_startup(rdev); +} + +int r100_suspend(struct radeon_device *rdev) +{ + r100_cp_disable(rdev); + r100_wb_disable(rdev); + r100_irq_disable(rdev); + if (rdev->flags & RADEON_IS_PCI) + r100_pci_gart_disable(rdev); + return 0; +} + +void r100_fini(struct radeon_device *rdev) +{ + r100_suspend(rdev); + r100_cp_fini(rdev); + r100_wb_fini(rdev); + r100_ib_fini(rdev); + radeon_gem_fini(rdev); + if (rdev->flags & RADEON_IS_PCI) + r100_pci_gart_fini(rdev); + radeon_irq_kms_fini(rdev); + radeon_fence_driver_fini(rdev); + radeon_object_fini(rdev); + radeon_atombios_fini(rdev); + kfree(rdev->bios); + rdev->bios = NULL; +} + +int r100_mc_init(struct radeon_device *rdev) +{ + int r; + u32 tmp; + + /* Setup GPU memory space */ + rdev->mc.vram_location = 0xFFFFFFFFUL; + rdev->mc.gtt_location = 0xFFFFFFFFUL; + if (rdev->flags & RADEON_IS_IGP) { + tmp = G_00015C_MC_FB_START(RREG32(R_00015C_NB_TOM)); + rdev->mc.vram_location = tmp << 16; + } + if (rdev->flags & RADEON_IS_AGP) { + r = radeon_agp_init(rdev); + if (r) { + printk(KERN_WARNING "[drm] Disabling AGP\n"); + rdev->flags &= ~RADEON_IS_AGP; + rdev->mc.gtt_size = radeon_gart_size * 1024 * 1024; + } else { + rdev->mc.gtt_location = rdev->mc.agp_base; + } + } + r = radeon_mc_setup(rdev); + if (r) + return r; + return 0; +} + +int r100_init(struct radeon_device *rdev) +{ + int r; + + /* Register debugfs file specific to this group of asics */ + r100_debugfs(rdev); + /* Disable VGA */ + r100_vga_render_disable(rdev); + /* Initialize scratch registers */ + radeon_scratch_init(rdev); + /* Initialize surface registers */ + radeon_surface_init(rdev); + /* TODO: disable VGA need to use VGA request */ + /* BIOS*/ + if (!radeon_get_bios(rdev)) { + if (ASIC_IS_AVIVO(rdev)) + return -EINVAL; + } + if (rdev->is_atom_bios) { + dev_err(rdev->dev, "Expecting combios for RS400/RS480 GPU\n"); + return -EINVAL; + } else { + r = radeon_combios_init(rdev); + if (r) + return r; + } + /* Reset gpu before posting otherwise ATOM will enter infinite loop */ + if (radeon_gpu_reset(rdev)) { + dev_warn(rdev->dev, + "GPU reset failed ! (0xE40=0x%08X, 0x7C0=0x%08X)\n", + RREG32(R_000E40_RBBM_STATUS), + RREG32(R_0007C0_CP_STAT)); + } + /* check if cards are posted or not */ + if (!radeon_card_posted(rdev) && rdev->bios) { + DRM_INFO("GPU not posted. posting now...\n"); + radeon_combios_asic_init(rdev->ddev); + } + /* Set asic errata */ + r100_errata(rdev); + /* Initialize clocks */ + radeon_get_clock_info(rdev->ddev); + /* Get vram informations */ + r100_vram_info(rdev); + /* Initialize memory controller (also test AGP) */ + r = r100_mc_init(rdev); + if (r) + return r; + /* Fence driver */ + r = radeon_fence_driver_init(rdev); + if (r) + return r; + r = radeon_irq_kms_init(rdev); + if (r) + return r; + /* Memory manager */ + r = radeon_object_init(rdev); + if (r) + return r; + if (rdev->flags & RADEON_IS_PCI) { + r = r100_pci_gart_init(rdev); + if (r) + return r; + } + r100_set_safe_registers(rdev); + rdev->accel_working = true; + r = r100_startup(rdev); + if (r) { + /* Somethings want wront with the accel init stop accel */ + dev_err(rdev->dev, "Disabling GPU acceleration\n"); + r100_suspend(rdev); + r100_cp_fini(rdev); + r100_wb_fini(rdev); + r100_ib_fini(rdev); + if (rdev->flags & RADEON_IS_PCI) + r100_pci_gart_fini(rdev); + radeon_irq_kms_fini(rdev); + rdev->accel_working = false; + } + return 0; +} diff --git a/drivers/gpu/drm/radeon/r100_track.h b/drivers/gpu/drm/radeon/r100_track.h index 70a82eda394a..0daf0d76a891 100644 --- a/drivers/gpu/drm/radeon/r100_track.h +++ b/drivers/gpu/drm/radeon/r100_track.h @@ -84,6 +84,8 @@ int r200_packet0_check(struct radeon_cs_parser *p, struct radeon_cs_packet *pkt, unsigned idx, unsigned reg); + + static inline int r100_reloc_pitch_offset(struct radeon_cs_parser *p, struct radeon_cs_packet *pkt, unsigned idx, @@ -93,9 +95,7 @@ static inline int r100_reloc_pitch_offset(struct radeon_cs_parser *p, u32 tile_flags = 0; u32 tmp; struct radeon_cs_reloc *reloc; - struct radeon_cs_chunk *ib_chunk; - - ib_chunk = &p->chunks[p->chunk_ib_idx]; + u32 value; r = r100_cs_packet_next_reloc(p, &reloc); if (r) { @@ -104,7 +104,8 @@ static inline int r100_reloc_pitch_offset(struct radeon_cs_parser *p, r100_cs_dump_packet(p, pkt); return r; } - tmp = ib_chunk->kdata[idx] & 0x003fffff; + value = radeon_get_ib_value(p, idx); + tmp = value & 0x003fffff; tmp += (((u32)reloc->lobj.gpu_offset) >> 10); if (reloc->lobj.tiling_flags & RADEON_TILING_MACRO) @@ -119,6 +120,64 @@ static inline int r100_reloc_pitch_offset(struct radeon_cs_parser *p, } tmp |= tile_flags; - p->ib->ptr[idx] = (ib_chunk->kdata[idx] & 0x3fc00000) | tmp; + p->ib->ptr[idx] = (value & 0x3fc00000) | tmp; return 0; } + +static inline int r100_packet3_load_vbpntr(struct radeon_cs_parser *p, + struct radeon_cs_packet *pkt, + int idx) +{ + unsigned c, i; + struct radeon_cs_reloc *reloc; + struct r100_cs_track *track; + int r = 0; + volatile uint32_t *ib; + u32 idx_value; + + ib = p->ib->ptr; + track = (struct r100_cs_track *)p->track; + c = radeon_get_ib_value(p, idx++) & 0x1F; + track->num_arrays = c; + for (i = 0; i < (c - 1); i+=2, idx+=3) { + r = r100_cs_packet_next_reloc(p, &reloc); + if (r) { + DRM_ERROR("No reloc for packet3 %d\n", + pkt->opcode); + r100_cs_dump_packet(p, pkt); + return r; + } + idx_value = radeon_get_ib_value(p, idx); + ib[idx+1] = radeon_get_ib_value(p, idx + 1) + ((u32)reloc->lobj.gpu_offset); + + track->arrays[i + 0].esize = idx_value >> 8; + track->arrays[i + 0].robj = reloc->robj; + track->arrays[i + 0].esize &= 0x7F; + r = r100_cs_packet_next_reloc(p, &reloc); + if (r) { + DRM_ERROR("No reloc for packet3 %d\n", + pkt->opcode); + r100_cs_dump_packet(p, pkt); + return r; + } + ib[idx+2] = radeon_get_ib_value(p, idx + 2) + ((u32)reloc->lobj.gpu_offset); + track->arrays[i + 1].robj = reloc->robj; + track->arrays[i + 1].esize = idx_value >> 24; + track->arrays[i + 1].esize &= 0x7F; + } + if (c & 1) { + r = r100_cs_packet_next_reloc(p, &reloc); + if (r) { + DRM_ERROR("No reloc for packet3 %d\n", + pkt->opcode); + r100_cs_dump_packet(p, pkt); + return r; + } + idx_value = radeon_get_ib_value(p, idx); + ib[idx+1] = radeon_get_ib_value(p, idx + 1) + ((u32)reloc->lobj.gpu_offset); + track->arrays[i + 0].robj = reloc->robj; + track->arrays[i + 0].esize = idx_value >> 8; + track->arrays[i + 0].esize &= 0x7F; + } + return r; +} diff --git a/drivers/gpu/drm/radeon/r100d.h b/drivers/gpu/drm/radeon/r100d.h index c4b257ec920e..df29a630c466 100644 --- a/drivers/gpu/drm/radeon/r100d.h +++ b/drivers/gpu/drm/radeon/r100d.h @@ -381,6 +381,24 @@ #define S_000054_VCRTC_IDX_MASTER(x) (((x) & 0x7F) << 24) #define G_000054_VCRTC_IDX_MASTER(x) (((x) >> 24) & 0x7F) #define C_000054_VCRTC_IDX_MASTER 0x80FFFFFF +#define R_000148_MC_FB_LOCATION 0x000148 +#define S_000148_MC_FB_START(x) (((x) & 0xFFFF) << 0) +#define G_000148_MC_FB_START(x) (((x) >> 0) & 0xFFFF) +#define C_000148_MC_FB_START 0xFFFF0000 +#define S_000148_MC_FB_TOP(x) (((x) & 0xFFFF) << 16) +#define G_000148_MC_FB_TOP(x) (((x) >> 16) & 0xFFFF) +#define C_000148_MC_FB_TOP 0x0000FFFF +#define R_00014C_MC_AGP_LOCATION 0x00014C +#define S_00014C_MC_AGP_START(x) (((x) & 0xFFFF) << 0) +#define G_00014C_MC_AGP_START(x) (((x) >> 0) & 0xFFFF) +#define C_00014C_MC_AGP_START 0xFFFF0000 +#define S_00014C_MC_AGP_TOP(x) (((x) & 0xFFFF) << 16) +#define G_00014C_MC_AGP_TOP(x) (((x) >> 16) & 0xFFFF) +#define C_00014C_MC_AGP_TOP 0x0000FFFF +#define R_000170_AGP_BASE 0x000170 +#define S_000170_AGP_BASE_ADDR(x) (((x) & 0xFFFFFFFF) << 0) +#define G_000170_AGP_BASE_ADDR(x) (((x) >> 0) & 0xFFFFFFFF) +#define C_000170_AGP_BASE_ADDR 0x00000000 #define R_00023C_DISPLAY_BASE_ADDR 0x00023C #define S_00023C_DISPLAY_BASE_ADDR(x) (((x) & 0xFFFFFFFF) << 0) #define G_00023C_DISPLAY_BASE_ADDR(x) (((x) >> 0) & 0xFFFFFFFF) @@ -403,25 +421,25 @@ #define S_000360_CUR2_LOCK(x) (((x) & 0x1) << 31) #define G_000360_CUR2_LOCK(x) (((x) >> 31) & 0x1) #define C_000360_CUR2_LOCK 0x7FFFFFFF -#define R_0003C0_GENMO_WT 0x0003C0 -#define S_0003C0_GENMO_MONO_ADDRESS_B(x) (((x) & 0x1) << 0) -#define G_0003C0_GENMO_MONO_ADDRESS_B(x) (((x) >> 0) & 0x1) -#define C_0003C0_GENMO_MONO_ADDRESS_B 0xFFFFFFFE -#define S_0003C0_VGA_RAM_EN(x) (((x) & 0x1) << 1) -#define G_0003C0_VGA_RAM_EN(x) (((x) >> 1) & 0x1) -#define C_0003C0_VGA_RAM_EN 0xFFFFFFFD -#define S_0003C0_VGA_CKSEL(x) (((x) & 0x3) << 2) -#define G_0003C0_VGA_CKSEL(x) (((x) >> 2) & 0x3) -#define C_0003C0_VGA_CKSEL 0xFFFFFFF3 -#define S_0003C0_ODD_EVEN_MD_PGSEL(x) (((x) & 0x1) << 5) -#define G_0003C0_ODD_EVEN_MD_PGSEL(x) (((x) >> 5) & 0x1) -#define C_0003C0_ODD_EVEN_MD_PGSEL 0xFFFFFFDF -#define S_0003C0_VGA_HSYNC_POL(x) (((x) & 0x1) << 6) -#define G_0003C0_VGA_HSYNC_POL(x) (((x) >> 6) & 0x1) -#define C_0003C0_VGA_HSYNC_POL 0xFFFFFFBF -#define S_0003C0_VGA_VSYNC_POL(x) (((x) & 0x1) << 7) -#define G_0003C0_VGA_VSYNC_POL(x) (((x) >> 7) & 0x1) -#define C_0003C0_VGA_VSYNC_POL 0xFFFFFF7F +#define R_0003C2_GENMO_WT 0x0003C0 +#define S_0003C2_GENMO_MONO_ADDRESS_B(x) (((x) & 0x1) << 0) +#define G_0003C2_GENMO_MONO_ADDRESS_B(x) (((x) >> 0) & 0x1) +#define C_0003C2_GENMO_MONO_ADDRESS_B 0xFE +#define S_0003C2_VGA_RAM_EN(x) (((x) & 0x1) << 1) +#define G_0003C2_VGA_RAM_EN(x) (((x) >> 1) & 0x1) +#define C_0003C2_VGA_RAM_EN 0xFD +#define S_0003C2_VGA_CKSEL(x) (((x) & 0x3) << 2) +#define G_0003C2_VGA_CKSEL(x) (((x) >> 2) & 0x3) +#define C_0003C2_VGA_CKSEL 0xF3 +#define S_0003C2_ODD_EVEN_MD_PGSEL(x) (((x) & 0x1) << 5) +#define G_0003C2_ODD_EVEN_MD_PGSEL(x) (((x) >> 5) & 0x1) +#define C_0003C2_ODD_EVEN_MD_PGSEL 0xDF +#define S_0003C2_VGA_HSYNC_POL(x) (((x) & 0x1) << 6) +#define G_0003C2_VGA_HSYNC_POL(x) (((x) >> 6) & 0x1) +#define C_0003C2_VGA_HSYNC_POL 0xBF +#define S_0003C2_VGA_VSYNC_POL(x) (((x) & 0x1) << 7) +#define G_0003C2_VGA_VSYNC_POL(x) (((x) >> 7) & 0x1) +#define C_0003C2_VGA_VSYNC_POL 0x7F #define R_0003F8_CRTC2_GEN_CNTL 0x0003F8 #define S_0003F8_CRTC2_DBL_SCAN_EN(x) (((x) & 0x1) << 0) #define G_0003F8_CRTC2_DBL_SCAN_EN(x) (((x) >> 0) & 0x1) @@ -545,6 +563,46 @@ #define S_000774_SCRATCH_ADDR(x) (((x) & 0x7FFFFFF) << 5) #define G_000774_SCRATCH_ADDR(x) (((x) >> 5) & 0x7FFFFFF) #define C_000774_SCRATCH_ADDR 0x0000001F +#define R_0007C0_CP_STAT 0x0007C0 +#define S_0007C0_MRU_BUSY(x) (((x) & 0x1) << 0) +#define G_0007C0_MRU_BUSY(x) (((x) >> 0) & 0x1) +#define C_0007C0_MRU_BUSY 0xFFFFFFFE +#define S_0007C0_MWU_BUSY(x) (((x) & 0x1) << 1) +#define G_0007C0_MWU_BUSY(x) (((x) >> 1) & 0x1) +#define C_0007C0_MWU_BUSY 0xFFFFFFFD +#define S_0007C0_RSIU_BUSY(x) (((x) & 0x1) << 2) +#define G_0007C0_RSIU_BUSY(x) (((x) >> 2) & 0x1) +#define C_0007C0_RSIU_BUSY 0xFFFFFFFB +#define S_0007C0_RCIU_BUSY(x) (((x) & 0x1) << 3) +#define G_0007C0_RCIU_BUSY(x) (((x) >> 3) & 0x1) +#define C_0007C0_RCIU_BUSY 0xFFFFFFF7 +#define S_0007C0_CSF_PRIMARY_BUSY(x) (((x) & 0x1) << 9) +#define G_0007C0_CSF_PRIMARY_BUSY(x) (((x) >> 9) & 0x1) +#define C_0007C0_CSF_PRIMARY_BUSY 0xFFFFFDFF +#define S_0007C0_CSF_INDIRECT_BUSY(x) (((x) & 0x1) << 10) +#define G_0007C0_CSF_INDIRECT_BUSY(x) (((x) >> 10) & 0x1) +#define C_0007C0_CSF_INDIRECT_BUSY 0xFFFFFBFF +#define S_0007C0_CSQ_PRIMARY_BUSY(x) (((x) & 0x1) << 11) +#define G_0007C0_CSQ_PRIMARY_BUSY(x) (((x) >> 11) & 0x1) +#define C_0007C0_CSQ_PRIMARY_BUSY 0xFFFFF7FF +#define S_0007C0_CSQ_INDIRECT_BUSY(x) (((x) & 0x1) << 12) +#define G_0007C0_CSQ_INDIRECT_BUSY(x) (((x) >> 12) & 0x1) +#define C_0007C0_CSQ_INDIRECT_BUSY 0xFFFFEFFF +#define S_0007C0_CSI_BUSY(x) (((x) & 0x1) << 13) +#define G_0007C0_CSI_BUSY(x) (((x) >> 13) & 0x1) +#define C_0007C0_CSI_BUSY 0xFFFFDFFF +#define S_0007C0_GUIDMA_BUSY(x) (((x) & 0x1) << 28) +#define G_0007C0_GUIDMA_BUSY(x) (((x) >> 28) & 0x1) +#define C_0007C0_GUIDMA_BUSY 0xEFFFFFFF +#define S_0007C0_VIDDMA_BUSY(x) (((x) & 0x1) << 29) +#define G_0007C0_VIDDMA_BUSY(x) (((x) >> 29) & 0x1) +#define C_0007C0_VIDDMA_BUSY 0xDFFFFFFF +#define S_0007C0_CMDSTRM_BUSY(x) (((x) & 0x1) << 30) +#define G_0007C0_CMDSTRM_BUSY(x) (((x) >> 30) & 0x1) +#define C_0007C0_CMDSTRM_BUSY 0xBFFFFFFF +#define S_0007C0_CP_BUSY(x) (((x) & 0x1) << 31) +#define G_0007C0_CP_BUSY(x) (((x) >> 31) & 0x1) +#define C_0007C0_CP_BUSY 0x7FFFFFFF #define R_000E40_RBBM_STATUS 0x000E40 #define S_000E40_CMDFIFO_AVAIL(x) (((x) & 0x7F) << 0) #define G_000E40_CMDFIFO_AVAIL(x) (((x) >> 0) & 0x7F) @@ -604,4 +662,53 @@ #define G_000E40_GUI_ACTIVE(x) (((x) >> 31) & 0x1) #define C_000E40_GUI_ACTIVE 0x7FFFFFFF + +#define R_00000D_SCLK_CNTL 0x00000D +#define S_00000D_SCLK_SRC_SEL(x) (((x) & 0x7) << 0) +#define G_00000D_SCLK_SRC_SEL(x) (((x) >> 0) & 0x7) +#define C_00000D_SCLK_SRC_SEL 0xFFFFFFF8 +#define S_00000D_TCLK_SRC_SEL(x) (((x) & 0x7) << 8) +#define G_00000D_TCLK_SRC_SEL(x) (((x) >> 8) & 0x7) +#define C_00000D_TCLK_SRC_SEL 0xFFFFF8FF +#define S_00000D_FORCE_CP(x) (((x) & 0x1) << 16) +#define G_00000D_FORCE_CP(x) (((x) >> 16) & 0x1) +#define C_00000D_FORCE_CP 0xFFFEFFFF +#define S_00000D_FORCE_HDP(x) (((x) & 0x1) << 17) +#define G_00000D_FORCE_HDP(x) (((x) >> 17) & 0x1) +#define C_00000D_FORCE_HDP 0xFFFDFFFF +#define S_00000D_FORCE_DISP(x) (((x) & 0x1) << 18) +#define G_00000D_FORCE_DISP(x) (((x) >> 18) & 0x1) +#define C_00000D_FORCE_DISP 0xFFFBFFFF +#define S_00000D_FORCE_TOP(x) (((x) & 0x1) << 19) +#define G_00000D_FORCE_TOP(x) (((x) >> 19) & 0x1) +#define C_00000D_FORCE_TOP 0xFFF7FFFF +#define S_00000D_FORCE_E2(x) (((x) & 0x1) << 20) +#define G_00000D_FORCE_E2(x) (((x) >> 20) & 0x1) +#define C_00000D_FORCE_E2 0xFFEFFFFF +#define S_00000D_FORCE_SE(x) (((x) & 0x1) << 21) +#define G_00000D_FORCE_SE(x) (((x) >> 21) & 0x1) +#define C_00000D_FORCE_SE 0xFFDFFFFF +#define S_00000D_FORCE_IDCT(x) (((x) & 0x1) << 22) +#define G_00000D_FORCE_IDCT(x) (((x) >> 22) & 0x1) +#define C_00000D_FORCE_IDCT 0xFFBFFFFF +#define S_00000D_FORCE_VIP(x) (((x) & 0x1) << 23) +#define G_00000D_FORCE_VIP(x) (((x) >> 23) & 0x1) +#define C_00000D_FORCE_VIP 0xFF7FFFFF +#define S_00000D_FORCE_RE(x) (((x) & 0x1) << 24) +#define G_00000D_FORCE_RE(x) (((x) >> 24) & 0x1) +#define C_00000D_FORCE_RE 0xFEFFFFFF +#define S_00000D_FORCE_PB(x) (((x) & 0x1) << 25) +#define G_00000D_FORCE_PB(x) (((x) >> 25) & 0x1) +#define C_00000D_FORCE_PB 0xFDFFFFFF +#define S_00000D_FORCE_TAM(x) (((x) & 0x1) << 26) +#define G_00000D_FORCE_TAM(x) (((x) >> 26) & 0x1) +#define C_00000D_FORCE_TAM 0xFBFFFFFF +#define S_00000D_FORCE_TDM(x) (((x) & 0x1) << 27) +#define G_00000D_FORCE_TDM(x) (((x) >> 27) & 0x1) +#define C_00000D_FORCE_TDM 0xF7FFFFFF +#define S_00000D_FORCE_RB(x) (((x) & 0x1) << 28) +#define G_00000D_FORCE_RB(x) (((x) >> 28) & 0x1) +#define C_00000D_FORCE_RB 0xEFFFFFFF + + #endif diff --git a/drivers/gpu/drm/radeon/r200.c b/drivers/gpu/drm/radeon/r200.c index 568c74bfba3d..eb740fc3549f 100644 --- a/drivers/gpu/drm/radeon/r200.c +++ b/drivers/gpu/drm/radeon/r200.c @@ -96,7 +96,6 @@ int r200_packet0_check(struct radeon_cs_parser *p, struct radeon_cs_packet *pkt, unsigned idx, unsigned reg) { - struct radeon_cs_chunk *ib_chunk; struct radeon_cs_reloc *reloc; struct r100_cs_track *track; volatile uint32_t *ib; @@ -105,11 +104,11 @@ int r200_packet0_check(struct radeon_cs_parser *p, int i; int face; u32 tile_flags = 0; + u32 idx_value; ib = p->ib->ptr; - ib_chunk = &p->chunks[p->chunk_ib_idx]; track = (struct r100_cs_track *)p->track; - + idx_value = radeon_get_ib_value(p, idx); switch (reg) { case RADEON_CRTC_GUI_TRIG_VLINE: r = r100_cs_packet_parse_vline(p); @@ -137,8 +136,8 @@ int r200_packet0_check(struct radeon_cs_parser *p, return r; } track->zb.robj = reloc->robj; - track->zb.offset = ib_chunk->kdata[idx]; - ib[idx] = ib_chunk->kdata[idx] + ((u32)reloc->lobj.gpu_offset); + track->zb.offset = idx_value; + ib[idx] = idx_value + ((u32)reloc->lobj.gpu_offset); break; case RADEON_RB3D_COLOROFFSET: r = r100_cs_packet_next_reloc(p, &reloc); @@ -149,8 +148,8 @@ int r200_packet0_check(struct radeon_cs_parser *p, return r; } track->cb[0].robj = reloc->robj; - track->cb[0].offset = ib_chunk->kdata[idx]; - ib[idx] = ib_chunk->kdata[idx] + ((u32)reloc->lobj.gpu_offset); + track->cb[0].offset = idx_value; + ib[idx] = idx_value + ((u32)reloc->lobj.gpu_offset); break; case R200_PP_TXOFFSET_0: case R200_PP_TXOFFSET_1: @@ -166,7 +165,7 @@ int r200_packet0_check(struct radeon_cs_parser *p, r100_cs_dump_packet(p, pkt); return r; } - ib[idx] = ib_chunk->kdata[idx] + ((u32)reloc->lobj.gpu_offset); + ib[idx] = idx_value + ((u32)reloc->lobj.gpu_offset); track->textures[i].robj = reloc->robj; break; case R200_PP_CUBIC_OFFSET_F1_0: @@ -208,12 +207,12 @@ int r200_packet0_check(struct radeon_cs_parser *p, r100_cs_dump_packet(p, pkt); return r; } - track->textures[i].cube_info[face - 1].offset = ib_chunk->kdata[idx]; - ib[idx] = ib_chunk->kdata[idx] + ((u32)reloc->lobj.gpu_offset); + track->textures[i].cube_info[face - 1].offset = idx_value; + ib[idx] = idx_value + ((u32)reloc->lobj.gpu_offset); track->textures[i].cube_info[face - 1].robj = reloc->robj; break; case RADEON_RE_WIDTH_HEIGHT: - track->maxy = ((ib_chunk->kdata[idx] >> 16) & 0x7FF); + track->maxy = ((idx_value >> 16) & 0x7FF); break; case RADEON_RB3D_COLORPITCH: r = r100_cs_packet_next_reloc(p, &reloc); @@ -229,17 +228,17 @@ int r200_packet0_check(struct radeon_cs_parser *p, if (reloc->lobj.tiling_flags & RADEON_TILING_MICRO) tile_flags |= RADEON_COLOR_MICROTILE_ENABLE; - tmp = ib_chunk->kdata[idx] & ~(0x7 << 16); + tmp = idx_value & ~(0x7 << 16); tmp |= tile_flags; ib[idx] = tmp; - track->cb[0].pitch = ib_chunk->kdata[idx] & RADEON_COLORPITCH_MASK; + track->cb[0].pitch = idx_value & RADEON_COLORPITCH_MASK; break; case RADEON_RB3D_DEPTHPITCH: - track->zb.pitch = ib_chunk->kdata[idx] & RADEON_DEPTHPITCH_MASK; + track->zb.pitch = idx_value & RADEON_DEPTHPITCH_MASK; break; case RADEON_RB3D_CNTL: - switch ((ib_chunk->kdata[idx] >> RADEON_RB3D_COLOR_FORMAT_SHIFT) & 0x1f) { + switch ((idx_value >> RADEON_RB3D_COLOR_FORMAT_SHIFT) & 0x1f) { case 7: case 8: case 9: @@ -257,18 +256,18 @@ int r200_packet0_check(struct radeon_cs_parser *p, break; default: DRM_ERROR("Invalid color buffer format (%d) !\n", - ((ib_chunk->kdata[idx] >> RADEON_RB3D_COLOR_FORMAT_SHIFT) & 0x1f)); + ((idx_value >> RADEON_RB3D_COLOR_FORMAT_SHIFT) & 0x1f)); return -EINVAL; } - if (ib_chunk->kdata[idx] & RADEON_DEPTHXY_OFFSET_ENABLE) { + if (idx_value & RADEON_DEPTHXY_OFFSET_ENABLE) { DRM_ERROR("No support for depth xy offset in kms\n"); return -EINVAL; } - track->z_enabled = !!(ib_chunk->kdata[idx] & RADEON_Z_ENABLE); + track->z_enabled = !!(idx_value & RADEON_Z_ENABLE); break; case RADEON_RB3D_ZSTENCILCNTL: - switch (ib_chunk->kdata[idx] & 0xf) { + switch (idx_value & 0xf) { case 0: track->zb.cpp = 2; break; @@ -292,27 +291,27 @@ int r200_packet0_check(struct radeon_cs_parser *p, r100_cs_dump_packet(p, pkt); return r; } - ib[idx] = ib_chunk->kdata[idx] + ((u32)reloc->lobj.gpu_offset); + ib[idx] = idx_value + ((u32)reloc->lobj.gpu_offset); break; case RADEON_PP_CNTL: { - uint32_t temp = ib_chunk->kdata[idx] >> 4; + uint32_t temp = idx_value >> 4; for (i = 0; i < track->num_texture; i++) track->textures[i].enabled = !!(temp & (1 << i)); } break; case RADEON_SE_VF_CNTL: - track->vap_vf_cntl = ib_chunk->kdata[idx]; + track->vap_vf_cntl = idx_value; break; case 0x210c: /* VAP_VF_MAX_VTX_INDX */ - track->max_indx = ib_chunk->kdata[idx] & 0x00FFFFFFUL; + track->max_indx = idx_value & 0x00FFFFFFUL; break; case R200_SE_VTX_FMT_0: - track->vtx_size = r200_get_vtx_size_0(ib_chunk->kdata[idx]); + track->vtx_size = r200_get_vtx_size_0(idx_value); break; case R200_SE_VTX_FMT_1: - track->vtx_size += r200_get_vtx_size_1(ib_chunk->kdata[idx]); + track->vtx_size += r200_get_vtx_size_1(idx_value); break; case R200_PP_TXSIZE_0: case R200_PP_TXSIZE_1: @@ -321,8 +320,8 @@ int r200_packet0_check(struct radeon_cs_parser *p, case R200_PP_TXSIZE_4: case R200_PP_TXSIZE_5: i = (reg - R200_PP_TXSIZE_0) / 32; - track->textures[i].width = (ib_chunk->kdata[idx] & RADEON_TEX_USIZE_MASK) + 1; - track->textures[i].height = ((ib_chunk->kdata[idx] & RADEON_TEX_VSIZE_MASK) >> RADEON_TEX_VSIZE_SHIFT) + 1; + track->textures[i].width = (idx_value & RADEON_TEX_USIZE_MASK) + 1; + track->textures[i].height = ((idx_value & RADEON_TEX_VSIZE_MASK) >> RADEON_TEX_VSIZE_SHIFT) + 1; break; case R200_PP_TXPITCH_0: case R200_PP_TXPITCH_1: @@ -331,7 +330,7 @@ int r200_packet0_check(struct radeon_cs_parser *p, case R200_PP_TXPITCH_4: case R200_PP_TXPITCH_5: i = (reg - R200_PP_TXPITCH_0) / 32; - track->textures[i].pitch = ib_chunk->kdata[idx] + 32; + track->textures[i].pitch = idx_value + 32; break; case R200_PP_TXFILTER_0: case R200_PP_TXFILTER_1: @@ -340,12 +339,12 @@ int r200_packet0_check(struct radeon_cs_parser *p, case R200_PP_TXFILTER_4: case R200_PP_TXFILTER_5: i = (reg - R200_PP_TXFILTER_0) / 32; - track->textures[i].num_levels = ((ib_chunk->kdata[idx] & R200_MAX_MIP_LEVEL_MASK) + track->textures[i].num_levels = ((idx_value & R200_MAX_MIP_LEVEL_MASK) >> R200_MAX_MIP_LEVEL_SHIFT); - tmp = (ib_chunk->kdata[idx] >> 23) & 0x7; + tmp = (idx_value >> 23) & 0x7; if (tmp == 2 || tmp == 6) track->textures[i].roundup_w = false; - tmp = (ib_chunk->kdata[idx] >> 27) & 0x7; + tmp = (idx_value >> 27) & 0x7; if (tmp == 2 || tmp == 6) track->textures[i].roundup_h = false; break; @@ -364,8 +363,8 @@ int r200_packet0_check(struct radeon_cs_parser *p, case R200_PP_TXFORMAT_X_4: case R200_PP_TXFORMAT_X_5: i = (reg - R200_PP_TXFORMAT_X_0) / 32; - track->textures[i].txdepth = ib_chunk->kdata[idx] & 0x7; - tmp = (ib_chunk->kdata[idx] >> 16) & 0x3; + track->textures[i].txdepth = idx_value & 0x7; + tmp = (idx_value >> 16) & 0x3; /* 2D, 3D, CUBE */ switch (tmp) { case 0: @@ -389,14 +388,14 @@ int r200_packet0_check(struct radeon_cs_parser *p, case R200_PP_TXFORMAT_4: case R200_PP_TXFORMAT_5: i = (reg - R200_PP_TXFORMAT_0) / 32; - if (ib_chunk->kdata[idx] & R200_TXFORMAT_NON_POWER2) { + if (idx_value & R200_TXFORMAT_NON_POWER2) { track->textures[i].use_pitch = 1; } else { track->textures[i].use_pitch = 0; - track->textures[i].width = 1 << ((ib_chunk->kdata[idx] >> RADEON_TXFORMAT_WIDTH_SHIFT) & RADEON_TXFORMAT_WIDTH_MASK); - track->textures[i].height = 1 << ((ib_chunk->kdata[idx] >> RADEON_TXFORMAT_HEIGHT_SHIFT) & RADEON_TXFORMAT_HEIGHT_MASK); + track->textures[i].width = 1 << ((idx_value >> RADEON_TXFORMAT_WIDTH_SHIFT) & RADEON_TXFORMAT_WIDTH_MASK); + track->textures[i].height = 1 << ((idx_value >> RADEON_TXFORMAT_HEIGHT_SHIFT) & RADEON_TXFORMAT_HEIGHT_MASK); } - switch ((ib_chunk->kdata[idx] & RADEON_TXFORMAT_FORMAT_MASK)) { + switch ((idx_value & RADEON_TXFORMAT_FORMAT_MASK)) { case R200_TXFORMAT_I8: case R200_TXFORMAT_RGB332: case R200_TXFORMAT_Y8: @@ -424,8 +423,8 @@ int r200_packet0_check(struct radeon_cs_parser *p, track->textures[i].cpp = 4; break; } - track->textures[i].cube_info[4].width = 1 << ((ib_chunk->kdata[idx] >> 16) & 0xf); - track->textures[i].cube_info[4].height = 1 << ((ib_chunk->kdata[idx] >> 20) & 0xf); + track->textures[i].cube_info[4].width = 1 << ((idx_value >> 16) & 0xf); + track->textures[i].cube_info[4].height = 1 << ((idx_value >> 20) & 0xf); break; case R200_PP_CUBIC_FACES_0: case R200_PP_CUBIC_FACES_1: @@ -433,7 +432,7 @@ int r200_packet0_check(struct radeon_cs_parser *p, case R200_PP_CUBIC_FACES_3: case R200_PP_CUBIC_FACES_4: case R200_PP_CUBIC_FACES_5: - tmp = ib_chunk->kdata[idx]; + tmp = idx_value; i = (reg - R200_PP_CUBIC_FACES_0) / 32; for (face = 0; face < 4; face++) { track->textures[i].cube_info[face].width = 1 << ((tmp >> (face * 8)) & 0xf); @@ -448,9 +447,8 @@ int r200_packet0_check(struct radeon_cs_parser *p, return 0; } -int r200_init(struct radeon_device *rdev) +void r200_set_safe_registers(struct radeon_device *rdev) { rdev->config.r100.reg_safe_bm = r200_reg_safe_bm; rdev->config.r100.reg_safe_bm_size = ARRAY_SIZE(r200_reg_safe_bm); - return 0; } diff --git a/drivers/gpu/drm/radeon/r300.c b/drivers/gpu/drm/radeon/r300.c index bb151ecdf8fc..2f43ee8e4048 100644 --- a/drivers/gpu/drm/radeon/r300.c +++ b/drivers/gpu/drm/radeon/r300.c @@ -33,43 +33,16 @@ #include "radeon_drm.h" #include "r100_track.h" #include "r300d.h" - +#include "rv350d.h" #include "r300_reg_safe.h" -/* r300,r350,rv350,rv370,rv380 depends on : */ -void r100_hdp_reset(struct radeon_device *rdev); -int r100_cp_reset(struct radeon_device *rdev); -int r100_rb2d_reset(struct radeon_device *rdev); -int r100_cp_init(struct radeon_device *rdev, unsigned ring_size); -int r100_pci_gart_enable(struct radeon_device *rdev); -void r100_mc_setup(struct radeon_device *rdev); -void r100_mc_disable_clients(struct radeon_device *rdev); -int r100_gui_wait_for_idle(struct radeon_device *rdev); -int r100_cs_packet_parse(struct radeon_cs_parser *p, - struct radeon_cs_packet *pkt, - unsigned idx); -int r100_cs_packet_parse_vline(struct radeon_cs_parser *p); -int r100_cs_parse_packet0(struct radeon_cs_parser *p, - struct radeon_cs_packet *pkt, - const unsigned *auth, unsigned n, - radeon_packet0_check_t check); -int r100_cs_track_check_pkt3_indx_buffer(struct radeon_cs_parser *p, - struct radeon_cs_packet *pkt, - struct radeon_object *robj); - -/* This files gather functions specifics to: - * r300,r350,rv350,rv370,rv380 - * - * Some of these functions might be used by newer ASICs. - */ -void r300_gpu_init(struct radeon_device *rdev); -int r300_mc_wait_for_idle(struct radeon_device *rdev); -int rv370_debugfs_pcie_gart_info_init(struct radeon_device *rdev); - +/* This files gather functions specifics to: r300,r350,rv350,rv370,rv380 */ /* * rv370,rv380 PCIE GART */ +static int rv370_debugfs_pcie_gart_info_init(struct radeon_device *rdev); + void rv370_pcie_gart_tlb_flush(struct radeon_device *rdev) { uint32_t tmp; @@ -140,7 +113,7 @@ int rv370_pcie_gart_enable(struct radeon_device *rdev) tmp = RADEON_PCIE_TX_GART_UNMAPPED_ACCESS_DISCARD; WREG32_PCIE(RADEON_PCIE_TX_GART_CNTL, tmp); WREG32_PCIE(RADEON_PCIE_TX_GART_START_LO, rdev->mc.gtt_location); - tmp = rdev->mc.gtt_location + rdev->mc.gtt_size - 4096; + tmp = rdev->mc.gtt_location + rdev->mc.gtt_size - RADEON_GPU_PAGE_SIZE; WREG32_PCIE(RADEON_PCIE_TX_GART_END_LO, tmp); WREG32_PCIE(RADEON_PCIE_TX_GART_START_HI, 0); WREG32_PCIE(RADEON_PCIE_TX_GART_END_HI, 0); @@ -182,59 +155,6 @@ void rv370_pcie_gart_fini(struct radeon_device *rdev) radeon_gart_fini(rdev); } -/* - * MC - */ -int r300_mc_init(struct radeon_device *rdev) -{ - int r; - - if (r100_debugfs_rbbm_init(rdev)) { - DRM_ERROR("Failed to register debugfs file for RBBM !\n"); - } - - r300_gpu_init(rdev); - r100_pci_gart_disable(rdev); - if (rdev->flags & RADEON_IS_PCIE) { - rv370_pcie_gart_disable(rdev); - } - - /* Setup GPU memory space */ - rdev->mc.vram_location = 0xFFFFFFFFUL; - rdev->mc.gtt_location = 0xFFFFFFFFUL; - if (rdev->flags & RADEON_IS_AGP) { - r = radeon_agp_init(rdev); - if (r) { - printk(KERN_WARNING "[drm] Disabling AGP\n"); - rdev->flags &= ~RADEON_IS_AGP; - rdev->mc.gtt_size = radeon_gart_size * 1024 * 1024; - } else { - rdev->mc.gtt_location = rdev->mc.agp_base; - } - } - r = radeon_mc_setup(rdev); - if (r) { - return r; - } - - /* Program GPU memory space */ - r100_mc_disable_clients(rdev); - if (r300_mc_wait_for_idle(rdev)) { - printk(KERN_WARNING "Failed to wait MC idle while " - "programming pipes. Bad things might happen.\n"); - } - r100_mc_setup(rdev); - return 0; -} - -void r300_mc_fini(struct radeon_device *rdev) -{ -} - - -/* - * Fence emission - */ void r300_fence_ring_emit(struct radeon_device *rdev, struct radeon_fence *fence) { @@ -260,10 +180,6 @@ void r300_fence_ring_emit(struct radeon_device *rdev, radeon_ring_write(rdev, RADEON_SW_INT_FIRE); } - -/* - * Global GPU functions - */ int r300_copy_dma(struct radeon_device *rdev, uint64_t src_offset, uint64_t dst_offset, @@ -582,11 +498,6 @@ void r300_vram_info(struct radeon_device *rdev) r100_vram_init_sizes(rdev); } - -/* - * PCIE Lanes - */ - void rv370_set_pcie_lanes(struct radeon_device *rdev, int lanes) { uint32_t link_width_cntl, mask; @@ -646,10 +557,6 @@ void rv370_set_pcie_lanes(struct radeon_device *rdev, int lanes) } - -/* - * Debugfs info - */ #if defined(CONFIG_DEBUG_FS) static int rv370_debugfs_pcie_gart_info(struct seq_file *m, void *data) { @@ -680,7 +587,7 @@ static struct drm_info_list rv370_pcie_gart_info_list[] = { }; #endif -int rv370_debugfs_pcie_gart_info_init(struct radeon_device *rdev) +static int rv370_debugfs_pcie_gart_info_init(struct radeon_device *rdev) { #if defined(CONFIG_DEBUG_FS) return radeon_debugfs_add_files(rdev, rv370_pcie_gart_info_list, 1); @@ -689,25 +596,22 @@ int rv370_debugfs_pcie_gart_info_init(struct radeon_device *rdev) #endif } - -/* - * CS functions - */ static int r300_packet0_check(struct radeon_cs_parser *p, struct radeon_cs_packet *pkt, unsigned idx, unsigned reg) { - struct radeon_cs_chunk *ib_chunk; struct radeon_cs_reloc *reloc; struct r100_cs_track *track; volatile uint32_t *ib; uint32_t tmp, tile_flags = 0; unsigned i; int r; + u32 idx_value; ib = p->ib->ptr; - ib_chunk = &p->chunks[p->chunk_ib_idx]; track = (struct r100_cs_track *)p->track; + idx_value = radeon_get_ib_value(p, idx); + switch(reg) { case AVIVO_D1MODE_VLINE_START_END: case RADEON_CRTC_GUI_TRIG_VLINE: @@ -738,8 +642,8 @@ static int r300_packet0_check(struct radeon_cs_parser *p, return r; } track->cb[i].robj = reloc->robj; - track->cb[i].offset = ib_chunk->kdata[idx]; - ib[idx] = ib_chunk->kdata[idx] + ((u32)reloc->lobj.gpu_offset); + track->cb[i].offset = idx_value; + ib[idx] = idx_value + ((u32)reloc->lobj.gpu_offset); break; case R300_ZB_DEPTHOFFSET: r = r100_cs_packet_next_reloc(p, &reloc); @@ -750,8 +654,8 @@ static int r300_packet0_check(struct radeon_cs_parser *p, return r; } track->zb.robj = reloc->robj; - track->zb.offset = ib_chunk->kdata[idx]; - ib[idx] = ib_chunk->kdata[idx] + ((u32)reloc->lobj.gpu_offset); + track->zb.offset = idx_value; + ib[idx] = idx_value + ((u32)reloc->lobj.gpu_offset); break; case R300_TX_OFFSET_0: case R300_TX_OFFSET_0+4: @@ -777,32 +681,32 @@ static int r300_packet0_check(struct radeon_cs_parser *p, r100_cs_dump_packet(p, pkt); return r; } - ib[idx] = ib_chunk->kdata[idx] + ((u32)reloc->lobj.gpu_offset); + ib[idx] = idx_value + ((u32)reloc->lobj.gpu_offset); track->textures[i].robj = reloc->robj; break; /* Tracked registers */ case 0x2084: /* VAP_VF_CNTL */ - track->vap_vf_cntl = ib_chunk->kdata[idx]; + track->vap_vf_cntl = idx_value; break; case 0x20B4: /* VAP_VTX_SIZE */ - track->vtx_size = ib_chunk->kdata[idx] & 0x7F; + track->vtx_size = idx_value & 0x7F; break; case 0x2134: /* VAP_VF_MAX_VTX_INDX */ - track->max_indx = ib_chunk->kdata[idx] & 0x00FFFFFFUL; + track->max_indx = idx_value & 0x00FFFFFFUL; break; case 0x43E4: /* SC_SCISSOR1 */ - track->maxy = ((ib_chunk->kdata[idx] >> 13) & 0x1FFF) + 1; + track->maxy = ((idx_value >> 13) & 0x1FFF) + 1; if (p->rdev->family < CHIP_RV515) { track->maxy -= 1440; } break; case 0x4E00: /* RB3D_CCTL */ - track->num_cb = ((ib_chunk->kdata[idx] >> 5) & 0x3) + 1; + track->num_cb = ((idx_value >> 5) & 0x3) + 1; break; case 0x4E38: case 0x4E3C: @@ -825,13 +729,13 @@ static int r300_packet0_check(struct radeon_cs_parser *p, if (reloc->lobj.tiling_flags & RADEON_TILING_MICRO) tile_flags |= R300_COLOR_MICROTILE_ENABLE; - tmp = ib_chunk->kdata[idx] & ~(0x7 << 16); + tmp = idx_value & ~(0x7 << 16); tmp |= tile_flags; ib[idx] = tmp; i = (reg - 0x4E38) >> 2; - track->cb[i].pitch = ib_chunk->kdata[idx] & 0x3FFE; - switch (((ib_chunk->kdata[idx] >> 21) & 0xF)) { + track->cb[i].pitch = idx_value & 0x3FFE; + switch (((idx_value >> 21) & 0xF)) { case 9: case 11: case 12: @@ -854,13 +758,13 @@ static int r300_packet0_check(struct radeon_cs_parser *p, break; default: DRM_ERROR("Invalid color buffer format (%d) !\n", - ((ib_chunk->kdata[idx] >> 21) & 0xF)); + ((idx_value >> 21) & 0xF)); return -EINVAL; } break; case 0x4F00: /* ZB_CNTL */ - if (ib_chunk->kdata[idx] & 2) { + if (idx_value & 2) { track->z_enabled = true; } else { track->z_enabled = false; @@ -868,7 +772,7 @@ static int r300_packet0_check(struct radeon_cs_parser *p, break; case 0x4F10: /* ZB_FORMAT */ - switch ((ib_chunk->kdata[idx] & 0xF)) { + switch ((idx_value & 0xF)) { case 0: case 1: track->zb.cpp = 2; @@ -878,7 +782,7 @@ static int r300_packet0_check(struct radeon_cs_parser *p, break; default: DRM_ERROR("Invalid z buffer format (%d) !\n", - (ib_chunk->kdata[idx] & 0xF)); + (idx_value & 0xF)); return -EINVAL; } break; @@ -897,17 +801,17 @@ static int r300_packet0_check(struct radeon_cs_parser *p, if (reloc->lobj.tiling_flags & RADEON_TILING_MICRO) tile_flags |= R300_DEPTHMICROTILE_TILED;; - tmp = ib_chunk->kdata[idx] & ~(0x7 << 16); + tmp = idx_value & ~(0x7 << 16); tmp |= tile_flags; ib[idx] = tmp; - track->zb.pitch = ib_chunk->kdata[idx] & 0x3FFC; + track->zb.pitch = idx_value & 0x3FFC; break; case 0x4104: for (i = 0; i < 16; i++) { bool enabled; - enabled = !!(ib_chunk->kdata[idx] & (1 << i)); + enabled = !!(idx_value & (1 << i)); track->textures[i].enabled = enabled; } break; @@ -929,9 +833,9 @@ static int r300_packet0_check(struct radeon_cs_parser *p, case 0x44FC: /* TX_FORMAT1_[0-15] */ i = (reg - 0x44C0) >> 2; - tmp = (ib_chunk->kdata[idx] >> 25) & 0x3; + tmp = (idx_value >> 25) & 0x3; track->textures[i].tex_coord_type = tmp; - switch ((ib_chunk->kdata[idx] & 0x1F)) { + switch ((idx_value & 0x1F)) { case R300_TX_FORMAT_X8: case R300_TX_FORMAT_Y4X4: case R300_TX_FORMAT_Z3Y3X2: @@ -971,7 +875,7 @@ static int r300_packet0_check(struct radeon_cs_parser *p, break; default: DRM_ERROR("Invalid texture format %u\n", - (ib_chunk->kdata[idx] & 0x1F)); + (idx_value & 0x1F)); return -EINVAL; break; } @@ -994,11 +898,11 @@ static int r300_packet0_check(struct radeon_cs_parser *p, case 0x443C: /* TX_FILTER0_[0-15] */ i = (reg - 0x4400) >> 2; - tmp = ib_chunk->kdata[idx] & 0x7; + tmp = idx_value & 0x7; if (tmp == 2 || tmp == 4 || tmp == 6) { track->textures[i].roundup_w = false; } - tmp = (ib_chunk->kdata[idx] >> 3) & 0x7; + tmp = (idx_value >> 3) & 0x7; if (tmp == 2 || tmp == 4 || tmp == 6) { track->textures[i].roundup_h = false; } @@ -1021,12 +925,12 @@ static int r300_packet0_check(struct radeon_cs_parser *p, case 0x453C: /* TX_FORMAT2_[0-15] */ i = (reg - 0x4500) >> 2; - tmp = ib_chunk->kdata[idx] & 0x3FFF; + tmp = idx_value & 0x3FFF; track->textures[i].pitch = tmp + 1; if (p->rdev->family >= CHIP_RV515) { - tmp = ((ib_chunk->kdata[idx] >> 15) & 1) << 11; + tmp = ((idx_value >> 15) & 1) << 11; track->textures[i].width_11 = tmp; - tmp = ((ib_chunk->kdata[idx] >> 16) & 1) << 11; + tmp = ((idx_value >> 16) & 1) << 11; track->textures[i].height_11 = tmp; } break; @@ -1048,15 +952,15 @@ static int r300_packet0_check(struct radeon_cs_parser *p, case 0x44BC: /* TX_FORMAT0_[0-15] */ i = (reg - 0x4480) >> 2; - tmp = ib_chunk->kdata[idx] & 0x7FF; + tmp = idx_value & 0x7FF; track->textures[i].width = tmp + 1; - tmp = (ib_chunk->kdata[idx] >> 11) & 0x7FF; + tmp = (idx_value >> 11) & 0x7FF; track->textures[i].height = tmp + 1; - tmp = (ib_chunk->kdata[idx] >> 26) & 0xF; + tmp = (idx_value >> 26) & 0xF; track->textures[i].num_levels = tmp; - tmp = ib_chunk->kdata[idx] & (1 << 31); + tmp = idx_value & (1 << 31); track->textures[i].use_pitch = !!tmp; - tmp = (ib_chunk->kdata[idx] >> 22) & 0xF; + tmp = (idx_value >> 22) & 0xF; track->textures[i].txdepth = tmp; break; case R300_ZB_ZPASS_ADDR: @@ -1067,7 +971,7 @@ static int r300_packet0_check(struct radeon_cs_parser *p, r100_cs_dump_packet(p, pkt); return r; } - ib[idx] = ib_chunk->kdata[idx] + ((u32)reloc->lobj.gpu_offset); + ib[idx] = idx_value + ((u32)reloc->lobj.gpu_offset); break; case 0x4be8: /* valid register only on RV530 */ @@ -1085,60 +989,20 @@ static int r300_packet0_check(struct radeon_cs_parser *p, static int r300_packet3_check(struct radeon_cs_parser *p, struct radeon_cs_packet *pkt) { - struct radeon_cs_chunk *ib_chunk; - struct radeon_cs_reloc *reloc; struct r100_cs_track *track; volatile uint32_t *ib; unsigned idx; - unsigned i, c; int r; ib = p->ib->ptr; - ib_chunk = &p->chunks[p->chunk_ib_idx]; idx = pkt->idx + 1; track = (struct r100_cs_track *)p->track; switch(pkt->opcode) { case PACKET3_3D_LOAD_VBPNTR: - c = ib_chunk->kdata[idx++] & 0x1F; - track->num_arrays = c; - for (i = 0; i < (c - 1); i+=2, idx+=3) { - r = r100_cs_packet_next_reloc(p, &reloc); - if (r) { - DRM_ERROR("No reloc for packet3 %d\n", - pkt->opcode); - r100_cs_dump_packet(p, pkt); - return r; - } - ib[idx+1] = ib_chunk->kdata[idx+1] + ((u32)reloc->lobj.gpu_offset); - track->arrays[i + 0].robj = reloc->robj; - track->arrays[i + 0].esize = ib_chunk->kdata[idx] >> 8; - track->arrays[i + 0].esize &= 0x7F; - r = r100_cs_packet_next_reloc(p, &reloc); - if (r) { - DRM_ERROR("No reloc for packet3 %d\n", - pkt->opcode); - r100_cs_dump_packet(p, pkt); - return r; - } - ib[idx+2] = ib_chunk->kdata[idx+2] + ((u32)reloc->lobj.gpu_offset); - track->arrays[i + 1].robj = reloc->robj; - track->arrays[i + 1].esize = ib_chunk->kdata[idx] >> 24; - track->arrays[i + 1].esize &= 0x7F; - } - if (c & 1) { - r = r100_cs_packet_next_reloc(p, &reloc); - if (r) { - DRM_ERROR("No reloc for packet3 %d\n", - pkt->opcode); - r100_cs_dump_packet(p, pkt); - return r; - } - ib[idx+1] = ib_chunk->kdata[idx+1] + ((u32)reloc->lobj.gpu_offset); - track->arrays[i + 0].robj = reloc->robj; - track->arrays[i + 0].esize = ib_chunk->kdata[idx] >> 8; - track->arrays[i + 0].esize &= 0x7F; - } + r = r100_packet3_load_vbpntr(p, pkt, idx); + if (r) + return r; break; case PACKET3_INDX_BUFFER: r = r100_cs_packet_next_reloc(p, &reloc); @@ -1147,7 +1011,7 @@ static int r300_packet3_check(struct radeon_cs_parser *p, r100_cs_dump_packet(p, pkt); return r; } - ib[idx+1] = ib_chunk->kdata[idx+1] + ((u32)reloc->lobj.gpu_offset); + ib[idx+1] = radeon_get_ib_value(p, idx + 1) + ((u32)reloc->lobj.gpu_offset); r = r100_cs_track_check_pkt3_indx_buffer(p, pkt, reloc->robj); if (r) { return r; @@ -1158,11 +1022,11 @@ static int r300_packet3_check(struct radeon_cs_parser *p, /* Number of dwords is vtx_size * (num_vertices - 1) * PRIM_WALK must be equal to 3 vertex data in embedded * in cmd stream */ - if (((ib_chunk->kdata[idx+1] >> 4) & 0x3) != 3) { + if (((radeon_get_ib_value(p, idx + 1) >> 4) & 0x3) != 3) { DRM_ERROR("PRIM_WALK must be 3 for IMMD draw\n"); return -EINVAL; } - track->vap_vf_cntl = ib_chunk->kdata[idx+1]; + track->vap_vf_cntl = radeon_get_ib_value(p, idx + 1); track->immd_dwords = pkt->count - 1; r = r100_cs_track_check(p->rdev, track); if (r) { @@ -1173,11 +1037,11 @@ static int r300_packet3_check(struct radeon_cs_parser *p, /* Number of dwords is vtx_size * (num_vertices - 1) * PRIM_WALK must be equal to 3 vertex data in embedded * in cmd stream */ - if (((ib_chunk->kdata[idx] >> 4) & 0x3) != 3) { + if (((radeon_get_ib_value(p, idx) >> 4) & 0x3) != 3) { DRM_ERROR("PRIM_WALK must be 3 for IMMD draw\n"); return -EINVAL; } - track->vap_vf_cntl = ib_chunk->kdata[idx]; + track->vap_vf_cntl = radeon_get_ib_value(p, idx); track->immd_dwords = pkt->count; r = r100_cs_track_check(p->rdev, track); if (r) { @@ -1185,28 +1049,28 @@ static int r300_packet3_check(struct radeon_cs_parser *p, } break; case PACKET3_3D_DRAW_VBUF: - track->vap_vf_cntl = ib_chunk->kdata[idx + 1]; + track->vap_vf_cntl = radeon_get_ib_value(p, idx + 1); r = r100_cs_track_check(p->rdev, track); if (r) { return r; } break; case PACKET3_3D_DRAW_VBUF_2: - track->vap_vf_cntl = ib_chunk->kdata[idx]; + track->vap_vf_cntl = radeon_get_ib_value(p, idx); r = r100_cs_track_check(p->rdev, track); if (r) { return r; } break; case PACKET3_3D_DRAW_INDX: - track->vap_vf_cntl = ib_chunk->kdata[idx + 1]; + track->vap_vf_cntl = radeon_get_ib_value(p, idx + 1); r = r100_cs_track_check(p->rdev, track); if (r) { return r; } break; case PACKET3_3D_DRAW_INDX_2: - track->vap_vf_cntl = ib_chunk->kdata[idx]; + track->vap_vf_cntl = radeon_get_ib_value(p, idx); r = r100_cs_track_check(p->rdev, track); if (r) { return r; @@ -1265,12 +1129,6 @@ void r300_set_reg_safe(struct radeon_device *rdev) rdev->config.r300.reg_safe_bm_size = ARRAY_SIZE(r300_reg_safe_bm); } -int r300_init(struct radeon_device *rdev) -{ - r300_set_reg_safe(rdev); - return 0; -} - void r300_mc_program(struct radeon_device *rdev) { struct r100_mc_save save; @@ -1304,3 +1162,198 @@ void r300_mc_program(struct radeon_device *rdev) S_000148_MC_FB_TOP(rdev->mc.vram_end >> 16)); r100_mc_resume(rdev, &save); } + +void r300_clock_startup(struct radeon_device *rdev) +{ + u32 tmp; + + if (radeon_dynclks != -1 && radeon_dynclks) + radeon_legacy_set_clock_gating(rdev, 1); + /* We need to force on some of the block */ + tmp = RREG32_PLL(R_00000D_SCLK_CNTL); + tmp |= S_00000D_FORCE_CP(1) | S_00000D_FORCE_VIP(1); + if ((rdev->family == CHIP_RV350) || (rdev->family == CHIP_RV380)) + tmp |= S_00000D_FORCE_VAP(1); + WREG32_PLL(R_00000D_SCLK_CNTL, tmp); +} + +static int r300_startup(struct radeon_device *rdev) +{ + int r; + + r300_mc_program(rdev); + /* Resume clock */ + r300_clock_startup(rdev); + /* Initialize GPU configuration (# pipes, ...) */ + r300_gpu_init(rdev); + /* Initialize GART (initialize after TTM so we can allocate + * memory through TTM but finalize after TTM) */ + if (rdev->flags & RADEON_IS_PCIE) { + r = rv370_pcie_gart_enable(rdev); + if (r) + return r; + } + if (rdev->flags & RADEON_IS_PCI) { + r = r100_pci_gart_enable(rdev); + if (r) + return r; + } + /* Enable IRQ */ + rdev->irq.sw_int = true; + r100_irq_set(rdev); + /* 1M ring buffer */ + r = r100_cp_init(rdev, 1024 * 1024); + if (r) { + dev_err(rdev->dev, "failled initializing CP (%d).\n", r); + return r; + } + r = r100_wb_init(rdev); + if (r) + dev_err(rdev->dev, "failled initializing WB (%d).\n", r); + r = r100_ib_init(rdev); + if (r) { + dev_err(rdev->dev, "failled initializing IB (%d).\n", r); + return r; + } + return 0; +} + +int r300_resume(struct radeon_device *rdev) +{ + /* Make sur GART are not working */ + if (rdev->flags & RADEON_IS_PCIE) + rv370_pcie_gart_disable(rdev); + if (rdev->flags & RADEON_IS_PCI) + r100_pci_gart_disable(rdev); + /* Resume clock before doing reset */ + r300_clock_startup(rdev); + /* Reset gpu before posting otherwise ATOM will enter infinite loop */ + if (radeon_gpu_reset(rdev)) { + dev_warn(rdev->dev, "GPU reset failed ! (0xE40=0x%08X, 0x7C0=0x%08X)\n", + RREG32(R_000E40_RBBM_STATUS), + RREG32(R_0007C0_CP_STAT)); + } + /* post */ + radeon_combios_asic_init(rdev->ddev); + /* Resume clock after posting */ + r300_clock_startup(rdev); + return r300_startup(rdev); +} + +int r300_suspend(struct radeon_device *rdev) +{ + r100_cp_disable(rdev); + r100_wb_disable(rdev); + r100_irq_disable(rdev); + if (rdev->flags & RADEON_IS_PCIE) + rv370_pcie_gart_disable(rdev); + if (rdev->flags & RADEON_IS_PCI) + r100_pci_gart_disable(rdev); + return 0; +} + +void r300_fini(struct radeon_device *rdev) +{ + r300_suspend(rdev); + r100_cp_fini(rdev); + r100_wb_fini(rdev); + r100_ib_fini(rdev); + radeon_gem_fini(rdev); + if (rdev->flags & RADEON_IS_PCIE) + rv370_pcie_gart_fini(rdev); + if (rdev->flags & RADEON_IS_PCI) + r100_pci_gart_fini(rdev); + radeon_irq_kms_fini(rdev); + radeon_fence_driver_fini(rdev); + radeon_object_fini(rdev); + radeon_atombios_fini(rdev); + kfree(rdev->bios); + rdev->bios = NULL; +} + +int r300_init(struct radeon_device *rdev) +{ + int r; + + /* Disable VGA */ + r100_vga_render_disable(rdev); + /* Initialize scratch registers */ + radeon_scratch_init(rdev); + /* Initialize surface registers */ + radeon_surface_init(rdev); + /* TODO: disable VGA need to use VGA request */ + /* BIOS*/ + if (!radeon_get_bios(rdev)) { + if (ASIC_IS_AVIVO(rdev)) + return -EINVAL; + } + if (rdev->is_atom_bios) { + dev_err(rdev->dev, "Expecting combios for RS400/RS480 GPU\n"); + return -EINVAL; + } else { + r = radeon_combios_init(rdev); + if (r) + return r; + } + /* Reset gpu before posting otherwise ATOM will enter infinite loop */ + if (radeon_gpu_reset(rdev)) { + dev_warn(rdev->dev, + "GPU reset failed ! (0xE40=0x%08X, 0x7C0=0x%08X)\n", + RREG32(R_000E40_RBBM_STATUS), + RREG32(R_0007C0_CP_STAT)); + } + /* check if cards are posted or not */ + if (!radeon_card_posted(rdev) && rdev->bios) { + DRM_INFO("GPU not posted. posting now...\n"); + radeon_combios_asic_init(rdev->ddev); + } + /* Set asic errata */ + r300_errata(rdev); + /* Initialize clocks */ + radeon_get_clock_info(rdev->ddev); + /* Get vram informations */ + r300_vram_info(rdev); + /* Initialize memory controller (also test AGP) */ + r = r420_mc_init(rdev); + if (r) + return r; + /* Fence driver */ + r = radeon_fence_driver_init(rdev); + if (r) + return r; + r = radeon_irq_kms_init(rdev); + if (r) + return r; + /* Memory manager */ + r = radeon_object_init(rdev); + if (r) + return r; + if (rdev->flags & RADEON_IS_PCIE) { + r = rv370_pcie_gart_init(rdev); + if (r) + return r; + } + if (rdev->flags & RADEON_IS_PCI) { + r = r100_pci_gart_init(rdev); + if (r) + return r; + } + r300_set_reg_safe(rdev); + rdev->accel_working = true; + r = r300_startup(rdev); + if (r) { + /* Somethings want wront with the accel init stop accel */ + dev_err(rdev->dev, "Disabling GPU acceleration\n"); + r300_suspend(rdev); + r100_cp_fini(rdev); + r100_wb_fini(rdev); + r100_ib_fini(rdev); + if (rdev->flags & RADEON_IS_PCIE) + rv370_pcie_gart_fini(rdev); + if (rdev->flags & RADEON_IS_PCI) + r100_pci_gart_fini(rdev); + radeon_irq_kms_fini(rdev); + rdev->accel_working = false; + } + return 0; +} diff --git a/drivers/gpu/drm/radeon/r300d.h b/drivers/gpu/drm/radeon/r300d.h index d4fa3eb1074f..4c73114f0de9 100644 --- a/drivers/gpu/drm/radeon/r300d.h +++ b/drivers/gpu/drm/radeon/r300d.h @@ -96,6 +96,211 @@ #define S_000170_AGP_BASE_ADDR(x) (((x) & 0xFFFFFFFF) << 0) #define G_000170_AGP_BASE_ADDR(x) (((x) >> 0) & 0xFFFFFFFF) #define C_000170_AGP_BASE_ADDR 0x00000000 +#define R_0007C0_CP_STAT 0x0007C0 +#define S_0007C0_MRU_BUSY(x) (((x) & 0x1) << 0) +#define G_0007C0_MRU_BUSY(x) (((x) >> 0) & 0x1) +#define C_0007C0_MRU_BUSY 0xFFFFFFFE +#define S_0007C0_MWU_BUSY(x) (((x) & 0x1) << 1) +#define G_0007C0_MWU_BUSY(x) (((x) >> 1) & 0x1) +#define C_0007C0_MWU_BUSY 0xFFFFFFFD +#define S_0007C0_RSIU_BUSY(x) (((x) & 0x1) << 2) +#define G_0007C0_RSIU_BUSY(x) (((x) >> 2) & 0x1) +#define C_0007C0_RSIU_BUSY 0xFFFFFFFB +#define S_0007C0_RCIU_BUSY(x) (((x) & 0x1) << 3) +#define G_0007C0_RCIU_BUSY(x) (((x) >> 3) & 0x1) +#define C_0007C0_RCIU_BUSY 0xFFFFFFF7 +#define S_0007C0_CSF_PRIMARY_BUSY(x) (((x) & 0x1) << 9) +#define G_0007C0_CSF_PRIMARY_BUSY(x) (((x) >> 9) & 0x1) +#define C_0007C0_CSF_PRIMARY_BUSY 0xFFFFFDFF +#define S_0007C0_CSF_INDIRECT_BUSY(x) (((x) & 0x1) << 10) +#define G_0007C0_CSF_INDIRECT_BUSY(x) (((x) >> 10) & 0x1) +#define C_0007C0_CSF_INDIRECT_BUSY 0xFFFFFBFF +#define S_0007C0_CSQ_PRIMARY_BUSY(x) (((x) & 0x1) << 11) +#define G_0007C0_CSQ_PRIMARY_BUSY(x) (((x) >> 11) & 0x1) +#define C_0007C0_CSQ_PRIMARY_BUSY 0xFFFFF7FF +#define S_0007C0_CSQ_INDIRECT_BUSY(x) (((x) & 0x1) << 12) +#define G_0007C0_CSQ_INDIRECT_BUSY(x) (((x) >> 12) & 0x1) +#define C_0007C0_CSQ_INDIRECT_BUSY 0xFFFFEFFF +#define S_0007C0_CSI_BUSY(x) (((x) & 0x1) << 13) +#define G_0007C0_CSI_BUSY(x) (((x) >> 13) & 0x1) +#define C_0007C0_CSI_BUSY 0xFFFFDFFF +#define S_0007C0_CSF_INDIRECT2_BUSY(x) (((x) & 0x1) << 14) +#define G_0007C0_CSF_INDIRECT2_BUSY(x) (((x) >> 14) & 0x1) +#define C_0007C0_CSF_INDIRECT2_BUSY 0xFFFFBFFF +#define S_0007C0_CSQ_INDIRECT2_BUSY(x) (((x) & 0x1) << 15) +#define G_0007C0_CSQ_INDIRECT2_BUSY(x) (((x) >> 15) & 0x1) +#define C_0007C0_CSQ_INDIRECT2_BUSY 0xFFFF7FFF +#define S_0007C0_GUIDMA_BUSY(x) (((x) & 0x1) << 28) +#define G_0007C0_GUIDMA_BUSY(x) (((x) >> 28) & 0x1) +#define C_0007C0_GUIDMA_BUSY 0xEFFFFFFF +#define S_0007C0_VIDDMA_BUSY(x) (((x) & 0x1) << 29) +#define G_0007C0_VIDDMA_BUSY(x) (((x) >> 29) & 0x1) +#define C_0007C0_VIDDMA_BUSY 0xDFFFFFFF +#define S_0007C0_CMDSTRM_BUSY(x) (((x) & 0x1) << 30) +#define G_0007C0_CMDSTRM_BUSY(x) (((x) >> 30) & 0x1) +#define C_0007C0_CMDSTRM_BUSY 0xBFFFFFFF +#define S_0007C0_CP_BUSY(x) (((x) & 0x1) << 31) +#define G_0007C0_CP_BUSY(x) (((x) >> 31) & 0x1) +#define C_0007C0_CP_BUSY 0x7FFFFFFF +#define R_000E40_RBBM_STATUS 0x000E40 +#define S_000E40_CMDFIFO_AVAIL(x) (((x) & 0x7F) << 0) +#define G_000E40_CMDFIFO_AVAIL(x) (((x) >> 0) & 0x7F) +#define C_000E40_CMDFIFO_AVAIL 0xFFFFFF80 +#define S_000E40_HIRQ_ON_RBB(x) (((x) & 0x1) << 8) +#define G_000E40_HIRQ_ON_RBB(x) (((x) >> 8) & 0x1) +#define C_000E40_HIRQ_ON_RBB 0xFFFFFEFF +#define S_000E40_CPRQ_ON_RBB(x) (((x) & 0x1) << 9) +#define G_000E40_CPRQ_ON_RBB(x) (((x) >> 9) & 0x1) +#define C_000E40_CPRQ_ON_RBB 0xFFFFFDFF +#define S_000E40_CFRQ_ON_RBB(x) (((x) & 0x1) << 10) +#define G_000E40_CFRQ_ON_RBB(x) (((x) >> 10) & 0x1) +#define C_000E40_CFRQ_ON_RBB 0xFFFFFBFF +#define S_000E40_HIRQ_IN_RTBUF(x) (((x) & 0x1) << 11) +#define G_000E40_HIRQ_IN_RTBUF(x) (((x) >> 11) & 0x1) +#define C_000E40_HIRQ_IN_RTBUF 0xFFFFF7FF +#define S_000E40_CPRQ_IN_RTBUF(x) (((x) & 0x1) << 12) +#define G_000E40_CPRQ_IN_RTBUF(x) (((x) >> 12) & 0x1) +#define C_000E40_CPRQ_IN_RTBUF 0xFFFFEFFF +#define S_000E40_CFRQ_IN_RTBUF(x) (((x) & 0x1) << 13) +#define G_000E40_CFRQ_IN_RTBUF(x) (((x) >> 13) & 0x1) +#define C_000E40_CFRQ_IN_RTBUF 0xFFFFDFFF +#define S_000E40_CF_PIPE_BUSY(x) (((x) & 0x1) << 14) +#define G_000E40_CF_PIPE_BUSY(x) (((x) >> 14) & 0x1) +#define C_000E40_CF_PIPE_BUSY 0xFFFFBFFF +#define S_000E40_ENG_EV_BUSY(x) (((x) & 0x1) << 15) +#define G_000E40_ENG_EV_BUSY(x) (((x) >> 15) & 0x1) +#define C_000E40_ENG_EV_BUSY 0xFFFF7FFF +#define S_000E40_CP_CMDSTRM_BUSY(x) (((x) & 0x1) << 16) +#define G_000E40_CP_CMDSTRM_BUSY(x) (((x) >> 16) & 0x1) +#define C_000E40_CP_CMDSTRM_BUSY 0xFFFEFFFF +#define S_000E40_E2_BUSY(x) (((x) & 0x1) << 17) +#define G_000E40_E2_BUSY(x) (((x) >> 17) & 0x1) +#define C_000E40_E2_BUSY 0xFFFDFFFF +#define S_000E40_RB2D_BUSY(x) (((x) & 0x1) << 18) +#define G_000E40_RB2D_BUSY(x) (((x) >> 18) & 0x1) +#define C_000E40_RB2D_BUSY 0xFFFBFFFF +#define S_000E40_RB3D_BUSY(x) (((x) & 0x1) << 19) +#define G_000E40_RB3D_BUSY(x) (((x) >> 19) & 0x1) +#define C_000E40_RB3D_BUSY 0xFFF7FFFF +#define S_000E40_VAP_BUSY(x) (((x) & 0x1) << 20) +#define G_000E40_VAP_BUSY(x) (((x) >> 20) & 0x1) +#define C_000E40_VAP_BUSY 0xFFEFFFFF +#define S_000E40_RE_BUSY(x) (((x) & 0x1) << 21) +#define G_000E40_RE_BUSY(x) (((x) >> 21) & 0x1) +#define C_000E40_RE_BUSY 0xFFDFFFFF +#define S_000E40_TAM_BUSY(x) (((x) & 0x1) << 22) +#define G_000E40_TAM_BUSY(x) (((x) >> 22) & 0x1) +#define C_000E40_TAM_BUSY 0xFFBFFFFF +#define S_000E40_TDM_BUSY(x) (((x) & 0x1) << 23) +#define G_000E40_TDM_BUSY(x) (((x) >> 23) & 0x1) +#define C_000E40_TDM_BUSY 0xFF7FFFFF +#define S_000E40_PB_BUSY(x) (((x) & 0x1) << 24) +#define G_000E40_PB_BUSY(x) (((x) >> 24) & 0x1) +#define C_000E40_PB_BUSY 0xFEFFFFFF +#define S_000E40_TIM_BUSY(x) (((x) & 0x1) << 25) +#define G_000E40_TIM_BUSY(x) (((x) >> 25) & 0x1) +#define C_000E40_TIM_BUSY 0xFDFFFFFF +#define S_000E40_GA_BUSY(x) (((x) & 0x1) << 26) +#define G_000E40_GA_BUSY(x) (((x) >> 26) & 0x1) +#define C_000E40_GA_BUSY 0xFBFFFFFF +#define S_000E40_CBA2D_BUSY(x) (((x) & 0x1) << 27) +#define G_000E40_CBA2D_BUSY(x) (((x) >> 27) & 0x1) +#define C_000E40_CBA2D_BUSY 0xF7FFFFFF +#define S_000E40_GUI_ACTIVE(x) (((x) & 0x1) << 31) +#define G_000E40_GUI_ACTIVE(x) (((x) >> 31) & 0x1) +#define C_000E40_GUI_ACTIVE 0x7FFFFFFF +#define R_00000D_SCLK_CNTL 0x00000D +#define S_00000D_SCLK_SRC_SEL(x) (((x) & 0x7) << 0) +#define G_00000D_SCLK_SRC_SEL(x) (((x) >> 0) & 0x7) +#define C_00000D_SCLK_SRC_SEL 0xFFFFFFF8 +#define S_00000D_CP_MAX_DYN_STOP_LAT(x) (((x) & 0x1) << 3) +#define G_00000D_CP_MAX_DYN_STOP_LAT(x) (((x) >> 3) & 0x1) +#define C_00000D_CP_MAX_DYN_STOP_LAT 0xFFFFFFF7 +#define S_00000D_HDP_MAX_DYN_STOP_LAT(x) (((x) & 0x1) << 4) +#define G_00000D_HDP_MAX_DYN_STOP_LAT(x) (((x) >> 4) & 0x1) +#define C_00000D_HDP_MAX_DYN_STOP_LAT 0xFFFFFFEF +#define S_00000D_TV_MAX_DYN_STOP_LAT(x) (((x) & 0x1) << 5) +#define G_00000D_TV_MAX_DYN_STOP_LAT(x) (((x) >> 5) & 0x1) +#define C_00000D_TV_MAX_DYN_STOP_LAT 0xFFFFFFDF +#define S_00000D_E2_MAX_DYN_STOP_LAT(x) (((x) & 0x1) << 6) +#define G_00000D_E2_MAX_DYN_STOP_LAT(x) (((x) >> 6) & 0x1) +#define C_00000D_E2_MAX_DYN_STOP_LAT 0xFFFFFFBF +#define S_00000D_SE_MAX_DYN_STOP_LAT(x) (((x) & 0x1) << 7) +#define G_00000D_SE_MAX_DYN_STOP_LAT(x) (((x) >> 7) & 0x1) +#define C_00000D_SE_MAX_DYN_STOP_LAT 0xFFFFFF7F +#define S_00000D_IDCT_MAX_DYN_STOP_LAT(x) (((x) & 0x1) << 8) +#define G_00000D_IDCT_MAX_DYN_STOP_LAT(x) (((x) >> 8) & 0x1) +#define C_00000D_IDCT_MAX_DYN_STOP_LAT 0xFFFFFEFF +#define S_00000D_VIP_MAX_DYN_STOP_LAT(x) (((x) & 0x1) << 9) +#define G_00000D_VIP_MAX_DYN_STOP_LAT(x) (((x) >> 9) & 0x1) +#define C_00000D_VIP_MAX_DYN_STOP_LAT 0xFFFFFDFF +#define S_00000D_RE_MAX_DYN_STOP_LAT(x) (((x) & 0x1) << 10) +#define G_00000D_RE_MAX_DYN_STOP_LAT(x) (((x) >> 10) & 0x1) +#define C_00000D_RE_MAX_DYN_STOP_LAT 0xFFFFFBFF +#define S_00000D_PB_MAX_DYN_STOP_LAT(x) (((x) & 0x1) << 11) +#define G_00000D_PB_MAX_DYN_STOP_LAT(x) (((x) >> 11) & 0x1) +#define C_00000D_PB_MAX_DYN_STOP_LAT 0xFFFFF7FF +#define S_00000D_TAM_MAX_DYN_STOP_LAT(x) (((x) & 0x1) << 12) +#define G_00000D_TAM_MAX_DYN_STOP_LAT(x) (((x) >> 12) & 0x1) +#define C_00000D_TAM_MAX_DYN_STOP_LAT 0xFFFFEFFF +#define S_00000D_TDM_MAX_DYN_STOP_LAT(x) (((x) & 0x1) << 13) +#define G_00000D_TDM_MAX_DYN_STOP_LAT(x) (((x) >> 13) & 0x1) +#define C_00000D_TDM_MAX_DYN_STOP_LAT 0xFFFFDFFF +#define S_00000D_RB_MAX_DYN_STOP_LAT(x) (((x) & 0x1) << 14) +#define G_00000D_RB_MAX_DYN_STOP_LAT(x) (((x) >> 14) & 0x1) +#define C_00000D_RB_MAX_DYN_STOP_LAT 0xFFFFBFFF +#define S_00000D_FORCE_DISP2(x) (((x) & 0x1) << 15) +#define G_00000D_FORCE_DISP2(x) (((x) >> 15) & 0x1) +#define C_00000D_FORCE_DISP2 0xFFFF7FFF +#define S_00000D_FORCE_CP(x) (((x) & 0x1) << 16) +#define G_00000D_FORCE_CP(x) (((x) >> 16) & 0x1) +#define C_00000D_FORCE_CP 0xFFFEFFFF +#define S_00000D_FORCE_HDP(x) (((x) & 0x1) << 17) +#define G_00000D_FORCE_HDP(x) (((x) >> 17) & 0x1) +#define C_00000D_FORCE_HDP 0xFFFDFFFF +#define S_00000D_FORCE_DISP1(x) (((x) & 0x1) << 18) +#define G_00000D_FORCE_DISP1(x) (((x) >> 18) & 0x1) +#define C_00000D_FORCE_DISP1 0xFFFBFFFF +#define S_00000D_FORCE_TOP(x) (((x) & 0x1) << 19) +#define G_00000D_FORCE_TOP(x) (((x) >> 19) & 0x1) +#define C_00000D_FORCE_TOP 0xFFF7FFFF +#define S_00000D_FORCE_E2(x) (((x) & 0x1) << 20) +#define G_00000D_FORCE_E2(x) (((x) >> 20) & 0x1) +#define C_00000D_FORCE_E2 0xFFEFFFFF +#define S_00000D_FORCE_SE(x) (((x) & 0x1) << 21) +#define G_00000D_FORCE_SE(x) (((x) >> 21) & 0x1) +#define C_00000D_FORCE_SE 0xFFDFFFFF +#define S_00000D_FORCE_IDCT(x) (((x) & 0x1) << 22) +#define G_00000D_FORCE_IDCT(x) (((x) >> 22) & 0x1) +#define C_00000D_FORCE_IDCT 0xFFBFFFFF +#define S_00000D_FORCE_VIP(x) (((x) & 0x1) << 23) +#define G_00000D_FORCE_VIP(x) (((x) >> 23) & 0x1) +#define C_00000D_FORCE_VIP 0xFF7FFFFF +#define S_00000D_FORCE_RE(x) (((x) & 0x1) << 24) +#define G_00000D_FORCE_RE(x) (((x) >> 24) & 0x1) +#define C_00000D_FORCE_RE 0xFEFFFFFF +#define S_00000D_FORCE_PB(x) (((x) & 0x1) << 25) +#define G_00000D_FORCE_PB(x) (((x) >> 25) & 0x1) +#define C_00000D_FORCE_PB 0xFDFFFFFF +#define S_00000D_FORCE_TAM(x) (((x) & 0x1) << 26) +#define G_00000D_FORCE_TAM(x) (((x) >> 26) & 0x1) +#define C_00000D_FORCE_TAM 0xFBFFFFFF +#define S_00000D_FORCE_TDM(x) (((x) & 0x1) << 27) +#define G_00000D_FORCE_TDM(x) (((x) >> 27) & 0x1) +#define C_00000D_FORCE_TDM 0xF7FFFFFF +#define S_00000D_FORCE_RB(x) (((x) & 0x1) << 28) +#define G_00000D_FORCE_RB(x) (((x) >> 28) & 0x1) +#define C_00000D_FORCE_RB 0xEFFFFFFF +#define S_00000D_FORCE_TV_SCLK(x) (((x) & 0x1) << 29) +#define G_00000D_FORCE_TV_SCLK(x) (((x) >> 29) & 0x1) +#define C_00000D_FORCE_TV_SCLK 0xDFFFFFFF +#define S_00000D_FORCE_SUBPIC(x) (((x) & 0x1) << 30) +#define G_00000D_FORCE_SUBPIC(x) (((x) >> 30) & 0x1) +#define C_00000D_FORCE_SUBPIC 0xBFFFFFFF +#define S_00000D_FORCE_OV0(x) (((x) & 0x1) << 31) +#define G_00000D_FORCE_OV0(x) (((x) >> 31) & 0x1) +#define C_00000D_FORCE_OV0 0x7FFFFFFF + #endif diff --git a/drivers/gpu/drm/radeon/r420.c b/drivers/gpu/drm/radeon/r420.c index 49a2fdc57d27..1cefdbcc0850 100644 --- a/drivers/gpu/drm/radeon/r420.c +++ b/drivers/gpu/drm/radeon/r420.c @@ -155,6 +155,9 @@ static void r420_debugfs(struct radeon_device *rdev) static void r420_clock_resume(struct radeon_device *rdev) { u32 sclk_cntl; + + if (radeon_dynclks != -1 && radeon_dynclks) + radeon_atom_set_clock_gating(rdev, 1); sclk_cntl = RREG32_PLL(R_00000D_SCLK_CNTL); sclk_cntl |= S_00000D_FORCE_CP(1) | S_00000D_FORCE_VIP(1); if (rdev->family == CHIP_R420) @@ -167,6 +170,8 @@ static int r420_startup(struct radeon_device *rdev) int r; r300_mc_program(rdev); + /* Resume clock */ + r420_clock_resume(rdev); /* Initialize GART (initialize after TTM so we can allocate * memory through TTM but finalize after TTM) */ if (rdev->flags & RADEON_IS_PCIE) { @@ -267,7 +272,6 @@ int r420_init(struct radeon_device *rdev) { int r; - rdev->new_init_path = true; /* Initialize scratch registers */ radeon_scratch_init(rdev); /* Initialize surface registers */ @@ -307,6 +311,8 @@ int r420_init(struct radeon_device *rdev) } /* Initialize clocks */ radeon_get_clock_info(rdev->ddev); + /* Initialize power management */ + radeon_pm_init(rdev); /* Get vram informations */ r300_vram_info(rdev); /* Initialize memory controller (also test AGP) */ diff --git a/drivers/gpu/drm/radeon/r420d.h b/drivers/gpu/drm/radeon/r420d.h index a48a7db1e2aa..fc78d31a0b4a 100644 --- a/drivers/gpu/drm/radeon/r420d.h +++ b/drivers/gpu/drm/radeon/r420d.h @@ -212,9 +212,9 @@ #define S_00000D_FORCE_E2(x) (((x) & 0x1) << 20) #define G_00000D_FORCE_E2(x) (((x) >> 20) & 0x1) #define C_00000D_FORCE_E2 0xFFEFFFFF -#define S_00000D_FORCE_SE(x) (((x) & 0x1) << 21) -#define G_00000D_FORCE_SE(x) (((x) >> 21) & 0x1) -#define C_00000D_FORCE_SE 0xFFDFFFFF +#define S_00000D_FORCE_VAP(x) (((x) & 0x1) << 21) +#define G_00000D_FORCE_VAP(x) (((x) >> 21) & 0x1) +#define C_00000D_FORCE_VAP 0xFFDFFFFF #define S_00000D_FORCE_IDCT(x) (((x) & 0x1) << 22) #define G_00000D_FORCE_IDCT(x) (((x) >> 22) & 0x1) #define C_00000D_FORCE_IDCT 0xFFBFFFFF @@ -224,24 +224,24 @@ #define S_00000D_FORCE_RE(x) (((x) & 0x1) << 24) #define G_00000D_FORCE_RE(x) (((x) >> 24) & 0x1) #define C_00000D_FORCE_RE 0xFEFFFFFF -#define S_00000D_FORCE_PB(x) (((x) & 0x1) << 25) -#define G_00000D_FORCE_PB(x) (((x) >> 25) & 0x1) -#define C_00000D_FORCE_PB 0xFDFFFFFF +#define S_00000D_FORCE_SR(x) (((x) & 0x1) << 25) +#define G_00000D_FORCE_SR(x) (((x) >> 25) & 0x1) +#define C_00000D_FORCE_SR 0xFDFFFFFF #define S_00000D_FORCE_PX(x) (((x) & 0x1) << 26) #define G_00000D_FORCE_PX(x) (((x) >> 26) & 0x1) #define C_00000D_FORCE_PX 0xFBFFFFFF #define S_00000D_FORCE_TX(x) (((x) & 0x1) << 27) #define G_00000D_FORCE_TX(x) (((x) >> 27) & 0x1) #define C_00000D_FORCE_TX 0xF7FFFFFF -#define S_00000D_FORCE_RB(x) (((x) & 0x1) << 28) -#define G_00000D_FORCE_RB(x) (((x) >> 28) & 0x1) -#define C_00000D_FORCE_RB 0xEFFFFFFF +#define S_00000D_FORCE_US(x) (((x) & 0x1) << 28) +#define G_00000D_FORCE_US(x) (((x) >> 28) & 0x1) +#define C_00000D_FORCE_US 0xEFFFFFFF #define S_00000D_FORCE_TV_SCLK(x) (((x) & 0x1) << 29) #define G_00000D_FORCE_TV_SCLK(x) (((x) >> 29) & 0x1) #define C_00000D_FORCE_TV_SCLK 0xDFFFFFFF -#define S_00000D_FORCE_SUBPIC(x) (((x) & 0x1) << 30) -#define G_00000D_FORCE_SUBPIC(x) (((x) >> 30) & 0x1) -#define C_00000D_FORCE_SUBPIC 0xBFFFFFFF +#define S_00000D_FORCE_SU(x) (((x) & 0x1) << 30) +#define G_00000D_FORCE_SU(x) (((x) >> 30) & 0x1) +#define C_00000D_FORCE_SU 0xBFFFFFFF #define S_00000D_FORCE_OV0(x) (((x) & 0x1) << 31) #define G_00000D_FORCE_OV0(x) (((x) >> 31) & 0x1) #define C_00000D_FORCE_OV0 0x7FFFFFFF diff --git a/drivers/gpu/drm/radeon/r500_reg.h b/drivers/gpu/drm/radeon/r500_reg.h index e1d5e0331e19..7baa73955563 100644 --- a/drivers/gpu/drm/radeon/r500_reg.h +++ b/drivers/gpu/drm/radeon/r500_reg.h @@ -384,9 +384,16 @@ # define AVIVO_D1GRPH_TILED (1 << 20) # define AVIVO_D1GRPH_MACRO_ADDRESS_MODE (1 << 21) +/* The R7xx *_HIGH surface regs are backwards; the D1 regs are in the D2 + * block and vice versa. This applies to GRPH, CUR, etc. + */ #define AVIVO_D1GRPH_LUT_SEL 0x6108 #define AVIVO_D1GRPH_PRIMARY_SURFACE_ADDRESS 0x6110 +#define R700_D1GRPH_PRIMARY_SURFACE_ADDRESS_HIGH 0x6914 +#define R700_D2GRPH_PRIMARY_SURFACE_ADDRESS_HIGH 0x6114 #define AVIVO_D1GRPH_SECONDARY_SURFACE_ADDRESS 0x6118 +#define R700_D1GRPH_SECONDARY_SURFACE_ADDRESS_HIGH 0x691c +#define R700_D2GRPH_SECONDARY_SURFACE_ADDRESS_HIGH 0x611c #define AVIVO_D1GRPH_PITCH 0x6120 #define AVIVO_D1GRPH_SURFACE_OFFSET_X 0x6124 #define AVIVO_D1GRPH_SURFACE_OFFSET_Y 0x6128 @@ -404,6 +411,8 @@ # define AVIVO_D1CURSOR_MODE_MASK (3 << 8) # define AVIVO_D1CURSOR_MODE_24BPP 2 #define AVIVO_D1CUR_SURFACE_ADDRESS 0x6408 +#define R700_D1CUR_SURFACE_ADDRESS_HIGH 0x6c0c +#define R700_D2CUR_SURFACE_ADDRESS_HIGH 0x640c #define AVIVO_D1CUR_SIZE 0x6410 #define AVIVO_D1CUR_POSITION 0x6414 #define AVIVO_D1CUR_HOT_SPOT 0x6418 @@ -445,6 +454,8 @@ #define AVIVO_D1MODE_VBLANK_STATUS 0x6534 # define AVIVO_VBLANK_ACK (1 << 4) #define AVIVO_D1MODE_VLINE_START_END 0x6538 +#define AVIVO_D1MODE_VLINE_STATUS 0x653c +# define AVIVO_D1MODE_VLINE_STAT (1 << 12) #define AVIVO_DxMODE_INT_MASK 0x6540 # define AVIVO_D1MODE_INT_MASK (1 << 0) # define AVIVO_D2MODE_INT_MASK (1 << 8) @@ -502,6 +513,7 @@ #define AVIVO_D2MODE_VBLANK_STATUS 0x6d34 #define AVIVO_D2MODE_VLINE_START_END 0x6d38 +#define AVIVO_D2MODE_VLINE_STATUS 0x6d3c #define AVIVO_D2MODE_VIEWPORT_START 0x6d80 #define AVIVO_D2MODE_VIEWPORT_SIZE 0x6d84 #define AVIVO_D2MODE_EXT_OVERSCAN_LEFT_RIGHT 0x6d88 diff --git a/drivers/gpu/drm/radeon/r520.c b/drivers/gpu/drm/radeon/r520.c index d4b0b9d2e39b..f7435185c0a6 100644 --- a/drivers/gpu/drm/radeon/r520.c +++ b/drivers/gpu/drm/radeon/r520.c @@ -26,108 +26,13 @@ * Jerome Glisse */ #include "drmP.h" -#include "radeon_reg.h" #include "radeon.h" +#include "atom.h" +#include "r520d.h" -/* r520,rv530,rv560,rv570,r580 depends on : */ -void r100_hdp_reset(struct radeon_device *rdev); -void r420_pipes_init(struct radeon_device *rdev); -void rs600_mc_disable_clients(struct radeon_device *rdev); -void rs600_disable_vga(struct radeon_device *rdev); -int rv515_debugfs_pipes_info_init(struct radeon_device *rdev); -int rv515_debugfs_ga_info_init(struct radeon_device *rdev); +/* This files gather functions specifics to: r520,rv530,rv560,rv570,r580 */ -/* This files gather functions specifics to: - * r520,rv530,rv560,rv570,r580 - * - * Some of these functions might be used by newer ASICs. - */ -void r520_gpu_init(struct radeon_device *rdev); -int r520_mc_wait_for_idle(struct radeon_device *rdev); - - -/* - * MC - */ -int r520_mc_init(struct radeon_device *rdev) -{ - uint32_t tmp; - int r; - - if (r100_debugfs_rbbm_init(rdev)) { - DRM_ERROR("Failed to register debugfs file for RBBM !\n"); - } - if (rv515_debugfs_pipes_info_init(rdev)) { - DRM_ERROR("Failed to register debugfs file for pipes !\n"); - } - if (rv515_debugfs_ga_info_init(rdev)) { - DRM_ERROR("Failed to register debugfs file for pipes !\n"); - } - - r520_gpu_init(rdev); - rv370_pcie_gart_disable(rdev); - - /* Setup GPU memory space */ - rdev->mc.vram_location = 0xFFFFFFFFUL; - rdev->mc.gtt_location = 0xFFFFFFFFUL; - if (rdev->flags & RADEON_IS_AGP) { - r = radeon_agp_init(rdev); - if (r) { - printk(KERN_WARNING "[drm] Disabling AGP\n"); - rdev->flags &= ~RADEON_IS_AGP; - rdev->mc.gtt_size = radeon_gart_size * 1024 * 1024; - } else { - rdev->mc.gtt_location = rdev->mc.agp_base; - } - } - r = radeon_mc_setup(rdev); - if (r) { - return r; - } - - /* Program GPU memory space */ - rs600_mc_disable_clients(rdev); - if (r520_mc_wait_for_idle(rdev)) { - printk(KERN_WARNING "Failed to wait MC idle while " - "programming pipes. Bad things might happen.\n"); - } - /* Write VRAM size in case we are limiting it */ - WREG32(RADEON_CONFIG_MEMSIZE, rdev->mc.real_vram_size); - tmp = rdev->mc.vram_location + rdev->mc.mc_vram_size - 1; - tmp = REG_SET(R520_MC_FB_TOP, tmp >> 16); - tmp |= REG_SET(R520_MC_FB_START, rdev->mc.vram_location >> 16); - WREG32_MC(R520_MC_FB_LOCATION, tmp); - WREG32(RS690_HDP_FB_LOCATION, rdev->mc.vram_location >> 16); - WREG32(0x310, rdev->mc.vram_location); - if (rdev->flags & RADEON_IS_AGP) { - tmp = rdev->mc.gtt_location + rdev->mc.gtt_size - 1; - tmp = REG_SET(R520_MC_AGP_TOP, tmp >> 16); - tmp |= REG_SET(R520_MC_AGP_START, rdev->mc.gtt_location >> 16); - WREG32_MC(R520_MC_AGP_LOCATION, tmp); - WREG32_MC(R520_MC_AGP_BASE, rdev->mc.agp_base); - WREG32_MC(R520_MC_AGP_BASE_2, 0); - } else { - WREG32_MC(R520_MC_AGP_LOCATION, 0x0FFFFFFF); - WREG32_MC(R520_MC_AGP_BASE, 0); - WREG32_MC(R520_MC_AGP_BASE_2, 0); - } - return 0; -} - -void r520_mc_fini(struct radeon_device *rdev) -{ -} - - -/* - * Global GPU functions - */ -void r520_errata(struct radeon_device *rdev) -{ - rdev->pll_errata = 0; -} - -int r520_mc_wait_for_idle(struct radeon_device *rdev) +static int r520_mc_wait_for_idle(struct radeon_device *rdev) { unsigned i; uint32_t tmp; @@ -143,12 +48,12 @@ int r520_mc_wait_for_idle(struct radeon_device *rdev) return -1; } -void r520_gpu_init(struct radeon_device *rdev) +static void r520_gpu_init(struct radeon_device *rdev) { unsigned pipe_select_current, gb_pipe_select, tmp; r100_hdp_reset(rdev); - rs600_disable_vga(rdev); + rv515_vga_render_disable(rdev); /* * DST_PIPE_CONFIG 0x170C * GB_TILE_CONFIG 0x4018 @@ -186,10 +91,6 @@ void r520_gpu_init(struct radeon_device *rdev) } } - -/* - * VRAM info - */ static void r520_vram_get_type(struct radeon_device *rdev) { uint32_t tmp; @@ -233,7 +134,169 @@ void r520_vram_info(struct radeon_device *rdev) rdev->pm.sclk.full = rfixed_div(rdev->pm.sclk, a); } -void r520_bandwidth_update(struct radeon_device *rdev) +void r520_mc_program(struct radeon_device *rdev) +{ + struct rv515_mc_save save; + + /* Stops all mc clients */ + rv515_mc_stop(rdev, &save); + + /* Wait for mc idle */ + if (r520_mc_wait_for_idle(rdev)) + dev_warn(rdev->dev, "Wait MC idle timeout before updating MC.\n"); + /* Write VRAM size in case we are limiting it */ + WREG32(R_0000F8_CONFIG_MEMSIZE, rdev->mc.real_vram_size); + /* Program MC, should be a 32bits limited address space */ + WREG32_MC(R_000004_MC_FB_LOCATION, + S_000004_MC_FB_START(rdev->mc.vram_start >> 16) | + S_000004_MC_FB_TOP(rdev->mc.vram_end >> 16)); + WREG32(R_000134_HDP_FB_LOCATION, + S_000134_HDP_FB_START(rdev->mc.vram_start >> 16)); + if (rdev->flags & RADEON_IS_AGP) { + WREG32_MC(R_000005_MC_AGP_LOCATION, + S_000005_MC_AGP_START(rdev->mc.gtt_start >> 16) | + S_000005_MC_AGP_TOP(rdev->mc.gtt_end >> 16)); + WREG32_MC(R_000006_AGP_BASE, lower_32_bits(rdev->mc.agp_base)); + WREG32_MC(R_000007_AGP_BASE_2, + S_000007_AGP_BASE_ADDR_2(upper_32_bits(rdev->mc.agp_base))); + } else { + WREG32_MC(R_000005_MC_AGP_LOCATION, 0xFFFFFFFF); + WREG32_MC(R_000006_AGP_BASE, 0); + WREG32_MC(R_000007_AGP_BASE_2, 0); + } + + rv515_mc_resume(rdev, &save); +} + +static int r520_startup(struct radeon_device *rdev) +{ + int r; + + r520_mc_program(rdev); + /* Resume clock */ + rv515_clock_startup(rdev); + /* Initialize GPU configuration (# pipes, ...) */ + r520_gpu_init(rdev); + /* Initialize GART (initialize after TTM so we can allocate + * memory through TTM but finalize after TTM) */ + if (rdev->flags & RADEON_IS_PCIE) { + r = rv370_pcie_gart_enable(rdev); + if (r) + return r; + } + /* Enable IRQ */ + rdev->irq.sw_int = true; + rs600_irq_set(rdev); + /* 1M ring buffer */ + r = r100_cp_init(rdev, 1024 * 1024); + if (r) { + dev_err(rdev->dev, "failled initializing CP (%d).\n", r); + return r; + } + r = r100_wb_init(rdev); + if (r) + dev_err(rdev->dev, "failled initializing WB (%d).\n", r); + r = r100_ib_init(rdev); + if (r) { + dev_err(rdev->dev, "failled initializing IB (%d).\n", r); + return r; + } + return 0; +} + +int r520_resume(struct radeon_device *rdev) { - rv515_bandwidth_avivo_update(rdev); + /* Make sur GART are not working */ + if (rdev->flags & RADEON_IS_PCIE) + rv370_pcie_gart_disable(rdev); + /* Resume clock before doing reset */ + rv515_clock_startup(rdev); + /* Reset gpu before posting otherwise ATOM will enter infinite loop */ + if (radeon_gpu_reset(rdev)) { + dev_warn(rdev->dev, "GPU reset failed ! (0xE40=0x%08X, 0x7C0=0x%08X)\n", + RREG32(R_000E40_RBBM_STATUS), + RREG32(R_0007C0_CP_STAT)); + } + /* post */ + atom_asic_init(rdev->mode_info.atom_context); + /* Resume clock after posting */ + rv515_clock_startup(rdev); + return r520_startup(rdev); +} + +int r520_init(struct radeon_device *rdev) +{ + int r; + + /* Initialize scratch registers */ + radeon_scratch_init(rdev); + /* Initialize surface registers */ + radeon_surface_init(rdev); + /* TODO: disable VGA need to use VGA request */ + /* BIOS*/ + if (!radeon_get_bios(rdev)) { + if (ASIC_IS_AVIVO(rdev)) + return -EINVAL; + } + if (rdev->is_atom_bios) { + r = radeon_atombios_init(rdev); + if (r) + return r; + } else { + dev_err(rdev->dev, "Expecting atombios for RV515 GPU\n"); + return -EINVAL; + } + /* Reset gpu before posting otherwise ATOM will enter infinite loop */ + if (radeon_gpu_reset(rdev)) { + dev_warn(rdev->dev, + "GPU reset failed ! (0xE40=0x%08X, 0x7C0=0x%08X)\n", + RREG32(R_000E40_RBBM_STATUS), + RREG32(R_0007C0_CP_STAT)); + } + /* check if cards are posted or not */ + if (!radeon_card_posted(rdev) && rdev->bios) { + DRM_INFO("GPU not posted. posting now...\n"); + atom_asic_init(rdev->mode_info.atom_context); + } + /* Initialize clocks */ + radeon_get_clock_info(rdev->ddev); + /* Initialize power management */ + radeon_pm_init(rdev); + /* Get vram informations */ + r520_vram_info(rdev); + /* Initialize memory controller (also test AGP) */ + r = r420_mc_init(rdev); + if (r) + return r; + rv515_debugfs(rdev); + /* Fence driver */ + r = radeon_fence_driver_init(rdev); + if (r) + return r; + r = radeon_irq_kms_init(rdev); + if (r) + return r; + /* Memory manager */ + r = radeon_object_init(rdev); + if (r) + return r; + r = rv370_pcie_gart_init(rdev); + if (r) + return r; + rv515_set_safe_registers(rdev); + rdev->accel_working = true; + r = r520_startup(rdev); + if (r) { + /* Somethings want wront with the accel init stop accel */ + dev_err(rdev->dev, "Disabling GPU acceleration\n"); + rv515_suspend(rdev); + r100_cp_fini(rdev); + r100_wb_fini(rdev); + r100_ib_fini(rdev); + rv370_pcie_gart_fini(rdev); + radeon_agp_fini(rdev); + radeon_irq_kms_fini(rdev); + rdev->accel_working = false; + } + return 0; } diff --git a/drivers/gpu/drm/radeon/r520d.h b/drivers/gpu/drm/radeon/r520d.h new file mode 100644 index 000000000000..61af61f644bc --- /dev/null +++ b/drivers/gpu/drm/radeon/r520d.h @@ -0,0 +1,187 @@ +/* + * Copyright 2008 Advanced Micro Devices, Inc. + * Copyright 2008 Red Hat Inc. + * Copyright 2009 Jerome Glisse. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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. + * + * Authors: Dave Airlie + * Alex Deucher + * Jerome Glisse + */ +#ifndef __R520D_H__ +#define __R520D_H__ + +/* Registers */ +#define R_0000F8_CONFIG_MEMSIZE 0x0000F8 +#define S_0000F8_CONFIG_MEMSIZE(x) (((x) & 0xFFFFFFFF) << 0) +#define G_0000F8_CONFIG_MEMSIZE(x) (((x) >> 0) & 0xFFFFFFFF) +#define C_0000F8_CONFIG_MEMSIZE 0x00000000 +#define R_000134_HDP_FB_LOCATION 0x000134 +#define S_000134_HDP_FB_START(x) (((x) & 0xFFFF) << 0) +#define G_000134_HDP_FB_START(x) (((x) >> 0) & 0xFFFF) +#define C_000134_HDP_FB_START 0xFFFF0000 +#define R_0007C0_CP_STAT 0x0007C0 +#define S_0007C0_MRU_BUSY(x) (((x) & 0x1) << 0) +#define G_0007C0_MRU_BUSY(x) (((x) >> 0) & 0x1) +#define C_0007C0_MRU_BUSY 0xFFFFFFFE +#define S_0007C0_MWU_BUSY(x) (((x) & 0x1) << 1) +#define G_0007C0_MWU_BUSY(x) (((x) >> 1) & 0x1) +#define C_0007C0_MWU_BUSY 0xFFFFFFFD +#define S_0007C0_RSIU_BUSY(x) (((x) & 0x1) << 2) +#define G_0007C0_RSIU_BUSY(x) (((x) >> 2) & 0x1) +#define C_0007C0_RSIU_BUSY 0xFFFFFFFB +#define S_0007C0_RCIU_BUSY(x) (((x) & 0x1) << 3) +#define G_0007C0_RCIU_BUSY(x) (((x) >> 3) & 0x1) +#define C_0007C0_RCIU_BUSY 0xFFFFFFF7 +#define S_0007C0_CSF_PRIMARY_BUSY(x) (((x) & 0x1) << 9) +#define G_0007C0_CSF_PRIMARY_BUSY(x) (((x) >> 9) & 0x1) +#define C_0007C0_CSF_PRIMARY_BUSY 0xFFFFFDFF +#define S_0007C0_CSF_INDIRECT_BUSY(x) (((x) & 0x1) << 10) +#define G_0007C0_CSF_INDIRECT_BUSY(x) (((x) >> 10) & 0x1) +#define C_0007C0_CSF_INDIRECT_BUSY 0xFFFFFBFF +#define S_0007C0_CSQ_PRIMARY_BUSY(x) (((x) & 0x1) << 11) +#define G_0007C0_CSQ_PRIMARY_BUSY(x) (((x) >> 11) & 0x1) +#define C_0007C0_CSQ_PRIMARY_BUSY 0xFFFFF7FF +#define S_0007C0_CSQ_INDIRECT_BUSY(x) (((x) & 0x1) << 12) +#define G_0007C0_CSQ_INDIRECT_BUSY(x) (((x) >> 12) & 0x1) +#define C_0007C0_CSQ_INDIRECT_BUSY 0xFFFFEFFF +#define S_0007C0_CSI_BUSY(x) (((x) & 0x1) << 13) +#define G_0007C0_CSI_BUSY(x) (((x) >> 13) & 0x1) +#define C_0007C0_CSI_BUSY 0xFFFFDFFF +#define S_0007C0_CSF_INDIRECT2_BUSY(x) (((x) & 0x1) << 14) +#define G_0007C0_CSF_INDIRECT2_BUSY(x) (((x) >> 14) & 0x1) +#define C_0007C0_CSF_INDIRECT2_BUSY 0xFFFFBFFF +#define S_0007C0_CSQ_INDIRECT2_BUSY(x) (((x) & 0x1) << 15) +#define G_0007C0_CSQ_INDIRECT2_BUSY(x) (((x) >> 15) & 0x1) +#define C_0007C0_CSQ_INDIRECT2_BUSY 0xFFFF7FFF +#define S_0007C0_GUIDMA_BUSY(x) (((x) & 0x1) << 28) +#define G_0007C0_GUIDMA_BUSY(x) (((x) >> 28) & 0x1) +#define C_0007C0_GUIDMA_BUSY 0xEFFFFFFF +#define S_0007C0_VIDDMA_BUSY(x) (((x) & 0x1) << 29) +#define G_0007C0_VIDDMA_BUSY(x) (((x) >> 29) & 0x1) +#define C_0007C0_VIDDMA_BUSY 0xDFFFFFFF +#define S_0007C0_CMDSTRM_BUSY(x) (((x) & 0x1) << 30) +#define G_0007C0_CMDSTRM_BUSY(x) (((x) >> 30) & 0x1) +#define C_0007C0_CMDSTRM_BUSY 0xBFFFFFFF +#define S_0007C0_CP_BUSY(x) (((x) & 0x1) << 31) +#define G_0007C0_CP_BUSY(x) (((x) >> 31) & 0x1) +#define C_0007C0_CP_BUSY 0x7FFFFFFF +#define R_000E40_RBBM_STATUS 0x000E40 +#define S_000E40_CMDFIFO_AVAIL(x) (((x) & 0x7F) << 0) +#define G_000E40_CMDFIFO_AVAIL(x) (((x) >> 0) & 0x7F) +#define C_000E40_CMDFIFO_AVAIL 0xFFFFFF80 +#define S_000E40_HIRQ_ON_RBB(x) (((x) & 0x1) << 8) +#define G_000E40_HIRQ_ON_RBB(x) (((x) >> 8) & 0x1) +#define C_000E40_HIRQ_ON_RBB 0xFFFFFEFF +#define S_000E40_CPRQ_ON_RBB(x) (((x) & 0x1) << 9) +#define G_000E40_CPRQ_ON_RBB(x) (((x) >> 9) & 0x1) +#define C_000E40_CPRQ_ON_RBB 0xFFFFFDFF +#define S_000E40_CFRQ_ON_RBB(x) (((x) & 0x1) << 10) +#define G_000E40_CFRQ_ON_RBB(x) (((x) >> 10) & 0x1) +#define C_000E40_CFRQ_ON_RBB 0xFFFFFBFF +#define S_000E40_HIRQ_IN_RTBUF(x) (((x) & 0x1) << 11) +#define G_000E40_HIRQ_IN_RTBUF(x) (((x) >> 11) & 0x1) +#define C_000E40_HIRQ_IN_RTBUF 0xFFFFF7FF +#define S_000E40_CPRQ_IN_RTBUF(x) (((x) & 0x1) << 12) +#define G_000E40_CPRQ_IN_RTBUF(x) (((x) >> 12) & 0x1) +#define C_000E40_CPRQ_IN_RTBUF 0xFFFFEFFF +#define S_000E40_CFRQ_IN_RTBUF(x) (((x) & 0x1) << 13) +#define G_000E40_CFRQ_IN_RTBUF(x) (((x) >> 13) & 0x1) +#define C_000E40_CFRQ_IN_RTBUF 0xFFFFDFFF +#define S_000E40_CF_PIPE_BUSY(x) (((x) & 0x1) << 14) +#define G_000E40_CF_PIPE_BUSY(x) (((x) >> 14) & 0x1) +#define C_000E40_CF_PIPE_BUSY 0xFFFFBFFF +#define S_000E40_ENG_EV_BUSY(x) (((x) & 0x1) << 15) +#define G_000E40_ENG_EV_BUSY(x) (((x) >> 15) & 0x1) +#define C_000E40_ENG_EV_BUSY 0xFFFF7FFF +#define S_000E40_CP_CMDSTRM_BUSY(x) (((x) & 0x1) << 16) +#define G_000E40_CP_CMDSTRM_BUSY(x) (((x) >> 16) & 0x1) +#define C_000E40_CP_CMDSTRM_BUSY 0xFFFEFFFF +#define S_000E40_E2_BUSY(x) (((x) & 0x1) << 17) +#define G_000E40_E2_BUSY(x) (((x) >> 17) & 0x1) +#define C_000E40_E2_BUSY 0xFFFDFFFF +#define S_000E40_RB2D_BUSY(x) (((x) & 0x1) << 18) +#define G_000E40_RB2D_BUSY(x) (((x) >> 18) & 0x1) +#define C_000E40_RB2D_BUSY 0xFFFBFFFF +#define S_000E40_RB3D_BUSY(x) (((x) & 0x1) << 19) +#define G_000E40_RB3D_BUSY(x) (((x) >> 19) & 0x1) +#define C_000E40_RB3D_BUSY 0xFFF7FFFF +#define S_000E40_VAP_BUSY(x) (((x) & 0x1) << 20) +#define G_000E40_VAP_BUSY(x) (((x) >> 20) & 0x1) +#define C_000E40_VAP_BUSY 0xFFEFFFFF +#define S_000E40_RE_BUSY(x) (((x) & 0x1) << 21) +#define G_000E40_RE_BUSY(x) (((x) >> 21) & 0x1) +#define C_000E40_RE_BUSY 0xFFDFFFFF +#define S_000E40_TAM_BUSY(x) (((x) & 0x1) << 22) +#define G_000E40_TAM_BUSY(x) (((x) >> 22) & 0x1) +#define C_000E40_TAM_BUSY 0xFFBFFFFF +#define S_000E40_TDM_BUSY(x) (((x) & 0x1) << 23) +#define G_000E40_TDM_BUSY(x) (((x) >> 23) & 0x1) +#define C_000E40_TDM_BUSY 0xFF7FFFFF +#define S_000E40_PB_BUSY(x) (((x) & 0x1) << 24) +#define G_000E40_PB_BUSY(x) (((x) >> 24) & 0x1) +#define C_000E40_PB_BUSY 0xFEFFFFFF +#define S_000E40_TIM_BUSY(x) (((x) & 0x1) << 25) +#define G_000E40_TIM_BUSY(x) (((x) >> 25) & 0x1) +#define C_000E40_TIM_BUSY 0xFDFFFFFF +#define S_000E40_GA_BUSY(x) (((x) & 0x1) << 26) +#define G_000E40_GA_BUSY(x) (((x) >> 26) & 0x1) +#define C_000E40_GA_BUSY 0xFBFFFFFF +#define S_000E40_CBA2D_BUSY(x) (((x) & 0x1) << 27) +#define G_000E40_CBA2D_BUSY(x) (((x) >> 27) & 0x1) +#define C_000E40_CBA2D_BUSY 0xF7FFFFFF +#define S_000E40_RBBM_HIBUSY(x) (((x) & 0x1) << 28) +#define G_000E40_RBBM_HIBUSY(x) (((x) >> 28) & 0x1) +#define C_000E40_RBBM_HIBUSY 0xEFFFFFFF +#define S_000E40_SKID_CFBUSY(x) (((x) & 0x1) << 29) +#define G_000E40_SKID_CFBUSY(x) (((x) >> 29) & 0x1) +#define C_000E40_SKID_CFBUSY 0xDFFFFFFF +#define S_000E40_VAP_VF_BUSY(x) (((x) & 0x1) << 30) +#define G_000E40_VAP_VF_BUSY(x) (((x) >> 30) & 0x1) +#define C_000E40_VAP_VF_BUSY 0xBFFFFFFF +#define S_000E40_GUI_ACTIVE(x) (((x) & 0x1) << 31) +#define G_000E40_GUI_ACTIVE(x) (((x) >> 31) & 0x1) +#define C_000E40_GUI_ACTIVE 0x7FFFFFFF + + +#define R_000004_MC_FB_LOCATION 0x000004 +#define S_000004_MC_FB_START(x) (((x) & 0xFFFF) << 0) +#define G_000004_MC_FB_START(x) (((x) >> 0) & 0xFFFF) +#define C_000004_MC_FB_START 0xFFFF0000 +#define S_000004_MC_FB_TOP(x) (((x) & 0xFFFF) << 16) +#define G_000004_MC_FB_TOP(x) (((x) >> 16) & 0xFFFF) +#define C_000004_MC_FB_TOP 0x0000FFFF +#define R_000005_MC_AGP_LOCATION 0x000005 +#define S_000005_MC_AGP_START(x) (((x) & 0xFFFF) << 0) +#define G_000005_MC_AGP_START(x) (((x) >> 0) & 0xFFFF) +#define C_000005_MC_AGP_START 0xFFFF0000 +#define S_000005_MC_AGP_TOP(x) (((x) & 0xFFFF) << 16) +#define G_000005_MC_AGP_TOP(x) (((x) >> 16) & 0xFFFF) +#define C_000005_MC_AGP_TOP 0x0000FFFF +#define R_000006_AGP_BASE 0x000006 +#define S_000006_AGP_BASE_ADDR(x) (((x) & 0xFFFFFFFF) << 0) +#define G_000006_AGP_BASE_ADDR(x) (((x) >> 0) & 0xFFFFFFFF) +#define C_000006_AGP_BASE_ADDR 0x00000000 +#define R_000007_AGP_BASE_2 0x000007 +#define S_000007_AGP_BASE_ADDR_2(x) (((x) & 0xF) << 0) +#define G_000007_AGP_BASE_ADDR_2(x) (((x) >> 0) & 0xF) +#define C_000007_AGP_BASE_ADDR_2 0xFFFFFFF0 + +#endif diff --git a/drivers/gpu/drm/radeon/r600.c b/drivers/gpu/drm/radeon/r600.c index eab31c1d6df1..278f646bc18e 100644 --- a/drivers/gpu/drm/radeon/r600.c +++ b/drivers/gpu/drm/radeon/r600.c @@ -33,8 +33,8 @@ #include "radeon.h" #include "radeon_mode.h" #include "r600d.h" -#include "avivod.h" #include "atom.h" +#include "avivod.h" #define PFP_UCODE_SIZE 576 #define PM4_UCODE_SIZE 1792 @@ -65,16 +65,11 @@ MODULE_FIRMWARE("radeon/RV710_me.bin"); int r600_debugfs_mc_info_init(struct radeon_device *rdev); -/* This files gather functions specifics to: - * r600,rv610,rv630,rv620,rv635,rv670 - * - * Some of these functions might be used by newer ASICs. - */ +/* r600,rv610,rv630,rv620,rv635,rv670 */ int r600_mc_wait_for_idle(struct radeon_device *rdev); void r600_gpu_init(struct radeon_device *rdev); void r600_fini(struct radeon_device *rdev); - /* * R600 PCIE GART */ @@ -168,7 +163,7 @@ int r600_pcie_gart_enable(struct radeon_device *rdev) WREG32(MC_VM_L1_TLB_MCB_RD_SEM_CNTL, tmp | ENABLE_SEMAPHORE_MODE); WREG32(MC_VM_L1_TLB_MCB_WR_SEM_CNTL, tmp | ENABLE_SEMAPHORE_MODE); WREG32(VM_CONTEXT0_PAGE_TABLE_START_ADDR, rdev->mc.gtt_start >> 12); - WREG32(VM_CONTEXT0_PAGE_TABLE_END_ADDR, (rdev->mc.gtt_end - 1) >> 12); + WREG32(VM_CONTEXT0_PAGE_TABLE_END_ADDR, rdev->mc.gtt_end >> 12); WREG32(VM_CONTEXT0_PAGE_TABLE_BASE_ADDR, rdev->gart.table_addr >> 12); WREG32(VM_CONTEXT0_CNTL, ENABLE_CONTEXT | PAGE_TABLE_DEPTH(0) | RANGE_PROTECTION_FAULT_ENABLE_DEFAULT); @@ -225,6 +220,40 @@ void r600_pcie_gart_fini(struct radeon_device *rdev) radeon_gart_fini(rdev); } +void r600_agp_enable(struct radeon_device *rdev) +{ + u32 tmp; + int i; + + /* Setup L2 cache */ + WREG32(VM_L2_CNTL, ENABLE_L2_CACHE | ENABLE_L2_FRAGMENT_PROCESSING | + ENABLE_L2_PTE_CACHE_LRU_UPDATE_BY_WRITE | + EFFECTIVE_L2_QUEUE_SIZE(7)); + WREG32(VM_L2_CNTL2, 0); + WREG32(VM_L2_CNTL3, BANK_SELECT_0(0) | BANK_SELECT_1(1)); + /* Setup TLB control */ + tmp = ENABLE_L1_TLB | ENABLE_L1_FRAGMENT_PROCESSING | + SYSTEM_ACCESS_MODE_NOT_IN_SYS | + EFFECTIVE_L1_TLB_SIZE(5) | EFFECTIVE_L1_QUEUE_SIZE(5) | + ENABLE_WAIT_L2_QUERY; + WREG32(MC_VM_L1_TLB_MCB_RD_SYS_CNTL, tmp); + WREG32(MC_VM_L1_TLB_MCB_WR_SYS_CNTL, tmp); + WREG32(MC_VM_L1_TLB_MCB_RD_HDP_CNTL, tmp | ENABLE_L1_STRICT_ORDERING); + WREG32(MC_VM_L1_TLB_MCB_WR_HDP_CNTL, tmp); + WREG32(MC_VM_L1_TLB_MCD_RD_A_CNTL, tmp); + WREG32(MC_VM_L1_TLB_MCD_WR_A_CNTL, tmp); + WREG32(MC_VM_L1_TLB_MCD_RD_B_CNTL, tmp); + WREG32(MC_VM_L1_TLB_MCD_WR_B_CNTL, tmp); + WREG32(MC_VM_L1_TLB_MCB_RD_GFX_CNTL, tmp); + WREG32(MC_VM_L1_TLB_MCB_WR_GFX_CNTL, tmp); + WREG32(MC_VM_L1_TLB_MCB_RD_PDMA_CNTL, tmp); + WREG32(MC_VM_L1_TLB_MCB_WR_PDMA_CNTL, tmp); + WREG32(MC_VM_L1_TLB_MCB_RD_SEM_CNTL, tmp | ENABLE_SEMAPHORE_MODE); + WREG32(MC_VM_L1_TLB_MCB_WR_SEM_CNTL, tmp | ENABLE_SEMAPHORE_MODE); + for (i = 0; i < 7; i++) + WREG32(VM_CONTEXT0_CNTL + (i * 4), 0); +} + int r600_mc_wait_for_idle(struct radeon_device *rdev) { unsigned i; @@ -240,14 +269,9 @@ int r600_mc_wait_for_idle(struct radeon_device *rdev) return -1; } -static void r600_mc_resume(struct radeon_device *rdev) +static void r600_mc_program(struct radeon_device *rdev) { - u32 d1vga_control, d2vga_control; - u32 vga_render_control, vga_hdp_control; - u32 d1crtc_control, d2crtc_control; - u32 new_d1grph_primary, new_d1grph_secondary; - u32 new_d2grph_primary, new_d2grph_secondary; - u64 old_vram_start; + struct rv515_mc_save save; u32 tmp; int i, j; @@ -261,99 +285,64 @@ static void r600_mc_resume(struct radeon_device *rdev) } WREG32(HDP_REG_COHERENCY_FLUSH_CNTL, 0); - d1vga_control = RREG32(D1VGA_CONTROL); - d2vga_control = RREG32(D2VGA_CONTROL); - vga_render_control = RREG32(VGA_RENDER_CONTROL); - vga_hdp_control = RREG32(VGA_HDP_CONTROL); - d1crtc_control = RREG32(D1CRTC_CONTROL); - d2crtc_control = RREG32(D2CRTC_CONTROL); - old_vram_start = (u64)(RREG32(MC_VM_FB_LOCATION) & 0xFFFF) << 24; - new_d1grph_primary = RREG32(D1GRPH_PRIMARY_SURFACE_ADDRESS); - new_d1grph_secondary = RREG32(D1GRPH_SECONDARY_SURFACE_ADDRESS); - new_d1grph_primary += rdev->mc.vram_start - old_vram_start; - new_d1grph_secondary += rdev->mc.vram_start - old_vram_start; - new_d2grph_primary = RREG32(D2GRPH_PRIMARY_SURFACE_ADDRESS); - new_d2grph_secondary = RREG32(D2GRPH_SECONDARY_SURFACE_ADDRESS); - new_d2grph_primary += rdev->mc.vram_start - old_vram_start; - new_d2grph_secondary += rdev->mc.vram_start - old_vram_start; - - /* Stop all video */ - WREG32(D1VGA_CONTROL, 0); - WREG32(D2VGA_CONTROL, 0); - WREG32(VGA_RENDER_CONTROL, 0); - WREG32(D1CRTC_UPDATE_LOCK, 1); - WREG32(D2CRTC_UPDATE_LOCK, 1); - WREG32(D1CRTC_CONTROL, 0); - WREG32(D2CRTC_CONTROL, 0); - WREG32(D1CRTC_UPDATE_LOCK, 0); - WREG32(D2CRTC_UPDATE_LOCK, 0); - - mdelay(1); + rv515_mc_stop(rdev, &save); if (r600_mc_wait_for_idle(rdev)) { - printk(KERN_WARNING "[drm] MC not idle !\n"); + dev_warn(rdev->dev, "Wait for MC idle timedout !\n"); } - - /* Lockout access through VGA aperture*/ + /* Lockout access through VGA aperture (doesn't exist before R600) */ WREG32(VGA_HDP_CONTROL, VGA_MEMORY_DISABLE); - /* Update configuration */ - WREG32(MC_VM_SYSTEM_APERTURE_LOW_ADDR, rdev->mc.vram_start >> 12); - WREG32(MC_VM_SYSTEM_APERTURE_HIGH_ADDR, (rdev->mc.vram_end - 1) >> 12); + if (rdev->flags & RADEON_IS_AGP) { + if (rdev->mc.vram_start < rdev->mc.gtt_start) { + /* VRAM before AGP */ + WREG32(MC_VM_SYSTEM_APERTURE_LOW_ADDR, + rdev->mc.vram_start >> 12); + WREG32(MC_VM_SYSTEM_APERTURE_HIGH_ADDR, + rdev->mc.gtt_end >> 12); + } else { + /* VRAM after AGP */ + WREG32(MC_VM_SYSTEM_APERTURE_LOW_ADDR, + rdev->mc.gtt_start >> 12); + WREG32(MC_VM_SYSTEM_APERTURE_HIGH_ADDR, + rdev->mc.vram_end >> 12); + } + } else { + WREG32(MC_VM_SYSTEM_APERTURE_LOW_ADDR, rdev->mc.vram_start >> 12); + WREG32(MC_VM_SYSTEM_APERTURE_HIGH_ADDR, rdev->mc.vram_end >> 12); + } WREG32(MC_VM_SYSTEM_APERTURE_DEFAULT_ADDR, 0); - tmp = (((rdev->mc.vram_end - 1) >> 24) & 0xFFFF) << 16; + tmp = ((rdev->mc.vram_end >> 24) & 0xFFFF) << 16; tmp |= ((rdev->mc.vram_start >> 24) & 0xFFFF); WREG32(MC_VM_FB_LOCATION, tmp); WREG32(HDP_NONSURFACE_BASE, (rdev->mc.vram_start >> 8)); WREG32(HDP_NONSURFACE_INFO, (2 << 7)); - WREG32(HDP_NONSURFACE_SIZE, (rdev->mc.mc_vram_size - 1) | 0x3FF); + WREG32(HDP_NONSURFACE_SIZE, rdev->mc.mc_vram_size | 0x3FF); if (rdev->flags & RADEON_IS_AGP) { - WREG32(MC_VM_AGP_TOP, (rdev->mc.gtt_end - 1) >> 16); - WREG32(MC_VM_AGP_BOT, rdev->mc.gtt_start >> 16); + WREG32(MC_VM_AGP_TOP, rdev->mc.gtt_end >> 22); + WREG32(MC_VM_AGP_BOT, rdev->mc.gtt_start >> 22); WREG32(MC_VM_AGP_BASE, rdev->mc.agp_base >> 22); } else { WREG32(MC_VM_AGP_BASE, 0); WREG32(MC_VM_AGP_TOP, 0x0FFFFFFF); WREG32(MC_VM_AGP_BOT, 0x0FFFFFFF); } - WREG32(D1GRPH_PRIMARY_SURFACE_ADDRESS, new_d1grph_primary); - WREG32(D1GRPH_SECONDARY_SURFACE_ADDRESS, new_d1grph_secondary); - WREG32(D2GRPH_PRIMARY_SURFACE_ADDRESS, new_d2grph_primary); - WREG32(D2GRPH_SECONDARY_SURFACE_ADDRESS, new_d2grph_secondary); - WREG32(VGA_MEMORY_BASE_ADDRESS, rdev->mc.vram_start); - - /* Unlock host access */ - WREG32(VGA_HDP_CONTROL, vga_hdp_control); - - mdelay(1); if (r600_mc_wait_for_idle(rdev)) { - printk(KERN_WARNING "[drm] MC not idle !\n"); + dev_warn(rdev->dev, "Wait for MC idle timedout !\n"); } - - /* Restore video state */ - WREG32(D1CRTC_UPDATE_LOCK, 1); - WREG32(D2CRTC_UPDATE_LOCK, 1); - WREG32(D1CRTC_CONTROL, d1crtc_control); - WREG32(D2CRTC_CONTROL, d2crtc_control); - WREG32(D1CRTC_UPDATE_LOCK, 0); - WREG32(D2CRTC_UPDATE_LOCK, 0); - WREG32(D1VGA_CONTROL, d1vga_control); - WREG32(D2VGA_CONTROL, d2vga_control); - WREG32(VGA_RENDER_CONTROL, vga_render_control); - + rv515_mc_resume(rdev, &save); /* we need to own VRAM, so turn off the VGA renderer here * to stop it overwriting our objects */ - radeon_avivo_vga_render_disable(rdev); + rv515_vga_render_disable(rdev); } int r600_mc_init(struct radeon_device *rdev) { fixed20_12 a; u32 tmp; - int chansize; + int chansize, numchan; int r; /* Get VRAM informations */ - rdev->mc.vram_width = 128; rdev->mc.vram_is_ddr = true; tmp = RREG32(RAMCFG); if (tmp & CHANSIZE_OVERRIDE) { @@ -363,23 +352,36 @@ int r600_mc_init(struct radeon_device *rdev) } else { chansize = 32; } - if (rdev->family == CHIP_R600) { - rdev->mc.vram_width = 8 * chansize; - } else if (rdev->family == CHIP_RV670) { - rdev->mc.vram_width = 4 * chansize; - } else if ((rdev->family == CHIP_RV610) || - (rdev->family == CHIP_RV620)) { - rdev->mc.vram_width = chansize; - } else if ((rdev->family == CHIP_RV630) || - (rdev->family == CHIP_RV635)) { - rdev->mc.vram_width = 2 * chansize; + tmp = RREG32(CHMAP); + switch ((tmp & NOOFCHAN_MASK) >> NOOFCHAN_SHIFT) { + case 0: + default: + numchan = 1; + break; + case 1: + numchan = 2; + break; + case 2: + numchan = 4; + break; + case 3: + numchan = 8; + break; } + rdev->mc.vram_width = numchan * chansize; /* Could aper size report 0 ? */ rdev->mc.aper_base = drm_get_resource_start(rdev->ddev, 0); rdev->mc.aper_size = drm_get_resource_len(rdev->ddev, 0); /* Setup GPU memory space */ rdev->mc.mc_vram_size = RREG32(CONFIG_MEMSIZE); rdev->mc.real_vram_size = RREG32(CONFIG_MEMSIZE); + + if (rdev->mc.mc_vram_size > rdev->mc.aper_size) + rdev->mc.mc_vram_size = rdev->mc.aper_size; + + if (rdev->mc.real_vram_size > rdev->mc.aper_size) + rdev->mc.real_vram_size = rdev->mc.aper_size; + if (rdev->flags & RADEON_IS_AGP) { r = radeon_agp_init(rdev); if (r) @@ -407,40 +409,34 @@ int r600_mc_init(struct radeon_device *rdev) rdev->mc.gtt_location = rdev->mc.mc_vram_size; } } else { - if (rdev->family == CHIP_RS780 || rdev->family == CHIP_RS880) { - rdev->mc.vram_location = (RREG32(MC_VM_FB_LOCATION) & - 0xFFFF) << 24; - rdev->mc.gtt_size = radeon_gart_size * 1024 * 1024; - tmp = rdev->mc.vram_location + rdev->mc.mc_vram_size; - if ((0xFFFFFFFFUL - tmp) >= rdev->mc.gtt_size) { - /* Enough place after vram */ - rdev->mc.gtt_location = tmp; - } else if (rdev->mc.vram_location >= rdev->mc.gtt_size) { - /* Enough place before vram */ + rdev->mc.gtt_size = radeon_gart_size * 1024 * 1024; + rdev->mc.vram_location = (RREG32(MC_VM_FB_LOCATION) & + 0xFFFF) << 24; + tmp = rdev->mc.vram_location + rdev->mc.mc_vram_size; + if ((0xFFFFFFFFUL - tmp) >= rdev->mc.gtt_size) { + /* Enough place after vram */ + rdev->mc.gtt_location = tmp; + } else if (rdev->mc.vram_location >= rdev->mc.gtt_size) { + /* Enough place before vram */ + rdev->mc.gtt_location = 0; + } else { + /* Not enough place after or before shrink + * gart size + */ + if (rdev->mc.vram_location > (0xFFFFFFFFUL - tmp)) { rdev->mc.gtt_location = 0; + rdev->mc.gtt_size = rdev->mc.vram_location; } else { - /* Not enough place after or before shrink - * gart size - */ - if (rdev->mc.vram_location > (0xFFFFFFFFUL - tmp)) { - rdev->mc.gtt_location = 0; - rdev->mc.gtt_size = rdev->mc.vram_location; - } else { - rdev->mc.gtt_location = tmp; - rdev->mc.gtt_size = 0xFFFFFFFFUL - tmp; - } + rdev->mc.gtt_location = tmp; + rdev->mc.gtt_size = 0xFFFFFFFFUL - tmp; } - rdev->mc.gtt_location = rdev->mc.mc_vram_size; - } else { - rdev->mc.vram_location = 0x00000000UL; - rdev->mc.gtt_location = rdev->mc.mc_vram_size; - rdev->mc.gtt_size = radeon_gart_size * 1024 * 1024; } + rdev->mc.gtt_location = rdev->mc.mc_vram_size; } rdev->mc.vram_start = rdev->mc.vram_location; - rdev->mc.vram_end = rdev->mc.vram_location + rdev->mc.mc_vram_size; + rdev->mc.vram_end = rdev->mc.vram_location + rdev->mc.mc_vram_size - 1; rdev->mc.gtt_start = rdev->mc.gtt_location; - rdev->mc.gtt_end = rdev->mc.gtt_location + rdev->mc.gtt_size; + rdev->mc.gtt_end = rdev->mc.gtt_location + rdev->mc.gtt_size - 1; /* FIXME: we should enforce default clock in case GPU is not in * default setup */ @@ -456,6 +452,7 @@ int r600_mc_init(struct radeon_device *rdev) */ int r600_gpu_soft_reset(struct radeon_device *rdev) { + struct rv515_mc_save save; u32 grbm_busy_mask = S_008010_VC_BUSY(1) | S_008010_VGT_BUSY_NO_DMA(1) | S_008010_VGT_BUSY(1) | S_008010_TA03_BUSY(1) | S_008010_TC_BUSY(1) | S_008010_SX_BUSY(1) | @@ -473,13 +470,25 @@ int r600_gpu_soft_reset(struct radeon_device *rdev) S_008014_CB0_BUSY(1) | S_008014_CB1_BUSY(1) | S_008014_CB2_BUSY(1) | S_008014_CB3_BUSY(1); u32 srbm_reset = 0; + u32 tmp; + dev_info(rdev->dev, "GPU softreset \n"); + dev_info(rdev->dev, " R_008010_GRBM_STATUS=0x%08X\n", + RREG32(R_008010_GRBM_STATUS)); + dev_info(rdev->dev, " R_008014_GRBM_STATUS2=0x%08X\n", + RREG32(R_008014_GRBM_STATUS2)); + dev_info(rdev->dev, " R_000E50_SRBM_STATUS=0x%08X\n", + RREG32(R_000E50_SRBM_STATUS)); + rv515_mc_stop(rdev, &save); + if (r600_mc_wait_for_idle(rdev)) { + dev_warn(rdev->dev, "Wait for MC idle timedout !\n"); + } /* Disable CP parsing/prefetching */ WREG32(R_0086D8_CP_ME_CNTL, S_0086D8_CP_ME_HALT(0xff)); /* Check if any of the rendering block is busy and reset it */ if ((RREG32(R_008010_GRBM_STATUS) & grbm_busy_mask) || (RREG32(R_008014_GRBM_STATUS2) & grbm2_busy_mask)) { - WREG32(R_008020_GRBM_SOFT_RESET, S_008020_SOFT_RESET_CR(1) | + tmp = S_008020_SOFT_RESET_CR(1) | S_008020_SOFT_RESET_DB(1) | S_008020_SOFT_RESET_CB(1) | S_008020_SOFT_RESET_PA(1) | @@ -491,14 +500,18 @@ int r600_gpu_soft_reset(struct radeon_device *rdev) S_008020_SOFT_RESET_TC(1) | S_008020_SOFT_RESET_TA(1) | S_008020_SOFT_RESET_VC(1) | - S_008020_SOFT_RESET_VGT(1)); + S_008020_SOFT_RESET_VGT(1); + dev_info(rdev->dev, " R_008020_GRBM_SOFT_RESET=0x%08X\n", tmp); + WREG32(R_008020_GRBM_SOFT_RESET, tmp); (void)RREG32(R_008020_GRBM_SOFT_RESET); udelay(50); WREG32(R_008020_GRBM_SOFT_RESET, 0); (void)RREG32(R_008020_GRBM_SOFT_RESET); } /* Reset CP (we always reset CP) */ - WREG32(R_008020_GRBM_SOFT_RESET, S_008020_SOFT_RESET_CP(1)); + tmp = S_008020_SOFT_RESET_CP(1); + dev_info(rdev->dev, "R_008020_GRBM_SOFT_RESET=0x%08X\n", tmp); + WREG32(R_008020_GRBM_SOFT_RESET, tmp); (void)RREG32(R_008020_GRBM_SOFT_RESET); udelay(50); WREG32(R_008020_GRBM_SOFT_RESET, 0); @@ -526,6 +539,14 @@ int r600_gpu_soft_reset(struct radeon_device *rdev) srbm_reset |= S_000E60_SOFT_RESET_RLC(1); if (G_000E50_SEM_BUSY(RREG32(R_000E50_SRBM_STATUS))) srbm_reset |= S_000E60_SOFT_RESET_SEM(1); + if (G_000E50_BIF_BUSY(RREG32(R_000E50_SRBM_STATUS))) + srbm_reset |= S_000E60_SOFT_RESET_BIF(1); + dev_info(rdev->dev, " R_000E60_SRBM_SOFT_RESET=0x%08X\n", srbm_reset); + WREG32(R_000E60_SRBM_SOFT_RESET, srbm_reset); + (void)RREG32(R_000E60_SRBM_SOFT_RESET); + udelay(50); + WREG32(R_000E60_SRBM_SOFT_RESET, 0); + (void)RREG32(R_000E60_SRBM_SOFT_RESET); WREG32(R_000E60_SRBM_SOFT_RESET, srbm_reset); (void)RREG32(R_000E60_SRBM_SOFT_RESET); udelay(50); @@ -533,6 +554,17 @@ int r600_gpu_soft_reset(struct radeon_device *rdev) (void)RREG32(R_000E60_SRBM_SOFT_RESET); /* Wait a little for things to settle down */ udelay(50); + dev_info(rdev->dev, " R_008010_GRBM_STATUS=0x%08X\n", + RREG32(R_008010_GRBM_STATUS)); + dev_info(rdev->dev, " R_008014_GRBM_STATUS2=0x%08X\n", + RREG32(R_008014_GRBM_STATUS2)); + dev_info(rdev->dev, " R_000E50_SRBM_STATUS=0x%08X\n", + RREG32(R_000E50_SRBM_STATUS)); + /* After reset we need to reinit the asic as GPU often endup in an + * incoherent state. + */ + atom_asic_init(rdev->mode_info.atom_context); + rv515_mc_resume(rdev, &save); return 0; } @@ -826,7 +858,8 @@ void r600_gpu_init(struct radeon_device *rdev) ((rdev->family) == CHIP_RV630) || ((rdev->family) == CHIP_RV610) || ((rdev->family) == CHIP_RV620) || - ((rdev->family) == CHIP_RS780)) { + ((rdev->family) == CHIP_RS780) || + ((rdev->family) == CHIP_RS880)) { WREG32(DB_DEBUG, PREZ_MUST_WAIT_FOR_POSTZ_DONE); } else { WREG32(DB_DEBUG, 0); @@ -843,7 +876,8 @@ void r600_gpu_init(struct radeon_device *rdev) tmp = RREG32(SQ_MS_FIFO_SIZES); if (((rdev->family) == CHIP_RV610) || ((rdev->family) == CHIP_RV620) || - ((rdev->family) == CHIP_RS780)) { + ((rdev->family) == CHIP_RS780) || + ((rdev->family) == CHIP_RS880)) { tmp = (CACHE_FIFO_SIZE(0xa) | FETCH_FIFO_HIWATER(0xa) | DONE_FIFO_HIWATER(0xe0) | @@ -886,7 +920,8 @@ void r600_gpu_init(struct radeon_device *rdev) NUM_ES_STACK_ENTRIES(0)); } else if (((rdev->family) == CHIP_RV610) || ((rdev->family) == CHIP_RV620) || - ((rdev->family) == CHIP_RS780)) { + ((rdev->family) == CHIP_RS780) || + ((rdev->family) == CHIP_RS880)) { /* no vertex cache */ sq_config &= ~VC_ENABLE; @@ -943,7 +978,8 @@ void r600_gpu_init(struct radeon_device *rdev) if (((rdev->family) == CHIP_RV610) || ((rdev->family) == CHIP_RV620) || - ((rdev->family) == CHIP_RS780)) { + ((rdev->family) == CHIP_RS780) || + ((rdev->family) == CHIP_RS880)) { WREG32(VGT_CACHE_INVALIDATION, CACHE_INVALIDATION(TC_ONLY)); } else { WREG32(VGT_CACHE_INVALIDATION, CACHE_INVALIDATION(VC_AND_TC)); @@ -969,8 +1005,9 @@ void r600_gpu_init(struct radeon_device *rdev) tmp = rdev->config.r600.max_pipes * 16; switch (rdev->family) { case CHIP_RV610: - case CHIP_RS780: case CHIP_RV620: + case CHIP_RS780: + case CHIP_RS880: tmp += 32; break; case CHIP_RV670: @@ -1011,8 +1048,9 @@ void r600_gpu_init(struct radeon_device *rdev) switch (rdev->family) { case CHIP_RV610: - case CHIP_RS780: case CHIP_RV620: + case CHIP_RS780: + case CHIP_RS880: tmp = TC_L2_SIZE(8); break; case CHIP_RV630: @@ -1234,19 +1272,17 @@ int r600_cp_resume(struct radeon_device *rdev) /* Set ring buffer size */ rb_bufsz = drm_order(rdev->cp.ring_size / 8); + tmp = RB_NO_UPDATE | (drm_order(RADEON_GPU_PAGE_SIZE/8) << 8) | rb_bufsz; #ifdef __BIG_ENDIAN - WREG32(CP_RB_CNTL, BUF_SWAP_32BIT | RB_NO_UPDATE | - (drm_order(4096/8) << 8) | rb_bufsz); -#else - WREG32(CP_RB_CNTL, RB_NO_UPDATE | (drm_order(4096/8) << 8) | rb_bufsz); + tmp |= BUF_SWAP_32BIT; #endif + WREG32(CP_RB_CNTL, tmp); WREG32(CP_SEM_WAIT_TIMER, 0x4); /* Set the write pointer delay */ WREG32(CP_RB_WPTR_DELAY, 0); /* Initialize the ring buffer's read and write pointers */ - tmp = RREG32(CP_RB_CNTL); WREG32(CP_RB_CNTL, tmp | RB_RPTR_WR_ENA); WREG32(CP_RB_RPTR_WR, 0); WREG32(CP_RB_WPTR, 0); @@ -1343,32 +1379,47 @@ int r600_ring_test(struct radeon_device *rdev) return r; } -/* - * Writeback - */ -int r600_wb_init(struct radeon_device *rdev) +void r600_wb_disable(struct radeon_device *rdev) +{ + WREG32(SCRATCH_UMSK, 0); + if (rdev->wb.wb_obj) { + radeon_object_kunmap(rdev->wb.wb_obj); + radeon_object_unpin(rdev->wb.wb_obj); + } +} + +void r600_wb_fini(struct radeon_device *rdev) +{ + r600_wb_disable(rdev); + if (rdev->wb.wb_obj) { + radeon_object_unref(&rdev->wb.wb_obj); + rdev->wb.wb = NULL; + rdev->wb.wb_obj = NULL; + } +} + +int r600_wb_enable(struct radeon_device *rdev) { int r; if (rdev->wb.wb_obj == NULL) { - r = radeon_object_create(rdev, NULL, 4096, - true, - RADEON_GEM_DOMAIN_GTT, - false, &rdev->wb.wb_obj); + r = radeon_object_create(rdev, NULL, RADEON_GPU_PAGE_SIZE, true, + RADEON_GEM_DOMAIN_GTT, false, &rdev->wb.wb_obj); if (r) { - DRM_ERROR("radeon: failed to create WB buffer (%d).\n", r); + dev_warn(rdev->dev, "failed to create WB buffer (%d).\n", r); return r; } - r = radeon_object_pin(rdev->wb.wb_obj, - RADEON_GEM_DOMAIN_GTT, - &rdev->wb.gpu_addr); + r = radeon_object_pin(rdev->wb.wb_obj, RADEON_GEM_DOMAIN_GTT, + &rdev->wb.gpu_addr); if (r) { - DRM_ERROR("radeon: failed to pin WB buffer (%d).\n", r); + dev_warn(rdev->dev, "failed to pin WB buffer (%d).\n", r); + r600_wb_fini(rdev); return r; } r = radeon_object_kmap(rdev->wb.wb_obj, (void **)&rdev->wb.wb); if (r) { - DRM_ERROR("radeon: failed to map WB buffer (%d).\n", r); + dev_warn(rdev->dev, "failed to map WB buffer (%d).\n", r); + r600_wb_fini(rdev); return r; } } @@ -1379,21 +1430,6 @@ int r600_wb_init(struct radeon_device *rdev) return 0; } -void r600_wb_fini(struct radeon_device *rdev) -{ - if (rdev->wb.wb_obj) { - radeon_object_kunmap(rdev->wb.wb_obj); - radeon_object_unpin(rdev->wb.wb_obj); - radeon_object_unref(&rdev->wb.wb_obj); - rdev->wb.wb = NULL; - rdev->wb.wb_obj = NULL; - } -} - - -/* - * CS - */ void r600_fence_ring_emit(struct radeon_device *rdev, struct radeon_fence *fence) { @@ -1417,8 +1453,8 @@ int r600_copy_blit(struct radeon_device *rdev, uint64_t src_offset, uint64_t dst_offset, unsigned num_pages, struct radeon_fence *fence) { - r600_blit_prepare_copy(rdev, num_pages * 4096); - r600_kms_blit_copy(rdev, src_offset, dst_offset, num_pages * 4096); + r600_blit_prepare_copy(rdev, num_pages * RADEON_GPU_PAGE_SIZE); + r600_kms_blit_copy(rdev, src_offset, dst_offset, num_pages * RADEON_GPU_PAGE_SIZE); r600_blit_done_copy(rdev, fence); return 0; } @@ -1470,11 +1506,14 @@ int r600_startup(struct radeon_device *rdev) { int r; - r600_gpu_reset(rdev); - r600_mc_resume(rdev); - r = r600_pcie_gart_enable(rdev); - if (r) - return r; + r600_mc_program(rdev); + if (rdev->flags & RADEON_IS_AGP) { + r600_agp_enable(rdev); + } else { + r = r600_pcie_gart_enable(rdev); + if (r) + return r; + } r600_gpu_init(rdev); r = radeon_object_pin(rdev->r600_blit.shader_obj, RADEON_GEM_DOMAIN_VRAM, @@ -1493,9 +1532,8 @@ int r600_startup(struct radeon_device *rdev) r = r600_cp_resume(rdev); if (r) return r; - r = r600_wb_init(rdev); - if (r) - return r; + /* write back buffer are not vital so don't worry about failure */ + r600_wb_enable(rdev); return 0; } @@ -1517,15 +1555,12 @@ int r600_resume(struct radeon_device *rdev) { int r; - if (radeon_gpu_reset(rdev)) { - /* FIXME: what do we want to do here ? */ - } + /* Do not reset GPU before posting, on r600 hw unlike on r500 hw, + * posting will perform necessary task to bring back GPU into good + * shape. + */ /* post card */ - if (rdev->is_atom_bios) { - atom_asic_init(rdev->mode_info.atom_context); - } else { - radeon_combios_asic_init(rdev->ddev); - } + atom_asic_init(rdev->mode_info.atom_context); /* Initialize clocks */ r = radeon_clocks_init(rdev); if (r) { @@ -1538,7 +1573,7 @@ int r600_resume(struct radeon_device *rdev) return r; } - r = radeon_ib_test(rdev); + r = r600_ib_test(rdev); if (r) { DRM_ERROR("radeon: failled testing IB (%d).\n", r); return r; @@ -1546,13 +1581,12 @@ int r600_resume(struct radeon_device *rdev) return r; } - int r600_suspend(struct radeon_device *rdev) { /* FIXME: we should wait for ring to be empty */ r600_cp_stop(rdev); rdev->cp.ready = false; - + r600_wb_disable(rdev); r600_pcie_gart_disable(rdev); /* unpin shaders bo */ radeon_object_unpin(rdev->r600_blit.shader_obj); @@ -1569,7 +1603,6 @@ int r600_init(struct radeon_device *rdev) { int r; - rdev->new_init_path = true; r = radeon_dummy_page_init(rdev); if (r) return r; @@ -1586,8 +1619,10 @@ int r600_init(struct radeon_device *rdev) return -EINVAL; } /* Must be an ATOMBIOS */ - if (!rdev->is_atom_bios) + if (!rdev->is_atom_bios) { + dev_err(rdev->dev, "Expecting atombios for R600 GPU\n"); return -EINVAL; + } r = radeon_atombios_init(rdev); if (r) return r; @@ -1600,24 +1635,20 @@ int r600_init(struct radeon_device *rdev) r600_scratch_init(rdev); /* Initialize surface registers */ radeon_surface_init(rdev); + /* Initialize clocks */ radeon_get_clock_info(rdev->ddev); r = radeon_clocks_init(rdev); if (r) return r; + /* Initialize power management */ + radeon_pm_init(rdev); /* Fence driver */ r = radeon_fence_driver_init(rdev); if (r) return r; r = r600_mc_init(rdev); - if (r) { - if (rdev->flags & RADEON_IS_AGP) { - /* Retry with disabling AGP */ - r600_fini(rdev); - rdev->flags &= ~RADEON_IS_AGP; - return r600_init(rdev); - } + if (r) return r; - } /* Memory manager */ r = radeon_object_init(rdev); if (r) @@ -1646,12 +1677,10 @@ int r600_init(struct radeon_device *rdev) r = r600_startup(rdev); if (r) { - if (rdev->flags & RADEON_IS_AGP) { - /* Retry with disabling AGP */ - r600_fini(rdev); - rdev->flags &= ~RADEON_IS_AGP; - return r600_init(rdev); - } + r600_suspend(rdev); + r600_wb_fini(rdev); + radeon_ring_fini(rdev); + r600_pcie_gart_fini(rdev); rdev->accel_working = false; } if (rdev->accel_working) { @@ -1660,7 +1689,7 @@ int r600_init(struct radeon_device *rdev) DRM_ERROR("radeon: failled initializing IB pool (%d).\n", r); rdev->accel_working = false; } - r = radeon_ib_test(rdev); + r = r600_ib_test(rdev); if (r) { DRM_ERROR("radeon: failled testing IB (%d).\n", r); rdev->accel_working = false; @@ -1676,19 +1705,15 @@ void r600_fini(struct radeon_device *rdev) r600_blit_fini(rdev); radeon_ring_fini(rdev); + r600_wb_fini(rdev); r600_pcie_gart_fini(rdev); radeon_gem_fini(rdev); radeon_fence_driver_fini(rdev); radeon_clocks_fini(rdev); -#if __OS_HAS_AGP if (rdev->flags & RADEON_IS_AGP) radeon_agp_fini(rdev); -#endif radeon_object_fini(rdev); - if (rdev->is_atom_bios) - radeon_atombios_fini(rdev); - else - radeon_combios_fini(rdev); + radeon_atombios_fini(rdev); kfree(rdev->bios); rdev->bios = NULL; radeon_dummy_page_fini(rdev); diff --git a/drivers/gpu/drm/radeon/r600_blit.c b/drivers/gpu/drm/radeon/r600_blit.c index dde2ccbf1d15..5ea432347589 100644 --- a/drivers/gpu/drm/radeon/r600_blit.c +++ b/drivers/gpu/drm/radeon/r600_blit.c @@ -737,7 +737,7 @@ r600_blit_copy(struct drm_device *dev, /* dst */ set_render_target(dev_priv, COLOR_8_8_8_8, - dst_x + cur_size, h, + (dst_x + cur_size) / 4, h, dst_gpu_addr); /* scissors */ @@ -774,11 +774,10 @@ r600_blit_swap(struct drm_device *dev, { drm_radeon_private_t *dev_priv = dev->dev_private; int cb_format, tex_format; + int sx2, sy2, dx2, dy2; u64 vb_addr; u32 *vb; - vb = r600_nomm_get_vb_ptr(dev); - if ((dev_priv->blit_vb->used + 48) > dev_priv->blit_vb->total) { r600_nomm_put_vb(dev); @@ -787,19 +786,13 @@ r600_blit_swap(struct drm_device *dev, return; set_shaders(dev); - vb = r600_nomm_get_vb_ptr(dev); } + vb = r600_nomm_get_vb_ptr(dev); - if (cpp == 4) { - cb_format = COLOR_8_8_8_8; - tex_format = FMT_8_8_8_8; - } else if (cpp == 2) { - cb_format = COLOR_5_6_5; - tex_format = FMT_5_6_5; - } else { - cb_format = COLOR_8; - tex_format = FMT_8; - } + sx2 = sx + w; + sy2 = sy + h; + dx2 = dx + w; + dy2 = dy + h; vb[0] = i2f(dx); vb[1] = i2f(dy); @@ -807,31 +800,46 @@ r600_blit_swap(struct drm_device *dev, vb[3] = i2f(sy); vb[4] = i2f(dx); - vb[5] = i2f(dy + h); + vb[5] = i2f(dy2); vb[6] = i2f(sx); - vb[7] = i2f(sy + h); + vb[7] = i2f(sy2); - vb[8] = i2f(dx + w); - vb[9] = i2f(dy + h); - vb[10] = i2f(sx + w); - vb[11] = i2f(sy + h); + vb[8] = i2f(dx2); + vb[9] = i2f(dy2); + vb[10] = i2f(sx2); + vb[11] = i2f(sy2); + + switch(cpp) { + case 4: + cb_format = COLOR_8_8_8_8; + tex_format = FMT_8_8_8_8; + break; + case 2: + cb_format = COLOR_5_6_5; + tex_format = FMT_5_6_5; + break; + default: + cb_format = COLOR_8; + tex_format = FMT_8; + break; + } /* src */ set_tex_resource(dev_priv, tex_format, src_pitch / cpp, - sy + h, src_pitch / cpp, + sy2, src_pitch / cpp, src_gpu_addr); cp_set_surface_sync(dev_priv, - R600_TC_ACTION_ENA, (src_pitch * (sy + h)), src_gpu_addr); + R600_TC_ACTION_ENA, src_pitch * sy2, src_gpu_addr); /* dst */ set_render_target(dev_priv, cb_format, - dst_pitch / cpp, dy + h, + dst_pitch / cpp, dy2, dst_gpu_addr); /* scissors */ - set_scissors(dev_priv, dx, dy, dx + w, dy + h); + set_scissors(dev_priv, dx, dy, dx2, dy2); /* Vertex buffer setup */ vb_addr = dev_priv->gart_buffers_offset + @@ -844,7 +852,7 @@ r600_blit_swap(struct drm_device *dev, cp_set_surface_sync(dev_priv, R600_CB_ACTION_ENA | R600_CB0_DEST_BASE_ENA, - dst_pitch * (dy + h), dst_gpu_addr); + dst_pitch * dy2, dst_gpu_addr); dev_priv->blit_vb->used += 12 * 4; } diff --git a/drivers/gpu/drm/radeon/r600_blit_kms.c b/drivers/gpu/drm/radeon/r600_blit_kms.c index 0a6f4681f468..dbf716e1fbf3 100644 --- a/drivers/gpu/drm/radeon/r600_blit_kms.c +++ b/drivers/gpu/drm/radeon/r600_blit_kms.c @@ -368,7 +368,7 @@ set_default_state(struct radeon_device *rdev) if ((rdev->family == CHIP_RV610) || (rdev->family == CHIP_RV620) || (rdev->family == CHIP_RS780) || - (rdev->family == CHIP_RS780) || + (rdev->family == CHIP_RS880) || (rdev->family == CHIP_RV710)) sq_config = 0; else @@ -774,7 +774,7 @@ void r600_kms_blit_copy(struct radeon_device *rdev, /* dst 23 */ set_render_target(rdev, COLOR_8_8_8_8, - dst_x + cur_size, h, + (dst_x + cur_size) / 4, h, dst_gpu_addr); /* scissors 12 */ diff --git a/drivers/gpu/drm/radeon/r600_cs.c b/drivers/gpu/drm/radeon/r600_cs.c index 33b89cd8743e..0d820764f340 100644 --- a/drivers/gpu/drm/radeon/r600_cs.c +++ b/drivers/gpu/drm/radeon/r600_cs.c @@ -28,7 +28,6 @@ #include "drmP.h" #include "radeon.h" #include "r600d.h" -#include "avivod.h" static int r600_cs_packet_next_reloc_mm(struct radeon_cs_parser *p, struct radeon_cs_reloc **cs_reloc); @@ -57,7 +56,7 @@ int r600_cs_packet_parse(struct radeon_cs_parser *p, idx, ib_chunk->length_dw); return -EINVAL; } - header = ib_chunk->kdata[idx]; + header = radeon_get_ib_value(p, idx); pkt->idx = idx; pkt->type = CP_PACKET_GET_TYPE(header); pkt->count = CP_PACKET_GET_COUNT(header); @@ -98,7 +97,6 @@ int r600_cs_packet_parse(struct radeon_cs_parser *p, static int r600_cs_packet_next_reloc_mm(struct radeon_cs_parser *p, struct radeon_cs_reloc **cs_reloc) { - struct radeon_cs_chunk *ib_chunk; struct radeon_cs_chunk *relocs_chunk; struct radeon_cs_packet p3reloc; unsigned idx; @@ -109,7 +107,6 @@ static int r600_cs_packet_next_reloc_mm(struct radeon_cs_parser *p, return -EINVAL; } *cs_reloc = NULL; - ib_chunk = &p->chunks[p->chunk_ib_idx]; relocs_chunk = &p->chunks[p->chunk_relocs_idx]; r = r600_cs_packet_parse(p, &p3reloc, p->idx); if (r) { @@ -121,7 +118,7 @@ static int r600_cs_packet_next_reloc_mm(struct radeon_cs_parser *p, p3reloc.idx); return -EINVAL; } - idx = ib_chunk->kdata[p3reloc.idx + 1]; + idx = radeon_get_ib_value(p, p3reloc.idx + 1); if (idx >= relocs_chunk->length_dw) { DRM_ERROR("Relocs at %d after relocations chunk end %d !\n", idx, relocs_chunk->length_dw); @@ -146,7 +143,6 @@ static int r600_cs_packet_next_reloc_mm(struct radeon_cs_parser *p, static int r600_cs_packet_next_reloc_nomm(struct radeon_cs_parser *p, struct radeon_cs_reloc **cs_reloc) { - struct radeon_cs_chunk *ib_chunk; struct radeon_cs_chunk *relocs_chunk; struct radeon_cs_packet p3reloc; unsigned idx; @@ -157,7 +153,6 @@ static int r600_cs_packet_next_reloc_nomm(struct radeon_cs_parser *p, return -EINVAL; } *cs_reloc = NULL; - ib_chunk = &p->chunks[p->chunk_ib_idx]; relocs_chunk = &p->chunks[p->chunk_relocs_idx]; r = r600_cs_packet_parse(p, &p3reloc, p->idx); if (r) { @@ -169,7 +164,7 @@ static int r600_cs_packet_next_reloc_nomm(struct radeon_cs_parser *p, p3reloc.idx); return -EINVAL; } - idx = ib_chunk->kdata[p3reloc.idx + 1]; + idx = radeon_get_ib_value(p, p3reloc.idx + 1); if (idx >= relocs_chunk->length_dw) { DRM_ERROR("Relocs at %d after relocations chunk end %d !\n", idx, relocs_chunk->length_dw); @@ -181,13 +176,136 @@ static int r600_cs_packet_next_reloc_nomm(struct radeon_cs_parser *p, return 0; } +/** + * r600_cs_packet_next_vline() - parse userspace VLINE packet + * @parser: parser structure holding parsing context. + * + * Userspace sends a special sequence for VLINE waits. + * PACKET0 - VLINE_START_END + value + * PACKET3 - WAIT_REG_MEM poll vline status reg + * RELOC (P3) - crtc_id in reloc. + * + * This function parses this and relocates the VLINE START END + * and WAIT_REG_MEM packets to the correct crtc. + * It also detects a switched off crtc and nulls out the + * wait in that case. + */ +static int r600_cs_packet_parse_vline(struct radeon_cs_parser *p) +{ + struct drm_mode_object *obj; + struct drm_crtc *crtc; + struct radeon_crtc *radeon_crtc; + struct radeon_cs_packet p3reloc, wait_reg_mem; + int crtc_id; + int r; + uint32_t header, h_idx, reg, wait_reg_mem_info; + volatile uint32_t *ib; + + ib = p->ib->ptr; + + /* parse the WAIT_REG_MEM */ + r = r600_cs_packet_parse(p, &wait_reg_mem, p->idx); + if (r) + return r; + + /* check its a WAIT_REG_MEM */ + if (wait_reg_mem.type != PACKET_TYPE3 || + wait_reg_mem.opcode != PACKET3_WAIT_REG_MEM) { + DRM_ERROR("vline wait missing WAIT_REG_MEM segment\n"); + r = -EINVAL; + return r; + } + + wait_reg_mem_info = radeon_get_ib_value(p, wait_reg_mem.idx + 1); + /* bit 4 is reg (0) or mem (1) */ + if (wait_reg_mem_info & 0x10) { + DRM_ERROR("vline WAIT_REG_MEM waiting on MEM rather than REG\n"); + r = -EINVAL; + return r; + } + /* waiting for value to be equal */ + if ((wait_reg_mem_info & 0x7) != 0x3) { + DRM_ERROR("vline WAIT_REG_MEM function not equal\n"); + r = -EINVAL; + return r; + } + if ((radeon_get_ib_value(p, wait_reg_mem.idx + 2) << 2) != AVIVO_D1MODE_VLINE_STATUS) { + DRM_ERROR("vline WAIT_REG_MEM bad reg\n"); + r = -EINVAL; + return r; + } + + if (radeon_get_ib_value(p, wait_reg_mem.idx + 5) != AVIVO_D1MODE_VLINE_STAT) { + DRM_ERROR("vline WAIT_REG_MEM bad bit mask\n"); + r = -EINVAL; + return r; + } + + /* jump over the NOP */ + r = r600_cs_packet_parse(p, &p3reloc, p->idx + wait_reg_mem.count + 2); + if (r) + return r; + + h_idx = p->idx - 2; + p->idx += wait_reg_mem.count + 2; + p->idx += p3reloc.count + 2; + + header = radeon_get_ib_value(p, h_idx); + crtc_id = radeon_get_ib_value(p, h_idx + 2 + 7 + 1); + reg = CP_PACKET0_GET_REG(header); + mutex_lock(&p->rdev->ddev->mode_config.mutex); + obj = drm_mode_object_find(p->rdev->ddev, crtc_id, DRM_MODE_OBJECT_CRTC); + if (!obj) { + DRM_ERROR("cannot find crtc %d\n", crtc_id); + r = -EINVAL; + goto out; + } + crtc = obj_to_crtc(obj); + radeon_crtc = to_radeon_crtc(crtc); + crtc_id = radeon_crtc->crtc_id; + + if (!crtc->enabled) { + /* if the CRTC isn't enabled - we need to nop out the WAIT_REG_MEM */ + ib[h_idx + 2] = PACKET2(0); + ib[h_idx + 3] = PACKET2(0); + ib[h_idx + 4] = PACKET2(0); + ib[h_idx + 5] = PACKET2(0); + ib[h_idx + 6] = PACKET2(0); + ib[h_idx + 7] = PACKET2(0); + ib[h_idx + 8] = PACKET2(0); + } else if (crtc_id == 1) { + switch (reg) { + case AVIVO_D1MODE_VLINE_START_END: + header &= ~R600_CP_PACKET0_REG_MASK; + header |= AVIVO_D2MODE_VLINE_START_END >> 2; + break; + default: + DRM_ERROR("unknown crtc reloc\n"); + r = -EINVAL; + goto out; + } + ib[h_idx] = header; + ib[h_idx + 4] = AVIVO_D2MODE_VLINE_STATUS >> 2; + } +out: + mutex_unlock(&p->rdev->ddev->mode_config.mutex); + return r; +} + static int r600_packet0_check(struct radeon_cs_parser *p, struct radeon_cs_packet *pkt, unsigned idx, unsigned reg) { + int r; + switch (reg) { case AVIVO_D1MODE_VLINE_START_END: - case AVIVO_D2MODE_VLINE_START_END: + r = r600_cs_packet_parse_vline(p); + if (r) { + DRM_ERROR("No reloc for ib[%d]=0x%04X\n", + idx, reg); + return r; + } break; default: printk(KERN_ERR "Forbidden register 0x%04X in cs at %d\n", @@ -218,17 +336,18 @@ static int r600_cs_parse_packet0(struct radeon_cs_parser *p, static int r600_packet3_check(struct radeon_cs_parser *p, struct radeon_cs_packet *pkt) { - struct radeon_cs_chunk *ib_chunk; struct radeon_cs_reloc *reloc; volatile u32 *ib; unsigned idx; unsigned i; unsigned start_reg, end_reg, reg; int r; + u32 idx_value; ib = p->ib->ptr; - ib_chunk = &p->chunks[p->chunk_ib_idx]; idx = pkt->idx + 1; + idx_value = radeon_get_ib_value(p, idx); + switch (pkt->opcode) { case PACKET3_START_3D_CMDBUF: if (p->family >= CHIP_RV770 || pkt->count) { @@ -259,8 +378,8 @@ static int r600_packet3_check(struct radeon_cs_parser *p, DRM_ERROR("bad DRAW_INDEX\n"); return -EINVAL; } - ib[idx+0] += (u32)(reloc->lobj.gpu_offset & 0xffffffff); - ib[idx+1] = upper_32_bits(reloc->lobj.gpu_offset) & 0xff; + ib[idx+0] = idx_value + (u32)(reloc->lobj.gpu_offset & 0xffffffff); + ib[idx+1] += upper_32_bits(reloc->lobj.gpu_offset) & 0xff; break; case PACKET3_DRAW_INDEX_AUTO: if (pkt->count != 1) { @@ -281,14 +400,14 @@ static int r600_packet3_check(struct radeon_cs_parser *p, return -EINVAL; } /* bit 4 is reg (0) or mem (1) */ - if (ib_chunk->kdata[idx+0] & 0x10) { + if (idx_value & 0x10) { r = r600_cs_packet_next_reloc(p, &reloc); if (r) { DRM_ERROR("bad WAIT_REG_MEM\n"); return -EINVAL; } ib[idx+1] += (u32)(reloc->lobj.gpu_offset & 0xffffffff); - ib[idx+2] = upper_32_bits(reloc->lobj.gpu_offset) & 0xff; + ib[idx+2] += upper_32_bits(reloc->lobj.gpu_offset) & 0xff; } break; case PACKET3_SURFACE_SYNC: @@ -297,8 +416,8 @@ static int r600_packet3_check(struct radeon_cs_parser *p, return -EINVAL; } /* 0xffffffff/0x0 is flush all cache flag */ - if (ib_chunk->kdata[idx+1] != 0xffffffff || - ib_chunk->kdata[idx+2] != 0) { + if (radeon_get_ib_value(p, idx + 1) != 0xffffffff || + radeon_get_ib_value(p, idx + 2) != 0) { r = r600_cs_packet_next_reloc(p, &reloc); if (r) { DRM_ERROR("bad SURFACE_SYNC\n"); @@ -319,7 +438,7 @@ static int r600_packet3_check(struct radeon_cs_parser *p, return -EINVAL; } ib[idx+1] += (u32)(reloc->lobj.gpu_offset & 0xffffffff); - ib[idx+2] |= upper_32_bits(reloc->lobj.gpu_offset) & 0xff; + ib[idx+2] += upper_32_bits(reloc->lobj.gpu_offset) & 0xff; } break; case PACKET3_EVENT_WRITE_EOP: @@ -333,10 +452,10 @@ static int r600_packet3_check(struct radeon_cs_parser *p, return -EINVAL; } ib[idx+1] += (u32)(reloc->lobj.gpu_offset & 0xffffffff); - ib[idx+2] |= upper_32_bits(reloc->lobj.gpu_offset) & 0xff; + ib[idx+2] += upper_32_bits(reloc->lobj.gpu_offset) & 0xff; break; case PACKET3_SET_CONFIG_REG: - start_reg = (ib[idx+0] << 2) + PACKET3_SET_CONFIG_REG_OFFSET; + start_reg = (idx_value << 2) + PACKET3_SET_CONFIG_REG_OFFSET; end_reg = 4 * pkt->count + start_reg - 4; if ((start_reg < PACKET3_SET_CONFIG_REG_OFFSET) || (start_reg >= PACKET3_SET_CONFIG_REG_END) || @@ -347,6 +466,23 @@ static int r600_packet3_check(struct radeon_cs_parser *p, for (i = 0; i < pkt->count; i++) { reg = start_reg + (4 * i); switch (reg) { + case SQ_ESGS_RING_BASE: + case SQ_GSVS_RING_BASE: + case SQ_ESTMP_RING_BASE: + case SQ_GSTMP_RING_BASE: + case SQ_VSTMP_RING_BASE: + case SQ_PSTMP_RING_BASE: + case SQ_FBUF_RING_BASE: + case SQ_REDUC_RING_BASE: + case SX_MEMORY_EXPORT_BASE: + r = r600_cs_packet_next_reloc(p, &reloc); + if (r) { + DRM_ERROR("bad SET_CONFIG_REG " + "0x%04X\n", reg); + return -EINVAL; + } + ib[idx+1+i] += (u32)((reloc->lobj.gpu_offset >> 8) & 0xffffffff); + break; case CP_COHER_BASE: /* use PACKET3_SURFACE_SYNC */ return -EINVAL; @@ -356,7 +492,7 @@ static int r600_packet3_check(struct radeon_cs_parser *p, } break; case PACKET3_SET_CONTEXT_REG: - start_reg = (ib[idx+0] << 2) + PACKET3_SET_CONTEXT_REG_OFFSET; + start_reg = (idx_value << 2) + PACKET3_SET_CONTEXT_REG_OFFSET; end_reg = 4 * pkt->count + start_reg - 4; if ((start_reg < PACKET3_SET_CONTEXT_REG_OFFSET) || (start_reg >= PACKET3_SET_CONTEXT_REG_END) || @@ -368,6 +504,7 @@ static int r600_packet3_check(struct radeon_cs_parser *p, reg = start_reg + (4 * i); switch (reg) { case DB_DEPTH_BASE: + case DB_HTILE_DATA_BASE: case CB_COLOR0_BASE: case CB_COLOR1_BASE: case CB_COLOR2_BASE: @@ -421,7 +558,7 @@ static int r600_packet3_check(struct radeon_cs_parser *p, DRM_ERROR("bad SET_RESOURCE\n"); return -EINVAL; } - start_reg = (ib[idx+0] << 2) + PACKET3_SET_RESOURCE_OFFSET; + start_reg = (idx_value << 2) + PACKET3_SET_RESOURCE_OFFSET; end_reg = 4 * pkt->count + start_reg - 4; if ((start_reg < PACKET3_SET_RESOURCE_OFFSET) || (start_reg >= PACKET3_SET_RESOURCE_END) || @@ -430,7 +567,7 @@ static int r600_packet3_check(struct radeon_cs_parser *p, return -EINVAL; } for (i = 0; i < (pkt->count / 7); i++) { - switch (G__SQ_VTX_CONSTANT_TYPE(ib[idx+(i*7)+6+1])) { + switch (G__SQ_VTX_CONSTANT_TYPE(radeon_get_ib_value(p, idx+(i*7)+6+1))) { case SQ_TEX_VTX_VALID_TEXTURE: /* tex base */ r = r600_cs_packet_next_reloc(p, &reloc); @@ -455,7 +592,7 @@ static int r600_packet3_check(struct radeon_cs_parser *p, return -EINVAL; } ib[idx+1+(i*7)+0] += (u32)((reloc->lobj.gpu_offset) & 0xffffffff); - ib[idx+1+(i*7)+2] |= upper_32_bits(reloc->lobj.gpu_offset) & 0xff; + ib[idx+1+(i*7)+2] += upper_32_bits(reloc->lobj.gpu_offset) & 0xff; break; case SQ_TEX_VTX_INVALID_TEXTURE: case SQ_TEX_VTX_INVALID_BUFFER: @@ -466,7 +603,7 @@ static int r600_packet3_check(struct radeon_cs_parser *p, } break; case PACKET3_SET_ALU_CONST: - start_reg = (ib[idx+0] << 2) + PACKET3_SET_ALU_CONST_OFFSET; + start_reg = (idx_value << 2) + PACKET3_SET_ALU_CONST_OFFSET; end_reg = 4 * pkt->count + start_reg - 4; if ((start_reg < PACKET3_SET_ALU_CONST_OFFSET) || (start_reg >= PACKET3_SET_ALU_CONST_END) || @@ -476,7 +613,7 @@ static int r600_packet3_check(struct radeon_cs_parser *p, } break; case PACKET3_SET_BOOL_CONST: - start_reg = (ib[idx+0] << 2) + PACKET3_SET_BOOL_CONST_OFFSET; + start_reg = (idx_value << 2) + PACKET3_SET_BOOL_CONST_OFFSET; end_reg = 4 * pkt->count + start_reg - 4; if ((start_reg < PACKET3_SET_BOOL_CONST_OFFSET) || (start_reg >= PACKET3_SET_BOOL_CONST_END) || @@ -486,7 +623,7 @@ static int r600_packet3_check(struct radeon_cs_parser *p, } break; case PACKET3_SET_LOOP_CONST: - start_reg = (ib[idx+0] << 2) + PACKET3_SET_LOOP_CONST_OFFSET; + start_reg = (idx_value << 2) + PACKET3_SET_LOOP_CONST_OFFSET; end_reg = 4 * pkt->count + start_reg - 4; if ((start_reg < PACKET3_SET_LOOP_CONST_OFFSET) || (start_reg >= PACKET3_SET_LOOP_CONST_END) || @@ -496,7 +633,7 @@ static int r600_packet3_check(struct radeon_cs_parser *p, } break; case PACKET3_SET_CTL_CONST: - start_reg = (ib[idx+0] << 2) + PACKET3_SET_CTL_CONST_OFFSET; + start_reg = (idx_value << 2) + PACKET3_SET_CTL_CONST_OFFSET; end_reg = 4 * pkt->count + start_reg - 4; if ((start_reg < PACKET3_SET_CTL_CONST_OFFSET) || (start_reg >= PACKET3_SET_CTL_CONST_END) || @@ -510,7 +647,7 @@ static int r600_packet3_check(struct radeon_cs_parser *p, DRM_ERROR("bad SET_SAMPLER\n"); return -EINVAL; } - start_reg = (ib[idx+0] << 2) + PACKET3_SET_SAMPLER_OFFSET; + start_reg = (idx_value << 2) + PACKET3_SET_SAMPLER_OFFSET; end_reg = 4 * pkt->count + start_reg - 4; if ((start_reg < PACKET3_SET_SAMPLER_OFFSET) || (start_reg >= PACKET3_SET_SAMPLER_END) || @@ -602,6 +739,8 @@ static void r600_cs_parser_fini(struct radeon_cs_parser *parser, int error) kfree(parser->relocs); for (i = 0; i < parser->nchunks; i++) { kfree(parser->chunks[i].kdata); + kfree(parser->chunks[i].kpage[0]); + kfree(parser->chunks[i].kpage[1]); } kfree(parser->chunks); kfree(parser->chunks_array); @@ -639,7 +778,6 @@ int r600_cs_legacy(struct drm_device *dev, void *data, struct drm_file *filp, * uncached). */ ib_chunk = &parser.chunks[parser.chunk_ib_idx]; parser.ib->length_dw = ib_chunk->length_dw; - memcpy((void *)parser.ib->ptr, ib_chunk->kdata, ib_chunk->length_dw*4); *l = parser.ib->length_dw; r = r600_cs_parse(&parser); if (r) { @@ -647,6 +785,12 @@ int r600_cs_legacy(struct drm_device *dev, void *data, struct drm_file *filp, r600_cs_parser_fini(&parser, r); return r; } + r = radeon_cs_finish_pages(&parser); + if (r) { + DRM_ERROR("Invalid command stream !\n"); + r600_cs_parser_fini(&parser, r); + return r; + } r600_cs_parser_fini(&parser, r); return r; } diff --git a/drivers/gpu/drm/radeon/r600d.h b/drivers/gpu/drm/radeon/r600d.h index 4a9028a85c9b..27ab428b149b 100644 --- a/drivers/gpu/drm/radeon/r600d.h +++ b/drivers/gpu/drm/radeon/r600d.h @@ -119,6 +119,7 @@ #define DB_DEBUG 0x9830 #define PREZ_MUST_WAIT_FOR_POSTZ_DONE (1 << 31) #define DB_DEPTH_BASE 0x2800C +#define DB_HTILE_DATA_BASE 0x28014 #define DB_WATERMARKS 0x9838 #define DEPTH_FREE(x) ((x) << 0) #define DEPTH_FLUSH(x) ((x) << 5) @@ -171,6 +172,14 @@ #define SQ_STACK_RESOURCE_MGMT_2 0x8c14 # define NUM_GS_STACK_ENTRIES(x) ((x) << 0) # define NUM_ES_STACK_ENTRIES(x) ((x) << 16) +#define SQ_ESGS_RING_BASE 0x8c40 +#define SQ_GSVS_RING_BASE 0x8c48 +#define SQ_ESTMP_RING_BASE 0x8c50 +#define SQ_GSTMP_RING_BASE 0x8c58 +#define SQ_VSTMP_RING_BASE 0x8c60 +#define SQ_PSTMP_RING_BASE 0x8c68 +#define SQ_FBUF_RING_BASE 0x8c70 +#define SQ_REDUC_RING_BASE 0x8c78 #define GRBM_CNTL 0x8000 # define GRBM_READ_TIMEOUT(x) ((x) << 0) @@ -271,6 +280,10 @@ #define PCIE_PORT_INDEX 0x0038 #define PCIE_PORT_DATA 0x003C +#define CHMAP 0x2004 +#define NOOFCHAN_SHIFT 12 +#define NOOFCHAN_MASK 0x00003000 + #define RAMCFG 0x2408 #define NOOFBANK_SHIFT 0 #define NOOFBANK_MASK 0x00000001 @@ -352,6 +365,7 @@ #define SX_MISC 0x28350 +#define SX_MEMORY_EXPORT_BASE 0x9010 #define SX_DEBUG_1 0x9054 #define SMX_EVENT_RELEASE (1 << 0) #define ENABLE_NEW_SMX_ADDRESS (1 << 16) @@ -643,6 +657,7 @@ #define G_000E50_MCDW_BUSY(x) (((x) >> 13) & 1) #define G_000E50_SEM_BUSY(x) (((x) >> 14) & 1) #define G_000E50_RLC_BUSY(x) (((x) >> 15) & 1) +#define G_000E50_BIF_BUSY(x) (((x) >> 29) & 1) #define R_000E60_SRBM_SOFT_RESET 0x0E60 #define S_000E60_SOFT_RESET_BIF(x) (((x) & 1) << 1) #define S_000E60_SOFT_RESET_CG(x) (((x) & 1) << 2) diff --git a/drivers/gpu/drm/radeon/radeon.h b/drivers/gpu/drm/radeon/radeon.h index c839b608970f..224506a2f7b1 100644 --- a/drivers/gpu/drm/radeon/radeon.h +++ b/drivers/gpu/drm/radeon/radeon.h @@ -44,11 +44,30 @@ * - TESTING, TESTING, TESTING */ +/* Initialization path: + * We expect that acceleration initialization might fail for various + * reasons even thought we work hard to make it works on most + * configurations. In order to still have a working userspace in such + * situation the init path must succeed up to the memory controller + * initialization point. Failure before this point are considered as + * fatal error. Here is the init callchain : + * radeon_device_init perform common structure, mutex initialization + * asic_init setup the GPU memory layout and perform all + * one time initialization (failure in this + * function are considered fatal) + * asic_startup setup the GPU acceleration, in order to + * follow guideline the first thing this + * function should do is setting the GPU + * memory controller (only MC setup failure + * are considered as fatal) + */ + #include <asm/atomic.h> #include <linux/wait.h> #include <linux/list.h> #include <linux/kref.h> +#include "radeon_family.h" #include "radeon_mode.h" #include "radeon_reg.h" @@ -77,64 +96,6 @@ extern int radeon_tv; #define RADEONFB_CONN_LIMIT 4 #define RADEON_BIOS_NUM_SCRATCH 8 -enum radeon_family { - CHIP_R100, - CHIP_RV100, - CHIP_RS100, - CHIP_RV200, - CHIP_RS200, - CHIP_R200, - CHIP_RV250, - CHIP_RS300, - CHIP_RV280, - CHIP_R300, - CHIP_R350, - CHIP_RV350, - CHIP_RV380, - CHIP_R420, - CHIP_R423, - CHIP_RV410, - CHIP_RS400, - CHIP_RS480, - CHIP_RS600, - CHIP_RS690, - CHIP_RS740, - CHIP_RV515, - CHIP_R520, - CHIP_RV530, - CHIP_RV560, - CHIP_RV570, - CHIP_R580, - CHIP_R600, - CHIP_RV610, - CHIP_RV630, - CHIP_RV670, - CHIP_RV620, - CHIP_RV635, - CHIP_RS780, - CHIP_RS880, - CHIP_RV770, - CHIP_RV730, - CHIP_RV710, - CHIP_RV740, - CHIP_LAST, -}; - -enum radeon_chip_flags { - RADEON_FAMILY_MASK = 0x0000ffffUL, - RADEON_FLAGS_MASK = 0xffff0000UL, - RADEON_IS_MOBILITY = 0x00010000UL, - RADEON_IS_IGP = 0x00020000UL, - RADEON_SINGLE_CRTC = 0x00040000UL, - RADEON_IS_AGP = 0x00080000UL, - RADEON_HAS_HIERZ = 0x00100000UL, - RADEON_IS_PCIE = 0x00200000UL, - RADEON_NEW_MEMMAP = 0x00400000UL, - RADEON_IS_PCI = 0x00800000UL, - RADEON_IS_IGPGART = 0x01000000UL, -}; - - /* * Errata workarounds. */ @@ -178,6 +139,10 @@ struct radeon_clock { uint32_t default_sclk; }; +/* + * Power management + */ +int radeon_pm_init(struct radeon_device *rdev); /* * Fences. @@ -315,6 +280,8 @@ union radeon_gart_table { struct radeon_gart_table_vram vram; }; +#define RADEON_GPU_PAGE_SIZE 4096 + struct radeon_gart { dma_addr_t table_addr; unsigned num_gpu_pages; @@ -399,7 +366,7 @@ struct radeon_ib { unsigned long idx; uint64_t gpu_addr; struct radeon_fence *fence; - volatile uint32_t *ptr; + uint32_t *ptr; uint32_t length_dw; }; @@ -472,7 +439,12 @@ struct radeon_cs_reloc { struct radeon_cs_chunk { uint32_t chunk_id; uint32_t length_dw; + int kpage_idx[2]; + uint32_t *kpage[2]; uint32_t *kdata; + void __user *user_ptr; + int last_copied_page; + int last_page_index; }; struct radeon_cs_parser { @@ -495,8 +467,38 @@ struct radeon_cs_parser { struct radeon_ib *ib; void *track; unsigned family; + int parser_error; }; +extern int radeon_cs_update_pages(struct radeon_cs_parser *p, int pg_idx); +extern int radeon_cs_finish_pages(struct radeon_cs_parser *p); + + +static inline u32 radeon_get_ib_value(struct radeon_cs_parser *p, int idx) +{ + struct radeon_cs_chunk *ibc = &p->chunks[p->chunk_ib_idx]; + u32 pg_idx, pg_offset; + u32 idx_value = 0; + int new_page; + + pg_idx = (idx * 4) / PAGE_SIZE; + pg_offset = (idx * 4) % PAGE_SIZE; + + if (ibc->kpage_idx[0] == pg_idx) + return ibc->kpage[0][pg_offset/4]; + if (ibc->kpage_idx[1] == pg_idx) + return ibc->kpage[1][pg_offset/4]; + + new_page = radeon_cs_update_pages(p, pg_idx); + if (new_page < 0) { + p->parser_error = new_page; + return 0; + } + + idx_value = ibc->kpage[new_page][pg_offset/4]; + return idx_value; +} + struct radeon_cs_packet { unsigned idx; unsigned type; @@ -517,6 +519,7 @@ typedef int (*radeon_packet3_check_t)(struct radeon_cs_parser *p, * AGP */ int radeon_agp_init(struct radeon_device *rdev); +void radeon_agp_resume(struct radeon_device *rdev); void radeon_agp_fini(struct radeon_device *rdev); @@ -594,18 +597,8 @@ struct radeon_asic { void (*fini)(struct radeon_device *rdev); int (*resume)(struct radeon_device *rdev); int (*suspend)(struct radeon_device *rdev); - void (*errata)(struct radeon_device *rdev); - void (*vram_info)(struct radeon_device *rdev); void (*vga_set_state)(struct radeon_device *rdev, bool state); int (*gpu_reset)(struct radeon_device *rdev); - int (*mc_init)(struct radeon_device *rdev); - void (*mc_fini)(struct radeon_device *rdev); - int (*wb_init)(struct radeon_device *rdev); - void (*wb_fini)(struct radeon_device *rdev); - int (*gart_init)(struct radeon_device *rdev); - void (*gart_fini)(struct radeon_device *rdev); - int (*gart_enable)(struct radeon_device *rdev); - void (*gart_disable)(struct radeon_device *rdev); void (*gart_tlb_flush)(struct radeon_device *rdev); int (*gart_set_page)(struct radeon_device *rdev, int i, uint64_t addr); int (*cp_init)(struct radeon_device *rdev, unsigned ring_size); @@ -615,7 +608,6 @@ struct radeon_asic { void (*ring_start)(struct radeon_device *rdev); int (*ring_test)(struct radeon_device *rdev); void (*ring_ib_execute)(struct radeon_device *rdev, struct radeon_ib *ib); - int (*ib_test)(struct radeon_device *rdev); int (*irq_set)(struct radeon_device *rdev); int (*irq_process)(struct radeon_device *rdev); u32 (*get_vblank_counter)(struct radeon_device *rdev, int crtc); @@ -636,7 +628,9 @@ struct radeon_asic { uint64_t dst_offset, unsigned num_pages, struct radeon_fence *fence); + uint32_t (*get_engine_clock)(struct radeon_device *rdev); void (*set_engine_clock)(struct radeon_device *rdev, uint32_t eng_clock); + uint32_t (*get_memory_clock)(struct radeon_device *rdev); void (*set_memory_clock)(struct radeon_device *rdev, uint32_t mem_clock); void (*set_pcie_lanes)(struct radeon_device *rdev, int lanes); void (*set_clock_gating)(struct radeon_device *rdev, int enable); @@ -793,12 +787,12 @@ struct radeon_device { bool shutdown; bool suspend; bool need_dma32; - bool new_init_path; bool accel_working; struct radeon_surface_reg surface_regs[RADEON_GEM_MAX_SURFACES]; const struct firmware *me_fw; /* all family ME firmware */ const struct firmware *pfp_fw; /* r6/700 PFP firmware */ struct r600_blit r600_blit; + int msi_enabled; /* msi enabled */ }; int radeon_device_init(struct radeon_device *rdev, @@ -953,28 +947,14 @@ static inline void radeon_ring_write(struct radeon_device *rdev, uint32_t v) #define radeon_resume(rdev) (rdev)->asic->resume((rdev)) #define radeon_suspend(rdev) (rdev)->asic->suspend((rdev)) #define radeon_cs_parse(p) rdev->asic->cs_parse((p)) -#define radeon_errata(rdev) (rdev)->asic->errata((rdev)) -#define radeon_vram_info(rdev) (rdev)->asic->vram_info((rdev)) #define radeon_vga_set_state(rdev, state) (rdev)->asic->vga_set_state((rdev), (state)) #define radeon_gpu_reset(rdev) (rdev)->asic->gpu_reset((rdev)) -#define radeon_mc_init(rdev) (rdev)->asic->mc_init((rdev)) -#define radeon_mc_fini(rdev) (rdev)->asic->mc_fini((rdev)) -#define radeon_wb_init(rdev) (rdev)->asic->wb_init((rdev)) -#define radeon_wb_fini(rdev) (rdev)->asic->wb_fini((rdev)) -#define radeon_gpu_gart_init(rdev) (rdev)->asic->gart_init((rdev)) -#define radeon_gpu_gart_fini(rdev) (rdev)->asic->gart_fini((rdev)) -#define radeon_gart_enable(rdev) (rdev)->asic->gart_enable((rdev)) -#define radeon_gart_disable(rdev) (rdev)->asic->gart_disable((rdev)) #define radeon_gart_tlb_flush(rdev) (rdev)->asic->gart_tlb_flush((rdev)) #define radeon_gart_set_page(rdev, i, p) (rdev)->asic->gart_set_page((rdev), (i), (p)) -#define radeon_cp_init(rdev,rsize) (rdev)->asic->cp_init((rdev), (rsize)) -#define radeon_cp_fini(rdev) (rdev)->asic->cp_fini((rdev)) -#define radeon_cp_disable(rdev) (rdev)->asic->cp_disable((rdev)) #define radeon_cp_commit(rdev) (rdev)->asic->cp_commit((rdev)) #define radeon_ring_start(rdev) (rdev)->asic->ring_start((rdev)) #define radeon_ring_test(rdev) (rdev)->asic->ring_test((rdev)) #define radeon_ring_ib_execute(rdev, ib) (rdev)->asic->ring_ib_execute((rdev), (ib)) -#define radeon_ib_test(rdev) (rdev)->asic->ib_test((rdev)) #define radeon_irq_set(rdev) (rdev)->asic->irq_set((rdev)) #define radeon_irq_process(rdev) (rdev)->asic->irq_process((rdev)) #define radeon_get_vblank_counter(rdev, crtc) (rdev)->asic->get_vblank_counter((rdev), (crtc)) @@ -982,7 +962,9 @@ static inline void radeon_ring_write(struct radeon_device *rdev, uint32_t v) #define radeon_copy_blit(rdev, s, d, np, f) (rdev)->asic->copy_blit((rdev), (s), (d), (np), (f)) #define radeon_copy_dma(rdev, s, d, np, f) (rdev)->asic->copy_dma((rdev), (s), (d), (np), (f)) #define radeon_copy(rdev, s, d, np, f) (rdev)->asic->copy((rdev), (s), (d), (np), (f)) +#define radeon_get_engine_clock(rdev) (rdev)->asic->get_engine_clock((rdev)) #define radeon_set_engine_clock(rdev, e) (rdev)->asic->set_engine_clock((rdev), (e)) +#define radeon_get_memory_clock(rdev) (rdev)->asic->get_memory_clock((rdev)) #define radeon_set_memory_clock(rdev, e) (rdev)->asic->set_engine_clock((rdev), (e)) #define radeon_set_pcie_lanes(rdev, l) (rdev)->asic->set_pcie_lanes((rdev), (l)) #define radeon_set_clock_gating(rdev, e) (rdev)->asic->set_clock_gating((rdev), (e)) @@ -1000,6 +982,8 @@ extern void radeon_clocks_fini(struct radeon_device *rdev); extern void radeon_scratch_init(struct radeon_device *rdev); extern void radeon_surface_init(struct radeon_device *rdev); extern int radeon_cs_parser_init(struct radeon_cs_parser *p, void *data); +extern void radeon_legacy_set_clock_gating(struct radeon_device *rdev, int enable); +extern void radeon_atom_set_clock_gating(struct radeon_device *rdev, int enable); /* r100,rv100,rs100,rv200,rs200,r200,rv250,rs300,rv280 */ struct r100_mc_save { @@ -1031,23 +1015,71 @@ extern void r100_vram_init_sizes(struct radeon_device *rdev); extern void r100_wb_disable(struct radeon_device *rdev); extern void r100_wb_fini(struct radeon_device *rdev); extern int r100_wb_init(struct radeon_device *rdev); +extern void r100_hdp_reset(struct radeon_device *rdev); +extern int r100_rb2d_reset(struct radeon_device *rdev); +extern int r100_cp_reset(struct radeon_device *rdev); +extern void r100_vga_render_disable(struct radeon_device *rdev); +extern int r100_cs_track_check_pkt3_indx_buffer(struct radeon_cs_parser *p, + struct radeon_cs_packet *pkt, + struct radeon_object *robj); +extern int r100_cs_parse_packet0(struct radeon_cs_parser *p, + struct radeon_cs_packet *pkt, + const unsigned *auth, unsigned n, + radeon_packet0_check_t check); +extern int r100_cs_packet_parse(struct radeon_cs_parser *p, + struct radeon_cs_packet *pkt, + unsigned idx); + +/* rv200,rv250,rv280 */ +extern void r200_set_safe_registers(struct radeon_device *rdev); /* r300,r350,rv350,rv370,rv380 */ extern void r300_set_reg_safe(struct radeon_device *rdev); extern void r300_mc_program(struct radeon_device *rdev); extern void r300_vram_info(struct radeon_device *rdev); +extern void r300_clock_startup(struct radeon_device *rdev); +extern int r300_mc_wait_for_idle(struct radeon_device *rdev); extern int rv370_pcie_gart_init(struct radeon_device *rdev); extern void rv370_pcie_gart_fini(struct radeon_device *rdev); extern int rv370_pcie_gart_enable(struct radeon_device *rdev); extern void rv370_pcie_gart_disable(struct radeon_device *rdev); /* r420,r423,rv410 */ +extern int r420_mc_init(struct radeon_device *rdev); extern u32 r420_mc_rreg(struct radeon_device *rdev, u32 reg); extern void r420_mc_wreg(struct radeon_device *rdev, u32 reg, u32 v); extern int r420_debugfs_pipes_info_init(struct radeon_device *rdev); +extern void r420_pipes_init(struct radeon_device *rdev); /* rv515 */ +struct rv515_mc_save { + u32 d1vga_control; + u32 d2vga_control; + u32 vga_render_control; + u32 vga_hdp_control; + u32 d1crtc_control; + u32 d2crtc_control; +}; extern void rv515_bandwidth_avivo_update(struct radeon_device *rdev); +extern void rv515_vga_render_disable(struct radeon_device *rdev); +extern void rv515_set_safe_registers(struct radeon_device *rdev); +extern void rv515_mc_stop(struct radeon_device *rdev, struct rv515_mc_save *save); +extern void rv515_mc_resume(struct radeon_device *rdev, struct rv515_mc_save *save); +extern void rv515_clock_startup(struct radeon_device *rdev); +extern void rv515_debugfs(struct radeon_device *rdev); +extern int rv515_suspend(struct radeon_device *rdev); + +/* rs400 */ +extern int rs400_gart_init(struct radeon_device *rdev); +extern int rs400_gart_enable(struct radeon_device *rdev); +extern void rs400_gart_adjust_size(struct radeon_device *rdev); +extern void rs400_gart_disable(struct radeon_device *rdev); +extern void rs400_gart_fini(struct radeon_device *rdev); + +/* rs600 */ +extern void rs600_set_safe_registers(struct radeon_device *rdev); +extern int rs600_irq_set(struct radeon_device *rdev); +extern void rs600_irq_disable(struct radeon_device *rdev); /* rs690, rs740 */ extern void rs690_line_buffer_adjust(struct radeon_device *rdev, @@ -1066,8 +1098,9 @@ extern int r600_pcie_gart_init(struct radeon_device *rdev); extern void r600_pcie_gart_tlb_flush(struct radeon_device *rdev); extern int r600_ib_test(struct radeon_device *rdev); extern int r600_ring_test(struct radeon_device *rdev); -extern int r600_wb_init(struct radeon_device *rdev); extern void r600_wb_fini(struct radeon_device *rdev); +extern int r600_wb_enable(struct radeon_device *rdev); +extern void r600_wb_disable(struct radeon_device *rdev); extern void r600_scratch_init(struct radeon_device *rdev); extern int r600_blit_init(struct radeon_device *rdev); extern void r600_blit_fini(struct radeon_device *rdev); diff --git a/drivers/gpu/drm/radeon/radeon_agp.c b/drivers/gpu/drm/radeon/radeon_agp.c index 23ea9955ac59..54bf49a6d676 100644 --- a/drivers/gpu/drm/radeon/radeon_agp.c +++ b/drivers/gpu/drm/radeon/radeon_agp.c @@ -237,6 +237,18 @@ int radeon_agp_init(struct radeon_device *rdev) #endif } +void radeon_agp_resume(struct radeon_device *rdev) +{ +#if __OS_HAS_AGP + int r; + if (rdev->flags & RADEON_IS_AGP) { + r = radeon_agp_init(rdev); + if (r) + dev_warn(rdev->dev, "radeon AGP reinit failed\n"); + } +#endif +} + void radeon_agp_fini(struct radeon_device *rdev) { #if __OS_HAS_AGP diff --git a/drivers/gpu/drm/radeon/radeon_asic.h b/drivers/gpu/drm/radeon/radeon_asic.h index 8968f78fa1e3..c18fbee387d7 100644 --- a/drivers/gpu/drm/radeon/radeon_asic.h +++ b/drivers/gpu/drm/radeon/radeon_asic.h @@ -31,38 +31,30 @@ /* * common functions */ +uint32_t radeon_legacy_get_engine_clock(struct radeon_device *rdev); void radeon_legacy_set_engine_clock(struct radeon_device *rdev, uint32_t eng_clock); void radeon_legacy_set_clock_gating(struct radeon_device *rdev, int enable); +uint32_t radeon_atom_get_engine_clock(struct radeon_device *rdev); void radeon_atom_set_engine_clock(struct radeon_device *rdev, uint32_t eng_clock); +uint32_t radeon_atom_get_memory_clock(struct radeon_device *rdev); void radeon_atom_set_memory_clock(struct radeon_device *rdev, uint32_t mem_clock); void radeon_atom_set_clock_gating(struct radeon_device *rdev, int enable); /* * r100,rv100,rs100,rv200,rs200,r200,rv250,rs300,rv280 */ -int r100_init(struct radeon_device *rdev); -int r200_init(struct radeon_device *rdev); +extern int r100_init(struct radeon_device *rdev); +extern void r100_fini(struct radeon_device *rdev); +extern int r100_suspend(struct radeon_device *rdev); +extern int r100_resume(struct radeon_device *rdev); uint32_t r100_mm_rreg(struct radeon_device *rdev, uint32_t reg); void r100_mm_wreg(struct radeon_device *rdev, uint32_t reg, uint32_t v); -void r100_errata(struct radeon_device *rdev); -void r100_vram_info(struct radeon_device *rdev); void r100_vga_set_state(struct radeon_device *rdev, bool state); int r100_gpu_reset(struct radeon_device *rdev); -int r100_mc_init(struct radeon_device *rdev); -void r100_mc_fini(struct radeon_device *rdev); u32 r100_get_vblank_counter(struct radeon_device *rdev, int crtc); -int r100_wb_init(struct radeon_device *rdev); -void r100_wb_fini(struct radeon_device *rdev); -int r100_pci_gart_init(struct radeon_device *rdev); -void r100_pci_gart_fini(struct radeon_device *rdev); -int r100_pci_gart_enable(struct radeon_device *rdev); -void r100_pci_gart_disable(struct radeon_device *rdev); void r100_pci_gart_tlb_flush(struct radeon_device *rdev); int r100_pci_gart_set_page(struct radeon_device *rdev, int i, uint64_t addr); -int r100_cp_init(struct radeon_device *rdev, unsigned ring_size); -void r100_cp_fini(struct radeon_device *rdev); -void r100_cp_disable(struct radeon_device *rdev); void r100_cp_commit(struct radeon_device *rdev); void r100_ring_start(struct radeon_device *rdev); int r100_irq_set(struct radeon_device *rdev); @@ -83,33 +75,21 @@ int r100_set_surface_reg(struct radeon_device *rdev, int reg, int r100_clear_surface_reg(struct radeon_device *rdev, int reg); void r100_bandwidth_update(struct radeon_device *rdev); void r100_ring_ib_execute(struct radeon_device *rdev, struct radeon_ib *ib); -int r100_ib_test(struct radeon_device *rdev); int r100_ring_test(struct radeon_device *rdev); static struct radeon_asic r100_asic = { .init = &r100_init, - .errata = &r100_errata, - .vram_info = &r100_vram_info, + .fini = &r100_fini, + .suspend = &r100_suspend, + .resume = &r100_resume, .vga_set_state = &r100_vga_set_state, .gpu_reset = &r100_gpu_reset, - .mc_init = &r100_mc_init, - .mc_fini = &r100_mc_fini, - .wb_init = &r100_wb_init, - .wb_fini = &r100_wb_fini, - .gart_init = &r100_pci_gart_init, - .gart_fini = &r100_pci_gart_fini, - .gart_enable = &r100_pci_gart_enable, - .gart_disable = &r100_pci_gart_disable, .gart_tlb_flush = &r100_pci_gart_tlb_flush, .gart_set_page = &r100_pci_gart_set_page, - .cp_init = &r100_cp_init, - .cp_fini = &r100_cp_fini, - .cp_disable = &r100_cp_disable, .cp_commit = &r100_cp_commit, .ring_start = &r100_ring_start, .ring_test = &r100_ring_test, .ring_ib_execute = &r100_ring_ib_execute, - .ib_test = &r100_ib_test, .irq_set = &r100_irq_set, .irq_process = &r100_irq_process, .get_vblank_counter = &r100_get_vblank_counter, @@ -118,7 +98,9 @@ static struct radeon_asic r100_asic = { .copy_blit = &r100_copy_blit, .copy_dma = NULL, .copy = &r100_copy_blit, + .get_engine_clock = &radeon_legacy_get_engine_clock, .set_engine_clock = &radeon_legacy_set_engine_clock, + .get_memory_clock = NULL, .set_memory_clock = NULL, .set_pcie_lanes = NULL, .set_clock_gating = &radeon_legacy_set_clock_gating, @@ -131,55 +113,38 @@ static struct radeon_asic r100_asic = { /* * r300,r350,rv350,rv380 */ -int r300_init(struct radeon_device *rdev); -void r300_errata(struct radeon_device *rdev); -void r300_vram_info(struct radeon_device *rdev); -int r300_gpu_reset(struct radeon_device *rdev); -int r300_mc_init(struct radeon_device *rdev); -void r300_mc_fini(struct radeon_device *rdev); -void r300_ring_start(struct radeon_device *rdev); -void r300_fence_ring_emit(struct radeon_device *rdev, - struct radeon_fence *fence); -int r300_cs_parse(struct radeon_cs_parser *p); -int rv370_pcie_gart_init(struct radeon_device *rdev); -void rv370_pcie_gart_fini(struct radeon_device *rdev); -int rv370_pcie_gart_enable(struct radeon_device *rdev); -void rv370_pcie_gart_disable(struct radeon_device *rdev); -void rv370_pcie_gart_tlb_flush(struct radeon_device *rdev); -int rv370_pcie_gart_set_page(struct radeon_device *rdev, int i, uint64_t addr); -uint32_t rv370_pcie_rreg(struct radeon_device *rdev, uint32_t reg); -void rv370_pcie_wreg(struct radeon_device *rdev, uint32_t reg, uint32_t v); -void rv370_set_pcie_lanes(struct radeon_device *rdev, int lanes); -int r300_copy_dma(struct radeon_device *rdev, - uint64_t src_offset, - uint64_t dst_offset, - unsigned num_pages, - struct radeon_fence *fence); - +extern int r300_init(struct radeon_device *rdev); +extern void r300_fini(struct radeon_device *rdev); +extern int r300_suspend(struct radeon_device *rdev); +extern int r300_resume(struct radeon_device *rdev); +extern int r300_gpu_reset(struct radeon_device *rdev); +extern void r300_ring_start(struct radeon_device *rdev); +extern void r300_fence_ring_emit(struct radeon_device *rdev, + struct radeon_fence *fence); +extern int r300_cs_parse(struct radeon_cs_parser *p); +extern void rv370_pcie_gart_tlb_flush(struct radeon_device *rdev); +extern int rv370_pcie_gart_set_page(struct radeon_device *rdev, int i, uint64_t addr); +extern uint32_t rv370_pcie_rreg(struct radeon_device *rdev, uint32_t reg); +extern void rv370_pcie_wreg(struct radeon_device *rdev, uint32_t reg, uint32_t v); +extern void rv370_set_pcie_lanes(struct radeon_device *rdev, int lanes); +extern int r300_copy_dma(struct radeon_device *rdev, + uint64_t src_offset, + uint64_t dst_offset, + unsigned num_pages, + struct radeon_fence *fence); static struct radeon_asic r300_asic = { .init = &r300_init, - .errata = &r300_errata, - .vram_info = &r300_vram_info, + .fini = &r300_fini, + .suspend = &r300_suspend, + .resume = &r300_resume, .vga_set_state = &r100_vga_set_state, .gpu_reset = &r300_gpu_reset, - .mc_init = &r300_mc_init, - .mc_fini = &r300_mc_fini, - .wb_init = &r100_wb_init, - .wb_fini = &r100_wb_fini, - .gart_init = &r100_pci_gart_init, - .gart_fini = &r100_pci_gart_fini, - .gart_enable = &r100_pci_gart_enable, - .gart_disable = &r100_pci_gart_disable, .gart_tlb_flush = &r100_pci_gart_tlb_flush, .gart_set_page = &r100_pci_gart_set_page, - .cp_init = &r100_cp_init, - .cp_fini = &r100_cp_fini, - .cp_disable = &r100_cp_disable, .cp_commit = &r100_cp_commit, .ring_start = &r300_ring_start, .ring_test = &r100_ring_test, .ring_ib_execute = &r100_ring_ib_execute, - .ib_test = &r100_ib_test, .irq_set = &r100_irq_set, .irq_process = &r100_irq_process, .get_vblank_counter = &r100_get_vblank_counter, @@ -188,7 +153,9 @@ static struct radeon_asic r300_asic = { .copy_blit = &r100_copy_blit, .copy_dma = &r300_copy_dma, .copy = &r100_copy_blit, + .get_engine_clock = &radeon_legacy_get_engine_clock, .set_engine_clock = &radeon_legacy_set_engine_clock, + .get_memory_clock = NULL, .set_memory_clock = NULL, .set_pcie_lanes = &rv370_set_pcie_lanes, .set_clock_gating = &radeon_legacy_set_clock_gating, @@ -209,26 +176,14 @@ static struct radeon_asic r420_asic = { .fini = &r420_fini, .suspend = &r420_suspend, .resume = &r420_resume, - .errata = NULL, - .vram_info = NULL, .vga_set_state = &r100_vga_set_state, .gpu_reset = &r300_gpu_reset, - .mc_init = NULL, - .mc_fini = NULL, - .wb_init = NULL, - .wb_fini = NULL, - .gart_enable = NULL, - .gart_disable = NULL, .gart_tlb_flush = &rv370_pcie_gart_tlb_flush, .gart_set_page = &rv370_pcie_gart_set_page, - .cp_init = NULL, - .cp_fini = NULL, - .cp_disable = NULL, .cp_commit = &r100_cp_commit, .ring_start = &r300_ring_start, .ring_test = &r100_ring_test, .ring_ib_execute = &r100_ring_ib_execute, - .ib_test = NULL, .irq_set = &r100_irq_set, .irq_process = &r100_irq_process, .get_vblank_counter = &r100_get_vblank_counter, @@ -237,7 +192,9 @@ static struct radeon_asic r420_asic = { .copy_blit = &r100_copy_blit, .copy_dma = &r300_copy_dma, .copy = &r100_copy_blit, + .get_engine_clock = &radeon_atom_get_engine_clock, .set_engine_clock = &radeon_atom_set_engine_clock, + .get_memory_clock = &radeon_atom_get_memory_clock, .set_memory_clock = &radeon_atom_set_memory_clock, .set_pcie_lanes = &rv370_set_pcie_lanes, .set_clock_gating = &radeon_atom_set_clock_gating, @@ -250,42 +207,27 @@ static struct radeon_asic r420_asic = { /* * rs400,rs480 */ -void rs400_errata(struct radeon_device *rdev); -void rs400_vram_info(struct radeon_device *rdev); -int rs400_mc_init(struct radeon_device *rdev); -void rs400_mc_fini(struct radeon_device *rdev); -int rs400_gart_init(struct radeon_device *rdev); -void rs400_gart_fini(struct radeon_device *rdev); -int rs400_gart_enable(struct radeon_device *rdev); -void rs400_gart_disable(struct radeon_device *rdev); +extern int rs400_init(struct radeon_device *rdev); +extern void rs400_fini(struct radeon_device *rdev); +extern int rs400_suspend(struct radeon_device *rdev); +extern int rs400_resume(struct radeon_device *rdev); void rs400_gart_tlb_flush(struct radeon_device *rdev); int rs400_gart_set_page(struct radeon_device *rdev, int i, uint64_t addr); uint32_t rs400_mc_rreg(struct radeon_device *rdev, uint32_t reg); void rs400_mc_wreg(struct radeon_device *rdev, uint32_t reg, uint32_t v); static struct radeon_asic rs400_asic = { - .init = &r300_init, - .errata = &rs400_errata, - .vram_info = &rs400_vram_info, + .init = &rs400_init, + .fini = &rs400_fini, + .suspend = &rs400_suspend, + .resume = &rs400_resume, .vga_set_state = &r100_vga_set_state, .gpu_reset = &r300_gpu_reset, - .mc_init = &rs400_mc_init, - .mc_fini = &rs400_mc_fini, - .wb_init = &r100_wb_init, - .wb_fini = &r100_wb_fini, - .gart_init = &rs400_gart_init, - .gart_fini = &rs400_gart_fini, - .gart_enable = &rs400_gart_enable, - .gart_disable = &rs400_gart_disable, .gart_tlb_flush = &rs400_gart_tlb_flush, .gart_set_page = &rs400_gart_set_page, - .cp_init = &r100_cp_init, - .cp_fini = &r100_cp_fini, - .cp_disable = &r100_cp_disable, .cp_commit = &r100_cp_commit, .ring_start = &r300_ring_start, .ring_test = &r100_ring_test, .ring_ib_execute = &r100_ring_ib_execute, - .ib_test = &r100_ib_test, .irq_set = &r100_irq_set, .irq_process = &r100_irq_process, .get_vblank_counter = &r100_get_vblank_counter, @@ -294,7 +236,9 @@ static struct radeon_asic rs400_asic = { .copy_blit = &r100_copy_blit, .copy_dma = &r300_copy_dma, .copy = &r100_copy_blit, + .get_engine_clock = &radeon_legacy_get_engine_clock, .set_engine_clock = &radeon_legacy_set_engine_clock, + .get_memory_clock = NULL, .set_memory_clock = NULL, .set_pcie_lanes = NULL, .set_clock_gating = &radeon_legacy_set_clock_gating, @@ -307,18 +251,13 @@ static struct radeon_asic rs400_asic = { /* * rs600. */ -int rs600_init(struct radeon_device *rdev); -void rs600_errata(struct radeon_device *rdev); -void rs600_vram_info(struct radeon_device *rdev); -int rs600_mc_init(struct radeon_device *rdev); -void rs600_mc_fini(struct radeon_device *rdev); +extern int rs600_init(struct radeon_device *rdev); +extern void rs600_fini(struct radeon_device *rdev); +extern int rs600_suspend(struct radeon_device *rdev); +extern int rs600_resume(struct radeon_device *rdev); int rs600_irq_set(struct radeon_device *rdev); int rs600_irq_process(struct radeon_device *rdev); u32 rs600_get_vblank_counter(struct radeon_device *rdev, int crtc); -int rs600_gart_init(struct radeon_device *rdev); -void rs600_gart_fini(struct radeon_device *rdev); -int rs600_gart_enable(struct radeon_device *rdev); -void rs600_gart_disable(struct radeon_device *rdev); void rs600_gart_tlb_flush(struct radeon_device *rdev); int rs600_gart_set_page(struct radeon_device *rdev, int i, uint64_t addr); uint32_t rs600_mc_rreg(struct radeon_device *rdev, uint32_t reg); @@ -326,28 +265,17 @@ void rs600_mc_wreg(struct radeon_device *rdev, uint32_t reg, uint32_t v); void rs600_bandwidth_update(struct radeon_device *rdev); static struct radeon_asic rs600_asic = { .init = &rs600_init, - .errata = &rs600_errata, - .vram_info = &rs600_vram_info, + .fini = &rs600_fini, + .suspend = &rs600_suspend, + .resume = &rs600_resume, .vga_set_state = &r100_vga_set_state, .gpu_reset = &r300_gpu_reset, - .mc_init = &rs600_mc_init, - .mc_fini = &rs600_mc_fini, - .wb_init = &r100_wb_init, - .wb_fini = &r100_wb_fini, - .gart_init = &rs600_gart_init, - .gart_fini = &rs600_gart_fini, - .gart_enable = &rs600_gart_enable, - .gart_disable = &rs600_gart_disable, .gart_tlb_flush = &rs600_gart_tlb_flush, .gart_set_page = &rs600_gart_set_page, - .cp_init = &r100_cp_init, - .cp_fini = &r100_cp_fini, - .cp_disable = &r100_cp_disable, .cp_commit = &r100_cp_commit, .ring_start = &r300_ring_start, .ring_test = &r100_ring_test, .ring_ib_execute = &r100_ring_ib_execute, - .ib_test = &r100_ib_test, .irq_set = &rs600_irq_set, .irq_process = &rs600_irq_process, .get_vblank_counter = &rs600_get_vblank_counter, @@ -356,7 +284,9 @@ static struct radeon_asic rs600_asic = { .copy_blit = &r100_copy_blit, .copy_dma = &r300_copy_dma, .copy = &r100_copy_blit, + .get_engine_clock = &radeon_atom_get_engine_clock, .set_engine_clock = &radeon_atom_set_engine_clock, + .get_memory_clock = &radeon_atom_get_memory_clock, .set_memory_clock = &radeon_atom_set_memory_clock, .set_pcie_lanes = NULL, .set_clock_gating = &radeon_atom_set_clock_gating, @@ -367,37 +297,26 @@ static struct radeon_asic rs600_asic = { /* * rs690,rs740 */ -void rs690_errata(struct radeon_device *rdev); -void rs690_vram_info(struct radeon_device *rdev); -int rs690_mc_init(struct radeon_device *rdev); -void rs690_mc_fini(struct radeon_device *rdev); +int rs690_init(struct radeon_device *rdev); +void rs690_fini(struct radeon_device *rdev); +int rs690_resume(struct radeon_device *rdev); +int rs690_suspend(struct radeon_device *rdev); uint32_t rs690_mc_rreg(struct radeon_device *rdev, uint32_t reg); void rs690_mc_wreg(struct radeon_device *rdev, uint32_t reg, uint32_t v); void rs690_bandwidth_update(struct radeon_device *rdev); static struct radeon_asic rs690_asic = { - .init = &rs600_init, - .errata = &rs690_errata, - .vram_info = &rs690_vram_info, + .init = &rs690_init, + .fini = &rs690_fini, + .suspend = &rs690_suspend, + .resume = &rs690_resume, .vga_set_state = &r100_vga_set_state, .gpu_reset = &r300_gpu_reset, - .mc_init = &rs690_mc_init, - .mc_fini = &rs690_mc_fini, - .wb_init = &r100_wb_init, - .wb_fini = &r100_wb_fini, - .gart_init = &rs400_gart_init, - .gart_fini = &rs400_gart_fini, - .gart_enable = &rs400_gart_enable, - .gart_disable = &rs400_gart_disable, .gart_tlb_flush = &rs400_gart_tlb_flush, .gart_set_page = &rs400_gart_set_page, - .cp_init = &r100_cp_init, - .cp_fini = &r100_cp_fini, - .cp_disable = &r100_cp_disable, .cp_commit = &r100_cp_commit, .ring_start = &r300_ring_start, .ring_test = &r100_ring_test, .ring_ib_execute = &r100_ring_ib_execute, - .ib_test = &r100_ib_test, .irq_set = &rs600_irq_set, .irq_process = &rs600_irq_process, .get_vblank_counter = &rs600_get_vblank_counter, @@ -406,7 +325,9 @@ static struct radeon_asic rs690_asic = { .copy_blit = &r100_copy_blit, .copy_dma = &r300_copy_dma, .copy = &r300_copy_dma, + .get_engine_clock = &radeon_atom_get_engine_clock, .set_engine_clock = &radeon_atom_set_engine_clock, + .get_memory_clock = &radeon_atom_get_memory_clock, .set_memory_clock = &radeon_atom_set_memory_clock, .set_pcie_lanes = NULL, .set_clock_gating = &radeon_atom_set_clock_gating, @@ -420,41 +341,29 @@ static struct radeon_asic rs690_asic = { * rv515 */ int rv515_init(struct radeon_device *rdev); -void rv515_errata(struct radeon_device *rdev); -void rv515_vram_info(struct radeon_device *rdev); +void rv515_fini(struct radeon_device *rdev); int rv515_gpu_reset(struct radeon_device *rdev); -int rv515_mc_init(struct radeon_device *rdev); -void rv515_mc_fini(struct radeon_device *rdev); uint32_t rv515_mc_rreg(struct radeon_device *rdev, uint32_t reg); void rv515_mc_wreg(struct radeon_device *rdev, uint32_t reg, uint32_t v); void rv515_ring_start(struct radeon_device *rdev); uint32_t rv515_pcie_rreg(struct radeon_device *rdev, uint32_t reg); void rv515_pcie_wreg(struct radeon_device *rdev, uint32_t reg, uint32_t v); void rv515_bandwidth_update(struct radeon_device *rdev); +int rv515_resume(struct radeon_device *rdev); +int rv515_suspend(struct radeon_device *rdev); static struct radeon_asic rv515_asic = { .init = &rv515_init, - .errata = &rv515_errata, - .vram_info = &rv515_vram_info, + .fini = &rv515_fini, + .suspend = &rv515_suspend, + .resume = &rv515_resume, .vga_set_state = &r100_vga_set_state, .gpu_reset = &rv515_gpu_reset, - .mc_init = &rv515_mc_init, - .mc_fini = &rv515_mc_fini, - .wb_init = &r100_wb_init, - .wb_fini = &r100_wb_fini, - .gart_init = &rv370_pcie_gart_init, - .gart_fini = &rv370_pcie_gart_fini, - .gart_enable = &rv370_pcie_gart_enable, - .gart_disable = &rv370_pcie_gart_disable, .gart_tlb_flush = &rv370_pcie_gart_tlb_flush, .gart_set_page = &rv370_pcie_gart_set_page, - .cp_init = &r100_cp_init, - .cp_fini = &r100_cp_fini, - .cp_disable = &r100_cp_disable, .cp_commit = &r100_cp_commit, .ring_start = &rv515_ring_start, .ring_test = &r100_ring_test, .ring_ib_execute = &r100_ring_ib_execute, - .ib_test = &r100_ib_test, .irq_set = &rs600_irq_set, .irq_process = &rs600_irq_process, .get_vblank_counter = &rs600_get_vblank_counter, @@ -463,7 +372,9 @@ static struct radeon_asic rv515_asic = { .copy_blit = &r100_copy_blit, .copy_dma = &r300_copy_dma, .copy = &r100_copy_blit, + .get_engine_clock = &radeon_atom_get_engine_clock, .set_engine_clock = &radeon_atom_set_engine_clock, + .get_memory_clock = &radeon_atom_get_memory_clock, .set_memory_clock = &radeon_atom_set_memory_clock, .set_pcie_lanes = &rv370_set_pcie_lanes, .set_clock_gating = &radeon_atom_set_clock_gating, @@ -476,35 +387,21 @@ static struct radeon_asic rv515_asic = { /* * r520,rv530,rv560,rv570,r580 */ -void r520_errata(struct radeon_device *rdev); -void r520_vram_info(struct radeon_device *rdev); -int r520_mc_init(struct radeon_device *rdev); -void r520_mc_fini(struct radeon_device *rdev); -void r520_bandwidth_update(struct radeon_device *rdev); +int r520_init(struct radeon_device *rdev); +int r520_resume(struct radeon_device *rdev); static struct radeon_asic r520_asic = { - .init = &rv515_init, - .errata = &r520_errata, - .vram_info = &r520_vram_info, + .init = &r520_init, + .fini = &rv515_fini, + .suspend = &rv515_suspend, + .resume = &r520_resume, .vga_set_state = &r100_vga_set_state, .gpu_reset = &rv515_gpu_reset, - .mc_init = &r520_mc_init, - .mc_fini = &r520_mc_fini, - .wb_init = &r100_wb_init, - .wb_fini = &r100_wb_fini, - .gart_init = &rv370_pcie_gart_init, - .gart_fini = &rv370_pcie_gart_fini, - .gart_enable = &rv370_pcie_gart_enable, - .gart_disable = &rv370_pcie_gart_disable, .gart_tlb_flush = &rv370_pcie_gart_tlb_flush, .gart_set_page = &rv370_pcie_gart_set_page, - .cp_init = &r100_cp_init, - .cp_fini = &r100_cp_fini, - .cp_disable = &r100_cp_disable, .cp_commit = &r100_cp_commit, .ring_start = &rv515_ring_start, .ring_test = &r100_ring_test, .ring_ib_execute = &r100_ring_ib_execute, - .ib_test = &r100_ib_test, .irq_set = &rs600_irq_set, .irq_process = &rs600_irq_process, .get_vblank_counter = &rs600_get_vblank_counter, @@ -513,13 +410,15 @@ static struct radeon_asic r520_asic = { .copy_blit = &r100_copy_blit, .copy_dma = &r300_copy_dma, .copy = &r100_copy_blit, + .get_engine_clock = &radeon_atom_get_engine_clock, .set_engine_clock = &radeon_atom_set_engine_clock, + .get_memory_clock = &radeon_atom_get_memory_clock, .set_memory_clock = &radeon_atom_set_memory_clock, .set_pcie_lanes = &rv370_set_pcie_lanes, .set_clock_gating = &radeon_atom_set_clock_gating, .set_surface_reg = r100_set_surface_reg, .clear_surface_reg = r100_clear_surface_reg, - .bandwidth_update = &r520_bandwidth_update, + .bandwidth_update = &rv515_bandwidth_update, }; /* @@ -552,37 +451,23 @@ int r600_set_surface_reg(struct radeon_device *rdev, int reg, uint32_t offset, uint32_t obj_size); int r600_clear_surface_reg(struct radeon_device *rdev, int reg); void r600_ring_ib_execute(struct radeon_device *rdev, struct radeon_ib *ib); -int r600_ib_test(struct radeon_device *rdev); int r600_ring_test(struct radeon_device *rdev); int r600_copy_blit(struct radeon_device *rdev, uint64_t src_offset, uint64_t dst_offset, unsigned num_pages, struct radeon_fence *fence); static struct radeon_asic r600_asic = { - .errata = NULL, .init = &r600_init, .fini = &r600_fini, .suspend = &r600_suspend, .resume = &r600_resume, .cp_commit = &r600_cp_commit, - .vram_info = NULL, .vga_set_state = &r600_vga_set_state, .gpu_reset = &r600_gpu_reset, - .mc_init = NULL, - .mc_fini = NULL, - .wb_init = &r600_wb_init, - .wb_fini = &r600_wb_fini, - .gart_enable = NULL, - .gart_disable = NULL, .gart_tlb_flush = &r600_pcie_gart_tlb_flush, .gart_set_page = &rs600_gart_set_page, - .cp_init = NULL, - .cp_fini = NULL, - .cp_disable = NULL, - .ring_start = NULL, .ring_test = &r600_ring_test, .ring_ib_execute = &r600_ring_ib_execute, - .ib_test = &r600_ib_test, .irq_set = &r600_irq_set, .irq_process = &r600_irq_process, .fence_ring_emit = &r600_fence_ring_emit, @@ -590,13 +475,15 @@ static struct radeon_asic r600_asic = { .copy_blit = &r600_copy_blit, .copy_dma = &r600_copy_blit, .copy = &r600_copy_blit, + .get_engine_clock = &radeon_atom_get_engine_clock, .set_engine_clock = &radeon_atom_set_engine_clock, + .get_memory_clock = &radeon_atom_get_memory_clock, .set_memory_clock = &radeon_atom_set_memory_clock, .set_pcie_lanes = NULL, .set_clock_gating = &radeon_atom_set_clock_gating, .set_surface_reg = r600_set_surface_reg, .clear_surface_reg = r600_clear_surface_reg, - .bandwidth_update = &r520_bandwidth_update, + .bandwidth_update = &rv515_bandwidth_update, }; /* @@ -609,30 +496,17 @@ int rv770_resume(struct radeon_device *rdev); int rv770_gpu_reset(struct radeon_device *rdev); static struct radeon_asic rv770_asic = { - .errata = NULL, .init = &rv770_init, .fini = &rv770_fini, .suspend = &rv770_suspend, .resume = &rv770_resume, .cp_commit = &r600_cp_commit, - .vram_info = NULL, .gpu_reset = &rv770_gpu_reset, .vga_set_state = &r600_vga_set_state, - .mc_init = NULL, - .mc_fini = NULL, - .wb_init = &r600_wb_init, - .wb_fini = &r600_wb_fini, - .gart_enable = NULL, - .gart_disable = NULL, .gart_tlb_flush = &r600_pcie_gart_tlb_flush, .gart_set_page = &rs600_gart_set_page, - .cp_init = NULL, - .cp_fini = NULL, - .cp_disable = NULL, - .ring_start = NULL, .ring_test = &r600_ring_test, .ring_ib_execute = &r600_ring_ib_execute, - .ib_test = &r600_ib_test, .irq_set = &r600_irq_set, .irq_process = &r600_irq_process, .fence_ring_emit = &r600_fence_ring_emit, @@ -640,13 +514,15 @@ static struct radeon_asic rv770_asic = { .copy_blit = &r600_copy_blit, .copy_dma = &r600_copy_blit, .copy = &r600_copy_blit, + .get_engine_clock = &radeon_atom_get_engine_clock, .set_engine_clock = &radeon_atom_set_engine_clock, + .get_memory_clock = &radeon_atom_get_memory_clock, .set_memory_clock = &radeon_atom_set_memory_clock, .set_pcie_lanes = NULL, .set_clock_gating = &radeon_atom_set_clock_gating, .set_surface_reg = r600_set_surface_reg, .clear_surface_reg = r600_clear_surface_reg, - .bandwidth_update = &r520_bandwidth_update, + .bandwidth_update = &rv515_bandwidth_update, }; #endif diff --git a/drivers/gpu/drm/radeon/radeon_atombios.c b/drivers/gpu/drm/radeon/radeon_atombios.c index 743742128307..2ed88a820935 100644 --- a/drivers/gpu/drm/radeon/radeon_atombios.c +++ b/drivers/gpu/drm/radeon/radeon_atombios.c @@ -46,7 +46,8 @@ radeon_add_atom_connector(struct drm_device *dev, uint32_t supported_device, int connector_type, struct radeon_i2c_bus_rec *i2c_bus, - bool linkb, uint32_t igp_lane_info); + bool linkb, uint32_t igp_lane_info, + uint16_t connector_object_id); /* from radeon_legacy_encoder.c */ extern void @@ -193,6 +194,23 @@ const int supported_devices_connector_convert[] = { DRM_MODE_CONNECTOR_DisplayPort }; +const uint16_t supported_devices_connector_object_id_convert[] = { + CONNECTOR_OBJECT_ID_NONE, + CONNECTOR_OBJECT_ID_VGA, + CONNECTOR_OBJECT_ID_DUAL_LINK_DVI_I, /* not all boards support DL */ + CONNECTOR_OBJECT_ID_DUAL_LINK_DVI_D, /* not all boards support DL */ + CONNECTOR_OBJECT_ID_VGA, /* technically DVI-A */ + CONNECTOR_OBJECT_ID_COMPOSITE, + CONNECTOR_OBJECT_ID_SVIDEO, + CONNECTOR_OBJECT_ID_LVDS, + CONNECTOR_OBJECT_ID_9PIN_DIN, + CONNECTOR_OBJECT_ID_9PIN_DIN, + CONNECTOR_OBJECT_ID_DISPLAYPORT, + CONNECTOR_OBJECT_ID_HDMI_TYPE_A, + CONNECTOR_OBJECT_ID_HDMI_TYPE_B, + CONNECTOR_OBJECT_ID_SVIDEO +}; + const int object_connector_convert[] = { DRM_MODE_CONNECTOR_Unknown, DRM_MODE_CONNECTOR_DVII, @@ -229,7 +247,7 @@ bool radeon_get_atom_connector_info_from_object_table(struct drm_device *dev) ATOM_OBJECT_HEADER *obj_header; int i, j, path_size, device_support; int connector_type; - uint16_t igp_lane_info, conn_id; + uint16_t igp_lane_info, conn_id, connector_object_id; bool linkb; struct radeon_i2c_bus_rec ddc_bus; @@ -272,15 +290,13 @@ bool radeon_get_atom_connector_info_from_object_table(struct drm_device *dev) (le16_to_cpu(path->usConnObjectId) & OBJECT_TYPE_MASK) >> OBJECT_TYPE_SHIFT; - if ((le16_to_cpu(path->usDeviceTag) == - ATOM_DEVICE_TV1_SUPPORT) - || (le16_to_cpu(path->usDeviceTag) == - ATOM_DEVICE_TV2_SUPPORT) - || (le16_to_cpu(path->usDeviceTag) == - ATOM_DEVICE_CV_SUPPORT)) + /* TODO CV support */ + if (le16_to_cpu(path->usDeviceTag) == + ATOM_DEVICE_CV_SUPPORT) continue; - if ((rdev->family == CHIP_RS780) && + /* IGP chips */ + if ((rdev->flags & RADEON_IS_IGP) && (con_obj_id == CONNECTOR_OBJECT_ID_PCIE_CONNECTOR)) { uint16_t igp_offset = 0; @@ -314,6 +330,7 @@ bool radeon_get_atom_connector_info_from_object_table(struct drm_device *dev) connector_type = object_connector_convert [ct]; + connector_object_id = ct; igp_lane_info = slot_config & 0xffff; } else @@ -324,6 +341,7 @@ bool radeon_get_atom_connector_info_from_object_table(struct drm_device *dev) igp_lane_info = 0; connector_type = object_connector_convert[con_obj_id]; + connector_object_id = con_obj_id; } if (connector_type == DRM_MODE_CONNECTOR_Unknown) @@ -428,7 +446,8 @@ bool radeon_get_atom_connector_info_from_object_table(struct drm_device *dev) le16_to_cpu(path-> usDeviceTag), connector_type, &ddc_bus, - linkb, igp_lane_info); + linkb, igp_lane_info, + connector_object_id); } } @@ -438,6 +457,45 @@ bool radeon_get_atom_connector_info_from_object_table(struct drm_device *dev) return true; } +static uint16_t atombios_get_connector_object_id(struct drm_device *dev, + int connector_type, + uint16_t devices) +{ + struct radeon_device *rdev = dev->dev_private; + + if (rdev->flags & RADEON_IS_IGP) { + return supported_devices_connector_object_id_convert + [connector_type]; + } else if (((connector_type == DRM_MODE_CONNECTOR_DVII) || + (connector_type == DRM_MODE_CONNECTOR_DVID)) && + (devices & ATOM_DEVICE_DFP2_SUPPORT)) { + struct radeon_mode_info *mode_info = &rdev->mode_info; + struct atom_context *ctx = mode_info->atom_context; + int index = GetIndexIntoMasterTable(DATA, XTMDS_Info); + uint16_t size, data_offset; + uint8_t frev, crev; + ATOM_XTMDS_INFO *xtmds; + + atom_parse_data_header(ctx, index, &size, &frev, &crev, &data_offset); + xtmds = (ATOM_XTMDS_INFO *)(ctx->bios + data_offset); + + if (xtmds->ucSupportedLink & ATOM_XTMDS_SUPPORTED_DUALLINK) { + if (connector_type == DRM_MODE_CONNECTOR_DVII) + return CONNECTOR_OBJECT_ID_DUAL_LINK_DVI_I; + else + return CONNECTOR_OBJECT_ID_DUAL_LINK_DVI_D; + } else { + if (connector_type == DRM_MODE_CONNECTOR_DVII) + return CONNECTOR_OBJECT_ID_SINGLE_LINK_DVI_I; + else + return CONNECTOR_OBJECT_ID_SINGLE_LINK_DVI_D; + } + } else { + return supported_devices_connector_object_id_convert + [connector_type]; + } +} + struct bios_connector { bool valid; uint16_t line_mux; @@ -596,14 +654,20 @@ bool radeon_get_atom_connector_info_from_supported_devices_table(struct /* add the connectors */ for (i = 0; i < ATOM_MAX_SUPPORTED_DEVICE; i++) { - if (bios_connectors[i].valid) + if (bios_connectors[i].valid) { + uint16_t connector_object_id = + atombios_get_connector_object_id(dev, + bios_connectors[i].connector_type, + bios_connectors[i].devices); radeon_add_atom_connector(dev, bios_connectors[i].line_mux, bios_connectors[i].devices, bios_connectors[i]. connector_type, &bios_connectors[i].ddc_bus, - false, 0); + false, 0, + connector_object_id); + } } radeon_link_encoder_connector(dev); @@ -644,8 +708,12 @@ bool radeon_atom_get_clock_info(struct drm_device *dev) le16_to_cpu(firmware_info->info.usReferenceClock); p1pll->reference_div = 0; - p1pll->pll_out_min = - le16_to_cpu(firmware_info->info.usMinPixelClockPLL_Output); + if (crev < 2) + p1pll->pll_out_min = + le16_to_cpu(firmware_info->info.usMinPixelClockPLL_Output); + else + p1pll->pll_out_min = + le32_to_cpu(firmware_info->info_12.ulMinPixelClockPLL_Output); p1pll->pll_out_max = le32_to_cpu(firmware_info->info.ulMaxPixelClockPLL_Output); @@ -654,6 +722,16 @@ bool radeon_atom_get_clock_info(struct drm_device *dev) p1pll->pll_out_min = 64800; else p1pll->pll_out_min = 20000; + } else if (p1pll->pll_out_min > 64800) { + /* Limiting the pll output range is a good thing generally as + * it limits the number of possible pll combinations for a given + * frequency presumably to the ones that work best on each card. + * However, certain duallink DVI monitors seem to like + * pll combinations that would be limited by this at least on + * pre-DCE 3.0 r6xx hardware. This might need to be adjusted per + * family. + */ + p1pll->pll_out_min = 64800; } p1pll->pll_in_min = @@ -770,6 +848,46 @@ bool radeon_atombios_get_tmds_info(struct radeon_encoder *encoder, return false; } +static struct radeon_atom_ss *radeon_atombios_get_ss_info(struct + radeon_encoder + *encoder, + int id) +{ + struct drm_device *dev = encoder->base.dev; + struct radeon_device *rdev = dev->dev_private; + struct radeon_mode_info *mode_info = &rdev->mode_info; + int index = GetIndexIntoMasterTable(DATA, PPLL_SS_Info); + uint16_t data_offset; + struct _ATOM_SPREAD_SPECTRUM_INFO *ss_info; + uint8_t frev, crev; + struct radeon_atom_ss *ss = NULL; + + if (id > ATOM_MAX_SS_ENTRY) + return NULL; + + atom_parse_data_header(mode_info->atom_context, index, NULL, &frev, + &crev, &data_offset); + + ss_info = + (struct _ATOM_SPREAD_SPECTRUM_INFO *)(mode_info->atom_context->bios + data_offset); + + if (ss_info) { + ss = + kzalloc(sizeof(struct radeon_atom_ss), GFP_KERNEL); + + if (!ss) + return NULL; + + ss->percentage = le16_to_cpu(ss_info->asSS_Info[id].usSpreadSpectrumPercentage); + ss->type = ss_info->asSS_Info[id].ucSpreadSpectrumType; + ss->step = ss_info->asSS_Info[id].ucSS_Step; + ss->delay = ss_info->asSS_Info[id].ucSS_Delay; + ss->range = ss_info->asSS_Info[id].ucSS_Range; + ss->refdiv = ss_info->asSS_Info[id].ucRecommendedRef_Div; + } + return ss; +} + union lvds_info { struct _ATOM_LVDS_INFO info; struct _ATOM_LVDS_INFO_V12 info_12; @@ -801,27 +919,31 @@ struct radeon_encoder_atom_dig *radeon_atombios_get_lvds_info(struct if (!lvds) return NULL; - lvds->native_mode.dotclock = + lvds->native_mode.clock = le16_to_cpu(lvds_info->info.sLCDTiming.usPixClk) * 10; - lvds->native_mode.panel_xres = + lvds->native_mode.hdisplay = le16_to_cpu(lvds_info->info.sLCDTiming.usHActive); - lvds->native_mode.panel_yres = + lvds->native_mode.vdisplay = le16_to_cpu(lvds_info->info.sLCDTiming.usVActive); - lvds->native_mode.hblank = - le16_to_cpu(lvds_info->info.sLCDTiming.usHBlanking_Time); - lvds->native_mode.hoverplus = - le16_to_cpu(lvds_info->info.sLCDTiming.usHSyncOffset); - lvds->native_mode.hsync_width = - le16_to_cpu(lvds_info->info.sLCDTiming.usHSyncWidth); - lvds->native_mode.vblank = - le16_to_cpu(lvds_info->info.sLCDTiming.usVBlanking_Time); - lvds->native_mode.voverplus = - le16_to_cpu(lvds_info->info.sLCDTiming.usVSyncOffset); - lvds->native_mode.vsync_width = - le16_to_cpu(lvds_info->info.sLCDTiming.usVSyncWidth); + lvds->native_mode.htotal = lvds->native_mode.hdisplay + + le16_to_cpu(lvds_info->info.sLCDTiming.usHBlanking_Time); + lvds->native_mode.hsync_start = lvds->native_mode.hdisplay + + le16_to_cpu(lvds_info->info.sLCDTiming.usHSyncOffset); + lvds->native_mode.hsync_end = lvds->native_mode.hsync_start + + le16_to_cpu(lvds_info->info.sLCDTiming.usHSyncWidth); + lvds->native_mode.vtotal = lvds->native_mode.vdisplay + + le16_to_cpu(lvds_info->info.sLCDTiming.usVBlanking_Time); + lvds->native_mode.vsync_start = lvds->native_mode.vdisplay + + le16_to_cpu(lvds_info->info.sLCDTiming.usVSyncWidth); + lvds->native_mode.vsync_end = lvds->native_mode.vsync_start + + le16_to_cpu(lvds_info->info.sLCDTiming.usVSyncWidth); lvds->panel_pwr_delay = le16_to_cpu(lvds_info->info.usOffDelayInMs); lvds->lvds_misc = lvds_info->info.ucLVDS_Misc; + /* set crtc values */ + drm_mode_set_crtcinfo(&lvds->native_mode, CRTC_INTERLACE_HALVE_V); + + lvds->ss = radeon_atombios_get_ss_info(encoder, lvds_info->info.ucSS_Id); encoder->native_mode = lvds->native_mode; } @@ -860,8 +982,7 @@ radeon_atombios_get_primary_dac_info(struct radeon_encoder *encoder) } bool radeon_atom_get_tv_timings(struct radeon_device *rdev, int index, - SET_CRTC_TIMING_PARAMETERS_PS_ALLOCATION *crtc_timing, - int32_t *pixel_clock) + struct drm_display_mode *mode) { struct radeon_mode_info *mode_info = &rdev->mode_info; ATOM_ANALOG_TV_INFO *tv_info; @@ -869,7 +990,7 @@ bool radeon_atom_get_tv_timings(struct radeon_device *rdev, int index, ATOM_DTD_FORMAT *dtd_timings; int data_index = GetIndexIntoMasterTable(DATA, AnalogTV_Info); u8 frev, crev; - uint16_t data_offset; + u16 data_offset, misc; atom_parse_data_header(mode_info->atom_context, data_index, NULL, &frev, &crev, &data_offset); @@ -879,28 +1000,37 @@ bool radeon_atom_get_tv_timings(struct radeon_device *rdev, int index, if (index > MAX_SUPPORTED_TV_TIMING) return false; - crtc_timing->usH_Total = le16_to_cpu(tv_info->aModeTimings[index].usCRTC_H_Total); - crtc_timing->usH_Disp = le16_to_cpu(tv_info->aModeTimings[index].usCRTC_H_Disp); - crtc_timing->usH_SyncStart = le16_to_cpu(tv_info->aModeTimings[index].usCRTC_H_SyncStart); - crtc_timing->usH_SyncWidth = le16_to_cpu(tv_info->aModeTimings[index].usCRTC_H_SyncWidth); - - crtc_timing->usV_Total = le16_to_cpu(tv_info->aModeTimings[index].usCRTC_V_Total); - crtc_timing->usV_Disp = le16_to_cpu(tv_info->aModeTimings[index].usCRTC_V_Disp); - crtc_timing->usV_SyncStart = le16_to_cpu(tv_info->aModeTimings[index].usCRTC_V_SyncStart); - crtc_timing->usV_SyncWidth = le16_to_cpu(tv_info->aModeTimings[index].usCRTC_V_SyncWidth); - - crtc_timing->susModeMiscInfo = tv_info->aModeTimings[index].susModeMiscInfo; - - crtc_timing->ucOverscanRight = le16_to_cpu(tv_info->aModeTimings[index].usCRTC_OverscanRight); - crtc_timing->ucOverscanLeft = le16_to_cpu(tv_info->aModeTimings[index].usCRTC_OverscanLeft); - crtc_timing->ucOverscanBottom = le16_to_cpu(tv_info->aModeTimings[index].usCRTC_OverscanBottom); - crtc_timing->ucOverscanTop = le16_to_cpu(tv_info->aModeTimings[index].usCRTC_OverscanTop); - *pixel_clock = le16_to_cpu(tv_info->aModeTimings[index].usPixelClock) * 10; + mode->crtc_htotal = le16_to_cpu(tv_info->aModeTimings[index].usCRTC_H_Total); + mode->crtc_hdisplay = le16_to_cpu(tv_info->aModeTimings[index].usCRTC_H_Disp); + mode->crtc_hsync_start = le16_to_cpu(tv_info->aModeTimings[index].usCRTC_H_SyncStart); + mode->crtc_hsync_end = le16_to_cpu(tv_info->aModeTimings[index].usCRTC_H_SyncStart) + + le16_to_cpu(tv_info->aModeTimings[index].usCRTC_H_SyncWidth); + + mode->crtc_vtotal = le16_to_cpu(tv_info->aModeTimings[index].usCRTC_V_Total); + mode->crtc_vdisplay = le16_to_cpu(tv_info->aModeTimings[index].usCRTC_V_Disp); + mode->crtc_vsync_start = le16_to_cpu(tv_info->aModeTimings[index].usCRTC_V_SyncStart); + mode->crtc_vsync_end = le16_to_cpu(tv_info->aModeTimings[index].usCRTC_V_SyncStart) + + le16_to_cpu(tv_info->aModeTimings[index].usCRTC_V_SyncWidth); + + mode->flags = 0; + misc = le16_to_cpu(tv_info->aModeTimings[index].susModeMiscInfo.usAccess); + if (misc & ATOM_VSYNC_POLARITY) + mode->flags |= DRM_MODE_FLAG_NVSYNC; + if (misc & ATOM_HSYNC_POLARITY) + mode->flags |= DRM_MODE_FLAG_NHSYNC; + if (misc & ATOM_COMPOSITESYNC) + mode->flags |= DRM_MODE_FLAG_CSYNC; + if (misc & ATOM_INTERLACE) + mode->flags |= DRM_MODE_FLAG_INTERLACE; + if (misc & ATOM_DOUBLE_CLOCK_MODE) + mode->flags |= DRM_MODE_FLAG_DBLSCAN; + + mode->clock = le16_to_cpu(tv_info->aModeTimings[index].usPixelClock) * 10; if (index == 1) { /* PAL timings appear to have wrong values for totals */ - crtc_timing->usH_Total -= 1; - crtc_timing->usV_Total -= 1; + mode->crtc_htotal -= 1; + mode->crtc_vtotal -= 1; } break; case 2: @@ -909,17 +1039,36 @@ bool radeon_atom_get_tv_timings(struct radeon_device *rdev, int index, return false; dtd_timings = &tv_info_v1_2->aModeTimings[index]; - crtc_timing->usH_Total = le16_to_cpu(dtd_timings->usHActive) + le16_to_cpu(dtd_timings->usHBlanking_Time); - crtc_timing->usH_Disp = le16_to_cpu(dtd_timings->usHActive); - crtc_timing->usH_SyncStart = le16_to_cpu(dtd_timings->usHActive) + le16_to_cpu(dtd_timings->usHSyncOffset); - crtc_timing->usH_SyncWidth = le16_to_cpu(dtd_timings->usHSyncWidth); - crtc_timing->usV_Total = le16_to_cpu(dtd_timings->usVActive) + le16_to_cpu(dtd_timings->usVBlanking_Time); - crtc_timing->usV_Disp = le16_to_cpu(dtd_timings->usVActive); - crtc_timing->usV_SyncStart = le16_to_cpu(dtd_timings->usVActive) + le16_to_cpu(dtd_timings->usVSyncOffset); - crtc_timing->usV_SyncWidth = le16_to_cpu(dtd_timings->usVSyncWidth); - - crtc_timing->susModeMiscInfo.usAccess = le16_to_cpu(dtd_timings->susModeMiscInfo.usAccess); - *pixel_clock = le16_to_cpu(dtd_timings->usPixClk) * 10; + mode->crtc_htotal = le16_to_cpu(dtd_timings->usHActive) + + le16_to_cpu(dtd_timings->usHBlanking_Time); + mode->crtc_hdisplay = le16_to_cpu(dtd_timings->usHActive); + mode->crtc_hsync_start = le16_to_cpu(dtd_timings->usHActive) + + le16_to_cpu(dtd_timings->usHSyncOffset); + mode->crtc_hsync_end = mode->crtc_hsync_start + + le16_to_cpu(dtd_timings->usHSyncWidth); + + mode->crtc_vtotal = le16_to_cpu(dtd_timings->usVActive) + + le16_to_cpu(dtd_timings->usVBlanking_Time); + mode->crtc_vdisplay = le16_to_cpu(dtd_timings->usVActive); + mode->crtc_vsync_start = le16_to_cpu(dtd_timings->usVActive) + + le16_to_cpu(dtd_timings->usVSyncOffset); + mode->crtc_vsync_end = mode->crtc_vsync_start + + le16_to_cpu(dtd_timings->usVSyncWidth); + + mode->flags = 0; + misc = le16_to_cpu(dtd_timings->susModeMiscInfo.usAccess); + if (misc & ATOM_VSYNC_POLARITY) + mode->flags |= DRM_MODE_FLAG_NVSYNC; + if (misc & ATOM_HSYNC_POLARITY) + mode->flags |= DRM_MODE_FLAG_NHSYNC; + if (misc & ATOM_COMPOSITESYNC) + mode->flags |= DRM_MODE_FLAG_CSYNC; + if (misc & ATOM_INTERLACE) + mode->flags |= DRM_MODE_FLAG_INTERLACE; + if (misc & ATOM_DOUBLE_CLOCK_MODE) + mode->flags |= DRM_MODE_FLAG_DBLSCAN; + + mode->clock = le16_to_cpu(dtd_timings->usPixClk) * 10; break; } return true; @@ -984,6 +1133,24 @@ void radeon_atom_static_pwrmgt_setup(struct radeon_device *rdev, int enable) atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args); } +uint32_t radeon_atom_get_engine_clock(struct radeon_device *rdev) +{ + GET_ENGINE_CLOCK_PS_ALLOCATION args; + int index = GetIndexIntoMasterTable(COMMAND, GetEngineClock); + + atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args); + return args.ulReturnEngineClock; +} + +uint32_t radeon_atom_get_memory_clock(struct radeon_device *rdev) +{ + GET_MEMORY_CLOCK_PS_ALLOCATION args; + int index = GetIndexIntoMasterTable(COMMAND, GetMemoryClock); + + atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args); + return args.ulReturnMemoryClock; +} + void radeon_atom_set_engine_clock(struct radeon_device *rdev, uint32_t eng_clock) { diff --git a/drivers/gpu/drm/radeon/radeon_benchmark.c b/drivers/gpu/drm/radeon/radeon_benchmark.c index 2e938f7496fb..10bd50a7db87 100644 --- a/drivers/gpu/drm/radeon/radeon_benchmark.c +++ b/drivers/gpu/drm/radeon/radeon_benchmark.c @@ -63,7 +63,7 @@ void radeon_benchmark_move(struct radeon_device *rdev, unsigned bsize, if (r) { goto out_cleanup; } - r = radeon_copy_dma(rdev, saddr, daddr, size / 4096, fence); + r = radeon_copy_dma(rdev, saddr, daddr, size / RADEON_GPU_PAGE_SIZE, fence); if (r) { goto out_cleanup; } @@ -88,7 +88,7 @@ void radeon_benchmark_move(struct radeon_device *rdev, unsigned bsize, if (r) { goto out_cleanup; } - r = radeon_copy_blit(rdev, saddr, daddr, size / 4096, fence); + r = radeon_copy_blit(rdev, saddr, daddr, size / RADEON_GPU_PAGE_SIZE, fence); if (r) { goto out_cleanup; } diff --git a/drivers/gpu/drm/radeon/radeon_bios.c b/drivers/gpu/drm/radeon/radeon_bios.c index 96e37a6e7ce4..906921740c60 100644 --- a/drivers/gpu/drm/radeon/radeon_bios.c +++ b/drivers/gpu/drm/radeon/radeon_bios.c @@ -33,12 +33,47 @@ /* * BIOS. */ + +/* If you boot an IGP board with a discrete card as the primary, + * the IGP rom is not accessible via the rom bar as the IGP rom is + * part of the system bios. On boot, the system bios puts a + * copy of the igp rom at the start of vram if a discrete card is + * present. + */ +static bool igp_read_bios_from_vram(struct radeon_device *rdev) +{ + uint8_t __iomem *bios; + resource_size_t vram_base; + resource_size_t size = 256 * 1024; /* ??? */ + + rdev->bios = NULL; + vram_base = drm_get_resource_start(rdev->ddev, 0); + bios = ioremap(vram_base, size); + if (!bios) { + return false; + } + + if (size == 0 || bios[0] != 0x55 || bios[1] != 0xaa) { + iounmap(bios); + return false; + } + rdev->bios = kmalloc(size, GFP_KERNEL); + if (rdev->bios == NULL) { + iounmap(bios); + return false; + } + memcpy(rdev->bios, bios, size); + iounmap(bios); + return true; +} + static bool radeon_read_bios(struct radeon_device *rdev) { uint8_t __iomem *bios; size_t size; rdev->bios = NULL; + /* XXX: some cards may return 0 for rom size? ddx has a workaround */ bios = pci_map_rom(rdev->pdev, &size); if (!bios) { return false; @@ -341,7 +376,9 @@ static bool legacy_read_disabled_bios(struct radeon_device *rdev) static bool radeon_read_disabled_bios(struct radeon_device *rdev) { - if (rdev->family >= CHIP_RV770) + if (rdev->flags & RADEON_IS_IGP) + return igp_read_bios_from_vram(rdev); + else if (rdev->family >= CHIP_RV770) return r700_read_disabled_bios(rdev); else if (rdev->family >= CHIP_R600) return r600_read_disabled_bios(rdev); @@ -356,7 +393,12 @@ bool radeon_get_bios(struct radeon_device *rdev) bool r; uint16_t tmp; - r = radeon_read_bios(rdev); + if (rdev->flags & RADEON_IS_IGP) { + r = igp_read_bios_from_vram(rdev); + if (r == false) + r = radeon_read_bios(rdev); + } else + r = radeon_read_bios(rdev); if (r == false) { r = radeon_read_disabled_bios(rdev); } diff --git a/drivers/gpu/drm/radeon/radeon_clocks.c b/drivers/gpu/drm/radeon/radeon_clocks.c index 152eef13197a..a81354167621 100644 --- a/drivers/gpu/drm/radeon/radeon_clocks.c +++ b/drivers/gpu/drm/radeon/radeon_clocks.c @@ -32,7 +32,7 @@ #include "atom.h" /* 10 khz */ -static uint32_t radeon_legacy_get_engine_clock(struct radeon_device *rdev) +uint32_t radeon_legacy_get_engine_clock(struct radeon_device *rdev) { struct radeon_pll *spll = &rdev->clock.spll; uint32_t fb_div, ref_div, post_div, sclk; @@ -411,7 +411,7 @@ void radeon_legacy_set_clock_gating(struct radeon_device *rdev, int enable) R300_PIXCLK_TRANS_ALWAYS_ONb | R300_PIXCLK_TVO_ALWAYS_ONb | R300_P2G2CLK_ALWAYS_ONb | - R300_P2G2CLK_ALWAYS_ONb); + R300_P2G2CLK_DAC_ALWAYS_ONb); WREG32_PLL(RADEON_PIXCLKS_CNTL, tmp); } else if (rdev->family >= CHIP_RV350) { tmp = RREG32_PLL(R300_SCLK_CNTL2); @@ -464,7 +464,7 @@ void radeon_legacy_set_clock_gating(struct radeon_device *rdev, int enable) R300_PIXCLK_TRANS_ALWAYS_ONb | R300_PIXCLK_TVO_ALWAYS_ONb | R300_P2G2CLK_ALWAYS_ONb | - R300_P2G2CLK_ALWAYS_ONb); + R300_P2G2CLK_DAC_ALWAYS_ONb); WREG32_PLL(RADEON_PIXCLKS_CNTL, tmp); tmp = RREG32_PLL(RADEON_MCLK_MISC); @@ -654,7 +654,7 @@ void radeon_legacy_set_clock_gating(struct radeon_device *rdev, int enable) R300_PIXCLK_TRANS_ALWAYS_ONb | R300_PIXCLK_TVO_ALWAYS_ONb | R300_P2G2CLK_ALWAYS_ONb | - R300_P2G2CLK_ALWAYS_ONb | + R300_P2G2CLK_DAC_ALWAYS_ONb | R300_DISP_DAC_PIXCLK_DAC2_BLANK_OFF); WREG32_PLL(RADEON_PIXCLKS_CNTL, tmp); } else if (rdev->family >= CHIP_RV350) { @@ -705,7 +705,7 @@ void radeon_legacy_set_clock_gating(struct radeon_device *rdev, int enable) R300_PIXCLK_TRANS_ALWAYS_ONb | R300_PIXCLK_TVO_ALWAYS_ONb | R300_P2G2CLK_ALWAYS_ONb | - R300_P2G2CLK_ALWAYS_ONb | + R300_P2G2CLK_DAC_ALWAYS_ONb | R300_DISP_DAC_PIXCLK_DAC2_BLANK_OFF); WREG32_PLL(RADEON_PIXCLKS_CNTL, tmp); } else { diff --git a/drivers/gpu/drm/radeon/radeon_combios.c b/drivers/gpu/drm/radeon/radeon_combios.c index 748265a105b3..5253cbf6db1f 100644 --- a/drivers/gpu/drm/radeon/radeon_combios.c +++ b/drivers/gpu/drm/radeon/radeon_combios.c @@ -49,7 +49,8 @@ radeon_add_legacy_connector(struct drm_device *dev, uint32_t connector_id, uint32_t supported_device, int connector_type, - struct radeon_i2c_bus_rec *i2c_bus); + struct radeon_i2c_bus_rec *i2c_bus, + uint16_t connector_object_id); /* from radeon_legacy_encoder.c */ extern void @@ -808,25 +809,25 @@ static struct radeon_encoder_lvds *radeon_legacy_get_lvds_info_from_regs(struct lvds->panel_blon_delay = (lvds_ss_gen_cntl >> RADEON_LVDS_PWRSEQ_DELAY2_SHIFT) & 0xf; if (fp_vert_stretch & RADEON_VERT_STRETCH_ENABLE) - lvds->native_mode.panel_yres = + lvds->native_mode.vdisplay = ((fp_vert_stretch & RADEON_VERT_PANEL_SIZE) >> RADEON_VERT_PANEL_SHIFT) + 1; else - lvds->native_mode.panel_yres = + lvds->native_mode.vdisplay = (RREG32(RADEON_CRTC_V_TOTAL_DISP) >> 16) + 1; if (fp_horz_stretch & RADEON_HORZ_STRETCH_ENABLE) - lvds->native_mode.panel_xres = + lvds->native_mode.hdisplay = (((fp_horz_stretch & RADEON_HORZ_PANEL_SIZE) >> RADEON_HORZ_PANEL_SHIFT) + 1) * 8; else - lvds->native_mode.panel_xres = + lvds->native_mode.hdisplay = ((RREG32(RADEON_CRTC_H_TOTAL_DISP) >> 16) + 1) * 8; - if ((lvds->native_mode.panel_xres < 640) || - (lvds->native_mode.panel_yres < 480)) { - lvds->native_mode.panel_xres = 640; - lvds->native_mode.panel_yres = 480; + if ((lvds->native_mode.hdisplay < 640) || + (lvds->native_mode.vdisplay < 480)) { + lvds->native_mode.hdisplay = 640; + lvds->native_mode.vdisplay = 480; } ppll_div_sel = RREG8(RADEON_CLOCK_CNTL_INDEX + 1) & 0x3; @@ -846,8 +847,8 @@ static struct radeon_encoder_lvds *radeon_legacy_get_lvds_info_from_regs(struct lvds->panel_vcc_delay = 200; DRM_INFO("Panel info derived from registers\n"); - DRM_INFO("Panel Size %dx%d\n", lvds->native_mode.panel_xres, - lvds->native_mode.panel_yres); + DRM_INFO("Panel Size %dx%d\n", lvds->native_mode.hdisplay, + lvds->native_mode.vdisplay); return lvds; } @@ -882,11 +883,11 @@ struct radeon_encoder_lvds *radeon_combios_get_lvds_info(struct radeon_encoder DRM_INFO("Panel ID String: %s\n", stmp); - lvds->native_mode.panel_xres = RBIOS16(lcd_info + 0x19); - lvds->native_mode.panel_yres = RBIOS16(lcd_info + 0x1b); + lvds->native_mode.hdisplay = RBIOS16(lcd_info + 0x19); + lvds->native_mode.vdisplay = RBIOS16(lcd_info + 0x1b); - DRM_INFO("Panel Size %dx%d\n", lvds->native_mode.panel_xres, - lvds->native_mode.panel_yres); + DRM_INFO("Panel Size %dx%d\n", lvds->native_mode.hdisplay, + lvds->native_mode.vdisplay); lvds->panel_vcc_delay = RBIOS16(lcd_info + 0x2c); if (lvds->panel_vcc_delay > 2000 || lvds->panel_vcc_delay < 0) @@ -944,27 +945,25 @@ struct radeon_encoder_lvds *radeon_combios_get_lvds_info(struct radeon_encoder if (tmp == 0) break; - if ((RBIOS16(tmp) == lvds->native_mode.panel_xres) && + if ((RBIOS16(tmp) == lvds->native_mode.hdisplay) && (RBIOS16(tmp + 2) == - lvds->native_mode.panel_yres)) { - lvds->native_mode.hblank = - (RBIOS16(tmp + 17) - RBIOS16(tmp + 19)) * 8; - lvds->native_mode.hoverplus = - (RBIOS16(tmp + 21) - RBIOS16(tmp + 19) - - 1) * 8; - lvds->native_mode.hsync_width = - RBIOS8(tmp + 23) * 8; - - lvds->native_mode.vblank = (RBIOS16(tmp + 24) - - RBIOS16(tmp + 26)); - lvds->native_mode.voverplus = - ((RBIOS16(tmp + 28) & 0x7ff) - - RBIOS16(tmp + 26)); - lvds->native_mode.vsync_width = - ((RBIOS16(tmp + 28) & 0xf800) >> 11); - lvds->native_mode.dotclock = - RBIOS16(tmp + 9) * 10; + lvds->native_mode.vdisplay)) { + lvds->native_mode.htotal = RBIOS16(tmp + 17) * 8; + lvds->native_mode.hsync_start = RBIOS16(tmp + 21) * 8; + lvds->native_mode.hsync_end = (RBIOS8(tmp + 23) + + RBIOS16(tmp + 21)) * 8; + + lvds->native_mode.vtotal = RBIOS16(tmp + 24); + lvds->native_mode.vsync_start = RBIOS16(tmp + 28) & 0x7ff; + lvds->native_mode.vsync_end = + ((RBIOS16(tmp + 28) & 0xf800) >> 11) + + (RBIOS16(tmp + 28) & 0x7ff); + + lvds->native_mode.clock = RBIOS16(tmp + 9) * 10; lvds->native_mode.flags = 0; + /* set crtc values */ + drm_mode_set_crtcinfo(&lvds->native_mode, CRTC_INTERLACE_HALVE_V); + } } } else { @@ -1178,7 +1177,8 @@ bool radeon_get_legacy_connector_info_from_table(struct drm_device *dev) radeon_add_legacy_connector(dev, 0, ATOM_DEVICE_CRT1_SUPPORT, DRM_MODE_CONNECTOR_VGA, - &ddc_i2c); + &ddc_i2c, + CONNECTOR_OBJECT_ID_VGA); } else if (rdev->flags & RADEON_IS_MOBILITY) { /* LVDS */ ddc_i2c = combios_setup_i2c_bus(RADEON_LCD_GPIO_MASK); @@ -1190,7 +1190,8 @@ bool radeon_get_legacy_connector_info_from_table(struct drm_device *dev) radeon_add_legacy_connector(dev, 0, ATOM_DEVICE_LCD1_SUPPORT, DRM_MODE_CONNECTOR_LVDS, - &ddc_i2c); + &ddc_i2c, + CONNECTOR_OBJECT_ID_LVDS); /* VGA - primary dac */ ddc_i2c = combios_setup_i2c_bus(RADEON_GPIO_VGA_DDC); @@ -1202,7 +1203,8 @@ bool radeon_get_legacy_connector_info_from_table(struct drm_device *dev) radeon_add_legacy_connector(dev, 1, ATOM_DEVICE_CRT1_SUPPORT, DRM_MODE_CONNECTOR_VGA, - &ddc_i2c); + &ddc_i2c, + CONNECTOR_OBJECT_ID_VGA); } else { /* DVI-I - tv dac, int tmds */ ddc_i2c = combios_setup_i2c_bus(RADEON_GPIO_DVI_DDC); @@ -1220,7 +1222,8 @@ bool radeon_get_legacy_connector_info_from_table(struct drm_device *dev) ATOM_DEVICE_DFP1_SUPPORT | ATOM_DEVICE_CRT2_SUPPORT, DRM_MODE_CONNECTOR_DVII, - &ddc_i2c); + &ddc_i2c, + CONNECTOR_OBJECT_ID_SINGLE_LINK_DVI_I); /* VGA - primary dac */ ddc_i2c = combios_setup_i2c_bus(RADEON_GPIO_VGA_DDC); @@ -1232,7 +1235,8 @@ bool radeon_get_legacy_connector_info_from_table(struct drm_device *dev) radeon_add_legacy_connector(dev, 1, ATOM_DEVICE_CRT1_SUPPORT, DRM_MODE_CONNECTOR_VGA, - &ddc_i2c); + &ddc_i2c, + CONNECTOR_OBJECT_ID_VGA); } if (rdev->family != CHIP_R100 && rdev->family != CHIP_R200) { @@ -1245,7 +1249,8 @@ bool radeon_get_legacy_connector_info_from_table(struct drm_device *dev) radeon_add_legacy_connector(dev, 2, ATOM_DEVICE_TV1_SUPPORT, DRM_MODE_CONNECTOR_SVIDEO, - &ddc_i2c); + &ddc_i2c, + CONNECTOR_OBJECT_ID_SVIDEO); } break; case CT_IBOOK: @@ -1259,7 +1264,8 @@ bool radeon_get_legacy_connector_info_from_table(struct drm_device *dev) 0), ATOM_DEVICE_LCD1_SUPPORT); radeon_add_legacy_connector(dev, 0, ATOM_DEVICE_LCD1_SUPPORT, - DRM_MODE_CONNECTOR_LVDS, &ddc_i2c); + DRM_MODE_CONNECTOR_LVDS, &ddc_i2c, + CONNECTOR_OBJECT_ID_LVDS); /* VGA - TV DAC */ ddc_i2c = combios_setup_i2c_bus(RADEON_GPIO_VGA_DDC); radeon_add_legacy_encoder(dev, @@ -1268,7 +1274,8 @@ bool radeon_get_legacy_connector_info_from_table(struct drm_device *dev) 2), ATOM_DEVICE_CRT2_SUPPORT); radeon_add_legacy_connector(dev, 1, ATOM_DEVICE_CRT2_SUPPORT, - DRM_MODE_CONNECTOR_VGA, &ddc_i2c); + DRM_MODE_CONNECTOR_VGA, &ddc_i2c, + CONNECTOR_OBJECT_ID_VGA); /* TV - TV DAC */ radeon_add_legacy_encoder(dev, radeon_get_encoder_id(dev, @@ -1277,7 +1284,8 @@ bool radeon_get_legacy_connector_info_from_table(struct drm_device *dev) ATOM_DEVICE_TV1_SUPPORT); radeon_add_legacy_connector(dev, 2, ATOM_DEVICE_TV1_SUPPORT, DRM_MODE_CONNECTOR_SVIDEO, - &ddc_i2c); + &ddc_i2c, + CONNECTOR_OBJECT_ID_SVIDEO); break; case CT_POWERBOOK_EXTERNAL: DRM_INFO("Connector Table: %d (powerbook external tmds)\n", @@ -1290,7 +1298,8 @@ bool radeon_get_legacy_connector_info_from_table(struct drm_device *dev) 0), ATOM_DEVICE_LCD1_SUPPORT); radeon_add_legacy_connector(dev, 0, ATOM_DEVICE_LCD1_SUPPORT, - DRM_MODE_CONNECTOR_LVDS, &ddc_i2c); + DRM_MODE_CONNECTOR_LVDS, &ddc_i2c, + CONNECTOR_OBJECT_ID_LVDS); /* DVI-I - primary dac, ext tmds */ ddc_i2c = combios_setup_i2c_bus(RADEON_GPIO_VGA_DDC); radeon_add_legacy_encoder(dev, @@ -1303,10 +1312,12 @@ bool radeon_get_legacy_connector_info_from_table(struct drm_device *dev) ATOM_DEVICE_CRT1_SUPPORT, 1), ATOM_DEVICE_CRT1_SUPPORT); + /* XXX some are SL */ radeon_add_legacy_connector(dev, 1, ATOM_DEVICE_DFP2_SUPPORT | ATOM_DEVICE_CRT1_SUPPORT, - DRM_MODE_CONNECTOR_DVII, &ddc_i2c); + DRM_MODE_CONNECTOR_DVII, &ddc_i2c, + CONNECTOR_OBJECT_ID_DUAL_LINK_DVI_I); /* TV - TV DAC */ radeon_add_legacy_encoder(dev, radeon_get_encoder_id(dev, @@ -1315,7 +1326,8 @@ bool radeon_get_legacy_connector_info_from_table(struct drm_device *dev) ATOM_DEVICE_TV1_SUPPORT); radeon_add_legacy_connector(dev, 2, ATOM_DEVICE_TV1_SUPPORT, DRM_MODE_CONNECTOR_SVIDEO, - &ddc_i2c); + &ddc_i2c, + CONNECTOR_OBJECT_ID_SVIDEO); break; case CT_POWERBOOK_INTERNAL: DRM_INFO("Connector Table: %d (powerbook internal tmds)\n", @@ -1328,7 +1340,8 @@ bool radeon_get_legacy_connector_info_from_table(struct drm_device *dev) 0), ATOM_DEVICE_LCD1_SUPPORT); radeon_add_legacy_connector(dev, 0, ATOM_DEVICE_LCD1_SUPPORT, - DRM_MODE_CONNECTOR_LVDS, &ddc_i2c); + DRM_MODE_CONNECTOR_LVDS, &ddc_i2c, + CONNECTOR_OBJECT_ID_LVDS); /* DVI-I - primary dac, int tmds */ ddc_i2c = combios_setup_i2c_bus(RADEON_GPIO_VGA_DDC); radeon_add_legacy_encoder(dev, @@ -1344,7 +1357,8 @@ bool radeon_get_legacy_connector_info_from_table(struct drm_device *dev) radeon_add_legacy_connector(dev, 1, ATOM_DEVICE_DFP1_SUPPORT | ATOM_DEVICE_CRT1_SUPPORT, - DRM_MODE_CONNECTOR_DVII, &ddc_i2c); + DRM_MODE_CONNECTOR_DVII, &ddc_i2c, + CONNECTOR_OBJECT_ID_SINGLE_LINK_DVI_I); /* TV - TV DAC */ radeon_add_legacy_encoder(dev, radeon_get_encoder_id(dev, @@ -1353,7 +1367,8 @@ bool radeon_get_legacy_connector_info_from_table(struct drm_device *dev) ATOM_DEVICE_TV1_SUPPORT); radeon_add_legacy_connector(dev, 2, ATOM_DEVICE_TV1_SUPPORT, DRM_MODE_CONNECTOR_SVIDEO, - &ddc_i2c); + &ddc_i2c, + CONNECTOR_OBJECT_ID_SVIDEO); break; case CT_POWERBOOK_VGA: DRM_INFO("Connector Table: %d (powerbook vga)\n", @@ -1366,7 +1381,8 @@ bool radeon_get_legacy_connector_info_from_table(struct drm_device *dev) 0), ATOM_DEVICE_LCD1_SUPPORT); radeon_add_legacy_connector(dev, 0, ATOM_DEVICE_LCD1_SUPPORT, - DRM_MODE_CONNECTOR_LVDS, &ddc_i2c); + DRM_MODE_CONNECTOR_LVDS, &ddc_i2c, + CONNECTOR_OBJECT_ID_LVDS); /* VGA - primary dac */ ddc_i2c = combios_setup_i2c_bus(RADEON_GPIO_VGA_DDC); radeon_add_legacy_encoder(dev, @@ -1375,7 +1391,8 @@ bool radeon_get_legacy_connector_info_from_table(struct drm_device *dev) 1), ATOM_DEVICE_CRT1_SUPPORT); radeon_add_legacy_connector(dev, 1, ATOM_DEVICE_CRT1_SUPPORT, - DRM_MODE_CONNECTOR_VGA, &ddc_i2c); + DRM_MODE_CONNECTOR_VGA, &ddc_i2c, + CONNECTOR_OBJECT_ID_VGA); /* TV - TV DAC */ radeon_add_legacy_encoder(dev, radeon_get_encoder_id(dev, @@ -1384,7 +1401,8 @@ bool radeon_get_legacy_connector_info_from_table(struct drm_device *dev) ATOM_DEVICE_TV1_SUPPORT); radeon_add_legacy_connector(dev, 2, ATOM_DEVICE_TV1_SUPPORT, DRM_MODE_CONNECTOR_SVIDEO, - &ddc_i2c); + &ddc_i2c, + CONNECTOR_OBJECT_ID_SVIDEO); break; case CT_MINI_EXTERNAL: DRM_INFO("Connector Table: %d (mini external tmds)\n", @@ -1401,10 +1419,12 @@ bool radeon_get_legacy_connector_info_from_table(struct drm_device *dev) ATOM_DEVICE_CRT2_SUPPORT, 2), ATOM_DEVICE_CRT2_SUPPORT); + /* XXX are any DL? */ radeon_add_legacy_connector(dev, 0, ATOM_DEVICE_DFP2_SUPPORT | ATOM_DEVICE_CRT2_SUPPORT, - DRM_MODE_CONNECTOR_DVII, &ddc_i2c); + DRM_MODE_CONNECTOR_DVII, &ddc_i2c, + CONNECTOR_OBJECT_ID_SINGLE_LINK_DVI_I); /* TV - TV DAC */ radeon_add_legacy_encoder(dev, radeon_get_encoder_id(dev, @@ -1413,7 +1433,8 @@ bool radeon_get_legacy_connector_info_from_table(struct drm_device *dev) ATOM_DEVICE_TV1_SUPPORT); radeon_add_legacy_connector(dev, 1, ATOM_DEVICE_TV1_SUPPORT, DRM_MODE_CONNECTOR_SVIDEO, - &ddc_i2c); + &ddc_i2c, + CONNECTOR_OBJECT_ID_SVIDEO); break; case CT_MINI_INTERNAL: DRM_INFO("Connector Table: %d (mini internal tmds)\n", @@ -1433,7 +1454,8 @@ bool radeon_get_legacy_connector_info_from_table(struct drm_device *dev) radeon_add_legacy_connector(dev, 0, ATOM_DEVICE_DFP1_SUPPORT | ATOM_DEVICE_CRT2_SUPPORT, - DRM_MODE_CONNECTOR_DVII, &ddc_i2c); + DRM_MODE_CONNECTOR_DVII, &ddc_i2c, + CONNECTOR_OBJECT_ID_SINGLE_LINK_DVI_I); /* TV - TV DAC */ radeon_add_legacy_encoder(dev, radeon_get_encoder_id(dev, @@ -1442,7 +1464,8 @@ bool radeon_get_legacy_connector_info_from_table(struct drm_device *dev) ATOM_DEVICE_TV1_SUPPORT); radeon_add_legacy_connector(dev, 1, ATOM_DEVICE_TV1_SUPPORT, DRM_MODE_CONNECTOR_SVIDEO, - &ddc_i2c); + &ddc_i2c, + CONNECTOR_OBJECT_ID_SVIDEO); break; case CT_IMAC_G5_ISIGHT: DRM_INFO("Connector Table: %d (imac g5 isight)\n", @@ -1455,7 +1478,8 @@ bool radeon_get_legacy_connector_info_from_table(struct drm_device *dev) 0), ATOM_DEVICE_DFP1_SUPPORT); radeon_add_legacy_connector(dev, 0, ATOM_DEVICE_DFP1_SUPPORT, - DRM_MODE_CONNECTOR_DVID, &ddc_i2c); + DRM_MODE_CONNECTOR_DVID, &ddc_i2c, + CONNECTOR_OBJECT_ID_SINGLE_LINK_DVI_D); /* VGA - tv dac */ ddc_i2c = combios_setup_i2c_bus(RADEON_GPIO_DVI_DDC); radeon_add_legacy_encoder(dev, @@ -1464,7 +1488,8 @@ bool radeon_get_legacy_connector_info_from_table(struct drm_device *dev) 2), ATOM_DEVICE_CRT2_SUPPORT); radeon_add_legacy_connector(dev, 1, ATOM_DEVICE_CRT2_SUPPORT, - DRM_MODE_CONNECTOR_VGA, &ddc_i2c); + DRM_MODE_CONNECTOR_VGA, &ddc_i2c, + CONNECTOR_OBJECT_ID_VGA); /* TV - TV DAC */ radeon_add_legacy_encoder(dev, radeon_get_encoder_id(dev, @@ -1473,7 +1498,8 @@ bool radeon_get_legacy_connector_info_from_table(struct drm_device *dev) ATOM_DEVICE_TV1_SUPPORT); radeon_add_legacy_connector(dev, 2, ATOM_DEVICE_TV1_SUPPORT, DRM_MODE_CONNECTOR_SVIDEO, - &ddc_i2c); + &ddc_i2c, + CONNECTOR_OBJECT_ID_SVIDEO); break; case CT_EMAC: DRM_INFO("Connector Table: %d (emac)\n", @@ -1486,7 +1512,8 @@ bool radeon_get_legacy_connector_info_from_table(struct drm_device *dev) 1), ATOM_DEVICE_CRT1_SUPPORT); radeon_add_legacy_connector(dev, 0, ATOM_DEVICE_CRT1_SUPPORT, - DRM_MODE_CONNECTOR_VGA, &ddc_i2c); + DRM_MODE_CONNECTOR_VGA, &ddc_i2c, + CONNECTOR_OBJECT_ID_VGA); /* VGA - tv dac */ ddc_i2c = combios_setup_i2c_bus(RADEON_GPIO_CRT2_DDC); radeon_add_legacy_encoder(dev, @@ -1495,7 +1522,8 @@ bool radeon_get_legacy_connector_info_from_table(struct drm_device *dev) 2), ATOM_DEVICE_CRT2_SUPPORT); radeon_add_legacy_connector(dev, 1, ATOM_DEVICE_CRT2_SUPPORT, - DRM_MODE_CONNECTOR_VGA, &ddc_i2c); + DRM_MODE_CONNECTOR_VGA, &ddc_i2c, + CONNECTOR_OBJECT_ID_VGA); /* TV - TV DAC */ radeon_add_legacy_encoder(dev, radeon_get_encoder_id(dev, @@ -1504,7 +1532,8 @@ bool radeon_get_legacy_connector_info_from_table(struct drm_device *dev) ATOM_DEVICE_TV1_SUPPORT); radeon_add_legacy_connector(dev, 2, ATOM_DEVICE_TV1_SUPPORT, DRM_MODE_CONNECTOR_SVIDEO, - &ddc_i2c); + &ddc_i2c, + CONNECTOR_OBJECT_ID_SVIDEO); break; default: DRM_INFO("Connector table: %d (invalid)\n", @@ -1581,11 +1610,63 @@ static bool radeon_apply_legacy_quirks(struct drm_device *dev, return true; } +static bool radeon_apply_legacy_tv_quirks(struct drm_device *dev) +{ + /* Acer 5102 has non-existent TV port */ + if (dev->pdev->device == 0x5975 && + dev->pdev->subsystem_vendor == 0x1025 && + dev->pdev->subsystem_device == 0x009f) + return false; + + /* HP dc5750 has non-existent TV port */ + if (dev->pdev->device == 0x5974 && + dev->pdev->subsystem_vendor == 0x103c && + dev->pdev->subsystem_device == 0x280a) + return false; + + return true; +} + +static uint16_t combios_check_dl_dvi(struct drm_device *dev, int is_dvi_d) +{ + struct radeon_device *rdev = dev->dev_private; + uint32_t ext_tmds_info; + + if (rdev->flags & RADEON_IS_IGP) { + if (is_dvi_d) + return CONNECTOR_OBJECT_ID_SINGLE_LINK_DVI_D; + else + return CONNECTOR_OBJECT_ID_SINGLE_LINK_DVI_I; + } + ext_tmds_info = combios_get_table_offset(dev, COMBIOS_EXT_TMDS_INFO_TABLE); + if (ext_tmds_info) { + uint8_t rev = RBIOS8(ext_tmds_info); + uint8_t flags = RBIOS8(ext_tmds_info + 4 + 5); + if (rev >= 3) { + if (is_dvi_d) + return CONNECTOR_OBJECT_ID_DUAL_LINK_DVI_D; + else + return CONNECTOR_OBJECT_ID_DUAL_LINK_DVI_I; + } else { + if (flags & 1) { + if (is_dvi_d) + return CONNECTOR_OBJECT_ID_DUAL_LINK_DVI_D; + else + return CONNECTOR_OBJECT_ID_DUAL_LINK_DVI_I; + } + } + } + if (is_dvi_d) + return CONNECTOR_OBJECT_ID_SINGLE_LINK_DVI_D; + else + return CONNECTOR_OBJECT_ID_SINGLE_LINK_DVI_I; +} + bool radeon_get_legacy_connector_info_from_bios(struct drm_device *dev) { struct radeon_device *rdev = dev->dev_private; uint32_t conn_info, entry, devices; - uint16_t tmp; + uint16_t tmp, connector_object_id; enum radeon_combios_ddc ddc_type; enum radeon_combios_connector connector; int i = 0; @@ -1628,8 +1709,9 @@ bool radeon_get_legacy_connector_info_from_bios(struct drm_device *dev) break; } - radeon_apply_legacy_quirks(dev, i, &connector, - &ddc_i2c); + if (!radeon_apply_legacy_quirks(dev, i, &connector, + &ddc_i2c)) + continue; switch (connector) { case CONNECTOR_PROPRIETARY_LEGACY: @@ -1644,7 +1726,8 @@ bool radeon_get_legacy_connector_info_from_bios(struct drm_device *dev) radeon_add_legacy_connector(dev, i, devices, legacy_connector_convert [connector], - &ddc_i2c); + &ddc_i2c, + CONNECTOR_OBJECT_ID_SINGLE_LINK_DVI_D); break; case CONNECTOR_CRT_LEGACY: if (tmp & 0x1) { @@ -1669,7 +1752,8 @@ bool radeon_get_legacy_connector_info_from_bios(struct drm_device *dev) devices, legacy_connector_convert [connector], - &ddc_i2c); + &ddc_i2c, + CONNECTOR_OBJECT_ID_VGA); break; case CONNECTOR_DVI_I_LEGACY: devices = 0; @@ -1698,6 +1782,7 @@ bool radeon_get_legacy_connector_info_from_bios(struct drm_device *dev) ATOM_DEVICE_DFP2_SUPPORT, 0), ATOM_DEVICE_DFP2_SUPPORT); + connector_object_id = combios_check_dl_dvi(dev, 0); } else { devices |= ATOM_DEVICE_DFP1_SUPPORT; radeon_add_legacy_encoder(dev, @@ -1706,19 +1791,24 @@ bool radeon_get_legacy_connector_info_from_bios(struct drm_device *dev) ATOM_DEVICE_DFP1_SUPPORT, 0), ATOM_DEVICE_DFP1_SUPPORT); + connector_object_id = CONNECTOR_OBJECT_ID_SINGLE_LINK_DVI_I; } radeon_add_legacy_connector(dev, i, devices, legacy_connector_convert [connector], - &ddc_i2c); + &ddc_i2c, + connector_object_id); break; case CONNECTOR_DVI_D_LEGACY: - if ((tmp >> 4) & 0x1) + if ((tmp >> 4) & 0x1) { devices = ATOM_DEVICE_DFP2_SUPPORT; - else + connector_object_id = combios_check_dl_dvi(dev, 1); + } else { devices = ATOM_DEVICE_DFP1_SUPPORT; + connector_object_id = CONNECTOR_OBJECT_ID_SINGLE_LINK_DVI_I; + } radeon_add_legacy_encoder(dev, radeon_get_encoder_id (dev, devices, 0), @@ -1726,7 +1816,8 @@ bool radeon_get_legacy_connector_info_from_bios(struct drm_device *dev) radeon_add_legacy_connector(dev, i, devices, legacy_connector_convert [connector], - &ddc_i2c); + &ddc_i2c, + connector_object_id); break; case CONNECTOR_CTV_LEGACY: case CONNECTOR_STV_LEGACY: @@ -1740,7 +1831,8 @@ bool radeon_get_legacy_connector_info_from_bios(struct drm_device *dev) ATOM_DEVICE_TV1_SUPPORT, legacy_connector_convert [connector], - &ddc_i2c); + &ddc_i2c, + CONNECTOR_OBJECT_ID_SVIDEO); break; default: DRM_ERROR("Unknown connector type: %d\n", @@ -1772,10 +1864,29 @@ bool radeon_get_legacy_connector_info_from_bios(struct drm_device *dev) ATOM_DEVICE_CRT1_SUPPORT | ATOM_DEVICE_DFP1_SUPPORT, DRM_MODE_CONNECTOR_DVII, - &ddc_i2c); + &ddc_i2c, + CONNECTOR_OBJECT_ID_SINGLE_LINK_DVI_I); } else { - DRM_DEBUG("No connector info found\n"); - return false; + uint16_t crt_info = + combios_get_table_offset(dev, COMBIOS_CRT_INFO_TABLE); + DRM_DEBUG("Found CRT table, assuming VGA connector\n"); + if (crt_info) { + radeon_add_legacy_encoder(dev, + radeon_get_encoder_id(dev, + ATOM_DEVICE_CRT1_SUPPORT, + 1), + ATOM_DEVICE_CRT1_SUPPORT); + ddc_i2c = combios_setup_i2c_bus(RADEON_GPIO_VGA_DDC); + radeon_add_legacy_connector(dev, + 0, + ATOM_DEVICE_CRT1_SUPPORT, + DRM_MODE_CONNECTOR_VGA, + &ddc_i2c, + CONNECTOR_OBJECT_ID_VGA); + } else { + DRM_DEBUG("No connector info found\n"); + return false; + } } } @@ -1870,7 +1981,8 @@ bool radeon_get_legacy_connector_info_from_bios(struct drm_device *dev) 5, ATOM_DEVICE_LCD1_SUPPORT, DRM_MODE_CONNECTOR_LVDS, - &ddc_i2c); + &ddc_i2c, + CONNECTOR_OBJECT_ID_LVDS); } } @@ -1880,16 +1992,19 @@ bool radeon_get_legacy_connector_info_from_bios(struct drm_device *dev) combios_get_table_offset(dev, COMBIOS_TV_INFO_TABLE); if (tv_info) { if (RBIOS8(tv_info + 6) == 'T') { - radeon_add_legacy_encoder(dev, - radeon_get_encoder_id - (dev, - ATOM_DEVICE_TV1_SUPPORT, - 2), - ATOM_DEVICE_TV1_SUPPORT); - radeon_add_legacy_connector(dev, 6, - ATOM_DEVICE_TV1_SUPPORT, - DRM_MODE_CONNECTOR_SVIDEO, - &ddc_i2c); + if (radeon_apply_legacy_tv_quirks(dev)) { + radeon_add_legacy_encoder(dev, + radeon_get_encoder_id + (dev, + ATOM_DEVICE_TV1_SUPPORT, + 2), + ATOM_DEVICE_TV1_SUPPORT); + radeon_add_legacy_connector(dev, 6, + ATOM_DEVICE_TV1_SUPPORT, + DRM_MODE_CONNECTOR_SVIDEO, + &ddc_i2c, + CONNECTOR_OBJECT_ID_SVIDEO); + } } } } diff --git a/drivers/gpu/drm/radeon/radeon_connectors.c b/drivers/gpu/drm/radeon/radeon_connectors.c index af1d551f1a8f..29763ceae3af 100644 --- a/drivers/gpu/drm/radeon/radeon_connectors.c +++ b/drivers/gpu/drm/radeon/radeon_connectors.c @@ -26,6 +26,7 @@ #include "drmP.h" #include "drm_edid.h" #include "drm_crtc_helper.h" +#include "drm_fb_helper.h" #include "radeon_drm.h" #include "radeon.h" #include "atom.h" @@ -177,25 +178,12 @@ static struct drm_display_mode *radeon_fp_native_mode(struct drm_encoder *encode struct drm_device *dev = encoder->dev; struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); struct drm_display_mode *mode = NULL; - struct radeon_native_mode *native_mode = &radeon_encoder->native_mode; - - if (native_mode->panel_xres != 0 && - native_mode->panel_yres != 0 && - native_mode->dotclock != 0) { - mode = drm_mode_create(dev); - - mode->hdisplay = native_mode->panel_xres; - mode->vdisplay = native_mode->panel_yres; - - mode->htotal = mode->hdisplay + native_mode->hblank; - mode->hsync_start = mode->hdisplay + native_mode->hoverplus; - mode->hsync_end = mode->hsync_start + native_mode->hsync_width; - mode->vtotal = mode->vdisplay + native_mode->vblank; - mode->vsync_start = mode->vdisplay + native_mode->voverplus; - mode->vsync_end = mode->vsync_start + native_mode->vsync_width; - mode->clock = native_mode->dotclock; - mode->flags = 0; + struct drm_display_mode *native_mode = &radeon_encoder->native_mode; + if (native_mode->hdisplay != 0 && + native_mode->vdisplay != 0 && + native_mode->clock != 0) { + mode = drm_mode_duplicate(dev, native_mode); mode->type = DRM_MODE_TYPE_PREFERRED | DRM_MODE_TYPE_DRIVER; drm_mode_set_name(mode); @@ -209,7 +197,7 @@ static void radeon_add_common_modes(struct drm_encoder *encoder, struct drm_conn struct drm_device *dev = encoder->dev; struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); struct drm_display_mode *mode = NULL; - struct radeon_native_mode *native_mode = &radeon_encoder->native_mode; + struct drm_display_mode *native_mode = &radeon_encoder->native_mode; int i; struct mode_size { int w; @@ -235,17 +223,22 @@ static void radeon_add_common_modes(struct drm_encoder *encoder, struct drm_conn }; for (i = 0; i < 17; i++) { + if (radeon_encoder->devices & (ATOM_DEVICE_TV_SUPPORT)) { + if (common_modes[i].w > 1024 || + common_modes[i].h > 768) + continue; + } if (radeon_encoder->devices & (ATOM_DEVICE_LCD_SUPPORT)) { - if (common_modes[i].w > native_mode->panel_xres || - common_modes[i].h > native_mode->panel_yres || - (common_modes[i].w == native_mode->panel_xres && - common_modes[i].h == native_mode->panel_yres)) + if (common_modes[i].w > native_mode->hdisplay || + common_modes[i].h > native_mode->vdisplay || + (common_modes[i].w == native_mode->hdisplay && + common_modes[i].h == native_mode->vdisplay)) continue; } if (common_modes[i].w < 320 || common_modes[i].h < 200) continue; - mode = drm_cvt_mode(dev, common_modes[i].w, common_modes[i].h, 60, false, false); + mode = drm_cvt_mode(dev, common_modes[i].w, common_modes[i].h, 60, false, false, false); drm_mode_probed_add(connector, mode); } } @@ -343,28 +336,23 @@ static void radeon_fixup_lvds_native_mode(struct drm_encoder *encoder, struct drm_connector *connector) { struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); - struct radeon_native_mode *native_mode = &radeon_encoder->native_mode; + struct drm_display_mode *native_mode = &radeon_encoder->native_mode; /* Try to get native mode details from EDID if necessary */ - if (!native_mode->dotclock) { + if (!native_mode->clock) { struct drm_display_mode *t, *mode; list_for_each_entry_safe(mode, t, &connector->probed_modes, head) { - if (mode->hdisplay == native_mode->panel_xres && - mode->vdisplay == native_mode->panel_yres) { - native_mode->hblank = mode->htotal - mode->hdisplay; - native_mode->hoverplus = mode->hsync_start - mode->hdisplay; - native_mode->hsync_width = mode->hsync_end - mode->hsync_start; - native_mode->vblank = mode->vtotal - mode->vdisplay; - native_mode->voverplus = mode->vsync_start - mode->vdisplay; - native_mode->vsync_width = mode->vsync_end - mode->vsync_start; - native_mode->dotclock = mode->clock; + if (mode->hdisplay == native_mode->hdisplay && + mode->vdisplay == native_mode->vdisplay) { + *native_mode = *mode; + drm_mode_set_crtcinfo(native_mode, CRTC_INTERLACE_HALVE_V); DRM_INFO("Determined LVDS native mode details from EDID\n"); break; } } } - if (!native_mode->dotclock) { + if (!native_mode->clock) { DRM_INFO("No LVDS native mode details, disabling RMX\n"); radeon_encoder->rmx_type = RMX_OFF; } @@ -409,13 +397,64 @@ static int radeon_lvds_get_modes(struct drm_connector *connector) static int radeon_lvds_mode_valid(struct drm_connector *connector, struct drm_display_mode *mode) { + struct drm_encoder *encoder = radeon_best_single_encoder(connector); + + if ((mode->hdisplay < 320) || (mode->vdisplay < 240)) + return MODE_PANEL; + + if (encoder) { + struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); + struct drm_display_mode *native_mode = &radeon_encoder->native_mode; + + /* AVIVO hardware supports downscaling modes larger than the panel + * to the panel size, but I'm not sure this is desirable. + */ + if ((mode->hdisplay > native_mode->hdisplay) || + (mode->vdisplay > native_mode->vdisplay)) + return MODE_PANEL; + + /* if scaling is disabled, block non-native modes */ + if (radeon_encoder->rmx_type == RMX_OFF) { + if ((mode->hdisplay != native_mode->hdisplay) || + (mode->vdisplay != native_mode->vdisplay)) + return MODE_PANEL; + } + } + return MODE_OK; } static enum drm_connector_status radeon_lvds_detect(struct drm_connector *connector) { - enum drm_connector_status ret = connector_status_connected; + struct radeon_connector *radeon_connector = to_radeon_connector(connector); + struct drm_encoder *encoder = radeon_best_single_encoder(connector); + enum drm_connector_status ret = connector_status_disconnected; + + if (encoder) { + struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); + struct drm_display_mode *native_mode = &radeon_encoder->native_mode; + + /* check if panel is valid */ + if (native_mode->hdisplay >= 320 && native_mode->vdisplay >= 240) + ret = connector_status_connected; + + } + + /* check for edid as well */ + if (radeon_connector->edid) + ret = connector_status_connected; + else { + if (radeon_connector->ddc_bus) { + radeon_i2c_do_lock(radeon_connector, 1); + radeon_connector->edid = drm_get_edid(&radeon_connector->base, + &radeon_connector->ddc_bus->adapter); + radeon_i2c_do_lock(radeon_connector, 0); + if (radeon_connector->edid) + ret = connector_status_connected; + } + } /* check acpi lid status ??? */ + radeon_connector_update_scratch_regs(connector, ret); return ret; } @@ -426,6 +465,8 @@ static void radeon_connector_destroy(struct drm_connector *connector) if (radeon_connector->ddc_bus) radeon_i2c_destroy(radeon_connector->ddc_bus); + if (radeon_connector->edid) + kfree(radeon_connector->edid); kfree(radeon_connector->con_priv); drm_sysfs_connector_remove(connector); drm_connector_cleanup(connector); @@ -495,6 +536,8 @@ static int radeon_vga_get_modes(struct drm_connector *connector) static int radeon_vga_mode_valid(struct drm_connector *connector, struct drm_display_mode *mode) { + /* XXX check mode bandwidth */ + /* XXX verify against max DAC output frequency */ return MODE_OK; } @@ -513,9 +556,33 @@ static enum drm_connector_status radeon_vga_detect(struct drm_connector *connect radeon_i2c_do_lock(radeon_connector, 1); dret = radeon_ddc_probe(radeon_connector); radeon_i2c_do_lock(radeon_connector, 0); - if (dret) - ret = connector_status_connected; - else { + if (dret) { + if (radeon_connector->edid) { + kfree(radeon_connector->edid); + radeon_connector->edid = NULL; + } + radeon_i2c_do_lock(radeon_connector, 1); + radeon_connector->edid = drm_get_edid(&radeon_connector->base, &radeon_connector->ddc_bus->adapter); + radeon_i2c_do_lock(radeon_connector, 0); + + if (!radeon_connector->edid) { + DRM_ERROR("%s: probed a monitor but no|invalid EDID\n", + drm_get_connector_name(connector)); + ret = connector_status_connected; + } else { + radeon_connector->use_digital = !!(radeon_connector->edid->input & DRM_EDID_INPUT_DIGITAL); + + /* some oems have boards with separate digital and analog connectors + * with a shared ddc line (often vga + hdmi) + */ + if (radeon_connector->use_digital && radeon_connector->shared_ddc) { + kfree(radeon_connector->edid); + radeon_connector->edid = NULL; + ret = connector_status_disconnected; + } else + ret = connector_status_connected; + } + } else { if (radeon_connector->dac_load_detect) { encoder_funcs = encoder->helper_private; ret = encoder_funcs->detect(encoder, connector); @@ -559,7 +626,7 @@ static int radeon_tv_get_modes(struct drm_connector *connector) radeon_add_common_modes(encoder, connector); else { /* only 800x600 is supported right now on pre-avivo chips */ - tv_mode = drm_cvt_mode(dev, 800, 600, 60, false, false); + tv_mode = drm_cvt_mode(dev, 800, 600, 60, false, false, false); tv_mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED; drm_mode_probed_add(connector, tv_mode); } @@ -569,6 +636,8 @@ static int radeon_tv_get_modes(struct drm_connector *connector) static int radeon_tv_mode_valid(struct drm_connector *connector, struct drm_display_mode *mode) { + if ((mode->hdisplay > 1024) || (mode->vdisplay > 768)) + return MODE_CLOCK_RANGE; return MODE_OK; } @@ -643,20 +712,29 @@ static enum drm_connector_status radeon_dvi_detect(struct drm_connector *connect dret = radeon_ddc_probe(radeon_connector); radeon_i2c_do_lock(radeon_connector, 0); if (dret) { + if (radeon_connector->edid) { + kfree(radeon_connector->edid); + radeon_connector->edid = NULL; + } radeon_i2c_do_lock(radeon_connector, 1); radeon_connector->edid = drm_get_edid(&radeon_connector->base, &radeon_connector->ddc_bus->adapter); radeon_i2c_do_lock(radeon_connector, 0); if (!radeon_connector->edid) { - DRM_ERROR("DDC responded but not EDID found for %s\n", - drm_get_connector_name(connector)); + DRM_ERROR("%s: probed a monitor but no|invalid EDID\n", + drm_get_connector_name(connector)); } else { radeon_connector->use_digital = !!(radeon_connector->edid->input & DRM_EDID_INPUT_DIGITAL); - /* if this isn't a digital monitor - then we need to make sure we don't have any - TV conflicts */ - ret = connector_status_connected; + /* some oems have boards with separate digital and analog connectors + * with a shared ddc line (often vga + hdmi) + */ + if ((!radeon_connector->use_digital) && radeon_connector->shared_ddc) { + kfree(radeon_connector->edid); + radeon_connector->edid = NULL; + ret = connector_status_disconnected; + } else + ret = connector_status_connected; } } @@ -743,9 +821,36 @@ struct drm_encoder *radeon_dvi_encoder(struct drm_connector *connector) return NULL; } +static void radeon_dvi_force(struct drm_connector *connector) +{ + struct radeon_connector *radeon_connector = to_radeon_connector(connector); + if (connector->force == DRM_FORCE_ON) + radeon_connector->use_digital = false; + if (connector->force == DRM_FORCE_ON_DIGITAL) + radeon_connector->use_digital = true; +} + +static int radeon_dvi_mode_valid(struct drm_connector *connector, + struct drm_display_mode *mode) +{ + struct radeon_connector *radeon_connector = to_radeon_connector(connector); + + /* XXX check mode bandwidth */ + + if (radeon_connector->use_digital && (mode->clock > 165000)) { + if ((radeon_connector->connector_object_id == CONNECTOR_OBJECT_ID_DUAL_LINK_DVI_I) || + (radeon_connector->connector_object_id == CONNECTOR_OBJECT_ID_DUAL_LINK_DVI_D) || + (radeon_connector->connector_object_id == CONNECTOR_OBJECT_ID_HDMI_TYPE_B)) + return MODE_OK; + else + return MODE_CLOCK_HIGH; + } + return MODE_OK; +} + struct drm_connector_helper_funcs radeon_dvi_connector_helper_funcs = { .get_modes = radeon_dvi_get_modes, - .mode_valid = radeon_vga_mode_valid, + .mode_valid = radeon_dvi_mode_valid, .best_encoder = radeon_dvi_encoder, }; @@ -755,6 +860,7 @@ struct drm_connector_funcs radeon_dvi_connector_funcs = { .fill_modes = drm_helper_probe_single_connector_modes, .set_property = radeon_connector_set_property, .destroy = radeon_connector_destroy, + .force = radeon_dvi_force, }; void @@ -764,13 +870,16 @@ radeon_add_atom_connector(struct drm_device *dev, int connector_type, struct radeon_i2c_bus_rec *i2c_bus, bool linkb, - uint32_t igp_lane_info) + uint32_t igp_lane_info, + uint16_t connector_object_id) { struct radeon_device *rdev = dev->dev_private; struct drm_connector *connector; struct radeon_connector *radeon_connector; struct radeon_connector_atom_dig *radeon_dig_connector; uint32_t subpixel_order = SubPixelNone; + bool shared_ddc = false; + int ret; /* fixme - tv/cv/din */ if (connector_type == DRM_MODE_CONNECTOR_Unknown) @@ -783,6 +892,13 @@ radeon_add_atom_connector(struct drm_device *dev, radeon_connector->devices |= supported_device; return; } + if (radeon_connector->ddc_bus && i2c_bus->valid) { + if (memcmp(&radeon_connector->ddc_bus->rec, i2c_bus, + sizeof(struct radeon_i2c_bus_rec)) == 0) { + radeon_connector->shared_ddc = true; + shared_ddc = true; + } + } } radeon_connector = kzalloc(sizeof(struct radeon_connector), GFP_KERNEL); @@ -793,27 +909,35 @@ radeon_add_atom_connector(struct drm_device *dev, radeon_connector->connector_id = connector_id; radeon_connector->devices = supported_device; + radeon_connector->shared_ddc = shared_ddc; + radeon_connector->connector_object_id = connector_object_id; switch (connector_type) { case DRM_MODE_CONNECTOR_VGA: drm_connector_init(dev, &radeon_connector->base, &radeon_vga_connector_funcs, connector_type); - drm_connector_helper_add(&radeon_connector->base, &radeon_vga_connector_helper_funcs); + ret = drm_connector_helper_add(&radeon_connector->base, &radeon_vga_connector_helper_funcs); + if (ret) + goto failed; if (i2c_bus->valid) { radeon_connector->ddc_bus = radeon_i2c_create(dev, i2c_bus, "VGA"); if (!radeon_connector->ddc_bus) goto failed; } + radeon_connector->dac_load_detect = true; drm_connector_attach_property(&radeon_connector->base, rdev->mode_info.load_detect_property, 1); break; case DRM_MODE_CONNECTOR_DVIA: drm_connector_init(dev, &radeon_connector->base, &radeon_vga_connector_funcs, connector_type); - drm_connector_helper_add(&radeon_connector->base, &radeon_vga_connector_helper_funcs); + ret = drm_connector_helper_add(&radeon_connector->base, &radeon_vga_connector_helper_funcs); + if (ret) + goto failed; if (i2c_bus->valid) { radeon_connector->ddc_bus = radeon_i2c_create(dev, i2c_bus, "DVI"); if (!radeon_connector->ddc_bus) goto failed; } + radeon_connector->dac_load_detect = true; drm_connector_attach_property(&radeon_connector->base, rdev->mode_info.load_detect_property, 1); @@ -827,7 +951,9 @@ radeon_add_atom_connector(struct drm_device *dev, radeon_dig_connector->igp_lane_info = igp_lane_info; radeon_connector->con_priv = radeon_dig_connector; drm_connector_init(dev, &radeon_connector->base, &radeon_dvi_connector_funcs, connector_type); - drm_connector_helper_add(&radeon_connector->base, &radeon_dvi_connector_helper_funcs); + ret = drm_connector_helper_add(&radeon_connector->base, &radeon_dvi_connector_helper_funcs); + if (ret) + goto failed; if (i2c_bus->valid) { radeon_connector->ddc_bus = radeon_i2c_create(dev, i2c_bus, "DVI"); if (!radeon_connector->ddc_bus) @@ -837,6 +963,7 @@ radeon_add_atom_connector(struct drm_device *dev, drm_connector_attach_property(&radeon_connector->base, rdev->mode_info.coherent_mode_property, 1); + radeon_connector->dac_load_detect = true; drm_connector_attach_property(&radeon_connector->base, rdev->mode_info.load_detect_property, 1); @@ -850,7 +977,9 @@ radeon_add_atom_connector(struct drm_device *dev, radeon_dig_connector->igp_lane_info = igp_lane_info; radeon_connector->con_priv = radeon_dig_connector; drm_connector_init(dev, &radeon_connector->base, &radeon_dvi_connector_funcs, connector_type); - drm_connector_helper_add(&radeon_connector->base, &radeon_dvi_connector_helper_funcs); + ret = drm_connector_helper_add(&radeon_connector->base, &radeon_dvi_connector_helper_funcs); + if (ret) + goto failed; if (i2c_bus->valid) { radeon_connector->ddc_bus = radeon_i2c_create(dev, i2c_bus, "HDMI"); if (!radeon_connector->ddc_bus) @@ -869,7 +998,9 @@ radeon_add_atom_connector(struct drm_device *dev, radeon_dig_connector->igp_lane_info = igp_lane_info; radeon_connector->con_priv = radeon_dig_connector; drm_connector_init(dev, &radeon_connector->base, &radeon_dvi_connector_funcs, connector_type); - drm_connector_helper_add(&radeon_connector->base, &radeon_dvi_connector_helper_funcs); + ret = drm_connector_helper_add(&radeon_connector->base, &radeon_dvi_connector_helper_funcs); + if (ret) + goto failed; if (i2c_bus->valid) { radeon_connector->ddc_bus = radeon_i2c_create(dev, i2c_bus, "DP"); if (!radeon_connector->ddc_bus) @@ -882,11 +1013,14 @@ radeon_add_atom_connector(struct drm_device *dev, case DRM_MODE_CONNECTOR_9PinDIN: if (radeon_tv == 1) { drm_connector_init(dev, &radeon_connector->base, &radeon_tv_connector_funcs, connector_type); - drm_connector_helper_add(&radeon_connector->base, &radeon_tv_connector_helper_funcs); + ret = drm_connector_helper_add(&radeon_connector->base, &radeon_tv_connector_helper_funcs); + if (ret) + goto failed; + radeon_connector->dac_load_detect = true; + drm_connector_attach_property(&radeon_connector->base, + rdev->mode_info.load_detect_property, + 1); } - drm_connector_attach_property(&radeon_connector->base, - rdev->mode_info.load_detect_property, - 1); break; case DRM_MODE_CONNECTOR_LVDS: radeon_dig_connector = kzalloc(sizeof(struct radeon_connector_atom_dig), GFP_KERNEL); @@ -896,7 +1030,9 @@ radeon_add_atom_connector(struct drm_device *dev, radeon_dig_connector->igp_lane_info = igp_lane_info; radeon_connector->con_priv = radeon_dig_connector; drm_connector_init(dev, &radeon_connector->base, &radeon_lvds_connector_funcs, connector_type); - drm_connector_helper_add(&radeon_connector->base, &radeon_lvds_connector_helper_funcs); + ret = drm_connector_helper_add(&radeon_connector->base, &radeon_lvds_connector_helper_funcs); + if (ret) + goto failed; if (i2c_bus->valid) { radeon_connector->ddc_bus = radeon_i2c_create(dev, i2c_bus, "LVDS"); if (!radeon_connector->ddc_bus) @@ -926,12 +1062,14 @@ radeon_add_legacy_connector(struct drm_device *dev, uint32_t connector_id, uint32_t supported_device, int connector_type, - struct radeon_i2c_bus_rec *i2c_bus) + struct radeon_i2c_bus_rec *i2c_bus, + uint16_t connector_object_id) { struct radeon_device *rdev = dev->dev_private; struct drm_connector *connector; struct radeon_connector *radeon_connector; uint32_t subpixel_order = SubPixelNone; + int ret; /* fixme - tv/cv/din */ if (connector_type == DRM_MODE_CONNECTOR_Unknown) @@ -954,27 +1092,34 @@ radeon_add_legacy_connector(struct drm_device *dev, radeon_connector->connector_id = connector_id; radeon_connector->devices = supported_device; + radeon_connector->connector_object_id = connector_object_id; switch (connector_type) { case DRM_MODE_CONNECTOR_VGA: drm_connector_init(dev, &radeon_connector->base, &radeon_vga_connector_funcs, connector_type); - drm_connector_helper_add(&radeon_connector->base, &radeon_vga_connector_helper_funcs); + ret = drm_connector_helper_add(&radeon_connector->base, &radeon_vga_connector_helper_funcs); + if (ret) + goto failed; if (i2c_bus->valid) { radeon_connector->ddc_bus = radeon_i2c_create(dev, i2c_bus, "VGA"); if (!radeon_connector->ddc_bus) goto failed; } + radeon_connector->dac_load_detect = true; drm_connector_attach_property(&radeon_connector->base, rdev->mode_info.load_detect_property, 1); break; case DRM_MODE_CONNECTOR_DVIA: drm_connector_init(dev, &radeon_connector->base, &radeon_vga_connector_funcs, connector_type); - drm_connector_helper_add(&radeon_connector->base, &radeon_vga_connector_helper_funcs); + ret = drm_connector_helper_add(&radeon_connector->base, &radeon_vga_connector_helper_funcs); + if (ret) + goto failed; if (i2c_bus->valid) { radeon_connector->ddc_bus = radeon_i2c_create(dev, i2c_bus, "DVI"); if (!radeon_connector->ddc_bus) goto failed; } + radeon_connector->dac_load_detect = true; drm_connector_attach_property(&radeon_connector->base, rdev->mode_info.load_detect_property, 1); @@ -982,11 +1127,14 @@ radeon_add_legacy_connector(struct drm_device *dev, case DRM_MODE_CONNECTOR_DVII: case DRM_MODE_CONNECTOR_DVID: drm_connector_init(dev, &radeon_connector->base, &radeon_dvi_connector_funcs, connector_type); - drm_connector_helper_add(&radeon_connector->base, &radeon_dvi_connector_helper_funcs); + ret = drm_connector_helper_add(&radeon_connector->base, &radeon_dvi_connector_helper_funcs); + if (ret) + goto failed; if (i2c_bus->valid) { radeon_connector->ddc_bus = radeon_i2c_create(dev, i2c_bus, "DVI"); if (!radeon_connector->ddc_bus) goto failed; + radeon_connector->dac_load_detect = true; drm_connector_attach_property(&radeon_connector->base, rdev->mode_info.load_detect_property, 1); @@ -998,7 +1146,17 @@ radeon_add_legacy_connector(struct drm_device *dev, case DRM_MODE_CONNECTOR_9PinDIN: if (radeon_tv == 1) { drm_connector_init(dev, &radeon_connector->base, &radeon_tv_connector_funcs, connector_type); - drm_connector_helper_add(&radeon_connector->base, &radeon_tv_connector_helper_funcs); + ret = drm_connector_helper_add(&radeon_connector->base, &radeon_tv_connector_helper_funcs); + if (ret) + goto failed; + radeon_connector->dac_load_detect = true; + /* RS400,RC410,RS480 chipset seems to report a lot + * of false positive on load detect, we haven't yet + * found a way to make load detect reliable on those + * chipset, thus just disable it for TV. + */ + if (rdev->family == CHIP_RS400 || rdev->family == CHIP_RS480) + radeon_connector->dac_load_detect = false; drm_connector_attach_property(&radeon_connector->base, rdev->mode_info.load_detect_property, 1); @@ -1006,7 +1164,9 @@ radeon_add_legacy_connector(struct drm_device *dev, break; case DRM_MODE_CONNECTOR_LVDS: drm_connector_init(dev, &radeon_connector->base, &radeon_lvds_connector_funcs, connector_type); - drm_connector_helper_add(&radeon_connector->base, &radeon_lvds_connector_helper_funcs); + ret = drm_connector_helper_add(&radeon_connector->base, &radeon_lvds_connector_helper_funcs); + if (ret) + goto failed; if (i2c_bus->valid) { radeon_connector->ddc_bus = radeon_i2c_create(dev, i2c_bus, "LVDS"); if (!radeon_connector->ddc_bus) diff --git a/drivers/gpu/drm/radeon/radeon_cs.c b/drivers/gpu/drm/radeon/radeon_cs.c index 12f5990c2d2a..5ab2cf96a264 100644 --- a/drivers/gpu/drm/radeon/radeon_cs.c +++ b/drivers/gpu/drm/radeon/radeon_cs.c @@ -142,15 +142,31 @@ int radeon_cs_parser_init(struct radeon_cs_parser *p, void *data) } p->chunks[i].length_dw = user_chunk.length_dw; - cdata = (uint32_t *)(unsigned long)user_chunk.chunk_data; + p->chunks[i].user_ptr = (void __user *)(unsigned long)user_chunk.chunk_data; - size = p->chunks[i].length_dw * sizeof(uint32_t); - p->chunks[i].kdata = kmalloc(size, GFP_KERNEL); - if (p->chunks[i].kdata == NULL) { - return -ENOMEM; - } - if (DRM_COPY_FROM_USER(p->chunks[i].kdata, cdata, size)) { - return -EFAULT; + cdata = (uint32_t *)(unsigned long)user_chunk.chunk_data; + if (p->chunks[i].chunk_id != RADEON_CHUNK_ID_IB) { + size = p->chunks[i].length_dw * sizeof(uint32_t); + p->chunks[i].kdata = kmalloc(size, GFP_KERNEL); + if (p->chunks[i].kdata == NULL) { + return -ENOMEM; + } + if (DRM_COPY_FROM_USER(p->chunks[i].kdata, + p->chunks[i].user_ptr, size)) { + return -EFAULT; + } + } else { + p->chunks[i].kpage[0] = kmalloc(PAGE_SIZE, GFP_KERNEL); + p->chunks[i].kpage[1] = kmalloc(PAGE_SIZE, GFP_KERNEL); + if (p->chunks[i].kpage[0] == NULL || p->chunks[i].kpage[1] == NULL) { + kfree(p->chunks[i].kpage[0]); + kfree(p->chunks[i].kpage[1]); + return -ENOMEM; + } + p->chunks[i].kpage_idx[0] = -1; + p->chunks[i].kpage_idx[1] = -1; + p->chunks[i].last_copied_page = -1; + p->chunks[i].last_page_index = ((p->chunks[i].length_dw * 4) - 1) / PAGE_SIZE; } } if (p->chunks[p->chunk_ib_idx].length_dw > (16 * 1024)) { @@ -190,6 +206,8 @@ static void radeon_cs_parser_fini(struct radeon_cs_parser *parser, int error) kfree(parser->relocs_ptr); for (i = 0; i < parser->nchunks; i++) { kfree(parser->chunks[i].kdata); + kfree(parser->chunks[i].kpage[0]); + kfree(parser->chunks[i].kpage[1]); } kfree(parser->chunks); kfree(parser->chunks_array); @@ -238,8 +256,14 @@ int radeon_cs_ioctl(struct drm_device *dev, void *data, struct drm_file *filp) * uncached). */ ib_chunk = &parser.chunks[parser.chunk_ib_idx]; parser.ib->length_dw = ib_chunk->length_dw; - memcpy((void *)parser.ib->ptr, ib_chunk->kdata, ib_chunk->length_dw*4); r = radeon_cs_parse(&parser); + if (r || parser.parser_error) { + DRM_ERROR("Invalid command stream !\n"); + radeon_cs_parser_fini(&parser, r); + mutex_unlock(&rdev->cs_mutex); + return r; + } + r = radeon_cs_finish_pages(&parser); if (r) { DRM_ERROR("Invalid command stream !\n"); radeon_cs_parser_fini(&parser, r); @@ -254,3 +278,64 @@ int radeon_cs_ioctl(struct drm_device *dev, void *data, struct drm_file *filp) mutex_unlock(&rdev->cs_mutex); return r; } + +int radeon_cs_finish_pages(struct radeon_cs_parser *p) +{ + struct radeon_cs_chunk *ibc = &p->chunks[p->chunk_ib_idx]; + int i; + int size = PAGE_SIZE; + + for (i = ibc->last_copied_page + 1; i <= ibc->last_page_index; i++) { + if (i == ibc->last_page_index) { + size = (ibc->length_dw * 4) % PAGE_SIZE; + if (size == 0) + size = PAGE_SIZE; + } + + if (DRM_COPY_FROM_USER(p->ib->ptr + (i * (PAGE_SIZE/4)), + ibc->user_ptr + (i * PAGE_SIZE), + size)) + return -EFAULT; + } + return 0; +} + +int radeon_cs_update_pages(struct radeon_cs_parser *p, int pg_idx) +{ + int new_page; + struct radeon_cs_chunk *ibc = &p->chunks[p->chunk_ib_idx]; + int i; + int size = PAGE_SIZE; + + for (i = ibc->last_copied_page + 1; i < pg_idx; i++) { + if (DRM_COPY_FROM_USER(p->ib->ptr + (i * (PAGE_SIZE/4)), + ibc->user_ptr + (i * PAGE_SIZE), + PAGE_SIZE)) { + p->parser_error = -EFAULT; + return 0; + } + } + + new_page = ibc->kpage_idx[0] < ibc->kpage_idx[1] ? 0 : 1; + + if (pg_idx == ibc->last_page_index) { + size = (ibc->length_dw * 4) % PAGE_SIZE; + if (size == 0) + size = PAGE_SIZE; + } + + if (DRM_COPY_FROM_USER(ibc->kpage[new_page], + ibc->user_ptr + (pg_idx * PAGE_SIZE), + size)) { + p->parser_error = -EFAULT; + return 0; + } + + /* copy to IB here */ + memcpy((void *)(p->ib->ptr+(pg_idx*(PAGE_SIZE/4))), ibc->kpage[new_page], size); + + ibc->last_copied_page = pg_idx; + ibc->kpage_idx[new_page] = pg_idx; + + return new_page; +} diff --git a/drivers/gpu/drm/radeon/radeon_cursor.c b/drivers/gpu/drm/radeon/radeon_cursor.c index b13c79e38bc0..28772a37009c 100644 --- a/drivers/gpu/drm/radeon/radeon_cursor.c +++ b/drivers/gpu/drm/radeon/radeon_cursor.c @@ -109,9 +109,15 @@ static void radeon_set_cursor(struct drm_crtc *crtc, struct drm_gem_object *obj, struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); struct radeon_device *rdev = crtc->dev->dev_private; - if (ASIC_IS_AVIVO(rdev)) + if (ASIC_IS_AVIVO(rdev)) { + if (rdev->family >= CHIP_RV770) { + if (radeon_crtc->crtc_id) + WREG32(R700_D2CUR_SURFACE_ADDRESS_HIGH, 0); + else + WREG32(R700_D1CUR_SURFACE_ADDRESS_HIGH, 0); + } WREG32(AVIVO_D1CUR_SURFACE_ADDRESS + radeon_crtc->crtc_offset, gpu_addr); - else { + } else { radeon_crtc->legacy_cursor_offset = gpu_addr - radeon_crtc->legacy_display_base_addr; /* offset is from DISP(2)_BASE_ADDRESS */ WREG32(RADEON_CUR_OFFSET + radeon_crtc->crtc_offset, radeon_crtc->legacy_cursor_offset); diff --git a/drivers/gpu/drm/radeon/radeon_device.c b/drivers/gpu/drm/radeon/radeon_device.c index daf5db780956..41bb76fbe734 100644 --- a/drivers/gpu/drm/radeon/radeon_device.c +++ b/drivers/gpu/drm/radeon/radeon_device.c @@ -322,10 +322,6 @@ int radeon_asic_init(struct radeon_device *rdev) case CHIP_RV380: rdev->asic = &r300_asic; if (rdev->flags & RADEON_IS_PCIE) { - rdev->asic->gart_init = &rv370_pcie_gart_init; - rdev->asic->gart_fini = &rv370_pcie_gart_fini; - rdev->asic->gart_enable = &rv370_pcie_gart_enable; - rdev->asic->gart_disable = &rv370_pcie_gart_disable; rdev->asic->gart_tlb_flush = &rv370_pcie_gart_tlb_flush; rdev->asic->gart_set_page = &rv370_pcie_gart_set_page; } @@ -448,20 +444,24 @@ static uint32_t cail_reg_read(struct card_info *info, uint32_t reg) return r; } -static struct card_info atom_card_info = { - .dev = NULL, - .reg_read = cail_reg_read, - .reg_write = cail_reg_write, - .mc_read = cail_mc_read, - .mc_write = cail_mc_write, - .pll_read = cail_pll_read, - .pll_write = cail_pll_write, -}; - int radeon_atombios_init(struct radeon_device *rdev) { - atom_card_info.dev = rdev->ddev; - rdev->mode_info.atom_context = atom_parse(&atom_card_info, rdev->bios); + struct card_info *atom_card_info = + kzalloc(sizeof(struct card_info), GFP_KERNEL); + + if (!atom_card_info) + return -ENOMEM; + + rdev->mode_info.atom_card_info = atom_card_info; + atom_card_info->dev = rdev->ddev; + atom_card_info->reg_read = cail_reg_read; + atom_card_info->reg_write = cail_reg_write; + atom_card_info->mc_read = cail_mc_read; + atom_card_info->mc_write = cail_mc_write; + atom_card_info->pll_read = cail_pll_read; + atom_card_info->pll_write = cail_pll_write; + + rdev->mode_info.atom_context = atom_parse(atom_card_info, rdev->bios); radeon_atom_initialize_bios_scratch_regs(rdev->ddev); return 0; } @@ -469,6 +469,7 @@ int radeon_atombios_init(struct radeon_device *rdev) void radeon_atombios_fini(struct radeon_device *rdev) { kfree(rdev->mode_info.atom_context); + kfree(rdev->mode_info.atom_card_info); } int radeon_combios_init(struct radeon_device *rdev) @@ -485,7 +486,6 @@ void radeon_combios_fini(struct radeon_device *rdev) static unsigned int radeon_vga_set_decode(void *cookie, bool state) { struct radeon_device *rdev = cookie; - radeon_vga_set_state(rdev, state); if (state) return VGA_RSRC_LEGACY_IO | VGA_RSRC_LEGACY_MEM | @@ -493,6 +493,29 @@ static unsigned int radeon_vga_set_decode(void *cookie, bool state) else return VGA_RSRC_NORMAL_IO | VGA_RSRC_NORMAL_MEM; } + +void radeon_agp_disable(struct radeon_device *rdev) +{ + rdev->flags &= ~RADEON_IS_AGP; + if (rdev->family >= CHIP_R600) { + DRM_INFO("Forcing AGP to PCIE mode\n"); + rdev->flags |= RADEON_IS_PCIE; + } else if (rdev->family >= CHIP_RV515 || + rdev->family == CHIP_RV380 || + rdev->family == CHIP_RV410 || + rdev->family == CHIP_R423) { + DRM_INFO("Forcing AGP to PCIE mode\n"); + rdev->flags |= RADEON_IS_PCIE; + rdev->asic->gart_tlb_flush = &rv370_pcie_gart_tlb_flush; + rdev->asic->gart_set_page = &rv370_pcie_gart_set_page; + } else { + DRM_INFO("Forcing AGP to PCI mode\n"); + rdev->flags |= RADEON_IS_PCI; + rdev->asic->gart_tlb_flush = &r100_pci_gart_tlb_flush; + rdev->asic->gart_set_page = &r100_pci_gart_set_page; + } +} + /* * Radeon device. */ @@ -531,29 +554,7 @@ int radeon_device_init(struct radeon_device *rdev, } if (radeon_agpmode == -1) { - rdev->flags &= ~RADEON_IS_AGP; - if (rdev->family >= CHIP_RV515 || - rdev->family == CHIP_RV380 || - rdev->family == CHIP_RV410 || - rdev->family == CHIP_R423) { - DRM_INFO("Forcing AGP to PCIE mode\n"); - rdev->flags |= RADEON_IS_PCIE; - rdev->asic->gart_init = &rv370_pcie_gart_init; - rdev->asic->gart_fini = &rv370_pcie_gart_fini; - rdev->asic->gart_enable = &rv370_pcie_gart_enable; - rdev->asic->gart_disable = &rv370_pcie_gart_disable; - rdev->asic->gart_tlb_flush = &rv370_pcie_gart_tlb_flush; - rdev->asic->gart_set_page = &rv370_pcie_gart_set_page; - } else { - DRM_INFO("Forcing AGP to PCI mode\n"); - rdev->flags |= RADEON_IS_PCI; - rdev->asic->gart_init = &r100_pci_gart_init; - rdev->asic->gart_fini = &r100_pci_gart_fini; - rdev->asic->gart_enable = &r100_pci_gart_enable; - rdev->asic->gart_disable = &r100_pci_gart_disable; - rdev->asic->gart_tlb_flush = &r100_pci_gart_tlb_flush; - rdev->asic->gart_set_page = &r100_pci_gart_set_page; - } + radeon_agp_disable(rdev); } /* set DMA mask + need_dma32 flags. @@ -585,111 +586,26 @@ int radeon_device_init(struct radeon_device *rdev, DRM_INFO("register mmio base: 0x%08X\n", (uint32_t)rdev->rmmio_base); DRM_INFO("register mmio size: %u\n", (unsigned)rdev->rmmio_size); - rdev->new_init_path = false; - r = radeon_init(rdev); - if (r) { - return r; - } - /* if we have > 1 VGA cards, then disable the radeon VGA resources */ - r = vga_client_register(rdev->pdev, rdev, NULL, radeon_vga_set_decode); - if (r) { - return -EINVAL; - } + /* this will fail for cards that aren't VGA class devices, just + * ignore it */ + vga_client_register(rdev->pdev, rdev, NULL, radeon_vga_set_decode); - if (!rdev->new_init_path) { - /* Setup errata flags */ - radeon_errata(rdev); - /* Initialize scratch registers */ - radeon_scratch_init(rdev); - /* Initialize surface registers */ - radeon_surface_init(rdev); - - /* BIOS*/ - if (!radeon_get_bios(rdev)) { - if (ASIC_IS_AVIVO(rdev)) - return -EINVAL; - } - if (rdev->is_atom_bios) { - r = radeon_atombios_init(rdev); - if (r) { - return r; - } - } else { - r = radeon_combios_init(rdev); - if (r) { - return r; - } - } - /* Reset gpu before posting otherwise ATOM will enter infinite loop */ - if (radeon_gpu_reset(rdev)) { - /* FIXME: what do we want to do here ? */ - } - /* check if cards are posted or not */ - if (!radeon_card_posted(rdev) && rdev->bios) { - DRM_INFO("GPU not posted. posting now...\n"); - if (rdev->is_atom_bios) { - atom_asic_init(rdev->mode_info.atom_context); - } else { - radeon_combios_asic_init(rdev->ddev); - } - } - /* Get clock & vram information */ - radeon_get_clock_info(rdev->ddev); - radeon_vram_info(rdev); - /* Initialize clocks */ - r = radeon_clocks_init(rdev); - if (r) { - return r; - } + r = radeon_init(rdev); + if (r) + return r; - /* Initialize memory controller (also test AGP) */ - r = radeon_mc_init(rdev); - if (r) { - return r; - } - /* Fence driver */ - r = radeon_fence_driver_init(rdev); - if (r) { - return r; - } - r = radeon_irq_kms_init(rdev); - if (r) { - return r; - } - /* Memory manager */ - r = radeon_object_init(rdev); - if (r) { - return r; - } - r = radeon_gpu_gart_init(rdev); + if (rdev->flags & RADEON_IS_AGP && !rdev->accel_working) { + /* Acceleration not working on AGP card try again + * with fallback to PCI or PCIE GART + */ + radeon_gpu_reset(rdev); + radeon_fini(rdev); + radeon_agp_disable(rdev); + r = radeon_init(rdev); if (r) return r; - /* Initialize GART (initialize after TTM so we can allocate - * memory through TTM but finalize after TTM) */ - r = radeon_gart_enable(rdev); - if (r) - return 0; - r = radeon_gem_init(rdev); - if (r) - return 0; - - /* 1M ring buffer */ - r = radeon_cp_init(rdev, 1024 * 1024); - if (r) - return 0; - r = radeon_wb_init(rdev); - if (r) - DRM_ERROR("radeon: failled initializing WB (%d).\n", r); - r = radeon_ib_pool_init(rdev); - if (r) - return 0; - r = radeon_ib_test(rdev); - if (r) - return 0; - rdev->accel_working = true; } - DRM_INFO("radeon: kernel modesetting successfully initialized.\n"); if (radeon_testing) { radeon_test_moves(rdev); } @@ -703,32 +619,8 @@ void radeon_device_fini(struct radeon_device *rdev) { DRM_INFO("radeon: finishing device.\n"); rdev->shutdown = true; - /* Order matter so becarefull if you rearrange anythings */ - if (!rdev->new_init_path) { - radeon_ib_pool_fini(rdev); - radeon_cp_fini(rdev); - radeon_wb_fini(rdev); - radeon_gpu_gart_fini(rdev); - radeon_gem_fini(rdev); - radeon_mc_fini(rdev); -#if __OS_HAS_AGP - radeon_agp_fini(rdev); -#endif - radeon_irq_kms_fini(rdev); - vga_client_register(rdev->pdev, NULL, NULL, NULL); - radeon_fence_driver_fini(rdev); - radeon_clocks_fini(rdev); - radeon_object_fini(rdev); - if (rdev->is_atom_bios) { - radeon_atombios_fini(rdev); - } else { - radeon_combios_fini(rdev); - } - kfree(rdev->bios); - rdev->bios = NULL; - } else { - radeon_fini(rdev); - } + radeon_fini(rdev); + vga_client_register(rdev->pdev, NULL, NULL, NULL); iounmap(rdev->rmmio); rdev->rmmio = NULL; } @@ -768,14 +660,7 @@ int radeon_suspend_kms(struct drm_device *dev, pm_message_t state) radeon_save_bios_scratch_regs(rdev); - if (!rdev->new_init_path) { - radeon_cp_disable(rdev); - radeon_gart_disable(rdev); - rdev->irq.sw_int = false; - radeon_irq_set(rdev); - } else { - radeon_suspend(rdev); - } + radeon_suspend(rdev); /* evict remaining vram memory */ radeon_object_evict_vram(rdev); @@ -794,7 +679,6 @@ int radeon_suspend_kms(struct drm_device *dev, pm_message_t state) int radeon_resume_kms(struct drm_device *dev) { struct radeon_device *rdev = dev->dev_private; - int r; acquire_console_sem(); pci_set_power_state(dev->pdev, PCI_D0); @@ -804,43 +688,9 @@ int radeon_resume_kms(struct drm_device *dev) return -1; } pci_set_master(dev->pdev); - /* Reset gpu before posting otherwise ATOM will enter infinite loop */ - if (!rdev->new_init_path) { - if (radeon_gpu_reset(rdev)) { - /* FIXME: what do we want to do here ? */ - } - /* post card */ - if (rdev->is_atom_bios) { - atom_asic_init(rdev->mode_info.atom_context); - } else { - radeon_combios_asic_init(rdev->ddev); - } - /* Initialize clocks */ - r = radeon_clocks_init(rdev); - if (r) { - release_console_sem(); - return r; - } - /* Enable IRQ */ - rdev->irq.sw_int = true; - radeon_irq_set(rdev); - /* Initialize GPU Memory Controller */ - r = radeon_mc_init(rdev); - if (r) { - goto out; - } - r = radeon_gart_enable(rdev); - if (r) { - goto out; - } - r = radeon_cp_init(rdev, rdev->cp.ring_size); - if (r) { - goto out; - } - } else { - radeon_resume(rdev); - } -out: + /* resume AGP if in use */ + radeon_agp_resume(rdev); + radeon_resume(rdev); radeon_restore_bios_scratch_regs(rdev); fb_set_suspend(rdev->fbdev_info, 0); release_console_sem(); diff --git a/drivers/gpu/drm/radeon/radeon_display.c b/drivers/gpu/drm/radeon/radeon_display.c index 5d8141b13765..c85df4afcb7a 100644 --- a/drivers/gpu/drm/radeon/radeon_display.c +++ b/drivers/gpu/drm/radeon/radeon_display.c @@ -106,51 +106,44 @@ void radeon_crtc_load_lut(struct drm_crtc *crtc) legacy_crtc_load_lut(crtc); } -/** Sets the color ramps on behalf of RandR */ +/** Sets the color ramps on behalf of fbcon */ void radeon_crtc_fb_gamma_set(struct drm_crtc *crtc, u16 red, u16 green, u16 blue, int regno) { struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); - if (regno == 0) - DRM_DEBUG("gamma set %d\n", radeon_crtc->crtc_id); radeon_crtc->lut_r[regno] = red >> 6; radeon_crtc->lut_g[regno] = green >> 6; radeon_crtc->lut_b[regno] = blue >> 6; } +/** Gets the color ramps on behalf of fbcon */ +void radeon_crtc_fb_gamma_get(struct drm_crtc *crtc, u16 *red, u16 *green, + u16 *blue, int regno) +{ + struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); + + *red = radeon_crtc->lut_r[regno] << 6; + *green = radeon_crtc->lut_g[regno] << 6; + *blue = radeon_crtc->lut_b[regno] << 6; +} + static void radeon_crtc_gamma_set(struct drm_crtc *crtc, u16 *red, u16 *green, u16 *blue, uint32_t size) { struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); - int i, j; + int i; if (size != 256) { return; } - if (crtc->fb == NULL) { - return; - } - if (crtc->fb->depth == 16) { - for (i = 0; i < 64; i++) { - if (i <= 31) { - for (j = 0; j < 8; j++) { - radeon_crtc->lut_r[i * 8 + j] = red[i] >> 6; - radeon_crtc->lut_b[i * 8 + j] = blue[i] >> 6; - } - } - for (j = 0; j < 4; j++) - radeon_crtc->lut_g[i * 4 + j] = green[i] >> 6; - } - } else { - for (i = 0; i < 256; i++) { - radeon_crtc->lut_r[i] = red[i] >> 6; - radeon_crtc->lut_g[i] = green[i] >> 6; - radeon_crtc->lut_b[i] = blue[i] >> 6; - } + /* userspace palettes are always correct as is */ + for (i = 0; i < 256; i++) { + radeon_crtc->lut_r[i] = red[i] >> 6; + radeon_crtc->lut_g[i] = green[i] >> 6; + radeon_crtc->lut_b[i] = blue[i] >> 6; } - radeon_crtc_load_lut(crtc); } @@ -341,27 +334,19 @@ static bool radeon_setup_enc_conn(struct drm_device *dev) int radeon_ddc_get_modes(struct radeon_connector *radeon_connector) { - struct edid *edid; int ret = 0; if (!radeon_connector->ddc_bus) return -1; if (!radeon_connector->edid) { radeon_i2c_do_lock(radeon_connector, 1); - edid = drm_get_edid(&radeon_connector->base, &radeon_connector->ddc_bus->adapter); + radeon_connector->edid = drm_get_edid(&radeon_connector->base, &radeon_connector->ddc_bus->adapter); radeon_i2c_do_lock(radeon_connector, 0); - } else - edid = radeon_connector->edid; + } - if (edid) { - /* update digital bits here */ - if (edid->input & DRM_EDID_INPUT_DIGITAL) - radeon_connector->use_digital = 1; - else - radeon_connector->use_digital = 0; - drm_mode_connector_update_edid_property(&radeon_connector->base, edid); - ret = drm_add_edid_modes(&radeon_connector->base, edid); - kfree(edid); + if (radeon_connector->edid) { + drm_mode_connector_update_edid_property(&radeon_connector->base, radeon_connector->edid); + ret = drm_add_edid_modes(&radeon_connector->base, radeon_connector->edid); return ret; } drm_mode_connector_update_edid_property(&radeon_connector->base, NULL); @@ -724,7 +709,11 @@ int radeon_modeset_init(struct radeon_device *rdev) if (ret) { return ret; } - /* allocate crtcs - TODO single crtc */ + + if (rdev->flags & RADEON_SINGLE_CRTC) + num_crtc = 1; + + /* allocate crtcs */ for (i = 0; i < num_crtc; i++) { radeon_crtc_init(rdev->ddev, i); } @@ -764,7 +753,7 @@ bool radeon_crtc_scaling_mode_fixup(struct drm_crtc *crtc, radeon_crtc->rmx_type = radeon_encoder->rmx_type; memcpy(&radeon_crtc->native_mode, &radeon_encoder->native_mode, - sizeof(struct radeon_native_mode)); + sizeof(struct drm_display_mode)); first = false; } else { if (radeon_crtc->rmx_type != radeon_encoder->rmx_type) { @@ -782,10 +771,10 @@ bool radeon_crtc_scaling_mode_fixup(struct drm_crtc *crtc, if (radeon_crtc->rmx_type != RMX_OFF) { fixed20_12 a, b; a.full = rfixed_const(crtc->mode.vdisplay); - b.full = rfixed_const(radeon_crtc->native_mode.panel_xres); + b.full = rfixed_const(radeon_crtc->native_mode.hdisplay); radeon_crtc->vsc.full = rfixed_div(a, b); a.full = rfixed_const(crtc->mode.hdisplay); - b.full = rfixed_const(radeon_crtc->native_mode.panel_yres); + b.full = rfixed_const(radeon_crtc->native_mode.vdisplay); radeon_crtc->hsc.full = rfixed_div(a, b); } else { radeon_crtc->vsc.full = rfixed_const(1); diff --git a/drivers/gpu/drm/radeon/radeon_drv.c b/drivers/gpu/drm/radeon/radeon_drv.c index 50fce498910c..7f50fb864af8 100644 --- a/drivers/gpu/drm/radeon/radeon_drv.c +++ b/drivers/gpu/drm/radeon/radeon_drv.c @@ -62,9 +62,6 @@ void radeon_driver_irq_preinstall_kms(struct drm_device *dev); int radeon_driver_irq_postinstall_kms(struct drm_device *dev); void radeon_driver_irq_uninstall_kms(struct drm_device *dev); irqreturn_t radeon_driver_irq_handler_kms(DRM_IRQ_ARGS); -int radeon_master_create_kms(struct drm_device *dev, struct drm_master *master); -void radeon_master_destroy_kms(struct drm_device *dev, - struct drm_master *master); int radeon_dma_ioctl_kms(struct drm_device *dev, void *data, struct drm_file *file_priv); int radeon_gem_object_init(struct drm_gem_object *obj); @@ -260,8 +257,6 @@ static struct drm_driver kms_driver = { .get_vblank_counter = radeon_get_vblank_counter_kms, .enable_vblank = radeon_enable_vblank_kms, .disable_vblank = radeon_disable_vblank_kms, - .master_create = radeon_master_create_kms, - .master_destroy = radeon_master_destroy_kms, #if defined(CONFIG_DEBUG_FS) .debugfs_init = radeon_debugfs_init, .debugfs_cleanup = radeon_debugfs_cleanup, diff --git a/drivers/gpu/drm/radeon/radeon_drv.h b/drivers/gpu/drm/radeon/radeon_drv.h index cb0cfe4b3082..350962e0f346 100644 --- a/drivers/gpu/drm/radeon/radeon_drv.h +++ b/drivers/gpu/drm/radeon/radeon_drv.h @@ -34,6 +34,8 @@ #include <linux/firmware.h> #include <linux/platform_device.h> +#include "radeon_family.h" + /* General customization: */ @@ -109,75 +111,12 @@ #define DRIVER_MINOR 31 #define DRIVER_PATCHLEVEL 0 -/* - * Radeon chip families - */ -enum radeon_family { - CHIP_R100, - CHIP_RV100, - CHIP_RS100, - CHIP_RV200, - CHIP_RS200, - CHIP_R200, - CHIP_RV250, - CHIP_RS300, - CHIP_RV280, - CHIP_R300, - CHIP_R350, - CHIP_RV350, - CHIP_RV380, - CHIP_R420, - CHIP_R423, - CHIP_RV410, - CHIP_RS400, - CHIP_RS480, - CHIP_RS600, - CHIP_RS690, - CHIP_RS740, - CHIP_RV515, - CHIP_R520, - CHIP_RV530, - CHIP_RV560, - CHIP_RV570, - CHIP_R580, - CHIP_R600, - CHIP_RV610, - CHIP_RV630, - CHIP_RV620, - CHIP_RV635, - CHIP_RV670, - CHIP_RS780, - CHIP_RS880, - CHIP_RV770, - CHIP_RV730, - CHIP_RV710, - CHIP_RV740, - CHIP_LAST, -}; - enum radeon_cp_microcode_version { UCODE_R100, UCODE_R200, UCODE_R300, }; -/* - * Chip flags - */ -enum radeon_chip_flags { - RADEON_FAMILY_MASK = 0x0000ffffUL, - RADEON_FLAGS_MASK = 0xffff0000UL, - RADEON_IS_MOBILITY = 0x00010000UL, - RADEON_IS_IGP = 0x00020000UL, - RADEON_SINGLE_CRTC = 0x00040000UL, - RADEON_IS_AGP = 0x00080000UL, - RADEON_HAS_HIERZ = 0x00100000UL, - RADEON_IS_PCIE = 0x00200000UL, - RADEON_NEW_MEMMAP = 0x00400000UL, - RADEON_IS_PCI = 0x00800000UL, - RADEON_IS_IGPGART = 0x01000000UL, -}; - typedef struct drm_radeon_freelist { unsigned int age; struct drm_buf *buf; diff --git a/drivers/gpu/drm/radeon/radeon_encoders.c b/drivers/gpu/drm/radeon/radeon_encoders.c index 621646752cd2..d42bc512d75a 100644 --- a/drivers/gpu/drm/radeon/radeon_encoders.c +++ b/drivers/gpu/drm/radeon/radeon_encoders.c @@ -31,6 +31,10 @@ extern int atom_debug; +/* evil but including atombios.h is much worse */ +bool radeon_atom_get_tv_timings(struct radeon_device *rdev, int index, + struct drm_display_mode *mode); + uint32_t radeon_get_encoder_id(struct drm_device *dev, uint32_t supported_device, uint8_t dac) { @@ -167,49 +171,17 @@ void radeon_rmx_mode_fixup(struct drm_encoder *encoder, struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); struct drm_device *dev = encoder->dev; struct radeon_device *rdev = dev->dev_private; - struct radeon_native_mode *native_mode = &radeon_encoder->native_mode; - - if (mode->hdisplay < native_mode->panel_xres || - mode->vdisplay < native_mode->panel_yres) { - if (ASIC_IS_AVIVO(rdev)) { - adjusted_mode->hdisplay = native_mode->panel_xres; - adjusted_mode->vdisplay = native_mode->panel_yres; - adjusted_mode->htotal = native_mode->panel_xres + native_mode->hblank; - adjusted_mode->hsync_start = native_mode->panel_xres + native_mode->hoverplus; - adjusted_mode->hsync_end = adjusted_mode->hsync_start + native_mode->hsync_width; - adjusted_mode->vtotal = native_mode->panel_yres + native_mode->vblank; - adjusted_mode->vsync_start = native_mode->panel_yres + native_mode->voverplus; - adjusted_mode->vsync_end = adjusted_mode->vsync_start + native_mode->vsync_width; - /* update crtc values */ - drm_mode_set_crtcinfo(adjusted_mode, CRTC_INTERLACE_HALVE_V); - /* adjust crtc values */ - adjusted_mode->crtc_hdisplay = native_mode->panel_xres; - adjusted_mode->crtc_vdisplay = native_mode->panel_yres; - adjusted_mode->crtc_htotal = adjusted_mode->crtc_hdisplay + native_mode->hblank; - adjusted_mode->crtc_hsync_start = adjusted_mode->crtc_hdisplay + native_mode->hoverplus; - adjusted_mode->crtc_hsync_end = adjusted_mode->crtc_hsync_start + native_mode->hsync_width; - adjusted_mode->crtc_vtotal = adjusted_mode->crtc_vdisplay + native_mode->vblank; - adjusted_mode->crtc_vsync_start = adjusted_mode->crtc_vdisplay + native_mode->voverplus; - adjusted_mode->crtc_vsync_end = adjusted_mode->crtc_vsync_start + native_mode->vsync_width; - } else { - adjusted_mode->htotal = native_mode->panel_xres + native_mode->hblank; - adjusted_mode->hsync_start = native_mode->panel_xres + native_mode->hoverplus; - adjusted_mode->hsync_end = adjusted_mode->hsync_start + native_mode->hsync_width; - adjusted_mode->vtotal = native_mode->panel_yres + native_mode->vblank; - adjusted_mode->vsync_start = native_mode->panel_yres + native_mode->voverplus; - adjusted_mode->vsync_end = adjusted_mode->vsync_start + native_mode->vsync_width; - /* update crtc values */ - drm_mode_set_crtcinfo(adjusted_mode, CRTC_INTERLACE_HALVE_V); - /* adjust crtc values */ - adjusted_mode->crtc_htotal = adjusted_mode->crtc_hdisplay + native_mode->hblank; - adjusted_mode->crtc_hsync_start = adjusted_mode->crtc_hdisplay + native_mode->hoverplus; - adjusted_mode->crtc_hsync_end = adjusted_mode->crtc_hsync_start + native_mode->hsync_width; - adjusted_mode->crtc_vtotal = adjusted_mode->crtc_vdisplay + native_mode->vblank; - adjusted_mode->crtc_vsync_start = adjusted_mode->crtc_vdisplay + native_mode->voverplus; - adjusted_mode->crtc_vsync_end = adjusted_mode->crtc_vsync_start + native_mode->vsync_width; + struct drm_display_mode *native_mode = &radeon_encoder->native_mode; + + if (mode->hdisplay < native_mode->hdisplay || + mode->vdisplay < native_mode->vdisplay) { + int mode_id = adjusted_mode->base.id; + *adjusted_mode = *native_mode; + if (!ASIC_IS_AVIVO(rdev)) { + adjusted_mode->hdisplay = mode->hdisplay; + adjusted_mode->vdisplay = mode->vdisplay; } - adjusted_mode->flags = native_mode->flags; - adjusted_mode->clock = native_mode->dotclock; + adjusted_mode->base.id = mode_id; } } @@ -219,7 +191,11 @@ static bool radeon_atom_mode_fixup(struct drm_encoder *encoder, struct drm_display_mode *adjusted_mode) { struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); + struct drm_device *dev = encoder->dev; + struct radeon_device *rdev = dev->dev_private; + /* set the active encoder to connector routing */ + radeon_encoder_set_active_device(encoder); drm_mode_set_crtcinfo(adjusted_mode, 0); if (radeon_encoder->rmx_type != RMX_OFF) @@ -230,6 +206,18 @@ static bool radeon_atom_mode_fixup(struct drm_encoder *encoder, && (mode->crtc_vsync_start < (mode->crtc_vdisplay + 2))) adjusted_mode->crtc_vsync_start = adjusted_mode->crtc_vdisplay + 2; + if (radeon_encoder->active_device & (ATOM_DEVICE_TV_SUPPORT)) { + struct radeon_encoder_atom_dac *tv_dac = radeon_encoder->enc_priv; + if (tv_dac) { + if (tv_dac->tv_std == TV_STD_NTSC || + tv_dac->tv_std == TV_STD_NTSC_J || + tv_dac->tv_std == TV_STD_PAL_M) + radeon_atom_get_tv_timings(rdev, 0, adjusted_mode); + else + radeon_atom_get_tv_timings(rdev, 1, adjusted_mode); + } + } + return true; } @@ -461,7 +449,7 @@ atombios_digital_setup(struct drm_encoder *encoder, int action) case 1: args.v1.ucMisc = 0; args.v1.ucAction = action; - if (drm_detect_hdmi_monitor((struct edid *)connector->edid_blob_ptr)) + if (drm_detect_hdmi_monitor(radeon_connector->edid)) args.v1.ucMisc |= PANEL_ENCODER_MISC_HDMI_TYPE; args.v1.usPixelClock = cpu_to_le16(radeon_encoder->pixel_clock / 10); if (radeon_encoder->devices & (ATOM_DEVICE_LCD_SUPPORT)) { @@ -486,7 +474,7 @@ atombios_digital_setup(struct drm_encoder *encoder, int action) if (dig->coherent_mode) args.v2.ucMisc |= PANEL_ENCODER_MISC_COHERENT; } - if (drm_detect_hdmi_monitor((struct edid *)connector->edid_blob_ptr)) + if (drm_detect_hdmi_monitor(radeon_connector->edid)) args.v2.ucMisc |= PANEL_ENCODER_MISC_HDMI_TYPE; args.v2.usPixelClock = cpu_to_le16(radeon_encoder->pixel_clock / 10); args.v2.ucTruncate = 0; @@ -544,7 +532,7 @@ atombios_get_encoder_mode(struct drm_encoder *encoder) switch (connector->connector_type) { case DRM_MODE_CONNECTOR_DVII: case DRM_MODE_CONNECTOR_HDMIB: /* HDMI-B is basically DL-DVI; analog works fine */ - if (drm_detect_hdmi_monitor((struct edid *)connector->edid_blob_ptr)) + if (drm_detect_hdmi_monitor(radeon_connector->edid)) return ATOM_ENCODER_MODE_HDMI; else if (radeon_connector->use_digital) return ATOM_ENCODER_MODE_DVI; @@ -554,7 +542,7 @@ atombios_get_encoder_mode(struct drm_encoder *encoder) case DRM_MODE_CONNECTOR_DVID: case DRM_MODE_CONNECTOR_HDMIA: default: - if (drm_detect_hdmi_monitor((struct edid *)connector->edid_blob_ptr)) + if (drm_detect_hdmi_monitor(radeon_connector->edid)) return ATOM_ENCODER_MODE_HDMI; else return ATOM_ENCODER_MODE_DVI; @@ -566,7 +554,7 @@ atombios_get_encoder_mode(struct drm_encoder *encoder) /*if (radeon_output->MonType == MT_DP) return ATOM_ENCODER_MODE_DP; else*/ - if (drm_detect_hdmi_monitor((struct edid *)connector->edid_blob_ptr)) + if (drm_detect_hdmi_monitor(radeon_connector->edid)) return ATOM_ENCODER_MODE_HDMI; else return ATOM_ENCODER_MODE_DVI; @@ -734,14 +722,17 @@ atombios_dig_transmitter_setup(struct drm_encoder *encoder, int action) atom_parse_cmd_header(rdev->mode_info.atom_context, index, &frev, &crev); args.v1.ucAction = action; - + if (action == ATOM_TRANSMITTER_ACTION_INIT) { + args.v1.usInitInfo = radeon_connector->connector_object_id; + } else { + if (radeon_encoder->pixel_clock > 165000) + args.v1.usPixelClock = cpu_to_le16((radeon_encoder->pixel_clock / 2) / 10); + else + args.v1.usPixelClock = cpu_to_le16(radeon_encoder->pixel_clock / 10); + } if (ASIC_IS_DCE32(rdev)) { - if (radeon_encoder->pixel_clock > 165000) { - args.v2.usPixelClock = cpu_to_le16((radeon_encoder->pixel_clock * 10 * 2) / 100); - args.v2.acConfig.fDualLinkConnector = 1; - } else { - args.v2.usPixelClock = cpu_to_le16((radeon_encoder->pixel_clock * 10 * 4) / 100); - } + if (radeon_encoder->pixel_clock > 165000) + args.v2.usPixelClock = cpu_to_le16((radeon_encoder->pixel_clock / 2) / 10); if (dig->dig_block) args.v2.acConfig.ucEncoderSel = 1; @@ -766,7 +757,6 @@ atombios_dig_transmitter_setup(struct drm_encoder *encoder, int action) } } else { args.v1.ucConfig = ATOM_TRANSMITTER_CONFIG_CLKSRC_PPLL; - args.v1.usPixelClock = cpu_to_le16((radeon_encoder->pixel_clock) / 10); switch (radeon_encoder->encoder_id) { case ENCODER_OBJECT_ID_INTERNAL_UNIPHY: @@ -874,16 +864,9 @@ radeon_atom_encoder_dpms(struct drm_encoder *encoder, int mode) DISPLAY_DEVICE_OUTPUT_CONTROL_PS_ALLOCATION args; int index = 0; bool is_dig = false; - int devices; memset(&args, 0, sizeof(args)); - /* on DPMS off we have no idea if active device is meaningful */ - if (mode != DRM_MODE_DPMS_ON && !radeon_encoder->active_device) - devices = radeon_encoder->devices; - else - devices = radeon_encoder->active_device; - DRM_DEBUG("encoder dpms %d to mode %d, devices %08x, active_devices %08x\n", radeon_encoder->encoder_id, mode, radeon_encoder->devices, radeon_encoder->active_device); @@ -914,18 +897,18 @@ radeon_atom_encoder_dpms(struct drm_encoder *encoder, int mode) break; case ENCODER_OBJECT_ID_INTERNAL_DAC1: case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DAC1: - if (devices & (ATOM_DEVICE_TV_SUPPORT)) + if (radeon_encoder->active_device & (ATOM_DEVICE_TV_SUPPORT)) index = GetIndexIntoMasterTable(COMMAND, TV1OutputControl); - else if (devices & (ATOM_DEVICE_CV_SUPPORT)) + else if (radeon_encoder->active_device & (ATOM_DEVICE_CV_SUPPORT)) index = GetIndexIntoMasterTable(COMMAND, CV1OutputControl); else index = GetIndexIntoMasterTable(COMMAND, DAC1OutputControl); break; case ENCODER_OBJECT_ID_INTERNAL_DAC2: case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DAC2: - if (devices & (ATOM_DEVICE_TV_SUPPORT)) + if (radeon_encoder->active_device & (ATOM_DEVICE_TV_SUPPORT)) index = GetIndexIntoMasterTable(COMMAND, TV1OutputControl); - else if (devices & (ATOM_DEVICE_CV_SUPPORT)) + else if (radeon_encoder->active_device & (ATOM_DEVICE_CV_SUPPORT)) index = GetIndexIntoMasterTable(COMMAND, CV1OutputControl); else index = GetIndexIntoMasterTable(COMMAND, DAC2OutputControl); @@ -1104,8 +1087,11 @@ atombios_apply_encoder_quirks(struct drm_encoder *encoder, } /* set scaler clears this on some chips */ - if (ASIC_IS_AVIVO(rdev) && (mode->flags & DRM_MODE_FLAG_INTERLACE)) - WREG32(AVIVO_D1MODE_DATA_FORMAT + radeon_crtc->crtc_offset, AVIVO_D1MODE_INTERLEAVE_EN); + if (!(radeon_encoder->active_device & (ATOM_DEVICE_TV_SUPPORT))) { + if (ASIC_IS_AVIVO(rdev) && (mode->flags & DRM_MODE_FLAG_INTERLACE)) + WREG32(AVIVO_D1MODE_DATA_FORMAT + radeon_crtc->crtc_offset, + AVIVO_D1MODE_INTERLEAVE_EN); + } } static void @@ -1153,6 +1139,7 @@ radeon_atom_encoder_mode_set(struct drm_encoder *encoder, /* setup and enable the encoder and transmitter */ atombios_dig_encoder_setup(encoder, ATOM_ENABLE); + atombios_dig_transmitter_setup(encoder, ATOM_TRANSMITTER_ACTION_INIT); atombios_dig_transmitter_setup(encoder, ATOM_TRANSMITTER_ACTION_SETUP); atombios_dig_transmitter_setup(encoder, ATOM_TRANSMITTER_ACTION_ENABLE); break; @@ -1268,8 +1255,6 @@ static void radeon_atom_encoder_prepare(struct drm_encoder *encoder) { radeon_atom_output_lock(encoder, true); radeon_atom_encoder_dpms(encoder, DRM_MODE_DPMS_OFF); - - radeon_encoder_set_active_device(encoder); } static void radeon_atom_encoder_commit(struct drm_encoder *encoder) @@ -1345,6 +1330,7 @@ radeon_atombios_set_dig_info(struct radeon_encoder *radeon_encoder) void radeon_add_atom_encoder(struct drm_device *dev, uint32_t encoder_id, uint32_t supported_device) { + struct radeon_device *rdev = dev->dev_private; struct drm_encoder *encoder; struct radeon_encoder *radeon_encoder; @@ -1364,7 +1350,10 @@ radeon_add_atom_encoder(struct drm_device *dev, uint32_t encoder_id, uint32_t su return; encoder = &radeon_encoder->base; - encoder->possible_crtcs = 0x3; + if (rdev->flags & RADEON_SINGLE_CRTC) + encoder->possible_crtcs = 0x1; + else + encoder->possible_crtcs = 0x3; encoder->possible_clones = 0; radeon_encoder->enc_priv = NULL; diff --git a/drivers/gpu/drm/radeon/radeon_family.h b/drivers/gpu/drm/radeon/radeon_family.h new file mode 100644 index 000000000000..797972e344a6 --- /dev/null +++ b/drivers/gpu/drm/radeon/radeon_family.h @@ -0,0 +1,97 @@ +/* + * Copyright 2008 Advanced Micro Devices, Inc. + * Copyright 2008 Red Hat Inc. + * Copyright 2009 Jerome Glisse. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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. + * + * Authors: Dave Airlie + * Alex Deucher + * Jerome Glisse + */ + +/* this file defines the CHIP_ and family flags used in the pciids, + * its is common between kms and non-kms because duplicating it and + * changing one place is fail. + */ +#ifndef RADEON_FAMILY_H +#define RADEON_FAMILY_H +/* + * Radeon chip families + */ +enum radeon_family { + CHIP_R100, + CHIP_RV100, + CHIP_RS100, + CHIP_RV200, + CHIP_RS200, + CHIP_R200, + CHIP_RV250, + CHIP_RS300, + CHIP_RV280, + CHIP_R300, + CHIP_R350, + CHIP_RV350, + CHIP_RV380, + CHIP_R420, + CHIP_R423, + CHIP_RV410, + CHIP_RS400, + CHIP_RS480, + CHIP_RS600, + CHIP_RS690, + CHIP_RS740, + CHIP_RV515, + CHIP_R520, + CHIP_RV530, + CHIP_RV560, + CHIP_RV570, + CHIP_R580, + CHIP_R600, + CHIP_RV610, + CHIP_RV630, + CHIP_RV670, + CHIP_RV620, + CHIP_RV635, + CHIP_RS780, + CHIP_RS880, + CHIP_RV770, + CHIP_RV730, + CHIP_RV710, + CHIP_RV740, + CHIP_LAST, +}; + +/* + * Chip flags + */ +enum radeon_chip_flags { + RADEON_FAMILY_MASK = 0x0000ffffUL, + RADEON_FLAGS_MASK = 0xffff0000UL, + RADEON_IS_MOBILITY = 0x00010000UL, + RADEON_IS_IGP = 0x00020000UL, + RADEON_SINGLE_CRTC = 0x00040000UL, + RADEON_IS_AGP = 0x00080000UL, + RADEON_HAS_HIERZ = 0x00100000UL, + RADEON_IS_PCIE = 0x00200000UL, + RADEON_NEW_MEMMAP = 0x00400000UL, + RADEON_IS_PCI = 0x00800000UL, + RADEON_IS_IGPGART = 0x01000000UL, +}; +#endif diff --git a/drivers/gpu/drm/radeon/radeon_fb.c b/drivers/gpu/drm/radeon/radeon_fb.c index 944e4fa78db5..b38c4c8e2c61 100644 --- a/drivers/gpu/drm/radeon/radeon_fb.c +++ b/drivers/gpu/drm/radeon/radeon_fb.c @@ -55,6 +55,7 @@ static struct fb_ops radeonfb_ops = { .fb_imageblit = cfb_imageblit, .fb_pan_display = drm_fb_helper_pan_display, .fb_blank = drm_fb_helper_blank, + .fb_setcmap = drm_fb_helper_setcmap, }; /** @@ -123,11 +124,13 @@ static int radeon_align_pitch(struct radeon_device *rdev, int width, int bpp, bo static struct drm_fb_helper_funcs radeon_fb_helper_funcs = { .gamma_set = radeon_crtc_fb_gamma_set, + .gamma_get = radeon_crtc_fb_gamma_get, }; int radeonfb_create(struct drm_device *dev, uint32_t fb_width, uint32_t fb_height, uint32_t surface_width, uint32_t surface_height, + uint32_t surface_depth, uint32_t surface_bpp, struct drm_framebuffer **fb_p) { struct radeon_device *rdev = dev->dev_private; @@ -145,13 +148,19 @@ int radeonfb_create(struct drm_device *dev, unsigned long tmp; bool fb_tiled = false; /* useful for testing */ u32 tiling_flags = 0; + int crtc_count; mode_cmd.width = surface_width; mode_cmd.height = surface_height; - mode_cmd.bpp = 32; + + /* avivo can't scanout real 24bpp */ + if ((surface_bpp == 24) && ASIC_IS_AVIVO(rdev)) + surface_bpp = 32; + + mode_cmd.bpp = surface_bpp; /* need to align pitch with crtc limits */ mode_cmd.pitch = radeon_align_pitch(rdev, mode_cmd.width, mode_cmd.bpp, fb_tiled) * ((mode_cmd.bpp + 1) / 8); - mode_cmd.depth = 24; + mode_cmd.depth = surface_depth; size = mode_cmd.pitch * mode_cmd.height; aligned_size = ALIGN(size, PAGE_SIZE); @@ -216,7 +225,11 @@ int radeonfb_create(struct drm_device *dev, rfbdev = info->par; rfbdev->helper.funcs = &radeon_fb_helper_funcs; rfbdev->helper.dev = dev; - ret = drm_fb_helper_init_crtc_count(&rfbdev->helper, 2, + if (rdev->flags & RADEON_SINGLE_CRTC) + crtc_count = 1; + else + crtc_count = 2; + ret = drm_fb_helper_init_crtc_count(&rfbdev->helper, crtc_count, RADEONFB_CONN_LIMIT); if (ret) goto out_unref; @@ -233,7 +246,7 @@ int radeonfb_create(struct drm_device *dev, strcpy(info->fix.id, "radeondrmfb"); - drm_fb_helper_fill_fix(info, fb->pitch); + drm_fb_helper_fill_fix(info, fb->pitch, fb->depth); info->flags = FBINFO_DEFAULT; info->fbops = &radeonfb_ops; @@ -290,13 +303,26 @@ out: return ret; } +static char *mode_option; +int radeon_parse_options(char *options) +{ + char *this_opt; + + if (!options || !*options) + return 0; + + while ((this_opt = strsep(&options, ",")) != NULL) { + if (!*this_opt) + continue; + mode_option = this_opt; + } + return 0; +} + int radeonfb_probe(struct drm_device *dev) { - int ret; - ret = drm_fb_helper_single_fb_probe(dev, &radeonfb_create); - return ret; + return drm_fb_helper_single_fb_probe(dev, 32, &radeonfb_create); } -EXPORT_SYMBOL(radeonfb_probe); int radeonfb_remove(struct drm_device *dev, struct drm_framebuffer *fb) { diff --git a/drivers/gpu/drm/radeon/radeon_gart.c b/drivers/gpu/drm/radeon/radeon_gart.c index a931af065dd4..a68d7566178c 100644 --- a/drivers/gpu/drm/radeon/radeon_gart.c +++ b/drivers/gpu/drm/radeon/radeon_gart.c @@ -140,15 +140,15 @@ void radeon_gart_unbind(struct radeon_device *rdev, unsigned offset, WARN(1, "trying to unbind memory to unitialized GART !\n"); return; } - t = offset / 4096; - p = t / (PAGE_SIZE / 4096); + t = offset / RADEON_GPU_PAGE_SIZE; + p = t / (PAGE_SIZE / RADEON_GPU_PAGE_SIZE); for (i = 0; i < pages; i++, p++) { if (rdev->gart.pages[p]) { pci_unmap_page(rdev->pdev, rdev->gart.pages_addr[p], PAGE_SIZE, PCI_DMA_BIDIRECTIONAL); rdev->gart.pages[p] = NULL; rdev->gart.pages_addr[p] = 0; - for (j = 0; j < (PAGE_SIZE / 4096); j++, t++) { + for (j = 0; j < (PAGE_SIZE / RADEON_GPU_PAGE_SIZE); j++, t++) { radeon_gart_set_page(rdev, t, 0); } } @@ -169,8 +169,8 @@ int radeon_gart_bind(struct radeon_device *rdev, unsigned offset, DRM_ERROR("trying to bind memory to unitialized GART !\n"); return -EINVAL; } - t = offset / 4096; - p = t / (PAGE_SIZE / 4096); + t = offset / RADEON_GPU_PAGE_SIZE; + p = t / (PAGE_SIZE / RADEON_GPU_PAGE_SIZE); for (i = 0; i < pages; i++, p++) { /* we need to support large memory configurations */ @@ -185,9 +185,9 @@ int radeon_gart_bind(struct radeon_device *rdev, unsigned offset, } rdev->gart.pages[p] = pagelist[i]; page_base = rdev->gart.pages_addr[p]; - for (j = 0; j < (PAGE_SIZE / 4096); j++, t++) { + for (j = 0; j < (PAGE_SIZE / RADEON_GPU_PAGE_SIZE); j++, t++) { radeon_gart_set_page(rdev, t, page_base); - page_base += 4096; + page_base += RADEON_GPU_PAGE_SIZE; } } mb(); @@ -200,14 +200,14 @@ int radeon_gart_init(struct radeon_device *rdev) if (rdev->gart.pages) { return 0; } - /* We need PAGE_SIZE >= 4096 */ - if (PAGE_SIZE < 4096) { + /* We need PAGE_SIZE >= RADEON_GPU_PAGE_SIZE */ + if (PAGE_SIZE < RADEON_GPU_PAGE_SIZE) { DRM_ERROR("Page size is smaller than GPU page size!\n"); return -EINVAL; } /* Compute table size */ rdev->gart.num_cpu_pages = rdev->mc.gtt_size / PAGE_SIZE; - rdev->gart.num_gpu_pages = rdev->mc.gtt_size / 4096; + rdev->gart.num_gpu_pages = rdev->mc.gtt_size / RADEON_GPU_PAGE_SIZE; DRM_INFO("GART: num cpu pages %u, num gpu pages %u\n", rdev->gart.num_cpu_pages, rdev->gart.num_gpu_pages); /* Allocate pages table */ diff --git a/drivers/gpu/drm/radeon/radeon_irq_kms.c b/drivers/gpu/drm/radeon/radeon_irq_kms.c index 1841145a7c4f..a0fe6232dcb6 100644 --- a/drivers/gpu/drm/radeon/radeon_irq_kms.c +++ b/drivers/gpu/drm/radeon/radeon_irq_kms.c @@ -83,11 +83,22 @@ void radeon_driver_irq_uninstall_kms(struct drm_device *dev) int radeon_irq_kms_init(struct radeon_device *rdev) { int r = 0; + int num_crtc = 2; - r = drm_vblank_init(rdev->ddev, 2); + if (rdev->flags & RADEON_SINGLE_CRTC) + num_crtc = 1; + + r = drm_vblank_init(rdev->ddev, num_crtc); if (r) { return r; } + /* enable msi */ + rdev->msi_enabled = 0; + if (rdev->family >= CHIP_RV380) { + int ret = pci_enable_msi(rdev->pdev); + if (!ret) + rdev->msi_enabled = 1; + } drm_irq_install(rdev->ddev); rdev->irq.installed = true; DRM_INFO("radeon: irq initialized.\n"); @@ -99,5 +110,7 @@ void radeon_irq_kms_fini(struct radeon_device *rdev) if (rdev->irq.installed) { rdev->irq.installed = false; drm_irq_uninstall(rdev->ddev); + if (rdev->msi_enabled) + pci_disable_msi(rdev->pdev); } } diff --git a/drivers/gpu/drm/radeon/radeon_kms.c b/drivers/gpu/drm/radeon/radeon_kms.c index 709bd892b3a9..ba128621057a 100644 --- a/drivers/gpu/drm/radeon/radeon_kms.c +++ b/drivers/gpu/drm/radeon/radeon_kms.c @@ -201,55 +201,6 @@ void radeon_disable_vblank_kms(struct drm_device *dev, int crtc) /* - * For multiple master (like multiple X). - */ -struct drm_radeon_master_private { - drm_local_map_t *sarea; - drm_radeon_sarea_t *sarea_priv; -}; - -int radeon_master_create_kms(struct drm_device *dev, struct drm_master *master) -{ - struct drm_radeon_master_private *master_priv; - unsigned long sareapage; - int ret; - - master_priv = kzalloc(sizeof(*master_priv), GFP_KERNEL); - if (master_priv == NULL) { - return -ENOMEM; - } - /* prebuild the SAREA */ - sareapage = max_t(unsigned long, SAREA_MAX, PAGE_SIZE); - ret = drm_addmap(dev, 0, sareapage, _DRM_SHM, - _DRM_CONTAINS_LOCK, - &master_priv->sarea); - if (ret) { - DRM_ERROR("SAREA setup failed\n"); - return ret; - } - master_priv->sarea_priv = master_priv->sarea->handle + sizeof(struct drm_sarea); - master_priv->sarea_priv->pfCurrentPage = 0; - master->driver_priv = master_priv; - return 0; -} - -void radeon_master_destroy_kms(struct drm_device *dev, - struct drm_master *master) -{ - struct drm_radeon_master_private *master_priv = master->driver_priv; - - if (master_priv == NULL) { - return; - } - if (master_priv->sarea) { - drm_rmmap_locked(dev, master_priv->sarea); - } - kfree(master_priv); - master->driver_priv = NULL; -} - - -/* * IOCTL. */ int radeon_dma_ioctl_kms(struct drm_device *dev, void *data, diff --git a/drivers/gpu/drm/radeon/radeon_legacy_crtc.c b/drivers/gpu/drm/radeon/radeon_legacy_crtc.c index 2b997a15fb1f..8d0b7aa87fa4 100644 --- a/drivers/gpu/drm/radeon/radeon_legacy_crtc.c +++ b/drivers/gpu/drm/radeon/radeon_legacy_crtc.c @@ -48,7 +48,7 @@ static void radeon_legacy_rmx_mode_set(struct drm_crtc *crtc, u32 fp_horz_stretch, fp_vert_stretch, fp_horz_vert_active; u32 fp_h_sync_strt_wid, fp_crtc_h_total_disp; u32 fp_v_sync_strt_wid, fp_crtc_v_total_disp; - struct radeon_native_mode *native_mode = &radeon_crtc->native_mode; + struct drm_display_mode *native_mode = &radeon_crtc->native_mode; fp_vert_stretch = RREG32(RADEON_FP_VERT_STRETCH) & (RADEON_VERT_STRETCH_RESERVED | @@ -95,19 +95,19 @@ static void radeon_legacy_rmx_mode_set(struct drm_crtc *crtc, fp_horz_vert_active = 0; - if (native_mode->panel_xres == 0 || - native_mode->panel_yres == 0) { + if (native_mode->hdisplay == 0 || + native_mode->vdisplay == 0) { hscale = false; vscale = false; } else { - if (xres > native_mode->panel_xres) - xres = native_mode->panel_xres; - if (yres > native_mode->panel_yres) - yres = native_mode->panel_yres; + if (xres > native_mode->hdisplay) + xres = native_mode->hdisplay; + if (yres > native_mode->vdisplay) + yres = native_mode->vdisplay; - if (xres == native_mode->panel_xres) + if (xres == native_mode->hdisplay) hscale = false; - if (yres == native_mode->panel_yres) + if (yres == native_mode->vdisplay) vscale = false; } @@ -119,11 +119,11 @@ static void radeon_legacy_rmx_mode_set(struct drm_crtc *crtc, else { inc = (fp_horz_stretch & RADEON_HORZ_AUTO_RATIO_INC) ? 1 : 0; scale = ((xres + inc) * RADEON_HORZ_STRETCH_RATIO_MAX) - / native_mode->panel_xres + 1; + / native_mode->hdisplay + 1; fp_horz_stretch |= (((scale) & RADEON_HORZ_STRETCH_RATIO_MASK) | RADEON_HORZ_STRETCH_BLEND | RADEON_HORZ_STRETCH_ENABLE | - ((native_mode->panel_xres/8-1) << 16)); + ((native_mode->hdisplay/8-1) << 16)); } if (!vscale) @@ -131,11 +131,11 @@ static void radeon_legacy_rmx_mode_set(struct drm_crtc *crtc, else { inc = (fp_vert_stretch & RADEON_VERT_AUTO_RATIO_INC) ? 1 : 0; scale = ((yres + inc) * RADEON_VERT_STRETCH_RATIO_MAX) - / native_mode->panel_yres + 1; + / native_mode->vdisplay + 1; fp_vert_stretch |= (((scale) & RADEON_VERT_STRETCH_RATIO_MASK) | RADEON_VERT_STRETCH_ENABLE | RADEON_VERT_STRETCH_BLEND | - ((native_mode->panel_yres-1) << 12)); + ((native_mode->vdisplay-1) << 12)); } break; case RMX_CENTER: @@ -175,8 +175,8 @@ static void radeon_legacy_rmx_mode_set(struct drm_crtc *crtc, ? RADEON_CRTC_V_SYNC_POL : 0))); - fp_horz_vert_active = (((native_mode->panel_yres) & 0xfff) | - (((native_mode->panel_xres / 8) & 0x1ff) << 16)); + fp_horz_vert_active = (((native_mode->vdisplay) & 0xfff) | + (((native_mode->hdisplay / 8) & 0x1ff) << 16)); break; case RMX_OFF: default: @@ -532,6 +532,10 @@ int radeon_crtc_set_base(struct drm_crtc *crtc, int x, int y, radeon_fb = to_radeon_framebuffer(old_fb); radeon_gem_object_unpin(radeon_fb->obj); } + + /* Bytes per pixel may have changed */ + radeon_bandwidth_update(rdev); + return 0; } @@ -664,6 +668,9 @@ static bool radeon_set_crtc_timing(struct drm_crtc *crtc, struct drm_display_mod WREG32(RADEON_DISP2_MERGE_CNTL, disp2_merge_cntl); WREG32(RADEON_CRTC2_GEN_CNTL, crtc2_gen_cntl); + + WREG32(RADEON_FP_H2_SYNC_STRT_WID, crtc_h_sync_strt_wid); + WREG32(RADEON_FP_V2_SYNC_STRT_WID, crtc_v_sync_strt_wid); } else { uint32_t crtc_gen_cntl; uint32_t crtc_ext_cntl; @@ -1015,14 +1022,11 @@ static int radeon_crtc_mode_set(struct drm_crtc *crtc, int x, int y, struct drm_framebuffer *old_fb) { struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); - struct drm_device *dev = crtc->dev; - struct radeon_device *rdev = dev->dev_private; /* TODO TV */ radeon_crtc_set_base(crtc, x, y, old_fb); radeon_set_crtc_timing(crtc, adjusted_mode); radeon_set_pll(crtc, adjusted_mode); - radeon_bandwidth_update(rdev); if (radeon_crtc->crtc_id == 0) { radeon_legacy_rmx_mode_set(crtc, mode, adjusted_mode); } else { @@ -1053,6 +1057,7 @@ static const struct drm_crtc_helper_funcs legacy_helper_funcs = { .mode_set_base = radeon_crtc_set_base, .prepare = radeon_crtc_prepare, .commit = radeon_crtc_commit, + .load_lut = radeon_crtc_load_lut, }; diff --git a/drivers/gpu/drm/radeon/radeon_legacy_encoders.c b/drivers/gpu/drm/radeon/radeon_legacy_encoders.c index b1547f700d73..00382122869b 100644 --- a/drivers/gpu/drm/radeon/radeon_legacy_encoders.c +++ b/drivers/gpu/drm/radeon/radeon_legacy_encoders.c @@ -107,8 +107,6 @@ static void radeon_legacy_lvds_prepare(struct drm_encoder *encoder) else radeon_combios_output_lock(encoder, true); radeon_legacy_lvds_dpms(encoder, DRM_MODE_DPMS_OFF); - - radeon_encoder_set_active_device(encoder); } static void radeon_legacy_lvds_commit(struct drm_encoder *encoder) @@ -192,6 +190,8 @@ static bool radeon_legacy_lvds_mode_fixup(struct drm_encoder *encoder, { struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); + /* set the active encoder to connector routing */ + radeon_encoder_set_active_device(encoder); drm_mode_set_crtcinfo(adjusted_mode, 0); if (radeon_encoder->rmx_type != RMX_OFF) @@ -218,7 +218,8 @@ static bool radeon_legacy_primary_dac_mode_fixup(struct drm_encoder *encoder, struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode) { - + /* set the active encoder to connector routing */ + radeon_encoder_set_active_device(encoder); drm_mode_set_crtcinfo(adjusted_mode, 0); return true; @@ -272,7 +273,6 @@ static void radeon_legacy_primary_dac_prepare(struct drm_encoder *encoder) else radeon_combios_output_lock(encoder, true); radeon_legacy_primary_dac_dpms(encoder, DRM_MODE_DPMS_OFF); - radeon_encoder_set_active_device(encoder); } static void radeon_legacy_primary_dac_commit(struct drm_encoder *encoder) @@ -468,7 +468,6 @@ static void radeon_legacy_tmds_int_prepare(struct drm_encoder *encoder) else radeon_combios_output_lock(encoder, true); radeon_legacy_tmds_int_dpms(encoder, DRM_MODE_DPMS_OFF); - radeon_encoder_set_active_device(encoder); } static void radeon_legacy_tmds_int_commit(struct drm_encoder *encoder) @@ -543,6 +542,14 @@ static void radeon_legacy_tmds_int_mode_set(struct drm_encoder *encoder, fp_gen_cntl &= ~(RADEON_FP_FPON | RADEON_FP_TMDS_EN); + fp_gen_cntl &= ~(RADEON_FP_RMX_HVSYNC_CONTROL_EN | + RADEON_FP_DFP_SYNC_SEL | + RADEON_FP_CRT_SYNC_SEL | + RADEON_FP_CRTC_LOCK_8DOT | + RADEON_FP_USE_SHADOW_EN | + RADEON_FP_CRTC_USE_SHADOW_VEND | + RADEON_FP_CRT_SYNC_ALT); + if (1) /* FIXME rgbBits == 8 */ fp_gen_cntl |= RADEON_FP_PANEL_FORMAT; /* 24 bit format */ else @@ -556,7 +563,7 @@ static void radeon_legacy_tmds_int_mode_set(struct drm_encoder *encoder, else fp_gen_cntl |= R200_FP_SOURCE_SEL_CRTC1; } else - fp_gen_cntl |= RADEON_FP_SEL_CRTC1; + fp_gen_cntl &= ~RADEON_FP_SEL_CRTC2; } else { if (ASIC_IS_R300(rdev) || rdev->family == CHIP_R200) { fp_gen_cntl &= ~R200_FP_SOURCE_SEL_MASK; @@ -593,7 +600,8 @@ static bool radeon_legacy_tmds_ext_mode_fixup(struct drm_encoder *encoder, struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode) { - + /* set the active encoder to connector routing */ + radeon_encoder_set_active_device(encoder); drm_mode_set_crtcinfo(adjusted_mode, 0); return true; @@ -636,7 +644,6 @@ static void radeon_legacy_tmds_ext_prepare(struct drm_encoder *encoder) else radeon_combios_output_lock(encoder, true); radeon_legacy_tmds_ext_dpms(encoder, DRM_MODE_DPMS_OFF); - radeon_encoder_set_active_device(encoder); } static void radeon_legacy_tmds_ext_commit(struct drm_encoder *encoder) @@ -735,7 +742,8 @@ static bool radeon_legacy_tv_dac_mode_fixup(struct drm_encoder *encoder, struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode) { - + /* set the active encoder to connector routing */ + radeon_encoder_set_active_device(encoder); drm_mode_set_crtcinfo(adjusted_mode, 0); return true; @@ -839,7 +847,6 @@ static void radeon_legacy_tv_dac_prepare(struct drm_encoder *encoder) else radeon_combios_output_lock(encoder, true); radeon_legacy_tv_dac_dpms(encoder, DRM_MODE_DPMS_OFF); - radeon_encoder_set_active_device(encoder); } static void radeon_legacy_tv_dac_commit(struct drm_encoder *encoder) @@ -881,7 +888,7 @@ static void radeon_legacy_tv_dac_mode_set(struct drm_encoder *encoder, R420_TV_DAC_DACADJ_MASK | R420_TV_DAC_RDACPD | R420_TV_DAC_GDACPD | - R420_TV_DAC_GDACPD | + R420_TV_DAC_BDACPD | R420_TV_DAC_TVENABLE); } else { tv_dac_cntl &= ~(RADEON_TV_DAC_STD_MASK | @@ -889,7 +896,7 @@ static void radeon_legacy_tv_dac_mode_set(struct drm_encoder *encoder, RADEON_TV_DAC_DACADJ_MASK | RADEON_TV_DAC_RDACPD | RADEON_TV_DAC_GDACPD | - RADEON_TV_DAC_GDACPD); + RADEON_TV_DAC_BDACPD); } /* FIXME TV */ @@ -1318,7 +1325,10 @@ radeon_add_legacy_encoder(struct drm_device *dev, uint32_t encoder_id, uint32_t return; encoder = &radeon_encoder->base; - encoder->possible_crtcs = 0x3; + if (rdev->flags & RADEON_SINGLE_CRTC) + encoder->possible_crtcs = 0x1; + else + encoder->possible_crtcs = 0x3; encoder->possible_clones = 0; radeon_encoder->enc_priv = NULL; diff --git a/drivers/gpu/drm/radeon/radeon_mode.h b/drivers/gpu/drm/radeon/radeon_mode.h index 570a58729daf..ace726aa0d76 100644 --- a/drivers/gpu/drm/radeon/radeon_mode.h +++ b/drivers/gpu/drm/radeon/radeon_mode.h @@ -172,6 +172,7 @@ enum radeon_connector_table { struct radeon_mode_info { struct atom_context *atom_context; + struct card_info *atom_card_info; enum radeon_connector_table connector_table; bool mode_config_initialized; struct radeon_crtc *crtcs[2]; @@ -186,17 +187,6 @@ struct radeon_mode_info { }; -struct radeon_native_mode { - /* preferred mode */ - uint32_t panel_xres, panel_yres; - uint32_t hoverplus, hsync_width; - uint32_t hblank; - uint32_t voverplus, vsync_width; - uint32_t vblank; - uint32_t dotclock; - uint32_t flags; -}; - #define MAX_H_CODE_TIMING_LEN 32 #define MAX_V_CODE_TIMING_LEN 32 @@ -228,7 +218,7 @@ struct radeon_crtc { enum radeon_rmx_type rmx_type; fixed20_12 vsc; fixed20_12 hsc; - struct radeon_native_mode native_mode; + struct drm_display_mode native_mode; }; struct radeon_encoder_primary_dac { @@ -248,7 +238,7 @@ struct radeon_encoder_lvds { bool use_bios_dividers; uint32_t lvds_gen_cntl; /* panel mode */ - struct radeon_native_mode native_mode; + struct drm_display_mode native_mode; }; struct radeon_encoder_tv_dac { @@ -271,6 +261,16 @@ struct radeon_encoder_int_tmds { struct radeon_tmds_pll tmds_pll[4]; }; +/* spread spectrum */ +struct radeon_atom_ss { + uint16_t percentage; + uint8_t type; + uint8_t step; + uint8_t delay; + uint8_t range; + uint8_t refdiv; +}; + struct radeon_encoder_atom_dig { /* atom dig */ bool coherent_mode; @@ -278,8 +278,9 @@ struct radeon_encoder_atom_dig { /* atom lvds */ uint32_t lvds_misc; uint16_t panel_pwr_delay; + struct radeon_atom_ss *ss; /* panel mode */ - struct radeon_native_mode native_mode; + struct drm_display_mode native_mode; }; struct radeon_encoder_atom_dac { @@ -294,7 +295,7 @@ struct radeon_encoder { uint32_t flags; uint32_t pixel_clock; enum radeon_rmx_type rmx_type; - struct radeon_native_mode native_mode; + struct drm_display_mode native_mode; void *enc_priv; }; @@ -308,12 +309,15 @@ struct radeon_connector { uint32_t connector_id; uint32_t devices; struct radeon_i2c_chan *ddc_bus; + /* some systems have a an hdmi and vga port with a shared ddc line */ + bool shared_ddc; bool use_digital; /* we need to mind the EDID between detect and get modes due to analog/digital/tvencoder */ struct edid *edid; void *con_priv; bool dac_load_detect; + uint16_t connector_object_id; }; struct radeon_framebuffer { @@ -407,6 +411,8 @@ extern void radeon_combios_encoder_dpms_scratch_regs(struct drm_encoder *encoder, bool on); extern void radeon_crtc_fb_gamma_set(struct drm_crtc *crtc, u16 red, u16 green, u16 blue, int regno); +extern void radeon_crtc_fb_gamma_get(struct drm_crtc *crtc, u16 *red, u16 *green, + u16 *blue, int regno); struct drm_framebuffer *radeon_framebuffer_create(struct drm_device *dev, struct drm_mode_fb_cmd *mode_cmd, struct drm_gem_object *obj); diff --git a/drivers/gpu/drm/radeon/radeon_object.c b/drivers/gpu/drm/radeon/radeon_object.c index 73af463b7a59..1f056dadc5c2 100644 --- a/drivers/gpu/drm/radeon/radeon_object.c +++ b/drivers/gpu/drm/radeon/radeon_object.c @@ -400,11 +400,9 @@ void radeon_object_list_add_object(struct radeon_object_list *lobj, int radeon_object_list_reserve(struct list_head *head) { struct radeon_object_list *lobj; - struct list_head *i; int r; - list_for_each(i, head) { - lobj = list_entry(i, struct radeon_object_list, list); + list_for_each_entry(lobj, head, list){ if (!lobj->robj->pin_count) { r = radeon_object_reserve(lobj->robj, true); if (unlikely(r != 0)) { @@ -420,13 +418,10 @@ int radeon_object_list_reserve(struct list_head *head) void radeon_object_list_unreserve(struct list_head *head) { struct radeon_object_list *lobj; - struct list_head *i; - list_for_each(i, head) { - lobj = list_entry(i, struct radeon_object_list, list); + list_for_each_entry(lobj, head, list) { if (!lobj->robj->pin_count) { radeon_object_unreserve(lobj->robj); - } else { } } } @@ -436,7 +431,6 @@ int radeon_object_list_validate(struct list_head *head, void *fence) struct radeon_object_list *lobj; struct radeon_object *robj; struct radeon_fence *old_fence = NULL; - struct list_head *i; int r; r = radeon_object_list_reserve(head); @@ -444,8 +438,7 @@ int radeon_object_list_validate(struct list_head *head, void *fence) radeon_object_list_unreserve(head); return r; } - list_for_each(i, head) { - lobj = list_entry(i, struct radeon_object_list, list); + list_for_each_entry(lobj, head, list) { robj = lobj->robj; if (!robj->pin_count) { if (lobj->wdomain) { @@ -482,10 +475,8 @@ void radeon_object_list_unvalidate(struct list_head *head) { struct radeon_object_list *lobj; struct radeon_fence *old_fence = NULL; - struct list_head *i; - list_for_each(i, head) { - lobj = list_entry(i, struct radeon_object_list, list); + list_for_each_entry(lobj, head, list) { old_fence = (struct radeon_fence *)lobj->robj->tobj.sync_obj; lobj->robj->tobj.sync_obj = NULL; if (old_fence) { diff --git a/drivers/gpu/drm/radeon/radeon_pm.c b/drivers/gpu/drm/radeon/radeon_pm.c new file mode 100644 index 000000000000..46146c6a2a06 --- /dev/null +++ b/drivers/gpu/drm/radeon/radeon_pm.c @@ -0,0 +1,65 @@ +/* + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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. + * + * Authors: RafaÅ‚ MiÅ‚ecki <zajec5@gmail.com> + */ +#include "drmP.h" +#include "radeon.h" + +int radeon_debugfs_pm_init(struct radeon_device *rdev); + +int radeon_pm_init(struct radeon_device *rdev) +{ + if (radeon_debugfs_pm_init(rdev)) { + DRM_ERROR("Failed to register debugfs file for CP !\n"); + } + + return 0; +} + +/* + * Debugfs info + */ +#if defined(CONFIG_DEBUG_FS) + +static int radeon_debugfs_pm_info(struct seq_file *m, void *data) +{ + struct drm_info_node *node = (struct drm_info_node *) m->private; + struct drm_device *dev = node->minor->dev; + struct radeon_device *rdev = dev->dev_private; + + seq_printf(m, "engine clock: %u0 Hz\n", radeon_get_engine_clock(rdev)); + seq_printf(m, "memory clock: %u0 Hz\n", radeon_get_memory_clock(rdev)); + + return 0; +} + +static struct drm_info_list radeon_pm_info_list[] = { + {"radeon_pm_info", radeon_debugfs_pm_info, 0, NULL}, +}; +#endif + +int radeon_debugfs_pm_init(struct radeon_device *rdev) +{ +#if defined(CONFIG_DEBUG_FS) + return radeon_debugfs_add_files(rdev, radeon_pm_info_list, ARRAY_SIZE(radeon_pm_info_list)); +#else + return 0; +#endif +} diff --git a/drivers/gpu/drm/radeon/radeon_reg.h b/drivers/gpu/drm/radeon/radeon_reg.h index 21da871a793c..29ab75903ec1 100644 --- a/drivers/gpu/drm/radeon/radeon_reg.h +++ b/drivers/gpu/drm/radeon/radeon_reg.h @@ -290,6 +290,8 @@ #define RADEON_BUS_CNTL 0x0030 # define RADEON_BUS_MASTER_DIS (1 << 6) # define RADEON_BUS_BIOS_DIS_ROM (1 << 12) +# define RS600_BUS_MASTER_DIS (1 << 14) +# define RS600_MSI_REARM (1 << 20) /* rs600/rs690/rs740 */ # define RADEON_BUS_RD_DISCARD_EN (1 << 24) # define RADEON_BUS_RD_ABORT_EN (1 << 25) # define RADEON_BUS_MSTR_DISCONNECT_EN (1 << 28) @@ -297,6 +299,9 @@ # define RADEON_BUS_READ_BURST (1 << 30) #define RADEON_BUS_CNTL1 0x0034 # define RADEON_BUS_WAIT_ON_LOCK_EN (1 << 4) +/* rv370/rv380, rv410, r423/r430/r480, r5xx */ +#define RADEON_MSI_REARM_EN 0x0160 +# define RV370_MSI_REARM_EN (1 << 0) /* #define RADEON_PCIE_INDEX 0x0030 */ /* #define RADEON_PCIE_DATA 0x0034 */ @@ -3311,6 +3316,7 @@ #define RADEON_AIC_CNTL 0x01d0 # define RADEON_PCIGART_TRANSLATE_EN (1 << 0) # define RADEON_DIS_OUT_OF_PCI_GART_ACCESS (1 << 1) +# define RS400_MSI_REARM (1 << 3) /* rs400/rs480 */ #define RADEON_AIC_LO_ADDR 0x01dc #define RADEON_AIC_PT_BASE 0x01d8 #define RADEON_AIC_HI_ADDR 0x01e0 @@ -3333,6 +3339,7 @@ # define RADEON_CP_PACKET_MAX_DWORDS (1 << 12) # define RADEON_CP_PACKET0_REG_MASK 0x000007ff # define R300_CP_PACKET0_REG_MASK 0x00001fff +# define R600_CP_PACKET0_REG_MASK 0x0000ffff # define RADEON_CP_PACKET1_REG0_MASK 0x000007ff # define RADEON_CP_PACKET1_REG1_MASK 0x003ff800 diff --git a/drivers/gpu/drm/radeon/radeon_test.c b/drivers/gpu/drm/radeon/radeon_test.c index 03c33cf4e14c..f8a465d9a1cf 100644 --- a/drivers/gpu/drm/radeon/radeon_test.c +++ b/drivers/gpu/drm/radeon/radeon_test.c @@ -42,7 +42,7 @@ void radeon_test_moves(struct radeon_device *rdev) /* Number of tests = * (Total GTT - IB pool - writeback page - ring buffer) / test size */ - n = (rdev->mc.gtt_size - RADEON_IB_POOL_SIZE*64*1024 - 4096 - + n = (rdev->mc.gtt_size - RADEON_IB_POOL_SIZE*64*1024 - RADEON_GPU_PAGE_SIZE - rdev->cp.ring_size) / size; gtt_obj = kzalloc(n * sizeof(*gtt_obj), GFP_KERNEL); @@ -102,7 +102,7 @@ void radeon_test_moves(struct radeon_device *rdev) goto out_cleanup; } - r = radeon_copy(rdev, gtt_addr, vram_addr, size / 4096, fence); + r = radeon_copy(rdev, gtt_addr, vram_addr, size / RADEON_GPU_PAGE_SIZE, fence); if (r) { DRM_ERROR("Failed GTT->VRAM copy %d\n", i); goto out_cleanup; @@ -145,7 +145,7 @@ void radeon_test_moves(struct radeon_device *rdev) goto out_cleanup; } - r = radeon_copy(rdev, vram_addr, gtt_addr, size / 4096, fence); + r = radeon_copy(rdev, vram_addr, gtt_addr, size / RADEON_GPU_PAGE_SIZE, fence); if (r) { DRM_ERROR("Failed VRAM->GTT copy %d\n", i); goto out_cleanup; diff --git a/drivers/gpu/drm/radeon/radeon_ttm.c b/drivers/gpu/drm/radeon/radeon_ttm.c index acd889c94549..1381e06d6af3 100644 --- a/drivers/gpu/drm/radeon/radeon_ttm.c +++ b/drivers/gpu/drm/radeon/radeon_ttm.c @@ -295,6 +295,12 @@ static int radeon_move_vram_ram(struct ttm_buffer_object *bo, if (unlikely(r)) { return r; } + + r = ttm_tt_set_placement_caching(bo->ttm, tmp_mem.placement); + if (unlikely(r)) { + goto out_cleanup; + } + r = ttm_tt_bind(bo->ttm, &tmp_mem); if (unlikely(r)) { goto out_cleanup; @@ -530,7 +536,7 @@ void radeon_ttm_fini(struct radeon_device *rdev) } static struct vm_operations_struct radeon_ttm_vm_ops; -static struct vm_operations_struct *ttm_vm_ops = NULL; +static const struct vm_operations_struct *ttm_vm_ops = NULL; static int radeon_ttm_fault(struct vm_area_struct *vma, struct vm_fault *vmf) { @@ -689,9 +695,6 @@ struct ttm_backend *radeon_ttm_backend_create(struct radeon_device *rdev) #define RADEON_DEBUGFS_MEM_TYPES 2 -static struct drm_info_list radeon_mem_types_list[RADEON_DEBUGFS_MEM_TYPES]; -static char radeon_mem_types_names[RADEON_DEBUGFS_MEM_TYPES][32]; - #if defined(CONFIG_DEBUG_FS) static int radeon_mm_dump_table(struct seq_file *m, void *data) { @@ -711,9 +714,11 @@ static int radeon_mm_dump_table(struct seq_file *m, void *data) static int radeon_ttm_debugfs_init(struct radeon_device *rdev) { +#if defined(CONFIG_DEBUG_FS) + static struct drm_info_list radeon_mem_types_list[RADEON_DEBUGFS_MEM_TYPES]; + static char radeon_mem_types_names[RADEON_DEBUGFS_MEM_TYPES][32]; unsigned i; -#if defined(CONFIG_DEBUG_FS) for (i = 0; i < RADEON_DEBUGFS_MEM_TYPES; i++) { if (i == 0) sprintf(radeon_mem_types_names[i], "radeon_vram_mm"); diff --git a/drivers/gpu/drm/radeon/rs100d.h b/drivers/gpu/drm/radeon/rs100d.h new file mode 100644 index 000000000000..48a913a06cfd --- /dev/null +++ b/drivers/gpu/drm/radeon/rs100d.h @@ -0,0 +1,40 @@ +/* + * Copyright 2008 Advanced Micro Devices, Inc. + * Copyright 2008 Red Hat Inc. + * Copyright 2009 Jerome Glisse. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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. + * + * Authors: Dave Airlie + * Alex Deucher + * Jerome Glisse + */ +#ifndef __RS100D_H__ +#define __RS100D_H__ + +/* Registers */ +#define R_00015C_NB_TOM 0x00015C +#define S_00015C_MC_FB_START(x) (((x) & 0xFFFF) << 0) +#define G_00015C_MC_FB_START(x) (((x) >> 0) & 0xFFFF) +#define C_00015C_MC_FB_START 0xFFFF0000 +#define S_00015C_MC_FB_TOP(x) (((x) & 0xFFFF) << 16) +#define G_00015C_MC_FB_TOP(x) (((x) >> 16) & 0xFFFF) +#define C_00015C_MC_FB_TOP 0x0000FFFF + +#endif diff --git a/drivers/gpu/drm/radeon/rs400.c b/drivers/gpu/drm/radeon/rs400.c index a3fbdad938c7..ca037160a582 100644 --- a/drivers/gpu/drm/radeon/rs400.c +++ b/drivers/gpu/drm/radeon/rs400.c @@ -27,27 +27,12 @@ */ #include <linux/seq_file.h> #include <drm/drmP.h> -#include "radeon_reg.h" #include "radeon.h" +#include "rs400d.h" -/* rs400,rs480 depends on : */ -void r100_hdp_reset(struct radeon_device *rdev); -void r100_mc_disable_clients(struct radeon_device *rdev); -int r300_mc_wait_for_idle(struct radeon_device *rdev); -void r420_pipes_init(struct radeon_device *rdev); +/* This files gather functions specifics to : rs400,rs480 */ +static int rs400_debugfs_pcie_gart_info_init(struct radeon_device *rdev); -/* This files gather functions specifics to : - * rs400,rs480 - * - * Some of these functions might be used by newer ASICs. - */ -void rs400_gpu_init(struct radeon_device *rdev); -int rs400_debugfs_pcie_gart_info_init(struct radeon_device *rdev); - - -/* - * GART functions. - */ void rs400_gart_adjust_size(struct radeon_device *rdev) { /* Check gart size */ @@ -238,61 +223,6 @@ int rs400_gart_set_page(struct radeon_device *rdev, int i, uint64_t addr) return 0; } - -/* - * MC functions. - */ -int rs400_mc_init(struct radeon_device *rdev) -{ - uint32_t tmp; - int r; - - if (r100_debugfs_rbbm_init(rdev)) { - DRM_ERROR("Failed to register debugfs file for RBBM !\n"); - } - - rs400_gpu_init(rdev); - rs400_gart_disable(rdev); - rdev->mc.gtt_location = rdev->mc.mc_vram_size; - rdev->mc.gtt_location += (rdev->mc.gtt_size - 1); - rdev->mc.gtt_location &= ~(rdev->mc.gtt_size - 1); - r = radeon_mc_setup(rdev); - if (r) { - return r; - } - - r100_mc_disable_clients(rdev); - if (r300_mc_wait_for_idle(rdev)) { - printk(KERN_WARNING "Failed to wait MC idle while " - "programming pipes. Bad things might happen.\n"); - } - - tmp = rdev->mc.vram_location + rdev->mc.mc_vram_size - 1; - tmp = REG_SET(RADEON_MC_FB_TOP, tmp >> 16); - tmp |= REG_SET(RADEON_MC_FB_START, rdev->mc.vram_location >> 16); - WREG32(RADEON_MC_FB_LOCATION, tmp); - tmp = RREG32(RADEON_HOST_PATH_CNTL) | RADEON_HP_LIN_RD_CACHE_DIS; - WREG32(RADEON_HOST_PATH_CNTL, tmp | RADEON_HDP_SOFT_RESET | RADEON_HDP_READ_BUFFER_INVALIDATE); - (void)RREG32(RADEON_HOST_PATH_CNTL); - WREG32(RADEON_HOST_PATH_CNTL, tmp); - (void)RREG32(RADEON_HOST_PATH_CNTL); - - return 0; -} - -void rs400_mc_fini(struct radeon_device *rdev) -{ -} - - -/* - * Global GPU functions - */ -void rs400_errata(struct radeon_device *rdev) -{ - rdev->pll_errata = 0; -} - void rs400_gpu_init(struct radeon_device *rdev) { /* FIXME: HDP same place on rs400 ? */ @@ -305,10 +235,6 @@ void rs400_gpu_init(struct radeon_device *rdev) } } - -/* - * VRAM info. - */ void rs400_vram_info(struct radeon_device *rdev) { rs400_gart_adjust_size(rdev); @@ -319,10 +245,6 @@ void rs400_vram_info(struct radeon_device *rdev) r100_vram_init_sizes(rdev); } - -/* - * Indirect registers accessor - */ uint32_t rs400_mc_rreg(struct radeon_device *rdev, uint32_t reg) { uint32_t r; @@ -340,10 +262,6 @@ void rs400_mc_wreg(struct radeon_device *rdev, uint32_t reg, uint32_t v) WREG32(RS480_NB_MC_INDEX, 0xff); } - -/* - * Debugfs info - */ #if defined(CONFIG_DEBUG_FS) static int rs400_debugfs_gart_info(struct seq_file *m, void *data) { @@ -419,7 +337,7 @@ static struct drm_info_list rs400_gart_info_list[] = { }; #endif -int rs400_debugfs_pcie_gart_info_init(struct radeon_device *rdev) +static int rs400_debugfs_pcie_gart_info_init(struct radeon_device *rdev) { #if defined(CONFIG_DEBUG_FS) return radeon_debugfs_add_files(rdev, rs400_gart_info_list, 1); @@ -427,3 +345,190 @@ int rs400_debugfs_pcie_gart_info_init(struct radeon_device *rdev) return 0; #endif } + +static int rs400_mc_init(struct radeon_device *rdev) +{ + int r; + u32 tmp; + + /* Setup GPU memory space */ + tmp = G_00015C_MC_FB_START(RREG32(R_00015C_NB_TOM)); + rdev->mc.vram_location = G_00015C_MC_FB_START(tmp) << 16; + rdev->mc.gtt_location = 0xFFFFFFFFUL; + r = radeon_mc_setup(rdev); + if (r) + return r; + return 0; +} + +void rs400_mc_program(struct radeon_device *rdev) +{ + struct r100_mc_save save; + + /* Stops all mc clients */ + r100_mc_stop(rdev, &save); + + /* Wait for mc idle */ + if (r300_mc_wait_for_idle(rdev)) + dev_warn(rdev->dev, "Wait MC idle timeout before updating MC.\n"); + WREG32(R_000148_MC_FB_LOCATION, + S_000148_MC_FB_START(rdev->mc.vram_start >> 16) | + S_000148_MC_FB_TOP(rdev->mc.vram_end >> 16)); + + r100_mc_resume(rdev, &save); +} + +static int rs400_startup(struct radeon_device *rdev) +{ + int r; + + rs400_mc_program(rdev); + /* Resume clock */ + r300_clock_startup(rdev); + /* Initialize GPU configuration (# pipes, ...) */ + rs400_gpu_init(rdev); + /* Initialize GART (initialize after TTM so we can allocate + * memory through TTM but finalize after TTM) */ + r = rs400_gart_enable(rdev); + if (r) + return r; + /* Enable IRQ */ + rdev->irq.sw_int = true; + r100_irq_set(rdev); + /* 1M ring buffer */ + r = r100_cp_init(rdev, 1024 * 1024); + if (r) { + dev_err(rdev->dev, "failled initializing CP (%d).\n", r); + return r; + } + r = r100_wb_init(rdev); + if (r) + dev_err(rdev->dev, "failled initializing WB (%d).\n", r); + r = r100_ib_init(rdev); + if (r) { + dev_err(rdev->dev, "failled initializing IB (%d).\n", r); + return r; + } + return 0; +} + +int rs400_resume(struct radeon_device *rdev) +{ + /* Make sur GART are not working */ + rs400_gart_disable(rdev); + /* Resume clock before doing reset */ + r300_clock_startup(rdev); + /* setup MC before calling post tables */ + rs400_mc_program(rdev); + /* Reset gpu before posting otherwise ATOM will enter infinite loop */ + if (radeon_gpu_reset(rdev)) { + dev_warn(rdev->dev, "GPU reset failed ! (0xE40=0x%08X, 0x7C0=0x%08X)\n", + RREG32(R_000E40_RBBM_STATUS), + RREG32(R_0007C0_CP_STAT)); + } + /* post */ + radeon_combios_asic_init(rdev->ddev); + /* Resume clock after posting */ + r300_clock_startup(rdev); + return rs400_startup(rdev); +} + +int rs400_suspend(struct radeon_device *rdev) +{ + r100_cp_disable(rdev); + r100_wb_disable(rdev); + r100_irq_disable(rdev); + rs400_gart_disable(rdev); + return 0; +} + +void rs400_fini(struct radeon_device *rdev) +{ + rs400_suspend(rdev); + r100_cp_fini(rdev); + r100_wb_fini(rdev); + r100_ib_fini(rdev); + radeon_gem_fini(rdev); + rs400_gart_fini(rdev); + radeon_irq_kms_fini(rdev); + radeon_fence_driver_fini(rdev); + radeon_object_fini(rdev); + radeon_atombios_fini(rdev); + kfree(rdev->bios); + rdev->bios = NULL; +} + +int rs400_init(struct radeon_device *rdev) +{ + int r; + + /* Disable VGA */ + r100_vga_render_disable(rdev); + /* Initialize scratch registers */ + radeon_scratch_init(rdev); + /* Initialize surface registers */ + radeon_surface_init(rdev); + /* TODO: disable VGA need to use VGA request */ + /* BIOS*/ + if (!radeon_get_bios(rdev)) { + if (ASIC_IS_AVIVO(rdev)) + return -EINVAL; + } + if (rdev->is_atom_bios) { + dev_err(rdev->dev, "Expecting combios for RS400/RS480 GPU\n"); + return -EINVAL; + } else { + r = radeon_combios_init(rdev); + if (r) + return r; + } + /* Reset gpu before posting otherwise ATOM will enter infinite loop */ + if (radeon_gpu_reset(rdev)) { + dev_warn(rdev->dev, + "GPU reset failed ! (0xE40=0x%08X, 0x7C0=0x%08X)\n", + RREG32(R_000E40_RBBM_STATUS), + RREG32(R_0007C0_CP_STAT)); + } + /* check if cards are posted or not */ + if (!radeon_card_posted(rdev) && rdev->bios) { + DRM_INFO("GPU not posted. posting now...\n"); + radeon_combios_asic_init(rdev->ddev); + } + /* Initialize clocks */ + radeon_get_clock_info(rdev->ddev); + /* Get vram informations */ + rs400_vram_info(rdev); + /* Initialize memory controller (also test AGP) */ + r = rs400_mc_init(rdev); + if (r) + return r; + /* Fence driver */ + r = radeon_fence_driver_init(rdev); + if (r) + return r; + r = radeon_irq_kms_init(rdev); + if (r) + return r; + /* Memory manager */ + r = radeon_object_init(rdev); + if (r) + return r; + r = rs400_gart_init(rdev); + if (r) + return r; + r300_set_reg_safe(rdev); + rdev->accel_working = true; + r = rs400_startup(rdev); + if (r) { + /* Somethings want wront with the accel init stop accel */ + dev_err(rdev->dev, "Disabling GPU acceleration\n"); + rs400_suspend(rdev); + r100_cp_fini(rdev); + r100_wb_fini(rdev); + r100_ib_fini(rdev); + rs400_gart_fini(rdev); + radeon_irq_kms_fini(rdev); + rdev->accel_working = false; + } + return 0; +} diff --git a/drivers/gpu/drm/radeon/rs400d.h b/drivers/gpu/drm/radeon/rs400d.h new file mode 100644 index 000000000000..6d8bac58ced9 --- /dev/null +++ b/drivers/gpu/drm/radeon/rs400d.h @@ -0,0 +1,160 @@ +/* + * Copyright 2008 Advanced Micro Devices, Inc. + * Copyright 2008 Red Hat Inc. + * Copyright 2009 Jerome Glisse. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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. + * + * Authors: Dave Airlie + * Alex Deucher + * Jerome Glisse + */ +#ifndef __RS400D_H__ +#define __RS400D_H__ + +/* Registers */ +#define R_000148_MC_FB_LOCATION 0x000148 +#define S_000148_MC_FB_START(x) (((x) & 0xFFFF) << 0) +#define G_000148_MC_FB_START(x) (((x) >> 0) & 0xFFFF) +#define C_000148_MC_FB_START 0xFFFF0000 +#define S_000148_MC_FB_TOP(x) (((x) & 0xFFFF) << 16) +#define G_000148_MC_FB_TOP(x) (((x) >> 16) & 0xFFFF) +#define C_000148_MC_FB_TOP 0x0000FFFF +#define R_00015C_NB_TOM 0x00015C +#define S_00015C_MC_FB_START(x) (((x) & 0xFFFF) << 0) +#define G_00015C_MC_FB_START(x) (((x) >> 0) & 0xFFFF) +#define C_00015C_MC_FB_START 0xFFFF0000 +#define S_00015C_MC_FB_TOP(x) (((x) & 0xFFFF) << 16) +#define G_00015C_MC_FB_TOP(x) (((x) >> 16) & 0xFFFF) +#define C_00015C_MC_FB_TOP 0x0000FFFF +#define R_0007C0_CP_STAT 0x0007C0 +#define S_0007C0_MRU_BUSY(x) (((x) & 0x1) << 0) +#define G_0007C0_MRU_BUSY(x) (((x) >> 0) & 0x1) +#define C_0007C0_MRU_BUSY 0xFFFFFFFE +#define S_0007C0_MWU_BUSY(x) (((x) & 0x1) << 1) +#define G_0007C0_MWU_BUSY(x) (((x) >> 1) & 0x1) +#define C_0007C0_MWU_BUSY 0xFFFFFFFD +#define S_0007C0_RSIU_BUSY(x) (((x) & 0x1) << 2) +#define G_0007C0_RSIU_BUSY(x) (((x) >> 2) & 0x1) +#define C_0007C0_RSIU_BUSY 0xFFFFFFFB +#define S_0007C0_RCIU_BUSY(x) (((x) & 0x1) << 3) +#define G_0007C0_RCIU_BUSY(x) (((x) >> 3) & 0x1) +#define C_0007C0_RCIU_BUSY 0xFFFFFFF7 +#define S_0007C0_CSF_PRIMARY_BUSY(x) (((x) & 0x1) << 9) +#define G_0007C0_CSF_PRIMARY_BUSY(x) (((x) >> 9) & 0x1) +#define C_0007C0_CSF_PRIMARY_BUSY 0xFFFFFDFF +#define S_0007C0_CSF_INDIRECT_BUSY(x) (((x) & 0x1) << 10) +#define G_0007C0_CSF_INDIRECT_BUSY(x) (((x) >> 10) & 0x1) +#define C_0007C0_CSF_INDIRECT_BUSY 0xFFFFFBFF +#define S_0007C0_CSQ_PRIMARY_BUSY(x) (((x) & 0x1) << 11) +#define G_0007C0_CSQ_PRIMARY_BUSY(x) (((x) >> 11) & 0x1) +#define C_0007C0_CSQ_PRIMARY_BUSY 0xFFFFF7FF +#define S_0007C0_CSQ_INDIRECT_BUSY(x) (((x) & 0x1) << 12) +#define G_0007C0_CSQ_INDIRECT_BUSY(x) (((x) >> 12) & 0x1) +#define C_0007C0_CSQ_INDIRECT_BUSY 0xFFFFEFFF +#define S_0007C0_CSI_BUSY(x) (((x) & 0x1) << 13) +#define G_0007C0_CSI_BUSY(x) (((x) >> 13) & 0x1) +#define C_0007C0_CSI_BUSY 0xFFFFDFFF +#define S_0007C0_CSF_INDIRECT2_BUSY(x) (((x) & 0x1) << 14) +#define G_0007C0_CSF_INDIRECT2_BUSY(x) (((x) >> 14) & 0x1) +#define C_0007C0_CSF_INDIRECT2_BUSY 0xFFFFBFFF +#define S_0007C0_CSQ_INDIRECT2_BUSY(x) (((x) & 0x1) << 15) +#define G_0007C0_CSQ_INDIRECT2_BUSY(x) (((x) >> 15) & 0x1) +#define C_0007C0_CSQ_INDIRECT2_BUSY 0xFFFF7FFF +#define S_0007C0_GUIDMA_BUSY(x) (((x) & 0x1) << 28) +#define G_0007C0_GUIDMA_BUSY(x) (((x) >> 28) & 0x1) +#define C_0007C0_GUIDMA_BUSY 0xEFFFFFFF +#define S_0007C0_VIDDMA_BUSY(x) (((x) & 0x1) << 29) +#define G_0007C0_VIDDMA_BUSY(x) (((x) >> 29) & 0x1) +#define C_0007C0_VIDDMA_BUSY 0xDFFFFFFF +#define S_0007C0_CMDSTRM_BUSY(x) (((x) & 0x1) << 30) +#define G_0007C0_CMDSTRM_BUSY(x) (((x) >> 30) & 0x1) +#define C_0007C0_CMDSTRM_BUSY 0xBFFFFFFF +#define S_0007C0_CP_BUSY(x) (((x) & 0x1) << 31) +#define G_0007C0_CP_BUSY(x) (((x) >> 31) & 0x1) +#define C_0007C0_CP_BUSY 0x7FFFFFFF +#define R_000E40_RBBM_STATUS 0x000E40 +#define S_000E40_CMDFIFO_AVAIL(x) (((x) & 0x7F) << 0) +#define G_000E40_CMDFIFO_AVAIL(x) (((x) >> 0) & 0x7F) +#define C_000E40_CMDFIFO_AVAIL 0xFFFFFF80 +#define S_000E40_HIRQ_ON_RBB(x) (((x) & 0x1) << 8) +#define G_000E40_HIRQ_ON_RBB(x) (((x) >> 8) & 0x1) +#define C_000E40_HIRQ_ON_RBB 0xFFFFFEFF +#define S_000E40_CPRQ_ON_RBB(x) (((x) & 0x1) << 9) +#define G_000E40_CPRQ_ON_RBB(x) (((x) >> 9) & 0x1) +#define C_000E40_CPRQ_ON_RBB 0xFFFFFDFF +#define S_000E40_CFRQ_ON_RBB(x) (((x) & 0x1) << 10) +#define G_000E40_CFRQ_ON_RBB(x) (((x) >> 10) & 0x1) +#define C_000E40_CFRQ_ON_RBB 0xFFFFFBFF +#define S_000E40_HIRQ_IN_RTBUF(x) (((x) & 0x1) << 11) +#define G_000E40_HIRQ_IN_RTBUF(x) (((x) >> 11) & 0x1) +#define C_000E40_HIRQ_IN_RTBUF 0xFFFFF7FF +#define S_000E40_CPRQ_IN_RTBUF(x) (((x) & 0x1) << 12) +#define G_000E40_CPRQ_IN_RTBUF(x) (((x) >> 12) & 0x1) +#define C_000E40_CPRQ_IN_RTBUF 0xFFFFEFFF +#define S_000E40_CFRQ_IN_RTBUF(x) (((x) & 0x1) << 13) +#define G_000E40_CFRQ_IN_RTBUF(x) (((x) >> 13) & 0x1) +#define C_000E40_CFRQ_IN_RTBUF 0xFFFFDFFF +#define S_000E40_CF_PIPE_BUSY(x) (((x) & 0x1) << 14) +#define G_000E40_CF_PIPE_BUSY(x) (((x) >> 14) & 0x1) +#define C_000E40_CF_PIPE_BUSY 0xFFFFBFFF +#define S_000E40_ENG_EV_BUSY(x) (((x) & 0x1) << 15) +#define G_000E40_ENG_EV_BUSY(x) (((x) >> 15) & 0x1) +#define C_000E40_ENG_EV_BUSY 0xFFFF7FFF +#define S_000E40_CP_CMDSTRM_BUSY(x) (((x) & 0x1) << 16) +#define G_000E40_CP_CMDSTRM_BUSY(x) (((x) >> 16) & 0x1) +#define C_000E40_CP_CMDSTRM_BUSY 0xFFFEFFFF +#define S_000E40_E2_BUSY(x) (((x) & 0x1) << 17) +#define G_000E40_E2_BUSY(x) (((x) >> 17) & 0x1) +#define C_000E40_E2_BUSY 0xFFFDFFFF +#define S_000E40_RB2D_BUSY(x) (((x) & 0x1) << 18) +#define G_000E40_RB2D_BUSY(x) (((x) >> 18) & 0x1) +#define C_000E40_RB2D_BUSY 0xFFFBFFFF +#define S_000E40_RB3D_BUSY(x) (((x) & 0x1) << 19) +#define G_000E40_RB3D_BUSY(x) (((x) >> 19) & 0x1) +#define C_000E40_RB3D_BUSY 0xFFF7FFFF +#define S_000E40_VAP_BUSY(x) (((x) & 0x1) << 20) +#define G_000E40_VAP_BUSY(x) (((x) >> 20) & 0x1) +#define C_000E40_VAP_BUSY 0xFFEFFFFF +#define S_000E40_RE_BUSY(x) (((x) & 0x1) << 21) +#define G_000E40_RE_BUSY(x) (((x) >> 21) & 0x1) +#define C_000E40_RE_BUSY 0xFFDFFFFF +#define S_000E40_TAM_BUSY(x) (((x) & 0x1) << 22) +#define G_000E40_TAM_BUSY(x) (((x) >> 22) & 0x1) +#define C_000E40_TAM_BUSY 0xFFBFFFFF +#define S_000E40_TDM_BUSY(x) (((x) & 0x1) << 23) +#define G_000E40_TDM_BUSY(x) (((x) >> 23) & 0x1) +#define C_000E40_TDM_BUSY 0xFF7FFFFF +#define S_000E40_PB_BUSY(x) (((x) & 0x1) << 24) +#define G_000E40_PB_BUSY(x) (((x) >> 24) & 0x1) +#define C_000E40_PB_BUSY 0xFEFFFFFF +#define S_000E40_TIM_BUSY(x) (((x) & 0x1) << 25) +#define G_000E40_TIM_BUSY(x) (((x) >> 25) & 0x1) +#define C_000E40_TIM_BUSY 0xFDFFFFFF +#define S_000E40_GA_BUSY(x) (((x) & 0x1) << 26) +#define G_000E40_GA_BUSY(x) (((x) >> 26) & 0x1) +#define C_000E40_GA_BUSY 0xFBFFFFFF +#define S_000E40_CBA2D_BUSY(x) (((x) & 0x1) << 27) +#define G_000E40_CBA2D_BUSY(x) (((x) >> 27) & 0x1) +#define C_000E40_CBA2D_BUSY 0xF7FFFFFF +#define S_000E40_GUI_ACTIVE(x) (((x) & 0x1) << 31) +#define G_000E40_GUI_ACTIVE(x) (((x) >> 31) & 0x1) +#define C_000E40_GUI_ACTIVE 0x7FFFFFFF + +#endif diff --git a/drivers/gpu/drm/radeon/rs600.c b/drivers/gpu/drm/radeon/rs600.c index 0e791e26def3..5f117cd8736a 100644 --- a/drivers/gpu/drm/radeon/rs600.c +++ b/drivers/gpu/drm/radeon/rs600.c @@ -25,28 +25,25 @@ * Alex Deucher * Jerome Glisse */ +/* RS600 / Radeon X1250/X1270 integrated GPU + * + * This file gather function specific to RS600 which is the IGP of + * the X1250/X1270 family supporting intel CPU (while RS690/RS740 + * is the X1250/X1270 supporting AMD CPU). The display engine are + * the avivo one, bios is an atombios, 3D block are the one of the + * R4XX family. The GART is different from the RS400 one and is very + * close to the one of the R600 family (R600 likely being an evolution + * of the RS600 GART block). + */ #include "drmP.h" -#include "radeon_reg.h" #include "radeon.h" -#include "avivod.h" +#include "atom.h" +#include "rs600d.h" #include "rs600_reg_safe.h" -/* rs600 depends on : */ -void r100_hdp_reset(struct radeon_device *rdev); -int r100_gui_wait_for_idle(struct radeon_device *rdev); -int r300_mc_wait_for_idle(struct radeon_device *rdev); -void r420_pipes_init(struct radeon_device *rdev); - -/* This files gather functions specifics to : - * rs600 - * - * Some of these functions might be used by newer ASICs. - */ void rs600_gpu_init(struct radeon_device *rdev); int rs600_mc_wait_for_idle(struct radeon_device *rdev); -void rs600_disable_vga(struct radeon_device *rdev); - /* * GART. @@ -55,18 +52,18 @@ void rs600_gart_tlb_flush(struct radeon_device *rdev) { uint32_t tmp; - tmp = RREG32_MC(RS600_MC_PT0_CNTL); - tmp &= ~(RS600_INVALIDATE_ALL_L1_TLBS | RS600_INVALIDATE_L2_CACHE); - WREG32_MC(RS600_MC_PT0_CNTL, tmp); + tmp = RREG32_MC(R_000100_MC_PT0_CNTL); + tmp &= C_000100_INVALIDATE_ALL_L1_TLBS & C_000100_INVALIDATE_L2_CACHE; + WREG32_MC(R_000100_MC_PT0_CNTL, tmp); - tmp = RREG32_MC(RS600_MC_PT0_CNTL); - tmp |= RS600_INVALIDATE_ALL_L1_TLBS | RS600_INVALIDATE_L2_CACHE; - WREG32_MC(RS600_MC_PT0_CNTL, tmp); + tmp = RREG32_MC(R_000100_MC_PT0_CNTL); + tmp |= S_000100_INVALIDATE_ALL_L1_TLBS(1) & S_000100_INVALIDATE_L2_CACHE(1); + WREG32_MC(R_000100_MC_PT0_CNTL, tmp); - tmp = RREG32_MC(RS600_MC_PT0_CNTL); - tmp &= ~(RS600_INVALIDATE_ALL_L1_TLBS | RS600_INVALIDATE_L2_CACHE); - WREG32_MC(RS600_MC_PT0_CNTL, tmp); - tmp = RREG32_MC(RS600_MC_PT0_CNTL); + tmp = RREG32_MC(R_000100_MC_PT0_CNTL); + tmp &= C_000100_INVALIDATE_ALL_L1_TLBS & C_000100_INVALIDATE_L2_CACHE; + WREG32_MC(R_000100_MC_PT0_CNTL, tmp); + tmp = RREG32_MC(R_000100_MC_PT0_CNTL); } int rs600_gart_init(struct radeon_device *rdev) @@ -88,7 +85,7 @@ int rs600_gart_init(struct radeon_device *rdev) int rs600_gart_enable(struct radeon_device *rdev) { - uint32_t tmp; + u32 tmp; int r, i; if (rdev->gart.table.vram.robj == NULL) { @@ -98,46 +95,50 @@ int rs600_gart_enable(struct radeon_device *rdev) r = radeon_gart_table_vram_pin(rdev); if (r) return r; + /* Enable bus master */ + tmp = RREG32(R_00004C_BUS_CNTL) & C_00004C_BUS_MASTER_DIS; + WREG32(R_00004C_BUS_CNTL, tmp); /* FIXME: setup default page */ - WREG32_MC(RS600_MC_PT0_CNTL, - (RS600_EFFECTIVE_L2_CACHE_SIZE(6) | - RS600_EFFECTIVE_L2_QUEUE_SIZE(6))); + WREG32_MC(R_000100_MC_PT0_CNTL, + (S_000100_EFFECTIVE_L2_CACHE_SIZE(6) | + S_000100_EFFECTIVE_L2_QUEUE_SIZE(6))); for (i = 0; i < 19; i++) { - WREG32_MC(RS600_MC_PT0_CLIENT0_CNTL + i, - (RS600_ENABLE_TRANSLATION_MODE_OVERRIDE | - RS600_SYSTEM_ACCESS_MODE_IN_SYS | - RS600_SYSTEM_APERTURE_UNMAPPED_ACCESS_DEFAULT_PAGE | - RS600_EFFECTIVE_L1_CACHE_SIZE(3) | - RS600_ENABLE_FRAGMENT_PROCESSING | - RS600_EFFECTIVE_L1_QUEUE_SIZE(3))); + WREG32_MC(R_00016C_MC_PT0_CLIENT0_CNTL + i, + S_00016C_ENABLE_TRANSLATION_MODE_OVERRIDE(1) | + S_00016C_SYSTEM_ACCESS_MODE_MASK( + V_00016C_SYSTEM_ACCESS_MODE_IN_SYS) | + S_00016C_SYSTEM_APERTURE_UNMAPPED_ACCESS( + V_00016C_SYSTEM_APERTURE_UNMAPPED_DEFAULT_PAGE) | + S_00016C_EFFECTIVE_L1_CACHE_SIZE(1) | + S_00016C_ENABLE_FRAGMENT_PROCESSING(1) | + S_00016C_EFFECTIVE_L1_QUEUE_SIZE(1)); } /* System context map to GART space */ - WREG32_MC(RS600_MC_PT0_SYSTEM_APERTURE_LOW_ADDR, rdev->mc.gtt_location); - tmp = rdev->mc.gtt_location + rdev->mc.gtt_size - 1; - WREG32_MC(RS600_MC_PT0_SYSTEM_APERTURE_HIGH_ADDR, tmp); + WREG32_MC(R_000112_MC_PT0_SYSTEM_APERTURE_LOW_ADDR, rdev->mc.gtt_start); + WREG32_MC(R_000114_MC_PT0_SYSTEM_APERTURE_HIGH_ADDR, rdev->mc.gtt_end); /* enable first context */ - WREG32_MC(RS600_MC_PT0_CONTEXT0_FLAT_START_ADDR, rdev->mc.gtt_location); - tmp = rdev->mc.gtt_location + rdev->mc.gtt_size - 1; - WREG32_MC(RS600_MC_PT0_CONTEXT0_FLAT_END_ADDR, tmp); - WREG32_MC(RS600_MC_PT0_CONTEXT0_CNTL, - (RS600_ENABLE_PAGE_TABLE | RS600_PAGE_TABLE_TYPE_FLAT)); + WREG32_MC(R_00013C_MC_PT0_CONTEXT0_FLAT_START_ADDR, rdev->mc.gtt_start); + WREG32_MC(R_00014C_MC_PT0_CONTEXT0_FLAT_END_ADDR, rdev->mc.gtt_end); + WREG32_MC(R_000102_MC_PT0_CONTEXT0_CNTL, + S_000102_ENABLE_PAGE_TABLE(1) | + S_000102_PAGE_TABLE_DEPTH(V_000102_PAGE_TABLE_FLAT)); /* disable all other contexts */ for (i = 1; i < 8; i++) { - WREG32_MC(RS600_MC_PT0_CONTEXT0_CNTL + i, 0); + WREG32_MC(R_000102_MC_PT0_CONTEXT0_CNTL + i, 0); } /* setup the page table */ - WREG32_MC(RS600_MC_PT0_CONTEXT0_FLAT_BASE_ADDR, - rdev->gart.table_addr); - WREG32_MC(RS600_MC_PT0_CONTEXT0_DEFAULT_READ_ADDR, 0); + WREG32_MC(R_00012C_MC_PT0_CONTEXT0_FLAT_BASE_ADDR, + rdev->gart.table_addr); + WREG32_MC(R_00011C_MC_PT0_CONTEXT0_DEFAULT_READ_ADDR, 0); /* enable page tables */ - tmp = RREG32_MC(RS600_MC_PT0_CNTL); - WREG32_MC(RS600_MC_PT0_CNTL, (tmp | RS600_ENABLE_PT)); - tmp = RREG32_MC(RS600_MC_CNTL1); - WREG32_MC(RS600_MC_CNTL1, (tmp | RS600_ENABLE_PAGE_TABLES)); + tmp = RREG32_MC(R_000100_MC_PT0_CNTL); + WREG32_MC(R_000100_MC_PT0_CNTL, (tmp | S_000100_ENABLE_PT(1))); + tmp = RREG32_MC(R_000009_MC_CNTL1); + WREG32_MC(R_000009_MC_CNTL1, (tmp | S_000009_ENABLE_PAGE_TABLES(1))); rs600_gart_tlb_flush(rdev); rdev->gart.ready = true; return 0; @@ -148,10 +149,9 @@ void rs600_gart_disable(struct radeon_device *rdev) uint32_t tmp; /* FIXME: disable out of gart access */ - WREG32_MC(RS600_MC_PT0_CNTL, 0); - tmp = RREG32_MC(RS600_MC_CNTL1); - tmp &= ~RS600_ENABLE_PAGE_TABLES; - WREG32_MC(RS600_MC_CNTL1, tmp); + WREG32_MC(R_000100_MC_PT0_CNTL, 0); + tmp = RREG32_MC(R_000009_MC_CNTL1); + WREG32_MC(R_000009_MC_CNTL1, tmp & C_000009_ENABLE_PAGE_TABLES); if (rdev->gart.table.vram.robj) { radeon_object_kunmap(rdev->gart.table.vram.robj); radeon_object_unpin(rdev->gart.table.vram.robj); @@ -185,132 +185,64 @@ int rs600_gart_set_page(struct radeon_device *rdev, int i, uint64_t addr) return 0; } - -/* - * MC. - */ -void rs600_mc_disable_clients(struct radeon_device *rdev) -{ - unsigned tmp; - - if (r100_gui_wait_for_idle(rdev)) { - printk(KERN_WARNING "Failed to wait GUI idle while " - "programming pipes. Bad things might happen.\n"); - } - - radeon_avivo_vga_render_disable(rdev); - - tmp = RREG32(AVIVO_D1VGA_CONTROL); - WREG32(AVIVO_D1VGA_CONTROL, tmp & ~AVIVO_DVGA_CONTROL_MODE_ENABLE); - tmp = RREG32(AVIVO_D2VGA_CONTROL); - WREG32(AVIVO_D2VGA_CONTROL, tmp & ~AVIVO_DVGA_CONTROL_MODE_ENABLE); - - tmp = RREG32(AVIVO_D1CRTC_CONTROL); - WREG32(AVIVO_D1CRTC_CONTROL, tmp & ~AVIVO_CRTC_EN); - tmp = RREG32(AVIVO_D2CRTC_CONTROL); - WREG32(AVIVO_D2CRTC_CONTROL, tmp & ~AVIVO_CRTC_EN); - - /* make sure all previous write got through */ - tmp = RREG32(AVIVO_D2CRTC_CONTROL); - - mdelay(1); -} - -int rs600_mc_init(struct radeon_device *rdev) -{ - uint32_t tmp; - int r; - - if (r100_debugfs_rbbm_init(rdev)) { - DRM_ERROR("Failed to register debugfs file for RBBM !\n"); - } - - rs600_gpu_init(rdev); - rs600_gart_disable(rdev); - - /* Setup GPU memory space */ - rdev->mc.vram_location = 0xFFFFFFFFUL; - rdev->mc.gtt_location = 0xFFFFFFFFUL; - r = radeon_mc_setup(rdev); - if (r) { - return r; - } - - /* Program GPU memory space */ - /* Enable bus master */ - tmp = RREG32(RADEON_BUS_CNTL) & ~RS600_BUS_MASTER_DIS; - WREG32(RADEON_BUS_CNTL, tmp); - /* FIXME: What does AGP means for such chipset ? */ - WREG32_MC(RS600_MC_AGP_LOCATION, 0x0FFFFFFF); - /* FIXME: are this AGP reg in indirect MC range ? */ - WREG32_MC(RS600_MC_AGP_BASE, 0); - WREG32_MC(RS600_MC_AGP_BASE_2, 0); - rs600_mc_disable_clients(rdev); - if (rs600_mc_wait_for_idle(rdev)) { - printk(KERN_WARNING "Failed to wait MC idle while " - "programming pipes. Bad things might happen.\n"); - } - tmp = rdev->mc.vram_location + rdev->mc.mc_vram_size - 1; - tmp = REG_SET(RS600_MC_FB_TOP, tmp >> 16); - tmp |= REG_SET(RS600_MC_FB_START, rdev->mc.vram_location >> 16); - WREG32_MC(RS600_MC_FB_LOCATION, tmp); - WREG32(RS690_HDP_FB_LOCATION, rdev->mc.vram_location >> 16); - return 0; -} - -void rs600_mc_fini(struct radeon_device *rdev) -{ -} - - -/* - * Interrupts - */ int rs600_irq_set(struct radeon_device *rdev) { uint32_t tmp = 0; uint32_t mode_int = 0; if (rdev->irq.sw_int) { - tmp |= RADEON_SW_INT_ENABLE; + tmp |= S_000040_SW_INT_EN(1); } if (rdev->irq.crtc_vblank_int[0]) { - mode_int |= AVIVO_D1MODE_INT_MASK; + mode_int |= S_006540_D1MODE_VBLANK_INT_MASK(1); } if (rdev->irq.crtc_vblank_int[1]) { - mode_int |= AVIVO_D2MODE_INT_MASK; + mode_int |= S_006540_D2MODE_VBLANK_INT_MASK(1); } - WREG32(RADEON_GEN_INT_CNTL, tmp); - WREG32(AVIVO_DxMODE_INT_MASK, mode_int); + WREG32(R_000040_GEN_INT_CNTL, tmp); + WREG32(R_006540_DxMODE_INT_MASK, mode_int); return 0; } static inline uint32_t rs600_irq_ack(struct radeon_device *rdev, u32 *r500_disp_int) { - uint32_t irqs = RREG32(RADEON_GEN_INT_STATUS); - uint32_t irq_mask = RADEON_SW_INT_TEST; - - if (irqs & AVIVO_DISPLAY_INT_STATUS) { - *r500_disp_int = RREG32(AVIVO_DISP_INTERRUPT_STATUS); - if (*r500_disp_int & AVIVO_D1_VBLANK_INTERRUPT) { - WREG32(AVIVO_D1MODE_VBLANK_STATUS, AVIVO_VBLANK_ACK); + uint32_t irqs = RREG32(R_000044_GEN_INT_STATUS); + uint32_t irq_mask = ~C_000044_SW_INT; + + if (G_000044_DISPLAY_INT_STAT(irqs)) { + *r500_disp_int = RREG32(R_007EDC_DISP_INTERRUPT_STATUS); + if (G_007EDC_LB_D1_VBLANK_INTERRUPT(*r500_disp_int)) { + WREG32(R_006534_D1MODE_VBLANK_STATUS, + S_006534_D1MODE_VBLANK_ACK(1)); } - if (*r500_disp_int & AVIVO_D2_VBLANK_INTERRUPT) { - WREG32(AVIVO_D2MODE_VBLANK_STATUS, AVIVO_VBLANK_ACK); + if (G_007EDC_LB_D2_VBLANK_INTERRUPT(*r500_disp_int)) { + WREG32(R_006D34_D2MODE_VBLANK_STATUS, + S_006D34_D2MODE_VBLANK_ACK(1)); } } else { *r500_disp_int = 0; } if (irqs) { - WREG32(RADEON_GEN_INT_STATUS, irqs); + WREG32(R_000044_GEN_INT_STATUS, irqs); } return irqs & irq_mask; } +void rs600_irq_disable(struct radeon_device *rdev) +{ + u32 tmp; + + WREG32(R_000040_GEN_INT_CNTL, 0); + WREG32(R_006540_DxMODE_INT_MASK, 0); + /* Wait and acknowledge irq */ + mdelay(1); + rs600_irq_ack(rdev, &tmp); +} + int rs600_irq_process(struct radeon_device *rdev) { - uint32_t status; + uint32_t status, msi_rearm; uint32_t r500_disp_int; status = rs600_irq_ack(rdev, &r500_disp_int); @@ -319,85 +251,65 @@ int rs600_irq_process(struct radeon_device *rdev) } while (status || r500_disp_int) { /* SW interrupt */ - if (status & RADEON_SW_INT_TEST) { + if (G_000040_SW_INT_EN(status)) radeon_fence_process(rdev); - } /* Vertical blank interrupts */ - if (r500_disp_int & AVIVO_D1_VBLANK_INTERRUPT) { + if (G_007EDC_LB_D1_VBLANK_INTERRUPT(r500_disp_int)) drm_handle_vblank(rdev->ddev, 0); - } - if (r500_disp_int & AVIVO_D2_VBLANK_INTERRUPT) { + if (G_007EDC_LB_D2_VBLANK_INTERRUPT(r500_disp_int)) drm_handle_vblank(rdev->ddev, 1); - } status = rs600_irq_ack(rdev, &r500_disp_int); } + if (rdev->msi_enabled) { + switch (rdev->family) { + case CHIP_RS600: + case CHIP_RS690: + case CHIP_RS740: + msi_rearm = RREG32(RADEON_BUS_CNTL) & ~RS600_MSI_REARM; + WREG32(RADEON_BUS_CNTL, msi_rearm); + WREG32(RADEON_BUS_CNTL, msi_rearm | RS600_MSI_REARM); + break; + default: + msi_rearm = RREG32(RADEON_MSI_REARM_EN) & ~RV370_MSI_REARM_EN; + WREG32(RADEON_MSI_REARM_EN, msi_rearm); + WREG32(RADEON_MSI_REARM_EN, msi_rearm | RV370_MSI_REARM_EN); + break; + } + } return IRQ_HANDLED; } u32 rs600_get_vblank_counter(struct radeon_device *rdev, int crtc) { if (crtc == 0) - return RREG32(AVIVO_D1CRTC_FRAME_COUNT); + return RREG32(R_0060A4_D1CRTC_STATUS_FRAME_COUNT); else - return RREG32(AVIVO_D2CRTC_FRAME_COUNT); -} - - -/* - * Global GPU functions - */ -void rs600_disable_vga(struct radeon_device *rdev) -{ - unsigned tmp; - - WREG32(0x330, 0); - WREG32(0x338, 0); - tmp = RREG32(0x300); - tmp &= ~(3 << 16); - WREG32(0x300, tmp); - WREG32(0x308, (1 << 8)); - WREG32(0x310, rdev->mc.vram_location); - WREG32(0x594, 0); + return RREG32(R_0068A4_D2CRTC_STATUS_FRAME_COUNT); } int rs600_mc_wait_for_idle(struct radeon_device *rdev) { unsigned i; - uint32_t tmp; for (i = 0; i < rdev->usec_timeout; i++) { - /* read MC_STATUS */ - tmp = RREG32_MC(RS600_MC_STATUS); - if (tmp & RS600_MC_STATUS_IDLE) { + if (G_000000_MC_IDLE(RREG32_MC(R_000000_MC_STATUS))) return 0; - } - DRM_UDELAY(1); + udelay(1); } return -1; } -void rs600_errata(struct radeon_device *rdev) -{ - rdev->pll_errata = 0; -} - void rs600_gpu_init(struct radeon_device *rdev) { /* FIXME: HDP same place on rs600 ? */ r100_hdp_reset(rdev); - rs600_disable_vga(rdev); /* FIXME: is this correct ? */ r420_pipes_init(rdev); - if (rs600_mc_wait_for_idle(rdev)) { - printk(KERN_WARNING "Failed to wait MC idle while " - "programming pipes. Bad things might happen.\n"); - } + /* Wait for mc idle */ + if (rs600_mc_wait_for_idle(rdev)) + dev_warn(rdev->dev, "Wait MC idle timeout before updating MC.\n"); } - -/* - * VRAM info. - */ void rs600_vram_info(struct radeon_device *rdev) { /* FIXME: to do or is these values sane ? */ @@ -410,31 +322,208 @@ void rs600_bandwidth_update(struct radeon_device *rdev) /* FIXME: implement, should this be like rs690 ? */ } - -/* - * Indirect registers accessor - */ uint32_t rs600_mc_rreg(struct radeon_device *rdev, uint32_t reg) { - uint32_t r; - - WREG32(RS600_MC_INDEX, - ((reg & RS600_MC_ADDR_MASK) | RS600_MC_IND_CITF_ARB0)); - r = RREG32(RS600_MC_DATA); - return r; + WREG32(R_000070_MC_IND_INDEX, S_000070_MC_IND_ADDR(reg) | + S_000070_MC_IND_CITF_ARB0(1)); + return RREG32(R_000074_MC_IND_DATA); } void rs600_mc_wreg(struct radeon_device *rdev, uint32_t reg, uint32_t v) { - WREG32(RS600_MC_INDEX, - RS600_MC_IND_WR_EN | RS600_MC_IND_CITF_ARB0 | - ((reg) & RS600_MC_ADDR_MASK)); - WREG32(RS600_MC_DATA, v); + WREG32(R_000070_MC_IND_INDEX, S_000070_MC_IND_ADDR(reg) | + S_000070_MC_IND_CITF_ARB0(1) | S_000070_MC_IND_WR_EN(1)); + WREG32(R_000074_MC_IND_DATA, v); } -int rs600_init(struct radeon_device *rdev) +void rs600_debugfs(struct radeon_device *rdev) +{ + if (r100_debugfs_rbbm_init(rdev)) + DRM_ERROR("Failed to register debugfs file for RBBM !\n"); +} + +void rs600_set_safe_registers(struct radeon_device *rdev) { rdev->config.r300.reg_safe_bm = rs600_reg_safe_bm; rdev->config.r300.reg_safe_bm_size = ARRAY_SIZE(rs600_reg_safe_bm); +} + +static void rs600_mc_program(struct radeon_device *rdev) +{ + struct rv515_mc_save save; + + /* Stops all mc clients */ + rv515_mc_stop(rdev, &save); + + /* Wait for mc idle */ + if (rs600_mc_wait_for_idle(rdev)) + dev_warn(rdev->dev, "Wait MC idle timeout before updating MC.\n"); + + /* FIXME: What does AGP means for such chipset ? */ + WREG32_MC(R_000005_MC_AGP_LOCATION, 0x0FFFFFFF); + WREG32_MC(R_000006_AGP_BASE, 0); + WREG32_MC(R_000007_AGP_BASE_2, 0); + /* Program MC */ + WREG32_MC(R_000004_MC_FB_LOCATION, + S_000004_MC_FB_START(rdev->mc.vram_start >> 16) | + S_000004_MC_FB_TOP(rdev->mc.vram_end >> 16)); + WREG32(R_000134_HDP_FB_LOCATION, + S_000134_HDP_FB_START(rdev->mc.vram_start >> 16)); + + rv515_mc_resume(rdev, &save); +} + +static int rs600_startup(struct radeon_device *rdev) +{ + int r; + + rs600_mc_program(rdev); + /* Resume clock */ + rv515_clock_startup(rdev); + /* Initialize GPU configuration (# pipes, ...) */ + rs600_gpu_init(rdev); + /* Initialize GART (initialize after TTM so we can allocate + * memory through TTM but finalize after TTM) */ + r = rs600_gart_enable(rdev); + if (r) + return r; + /* Enable IRQ */ + rdev->irq.sw_int = true; + rs600_irq_set(rdev); + /* 1M ring buffer */ + r = r100_cp_init(rdev, 1024 * 1024); + if (r) { + dev_err(rdev->dev, "failled initializing CP (%d).\n", r); + return r; + } + r = r100_wb_init(rdev); + if (r) + dev_err(rdev->dev, "failled initializing WB (%d).\n", r); + r = r100_ib_init(rdev); + if (r) { + dev_err(rdev->dev, "failled initializing IB (%d).\n", r); + return r; + } + return 0; +} + +int rs600_resume(struct radeon_device *rdev) +{ + /* Make sur GART are not working */ + rs600_gart_disable(rdev); + /* Resume clock before doing reset */ + rv515_clock_startup(rdev); + /* Reset gpu before posting otherwise ATOM will enter infinite loop */ + if (radeon_gpu_reset(rdev)) { + dev_warn(rdev->dev, "GPU reset failed ! (0xE40=0x%08X, 0x7C0=0x%08X)\n", + RREG32(R_000E40_RBBM_STATUS), + RREG32(R_0007C0_CP_STAT)); + } + /* post */ + atom_asic_init(rdev->mode_info.atom_context); + /* Resume clock after posting */ + rv515_clock_startup(rdev); + return rs600_startup(rdev); +} + +int rs600_suspend(struct radeon_device *rdev) +{ + r100_cp_disable(rdev); + r100_wb_disable(rdev); + rs600_irq_disable(rdev); + rs600_gart_disable(rdev); + return 0; +} + +void rs600_fini(struct radeon_device *rdev) +{ + rs600_suspend(rdev); + r100_cp_fini(rdev); + r100_wb_fini(rdev); + r100_ib_fini(rdev); + radeon_gem_fini(rdev); + rs600_gart_fini(rdev); + radeon_irq_kms_fini(rdev); + radeon_fence_driver_fini(rdev); + radeon_object_fini(rdev); + radeon_atombios_fini(rdev); + kfree(rdev->bios); + rdev->bios = NULL; +} + +int rs600_init(struct radeon_device *rdev) +{ + int r; + + /* Disable VGA */ + rv515_vga_render_disable(rdev); + /* Initialize scratch registers */ + radeon_scratch_init(rdev); + /* Initialize surface registers */ + radeon_surface_init(rdev); + /* BIOS */ + if (!radeon_get_bios(rdev)) { + if (ASIC_IS_AVIVO(rdev)) + return -EINVAL; + } + if (rdev->is_atom_bios) { + r = radeon_atombios_init(rdev); + if (r) + return r; + } else { + dev_err(rdev->dev, "Expecting atombios for RS600 GPU\n"); + return -EINVAL; + } + /* Reset gpu before posting otherwise ATOM will enter infinite loop */ + if (radeon_gpu_reset(rdev)) { + dev_warn(rdev->dev, + "GPU reset failed ! (0xE40=0x%08X, 0x7C0=0x%08X)\n", + RREG32(R_000E40_RBBM_STATUS), + RREG32(R_0007C0_CP_STAT)); + } + /* check if cards are posted or not */ + if (!radeon_card_posted(rdev) && rdev->bios) { + DRM_INFO("GPU not posted. posting now...\n"); + atom_asic_init(rdev->mode_info.atom_context); + } + /* Initialize clocks */ + radeon_get_clock_info(rdev->ddev); + /* Initialize power management */ + radeon_pm_init(rdev); + /* Get vram informations */ + rs600_vram_info(rdev); + /* Initialize memory controller (also test AGP) */ + r = r420_mc_init(rdev); + if (r) + return r; + rs600_debugfs(rdev); + /* Fence driver */ + r = radeon_fence_driver_init(rdev); + if (r) + return r; + r = radeon_irq_kms_init(rdev); + if (r) + return r; + /* Memory manager */ + r = radeon_object_init(rdev); + if (r) + return r; + r = rs600_gart_init(rdev); + if (r) + return r; + rs600_set_safe_registers(rdev); + rdev->accel_working = true; + r = rs600_startup(rdev); + if (r) { + /* Somethings want wront with the accel init stop accel */ + dev_err(rdev->dev, "Disabling GPU acceleration\n"); + rs600_suspend(rdev); + r100_cp_fini(rdev); + r100_wb_fini(rdev); + r100_ib_fini(rdev); + rs600_gart_fini(rdev); + radeon_irq_kms_fini(rdev); + rdev->accel_working = false; + } return 0; } diff --git a/drivers/gpu/drm/radeon/rs600d.h b/drivers/gpu/drm/radeon/rs600d.h new file mode 100644 index 000000000000..81308924859a --- /dev/null +++ b/drivers/gpu/drm/radeon/rs600d.h @@ -0,0 +1,470 @@ +/* + * Copyright 2008 Advanced Micro Devices, Inc. + * Copyright 2008 Red Hat Inc. + * Copyright 2009 Jerome Glisse. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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. + * + * Authors: Dave Airlie + * Alex Deucher + * Jerome Glisse + */ +#ifndef __RS600D_H__ +#define __RS600D_H__ + +/* Registers */ +#define R_000040_GEN_INT_CNTL 0x000040 +#define S_000040_DISPLAY_INT_STATUS(x) (((x) & 0x1) << 0) +#define G_000040_DISPLAY_INT_STATUS(x) (((x) >> 0) & 0x1) +#define C_000040_DISPLAY_INT_STATUS 0xFFFFFFFE +#define S_000040_DMA_VIPH0_INT_EN(x) (((x) & 0x1) << 12) +#define G_000040_DMA_VIPH0_INT_EN(x) (((x) >> 12) & 0x1) +#define C_000040_DMA_VIPH0_INT_EN 0xFFFFEFFF +#define S_000040_CRTC2_VSYNC(x) (((x) & 0x1) << 6) +#define G_000040_CRTC2_VSYNC(x) (((x) >> 6) & 0x1) +#define C_000040_CRTC2_VSYNC 0xFFFFFFBF +#define S_000040_SNAPSHOT2(x) (((x) & 0x1) << 7) +#define G_000040_SNAPSHOT2(x) (((x) >> 7) & 0x1) +#define C_000040_SNAPSHOT2 0xFFFFFF7F +#define S_000040_CRTC2_VBLANK(x) (((x) & 0x1) << 9) +#define G_000040_CRTC2_VBLANK(x) (((x) >> 9) & 0x1) +#define C_000040_CRTC2_VBLANK 0xFFFFFDFF +#define S_000040_FP2_DETECT(x) (((x) & 0x1) << 10) +#define G_000040_FP2_DETECT(x) (((x) >> 10) & 0x1) +#define C_000040_FP2_DETECT 0xFFFFFBFF +#define S_000040_VSYNC_DIFF_OVER_LIMIT(x) (((x) & 0x1) << 11) +#define G_000040_VSYNC_DIFF_OVER_LIMIT(x) (((x) >> 11) & 0x1) +#define C_000040_VSYNC_DIFF_OVER_LIMIT 0xFFFFF7FF +#define S_000040_DMA_VIPH1_INT_EN(x) (((x) & 0x1) << 13) +#define G_000040_DMA_VIPH1_INT_EN(x) (((x) >> 13) & 0x1) +#define C_000040_DMA_VIPH1_INT_EN 0xFFFFDFFF +#define S_000040_DMA_VIPH2_INT_EN(x) (((x) & 0x1) << 14) +#define G_000040_DMA_VIPH2_INT_EN(x) (((x) >> 14) & 0x1) +#define C_000040_DMA_VIPH2_INT_EN 0xFFFFBFFF +#define S_000040_DMA_VIPH3_INT_EN(x) (((x) & 0x1) << 15) +#define G_000040_DMA_VIPH3_INT_EN(x) (((x) >> 15) & 0x1) +#define C_000040_DMA_VIPH3_INT_EN 0xFFFF7FFF +#define S_000040_I2C_INT_EN(x) (((x) & 0x1) << 17) +#define G_000040_I2C_INT_EN(x) (((x) >> 17) & 0x1) +#define C_000040_I2C_INT_EN 0xFFFDFFFF +#define S_000040_GUI_IDLE(x) (((x) & 0x1) << 19) +#define G_000040_GUI_IDLE(x) (((x) >> 19) & 0x1) +#define C_000040_GUI_IDLE 0xFFF7FFFF +#define S_000040_VIPH_INT_EN(x) (((x) & 0x1) << 24) +#define G_000040_VIPH_INT_EN(x) (((x) >> 24) & 0x1) +#define C_000040_VIPH_INT_EN 0xFEFFFFFF +#define S_000040_SW_INT_EN(x) (((x) & 0x1) << 25) +#define G_000040_SW_INT_EN(x) (((x) >> 25) & 0x1) +#define C_000040_SW_INT_EN 0xFDFFFFFF +#define S_000040_GEYSERVILLE(x) (((x) & 0x1) << 27) +#define G_000040_GEYSERVILLE(x) (((x) >> 27) & 0x1) +#define C_000040_GEYSERVILLE 0xF7FFFFFF +#define S_000040_HDCP_AUTHORIZED_INT(x) (((x) & 0x1) << 28) +#define G_000040_HDCP_AUTHORIZED_INT(x) (((x) >> 28) & 0x1) +#define C_000040_HDCP_AUTHORIZED_INT 0xEFFFFFFF +#define S_000040_DVI_I2C_INT(x) (((x) & 0x1) << 29) +#define G_000040_DVI_I2C_INT(x) (((x) >> 29) & 0x1) +#define C_000040_DVI_I2C_INT 0xDFFFFFFF +#define S_000040_GUIDMA(x) (((x) & 0x1) << 30) +#define G_000040_GUIDMA(x) (((x) >> 30) & 0x1) +#define C_000040_GUIDMA 0xBFFFFFFF +#define S_000040_VIDDMA(x) (((x) & 0x1) << 31) +#define G_000040_VIDDMA(x) (((x) >> 31) & 0x1) +#define C_000040_VIDDMA 0x7FFFFFFF +#define R_000044_GEN_INT_STATUS 0x000044 +#define S_000044_DISPLAY_INT_STAT(x) (((x) & 0x1) << 0) +#define G_000044_DISPLAY_INT_STAT(x) (((x) >> 0) & 0x1) +#define C_000044_DISPLAY_INT_STAT 0xFFFFFFFE +#define S_000044_VGA_INT_STAT(x) (((x) & 0x1) << 1) +#define G_000044_VGA_INT_STAT(x) (((x) >> 1) & 0x1) +#define C_000044_VGA_INT_STAT 0xFFFFFFFD +#define S_000044_CAP0_INT_ACTIVE(x) (((x) & 0x1) << 8) +#define G_000044_CAP0_INT_ACTIVE(x) (((x) >> 8) & 0x1) +#define C_000044_CAP0_INT_ACTIVE 0xFFFFFEFF +#define S_000044_DMA_VIPH0_INT(x) (((x) & 0x1) << 12) +#define G_000044_DMA_VIPH0_INT(x) (((x) >> 12) & 0x1) +#define C_000044_DMA_VIPH0_INT 0xFFFFEFFF +#define S_000044_DMA_VIPH1_INT(x) (((x) & 0x1) << 13) +#define G_000044_DMA_VIPH1_INT(x) (((x) >> 13) & 0x1) +#define C_000044_DMA_VIPH1_INT 0xFFFFDFFF +#define S_000044_DMA_VIPH2_INT(x) (((x) & 0x1) << 14) +#define G_000044_DMA_VIPH2_INT(x) (((x) >> 14) & 0x1) +#define C_000044_DMA_VIPH2_INT 0xFFFFBFFF +#define S_000044_DMA_VIPH3_INT(x) (((x) & 0x1) << 15) +#define G_000044_DMA_VIPH3_INT(x) (((x) >> 15) & 0x1) +#define C_000044_DMA_VIPH3_INT 0xFFFF7FFF +#define S_000044_MC_PROBE_FAULT_STAT(x) (((x) & 0x1) << 16) +#define G_000044_MC_PROBE_FAULT_STAT(x) (((x) >> 16) & 0x1) +#define C_000044_MC_PROBE_FAULT_STAT 0xFFFEFFFF +#define S_000044_I2C_INT(x) (((x) & 0x1) << 17) +#define G_000044_I2C_INT(x) (((x) >> 17) & 0x1) +#define C_000044_I2C_INT 0xFFFDFFFF +#define S_000044_SCRATCH_INT_STAT(x) (((x) & 0x1) << 18) +#define G_000044_SCRATCH_INT_STAT(x) (((x) >> 18) & 0x1) +#define C_000044_SCRATCH_INT_STAT 0xFFFBFFFF +#define S_000044_GUI_IDLE_STAT(x) (((x) & 0x1) << 19) +#define G_000044_GUI_IDLE_STAT(x) (((x) >> 19) & 0x1) +#define C_000044_GUI_IDLE_STAT 0xFFF7FFFF +#define S_000044_ATI_OVERDRIVE_INT_STAT(x) (((x) & 0x1) << 20) +#define G_000044_ATI_OVERDRIVE_INT_STAT(x) (((x) >> 20) & 0x1) +#define C_000044_ATI_OVERDRIVE_INT_STAT 0xFFEFFFFF +#define S_000044_MC_PROTECTION_FAULT_STAT(x) (((x) & 0x1) << 21) +#define G_000044_MC_PROTECTION_FAULT_STAT(x) (((x) >> 21) & 0x1) +#define C_000044_MC_PROTECTION_FAULT_STAT 0xFFDFFFFF +#define S_000044_RBBM_READ_INT_STAT(x) (((x) & 0x1) << 22) +#define G_000044_RBBM_READ_INT_STAT(x) (((x) >> 22) & 0x1) +#define C_000044_RBBM_READ_INT_STAT 0xFFBFFFFF +#define S_000044_CB_CONTEXT_SWITCH_STAT(x) (((x) & 0x1) << 23) +#define G_000044_CB_CONTEXT_SWITCH_STAT(x) (((x) >> 23) & 0x1) +#define C_000044_CB_CONTEXT_SWITCH_STAT 0xFF7FFFFF +#define S_000044_VIPH_INT(x) (((x) & 0x1) << 24) +#define G_000044_VIPH_INT(x) (((x) >> 24) & 0x1) +#define C_000044_VIPH_INT 0xFEFFFFFF +#define S_000044_SW_INT(x) (((x) & 0x1) << 25) +#define G_000044_SW_INT(x) (((x) >> 25) & 0x1) +#define C_000044_SW_INT 0xFDFFFFFF +#define S_000044_SW_INT_SET(x) (((x) & 0x1) << 26) +#define G_000044_SW_INT_SET(x) (((x) >> 26) & 0x1) +#define C_000044_SW_INT_SET 0xFBFFFFFF +#define S_000044_IDCT_INT_STAT(x) (((x) & 0x1) << 27) +#define G_000044_IDCT_INT_STAT(x) (((x) >> 27) & 0x1) +#define C_000044_IDCT_INT_STAT 0xF7FFFFFF +#define S_000044_GUIDMA_STAT(x) (((x) & 0x1) << 30) +#define G_000044_GUIDMA_STAT(x) (((x) >> 30) & 0x1) +#define C_000044_GUIDMA_STAT 0xBFFFFFFF +#define S_000044_VIDDMA_STAT(x) (((x) & 0x1) << 31) +#define G_000044_VIDDMA_STAT(x) (((x) >> 31) & 0x1) +#define C_000044_VIDDMA_STAT 0x7FFFFFFF +#define R_00004C_BUS_CNTL 0x00004C +#define S_00004C_BUS_MASTER_DIS(x) (((x) & 0x1) << 14) +#define G_00004C_BUS_MASTER_DIS(x) (((x) >> 14) & 0x1) +#define C_00004C_BUS_MASTER_DIS 0xFFFFBFFF +#define S_00004C_BUS_MSI_REARM(x) (((x) & 0x1) << 20) +#define G_00004C_BUS_MSI_REARM(x) (((x) >> 20) & 0x1) +#define C_00004C_BUS_MSI_REARM 0xFFEFFFFF +#define R_000070_MC_IND_INDEX 0x000070 +#define S_000070_MC_IND_ADDR(x) (((x) & 0xFFFF) << 0) +#define G_000070_MC_IND_ADDR(x) (((x) >> 0) & 0xFFFF) +#define C_000070_MC_IND_ADDR 0xFFFF0000 +#define S_000070_MC_IND_SEQ_RBS_0(x) (((x) & 0x1) << 16) +#define G_000070_MC_IND_SEQ_RBS_0(x) (((x) >> 16) & 0x1) +#define C_000070_MC_IND_SEQ_RBS_0 0xFFFEFFFF +#define S_000070_MC_IND_SEQ_RBS_1(x) (((x) & 0x1) << 17) +#define G_000070_MC_IND_SEQ_RBS_1(x) (((x) >> 17) & 0x1) +#define C_000070_MC_IND_SEQ_RBS_1 0xFFFDFFFF +#define S_000070_MC_IND_SEQ_RBS_2(x) (((x) & 0x1) << 18) +#define G_000070_MC_IND_SEQ_RBS_2(x) (((x) >> 18) & 0x1) +#define C_000070_MC_IND_SEQ_RBS_2 0xFFFBFFFF +#define S_000070_MC_IND_SEQ_RBS_3(x) (((x) & 0x1) << 19) +#define G_000070_MC_IND_SEQ_RBS_3(x) (((x) >> 19) & 0x1) +#define C_000070_MC_IND_SEQ_RBS_3 0xFFF7FFFF +#define S_000070_MC_IND_AIC_RBS(x) (((x) & 0x1) << 20) +#define G_000070_MC_IND_AIC_RBS(x) (((x) >> 20) & 0x1) +#define C_000070_MC_IND_AIC_RBS 0xFFEFFFFF +#define S_000070_MC_IND_CITF_ARB0(x) (((x) & 0x1) << 21) +#define G_000070_MC_IND_CITF_ARB0(x) (((x) >> 21) & 0x1) +#define C_000070_MC_IND_CITF_ARB0 0xFFDFFFFF +#define S_000070_MC_IND_CITF_ARB1(x) (((x) & 0x1) << 22) +#define G_000070_MC_IND_CITF_ARB1(x) (((x) >> 22) & 0x1) +#define C_000070_MC_IND_CITF_ARB1 0xFFBFFFFF +#define S_000070_MC_IND_WR_EN(x) (((x) & 0x1) << 23) +#define G_000070_MC_IND_WR_EN(x) (((x) >> 23) & 0x1) +#define C_000070_MC_IND_WR_EN 0xFF7FFFFF +#define S_000070_MC_IND_RD_INV(x) (((x) & 0x1) << 24) +#define G_000070_MC_IND_RD_INV(x) (((x) >> 24) & 0x1) +#define C_000070_MC_IND_RD_INV 0xFEFFFFFF +#define R_000074_MC_IND_DATA 0x000074 +#define S_000074_MC_IND_DATA(x) (((x) & 0xFFFFFFFF) << 0) +#define G_000074_MC_IND_DATA(x) (((x) >> 0) & 0xFFFFFFFF) +#define C_000074_MC_IND_DATA 0x00000000 +#define R_000134_HDP_FB_LOCATION 0x000134 +#define S_000134_HDP_FB_START(x) (((x) & 0xFFFF) << 0) +#define G_000134_HDP_FB_START(x) (((x) >> 0) & 0xFFFF) +#define C_000134_HDP_FB_START 0xFFFF0000 +#define R_0007C0_CP_STAT 0x0007C0 +#define S_0007C0_MRU_BUSY(x) (((x) & 0x1) << 0) +#define G_0007C0_MRU_BUSY(x) (((x) >> 0) & 0x1) +#define C_0007C0_MRU_BUSY 0xFFFFFFFE +#define S_0007C0_MWU_BUSY(x) (((x) & 0x1) << 1) +#define G_0007C0_MWU_BUSY(x) (((x) >> 1) & 0x1) +#define C_0007C0_MWU_BUSY 0xFFFFFFFD +#define S_0007C0_RSIU_BUSY(x) (((x) & 0x1) << 2) +#define G_0007C0_RSIU_BUSY(x) (((x) >> 2) & 0x1) +#define C_0007C0_RSIU_BUSY 0xFFFFFFFB +#define S_0007C0_RCIU_BUSY(x) (((x) & 0x1) << 3) +#define G_0007C0_RCIU_BUSY(x) (((x) >> 3) & 0x1) +#define C_0007C0_RCIU_BUSY 0xFFFFFFF7 +#define S_0007C0_CSF_PRIMARY_BUSY(x) (((x) & 0x1) << 9) +#define G_0007C0_CSF_PRIMARY_BUSY(x) (((x) >> 9) & 0x1) +#define C_0007C0_CSF_PRIMARY_BUSY 0xFFFFFDFF +#define S_0007C0_CSF_INDIRECT_BUSY(x) (((x) & 0x1) << 10) +#define G_0007C0_CSF_INDIRECT_BUSY(x) (((x) >> 10) & 0x1) +#define C_0007C0_CSF_INDIRECT_BUSY 0xFFFFFBFF +#define S_0007C0_CSQ_PRIMARY_BUSY(x) (((x) & 0x1) << 11) +#define G_0007C0_CSQ_PRIMARY_BUSY(x) (((x) >> 11) & 0x1) +#define C_0007C0_CSQ_PRIMARY_BUSY 0xFFFFF7FF +#define S_0007C0_CSQ_INDIRECT_BUSY(x) (((x) & 0x1) << 12) +#define G_0007C0_CSQ_INDIRECT_BUSY(x) (((x) >> 12) & 0x1) +#define C_0007C0_CSQ_INDIRECT_BUSY 0xFFFFEFFF +#define S_0007C0_CSI_BUSY(x) (((x) & 0x1) << 13) +#define G_0007C0_CSI_BUSY(x) (((x) >> 13) & 0x1) +#define C_0007C0_CSI_BUSY 0xFFFFDFFF +#define S_0007C0_CSF_INDIRECT2_BUSY(x) (((x) & 0x1) << 14) +#define G_0007C0_CSF_INDIRECT2_BUSY(x) (((x) >> 14) & 0x1) +#define C_0007C0_CSF_INDIRECT2_BUSY 0xFFFFBFFF +#define S_0007C0_CSQ_INDIRECT2_BUSY(x) (((x) & 0x1) << 15) +#define G_0007C0_CSQ_INDIRECT2_BUSY(x) (((x) >> 15) & 0x1) +#define C_0007C0_CSQ_INDIRECT2_BUSY 0xFFFF7FFF +#define S_0007C0_GUIDMA_BUSY(x) (((x) & 0x1) << 28) +#define G_0007C0_GUIDMA_BUSY(x) (((x) >> 28) & 0x1) +#define C_0007C0_GUIDMA_BUSY 0xEFFFFFFF +#define S_0007C0_VIDDMA_BUSY(x) (((x) & 0x1) << 29) +#define G_0007C0_VIDDMA_BUSY(x) (((x) >> 29) & 0x1) +#define C_0007C0_VIDDMA_BUSY 0xDFFFFFFF +#define S_0007C0_CMDSTRM_BUSY(x) (((x) & 0x1) << 30) +#define G_0007C0_CMDSTRM_BUSY(x) (((x) >> 30) & 0x1) +#define C_0007C0_CMDSTRM_BUSY 0xBFFFFFFF +#define S_0007C0_CP_BUSY(x) (((x) & 0x1) << 31) +#define G_0007C0_CP_BUSY(x) (((x) >> 31) & 0x1) +#define C_0007C0_CP_BUSY 0x7FFFFFFF +#define R_000E40_RBBM_STATUS 0x000E40 +#define S_000E40_CMDFIFO_AVAIL(x) (((x) & 0x7F) << 0) +#define G_000E40_CMDFIFO_AVAIL(x) (((x) >> 0) & 0x7F) +#define C_000E40_CMDFIFO_AVAIL 0xFFFFFF80 +#define S_000E40_HIRQ_ON_RBB(x) (((x) & 0x1) << 8) +#define G_000E40_HIRQ_ON_RBB(x) (((x) >> 8) & 0x1) +#define C_000E40_HIRQ_ON_RBB 0xFFFFFEFF +#define S_000E40_CPRQ_ON_RBB(x) (((x) & 0x1) << 9) +#define G_000E40_CPRQ_ON_RBB(x) (((x) >> 9) & 0x1) +#define C_000E40_CPRQ_ON_RBB 0xFFFFFDFF +#define S_000E40_CFRQ_ON_RBB(x) (((x) & 0x1) << 10) +#define G_000E40_CFRQ_ON_RBB(x) (((x) >> 10) & 0x1) +#define C_000E40_CFRQ_ON_RBB 0xFFFFFBFF +#define S_000E40_HIRQ_IN_RTBUF(x) (((x) & 0x1) << 11) +#define G_000E40_HIRQ_IN_RTBUF(x) (((x) >> 11) & 0x1) +#define C_000E40_HIRQ_IN_RTBUF 0xFFFFF7FF +#define S_000E40_CPRQ_IN_RTBUF(x) (((x) & 0x1) << 12) +#define G_000E40_CPRQ_IN_RTBUF(x) (((x) >> 12) & 0x1) +#define C_000E40_CPRQ_IN_RTBUF 0xFFFFEFFF +#define S_000E40_CFRQ_IN_RTBUF(x) (((x) & 0x1) << 13) +#define G_000E40_CFRQ_IN_RTBUF(x) (((x) >> 13) & 0x1) +#define C_000E40_CFRQ_IN_RTBUF 0xFFFFDFFF +#define S_000E40_CF_PIPE_BUSY(x) (((x) & 0x1) << 14) +#define G_000E40_CF_PIPE_BUSY(x) (((x) >> 14) & 0x1) +#define C_000E40_CF_PIPE_BUSY 0xFFFFBFFF +#define S_000E40_ENG_EV_BUSY(x) (((x) & 0x1) << 15) +#define G_000E40_ENG_EV_BUSY(x) (((x) >> 15) & 0x1) +#define C_000E40_ENG_EV_BUSY 0xFFFF7FFF +#define S_000E40_CP_CMDSTRM_BUSY(x) (((x) & 0x1) << 16) +#define G_000E40_CP_CMDSTRM_BUSY(x) (((x) >> 16) & 0x1) +#define C_000E40_CP_CMDSTRM_BUSY 0xFFFEFFFF +#define S_000E40_E2_BUSY(x) (((x) & 0x1) << 17) +#define G_000E40_E2_BUSY(x) (((x) >> 17) & 0x1) +#define C_000E40_E2_BUSY 0xFFFDFFFF +#define S_000E40_RB2D_BUSY(x) (((x) & 0x1) << 18) +#define G_000E40_RB2D_BUSY(x) (((x) >> 18) & 0x1) +#define C_000E40_RB2D_BUSY 0xFFFBFFFF +#define S_000E40_RB3D_BUSY(x) (((x) & 0x1) << 19) +#define G_000E40_RB3D_BUSY(x) (((x) >> 19) & 0x1) +#define C_000E40_RB3D_BUSY 0xFFF7FFFF +#define S_000E40_VAP_BUSY(x) (((x) & 0x1) << 20) +#define G_000E40_VAP_BUSY(x) (((x) >> 20) & 0x1) +#define C_000E40_VAP_BUSY 0xFFEFFFFF +#define S_000E40_RE_BUSY(x) (((x) & 0x1) << 21) +#define G_000E40_RE_BUSY(x) (((x) >> 21) & 0x1) +#define C_000E40_RE_BUSY 0xFFDFFFFF +#define S_000E40_TAM_BUSY(x) (((x) & 0x1) << 22) +#define G_000E40_TAM_BUSY(x) (((x) >> 22) & 0x1) +#define C_000E40_TAM_BUSY 0xFFBFFFFF +#define S_000E40_TDM_BUSY(x) (((x) & 0x1) << 23) +#define G_000E40_TDM_BUSY(x) (((x) >> 23) & 0x1) +#define C_000E40_TDM_BUSY 0xFF7FFFFF +#define S_000E40_PB_BUSY(x) (((x) & 0x1) << 24) +#define G_000E40_PB_BUSY(x) (((x) >> 24) & 0x1) +#define C_000E40_PB_BUSY 0xFEFFFFFF +#define S_000E40_TIM_BUSY(x) (((x) & 0x1) << 25) +#define G_000E40_TIM_BUSY(x) (((x) >> 25) & 0x1) +#define C_000E40_TIM_BUSY 0xFDFFFFFF +#define S_000E40_GA_BUSY(x) (((x) & 0x1) << 26) +#define G_000E40_GA_BUSY(x) (((x) >> 26) & 0x1) +#define C_000E40_GA_BUSY 0xFBFFFFFF +#define S_000E40_CBA2D_BUSY(x) (((x) & 0x1) << 27) +#define G_000E40_CBA2D_BUSY(x) (((x) >> 27) & 0x1) +#define C_000E40_CBA2D_BUSY 0xF7FFFFFF +#define S_000E40_GUI_ACTIVE(x) (((x) & 0x1) << 31) +#define G_000E40_GUI_ACTIVE(x) (((x) >> 31) & 0x1) +#define C_000E40_GUI_ACTIVE 0x7FFFFFFF +#define R_0060A4_D1CRTC_STATUS_FRAME_COUNT 0x0060A4 +#define S_0060A4_D1CRTC_FRAME_COUNT(x) (((x) & 0xFFFFFF) << 0) +#define G_0060A4_D1CRTC_FRAME_COUNT(x) (((x) >> 0) & 0xFFFFFF) +#define C_0060A4_D1CRTC_FRAME_COUNT 0xFF000000 +#define R_006534_D1MODE_VBLANK_STATUS 0x006534 +#define S_006534_D1MODE_VBLANK_OCCURRED(x) (((x) & 0x1) << 0) +#define G_006534_D1MODE_VBLANK_OCCURRED(x) (((x) >> 0) & 0x1) +#define C_006534_D1MODE_VBLANK_OCCURRED 0xFFFFFFFE +#define S_006534_D1MODE_VBLANK_ACK(x) (((x) & 0x1) << 4) +#define G_006534_D1MODE_VBLANK_ACK(x) (((x) >> 4) & 0x1) +#define C_006534_D1MODE_VBLANK_ACK 0xFFFFFFEF +#define S_006534_D1MODE_VBLANK_STAT(x) (((x) & 0x1) << 12) +#define G_006534_D1MODE_VBLANK_STAT(x) (((x) >> 12) & 0x1) +#define C_006534_D1MODE_VBLANK_STAT 0xFFFFEFFF +#define S_006534_D1MODE_VBLANK_INTERRUPT(x) (((x) & 0x1) << 16) +#define G_006534_D1MODE_VBLANK_INTERRUPT(x) (((x) >> 16) & 0x1) +#define C_006534_D1MODE_VBLANK_INTERRUPT 0xFFFEFFFF +#define R_006540_DxMODE_INT_MASK 0x006540 +#define S_006540_D1MODE_VBLANK_INT_MASK(x) (((x) & 0x1) << 0) +#define G_006540_D1MODE_VBLANK_INT_MASK(x) (((x) >> 0) & 0x1) +#define C_006540_D1MODE_VBLANK_INT_MASK 0xFFFFFFFE +#define S_006540_D1MODE_VLINE_INT_MASK(x) (((x) & 0x1) << 4) +#define G_006540_D1MODE_VLINE_INT_MASK(x) (((x) >> 4) & 0x1) +#define C_006540_D1MODE_VLINE_INT_MASK 0xFFFFFFEF +#define S_006540_D2MODE_VBLANK_INT_MASK(x) (((x) & 0x1) << 8) +#define G_006540_D2MODE_VBLANK_INT_MASK(x) (((x) >> 8) & 0x1) +#define C_006540_D2MODE_VBLANK_INT_MASK 0xFFFFFEFF +#define S_006540_D2MODE_VLINE_INT_MASK(x) (((x) & 0x1) << 12) +#define G_006540_D2MODE_VLINE_INT_MASK(x) (((x) >> 12) & 0x1) +#define C_006540_D2MODE_VLINE_INT_MASK 0xFFFFEFFF +#define S_006540_D1MODE_VBLANK_CP_SEL(x) (((x) & 0x1) << 30) +#define G_006540_D1MODE_VBLANK_CP_SEL(x) (((x) >> 30) & 0x1) +#define C_006540_D1MODE_VBLANK_CP_SEL 0xBFFFFFFF +#define S_006540_D2MODE_VBLANK_CP_SEL(x) (((x) & 0x1) << 31) +#define G_006540_D2MODE_VBLANK_CP_SEL(x) (((x) >> 31) & 0x1) +#define C_006540_D2MODE_VBLANK_CP_SEL 0x7FFFFFFF +#define R_0068A4_D2CRTC_STATUS_FRAME_COUNT 0x0068A4 +#define S_0068A4_D2CRTC_FRAME_COUNT(x) (((x) & 0xFFFFFF) << 0) +#define G_0068A4_D2CRTC_FRAME_COUNT(x) (((x) >> 0) & 0xFFFFFF) +#define C_0068A4_D2CRTC_FRAME_COUNT 0xFF000000 +#define R_006D34_D2MODE_VBLANK_STATUS 0x006D34 +#define S_006D34_D2MODE_VBLANK_OCCURRED(x) (((x) & 0x1) << 0) +#define G_006D34_D2MODE_VBLANK_OCCURRED(x) (((x) >> 0) & 0x1) +#define C_006D34_D2MODE_VBLANK_OCCURRED 0xFFFFFFFE +#define S_006D34_D2MODE_VBLANK_ACK(x) (((x) & 0x1) << 4) +#define G_006D34_D2MODE_VBLANK_ACK(x) (((x) >> 4) & 0x1) +#define C_006D34_D2MODE_VBLANK_ACK 0xFFFFFFEF +#define S_006D34_D2MODE_VBLANK_STAT(x) (((x) & 0x1) << 12) +#define G_006D34_D2MODE_VBLANK_STAT(x) (((x) >> 12) & 0x1) +#define C_006D34_D2MODE_VBLANK_STAT 0xFFFFEFFF +#define S_006D34_D2MODE_VBLANK_INTERRUPT(x) (((x) & 0x1) << 16) +#define G_006D34_D2MODE_VBLANK_INTERRUPT(x) (((x) >> 16) & 0x1) +#define C_006D34_D2MODE_VBLANK_INTERRUPT 0xFFFEFFFF +#define R_007EDC_DISP_INTERRUPT_STATUS 0x007EDC +#define S_007EDC_LB_D1_VBLANK_INTERRUPT(x) (((x) & 0x1) << 4) +#define G_007EDC_LB_D1_VBLANK_INTERRUPT(x) (((x) >> 4) & 0x1) +#define C_007EDC_LB_D1_VBLANK_INTERRUPT 0xFFFFFFEF +#define S_007EDC_LB_D2_VBLANK_INTERRUPT(x) (((x) & 0x1) << 5) +#define G_007EDC_LB_D2_VBLANK_INTERRUPT(x) (((x) >> 5) & 0x1) +#define C_007EDC_LB_D2_VBLANK_INTERRUPT 0xFFFFFFDF + + +/* MC registers */ +#define R_000000_MC_STATUS 0x000000 +#define S_000000_MC_IDLE(x) (((x) & 0x1) << 0) +#define G_000000_MC_IDLE(x) (((x) >> 0) & 0x1) +#define C_000000_MC_IDLE 0xFFFFFFFE +#define R_000004_MC_FB_LOCATION 0x000004 +#define S_000004_MC_FB_START(x) (((x) & 0xFFFF) << 0) +#define G_000004_MC_FB_START(x) (((x) >> 0) & 0xFFFF) +#define C_000004_MC_FB_START 0xFFFF0000 +#define S_000004_MC_FB_TOP(x) (((x) & 0xFFFF) << 16) +#define G_000004_MC_FB_TOP(x) (((x) >> 16) & 0xFFFF) +#define C_000004_MC_FB_TOP 0x0000FFFF +#define R_000005_MC_AGP_LOCATION 0x000005 +#define S_000005_MC_AGP_START(x) (((x) & 0xFFFF) << 0) +#define G_000005_MC_AGP_START(x) (((x) >> 0) & 0xFFFF) +#define C_000005_MC_AGP_START 0xFFFF0000 +#define S_000005_MC_AGP_TOP(x) (((x) & 0xFFFF) << 16) +#define G_000005_MC_AGP_TOP(x) (((x) >> 16) & 0xFFFF) +#define C_000005_MC_AGP_TOP 0x0000FFFF +#define R_000006_AGP_BASE 0x000006 +#define S_000006_AGP_BASE_ADDR(x) (((x) & 0xFFFFFFFF) << 0) +#define G_000006_AGP_BASE_ADDR(x) (((x) >> 0) & 0xFFFFFFFF) +#define C_000006_AGP_BASE_ADDR 0x00000000 +#define R_000007_AGP_BASE_2 0x000007 +#define S_000007_AGP_BASE_ADDR_2(x) (((x) & 0xF) << 0) +#define G_000007_AGP_BASE_ADDR_2(x) (((x) >> 0) & 0xF) +#define C_000007_AGP_BASE_ADDR_2 0xFFFFFFF0 +#define R_000009_MC_CNTL1 0x000009 +#define S_000009_ENABLE_PAGE_TABLES(x) (((x) & 0x1) << 26) +#define G_000009_ENABLE_PAGE_TABLES(x) (((x) >> 26) & 0x1) +#define C_000009_ENABLE_PAGE_TABLES 0xFBFFFFFF +/* FIXME don't know the various field size need feedback from AMD */ +#define R_000100_MC_PT0_CNTL 0x000100 +#define S_000100_ENABLE_PT(x) (((x) & 0x1) << 0) +#define G_000100_ENABLE_PT(x) (((x) >> 0) & 0x1) +#define C_000100_ENABLE_PT 0xFFFFFFFE +#define S_000100_EFFECTIVE_L2_CACHE_SIZE(x) (((x) & 0x7) << 15) +#define G_000100_EFFECTIVE_L2_CACHE_SIZE(x) (((x) >> 15) & 0x7) +#define C_000100_EFFECTIVE_L2_CACHE_SIZE 0xFFFC7FFF +#define S_000100_EFFECTIVE_L2_QUEUE_SIZE(x) (((x) & 0x7) << 21) +#define G_000100_EFFECTIVE_L2_QUEUE_SIZE(x) (((x) >> 21) & 0x7) +#define C_000100_EFFECTIVE_L2_QUEUE_SIZE 0xFF1FFFFF +#define S_000100_INVALIDATE_ALL_L1_TLBS(x) (((x) & 0x1) << 28) +#define G_000100_INVALIDATE_ALL_L1_TLBS(x) (((x) >> 28) & 0x1) +#define C_000100_INVALIDATE_ALL_L1_TLBS 0xEFFFFFFF +#define S_000100_INVALIDATE_L2_CACHE(x) (((x) & 0x1) << 29) +#define G_000100_INVALIDATE_L2_CACHE(x) (((x) >> 29) & 0x1) +#define C_000100_INVALIDATE_L2_CACHE 0xDFFFFFFF +#define R_000102_MC_PT0_CONTEXT0_CNTL 0x000102 +#define S_000102_ENABLE_PAGE_TABLE(x) (((x) & 0x1) << 0) +#define G_000102_ENABLE_PAGE_TABLE(x) (((x) >> 0) & 0x1) +#define C_000102_ENABLE_PAGE_TABLE 0xFFFFFFFE +#define S_000102_PAGE_TABLE_DEPTH(x) (((x) & 0x3) << 1) +#define G_000102_PAGE_TABLE_DEPTH(x) (((x) >> 1) & 0x3) +#define C_000102_PAGE_TABLE_DEPTH 0xFFFFFFF9 +#define V_000102_PAGE_TABLE_FLAT 0 +/* R600 documentation suggest that this should be a number of pages */ +#define R_000112_MC_PT0_SYSTEM_APERTURE_LOW_ADDR 0x000112 +#define R_000114_MC_PT0_SYSTEM_APERTURE_HIGH_ADDR 0x000114 +#define R_00011C_MC_PT0_CONTEXT0_DEFAULT_READ_ADDR 0x00011C +#define R_00012C_MC_PT0_CONTEXT0_FLAT_BASE_ADDR 0x00012C +#define R_00013C_MC_PT0_CONTEXT0_FLAT_START_ADDR 0x00013C +#define R_00014C_MC_PT0_CONTEXT0_FLAT_END_ADDR 0x00014C +#define R_00016C_MC_PT0_CLIENT0_CNTL 0x00016C +#define S_00016C_ENABLE_TRANSLATION_MODE_OVERRIDE(x) (((x) & 0x1) << 0) +#define G_00016C_ENABLE_TRANSLATION_MODE_OVERRIDE(x) (((x) >> 0) & 0x1) +#define C_00016C_ENABLE_TRANSLATION_MODE_OVERRIDE 0xFFFFFFFE +#define S_00016C_TRANSLATION_MODE_OVERRIDE(x) (((x) & 0x1) << 1) +#define G_00016C_TRANSLATION_MODE_OVERRIDE(x) (((x) >> 1) & 0x1) +#define C_00016C_TRANSLATION_MODE_OVERRIDE 0xFFFFFFFD +#define S_00016C_SYSTEM_ACCESS_MODE_MASK(x) (((x) & 0x3) << 8) +#define G_00016C_SYSTEM_ACCESS_MODE_MASK(x) (((x) >> 8) & 0x3) +#define C_00016C_SYSTEM_ACCESS_MODE_MASK 0xFFFFFCFF +#define V_00016C_SYSTEM_ACCESS_MODE_PA_ONLY 0 +#define V_00016C_SYSTEM_ACCESS_MODE_USE_SYS_MAP 1 +#define V_00016C_SYSTEM_ACCESS_MODE_IN_SYS 2 +#define V_00016C_SYSTEM_ACCESS_MODE_NOT_IN_SYS 3 +#define S_00016C_SYSTEM_APERTURE_UNMAPPED_ACCESS(x) (((x) & 0x1) << 10) +#define G_00016C_SYSTEM_APERTURE_UNMAPPED_ACCESS(x) (((x) >> 10) & 0x1) +#define C_00016C_SYSTEM_APERTURE_UNMAPPED_ACCESS 0xFFFFFBFF +#define V_00016C_SYSTEM_APERTURE_UNMAPPED_PASSTHROUGH 0 +#define V_00016C_SYSTEM_APERTURE_UNMAPPED_DEFAULT_PAGE 1 +#define S_00016C_EFFECTIVE_L1_CACHE_SIZE(x) (((x) & 0x7) << 11) +#define G_00016C_EFFECTIVE_L1_CACHE_SIZE(x) (((x) >> 11) & 0x7) +#define C_00016C_EFFECTIVE_L1_CACHE_SIZE 0xFFFFC7FF +#define S_00016C_ENABLE_FRAGMENT_PROCESSING(x) (((x) & 0x1) << 14) +#define G_00016C_ENABLE_FRAGMENT_PROCESSING(x) (((x) >> 14) & 0x1) +#define C_00016C_ENABLE_FRAGMENT_PROCESSING 0xFFFFBFFF +#define S_00016C_EFFECTIVE_L1_QUEUE_SIZE(x) (((x) & 0x7) << 15) +#define G_00016C_EFFECTIVE_L1_QUEUE_SIZE(x) (((x) >> 15) & 0x7) +#define C_00016C_EFFECTIVE_L1_QUEUE_SIZE 0xFFFC7FFF +#define S_00016C_INVALIDATE_L1_TLB(x) (((x) & 0x1) << 20) +#define G_00016C_INVALIDATE_L1_TLB(x) (((x) >> 20) & 0x1) +#define C_00016C_INVALIDATE_L1_TLB 0xFFEFFFFF + +#endif diff --git a/drivers/gpu/drm/radeon/rs690.c b/drivers/gpu/drm/radeon/rs690.c index 0f585ca8276d..27547175cf93 100644 --- a/drivers/gpu/drm/radeon/rs690.c +++ b/drivers/gpu/drm/radeon/rs690.c @@ -26,106 +26,29 @@ * Jerome Glisse */ #include "drmP.h" -#include "radeon_reg.h" #include "radeon.h" -#include "rs690r.h" #include "atom.h" -#include "atom-bits.h" - -/* rs690,rs740 depends on : */ -void r100_hdp_reset(struct radeon_device *rdev); -int r300_mc_wait_for_idle(struct radeon_device *rdev); -void r420_pipes_init(struct radeon_device *rdev); -void rs400_gart_disable(struct radeon_device *rdev); -int rs400_gart_enable(struct radeon_device *rdev); -void rs400_gart_adjust_size(struct radeon_device *rdev); -void rs600_mc_disable_clients(struct radeon_device *rdev); -void rs600_disable_vga(struct radeon_device *rdev); - -/* This files gather functions specifics to : - * rs690,rs740 - * - * Some of these functions might be used by newer ASICs. - */ -void rs690_gpu_init(struct radeon_device *rdev); -int rs690_mc_wait_for_idle(struct radeon_device *rdev); - +#include "rs690d.h" -/* - * MC functions. - */ -int rs690_mc_init(struct radeon_device *rdev) -{ - uint32_t tmp; - int r; - - if (r100_debugfs_rbbm_init(rdev)) { - DRM_ERROR("Failed to register debugfs file for RBBM !\n"); - } - - rs690_gpu_init(rdev); - rs400_gart_disable(rdev); - - /* Setup GPU memory space */ - rdev->mc.gtt_location = rdev->mc.mc_vram_size; - rdev->mc.gtt_location += (rdev->mc.gtt_size - 1); - rdev->mc.gtt_location &= ~(rdev->mc.gtt_size - 1); - rdev->mc.vram_location = 0xFFFFFFFFUL; - r = radeon_mc_setup(rdev); - if (r) { - return r; - } - - /* Program GPU memory space */ - rs600_mc_disable_clients(rdev); - if (rs690_mc_wait_for_idle(rdev)) { - printk(KERN_WARNING "Failed to wait MC idle while " - "programming pipes. Bad things might happen.\n"); - } - tmp = rdev->mc.vram_location + rdev->mc.mc_vram_size - 1; - tmp = REG_SET(RS690_MC_FB_TOP, tmp >> 16); - tmp |= REG_SET(RS690_MC_FB_START, rdev->mc.vram_location >> 16); - WREG32_MC(RS690_MCCFG_FB_LOCATION, tmp); - /* FIXME: Does this reg exist on RS480,RS740 ? */ - WREG32(0x310, rdev->mc.vram_location); - WREG32(RS690_HDP_FB_LOCATION, rdev->mc.vram_location >> 16); - return 0; -} - -void rs690_mc_fini(struct radeon_device *rdev) -{ -} - - -/* - * Global GPU functions - */ -int rs690_mc_wait_for_idle(struct radeon_device *rdev) +static int rs690_mc_wait_for_idle(struct radeon_device *rdev) { unsigned i; uint32_t tmp; for (i = 0; i < rdev->usec_timeout; i++) { /* read MC_STATUS */ - tmp = RREG32_MC(RS690_MC_STATUS); - if (tmp & RS690_MC_STATUS_IDLE) { + tmp = RREG32_MC(R_000090_MC_SYSTEM_STATUS); + if (G_000090_MC_SYSTEM_IDLE(tmp)) return 0; - } - DRM_UDELAY(1); + udelay(1); } return -1; } -void rs690_errata(struct radeon_device *rdev) -{ - rdev->pll_errata = 0; -} - -void rs690_gpu_init(struct radeon_device *rdev) +static void rs690_gpu_init(struct radeon_device *rdev) { /* FIXME: HDP same place on rs690 ? */ r100_hdp_reset(rdev); - rs600_disable_vga(rdev); /* FIXME: is this correct ? */ r420_pipes_init(rdev); if (rs690_mc_wait_for_idle(rdev)) { @@ -134,10 +57,6 @@ void rs690_gpu_init(struct radeon_device *rdev) } } - -/* - * VRAM info. - */ void rs690_pm_info(struct radeon_device *rdev) { int index = GetIndexIntoMasterTable(DATA, IntegratedSystemInfo); @@ -251,39 +170,39 @@ void rs690_line_buffer_adjust(struct radeon_device *rdev, /* * Line Buffer Setup * There is a single line buffer shared by both display controllers. - * DC_LB_MEMORY_SPLIT controls how that line buffer is shared between + * R_006520_DC_LB_MEMORY_SPLIT controls how that line buffer is shared between * the display controllers. The paritioning can either be done * manually or via one of four preset allocations specified in bits 1:0: * 0 - line buffer is divided in half and shared between crtc * 1 - D1 gets 3/4 of the line buffer, D2 gets 1/4 * 2 - D1 gets the whole buffer * 3 - D1 gets 1/4 of the line buffer, D2 gets 3/4 - * Setting bit 2 of DC_LB_MEMORY_SPLIT controls switches to manual + * Setting bit 2 of R_006520_DC_LB_MEMORY_SPLIT controls switches to manual * allocation mode. In manual allocation mode, D1 always starts at 0, * D1 end/2 is specified in bits 14:4; D2 allocation follows D1. */ - tmp = RREG32(DC_LB_MEMORY_SPLIT) & ~DC_LB_MEMORY_SPLIT_MASK; - tmp &= ~DC_LB_MEMORY_SPLIT_SHIFT_MODE; + tmp = RREG32(R_006520_DC_LB_MEMORY_SPLIT) & C_006520_DC_LB_MEMORY_SPLIT; + tmp &= ~C_006520_DC_LB_MEMORY_SPLIT_MODE; /* auto */ if (mode1 && mode2) { if (mode1->hdisplay > mode2->hdisplay) { if (mode1->hdisplay > 2560) - tmp |= DC_LB_MEMORY_SPLIT_D1_3Q_D2_1Q; + tmp |= V_006520_DC_LB_MEMORY_SPLIT_D1_3Q_D2_1Q; else - tmp |= DC_LB_MEMORY_SPLIT_D1HALF_D2HALF; + tmp |= V_006520_DC_LB_MEMORY_SPLIT_D1HALF_D2HALF; } else if (mode2->hdisplay > mode1->hdisplay) { if (mode2->hdisplay > 2560) - tmp |= DC_LB_MEMORY_SPLIT_D1_1Q_D2_3Q; + tmp |= V_006520_DC_LB_MEMORY_SPLIT_D1_1Q_D2_3Q; else - tmp |= DC_LB_MEMORY_SPLIT_D1HALF_D2HALF; + tmp |= V_006520_DC_LB_MEMORY_SPLIT_D1HALF_D2HALF; } else - tmp |= AVIVO_DC_LB_MEMORY_SPLIT_D1HALF_D2HALF; + tmp |= V_006520_DC_LB_MEMORY_SPLIT_D1HALF_D2HALF; } else if (mode1) { - tmp |= DC_LB_MEMORY_SPLIT_D1_ONLY; + tmp |= V_006520_DC_LB_MEMORY_SPLIT_D1_ONLY; } else if (mode2) { - tmp |= DC_LB_MEMORY_SPLIT_D1_1Q_D2_3Q; + tmp |= V_006520_DC_LB_MEMORY_SPLIT_D1_1Q_D2_3Q; } - WREG32(DC_LB_MEMORY_SPLIT, tmp); + WREG32(R_006520_DC_LB_MEMORY_SPLIT, tmp); } struct rs690_watermark { @@ -488,28 +407,28 @@ void rs690_bandwidth_update(struct radeon_device *rdev) * option. */ if (rdev->disp_priority == 2) { - tmp = RREG32_MC(MC_INIT_MISC_LAT_TIMER); - tmp &= ~MC_DISP1R_INIT_LAT_MASK; - tmp &= ~MC_DISP0R_INIT_LAT_MASK; - if (mode1) - tmp |= (1 << MC_DISP1R_INIT_LAT_SHIFT); + tmp = RREG32_MC(R_000104_MC_INIT_MISC_LAT_TIMER); + tmp &= C_000104_MC_DISP0R_INIT_LAT; + tmp &= C_000104_MC_DISP1R_INIT_LAT; if (mode0) - tmp |= (1 << MC_DISP0R_INIT_LAT_SHIFT); - WREG32_MC(MC_INIT_MISC_LAT_TIMER, tmp); + tmp |= S_000104_MC_DISP0R_INIT_LAT(1); + if (mode1) + tmp |= S_000104_MC_DISP1R_INIT_LAT(1); + WREG32_MC(R_000104_MC_INIT_MISC_LAT_TIMER, tmp); } rs690_line_buffer_adjust(rdev, mode0, mode1); if ((rdev->family == CHIP_RS690) || (rdev->family == CHIP_RS740)) - WREG32(DCP_CONTROL, 0); + WREG32(R_006C9C_DCP_CONTROL, 0); if ((rdev->family == CHIP_RS780) || (rdev->family == CHIP_RS880)) - WREG32(DCP_CONTROL, 2); + WREG32(R_006C9C_DCP_CONTROL, 2); rs690_crtc_bandwidth_compute(rdev, rdev->mode_info.crtcs[0], &wm0); rs690_crtc_bandwidth_compute(rdev, rdev->mode_info.crtcs[1], &wm1); tmp = (wm0.lb_request_fifo_depth - 1); tmp |= (wm1.lb_request_fifo_depth - 1) << 16; - WREG32(LB_MAX_REQ_OUTSTANDING, tmp); + WREG32(R_006D58_LB_MAX_REQ_OUTSTANDING, tmp); if (mode0 && mode1) { if (rfixed_trunc(wm0.dbpp) > 64) @@ -562,10 +481,10 @@ void rs690_bandwidth_update(struct radeon_device *rdev) priority_mark12.full = 0; if (wm1.priority_mark_max.full > priority_mark12.full) priority_mark12.full = wm1.priority_mark_max.full; - WREG32(D1MODE_PRIORITY_A_CNT, rfixed_trunc(priority_mark02)); - WREG32(D1MODE_PRIORITY_B_CNT, rfixed_trunc(priority_mark02)); - WREG32(D2MODE_PRIORITY_A_CNT, rfixed_trunc(priority_mark12)); - WREG32(D2MODE_PRIORITY_B_CNT, rfixed_trunc(priority_mark12)); + WREG32(R_006548_D1MODE_PRIORITY_A_CNT, rfixed_trunc(priority_mark02)); + WREG32(R_00654C_D1MODE_PRIORITY_B_CNT, rfixed_trunc(priority_mark02)); + WREG32(R_006D48_D2MODE_PRIORITY_A_CNT, rfixed_trunc(priority_mark12)); + WREG32(R_006D4C_D2MODE_PRIORITY_B_CNT, rfixed_trunc(priority_mark12)); } else if (mode0) { if (rfixed_trunc(wm0.dbpp) > 64) a.full = rfixed_mul(wm0.dbpp, wm0.num_line_pair); @@ -592,10 +511,12 @@ void rs690_bandwidth_update(struct radeon_device *rdev) priority_mark02.full = 0; if (wm0.priority_mark_max.full > priority_mark02.full) priority_mark02.full = wm0.priority_mark_max.full; - WREG32(D1MODE_PRIORITY_A_CNT, rfixed_trunc(priority_mark02)); - WREG32(D1MODE_PRIORITY_B_CNT, rfixed_trunc(priority_mark02)); - WREG32(D2MODE_PRIORITY_A_CNT, MODE_PRIORITY_OFF); - WREG32(D2MODE_PRIORITY_B_CNT, MODE_PRIORITY_OFF); + WREG32(R_006548_D1MODE_PRIORITY_A_CNT, rfixed_trunc(priority_mark02)); + WREG32(R_00654C_D1MODE_PRIORITY_B_CNT, rfixed_trunc(priority_mark02)); + WREG32(R_006D48_D2MODE_PRIORITY_A_CNT, + S_006D48_D2MODE_PRIORITY_A_OFF(1)); + WREG32(R_006D4C_D2MODE_PRIORITY_B_CNT, + S_006D4C_D2MODE_PRIORITY_B_OFF(1)); } else { if (rfixed_trunc(wm1.dbpp) > 64) a.full = rfixed_mul(wm1.dbpp, wm1.num_line_pair); @@ -622,30 +543,205 @@ void rs690_bandwidth_update(struct radeon_device *rdev) priority_mark12.full = 0; if (wm1.priority_mark_max.full > priority_mark12.full) priority_mark12.full = wm1.priority_mark_max.full; - WREG32(D1MODE_PRIORITY_A_CNT, MODE_PRIORITY_OFF); - WREG32(D1MODE_PRIORITY_B_CNT, MODE_PRIORITY_OFF); - WREG32(D2MODE_PRIORITY_A_CNT, rfixed_trunc(priority_mark12)); - WREG32(D2MODE_PRIORITY_B_CNT, rfixed_trunc(priority_mark12)); + WREG32(R_006548_D1MODE_PRIORITY_A_CNT, + S_006548_D1MODE_PRIORITY_A_OFF(1)); + WREG32(R_00654C_D1MODE_PRIORITY_B_CNT, + S_00654C_D1MODE_PRIORITY_B_OFF(1)); + WREG32(R_006D48_D2MODE_PRIORITY_A_CNT, rfixed_trunc(priority_mark12)); + WREG32(R_006D4C_D2MODE_PRIORITY_B_CNT, rfixed_trunc(priority_mark12)); } } -/* - * Indirect registers accessor - */ uint32_t rs690_mc_rreg(struct radeon_device *rdev, uint32_t reg) { uint32_t r; - WREG32(RS690_MC_INDEX, (reg & RS690_MC_INDEX_MASK)); - r = RREG32(RS690_MC_DATA); - WREG32(RS690_MC_INDEX, RS690_MC_INDEX_MASK); + WREG32(R_000078_MC_INDEX, S_000078_MC_IND_ADDR(reg)); + r = RREG32(R_00007C_MC_DATA); + WREG32(R_000078_MC_INDEX, ~C_000078_MC_IND_ADDR); return r; } void rs690_mc_wreg(struct radeon_device *rdev, uint32_t reg, uint32_t v) { - WREG32(RS690_MC_INDEX, - RS690_MC_INDEX_WR_EN | ((reg) & RS690_MC_INDEX_MASK)); - WREG32(RS690_MC_DATA, v); - WREG32(RS690_MC_INDEX, RS690_MC_INDEX_WR_ACK); + WREG32(R_000078_MC_INDEX, S_000078_MC_IND_ADDR(reg) | + S_000078_MC_IND_WR_EN(1)); + WREG32(R_00007C_MC_DATA, v); + WREG32(R_000078_MC_INDEX, 0x7F); +} + +void rs690_mc_program(struct radeon_device *rdev) +{ + struct rv515_mc_save save; + + /* Stops all mc clients */ + rv515_mc_stop(rdev, &save); + + /* Wait for mc idle */ + if (rs690_mc_wait_for_idle(rdev)) + dev_warn(rdev->dev, "Wait MC idle timeout before updating MC.\n"); + /* Program MC, should be a 32bits limited address space */ + WREG32_MC(R_000100_MCCFG_FB_LOCATION, + S_000100_MC_FB_START(rdev->mc.vram_start >> 16) | + S_000100_MC_FB_TOP(rdev->mc.vram_end >> 16)); + WREG32(R_000134_HDP_FB_LOCATION, + S_000134_HDP_FB_START(rdev->mc.vram_start >> 16)); + + rv515_mc_resume(rdev, &save); +} + +static int rs690_startup(struct radeon_device *rdev) +{ + int r; + + rs690_mc_program(rdev); + /* Resume clock */ + rv515_clock_startup(rdev); + /* Initialize GPU configuration (# pipes, ...) */ + rs690_gpu_init(rdev); + /* Initialize GART (initialize after TTM so we can allocate + * memory through TTM but finalize after TTM) */ + r = rs400_gart_enable(rdev); + if (r) + return r; + /* Enable IRQ */ + rdev->irq.sw_int = true; + rs600_irq_set(rdev); + /* 1M ring buffer */ + r = r100_cp_init(rdev, 1024 * 1024); + if (r) { + dev_err(rdev->dev, "failled initializing CP (%d).\n", r); + return r; + } + r = r100_wb_init(rdev); + if (r) + dev_err(rdev->dev, "failled initializing WB (%d).\n", r); + r = r100_ib_init(rdev); + if (r) { + dev_err(rdev->dev, "failled initializing IB (%d).\n", r); + return r; + } + return 0; +} + +int rs690_resume(struct radeon_device *rdev) +{ + /* Make sur GART are not working */ + rs400_gart_disable(rdev); + /* Resume clock before doing reset */ + rv515_clock_startup(rdev); + /* Reset gpu before posting otherwise ATOM will enter infinite loop */ + if (radeon_gpu_reset(rdev)) { + dev_warn(rdev->dev, "GPU reset failed ! (0xE40=0x%08X, 0x7C0=0x%08X)\n", + RREG32(R_000E40_RBBM_STATUS), + RREG32(R_0007C0_CP_STAT)); + } + /* post */ + atom_asic_init(rdev->mode_info.atom_context); + /* Resume clock after posting */ + rv515_clock_startup(rdev); + return rs690_startup(rdev); +} + +int rs690_suspend(struct radeon_device *rdev) +{ + r100_cp_disable(rdev); + r100_wb_disable(rdev); + rs600_irq_disable(rdev); + rs400_gart_disable(rdev); + return 0; +} + +void rs690_fini(struct radeon_device *rdev) +{ + rs690_suspend(rdev); + r100_cp_fini(rdev); + r100_wb_fini(rdev); + r100_ib_fini(rdev); + radeon_gem_fini(rdev); + rs400_gart_fini(rdev); + radeon_irq_kms_fini(rdev); + radeon_fence_driver_fini(rdev); + radeon_object_fini(rdev); + radeon_atombios_fini(rdev); + kfree(rdev->bios); + rdev->bios = NULL; +} + +int rs690_init(struct radeon_device *rdev) +{ + int r; + + /* Disable VGA */ + rv515_vga_render_disable(rdev); + /* Initialize scratch registers */ + radeon_scratch_init(rdev); + /* Initialize surface registers */ + radeon_surface_init(rdev); + /* TODO: disable VGA need to use VGA request */ + /* BIOS*/ + if (!radeon_get_bios(rdev)) { + if (ASIC_IS_AVIVO(rdev)) + return -EINVAL; + } + if (rdev->is_atom_bios) { + r = radeon_atombios_init(rdev); + if (r) + return r; + } else { + dev_err(rdev->dev, "Expecting atombios for RV515 GPU\n"); + return -EINVAL; + } + /* Reset gpu before posting otherwise ATOM will enter infinite loop */ + if (radeon_gpu_reset(rdev)) { + dev_warn(rdev->dev, + "GPU reset failed ! (0xE40=0x%08X, 0x7C0=0x%08X)\n", + RREG32(R_000E40_RBBM_STATUS), + RREG32(R_0007C0_CP_STAT)); + } + /* check if cards are posted or not */ + if (!radeon_card_posted(rdev) && rdev->bios) { + DRM_INFO("GPU not posted. posting now...\n"); + atom_asic_init(rdev->mode_info.atom_context); + } + /* Initialize clocks */ + radeon_get_clock_info(rdev->ddev); + /* Initialize power management */ + radeon_pm_init(rdev); + /* Get vram informations */ + rs690_vram_info(rdev); + /* Initialize memory controller (also test AGP) */ + r = r420_mc_init(rdev); + if (r) + return r; + rv515_debugfs(rdev); + /* Fence driver */ + r = radeon_fence_driver_init(rdev); + if (r) + return r; + r = radeon_irq_kms_init(rdev); + if (r) + return r; + /* Memory manager */ + r = radeon_object_init(rdev); + if (r) + return r; + r = rs400_gart_init(rdev); + if (r) + return r; + rs600_set_safe_registers(rdev); + rdev->accel_working = true; + r = rs690_startup(rdev); + if (r) { + /* Somethings want wront with the accel init stop accel */ + dev_err(rdev->dev, "Disabling GPU acceleration\n"); + rs690_suspend(rdev); + r100_cp_fini(rdev); + r100_wb_fini(rdev); + r100_ib_fini(rdev); + rs400_gart_fini(rdev); + radeon_irq_kms_fini(rdev); + rdev->accel_working = false; + } + return 0; } diff --git a/drivers/gpu/drm/radeon/rs690d.h b/drivers/gpu/drm/radeon/rs690d.h new file mode 100644 index 000000000000..62d31e7a897f --- /dev/null +++ b/drivers/gpu/drm/radeon/rs690d.h @@ -0,0 +1,307 @@ +/* + * Copyright 2008 Advanced Micro Devices, Inc. + * Copyright 2008 Red Hat Inc. + * Copyright 2009 Jerome Glisse. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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. + * + * Authors: Dave Airlie + * Alex Deucher + * Jerome Glisse + */ +#ifndef __RS690D_H__ +#define __RS690D_H__ + +/* Registers */ +#define R_000078_MC_INDEX 0x000078 +#define S_000078_MC_IND_ADDR(x) (((x) & 0x1FF) << 0) +#define G_000078_MC_IND_ADDR(x) (((x) >> 0) & 0x1FF) +#define C_000078_MC_IND_ADDR 0xFFFFFE00 +#define S_000078_MC_IND_WR_EN(x) (((x) & 0x1) << 9) +#define G_000078_MC_IND_WR_EN(x) (((x) >> 9) & 0x1) +#define C_000078_MC_IND_WR_EN 0xFFFFFDFF +#define R_00007C_MC_DATA 0x00007C +#define S_00007C_MC_DATA(x) (((x) & 0xFFFFFFFF) << 0) +#define G_00007C_MC_DATA(x) (((x) >> 0) & 0xFFFFFFFF) +#define C_00007C_MC_DATA 0x00000000 +#define R_0000F8_CONFIG_MEMSIZE 0x0000F8 +#define S_0000F8_CONFIG_MEMSIZE(x) (((x) & 0xFFFFFFFF) << 0) +#define G_0000F8_CONFIG_MEMSIZE(x) (((x) >> 0) & 0xFFFFFFFF) +#define C_0000F8_CONFIG_MEMSIZE 0x00000000 +#define R_000134_HDP_FB_LOCATION 0x000134 +#define S_000134_HDP_FB_START(x) (((x) & 0xFFFF) << 0) +#define G_000134_HDP_FB_START(x) (((x) >> 0) & 0xFFFF) +#define C_000134_HDP_FB_START 0xFFFF0000 +#define R_0007C0_CP_STAT 0x0007C0 +#define S_0007C0_MRU_BUSY(x) (((x) & 0x1) << 0) +#define G_0007C0_MRU_BUSY(x) (((x) >> 0) & 0x1) +#define C_0007C0_MRU_BUSY 0xFFFFFFFE +#define S_0007C0_MWU_BUSY(x) (((x) & 0x1) << 1) +#define G_0007C0_MWU_BUSY(x) (((x) >> 1) & 0x1) +#define C_0007C0_MWU_BUSY 0xFFFFFFFD +#define S_0007C0_RSIU_BUSY(x) (((x) & 0x1) << 2) +#define G_0007C0_RSIU_BUSY(x) (((x) >> 2) & 0x1) +#define C_0007C0_RSIU_BUSY 0xFFFFFFFB +#define S_0007C0_RCIU_BUSY(x) (((x) & 0x1) << 3) +#define G_0007C0_RCIU_BUSY(x) (((x) >> 3) & 0x1) +#define C_0007C0_RCIU_BUSY 0xFFFFFFF7 +#define S_0007C0_CSF_PRIMARY_BUSY(x) (((x) & 0x1) << 9) +#define G_0007C0_CSF_PRIMARY_BUSY(x) (((x) >> 9) & 0x1) +#define C_0007C0_CSF_PRIMARY_BUSY 0xFFFFFDFF +#define S_0007C0_CSF_INDIRECT_BUSY(x) (((x) & 0x1) << 10) +#define G_0007C0_CSF_INDIRECT_BUSY(x) (((x) >> 10) & 0x1) +#define C_0007C0_CSF_INDIRECT_BUSY 0xFFFFFBFF +#define S_0007C0_CSQ_PRIMARY_BUSY(x) (((x) & 0x1) << 11) +#define G_0007C0_CSQ_PRIMARY_BUSY(x) (((x) >> 11) & 0x1) +#define C_0007C0_CSQ_PRIMARY_BUSY 0xFFFFF7FF +#define S_0007C0_CSQ_INDIRECT_BUSY(x) (((x) & 0x1) << 12) +#define G_0007C0_CSQ_INDIRECT_BUSY(x) (((x) >> 12) & 0x1) +#define C_0007C0_CSQ_INDIRECT_BUSY 0xFFFFEFFF +#define S_0007C0_CSI_BUSY(x) (((x) & 0x1) << 13) +#define G_0007C0_CSI_BUSY(x) (((x) >> 13) & 0x1) +#define C_0007C0_CSI_BUSY 0xFFFFDFFF +#define S_0007C0_CSF_INDIRECT2_BUSY(x) (((x) & 0x1) << 14) +#define G_0007C0_CSF_INDIRECT2_BUSY(x) (((x) >> 14) & 0x1) +#define C_0007C0_CSF_INDIRECT2_BUSY 0xFFFFBFFF +#define S_0007C0_CSQ_INDIRECT2_BUSY(x) (((x) & 0x1) << 15) +#define G_0007C0_CSQ_INDIRECT2_BUSY(x) (((x) >> 15) & 0x1) +#define C_0007C0_CSQ_INDIRECT2_BUSY 0xFFFF7FFF +#define S_0007C0_GUIDMA_BUSY(x) (((x) & 0x1) << 28) +#define G_0007C0_GUIDMA_BUSY(x) (((x) >> 28) & 0x1) +#define C_0007C0_GUIDMA_BUSY 0xEFFFFFFF +#define S_0007C0_VIDDMA_BUSY(x) (((x) & 0x1) << 29) +#define G_0007C0_VIDDMA_BUSY(x) (((x) >> 29) & 0x1) +#define C_0007C0_VIDDMA_BUSY 0xDFFFFFFF +#define S_0007C0_CMDSTRM_BUSY(x) (((x) & 0x1) << 30) +#define G_0007C0_CMDSTRM_BUSY(x) (((x) >> 30) & 0x1) +#define C_0007C0_CMDSTRM_BUSY 0xBFFFFFFF +#define S_0007C0_CP_BUSY(x) (((x) & 0x1) << 31) +#define G_0007C0_CP_BUSY(x) (((x) >> 31) & 0x1) +#define C_0007C0_CP_BUSY 0x7FFFFFFF +#define R_000E40_RBBM_STATUS 0x000E40 +#define S_000E40_CMDFIFO_AVAIL(x) (((x) & 0x7F) << 0) +#define G_000E40_CMDFIFO_AVAIL(x) (((x) >> 0) & 0x7F) +#define C_000E40_CMDFIFO_AVAIL 0xFFFFFF80 +#define S_000E40_HIRQ_ON_RBB(x) (((x) & 0x1) << 8) +#define G_000E40_HIRQ_ON_RBB(x) (((x) >> 8) & 0x1) +#define C_000E40_HIRQ_ON_RBB 0xFFFFFEFF +#define S_000E40_CPRQ_ON_RBB(x) (((x) & 0x1) << 9) +#define G_000E40_CPRQ_ON_RBB(x) (((x) >> 9) & 0x1) +#define C_000E40_CPRQ_ON_RBB 0xFFFFFDFF +#define S_000E40_CFRQ_ON_RBB(x) (((x) & 0x1) << 10) +#define G_000E40_CFRQ_ON_RBB(x) (((x) >> 10) & 0x1) +#define C_000E40_CFRQ_ON_RBB 0xFFFFFBFF +#define S_000E40_HIRQ_IN_RTBUF(x) (((x) & 0x1) << 11) +#define G_000E40_HIRQ_IN_RTBUF(x) (((x) >> 11) & 0x1) +#define C_000E40_HIRQ_IN_RTBUF 0xFFFFF7FF +#define S_000E40_CPRQ_IN_RTBUF(x) (((x) & 0x1) << 12) +#define G_000E40_CPRQ_IN_RTBUF(x) (((x) >> 12) & 0x1) +#define C_000E40_CPRQ_IN_RTBUF 0xFFFFEFFF +#define S_000E40_CFRQ_IN_RTBUF(x) (((x) & 0x1) << 13) +#define G_000E40_CFRQ_IN_RTBUF(x) (((x) >> 13) & 0x1) +#define C_000E40_CFRQ_IN_RTBUF 0xFFFFDFFF +#define S_000E40_CF_PIPE_BUSY(x) (((x) & 0x1) << 14) +#define G_000E40_CF_PIPE_BUSY(x) (((x) >> 14) & 0x1) +#define C_000E40_CF_PIPE_BUSY 0xFFFFBFFF +#define S_000E40_ENG_EV_BUSY(x) (((x) & 0x1) << 15) +#define G_000E40_ENG_EV_BUSY(x) (((x) >> 15) & 0x1) +#define C_000E40_ENG_EV_BUSY 0xFFFF7FFF +#define S_000E40_CP_CMDSTRM_BUSY(x) (((x) & 0x1) << 16) +#define G_000E40_CP_CMDSTRM_BUSY(x) (((x) >> 16) & 0x1) +#define C_000E40_CP_CMDSTRM_BUSY 0xFFFEFFFF +#define S_000E40_E2_BUSY(x) (((x) & 0x1) << 17) +#define G_000E40_E2_BUSY(x) (((x) >> 17) & 0x1) +#define C_000E40_E2_BUSY 0xFFFDFFFF +#define S_000E40_RB2D_BUSY(x) (((x) & 0x1) << 18) +#define G_000E40_RB2D_BUSY(x) (((x) >> 18) & 0x1) +#define C_000E40_RB2D_BUSY 0xFFFBFFFF +#define S_000E40_RB3D_BUSY(x) (((x) & 0x1) << 19) +#define G_000E40_RB3D_BUSY(x) (((x) >> 19) & 0x1) +#define C_000E40_RB3D_BUSY 0xFFF7FFFF +#define S_000E40_VAP_BUSY(x) (((x) & 0x1) << 20) +#define G_000E40_VAP_BUSY(x) (((x) >> 20) & 0x1) +#define C_000E40_VAP_BUSY 0xFFEFFFFF +#define S_000E40_RE_BUSY(x) (((x) & 0x1) << 21) +#define G_000E40_RE_BUSY(x) (((x) >> 21) & 0x1) +#define C_000E40_RE_BUSY 0xFFDFFFFF +#define S_000E40_TAM_BUSY(x) (((x) & 0x1) << 22) +#define G_000E40_TAM_BUSY(x) (((x) >> 22) & 0x1) +#define C_000E40_TAM_BUSY 0xFFBFFFFF +#define S_000E40_TDM_BUSY(x) (((x) & 0x1) << 23) +#define G_000E40_TDM_BUSY(x) (((x) >> 23) & 0x1) +#define C_000E40_TDM_BUSY 0xFF7FFFFF +#define S_000E40_PB_BUSY(x) (((x) & 0x1) << 24) +#define G_000E40_PB_BUSY(x) (((x) >> 24) & 0x1) +#define C_000E40_PB_BUSY 0xFEFFFFFF +#define S_000E40_TIM_BUSY(x) (((x) & 0x1) << 25) +#define G_000E40_TIM_BUSY(x) (((x) >> 25) & 0x1) +#define C_000E40_TIM_BUSY 0xFDFFFFFF +#define S_000E40_GA_BUSY(x) (((x) & 0x1) << 26) +#define G_000E40_GA_BUSY(x) (((x) >> 26) & 0x1) +#define C_000E40_GA_BUSY 0xFBFFFFFF +#define S_000E40_CBA2D_BUSY(x) (((x) & 0x1) << 27) +#define G_000E40_CBA2D_BUSY(x) (((x) >> 27) & 0x1) +#define C_000E40_CBA2D_BUSY 0xF7FFFFFF +#define S_000E40_GUI_ACTIVE(x) (((x) & 0x1) << 31) +#define G_000E40_GUI_ACTIVE(x) (((x) >> 31) & 0x1) +#define C_000E40_GUI_ACTIVE 0x7FFFFFFF +#define R_006520_DC_LB_MEMORY_SPLIT 0x006520 +#define S_006520_DC_LB_MEMORY_SPLIT(x) (((x) & 0x3) << 0) +#define G_006520_DC_LB_MEMORY_SPLIT(x) (((x) >> 0) & 0x3) +#define C_006520_DC_LB_MEMORY_SPLIT 0xFFFFFFFC +#define S_006520_DC_LB_MEMORY_SPLIT_MODE(x) (((x) & 0x1) << 2) +#define G_006520_DC_LB_MEMORY_SPLIT_MODE(x) (((x) >> 2) & 0x1) +#define C_006520_DC_LB_MEMORY_SPLIT_MODE 0xFFFFFFFB +#define V_006520_DC_LB_MEMORY_SPLIT_D1HALF_D2HALF 0 +#define V_006520_DC_LB_MEMORY_SPLIT_D1_3Q_D2_1Q 1 +#define V_006520_DC_LB_MEMORY_SPLIT_D1_ONLY 2 +#define V_006520_DC_LB_MEMORY_SPLIT_D1_1Q_D2_3Q 3 +#define S_006520_DC_LB_DISP1_END_ADR(x) (((x) & 0x7FF) << 4) +#define G_006520_DC_LB_DISP1_END_ADR(x) (((x) >> 4) & 0x7FF) +#define C_006520_DC_LB_DISP1_END_ADR 0xFFFF800F +#define R_006548_D1MODE_PRIORITY_A_CNT 0x006548 +#define S_006548_D1MODE_PRIORITY_MARK_A(x) (((x) & 0x7FFF) << 0) +#define G_006548_D1MODE_PRIORITY_MARK_A(x) (((x) >> 0) & 0x7FFF) +#define C_006548_D1MODE_PRIORITY_MARK_A 0xFFFF8000 +#define S_006548_D1MODE_PRIORITY_A_OFF(x) (((x) & 0x1) << 16) +#define G_006548_D1MODE_PRIORITY_A_OFF(x) (((x) >> 16) & 0x1) +#define C_006548_D1MODE_PRIORITY_A_OFF 0xFFFEFFFF +#define S_006548_D1MODE_PRIORITY_A_FORCE_MASK(x) (((x) & 0x1) << 24) +#define G_006548_D1MODE_PRIORITY_A_FORCE_MASK(x) (((x) >> 24) & 0x1) +#define C_006548_D1MODE_PRIORITY_A_FORCE_MASK 0xFEFFFFFF +#define R_00654C_D1MODE_PRIORITY_B_CNT 0x00654C +#define S_00654C_D1MODE_PRIORITY_MARK_B(x) (((x) & 0x7FFF) << 0) +#define G_00654C_D1MODE_PRIORITY_MARK_B(x) (((x) >> 0) & 0x7FFF) +#define C_00654C_D1MODE_PRIORITY_MARK_B 0xFFFF8000 +#define S_00654C_D1MODE_PRIORITY_B_OFF(x) (((x) & 0x1) << 16) +#define G_00654C_D1MODE_PRIORITY_B_OFF(x) (((x) >> 16) & 0x1) +#define C_00654C_D1MODE_PRIORITY_B_OFF 0xFFFEFFFF +#define S_00654C_D1MODE_PRIORITY_B_ALWAYS_ON(x) (((x) & 0x1) << 20) +#define G_00654C_D1MODE_PRIORITY_B_ALWAYS_ON(x) (((x) >> 20) & 0x1) +#define C_00654C_D1MODE_PRIORITY_B_ALWAYS_ON 0xFFEFFFFF +#define S_00654C_D1MODE_PRIORITY_B_FORCE_MASK(x) (((x) & 0x1) << 24) +#define G_00654C_D1MODE_PRIORITY_B_FORCE_MASK(x) (((x) >> 24) & 0x1) +#define C_00654C_D1MODE_PRIORITY_B_FORCE_MASK 0xFEFFFFFF +#define R_006C9C_DCP_CONTROL 0x006C9C +#define R_006D48_D2MODE_PRIORITY_A_CNT 0x006D48 +#define S_006D48_D2MODE_PRIORITY_MARK_A(x) (((x) & 0x7FFF) << 0) +#define G_006D48_D2MODE_PRIORITY_MARK_A(x) (((x) >> 0) & 0x7FFF) +#define C_006D48_D2MODE_PRIORITY_MARK_A 0xFFFF8000 +#define S_006D48_D2MODE_PRIORITY_A_OFF(x) (((x) & 0x1) << 16) +#define G_006D48_D2MODE_PRIORITY_A_OFF(x) (((x) >> 16) & 0x1) +#define C_006D48_D2MODE_PRIORITY_A_OFF 0xFFFEFFFF +#define S_006D48_D2MODE_PRIORITY_A_ALWAYS_ON(x) (((x) & 0x1) << 20) +#define G_006D48_D2MODE_PRIORITY_A_ALWAYS_ON(x) (((x) >> 20) & 0x1) +#define C_006D48_D2MODE_PRIORITY_A_ALWAYS_ON 0xFFEFFFFF +#define S_006D48_D2MODE_PRIORITY_A_FORCE_MASK(x) (((x) & 0x1) << 24) +#define G_006D48_D2MODE_PRIORITY_A_FORCE_MASK(x) (((x) >> 24) & 0x1) +#define C_006D48_D2MODE_PRIORITY_A_FORCE_MASK 0xFEFFFFFF +#define R_006D4C_D2MODE_PRIORITY_B_CNT 0x006D4C +#define S_006D4C_D2MODE_PRIORITY_MARK_B(x) (((x) & 0x7FFF) << 0) +#define G_006D4C_D2MODE_PRIORITY_MARK_B(x) (((x) >> 0) & 0x7FFF) +#define C_006D4C_D2MODE_PRIORITY_MARK_B 0xFFFF8000 +#define S_006D4C_D2MODE_PRIORITY_B_OFF(x) (((x) & 0x1) << 16) +#define G_006D4C_D2MODE_PRIORITY_B_OFF(x) (((x) >> 16) & 0x1) +#define C_006D4C_D2MODE_PRIORITY_B_OFF 0xFFFEFFFF +#define S_006D4C_D2MODE_PRIORITY_B_ALWAYS_ON(x) (((x) & 0x1) << 20) +#define G_006D4C_D2MODE_PRIORITY_B_ALWAYS_ON(x) (((x) >> 20) & 0x1) +#define C_006D4C_D2MODE_PRIORITY_B_ALWAYS_ON 0xFFEFFFFF +#define S_006D4C_D2MODE_PRIORITY_B_FORCE_MASK(x) (((x) & 0x1) << 24) +#define G_006D4C_D2MODE_PRIORITY_B_FORCE_MASK(x) (((x) >> 24) & 0x1) +#define C_006D4C_D2MODE_PRIORITY_B_FORCE_MASK 0xFEFFFFFF +#define R_006D58_LB_MAX_REQ_OUTSTANDING 0x006D58 +#define S_006D58_LB_D1_MAX_REQ_OUTSTANDING(x) (((x) & 0xF) << 0) +#define G_006D58_LB_D1_MAX_REQ_OUTSTANDING(x) (((x) >> 0) & 0xF) +#define C_006D58_LB_D1_MAX_REQ_OUTSTANDING 0xFFFFFFF0 +#define S_006D58_LB_D2_MAX_REQ_OUTSTANDING(x) (((x) & 0xF) << 16) +#define G_006D58_LB_D2_MAX_REQ_OUTSTANDING(x) (((x) >> 16) & 0xF) +#define C_006D58_LB_D2_MAX_REQ_OUTSTANDING 0xFFF0FFFF + + +#define R_000090_MC_SYSTEM_STATUS 0x000090 +#define S_000090_MC_SYSTEM_IDLE(x) (((x) & 0x1) << 0) +#define G_000090_MC_SYSTEM_IDLE(x) (((x) >> 0) & 0x1) +#define C_000090_MC_SYSTEM_IDLE 0xFFFFFFFE +#define S_000090_MC_SEQUENCER_IDLE(x) (((x) & 0x1) << 1) +#define G_000090_MC_SEQUENCER_IDLE(x) (((x) >> 1) & 0x1) +#define C_000090_MC_SEQUENCER_IDLE 0xFFFFFFFD +#define S_000090_MC_ARBITER_IDLE(x) (((x) & 0x1) << 2) +#define G_000090_MC_ARBITER_IDLE(x) (((x) >> 2) & 0x1) +#define C_000090_MC_ARBITER_IDLE 0xFFFFFFFB +#define S_000090_MC_SELECT_PM(x) (((x) & 0x1) << 3) +#define G_000090_MC_SELECT_PM(x) (((x) >> 3) & 0x1) +#define C_000090_MC_SELECT_PM 0xFFFFFFF7 +#define S_000090_RESERVED4(x) (((x) & 0xF) << 4) +#define G_000090_RESERVED4(x) (((x) >> 4) & 0xF) +#define C_000090_RESERVED4 0xFFFFFF0F +#define S_000090_RESERVED8(x) (((x) & 0xF) << 8) +#define G_000090_RESERVED8(x) (((x) >> 8) & 0xF) +#define C_000090_RESERVED8 0xFFFFF0FF +#define S_000090_RESERVED12(x) (((x) & 0xF) << 12) +#define G_000090_RESERVED12(x) (((x) >> 12) & 0xF) +#define C_000090_RESERVED12 0xFFFF0FFF +#define S_000090_MCA_INIT_EXECUTED(x) (((x) & 0x1) << 16) +#define G_000090_MCA_INIT_EXECUTED(x) (((x) >> 16) & 0x1) +#define C_000090_MCA_INIT_EXECUTED 0xFFFEFFFF +#define S_000090_MCA_IDLE(x) (((x) & 0x1) << 17) +#define G_000090_MCA_IDLE(x) (((x) >> 17) & 0x1) +#define C_000090_MCA_IDLE 0xFFFDFFFF +#define S_000090_MCA_SEQ_IDLE(x) (((x) & 0x1) << 18) +#define G_000090_MCA_SEQ_IDLE(x) (((x) >> 18) & 0x1) +#define C_000090_MCA_SEQ_IDLE 0xFFFBFFFF +#define S_000090_MCA_ARB_IDLE(x) (((x) & 0x1) << 19) +#define G_000090_MCA_ARB_IDLE(x) (((x) >> 19) & 0x1) +#define C_000090_MCA_ARB_IDLE 0xFFF7FFFF +#define S_000090_RESERVED20(x) (((x) & 0xFFF) << 20) +#define G_000090_RESERVED20(x) (((x) >> 20) & 0xFFF) +#define C_000090_RESERVED20 0x000FFFFF +#define R_000100_MCCFG_FB_LOCATION 0x000100 +#define S_000100_MC_FB_START(x) (((x) & 0xFFFF) << 0) +#define G_000100_MC_FB_START(x) (((x) >> 0) & 0xFFFF) +#define C_000100_MC_FB_START 0xFFFF0000 +#define S_000100_MC_FB_TOP(x) (((x) & 0xFFFF) << 16) +#define G_000100_MC_FB_TOP(x) (((x) >> 16) & 0xFFFF) +#define C_000100_MC_FB_TOP 0x0000FFFF +#define R_000104_MC_INIT_MISC_LAT_TIMER 0x000104 +#define S_000104_MC_CPR_INIT_LAT(x) (((x) & 0xF) << 0) +#define G_000104_MC_CPR_INIT_LAT(x) (((x) >> 0) & 0xF) +#define C_000104_MC_CPR_INIT_LAT 0xFFFFFFF0 +#define S_000104_MC_VF_INIT_LAT(x) (((x) & 0xF) << 4) +#define G_000104_MC_VF_INIT_LAT(x) (((x) >> 4) & 0xF) +#define C_000104_MC_VF_INIT_LAT 0xFFFFFF0F +#define S_000104_MC_DISP0R_INIT_LAT(x) (((x) & 0xF) << 8) +#define G_000104_MC_DISP0R_INIT_LAT(x) (((x) >> 8) & 0xF) +#define C_000104_MC_DISP0R_INIT_LAT 0xFFFFF0FF +#define S_000104_MC_DISP1R_INIT_LAT(x) (((x) & 0xF) << 12) +#define G_000104_MC_DISP1R_INIT_LAT(x) (((x) >> 12) & 0xF) +#define C_000104_MC_DISP1R_INIT_LAT 0xFFFF0FFF +#define S_000104_MC_FIXED_INIT_LAT(x) (((x) & 0xF) << 16) +#define G_000104_MC_FIXED_INIT_LAT(x) (((x) >> 16) & 0xF) +#define C_000104_MC_FIXED_INIT_LAT 0xFFF0FFFF +#define S_000104_MC_E2R_INIT_LAT(x) (((x) & 0xF) << 20) +#define G_000104_MC_E2R_INIT_LAT(x) (((x) >> 20) & 0xF) +#define C_000104_MC_E2R_INIT_LAT 0xFF0FFFFF +#define S_000104_SAME_PAGE_PRIO(x) (((x) & 0xF) << 24) +#define G_000104_SAME_PAGE_PRIO(x) (((x) >> 24) & 0xF) +#define C_000104_SAME_PAGE_PRIO 0xF0FFFFFF +#define S_000104_MC_GLOBW_INIT_LAT(x) (((x) & 0xF) << 28) +#define G_000104_MC_GLOBW_INIT_LAT(x) (((x) >> 28) & 0xF) +#define C_000104_MC_GLOBW_INIT_LAT 0x0FFFFFFF + +#endif diff --git a/drivers/gpu/drm/radeon/rs690r.h b/drivers/gpu/drm/radeon/rs690r.h deleted file mode 100644 index c0d9faa2175b..000000000000 --- a/drivers/gpu/drm/radeon/rs690r.h +++ /dev/null @@ -1,99 +0,0 @@ -/* - * Copyright 2008 Advanced Micro Devices, Inc. - * Copyright 2008 Red Hat Inc. - * Copyright 2009 Jerome Glisse. - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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. - * - * Authors: Dave Airlie - * Alex Deucher - * Jerome Glisse - */ -#ifndef RS690R_H -#define RS690R_H - -/* RS690/RS740 registers */ -#define MC_INDEX 0x0078 -# define MC_INDEX_MASK 0x1FF -# define MC_INDEX_WR_EN (1 << 9) -# define MC_INDEX_WR_ACK 0x7F -#define MC_DATA 0x007C -#define HDP_FB_LOCATION 0x0134 -#define DC_LB_MEMORY_SPLIT 0x6520 -#define DC_LB_MEMORY_SPLIT_MASK 0x00000003 -#define DC_LB_MEMORY_SPLIT_SHIFT 0 -#define DC_LB_MEMORY_SPLIT_D1HALF_D2HALF 0 -#define DC_LB_MEMORY_SPLIT_D1_3Q_D2_1Q 1 -#define DC_LB_MEMORY_SPLIT_D1_ONLY 2 -#define DC_LB_MEMORY_SPLIT_D1_1Q_D2_3Q 3 -#define DC_LB_MEMORY_SPLIT_SHIFT_MODE (1 << 2) -#define DC_LB_DISP1_END_ADR_SHIFT 4 -#define DC_LB_DISP1_END_ADR_MASK 0x00007FF0 -#define D1MODE_PRIORITY_A_CNT 0x6548 -#define MODE_PRIORITY_MARK_MASK 0x00007FFF -#define MODE_PRIORITY_OFF (1 << 16) -#define MODE_PRIORITY_ALWAYS_ON (1 << 20) -#define MODE_PRIORITY_FORCE_MASK (1 << 24) -#define D1MODE_PRIORITY_B_CNT 0x654C -#define LB_MAX_REQ_OUTSTANDING 0x6D58 -#define LB_D1_MAX_REQ_OUTSTANDING_MASK 0x0000000F -#define LB_D1_MAX_REQ_OUTSTANDING_SHIFT 0 -#define LB_D2_MAX_REQ_OUTSTANDING_MASK 0x000F0000 -#define LB_D2_MAX_REQ_OUTSTANDING_SHIFT 16 -#define DCP_CONTROL 0x6C9C -#define D2MODE_PRIORITY_A_CNT 0x6D48 -#define D2MODE_PRIORITY_B_CNT 0x6D4C - -/* MC indirect registers */ -#define MC_STATUS_IDLE (1 << 0) -#define MC_MISC_CNTL 0x18 -#define DISABLE_GTW (1 << 1) -#define GART_INDEX_REG_EN (1 << 12) -#define BLOCK_GFX_D3_EN (1 << 14) -#define GART_FEATURE_ID 0x2B -#define HANG_EN (1 << 11) -#define TLB_ENABLE (1 << 18) -#define P2P_ENABLE (1 << 19) -#define GTW_LAC_EN (1 << 25) -#define LEVEL2_GART (0 << 30) -#define LEVEL1_GART (1 << 30) -#define PDC_EN (1 << 31) -#define GART_BASE 0x2C -#define GART_CACHE_CNTRL 0x2E -# define GART_CACHE_INVALIDATE (1 << 0) -#define MC_STATUS 0x90 -#define MCCFG_FB_LOCATION 0x100 -#define MC_FB_START_MASK 0x0000FFFF -#define MC_FB_START_SHIFT 0 -#define MC_FB_TOP_MASK 0xFFFF0000 -#define MC_FB_TOP_SHIFT 16 -#define MCCFG_AGP_LOCATION 0x101 -#define MC_AGP_START_MASK 0x0000FFFF -#define MC_AGP_START_SHIFT 0 -#define MC_AGP_TOP_MASK 0xFFFF0000 -#define MC_AGP_TOP_SHIFT 16 -#define MCCFG_AGP_BASE 0x102 -#define MCCFG_AGP_BASE_2 0x103 -#define MC_INIT_MISC_LAT_TIMER 0x104 -#define MC_DISP0R_INIT_LAT_SHIFT 8 -#define MC_DISP0R_INIT_LAT_MASK 0x00000F00 -#define MC_DISP1R_INIT_LAT_SHIFT 12 -#define MC_DISP1R_INIT_LAT_MASK 0x0000F000 - -#endif diff --git a/drivers/gpu/drm/radeon/rv200d.h b/drivers/gpu/drm/radeon/rv200d.h new file mode 100644 index 000000000000..c5b398330c26 --- /dev/null +++ b/drivers/gpu/drm/radeon/rv200d.h @@ -0,0 +1,36 @@ +/* + * Copyright 2008 Advanced Micro Devices, Inc. + * Copyright 2008 Red Hat Inc. + * Copyright 2009 Jerome Glisse. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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. + * + * Authors: Dave Airlie + * Alex Deucher + * Jerome Glisse + */ +#ifndef __RV200D_H__ +#define __RV200D_H__ + +#define R_00015C_AGP_BASE_2 0x00015C +#define S_00015C_AGP_BASE_ADDR_2(x) (((x) & 0xF) << 0) +#define G_00015C_AGP_BASE_ADDR_2(x) (((x) >> 0) & 0xF) +#define C_00015C_AGP_BASE_ADDR_2 0xFFFFFFF0 + +#endif diff --git a/drivers/gpu/drm/radeon/rv250d.h b/drivers/gpu/drm/radeon/rv250d.h new file mode 100644 index 000000000000..e5a70b06fe1f --- /dev/null +++ b/drivers/gpu/drm/radeon/rv250d.h @@ -0,0 +1,123 @@ +/* + * Copyright 2008 Advanced Micro Devices, Inc. + * Copyright 2008 Red Hat Inc. + * Copyright 2009 Jerome Glisse. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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. + * + * Authors: Dave Airlie + * Alex Deucher + * Jerome Glisse + */ +#ifndef __RV250D_H__ +#define __RV250D_H__ + +#define R_00000D_SCLK_CNTL_M6 0x00000D +#define S_00000D_SCLK_SRC_SEL(x) (((x) & 0x7) << 0) +#define G_00000D_SCLK_SRC_SEL(x) (((x) >> 0) & 0x7) +#define C_00000D_SCLK_SRC_SEL 0xFFFFFFF8 +#define S_00000D_CP_MAX_DYN_STOP_LAT(x) (((x) & 0x1) << 3) +#define G_00000D_CP_MAX_DYN_STOP_LAT(x) (((x) >> 3) & 0x1) +#define C_00000D_CP_MAX_DYN_STOP_LAT 0xFFFFFFF7 +#define S_00000D_HDP_MAX_DYN_STOP_LAT(x) (((x) & 0x1) << 4) +#define G_00000D_HDP_MAX_DYN_STOP_LAT(x) (((x) >> 4) & 0x1) +#define C_00000D_HDP_MAX_DYN_STOP_LAT 0xFFFFFFEF +#define S_00000D_TV_MAX_DYN_STOP_LAT(x) (((x) & 0x1) << 5) +#define G_00000D_TV_MAX_DYN_STOP_LAT(x) (((x) >> 5) & 0x1) +#define C_00000D_TV_MAX_DYN_STOP_LAT 0xFFFFFFDF +#define S_00000D_E2_MAX_DYN_STOP_LAT(x) (((x) & 0x1) << 6) +#define G_00000D_E2_MAX_DYN_STOP_LAT(x) (((x) >> 6) & 0x1) +#define C_00000D_E2_MAX_DYN_STOP_LAT 0xFFFFFFBF +#define S_00000D_SE_MAX_DYN_STOP_LAT(x) (((x) & 0x1) << 7) +#define G_00000D_SE_MAX_DYN_STOP_LAT(x) (((x) >> 7) & 0x1) +#define C_00000D_SE_MAX_DYN_STOP_LAT 0xFFFFFF7F +#define S_00000D_IDCT_MAX_DYN_STOP_LAT(x) (((x) & 0x1) << 8) +#define G_00000D_IDCT_MAX_DYN_STOP_LAT(x) (((x) >> 8) & 0x1) +#define C_00000D_IDCT_MAX_DYN_STOP_LAT 0xFFFFFEFF +#define S_00000D_VIP_MAX_DYN_STOP_LAT(x) (((x) & 0x1) << 9) +#define G_00000D_VIP_MAX_DYN_STOP_LAT(x) (((x) >> 9) & 0x1) +#define C_00000D_VIP_MAX_DYN_STOP_LAT 0xFFFFFDFF +#define S_00000D_RE_MAX_DYN_STOP_LAT(x) (((x) & 0x1) << 10) +#define G_00000D_RE_MAX_DYN_STOP_LAT(x) (((x) >> 10) & 0x1) +#define C_00000D_RE_MAX_DYN_STOP_LAT 0xFFFFFBFF +#define S_00000D_PB_MAX_DYN_STOP_LAT(x) (((x) & 0x1) << 11) +#define G_00000D_PB_MAX_DYN_STOP_LAT(x) (((x) >> 11) & 0x1) +#define C_00000D_PB_MAX_DYN_STOP_LAT 0xFFFFF7FF +#define S_00000D_TAM_MAX_DYN_STOP_LAT(x) (((x) & 0x1) << 12) +#define G_00000D_TAM_MAX_DYN_STOP_LAT(x) (((x) >> 12) & 0x1) +#define C_00000D_TAM_MAX_DYN_STOP_LAT 0xFFFFEFFF +#define S_00000D_TDM_MAX_DYN_STOP_LAT(x) (((x) & 0x1) << 13) +#define G_00000D_TDM_MAX_DYN_STOP_LAT(x) (((x) >> 13) & 0x1) +#define C_00000D_TDM_MAX_DYN_STOP_LAT 0xFFFFDFFF +#define S_00000D_RB_MAX_DYN_STOP_LAT(x) (((x) & 0x1) << 14) +#define G_00000D_RB_MAX_DYN_STOP_LAT(x) (((x) >> 14) & 0x1) +#define C_00000D_RB_MAX_DYN_STOP_LAT 0xFFFFBFFF +#define S_00000D_FORCE_DISP2(x) (((x) & 0x1) << 15) +#define G_00000D_FORCE_DISP2(x) (((x) >> 15) & 0x1) +#define C_00000D_FORCE_DISP2 0xFFFF7FFF +#define S_00000D_FORCE_CP(x) (((x) & 0x1) << 16) +#define G_00000D_FORCE_CP(x) (((x) >> 16) & 0x1) +#define C_00000D_FORCE_CP 0xFFFEFFFF +#define S_00000D_FORCE_HDP(x) (((x) & 0x1) << 17) +#define G_00000D_FORCE_HDP(x) (((x) >> 17) & 0x1) +#define C_00000D_FORCE_HDP 0xFFFDFFFF +#define S_00000D_FORCE_DISP1(x) (((x) & 0x1) << 18) +#define G_00000D_FORCE_DISP1(x) (((x) >> 18) & 0x1) +#define C_00000D_FORCE_DISP1 0xFFFBFFFF +#define S_00000D_FORCE_TOP(x) (((x) & 0x1) << 19) +#define G_00000D_FORCE_TOP(x) (((x) >> 19) & 0x1) +#define C_00000D_FORCE_TOP 0xFFF7FFFF +#define S_00000D_FORCE_E2(x) (((x) & 0x1) << 20) +#define G_00000D_FORCE_E2(x) (((x) >> 20) & 0x1) +#define C_00000D_FORCE_E2 0xFFEFFFFF +#define S_00000D_FORCE_SE(x) (((x) & 0x1) << 21) +#define G_00000D_FORCE_SE(x) (((x) >> 21) & 0x1) +#define C_00000D_FORCE_SE 0xFFDFFFFF +#define S_00000D_FORCE_IDCT(x) (((x) & 0x1) << 22) +#define G_00000D_FORCE_IDCT(x) (((x) >> 22) & 0x1) +#define C_00000D_FORCE_IDCT 0xFFBFFFFF +#define S_00000D_FORCE_VIP(x) (((x) & 0x1) << 23) +#define G_00000D_FORCE_VIP(x) (((x) >> 23) & 0x1) +#define C_00000D_FORCE_VIP 0xFF7FFFFF +#define S_00000D_FORCE_RE(x) (((x) & 0x1) << 24) +#define G_00000D_FORCE_RE(x) (((x) >> 24) & 0x1) +#define C_00000D_FORCE_RE 0xFEFFFFFF +#define S_00000D_FORCE_PB(x) (((x) & 0x1) << 25) +#define G_00000D_FORCE_PB(x) (((x) >> 25) & 0x1) +#define C_00000D_FORCE_PB 0xFDFFFFFF +#define S_00000D_FORCE_TAM(x) (((x) & 0x1) << 26) +#define G_00000D_FORCE_TAM(x) (((x) >> 26) & 0x1) +#define C_00000D_FORCE_TAM 0xFBFFFFFF +#define S_00000D_FORCE_TDM(x) (((x) & 0x1) << 27) +#define G_00000D_FORCE_TDM(x) (((x) >> 27) & 0x1) +#define C_00000D_FORCE_TDM 0xF7FFFFFF +#define S_00000D_FORCE_RB(x) (((x) & 0x1) << 28) +#define G_00000D_FORCE_RB(x) (((x) >> 28) & 0x1) +#define C_00000D_FORCE_RB 0xEFFFFFFF +#define S_00000D_FORCE_TV_SCLK(x) (((x) & 0x1) << 29) +#define G_00000D_FORCE_TV_SCLK(x) (((x) >> 29) & 0x1) +#define C_00000D_FORCE_TV_SCLK 0xDFFFFFFF +#define S_00000D_FORCE_SUBPIC(x) (((x) & 0x1) << 30) +#define G_00000D_FORCE_SUBPIC(x) (((x) >> 30) & 0x1) +#define C_00000D_FORCE_SUBPIC 0xBFFFFFFF +#define S_00000D_FORCE_OV0(x) (((x) & 0x1) << 31) +#define G_00000D_FORCE_OV0(x) (((x) >> 31) & 0x1) +#define C_00000D_FORCE_OV0 0x7FFFFFFF + +#endif diff --git a/drivers/gpu/drm/radeon/rv350d.h b/drivers/gpu/drm/radeon/rv350d.h new file mode 100644 index 000000000000..c75c5ed9e654 --- /dev/null +++ b/drivers/gpu/drm/radeon/rv350d.h @@ -0,0 +1,52 @@ +/* + * Copyright 2008 Advanced Micro Devices, Inc. + * Copyright 2008 Red Hat Inc. + * Copyright 2009 Jerome Glisse. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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. + * + * Authors: Dave Airlie + * Alex Deucher + * Jerome Glisse + */ +#ifndef __RV350D_H__ +#define __RV350D_H__ + +/* RV350, RV380 registers */ +/* #define R_00000D_SCLK_CNTL 0x00000D */ +#define S_00000D_FORCE_VAP(x) (((x) & 0x1) << 21) +#define G_00000D_FORCE_VAP(x) (((x) >> 21) & 0x1) +#define C_00000D_FORCE_VAP 0xFFDFFFFF +#define S_00000D_FORCE_SR(x) (((x) & 0x1) << 25) +#define G_00000D_FORCE_SR(x) (((x) >> 25) & 0x1) +#define C_00000D_FORCE_SR 0xFDFFFFFF +#define S_00000D_FORCE_PX(x) (((x) & 0x1) << 26) +#define G_00000D_FORCE_PX(x) (((x) >> 26) & 0x1) +#define C_00000D_FORCE_PX 0xFBFFFFFF +#define S_00000D_FORCE_TX(x) (((x) & 0x1) << 27) +#define G_00000D_FORCE_TX(x) (((x) >> 27) & 0x1) +#define C_00000D_FORCE_TX 0xF7FFFFFF +#define S_00000D_FORCE_US(x) (((x) & 0x1) << 28) +#define G_00000D_FORCE_US(x) (((x) >> 28) & 0x1) +#define C_00000D_FORCE_US 0xEFFFFFFF +#define S_00000D_FORCE_SU(x) (((x) & 0x1) << 30) +#define G_00000D_FORCE_SU(x) (((x) >> 30) & 0x1) +#define C_00000D_FORCE_SU 0xBFFFFFFF + +#endif diff --git a/drivers/gpu/drm/radeon/rv515.c b/drivers/gpu/drm/radeon/rv515.c index fd799748e7d8..ba68c9fe90a1 100644 --- a/drivers/gpu/drm/radeon/rv515.c +++ b/drivers/gpu/drm/radeon/rv515.c @@ -29,37 +29,17 @@ #include "drmP.h" #include "rv515d.h" #include "radeon.h" - +#include "atom.h" #include "rv515_reg_safe.h" -/* rv515 depends on : */ -void r100_hdp_reset(struct radeon_device *rdev); -int r100_cp_reset(struct radeon_device *rdev); -int r100_rb2d_reset(struct radeon_device *rdev); -int r100_gui_wait_for_idle(struct radeon_device *rdev); -int r100_cp_init(struct radeon_device *rdev, unsigned ring_size); -void r420_pipes_init(struct radeon_device *rdev); -void rs600_mc_disable_clients(struct radeon_device *rdev); -void rs600_disable_vga(struct radeon_device *rdev); - -/* This files gather functions specifics to: - * rv515 - * - * Some of these functions might be used by newer ASICs. - */ + +/* This files gather functions specifics to: rv515 */ int rv515_debugfs_pipes_info_init(struct radeon_device *rdev); int rv515_debugfs_ga_info_init(struct radeon_device *rdev); void rv515_gpu_init(struct radeon_device *rdev); int rv515_mc_wait_for_idle(struct radeon_device *rdev); - -/* - * MC - */ -int rv515_mc_init(struct radeon_device *rdev) +void rv515_debugfs(struct radeon_device *rdev) { - uint32_t tmp; - int r; - if (r100_debugfs_rbbm_init(rdev)) { DRM_ERROR("Failed to register debugfs file for RBBM !\n"); } @@ -69,67 +49,8 @@ int rv515_mc_init(struct radeon_device *rdev) if (rv515_debugfs_ga_info_init(rdev)) { DRM_ERROR("Failed to register debugfs file for pipes !\n"); } - - rv515_gpu_init(rdev); - rv370_pcie_gart_disable(rdev); - - /* Setup GPU memory space */ - rdev->mc.vram_location = 0xFFFFFFFFUL; - rdev->mc.gtt_location = 0xFFFFFFFFUL; - if (rdev->flags & RADEON_IS_AGP) { - r = radeon_agp_init(rdev); - if (r) { - printk(KERN_WARNING "[drm] Disabling AGP\n"); - rdev->flags &= ~RADEON_IS_AGP; - rdev->mc.gtt_size = radeon_gart_size * 1024 * 1024; - } else { - rdev->mc.gtt_location = rdev->mc.agp_base; - } - } - r = radeon_mc_setup(rdev); - if (r) { - return r; - } - - /* Program GPU memory space */ - rs600_mc_disable_clients(rdev); - if (rv515_mc_wait_for_idle(rdev)) { - printk(KERN_WARNING "Failed to wait MC idle while " - "programming pipes. Bad things might happen.\n"); - } - /* Write VRAM size in case we are limiting it */ - WREG32(RADEON_CONFIG_MEMSIZE, rdev->mc.real_vram_size); - tmp = REG_SET(MC_FB_START, rdev->mc.vram_location >> 16); - WREG32(0x134, tmp); - tmp = rdev->mc.vram_location + rdev->mc.mc_vram_size - 1; - tmp = REG_SET(MC_FB_TOP, tmp >> 16); - tmp |= REG_SET(MC_FB_START, rdev->mc.vram_location >> 16); - WREG32_MC(MC_FB_LOCATION, tmp); - WREG32(HDP_FB_LOCATION, rdev->mc.vram_location >> 16); - WREG32(0x310, rdev->mc.vram_location); - if (rdev->flags & RADEON_IS_AGP) { - tmp = rdev->mc.gtt_location + rdev->mc.gtt_size - 1; - tmp = REG_SET(MC_AGP_TOP, tmp >> 16); - tmp |= REG_SET(MC_AGP_START, rdev->mc.gtt_location >> 16); - WREG32_MC(MC_AGP_LOCATION, tmp); - WREG32_MC(MC_AGP_BASE, rdev->mc.agp_base); - WREG32_MC(MC_AGP_BASE_2, 0); - } else { - WREG32_MC(MC_AGP_LOCATION, 0x0FFFFFFF); - WREG32_MC(MC_AGP_BASE, 0); - WREG32_MC(MC_AGP_BASE_2, 0); - } - return 0; -} - -void rv515_mc_fini(struct radeon_device *rdev) -{ } - -/* - * Global GPU functions - */ void rv515_ring_start(struct radeon_device *rdev) { int r; @@ -198,11 +119,6 @@ void rv515_ring_start(struct radeon_device *rdev) radeon_ring_unlock_commit(rdev); } -void rv515_errata(struct radeon_device *rdev) -{ - rdev->pll_errata = 0; -} - int rv515_mc_wait_for_idle(struct radeon_device *rdev) { unsigned i; @@ -219,6 +135,12 @@ int rv515_mc_wait_for_idle(struct radeon_device *rdev) return -1; } +void rv515_vga_render_disable(struct radeon_device *rdev) +{ + WREG32(R_000300_VGA_RENDER_CONTROL, + RREG32(R_000300_VGA_RENDER_CONTROL) & C_000300_VGA_VSTATUS_CNTL); +} + void rv515_gpu_init(struct radeon_device *rdev) { unsigned pipe_select_current, gb_pipe_select, tmp; @@ -231,7 +153,7 @@ void rv515_gpu_init(struct radeon_device *rdev) "reseting GPU. Bad things might happen.\n"); } - rs600_disable_vga(rdev); + rv515_vga_render_disable(rdev); r420_pipes_init(rdev); gb_pipe_select = RREG32(0x402C); @@ -335,10 +257,6 @@ int rv515_gpu_reset(struct radeon_device *rdev) return 0; } - -/* - * VRAM info - */ static void rv515_vram_get_type(struct radeon_device *rdev) { uint32_t tmp; @@ -374,10 +292,6 @@ void rv515_vram_info(struct radeon_device *rdev) rdev->pm.sclk.full = rfixed_div(rdev->pm.sclk, a); } - -/* - * Indirect registers accessor - */ uint32_t rv515_mc_rreg(struct radeon_device *rdev, uint32_t reg) { uint32_t r; @@ -395,9 +309,6 @@ void rv515_mc_wreg(struct radeon_device *rdev, uint32_t reg, uint32_t v) WREG32(MC_IND_INDEX, 0); } -/* - * Debugfs info - */ #if defined(CONFIG_DEBUG_FS) static int rv515_debugfs_pipes_info(struct seq_file *m, void *data) { @@ -459,13 +370,260 @@ int rv515_debugfs_ga_info_init(struct radeon_device *rdev) #endif } -/* - * Asic initialization - */ -int rv515_init(struct radeon_device *rdev) +void rv515_mc_stop(struct radeon_device *rdev, struct rv515_mc_save *save) +{ + save->d1vga_control = RREG32(R_000330_D1VGA_CONTROL); + save->d2vga_control = RREG32(R_000338_D2VGA_CONTROL); + save->vga_render_control = RREG32(R_000300_VGA_RENDER_CONTROL); + save->vga_hdp_control = RREG32(R_000328_VGA_HDP_CONTROL); + save->d1crtc_control = RREG32(R_006080_D1CRTC_CONTROL); + save->d2crtc_control = RREG32(R_006880_D2CRTC_CONTROL); + + /* Stop all video */ + WREG32(R_0068E8_D2CRTC_UPDATE_LOCK, 0); + WREG32(R_000300_VGA_RENDER_CONTROL, 0); + WREG32(R_0060E8_D1CRTC_UPDATE_LOCK, 1); + WREG32(R_0068E8_D2CRTC_UPDATE_LOCK, 1); + WREG32(R_006080_D1CRTC_CONTROL, 0); + WREG32(R_006880_D2CRTC_CONTROL, 0); + WREG32(R_0060E8_D1CRTC_UPDATE_LOCK, 0); + WREG32(R_0068E8_D2CRTC_UPDATE_LOCK, 0); + WREG32(R_000330_D1VGA_CONTROL, 0); + WREG32(R_000338_D2VGA_CONTROL, 0); +} + +void rv515_mc_resume(struct radeon_device *rdev, struct rv515_mc_save *save) +{ + WREG32(R_006110_D1GRPH_PRIMARY_SURFACE_ADDRESS, rdev->mc.vram_start); + WREG32(R_006118_D1GRPH_SECONDARY_SURFACE_ADDRESS, rdev->mc.vram_start); + WREG32(R_006910_D2GRPH_PRIMARY_SURFACE_ADDRESS, rdev->mc.vram_start); + WREG32(R_006918_D2GRPH_SECONDARY_SURFACE_ADDRESS, rdev->mc.vram_start); + WREG32(R_000310_VGA_MEMORY_BASE_ADDRESS, rdev->mc.vram_start); + /* Unlock host access */ + WREG32(R_000328_VGA_HDP_CONTROL, save->vga_hdp_control); + mdelay(1); + /* Restore video state */ + WREG32(R_000330_D1VGA_CONTROL, save->d1vga_control); + WREG32(R_000338_D2VGA_CONTROL, save->d2vga_control); + WREG32(R_0060E8_D1CRTC_UPDATE_LOCK, 1); + WREG32(R_0068E8_D2CRTC_UPDATE_LOCK, 1); + WREG32(R_006080_D1CRTC_CONTROL, save->d1crtc_control); + WREG32(R_006880_D2CRTC_CONTROL, save->d2crtc_control); + WREG32(R_0060E8_D1CRTC_UPDATE_LOCK, 0); + WREG32(R_0068E8_D2CRTC_UPDATE_LOCK, 0); + WREG32(R_000300_VGA_RENDER_CONTROL, save->vga_render_control); +} + +void rv515_mc_program(struct radeon_device *rdev) +{ + struct rv515_mc_save save; + + /* Stops all mc clients */ + rv515_mc_stop(rdev, &save); + + /* Wait for mc idle */ + if (rv515_mc_wait_for_idle(rdev)) + dev_warn(rdev->dev, "Wait MC idle timeout before updating MC.\n"); + /* Write VRAM size in case we are limiting it */ + WREG32(R_0000F8_CONFIG_MEMSIZE, rdev->mc.real_vram_size); + /* Program MC, should be a 32bits limited address space */ + WREG32_MC(R_000001_MC_FB_LOCATION, + S_000001_MC_FB_START(rdev->mc.vram_start >> 16) | + S_000001_MC_FB_TOP(rdev->mc.vram_end >> 16)); + WREG32(R_000134_HDP_FB_LOCATION, + S_000134_HDP_FB_START(rdev->mc.vram_start >> 16)); + if (rdev->flags & RADEON_IS_AGP) { + WREG32_MC(R_000002_MC_AGP_LOCATION, + S_000002_MC_AGP_START(rdev->mc.gtt_start >> 16) | + S_000002_MC_AGP_TOP(rdev->mc.gtt_end >> 16)); + WREG32_MC(R_000003_MC_AGP_BASE, lower_32_bits(rdev->mc.agp_base)); + WREG32_MC(R_000004_MC_AGP_BASE_2, + S_000004_AGP_BASE_ADDR_2(upper_32_bits(rdev->mc.agp_base))); + } else { + WREG32_MC(R_000002_MC_AGP_LOCATION, 0xFFFFFFFF); + WREG32_MC(R_000003_MC_AGP_BASE, 0); + WREG32_MC(R_000004_MC_AGP_BASE_2, 0); + } + + rv515_mc_resume(rdev, &save); +} + +void rv515_clock_startup(struct radeon_device *rdev) +{ + if (radeon_dynclks != -1 && radeon_dynclks) + radeon_atom_set_clock_gating(rdev, 1); + /* We need to force on some of the block */ + WREG32_PLL(R_00000F_CP_DYN_CNTL, + RREG32_PLL(R_00000F_CP_DYN_CNTL) | S_00000F_CP_FORCEON(1)); + WREG32_PLL(R_000011_E2_DYN_CNTL, + RREG32_PLL(R_000011_E2_DYN_CNTL) | S_000011_E2_FORCEON(1)); + WREG32_PLL(R_000013_IDCT_DYN_CNTL, + RREG32_PLL(R_000013_IDCT_DYN_CNTL) | S_000013_IDCT_FORCEON(1)); +} + +static int rv515_startup(struct radeon_device *rdev) +{ + int r; + + rv515_mc_program(rdev); + /* Resume clock */ + rv515_clock_startup(rdev); + /* Initialize GPU configuration (# pipes, ...) */ + rv515_gpu_init(rdev); + /* Initialize GART (initialize after TTM so we can allocate + * memory through TTM but finalize after TTM) */ + if (rdev->flags & RADEON_IS_PCIE) { + r = rv370_pcie_gart_enable(rdev); + if (r) + return r; + } + /* Enable IRQ */ + rdev->irq.sw_int = true; + rs600_irq_set(rdev); + /* 1M ring buffer */ + r = r100_cp_init(rdev, 1024 * 1024); + if (r) { + dev_err(rdev->dev, "failled initializing CP (%d).\n", r); + return r; + } + r = r100_wb_init(rdev); + if (r) + dev_err(rdev->dev, "failled initializing WB (%d).\n", r); + r = r100_ib_init(rdev); + if (r) { + dev_err(rdev->dev, "failled initializing IB (%d).\n", r); + return r; + } + return 0; +} + +int rv515_resume(struct radeon_device *rdev) +{ + /* Make sur GART are not working */ + if (rdev->flags & RADEON_IS_PCIE) + rv370_pcie_gart_disable(rdev); + /* Resume clock before doing reset */ + rv515_clock_startup(rdev); + /* Reset gpu before posting otherwise ATOM will enter infinite loop */ + if (radeon_gpu_reset(rdev)) { + dev_warn(rdev->dev, "GPU reset failed ! (0xE40=0x%08X, 0x7C0=0x%08X)\n", + RREG32(R_000E40_RBBM_STATUS), + RREG32(R_0007C0_CP_STAT)); + } + /* post */ + atom_asic_init(rdev->mode_info.atom_context); + /* Resume clock after posting */ + rv515_clock_startup(rdev); + return rv515_startup(rdev); +} + +int rv515_suspend(struct radeon_device *rdev) +{ + r100_cp_disable(rdev); + r100_wb_disable(rdev); + rs600_irq_disable(rdev); + if (rdev->flags & RADEON_IS_PCIE) + rv370_pcie_gart_disable(rdev); + return 0; +} + +void rv515_set_safe_registers(struct radeon_device *rdev) { rdev->config.r300.reg_safe_bm = rv515_reg_safe_bm; rdev->config.r300.reg_safe_bm_size = ARRAY_SIZE(rv515_reg_safe_bm); +} + +void rv515_fini(struct radeon_device *rdev) +{ + rv515_suspend(rdev); + r100_cp_fini(rdev); + r100_wb_fini(rdev); + r100_ib_fini(rdev); + radeon_gem_fini(rdev); + rv370_pcie_gart_fini(rdev); + radeon_agp_fini(rdev); + radeon_irq_kms_fini(rdev); + radeon_fence_driver_fini(rdev); + radeon_object_fini(rdev); + radeon_atombios_fini(rdev); + kfree(rdev->bios); + rdev->bios = NULL; +} + +int rv515_init(struct radeon_device *rdev) +{ + int r; + + /* Initialize scratch registers */ + radeon_scratch_init(rdev); + /* Initialize surface registers */ + radeon_surface_init(rdev); + /* TODO: disable VGA need to use VGA request */ + /* BIOS*/ + if (!radeon_get_bios(rdev)) { + if (ASIC_IS_AVIVO(rdev)) + return -EINVAL; + } + if (rdev->is_atom_bios) { + r = radeon_atombios_init(rdev); + if (r) + return r; + } else { + dev_err(rdev->dev, "Expecting atombios for RV515 GPU\n"); + return -EINVAL; + } + /* Reset gpu before posting otherwise ATOM will enter infinite loop */ + if (radeon_gpu_reset(rdev)) { + dev_warn(rdev->dev, + "GPU reset failed ! (0xE40=0x%08X, 0x7C0=0x%08X)\n", + RREG32(R_000E40_RBBM_STATUS), + RREG32(R_0007C0_CP_STAT)); + } + /* check if cards are posted or not */ + if (!radeon_card_posted(rdev) && rdev->bios) { + DRM_INFO("GPU not posted. posting now...\n"); + atom_asic_init(rdev->mode_info.atom_context); + } + /* Initialize clocks */ + radeon_get_clock_info(rdev->ddev); + /* Initialize power management */ + radeon_pm_init(rdev); + /* Get vram informations */ + rv515_vram_info(rdev); + /* Initialize memory controller (also test AGP) */ + r = r420_mc_init(rdev); + if (r) + return r; + rv515_debugfs(rdev); + /* Fence driver */ + r = radeon_fence_driver_init(rdev); + if (r) + return r; + r = radeon_irq_kms_init(rdev); + if (r) + return r; + /* Memory manager */ + r = radeon_object_init(rdev); + if (r) + return r; + r = rv370_pcie_gart_init(rdev); + if (r) + return r; + rv515_set_safe_registers(rdev); + rdev->accel_working = true; + r = rv515_startup(rdev); + if (r) { + /* Somethings want wront with the accel init stop accel */ + dev_err(rdev->dev, "Disabling GPU acceleration\n"); + rv515_suspend(rdev); + r100_cp_fini(rdev); + r100_wb_fini(rdev); + r100_ib_fini(rdev); + rv370_pcie_gart_fini(rdev); + radeon_agp_fini(rdev); + radeon_irq_kms_fini(rdev); + rdev->accel_working = false; + } return 0; } diff --git a/drivers/gpu/drm/radeon/rv515d.h b/drivers/gpu/drm/radeon/rv515d.h index a65e17ec1c08..fc216e49384d 100644 --- a/drivers/gpu/drm/radeon/rv515d.h +++ b/drivers/gpu/drm/radeon/rv515d.h @@ -216,5 +216,388 @@ #define CP_PACKET0_GET_ONE_REG_WR(h) (((h) >> 15) & 1) #define CP_PACKET3_GET_OPCODE(h) (((h) >> 8) & 0xFF) -#endif +/* Registers */ +#define R_0000F8_CONFIG_MEMSIZE 0x0000F8 +#define S_0000F8_CONFIG_MEMSIZE(x) (((x) & 0xFFFFFFFF) << 0) +#define G_0000F8_CONFIG_MEMSIZE(x) (((x) >> 0) & 0xFFFFFFFF) +#define C_0000F8_CONFIG_MEMSIZE 0x00000000 +#define R_000134_HDP_FB_LOCATION 0x000134 +#define S_000134_HDP_FB_START(x) (((x) & 0xFFFF) << 0) +#define G_000134_HDP_FB_START(x) (((x) >> 0) & 0xFFFF) +#define C_000134_HDP_FB_START 0xFFFF0000 +#define R_000300_VGA_RENDER_CONTROL 0x000300 +#define S_000300_VGA_BLINK_RATE(x) (((x) & 0x1F) << 0) +#define G_000300_VGA_BLINK_RATE(x) (((x) >> 0) & 0x1F) +#define C_000300_VGA_BLINK_RATE 0xFFFFFFE0 +#define S_000300_VGA_BLINK_MODE(x) (((x) & 0x3) << 5) +#define G_000300_VGA_BLINK_MODE(x) (((x) >> 5) & 0x3) +#define C_000300_VGA_BLINK_MODE 0xFFFFFF9F +#define S_000300_VGA_CURSOR_BLINK_INVERT(x) (((x) & 0x1) << 7) +#define G_000300_VGA_CURSOR_BLINK_INVERT(x) (((x) >> 7) & 0x1) +#define C_000300_VGA_CURSOR_BLINK_INVERT 0xFFFFFF7F +#define S_000300_VGA_EXTD_ADDR_COUNT_ENABLE(x) (((x) & 0x1) << 8) +#define G_000300_VGA_EXTD_ADDR_COUNT_ENABLE(x) (((x) >> 8) & 0x1) +#define C_000300_VGA_EXTD_ADDR_COUNT_ENABLE 0xFFFFFEFF +#define S_000300_VGA_VSTATUS_CNTL(x) (((x) & 0x3) << 16) +#define G_000300_VGA_VSTATUS_CNTL(x) (((x) >> 16) & 0x3) +#define C_000300_VGA_VSTATUS_CNTL 0xFFFCFFFF +#define S_000300_VGA_LOCK_8DOT(x) (((x) & 0x1) << 24) +#define G_000300_VGA_LOCK_8DOT(x) (((x) >> 24) & 0x1) +#define C_000300_VGA_LOCK_8DOT 0xFEFFFFFF +#define S_000300_VGAREG_LINECMP_COMPATIBILITY_SEL(x) (((x) & 0x1) << 25) +#define G_000300_VGAREG_LINECMP_COMPATIBILITY_SEL(x) (((x) >> 25) & 0x1) +#define C_000300_VGAREG_LINECMP_COMPATIBILITY_SEL 0xFDFFFFFF +#define R_000310_VGA_MEMORY_BASE_ADDRESS 0x000310 +#define S_000310_VGA_MEMORY_BASE_ADDRESS(x) (((x) & 0xFFFFFFFF) << 0) +#define G_000310_VGA_MEMORY_BASE_ADDRESS(x) (((x) >> 0) & 0xFFFFFFFF) +#define C_000310_VGA_MEMORY_BASE_ADDRESS 0x00000000 +#define R_000328_VGA_HDP_CONTROL 0x000328 +#define S_000328_VGA_MEM_PAGE_SELECT_EN(x) (((x) & 0x1) << 0) +#define G_000328_VGA_MEM_PAGE_SELECT_EN(x) (((x) >> 0) & 0x1) +#define C_000328_VGA_MEM_PAGE_SELECT_EN 0xFFFFFFFE +#define S_000328_VGA_RBBM_LOCK_DISABLE(x) (((x) & 0x1) << 8) +#define G_000328_VGA_RBBM_LOCK_DISABLE(x) (((x) >> 8) & 0x1) +#define C_000328_VGA_RBBM_LOCK_DISABLE 0xFFFFFEFF +#define S_000328_VGA_SOFT_RESET(x) (((x) & 0x1) << 16) +#define G_000328_VGA_SOFT_RESET(x) (((x) >> 16) & 0x1) +#define C_000328_VGA_SOFT_RESET 0xFFFEFFFF +#define S_000328_VGA_TEST_RESET_CONTROL(x) (((x) & 0x1) << 24) +#define G_000328_VGA_TEST_RESET_CONTROL(x) (((x) >> 24) & 0x1) +#define C_000328_VGA_TEST_RESET_CONTROL 0xFEFFFFFF +#define R_000330_D1VGA_CONTROL 0x000330 +#define S_000330_D1VGA_MODE_ENABLE(x) (((x) & 0x1) << 0) +#define G_000330_D1VGA_MODE_ENABLE(x) (((x) >> 0) & 0x1) +#define C_000330_D1VGA_MODE_ENABLE 0xFFFFFFFE +#define S_000330_D1VGA_TIMING_SELECT(x) (((x) & 0x1) << 8) +#define G_000330_D1VGA_TIMING_SELECT(x) (((x) >> 8) & 0x1) +#define C_000330_D1VGA_TIMING_SELECT 0xFFFFFEFF +#define S_000330_D1VGA_SYNC_POLARITY_SELECT(x) (((x) & 0x1) << 9) +#define G_000330_D1VGA_SYNC_POLARITY_SELECT(x) (((x) >> 9) & 0x1) +#define C_000330_D1VGA_SYNC_POLARITY_SELECT 0xFFFFFDFF +#define S_000330_D1VGA_OVERSCAN_TIMING_SELECT(x) (((x) & 0x1) << 10) +#define G_000330_D1VGA_OVERSCAN_TIMING_SELECT(x) (((x) >> 10) & 0x1) +#define C_000330_D1VGA_OVERSCAN_TIMING_SELECT 0xFFFFFBFF +#define S_000330_D1VGA_OVERSCAN_COLOR_EN(x) (((x) & 0x1) << 16) +#define G_000330_D1VGA_OVERSCAN_COLOR_EN(x) (((x) >> 16) & 0x1) +#define C_000330_D1VGA_OVERSCAN_COLOR_EN 0xFFFEFFFF +#define S_000330_D1VGA_ROTATE(x) (((x) & 0x3) << 24) +#define G_000330_D1VGA_ROTATE(x) (((x) >> 24) & 0x3) +#define C_000330_D1VGA_ROTATE 0xFCFFFFFF +#define R_000338_D2VGA_CONTROL 0x000338 +#define S_000338_D2VGA_MODE_ENABLE(x) (((x) & 0x1) << 0) +#define G_000338_D2VGA_MODE_ENABLE(x) (((x) >> 0) & 0x1) +#define C_000338_D2VGA_MODE_ENABLE 0xFFFFFFFE +#define S_000338_D2VGA_TIMING_SELECT(x) (((x) & 0x1) << 8) +#define G_000338_D2VGA_TIMING_SELECT(x) (((x) >> 8) & 0x1) +#define C_000338_D2VGA_TIMING_SELECT 0xFFFFFEFF +#define S_000338_D2VGA_SYNC_POLARITY_SELECT(x) (((x) & 0x1) << 9) +#define G_000338_D2VGA_SYNC_POLARITY_SELECT(x) (((x) >> 9) & 0x1) +#define C_000338_D2VGA_SYNC_POLARITY_SELECT 0xFFFFFDFF +#define S_000338_D2VGA_OVERSCAN_TIMING_SELECT(x) (((x) & 0x1) << 10) +#define G_000338_D2VGA_OVERSCAN_TIMING_SELECT(x) (((x) >> 10) & 0x1) +#define C_000338_D2VGA_OVERSCAN_TIMING_SELECT 0xFFFFFBFF +#define S_000338_D2VGA_OVERSCAN_COLOR_EN(x) (((x) & 0x1) << 16) +#define G_000338_D2VGA_OVERSCAN_COLOR_EN(x) (((x) >> 16) & 0x1) +#define C_000338_D2VGA_OVERSCAN_COLOR_EN 0xFFFEFFFF +#define S_000338_D2VGA_ROTATE(x) (((x) & 0x3) << 24) +#define G_000338_D2VGA_ROTATE(x) (((x) >> 24) & 0x3) +#define C_000338_D2VGA_ROTATE 0xFCFFFFFF +#define R_0007C0_CP_STAT 0x0007C0 +#define S_0007C0_MRU_BUSY(x) (((x) & 0x1) << 0) +#define G_0007C0_MRU_BUSY(x) (((x) >> 0) & 0x1) +#define C_0007C0_MRU_BUSY 0xFFFFFFFE +#define S_0007C0_MWU_BUSY(x) (((x) & 0x1) << 1) +#define G_0007C0_MWU_BUSY(x) (((x) >> 1) & 0x1) +#define C_0007C0_MWU_BUSY 0xFFFFFFFD +#define S_0007C0_RSIU_BUSY(x) (((x) & 0x1) << 2) +#define G_0007C0_RSIU_BUSY(x) (((x) >> 2) & 0x1) +#define C_0007C0_RSIU_BUSY 0xFFFFFFFB +#define S_0007C0_RCIU_BUSY(x) (((x) & 0x1) << 3) +#define G_0007C0_RCIU_BUSY(x) (((x) >> 3) & 0x1) +#define C_0007C0_RCIU_BUSY 0xFFFFFFF7 +#define S_0007C0_CSF_PRIMARY_BUSY(x) (((x) & 0x1) << 9) +#define G_0007C0_CSF_PRIMARY_BUSY(x) (((x) >> 9) & 0x1) +#define C_0007C0_CSF_PRIMARY_BUSY 0xFFFFFDFF +#define S_0007C0_CSF_INDIRECT_BUSY(x) (((x) & 0x1) << 10) +#define G_0007C0_CSF_INDIRECT_BUSY(x) (((x) >> 10) & 0x1) +#define C_0007C0_CSF_INDIRECT_BUSY 0xFFFFFBFF +#define S_0007C0_CSQ_PRIMARY_BUSY(x) (((x) & 0x1) << 11) +#define G_0007C0_CSQ_PRIMARY_BUSY(x) (((x) >> 11) & 0x1) +#define C_0007C0_CSQ_PRIMARY_BUSY 0xFFFFF7FF +#define S_0007C0_CSQ_INDIRECT_BUSY(x) (((x) & 0x1) << 12) +#define G_0007C0_CSQ_INDIRECT_BUSY(x) (((x) >> 12) & 0x1) +#define C_0007C0_CSQ_INDIRECT_BUSY 0xFFFFEFFF +#define S_0007C0_CSI_BUSY(x) (((x) & 0x1) << 13) +#define G_0007C0_CSI_BUSY(x) (((x) >> 13) & 0x1) +#define C_0007C0_CSI_BUSY 0xFFFFDFFF +#define S_0007C0_CSF_INDIRECT2_BUSY(x) (((x) & 0x1) << 14) +#define G_0007C0_CSF_INDIRECT2_BUSY(x) (((x) >> 14) & 0x1) +#define C_0007C0_CSF_INDIRECT2_BUSY 0xFFFFBFFF +#define S_0007C0_CSQ_INDIRECT2_BUSY(x) (((x) & 0x1) << 15) +#define G_0007C0_CSQ_INDIRECT2_BUSY(x) (((x) >> 15) & 0x1) +#define C_0007C0_CSQ_INDIRECT2_BUSY 0xFFFF7FFF +#define S_0007C0_GUIDMA_BUSY(x) (((x) & 0x1) << 28) +#define G_0007C0_GUIDMA_BUSY(x) (((x) >> 28) & 0x1) +#define C_0007C0_GUIDMA_BUSY 0xEFFFFFFF +#define S_0007C0_VIDDMA_BUSY(x) (((x) & 0x1) << 29) +#define G_0007C0_VIDDMA_BUSY(x) (((x) >> 29) & 0x1) +#define C_0007C0_VIDDMA_BUSY 0xDFFFFFFF +#define S_0007C0_CMDSTRM_BUSY(x) (((x) & 0x1) << 30) +#define G_0007C0_CMDSTRM_BUSY(x) (((x) >> 30) & 0x1) +#define C_0007C0_CMDSTRM_BUSY 0xBFFFFFFF +#define S_0007C0_CP_BUSY(x) (((x) & 0x1) << 31) +#define G_0007C0_CP_BUSY(x) (((x) >> 31) & 0x1) +#define C_0007C0_CP_BUSY 0x7FFFFFFF +#define R_000E40_RBBM_STATUS 0x000E40 +#define S_000E40_CMDFIFO_AVAIL(x) (((x) & 0x7F) << 0) +#define G_000E40_CMDFIFO_AVAIL(x) (((x) >> 0) & 0x7F) +#define C_000E40_CMDFIFO_AVAIL 0xFFFFFF80 +#define S_000E40_HIRQ_ON_RBB(x) (((x) & 0x1) << 8) +#define G_000E40_HIRQ_ON_RBB(x) (((x) >> 8) & 0x1) +#define C_000E40_HIRQ_ON_RBB 0xFFFFFEFF +#define S_000E40_CPRQ_ON_RBB(x) (((x) & 0x1) << 9) +#define G_000E40_CPRQ_ON_RBB(x) (((x) >> 9) & 0x1) +#define C_000E40_CPRQ_ON_RBB 0xFFFFFDFF +#define S_000E40_CFRQ_ON_RBB(x) (((x) & 0x1) << 10) +#define G_000E40_CFRQ_ON_RBB(x) (((x) >> 10) & 0x1) +#define C_000E40_CFRQ_ON_RBB 0xFFFFFBFF +#define S_000E40_HIRQ_IN_RTBUF(x) (((x) & 0x1) << 11) +#define G_000E40_HIRQ_IN_RTBUF(x) (((x) >> 11) & 0x1) +#define C_000E40_HIRQ_IN_RTBUF 0xFFFFF7FF +#define S_000E40_CPRQ_IN_RTBUF(x) (((x) & 0x1) << 12) +#define G_000E40_CPRQ_IN_RTBUF(x) (((x) >> 12) & 0x1) +#define C_000E40_CPRQ_IN_RTBUF 0xFFFFEFFF +#define S_000E40_CFRQ_IN_RTBUF(x) (((x) & 0x1) << 13) +#define G_000E40_CFRQ_IN_RTBUF(x) (((x) >> 13) & 0x1) +#define C_000E40_CFRQ_IN_RTBUF 0xFFFFDFFF +#define S_000E40_CF_PIPE_BUSY(x) (((x) & 0x1) << 14) +#define G_000E40_CF_PIPE_BUSY(x) (((x) >> 14) & 0x1) +#define C_000E40_CF_PIPE_BUSY 0xFFFFBFFF +#define S_000E40_ENG_EV_BUSY(x) (((x) & 0x1) << 15) +#define G_000E40_ENG_EV_BUSY(x) (((x) >> 15) & 0x1) +#define C_000E40_ENG_EV_BUSY 0xFFFF7FFF +#define S_000E40_CP_CMDSTRM_BUSY(x) (((x) & 0x1) << 16) +#define G_000E40_CP_CMDSTRM_BUSY(x) (((x) >> 16) & 0x1) +#define C_000E40_CP_CMDSTRM_BUSY 0xFFFEFFFF +#define S_000E40_E2_BUSY(x) (((x) & 0x1) << 17) +#define G_000E40_E2_BUSY(x) (((x) >> 17) & 0x1) +#define C_000E40_E2_BUSY 0xFFFDFFFF +#define S_000E40_RB2D_BUSY(x) (((x) & 0x1) << 18) +#define G_000E40_RB2D_BUSY(x) (((x) >> 18) & 0x1) +#define C_000E40_RB2D_BUSY 0xFFFBFFFF +#define S_000E40_RB3D_BUSY(x) (((x) & 0x1) << 19) +#define G_000E40_RB3D_BUSY(x) (((x) >> 19) & 0x1) +#define C_000E40_RB3D_BUSY 0xFFF7FFFF +#define S_000E40_VAP_BUSY(x) (((x) & 0x1) << 20) +#define G_000E40_VAP_BUSY(x) (((x) >> 20) & 0x1) +#define C_000E40_VAP_BUSY 0xFFEFFFFF +#define S_000E40_RE_BUSY(x) (((x) & 0x1) << 21) +#define G_000E40_RE_BUSY(x) (((x) >> 21) & 0x1) +#define C_000E40_RE_BUSY 0xFFDFFFFF +#define S_000E40_TAM_BUSY(x) (((x) & 0x1) << 22) +#define G_000E40_TAM_BUSY(x) (((x) >> 22) & 0x1) +#define C_000E40_TAM_BUSY 0xFFBFFFFF +#define S_000E40_TDM_BUSY(x) (((x) & 0x1) << 23) +#define G_000E40_TDM_BUSY(x) (((x) >> 23) & 0x1) +#define C_000E40_TDM_BUSY 0xFF7FFFFF +#define S_000E40_PB_BUSY(x) (((x) & 0x1) << 24) +#define G_000E40_PB_BUSY(x) (((x) >> 24) & 0x1) +#define C_000E40_PB_BUSY 0xFEFFFFFF +#define S_000E40_TIM_BUSY(x) (((x) & 0x1) << 25) +#define G_000E40_TIM_BUSY(x) (((x) >> 25) & 0x1) +#define C_000E40_TIM_BUSY 0xFDFFFFFF +#define S_000E40_GA_BUSY(x) (((x) & 0x1) << 26) +#define G_000E40_GA_BUSY(x) (((x) >> 26) & 0x1) +#define C_000E40_GA_BUSY 0xFBFFFFFF +#define S_000E40_CBA2D_BUSY(x) (((x) & 0x1) << 27) +#define G_000E40_CBA2D_BUSY(x) (((x) >> 27) & 0x1) +#define C_000E40_CBA2D_BUSY 0xF7FFFFFF +#define S_000E40_RBBM_HIBUSY(x) (((x) & 0x1) << 28) +#define G_000E40_RBBM_HIBUSY(x) (((x) >> 28) & 0x1) +#define C_000E40_RBBM_HIBUSY 0xEFFFFFFF +#define S_000E40_SKID_CFBUSY(x) (((x) & 0x1) << 29) +#define G_000E40_SKID_CFBUSY(x) (((x) >> 29) & 0x1) +#define C_000E40_SKID_CFBUSY 0xDFFFFFFF +#define S_000E40_VAP_VF_BUSY(x) (((x) & 0x1) << 30) +#define G_000E40_VAP_VF_BUSY(x) (((x) >> 30) & 0x1) +#define C_000E40_VAP_VF_BUSY 0xBFFFFFFF +#define S_000E40_GUI_ACTIVE(x) (((x) & 0x1) << 31) +#define G_000E40_GUI_ACTIVE(x) (((x) >> 31) & 0x1) +#define C_000E40_GUI_ACTIVE 0x7FFFFFFF +#define R_006080_D1CRTC_CONTROL 0x006080 +#define S_006080_D1CRTC_MASTER_EN(x) (((x) & 0x1) << 0) +#define G_006080_D1CRTC_MASTER_EN(x) (((x) >> 0) & 0x1) +#define C_006080_D1CRTC_MASTER_EN 0xFFFFFFFE +#define S_006080_D1CRTC_SYNC_RESET_SEL(x) (((x) & 0x1) << 4) +#define G_006080_D1CRTC_SYNC_RESET_SEL(x) (((x) >> 4) & 0x1) +#define C_006080_D1CRTC_SYNC_RESET_SEL 0xFFFFFFEF +#define S_006080_D1CRTC_DISABLE_POINT_CNTL(x) (((x) & 0x3) << 8) +#define G_006080_D1CRTC_DISABLE_POINT_CNTL(x) (((x) >> 8) & 0x3) +#define C_006080_D1CRTC_DISABLE_POINT_CNTL 0xFFFFFCFF +#define S_006080_D1CRTC_CURRENT_MASTER_EN_STATE(x) (((x) & 0x1) << 16) +#define G_006080_D1CRTC_CURRENT_MASTER_EN_STATE(x) (((x) >> 16) & 0x1) +#define C_006080_D1CRTC_CURRENT_MASTER_EN_STATE 0xFFFEFFFF +#define S_006080_D1CRTC_DISP_READ_REQUEST_DISABLE(x) (((x) & 0x1) << 24) +#define G_006080_D1CRTC_DISP_READ_REQUEST_DISABLE(x) (((x) >> 24) & 0x1) +#define C_006080_D1CRTC_DISP_READ_REQUEST_DISABLE 0xFEFFFFFF +#define R_0060E8_D1CRTC_UPDATE_LOCK 0x0060E8 +#define S_0060E8_D1CRTC_UPDATE_LOCK(x) (((x) & 0x1) << 0) +#define G_0060E8_D1CRTC_UPDATE_LOCK(x) (((x) >> 0) & 0x1) +#define C_0060E8_D1CRTC_UPDATE_LOCK 0xFFFFFFFE +#define R_006110_D1GRPH_PRIMARY_SURFACE_ADDRESS 0x006110 +#define S_006110_D1GRPH_PRIMARY_SURFACE_ADDRESS(x) (((x) & 0xFFFFFFFF) << 0) +#define G_006110_D1GRPH_PRIMARY_SURFACE_ADDRESS(x) (((x) >> 0) & 0xFFFFFFFF) +#define C_006110_D1GRPH_PRIMARY_SURFACE_ADDRESS 0x00000000 +#define R_006118_D1GRPH_SECONDARY_SURFACE_ADDRESS 0x006118 +#define S_006118_D1GRPH_SECONDARY_SURFACE_ADDRESS(x) (((x) & 0xFFFFFFFF) << 0) +#define G_006118_D1GRPH_SECONDARY_SURFACE_ADDRESS(x) (((x) >> 0) & 0xFFFFFFFF) +#define C_006118_D1GRPH_SECONDARY_SURFACE_ADDRESS 0x00000000 +#define R_006880_D2CRTC_CONTROL 0x006880 +#define S_006880_D2CRTC_MASTER_EN(x) (((x) & 0x1) << 0) +#define G_006880_D2CRTC_MASTER_EN(x) (((x) >> 0) & 0x1) +#define C_006880_D2CRTC_MASTER_EN 0xFFFFFFFE +#define S_006880_D2CRTC_SYNC_RESET_SEL(x) (((x) & 0x1) << 4) +#define G_006880_D2CRTC_SYNC_RESET_SEL(x) (((x) >> 4) & 0x1) +#define C_006880_D2CRTC_SYNC_RESET_SEL 0xFFFFFFEF +#define S_006880_D2CRTC_DISABLE_POINT_CNTL(x) (((x) & 0x3) << 8) +#define G_006880_D2CRTC_DISABLE_POINT_CNTL(x) (((x) >> 8) & 0x3) +#define C_006880_D2CRTC_DISABLE_POINT_CNTL 0xFFFFFCFF +#define S_006880_D2CRTC_CURRENT_MASTER_EN_STATE(x) (((x) & 0x1) << 16) +#define G_006880_D2CRTC_CURRENT_MASTER_EN_STATE(x) (((x) >> 16) & 0x1) +#define C_006880_D2CRTC_CURRENT_MASTER_EN_STATE 0xFFFEFFFF +#define S_006880_D2CRTC_DISP_READ_REQUEST_DISABLE(x) (((x) & 0x1) << 24) +#define G_006880_D2CRTC_DISP_READ_REQUEST_DISABLE(x) (((x) >> 24) & 0x1) +#define C_006880_D2CRTC_DISP_READ_REQUEST_DISABLE 0xFEFFFFFF +#define R_0068E8_D2CRTC_UPDATE_LOCK 0x0068E8 +#define S_0068E8_D2CRTC_UPDATE_LOCK(x) (((x) & 0x1) << 0) +#define G_0068E8_D2CRTC_UPDATE_LOCK(x) (((x) >> 0) & 0x1) +#define C_0068E8_D2CRTC_UPDATE_LOCK 0xFFFFFFFE +#define R_006910_D2GRPH_PRIMARY_SURFACE_ADDRESS 0x006910 +#define S_006910_D2GRPH_PRIMARY_SURFACE_ADDRESS(x) (((x) & 0xFFFFFFFF) << 0) +#define G_006910_D2GRPH_PRIMARY_SURFACE_ADDRESS(x) (((x) >> 0) & 0xFFFFFFFF) +#define C_006910_D2GRPH_PRIMARY_SURFACE_ADDRESS 0x00000000 +#define R_006918_D2GRPH_SECONDARY_SURFACE_ADDRESS 0x006918 +#define S_006918_D2GRPH_SECONDARY_SURFACE_ADDRESS(x) (((x) & 0xFFFFFFFF) << 0) +#define G_006918_D2GRPH_SECONDARY_SURFACE_ADDRESS(x) (((x) >> 0) & 0xFFFFFFFF) +#define C_006918_D2GRPH_SECONDARY_SURFACE_ADDRESS 0x00000000 + + +#define R_000001_MC_FB_LOCATION 0x000001 +#define S_000001_MC_FB_START(x) (((x) & 0xFFFF) << 0) +#define G_000001_MC_FB_START(x) (((x) >> 0) & 0xFFFF) +#define C_000001_MC_FB_START 0xFFFF0000 +#define S_000001_MC_FB_TOP(x) (((x) & 0xFFFF) << 16) +#define G_000001_MC_FB_TOP(x) (((x) >> 16) & 0xFFFF) +#define C_000001_MC_FB_TOP 0x0000FFFF +#define R_000002_MC_AGP_LOCATION 0x000002 +#define S_000002_MC_AGP_START(x) (((x) & 0xFFFF) << 0) +#define G_000002_MC_AGP_START(x) (((x) >> 0) & 0xFFFF) +#define C_000002_MC_AGP_START 0xFFFF0000 +#define S_000002_MC_AGP_TOP(x) (((x) & 0xFFFF) << 16) +#define G_000002_MC_AGP_TOP(x) (((x) >> 16) & 0xFFFF) +#define C_000002_MC_AGP_TOP 0x0000FFFF +#define R_000003_MC_AGP_BASE 0x000003 +#define S_000003_AGP_BASE_ADDR(x) (((x) & 0xFFFFFFFF) << 0) +#define G_000003_AGP_BASE_ADDR(x) (((x) >> 0) & 0xFFFFFFFF) +#define C_000003_AGP_BASE_ADDR 0x00000000 +#define R_000004_MC_AGP_BASE_2 0x000004 +#define S_000004_AGP_BASE_ADDR_2(x) (((x) & 0xF) << 0) +#define G_000004_AGP_BASE_ADDR_2(x) (((x) >> 0) & 0xF) +#define C_000004_AGP_BASE_ADDR_2 0xFFFFFFF0 + +#define R_00000F_CP_DYN_CNTL 0x00000F +#define S_00000F_CP_FORCEON(x) (((x) & 0x1) << 0) +#define G_00000F_CP_FORCEON(x) (((x) >> 0) & 0x1) +#define C_00000F_CP_FORCEON 0xFFFFFFFE +#define S_00000F_CP_MAX_DYN_STOP_LAT(x) (((x) & 0x1) << 1) +#define G_00000F_CP_MAX_DYN_STOP_LAT(x) (((x) >> 1) & 0x1) +#define C_00000F_CP_MAX_DYN_STOP_LAT 0xFFFFFFFD +#define S_00000F_CP_CLOCK_STATUS(x) (((x) & 0x1) << 2) +#define G_00000F_CP_CLOCK_STATUS(x) (((x) >> 2) & 0x1) +#define C_00000F_CP_CLOCK_STATUS 0xFFFFFFFB +#define S_00000F_CP_PROG_SHUTOFF(x) (((x) & 0x1) << 3) +#define G_00000F_CP_PROG_SHUTOFF(x) (((x) >> 3) & 0x1) +#define C_00000F_CP_PROG_SHUTOFF 0xFFFFFFF7 +#define S_00000F_CP_PROG_DELAY_VALUE(x) (((x) & 0xFF) << 4) +#define G_00000F_CP_PROG_DELAY_VALUE(x) (((x) >> 4) & 0xFF) +#define C_00000F_CP_PROG_DELAY_VALUE 0xFFFFF00F +#define S_00000F_CP_LOWER_POWER_IDLE(x) (((x) & 0xFF) << 12) +#define G_00000F_CP_LOWER_POWER_IDLE(x) (((x) >> 12) & 0xFF) +#define C_00000F_CP_LOWER_POWER_IDLE 0xFFF00FFF +#define S_00000F_CP_LOWER_POWER_IGNORE(x) (((x) & 0x1) << 20) +#define G_00000F_CP_LOWER_POWER_IGNORE(x) (((x) >> 20) & 0x1) +#define C_00000F_CP_LOWER_POWER_IGNORE 0xFFEFFFFF +#define S_00000F_CP_NORMAL_POWER_IGNORE(x) (((x) & 0x1) << 21) +#define G_00000F_CP_NORMAL_POWER_IGNORE(x) (((x) >> 21) & 0x1) +#define C_00000F_CP_NORMAL_POWER_IGNORE 0xFFDFFFFF +#define S_00000F_SPARE(x) (((x) & 0x3) << 22) +#define G_00000F_SPARE(x) (((x) >> 22) & 0x3) +#define C_00000F_SPARE 0xFF3FFFFF +#define S_00000F_CP_NORMAL_POWER_BUSY(x) (((x) & 0xFF) << 24) +#define G_00000F_CP_NORMAL_POWER_BUSY(x) (((x) >> 24) & 0xFF) +#define C_00000F_CP_NORMAL_POWER_BUSY 0x00FFFFFF +#define R_000011_E2_DYN_CNTL 0x000011 +#define S_000011_E2_FORCEON(x) (((x) & 0x1) << 0) +#define G_000011_E2_FORCEON(x) (((x) >> 0) & 0x1) +#define C_000011_E2_FORCEON 0xFFFFFFFE +#define S_000011_E2_MAX_DYN_STOP_LAT(x) (((x) & 0x1) << 1) +#define G_000011_E2_MAX_DYN_STOP_LAT(x) (((x) >> 1) & 0x1) +#define C_000011_E2_MAX_DYN_STOP_LAT 0xFFFFFFFD +#define S_000011_E2_CLOCK_STATUS(x) (((x) & 0x1) << 2) +#define G_000011_E2_CLOCK_STATUS(x) (((x) >> 2) & 0x1) +#define C_000011_E2_CLOCK_STATUS 0xFFFFFFFB +#define S_000011_E2_PROG_SHUTOFF(x) (((x) & 0x1) << 3) +#define G_000011_E2_PROG_SHUTOFF(x) (((x) >> 3) & 0x1) +#define C_000011_E2_PROG_SHUTOFF 0xFFFFFFF7 +#define S_000011_E2_PROG_DELAY_VALUE(x) (((x) & 0xFF) << 4) +#define G_000011_E2_PROG_DELAY_VALUE(x) (((x) >> 4) & 0xFF) +#define C_000011_E2_PROG_DELAY_VALUE 0xFFFFF00F +#define S_000011_E2_LOWER_POWER_IDLE(x) (((x) & 0xFF) << 12) +#define G_000011_E2_LOWER_POWER_IDLE(x) (((x) >> 12) & 0xFF) +#define C_000011_E2_LOWER_POWER_IDLE 0xFFF00FFF +#define S_000011_E2_LOWER_POWER_IGNORE(x) (((x) & 0x1) << 20) +#define G_000011_E2_LOWER_POWER_IGNORE(x) (((x) >> 20) & 0x1) +#define C_000011_E2_LOWER_POWER_IGNORE 0xFFEFFFFF +#define S_000011_E2_NORMAL_POWER_IGNORE(x) (((x) & 0x1) << 21) +#define G_000011_E2_NORMAL_POWER_IGNORE(x) (((x) >> 21) & 0x1) +#define C_000011_E2_NORMAL_POWER_IGNORE 0xFFDFFFFF +#define S_000011_SPARE(x) (((x) & 0x3) << 22) +#define G_000011_SPARE(x) (((x) >> 22) & 0x3) +#define C_000011_SPARE 0xFF3FFFFF +#define S_000011_E2_NORMAL_POWER_BUSY(x) (((x) & 0xFF) << 24) +#define G_000011_E2_NORMAL_POWER_BUSY(x) (((x) >> 24) & 0xFF) +#define C_000011_E2_NORMAL_POWER_BUSY 0x00FFFFFF +#define R_000013_IDCT_DYN_CNTL 0x000013 +#define S_000013_IDCT_FORCEON(x) (((x) & 0x1) << 0) +#define G_000013_IDCT_FORCEON(x) (((x) >> 0) & 0x1) +#define C_000013_IDCT_FORCEON 0xFFFFFFFE +#define S_000013_IDCT_MAX_DYN_STOP_LAT(x) (((x) & 0x1) << 1) +#define G_000013_IDCT_MAX_DYN_STOP_LAT(x) (((x) >> 1) & 0x1) +#define C_000013_IDCT_MAX_DYN_STOP_LAT 0xFFFFFFFD +#define S_000013_IDCT_CLOCK_STATUS(x) (((x) & 0x1) << 2) +#define G_000013_IDCT_CLOCK_STATUS(x) (((x) >> 2) & 0x1) +#define C_000013_IDCT_CLOCK_STATUS 0xFFFFFFFB +#define S_000013_IDCT_PROG_SHUTOFF(x) (((x) & 0x1) << 3) +#define G_000013_IDCT_PROG_SHUTOFF(x) (((x) >> 3) & 0x1) +#define C_000013_IDCT_PROG_SHUTOFF 0xFFFFFFF7 +#define S_000013_IDCT_PROG_DELAY_VALUE(x) (((x) & 0xFF) << 4) +#define G_000013_IDCT_PROG_DELAY_VALUE(x) (((x) >> 4) & 0xFF) +#define C_000013_IDCT_PROG_DELAY_VALUE 0xFFFFF00F +#define S_000013_IDCT_LOWER_POWER_IDLE(x) (((x) & 0xFF) << 12) +#define G_000013_IDCT_LOWER_POWER_IDLE(x) (((x) >> 12) & 0xFF) +#define C_000013_IDCT_LOWER_POWER_IDLE 0xFFF00FFF +#define S_000013_IDCT_LOWER_POWER_IGNORE(x) (((x) & 0x1) << 20) +#define G_000013_IDCT_LOWER_POWER_IGNORE(x) (((x) >> 20) & 0x1) +#define C_000013_IDCT_LOWER_POWER_IGNORE 0xFFEFFFFF +#define S_000013_IDCT_NORMAL_POWER_IGNORE(x) (((x) & 0x1) << 21) +#define G_000013_IDCT_NORMAL_POWER_IGNORE(x) (((x) >> 21) & 0x1) +#define C_000013_IDCT_NORMAL_POWER_IGNORE 0xFFDFFFFF +#define S_000013_SPARE(x) (((x) & 0x3) << 22) +#define G_000013_SPARE(x) (((x) >> 22) & 0x3) +#define C_000013_SPARE 0xFF3FFFFF +#define S_000013_IDCT_NORMAL_POWER_BUSY(x) (((x) & 0xFF) << 24) +#define G_000013_IDCT_NORMAL_POWER_BUSY(x) (((x) >> 24) & 0xFF) +#define C_000013_IDCT_NORMAL_POWER_BUSY 0x00FFFFFF + +#endif diff --git a/drivers/gpu/drm/radeon/rv770.c b/drivers/gpu/drm/radeon/rv770.c index b574c73a5109..b0efd0ddae7a 100644 --- a/drivers/gpu/drm/radeon/rv770.c +++ b/drivers/gpu/drm/radeon/rv770.c @@ -31,8 +31,8 @@ #include "radeon.h" #include "radeon_drm.h" #include "rv770d.h" -#include "avivod.h" #include "atom.h" +#include "avivod.h" #define R700_PFP_UCODE_SIZE 848 #define R700_PM4_UCODE_SIZE 1360 @@ -75,7 +75,7 @@ int rv770_pcie_gart_enable(struct radeon_device *rdev) WREG32(MC_VM_MB_L1_TLB2_CNTL, tmp); WREG32(MC_VM_MB_L1_TLB3_CNTL, tmp); WREG32(VM_CONTEXT0_PAGE_TABLE_START_ADDR, rdev->mc.gtt_start >> 12); - WREG32(VM_CONTEXT0_PAGE_TABLE_END_ADDR, (rdev->mc.gtt_end - 1) >> 12); + WREG32(VM_CONTEXT0_PAGE_TABLE_END_ADDR, rdev->mc.gtt_end >> 12); WREG32(VM_CONTEXT0_PAGE_TABLE_BASE_ADDR, rdev->gart.table_addr >> 12); WREG32(VM_CONTEXT0_CNTL, ENABLE_CONTEXT | PAGE_TABLE_DEPTH(0) | RANGE_PROTECTION_FAULT_ENABLE_DEFAULT); @@ -126,17 +126,36 @@ void rv770_pcie_gart_fini(struct radeon_device *rdev) } -/* - * MC - */ -static void rv770_mc_resume(struct radeon_device *rdev) +void rv770_agp_enable(struct radeon_device *rdev) +{ + u32 tmp; + int i; + + /* Setup L2 cache */ + WREG32(VM_L2_CNTL, ENABLE_L2_CACHE | ENABLE_L2_FRAGMENT_PROCESSING | + ENABLE_L2_PTE_CACHE_LRU_UPDATE_BY_WRITE | + EFFECTIVE_L2_QUEUE_SIZE(7)); + WREG32(VM_L2_CNTL2, 0); + WREG32(VM_L2_CNTL3, BANK_SELECT(0) | CACHE_UPDATE_MODE(2)); + /* Setup TLB control */ + tmp = ENABLE_L1_TLB | ENABLE_L1_FRAGMENT_PROCESSING | + SYSTEM_ACCESS_MODE_NOT_IN_SYS | + SYSTEM_APERTURE_UNMAPPED_ACCESS_PASS_THRU | + EFFECTIVE_L1_TLB_SIZE(5) | EFFECTIVE_L1_QUEUE_SIZE(5); + WREG32(MC_VM_MD_L1_TLB0_CNTL, tmp); + WREG32(MC_VM_MD_L1_TLB1_CNTL, tmp); + WREG32(MC_VM_MD_L1_TLB2_CNTL, tmp); + WREG32(MC_VM_MB_L1_TLB0_CNTL, tmp); + WREG32(MC_VM_MB_L1_TLB1_CNTL, tmp); + WREG32(MC_VM_MB_L1_TLB2_CNTL, tmp); + WREG32(MC_VM_MB_L1_TLB3_CNTL, tmp); + for (i = 0; i < 7; i++) + WREG32(VM_CONTEXT0_CNTL + (i * 4), 0); +} + +static void rv770_mc_program(struct radeon_device *rdev) { - u32 d1vga_control, d2vga_control; - u32 vga_render_control, vga_hdp_control; - u32 d1crtc_control, d2crtc_control; - u32 new_d1grph_primary, new_d1grph_secondary; - u32 new_d2grph_primary, new_d2grph_secondary; - u64 old_vram_start; + struct rv515_mc_save save; u32 tmp; int i, j; @@ -150,53 +169,42 @@ static void rv770_mc_resume(struct radeon_device *rdev) } WREG32(HDP_REG_COHERENCY_FLUSH_CNTL, 0); - d1vga_control = RREG32(D1VGA_CONTROL); - d2vga_control = RREG32(D2VGA_CONTROL); - vga_render_control = RREG32(VGA_RENDER_CONTROL); - vga_hdp_control = RREG32(VGA_HDP_CONTROL); - d1crtc_control = RREG32(D1CRTC_CONTROL); - d2crtc_control = RREG32(D2CRTC_CONTROL); - old_vram_start = (u64)(RREG32(MC_VM_FB_LOCATION) & 0xFFFF) << 24; - new_d1grph_primary = RREG32(D1GRPH_PRIMARY_SURFACE_ADDRESS); - new_d1grph_secondary = RREG32(D1GRPH_SECONDARY_SURFACE_ADDRESS); - new_d1grph_primary += rdev->mc.vram_start - old_vram_start; - new_d1grph_secondary += rdev->mc.vram_start - old_vram_start; - new_d2grph_primary = RREG32(D2GRPH_PRIMARY_SURFACE_ADDRESS); - new_d2grph_secondary = RREG32(D2GRPH_SECONDARY_SURFACE_ADDRESS); - new_d2grph_primary += rdev->mc.vram_start - old_vram_start; - new_d2grph_secondary += rdev->mc.vram_start - old_vram_start; - - /* Stop all video */ - WREG32(D1VGA_CONTROL, 0); - WREG32(D2VGA_CONTROL, 0); - WREG32(VGA_RENDER_CONTROL, 0); - WREG32(D1CRTC_UPDATE_LOCK, 1); - WREG32(D2CRTC_UPDATE_LOCK, 1); - WREG32(D1CRTC_CONTROL, 0); - WREG32(D2CRTC_CONTROL, 0); - WREG32(D1CRTC_UPDATE_LOCK, 0); - WREG32(D2CRTC_UPDATE_LOCK, 0); - - mdelay(1); + rv515_mc_stop(rdev, &save); if (r600_mc_wait_for_idle(rdev)) { - printk(KERN_WARNING "[drm] MC not idle !\n"); + dev_warn(rdev->dev, "Wait for MC idle timedout !\n"); } - /* Lockout access through VGA aperture*/ WREG32(VGA_HDP_CONTROL, VGA_MEMORY_DISABLE); - /* Update configuration */ - WREG32(MC_VM_SYSTEM_APERTURE_LOW_ADDR, rdev->mc.vram_start >> 12); - WREG32(MC_VM_SYSTEM_APERTURE_HIGH_ADDR, (rdev->mc.vram_end - 1) >> 12); + if (rdev->flags & RADEON_IS_AGP) { + if (rdev->mc.vram_start < rdev->mc.gtt_start) { + /* VRAM before AGP */ + WREG32(MC_VM_SYSTEM_APERTURE_LOW_ADDR, + rdev->mc.vram_start >> 12); + WREG32(MC_VM_SYSTEM_APERTURE_HIGH_ADDR, + rdev->mc.gtt_end >> 12); + } else { + /* VRAM after AGP */ + WREG32(MC_VM_SYSTEM_APERTURE_LOW_ADDR, + rdev->mc.gtt_start >> 12); + WREG32(MC_VM_SYSTEM_APERTURE_HIGH_ADDR, + rdev->mc.vram_end >> 12); + } + } else { + WREG32(MC_VM_SYSTEM_APERTURE_LOW_ADDR, + rdev->mc.vram_start >> 12); + WREG32(MC_VM_SYSTEM_APERTURE_HIGH_ADDR, + rdev->mc.vram_end >> 12); + } WREG32(MC_VM_SYSTEM_APERTURE_DEFAULT_ADDR, 0); - tmp = (((rdev->mc.vram_end - 1) >> 24) & 0xFFFF) << 16; + tmp = ((rdev->mc.vram_end >> 24) & 0xFFFF) << 16; tmp |= ((rdev->mc.vram_start >> 24) & 0xFFFF); WREG32(MC_VM_FB_LOCATION, tmp); WREG32(HDP_NONSURFACE_BASE, (rdev->mc.vram_start >> 8)); WREG32(HDP_NONSURFACE_INFO, (2 << 7)); WREG32(HDP_NONSURFACE_SIZE, (rdev->mc.mc_vram_size - 1) | 0x3FF); if (rdev->flags & RADEON_IS_AGP) { - WREG32(MC_VM_AGP_TOP, (rdev->mc.gtt_end - 1) >> 16); + WREG32(MC_VM_AGP_TOP, rdev->mc.gtt_end >> 16); WREG32(MC_VM_AGP_BOT, rdev->mc.gtt_start >> 16); WREG32(MC_VM_AGP_BASE, rdev->mc.agp_base >> 22); } else { @@ -204,34 +212,13 @@ static void rv770_mc_resume(struct radeon_device *rdev) WREG32(MC_VM_AGP_TOP, 0x0FFFFFFF); WREG32(MC_VM_AGP_BOT, 0x0FFFFFFF); } - WREG32(D1GRPH_PRIMARY_SURFACE_ADDRESS, new_d1grph_primary); - WREG32(D1GRPH_SECONDARY_SURFACE_ADDRESS, new_d1grph_secondary); - WREG32(D2GRPH_PRIMARY_SURFACE_ADDRESS, new_d2grph_primary); - WREG32(D2GRPH_SECONDARY_SURFACE_ADDRESS, new_d2grph_secondary); - WREG32(VGA_MEMORY_BASE_ADDRESS, rdev->mc.vram_start); - - /* Unlock host access */ - WREG32(VGA_HDP_CONTROL, vga_hdp_control); - - mdelay(1); if (r600_mc_wait_for_idle(rdev)) { - printk(KERN_WARNING "[drm] MC not idle !\n"); + dev_warn(rdev->dev, "Wait for MC idle timedout !\n"); } - - /* Restore video state */ - WREG32(D1CRTC_UPDATE_LOCK, 1); - WREG32(D2CRTC_UPDATE_LOCK, 1); - WREG32(D1CRTC_CONTROL, d1crtc_control); - WREG32(D2CRTC_CONTROL, d2crtc_control); - WREG32(D1CRTC_UPDATE_LOCK, 0); - WREG32(D2CRTC_UPDATE_LOCK, 0); - WREG32(D1VGA_CONTROL, d1vga_control); - WREG32(D2VGA_CONTROL, d2vga_control); - WREG32(VGA_RENDER_CONTROL, vga_render_control); - + rv515_mc_resume(rdev, &save); /* we need to own VRAM, so turn off the VGA renderer here * to stop it overwriting our objects */ - radeon_avivo_vga_render_disable(rdev); + rv515_vga_render_disable(rdev); } @@ -542,11 +529,11 @@ static void rv770_gpu_init(struct radeon_device *rdev) if (rdev->family == CHIP_RV770) gb_tiling_config |= BANK_TILING(1); else - gb_tiling_config |= BANK_TILING((mc_arb_ramcfg & NOOFBANK_SHIFT) >> NOOFBANK_MASK); + gb_tiling_config |= BANK_TILING((mc_arb_ramcfg & NOOFBANK_MASK) >> NOOFBANK_SHIFT); gb_tiling_config |= GROUP_SIZE(0); - if (((mc_arb_ramcfg & NOOFROWS_MASK) & NOOFROWS_SHIFT) > 3) { + if (((mc_arb_ramcfg & NOOFROWS_MASK) >> NOOFROWS_SHIFT) > 3) { gb_tiling_config |= ROW_TILING(3); gb_tiling_config |= SAMPLE_SPLIT(3); } else { @@ -592,14 +579,14 @@ static void rv770_gpu_init(struct radeon_device *rdev) /* set HW defaults for 3D engine */ WREG32(CP_QUEUE_THRESHOLDS, (ROQ_IB1_START(0x16) | - ROQ_IB2_START(0x2b))); + ROQ_IB2_START(0x2b))); WREG32(CP_MEQ_THRESHOLDS, STQ_SPLIT(0x30)); WREG32(TA_CNTL_AUX, (DISABLE_CUBE_ANISO | - SYNC_GRADIENT | - SYNC_WALKER | - SYNC_ALIGNER)); + SYNC_GRADIENT | + SYNC_WALKER | + SYNC_ALIGNER)); sx_debug_1 = RREG32(SX_DEBUG_1); sx_debug_1 |= ENABLE_NEW_SMX_ADDRESS; @@ -611,9 +598,9 @@ static void rv770_gpu_init(struct radeon_device *rdev) WREG32(SMX_DC_CTL0, smx_dc_ctl0); WREG32(SMX_EVENT_CTL, (ES_FLUSH_CTL(4) | - GS_FLUSH_CTL(4) | - ACK_FLUSH_CTL(3) | - SYNC_FLUSH_CTL)); + GS_FLUSH_CTL(4) | + ACK_FLUSH_CTL(3) | + SYNC_FLUSH_CTL)); if (rdev->family == CHIP_RV770) WREG32(DB_DEBUG3, DB_CLK_OFF_DELAY(0x1f)); @@ -624,12 +611,12 @@ static void rv770_gpu_init(struct radeon_device *rdev) } WREG32(SX_EXPORT_BUFFER_SIZES, (COLOR_BUFFER_SIZE((rdev->config.rv770.sx_max_export_size / 4) - 1) | - POSITION_BUFFER_SIZE((rdev->config.rv770.sx_max_export_pos_size / 4) - 1) | - SMX_BUFFER_SIZE((rdev->config.rv770.sx_max_export_smx_size / 4) - 1))); + POSITION_BUFFER_SIZE((rdev->config.rv770.sx_max_export_pos_size / 4) - 1) | + SMX_BUFFER_SIZE((rdev->config.rv770.sx_max_export_smx_size / 4) - 1))); WREG32(PA_SC_FIFO_SIZE, (SC_PRIM_FIFO_SIZE(rdev->config.rv770.sc_prim_fifo_size) | - SC_HIZ_TILE_FIFO_SIZE(rdev->config.rv770.sc_hiz_tile_fifo_size) | - SC_EARLYZ_TILE_FIFO_SIZE(rdev->config.rv770.sc_earlyz_tile_fifo_fize))); + SC_HIZ_TILE_FIFO_SIZE(rdev->config.rv770.sc_hiz_tile_fifo_size) | + SC_EARLYZ_TILE_FIFO_SIZE(rdev->config.rv770.sc_earlyz_tile_fifo_fize))); WREG32(PA_SC_MULTI_CHIP_CNTL, 0); @@ -787,20 +774,49 @@ int rv770_mc_init(struct radeon_device *rdev) { fixed20_12 a; u32 tmp; + int chansize, numchan; int r; /* Get VRAM informations */ - /* FIXME: Don't know how to determine vram width, need to check - * vram_width usage - */ - rdev->mc.vram_width = 128; rdev->mc.vram_is_ddr = true; + tmp = RREG32(MC_ARB_RAMCFG); + if (tmp & CHANSIZE_OVERRIDE) { + chansize = 16; + } else if (tmp & CHANSIZE_MASK) { + chansize = 64; + } else { + chansize = 32; + } + tmp = RREG32(MC_SHARED_CHMAP); + switch ((tmp & NOOFCHAN_MASK) >> NOOFCHAN_SHIFT) { + case 0: + default: + numchan = 1; + break; + case 1: + numchan = 2; + break; + case 2: + numchan = 4; + break; + case 3: + numchan = 8; + break; + } + rdev->mc.vram_width = numchan * chansize; /* Could aper size report 0 ? */ rdev->mc.aper_base = drm_get_resource_start(rdev->ddev, 0); rdev->mc.aper_size = drm_get_resource_len(rdev->ddev, 0); /* Setup GPU memory space */ rdev->mc.mc_vram_size = RREG32(CONFIG_MEMSIZE); rdev->mc.real_vram_size = RREG32(CONFIG_MEMSIZE); + + if (rdev->mc.mc_vram_size > rdev->mc.aper_size) + rdev->mc.mc_vram_size = rdev->mc.aper_size; + + if (rdev->mc.real_vram_size > rdev->mc.aper_size) + rdev->mc.real_vram_size = rdev->mc.aper_size; + if (rdev->flags & RADEON_IS_AGP) { r = radeon_agp_init(rdev); if (r) @@ -833,9 +849,9 @@ int rv770_mc_init(struct radeon_device *rdev) rdev->mc.gtt_size = radeon_gart_size * 1024 * 1024; } rdev->mc.vram_start = rdev->mc.vram_location; - rdev->mc.vram_end = rdev->mc.vram_location + rdev->mc.mc_vram_size; + rdev->mc.vram_end = rdev->mc.vram_location + rdev->mc.mc_vram_size - 1; rdev->mc.gtt_start = rdev->mc.gtt_location; - rdev->mc.gtt_end = rdev->mc.gtt_location + rdev->mc.gtt_size; + rdev->mc.gtt_end = rdev->mc.gtt_location + rdev->mc.gtt_size - 1; /* FIXME: we should enforce default clock in case GPU is not in * default setup */ @@ -854,11 +870,14 @@ static int rv770_startup(struct radeon_device *rdev) { int r; - radeon_gpu_reset(rdev); - rv770_mc_resume(rdev); - r = rv770_pcie_gart_enable(rdev); - if (r) - return r; + rv770_mc_program(rdev); + if (rdev->flags & RADEON_IS_AGP) { + rv770_agp_enable(rdev); + } else { + r = rv770_pcie_gart_enable(rdev); + if (r) + return r; + } rv770_gpu_init(rdev); r = radeon_object_pin(rdev->r600_blit.shader_obj, RADEON_GEM_DOMAIN_VRAM, @@ -877,9 +896,8 @@ static int rv770_startup(struct radeon_device *rdev) r = r600_cp_resume(rdev); if (r) return r; - r = r600_wb_init(rdev); - if (r) - return r; + /* write back buffer are not vital so don't worry about failure */ + r600_wb_enable(rdev); return 0; } @@ -887,15 +905,12 @@ int rv770_resume(struct radeon_device *rdev) { int r; - if (radeon_gpu_reset(rdev)) { - /* FIXME: what do we want to do here ? */ - } + /* Do not reset GPU before posting, on rv770 hw unlike on r500 hw, + * posting will perform necessary task to bring back GPU into good + * shape. + */ /* post card */ - if (rdev->is_atom_bios) { - atom_asic_init(rdev->mode_info.atom_context); - } else { - radeon_combios_asic_init(rdev->ddev); - } + atom_asic_init(rdev->mode_info.atom_context); /* Initialize clocks */ r = radeon_clocks_init(rdev); if (r) { @@ -908,7 +923,7 @@ int rv770_resume(struct radeon_device *rdev) return r; } - r = radeon_ib_test(rdev); + r = r600_ib_test(rdev); if (r) { DRM_ERROR("radeon: failled testing IB (%d).\n", r); return r; @@ -922,8 +937,8 @@ int rv770_suspend(struct radeon_device *rdev) /* FIXME: we should wait for ring to be empty */ r700_cp_stop(rdev); rdev->cp.ready = false; + r600_wb_disable(rdev); rv770_pcie_gart_disable(rdev); - /* unpin shaders bo */ radeon_object_unpin(rdev->r600_blit.shader_obj); return 0; @@ -939,7 +954,6 @@ int rv770_init(struct radeon_device *rdev) { int r; - rdev->new_init_path = true; r = radeon_dummy_page_init(rdev); if (r) return r; @@ -953,8 +967,10 @@ int rv770_init(struct radeon_device *rdev) return -EINVAL; } /* Must be an ATOMBIOS */ - if (!rdev->is_atom_bios) + if (!rdev->is_atom_bios) { + dev_err(rdev->dev, "Expecting atombios for R600 GPU\n"); return -EINVAL; + } r = radeon_atombios_init(rdev); if (r) return r; @@ -967,24 +983,20 @@ int rv770_init(struct radeon_device *rdev) r600_scratch_init(rdev); /* Initialize surface registers */ radeon_surface_init(rdev); + /* Initialize clocks */ radeon_get_clock_info(rdev->ddev); r = radeon_clocks_init(rdev); if (r) return r; + /* Initialize power management */ + radeon_pm_init(rdev); /* Fence driver */ r = radeon_fence_driver_init(rdev); if (r) return r; r = rv770_mc_init(rdev); - if (r) { - if (rdev->flags & RADEON_IS_AGP) { - /* Retry with disabling AGP */ - rv770_fini(rdev); - rdev->flags &= ~RADEON_IS_AGP; - return rv770_init(rdev); - } + if (r) return r; - } /* Memory manager */ r = radeon_object_init(rdev); if (r) @@ -1013,12 +1025,10 @@ int rv770_init(struct radeon_device *rdev) r = rv770_startup(rdev); if (r) { - if (rdev->flags & RADEON_IS_AGP) { - /* Retry with disabling AGP */ - rv770_fini(rdev); - rdev->flags &= ~RADEON_IS_AGP; - return rv770_init(rdev); - } + rv770_suspend(rdev); + r600_wb_fini(rdev); + radeon_ring_fini(rdev); + rv770_pcie_gart_fini(rdev); rdev->accel_working = false; } if (rdev->accel_working) { @@ -1027,7 +1037,7 @@ int rv770_init(struct radeon_device *rdev) DRM_ERROR("radeon: failled initializing IB pool (%d).\n", r); rdev->accel_working = false; } - r = radeon_ib_test(rdev); + r = r600_ib_test(rdev); if (r) { DRM_ERROR("radeon: failled testing IB (%d).\n", r); rdev->accel_working = false; @@ -1042,20 +1052,15 @@ void rv770_fini(struct radeon_device *rdev) r600_blit_fini(rdev); radeon_ring_fini(rdev); + r600_wb_fini(rdev); rv770_pcie_gart_fini(rdev); radeon_gem_fini(rdev); radeon_fence_driver_fini(rdev); radeon_clocks_fini(rdev); -#if __OS_HAS_AGP if (rdev->flags & RADEON_IS_AGP) radeon_agp_fini(rdev); -#endif radeon_object_fini(rdev); - if (rdev->is_atom_bios) { - radeon_atombios_fini(rdev); - } else { - radeon_combios_fini(rdev); - } + radeon_atombios_fini(rdev); kfree(rdev->bios); rdev->bios = NULL; radeon_dummy_page_fini(rdev); diff --git a/drivers/gpu/drm/radeon/rv770d.h b/drivers/gpu/drm/radeon/rv770d.h index 4b9c3d6396ff..a1367ab6f261 100644 --- a/drivers/gpu/drm/radeon/rv770d.h +++ b/drivers/gpu/drm/radeon/rv770d.h @@ -129,6 +129,10 @@ #define HDP_REG_COHERENCY_FLUSH_CNTL 0x54A0 #define HDP_TILING_CONFIG 0x2F3C +#define MC_SHARED_CHMAP 0x2004 +#define NOOFCHAN_SHIFT 12 +#define NOOFCHAN_MASK 0x00003000 + #define MC_ARB_RAMCFG 0x2760 #define NOOFBANK_SHIFT 0 #define NOOFBANK_MASK 0x00000003 @@ -142,6 +146,7 @@ #define CHANSIZE_MASK 0x00000100 #define BURSTLENGTH_SHIFT 9 #define BURSTLENGTH_MASK 0x00000200 +#define CHANSIZE_OVERRIDE (1 << 11) #define MC_VM_AGP_TOP 0x2028 #define MC_VM_AGP_BOT 0x202C #define MC_VM_AGP_BASE 0x2030 diff --git a/drivers/gpu/drm/ttm/ttm_bo_vm.c b/drivers/gpu/drm/ttm/ttm_bo_vm.c index 33de7637c0c6..1c040d040338 100644 --- a/drivers/gpu/drm/ttm/ttm_bo_vm.c +++ b/drivers/gpu/drm/ttm/ttm_bo_vm.c @@ -228,7 +228,7 @@ static void ttm_bo_vm_close(struct vm_area_struct *vma) vma->vm_private_data = NULL; } -static struct vm_operations_struct ttm_bo_vm_ops = { +static const struct vm_operations_struct ttm_bo_vm_ops = { .fault = ttm_bo_vm_fault, .open = ttm_bo_vm_open, .close = ttm_bo_vm_close diff --git a/drivers/gpu/drm/ttm/ttm_global.c b/drivers/gpu/drm/ttm/ttm_global.c index 541744d00d3e..b17007178a36 100644 --- a/drivers/gpu/drm/ttm/ttm_global.c +++ b/drivers/gpu/drm/ttm/ttm_global.c @@ -82,8 +82,8 @@ int ttm_global_item_ref(struct ttm_global_reference *ref) if (unlikely(ret != 0)) goto out_err; - ++item->refcount; } + ++item->refcount; ref->object = item->object; object = item->object; mutex_unlock(&item->mutex); diff --git a/drivers/gpu/drm/ttm/ttm_tt.c b/drivers/gpu/drm/ttm/ttm_tt.c index a55ee1a56c16..7bcb89f39ce8 100644 --- a/drivers/gpu/drm/ttm/ttm_tt.c +++ b/drivers/gpu/drm/ttm/ttm_tt.c @@ -279,6 +279,7 @@ int ttm_tt_set_placement_caching(struct ttm_tt *ttm, uint32_t placement) return ttm_tt_set_caching(ttm, state); } +EXPORT_SYMBOL(ttm_tt_set_placement_caching); static void ttm_tt_free_alloced_pages(struct ttm_tt *ttm) { diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index be34d32906bd..7d05c4bb201e 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -1066,7 +1066,7 @@ EXPORT_SYMBOL_GPL(hid_report_raw_event); * @type: HID report type (HID_*_REPORT) * @data: report contents * @size: size of data parameter - * @interrupt: called from atomic? + * @interrupt: distinguish between interrupt and control transfers * * This is data entry for lower layers. */ diff --git a/drivers/hid/hid-twinhan.c b/drivers/hid/hid-twinhan.c index b05f602c051e..c40afc57fc8f 100644 --- a/drivers/hid/hid-twinhan.c +++ b/drivers/hid/hid-twinhan.c @@ -132,12 +132,12 @@ static struct hid_driver twinhan_driver = { .input_mapping = twinhan_input_mapping, }; -static int twinhan_init(void) +static int __init twinhan_init(void) { return hid_register_driver(&twinhan_driver); } -static void twinhan_exit(void) +static void __exit twinhan_exit(void) { hid_unregister_driver(&twinhan_driver); } diff --git a/drivers/hid/hidraw.c b/drivers/hid/hidraw.c index 0c6639ea03dd..cdd136942bca 100644 --- a/drivers/hid/hidraw.c +++ b/drivers/hid/hidraw.c @@ -30,6 +30,7 @@ #include <linux/major.h> #include <linux/hid.h> #include <linux/mutex.h> +#include <linux/sched.h> #include <linux/smp_lock.h> #include <linux/hidraw.h> @@ -47,10 +48,9 @@ static ssize_t hidraw_read(struct file *file, char __user *buffer, size_t count, char *report; DECLARE_WAITQUEUE(wait, current); - while (ret == 0) { - - mutex_lock(&list->read_mutex); + mutex_lock(&list->read_mutex); + while (ret == 0) { if (list->head == list->tail) { add_wait_queue(&list->hidraw->wait, &wait); set_current_state(TASK_INTERRUPTIBLE); diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index ed7711d11ae8..700e93adeb33 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -325,34 +325,6 @@ config SENSORS_F75375S This driver can also be built as a module. If so, the module will be called f75375s. -config SENSORS_FSCHER - tristate "FSC Hermes (DEPRECATED)" - depends on X86 && I2C - help - This driver is DEPRECATED please use the new merged fschmd - ("FSC Poseidon, Scylla, Hermes, Heimdall and Heracles") driver - instead. - - If you say yes here you get support for Fujitsu Siemens - Computers Hermes sensor chips. - - This driver can also be built as a module. If so, the module - will be called fscher. - -config SENSORS_FSCPOS - tristate "FSC Poseidon (DEPRECATED)" - depends on X86 && I2C - help - This driver is DEPRECATED please use the new merged fschmd - ("FSC Poseidon, Scylla, Hermes, Heimdall and Heracles") driver - instead. - - If you say yes here you get support for Fujitsu Siemens - Computers Poseidon sensor chips. - - This driver can also be built as a module. If so, the module - will be called fscpos. - config SENSORS_FSCHMD tristate "Fujitsu Siemens Computers sensor chips" depends on X86 && I2C @@ -401,12 +373,12 @@ config SENSORS_GL520SM will be called gl520sm. config SENSORS_CORETEMP - tristate "Intel Core (2) Duo/Solo temperature sensor" + tristate "Intel Core/Core2/Atom temperature sensor" depends on X86 && EXPERIMENTAL help If you say yes here you get support for the temperature - sensor inside your CPU. Supported all are all known variants - of Intel Core family. + sensor inside your CPU. Most of the family 6 CPUs + are supported. Check documentation/driver for details. config SENSORS_IBMAEM tristate "IBM Active Energy Manager temperature/power sensors and control" @@ -703,7 +675,7 @@ config SENSORS_SHT15 config SENSORS_S3C tristate "S3C24XX/S3C64XX Inbuilt ADC" - depends on ARCH_S3C2410 || ARCH_S3C64XX + depends on ARCH_S3C2410 help If you say yes here you get support for the on-board ADCs of the Samsung S3C24XX or S3C64XX series of SoC diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile index bcf73a9bb619..9f46cb019cc6 100644 --- a/drivers/hwmon/Makefile +++ b/drivers/hwmon/Makefile @@ -42,9 +42,7 @@ obj-$(CONFIG_SENSORS_DS1621) += ds1621.o obj-$(CONFIG_SENSORS_F71805F) += f71805f.o obj-$(CONFIG_SENSORS_F71882FG) += f71882fg.o obj-$(CONFIG_SENSORS_F75375S) += f75375s.o -obj-$(CONFIG_SENSORS_FSCHER) += fscher.o obj-$(CONFIG_SENSORS_FSCHMD) += fschmd.o -obj-$(CONFIG_SENSORS_FSCPOS) += fscpos.o obj-$(CONFIG_SENSORS_G760A) += g760a.o obj-$(CONFIG_SENSORS_GL518SM) += gl518sm.o obj-$(CONFIG_SENSORS_GL520SM) += gl520sm.o diff --git a/drivers/hwmon/adcxx.c b/drivers/hwmon/adcxx.c index 242294db3db6..5e9e095f1136 100644 --- a/drivers/hwmon/adcxx.c +++ b/drivers/hwmon/adcxx.c @@ -43,6 +43,7 @@ #include <linux/hwmon.h> #include <linux/hwmon-sysfs.h> #include <linux/mutex.h> +#include <linux/mod_devicetable.h> #include <linux/spi/spi.h> #define DRVNAME "adcxx" @@ -157,8 +158,9 @@ static struct sensor_device_attribute ad_input[] = { /*----------------------------------------------------------------------*/ -static int __devinit adcxx_probe(struct spi_device *spi, int channels) +static int __devinit adcxx_probe(struct spi_device *spi) { + int channels = spi_get_device_id(spi)->driver_data; struct adcxx *adc; int status; int i; @@ -204,26 +206,6 @@ out_err: return status; } -static int __devinit adcxx1s_probe(struct spi_device *spi) -{ - return adcxx_probe(spi, 1); -} - -static int __devinit adcxx2s_probe(struct spi_device *spi) -{ - return adcxx_probe(spi, 2); -} - -static int __devinit adcxx4s_probe(struct spi_device *spi) -{ - return adcxx_probe(spi, 4); -} - -static int __devinit adcxx8s_probe(struct spi_device *spi) -{ - return adcxx_probe(spi, 8); -} - static int __devexit adcxx_remove(struct spi_device *spi) { struct adcxx *adc = dev_get_drvdata(&spi->dev); @@ -241,79 +223,33 @@ static int __devexit adcxx_remove(struct spi_device *spi) return 0; } -static struct spi_driver adcxx1s_driver = { - .driver = { - .name = "adcxx1s", - .owner = THIS_MODULE, - }, - .probe = adcxx1s_probe, - .remove = __devexit_p(adcxx_remove), +static const struct spi_device_id adcxx_ids[] = { + { "adcxx1s", 1 }, + { "adcxx2s", 2 }, + { "adcxx4s", 4 }, + { "adcxx8s", 8 }, + { }, }; +MODULE_DEVICE_TABLE(spi, adcxx_ids); -static struct spi_driver adcxx2s_driver = { +static struct spi_driver adcxx_driver = { .driver = { - .name = "adcxx2s", + .name = "adcxx", .owner = THIS_MODULE, }, - .probe = adcxx2s_probe, - .remove = __devexit_p(adcxx_remove), -}; - -static struct spi_driver adcxx4s_driver = { - .driver = { - .name = "adcxx4s", - .owner = THIS_MODULE, - }, - .probe = adcxx4s_probe, - .remove = __devexit_p(adcxx_remove), -}; - -static struct spi_driver adcxx8s_driver = { - .driver = { - .name = "adcxx8s", - .owner = THIS_MODULE, - }, - .probe = adcxx8s_probe, + .id_table = adcxx_ids, + .probe = adcxx_probe, .remove = __devexit_p(adcxx_remove), }; static int __init init_adcxx(void) { - int status; - status = spi_register_driver(&adcxx1s_driver); - if (status) - goto reg_1_failed; - - status = spi_register_driver(&adcxx2s_driver); - if (status) - goto reg_2_failed; - - status = spi_register_driver(&adcxx4s_driver); - if (status) - goto reg_4_failed; - - status = spi_register_driver(&adcxx8s_driver); - if (status) - goto reg_8_failed; - - return status; - -reg_8_failed: - spi_unregister_driver(&adcxx4s_driver); -reg_4_failed: - spi_unregister_driver(&adcxx2s_driver); -reg_2_failed: - spi_unregister_driver(&adcxx1s_driver); -reg_1_failed: - return status; + return spi_register_driver(&adcxx_driver); } static void __exit exit_adcxx(void) { - spi_unregister_driver(&adcxx1s_driver); - spi_unregister_driver(&adcxx2s_driver); - spi_unregister_driver(&adcxx4s_driver); - spi_unregister_driver(&adcxx8s_driver); + spi_unregister_driver(&adcxx_driver); } module_init(init_adcxx); @@ -322,8 +258,3 @@ module_exit(exit_adcxx); MODULE_AUTHOR("Marc Pignat"); MODULE_DESCRIPTION("National Semiconductor adcxx8sxxx Linux driver"); MODULE_LICENSE("GPL"); - -MODULE_ALIAS("adcxx1s"); -MODULE_ALIAS("adcxx2s"); -MODULE_ALIAS("adcxx4s"); -MODULE_ALIAS("adcxx8s"); diff --git a/drivers/hwmon/adm1031.c b/drivers/hwmon/adm1031.c index 789441830cd8..56905955352c 100644 --- a/drivers/hwmon/adm1031.c +++ b/drivers/hwmon/adm1031.c @@ -37,6 +37,7 @@ #define ADM1031_REG_PWM (0x22) #define ADM1031_REG_FAN_MIN(nr) (0x10 + (nr)) +#define ADM1031_REG_TEMP_OFFSET(nr) (0x0d + (nr)) #define ADM1031_REG_TEMP_MAX(nr) (0x14 + 4 * (nr)) #define ADM1031_REG_TEMP_MIN(nr) (0x15 + 4 * (nr)) #define ADM1031_REG_TEMP_CRIT(nr) (0x16 + 4 * (nr)) @@ -93,6 +94,7 @@ struct adm1031_data { u8 auto_temp_min[3]; u8 auto_temp_off[3]; u8 auto_temp_max[3]; + s8 temp_offset[3]; s8 temp_min[3]; s8 temp_max[3]; s8 temp_crit[3]; @@ -145,6 +147,10 @@ adm1031_write_value(struct i2c_client *client, u8 reg, unsigned int value) #define TEMP_FROM_REG_EXT(val, ext) (TEMP_FROM_REG(val) + (ext) * 125) +#define TEMP_OFFSET_TO_REG(val) (TEMP_TO_REG(val) & 0x8f) +#define TEMP_OFFSET_FROM_REG(val) TEMP_FROM_REG((val) < 0 ? \ + (val) | 0x70 : (val)) + #define FAN_FROM_REG(reg, div) ((reg) ? (11250 * 60) / ((reg) * (div)) : 0) static int FAN_TO_REG(int reg, int div) @@ -585,6 +591,14 @@ static ssize_t show_temp(struct device *dev, (((data->ext_temp[nr] >> ((nr - 1) * 3)) & 7)); return sprintf(buf, "%d\n", TEMP_FROM_REG_EXT(data->temp[nr], ext)); } +static ssize_t show_temp_offset(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int nr = to_sensor_dev_attr(attr)->index; + struct adm1031_data *data = adm1031_update_device(dev); + return sprintf(buf, "%d\n", + TEMP_OFFSET_FROM_REG(data->temp_offset[nr])); +} static ssize_t show_temp_min(struct device *dev, struct device_attribute *attr, char *buf) { @@ -606,6 +620,24 @@ static ssize_t show_temp_crit(struct device *dev, struct adm1031_data *data = adm1031_update_device(dev); return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_crit[nr])); } +static ssize_t set_temp_offset(struct device *dev, + struct device_attribute *attr, const char *buf, + size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct adm1031_data *data = i2c_get_clientdata(client); + int nr = to_sensor_dev_attr(attr)->index; + int val; + + val = simple_strtol(buf, NULL, 10); + val = SENSORS_LIMIT(val, -15000, 15000); + mutex_lock(&data->update_lock); + data->temp_offset[nr] = TEMP_OFFSET_TO_REG(val); + adm1031_write_value(client, ADM1031_REG_TEMP_OFFSET(nr), + data->temp_offset[nr]); + mutex_unlock(&data->update_lock); + return count; +} static ssize_t set_temp_min(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { @@ -661,6 +693,8 @@ static ssize_t set_temp_crit(struct device *dev, struct device_attribute *attr, #define temp_reg(offset) \ static SENSOR_DEVICE_ATTR(temp##offset##_input, S_IRUGO, \ show_temp, NULL, offset - 1); \ +static SENSOR_DEVICE_ATTR(temp##offset##_offset, S_IRUGO | S_IWUSR, \ + show_temp_offset, set_temp_offset, offset - 1); \ static SENSOR_DEVICE_ATTR(temp##offset##_min, S_IRUGO | S_IWUSR, \ show_temp_min, set_temp_min, offset - 1); \ static SENSOR_DEVICE_ATTR(temp##offset##_max, S_IRUGO | S_IWUSR, \ @@ -714,6 +748,7 @@ static struct attribute *adm1031_attributes[] = { &sensor_dev_attr_pwm1.dev_attr.attr, &sensor_dev_attr_auto_fan1_channel.dev_attr.attr, &sensor_dev_attr_temp1_input.dev_attr.attr, + &sensor_dev_attr_temp1_offset.dev_attr.attr, &sensor_dev_attr_temp1_min.dev_attr.attr, &sensor_dev_attr_temp1_min_alarm.dev_attr.attr, &sensor_dev_attr_temp1_max.dev_attr.attr, @@ -721,6 +756,7 @@ static struct attribute *adm1031_attributes[] = { &sensor_dev_attr_temp1_crit.dev_attr.attr, &sensor_dev_attr_temp1_crit_alarm.dev_attr.attr, &sensor_dev_attr_temp2_input.dev_attr.attr, + &sensor_dev_attr_temp2_offset.dev_attr.attr, &sensor_dev_attr_temp2_min.dev_attr.attr, &sensor_dev_attr_temp2_min_alarm.dev_attr.attr, &sensor_dev_attr_temp2_max.dev_attr.attr, @@ -757,6 +793,7 @@ static struct attribute *adm1031_attributes_opt[] = { &sensor_dev_attr_pwm2.dev_attr.attr, &sensor_dev_attr_auto_fan2_channel.dev_attr.attr, &sensor_dev_attr_temp3_input.dev_attr.attr, + &sensor_dev_attr_temp3_offset.dev_attr.attr, &sensor_dev_attr_temp3_min.dev_attr.attr, &sensor_dev_attr_temp3_min_alarm.dev_attr.attr, &sensor_dev_attr_temp3_max.dev_attr.attr, @@ -937,6 +974,9 @@ static struct adm1031_data *adm1031_update_device(struct device *dev) } data->temp[chan] = newh; + data->temp_offset[chan] = + adm1031_read_value(client, + ADM1031_REG_TEMP_OFFSET(chan)); data->temp_min[chan] = adm1031_read_value(client, ADM1031_REG_TEMP_MIN(chan)); diff --git a/drivers/hwmon/adt7475.c b/drivers/hwmon/adt7475.c index d39877a7da63..b5a95193c694 100644 --- a/drivers/hwmon/adt7475.c +++ b/drivers/hwmon/adt7475.c @@ -350,8 +350,7 @@ static ssize_t show_temp(struct device *dev, struct device_attribute *attr, case FAULT: /* Note - only for remote1 and remote2 */ - out = data->alarms & (sattr->index ? 0x8000 : 0x4000); - out = out ? 0 : 1; + out = !!(data->alarms & (sattr->index ? 0x8000 : 0x4000)); break; default: @@ -863,7 +862,7 @@ static SENSOR_DEVICE_ATTR_2(pwm1_freq, S_IRUGO | S_IWUSR, show_pwmfreq, set_pwmfreq, INPUT, 0); static SENSOR_DEVICE_ATTR_2(pwm1_enable, S_IRUGO | S_IWUSR, show_pwmctrl, set_pwmctrl, INPUT, 0); -static SENSOR_DEVICE_ATTR_2(pwm1_auto_channel_temp, S_IRUGO | S_IWUSR, +static SENSOR_DEVICE_ATTR_2(pwm1_auto_channels_temp, S_IRUGO | S_IWUSR, show_pwmchan, set_pwmchan, INPUT, 0); static SENSOR_DEVICE_ATTR_2(pwm1_auto_point1_pwm, S_IRUGO | S_IWUSR, show_pwm, set_pwm, MIN, 0); @@ -875,7 +874,7 @@ static SENSOR_DEVICE_ATTR_2(pwm2_freq, S_IRUGO | S_IWUSR, show_pwmfreq, set_pwmfreq, INPUT, 1); static SENSOR_DEVICE_ATTR_2(pwm2_enable, S_IRUGO | S_IWUSR, show_pwmctrl, set_pwmctrl, INPUT, 1); -static SENSOR_DEVICE_ATTR_2(pwm2_auto_channel_temp, S_IRUGO | S_IWUSR, +static SENSOR_DEVICE_ATTR_2(pwm2_auto_channels_temp, S_IRUGO | S_IWUSR, show_pwmchan, set_pwmchan, INPUT, 1); static SENSOR_DEVICE_ATTR_2(pwm2_auto_point1_pwm, S_IRUGO | S_IWUSR, show_pwm, set_pwm, MIN, 1); @@ -887,7 +886,7 @@ static SENSOR_DEVICE_ATTR_2(pwm3_freq, S_IRUGO | S_IWUSR, show_pwmfreq, set_pwmfreq, INPUT, 2); static SENSOR_DEVICE_ATTR_2(pwm3_enable, S_IRUGO | S_IWUSR, show_pwmctrl, set_pwmctrl, INPUT, 2); -static SENSOR_DEVICE_ATTR_2(pwm3_auto_channel_temp, S_IRUGO | S_IWUSR, +static SENSOR_DEVICE_ATTR_2(pwm3_auto_channels_temp, S_IRUGO | S_IWUSR, show_pwmchan, set_pwmchan, INPUT, 2); static SENSOR_DEVICE_ATTR_2(pwm3_auto_point1_pwm, S_IRUGO | S_IWUSR, show_pwm, set_pwm, MIN, 2); @@ -947,19 +946,19 @@ static struct attribute *adt7475_attrs[] = { &sensor_dev_attr_pwm1.dev_attr.attr, &sensor_dev_attr_pwm1_freq.dev_attr.attr, &sensor_dev_attr_pwm1_enable.dev_attr.attr, - &sensor_dev_attr_pwm1_auto_channel_temp.dev_attr.attr, + &sensor_dev_attr_pwm1_auto_channels_temp.dev_attr.attr, &sensor_dev_attr_pwm1_auto_point1_pwm.dev_attr.attr, &sensor_dev_attr_pwm1_auto_point2_pwm.dev_attr.attr, &sensor_dev_attr_pwm2.dev_attr.attr, &sensor_dev_attr_pwm2_freq.dev_attr.attr, &sensor_dev_attr_pwm2_enable.dev_attr.attr, - &sensor_dev_attr_pwm2_auto_channel_temp.dev_attr.attr, + &sensor_dev_attr_pwm2_auto_channels_temp.dev_attr.attr, &sensor_dev_attr_pwm2_auto_point1_pwm.dev_attr.attr, &sensor_dev_attr_pwm2_auto_point2_pwm.dev_attr.attr, &sensor_dev_attr_pwm3.dev_attr.attr, &sensor_dev_attr_pwm3_freq.dev_attr.attr, &sensor_dev_attr_pwm3_enable.dev_attr.attr, - &sensor_dev_attr_pwm3_auto_channel_temp.dev_attr.attr, + &sensor_dev_attr_pwm3_auto_channels_temp.dev_attr.attr, &sensor_dev_attr_pwm3_auto_point1_pwm.dev_attr.attr, &sensor_dev_attr_pwm3_auto_point2_pwm.dev_attr.attr, NULL, @@ -1152,7 +1151,7 @@ static struct adt7475_data *adt7475_update_device(struct device *dev) } /* Limits and settings, should never change update every 60 seconds */ - if (time_after(jiffies, data->limits_updated + HZ * 2) || + if (time_after(jiffies, data->limits_updated + HZ * 60) || !data->valid) { data->config5 = adt7475_read(REG_CONFIG5); diff --git a/drivers/hwmon/asus_atk0110.c b/drivers/hwmon/asus_atk0110.c index fe4fa29c9219..5a3ee00c0e7d 100644 --- a/drivers/hwmon/asus_atk0110.c +++ b/drivers/hwmon/asus_atk0110.c @@ -35,18 +35,22 @@ #define METHOD_OLD_ENUM_FAN "FSIF" #define ATK_MUX_HWMON 0x00000006ULL +#define ATK_MUX_MGMT 0x00000011ULL #define ATK_CLASS_MASK 0xff000000ULL #define ATK_CLASS_FREQ_CTL 0x03000000ULL #define ATK_CLASS_FAN_CTL 0x04000000ULL #define ATK_CLASS_HWMON 0x06000000ULL +#define ATK_CLASS_MGMT 0x11000000ULL #define ATK_TYPE_MASK 0x00ff0000ULL #define HWMON_TYPE_VOLT 0x00020000ULL #define HWMON_TYPE_TEMP 0x00030000ULL #define HWMON_TYPE_FAN 0x00040000ULL -#define HWMON_SENSOR_ID_MASK 0x0000ffffULL +#define ATK_ELEMENT_ID_MASK 0x0000ffffULL + +#define ATK_EC_ID 0x11060004ULL enum atk_pack_member { HWMON_PACK_FLAGS, @@ -89,6 +93,9 @@ struct atk_data { /* new inteface */ acpi_handle enumerate_handle; acpi_handle read_handle; + acpi_handle write_handle; + + bool disable_ec; int voltage_count; int temperature_count; @@ -129,9 +136,22 @@ struct atk_sensor_data { char const *acpi_name; }; -struct atk_acpi_buffer_u64 { - union acpi_object buf; - u64 value; +/* Return buffer format: + * [0-3] "value" is valid flag + * [4-7] value + * [8- ] unknown stuff on newer mobos + */ +struct atk_acpi_ret_buffer { + u32 flags; + u32 value; + u8 data[]; +}; + +/* Input buffer used for GITM and SITM methods */ +struct atk_acpi_input_buf { + u32 id; + u32 param1; + u32 param2; }; static int atk_add(struct acpi_device *device); @@ -439,52 +459,147 @@ static int atk_read_value_old(struct atk_sensor_data *sensor, u64 *value) return 0; } -static int atk_read_value_new(struct atk_sensor_data *sensor, u64 *value) +static union acpi_object *atk_ggrp(struct atk_data *data, u16 mux) { - struct atk_data *data = sensor->data; struct device *dev = &data->acpi_dev->dev; + struct acpi_buffer buf; + acpi_status ret; struct acpi_object_list params; - struct acpi_buffer ret; union acpi_object id; - struct atk_acpi_buffer_u64 tmp; - acpi_status status; + union acpi_object *pack; id.type = ACPI_TYPE_INTEGER; - id.integer.value = sensor->id; - + id.integer.value = mux; params.count = 1; params.pointer = &id; - tmp.buf.type = ACPI_TYPE_BUFFER; - tmp.buf.buffer.pointer = (u8 *)&tmp.value; - tmp.buf.buffer.length = sizeof(u64); - ret.length = sizeof(tmp); - ret.pointer = &tmp; + buf.length = ACPI_ALLOCATE_BUFFER; + ret = acpi_evaluate_object(data->enumerate_handle, NULL, ¶ms, &buf); + if (ret != AE_OK) { + dev_err(dev, "GGRP[%#x] ACPI exception: %s\n", mux, + acpi_format_exception(ret)); + return ERR_PTR(-EIO); + } + pack = buf.pointer; + if (pack->type != ACPI_TYPE_PACKAGE) { + /* Execution was successful, but the id was not found */ + ACPI_FREE(pack); + return ERR_PTR(-ENOENT); + } + + if (pack->package.count < 1) { + dev_err(dev, "GGRP[%#x] package is too small\n", mux); + ACPI_FREE(pack); + return ERR_PTR(-EIO); + } + return pack; +} + +static union acpi_object *atk_gitm(struct atk_data *data, u64 id) +{ + struct device *dev = &data->acpi_dev->dev; + struct atk_acpi_input_buf buf; + union acpi_object tmp; + struct acpi_object_list params; + struct acpi_buffer ret; + union acpi_object *obj; + acpi_status status; + + buf.id = id; + buf.param1 = 0; + buf.param2 = 0; + tmp.type = ACPI_TYPE_BUFFER; + tmp.buffer.pointer = (u8 *)&buf; + tmp.buffer.length = sizeof(buf); + + params.count = 1; + params.pointer = (void *)&tmp; + + ret.length = ACPI_ALLOCATE_BUFFER; status = acpi_evaluate_object_typed(data->read_handle, NULL, ¶ms, &ret, ACPI_TYPE_BUFFER); if (status != AE_OK) { - dev_warn(dev, "%s: ACPI exception: %s\n", __func__, + dev_warn(dev, "GITM[%#llx] ACPI exception: %s\n", id, acpi_format_exception(status)); - return -EIO; + return ERR_PTR(-EIO); + } + obj = ret.pointer; + + /* Sanity check */ + if (obj->buffer.length < 8) { + dev_warn(dev, "Unexpected ASBF length: %u\n", + obj->buffer.length); + ACPI_FREE(obj); + return ERR_PTR(-EIO); } + return obj; +} - /* Return buffer format: - * [0-3] "value" is valid flag - * [4-7] value - */ - if (!(tmp.value & 0xffffffff)) { +static union acpi_object *atk_sitm(struct atk_data *data, + struct atk_acpi_input_buf *buf) +{ + struct device *dev = &data->acpi_dev->dev; + struct acpi_object_list params; + union acpi_object tmp; + struct acpi_buffer ret; + union acpi_object *obj; + acpi_status status; + + tmp.type = ACPI_TYPE_BUFFER; + tmp.buffer.pointer = (u8 *)buf; + tmp.buffer.length = sizeof(*buf); + + params.count = 1; + params.pointer = &tmp; + + ret.length = ACPI_ALLOCATE_BUFFER; + status = acpi_evaluate_object_typed(data->write_handle, NULL, ¶ms, + &ret, ACPI_TYPE_BUFFER); + if (status != AE_OK) { + dev_warn(dev, "SITM[%#x] ACPI exception: %s\n", buf->id, + acpi_format_exception(status)); + return ERR_PTR(-EIO); + } + obj = ret.pointer; + + /* Sanity check */ + if (obj->buffer.length < 8) { + dev_warn(dev, "Unexpected ASBF length: %u\n", + obj->buffer.length); + ACPI_FREE(obj); + return ERR_PTR(-EIO); + } + return obj; +} + +static int atk_read_value_new(struct atk_sensor_data *sensor, u64 *value) +{ + struct atk_data *data = sensor->data; + struct device *dev = &data->acpi_dev->dev; + union acpi_object *obj; + struct atk_acpi_ret_buffer *buf; + int err = 0; + + obj = atk_gitm(data, sensor->id); + if (IS_ERR(obj)) + return PTR_ERR(obj); + + buf = (struct atk_acpi_ret_buffer *)obj->buffer.pointer; + if (buf->flags == 0) { /* The reading is not valid, possible causes: * - sensor failure * - enumeration was FUBAR (and we didn't notice) */ - dev_info(dev, "Failure: %#llx\n", tmp.value); - return -EIO; + dev_warn(dev, "Read failed, sensor = %#llx\n", sensor->id); + err = -EIO; + goto out; } - *value = (tmp.value & 0xffffffff00000000ULL) >> 32; - - return 0; + *value = buf->value; +out: + ACPI_FREE(obj); + return err; } static int atk_read_value(struct atk_sensor_data *sensor, u64 *value) @@ -713,43 +828,141 @@ cleanup: return ret; } -static int atk_enumerate_new_hwmon(struct atk_data *data) +static int atk_ec_present(struct atk_data *data) { struct device *dev = &data->acpi_dev->dev; - struct acpi_buffer buf; - acpi_status ret; - struct acpi_object_list params; - union acpi_object id; union acpi_object *pack; - int err; + union acpi_object *ec; + int ret; int i; - dev_dbg(dev, "Enumerating hwmon sensors\n"); + pack = atk_ggrp(data, ATK_MUX_MGMT); + if (IS_ERR(pack)) { + if (PTR_ERR(pack) == -ENOENT) { + /* The MGMT class does not exists - that's ok */ + dev_dbg(dev, "Class %#llx not found\n", ATK_MUX_MGMT); + return 0; + } + return PTR_ERR(pack); + } - id.type = ACPI_TYPE_INTEGER; - id.integer.value = ATK_MUX_HWMON; - params.count = 1; - params.pointer = &id; + /* Search the EC */ + ec = NULL; + for (i = 0; i < pack->package.count; i++) { + union acpi_object *obj = &pack->package.elements[i]; + union acpi_object *id; - buf.length = ACPI_ALLOCATE_BUFFER; - ret = acpi_evaluate_object_typed(data->enumerate_handle, NULL, ¶ms, - &buf, ACPI_TYPE_PACKAGE); - if (ret != AE_OK) { - dev_warn(dev, METHOD_ENUMERATE ": ACPI exception: %s\n", - acpi_format_exception(ret)); - return -ENODEV; + if (obj->type != ACPI_TYPE_PACKAGE) + continue; + + id = &obj->package.elements[0]; + if (id->type != ACPI_TYPE_INTEGER) + continue; + + if (id->integer.value == ATK_EC_ID) { + ec = obj; + break; + } } - /* Result must be a package */ - pack = buf.pointer; + ret = (ec != NULL); + if (!ret) + /* The system has no EC */ + dev_dbg(dev, "EC not found\n"); - if (pack->package.count < 1) { - dev_dbg(dev, "%s: hwmon package is too small: %d\n", __func__, - pack->package.count); - err = -EINVAL; - goto out; + ACPI_FREE(pack); + return ret; +} + +static int atk_ec_enabled(struct atk_data *data) +{ + struct device *dev = &data->acpi_dev->dev; + union acpi_object *obj; + struct atk_acpi_ret_buffer *buf; + int err; + + obj = atk_gitm(data, ATK_EC_ID); + if (IS_ERR(obj)) { + dev_err(dev, "Unable to query EC status\n"); + return PTR_ERR(obj); + } + buf = (struct atk_acpi_ret_buffer *)obj->buffer.pointer; + + if (buf->flags == 0) { + dev_err(dev, "Unable to query EC status\n"); + err = -EIO; + } else { + err = (buf->value != 0); + dev_dbg(dev, "EC is %sabled\n", + err ? "en" : "dis"); + } + + ACPI_FREE(obj); + return err; +} + +static int atk_ec_ctl(struct atk_data *data, int enable) +{ + struct device *dev = &data->acpi_dev->dev; + union acpi_object *obj; + struct atk_acpi_input_buf sitm; + struct atk_acpi_ret_buffer *ec_ret; + int err = 0; + + sitm.id = ATK_EC_ID; + sitm.param1 = enable; + sitm.param2 = 0; + + obj = atk_sitm(data, &sitm); + if (IS_ERR(obj)) { + dev_err(dev, "Failed to %sable the EC\n", + enable ? "en" : "dis"); + return PTR_ERR(obj); + } + ec_ret = (struct atk_acpi_ret_buffer *)obj->buffer.pointer; + if (ec_ret->flags == 0) { + dev_err(dev, "Failed to %sable the EC\n", + enable ? "en" : "dis"); + err = -EIO; + } else { + dev_info(dev, "EC %sabled\n", + enable ? "en" : "dis"); + } + + ACPI_FREE(obj); + return err; +} + +static int atk_enumerate_new_hwmon(struct atk_data *data) +{ + struct device *dev = &data->acpi_dev->dev; + union acpi_object *pack; + int err; + int i; + + err = atk_ec_present(data); + if (err < 0) + return err; + if (err) { + err = atk_ec_enabled(data); + if (err < 0) + return err; + /* If the EC was disabled we will disable it again on unload */ + data->disable_ec = err; + + err = atk_ec_ctl(data, 1); + if (err) { + data->disable_ec = false; + return err; + } } + dev_dbg(dev, "Enumerating hwmon sensors\n"); + + pack = atk_ggrp(data, ATK_MUX_HWMON); + if (IS_ERR(pack)) + return PTR_ERR(pack); + for (i = 0; i < pack->package.count; i++) { union acpi_object *obj = &pack->package.elements[i]; @@ -758,8 +971,7 @@ static int atk_enumerate_new_hwmon(struct atk_data *data) err = data->voltage_count + data->temperature_count + data->fan_count; -out: - ACPI_FREE(buf.pointer); + ACPI_FREE(pack); return err; } @@ -895,6 +1107,15 @@ static int atk_check_new_if(struct atk_data *data) } data->read_handle = ret; + /* De-multiplexer (write) */ + status = acpi_get_handle(data->atk_handle, METHOD_WRITE, &ret); + if (status != AE_OK) { + dev_dbg(dev, "method " METHOD_READ " not found: %s\n", + acpi_format_exception(status)); + return -ENODEV; + } + data->write_handle = ret; + return 0; } @@ -915,6 +1136,7 @@ static int atk_add(struct acpi_device *device) data->acpi_dev = device; data->atk_handle = device->handle; INIT_LIST_HEAD(&data->sensor_list); + data->disable_ec = false; buf.length = ACPI_ALLOCATE_BUFFER; ret = acpi_evaluate_object_typed(data->atk_handle, BOARD_ID, NULL, @@ -973,6 +1195,8 @@ static int atk_add(struct acpi_device *device) cleanup: atk_free_sensors(data); out: + if (data->disable_ec) + atk_ec_ctl(data, 0); kfree(data); return err; } @@ -988,6 +1212,11 @@ static int atk_remove(struct acpi_device *device, int type) atk_free_sensors(data); hwmon_device_unregister(data->hwmon_dev); + if (data->disable_ec) { + if (atk_ec_ctl(data, 0)) + dev_err(&device->dev, "Failed to disable EC\n"); + } + kfree(data); return 0; diff --git a/drivers/hwmon/coretemp.c b/drivers/hwmon/coretemp.c index 972cf4ba963c..caef39cda8c8 100644 --- a/drivers/hwmon/coretemp.c +++ b/drivers/hwmon/coretemp.c @@ -157,17 +157,26 @@ static int __devinit adjust_tjmax(struct cpuinfo_x86 *c, u32 id, struct device * /* The 100C is default for both mobile and non mobile CPUs */ int tjmax = 100000; - int ismobile = 1; + int tjmax_ee = 85000; + int usemsr_ee = 1; int err; u32 eax, edx; /* Early chips have no MSR for TjMax */ if ((c->x86_model == 0xf) && (c->x86_mask < 4)) { - ismobile = 0; + usemsr_ee = 0; } - if ((c->x86_model > 0xe) && (ismobile)) { + /* Atoms seems to have TjMax at 90C */ + + if (c->x86_model == 0x1c) { + usemsr_ee = 0; + tjmax = 90000; + } + + if ((c->x86_model > 0xe) && (usemsr_ee)) { + u8 platform_id; /* Now we can detect the mobile CPU using Intel provided table http://softwarecommunity.intel.com/Wiki/Mobility/720.htm @@ -179,13 +188,29 @@ static int __devinit adjust_tjmax(struct cpuinfo_x86 *c, u32 id, struct device * dev_warn(dev, "Unable to access MSR 0x17, assuming desktop" " CPU\n"); - ismobile = 0; - } else if (!(eax & 0x10000000)) { - ismobile = 0; + usemsr_ee = 0; + } else if (c->x86_model < 0x17 && !(eax & 0x10000000)) { + /* Trust bit 28 up to Penryn, I could not find any + documentation on that; if you happen to know + someone at Intel please ask */ + usemsr_ee = 0; + } else { + /* Platform ID bits 52:50 (EDX starts at bit 32) */ + platform_id = (edx >> 18) & 0x7; + + /* Mobile Penryn CPU seems to be platform ID 7 or 5 + (guesswork) */ + if ((c->x86_model == 0x17) && + ((platform_id == 5) || (platform_id == 7))) { + /* If MSR EE bit is set, set it to 90 degrees C, + otherwise 105 degrees C */ + tjmax_ee = 90000; + tjmax = 105000; + } } } - if (ismobile || c->x86_model == 0x1c) { + if (usemsr_ee) { err = rdmsr_safe_on_cpu(id, 0xee, &eax, &edx); if (err) { @@ -193,9 +218,11 @@ static int __devinit adjust_tjmax(struct cpuinfo_x86 *c, u32 id, struct device * "Unable to access MSR 0xEE, for Tjmax, left" " at default"); } else if (eax & 0x40000000) { - tjmax = 85000; + tjmax = tjmax_ee; } - } else { + /* if we dont use msr EE it means we are desktop CPU (with exeception + of Atom) */ + } else if (tjmax == 100000) { dev_warn(dev, "Using relative temperature scale!\n"); } @@ -248,9 +275,9 @@ static int __devinit coretemp_probe(struct platform_device *pdev) platform_set_drvdata(pdev, data); /* read the still undocumented IA32_TEMPERATURE_TARGET it exists - on older CPUs but not in this register */ + on older CPUs but not in this register, Atoms don't have it either */ - if (c->x86_model > 0xe) { + if ((c->x86_model > 0xe) && (c->x86_model != 0x1c)) { err = rdmsr_safe_on_cpu(data->id, 0x1a2, &eax, &edx); if (err) { dev_warn(&pdev->dev, "Unable to read" @@ -413,11 +440,15 @@ static int __init coretemp_init(void) for_each_online_cpu(i) { struct cpuinfo_x86 *c = &cpu_data(i); - /* check if family 6, models 0xe, 0xf, 0x16, 0x17, 0x1A */ + /* check if family 6, models 0xe (Pentium M DC), + 0xf (Core 2 DC 65nm), 0x16 (Core 2 SC 65nm), + 0x17 (Penryn 45nm), 0x1a (Nehalem), 0x1c (Atom), + 0x1e (Lynnfield) */ if ((c->cpuid_level < 0) || (c->x86 != 0x6) || !((c->x86_model == 0xe) || (c->x86_model == 0xf) || (c->x86_model == 0x16) || (c->x86_model == 0x17) || - (c->x86_model == 0x1A) || (c->x86_model == 0x1c))) { + (c->x86_model == 0x1a) || (c->x86_model == 0x1c) || + (c->x86_model == 0x1e))) { /* supported CPU not found, but report the unknown family 6 CPU */ diff --git a/drivers/hwmon/dme1737.c b/drivers/hwmon/dme1737.c index 9814d51b3af4..27d62574284f 100644 --- a/drivers/hwmon/dme1737.c +++ b/drivers/hwmon/dme1737.c @@ -572,7 +572,7 @@ static struct dme1737_data *dme1737_update_device(struct device *dev) /* Sample register contents every 1 sec */ if (time_after(jiffies, data->last_update + HZ) || !data->valid) { - if (data->type != sch5027) { + if (data->type == dme1737) { data->vid = dme1737_read(data, DME1737_REG_VID) & 0x3f; } @@ -1134,7 +1134,7 @@ static ssize_t show_pwm(struct device *dev, struct device_attribute *attr, res = PWM_FREQ_FROM_REG(data->pwm_freq[ix]); break; case SYS_PWM_ENABLE: - if (ix > 3) { + if (ix >= 3) { res = 1; /* pwm[5-6] hard-wired to manual mode */ } else { res = PWM_EN_FROM_REG(data->pwm_config[ix]); @@ -1621,9 +1621,6 @@ static struct attribute *dme1737_misc_attr[] = { &sensor_dev_attr_zone1_auto_point1_temp_hyst.dev_attr.attr, &sensor_dev_attr_zone2_auto_point1_temp_hyst.dev_attr.attr, &sensor_dev_attr_zone3_auto_point1_temp_hyst.dev_attr.attr, - /* Misc */ - &dev_attr_vrm.attr, - &dev_attr_cpu0_vid.attr, NULL }; @@ -1631,6 +1628,18 @@ static const struct attribute_group dme1737_misc_group = { .attrs = dme1737_misc_attr, }; +/* The following struct holds VID-related attributes. Their creation + depends on the chip type which is determined during module load. */ +static struct attribute *dme1737_vid_attr[] = { + &dev_attr_vrm.attr, + &dev_attr_cpu0_vid.attr, + NULL +}; + +static const struct attribute_group dme1737_vid_group = { + .attrs = dme1737_vid_attr, +}; + /* The following structs hold the PWM attributes, some of which are optional. * Their creation depends on the chip configuration which is determined during * module load. */ @@ -1902,6 +1911,9 @@ static void dme1737_remove_files(struct device *dev) if (data->type != sch5027) { sysfs_remove_group(&dev->kobj, &dme1737_misc_group); } + if (data->type == dme1737) { + sysfs_remove_group(&dev->kobj, &dme1737_vid_group); + } sysfs_remove_group(&dev->kobj, &dme1737_group); @@ -1933,6 +1945,13 @@ static int dme1737_create_files(struct device *dev) goto exit_remove; } + /* Create VID-related sysfs attributes */ + if ((data->type == dme1737) && + (err = sysfs_create_group(&dev->kobj, + &dme1737_vid_group))) { + goto exit_remove; + } + /* Create fan sysfs attributes */ for (ix = 0; ix < ARRAY_SIZE(dme1737_fan_group); ix++) { if (data->has_fan & (1 << ix)) { @@ -2127,7 +2146,7 @@ static int dme1737_init_device(struct device *dev) data->pwm_acz[2] = 4; /* pwm3 -> zone3 */ /* Set VRM */ - if (data->type != sch5027) { + if (data->type == dme1737) { data->vrm = vid_which_vrm(); } diff --git a/drivers/hwmon/fscher.c b/drivers/hwmon/fscher.c deleted file mode 100644 index 12c70e402cb2..000000000000 --- a/drivers/hwmon/fscher.c +++ /dev/null @@ -1,680 +0,0 @@ -/* - * fscher.c - Part of lm_sensors, Linux kernel modules for hardware - * monitoring - * Copyright (C) 2003, 2004 Reinhard Nissl <rnissl@gmx.de> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -/* - * fujitsu siemens hermes chip, - * module based on fscpos.c - * Copyright (C) 2000 Hermann Jung <hej@odn.de> - * Copyright (C) 1998, 1999 Frodo Looijaard <frodol@dds.nl> - * and Philip Edelbrock <phil@netroedge.com> - */ - -#include <linux/module.h> -#include <linux/init.h> -#include <linux/slab.h> -#include <linux/jiffies.h> -#include <linux/i2c.h> -#include <linux/hwmon.h> -#include <linux/err.h> -#include <linux/mutex.h> -#include <linux/sysfs.h> - -/* - * Addresses to scan - */ - -static const unsigned short normal_i2c[] = { 0x73, I2C_CLIENT_END }; - -/* - * Insmod parameters - */ - -I2C_CLIENT_INSMOD_1(fscher); - -/* - * The FSCHER registers - */ - -/* chip identification */ -#define FSCHER_REG_IDENT_0 0x00 -#define FSCHER_REG_IDENT_1 0x01 -#define FSCHER_REG_IDENT_2 0x02 -#define FSCHER_REG_REVISION 0x03 - -/* global control and status */ -#define FSCHER_REG_EVENT_STATE 0x04 -#define FSCHER_REG_CONTROL 0x05 - -/* watchdog */ -#define FSCHER_REG_WDOG_PRESET 0x28 -#define FSCHER_REG_WDOG_STATE 0x23 -#define FSCHER_REG_WDOG_CONTROL 0x21 - -/* fan 0 */ -#define FSCHER_REG_FAN0_MIN 0x55 -#define FSCHER_REG_FAN0_ACT 0x0e -#define FSCHER_REG_FAN0_STATE 0x0d -#define FSCHER_REG_FAN0_RIPPLE 0x0f - -/* fan 1 */ -#define FSCHER_REG_FAN1_MIN 0x65 -#define FSCHER_REG_FAN1_ACT 0x6b -#define FSCHER_REG_FAN1_STATE 0x62 -#define FSCHER_REG_FAN1_RIPPLE 0x6f - -/* fan 2 */ -#define FSCHER_REG_FAN2_MIN 0xb5 -#define FSCHER_REG_FAN2_ACT 0xbb -#define FSCHER_REG_FAN2_STATE 0xb2 -#define FSCHER_REG_FAN2_RIPPLE 0xbf - -/* voltage supervision */ -#define FSCHER_REG_VOLT_12 0x45 -#define FSCHER_REG_VOLT_5 0x42 -#define FSCHER_REG_VOLT_BATT 0x48 - -/* temperature 0 */ -#define FSCHER_REG_TEMP0_ACT 0x64 -#define FSCHER_REG_TEMP0_STATE 0x71 - -/* temperature 1 */ -#define FSCHER_REG_TEMP1_ACT 0x32 -#define FSCHER_REG_TEMP1_STATE 0x81 - -/* temperature 2 */ -#define FSCHER_REG_TEMP2_ACT 0x35 -#define FSCHER_REG_TEMP2_STATE 0x91 - -/* - * Functions declaration - */ - -static int fscher_probe(struct i2c_client *client, - const struct i2c_device_id *id); -static int fscher_detect(struct i2c_client *client, int kind, - struct i2c_board_info *info); -static int fscher_remove(struct i2c_client *client); -static struct fscher_data *fscher_update_device(struct device *dev); -static void fscher_init_client(struct i2c_client *client); - -static int fscher_read_value(struct i2c_client *client, u8 reg); -static int fscher_write_value(struct i2c_client *client, u8 reg, u8 value); - -/* - * Driver data (common to all clients) - */ - -static const struct i2c_device_id fscher_id[] = { - { "fscher", fscher }, - { } -}; - -static struct i2c_driver fscher_driver = { - .class = I2C_CLASS_HWMON, - .driver = { - .name = "fscher", - }, - .probe = fscher_probe, - .remove = fscher_remove, - .id_table = fscher_id, - .detect = fscher_detect, - .address_data = &addr_data, -}; - -/* - * Client data (each client gets its own) - */ - -struct fscher_data { - struct device *hwmon_dev; - struct mutex update_lock; - char valid; /* zero until following fields are valid */ - unsigned long last_updated; /* in jiffies */ - - /* register values */ - u8 revision; /* revision of chip */ - u8 global_event; /* global event status */ - u8 global_control; /* global control register */ - u8 watchdog[3]; /* watchdog */ - u8 volt[3]; /* 12, 5, battery voltage */ - u8 temp_act[3]; /* temperature */ - u8 temp_status[3]; /* status of sensor */ - u8 fan_act[3]; /* fans revolutions per second */ - u8 fan_status[3]; /* fan status */ - u8 fan_min[3]; /* fan min value for rps */ - u8 fan_ripple[3]; /* divider for rps */ -}; - -/* - * Sysfs stuff - */ - -#define sysfs_r(kind, sub, offset, reg) \ -static ssize_t show_##kind##sub (struct fscher_data *, char *, int); \ -static ssize_t show_##kind##offset##sub (struct device *, struct device_attribute *attr, char *); \ -static ssize_t show_##kind##offset##sub (struct device *dev, struct device_attribute *attr, char *buf) \ -{ \ - struct fscher_data *data = fscher_update_device(dev); \ - return show_##kind##sub(data, buf, (offset)); \ -} - -#define sysfs_w(kind, sub, offset, reg) \ -static ssize_t set_##kind##sub (struct i2c_client *, struct fscher_data *, const char *, size_t, int, int); \ -static ssize_t set_##kind##offset##sub (struct device *, struct device_attribute *attr, const char *, size_t); \ -static ssize_t set_##kind##offset##sub (struct device *dev, struct device_attribute *attr, const char *buf, size_t count) \ -{ \ - struct i2c_client *client = to_i2c_client(dev); \ - struct fscher_data *data = i2c_get_clientdata(client); \ - return set_##kind##sub(client, data, buf, count, (offset), reg); \ -} - -#define sysfs_rw_n(kind, sub, offset, reg) \ -sysfs_r(kind, sub, offset, reg) \ -sysfs_w(kind, sub, offset, reg) \ -static DEVICE_ATTR(kind##offset##sub, S_IRUGO | S_IWUSR, show_##kind##offset##sub, set_##kind##offset##sub); - -#define sysfs_rw(kind, sub, reg) \ -sysfs_r(kind, sub, 0, reg) \ -sysfs_w(kind, sub, 0, reg) \ -static DEVICE_ATTR(kind##sub, S_IRUGO | S_IWUSR, show_##kind##0##sub, set_##kind##0##sub); - -#define sysfs_ro_n(kind, sub, offset, reg) \ -sysfs_r(kind, sub, offset, reg) \ -static DEVICE_ATTR(kind##offset##sub, S_IRUGO, show_##kind##offset##sub, NULL); - -#define sysfs_ro(kind, sub, reg) \ -sysfs_r(kind, sub, 0, reg) \ -static DEVICE_ATTR(kind, S_IRUGO, show_##kind##0##sub, NULL); - -#define sysfs_fan(offset, reg_status, reg_min, reg_ripple, reg_act) \ -sysfs_rw_n(pwm, , offset, reg_min) \ -sysfs_rw_n(fan, _status, offset, reg_status) \ -sysfs_rw_n(fan, _div , offset, reg_ripple) \ -sysfs_ro_n(fan, _input , offset, reg_act) - -#define sysfs_temp(offset, reg_status, reg_act) \ -sysfs_rw_n(temp, _status, offset, reg_status) \ -sysfs_ro_n(temp, _input , offset, reg_act) - -#define sysfs_in(offset, reg_act) \ -sysfs_ro_n(in, _input, offset, reg_act) - -#define sysfs_revision(reg_revision) \ -sysfs_ro(revision, , reg_revision) - -#define sysfs_alarms(reg_events) \ -sysfs_ro(alarms, , reg_events) - -#define sysfs_control(reg_control) \ -sysfs_rw(control, , reg_control) - -#define sysfs_watchdog(reg_control, reg_status, reg_preset) \ -sysfs_rw(watchdog, _control, reg_control) \ -sysfs_rw(watchdog, _status , reg_status) \ -sysfs_rw(watchdog, _preset , reg_preset) - -sysfs_fan(1, FSCHER_REG_FAN0_STATE, FSCHER_REG_FAN0_MIN, - FSCHER_REG_FAN0_RIPPLE, FSCHER_REG_FAN0_ACT) -sysfs_fan(2, FSCHER_REG_FAN1_STATE, FSCHER_REG_FAN1_MIN, - FSCHER_REG_FAN1_RIPPLE, FSCHER_REG_FAN1_ACT) -sysfs_fan(3, FSCHER_REG_FAN2_STATE, FSCHER_REG_FAN2_MIN, - FSCHER_REG_FAN2_RIPPLE, FSCHER_REG_FAN2_ACT) - -sysfs_temp(1, FSCHER_REG_TEMP0_STATE, FSCHER_REG_TEMP0_ACT) -sysfs_temp(2, FSCHER_REG_TEMP1_STATE, FSCHER_REG_TEMP1_ACT) -sysfs_temp(3, FSCHER_REG_TEMP2_STATE, FSCHER_REG_TEMP2_ACT) - -sysfs_in(0, FSCHER_REG_VOLT_12) -sysfs_in(1, FSCHER_REG_VOLT_5) -sysfs_in(2, FSCHER_REG_VOLT_BATT) - -sysfs_revision(FSCHER_REG_REVISION) -sysfs_alarms(FSCHER_REG_EVENTS) -sysfs_control(FSCHER_REG_CONTROL) -sysfs_watchdog(FSCHER_REG_WDOG_CONTROL, FSCHER_REG_WDOG_STATE, FSCHER_REG_WDOG_PRESET) - -static struct attribute *fscher_attributes[] = { - &dev_attr_revision.attr, - &dev_attr_alarms.attr, - &dev_attr_control.attr, - - &dev_attr_watchdog_status.attr, - &dev_attr_watchdog_control.attr, - &dev_attr_watchdog_preset.attr, - - &dev_attr_in0_input.attr, - &dev_attr_in1_input.attr, - &dev_attr_in2_input.attr, - - &dev_attr_fan1_status.attr, - &dev_attr_fan1_div.attr, - &dev_attr_fan1_input.attr, - &dev_attr_pwm1.attr, - &dev_attr_fan2_status.attr, - &dev_attr_fan2_div.attr, - &dev_attr_fan2_input.attr, - &dev_attr_pwm2.attr, - &dev_attr_fan3_status.attr, - &dev_attr_fan3_div.attr, - &dev_attr_fan3_input.attr, - &dev_attr_pwm3.attr, - - &dev_attr_temp1_status.attr, - &dev_attr_temp1_input.attr, - &dev_attr_temp2_status.attr, - &dev_attr_temp2_input.attr, - &dev_attr_temp3_status.attr, - &dev_attr_temp3_input.attr, - NULL -}; - -static const struct attribute_group fscher_group = { - .attrs = fscher_attributes, -}; - -/* - * Real code - */ - -/* Return 0 if detection is successful, -ENODEV otherwise */ -static int fscher_detect(struct i2c_client *new_client, int kind, - struct i2c_board_info *info) -{ - struct i2c_adapter *adapter = new_client->adapter; - - if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) - return -ENODEV; - - /* Do the remaining detection unless force or force_fscher parameter */ - if (kind < 0) { - if ((i2c_smbus_read_byte_data(new_client, - FSCHER_REG_IDENT_0) != 0x48) /* 'H' */ - || (i2c_smbus_read_byte_data(new_client, - FSCHER_REG_IDENT_1) != 0x45) /* 'E' */ - || (i2c_smbus_read_byte_data(new_client, - FSCHER_REG_IDENT_2) != 0x52)) /* 'R' */ - return -ENODEV; - } - - strlcpy(info->type, "fscher", I2C_NAME_SIZE); - - return 0; -} - -static int fscher_probe(struct i2c_client *new_client, - const struct i2c_device_id *id) -{ - struct fscher_data *data; - int err; - - data = kzalloc(sizeof(struct fscher_data), GFP_KERNEL); - if (!data) { - err = -ENOMEM; - goto exit; - } - - i2c_set_clientdata(new_client, data); - data->valid = 0; - mutex_init(&data->update_lock); - - fscher_init_client(new_client); - - /* Register sysfs hooks */ - if ((err = sysfs_create_group(&new_client->dev.kobj, &fscher_group))) - goto exit_free; - - data->hwmon_dev = hwmon_device_register(&new_client->dev); - if (IS_ERR(data->hwmon_dev)) { - err = PTR_ERR(data->hwmon_dev); - goto exit_remove_files; - } - - return 0; - -exit_remove_files: - sysfs_remove_group(&new_client->dev.kobj, &fscher_group); -exit_free: - kfree(data); -exit: - return err; -} - -static int fscher_remove(struct i2c_client *client) -{ - struct fscher_data *data = i2c_get_clientdata(client); - - hwmon_device_unregister(data->hwmon_dev); - sysfs_remove_group(&client->dev.kobj, &fscher_group); - - kfree(data); - return 0; -} - -static int fscher_read_value(struct i2c_client *client, u8 reg) -{ - dev_dbg(&client->dev, "read reg 0x%02x\n", reg); - - return i2c_smbus_read_byte_data(client, reg); -} - -static int fscher_write_value(struct i2c_client *client, u8 reg, u8 value) -{ - dev_dbg(&client->dev, "write reg 0x%02x, val 0x%02x\n", - reg, value); - - return i2c_smbus_write_byte_data(client, reg, value); -} - -/* Called when we have found a new FSC Hermes. */ -static void fscher_init_client(struct i2c_client *client) -{ - struct fscher_data *data = i2c_get_clientdata(client); - - /* Read revision from chip */ - data->revision = fscher_read_value(client, FSCHER_REG_REVISION); -} - -static struct fscher_data *fscher_update_device(struct device *dev) -{ - struct i2c_client *client = to_i2c_client(dev); - struct fscher_data *data = i2c_get_clientdata(client); - - mutex_lock(&data->update_lock); - - if (time_after(jiffies, data->last_updated + 2 * HZ) || !data->valid) { - - dev_dbg(&client->dev, "Starting fscher update\n"); - - data->temp_act[0] = fscher_read_value(client, FSCHER_REG_TEMP0_ACT); - data->temp_act[1] = fscher_read_value(client, FSCHER_REG_TEMP1_ACT); - data->temp_act[2] = fscher_read_value(client, FSCHER_REG_TEMP2_ACT); - data->temp_status[0] = fscher_read_value(client, FSCHER_REG_TEMP0_STATE); - data->temp_status[1] = fscher_read_value(client, FSCHER_REG_TEMP1_STATE); - data->temp_status[2] = fscher_read_value(client, FSCHER_REG_TEMP2_STATE); - - data->volt[0] = fscher_read_value(client, FSCHER_REG_VOLT_12); - data->volt[1] = fscher_read_value(client, FSCHER_REG_VOLT_5); - data->volt[2] = fscher_read_value(client, FSCHER_REG_VOLT_BATT); - - data->fan_act[0] = fscher_read_value(client, FSCHER_REG_FAN0_ACT); - data->fan_act[1] = fscher_read_value(client, FSCHER_REG_FAN1_ACT); - data->fan_act[2] = fscher_read_value(client, FSCHER_REG_FAN2_ACT); - data->fan_status[0] = fscher_read_value(client, FSCHER_REG_FAN0_STATE); - data->fan_status[1] = fscher_read_value(client, FSCHER_REG_FAN1_STATE); - data->fan_status[2] = fscher_read_value(client, FSCHER_REG_FAN2_STATE); - data->fan_min[0] = fscher_read_value(client, FSCHER_REG_FAN0_MIN); - data->fan_min[1] = fscher_read_value(client, FSCHER_REG_FAN1_MIN); - data->fan_min[2] = fscher_read_value(client, FSCHER_REG_FAN2_MIN); - data->fan_ripple[0] = fscher_read_value(client, FSCHER_REG_FAN0_RIPPLE); - data->fan_ripple[1] = fscher_read_value(client, FSCHER_REG_FAN1_RIPPLE); - data->fan_ripple[2] = fscher_read_value(client, FSCHER_REG_FAN2_RIPPLE); - - data->watchdog[0] = fscher_read_value(client, FSCHER_REG_WDOG_PRESET); - data->watchdog[1] = fscher_read_value(client, FSCHER_REG_WDOG_STATE); - data->watchdog[2] = fscher_read_value(client, FSCHER_REG_WDOG_CONTROL); - - data->global_event = fscher_read_value(client, FSCHER_REG_EVENT_STATE); - data->global_control = fscher_read_value(client, - FSCHER_REG_CONTROL); - - data->last_updated = jiffies; - data->valid = 1; - } - - mutex_unlock(&data->update_lock); - - return data; -} - - - -#define FAN_INDEX_FROM_NUM(nr) ((nr) - 1) - -static ssize_t set_fan_status(struct i2c_client *client, struct fscher_data *data, - const char *buf, size_t count, int nr, int reg) -{ - /* bits 0..1, 3..7 reserved => mask with 0x04 */ - unsigned long v = simple_strtoul(buf, NULL, 10) & 0x04; - - mutex_lock(&data->update_lock); - data->fan_status[FAN_INDEX_FROM_NUM(nr)] &= ~v; - fscher_write_value(client, reg, v); - mutex_unlock(&data->update_lock); - return count; -} - -static ssize_t show_fan_status(struct fscher_data *data, char *buf, int nr) -{ - /* bits 0..1, 3..7 reserved => mask with 0x04 */ - return sprintf(buf, "%u\n", data->fan_status[FAN_INDEX_FROM_NUM(nr)] & 0x04); -} - -static ssize_t set_pwm(struct i2c_client *client, struct fscher_data *data, - const char *buf, size_t count, int nr, int reg) -{ - unsigned long v = simple_strtoul(buf, NULL, 10); - - mutex_lock(&data->update_lock); - data->fan_min[FAN_INDEX_FROM_NUM(nr)] = v > 0xff ? 0xff : v; - fscher_write_value(client, reg, data->fan_min[FAN_INDEX_FROM_NUM(nr)]); - mutex_unlock(&data->update_lock); - return count; -} - -static ssize_t show_pwm(struct fscher_data *data, char *buf, int nr) -{ - return sprintf(buf, "%u\n", data->fan_min[FAN_INDEX_FROM_NUM(nr)]); -} - -static ssize_t set_fan_div(struct i2c_client *client, struct fscher_data *data, - const char *buf, size_t count, int nr, int reg) -{ - /* supported values: 2, 4, 8 */ - unsigned long v = simple_strtoul(buf, NULL, 10); - - switch (v) { - case 2: v = 1; break; - case 4: v = 2; break; - case 8: v = 3; break; - default: - dev_err(&client->dev, "fan_div value %ld not " - "supported. Choose one of 2, 4 or 8!\n", v); - return -EINVAL; - } - - mutex_lock(&data->update_lock); - - /* bits 2..7 reserved => mask with 0x03 */ - data->fan_ripple[FAN_INDEX_FROM_NUM(nr)] &= ~0x03; - data->fan_ripple[FAN_INDEX_FROM_NUM(nr)] |= v; - - fscher_write_value(client, reg, data->fan_ripple[FAN_INDEX_FROM_NUM(nr)]); - mutex_unlock(&data->update_lock); - return count; -} - -static ssize_t show_fan_div(struct fscher_data *data, char *buf, int nr) -{ - /* bits 2..7 reserved => mask with 0x03 */ - return sprintf(buf, "%u\n", 1 << (data->fan_ripple[FAN_INDEX_FROM_NUM(nr)] & 0x03)); -} - -#define RPM_FROM_REG(val) (val*60) - -static ssize_t show_fan_input (struct fscher_data *data, char *buf, int nr) -{ - return sprintf(buf, "%u\n", RPM_FROM_REG(data->fan_act[FAN_INDEX_FROM_NUM(nr)])); -} - - - -#define TEMP_INDEX_FROM_NUM(nr) ((nr) - 1) - -static ssize_t set_temp_status(struct i2c_client *client, struct fscher_data *data, - const char *buf, size_t count, int nr, int reg) -{ - /* bits 2..7 reserved, 0 read only => mask with 0x02 */ - unsigned long v = simple_strtoul(buf, NULL, 10) & 0x02; - - mutex_lock(&data->update_lock); - data->temp_status[TEMP_INDEX_FROM_NUM(nr)] &= ~v; - fscher_write_value(client, reg, v); - mutex_unlock(&data->update_lock); - return count; -} - -static ssize_t show_temp_status(struct fscher_data *data, char *buf, int nr) -{ - /* bits 2..7 reserved => mask with 0x03 */ - return sprintf(buf, "%u\n", data->temp_status[TEMP_INDEX_FROM_NUM(nr)] & 0x03); -} - -#define TEMP_FROM_REG(val) (((val) - 128) * 1000) - -static ssize_t show_temp_input(struct fscher_data *data, char *buf, int nr) -{ - return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_act[TEMP_INDEX_FROM_NUM(nr)])); -} - -/* - * The final conversion is specified in sensors.conf, as it depends on - * mainboard specific values. We export the registers contents as - * pseudo-hundredths-of-Volts (range 0V - 2.55V). Not that it makes much - * sense per se, but it minimizes the conversions count and keeps the - * values within a usual range. - */ -#define VOLT_FROM_REG(val) ((val) * 10) - -static ssize_t show_in_input(struct fscher_data *data, char *buf, int nr) -{ - return sprintf(buf, "%u\n", VOLT_FROM_REG(data->volt[nr])); -} - - - -static ssize_t show_revision(struct fscher_data *data, char *buf, int nr) -{ - return sprintf(buf, "%u\n", data->revision); -} - - - -static ssize_t show_alarms(struct fscher_data *data, char *buf, int nr) -{ - /* bits 2, 5..6 reserved => mask with 0x9b */ - return sprintf(buf, "%u\n", data->global_event & 0x9b); -} - - - -static ssize_t set_control(struct i2c_client *client, struct fscher_data *data, - const char *buf, size_t count, int nr, int reg) -{ - /* bits 1..7 reserved => mask with 0x01 */ - unsigned long v = simple_strtoul(buf, NULL, 10) & 0x01; - - mutex_lock(&data->update_lock); - data->global_control = v; - fscher_write_value(client, reg, v); - mutex_unlock(&data->update_lock); - return count; -} - -static ssize_t show_control(struct fscher_data *data, char *buf, int nr) -{ - /* bits 1..7 reserved => mask with 0x01 */ - return sprintf(buf, "%u\n", data->global_control & 0x01); -} - - - -static ssize_t set_watchdog_control(struct i2c_client *client, struct - fscher_data *data, const char *buf, size_t count, - int nr, int reg) -{ - /* bits 0..3 reserved => mask with 0xf0 */ - unsigned long v = simple_strtoul(buf, NULL, 10) & 0xf0; - - mutex_lock(&data->update_lock); - data->watchdog[2] &= ~0xf0; - data->watchdog[2] |= v; - fscher_write_value(client, reg, data->watchdog[2]); - mutex_unlock(&data->update_lock); - return count; -} - -static ssize_t show_watchdog_control(struct fscher_data *data, char *buf, int nr) -{ - /* bits 0..3 reserved, bit 5 write only => mask with 0xd0 */ - return sprintf(buf, "%u\n", data->watchdog[2] & 0xd0); -} - -static ssize_t set_watchdog_status(struct i2c_client *client, struct fscher_data *data, - const char *buf, size_t count, int nr, int reg) -{ - /* bits 0, 2..7 reserved => mask with 0x02 */ - unsigned long v = simple_strtoul(buf, NULL, 10) & 0x02; - - mutex_lock(&data->update_lock); - data->watchdog[1] &= ~v; - fscher_write_value(client, reg, v); - mutex_unlock(&data->update_lock); - return count; -} - -static ssize_t show_watchdog_status(struct fscher_data *data, char *buf, int nr) -{ - /* bits 0, 2..7 reserved => mask with 0x02 */ - return sprintf(buf, "%u\n", data->watchdog[1] & 0x02); -} - -static ssize_t set_watchdog_preset(struct i2c_client *client, struct fscher_data *data, - const char *buf, size_t count, int nr, int reg) -{ - unsigned long v = simple_strtoul(buf, NULL, 10) & 0xff; - - mutex_lock(&data->update_lock); - data->watchdog[0] = v; - fscher_write_value(client, reg, data->watchdog[0]); - mutex_unlock(&data->update_lock); - return count; -} - -static ssize_t show_watchdog_preset(struct fscher_data *data, char *buf, int nr) -{ - return sprintf(buf, "%u\n", data->watchdog[0]); -} - -static int __init sensors_fscher_init(void) -{ - return i2c_add_driver(&fscher_driver); -} - -static void __exit sensors_fscher_exit(void) -{ - i2c_del_driver(&fscher_driver); -} - -MODULE_AUTHOR("Reinhard Nissl <rnissl@gmx.de>"); -MODULE_DESCRIPTION("FSC Hermes driver"); -MODULE_LICENSE("GPL"); - -module_init(sensors_fscher_init); -module_exit(sensors_fscher_exit); diff --git a/drivers/hwmon/fschmd.c b/drivers/hwmon/fschmd.c index ea955edde87e..da1b1f9488af 100644 --- a/drivers/hwmon/fschmd.c +++ b/drivers/hwmon/fschmd.c @@ -819,7 +819,7 @@ static int watchdog_release(struct inode *inode, struct file *filp) static ssize_t watchdog_write(struct file *filp, const char __user *buf, size_t count, loff_t *offset) { - size_t ret; + int ret; struct fschmd_data *data = filp->private_data; if (count) { @@ -915,7 +915,7 @@ static int watchdog_ioctl(struct inode *inode, struct file *filp, return ret; } -static struct file_operations watchdog_fops = { +static const struct file_operations watchdog_fops = { .owner = THIS_MODULE, .llseek = no_llseek, .open = watchdog_open, diff --git a/drivers/hwmon/fscpos.c b/drivers/hwmon/fscpos.c deleted file mode 100644 index 8a7bcf500b4e..000000000000 --- a/drivers/hwmon/fscpos.c +++ /dev/null @@ -1,654 +0,0 @@ -/* - fscpos.c - Kernel module for hardware monitoring with FSC Poseidon chips - Copyright (C) 2004, 2005 Stefan Ott <stefan@desire.ch> - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -*/ - -/* - fujitsu siemens poseidon chip, - module based on the old fscpos module by Hermann Jung <hej@odn.de> and - the fscher module by Reinhard Nissl <rnissl@gmx.de> - - original module based on lm80.c - Copyright (C) 1998, 1999 Frodo Looijaard <frodol@dds.nl> - and Philip Edelbrock <phil@netroedge.com> - - Thanks to Jean Delvare for reviewing my code and suggesting a lot of - improvements. -*/ - -#include <linux/module.h> -#include <linux/slab.h> -#include <linux/jiffies.h> -#include <linux/i2c.h> -#include <linux/init.h> -#include <linux/hwmon.h> -#include <linux/err.h> -#include <linux/mutex.h> -#include <linux/sysfs.h> - -/* - * Addresses to scan - */ -static const unsigned short normal_i2c[] = { 0x73, I2C_CLIENT_END }; - -/* - * Insmod parameters - */ -I2C_CLIENT_INSMOD_1(fscpos); - -/* - * The FSCPOS registers - */ - -/* chip identification */ -#define FSCPOS_REG_IDENT_0 0x00 -#define FSCPOS_REG_IDENT_1 0x01 -#define FSCPOS_REG_IDENT_2 0x02 -#define FSCPOS_REG_REVISION 0x03 - -/* global control and status */ -#define FSCPOS_REG_EVENT_STATE 0x04 -#define FSCPOS_REG_CONTROL 0x05 - -/* watchdog */ -#define FSCPOS_REG_WDOG_PRESET 0x28 -#define FSCPOS_REG_WDOG_STATE 0x23 -#define FSCPOS_REG_WDOG_CONTROL 0x21 - -/* voltages */ -#define FSCPOS_REG_VOLT_12 0x45 -#define FSCPOS_REG_VOLT_5 0x42 -#define FSCPOS_REG_VOLT_BATT 0x48 - -/* fans - the chip does not support minimum speed for fan2 */ -static u8 FSCPOS_REG_PWM[] = { 0x55, 0x65 }; -static u8 FSCPOS_REG_FAN_ACT[] = { 0x0e, 0x6b, 0xab }; -static u8 FSCPOS_REG_FAN_STATE[] = { 0x0d, 0x62, 0xa2 }; -static u8 FSCPOS_REG_FAN_RIPPLE[] = { 0x0f, 0x6f, 0xaf }; - -/* temperatures */ -static u8 FSCPOS_REG_TEMP_ACT[] = { 0x64, 0x32, 0x35 }; -static u8 FSCPOS_REG_TEMP_STATE[] = { 0x71, 0x81, 0x91 }; - -/* - * Functions declaration - */ -static int fscpos_probe(struct i2c_client *client, - const struct i2c_device_id *id); -static int fscpos_detect(struct i2c_client *client, int kind, - struct i2c_board_info *info); -static int fscpos_remove(struct i2c_client *client); - -static int fscpos_read_value(struct i2c_client *client, u8 reg); -static int fscpos_write_value(struct i2c_client *client, u8 reg, u8 value); -static struct fscpos_data *fscpos_update_device(struct device *dev); -static void fscpos_init_client(struct i2c_client *client); - -static void reset_fan_alarm(struct i2c_client *client, int nr); - -/* - * Driver data (common to all clients) - */ -static const struct i2c_device_id fscpos_id[] = { - { "fscpos", fscpos }, - { } -}; - -static struct i2c_driver fscpos_driver = { - .class = I2C_CLASS_HWMON, - .driver = { - .name = "fscpos", - }, - .probe = fscpos_probe, - .remove = fscpos_remove, - .id_table = fscpos_id, - .detect = fscpos_detect, - .address_data = &addr_data, -}; - -/* - * Client data (each client gets its own) - */ -struct fscpos_data { - struct device *hwmon_dev; - struct mutex update_lock; - char valid; /* 0 until following fields are valid */ - unsigned long last_updated; /* In jiffies */ - - /* register values */ - u8 revision; /* revision of chip */ - u8 global_event; /* global event status */ - u8 global_control; /* global control register */ - u8 wdog_control; /* watchdog control */ - u8 wdog_state; /* watchdog status */ - u8 wdog_preset; /* watchdog preset */ - u8 volt[3]; /* 12, 5, battery current */ - u8 temp_act[3]; /* temperature */ - u8 temp_status[3]; /* status of sensor */ - u8 fan_act[3]; /* fans revolutions per second */ - u8 fan_status[3]; /* fan status */ - u8 pwm[2]; /* fan min value for rps */ - u8 fan_ripple[3]; /* divider for rps */ -}; - -/* Temperature */ -#define TEMP_FROM_REG(val) (((val) - 128) * 1000) - -static ssize_t show_temp_input(struct fscpos_data *data, char *buf, int nr) -{ - return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_act[nr - 1])); -} - -static ssize_t show_temp_status(struct fscpos_data *data, char *buf, int nr) -{ - /* bits 2..7 reserved => mask with 0x03 */ - return sprintf(buf, "%u\n", data->temp_status[nr - 1] & 0x03); -} - -static ssize_t show_temp_reset(struct fscpos_data *data, char *buf, int nr) -{ - return sprintf(buf, "1\n"); -} - -static ssize_t set_temp_reset(struct i2c_client *client, struct fscpos_data - *data, const char *buf, size_t count, int nr, int reg) -{ - unsigned long v = simple_strtoul(buf, NULL, 10); - if (v != 1) { - dev_err(&client->dev, "temp_reset value %ld not supported. " - "Use 1 to reset the alarm!\n", v); - return -EINVAL; - } - - dev_info(&client->dev, "You used the temp_reset feature which has not " - "been proplerly tested. Please report your " - "experience to the module author.\n"); - - /* Supported value: 2 (clears the status) */ - fscpos_write_value(client, FSCPOS_REG_TEMP_STATE[nr - 1], 2); - return count; -} - -/* Fans */ -#define RPM_FROM_REG(val) ((val) * 60) - -static ssize_t show_fan_status(struct fscpos_data *data, char *buf, int nr) -{ - /* bits 0..1, 3..7 reserved => mask with 0x04 */ - return sprintf(buf, "%u\n", data->fan_status[nr - 1] & 0x04); -} - -static ssize_t show_fan_input(struct fscpos_data *data, char *buf, int nr) -{ - return sprintf(buf, "%u\n", RPM_FROM_REG(data->fan_act[nr - 1])); -} - -static ssize_t show_fan_ripple(struct fscpos_data *data, char *buf, int nr) -{ - /* bits 2..7 reserved => mask with 0x03 */ - return sprintf(buf, "%u\n", data->fan_ripple[nr - 1] & 0x03); -} - -static ssize_t set_fan_ripple(struct i2c_client *client, struct fscpos_data - *data, const char *buf, size_t count, int nr, int reg) -{ - /* supported values: 2, 4, 8 */ - unsigned long v = simple_strtoul(buf, NULL, 10); - - switch (v) { - case 2: v = 1; break; - case 4: v = 2; break; - case 8: v = 3; break; - default: - dev_err(&client->dev, "fan_ripple value %ld not supported. " - "Must be one of 2, 4 or 8!\n", v); - return -EINVAL; - } - - mutex_lock(&data->update_lock); - /* bits 2..7 reserved => mask with 0x03 */ - data->fan_ripple[nr - 1] &= ~0x03; - data->fan_ripple[nr - 1] |= v; - - fscpos_write_value(client, reg, data->fan_ripple[nr - 1]); - mutex_unlock(&data->update_lock); - return count; -} - -static ssize_t show_pwm(struct fscpos_data *data, char *buf, int nr) -{ - return sprintf(buf, "%u\n", data->pwm[nr - 1]); -} - -static ssize_t set_pwm(struct i2c_client *client, struct fscpos_data *data, - const char *buf, size_t count, int nr, int reg) -{ - unsigned long v = simple_strtoul(buf, NULL, 10); - - /* Range: 0..255 */ - if (v < 0) v = 0; - if (v > 255) v = 255; - - mutex_lock(&data->update_lock); - data->pwm[nr - 1] = v; - fscpos_write_value(client, reg, data->pwm[nr - 1]); - mutex_unlock(&data->update_lock); - return count; -} - -static void reset_fan_alarm(struct i2c_client *client, int nr) -{ - fscpos_write_value(client, FSCPOS_REG_FAN_STATE[nr], 4); -} - -/* Volts */ -#define VOLT_FROM_REG(val, mult) ((val) * (mult) / 255) - -static ssize_t show_volt_12(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct fscpos_data *data = fscpos_update_device(dev); - return sprintf(buf, "%u\n", VOLT_FROM_REG(data->volt[0], 14200)); -} - -static ssize_t show_volt_5(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct fscpos_data *data = fscpos_update_device(dev); - return sprintf(buf, "%u\n", VOLT_FROM_REG(data->volt[1], 6600)); -} - -static ssize_t show_volt_batt(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct fscpos_data *data = fscpos_update_device(dev); - return sprintf(buf, "%u\n", VOLT_FROM_REG(data->volt[2], 3300)); -} - -/* Watchdog */ -static ssize_t show_wdog_control(struct fscpos_data *data, char *buf) -{ - /* bits 0..3 reserved, bit 6 write only => mask with 0xb0 */ - return sprintf(buf, "%u\n", data->wdog_control & 0xb0); -} - -static ssize_t set_wdog_control(struct i2c_client *client, struct fscpos_data - *data, const char *buf, size_t count, int reg) -{ - /* bits 0..3 reserved => mask with 0xf0 */ - unsigned long v = simple_strtoul(buf, NULL, 10) & 0xf0; - - mutex_lock(&data->update_lock); - data->wdog_control &= ~0xf0; - data->wdog_control |= v; - fscpos_write_value(client, reg, data->wdog_control); - mutex_unlock(&data->update_lock); - return count; -} - -static ssize_t show_wdog_state(struct fscpos_data *data, char *buf) -{ - /* bits 0, 2..7 reserved => mask with 0x02 */ - return sprintf(buf, "%u\n", data->wdog_state & 0x02); -} - -static ssize_t set_wdog_state(struct i2c_client *client, struct fscpos_data - *data, const char *buf, size_t count, int reg) -{ - unsigned long v = simple_strtoul(buf, NULL, 10) & 0x02; - - /* Valid values: 2 (clear) */ - if (v != 2) { - dev_err(&client->dev, "wdog_state value %ld not supported. " - "Must be 2 to clear the state!\n", v); - return -EINVAL; - } - - mutex_lock(&data->update_lock); - data->wdog_state &= ~v; - fscpos_write_value(client, reg, v); - mutex_unlock(&data->update_lock); - return count; -} - -static ssize_t show_wdog_preset(struct fscpos_data *data, char *buf) -{ - return sprintf(buf, "%u\n", data->wdog_preset); -} - -static ssize_t set_wdog_preset(struct i2c_client *client, struct fscpos_data - *data, const char *buf, size_t count, int reg) -{ - unsigned long v = simple_strtoul(buf, NULL, 10) & 0xff; - - mutex_lock(&data->update_lock); - data->wdog_preset = v; - fscpos_write_value(client, reg, data->wdog_preset); - mutex_unlock(&data->update_lock); - return count; -} - -/* Event */ -static ssize_t show_event(struct device *dev, struct device_attribute *attr, char *buf) -{ - /* bits 5..7 reserved => mask with 0x1f */ - struct fscpos_data *data = fscpos_update_device(dev); - return sprintf(buf, "%u\n", data->global_event & 0x9b); -} - -/* - * Sysfs stuff - */ -#define create_getter(kind, sub) \ - static ssize_t sysfs_show_##kind##sub(struct device *dev, struct device_attribute *attr, char *buf) \ - { \ - struct fscpos_data *data = fscpos_update_device(dev); \ - return show_##kind##sub(data, buf); \ - } - -#define create_getter_n(kind, offset, sub) \ - static ssize_t sysfs_show_##kind##offset##sub(struct device *dev, struct device_attribute *attr, char\ - *buf) \ - { \ - struct fscpos_data *data = fscpos_update_device(dev); \ - return show_##kind##sub(data, buf, offset); \ - } - -#define create_setter(kind, sub, reg) \ - static ssize_t sysfs_set_##kind##sub (struct device *dev, struct device_attribute *attr, const char \ - *buf, size_t count) \ - { \ - struct i2c_client *client = to_i2c_client(dev); \ - struct fscpos_data *data = i2c_get_clientdata(client); \ - return set_##kind##sub(client, data, buf, count, reg); \ - } - -#define create_setter_n(kind, offset, sub, reg) \ - static ssize_t sysfs_set_##kind##offset##sub (struct device *dev, struct device_attribute *attr, \ - const char *buf, size_t count) \ - { \ - struct i2c_client *client = to_i2c_client(dev); \ - struct fscpos_data *data = i2c_get_clientdata(client); \ - return set_##kind##sub(client, data, buf, count, offset, reg);\ - } - -#define create_sysfs_device_ro(kind, sub, offset) \ - static DEVICE_ATTR(kind##offset##sub, S_IRUGO, \ - sysfs_show_##kind##offset##sub, NULL); - -#define create_sysfs_device_rw(kind, sub, offset) \ - static DEVICE_ATTR(kind##offset##sub, S_IRUGO | S_IWUSR, \ - sysfs_show_##kind##offset##sub, sysfs_set_##kind##offset##sub); - -#define sysfs_ro_n(kind, sub, offset) \ - create_getter_n(kind, offset, sub); \ - create_sysfs_device_ro(kind, sub, offset); - -#define sysfs_rw_n(kind, sub, offset, reg) \ - create_getter_n(kind, offset, sub); \ - create_setter_n(kind, offset, sub, reg); \ - create_sysfs_device_rw(kind, sub, offset); - -#define sysfs_rw(kind, sub, reg) \ - create_getter(kind, sub); \ - create_setter(kind, sub, reg); \ - create_sysfs_device_rw(kind, sub,); - -#define sysfs_fan_with_min(offset, reg_status, reg_ripple, reg_min) \ - sysfs_fan(offset, reg_status, reg_ripple); \ - sysfs_rw_n(pwm,, offset, reg_min); - -#define sysfs_fan(offset, reg_status, reg_ripple) \ - sysfs_ro_n(fan, _input, offset); \ - sysfs_ro_n(fan, _status, offset); \ - sysfs_rw_n(fan, _ripple, offset, reg_ripple); - -#define sysfs_temp(offset, reg_status) \ - sysfs_ro_n(temp, _input, offset); \ - sysfs_ro_n(temp, _status, offset); \ - sysfs_rw_n(temp, _reset, offset, reg_status); - -#define sysfs_watchdog(reg_wdog_preset, reg_wdog_state, reg_wdog_control) \ - sysfs_rw(wdog, _control, reg_wdog_control); \ - sysfs_rw(wdog, _preset, reg_wdog_preset); \ - sysfs_rw(wdog, _state, reg_wdog_state); - -sysfs_fan_with_min(1, FSCPOS_REG_FAN_STATE[0], FSCPOS_REG_FAN_RIPPLE[0], - FSCPOS_REG_PWM[0]); -sysfs_fan_with_min(2, FSCPOS_REG_FAN_STATE[1], FSCPOS_REG_FAN_RIPPLE[1], - FSCPOS_REG_PWM[1]); -sysfs_fan(3, FSCPOS_REG_FAN_STATE[2], FSCPOS_REG_FAN_RIPPLE[2]); - -sysfs_temp(1, FSCPOS_REG_TEMP_STATE[0]); -sysfs_temp(2, FSCPOS_REG_TEMP_STATE[1]); -sysfs_temp(3, FSCPOS_REG_TEMP_STATE[2]); - -sysfs_watchdog(FSCPOS_REG_WDOG_PRESET, FSCPOS_REG_WDOG_STATE, - FSCPOS_REG_WDOG_CONTROL); - -static DEVICE_ATTR(event, S_IRUGO, show_event, NULL); -static DEVICE_ATTR(in0_input, S_IRUGO, show_volt_12, NULL); -static DEVICE_ATTR(in1_input, S_IRUGO, show_volt_5, NULL); -static DEVICE_ATTR(in2_input, S_IRUGO, show_volt_batt, NULL); - -static struct attribute *fscpos_attributes[] = { - &dev_attr_event.attr, - &dev_attr_in0_input.attr, - &dev_attr_in1_input.attr, - &dev_attr_in2_input.attr, - - &dev_attr_wdog_control.attr, - &dev_attr_wdog_preset.attr, - &dev_attr_wdog_state.attr, - - &dev_attr_temp1_input.attr, - &dev_attr_temp1_status.attr, - &dev_attr_temp1_reset.attr, - &dev_attr_temp2_input.attr, - &dev_attr_temp2_status.attr, - &dev_attr_temp2_reset.attr, - &dev_attr_temp3_input.attr, - &dev_attr_temp3_status.attr, - &dev_attr_temp3_reset.attr, - - &dev_attr_fan1_input.attr, - &dev_attr_fan1_status.attr, - &dev_attr_fan1_ripple.attr, - &dev_attr_pwm1.attr, - &dev_attr_fan2_input.attr, - &dev_attr_fan2_status.attr, - &dev_attr_fan2_ripple.attr, - &dev_attr_pwm2.attr, - &dev_attr_fan3_input.attr, - &dev_attr_fan3_status.attr, - &dev_attr_fan3_ripple.attr, - NULL -}; - -static const struct attribute_group fscpos_group = { - .attrs = fscpos_attributes, -}; - -/* Return 0 if detection is successful, -ENODEV otherwise */ -static int fscpos_detect(struct i2c_client *new_client, int kind, - struct i2c_board_info *info) -{ - struct i2c_adapter *adapter = new_client->adapter; - - if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) - return -ENODEV; - - /* Do the remaining detection unless force or force_fscpos parameter */ - if (kind < 0) { - if ((fscpos_read_value(new_client, FSCPOS_REG_IDENT_0) - != 0x50) /* 'P' */ - || (fscpos_read_value(new_client, FSCPOS_REG_IDENT_1) - != 0x45) /* 'E' */ - || (fscpos_read_value(new_client, FSCPOS_REG_IDENT_2) - != 0x47))/* 'G' */ - return -ENODEV; - } - - strlcpy(info->type, "fscpos", I2C_NAME_SIZE); - - return 0; -} - -static int fscpos_probe(struct i2c_client *new_client, - const struct i2c_device_id *id) -{ - struct fscpos_data *data; - int err; - - data = kzalloc(sizeof(struct fscpos_data), GFP_KERNEL); - if (!data) { - err = -ENOMEM; - goto exit; - } - - i2c_set_clientdata(new_client, data); - data->valid = 0; - mutex_init(&data->update_lock); - - /* Inizialize the fscpos chip */ - fscpos_init_client(new_client); - - /* Announce that the chip was found */ - dev_info(&new_client->dev, "Found fscpos chip, rev %u\n", data->revision); - - /* Register sysfs hooks */ - if ((err = sysfs_create_group(&new_client->dev.kobj, &fscpos_group))) - goto exit_free; - - data->hwmon_dev = hwmon_device_register(&new_client->dev); - if (IS_ERR(data->hwmon_dev)) { - err = PTR_ERR(data->hwmon_dev); - goto exit_remove_files; - } - - return 0; - -exit_remove_files: - sysfs_remove_group(&new_client->dev.kobj, &fscpos_group); -exit_free: - kfree(data); -exit: - return err; -} - -static int fscpos_remove(struct i2c_client *client) -{ - struct fscpos_data *data = i2c_get_clientdata(client); - - hwmon_device_unregister(data->hwmon_dev); - sysfs_remove_group(&client->dev.kobj, &fscpos_group); - - kfree(data); - return 0; -} - -static int fscpos_read_value(struct i2c_client *client, u8 reg) -{ - dev_dbg(&client->dev, "Read reg 0x%02x\n", reg); - return i2c_smbus_read_byte_data(client, reg); -} - -static int fscpos_write_value(struct i2c_client *client, u8 reg, u8 value) -{ - dev_dbg(&client->dev, "Write reg 0x%02x, val 0x%02x\n", reg, value); - return i2c_smbus_write_byte_data(client, reg, value); -} - -/* Called when we have found a new FSCPOS chip */ -static void fscpos_init_client(struct i2c_client *client) -{ - struct fscpos_data *data = i2c_get_clientdata(client); - - /* read revision from chip */ - data->revision = fscpos_read_value(client, FSCPOS_REG_REVISION); -} - -static struct fscpos_data *fscpos_update_device(struct device *dev) -{ - struct i2c_client *client = to_i2c_client(dev); - struct fscpos_data *data = i2c_get_clientdata(client); - - mutex_lock(&data->update_lock); - - if (time_after(jiffies, data->last_updated + 2 * HZ) || !data->valid) { - int i; - - dev_dbg(&client->dev, "Starting fscpos update\n"); - - for (i = 0; i < 3; i++) { - data->temp_act[i] = fscpos_read_value(client, - FSCPOS_REG_TEMP_ACT[i]); - data->temp_status[i] = fscpos_read_value(client, - FSCPOS_REG_TEMP_STATE[i]); - data->fan_act[i] = fscpos_read_value(client, - FSCPOS_REG_FAN_ACT[i]); - data->fan_status[i] = fscpos_read_value(client, - FSCPOS_REG_FAN_STATE[i]); - data->fan_ripple[i] = fscpos_read_value(client, - FSCPOS_REG_FAN_RIPPLE[i]); - if (i < 2) { - /* fan2_min is not supported by the chip */ - data->pwm[i] = fscpos_read_value(client, - FSCPOS_REG_PWM[i]); - } - /* reset fan status if speed is back to > 0 */ - if (data->fan_status[i] != 0 && data->fan_act[i] > 0) { - reset_fan_alarm(client, i); - } - } - - data->volt[0] = fscpos_read_value(client, FSCPOS_REG_VOLT_12); - data->volt[1] = fscpos_read_value(client, FSCPOS_REG_VOLT_5); - data->volt[2] = fscpos_read_value(client, FSCPOS_REG_VOLT_BATT); - - data->wdog_preset = fscpos_read_value(client, - FSCPOS_REG_WDOG_PRESET); - data->wdog_state = fscpos_read_value(client, - FSCPOS_REG_WDOG_STATE); - data->wdog_control = fscpos_read_value(client, - FSCPOS_REG_WDOG_CONTROL); - - data->global_event = fscpos_read_value(client, - FSCPOS_REG_EVENT_STATE); - - data->last_updated = jiffies; - data->valid = 1; - } - mutex_unlock(&data->update_lock); - return data; -} - -static int __init sm_fscpos_init(void) -{ - return i2c_add_driver(&fscpos_driver); -} - -static void __exit sm_fscpos_exit(void) -{ - i2c_del_driver(&fscpos_driver); -} - -MODULE_AUTHOR("Stefan Ott <stefan@desire.ch> based on work from Hermann Jung " - "<hej@odn.de>, Frodo Looijaard <frodol@dds.nl>" - " and Philip Edelbrock <phil@netroedge.com>"); -MODULE_DESCRIPTION("fujitsu siemens poseidon chip driver"); -MODULE_LICENSE("GPL"); - -module_init(sm_fscpos_init); -module_exit(sm_fscpos_exit); diff --git a/drivers/hwmon/hp_accel.c b/drivers/hwmon/hp_accel.c index 6679854c85b0..be475e844c2a 100644 --- a/drivers/hwmon/hp_accel.c +++ b/drivers/hwmon/hp_accel.c @@ -197,11 +197,13 @@ static struct dmi_system_id lis3lv02d_dmi_ids[] = { AXIS_DMI_MATCH("HP2133", "HP 2133", xy_rotated_left), AXIS_DMI_MATCH("HP2140", "HP 2140", xy_swap_inverted), AXIS_DMI_MATCH("NC653x", "HP Compaq 653", xy_rotated_left_usd), - AXIS_DMI_MATCH("NC673x", "HP Compaq 673", xy_rotated_left_usd), + AXIS_DMI_MATCH("NC6730b", "HP Compaq 6730b", xy_rotated_left_usd), + AXIS_DMI_MATCH("NC6730s", "HP Compaq 6730s", xy_swap), AXIS_DMI_MATCH("NC651xx", "HP Compaq 651", xy_rotated_right), AXIS_DMI_MATCH("NC6710x", "HP Compaq 6710", xy_swap_yz_inverted), AXIS_DMI_MATCH("NC6715x", "HP Compaq 6715", y_inverted), AXIS_DMI_MATCH("NC693xx", "HP EliteBook 693", xy_rotated_right), + AXIS_DMI_MATCH("NC693xx", "HP EliteBook 853", xy_swap), /* Intel-based HP Pavilion dv5 */ AXIS_DMI_MATCH2("HPDV5_I", PRODUCT_NAME, "HP Pavilion dv5", @@ -214,6 +216,7 @@ static struct dmi_system_id lis3lv02d_dmi_ids[] = { y_inverted), AXIS_DMI_MATCH("DV7", "HP Pavilion dv7", x_inverted), AXIS_DMI_MATCH("HP8710", "HP Compaq 8710", y_inverted), + AXIS_DMI_MATCH("HDX18", "HP HDX 18", x_inverted), { NULL, } /* Laptop models without axis info (yet): * "NC6910" "HP Compaq 6910" diff --git a/drivers/hwmon/it87.c b/drivers/hwmon/it87.c index ffeb2a10e1a7..a3749cb0f181 100644 --- a/drivers/hwmon/it87.c +++ b/drivers/hwmon/it87.c @@ -1028,12 +1028,11 @@ static int __init it87_find(unsigned short *address, chip_type, *address, sio_data->revision); /* Read GPIO config and VID value from LDN 7 (GPIO) */ - if (chip_type != IT8705F_DEVID) { + if (sio_data->type != it87) { int reg; superio_select(GPIO); - if ((chip_type == it8718) || - (chip_type == it8720)) + if (sio_data->type == it8718 || sio_data->type == it8720) sio_data->vid_value = superio_inb(IT87_SIO_VID_REG); reg = superio_inb(IT87_SIO_PINX2_REG); diff --git a/drivers/hwmon/lis3lv02d_spi.c b/drivers/hwmon/lis3lv02d_spi.c index 82ebca5a699c..82b16808a274 100644 --- a/drivers/hwmon/lis3lv02d_spi.c +++ b/drivers/hwmon/lis3lv02d_spi.c @@ -83,7 +83,8 @@ static int __devexit lis302dl_spi_remove(struct spi_device *spi) struct lis3lv02d *lis3 = spi_get_drvdata(spi); lis3lv02d_joystick_disable(); lis3lv02d_poweroff(lis3); - return 0; + + return lis3lv02d_remove_fs(&lis3_dev); } #ifdef CONFIG_PM @@ -139,4 +140,4 @@ module_exit(lis302dl_exit); MODULE_AUTHOR("Daniel Mack <daniel@caiaq.de>"); MODULE_DESCRIPTION("lis3lv02d SPI glue layer"); MODULE_LICENSE("GPL"); - +MODULE_ALIAS("spi:" DRV_NAME); diff --git a/drivers/hwmon/lm70.c b/drivers/hwmon/lm70.c index ae6204f33214..ab8a5d3c7690 100644 --- a/drivers/hwmon/lm70.c +++ b/drivers/hwmon/lm70.c @@ -32,6 +32,7 @@ #include <linux/sysfs.h> #include <linux/hwmon.h> #include <linux/mutex.h> +#include <linux/mod_devicetable.h> #include <linux/spi/spi.h> @@ -130,11 +131,20 @@ static DEVICE_ATTR(name, S_IRUGO, lm70_show_name, NULL); /*----------------------------------------------------------------------*/ -static int __devinit common_probe(struct spi_device *spi, int chip) +static int __devinit lm70_probe(struct spi_device *spi) { + int chip = spi_get_device_id(spi)->driver_data; struct lm70 *p_lm70; int status; + /* signaling is SPI_MODE_0 for both LM70 and TMP121 */ + if (spi->mode & (SPI_CPOL | SPI_CPHA)) + return -EINVAL; + + /* 3-wire link (shared SI/SO) for LM70 */ + if (chip == LM70_CHIP_LM70 && !(spi->mode & SPI_3WIRE)) + return -EINVAL; + /* NOTE: we assume 8-bit words, and convert to 16 bits manually */ p_lm70 = kzalloc(sizeof *p_lm70, GFP_KERNEL); @@ -170,24 +180,6 @@ out_dev_reg_failed: return status; } -static int __devinit lm70_probe(struct spi_device *spi) -{ - /* signaling is SPI_MODE_0 on a 3-wire link (shared SI/SO) */ - if ((spi->mode & (SPI_CPOL | SPI_CPHA)) || !(spi->mode & SPI_3WIRE)) - return -EINVAL; - - return common_probe(spi, LM70_CHIP_LM70); -} - -static int __devinit tmp121_probe(struct spi_device *spi) -{ - /* signaling is SPI_MODE_0 with only MISO connected */ - if (spi->mode & (SPI_CPOL | SPI_CPHA)) - return -EINVAL; - - return common_probe(spi, LM70_CHIP_TMP121); -} - static int __devexit lm70_remove(struct spi_device *spi) { struct lm70 *p_lm70 = dev_get_drvdata(&spi->dev); @@ -201,41 +193,32 @@ static int __devexit lm70_remove(struct spi_device *spi) return 0; } -static struct spi_driver tmp121_driver = { - .driver = { - .name = "tmp121", - .owner = THIS_MODULE, - }, - .probe = tmp121_probe, - .remove = __devexit_p(lm70_remove), + +static const struct spi_device_id lm70_ids[] = { + { "lm70", LM70_CHIP_LM70 }, + { "tmp121", LM70_CHIP_TMP121 }, + { }, }; +MODULE_DEVICE_TABLE(spi, lm70_ids); static struct spi_driver lm70_driver = { .driver = { .name = "lm70", .owner = THIS_MODULE, }, + .id_table = lm70_ids, .probe = lm70_probe, .remove = __devexit_p(lm70_remove), }; static int __init init_lm70(void) { - int ret = spi_register_driver(&lm70_driver); - if (ret) - return ret; - - ret = spi_register_driver(&tmp121_driver); - if (ret) - spi_unregister_driver(&lm70_driver); - - return ret; + return spi_register_driver(&lm70_driver); } static void __exit cleanup_lm70(void) { spi_unregister_driver(&lm70_driver); - spi_unregister_driver(&tmp121_driver); } module_init(init_lm70); diff --git a/drivers/hwmon/ltc4215.c b/drivers/hwmon/ltc4215.c index 9386e2a39211..00d975eb5b83 100644 --- a/drivers/hwmon/ltc4215.c +++ b/drivers/hwmon/ltc4215.c @@ -20,11 +20,6 @@ #include <linux/hwmon.h> #include <linux/hwmon-sysfs.h> -static const unsigned short normal_i2c[] = { I2C_CLIENT_END }; - -/* Insmod parameters */ -I2C_CLIENT_INSMOD_1(ltc4215); - /* Here are names of the chip's registers (a.k.a. commands) */ enum ltc4215_cmd { LTC4215_CONTROL = 0x00, /* rw */ @@ -246,9 +241,13 @@ static const struct attribute_group ltc4215_group = { static int ltc4215_probe(struct i2c_client *client, const struct i2c_device_id *id) { + struct i2c_adapter *adapter = client->adapter; struct ltc4215_data *data; int ret; + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + return -ENODEV; + data = kzalloc(sizeof(*data), GFP_KERNEL); if (!data) { ret = -ENOMEM; @@ -259,7 +258,7 @@ static int ltc4215_probe(struct i2c_client *client, mutex_init(&data->update_lock); /* Initialize the LTC4215 chip */ - /* TODO */ + i2c_smbus_write_byte_data(client, LTC4215_FAULT, 0x00); /* Register sysfs hooks */ ret = sysfs_create_group(&client->dev.kobj, <c4215_group); @@ -294,56 +293,20 @@ static int ltc4215_remove(struct i2c_client *client) return 0; } -static int ltc4215_detect(struct i2c_client *client, - int kind, - struct i2c_board_info *info) -{ - struct i2c_adapter *adapter = client->adapter; - - if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) - return -ENODEV; - - if (kind < 0) { /* probed detection - check the chip type */ - s32 v; /* 8 bits from the chip, or -ERRNO */ - - /* - * Register 0x01 bit b7 is reserved, expect 0 - * Register 0x03 bit b6 and b7 are reserved, expect 0 - */ - v = i2c_smbus_read_byte_data(client, LTC4215_ALERT); - if (v < 0 || (v & (1 << 7)) != 0) - return -ENODEV; - - v = i2c_smbus_read_byte_data(client, LTC4215_FAULT); - if (v < 0 || (v & ((1 << 6) | (1 << 7))) != 0) - return -ENODEV; - } - - strlcpy(info->type, "ltc4215", I2C_NAME_SIZE); - dev_info(&adapter->dev, "ltc4215 %s at address 0x%02x\n", - kind < 0 ? "probed" : "forced", - client->addr); - - return 0; -} - static const struct i2c_device_id ltc4215_id[] = { - { "ltc4215", ltc4215 }, + { "ltc4215", 0 }, { } }; MODULE_DEVICE_TABLE(i2c, ltc4215_id); /* This is the driver that will be inserted */ static struct i2c_driver ltc4215_driver = { - .class = I2C_CLASS_HWMON, .driver = { .name = "ltc4215", }, .probe = ltc4215_probe, .remove = ltc4215_remove, .id_table = ltc4215_id, - .detect = ltc4215_detect, - .address_data = &addr_data, }; static int __init ltc4215_init(void) diff --git a/drivers/hwmon/ltc4245.c b/drivers/hwmon/ltc4245.c index 034b2c515848..65c232a9d0c5 100644 --- a/drivers/hwmon/ltc4245.c +++ b/drivers/hwmon/ltc4245.c @@ -22,15 +22,6 @@ #include <linux/hwmon.h> #include <linux/hwmon-sysfs.h> -/* Valid addresses are 0x20 - 0x3f - * - * For now, we do not probe, since some of these addresses - * are known to be unfriendly to probing */ -static const unsigned short normal_i2c[] = { I2C_CLIENT_END }; - -/* Insmod parameters */ -I2C_CLIENT_INSMOD_1(ltc4245); - /* Here are names of the chip's registers (a.k.a. commands) */ enum ltc4245_cmd { LTC4245_STATUS = 0x00, /* readonly */ @@ -369,9 +360,13 @@ static const struct attribute_group ltc4245_group = { static int ltc4245_probe(struct i2c_client *client, const struct i2c_device_id *id) { + struct i2c_adapter *adapter = client->adapter; struct ltc4245_data *data; int ret; + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + return -ENODEV; + data = kzalloc(sizeof(*data), GFP_KERNEL); if (!data) { ret = -ENOMEM; @@ -382,7 +377,8 @@ static int ltc4245_probe(struct i2c_client *client, mutex_init(&data->update_lock); /* Initialize the LTC4245 chip */ - /* TODO */ + i2c_smbus_write_byte_data(client, LTC4245_FAULT1, 0x00); + i2c_smbus_write_byte_data(client, LTC4245_FAULT2, 0x00); /* Register sysfs hooks */ ret = sysfs_create_group(&client->dev.kobj, <c4245_group); @@ -417,136 +413,20 @@ static int ltc4245_remove(struct i2c_client *client) return 0; } -/* Check that some bits in a control register appear at all possible - * locations without changing value - * - * @client: the i2c client to use - * @reg: the register to read - * @bits: the bits to check (0xff checks all bits, - * 0x03 checks only the last two bits) - * - * return -ERRNO if the register read failed - * return -ENODEV if the register value doesn't stay constant at all - * possible addresses - * - * return 0 for success - */ -static int ltc4245_check_control_reg(struct i2c_client *client, u8 reg, u8 bits) -{ - int i; - s32 v, voff1, voff2; - - /* Read register and check for error */ - v = i2c_smbus_read_byte_data(client, reg); - if (v < 0) - return v; - - v &= bits; - - for (i = 0x00; i < 0xff; i += 0x20) { - - voff1 = i2c_smbus_read_byte_data(client, reg + i); - if (voff1 < 0) - return voff1; - - voff2 = i2c_smbus_read_byte_data(client, reg + i + 0x08); - if (voff2 < 0) - return voff2; - - voff1 &= bits; - voff2 &= bits; - - if (v != voff1 || v != voff2) - return -ENODEV; - } - - return 0; -} - -static int ltc4245_detect(struct i2c_client *client, - int kind, - struct i2c_board_info *info) -{ - struct i2c_adapter *adapter = client->adapter; - - if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) - return -ENODEV; - - if (kind < 0) { /* probed detection - check the chip type */ - s32 v; /* 8 bits from the chip, or -ERRNO */ - - /* Chip registers 0x00-0x07 are control registers - * Chip registers 0x10-0x1f are data registers - * - * Address bits b7-b5 are ignored. This makes the chip "repeat" - * in steps of 0x20. Any control registers should appear with - * the same values across all duplicated addresses. - * - * Register 0x02 bit b2 is reserved, expect 0 - * Register 0x07 bits b7 to b4 are reserved, expect 0 - * - * Registers 0x01, 0x02 are control registers and should not - * change on their own. - * - * Register 0x06 bits b6 and b7 are control bits, and should - * not change on their own. - * - * Register 0x07 bits b3 to b0 are control bits, and should - * not change on their own. - */ - - /* read register 0x02 reserved bit, expect 0 */ - v = i2c_smbus_read_byte_data(client, LTC4245_CONTROL); - if (v < 0 || (v & 0x04) != 0) - return -ENODEV; - - /* read register 0x07 reserved bits, expect 0 */ - v = i2c_smbus_read_byte_data(client, LTC4245_ADCADR); - if (v < 0 || (v & 0xf0) != 0) - return -ENODEV; - - /* check that the alert register appears at all locations */ - if (ltc4245_check_control_reg(client, LTC4245_ALERT, 0xff)) - return -ENODEV; - - /* check that the control register appears at all locations */ - if (ltc4245_check_control_reg(client, LTC4245_CONTROL, 0xff)) - return -ENODEV; - - /* check that register 0x06 bits b6 and b7 stay constant */ - if (ltc4245_check_control_reg(client, LTC4245_GPIO, 0xc0)) - return -ENODEV; - - /* check that register 0x07 bits b3-b0 stay constant */ - if (ltc4245_check_control_reg(client, LTC4245_ADCADR, 0x0f)) - return -ENODEV; - } - - strlcpy(info->type, "ltc4245", I2C_NAME_SIZE); - dev_info(&adapter->dev, "ltc4245 %s at address 0x%02x\n", - kind < 0 ? "probed" : "forced", - client->addr); - - return 0; -} - static const struct i2c_device_id ltc4245_id[] = { - { "ltc4245", ltc4245 }, + { "ltc4245", 0 }, { } }; MODULE_DEVICE_TABLE(i2c, ltc4245_id); /* This is the driver that will be inserted */ static struct i2c_driver ltc4245_driver = { - .class = I2C_CLASS_HWMON, .driver = { .name = "ltc4245", }, .probe = ltc4245_probe, .remove = ltc4245_remove, .id_table = ltc4245_id, - .detect = ltc4245_detect, - .address_data = &addr_data, }; static int __init ltc4245_init(void) diff --git a/drivers/hwmon/max1111.c b/drivers/hwmon/max1111.c index bfaa665ccf32..9ac497271adf 100644 --- a/drivers/hwmon/max1111.c +++ b/drivers/hwmon/max1111.c @@ -242,3 +242,4 @@ module_exit(max1111_exit); MODULE_AUTHOR("Eric Miao <eric.miao@marvell.com>"); MODULE_DESCRIPTION("MAX1111 ADC Driver"); MODULE_LICENSE("GPL"); +MODULE_ALIAS("spi:max1111"); diff --git a/drivers/hwmon/s3c-hwmon.c b/drivers/hwmon/s3c-hwmon.c index 3a524f2fe493..71835412529f 100644 --- a/drivers/hwmon/s3c-hwmon.c +++ b/drivers/hwmon/s3c-hwmon.c @@ -323,14 +323,21 @@ static int __devinit s3c_hwmon_probe(struct platform_device *dev) } for (i = 0; i < ARRAY_SIZE(pdata->in); i++) { - if (!pdata->in[i]) + struct s3c24xx_adc_hwmon_incfg *cfg = pdata->in[i]; + + if (!cfg) continue; - if (pdata->in[i]->mult >= 0x10000) + if (cfg->mult >= 0x10000) dev_warn(&dev->dev, "channel %d multiplier too large\n", i); + if (cfg->divider == 0) { + dev_err(&dev->dev, "channel %d divider zero\n", i); + continue; + } + ret = s3c_hwmon_create_attr(&dev->dev, pdata->in[i], &hwmon->attrs[i], i); if (ret) { diff --git a/drivers/hwmon/sht15.c b/drivers/hwmon/sht15.c index 303c02694c3c..ebe38b680ee3 100644 --- a/drivers/hwmon/sht15.c +++ b/drivers/hwmon/sht15.c @@ -30,6 +30,7 @@ #include <linux/hwmon-sysfs.h> #include <linux/mutex.h> #include <linux/platform_device.h> +#include <linux/sched.h> #include <linux/delay.h> #include <linux/jiffies.h> #include <linux/err.h> @@ -622,7 +623,12 @@ static int __devexit sht15_remove(struct platform_device *pdev) } -static struct platform_driver sht_drivers[] = { +/* + * sht_drivers simultaneously refers to __devinit and __devexit function + * which causes spurious section mismatch warning. So use __refdata to + * get rid from this. + */ +static struct platform_driver __refdata sht_drivers[] = { { .driver = { .name = "sht10", diff --git a/drivers/i2c/Kconfig b/drivers/i2c/Kconfig index 711ca08ab776..d7ece131b4f4 100644 --- a/drivers/i2c/Kconfig +++ b/drivers/i2c/Kconfig @@ -27,6 +27,14 @@ config I2C_BOARDINFO boolean default y +config I2C_COMPAT + boolean "Enable compatibility bits for old user-space" + default y + help + Say Y here if you intend to run lm-sensors 3.1.1 or older, or any + other user-space package which expects i2c adapters to be class + devices. If you don't know, say Y. + config I2C_CHARDEV tristate "I2C device interface" help diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index 8206442fbabd..e8fe7f169e25 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig @@ -113,7 +113,7 @@ config I2C_ISCH will be called i2c-isch. config I2C_PIIX4 - tristate "Intel PIIX4 and compatible (ATI/Serverworks/Broadcom/SMSC)" + tristate "Intel PIIX4 and compatible (ATI/AMD/Serverworks/Broadcom/SMSC)" depends on PCI help If you say yes to this option, support will be included for the Intel @@ -128,6 +128,7 @@ config I2C_PIIX4 ATI SB600 ATI SB700 ATI SB800 + AMD Hudson-2 Serverworks OSB4 Serverworks CSB5 Serverworks CSB6 @@ -231,6 +232,22 @@ config I2C_VIAPRO This driver can also be built as a module. If so, the module will be called i2c-viapro. +if ACPI + +comment "ACPI drivers" + +config I2C_SCMI + tristate "SMBus Control Method Interface" + help + This driver supports the SMBus Control Method Interface. It needs the + BIOS to declare ACPI control methods as described in the SMBus Control + Method Interface specification. + + To compile this driver as a module, choose M here: + the module will be called i2c-scmi. + +endif # ACPI + comment "Mac SMBus host controller drivers" depends on PPC_CHRP || PPC_PMAC @@ -460,8 +477,8 @@ config I2C_PNX will be called i2c-pnx. config I2C_PXA - tristate "Intel PXA2XX I2C adapter (EXPERIMENTAL)" - depends on EXPERIMENTAL && ARCH_PXA + tristate "Intel PXA2XX I2C adapter" + depends on ARCH_PXA || ARCH_MMP help If you have devices in the PXA I2C bus, say yes to this option. This driver can also be built as a module. If so, the module diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile index e654263bfc01..ff937ac69f5b 100644 --- a/drivers/i2c/busses/Makefile +++ b/drivers/i2c/busses/Makefile @@ -2,6 +2,9 @@ # Makefile for the i2c bus drivers. # +# ACPI drivers +obj-$(CONFIG_I2C_SCMI) += i2c-scmi.o + # PC SMBus host controller drivers obj-$(CONFIG_I2C_ALI1535) += i2c-ali1535.o obj-$(CONFIG_I2C_ALI1563) += i2c-ali1563.o diff --git a/drivers/i2c/busses/i2c-amd756.c b/drivers/i2c/busses/i2c-amd756.c index f7d6fe9c49ba..8f0b90ef8c76 100644 --- a/drivers/i2c/busses/i2c-amd756.c +++ b/drivers/i2c/busses/i2c-amd756.c @@ -364,7 +364,7 @@ static int __devinit amd756_probe(struct pci_dev *pdev, error = acpi_check_region(amd756_ioport, SMB_IOSIZE, amd756_driver.name); if (error) - return error; + return -ENODEV; if (!request_region(amd756_ioport, SMB_IOSIZE, amd756_driver.name)) { dev_err(&pdev->dev, "SMB region 0x%x already in use!\n", diff --git a/drivers/i2c/busses/i2c-amd8111.c b/drivers/i2c/busses/i2c-amd8111.c index a7c59908c457..5b4ad86ca166 100644 --- a/drivers/i2c/busses/i2c-amd8111.c +++ b/drivers/i2c/busses/i2c-amd8111.c @@ -376,8 +376,10 @@ static int __devinit amd8111_probe(struct pci_dev *dev, smbus->size = pci_resource_len(dev, 0); error = acpi_check_resource_conflict(&dev->resource[0]); - if (error) + if (error) { + error = -ENODEV; goto out_kfree; + } if (!request_region(smbus->base, smbus->size, amd8111_driver.name)) { error = -EBUSY; diff --git a/drivers/i2c/busses/i2c-i801.c b/drivers/i2c/busses/i2c-i801.c index 9d2c5adf5d4f..55edcfe5b851 100644 --- a/drivers/i2c/busses/i2c-i801.c +++ b/drivers/i2c/busses/i2c-i801.c @@ -732,8 +732,10 @@ static int __devinit i801_probe(struct pci_dev *dev, const struct pci_device_id } err = acpi_check_resource_conflict(&dev->resource[SMBBAR]); - if (err) + if (err) { + err = -ENODEV; goto exit; + } err = pci_request_region(dev, SMBBAR, i801_driver.name); if (err) { diff --git a/drivers/i2c/busses/i2c-imx.c b/drivers/i2c/busses/i2c-imx.c index 4afba3ec2a61..e3654d683e15 100644 --- a/drivers/i2c/busses/i2c-imx.c +++ b/drivers/i2c/busses/i2c-imx.c @@ -120,19 +120,26 @@ struct imx_i2c_struct { wait_queue_head_t queue; unsigned long i2csr; unsigned int disable_delay; + int stopped; + unsigned int ifdr; /* IMX_I2C_IFDR */ }; /** Functions for IMX I2C adapter driver *************************************** *******************************************************************************/ -static int i2c_imx_bus_busy(struct imx_i2c_struct *i2c_imx) +static int i2c_imx_bus_busy(struct imx_i2c_struct *i2c_imx, int for_busy) { unsigned long orig_jiffies = jiffies; + unsigned int temp; dev_dbg(&i2c_imx->adapter.dev, "<%s>\n", __func__); - /* wait for bus not busy */ - while (readb(i2c_imx->base + IMX_I2C_I2SR) & I2SR_IBB) { + while (1) { + temp = readb(i2c_imx->base + IMX_I2C_I2SR); + if (for_busy && (temp & I2SR_IBB)) + break; + if (!for_busy && !(temp & I2SR_IBB)) + break; if (signal_pending(current)) { dev_dbg(&i2c_imx->adapter.dev, "<%s> I2C Interrupted\n", __func__); @@ -179,41 +186,62 @@ static int i2c_imx_acked(struct imx_i2c_struct *i2c_imx) return 0; } -static void i2c_imx_start(struct imx_i2c_struct *i2c_imx) +static int i2c_imx_start(struct imx_i2c_struct *i2c_imx) { unsigned int temp = 0; + int result; dev_dbg(&i2c_imx->adapter.dev, "<%s>\n", __func__); + clk_enable(i2c_imx->clk); + writeb(i2c_imx->ifdr, i2c_imx->base + IMX_I2C_IFDR); /* Enable I2C controller */ + writeb(0, i2c_imx->base + IMX_I2C_I2SR); writeb(I2CR_IEN, i2c_imx->base + IMX_I2C_I2CR); + + /* Wait controller to be stable */ + udelay(50); + /* Start I2C transaction */ temp = readb(i2c_imx->base + IMX_I2C_I2CR); temp |= I2CR_MSTA; writeb(temp, i2c_imx->base + IMX_I2C_I2CR); + result = i2c_imx_bus_busy(i2c_imx, 1); + if (result) + return result; + i2c_imx->stopped = 0; + temp |= I2CR_IIEN | I2CR_MTX | I2CR_TXAK; writeb(temp, i2c_imx->base + IMX_I2C_I2CR); + return result; } static void i2c_imx_stop(struct imx_i2c_struct *i2c_imx) { unsigned int temp = 0; - /* Stop I2C transaction */ - dev_dbg(&i2c_imx->adapter.dev, "<%s>\n", __func__); - temp = readb(i2c_imx->base + IMX_I2C_I2CR); - temp &= ~I2CR_MSTA; - writeb(temp, i2c_imx->base + IMX_I2C_I2CR); - /* setup chip registers to defaults */ - writeb(I2CR_IEN, i2c_imx->base + IMX_I2C_I2CR); - writeb(0, i2c_imx->base + IMX_I2C_I2SR); - /* - * This delay caused by an i.MXL hardware bug. - * If no (or too short) delay, no "STOP" bit will be generated. - */ - udelay(i2c_imx->disable_delay); + if (!i2c_imx->stopped) { + /* Stop I2C transaction */ + dev_dbg(&i2c_imx->adapter.dev, "<%s>\n", __func__); + temp = readb(i2c_imx->base + IMX_I2C_I2CR); + temp &= ~(I2CR_MSTA | I2CR_MTX); + writeb(temp, i2c_imx->base + IMX_I2C_I2CR); + i2c_imx->stopped = 1; + } + if (cpu_is_mx1()) { + /* + * This delay caused by an i.MXL hardware bug. + * If no (or too short) delay, no "STOP" bit will be generated. + */ + udelay(i2c_imx->disable_delay); + } + + if (!i2c_imx->stopped) + i2c_imx_bus_busy(i2c_imx, 0); + /* Disable I2C controller */ writeb(0, i2c_imx->base + IMX_I2C_I2CR); + clk_disable(i2c_imx->clk); } static void __init i2c_imx_set_clk(struct imx_i2c_struct *i2c_imx, @@ -233,8 +261,8 @@ static void __init i2c_imx_set_clk(struct imx_i2c_struct *i2c_imx, else for (i = 0; i2c_clk_div[i][0] < div; i++); - /* Write divider value to register */ - writeb(i2c_clk_div[i][1], i2c_imx->base + IMX_I2C_IFDR); + /* Store divider value */ + i2c_imx->ifdr = i2c_clk_div[i][1]; /* * There dummy delay is calculated. @@ -341,11 +369,15 @@ static int i2c_imx_read(struct imx_i2c_struct *i2c_imx, struct i2c_msg *msgs) if (result) return result; if (i == (msgs->len - 1)) { + /* It must generate STOP before read I2DR to prevent + controller from generating another clock cycle */ dev_dbg(&i2c_imx->adapter.dev, "<%s> clear MSTA\n", __func__); temp = readb(i2c_imx->base + IMX_I2C_I2CR); - temp &= ~I2CR_MSTA; + temp &= ~(I2CR_MSTA | I2CR_MTX); writeb(temp, i2c_imx->base + IMX_I2C_I2CR); + i2c_imx_bus_busy(i2c_imx, 0); + i2c_imx->stopped = 1; } else if (i == (msgs->len - 2)) { dev_dbg(&i2c_imx->adapter.dev, "<%s> set TXAK\n", __func__); @@ -370,14 +402,11 @@ static int i2c_imx_xfer(struct i2c_adapter *adapter, dev_dbg(&i2c_imx->adapter.dev, "<%s>\n", __func__); - /* Check if i2c bus is not busy */ - result = i2c_imx_bus_busy(i2c_imx); + /* Start I2C transfer */ + result = i2c_imx_start(i2c_imx); if (result) goto fail0; - /* Start I2C transfer */ - i2c_imx_start(i2c_imx); - /* read/write data */ for (i = 0; i < num; i++) { if (i) { @@ -386,6 +415,9 @@ static int i2c_imx_xfer(struct i2c_adapter *adapter, temp = readb(i2c_imx->base + IMX_I2C_I2CR); temp |= I2CR_RSTA; writeb(temp, i2c_imx->base + IMX_I2C_I2CR); + result = i2c_imx_bus_busy(i2c_imx, 1); + if (result) + goto fail0; } dev_dbg(&i2c_imx->adapter.dev, "<%s> transfer message: %d\n", __func__, i); @@ -500,7 +532,6 @@ static int __init i2c_imx_probe(struct platform_device *pdev) dev_err(&pdev->dev, "can't get I2C clock\n"); goto fail3; } - clk_enable(i2c_imx->clk); /* Request IRQ */ ret = request_irq(i2c_imx->irq, i2c_imx_isr, 0, pdev->name, i2c_imx); @@ -549,7 +580,6 @@ static int __init i2c_imx_probe(struct platform_device *pdev) fail5: free_irq(i2c_imx->irq, i2c_imx); fail4: - clk_disable(i2c_imx->clk); clk_put(i2c_imx->clk); fail3: release_mem_region(i2c_imx->res->start, resource_size(res)); @@ -586,8 +616,6 @@ static int __exit i2c_imx_remove(struct platform_device *pdev) if (pdata && pdata->exit) pdata->exit(&pdev->dev); - /* Disable I2C clock */ - clk_disable(i2c_imx->clk); clk_put(i2c_imx->clk); release_mem_region(i2c_imx->res->start, resource_size(i2c_imx->res)); diff --git a/drivers/i2c/busses/i2c-isch.c b/drivers/i2c/busses/i2c-isch.c index 9f6b8e0f8632..dba6eb053e2f 100644 --- a/drivers/i2c/busses/i2c-isch.c +++ b/drivers/i2c/busses/i2c-isch.c @@ -281,7 +281,7 @@ static int __devinit sch_probe(struct pci_dev *dev, return -ENODEV; } if (acpi_check_region(sch_smba, SMBIOSIZE, sch_driver.name)) - return -EBUSY; + return -ENODEV; if (!request_region(sch_smba, SMBIOSIZE, sch_driver.name)) { dev_err(&dev->dev, "SMBus region 0x%x already in use!\n", sch_smba); diff --git a/drivers/i2c/busses/i2c-mpc.c b/drivers/i2c/busses/i2c-mpc.c index d325e86e3103..f627001108b8 100644 --- a/drivers/i2c/busses/i2c-mpc.c +++ b/drivers/i2c/busses/i2c-mpc.c @@ -365,9 +365,6 @@ static int mpc_write(struct mpc_i2c *i2c, int target, unsigned timeout = i2c->adap.timeout; u32 flags = restart ? CCR_RSTA : 0; - /* Start with MEN */ - if (!restart) - writeccr(i2c, CCR_MEN); /* Start as master */ writeccr(i2c, CCR_MIEN | CCR_MEN | CCR_MSTA | CCR_MTX | flags); /* Write target byte */ @@ -396,9 +393,6 @@ static int mpc_read(struct mpc_i2c *i2c, int target, int i, result; u32 flags = restart ? CCR_RSTA : 0; - /* Start with MEN */ - if (!restart) - writeccr(i2c, CCR_MEN); /* Switch to read - restart */ writeccr(i2c, CCR_MIEN | CCR_MEN | CCR_MSTA | CCR_MTX | flags); /* Write target address byte - this time with the read flag set */ @@ -425,9 +419,9 @@ static int mpc_read(struct mpc_i2c *i2c, int target, /* Generate txack on next to last byte */ if (i == length - 2) writeccr(i2c, CCR_MIEN | CCR_MEN | CCR_MSTA | CCR_TXAK); - /* Generate stop on last byte */ + /* Do not generate stop on last byte */ if (i == length - 1) - writeccr(i2c, CCR_MIEN | CCR_MEN | CCR_TXAK); + writeccr(i2c, CCR_MIEN | CCR_MEN | CCR_MSTA | CCR_MTX); data[i] = readb(i2c->base + MPC_I2C_DR); } diff --git a/drivers/i2c/busses/i2c-piix4.c b/drivers/i2c/busses/i2c-piix4.c index 0249a7d762b9..1e245e9cad31 100644 --- a/drivers/i2c/busses/i2c-piix4.c +++ b/drivers/i2c/busses/i2c-piix4.c @@ -22,6 +22,7 @@ Intel PIIX4, 440MX Serverworks OSB4, CSB5, CSB6, HT-1000, HT-1100 ATI IXP200, IXP300, IXP400, SB600, SB700, SB800 + AMD Hudson-2 SMSC Victory66 Note: we assume there can only be one device, with one SMBus interface. @@ -168,7 +169,7 @@ static int __devinit piix4_setup(struct pci_dev *PIIX4_dev, } if (acpi_check_region(piix4_smba, SMBIOSIZE, piix4_driver.name)) - return -EBUSY; + return -ENODEV; if (!request_region(piix4_smba, SMBIOSIZE, piix4_driver.name)) { dev_err(&PIIX4_dev->dev, "SMBus region 0x%x already in use!\n", @@ -232,9 +233,9 @@ static int __devinit piix4_setup_sb800(struct pci_dev *PIIX4_dev, unsigned short smba_idx = 0xcd6; u8 smba_en_lo, smba_en_hi, i2ccfg, i2ccfg_offset = 0x10, smb_en = 0x2c; - /* SB800 SMBus does not support forcing address */ + /* SB800 and later SMBus does not support forcing address */ if (force || force_addr) { - dev_err(&PIIX4_dev->dev, "SB800 SMBus does not support " + dev_err(&PIIX4_dev->dev, "SMBus does not support " "forcing address!\n"); return -EINVAL; } @@ -259,7 +260,7 @@ static int __devinit piix4_setup_sb800(struct pci_dev *PIIX4_dev, piix4_smba = ((smba_en_hi << 8) | smba_en_lo) & 0xffe0; if (acpi_check_region(piix4_smba, SMBIOSIZE, piix4_driver.name)) - return -EBUSY; + return -ENODEV; if (!request_region(piix4_smba, SMBIOSIZE, piix4_driver.name)) { dev_err(&PIIX4_dev->dev, "SMBus region 0x%x already in use!\n", @@ -479,6 +480,7 @@ static struct pci_device_id piix4_ids[] = { { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_IXP300_SMBUS) }, { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_IXP400_SMBUS) }, { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_SBX00_SMBUS) }, + { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_HUDSON2_SMBUS) }, { PCI_DEVICE(PCI_VENDOR_ID_SERVERWORKS, PCI_DEVICE_ID_SERVERWORKS_OSB4) }, { PCI_DEVICE(PCI_VENDOR_ID_SERVERWORKS, @@ -499,9 +501,10 @@ static int __devinit piix4_probe(struct pci_dev *dev, { int retval; - if ((dev->vendor == PCI_VENDOR_ID_ATI) && - (dev->device == PCI_DEVICE_ID_ATI_SBX00_SMBUS) && - (dev->revision >= 0x40)) + if ((dev->vendor == PCI_VENDOR_ID_ATI && + dev->device == PCI_DEVICE_ID_ATI_SBX00_SMBUS && + dev->revision >= 0x40) || + dev->vendor == PCI_VENDOR_ID_AMD) /* base address location etc changed in SB800 */ retval = piix4_setup_sb800(dev, id); else diff --git a/drivers/i2c/busses/i2c-pnx.c b/drivers/i2c/busses/i2c-pnx.c index ec15cff556b9..1fca59077949 100644 --- a/drivers/i2c/busses/i2c-pnx.c +++ b/drivers/i2c/busses/i2c-pnx.c @@ -19,7 +19,9 @@ #include <linux/completion.h> #include <linux/platform_device.h> #include <linux/i2c-pnx.h> +#include <linux/io.h> #include <mach/hardware.h> +#include <mach/i2c.h> #include <asm/irq.h> #include <asm/uaccess.h> @@ -54,6 +56,9 @@ static inline void i2c_pnx_arm_timer(struct i2c_adapter *adap) struct timer_list *timer = &data->mif.timer; int expires = I2C_PNX_TIMEOUT / (1000 / HZ); + if (expires <= 1) + expires = 2; + del_timer_sync(timer); dev_dbg(&adap->dev, "Timer armed at %lu plus %u jiffies.\n", @@ -586,7 +591,8 @@ static int __devinit i2c_pnx_probe(struct platform_device *pdev) alg_data->mif.timer.data = (unsigned long)i2c_pnx->adapter; /* Register I/O resource */ - if (!request_region(alg_data->base, I2C_PNX_REGION_SIZE, pdev->name)) { + if (!request_mem_region(alg_data->base, I2C_PNX_REGION_SIZE, + pdev->name)) { dev_err(&pdev->dev, "I/O region 0x%08x for I2C already in use.\n", alg_data->base); @@ -650,7 +656,7 @@ out_clock: out_unmap: iounmap((void *)alg_data->ioaddr); out_release: - release_region(alg_data->base, I2C_PNX_REGION_SIZE); + release_mem_region(alg_data->base, I2C_PNX_REGION_SIZE); out_drvdata: platform_set_drvdata(pdev, NULL); out: @@ -667,7 +673,7 @@ static int __devexit i2c_pnx_remove(struct platform_device *pdev) i2c_del_adapter(adap); i2c_pnx->set_clock_stop(pdev); iounmap((void *)alg_data->ioaddr); - release_region(alg_data->base, I2C_PNX_REGION_SIZE); + release_mem_region(alg_data->base, I2C_PNX_REGION_SIZE); platform_set_drvdata(pdev, NULL); return 0; diff --git a/drivers/i2c/busses/i2c-scmi.c b/drivers/i2c/busses/i2c-scmi.c new file mode 100644 index 000000000000..b4a55d407bf5 --- /dev/null +++ b/drivers/i2c/busses/i2c-scmi.c @@ -0,0 +1,429 @@ +/* + * SMBus driver for ACPI SMBus CMI + * + * Copyright (C) 2009 Crane Cai <crane.cai@amd.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation version 2. + */ + +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/kernel.h> +#include <linux/stddef.h> +#include <linux/init.h> +#include <linux/i2c.h> +#include <linux/acpi.h> + +#define ACPI_SMBUS_HC_CLASS "smbus" +#define ACPI_SMBUS_HC_DEVICE_NAME "cmi" + +ACPI_MODULE_NAME("smbus_cmi"); + +struct smbus_methods_t { + char *mt_info; + char *mt_sbr; + char *mt_sbw; +}; + +struct acpi_smbus_cmi { + acpi_handle handle; + struct i2c_adapter adapter; + u8 cap_info:1; + u8 cap_read:1; + u8 cap_write:1; +}; + +static const struct smbus_methods_t smbus_methods = { + .mt_info = "_SBI", + .mt_sbr = "_SBR", + .mt_sbw = "_SBW", +}; + +static const struct acpi_device_id acpi_smbus_cmi_ids[] = { + {"SMBUS01", 0}, + {"", 0} +}; + +#define ACPI_SMBUS_STATUS_OK 0x00 +#define ACPI_SMBUS_STATUS_FAIL 0x07 +#define ACPI_SMBUS_STATUS_DNAK 0x10 +#define ACPI_SMBUS_STATUS_DERR 0x11 +#define ACPI_SMBUS_STATUS_CMD_DENY 0x12 +#define ACPI_SMBUS_STATUS_UNKNOWN 0x13 +#define ACPI_SMBUS_STATUS_ACC_DENY 0x17 +#define ACPI_SMBUS_STATUS_TIMEOUT 0x18 +#define ACPI_SMBUS_STATUS_NOTSUP 0x19 +#define ACPI_SMBUS_STATUS_BUSY 0x1a +#define ACPI_SMBUS_STATUS_PEC 0x1f + +#define ACPI_SMBUS_PRTCL_WRITE 0x00 +#define ACPI_SMBUS_PRTCL_READ 0x01 +#define ACPI_SMBUS_PRTCL_QUICK 0x02 +#define ACPI_SMBUS_PRTCL_BYTE 0x04 +#define ACPI_SMBUS_PRTCL_BYTE_DATA 0x06 +#define ACPI_SMBUS_PRTCL_WORD_DATA 0x08 +#define ACPI_SMBUS_PRTCL_BLOCK_DATA 0x0a + + +static int +acpi_smbus_cmi_access(struct i2c_adapter *adap, u16 addr, unsigned short flags, + char read_write, u8 command, int size, + union i2c_smbus_data *data) +{ + int result = 0; + struct acpi_smbus_cmi *smbus_cmi = adap->algo_data; + unsigned char protocol; + acpi_status status = 0; + struct acpi_object_list input; + union acpi_object mt_params[5]; + struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; + union acpi_object *obj; + union acpi_object *pkg; + char *method; + int len = 0; + + dev_dbg(&adap->dev, "access size: %d %s\n", size, + (read_write) ? "READ" : "WRITE"); + switch (size) { + case I2C_SMBUS_QUICK: + protocol = ACPI_SMBUS_PRTCL_QUICK; + command = 0; + if (read_write == I2C_SMBUS_WRITE) { + mt_params[3].type = ACPI_TYPE_INTEGER; + mt_params[3].integer.value = 0; + mt_params[4].type = ACPI_TYPE_INTEGER; + mt_params[4].integer.value = 0; + } + break; + + case I2C_SMBUS_BYTE: + protocol = ACPI_SMBUS_PRTCL_BYTE; + if (read_write == I2C_SMBUS_WRITE) { + mt_params[3].type = ACPI_TYPE_INTEGER; + mt_params[3].integer.value = 0; + mt_params[4].type = ACPI_TYPE_INTEGER; + mt_params[4].integer.value = 0; + } else { + command = 0; + } + break; + + case I2C_SMBUS_BYTE_DATA: + protocol = ACPI_SMBUS_PRTCL_BYTE_DATA; + if (read_write == I2C_SMBUS_WRITE) { + mt_params[3].type = ACPI_TYPE_INTEGER; + mt_params[3].integer.value = 1; + mt_params[4].type = ACPI_TYPE_INTEGER; + mt_params[4].integer.value = data->byte; + } + break; + + case I2C_SMBUS_WORD_DATA: + protocol = ACPI_SMBUS_PRTCL_WORD_DATA; + if (read_write == I2C_SMBUS_WRITE) { + mt_params[3].type = ACPI_TYPE_INTEGER; + mt_params[3].integer.value = 2; + mt_params[4].type = ACPI_TYPE_INTEGER; + mt_params[4].integer.value = data->word; + } + break; + + case I2C_SMBUS_BLOCK_DATA: + protocol = ACPI_SMBUS_PRTCL_BLOCK_DATA; + if (read_write == I2C_SMBUS_WRITE) { + len = data->block[0]; + if (len == 0 || len > I2C_SMBUS_BLOCK_MAX) + return -EINVAL; + mt_params[3].type = ACPI_TYPE_INTEGER; + mt_params[3].integer.value = len; + mt_params[4].type = ACPI_TYPE_BUFFER; + mt_params[4].buffer.pointer = data->block + 1; + } + break; + + default: + dev_warn(&adap->dev, "Unsupported transaction %d\n", size); + return -EOPNOTSUPP; + } + + if (read_write == I2C_SMBUS_READ) { + protocol |= ACPI_SMBUS_PRTCL_READ; + method = smbus_methods.mt_sbr; + input.count = 3; + } else { + protocol |= ACPI_SMBUS_PRTCL_WRITE; + method = smbus_methods.mt_sbw; + input.count = 5; + } + + input.pointer = mt_params; + mt_params[0].type = ACPI_TYPE_INTEGER; + mt_params[0].integer.value = protocol; + mt_params[1].type = ACPI_TYPE_INTEGER; + mt_params[1].integer.value = addr; + mt_params[2].type = ACPI_TYPE_INTEGER; + mt_params[2].integer.value = command; + + status = acpi_evaluate_object(smbus_cmi->handle, method, &input, + &buffer); + if (ACPI_FAILURE(status)) { + ACPI_ERROR((AE_INFO, "Evaluating %s: %i", method, status)); + return -EIO; + } + + pkg = buffer.pointer; + if (pkg && pkg->type == ACPI_TYPE_PACKAGE) + obj = pkg->package.elements; + else { + ACPI_ERROR((AE_INFO, "Invalid argument type")); + result = -EIO; + goto out; + } + if (obj == NULL || obj->type != ACPI_TYPE_INTEGER) { + ACPI_ERROR((AE_INFO, "Invalid argument type")); + result = -EIO; + goto out; + } + + result = obj->integer.value; + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "%s return status: %i\n", + method, result)); + + switch (result) { + case ACPI_SMBUS_STATUS_OK: + result = 0; + break; + case ACPI_SMBUS_STATUS_BUSY: + result = -EBUSY; + goto out; + case ACPI_SMBUS_STATUS_TIMEOUT: + result = -ETIMEDOUT; + goto out; + case ACPI_SMBUS_STATUS_DNAK: + result = -ENXIO; + goto out; + default: + result = -EIO; + goto out; + } + + if (read_write == I2C_SMBUS_WRITE || size == I2C_SMBUS_QUICK) + goto out; + + obj = pkg->package.elements + 1; + if (obj == NULL || obj->type != ACPI_TYPE_INTEGER) { + ACPI_ERROR((AE_INFO, "Invalid argument type")); + result = -EIO; + goto out; + } + + len = obj->integer.value; + obj = pkg->package.elements + 2; + switch (size) { + case I2C_SMBUS_BYTE: + case I2C_SMBUS_BYTE_DATA: + case I2C_SMBUS_WORD_DATA: + if (obj == NULL || obj->type != ACPI_TYPE_INTEGER) { + ACPI_ERROR((AE_INFO, "Invalid argument type")); + result = -EIO; + goto out; + } + if (len == 2) + data->word = obj->integer.value; + else + data->byte = obj->integer.value; + break; + case I2C_SMBUS_BLOCK_DATA: + if (obj == NULL || obj->type != ACPI_TYPE_BUFFER) { + ACPI_ERROR((AE_INFO, "Invalid argument type")); + result = -EIO; + goto out; + } + if (len == 0 || len > I2C_SMBUS_BLOCK_MAX) + return -EPROTO; + data->block[0] = len; + memcpy(data->block + 1, obj->buffer.pointer, len); + break; + } + +out: + kfree(buffer.pointer); + dev_dbg(&adap->dev, "Transaction status: %i\n", result); + return result; +} + +static u32 acpi_smbus_cmi_func(struct i2c_adapter *adapter) +{ + struct acpi_smbus_cmi *smbus_cmi = adapter->algo_data; + u32 ret; + + ret = smbus_cmi->cap_read | smbus_cmi->cap_write ? + I2C_FUNC_SMBUS_QUICK : 0; + + ret |= smbus_cmi->cap_read ? + (I2C_FUNC_SMBUS_READ_BYTE | + I2C_FUNC_SMBUS_READ_BYTE_DATA | + I2C_FUNC_SMBUS_READ_WORD_DATA | + I2C_FUNC_SMBUS_READ_BLOCK_DATA) : 0; + + ret |= smbus_cmi->cap_write ? + (I2C_FUNC_SMBUS_WRITE_BYTE | + I2C_FUNC_SMBUS_WRITE_BYTE_DATA | + I2C_FUNC_SMBUS_WRITE_WORD_DATA | + I2C_FUNC_SMBUS_WRITE_BLOCK_DATA) : 0; + + return ret; +} + +static const struct i2c_algorithm acpi_smbus_cmi_algorithm = { + .smbus_xfer = acpi_smbus_cmi_access, + .functionality = acpi_smbus_cmi_func, +}; + + +static int acpi_smbus_cmi_add_cap(struct acpi_smbus_cmi *smbus_cmi, + const char *name) +{ + struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; + union acpi_object *obj; + acpi_status status; + + if (!strcmp(name, smbus_methods.mt_info)) { + status = acpi_evaluate_object(smbus_cmi->handle, + smbus_methods.mt_info, + NULL, &buffer); + if (ACPI_FAILURE(status)) { + ACPI_ERROR((AE_INFO, "Evaluating %s: %i", + smbus_methods.mt_info, status)); + return -EIO; + } + + obj = buffer.pointer; + if (obj && obj->type == ACPI_TYPE_PACKAGE) + obj = obj->package.elements; + else { + ACPI_ERROR((AE_INFO, "Invalid argument type")); + kfree(buffer.pointer); + return -EIO; + } + + if (obj->type != ACPI_TYPE_INTEGER) { + ACPI_ERROR((AE_INFO, "Invalid argument type")); + kfree(buffer.pointer); + return -EIO; + } else + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "SMBus CMI Version %x" + "\n", (int)obj->integer.value)); + + kfree(buffer.pointer); + smbus_cmi->cap_info = 1; + } else if (!strcmp(name, smbus_methods.mt_sbr)) + smbus_cmi->cap_read = 1; + else if (!strcmp(name, smbus_methods.mt_sbw)) + smbus_cmi->cap_write = 1; + else + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Unsupported CMI method: %s\n", + name)); + + return 0; +} + +static acpi_status acpi_smbus_cmi_query_methods(acpi_handle handle, u32 level, + void *context, void **return_value) +{ + char node_name[5]; + struct acpi_buffer buffer = { sizeof(node_name), node_name }; + struct acpi_smbus_cmi *smbus_cmi = context; + acpi_status status; + + status = acpi_get_name(handle, ACPI_SINGLE_NAME, &buffer); + + if (ACPI_SUCCESS(status)) + acpi_smbus_cmi_add_cap(smbus_cmi, node_name); + + return AE_OK; +} + +static int acpi_smbus_cmi_add(struct acpi_device *device) +{ + struct acpi_smbus_cmi *smbus_cmi; + + smbus_cmi = kzalloc(sizeof(struct acpi_smbus_cmi), GFP_KERNEL); + if (!smbus_cmi) + return -ENOMEM; + + smbus_cmi->handle = device->handle; + strcpy(acpi_device_name(device), ACPI_SMBUS_HC_DEVICE_NAME); + strcpy(acpi_device_class(device), ACPI_SMBUS_HC_CLASS); + device->driver_data = smbus_cmi; + smbus_cmi->cap_info = 0; + smbus_cmi->cap_read = 0; + smbus_cmi->cap_write = 0; + + acpi_walk_namespace(ACPI_TYPE_METHOD, smbus_cmi->handle, 1, + acpi_smbus_cmi_query_methods, smbus_cmi, NULL); + + if (smbus_cmi->cap_info == 0) + goto err; + + snprintf(smbus_cmi->adapter.name, sizeof(smbus_cmi->adapter.name), + "SMBus CMI adapter %s", + acpi_device_name(device)); + smbus_cmi->adapter.owner = THIS_MODULE; + smbus_cmi->adapter.algo = &acpi_smbus_cmi_algorithm; + smbus_cmi->adapter.algo_data = smbus_cmi; + smbus_cmi->adapter.class = I2C_CLASS_HWMON | I2C_CLASS_SPD; + smbus_cmi->adapter.dev.parent = &device->dev; + + if (i2c_add_adapter(&smbus_cmi->adapter)) { + dev_err(&device->dev, "Couldn't register adapter!\n"); + goto err; + } + + return 0; + +err: + kfree(smbus_cmi); + device->driver_data = NULL; + return -EIO; +} + +static int acpi_smbus_cmi_remove(struct acpi_device *device, int type) +{ + struct acpi_smbus_cmi *smbus_cmi = acpi_driver_data(device); + + i2c_del_adapter(&smbus_cmi->adapter); + kfree(smbus_cmi); + device->driver_data = NULL; + + return 0; +} + +static struct acpi_driver acpi_smbus_cmi_driver = { + .name = ACPI_SMBUS_HC_DEVICE_NAME, + .class = ACPI_SMBUS_HC_CLASS, + .ids = acpi_smbus_cmi_ids, + .ops = { + .add = acpi_smbus_cmi_add, + .remove = acpi_smbus_cmi_remove, + }, +}; + +static int __init acpi_smbus_cmi_init(void) +{ + return acpi_bus_register_driver(&acpi_smbus_cmi_driver); +} + +static void __exit acpi_smbus_cmi_exit(void) +{ + acpi_bus_unregister_driver(&acpi_smbus_cmi_driver); +} + +module_init(acpi_smbus_cmi_init); +module_exit(acpi_smbus_cmi_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Crane Cai <crane.cai@amd.com>"); +MODULE_DESCRIPTION("ACPI SMBus CMI driver"); diff --git a/drivers/i2c/busses/i2c-sis96x.c b/drivers/i2c/busses/i2c-sis96x.c index 8295885b2fdb..1649963b00dc 100644 --- a/drivers/i2c/busses/i2c-sis96x.c +++ b/drivers/i2c/busses/i2c-sis96x.c @@ -280,7 +280,7 @@ static int __devinit sis96x_probe(struct pci_dev *dev, retval = acpi_check_resource_conflict(&dev->resource[SIS96x_BAR]); if (retval) - return retval; + return -ENODEV; /* Everything is happy, let's grab the memory and set things up. */ if (!request_region(sis96x_smbus_base, SMB_IOSIZE, diff --git a/drivers/i2c/busses/i2c-taos-evm.c b/drivers/i2c/busses/i2c-taos-evm.c index 224aa12ee7c8..dd39c1eb03ed 100644 --- a/drivers/i2c/busses/i2c-taos-evm.c +++ b/drivers/i2c/busses/i2c-taos-evm.c @@ -32,10 +32,12 @@ #define TAOS_STATE_INIT 0 #define TAOS_STATE_IDLE 1 -#define TAOS_STATE_SEND 2 +#define TAOS_STATE_EOFF 2 #define TAOS_STATE_RECV 3 #define TAOS_CMD_RESET 0x12 +#define TAOS_CMD_ECHO_ON '+' +#define TAOS_CMD_ECHO_OFF '-' static DECLARE_WAIT_QUEUE_HEAD(wq); @@ -102,17 +104,9 @@ static int taos_smbus_xfer(struct i2c_adapter *adapter, u16 addr, /* Send the transaction to the TAOS EVM */ dev_dbg(&adapter->dev, "Command buffer: %s\n", taos->buffer); - taos->pos = 0; - taos->state = TAOS_STATE_SEND; - serio_write(serio, taos->buffer[0]); - wait_event_interruptible_timeout(wq, taos->state == TAOS_STATE_IDLE, - msecs_to_jiffies(250)); - if (taos->state != TAOS_STATE_IDLE) { - dev_err(&adapter->dev, "Transaction failed " - "(state=%d, pos=%d)\n", taos->state, taos->pos); - taos->addr = 0; - return -EIO; - } + for (p = taos->buffer; *p; p++) + serio_write(serio, *p); + taos->addr = addr; /* Start the transaction and read the answer */ @@ -122,7 +116,7 @@ static int taos_smbus_xfer(struct i2c_adapter *adapter, u16 addr, wait_event_interruptible_timeout(wq, taos->state == TAOS_STATE_IDLE, msecs_to_jiffies(150)); if (taos->state != TAOS_STATE_IDLE - || taos->pos != 6) { + || taos->pos != 5) { dev_err(&adapter->dev, "Transaction timeout (pos=%d)\n", taos->pos); return -EIO; @@ -130,7 +124,7 @@ static int taos_smbus_xfer(struct i2c_adapter *adapter, u16 addr, dev_dbg(&adapter->dev, "Answer buffer: %s\n", taos->buffer); /* Interpret the returned string */ - p = taos->buffer + 2; + p = taos->buffer + 1; p[3] = '\0'; if (!strcmp(p, "NAK")) return -ENODEV; @@ -173,13 +167,9 @@ static irqreturn_t taos_interrupt(struct serio *serio, unsigned char data, wake_up_interruptible(&wq); } break; - case TAOS_STATE_SEND: - if (taos->buffer[++taos->pos]) - serio_write(serio, taos->buffer[taos->pos]); - else { - taos->state = TAOS_STATE_IDLE; - wake_up_interruptible(&wq); - } + case TAOS_STATE_EOFF: + taos->state = TAOS_STATE_IDLE; + wake_up_interruptible(&wq); break; case TAOS_STATE_RECV: taos->buffer[taos->pos++] = data; @@ -257,6 +247,19 @@ static int taos_connect(struct serio *serio, struct serio_driver *drv) } strlcpy(adapter->name, name, sizeof(adapter->name)); + /* Turn echo off for better performance */ + taos->state = TAOS_STATE_EOFF; + serio_write(serio, TAOS_CMD_ECHO_OFF); + + wait_event_interruptible_timeout(wq, taos->state == TAOS_STATE_IDLE, + msecs_to_jiffies(250)); + if (taos->state != TAOS_STATE_IDLE) { + err = -ENODEV; + dev_err(&adapter->dev, "Echo off failed " + "(state=%d)\n", taos->state); + goto exit_close; + } + err = i2c_add_adapter(adapter); if (err) goto exit_close; diff --git a/drivers/i2c/busses/i2c-viapro.c b/drivers/i2c/busses/i2c-viapro.c index 54d810a4d00f..e4b1543015af 100644 --- a/drivers/i2c/busses/i2c-viapro.c +++ b/drivers/i2c/busses/i2c-viapro.c @@ -365,7 +365,7 @@ static int __devinit vt596_probe(struct pci_dev *pdev, found: error = acpi_check_region(vt596_smba, 8, vt596_driver.name); if (error) - return error; + return -ENODEV; if (!request_region(vt596_smba, 8, vt596_driver.name)) { dev_err(&pdev->dev, "SMBus region 0x%x already in use!\n", diff --git a/drivers/i2c/busses/scx200_acb.c b/drivers/i2c/busses/scx200_acb.c index 648ecc6f60e6..cf994bd01d9c 100644 --- a/drivers/i2c/busses/scx200_acb.c +++ b/drivers/i2c/busses/scx200_acb.c @@ -217,8 +217,10 @@ static void scx200_acb_machine(struct scx200_acb_iface *iface, u8 status) return; error: - dev_err(&iface->adapter.dev, "%s in state %s\n", errmsg, - scx200_acb_state_name[iface->state]); + dev_err(&iface->adapter.dev, + "%s in state %s (addr=0x%02x, len=%d, status=0x%02x)\n", errmsg, + scx200_acb_state_name[iface->state], iface->address_byte, + iface->len, status); iface->state = state_idle; iface->result = -EIO; diff --git a/drivers/i2c/chips/Kconfig b/drivers/i2c/chips/Kconfig index 02d746c9c474..f9618f4d4e47 100644 --- a/drivers/i2c/chips/Kconfig +++ b/drivers/i2c/chips/Kconfig @@ -16,54 +16,6 @@ config DS1682 This driver can also be built as a module. If so, the module will be called ds1682. -config SENSORS_PCF8574 - tristate "Philips PCF8574 and PCF8574A (DEPRECATED)" - depends on EXPERIMENTAL && GPIO_PCF857X = "n" - default n - help - If you say yes here you get support for Philips PCF8574 and - PCF8574A chips. These chips are 8-bit I/O expanders for the I2C bus. - - This driver can also be built as a module. If so, the module - will be called pcf8574. - - This driver is deprecated and will be dropped soon. Use - drivers/gpio/pcf857x.c instead. - - These devices are hard to detect and rarely found on mainstream - hardware. If unsure, say N. - -config PCF8575 - tristate "Philips PCF8575 (DEPRECATED)" - default n - depends on GPIO_PCF857X = "n" - help - If you say yes here you get support for Philips PCF8575 chip. - This chip is a 16-bit I/O expander for the I2C bus. Several other - chip manufacturers sell equivalent chips, e.g. Texas Instruments. - - This driver can also be built as a module. If so, the module - will be called pcf8575. - - This driver is deprecated and will be dropped soon. Use - drivers/gpio/pcf857x.c instead. - - This device is hard to detect and is rarely found on mainstream - hardware. If unsure, say N. - -config SENSORS_PCA9539 - tristate "Philips PCA9539 16-bit I/O port (DEPRECATED)" - depends on EXPERIMENTAL && GPIO_PCA953X = "n" - help - If you say yes here you get support for the Philips PCA9539 - 16-bit I/O port. - - This driver can also be built as a module. If so, the module - will be called pca9539. - - This driver is deprecated and will be dropped soon. Use - drivers/gpio/pca953x.c instead. - config SENSORS_TSL2550 tristate "Taos TSL2550 ambient light sensor" depends on EXPERIMENTAL diff --git a/drivers/i2c/chips/Makefile b/drivers/i2c/chips/Makefile index f4680d16ee34..749cf3606294 100644 --- a/drivers/i2c/chips/Makefile +++ b/drivers/i2c/chips/Makefile @@ -11,9 +11,6 @@ # obj-$(CONFIG_DS1682) += ds1682.o -obj-$(CONFIG_SENSORS_PCA9539) += pca9539.o -obj-$(CONFIG_SENSORS_PCF8574) += pcf8574.o -obj-$(CONFIG_PCF8575) += pcf8575.o obj-$(CONFIG_SENSORS_TSL2550) += tsl2550.o ifeq ($(CONFIG_I2C_DEBUG_CHIP),y) diff --git a/drivers/i2c/chips/pca9539.c b/drivers/i2c/chips/pca9539.c deleted file mode 100644 index 270de4e56a81..000000000000 --- a/drivers/i2c/chips/pca9539.c +++ /dev/null @@ -1,152 +0,0 @@ -/* - pca9539.c - 16-bit I/O port with interrupt and reset - - Copyright (C) 2005 Ben Gardner <bgardner@wabtec.com> - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; version 2 of the License. -*/ - -#include <linux/module.h> -#include <linux/init.h> -#include <linux/slab.h> -#include <linux/i2c.h> -#include <linux/hwmon-sysfs.h> - -/* Addresses to scan: none, device is not autodetected */ -static const unsigned short normal_i2c[] = { I2C_CLIENT_END }; - -/* Insmod parameters */ -I2C_CLIENT_INSMOD_1(pca9539); - -enum pca9539_cmd -{ - PCA9539_INPUT_0 = 0, - PCA9539_INPUT_1 = 1, - PCA9539_OUTPUT_0 = 2, - PCA9539_OUTPUT_1 = 3, - PCA9539_INVERT_0 = 4, - PCA9539_INVERT_1 = 5, - PCA9539_DIRECTION_0 = 6, - PCA9539_DIRECTION_1 = 7, -}; - -/* following are the sysfs callback functions */ -static ssize_t pca9539_show(struct device *dev, struct device_attribute *attr, - char *buf) -{ - struct sensor_device_attribute *psa = to_sensor_dev_attr(attr); - struct i2c_client *client = to_i2c_client(dev); - return sprintf(buf, "%d\n", i2c_smbus_read_byte_data(client, - psa->index)); -} - -static ssize_t pca9539_store(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) -{ - struct sensor_device_attribute *psa = to_sensor_dev_attr(attr); - struct i2c_client *client = to_i2c_client(dev); - unsigned long val = simple_strtoul(buf, NULL, 0); - if (val > 0xff) - return -EINVAL; - i2c_smbus_write_byte_data(client, psa->index, val); - return count; -} - -/* Define the device attributes */ - -#define PCA9539_ENTRY_RO(name, cmd_idx) \ - static SENSOR_DEVICE_ATTR(name, S_IRUGO, pca9539_show, NULL, cmd_idx) - -#define PCA9539_ENTRY_RW(name, cmd_idx) \ - static SENSOR_DEVICE_ATTR(name, S_IRUGO | S_IWUSR, pca9539_show, \ - pca9539_store, cmd_idx) - -PCA9539_ENTRY_RO(input0, PCA9539_INPUT_0); -PCA9539_ENTRY_RO(input1, PCA9539_INPUT_1); -PCA9539_ENTRY_RW(output0, PCA9539_OUTPUT_0); -PCA9539_ENTRY_RW(output1, PCA9539_OUTPUT_1); -PCA9539_ENTRY_RW(invert0, PCA9539_INVERT_0); -PCA9539_ENTRY_RW(invert1, PCA9539_INVERT_1); -PCA9539_ENTRY_RW(direction0, PCA9539_DIRECTION_0); -PCA9539_ENTRY_RW(direction1, PCA9539_DIRECTION_1); - -static struct attribute *pca9539_attributes[] = { - &sensor_dev_attr_input0.dev_attr.attr, - &sensor_dev_attr_input1.dev_attr.attr, - &sensor_dev_attr_output0.dev_attr.attr, - &sensor_dev_attr_output1.dev_attr.attr, - &sensor_dev_attr_invert0.dev_attr.attr, - &sensor_dev_attr_invert1.dev_attr.attr, - &sensor_dev_attr_direction0.dev_attr.attr, - &sensor_dev_attr_direction1.dev_attr.attr, - NULL -}; - -static struct attribute_group pca9539_defattr_group = { - .attrs = pca9539_attributes, -}; - -/* Return 0 if detection is successful, -ENODEV otherwise */ -static int pca9539_detect(struct i2c_client *client, int kind, - struct i2c_board_info *info) -{ - struct i2c_adapter *adapter = client->adapter; - - if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) - return -ENODEV; - - strlcpy(info->type, "pca9539", I2C_NAME_SIZE); - - return 0; -} - -static int pca9539_probe(struct i2c_client *client, - const struct i2c_device_id *id) -{ - /* Register sysfs hooks */ - return sysfs_create_group(&client->dev.kobj, - &pca9539_defattr_group); -} - -static int pca9539_remove(struct i2c_client *client) -{ - sysfs_remove_group(&client->dev.kobj, &pca9539_defattr_group); - return 0; -} - -static const struct i2c_device_id pca9539_id[] = { - { "pca9539", 0 }, - { } -}; - -static struct i2c_driver pca9539_driver = { - .driver = { - .name = "pca9539", - }, - .probe = pca9539_probe, - .remove = pca9539_remove, - .id_table = pca9539_id, - - .detect = pca9539_detect, - .address_data = &addr_data, -}; - -static int __init pca9539_init(void) -{ - return i2c_add_driver(&pca9539_driver); -} - -static void __exit pca9539_exit(void) -{ - i2c_del_driver(&pca9539_driver); -} - -MODULE_AUTHOR("Ben Gardner <bgardner@wabtec.com>"); -MODULE_DESCRIPTION("PCA9539 driver"); -MODULE_LICENSE("GPL"); - -module_init(pca9539_init); -module_exit(pca9539_exit); - diff --git a/drivers/i2c/chips/pcf8574.c b/drivers/i2c/chips/pcf8574.c deleted file mode 100644 index 6ec309894c88..000000000000 --- a/drivers/i2c/chips/pcf8574.c +++ /dev/null @@ -1,215 +0,0 @@ -/* - Copyright (c) 2000 Frodo Looijaard <frodol@dds.nl>, - Philip Edelbrock <phil@netroedge.com>, - Dan Eaton <dan.eaton@rocketlogix.com> - Ported to Linux 2.6 by Aurelien Jarno <aurel32@debian.org> with - the help of Jean Delvare <khali@linux-fr.org> - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -*/ - -/* A few notes about the PCF8574: - -* The PCF8574 is an 8-bit I/O expander for the I2C bus produced by - Philips Semiconductors. It is designed to provide a byte I2C - interface to up to 8 separate devices. - -* The PCF8574 appears as a very simple SMBus device which can be - read from or written to with SMBUS byte read/write accesses. - - --Dan - -*/ - -#include <linux/module.h> -#include <linux/init.h> -#include <linux/slab.h> -#include <linux/i2c.h> - -/* Addresses to scan: none, device can't be detected */ -static const unsigned short normal_i2c[] = { I2C_CLIENT_END }; - -/* Insmod parameters */ -I2C_CLIENT_INSMOD_2(pcf8574, pcf8574a); - -/* Each client has this additional data */ -struct pcf8574_data { - int write; /* Remember last written value */ -}; - -static void pcf8574_init_client(struct i2c_client *client); - -/* following are the sysfs callback functions */ -static ssize_t show_read(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct i2c_client *client = to_i2c_client(dev); - return sprintf(buf, "%u\n", i2c_smbus_read_byte(client)); -} - -static DEVICE_ATTR(read, S_IRUGO, show_read, NULL); - -static ssize_t show_write(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct pcf8574_data *data = i2c_get_clientdata(to_i2c_client(dev)); - - if (data->write < 0) - return data->write; - - return sprintf(buf, "%d\n", data->write); -} - -static ssize_t set_write(struct device *dev, struct device_attribute *attr, const char *buf, - size_t count) -{ - struct i2c_client *client = to_i2c_client(dev); - struct pcf8574_data *data = i2c_get_clientdata(client); - unsigned long val = simple_strtoul(buf, NULL, 10); - - if (val > 0xff) - return -EINVAL; - - data->write = val; - i2c_smbus_write_byte(client, data->write); - return count; -} - -static DEVICE_ATTR(write, S_IWUSR | S_IRUGO, show_write, set_write); - -static struct attribute *pcf8574_attributes[] = { - &dev_attr_read.attr, - &dev_attr_write.attr, - NULL -}; - -static const struct attribute_group pcf8574_attr_group = { - .attrs = pcf8574_attributes, -}; - -/* - * Real code - */ - -/* Return 0 if detection is successful, -ENODEV otherwise */ -static int pcf8574_detect(struct i2c_client *client, int kind, - struct i2c_board_info *info) -{ - struct i2c_adapter *adapter = client->adapter; - const char *client_name; - - if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE)) - return -ENODEV; - - /* Now, we would do the remaining detection. But the PCF8574 is plainly - impossible to detect! Stupid chip. */ - - /* Determine the chip type */ - if (kind <= 0) { - if (client->addr >= 0x38 && client->addr <= 0x3f) - kind = pcf8574a; - else - kind = pcf8574; - } - - if (kind == pcf8574a) - client_name = "pcf8574a"; - else - client_name = "pcf8574"; - strlcpy(info->type, client_name, I2C_NAME_SIZE); - - return 0; -} - -static int pcf8574_probe(struct i2c_client *client, - const struct i2c_device_id *id) -{ - struct pcf8574_data *data; - int err; - - data = kzalloc(sizeof(struct pcf8574_data), GFP_KERNEL); - if (!data) { - err = -ENOMEM; - goto exit; - } - - i2c_set_clientdata(client, data); - - /* Initialize the PCF8574 chip */ - pcf8574_init_client(client); - - /* Register sysfs hooks */ - err = sysfs_create_group(&client->dev.kobj, &pcf8574_attr_group); - if (err) - goto exit_free; - return 0; - - exit_free: - kfree(data); - exit: - return err; -} - -static int pcf8574_remove(struct i2c_client *client) -{ - sysfs_remove_group(&client->dev.kobj, &pcf8574_attr_group); - kfree(i2c_get_clientdata(client)); - return 0; -} - -/* Called when we have found a new PCF8574. */ -static void pcf8574_init_client(struct i2c_client *client) -{ - struct pcf8574_data *data = i2c_get_clientdata(client); - data->write = -EAGAIN; -} - -static const struct i2c_device_id pcf8574_id[] = { - { "pcf8574", 0 }, - { "pcf8574a", 0 }, - { } -}; - -static struct i2c_driver pcf8574_driver = { - .driver = { - .name = "pcf8574", - }, - .probe = pcf8574_probe, - .remove = pcf8574_remove, - .id_table = pcf8574_id, - - .detect = pcf8574_detect, - .address_data = &addr_data, -}; - -static int __init pcf8574_init(void) -{ - return i2c_add_driver(&pcf8574_driver); -} - -static void __exit pcf8574_exit(void) -{ - i2c_del_driver(&pcf8574_driver); -} - - -MODULE_AUTHOR - ("Frodo Looijaard <frodol@dds.nl>, " - "Philip Edelbrock <phil@netroedge.com>, " - "Dan Eaton <dan.eaton@rocketlogix.com> " - "and Aurelien Jarno <aurelien@aurel32.net>"); -MODULE_DESCRIPTION("PCF8574 driver"); -MODULE_LICENSE("GPL"); - -module_init(pcf8574_init); -module_exit(pcf8574_exit); diff --git a/drivers/i2c/chips/pcf8575.c b/drivers/i2c/chips/pcf8575.c deleted file mode 100644 index 07fd7cb3c57d..000000000000 --- a/drivers/i2c/chips/pcf8575.c +++ /dev/null @@ -1,198 +0,0 @@ -/* - pcf8575.c - - About the PCF8575 chip: the PCF8575 is a 16-bit I/O expander for the I2C bus - produced by a.o. Philips Semiconductors. - - Copyright (C) 2006 Michael Hennerich, Analog Devices Inc. - <hennerich@blackfin.uclinux.org> - Based on pcf8574.c. - - Copyright (c) 2007 Bart Van Assche <bart.vanassche@gmail.com>. - Ported this driver from ucLinux to the mainstream Linux kernel. - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -*/ - -#include <linux/module.h> -#include <linux/init.h> -#include <linux/i2c.h> -#include <linux/slab.h> /* kzalloc() */ -#include <linux/sysfs.h> /* sysfs_create_group() */ - -/* Addresses to scan: none, device can't be detected */ -static const unsigned short normal_i2c[] = { I2C_CLIENT_END }; - -/* Insmod parameters */ -I2C_CLIENT_INSMOD; - - -/* Each client has this additional data */ -struct pcf8575_data { - int write; /* last written value, or error code */ -}; - -/* following are the sysfs callback functions */ -static ssize_t show_read(struct device *dev, struct device_attribute *attr, - char *buf) -{ - struct i2c_client *client = to_i2c_client(dev); - u16 val; - u8 iopin_state[2]; - - i2c_master_recv(client, iopin_state, 2); - - val = iopin_state[0]; - val |= iopin_state[1] << 8; - - return sprintf(buf, "%u\n", val); -} - -static DEVICE_ATTR(read, S_IRUGO, show_read, NULL); - -static ssize_t show_write(struct device *dev, struct device_attribute *attr, - char *buf) -{ - struct pcf8575_data *data = dev_get_drvdata(dev); - if (data->write < 0) - return data->write; - return sprintf(buf, "%d\n", data->write); -} - -static ssize_t set_write(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) -{ - struct i2c_client *client = to_i2c_client(dev); - struct pcf8575_data *data = i2c_get_clientdata(client); - unsigned long val = simple_strtoul(buf, NULL, 10); - u8 iopin_state[2]; - - if (val > 0xffff) - return -EINVAL; - - data->write = val; - - iopin_state[0] = val & 0xFF; - iopin_state[1] = val >> 8; - - i2c_master_send(client, iopin_state, 2); - - return count; -} - -static DEVICE_ATTR(write, S_IWUSR | S_IRUGO, show_write, set_write); - -static struct attribute *pcf8575_attributes[] = { - &dev_attr_read.attr, - &dev_attr_write.attr, - NULL -}; - -static const struct attribute_group pcf8575_attr_group = { - .attrs = pcf8575_attributes, -}; - -/* - * Real code - */ - -/* Return 0 if detection is successful, -ENODEV otherwise */ -static int pcf8575_detect(struct i2c_client *client, int kind, - struct i2c_board_info *info) -{ - struct i2c_adapter *adapter = client->adapter; - - if (!i2c_check_functionality(adapter, I2C_FUNC_I2C)) - return -ENODEV; - - /* This is the place to detect whether the chip at the specified - address really is a PCF8575 chip. However, there is no method known - to detect whether an I2C chip is a PCF8575 or any other I2C chip. */ - - strlcpy(info->type, "pcf8575", I2C_NAME_SIZE); - - return 0; -} - -static int pcf8575_probe(struct i2c_client *client, - const struct i2c_device_id *id) -{ - struct pcf8575_data *data; - int err; - - data = kzalloc(sizeof(struct pcf8575_data), GFP_KERNEL); - if (!data) { - err = -ENOMEM; - goto exit; - } - - i2c_set_clientdata(client, data); - data->write = -EAGAIN; - - /* Register sysfs hooks */ - err = sysfs_create_group(&client->dev.kobj, &pcf8575_attr_group); - if (err) - goto exit_free; - - return 0; - -exit_free: - kfree(data); -exit: - return err; -} - -static int pcf8575_remove(struct i2c_client *client) -{ - sysfs_remove_group(&client->dev.kobj, &pcf8575_attr_group); - kfree(i2c_get_clientdata(client)); - return 0; -} - -static const struct i2c_device_id pcf8575_id[] = { - { "pcf8575", 0 }, - { } -}; - -static struct i2c_driver pcf8575_driver = { - .driver = { - .owner = THIS_MODULE, - .name = "pcf8575", - }, - .probe = pcf8575_probe, - .remove = pcf8575_remove, - .id_table = pcf8575_id, - - .detect = pcf8575_detect, - .address_data = &addr_data, -}; - -static int __init pcf8575_init(void) -{ - return i2c_add_driver(&pcf8575_driver); -} - -static void __exit pcf8575_exit(void) -{ - i2c_del_driver(&pcf8575_driver); -} - -MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>, " - "Bart Van Assche <bart.vanassche@gmail.com>"); -MODULE_DESCRIPTION("pcf8575 driver"); -MODULE_LICENSE("GPL"); - -module_init(pcf8575_init); -module_exit(pcf8575_exit); diff --git a/drivers/i2c/chips/tsl2550.c b/drivers/i2c/chips/tsl2550.c index b96f3025e588..aa96bd2d27ea 100644 --- a/drivers/i2c/chips/tsl2550.c +++ b/drivers/i2c/chips/tsl2550.c @@ -24,10 +24,9 @@ #include <linux/slab.h> #include <linux/i2c.h> #include <linux/mutex.h> -#include <linux/delay.h> #define TSL2550_DRV_NAME "tsl2550" -#define DRIVER_VERSION "1.1.2" +#define DRIVER_VERSION "1.2" /* * Defines @@ -96,32 +95,13 @@ static int tsl2550_set_power_state(struct i2c_client *client, int state) static int tsl2550_get_adc_value(struct i2c_client *client, u8 cmd) { - unsigned long end; - int loop = 0, ret = 0; + int ret; - /* - * Read ADC channel waiting at most 400ms (see data sheet for further - * info). - * To avoid long busy wait we spin for few milliseconds then - * start sleeping. - */ - end = jiffies + msecs_to_jiffies(400); - while (time_before(jiffies, end)) { - i2c_smbus_write_byte(client, cmd); - - if (loop++ < 5) - mdelay(1); - else - msleep(1); - - ret = i2c_smbus_read_byte(client); - if (ret < 0) - return ret; - else if (ret & 0x0080) - break; - } + ret = i2c_smbus_read_byte_data(client, cmd); + if (ret < 0) + return ret; if (!(ret & 0x80)) - return -EIO; + return -EAGAIN; return ret & 0x7f; /* remove the "valid" bit */ } @@ -285,8 +265,6 @@ static ssize_t __tsl2550_show_lux(struct i2c_client *client, char *buf) return ret; ch0 = ret; - mdelay(1); - ret = tsl2550_get_adc_value(client, TSL2550_READ_ADC1); if (ret < 0) return ret; @@ -345,11 +323,10 @@ static int tsl2550_init_client(struct i2c_client *client) * Probe the chip. To do so we try to power up the device and then to * read back the 0x03 code */ - err = i2c_smbus_write_byte(client, TSL2550_POWER_UP); + err = i2c_smbus_read_byte_data(client, TSL2550_POWER_UP); if (err < 0) return err; - mdelay(1); - if (i2c_smbus_read_byte(client) != TSL2550_POWER_UP) + if (err != TSL2550_POWER_UP) return -ENODEV; data->power_state = 1; @@ -374,7 +351,8 @@ static int __devinit tsl2550_probe(struct i2c_client *client, struct tsl2550_data *data; int *opmode, err = 0; - if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE)) { + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WRITE_BYTE + | I2C_FUNC_SMBUS_READ_BYTE_DATA)) { err = -EIO; goto exit; } diff --git a/drivers/i2c/i2c-core.c b/drivers/i2c/i2c-core.c index 0e45c296d3d2..8d80fceca6a4 100644 --- a/drivers/i2c/i2c-core.c +++ b/drivers/i2c/i2c-core.c @@ -46,6 +46,7 @@ static DEFINE_MUTEX(core_lock); static DEFINE_IDR(i2c_adapter_idr); static LIST_HEAD(userspace_devices); +static struct device_type i2c_client_type; static int i2c_check_addr(struct i2c_adapter *adapter, int addr); static int i2c_detect(struct i2c_adapter *adapter, struct i2c_driver *driver); @@ -64,9 +65,13 @@ static const struct i2c_device_id *i2c_match_id(const struct i2c_device_id *id, static int i2c_device_match(struct device *dev, struct device_driver *drv) { - struct i2c_client *client = to_i2c_client(dev); - struct i2c_driver *driver = to_i2c_driver(drv); + struct i2c_client *client = i2c_verify_client(dev); + struct i2c_driver *driver; + + if (!client) + return 0; + driver = to_i2c_driver(drv); /* match on an id table if there is one */ if (driver->id_table) return i2c_match_id(driver->id_table, client) != NULL; @@ -94,10 +99,14 @@ static int i2c_device_uevent(struct device *dev, struct kobj_uevent_env *env) static int i2c_device_probe(struct device *dev) { - struct i2c_client *client = to_i2c_client(dev); - struct i2c_driver *driver = to_i2c_driver(dev->driver); + struct i2c_client *client = i2c_verify_client(dev); + struct i2c_driver *driver; int status; + if (!client) + return 0; + + driver = to_i2c_driver(dev->driver); if (!driver->probe || !driver->id_table) return -ENODEV; client->driver = driver; @@ -114,11 +123,11 @@ static int i2c_device_probe(struct device *dev) static int i2c_device_remove(struct device *dev) { - struct i2c_client *client = to_i2c_client(dev); + struct i2c_client *client = i2c_verify_client(dev); struct i2c_driver *driver; int status; - if (!dev->driver) + if (!client || !dev->driver) return 0; driver = to_i2c_driver(dev->driver); @@ -136,37 +145,40 @@ static int i2c_device_remove(struct device *dev) static void i2c_device_shutdown(struct device *dev) { + struct i2c_client *client = i2c_verify_client(dev); struct i2c_driver *driver; - if (!dev->driver) + if (!client || !dev->driver) return; driver = to_i2c_driver(dev->driver); if (driver->shutdown) - driver->shutdown(to_i2c_client(dev)); + driver->shutdown(client); } static int i2c_device_suspend(struct device *dev, pm_message_t mesg) { + struct i2c_client *client = i2c_verify_client(dev); struct i2c_driver *driver; - if (!dev->driver) + if (!client || !dev->driver) return 0; driver = to_i2c_driver(dev->driver); if (!driver->suspend) return 0; - return driver->suspend(to_i2c_client(dev), mesg); + return driver->suspend(client, mesg); } static int i2c_device_resume(struct device *dev) { + struct i2c_client *client = i2c_verify_client(dev); struct i2c_driver *driver; - if (!dev->driver) + if (!client || !dev->driver) return 0; driver = to_i2c_driver(dev->driver); if (!driver->resume) return 0; - return driver->resume(to_i2c_client(dev)); + return driver->resume(client); } static void i2c_client_dev_release(struct device *dev) @@ -175,10 +187,10 @@ static void i2c_client_dev_release(struct device *dev) } static ssize_t -show_client_name(struct device *dev, struct device_attribute *attr, char *buf) +show_name(struct device *dev, struct device_attribute *attr, char *buf) { - struct i2c_client *client = to_i2c_client(dev); - return sprintf(buf, "%s\n", client->name); + return sprintf(buf, "%s\n", dev->type == &i2c_client_type ? + to_i2c_client(dev)->name : to_i2c_adapter(dev)->name); } static ssize_t @@ -188,18 +200,28 @@ show_modalias(struct device *dev, struct device_attribute *attr, char *buf) return sprintf(buf, "%s%s\n", I2C_MODULE_PREFIX, client->name); } -static struct device_attribute i2c_dev_attrs[] = { - __ATTR(name, S_IRUGO, show_client_name, NULL), +static DEVICE_ATTR(name, S_IRUGO, show_name, NULL); +static DEVICE_ATTR(modalias, S_IRUGO, show_modalias, NULL); + +static struct attribute *i2c_dev_attrs[] = { + &dev_attr_name.attr, /* modalias helps coldplug: modprobe $(cat .../modalias) */ - __ATTR(modalias, S_IRUGO, show_modalias, NULL), - { }, + &dev_attr_modalias.attr, + NULL +}; + +static struct attribute_group i2c_dev_attr_group = { + .attrs = i2c_dev_attrs, +}; + +static const struct attribute_group *i2c_dev_attr_groups[] = { + &i2c_dev_attr_group, + NULL }; struct bus_type i2c_bus_type = { .name = "i2c", - .dev_attrs = i2c_dev_attrs, .match = i2c_device_match, - .uevent = i2c_device_uevent, .probe = i2c_device_probe, .remove = i2c_device_remove, .shutdown = i2c_device_shutdown, @@ -208,6 +230,12 @@ struct bus_type i2c_bus_type = { }; EXPORT_SYMBOL_GPL(i2c_bus_type); +static struct device_type i2c_client_type = { + .groups = i2c_dev_attr_groups, + .uevent = i2c_device_uevent, + .release = i2c_client_dev_release, +}; + /** * i2c_verify_client - return parameter as i2c_client, or NULL @@ -220,7 +248,7 @@ EXPORT_SYMBOL_GPL(i2c_bus_type); */ struct i2c_client *i2c_verify_client(struct device *dev) { - return (dev->bus == &i2c_bus_type) + return (dev->type == &i2c_client_type) ? to_i2c_client(dev) : NULL; } @@ -273,7 +301,7 @@ i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info) client->dev.parent = &client->adapter->dev; client->dev.bus = &i2c_bus_type; - client->dev.release = i2c_client_dev_release; + client->dev.type = &i2c_client_type; dev_set_name(&client->dev, "%d-%04x", i2c_adapter_id(adap), client->addr); @@ -368,13 +396,6 @@ static void i2c_adapter_dev_release(struct device *dev) complete(&adap->dev_released); } -static ssize_t -show_adapter_name(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct i2c_adapter *adap = to_i2c_adapter(dev); - return sprintf(buf, "%s\n", adap->name); -} - /* * Let users instantiate I2C devices through sysfs. This can be used when * platform initialization code doesn't contain the proper data for @@ -493,19 +514,34 @@ i2c_sysfs_delete_device(struct device *dev, struct device_attribute *attr, return res; } -static struct device_attribute i2c_adapter_attrs[] = { - __ATTR(name, S_IRUGO, show_adapter_name, NULL), - __ATTR(new_device, S_IWUSR, NULL, i2c_sysfs_new_device), - __ATTR(delete_device, S_IWUSR, NULL, i2c_sysfs_delete_device), - { }, +static DEVICE_ATTR(new_device, S_IWUSR, NULL, i2c_sysfs_new_device); +static DEVICE_ATTR(delete_device, S_IWUSR, NULL, i2c_sysfs_delete_device); + +static struct attribute *i2c_adapter_attrs[] = { + &dev_attr_name.attr, + &dev_attr_new_device.attr, + &dev_attr_delete_device.attr, + NULL }; -static struct class i2c_adapter_class = { - .owner = THIS_MODULE, - .name = "i2c-adapter", - .dev_attrs = i2c_adapter_attrs, +static struct attribute_group i2c_adapter_attr_group = { + .attrs = i2c_adapter_attrs, }; +static const struct attribute_group *i2c_adapter_attr_groups[] = { + &i2c_adapter_attr_group, + NULL +}; + +static struct device_type i2c_adapter_type = { + .groups = i2c_adapter_attr_groups, + .release = i2c_adapter_dev_release, +}; + +#ifdef CONFIG_I2C_COMPAT +static struct class_compat *i2c_adapter_compat_class; +#endif + static void i2c_scan_static_board_info(struct i2c_adapter *adapter) { struct i2c_devinfo *devinfo; @@ -555,14 +591,22 @@ static int i2c_register_adapter(struct i2c_adapter *adap) adap->timeout = HZ; dev_set_name(&adap->dev, "i2c-%d", adap->nr); - adap->dev.release = &i2c_adapter_dev_release; - adap->dev.class = &i2c_adapter_class; + adap->dev.bus = &i2c_bus_type; + adap->dev.type = &i2c_adapter_type; res = device_register(&adap->dev); if (res) goto out_list; dev_dbg(&adap->dev, "adapter [%s] registered\n", adap->name); +#ifdef CONFIG_I2C_COMPAT + res = class_compat_create_link(i2c_adapter_compat_class, &adap->dev, + adap->dev.parent); + if (res) + dev_warn(&adap->dev, + "Failed to create compatibility class link\n"); +#endif + /* create pre-declared device nodes */ if (adap->nr < __i2c_first_dynamic_bus_num) i2c_scan_static_board_info(adap); @@ -741,6 +785,11 @@ int i2c_del_adapter(struct i2c_adapter *adap) checking the returned value. */ res = device_for_each_child(&adap->dev, NULL, __unregister_client); +#ifdef CONFIG_I2C_COMPAT + class_compat_remove_link(i2c_adapter_compat_class, &adap->dev, + adap->dev.parent); +#endif + /* clean up the sysfs representation */ init_completion(&adap->dev_released); device_unregister(&adap->dev); @@ -768,9 +817,13 @@ EXPORT_SYMBOL(i2c_del_adapter); static int __attach_adapter(struct device *dev, void *data) { - struct i2c_adapter *adapter = to_i2c_adapter(dev); + struct i2c_adapter *adapter; struct i2c_driver *driver = data; + if (dev->type != &i2c_adapter_type) + return 0; + adapter = to_i2c_adapter(dev); + i2c_detect(adapter, driver); /* Legacy drivers scan i2c busses directly */ @@ -809,8 +862,7 @@ int i2c_register_driver(struct module *owner, struct i2c_driver *driver) INIT_LIST_HEAD(&driver->clients); /* Walk the adapters that are already present */ mutex_lock(&core_lock); - class_for_each_device(&i2c_adapter_class, NULL, driver, - __attach_adapter); + bus_for_each_dev(&i2c_bus_type, NULL, driver, __attach_adapter); mutex_unlock(&core_lock); return 0; @@ -819,10 +871,14 @@ EXPORT_SYMBOL(i2c_register_driver); static int __detach_adapter(struct device *dev, void *data) { - struct i2c_adapter *adapter = to_i2c_adapter(dev); + struct i2c_adapter *adapter; struct i2c_driver *driver = data; struct i2c_client *client, *_n; + if (dev->type != &i2c_adapter_type) + return 0; + adapter = to_i2c_adapter(dev); + /* Remove the devices we created ourselves as the result of hardware * probing (using a driver's detect method) */ list_for_each_entry_safe(client, _n, &driver->clients, detected) { @@ -850,8 +906,7 @@ static int __detach_adapter(struct device *dev, void *data) void i2c_del_driver(struct i2c_driver *driver) { mutex_lock(&core_lock); - class_for_each_device(&i2c_adapter_class, NULL, driver, - __detach_adapter); + bus_for_each_dev(&i2c_bus_type, NULL, driver, __detach_adapter); mutex_unlock(&core_lock); driver_unregister(&driver->driver); @@ -940,17 +995,23 @@ static int __init i2c_init(void) retval = bus_register(&i2c_bus_type); if (retval) return retval; - retval = class_register(&i2c_adapter_class); - if (retval) +#ifdef CONFIG_I2C_COMPAT + i2c_adapter_compat_class = class_compat_register("i2c-adapter"); + if (!i2c_adapter_compat_class) { + retval = -ENOMEM; goto bus_err; + } +#endif retval = i2c_add_driver(&dummy_driver); if (retval) goto class_err; return 0; class_err: - class_unregister(&i2c_adapter_class); +#ifdef CONFIG_I2C_COMPAT + class_compat_unregister(i2c_adapter_compat_class); bus_err: +#endif bus_unregister(&i2c_bus_type); return retval; } @@ -958,7 +1019,9 @@ bus_err: static void __exit i2c_exit(void) { i2c_del_driver(&dummy_driver); - class_unregister(&i2c_adapter_class); +#ifdef CONFIG_I2C_COMPAT + class_compat_unregister(i2c_adapter_compat_class); +#endif bus_unregister(&i2c_bus_type); } diff --git a/drivers/ide/atiixp.c b/drivers/ide/atiixp.c index 6396c3ad3252..837322b10a4c 100644 --- a/drivers/ide/atiixp.c +++ b/drivers/ide/atiixp.c @@ -177,7 +177,7 @@ static const struct pci_device_id atiixp_pci_tbl[] = { { PCI_VDEVICE(ATI, PCI_DEVICE_ID_ATI_IXP400_IDE), 0 }, { PCI_VDEVICE(ATI, PCI_DEVICE_ID_ATI_IXP600_IDE), 1 }, { PCI_VDEVICE(ATI, PCI_DEVICE_ID_ATI_IXP700_IDE), 0 }, - { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_SB900_IDE), 0 }, + { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_HUDSON2_IDE), 0 }, { 0, }, }; MODULE_DEVICE_TABLE(pci, atiixp_pci_tbl); diff --git a/drivers/ide/ide-acpi.c b/drivers/ide/ide-acpi.c index c509c9916464..c0cf45a11b93 100644 --- a/drivers/ide/ide-acpi.c +++ b/drivers/ide/ide-acpi.c @@ -114,8 +114,6 @@ static int ide_get_dev_handle(struct device *dev, acpi_handle *handle, unsigned int bus, devnum, func; acpi_integer addr; acpi_handle dev_handle; - struct acpi_buffer buffer = {.length = ACPI_ALLOCATE_BUFFER, - .pointer = NULL}; acpi_status status; struct acpi_device_info *dinfo = NULL; int ret = -ENODEV; @@ -134,12 +132,11 @@ static int ide_get_dev_handle(struct device *dev, acpi_handle *handle, goto err; } - status = acpi_get_object_info(dev_handle, &buffer); + status = acpi_get_object_info(dev_handle, &dinfo); if (ACPI_FAILURE(status)) { DEBPRINT("get_object_info for device failed\n"); goto err; } - dinfo = buffer.pointer; if (dinfo && (dinfo->valid & ACPI_VALID_ADR) && dinfo->address == addr) { *pcidevfn = addr; diff --git a/drivers/idle/i7300_idle.c b/drivers/idle/i7300_idle.c index 949c97ff57e3..1f20a042a4f5 100644 --- a/drivers/idle/i7300_idle.c +++ b/drivers/idle/i7300_idle.c @@ -29,8 +29,8 @@ #include <asm/idle.h> -#include "../dma/ioatdma_hw.h" -#include "../dma/ioatdma_registers.h" +#include "../dma/ioat/hw.h" +#include "../dma/ioat/registers.h" #define I7300_IDLE_DRIVER_VERSION "1.55" #define I7300_PRINT "i7300_idle:" @@ -126,9 +126,9 @@ static void i7300_idle_ioat_stop(void) udelay(10); sts = readq(ioat_chanbase + IOAT1_CHANSTS_OFFSET) & - IOAT_CHANSTS_DMA_TRANSFER_STATUS; + IOAT_CHANSTS_STATUS; - if (sts != IOAT_CHANSTS_DMA_TRANSFER_STATUS_ACTIVE) + if (sts != IOAT_CHANSTS_ACTIVE) break; } @@ -160,9 +160,9 @@ static int __init i7300_idle_ioat_selftest(u8 *ctl, udelay(1000); chan_sts = readq(ioat_chanbase + IOAT1_CHANSTS_OFFSET) & - IOAT_CHANSTS_DMA_TRANSFER_STATUS; + IOAT_CHANSTS_STATUS; - if (chan_sts != IOAT_CHANSTS_DMA_TRANSFER_STATUS_DONE) { + if (chan_sts != IOAT_CHANSTS_DONE) { /* Not complete, reset the channel */ writeb(IOAT_CHANCMD_RESET, ioat_chanbase + IOAT1_CHANCMD_OFFSET); @@ -288,9 +288,9 @@ static void __exit i7300_idle_ioat_exit(void) ioat_chanbase + IOAT1_CHANCMD_OFFSET); chan_sts = readq(ioat_chanbase + IOAT1_CHANSTS_OFFSET) & - IOAT_CHANSTS_DMA_TRANSFER_STATUS; + IOAT_CHANSTS_STATUS; - if (chan_sts != IOAT_CHANSTS_DMA_TRANSFER_STATUS_ACTIVE) { + if (chan_sts != IOAT_CHANSTS_ACTIVE) { writew(0, ioat_chanbase + IOAT_CHANCTRL_OFFSET); break; } @@ -298,14 +298,14 @@ static void __exit i7300_idle_ioat_exit(void) } chan_sts = readq(ioat_chanbase + IOAT1_CHANSTS_OFFSET) & - IOAT_CHANSTS_DMA_TRANSFER_STATUS; + IOAT_CHANSTS_STATUS; /* * We tried to reset multiple times. If IO A/T channel is still active * flag an error and return without cleanup. Memory leak is better * than random corruption in that extreme error situation. */ - if (chan_sts == IOAT_CHANSTS_DMA_TRANSFER_STATUS_ACTIVE) { + if (chan_sts == IOAT_CHANSTS_ACTIVE) { printk(KERN_ERR I7300_PRINT "Unable to stop IO A/T channels." " Not freeing resources\n"); return; diff --git a/drivers/ieee1394/dma.c b/drivers/ieee1394/dma.c index 1aba8c13fe8f..8e7e3344c4b3 100644 --- a/drivers/ieee1394/dma.c +++ b/drivers/ieee1394/dma.c @@ -247,7 +247,7 @@ static int dma_region_pagefault(struct vm_area_struct *vma, return 0; } -static struct vm_operations_struct dma_region_vm_ops = { +static const struct vm_operations_struct dma_region_vm_ops = { .fault = dma_region_pagefault, }; diff --git a/drivers/ieee1394/raw1394.c b/drivers/ieee1394/raw1394.c index da5f8829b503..8aa56ac07e29 100644 --- a/drivers/ieee1394/raw1394.c +++ b/drivers/ieee1394/raw1394.c @@ -29,6 +29,7 @@ #include <linux/kernel.h> #include <linux/list.h> +#include <linux/sched.h> #include <linux/string.h> #include <linux/slab.h> #include <linux/fs.h> @@ -2272,8 +2273,10 @@ static ssize_t raw1394_write(struct file *file, const char __user * buffer, return -EFAULT; } - if (!mutex_trylock(&fi->state_mutex)) + if (!mutex_trylock(&fi->state_mutex)) { + free_pending_request(req); return -EAGAIN; + } switch (fi->state) { case opened: diff --git a/drivers/ieee1394/sbp2.c b/drivers/ieee1394/sbp2.c index 52b25f8b111d..f199896c4113 100644 --- a/drivers/ieee1394/sbp2.c +++ b/drivers/ieee1394/sbp2.c @@ -372,8 +372,7 @@ static const struct { /* DViCO Momobay FX-3A with TSB42AA9A bridge */ { .firmware_revision = 0x002800, .model = 0x000000, - .workarounds = SBP2_WORKAROUND_DELAY_INQUIRY | - SBP2_WORKAROUND_POWER_CONDITION, + .workarounds = SBP2_WORKAROUND_POWER_CONDITION, }, /* Initio bridges, actually only needed for some older ones */ { .firmware_revision = 0x000200, diff --git a/drivers/ieee1394/video1394.c b/drivers/ieee1394/video1394.c index d287ba79821d..949064a05675 100644 --- a/drivers/ieee1394/video1394.c +++ b/drivers/ieee1394/video1394.c @@ -30,6 +30,7 @@ */ #include <linux/kernel.h> #include <linux/list.h> +#include <linux/sched.h> #include <linux/slab.h> #include <linux/interrupt.h> #include <linux/wait.h> diff --git a/drivers/ieee802154/fakehard.c b/drivers/ieee802154/fakehard.c index 96a2959ce877..7c544f7c74c4 100644 --- a/drivers/ieee802154/fakehard.c +++ b/drivers/ieee802154/fakehard.c @@ -260,15 +260,12 @@ static int ieee802154_fake_close(struct net_device *dev) static netdev_tx_t ieee802154_fake_xmit(struct sk_buff *skb, struct net_device *dev) { - skb->iif = dev->ifindex; - skb->dev = dev; dev->stats.tx_packets++; dev->stats.tx_bytes += skb->len; - dev->trans_start = jiffies; - /* FIXME: do hardware work here ... */ + dev_kfree_skb(skb); return NETDEV_TX_OK; } diff --git a/drivers/infiniband/core/addr.c b/drivers/infiniband/core/addr.c index 5be1bd4fc7ed..bd07803e9183 100644 --- a/drivers/infiniband/core/addr.c +++ b/drivers/infiniband/core/addr.c @@ -393,7 +393,7 @@ static int addr_resolve_local(struct sockaddr *src_in, for_each_netdev(&init_net, dev) if (ipv6_chk_addr(&init_net, - &((struct sockaddr_in6 *) addr)->sin6_addr, + &((struct sockaddr_in6 *) dst_in)->sin6_addr, dev, 1)) break; diff --git a/drivers/infiniband/core/iwcm.c b/drivers/infiniband/core/iwcm.c index 55d093a36ae4..0f89909abce9 100644 --- a/drivers/infiniband/core/iwcm.c +++ b/drivers/infiniband/core/iwcm.c @@ -40,6 +40,7 @@ #include <linux/idr.h> #include <linux/interrupt.h> #include <linux/rbtree.h> +#include <linux/sched.h> #include <linux/spinlock.h> #include <linux/workqueue.h> #include <linux/completion.h> @@ -362,7 +363,9 @@ static void destroy_cm_id(struct iw_cm_id *cm_id) * In either case, must tell the provider to reject. */ cm_id_priv->state = IW_CM_STATE_DESTROYING; + spin_unlock_irqrestore(&cm_id_priv->lock, flags); cm_id->device->iwcm->reject(cm_id, NULL, 0); + spin_lock_irqsave(&cm_id_priv->lock, flags); break; case IW_CM_STATE_CONN_SENT: case IW_CM_STATE_DESTROYING: diff --git a/drivers/infiniband/core/mad_rmpp.c b/drivers/infiniband/core/mad_rmpp.c index 57a3c6f947b2..4e0f2829e0e5 100644 --- a/drivers/infiniband/core/mad_rmpp.c +++ b/drivers/infiniband/core/mad_rmpp.c @@ -37,7 +37,8 @@ enum rmpp_state { RMPP_STATE_ACTIVE, RMPP_STATE_TIMEOUT, - RMPP_STATE_COMPLETE + RMPP_STATE_COMPLETE, + RMPP_STATE_CANCELING }; struct mad_rmpp_recv { @@ -87,18 +88,22 @@ void ib_cancel_rmpp_recvs(struct ib_mad_agent_private *agent) spin_lock_irqsave(&agent->lock, flags); list_for_each_entry(rmpp_recv, &agent->rmpp_list, list) { + if (rmpp_recv->state != RMPP_STATE_COMPLETE) + ib_free_recv_mad(rmpp_recv->rmpp_wc); + rmpp_recv->state = RMPP_STATE_CANCELING; + } + spin_unlock_irqrestore(&agent->lock, flags); + + list_for_each_entry(rmpp_recv, &agent->rmpp_list, list) { cancel_delayed_work(&rmpp_recv->timeout_work); cancel_delayed_work(&rmpp_recv->cleanup_work); } - spin_unlock_irqrestore(&agent->lock, flags); flush_workqueue(agent->qp_info->port_priv->wq); list_for_each_entry_safe(rmpp_recv, temp_rmpp_recv, &agent->rmpp_list, list) { list_del(&rmpp_recv->list); - if (rmpp_recv->state != RMPP_STATE_COMPLETE) - ib_free_recv_mad(rmpp_recv->rmpp_wc); destroy_rmpp_recv(rmpp_recv); } } @@ -260,6 +265,10 @@ static void recv_cleanup_handler(struct work_struct *work) unsigned long flags; spin_lock_irqsave(&rmpp_recv->agent->lock, flags); + if (rmpp_recv->state == RMPP_STATE_CANCELING) { + spin_unlock_irqrestore(&rmpp_recv->agent->lock, flags); + return; + } list_del(&rmpp_recv->list); spin_unlock_irqrestore(&rmpp_recv->agent->lock, flags); destroy_rmpp_recv(rmpp_recv); diff --git a/drivers/infiniband/core/ucm.c b/drivers/infiniband/core/ucm.c index 51bd9669cb1f..f504c9b00c1b 100644 --- a/drivers/infiniband/core/ucm.c +++ b/drivers/infiniband/core/ucm.c @@ -38,6 +38,7 @@ #include <linux/device.h> #include <linux/err.h> #include <linux/poll.h> +#include <linux/sched.h> #include <linux/file.h> #include <linux/mount.h> #include <linux/cdev.h> diff --git a/drivers/infiniband/core/ucma.c b/drivers/infiniband/core/ucma.c index 4346a24568fb..bb96d3c4b0f4 100644 --- a/drivers/infiniband/core/ucma.c +++ b/drivers/infiniband/core/ucma.c @@ -34,6 +34,7 @@ #include <linux/file.h> #include <linux/mutex.h> #include <linux/poll.h> +#include <linux/sched.h> #include <linux/idr.h> #include <linux/in.h> #include <linux/in6.h> diff --git a/drivers/infiniband/core/user_mad.c b/drivers/infiniband/core/user_mad.c index 8c46f2257098..7de02969ed7d 100644 --- a/drivers/infiniband/core/user_mad.c +++ b/drivers/infiniband/core/user_mad.c @@ -44,6 +44,7 @@ #include <linux/mutex.h> #include <linux/kref.h> #include <linux/compat.h> +#include <linux/sched.h> #include <linux/semaphore.h> #include <asm/uaccess.h> diff --git a/drivers/infiniband/core/uverbs_main.c b/drivers/infiniband/core/uverbs_main.c index d3fff9e008a3..aec0fbdfe7f0 100644 --- a/drivers/infiniband/core/uverbs_main.c +++ b/drivers/infiniband/core/uverbs_main.c @@ -40,6 +40,7 @@ #include <linux/err.h> #include <linux/fs.h> #include <linux/poll.h> +#include <linux/sched.h> #include <linux/file.h> #include <linux/mount.h> #include <linux/cdev.h> diff --git a/drivers/infiniband/hw/cxgb3/iwch_provider.c b/drivers/infiniband/hw/cxgb3/iwch_provider.c index 6895523779d0..ed7175549ebd 100644 --- a/drivers/infiniband/hw/cxgb3/iwch_provider.c +++ b/drivers/infiniband/hw/cxgb3/iwch_provider.c @@ -37,6 +37,7 @@ #include <linux/delay.h> #include <linux/errno.h> #include <linux/list.h> +#include <linux/sched.h> #include <linux/spinlock.h> #include <linux/ethtool.h> #include <linux/rtnetlink.h> @@ -1199,11 +1200,14 @@ static int iwch_query_port(struct ib_device *ibdev, props->state = IB_PORT_DOWN; else { inetdev = in_dev_get(netdev); - if (inetdev->ifa_list) - props->state = IB_PORT_ACTIVE; - else + if (inetdev) { + if (inetdev->ifa_list) + props->state = IB_PORT_ACTIVE; + else + props->state = IB_PORT_INIT; + in_dev_put(inetdev); + } else props->state = IB_PORT_INIT; - in_dev_put(inetdev); } props->port_cap_flags = diff --git a/drivers/infiniband/hw/cxgb3/iwch_qp.c b/drivers/infiniband/hw/cxgb3/iwch_qp.c index 6e8653471941..1cecf98829ac 100644 --- a/drivers/infiniband/hw/cxgb3/iwch_qp.c +++ b/drivers/infiniband/hw/cxgb3/iwch_qp.c @@ -29,6 +29,7 @@ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ +#include <linux/sched.h> #include "iwch_provider.h" #include "iwch.h" #include "iwch_cm.h" diff --git a/drivers/infiniband/hw/ehca/ehca_mrmw.c b/drivers/infiniband/hw/ehca/ehca_mrmw.c index 7663a2a9f130..7550a534005c 100644 --- a/drivers/infiniband/hw/ehca/ehca_mrmw.c +++ b/drivers/infiniband/hw/ehca/ehca_mrmw.c @@ -2463,7 +2463,7 @@ int ehca_create_busmap(void) int ret; ehca_mr_len = 0; - ret = walk_memory_resource(0, 1ULL << MAX_PHYSMEM_BITS, NULL, + ret = walk_system_ram_range(0, 1ULL << MAX_PHYSMEM_BITS, NULL, ehca_create_busmap_callback); return ret; } diff --git a/drivers/infiniband/hw/ehca/ehca_uverbs.c b/drivers/infiniband/hw/ehca/ehca_uverbs.c index 3cb688d29131..f1565cae8ec6 100644 --- a/drivers/infiniband/hw/ehca/ehca_uverbs.c +++ b/drivers/infiniband/hw/ehca/ehca_uverbs.c @@ -95,7 +95,7 @@ static void ehca_mm_close(struct vm_area_struct *vma) vma->vm_start, vma->vm_end, *count); } -static struct vm_operations_struct vm_ops = { +static const struct vm_operations_struct vm_ops = { .open = ehca_mm_open, .close = ehca_mm_close, }; diff --git a/drivers/infiniband/hw/ipath/ipath_driver.c b/drivers/infiniband/hw/ipath/ipath_driver.c index 04e88b600558..013d1380e77c 100644 --- a/drivers/infiniband/hw/ipath/ipath_driver.c +++ b/drivers/infiniband/hw/ipath/ipath_driver.c @@ -31,6 +31,7 @@ * SOFTWARE. */ +#include <linux/sched.h> #include <linux/spinlock.h> #include <linux/idr.h> #include <linux/pci.h> diff --git a/drivers/infiniband/hw/ipath/ipath_file_ops.c b/drivers/infiniband/hw/ipath/ipath_file_ops.c index 38a287006612..40dbe54056c7 100644 --- a/drivers/infiniband/hw/ipath/ipath_file_ops.c +++ b/drivers/infiniband/hw/ipath/ipath_file_ops.c @@ -1151,7 +1151,7 @@ static int ipath_file_vma_fault(struct vm_area_struct *vma, return 0; } -static struct vm_operations_struct ipath_file_vm_ops = { +static const struct vm_operations_struct ipath_file_vm_ops = { .fault = ipath_file_vma_fault, }; diff --git a/drivers/infiniband/hw/ipath/ipath_iba7220.c b/drivers/infiniband/hw/ipath/ipath_iba7220.c index b2a9d4c155d1..a805402dd4ae 100644 --- a/drivers/infiniband/hw/ipath/ipath_iba7220.c +++ b/drivers/infiniband/hw/ipath/ipath_iba7220.c @@ -37,6 +37,7 @@ #include <linux/interrupt.h> #include <linux/pci.h> +#include <linux/sched.h> #include <linux/delay.h> #include <linux/io.h> #include <rdma/ib_verbs.h> diff --git a/drivers/infiniband/hw/ipath/ipath_intr.c b/drivers/infiniband/hw/ipath/ipath_intr.c index 6c21b4b5ec71..c0a03ac03ee7 100644 --- a/drivers/infiniband/hw/ipath/ipath_intr.c +++ b/drivers/infiniband/hw/ipath/ipath_intr.c @@ -33,6 +33,7 @@ #include <linux/pci.h> #include <linux/delay.h> +#include <linux/sched.h> #include "ipath_kernel.h" #include "ipath_verbs.h" diff --git a/drivers/infiniband/hw/ipath/ipath_mmap.c b/drivers/infiniband/hw/ipath/ipath_mmap.c index fa830e22002f..b28865faf435 100644 --- a/drivers/infiniband/hw/ipath/ipath_mmap.c +++ b/drivers/infiniband/hw/ipath/ipath_mmap.c @@ -74,7 +74,7 @@ static void ipath_vma_close(struct vm_area_struct *vma) kref_put(&ip->ref, ipath_release_mmap_info); } -static struct vm_operations_struct ipath_vm_ops = { +static const struct vm_operations_struct ipath_vm_ops = { .open = ipath_vma_open, .close = ipath_vma_close, }; diff --git a/drivers/infiniband/hw/ipath/ipath_qp.c b/drivers/infiniband/hw/ipath/ipath_qp.c index 3a5a89b609c4..cb2d3ef2ae12 100644 --- a/drivers/infiniband/hw/ipath/ipath_qp.c +++ b/drivers/infiniband/hw/ipath/ipath_qp.c @@ -32,6 +32,7 @@ */ #include <linux/err.h> +#include <linux/sched.h> #include <linux/vmalloc.h> #include "ipath_verbs.h" diff --git a/drivers/infiniband/hw/ipath/ipath_ruc.c b/drivers/infiniband/hw/ipath/ipath_ruc.c index 2296832f94da..1f95bbaf7602 100644 --- a/drivers/infiniband/hw/ipath/ipath_ruc.c +++ b/drivers/infiniband/hw/ipath/ipath_ruc.c @@ -31,6 +31,7 @@ * SOFTWARE. */ +#include <linux/sched.h> #include <linux/spinlock.h> #include "ipath_verbs.h" diff --git a/drivers/infiniband/hw/ipath/ipath_ud.c b/drivers/infiniband/hw/ipath/ipath_ud.c index 6076cb61bf6a..7420715256a9 100644 --- a/drivers/infiniband/hw/ipath/ipath_ud.c +++ b/drivers/infiniband/hw/ipath/ipath_ud.c @@ -31,6 +31,7 @@ * SOFTWARE. */ +#include <linux/sched.h> #include <rdma/ib_smi.h> #include "ipath_verbs.h" diff --git a/drivers/infiniband/hw/ipath/ipath_user_pages.c b/drivers/infiniband/hw/ipath/ipath_user_pages.c index 855911e7396d..82878e348627 100644 --- a/drivers/infiniband/hw/ipath/ipath_user_pages.c +++ b/drivers/infiniband/hw/ipath/ipath_user_pages.c @@ -33,6 +33,7 @@ #include <linux/mm.h> #include <linux/device.h> +#include <linux/sched.h> #include "ipath_kernel.h" diff --git a/drivers/infiniband/hw/ipath/ipath_user_sdma.c b/drivers/infiniband/hw/ipath/ipath_user_sdma.c index 7bff4b9baa0a..be78f6643c06 100644 --- a/drivers/infiniband/hw/ipath/ipath_user_sdma.c +++ b/drivers/infiniband/hw/ipath/ipath_user_sdma.c @@ -33,6 +33,7 @@ #include <linux/types.h> #include <linux/device.h> #include <linux/dmapool.h> +#include <linux/sched.h> #include <linux/slab.h> #include <linux/list.h> #include <linux/highmem.h> diff --git a/drivers/infiniband/hw/ipath/ipath_verbs_mcast.c b/drivers/infiniband/hw/ipath/ipath_verbs_mcast.c index d73e32232879..6923e1d986da 100644 --- a/drivers/infiniband/hw/ipath/ipath_verbs_mcast.c +++ b/drivers/infiniband/hw/ipath/ipath_verbs_mcast.c @@ -32,6 +32,7 @@ */ #include <linux/rculist.h> +#include <linux/sched.h> #include "ipath_verbs.h" diff --git a/drivers/infiniband/hw/mthca/mthca_catas.c b/drivers/infiniband/hw/mthca/mthca_catas.c index 056b2a4c6970..0aa0110e4b6c 100644 --- a/drivers/infiniband/hw/mthca/mthca_catas.c +++ b/drivers/infiniband/hw/mthca/mthca_catas.c @@ -68,11 +68,16 @@ static void catas_reset(struct work_struct *work) spin_unlock_irq(&catas_lock); list_for_each_entry_safe(dev, tmpdev, &tlist, catas_err.list) { + struct pci_dev *pdev = dev->pdev; ret = __mthca_restart_one(dev->pdev); + /* 'dev' now is not valid */ if (ret) - mthca_err(dev, "Reset failed (%d)\n", ret); - else - mthca_dbg(dev, "Reset succeeded\n"); + printk(KERN_ERR "mthca %s: Reset failed (%d)\n", + pci_name(pdev), ret); + else { + struct mthca_dev *d = pci_get_drvdata(pdev); + mthca_dbg(d, "Reset succeeded\n"); + } } mutex_unlock(&mthca_device_mutex); diff --git a/drivers/infiniband/hw/nes/nes_nic.c b/drivers/infiniband/hw/nes/nes_nic.c index 538e409d4515..e593af3354b8 100644 --- a/drivers/infiniband/hw/nes/nes_nic.c +++ b/drivers/infiniband/hw/nes/nes_nic.c @@ -1566,7 +1566,6 @@ static const struct net_device_ops nes_netdev_ops = { .ndo_set_mac_address = nes_netdev_set_mac_address, .ndo_set_multicast_list = nes_netdev_set_multicast_list, .ndo_change_mtu = nes_netdev_change_mtu, - .ndo_set_mac_address = eth_mac_addr, .ndo_validate_addr = eth_validate_addr, .ndo_vlan_rx_register = nes_netdev_vlan_rx_register, }; diff --git a/drivers/infiniband/ulp/ipoib/ipoib_multicast.c b/drivers/infiniband/ulp/ipoib/ipoib_multicast.c index 25874fc680c9..8763c1ea5eb4 100644 --- a/drivers/infiniband/ulp/ipoib/ipoib_multicast.c +++ b/drivers/infiniband/ulp/ipoib/ipoib_multicast.c @@ -362,12 +362,19 @@ void ipoib_mcast_carrier_on_task(struct work_struct *work) { struct ipoib_dev_priv *priv = container_of(work, struct ipoib_dev_priv, carrier_on_task); + struct ib_port_attr attr; /* * Take rtnl_lock to avoid racing with ipoib_stop() and * turning the carrier back on while a device is being * removed. */ + if (ib_query_port(priv->ca, priv->port, &attr) || + attr.state != IB_PORT_ACTIVE) { + ipoib_dbg(priv, "Keeping carrier off until IB port is active\n"); + return; + } + rtnl_lock(); netif_carrier_on(priv->dev); rtnl_unlock(); diff --git a/drivers/infiniband/ulp/iser/iscsi_iser.c b/drivers/infiniband/ulp/iser/iscsi_iser.c index 0ba6ec876296..add9188663ff 100644 --- a/drivers/infiniband/ulp/iser/iscsi_iser.c +++ b/drivers/infiniband/ulp/iser/iscsi_iser.c @@ -426,7 +426,7 @@ iscsi_iser_session_create(struct iscsi_endpoint *ep, * because we preallocate so many resources */ cls_session = iscsi_session_setup(&iscsi_iser_transport, shost, - ISCSI_DEF_XMIT_CMDS_MAX, + ISCSI_DEF_XMIT_CMDS_MAX, 0, sizeof(struct iscsi_iser_task), initial_cmdsn, 0); if (!cls_session) diff --git a/drivers/input/evdev.c b/drivers/input/evdev.c index 1148140d08a1..dee6706038aa 100644 --- a/drivers/input/evdev.c +++ b/drivers/input/evdev.c @@ -13,6 +13,7 @@ #define EVDEV_BUFFER_SIZE 64 #include <linux/poll.h> +#include <linux/sched.h> #include <linux/slab.h> #include <linux/module.h> #include <linux/init.h> diff --git a/drivers/input/ff-core.c b/drivers/input/ff-core.c index 72c63e5dd630..38df81fcdc3a 100644 --- a/drivers/input/ff-core.c +++ b/drivers/input/ff-core.c @@ -337,16 +337,16 @@ int input_ff_create(struct input_dev *dev, int max_effects) dev->ff = ff; dev->flush = flush_effects; dev->event = input_ff_event; - set_bit(EV_FF, dev->evbit); + __set_bit(EV_FF, dev->evbit); /* Copy "true" bits into ff device bitmap */ for (i = 0; i <= FF_MAX; i++) if (test_bit(i, dev->ffbit)) - set_bit(i, ff->ffbit); + __set_bit(i, ff->ffbit); /* we can emulate RUMBLE with periodic effects */ if (test_bit(FF_PERIODIC, ff->ffbit)) - set_bit(FF_RUMBLE, dev->ffbit); + __set_bit(FF_RUMBLE, dev->ffbit); return 0; } @@ -362,12 +362,14 @@ EXPORT_SYMBOL_GPL(input_ff_create); */ void input_ff_destroy(struct input_dev *dev) { - clear_bit(EV_FF, dev->evbit); - if (dev->ff) { - if (dev->ff->destroy) - dev->ff->destroy(dev->ff); - kfree(dev->ff->private); - kfree(dev->ff); + struct ff_device *ff = dev->ff; + + __clear_bit(EV_FF, dev->evbit); + if (ff) { + if (ff->destroy) + ff->destroy(ff); + kfree(ff->private); + kfree(ff); dev->ff = NULL; } } diff --git a/drivers/input/ff-memless.c b/drivers/input/ff-memless.c index 2d1415e16834..b483b2995fa9 100644 --- a/drivers/input/ff-memless.c +++ b/drivers/input/ff-memless.c @@ -61,7 +61,6 @@ struct ml_device { struct ml_effect_state states[FF_MEMLESS_EFFECTS]; int gain; struct timer_list timer; - spinlock_t timer_lock; struct input_dev *dev; int (*play_effect)(struct input_dev *dev, void *data, @@ -368,38 +367,38 @@ static void ml_effect_timer(unsigned long timer_data) { struct input_dev *dev = (struct input_dev *)timer_data; struct ml_device *ml = dev->ff->private; + unsigned long flags; debug("timer: updating effects"); - spin_lock(&ml->timer_lock); + spin_lock_irqsave(&dev->event_lock, flags); ml_play_effects(ml); - spin_unlock(&ml->timer_lock); + spin_unlock_irqrestore(&dev->event_lock, flags); } +/* + * Sets requested gain for FF effects. Called with dev->event_lock held. + */ static void ml_ff_set_gain(struct input_dev *dev, u16 gain) { struct ml_device *ml = dev->ff->private; int i; - spin_lock_bh(&ml->timer_lock); - ml->gain = gain; for (i = 0; i < FF_MEMLESS_EFFECTS; i++) __clear_bit(FF_EFFECT_PLAYING, &ml->states[i].flags); ml_play_effects(ml); - - spin_unlock_bh(&ml->timer_lock); } +/* + * Start/stop specified FF effect. Called with dev->event_lock held. + */ static int ml_ff_playback(struct input_dev *dev, int effect_id, int value) { struct ml_device *ml = dev->ff->private; struct ml_effect_state *state = &ml->states[effect_id]; - unsigned long flags; - - spin_lock_irqsave(&ml->timer_lock, flags); if (value > 0) { debug("initiated play"); @@ -425,8 +424,6 @@ static int ml_ff_playback(struct input_dev *dev, int effect_id, int value) ml_play_effects(ml); } - spin_unlock_irqrestore(&ml->timer_lock, flags); - return 0; } @@ -436,7 +433,7 @@ static int ml_ff_upload(struct input_dev *dev, struct ml_device *ml = dev->ff->private; struct ml_effect_state *state = &ml->states[effect->id]; - spin_lock_bh(&ml->timer_lock); + spin_lock_irq(&dev->event_lock); if (test_bit(FF_EFFECT_STARTED, &state->flags)) { __clear_bit(FF_EFFECT_PLAYING, &state->flags); @@ -448,7 +445,7 @@ static int ml_ff_upload(struct input_dev *dev, ml_schedule_timer(ml); } - spin_unlock_bh(&ml->timer_lock); + spin_unlock_irq(&dev->event_lock); return 0; } @@ -482,7 +479,6 @@ int input_ff_create_memless(struct input_dev *dev, void *data, ml->private = data; ml->play_effect = play_effect; ml->gain = 0xffff; - spin_lock_init(&ml->timer_lock); setup_timer(&ml->timer, ml_effect_timer, (unsigned long)dev); set_bit(FF_GAIN, dev->ffbit); diff --git a/drivers/input/input.c b/drivers/input/input.c index 556539d617a4..2266ecbfbc01 100644 --- a/drivers/input/input.c +++ b/drivers/input/input.c @@ -11,11 +11,13 @@ */ #include <linux/init.h> +#include <linux/types.h> #include <linux/input.h> #include <linux/module.h> #include <linux/random.h> #include <linux/major.h> #include <linux/proc_fs.h> +#include <linux/sched.h> #include <linux/seq_file.h> #include <linux/poll.h> #include <linux/device.h> @@ -514,7 +516,7 @@ static void input_disconnect_device(struct input_dev *dev) * that there are no threads in the middle of input_open_device() */ mutex_lock(&dev->mutex); - dev->going_away = 1; + dev->going_away = true; mutex_unlock(&dev->mutex); spin_lock_irq(&dev->event_lock); @@ -780,10 +782,29 @@ static unsigned int input_proc_devices_poll(struct file *file, poll_table *wait) return 0; } +union input_seq_state { + struct { + unsigned short pos; + bool mutex_acquired; + }; + void *p; +}; + static void *input_devices_seq_start(struct seq_file *seq, loff_t *pos) { - if (mutex_lock_interruptible(&input_mutex)) - return NULL; + union input_seq_state *state = (union input_seq_state *)&seq->private; + int error; + + /* We need to fit into seq->private pointer */ + BUILD_BUG_ON(sizeof(union input_seq_state) != sizeof(seq->private)); + + error = mutex_lock_interruptible(&input_mutex); + if (error) { + state->mutex_acquired = false; + return ERR_PTR(error); + } + + state->mutex_acquired = true; return seq_list_start(&input_dev_list, *pos); } @@ -793,9 +814,12 @@ static void *input_devices_seq_next(struct seq_file *seq, void *v, loff_t *pos) return seq_list_next(v, &input_dev_list, pos); } -static void input_devices_seq_stop(struct seq_file *seq, void *v) +static void input_seq_stop(struct seq_file *seq, void *v) { - mutex_unlock(&input_mutex); + union input_seq_state *state = (union input_seq_state *)&seq->private; + + if (state->mutex_acquired) + mutex_unlock(&input_mutex); } static void input_seq_print_bitmap(struct seq_file *seq, const char *name, @@ -859,7 +883,7 @@ static int input_devices_seq_show(struct seq_file *seq, void *v) static const struct seq_operations input_devices_seq_ops = { .start = input_devices_seq_start, .next = input_devices_seq_next, - .stop = input_devices_seq_stop, + .stop = input_seq_stop, .show = input_devices_seq_show, }; @@ -879,40 +903,49 @@ static const struct file_operations input_devices_fileops = { static void *input_handlers_seq_start(struct seq_file *seq, loff_t *pos) { - if (mutex_lock_interruptible(&input_mutex)) - return NULL; + union input_seq_state *state = (union input_seq_state *)&seq->private; + int error; + + /* We need to fit into seq->private pointer */ + BUILD_BUG_ON(sizeof(union input_seq_state) != sizeof(seq->private)); + + error = mutex_lock_interruptible(&input_mutex); + if (error) { + state->mutex_acquired = false; + return ERR_PTR(error); + } + + state->mutex_acquired = true; + state->pos = *pos; - seq->private = (void *)(unsigned long)*pos; return seq_list_start(&input_handler_list, *pos); } static void *input_handlers_seq_next(struct seq_file *seq, void *v, loff_t *pos) { - seq->private = (void *)(unsigned long)(*pos + 1); - return seq_list_next(v, &input_handler_list, pos); -} + union input_seq_state *state = (union input_seq_state *)&seq->private; -static void input_handlers_seq_stop(struct seq_file *seq, void *v) -{ - mutex_unlock(&input_mutex); + state->pos = *pos + 1; + return seq_list_next(v, &input_handler_list, pos); } static int input_handlers_seq_show(struct seq_file *seq, void *v) { struct input_handler *handler = container_of(v, struct input_handler, node); + union input_seq_state *state = (union input_seq_state *)&seq->private; - seq_printf(seq, "N: Number=%ld Name=%s", - (unsigned long)seq->private, handler->name); + seq_printf(seq, "N: Number=%u Name=%s", state->pos, handler->name); if (handler->fops) seq_printf(seq, " Minor=%d", handler->minor); seq_putc(seq, '\n'); return 0; } + static const struct seq_operations input_handlers_seq_ops = { .start = input_handlers_seq_start, .next = input_handlers_seq_next, - .stop = input_handlers_seq_stop, + .stop = input_seq_stop, .show = input_handlers_seq_show, }; @@ -1259,10 +1292,78 @@ static int input_dev_uevent(struct device *device, struct kobj_uevent_env *env) return 0; } +#define INPUT_DO_TOGGLE(dev, type, bits, on) \ + do { \ + int i; \ + bool active; \ + \ + if (!test_bit(EV_##type, dev->evbit)) \ + break; \ + \ + for (i = 0; i < type##_MAX; i++) { \ + if (!test_bit(i, dev->bits##bit)) \ + continue; \ + \ + active = test_bit(i, dev->bits); \ + if (!active && !on) \ + continue; \ + \ + dev->event(dev, EV_##type, i, on ? active : 0); \ + } \ + } while (0) + +#ifdef CONFIG_PM +static void input_dev_reset(struct input_dev *dev, bool activate) +{ + if (!dev->event) + return; + + INPUT_DO_TOGGLE(dev, LED, led, activate); + INPUT_DO_TOGGLE(dev, SND, snd, activate); + + if (activate && test_bit(EV_REP, dev->evbit)) { + dev->event(dev, EV_REP, REP_PERIOD, dev->rep[REP_PERIOD]); + dev->event(dev, EV_REP, REP_DELAY, dev->rep[REP_DELAY]); + } +} + +static int input_dev_suspend(struct device *dev) +{ + struct input_dev *input_dev = to_input_dev(dev); + + mutex_lock(&input_dev->mutex); + input_dev_reset(input_dev, false); + mutex_unlock(&input_dev->mutex); + + return 0; +} + +static int input_dev_resume(struct device *dev) +{ + struct input_dev *input_dev = to_input_dev(dev); + + mutex_lock(&input_dev->mutex); + input_dev_reset(input_dev, true); + mutex_unlock(&input_dev->mutex); + + return 0; +} + +static const struct dev_pm_ops input_dev_pm_ops = { + .suspend = input_dev_suspend, + .resume = input_dev_resume, + .poweroff = input_dev_suspend, + .restore = input_dev_resume, +}; +#endif /* CONFIG_PM */ + static struct device_type input_dev_type = { .groups = input_dev_attr_groups, .release = input_dev_release, .uevent = input_dev_uevent, +#ifdef CONFIG_PM + .pm = &input_dev_pm_ops, +#endif }; static char *input_devnode(struct device *dev, mode_t *mode) diff --git a/drivers/input/joydev.c b/drivers/input/joydev.c index 901b2525993e..b1bd6dd32286 100644 --- a/drivers/input/joydev.c +++ b/drivers/input/joydev.c @@ -18,6 +18,7 @@ #include <linux/input.h> #include <linux/kernel.h> #include <linux/major.h> +#include <linux/sched.h> #include <linux/slab.h> #include <linux/mm.h> #include <linux/miscdevice.h> diff --git a/drivers/input/joystick/xpad.c b/drivers/input/joystick/xpad.c index 2388cf578a62..79e3edcced1a 100644 --- a/drivers/input/joystick/xpad.c +++ b/drivers/input/joystick/xpad.c @@ -143,6 +143,7 @@ static const struct xpad_device { { 0x12ab, 0x8809, "Xbox DDR dancepad", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX }, { 0x1430, 0x4748, "RedOctane Guitar Hero X-plorer", MAP_DPAD_TO_AXES, XTYPE_XBOX360 }, { 0x1430, 0x8888, "TX6500+ Dance Pad (first generation)", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX }, + { 0x146b, 0x0601, "BigBen Interactive XBOX 360 Controller", MAP_DPAD_TO_AXES, XTYPE_XBOX360 }, { 0x045e, 0x028e, "Microsoft X-Box 360 pad", MAP_DPAD_TO_AXES, XTYPE_XBOX360 }, { 0x1bad, 0x0003, "Harmonix Rock Band Drumkit", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX360 }, { 0xffff, 0xffff, "Chinese-made Xbox Controller", MAP_DPAD_TO_AXES, XTYPE_XBOX }, @@ -209,6 +210,7 @@ static struct usb_device_id xpad_table [] = { XPAD_XBOX360_VENDOR(0x0738), /* Mad Catz X-Box 360 controllers */ XPAD_XBOX360_VENDOR(0x0e6f), /* 0x0e6f X-Box 360 controllers */ XPAD_XBOX360_VENDOR(0x1430), /* RedOctane X-Box 360 controllers */ + XPAD_XBOX360_VENDOR(0x146b), /* BigBen Interactive Controllers */ XPAD_XBOX360_VENDOR(0x1bad), /* Rock Band Drums */ { } }; diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig index 3525c19be428..ee98b1bc5d89 100644 --- a/drivers/input/keyboard/Kconfig +++ b/drivers/input/keyboard/Kconfig @@ -24,6 +24,16 @@ config KEYBOARD_AAED2000 To compile this driver as a module, choose M here: the module will be called aaed2000_kbd. +config KEYBOARD_ADP5588 + tristate "ADP5588 I2C QWERTY Keypad and IO Expander" + depends on I2C + help + Say Y here if you want to use a ADP5588 attached to your + system I2C bus. + + To compile this driver as a module, choose M here: the + module will be called adp5588-keys. + config KEYBOARD_AMIGA tristate "Amiga keyboard" depends on AMIGA @@ -104,6 +114,16 @@ config KEYBOARD_ATKBD_RDI_KEYCODES right-hand column will be interpreted as the key shown in the left-hand column. +config QT2160 + tristate "Atmel AT42QT2160 Touch Sensor Chip" + depends on I2C && EXPERIMENTAL + help + If you say yes here you get support for Atmel AT42QT2160 Touch + Sensor chip as a keyboard input. + + This driver can also be built as a module. If so, the module + will be called qt2160. + config KEYBOARD_BFIN tristate "Blackfin BF54x keypad support" depends on (BF54x && !BF544) @@ -251,6 +271,17 @@ config KEYBOARD_MAPLE To compile this driver as a module, choose M here: the module will be called maple_keyb. +config KEYBOARD_MAX7359 + tristate "Maxim MAX7359 Key Switch Controller" + depends on I2C + help + If you say yes here you get support for the Maxim MAX7359 Key + Switch Controller chip. This providers microprocessors with + management of up to 64 key switches + + To compile this driver as a module, choose M here: the + module will be called max7359_keypad. + config KEYBOARD_NEWTON tristate "Newton keyboard" select SERIO @@ -260,6 +291,15 @@ config KEYBOARD_NEWTON To compile this driver as a module, choose M here: the module will be called newtonkbd. +config KEYBOARD_OPENCORES + tristate "OpenCores Keyboard Controller" + help + Say Y here if you want to use the OpenCores Keyboard Controller + http://www.opencores.org/project,keyboardcontroller + + To compile this driver as a module, choose M here; the + module will be called opencores-kbd. + config KEYBOARD_PXA27x tristate "PXA27x/PXA3xx keypad support" depends on PXA27x || PXA3xx diff --git a/drivers/input/keyboard/Makefile b/drivers/input/keyboard/Makefile index 8a7a22b30266..babad5e58b77 100644 --- a/drivers/input/keyboard/Makefile +++ b/drivers/input/keyboard/Makefile @@ -5,6 +5,7 @@ # Each configuration option enables a list of files. obj-$(CONFIG_KEYBOARD_AAED2000) += aaed2000_kbd.o +obj-$(CONFIG_KEYBOARD_ADP5588) += adp5588-keys.o obj-$(CONFIG_KEYBOARD_AMIGA) += amikbd.o obj-$(CONFIG_KEYBOARD_ATARI) += atakbd.o obj-$(CONFIG_KEYBOARD_ATKBD) += atkbd.o @@ -21,10 +22,13 @@ obj-$(CONFIG_KEYBOARD_LM8323) += lm8323.o obj-$(CONFIG_KEYBOARD_LOCOMO) += locomokbd.o obj-$(CONFIG_KEYBOARD_MAPLE) += maple_keyb.o obj-$(CONFIG_KEYBOARD_MATRIX) += matrix_keypad.o +obj-$(CONFIG_KEYBOARD_MAX7359) += max7359_keypad.o obj-$(CONFIG_KEYBOARD_NEWTON) += newtonkbd.o obj-$(CONFIG_KEYBOARD_OMAP) += omap-keypad.o +obj-$(CONFIG_KEYBOARD_OPENCORES) += opencores-kbd.o obj-$(CONFIG_KEYBOARD_PXA27x) += pxa27x_keypad.o obj-$(CONFIG_KEYBOARD_PXA930_ROTARY) += pxa930_rotary.o +obj-$(CONFIG_KEYBOARD_QT2160) += qt2160.o obj-$(CONFIG_KEYBOARD_SH_KEYSC) += sh_keysc.o obj-$(CONFIG_KEYBOARD_SPITZ) += spitzkbd.o obj-$(CONFIG_KEYBOARD_STOWAWAY) += stowaway.o diff --git a/drivers/input/keyboard/adp5588-keys.c b/drivers/input/keyboard/adp5588-keys.c new file mode 100644 index 000000000000..d48c808d5928 --- /dev/null +++ b/drivers/input/keyboard/adp5588-keys.c @@ -0,0 +1,361 @@ +/* + * File: drivers/input/keyboard/adp5588_keys.c + * Description: keypad driver for ADP5588 I2C QWERTY Keypad and IO Expander + * Bugs: Enter bugs at http://blackfin.uclinux.org/ + * + * Copyright (C) 2008-2009 Analog Devices Inc. + * Licensed under the GPL-2 or later. + */ + +#include <linux/module.h> +#include <linux/version.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/workqueue.h> +#include <linux/errno.h> +#include <linux/pm.h> +#include <linux/platform_device.h> +#include <linux/input.h> +#include <linux/i2c.h> + +#include <linux/i2c/adp5588.h> + + /* Configuration Register1 */ +#define AUTO_INC (1 << 7) +#define GPIEM_CFG (1 << 6) +#define OVR_FLOW_M (1 << 5) +#define INT_CFG (1 << 4) +#define OVR_FLOW_IEN (1 << 3) +#define K_LCK_IM (1 << 2) +#define GPI_IEN (1 << 1) +#define KE_IEN (1 << 0) + +/* Interrupt Status Register */ +#define CMP2_INT (1 << 5) +#define CMP1_INT (1 << 4) +#define OVR_FLOW_INT (1 << 3) +#define K_LCK_INT (1 << 2) +#define GPI_INT (1 << 1) +#define KE_INT (1 << 0) + +/* Key Lock and Event Counter Register */ +#define K_LCK_EN (1 << 6) +#define LCK21 0x30 +#define KEC 0xF + +/* Key Event Register xy */ +#define KEY_EV_PRESSED (1 << 7) +#define KEY_EV_MASK (0x7F) + +#define KP_SEL(x) (0xFFFF >> (16 - x)) /* 2^x-1 */ + +#define KEYP_MAX_EVENT 10 + +/* + * Early pre 4.0 Silicon required to delay readout by at least 25ms, + * since the Event Counter Register updated 25ms after the interrupt + * asserted. + */ +#define WA_DELAYED_READOUT_REVID(rev) ((rev) < 4) + +struct adp5588_kpad { + struct i2c_client *client; + struct input_dev *input; + struct delayed_work work; + unsigned long delay; + unsigned short keycode[ADP5588_KEYMAPSIZE]; +}; + +static int adp5588_read(struct i2c_client *client, u8 reg) +{ + int ret = i2c_smbus_read_byte_data(client, reg); + + if (ret < 0) + dev_err(&client->dev, "Read Error\n"); + + return ret; +} + +static int adp5588_write(struct i2c_client *client, u8 reg, u8 val) +{ + return i2c_smbus_write_byte_data(client, reg, val); +} + +static void adp5588_work(struct work_struct *work) +{ + struct adp5588_kpad *kpad = container_of(work, + struct adp5588_kpad, work.work); + struct i2c_client *client = kpad->client; + int i, key, status, ev_cnt; + + status = adp5588_read(client, INT_STAT); + + if (status & OVR_FLOW_INT) /* Unlikely and should never happen */ + dev_err(&client->dev, "Event Overflow Error\n"); + + if (status & KE_INT) { + ev_cnt = adp5588_read(client, KEY_LCK_EC_STAT) & KEC; + if (ev_cnt) { + for (i = 0; i < ev_cnt; i++) { + key = adp5588_read(client, Key_EVENTA + i); + input_report_key(kpad->input, + kpad->keycode[(key & KEY_EV_MASK) - 1], + key & KEY_EV_PRESSED); + } + input_sync(kpad->input); + } + } + adp5588_write(client, INT_STAT, status); /* Status is W1C */ +} + +static irqreturn_t adp5588_irq(int irq, void *handle) +{ + struct adp5588_kpad *kpad = handle; + + /* + * use keventd context to read the event fifo registers + * Schedule readout at least 25ms after notification for + * REVID < 4 + */ + + schedule_delayed_work(&kpad->work, kpad->delay); + + return IRQ_HANDLED; +} + +static int __devinit adp5588_setup(struct i2c_client *client) +{ + struct adp5588_kpad_platform_data *pdata = client->dev.platform_data; + int i, ret; + + ret = adp5588_write(client, KP_GPIO1, KP_SEL(pdata->rows)); + ret |= adp5588_write(client, KP_GPIO2, KP_SEL(pdata->cols) & 0xFF); + ret |= adp5588_write(client, KP_GPIO3, KP_SEL(pdata->cols) >> 8); + + if (pdata->en_keylock) { + ret |= adp5588_write(client, UNLOCK1, pdata->unlock_key1); + ret |= adp5588_write(client, UNLOCK2, pdata->unlock_key2); + ret |= adp5588_write(client, KEY_LCK_EC_STAT, K_LCK_EN); + } + + for (i = 0; i < KEYP_MAX_EVENT; i++) + ret |= adp5588_read(client, Key_EVENTA); + + ret |= adp5588_write(client, INT_STAT, CMP2_INT | CMP1_INT | + OVR_FLOW_INT | K_LCK_INT | + GPI_INT | KE_INT); /* Status is W1C */ + + ret |= adp5588_write(client, CFG, INT_CFG | OVR_FLOW_IEN | KE_IEN); + + if (ret < 0) { + dev_err(&client->dev, "Write Error\n"); + return ret; + } + + return 0; +} + +static int __devinit adp5588_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct adp5588_kpad *kpad; + struct adp5588_kpad_platform_data *pdata = client->dev.platform_data; + struct input_dev *input; + unsigned int revid; + int ret, i; + int error; + + if (!i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_BYTE_DATA)) { + dev_err(&client->dev, "SMBUS Byte Data not Supported\n"); + return -EIO; + } + + if (!pdata) { + dev_err(&client->dev, "no platform data?\n"); + return -EINVAL; + } + + if (!pdata->rows || !pdata->cols || !pdata->keymap) { + dev_err(&client->dev, "no rows, cols or keymap from pdata\n"); + return -EINVAL; + } + + if (pdata->keymapsize != ADP5588_KEYMAPSIZE) { + dev_err(&client->dev, "invalid keymapsize\n"); + return -EINVAL; + } + + if (!client->irq) { + dev_err(&client->dev, "no IRQ?\n"); + return -EINVAL; + } + + kpad = kzalloc(sizeof(*kpad), GFP_KERNEL); + input = input_allocate_device(); + if (!kpad || !input) { + error = -ENOMEM; + goto err_free_mem; + } + + kpad->client = client; + kpad->input = input; + INIT_DELAYED_WORK(&kpad->work, adp5588_work); + + ret = adp5588_read(client, DEV_ID); + if (ret < 0) { + error = ret; + goto err_free_mem; + } + + revid = (u8) ret & ADP5588_DEVICE_ID_MASK; + if (WA_DELAYED_READOUT_REVID(revid)) + kpad->delay = msecs_to_jiffies(30); + + input->name = client->name; + input->phys = "adp5588-keys/input0"; + input->dev.parent = &client->dev; + + input_set_drvdata(input, kpad); + + input->id.bustype = BUS_I2C; + input->id.vendor = 0x0001; + input->id.product = 0x0001; + input->id.version = revid; + + input->keycodesize = sizeof(kpad->keycode[0]); + input->keycodemax = pdata->keymapsize; + input->keycode = kpad->keycode; + + memcpy(kpad->keycode, pdata->keymap, + pdata->keymapsize * input->keycodesize); + + /* setup input device */ + __set_bit(EV_KEY, input->evbit); + + if (pdata->repeat) + __set_bit(EV_REP, input->evbit); + + for (i = 0; i < input->keycodemax; i++) + __set_bit(kpad->keycode[i] & KEY_MAX, input->keybit); + __clear_bit(KEY_RESERVED, input->keybit); + + error = input_register_device(input); + if (error) { + dev_err(&client->dev, "unable to register input device\n"); + goto err_free_mem; + } + + error = request_irq(client->irq, adp5588_irq, + IRQF_TRIGGER_FALLING | IRQF_DISABLED, + client->dev.driver->name, kpad); + if (error) { + dev_err(&client->dev, "irq %d busy?\n", client->irq); + goto err_unreg_dev; + } + + error = adp5588_setup(client); + if (error) + goto err_free_irq; + + device_init_wakeup(&client->dev, 1); + i2c_set_clientdata(client, kpad); + + dev_info(&client->dev, "Rev.%d keypad, irq %d\n", revid, client->irq); + return 0; + + err_free_irq: + free_irq(client->irq, kpad); + err_unreg_dev: + input_unregister_device(input); + input = NULL; + err_free_mem: + input_free_device(input); + kfree(kpad); + + return error; +} + +static int __devexit adp5588_remove(struct i2c_client *client) +{ + struct adp5588_kpad *kpad = i2c_get_clientdata(client); + + adp5588_write(client, CFG, 0); + free_irq(client->irq, kpad); + cancel_delayed_work_sync(&kpad->work); + input_unregister_device(kpad->input); + i2c_set_clientdata(client, NULL); + kfree(kpad); + + return 0; +} + +#ifdef CONFIG_PM +static int adp5588_suspend(struct device *dev) +{ + struct adp5588_kpad *kpad = dev_get_drvdata(dev); + struct i2c_client *client = kpad->client; + + disable_irq(client->irq); + cancel_delayed_work_sync(&kpad->work); + + if (device_may_wakeup(&client->dev)) + enable_irq_wake(client->irq); + + return 0; +} + +static int adp5588_resume(struct device *dev) +{ + struct adp5588_kpad *kpad = dev_get_drvdata(dev); + struct i2c_client *client = kpad->client; + + if (device_may_wakeup(&client->dev)) + disable_irq_wake(client->irq); + + enable_irq(client->irq); + + return 0; +} + +static struct dev_pm_ops adp5588_dev_pm_ops = { + .suspend = adp5588_suspend, + .resume = adp5588_resume, +}; +#endif + +static const struct i2c_device_id adp5588_id[] = { + { KBUILD_MODNAME, 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, adp5588_id); + +static struct i2c_driver adp5588_driver = { + .driver = { + .name = KBUILD_MODNAME, +#ifdef CONFIG_PM + .pm = &adp5588_dev_pm_ops, +#endif + }, + .probe = adp5588_probe, + .remove = __devexit_p(adp5588_remove), + .id_table = adp5588_id, +}; + +static int __init adp5588_init(void) +{ + return i2c_add_driver(&adp5588_driver); +} +module_init(adp5588_init); + +static void __exit adp5588_exit(void) +{ + i2c_del_driver(&adp5588_driver); +} +module_exit(adp5588_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>"); +MODULE_DESCRIPTION("ADP5588 Keypad driver"); +MODULE_ALIAS("platform:adp5588-keys"); diff --git a/drivers/input/keyboard/atkbd.c b/drivers/input/keyboard/atkbd.c index adb09e2ba394..28e6110d1ff8 100644 --- a/drivers/input/keyboard/atkbd.c +++ b/drivers/input/keyboard/atkbd.c @@ -233,6 +233,7 @@ struct atkbd { */ static void (*atkbd_platform_fixup)(struct atkbd *, const void *data); static void *atkbd_platform_fixup_data; +static unsigned int (*atkbd_platform_scancode_fixup)(struct atkbd *, unsigned int); static ssize_t atkbd_attr_show_helper(struct device *dev, char *buf, ssize_t (*handler)(struct atkbd *, char *)); @@ -393,6 +394,9 @@ static irqreturn_t atkbd_interrupt(struct serio *serio, unsigned char data, input_event(dev, EV_MSC, MSC_RAW, code); + if (atkbd_platform_scancode_fixup) + code = atkbd_platform_scancode_fixup(atkbd, code); + if (atkbd->translated) { if (atkbd->emul || atkbd_need_xlate(atkbd->xl_bit, code)) { @@ -574,11 +578,22 @@ static void atkbd_event_work(struct work_struct *work) mutex_lock(&atkbd->event_mutex); - if (test_and_clear_bit(ATKBD_LED_EVENT_BIT, &atkbd->event_mask)) - atkbd_set_leds(atkbd); + if (!atkbd->enabled) { + /* + * Serio ports are resumed asynchronously so while driver core + * thinks that device is already fully operational in reality + * it may not be ready yet. In this case we need to keep + * rescheduling till reconnect completes. + */ + schedule_delayed_work(&atkbd->event_work, + msecs_to_jiffies(100)); + } else { + if (test_and_clear_bit(ATKBD_LED_EVENT_BIT, &atkbd->event_mask)) + atkbd_set_leds(atkbd); - if (test_and_clear_bit(ATKBD_REP_EVENT_BIT, &atkbd->event_mask)) - atkbd_set_repeat_rate(atkbd); + if (test_and_clear_bit(ATKBD_REP_EVENT_BIT, &atkbd->event_mask)) + atkbd_set_repeat_rate(atkbd); + } mutex_unlock(&atkbd->event_mutex); } @@ -770,13 +785,13 @@ static int atkbd_select_set(struct atkbd *atkbd, int target_set, int allow_extra return 3; } -static int atkbd_activate(struct atkbd *atkbd) +static int atkbd_reset_state(struct atkbd *atkbd) { - struct ps2dev *ps2dev = &atkbd->ps2dev; + struct ps2dev *ps2dev = &atkbd->ps2dev; unsigned char param[1]; /* - * Set the LEDs to a defined state. + * Set the LEDs to a predefined state (all off). */ param[0] = 0; @@ -791,6 +806,13 @@ static int atkbd_activate(struct atkbd *atkbd) if (ps2_command(ps2dev, param, ATKBD_CMD_SETREP)) return -1; + return 0; +} + +static int atkbd_activate(struct atkbd *atkbd) +{ + struct ps2dev *ps2dev = &atkbd->ps2dev; + /* * Enable the keyboard to receive keystrokes. */ @@ -869,29 +891,6 @@ static unsigned int atkbd_hp_forced_release_keys[] = { }; /* - * Inventec system with broken key release on volume keys - */ -static unsigned int atkbd_inventec_forced_release_keys[] = { - 0xae, 0xb0, -1U -}; - -/* - * Perform fixup for HP Pavilion ZV6100 laptop that doesn't generate release - * for its volume buttons - */ -static unsigned int atkbd_hp_zv6100_forced_release_keys[] = { - 0xae, 0xb0, -1U -}; - -/* - * Perform fixup for HP (Compaq) Presario R4000 R4100 R4200 that don't generate - * release for their volume buttons - */ -static unsigned int atkbd_hp_r4000_forced_release_keys[] = { - 0xae, 0xb0, -1U -}; - -/* * Samsung NC10,NC20 with Fn+F? key release not working */ static unsigned int atkbd_samsung_forced_release_keys[] = { @@ -899,14 +898,6 @@ static unsigned int atkbd_samsung_forced_release_keys[] = { }; /* - * The volume up and volume down special keys on a Fujitsu Amilo PA 1510 laptop - * do not generate release events so we have to do it ourselves. - */ -static unsigned int atkbd_amilo_pa1510_forced_release_keys[] = { - 0xb0, 0xae, -1U -}; - -/* * Amilo Pi 3525 key release for Fn+Volume keys not working */ static unsigned int atkbd_amilo_pi3525_forced_release_keys[] = { @@ -928,6 +919,30 @@ static unsigned int atkdb_soltech_ta12_forced_release_keys[] = { }; /* + * Many notebooks don't send key release event for volume up/down + * keys, with key list below common among them + */ +static unsigned int atkbd_volume_forced_release_keys[] = { + 0xae, 0xb0, -1U +}; + +/* + * OQO 01+ multimedia keys (64--66) generate e0 6x upon release whereas + * they should be generating e4-e6 (0x80 | code). + */ +static unsigned int atkbd_oqo_01plus_scancode_fixup(struct atkbd *atkbd, + unsigned int code) +{ + if (atkbd->translated && atkbd->emul == 1 && + (code == 0x64 || code == 0x65 || code == 0x66)) { + atkbd->emul = 0; + code |= 0x80; + } + + return code; +} + +/* * atkbd_set_keycode_table() initializes keyboard's keycode table * according to the selected scancode set */ @@ -1104,6 +1119,7 @@ static int atkbd_connect(struct serio *serio, struct serio_driver *drv) } atkbd->set = atkbd_select_set(atkbd, atkbd_set, atkbd_extra); + atkbd_reset_state(atkbd); atkbd_activate(atkbd); } else { @@ -1159,13 +1175,17 @@ static int atkbd_reconnect(struct serio *serio) atkbd_activate(atkbd); -/* - * Restore repeat rate and LEDs (that were reset by atkbd_activate) - * to pre-resume state - */ + /* + * Restore LED state and repeat rate. While input core + * will do this for us at resume time reconnect may happen + * because user requested it via sysfs or simply because + * keyboard was unplugged and plugged in again so we need + * to do it ourselves here. + */ + atkbd_set_leds(atkbd); if (!atkbd->softrepeat) atkbd_set_repeat_rate(atkbd); - atkbd_set_leds(atkbd); + } atkbd_enable(atkbd); @@ -1292,6 +1312,7 @@ static ssize_t atkbd_set_extra(struct atkbd *atkbd, const char *buf, size_t coun atkbd->dev = new_dev; atkbd->set = atkbd_select_set(atkbd, atkbd->set, value); + atkbd_reset_state(atkbd); atkbd_activate(atkbd); atkbd_set_keycode_table(atkbd); atkbd_set_device_attrs(atkbd); @@ -1413,6 +1434,7 @@ static ssize_t atkbd_set_set(struct atkbd *atkbd, const char *buf, size_t count) atkbd->dev = new_dev; atkbd->set = atkbd_select_set(atkbd, value, atkbd->extra); + atkbd_reset_state(atkbd); atkbd_activate(atkbd); atkbd_set_keycode_table(atkbd); atkbd_set_device_attrs(atkbd); @@ -1538,6 +1560,13 @@ static int __init atkbd_setup_forced_release(const struct dmi_system_id *id) return 0; } +static int __init atkbd_setup_scancode_fixup(const struct dmi_system_id *id) +{ + atkbd_platform_scancode_fixup = id->driver_data; + + return 0; +} + static struct dmi_system_id atkbd_dmi_quirk_table[] __initdata = { { .ident = "Dell Laptop", @@ -1573,7 +1602,7 @@ static struct dmi_system_id atkbd_dmi_quirk_table[] __initdata = { DMI_MATCH(DMI_PRODUCT_NAME, "Pavilion ZV6100"), }, .callback = atkbd_setup_forced_release, - .driver_data = atkbd_hp_zv6100_forced_release_keys, + .driver_data = atkbd_volume_forced_release_keys, }, { .ident = "HP Presario R4000", @@ -1582,7 +1611,7 @@ static struct dmi_system_id atkbd_dmi_quirk_table[] __initdata = { DMI_MATCH(DMI_PRODUCT_NAME, "Presario R4000"), }, .callback = atkbd_setup_forced_release, - .driver_data = atkbd_hp_r4000_forced_release_keys, + .driver_data = atkbd_volume_forced_release_keys, }, { .ident = "HP Presario R4100", @@ -1591,7 +1620,7 @@ static struct dmi_system_id atkbd_dmi_quirk_table[] __initdata = { DMI_MATCH(DMI_PRODUCT_NAME, "Presario R4100"), }, .callback = atkbd_setup_forced_release, - .driver_data = atkbd_hp_r4000_forced_release_keys, + .driver_data = atkbd_volume_forced_release_keys, }, { .ident = "HP Presario R4200", @@ -1600,7 +1629,7 @@ static struct dmi_system_id atkbd_dmi_quirk_table[] __initdata = { DMI_MATCH(DMI_PRODUCT_NAME, "Presario R4200"), }, .callback = atkbd_setup_forced_release, - .driver_data = atkbd_hp_r4000_forced_release_keys, + .driver_data = atkbd_volume_forced_release_keys, }, { .ident = "Inventec Symphony", @@ -1609,7 +1638,7 @@ static struct dmi_system_id atkbd_dmi_quirk_table[] __initdata = { DMI_MATCH(DMI_PRODUCT_NAME, "SYMPHONY 6.0/7.0"), }, .callback = atkbd_setup_forced_release, - .driver_data = atkbd_inventec_forced_release_keys, + .driver_data = atkbd_volume_forced_release_keys, }, { .ident = "Samsung NC10", @@ -1645,7 +1674,7 @@ static struct dmi_system_id atkbd_dmi_quirk_table[] __initdata = { DMI_MATCH(DMI_PRODUCT_NAME, "AMILO Pa 1510"), }, .callback = atkbd_setup_forced_release, - .driver_data = atkbd_amilo_pa1510_forced_release_keys, + .driver_data = atkbd_volume_forced_release_keys, }, { .ident = "Fujitsu Amilo Pi 3525", @@ -1674,6 +1703,15 @@ static struct dmi_system_id atkbd_dmi_quirk_table[] __initdata = { .callback = atkbd_setup_forced_release, .driver_data = atkdb_soltech_ta12_forced_release_keys, }, + { + .ident = "OQO Model 01+", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "OQO"), + DMI_MATCH(DMI_PRODUCT_NAME, "ZEPTO"), + }, + .callback = atkbd_setup_scancode_fixup, + .driver_data = atkbd_oqo_01plus_scancode_fixup, + }, { } }; diff --git a/drivers/input/keyboard/gpio_keys.c b/drivers/input/keyboard/gpio_keys.c index a88aff3816a0..77d130914259 100644 --- a/drivers/input/keyboard/gpio_keys.c +++ b/drivers/input/keyboard/gpio_keys.c @@ -147,6 +147,7 @@ static int __devinit gpio_keys_probe(struct platform_device *pdev) } error = request_irq(irq, gpio_keys_isr, + IRQF_SHARED | IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, button->desc ? button->desc : "gpio_keys", bdata); diff --git a/drivers/input/keyboard/hilkbd.c b/drivers/input/keyboard/hilkbd.c index e9d639ec283d..5f72440b50c8 100644 --- a/drivers/input/keyboard/hilkbd.c +++ b/drivers/input/keyboard/hilkbd.c @@ -24,6 +24,7 @@ #include <linux/interrupt.h> #include <linux/hil.h> #include <linux/io.h> +#include <linux/sched.h> #include <linux/spinlock.h> #include <asm/irq.h> #ifdef CONFIG_HP300 diff --git a/drivers/input/keyboard/max7359_keypad.c b/drivers/input/keyboard/max7359_keypad.c new file mode 100644 index 000000000000..3b5b948eba39 --- /dev/null +++ b/drivers/input/keyboard/max7359_keypad.c @@ -0,0 +1,330 @@ +/* + * max7359_keypad.c - MAX7359 Key Switch Controller Driver + * + * Copyright (C) 2009 Samsung Electronics + * Kim Kyuwon <q1.kim@samsung.com> + * + * Based on pxa27x_keypad.c + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Datasheet: http://www.maxim-ic.com/quick_view2.cfm/qv_pk/5456 + */ + +#include <linux/module.h> +#include <linux/i2c.h> +#include <linux/interrupt.h> +#include <linux/input.h> +#include <linux/input/matrix_keypad.h> + +#define MAX7359_MAX_KEY_ROWS 8 +#define MAX7359_MAX_KEY_COLS 8 +#define MAX7359_MAX_KEY_NUM (MAX7359_MAX_KEY_ROWS * MAX7359_MAX_KEY_COLS) +#define MAX7359_ROW_SHIFT 3 + +/* + * MAX7359 registers + */ +#define MAX7359_REG_KEYFIFO 0x00 +#define MAX7359_REG_CONFIG 0x01 +#define MAX7359_REG_DEBOUNCE 0x02 +#define MAX7359_REG_INTERRUPT 0x03 +#define MAX7359_REG_PORTS 0x04 +#define MAX7359_REG_KEYREP 0x05 +#define MAX7359_REG_SLEEP 0x06 + +/* + * Configuration register bits + */ +#define MAX7359_CFG_SLEEP (1 << 7) +#define MAX7359_CFG_INTERRUPT (1 << 5) +#define MAX7359_CFG_KEY_RELEASE (1 << 3) +#define MAX7359_CFG_WAKEUP (1 << 1) +#define MAX7359_CFG_TIMEOUT (1 << 0) + +/* + * Autosleep register values (ms) + */ +#define MAX7359_AUTOSLEEP_8192 0x01 +#define MAX7359_AUTOSLEEP_4096 0x02 +#define MAX7359_AUTOSLEEP_2048 0x03 +#define MAX7359_AUTOSLEEP_1024 0x04 +#define MAX7359_AUTOSLEEP_512 0x05 +#define MAX7359_AUTOSLEEP_256 0x06 + +struct max7359_keypad { + /* matrix key code map */ + unsigned short keycodes[MAX7359_MAX_KEY_NUM]; + + struct input_dev *input_dev; + struct i2c_client *client; +}; + +static int max7359_write_reg(struct i2c_client *client, u8 reg, u8 val) +{ + int ret = i2c_smbus_write_byte_data(client, reg, val); + + if (ret < 0) + dev_err(&client->dev, "%s: reg 0x%x, val 0x%x, err %d\n", + __func__, reg, val, ret); + return ret; +} + +static int max7359_read_reg(struct i2c_client *client, int reg) +{ + int ret = i2c_smbus_read_byte_data(client, reg); + + if (ret < 0) + dev_err(&client->dev, "%s: reg 0x%x, err %d\n", + __func__, reg, ret); + return ret; +} + +static void max7359_build_keycode(struct max7359_keypad *keypad, + const struct matrix_keymap_data *keymap_data) +{ + struct input_dev *input_dev = keypad->input_dev; + int i; + + for (i = 0; i < keymap_data->keymap_size; i++) { + unsigned int key = keymap_data->keymap[i]; + unsigned int row = KEY_ROW(key); + unsigned int col = KEY_COL(key); + unsigned int scancode = MATRIX_SCAN_CODE(row, col, + MAX7359_ROW_SHIFT); + unsigned short keycode = KEY_VAL(key); + + keypad->keycodes[scancode] = keycode; + __set_bit(keycode, input_dev->keybit); + } + __clear_bit(KEY_RESERVED, input_dev->keybit); +} + +/* runs in an IRQ thread -- can (and will!) sleep */ +static irqreturn_t max7359_interrupt(int irq, void *dev_id) +{ + struct max7359_keypad *keypad = dev_id; + struct input_dev *input_dev = keypad->input_dev; + int val, row, col, release, code; + + val = max7359_read_reg(keypad->client, MAX7359_REG_KEYFIFO); + row = val & 0x7; + col = (val >> 3) & 0x7; + release = val & 0x40; + + code = MATRIX_SCAN_CODE(row, col, MAX7359_ROW_SHIFT); + + dev_dbg(&keypad->client->dev, + "key[%d:%d] %s\n", row, col, release ? "release" : "press"); + + input_event(input_dev, EV_MSC, MSC_SCAN, code); + input_report_key(input_dev, keypad->keycodes[code], !release); + input_sync(input_dev); + + return IRQ_HANDLED; +} + +/* + * Let MAX7359 fall into a deep sleep: + * If no keys are pressed, enter sleep mode for 8192 ms. And if any + * key is pressed, the MAX7359 returns to normal operating mode. + */ +static inline void max7359_fall_deepsleep(struct i2c_client *client) +{ + max7359_write_reg(client, MAX7359_REG_SLEEP, MAX7359_AUTOSLEEP_8192); +} + +/* + * Let MAX7359 take a catnap: + * Autosleep just for 256 ms. + */ +static inline void max7359_take_catnap(struct i2c_client *client) +{ + max7359_write_reg(client, MAX7359_REG_SLEEP, MAX7359_AUTOSLEEP_256); +} + +static int max7359_open(struct input_dev *dev) +{ + struct max7359_keypad *keypad = input_get_drvdata(dev); + + max7359_take_catnap(keypad->client); + + return 0; +} + +static void max7359_close(struct input_dev *dev) +{ + struct max7359_keypad *keypad = input_get_drvdata(dev); + + max7359_fall_deepsleep(keypad->client); +} + +static void max7359_initialize(struct i2c_client *client) +{ + max7359_write_reg(client, MAX7359_REG_CONFIG, + MAX7359_CFG_INTERRUPT | /* Irq clears after host read */ + MAX7359_CFG_KEY_RELEASE | /* Key release enable */ + MAX7359_CFG_WAKEUP); /* Key press wakeup enable */ + + /* Full key-scan functionality */ + max7359_write_reg(client, MAX7359_REG_DEBOUNCE, 0x1F); + + /* nINT asserts every debounce cycles */ + max7359_write_reg(client, MAX7359_REG_INTERRUPT, 0x01); + + max7359_fall_deepsleep(client); +} + +static int __devinit max7359_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + const struct matrix_keymap_data *keymap_data = client->dev.platform_data; + struct max7359_keypad *keypad; + struct input_dev *input_dev; + int ret; + int error; + + if (!client->irq) { + dev_err(&client->dev, "The irq number should not be zero\n"); + return -EINVAL; + } + + /* Detect MAX7359: The initial Keys FIFO value is '0x3F' */ + ret = max7359_read_reg(client, MAX7359_REG_KEYFIFO); + if (ret < 0) { + dev_err(&client->dev, "failed to detect device\n"); + return -ENODEV; + } + + dev_dbg(&client->dev, "keys FIFO is 0x%02x\n", ret); + + keypad = kzalloc(sizeof(struct max7359_keypad), GFP_KERNEL); + input_dev = input_allocate_device(); + if (!keypad || !input_dev) { + dev_err(&client->dev, "failed to allocate memory\n"); + error = -ENOMEM; + goto failed_free_mem; + } + + keypad->client = client; + keypad->input_dev = input_dev; + + input_dev->name = client->name; + input_dev->id.bustype = BUS_I2C; + input_dev->open = max7359_open; + input_dev->close = max7359_close; + input_dev->dev.parent = &client->dev; + + input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP); + input_dev->keycodesize = sizeof(keypad->keycodes[0]); + input_dev->keycodemax = ARRAY_SIZE(keypad->keycodes); + input_dev->keycode = keypad->keycodes; + + input_set_capability(input_dev, EV_MSC, MSC_SCAN); + input_set_drvdata(input_dev, keypad); + + max7359_build_keycode(keypad, keymap_data); + + error = request_threaded_irq(client->irq, NULL, max7359_interrupt, + IRQF_TRIGGER_LOW | IRQF_ONESHOT, + client->name, keypad); + if (error) { + dev_err(&client->dev, "failed to register interrupt\n"); + goto failed_free_mem; + } + + /* Register the input device */ + error = input_register_device(input_dev); + if (error) { + dev_err(&client->dev, "failed to register input device\n"); + goto failed_free_irq; + } + + /* Initialize MAX7359 */ + max7359_initialize(client); + + i2c_set_clientdata(client, keypad); + device_init_wakeup(&client->dev, 1); + + return 0; + +failed_free_irq: + free_irq(client->irq, keypad); +failed_free_mem: + input_free_device(input_dev); + kfree(keypad); + return error; +} + +static int __devexit max7359_remove(struct i2c_client *client) +{ + struct max7359_keypad *keypad = i2c_get_clientdata(client); + + free_irq(client->irq, keypad); + input_unregister_device(keypad->input_dev); + i2c_set_clientdata(client, NULL); + kfree(keypad); + + return 0; +} + +#ifdef CONFIG_PM +static int max7359_suspend(struct i2c_client *client, pm_message_t mesg) +{ + max7359_fall_deepsleep(client); + + if (device_may_wakeup(&client->dev)) + enable_irq_wake(client->irq); + + return 0; +} + +static int max7359_resume(struct i2c_client *client) +{ + if (device_may_wakeup(&client->dev)) + disable_irq_wake(client->irq); + + /* Restore the default setting */ + max7359_take_catnap(client); + + return 0; +} +#else +#define max7359_suspend NULL +#define max7359_resume NULL +#endif + +static const struct i2c_device_id max7359_ids[] = { + { "max7359", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, max7359_ids); + +static struct i2c_driver max7359_i2c_driver = { + .driver = { + .name = "max7359", + }, + .probe = max7359_probe, + .remove = __devexit_p(max7359_remove), + .suspend = max7359_suspend, + .resume = max7359_resume, + .id_table = max7359_ids, +}; + +static int __init max7359_init(void) +{ + return i2c_add_driver(&max7359_i2c_driver); +} +module_init(max7359_init); + +static void __exit max7359_exit(void) +{ + i2c_del_driver(&max7359_i2c_driver); +} +module_exit(max7359_exit); + +MODULE_AUTHOR("Kim Kyuwon <q1.kim@samsung.com>"); +MODULE_DESCRIPTION("MAX7359 Key Switch Controller Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/input/keyboard/opencores-kbd.c b/drivers/input/keyboard/opencores-kbd.c new file mode 100644 index 000000000000..78cccddbf551 --- /dev/null +++ b/drivers/input/keyboard/opencores-kbd.c @@ -0,0 +1,180 @@ +/* + * OpenCores Keyboard Controller Driver + * http://www.opencores.org/project,keyboardcontroller + * + * Copyright 2007-2009 HV Sistemas S.L. + * + * Licensed under the GPL-2 or later. + */ + +#include <linux/input.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/ioport.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/platform_device.h> + +struct opencores_kbd { + struct input_dev *input; + struct resource *addr_res; + void __iomem *addr; + int irq; + unsigned short keycodes[128]; +}; + +static irqreturn_t opencores_kbd_isr(int irq, void *dev_id) +{ + struct opencores_kbd *opencores_kbd = dev_id; + struct input_dev *input = opencores_kbd->input; + unsigned char c; + + c = readb(opencores_kbd->addr); + input_report_key(input, c & 0x7f, c & 0x80 ? 0 : 1); + input_sync(input); + + return IRQ_HANDLED; +} + +static int __devinit opencores_kbd_probe(struct platform_device *pdev) +{ + struct input_dev *input; + struct opencores_kbd *opencores_kbd; + struct resource *res; + int irq, i, error; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "missing board memory resource\n"); + return -EINVAL; + } + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_err(&pdev->dev, "missing board IRQ resource\n"); + return -EINVAL; + } + + opencores_kbd = kzalloc(sizeof(*opencores_kbd), GFP_KERNEL); + input = input_allocate_device(); + if (!opencores_kbd || !input) { + dev_err(&pdev->dev, "failed to allocate device structures\n"); + error = -ENOMEM; + goto err_free_mem; + } + + opencores_kbd->addr_res = res; + res = request_mem_region(res->start, resource_size(res), pdev->name); + if (!res) { + dev_err(&pdev->dev, "failed to request I/O memory\n"); + error = -EBUSY; + goto err_free_mem; + } + + opencores_kbd->addr = ioremap(res->start, resource_size(res)); + if (!opencores_kbd->addr) { + dev_err(&pdev->dev, "failed to remap I/O memory\n"); + error = -ENXIO; + goto err_rel_mem; + } + + opencores_kbd->input = input; + opencores_kbd->irq = irq; + + input->name = pdev->name; + input->phys = "opencores-kbd/input0"; + input->dev.parent = &pdev->dev; + + input_set_drvdata(input, opencores_kbd); + + input->id.bustype = BUS_HOST; + input->id.vendor = 0x0001; + input->id.product = 0x0001; + input->id.version = 0x0100; + + input->keycode = opencores_kbd->keycodes; + input->keycodesize = sizeof(opencores_kbd->keycodes[0]); + input->keycodemax = ARRAY_SIZE(opencores_kbd->keycodes); + + __set_bit(EV_KEY, input->evbit); + + for (i = 0; i < ARRAY_SIZE(opencores_kbd->keycodes); i++) { + /* + * OpenCores controller happens to have scancodes match + * our KEY_* definitions. + */ + opencores_kbd->keycodes[i] = i; + __set_bit(opencores_kbd->keycodes[i], input->keybit); + } + __clear_bit(KEY_RESERVED, input->keybit); + + error = request_irq(irq, &opencores_kbd_isr, + IRQF_TRIGGER_RISING, pdev->name, opencores_kbd); + if (error) { + dev_err(&pdev->dev, "unable to claim irq %d\n", irq); + goto err_unmap_mem; + } + + error = input_register_device(input); + if (error) { + dev_err(&pdev->dev, "unable to register input device\n"); + goto err_free_irq; + } + + platform_set_drvdata(pdev, opencores_kbd); + + return 0; + + err_free_irq: + free_irq(irq, opencores_kbd); + err_unmap_mem: + iounmap(opencores_kbd->addr); + err_rel_mem: + release_mem_region(res->start, resource_size(res)); + err_free_mem: + input_free_device(input); + kfree(opencores_kbd); + + return error; +} + +static int __devexit opencores_kbd_remove(struct platform_device *pdev) +{ + struct opencores_kbd *opencores_kbd = platform_get_drvdata(pdev); + + free_irq(opencores_kbd->irq, opencores_kbd); + + iounmap(opencores_kbd->addr); + release_mem_region(opencores_kbd->addr_res->start, + resource_size(opencores_kbd->addr_res)); + input_unregister_device(opencores_kbd->input); + kfree(opencores_kbd); + + platform_set_drvdata(pdev, NULL); + + return 0; +} + +static struct platform_driver opencores_kbd_device_driver = { + .probe = opencores_kbd_probe, + .remove = __devexit_p(opencores_kbd_remove), + .driver = { + .name = "opencores-kbd", + }, +}; + +static int __init opencores_kbd_init(void) +{ + return platform_driver_register(&opencores_kbd_device_driver); +} +module_init(opencores_kbd_init); + +static void __exit opencores_kbd_exit(void) +{ + platform_driver_unregister(&opencores_kbd_device_driver); +} +module_exit(opencores_kbd_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Javier Herrero <jherrero@hvsistemas.es>"); +MODULE_DESCRIPTION("Keyboard driver for OpenCores Keyboard Controller"); diff --git a/drivers/input/keyboard/qt2160.c b/drivers/input/keyboard/qt2160.c new file mode 100644 index 000000000000..191cc51d6cf8 --- /dev/null +++ b/drivers/input/keyboard/qt2160.c @@ -0,0 +1,397 @@ +/* + * qt2160.c - Atmel AT42QT2160 Touch Sense Controller + * + * Copyright (C) 2009 Raphael Derosso Pereira <raphaelpereira@gmail.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/jiffies.h> +#include <linux/i2c.h> +#include <linux/irq.h> +#include <linux/interrupt.h> +#include <linux/input.h> + +#define QT2160_VALID_CHIPID 0x11 + +#define QT2160_CMD_CHIPID 0 +#define QT2160_CMD_CODEVER 1 +#define QT2160_CMD_GSTAT 2 +#define QT2160_CMD_KEYS3 3 +#define QT2160_CMD_KEYS4 4 +#define QT2160_CMD_SLIDE 5 +#define QT2160_CMD_GPIOS 6 +#define QT2160_CMD_SUBVER 7 +#define QT2160_CMD_CALIBRATE 10 + +#define QT2160_CYCLE_INTERVAL (2*HZ) + +static unsigned char qt2160_key2code[] = { + KEY_0, KEY_1, KEY_2, KEY_3, + KEY_4, KEY_5, KEY_6, KEY_7, + KEY_8, KEY_9, KEY_A, KEY_B, + KEY_C, KEY_D, KEY_E, KEY_F, +}; + +struct qt2160_data { + struct i2c_client *client; + struct input_dev *input; + struct delayed_work dwork; + spinlock_t lock; /* Protects canceling/rescheduling of dwork */ + unsigned short keycodes[ARRAY_SIZE(qt2160_key2code)]; + u16 key_matrix; +}; + +static int qt2160_read_block(struct i2c_client *client, + u8 inireg, u8 *buffer, unsigned int count) +{ + int error, idx = 0; + + /* + * Can't use SMBus block data read. Check for I2C functionality to speed + * things up whenever possible. Otherwise we will be forced to read + * sequentially. + */ + if (i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + + error = i2c_smbus_write_byte(client, inireg + idx); + if (error) { + dev_err(&client->dev, + "couldn't send request. Returned %d\n", error); + return error; + } + + error = i2c_master_recv(client, buffer, count); + if (error != count) { + dev_err(&client->dev, + "couldn't read registers. Returned %d bytes\n", error); + return error; + } + } else { + + while (count--) { + int data; + + error = i2c_smbus_write_byte(client, inireg + idx); + if (error) { + dev_err(&client->dev, + "couldn't send request. Returned %d\n", error); + return error; + } + + data = i2c_smbus_read_byte(client); + if (data < 0) { + dev_err(&client->dev, + "couldn't read register. Returned %d\n", data); + return data; + } + + buffer[idx++] = data; + } + } + + return 0; +} + +static int qt2160_get_key_matrix(struct qt2160_data *qt2160) +{ + struct i2c_client *client = qt2160->client; + struct input_dev *input = qt2160->input; + u8 regs[6]; + u16 old_matrix, new_matrix; + int ret, i, mask; + + dev_dbg(&client->dev, "requesting keys...\n"); + + /* + * Read all registers from General Status Register + * to GPIOs register + */ + ret = qt2160_read_block(client, QT2160_CMD_GSTAT, regs, 6); + if (ret) { + dev_err(&client->dev, + "could not perform chip read.\n"); + return ret; + } + + old_matrix = qt2160->key_matrix; + qt2160->key_matrix = new_matrix = (regs[2] << 8) | regs[1]; + + mask = 0x01; + for (i = 0; i < 16; ++i, mask <<= 1) { + int keyval = new_matrix & mask; + + if ((old_matrix & mask) != keyval) { + input_report_key(input, qt2160->keycodes[i], keyval); + dev_dbg(&client->dev, "key %d %s\n", + i, keyval ? "pressed" : "released"); + } + } + + input_sync(input); + + return 0; +} + +static irqreturn_t qt2160_irq(int irq, void *_qt2160) +{ + struct qt2160_data *qt2160 = _qt2160; + unsigned long flags; + + spin_lock_irqsave(&qt2160->lock, flags); + + __cancel_delayed_work(&qt2160->dwork); + schedule_delayed_work(&qt2160->dwork, 0); + + spin_unlock_irqrestore(&qt2160->lock, flags); + + return IRQ_HANDLED; +} + +static void qt2160_schedule_read(struct qt2160_data *qt2160) +{ + spin_lock_irq(&qt2160->lock); + schedule_delayed_work(&qt2160->dwork, QT2160_CYCLE_INTERVAL); + spin_unlock_irq(&qt2160->lock); +} + +static void qt2160_worker(struct work_struct *work) +{ + struct qt2160_data *qt2160 = + container_of(work, struct qt2160_data, dwork.work); + + dev_dbg(&qt2160->client->dev, "worker\n"); + + qt2160_get_key_matrix(qt2160); + + /* Avoid device lock up by checking every so often */ + qt2160_schedule_read(qt2160); +} + +static int __devinit qt2160_read(struct i2c_client *client, u8 reg) +{ + int ret; + + ret = i2c_smbus_write_byte(client, reg); + if (ret) { + dev_err(&client->dev, + "couldn't send request. Returned %d\n", ret); + return ret; + } + + ret = i2c_smbus_read_byte(client); + if (ret < 0) { + dev_err(&client->dev, + "couldn't read register. Returned %d\n", ret); + return ret; + } + + return ret; +} + +static int __devinit qt2160_write(struct i2c_client *client, u8 reg, u8 data) +{ + int error; + + error = i2c_smbus_write_byte(client, reg); + if (error) { + dev_err(&client->dev, + "couldn't send request. Returned %d\n", error); + return error; + } + + error = i2c_smbus_write_byte(client, data); + if (error) { + dev_err(&client->dev, + "couldn't write data. Returned %d\n", error); + return error; + } + + return error; +} + + +static bool __devinit qt2160_identify(struct i2c_client *client) +{ + int id, ver, rev; + + /* Read Chid ID to check if chip is valid */ + id = qt2160_read(client, QT2160_CMD_CHIPID); + if (id != QT2160_VALID_CHIPID) { + dev_err(&client->dev, "ID %d not supported\n", id); + return false; + } + + /* Read chip firmware version */ + ver = qt2160_read(client, QT2160_CMD_CODEVER); + if (ver < 0) { + dev_err(&client->dev, "could not get firmware version\n"); + return false; + } + + /* Read chip firmware revision */ + rev = qt2160_read(client, QT2160_CMD_SUBVER); + if (rev < 0) { + dev_err(&client->dev, "could not get firmware revision\n"); + return false; + } + + dev_info(&client->dev, "AT42QT2160 firmware version %d.%d.%d\n", + ver >> 4, ver & 0xf, rev); + + return true; +} + +static int __devinit qt2160_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct qt2160_data *qt2160; + struct input_dev *input; + int i; + int error; + + /* Check functionality */ + error = i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_BYTE); + if (!error) { + dev_err(&client->dev, "%s adapter not supported\n", + dev_driver_string(&client->adapter->dev)); + return -ENODEV; + } + + if (!qt2160_identify(client)) + return -ENODEV; + + /* Chip is valid and active. Allocate structure */ + qt2160 = kzalloc(sizeof(struct qt2160_data), GFP_KERNEL); + input = input_allocate_device(); + if (!qt2160 || !input) { + dev_err(&client->dev, "insufficient memory\n"); + error = -ENOMEM; + goto err_free_mem; + } + + qt2160->client = client; + qt2160->input = input; + INIT_DELAYED_WORK(&qt2160->dwork, qt2160_worker); + spin_lock_init(&qt2160->lock); + + input->name = "AT42QT2160 Touch Sense Keyboard"; + input->id.bustype = BUS_I2C; + + input->keycode = qt2160->keycodes; + input->keycodesize = sizeof(qt2160->keycodes[0]); + input->keycodemax = ARRAY_SIZE(qt2160_key2code); + + __set_bit(EV_KEY, input->evbit); + __clear_bit(EV_REP, input->evbit); + for (i = 0; i < ARRAY_SIZE(qt2160_key2code); i++) { + qt2160->keycodes[i] = qt2160_key2code[i]; + __set_bit(qt2160_key2code[i], input->keybit); + } + __clear_bit(KEY_RESERVED, input->keybit); + + /* Calibrate device */ + error = qt2160_write(client, QT2160_CMD_CALIBRATE, 1); + if (error) { + dev_err(&client->dev, "failed to calibrate device\n"); + goto err_free_mem; + } + + if (client->irq) { + error = request_irq(client->irq, qt2160_irq, + IRQF_TRIGGER_FALLING, "qt2160", qt2160); + if (error) { + dev_err(&client->dev, + "failed to allocate irq %d\n", client->irq); + goto err_free_mem; + } + } + + error = input_register_device(qt2160->input); + if (error) { + dev_err(&client->dev, + "Failed to register input device\n"); + goto err_free_irq; + } + + i2c_set_clientdata(client, qt2160); + qt2160_schedule_read(qt2160); + + return 0; + +err_free_irq: + if (client->irq) + free_irq(client->irq, qt2160); +err_free_mem: + input_free_device(input); + kfree(qt2160); + return error; +} + +static int __devexit qt2160_remove(struct i2c_client *client) +{ + struct qt2160_data *qt2160 = i2c_get_clientdata(client); + + /* Release IRQ so no queue will be scheduled */ + if (client->irq) + free_irq(client->irq, qt2160); + + cancel_delayed_work_sync(&qt2160->dwork); + + input_unregister_device(qt2160->input); + kfree(qt2160); + + i2c_set_clientdata(client, NULL); + return 0; +} + +static struct i2c_device_id qt2160_idtable[] = { + { "qt2160", 0, }, + { } +}; + +MODULE_DEVICE_TABLE(i2c, qt2160_idtable); + +static struct i2c_driver qt2160_driver = { + .driver = { + .name = "qt2160", + .owner = THIS_MODULE, + }, + + .id_table = qt2160_idtable, + .probe = qt2160_probe, + .remove = __devexit_p(qt2160_remove), +}; + +static int __init qt2160_init(void) +{ + return i2c_add_driver(&qt2160_driver); +} +module_init(qt2160_init); + +static void __exit qt2160_cleanup(void) +{ + i2c_del_driver(&qt2160_driver); +} +module_exit(qt2160_cleanup); + +MODULE_AUTHOR("Raphael Derosso Pereira <raphaelpereira@gmail.com>"); +MODULE_DESCRIPTION("Driver for AT42QT2160 Touch Sensor"); +MODULE_LICENSE("GPL"); diff --git a/drivers/input/keyboard/sunkbd.c b/drivers/input/keyboard/sunkbd.c index 472b56639cdb..a99a04b03ee4 100644 --- a/drivers/input/keyboard/sunkbd.c +++ b/drivers/input/keyboard/sunkbd.c @@ -27,6 +27,7 @@ */ #include <linux/delay.h> +#include <linux/sched.h> #include <linux/slab.h> #include <linux/module.h> #include <linux/interrupt.h> diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig index 76d6751f89a7..a9bb2544b2de 100644 --- a/drivers/input/misc/Kconfig +++ b/drivers/input/misc/Kconfig @@ -225,7 +225,9 @@ config INPUT_SGI_BTNS config INPUT_WINBOND_CIR tristate "Winbond IR remote control" depends on X86 && PNP + select NEW_LEDS select LEDS_CLASS + select LEDS_TRIGGERS select BITREVERSE help Say Y here if you want to use the IR remote functionality found diff --git a/drivers/input/misc/dm355evm_keys.c b/drivers/input/misc/dm355evm_keys.c index 0918acae584a..f2b67dc81d80 100644 --- a/drivers/input/misc/dm355evm_keys.c +++ b/drivers/input/misc/dm355evm_keys.c @@ -96,7 +96,13 @@ static struct { { 0x3169, KEY_PAUSE, }, }; -/* runs in an IRQ thread -- can (and will!) sleep */ +/* + * Because we communicate with the MSP430 using I2C, and all I2C calls + * in Linux sleep, we use a threaded IRQ handler. The IRQ itself is + * active low, but we go through the GPIO controller so we can trigger + * on falling edges and not worry about enabling/disabling the IRQ in + * the keypress handling path. + */ static irqreturn_t dm355evm_keys_irq(int irq, void *_keys) { struct dm355evm_keys *keys = _keys; @@ -171,18 +177,6 @@ static irqreturn_t dm355evm_keys_irq(int irq, void *_keys) return IRQ_HANDLED; } -/* - * Because we communicate with the MSP430 using I2C, and all I2C calls - * in Linux sleep, we use a threaded IRQ handler. The IRQ itself is - * active low, but we go through the GPIO controller so we can trigger - * on falling edges and not worry about enabling/disabling the IRQ in - * the keypress handling path. - */ -static irqreturn_t dm355evm_keys_hardirq(int irq, void *_keys) -{ - return IRQ_WAKE_THREAD; -} - static int dm355evm_setkeycode(struct input_dev *dev, int index, int keycode) { u16 old_keycode; @@ -257,10 +251,8 @@ static int __devinit dm355evm_keys_probe(struct platform_device *pdev) /* REVISIT: flush the event queue? */ - status = request_threaded_irq(keys->irq, - dm355evm_keys_hardirq, dm355evm_keys_irq, - IRQF_TRIGGER_FALLING, - dev_name(&pdev->dev), keys); + status = request_threaded_irq(keys->irq, NULL, dm355evm_keys_irq, + IRQF_TRIGGER_FALLING, dev_name(&pdev->dev), keys); if (status < 0) goto fail1; diff --git a/drivers/input/misc/hp_sdc_rtc.c b/drivers/input/misc/hp_sdc_rtc.c index 216a559f55ea..ea821b546969 100644 --- a/drivers/input/misc/hp_sdc_rtc.c +++ b/drivers/input/misc/hp_sdc_rtc.c @@ -209,7 +209,7 @@ static inline int hp_sdc_rtc_read_rt(struct timeval *res) { /* Read the i8042 fast handshake timer */ static inline int hp_sdc_rtc_read_fhs(struct timeval *res) { - uint64_t raw; + int64_t raw; unsigned int tenms; raw = hp_sdc_rtc_read_i8042timer(HP_SDC_CMD_LOAD_FHS, 2); diff --git a/drivers/input/misc/rotary_encoder.c b/drivers/input/misc/rotary_encoder.c index c806fbf1e174..3b9f588fc747 100644 --- a/drivers/input/misc/rotary_encoder.c +++ b/drivers/input/misc/rotary_encoder.c @@ -106,8 +106,8 @@ static int __devinit rotary_encoder_probe(struct platform_device *pdev) struct input_dev *input; int err; - if (!pdata || !pdata->steps) { - dev_err(&pdev->dev, "invalid platform data\n"); + if (!pdata) { + dev_err(&pdev->dev, "missing platform data\n"); return -ENOENT; } diff --git a/drivers/input/misc/sparcspkr.c b/drivers/input/misc/sparcspkr.c index c4f42311fdec..b064419b90a2 100644 --- a/drivers/input/misc/sparcspkr.c +++ b/drivers/input/misc/sparcspkr.c @@ -230,7 +230,7 @@ out_err: return err; } -static int bbc_remove(struct of_device *op) +static int __devexit bbc_remove(struct of_device *op) { struct sparcspkr_state *state = dev_get_drvdata(&op->dev); struct input_dev *input_dev = state->input_dev; @@ -308,7 +308,7 @@ out_err: return err; } -static int grover_remove(struct of_device *op) +static int __devexit grover_remove(struct of_device *op) { struct sparcspkr_state *state = dev_get_drvdata(&op->dev); struct grover_beep_info *info = &state->u.grover; diff --git a/drivers/input/misc/uinput.c b/drivers/input/misc/uinput.c index c5a49aba418f..d3f57245420a 100644 --- a/drivers/input/misc/uinput.c +++ b/drivers/input/misc/uinput.c @@ -30,6 +30,7 @@ * - first public version */ #include <linux/poll.h> +#include <linux/sched.h> #include <linux/slab.h> #include <linux/module.h> #include <linux/init.h> diff --git a/drivers/input/misc/wistron_btns.c b/drivers/input/misc/wistron_btns.c index 11fd038a078f..a932179c4c9e 100644 --- a/drivers/input/misc/wistron_btns.c +++ b/drivers/input/misc/wistron_btns.c @@ -936,6 +936,15 @@ static struct dmi_system_id dmi_ids[] __initdata = { }, { .callback = dmi_matched, + .ident = "Medion MD 42200", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Medion"), + DMI_MATCH(DMI_PRODUCT_NAME, "WIM 2030"), + }, + .driver_data = keymap_fs_amilo_pro_v2000 + }, + { + .callback = dmi_matched, .ident = "Medion MD 96500", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "MEDIONPC"), diff --git a/drivers/input/mouse/lifebook.c b/drivers/input/mouse/lifebook.c index 5e6308694408..82811558ec33 100644 --- a/drivers/input/mouse/lifebook.c +++ b/drivers/input/mouse/lifebook.c @@ -107,8 +107,7 @@ static const struct dmi_system_id lifebook_dmi_table[] = { .matches = { DMI_MATCH(DMI_PRODUCT_NAME, "CF-72"), }, - .callback = lifebook_set_serio_phys, - .driver_data = "isa0060/serio3", + .callback = lifebook_set_6byte_proto, }, { .ident = "Lifebook B142", diff --git a/drivers/input/mouse/logips2pp.c b/drivers/input/mouse/logips2pp.c index de745d751162..ab5dc5f5fd83 100644 --- a/drivers/input/mouse/logips2pp.c +++ b/drivers/input/mouse/logips2pp.c @@ -219,7 +219,7 @@ static const struct ps2pp_info *get_model_info(unsigned char model) PS2PP_WHEEL | PS2PP_SIDE_BTN | PS2PP_TASK_BTN | PS2PP_EXTRA_BTN | PS2PP_NAV_BTN | PS2PP_HWHEEL }, { 72, PS2PP_KIND_TRACKMAN, 0 }, /* T-CH11: TrackMan Marble */ - { 73, 0, PS2PP_SIDE_BTN }, + { 73, PS2PP_KIND_TRACKMAN, PS2PP_SIDE_BTN }, /* TrackMan FX */ { 75, PS2PP_KIND_WHEEL, PS2PP_WHEEL }, { 76, PS2PP_KIND_WHEEL, PS2PP_WHEEL }, { 79, PS2PP_KIND_TRACKMAN, PS2PP_WHEEL }, /* TrackMan with wheel */ diff --git a/drivers/input/mouse/psmouse-base.c b/drivers/input/mouse/psmouse-base.c index 690aed905436..07c53798301a 100644 --- a/drivers/input/mouse/psmouse-base.c +++ b/drivers/input/mouse/psmouse-base.c @@ -581,7 +581,7 @@ static int cortron_detect(struct psmouse *psmouse, bool set_properties) static int psmouse_extensions(struct psmouse *psmouse, unsigned int max_proto, bool set_properties) { - bool synaptics_hardware = true; + bool synaptics_hardware = false; /* * We always check for lifebook because it does not disturb mouse @@ -1673,7 +1673,7 @@ static int psmouse_get_maxproto(char *buffer, struct kernel_param *kp) { int type = *((unsigned int *)kp->arg); - return sprintf(buffer, "%s\n", psmouse_protocol_by_type(type)->name); + return sprintf(buffer, "%s", psmouse_protocol_by_type(type)->name); } static int __init psmouse_init(void) diff --git a/drivers/input/mouse/sentelic.c b/drivers/input/mouse/sentelic.c index 84e2fc04d11b..f84cbd97c884 100644 --- a/drivers/input/mouse/sentelic.c +++ b/drivers/input/mouse/sentelic.c @@ -92,7 +92,8 @@ static int fsp_reg_read(struct psmouse *psmouse, int reg_addr, int *reg_val) */ ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE); psmouse_set_state(psmouse, PSMOUSE_CMD_MODE); - mutex_lock(&ps2dev->cmd_mutex); + + ps2_begin_command(ps2dev); if (ps2_sendbyte(ps2dev, 0xf3, FSP_CMD_TIMEOUT) < 0) goto out; @@ -126,7 +127,7 @@ static int fsp_reg_read(struct psmouse *psmouse, int reg_addr, int *reg_val) rc = 0; out: - mutex_unlock(&ps2dev->cmd_mutex); + ps2_end_command(ps2dev); ps2_command(ps2dev, NULL, PSMOUSE_CMD_ENABLE); psmouse_set_state(psmouse, PSMOUSE_ACTIVATED); dev_dbg(&ps2dev->serio->dev, "READ REG: 0x%02x is 0x%02x (rc = %d)\n", @@ -140,7 +141,7 @@ static int fsp_reg_write(struct psmouse *psmouse, int reg_addr, int reg_val) unsigned char v; int rc = -1; - mutex_lock(&ps2dev->cmd_mutex); + ps2_begin_command(ps2dev); if (ps2_sendbyte(ps2dev, 0xf3, FSP_CMD_TIMEOUT) < 0) goto out; @@ -179,7 +180,7 @@ static int fsp_reg_write(struct psmouse *psmouse, int reg_addr, int reg_val) rc = 0; out: - mutex_unlock(&ps2dev->cmd_mutex); + ps2_end_command(ps2dev); dev_dbg(&ps2dev->serio->dev, "WRITE REG: 0x%02x to 0x%02x (rc = %d)\n", reg_addr, reg_val, rc); return rc; @@ -214,7 +215,8 @@ static int fsp_page_reg_read(struct psmouse *psmouse, int *reg_val) ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE); psmouse_set_state(psmouse, PSMOUSE_CMD_MODE); - mutex_lock(&ps2dev->cmd_mutex); + + ps2_begin_command(ps2dev); if (ps2_sendbyte(ps2dev, 0xf3, FSP_CMD_TIMEOUT) < 0) goto out; @@ -236,7 +238,7 @@ static int fsp_page_reg_read(struct psmouse *psmouse, int *reg_val) rc = 0; out: - mutex_unlock(&ps2dev->cmd_mutex); + ps2_end_command(ps2dev); ps2_command(ps2dev, NULL, PSMOUSE_CMD_ENABLE); psmouse_set_state(psmouse, PSMOUSE_ACTIVATED); dev_dbg(&ps2dev->serio->dev, "READ PAGE REG: 0x%02x (rc = %d)\n", @@ -250,7 +252,7 @@ static int fsp_page_reg_write(struct psmouse *psmouse, int reg_val) unsigned char v; int rc = -1; - mutex_lock(&ps2dev->cmd_mutex); + ps2_begin_command(ps2dev); if (ps2_sendbyte(ps2dev, 0xf3, FSP_CMD_TIMEOUT) < 0) goto out; @@ -275,7 +277,7 @@ static int fsp_page_reg_write(struct psmouse *psmouse, int reg_val) rc = 0; out: - mutex_unlock(&ps2dev->cmd_mutex); + ps2_end_command(ps2dev); dev_dbg(&ps2dev->serio->dev, "WRITE PAGE REG: to 0x%02x (rc = %d)\n", reg_val, rc); return rc; diff --git a/drivers/input/mouse/synaptics.c b/drivers/input/mouse/synaptics.c index b66ff1ac7dea..f4a61252bcc9 100644 --- a/drivers/input/mouse/synaptics.c +++ b/drivers/input/mouse/synaptics.c @@ -652,6 +652,16 @@ static const struct dmi_system_id toshiba_dmi_table[] = { DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"), DMI_MATCH(DMI_PRODUCT_NAME, "PORTEGE M300"), }, + + }, + { + .ident = "Toshiba Portege M300", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"), + DMI_MATCH(DMI_PRODUCT_NAME, "Portable PC"), + DMI_MATCH(DMI_PRODUCT_VERSION, "Version 1.0"), + }, + }, { } }; diff --git a/drivers/input/mouse/synaptics_i2c.c b/drivers/input/mouse/synaptics_i2c.c index eac9fdde7ee9..7283c78044af 100644 --- a/drivers/input/mouse/synaptics_i2c.c +++ b/drivers/input/mouse/synaptics_i2c.c @@ -203,7 +203,7 @@ MODULE_PARM_DESC(no_filter, "No Filter. Default = 0 (off)"); * and the irq configuration should be set to Falling Edge Trigger */ /* Control IRQ / Polling option */ -static int polling_req; +static bool polling_req; module_param(polling_req, bool, 0444); MODULE_PARM_DESC(polling_req, "Request Polling. Default = 0 (use irq)"); @@ -217,6 +217,7 @@ struct synaptics_i2c { struct i2c_client *client; struct input_dev *input; struct delayed_work dwork; + spinlock_t lock; int no_data_count; int no_decel_param; int reduce_report_param; @@ -366,17 +367,28 @@ static bool synaptics_i2c_get_input(struct synaptics_i2c *touch) return xy_delta || gesture; } -static irqreturn_t synaptics_i2c_irq(int irq, void *dev_id) +static void synaptics_i2c_reschedule_work(struct synaptics_i2c *touch, + unsigned long delay) { - struct synaptics_i2c *touch = dev_id; + unsigned long flags; + + spin_lock_irqsave(&touch->lock, flags); /* - * We want to have the work run immediately but it might have - * already been scheduled with a delay, that's why we have to - * cancel it first. + * If work is already scheduled then subsequent schedules will not + * change the scheduled time that's why we have to cancel it first. */ - cancel_delayed_work(&touch->dwork); - schedule_delayed_work(&touch->dwork, 0); + __cancel_delayed_work(&touch->dwork); + schedule_delayed_work(&touch->dwork, delay); + + spin_unlock_irqrestore(&touch->lock, flags); +} + +static irqreturn_t synaptics_i2c_irq(int irq, void *dev_id) +{ + struct synaptics_i2c *touch = dev_id; + + synaptics_i2c_reschedule_work(touch, 0); return IRQ_HANDLED; } @@ -452,7 +464,7 @@ static void synaptics_i2c_work_handler(struct work_struct *work) * We poll the device once in THREAD_IRQ_SLEEP_SECS and * if error is detected, we try to reset and reconfigure the touchpad. */ - schedule_delayed_work(&touch->dwork, delay); + synaptics_i2c_reschedule_work(touch, delay); } static int synaptics_i2c_open(struct input_dev *input) @@ -465,8 +477,8 @@ static int synaptics_i2c_open(struct input_dev *input) return ret; if (polling_req) - schedule_delayed_work(&touch->dwork, - msecs_to_jiffies(NO_DATA_SLEEP_MSECS)); + synaptics_i2c_reschedule_work(touch, + msecs_to_jiffies(NO_DATA_SLEEP_MSECS)); return 0; } @@ -521,6 +533,7 @@ struct synaptics_i2c *synaptics_i2c_touch_create(struct i2c_client *client) touch->scan_rate_param = scan_rate; set_scan_rate(touch, scan_rate); INIT_DELAYED_WORK(&touch->dwork, synaptics_i2c_work_handler); + spin_lock_init(&touch->lock); return touch; } @@ -535,14 +548,12 @@ static int __devinit synaptics_i2c_probe(struct i2c_client *client, if (!touch) return -ENOMEM; - i2c_set_clientdata(client, touch); - ret = synaptics_i2c_reset_config(client); if (ret) goto err_mem_free; if (client->irq < 1) - polling_req = 1; + polling_req = true; touch->input = input_allocate_device(); if (!touch->input) { @@ -563,7 +574,7 @@ static int __devinit synaptics_i2c_probe(struct i2c_client *client, dev_warn(&touch->client->dev, "IRQ request failed: %d, " "falling back to polling\n", ret); - polling_req = 1; + polling_req = true; synaptics_i2c_reg_set(touch->client, INTERRUPT_EN_REG, 0); } @@ -580,12 +591,14 @@ static int __devinit synaptics_i2c_probe(struct i2c_client *client, "Input device register failed: %d\n", ret); goto err_input_free; } + + i2c_set_clientdata(client, touch); + return 0; err_input_free: input_free_device(touch->input); err_mem_free: - i2c_set_clientdata(client, NULL); kfree(touch); return ret; @@ -596,7 +609,7 @@ static int __devexit synaptics_i2c_remove(struct i2c_client *client) struct synaptics_i2c *touch = i2c_get_clientdata(client); if (!polling_req) - free_irq(touch->client->irq, touch); + free_irq(client->irq, touch); input_unregister_device(touch->input); i2c_set_clientdata(client, NULL); @@ -627,8 +640,8 @@ static int synaptics_i2c_resume(struct i2c_client *client) if (ret) return ret; - schedule_delayed_work(&touch->dwork, - msecs_to_jiffies(NO_DATA_SLEEP_MSECS)); + synaptics_i2c_reschedule_work(touch, + msecs_to_jiffies(NO_DATA_SLEEP_MSECS)); return 0; } diff --git a/drivers/input/mousedev.c b/drivers/input/mousedev.c index 966b8868f792..a13d80f7da17 100644 --- a/drivers/input/mousedev.c +++ b/drivers/input/mousedev.c @@ -13,6 +13,7 @@ #define MOUSEDEV_MINORS 32 #define MOUSEDEV_MIX 31 +#include <linux/sched.h> #include <linux/slab.h> #include <linux/smp_lock.h> #include <linux/poll.h> diff --git a/drivers/input/serio/Kconfig b/drivers/input/serio/Kconfig index c4b3fbd1a80f..aa533ceffe34 100644 --- a/drivers/input/serio/Kconfig +++ b/drivers/input/serio/Kconfig @@ -4,7 +4,7 @@ config SERIO tristate "Serial I/O support" if EMBEDDED || !X86 default y - ---help--- + help Say Yes here if you have any input device that uses serial I/O to communicate with the system. This includes the * standard AT keyboard and PS/2 mouse * @@ -22,7 +22,7 @@ config SERIO_I8042 tristate "i8042 PC Keyboard controller" if EMBEDDED || !X86 default y depends on !PARISC && (!ARM || ARCH_SHARK || FOOTBRIDGE_HOST) && !M68K && !BLACKFIN - ---help--- + help i8042 is the chip over which the standard AT keyboard and PS/2 mouse are connected to the computer. If you use these devices, you'll need to say Y here. @@ -35,7 +35,7 @@ config SERIO_I8042 config SERIO_SERPORT tristate "Serial port line discipline" default y - ---help--- + help Say Y here if you plan to use an input device (mouse, joystick, tablet, 6dof) that communicates over the RS232 serial (COM) port. @@ -49,7 +49,7 @@ config SERIO_SERPORT config SERIO_CT82C710 tristate "ct82c710 Aux port controller" depends on X86 - ---help--- + help Say Y here if you have a Texas Instruments TravelMate notebook equipped with the ct82c710 chip and want to use a mouse connected to the "QuickPort". @@ -66,7 +66,7 @@ config SERIO_Q40KBD config SERIO_PARKBD tristate "Parallel port keyboard adapter" depends on PARPORT - ---help--- + help Say Y here if you built a simple parallel port adapter to attach an additional AT keyboard, XT keyboard or PS/2 mouse. @@ -124,7 +124,7 @@ config HP_SDC tristate "HP System Device Controller i8042 Support" depends on (GSC || HP300) && SERIO default y - ---help--- + help This option enables support for the "System Device Controller", an i8042 carrying microcode to manage a few miscellaneous devices on some Hewlett Packard systems. @@ -168,6 +168,7 @@ config SERIO_MACEPS2 config SERIO_LIBPS2 tristate "PS/2 driver library" if EMBEDDED + depends on SERIO_I8042 || SERIO_I8042=n help Say Y here if you are using a driver for device connected to a PS/2 port, such as PS/2 mouse or standard AT keyboard. diff --git a/drivers/input/serio/i8042-x86ia64io.h b/drivers/input/serio/i8042-x86ia64io.h index a39bc4eb902b..a537925f7651 100644 --- a/drivers/input/serio/i8042-x86ia64io.h +++ b/drivers/input/serio/i8042-x86ia64io.h @@ -327,6 +327,17 @@ static struct dmi_system_id __initdata i8042_dmi_nomux_table[] = { }, }, { + /* + * Reset and GET ID commands issued via KBD port are + * sometimes being delivered to AUX3. + */ + .ident = "Sony Vaio FZ-240E", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"), + DMI_MATCH(DMI_PRODUCT_NAME, "VGN-FZ240E"), + }, + }, + { .ident = "Amoi M636/A737", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Amoi Electronics CO.,LTD."), @@ -661,7 +672,7 @@ static void i8042_pnp_exit(void) static int __init i8042_pnp_init(void) { char kbd_irq_str[4] = { 0 }, aux_irq_str[4] = { 0 }; - int pnp_data_busted = false; + bool pnp_data_busted = false; int err; #ifdef CONFIG_X86 diff --git a/drivers/input/serio/i8042.c b/drivers/input/serio/i8042.c index eb3ff94af58c..1df02d25aca5 100644 --- a/drivers/input/serio/i8042.c +++ b/drivers/input/serio/i8042.c @@ -87,8 +87,22 @@ static bool i8042_bypass_aux_irq_test; #include "i8042.h" +/* + * i8042_lock protects serialization between i8042_command and + * the interrupt handler. + */ static DEFINE_SPINLOCK(i8042_lock); +/* + * Writers to AUX and KBD ports as well as users issuing i8042_command + * directly should acquire i8042_mutex (by means of calling + * i8042_lock_chip() and i8042_unlock_ship() helpers) to ensure that + * they do not disturb each other (unfortunately in many i8042 + * implementations write to one of the ports will immediately abort + * command that is being processed by another port). + */ +static DEFINE_MUTEX(i8042_mutex); + struct i8042_port { struct serio *serio; int irq; @@ -113,6 +127,18 @@ static struct platform_device *i8042_platform_device; static irqreturn_t i8042_interrupt(int irq, void *dev_id); +void i8042_lock_chip(void) +{ + mutex_lock(&i8042_mutex); +} +EXPORT_SYMBOL(i8042_lock_chip); + +void i8042_unlock_chip(void) +{ + mutex_unlock(&i8042_mutex); +} +EXPORT_SYMBOL(i8042_unlock_chip); + /* * The i8042_wait_read() and i8042_wait_write functions wait for the i8042 to * be ready for reading values from it / writing values to it. @@ -583,6 +609,8 @@ static irqreturn_t __init i8042_aux_test_irq(int irq, void *dev_id) str = i8042_read_status(); if (str & I8042_STR_OBF) { data = i8042_read_data(); + dbg("%02x <- i8042 (aux_test_irq, %s)", + data, str & I8042_STR_AUXDATA ? "aux" : "kbd"); if (i8042_irq_being_tested && data == 0xa5 && (str & I8042_STR_AUXDATA)) complete(&i8042_aux_irq_delivered); @@ -724,6 +752,7 @@ static int __init i8042_check_aux(void) * AUX IRQ was never delivered so we need to flush the controller to * get rid of the byte we put there; otherwise keyboard may not work. */ + dbg(" -- i8042 (aux irq test timeout)"); i8042_flush(); retval = -1; } @@ -807,17 +836,32 @@ static int i8042_controller_selftest(void) static int i8042_controller_init(void) { unsigned long flags; + int n = 0; + unsigned char ctr[2]; /* - * Save the CTR for restoral on unload / reboot. + * Save the CTR for restore on unload / reboot. */ - if (i8042_command(&i8042_ctr, I8042_CMD_CTL_RCTR)) { - printk(KERN_ERR "i8042.c: Can't read CTR while initializing i8042.\n"); - return -EIO; - } + do { + if (n >= 10) { + printk(KERN_ERR + "i8042.c: Unable to get stable CTR read.\n"); + return -EIO; + } + + if (n != 0) + udelay(50); + + if (i8042_command(&ctr[n++ % 2], I8042_CMD_CTL_RCTR)) { + printk(KERN_ERR + "i8042.c: Can't read CTR while initializing i8042.\n"); + return -EIO; + } + + } while (n < 2 || ctr[0] != ctr[1]); - i8042_initial_ctr = i8042_ctr; + i8042_initial_ctr = i8042_ctr = ctr[0]; /* * Disable the keyboard interface and interrupt. @@ -866,6 +910,12 @@ static int i8042_controller_init(void) return -EIO; } +/* + * Flush whatever accumulated while we were disabling keyboard port. + */ + + i8042_flush(); + return 0; } @@ -885,7 +935,7 @@ static void i8042_controller_reset(void) i8042_ctr |= I8042_CTR_KBDDIS | I8042_CTR_AUXDIS; i8042_ctr &= ~(I8042_CTR_KBDINT | I8042_CTR_AUXINT); - if (i8042_command(&i8042_initial_ctr, I8042_CMD_CTL_WCTR)) + if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR)) printk(KERN_WARNING "i8042.c: Can't write CTR while resetting.\n"); /* @@ -1161,6 +1211,21 @@ static void __devexit i8042_unregister_ports(void) } } +/* + * Checks whether port belongs to i8042 controller. + */ +bool i8042_check_port_owner(const struct serio *port) +{ + int i; + + for (i = 0; i < I8042_NUM_PORTS; i++) + if (i8042_ports[i].serio == port) + return true; + + return false; +} +EXPORT_SYMBOL(i8042_check_port_owner); + static void i8042_free_irqs(void) { if (i8042_aux_irq_registered) diff --git a/drivers/input/serio/libps2.c b/drivers/input/serio/libps2.c index 3a95b508bf27..f3876acc3e83 100644 --- a/drivers/input/serio/libps2.c +++ b/drivers/input/serio/libps2.c @@ -13,10 +13,12 @@ #include <linux/delay.h> #include <linux/module.h> +#include <linux/sched.h> #include <linux/slab.h> #include <linux/interrupt.h> #include <linux/input.h> #include <linux/serio.h> +#include <linux/i8042.h> #include <linux/init.h> #include <linux/libps2.h> @@ -54,6 +56,24 @@ int ps2_sendbyte(struct ps2dev *ps2dev, unsigned char byte, int timeout) } EXPORT_SYMBOL(ps2_sendbyte); +void ps2_begin_command(struct ps2dev *ps2dev) +{ + mutex_lock(&ps2dev->cmd_mutex); + + if (i8042_check_port_owner(ps2dev->serio)) + i8042_lock_chip(); +} +EXPORT_SYMBOL(ps2_begin_command); + +void ps2_end_command(struct ps2dev *ps2dev) +{ + if (i8042_check_port_owner(ps2dev->serio)) + i8042_unlock_chip(); + + mutex_unlock(&ps2dev->cmd_mutex); +} +EXPORT_SYMBOL(ps2_end_command); + /* * ps2_drain() waits for device to transmit requested number of bytes * and discards them. @@ -66,7 +86,7 @@ void ps2_drain(struct ps2dev *ps2dev, int maxbytes, int timeout) maxbytes = sizeof(ps2dev->cmdbuf); } - mutex_lock(&ps2dev->cmd_mutex); + ps2_begin_command(ps2dev); serio_pause_rx(ps2dev->serio); ps2dev->flags = PS2_FLAG_CMD; @@ -76,7 +96,8 @@ void ps2_drain(struct ps2dev *ps2dev, int maxbytes, int timeout) wait_event_timeout(ps2dev->wait, !(ps2dev->flags & PS2_FLAG_CMD), msecs_to_jiffies(timeout)); - mutex_unlock(&ps2dev->cmd_mutex); + + ps2_end_command(ps2dev); } EXPORT_SYMBOL(ps2_drain); @@ -237,9 +258,9 @@ int ps2_command(struct ps2dev *ps2dev, unsigned char *param, int command) { int rc; - mutex_lock(&ps2dev->cmd_mutex); + ps2_begin_command(ps2dev); rc = __ps2_command(ps2dev, param, command); - mutex_unlock(&ps2dev->cmd_mutex); + ps2_end_command(ps2dev); return rc; } diff --git a/drivers/input/serio/serio_raw.c b/drivers/input/serio/serio_raw.c index b03009bb7468..27fdaaffbb40 100644 --- a/drivers/input/serio/serio_raw.c +++ b/drivers/input/serio/serio_raw.c @@ -9,6 +9,7 @@ * the Free Software Foundation. */ +#include <linux/sched.h> #include <linux/slab.h> #include <linux/smp_lock.h> #include <linux/poll.h> diff --git a/drivers/input/serio/serport.c b/drivers/input/serio/serport.c index b9694b6445d0..6d345112bcb7 100644 --- a/drivers/input/serio/serport.c +++ b/drivers/input/serio/serport.c @@ -15,6 +15,7 @@ #include <asm/uaccess.h> #include <linux/kernel.h> +#include <linux/sched.h> #include <linux/slab.h> #include <linux/module.h> #include <linux/init.h> diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index ab02d72afbf3..8cc453c85ea7 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -48,8 +48,8 @@ config TOUCHSCREEN_AD7879_I2C select TOUCHSCREEN_AD7879 help Say Y here if you have a touchscreen interface using the - AD7879-1 controller, and your board-specific initialization - code includes that in its table of I2C devices. + AD7879-1/AD7889-1 controller, and your board-specific + initialization code includes that in its table of I2C devices. If unsure, say N (but it's safe to say "Y"). @@ -62,7 +62,7 @@ config TOUCHSCREEN_AD7879_SPI select TOUCHSCREEN_AD7879 help Say Y here if you have a touchscreen interface using the - AD7879 controller, and your board-specific initialization + AD7879/AD7889 controller, and your board-specific initialization code includes that in its table of SPI devices. If unsure, say N (but it's safe to say "Y"). @@ -169,6 +169,17 @@ config TOUCHSCREEN_WACOM_W8001 To compile this driver as a module, choose M here: the module will be called wacom_w8001. +config TOUCHSCREEN_MCS5000 + tristate "MELFAS MCS-5000 touchscreen" + depends on I2C + help + Say Y here if you have the MELFAS MCS-5000 touchscreen controller + chip in your system. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called mcs5000_ts. config TOUCHSCREEN_MTOUCH tristate "MicroTouch serial touchscreens" diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile index 4599bf7ad819..15fa62cffc77 100644 --- a/drivers/input/touchscreen/Makefile +++ b/drivers/input/touchscreen/Makefile @@ -17,6 +17,7 @@ obj-$(CONFIG_TOUCHSCREEN_EETI) += eeti_ts.o obj-$(CONFIG_TOUCHSCREEN_ELO) += elo.o obj-$(CONFIG_TOUCHSCREEN_FUJITSU) += fujitsu_ts.o obj-$(CONFIG_TOUCHSCREEN_INEXIO) += inexio.o +obj-$(CONFIG_TOUCHSCREEN_MCS5000) += mcs5000_ts.o obj-$(CONFIG_TOUCHSCREEN_MIGOR) += migor_ts.o obj-$(CONFIG_TOUCHSCREEN_MTOUCH) += mtouch.o obj-$(CONFIG_TOUCHSCREEN_MK712) += mk712.o diff --git a/drivers/input/touchscreen/ad7877.c b/drivers/input/touchscreen/ad7877.c index ecaeb7e8e75e..eb83939c705e 100644 --- a/drivers/input/touchscreen/ad7877.c +++ b/drivers/input/touchscreen/ad7877.c @@ -842,3 +842,4 @@ module_exit(ad7877_exit); MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>"); MODULE_DESCRIPTION("AD7877 touchscreen Driver"); MODULE_LICENSE("GPL"); +MODULE_ALIAS("spi:ad7877"); diff --git a/drivers/input/touchscreen/ad7879.c b/drivers/input/touchscreen/ad7879.c index 5d8a70398807..c21e6d3a8844 100644 --- a/drivers/input/touchscreen/ad7879.c +++ b/drivers/input/touchscreen/ad7879.c @@ -1,7 +1,8 @@ /* - * Copyright (C) 2008 Michael Hennerich, Analog Devices Inc. + * Copyright (C) 2008-2009 Michael Hennerich, Analog Devices Inc. * - * Description: AD7879 based touchscreen, and GPIO driver (I2C/SPI Interface) + * Description: AD7879/AD7889 based touchscreen, and GPIO driver + * (I2C/SPI Interface) * * Bugs: Enter bugs at http://blackfin.uclinux.org/ * @@ -644,7 +645,7 @@ static int __devinit ad7879_probe(struct spi_device *spi) kfree(ts); } - return 0; + return error; } static int __devexit ad7879_remove(struct spi_device *spi) @@ -731,7 +732,7 @@ static int __devinit ad7879_probe(struct i2c_client *client, kfree(ts); } - return 0; + return error; } static int __devexit ad7879_remove(struct i2c_client *client) @@ -747,6 +748,7 @@ static int __devexit ad7879_remove(struct i2c_client *client) static const struct i2c_device_id ad7879_id[] = { { "ad7879", 0 }, + { "ad7889", 0 }, { } }; MODULE_DEVICE_TABLE(i2c, ad7879_id); @@ -779,3 +781,4 @@ module_exit(ad7879_exit); MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>"); MODULE_DESCRIPTION("AD7879(-1) touchscreen Driver"); MODULE_LICENSE("GPL"); +MODULE_ALIAS("spi:ad7879"); diff --git a/drivers/input/touchscreen/ads7846.c b/drivers/input/touchscreen/ads7846.c index ba9d38c3f412..09c810999b92 100644 --- a/drivers/input/touchscreen/ads7846.c +++ b/drivers/input/touchscreen/ads7846.c @@ -1256,3 +1256,4 @@ module_exit(ads7846_exit); MODULE_DESCRIPTION("ADS7846 TouchScreen Driver"); MODULE_LICENSE("GPL"); +MODULE_ALIAS("spi:ads7846"); diff --git a/drivers/input/touchscreen/mcs5000_ts.c b/drivers/input/touchscreen/mcs5000_ts.c new file mode 100644 index 000000000000..4c28b89757f9 --- /dev/null +++ b/drivers/input/touchscreen/mcs5000_ts.c @@ -0,0 +1,318 @@ +/* + * mcs5000_ts.c - Touchscreen driver for MELFAS MCS-5000 controller + * + * Copyright (C) 2009 Samsung Electronics Co.Ltd + * Author: Joonyoung Shim <jy0922.shim@samsung.com> + * + * Based on wm97xx-core.c + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/i2c.h> +#include <linux/i2c/mcs5000_ts.h> +#include <linux/interrupt.h> +#include <linux/input.h> +#include <linux/irq.h> + +/* Registers */ +#define MCS5000_TS_STATUS 0x00 +#define STATUS_OFFSET 0 +#define STATUS_NO (0 << STATUS_OFFSET) +#define STATUS_INIT (1 << STATUS_OFFSET) +#define STATUS_SENSING (2 << STATUS_OFFSET) +#define STATUS_COORD (3 << STATUS_OFFSET) +#define STATUS_GESTURE (4 << STATUS_OFFSET) +#define ERROR_OFFSET 4 +#define ERROR_NO (0 << ERROR_OFFSET) +#define ERROR_POWER_ON_RESET (1 << ERROR_OFFSET) +#define ERROR_INT_RESET (2 << ERROR_OFFSET) +#define ERROR_EXT_RESET (3 << ERROR_OFFSET) +#define ERROR_INVALID_REG_ADDRESS (8 << ERROR_OFFSET) +#define ERROR_INVALID_REG_VALUE (9 << ERROR_OFFSET) + +#define MCS5000_TS_OP_MODE 0x01 +#define RESET_OFFSET 0 +#define RESET_NO (0 << RESET_OFFSET) +#define RESET_EXT_SOFT (1 << RESET_OFFSET) +#define OP_MODE_OFFSET 1 +#define OP_MODE_SLEEP (0 << OP_MODE_OFFSET) +#define OP_MODE_ACTIVE (1 << OP_MODE_OFFSET) +#define GESTURE_OFFSET 4 +#define GESTURE_DISABLE (0 << GESTURE_OFFSET) +#define GESTURE_ENABLE (1 << GESTURE_OFFSET) +#define PROXIMITY_OFFSET 5 +#define PROXIMITY_DISABLE (0 << PROXIMITY_OFFSET) +#define PROXIMITY_ENABLE (1 << PROXIMITY_OFFSET) +#define SCAN_MODE_OFFSET 6 +#define SCAN_MODE_INTERRUPT (0 << SCAN_MODE_OFFSET) +#define SCAN_MODE_POLLING (1 << SCAN_MODE_OFFSET) +#define REPORT_RATE_OFFSET 7 +#define REPORT_RATE_40 (0 << REPORT_RATE_OFFSET) +#define REPORT_RATE_80 (1 << REPORT_RATE_OFFSET) + +#define MCS5000_TS_SENS_CTL 0x02 +#define MCS5000_TS_FILTER_CTL 0x03 +#define PRI_FILTER_OFFSET 0 +#define SEC_FILTER_OFFSET 4 + +#define MCS5000_TS_X_SIZE_UPPER 0x08 +#define MCS5000_TS_X_SIZE_LOWER 0x09 +#define MCS5000_TS_Y_SIZE_UPPER 0x0A +#define MCS5000_TS_Y_SIZE_LOWER 0x0B + +#define MCS5000_TS_INPUT_INFO 0x10 +#define INPUT_TYPE_OFFSET 0 +#define INPUT_TYPE_NONTOUCH (0 << INPUT_TYPE_OFFSET) +#define INPUT_TYPE_SINGLE (1 << INPUT_TYPE_OFFSET) +#define INPUT_TYPE_DUAL (2 << INPUT_TYPE_OFFSET) +#define INPUT_TYPE_PALM (3 << INPUT_TYPE_OFFSET) +#define INPUT_TYPE_PROXIMITY (7 << INPUT_TYPE_OFFSET) +#define GESTURE_CODE_OFFSET 3 +#define GESTURE_CODE_NO (0 << GESTURE_CODE_OFFSET) + +#define MCS5000_TS_X_POS_UPPER 0x11 +#define MCS5000_TS_X_POS_LOWER 0x12 +#define MCS5000_TS_Y_POS_UPPER 0x13 +#define MCS5000_TS_Y_POS_LOWER 0x14 +#define MCS5000_TS_Z_POS 0x15 +#define MCS5000_TS_WIDTH 0x16 +#define MCS5000_TS_GESTURE_VAL 0x17 +#define MCS5000_TS_MODULE_REV 0x20 +#define MCS5000_TS_FIRMWARE_VER 0x21 + +/* Touchscreen absolute values */ +#define MCS5000_MAX_XC 0x3ff +#define MCS5000_MAX_YC 0x3ff + +enum mcs5000_ts_read_offset { + READ_INPUT_INFO, + READ_X_POS_UPPER, + READ_X_POS_LOWER, + READ_Y_POS_UPPER, + READ_Y_POS_LOWER, + READ_BLOCK_SIZE, +}; + +/* Each client has this additional data */ +struct mcs5000_ts_data { + struct i2c_client *client; + struct input_dev *input_dev; + const struct mcs5000_ts_platform_data *platform_data; +}; + +static irqreturn_t mcs5000_ts_interrupt(int irq, void *dev_id) +{ + struct mcs5000_ts_data *data = dev_id; + struct i2c_client *client = data->client; + u8 buffer[READ_BLOCK_SIZE]; + int err; + int x; + int y; + + err = i2c_smbus_read_i2c_block_data(client, MCS5000_TS_INPUT_INFO, + READ_BLOCK_SIZE, buffer); + if (err < 0) { + dev_err(&client->dev, "%s, err[%d]\n", __func__, err); + goto out; + } + + switch (buffer[READ_INPUT_INFO]) { + case INPUT_TYPE_NONTOUCH: + input_report_key(data->input_dev, BTN_TOUCH, 0); + input_sync(data->input_dev); + break; + + case INPUT_TYPE_SINGLE: + x = (buffer[READ_X_POS_UPPER] << 8) | buffer[READ_X_POS_LOWER]; + y = (buffer[READ_Y_POS_UPPER] << 8) | buffer[READ_Y_POS_LOWER]; + + input_report_key(data->input_dev, BTN_TOUCH, 1); + input_report_abs(data->input_dev, ABS_X, x); + input_report_abs(data->input_dev, ABS_Y, y); + input_sync(data->input_dev); + break; + + case INPUT_TYPE_DUAL: + /* TODO */ + break; + + case INPUT_TYPE_PALM: + /* TODO */ + break; + + case INPUT_TYPE_PROXIMITY: + /* TODO */ + break; + + default: + dev_err(&client->dev, "Unknown ts input type %d\n", + buffer[READ_INPUT_INFO]); + break; + } + + out: + return IRQ_HANDLED; +} + +static void mcs5000_ts_phys_init(struct mcs5000_ts_data *data) +{ + const struct mcs5000_ts_platform_data *platform_data = + data->platform_data; + struct i2c_client *client = data->client; + + /* Touch reset & sleep mode */ + i2c_smbus_write_byte_data(client, MCS5000_TS_OP_MODE, + RESET_EXT_SOFT | OP_MODE_SLEEP); + + /* Touch size */ + i2c_smbus_write_byte_data(client, MCS5000_TS_X_SIZE_UPPER, + platform_data->x_size >> 8); + i2c_smbus_write_byte_data(client, MCS5000_TS_X_SIZE_LOWER, + platform_data->x_size & 0xff); + i2c_smbus_write_byte_data(client, MCS5000_TS_Y_SIZE_UPPER, + platform_data->y_size >> 8); + i2c_smbus_write_byte_data(client, MCS5000_TS_Y_SIZE_LOWER, + platform_data->y_size & 0xff); + + /* Touch active mode & 80 report rate */ + i2c_smbus_write_byte_data(data->client, MCS5000_TS_OP_MODE, + OP_MODE_ACTIVE | REPORT_RATE_80); +} + +static int __devinit mcs5000_ts_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct mcs5000_ts_data *data; + struct input_dev *input_dev; + int ret; + + if (!client->dev.platform_data) + return -EINVAL; + + data = kzalloc(sizeof(struct mcs5000_ts_data), GFP_KERNEL); + input_dev = input_allocate_device(); + if (!data || !input_dev) { + dev_err(&client->dev, "Failed to allocate memory\n"); + ret = -ENOMEM; + goto err_free_mem; + } + + data->client = client; + data->input_dev = input_dev; + data->platform_data = client->dev.platform_data; + + input_dev->name = "MELPAS MCS-5000 Touchscreen"; + input_dev->id.bustype = BUS_I2C; + input_dev->dev.parent = &client->dev; + + __set_bit(EV_ABS, input_dev->evbit); + __set_bit(EV_KEY, input_dev->evbit); + __set_bit(BTN_TOUCH, input_dev->keybit); + input_set_abs_params(input_dev, ABS_X, 0, MCS5000_MAX_XC, 0, 0); + input_set_abs_params(input_dev, ABS_Y, 0, MCS5000_MAX_YC, 0, 0); + + input_set_drvdata(input_dev, data); + + if (data->platform_data->cfg_pin) + data->platform_data->cfg_pin(); + + ret = request_threaded_irq(client->irq, NULL, mcs5000_ts_interrupt, + IRQF_TRIGGER_LOW | IRQF_ONESHOT, "mcs5000_ts", data); + + if (ret < 0) { + dev_err(&client->dev, "Failed to register interrupt\n"); + goto err_free_mem; + } + + ret = input_register_device(data->input_dev); + if (ret < 0) + goto err_free_irq; + + mcs5000_ts_phys_init(data); + i2c_set_clientdata(client, data); + + return 0; + +err_free_irq: + free_irq(client->irq, data); +err_free_mem: + input_free_device(input_dev); + kfree(data); + return ret; +} + +static int __devexit mcs5000_ts_remove(struct i2c_client *client) +{ + struct mcs5000_ts_data *data = i2c_get_clientdata(client); + + free_irq(client->irq, data); + input_unregister_device(data->input_dev); + kfree(data); + i2c_set_clientdata(client, NULL); + + return 0; +} + +#ifdef CONFIG_PM +static int mcs5000_ts_suspend(struct i2c_client *client, pm_message_t mesg) +{ + /* Touch sleep mode */ + i2c_smbus_write_byte_data(client, MCS5000_TS_OP_MODE, OP_MODE_SLEEP); + + return 0; +} + +static int mcs5000_ts_resume(struct i2c_client *client) +{ + struct mcs5000_ts_data *data = i2c_get_clientdata(client); + + mcs5000_ts_phys_init(data); + + return 0; +} +#else +#define mcs5000_ts_suspend NULL +#define mcs5000_ts_resume NULL +#endif + +static const struct i2c_device_id mcs5000_ts_id[] = { + { "mcs5000_ts", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, mcs5000_ts_id); + +static struct i2c_driver mcs5000_ts_driver = { + .probe = mcs5000_ts_probe, + .remove = __devexit_p(mcs5000_ts_remove), + .suspend = mcs5000_ts_suspend, + .resume = mcs5000_ts_resume, + .driver = { + .name = "mcs5000_ts", + }, + .id_table = mcs5000_ts_id, +}; + +static int __init mcs5000_ts_init(void) +{ + return i2c_add_driver(&mcs5000_ts_driver); +} + +static void __exit mcs5000_ts_exit(void) +{ + i2c_del_driver(&mcs5000_ts_driver); +} + +module_init(mcs5000_ts_init); +module_exit(mcs5000_ts_exit); + +/* Module information */ +MODULE_AUTHOR("Joonyoung Shim <jy0922.shim@samsung.com>"); +MODULE_DESCRIPTION("Touchscreen driver for MELFAS MCS-5000 controller"); +MODULE_LICENSE("GPL"); diff --git a/drivers/input/touchscreen/wm97xx-core.c b/drivers/input/touchscreen/wm97xx-core.c index 252eb11fe9db..f944918466e5 100644 --- a/drivers/input/touchscreen/wm97xx-core.c +++ b/drivers/input/touchscreen/wm97xx-core.c @@ -561,6 +561,7 @@ static void wm97xx_ts_input_close(struct input_dev *idev) static int wm97xx_probe(struct device *dev) { struct wm97xx *wm; + struct wm97xx_pdata *pdata = dev->platform_data; int ret = 0, id = 0; wm = kzalloc(sizeof(struct wm97xx), GFP_KERNEL); @@ -658,6 +659,7 @@ static int wm97xx_probe(struct device *dev) } platform_set_drvdata(wm->battery_dev, wm); wm->battery_dev->dev.parent = dev; + wm->battery_dev->dev.platform_data = pdata; ret = platform_device_add(wm->battery_dev); if (ret < 0) goto batt_reg_err; @@ -671,6 +673,7 @@ static int wm97xx_probe(struct device *dev) } platform_set_drvdata(wm->touch_dev, wm); wm->touch_dev->dev.parent = dev; + wm->touch_dev->dev.platform_data = pdata; ret = platform_device_add(wm->touch_dev); if (ret < 0) goto touch_reg_err; diff --git a/drivers/isdn/capi/capi.c b/drivers/isdn/capi/capi.c index 2d8352419c0d..65bf91e16a42 100644 --- a/drivers/isdn/capi/capi.c +++ b/drivers/isdn/capi/capi.c @@ -603,7 +603,7 @@ static void capi_recv_message(struct capi20_appl *ap, struct sk_buff *skb) if (CAPIMSG_CMD(skb->data) == CAPI_CONNECT_B3_CONF) { u16 info = CAPIMSG_U16(skb->data, 12); // Info field - if (info == 0) { + if ((info & 0xff00) == 0) { mutex_lock(&cdev->ncci_list_mtx); capincci_alloc(cdev, CAPIMSG_NCCI(skb->data)); mutex_unlock(&cdev->ncci_list_mtx); diff --git a/drivers/isdn/capi/capidrv.c b/drivers/isdn/capi/capidrv.c index 650120261abf..3e6d17f42a98 100644 --- a/drivers/isdn/capi/capidrv.c +++ b/drivers/isdn/capi/capidrv.c @@ -40,7 +40,7 @@ static int debugmode = 0; MODULE_DESCRIPTION("CAPI4Linux: Interface to ISDN4Linux"); MODULE_AUTHOR("Carsten Paeth"); MODULE_LICENSE("GPL"); -module_param(debugmode, uint, 0); +module_param(debugmode, uint, S_IRUGO|S_IWUSR); /* -------- type definitions ----------------------------------------- */ @@ -671,8 +671,8 @@ static void n0(capidrv_contr * card, capidrv_ncci * ncci) NULL, /* Useruserdata */ /* $$$$ */ NULL /* Facilitydataarray */ ); - send_message(card, &cmsg); plci_change_state(card, ncci->plcip, EV_PLCI_DISCONNECT_REQ); + send_message(card, &cmsg); cmd.command = ISDN_STAT_BHUP; cmd.driver = card->myid; @@ -924,8 +924,8 @@ static void handle_incoming_call(capidrv_contr * card, _cmsg * cmsg) */ capi_cmsg_answer(cmsg); cmsg->Reject = 1; /* ignore */ - send_message(card, cmsg); plci_change_state(card, plcip, EV_PLCI_CONNECT_REJECT); + send_message(card, cmsg); printk(KERN_INFO "capidrv-%d: incoming call %s,%d,%d,%s ignored\n", card->contrnr, cmd.parm.setup.phone, @@ -974,8 +974,8 @@ static void handle_incoming_call(capidrv_contr * card, _cmsg * cmsg) case 2: /* Call will be rejected. */ capi_cmsg_answer(cmsg); cmsg->Reject = 2; /* reject call, normal call clearing */ - send_message(card, cmsg); plci_change_state(card, plcip, EV_PLCI_CONNECT_REJECT); + send_message(card, cmsg); break; default: @@ -983,8 +983,8 @@ static void handle_incoming_call(capidrv_contr * card, _cmsg * cmsg) capi_cmsg_answer(cmsg); cmsg->Reject = 8; /* reject call, destination out of order */ - send_message(card, cmsg); plci_change_state(card, plcip, EV_PLCI_CONNECT_REJECT); + send_message(card, cmsg); break; } return; @@ -1020,8 +1020,8 @@ static void handle_plci(_cmsg * cmsg) card->bchans[plcip->chan].disconnecting = 1; plci_change_state(card, plcip, EV_PLCI_DISCONNECT_IND); capi_cmsg_answer(cmsg); - send_message(card, cmsg); plci_change_state(card, plcip, EV_PLCI_DISCONNECT_RESP); + send_message(card, cmsg); break; case CAPI_DISCONNECT_CONF: /* plci */ @@ -1078,8 +1078,8 @@ static void handle_plci(_cmsg * cmsg) if (card->bchans[plcip->chan].incoming) { capi_cmsg_answer(cmsg); - send_message(card, cmsg); plci_change_state(card, plcip, EV_PLCI_CONNECT_ACTIVE_IND); + send_message(card, cmsg); } else { capidrv_ncci *nccip; capi_cmsg_answer(cmsg); @@ -1098,13 +1098,14 @@ static void handle_plci(_cmsg * cmsg) NULL /* NCPI */ ); nccip->msgid = cmsg->Messagenumber; + plci_change_state(card, plcip, + EV_PLCI_CONNECT_ACTIVE_IND); + ncci_change_state(card, nccip, EV_NCCI_CONNECT_B3_REQ); send_message(card, cmsg); cmd.command = ISDN_STAT_DCONN; cmd.driver = card->myid; cmd.arg = plcip->chan; card->interface.statcallb(&cmd); - plci_change_state(card, plcip, EV_PLCI_CONNECT_ACTIVE_IND); - ncci_change_state(card, nccip, EV_NCCI_CONNECT_B3_REQ); } break; @@ -1193,8 +1194,8 @@ static void handle_ncci(_cmsg * cmsg) goto notfound; capi_cmsg_answer(cmsg); - send_message(card, cmsg); ncci_change_state(card, nccip, EV_NCCI_CONNECT_B3_ACTIVE_IND); + send_message(card, cmsg); cmd.command = ISDN_STAT_BCONN; cmd.driver = card->myid; @@ -1222,8 +1223,8 @@ static void handle_ncci(_cmsg * cmsg) 0, /* Reject */ NULL /* NCPI */ ); - send_message(card, cmsg); ncci_change_state(card, nccip, EV_NCCI_CONNECT_B3_RESP); + send_message(card, cmsg); break; } printk(KERN_ERR "capidrv-%d: no mem for ncci, sorry\n", card->contrnr); @@ -1299,8 +1300,8 @@ static void handle_ncci(_cmsg * cmsg) card->bchans[nccip->chan].disconnecting = 1; ncci_change_state(card, nccip, EV_NCCI_DISCONNECT_B3_IND); capi_cmsg_answer(cmsg); - send_message(card, cmsg); ncci_change_state(card, nccip, EV_NCCI_DISCONNECT_B3_RESP); + send_message(card, cmsg); break; case CAPI_DISCONNECT_B3_CONF: /* ncci */ @@ -2014,8 +2015,8 @@ static void send_listen(capidrv_contr *card) card->cipmask, card->cipmask2, NULL, NULL); - send_message(card, &cmdcmsg); listen_change_state(card, EV_LISTEN_REQ); + send_message(card, &cmdcmsg); } static void listentimerfunc(unsigned long x) diff --git a/drivers/isdn/capi/kcapi.c b/drivers/isdn/capi/kcapi.c index 57d26360f64e..dc506ab99cac 100644 --- a/drivers/isdn/capi/kcapi.c +++ b/drivers/isdn/capi/kcapi.c @@ -18,6 +18,7 @@ #include <linux/interrupt.h> #include <linux/ioport.h> #include <linux/proc_fs.h> +#include <linux/sched.h> #include <linux/seq_file.h> #include <linux/skbuff.h> #include <linux/workqueue.h> diff --git a/drivers/isdn/capi/kcapi_proc.c b/drivers/isdn/capi/kcapi_proc.c index 50ed778f63fc..09d4db764d22 100644 --- a/drivers/isdn/capi/kcapi_proc.c +++ b/drivers/isdn/capi/kcapi_proc.c @@ -89,14 +89,14 @@ static int contrstats_show(struct seq_file *seq, void *v) return 0; } -static struct seq_operations seq_controller_ops = { +static const struct seq_operations seq_controller_ops = { .start = controller_start, .next = controller_next, .stop = controller_stop, .show = controller_show, }; -static struct seq_operations seq_contrstats_ops = { +static const struct seq_operations seq_contrstats_ops = { .start = controller_start, .next = controller_next, .stop = controller_stop, @@ -194,14 +194,14 @@ applstats_show(struct seq_file *seq, void *v) return 0; } -static struct seq_operations seq_applications_ops = { +static const struct seq_operations seq_applications_ops = { .start = applications_start, .next = applications_next, .stop = applications_stop, .show = applications_show, }; -static struct seq_operations seq_applstats_ops = { +static const struct seq_operations seq_applstats_ops = { .start = applications_start, .next = applications_next, .stop = applications_stop, @@ -264,7 +264,7 @@ static int capi_driver_show(struct seq_file *seq, void *v) return 0; } -static struct seq_operations seq_capi_driver_ops = { +static const struct seq_operations seq_capi_driver_ops = { .start = capi_driver_start, .next = capi_driver_next, .stop = capi_driver_stop, diff --git a/drivers/isdn/divert/divert_procfs.c b/drivers/isdn/divert/divert_procfs.c index 8b256a617c8a..3697c409bec6 100644 --- a/drivers/isdn/divert/divert_procfs.c +++ b/drivers/isdn/divert/divert_procfs.c @@ -16,6 +16,7 @@ #else #include <linux/fs.h> #endif +#include <linux/sched.h> #include <linux/isdnif.h> #include <net/net_namespace.h> #include "isdn_divert.h" diff --git a/drivers/isdn/gigaset/asyncdata.c b/drivers/isdn/gigaset/asyncdata.c index 234cc5d53312..44a58e6f8f65 100644 --- a/drivers/isdn/gigaset/asyncdata.c +++ b/drivers/isdn/gigaset/asyncdata.c @@ -334,7 +334,14 @@ static inline int iraw_loop(unsigned char c, unsigned char *src, int numbytes, return startbytes - numbytes; } -/* process a block of data received from the device +/** + * gigaset_m10x_input() - process a block of data received from the device + * @inbuf: received data and device descriptor structure. + * + * Called by hardware module {ser,usb}_gigaset with a block of received + * bytes. Separates the bytes received over the serial data channel into + * user data and command replies (locked/unlocked) according to the + * current state of the interface. */ void gigaset_m10x_input(struct inbuf_t *inbuf) { @@ -543,16 +550,17 @@ static struct sk_buff *iraw_encode(struct sk_buff *skb, int head, int tail) return iraw_skb; } -/* gigaset_send_skb - * called by common.c to queue an skb for sending - * and start transmission if necessary - * parameters: - * B Channel control structure - * skb +/** + * gigaset_m10x_send_skb() - queue an skb for sending + * @bcs: B channel descriptor structure. + * @skb: data to send. + * + * Called by i4l.c to encode and queue an skb for sending, and start + * transmission if necessary. + * * Return value: - * number of bytes accepted for sending - * (skb->len if ok, 0 if out of buffer space) - * or error code (< 0, eg. -EINVAL) + * number of bytes accepted for sending (skb->len) if ok, + * error code < 0 (eg. -ENOMEM) on error */ int gigaset_m10x_send_skb(struct bc_state *bcs, struct sk_buff *skb) { diff --git a/drivers/isdn/gigaset/bas-gigaset.c b/drivers/isdn/gigaset/bas-gigaset.c index 781c4041f7b0..5ed1d99eb9f3 100644 --- a/drivers/isdn/gigaset/bas-gigaset.c +++ b/drivers/isdn/gigaset/bas-gigaset.c @@ -134,6 +134,7 @@ struct bas_cardstate { #define BS_ATRDPEND 0x040 /* urb_cmd_in in use */ #define BS_ATWRPEND 0x080 /* urb_cmd_out in use */ #define BS_SUSPEND 0x100 /* USB port suspended */ +#define BS_RESETTING 0x200 /* waiting for HD_RESET_INTERRUPT_PIPE_ACK */ static struct gigaset_driver *driver = NULL; @@ -319,6 +320,21 @@ static int gigaset_set_line_ctrl(struct cardstate *cs, unsigned cflag) return -EINVAL; } +/* set/clear bits in base connection state, return previous state + */ +static inline int update_basstate(struct bas_cardstate *ucs, + int set, int clear) +{ + unsigned long flags; + int state; + + spin_lock_irqsave(&ucs->lock, flags); + state = ucs->basstate; + ucs->basstate = (state & ~clear) | set; + spin_unlock_irqrestore(&ucs->lock, flags); + return state; +} + /* error_hangup * hang up any existing connection because of an unrecoverable error * This function may be called from any context and takes care of scheduling @@ -350,12 +366,9 @@ static inline void error_hangup(struct bc_state *bcs) */ static inline void error_reset(struct cardstate *cs) { - /* close AT command channel to recover (ignore errors) */ - req_submit(cs->bcs, HD_CLOSE_ATCHANNEL, 0, BAS_TIMEOUT); - - //FIXME try to recover without bothering the user - dev_err(cs->dev, - "unrecoverable error - please disconnect Gigaset base to reset\n"); + /* reset interrupt pipe to recover (ignore errors) */ + update_basstate(cs->hw.bas, BS_RESETTING, 0); + req_submit(cs->bcs, HD_RESET_INTERRUPT_PIPE, 0, BAS_TIMEOUT); } /* check_pending @@ -398,8 +411,13 @@ static void check_pending(struct bas_cardstate *ucs) case HD_DEVICE_INIT_ACK: /* no reply expected */ ucs->pending = 0; break; - /* HD_READ_ATMESSAGE, HD_WRITE_ATMESSAGE, HD_RESET_INTERRUPTPIPE - * are handled separately and should never end up here + case HD_RESET_INTERRUPT_PIPE: + if (!(ucs->basstate & BS_RESETTING)) + ucs->pending = 0; + break; + /* + * HD_READ_ATMESSAGE and HD_WRITE_ATMESSAGE are handled separately + * and should never end up here */ default: dev_warn(&ucs->interface->dev, @@ -449,21 +467,6 @@ static void cmd_in_timeout(unsigned long data) error_reset(cs); } -/* set/clear bits in base connection state, return previous state - */ -inline static int update_basstate(struct bas_cardstate *ucs, - int set, int clear) -{ - unsigned long flags; - int state; - - spin_lock_irqsave(&ucs->lock, flags); - state = ucs->basstate; - ucs->basstate = (state & ~clear) | set; - spin_unlock_irqrestore(&ucs->lock, flags); - return state; -} - /* read_ctrl_callback * USB completion handler for control pipe input * called by the USB subsystem in interrupt context @@ -762,7 +765,8 @@ static void read_int_callback(struct urb *urb) break; case HD_RESET_INTERRUPT_PIPE_ACK: - gig_dbg(DEBUG_USBREQ, "HD_RESET_INTERRUPT_PIPE_ACK"); + update_basstate(ucs, 0, BS_RESETTING); + dev_notice(cs->dev, "interrupt pipe reset\n"); break; case HD_SUSPEND_END: @@ -1331,28 +1335,24 @@ static void read_iso_tasklet(unsigned long data) rcvbuf = urb->transfer_buffer; totleft = urb->actual_length; for (frame = 0; totleft > 0 && frame < BAS_NUMFRAMES; frame++) { - if (unlikely(urb->iso_frame_desc[frame].status)) { + numbytes = urb->iso_frame_desc[frame].actual_length; + if (unlikely(urb->iso_frame_desc[frame].status)) dev_warn(cs->dev, - "isochronous read: frame %d: %s\n", - frame, + "isochronous read: frame %d[%d]: %s\n", + frame, numbytes, get_usb_statmsg( urb->iso_frame_desc[frame].status)); - break; - } - numbytes = urb->iso_frame_desc[frame].actual_length; - if (unlikely(numbytes > BAS_MAXFRAME)) { + if (unlikely(numbytes > BAS_MAXFRAME)) dev_warn(cs->dev, "isochronous read: frame %d: " "numbytes (%d) > BAS_MAXFRAME\n", frame, numbytes); - break; - } if (unlikely(numbytes > totleft)) { dev_warn(cs->dev, "isochronous read: frame %d: " "numbytes (%d) > totleft (%d)\n", frame, numbytes, totleft); - break; + numbytes = totleft; } offset = urb->iso_frame_desc[frame].offset; if (unlikely(offset + numbytes > BAS_INBUFSIZE)) { @@ -1361,7 +1361,7 @@ static void read_iso_tasklet(unsigned long data) "offset (%d) + numbytes (%d) " "> BAS_INBUFSIZE\n", frame, offset, numbytes); - break; + numbytes = BAS_INBUFSIZE - offset; } gigaset_isoc_receive(rcvbuf + offset, numbytes, bcs); totleft -= numbytes; @@ -1433,6 +1433,7 @@ static void req_timeout(unsigned long data) case HD_CLOSE_ATCHANNEL: dev_err(bcs->cs->dev, "timeout closing AT channel\n"); + error_reset(bcs->cs); break; case HD_CLOSE_B2CHANNEL: @@ -1442,6 +1443,13 @@ static void req_timeout(unsigned long data) error_reset(bcs->cs); break; + case HD_RESET_INTERRUPT_PIPE: + /* error recovery escalation */ + dev_err(bcs->cs->dev, + "reset interrupt pipe timeout, attempting USB reset\n"); + usb_queue_reset_device(bcs->cs->hw.bas->interface); + break; + default: dev_warn(bcs->cs->dev, "request 0x%02x timed out, clearing\n", pending); @@ -1934,6 +1942,15 @@ static int gigaset_write_cmd(struct cardstate *cs, goto notqueued; } + /* translate "+++" escape sequence sent as a single separate command + * into "close AT channel" command for error recovery + * The next command will reopen the AT channel automatically. + */ + if (len == 3 && !memcmp(buf, "+++", 3)) { + rc = req_submit(cs->bcs, HD_CLOSE_ATCHANNEL, 0, BAS_TIMEOUT); + goto notqueued; + } + if (len > IF_WRITEBUF) len = IF_WRITEBUF; if (!(cb = kmalloc(sizeof(struct cmdbuf_t) + len, GFP_ATOMIC))) { diff --git a/drivers/isdn/gigaset/common.c b/drivers/isdn/gigaset/common.c index e4141bf8b2f3..33dcd8d72b7c 100644 --- a/drivers/isdn/gigaset/common.c +++ b/drivers/isdn/gigaset/common.c @@ -22,6 +22,12 @@ #define DRIVER_AUTHOR "Hansjoerg Lipp <hjlipp@web.de>, Tilman Schmidt <tilman@imap.cc>, Stefan Eilers" #define DRIVER_DESC "Driver for Gigaset 307x" +#ifdef CONFIG_GIGASET_DEBUG +#define DRIVER_DESC_DEBUG " (debug build)" +#else +#define DRIVER_DESC_DEBUG "" +#endif + /* Module parameters */ int gigaset_debuglevel = DEBUG_DEFAULT; EXPORT_SYMBOL_GPL(gigaset_debuglevel); @@ -32,6 +38,17 @@ MODULE_PARM_DESC(debug, "debug level"); #define VALID_MINOR 0x01 #define VALID_ID 0x02 +/** + * gigaset_dbg_buffer() - dump data in ASCII and hex for debugging + * @level: debugging level. + * @msg: message prefix. + * @len: number of bytes to dump. + * @buf: data to dump. + * + * If the current debugging level includes one of the bits set in @level, + * @len bytes starting at @buf are logged to dmesg at KERN_DEBUG prio, + * prefixed by the text @msg. + */ void gigaset_dbg_buffer(enum debuglevel level, const unsigned char *msg, size_t len, const unsigned char *buf) { @@ -274,6 +291,20 @@ static void clear_events(struct cardstate *cs) spin_unlock_irqrestore(&cs->ev_lock, flags); } +/** + * gigaset_add_event() - add event to device event queue + * @cs: device descriptor structure. + * @at_state: connection state structure. + * @type: event type. + * @ptr: pointer parameter for event. + * @parameter: integer parameter for event. + * @arg: pointer parameter for event. + * + * Allocate an event queue entry from the device's event queue, and set it up + * with the parameters given. + * + * Return value: added event + */ struct event_t *gigaset_add_event(struct cardstate *cs, struct at_state_t *at_state, int type, void *ptr, int parameter, void *arg) @@ -398,6 +429,15 @@ static void make_invalid(struct cardstate *cs, unsigned mask) spin_unlock_irqrestore(&drv->lock, flags); } +/** + * gigaset_freecs() - free all associated ressources of a device + * @cs: device descriptor structure. + * + * Stops all tasklets and timers, unregisters the device from all + * subsystems it was registered to, deallocates the device structure + * @cs and all structures referenced from it. + * Operations on the device should be stopped before calling this. + */ void gigaset_freecs(struct cardstate *cs) { int i; @@ -506,7 +546,12 @@ static void gigaset_inbuf_init(struct inbuf_t *inbuf, struct bc_state *bcs, inbuf->inputstate = inputstate; } -/* append received bytes to inbuf */ +/** + * gigaset_fill_inbuf() - append received data to input buffer + * @inbuf: buffer structure. + * @src: received data. + * @numbytes: number of bytes received. + */ int gigaset_fill_inbuf(struct inbuf_t *inbuf, const unsigned char *src, unsigned numbytes) { @@ -606,20 +651,22 @@ static struct bc_state *gigaset_initbcs(struct bc_state *bcs, return NULL; } -/* gigaset_initcs +/** + * gigaset_initcs() - initialize device structure + * @drv: hardware driver the device belongs to + * @channels: number of B channels supported by device + * @onechannel: !=0 if B channel data and AT commands share one + * communication channel (M10x), + * ==0 if B channels have separate communication channels (base) + * @ignoreframes: number of frames to ignore after setting up B channel + * @cidmode: !=0: start in CallID mode + * @modulename: name of driver module for LL registration + * * Allocate and initialize cardstate structure for Gigaset driver * Calls hardware dependent gigaset_initcshw() function * Calls B channel initialization function gigaset_initbcs() for each B channel - * parameters: - * drv hardware driver the device belongs to - * channels number of B channels supported by device - * onechannel !=0: B channel data and AT commands share one - * communication channel - * ==0: B channels have separate communication channels - * ignoreframes number of frames to ignore after setting up B channel - * cidmode !=0: start in CallID mode - * modulename name of driver module (used for I4L registration) - * return value: + * + * Return value: * pointer to cardstate structure */ struct cardstate *gigaset_initcs(struct gigaset_driver *drv, int channels, @@ -837,6 +884,17 @@ static void cleanup_cs(struct cardstate *cs) } +/** + * gigaset_start() - start device operations + * @cs: device descriptor structure. + * + * Prepares the device for use by setting up communication parameters, + * scheduling an EV_START event to initiate device initialization, and + * waiting for completion of the initialization. + * + * Return value: + * 1 - success, 0 - error + */ int gigaset_start(struct cardstate *cs) { unsigned long flags; @@ -879,9 +937,15 @@ error: } EXPORT_SYMBOL_GPL(gigaset_start); -/* gigaset_shutdown - * check if a device is associated to the cardstate structure and stop it - * return value: 0 if ok, -1 if no device was associated +/** + * gigaset_shutdown() - shut down device operations + * @cs: device descriptor structure. + * + * Deactivates the device by scheduling an EV_SHUTDOWN event and + * waiting for completion of the shutdown. + * + * Return value: + * 0 - success, -1 - error (no device associated) */ int gigaset_shutdown(struct cardstate *cs) { @@ -912,6 +976,13 @@ exit: } EXPORT_SYMBOL_GPL(gigaset_shutdown); +/** + * gigaset_stop() - stop device operations + * @cs: device descriptor structure. + * + * Stops operations on the device by scheduling an EV_STOP event and + * waiting for completion of the shutdown. + */ void gigaset_stop(struct cardstate *cs) { mutex_lock(&cs->mutex); @@ -1020,6 +1091,14 @@ struct cardstate *gigaset_get_cs_by_tty(struct tty_struct *tty) return gigaset_get_cs_by_minor(tty->index + tty->driver->minor_start); } +/** + * gigaset_freedriver() - free all associated ressources of a driver + * @drv: driver descriptor structure. + * + * Unregisters the driver from the system and deallocates the driver + * structure @drv and all structures referenced from it. + * All devices should be shut down before calling this. + */ void gigaset_freedriver(struct gigaset_driver *drv) { unsigned long flags; @@ -1035,14 +1114,16 @@ void gigaset_freedriver(struct gigaset_driver *drv) } EXPORT_SYMBOL_GPL(gigaset_freedriver); -/* gigaset_initdriver +/** + * gigaset_initdriver() - initialize driver structure + * @minor: First minor number + * @minors: Number of minors this driver can handle + * @procname: Name of the driver + * @devname: Name of the device files (prefix without minor number) + * * Allocate and initialize gigaset_driver structure. Initialize interface. - * parameters: - * minor First minor number - * minors Number of minors this driver can handle - * procname Name of the driver - * devname Name of the device files (prefix without minor number) - * return value: + * + * Return value: * Pointer to the gigaset_driver structure on success, NULL on failure. */ struct gigaset_driver *gigaset_initdriver(unsigned minor, unsigned minors, @@ -1095,6 +1176,13 @@ error: } EXPORT_SYMBOL_GPL(gigaset_initdriver); +/** + * gigaset_blockdriver() - block driver + * @drv: driver descriptor structure. + * + * Prevents the driver from attaching new devices, in preparation for + * deregistration. + */ void gigaset_blockdriver(struct gigaset_driver *drv) { drv->blocked = 1; @@ -1110,7 +1198,7 @@ static int __init gigaset_init_module(void) if (gigaset_debuglevel == 1) gigaset_debuglevel = DEBUG_DEFAULT; - pr_info(DRIVER_DESC "\n"); + pr_info(DRIVER_DESC DRIVER_DESC_DEBUG "\n"); return 0; } diff --git a/drivers/isdn/gigaset/ev-layer.c b/drivers/isdn/gigaset/ev-layer.c index 2d91049571a4..cc768caa38f5 100644 --- a/drivers/isdn/gigaset/ev-layer.c +++ b/drivers/isdn/gigaset/ev-layer.c @@ -207,7 +207,6 @@ struct reply_t gigaset_tab_nocid[] = /* leave dle mode */ {RSP_INIT, 0, 0,SEQ_DLE0, 201, 5, {0}, "^SDLE=0\r"}, {RSP_OK, 201,201, -1, 202,-1}, - //{RSP_ZDLE, 202,202, 0, 202, 0, {ACT_ERROR}},//DELETE {RSP_ZDLE, 202,202, 0, 0, 0, {ACT_DLE0}}, {RSP_NODEV, 200,249, -1, 0, 0, {ACT_FAKEDLE0}}, {RSP_ERROR, 200,249, -1, 0, 0, {ACT_FAILDLE0}}, @@ -265,6 +264,7 @@ struct reply_t gigaset_tab_nocid[] = {EV_SHUTDOWN, -1, -1, -1, -1,-1, {ACT_SHUTDOWN}}, //FIXME /* misc. */ + {RSP_ERROR, -1, -1, -1, -1, -1, {ACT_ERROR} }, {RSP_EMPTY, -1, -1, -1, -1,-1, {ACT_DEBUG}}, //FIXME {RSP_ZCFGT, -1, -1, -1, -1,-1, {ACT_DEBUG}}, //FIXME {RSP_ZCFG, -1, -1, -1, -1,-1, {ACT_DEBUG}}, //FIXME @@ -328,10 +328,9 @@ struct reply_t gigaset_tab_cid[] = {RSP_INIT, -1, -1,SEQ_HUP, 401, 5, {0}, "+VLS=0\r"}, /* hang up */ //-1,-1? {RSP_OK, 401,401, -1, 402, 5}, {RSP_ZVLS, 402,402, 0, 403, 5}, - {RSP_ZSAU, 403,403,ZSAU_DISCONNECT_REQ, -1,-1, {ACT_DEBUG}}, /* if not remote hup */ - //{RSP_ZSAU, 403,403,ZSAU_NULL, 401, 0, {ACT_ERROR}}, //DELETE//FIXME -> DLE0 // should we do this _before_ hanging up for base driver? - {RSP_ZSAU, 403,403,ZSAU_NULL, 0, 0, {ACT_DISCONNECT}}, //FIXME -> DLE0 // should we do this _before_ hanging up for base driver? - {RSP_NODEV, 401,403, -1, 0, 0, {ACT_FAKEHUP}}, //FIXME -> DLE0 // should we do this _before_ hanging up for base driver? + {RSP_ZSAU, 403, 403, ZSAU_DISCONNECT_REQ, -1, -1, {ACT_DEBUG} }, + {RSP_ZSAU, 403, 403, ZSAU_NULL, 0, 0, {ACT_DISCONNECT} }, + {RSP_NODEV, 401, 403, -1, 0, 0, {ACT_FAKEHUP} }, {RSP_ERROR, 401,401, -1, 0, 0, {ACT_ABORTHUP}}, {EV_TIMEOUT, 401,403, -1, 0, 0, {ACT_ABORTHUP}}, @@ -474,8 +473,13 @@ static int cid_of_response(char *s) //FIXME is ;<digit>+ at end of non-CID response really impossible? } -/* This function will be called via task queue from the callback handler. - * We received a modem response and have to handle it.. +/** + * gigaset_handle_modem_response() - process received modem response + * @cs: device descriptor structure. + * + * Called by asyncdata/isocdata if a block of data received from the + * device must be processed as a modem command response. The data is + * already in the cs structure. */ void gigaset_handle_modem_response(struct cardstate *cs) { @@ -707,6 +711,11 @@ static void disconnect(struct at_state_t **at_state_p) if (bcs) { /* B channel assigned: invoke hardware specific handler */ cs->ops->close_bchannel(bcs); + /* notify LL */ + if (bcs->chstate & (CHS_D_UP | CHS_NOTIFY_LL)) { + bcs->chstate &= ~(CHS_D_UP | CHS_NOTIFY_LL); + gigaset_i4l_channel_cmd(bcs, ISDN_STAT_DHUP); + } } else { /* no B channel assigned: just deallocate */ spin_lock_irqsave(&cs->lock, flags); @@ -1429,11 +1438,12 @@ static void do_action(int action, struct cardstate *cs, cs->gotfwver = -1; dev_err(cs->dev, "could not read firmware version.\n"); break; -#ifdef CONFIG_GIGASET_DEBUG case ACT_ERROR: - *p_genresp = 1; - *p_resp_code = RSP_ERROR; + gig_dbg(DEBUG_ANY, "%s: ERROR response in ConState %d", + __func__, at_state->ConState); + cs->cur_at_seq = SEQ_NONE; break; +#ifdef CONFIG_GIGASET_DEBUG case ACT_TEST: { static int count = 3; //2; //1; diff --git a/drivers/isdn/gigaset/i4l.c b/drivers/isdn/gigaset/i4l.c index 9b22f9cf2f33..654489d836cd 100644 --- a/drivers/isdn/gigaset/i4l.c +++ b/drivers/isdn/gigaset/i4l.c @@ -51,6 +51,12 @@ static int writebuf_from_LL(int driverID, int channel, int ack, return -ENODEV; } bcs = &cs->bcs[channel]; + + /* can only handle linear sk_buffs */ + if (skb_linearize(skb) < 0) { + dev_err(cs->dev, "%s: skb_linearize failed\n", __func__); + return -ENOMEM; + } len = skb->len; gig_dbg(DEBUG_LLDATA, @@ -79,6 +85,14 @@ static int writebuf_from_LL(int driverID, int channel, int ack, return cs->ops->send_skb(bcs, skb); } +/** + * gigaset_skb_sent() - acknowledge sending an skb + * @bcs: B channel descriptor structure. + * @skb: sent data. + * + * Called by hardware module {bas,ser,usb}_gigaset when the data in a + * skb has been successfully sent, for signalling completion to the LL. + */ void gigaset_skb_sent(struct bc_state *bcs, struct sk_buff *skb) { unsigned len; @@ -455,6 +469,15 @@ int gigaset_isdn_setup_accept(struct at_state_t *at_state) return 0; } +/** + * gigaset_isdn_icall() - signal incoming call + * @at_state: connection state structure. + * + * Called by main module to notify the LL that an incoming call has been + * received. @at_state contains the parameters of the call. + * + * Return value: call disposition (ICALL_*) + */ int gigaset_isdn_icall(struct at_state_t *at_state) { struct cardstate *cs = at_state->cs; diff --git a/drivers/isdn/gigaset/interface.c b/drivers/isdn/gigaset/interface.c index f33ac27de643..6a8e1384e7bd 100644 --- a/drivers/isdn/gigaset/interface.c +++ b/drivers/isdn/gigaset/interface.c @@ -616,6 +616,15 @@ void gigaset_if_free(struct cardstate *cs) tty_unregister_device(drv->tty, cs->minor_index); } +/** + * gigaset_if_receive() - pass a received block of data to the tty device + * @cs: device descriptor structure. + * @buffer: received data. + * @len: number of bytes received. + * + * Called by asyncdata/isocdata if a block of data received from the + * device must be sent to userspace through the ttyG* device. + */ void gigaset_if_receive(struct cardstate *cs, unsigned char *buffer, size_t len) { diff --git a/drivers/isdn/gigaset/isocdata.c b/drivers/isdn/gigaset/isocdata.c index bed38fcc432b..9f3ef7b4248c 100644 --- a/drivers/isdn/gigaset/isocdata.c +++ b/drivers/isdn/gigaset/isocdata.c @@ -429,7 +429,7 @@ static inline int hdlc_buildframe(struct isowbuf_t *iwb, return -EAGAIN; } - dump_bytes(DEBUG_STREAM, "snd data", in, count); + dump_bytes(DEBUG_STREAM_DUMP, "snd data", in, count); /* bitstuff and checksum input data */ fcs = PPP_INITFCS; @@ -448,7 +448,6 @@ static inline int hdlc_buildframe(struct isowbuf_t *iwb, /* put closing flag and repeat byte for flag idle */ isowbuf_putflag(iwb); end = isowbuf_donewrite(iwb); - dump_bytes(DEBUG_STREAM_DUMP, "isowbuf", iwb->data, end + 1); return end; } @@ -482,6 +481,8 @@ static inline int trans_buildframe(struct isowbuf_t *iwb, } gig_dbg(DEBUG_STREAM, "put %d bytes", count); + dump_bytes(DEBUG_STREAM_DUMP, "snd data", in, count); + write = iwb->write; do { c = bitrev8(*in++); @@ -583,7 +584,7 @@ static inline void hdlc_done(struct bc_state *bcs) procskb->tail -= 2; gig_dbg(DEBUG_ISO, "%s: good frame (%d octets)", __func__, procskb->len); - dump_bytes(DEBUG_STREAM, + dump_bytes(DEBUG_STREAM_DUMP, "rcv data", procskb->data, procskb->len); bcs->hw.bas->goodbytes += procskb->len; gigaset_rcv_skb(procskb, bcs->cs, bcs); @@ -878,6 +879,8 @@ static inline void trans_receive(unsigned char *src, unsigned count, dobytes--; } if (dobytes == 0) { + dump_bytes(DEBUG_STREAM_DUMP, + "rcv data", skb->data, skb->len); gigaset_rcv_skb(skb, bcs->cs, bcs); bcs->skb = skb = dev_alloc_skb(SBUFSIZE + HW_HDR_LEN); if (!skb) { @@ -973,16 +976,17 @@ void gigaset_isoc_input(struct inbuf_t *inbuf) /* == data output ========================================================== */ -/* gigaset_send_skb - * called by common.c to queue an skb for sending - * and start transmission if necessary - * parameters: - * B Channel control structure - * skb - * return value: - * number of bytes accepted for sending - * (skb->len if ok, 0 if out of buffer space) - * or error code (< 0, eg. -EINVAL) +/** + * gigaset_isoc_send_skb() - queue an skb for sending + * @bcs: B channel descriptor structure. + * @skb: data to send. + * + * Called by i4l.c to queue an skb for sending, and start transmission if + * necessary. + * + * Return value: + * number of bytes accepted for sending (skb->len) if ok, + * error code < 0 (eg. -ENODEV) on error */ int gigaset_isoc_send_skb(struct bc_state *bcs, struct sk_buff *skb) { diff --git a/drivers/isdn/hardware/eicon/maintidi.c b/drivers/isdn/hardware/eicon/maintidi.c index 23960cb6eaab..41c26e756452 100644 --- a/drivers/isdn/hardware/eicon/maintidi.c +++ b/drivers/isdn/hardware/eicon/maintidi.c @@ -959,8 +959,9 @@ static int process_idi_event (diva_strace_context_t* pLib, } if (!strncmp("State\\Layer2 No1", path, pVar->path_length)) { char* tmp = &pLib->lines[0].pInterface->Layer2[0]; - dword l2_state; - diva_strace_read_uint (pVar, &l2_state); + dword l2_state; + if (diva_strace_read_uint(pVar, &l2_state)) + return -1; switch (l2_state) { case 0: diff --git a/drivers/isdn/hardware/eicon/message.c b/drivers/isdn/hardware/eicon/message.c index 27d5dd68f4fb..ae89fb89da64 100644 --- a/drivers/isdn/hardware/eicon/message.c +++ b/drivers/isdn/hardware/eicon/message.c @@ -2692,7 +2692,7 @@ static byte connect_b3_req(dword Id, word Number, DIVA_CAPI_ADAPTER *a, if (!(fax_control_bits & T30_CONTROL_BIT_MORE_DOCUMENTS) || (fax_feature_bits & T30_FEATURE_BIT_MORE_DOCUMENTS)) { - len = (byte)(&(((T30_INFO *) 0)->universal_6)); + len = offsetof(T30_INFO, universal_6); fax_info_change = false; if (ncpi->length >= 4) { @@ -2754,7 +2754,7 @@ static byte connect_b3_req(dword Id, word Number, DIVA_CAPI_ADAPTER *a, for (i = 0; i < w; i++) ((T30_INFO *)(plci->fax_connect_info_buffer))->station_id[i] = fax_parms[4].info[1+i]; ((T30_INFO *)(plci->fax_connect_info_buffer))->head_line_len = 0; - len = (byte)(((T30_INFO *) 0)->station_id + 20); + len = offsetof(T30_INFO, station_id) + 20; w = fax_parms[5].length; if (w > 20) w = 20; @@ -2788,7 +2788,7 @@ static byte connect_b3_req(dword Id, word Number, DIVA_CAPI_ADAPTER *a, } else { - len = (byte)(&(((T30_INFO *) 0)->universal_6)); + len = offsetof(T30_INFO, universal_6); } fax_info_change = true; @@ -2892,7 +2892,7 @@ static byte connect_b3_res(dword Id, word Number, DIVA_CAPI_ADAPTER *a, && (plci->nsf_control_bits & T30_NSF_CONTROL_BIT_ENABLE_NSF) && (plci->nsf_control_bits & T30_NSF_CONTROL_BIT_NEGOTIATE_RESP)) { - len = ((byte)(((T30_INFO *) 0)->station_id + 20)); + len = offsetof(T30_INFO, station_id) + 20; if (plci->fax_connect_info_length < len) { ((T30_INFO *)(plci->fax_connect_info_buffer))->station_id_len = 0; @@ -3802,7 +3802,7 @@ static byte manufacturer_res(dword Id, word Number, DIVA_CAPI_ADAPTER *a, break; } ncpi = &m_parms[1]; - len = ((byte)(((T30_INFO *) 0)->station_id + 20)); + len = offsetof(T30_INFO, station_id) + 20; if (plci->fax_connect_info_length < len) { ((T30_INFO *)(plci->fax_connect_info_buffer))->station_id_len = 0; @@ -6844,7 +6844,7 @@ static void nl_ind(PLCI *plci) if ((plci->requested_options_conn | plci->requested_options | a->requested_options_table[plci->appl->Id-1]) & ((1L << PRIVATE_FAX_SUB_SEP_PWD) | (1L << PRIVATE_FAX_NONSTANDARD))) { - i = ((word)(((T30_INFO *) 0)->station_id + 20)) + ((T30_INFO *)plci->NL.RBuffer->P)->head_line_len; + i = offsetof(T30_INFO, station_id) + 20 + ((T30_INFO *)plci->NL.RBuffer->P)->head_line_len; while (i < plci->NL.RBuffer->length) plci->ncpi_buffer[++len] = plci->NL.RBuffer->P[i++]; } @@ -7236,7 +7236,7 @@ static void nl_ind(PLCI *plci) { plci->RData[1].P = plci->RData[0].P; plci->RData[1].PLength = plci->RData[0].PLength; - plci->RData[0].P = v120_header_buffer + (-((int) v120_header_buffer) & 3); + plci->RData[0].P = v120_header_buffer + (-((unsigned long)v120_header_buffer) & 3); if ((plci->NL.RBuffer->P[0] & V120_HEADER_EXTEND_BIT) || (plci->NL.RLength == 1)) plci->RData[0].PLength = 1; else @@ -8473,7 +8473,7 @@ static word add_b23(PLCI *plci, API_PARSE *bp) fax_control_bits |= T30_CONTROL_BIT_ACCEPT_SEL_POLLING; } len = nlc[0]; - pos = ((byte)(((T30_INFO *) 0)->station_id + 20)); + pos = offsetof(T30_INFO, station_id) + 20; if (pos < plci->fax_connect_info_length) { for (i = 1 + plci->fax_connect_info_buffer[pos]; i != 0; i--) @@ -8525,7 +8525,7 @@ static word add_b23(PLCI *plci, API_PARSE *bp) } PUT_WORD(&(((T30_INFO *)&nlc[1])->control_bits_low), fax_control_bits); - len = ((byte)(((T30_INFO *) 0)->station_id + 20)); + len = offsetof(T30_INFO, station_id) + 20; for (i = 0; i < len; i++) plci->fax_connect_info_buffer[i] = nlc[1+i]; ((T30_INFO *) plci->fax_connect_info_buffer)->head_line_len = 0; diff --git a/drivers/isdn/hardware/mISDN/Kconfig b/drivers/isdn/hardware/mISDN/Kconfig index bde55d7287fa..eadc1cd34a20 100644 --- a/drivers/isdn/hardware/mISDN/Kconfig +++ b/drivers/isdn/hardware/mISDN/Kconfig @@ -78,6 +78,7 @@ config MISDN_NETJET depends on PCI select MISDN_IPAC select ISDN_HDLC + select ISDN_I4L help Enable support for Traverse Technologies NETJet PCI cards. diff --git a/drivers/isdn/hardware/mISDN/hfcmulti.c b/drivers/isdn/hardware/mISDN/hfcmulti.c index faed794cf75a..a6624ad252c5 100644 --- a/drivers/isdn/hardware/mISDN/hfcmulti.c +++ b/drivers/isdn/hardware/mISDN/hfcmulti.c @@ -5481,7 +5481,7 @@ HFCmulti_init(void) if (err) { printk(KERN_ERR "error registering embedded driver: " "%x\n", err); - return -err; + return err; } HFC_cnt++; printk(KERN_INFO "%d devices registered\n", HFC_cnt); diff --git a/drivers/isdn/hisax/amd7930_fn.c b/drivers/isdn/hisax/amd7930_fn.c index bf526a7a63af..d6fdf1f66754 100644 --- a/drivers/isdn/hisax/amd7930_fn.c +++ b/drivers/isdn/hisax/amd7930_fn.c @@ -594,6 +594,7 @@ Amd7930_l1hw(struct PStack *st, int pr, void *arg) if (cs->debug & L1_DEB_WARN) debugl1(cs, "Amd7930: l1hw: l2l1 tx_skb exist this shouldn't happen"); skb_queue_tail(&cs->sq, skb); + spin_unlock_irqrestore(&cs->lock, flags); break; } if (cs->debug & DEB_DLOG_HEX) diff --git a/drivers/isdn/hisax/arcofi.c b/drivers/isdn/hisax/arcofi.c index d30ce5b978c2..85a8fd8dd0b7 100644 --- a/drivers/isdn/hisax/arcofi.c +++ b/drivers/isdn/hisax/arcofi.c @@ -10,6 +10,7 @@ * */ +#include <linux/sched.h> #include "hisax.h" #include "isdnl1.h" #include "isac.h" diff --git a/drivers/isdn/hisax/diva.c b/drivers/isdn/hisax/diva.c index 018bd293e580..0b0c2e5d806b 100644 --- a/drivers/isdn/hisax/diva.c +++ b/drivers/isdn/hisax/diva.c @@ -382,7 +382,7 @@ MemwaitforXFW(struct IsdnCardState *cs, int hscx) { int to = 50; - while ((!(MemReadHSCX(cs, hscx, HSCX_STAR) & 0x44) == 0x40) && to) { + while (((MemReadHSCX(cs, hscx, HSCX_STAR) & 0x44) != 0x40) && to) { udelay(1); to--; } diff --git a/drivers/isdn/hisax/elsa_ser.c b/drivers/isdn/hisax/elsa_ser.c index f181db464392..1657bba7879e 100644 --- a/drivers/isdn/hisax/elsa_ser.c +++ b/drivers/isdn/hisax/elsa_ser.c @@ -477,62 +477,62 @@ static void modem_set_init(struct IsdnCardState *cs) { int timeout; -#define RCV_DELAY 20000 +#define RCV_DELAY 20 modem_write_cmd(cs, MInit_1, strlen(MInit_1)); timeout = 1000; while(timeout-- && cs->hw.elsa.transcnt) udelay(1000); debugl1(cs, "msi tout=%d", timeout); - udelay(RCV_DELAY); + mdelay(RCV_DELAY); modem_write_cmd(cs, MInit_2, strlen(MInit_2)); timeout = 1000; while(timeout-- && cs->hw.elsa.transcnt) udelay(1000); debugl1(cs, "msi tout=%d", timeout); - udelay(RCV_DELAY); + mdelay(RCV_DELAY); modem_write_cmd(cs, MInit_3, strlen(MInit_3)); timeout = 1000; while(timeout-- && cs->hw.elsa.transcnt) udelay(1000); debugl1(cs, "msi tout=%d", timeout); - udelay(RCV_DELAY); + mdelay(RCV_DELAY); modem_write_cmd(cs, MInit_4, strlen(MInit_4)); timeout = 1000; while(timeout-- && cs->hw.elsa.transcnt) udelay(1000); debugl1(cs, "msi tout=%d", timeout); - udelay(RCV_DELAY ); + mdelay(RCV_DELAY); modem_write_cmd(cs, MInit_5, strlen(MInit_5)); timeout = 1000; while(timeout-- && cs->hw.elsa.transcnt) udelay(1000); debugl1(cs, "msi tout=%d", timeout); - udelay(RCV_DELAY); + mdelay(RCV_DELAY); modem_write_cmd(cs, MInit_6, strlen(MInit_6)); timeout = 1000; while(timeout-- && cs->hw.elsa.transcnt) udelay(1000); debugl1(cs, "msi tout=%d", timeout); - udelay(RCV_DELAY); + mdelay(RCV_DELAY); modem_write_cmd(cs, MInit_7, strlen(MInit_7)); timeout = 1000; while(timeout-- && cs->hw.elsa.transcnt) udelay(1000); debugl1(cs, "msi tout=%d", timeout); - udelay(RCV_DELAY); + mdelay(RCV_DELAY); } static void modem_set_dial(struct IsdnCardState *cs, int outgoing) { int timeout; -#define RCV_DELAY 20000 +#define RCV_DELAY 20 modem_write_cmd(cs, MInit_speed28800, strlen(MInit_speed28800)); timeout = 1000; while(timeout-- && cs->hw.elsa.transcnt) udelay(1000); debugl1(cs, "msi tout=%d", timeout); - udelay(RCV_DELAY); + mdelay(RCV_DELAY); if (outgoing) modem_write_cmd(cs, MInit_dialout, strlen(MInit_dialout)); else @@ -541,7 +541,7 @@ modem_set_dial(struct IsdnCardState *cs, int outgoing) { while(timeout-- && cs->hw.elsa.transcnt) udelay(1000); debugl1(cs, "msi tout=%d", timeout); - udelay(RCV_DELAY); + mdelay(RCV_DELAY); } static void diff --git a/drivers/isdn/hisax/hfc_2bds0.c b/drivers/isdn/hisax/hfc_2bds0.c index 5c46a7130e06..8d22f50760eb 100644 --- a/drivers/isdn/hisax/hfc_2bds0.c +++ b/drivers/isdn/hisax/hfc_2bds0.c @@ -11,6 +11,7 @@ */ #include <linux/init.h> +#include <linux/sched.h> #include "hisax.h" #include "hfc_2bds0.h" #include "isdnl1.h" diff --git a/drivers/isdn/hisax/hfc_pci.c b/drivers/isdn/hisax/hfc_pci.c index d110a77940a4..10914731b304 100644 --- a/drivers/isdn/hisax/hfc_pci.c +++ b/drivers/isdn/hisax/hfc_pci.c @@ -20,6 +20,7 @@ #include "hfc_pci.h" #include "isdnl1.h" #include <linux/pci.h> +#include <linux/sched.h> #include <linux/interrupt.h> static const char *hfcpci_revision = "$Revision: 1.48.2.4 $"; diff --git a/drivers/isdn/hisax/hfc_usb.c b/drivers/isdn/hisax/hfc_usb.c index 9de54202c90c..a420b64472e3 100644 --- a/drivers/isdn/hisax/hfc_usb.c +++ b/drivers/isdn/hisax/hfc_usb.c @@ -817,8 +817,8 @@ collect_rx_frame(usb_fifo * fifo, __u8 * data, int len, int finish) } /* we have a complete hdlc packet */ if (finish) { - if ((!fifo->skbuff->data[fifo->skbuff->len - 1]) - && (fifo->skbuff->len > 3)) { + if (fifo->skbuff->len > 3 && + !fifo->skbuff->data[fifo->skbuff->len - 1]) { if (fifon == HFCUSB_D_RX) { DBG(HFCUSB_DBG_DCHANNEL, diff --git a/drivers/isdn/hisax/hscx_irq.c b/drivers/isdn/hisax/hscx_irq.c index 7b1ad5e4ecda..2387d76c721a 100644 --- a/drivers/isdn/hisax/hscx_irq.c +++ b/drivers/isdn/hisax/hscx_irq.c @@ -32,7 +32,7 @@ waitforXFW(struct IsdnCardState *cs, int hscx) { int to = 50; - while ((!(READHSCX(cs, hscx, HSCX_STAR) & 0x44) == 0x40) && to) { + while (((READHSCX(cs, hscx, HSCX_STAR) & 0x44) != 0x40) && to) { udelay(1); to--; } diff --git a/drivers/isdn/hisax/icc.c b/drivers/isdn/hisax/icc.c index 9aba646ba221..c80cbb8a2ef9 100644 --- a/drivers/isdn/hisax/icc.c +++ b/drivers/isdn/hisax/icc.c @@ -468,6 +468,7 @@ ICC_l1hw(struct PStack *st, int pr, void *arg) if (cs->debug & L1_DEB_WARN) debugl1(cs, " l2l1 tx_skb exist this shouldn't happen"); skb_queue_tail(&cs->sq, skb); + spin_unlock_irqrestore(&cs->lock, flags); break; } if (cs->debug & DEB_DLOG_HEX) diff --git a/drivers/isdn/hysdn/hysdn_procconf.c b/drivers/isdn/hysdn/hysdn_procconf.c index 8f9f4912de32..90b35e1a4b7e 100644 --- a/drivers/isdn/hysdn/hysdn_procconf.c +++ b/drivers/isdn/hysdn/hysdn_procconf.c @@ -11,6 +11,7 @@ * */ +#include <linux/cred.h> #include <linux/module.h> #include <linux/poll.h> #include <linux/proc_fs.h> diff --git a/drivers/isdn/hysdn/hysdn_proclog.c b/drivers/isdn/hysdn/hysdn_proclog.c index 8991d2c8ee4a..8bcae28c4409 100644 --- a/drivers/isdn/hysdn/hysdn_proclog.c +++ b/drivers/isdn/hysdn/hysdn_proclog.c @@ -13,6 +13,7 @@ #include <linux/module.h> #include <linux/poll.h> #include <linux/proc_fs.h> +#include <linux/sched.h> #include <linux/smp_lock.h> #include "hysdn_defs.h" diff --git a/drivers/isdn/i4l/Kconfig b/drivers/isdn/i4l/Kconfig index dd744ffd240b..07c4e49f9e77 100644 --- a/drivers/isdn/i4l/Kconfig +++ b/drivers/isdn/i4l/Kconfig @@ -141,8 +141,7 @@ endmenu endif config ISDN_HDLC - tristate - depends on HISAX_ST5481 + tristate select CRC_CCITT select BITREVERSE diff --git a/drivers/isdn/i4l/isdn_net.h b/drivers/isdn/i4l/isdn_net.h index 74032d0881ef..7511f08effa5 100644 --- a/drivers/isdn/i4l/isdn_net.h +++ b/drivers/isdn/i4l/isdn_net.h @@ -83,19 +83,19 @@ static __inline__ isdn_net_local * isdn_net_get_locked_lp(isdn_net_dev *nd) spin_lock_irqsave(&nd->queue_lock, flags); lp = nd->queue; /* get lp on top of queue */ - spin_lock(&nd->queue->xmit_lock); while (isdn_net_lp_busy(nd->queue)) { - spin_unlock(&nd->queue->xmit_lock); nd->queue = nd->queue->next; if (nd->queue == lp) { /* not found -- should never happen */ lp = NULL; goto errout; } - spin_lock(&nd->queue->xmit_lock); } lp = nd->queue; nd->queue = nd->queue->next; + spin_unlock_irqrestore(&nd->queue_lock, flags); + spin_lock(&lp->xmit_lock); local_bh_disable(); + return lp; errout: spin_unlock_irqrestore(&nd->queue_lock, flags); return lp; diff --git a/drivers/isdn/i4l/isdn_ppp.c b/drivers/isdn/i4l/isdn_ppp.c index 2d14b64202a3..642d5aaf53ce 100644 --- a/drivers/isdn/i4l/isdn_ppp.c +++ b/drivers/isdn/i4l/isdn_ppp.c @@ -1535,10 +1535,8 @@ static int isdn_ppp_mp_bundle_array_init(void) int sz = ISDN_MAX_CHANNELS*sizeof(ippp_bundle); if( (isdn_ppp_bundle_arr = kzalloc(sz, GFP_KERNEL)) == NULL ) return -ENOMEM; - for (i = 0; i < ISDN_MAX_CHANNELS; i++) { + for( i = 0; i < ISDN_MAX_CHANNELS; i++ ) spin_lock_init(&isdn_ppp_bundle_arr[i].lock); - skb_queue_head_init(&isdn_ppp_bundle_arr[i].frags); - } return 0; } @@ -1571,7 +1569,7 @@ static int isdn_ppp_mp_init( isdn_net_local * lp, ippp_bundle * add_to ) if ((lp->netdev->pb = isdn_ppp_mp_bundle_alloc()) == NULL) return -ENOMEM; lp->next = lp->last = lp; /* nobody else in a queue */ - skb_queue_head_init(&lp->netdev->pb->frags); + lp->netdev->pb->frags = NULL; lp->netdev->pb->frames = 0; lp->netdev->pb->seq = UINT_MAX; } @@ -1583,29 +1581,28 @@ static int isdn_ppp_mp_init( isdn_net_local * lp, ippp_bundle * add_to ) static u32 isdn_ppp_mp_get_seq( int short_seq, struct sk_buff * skb, u32 last_seq ); -static void isdn_ppp_mp_discard(ippp_bundle *mp, struct sk_buff *from, - struct sk_buff *to); -static void isdn_ppp_mp_reassembly(isdn_net_dev *net_dev, isdn_net_local *lp, - struct sk_buff *from, struct sk_buff *to, - u32 lastseq); -static void isdn_ppp_mp_free_skb(ippp_bundle *mp, struct sk_buff *skb); +static struct sk_buff * isdn_ppp_mp_discard( ippp_bundle * mp, + struct sk_buff * from, struct sk_buff * to ); +static void isdn_ppp_mp_reassembly( isdn_net_dev * net_dev, isdn_net_local * lp, + struct sk_buff * from, struct sk_buff * to ); +static void isdn_ppp_mp_free_skb( ippp_bundle * mp, struct sk_buff * skb ); static void isdn_ppp_mp_print_recv_pkt( int slot, struct sk_buff * skb ); static void isdn_ppp_mp_receive(isdn_net_dev * net_dev, isdn_net_local * lp, - struct sk_buff *skb) + struct sk_buff *skb) { - struct sk_buff *newfrag, *frag, *start, *nextf; - u32 newseq, minseq, thisseq; - isdn_mppp_stats *stats; struct ippp_struct *is; + isdn_net_local * lpq; + ippp_bundle * mp; + isdn_mppp_stats * stats; + struct sk_buff * newfrag, * frag, * start, *nextf; + u32 newseq, minseq, thisseq; unsigned long flags; - isdn_net_local *lpq; - ippp_bundle *mp; int slot; spin_lock_irqsave(&net_dev->pb->lock, flags); - mp = net_dev->pb; - stats = &mp->stats; + mp = net_dev->pb; + stats = &mp->stats; slot = lp->ppp_slot; if (slot < 0 || slot >= ISDN_MAX_CHANNELS) { printk(KERN_ERR "%s: lp->ppp_slot(%d)\n", @@ -1616,19 +1613,20 @@ static void isdn_ppp_mp_receive(isdn_net_dev * net_dev, isdn_net_local * lp, return; } is = ippp_table[slot]; - if (++mp->frames > stats->max_queue_len) + if( ++mp->frames > stats->max_queue_len ) stats->max_queue_len = mp->frames; - + if (is->debug & 0x8) isdn_ppp_mp_print_recv_pkt(lp->ppp_slot, skb); - newseq = isdn_ppp_mp_get_seq(is->mpppcfg & SC_IN_SHORT_SEQ, - skb, is->last_link_seqno); + newseq = isdn_ppp_mp_get_seq(is->mpppcfg & SC_IN_SHORT_SEQ, + skb, is->last_link_seqno); + /* if this packet seq # is less than last already processed one, * toss it right away, but check for sequence start case first */ - if (mp->seq > MP_LONGSEQ_MAX && (newseq & MP_LONGSEQ_MAXBIT)) { + if( mp->seq > MP_LONGSEQ_MAX && (newseq & MP_LONGSEQ_MAXBIT) ) { mp->seq = newseq; /* the first packet: required for * rfc1990 non-compliant clients -- * prevents constant packet toss */ @@ -1638,7 +1636,7 @@ static void isdn_ppp_mp_receive(isdn_net_dev * net_dev, isdn_net_local * lp, spin_unlock_irqrestore(&mp->lock, flags); return; } - + /* find the minimum received sequence number over all links */ is->last_link_seqno = minseq = newseq; for (lpq = net_dev->queue;;) { @@ -1659,31 +1657,22 @@ static void isdn_ppp_mp_receive(isdn_net_dev * net_dev, isdn_net_local * lp, * packets */ newfrag = skb; - /* Insert new fragment into the proper sequence slot. */ - skb_queue_walk(&mp->frags, frag) { - if (MP_SEQ(frag) == newseq) { - isdn_ppp_mp_free_skb(mp, newfrag); - newfrag = NULL; - break; - } - if (MP_LT(newseq, MP_SEQ(frag))) { - __skb_queue_before(&mp->frags, frag, newfrag); - newfrag = NULL; - break; - } - } - if (newfrag) - __skb_queue_tail(&mp->frags, newfrag); + /* if this new fragment is before the first one, then enqueue it now. */ + if ((frag = mp->frags) == NULL || MP_LT(newseq, MP_SEQ(frag))) { + newfrag->next = frag; + mp->frags = frag = newfrag; + newfrag = NULL; + } - frag = skb_peek(&mp->frags); - start = ((MP_FLAGS(frag) & MP_BEGIN_FRAG) && - (MP_SEQ(frag) == mp->seq)) ? frag : NULL; - if (!start) - goto check_overflow; + start = MP_FLAGS(frag) & MP_BEGIN_FRAG && + MP_SEQ(frag) == mp->seq ? frag : NULL; - /* main fragment traversing loop + /* + * main fragment traversing loop * * try to accomplish several tasks: + * - insert new fragment into the proper sequence slot (once that's done + * newfrag will be set to NULL) * - reassemble any complete fragment sequence (non-null 'start' * indicates there is a continguous sequence present) * - discard any incomplete sequences that are below minseq -- due @@ -1692,46 +1681,71 @@ static void isdn_ppp_mp_receive(isdn_net_dev * net_dev, isdn_net_local * lp, * come to complete such sequence and it should be discarded * * loop completes when we accomplished the following tasks: + * - new fragment is inserted in the proper sequence ('newfrag' is + * set to NULL) * - we hit a gap in the sequence, so no reassembly/processing is * possible ('start' would be set to NULL) * * algorithm for this code is derived from code in the book * 'PPP Design And Debugging' by James Carlson (Addison-Wesley) */ - skb_queue_walk_safe(&mp->frags, frag, nextf) { - thisseq = MP_SEQ(frag); - - /* check for misplaced start */ - if (start != frag && (MP_FLAGS(frag) & MP_BEGIN_FRAG)) { - printk(KERN_WARNING"isdn_mppp(seq %d): new " - "BEGIN flag with no prior END", thisseq); - stats->seqerrs++; - stats->frame_drops++; - isdn_ppp_mp_discard(mp, start, frag); - start = frag; - } else if (MP_LE(thisseq, minseq)) { - if (MP_FLAGS(frag) & MP_BEGIN_FRAG) + while (start != NULL || newfrag != NULL) { + + thisseq = MP_SEQ(frag); + nextf = frag->next; + + /* drop any duplicate fragments */ + if (newfrag != NULL && thisseq == newseq) { + isdn_ppp_mp_free_skb(mp, newfrag); + newfrag = NULL; + } + + /* insert new fragment before next element if possible. */ + if (newfrag != NULL && (nextf == NULL || + MP_LT(newseq, MP_SEQ(nextf)))) { + newfrag->next = nextf; + frag->next = nextf = newfrag; + newfrag = NULL; + } + + if (start != NULL) { + /* check for misplaced start */ + if (start != frag && (MP_FLAGS(frag) & MP_BEGIN_FRAG)) { + printk(KERN_WARNING"isdn_mppp(seq %d): new " + "BEGIN flag with no prior END", thisseq); + stats->seqerrs++; + stats->frame_drops++; + start = isdn_ppp_mp_discard(mp, start,frag); + nextf = frag->next; + } + } else if (MP_LE(thisseq, minseq)) { + if (MP_FLAGS(frag) & MP_BEGIN_FRAG) start = frag; - else { + else { if (MP_FLAGS(frag) & MP_END_FRAG) - stats->frame_drops++; - __skb_unlink(skb, &mp->frags); + stats->frame_drops++; + if( mp->frags == frag ) + mp->frags = nextf; isdn_ppp_mp_free_skb(mp, frag); + frag = nextf; continue; - } + } } - - /* if we have end fragment, then we have full reassembly - * sequence -- reassemble and process packet now + + /* if start is non-null and we have end fragment, then + * we have full reassembly sequence -- reassemble + * and process packet now */ - if (MP_FLAGS(frag) & MP_END_FRAG) { - minseq = mp->seq = (thisseq+1) & MP_LONGSEQ_MASK; - /* Reassemble the packet then dispatch it */ - isdn_ppp_mp_reassembly(net_dev, lp, start, frag, thisseq); + if (start != NULL && (MP_FLAGS(frag) & MP_END_FRAG)) { + minseq = mp->seq = (thisseq+1) & MP_LONGSEQ_MASK; + /* Reassemble the packet then dispatch it */ + isdn_ppp_mp_reassembly(net_dev, lp, start, nextf); + + start = NULL; + frag = NULL; - start = NULL; - frag = NULL; - } + mp->frags = nextf; + } /* check if need to update start pointer: if we just * reassembled the packet and sequence is contiguous @@ -1742,25 +1756,26 @@ static void isdn_ppp_mp_receive(isdn_net_dev * net_dev, isdn_net_local * lp, * below low watermark and set start to the next frag or * clear start ptr. */ - if (nextf != (struct sk_buff *)&mp->frags && + if (nextf != NULL && ((thisseq+1) & MP_LONGSEQ_MASK) == MP_SEQ(nextf)) { - /* if we just reassembled and the next one is here, - * then start another reassembly. - */ - if (frag == NULL) { + /* if we just reassembled and the next one is here, + * then start another reassembly. */ + + if (frag == NULL) { if (MP_FLAGS(nextf) & MP_BEGIN_FRAG) - start = nextf; - else { - printk(KERN_WARNING"isdn_mppp(seq %d):" - " END flag with no following " - "BEGIN", thisseq); + start = nextf; + else + { + printk(KERN_WARNING"isdn_mppp(seq %d):" + " END flag with no following " + "BEGIN", thisseq); stats->seqerrs++; } } - } else { - if (nextf != (struct sk_buff *)&mp->frags && - frag != NULL && - MP_LT(thisseq, minseq)) { + + } else { + if ( nextf != NULL && frag != NULL && + MP_LT(thisseq, minseq)) { /* we've got a break in the sequence * and we not at the end yet * and we did not just reassembled @@ -1769,39 +1784,41 @@ static void isdn_ppp_mp_receive(isdn_net_dev * net_dev, isdn_net_local * lp, * discard all the frames below low watermark * and start over */ stats->frame_drops++; - isdn_ppp_mp_discard(mp, start, nextf); + mp->frags = isdn_ppp_mp_discard(mp,start,nextf); } /* break in the sequence, no reassembly */ - start = NULL; - } - if (!start) - break; - } - -check_overflow: + start = NULL; + } + + frag = nextf; + } /* while -- main loop */ + + if (mp->frags == NULL) + mp->frags = frag; + /* rather straighforward way to deal with (not very) possible - * queue overflow - */ + * queue overflow */ if (mp->frames > MP_MAX_QUEUE_LEN) { stats->overflows++; - skb_queue_walk_safe(&mp->frags, frag, nextf) { - if (mp->frames <= MP_MAX_QUEUE_LEN) - break; - __skb_unlink(frag, &mp->frags); - isdn_ppp_mp_free_skb(mp, frag); + while (mp->frames > MP_MAX_QUEUE_LEN) { + frag = mp->frags->next; + isdn_ppp_mp_free_skb(mp, mp->frags); + mp->frags = frag; } } spin_unlock_irqrestore(&mp->lock, flags); } -static void isdn_ppp_mp_cleanup(isdn_net_local *lp) +static void isdn_ppp_mp_cleanup( isdn_net_local * lp ) { - struct sk_buff *skb, *tmp; - - skb_queue_walk_safe(&lp->netdev->pb->frags, skb, tmp) { - __skb_unlink(skb, &lp->netdev->pb->frags); - isdn_ppp_mp_free_skb(lp->netdev->pb, skb); - } + struct sk_buff * frag = lp->netdev->pb->frags; + struct sk_buff * nextfrag; + while( frag ) { + nextfrag = frag->next; + isdn_ppp_mp_free_skb(lp->netdev->pb, frag); + frag = nextfrag; + } + lp->netdev->pb->frags = NULL; } static u32 isdn_ppp_mp_get_seq( int short_seq, @@ -1838,115 +1855,72 @@ static u32 isdn_ppp_mp_get_seq( int short_seq, return seq; } -static void isdn_ppp_mp_discard(ippp_bundle *mp, struct sk_buff *from, - struct sk_buff *to) +struct sk_buff * isdn_ppp_mp_discard( ippp_bundle * mp, + struct sk_buff * from, struct sk_buff * to ) { - if (from) { - struct sk_buff *skb, *tmp; - int freeing = 0; - - skb_queue_walk_safe(&mp->frags, skb, tmp) { - if (skb == to) - break; - if (skb == from) - freeing = 1; - if (!freeing) - continue; - __skb_unlink(skb, &mp->frags); - isdn_ppp_mp_free_skb(mp, skb); + if( from ) + while (from != to) { + struct sk_buff * next = from->next; + isdn_ppp_mp_free_skb(mp, from); + from = next; } - } -} - -static unsigned int calc_tot_len(struct sk_buff_head *queue, - struct sk_buff *from, struct sk_buff *to) -{ - unsigned int tot_len = 0; - struct sk_buff *skb; - int found_start = 0; - - skb_queue_walk(queue, skb) { - if (skb == from) - found_start = 1; - if (!found_start) - continue; - tot_len += skb->len - MP_HEADER_LEN; - if (skb == to) - break; - } - return tot_len; + return from; } -/* Reassemble packet using fragments in the reassembly queue from - * 'from' until 'to', inclusive. - */ -static void isdn_ppp_mp_reassembly(isdn_net_dev *net_dev, isdn_net_local *lp, - struct sk_buff *from, struct sk_buff *to, - u32 lastseq) +void isdn_ppp_mp_reassembly( isdn_net_dev * net_dev, isdn_net_local * lp, + struct sk_buff * from, struct sk_buff * to ) { - ippp_bundle *mp = net_dev->pb; - unsigned int tot_len; - struct sk_buff *skb; + ippp_bundle * mp = net_dev->pb; int proto; + struct sk_buff * skb; + unsigned int tot_len; if (lp->ppp_slot < 0 || lp->ppp_slot >= ISDN_MAX_CHANNELS) { printk(KERN_ERR "%s: lp->ppp_slot(%d) out of range\n", __func__, lp->ppp_slot); return; } - - tot_len = calc_tot_len(&mp->frags, from, to); - - if (MP_FLAGS(from) == (MP_BEGIN_FRAG | MP_END_FRAG)) { - if (ippp_table[lp->ppp_slot]->debug & 0x40) + if( MP_FLAGS(from) == (MP_BEGIN_FRAG | MP_END_FRAG) ) { + if( ippp_table[lp->ppp_slot]->debug & 0x40 ) printk(KERN_DEBUG "isdn_mppp: reassembly: frame %d, " - "len %d\n", MP_SEQ(from), from->len); + "len %d\n", MP_SEQ(from), from->len ); skb = from; skb_pull(skb, MP_HEADER_LEN); - __skb_unlink(skb, &mp->frags); mp->frames--; } else { - struct sk_buff *walk, *tmp; - int found_start = 0; + struct sk_buff * frag; + int n; - if (ippp_table[lp->ppp_slot]->debug & 0x40) - printk(KERN_DEBUG"isdn_mppp: reassembling frames %d " - "to %d, len %d\n", MP_SEQ(from), lastseq, - tot_len); + for(tot_len=n=0, frag=from; frag != to; frag=frag->next, n++) + tot_len += frag->len - MP_HEADER_LEN; - skb = dev_alloc_skb(tot_len); - if (!skb) + if( ippp_table[lp->ppp_slot]->debug & 0x40 ) + printk(KERN_DEBUG"isdn_mppp: reassembling frames %d " + "to %d, len %d\n", MP_SEQ(from), + (MP_SEQ(from)+n-1) & MP_LONGSEQ_MASK, tot_len ); + if( (skb = dev_alloc_skb(tot_len)) == NULL ) { printk(KERN_ERR "isdn_mppp: cannot allocate sk buff " - "of size %d\n", tot_len); - - found_start = 0; - skb_queue_walk_safe(&mp->frags, walk, tmp) { - if (walk == from) - found_start = 1; - if (!found_start) - continue; + "of size %d\n", tot_len); + isdn_ppp_mp_discard(mp, from, to); + return; + } - if (skb) { - unsigned int len = walk->len - MP_HEADER_LEN; - skb_copy_from_linear_data_offset(walk, MP_HEADER_LEN, - skb_put(skb, len), - len); - } - __skb_unlink(walk, &mp->frags); - isdn_ppp_mp_free_skb(mp, walk); + while( from != to ) { + unsigned int len = from->len - MP_HEADER_LEN; - if (walk == to) - break; + skb_copy_from_linear_data_offset(from, MP_HEADER_LEN, + skb_put(skb,len), + len); + frag = from->next; + isdn_ppp_mp_free_skb(mp, from); + from = frag; } } - if (!skb) - return; - proto = isdn_ppp_strip_proto(skb); isdn_ppp_push_higher(net_dev, lp, skb, proto); } -static void isdn_ppp_mp_free_skb(ippp_bundle *mp, struct sk_buff *skb) +static void isdn_ppp_mp_free_skb(ippp_bundle * mp, struct sk_buff * skb) { dev_kfree_skb(skb); mp->frames--; diff --git a/drivers/isdn/mISDN/socket.c b/drivers/isdn/mISDN/socket.c index c36f52137456..feb0fa45b664 100644 --- a/drivers/isdn/mISDN/socket.c +++ b/drivers/isdn/mISDN/socket.c @@ -415,7 +415,7 @@ data_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) } static int data_sock_setsockopt(struct socket *sock, int level, int optname, - char __user *optval, int len) + char __user *optval, unsigned int len) { struct sock *sk = sock->sk; int err = 0, opt = 0; diff --git a/drivers/isdn/mISDN/stack.c b/drivers/isdn/mISDN/stack.c index 3e1532a180ff..0d05ec43012c 100644 --- a/drivers/isdn/mISDN/stack.c +++ b/drivers/isdn/mISDN/stack.c @@ -364,7 +364,7 @@ add_layer2(struct mISDNchannel *ch, struct mISDNstack *st) static int st_own_ctrl(struct mISDNchannel *ch, u_int cmd, void *arg) { - if (!ch->st || ch->st->layer1) + if (!ch->st || !ch->st->layer1) return -EINVAL; return ch->st->layer1->ctrl(ch->st->layer1, cmd, arg); } diff --git a/drivers/isdn/pcbit/drv.c b/drivers/isdn/pcbit/drv.c index 8c66bcb953a1..123c1d6c43b4 100644 --- a/drivers/isdn/pcbit/drv.c +++ b/drivers/isdn/pcbit/drv.c @@ -23,6 +23,7 @@ #include <linux/kernel.h> #include <linux/types.h> +#include <linux/sched.h> #include <linux/slab.h> #include <linux/mm.h> #include <linux/interrupt.h> diff --git a/drivers/isdn/pcbit/layer2.c b/drivers/isdn/pcbit/layer2.c index e075e8d2fce0..30f0f45e3139 100644 --- a/drivers/isdn/pcbit/layer2.c +++ b/drivers/isdn/pcbit/layer2.c @@ -27,6 +27,7 @@ #include <linux/string.h> #include <linux/kernel.h> #include <linux/types.h> +#include <linux/sched.h> #include <linux/slab.h> #include <linux/interrupt.h> #include <linux/workqueue.h> diff --git a/drivers/isdn/sc/init.c b/drivers/isdn/sc/init.c index dd0acd06750b..5a0774880d56 100644 --- a/drivers/isdn/sc/init.c +++ b/drivers/isdn/sc/init.c @@ -8,6 +8,7 @@ #include <linux/init.h> #include <linux/interrupt.h> #include <linux/delay.h> +#include <linux/sched.h> #include "includes.h" #include "hardware.h" #include "card.h" diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig index 7c8e7122aaa9..e4f599f20e38 100644 --- a/drivers/leds/Kconfig +++ b/drivers/leds/Kconfig @@ -150,9 +150,9 @@ config LEDS_LP3944 tristate "LED Support for N.S. LP3944 (Fun Light) I2C chip" depends on LEDS_CLASS && I2C help - This option enables support for LEDs connected to the National - Semiconductor LP3944 Lighting Management Unit (LMU) also known as - Fun Light Chip. + This option enables support for LEDs connected to the National + Semiconductor LP3944 Lighting Management Unit (LMU) also known as + Fun Light Chip. To compile this driver as a module, choose M here: the module will be called leds-lp3944. @@ -195,6 +195,13 @@ config LEDS_PCA955X LED driver chips accessed via the I2C bus. Supported devices include PCA9550, PCA9551, PCA9552, and PCA9553. +config LEDS_WM831X_STATUS + tristate "LED support for status LEDs on WM831x PMICs" + depends on LEDS_CLASS && MFD_WM831X + help + This option enables support for the status LEDs of the WM831x + series of PMICs. + config LEDS_WM8350 tristate "LED Support for WM8350 AudioPlus PMIC" depends on LEDS_CLASS && MFD_WM8350 diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile index e8cdcf77a4c3..46d72704d606 100644 --- a/drivers/leds/Makefile +++ b/drivers/leds/Makefile @@ -26,6 +26,7 @@ obj-$(CONFIG_LEDS_HP6XX) += leds-hp6xx.o obj-$(CONFIG_LEDS_FSG) += leds-fsg.o obj-$(CONFIG_LEDS_PCA955X) += leds-pca955x.o obj-$(CONFIG_LEDS_DA903X) += leds-da903x.o +obj-$(CONFIG_LEDS_WM831X_STATUS) += leds-wm831x-status.o obj-$(CONFIG_LEDS_WM8350) += leds-wm8350.o obj-$(CONFIG_LEDS_PWM) += leds-pwm.o diff --git a/drivers/leds/leds-clevo-mail.c b/drivers/leds/leds-clevo-mail.c index 1813c84ea5fc..a498135a4e80 100644 --- a/drivers/leds/leds-clevo-mail.c +++ b/drivers/leds/leds-clevo-mail.c @@ -93,6 +93,8 @@ static struct dmi_system_id __initdata mail_led_whitelist[] = { static void clevo_mail_led_set(struct led_classdev *led_cdev, enum led_brightness value) { + i8042_lock_chip(); + if (value == LED_OFF) i8042_command(NULL, CLEVO_MAIL_LED_OFF); else if (value <= LED_HALF) @@ -100,6 +102,8 @@ static void clevo_mail_led_set(struct led_classdev *led_cdev, else i8042_command(NULL, CLEVO_MAIL_LED_BLINK_1HZ); + i8042_unlock_chip(); + } static int clevo_mail_led_blink(struct led_classdev *led_cdev, @@ -108,6 +112,8 @@ static int clevo_mail_led_blink(struct led_classdev *led_cdev, { int status = -EINVAL; + i8042_lock_chip(); + if (*delay_on == 0 /* ms */ && *delay_off == 0 /* ms */) { /* Special case: the leds subsystem requested us to * chose one user friendly blinking of the LED, and @@ -135,6 +141,8 @@ static int clevo_mail_led_blink(struct led_classdev *led_cdev, *delay_on, *delay_off); } + i8042_unlock_chip(); + return status; } @@ -145,7 +153,7 @@ static struct led_classdev clevo_mail_led = { .flags = LED_CORE_SUSPENDRESUME, }; -static int __init clevo_mail_led_probe(struct platform_device *pdev) +static int __devinit clevo_mail_led_probe(struct platform_device *pdev) { return led_classdev_register(&pdev->dev, &clevo_mail_led); } diff --git a/drivers/leds/leds-cobalt-qube.c b/drivers/leds/leds-cobalt-qube.c index 059aa2924b1c..8816806accd2 100644 --- a/drivers/leds/leds-cobalt-qube.c +++ b/drivers/leds/leds-cobalt-qube.c @@ -28,7 +28,7 @@ static void qube_front_led_set(struct led_classdev *led_cdev, } static struct led_classdev qube_front_led = { - .name = "qube-front", + .name = "qube::front", .brightness = LED_FULL, .brightness_set = qube_front_led_set, .default_trigger = "ide-disk", diff --git a/drivers/leds/leds-cobalt-raq.c b/drivers/leds/leds-cobalt-raq.c index 5f1ce810815f..defc212105f3 100644 --- a/drivers/leds/leds-cobalt-raq.c +++ b/drivers/leds/leds-cobalt-raq.c @@ -49,7 +49,7 @@ static void raq_web_led_set(struct led_classdev *led_cdev, } static struct led_classdev raq_web_led = { - .name = "raq-web", + .name = "raq::web", .brightness_set = raq_web_led_set, }; @@ -70,7 +70,7 @@ static void raq_power_off_led_set(struct led_classdev *led_cdev, } static struct led_classdev raq_power_off_led = { - .name = "raq-power-off", + .name = "raq::power-off", .brightness_set = raq_power_off_led_set, .default_trigger = "power-off", }; diff --git a/drivers/leds/leds-dac124s085.c b/drivers/leds/leds-dac124s085.c index 098d9aae7259..2913d76ad3d2 100644 --- a/drivers/leds/leds-dac124s085.c +++ b/drivers/leds/leds-dac124s085.c @@ -148,3 +148,4 @@ module_exit(dac124s085_leds_exit); MODULE_AUTHOR("Guennadi Liakhovetski <lg@denx.de>"); MODULE_DESCRIPTION("DAC124S085 LED driver"); MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("spi:dac124s085"); diff --git a/drivers/leds/leds-gpio.c b/drivers/leds/leds-gpio.c index 6b06638eb5b4..e5225d28f392 100644 --- a/drivers/leds/leds-gpio.c +++ b/drivers/leds/leds-gpio.c @@ -78,9 +78,11 @@ static int __devinit create_gpio_led(const struct gpio_led *template, { int ret, state; + led_dat->gpio = -1; + /* skip leds that aren't available */ if (!gpio_is_valid(template->gpio)) { - printk(KERN_INFO "Skipping unavilable LED gpio %d (%s)\n", + printk(KERN_INFO "Skipping unavailable LED gpio %d (%s)\n", template->gpio, template->name); return 0; } diff --git a/drivers/leds/leds-pca9532.c b/drivers/leds/leds-pca9532.c index dba8921240f2..adc561eb59d2 100644 --- a/drivers/leds/leds-pca9532.c +++ b/drivers/leds/leds-pca9532.c @@ -19,9 +19,6 @@ #include <linux/workqueue.h> #include <linux/leds-pca9532.h> -static const unsigned short normal_i2c[] = { /*0x60,*/ I2C_CLIENT_END}; -I2C_CLIENT_INSMOD_1(pca9532); - #define PCA9532_REG_PSC(i) (0x2+(i)*2) #define PCA9532_REG_PWM(i) (0x3+(i)*2) #define PCA9532_REG_LS0 0x6 @@ -34,7 +31,7 @@ struct pca9532_data { struct i2c_client *client; struct pca9532_led leds[16]; struct mutex update_lock; - struct input_dev *idev; + struct input_dev *idev; struct work_struct work; u8 pwm[2]; u8 psc[2]; @@ -53,9 +50,9 @@ MODULE_DEVICE_TABLE(i2c, pca9532_id); static struct i2c_driver pca9532_driver = { .driver = { - .name = "pca9532", + .name = "pca9532", }, - .probe = pca9532_probe, + .probe = pca9532_probe, .remove = pca9532_remove, .id_table = pca9532_id, }; @@ -149,7 +146,7 @@ static int pca9532_set_blink(struct led_classdev *led_cdev, if (*delay_on == 0 && *delay_off == 0) { /* led subsystem ask us for a blink rate */ - *delay_on = 1000; + *delay_on = 1000; *delay_off = 1000; } if (*delay_on != *delay_off || *delay_on > 1690 || *delay_on < 6) @@ -227,7 +224,7 @@ static int pca9532_configure(struct i2c_client *client, break; case PCA9532_TYPE_LED: led->state = pled->state; - led->name = pled->name; + led->name = pled->name; led->ldev.name = led->name; led->ldev.brightness = LED_OFF; led->ldev.brightness_set = pca9532_set_brightness; @@ -254,7 +251,7 @@ static int pca9532_configure(struct i2c_client *client, data->idev->name = pled->name; data->idev->phys = "i2c/pca9532"; data->idev->id.bustype = BUS_HOST; - data->idev->id.vendor = 0x001f; + data->idev->id.vendor = 0x001f; data->idev->id.product = 0x0001; data->idev->id.version = 0x0100; data->idev->evbit[0] = BIT_MASK(EV_SND); diff --git a/drivers/leds/leds-wm831x-status.c b/drivers/leds/leds-wm831x-status.c new file mode 100644 index 000000000000..c586d05e336a --- /dev/null +++ b/drivers/leds/leds-wm831x-status.c @@ -0,0 +1,341 @@ +/* + * LED driver for WM831x status LEDs + * + * Copyright(C) 2009 Wolfson Microelectronics PLC. + * + * 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/err.h> +#include <linux/mfd/wm831x/core.h> +#include <linux/mfd/wm831x/pdata.h> +#include <linux/mfd/wm831x/status.h> + + +struct wm831x_status { + struct led_classdev cdev; + struct wm831x *wm831x; + struct work_struct work; + struct mutex mutex; + + spinlock_t value_lock; + int reg; /* Control register */ + int reg_val; /* Control register value */ + + int blink; + int blink_time; + int blink_cyc; + int src; + enum led_brightness brightness; +}; + +#define to_wm831x_status(led_cdev) \ + container_of(led_cdev, struct wm831x_status, cdev) + +static void wm831x_status_work(struct work_struct *work) +{ + struct wm831x_status *led = container_of(work, struct wm831x_status, + work); + unsigned long flags; + + mutex_lock(&led->mutex); + + led->reg_val &= ~(WM831X_LED_SRC_MASK | WM831X_LED_MODE_MASK | + WM831X_LED_DUTY_CYC_MASK | WM831X_LED_DUR_MASK); + + spin_lock_irqsave(&led->value_lock, flags); + + led->reg_val |= led->src << WM831X_LED_SRC_SHIFT; + if (led->blink) { + led->reg_val |= 2 << WM831X_LED_MODE_SHIFT; + led->reg_val |= led->blink_time << WM831X_LED_DUR_SHIFT; + led->reg_val |= led->blink_cyc; + } else { + if (led->brightness != LED_OFF) + led->reg_val |= 1 << WM831X_LED_MODE_SHIFT; + } + + spin_unlock_irqrestore(&led->value_lock, flags); + + wm831x_reg_write(led->wm831x, led->reg, led->reg_val); + + mutex_unlock(&led->mutex); +} + +static void wm831x_status_set(struct led_classdev *led_cdev, + enum led_brightness value) +{ + struct wm831x_status *led = to_wm831x_status(led_cdev); + unsigned long flags; + + spin_lock_irqsave(&led->value_lock, flags); + led->brightness = value; + if (value == LED_OFF) + led->blink = 0; + schedule_work(&led->work); + spin_unlock_irqrestore(&led->value_lock, flags); +} + +static int wm831x_status_blink_set(struct led_classdev *led_cdev, + unsigned long *delay_on, + unsigned long *delay_off) +{ + struct wm831x_status *led = to_wm831x_status(led_cdev); + unsigned long flags; + int ret = 0; + + /* Pick some defaults if we've not been given times */ + if (*delay_on == 0 && *delay_off == 0) { + *delay_on = 250; + *delay_off = 250; + } + + spin_lock_irqsave(&led->value_lock, flags); + + /* We only have a limited selection of settings, see if we can + * support the configuration we're being given */ + switch (*delay_on) { + case 1000: + led->blink_time = 0; + break; + case 250: + led->blink_time = 1; + break; + case 125: + led->blink_time = 2; + break; + case 62: + case 63: + /* Actually 62.5ms */ + led->blink_time = 3; + break; + default: + ret = -EINVAL; + break; + } + + if (ret == 0) { + switch (*delay_off / *delay_on) { + case 1: + led->blink_cyc = 0; + break; + case 3: + led->blink_cyc = 1; + break; + case 4: + led->blink_cyc = 2; + break; + case 8: + led->blink_cyc = 3; + break; + default: + ret = -EINVAL; + break; + } + } + + if (ret == 0) + led->blink = 1; + else + led->blink = 0; + + /* Always update; if we fail turn off blinking since we expect + * a software fallback. */ + schedule_work(&led->work); + + spin_unlock_irqrestore(&led->value_lock, flags); + + return ret; +} + +static const char *led_src_texts[] = { + "otp", + "power", + "charger", + "soft", +}; + +static ssize_t wm831x_status_src_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct led_classdev *led_cdev = dev_get_drvdata(dev); + struct wm831x_status *led = to_wm831x_status(led_cdev); + int i; + ssize_t ret = 0; + + mutex_lock(&led->mutex); + + for (i = 0; i < ARRAY_SIZE(led_src_texts); i++) + if (i == led->src) + ret += sprintf(&buf[ret], "[%s] ", led_src_texts[i]); + else + ret += sprintf(&buf[ret], "%s ", led_src_texts[i]); + + mutex_unlock(&led->mutex); + + ret += sprintf(&buf[ret], "\n"); + + return ret; +} + +static ssize_t wm831x_status_src_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + struct led_classdev *led_cdev = dev_get_drvdata(dev); + struct wm831x_status *led = to_wm831x_status(led_cdev); + char name[20]; + int i; + size_t len; + + name[sizeof(name) - 1] = '\0'; + strncpy(name, buf, sizeof(name) - 1); + len = strlen(name); + + if (len && name[len - 1] == '\n') + name[len - 1] = '\0'; + + for (i = 0; i < ARRAY_SIZE(led_src_texts); i++) { + if (!strcmp(name, led_src_texts[i])) { + mutex_lock(&led->mutex); + + led->src = i; + schedule_work(&led->work); + + mutex_unlock(&led->mutex); + } + } + + return size; +} + +static DEVICE_ATTR(src, 0644, wm831x_status_src_show, wm831x_status_src_store); + +static int wm831x_status_probe(struct platform_device *pdev) +{ + struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent); + struct wm831x_pdata *chip_pdata; + struct wm831x_status_pdata pdata; + struct wm831x_status *drvdata; + struct resource *res; + int id = pdev->id % ARRAY_SIZE(chip_pdata->status); + int ret; + + res = platform_get_resource(pdev, IORESOURCE_IO, 0); + if (res == NULL) { + dev_err(&pdev->dev, "No I/O resource\n"); + ret = -EINVAL; + goto err; + } + + drvdata = kzalloc(sizeof(struct wm831x_status), GFP_KERNEL); + if (!drvdata) + return -ENOMEM; + dev_set_drvdata(&pdev->dev, drvdata); + + drvdata->wm831x = wm831x; + drvdata->reg = res->start; + + if (wm831x->dev->platform_data) + chip_pdata = wm831x->dev->platform_data; + else + chip_pdata = NULL; + + memset(&pdata, 0, sizeof(pdata)); + if (chip_pdata && chip_pdata->status[id]) + memcpy(&pdata, chip_pdata->status[id], sizeof(pdata)); + else + pdata.name = dev_name(&pdev->dev); + + mutex_init(&drvdata->mutex); + INIT_WORK(&drvdata->work, wm831x_status_work); + spin_lock_init(&drvdata->value_lock); + + /* We cache the configuration register and read startup values + * from it. */ + drvdata->reg_val = wm831x_reg_read(wm831x, drvdata->reg); + + if (drvdata->reg_val & WM831X_LED_MODE_MASK) + drvdata->brightness = LED_FULL; + else + drvdata->brightness = LED_OFF; + + /* Set a default source if configured, otherwise leave the + * current hardware setting. + */ + if (pdata.default_src == WM831X_STATUS_PRESERVE) { + drvdata->src = drvdata->reg_val; + drvdata->src &= WM831X_LED_SRC_MASK; + drvdata->src >>= WM831X_LED_SRC_SHIFT; + } else { + drvdata->src = pdata.default_src - 1; + } + + drvdata->cdev.name = pdata.name; + drvdata->cdev.default_trigger = pdata.default_trigger; + drvdata->cdev.brightness_set = wm831x_status_set; + drvdata->cdev.blink_set = wm831x_status_blink_set; + + ret = led_classdev_register(wm831x->dev, &drvdata->cdev); + if (ret < 0) { + dev_err(&pdev->dev, "Failed to register LED: %d\n", ret); + goto err_led; + } + + ret = device_create_file(drvdata->cdev.dev, &dev_attr_src); + if (ret != 0) + dev_err(&pdev->dev, + "No source control for LED: %d\n", ret); + + return 0; + +err_led: + led_classdev_unregister(&drvdata->cdev); + kfree(drvdata); +err: + return ret; +} + +static int wm831x_status_remove(struct platform_device *pdev) +{ + struct wm831x_status *drvdata = platform_get_drvdata(pdev); + + device_remove_file(drvdata->cdev.dev, &dev_attr_src); + led_classdev_unregister(&drvdata->cdev); + kfree(drvdata); + + return 0; +} + +static struct platform_driver wm831x_status_driver = { + .driver = { + .name = "wm831x-status", + .owner = THIS_MODULE, + }, + .probe = wm831x_status_probe, + .remove = wm831x_status_remove, +}; + +static int __devinit wm831x_status_init(void) +{ + return platform_driver_register(&wm831x_status_driver); +} +module_init(wm831x_status_init); + +static void wm831x_status_exit(void) +{ + platform_driver_unregister(&wm831x_status_driver); +} +module_exit(wm831x_status_exit); + +MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>"); +MODULE_DESCRIPTION("WM831x status LED driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:wm831x-status"); diff --git a/drivers/leds/ledtrig-gpio.c b/drivers/leds/ledtrig-gpio.c index 1bc5db4ece0d..f5913372d691 100644 --- a/drivers/leds/ledtrig-gpio.c +++ b/drivers/leds/ledtrig-gpio.c @@ -44,22 +44,22 @@ static void gpio_trig_work(struct work_struct *work) struct gpio_trig_data, work); int tmp; - if (!gpio_data->gpio) - return; - - tmp = gpio_get_value(gpio_data->gpio); - if (gpio_data->inverted) - tmp = !tmp; - - if (tmp) { - if (gpio_data->desired_brightness) - led_set_brightness(gpio_data->led, - gpio_data->desired_brightness); - else - led_set_brightness(gpio_data->led, LED_FULL); - } else { - led_set_brightness(gpio_data->led, LED_OFF); - } + if (!gpio_data->gpio) + return; + + tmp = gpio_get_value(gpio_data->gpio); + if (gpio_data->inverted) + tmp = !tmp; + + if (tmp) { + if (gpio_data->desired_brightness) + led_set_brightness(gpio_data->led, + gpio_data->desired_brightness); + else + led_set_brightness(gpio_data->led, LED_FULL); + } else { + led_set_brightness(gpio_data->led, LED_OFF); + } } static ssize_t gpio_trig_brightness_show(struct device *dev, diff --git a/drivers/lguest/core.c b/drivers/lguest/core.c index 1e2cb846b3c9..8744d24ac6e6 100644 --- a/drivers/lguest/core.c +++ b/drivers/lguest/core.c @@ -67,12 +67,11 @@ static __init int map_switcher(void) * so we make sure they're zeroed. */ for (i = 0; i < TOTAL_SWITCHER_PAGES; i++) { - unsigned long addr = get_zeroed_page(GFP_KERNEL); - if (!addr) { + switcher_page[i] = alloc_page(GFP_KERNEL|__GFP_ZERO); + if (!switcher_page[i]) { err = -ENOMEM; goto free_some_pages; } - switcher_page[i] = virt_to_page(addr); } /* diff --git a/drivers/lguest/interrupts_and_traps.c b/drivers/lguest/interrupts_and_traps.c index 18648180db02..daaf86631647 100644 --- a/drivers/lguest/interrupts_and_traps.c +++ b/drivers/lguest/interrupts_and_traps.c @@ -16,6 +16,7 @@ #include <linux/uaccess.h> #include <linux/interrupt.h> #include <linux/module.h> +#include <linux/sched.h> #include "lg.h" /* Allow Guests to use a non-128 (ie. non-Linux) syscall trap. */ diff --git a/drivers/lguest/lguest_user.c b/drivers/lguest/lguest_user.c index b4d3f7ca554f..bd1632388e4a 100644 --- a/drivers/lguest/lguest_user.c +++ b/drivers/lguest/lguest_user.c @@ -508,7 +508,7 @@ static int close(struct inode *inode, struct file *file) * uses: reading and writing a character device called /dev/lguest. All the * work happens in the read(), write() and close() routines: */ -static struct file_operations lguest_fops = { +static const struct file_operations lguest_fops = { .owner = THIS_MODULE, .release = close, .write = write, diff --git a/drivers/lguest/page_tables.c b/drivers/lguest/page_tables.c index 8aaad65c3bb5..cf94326f1b59 100644 --- a/drivers/lguest/page_tables.c +++ b/drivers/lguest/page_tables.c @@ -380,7 +380,7 @@ bool demand_page(struct lg_cpu *cpu, unsigned long vaddr, int errcode) * And we copy the flags to the shadow PMD entry. The page * number in the shadow PMD is the page we just allocated. */ - native_set_pmd(spmd, __pmd(__pa(ptepage) | pmd_flags(gpmd))); + set_pmd(spmd, __pmd(__pa(ptepage) | pmd_flags(gpmd))); } /* @@ -447,7 +447,7 @@ bool demand_page(struct lg_cpu *cpu, unsigned long vaddr, int errcode) * we will come back here when a write does actually occur, so * we can update the Guest's _PAGE_DIRTY flag. */ - native_set_pte(spte, gpte_to_spte(cpu, pte_wrprotect(gpte), 0)); + set_pte(spte, gpte_to_spte(cpu, pte_wrprotect(gpte), 0)); /* * Finally, we write the Guest PTE entry back: we've set the @@ -528,7 +528,7 @@ static void release_pmd(pmd_t *spmd) /* Now we can free the page of PTEs */ free_page((long)ptepage); /* And zero out the PMD entry so we never release it twice. */ - native_set_pmd(spmd, __pmd(0)); + set_pmd(spmd, __pmd(0)); } } @@ -833,15 +833,15 @@ static void do_set_pte(struct lg_cpu *cpu, int idx, */ if (pte_flags(gpte) & (_PAGE_DIRTY | _PAGE_ACCESSED)) { check_gpte(cpu, gpte); - native_set_pte(spte, - gpte_to_spte(cpu, gpte, + set_pte(spte, + gpte_to_spte(cpu, gpte, pte_flags(gpte) & _PAGE_DIRTY)); } else { /* * Otherwise kill it and we can demand_page() * it in later. */ - native_set_pte(spte, __pte(0)); + set_pte(spte, __pte(0)); } #ifdef CONFIG_X86_PAE } @@ -983,25 +983,22 @@ static unsigned long setup_pagetables(struct lguest *lg, */ for (i = j = 0; i < mapped_pages && j < PTRS_PER_PMD; i += PTRS_PER_PTE, j++) { - /* FIXME: native_set_pmd is overkill here. */ - native_set_pmd(&pmd, __pmd(((unsigned long)(linear + i) - - mem_base) | _PAGE_PRESENT | _PAGE_RW | _PAGE_USER)); + pmd = pfn_pmd(((unsigned long)&linear[i] - mem_base)/PAGE_SIZE, + __pgprot(_PAGE_PRESENT | _PAGE_RW | _PAGE_USER)); if (copy_to_user(&pmds[j], &pmd, sizeof(pmd)) != 0) return -EFAULT; } /* One PGD entry, pointing to that PMD page. */ - set_pgd(&pgd, __pgd(((u32)pmds - mem_base) | _PAGE_PRESENT)); + pgd = __pgd(((unsigned long)pmds - mem_base) | _PAGE_PRESENT); /* Copy it in as the first PGD entry (ie. addresses 0-1G). */ if (copy_to_user(&pgdir[0], &pgd, sizeof(pgd)) != 0) return -EFAULT; /* - * And the third PGD entry (ie. addresses 3G-4G). - * - * FIXME: This assumes that PAGE_OFFSET for the Guest is 0xC0000000. + * And the other PGD entry to make the linear mapping at PAGE_OFFSET */ - if (copy_to_user(&pgdir[3], &pgd, sizeof(pgd)) != 0) + if (copy_to_user(&pgdir[KERNEL_PGD_BOUNDARY], &pgd, sizeof(pgd))) return -EFAULT; #else /* @@ -1141,15 +1138,13 @@ void map_switcher_in_guest(struct lg_cpu *cpu, struct lguest_pages *pages) { pte_t *switcher_pte_page = __get_cpu_var(switcher_pte_pages); pte_t regs_pte; - unsigned long pfn; #ifdef CONFIG_X86_PAE pmd_t switcher_pmd; pmd_t *pmd_table; - /* FIXME: native_set_pmd is overkill here. */ - native_set_pmd(&switcher_pmd, pfn_pmd(__pa(switcher_pte_page) >> - PAGE_SHIFT, PAGE_KERNEL_EXEC)); + switcher_pmd = pfn_pmd(__pa(switcher_pte_page) >> PAGE_SHIFT, + PAGE_KERNEL_EXEC); /* Figure out where the pmd page is, by reading the PGD, and converting * it to a virtual address. */ @@ -1157,7 +1152,7 @@ void map_switcher_in_guest(struct lg_cpu *cpu, struct lguest_pages *pages) pgdirs[cpu->cpu_pgd].pgdir[SWITCHER_PGD_INDEX]) << PAGE_SHIFT); /* Now write it into the shadow page table. */ - native_set_pmd(&pmd_table[SWITCHER_PMD_INDEX], switcher_pmd); + set_pmd(&pmd_table[SWITCHER_PMD_INDEX], switcher_pmd); #else pgd_t switcher_pgd; @@ -1179,10 +1174,8 @@ void map_switcher_in_guest(struct lg_cpu *cpu, struct lguest_pages *pages) * page is already mapped there, we don't have to copy them out * again. */ - pfn = __pa(cpu->regs_page) >> PAGE_SHIFT; - native_set_pte(®s_pte, pfn_pte(pfn, PAGE_KERNEL)); - native_set_pte(&switcher_pte_page[pte_index((unsigned long)pages)], - regs_pte); + regs_pte = pfn_pte(__pa(cpu->regs_page) >> PAGE_SHIFT, PAGE_KERNEL); + set_pte(&switcher_pte_page[pte_index((unsigned long)pages)], regs_pte); } /*:*/ @@ -1209,7 +1202,7 @@ static __init void populate_switcher_pte_page(unsigned int cpu, /* The first entries are easy: they map the Switcher code. */ for (i = 0; i < pages; i++) { - native_set_pte(&pte[i], mk_pte(switcher_page[i], + set_pte(&pte[i], mk_pte(switcher_page[i], __pgprot(_PAGE_PRESENT|_PAGE_ACCESSED))); } @@ -1217,14 +1210,14 @@ static __init void populate_switcher_pte_page(unsigned int cpu, i = pages + cpu*2; /* First page (Guest registers) is writable from the Guest */ - native_set_pte(&pte[i], pfn_pte(page_to_pfn(switcher_page[i]), + set_pte(&pte[i], pfn_pte(page_to_pfn(switcher_page[i]), __pgprot(_PAGE_PRESENT|_PAGE_ACCESSED|_PAGE_RW))); /* * The second page contains the "struct lguest_ro_state", and is * read-only. */ - native_set_pte(&pte[i+1], pfn_pte(page_to_pfn(switcher_page[i+1]), + set_pte(&pte[i+1], pfn_pte(page_to_pfn(switcher_page[i+1]), __pgprot(_PAGE_PRESENT|_PAGE_ACCESSED))); } diff --git a/drivers/macintosh/therm_adt746x.c b/drivers/macintosh/therm_adt746x.c index fde377c60cca..556f0feaa4df 100644 --- a/drivers/macintosh/therm_adt746x.c +++ b/drivers/macintosh/therm_adt746x.c @@ -124,6 +124,8 @@ read_reg(struct thermostat* th, int reg) return data; } +static struct i2c_driver thermostat_driver; + static int attach_thermostat(struct i2c_adapter *adapter) { @@ -148,7 +150,7 @@ attach_thermostat(struct i2c_adapter *adapter) * Let i2c-core delete that device on driver removal. * This is safe because i2c-core holds the core_lock mutex for us. */ - list_add_tail(&client->detected, &client->driver->clients); + list_add_tail(&client->detected, &thermostat_driver.clients); return 0; } diff --git a/drivers/macintosh/therm_pm72.c b/drivers/macintosh/therm_pm72.c index a028598af2d3..ea32c7e5a9af 100644 --- a/drivers/macintosh/therm_pm72.c +++ b/drivers/macintosh/therm_pm72.c @@ -286,6 +286,8 @@ struct fcu_fan_table fcu_fans[] = { }, }; +static struct i2c_driver therm_pm72_driver; + /* * Utility function to create an i2c_client structure and * attach it to one of u3 adapters @@ -318,7 +320,7 @@ static struct i2c_client *attach_i2c_chip(int id, const char *name) * Let i2c-core delete that device on driver removal. * This is safe because i2c-core holds the core_lock mutex for us. */ - list_add_tail(&clt->detected, &clt->driver->clients); + list_add_tail(&clt->detected, &therm_pm72_driver.clients); return clt; } diff --git a/drivers/macintosh/via-pmu-led.c b/drivers/macintosh/via-pmu-led.c index 55ad95671387..d242976bcfe7 100644 --- a/drivers/macintosh/via-pmu-led.c +++ b/drivers/macintosh/via-pmu-led.c @@ -72,7 +72,7 @@ static void pmu_led_set(struct led_classdev *led_cdev, } static struct led_classdev pmu_led = { - .name = "pmu-front-led", + .name = "pmu-led::front", #ifdef CONFIG_ADB_PMU_LED_IDE .default_trigger = "ide-disk", #endif diff --git a/drivers/macintosh/via-pmu.c b/drivers/macintosh/via-pmu.c index b40fb9b6c862..6f308a4757ee 100644 --- a/drivers/macintosh/via-pmu.c +++ b/drivers/macintosh/via-pmu.c @@ -405,7 +405,11 @@ static int __init via_pmu_start(void) printk(KERN_ERR "via-pmu: can't map interrupt\n"); return -ENODEV; } - if (request_irq(irq, via_pmu_interrupt, 0, "VIA-PMU", (void *)0)) { + /* We set IRQF_TIMER because we don't want the interrupt to be disabled + * between the 2 passes of driver suspend, we control our own disabling + * for that one + */ + if (request_irq(irq, via_pmu_interrupt, IRQF_TIMER, "VIA-PMU", (void *)0)) { printk(KERN_ERR "via-pmu: can't request irq %d\n", irq); return -ENODEV; } @@ -419,7 +423,7 @@ static int __init via_pmu_start(void) gpio_irq = irq_of_parse_and_map(gpio_node, 0); if (gpio_irq != NO_IRQ) { - if (request_irq(gpio_irq, gpio1_interrupt, 0, + if (request_irq(gpio_irq, gpio1_interrupt, IRQF_TIMER, "GPIO1 ADB", (void *)0)) printk(KERN_ERR "pmu: can't get irq %d" " (GPIO1)\n", gpio_irq); @@ -925,8 +929,7 @@ proc_write_options(struct file *file, const char __user *buffer, #ifdef CONFIG_ADB /* Send an ADB command */ -static int -pmu_send_request(struct adb_request *req, int sync) +static int pmu_send_request(struct adb_request *req, int sync) { int i, ret; @@ -1005,16 +1008,11 @@ pmu_send_request(struct adb_request *req, int sync) } /* Enable/disable autopolling */ -static int -pmu_adb_autopoll(int devs) +static int __pmu_adb_autopoll(int devs) { struct adb_request req; - if ((vias == NULL) || (!pmu_fully_inited) || !pmu_has_adb) - return -ENXIO; - if (devs) { - adb_dev_map = devs; pmu_request(&req, NULL, 5, PMU_ADB_CMD, 0, 0x86, adb_dev_map >> 8, adb_dev_map); pmu_adb_flags = 2; @@ -1027,9 +1025,17 @@ pmu_adb_autopoll(int devs) return 0; } +static int pmu_adb_autopoll(int devs) +{ + if ((vias == NULL) || (!pmu_fully_inited) || !pmu_has_adb) + return -ENXIO; + + adb_dev_map = devs; + return __pmu_adb_autopoll(devs); +} + /* Reset the ADB bus */ -static int -pmu_adb_reset_bus(void) +static int pmu_adb_reset_bus(void) { struct adb_request req; int save_autopoll = adb_dev_map; @@ -1038,13 +1044,13 @@ pmu_adb_reset_bus(void) return -ENXIO; /* anyone got a better idea?? */ - pmu_adb_autopoll(0); + __pmu_adb_autopoll(0); - req.nbytes = 5; + req.nbytes = 4; req.done = NULL; req.data[0] = PMU_ADB_CMD; - req.data[1] = 0; - req.data[2] = ADB_BUSRESET; + req.data[1] = ADB_BUSRESET; + req.data[2] = 0; req.data[3] = 0; req.data[4] = 0; req.reply_len = 0; @@ -1056,7 +1062,7 @@ pmu_adb_reset_bus(void) pmu_wait_complete(&req); if (save_autopoll != 0) - pmu_adb_autopoll(save_autopoll); + __pmu_adb_autopoll(save_autopoll); return 0; } diff --git a/drivers/macintosh/windfarm_lm75_sensor.c b/drivers/macintosh/windfarm_lm75_sensor.c index 529886c7a826..ed6426a10773 100644 --- a/drivers/macintosh/windfarm_lm75_sensor.c +++ b/drivers/macintosh/windfarm_lm75_sensor.c @@ -115,6 +115,8 @@ static int wf_lm75_probe(struct i2c_client *client, return rc; } +static struct i2c_driver wf_lm75_driver; + static struct i2c_client *wf_lm75_create(struct i2c_adapter *adapter, u8 addr, int ds1775, const char *loc) @@ -157,7 +159,7 @@ static struct i2c_client *wf_lm75_create(struct i2c_adapter *adapter, * Let i2c-core delete that device on driver removal. * This is safe because i2c-core holds the core_lock mutex for us. */ - list_add_tail(&client->detected, &client->driver->clients); + list_add_tail(&client->detected, &wf_lm75_driver.clients); return client; fail: return NULL; diff --git a/drivers/macintosh/windfarm_max6690_sensor.c b/drivers/macintosh/windfarm_max6690_sensor.c index e2a55ecda2b2..a67b349319e9 100644 --- a/drivers/macintosh/windfarm_max6690_sensor.c +++ b/drivers/macintosh/windfarm_max6690_sensor.c @@ -88,6 +88,8 @@ static int wf_max6690_probe(struct i2c_client *client, return rc; } +static struct i2c_driver wf_max6690_driver; + static struct i2c_client *wf_max6690_create(struct i2c_adapter *adapter, u8 addr, const char *loc) { @@ -119,7 +121,7 @@ static struct i2c_client *wf_max6690_create(struct i2c_adapter *adapter, * Let i2c-core delete that device on driver removal. * This is safe because i2c-core holds the core_lock mutex for us. */ - list_add_tail(&client->detected, &client->driver->clients); + list_add_tail(&client->detected, &wf_max6690_driver.clients); return client; fail: diff --git a/drivers/macintosh/windfarm_smu_sat.c b/drivers/macintosh/windfarm_smu_sat.c index 5da729e58f99..e20330a28959 100644 --- a/drivers/macintosh/windfarm_smu_sat.c +++ b/drivers/macintosh/windfarm_smu_sat.c @@ -194,6 +194,8 @@ static struct wf_sensor_ops wf_sat_ops = { .owner = THIS_MODULE, }; +static struct i2c_driver wf_sat_driver; + static void wf_sat_create(struct i2c_adapter *adapter, struct device_node *dev) { struct i2c_board_info info; @@ -222,7 +224,7 @@ static void wf_sat_create(struct i2c_adapter *adapter, struct device_node *dev) * Let i2c-core delete that device on driver removal. * This is safe because i2c-core holds the core_lock mutex for us. */ - list_add_tail(&client->detected, &client->driver->clients); + list_add_tail(&client->detected, &wf_sat_driver.clients); } static int wf_sat_probe(struct i2c_client *client, diff --git a/drivers/md/Kconfig b/drivers/md/Kconfig index 020f9573fd82..2158377a1359 100644 --- a/drivers/md/Kconfig +++ b/drivers/md/Kconfig @@ -124,6 +124,8 @@ config MD_RAID456 select MD_RAID6_PQ select ASYNC_MEMCPY select ASYNC_XOR + select ASYNC_PQ + select ASYNC_RAID6_RECOV ---help--- A RAID-5 set of N drives with a capacity of C MB per drive provides the capacity of C * (N - 1) MB, and protects against a failure @@ -152,9 +154,33 @@ config MD_RAID456 If unsure, say Y. +config MULTICORE_RAID456 + bool "RAID-4/RAID-5/RAID-6 Multicore processing (EXPERIMENTAL)" + depends on MD_RAID456 + depends on SMP + depends on EXPERIMENTAL + ---help--- + Enable the raid456 module to dispatch per-stripe raid operations to a + thread pool. + + If unsure, say N. + config MD_RAID6_PQ tristate +config ASYNC_RAID6_TEST + tristate "Self test for hardware accelerated raid6 recovery" + depends on MD_RAID6_PQ + select ASYNC_RAID6_RECOV + ---help--- + This is a one-shot self test that permutes through the + recovery of all the possible two disk failure scenarios for a + N-disk array. Recovery is performed with the asynchronous + raid6 recovery routines, and will optionally use an offload + engine if one is available. + + If unsure, say N. + config MD_MULTIPATH tristate "Multipath I/O support" depends on BLK_DEV_MD diff --git a/drivers/md/Makefile b/drivers/md/Makefile index 1dc4185bd781..e355e7f6a536 100644 --- a/drivers/md/Makefile +++ b/drivers/md/Makefile @@ -46,7 +46,7 @@ obj-$(CONFIG_DM_LOG_USERSPACE) += dm-log-userspace.o obj-$(CONFIG_DM_ZERO) += dm-zero.o quiet_cmd_unroll = UNROLL $@ - cmd_unroll = $(PERL) $(srctree)/$(src)/unroll.pl $(UNROLL) \ + cmd_unroll = $(AWK) -f$(srctree)/$(src)/unroll.awk -vN=$(UNROLL) \ < $< > $@ || ( rm -f $@ && exit 1 ) ifeq ($(CONFIG_ALTIVEC),y) @@ -59,56 +59,56 @@ endif targets += raid6int1.c $(obj)/raid6int1.c: UNROLL := 1 -$(obj)/raid6int1.c: $(src)/raid6int.uc $(src)/unroll.pl FORCE +$(obj)/raid6int1.c: $(src)/raid6int.uc $(src)/unroll.awk FORCE $(call if_changed,unroll) targets += raid6int2.c $(obj)/raid6int2.c: UNROLL := 2 -$(obj)/raid6int2.c: $(src)/raid6int.uc $(src)/unroll.pl FORCE +$(obj)/raid6int2.c: $(src)/raid6int.uc $(src)/unroll.awk FORCE $(call if_changed,unroll) targets += raid6int4.c $(obj)/raid6int4.c: UNROLL := 4 -$(obj)/raid6int4.c: $(src)/raid6int.uc $(src)/unroll.pl FORCE +$(obj)/raid6int4.c: $(src)/raid6int.uc $(src)/unroll.awk FORCE $(call if_changed,unroll) targets += raid6int8.c $(obj)/raid6int8.c: UNROLL := 8 -$(obj)/raid6int8.c: $(src)/raid6int.uc $(src)/unroll.pl FORCE +$(obj)/raid6int8.c: $(src)/raid6int.uc $(src)/unroll.awk FORCE $(call if_changed,unroll) targets += raid6int16.c $(obj)/raid6int16.c: UNROLL := 16 -$(obj)/raid6int16.c: $(src)/raid6int.uc $(src)/unroll.pl FORCE +$(obj)/raid6int16.c: $(src)/raid6int.uc $(src)/unroll.awk FORCE $(call if_changed,unroll) targets += raid6int32.c $(obj)/raid6int32.c: UNROLL := 32 -$(obj)/raid6int32.c: $(src)/raid6int.uc $(src)/unroll.pl FORCE +$(obj)/raid6int32.c: $(src)/raid6int.uc $(src)/unroll.awk FORCE $(call if_changed,unroll) CFLAGS_raid6altivec1.o += $(altivec_flags) targets += raid6altivec1.c $(obj)/raid6altivec1.c: UNROLL := 1 -$(obj)/raid6altivec1.c: $(src)/raid6altivec.uc $(src)/unroll.pl FORCE +$(obj)/raid6altivec1.c: $(src)/raid6altivec.uc $(src)/unroll.awk FORCE $(call if_changed,unroll) CFLAGS_raid6altivec2.o += $(altivec_flags) targets += raid6altivec2.c $(obj)/raid6altivec2.c: UNROLL := 2 -$(obj)/raid6altivec2.c: $(src)/raid6altivec.uc $(src)/unroll.pl FORCE +$(obj)/raid6altivec2.c: $(src)/raid6altivec.uc $(src)/unroll.awk FORCE $(call if_changed,unroll) CFLAGS_raid6altivec4.o += $(altivec_flags) targets += raid6altivec4.c $(obj)/raid6altivec4.c: UNROLL := 4 -$(obj)/raid6altivec4.c: $(src)/raid6altivec.uc $(src)/unroll.pl FORCE +$(obj)/raid6altivec4.c: $(src)/raid6altivec.uc $(src)/unroll.awk FORCE $(call if_changed,unroll) CFLAGS_raid6altivec8.o += $(altivec_flags) targets += raid6altivec8.c $(obj)/raid6altivec8.c: UNROLL := 8 -$(obj)/raid6altivec8.c: $(src)/raid6altivec.uc $(src)/unroll.pl FORCE +$(obj)/raid6altivec8.c: $(src)/raid6altivec.uc $(src)/unroll.awk FORCE $(call if_changed,unroll) quiet_cmd_mktable = TABLE $@ diff --git a/drivers/md/bitmap.c b/drivers/md/bitmap.c index 3319c2fec28e..60e2b322db11 100644 --- a/drivers/md/bitmap.c +++ b/drivers/md/bitmap.c @@ -108,6 +108,8 @@ static void bitmap_free_page(struct bitmap *bitmap, unsigned char *page) * allocated while we're using it */ static int bitmap_checkpage(struct bitmap *bitmap, unsigned long page, int create) +__releases(bitmap->lock) +__acquires(bitmap->lock) { unsigned char *mappage; @@ -325,7 +327,6 @@ static int write_sb_page(struct bitmap *bitmap, struct page *page, int wait) return 0; bad_alignment: - rcu_read_unlock(); return -EINVAL; } @@ -1207,6 +1208,8 @@ void bitmap_daemon_work(struct bitmap *bitmap) static bitmap_counter_t *bitmap_get_counter(struct bitmap *bitmap, sector_t offset, int *blocks, int create) +__releases(bitmap->lock) +__acquires(bitmap->lock) { /* If 'create', we might release the lock and reclaim it. * The lock must have been taken with interrupts enabled. @@ -1621,10 +1624,11 @@ int bitmap_create(mddev_t *mddev) bitmap->offset = mddev->bitmap_offset; if (file) { get_file(file); - do_sync_mapping_range(file->f_mapping, 0, LLONG_MAX, - SYNC_FILE_RANGE_WAIT_BEFORE | - SYNC_FILE_RANGE_WRITE | - SYNC_FILE_RANGE_WAIT_AFTER); + /* As future accesses to this file will use bmap, + * and bypass the page cache, we must sync the file + * first. + */ + vfs_fsync(file, file->f_dentry, 1); } /* read superblock from bitmap file (this sets bitmap->chunksize) */ err = bitmap_read_sb(bitmap); diff --git a/drivers/md/dm-exception-store.c b/drivers/md/dm-exception-store.c index 556acff3952f..7dbe652efb5a 100644 --- a/drivers/md/dm-exception-store.c +++ b/drivers/md/dm-exception-store.c @@ -138,16 +138,6 @@ int dm_exception_store_type_unregister(struct dm_exception_store_type *type) } EXPORT_SYMBOL(dm_exception_store_type_unregister); -/* - * Round a number up to the nearest 'size' boundary. size must - * be a power of 2. - */ -static ulong round_up(ulong n, ulong size) -{ - size--; - return (n + size) & ~size; -} - static int set_chunk_size(struct dm_exception_store *store, const char *chunk_size_arg, char **error) { @@ -155,7 +145,8 @@ static int set_chunk_size(struct dm_exception_store *store, char *value; chunk_size_ulong = simple_strtoul(chunk_size_arg, &value, 10); - if (*chunk_size_arg == '\0' || *value != '\0') { + if (*chunk_size_arg == '\0' || *value != '\0' || + chunk_size_ulong > UINT_MAX) { *error = "Invalid chunk size"; return -EINVAL; } @@ -165,40 +156,35 @@ static int set_chunk_size(struct dm_exception_store *store, return 0; } - /* - * Chunk size must be multiple of page size. Silently - * round up if it's not. - */ - chunk_size_ulong = round_up(chunk_size_ulong, PAGE_SIZE >> 9); - - return dm_exception_store_set_chunk_size(store, chunk_size_ulong, + return dm_exception_store_set_chunk_size(store, + (unsigned) chunk_size_ulong, error); } int dm_exception_store_set_chunk_size(struct dm_exception_store *store, - unsigned long chunk_size_ulong, + unsigned chunk_size, char **error) { /* Check chunk_size is a power of 2 */ - if (!is_power_of_2(chunk_size_ulong)) { + if (!is_power_of_2(chunk_size)) { *error = "Chunk size is not a power of 2"; return -EINVAL; } /* Validate the chunk size against the device block size */ - if (chunk_size_ulong % (bdev_logical_block_size(store->cow->bdev) >> 9)) { + if (chunk_size % (bdev_logical_block_size(store->cow->bdev) >> 9)) { *error = "Chunk size is not a multiple of device blocksize"; return -EINVAL; } - if (chunk_size_ulong > INT_MAX >> SECTOR_SHIFT) { + if (chunk_size > INT_MAX >> SECTOR_SHIFT) { *error = "Chunk size is too high"; return -EINVAL; } - store->chunk_size = chunk_size_ulong; - store->chunk_mask = chunk_size_ulong - 1; - store->chunk_shift = ffs(chunk_size_ulong) - 1; + store->chunk_size = chunk_size; + store->chunk_mask = chunk_size - 1; + store->chunk_shift = ffs(chunk_size) - 1; return 0; } @@ -251,7 +237,7 @@ int dm_exception_store_create(struct dm_target *ti, int argc, char **argv, r = set_chunk_size(tmp_store, argv[2], &ti->error); if (r) - goto bad_cow; + goto bad_ctr; r = type->ctr(tmp_store, 0, NULL); if (r) { diff --git a/drivers/md/dm-exception-store.h b/drivers/md/dm-exception-store.h index 812c71872ba0..8a223a48802c 100644 --- a/drivers/md/dm-exception-store.h +++ b/drivers/md/dm-exception-store.h @@ -101,9 +101,9 @@ struct dm_exception_store { struct dm_dev *cow; /* Size of data blocks saved - must be a power of 2 */ - chunk_t chunk_size; - chunk_t chunk_mask; - chunk_t chunk_shift; + unsigned chunk_size; + unsigned chunk_mask; + unsigned chunk_shift; void *context; }; @@ -169,7 +169,7 @@ int dm_exception_store_type_register(struct dm_exception_store_type *type); int dm_exception_store_type_unregister(struct dm_exception_store_type *type); int dm_exception_store_set_chunk_size(struct dm_exception_store *store, - unsigned long chunk_size_ulong, + unsigned chunk_size, char **error); int dm_exception_store_create(struct dm_target *ti, int argc, char **argv, diff --git a/drivers/md/dm-log-userspace-base.c b/drivers/md/dm-log-userspace-base.c index 652bd33109e3..7ac2c1450d10 100644 --- a/drivers/md/dm-log-userspace-base.c +++ b/drivers/md/dm-log-userspace-base.c @@ -156,7 +156,7 @@ static int userspace_ctr(struct dm_dirty_log *log, struct dm_target *ti, } /* The ptr value is sufficient for local unique id */ - lc->luid = (uint64_t)lc; + lc->luid = (unsigned long)lc; lc->ti = ti; diff --git a/drivers/md/dm-log-userspace-transfer.c b/drivers/md/dm-log-userspace-transfer.c index ba0edad2d048..54abf9e303b7 100644 --- a/drivers/md/dm-log-userspace-transfer.c +++ b/drivers/md/dm-log-userspace-transfer.c @@ -129,11 +129,13 @@ static int fill_pkg(struct cn_msg *msg, struct dm_ulog_request *tfr) * This is the connector callback that delivers data * that was sent from userspace. */ -static void cn_ulog_callback(void *data) +static void cn_ulog_callback(struct cn_msg *msg, struct netlink_skb_parms *nsp) { - struct cn_msg *msg = (struct cn_msg *)data; struct dm_ulog_request *tfr = (struct dm_ulog_request *)(msg + 1); + if (!cap_raised(nsp->eff_cap, CAP_SYS_ADMIN)) + return; + spin_lock(&receiving_list_lock); if (msg->len == 0) fill_pkg(msg, NULL); diff --git a/drivers/md/dm-snap-persistent.c b/drivers/md/dm-snap-persistent.c index d5b2e08750d5..0c746420c008 100644 --- a/drivers/md/dm-snap-persistent.c +++ b/drivers/md/dm-snap-persistent.c @@ -284,12 +284,13 @@ static int read_header(struct pstore *ps, int *new_snapshot) { int r; struct disk_header *dh; - chunk_t chunk_size; + unsigned chunk_size; int chunk_size_supplied = 1; char *chunk_err; /* - * Use default chunk size (or hardsect_size, if larger) if none supplied + * Use default chunk size (or logical_block_size, if larger) + * if none supplied */ if (!ps->store->chunk_size) { ps->store->chunk_size = max(DM_CHUNK_SIZE_DEFAULT_SECTORS, @@ -334,10 +335,9 @@ static int read_header(struct pstore *ps, int *new_snapshot) return 0; if (chunk_size_supplied) - DMWARN("chunk size %llu in device metadata overrides " - "table chunk size of %llu.", - (unsigned long long)chunk_size, - (unsigned long long)ps->store->chunk_size); + DMWARN("chunk size %u in device metadata overrides " + "table chunk size of %u.", + chunk_size, ps->store->chunk_size); /* We had a bogus chunk_size. Fix stuff up. */ free_area(ps); @@ -345,8 +345,8 @@ static int read_header(struct pstore *ps, int *new_snapshot) r = dm_exception_store_set_chunk_size(ps->store, chunk_size, &chunk_err); if (r) { - DMERR("invalid on-disk chunk size %llu: %s.", - (unsigned long long)chunk_size, chunk_err); + DMERR("invalid on-disk chunk size %u: %s.", + chunk_size, chunk_err); return r; } diff --git a/drivers/md/dm-snap.c b/drivers/md/dm-snap.c index 57f1bf7f3b7a..3a3ba46e6d4b 100644 --- a/drivers/md/dm-snap.c +++ b/drivers/md/dm-snap.c @@ -296,6 +296,7 @@ static void __insert_origin(struct origin *o) */ static int register_snapshot(struct dm_snapshot *snap) { + struct dm_snapshot *l; struct origin *o, *new_o; struct block_device *bdev = snap->origin->bdev; @@ -319,7 +320,11 @@ static int register_snapshot(struct dm_snapshot *snap) __insert_origin(o); } - list_add_tail(&snap->list, &o->snapshots); + /* Sort the list according to chunk size, largest-first smallest-last */ + list_for_each_entry(l, &o->snapshots, list) + if (l->store->chunk_size < snap->store->chunk_size) + break; + list_add_tail(&snap->list, &l->list); up_write(&_origins_lock); return 0; @@ -668,6 +673,11 @@ static int snapshot_ctr(struct dm_target *ti, unsigned int argc, char **argv) bio_list_init(&s->queued_bios); INIT_WORK(&s->queued_bios_work, flush_queued_bios); + if (!s->store->chunk_size) { + ti->error = "Chunk size not set"; + goto bad_load_and_register; + } + /* Add snapshot to the list of snapshots for this origin */ /* Exceptions aren't triggered till snapshot_resume() is called */ if (register_snapshot(s)) { @@ -951,7 +961,7 @@ static void start_copy(struct dm_snap_pending_exception *pe) src.bdev = bdev; src.sector = chunk_to_sector(s->store, pe->e.old_chunk); - src.count = min(s->store->chunk_size, dev_size - src.sector); + src.count = min((sector_t)s->store->chunk_size, dev_size - src.sector); dest.bdev = s->store->cow->bdev; dest.sector = chunk_to_sector(s->store, pe->e.new_chunk); @@ -1142,6 +1152,8 @@ static int snapshot_status(struct dm_target *ti, status_type_t type, unsigned sz = 0; struct dm_snapshot *snap = ti->private; + down_write(&snap->lock); + switch (type) { case STATUSTYPE_INFO: if (!snap->valid) @@ -1173,6 +1185,8 @@ static int snapshot_status(struct dm_target *ti, status_type_t type, break; } + up_write(&snap->lock); + return 0; } @@ -1388,7 +1402,7 @@ static void origin_resume(struct dm_target *ti) struct dm_dev *dev = ti->private; struct dm_snapshot *snap; struct origin *o; - chunk_t chunk_size = 0; + unsigned chunk_size = 0; down_read(&_origins_lock); o = __lookup_origin(dev->bdev); @@ -1465,7 +1479,7 @@ static int __init dm_snapshot_init(void) r = dm_register_target(&snapshot_target); if (r) { DMERR("snapshot target register failed %d", r); - return r; + goto bad_register_snapshot_target; } r = dm_register_target(&origin_target); @@ -1522,6 +1536,9 @@ bad2: dm_unregister_target(&origin_target); bad1: dm_unregister_target(&snapshot_target); + +bad_register_snapshot_target: + dm_exception_store_exit(); return r; } diff --git a/drivers/md/dm.c b/drivers/md/dm.c index 376f1ab48a24..724efc63904d 100644 --- a/drivers/md/dm.c +++ b/drivers/md/dm.c @@ -47,6 +47,7 @@ struct dm_io { atomic_t io_count; struct bio *bio; unsigned long start_time; + spinlock_t endio_lock; }; /* @@ -578,8 +579,12 @@ static void dec_pending(struct dm_io *io, int error) struct mapped_device *md = io->md; /* Push-back supersedes any I/O errors */ - if (error && !(io->error > 0 && __noflush_suspending(md))) - io->error = error; + if (unlikely(error)) { + spin_lock_irqsave(&io->endio_lock, flags); + if (!(io->error > 0 && __noflush_suspending(md))) + io->error = error; + spin_unlock_irqrestore(&io->endio_lock, flags); + } if (atomic_dec_and_test(&io->io_count)) { if (io->error == DM_ENDIO_REQUEUE) { @@ -1226,6 +1231,7 @@ static void __split_and_process_bio(struct mapped_device *md, struct bio *bio) atomic_set(&ci.io->io_count, 1); ci.io->bio = bio; ci.io->md = md; + spin_lock_init(&ci.io->endio_lock); ci.sector = bio->bi_sector; ci.sector_count = bio_sectors(bio); if (unlikely(bio_empty_barrier(bio))) @@ -1822,6 +1828,7 @@ static struct mapped_device *alloc_dev(int minor) bad_bdev: destroy_workqueue(md->wq); bad_thread: + del_gendisk(md->disk); put_disk(md->disk); bad_disk: blk_cleanup_queue(md->queue); diff --git a/drivers/md/linear.c b/drivers/md/linear.c index ea4842905444..1ceceb334d5e 100644 --- a/drivers/md/linear.c +++ b/drivers/md/linear.c @@ -108,6 +108,9 @@ static int linear_congested(void *data, int bits) linear_conf_t *conf; int i, ret = 0; + if (mddev_congested(mddev, bits)) + return 1; + rcu_read_lock(); conf = rcu_dereference(mddev->private); diff --git a/drivers/md/md.c b/drivers/md/md.c index 6aa497e4baf8..b182f86a19dd 100644 --- a/drivers/md/md.c +++ b/drivers/md/md.c @@ -262,6 +262,12 @@ static void mddev_resume(mddev_t *mddev) mddev->pers->quiesce(mddev, 0); } +int mddev_congested(mddev_t *mddev, int bits) +{ + return mddev->suspended; +} +EXPORT_SYMBOL(mddev_congested); + static inline mddev_t *mddev_get(mddev_t *mddev) { @@ -938,6 +944,14 @@ static int super_90_validate(mddev_t *mddev, mdk_rdev_t *rdev) desc->raid_disk < mddev->raid_disks */) { set_bit(In_sync, &rdev->flags); rdev->raid_disk = desc->raid_disk; + } else if (desc->state & (1<<MD_DISK_ACTIVE)) { + /* active but not in sync implies recovery up to + * reshape position. We don't know exactly where + * that is, so set to zero for now */ + if (mddev->minor_version >= 91) { + rdev->recovery_offset = 0; + rdev->raid_disk = desc->raid_disk; + } } if (desc->state & (1<<MD_DISK_WRITEMOSTLY)) set_bit(WriteMostly, &rdev->flags); @@ -1026,8 +1040,19 @@ static void super_90_sync(mddev_t *mddev, mdk_rdev_t *rdev) list_for_each_entry(rdev2, &mddev->disks, same_set) { mdp_disk_t *d; int desc_nr; - if (rdev2->raid_disk >= 0 && test_bit(In_sync, &rdev2->flags) - && !test_bit(Faulty, &rdev2->flags)) + int is_active = test_bit(In_sync, &rdev2->flags); + + if (rdev2->raid_disk >= 0 && + sb->minor_version >= 91) + /* we have nowhere to store the recovery_offset, + * but if it is not below the reshape_position, + * we can piggy-back on that. + */ + is_active = 1; + if (rdev2->raid_disk < 0 || + test_bit(Faulty, &rdev2->flags)) + is_active = 0; + if (is_active) desc_nr = rdev2->raid_disk; else desc_nr = next_spare++; @@ -1037,16 +1062,16 @@ static void super_90_sync(mddev_t *mddev, mdk_rdev_t *rdev) d->number = rdev2->desc_nr; d->major = MAJOR(rdev2->bdev->bd_dev); d->minor = MINOR(rdev2->bdev->bd_dev); - if (rdev2->raid_disk >= 0 && test_bit(In_sync, &rdev2->flags) - && !test_bit(Faulty, &rdev2->flags)) + if (is_active) d->raid_disk = rdev2->raid_disk; else d->raid_disk = rdev2->desc_nr; /* compatibility */ if (test_bit(Faulty, &rdev2->flags)) d->state = (1<<MD_DISK_FAULTY); - else if (test_bit(In_sync, &rdev2->flags)) { + else if (is_active) { d->state = (1<<MD_DISK_ACTIVE); - d->state |= (1<<MD_DISK_SYNC); + if (test_bit(In_sync, &rdev2->flags)) + d->state |= (1<<MD_DISK_SYNC); active++; working++; } else { @@ -1376,8 +1401,6 @@ static void super_1_sync(mddev_t *mddev, mdk_rdev_t *rdev) if (rdev->raid_disk >= 0 && !test_bit(In_sync, &rdev->flags)) { - if (mddev->curr_resync_completed > rdev->recovery_offset) - rdev->recovery_offset = mddev->curr_resync_completed; if (rdev->recovery_offset > 0) { sb->feature_map |= cpu_to_le32(MD_FEATURE_RECOVERY_OFFSET); @@ -1911,6 +1934,14 @@ static void sync_sbs(mddev_t * mddev, int nospares) */ mdk_rdev_t *rdev; + /* First make sure individual recovery_offsets are correct */ + list_for_each_entry(rdev, &mddev->disks, same_set) { + if (rdev->raid_disk >= 0 && + !test_bit(In_sync, &rdev->flags) && + mddev->curr_resync_completed > rdev->recovery_offset) + rdev->recovery_offset = mddev->curr_resync_completed; + + } list_for_each_entry(rdev, &mddev->disks, same_set) { if (rdev->sb_events == mddev->events || (nospares && @@ -2625,7 +2656,7 @@ static void analyze_sbs(mddev_t * mddev) rdev->desc_nr = i++; rdev->raid_disk = rdev->desc_nr; set_bit(In_sync, &rdev->flags); - } else if (rdev->raid_disk >= mddev->raid_disks) { + } else if (rdev->raid_disk >= (mddev->raid_disks - min(0, mddev->delta_disks))) { rdev->raid_disk = -1; clear_bit(In_sync, &rdev->flags); } @@ -4218,7 +4249,7 @@ static int do_md_run(mddev_t * mddev) set_bit(MD_RECOVERY_RUNNING, &mddev->recovery); mddev->sync_thread = md_register_thread(md_do_sync, mddev, - "%s_resync"); + "resync"); if (!mddev->sync_thread) { printk(KERN_ERR "%s: could not start resync" " thread...\n", @@ -4575,10 +4606,10 @@ static int get_version(void __user * arg) static int get_array_info(mddev_t * mddev, void __user * arg) { mdu_array_info_t info; - int nr,working,active,failed,spare; + int nr,working,insync,failed,spare; mdk_rdev_t *rdev; - nr=working=active=failed=spare=0; + nr=working=insync=failed=spare=0; list_for_each_entry(rdev, &mddev->disks, same_set) { nr++; if (test_bit(Faulty, &rdev->flags)) @@ -4586,7 +4617,7 @@ static int get_array_info(mddev_t * mddev, void __user * arg) else { working++; if (test_bit(In_sync, &rdev->flags)) - active++; + insync++; else spare++; } @@ -4611,7 +4642,7 @@ static int get_array_info(mddev_t * mddev, void __user * arg) info.state = (1<<MD_SB_CLEAN); if (mddev->bitmap && mddev->bitmap_offset) info.state = (1<<MD_SB_BITMAP_PRESENT); - info.active_disks = active; + info.active_disks = insync; info.working_disks = working; info.failed_disks = failed; info.spare_disks = spare; @@ -4721,7 +4752,7 @@ static int add_new_disk(mddev_t * mddev, mdu_disk_info_t *info) if (!list_empty(&mddev->disks)) { mdk_rdev_t *rdev0 = list_entry(mddev->disks.next, mdk_rdev_t, same_set); - int err = super_types[mddev->major_version] + err = super_types[mddev->major_version] .load_super(rdev, rdev0, mddev->minor_version); if (err < 0) { printk(KERN_WARNING @@ -5631,7 +5662,10 @@ mdk_thread_t *md_register_thread(void (*run) (mddev_t *), mddev_t *mddev, thread->run = run; thread->mddev = mddev; thread->timeout = MAX_SCHEDULE_TIMEOUT; - thread->tsk = kthread_run(md_thread, thread, name, mdname(thread->mddev)); + thread->tsk = kthread_run(md_thread, thread, + "%s_%s", + mdname(thread->mddev), + name ?: mddev->pers->name); if (IS_ERR(thread->tsk)) { kfree(thread); return NULL; @@ -6495,8 +6529,9 @@ void md_do_sync(mddev_t *mddev) skip: mddev->curr_resync = 0; mddev->curr_resync_completed = 0; - mddev->resync_min = 0; - mddev->resync_max = MaxSector; + if (!test_bit(MD_RECOVERY_INTR, &mddev->recovery)) + /* We completed so max setting can be forgotten. */ + mddev->resync_max = MaxSector; sysfs_notify(&mddev->kobj, NULL, "sync_completed"); wake_up(&resync_wait); set_bit(MD_RECOVERY_DONE, &mddev->recovery); @@ -6745,7 +6780,7 @@ void md_check_recovery(mddev_t *mddev) } mddev->sync_thread = md_register_thread(md_do_sync, mddev, - "%s_resync"); + "resync"); if (!mddev->sync_thread) { printk(KERN_ERR "%s: could not start resync" " thread...\n", diff --git a/drivers/md/md.h b/drivers/md/md.h index f55d2ff95133..f184b69ef337 100644 --- a/drivers/md/md.h +++ b/drivers/md/md.h @@ -430,6 +430,7 @@ extern void md_write_end(mddev_t *mddev); extern void md_done_sync(mddev_t *mddev, int blocks, int ok); extern void md_error(mddev_t *mddev, mdk_rdev_t *rdev); +extern int mddev_congested(mddev_t *mddev, int bits); extern void md_super_write(mddev_t *mddev, mdk_rdev_t *rdev, sector_t sector, int size, struct page *page); extern void md_super_wait(mddev_t *mddev); diff --git a/drivers/md/multipath.c b/drivers/md/multipath.c index d2d3fd54cc68..ee7646f974a0 100644 --- a/drivers/md/multipath.c +++ b/drivers/md/multipath.c @@ -150,7 +150,6 @@ static int multipath_make_request (struct request_queue *q, struct bio * bio) } mp_bh = mempool_alloc(conf->pool, GFP_NOIO); - memset(mp_bh, 0, sizeof(*mp_bh)); mp_bh->master_bio = bio; mp_bh->mddev = mddev; @@ -199,6 +198,9 @@ static int multipath_congested(void *data, int bits) multipath_conf_t *conf = mddev->private; int i, ret = 0; + if (mddev_congested(mddev, bits)) + return 1; + rcu_read_lock(); for (i = 0; i < mddev->raid_disks ; i++) { mdk_rdev_t *rdev = rcu_dereference(conf->multipaths[i].rdev); @@ -504,7 +506,7 @@ static int multipath_run (mddev_t *mddev) } { - mddev->thread = md_register_thread(multipathd, mddev, "%s_multipath"); + mddev->thread = md_register_thread(multipathd, mddev, NULL); if (!mddev->thread) { printk(KERN_ERR "multipath: couldn't allocate thread" " for %s\n", mdname(mddev)); diff --git a/drivers/md/raid0.c b/drivers/md/raid0.c index f845ed98fec9..d3a4ce06015a 100644 --- a/drivers/md/raid0.c +++ b/drivers/md/raid0.c @@ -44,6 +44,9 @@ static int raid0_congested(void *data, int bits) mdk_rdev_t **devlist = conf->devlist; int i, ret = 0; + if (mddev_congested(mddev, bits)) + return 1; + for (i = 0; i < mddev->raid_disks && !ret ; i++) { struct request_queue *q = bdev_get_queue(devlist[i]->bdev); @@ -86,7 +89,7 @@ static void dump_zones(mddev_t *mddev) static int create_strip_zones(mddev_t *mddev) { - int i, c, j, err; + int i, c, err; sector_t curr_zone_end, sectors; mdk_rdev_t *smallest, *rdev1, *rdev2, *rdev, **dev; struct strip_zone *zone; @@ -198,6 +201,8 @@ static int create_strip_zones(mddev_t *mddev) /* now do the other zones */ for (i = 1; i < conf->nr_strip_zones; i++) { + int j; + zone = conf->strip_zone + i; dev = conf->devlist + i * mddev->raid_disks; @@ -207,7 +212,6 @@ static int create_strip_zones(mddev_t *mddev) c = 0; for (j=0; j<cnt; j++) { - char b[BDEVNAME_SIZE]; rdev = conf->devlist[j]; printk(KERN_INFO "raid0: checking %s ...", bdevname(rdev->bdev, b)); diff --git a/drivers/md/raid1.c b/drivers/md/raid1.c index ff7ed3335995..a053423785c9 100644 --- a/drivers/md/raid1.c +++ b/drivers/md/raid1.c @@ -64,7 +64,7 @@ static void * r1bio_pool_alloc(gfp_t gfp_flags, void *data) /* allocate a r1bio with room for raid_disks entries in the bios array */ r1_bio = kzalloc(size, gfp_flags); - if (!r1_bio) + if (!r1_bio && pi->mddev) unplug_slaves(pi->mddev); return r1_bio; @@ -576,6 +576,9 @@ static int raid1_congested(void *data, int bits) conf_t *conf = mddev->private; int i, ret = 0; + if (mddev_congested(mddev, bits)) + return 1; + rcu_read_lock(); for (i = 0; i < mddev->raid_disks; i++) { mdk_rdev_t *rdev = rcu_dereference(conf->mirrors[i].rdev); @@ -851,7 +854,7 @@ static int make_request(struct request_queue *q, struct bio * bio) read_bio->bi_sector = r1_bio->sector + mirror->rdev->data_offset; read_bio->bi_bdev = mirror->rdev->bdev; read_bio->bi_end_io = raid1_end_read_request; - read_bio->bi_rw = READ | do_sync; + read_bio->bi_rw = READ | (do_sync << BIO_RW_SYNCIO); read_bio->bi_private = r1_bio; generic_make_request(read_bio); @@ -943,7 +946,8 @@ static int make_request(struct request_queue *q, struct bio * bio) mbio->bi_sector = r1_bio->sector + conf->mirrors[i].rdev->data_offset; mbio->bi_bdev = conf->mirrors[i].rdev->bdev; mbio->bi_end_io = raid1_end_write_request; - mbio->bi_rw = WRITE | do_barriers | do_sync; + mbio->bi_rw = WRITE | (do_barriers << BIO_RW_BARRIER) | + (do_sync << BIO_RW_SYNCIO); mbio->bi_private = r1_bio; if (behind_pages) { @@ -1623,7 +1627,8 @@ static void raid1d(mddev_t *mddev) conf->mirrors[i].rdev->data_offset; bio->bi_bdev = conf->mirrors[i].rdev->bdev; bio->bi_end_io = raid1_end_write_request; - bio->bi_rw = WRITE | do_sync; + bio->bi_rw = WRITE | + (do_sync << BIO_RW_SYNCIO); bio->bi_private = r1_bio; r1_bio->bios[i] = bio; generic_make_request(bio); @@ -1672,12 +1677,13 @@ static void raid1d(mddev_t *mddev) bio->bi_sector = r1_bio->sector + rdev->data_offset; bio->bi_bdev = rdev->bdev; bio->bi_end_io = raid1_end_read_request; - bio->bi_rw = READ | do_sync; + bio->bi_rw = READ | (do_sync << BIO_RW_SYNCIO); bio->bi_private = r1_bio; unplug = 1; generic_make_request(bio); } } + cond_resched(); } if (unplug) unplug_slaves(mddev); @@ -1973,13 +1979,14 @@ static int run(mddev_t *mddev) conf->poolinfo = kmalloc(sizeof(*conf->poolinfo), GFP_KERNEL); if (!conf->poolinfo) goto out_no_mem; - conf->poolinfo->mddev = mddev; + conf->poolinfo->mddev = NULL; conf->poolinfo->raid_disks = mddev->raid_disks; conf->r1bio_pool = mempool_create(NR_RAID1_BIOS, r1bio_pool_alloc, r1bio_pool_free, conf->poolinfo); if (!conf->r1bio_pool) goto out_no_mem; + conf->poolinfo->mddev = mddev; spin_lock_init(&conf->device_lock); mddev->queue->queue_lock = &conf->device_lock; @@ -2047,7 +2054,7 @@ static int run(mddev_t *mddev) conf->last_used = j; - mddev->thread = md_register_thread(raid1d, mddev, "%s_raid1"); + mddev->thread = md_register_thread(raid1d, mddev, NULL); if (!mddev->thread) { printk(KERN_ERR "raid1: couldn't allocate thread for %s\n", diff --git a/drivers/md/raid10.c b/drivers/md/raid10.c index d0a2152e064f..c2cb7b87b440 100644 --- a/drivers/md/raid10.c +++ b/drivers/md/raid10.c @@ -68,7 +68,7 @@ static void * r10bio_pool_alloc(gfp_t gfp_flags, void *data) /* allocate a r10bio with room for raid_disks entries in the bios array */ r10_bio = kzalloc(size, gfp_flags); - if (!r10_bio) + if (!r10_bio && conf->mddev) unplug_slaves(conf->mddev); return r10_bio; @@ -631,6 +631,8 @@ static int raid10_congested(void *data, int bits) conf_t *conf = mddev->private; int i, ret = 0; + if (mddev_congested(mddev, bits)) + return 1; rcu_read_lock(); for (i = 0; i < mddev->raid_disks && ret == 0; i++) { mdk_rdev_t *rdev = rcu_dereference(conf->mirrors[i].rdev); @@ -882,7 +884,7 @@ static int make_request(struct request_queue *q, struct bio * bio) mirror->rdev->data_offset; read_bio->bi_bdev = mirror->rdev->bdev; read_bio->bi_end_io = raid10_end_read_request; - read_bio->bi_rw = READ | do_sync; + read_bio->bi_rw = READ | (do_sync << BIO_RW_SYNCIO); read_bio->bi_private = r10_bio; generic_make_request(read_bio); @@ -950,7 +952,7 @@ static int make_request(struct request_queue *q, struct bio * bio) conf->mirrors[d].rdev->data_offset; mbio->bi_bdev = conf->mirrors[d].rdev->bdev; mbio->bi_end_io = raid10_end_write_request; - mbio->bi_rw = WRITE | do_sync; + mbio->bi_rw = WRITE | (do_sync << BIO_RW_SYNCIO); mbio->bi_private = r10_bio; atomic_inc(&r10_bio->remaining); @@ -1623,13 +1625,14 @@ static void raid10d(mddev_t *mddev) bio->bi_sector = r10_bio->devs[r10_bio->read_slot].addr + rdev->data_offset; bio->bi_bdev = rdev->bdev; - bio->bi_rw = READ | do_sync; + bio->bi_rw = READ | (do_sync << BIO_RW_SYNCIO); bio->bi_private = r10_bio; bio->bi_end_io = raid10_end_read_request; unplug = 1; generic_make_request(bio); } } + cond_resched(); } if (unplug) unplug_slaves(mddev); @@ -1773,7 +1776,7 @@ static sector_t sync_request(mddev_t *mddev, sector_t sector_nr, int *skipped, i max_sync = RESYNC_PAGES << (PAGE_SHIFT-9); if (!test_bit(MD_RECOVERY_SYNC, &mddev->recovery)) { /* recovery... the complicated one */ - int i, j, k; + int j, k; r10_bio = NULL; for (i=0 ; i<conf->raid_disks; i++) @@ -2093,7 +2096,6 @@ static int run(mddev_t *mddev) if (!conf->tmppage) goto out_free_conf; - conf->mddev = mddev; conf->raid_disks = mddev->raid_disks; conf->near_copies = nc; conf->far_copies = fc; @@ -2130,6 +2132,7 @@ static int run(mddev_t *mddev) goto out_free_conf; } + conf->mddev = mddev; spin_lock_init(&conf->device_lock); mddev->queue->queue_lock = &conf->device_lock; @@ -2188,7 +2191,7 @@ static int run(mddev_t *mddev) } - mddev->thread = md_register_thread(raid10d, mddev, "%s_raid10"); + mddev->thread = md_register_thread(raid10d, mddev, NULL); if (!mddev->thread) { printk(KERN_ERR "raid10: couldn't allocate thread for %s\n", diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c index 826eb3467357..d29215d966da 100644 --- a/drivers/md/raid5.c +++ b/drivers/md/raid5.c @@ -47,7 +47,9 @@ #include <linux/kthread.h> #include <linux/raid/pq.h> #include <linux/async_tx.h> +#include <linux/async.h> #include <linux/seq_file.h> +#include <linux/cpu.h> #include "md.h" #include "raid5.h" #include "bitmap.h" @@ -154,13 +156,16 @@ static inline int raid6_next_disk(int disk, int raid_disks) static int raid6_idx_to_slot(int idx, struct stripe_head *sh, int *count, int syndrome_disks) { - int slot; + int slot = *count; + if (sh->ddf_layout) + (*count)++; if (idx == sh->pd_idx) return syndrome_disks; if (idx == sh->qd_idx) return syndrome_disks + 1; - slot = (*count)++; + if (!sh->ddf_layout) + (*count)++; return slot; } @@ -499,11 +504,18 @@ async_copy_data(int frombio, struct bio *bio, struct page *page, struct page *bio_page; int i; int page_offset; + struct async_submit_ctl submit; + enum async_tx_flags flags = 0; if (bio->bi_sector >= sector) page_offset = (signed)(bio->bi_sector - sector) * 512; else page_offset = (signed)(sector - bio->bi_sector) * -512; + + if (frombio) + flags |= ASYNC_TX_FENCE; + init_async_submit(&submit, flags, tx, NULL, NULL, NULL); + bio_for_each_segment(bvl, bio, i) { int len = bio_iovec_idx(bio, i)->bv_len; int clen; @@ -525,15 +537,14 @@ async_copy_data(int frombio, struct bio *bio, struct page *page, bio_page = bio_iovec_idx(bio, i)->bv_page; if (frombio) tx = async_memcpy(page, bio_page, page_offset, - b_offset, clen, - ASYNC_TX_DEP_ACK, - tx, NULL, NULL); + b_offset, clen, &submit); else tx = async_memcpy(bio_page, page, b_offset, - page_offset, clen, - ASYNC_TX_DEP_ACK, - tx, NULL, NULL); + page_offset, clen, &submit); } + /* chain the operations */ + submit.depend_tx = tx; + if (clen < len) /* hit end of page */ break; page_offset += len; @@ -592,6 +603,7 @@ static void ops_run_biofill(struct stripe_head *sh) { struct dma_async_tx_descriptor *tx = NULL; raid5_conf_t *conf = sh->raid_conf; + struct async_submit_ctl submit; int i; pr_debug("%s: stripe %llu\n", __func__, @@ -615,22 +627,34 @@ static void ops_run_biofill(struct stripe_head *sh) } atomic_inc(&sh->count); - async_trigger_callback(ASYNC_TX_DEP_ACK | ASYNC_TX_ACK, tx, - ops_complete_biofill, sh); + init_async_submit(&submit, ASYNC_TX_ACK, tx, ops_complete_biofill, sh, NULL); + async_trigger_callback(&submit); } -static void ops_complete_compute5(void *stripe_head_ref) +static void mark_target_uptodate(struct stripe_head *sh, int target) { - struct stripe_head *sh = stripe_head_ref; - int target = sh->ops.target; - struct r5dev *tgt = &sh->dev[target]; + struct r5dev *tgt; - pr_debug("%s: stripe %llu\n", __func__, - (unsigned long long)sh->sector); + if (target < 0) + return; + tgt = &sh->dev[target]; set_bit(R5_UPTODATE, &tgt->flags); BUG_ON(!test_bit(R5_Wantcompute, &tgt->flags)); clear_bit(R5_Wantcompute, &tgt->flags); +} + +static void ops_complete_compute(void *stripe_head_ref) +{ + struct stripe_head *sh = stripe_head_ref; + + pr_debug("%s: stripe %llu\n", __func__, + (unsigned long long)sh->sector); + + /* mark the computed target(s) as uptodate */ + mark_target_uptodate(sh, sh->ops.target); + mark_target_uptodate(sh, sh->ops.target2); + clear_bit(STRIPE_COMPUTE_RUN, &sh->state); if (sh->check_state == check_state_compute_run) sh->check_state = check_state_compute_result; @@ -638,16 +662,24 @@ static void ops_complete_compute5(void *stripe_head_ref) release_stripe(sh); } -static struct dma_async_tx_descriptor *ops_run_compute5(struct stripe_head *sh) +/* return a pointer to the address conversion region of the scribble buffer */ +static addr_conv_t *to_addr_conv(struct stripe_head *sh, + struct raid5_percpu *percpu) +{ + return percpu->scribble + sizeof(struct page *) * (sh->disks + 2); +} + +static struct dma_async_tx_descriptor * +ops_run_compute5(struct stripe_head *sh, struct raid5_percpu *percpu) { - /* kernel stack size limits the total number of disks */ int disks = sh->disks; - struct page *xor_srcs[disks]; + struct page **xor_srcs = percpu->scribble; int target = sh->ops.target; struct r5dev *tgt = &sh->dev[target]; struct page *xor_dest = tgt->page; int count = 0; struct dma_async_tx_descriptor *tx; + struct async_submit_ctl submit; int i; pr_debug("%s: stripe %llu block: %d\n", @@ -660,17 +692,213 @@ static struct dma_async_tx_descriptor *ops_run_compute5(struct stripe_head *sh) atomic_inc(&sh->count); + init_async_submit(&submit, ASYNC_TX_FENCE|ASYNC_TX_XOR_ZERO_DST, NULL, + ops_complete_compute, sh, to_addr_conv(sh, percpu)); if (unlikely(count == 1)) - tx = async_memcpy(xor_dest, xor_srcs[0], 0, 0, STRIPE_SIZE, - 0, NULL, ops_complete_compute5, sh); + tx = async_memcpy(xor_dest, xor_srcs[0], 0, 0, STRIPE_SIZE, &submit); + else + tx = async_xor(xor_dest, xor_srcs, 0, count, STRIPE_SIZE, &submit); + + return tx; +} + +/* set_syndrome_sources - populate source buffers for gen_syndrome + * @srcs - (struct page *) array of size sh->disks + * @sh - stripe_head to parse + * + * Populates srcs in proper layout order for the stripe and returns the + * 'count' of sources to be used in a call to async_gen_syndrome. The P + * destination buffer is recorded in srcs[count] and the Q destination + * is recorded in srcs[count+1]]. + */ +static int set_syndrome_sources(struct page **srcs, struct stripe_head *sh) +{ + int disks = sh->disks; + int syndrome_disks = sh->ddf_layout ? disks : (disks - 2); + int d0_idx = raid6_d0(sh); + int count; + int i; + + for (i = 0; i < disks; i++) + srcs[i] = NULL; + + count = 0; + i = d0_idx; + do { + int slot = raid6_idx_to_slot(i, sh, &count, syndrome_disks); + + srcs[slot] = sh->dev[i].page; + i = raid6_next_disk(i, disks); + } while (i != d0_idx); + + return syndrome_disks; +} + +static struct dma_async_tx_descriptor * +ops_run_compute6_1(struct stripe_head *sh, struct raid5_percpu *percpu) +{ + int disks = sh->disks; + struct page **blocks = percpu->scribble; + int target; + int qd_idx = sh->qd_idx; + struct dma_async_tx_descriptor *tx; + struct async_submit_ctl submit; + struct r5dev *tgt; + struct page *dest; + int i; + int count; + + if (sh->ops.target < 0) + target = sh->ops.target2; + else if (sh->ops.target2 < 0) + target = sh->ops.target; else - tx = async_xor(xor_dest, xor_srcs, 0, count, STRIPE_SIZE, - ASYNC_TX_XOR_ZERO_DST, NULL, - ops_complete_compute5, sh); + /* we should only have one valid target */ + BUG(); + BUG_ON(target < 0); + pr_debug("%s: stripe %llu block: %d\n", + __func__, (unsigned long long)sh->sector, target); + + tgt = &sh->dev[target]; + BUG_ON(!test_bit(R5_Wantcompute, &tgt->flags)); + dest = tgt->page; + + atomic_inc(&sh->count); + + if (target == qd_idx) { + count = set_syndrome_sources(blocks, sh); + blocks[count] = NULL; /* regenerating p is not necessary */ + BUG_ON(blocks[count+1] != dest); /* q should already be set */ + init_async_submit(&submit, ASYNC_TX_FENCE, NULL, + ops_complete_compute, sh, + to_addr_conv(sh, percpu)); + tx = async_gen_syndrome(blocks, 0, count+2, STRIPE_SIZE, &submit); + } else { + /* Compute any data- or p-drive using XOR */ + count = 0; + for (i = disks; i-- ; ) { + if (i == target || i == qd_idx) + continue; + blocks[count++] = sh->dev[i].page; + } + + init_async_submit(&submit, ASYNC_TX_FENCE|ASYNC_TX_XOR_ZERO_DST, + NULL, ops_complete_compute, sh, + to_addr_conv(sh, percpu)); + tx = async_xor(dest, blocks, 0, count, STRIPE_SIZE, &submit); + } return tx; } +static struct dma_async_tx_descriptor * +ops_run_compute6_2(struct stripe_head *sh, struct raid5_percpu *percpu) +{ + int i, count, disks = sh->disks; + int syndrome_disks = sh->ddf_layout ? disks : disks-2; + int d0_idx = raid6_d0(sh); + int faila = -1, failb = -1; + int target = sh->ops.target; + int target2 = sh->ops.target2; + struct r5dev *tgt = &sh->dev[target]; + struct r5dev *tgt2 = &sh->dev[target2]; + struct dma_async_tx_descriptor *tx; + struct page **blocks = percpu->scribble; + struct async_submit_ctl submit; + + pr_debug("%s: stripe %llu block1: %d block2: %d\n", + __func__, (unsigned long long)sh->sector, target, target2); + BUG_ON(target < 0 || target2 < 0); + BUG_ON(!test_bit(R5_Wantcompute, &tgt->flags)); + BUG_ON(!test_bit(R5_Wantcompute, &tgt2->flags)); + + /* we need to open-code set_syndrome_sources to handle the + * slot number conversion for 'faila' and 'failb' + */ + for (i = 0; i < disks ; i++) + blocks[i] = NULL; + count = 0; + i = d0_idx; + do { + int slot = raid6_idx_to_slot(i, sh, &count, syndrome_disks); + + blocks[slot] = sh->dev[i].page; + + if (i == target) + faila = slot; + if (i == target2) + failb = slot; + i = raid6_next_disk(i, disks); + } while (i != d0_idx); + + BUG_ON(faila == failb); + if (failb < faila) + swap(faila, failb); + pr_debug("%s: stripe: %llu faila: %d failb: %d\n", + __func__, (unsigned long long)sh->sector, faila, failb); + + atomic_inc(&sh->count); + + if (failb == syndrome_disks+1) { + /* Q disk is one of the missing disks */ + if (faila == syndrome_disks) { + /* Missing P+Q, just recompute */ + init_async_submit(&submit, ASYNC_TX_FENCE, NULL, + ops_complete_compute, sh, + to_addr_conv(sh, percpu)); + return async_gen_syndrome(blocks, 0, syndrome_disks+2, + STRIPE_SIZE, &submit); + } else { + struct page *dest; + int data_target; + int qd_idx = sh->qd_idx; + + /* Missing D+Q: recompute D from P, then recompute Q */ + if (target == qd_idx) + data_target = target2; + else + data_target = target; + + count = 0; + for (i = disks; i-- ; ) { + if (i == data_target || i == qd_idx) + continue; + blocks[count++] = sh->dev[i].page; + } + dest = sh->dev[data_target].page; + init_async_submit(&submit, + ASYNC_TX_FENCE|ASYNC_TX_XOR_ZERO_DST, + NULL, NULL, NULL, + to_addr_conv(sh, percpu)); + tx = async_xor(dest, blocks, 0, count, STRIPE_SIZE, + &submit); + + count = set_syndrome_sources(blocks, sh); + init_async_submit(&submit, ASYNC_TX_FENCE, tx, + ops_complete_compute, sh, + to_addr_conv(sh, percpu)); + return async_gen_syndrome(blocks, 0, count+2, + STRIPE_SIZE, &submit); + } + } else { + init_async_submit(&submit, ASYNC_TX_FENCE, NULL, + ops_complete_compute, sh, + to_addr_conv(sh, percpu)); + if (failb == syndrome_disks) { + /* We're missing D+P. */ + return async_raid6_datap_recov(syndrome_disks+2, + STRIPE_SIZE, faila, + blocks, &submit); + } else { + /* We're missing D+D. */ + return async_raid6_2data_recov(syndrome_disks+2, + STRIPE_SIZE, faila, failb, + blocks, &submit); + } + } +} + + static void ops_complete_prexor(void *stripe_head_ref) { struct stripe_head *sh = stripe_head_ref; @@ -680,12 +908,13 @@ static void ops_complete_prexor(void *stripe_head_ref) } static struct dma_async_tx_descriptor * -ops_run_prexor(struct stripe_head *sh, struct dma_async_tx_descriptor *tx) +ops_run_prexor(struct stripe_head *sh, struct raid5_percpu *percpu, + struct dma_async_tx_descriptor *tx) { - /* kernel stack size limits the total number of disks */ int disks = sh->disks; - struct page *xor_srcs[disks]; + struct page **xor_srcs = percpu->scribble; int count = 0, pd_idx = sh->pd_idx, i; + struct async_submit_ctl submit; /* existing parity data subtracted */ struct page *xor_dest = xor_srcs[count++] = sh->dev[pd_idx].page; @@ -700,9 +929,9 @@ ops_run_prexor(struct stripe_head *sh, struct dma_async_tx_descriptor *tx) xor_srcs[count++] = dev->page; } - tx = async_xor(xor_dest, xor_srcs, 0, count, STRIPE_SIZE, - ASYNC_TX_DEP_ACK | ASYNC_TX_XOR_DROP_DST, tx, - ops_complete_prexor, sh); + init_async_submit(&submit, ASYNC_TX_FENCE|ASYNC_TX_XOR_DROP_DST, tx, + ops_complete_prexor, sh, to_addr_conv(sh, percpu)); + tx = async_xor(xor_dest, xor_srcs, 0, count, STRIPE_SIZE, &submit); return tx; } @@ -742,17 +971,21 @@ ops_run_biodrain(struct stripe_head *sh, struct dma_async_tx_descriptor *tx) return tx; } -static void ops_complete_postxor(void *stripe_head_ref) +static void ops_complete_reconstruct(void *stripe_head_ref) { struct stripe_head *sh = stripe_head_ref; - int disks = sh->disks, i, pd_idx = sh->pd_idx; + int disks = sh->disks; + int pd_idx = sh->pd_idx; + int qd_idx = sh->qd_idx; + int i; pr_debug("%s: stripe %llu\n", __func__, (unsigned long long)sh->sector); for (i = disks; i--; ) { struct r5dev *dev = &sh->dev[i]; - if (dev->written || i == pd_idx) + + if (dev->written || i == pd_idx || i == qd_idx) set_bit(R5_UPTODATE, &dev->flags); } @@ -770,12 +1003,12 @@ static void ops_complete_postxor(void *stripe_head_ref) } static void -ops_run_postxor(struct stripe_head *sh, struct dma_async_tx_descriptor *tx) +ops_run_reconstruct5(struct stripe_head *sh, struct raid5_percpu *percpu, + struct dma_async_tx_descriptor *tx) { - /* kernel stack size limits the total number of disks */ int disks = sh->disks; - struct page *xor_srcs[disks]; - + struct page **xor_srcs = percpu->scribble; + struct async_submit_ctl submit; int count = 0, pd_idx = sh->pd_idx, i; struct page *xor_dest; int prexor = 0; @@ -809,18 +1042,36 @@ ops_run_postxor(struct stripe_head *sh, struct dma_async_tx_descriptor *tx) * set ASYNC_TX_XOR_DROP_DST and ASYNC_TX_XOR_ZERO_DST * for the synchronous xor case */ - flags = ASYNC_TX_DEP_ACK | ASYNC_TX_ACK | + flags = ASYNC_TX_ACK | (prexor ? ASYNC_TX_XOR_DROP_DST : ASYNC_TX_XOR_ZERO_DST); atomic_inc(&sh->count); - if (unlikely(count == 1)) { - flags &= ~(ASYNC_TX_XOR_DROP_DST | ASYNC_TX_XOR_ZERO_DST); - tx = async_memcpy(xor_dest, xor_srcs[0], 0, 0, STRIPE_SIZE, - flags, tx, ops_complete_postxor, sh); - } else - tx = async_xor(xor_dest, xor_srcs, 0, count, STRIPE_SIZE, - flags, tx, ops_complete_postxor, sh); + init_async_submit(&submit, flags, tx, ops_complete_reconstruct, sh, + to_addr_conv(sh, percpu)); + if (unlikely(count == 1)) + tx = async_memcpy(xor_dest, xor_srcs[0], 0, 0, STRIPE_SIZE, &submit); + else + tx = async_xor(xor_dest, xor_srcs, 0, count, STRIPE_SIZE, &submit); +} + +static void +ops_run_reconstruct6(struct stripe_head *sh, struct raid5_percpu *percpu, + struct dma_async_tx_descriptor *tx) +{ + struct async_submit_ctl submit; + struct page **blocks = percpu->scribble; + int count; + + pr_debug("%s: stripe %llu\n", __func__, (unsigned long long)sh->sector); + + count = set_syndrome_sources(blocks, sh); + + atomic_inc(&sh->count); + + init_async_submit(&submit, ASYNC_TX_ACK, tx, ops_complete_reconstruct, + sh, to_addr_conv(sh, percpu)); + async_gen_syndrome(blocks, 0, count+2, STRIPE_SIZE, &submit); } static void ops_complete_check(void *stripe_head_ref) @@ -835,63 +1086,115 @@ static void ops_complete_check(void *stripe_head_ref) release_stripe(sh); } -static void ops_run_check(struct stripe_head *sh) +static void ops_run_check_p(struct stripe_head *sh, struct raid5_percpu *percpu) { - /* kernel stack size limits the total number of disks */ int disks = sh->disks; - struct page *xor_srcs[disks]; + int pd_idx = sh->pd_idx; + int qd_idx = sh->qd_idx; + struct page *xor_dest; + struct page **xor_srcs = percpu->scribble; struct dma_async_tx_descriptor *tx; - - int count = 0, pd_idx = sh->pd_idx, i; - struct page *xor_dest = xor_srcs[count++] = sh->dev[pd_idx].page; + struct async_submit_ctl submit; + int count; + int i; pr_debug("%s: stripe %llu\n", __func__, (unsigned long long)sh->sector); + count = 0; + xor_dest = sh->dev[pd_idx].page; + xor_srcs[count++] = xor_dest; for (i = disks; i--; ) { - struct r5dev *dev = &sh->dev[i]; - if (i != pd_idx) - xor_srcs[count++] = dev->page; + if (i == pd_idx || i == qd_idx) + continue; + xor_srcs[count++] = sh->dev[i].page; } - tx = async_xor_zero_sum(xor_dest, xor_srcs, 0, count, STRIPE_SIZE, - &sh->ops.zero_sum_result, 0, NULL, NULL, NULL); + init_async_submit(&submit, 0, NULL, NULL, NULL, + to_addr_conv(sh, percpu)); + tx = async_xor_val(xor_dest, xor_srcs, 0, count, STRIPE_SIZE, + &sh->ops.zero_sum_result, &submit); atomic_inc(&sh->count); - tx = async_trigger_callback(ASYNC_TX_DEP_ACK | ASYNC_TX_ACK, tx, - ops_complete_check, sh); + init_async_submit(&submit, ASYNC_TX_ACK, tx, ops_complete_check, sh, NULL); + tx = async_trigger_callback(&submit); } -static void raid5_run_ops(struct stripe_head *sh, unsigned long ops_request) +static void ops_run_check_pq(struct stripe_head *sh, struct raid5_percpu *percpu, int checkp) +{ + struct page **srcs = percpu->scribble; + struct async_submit_ctl submit; + int count; + + pr_debug("%s: stripe %llu checkp: %d\n", __func__, + (unsigned long long)sh->sector, checkp); + + count = set_syndrome_sources(srcs, sh); + if (!checkp) + srcs[count] = NULL; + + atomic_inc(&sh->count); + init_async_submit(&submit, ASYNC_TX_ACK, NULL, ops_complete_check, + sh, to_addr_conv(sh, percpu)); + async_syndrome_val(srcs, 0, count+2, STRIPE_SIZE, + &sh->ops.zero_sum_result, percpu->spare_page, &submit); +} + +static void __raid_run_ops(struct stripe_head *sh, unsigned long ops_request) { int overlap_clear = 0, i, disks = sh->disks; struct dma_async_tx_descriptor *tx = NULL; + raid5_conf_t *conf = sh->raid_conf; + int level = conf->level; + struct raid5_percpu *percpu; + unsigned long cpu; + cpu = get_cpu(); + percpu = per_cpu_ptr(conf->percpu, cpu); if (test_bit(STRIPE_OP_BIOFILL, &ops_request)) { ops_run_biofill(sh); overlap_clear++; } if (test_bit(STRIPE_OP_COMPUTE_BLK, &ops_request)) { - tx = ops_run_compute5(sh); - /* terminate the chain if postxor is not set to be run */ - if (tx && !test_bit(STRIPE_OP_POSTXOR, &ops_request)) + if (level < 6) + tx = ops_run_compute5(sh, percpu); + else { + if (sh->ops.target2 < 0 || sh->ops.target < 0) + tx = ops_run_compute6_1(sh, percpu); + else + tx = ops_run_compute6_2(sh, percpu); + } + /* terminate the chain if reconstruct is not set to be run */ + if (tx && !test_bit(STRIPE_OP_RECONSTRUCT, &ops_request)) async_tx_ack(tx); } if (test_bit(STRIPE_OP_PREXOR, &ops_request)) - tx = ops_run_prexor(sh, tx); + tx = ops_run_prexor(sh, percpu, tx); if (test_bit(STRIPE_OP_BIODRAIN, &ops_request)) { tx = ops_run_biodrain(sh, tx); overlap_clear++; } - if (test_bit(STRIPE_OP_POSTXOR, &ops_request)) - ops_run_postxor(sh, tx); + if (test_bit(STRIPE_OP_RECONSTRUCT, &ops_request)) { + if (level < 6) + ops_run_reconstruct5(sh, percpu, tx); + else + ops_run_reconstruct6(sh, percpu, tx); + } - if (test_bit(STRIPE_OP_CHECK, &ops_request)) - ops_run_check(sh); + if (test_bit(STRIPE_OP_CHECK, &ops_request)) { + if (sh->check_state == check_state_run) + ops_run_check_p(sh, percpu); + else if (sh->check_state == check_state_run_q) + ops_run_check_pq(sh, percpu, 0); + else if (sh->check_state == check_state_run_pq) + ops_run_check_pq(sh, percpu, 1); + else + BUG(); + } if (overlap_clear) for (i = disks; i--; ) { @@ -899,24 +1202,58 @@ static void raid5_run_ops(struct stripe_head *sh, unsigned long ops_request) if (test_and_clear_bit(R5_Overlap, &dev->flags)) wake_up(&sh->raid_conf->wait_for_overlap); } + put_cpu(); +} + +#ifdef CONFIG_MULTICORE_RAID456 +static void async_run_ops(void *param, async_cookie_t cookie) +{ + struct stripe_head *sh = param; + unsigned long ops_request = sh->ops.request; + + clear_bit_unlock(STRIPE_OPS_REQ_PENDING, &sh->state); + wake_up(&sh->ops.wait_for_ops); + + __raid_run_ops(sh, ops_request); + release_stripe(sh); } +static void raid_run_ops(struct stripe_head *sh, unsigned long ops_request) +{ + /* since handle_stripe can be called outside of raid5d context + * we need to ensure sh->ops.request is de-staged before another + * request arrives + */ + wait_event(sh->ops.wait_for_ops, + !test_and_set_bit_lock(STRIPE_OPS_REQ_PENDING, &sh->state)); + sh->ops.request = ops_request; + + atomic_inc(&sh->count); + async_schedule(async_run_ops, sh); +} +#else +#define raid_run_ops __raid_run_ops +#endif + static int grow_one_stripe(raid5_conf_t *conf) { struct stripe_head *sh; + int disks = max(conf->raid_disks, conf->previous_raid_disks); sh = kmem_cache_alloc(conf->slab_cache, GFP_KERNEL); if (!sh) return 0; - memset(sh, 0, sizeof(*sh) + (conf->raid_disks-1)*sizeof(struct r5dev)); + memset(sh, 0, sizeof(*sh) + (disks-1)*sizeof(struct r5dev)); sh->raid_conf = conf; spin_lock_init(&sh->lock); + #ifdef CONFIG_MULTICORE_RAID456 + init_waitqueue_head(&sh->ops.wait_for_ops); + #endif - if (grow_buffers(sh, conf->raid_disks)) { - shrink_buffers(sh, conf->raid_disks); + if (grow_buffers(sh, disks)) { + shrink_buffers(sh, disks); kmem_cache_free(conf->slab_cache, sh); return 0; } - sh->disks = conf->raid_disks; /* we just created an active stripe so... */ atomic_set(&sh->count, 1); atomic_inc(&conf->active_stripes); @@ -928,7 +1265,7 @@ static int grow_one_stripe(raid5_conf_t *conf) static int grow_stripes(raid5_conf_t *conf, int num) { struct kmem_cache *sc; - int devs = conf->raid_disks; + int devs = max(conf->raid_disks, conf->previous_raid_disks); sprintf(conf->cache_name[0], "raid%d-%s", conf->level, mdname(conf->mddev)); @@ -948,6 +1285,28 @@ static int grow_stripes(raid5_conf_t *conf, int num) return 0; } +/** + * scribble_len - return the required size of the scribble region + * @num - total number of disks in the array + * + * The size must be enough to contain: + * 1/ a struct page pointer for each device in the array +2 + * 2/ room to convert each entry in (1) to its corresponding dma + * (dma_map_page()) or page (page_address()) address. + * + * Note: the +2 is for the destination buffers of the ddf/raid6 case where we + * calculate over all devices (not just the data blocks), using zeros in place + * of the P and Q blocks. + */ +static size_t scribble_len(int num) +{ + size_t len; + + len = sizeof(struct page *) * (num+2) + sizeof(addr_conv_t) * (num+2); + + return len; +} + static int resize_stripes(raid5_conf_t *conf, int newsize) { /* Make all the stripes able to hold 'newsize' devices. @@ -976,6 +1335,7 @@ static int resize_stripes(raid5_conf_t *conf, int newsize) struct stripe_head *osh, *nsh; LIST_HEAD(newstripes); struct disk_info *ndisks; + unsigned long cpu; int err; struct kmem_cache *sc; int i; @@ -1003,6 +1363,9 @@ static int resize_stripes(raid5_conf_t *conf, int newsize) nsh->raid_conf = conf; spin_lock_init(&nsh->lock); + #ifdef CONFIG_MULTICORE_RAID456 + init_waitqueue_head(&nsh->ops.wait_for_ops); + #endif list_add(&nsh->lru, &newstripes); } @@ -1041,7 +1404,7 @@ static int resize_stripes(raid5_conf_t *conf, int newsize) /* Step 3. * At this point, we are holding all the stripes so the array * is completely stalled, so now is a good time to resize - * conf->disks. + * conf->disks and the scribble region */ ndisks = kzalloc(newsize * sizeof(struct disk_info), GFP_NOIO); if (ndisks) { @@ -1052,10 +1415,30 @@ static int resize_stripes(raid5_conf_t *conf, int newsize) } else err = -ENOMEM; + get_online_cpus(); + conf->scribble_len = scribble_len(newsize); + for_each_present_cpu(cpu) { + struct raid5_percpu *percpu; + void *scribble; + + percpu = per_cpu_ptr(conf->percpu, cpu); + scribble = kmalloc(conf->scribble_len, GFP_NOIO); + + if (scribble) { + kfree(percpu->scribble); + percpu->scribble = scribble; + } else { + err = -ENOMEM; + break; + } + } + put_online_cpus(); + /* Step 4, return new stripes to service */ while(!list_empty(&newstripes)) { nsh = list_entry(newstripes.next, struct stripe_head, lru); list_del_init(&nsh->lru); + for (i=conf->raid_disks; i < newsize; i++) if (nsh->dev[i].page == NULL) { struct page *p = alloc_page(GFP_NOIO); @@ -1553,10 +1936,15 @@ static sector_t compute_blocknr(struct stripe_head *sh, int i, int previous) case ALGORITHM_PARITY_N: break; case ALGORITHM_ROTATING_N_CONTINUE: + /* Like left_symmetric, but P is before Q */ if (sh->pd_idx == 0) i--; /* P D D D Q */ - else if (i > sh->pd_idx) - i -= 2; /* D D Q P D */ + else { + /* D D Q P D */ + if (i < sh->pd_idx) + i += raid_disks; + i -= (sh->pd_idx + 1); + } break; case ALGORITHM_LEFT_ASYMMETRIC_6: case ALGORITHM_RIGHT_ASYMMETRIC_6: @@ -1594,258 +1982,13 @@ static sector_t compute_blocknr(struct stripe_head *sh, int i, int previous) } - -/* - * Copy data between a page in the stripe cache, and one or more bion - * The page could align with the middle of the bio, or there could be - * several bion, each with several bio_vecs, which cover part of the page - * Multiple bion are linked together on bi_next. There may be extras - * at the end of this list. We ignore them. - */ -static void copy_data(int frombio, struct bio *bio, - struct page *page, - sector_t sector) -{ - char *pa = page_address(page); - struct bio_vec *bvl; - int i; - int page_offset; - - if (bio->bi_sector >= sector) - page_offset = (signed)(bio->bi_sector - sector) * 512; - else - page_offset = (signed)(sector - bio->bi_sector) * -512; - bio_for_each_segment(bvl, bio, i) { - int len = bio_iovec_idx(bio,i)->bv_len; - int clen; - int b_offset = 0; - - if (page_offset < 0) { - b_offset = -page_offset; - page_offset += b_offset; - len -= b_offset; - } - - if (len > 0 && page_offset + len > STRIPE_SIZE) - clen = STRIPE_SIZE - page_offset; - else clen = len; - - if (clen > 0) { - char *ba = __bio_kmap_atomic(bio, i, KM_USER0); - if (frombio) - memcpy(pa+page_offset, ba+b_offset, clen); - else - memcpy(ba+b_offset, pa+page_offset, clen); - __bio_kunmap_atomic(ba, KM_USER0); - } - if (clen < len) /* hit end of page */ - break; - page_offset += len; - } -} - -#define check_xor() do { \ - if (count == MAX_XOR_BLOCKS) { \ - xor_blocks(count, STRIPE_SIZE, dest, ptr);\ - count = 0; \ - } \ - } while(0) - -static void compute_parity6(struct stripe_head *sh, int method) -{ - raid5_conf_t *conf = sh->raid_conf; - int i, pd_idx, qd_idx, d0_idx, disks = sh->disks, count; - int syndrome_disks = sh->ddf_layout ? disks : (disks - 2); - struct bio *chosen; - /**** FIX THIS: This could be very bad if disks is close to 256 ****/ - void *ptrs[syndrome_disks+2]; - - pd_idx = sh->pd_idx; - qd_idx = sh->qd_idx; - d0_idx = raid6_d0(sh); - - pr_debug("compute_parity, stripe %llu, method %d\n", - (unsigned long long)sh->sector, method); - - switch(method) { - case READ_MODIFY_WRITE: - BUG(); /* READ_MODIFY_WRITE N/A for RAID-6 */ - case RECONSTRUCT_WRITE: - for (i= disks; i-- ;) - if ( i != pd_idx && i != qd_idx && sh->dev[i].towrite ) { - chosen = sh->dev[i].towrite; - sh->dev[i].towrite = NULL; - - if (test_and_clear_bit(R5_Overlap, &sh->dev[i].flags)) - wake_up(&conf->wait_for_overlap); - - BUG_ON(sh->dev[i].written); - sh->dev[i].written = chosen; - } - break; - case CHECK_PARITY: - BUG(); /* Not implemented yet */ - } - - for (i = disks; i--;) - if (sh->dev[i].written) { - sector_t sector = sh->dev[i].sector; - struct bio *wbi = sh->dev[i].written; - while (wbi && wbi->bi_sector < sector + STRIPE_SECTORS) { - copy_data(1, wbi, sh->dev[i].page, sector); - wbi = r5_next_bio(wbi, sector); - } - - set_bit(R5_LOCKED, &sh->dev[i].flags); - set_bit(R5_UPTODATE, &sh->dev[i].flags); - } - - /* Note that unlike RAID-5, the ordering of the disks matters greatly.*/ - - for (i = 0; i < disks; i++) - ptrs[i] = (void *)raid6_empty_zero_page; - - count = 0; - i = d0_idx; - do { - int slot = raid6_idx_to_slot(i, sh, &count, syndrome_disks); - - ptrs[slot] = page_address(sh->dev[i].page); - if (slot < syndrome_disks && - !test_bit(R5_UPTODATE, &sh->dev[i].flags)) { - printk(KERN_ERR "block %d/%d not uptodate " - "on parity calc\n", i, count); - BUG(); - } - - i = raid6_next_disk(i, disks); - } while (i != d0_idx); - BUG_ON(count != syndrome_disks); - - raid6_call.gen_syndrome(syndrome_disks+2, STRIPE_SIZE, ptrs); - - switch(method) { - case RECONSTRUCT_WRITE: - set_bit(R5_UPTODATE, &sh->dev[pd_idx].flags); - set_bit(R5_UPTODATE, &sh->dev[qd_idx].flags); - set_bit(R5_LOCKED, &sh->dev[pd_idx].flags); - set_bit(R5_LOCKED, &sh->dev[qd_idx].flags); - break; - case UPDATE_PARITY: - set_bit(R5_UPTODATE, &sh->dev[pd_idx].flags); - set_bit(R5_UPTODATE, &sh->dev[qd_idx].flags); - break; - } -} - - -/* Compute one missing block */ -static void compute_block_1(struct stripe_head *sh, int dd_idx, int nozero) -{ - int i, count, disks = sh->disks; - void *ptr[MAX_XOR_BLOCKS], *dest, *p; - int qd_idx = sh->qd_idx; - - pr_debug("compute_block_1, stripe %llu, idx %d\n", - (unsigned long long)sh->sector, dd_idx); - - if ( dd_idx == qd_idx ) { - /* We're actually computing the Q drive */ - compute_parity6(sh, UPDATE_PARITY); - } else { - dest = page_address(sh->dev[dd_idx].page); - if (!nozero) memset(dest, 0, STRIPE_SIZE); - count = 0; - for (i = disks ; i--; ) { - if (i == dd_idx || i == qd_idx) - continue; - p = page_address(sh->dev[i].page); - if (test_bit(R5_UPTODATE, &sh->dev[i].flags)) - ptr[count++] = p; - else - printk("compute_block() %d, stripe %llu, %d" - " not present\n", dd_idx, - (unsigned long long)sh->sector, i); - - check_xor(); - } - if (count) - xor_blocks(count, STRIPE_SIZE, dest, ptr); - if (!nozero) set_bit(R5_UPTODATE, &sh->dev[dd_idx].flags); - else clear_bit(R5_UPTODATE, &sh->dev[dd_idx].flags); - } -} - -/* Compute two missing blocks */ -static void compute_block_2(struct stripe_head *sh, int dd_idx1, int dd_idx2) -{ - int i, count, disks = sh->disks; - int syndrome_disks = sh->ddf_layout ? disks : disks-2; - int d0_idx = raid6_d0(sh); - int faila = -1, failb = -1; - /**** FIX THIS: This could be very bad if disks is close to 256 ****/ - void *ptrs[syndrome_disks+2]; - - for (i = 0; i < disks ; i++) - ptrs[i] = (void *)raid6_empty_zero_page; - count = 0; - i = d0_idx; - do { - int slot = raid6_idx_to_slot(i, sh, &count, syndrome_disks); - - ptrs[slot] = page_address(sh->dev[i].page); - - if (i == dd_idx1) - faila = slot; - if (i == dd_idx2) - failb = slot; - i = raid6_next_disk(i, disks); - } while (i != d0_idx); - BUG_ON(count != syndrome_disks); - - BUG_ON(faila == failb); - if ( failb < faila ) { int tmp = faila; faila = failb; failb = tmp; } - - pr_debug("compute_block_2, stripe %llu, idx %d,%d (%d,%d)\n", - (unsigned long long)sh->sector, dd_idx1, dd_idx2, - faila, failb); - - if (failb == syndrome_disks+1) { - /* Q disk is one of the missing disks */ - if (faila == syndrome_disks) { - /* Missing P+Q, just recompute */ - compute_parity6(sh, UPDATE_PARITY); - return; - } else { - /* We're missing D+Q; recompute D from P */ - compute_block_1(sh, ((dd_idx1 == sh->qd_idx) ? - dd_idx2 : dd_idx1), - 0); - compute_parity6(sh, UPDATE_PARITY); /* Is this necessary? */ - return; - } - } - - /* We're missing D+P or D+D; */ - if (failb == syndrome_disks) { - /* We're missing D+P. */ - raid6_datap_recov(syndrome_disks+2, STRIPE_SIZE, faila, ptrs); - } else { - /* We're missing D+D. */ - raid6_2data_recov(syndrome_disks+2, STRIPE_SIZE, faila, failb, - ptrs); - } - - /* Both the above update both missing blocks */ - set_bit(R5_UPTODATE, &sh->dev[dd_idx1].flags); - set_bit(R5_UPTODATE, &sh->dev[dd_idx2].flags); -} - static void -schedule_reconstruction5(struct stripe_head *sh, struct stripe_head_state *s, +schedule_reconstruction(struct stripe_head *sh, struct stripe_head_state *s, int rcw, int expand) { int i, pd_idx = sh->pd_idx, disks = sh->disks; + raid5_conf_t *conf = sh->raid_conf; + int level = conf->level; if (rcw) { /* if we are not expanding this is a proper write request, and @@ -1858,7 +2001,7 @@ schedule_reconstruction5(struct stripe_head *sh, struct stripe_head_state *s, } else sh->reconstruct_state = reconstruct_state_run; - set_bit(STRIPE_OP_POSTXOR, &s->ops_request); + set_bit(STRIPE_OP_RECONSTRUCT, &s->ops_request); for (i = disks; i--; ) { struct r5dev *dev = &sh->dev[i]; @@ -1871,17 +2014,18 @@ schedule_reconstruction5(struct stripe_head *sh, struct stripe_head_state *s, s->locked++; } } - if (s->locked + 1 == disks) + if (s->locked + conf->max_degraded == disks) if (!test_and_set_bit(STRIPE_FULL_WRITE, &sh->state)) - atomic_inc(&sh->raid_conf->pending_full_writes); + atomic_inc(&conf->pending_full_writes); } else { + BUG_ON(level == 6); BUG_ON(!(test_bit(R5_UPTODATE, &sh->dev[pd_idx].flags) || test_bit(R5_Wantcompute, &sh->dev[pd_idx].flags))); sh->reconstruct_state = reconstruct_state_prexor_drain_run; set_bit(STRIPE_OP_PREXOR, &s->ops_request); set_bit(STRIPE_OP_BIODRAIN, &s->ops_request); - set_bit(STRIPE_OP_POSTXOR, &s->ops_request); + set_bit(STRIPE_OP_RECONSTRUCT, &s->ops_request); for (i = disks; i--; ) { struct r5dev *dev = &sh->dev[i]; @@ -1899,13 +2043,22 @@ schedule_reconstruction5(struct stripe_head *sh, struct stripe_head_state *s, } } - /* keep the parity disk locked while asynchronous operations + /* keep the parity disk(s) locked while asynchronous operations * are in flight */ set_bit(R5_LOCKED, &sh->dev[pd_idx].flags); clear_bit(R5_UPTODATE, &sh->dev[pd_idx].flags); s->locked++; + if (level == 6) { + int qd_idx = sh->qd_idx; + struct r5dev *dev = &sh->dev[qd_idx]; + + set_bit(R5_LOCKED, &dev->flags); + clear_bit(R5_UPTODATE, &dev->flags); + s->locked++; + } + pr_debug("%s: stripe %llu locked: %d ops_request: %lx\n", __func__, (unsigned long long)sh->sector, s->locked, s->ops_request); @@ -1986,13 +2139,6 @@ static int add_stripe_bio(struct stripe_head *sh, struct bio *bi, int dd_idx, in static void end_reshape(raid5_conf_t *conf); -static int page_is_zero(struct page *p) -{ - char *a = page_address(p); - return ((*(u32*)a) == 0 && - memcmp(a, a+4, STRIPE_SIZE-4)==0); -} - static void stripe_set_idx(sector_t stripe, raid5_conf_t *conf, int previous, struct stripe_head *sh) { @@ -2132,9 +2278,10 @@ static int fetch_block5(struct stripe_head *sh, struct stripe_head_state *s, set_bit(STRIPE_OP_COMPUTE_BLK, &s->ops_request); set_bit(R5_Wantcompute, &dev->flags); sh->ops.target = disk_idx; + sh->ops.target2 = -1; s->req_compute = 1; /* Careful: from this point on 'uptodate' is in the eye - * of raid5_run_ops which services 'compute' operations + * of raid_run_ops which services 'compute' operations * before writes. R5_Wantcompute flags a block that will * be R5_UPTODATE by the time it is needed for a * subsequent operation. @@ -2173,61 +2320,104 @@ static void handle_stripe_fill5(struct stripe_head *sh, set_bit(STRIPE_HANDLE, &sh->state); } -static void handle_stripe_fill6(struct stripe_head *sh, - struct stripe_head_state *s, struct r6_state *r6s, - int disks) +/* fetch_block6 - checks the given member device to see if its data needs + * to be read or computed to satisfy a request. + * + * Returns 1 when no more member devices need to be checked, otherwise returns + * 0 to tell the loop in handle_stripe_fill6 to continue + */ +static int fetch_block6(struct stripe_head *sh, struct stripe_head_state *s, + struct r6_state *r6s, int disk_idx, int disks) { - int i; - for (i = disks; i--; ) { - struct r5dev *dev = &sh->dev[i]; - if (!test_bit(R5_LOCKED, &dev->flags) && - !test_bit(R5_UPTODATE, &dev->flags) && - (dev->toread || (dev->towrite && - !test_bit(R5_OVERWRITE, &dev->flags)) || - s->syncing || s->expanding || - (s->failed >= 1 && - (sh->dev[r6s->failed_num[0]].toread || - s->to_write)) || - (s->failed >= 2 && - (sh->dev[r6s->failed_num[1]].toread || - s->to_write)))) { - /* we would like to get this block, possibly - * by computing it, but we might not be able to + struct r5dev *dev = &sh->dev[disk_idx]; + struct r5dev *fdev[2] = { &sh->dev[r6s->failed_num[0]], + &sh->dev[r6s->failed_num[1]] }; + + if (!test_bit(R5_LOCKED, &dev->flags) && + !test_bit(R5_UPTODATE, &dev->flags) && + (dev->toread || + (dev->towrite && !test_bit(R5_OVERWRITE, &dev->flags)) || + s->syncing || s->expanding || + (s->failed >= 1 && + (fdev[0]->toread || s->to_write)) || + (s->failed >= 2 && + (fdev[1]->toread || s->to_write)))) { + /* we would like to get this block, possibly by computing it, + * otherwise read it if the backing disk is insync + */ + BUG_ON(test_bit(R5_Wantcompute, &dev->flags)); + BUG_ON(test_bit(R5_Wantread, &dev->flags)); + if ((s->uptodate == disks - 1) && + (s->failed && (disk_idx == r6s->failed_num[0] || + disk_idx == r6s->failed_num[1]))) { + /* have disk failed, and we're requested to fetch it; + * do compute it */ - if ((s->uptodate == disks - 1) && - (s->failed && (i == r6s->failed_num[0] || - i == r6s->failed_num[1]))) { - pr_debug("Computing stripe %llu block %d\n", - (unsigned long long)sh->sector, i); - compute_block_1(sh, i, 0); - s->uptodate++; - } else if ( s->uptodate == disks-2 && s->failed >= 2 ) { - /* Computing 2-failure is *very* expensive; only - * do it if failed >= 2 - */ - int other; - for (other = disks; other--; ) { - if (other == i) - continue; - if (!test_bit(R5_UPTODATE, - &sh->dev[other].flags)) - break; - } - BUG_ON(other < 0); - pr_debug("Computing stripe %llu blocks %d,%d\n", - (unsigned long long)sh->sector, - i, other); - compute_block_2(sh, i, other); - s->uptodate += 2; - } else if (test_bit(R5_Insync, &dev->flags)) { - set_bit(R5_LOCKED, &dev->flags); - set_bit(R5_Wantread, &dev->flags); - s->locked++; - pr_debug("Reading block %d (sync=%d)\n", - i, s->syncing); + pr_debug("Computing stripe %llu block %d\n", + (unsigned long long)sh->sector, disk_idx); + set_bit(STRIPE_COMPUTE_RUN, &sh->state); + set_bit(STRIPE_OP_COMPUTE_BLK, &s->ops_request); + set_bit(R5_Wantcompute, &dev->flags); + sh->ops.target = disk_idx; + sh->ops.target2 = -1; /* no 2nd target */ + s->req_compute = 1; + s->uptodate++; + return 1; + } else if (s->uptodate == disks-2 && s->failed >= 2) { + /* Computing 2-failure is *very* expensive; only + * do it if failed >= 2 + */ + int other; + for (other = disks; other--; ) { + if (other == disk_idx) + continue; + if (!test_bit(R5_UPTODATE, + &sh->dev[other].flags)) + break; } + BUG_ON(other < 0); + pr_debug("Computing stripe %llu blocks %d,%d\n", + (unsigned long long)sh->sector, + disk_idx, other); + set_bit(STRIPE_COMPUTE_RUN, &sh->state); + set_bit(STRIPE_OP_COMPUTE_BLK, &s->ops_request); + set_bit(R5_Wantcompute, &sh->dev[disk_idx].flags); + set_bit(R5_Wantcompute, &sh->dev[other].flags); + sh->ops.target = disk_idx; + sh->ops.target2 = other; + s->uptodate += 2; + s->req_compute = 1; + return 1; + } else if (test_bit(R5_Insync, &dev->flags)) { + set_bit(R5_LOCKED, &dev->flags); + set_bit(R5_Wantread, &dev->flags); + s->locked++; + pr_debug("Reading block %d (sync=%d)\n", + disk_idx, s->syncing); } } + + return 0; +} + +/** + * handle_stripe_fill6 - read or compute data to satisfy pending requests. + */ +static void handle_stripe_fill6(struct stripe_head *sh, + struct stripe_head_state *s, struct r6_state *r6s, + int disks) +{ + int i; + + /* look for blocks to read/compute, skip this if a compute + * is already in flight, or if the stripe contents are in the + * midst of changing due to a write + */ + if (!test_bit(STRIPE_COMPUTE_RUN, &sh->state) && !sh->check_state && + !sh->reconstruct_state) + for (i = disks; i--; ) + if (fetch_block6(sh, s, r6s, i, disks)) + break; set_bit(STRIPE_HANDLE, &sh->state); } @@ -2361,114 +2551,61 @@ static void handle_stripe_dirtying5(raid5_conf_t *conf, */ /* since handle_stripe can be called at any time we need to handle the * case where a compute block operation has been submitted and then a - * subsequent call wants to start a write request. raid5_run_ops only - * handles the case where compute block and postxor are requested + * subsequent call wants to start a write request. raid_run_ops only + * handles the case where compute block and reconstruct are requested * simultaneously. If this is not the case then new writes need to be * held off until the compute completes. */ if ((s->req_compute || !test_bit(STRIPE_COMPUTE_RUN, &sh->state)) && (s->locked == 0 && (rcw == 0 || rmw == 0) && !test_bit(STRIPE_BIT_DELAY, &sh->state))) - schedule_reconstruction5(sh, s, rcw == 0, 0); + schedule_reconstruction(sh, s, rcw == 0, 0); } static void handle_stripe_dirtying6(raid5_conf_t *conf, struct stripe_head *sh, struct stripe_head_state *s, struct r6_state *r6s, int disks) { - int rcw = 0, must_compute = 0, pd_idx = sh->pd_idx, i; + int rcw = 0, pd_idx = sh->pd_idx, i; int qd_idx = sh->qd_idx; + + set_bit(STRIPE_HANDLE, &sh->state); for (i = disks; i--; ) { struct r5dev *dev = &sh->dev[i]; - /* Would I have to read this buffer for reconstruct_write */ - if (!test_bit(R5_OVERWRITE, &dev->flags) - && i != pd_idx && i != qd_idx - && (!test_bit(R5_LOCKED, &dev->flags) - ) && - !test_bit(R5_UPTODATE, &dev->flags)) { - if (test_bit(R5_Insync, &dev->flags)) rcw++; - else { - pr_debug("raid6: must_compute: " - "disk %d flags=%#lx\n", i, dev->flags); - must_compute++; + /* check if we haven't enough data */ + if (!test_bit(R5_OVERWRITE, &dev->flags) && + i != pd_idx && i != qd_idx && + !test_bit(R5_LOCKED, &dev->flags) && + !(test_bit(R5_UPTODATE, &dev->flags) || + test_bit(R5_Wantcompute, &dev->flags))) { + rcw++; + if (!test_bit(R5_Insync, &dev->flags)) + continue; /* it's a failed drive */ + + if ( + test_bit(STRIPE_PREREAD_ACTIVE, &sh->state)) { + pr_debug("Read_old stripe %llu " + "block %d for Reconstruct\n", + (unsigned long long)sh->sector, i); + set_bit(R5_LOCKED, &dev->flags); + set_bit(R5_Wantread, &dev->flags); + s->locked++; + } else { + pr_debug("Request delayed stripe %llu " + "block %d for Reconstruct\n", + (unsigned long long)sh->sector, i); + set_bit(STRIPE_DELAYED, &sh->state); + set_bit(STRIPE_HANDLE, &sh->state); } } } - pr_debug("for sector %llu, rcw=%d, must_compute=%d\n", - (unsigned long long)sh->sector, rcw, must_compute); - set_bit(STRIPE_HANDLE, &sh->state); - - if (rcw > 0) - /* want reconstruct write, but need to get some data */ - for (i = disks; i--; ) { - struct r5dev *dev = &sh->dev[i]; - if (!test_bit(R5_OVERWRITE, &dev->flags) - && !(s->failed == 0 && (i == pd_idx || i == qd_idx)) - && !test_bit(R5_LOCKED, &dev->flags) && - !test_bit(R5_UPTODATE, &dev->flags) && - test_bit(R5_Insync, &dev->flags)) { - if ( - test_bit(STRIPE_PREREAD_ACTIVE, &sh->state)) { - pr_debug("Read_old stripe %llu " - "block %d for Reconstruct\n", - (unsigned long long)sh->sector, i); - set_bit(R5_LOCKED, &dev->flags); - set_bit(R5_Wantread, &dev->flags); - s->locked++; - } else { - pr_debug("Request delayed stripe %llu " - "block %d for Reconstruct\n", - (unsigned long long)sh->sector, i); - set_bit(STRIPE_DELAYED, &sh->state); - set_bit(STRIPE_HANDLE, &sh->state); - } - } - } /* now if nothing is locked, and if we have enough data, we can start a * write request */ - if (s->locked == 0 && rcw == 0 && + if ((s->req_compute || !test_bit(STRIPE_COMPUTE_RUN, &sh->state)) && + s->locked == 0 && rcw == 0 && !test_bit(STRIPE_BIT_DELAY, &sh->state)) { - if (must_compute > 0) { - /* We have failed blocks and need to compute them */ - switch (s->failed) { - case 0: - BUG(); - case 1: - compute_block_1(sh, r6s->failed_num[0], 0); - break; - case 2: - compute_block_2(sh, r6s->failed_num[0], - r6s->failed_num[1]); - break; - default: /* This request should have been failed? */ - BUG(); - } - } - - pr_debug("Computing parity for stripe %llu\n", - (unsigned long long)sh->sector); - compute_parity6(sh, RECONSTRUCT_WRITE); - /* now every locked buffer is ready to be written */ - for (i = disks; i--; ) - if (test_bit(R5_LOCKED, &sh->dev[i].flags)) { - pr_debug("Writing stripe %llu block %d\n", - (unsigned long long)sh->sector, i); - s->locked++; - set_bit(R5_Wantwrite, &sh->dev[i].flags); - } - if (s->locked == disks) - if (!test_and_set_bit(STRIPE_FULL_WRITE, &sh->state)) - atomic_inc(&conf->pending_full_writes); - /* after a RECONSTRUCT_WRITE, the stripe MUST be in-sync */ - set_bit(STRIPE_INSYNC, &sh->state); - - if (test_and_clear_bit(STRIPE_PREREAD_ACTIVE, &sh->state)) { - atomic_dec(&conf->preread_active_stripes); - if (atomic_read(&conf->preread_active_stripes) < - IO_THRESHOLD) - md_wakeup_thread(conf->mddev->thread); - } + schedule_reconstruction(sh, s, 1, 0); } } @@ -2527,7 +2664,7 @@ static void handle_parity_checks5(raid5_conf_t *conf, struct stripe_head *sh, * we are done. Otherwise update the mismatch count and repair * parity if !MD_RECOVERY_CHECK */ - if (sh->ops.zero_sum_result == 0) + if ((sh->ops.zero_sum_result & SUM_CHECK_P_RESULT) == 0) /* parity is correct (on disc, * not in buffer any more) */ @@ -2544,6 +2681,7 @@ static void handle_parity_checks5(raid5_conf_t *conf, struct stripe_head *sh, set_bit(R5_Wantcompute, &sh->dev[sh->pd_idx].flags); sh->ops.target = sh->pd_idx; + sh->ops.target2 = -1; s->uptodate++; } } @@ -2560,67 +2698,74 @@ static void handle_parity_checks5(raid5_conf_t *conf, struct stripe_head *sh, static void handle_parity_checks6(raid5_conf_t *conf, struct stripe_head *sh, - struct stripe_head_state *s, - struct r6_state *r6s, struct page *tmp_page, - int disks) + struct stripe_head_state *s, + struct r6_state *r6s, int disks) { - int update_p = 0, update_q = 0; - struct r5dev *dev; int pd_idx = sh->pd_idx; int qd_idx = sh->qd_idx; + struct r5dev *dev; set_bit(STRIPE_HANDLE, &sh->state); BUG_ON(s->failed > 2); - BUG_ON(s->uptodate < disks); + /* Want to check and possibly repair P and Q. * However there could be one 'failed' device, in which * case we can only check one of them, possibly using the * other to generate missing data */ - /* If !tmp_page, we cannot do the calculations, - * but as we have set STRIPE_HANDLE, we will soon be called - * by stripe_handle with a tmp_page - just wait until then. - */ - if (tmp_page) { + switch (sh->check_state) { + case check_state_idle: + /* start a new check operation if there are < 2 failures */ if (s->failed == r6s->q_failed) { - /* The only possible failed device holds 'Q', so it + /* The only possible failed device holds Q, so it * makes sense to check P (If anything else were failed, * we would have used P to recreate it). */ - compute_block_1(sh, pd_idx, 1); - if (!page_is_zero(sh->dev[pd_idx].page)) { - compute_block_1(sh, pd_idx, 0); - update_p = 1; - } + sh->check_state = check_state_run; } if (!r6s->q_failed && s->failed < 2) { - /* q is not failed, and we didn't use it to generate + /* Q is not failed, and we didn't use it to generate * anything, so it makes sense to check it */ - memcpy(page_address(tmp_page), - page_address(sh->dev[qd_idx].page), - STRIPE_SIZE); - compute_parity6(sh, UPDATE_PARITY); - if (memcmp(page_address(tmp_page), - page_address(sh->dev[qd_idx].page), - STRIPE_SIZE) != 0) { - clear_bit(STRIPE_INSYNC, &sh->state); - update_q = 1; - } + if (sh->check_state == check_state_run) + sh->check_state = check_state_run_pq; + else + sh->check_state = check_state_run_q; } - if (update_p || update_q) { - conf->mddev->resync_mismatches += STRIPE_SECTORS; - if (test_bit(MD_RECOVERY_CHECK, &conf->mddev->recovery)) - /* don't try to repair!! */ - update_p = update_q = 0; + + /* discard potentially stale zero_sum_result */ + sh->ops.zero_sum_result = 0; + + if (sh->check_state == check_state_run) { + /* async_xor_zero_sum destroys the contents of P */ + clear_bit(R5_UPTODATE, &sh->dev[pd_idx].flags); + s->uptodate--; } + if (sh->check_state >= check_state_run && + sh->check_state <= check_state_run_pq) { + /* async_syndrome_zero_sum preserves P and Q, so + * no need to mark them !uptodate here + */ + set_bit(STRIPE_OP_CHECK, &s->ops_request); + break; + } + + /* we have 2-disk failure */ + BUG_ON(s->failed != 2); + /* fall through */ + case check_state_compute_result: + sh->check_state = check_state_idle; + + /* check that a write has not made the stripe insync */ + if (test_bit(STRIPE_INSYNC, &sh->state)) + break; /* now write out any block on a failed drive, - * or P or Q if they need it + * or P or Q if they were recomputed */ - + BUG_ON(s->uptodate < disks - 1); /* We don't need Q to recover */ if (s->failed == 2) { dev = &sh->dev[r6s->failed_num[1]]; s->locked++; @@ -2633,14 +2778,13 @@ static void handle_parity_checks6(raid5_conf_t *conf, struct stripe_head *sh, set_bit(R5_LOCKED, &dev->flags); set_bit(R5_Wantwrite, &dev->flags); } - - if (update_p) { + if (sh->ops.zero_sum_result & SUM_CHECK_P_RESULT) { dev = &sh->dev[pd_idx]; s->locked++; set_bit(R5_LOCKED, &dev->flags); set_bit(R5_Wantwrite, &dev->flags); } - if (update_q) { + if (sh->ops.zero_sum_result & SUM_CHECK_Q_RESULT) { dev = &sh->dev[qd_idx]; s->locked++; set_bit(R5_LOCKED, &dev->flags); @@ -2649,6 +2793,70 @@ static void handle_parity_checks6(raid5_conf_t *conf, struct stripe_head *sh, clear_bit(STRIPE_DEGRADED, &sh->state); set_bit(STRIPE_INSYNC, &sh->state); + break; + case check_state_run: + case check_state_run_q: + case check_state_run_pq: + break; /* we will be called again upon completion */ + case check_state_check_result: + sh->check_state = check_state_idle; + + /* handle a successful check operation, if parity is correct + * we are done. Otherwise update the mismatch count and repair + * parity if !MD_RECOVERY_CHECK + */ + if (sh->ops.zero_sum_result == 0) { + /* both parities are correct */ + if (!s->failed) + set_bit(STRIPE_INSYNC, &sh->state); + else { + /* in contrast to the raid5 case we can validate + * parity, but still have a failure to write + * back + */ + sh->check_state = check_state_compute_result; + /* Returning at this point means that we may go + * off and bring p and/or q uptodate again so + * we make sure to check zero_sum_result again + * to verify if p or q need writeback + */ + } + } else { + conf->mddev->resync_mismatches += STRIPE_SECTORS; + if (test_bit(MD_RECOVERY_CHECK, &conf->mddev->recovery)) + /* don't try to repair!! */ + set_bit(STRIPE_INSYNC, &sh->state); + else { + int *target = &sh->ops.target; + + sh->ops.target = -1; + sh->ops.target2 = -1; + sh->check_state = check_state_compute_run; + set_bit(STRIPE_COMPUTE_RUN, &sh->state); + set_bit(STRIPE_OP_COMPUTE_BLK, &s->ops_request); + if (sh->ops.zero_sum_result & SUM_CHECK_P_RESULT) { + set_bit(R5_Wantcompute, + &sh->dev[pd_idx].flags); + *target = pd_idx; + target = &sh->ops.target2; + s->uptodate++; + } + if (sh->ops.zero_sum_result & SUM_CHECK_Q_RESULT) { + set_bit(R5_Wantcompute, + &sh->dev[qd_idx].flags); + *target = qd_idx; + s->uptodate++; + } + } + } + break; + case check_state_compute_run: + break; + default: + printk(KERN_ERR "%s: unknown check_state: %d sector: %llu\n", + __func__, sh->check_state, + (unsigned long long) sh->sector); + BUG(); } } @@ -2666,6 +2874,7 @@ static void handle_stripe_expansion(raid5_conf_t *conf, struct stripe_head *sh, if (i != sh->pd_idx && i != sh->qd_idx) { int dd_idx, j; struct stripe_head *sh2; + struct async_submit_ctl submit; sector_t bn = compute_blocknr(sh, i, 1); sector_t s = raid5_compute_sector(conf, bn, 0, @@ -2685,9 +2894,10 @@ static void handle_stripe_expansion(raid5_conf_t *conf, struct stripe_head *sh, } /* place all the copies on one channel */ + init_async_submit(&submit, 0, tx, NULL, NULL, NULL); tx = async_memcpy(sh2->dev[dd_idx].page, - sh->dev[i].page, 0, 0, STRIPE_SIZE, - ASYNC_TX_DEP_ACK, tx, NULL, NULL); + sh->dev[i].page, 0, 0, STRIPE_SIZE, + &submit); set_bit(R5_Expanded, &sh2->dev[dd_idx].flags); set_bit(R5_UPTODATE, &sh2->dev[dd_idx].flags); @@ -2728,7 +2938,7 @@ static void handle_stripe_expansion(raid5_conf_t *conf, struct stripe_head *sh, * */ -static bool handle_stripe5(struct stripe_head *sh) +static void handle_stripe5(struct stripe_head *sh) { raid5_conf_t *conf = sh->raid_conf; int disks = sh->disks, i; @@ -2756,7 +2966,8 @@ static bool handle_stripe5(struct stripe_head *sh) rcu_read_lock(); for (i=disks; i--; ) { mdk_rdev_t *rdev; - struct r5dev *dev = &sh->dev[i]; + + dev = &sh->dev[i]; clear_bit(R5_Insync, &dev->flags); pr_debug("check %d: state 0x%lx toread %p read %p write %p " @@ -2973,7 +3184,7 @@ static bool handle_stripe5(struct stripe_head *sh) /* Need to write out all blocks after computing parity */ sh->disks = conf->raid_disks; stripe_set_idx(sh->sector, conf, 0, sh); - schedule_reconstruction5(sh, &s, 1, 1); + schedule_reconstruction(sh, &s, 1, 1); } else if (s.expanded && !sh->reconstruct_state && s.locked == 0) { clear_bit(STRIPE_EXPAND_READY, &sh->state); atomic_dec(&conf->reshape_stripes); @@ -2993,16 +3204,14 @@ static bool handle_stripe5(struct stripe_head *sh) md_wait_for_blocked_rdev(blocked_rdev, conf->mddev); if (s.ops_request) - raid5_run_ops(sh, s.ops_request); + raid_run_ops(sh, s.ops_request); ops_run_io(sh, &s); return_io(return_bi); - - return blocked_rdev == NULL; } -static bool handle_stripe6(struct stripe_head *sh, struct page *tmp_page) +static void handle_stripe6(struct stripe_head *sh) { raid5_conf_t *conf = sh->raid_conf; int disks = sh->disks; @@ -3014,9 +3223,10 @@ static bool handle_stripe6(struct stripe_head *sh, struct page *tmp_page) mdk_rdev_t *blocked_rdev = NULL; pr_debug("handling stripe %llu, state=%#lx cnt=%d, " - "pd_idx=%d, qd_idx=%d\n", + "pd_idx=%d, qd_idx=%d\n, check:%d, reconstruct:%d\n", (unsigned long long)sh->sector, sh->state, - atomic_read(&sh->count), pd_idx, qd_idx); + atomic_read(&sh->count), pd_idx, qd_idx, + sh->check_state, sh->reconstruct_state); memset(&s, 0, sizeof(s)); spin_lock(&sh->lock); @@ -3036,35 +3246,26 @@ static bool handle_stripe6(struct stripe_head *sh, struct page *tmp_page) pr_debug("check %d: state 0x%lx read %p write %p written %p\n", i, dev->flags, dev->toread, dev->towrite, dev->written); - /* maybe we can reply to a read */ - if (test_bit(R5_UPTODATE, &dev->flags) && dev->toread) { - struct bio *rbi, *rbi2; - pr_debug("Return read for disc %d\n", i); - spin_lock_irq(&conf->device_lock); - rbi = dev->toread; - dev->toread = NULL; - if (test_and_clear_bit(R5_Overlap, &dev->flags)) - wake_up(&conf->wait_for_overlap); - spin_unlock_irq(&conf->device_lock); - while (rbi && rbi->bi_sector < dev->sector + STRIPE_SECTORS) { - copy_data(0, rbi, dev->page, dev->sector); - rbi2 = r5_next_bio(rbi, dev->sector); - spin_lock_irq(&conf->device_lock); - if (!raid5_dec_bi_phys_segments(rbi)) { - rbi->bi_next = return_bi; - return_bi = rbi; - } - spin_unlock_irq(&conf->device_lock); - rbi = rbi2; - } - } + /* maybe we can reply to a read + * + * new wantfill requests are only permitted while + * ops_complete_biofill is guaranteed to be inactive + */ + if (test_bit(R5_UPTODATE, &dev->flags) && dev->toread && + !test_bit(STRIPE_BIOFILL_RUN, &sh->state)) + set_bit(R5_Wantfill, &dev->flags); /* now count some things */ if (test_bit(R5_LOCKED, &dev->flags)) s.locked++; if (test_bit(R5_UPTODATE, &dev->flags)) s.uptodate++; + if (test_bit(R5_Wantcompute, &dev->flags)) { + s.compute++; + BUG_ON(s.compute > 2); + } - - if (dev->toread) + if (test_bit(R5_Wantfill, &dev->flags)) { + s.to_fill++; + } else if (dev->toread) s.to_read++; if (dev->towrite) { s.to_write++; @@ -3105,6 +3306,11 @@ static bool handle_stripe6(struct stripe_head *sh, struct page *tmp_page) blocked_rdev = NULL; } + if (s.to_fill && !test_bit(STRIPE_BIOFILL_RUN, &sh->state)) { + set_bit(STRIPE_OP_BIOFILL, &s.ops_request); + set_bit(STRIPE_BIOFILL_RUN, &sh->state); + } + pr_debug("locked=%d uptodate=%d to_read=%d" " to_write=%d failed=%d failed_num=%d,%d\n", s.locked, s.uptodate, s.to_read, s.to_write, s.failed, @@ -3145,19 +3351,62 @@ static bool handle_stripe6(struct stripe_head *sh, struct page *tmp_page) * or to load a block that is being partially written. */ if (s.to_read || s.non_overwrite || (s.to_write && s.failed) || - (s.syncing && (s.uptodate < disks)) || s.expanding) + (s.syncing && (s.uptodate + s.compute < disks)) || s.expanding) handle_stripe_fill6(sh, &s, &r6s, disks); - /* now to consider writing and what else, if anything should be read */ - if (s.to_write) + /* Now we check to see if any write operations have recently + * completed + */ + if (sh->reconstruct_state == reconstruct_state_drain_result) { + int qd_idx = sh->qd_idx; + + sh->reconstruct_state = reconstruct_state_idle; + /* All the 'written' buffers and the parity blocks are ready to + * be written back to disk + */ + BUG_ON(!test_bit(R5_UPTODATE, &sh->dev[sh->pd_idx].flags)); + BUG_ON(!test_bit(R5_UPTODATE, &sh->dev[qd_idx].flags)); + for (i = disks; i--; ) { + dev = &sh->dev[i]; + if (test_bit(R5_LOCKED, &dev->flags) && + (i == sh->pd_idx || i == qd_idx || + dev->written)) { + pr_debug("Writing block %d\n", i); + BUG_ON(!test_bit(R5_UPTODATE, &dev->flags)); + set_bit(R5_Wantwrite, &dev->flags); + if (!test_bit(R5_Insync, &dev->flags) || + ((i == sh->pd_idx || i == qd_idx) && + s.failed == 0)) + set_bit(STRIPE_INSYNC, &sh->state); + } + } + if (test_and_clear_bit(STRIPE_PREREAD_ACTIVE, &sh->state)) { + atomic_dec(&conf->preread_active_stripes); + if (atomic_read(&conf->preread_active_stripes) < + IO_THRESHOLD) + md_wakeup_thread(conf->mddev->thread); + } + } + + /* Now to consider new write requests and what else, if anything + * should be read. We do not handle new writes when: + * 1/ A 'write' operation (copy+gen_syndrome) is already in flight. + * 2/ A 'check' operation is in flight, as it may clobber the parity + * block. + */ + if (s.to_write && !sh->reconstruct_state && !sh->check_state) handle_stripe_dirtying6(conf, sh, &s, &r6s, disks); /* maybe we need to check and possibly fix the parity for this stripe * Any reads will already have been scheduled, so we just see if enough - * data is available + * data is available. The parity check is held off while parity + * dependent operations are in flight. */ - if (s.syncing && s.locked == 0 && !test_bit(STRIPE_INSYNC, &sh->state)) - handle_parity_checks6(conf, sh, &s, &r6s, tmp_page, disks); + if (sh->check_state || + (s.syncing && s.locked == 0 && + !test_bit(STRIPE_COMPUTE_RUN, &sh->state) && + !test_bit(STRIPE_INSYNC, &sh->state))) + handle_parity_checks6(conf, sh, &s, &r6s, disks); if (s.syncing && s.locked == 0 && test_bit(STRIPE_INSYNC, &sh->state)) { md_done_sync(conf->mddev, STRIPE_SECTORS,1); @@ -3178,15 +3427,29 @@ static bool handle_stripe6(struct stripe_head *sh, struct page *tmp_page) set_bit(R5_Wantwrite, &dev->flags); set_bit(R5_ReWrite, &dev->flags); set_bit(R5_LOCKED, &dev->flags); + s.locked++; } else { /* let's read it back */ set_bit(R5_Wantread, &dev->flags); set_bit(R5_LOCKED, &dev->flags); + s.locked++; } } } - if (s.expanded && test_bit(STRIPE_EXPANDING, &sh->state)) { + /* Finish reconstruct operations initiated by the expansion process */ + if (sh->reconstruct_state == reconstruct_state_result) { + sh->reconstruct_state = reconstruct_state_idle; + clear_bit(STRIPE_EXPANDING, &sh->state); + for (i = conf->raid_disks; i--; ) { + set_bit(R5_Wantwrite, &sh->dev[i].flags); + set_bit(R5_LOCKED, &sh->dev[i].flags); + s.locked++; + } + } + + if (s.expanded && test_bit(STRIPE_EXPANDING, &sh->state) && + !sh->reconstruct_state) { struct stripe_head *sh2 = get_active_stripe(conf, sh->sector, 1, 1, 1); if (sh2 && test_bit(STRIPE_EXPAND_SOURCE, &sh2->state)) { @@ -3207,14 +3470,8 @@ static bool handle_stripe6(struct stripe_head *sh, struct page *tmp_page) /* Need to write out all blocks after computing P&Q */ sh->disks = conf->raid_disks; stripe_set_idx(sh->sector, conf, 0, sh); - compute_parity6(sh, RECONSTRUCT_WRITE); - for (i = conf->raid_disks ; i-- ; ) { - set_bit(R5_LOCKED, &sh->dev[i].flags); - s.locked++; - set_bit(R5_Wantwrite, &sh->dev[i].flags); - } - clear_bit(STRIPE_EXPANDING, &sh->state); - } else if (s.expanded) { + schedule_reconstruction(sh, &s, 1, 1); + } else if (s.expanded && !sh->reconstruct_state && s.locked == 0) { clear_bit(STRIPE_EXPAND_READY, &sh->state); atomic_dec(&conf->reshape_stripes); wake_up(&conf->wait_for_overlap); @@ -3232,24 +3489,22 @@ static bool handle_stripe6(struct stripe_head *sh, struct page *tmp_page) if (unlikely(blocked_rdev)) md_wait_for_blocked_rdev(blocked_rdev, conf->mddev); + if (s.ops_request) + raid_run_ops(sh, s.ops_request); + ops_run_io(sh, &s); return_io(return_bi); - - return blocked_rdev == NULL; } -/* returns true if the stripe was handled */ -static bool handle_stripe(struct stripe_head *sh, struct page *tmp_page) +static void handle_stripe(struct stripe_head *sh) { if (sh->raid_conf->level == 6) - return handle_stripe6(sh, tmp_page); + handle_stripe6(sh); else - return handle_stripe5(sh); + handle_stripe5(sh); } - - static void raid5_activate_delayed(raid5_conf_t *conf) { if (atomic_read(&conf->preread_active_stripes) < IO_THRESHOLD) { @@ -3285,9 +3540,10 @@ static void unplug_slaves(mddev_t *mddev) { raid5_conf_t *conf = mddev->private; int i; + int devs = max(conf->raid_disks, conf->previous_raid_disks); rcu_read_lock(); - for (i = 0; i < conf->raid_disks; i++) { + for (i = 0; i < devs; i++) { mdk_rdev_t *rdev = rcu_dereference(conf->disks[i].rdev); if (rdev && !test_bit(Faulty, &rdev->flags) && atomic_read(&rdev->nr_pending)) { struct request_queue *r_queue = bdev_get_queue(rdev->bdev); @@ -3331,6 +3587,9 @@ static int raid5_congested(void *data, int bits) /* No difference between reads and writes. Just check * how busy the stripe_cache is */ + + if (mddev_congested(mddev, bits)) + return 1; if (conf->inactive_blocked) return 1; if (conf->quiesce) @@ -3790,6 +4049,8 @@ static sector_t reshape_request(mddev_t *mddev, sector_t sector_nr, int *skipped sector_nr = conf->reshape_progress; sector_div(sector_nr, new_data_disks); if (sector_nr) { + mddev->curr_resync_completed = sector_nr; + sysfs_notify(&mddev->kobj, NULL, "sync_completed"); *skipped = 1; return sector_nr; } @@ -3880,7 +4141,7 @@ static sector_t reshape_request(mddev_t *mddev, sector_t sector_nr, int *skipped INIT_LIST_HEAD(&stripes); for (i = 0; i < reshape_sectors; i += STRIPE_SECTORS) { int j; - int skipped = 0; + int skipped_disk = 0; sh = get_active_stripe(conf, stripe_addr+i, 0, 0, 1); set_bit(STRIPE_EXPANDING, &sh->state); atomic_inc(&conf->reshape_stripes); @@ -3896,14 +4157,14 @@ static sector_t reshape_request(mddev_t *mddev, sector_t sector_nr, int *skipped continue; s = compute_blocknr(sh, j, 0); if (s < raid5_size(mddev, 0, 0)) { - skipped = 1; + skipped_disk = 1; continue; } memset(page_address(sh->dev[j].page), 0, STRIPE_SIZE); set_bit(R5_Expanded, &sh->dev[j].flags); set_bit(R5_UPTODATE, &sh->dev[j].flags); } - if (!skipped) { + if (!skipped_disk) { set_bit(STRIPE_EXPAND_READY, &sh->state); set_bit(STRIPE_HANDLE, &sh->state); } @@ -4056,9 +4317,7 @@ static inline sector_t sync_request(mddev_t *mddev, sector_t sector_nr, int *ski clear_bit(STRIPE_INSYNC, &sh->state); spin_unlock(&sh->lock); - /* wait for any blocked device to be handled */ - while(unlikely(!handle_stripe(sh, NULL))) - ; + handle_stripe(sh); release_stripe(sh); return STRIPE_SECTORS; @@ -4114,7 +4373,7 @@ static int retry_aligned_read(raid5_conf_t *conf, struct bio *raid_bio) return handled; } - handle_stripe(sh, NULL); + handle_stripe(sh); release_stripe(sh); handled++; } @@ -4129,7 +4388,6 @@ static int retry_aligned_read(raid5_conf_t *conf, struct bio *raid_bio) } - /* * This is our raid5 kernel thread. * @@ -4178,8 +4436,9 @@ static void raid5d(mddev_t *mddev) spin_unlock_irq(&conf->device_lock); handled++; - handle_stripe(sh, conf->spare_page); + handle_stripe(sh); release_stripe(sh); + cond_resched(); spin_lock_irq(&conf->device_lock); } @@ -4306,32 +4565,131 @@ raid5_size(mddev_t *mddev, sector_t sectors, int raid_disks) if (!sectors) sectors = mddev->dev_sectors; - if (!raid_disks) { + if (!raid_disks) /* size is defined by the smallest of previous and new size */ - if (conf->raid_disks < conf->previous_raid_disks) - raid_disks = conf->raid_disks; - else - raid_disks = conf->previous_raid_disks; - } + raid_disks = min(conf->raid_disks, conf->previous_raid_disks); sectors &= ~((sector_t)mddev->chunk_sectors - 1); sectors &= ~((sector_t)mddev->new_chunk_sectors - 1); return sectors * (raid_disks - conf->max_degraded); } +static void raid5_free_percpu(raid5_conf_t *conf) +{ + struct raid5_percpu *percpu; + unsigned long cpu; + + if (!conf->percpu) + return; + + get_online_cpus(); + for_each_possible_cpu(cpu) { + percpu = per_cpu_ptr(conf->percpu, cpu); + safe_put_page(percpu->spare_page); + kfree(percpu->scribble); + } +#ifdef CONFIG_HOTPLUG_CPU + unregister_cpu_notifier(&conf->cpu_notify); +#endif + put_online_cpus(); + + free_percpu(conf->percpu); +} + static void free_conf(raid5_conf_t *conf) { shrink_stripes(conf); - safe_put_page(conf->spare_page); + raid5_free_percpu(conf); kfree(conf->disks); kfree(conf->stripe_hashtbl); kfree(conf); } +#ifdef CONFIG_HOTPLUG_CPU +static int raid456_cpu_notify(struct notifier_block *nfb, unsigned long action, + void *hcpu) +{ + raid5_conf_t *conf = container_of(nfb, raid5_conf_t, cpu_notify); + long cpu = (long)hcpu; + struct raid5_percpu *percpu = per_cpu_ptr(conf->percpu, cpu); + + switch (action) { + case CPU_UP_PREPARE: + case CPU_UP_PREPARE_FROZEN: + if (conf->level == 6 && !percpu->spare_page) + percpu->spare_page = alloc_page(GFP_KERNEL); + if (!percpu->scribble) + percpu->scribble = kmalloc(conf->scribble_len, GFP_KERNEL); + + if (!percpu->scribble || + (conf->level == 6 && !percpu->spare_page)) { + safe_put_page(percpu->spare_page); + kfree(percpu->scribble); + pr_err("%s: failed memory allocation for cpu%ld\n", + __func__, cpu); + return NOTIFY_BAD; + } + break; + case CPU_DEAD: + case CPU_DEAD_FROZEN: + safe_put_page(percpu->spare_page); + kfree(percpu->scribble); + percpu->spare_page = NULL; + percpu->scribble = NULL; + break; + default: + break; + } + return NOTIFY_OK; +} +#endif + +static int raid5_alloc_percpu(raid5_conf_t *conf) +{ + unsigned long cpu; + struct page *spare_page; + struct raid5_percpu *allcpus; + void *scribble; + int err; + + allcpus = alloc_percpu(struct raid5_percpu); + if (!allcpus) + return -ENOMEM; + conf->percpu = allcpus; + + get_online_cpus(); + err = 0; + for_each_present_cpu(cpu) { + if (conf->level == 6) { + spare_page = alloc_page(GFP_KERNEL); + if (!spare_page) { + err = -ENOMEM; + break; + } + per_cpu_ptr(conf->percpu, cpu)->spare_page = spare_page; + } + scribble = kmalloc(conf->scribble_len, GFP_KERNEL); + if (!scribble) { + err = -ENOMEM; + break; + } + per_cpu_ptr(conf->percpu, cpu)->scribble = scribble; + } +#ifdef CONFIG_HOTPLUG_CPU + conf->cpu_notify.notifier_call = raid456_cpu_notify; + conf->cpu_notify.priority = 0; + if (err == 0) + err = register_cpu_notifier(&conf->cpu_notify); +#endif + put_online_cpus(); + + return err; +} + static raid5_conf_t *setup_conf(mddev_t *mddev) { raid5_conf_t *conf; - int raid_disk, memory; + int raid_disk, memory, max_disks; mdk_rdev_t *rdev; struct disk_info *disk; @@ -4367,14 +4725,28 @@ static raid5_conf_t *setup_conf(mddev_t *mddev) conf = kzalloc(sizeof(raid5_conf_t), GFP_KERNEL); if (conf == NULL) goto abort; + spin_lock_init(&conf->device_lock); + init_waitqueue_head(&conf->wait_for_stripe); + init_waitqueue_head(&conf->wait_for_overlap); + INIT_LIST_HEAD(&conf->handle_list); + INIT_LIST_HEAD(&conf->hold_list); + INIT_LIST_HEAD(&conf->delayed_list); + INIT_LIST_HEAD(&conf->bitmap_list); + INIT_LIST_HEAD(&conf->inactive_list); + atomic_set(&conf->active_stripes, 0); + atomic_set(&conf->preread_active_stripes, 0); + atomic_set(&conf->active_aligned_reads, 0); + conf->bypass_threshold = BYPASS_THRESHOLD; conf->raid_disks = mddev->raid_disks; if (mddev->reshape_position == MaxSector) conf->previous_raid_disks = mddev->raid_disks; else conf->previous_raid_disks = mddev->raid_disks - mddev->delta_disks; + max_disks = max(conf->raid_disks, conf->previous_raid_disks); + conf->scribble_len = scribble_len(max_disks); - conf->disks = kzalloc(conf->raid_disks * sizeof(struct disk_info), + conf->disks = kzalloc(max_disks * sizeof(struct disk_info), GFP_KERNEL); if (!conf->disks) goto abort; @@ -4384,29 +4756,15 @@ static raid5_conf_t *setup_conf(mddev_t *mddev) if ((conf->stripe_hashtbl = kzalloc(PAGE_SIZE, GFP_KERNEL)) == NULL) goto abort; - if (mddev->new_level == 6) { - conf->spare_page = alloc_page(GFP_KERNEL); - if (!conf->spare_page) - goto abort; - } - spin_lock_init(&conf->device_lock); - init_waitqueue_head(&conf->wait_for_stripe); - init_waitqueue_head(&conf->wait_for_overlap); - INIT_LIST_HEAD(&conf->handle_list); - INIT_LIST_HEAD(&conf->hold_list); - INIT_LIST_HEAD(&conf->delayed_list); - INIT_LIST_HEAD(&conf->bitmap_list); - INIT_LIST_HEAD(&conf->inactive_list); - atomic_set(&conf->active_stripes, 0); - atomic_set(&conf->preread_active_stripes, 0); - atomic_set(&conf->active_aligned_reads, 0); - conf->bypass_threshold = BYPASS_THRESHOLD; + conf->level = mddev->new_level; + if (raid5_alloc_percpu(conf) != 0) + goto abort; pr_debug("raid5: run(%s) called.\n", mdname(mddev)); list_for_each_entry(rdev, &mddev->disks, same_set) { raid_disk = rdev->raid_disk; - if (raid_disk >= conf->raid_disks + if (raid_disk >= max_disks || raid_disk < 0) continue; disk = conf->disks + raid_disk; @@ -4438,7 +4796,7 @@ static raid5_conf_t *setup_conf(mddev_t *mddev) } memory = conf->max_nr_stripes * (sizeof(struct stripe_head) + - conf->raid_disks * ((sizeof(struct bio) + PAGE_SIZE))) / 1024; + max_disks * ((sizeof(struct bio) + PAGE_SIZE))) / 1024; if (grow_stripes(conf, conf->max_nr_stripes)) { printk(KERN_ERR "raid5: couldn't allocate %dkB for buffers\n", memory); @@ -4447,7 +4805,7 @@ static raid5_conf_t *setup_conf(mddev_t *mddev) printk(KERN_INFO "raid5: allocated %dkB for %s\n", memory, mdname(mddev)); - conf->thread = md_register_thread(raid5d, mddev, "%s_raid5"); + conf->thread = md_register_thread(raid5d, mddev, NULL); if (!conf->thread) { printk(KERN_ERR "raid5: couldn't allocate thread for %s\n", @@ -4465,11 +4823,40 @@ static raid5_conf_t *setup_conf(mddev_t *mddev) return ERR_PTR(-ENOMEM); } + +static int only_parity(int raid_disk, int algo, int raid_disks, int max_degraded) +{ + switch (algo) { + case ALGORITHM_PARITY_0: + if (raid_disk < max_degraded) + return 1; + break; + case ALGORITHM_PARITY_N: + if (raid_disk >= raid_disks - max_degraded) + return 1; + break; + case ALGORITHM_PARITY_0_6: + if (raid_disk == 0 || + raid_disk == raid_disks - 1) + return 1; + break; + case ALGORITHM_LEFT_ASYMMETRIC_6: + case ALGORITHM_RIGHT_ASYMMETRIC_6: + case ALGORITHM_LEFT_SYMMETRIC_6: + case ALGORITHM_RIGHT_SYMMETRIC_6: + if (raid_disk == raid_disks - 1) + return 1; + } + return 0; +} + static int run(mddev_t *mddev) { raid5_conf_t *conf; int working_disks = 0, chunk_size; + int dirty_parity_disks = 0; mdk_rdev_t *rdev; + sector_t reshape_offset = 0; if (mddev->recovery_cp != MaxSector) printk(KERN_NOTICE "raid5: %s is not clean" @@ -4503,6 +4890,7 @@ static int run(mddev_t *mddev) "on a stripe boundary\n"); return -EINVAL; } + reshape_offset = here_new * mddev->new_chunk_sectors; /* here_new is the stripe we will write to */ here_old = mddev->reshape_position; sector_div(here_old, mddev->chunk_sectors * @@ -4558,12 +4946,54 @@ static int run(mddev_t *mddev) /* * 0 for a fully functional array, 1 or 2 for a degraded array. */ - list_for_each_entry(rdev, &mddev->disks, same_set) - if (rdev->raid_disk >= 0 && - test_bit(In_sync, &rdev->flags)) + list_for_each_entry(rdev, &mddev->disks, same_set) { + if (rdev->raid_disk < 0) + continue; + if (test_bit(In_sync, &rdev->flags)) working_disks++; + /* This disc is not fully in-sync. However if it + * just stored parity (beyond the recovery_offset), + * when we don't need to be concerned about the + * array being dirty. + * When reshape goes 'backwards', we never have + * partially completed devices, so we only need + * to worry about reshape going forwards. + */ + /* Hack because v0.91 doesn't store recovery_offset properly. */ + if (mddev->major_version == 0 && + mddev->minor_version > 90) + rdev->recovery_offset = reshape_offset; + + printk("%d: w=%d pa=%d pr=%d m=%d a=%d r=%d op1=%d op2=%d\n", + rdev->raid_disk, working_disks, conf->prev_algo, + conf->previous_raid_disks, conf->max_degraded, + conf->algorithm, conf->raid_disks, + only_parity(rdev->raid_disk, + conf->prev_algo, + conf->previous_raid_disks, + conf->max_degraded), + only_parity(rdev->raid_disk, + conf->algorithm, + conf->raid_disks, + conf->max_degraded)); + if (rdev->recovery_offset < reshape_offset) { + /* We need to check old and new layout */ + if (!only_parity(rdev->raid_disk, + conf->algorithm, + conf->raid_disks, + conf->max_degraded)) + continue; + } + if (!only_parity(rdev->raid_disk, + conf->prev_algo, + conf->previous_raid_disks, + conf->max_degraded)) + continue; + dirty_parity_disks++; + } - mddev->degraded = conf->raid_disks - working_disks; + mddev->degraded = (max(conf->raid_disks, conf->previous_raid_disks) + - working_disks); if (mddev->degraded > conf->max_degraded) { printk(KERN_ERR "raid5: not enough operational devices for %s" @@ -4576,7 +5006,7 @@ static int run(mddev_t *mddev) mddev->dev_sectors &= ~(mddev->chunk_sectors - 1); mddev->resync_max_sectors = mddev->dev_sectors; - if (mddev->degraded > 0 && + if (mddev->degraded > dirty_parity_disks && mddev->recovery_cp != MaxSector) { if (mddev->ok_start_degraded) printk(KERN_WARNING @@ -4613,7 +5043,7 @@ static int run(mddev_t *mddev) set_bit(MD_RECOVERY_RESHAPE, &mddev->recovery); set_bit(MD_RECOVERY_RUNNING, &mddev->recovery); mddev->sync_thread = md_register_thread(md_do_sync, mddev, - "%s_reshape"); + "reshape"); } /* read-ahead size must cover two whole stripes, which is @@ -5002,9 +5432,11 @@ static int raid5_start_reshape(mddev_t *mddev) !test_bit(Faulty, &rdev->flags)) { if (raid5_add_disk(mddev, rdev) == 0) { char nm[20]; - set_bit(In_sync, &rdev->flags); + if (rdev->raid_disk >= conf->previous_raid_disks) + set_bit(In_sync, &rdev->flags); + else + rdev->recovery_offset = 0; added_devices++; - rdev->recovery_offset = 0; sprintf(nm, "rd%d", rdev->raid_disk); if (sysfs_create_link(&mddev->kobj, &rdev->kobj, nm)) @@ -5031,7 +5463,7 @@ static int raid5_start_reshape(mddev_t *mddev) set_bit(MD_RECOVERY_RESHAPE, &mddev->recovery); set_bit(MD_RECOVERY_RUNNING, &mddev->recovery); mddev->sync_thread = md_register_thread(md_do_sync, mddev, - "%s_reshape"); + "reshape"); if (!mddev->sync_thread) { mddev->recovery = 0; spin_lock_irq(&conf->device_lock); diff --git a/drivers/md/raid5.h b/drivers/md/raid5.h index 9459689c4ea0..dd708359b451 100644 --- a/drivers/md/raid5.h +++ b/drivers/md/raid5.h @@ -2,6 +2,7 @@ #define _RAID5_H #include <linux/raid/xor.h> +#include <linux/dmaengine.h> /* * @@ -175,7 +176,9 @@ */ enum check_states { check_state_idle = 0, - check_state_run, /* parity check */ + check_state_run, /* xor parity check */ + check_state_run_q, /* q-parity check */ + check_state_run_pq, /* pq dual parity check */ check_state_check_result, check_state_compute_run, /* parity repair */ check_state_compute_result, @@ -211,12 +214,20 @@ struct stripe_head { int disks; /* disks in stripe */ enum check_states check_state; enum reconstruct_states reconstruct_state; - /* stripe_operations + /** + * struct stripe_operations * @target - STRIPE_OP_COMPUTE_BLK target + * @target2 - 2nd compute target in the raid6 case + * @zero_sum_result - P and Q verification flags + * @request - async service request flags for raid_run_ops */ struct stripe_operations { - int target; - u32 zero_sum_result; + int target, target2; + enum sum_check_flags zero_sum_result; + #ifdef CONFIG_MULTICORE_RAID456 + unsigned long request; + wait_queue_head_t wait_for_ops; + #endif } ops; struct r5dev { struct bio req; @@ -291,6 +302,8 @@ struct r6_state { #define STRIPE_FULL_WRITE 13 /* all blocks are set to be overwritten */ #define STRIPE_BIOFILL_RUN 14 #define STRIPE_COMPUTE_RUN 15 +#define STRIPE_OPS_REQ_PENDING 16 + /* * Operation request flags */ @@ -298,7 +311,7 @@ struct r6_state { #define STRIPE_OP_COMPUTE_BLK 1 #define STRIPE_OP_PREXOR 2 #define STRIPE_OP_BIODRAIN 3 -#define STRIPE_OP_POSTXOR 4 +#define STRIPE_OP_RECONSTRUCT 4 #define STRIPE_OP_CHECK 5 /* @@ -385,8 +398,21 @@ struct raid5_private_data { * (fresh device added). * Cleared when a sync completes. */ - - struct page *spare_page; /* Used when checking P/Q in raid6 */ + /* per cpu variables */ + struct raid5_percpu { + struct page *spare_page; /* Used when checking P/Q in raid6 */ + void *scribble; /* space for constructing buffer + * lists and performing address + * conversions + */ + } *percpu; + size_t scribble_len; /* size of scribble region must be + * associated with conf to handle + * cpu hotplug while reshaping + */ +#ifdef CONFIG_HOTPLUG_CPU + struct notifier_block cpu_notify; +#endif /* * Free stripes pool @@ -462,7 +488,7 @@ static inline int algorithm_valid_raid6(int layout) { return (layout >= 0 && layout <= 5) || - (layout == 8 || layout == 10) + (layout >= 8 && layout <= 10) || (layout >= 16 && layout <= 20); } diff --git a/drivers/md/raid6altivec.uc b/drivers/md/raid6altivec.uc index 699dfeee4944..2654d5c854be 100644 --- a/drivers/md/raid6altivec.uc +++ b/drivers/md/raid6altivec.uc @@ -15,7 +15,7 @@ * * $#-way unrolled portable integer math RAID-6 instruction set * - * This file is postprocessed using unroll.pl + * This file is postprocessed using unroll.awk * * <benh> hpa: in process, * you can just "steal" the vec unit with enable_kernel_altivec() (but diff --git a/drivers/md/raid6int.uc b/drivers/md/raid6int.uc index f9bf9cba357f..d1e276a14fab 100644 --- a/drivers/md/raid6int.uc +++ b/drivers/md/raid6int.uc @@ -15,7 +15,7 @@ * * $#-way unrolled portable integer math RAID-6 instruction set * - * This file is postprocessed using unroll.pl + * This file is postprocessed using unroll.awk */ #include <linux/raid/pq.h> diff --git a/drivers/md/raid6test/Makefile b/drivers/md/raid6test/Makefile index 58ffdf4f5161..2874cbef529d 100644 --- a/drivers/md/raid6test/Makefile +++ b/drivers/md/raid6test/Makefile @@ -7,7 +7,7 @@ CC = gcc OPTFLAGS = -O2 # Adjust as desired CFLAGS = -I.. -I ../../../include -g $(OPTFLAGS) LD = ld -PERL = perl +AWK = awk AR = ar RANLIB = ranlib @@ -35,35 +35,35 @@ raid6.a: raid6int1.o raid6int2.o raid6int4.o raid6int8.o raid6int16.o \ raid6test: test.c raid6.a $(CC) $(CFLAGS) -o raid6test $^ -raid6altivec1.c: raid6altivec.uc ../unroll.pl - $(PERL) ../unroll.pl 1 < raid6altivec.uc > $@ +raid6altivec1.c: raid6altivec.uc ../unroll.awk + $(AWK) ../unroll.awk -vN=1 < raid6altivec.uc > $@ -raid6altivec2.c: raid6altivec.uc ../unroll.pl - $(PERL) ../unroll.pl 2 < raid6altivec.uc > $@ +raid6altivec2.c: raid6altivec.uc ../unroll.awk + $(AWK) ../unroll.awk -vN=2 < raid6altivec.uc > $@ -raid6altivec4.c: raid6altivec.uc ../unroll.pl - $(PERL) ../unroll.pl 4 < raid6altivec.uc > $@ +raid6altivec4.c: raid6altivec.uc ../unroll.awk + $(AWK) ../unroll.awk -vN=4 < raid6altivec.uc > $@ -raid6altivec8.c: raid6altivec.uc ../unroll.pl - $(PERL) ../unroll.pl 8 < raid6altivec.uc > $@ +raid6altivec8.c: raid6altivec.uc ../unroll.awk + $(AWK) ../unroll.awk -vN=8 < raid6altivec.uc > $@ -raid6int1.c: raid6int.uc ../unroll.pl - $(PERL) ../unroll.pl 1 < raid6int.uc > $@ +raid6int1.c: raid6int.uc ../unroll.awk + $(AWK) ../unroll.awk -vN=1 < raid6int.uc > $@ -raid6int2.c: raid6int.uc ../unroll.pl - $(PERL) ../unroll.pl 2 < raid6int.uc > $@ +raid6int2.c: raid6int.uc ../unroll.awk + $(AWK) ../unroll.awk -vN=2 < raid6int.uc > $@ -raid6int4.c: raid6int.uc ../unroll.pl - $(PERL) ../unroll.pl 4 < raid6int.uc > $@ +raid6int4.c: raid6int.uc ../unroll.awk + $(AWK) ../unroll.awk -vN=4 < raid6int.uc > $@ -raid6int8.c: raid6int.uc ../unroll.pl - $(PERL) ../unroll.pl 8 < raid6int.uc > $@ +raid6int8.c: raid6int.uc ../unroll.awk + $(AWK) ../unroll.awk -vN=8 < raid6int.uc > $@ -raid6int16.c: raid6int.uc ../unroll.pl - $(PERL) ../unroll.pl 16 < raid6int.uc > $@ +raid6int16.c: raid6int.uc ../unroll.awk + $(AWK) ../unroll.awk -vN=16 < raid6int.uc > $@ -raid6int32.c: raid6int.uc ../unroll.pl - $(PERL) ../unroll.pl 32 < raid6int.uc > $@ +raid6int32.c: raid6int.uc ../unroll.awk + $(AWK) ../unroll.awk -vN=32 < raid6int.uc > $@ raid6tables.c: mktables ./mktables > raid6tables.c diff --git a/drivers/md/unroll.awk b/drivers/md/unroll.awk new file mode 100644 index 000000000000..c6aa03631df8 --- /dev/null +++ b/drivers/md/unroll.awk @@ -0,0 +1,20 @@ + +# This filter requires one command line option of form -vN=n +# where n must be a decimal number. +# +# Repeat each input line containing $$ n times, replacing $$ with 0...n-1. +# Replace each $# with n, and each $* with a single $. + +BEGIN { + n = N + 0 +} +{ + if (/\$\$/) { rep = n } else { rep = 1 } + for (i = 0; i < rep; ++i) { + tmp = $0 + gsub(/\$\$/, i, tmp) + gsub(/\$\#/, n, tmp) + gsub(/\$\*/, "$", tmp) + print tmp + } +} diff --git a/drivers/md/unroll.pl b/drivers/md/unroll.pl deleted file mode 100644 index 3acc710a20ea..000000000000 --- a/drivers/md/unroll.pl +++ /dev/null @@ -1,24 +0,0 @@ -#!/usr/bin/perl -# -# Take a piece of C code and for each line which contains the sequence $$ -# repeat n times with $ replaced by 0...n-1; the sequence $# is replaced -# by the unrolling factor, and $* with a single $ -# - -($n) = @ARGV; -$n += 0; - -while ( defined($line = <STDIN>) ) { - if ( $line =~ /\$\$/ ) { - $rep = $n; - } else { - $rep = 1; - } - for ( $i = 0 ; $i < $rep ; $i++ ) { - $tmp = $line; - $tmp =~ s/\$\$/$i/g; - $tmp =~ s/\$\#/$n/g; - $tmp =~ s/\$\*/\$/g; - print $tmp; - } -} diff --git a/drivers/media/common/ir-functions.c b/drivers/media/common/ir-functions.c index 655474b29e21..abd4791acb0e 100644 --- a/drivers/media/common/ir-functions.c +++ b/drivers/media/common/ir-functions.c @@ -64,7 +64,7 @@ void ir_input_init(struct input_dev *dev, struct ir_input_state *ir, ir->ir_type = ir_type; - memset(ir->ir_codes, sizeof(ir->ir_codes), 0); + memset(ir->ir_codes, 0, sizeof(ir->ir_codes)); /* * FIXME: This is a temporary workaround to use the new IR tables diff --git a/drivers/media/common/tuners/tda18271-fe.c b/drivers/media/common/tuners/tda18271-fe.c index 64595112000d..3a50ce96fcb9 100644 --- a/drivers/media/common/tuners/tda18271-fe.c +++ b/drivers/media/common/tuners/tda18271-fe.c @@ -616,13 +616,13 @@ static int tda18271_rf_tracking_filters_init(struct dvb_frontend *fe, u32 freq) case RF2: map[i].rf_a1 = (prog_cal[RF2] - prog_tab[RF2] - prog_cal[RF1] + prog_tab[RF1]) / - ((rf_freq[RF2] - rf_freq[RF1]) / 1000); + (s32)((rf_freq[RF2] - rf_freq[RF1]) / 1000); map[i].rf2 = rf_freq[RF2] / 1000; break; case RF3: map[i].rf_a2 = (prog_cal[RF3] - prog_tab[RF3] - prog_cal[RF2] + prog_tab[RF2]) / - ((rf_freq[RF3] - rf_freq[RF2]) / 1000); + (s32)((rf_freq[RF3] - rf_freq[RF2]) / 1000); map[i].rf_b2 = prog_cal[RF2] - prog_tab[RF2]; map[i].rf3 = rf_freq[RF3] / 1000; break; @@ -1000,12 +1000,12 @@ static int tda18271_set_analog_params(struct dvb_frontend *fe, struct tda18271_std_map_item *map; char *mode; int ret; - u32 freq = params->frequency * 62500; + u32 freq = params->frequency * 125 * + ((params->mode == V4L2_TUNER_RADIO) ? 1 : 1000) / 2; priv->mode = TDA18271_ANALOG; if (params->mode == V4L2_TUNER_RADIO) { - freq = freq / 1000; map = &std_map->fm_radio; mode = "fm"; } else if (params->std & V4L2_STD_MN) { diff --git a/drivers/media/dvb/dvb-core/dmxdev.c b/drivers/media/dvb/dvb-core/dmxdev.c index 3750ff48cba1..c37790ad92d0 100644 --- a/drivers/media/dvb/dvb-core/dmxdev.c +++ b/drivers/media/dvb/dvb-core/dmxdev.c @@ -20,6 +20,7 @@ * */ +#include <linux/sched.h> #include <linux/spinlock.h> #include <linux/slab.h> #include <linux/vmalloc.h> @@ -1203,7 +1204,7 @@ static unsigned int dvb_dvr_poll(struct file *file, poll_table *wait) return mask; } -static struct file_operations dvb_dvr_fops = { +static const struct file_operations dvb_dvr_fops = { .owner = THIS_MODULE, .read = dvb_dvr_read, .write = dvb_dvr_write, diff --git a/drivers/media/dvb/dvb-core/dvb_demux.c b/drivers/media/dvb/dvb-core/dvb_demux.c index eef6d3616626..91c537bca8ad 100644 --- a/drivers/media/dvb/dvb-core/dvb_demux.c +++ b/drivers/media/dvb/dvb-core/dvb_demux.c @@ -21,6 +21,7 @@ * */ +#include <linux/sched.h> #include <linux/spinlock.h> #include <linux/slab.h> #include <linux/vmalloc.h> diff --git a/drivers/media/dvb/dvb-core/dvb_net.c b/drivers/media/dvb/dvb-core/dvb_net.c index 8c9ae0a3a272..0241a7c5c34a 100644 --- a/drivers/media/dvb/dvb-core/dvb_net.c +++ b/drivers/media/dvb/dvb-core/dvb_net.c @@ -63,6 +63,7 @@ #include <asm/uaccess.h> #include <linux/crc32.h> #include <linux/mutex.h> +#include <linux/sched.h> #include "dvb_demux.h" #include "dvb_net.h" diff --git a/drivers/media/dvb/dvb-core/dvbdev.h b/drivers/media/dvb/dvb-core/dvbdev.h index 895e2efca8a9..01fc70484743 100644 --- a/drivers/media/dvb/dvb-core/dvbdev.h +++ b/drivers/media/dvb/dvb-core/dvbdev.h @@ -31,10 +31,9 @@ #define DVB_MAJOR 212 #if defined(CONFIG_DVB_MAX_ADAPTERS) && CONFIG_DVB_MAX_ADAPTERS > 0 -#define DVB_MAX_ADAPTERS CONFIG_DVB_MAX_ADAPTERS + #define DVB_MAX_ADAPTERS CONFIG_DVB_MAX_ADAPTERS #else -#warning invalid CONFIG_DVB_MAX_ADAPTERS value -#define DVB_MAX_ADAPTERS 8 + #define DVB_MAX_ADAPTERS 8 #endif #define DVB_UNSET (-1) diff --git a/drivers/media/dvb/dvb-usb/ce6230.c b/drivers/media/dvb/dvb-usb/ce6230.c index 0737c6377892..3df2045b7d2d 100644 --- a/drivers/media/dvb/dvb-usb/ce6230.c +++ b/drivers/media/dvb/dvb-usb/ce6230.c @@ -105,7 +105,7 @@ static int ce6230_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[], int i = 0; struct req_t req; int ret = 0; - memset(&req, 0, sizeof(&req)); + memset(&req, 0, sizeof(req)); if (num > 2) return -EINVAL; diff --git a/drivers/media/dvb/dvb-usb/cxusb.c b/drivers/media/dvb/dvb-usb/cxusb.c index f65591fb7cec..2a53dd096eef 100644 --- a/drivers/media/dvb/dvb-usb/cxusb.c +++ b/drivers/media/dvb/dvb-usb/cxusb.c @@ -663,6 +663,14 @@ static struct zl10353_config cxusb_zl10353_xc3028_config = { .parallel_ts = 1, }; +static struct zl10353_config cxusb_zl10353_xc3028_config_no_i2c_gate = { + .demod_address = 0x0f, + .if2 = 45600, + .no_tuner = 1, + .parallel_ts = 1, + .disable_i2c_gate_ctrl = 1, +}; + static struct mt352_config cxusb_mt352_xc3028_config = { .demod_address = 0x0f, .if2 = 4560, @@ -894,7 +902,7 @@ static int cxusb_dualdig4_frontend_attach(struct dvb_usb_adapter *adap) cxusb_bluebird_gpio_pulse(adap->dev, 0x02, 1); if ((adap->fe = dvb_attach(zl10353_attach, - &cxusb_zl10353_xc3028_config, + &cxusb_zl10353_xc3028_config_no_i2c_gate, &adap->dev->i2c_adap)) == NULL) return -EIO; diff --git a/drivers/media/dvb/dvb-usb/dib0700_devices.c b/drivers/media/dvb/dvb-usb/dib0700_devices.c index 0b2812aa30a4..6bd8951ea02b 100644 --- a/drivers/media/dvb/dvb-usb/dib0700_devices.c +++ b/drivers/media/dvb/dvb-usb/dib0700_devices.c @@ -1925,7 +1925,7 @@ struct dvb_usb_device_properties dib0700_devices[] = { { NULL }, }, { "Leadtek Winfast DTV Dongle (STK7700P based)", - { &dib0700_usb_id_table[8] }, + { &dib0700_usb_id_table[8], &dib0700_usb_id_table[34] }, { NULL }, }, { "AVerMedia AVerTV DVB-T Express", @@ -2064,7 +2064,7 @@ struct dvb_usb_device_properties dib0700_devices[] = { }, }, - .num_device_descs = 12, + .num_device_descs = 11, .devices = { { "DiBcom STK7070P reference design", { &dib0700_usb_id_table[15], NULL }, @@ -2098,11 +2098,6 @@ struct dvb_usb_device_properties dib0700_devices[] = { { &dib0700_usb_id_table[30], NULL }, { NULL }, }, - { "Terratec Cinergy T USB XXS/ T3", - { &dib0700_usb_id_table[33], - &dib0700_usb_id_table[52], NULL }, - { NULL }, - }, { "Elgato EyeTV DTT", { &dib0700_usb_id_table[49], NULL }, { NULL }, @@ -2343,8 +2338,10 @@ struct dvb_usb_device_properties dib0700_devices[] = { { &dib0700_usb_id_table[59], NULL }, { NULL }, }, - { "Terratec Cinergy T USB XXS (HD)", - { &dib0700_usb_id_table[34], &dib0700_usb_id_table[60] }, + { "Terratec Cinergy T USB XXS (HD)/ T3", + { &dib0700_usb_id_table[33], + &dib0700_usb_id_table[52], + &dib0700_usb_id_table[60], NULL}, { NULL }, }, }, diff --git a/drivers/media/dvb/firewire/firedtv-avc.c b/drivers/media/dvb/firewire/firedtv-avc.c index d1b67fe0f011..485d061319ab 100644 --- a/drivers/media/dvb/firewire/firedtv-avc.c +++ b/drivers/media/dvb/firewire/firedtv-avc.c @@ -1050,28 +1050,28 @@ int avc_ca_pmt(struct firedtv *fdtv, char *msg, int length) c->operand[4] = 0; /* slot */ c->operand[5] = SFE_VENDOR_TAG_CA_PMT; /* ca tag */ c->operand[6] = 0; /* more/last */ - /* c->operand[7] = XXXprogram_info_length + 17; */ /* length */ - c->operand[8] = list_management; - c->operand[9] = 0x01; /* pmt_cmd=OK_descramble */ + /* Use three bytes for length field in case length > 127 */ + c->operand[10] = list_management; + c->operand[11] = 0x01; /* pmt_cmd=OK_descramble */ /* TS program map table */ - c->operand[10] = 0x02; /* Table id=2 */ - c->operand[11] = 0x80; /* Section syntax + length */ - /* c->operand[12] = XXXprogram_info_length + 12; */ - c->operand[13] = msg[1]; /* Program number */ - c->operand[14] = msg[2]; - c->operand[15] = 0x01; /* Version number=0 + current/next=1 */ - c->operand[16] = 0x00; /* Section number=0 */ - c->operand[17] = 0x00; /* Last section number=0 */ - c->operand[18] = 0x1f; /* PCR_PID=1FFF */ - c->operand[19] = 0xff; - c->operand[20] = (program_info_length >> 8); /* Program info length */ - c->operand[21] = (program_info_length & 0xff); + c->operand[12] = 0x02; /* Table id=2 */ + c->operand[13] = 0x80; /* Section syntax + length */ + /* c->operand[14] = XXXprogram_info_length + 12; */ + c->operand[15] = msg[1]; /* Program number */ + c->operand[16] = msg[2]; + c->operand[17] = 0x01; /* Version number=0 + current/next=1 */ + c->operand[18] = 0x00; /* Section number=0 */ + c->operand[19] = 0x00; /* Last section number=0 */ + c->operand[20] = 0x1f; /* PCR_PID=1FFF */ + c->operand[21] = 0xff; + c->operand[22] = (program_info_length >> 8); /* Program info length */ + c->operand[23] = (program_info_length & 0xff); /* CA descriptors at programme level */ read_pos = 6; - write_pos = 22; + write_pos = 24; if (program_info_length > 0) { pmt_cmd_id = msg[read_pos++]; if (pmt_cmd_id != 1 && pmt_cmd_id != 4) @@ -1113,8 +1113,10 @@ int avc_ca_pmt(struct firedtv *fdtv, char *msg, int length) c->operand[write_pos++] = 0x00; c->operand[write_pos++] = 0x00; - c->operand[7] = write_pos - 8; - c->operand[12] = write_pos - 13; + c->operand[7] = 0x82; + c->operand[8] = (write_pos - 10) >> 8; + c->operand[9] = (write_pos - 10) & 0xff; + c->operand[14] = write_pos - 15; crc32_csum = crc32_be(0, &c->operand[10], c->operand[12] - 1); c->operand[write_pos - 4] = (crc32_csum >> 24) & 0xff; diff --git a/drivers/media/dvb/firewire/firedtv-ci.c b/drivers/media/dvb/firewire/firedtv-ci.c index eeb80d0ea3ff..853e04b7cb36 100644 --- a/drivers/media/dvb/firewire/firedtv-ci.c +++ b/drivers/media/dvb/firewire/firedtv-ci.c @@ -215,7 +215,7 @@ static unsigned int fdtv_ca_io_poll(struct file *file, poll_table *wait) return POLLIN; } -static struct file_operations fdtv_ca_fops = { +static const struct file_operations fdtv_ca_fops = { .owner = THIS_MODULE, .ioctl = dvb_generic_ioctl, .open = dvb_generic_open, diff --git a/drivers/media/dvb/firewire/firedtv-fe.c b/drivers/media/dvb/firewire/firedtv-fe.c index 7ba43630a25d..e49cdc88b0c7 100644 --- a/drivers/media/dvb/firewire/firedtv-fe.c +++ b/drivers/media/dvb/firewire/firedtv-fe.c @@ -141,18 +141,12 @@ static int fdtv_read_uncorrected_blocks(struct dvb_frontend *fe, u32 *ucblocks) return -EOPNOTSUPP; } -#define ACCEPTED 0x9 - static int fdtv_set_frontend(struct dvb_frontend *fe, struct dvb_frontend_parameters *params) { struct firedtv *fdtv = fe->sec_priv; - /* FIXME: avc_tuner_dsd never returns ACCEPTED. Check status? */ - if (avc_tuner_dsd(fdtv, params) != ACCEPTED) - return -EINVAL; - else - return 0; /* not sure of this... */ + return avc_tuner_dsd(fdtv, params); } static int fdtv_get_frontend(struct dvb_frontend *fe, diff --git a/drivers/media/dvb/frontends/dib0070.h b/drivers/media/dvb/frontends/dib0070.h index 8a2e1e710adb..eec9e52ffa75 100644 --- a/drivers/media/dvb/frontends/dib0070.h +++ b/drivers/media/dvb/frontends/dib0070.h @@ -51,6 +51,7 @@ struct dib0070_config { #if defined(CONFIG_DVB_TUNER_DIB0070) || (defined(CONFIG_DVB_TUNER_DIB0070_MODULE) && defined(MODULE)) extern struct dvb_frontend *dib0070_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, struct dib0070_config *cfg); extern u16 dib0070_wbd_offset(struct dvb_frontend *); +extern void dib0070_ctrl_agc_filter(struct dvb_frontend *, u8 open); #else static inline struct dvb_frontend *dib0070_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, struct dib0070_config *cfg) { @@ -63,7 +64,11 @@ static inline u16 dib0070_wbd_offset(struct dvb_frontend *fe) printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); return -ENODEV; } + +static inline void dib0070_ctrl_agc_filter(struct dvb_frontend *fe, u8 open) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); +} #endif -extern void dib0070_ctrl_agc_filter(struct dvb_frontend *, u8 open); #endif diff --git a/drivers/media/dvb/frontends/dib7000p.c b/drivers/media/dvb/frontends/dib7000p.c index 55ef6eeb0769..0781f94e05d2 100644 --- a/drivers/media/dvb/frontends/dib7000p.c +++ b/drivers/media/dvb/frontends/dib7000p.c @@ -1375,6 +1375,11 @@ struct dvb_frontend * dib7000p_attach(struct i2c_adapter *i2c_adap, u8 i2c_addr, if (dib7000p_identify(st) != 0) goto error; + /* FIXME: make sure the dev.parent field is initialized, or else + request_firmware() will hit an OOPS (this should be moved somewhere + more common) */ + st->i2c_master.gated_tuner_i2c_adap.dev.parent = i2c_adap->dev.parent; + dibx000_init_i2c_master(&st->i2c_master, DIB7000P, st->i2c_adap, st->i2c_addr); dib7000p_demod_reset(st); diff --git a/drivers/media/dvb/pt1/pt1.c b/drivers/media/dvb/pt1/pt1.c index 81e623a90f09..1fd8306371e2 100644 --- a/drivers/media/dvb/pt1/pt1.c +++ b/drivers/media/dvb/pt1/pt1.c @@ -27,6 +27,7 @@ #include <linux/pci.h> #include <linux/kthread.h> #include <linux/freezer.h> +#include <linux/vmalloc.h> #include "dvbdev.h" #include "dvb_demux.h" diff --git a/drivers/media/dvb/siano/Kconfig b/drivers/media/dvb/siano/Kconfig index 8c1aed77ea30..85a222c4eaa0 100644 --- a/drivers/media/dvb/siano/Kconfig +++ b/drivers/media/dvb/siano/Kconfig @@ -4,7 +4,7 @@ config SMS_SIANO_MDTV tristate "Siano SMS1xxx based MDTV receiver" - depends on DVB_CORE && INPUT + depends on DVB_CORE && INPUT && HAS_DMA ---help--- Choose Y or M here if you have MDTV receiver with a Siano chipset. diff --git a/drivers/media/dvb/siano/smsusb.c b/drivers/media/dvb/siano/smsusb.c index cb8a358b7310..8f88a586b0dd 100644 --- a/drivers/media/dvb/siano/smsusb.c +++ b/drivers/media/dvb/siano/smsusb.c @@ -529,6 +529,12 @@ struct usb_device_id smsusb_id_table[] = { .driver_info = SMS1XXX_BOARD_SIANO_NICE }, { USB_DEVICE(0x187f, 0x0301), .driver_info = SMS1XXX_BOARD_SIANO_VENICE }, + { USB_DEVICE(0x2040, 0xb900), + .driver_info = SMS1XXX_BOARD_HAUPPAUGE_WINDHAM }, + { USB_DEVICE(0x2040, 0xb910), + .driver_info = SMS1XXX_BOARD_HAUPPAUGE_WINDHAM }, + { USB_DEVICE(0x2040, 0xc000), + .driver_info = SMS1XXX_BOARD_HAUPPAUGE_WINDHAM }, { } /* Terminating entry */ }; diff --git a/drivers/media/radio/radio-cadet.c b/drivers/media/radio/radio-cadet.c index 8b1440136c45..482d0f3be5ff 100644 --- a/drivers/media/radio/radio-cadet.c +++ b/drivers/media/radio/radio-cadet.c @@ -38,6 +38,7 @@ #include <linux/videodev2.h> /* V4L2 API defs */ #include <linux/param.h> #include <linux/pnp.h> +#include <linux/sched.h> #include <linux/io.h> /* outb, outb_p */ #include <media/v4l2-device.h> #include <media/v4l2-ioctl.h> diff --git a/drivers/media/radio/radio-gemtek-pci.c b/drivers/media/radio/radio-gemtek-pci.c index c3f579de6e71..c6cf11661868 100644 --- a/drivers/media/radio/radio-gemtek-pci.c +++ b/drivers/media/radio/radio-gemtek-pci.c @@ -181,12 +181,10 @@ static void gemtek_pci_mute(struct gemtek_pci *card) static void gemtek_pci_unmute(struct gemtek_pci *card) { - mutex_lock(&card->lock); if (card->mute) { gemtek_pci_setfrequency(card, card->current_frequency); card->mute = false; } - mutex_unlock(&card->lock); } static int gemtek_pci_getsignal(struct gemtek_pci *card) diff --git a/drivers/media/video/bt8xx/bttv-driver.c b/drivers/media/video/bt8xx/bttv-driver.c index 939d1e512974..a6724019c66f 100644 --- a/drivers/media/video/bt8xx/bttv-driver.c +++ b/drivers/media/video/bt8xx/bttv-driver.c @@ -1299,7 +1299,7 @@ set_tvnorm(struct bttv *btv, unsigned int norm) tvnorm = &bttv_tvnorms[norm]; - if (!memcmp(&bttv_tvnorms[btv->tvnorm].cropcap, &tvnorm->cropcap, + if (memcmp(&bttv_tvnorms[btv->tvnorm].cropcap, &tvnorm->cropcap, sizeof (tvnorm->cropcap))) { bttv_crop_reset(&btv->crop[0], norm); btv->crop[1] = btv->crop[0]; /* current = default */ @@ -3800,11 +3800,34 @@ bttv_irq_next_video(struct bttv *btv, struct bttv_buffer_set *set) if (!V4L2_FIELD_HAS_BOTH(item->vb.field) && (item->vb.queue.next != &btv->capture)) { item = list_entry(item->vb.queue.next, struct bttv_buffer, vb.queue); + /* Mike Isely <isely@pobox.com> - Only check + * and set up the bottom field in the logic + * below. Don't ever do the top field. This + * of course means that if we set up the + * bottom field in the above code that we'll + * actually skip a field. But that's OK. + * Having processed only a single buffer this + * time, then the next time around the first + * available buffer should be for a top field. + * That will then cause us here to set up a + * top then a bottom field in the normal way. + * The alternative to this understanding is + * that we set up the second available buffer + * as a top field, but that's out of order + * since this driver always processes the top + * field first - the effect will be the two + * buffers being returned in the wrong order, + * with the second buffer also being delayed + * by one field time (owing to the fifo nature + * of videobuf). Worse still, we'll be stuck + * doing fields out of order now every time + * until something else causes a field to be + * dropped. By effectively forcing a field to + * drop this way then we always get back into + * sync within a single frame time. (Out of + * order fields can screw up deinterlacing + * algorithms.) */ if (!V4L2_FIELD_HAS_BOTH(item->vb.field)) { - if (NULL == set->top && - V4L2_FIELD_TOP == item->vb.field) { - set->top = item; - } if (NULL == set->bottom && V4L2_FIELD_BOTTOM == item->vb.field) { set->bottom = item; diff --git a/drivers/media/video/cafe_ccic.c b/drivers/media/video/cafe_ccic.c index 657c481d255c..10230cb3d210 100644 --- a/drivers/media/video/cafe_ccic.c +++ b/drivers/media/video/cafe_ccic.c @@ -1325,7 +1325,7 @@ static void cafe_v4l_vm_close(struct vm_area_struct *vma) mutex_unlock(&sbuf->cam->s_mutex); } -static struct vm_operations_struct cafe_v4l_vm_ops = { +static const struct vm_operations_struct cafe_v4l_vm_ops = { .open = cafe_v4l_vm_open, .close = cafe_v4l_vm_close }; diff --git a/drivers/media/video/cpia.c b/drivers/media/video/cpia.c index 43ab0adf3b61..2377313c041a 100644 --- a/drivers/media/video/cpia.c +++ b/drivers/media/video/cpia.c @@ -31,6 +31,7 @@ #include <linux/init.h> #include <linux/fs.h> #include <linux/vmalloc.h> +#include <linux/sched.h> #include <linux/slab.h> #include <linux/proc_fs.h> #include <linux/ctype.h> diff --git a/drivers/media/video/davinci/vpif_display.c b/drivers/media/video/davinci/vpif_display.c index c015da813dda..d14cfb200ed0 100644 --- a/drivers/media/video/davinci/vpif_display.c +++ b/drivers/media/video/davinci/vpif_display.c @@ -1426,7 +1426,6 @@ static __init int vpif_probe(struct platform_device *pdev) struct vpif_display_config *config; int i, j = 0, k, q, m, err = 0; struct i2c_adapter *i2c_adap; - struct vpif_config *config; struct common_obj *common; struct channel_obj *ch; struct video_device *vfd; diff --git a/drivers/media/video/em28xx/em28xx-audio.c b/drivers/media/video/em28xx/em28xx-audio.c index 7bd8a70f0a0b..ac947aecb9c3 100644 --- a/drivers/media/video/em28xx/em28xx-audio.c +++ b/drivers/media/video/em28xx/em28xx-audio.c @@ -383,6 +383,11 @@ static int snd_em28xx_hw_capture_free(struct snd_pcm_substream *substream) static int snd_em28xx_prepare(struct snd_pcm_substream *substream) { + struct em28xx *dev = snd_pcm_substream_chip(substream); + + dev->adev.hwptr_done_capture = 0; + dev->adev.capture_transfer_done = 0; + return 0; } diff --git a/drivers/media/video/em28xx/em28xx-cards.c b/drivers/media/video/em28xx/em28xx-cards.c index bdb249bd9d5d..c0fd5c6feeac 100644 --- a/drivers/media/video/em28xx/em28xx-cards.c +++ b/drivers/media/video/em28xx/em28xx-cards.c @@ -1584,8 +1584,8 @@ struct em28xx_board em28xx_boards[] = { [EM2870_BOARD_REDDO_DVB_C_USB_BOX] = { .name = "Reddo DVB-C USB TV Box", .tuner_type = TUNER_ABSENT, + .tuner_gpio = reddo_dvb_c_usb_box, .has_dvb = 1, - .dvb_gpio = reddo_dvb_c_usb_box, }, }; const unsigned int em28xx_bcount = ARRAY_SIZE(em28xx_boards); diff --git a/drivers/media/video/et61x251/et61x251_core.c b/drivers/media/video/et61x251/et61x251_core.c index 74092f436be6..88987a57cf7b 100644 --- a/drivers/media/video/et61x251/et61x251_core.c +++ b/drivers/media/video/et61x251/et61x251_core.c @@ -1496,7 +1496,7 @@ static void et61x251_vm_close(struct vm_area_struct* vma) } -static struct vm_operations_struct et61x251_vm_ops = { +static const struct vm_operations_struct et61x251_vm_ops = { .open = et61x251_vm_open, .close = et61x251_vm_close, }; diff --git a/drivers/media/video/gspca/gspca.c b/drivers/media/video/gspca/gspca.c index cf6540da1e42..23d3fb776918 100644 --- a/drivers/media/video/gspca/gspca.c +++ b/drivers/media/video/gspca/gspca.c @@ -99,7 +99,7 @@ static void gspca_vm_close(struct vm_area_struct *vma) frame->v4l2_buf.flags &= ~V4L2_BUF_FLAG_MAPPED; } -static struct vm_operations_struct gspca_vm_ops = { +static const struct vm_operations_struct gspca_vm_ops = { .open = gspca_vm_open, .close = gspca_vm_close, }; diff --git a/drivers/media/video/gspca/m5602/m5602_s5k4aa.c b/drivers/media/video/gspca/m5602/m5602_s5k4aa.c index 59400e858965..a27afeb6f39b 100644 --- a/drivers/media/video/gspca/m5602/m5602_s5k4aa.c +++ b/drivers/media/video/gspca/m5602/m5602_s5k4aa.c @@ -35,12 +35,25 @@ static const struct dmi_system_id s5k4aa_vflip_dmi_table[] = { { + .ident = "BRUNEINIT", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "BRUNENIT"), + DMI_MATCH(DMI_PRODUCT_NAME, "BRUNENIT"), + DMI_MATCH(DMI_BOARD_VERSION, "00030D0000000001") + } + }, { .ident = "Fujitsu-Siemens Amilo Xa 2528", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"), DMI_MATCH(DMI_PRODUCT_NAME, "AMILO Xa 2528") } }, { + .ident = "Fujitsu-Siemens Amilo Xi 2528", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"), + DMI_MATCH(DMI_PRODUCT_NAME, "AMILO Xi 2528") + } + }, { .ident = "Fujitsu-Siemens Amilo Xi 2550", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"), @@ -57,6 +70,13 @@ static .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Micro-Star International"), DMI_MATCH(DMI_PRODUCT_NAME, "GX700"), + DMI_MATCH(DMI_BIOS_DATE, "12/02/2008") + } + }, { + .ident = "MSI GX700", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Micro-Star International"), + DMI_MATCH(DMI_PRODUCT_NAME, "GX700"), DMI_MATCH(DMI_BIOS_DATE, "07/26/2007") } }, { diff --git a/drivers/media/video/gspca/mr97310a.c b/drivers/media/video/gspca/mr97310a.c index 140c8f320e47..f8328b9efae5 100644 --- a/drivers/media/video/gspca/mr97310a.c +++ b/drivers/media/video/gspca/mr97310a.c @@ -483,7 +483,7 @@ static int start_cif_cam(struct gspca_dev *gspca_dev) data[3] = 0x2c; /* reg 2, H size/8 */ data[4] = 0x48; /* reg 3, V size/4 */ data[6] = 0x06; /* reg 5, H start */ - data[8] = 0x06 + sd->sensor_type; /* reg 7, V start */ + data[8] = 0x06 - sd->sensor_type; /* reg 7, V start */ break; } err_code = mr_write(gspca_dev, 11); diff --git a/drivers/media/video/gspca/ov519.c b/drivers/media/video/gspca/ov519.c index 2f6e135d94bc..a5c190e93799 100644 --- a/drivers/media/video/gspca/ov519.c +++ b/drivers/media/video/gspca/ov519.c @@ -2919,7 +2919,7 @@ static void ov518_pkt_scan(struct gspca_dev *gspca_dev, /* A false positive here is likely, until OVT gives me * the definitive SOF/EOF format */ if ((!(data[0] | data[1] | data[2] | data[3] | data[5])) && data[6]) { - gspca_frame_add(gspca_dev, LAST_PACKET, frame, data, 0); + frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame, data, 0); gspca_frame_add(gspca_dev, FIRST_PACKET, frame, data, 0); sd->packet_nr = 0; } diff --git a/drivers/media/video/gspca/stv06xx/stv06xx.c b/drivers/media/video/gspca/stv06xx/stv06xx.c index 65489d6b0d89..bfae63f5584c 100644 --- a/drivers/media/video/gspca/stv06xx/stv06xx.c +++ b/drivers/media/video/gspca/stv06xx/stv06xx.c @@ -394,7 +394,8 @@ frame_data: PDEBUG(D_PACK, "End of frame detected"); /* Complete the last frame (if any) */ - gspca_frame_add(gspca_dev, LAST_PACKET, frame, data, 0); + frame = gspca_frame_add(gspca_dev, LAST_PACKET, + frame, data, 0); if (chunk_len) PDEBUG(D_ERR, "Chunk length is " diff --git a/drivers/media/video/meye.c b/drivers/media/video/meye.c index d0765bed79c9..01e1eefcf1eb 100644 --- a/drivers/media/video/meye.c +++ b/drivers/media/video/meye.c @@ -28,6 +28,7 @@ */ #include <linux/module.h> #include <linux/pci.h> +#include <linux/sched.h> #include <linux/init.h> #include <linux/videodev.h> #include <media/v4l2-common.h> @@ -1589,7 +1590,7 @@ static void meye_vm_close(struct vm_area_struct *vma) meye.vma_use_count[idx]--; } -static struct vm_operations_struct meye_vm_ops = { +static const struct vm_operations_struct meye_vm_ops = { .open = meye_vm_open, .close = meye_vm_close, }; diff --git a/drivers/media/video/mx1_camera.c b/drivers/media/video/mx1_camera.c index 5f37952c75cf..72802291e812 100644 --- a/drivers/media/video/mx1_camera.c +++ b/drivers/media/video/mx1_camera.c @@ -28,6 +28,7 @@ #include <linux/moduleparam.h> #include <linux/mutex.h> #include <linux/platform_device.h> +#include <linux/sched.h> #include <linux/time.h> #include <linux/version.h> #include <linux/videodev2.h> diff --git a/drivers/media/video/mx3_camera.c b/drivers/media/video/mx3_camera.c index dff2e5e2d8c6..7db82bdf6f31 100644 --- a/drivers/media/video/mx3_camera.c +++ b/drivers/media/video/mx3_camera.c @@ -17,6 +17,7 @@ #include <linux/clk.h> #include <linux/vmalloc.h> #include <linux/interrupt.h> +#include <linux/sched.h> #include <media/v4l2-common.h> #include <media/v4l2-dev.h> diff --git a/drivers/media/video/pxa_camera.c b/drivers/media/video/pxa_camera.c index 6952e9602d5d..51b683c63b70 100644 --- a/drivers/media/video/pxa_camera.c +++ b/drivers/media/video/pxa_camera.c @@ -26,6 +26,7 @@ #include <linux/device.h> #include <linux/platform_device.h> #include <linux/clk.h> +#include <linux/sched.h> #include <media/v4l2-common.h> #include <media/v4l2-dev.h> @@ -1432,7 +1433,9 @@ static int pxa_camera_set_fmt(struct soc_camera_device *icd, icd->sense = &sense; cam_f.fmt.pix.pixelformat = cam_fmt->fourcc; - ret = v4l2_subdev_call(sd, video, s_fmt, f); + ret = v4l2_subdev_call(sd, video, s_fmt, &cam_f); + cam_f.fmt.pix.pixelformat = pix->pixelformat; + *pix = cam_f.fmt.pix; icd->sense = NULL; diff --git a/drivers/media/video/s2255drv.c b/drivers/media/video/s2255drv.c index 9e3262c0ba37..2c0bb06cab3b 100644 --- a/drivers/media/video/s2255drv.c +++ b/drivers/media/video/s2255drv.c @@ -598,11 +598,6 @@ static int s2255_got_frame(struct s2255_dev *dev, int chn, int jpgsize) buf = list_entry(dma_q->active.next, struct s2255_buffer, vb.queue); - if (!waitqueue_active(&buf->vb.done)) { - /* no one active */ - rc = -1; - goto unlock; - } list_del(&buf->vb.queue); do_gettimeofday(&buf->vb.ts); dprintk(100, "[%p/%d] wakeup\n", buf, buf->vb.i); diff --git a/drivers/media/video/saa7134/saa7134-cards.c b/drivers/media/video/saa7134/saa7134-cards.c index 71145bff94fa..09013229d4aa 100644 --- a/drivers/media/video/saa7134/saa7134-cards.c +++ b/drivers/media/video/saa7134/saa7134-cards.c @@ -3428,6 +3428,7 @@ struct saa7134_board saa7134_boards[] = { .tuner_config = 3, .mpeg = SAA7134_MPEG_DVB, .ts_type = SAA7134_MPEG_TS_SERIAL, + .ts_force_val = 1, .gpiomask = 0x0800100, /* GPIO 21 is an INPUT */ .inputs = {{ .name = name_tv, diff --git a/drivers/media/video/saa7134/saa7134-ts.c b/drivers/media/video/saa7134/saa7134-ts.c index 3fa652279ac0..03488ba4c99c 100644 --- a/drivers/media/video/saa7134/saa7134-ts.c +++ b/drivers/media/video/saa7134/saa7134-ts.c @@ -262,11 +262,13 @@ int saa7134_ts_start(struct saa7134_dev *dev) switch (saa7134_boards[dev->board].ts_type) { case SAA7134_MPEG_TS_PARALLEL: saa_writeb(SAA7134_TS_SERIAL0, 0x40); - saa_writeb(SAA7134_TS_PARALLEL, 0xec); + saa_writeb(SAA7134_TS_PARALLEL, 0xec | + (saa7134_boards[dev->board].ts_force_val << 4)); break; case SAA7134_MPEG_TS_SERIAL: saa_writeb(SAA7134_TS_SERIAL0, 0xd8); - saa_writeb(SAA7134_TS_PARALLEL, 0x6c); + saa_writeb(SAA7134_TS_PARALLEL, 0x6c | + (saa7134_boards[dev->board].ts_force_val << 4)); saa_writeb(SAA7134_TS_PARALLEL_SERIAL, 0xbc); saa_writeb(SAA7134_TS_SERIAL1, 0x02); break; diff --git a/drivers/media/video/saa7134/saa7134.h b/drivers/media/video/saa7134/saa7134.h index 6ee3e9b7769e..f8697d46ff5f 100644 --- a/drivers/media/video/saa7134/saa7134.h +++ b/drivers/media/video/saa7134/saa7134.h @@ -360,6 +360,7 @@ struct saa7134_board { enum saa7134_mpeg_type mpeg; enum saa7134_mpeg_ts_type ts_type; unsigned int vid_port_opts; + unsigned int ts_force_val:1; }; #define card_has_radio(dev) (NULL != saa7134_boards[dev->board].radio.name) diff --git a/drivers/media/video/saa7164/saa7164-api.c b/drivers/media/video/saa7164/saa7164-api.c index bb6df1b276be..6f094a96ac81 100644 --- a/drivers/media/video/saa7164/saa7164-api.c +++ b/drivers/media/video/saa7164/saa7164-api.c @@ -415,7 +415,7 @@ int saa7164_api_enum_subdevs(struct saa7164_dev *dev) goto out; } - if (debug & DBGLVL_API) + if (saa_debug & DBGLVL_API) saa7164_dumphex16(dev, buf, (buflen/16)*16); saa7164_api_dump_subdevs(dev, buf, buflen); @@ -480,7 +480,7 @@ int saa7164_api_i2c_read(struct saa7164_i2c *bus, u8 addr, u32 reglen, u8 *reg, dprintk(DBGLVL_API, "%s() len = %d bytes\n", __func__, len); - if (debug & DBGLVL_I2C) + if (saa_debug & DBGLVL_I2C) saa7164_dumphex16(dev, buf, 2 * 16); ret = saa7164_cmd_send(bus->dev, unitid, GET_CUR, @@ -488,7 +488,7 @@ int saa7164_api_i2c_read(struct saa7164_i2c *bus, u8 addr, u32 reglen, u8 *reg, if (ret != SAA_OK) printk(KERN_ERR "%s() error, ret(2) = 0x%x\n", __func__, ret); else { - if (debug & DBGLVL_I2C) + if (saa_debug & DBGLVL_I2C) saa7164_dumphex16(dev, buf, sizeof(buf)); memcpy(data, (buf + 2 * sizeof(u32) + reglen), datalen); } @@ -548,7 +548,7 @@ int saa7164_api_i2c_write(struct saa7164_i2c *bus, u8 addr, u32 datalen, *((u32 *)(buf + 1 * sizeof(u32))) = datalen - reglen; memcpy((buf + 2 * sizeof(u32)), data, datalen); - if (debug & DBGLVL_I2C) + if (saa_debug & DBGLVL_I2C) saa7164_dumphex16(dev, buf, sizeof(buf)); ret = saa7164_cmd_send(bus->dev, unitid, SET_CUR, diff --git a/drivers/media/video/saa7164/saa7164-cmd.c b/drivers/media/video/saa7164/saa7164-cmd.c index e097f1a0969a..9c1d3ac43869 100644 --- a/drivers/media/video/saa7164/saa7164-cmd.c +++ b/drivers/media/video/saa7164/saa7164-cmd.c @@ -250,7 +250,7 @@ int saa7164_cmd_wait(struct saa7164_dev *dev, u8 seqno) unsigned long stamp; int r; - if (debug >= 4) + if (saa_debug >= 4) saa7164_bus_dump(dev); dprintk(DBGLVL_CMD, "%s(seqno=%d)\n", __func__, seqno); @@ -347,7 +347,7 @@ int saa7164_cmd_send(struct saa7164_dev *dev, u8 id, tmComResCmd_t command, /* Prepare some basic command/response structures */ memset(&command_t, 0, sizeof(command_t)); - memset(&response_t, 0, sizeof(&response_t)); + memset(&response_t, 0, sizeof(response_t)); pcommand_t = &command_t; presponse_t = &response_t; command_t.id = id; diff --git a/drivers/media/video/saa7164/saa7164-core.c b/drivers/media/video/saa7164/saa7164-core.c index f0dbead188c8..709affc31042 100644 --- a/drivers/media/video/saa7164/saa7164-core.c +++ b/drivers/media/video/saa7164/saa7164-core.c @@ -45,8 +45,8 @@ MODULE_LICENSE("GPL"); 32 bus */ -unsigned int debug; -module_param(debug, int, 0644); +unsigned int saa_debug; +module_param_named(debug, saa_debug, int, 0644); MODULE_PARM_DESC(debug, "enable debug messages"); unsigned int waitsecs = 10; @@ -653,7 +653,7 @@ static int __devinit saa7164_initdev(struct pci_dev *pci_dev, printk(KERN_ERR "%s() Unsupported board detected, " "registering without firmware\n", __func__); - dprintk(1, "%s() parameter debug = %d\n", __func__, debug); + dprintk(1, "%s() parameter debug = %d\n", __func__, saa_debug); dprintk(1, "%s() parameter waitsecs = %d\n", __func__, waitsecs); fail_fw: diff --git a/drivers/media/video/saa7164/saa7164.h b/drivers/media/video/saa7164/saa7164.h index 6753008a9c9b..42660b546f0e 100644 --- a/drivers/media/video/saa7164/saa7164.h +++ b/drivers/media/video/saa7164/saa7164.h @@ -375,9 +375,9 @@ extern int saa7164_buffer_dealloc(struct saa7164_tsport *port, /* ----------------------------------------------------------- */ -extern unsigned int debug; +extern unsigned int saa_debug; #define dprintk(level, fmt, arg...)\ - do { if (debug & level)\ + do { if (saa_debug & level)\ printk(KERN_DEBUG "%s: " fmt, dev->name, ## arg);\ } while (0) diff --git a/drivers/media/video/sh_mobile_ceu_camera.c b/drivers/media/video/sh_mobile_ceu_camera.c index 5ab7c5aefd62..9c8b7c7b89ee 100644 --- a/drivers/media/video/sh_mobile_ceu_camera.c +++ b/drivers/media/video/sh_mobile_ceu_camera.c @@ -31,6 +31,7 @@ #include <linux/platform_device.h> #include <linux/videodev2.h> #include <linux/pm_runtime.h> +#include <linux/sched.h> #include <media/v4l2-common.h> #include <media/v4l2-dev.h> @@ -404,7 +405,7 @@ static int sh_mobile_ceu_add_device(struct soc_camera_device *icd) "SuperH Mobile CEU driver attached to camera %d\n", icd->devnum); - clk_enable(pcdev->clk); + pm_runtime_get_sync(ici->v4l2_dev.dev); ceu_write(pcdev, CAPSR, 1 << 16); /* reset */ while (ceu_read(pcdev, CSTSR) & 1) @@ -438,7 +439,7 @@ static void sh_mobile_ceu_remove_device(struct soc_camera_device *icd) } spin_unlock_irqrestore(&pcdev->lock, flags); - clk_disable(pcdev->clk); + pm_runtime_put_sync(ici->v4l2_dev.dev); dev_info(icd->dev.parent, "SuperH Mobile CEU driver detached from camera %d\n", @@ -1173,8 +1174,8 @@ static int get_scales(struct soc_camera_device *icd, width_in = scale_up(cam->ceu_rect.width, *scale_h); height_in = scale_up(cam->ceu_rect.height, *scale_v); - *scale_h = calc_generic_scale(cam->ceu_rect.width, icd->user_width); - *scale_v = calc_generic_scale(cam->ceu_rect.height, icd->user_height); + *scale_h = calc_generic_scale(width_in, icd->user_width); + *scale_v = calc_generic_scale(height_in, icd->user_height); return 0; } @@ -1723,11 +1724,12 @@ static int __devinit sh_mobile_ceu_probe(struct platform_device *pdev) err = soc_camera_host_register(&pcdev->ici); if (err) - goto exit_free_irq; + goto exit_free_clk; return 0; -exit_free_irq: +exit_free_clk: + pm_runtime_disable(&pdev->dev); free_irq(pcdev->irq, pcdev); exit_release_mem: if (platform_get_resource(pdev, IORESOURCE_MEM, 1)) @@ -1747,6 +1749,7 @@ static int __devexit sh_mobile_ceu_remove(struct platform_device *pdev) struct sh_mobile_ceu_dev, ici); soc_camera_host_unregister(soc_host); + pm_runtime_disable(&pdev->dev); free_irq(pcdev->irq, pcdev); if (platform_get_resource(pdev, IORESOURCE_MEM, 1)) dma_release_declared_memory(&pdev->dev); diff --git a/drivers/media/video/sn9c102/sn9c102_core.c b/drivers/media/video/sn9c102/sn9c102_core.c index 9d84c94e8a40..4a7711c3e745 100644 --- a/drivers/media/video/sn9c102/sn9c102_core.c +++ b/drivers/media/video/sn9c102/sn9c102_core.c @@ -2077,7 +2077,7 @@ static void sn9c102_vm_close(struct vm_area_struct* vma) } -static struct vm_operations_struct sn9c102_vm_ops = { +static const struct vm_operations_struct sn9c102_vm_ops = { .open = sn9c102_vm_open, .close = sn9c102_vm_close, }; diff --git a/drivers/media/video/soc_camera.c b/drivers/media/video/soc_camera.c index 59aa7a3694c2..95fdeb23c2c1 100644 --- a/drivers/media/video/soc_camera.c +++ b/drivers/media/video/soc_camera.c @@ -1097,6 +1097,13 @@ static int default_s_crop(struct soc_camera_device *icd, struct v4l2_crop *a) return v4l2_subdev_call(sd, video, s_crop, a); } +static void soc_camera_device_init(struct device *dev, void *pdata) +{ + dev->platform_data = pdata; + dev->bus = &soc_camera_bus_type; + dev->release = dummy_release; +} + int soc_camera_host_register(struct soc_camera_host *ici) { struct soc_camera_host *ix; @@ -1158,15 +1165,19 @@ void soc_camera_host_unregister(struct soc_camera_host *ici) list_for_each_entry(icd, &devices, list) { if (icd->iface == ici->nr) { + void *pdata = icd->dev.platform_data; /* The bus->remove will be called */ device_unregister(&icd->dev); - /* Not before device_unregister(), .remove - * needs parent to call ici->ops->remove() */ - icd->dev.parent = NULL; - - /* If the host module is loaded again, device_register() - * would complain "already initialised" */ - memset(&icd->dev.kobj, 0, sizeof(icd->dev.kobj)); + /* + * Not before device_unregister(), .remove + * needs parent to call ici->ops->remove(). + * If the host module is loaded again, device_register() + * would complain "already initialised," since 2.6.32 + * this is also needed to prevent use-after-free of the + * device private data. + */ + memset(&icd->dev, 0, sizeof(icd->dev)); + soc_camera_device_init(&icd->dev, pdata); } } @@ -1198,10 +1209,7 @@ static int soc_camera_device_register(struct soc_camera_device *icd) * man, stay reasonable... */ return -ENOMEM; - icd->devnum = num; - icd->dev.bus = &soc_camera_bus_type; - - icd->dev.release = dummy_release; + icd->devnum = num; icd->use_count = 0; icd->host_priv = NULL; mutex_init(&icd->video_lock); @@ -1309,12 +1317,13 @@ static int __devinit soc_camera_pdrv_probe(struct platform_device *pdev) icd->iface = icl->bus_id; icd->pdev = &pdev->dev; platform_set_drvdata(pdev, icd); - icd->dev.platform_data = icl; ret = soc_camera_device_register(icd); if (ret < 0) goto escdevreg; + soc_camera_device_init(&icd->dev, icl); + icd->user_width = DEFAULT_WIDTH; icd->user_height = DEFAULT_HEIGHT; diff --git a/drivers/media/video/stk-webcam.c b/drivers/media/video/stk-webcam.c index 0b996ea4134e..6b41865f42bd 100644 --- a/drivers/media/video/stk-webcam.c +++ b/drivers/media/video/stk-webcam.c @@ -790,7 +790,7 @@ static void stk_v4l_vm_close(struct vm_area_struct *vma) if (sbuf->mapcount == 0) sbuf->v4lbuf.flags &= ~V4L2_BUF_FLAG_MAPPED; } -static struct vm_operations_struct stk_v4l_vm_ops = { +static const struct vm_operations_struct stk_v4l_vm_ops = { .open = stk_v4l_vm_open, .close = stk_v4l_vm_close }; diff --git a/drivers/media/video/usbvision/usbvision-core.c b/drivers/media/video/usbvision/usbvision-core.c index 6ba16abeebdd..e0f91e4ab653 100644 --- a/drivers/media/video/usbvision/usbvision-core.c +++ b/drivers/media/video/usbvision/usbvision-core.c @@ -28,7 +28,6 @@ #include <linux/timer.h> #include <linux/slab.h> #include <linux/mm.h> -#include <linux/utsname.h> #include <linux/highmem.h> #include <linux/vmalloc.h> #include <linux/module.h> diff --git a/drivers/media/video/usbvision/usbvision-i2c.c b/drivers/media/video/usbvision/usbvision-i2c.c index f97fd06d5948..c19f51dba2ee 100644 --- a/drivers/media/video/usbvision/usbvision-i2c.c +++ b/drivers/media/video/usbvision/usbvision-i2c.c @@ -28,7 +28,6 @@ #include <linux/module.h> #include <linux/delay.h> #include <linux/slab.h> -#include <linux/utsname.h> #include <linux/init.h> #include <asm/uaccess.h> #include <linux/ioport.h> diff --git a/drivers/media/video/usbvision/usbvision-video.c b/drivers/media/video/usbvision/usbvision-video.c index 90d9b5c0e9a7..a2a50d608a3f 100644 --- a/drivers/media/video/usbvision/usbvision-video.c +++ b/drivers/media/video/usbvision/usbvision-video.c @@ -52,7 +52,6 @@ #include <linux/slab.h> #include <linux/smp_lock.h> #include <linux/mm.h> -#include <linux/utsname.h> #include <linux/highmem.h> #include <linux/vmalloc.h> #include <linux/module.h> diff --git a/drivers/media/video/uvc/uvc_ctrl.c b/drivers/media/video/uvc/uvc_ctrl.c index c3225a561748..1b89735e62fd 100644 --- a/drivers/media/video/uvc/uvc_ctrl.c +++ b/drivers/media/video/uvc/uvc_ctrl.c @@ -348,7 +348,7 @@ static void uvc_ctrl_set_zoom(struct uvc_control_mapping *mapping, __s32 value, __u8 *data) { data[0] = value == 0 ? 0 : (value > 0) ? 1 : 0xff; - data[2] = min(abs(value), 0xff); + data[2] = min((int)abs(value), 0xff); } static struct uvc_control_mapping uvc_ctrl_mappings[] = { diff --git a/drivers/media/video/uvc/uvc_v4l2.c b/drivers/media/video/uvc/uvc_v4l2.c index 9e7351569b5d..a2bdd806efab 100644 --- a/drivers/media/video/uvc/uvc_v4l2.c +++ b/drivers/media/video/uvc/uvc_v4l2.c @@ -1069,7 +1069,7 @@ static void uvc_vm_close(struct vm_area_struct *vma) buffer->vma_use_count--; } -static struct vm_operations_struct uvc_vm_ops = { +static const struct vm_operations_struct uvc_vm_ops = { .open = uvc_vm_open, .close = uvc_vm_close, }; diff --git a/drivers/media/video/uvc/uvc_video.c b/drivers/media/video/uvc/uvc_video.c index f960e8ea4f17..a6e41d12b221 100644 --- a/drivers/media/video/uvc/uvc_video.c +++ b/drivers/media/video/uvc/uvc_video.c @@ -90,7 +90,8 @@ static void uvc_fixup_video_ctrl(struct uvc_streaming *stream, ctrl->dwMaxVideoFrameSize = frame->dwMaxVideoFrameBufferSize; - if (stream->dev->quirks & UVC_QUIRK_FIX_BANDWIDTH && + if (!(format->flags & UVC_FMT_FLAG_COMPRESSED) && + stream->dev->quirks & UVC_QUIRK_FIX_BANDWIDTH && stream->intf->num_altsetting > 1) { u32 interval; u32 bandwidth; diff --git a/drivers/media/video/videobuf-core.c b/drivers/media/video/videobuf-core.c index f1ccf98c0a6f..8e93c6f25c83 100644 --- a/drivers/media/video/videobuf-core.c +++ b/drivers/media/video/videobuf-core.c @@ -17,6 +17,7 @@ #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/mm.h> +#include <linux/sched.h> #include <linux/slab.h> #include <linux/interrupt.h> diff --git a/drivers/media/video/videobuf-dma-contig.c b/drivers/media/video/videobuf-dma-contig.c index d09ce83a9429..c3065c4bcba9 100644 --- a/drivers/media/video/videobuf-dma-contig.c +++ b/drivers/media/video/videobuf-dma-contig.c @@ -19,6 +19,7 @@ #include <linux/mm.h> #include <linux/pagemap.h> #include <linux/dma-mapping.h> +#include <linux/sched.h> #include <media/videobuf-dma-contig.h> struct videobuf_dma_contig_memory { @@ -105,7 +106,7 @@ static void videobuf_vm_close(struct vm_area_struct *vma) } } -static struct vm_operations_struct videobuf_vm_ops = { +static const struct vm_operations_struct videobuf_vm_ops = { .open = videobuf_vm_open, .close = videobuf_vm_close, }; diff --git a/drivers/media/video/videobuf-dma-sg.c b/drivers/media/video/videobuf-dma-sg.c index a8dd22ace3fb..032ebae0134a 100644 --- a/drivers/media/video/videobuf-dma-sg.c +++ b/drivers/media/video/videobuf-dma-sg.c @@ -21,6 +21,7 @@ #include <linux/init.h> #include <linux/module.h> #include <linux/moduleparam.h> +#include <linux/sched.h> #include <linux/slab.h> #include <linux/interrupt.h> @@ -394,7 +395,7 @@ videobuf_vm_fault(struct vm_area_struct *vma, struct vm_fault *vmf) return 0; } -static struct vm_operations_struct videobuf_vm_ops = +static const struct vm_operations_struct videobuf_vm_ops = { .open = videobuf_vm_open, .close = videobuf_vm_close, diff --git a/drivers/media/video/videobuf-vmalloc.c b/drivers/media/video/videobuf-vmalloc.c index 30ae30f99ccc..35f3900c5633 100644 --- a/drivers/media/video/videobuf-vmalloc.c +++ b/drivers/media/video/videobuf-vmalloc.c @@ -116,7 +116,7 @@ static void videobuf_vm_close(struct vm_area_struct *vma) return; } -static struct vm_operations_struct videobuf_vm_ops = +static const struct vm_operations_struct videobuf_vm_ops = { .open = videobuf_vm_open, .close = videobuf_vm_close, diff --git a/drivers/media/video/vino.c b/drivers/media/video/vino.c index cd6a3446ab7e..b034a81d2b1c 100644 --- a/drivers/media/video/vino.c +++ b/drivers/media/video/vino.c @@ -3857,7 +3857,7 @@ static void vino_vm_close(struct vm_area_struct *vma) dprintk("vino_vm_close(): count = %d\n", fb->map_count); } -static struct vm_operations_struct vino_vm_ops = { +static const struct vm_operations_struct vino_vm_ops = { .open = vino_vm_open, .close = vino_vm_close, }; diff --git a/drivers/media/video/zc0301/zc0301_core.c b/drivers/media/video/zc0301/zc0301_core.c index b3c6436b33ba..312a71336fd0 100644 --- a/drivers/media/video/zc0301/zc0301_core.c +++ b/drivers/media/video/zc0301/zc0301_core.c @@ -935,7 +935,7 @@ static void zc0301_vm_close(struct vm_area_struct* vma) } -static struct vm_operations_struct zc0301_vm_ops = { +static const struct vm_operations_struct zc0301_vm_ops = { .open = zc0301_vm_open, .close = zc0301_vm_close, }; diff --git a/drivers/media/video/zoran/zoran_driver.c b/drivers/media/video/zoran/zoran_driver.c index bcdefb1bcb3d..47137deafcfd 100644 --- a/drivers/media/video/zoran/zoran_driver.c +++ b/drivers/media/video/zoran/zoran_driver.c @@ -3172,7 +3172,7 @@ zoran_vm_close (struct vm_area_struct *vma) mutex_unlock(&zr->resource_lock); } -static struct vm_operations_struct zoran_vm_ops = { +static const struct vm_operations_struct zoran_vm_ops = { .open = zoran_vm_open, .close = zoran_vm_close, }; diff --git a/drivers/memstick/core/memstick.c b/drivers/memstick/core/memstick.c index a5b448ea4eab..b3bf1c44d74d 100644 --- a/drivers/memstick/core/memstick.c +++ b/drivers/memstick/core/memstick.c @@ -339,9 +339,9 @@ static int h_memstick_read_dev_id(struct memstick_dev *card, card->id.type = id_reg.type; card->id.category = id_reg.category; card->id.class = id_reg.class; + dev_dbg(&card->dev, "if_mode = %02x\n", id_reg.if_mode); } complete(&card->mrq_complete); - dev_dbg(&card->dev, "if_mode = %02x\n", id_reg.if_mode); return -EAGAIN; } } diff --git a/drivers/message/fusion/mptlan.c b/drivers/message/fusion/mptlan.c index bc2ec2182c00..34f3f36f819b 100644 --- a/drivers/message/fusion/mptlan.c +++ b/drivers/message/fusion/mptlan.c @@ -56,6 +56,7 @@ #include <linux/init.h> #include <linux/module.h> #include <linux/fs.h> +#include <linux/sched.h> #define my_VERSION MPT_LINUX_VERSION_COMMON #define MYNAM "mptlan" diff --git a/drivers/mfd/ab3100-core.c b/drivers/mfd/ab3100-core.c index 5447da16a170..613481028272 100644 --- a/drivers/mfd/ab3100-core.c +++ b/drivers/mfd/ab3100-core.c @@ -57,8 +57,6 @@ * The AB3100 is usually assigned address 0x48 (7-bit) * The chip is defined in the platform i2c_board_data section. */ -static unsigned short normal_i2c[] = { 0x48, I2C_CLIENT_END }; -I2C_CLIENT_INSMOD_1(ab3100); u8 ab3100_get_chip_type(struct ab3100 *ab3100) { @@ -966,7 +964,7 @@ static int __exit ab3100_remove(struct i2c_client *client) } static const struct i2c_device_id ab3100_id[] = { - { "ab3100", ab3100 }, + { "ab3100", 0 }, { } }; MODULE_DEVICE_TABLE(i2c, ab3100_id); diff --git a/drivers/mfd/ezx-pcap.c b/drivers/mfd/ezx-pcap.c index 016be4938e4c..876288917976 100644 --- a/drivers/mfd/ezx-pcap.c +++ b/drivers/mfd/ezx-pcap.c @@ -548,3 +548,4 @@ module_exit(ezx_pcap_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Daniel Ribeiro / Harald Welte"); MODULE_DESCRIPTION("Motorola PCAP2 ASIC Driver"); +MODULE_ALIAS("spi:ezx-pcap"); diff --git a/drivers/mfd/twl4030-core.c b/drivers/mfd/twl4030-core.c index e424cf6d8e9e..a1c47ee95c0e 100644 --- a/drivers/mfd/twl4030-core.c +++ b/drivers/mfd/twl4030-core.c @@ -480,7 +480,6 @@ static int add_children(struct twl4030_platform_data *pdata, unsigned long features) { struct device *child; - struct device *usb_transceiver = NULL; if (twl_has_bci() && pdata->bci && !(features & TPS_SUBSET)) { child = add_child(3, "twl4030_bci", @@ -532,16 +531,61 @@ add_children(struct twl4030_platform_data *pdata, unsigned long features) } if (twl_has_usb() && pdata->usb) { + + static struct regulator_consumer_supply usb1v5 = { + .supply = "usb1v5", + }; + static struct regulator_consumer_supply usb1v8 = { + .supply = "usb1v8", + }; + static struct regulator_consumer_supply usb3v1 = { + .supply = "usb3v1", + }; + + /* First add the regulators so that they can be used by transceiver */ + if (twl_has_regulator()) { + /* this is a template that gets copied */ + struct regulator_init_data usb_fixed = { + .constraints.valid_modes_mask = + REGULATOR_MODE_NORMAL + | REGULATOR_MODE_STANDBY, + .constraints.valid_ops_mask = + REGULATOR_CHANGE_MODE + | REGULATOR_CHANGE_STATUS, + }; + + child = add_regulator_linked(TWL4030_REG_VUSB1V5, + &usb_fixed, &usb1v5, 1); + if (IS_ERR(child)) + return PTR_ERR(child); + + child = add_regulator_linked(TWL4030_REG_VUSB1V8, + &usb_fixed, &usb1v8, 1); + if (IS_ERR(child)) + return PTR_ERR(child); + + child = add_regulator_linked(TWL4030_REG_VUSB3V1, + &usb_fixed, &usb3v1, 1); + if (IS_ERR(child)) + return PTR_ERR(child); + + } + child = add_child(0, "twl4030_usb", pdata->usb, sizeof(*pdata->usb), true, /* irq0 = USB_PRES, irq1 = USB */ pdata->irq_base + 8 + 2, pdata->irq_base + 4); + if (IS_ERR(child)) return PTR_ERR(child); /* we need to connect regulators to this transceiver */ - usb_transceiver = child; + if (twl_has_regulator() && child) { + usb1v5.dev = child; + usb1v8.dev = child; + usb3v1.dev = child; + } } if (twl_has_watchdog()) { @@ -580,47 +624,6 @@ add_children(struct twl4030_platform_data *pdata, unsigned long features) return PTR_ERR(child); } - if (twl_has_regulator() && usb_transceiver) { - static struct regulator_consumer_supply usb1v5 = { - .supply = "usb1v5", - }; - static struct regulator_consumer_supply usb1v8 = { - .supply = "usb1v8", - }; - static struct regulator_consumer_supply usb3v1 = { - .supply = "usb3v1", - }; - - /* this is a template that gets copied */ - struct regulator_init_data usb_fixed = { - .constraints.valid_modes_mask = - REGULATOR_MODE_NORMAL - | REGULATOR_MODE_STANDBY, - .constraints.valid_ops_mask = - REGULATOR_CHANGE_MODE - | REGULATOR_CHANGE_STATUS, - }; - - usb1v5.dev = usb_transceiver; - usb1v8.dev = usb_transceiver; - usb3v1.dev = usb_transceiver; - - child = add_regulator_linked(TWL4030_REG_VUSB1V5, &usb_fixed, - &usb1v5, 1); - if (IS_ERR(child)) - return PTR_ERR(child); - - child = add_regulator_linked(TWL4030_REG_VUSB1V8, &usb_fixed, - &usb1v8, 1); - if (IS_ERR(child)) - return PTR_ERR(child); - - child = add_regulator_linked(TWL4030_REG_VUSB3V1, &usb_fixed, - &usb3v1, 1); - if (IS_ERR(child)) - return PTR_ERR(child); - } - /* maybe add LDOs that are omitted on cost-reduced parts */ if (twl_has_regulator() && !(features & TPS_SUBSET)) { child = add_regulator(TWL4030_REG_VPLL2, pdata->vpll2); @@ -792,7 +795,7 @@ twl4030_probe(struct i2c_client *client, const struct i2c_device_id *id) twl->client = i2c_new_dummy(client->adapter, twl->address); if (!twl->client) { - dev_err(&twl->client->dev, + dev_err(&client->dev, "can't attach client %d\n", i); status = -ENOMEM; goto fail; diff --git a/drivers/mfd/ucb1400_core.c b/drivers/mfd/ucb1400_core.c index 78c2135c5de6..fa294b6d600a 100644 --- a/drivers/mfd/ucb1400_core.c +++ b/drivers/mfd/ucb1400_core.c @@ -21,6 +21,7 @@ */ #include <linux/module.h> +#include <linux/sched.h> #include <linux/ucb1400.h> unsigned int ucb1400_adc_read(struct snd_ac97 *ac97, u16 adc_channel, @@ -48,9 +49,11 @@ static int ucb1400_core_probe(struct device *dev) int err; struct ucb1400 *ucb; struct ucb1400_ts ucb_ts; + struct ucb1400_gpio ucb_gpio; struct snd_ac97 *ac97; memset(&ucb_ts, 0, sizeof(ucb_ts)); + memset(&ucb_gpio, 0, sizeof(ucb_gpio)); ucb = kzalloc(sizeof(struct ucb1400), GFP_KERNEL); if (!ucb) { @@ -68,25 +71,44 @@ static int ucb1400_core_probe(struct device *dev) goto err0; } + /* GPIO */ + ucb_gpio.ac97 = ac97; + ucb->ucb1400_gpio = platform_device_alloc("ucb1400_gpio", -1); + if (!ucb->ucb1400_gpio) { + err = -ENOMEM; + goto err0; + } + err = platform_device_add_data(ucb->ucb1400_gpio, &ucb_gpio, + sizeof(ucb_gpio)); + if (err) + goto err1; + err = platform_device_add(ucb->ucb1400_gpio); + if (err) + goto err1; + /* TOUCHSCREEN */ ucb_ts.ac97 = ac97; ucb->ucb1400_ts = platform_device_alloc("ucb1400_ts", -1); if (!ucb->ucb1400_ts) { err = -ENOMEM; - goto err0; + goto err2; } err = platform_device_add_data(ucb->ucb1400_ts, &ucb_ts, sizeof(ucb_ts)); if (err) - goto err1; + goto err3; err = platform_device_add(ucb->ucb1400_ts); if (err) - goto err1; + goto err3; return 0; -err1: +err3: platform_device_put(ucb->ucb1400_ts); +err2: + platform_device_unregister(ucb->ucb1400_gpio); +err1: + platform_device_put(ucb->ucb1400_gpio); err0: kfree(ucb); err: @@ -98,6 +120,8 @@ static int ucb1400_core_remove(struct device *dev) struct ucb1400 *ucb = dev_get_drvdata(dev); platform_device_unregister(ucb->ucb1400_ts); + platform_device_unregister(ucb->ucb1400_gpio); + kfree(ucb); return 0; } diff --git a/drivers/mfd/ucb1x00-core.c b/drivers/mfd/ucb1x00-core.c index fea9085fe52c..60c3988f3cf3 100644 --- a/drivers/mfd/ucb1x00-core.c +++ b/drivers/mfd/ucb1x00-core.c @@ -18,6 +18,7 @@ */ #include <linux/module.h> #include <linux/kernel.h> +#include <linux/sched.h> #include <linux/slab.h> #include <linux/init.h> #include <linux/errno.h> diff --git a/drivers/mfd/wm831x-irq.c b/drivers/mfd/wm831x-irq.c index d3015dfb9134..ac056ea6b66e 100644 --- a/drivers/mfd/wm831x-irq.c +++ b/drivers/mfd/wm831x-irq.c @@ -507,6 +507,8 @@ int wm831x_irq_init(struct wm831x *wm831x, int irq) { int i, ret; + mutex_init(&wm831x->irq_lock); + if (!irq) { dev_warn(wm831x->dev, "No interrupt specified - functionality limited\n"); @@ -521,7 +523,6 @@ int wm831x_irq_init(struct wm831x *wm831x, int irq) } wm831x->irq = irq; - mutex_init(&wm831x->irq_lock); INIT_WORK(&wm831x->irq_work, wm831x_irq_worker); /* Mask the individual interrupt sources */ diff --git a/drivers/misc/eeprom/at25.c b/drivers/misc/eeprom/at25.c index 2e535a0ccd5e..d902d81dde39 100644 --- a/drivers/misc/eeprom/at25.c +++ b/drivers/misc/eeprom/at25.c @@ -417,4 +417,4 @@ module_exit(at25_exit); MODULE_DESCRIPTION("Driver for most SPI EEPROMs"); MODULE_AUTHOR("David Brownell"); MODULE_LICENSE("GPL"); - +MODULE_ALIAS("spi:at25"); diff --git a/drivers/misc/eeprom/max6875.c b/drivers/misc/eeprom/max6875.c index 3c0c58eed347..5a6b2bce8ad5 100644 --- a/drivers/misc/eeprom/max6875.c +++ b/drivers/misc/eeprom/max6875.c @@ -33,12 +33,6 @@ #include <linux/i2c.h> #include <linux/mutex.h> -/* Do not scan - the MAX6875 access method will write to some EEPROM chips */ -static const unsigned short normal_i2c[] = { I2C_CLIENT_END }; - -/* Insmod parameters */ -I2C_CLIENT_INSMOD_1(max6875); - /* The MAX6875 can only read/write 16 bytes at a time */ #define SLICE_SIZE 16 #define SLICE_BITS 4 @@ -146,31 +140,21 @@ static struct bin_attribute user_eeprom_attr = { .read = max6875_read, }; -/* Return 0 if detection is successful, -ENODEV otherwise */ -static int max6875_detect(struct i2c_client *client, int kind, - struct i2c_board_info *info) +static int max6875_probe(struct i2c_client *client, + const struct i2c_device_id *id) { struct i2c_adapter *adapter = client->adapter; + struct max6875_data *data; + int err; if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WRITE_BYTE_DATA | I2C_FUNC_SMBUS_READ_BYTE)) return -ENODEV; - /* Only check even addresses */ + /* Only bind to even addresses */ if (client->addr & 1) return -ENODEV; - strlcpy(info->type, "max6875", I2C_NAME_SIZE); - - return 0; -} - -static int max6875_probe(struct i2c_client *client, - const struct i2c_device_id *id) -{ - struct max6875_data *data; - int err; - if (!(data = kzalloc(sizeof(struct max6875_data), GFP_KERNEL))) return -ENOMEM; @@ -222,9 +206,6 @@ static struct i2c_driver max6875_driver = { .probe = max6875_probe, .remove = max6875_remove, .id_table = max6875_id, - - .detect = max6875_detect, - .address_data = &addr_data, }; static int __init max6875_init(void) diff --git a/drivers/misc/hpilo.c b/drivers/misc/hpilo.c index 1ad27c6abcca..a92a3a742b43 100644 --- a/drivers/misc/hpilo.c +++ b/drivers/misc/hpilo.c @@ -18,6 +18,7 @@ #include <linux/device.h> #include <linux/file.h> #include <linux/cdev.h> +#include <linux/sched.h> #include <linux/spinlock.h> #include <linux/delay.h> #include <linux/uaccess.h> diff --git a/drivers/misc/ibmasm/command.c b/drivers/misc/ibmasm/command.c index 276d3fb68094..e2031739aa29 100644 --- a/drivers/misc/ibmasm/command.c +++ b/drivers/misc/ibmasm/command.c @@ -22,6 +22,7 @@ * */ +#include <linux/sched.h> #include "ibmasm.h" #include "lowlevel.h" diff --git a/drivers/misc/ibmasm/event.c b/drivers/misc/ibmasm/event.c index 68a0a5b94795..572d41ffc186 100644 --- a/drivers/misc/ibmasm/event.c +++ b/drivers/misc/ibmasm/event.c @@ -22,6 +22,7 @@ * */ +#include <linux/sched.h> #include "ibmasm.h" #include "lowlevel.h" diff --git a/drivers/misc/ibmasm/r_heartbeat.c b/drivers/misc/ibmasm/r_heartbeat.c index bec9e2c44bef..2de487ac788c 100644 --- a/drivers/misc/ibmasm/r_heartbeat.c +++ b/drivers/misc/ibmasm/r_heartbeat.c @@ -20,6 +20,7 @@ * */ +#include <linux/sched.h> #include "ibmasm.h" #include "dot_command.h" diff --git a/drivers/misc/lkdtm.c b/drivers/misc/lkdtm.c index 1bfe5d16963b..3648b23d5c92 100644 --- a/drivers/misc/lkdtm.c +++ b/drivers/misc/lkdtm.c @@ -283,7 +283,7 @@ static int __init lkdtm_module_init(void) switch (cpoint) { case INT_HARDWARE_ENTRY: - lkdtm.kp.symbol_name = "__do_IRQ"; + lkdtm.kp.symbol_name = "do_IRQ"; lkdtm.entry = (kprobe_opcode_t*) jp_do_irq; break; case INT_HW_IRQ_EN: diff --git a/drivers/misc/phantom.c b/drivers/misc/phantom.c index fa57b67593ae..04c27266f567 100644 --- a/drivers/misc/phantom.c +++ b/drivers/misc/phantom.c @@ -22,6 +22,7 @@ #include <linux/interrupt.h> #include <linux/cdev.h> #include <linux/phantom.h> +#include <linux/sched.h> #include <linux/smp_lock.h> #include <asm/atomic.h> @@ -271,7 +272,7 @@ static unsigned int phantom_poll(struct file *file, poll_table *wait) return mask; } -static struct file_operations phantom_file_ops = { +static const struct file_operations phantom_file_ops = { .open = phantom_open, .release = phantom_release, .unlocked_ioctl = phantom_ioctl, diff --git a/drivers/misc/sgi-gru/grufile.c b/drivers/misc/sgi-gru/grufile.c index aed609832bc2..41c8fe2a928c 100644 --- a/drivers/misc/sgi-gru/grufile.c +++ b/drivers/misc/sgi-gru/grufile.c @@ -53,7 +53,6 @@ struct gru_stats_s gru_stats; /* Guaranteed user available resources on each node */ static int max_user_cbrs, max_user_dsr_bytes; -static struct file_operations gru_fops; static struct miscdevice gru_miscdev; @@ -426,7 +425,7 @@ static void __exit gru_exit(void) gru_proc_exit(); } -static struct file_operations gru_fops = { +static const struct file_operations gru_fops = { .owner = THIS_MODULE, .unlocked_ioctl = gru_file_unlocked_ioctl, .mmap = gru_file_mmap, @@ -438,7 +437,7 @@ static struct miscdevice gru_miscdev = { .fops = &gru_fops, }; -struct vm_operations_struct gru_vm_ops = { +const struct vm_operations_struct gru_vm_ops = { .close = gru_vma_close, .fault = gru_fault, }; diff --git a/drivers/misc/sgi-gru/grukservices.c b/drivers/misc/sgi-gru/grukservices.c index 79689b10f937..766e21e15574 100644 --- a/drivers/misc/sgi-gru/grukservices.c +++ b/drivers/misc/sgi-gru/grukservices.c @@ -937,6 +937,8 @@ static int quicktest1(unsigned long arg) /* Need 1K cacheline aligned that does not cross page boundary */ p = kmalloc(4096, 0); + if (p == NULL) + return -ENOMEM; mq = ALIGNUP(p, 1024); memset(mes, 0xee, sizeof(mes)); dw = mq; diff --git a/drivers/misc/sgi-gru/gruprocfs.c b/drivers/misc/sgi-gru/gruprocfs.c index 9cbf95bedce6..3f2375c5ba5b 100644 --- a/drivers/misc/sgi-gru/gruprocfs.c +++ b/drivers/misc/sgi-gru/gruprocfs.c @@ -161,14 +161,15 @@ static int options_show(struct seq_file *s, void *p) static ssize_t options_write(struct file *file, const char __user *userbuf, size_t count, loff_t *data) { - unsigned long val; - char buf[80]; + char buf[20]; - if (strncpy_from_user(buf, userbuf, sizeof(buf) - 1) < 0) + if (count >= sizeof(buf)) + return -EINVAL; + if (copy_from_user(buf, userbuf, count)) return -EFAULT; - buf[count - 1] = '\0'; - if (!strict_strtoul(buf, 10, &val)) - gru_options = val; + buf[count] = '\0'; + if (strict_strtoul(buf, 0, &gru_options)) + return -EINVAL; return count; } @@ -340,10 +341,9 @@ static struct proc_dir_entry *proc_gru __read_mostly; static int create_proc_file(struct proc_entry *p) { - p->entry = create_proc_entry(p->name, p->mode, proc_gru); + p->entry = proc_create(p->name, p->mode, proc_gru, p->fops); if (!p->entry) return -1; - p->entry->proc_fops = p->fops; return 0; } diff --git a/drivers/misc/sgi-gru/grutables.h b/drivers/misc/sgi-gru/grutables.h index 34ab3d453919..46990bcfa536 100644 --- a/drivers/misc/sgi-gru/grutables.h +++ b/drivers/misc/sgi-gru/grutables.h @@ -624,7 +624,7 @@ static inline int is_kernel_context(struct gru_thread_state *gts) */ struct gru_unload_context_req; -extern struct vm_operations_struct gru_vm_ops; +extern const struct vm_operations_struct gru_vm_ops; extern struct device *grudev; extern struct gru_vma_data *gru_alloc_vma_data(struct vm_area_struct *vma, diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index d84c880fac84..7dab2e5f4bc9 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -344,6 +344,101 @@ unsigned int mmc_align_data_size(struct mmc_card *card, unsigned int sz) EXPORT_SYMBOL(mmc_align_data_size); /** + * mmc_host_enable - enable a host. + * @host: mmc host to enable + * + * Hosts that support power saving can use the 'enable' and 'disable' + * methods to exit and enter power saving states. For more information + * see comments for struct mmc_host_ops. + */ +int mmc_host_enable(struct mmc_host *host) +{ + if (!(host->caps & MMC_CAP_DISABLE)) + return 0; + + if (host->en_dis_recurs) + return 0; + + if (host->nesting_cnt++) + return 0; + + cancel_delayed_work_sync(&host->disable); + + if (host->enabled) + return 0; + + if (host->ops->enable) { + int err; + + host->en_dis_recurs = 1; + err = host->ops->enable(host); + host->en_dis_recurs = 0; + + if (err) { + pr_debug("%s: enable error %d\n", + mmc_hostname(host), err); + return err; + } + } + host->enabled = 1; + return 0; +} +EXPORT_SYMBOL(mmc_host_enable); + +static int mmc_host_do_disable(struct mmc_host *host, int lazy) +{ + if (host->ops->disable) { + int err; + + host->en_dis_recurs = 1; + err = host->ops->disable(host, lazy); + host->en_dis_recurs = 0; + + if (err < 0) { + pr_debug("%s: disable error %d\n", + mmc_hostname(host), err); + return err; + } + if (err > 0) { + unsigned long delay = msecs_to_jiffies(err); + + mmc_schedule_delayed_work(&host->disable, delay); + } + } + host->enabled = 0; + return 0; +} + +/** + * mmc_host_disable - disable a host. + * @host: mmc host to disable + * + * Hosts that support power saving can use the 'enable' and 'disable' + * methods to exit and enter power saving states. For more information + * see comments for struct mmc_host_ops. + */ +int mmc_host_disable(struct mmc_host *host) +{ + int err; + + if (!(host->caps & MMC_CAP_DISABLE)) + return 0; + + if (host->en_dis_recurs) + return 0; + + if (--host->nesting_cnt) + return 0; + + if (!host->enabled) + return 0; + + err = mmc_host_do_disable(host, 0); + return err; +} +EXPORT_SYMBOL(mmc_host_disable); + +/** * __mmc_claim_host - exclusively claim a host * @host: mmc host to claim * @abort: whether or not the operation should be aborted @@ -366,25 +461,111 @@ int __mmc_claim_host(struct mmc_host *host, atomic_t *abort) while (1) { set_current_state(TASK_UNINTERRUPTIBLE); stop = abort ? atomic_read(abort) : 0; - if (stop || !host->claimed) + if (stop || !host->claimed || host->claimer == current) break; spin_unlock_irqrestore(&host->lock, flags); schedule(); spin_lock_irqsave(&host->lock, flags); } set_current_state(TASK_RUNNING); - if (!stop) + if (!stop) { host->claimed = 1; - else + host->claimer = current; + host->claim_cnt += 1; + } else wake_up(&host->wq); spin_unlock_irqrestore(&host->lock, flags); remove_wait_queue(&host->wq, &wait); + if (!stop) + mmc_host_enable(host); return stop; } EXPORT_SYMBOL(__mmc_claim_host); /** + * mmc_try_claim_host - try exclusively to claim a host + * @host: mmc host to claim + * + * Returns %1 if the host is claimed, %0 otherwise. + */ +int mmc_try_claim_host(struct mmc_host *host) +{ + int claimed_host = 0; + unsigned long flags; + + spin_lock_irqsave(&host->lock, flags); + if (!host->claimed || host->claimer == current) { + host->claimed = 1; + host->claimer = current; + host->claim_cnt += 1; + claimed_host = 1; + } + spin_unlock_irqrestore(&host->lock, flags); + return claimed_host; +} +EXPORT_SYMBOL(mmc_try_claim_host); + +static void mmc_do_release_host(struct mmc_host *host) +{ + unsigned long flags; + + spin_lock_irqsave(&host->lock, flags); + if (--host->claim_cnt) { + /* Release for nested claim */ + spin_unlock_irqrestore(&host->lock, flags); + } else { + host->claimed = 0; + host->claimer = NULL; + spin_unlock_irqrestore(&host->lock, flags); + wake_up(&host->wq); + } +} + +void mmc_host_deeper_disable(struct work_struct *work) +{ + struct mmc_host *host = + container_of(work, struct mmc_host, disable.work); + + /* If the host is claimed then we do not want to disable it anymore */ + if (!mmc_try_claim_host(host)) + return; + mmc_host_do_disable(host, 1); + mmc_do_release_host(host); +} + +/** + * mmc_host_lazy_disable - lazily disable a host. + * @host: mmc host to disable + * + * Hosts that support power saving can use the 'enable' and 'disable' + * methods to exit and enter power saving states. For more information + * see comments for struct mmc_host_ops. + */ +int mmc_host_lazy_disable(struct mmc_host *host) +{ + if (!(host->caps & MMC_CAP_DISABLE)) + return 0; + + if (host->en_dis_recurs) + return 0; + + if (--host->nesting_cnt) + return 0; + + if (!host->enabled) + return 0; + + if (host->disable_delay) { + mmc_schedule_delayed_work(&host->disable, + msecs_to_jiffies(host->disable_delay)); + return 0; + } else + return mmc_host_do_disable(host, 1); +} +EXPORT_SYMBOL(mmc_host_lazy_disable); + +/** * mmc_release_host - release a host * @host: mmc host to release * @@ -393,15 +574,11 @@ EXPORT_SYMBOL(__mmc_claim_host); */ void mmc_release_host(struct mmc_host *host) { - unsigned long flags; - WARN_ON(!host->claimed); - spin_lock_irqsave(&host->lock, flags); - host->claimed = 0; - spin_unlock_irqrestore(&host->lock, flags); + mmc_host_lazy_disable(host); - wake_up(&host->wq); + mmc_do_release_host(host); } EXPORT_SYMBOL(mmc_release_host); @@ -687,7 +864,13 @@ void mmc_set_timing(struct mmc_host *host, unsigned int timing) */ static void mmc_power_up(struct mmc_host *host) { - int bit = fls(host->ocr_avail) - 1; + int bit; + + /* If ocr is set, we use it */ + if (host->ocr) + bit = ffs(host->ocr) - 1; + else + bit = fls(host->ocr_avail) - 1; host->ios.vdd = bit; if (mmc_host_is_spi(host)) { @@ -947,6 +1130,8 @@ void mmc_stop_host(struct mmc_host *host) spin_unlock_irqrestore(&host->lock, flags); #endif + if (host->caps & MMC_CAP_DISABLE) + cancel_delayed_work(&host->disable); cancel_delayed_work(&host->detect); mmc_flush_scheduled_work(); @@ -958,6 +1143,8 @@ void mmc_stop_host(struct mmc_host *host) mmc_claim_host(host); mmc_detach_bus(host); mmc_release_host(host); + mmc_bus_put(host); + return; } mmc_bus_put(host); @@ -966,6 +1153,80 @@ void mmc_stop_host(struct mmc_host *host) mmc_power_off(host); } +void mmc_power_save_host(struct mmc_host *host) +{ + mmc_bus_get(host); + + if (!host->bus_ops || host->bus_dead || !host->bus_ops->power_restore) { + mmc_bus_put(host); + return; + } + + if (host->bus_ops->power_save) + host->bus_ops->power_save(host); + + mmc_bus_put(host); + + mmc_power_off(host); +} +EXPORT_SYMBOL(mmc_power_save_host); + +void mmc_power_restore_host(struct mmc_host *host) +{ + mmc_bus_get(host); + + if (!host->bus_ops || host->bus_dead || !host->bus_ops->power_restore) { + mmc_bus_put(host); + return; + } + + mmc_power_up(host); + host->bus_ops->power_restore(host); + + mmc_bus_put(host); +} +EXPORT_SYMBOL(mmc_power_restore_host); + +int mmc_card_awake(struct mmc_host *host) +{ + int err = -ENOSYS; + + mmc_bus_get(host); + + if (host->bus_ops && !host->bus_dead && host->bus_ops->awake) + err = host->bus_ops->awake(host); + + mmc_bus_put(host); + + return err; +} +EXPORT_SYMBOL(mmc_card_awake); + +int mmc_card_sleep(struct mmc_host *host) +{ + int err = -ENOSYS; + + mmc_bus_get(host); + + if (host->bus_ops && !host->bus_dead && host->bus_ops->awake) + err = host->bus_ops->sleep(host); + + mmc_bus_put(host); + + return err; +} +EXPORT_SYMBOL(mmc_card_sleep); + +int mmc_card_can_sleep(struct mmc_host *host) +{ + struct mmc_card *card = host->card; + + if (card && mmc_card_mmc(card) && card->ext_csd.rev >= 3) + return 1; + return 0; +} +EXPORT_SYMBOL(mmc_card_can_sleep); + #ifdef CONFIG_PM /** @@ -975,27 +1236,36 @@ void mmc_stop_host(struct mmc_host *host) */ int mmc_suspend_host(struct mmc_host *host, pm_message_t state) { + int err = 0; + + if (host->caps & MMC_CAP_DISABLE) + cancel_delayed_work(&host->disable); cancel_delayed_work(&host->detect); mmc_flush_scheduled_work(); mmc_bus_get(host); if (host->bus_ops && !host->bus_dead) { if (host->bus_ops->suspend) - host->bus_ops->suspend(host); - if (!host->bus_ops->resume) { + err = host->bus_ops->suspend(host); + if (err == -ENOSYS || !host->bus_ops->resume) { + /* + * We simply "remove" the card in this case. + * It will be redetected on resume. + */ if (host->bus_ops->remove) host->bus_ops->remove(host); - mmc_claim_host(host); mmc_detach_bus(host); mmc_release_host(host); + err = 0; } } mmc_bus_put(host); - mmc_power_off(host); + if (!err) + mmc_power_off(host); - return 0; + return err; } EXPORT_SYMBOL(mmc_suspend_host); @@ -1006,12 +1276,26 @@ EXPORT_SYMBOL(mmc_suspend_host); */ int mmc_resume_host(struct mmc_host *host) { + int err = 0; + mmc_bus_get(host); if (host->bus_ops && !host->bus_dead) { mmc_power_up(host); mmc_select_voltage(host, host->ocr); BUG_ON(!host->bus_ops->resume); - host->bus_ops->resume(host); + err = host->bus_ops->resume(host); + if (err) { + printk(KERN_WARNING "%s: error %d during resume " + "(card was removed?)\n", + mmc_hostname(host), err); + if (host->bus_ops->remove) + host->bus_ops->remove(host); + mmc_claim_host(host); + mmc_detach_bus(host); + mmc_release_host(host); + /* no need to bother upper layers */ + err = 0; + } } mmc_bus_put(host); @@ -1021,7 +1305,7 @@ int mmc_resume_host(struct mmc_host *host) */ mmc_detect_change(host, 1); - return 0; + return err; } EXPORT_SYMBOL(mmc_resume_host); diff --git a/drivers/mmc/core/core.h b/drivers/mmc/core/core.h index c819effa1032..67ae6abc4230 100644 --- a/drivers/mmc/core/core.h +++ b/drivers/mmc/core/core.h @@ -16,10 +16,14 @@ #define MMC_CMD_RETRIES 3 struct mmc_bus_ops { + int (*awake)(struct mmc_host *); + int (*sleep)(struct mmc_host *); void (*remove)(struct mmc_host *); void (*detect)(struct mmc_host *); - void (*suspend)(struct mmc_host *); - void (*resume)(struct mmc_host *); + int (*suspend)(struct mmc_host *); + int (*resume)(struct mmc_host *); + void (*power_save)(struct mmc_host *); + void (*power_restore)(struct mmc_host *); }; void mmc_attach_bus(struct mmc_host *host, const struct mmc_bus_ops *ops); diff --git a/drivers/mmc/core/debugfs.c b/drivers/mmc/core/debugfs.c index 610dbd1fcc82..96d10f40fb23 100644 --- a/drivers/mmc/core/debugfs.c +++ b/drivers/mmc/core/debugfs.c @@ -240,7 +240,7 @@ static int mmc_ext_csd_release(struct inode *inode, struct file *file) return 0; } -static struct file_operations mmc_dbg_ext_csd_fops = { +static const struct file_operations mmc_dbg_ext_csd_fops = { .open = mmc_ext_csd_open, .read = mmc_ext_csd_read, .release = mmc_ext_csd_release, diff --git a/drivers/mmc/core/host.c b/drivers/mmc/core/host.c index 5e945e64ead7..a268d12f1af0 100644 --- a/drivers/mmc/core/host.c +++ b/drivers/mmc/core/host.c @@ -83,6 +83,7 @@ struct mmc_host *mmc_alloc_host(int extra, struct device *dev) spin_lock_init(&host->lock); init_waitqueue_head(&host->wq); INIT_DELAYED_WORK(&host->detect, mmc_rescan); + INIT_DELAYED_WORK_DEFERRABLE(&host->disable, mmc_host_deeper_disable); /* * By default, hosts do not support SGIO or large requests. diff --git a/drivers/mmc/core/host.h b/drivers/mmc/core/host.h index c2dc3d2d9f9a..8c87e1109a34 100644 --- a/drivers/mmc/core/host.h +++ b/drivers/mmc/core/host.h @@ -14,5 +14,7 @@ int mmc_register_host_class(void); void mmc_unregister_host_class(void); +void mmc_host_deeper_disable(struct work_struct *work); + #endif diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index 2fb9d5f271ea..bfefce365ae7 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -160,7 +160,6 @@ static int mmc_read_ext_csd(struct mmc_card *card) { int err; u8 *ext_csd; - unsigned int ext_csd_struct; BUG_ON(!card); @@ -180,11 +179,11 @@ static int mmc_read_ext_csd(struct mmc_card *card) err = mmc_send_ext_csd(card, ext_csd); if (err) { - /* - * We all hosts that cannot perform the command - * to fail more gracefully - */ - if (err != -EINVAL) + /* If the host or the card can't do the switch, + * fail more gracefully. */ + if ((err != -EINVAL) + && (err != -ENOSYS) + && (err != -EFAULT)) goto out; /* @@ -207,16 +206,16 @@ static int mmc_read_ext_csd(struct mmc_card *card) goto out; } - ext_csd_struct = ext_csd[EXT_CSD_REV]; - if (ext_csd_struct > 3) { + card->ext_csd.rev = ext_csd[EXT_CSD_REV]; + if (card->ext_csd.rev > 3) { printk(KERN_ERR "%s: unrecognised EXT_CSD structure " "version %d\n", mmc_hostname(card->host), - ext_csd_struct); + card->ext_csd.rev); err = -EINVAL; goto out; } - if (ext_csd_struct >= 2) { + if (card->ext_csd.rev >= 2) { card->ext_csd.sectors = ext_csd[EXT_CSD_SEC_CNT + 0] << 0 | ext_csd[EXT_CSD_SEC_CNT + 1] << 8 | @@ -241,6 +240,15 @@ static int mmc_read_ext_csd(struct mmc_card *card) goto out; } + if (card->ext_csd.rev >= 3) { + u8 sa_shift = ext_csd[EXT_CSD_S_A_TIMEOUT]; + + /* Sleep / awake timeout in 100ns units */ + if (sa_shift > 0 && sa_shift <= 0x17) + card->ext_csd.sa_timeout = + 1 << ext_csd[EXT_CSD_S_A_TIMEOUT]; + } + out: kfree(ext_csd); @@ -408,12 +416,17 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, (host->caps & MMC_CAP_MMC_HIGHSPEED)) { err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_HS_TIMING, 1); - if (err) + if (err && err != -EBADMSG) goto free_card; - mmc_card_set_highspeed(card); - - mmc_set_timing(card->host, MMC_TIMING_MMC_HS); + if (err) { + printk(KERN_WARNING "%s: switch to highspeed failed\n", + mmc_hostname(card->host)); + err = 0; + } else { + mmc_card_set_highspeed(card); + mmc_set_timing(card->host, MMC_TIMING_MMC_HS); + } } /* @@ -448,10 +461,17 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_BUS_WIDTH, ext_csd_bit); - if (err) + if (err && err != -EBADMSG) goto free_card; - mmc_set_bus_width(card->host, bus_width); + if (err) { + printk(KERN_WARNING "%s: switch to bus width %d " + "failed\n", mmc_hostname(card->host), + 1 << bus_width); + err = 0; + } else { + mmc_set_bus_width(card->host, bus_width); + } } if (!oldcard) @@ -507,12 +527,10 @@ static void mmc_detect(struct mmc_host *host) } } -#ifdef CONFIG_MMC_UNSAFE_RESUME - /* * Suspend callback from host. */ -static void mmc_suspend(struct mmc_host *host) +static int mmc_suspend(struct mmc_host *host) { BUG_ON(!host); BUG_ON(!host->card); @@ -522,6 +540,8 @@ static void mmc_suspend(struct mmc_host *host) mmc_deselect_cards(host); host->card->state &= ~MMC_STATE_HIGHSPEED; mmc_release_host(host); + + return 0; } /* @@ -530,7 +550,7 @@ static void mmc_suspend(struct mmc_host *host) * This function tries to determine if the same card is still present * and, if so, restore all state to it. */ -static void mmc_resume(struct mmc_host *host) +static int mmc_resume(struct mmc_host *host) { int err; @@ -541,30 +561,99 @@ static void mmc_resume(struct mmc_host *host) err = mmc_init_card(host, host->ocr, host->card); mmc_release_host(host); - if (err) { - mmc_remove(host); + return err; +} - mmc_claim_host(host); - mmc_detach_bus(host); - mmc_release_host(host); +static void mmc_power_restore(struct mmc_host *host) +{ + host->card->state &= ~MMC_STATE_HIGHSPEED; + mmc_claim_host(host); + mmc_init_card(host, host->ocr, host->card); + mmc_release_host(host); +} + +static int mmc_sleep(struct mmc_host *host) +{ + struct mmc_card *card = host->card; + int err = -ENOSYS; + + if (card && card->ext_csd.rev >= 3) { + err = mmc_card_sleepawake(host, 1); + if (err < 0) + pr_debug("%s: Error %d while putting card into sleep", + mmc_hostname(host), err); } + return err; } -#else +static int mmc_awake(struct mmc_host *host) +{ + struct mmc_card *card = host->card; + int err = -ENOSYS; + + if (card && card->ext_csd.rev >= 3) { + err = mmc_card_sleepawake(host, 0); + if (err < 0) + pr_debug("%s: Error %d while awaking sleeping card", + mmc_hostname(host), err); + } + + return err; +} -#define mmc_suspend NULL -#define mmc_resume NULL +#ifdef CONFIG_MMC_UNSAFE_RESUME -#endif +static const struct mmc_bus_ops mmc_ops = { + .awake = mmc_awake, + .sleep = mmc_sleep, + .remove = mmc_remove, + .detect = mmc_detect, + .suspend = mmc_suspend, + .resume = mmc_resume, + .power_restore = mmc_power_restore, +}; + +static void mmc_attach_bus_ops(struct mmc_host *host) +{ + mmc_attach_bus(host, &mmc_ops); +} + +#else static const struct mmc_bus_ops mmc_ops = { + .awake = mmc_awake, + .sleep = mmc_sleep, + .remove = mmc_remove, + .detect = mmc_detect, + .suspend = NULL, + .resume = NULL, + .power_restore = mmc_power_restore, +}; + +static const struct mmc_bus_ops mmc_ops_unsafe = { + .awake = mmc_awake, + .sleep = mmc_sleep, .remove = mmc_remove, .detect = mmc_detect, .suspend = mmc_suspend, .resume = mmc_resume, + .power_restore = mmc_power_restore, }; +static void mmc_attach_bus_ops(struct mmc_host *host) +{ + const struct mmc_bus_ops *bus_ops; + + if (host->caps & MMC_CAP_NONREMOVABLE) + bus_ops = &mmc_ops_unsafe; + else + bus_ops = &mmc_ops; + mmc_attach_bus(host, bus_ops); +} + +#endif + /* * Starting point for MMC card init. */ @@ -575,7 +664,7 @@ int mmc_attach_mmc(struct mmc_host *host, u32 ocr) BUG_ON(!host); WARN_ON(!host->claimed); - mmc_attach_bus(host, &mmc_ops); + mmc_attach_bus_ops(host); /* * We need to get OCR a different way for SPI. diff --git a/drivers/mmc/core/mmc_ops.c b/drivers/mmc/core/mmc_ops.c index 34ce2703d29a..d2cb5c634392 100644 --- a/drivers/mmc/core/mmc_ops.c +++ b/drivers/mmc/core/mmc_ops.c @@ -57,6 +57,42 @@ int mmc_deselect_cards(struct mmc_host *host) return _mmc_select_card(host, NULL); } +int mmc_card_sleepawake(struct mmc_host *host, int sleep) +{ + struct mmc_command cmd; + struct mmc_card *card = host->card; + int err; + + if (sleep) + mmc_deselect_cards(host); + + memset(&cmd, 0, sizeof(struct mmc_command)); + + cmd.opcode = MMC_SLEEP_AWAKE; + cmd.arg = card->rca << 16; + if (sleep) + cmd.arg |= 1 << 15; + + cmd.flags = MMC_RSP_R1B | MMC_CMD_AC; + err = mmc_wait_for_cmd(host, &cmd, 0); + if (err) + return err; + + /* + * If the host does not wait while the card signals busy, then we will + * will have to wait the sleep/awake timeout. Note, we cannot use the + * SEND_STATUS command to poll the status because that command (and most + * others) is invalid while the card sleeps. + */ + if (!(host->caps & MMC_CAP_WAIT_WHILE_BUSY)) + mmc_delay(DIV_ROUND_UP(card->ext_csd.sa_timeout, 10000)); + + if (!sleep) + err = mmc_select_card(card); + + return err; +} + int mmc_go_idle(struct mmc_host *host) { int err; @@ -354,6 +390,7 @@ int mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value) { int err; struct mmc_command cmd; + u32 status; BUG_ON(!card); BUG_ON(!card->host); @@ -371,6 +408,28 @@ int mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value) if (err) return err; + /* Must check status to be sure of no errors */ + do { + err = mmc_send_status(card, &status); + if (err) + return err; + if (card->host->caps & MMC_CAP_WAIT_WHILE_BUSY) + break; + if (mmc_host_is_spi(card->host)) + break; + } while (R1_CURRENT_STATE(status) == 7); + + if (mmc_host_is_spi(card->host)) { + if (status & R1_SPI_ILLEGAL_COMMAND) + return -EBADMSG; + } else { + if (status & 0xFDFFA000) + printk(KERN_WARNING "%s: unexpected status %#x after " + "switch", mmc_hostname(card->host), status); + if (status & R1_SWITCH_ERROR) + return -EBADMSG; + } + return 0; } diff --git a/drivers/mmc/core/mmc_ops.h b/drivers/mmc/core/mmc_ops.h index 17854bf7cf0d..653eb8e84178 100644 --- a/drivers/mmc/core/mmc_ops.h +++ b/drivers/mmc/core/mmc_ops.h @@ -25,6 +25,7 @@ int mmc_send_status(struct mmc_card *card, u32 *status); int mmc_send_cid(struct mmc_host *host, u32 *cid); int mmc_spi_read_ocr(struct mmc_host *host, int highcap, u32 *ocrp); int mmc_spi_set_crc(struct mmc_host *host, int use_crc); +int mmc_card_sleepawake(struct mmc_host *host, int sleep); #endif diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c index 7ad646fe077e..10b2a4d20f5a 100644 --- a/drivers/mmc/core/sd.c +++ b/drivers/mmc/core/sd.c @@ -210,11 +210,11 @@ static int mmc_read_switch(struct mmc_card *card) err = mmc_sd_switch(card, 0, 0, 1, status); if (err) { - /* - * We all hosts that cannot perform the command - * to fail more gracefully - */ - if (err != -EINVAL) + /* If the host or the card can't do the switch, + * fail more gracefully. */ + if ((err != -EINVAL) + && (err != -ENOSYS) + && (err != -EFAULT)) goto out; printk(KERN_WARNING "%s: problem reading switch " @@ -561,12 +561,10 @@ static void mmc_sd_detect(struct mmc_host *host) } } -#ifdef CONFIG_MMC_UNSAFE_RESUME - /* * Suspend callback from host. */ -static void mmc_sd_suspend(struct mmc_host *host) +static int mmc_sd_suspend(struct mmc_host *host) { BUG_ON(!host); BUG_ON(!host->card); @@ -576,6 +574,8 @@ static void mmc_sd_suspend(struct mmc_host *host) mmc_deselect_cards(host); host->card->state &= ~MMC_STATE_HIGHSPEED; mmc_release_host(host); + + return 0; } /* @@ -584,7 +584,7 @@ static void mmc_sd_suspend(struct mmc_host *host) * This function tries to determine if the same card is still present * and, if so, restore all state to it. */ -static void mmc_sd_resume(struct mmc_host *host) +static int mmc_sd_resume(struct mmc_host *host) { int err; @@ -595,30 +595,63 @@ static void mmc_sd_resume(struct mmc_host *host) err = mmc_sd_init_card(host, host->ocr, host->card); mmc_release_host(host); - if (err) { - mmc_sd_remove(host); - - mmc_claim_host(host); - mmc_detach_bus(host); - mmc_release_host(host); - } + return err; +} +static void mmc_sd_power_restore(struct mmc_host *host) +{ + host->card->state &= ~MMC_STATE_HIGHSPEED; + mmc_claim_host(host); + mmc_sd_init_card(host, host->ocr, host->card); + mmc_release_host(host); } -#else +#ifdef CONFIG_MMC_UNSAFE_RESUME -#define mmc_sd_suspend NULL -#define mmc_sd_resume NULL +static const struct mmc_bus_ops mmc_sd_ops = { + .remove = mmc_sd_remove, + .detect = mmc_sd_detect, + .suspend = mmc_sd_suspend, + .resume = mmc_sd_resume, + .power_restore = mmc_sd_power_restore, +}; -#endif +static void mmc_sd_attach_bus_ops(struct mmc_host *host) +{ + mmc_attach_bus(host, &mmc_sd_ops); +} + +#else static const struct mmc_bus_ops mmc_sd_ops = { .remove = mmc_sd_remove, .detect = mmc_sd_detect, + .suspend = NULL, + .resume = NULL, + .power_restore = mmc_sd_power_restore, +}; + +static const struct mmc_bus_ops mmc_sd_ops_unsafe = { + .remove = mmc_sd_remove, + .detect = mmc_sd_detect, .suspend = mmc_sd_suspend, .resume = mmc_sd_resume, + .power_restore = mmc_sd_power_restore, }; +static void mmc_sd_attach_bus_ops(struct mmc_host *host) +{ + const struct mmc_bus_ops *bus_ops; + + if (host->caps & MMC_CAP_NONREMOVABLE) + bus_ops = &mmc_sd_ops_unsafe; + else + bus_ops = &mmc_sd_ops; + mmc_attach_bus(host, bus_ops); +} + +#endif + /* * Starting point for SD card init. */ @@ -629,7 +662,7 @@ int mmc_attach_sd(struct mmc_host *host, u32 ocr) BUG_ON(!host); WARN_ON(!host->claimed); - mmc_attach_bus(host, &mmc_sd_ops); + mmc_sd_attach_bus_ops(host); /* * We need to get OCR a different way for SPI. diff --git a/drivers/mmc/core/sdio.c b/drivers/mmc/core/sdio.c index fb99ccff9080..cdb845b68ab5 100644 --- a/drivers/mmc/core/sdio.c +++ b/drivers/mmc/core/sdio.c @@ -165,6 +165,29 @@ static int sdio_enable_wide(struct mmc_card *card) } /* + * If desired, disconnect the pull-up resistor on CD/DAT[3] (pin 1) + * of the card. This may be required on certain setups of boards, + * controllers and embedded sdio device which do not need the card's + * pull-up. As a result, card detection is disabled and power is saved. + */ +static int sdio_disable_cd(struct mmc_card *card) +{ + int ret; + u8 ctrl; + + if (!card->cccr.disable_cd) + return 0; + + ret = mmc_io_rw_direct(card, 0, 0, SDIO_CCCR_IF, 0, &ctrl); + if (ret) + return ret; + + ctrl |= SDIO_BUS_CD_DISABLE; + + return mmc_io_rw_direct(card, 1, 0, SDIO_CCCR_IF, ctrl, NULL); +} + +/* * Test if the card supports high-speed mode and, if so, switch to it. */ static int sdio_enable_hs(struct mmc_card *card) @@ -195,6 +218,135 @@ static int sdio_enable_hs(struct mmc_card *card) } /* + * Handle the detection and initialisation of a card. + * + * In the case of a resume, "oldcard" will contain the card + * we're trying to reinitialise. + */ +static int mmc_sdio_init_card(struct mmc_host *host, u32 ocr, + struct mmc_card *oldcard) +{ + struct mmc_card *card; + int err; + + BUG_ON(!host); + WARN_ON(!host->claimed); + + /* + * Inform the card of the voltage + */ + err = mmc_send_io_op_cond(host, host->ocr, &ocr); + if (err) + goto err; + + /* + * For SPI, enable CRC as appropriate. + */ + if (mmc_host_is_spi(host)) { + err = mmc_spi_set_crc(host, use_spi_crc); + if (err) + goto err; + } + + /* + * Allocate card structure. + */ + card = mmc_alloc_card(host, NULL); + if (IS_ERR(card)) { + err = PTR_ERR(card); + goto err; + } + + card->type = MMC_TYPE_SDIO; + + /* + * For native busses: set card RCA and quit open drain mode. + */ + if (!mmc_host_is_spi(host)) { + err = mmc_send_relative_addr(host, &card->rca); + if (err) + goto remove; + + mmc_set_bus_mode(host, MMC_BUSMODE_PUSHPULL); + } + + /* + * Select card, as all following commands rely on that. + */ + if (!mmc_host_is_spi(host)) { + err = mmc_select_card(card); + if (err) + goto remove; + } + + /* + * Read the common registers. + */ + err = sdio_read_cccr(card); + if (err) + goto remove; + + /* + * Read the common CIS tuples. + */ + err = sdio_read_common_cis(card); + if (err) + goto remove; + + if (oldcard) { + int same = (card->cis.vendor == oldcard->cis.vendor && + card->cis.device == oldcard->cis.device); + mmc_remove_card(card); + if (!same) { + err = -ENOENT; + goto err; + } + card = oldcard; + return 0; + } + + /* + * Switch to high-speed (if supported). + */ + err = sdio_enable_hs(card); + if (err) + goto remove; + + /* + * Change to the card's maximum speed. + */ + if (mmc_card_highspeed(card)) { + /* + * The SDIO specification doesn't mention how + * the CIS transfer speed register relates to + * high-speed, but it seems that 50 MHz is + * mandatory. + */ + mmc_set_clock(host, 50000000); + } else { + mmc_set_clock(host, card->cis.max_dtr); + } + + /* + * Switch to wider bus (if supported). + */ + err = sdio_enable_wide(card); + if (err) + goto remove; + + if (!oldcard) + host->card = card; + return 0; + +remove: + if (!oldcard) + mmc_remove_card(card); + +err: + return err; +} + +/* * Host is being removed. Free up the current card. */ static void mmc_sdio_remove(struct mmc_host *host) @@ -243,10 +395,77 @@ static void mmc_sdio_detect(struct mmc_host *host) } } +/* + * SDIO suspend. We need to suspend all functions separately. + * Therefore all registered functions must have drivers with suspend + * and resume methods. Failing that we simply remove the whole card. + */ +static int mmc_sdio_suspend(struct mmc_host *host) +{ + int i, err = 0; + + for (i = 0; i < host->card->sdio_funcs; i++) { + struct sdio_func *func = host->card->sdio_func[i]; + if (func && sdio_func_present(func) && func->dev.driver) { + const struct dev_pm_ops *pmops = func->dev.driver->pm; + if (!pmops || !pmops->suspend || !pmops->resume) { + /* force removal of entire card in that case */ + err = -ENOSYS; + } else + err = pmops->suspend(&func->dev); + if (err) + break; + } + } + while (err && --i >= 0) { + struct sdio_func *func = host->card->sdio_func[i]; + if (func && sdio_func_present(func) && func->dev.driver) { + const struct dev_pm_ops *pmops = func->dev.driver->pm; + pmops->resume(&func->dev); + } + } + + return err; +} + +static int mmc_sdio_resume(struct mmc_host *host) +{ + int i, err; + + BUG_ON(!host); + BUG_ON(!host->card); + + /* Basic card reinitialization. */ + mmc_claim_host(host); + err = mmc_sdio_init_card(host, host->ocr, host->card); + mmc_release_host(host); + + /* + * If the card looked to be the same as before suspending, then + * we proceed to resume all card functions. If one of them returns + * an error then we simply return that error to the core and the + * card will be redetected as new. It is the responsibility of + * the function driver to perform further tests with the extra + * knowledge it has of the card to confirm the card is indeed the + * same as before suspending (same MAC address for network cards, + * etc.) and return an error otherwise. + */ + for (i = 0; !err && i < host->card->sdio_funcs; i++) { + struct sdio_func *func = host->card->sdio_func[i]; + if (func && sdio_func_present(func) && func->dev.driver) { + const struct dev_pm_ops *pmops = func->dev.driver->pm; + err = pmops->resume(&func->dev); + } + } + + return err; +} static const struct mmc_bus_ops mmc_sdio_ops = { .remove = mmc_sdio_remove, .detect = mmc_sdio_detect, + .suspend = mmc_sdio_suspend, + .resume = mmc_sdio_resume, }; @@ -275,13 +494,6 @@ int mmc_attach_sdio(struct mmc_host *host, u32 ocr) ocr &= ~0x7F; } - if (ocr & MMC_VDD_165_195) { - printk(KERN_WARNING "%s: SDIO card claims to support the " - "incompletely defined 'low voltage range'. This " - "will be ignored.\n", mmc_hostname(host)); - ocr &= ~MMC_VDD_165_195; - } - host->ocr = mmc_select_voltage(host, ocr); /* @@ -293,101 +505,23 @@ int mmc_attach_sdio(struct mmc_host *host, u32 ocr) } /* - * Inform the card of the voltage + * Detect and init the card. */ - err = mmc_send_io_op_cond(host, host->ocr, &ocr); + err = mmc_sdio_init_card(host, host->ocr, NULL); if (err) goto err; - - /* - * For SPI, enable CRC as appropriate. - */ - if (mmc_host_is_spi(host)) { - err = mmc_spi_set_crc(host, use_spi_crc); - if (err) - goto err; - } + card = host->card; /* * The number of functions on the card is encoded inside * the ocr. */ - funcs = (ocr & 0x70000000) >> 28; - - /* - * Allocate card structure. - */ - card = mmc_alloc_card(host, NULL); - if (IS_ERR(card)) { - err = PTR_ERR(card); - goto err; - } - - card->type = MMC_TYPE_SDIO; - card->sdio_funcs = funcs; - - host->card = card; - - /* - * For native busses: set card RCA and quit open drain mode. - */ - if (!mmc_host_is_spi(host)) { - err = mmc_send_relative_addr(host, &card->rca); - if (err) - goto remove; - - mmc_set_bus_mode(host, MMC_BUSMODE_PUSHPULL); - } - - /* - * Select card, as all following commands rely on that. - */ - if (!mmc_host_is_spi(host)) { - err = mmc_select_card(card); - if (err) - goto remove; - } + card->sdio_funcs = funcs = (ocr & 0x70000000) >> 28; /* - * Read the common registers. + * If needed, disconnect card detection pull-up resistor. */ - err = sdio_read_cccr(card); - if (err) - goto remove; - - /* - * Read the common CIS tuples. - */ - err = sdio_read_common_cis(card); - if (err) - goto remove; - - /* - * Switch to high-speed (if supported). - */ - err = sdio_enable_hs(card); - if (err) - goto remove; - - /* - * Change to the card's maximum speed. - */ - if (mmc_card_highspeed(card)) { - /* - * The SDIO specification doesn't mention how - * the CIS transfer speed register relates to - * high-speed, but it seems that 50 MHz is - * mandatory. - */ - mmc_set_clock(host, 50000000); - } else { - mmc_set_clock(host, card->cis.max_dtr); - } - - /* - * Switch to wider bus (if supported). - */ - err = sdio_enable_wide(card); + err = sdio_disable_cd(card); if (err) goto remove; diff --git a/drivers/mmc/core/sdio_bus.c b/drivers/mmc/core/sdio_bus.c index 46284b527397..d37464e296a5 100644 --- a/drivers/mmc/core/sdio_bus.c +++ b/drivers/mmc/core/sdio_bus.c @@ -20,9 +20,6 @@ #include "sdio_cis.h" #include "sdio_bus.h" -#define dev_to_sdio_func(d) container_of(d, struct sdio_func, dev) -#define to_sdio_driver(d) container_of(d, struct sdio_driver, drv) - /* show configuration fields */ #define sdio_config_attr(field, format_string) \ static ssize_t \ diff --git a/drivers/mmc/core/sdio_cis.c b/drivers/mmc/core/sdio_cis.c index 963f2937c5e3..f85dcd536508 100644 --- a/drivers/mmc/core/sdio_cis.c +++ b/drivers/mmc/core/sdio_cis.c @@ -29,6 +29,8 @@ static int cistpl_vers_1(struct mmc_card *card, struct sdio_func *func, unsigned i, nr_strings; char **buffer, *string; + /* Find all null-terminated (including zero length) strings in + the TPLLV1_INFO field. Trailing garbage is ignored. */ buf += 2; size -= 2; @@ -39,11 +41,8 @@ static int cistpl_vers_1(struct mmc_card *card, struct sdio_func *func, if (buf[i] == 0) nr_strings++; } - - if (buf[i-1] != '\0') { - printk(KERN_WARNING "SDIO: ignoring broken CISTPL_VERS_1\n"); + if (nr_strings == 0) return 0; - } size = i; @@ -98,6 +97,22 @@ static const unsigned char speed_val[16] = static const unsigned int speed_unit[8] = { 10000, 100000, 1000000, 10000000, 0, 0, 0, 0 }; +/* FUNCE tuples with these types get passed to SDIO drivers */ +static const unsigned char funce_type_whitelist[] = { + 4 /* CISTPL_FUNCE_LAN_NODE_ID used in Broadcom cards */ +}; + +static int cistpl_funce_whitelisted(unsigned char type) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(funce_type_whitelist); i++) { + if (funce_type_whitelist[i] == type) + return 1; + } + return 0; +} + static int cistpl_funce_common(struct mmc_card *card, const unsigned char *buf, unsigned size) { @@ -120,6 +135,10 @@ static int cistpl_funce_func(struct sdio_func *func, unsigned vsn; unsigned min_size; + /* let SDIO drivers take care of whitelisted FUNCE tuples */ + if (cistpl_funce_whitelisted(buf[0])) + return -EILSEQ; + vsn = func->card->cccr.sdio_vsn; min_size = (vsn == SDIO_SDIO_REV_1_00) ? 28 : 42; @@ -154,13 +173,12 @@ static int cistpl_funce(struct mmc_card *card, struct sdio_func *func, else ret = cistpl_funce_common(card, buf, size); - if (ret) { + if (ret && ret != -EILSEQ) { printk(KERN_ERR "%s: bad CISTPL_FUNCE size %u " "type %u\n", mmc_hostname(card->host), size, buf[0]); - return ret; } - return 0; + return ret; } typedef int (tpl_parse_t)(struct mmc_card *, struct sdio_func *, @@ -253,21 +271,12 @@ static int sdio_read_cis(struct mmc_card *card, struct sdio_func *func) for (i = 0; i < ARRAY_SIZE(cis_tpl_list); i++) if (cis_tpl_list[i].code == tpl_code) break; - if (i >= ARRAY_SIZE(cis_tpl_list)) { - /* this tuple is unknown to the core */ - this->next = NULL; - this->code = tpl_code; - this->size = tpl_link; - *prev = this; - prev = &this->next; - printk(KERN_DEBUG - "%s: queuing CIS tuple 0x%02x length %u\n", - mmc_hostname(card->host), tpl_code, tpl_link); - } else { + if (i < ARRAY_SIZE(cis_tpl_list)) { const struct cis_tpl *tpl = cis_tpl_list + i; if (tpl_link < tpl->min_size) { printk(KERN_ERR - "%s: bad CIS tuple 0x%02x (length = %u, expected >= %u)\n", + "%s: bad CIS tuple 0x%02x" + " (length = %u, expected >= %u)\n", mmc_hostname(card->host), tpl_code, tpl_link, tpl->min_size); ret = -EINVAL; @@ -275,7 +284,30 @@ static int sdio_read_cis(struct mmc_card *card, struct sdio_func *func) ret = tpl->parse(card, func, this->data, tpl_link); } - kfree(this); + /* + * We don't need the tuple anymore if it was + * successfully parsed by the SDIO core or if it is + * not going to be parsed by SDIO drivers. + */ + if (!ret || ret != -EILSEQ) + kfree(this); + } else { + /* unknown tuple */ + ret = -EILSEQ; + } + + if (ret == -EILSEQ) { + /* this tuple is unknown to the core or whitelisted */ + this->next = NULL; + this->code = tpl_code; + this->size = tpl_link; + *prev = this; + prev = &this->next; + printk(KERN_DEBUG + "%s: queuing CIS tuple 0x%02x length %u\n", + mmc_hostname(card->host), tpl_code, tpl_link); + /* keep on analyzing tuples */ + ret = 0; } ptr += tpl_link; diff --git a/drivers/mmc/core/sdio_io.c b/drivers/mmc/core/sdio_io.c index f61fc2d4cd0a..f9aa8a7deffa 100644 --- a/drivers/mmc/core/sdio_io.c +++ b/drivers/mmc/core/sdio_io.c @@ -624,7 +624,7 @@ void sdio_f0_writeb(struct sdio_func *func, unsigned char b, unsigned int addr, BUG_ON(!func); - if (addr < 0xF0 || addr > 0xFF) { + if ((addr < 0xF0 || addr > 0xFF) && (!mmc_card_lenient_fn0(func->card))) { if (err_ret) *err_ret = -EINVAL; return; diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig index 891ef18bd77b..432ae8358c86 100644 --- a/drivers/mmc/host/Kconfig +++ b/drivers/mmc/host/Kconfig @@ -132,11 +132,11 @@ config MMC_OMAP config MMC_OMAP_HS tristate "TI OMAP High Speed Multimedia Card Interface support" - depends on ARCH_OMAP2430 || ARCH_OMAP3 + depends on ARCH_OMAP2430 || ARCH_OMAP3 || ARCH_OMAP4 help This selects the TI OMAP High Speed Multimedia card Interface. - If you have an OMAP2430 or OMAP3 board with a Multimedia Card slot, - say Y or M here. + If you have an OMAP2430 or OMAP3 board or OMAP4 board with a + Multimedia Card slot, say Y or M here. If unsure, say N. @@ -160,6 +160,12 @@ config MMC_AU1X If unsure, say N. +choice + prompt "Atmel SD/MMC Driver" + default MMC_ATMELMCI if AVR32 + help + Choose which driver to use for the Atmel MCI Silicon + config MMC_AT91 tristate "AT91 SD/MMC Card Interface support" depends on ARCH_AT91 @@ -170,17 +176,19 @@ config MMC_AT91 config MMC_ATMELMCI tristate "Atmel Multimedia Card Interface support" - depends on AVR32 + depends on AVR32 || ARCH_AT91 help This selects the Atmel Multimedia Card Interface driver. If - you have an AT32 (AVR32) platform with a Multimedia Card - slot, say Y or M here. + you have an AT32 (AVR32) or AT91 platform with a Multimedia + Card slot, say Y or M here. If unsure, say N. +endchoice + config MMC_ATMELMCI_DMA bool "Atmel MCI DMA support (EXPERIMENTAL)" - depends on MMC_ATMELMCI && DMA_ENGINE && EXPERIMENTAL + depends on MMC_ATMELMCI && AVR32 && DMA_ENGINE && EXPERIMENTAL help Say Y here to have the Atmel MCI driver use a DMA engine to do data transfers and thus increase the throughput and @@ -199,6 +207,13 @@ config MMC_IMX If unsure, say N. +config MMC_MSM7X00A + tristate "Qualcomm MSM 7X00A SDCC Controller Support" + depends on MMC && ARCH_MSM + help + This provides support for the SD/MMC cell found in the + MSM 7X00A controllers from Qualcomm. + config MMC_MXC tristate "Freescale i.MX2/3 Multimedia Card Interface support" depends on ARCH_MXC @@ -261,6 +276,47 @@ config MMC_S3C If unsure, say N. +config MMC_S3C_HW_SDIO_IRQ + bool "Hardware support for SDIO IRQ" + depends on MMC_S3C + help + Enable the hardware support for SDIO interrupts instead of using + the generic polling code. + +choice + prompt "Samsung S3C SD/MMC transfer code" + depends on MMC_S3C + +config MMC_S3C_PIO + bool "Use PIO transfers only" + help + Use PIO to transfer data between memory and the hardware. + + PIO is slower than DMA as it requires CPU instructions to + move the data. This has been the traditional default for + the S3C MCI driver. + +config MMC_S3C_DMA + bool "Use DMA transfers only (EXPERIMENTAL)" + depends on EXPERIMENTAL + help + Use DMA to transfer data between memory and the hardare. + + Currently, the DMA support in this driver seems to not be + working properly and needs to be debugged before this + option is useful. + +config MMC_S3C_PIODMA + bool "Support for both PIO and DMA (EXPERIMENTAL)" + help + Compile both the PIO and DMA transfer routines into the + driver and let the platform select at run-time which one + is best. + + See notes for the DMA option. + +endchoice + config MMC_SDRICOH_CS tristate "MMC/SD driver for Ricoh Bay1Controllers (EXPERIMENTAL)" depends on EXPERIMENTAL && PCI && PCMCIA diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile index cf153f628457..abcb0400e06d 100644 --- a/drivers/mmc/host/Makefile +++ b/drivers/mmc/host/Makefile @@ -23,6 +23,7 @@ obj-$(CONFIG_MMC_OMAP_HS) += omap_hsmmc.o obj-$(CONFIG_MMC_AT91) += at91_mci.o obj-$(CONFIG_MMC_ATMELMCI) += atmel-mci.o obj-$(CONFIG_MMC_TIFM_SD) += tifm_sd.o +obj-$(CONFIG_MMC_MSM7X00A) += msm_sdcc.o obj-$(CONFIG_MMC_MVSDIO) += mvsdio.o obj-$(CONFIG_MMC_SPI) += mmc_spi.o ifeq ($(CONFIG_OF),y) diff --git a/drivers/mmc/host/at91_mci.c b/drivers/mmc/host/at91_mci.c index e556d42cc45a..63924e0c7ea9 100644 --- a/drivers/mmc/host/at91_mci.c +++ b/drivers/mmc/host/at91_mci.c @@ -72,7 +72,6 @@ #include <asm/irq.h> #include <asm/gpio.h> -#include <asm/mach/mmc.h> #include <mach/board.h> #include <mach/cpu.h> #include <mach/at91_mci.h> diff --git a/drivers/mmc/host/atmel-mci.c b/drivers/mmc/host/atmel-mci.c index 7b603e4b41db..fc25586b7ee1 100644 --- a/drivers/mmc/host/atmel-mci.c +++ b/drivers/mmc/host/atmel-mci.c @@ -30,6 +30,7 @@ #include <asm/io.h> #include <asm/unaligned.h> +#include <mach/cpu.h> #include <mach/board.h> #include "atmel-mci-regs.h" @@ -210,6 +211,18 @@ struct atmel_mci_slot { set_bit(event, &host->pending_events) /* + * Enable or disable features/registers based on + * whether the processor supports them + */ +static bool mci_has_rwproof(void) +{ + if (cpu_is_at91sam9261() || cpu_is_at91rm9200()) + return false; + else + return true; +} + +/* * The debugfs stuff below is mostly optimized away when * CONFIG_DEBUG_FS is not set. */ @@ -276,8 +289,13 @@ static void atmci_show_status_reg(struct seq_file *s, [3] = "BLKE", [4] = "DTIP", [5] = "NOTBUSY", + [6] = "ENDRX", + [7] = "ENDTX", [8] = "SDIOIRQA", [9] = "SDIOIRQB", + [12] = "SDIOWAIT", + [14] = "RXBUFF", + [15] = "TXBUFE", [16] = "RINDE", [17] = "RDIRE", [18] = "RCRCE", @@ -285,6 +303,11 @@ static void atmci_show_status_reg(struct seq_file *s, [20] = "RTOE", [21] = "DCRCE", [22] = "DTOE", + [23] = "CSTOE", + [24] = "BLKOVRE", + [25] = "DMADONE", + [26] = "FIFOEMPTY", + [27] = "XFRDONE", [30] = "OVRE", [31] = "UNRE", }; @@ -576,6 +599,7 @@ atmci_submit_data_dma(struct atmel_mci *host, struct mmc_data *data) struct scatterlist *sg; unsigned int i; enum dma_data_direction direction; + unsigned int sglen; /* * We don't do DMA on "complex" transfers, i.e. with @@ -605,11 +629,14 @@ atmci_submit_data_dma(struct atmel_mci *host, struct mmc_data *data) else direction = DMA_TO_DEVICE; + sglen = dma_map_sg(&host->pdev->dev, data->sg, data->sg_len, direction); + if (sglen != data->sg_len) + goto unmap_exit; desc = chan->device->device_prep_slave_sg(chan, data->sg, data->sg_len, direction, DMA_PREP_INTERRUPT | DMA_CTRL_ACK); if (!desc) - return -ENOMEM; + goto unmap_exit; host->dma.data_desc = desc; desc->callback = atmci_dma_complete; @@ -620,6 +647,9 @@ atmci_submit_data_dma(struct atmel_mci *host, struct mmc_data *data) chan->device->device_issue_pending(chan); return 0; +unmap_exit: + dma_unmap_sg(&host->pdev->dev, data->sg, sglen, direction); + return -ENOMEM; } #else /* CONFIG_MMC_ATMELMCI_DMA */ @@ -849,13 +879,15 @@ static void atmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) clkdiv = 255; } + host->mode_reg = MCI_MR_CLKDIV(clkdiv); + /* * WRPROOF and RDPROOF prevent overruns/underruns by * stopping the clock when the FIFO is full/empty. * This state is not expected to last for long. */ - host->mode_reg = MCI_MR_CLKDIV(clkdiv) | MCI_MR_WRPROOF - | MCI_MR_RDPROOF; + if (mci_has_rwproof()) + host->mode_reg |= (MCI_MR_WRPROOF | MCI_MR_RDPROOF); if (list_empty(&host->queue)) mci_writel(host, MR, host->mode_reg); @@ -1648,8 +1680,10 @@ static int __init atmci_probe(struct platform_device *pdev) nr_slots++; } - if (!nr_slots) + if (!nr_slots) { + dev_err(&pdev->dev, "init failed: no slot defined\n"); goto err_init_slot; + } dev_info(&pdev->dev, "Atmel MCI controller at 0x%08lx irq %d, %u slots\n", diff --git a/drivers/mmc/host/mmc_spi.c b/drivers/mmc/host/mmc_spi.c index a461017ce5ce..d55fe4fb7935 100644 --- a/drivers/mmc/host/mmc_spi.c +++ b/drivers/mmc/host/mmc_spi.c @@ -1562,3 +1562,4 @@ MODULE_AUTHOR("Mike Lavender, David Brownell, " "Hans-Peter Nilsson, Jan Nikitenko"); MODULE_DESCRIPTION("SPI SD/MMC host driver"); MODULE_LICENSE("GPL"); +MODULE_ALIAS("spi:mmc_spi"); diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c index 8741d0f5146a..705a5894a6bb 100644 --- a/drivers/mmc/host/mmci.c +++ b/drivers/mmc/host/mmci.c @@ -22,12 +22,13 @@ #include <linux/clk.h> #include <linux/scatterlist.h> #include <linux/gpio.h> +#include <linux/amba/mmci.h> +#include <linux/regulator/consumer.h> #include <asm/cacheflush.h> #include <asm/div64.h> #include <asm/io.h> #include <asm/sizes.h> -#include <asm/mach/mmc.h> #include "mmci.h" @@ -38,6 +39,36 @@ static unsigned int fmax = 515633; +/* + * This must be called with host->lock held + */ +static void mmci_set_clkreg(struct mmci_host *host, unsigned int desired) +{ + u32 clk = 0; + + if (desired) { + if (desired >= host->mclk) { + clk = MCI_CLK_BYPASS; + host->cclk = host->mclk; + } else { + clk = host->mclk / (2 * desired) - 1; + if (clk >= 256) + clk = 255; + host->cclk = host->mclk / (2 * (clk + 1)); + } + if (host->hw_designer == 0x80) + clk |= MCI_FCEN; /* Bug fix in ST IP block */ + clk |= MCI_CLK_ENABLE; + /* This hasn't proven to be worthwhile */ + /* clk |= MCI_CLK_PWRSAVE; */ + } + + if (host->mmc->ios.bus_width == MMC_BUS_WIDTH_4) + clk |= MCI_WIDE_BUS; + + writel(clk, host->base + MMCICLOCK); +} + static void mmci_request_end(struct mmci_host *host, struct mmc_request *mrq) { @@ -419,30 +450,31 @@ static void mmci_request(struct mmc_host *mmc, struct mmc_request *mrq) static void mmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) { struct mmci_host *host = mmc_priv(mmc); - u32 clk = 0, pwr = 0; - - if (ios->clock) { - if (ios->clock >= host->mclk) { - clk = MCI_CLK_BYPASS; - host->cclk = host->mclk; - } else { - clk = host->mclk / (2 * ios->clock) - 1; - if (clk >= 256) - clk = 255; - host->cclk = host->mclk / (2 * (clk + 1)); - } - if (host->hw_designer == AMBA_VENDOR_ST) - clk |= MCI_FCEN; /* Bug fix in ST IP block */ - clk |= MCI_CLK_ENABLE; - } - - if (host->plat->translate_vdd) - pwr |= host->plat->translate_vdd(mmc_dev(mmc), ios->vdd); + u32 pwr = 0; + unsigned long flags; switch (ios->power_mode) { case MMC_POWER_OFF: + if(host->vcc && + regulator_is_enabled(host->vcc)) + regulator_disable(host->vcc); break; case MMC_POWER_UP: +#ifdef CONFIG_REGULATOR + if (host->vcc) + /* This implicitly enables the regulator */ + mmc_regulator_set_ocr(host->vcc, ios->vdd); +#endif + /* + * The translate_vdd function is not used if you have + * an external regulator, or your design is really weird. + * Using it would mean sending in power control BOTH using + * a regulator AND the 4 MMCIPWR bits. If we don't have + * a regulator, we might have some other platform specific + * power control behind this translate function. + */ + if (!host->vcc && host->plat->translate_vdd) + pwr |= host->plat->translate_vdd(mmc_dev(mmc), ios->vdd); /* The ST version does not have this, fall through to POWER_ON */ if (host->hw_designer != AMBA_VENDOR_ST) { pwr |= MCI_PWR_UP; @@ -465,12 +497,16 @@ static void mmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) } } - writel(clk, host->base + MMCICLOCK); + spin_lock_irqsave(&host->lock, flags); + + mmci_set_clkreg(host, ios->clock); if (host->pwr != pwr) { host->pwr = pwr; writel(pwr, host->base + MMCIPOWER); } + + spin_unlock_irqrestore(&host->lock, flags); } static int mmci_get_ro(struct mmc_host *mmc) @@ -517,7 +553,7 @@ static void mmci_check_status(unsigned long data) static int __devinit mmci_probe(struct amba_device *dev, struct amba_id *id) { - struct mmc_platform_data *plat = dev->dev.platform_data; + struct mmci_platform_data *plat = dev->dev.platform_data; struct mmci_host *host; struct mmc_host *mmc; int ret; @@ -583,7 +619,30 @@ static int __devinit mmci_probe(struct amba_device *dev, struct amba_id *id) mmc->ops = &mmci_ops; mmc->f_min = (host->mclk + 511) / 512; mmc->f_max = min(host->mclk, fmax); - mmc->ocr_avail = plat->ocr_mask; +#ifdef CONFIG_REGULATOR + /* If we're using the regulator framework, try to fetch a regulator */ + host->vcc = regulator_get(&dev->dev, "vmmc"); + if (IS_ERR(host->vcc)) + host->vcc = NULL; + else { + int mask = mmc_regulator_get_ocrmask(host->vcc); + + if (mask < 0) + dev_err(&dev->dev, "error getting OCR mask (%d)\n", + mask); + else { + host->mmc->ocr_avail = (u32) mask; + if (plat->ocr_mask) + dev_warn(&dev->dev, + "Provided ocr_mask/setpower will not be used " + "(using regulator instead)\n"); + } + } +#endif + /* Fall back to platform data if no regulator is found */ + if (host->vcc == NULL) + mmc->ocr_avail = plat->ocr_mask; + mmc->caps = plat->capabilities; /* * We can do SGIO @@ -619,7 +678,6 @@ static int __devinit mmci_probe(struct amba_device *dev, struct amba_id *id) writel(0, host->base + MMCIMASK1); writel(0xfff, host->base + MMCICLEAR); -#ifdef CONFIG_GPIOLIB if (gpio_is_valid(plat->gpio_cd)) { ret = gpio_request(plat->gpio_cd, DRIVER_NAME " (cd)"); if (ret == 0) @@ -638,7 +696,6 @@ static int __devinit mmci_probe(struct amba_device *dev, struct amba_id *id) else if (ret != -ENOSYS) goto err_gpio_wp; } -#endif ret = request_irq(dev->irq[0], mmci_irq, IRQF_SHARED, DRIVER_NAME " (cmd)", host); if (ret) @@ -720,6 +777,10 @@ static int __devexit mmci_remove(struct amba_device *dev) clk_disable(host->clk); clk_put(host->clk); + if (regulator_is_enabled(host->vcc)) + regulator_disable(host->vcc); + regulator_put(host->vcc); + mmc_free_host(mmc); amba_release_regions(dev); diff --git a/drivers/mmc/host/mmci.h b/drivers/mmc/host/mmci.h index 839f264c9725..1ceb9a90f59b 100644 --- a/drivers/mmc/host/mmci.h +++ b/drivers/mmc/host/mmci.h @@ -161,7 +161,7 @@ struct mmci_host { unsigned int mclk; unsigned int cclk; u32 pwr; - struct mmc_platform_data *plat; + struct mmci_platform_data *plat; u8 hw_designer; u8 hw_revision:4; @@ -175,6 +175,7 @@ struct mmci_host { struct scatterlist *sg_ptr; unsigned int sg_off; unsigned int size; + struct regulator *vcc; }; static inline void mmci_init_sg(struct mmci_host *host, struct mmc_data *data) diff --git a/drivers/mmc/host/msm_sdcc.c b/drivers/mmc/host/msm_sdcc.c new file mode 100644 index 000000000000..dba4600bcdb4 --- /dev/null +++ b/drivers/mmc/host/msm_sdcc.c @@ -0,0 +1,1287 @@ +/* + * linux/drivers/mmc/host/msm_sdcc.c - Qualcomm MSM 7X00A SDCC Driver + * + * Copyright (C) 2007 Google Inc, + * Copyright (C) 2003 Deep Blue Solutions, Ltd, All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Based on mmci.c + * + * Author: San Mehat (san@android.com) + * + */ + +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/init.h> +#include <linux/ioport.h> +#include <linux/device.h> +#include <linux/interrupt.h> +#include <linux/delay.h> +#include <linux/err.h> +#include <linux/highmem.h> +#include <linux/log2.h> +#include <linux/mmc/host.h> +#include <linux/mmc/card.h> +#include <linux/clk.h> +#include <linux/scatterlist.h> +#include <linux/platform_device.h> +#include <linux/dma-mapping.h> +#include <linux/debugfs.h> +#include <linux/io.h> +#include <linux/memory.h> + +#include <asm/cacheflush.h> +#include <asm/div64.h> +#include <asm/sizes.h> + +#include <asm/mach/mmc.h> +#include <mach/msm_iomap.h> +#include <mach/dma.h> +#include <mach/htc_pwrsink.h> + +#include "msm_sdcc.h" + +#define DRIVER_NAME "msm-sdcc" + +static unsigned int msmsdcc_fmin = 144000; +static unsigned int msmsdcc_fmax = 50000000; +static unsigned int msmsdcc_4bit = 1; +static unsigned int msmsdcc_pwrsave = 1; +static unsigned int msmsdcc_piopoll = 1; +static unsigned int msmsdcc_sdioirq; + +#define PIO_SPINMAX 30 +#define CMD_SPINMAX 20 + +static void +msmsdcc_start_command(struct msmsdcc_host *host, struct mmc_command *cmd, + u32 c); + +static void +msmsdcc_request_end(struct msmsdcc_host *host, struct mmc_request *mrq) +{ + writel(0, host->base + MMCICOMMAND); + + BUG_ON(host->curr.data); + + host->curr.mrq = NULL; + host->curr.cmd = NULL; + + if (mrq->data) + mrq->data->bytes_xfered = host->curr.data_xfered; + if (mrq->cmd->error == -ETIMEDOUT) + mdelay(5); + + /* + * Need to drop the host lock here; mmc_request_done may call + * back into the driver... + */ + spin_unlock(&host->lock); + mmc_request_done(host->mmc, mrq); + spin_lock(&host->lock); +} + +static void +msmsdcc_stop_data(struct msmsdcc_host *host) +{ + writel(0, host->base + MMCIDATACTRL); + host->curr.data = NULL; + host->curr.got_dataend = host->curr.got_datablkend = 0; +} + +uint32_t msmsdcc_fifo_addr(struct msmsdcc_host *host) +{ + switch (host->pdev_id) { + case 1: + return MSM_SDC1_PHYS + MMCIFIFO; + case 2: + return MSM_SDC2_PHYS + MMCIFIFO; + case 3: + return MSM_SDC3_PHYS + MMCIFIFO; + case 4: + return MSM_SDC4_PHYS + MMCIFIFO; + } + BUG(); + return 0; +} + +static void +msmsdcc_dma_complete_func(struct msm_dmov_cmd *cmd, + unsigned int result, + struct msm_dmov_errdata *err) +{ + struct msmsdcc_dma_data *dma_data = + container_of(cmd, struct msmsdcc_dma_data, hdr); + struct msmsdcc_host *host = dma_data->host; + unsigned long flags; + struct mmc_request *mrq; + + spin_lock_irqsave(&host->lock, flags); + mrq = host->curr.mrq; + BUG_ON(!mrq); + + if (!(result & DMOV_RSLT_VALID)) { + pr_err("msmsdcc: Invalid DataMover result\n"); + goto out; + } + + if (result & DMOV_RSLT_DONE) { + host->curr.data_xfered = host->curr.xfer_size; + } else { + /* Error or flush */ + if (result & DMOV_RSLT_ERROR) + pr_err("%s: DMA error (0x%.8x)\n", + mmc_hostname(host->mmc), result); + if (result & DMOV_RSLT_FLUSH) + pr_err("%s: DMA channel flushed (0x%.8x)\n", + mmc_hostname(host->mmc), result); + if (err) + pr_err("Flush data: %.8x %.8x %.8x %.8x %.8x %.8x\n", + err->flush[0], err->flush[1], err->flush[2], + err->flush[3], err->flush[4], err->flush[5]); + if (!mrq->data->error) + mrq->data->error = -EIO; + } + host->dma.busy = 0; + dma_unmap_sg(mmc_dev(host->mmc), host->dma.sg, host->dma.num_ents, + host->dma.dir); + + if (host->curr.user_pages) { + struct scatterlist *sg = host->dma.sg; + int i; + + for (i = 0; i < host->dma.num_ents; i++) + flush_dcache_page(sg_page(sg++)); + } + + host->dma.sg = NULL; + + if ((host->curr.got_dataend && host->curr.got_datablkend) + || mrq->data->error) { + + /* + * If we've already gotten our DATAEND / DATABLKEND + * for this request, then complete it through here. + */ + msmsdcc_stop_data(host); + + if (!mrq->data->error) + host->curr.data_xfered = host->curr.xfer_size; + if (!mrq->data->stop || mrq->cmd->error) { + writel(0, host->base + MMCICOMMAND); + host->curr.mrq = NULL; + host->curr.cmd = NULL; + mrq->data->bytes_xfered = host->curr.data_xfered; + + spin_unlock_irqrestore(&host->lock, flags); + mmc_request_done(host->mmc, mrq); + return; + } else + msmsdcc_start_command(host, mrq->data->stop, 0); + } + +out: + spin_unlock_irqrestore(&host->lock, flags); + return; +} + +static int validate_dma(struct msmsdcc_host *host, struct mmc_data *data) +{ + if (host->dma.channel == -1) + return -ENOENT; + + if ((data->blksz * data->blocks) < MCI_FIFOSIZE) + return -EINVAL; + if ((data->blksz * data->blocks) % MCI_FIFOSIZE) + return -EINVAL; + return 0; +} + +static int msmsdcc_config_dma(struct msmsdcc_host *host, struct mmc_data *data) +{ + struct msmsdcc_nc_dmadata *nc; + dmov_box *box; + uint32_t rows; + uint32_t crci; + unsigned int n; + int i, rc; + struct scatterlist *sg = data->sg; + + rc = validate_dma(host, data); + if (rc) + return rc; + + host->dma.sg = data->sg; + host->dma.num_ents = data->sg_len; + + nc = host->dma.nc; + + switch (host->pdev_id) { + case 1: + crci = MSMSDCC_CRCI_SDC1; + break; + case 2: + crci = MSMSDCC_CRCI_SDC2; + break; + case 3: + crci = MSMSDCC_CRCI_SDC3; + break; + case 4: + crci = MSMSDCC_CRCI_SDC4; + break; + default: + host->dma.sg = NULL; + host->dma.num_ents = 0; + return -ENOENT; + } + + if (data->flags & MMC_DATA_READ) + host->dma.dir = DMA_FROM_DEVICE; + else + host->dma.dir = DMA_TO_DEVICE; + + host->curr.user_pages = 0; + + n = dma_map_sg(mmc_dev(host->mmc), host->dma.sg, + host->dma.num_ents, host->dma.dir); + + if (n != host->dma.num_ents) { + pr_err("%s: Unable to map in all sg elements\n", + mmc_hostname(host->mmc)); + host->dma.sg = NULL; + host->dma.num_ents = 0; + return -ENOMEM; + } + + box = &nc->cmd[0]; + for (i = 0; i < host->dma.num_ents; i++) { + box->cmd = CMD_MODE_BOX; + + if (i == (host->dma.num_ents - 1)) + box->cmd |= CMD_LC; + rows = (sg_dma_len(sg) % MCI_FIFOSIZE) ? + (sg_dma_len(sg) / MCI_FIFOSIZE) + 1 : + (sg_dma_len(sg) / MCI_FIFOSIZE) ; + + if (data->flags & MMC_DATA_READ) { + box->src_row_addr = msmsdcc_fifo_addr(host); + box->dst_row_addr = sg_dma_address(sg); + + box->src_dst_len = (MCI_FIFOSIZE << 16) | + (MCI_FIFOSIZE); + box->row_offset = MCI_FIFOSIZE; + + box->num_rows = rows * ((1 << 16) + 1); + box->cmd |= CMD_SRC_CRCI(crci); + } else { + box->src_row_addr = sg_dma_address(sg); + box->dst_row_addr = msmsdcc_fifo_addr(host); + + box->src_dst_len = (MCI_FIFOSIZE << 16) | + (MCI_FIFOSIZE); + box->row_offset = (MCI_FIFOSIZE << 16); + + box->num_rows = rows * ((1 << 16) + 1); + box->cmd |= CMD_DST_CRCI(crci); + } + box++; + sg++; + } + + /* location of command block must be 64 bit aligned */ + BUG_ON(host->dma.cmd_busaddr & 0x07); + + nc->cmdptr = (host->dma.cmd_busaddr >> 3) | CMD_PTR_LP; + host->dma.hdr.cmdptr = DMOV_CMD_PTR_LIST | + DMOV_CMD_ADDR(host->dma.cmdptr_busaddr); + host->dma.hdr.complete_func = msmsdcc_dma_complete_func; + + return 0; +} + +static void +msmsdcc_start_data(struct msmsdcc_host *host, struct mmc_data *data) +{ + unsigned int datactrl, timeout; + unsigned long long clks; + void __iomem *base = host->base; + unsigned int pio_irqmask = 0; + + host->curr.data = data; + host->curr.xfer_size = data->blksz * data->blocks; + host->curr.xfer_remain = host->curr.xfer_size; + host->curr.data_xfered = 0; + host->curr.got_dataend = 0; + host->curr.got_datablkend = 0; + + memset(&host->pio, 0, sizeof(host->pio)); + + clks = (unsigned long long)data->timeout_ns * host->clk_rate; + do_div(clks, NSEC_PER_SEC); + timeout = data->timeout_clks + (unsigned int)clks; + writel(timeout, base + MMCIDATATIMER); + + writel(host->curr.xfer_size, base + MMCIDATALENGTH); + + datactrl = MCI_DPSM_ENABLE | (data->blksz << 4); + + if (!msmsdcc_config_dma(host, data)) + datactrl |= MCI_DPSM_DMAENABLE; + else { + host->pio.sg = data->sg; + host->pio.sg_len = data->sg_len; + host->pio.sg_off = 0; + + if (data->flags & MMC_DATA_READ) { + pio_irqmask = MCI_RXFIFOHALFFULLMASK; + if (host->curr.xfer_remain < MCI_FIFOSIZE) + pio_irqmask |= MCI_RXDATAAVLBLMASK; + } else + pio_irqmask = MCI_TXFIFOHALFEMPTYMASK; + } + + if (data->flags & MMC_DATA_READ) + datactrl |= MCI_DPSM_DIRECTION; + + writel(pio_irqmask, base + MMCIMASK1); + writel(datactrl, base + MMCIDATACTRL); + + if (datactrl & MCI_DPSM_DMAENABLE) { + host->dma.busy = 1; + msm_dmov_enqueue_cmd(host->dma.channel, &host->dma.hdr); + } +} + +static void +msmsdcc_start_command(struct msmsdcc_host *host, struct mmc_command *cmd, u32 c) +{ + void __iomem *base = host->base; + + if (readl(base + MMCICOMMAND) & MCI_CPSM_ENABLE) { + writel(0, base + MMCICOMMAND); + udelay(2 + ((5 * 1000000) / host->clk_rate)); + } + + c |= cmd->opcode | MCI_CPSM_ENABLE; + + if (cmd->flags & MMC_RSP_PRESENT) { + if (cmd->flags & MMC_RSP_136) + c |= MCI_CPSM_LONGRSP; + c |= MCI_CPSM_RESPONSE; + } + + if (cmd->opcode == 17 || cmd->opcode == 18 || + cmd->opcode == 24 || cmd->opcode == 25 || + cmd->opcode == 53) + c |= MCI_CSPM_DATCMD; + + if (cmd == cmd->mrq->stop) + c |= MCI_CSPM_MCIABORT; + + host->curr.cmd = cmd; + + host->stats.cmds++; + + writel(cmd->arg, base + MMCIARGUMENT); + writel(c, base + MMCICOMMAND); +} + +static void +msmsdcc_data_err(struct msmsdcc_host *host, struct mmc_data *data, + unsigned int status) +{ + if (status & MCI_DATACRCFAIL) { + pr_err("%s: Data CRC error\n", mmc_hostname(host->mmc)); + pr_err("%s: opcode 0x%.8x\n", __func__, + data->mrq->cmd->opcode); + pr_err("%s: blksz %d, blocks %d\n", __func__, + data->blksz, data->blocks); + data->error = -EILSEQ; + } else if (status & MCI_DATATIMEOUT) { + pr_err("%s: Data timeout\n", mmc_hostname(host->mmc)); + data->error = -ETIMEDOUT; + } else if (status & MCI_RXOVERRUN) { + pr_err("%s: RX overrun\n", mmc_hostname(host->mmc)); + data->error = -EIO; + } else if (status & MCI_TXUNDERRUN) { + pr_err("%s: TX underrun\n", mmc_hostname(host->mmc)); + data->error = -EIO; + } else { + pr_err("%s: Unknown error (0x%.8x)\n", + mmc_hostname(host->mmc), status); + data->error = -EIO; + } +} + + +static int +msmsdcc_pio_read(struct msmsdcc_host *host, char *buffer, unsigned int remain) +{ + void __iomem *base = host->base; + uint32_t *ptr = (uint32_t *) buffer; + int count = 0; + + while (readl(base + MMCISTATUS) & MCI_RXDATAAVLBL) { + + *ptr = readl(base + MMCIFIFO + (count % MCI_FIFOSIZE)); + ptr++; + count += sizeof(uint32_t); + + remain -= sizeof(uint32_t); + if (remain == 0) + break; + } + return count; +} + +static int +msmsdcc_pio_write(struct msmsdcc_host *host, char *buffer, + unsigned int remain, u32 status) +{ + void __iomem *base = host->base; + char *ptr = buffer; + + do { + unsigned int count, maxcnt; + + maxcnt = status & MCI_TXFIFOEMPTY ? MCI_FIFOSIZE : + MCI_FIFOHALFSIZE; + count = min(remain, maxcnt); + + writesl(base + MMCIFIFO, ptr, count >> 2); + ptr += count; + remain -= count; + + if (remain == 0) + break; + + status = readl(base + MMCISTATUS); + } while (status & MCI_TXFIFOHALFEMPTY); + + return ptr - buffer; +} + +static int +msmsdcc_spin_on_status(struct msmsdcc_host *host, uint32_t mask, int maxspin) +{ + while (maxspin) { + if ((readl(host->base + MMCISTATUS) & mask)) + return 0; + udelay(1); + --maxspin; + } + return -ETIMEDOUT; +} + +static int +msmsdcc_pio_irq(int irq, void *dev_id) +{ + struct msmsdcc_host *host = dev_id; + void __iomem *base = host->base; + uint32_t status; + + status = readl(base + MMCISTATUS); + + do { + unsigned long flags; + unsigned int remain, len; + char *buffer; + + if (!(status & (MCI_TXFIFOHALFEMPTY | MCI_RXDATAAVLBL))) { + if (host->curr.xfer_remain == 0 || !msmsdcc_piopoll) + break; + + if (msmsdcc_spin_on_status(host, + (MCI_TXFIFOHALFEMPTY | + MCI_RXDATAAVLBL), + PIO_SPINMAX)) { + break; + } + } + + /* Map the current scatter buffer */ + local_irq_save(flags); + buffer = kmap_atomic(sg_page(host->pio.sg), + KM_BIO_SRC_IRQ) + host->pio.sg->offset; + buffer += host->pio.sg_off; + remain = host->pio.sg->length - host->pio.sg_off; + len = 0; + if (status & MCI_RXACTIVE) + len = msmsdcc_pio_read(host, buffer, remain); + if (status & MCI_TXACTIVE) + len = msmsdcc_pio_write(host, buffer, remain, status); + + /* Unmap the buffer */ + kunmap_atomic(buffer, KM_BIO_SRC_IRQ); + local_irq_restore(flags); + + host->pio.sg_off += len; + host->curr.xfer_remain -= len; + host->curr.data_xfered += len; + remain -= len; + + if (remain == 0) { + /* This sg page is full - do some housekeeping */ + if (status & MCI_RXACTIVE && host->curr.user_pages) + flush_dcache_page(sg_page(host->pio.sg)); + + if (!--host->pio.sg_len) { + memset(&host->pio, 0, sizeof(host->pio)); + break; + } + + /* Advance to next sg */ + host->pio.sg++; + host->pio.sg_off = 0; + } + + status = readl(base + MMCISTATUS); + } while (1); + + if (status & MCI_RXACTIVE && host->curr.xfer_remain < MCI_FIFOSIZE) + writel(MCI_RXDATAAVLBLMASK, base + MMCIMASK1); + + if (!host->curr.xfer_remain) + writel(0, base + MMCIMASK1); + + return IRQ_HANDLED; +} + +static void msmsdcc_do_cmdirq(struct msmsdcc_host *host, uint32_t status) +{ + struct mmc_command *cmd = host->curr.cmd; + void __iomem *base = host->base; + + host->curr.cmd = NULL; + cmd->resp[0] = readl(base + MMCIRESPONSE0); + cmd->resp[1] = readl(base + MMCIRESPONSE1); + cmd->resp[2] = readl(base + MMCIRESPONSE2); + cmd->resp[3] = readl(base + MMCIRESPONSE3); + + del_timer(&host->command_timer); + if (status & MCI_CMDTIMEOUT) { + cmd->error = -ETIMEDOUT; + } else if (status & MCI_CMDCRCFAIL && + cmd->flags & MMC_RSP_CRC) { + pr_err("%s: Command CRC error\n", mmc_hostname(host->mmc)); + cmd->error = -EILSEQ; + } + + if (!cmd->data || cmd->error) { + if (host->curr.data && host->dma.sg) + msm_dmov_stop_cmd(host->dma.channel, + &host->dma.hdr, 0); + else if (host->curr.data) { /* Non DMA */ + msmsdcc_stop_data(host); + msmsdcc_request_end(host, cmd->mrq); + } else /* host->data == NULL */ + msmsdcc_request_end(host, cmd->mrq); + } else if (!(cmd->data->flags & MMC_DATA_READ)) + msmsdcc_start_data(host, cmd->data); +} + +static void +msmsdcc_handle_irq_data(struct msmsdcc_host *host, u32 status, + void __iomem *base) +{ + struct mmc_data *data = host->curr.data; + + if (!data) + return; + + /* Check for data errors */ + if (status & (MCI_DATACRCFAIL | MCI_DATATIMEOUT | + MCI_TXUNDERRUN | MCI_RXOVERRUN)) { + msmsdcc_data_err(host, data, status); + host->curr.data_xfered = 0; + if (host->dma.sg) + msm_dmov_stop_cmd(host->dma.channel, + &host->dma.hdr, 0); + else { + msmsdcc_stop_data(host); + if (!data->stop) + msmsdcc_request_end(host, data->mrq); + else + msmsdcc_start_command(host, data->stop, 0); + } + } + + /* Check for data done */ + if (!host->curr.got_dataend && (status & MCI_DATAEND)) + host->curr.got_dataend = 1; + + if (!host->curr.got_datablkend && (status & MCI_DATABLOCKEND)) + host->curr.got_datablkend = 1; + + /* + * If DMA is still in progress, we complete via the completion handler + */ + if (host->curr.got_dataend && host->curr.got_datablkend && + !host->dma.busy) { + /* + * There appears to be an issue in the controller where + * if you request a small block transfer (< fifo size), + * you may get your DATAEND/DATABLKEND irq without the + * PIO data irq. + * + * Check to see if there is still data to be read, + * and simulate a PIO irq. + */ + if (readl(base + MMCISTATUS) & MCI_RXDATAAVLBL) + msmsdcc_pio_irq(1, host); + + msmsdcc_stop_data(host); + if (!data->error) + host->curr.data_xfered = host->curr.xfer_size; + + if (!data->stop) + msmsdcc_request_end(host, data->mrq); + else + msmsdcc_start_command(host, data->stop, 0); + } +} + +static irqreturn_t +msmsdcc_irq(int irq, void *dev_id) +{ + struct msmsdcc_host *host = dev_id; + void __iomem *base = host->base; + u32 status; + int ret = 0; + int cardint = 0; + + spin_lock(&host->lock); + + do { + status = readl(base + MMCISTATUS); + + status &= (readl(base + MMCIMASK0) | MCI_DATABLOCKENDMASK); + writel(status, base + MMCICLEAR); + + msmsdcc_handle_irq_data(host, status, base); + + if (status & (MCI_CMDSENT | MCI_CMDRESPEND | MCI_CMDCRCFAIL | + MCI_CMDTIMEOUT) && host->curr.cmd) { + msmsdcc_do_cmdirq(host, status); + } + + if (status & MCI_SDIOINTOPER) { + cardint = 1; + status &= ~MCI_SDIOINTOPER; + } + ret = 1; + } while (status); + + spin_unlock(&host->lock); + + /* + * We have to delay handling the card interrupt as it calls + * back into the driver. + */ + if (cardint) + mmc_signal_sdio_irq(host->mmc); + + return IRQ_RETVAL(ret); +} + +static void +msmsdcc_request(struct mmc_host *mmc, struct mmc_request *mrq) +{ + struct msmsdcc_host *host = mmc_priv(mmc); + unsigned long flags; + + WARN_ON(host->curr.mrq != NULL); + WARN_ON(host->pwr == 0); + + spin_lock_irqsave(&host->lock, flags); + + host->stats.reqs++; + + if (host->eject) { + if (mrq->data && !(mrq->data->flags & MMC_DATA_READ)) { + mrq->cmd->error = 0; + mrq->data->bytes_xfered = mrq->data->blksz * + mrq->data->blocks; + } else + mrq->cmd->error = -ENOMEDIUM; + + spin_unlock_irqrestore(&host->lock, flags); + mmc_request_done(mmc, mrq); + return; + } + + host->curr.mrq = mrq; + + if (mrq->data && mrq->data->flags & MMC_DATA_READ) + msmsdcc_start_data(host, mrq->data); + + msmsdcc_start_command(host, mrq->cmd, 0); + + if (host->cmdpoll && !msmsdcc_spin_on_status(host, + MCI_CMDRESPEND|MCI_CMDCRCFAIL|MCI_CMDTIMEOUT, + CMD_SPINMAX)) { + uint32_t status = readl(host->base + MMCISTATUS); + msmsdcc_do_cmdirq(host, status); + writel(MCI_CMDRESPEND | MCI_CMDCRCFAIL | MCI_CMDTIMEOUT, + host->base + MMCICLEAR); + host->stats.cmdpoll_hits++; + } else { + host->stats.cmdpoll_misses++; + mod_timer(&host->command_timer, jiffies + HZ); + } + spin_unlock_irqrestore(&host->lock, flags); +} + +static void +msmsdcc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) +{ + struct msmsdcc_host *host = mmc_priv(mmc); + u32 clk = 0, pwr = 0; + int rc; + + if (ios->clock) { + + if (!host->clks_on) { + clk_enable(host->pclk); + clk_enable(host->clk); + host->clks_on = 1; + } + if (ios->clock != host->clk_rate) { + rc = clk_set_rate(host->clk, ios->clock); + if (rc < 0) + pr_err("%s: Error setting clock rate (%d)\n", + mmc_hostname(host->mmc), rc); + else + host->clk_rate = ios->clock; + } + clk |= MCI_CLK_ENABLE; + } + + if (ios->bus_width == MMC_BUS_WIDTH_4) + clk |= (2 << 10); /* Set WIDEBUS */ + + if (ios->clock > 400000 && msmsdcc_pwrsave) + clk |= (1 << 9); /* PWRSAVE */ + + clk |= (1 << 12); /* FLOW_ENA */ + clk |= (1 << 15); /* feedback clock */ + + if (host->plat->translate_vdd) + pwr |= host->plat->translate_vdd(mmc_dev(mmc), ios->vdd); + + switch (ios->power_mode) { + case MMC_POWER_OFF: + htc_pwrsink_set(PWRSINK_SDCARD, 0); + break; + case MMC_POWER_UP: + pwr |= MCI_PWR_UP; + break; + case MMC_POWER_ON: + htc_pwrsink_set(PWRSINK_SDCARD, 100); + pwr |= MCI_PWR_ON; + break; + } + + if (ios->bus_mode == MMC_BUSMODE_OPENDRAIN) + pwr |= MCI_OD; + + writel(clk, host->base + MMCICLOCK); + + if (host->pwr != pwr) { + host->pwr = pwr; + writel(pwr, host->base + MMCIPOWER); + } + + if (!(clk & MCI_CLK_ENABLE) && host->clks_on) { + clk_disable(host->clk); + clk_disable(host->pclk); + host->clks_on = 0; + } +} + +static void msmsdcc_enable_sdio_irq(struct mmc_host *mmc, int enable) +{ + struct msmsdcc_host *host = mmc_priv(mmc); + unsigned long flags; + u32 status; + + spin_lock_irqsave(&host->lock, flags); + if (msmsdcc_sdioirq == 1) { + status = readl(host->base + MMCIMASK0); + if (enable) + status |= MCI_SDIOINTOPERMASK; + else + status &= ~MCI_SDIOINTOPERMASK; + host->saved_irq0mask = status; + writel(status, host->base + MMCIMASK0); + } + spin_unlock_irqrestore(&host->lock, flags); +} + +static const struct mmc_host_ops msmsdcc_ops = { + .request = msmsdcc_request, + .set_ios = msmsdcc_set_ios, + .enable_sdio_irq = msmsdcc_enable_sdio_irq, +}; + +static void +msmsdcc_check_status(unsigned long data) +{ + struct msmsdcc_host *host = (struct msmsdcc_host *)data; + unsigned int status; + + if (!host->plat->status) { + mmc_detect_change(host->mmc, 0); + goto out; + } + + status = host->plat->status(mmc_dev(host->mmc)); + host->eject = !status; + if (status ^ host->oldstat) { + pr_info("%s: Slot status change detected (%d -> %d)\n", + mmc_hostname(host->mmc), host->oldstat, status); + if (status) + mmc_detect_change(host->mmc, (5 * HZ) / 2); + else + mmc_detect_change(host->mmc, 0); + } + + host->oldstat = status; + +out: + if (host->timer.function) + mod_timer(&host->timer, jiffies + HZ); +} + +static irqreturn_t +msmsdcc_platform_status_irq(int irq, void *dev_id) +{ + struct msmsdcc_host *host = dev_id; + + printk(KERN_DEBUG "%s: %d\n", __func__, irq); + msmsdcc_check_status((unsigned long) host); + return IRQ_HANDLED; +} + +static void +msmsdcc_status_notify_cb(int card_present, void *dev_id) +{ + struct msmsdcc_host *host = dev_id; + + printk(KERN_DEBUG "%s: card_present %d\n", mmc_hostname(host->mmc), + card_present); + msmsdcc_check_status((unsigned long) host); +} + +/* + * called when a command expires. + * Dump some debugging, and then error + * out the transaction. + */ +static void +msmsdcc_command_expired(unsigned long _data) +{ + struct msmsdcc_host *host = (struct msmsdcc_host *) _data; + struct mmc_request *mrq; + unsigned long flags; + + spin_lock_irqsave(&host->lock, flags); + mrq = host->curr.mrq; + + if (!mrq) { + pr_info("%s: Command expiry misfire\n", + mmc_hostname(host->mmc)); + spin_unlock_irqrestore(&host->lock, flags); + return; + } + + pr_err("%s: Command timeout (%p %p %p %p)\n", + mmc_hostname(host->mmc), mrq, mrq->cmd, + mrq->data, host->dma.sg); + + mrq->cmd->error = -ETIMEDOUT; + msmsdcc_stop_data(host); + + writel(0, host->base + MMCICOMMAND); + + host->curr.mrq = NULL; + host->curr.cmd = NULL; + + spin_unlock_irqrestore(&host->lock, flags); + mmc_request_done(host->mmc, mrq); +} + +static int +msmsdcc_init_dma(struct msmsdcc_host *host) +{ + memset(&host->dma, 0, sizeof(struct msmsdcc_dma_data)); + host->dma.host = host; + host->dma.channel = -1; + + if (!host->dmares) + return -ENODEV; + + host->dma.nc = dma_alloc_coherent(NULL, + sizeof(struct msmsdcc_nc_dmadata), + &host->dma.nc_busaddr, + GFP_KERNEL); + if (host->dma.nc == NULL) { + pr_err("Unable to allocate DMA buffer\n"); + return -ENOMEM; + } + memset(host->dma.nc, 0x00, sizeof(struct msmsdcc_nc_dmadata)); + host->dma.cmd_busaddr = host->dma.nc_busaddr; + host->dma.cmdptr_busaddr = host->dma.nc_busaddr + + offsetof(struct msmsdcc_nc_dmadata, cmdptr); + host->dma.channel = host->dmares->start; + + return 0; +} + +#ifdef CONFIG_MMC_MSM7X00A_RESUME_IN_WQ +static void +do_resume_work(struct work_struct *work) +{ + struct msmsdcc_host *host = + container_of(work, struct msmsdcc_host, resume_task); + struct mmc_host *mmc = host->mmc; + + if (mmc) { + mmc_resume_host(mmc); + if (host->stat_irq) + enable_irq(host->stat_irq); + } +} +#endif + +static int +msmsdcc_probe(struct platform_device *pdev) +{ + struct mmc_platform_data *plat = pdev->dev.platform_data; + struct msmsdcc_host *host; + struct mmc_host *mmc; + struct resource *cmd_irqres = NULL; + struct resource *pio_irqres = NULL; + struct resource *stat_irqres = NULL; + struct resource *memres = NULL; + struct resource *dmares = NULL; + int ret; + + /* must have platform data */ + if (!plat) { + pr_err("%s: Platform data not available\n", __func__); + ret = -EINVAL; + goto out; + } + + if (pdev->id < 1 || pdev->id > 4) + return -EINVAL; + + if (pdev->resource == NULL || pdev->num_resources < 2) { + pr_err("%s: Invalid resource\n", __func__); + return -ENXIO; + } + + memres = platform_get_resource(pdev, IORESOURCE_MEM, 0); + dmares = platform_get_resource(pdev, IORESOURCE_DMA, 0); + cmd_irqres = platform_get_resource_byname(pdev, IORESOURCE_IRQ, + "cmd_irq"); + pio_irqres = platform_get_resource_byname(pdev, IORESOURCE_IRQ, + "pio_irq"); + stat_irqres = platform_get_resource_byname(pdev, IORESOURCE_IRQ, + "status_irq"); + + if (!cmd_irqres || !pio_irqres || !memres) { + pr_err("%s: Invalid resource\n", __func__); + return -ENXIO; + } + + /* + * Setup our host structure + */ + + mmc = mmc_alloc_host(sizeof(struct msmsdcc_host), &pdev->dev); + if (!mmc) { + ret = -ENOMEM; + goto out; + } + + host = mmc_priv(mmc); + host->pdev_id = pdev->id; + host->plat = plat; + host->mmc = mmc; + + host->cmdpoll = 1; + + host->base = ioremap(memres->start, PAGE_SIZE); + if (!host->base) { + ret = -ENOMEM; + goto out; + } + + host->cmd_irqres = cmd_irqres; + host->pio_irqres = pio_irqres; + host->memres = memres; + host->dmares = dmares; + spin_lock_init(&host->lock); + + /* + * Setup DMA + */ + msmsdcc_init_dma(host); + + /* + * Setup main peripheral bus clock + */ + host->pclk = clk_get(&pdev->dev, "sdc_pclk"); + if (IS_ERR(host->pclk)) { + ret = PTR_ERR(host->pclk); + goto host_free; + } + + ret = clk_enable(host->pclk); + if (ret) + goto pclk_put; + + host->pclk_rate = clk_get_rate(host->pclk); + + /* + * Setup SDC MMC clock + */ + host->clk = clk_get(&pdev->dev, "sdc_clk"); + if (IS_ERR(host->clk)) { + ret = PTR_ERR(host->clk); + goto pclk_disable; + } + + ret = clk_enable(host->clk); + if (ret) + goto clk_put; + + ret = clk_set_rate(host->clk, msmsdcc_fmin); + if (ret) { + pr_err("%s: Clock rate set failed (%d)\n", __func__, ret); + goto clk_disable; + } + + host->clk_rate = clk_get_rate(host->clk); + + host->clks_on = 1; + + /* + * Setup MMC host structure + */ + mmc->ops = &msmsdcc_ops; + mmc->f_min = msmsdcc_fmin; + mmc->f_max = msmsdcc_fmax; + mmc->ocr_avail = plat->ocr_mask; + + if (msmsdcc_4bit) + mmc->caps |= MMC_CAP_4_BIT_DATA; + if (msmsdcc_sdioirq) + mmc->caps |= MMC_CAP_SDIO_IRQ; + mmc->caps |= MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED; + + mmc->max_phys_segs = NR_SG; + mmc->max_hw_segs = NR_SG; + mmc->max_blk_size = 4096; /* MCI_DATA_CTL BLOCKSIZE up to 4096 */ + mmc->max_blk_count = 65536; + + mmc->max_req_size = 33554432; /* MCI_DATA_LENGTH is 25 bits */ + mmc->max_seg_size = mmc->max_req_size; + + writel(0, host->base + MMCIMASK0); + writel(0x5e007ff, host->base + MMCICLEAR); /* Add: 1 << 25 */ + + writel(MCI_IRQENABLE, host->base + MMCIMASK0); + host->saved_irq0mask = MCI_IRQENABLE; + + /* + * Setup card detect change + */ + + memset(&host->timer, 0, sizeof(host->timer)); + + if (stat_irqres && !(stat_irqres->flags & IORESOURCE_DISABLED)) { + unsigned long irqflags = IRQF_SHARED | + (stat_irqres->flags & IRQF_TRIGGER_MASK); + + host->stat_irq = stat_irqres->start; + ret = request_irq(host->stat_irq, + msmsdcc_platform_status_irq, + irqflags, + DRIVER_NAME " (slot)", + host); + if (ret) { + pr_err("%s: Unable to get slot IRQ %d (%d)\n", + mmc_hostname(mmc), host->stat_irq, ret); + goto clk_disable; + } + } else if (plat->register_status_notify) { + plat->register_status_notify(msmsdcc_status_notify_cb, host); + } else if (!plat->status) + pr_err("%s: No card detect facilities available\n", + mmc_hostname(mmc)); + else { + init_timer(&host->timer); + host->timer.data = (unsigned long)host; + host->timer.function = msmsdcc_check_status; + host->timer.expires = jiffies + HZ; + add_timer(&host->timer); + } + + if (plat->status) { + host->oldstat = host->plat->status(mmc_dev(host->mmc)); + host->eject = !host->oldstat; + } + + /* + * Setup a command timer. We currently need this due to + * some 'strange' timeout / error handling situations. + */ + init_timer(&host->command_timer); + host->command_timer.data = (unsigned long) host; + host->command_timer.function = msmsdcc_command_expired; + + ret = request_irq(cmd_irqres->start, msmsdcc_irq, IRQF_SHARED, + DRIVER_NAME " (cmd)", host); + if (ret) + goto stat_irq_free; + + ret = request_irq(pio_irqres->start, msmsdcc_pio_irq, IRQF_SHARED, + DRIVER_NAME " (pio)", host); + if (ret) + goto cmd_irq_free; + + mmc_set_drvdata(pdev, mmc); + mmc_add_host(mmc); + + pr_info("%s: Qualcomm MSM SDCC at 0x%016llx irq %d,%d dma %d\n", + mmc_hostname(mmc), (unsigned long long)memres->start, + (unsigned int) cmd_irqres->start, + (unsigned int) host->stat_irq, host->dma.channel); + pr_info("%s: 4 bit data mode %s\n", mmc_hostname(mmc), + (mmc->caps & MMC_CAP_4_BIT_DATA ? "enabled" : "disabled")); + pr_info("%s: MMC clock %u -> %u Hz, PCLK %u Hz\n", + mmc_hostname(mmc), msmsdcc_fmin, msmsdcc_fmax, host->pclk_rate); + pr_info("%s: Slot eject status = %d\n", mmc_hostname(mmc), host->eject); + pr_info("%s: Power save feature enable = %d\n", + mmc_hostname(mmc), msmsdcc_pwrsave); + + if (host->dma.channel != -1) { + pr_info("%s: DM non-cached buffer at %p, dma_addr 0x%.8x\n", + mmc_hostname(mmc), host->dma.nc, host->dma.nc_busaddr); + pr_info("%s: DM cmd busaddr 0x%.8x, cmdptr busaddr 0x%.8x\n", + mmc_hostname(mmc), host->dma.cmd_busaddr, + host->dma.cmdptr_busaddr); + } else + pr_info("%s: PIO transfer enabled\n", mmc_hostname(mmc)); + if (host->timer.function) + pr_info("%s: Polling status mode enabled\n", mmc_hostname(mmc)); + + return 0; + cmd_irq_free: + free_irq(cmd_irqres->start, host); + stat_irq_free: + if (host->stat_irq) + free_irq(host->stat_irq, host); + clk_disable: + clk_disable(host->clk); + clk_put: + clk_put(host->clk); + pclk_disable: + clk_disable(host->pclk); + pclk_put: + clk_put(host->pclk); + host_free: + mmc_free_host(mmc); + out: + return ret; +} + +static int +msmsdcc_suspend(struct platform_device *dev, pm_message_t state) +{ + struct mmc_host *mmc = mmc_get_drvdata(dev); + int rc = 0; + + if (mmc) { + struct msmsdcc_host *host = mmc_priv(mmc); + + if (host->stat_irq) + disable_irq(host->stat_irq); + + if (mmc->card && mmc->card->type != MMC_TYPE_SDIO) + rc = mmc_suspend_host(mmc, state); + if (!rc) { + writel(0, host->base + MMCIMASK0); + + if (host->clks_on) { + clk_disable(host->clk); + clk_disable(host->pclk); + host->clks_on = 0; + } + } + } + return rc; +} + +static int +msmsdcc_resume(struct platform_device *dev) +{ + struct mmc_host *mmc = mmc_get_drvdata(dev); + unsigned long flags; + + if (mmc) { + struct msmsdcc_host *host = mmc_priv(mmc); + + spin_lock_irqsave(&host->lock, flags); + + if (!host->clks_on) { + clk_enable(host->pclk); + clk_enable(host->clk); + host->clks_on = 1; + } + + writel(host->saved_irq0mask, host->base + MMCIMASK0); + + spin_unlock_irqrestore(&host->lock, flags); + + if (mmc->card && mmc->card->type != MMC_TYPE_SDIO) + mmc_resume_host(mmc); + if (host->stat_irq) + enable_irq(host->stat_irq); + else if (host->stat_irq) + enable_irq(host->stat_irq); + } + return 0; +} + +static struct platform_driver msmsdcc_driver = { + .probe = msmsdcc_probe, + .suspend = msmsdcc_suspend, + .resume = msmsdcc_resume, + .driver = { + .name = "msm_sdcc", + }, +}; + +static int __init msmsdcc_init(void) +{ + return platform_driver_register(&msmsdcc_driver); +} + +static void __exit msmsdcc_exit(void) +{ + platform_driver_unregister(&msmsdcc_driver); +} + +module_init(msmsdcc_init); +module_exit(msmsdcc_exit); + +MODULE_DESCRIPTION("Qualcomm MSM 7X00A Multimedia Card Interface driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/mmc/host/msm_sdcc.h b/drivers/mmc/host/msm_sdcc.h new file mode 100644 index 000000000000..8c8448469811 --- /dev/null +++ b/drivers/mmc/host/msm_sdcc.h @@ -0,0 +1,238 @@ +/* + * linux/drivers/mmc/host/msmsdcc.h - QCT MSM7K SDC Controller + * + * Copyright (C) 2008 Google, All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * - Based on mmci.h + */ + +#ifndef _MSM_SDCC_H +#define _MSM_SDCC_H + +#define MSMSDCC_CRCI_SDC1 6 +#define MSMSDCC_CRCI_SDC2 7 +#define MSMSDCC_CRCI_SDC3 12 +#define MSMSDCC_CRCI_SDC4 13 + +#define MMCIPOWER 0x000 +#define MCI_PWR_OFF 0x00 +#define MCI_PWR_UP 0x02 +#define MCI_PWR_ON 0x03 +#define MCI_OD (1 << 6) + +#define MMCICLOCK 0x004 +#define MCI_CLK_ENABLE (1 << 8) +#define MCI_CLK_PWRSAVE (1 << 9) +#define MCI_CLK_WIDEBUS (1 << 10) +#define MCI_CLK_FLOWENA (1 << 12) +#define MCI_CLK_INVERTOUT (1 << 13) +#define MCI_CLK_SELECTIN (1 << 14) + +#define MMCIARGUMENT 0x008 +#define MMCICOMMAND 0x00c +#define MCI_CPSM_RESPONSE (1 << 6) +#define MCI_CPSM_LONGRSP (1 << 7) +#define MCI_CPSM_INTERRUPT (1 << 8) +#define MCI_CPSM_PENDING (1 << 9) +#define MCI_CPSM_ENABLE (1 << 10) +#define MCI_CPSM_PROGENA (1 << 11) +#define MCI_CSPM_DATCMD (1 << 12) +#define MCI_CSPM_MCIABORT (1 << 13) +#define MCI_CSPM_CCSENABLE (1 << 14) +#define MCI_CSPM_CCSDISABLE (1 << 15) + + +#define MMCIRESPCMD 0x010 +#define MMCIRESPONSE0 0x014 +#define MMCIRESPONSE1 0x018 +#define MMCIRESPONSE2 0x01c +#define MMCIRESPONSE3 0x020 +#define MMCIDATATIMER 0x024 +#define MMCIDATALENGTH 0x028 + +#define MMCIDATACTRL 0x02c +#define MCI_DPSM_ENABLE (1 << 0) +#define MCI_DPSM_DIRECTION (1 << 1) +#define MCI_DPSM_MODE (1 << 2) +#define MCI_DPSM_DMAENABLE (1 << 3) + +#define MMCIDATACNT 0x030 +#define MMCISTATUS 0x034 +#define MCI_CMDCRCFAIL (1 << 0) +#define MCI_DATACRCFAIL (1 << 1) +#define MCI_CMDTIMEOUT (1 << 2) +#define MCI_DATATIMEOUT (1 << 3) +#define MCI_TXUNDERRUN (1 << 4) +#define MCI_RXOVERRUN (1 << 5) +#define MCI_CMDRESPEND (1 << 6) +#define MCI_CMDSENT (1 << 7) +#define MCI_DATAEND (1 << 8) +#define MCI_DATABLOCKEND (1 << 10) +#define MCI_CMDACTIVE (1 << 11) +#define MCI_TXACTIVE (1 << 12) +#define MCI_RXACTIVE (1 << 13) +#define MCI_TXFIFOHALFEMPTY (1 << 14) +#define MCI_RXFIFOHALFFULL (1 << 15) +#define MCI_TXFIFOFULL (1 << 16) +#define MCI_RXFIFOFULL (1 << 17) +#define MCI_TXFIFOEMPTY (1 << 18) +#define MCI_RXFIFOEMPTY (1 << 19) +#define MCI_TXDATAAVLBL (1 << 20) +#define MCI_RXDATAAVLBL (1 << 21) +#define MCI_SDIOINTR (1 << 22) +#define MCI_PROGDONE (1 << 23) +#define MCI_ATACMDCOMPL (1 << 24) +#define MCI_SDIOINTOPER (1 << 25) +#define MCI_CCSTIMEOUT (1 << 26) + +#define MMCICLEAR 0x038 +#define MCI_CMDCRCFAILCLR (1 << 0) +#define MCI_DATACRCFAILCLR (1 << 1) +#define MCI_CMDTIMEOUTCLR (1 << 2) +#define MCI_DATATIMEOUTCLR (1 << 3) +#define MCI_TXUNDERRUNCLR (1 << 4) +#define MCI_RXOVERRUNCLR (1 << 5) +#define MCI_CMDRESPENDCLR (1 << 6) +#define MCI_CMDSENTCLR (1 << 7) +#define MCI_DATAENDCLR (1 << 8) +#define MCI_DATABLOCKENDCLR (1 << 10) + +#define MMCIMASK0 0x03c +#define MCI_CMDCRCFAILMASK (1 << 0) +#define MCI_DATACRCFAILMASK (1 << 1) +#define MCI_CMDTIMEOUTMASK (1 << 2) +#define MCI_DATATIMEOUTMASK (1 << 3) +#define MCI_TXUNDERRUNMASK (1 << 4) +#define MCI_RXOVERRUNMASK (1 << 5) +#define MCI_CMDRESPENDMASK (1 << 6) +#define MCI_CMDSENTMASK (1 << 7) +#define MCI_DATAENDMASK (1 << 8) +#define MCI_DATABLOCKENDMASK (1 << 10) +#define MCI_CMDACTIVEMASK (1 << 11) +#define MCI_TXACTIVEMASK (1 << 12) +#define MCI_RXACTIVEMASK (1 << 13) +#define MCI_TXFIFOHALFEMPTYMASK (1 << 14) +#define MCI_RXFIFOHALFFULLMASK (1 << 15) +#define MCI_TXFIFOFULLMASK (1 << 16) +#define MCI_RXFIFOFULLMASK (1 << 17) +#define MCI_TXFIFOEMPTYMASK (1 << 18) +#define MCI_RXFIFOEMPTYMASK (1 << 19) +#define MCI_TXDATAAVLBLMASK (1 << 20) +#define MCI_RXDATAAVLBLMASK (1 << 21) +#define MCI_SDIOINTMASK (1 << 22) +#define MCI_PROGDONEMASK (1 << 23) +#define MCI_ATACMDCOMPLMASK (1 << 24) +#define MCI_SDIOINTOPERMASK (1 << 25) +#define MCI_CCSTIMEOUTMASK (1 << 26) + +#define MMCIMASK1 0x040 +#define MMCIFIFOCNT 0x044 +#define MCICCSTIMER 0x058 + +#define MMCIFIFO 0x080 /* to 0x0bc */ + +#define MCI_IRQENABLE \ + (MCI_CMDCRCFAILMASK|MCI_DATACRCFAILMASK|MCI_CMDTIMEOUTMASK| \ + MCI_DATATIMEOUTMASK|MCI_TXUNDERRUNMASK|MCI_RXOVERRUNMASK| \ + MCI_CMDRESPENDMASK|MCI_CMDSENTMASK|MCI_DATAENDMASK) + +/* + * The size of the FIFO in bytes. + */ +#define MCI_FIFOSIZE (16*4) + +#define MCI_FIFOHALFSIZE (MCI_FIFOSIZE / 2) + +#define NR_SG 32 + +struct clk; + +struct msmsdcc_nc_dmadata { + dmov_box cmd[NR_SG]; + uint32_t cmdptr; +}; + +struct msmsdcc_dma_data { + struct msmsdcc_nc_dmadata *nc; + dma_addr_t nc_busaddr; + dma_addr_t cmd_busaddr; + dma_addr_t cmdptr_busaddr; + + struct msm_dmov_cmd hdr; + enum dma_data_direction dir; + + struct scatterlist *sg; + int num_ents; + + int channel; + struct msmsdcc_host *host; + int busy; /* Set if DM is busy */ +}; + +struct msmsdcc_pio_data { + struct scatterlist *sg; + unsigned int sg_len; + unsigned int sg_off; +}; + +struct msmsdcc_curr_req { + struct mmc_request *mrq; + struct mmc_command *cmd; + struct mmc_data *data; + unsigned int xfer_size; /* Total data size */ + unsigned int xfer_remain; /* Bytes remaining to send */ + unsigned int data_xfered; /* Bytes acked by BLKEND irq */ + int got_dataend; + int got_datablkend; + int user_pages; +}; + +struct msmsdcc_stats { + unsigned int reqs; + unsigned int cmds; + unsigned int cmdpoll_hits; + unsigned int cmdpoll_misses; +}; + +struct msmsdcc_host { + struct resource *cmd_irqres; + struct resource *pio_irqres; + struct resource *memres; + struct resource *dmares; + void __iomem *base; + int pdev_id; + unsigned int stat_irq; + + struct msmsdcc_curr_req curr; + + struct mmc_host *mmc; + struct clk *clk; /* main MMC bus clock */ + struct clk *pclk; /* SDCC peripheral bus clock */ + unsigned int clks_on; /* set if clocks are enabled */ + struct timer_list command_timer; + + unsigned int eject; /* eject state */ + + spinlock_t lock; + + unsigned int clk_rate; /* Current clock rate */ + unsigned int pclk_rate; + + u32 pwr; + u32 saved_irq0mask; /* MMCIMASK0 reg value */ + struct mmc_platform_data *plat; + + struct timer_list timer; + unsigned int oldstat; + + struct msmsdcc_dma_data dma; + struct msmsdcc_pio_data pio; + int cmdpoll; + struct msmsdcc_stats stats; +}; + +#endif diff --git a/drivers/mmc/host/omap.c b/drivers/mmc/host/omap.c index e7a331de5733..b8fd7af1ceeb 100644 --- a/drivers/mmc/host/omap.c +++ b/drivers/mmc/host/omap.c @@ -1529,6 +1529,7 @@ static int mmc_omap_remove(struct platform_device *pdev) host->pdata->cleanup(&pdev->dev); mmc_omap_fclk_enable(host, 0); + free_irq(host->irq, host); clk_put(host->fclk); clk_disable(host->iclk); clk_put(host->iclk); diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c index 1cf9cfb3b64f..0aecaaebef3d 100644 --- a/drivers/mmc/host/omap_hsmmc.c +++ b/drivers/mmc/host/omap_hsmmc.c @@ -17,6 +17,8 @@ #include <linux/module.h> #include <linux/init.h> +#include <linux/debugfs.h> +#include <linux/seq_file.h> #include <linux/interrupt.h> #include <linux/delay.h> #include <linux/dma-mapping.h> @@ -25,6 +27,7 @@ #include <linux/timer.h> #include <linux/clk.h> #include <linux/mmc/host.h> +#include <linux/mmc/core.h> #include <linux/io.h> #include <linux/semaphore.h> #include <mach/dma.h> @@ -35,6 +38,7 @@ /* OMAP HSMMC Host Controller Registers */ #define OMAP_HSMMC_SYSCONFIG 0x0010 +#define OMAP_HSMMC_SYSSTATUS 0x0014 #define OMAP_HSMMC_CON 0x002C #define OMAP_HSMMC_BLK 0x0104 #define OMAP_HSMMC_ARG 0x0108 @@ -70,6 +74,8 @@ #define DTO_MASK 0x000F0000 #define DTO_SHIFT 16 #define INT_EN_MASK 0x307F0033 +#define BWR_ENABLE (1 << 4) +#define BRR_ENABLE (1 << 5) #define INIT_STREAM (1 << 1) #define DP_SELECT (1 << 21) #define DDIR (1 << 4) @@ -92,6 +98,8 @@ #define DUAL_VOLT_OCR_BIT 7 #define SRC (1 << 25) #define SRD (1 << 26) +#define SOFTRESET (1 << 1) +#define RESETDONE (1 << 0) /* * FIXME: Most likely all the data using these _DEVID defines should come @@ -101,11 +109,18 @@ #define OMAP_MMC1_DEVID 0 #define OMAP_MMC2_DEVID 1 #define OMAP_MMC3_DEVID 2 +#define OMAP_MMC4_DEVID 3 +#define OMAP_MMC5_DEVID 4 #define MMC_TIMEOUT_MS 20 #define OMAP_MMC_MASTER_CLOCK 96000000 #define DRIVER_NAME "mmci-omap-hs" +/* Timeouts for entering power saving states on inactivity, msec */ +#define OMAP_MMC_DISABLED_TIMEOUT 100 +#define OMAP_MMC_SLEEP_TIMEOUT 1000 +#define OMAP_MMC_OFF_TIMEOUT 8000 + /* * One controller can have multiple slots, like on some omap boards using * omap.c controller driver. Luckily this is not currently done on any known @@ -122,7 +137,7 @@ #define OMAP_HSMMC_WRITE(base, reg, val) \ __raw_writel((val), (base) + OMAP_HSMMC_##reg) -struct mmc_omap_host { +struct omap_hsmmc_host { struct device *dev; struct mmc_host *mmc; struct mmc_request *mrq; @@ -135,27 +150,35 @@ struct mmc_omap_host { struct work_struct mmc_carddetect_work; void __iomem *base; resource_size_t mapbase; + spinlock_t irq_lock; /* Prevent races with irq handler */ + unsigned long flags; unsigned int id; unsigned int dma_len; unsigned int dma_sg_idx; unsigned char bus_mode; + unsigned char power_mode; u32 *buffer; u32 bytesleft; int suspended; int irq; - int carddetect; int use_dma, dma_ch; int dma_line_tx, dma_line_rx; int slot_id; - int dbclk_enabled; + int got_dbclk; int response_busy; + int context_loss; + int dpm_state; + int vdd; + int protect_card; + int reqs_blocked; + struct omap_mmc_platform_data *pdata; }; /* * Stop clock to the card */ -static void omap_mmc_stop_clock(struct mmc_omap_host *host) +static void omap_hsmmc_stop_clock(struct omap_hsmmc_host *host) { OMAP_HSMMC_WRITE(host->base, SYSCTL, OMAP_HSMMC_READ(host->base, SYSCTL) & ~CEN); @@ -163,15 +186,178 @@ static void omap_mmc_stop_clock(struct mmc_omap_host *host) dev_dbg(mmc_dev(host->mmc), "MMC Clock is not stoped\n"); } +#ifdef CONFIG_PM + +/* + * Restore the MMC host context, if it was lost as result of a + * power state change. + */ +static int omap_hsmmc_context_restore(struct omap_hsmmc_host *host) +{ + struct mmc_ios *ios = &host->mmc->ios; + struct omap_mmc_platform_data *pdata = host->pdata; + int context_loss = 0; + u32 hctl, capa, con; + u16 dsor = 0; + unsigned long timeout; + + if (pdata->get_context_loss_count) { + context_loss = pdata->get_context_loss_count(host->dev); + if (context_loss < 0) + return 1; + } + + dev_dbg(mmc_dev(host->mmc), "context was %slost\n", + context_loss == host->context_loss ? "not " : ""); + if (host->context_loss == context_loss) + return 1; + + /* Wait for hardware reset */ + timeout = jiffies + msecs_to_jiffies(MMC_TIMEOUT_MS); + while ((OMAP_HSMMC_READ(host->base, SYSSTATUS) & RESETDONE) != RESETDONE + && time_before(jiffies, timeout)) + ; + + /* Do software reset */ + OMAP_HSMMC_WRITE(host->base, SYSCONFIG, SOFTRESET); + timeout = jiffies + msecs_to_jiffies(MMC_TIMEOUT_MS); + while ((OMAP_HSMMC_READ(host->base, SYSSTATUS) & RESETDONE) != RESETDONE + && time_before(jiffies, timeout)) + ; + + OMAP_HSMMC_WRITE(host->base, SYSCONFIG, + OMAP_HSMMC_READ(host->base, SYSCONFIG) | AUTOIDLE); + + if (host->id == OMAP_MMC1_DEVID) { + if (host->power_mode != MMC_POWER_OFF && + (1 << ios->vdd) <= MMC_VDD_23_24) + hctl = SDVS18; + else + hctl = SDVS30; + capa = VS30 | VS18; + } else { + hctl = SDVS18; + capa = VS18; + } + + OMAP_HSMMC_WRITE(host->base, HCTL, + OMAP_HSMMC_READ(host->base, HCTL) | hctl); + + OMAP_HSMMC_WRITE(host->base, CAPA, + OMAP_HSMMC_READ(host->base, CAPA) | capa); + + OMAP_HSMMC_WRITE(host->base, HCTL, + OMAP_HSMMC_READ(host->base, HCTL) | SDBP); + + timeout = jiffies + msecs_to_jiffies(MMC_TIMEOUT_MS); + while ((OMAP_HSMMC_READ(host->base, HCTL) & SDBP) != SDBP + && time_before(jiffies, timeout)) + ; + + OMAP_HSMMC_WRITE(host->base, STAT, STAT_CLEAR); + OMAP_HSMMC_WRITE(host->base, ISE, INT_EN_MASK); + OMAP_HSMMC_WRITE(host->base, IE, INT_EN_MASK); + + /* Do not initialize card-specific things if the power is off */ + if (host->power_mode == MMC_POWER_OFF) + goto out; + + con = OMAP_HSMMC_READ(host->base, CON); + switch (ios->bus_width) { + case MMC_BUS_WIDTH_8: + OMAP_HSMMC_WRITE(host->base, CON, con | DW8); + break; + case MMC_BUS_WIDTH_4: + OMAP_HSMMC_WRITE(host->base, CON, con & ~DW8); + OMAP_HSMMC_WRITE(host->base, HCTL, + OMAP_HSMMC_READ(host->base, HCTL) | FOUR_BIT); + break; + case MMC_BUS_WIDTH_1: + OMAP_HSMMC_WRITE(host->base, CON, con & ~DW8); + OMAP_HSMMC_WRITE(host->base, HCTL, + OMAP_HSMMC_READ(host->base, HCTL) & ~FOUR_BIT); + break; + } + + if (ios->clock) { + dsor = OMAP_MMC_MASTER_CLOCK / ios->clock; + if (dsor < 1) + dsor = 1; + + if (OMAP_MMC_MASTER_CLOCK / dsor > ios->clock) + dsor++; + + if (dsor > 250) + dsor = 250; + } + + OMAP_HSMMC_WRITE(host->base, SYSCTL, + OMAP_HSMMC_READ(host->base, SYSCTL) & ~CEN); + OMAP_HSMMC_WRITE(host->base, SYSCTL, (dsor << 6) | (DTO << 16)); + OMAP_HSMMC_WRITE(host->base, SYSCTL, + OMAP_HSMMC_READ(host->base, SYSCTL) | ICE); + + timeout = jiffies + msecs_to_jiffies(MMC_TIMEOUT_MS); + while ((OMAP_HSMMC_READ(host->base, SYSCTL) & ICS) != ICS + && time_before(jiffies, timeout)) + ; + + OMAP_HSMMC_WRITE(host->base, SYSCTL, + OMAP_HSMMC_READ(host->base, SYSCTL) | CEN); + + con = OMAP_HSMMC_READ(host->base, CON); + if (ios->bus_mode == MMC_BUSMODE_OPENDRAIN) + OMAP_HSMMC_WRITE(host->base, CON, con | OD); + else + OMAP_HSMMC_WRITE(host->base, CON, con & ~OD); +out: + host->context_loss = context_loss; + + dev_dbg(mmc_dev(host->mmc), "context is restored\n"); + return 0; +} + +/* + * Save the MMC host context (store the number of power state changes so far). + */ +static void omap_hsmmc_context_save(struct omap_hsmmc_host *host) +{ + struct omap_mmc_platform_data *pdata = host->pdata; + int context_loss; + + if (pdata->get_context_loss_count) { + context_loss = pdata->get_context_loss_count(host->dev); + if (context_loss < 0) + return; + host->context_loss = context_loss; + } +} + +#else + +static int omap_hsmmc_context_restore(struct omap_hsmmc_host *host) +{ + return 0; +} + +static void omap_hsmmc_context_save(struct omap_hsmmc_host *host) +{ +} + +#endif + /* * Send init stream sequence to card * before sending IDLE command */ -static void send_init_stream(struct mmc_omap_host *host) +static void send_init_stream(struct omap_hsmmc_host *host) { int reg = 0; unsigned long timeout; + if (host->protect_card) + return; + disable_irq(host->irq); OMAP_HSMMC_WRITE(host->base, CON, OMAP_HSMMC_READ(host->base, CON) | INIT_STREAM); @@ -183,51 +369,53 @@ static void send_init_stream(struct mmc_omap_host *host) OMAP_HSMMC_WRITE(host->base, CON, OMAP_HSMMC_READ(host->base, CON) & ~INIT_STREAM); + + OMAP_HSMMC_WRITE(host->base, STAT, STAT_CLEAR); + OMAP_HSMMC_READ(host->base, STAT); + enable_irq(host->irq); } static inline -int mmc_omap_cover_is_closed(struct mmc_omap_host *host) +int omap_hsmmc_cover_is_closed(struct omap_hsmmc_host *host) { int r = 1; - if (host->pdata->slots[host->slot_id].get_cover_state) - r = host->pdata->slots[host->slot_id].get_cover_state(host->dev, - host->slot_id); + if (mmc_slot(host).get_cover_state) + r = mmc_slot(host).get_cover_state(host->dev, host->slot_id); return r; } static ssize_t -mmc_omap_show_cover_switch(struct device *dev, struct device_attribute *attr, +omap_hsmmc_show_cover_switch(struct device *dev, struct device_attribute *attr, char *buf) { struct mmc_host *mmc = container_of(dev, struct mmc_host, class_dev); - struct mmc_omap_host *host = mmc_priv(mmc); + struct omap_hsmmc_host *host = mmc_priv(mmc); - return sprintf(buf, "%s\n", mmc_omap_cover_is_closed(host) ? "closed" : - "open"); + return sprintf(buf, "%s\n", + omap_hsmmc_cover_is_closed(host) ? "closed" : "open"); } -static DEVICE_ATTR(cover_switch, S_IRUGO, mmc_omap_show_cover_switch, NULL); +static DEVICE_ATTR(cover_switch, S_IRUGO, omap_hsmmc_show_cover_switch, NULL); static ssize_t -mmc_omap_show_slot_name(struct device *dev, struct device_attribute *attr, +omap_hsmmc_show_slot_name(struct device *dev, struct device_attribute *attr, char *buf) { struct mmc_host *mmc = container_of(dev, struct mmc_host, class_dev); - struct mmc_omap_host *host = mmc_priv(mmc); - struct omap_mmc_slot_data slot = host->pdata->slots[host->slot_id]; + struct omap_hsmmc_host *host = mmc_priv(mmc); - return sprintf(buf, "%s\n", slot.name); + return sprintf(buf, "%s\n", mmc_slot(host).name); } -static DEVICE_ATTR(slot_name, S_IRUGO, mmc_omap_show_slot_name, NULL); +static DEVICE_ATTR(slot_name, S_IRUGO, omap_hsmmc_show_slot_name, NULL); /* * Configure the response type and send the cmd. */ static void -mmc_omap_start_command(struct mmc_omap_host *host, struct mmc_command *cmd, +omap_hsmmc_start_command(struct omap_hsmmc_host *host, struct mmc_command *cmd, struct mmc_data *data) { int cmdreg = 0, resptype = 0, cmdtype = 0; @@ -241,7 +429,12 @@ mmc_omap_start_command(struct mmc_omap_host *host, struct mmc_command *cmd, */ OMAP_HSMMC_WRITE(host->base, STAT, STAT_CLEAR); OMAP_HSMMC_WRITE(host->base, ISE, INT_EN_MASK); - OMAP_HSMMC_WRITE(host->base, IE, INT_EN_MASK); + + if (host->use_dma) + OMAP_HSMMC_WRITE(host->base, IE, + INT_EN_MASK & ~(BRR_ENABLE | BWR_ENABLE)); + else + OMAP_HSMMC_WRITE(host->base, IE, INT_EN_MASK); host->response_busy = 0; if (cmd->flags & MMC_RSP_PRESENT) { @@ -275,12 +468,20 @@ mmc_omap_start_command(struct mmc_omap_host *host, struct mmc_command *cmd, if (host->use_dma) cmdreg |= DMA_EN; + /* + * In an interrupt context (i.e. STOP command), the spinlock is unlocked + * by the interrupt handler, otherwise (i.e. for a new request) it is + * unlocked here. + */ + if (!in_interrupt()) + spin_unlock_irqrestore(&host->irq_lock, host->flags); + OMAP_HSMMC_WRITE(host->base, ARG, cmd->arg); OMAP_HSMMC_WRITE(host->base, CMD, cmdreg); } static int -mmc_omap_get_dma_dir(struct mmc_omap_host *host, struct mmc_data *data) +omap_hsmmc_get_dma_dir(struct omap_hsmmc_host *host, struct mmc_data *data) { if (data->flags & MMC_DATA_WRITE) return DMA_TO_DEVICE; @@ -292,11 +493,18 @@ mmc_omap_get_dma_dir(struct mmc_omap_host *host, struct mmc_data *data) * Notify the transfer complete to MMC core */ static void -mmc_omap_xfer_done(struct mmc_omap_host *host, struct mmc_data *data) +omap_hsmmc_xfer_done(struct omap_hsmmc_host *host, struct mmc_data *data) { if (!data) { struct mmc_request *mrq = host->mrq; + /* TC before CC from CMD6 - don't know why, but it happens */ + if (host->cmd && host->cmd->opcode == 6 && + host->response_busy) { + host->response_busy = 0; + return; + } + host->mrq = NULL; mmc_request_done(host->mmc, mrq); return; @@ -306,7 +514,7 @@ mmc_omap_xfer_done(struct mmc_omap_host *host, struct mmc_data *data) if (host->use_dma && host->dma_ch != -1) dma_unmap_sg(mmc_dev(host->mmc), data->sg, host->dma_len, - mmc_omap_get_dma_dir(host, data)); + omap_hsmmc_get_dma_dir(host, data)); if (!data->error) data->bytes_xfered += data->blocks * (data->blksz); @@ -318,14 +526,14 @@ mmc_omap_xfer_done(struct mmc_omap_host *host, struct mmc_data *data) mmc_request_done(host->mmc, data->mrq); return; } - mmc_omap_start_command(host, data->stop, NULL); + omap_hsmmc_start_command(host, data->stop, NULL); } /* * Notify the core about command completion */ static void -mmc_omap_cmd_done(struct mmc_omap_host *host, struct mmc_command *cmd) +omap_hsmmc_cmd_done(struct omap_hsmmc_host *host, struct mmc_command *cmd) { host->cmd = NULL; @@ -350,13 +558,13 @@ mmc_omap_cmd_done(struct mmc_omap_host *host, struct mmc_command *cmd) /* * DMA clean up for command errors */ -static void mmc_dma_cleanup(struct mmc_omap_host *host, int errno) +static void omap_hsmmc_dma_cleanup(struct omap_hsmmc_host *host, int errno) { host->data->error = errno; if (host->use_dma && host->dma_ch != -1) { dma_unmap_sg(mmc_dev(host->mmc), host->data->sg, host->dma_len, - mmc_omap_get_dma_dir(host, host->data)); + omap_hsmmc_get_dma_dir(host, host->data)); omap_free_dma(host->dma_ch); host->dma_ch = -1; up(&host->sem); @@ -368,10 +576,10 @@ static void mmc_dma_cleanup(struct mmc_omap_host *host, int errno) * Readable error output */ #ifdef CONFIG_MMC_DEBUG -static void mmc_omap_report_irq(struct mmc_omap_host *host, u32 status) +static void omap_hsmmc_report_irq(struct omap_hsmmc_host *host, u32 status) { /* --- means reserved bit without definition at documentation */ - static const char *mmc_omap_status_bits[] = { + static const char *omap_hsmmc_status_bits[] = { "CC", "TC", "BGE", "---", "BWR", "BRR", "---", "---", "CIRQ", "OBI", "---", "---", "---", "---", "---", "ERRI", "CTO", "CCRC", "CEB", "CIE", "DTO", "DCRC", "DEB", "---", "ACE", "---", @@ -384,9 +592,9 @@ static void mmc_omap_report_irq(struct mmc_omap_host *host, u32 status) len = sprintf(buf, "MMC IRQ 0x%x :", status); buf += len; - for (i = 0; i < ARRAY_SIZE(mmc_omap_status_bits); i++) + for (i = 0; i < ARRAY_SIZE(omap_hsmmc_status_bits); i++) if (status & (1 << i)) { - len = sprintf(buf, " %s", mmc_omap_status_bits[i]); + len = sprintf(buf, " %s", omap_hsmmc_status_bits[i]); buf += len; } @@ -401,8 +609,8 @@ static void mmc_omap_report_irq(struct mmc_omap_host *host, u32 status) * SRC or SRD bit of SYSCTL register * Can be called from interrupt context */ -static inline void mmc_omap_reset_controller_fsm(struct mmc_omap_host *host, - unsigned long bit) +static inline void omap_hsmmc_reset_controller_fsm(struct omap_hsmmc_host *host, + unsigned long bit) { unsigned long i = 0; unsigned long limit = (loops_per_jiffy * @@ -424,17 +632,20 @@ static inline void mmc_omap_reset_controller_fsm(struct mmc_omap_host *host, /* * MMC controller IRQ handler */ -static irqreturn_t mmc_omap_irq(int irq, void *dev_id) +static irqreturn_t omap_hsmmc_irq(int irq, void *dev_id) { - struct mmc_omap_host *host = dev_id; + struct omap_hsmmc_host *host = dev_id; struct mmc_data *data; int end_cmd = 0, end_trans = 0, status; + spin_lock(&host->irq_lock); + if (host->mrq == NULL) { OMAP_HSMMC_WRITE(host->base, STAT, OMAP_HSMMC_READ(host->base, STAT)); /* Flush posted write */ OMAP_HSMMC_READ(host->base, STAT); + spin_unlock(&host->irq_lock); return IRQ_HANDLED; } @@ -444,13 +655,14 @@ static irqreturn_t mmc_omap_irq(int irq, void *dev_id) if (status & ERR) { #ifdef CONFIG_MMC_DEBUG - mmc_omap_report_irq(host, status); + omap_hsmmc_report_irq(host, status); #endif if ((status & CMD_TIMEOUT) || (status & CMD_CRC)) { if (host->cmd) { if (status & CMD_TIMEOUT) { - mmc_omap_reset_controller_fsm(host, SRC); + omap_hsmmc_reset_controller_fsm(host, + SRC); host->cmd->error = -ETIMEDOUT; } else { host->cmd->error = -EILSEQ; @@ -459,9 +671,10 @@ static irqreturn_t mmc_omap_irq(int irq, void *dev_id) } if (host->data || host->response_busy) { if (host->data) - mmc_dma_cleanup(host, -ETIMEDOUT); + omap_hsmmc_dma_cleanup(host, + -ETIMEDOUT); host->response_busy = 0; - mmc_omap_reset_controller_fsm(host, SRD); + omap_hsmmc_reset_controller_fsm(host, SRD); } } if ((status & DATA_TIMEOUT) || @@ -471,11 +684,11 @@ static irqreturn_t mmc_omap_irq(int irq, void *dev_id) -ETIMEDOUT : -EILSEQ; if (host->data) - mmc_dma_cleanup(host, err); + omap_hsmmc_dma_cleanup(host, err); else host->mrq->cmd->error = err; host->response_busy = 0; - mmc_omap_reset_controller_fsm(host, SRD); + omap_hsmmc_reset_controller_fsm(host, SRD); end_trans = 1; } } @@ -494,14 +707,16 @@ static irqreturn_t mmc_omap_irq(int irq, void *dev_id) OMAP_HSMMC_READ(host->base, STAT); if (end_cmd || ((status & CC) && host->cmd)) - mmc_omap_cmd_done(host, host->cmd); - if (end_trans || (status & TC)) - mmc_omap_xfer_done(host, data); + omap_hsmmc_cmd_done(host, host->cmd); + if ((end_trans || (status & TC)) && host->mrq) + omap_hsmmc_xfer_done(host, data); + + spin_unlock(&host->irq_lock); return IRQ_HANDLED; } -static void set_sd_bus_power(struct mmc_omap_host *host) +static void set_sd_bus_power(struct omap_hsmmc_host *host) { unsigned long i; @@ -521,7 +736,7 @@ static void set_sd_bus_power(struct mmc_omap_host *host) * The MMC2 transceiver controls are used instead of DAT4..DAT7. * Some chips, like eMMC ones, use internal transceivers. */ -static int omap_mmc_switch_opcond(struct mmc_omap_host *host, int vdd) +static int omap_hsmmc_switch_opcond(struct omap_hsmmc_host *host, int vdd) { u32 reg_val = 0; int ret; @@ -529,22 +744,24 @@ static int omap_mmc_switch_opcond(struct mmc_omap_host *host, int vdd) /* Disable the clocks */ clk_disable(host->fclk); clk_disable(host->iclk); - clk_disable(host->dbclk); + if (host->got_dbclk) + clk_disable(host->dbclk); /* Turn the power off */ ret = mmc_slot(host).set_power(host->dev, host->slot_id, 0, 0); - if (ret != 0) - goto err; /* Turn the power ON with given VDD 1.8 or 3.0v */ - ret = mmc_slot(host).set_power(host->dev, host->slot_id, 1, vdd); + if (!ret) + ret = mmc_slot(host).set_power(host->dev, host->slot_id, 1, + vdd); + clk_enable(host->iclk); + clk_enable(host->fclk); + if (host->got_dbclk) + clk_enable(host->dbclk); + if (ret != 0) goto err; - clk_enable(host->fclk); - clk_enable(host->iclk); - clk_enable(host->dbclk); - OMAP_HSMMC_WRITE(host->base, HCTL, OMAP_HSMMC_READ(host->base, HCTL) & SDVSCLR); reg_val = OMAP_HSMMC_READ(host->base, HCTL); @@ -552,7 +769,7 @@ static int omap_mmc_switch_opcond(struct mmc_omap_host *host, int vdd) /* * If a MMC dual voltage card is detected, the set_ios fn calls * this fn with VDD bit set for 1.8V. Upon card removal from the - * slot, omap_mmc_set_ios sets the VDD back to 3V on MMC_POWER_OFF. + * slot, omap_hsmmc_set_ios sets the VDD back to 3V on MMC_POWER_OFF. * * Cope with a bit of slop in the range ... per data sheets: * - "1.8V" for vdds_mmc1/vdds_mmc1a can be up to 2.45V max, @@ -578,25 +795,59 @@ err: return ret; } +/* Protect the card while the cover is open */ +static void omap_hsmmc_protect_card(struct omap_hsmmc_host *host) +{ + if (!mmc_slot(host).get_cover_state) + return; + + host->reqs_blocked = 0; + if (mmc_slot(host).get_cover_state(host->dev, host->slot_id)) { + if (host->protect_card) { + printk(KERN_INFO "%s: cover is closed, " + "card is now accessible\n", + mmc_hostname(host->mmc)); + host->protect_card = 0; + } + } else { + if (!host->protect_card) { + printk(KERN_INFO "%s: cover is open, " + "card is now inaccessible\n", + mmc_hostname(host->mmc)); + host->protect_card = 1; + } + } +} + /* * Work Item to notify the core about card insertion/removal */ -static void mmc_omap_detect(struct work_struct *work) +static void omap_hsmmc_detect(struct work_struct *work) { - struct mmc_omap_host *host = container_of(work, struct mmc_omap_host, - mmc_carddetect_work); + struct omap_hsmmc_host *host = + container_of(work, struct omap_hsmmc_host, mmc_carddetect_work); struct omap_mmc_slot_data *slot = &mmc_slot(host); + int carddetect; - if (mmc_slot(host).card_detect) - host->carddetect = slot->card_detect(slot->card_detect_irq); - else - host->carddetect = -ENOSYS; + if (host->suspended) + return; sysfs_notify(&host->mmc->class_dev.kobj, NULL, "cover_switch"); - if (host->carddetect) { + + if (slot->card_detect) + carddetect = slot->card_detect(slot->card_detect_irq); + else { + omap_hsmmc_protect_card(host); + carddetect = -ENOSYS; + } + + if (carddetect) { mmc_detect_change(host->mmc, (HZ * 200) / 1000); } else { - mmc_omap_reset_controller_fsm(host, SRD); + mmc_host_enable(host->mmc); + omap_hsmmc_reset_controller_fsm(host, SRD); + mmc_host_lazy_disable(host->mmc); + mmc_detect_change(host->mmc, (HZ * 50) / 1000); } } @@ -604,16 +855,18 @@ static void mmc_omap_detect(struct work_struct *work) /* * ISR for handling card insertion and removal */ -static irqreturn_t omap_mmc_cd_handler(int irq, void *dev_id) +static irqreturn_t omap_hsmmc_cd_handler(int irq, void *dev_id) { - struct mmc_omap_host *host = (struct mmc_omap_host *)dev_id; + struct omap_hsmmc_host *host = (struct omap_hsmmc_host *)dev_id; + if (host->suspended) + return IRQ_HANDLED; schedule_work(&host->mmc_carddetect_work); return IRQ_HANDLED; } -static int mmc_omap_get_dma_sync_dev(struct mmc_omap_host *host, +static int omap_hsmmc_get_dma_sync_dev(struct omap_hsmmc_host *host, struct mmc_data *data) { int sync_dev; @@ -625,7 +878,7 @@ static int mmc_omap_get_dma_sync_dev(struct mmc_omap_host *host, return sync_dev; } -static void mmc_omap_config_dma_params(struct mmc_omap_host *host, +static void omap_hsmmc_config_dma_params(struct omap_hsmmc_host *host, struct mmc_data *data, struct scatterlist *sgl) { @@ -639,7 +892,7 @@ static void mmc_omap_config_dma_params(struct mmc_omap_host *host, sg_dma_address(sgl), 0, 0); } else { omap_set_dma_src_params(dma_ch, 0, OMAP_DMA_AMODE_CONSTANT, - (host->mapbase + OMAP_HSMMC_DATA), 0, 0); + (host->mapbase + OMAP_HSMMC_DATA), 0, 0); omap_set_dma_dest_params(dma_ch, 0, OMAP_DMA_AMODE_POST_INC, sg_dma_address(sgl), 0, 0); } @@ -649,7 +902,7 @@ static void mmc_omap_config_dma_params(struct mmc_omap_host *host, omap_set_dma_transfer_params(dma_ch, OMAP_DMA_DATA_TYPE_S32, blksz / 4, nblk, OMAP_DMA_SYNC_FRAME, - mmc_omap_get_dma_sync_dev(host, data), + omap_hsmmc_get_dma_sync_dev(host, data), !(data->flags & MMC_DATA_WRITE)); omap_start_dma(dma_ch); @@ -658,9 +911,9 @@ static void mmc_omap_config_dma_params(struct mmc_omap_host *host, /* * DMA call back function */ -static void mmc_omap_dma_cb(int lch, u16 ch_status, void *data) +static void omap_hsmmc_dma_cb(int lch, u16 ch_status, void *data) { - struct mmc_omap_host *host = data; + struct omap_hsmmc_host *host = data; if (ch_status & OMAP2_DMA_MISALIGNED_ERR_IRQ) dev_dbg(mmc_dev(host->mmc), "MISALIGNED_ADRS_ERR\n"); @@ -671,7 +924,7 @@ static void mmc_omap_dma_cb(int lch, u16 ch_status, void *data) host->dma_sg_idx++; if (host->dma_sg_idx < host->dma_len) { /* Fire up the next transfer. */ - mmc_omap_config_dma_params(host, host->data, + omap_hsmmc_config_dma_params(host, host->data, host->data->sg + host->dma_sg_idx); return; } @@ -688,14 +941,14 @@ static void mmc_omap_dma_cb(int lch, u16 ch_status, void *data) /* * Routine to configure and start DMA for the MMC card */ -static int -mmc_omap_start_dma_transfer(struct mmc_omap_host *host, struct mmc_request *req) +static int omap_hsmmc_start_dma_transfer(struct omap_hsmmc_host *host, + struct mmc_request *req) { int dma_ch = 0, ret = 0, err = 1, i; struct mmc_data *data = req->data; /* Sanity check: all the SG entries must be aligned by block size. */ - for (i = 0; i < host->dma_len; i++) { + for (i = 0; i < data->sg_len; i++) { struct scatterlist *sgl; sgl = data->sg + i; @@ -726,8 +979,8 @@ mmc_omap_start_dma_transfer(struct mmc_omap_host *host, struct mmc_request *req) return err; } - ret = omap_request_dma(mmc_omap_get_dma_sync_dev(host, data), "MMC/SD", - mmc_omap_dma_cb,host, &dma_ch); + ret = omap_request_dma(omap_hsmmc_get_dma_sync_dev(host, data), + "MMC/SD", omap_hsmmc_dma_cb, host, &dma_ch); if (ret != 0) { dev_err(mmc_dev(host->mmc), "%s: omap_request_dma() failed with %d\n", @@ -736,17 +989,18 @@ mmc_omap_start_dma_transfer(struct mmc_omap_host *host, struct mmc_request *req) } host->dma_len = dma_map_sg(mmc_dev(host->mmc), data->sg, - data->sg_len, mmc_omap_get_dma_dir(host, data)); + data->sg_len, omap_hsmmc_get_dma_dir(host, data)); host->dma_ch = dma_ch; host->dma_sg_idx = 0; - mmc_omap_config_dma_params(host, data, data->sg); + omap_hsmmc_config_dma_params(host, data, data->sg); return 0; } -static void set_data_timeout(struct mmc_omap_host *host, - struct mmc_request *req) +static void set_data_timeout(struct omap_hsmmc_host *host, + unsigned int timeout_ns, + unsigned int timeout_clks) { unsigned int timeout, cycle_ns; uint32_t reg, clkd, dto = 0; @@ -757,8 +1011,8 @@ static void set_data_timeout(struct mmc_omap_host *host, clkd = 1; cycle_ns = 1000000000 / (clk_get_rate(host->fclk) / clkd); - timeout = req->data->timeout_ns / cycle_ns; - timeout += req->data->timeout_clks; + timeout = timeout_ns / cycle_ns; + timeout += timeout_clks; if (timeout) { while ((timeout & 0x80000000) == 0) { dto += 1; @@ -785,22 +1039,28 @@ static void set_data_timeout(struct mmc_omap_host *host, * Configure block length for MMC/SD cards and initiate the transfer. */ static int -mmc_omap_prepare_data(struct mmc_omap_host *host, struct mmc_request *req) +omap_hsmmc_prepare_data(struct omap_hsmmc_host *host, struct mmc_request *req) { int ret; host->data = req->data; if (req->data == NULL) { OMAP_HSMMC_WRITE(host->base, BLK, 0); + /* + * Set an arbitrary 100ms data timeout for commands with + * busy signal. + */ + if (req->cmd->flags & MMC_RSP_BUSY) + set_data_timeout(host, 100000000U, 0); return 0; } OMAP_HSMMC_WRITE(host->base, BLK, (req->data->blksz) | (req->data->blocks << 16)); - set_data_timeout(host, req); + set_data_timeout(host, req->data->timeout_ns, req->data->timeout_clks); if (host->use_dma) { - ret = mmc_omap_start_dma_transfer(host, req); + ret = omap_hsmmc_start_dma_transfer(host, req); if (ret != 0) { dev_dbg(mmc_dev(host->mmc), "MMC start dma failure\n"); return ret; @@ -812,35 +1072,92 @@ mmc_omap_prepare_data(struct mmc_omap_host *host, struct mmc_request *req) /* * Request function. for read/write operation */ -static void omap_mmc_request(struct mmc_host *mmc, struct mmc_request *req) +static void omap_hsmmc_request(struct mmc_host *mmc, struct mmc_request *req) { - struct mmc_omap_host *host = mmc_priv(mmc); + struct omap_hsmmc_host *host = mmc_priv(mmc); + int err; + /* + * Prevent races with the interrupt handler because of unexpected + * interrupts, but not if we are already in interrupt context i.e. + * retries. + */ + if (!in_interrupt()) { + spin_lock_irqsave(&host->irq_lock, host->flags); + /* + * Protect the card from I/O if there is a possibility + * it can be removed. + */ + if (host->protect_card) { + if (host->reqs_blocked < 3) { + /* + * Ensure the controller is left in a consistent + * state by resetting the command and data state + * machines. + */ + omap_hsmmc_reset_controller_fsm(host, SRD); + omap_hsmmc_reset_controller_fsm(host, SRC); + host->reqs_blocked += 1; + } + req->cmd->error = -EBADF; + if (req->data) + req->data->error = -EBADF; + spin_unlock_irqrestore(&host->irq_lock, host->flags); + mmc_request_done(mmc, req); + return; + } else if (host->reqs_blocked) + host->reqs_blocked = 0; + } WARN_ON(host->mrq != NULL); host->mrq = req; - mmc_omap_prepare_data(host, req); - mmc_omap_start_command(host, req->cmd, req->data); -} + err = omap_hsmmc_prepare_data(host, req); + if (err) { + req->cmd->error = err; + if (req->data) + req->data->error = err; + host->mrq = NULL; + if (!in_interrupt()) + spin_unlock_irqrestore(&host->irq_lock, host->flags); + mmc_request_done(mmc, req); + return; + } + omap_hsmmc_start_command(host, req->cmd, req->data); +} /* Routine to configure clock values. Exposed API to core */ -static void omap_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) +static void omap_hsmmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) { - struct mmc_omap_host *host = mmc_priv(mmc); + struct omap_hsmmc_host *host = mmc_priv(mmc); u16 dsor = 0; unsigned long regval; unsigned long timeout; u32 con; + int do_send_init_stream = 0; - switch (ios->power_mode) { - case MMC_POWER_OFF: - mmc_slot(host).set_power(host->dev, host->slot_id, 0, 0); - break; - case MMC_POWER_UP: - mmc_slot(host).set_power(host->dev, host->slot_id, 1, ios->vdd); - break; + mmc_host_enable(host->mmc); + + if (ios->power_mode != host->power_mode) { + switch (ios->power_mode) { + case MMC_POWER_OFF: + mmc_slot(host).set_power(host->dev, host->slot_id, + 0, 0); + host->vdd = 0; + break; + case MMC_POWER_UP: + mmc_slot(host).set_power(host->dev, host->slot_id, + 1, ios->vdd); + host->vdd = ios->vdd; + break; + case MMC_POWER_ON: + do_send_init_stream = 1; + break; + } + host->power_mode = ios->power_mode; } + /* FIXME: set registers based only on changes to ios */ + con = OMAP_HSMMC_READ(host->base, CON); switch (mmc->ios.bus_width) { case MMC_BUS_WIDTH_8: @@ -870,8 +1187,8 @@ static void omap_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) * MMC_POWER_UP upon recalculating the voltage. * vdd 1.8v. */ - if (omap_mmc_switch_opcond(host, ios->vdd) != 0) - dev_dbg(mmc_dev(host->mmc), + if (omap_hsmmc_switch_opcond(host, ios->vdd) != 0) + dev_dbg(mmc_dev(host->mmc), "Switch operation failed\n"); } } @@ -887,7 +1204,7 @@ static void omap_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) if (dsor > 250) dsor = 250; } - omap_mmc_stop_clock(host); + omap_hsmmc_stop_clock(host); regval = OMAP_HSMMC_READ(host->base, SYSCTL); regval = regval & ~(CLKD_MASK); regval = regval | (dsor << 6) | (DTO << 16); @@ -897,42 +1214,47 @@ static void omap_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) /* Wait till the ICS bit is set */ timeout = jiffies + msecs_to_jiffies(MMC_TIMEOUT_MS); - while ((OMAP_HSMMC_READ(host->base, SYSCTL) & ICS) != 0x2 + while ((OMAP_HSMMC_READ(host->base, SYSCTL) & ICS) != ICS && time_before(jiffies, timeout)) msleep(1); OMAP_HSMMC_WRITE(host->base, SYSCTL, OMAP_HSMMC_READ(host->base, SYSCTL) | CEN); - if (ios->power_mode == MMC_POWER_ON) + if (do_send_init_stream) send_init_stream(host); + con = OMAP_HSMMC_READ(host->base, CON); if (ios->bus_mode == MMC_BUSMODE_OPENDRAIN) - OMAP_HSMMC_WRITE(host->base, CON, - OMAP_HSMMC_READ(host->base, CON) | OD); + OMAP_HSMMC_WRITE(host->base, CON, con | OD); + else + OMAP_HSMMC_WRITE(host->base, CON, con & ~OD); + + if (host->power_mode == MMC_POWER_OFF) + mmc_host_disable(host->mmc); + else + mmc_host_lazy_disable(host->mmc); } static int omap_hsmmc_get_cd(struct mmc_host *mmc) { - struct mmc_omap_host *host = mmc_priv(mmc); - struct omap_mmc_platform_data *pdata = host->pdata; + struct omap_hsmmc_host *host = mmc_priv(mmc); - if (!pdata->slots[0].card_detect) + if (!mmc_slot(host).card_detect) return -ENOSYS; - return pdata->slots[0].card_detect(pdata->slots[0].card_detect_irq); + return mmc_slot(host).card_detect(mmc_slot(host).card_detect_irq); } static int omap_hsmmc_get_ro(struct mmc_host *mmc) { - struct mmc_omap_host *host = mmc_priv(mmc); - struct omap_mmc_platform_data *pdata = host->pdata; + struct omap_hsmmc_host *host = mmc_priv(mmc); - if (!pdata->slots[0].get_ro) + if (!mmc_slot(host).get_ro) return -ENOSYS; - return pdata->slots[0].get_ro(host->dev, 0); + return mmc_slot(host).get_ro(host->dev, 0); } -static void omap_hsmmc_init(struct mmc_omap_host *host) +static void omap_hsmmc_conf_bus_power(struct omap_hsmmc_host *host) { u32 hctl, capa, value; @@ -959,19 +1281,340 @@ static void omap_hsmmc_init(struct mmc_omap_host *host) set_sd_bus_power(host); } -static struct mmc_host_ops mmc_omap_ops = { - .request = omap_mmc_request, - .set_ios = omap_mmc_set_ios, +/* + * Dynamic power saving handling, FSM: + * ENABLED -> DISABLED -> CARDSLEEP / REGSLEEP -> OFF + * ^___________| | | + * |______________________|______________________| + * + * ENABLED: mmc host is fully functional + * DISABLED: fclk is off + * CARDSLEEP: fclk is off, card is asleep, voltage regulator is asleep + * REGSLEEP: fclk is off, voltage regulator is asleep + * OFF: fclk is off, voltage regulator is off + * + * Transition handlers return the timeout for the next state transition + * or negative error. + */ + +enum {ENABLED = 0, DISABLED, CARDSLEEP, REGSLEEP, OFF}; + +/* Handler for [ENABLED -> DISABLED] transition */ +static int omap_hsmmc_enabled_to_disabled(struct omap_hsmmc_host *host) +{ + omap_hsmmc_context_save(host); + clk_disable(host->fclk); + host->dpm_state = DISABLED; + + dev_dbg(mmc_dev(host->mmc), "ENABLED -> DISABLED\n"); + + if (host->power_mode == MMC_POWER_OFF) + return 0; + + return msecs_to_jiffies(OMAP_MMC_SLEEP_TIMEOUT); +} + +/* Handler for [DISABLED -> REGSLEEP / CARDSLEEP] transition */ +static int omap_hsmmc_disabled_to_sleep(struct omap_hsmmc_host *host) +{ + int err, new_state; + + if (!mmc_try_claim_host(host->mmc)) + return 0; + + clk_enable(host->fclk); + omap_hsmmc_context_restore(host); + if (mmc_card_can_sleep(host->mmc)) { + err = mmc_card_sleep(host->mmc); + if (err < 0) { + clk_disable(host->fclk); + mmc_release_host(host->mmc); + return err; + } + new_state = CARDSLEEP; + } else { + new_state = REGSLEEP; + } + if (mmc_slot(host).set_sleep) + mmc_slot(host).set_sleep(host->dev, host->slot_id, 1, 0, + new_state == CARDSLEEP); + /* FIXME: turn off bus power and perhaps interrupts too */ + clk_disable(host->fclk); + host->dpm_state = new_state; + + mmc_release_host(host->mmc); + + dev_dbg(mmc_dev(host->mmc), "DISABLED -> %s\n", + host->dpm_state == CARDSLEEP ? "CARDSLEEP" : "REGSLEEP"); + + if ((host->mmc->caps & MMC_CAP_NONREMOVABLE) || + mmc_slot(host).card_detect || + (mmc_slot(host).get_cover_state && + mmc_slot(host).get_cover_state(host->dev, host->slot_id))) + return msecs_to_jiffies(OMAP_MMC_OFF_TIMEOUT); + + return 0; +} + +/* Handler for [REGSLEEP / CARDSLEEP -> OFF] transition */ +static int omap_hsmmc_sleep_to_off(struct omap_hsmmc_host *host) +{ + if (!mmc_try_claim_host(host->mmc)) + return 0; + + if (!((host->mmc->caps & MMC_CAP_NONREMOVABLE) || + mmc_slot(host).card_detect || + (mmc_slot(host).get_cover_state && + mmc_slot(host).get_cover_state(host->dev, host->slot_id)))) { + mmc_release_host(host->mmc); + return 0; + } + + mmc_slot(host).set_power(host->dev, host->slot_id, 0, 0); + host->vdd = 0; + host->power_mode = MMC_POWER_OFF; + + dev_dbg(mmc_dev(host->mmc), "%s -> OFF\n", + host->dpm_state == CARDSLEEP ? "CARDSLEEP" : "REGSLEEP"); + + host->dpm_state = OFF; + + mmc_release_host(host->mmc); + + return 0; +} + +/* Handler for [DISABLED -> ENABLED] transition */ +static int omap_hsmmc_disabled_to_enabled(struct omap_hsmmc_host *host) +{ + int err; + + err = clk_enable(host->fclk); + if (err < 0) + return err; + + omap_hsmmc_context_restore(host); + host->dpm_state = ENABLED; + + dev_dbg(mmc_dev(host->mmc), "DISABLED -> ENABLED\n"); + + return 0; +} + +/* Handler for [SLEEP -> ENABLED] transition */ +static int omap_hsmmc_sleep_to_enabled(struct omap_hsmmc_host *host) +{ + if (!mmc_try_claim_host(host->mmc)) + return 0; + + clk_enable(host->fclk); + omap_hsmmc_context_restore(host); + if (mmc_slot(host).set_sleep) + mmc_slot(host).set_sleep(host->dev, host->slot_id, 0, + host->vdd, host->dpm_state == CARDSLEEP); + if (mmc_card_can_sleep(host->mmc)) + mmc_card_awake(host->mmc); + + dev_dbg(mmc_dev(host->mmc), "%s -> ENABLED\n", + host->dpm_state == CARDSLEEP ? "CARDSLEEP" : "REGSLEEP"); + + host->dpm_state = ENABLED; + + mmc_release_host(host->mmc); + + return 0; +} + +/* Handler for [OFF -> ENABLED] transition */ +static int omap_hsmmc_off_to_enabled(struct omap_hsmmc_host *host) +{ + clk_enable(host->fclk); + + omap_hsmmc_context_restore(host); + omap_hsmmc_conf_bus_power(host); + mmc_power_restore_host(host->mmc); + + host->dpm_state = ENABLED; + + dev_dbg(mmc_dev(host->mmc), "OFF -> ENABLED\n"); + + return 0; +} + +/* + * Bring MMC host to ENABLED from any other PM state. + */ +static int omap_hsmmc_enable(struct mmc_host *mmc) +{ + struct omap_hsmmc_host *host = mmc_priv(mmc); + + switch (host->dpm_state) { + case DISABLED: + return omap_hsmmc_disabled_to_enabled(host); + case CARDSLEEP: + case REGSLEEP: + return omap_hsmmc_sleep_to_enabled(host); + case OFF: + return omap_hsmmc_off_to_enabled(host); + default: + dev_dbg(mmc_dev(host->mmc), "UNKNOWN state\n"); + return -EINVAL; + } +} + +/* + * Bring MMC host in PM state (one level deeper). + */ +static int omap_hsmmc_disable(struct mmc_host *mmc, int lazy) +{ + struct omap_hsmmc_host *host = mmc_priv(mmc); + + switch (host->dpm_state) { + case ENABLED: { + int delay; + + delay = omap_hsmmc_enabled_to_disabled(host); + if (lazy || delay < 0) + return delay; + return 0; + } + case DISABLED: + return omap_hsmmc_disabled_to_sleep(host); + case CARDSLEEP: + case REGSLEEP: + return omap_hsmmc_sleep_to_off(host); + default: + dev_dbg(mmc_dev(host->mmc), "UNKNOWN state\n"); + return -EINVAL; + } +} + +static int omap_hsmmc_enable_fclk(struct mmc_host *mmc) +{ + struct omap_hsmmc_host *host = mmc_priv(mmc); + int err; + + err = clk_enable(host->fclk); + if (err) + return err; + dev_dbg(mmc_dev(host->mmc), "mmc_fclk: enabled\n"); + omap_hsmmc_context_restore(host); + return 0; +} + +static int omap_hsmmc_disable_fclk(struct mmc_host *mmc, int lazy) +{ + struct omap_hsmmc_host *host = mmc_priv(mmc); + + omap_hsmmc_context_save(host); + clk_disable(host->fclk); + dev_dbg(mmc_dev(host->mmc), "mmc_fclk: disabled\n"); + return 0; +} + +static const struct mmc_host_ops omap_hsmmc_ops = { + .enable = omap_hsmmc_enable_fclk, + .disable = omap_hsmmc_disable_fclk, + .request = omap_hsmmc_request, + .set_ios = omap_hsmmc_set_ios, .get_cd = omap_hsmmc_get_cd, .get_ro = omap_hsmmc_get_ro, /* NYET -- enable_sdio_irq */ }; -static int __init omap_mmc_probe(struct platform_device *pdev) +static const struct mmc_host_ops omap_hsmmc_ps_ops = { + .enable = omap_hsmmc_enable, + .disable = omap_hsmmc_disable, + .request = omap_hsmmc_request, + .set_ios = omap_hsmmc_set_ios, + .get_cd = omap_hsmmc_get_cd, + .get_ro = omap_hsmmc_get_ro, + /* NYET -- enable_sdio_irq */ +}; + +#ifdef CONFIG_DEBUG_FS + +static int omap_hsmmc_regs_show(struct seq_file *s, void *data) +{ + struct mmc_host *mmc = s->private; + struct omap_hsmmc_host *host = mmc_priv(mmc); + int context_loss = 0; + + if (host->pdata->get_context_loss_count) + context_loss = host->pdata->get_context_loss_count(host->dev); + + seq_printf(s, "mmc%d:\n" + " enabled:\t%d\n" + " dpm_state:\t%d\n" + " nesting_cnt:\t%d\n" + " ctx_loss:\t%d:%d\n" + "\nregs:\n", + mmc->index, mmc->enabled ? 1 : 0, + host->dpm_state, mmc->nesting_cnt, + host->context_loss, context_loss); + + if (host->suspended || host->dpm_state == OFF) { + seq_printf(s, "host suspended, can't read registers\n"); + return 0; + } + + if (clk_enable(host->fclk) != 0) { + seq_printf(s, "can't read the regs\n"); + return 0; + } + + seq_printf(s, "SYSCONFIG:\t0x%08x\n", + OMAP_HSMMC_READ(host->base, SYSCONFIG)); + seq_printf(s, "CON:\t\t0x%08x\n", + OMAP_HSMMC_READ(host->base, CON)); + seq_printf(s, "HCTL:\t\t0x%08x\n", + OMAP_HSMMC_READ(host->base, HCTL)); + seq_printf(s, "SYSCTL:\t\t0x%08x\n", + OMAP_HSMMC_READ(host->base, SYSCTL)); + seq_printf(s, "IE:\t\t0x%08x\n", + OMAP_HSMMC_READ(host->base, IE)); + seq_printf(s, "ISE:\t\t0x%08x\n", + OMAP_HSMMC_READ(host->base, ISE)); + seq_printf(s, "CAPA:\t\t0x%08x\n", + OMAP_HSMMC_READ(host->base, CAPA)); + + clk_disable(host->fclk); + + return 0; +} + +static int omap_hsmmc_regs_open(struct inode *inode, struct file *file) +{ + return single_open(file, omap_hsmmc_regs_show, inode->i_private); +} + +static const struct file_operations mmc_regs_fops = { + .open = omap_hsmmc_regs_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static void omap_hsmmc_debugfs(struct mmc_host *mmc) +{ + if (mmc->debugfs_root) + debugfs_create_file("regs", S_IRUSR, mmc->debugfs_root, + mmc, &mmc_regs_fops); +} + +#else + +static void omap_hsmmc_debugfs(struct mmc_host *mmc) +{ +} + +#endif + +static int __init omap_hsmmc_probe(struct platform_device *pdev) { struct omap_mmc_platform_data *pdata = pdev->dev.platform_data; struct mmc_host *mmc; - struct mmc_omap_host *host = NULL; + struct omap_hsmmc_host *host = NULL; struct resource *res; int ret = 0, irq; @@ -995,7 +1638,7 @@ static int __init omap_mmc_probe(struct platform_device *pdev) if (res == NULL) return -EBUSY; - mmc = mmc_alloc_host(sizeof(struct mmc_omap_host), &pdev->dev); + mmc = mmc_alloc_host(sizeof(struct omap_hsmmc_host), &pdev->dev); if (!mmc) { ret = -ENOMEM; goto err; @@ -1013,15 +1656,21 @@ static int __init omap_mmc_probe(struct platform_device *pdev) host->slot_id = 0; host->mapbase = res->start; host->base = ioremap(host->mapbase, SZ_4K); + host->power_mode = -1; platform_set_drvdata(pdev, host); - INIT_WORK(&host->mmc_carddetect_work, mmc_omap_detect); + INIT_WORK(&host->mmc_carddetect_work, omap_hsmmc_detect); + + if (mmc_slot(host).power_saving) + mmc->ops = &omap_hsmmc_ps_ops; + else + mmc->ops = &omap_hsmmc_ops; - mmc->ops = &mmc_omap_ops; mmc->f_min = 400000; mmc->f_max = 52000000; sema_init(&host->sem, 1); + spin_lock_init(&host->irq_lock); host->iclk = clk_get(&pdev->dev, "ick"); if (IS_ERR(host->iclk)) { @@ -1037,31 +1686,42 @@ static int __init omap_mmc_probe(struct platform_device *pdev) goto err1; } - if (clk_enable(host->fclk) != 0) { + omap_hsmmc_context_save(host); + + mmc->caps |= MMC_CAP_DISABLE; + mmc_set_disable_delay(mmc, OMAP_MMC_DISABLED_TIMEOUT); + /* we start off in DISABLED state */ + host->dpm_state = DISABLED; + + if (mmc_host_enable(host->mmc) != 0) { clk_put(host->iclk); clk_put(host->fclk); goto err1; } if (clk_enable(host->iclk) != 0) { - clk_disable(host->fclk); + mmc_host_disable(host->mmc); clk_put(host->iclk); clk_put(host->fclk); goto err1; } - host->dbclk = clk_get(&pdev->dev, "mmchsdb_fck"); - /* - * MMC can still work without debounce clock. - */ - if (IS_ERR(host->dbclk)) - dev_warn(mmc_dev(host->mmc), "Failed to get debounce clock\n"); - else - if (clk_enable(host->dbclk) != 0) - dev_dbg(mmc_dev(host->mmc), "Enabling debounce" - " clk failed\n"); + if (cpu_is_omap2430()) { + host->dbclk = clk_get(&pdev->dev, "mmchsdb_fck"); + /* + * MMC can still work without debounce clock. + */ + if (IS_ERR(host->dbclk)) + dev_warn(mmc_dev(host->mmc), + "Failed to get debounce clock\n"); else - host->dbclk_enabled = 1; + host->got_dbclk = 1; + + if (host->got_dbclk) + if (clk_enable(host->dbclk) != 0) + dev_dbg(mmc_dev(host->mmc), "Enabling debounce" + " clk failed\n"); + } /* Since we do only SG emulation, we can have as many segs * as we want. */ @@ -1073,14 +1733,18 @@ static int __init omap_mmc_probe(struct platform_device *pdev) mmc->max_req_size = mmc->max_blk_size * mmc->max_blk_count; mmc->max_seg_size = mmc->max_req_size; - mmc->caps |= MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED; + mmc->caps |= MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED | + MMC_CAP_WAIT_WHILE_BUSY; - if (pdata->slots[host->slot_id].wires >= 8) + if (mmc_slot(host).wires >= 8) mmc->caps |= MMC_CAP_8_BIT_DATA; - else if (pdata->slots[host->slot_id].wires >= 4) + else if (mmc_slot(host).wires >= 4) mmc->caps |= MMC_CAP_4_BIT_DATA; - omap_hsmmc_init(host); + if (mmc_slot(host).nonremovable) + mmc->caps |= MMC_CAP_NONREMOVABLE; + + omap_hsmmc_conf_bus_power(host); /* Select DMA lines */ switch (host->id) { @@ -1096,13 +1760,21 @@ static int __init omap_mmc_probe(struct platform_device *pdev) host->dma_line_tx = OMAP34XX_DMA_MMC3_TX; host->dma_line_rx = OMAP34XX_DMA_MMC3_RX; break; + case OMAP_MMC4_DEVID: + host->dma_line_tx = OMAP44XX_DMA_MMC4_TX; + host->dma_line_rx = OMAP44XX_DMA_MMC4_RX; + break; + case OMAP_MMC5_DEVID: + host->dma_line_tx = OMAP44XX_DMA_MMC5_TX; + host->dma_line_rx = OMAP44XX_DMA_MMC5_RX; + break; default: dev_err(mmc_dev(host->mmc), "Invalid MMC id\n"); goto err_irq; } /* Request IRQ for MMC operations */ - ret = request_irq(host->irq, mmc_omap_irq, IRQF_DISABLED, + ret = request_irq(host->irq, omap_hsmmc_irq, IRQF_DISABLED, mmc_hostname(mmc), host); if (ret) { dev_dbg(mmc_dev(host->mmc), "Unable to grab HSMMC IRQ\n"); @@ -1112,7 +1784,8 @@ static int __init omap_mmc_probe(struct platform_device *pdev) /* initialize power supplies, gpios, etc */ if (pdata->init != NULL) { if (pdata->init(&pdev->dev) != 0) { - dev_dbg(mmc_dev(host->mmc), "late init error\n"); + dev_dbg(mmc_dev(host->mmc), + "Unable to configure MMC IRQs\n"); goto err_irq_cd_init; } } @@ -1121,7 +1794,7 @@ static int __init omap_mmc_probe(struct platform_device *pdev) /* Request IRQ for card detect */ if ((mmc_slot(host).card_detect_irq)) { ret = request_irq(mmc_slot(host).card_detect_irq, - omap_mmc_cd_handler, + omap_hsmmc_cd_handler, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_DISABLED, mmc_hostname(mmc), host); @@ -1135,21 +1808,26 @@ static int __init omap_mmc_probe(struct platform_device *pdev) OMAP_HSMMC_WRITE(host->base, ISE, INT_EN_MASK); OMAP_HSMMC_WRITE(host->base, IE, INT_EN_MASK); + mmc_host_lazy_disable(host->mmc); + + omap_hsmmc_protect_card(host); + mmc_add_host(mmc); - if (host->pdata->slots[host->slot_id].name != NULL) { + if (mmc_slot(host).name != NULL) { ret = device_create_file(&mmc->class_dev, &dev_attr_slot_name); if (ret < 0) goto err_slot_name; } - if (mmc_slot(host).card_detect_irq && - host->pdata->slots[host->slot_id].get_cover_state) { + if (mmc_slot(host).card_detect_irq && mmc_slot(host).get_cover_state) { ret = device_create_file(&mmc->class_dev, &dev_attr_cover_switch); if (ret < 0) goto err_cover_switch; } + omap_hsmmc_debugfs(mmc); + return 0; err_cover_switch: @@ -1161,11 +1839,11 @@ err_irq_cd: err_irq_cd_init: free_irq(host->irq, host); err_irq: - clk_disable(host->fclk); + mmc_host_disable(host->mmc); clk_disable(host->iclk); clk_put(host->fclk); clk_put(host->iclk); - if (host->dbclk_enabled) { + if (host->got_dbclk) { clk_disable(host->dbclk); clk_put(host->dbclk); } @@ -1180,12 +1858,13 @@ err: return ret; } -static int omap_mmc_remove(struct platform_device *pdev) +static int omap_hsmmc_remove(struct platform_device *pdev) { - struct mmc_omap_host *host = platform_get_drvdata(pdev); + struct omap_hsmmc_host *host = platform_get_drvdata(pdev); struct resource *res; if (host) { + mmc_host_enable(host->mmc); mmc_remove_host(host->mmc); if (host->pdata->cleanup) host->pdata->cleanup(&pdev->dev); @@ -1194,11 +1873,11 @@ static int omap_mmc_remove(struct platform_device *pdev) free_irq(mmc_slot(host).card_detect_irq, host); flush_scheduled_work(); - clk_disable(host->fclk); + mmc_host_disable(host->mmc); clk_disable(host->iclk); clk_put(host->fclk); clk_put(host->iclk); - if (host->dbclk_enabled) { + if (host->got_dbclk) { clk_disable(host->dbclk); clk_put(host->dbclk); } @@ -1216,36 +1895,51 @@ static int omap_mmc_remove(struct platform_device *pdev) } #ifdef CONFIG_PM -static int omap_mmc_suspend(struct platform_device *pdev, pm_message_t state) +static int omap_hsmmc_suspend(struct platform_device *pdev, pm_message_t state) { int ret = 0; - struct mmc_omap_host *host = platform_get_drvdata(pdev); + struct omap_hsmmc_host *host = platform_get_drvdata(pdev); if (host && host->suspended) return 0; if (host) { + host->suspended = 1; + if (host->pdata->suspend) { + ret = host->pdata->suspend(&pdev->dev, + host->slot_id); + if (ret) { + dev_dbg(mmc_dev(host->mmc), + "Unable to handle MMC board" + " level suspend\n"); + host->suspended = 0; + return ret; + } + } + cancel_work_sync(&host->mmc_carddetect_work); + mmc_host_enable(host->mmc); ret = mmc_suspend_host(host->mmc, state); if (ret == 0) { - host->suspended = 1; - OMAP_HSMMC_WRITE(host->base, ISE, 0); OMAP_HSMMC_WRITE(host->base, IE, 0); - if (host->pdata->suspend) { - ret = host->pdata->suspend(&pdev->dev, - host->slot_id); - if (ret) - dev_dbg(mmc_dev(host->mmc), - "Unable to handle MMC board" - " level suspend\n"); - } OMAP_HSMMC_WRITE(host->base, HCTL, - OMAP_HSMMC_READ(host->base, HCTL) & ~SDBP); - clk_disable(host->fclk); + OMAP_HSMMC_READ(host->base, HCTL) & ~SDBP); + mmc_host_disable(host->mmc); clk_disable(host->iclk); - clk_disable(host->dbclk); + if (host->got_dbclk) + clk_disable(host->dbclk); + } else { + host->suspended = 0; + if (host->pdata->resume) { + ret = host->pdata->resume(&pdev->dev, + host->slot_id); + if (ret) + dev_dbg(mmc_dev(host->mmc), + "Unmask interrupt failed\n"); + } + mmc_host_disable(host->mmc); } } @@ -1253,32 +1947,28 @@ static int omap_mmc_suspend(struct platform_device *pdev, pm_message_t state) } /* Routine to resume the MMC device */ -static int omap_mmc_resume(struct platform_device *pdev) +static int omap_hsmmc_resume(struct platform_device *pdev) { int ret = 0; - struct mmc_omap_host *host = platform_get_drvdata(pdev); + struct omap_hsmmc_host *host = platform_get_drvdata(pdev); if (host && !host->suspended) return 0; if (host) { - - ret = clk_enable(host->fclk); + ret = clk_enable(host->iclk); if (ret) goto clk_en_err; - ret = clk_enable(host->iclk); - if (ret) { - clk_disable(host->fclk); - clk_put(host->fclk); + if (mmc_host_enable(host->mmc) != 0) { + clk_disable(host->iclk); goto clk_en_err; } - if (clk_enable(host->dbclk) != 0) - dev_dbg(mmc_dev(host->mmc), - "Enabling debounce clk failed\n"); + if (host->got_dbclk) + clk_enable(host->dbclk); - omap_hsmmc_init(host); + omap_hsmmc_conf_bus_power(host); if (host->pdata->resume) { ret = host->pdata->resume(&pdev->dev, host->slot_id); @@ -1287,10 +1977,14 @@ static int omap_mmc_resume(struct platform_device *pdev) "Unmask interrupt failed\n"); } + omap_hsmmc_protect_card(host); + /* Notify the core to resume the host */ ret = mmc_resume_host(host->mmc); if (ret == 0) host->suspended = 0; + + mmc_host_lazy_disable(host->mmc); } return ret; @@ -1302,35 +1996,34 @@ clk_en_err: } #else -#define omap_mmc_suspend NULL -#define omap_mmc_resume NULL +#define omap_hsmmc_suspend NULL +#define omap_hsmmc_resume NULL #endif -static struct platform_driver omap_mmc_driver = { - .probe = omap_mmc_probe, - .remove = omap_mmc_remove, - .suspend = omap_mmc_suspend, - .resume = omap_mmc_resume, +static struct platform_driver omap_hsmmc_driver = { + .remove = omap_hsmmc_remove, + .suspend = omap_hsmmc_suspend, + .resume = omap_hsmmc_resume, .driver = { .name = DRIVER_NAME, .owner = THIS_MODULE, }, }; -static int __init omap_mmc_init(void) +static int __init omap_hsmmc_init(void) { /* Register the MMC driver */ - return platform_driver_register(&omap_mmc_driver); + return platform_driver_probe(&omap_hsmmc_driver, omap_hsmmc_probe); } -static void __exit omap_mmc_cleanup(void) +static void __exit omap_hsmmc_cleanup(void) { /* Unregister MMC driver */ - platform_driver_unregister(&omap_mmc_driver); + platform_driver_unregister(&omap_hsmmc_driver); } -module_init(omap_mmc_init); -module_exit(omap_mmc_cleanup); +module_init(omap_hsmmc_init); +module_exit(omap_hsmmc_cleanup); MODULE_DESCRIPTION("OMAP High Speed Multimedia Card driver"); MODULE_LICENSE("GPL"); diff --git a/drivers/mmc/host/pxamci.c b/drivers/mmc/host/pxamci.c index e55ac792d68c..b00d67319058 100644 --- a/drivers/mmc/host/pxamci.c +++ b/drivers/mmc/host/pxamci.c @@ -28,6 +28,7 @@ #include <linux/mmc/host.h> #include <linux/io.h> #include <linux/regulator/consumer.h> +#include <linux/gpio.h> #include <asm/sizes.h> @@ -96,10 +97,18 @@ static inline void pxamci_init_ocr(struct pxamci_host *host) static inline void pxamci_set_power(struct pxamci_host *host, unsigned int vdd) { + int on; + #ifdef CONFIG_REGULATOR if (host->vcc) mmc_regulator_set_ocr(host->vcc, vdd); #endif + if (!host->vcc && host->pdata && + gpio_is_valid(host->pdata->gpio_power)) { + on = ((1 << vdd) & host->pdata->ocr_mask); + gpio_set_value(host->pdata->gpio_power, + !!on ^ host->pdata->gpio_power_invert); + } if (!host->vcc && host->pdata && host->pdata->setpower) host->pdata->setpower(mmc_dev(host->mmc), vdd); } @@ -421,6 +430,12 @@ static int pxamci_get_ro(struct mmc_host *mmc) { struct pxamci_host *host = mmc_priv(mmc); + if (host->pdata && gpio_is_valid(host->pdata->gpio_card_ro)) { + if (host->pdata->gpio_card_ro_invert) + return !gpio_get_value(host->pdata->gpio_card_ro); + else + return gpio_get_value(host->pdata->gpio_card_ro); + } if (host->pdata && host->pdata->get_ro) return !!host->pdata->get_ro(mmc_dev(mmc)); /* @@ -534,7 +549,7 @@ static int pxamci_probe(struct platform_device *pdev) struct mmc_host *mmc; struct pxamci_host *host = NULL; struct resource *r, *dmarx, *dmatx; - int ret, irq; + int ret, irq, gpio_cd = -1, gpio_ro = -1, gpio_power = -1; r = platform_get_resource(pdev, IORESOURCE_MEM, 0); irq = platform_get_irq(pdev, 0); @@ -661,13 +676,63 @@ static int pxamci_probe(struct platform_device *pdev) } host->dma_drcmrtx = dmatx->start; + if (host->pdata) { + gpio_cd = host->pdata->gpio_card_detect; + gpio_ro = host->pdata->gpio_card_ro; + gpio_power = host->pdata->gpio_power; + } + if (gpio_is_valid(gpio_power)) { + ret = gpio_request(gpio_power, "mmc card power"); + if (ret) { + dev_err(&pdev->dev, "Failed requesting gpio_power %d\n", gpio_power); + goto out; + } + gpio_direction_output(gpio_power, + host->pdata->gpio_power_invert); + } + if (gpio_is_valid(gpio_ro)) { + ret = gpio_request(gpio_ro, "mmc card read only"); + if (ret) { + dev_err(&pdev->dev, "Failed requesting gpio_ro %d\n", gpio_ro); + goto err_gpio_ro; + } + gpio_direction_input(gpio_ro); + } + if (gpio_is_valid(gpio_cd)) { + ret = gpio_request(gpio_cd, "mmc card detect"); + if (ret) { + dev_err(&pdev->dev, "Failed requesting gpio_cd %d\n", gpio_cd); + goto err_gpio_cd; + } + gpio_direction_input(gpio_cd); + + ret = request_irq(gpio_to_irq(gpio_cd), pxamci_detect_irq, + IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, + "mmc card detect", mmc); + if (ret) { + dev_err(&pdev->dev, "failed to request card detect IRQ\n"); + goto err_request_irq; + } + } + if (host->pdata && host->pdata->init) host->pdata->init(&pdev->dev, pxamci_detect_irq, mmc); + if (gpio_is_valid(gpio_power) && host->pdata->setpower) + dev_warn(&pdev->dev, "gpio_power and setpower() both defined\n"); + if (gpio_is_valid(gpio_ro) && host->pdata->get_ro) + dev_warn(&pdev->dev, "gpio_ro and get_ro() both defined\n"); + mmc_add_host(mmc); return 0; +err_request_irq: + gpio_free(gpio_cd); +err_gpio_cd: + gpio_free(gpio_ro); +err_gpio_ro: + gpio_free(gpio_power); out: if (host) { if (host->dma >= 0) @@ -688,12 +753,26 @@ static int pxamci_probe(struct platform_device *pdev) static int pxamci_remove(struct platform_device *pdev) { struct mmc_host *mmc = platform_get_drvdata(pdev); + int gpio_cd = -1, gpio_ro = -1, gpio_power = -1; platform_set_drvdata(pdev, NULL); if (mmc) { struct pxamci_host *host = mmc_priv(mmc); + if (host->pdata) { + gpio_cd = host->pdata->gpio_card_detect; + gpio_ro = host->pdata->gpio_card_ro; + gpio_power = host->pdata->gpio_power; + } + if (gpio_is_valid(gpio_cd)) { + free_irq(gpio_to_irq(gpio_cd), mmc); + gpio_free(gpio_cd); + } + if (gpio_is_valid(gpio_ro)) + gpio_free(gpio_ro); + if (gpio_is_valid(gpio_power)) + gpio_free(gpio_power); if (host->vcc) regulator_put(host->vcc); @@ -725,20 +804,20 @@ static int pxamci_remove(struct platform_device *pdev) } #ifdef CONFIG_PM -static int pxamci_suspend(struct platform_device *dev, pm_message_t state) +static int pxamci_suspend(struct device *dev) { - struct mmc_host *mmc = platform_get_drvdata(dev); + struct mmc_host *mmc = dev_get_drvdata(dev); int ret = 0; if (mmc) - ret = mmc_suspend_host(mmc, state); + ret = mmc_suspend_host(mmc, PMSG_SUSPEND); return ret; } -static int pxamci_resume(struct platform_device *dev) +static int pxamci_resume(struct device *dev) { - struct mmc_host *mmc = platform_get_drvdata(dev); + struct mmc_host *mmc = dev_get_drvdata(dev); int ret = 0; if (mmc) @@ -746,19 +825,22 @@ static int pxamci_resume(struct platform_device *dev) return ret; } -#else -#define pxamci_suspend NULL -#define pxamci_resume NULL + +static struct dev_pm_ops pxamci_pm_ops = { + .suspend = pxamci_suspend, + .resume = pxamci_resume, +}; #endif static struct platform_driver pxamci_driver = { .probe = pxamci_probe, .remove = pxamci_remove, - .suspend = pxamci_suspend, - .resume = pxamci_resume, .driver = { .name = DRIVER_NAME, .owner = THIS_MODULE, +#ifdef CONFIG_PM + .pm = &pxamci_pm_ops, +#endif }, }; diff --git a/drivers/mmc/host/s3cmci.c b/drivers/mmc/host/s3cmci.c index 8c08cd7efa7f..99b74a351020 100644 --- a/drivers/mmc/host/s3cmci.c +++ b/drivers/mmc/host/s3cmci.c @@ -17,6 +17,8 @@ #include <linux/mmc/host.h> #include <linux/platform_device.h> #include <linux/cpufreq.h> +#include <linux/debugfs.h> +#include <linux/seq_file.h> #include <linux/gpio.h> #include <linux/irq.h> #include <linux/io.h> @@ -58,8 +60,6 @@ static const int dbgmap_debug = dbg_err | dbg_debug; dev_dbg(&host->pdev->dev, args); \ } while (0) -#define RESSIZE(ressource) (((ressource)->end - (ressource)->start)+1) - static struct s3c2410_dma_client s3cmci_dma_client = { .name = "s3c-mci", }; @@ -164,6 +164,40 @@ static void dbg_dumpregs(struct s3cmci_host *host, char *prefix) { } #endif /* CONFIG_MMC_DEBUG */ +/** + * s3cmci_host_usedma - return whether the host is using dma or pio + * @host: The host state + * + * Return true if the host is using DMA to transfer data, else false + * to use PIO mode. Will return static data depending on the driver + * configuration. + */ +static inline bool s3cmci_host_usedma(struct s3cmci_host *host) +{ +#ifdef CONFIG_MMC_S3C_PIO + return false; +#elif defined(CONFIG_MMC_S3C_DMA) + return true; +#else + return host->dodma; +#endif +} + +/** + * s3cmci_host_canpio - return true if host has pio code available + * + * Return true if the driver has been compiled with the PIO support code + * available. + */ +static inline bool s3cmci_host_canpio(void) +{ +#ifdef CONFIG_MMC_S3C_PIO + return true; +#else + return false; +#endif +} + static inline u32 enable_imask(struct s3cmci_host *host, u32 imask) { u32 newmask; @@ -190,7 +224,33 @@ static inline u32 disable_imask(struct s3cmci_host *host, u32 imask) static inline void clear_imask(struct s3cmci_host *host) { - writel(0, host->base + host->sdiimsk); + u32 mask = readl(host->base + host->sdiimsk); + + /* preserve the SDIO IRQ mask state */ + mask &= S3C2410_SDIIMSK_SDIOIRQ; + writel(mask, host->base + host->sdiimsk); +} + +/** + * s3cmci_check_sdio_irq - test whether the SDIO IRQ is being signalled + * @host: The host to check. + * + * Test to see if the SDIO interrupt is being signalled in case the + * controller has failed to re-detect a card interrupt. Read GPE8 and + * see if it is low and if so, signal a SDIO interrupt. + * + * This is currently called if a request is finished (we assume that the + * bus is now idle) and when the SDIO IRQ is enabled in case the IRQ is + * already being indicated. +*/ +static void s3cmci_check_sdio_irq(struct s3cmci_host *host) +{ + if (host->sdio_irqen) { + if (gpio_get_value(S3C2410_GPE(8)) == 0) { + printk(KERN_DEBUG "%s: signalling irq\n", __func__); + mmc_signal_sdio_irq(host->mmc); + } + } } static inline int get_data_buffer(struct s3cmci_host *host, @@ -238,6 +298,64 @@ static inline u32 fifo_free(struct s3cmci_host *host) return 63 - fifostat; } +/** + * s3cmci_enable_irq - enable IRQ, after having disabled it. + * @host: The device state. + * @more: True if more IRQs are expected from transfer. + * + * Enable the main IRQ if needed after it has been disabled. + * + * The IRQ can be one of the following states: + * - disabled during IDLE + * - disabled whilst processing data + * - enabled during transfer + * - enabled whilst awaiting SDIO interrupt detection + */ +static void s3cmci_enable_irq(struct s3cmci_host *host, bool more) +{ + unsigned long flags; + bool enable = false; + + local_irq_save(flags); + + host->irq_enabled = more; + host->irq_disabled = false; + + enable = more | host->sdio_irqen; + + if (host->irq_state != enable) { + host->irq_state = enable; + + if (enable) + enable_irq(host->irq); + else + disable_irq(host->irq); + } + + local_irq_restore(flags); +} + +/** + * + */ +static void s3cmci_disable_irq(struct s3cmci_host *host, bool transfer) +{ + unsigned long flags; + + local_irq_save(flags); + + //printk(KERN_DEBUG "%s: transfer %d\n", __func__, transfer); + + host->irq_disabled = transfer; + + if (transfer && host->irq_state) { + host->irq_state = false; + disable_irq(host->irq); + } + + local_irq_restore(flags); +} + static void do_pio_read(struct s3cmci_host *host) { int res; @@ -374,8 +492,7 @@ static void pio_tasklet(unsigned long data) { struct s3cmci_host *host = (struct s3cmci_host *) data; - - disable_irq(host->irq); + s3cmci_disable_irq(host, true); if (host->pio_active == XFER_WRITE) do_pio_write(host); @@ -395,9 +512,10 @@ static void pio_tasklet(unsigned long data) host->mrq->data->error = -EINVAL; } + s3cmci_enable_irq(host, false); finalize_request(host); } else - enable_irq(host->irq); + s3cmci_enable_irq(host, true); } /* @@ -432,17 +550,27 @@ static irqreturn_t s3cmci_irq(int irq, void *dev_id) struct s3cmci_host *host = dev_id; struct mmc_command *cmd; u32 mci_csta, mci_dsta, mci_fsta, mci_dcnt, mci_imsk; - u32 mci_cclear, mci_dclear; + u32 mci_cclear = 0, mci_dclear; unsigned long iflags; + mci_dsta = readl(host->base + S3C2410_SDIDSTA); + mci_imsk = readl(host->base + host->sdiimsk); + + if (mci_dsta & S3C2410_SDIDSTA_SDIOIRQDETECT) { + if (mci_imsk & S3C2410_SDIIMSK_SDIOIRQ) { + mci_dclear = S3C2410_SDIDSTA_SDIOIRQDETECT; + writel(mci_dclear, host->base + S3C2410_SDIDSTA); + + mmc_signal_sdio_irq(host->mmc); + return IRQ_HANDLED; + } + } + spin_lock_irqsave(&host->complete_lock, iflags); mci_csta = readl(host->base + S3C2410_SDICMDSTAT); - mci_dsta = readl(host->base + S3C2410_SDIDSTA); mci_dcnt = readl(host->base + S3C2410_SDIDCNT); mci_fsta = readl(host->base + S3C2410_SDIFSTA); - mci_imsk = readl(host->base + host->sdiimsk); - mci_cclear = 0; mci_dclear = 0; if ((host->complete_what == COMPLETION_NONE) || @@ -466,7 +594,7 @@ static irqreturn_t s3cmci_irq(int irq, void *dev_id) goto irq_out; } - if (!host->dodma) { + if (!s3cmci_host_usedma(host)) { if ((host->pio_active == XFER_WRITE) && (mci_fsta & S3C2410_SDIFSTA_TFDET)) { @@ -673,6 +801,7 @@ static void s3cmci_dma_done_callback(struct s3c2410_dma_chan *dma_ch, dbg(host, dbg_dma, "DMA FINISHED Size:%i DSTA:%08x DCNT:%08x\n", size, mci_dsta, mci_dcnt); + host->dma_complete = 1; host->complete_what = COMPLETION_FINALIZE; out: @@ -683,9 +812,9 @@ out: fail_request: host->mrq->data->error = -EINVAL; host->complete_what = COMPLETION_FINALIZE; - writel(0, host->base + host->sdiimsk); - goto out; + clear_imask(host); + goto out; } static void finalize_request(struct s3cmci_host *host) @@ -702,8 +831,9 @@ static void finalize_request(struct s3cmci_host *host) if (cmd->data && (cmd->error == 0) && (cmd->data->error == 0)) { - if (host->dodma && (!host->dma_complete)) { - dbg(host, dbg_dma, "DMA Missing!\n"); + if (s3cmci_host_usedma(host) && (!host->dma_complete)) { + dbg(host, dbg_dma, "DMA Missing (%d)!\n", + host->dma_complete); return; } } @@ -728,7 +858,7 @@ static void finalize_request(struct s3cmci_host *host) writel(0, host->base + S3C2410_SDICMDARG); writel(S3C2410_SDIDCON_STOP, host->base + S3C2410_SDIDCON); writel(0, host->base + S3C2410_SDICMDCON); - writel(0, host->base + host->sdiimsk); + clear_imask(host); if (cmd->data && cmd->error) cmd->data->error = cmd->error; @@ -754,7 +884,7 @@ static void finalize_request(struct s3cmci_host *host) /* If we had an error while transfering data we flush the * DMA channel and the fifo to clear out any garbage. */ if (mrq->data->error != 0) { - if (host->dodma) + if (s3cmci_host_usedma(host)) s3c2410_dma_ctrl(host->dma, S3C2410_DMAOP_FLUSH); if (host->is2440) { @@ -776,6 +906,8 @@ static void finalize_request(struct s3cmci_host *host) request_done: host->complete_what = COMPLETION_NONE; host->mrq = NULL; + + s3cmci_check_sdio_irq(host); mmc_request_done(host->mmc, mrq); } @@ -872,7 +1004,7 @@ static int s3cmci_setup_data(struct s3cmci_host *host, struct mmc_data *data) dcon = data->blocks & S3C2410_SDIDCON_BLKNUM_MASK; - if (host->dodma) + if (s3cmci_host_usedma(host)) dcon |= S3C2410_SDIDCON_DMAEN; if (host->bus_width == MMC_BUS_WIDTH_4) @@ -950,7 +1082,7 @@ static int s3cmci_prepare_pio(struct s3cmci_host *host, struct mmc_data *data) static int s3cmci_prepare_dma(struct s3cmci_host *host, struct mmc_data *data) { int dma_len, i; - int rw = (data->flags & MMC_DATA_WRITE) ? 1 : 0; + int rw = data->flags & MMC_DATA_WRITE; BUG_ON((data->flags & BOTH_DIR) == BOTH_DIR); @@ -958,7 +1090,7 @@ static int s3cmci_prepare_dma(struct s3cmci_host *host, struct mmc_data *data) s3c2410_dma_ctrl(host->dma, S3C2410_DMAOP_FLUSH); dma_len = dma_map_sg(mmc_dev(host->mmc), data->sg, data->sg_len, - (rw) ? DMA_TO_DEVICE : DMA_FROM_DEVICE); + rw ? DMA_TO_DEVICE : DMA_FROM_DEVICE); if (dma_len == 0) return -ENOMEM; @@ -969,11 +1101,11 @@ static int s3cmci_prepare_dma(struct s3cmci_host *host, struct mmc_data *data) for (i = 0; i < dma_len; i++) { int res; - dbg(host, dbg_dma, "enqueue %i:%u@%u\n", i, - sg_dma_address(&data->sg[i]), - sg_dma_len(&data->sg[i])); + dbg(host, dbg_dma, "enqueue %i: %08x@%u\n", i, + sg_dma_address(&data->sg[i]), + sg_dma_len(&data->sg[i])); - res = s3c2410_dma_enqueue(host->dma, (void *) host, + res = s3c2410_dma_enqueue(host->dma, host, sg_dma_address(&data->sg[i]), sg_dma_len(&data->sg[i])); @@ -1018,7 +1150,7 @@ static void s3cmci_send_request(struct mmc_host *mmc) return; } - if (host->dodma) + if (s3cmci_host_usedma(host)) res = s3cmci_prepare_dma(host, cmd->data); else res = s3cmci_prepare_pio(host, cmd->data); @@ -1037,7 +1169,7 @@ static void s3cmci_send_request(struct mmc_host *mmc) s3cmci_send_command(host, cmd); /* Enable Interrupt */ - enable_irq(host->irq); + s3cmci_enable_irq(host, true); } static int s3cmci_card_present(struct mmc_host *mmc) @@ -1049,7 +1181,7 @@ static int s3cmci_card_present(struct mmc_host *mmc) if (pdata->gpio_detect == 0) return -ENOSYS; - ret = s3c2410_gpio_getpin(pdata->gpio_detect) ? 0 : 1; + ret = gpio_get_value(pdata->gpio_detect) ? 0 : 1; return ret ^ pdata->detect_invert; } @@ -1104,12 +1236,12 @@ static void s3cmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) switch (ios->power_mode) { case MMC_POWER_ON: case MMC_POWER_UP: - s3c2410_gpio_cfgpin(S3C2410_GPE5, S3C2410_GPE5_SDCLK); - s3c2410_gpio_cfgpin(S3C2410_GPE6, S3C2410_GPE6_SDCMD); - s3c2410_gpio_cfgpin(S3C2410_GPE7, S3C2410_GPE7_SDDAT0); - s3c2410_gpio_cfgpin(S3C2410_GPE8, S3C2410_GPE8_SDDAT1); - s3c2410_gpio_cfgpin(S3C2410_GPE9, S3C2410_GPE9_SDDAT2); - s3c2410_gpio_cfgpin(S3C2410_GPE10, S3C2410_GPE10_SDDAT3); + s3c2410_gpio_cfgpin(S3C2410_GPE(5), S3C2410_GPE5_SDCLK); + s3c2410_gpio_cfgpin(S3C2410_GPE(6), S3C2410_GPE6_SDCMD); + s3c2410_gpio_cfgpin(S3C2410_GPE(7), S3C2410_GPE7_SDDAT0); + s3c2410_gpio_cfgpin(S3C2410_GPE(8), S3C2410_GPE8_SDDAT1); + s3c2410_gpio_cfgpin(S3C2410_GPE(9), S3C2410_GPE9_SDDAT2); + s3c2410_gpio_cfgpin(S3C2410_GPE(10), S3C2410_GPE10_SDDAT3); if (host->pdata->set_power) host->pdata->set_power(ios->power_mode, ios->vdd); @@ -1121,8 +1253,7 @@ static void s3cmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) case MMC_POWER_OFF: default: - s3c2410_gpio_setpin(S3C2410_GPE5, 0); - s3c2410_gpio_cfgpin(S3C2410_GPE5, S3C2410_GPIO_OUTPUT); + gpio_direction_output(S3C2410_GPE(5), 0); if (host->is2440) mci_con |= S3C2440_SDICON_SDRESET; @@ -1168,7 +1299,7 @@ static int s3cmci_get_ro(struct mmc_host *mmc) struct s3c24xx_mci_pdata *pdata = host->pdata; int ret; - if (pdata->gpio_wprotect == 0) + if (pdata->no_wprotect) return 0; ret = s3c2410_gpio_getpin(pdata->gpio_wprotect); @@ -1179,11 +1310,52 @@ static int s3cmci_get_ro(struct mmc_host *mmc) return ret; } +static void s3cmci_enable_sdio_irq(struct mmc_host *mmc, int enable) +{ + struct s3cmci_host *host = mmc_priv(mmc); + unsigned long flags; + u32 con; + + local_irq_save(flags); + + con = readl(host->base + S3C2410_SDICON); + host->sdio_irqen = enable; + + if (enable == host->sdio_irqen) + goto same_state; + + if (enable) { + con |= S3C2410_SDICON_SDIOIRQ; + enable_imask(host, S3C2410_SDIIMSK_SDIOIRQ); + + if (!host->irq_state && !host->irq_disabled) { + host->irq_state = true; + enable_irq(host->irq); + } + } else { + disable_imask(host, S3C2410_SDIIMSK_SDIOIRQ); + con &= ~S3C2410_SDICON_SDIOIRQ; + + if (!host->irq_enabled && host->irq_state) { + disable_irq_nosync(host->irq); + host->irq_state = false; + } + } + + writel(con, host->base + S3C2410_SDICON); + + same_state: + local_irq_restore(flags); + + s3cmci_check_sdio_irq(host); +} + static struct mmc_host_ops s3cmci_ops = { .request = s3cmci_request, .set_ios = s3cmci_set_ios, .get_ro = s3cmci_get_ro, .get_cd = s3cmci_card_present, + .enable_sdio_irq = s3cmci_enable_sdio_irq, }; static struct s3c24xx_mci_pdata s3cmci_def_pdata = { @@ -1246,11 +1418,140 @@ static inline void s3cmci_cpufreq_deregister(struct s3cmci_host *host) } #endif -static int __devinit s3cmci_probe(struct platform_device *pdev, int is2440) + +#ifdef CONFIG_DEBUG_FS + +static int s3cmci_state_show(struct seq_file *seq, void *v) +{ + struct s3cmci_host *host = seq->private; + + seq_printf(seq, "Register base = 0x%08x\n", (u32)host->base); + seq_printf(seq, "Clock rate = %ld\n", host->clk_rate); + seq_printf(seq, "Prescale = %d\n", host->prescaler); + seq_printf(seq, "is2440 = %d\n", host->is2440); + seq_printf(seq, "IRQ = %d\n", host->irq); + seq_printf(seq, "IRQ enabled = %d\n", host->irq_enabled); + seq_printf(seq, "IRQ disabled = %d\n", host->irq_disabled); + seq_printf(seq, "IRQ state = %d\n", host->irq_state); + seq_printf(seq, "CD IRQ = %d\n", host->irq_cd); + seq_printf(seq, "Do DMA = %d\n", s3cmci_host_usedma(host)); + seq_printf(seq, "SDIIMSK at %d\n", host->sdiimsk); + seq_printf(seq, "SDIDATA at %d\n", host->sdidata); + + return 0; +} + +static int s3cmci_state_open(struct inode *inode, struct file *file) +{ + return single_open(file, s3cmci_state_show, inode->i_private); +} + +static const struct file_operations s3cmci_fops_state = { + .owner = THIS_MODULE, + .open = s3cmci_state_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +#define DBG_REG(_r) { .addr = S3C2410_SDI##_r, .name = #_r } + +struct s3cmci_reg { + unsigned short addr; + unsigned char *name; +} debug_regs[] = { + DBG_REG(CON), + DBG_REG(PRE), + DBG_REG(CMDARG), + DBG_REG(CMDCON), + DBG_REG(CMDSTAT), + DBG_REG(RSP0), + DBG_REG(RSP1), + DBG_REG(RSP2), + DBG_REG(RSP3), + DBG_REG(TIMER), + DBG_REG(BSIZE), + DBG_REG(DCON), + DBG_REG(DCNT), + DBG_REG(DSTA), + DBG_REG(FSTA), + {} +}; + +static int s3cmci_regs_show(struct seq_file *seq, void *v) +{ + struct s3cmci_host *host = seq->private; + struct s3cmci_reg *rptr = debug_regs; + + for (; rptr->name; rptr++) + seq_printf(seq, "SDI%s\t=0x%08x\n", rptr->name, + readl(host->base + rptr->addr)); + + seq_printf(seq, "SDIIMSK\t=0x%08x\n", readl(host->base + host->sdiimsk)); + + return 0; +} + +static int s3cmci_regs_open(struct inode *inode, struct file *file) +{ + return single_open(file, s3cmci_regs_show, inode->i_private); +} + +static const struct file_operations s3cmci_fops_regs = { + .owner = THIS_MODULE, + .open = s3cmci_regs_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static void s3cmci_debugfs_attach(struct s3cmci_host *host) +{ + struct device *dev = &host->pdev->dev; + + host->debug_root = debugfs_create_dir(dev_name(dev), NULL); + if (IS_ERR(host->debug_root)) { + dev_err(dev, "failed to create debugfs root\n"); + return; + } + + host->debug_state = debugfs_create_file("state", 0444, + host->debug_root, host, + &s3cmci_fops_state); + + if (IS_ERR(host->debug_state)) + dev_err(dev, "failed to create debug state file\n"); + + host->debug_regs = debugfs_create_file("regs", 0444, + host->debug_root, host, + &s3cmci_fops_regs); + + if (IS_ERR(host->debug_regs)) + dev_err(dev, "failed to create debug regs file\n"); +} + +static void s3cmci_debugfs_remove(struct s3cmci_host *host) +{ + debugfs_remove(host->debug_regs); + debugfs_remove(host->debug_state); + debugfs_remove(host->debug_root); +} + +#else +static inline void s3cmci_debugfs_attach(struct s3cmci_host *host) { } +static inline void s3cmci_debugfs_remove(struct s3cmci_host *host) { } + +#endif /* CONFIG_DEBUG_FS */ + +static int __devinit s3cmci_probe(struct platform_device *pdev) { struct s3cmci_host *host; struct mmc_host *mmc; int ret; + int is2440; + int i; + + is2440 = platform_get_device_id(pdev)->driver_data; mmc = mmc_alloc_host(sizeof(struct s3cmci_host), &pdev->dev); if (!mmc) { @@ -1258,6 +1559,18 @@ static int __devinit s3cmci_probe(struct platform_device *pdev, int is2440) goto probe_out; } + for (i = S3C2410_GPE(5); i <= S3C2410_GPE(10); i++) { + ret = gpio_request(i, dev_name(&pdev->dev)); + if (ret) { + dev_err(&pdev->dev, "failed to get gpio %d\n", i); + + for (i--; i >= S3C2410_GPE(5); i--) + gpio_free(i); + + goto probe_free_host; + } + } + host = mmc_priv(mmc); host->mmc = mmc; host->pdev = pdev; @@ -1282,11 +1595,12 @@ static int __devinit s3cmci_probe(struct platform_device *pdev, int is2440) host->clk_div = 2; } - host->dodma = 0; host->complete_what = COMPLETION_NONE; host->pio_active = XFER_NONE; - host->dma = S3CMCI_DMA; +#ifdef CONFIG_MMC_S3C_PIODMA + host->dodma = host->pdata->dma; +#endif host->mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!host->mem) { @@ -1294,19 +1608,19 @@ static int __devinit s3cmci_probe(struct platform_device *pdev, int is2440) "failed to get io memory region resouce.\n"); ret = -ENOENT; - goto probe_free_host; + goto probe_free_gpio; } host->mem = request_mem_region(host->mem->start, - RESSIZE(host->mem), pdev->name); + resource_size(host->mem), pdev->name); if (!host->mem) { dev_err(&pdev->dev, "failed to request io memory region.\n"); ret = -ENOENT; - goto probe_free_host; + goto probe_free_gpio; } - host->base = ioremap(host->mem->start, RESSIZE(host->mem)); + host->base = ioremap(host->mem->start, resource_size(host->mem)); if (!host->base) { dev_err(&pdev->dev, "failed to ioremap() io memory region.\n"); ret = -EINVAL; @@ -1331,31 +1645,60 @@ static int __devinit s3cmci_probe(struct platform_device *pdev, int is2440) * ensure we don't lock the system with un-serviceable requests. */ disable_irq(host->irq); + host->irq_state = false; - host->irq_cd = s3c2410_gpio_getirq(host->pdata->gpio_detect); - - if (host->irq_cd >= 0) { - if (request_irq(host->irq_cd, s3cmci_irq_cd, - IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, - DRIVER_NAME, host)) { - dev_err(&pdev->dev, "can't get card detect irq.\n"); - ret = -ENOENT; + if (!host->pdata->no_detect) { + ret = gpio_request(host->pdata->gpio_detect, "s3cmci detect"); + if (ret) { + dev_err(&pdev->dev, "failed to get detect gpio\n"); goto probe_free_irq; } - } else { - dev_warn(&pdev->dev, "host detect has no irq available\n"); - s3c2410_gpio_cfgpin(host->pdata->gpio_detect, - S3C2410_GPIO_INPUT); + + host->irq_cd = s3c2410_gpio_getirq(host->pdata->gpio_detect); + + if (host->irq_cd >= 0) { + if (request_irq(host->irq_cd, s3cmci_irq_cd, + IRQF_TRIGGER_RISING | + IRQF_TRIGGER_FALLING, + DRIVER_NAME, host)) { + dev_err(&pdev->dev, + "can't get card detect irq.\n"); + ret = -ENOENT; + goto probe_free_gpio_cd; + } + } else { + dev_warn(&pdev->dev, + "host detect has no irq available\n"); + gpio_direction_input(host->pdata->gpio_detect); + } + } else + host->irq_cd = -1; + + if (!host->pdata->no_wprotect) { + ret = gpio_request(host->pdata->gpio_wprotect, "s3cmci wp"); + if (ret) { + dev_err(&pdev->dev, "failed to get writeprotect\n"); + goto probe_free_irq_cd; + } + + gpio_direction_input(host->pdata->gpio_wprotect); } - if (host->pdata->gpio_wprotect) - s3c2410_gpio_cfgpin(host->pdata->gpio_wprotect, - S3C2410_GPIO_INPUT); + /* depending on the dma state, get a dma channel to use. */ - if (s3c2410_dma_request(S3CMCI_DMA, &s3cmci_dma_client, NULL) < 0) { - dev_err(&pdev->dev, "unable to get DMA channel.\n"); - ret = -EBUSY; - goto probe_free_irq_cd; + if (s3cmci_host_usedma(host)) { + host->dma = s3c2410_dma_request(DMACH_SDI, &s3cmci_dma_client, + host); + if (host->dma < 0) { + dev_err(&pdev->dev, "cannot get DMA channel.\n"); + if (!s3cmci_host_canpio()) { + ret = -EBUSY; + goto probe_free_gpio_wp; + } else { + dev_warn(&pdev->dev, "falling back to PIO.\n"); + host->dodma = 0; + } + } } host->clk = clk_get(&pdev->dev, "sdi"); @@ -1363,7 +1706,7 @@ static int __devinit s3cmci_probe(struct platform_device *pdev, int is2440) dev_err(&pdev->dev, "failed to find clock source.\n"); ret = PTR_ERR(host->clk); host->clk = NULL; - goto probe_free_host; + goto probe_free_dma; } ret = clk_enable(host->clk); @@ -1376,7 +1719,11 @@ static int __devinit s3cmci_probe(struct platform_device *pdev, int is2440) mmc->ops = &s3cmci_ops; mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34; +#ifdef CONFIG_MMC_S3C_HW_SDIO_IRQ + mmc->caps = MMC_CAP_4_BIT_DATA | MMC_CAP_SDIO_IRQ; +#else mmc->caps = MMC_CAP_4_BIT_DATA; +#endif mmc->f_min = host->clk_rate / (host->clk_div * 256); mmc->f_max = host->clk_rate / host->clk_div; @@ -1408,8 +1755,12 @@ static int __devinit s3cmci_probe(struct platform_device *pdev, int is2440) goto free_cpufreq; } + s3cmci_debugfs_attach(host); + platform_set_drvdata(pdev, mmc); - dev_info(&pdev->dev, "initialisation done.\n"); + dev_info(&pdev->dev, "%s - using %s, %s SDIO IRQ\n", mmc_hostname(mmc), + s3cmci_host_usedma(host) ? "dma" : "pio", + mmc->caps & MMC_CAP_SDIO_IRQ ? "hw" : "sw"); return 0; @@ -1422,6 +1773,18 @@ static int __devinit s3cmci_probe(struct platform_device *pdev, int is2440) clk_free: clk_put(host->clk); + probe_free_dma: + if (s3cmci_host_usedma(host)) + s3c2410_dma_free(host->dma, &s3cmci_dma_client); + + probe_free_gpio_wp: + if (!host->pdata->no_wprotect) + gpio_free(host->pdata->gpio_wprotect); + + probe_free_gpio_cd: + if (!host->pdata->no_detect) + gpio_free(host->pdata->gpio_detect); + probe_free_irq_cd: if (host->irq_cd >= 0) free_irq(host->irq_cd, host); @@ -1433,10 +1796,15 @@ static int __devinit s3cmci_probe(struct platform_device *pdev, int is2440) iounmap(host->base); probe_free_mem_region: - release_mem_region(host->mem->start, RESSIZE(host->mem)); + release_mem_region(host->mem->start, resource_size(host->mem)); + + probe_free_gpio: + for (i = S3C2410_GPE(5); i <= S3C2410_GPE(10); i++) + gpio_free(i); probe_free_host: mmc_free_host(mmc); + probe_out: return ret; } @@ -1449,6 +1817,7 @@ static void s3cmci_shutdown(struct platform_device *pdev) if (host->irq_cd >= 0) free_irq(host->irq_cd, host); + s3cmci_debugfs_remove(host); s3cmci_cpufreq_deregister(host); mmc_remove_host(mmc); clk_disable(host->clk); @@ -1458,104 +1827,102 @@ static int __devexit s3cmci_remove(struct platform_device *pdev) { struct mmc_host *mmc = platform_get_drvdata(pdev); struct s3cmci_host *host = mmc_priv(mmc); + struct s3c24xx_mci_pdata *pd = host->pdata; + int i; s3cmci_shutdown(pdev); clk_put(host->clk); tasklet_disable(&host->pio_tasklet); - s3c2410_dma_free(S3CMCI_DMA, &s3cmci_dma_client); + + if (s3cmci_host_usedma(host)) + s3c2410_dma_free(host->dma, &s3cmci_dma_client); free_irq(host->irq, host); + if (!pd->no_wprotect) + gpio_free(pd->gpio_wprotect); + + if (!pd->no_detect) + gpio_free(pd->gpio_detect); + + for (i = S3C2410_GPE(5); i <= S3C2410_GPE(10); i++) + gpio_free(i); + + iounmap(host->base); - release_mem_region(host->mem->start, RESSIZE(host->mem)); + release_mem_region(host->mem->start, resource_size(host->mem)); mmc_free_host(mmc); return 0; } -static int __devinit s3cmci_2410_probe(struct platform_device *dev) -{ - return s3cmci_probe(dev, 0); -} +static struct platform_device_id s3cmci_driver_ids[] = { + { + .name = "s3c2410-sdi", + .driver_data = 0, + }, { + .name = "s3c2412-sdi", + .driver_data = 1, + }, { + .name = "s3c2440-sdi", + .driver_data = 1, + }, + { } +}; -static int __devinit s3cmci_2412_probe(struct platform_device *dev) -{ - return s3cmci_probe(dev, 1); -} +MODULE_DEVICE_TABLE(platform, s3cmci_driver_ids); -static int __devinit s3cmci_2440_probe(struct platform_device *dev) -{ - return s3cmci_probe(dev, 1); -} #ifdef CONFIG_PM -static int s3cmci_suspend(struct platform_device *dev, pm_message_t state) +static int s3cmci_suspend(struct device *dev) { - struct mmc_host *mmc = platform_get_drvdata(dev); + struct mmc_host *mmc = platform_get_drvdata(to_platform_device(dev)); + struct pm_message event = { PM_EVENT_SUSPEND }; - return mmc_suspend_host(mmc, state); + return mmc_suspend_host(mmc, event); } -static int s3cmci_resume(struct platform_device *dev) +static int s3cmci_resume(struct device *dev) { - struct mmc_host *mmc = platform_get_drvdata(dev); + struct mmc_host *mmc = platform_get_drvdata(to_platform_device(dev)); return mmc_resume_host(mmc); } -#else /* CONFIG_PM */ -#define s3cmci_suspend NULL -#define s3cmci_resume NULL -#endif /* CONFIG_PM */ - - -static struct platform_driver s3cmci_2410_driver = { - .driver.name = "s3c2410-sdi", - .driver.owner = THIS_MODULE, - .probe = s3cmci_2410_probe, - .remove = __devexit_p(s3cmci_remove), - .shutdown = s3cmci_shutdown, +static struct dev_pm_ops s3cmci_pm = { .suspend = s3cmci_suspend, .resume = s3cmci_resume, }; -static struct platform_driver s3cmci_2412_driver = { - .driver.name = "s3c2412-sdi", - .driver.owner = THIS_MODULE, - .probe = s3cmci_2412_probe, - .remove = __devexit_p(s3cmci_remove), - .shutdown = s3cmci_shutdown, - .suspend = s3cmci_suspend, - .resume = s3cmci_resume, -}; +#define s3cmci_pm_ops &s3cmci_pm +#else /* CONFIG_PM */ +#define s3cmci_pm_ops NULL +#endif /* CONFIG_PM */ -static struct platform_driver s3cmci_2440_driver = { - .driver.name = "s3c2440-sdi", - .driver.owner = THIS_MODULE, - .probe = s3cmci_2440_probe, + +static struct platform_driver s3cmci_driver = { + .driver = { + .name = "s3c-sdi", + .owner = THIS_MODULE, + .pm = s3cmci_pm_ops, + }, + .id_table = s3cmci_driver_ids, + .probe = s3cmci_probe, .remove = __devexit_p(s3cmci_remove), .shutdown = s3cmci_shutdown, - .suspend = s3cmci_suspend, - .resume = s3cmci_resume, }; - static int __init s3cmci_init(void) { - platform_driver_register(&s3cmci_2410_driver); - platform_driver_register(&s3cmci_2412_driver); - platform_driver_register(&s3cmci_2440_driver); - return 0; + return platform_driver_register(&s3cmci_driver); } static void __exit s3cmci_exit(void) { - platform_driver_unregister(&s3cmci_2410_driver); - platform_driver_unregister(&s3cmci_2412_driver); - platform_driver_unregister(&s3cmci_2440_driver); + platform_driver_unregister(&s3cmci_driver); } module_init(s3cmci_init); @@ -1564,6 +1931,3 @@ module_exit(s3cmci_exit); MODULE_DESCRIPTION("Samsung S3C MMC/SD Card Interface driver"); MODULE_LICENSE("GPL v2"); MODULE_AUTHOR("Thomas Kleffel <tk@maintech.de>, Ben Dooks <ben-linux@fluff.org>"); -MODULE_ALIAS("platform:s3c2410-sdi"); -MODULE_ALIAS("platform:s3c2412-sdi"); -MODULE_ALIAS("platform:s3c2440-sdi"); diff --git a/drivers/mmc/host/s3cmci.h b/drivers/mmc/host/s3cmci.h index ca1ba3d58cfd..c76b53dbeb61 100644 --- a/drivers/mmc/host/s3cmci.h +++ b/drivers/mmc/host/s3cmci.h @@ -8,9 +8,6 @@ * published by the Free Software Foundation. */ -/* FIXME: DMA Resource management ?! */ -#define S3CMCI_DMA 0 - enum s3cmci_waitfor { COMPLETION_NONE, COMPLETION_FINALIZE, @@ -42,6 +39,11 @@ struct s3cmci_host { int dodma; int dmatogo; + bool irq_disabled; + bool irq_enabled; + bool irq_state; + int sdio_irqen; + struct mmc_request *mrq; int cmd_is_stop; @@ -68,6 +70,12 @@ struct s3cmci_host { unsigned int ccnt, dcnt; struct tasklet_struct pio_tasklet; +#ifdef CONFIG_DEBUG_FS + struct dentry *debug_root; + struct dentry *debug_state; + struct dentry *debug_regs; +#endif + #ifdef CONFIG_CPU_FREQ struct notifier_block freq_transition; #endif diff --git a/drivers/mmc/host/sdhci-of.c b/drivers/mmc/host/sdhci-of.c index 1e8aa590bb39..01ab916c2802 100644 --- a/drivers/mmc/host/sdhci-of.c +++ b/drivers/mmc/host/sdhci-of.c @@ -21,6 +21,7 @@ #include <linux/of.h> #include <linux/of_platform.h> #include <linux/mmc/host.h> +#include <asm/machdep.h> #include "sdhci.h" struct sdhci_of_data { @@ -48,6 +49,8 @@ struct sdhci_of_host { #define ESDHC_CLOCK_HCKEN 0x00000002 #define ESDHC_CLOCK_IPGEN 0x00000001 +#define ESDHC_HOST_CONTROL_RES 0x05 + static u32 esdhc_readl(struct sdhci_host *host, int reg) { return in_be32(host->ioaddr + reg); @@ -109,13 +112,17 @@ static void esdhc_writeb(struct sdhci_host *host, u8 val, int reg) int base = reg & ~0x3; int shift = (reg & 0x3) * 8; + /* Prevent SDHCI core from writing reserved bits (e.g. HISPD). */ + if (reg == SDHCI_HOST_CONTROL) + val &= ~ESDHC_HOST_CONTROL_RES; + clrsetbits_be32(host->ioaddr + base , 0xff << shift, val << shift); } static void esdhc_set_clock(struct sdhci_host *host, unsigned int clock) { - int div; int pre_div = 2; + int div = 1; clrbits32(host->ioaddr + ESDHC_SYSTEM_CONTROL, ESDHC_CLOCK_IPGEN | ESDHC_CLOCK_HCKEN | ESDHC_CLOCK_PEREN | ESDHC_CLOCK_MASK); @@ -123,19 +130,17 @@ static void esdhc_set_clock(struct sdhci_host *host, unsigned int clock) if (clock == 0) goto out; - if (host->max_clk / 16 > clock) { - for (; pre_div < 256; pre_div *= 2) { - if (host->max_clk / pre_div < clock * 16) - break; - } - } + while (host->max_clk / pre_div / 16 > clock && pre_div < 256) + pre_div *= 2; - for (div = 1; div <= 16; div++) { - if (host->max_clk / (div * pre_div) <= clock) - break; - } + while (host->max_clk / pre_div / div > clock && div < 16) + div++; + + dev_dbg(mmc_dev(host->mmc), "desired SD clock: %d, actual: %d\n", + clock, host->max_clk / pre_div / div); pre_div >>= 1; + div--; setbits32(host->ioaddr + ESDHC_SYSTEM_CONTROL, ESDHC_CLOCK_IPGEN | ESDHC_CLOCK_HCKEN | ESDHC_CLOCK_PEREN | @@ -165,19 +170,12 @@ static unsigned int esdhc_get_min_clock(struct sdhci_host *host) return of_host->clock / 256 / 16; } -static unsigned int esdhc_get_timeout_clock(struct sdhci_host *host) -{ - struct sdhci_of_host *of_host = sdhci_priv(host); - - return of_host->clock / 1000; -} - static struct sdhci_of_data sdhci_esdhc = { .quirks = SDHCI_QUIRK_FORCE_BLK_SZ_2048 | SDHCI_QUIRK_BROKEN_CARD_DETECTION | - SDHCI_QUIRK_INVERTED_WRITE_PROTECT | SDHCI_QUIRK_NO_BUSY_IRQ | SDHCI_QUIRK_NONSTANDARD_CLOCK | + SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK | SDHCI_QUIRK_PIO_NEEDS_DELAY | SDHCI_QUIRK_RESTORE_IRQS_AFTER_RESET | SDHCI_QUIRK_NO_CARD_NO_RESET, @@ -192,7 +190,6 @@ static struct sdhci_of_data sdhci_esdhc = { .enable_dma = esdhc_enable_dma, .get_max_clock = esdhc_get_max_clock, .get_min_clock = esdhc_get_min_clock, - .get_timeout_clock = esdhc_get_timeout_clock, }, }; @@ -219,6 +216,15 @@ static int sdhci_of_resume(struct of_device *ofdev) #endif +static bool __devinit sdhci_of_wp_inverted(struct device_node *np) +{ + if (of_get_property(np, "sdhci,wp-inverted", NULL)) + return true; + + /* Old device trees don't have the wp-inverted property. */ + return machine_is(mpc837x_rdb) || machine_is(mpc837x_mds); +} + static int __devinit sdhci_of_probe(struct of_device *ofdev, const struct of_device_id *match) { @@ -261,6 +267,9 @@ static int __devinit sdhci_of_probe(struct of_device *ofdev, if (of_get_property(np, "sdhci,1-bit-only", NULL)) host->quirks |= SDHCI_QUIRK_FORCE_1_BIT_DATA; + if (sdhci_of_wp_inverted(np)) + host->quirks |= SDHCI_QUIRK_INVERTED_WRITE_PROTECT; + clk = of_get_property(np, "clock-frequency", &size); if (clk && size == sizeof(*clk) && *clk) of_host->clock = *clk; diff --git a/drivers/mmc/host/sdhci-pci.c b/drivers/mmc/host/sdhci-pci.c index 2f15cc17d887..e0356644d1aa 100644 --- a/drivers/mmc/host/sdhci-pci.c +++ b/drivers/mmc/host/sdhci-pci.c @@ -83,7 +83,8 @@ static int ricoh_probe(struct sdhci_pci_chip *chip) if (chip->pdev->subsystem_vendor == PCI_VENDOR_ID_IBM) chip->quirks |= SDHCI_QUIRK_CLOCK_BEFORE_RESET; - if (chip->pdev->subsystem_vendor == PCI_VENDOR_ID_SAMSUNG) + if (chip->pdev->subsystem_vendor == PCI_VENDOR_ID_SAMSUNG || + chip->pdev->subsystem_vendor == PCI_VENDOR_ID_SONY) chip->quirks |= SDHCI_QUIRK_NO_CARD_NO_RESET; return 0; @@ -395,7 +396,7 @@ static int sdhci_pci_enable_dma(struct sdhci_host *host) if (((pdev->class & 0xFFFF00) == (PCI_CLASS_SYSTEM_SDHCI << 8)) && ((pdev->class & 0x0000FF) != PCI_SDHCI_IFDMA) && - (host->flags & SDHCI_USE_DMA)) { + (host->flags & SDHCI_USE_SDMA)) { dev_warn(&pdev->dev, "Will use DMA mode even though HW " "doesn't fully claim to support it.\n"); } diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index fc96f8cb9c0b..c279fbc4c2e5 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -591,6 +591,9 @@ static u8 sdhci_calc_timeout(struct sdhci_host *host, struct mmc_data *data) target_timeout = data->timeout_ns / 1000 + data->timeout_clks / host->clock; + if (host->quirks & SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK) + host->timeout_clk = host->clock / 1000; + /* * Figure out needed cycles. * We do this in steps in order to fit inside a 32 bit int. @@ -652,7 +655,7 @@ static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_data *data) count = sdhci_calc_timeout(host, data); sdhci_writeb(host, count, SDHCI_TIMEOUT_CONTROL); - if (host->flags & SDHCI_USE_DMA) + if (host->flags & (SDHCI_USE_SDMA | SDHCI_USE_ADMA)) host->flags |= SDHCI_REQ_USE_DMA; /* @@ -991,8 +994,8 @@ static void sdhci_set_clock(struct sdhci_host *host, unsigned int clock) clk |= SDHCI_CLOCK_INT_EN; sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL); - /* Wait max 10 ms */ - timeout = 10; + /* Wait max 20 ms */ + timeout = 20; while (!((clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL)) & SDHCI_CLOCK_INT_STABLE)) { if (timeout == 0) { @@ -1597,7 +1600,7 @@ int sdhci_resume_host(struct sdhci_host *host) { int ret; - if (host->flags & SDHCI_USE_DMA) { + if (host->flags & (SDHCI_USE_SDMA | SDHCI_USE_ADMA)) { if (host->ops->enable_dma) host->ops->enable_dma(host); } @@ -1678,23 +1681,20 @@ int sdhci_add_host(struct sdhci_host *host) caps = sdhci_readl(host, SDHCI_CAPABILITIES); if (host->quirks & SDHCI_QUIRK_FORCE_DMA) - host->flags |= SDHCI_USE_DMA; - else if (!(caps & SDHCI_CAN_DO_DMA)) - DBG("Controller doesn't have DMA capability\n"); + host->flags |= SDHCI_USE_SDMA; + else if (!(caps & SDHCI_CAN_DO_SDMA)) + DBG("Controller doesn't have SDMA capability\n"); else - host->flags |= SDHCI_USE_DMA; + host->flags |= SDHCI_USE_SDMA; if ((host->quirks & SDHCI_QUIRK_BROKEN_DMA) && - (host->flags & SDHCI_USE_DMA)) { + (host->flags & SDHCI_USE_SDMA)) { DBG("Disabling DMA as it is marked broken\n"); - host->flags &= ~SDHCI_USE_DMA; + host->flags &= ~SDHCI_USE_SDMA; } - if (host->flags & SDHCI_USE_DMA) { - if ((host->version >= SDHCI_SPEC_200) && - (caps & SDHCI_CAN_DO_ADMA2)) - host->flags |= SDHCI_USE_ADMA; - } + if ((host->version >= SDHCI_SPEC_200) && (caps & SDHCI_CAN_DO_ADMA2)) + host->flags |= SDHCI_USE_ADMA; if ((host->quirks & SDHCI_QUIRK_BROKEN_ADMA) && (host->flags & SDHCI_USE_ADMA)) { @@ -1702,13 +1702,14 @@ int sdhci_add_host(struct sdhci_host *host) host->flags &= ~SDHCI_USE_ADMA; } - if (host->flags & SDHCI_USE_DMA) { + if (host->flags & (SDHCI_USE_SDMA | SDHCI_USE_ADMA)) { if (host->ops->enable_dma) { if (host->ops->enable_dma(host)) { printk(KERN_WARNING "%s: No suitable DMA " "available. Falling back to PIO.\n", mmc_hostname(mmc)); - host->flags &= ~(SDHCI_USE_DMA | SDHCI_USE_ADMA); + host->flags &= + ~(SDHCI_USE_SDMA | SDHCI_USE_ADMA); } } } @@ -1736,7 +1737,7 @@ int sdhci_add_host(struct sdhci_host *host) * mask, but PIO does not need the hw shim so we set a new * mask here in that case. */ - if (!(host->flags & SDHCI_USE_DMA)) { + if (!(host->flags & (SDHCI_USE_SDMA | SDHCI_USE_ADMA))) { host->dma_mask = DMA_BIT_MASK(64); mmc_dev(host->mmc)->dma_mask = &host->dma_mask; } @@ -1757,13 +1758,15 @@ int sdhci_add_host(struct sdhci_host *host) host->timeout_clk = (caps & SDHCI_TIMEOUT_CLK_MASK) >> SDHCI_TIMEOUT_CLK_SHIFT; if (host->timeout_clk == 0) { - if (!host->ops->get_timeout_clock) { + if (host->ops->get_timeout_clock) { + host->timeout_clk = host->ops->get_timeout_clock(host); + } else if (!(host->quirks & + SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK)) { printk(KERN_ERR "%s: Hardware doesn't specify timeout clock " "frequency.\n", mmc_hostname(mmc)); return -ENODEV; } - host->timeout_clk = host->ops->get_timeout_clock(host); } if (caps & SDHCI_TIMEOUT_CLK_UNIT) host->timeout_clk *= 1000; @@ -1772,7 +1775,8 @@ int sdhci_add_host(struct sdhci_host *host) * Set host parameters. */ mmc->ops = &sdhci_ops; - if (host->ops->get_min_clock) + if (host->quirks & SDHCI_QUIRK_NONSTANDARD_CLOCK && + host->ops->set_clock && host->ops->get_min_clock) mmc->f_min = host->ops->get_min_clock(host); else mmc->f_min = host->max_clk / 256; @@ -1810,7 +1814,7 @@ int sdhci_add_host(struct sdhci_host *host) */ if (host->flags & SDHCI_USE_ADMA) mmc->max_hw_segs = 128; - else if (host->flags & SDHCI_USE_DMA) + else if (host->flags & SDHCI_USE_SDMA) mmc->max_hw_segs = 1; else /* PIO */ mmc->max_hw_segs = 128; @@ -1893,10 +1897,10 @@ int sdhci_add_host(struct sdhci_host *host) mmc_add_host(mmc); - printk(KERN_INFO "%s: SDHCI controller on %s [%s] using %s%s\n", + printk(KERN_INFO "%s: SDHCI controller on %s [%s] using %s\n", mmc_hostname(mmc), host->hw_name, dev_name(mmc_dev(mmc)), - (host->flags & SDHCI_USE_ADMA)?"A":"", - (host->flags & SDHCI_USE_DMA)?"DMA":"PIO"); + (host->flags & SDHCI_USE_ADMA) ? "ADMA" : + (host->flags & SDHCI_USE_SDMA) ? "DMA" : "PIO"); sdhci_enable_card_detection(host); diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h index c77e9ff30223..ce5f1d73dc04 100644 --- a/drivers/mmc/host/sdhci.h +++ b/drivers/mmc/host/sdhci.h @@ -143,7 +143,7 @@ #define SDHCI_CAN_DO_ADMA2 0x00080000 #define SDHCI_CAN_DO_ADMA1 0x00100000 #define SDHCI_CAN_DO_HISPD 0x00200000 -#define SDHCI_CAN_DO_DMA 0x00400000 +#define SDHCI_CAN_DO_SDMA 0x00400000 #define SDHCI_CAN_VDD_330 0x01000000 #define SDHCI_CAN_VDD_300 0x02000000 #define SDHCI_CAN_VDD_180 0x04000000 @@ -232,6 +232,8 @@ struct sdhci_host { #define SDHCI_QUIRK_FORCE_1_BIT_DATA (1<<22) /* Controller needs 10ms delay between applying power and clock */ #define SDHCI_QUIRK_DELAY_AFTER_POWER (1<<23) +/* Controller uses SDCLK instead of TMCLK for data timeouts */ +#define SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK (1<<24) int irq; /* Device IRQ */ void __iomem * ioaddr; /* Mapped address */ @@ -250,7 +252,7 @@ struct sdhci_host { spinlock_t lock; /* Mutex */ int flags; /* Host attributes */ -#define SDHCI_USE_DMA (1<<0) /* Host is DMA capable */ +#define SDHCI_USE_SDMA (1<<0) /* Host is SDMA capable */ #define SDHCI_USE_ADMA (1<<1) /* Host is ADMA capable */ #define SDHCI_REQ_USE_DMA (1<<2) /* Use DMA for this req. */ #define SDHCI_DEVICE_DEAD (1<<3) /* Device unresponsive */ diff --git a/drivers/mtd/Kconfig b/drivers/mtd/Kconfig index b8e35a0b4d72..ecf90f5c97c2 100644 --- a/drivers/mtd/Kconfig +++ b/drivers/mtd/Kconfig @@ -25,6 +25,14 @@ config MTD_DEBUG_VERBOSE help Determines the verbosity level of the MTD debugging messages. +config MTD_TESTS + tristate "MTD tests support" + depends on m + help + This option includes various MTD tests into compilation. The tests + should normally be compiled as kernel modules. The modules perform + various checks and verifications when loaded. + config MTD_CONCAT tristate "MTD concatenating support" help @@ -45,14 +53,6 @@ config MTD_PARTITIONS devices. Partitioning on NFTL 'devices' is a different - that's the 'normal' form of partitioning used on a block device. -config MTD_TESTS - tristate "MTD tests support" - depends on m - help - This option includes various MTD tests into compilation. The tests - should normally be compiled as kernel modules. The modules perform - various checks and verifications when loaded. - config MTD_REDBOOT_PARTS tristate "RedBoot partition table parsing" depends on MTD_PARTITIONS @@ -159,7 +159,7 @@ config MTD_AFS_PARTS config MTD_OF_PARTS tristate "Flash partition map based on OF description" - depends on PPC_OF && MTD_PARTITIONS + depends on (MICROBLAZE || PPC_OF) && MTD_PARTITIONS help This provides a partition parsing function which derives the partition map from the children of the flash node, diff --git a/drivers/mtd/afs.c b/drivers/mtd/afs.c index d072ca5be689..cec7ab98b2a9 100644 --- a/drivers/mtd/afs.c +++ b/drivers/mtd/afs.c @@ -239,7 +239,7 @@ static int parse_afs_partitions(struct mtd_info *mtd, parts[idx].offset = img_ptr; parts[idx].mask_flags = 0; - printk(" mtd%d: at 0x%08x, %5dKB, %8u, %s\n", + printk(" mtd%d: at 0x%08x, %5lluKiB, %8u, %s\n", idx, img_ptr, parts[idx].size / 1024, iis.imageNumber, str); diff --git a/drivers/mtd/chips/cfi_cmdset_0002.c b/drivers/mtd/chips/cfi_cmdset_0002.c index 61ea833e0908..94bb61e19047 100644 --- a/drivers/mtd/chips/cfi_cmdset_0002.c +++ b/drivers/mtd/chips/cfi_cmdset_0002.c @@ -282,16 +282,6 @@ static void fixup_s29gl032n_sectors(struct mtd_info *mtd, void *param) } } -static void fixup_M29W128G_write_buffer(struct mtd_info *mtd, void *param) -{ - struct map_info *map = mtd->priv; - struct cfi_private *cfi = map->fldrv_priv; - if (cfi->cfiq->BufWriteTimeoutTyp) { - pr_warning("Don't use write buffer on ST flash M29W128G\n"); - cfi->cfiq->BufWriteTimeoutTyp = 0; - } -} - static struct cfi_fixup cfi_fixup_table[] = { { CFI_MFR_ATMEL, CFI_ID_ANY, fixup_convert_atmel_pri, NULL }, #ifdef AMD_BOOTLOC_BUG @@ -308,7 +298,6 @@ static struct cfi_fixup cfi_fixup_table[] = { { CFI_MFR_AMD, 0x1301, fixup_s29gl064n_sectors, NULL, }, { CFI_MFR_AMD, 0x1a00, fixup_s29gl032n_sectors, NULL, }, { CFI_MFR_AMD, 0x1a01, fixup_s29gl032n_sectors, NULL, }, - { CFI_MFR_ST, 0x227E, fixup_M29W128G_write_buffer, NULL, }, #if !FORCE_WORD_WRITE { CFI_MFR_ANY, CFI_ID_ANY, fixup_use_write_buffers, NULL, }, #endif diff --git a/drivers/mtd/chips/cfi_util.c b/drivers/mtd/chips/cfi_util.c index 34d40e25d312..c5a84fda5410 100644..100755 --- a/drivers/mtd/chips/cfi_util.c +++ b/drivers/mtd/chips/cfi_util.c @@ -81,6 +81,10 @@ void __xipram cfi_qry_mode_off(uint32_t base, struct map_info *map, { cfi_send_gen_cmd(0xF0, 0, base, map, cfi, cfi->device_type, NULL); cfi_send_gen_cmd(0xFF, 0, base, map, cfi, cfi->device_type, NULL); + /* M29W128G flashes require an additional reset command + when exit qry mode */ + if ((cfi->mfr == CFI_MFR_ST) && (cfi->id == 0x227E || cfi->id == 0x7E)) + cfi_send_gen_cmd(0xF0, 0, base, map, cfi, cfi->device_type, NULL); } EXPORT_SYMBOL_GPL(cfi_qry_mode_off); diff --git a/drivers/mtd/chips/jedec_probe.c b/drivers/mtd/chips/jedec_probe.c index ccc4cfc7e4b5..736a3be265f2 100644 --- a/drivers/mtd/chips/jedec_probe.c +++ b/drivers/mtd/chips/jedec_probe.c @@ -111,6 +111,11 @@ #define I28F320B3B 0x8897 #define I28F640B3T 0x8898 #define I28F640B3B 0x8899 +#define I28F640C3B 0x88CD +#define I28F160F3T 0x88F3 +#define I28F160F3B 0x88F4 +#define I28F160C3T 0x88C2 +#define I28F160C3B 0x88C3 #define I82802AB 0x00ad #define I82802AC 0x00ac @@ -150,6 +155,7 @@ #define M50LPW080 0x002F #define M50FLW080A 0x0080 #define M50FLW080B 0x0081 +#define PSD4256G6V 0x00e9 /* SST */ #define SST29EE020 0x0010 @@ -201,6 +207,7 @@ enum uaddr { MTD_UADDR_0x0555_0x02AA, MTD_UADDR_0x0555_0x0AAA, MTD_UADDR_0x5555_0x2AAA, + MTD_UADDR_0x0AAA_0x0554, MTD_UADDR_0x0AAA_0x0555, MTD_UADDR_0xAAAA_0x5555, MTD_UADDR_DONT_CARE, /* Requires an arbitrary address */ @@ -245,6 +252,11 @@ static const struct unlock_addr unlock_addrs[] = { .addr2 = 0x2aaa }, + [MTD_UADDR_0x0AAA_0x0554] = { + .addr1 = 0x0AAA, + .addr2 = 0x0554 + }, + [MTD_UADDR_0x0AAA_0x0555] = { .addr1 = 0x0AAA, .addr2 = 0x0555 @@ -1103,6 +1115,19 @@ static const struct amd_flash_info jedec_table[] = { } }, { .mfr_id = MANUFACTURER_INTEL, + .dev_id = I28F640C3B, + .name = "Intel 28F640C3B", + .devtypes = CFI_DEVICETYPE_X16, + .uaddr = MTD_UADDR_UNNECESSARY, + .dev_size = SIZE_8MiB, + .cmd_set = P_ID_INTEL_STD, + .nr_regions = 2, + .regions = { + ERASEINFO(0x02000, 8), + ERASEINFO(0x10000, 127), + } + }, { + .mfr_id = MANUFACTURER_INTEL, .dev_id = I82802AB, .name = "Intel 82802AB", .devtypes = CFI_DEVICETYPE_X8, @@ -1156,8 +1181,8 @@ static const struct amd_flash_info jedec_table[] = { .mfr_id = MANUFACTURER_NEC, .dev_id = UPD29F064115, .name = "NEC uPD29F064115", - .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, - .uaddr = MTD_UADDR_0x0555_0x02AA, /* ???? */ + .devtypes = CFI_DEVICETYPE_X16, + .uaddr = MTD_UADDR_0xAAAA_0x5555, .dev_size = SIZE_8MiB, .cmd_set = P_ID_AMD_STD, .nr_regions = 3, @@ -1726,6 +1751,18 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x1000,16), } }, { + .mfr_id = 0xff00 | MANUFACTURER_ST, + .dev_id = 0xff00 | PSD4256G6V, + .name = "ST PSD4256G6V", + .devtypes = CFI_DEVICETYPE_X16, + .uaddr = MTD_UADDR_0x0AAA_0x0554, + .dev_size = SIZE_1MiB, + .cmd_set = P_ID_AMD_STD, + .nr_regions = 1, + .regions = { + ERASEINFO(0x10000,16), + } + }, { .mfr_id = MANUFACTURER_TOSHIBA, .dev_id = TC58FVT160, .name = "Toshiba TC58FVT160", diff --git a/drivers/mtd/devices/Kconfig b/drivers/mtd/devices/Kconfig index 325fab92a62c..c222514bb70d 100644 --- a/drivers/mtd/devices/Kconfig +++ b/drivers/mtd/devices/Kconfig @@ -104,6 +104,16 @@ config M25PXX_USE_FAST_READ help This option enables FAST_READ access supported by ST M25Pxx. +config MTD_SST25L + tristate "Support SST25L (non JEDEC) SPI Flash chips" + depends on SPI_MASTER + help + This enables access to the non JEDEC SST25L SPI flash chips, used + for program and data storage. + + Set up your spi devices with the right board-specific platform data, + if you want to specify device partitioning. + config MTD_SLRAM tristate "Uncached system RAM" help diff --git a/drivers/mtd/devices/Makefile b/drivers/mtd/devices/Makefile index 0993d5cf3923..ab5c9b92ac82 100644 --- a/drivers/mtd/devices/Makefile +++ b/drivers/mtd/devices/Makefile @@ -16,3 +16,4 @@ obj-$(CONFIG_MTD_LART) += lart.o obj-$(CONFIG_MTD_BLOCK2MTD) += block2mtd.o obj-$(CONFIG_MTD_DATAFLASH) += mtd_dataflash.o obj-$(CONFIG_MTD_M25P80) += m25p80.o +obj-$(CONFIG_MTD_SST25L) += sst25l.o diff --git a/drivers/mtd/devices/lart.c b/drivers/mtd/devices/lart.c index 578de1c67bfe..f4359fe7150f 100644 --- a/drivers/mtd/devices/lart.c +++ b/drivers/mtd/devices/lart.c @@ -393,7 +393,8 @@ static int flash_erase (struct mtd_info *mtd,struct erase_info *instr) * erase range is aligned with the erase size which is in * effect here. */ - if (instr->addr & (mtd->eraseregions[i].erasesize - 1)) return (-EINVAL); + if (i < 0 || (instr->addr & (mtd->eraseregions[i].erasesize - 1))) + return -EINVAL; /* Remember the erase region we start on */ first = i; @@ -409,7 +410,8 @@ static int flash_erase (struct mtd_info *mtd,struct erase_info *instr) i--; /* is the end aligned on a block boundary? */ - if ((instr->addr + instr->len) & (mtd->eraseregions[i].erasesize - 1)) return (-EINVAL); + if (i < 0 || ((instr->addr + instr->len) & (mtd->eraseregions[i].erasesize - 1))) + return -EINVAL; addr = instr->addr; len = instr->len; diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c index eb495d83064f..4c19269de91a 100644 --- a/drivers/mtd/devices/m25p80.c +++ b/drivers/mtd/devices/m25p80.c @@ -21,6 +21,7 @@ #include <linux/interrupt.h> #include <linux/mutex.h> #include <linux/math64.h> +#include <linux/sched.h> #include <linux/mtd/mtd.h> #include <linux/mtd/partitions.h> @@ -44,6 +45,11 @@ #define OPCODE_SE 0xd8 /* Sector erase (usually 64KiB) */ #define OPCODE_RDID 0x9f /* Read JEDEC ID */ +/* Used for SST flashes only. */ +#define OPCODE_BP 0x02 /* Byte program */ +#define OPCODE_WRDI 0x04 /* Write disable */ +#define OPCODE_AAI_WP 0xad /* Auto address increment word program */ + /* Status Register bits. */ #define SR_WIP 1 /* Write in progress */ #define SR_WEL 2 /* Write enable latch */ @@ -132,6 +138,15 @@ static inline int write_enable(struct m25p *flash) return spi_write_then_read(flash->spi, &code, 1, NULL, 0); } +/* + * Send write disble instruction to the chip. + */ +static inline int write_disable(struct m25p *flash) +{ + u8 code = OPCODE_WRDI; + + return spi_write_then_read(flash->spi, &code, 1, NULL, 0); +} /* * Service routine to read status register until ready, or timeout occurs. @@ -454,6 +469,111 @@ static int m25p80_write(struct mtd_info *mtd, loff_t to, size_t len, return 0; } +static int sst_write(struct mtd_info *mtd, loff_t to, size_t len, + size_t *retlen, const u_char *buf) +{ + struct m25p *flash = mtd_to_m25p(mtd); + struct spi_transfer t[2]; + struct spi_message m; + size_t actual; + int cmd_sz, ret; + + if (retlen) + *retlen = 0; + + /* sanity checks */ + if (!len) + return 0; + + if (to + len > flash->mtd.size) + return -EINVAL; + + spi_message_init(&m); + memset(t, 0, (sizeof t)); + + t[0].tx_buf = flash->command; + t[0].len = CMD_SIZE; + spi_message_add_tail(&t[0], &m); + + t[1].tx_buf = buf; + spi_message_add_tail(&t[1], &m); + + mutex_lock(&flash->lock); + + /* Wait until finished previous write command. */ + ret = wait_till_ready(flash); + if (ret) + goto time_out; + + write_enable(flash); + + actual = to % 2; + /* Start write from odd address. */ + if (actual) { + flash->command[0] = OPCODE_BP; + flash->command[1] = to >> 16; + flash->command[2] = to >> 8; + flash->command[3] = to; + + /* write one byte. */ + t[1].len = 1; + spi_sync(flash->spi, &m); + ret = wait_till_ready(flash); + if (ret) + goto time_out; + *retlen += m.actual_length - CMD_SIZE; + } + to += actual; + + flash->command[0] = OPCODE_AAI_WP; + flash->command[1] = to >> 16; + flash->command[2] = to >> 8; + flash->command[3] = to; + + /* Write out most of the data here. */ + cmd_sz = CMD_SIZE; + for (; actual < len - 1; actual += 2) { + t[0].len = cmd_sz; + /* write two bytes. */ + t[1].len = 2; + t[1].tx_buf = buf + actual; + + spi_sync(flash->spi, &m); + ret = wait_till_ready(flash); + if (ret) + goto time_out; + *retlen += m.actual_length - cmd_sz; + cmd_sz = 1; + to += 2; + } + write_disable(flash); + ret = wait_till_ready(flash); + if (ret) + goto time_out; + + /* Write out trailing byte if it exists. */ + if (actual != len) { + write_enable(flash); + flash->command[0] = OPCODE_BP; + flash->command[1] = to >> 16; + flash->command[2] = to >> 8; + flash->command[3] = to; + t[0].len = CMD_SIZE; + t[1].len = 1; + t[1].tx_buf = buf + actual; + + spi_sync(flash->spi, &m); + ret = wait_till_ready(flash); + if (ret) + goto time_out; + *retlen += m.actual_length - CMD_SIZE; + write_disable(flash); + } + +time_out: + mutex_unlock(&flash->lock); + return ret; +} /****************************************************************************/ @@ -501,7 +621,10 @@ static struct flash_info __devinitdata m25p_data [] = { { "at26df321", 0x1f4701, 0, 64 * 1024, 64, SECT_4K, }, /* Macronix */ + { "mx25l3205d", 0xc22016, 0, 64 * 1024, 64, }, + { "mx25l6405d", 0xc22017, 0, 64 * 1024, 128, }, { "mx25l12805d", 0xc22018, 0, 64 * 1024, 256, }, + { "mx25l12855e", 0xc22618, 0, 64 * 1024, 256, }, /* Spansion -- single (large) sector size only, at least * for the chips listed here (without boot sectors). @@ -511,14 +634,20 @@ static struct flash_info __devinitdata m25p_data [] = { { "s25sl016a", 0x010214, 0, 64 * 1024, 32, }, { "s25sl032a", 0x010215, 0, 64 * 1024, 64, }, { "s25sl064a", 0x010216, 0, 64 * 1024, 128, }, - { "s25sl12800", 0x012018, 0x0300, 256 * 1024, 64, }, + { "s25sl12800", 0x012018, 0x0300, 256 * 1024, 64, }, { "s25sl12801", 0x012018, 0x0301, 64 * 1024, 256, }, + { "s25fl129p0", 0x012018, 0x4d00, 256 * 1024, 64, }, + { "s25fl129p1", 0x012018, 0x4d01, 64 * 1024, 256, }, /* SST -- large erase sizes are "overlays", "sectors" are 4K */ { "sst25vf040b", 0xbf258d, 0, 64 * 1024, 8, SECT_4K, }, { "sst25vf080b", 0xbf258e, 0, 64 * 1024, 16, SECT_4K, }, { "sst25vf016b", 0xbf2541, 0, 64 * 1024, 32, SECT_4K, }, { "sst25vf032b", 0xbf254a, 0, 64 * 1024, 64, SECT_4K, }, + { "sst25wf512", 0xbf2501, 0, 64 * 1024, 1, SECT_4K, }, + { "sst25wf010", 0xbf2502, 0, 64 * 1024, 2, SECT_4K, }, + { "sst25wf020", 0xbf2503, 0, 64 * 1024, 4, SECT_4K, }, + { "sst25wf040", 0xbf2504, 0, 64 * 1024, 8, SECT_4K, }, /* ST Microelectronics -- newer production may have feature updates */ { "m25p05", 0x202010, 0, 32 * 1024, 2, }, @@ -667,7 +796,12 @@ static int __devinit m25p_probe(struct spi_device *spi) flash->mtd.size = info->sector_size * info->n_sectors; flash->mtd.erase = m25p80_erase; flash->mtd.read = m25p80_read; - flash->mtd.write = m25p80_write; + + /* sst flash chips use AAI word program */ + if (info->jedec_id >> 16 == 0xbf) + flash->mtd.write = sst_write; + else + flash->mtd.write = m25p80_write; /* prefer "small sector" erase if possible */ if (info->flags & SECT_4K) { diff --git a/drivers/mtd/devices/mtd_dataflash.c b/drivers/mtd/devices/mtd_dataflash.c index 43976aa4dbb1..93e3627be74c 100644 --- a/drivers/mtd/devices/mtd_dataflash.c +++ b/drivers/mtd/devices/mtd_dataflash.c @@ -401,7 +401,7 @@ static int dataflash_write(struct mtd_info *mtd, loff_t to, size_t len, (void) dataflash_waitready(priv->spi); -#ifdef CONFIG_MTD_DATAFLASH_VERIFY_WRITE +#ifdef CONFIG_MTD_DATAFLASH_WRITE_VERIFY /* (3) Compare to Buffer1 */ addr = pageaddr << priv->page_offset; @@ -430,7 +430,7 @@ static int dataflash_write(struct mtd_info *mtd, loff_t to, size_t len, } else status = 0; -#endif /* CONFIG_MTD_DATAFLASH_VERIFY_WRITE */ +#endif /* CONFIG_MTD_DATAFLASH_WRITE_VERIFY */ remaining = remaining - writelen; pageaddr++; @@ -966,3 +966,4 @@ module_exit(dataflash_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Andrew Victor, David Brownell"); MODULE_DESCRIPTION("MTD DataFlash driver"); +MODULE_ALIAS("spi:mtd_dataflash"); diff --git a/drivers/mtd/devices/phram.c b/drivers/mtd/devices/phram.c index 088fbb7595b5..1696bbecaa7e 100644 --- a/drivers/mtd/devices/phram.c +++ b/drivers/mtd/devices/phram.c @@ -14,6 +14,9 @@ * Example: * phram=swap,64Mi,128Mi phram=test,900Mi,1Mi */ + +#define pr_fmt(fmt) "phram: " fmt + #include <asm/io.h> #include <linux/init.h> #include <linux/kernel.h> @@ -23,8 +26,6 @@ #include <linux/slab.h> #include <linux/mtd/mtd.h> -#define ERROR(fmt, args...) printk(KERN_ERR "phram: " fmt , ## args) - struct phram_mtd_list { struct mtd_info mtd; struct list_head list; @@ -132,7 +133,7 @@ static int register_device(char *name, unsigned long start, unsigned long len) ret = -EIO; new->mtd.priv = ioremap(start, len); if (!new->mtd.priv) { - ERROR("ioremap failed\n"); + pr_err("ioremap failed\n"); goto out1; } @@ -152,7 +153,7 @@ static int register_device(char *name, unsigned long start, unsigned long len) ret = -EAGAIN; if (add_mtd_device(&new->mtd)) { - ERROR("Failed to register new device\n"); + pr_err("Failed to register new device\n"); goto out2; } @@ -227,8 +228,8 @@ static inline void kill_final_newline(char *str) #define parse_err(fmt, args...) do { \ - ERROR(fmt , ## args); \ - return 0; \ + pr_err(fmt , ## args); \ + return 1; \ } while (0) static int phram_setup(const char *val, struct kernel_param *kp) @@ -256,12 +257,8 @@ static int phram_setup(const char *val, struct kernel_param *kp) parse_err("not enough arguments\n"); ret = parse_name(&name, token[0]); - if (ret == -ENOMEM) - parse_err("out of memory\n"); - if (ret == -ENOSPC) - parse_err("name too long\n"); if (ret) - return 0; + return ret; ret = parse_num32(&start, token[1]); if (ret) { @@ -275,9 +272,11 @@ static int phram_setup(const char *val, struct kernel_param *kp) parse_err("illegal device length\n"); } - register_device(name, start, len); + ret = register_device(name, start, len); + if (!ret) + pr_info("%s device: %#x at %#x\n", name, len, start); - return 0; + return ret; } module_param_call(phram, phram_setup, NULL, NULL, 000); diff --git a/drivers/mtd/devices/slram.c b/drivers/mtd/devices/slram.c index 7d846e9173da..3aa05cd18ea1 100644 --- a/drivers/mtd/devices/slram.c +++ b/drivers/mtd/devices/slram.c @@ -341,7 +341,7 @@ static int __init init_slram(void) #else int count; - for (count = 0; (map[count]) && (count < SLRAM_MAX_DEVICES_PARAMS); + for (count = 0; count < SLRAM_MAX_DEVICES_PARAMS && map[count]; count++) { } diff --git a/drivers/mtd/devices/sst25l.c b/drivers/mtd/devices/sst25l.c new file mode 100644 index 000000000000..0a11721f146e --- /dev/null +++ b/drivers/mtd/devices/sst25l.c @@ -0,0 +1,513 @@ +/* + * sst25l.c + * + * Driver for SST25L SPI Flash chips + * + * Copyright © 2009 Bluewater Systems Ltd + * Author: Andre Renaud <andre@bluewatersys.com> + * Author: Ryan Mallon <ryan@bluewatersys.com> + * + * Based on m25p80.c + * + * This code is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include <linux/init.h> +#include <linux/module.h> +#include <linux/device.h> +#include <linux/mutex.h> +#include <linux/interrupt.h> +#include <linux/sched.h> + +#include <linux/mtd/mtd.h> +#include <linux/mtd/partitions.h> + +#include <linux/spi/spi.h> +#include <linux/spi/flash.h> + +/* Erases can take up to 3 seconds! */ +#define MAX_READY_WAIT_JIFFIES msecs_to_jiffies(3000) + +#define SST25L_CMD_WRSR 0x01 /* Write status register */ +#define SST25L_CMD_WRDI 0x04 /* Write disable */ +#define SST25L_CMD_RDSR 0x05 /* Read status register */ +#define SST25L_CMD_WREN 0x06 /* Write enable */ +#define SST25L_CMD_READ 0x03 /* High speed read */ + +#define SST25L_CMD_EWSR 0x50 /* Enable write status register */ +#define SST25L_CMD_SECTOR_ERASE 0x20 /* Erase sector */ +#define SST25L_CMD_READ_ID 0x90 /* Read device ID */ +#define SST25L_CMD_AAI_PROGRAM 0xaf /* Auto address increment */ + +#define SST25L_STATUS_BUSY (1 << 0) /* Chip is busy */ +#define SST25L_STATUS_WREN (1 << 1) /* Write enabled */ +#define SST25L_STATUS_BP0 (1 << 2) /* Block protection 0 */ +#define SST25L_STATUS_BP1 (1 << 3) /* Block protection 1 */ + +struct sst25l_flash { + struct spi_device *spi; + struct mutex lock; + struct mtd_info mtd; + + int partitioned; +}; + +struct flash_info { + const char *name; + uint16_t device_id; + unsigned page_size; + unsigned nr_pages; + unsigned erase_size; +}; + +#define to_sst25l_flash(x) container_of(x, struct sst25l_flash, mtd) + +static struct flash_info __initdata sst25l_flash_info[] = { + {"sst25lf020a", 0xbf43, 256, 1024, 4096}, + {"sst25lf040a", 0xbf44, 256, 2048, 4096}, +}; + +static int sst25l_status(struct sst25l_flash *flash, int *status) +{ + unsigned char command, response; + int err; + + command = SST25L_CMD_RDSR; + err = spi_write_then_read(flash->spi, &command, 1, &response, 1); + if (err < 0) + return err; + + *status = response; + return 0; +} + +static int sst25l_write_enable(struct sst25l_flash *flash, int enable) +{ + unsigned char command[2]; + int status, err; + + command[0] = enable ? SST25L_CMD_WREN : SST25L_CMD_WRDI; + err = spi_write(flash->spi, command, 1); + if (err) + return err; + + command[0] = SST25L_CMD_EWSR; + err = spi_write(flash->spi, command, 1); + if (err) + return err; + + command[0] = SST25L_CMD_WRSR; + command[1] = enable ? 0 : SST25L_STATUS_BP0 | SST25L_STATUS_BP1; + err = spi_write(flash->spi, command, 2); + if (err) + return err; + + if (enable) { + err = sst25l_status(flash, &status); + if (err) + return err; + if (!(status & SST25L_STATUS_WREN)) + return -EROFS; + } + + return 0; +} + +static int sst25l_wait_till_ready(struct sst25l_flash *flash) +{ + unsigned long deadline; + int status, err; + + deadline = jiffies + MAX_READY_WAIT_JIFFIES; + do { + err = sst25l_status(flash, &status); + if (err) + return err; + if (!(status & SST25L_STATUS_BUSY)) + return 0; + + cond_resched(); + } while (!time_after_eq(jiffies, deadline)); + + return -ETIMEDOUT; +} + +static int sst25l_erase_sector(struct sst25l_flash *flash, uint32_t offset) +{ + unsigned char command[4]; + int err; + + err = sst25l_write_enable(flash, 1); + if (err) + return err; + + command[0] = SST25L_CMD_SECTOR_ERASE; + command[1] = offset >> 16; + command[2] = offset >> 8; + command[3] = offset; + err = spi_write(flash->spi, command, 4); + if (err) + return err; + + err = sst25l_wait_till_ready(flash); + if (err) + return err; + + return sst25l_write_enable(flash, 0); +} + +static int sst25l_erase(struct mtd_info *mtd, struct erase_info *instr) +{ + struct sst25l_flash *flash = to_sst25l_flash(mtd); + uint32_t addr, end; + int err; + + /* Sanity checks */ + if (instr->addr + instr->len > flash->mtd.size) + return -EINVAL; + + if ((uint32_t)instr->len % mtd->erasesize) + return -EINVAL; + + if ((uint32_t)instr->addr % mtd->erasesize) + return -EINVAL; + + addr = instr->addr; + end = addr + instr->len; + + mutex_lock(&flash->lock); + + err = sst25l_wait_till_ready(flash); + if (err) { + mutex_unlock(&flash->lock); + return err; + } + + while (addr < end) { + err = sst25l_erase_sector(flash, addr); + if (err) { + mutex_unlock(&flash->lock); + instr->state = MTD_ERASE_FAILED; + dev_err(&flash->spi->dev, "Erase failed\n"); + return err; + } + + addr += mtd->erasesize; + } + + mutex_unlock(&flash->lock); + + instr->state = MTD_ERASE_DONE; + mtd_erase_callback(instr); + return 0; +} + +static int sst25l_read(struct mtd_info *mtd, loff_t from, size_t len, + size_t *retlen, unsigned char *buf) +{ + struct sst25l_flash *flash = to_sst25l_flash(mtd); + struct spi_transfer transfer[2]; + struct spi_message message; + unsigned char command[4]; + int ret; + + /* Sanity checking */ + if (len == 0) + return 0; + + if (from + len > flash->mtd.size) + return -EINVAL; + + if (retlen) + *retlen = 0; + + spi_message_init(&message); + memset(&transfer, 0, sizeof(transfer)); + + command[0] = SST25L_CMD_READ; + command[1] = from >> 16; + command[2] = from >> 8; + command[3] = from; + + transfer[0].tx_buf = command; + transfer[0].len = sizeof(command); + spi_message_add_tail(&transfer[0], &message); + + transfer[1].rx_buf = buf; + transfer[1].len = len; + spi_message_add_tail(&transfer[1], &message); + + mutex_lock(&flash->lock); + + /* Wait for previous write/erase to complete */ + ret = sst25l_wait_till_ready(flash); + if (ret) { + mutex_unlock(&flash->lock); + return ret; + } + + spi_sync(flash->spi, &message); + + if (retlen && message.actual_length > sizeof(command)) + *retlen += message.actual_length - sizeof(command); + + mutex_unlock(&flash->lock); + return 0; +} + +static int sst25l_write(struct mtd_info *mtd, loff_t to, size_t len, + size_t *retlen, const unsigned char *buf) +{ + struct sst25l_flash *flash = to_sst25l_flash(mtd); + int i, j, ret, bytes, copied = 0; + unsigned char command[5]; + + /* Sanity checks */ + if (!len) + return 0; + + if (to + len > flash->mtd.size) + return -EINVAL; + + if ((uint32_t)to % mtd->writesize) + return -EINVAL; + + mutex_lock(&flash->lock); + + ret = sst25l_write_enable(flash, 1); + if (ret) + goto out; + + for (i = 0; i < len; i += mtd->writesize) { + ret = sst25l_wait_till_ready(flash); + if (ret) + goto out; + + /* Write the first byte of the page */ + command[0] = SST25L_CMD_AAI_PROGRAM; + command[1] = (to + i) >> 16; + command[2] = (to + i) >> 8; + command[3] = (to + i); + command[4] = buf[i]; + ret = spi_write(flash->spi, command, 5); + if (ret < 0) + goto out; + copied++; + + /* + * Write the remaining bytes using auto address + * increment mode + */ + bytes = min_t(uint32_t, mtd->writesize, len - i); + for (j = 1; j < bytes; j++, copied++) { + ret = sst25l_wait_till_ready(flash); + if (ret) + goto out; + + command[1] = buf[i + j]; + ret = spi_write(flash->spi, command, 2); + if (ret) + goto out; + } + } + +out: + ret = sst25l_write_enable(flash, 0); + + if (retlen) + *retlen = copied; + + mutex_unlock(&flash->lock); + return ret; +} + +static struct flash_info *__init sst25l_match_device(struct spi_device *spi) +{ + struct flash_info *flash_info = NULL; + unsigned char command[4], response; + int i, err; + uint16_t id; + + command[0] = SST25L_CMD_READ_ID; + command[1] = 0; + command[2] = 0; + command[3] = 0; + err = spi_write_then_read(spi, command, sizeof(command), &response, 1); + if (err < 0) { + dev_err(&spi->dev, "error reading device id msb\n"); + return NULL; + } + + id = response << 8; + + command[0] = SST25L_CMD_READ_ID; + command[1] = 0; + command[2] = 0; + command[3] = 1; + err = spi_write_then_read(spi, command, sizeof(command), &response, 1); + if (err < 0) { + dev_err(&spi->dev, "error reading device id lsb\n"); + return NULL; + } + + id |= response; + + for (i = 0; i < ARRAY_SIZE(sst25l_flash_info); i++) + if (sst25l_flash_info[i].device_id == id) + flash_info = &sst25l_flash_info[i]; + + if (!flash_info) + dev_err(&spi->dev, "unknown id %.4x\n", id); + + return flash_info; +} + +static int __init sst25l_probe(struct spi_device *spi) +{ + struct flash_info *flash_info; + struct sst25l_flash *flash; + struct flash_platform_data *data; + int ret, i; + + flash_info = sst25l_match_device(spi); + if (!flash_info) + return -ENODEV; + + flash = kzalloc(sizeof(struct sst25l_flash), GFP_KERNEL); + if (!flash) + return -ENOMEM; + + flash->spi = spi; + mutex_init(&flash->lock); + dev_set_drvdata(&spi->dev, flash); + + data = spi->dev.platform_data; + if (data && data->name) + flash->mtd.name = data->name; + else + flash->mtd.name = dev_name(&spi->dev); + + flash->mtd.type = MTD_NORFLASH; + flash->mtd.flags = MTD_CAP_NORFLASH; + flash->mtd.erasesize = flash_info->erase_size; + flash->mtd.writesize = flash_info->page_size; + flash->mtd.size = flash_info->page_size * flash_info->nr_pages; + flash->mtd.erase = sst25l_erase; + flash->mtd.read = sst25l_read; + flash->mtd.write = sst25l_write; + + dev_info(&spi->dev, "%s (%lld KiB)\n", flash_info->name, + (long long)flash->mtd.size >> 10); + + DEBUG(MTD_DEBUG_LEVEL2, + "mtd .name = %s, .size = 0x%llx (%lldMiB) " + ".erasesize = 0x%.8x (%uKiB) .numeraseregions = %d\n", + flash->mtd.name, + (long long)flash->mtd.size, (long long)(flash->mtd.size >> 20), + flash->mtd.erasesize, flash->mtd.erasesize / 1024, + flash->mtd.numeraseregions); + + if (flash->mtd.numeraseregions) + for (i = 0; i < flash->mtd.numeraseregions; i++) + DEBUG(MTD_DEBUG_LEVEL2, + "mtd.eraseregions[%d] = { .offset = 0x%llx, " + ".erasesize = 0x%.8x (%uKiB), " + ".numblocks = %d }\n", + i, (long long)flash->mtd.eraseregions[i].offset, + flash->mtd.eraseregions[i].erasesize, + flash->mtd.eraseregions[i].erasesize / 1024, + flash->mtd.eraseregions[i].numblocks); + + if (mtd_has_partitions()) { + struct mtd_partition *parts = NULL; + int nr_parts = 0; + + if (mtd_has_cmdlinepart()) { + static const char *part_probes[] = + {"cmdlinepart", NULL}; + + nr_parts = parse_mtd_partitions(&flash->mtd, + part_probes, + &parts, 0); + } + + if (nr_parts <= 0 && data && data->parts) { + parts = data->parts; + nr_parts = data->nr_parts; + } + + if (nr_parts > 0) { + for (i = 0; i < nr_parts; i++) { + DEBUG(MTD_DEBUG_LEVEL2, "partitions[%d] = " + "{.name = %s, .offset = 0x%llx, " + ".size = 0x%llx (%lldKiB) }\n", + i, parts[i].name, + (long long)parts[i].offset, + (long long)parts[i].size, + (long long)(parts[i].size >> 10)); + } + + flash->partitioned = 1; + return add_mtd_partitions(&flash->mtd, + parts, nr_parts); + } + + } else if (data->nr_parts) { + dev_warn(&spi->dev, "ignoring %d default partitions on %s\n", + data->nr_parts, data->name); + } + + ret = add_mtd_device(&flash->mtd); + if (ret == 1) { + kfree(flash); + dev_set_drvdata(&spi->dev, NULL); + return -ENODEV; + } + + return 0; +} + +static int __exit sst25l_remove(struct spi_device *spi) +{ + struct sst25l_flash *flash = dev_get_drvdata(&spi->dev); + int ret; + + if (mtd_has_partitions() && flash->partitioned) + ret = del_mtd_partitions(&flash->mtd); + else + ret = del_mtd_device(&flash->mtd); + if (ret == 0) + kfree(flash); + return ret; +} + +static struct spi_driver sst25l_driver = { + .driver = { + .name = "sst25l", + .bus = &spi_bus_type, + .owner = THIS_MODULE, + }, + .probe = sst25l_probe, + .remove = __exit_p(sst25l_remove), +}; + +static int __init sst25l_init(void) +{ + return spi_register_driver(&sst25l_driver); +} + +static void __exit sst25l_exit(void) +{ + spi_unregister_driver(&sst25l_driver); +} + +module_init(sst25l_init); +module_exit(sst25l_exit); + +MODULE_DESCRIPTION("MTD SPI driver for SST25L Flash chips"); +MODULE_AUTHOR("Andre Renaud <andre@bluewatersys.com>, " + "Ryan Mallon <ryan@bluewatersys.com>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/mtd/inftlcore.c b/drivers/mtd/inftlcore.c index d8cf29c01cc4..8aca5523a337 100644..100755 --- a/drivers/mtd/inftlcore.c +++ b/drivers/mtd/inftlcore.c @@ -550,7 +550,7 @@ hitused: * waiting to be picked up. We're going to have to fold * a chain to make room. */ - thisEUN = INFTL_makefreeblock(inftl, BLOCK_NIL); + thisEUN = INFTL_makefreeblock(inftl, block); /* * Hopefully we free something, lets try again. diff --git a/drivers/mtd/maps/Kconfig b/drivers/mtd/maps/Kconfig index 7a58bd5522fd..14be0755d7cd 100644 --- a/drivers/mtd/maps/Kconfig +++ b/drivers/mtd/maps/Kconfig @@ -74,7 +74,7 @@ config MTD_PHYSMAP_BANKWIDTH config MTD_PHYSMAP_OF tristate "Flash device in physical memory map based on OF description" - depends on PPC_OF && (MTD_CFI || MTD_JEDECPROBE || MTD_ROM) + depends on (MICROBLAZE || PPC_OF) && (MTD_CFI || MTD_JEDECPROBE || MTD_ROM) help This provides a 'mapping' driver which allows the NOR Flash and ROM driver code to communicate with chips which are mapped @@ -484,9 +484,20 @@ config MTD_BFIN_ASYNC If compiled as a module, it will be called bfin-async-flash. +config MTD_GPIO_ADDR + tristate "GPIO-assisted Flash Chip Support" + depends on GENERIC_GPIO || GPIOLIB + depends on MTD_COMPLEX_MAPPINGS + select MTD_PARTITIONS + help + Map driver which allows flashes to be partially physically addressed + and assisted by GPIOs. + + If compiled as a module, it will be called gpio-addr-flash. + config MTD_UCLINUX bool "Generic uClinux RAM/ROM filesystem support" - depends on MTD_PARTITIONS && MTD_RAM && !MMU + depends on MTD_PARTITIONS && MTD_RAM=y && !MMU help Map driver to support image based filesystems for uClinux. diff --git a/drivers/mtd/maps/Makefile b/drivers/mtd/maps/Makefile index 5beb0662d724..ae2f6dbe43c3 100644 --- a/drivers/mtd/maps/Makefile +++ b/drivers/mtd/maps/Makefile @@ -60,3 +60,4 @@ obj-$(CONFIG_MTD_INTEL_VR_NOR) += intel_vr_nor.o obj-$(CONFIG_MTD_BFIN_ASYNC) += bfin-async-flash.o obj-$(CONFIG_MTD_RBTX4939) += rbtx4939-flash.o obj-$(CONFIG_MTD_VMU) += vmu-flash.o +obj-$(CONFIG_MTD_GPIO_ADDR) += gpio-addr-flash.o diff --git a/drivers/mtd/maps/gpio-addr-flash.c b/drivers/mtd/maps/gpio-addr-flash.c new file mode 100644 index 000000000000..1ad5caf9fe69 --- /dev/null +++ b/drivers/mtd/maps/gpio-addr-flash.c @@ -0,0 +1,310 @@ +/* + * drivers/mtd/maps/gpio-addr-flash.c + * + * Handle the case where a flash device is mostly addressed using physical + * line and supplemented by GPIOs. This way you can hook up say a 8MiB flash + * to a 2MiB memory range and use the GPIOs to select a particular range. + * + * Copyright © 2000 Nicolas Pitre <nico@cam.org> + * Copyright © 2005-2009 Analog Devices Inc. + * + * Enter bugs at http://blackfin.uclinux.org/ + * + * Licensed under the GPL-2 or later. + */ + +#include <linux/gpio.h> +#include <linux/init.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/mtd/mtd.h> +#include <linux/mtd/map.h> +#include <linux/mtd/partitions.h> +#include <linux/mtd/physmap.h> +#include <linux/platform_device.h> +#include <linux/types.h> + +#define pr_devinit(fmt, args...) ({ static const __devinitconst char __fmt[] = fmt; printk(__fmt, ## args); }) + +#define DRIVER_NAME "gpio-addr-flash" +#define PFX DRIVER_NAME ": " + +/** + * struct async_state - keep GPIO flash state + * @mtd: MTD state for this mapping + * @map: MTD map state for this flash + * @gpio_count: number of GPIOs used to address + * @gpio_addrs: array of GPIOs to twiddle + * @gpio_values: cached GPIO values + * @win_size: dedicated memory size (if no GPIOs) + */ +struct async_state { + struct mtd_info *mtd; + struct map_info map; + size_t gpio_count; + unsigned *gpio_addrs; + int *gpio_values; + unsigned long win_size; +}; +#define gf_map_info_to_state(mi) ((struct async_state *)(mi)->map_priv_1) + +/** + * gf_set_gpios() - set GPIO address lines to access specified flash offset + * @state: GPIO flash state + * @ofs: desired offset to access + * + * Rather than call the GPIO framework every time, cache the last-programmed + * value. This speeds up sequential accesses (which are by far the most common + * type). We rely on the GPIO framework to treat non-zero value as high so + * that we don't have to normalize the bits. + */ +static void gf_set_gpios(struct async_state *state, unsigned long ofs) +{ + size_t i = 0; + int value; + ofs /= state->win_size; + do { + value = ofs & (1 << i); + if (state->gpio_values[i] != value) { + gpio_set_value(state->gpio_addrs[i], value); + state->gpio_values[i] = value; + } + } while (++i < state->gpio_count); +} + +/** + * gf_read() - read a word at the specified offset + * @map: MTD map state + * @ofs: desired offset to read + */ +static map_word gf_read(struct map_info *map, unsigned long ofs) +{ + struct async_state *state = gf_map_info_to_state(map); + uint16_t word; + map_word test; + + gf_set_gpios(state, ofs); + + word = readw(map->virt + (ofs % state->win_size)); + test.x[0] = word; + return test; +} + +/** + * gf_copy_from() - copy a chunk of data from the flash + * @map: MTD map state + * @to: memory to copy to + * @from: flash offset to copy from + * @len: how much to copy + * + * We rely on the MTD layer to chunk up copies such that a single request here + * will not cross a window size. This allows us to only wiggle the GPIOs once + * before falling back to a normal memcpy. Reading the higher layer code shows + * that this is indeed the case, but add a BUG_ON() to future proof. + */ +static void gf_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) +{ + struct async_state *state = gf_map_info_to_state(map); + + gf_set_gpios(state, from); + + /* BUG if operation crosses the win_size */ + BUG_ON(!((from + len) % state->win_size <= (from + len))); + + /* operation does not cross the win_size, so one shot it */ + memcpy_fromio(to, map->virt + (from % state->win_size), len); +} + +/** + * gf_write() - write a word at the specified offset + * @map: MTD map state + * @ofs: desired offset to write + */ +static void gf_write(struct map_info *map, map_word d1, unsigned long ofs) +{ + struct async_state *state = gf_map_info_to_state(map); + uint16_t d; + + gf_set_gpios(state, ofs); + + d = d1.x[0]; + writew(d, map->virt + (ofs % state->win_size)); +} + +/** + * gf_copy_to() - copy a chunk of data to the flash + * @map: MTD map state + * @to: flash offset to copy to + * @from: memory to copy from + * @len: how much to copy + * + * See gf_copy_from() caveat. + */ +static void gf_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len) +{ + struct async_state *state = gf_map_info_to_state(map); + + gf_set_gpios(state, to); + + /* BUG if operation crosses the win_size */ + BUG_ON(!((to + len) % state->win_size <= (to + len))); + + /* operation does not cross the win_size, so one shot it */ + memcpy_toio(map->virt + (to % state->win_size), from, len); +} + +#ifdef CONFIG_MTD_PARTITIONS +static const char *part_probe_types[] = { "cmdlinepart", "RedBoot", NULL }; +#endif + +/** + * gpio_flash_probe() - setup a mapping for a GPIO assisted flash + * @pdev: platform device + * + * The platform resource layout expected looks something like: + * struct mtd_partition partitions[] = { ... }; + * struct physmap_flash_data flash_data = { ... }; + * unsigned flash_gpios[] = { GPIO_XX, GPIO_XX, ... }; + * struct resource flash_resource[] = { + * { + * .name = "cfi_probe", + * .start = 0x20000000, + * .end = 0x201fffff, + * .flags = IORESOURCE_MEM, + * }, { + * .start = (unsigned long)flash_gpios, + * .end = ARRAY_SIZE(flash_gpios), + * .flags = IORESOURCE_IRQ, + * } + * }; + * struct platform_device flash_device = { + * .name = "gpio-addr-flash", + * .dev = { .platform_data = &flash_data, }, + * .num_resources = ARRAY_SIZE(flash_resource), + * .resource = flash_resource, + * ... + * }; + */ +static int __devinit gpio_flash_probe(struct platform_device *pdev) +{ + int ret; + size_t i, arr_size; + struct physmap_flash_data *pdata; + struct resource *memory; + struct resource *gpios; + struct async_state *state; + + pdata = pdev->dev.platform_data; + memory = platform_get_resource(pdev, IORESOURCE_MEM, 0); + gpios = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + + if (!memory || !gpios || !gpios->end) + return -EINVAL; + + arr_size = sizeof(int) * gpios->end; + state = kzalloc(sizeof(*state) + arr_size, GFP_KERNEL); + if (!state) + return -ENOMEM; + + state->gpio_count = gpios->end; + state->gpio_addrs = (void *)gpios->start; + state->gpio_values = (void *)(state + 1); + state->win_size = memory->end - memory->start + 1; + memset(state->gpio_values, 0xff, arr_size); + + state->map.name = DRIVER_NAME; + state->map.read = gf_read; + state->map.copy_from = gf_copy_from; + state->map.write = gf_write; + state->map.copy_to = gf_copy_to; + state->map.bankwidth = pdata->width; + state->map.size = state->win_size * (1 << state->gpio_count); + state->map.virt = (void __iomem *)memory->start; + state->map.phys = NO_XIP; + state->map.map_priv_1 = (unsigned long)state; + + platform_set_drvdata(pdev, state); + + i = 0; + do { + if (gpio_request(state->gpio_addrs[i], DRIVER_NAME)) { + pr_devinit(KERN_ERR PFX "failed to request gpio %d\n", + state->gpio_addrs[i]); + while (i--) + gpio_free(state->gpio_addrs[i]); + kfree(state); + return -EBUSY; + } + gpio_direction_output(state->gpio_addrs[i], 0); + } while (++i < state->gpio_count); + + pr_devinit(KERN_NOTICE PFX "probing %d-bit flash bus\n", + state->map.bankwidth * 8); + state->mtd = do_map_probe(memory->name, &state->map); + if (!state->mtd) { + for (i = 0; i < state->gpio_count; ++i) + gpio_free(state->gpio_addrs[i]); + kfree(state); + return -ENXIO; + } + +#ifdef CONFIG_MTD_PARTITIONS + ret = parse_mtd_partitions(state->mtd, part_probe_types, &pdata->parts, 0); + if (ret > 0) { + pr_devinit(KERN_NOTICE PFX "Using commandline partition definition\n"); + add_mtd_partitions(state->mtd, pdata->parts, ret); + kfree(pdata->parts); + + } else if (pdata->nr_parts) { + pr_devinit(KERN_NOTICE PFX "Using board partition definition\n"); + add_mtd_partitions(state->mtd, pdata->parts, pdata->nr_parts); + + } else +#endif + { + pr_devinit(KERN_NOTICE PFX "no partition info available, registering whole flash at once\n"); + add_mtd_device(state->mtd); + } + + return 0; +} + +static int __devexit gpio_flash_remove(struct platform_device *pdev) +{ + struct async_state *state = platform_get_drvdata(pdev); + size_t i = 0; + do { + gpio_free(state->gpio_addrs[i]); + } while (++i < state->gpio_count); +#ifdef CONFIG_MTD_PARTITIONS + del_mtd_partitions(state->mtd); +#endif + map_destroy(state->mtd); + kfree(state); + return 0; +} + +static struct platform_driver gpio_flash_driver = { + .probe = gpio_flash_probe, + .remove = __devexit_p(gpio_flash_remove), + .driver = { + .name = DRIVER_NAME, + }, +}; + +static int __init gpio_flash_init(void) +{ + return platform_driver_register(&gpio_flash_driver); +} +module_init(gpio_flash_init); + +static void __exit gpio_flash_exit(void) +{ + platform_driver_unregister(&gpio_flash_driver); +} +module_exit(gpio_flash_exit); + +MODULE_AUTHOR("Mike Frysinger <vapier@gentoo.org>"); +MODULE_DESCRIPTION("MTD map driver for flashes addressed physically and with gpios"); +MODULE_LICENSE("GPL"); diff --git a/drivers/mtd/maps/physmap_of.c b/drivers/mtd/maps/physmap_of.c index 39d357b2eb47..61e4eb48bb2d 100644 --- a/drivers/mtd/maps/physmap_of.c +++ b/drivers/mtd/maps/physmap_of.c @@ -190,6 +190,7 @@ static int __devinit of_flash_probe(struct of_device *dev, const u32 *p; int reg_tuple_size; struct mtd_info **mtd_list = NULL; + resource_size_t res_size; reg_tuple_size = (of_n_addr_cells(dp) + of_n_size_cells(dp)) * sizeof(u32); @@ -204,7 +205,7 @@ static int __devinit of_flash_probe(struct of_device *dev, dev_err(&dev->dev, "Malformed reg property on %s\n", dev->node->full_name); err = -EINVAL; - goto err_out; + goto err_flash_remove; } count /= reg_tuple_size; @@ -212,14 +213,14 @@ static int __devinit of_flash_probe(struct of_device *dev, info = kzalloc(sizeof(struct of_flash) + sizeof(struct of_flash_list) * count, GFP_KERNEL); if (!info) - goto err_out; - - mtd_list = kzalloc(sizeof(struct mtd_info) * count, GFP_KERNEL); - if (!info) - goto err_out; + goto err_flash_remove; dev_set_drvdata(&dev->dev, info); + mtd_list = kzalloc(sizeof(struct mtd_info) * count, GFP_KERNEL); + if (!mtd_list) + goto err_flash_remove; + for (i = 0; i < count; i++) { err = -ENXIO; if (of_address_to_resource(dp, i, &res)) { @@ -233,8 +234,8 @@ static int __devinit of_flash_probe(struct of_device *dev, (unsigned long long)res.end); err = -EBUSY; - info->list[i].res = request_mem_region(res.start, res.end - - res.start + 1, + res_size = resource_size(&res); + info->list[i].res = request_mem_region(res.start, res_size, dev_name(&dev->dev)); if (!info->list[i].res) goto err_out; @@ -249,7 +250,7 @@ static int __devinit of_flash_probe(struct of_device *dev, info->list[i].map.name = dev_name(&dev->dev); info->list[i].map.phys = res.start; - info->list[i].map.size = res.end - res.start + 1; + info->list[i].map.size = res_size; info->list[i].map.bankwidth = *width; err = -ENOMEM; @@ -338,6 +339,7 @@ static int __devinit of_flash_probe(struct of_device *dev, err_out: kfree(mtd_list); +err_flash_remove: of_flash_remove(dev); return err; @@ -360,6 +362,10 @@ static struct of_device_id of_flash_match[] = { .data = (void *)"jedec_probe", }, { + .compatible = "mtd-ram", + .data = (void *)"map_ram", + }, + { .type = "rom", .compatible = "direct-mapped" }, diff --git a/drivers/mtd/maps/plat-ram.c b/drivers/mtd/maps/plat-ram.c index 49c9ece76477..dafb91944e70 100644 --- a/drivers/mtd/maps/plat-ram.c +++ b/drivers/mtd/maps/plat-ram.c @@ -175,7 +175,7 @@ static int platram_probe(struct platform_device *pdev) /* setup map parameters */ info->map.phys = res->start; - info->map.size = (res->end - res->start) + 1; + info->map.size = resource_size(res); info->map.name = pdata->mapname != NULL ? (char *)pdata->mapname : (char *)pdev->name; info->map.bankwidth = pdata->bankwidth; diff --git a/drivers/mtd/maps/pmcmsp-flash.c b/drivers/mtd/maps/pmcmsp-flash.c index 4768bd5459d6..c8fd8da4bc87 100644 --- a/drivers/mtd/maps/pmcmsp-flash.c +++ b/drivers/mtd/maps/pmcmsp-flash.c @@ -50,7 +50,7 @@ static int fcnt; static int __init init_msp_flash(void) { - int i, j; + int i, j, ret = -ENOMEM; int offset, coff; char *env; int pcnt; @@ -75,14 +75,16 @@ static int __init init_msp_flash(void) printk(KERN_NOTICE "Found %d PMC flash devices\n", fcnt); msp_flash = kmalloc(fcnt * sizeof(struct map_info *), GFP_KERNEL); + if (!msp_flash) + return -ENOMEM; + msp_parts = kmalloc(fcnt * sizeof(struct mtd_partition *), GFP_KERNEL); + if (!msp_parts) + goto free_msp_flash; + msp_maps = kcalloc(fcnt, sizeof(struct mtd_info), GFP_KERNEL); - if (!msp_flash || !msp_parts || !msp_maps) { - kfree(msp_maps); - kfree(msp_parts); - kfree(msp_flash); - return -ENOMEM; - } + if (!msp_maps) + goto free_msp_parts; /* loop over the flash devices, initializing each */ for (i = 0; i < fcnt; i++) { @@ -100,13 +102,18 @@ static int __init init_msp_flash(void) msp_parts[i] = kcalloc(pcnt, sizeof(struct mtd_partition), GFP_KERNEL); + if (!msp_parts[i]) + goto cleanup_loop; /* now initialize the devices proper */ flash_name[5] = '0' + i; env = prom_getenv(flash_name); - if (sscanf(env, "%x:%x", &addr, &size) < 2) - return -ENXIO; + if (sscanf(env, "%x:%x", &addr, &size) < 2) { + ret = -ENXIO; + kfree(msp_parts[i]); + goto cleanup_loop; + } addr = CPHYSADDR(addr); printk(KERN_NOTICE @@ -122,13 +129,23 @@ static int __init init_msp_flash(void) */ if (size > CONFIG_MSP_FLASH_MAP_LIMIT) size = CONFIG_MSP_FLASH_MAP_LIMIT; + msp_maps[i].virt = ioremap(addr, size); + if (msp_maps[i].virt == NULL) { + ret = -ENXIO; + kfree(msp_parts[i]); + goto cleanup_loop; + } + msp_maps[i].bankwidth = 1; - msp_maps[i].name = strncpy(kmalloc(7, GFP_KERNEL), - flash_name, 7); + msp_maps[i].name = kmalloc(7, GFP_KERNEL); + if (!msp_maps[i].name) { + iounmap(msp_maps[i].virt); + kfree(msp_parts[i]); + goto cleanup_loop; + } - if (msp_maps[i].virt == NULL) - return -ENXIO; + msp_maps[i].name = strncpy(msp_maps[i].name, flash_name, 7); for (j = 0; j < pcnt; j++) { part_name[5] = '0' + i; @@ -136,8 +153,14 @@ static int __init init_msp_flash(void) env = prom_getenv(part_name); - if (sscanf(env, "%x:%x:%n", &offset, &size, &coff) < 2) - return -ENXIO; + if (sscanf(env, "%x:%x:%n", &offset, &size, + &coff) < 2) { + ret = -ENXIO; + kfree(msp_maps[i].name); + iounmap(msp_maps[i].virt); + kfree(msp_parts[i]); + goto cleanup_loop; + } msp_parts[i][j].size = size; msp_parts[i][j].offset = offset; @@ -152,18 +175,37 @@ static int __init init_msp_flash(void) add_mtd_partitions(msp_flash[i], msp_parts[i], pcnt); } else { printk(KERN_ERR "map probe failed for flash\n"); - return -ENXIO; + ret = -ENXIO; + kfree(msp_maps[i].name); + iounmap(msp_maps[i].virt); + kfree(msp_parts[i]); + goto cleanup_loop; } } return 0; + +cleanup_loop: + while (i--) { + del_mtd_partitions(msp_flash[i]); + map_destroy(msp_flash[i]); + kfree(msp_maps[i].name); + iounmap(msp_maps[i].virt); + kfree(msp_parts[i]); + } + kfree(msp_maps); +free_msp_parts: + kfree(msp_parts); +free_msp_flash: + kfree(msp_flash); + return ret; } static void __exit cleanup_msp_flash(void) { int i; - for (i = 0; i < sizeof(msp_flash) / sizeof(struct mtd_info **); i++) { + for (i = 0; i < fcnt; i++) { del_mtd_partitions(msp_flash[i]); map_destroy(msp_flash[i]); iounmap((void *)msp_maps[i].virt); diff --git a/drivers/mtd/maps/sa1100-flash.c b/drivers/mtd/maps/sa1100-flash.c index fdb97f3d30e9..d7a47574d21e 100644 --- a/drivers/mtd/maps/sa1100-flash.c +++ b/drivers/mtd/maps/sa1100-flash.c @@ -209,8 +209,8 @@ static int sa1100_probe_subdev(struct sa_subdev_info *subdev, struct resource *r } subdev->mtd->owner = THIS_MODULE; - printk(KERN_INFO "SA1100 flash: CFI device at 0x%08lx, %dMiB, " - "%d-bit\n", phys, subdev->mtd->size >> 20, + printk(KERN_INFO "SA1100 flash: CFI device at 0x%08lx, %uMiB, %d-bit\n", + phys, (unsigned)(subdev->mtd->size >> 20), subdev->map.bankwidth * 8); return 0; diff --git a/drivers/mtd/maps/uclinux.c b/drivers/mtd/maps/uclinux.c index d4314fb88212..35009294b435 100644 --- a/drivers/mtd/maps/uclinux.c +++ b/drivers/mtd/maps/uclinux.c @@ -89,7 +89,11 @@ static int __init uclinux_mtd_init(void) mtd->priv = mapp; uclinux_ram_mtdinfo = mtd; +#ifdef CONFIG_MTD_PARTITIONS add_mtd_partitions(mtd, uclinux_romfs, NUM_PARTITIONS); +#else + add_mtd_device(mtd); +#endif return(0); } @@ -99,7 +103,11 @@ static int __init uclinux_mtd_init(void) static void __exit uclinux_mtd_cleanup(void) { if (uclinux_ram_mtdinfo) { +#ifdef CONFIG_MTD_PARTITIONS del_mtd_partitions(uclinux_ram_mtdinfo); +#else + del_mtd_device(uclinux_ram_mtdinfo); +#endif map_destroy(uclinux_ram_mtdinfo); uclinux_ram_mtdinfo = NULL; } diff --git a/drivers/mtd/mtd_blkdevs.c b/drivers/mtd/mtd_blkdevs.c index 0acbf4f5be50..8ca17a3e96ea 100644 --- a/drivers/mtd/mtd_blkdevs.c +++ b/drivers/mtd/mtd_blkdevs.c @@ -32,14 +32,6 @@ struct mtd_blkcore_priv { spinlock_t queue_lock; }; -static int blktrans_discard_request(struct request_queue *q, - struct request *req) -{ - req->cmd_type = REQ_TYPE_LINUX_BLOCK; - req->cmd[0] = REQ_LB_OP_DISCARD; - return 0; -} - static int do_blktrans_request(struct mtd_blktrans_ops *tr, struct mtd_blktrans_dev *dev, struct request *req) @@ -52,10 +44,6 @@ static int do_blktrans_request(struct mtd_blktrans_ops *tr, buf = req->buffer; - if (req->cmd_type == REQ_TYPE_LINUX_BLOCK && - req->cmd[0] == REQ_LB_OP_DISCARD) - return tr->discard(dev, block, nsect); - if (!blk_fs_request(req)) return -EIO; @@ -63,6 +51,9 @@ static int do_blktrans_request(struct mtd_blktrans_ops *tr, get_capacity(req->rq_disk)) return -EIO; + if (blk_discard_rq(req)) + return tr->discard(dev, block, nsect); + switch(rq_data_dir(req)) { case READ: for (; nsect > 0; nsect--, block++, buf += tr->blksize) @@ -380,8 +371,8 @@ int register_mtd_blktrans(struct mtd_blktrans_ops *tr) tr->blkcore_priv->rq->queuedata = tr; blk_queue_logical_block_size(tr->blkcore_priv->rq, tr->blksize); if (tr->discard) - blk_queue_set_discard(tr->blkcore_priv->rq, - blktrans_discard_request); + queue_flag_set_unlocked(QUEUE_FLAG_DISCARD, + tr->blkcore_priv->rq); tr->blkshift = ffs(tr->blksize) - 1; diff --git a/drivers/mtd/mtdblock.c b/drivers/mtd/mtdblock.c index 2d70295a5fa3..9f41b1a853c1 100644 --- a/drivers/mtd/mtdblock.c +++ b/drivers/mtd/mtdblock.c @@ -84,7 +84,7 @@ static int erase_write (struct mtd_info *mtd, unsigned long pos, remove_wait_queue(&wait_q, &wait); /* - * Next, writhe data to flash. + * Next, write the data to flash. */ ret = mtd->write(mtd, pos, len, &retlen, buf); diff --git a/drivers/mtd/mtdconcat.c b/drivers/mtd/mtdconcat.c index 792b547786b8..db6de74082ad 100644 --- a/drivers/mtd/mtdconcat.c +++ b/drivers/mtd/mtdconcat.c @@ -427,7 +427,7 @@ static int concat_erase(struct mtd_info *mtd, struct erase_info *instr) * to-be-erased area begins. Verify that the starting * offset is aligned to this region's erase size: */ - if (instr->addr & (erase_regions[i].erasesize - 1)) + if (i < 0 || instr->addr & (erase_regions[i].erasesize - 1)) return -EINVAL; /* @@ -440,8 +440,8 @@ static int concat_erase(struct mtd_info *mtd, struct erase_info *instr) /* * check if the ending offset is aligned to this region's erase size */ - if ((instr->addr + instr->len) & (erase_regions[i].erasesize - - 1)) + if (i < 0 || ((instr->addr + instr->len) & + (erase_regions[i].erasesize - 1))) return -EINVAL; } diff --git a/drivers/mtd/mtdcore.c b/drivers/mtd/mtdcore.c index 69007a6eff50..467a4f177bfb 100644 --- a/drivers/mtd/mtdcore.c +++ b/drivers/mtd/mtdcore.c @@ -213,11 +213,11 @@ static struct attribute *mtd_attrs[] = { NULL, }; -struct attribute_group mtd_group = { +static struct attribute_group mtd_group = { .attrs = mtd_attrs, }; -const struct attribute_group *mtd_groups[] = { +static const struct attribute_group *mtd_groups[] = { &mtd_group, NULL, }; diff --git a/drivers/mtd/mtdpart.c b/drivers/mtd/mtdpart.c index 742504ea96f5..b8043a9ba32d 100644 --- a/drivers/mtd/mtdpart.c +++ b/drivers/mtd/mtdpart.c @@ -453,7 +453,8 @@ static struct mtd_part *add_one_partition(struct mtd_info *master, for (i = 0; i < max && regions[i].offset <= slave->offset; i++) ; /* The loop searched for the region _behind_ the first one */ - i--; + if (i > 0) + i--; /* Pick biggest erasesize */ for (; i < max && regions[i].offset < end; i++) { diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig index ce96c091f01b..2fda0b615246 100644 --- a/drivers/mtd/nand/Kconfig +++ b/drivers/mtd/nand/Kconfig @@ -80,6 +80,23 @@ config MTD_NAND_OMAP2 help Support for NAND flash on Texas Instruments OMAP2 and OMAP3 platforms. +config MTD_NAND_OMAP_PREFETCH + bool "GPMC prefetch support for NAND Flash device" + depends on MTD_NAND && MTD_NAND_OMAP2 + default y + help + The NAND device can be accessed for Read/Write using GPMC PREFETCH engine + to improve the performance. + +config MTD_NAND_OMAP_PREFETCH_DMA + depends on MTD_NAND_OMAP_PREFETCH + bool "DMA mode" + default n + help + The GPMC PREFETCH engine can be configured eigther in MPU interrupt mode + or in DMA interrupt mode. + Say y for DMA mode or MPU mode will be used + config MTD_NAND_TS7250 tristate "NAND Flash device on TS-7250 board" depends on MACH_TS72XX @@ -426,6 +443,12 @@ config MTD_NAND_MXC This enables the driver for the NAND flash controller on the MXC processors. +config MTD_NAND_NOMADIK + tristate "ST Nomadik 8815 NAND support" + depends on ARCH_NOMADIK + help + Driver for the NAND flash controller on the Nomadik, with ECC. + config MTD_NAND_SH_FLCTL tristate "Support for NAND on Renesas SuperH FLCTL" depends on MTD_NAND && SUPERH && CPU_SUBTYPE_SH7723 @@ -452,4 +475,11 @@ config MTD_NAND_SOCRATES help Enables support for NAND Flash chips wired onto Socrates board. +config MTD_NAND_W90P910 + tristate "Support for NAND on w90p910 evaluation board." + depends on ARCH_W90X900 && MTD_PARTITIONS + help + This enables the driver for the NAND Flash on evaluation board based + on w90p910. + endif # MTD_NAND diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile index f3a786b3cff3..6950d3dabf10 100644 --- a/drivers/mtd/nand/Makefile +++ b/drivers/mtd/nand/Makefile @@ -40,5 +40,7 @@ obj-$(CONFIG_MTD_NAND_SH_FLCTL) += sh_flctl.o obj-$(CONFIG_MTD_NAND_MXC) += mxc_nand.o obj-$(CONFIG_MTD_NAND_SOCRATES) += socrates_nand.o obj-$(CONFIG_MTD_NAND_TXX9NDFMC) += txx9ndfmc.o +obj-$(CONFIG_MTD_NAND_W90P910) += w90p910_nand.o +obj-$(CONFIG_MTD_NAND_NOMADIK) += nomadik_nand.o nand-objs := nand_base.o nand_bbt.o diff --git a/drivers/mtd/nand/atmel_nand.c b/drivers/mtd/nand/atmel_nand.c index 20c828ba9405..f8e9975c86e5 100644 --- a/drivers/mtd/nand/atmel_nand.c +++ b/drivers/mtd/nand/atmel_nand.c @@ -218,7 +218,7 @@ static int atmel_nand_calculate(struct mtd_info *mtd, * buf: buffer to store read data */ static int atmel_nand_read_page(struct mtd_info *mtd, - struct nand_chip *chip, uint8_t *buf) + struct nand_chip *chip, uint8_t *buf, int page) { int eccsize = chip->ecc.size; int eccbytes = chip->ecc.bytes; diff --git a/drivers/mtd/nand/cafe_nand.c b/drivers/mtd/nand/cafe_nand.c index 1b4690bdfdb3..c828d9ac7bd7 100644 --- a/drivers/mtd/nand/cafe_nand.c +++ b/drivers/mtd/nand/cafe_nand.c @@ -381,7 +381,7 @@ static int cafe_nand_read_oob(struct mtd_info *mtd, struct nand_chip *chip, * we need a special oob layout and handling. */ static int cafe_nand_read_page(struct mtd_info *mtd, struct nand_chip *chip, - uint8_t *buf) + uint8_t *buf, int page) { struct cafe_priv *cafe = mtd->priv; diff --git a/drivers/mtd/nand/davinci_nand.c b/drivers/mtd/nand/davinci_nand.c index 0fad6487e6f4..f13f5b9afaf7 100644 --- a/drivers/mtd/nand/davinci_nand.c +++ b/drivers/mtd/nand/davinci_nand.c @@ -348,6 +348,12 @@ compare: if (!(syndrome[0] | syndrome[1] | syndrome[2] | syndrome[3])) return 0; + /* + * Clear any previous address calculation by doing a dummy read of an + * error address register. + */ + davinci_nand_readl(info, NAND_ERR_ADD1_OFFSET); + /* Start address calculation, and wait for it to complete. * We _could_ start reading more data while this is working, * to speed up the overall page read. @@ -359,8 +365,10 @@ compare: switch ((fsr >> 8) & 0x0f) { case 0: /* no error, should not happen */ + davinci_nand_readl(info, NAND_ERR_ERRVAL1_OFFSET); return 0; case 1: /* five or more errors detected */ + davinci_nand_readl(info, NAND_ERR_ERRVAL1_OFFSET); return -EIO; case 2: /* error addresses computed */ case 3: @@ -500,6 +508,26 @@ static struct nand_ecclayout hwecc4_small __initconst = { }, }; +/* An ECC layout for using 4-bit ECC with large-page (2048bytes) flash, + * storing ten ECC bytes plus the manufacturer's bad block marker byte, + * and not overlapping the default BBT markers. + */ +static struct nand_ecclayout hwecc4_2048 __initconst = { + .eccbytes = 40, + .eccpos = { + /* at the end of spare sector */ + 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, + 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, + 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, + 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, + }, + .oobfree = { + /* 2 bytes at offset 0 hold manufacturer badblock markers */ + {.offset = 2, .length = 22, }, + /* 5 bytes at offset 8 hold BBT markers */ + /* 8 bytes at offset 16 hold JFFS2 clean markers */ + }, +}; static int __init nand_davinci_probe(struct platform_device *pdev) { @@ -690,15 +718,20 @@ static int __init nand_davinci_probe(struct platform_device *pdev) info->mtd.oobsize - 16; goto syndrome_done; } + if (chunks == 4) { + info->ecclayout = hwecc4_2048; + info->chip.ecc.mode = NAND_ECC_HW_OOB_FIRST; + goto syndrome_done; + } - /* For large page chips we'll be wanting to use a - * not-yet-implemented mode that reads OOB data - * before reading the body of the page, to avoid - * the "infix OOB" model of NAND_ECC_HW_SYNDROME - * (and preserve manufacturer badblock markings). + /* 4KiB page chips are not yet supported. The eccpos from + * nand_ecclayout cannot hold 80 bytes and change to eccpos[] + * breaks userspace ioctl interface with mtd-utils. Once we + * resolve this issue, NAND_ECC_HW_OOB_FIRST mode can be used + * for the 4KiB page chips. */ dev_warn(&pdev->dev, "no 4-bit ECC support yet " - "for large page NAND\n"); + "for 4KiB-page NAND\n"); ret = -EIO; goto err_scan; diff --git a/drivers/mtd/nand/fsl_elbc_nand.c b/drivers/mtd/nand/fsl_elbc_nand.c index 1f6eb2578717..ddd37d2554ed 100644 --- a/drivers/mtd/nand/fsl_elbc_nand.c +++ b/drivers/mtd/nand/fsl_elbc_nand.c @@ -739,7 +739,8 @@ static int fsl_elbc_chip_init_tail(struct mtd_info *mtd) static int fsl_elbc_read_page(struct mtd_info *mtd, struct nand_chip *chip, - uint8_t *buf) + uint8_t *buf, + int page) { fsl_elbc_read_buf(mtd, buf, mtd->writesize); fsl_elbc_read_buf(mtd, chip->oob_poi, mtd->oobsize); diff --git a/drivers/mtd/nand/mxc_nand.c b/drivers/mtd/nand/mxc_nand.c index 76beea40d2cf..65b26d5a5c0d 100644 --- a/drivers/mtd/nand/mxc_nand.c +++ b/drivers/mtd/nand/mxc_nand.c @@ -857,6 +857,17 @@ static void mxc_nand_command(struct mtd_info *mtd, unsigned command, } } +/* Define some generic bad / good block scan pattern which are used + * while scanning a device for factory marked good / bad blocks. */ +static uint8_t scan_ff_pattern[] = { 0xff, 0xff }; + +static struct nand_bbt_descr smallpage_memorybased = { + .options = NAND_BBT_SCAN2NDPAGE, + .offs = 5, + .len = 1, + .pattern = scan_ff_pattern +}; + static int __init mxcnd_probe(struct platform_device *pdev) { struct nand_chip *this; @@ -973,7 +984,10 @@ static int __init mxcnd_probe(struct platform_device *pdev) goto escan; } - host->pagesize_2k = (mtd->writesize == 2048) ? 1 : 0; + if (mtd->writesize == 2048) { + host->pagesize_2k = 1; + this->badblock_pattern = &smallpage_memorybased; + } if (this->ecc.mode == NAND_ECC_HW) { switch (mtd->oobsize) { diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 8c21b89d2d0c..2957cc70da3d 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -688,8 +688,7 @@ nand_get_device(struct nand_chip *chip, struct mtd_info *mtd, int new_state) retry: spin_lock(lock); - /* Hardware controller shared among independend devices */ - /* Hardware controller shared among independend devices */ + /* Hardware controller shared among independent devices */ if (!chip->controller->active) chip->controller->active = chip; @@ -762,11 +761,12 @@ static int nand_wait(struct mtd_info *mtd, struct nand_chip *chip) * @mtd: mtd info structure * @chip: nand chip info structure * @buf: buffer to store read data + * @page: page number to read * * Not for syndrome calculating ecc controllers, which use a special oob layout */ static int nand_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip, - uint8_t *buf) + uint8_t *buf, int page) { chip->read_buf(mtd, buf, mtd->writesize); chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); @@ -778,11 +778,12 @@ static int nand_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip, * @mtd: mtd info structure * @chip: nand chip info structure * @buf: buffer to store read data + * @page: page number to read * * We need a special oob layout and handling even when OOB isn't used. */ static int nand_read_page_raw_syndrome(struct mtd_info *mtd, struct nand_chip *chip, - uint8_t *buf) + uint8_t *buf, int page) { int eccsize = chip->ecc.size; int eccbytes = chip->ecc.bytes; @@ -819,9 +820,10 @@ static int nand_read_page_raw_syndrome(struct mtd_info *mtd, struct nand_chip *c * @mtd: mtd info structure * @chip: nand chip info structure * @buf: buffer to store read data + * @page: page number to read */ static int nand_read_page_swecc(struct mtd_info *mtd, struct nand_chip *chip, - uint8_t *buf) + uint8_t *buf, int page) { int i, eccsize = chip->ecc.size; int eccbytes = chip->ecc.bytes; @@ -831,7 +833,7 @@ static int nand_read_page_swecc(struct mtd_info *mtd, struct nand_chip *chip, uint8_t *ecc_code = chip->buffers->ecccode; uint32_t *eccpos = chip->ecc.layout->eccpos; - chip->ecc.read_page_raw(mtd, chip, buf); + chip->ecc.read_page_raw(mtd, chip, buf, page); for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) chip->ecc.calculate(mtd, p, &ecc_calc[i]); @@ -940,11 +942,12 @@ static int nand_read_subpage(struct mtd_info *mtd, struct nand_chip *chip, uint3 * @mtd: mtd info structure * @chip: nand chip info structure * @buf: buffer to store read data + * @page: page number to read * * Not for syndrome calculating ecc controllers which need a special oob layout */ static int nand_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, - uint8_t *buf) + uint8_t *buf, int page) { int i, eccsize = chip->ecc.size; int eccbytes = chip->ecc.bytes; @@ -980,16 +983,66 @@ static int nand_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, } /** + * nand_read_page_hwecc_oob_first - [REPLACABLE] hw ecc, read oob first + * @mtd: mtd info structure + * @chip: nand chip info structure + * @buf: buffer to store read data + * @page: page number to read + * + * Hardware ECC for large page chips, require OOB to be read first. + * For this ECC mode, the write_page method is re-used from ECC_HW. + * These methods read/write ECC from the OOB area, unlike the + * ECC_HW_SYNDROME support with multiple ECC steps, follows the + * "infix ECC" scheme and reads/writes ECC from the data area, by + * overwriting the NAND manufacturer bad block markings. + */ +static int nand_read_page_hwecc_oob_first(struct mtd_info *mtd, + struct nand_chip *chip, uint8_t *buf, int page) +{ + int i, eccsize = chip->ecc.size; + int eccbytes = chip->ecc.bytes; + int eccsteps = chip->ecc.steps; + uint8_t *p = buf; + uint8_t *ecc_code = chip->buffers->ecccode; + uint32_t *eccpos = chip->ecc.layout->eccpos; + uint8_t *ecc_calc = chip->buffers->ecccalc; + + /* Read the OOB area first */ + chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page); + chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); + chip->cmdfunc(mtd, NAND_CMD_READ0, 0, page); + + for (i = 0; i < chip->ecc.total; i++) + ecc_code[i] = chip->oob_poi[eccpos[i]]; + + for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { + int stat; + + chip->ecc.hwctl(mtd, NAND_ECC_READ); + chip->read_buf(mtd, p, eccsize); + chip->ecc.calculate(mtd, p, &ecc_calc[i]); + + stat = chip->ecc.correct(mtd, p, &ecc_code[i], NULL); + if (stat < 0) + mtd->ecc_stats.failed++; + else + mtd->ecc_stats.corrected += stat; + } + return 0; +} + +/** * nand_read_page_syndrome - [REPLACABLE] hardware ecc syndrom based page read * @mtd: mtd info structure * @chip: nand chip info structure * @buf: buffer to store read data + * @page: page number to read * * The hw generator calculates the error syndrome automatically. Therefor * we need a special oob layout and handling. */ static int nand_read_page_syndrome(struct mtd_info *mtd, struct nand_chip *chip, - uint8_t *buf) + uint8_t *buf, int page) { int i, eccsize = chip->ecc.size; int eccbytes = chip->ecc.bytes; @@ -1131,11 +1184,13 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from, /* Now read the page into the buffer */ if (unlikely(ops->mode == MTD_OOB_RAW)) - ret = chip->ecc.read_page_raw(mtd, chip, bufpoi); + ret = chip->ecc.read_page_raw(mtd, chip, + bufpoi, page); else if (!aligned && NAND_SUBPAGE_READ(chip) && !oob) ret = chip->ecc.read_subpage(mtd, chip, col, bytes, bufpoi); else - ret = chip->ecc.read_page(mtd, chip, bufpoi); + ret = chip->ecc.read_page(mtd, chip, bufpoi, + page); if (ret < 0) break; @@ -1413,8 +1468,8 @@ static int nand_do_read_oob(struct mtd_info *mtd, loff_t from, int len; uint8_t *buf = ops->oobbuf; - DEBUG(MTD_DEBUG_LEVEL3, "nand_read_oob: from = 0x%08Lx, len = %i\n", - (unsigned long long)from, readlen); + DEBUG(MTD_DEBUG_LEVEL3, "%s: from = 0x%08Lx, len = %i\n", + __func__, (unsigned long long)from, readlen); if (ops->mode == MTD_OOB_AUTO) len = chip->ecc.layout->oobavail; @@ -1422,8 +1477,8 @@ static int nand_do_read_oob(struct mtd_info *mtd, loff_t from, len = mtd->oobsize; if (unlikely(ops->ooboffs >= len)) { - DEBUG(MTD_DEBUG_LEVEL0, "nand_read_oob: " - "Attempt to start read outside oob\n"); + DEBUG(MTD_DEBUG_LEVEL0, "%s: Attempt to start read " + "outside oob\n", __func__); return -EINVAL; } @@ -1431,8 +1486,8 @@ static int nand_do_read_oob(struct mtd_info *mtd, loff_t from, if (unlikely(from >= mtd->size || ops->ooboffs + readlen > ((mtd->size >> chip->page_shift) - (from >> chip->page_shift)) * len)) { - DEBUG(MTD_DEBUG_LEVEL0, "nand_read_oob: " - "Attempt read beyond end of device\n"); + DEBUG(MTD_DEBUG_LEVEL0, "%s: Attempt read beyond end " + "of device\n", __func__); return -EINVAL; } @@ -1506,8 +1561,8 @@ static int nand_read_oob(struct mtd_info *mtd, loff_t from, /* Do not allow reads past end of device */ if (ops->datbuf && (from + ops->len) > mtd->size) { - DEBUG(MTD_DEBUG_LEVEL0, "nand_read_oob: " - "Attempt read beyond end of device\n"); + DEBUG(MTD_DEBUG_LEVEL0, "%s: Attempt read " + "beyond end of device\n", __func__); return -EINVAL; } @@ -1816,8 +1871,8 @@ static int nand_do_write_ops(struct mtd_info *mtd, loff_t to, /* reject writes, which are not page aligned */ if (NOTALIGNED(to) || NOTALIGNED(ops->len)) { - printk(KERN_NOTICE "nand_write: " - "Attempt to write not page aligned data\n"); + printk(KERN_NOTICE "%s: Attempt to write not " + "page aligned data\n", __func__); return -EINVAL; } @@ -1944,8 +1999,8 @@ static int nand_do_write_oob(struct mtd_info *mtd, loff_t to, int chipnr, page, status, len; struct nand_chip *chip = mtd->priv; - DEBUG(MTD_DEBUG_LEVEL3, "nand_write_oob: to = 0x%08x, len = %i\n", - (unsigned int)to, (int)ops->ooblen); + DEBUG(MTD_DEBUG_LEVEL3, "%s: to = 0x%08x, len = %i\n", + __func__, (unsigned int)to, (int)ops->ooblen); if (ops->mode == MTD_OOB_AUTO) len = chip->ecc.layout->oobavail; @@ -1954,14 +2009,14 @@ static int nand_do_write_oob(struct mtd_info *mtd, loff_t to, /* Do not allow write past end of page */ if ((ops->ooboffs + ops->ooblen) > len) { - DEBUG(MTD_DEBUG_LEVEL0, "nand_write_oob: " - "Attempt to write past end of page\n"); + DEBUG(MTD_DEBUG_LEVEL0, "%s: Attempt to write " + "past end of page\n", __func__); return -EINVAL; } if (unlikely(ops->ooboffs >= len)) { - DEBUG(MTD_DEBUG_LEVEL0, "nand_do_write_oob: " - "Attempt to start write outside oob\n"); + DEBUG(MTD_DEBUG_LEVEL0, "%s: Attempt to start " + "write outside oob\n", __func__); return -EINVAL; } @@ -1970,8 +2025,8 @@ static int nand_do_write_oob(struct mtd_info *mtd, loff_t to, ops->ooboffs + ops->ooblen > ((mtd->size >> chip->page_shift) - (to >> chip->page_shift)) * len)) { - DEBUG(MTD_DEBUG_LEVEL0, "nand_do_write_oob: " - "Attempt write beyond end of device\n"); + DEBUG(MTD_DEBUG_LEVEL0, "%s: Attempt write beyond " + "end of device\n", __func__); return -EINVAL; } @@ -2026,8 +2081,8 @@ static int nand_write_oob(struct mtd_info *mtd, loff_t to, /* Do not allow writes past end of device */ if (ops->datbuf && (to + ops->len) > mtd->size) { - DEBUG(MTD_DEBUG_LEVEL0, "nand_write_oob: " - "Attempt write beyond end of device\n"); + DEBUG(MTD_DEBUG_LEVEL0, "%s: Attempt write beyond " + "end of device\n", __func__); return -EINVAL; } @@ -2117,26 +2172,27 @@ int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr, unsigned int bbt_masked_page = 0xffffffff; loff_t len; - DEBUG(MTD_DEBUG_LEVEL3, "nand_erase: start = 0x%012llx, len = %llu\n", - (unsigned long long)instr->addr, (unsigned long long)instr->len); + DEBUG(MTD_DEBUG_LEVEL3, "%s: start = 0x%012llx, len = %llu\n", + __func__, (unsigned long long)instr->addr, + (unsigned long long)instr->len); /* Start address must align on block boundary */ if (instr->addr & ((1 << chip->phys_erase_shift) - 1)) { - DEBUG(MTD_DEBUG_LEVEL0, "nand_erase: Unaligned address\n"); + DEBUG(MTD_DEBUG_LEVEL0, "%s: Unaligned address\n", __func__); return -EINVAL; } /* Length must align on block boundary */ if (instr->len & ((1 << chip->phys_erase_shift) - 1)) { - DEBUG(MTD_DEBUG_LEVEL0, "nand_erase: " - "Length not block aligned\n"); + DEBUG(MTD_DEBUG_LEVEL0, "%s: Length not block aligned\n", + __func__); return -EINVAL; } /* Do not allow erase past end of device */ if ((instr->len + instr->addr) > mtd->size) { - DEBUG(MTD_DEBUG_LEVEL0, "nand_erase: " - "Erase past end of device\n"); + DEBUG(MTD_DEBUG_LEVEL0, "%s: Erase past end of device\n", + __func__); return -EINVAL; } @@ -2157,8 +2213,8 @@ int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr, /* Check, if it is write protected */ if (nand_check_wp(mtd)) { - DEBUG(MTD_DEBUG_LEVEL0, "nand_erase: " - "Device is write protected!!!\n"); + DEBUG(MTD_DEBUG_LEVEL0, "%s: Device is write protected!!!\n", + __func__); instr->state = MTD_ERASE_FAILED; goto erase_exit; } @@ -2183,8 +2239,8 @@ int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr, */ if (nand_block_checkbad(mtd, ((loff_t) page) << chip->page_shift, 0, allowbbt)) { - printk(KERN_WARNING "nand_erase: attempt to erase a " - "bad block at page 0x%08x\n", page); + printk(KERN_WARNING "%s: attempt to erase a bad block " + "at page 0x%08x\n", __func__, page); instr->state = MTD_ERASE_FAILED; goto erase_exit; } @@ -2211,8 +2267,8 @@ int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr, /* See if block erase succeeded */ if (status & NAND_STATUS_FAIL) { - DEBUG(MTD_DEBUG_LEVEL0, "nand_erase: " - "Failed erase, page 0x%08x\n", page); + DEBUG(MTD_DEBUG_LEVEL0, "%s: Failed erase, " + "page 0x%08x\n", __func__, page); instr->state = MTD_ERASE_FAILED; instr->fail_addr = ((loff_t)page << chip->page_shift); @@ -2272,9 +2328,9 @@ int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr, if (!rewrite_bbt[chipnr]) continue; /* update the BBT for chip */ - DEBUG(MTD_DEBUG_LEVEL0, "nand_erase_nand: nand_update_bbt " - "(%d:0x%0llx 0x%0x)\n", chipnr, rewrite_bbt[chipnr], - chip->bbt_td->pages[chipnr]); + DEBUG(MTD_DEBUG_LEVEL0, "%s: nand_update_bbt " + "(%d:0x%0llx 0x%0x)\n", __func__, chipnr, + rewrite_bbt[chipnr], chip->bbt_td->pages[chipnr]); nand_update_bbt(mtd, rewrite_bbt[chipnr]); } @@ -2292,7 +2348,7 @@ static void nand_sync(struct mtd_info *mtd) { struct nand_chip *chip = mtd->priv; - DEBUG(MTD_DEBUG_LEVEL3, "nand_sync: called\n"); + DEBUG(MTD_DEBUG_LEVEL3, "%s: called\n", __func__); /* Grab the lock and see if the device is available */ nand_get_device(chip, mtd, FL_SYNCING); @@ -2356,8 +2412,8 @@ static void nand_resume(struct mtd_info *mtd) if (chip->state == FL_PM_SUSPENDED) nand_release_device(mtd); else - printk(KERN_ERR "nand_resume() called for a chip which is not " - "in suspended state\n"); + printk(KERN_ERR "%s called for a chip which is not " + "in suspended state\n", __func__); } /* @@ -2671,6 +2727,17 @@ int nand_scan_tail(struct mtd_info *mtd) */ switch (chip->ecc.mode) { + case NAND_ECC_HW_OOB_FIRST: + /* Similar to NAND_ECC_HW, but a separate read_page handle */ + if (!chip->ecc.calculate || !chip->ecc.correct || + !chip->ecc.hwctl) { + printk(KERN_WARNING "No ECC functions supplied; " + "Hardware ECC not possible\n"); + BUG(); + } + if (!chip->ecc.read_page) + chip->ecc.read_page = nand_read_page_hwecc_oob_first; + case NAND_ECC_HW: /* Use standard hwecc read page function ? */ if (!chip->ecc.read_page) @@ -2693,7 +2760,7 @@ int nand_scan_tail(struct mtd_info *mtd) chip->ecc.read_page == nand_read_page_hwecc || !chip->ecc.write_page || chip->ecc.write_page == nand_write_page_hwecc)) { - printk(KERN_WARNING "No ECC functions supplied, " + printk(KERN_WARNING "No ECC functions supplied; " "Hardware ECC not possible\n"); BUG(); } @@ -2728,7 +2795,8 @@ int nand_scan_tail(struct mtd_info *mtd) chip->ecc.write_page_raw = nand_write_page_raw; chip->ecc.read_oob = nand_read_oob_std; chip->ecc.write_oob = nand_write_oob_std; - chip->ecc.size = 256; + if (!chip->ecc.size) + chip->ecc.size = 256; chip->ecc.bytes = 3; break; @@ -2858,7 +2926,8 @@ int nand_scan(struct mtd_info *mtd, int maxchips) /* Many callers got this wrong, so check for it for a while... */ if (!mtd->owner && caller_is_module()) { - printk(KERN_CRIT "nand_scan() called with NULL mtd->owner!\n"); + printk(KERN_CRIT "%s called with NULL mtd->owner!\n", + __func__); BUG(); } diff --git a/drivers/mtd/nand/nand_ecc.c b/drivers/mtd/nand/nand_ecc.c index c0cb87d6d16e..db7ae9d6a296 100644 --- a/drivers/mtd/nand/nand_ecc.c +++ b/drivers/mtd/nand/nand_ecc.c @@ -417,22 +417,22 @@ int nand_calculate_ecc(struct mtd_info *mtd, const unsigned char *buf, EXPORT_SYMBOL(nand_calculate_ecc); /** - * nand_correct_data - [NAND Interface] Detect and correct bit error(s) - * @mtd: MTD block structure + * __nand_correct_data - [NAND Interface] Detect and correct bit error(s) * @buf: raw data read from the chip * @read_ecc: ECC from the chip * @calc_ecc: the ECC calculated from raw data + * @eccsize: data bytes per ecc step (256 or 512) * - * Detect and correct a 1 bit error for 256/512 byte block + * Detect and correct a 1 bit error for eccsize byte block */ -int nand_correct_data(struct mtd_info *mtd, unsigned char *buf, - unsigned char *read_ecc, unsigned char *calc_ecc) +int __nand_correct_data(unsigned char *buf, + unsigned char *read_ecc, unsigned char *calc_ecc, + unsigned int eccsize) { unsigned char b0, b1, b2, bit_addr; unsigned int byte_addr; /* 256 or 512 bytes/ecc */ - const uint32_t eccsize_mult = - (((struct nand_chip *)mtd->priv)->ecc.size) >> 8; + const uint32_t eccsize_mult = eccsize >> 8; /* * b0 to b2 indicate which bit is faulty (if any) @@ -495,6 +495,23 @@ int nand_correct_data(struct mtd_info *mtd, unsigned char *buf, printk(KERN_ERR "uncorrectable error : "); return -1; } +EXPORT_SYMBOL(__nand_correct_data); + +/** + * nand_correct_data - [NAND Interface] Detect and correct bit error(s) + * @mtd: MTD block structure + * @buf: raw data read from the chip + * @read_ecc: ECC from the chip + * @calc_ecc: the ECC calculated from raw data + * + * Detect and correct a 1 bit error for 256/512 byte block + */ +int nand_correct_data(struct mtd_info *mtd, unsigned char *buf, + unsigned char *read_ecc, unsigned char *calc_ecc) +{ + return __nand_correct_data(buf, read_ecc, calc_ecc, + ((struct nand_chip *)mtd->priv)->ecc.size); +} EXPORT_SYMBOL(nand_correct_data); MODULE_LICENSE("GPL"); diff --git a/drivers/mtd/nand/ndfc.c b/drivers/mtd/nand/ndfc.c index 89bf85af642c..40b5658bdbe6 100644 --- a/drivers/mtd/nand/ndfc.c +++ b/drivers/mtd/nand/ndfc.c @@ -102,8 +102,8 @@ static int ndfc_calculate_ecc(struct mtd_info *mtd, wmb(); ecc = in_be32(ndfc->ndfcbase + NDFC_ECC); /* The NDFC uses Smart Media (SMC) bytes order */ - ecc_code[0] = p[2]; - ecc_code[1] = p[1]; + ecc_code[0] = p[1]; + ecc_code[1] = p[2]; ecc_code[2] = p[3]; return 0; diff --git a/drivers/mtd/nand/nomadik_nand.c b/drivers/mtd/nand/nomadik_nand.c new file mode 100644 index 000000000000..7c302d55910e --- /dev/null +++ b/drivers/mtd/nand/nomadik_nand.c @@ -0,0 +1,250 @@ +/* + * drivers/mtd/nand/nomadik_nand.c + * + * Overview: + * Driver for on-board NAND flash on Nomadik Platforms + * + * Copyright © 2007 STMicroelectronics Pvt. Ltd. + * Author: Sachin Verma <sachin.verma@st.com> + * + * Copyright © 2009 Alessandro Rubini + * + * 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. + * + */ + +#include <linux/init.h> +#include <linux/module.h> +#include <linux/types.h> +#include <linux/mtd/mtd.h> +#include <linux/mtd/nand.h> +#include <linux/mtd/nand_ecc.h> +#include <linux/platform_device.h> +#include <linux/mtd/partitions.h> +#include <linux/io.h> +#include <mach/nand.h> +#include <mach/fsmc.h> + +#include <mtd/mtd-abi.h> + +struct nomadik_nand_host { + struct mtd_info mtd; + struct nand_chip nand; + void __iomem *data_va; + void __iomem *cmd_va; + void __iomem *addr_va; + struct nand_bbt_descr *bbt_desc; +}; + +static struct nand_ecclayout nomadik_ecc_layout = { + .eccbytes = 3 * 4, + .eccpos = { /* each subpage has 16 bytes: pos 2,3,4 hosts ECC */ + 0x02, 0x03, 0x04, + 0x12, 0x13, 0x14, + 0x22, 0x23, 0x24, + 0x32, 0x33, 0x34}, + /* let's keep bytes 5,6,7 for us, just in case we change ECC algo */ + .oobfree = { {0x08, 0x08}, {0x18, 0x08}, {0x28, 0x08}, {0x38, 0x08} }, +}; + +static void nomadik_ecc_control(struct mtd_info *mtd, int mode) +{ + /* No need to enable hw ecc, it's on by default */ +} + +static void nomadik_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl) +{ + struct nand_chip *nand = mtd->priv; + struct nomadik_nand_host *host = nand->priv; + + if (cmd == NAND_CMD_NONE) + return; + + if (ctrl & NAND_CLE) + writeb(cmd, host->cmd_va); + else + writeb(cmd, host->addr_va); +} + +static int nomadik_nand_probe(struct platform_device *pdev) +{ + struct nomadik_nand_platform_data *pdata = pdev->dev.platform_data; + struct nomadik_nand_host *host; + struct mtd_info *mtd; + struct nand_chip *nand; + struct resource *res; + int ret = 0; + + /* Allocate memory for the device structure (and zero it) */ + host = kzalloc(sizeof(struct nomadik_nand_host), GFP_KERNEL); + if (!host) { + dev_err(&pdev->dev, "Failed to allocate device structure.\n"); + return -ENOMEM; + } + + /* Call the client's init function, if any */ + if (pdata->init) + ret = pdata->init(); + if (ret < 0) { + dev_err(&pdev->dev, "Init function failed\n"); + goto err; + } + + /* ioremap three regions */ + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "nand_addr"); + if (!res) { + ret = -EIO; + goto err_unmap; + } + host->addr_va = ioremap(res->start, res->end - res->start + 1); + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "nand_data"); + if (!res) { + ret = -EIO; + goto err_unmap; + } + host->data_va = ioremap(res->start, res->end - res->start + 1); + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "nand_cmd"); + if (!res) { + ret = -EIO; + goto err_unmap; + } + host->cmd_va = ioremap(res->start, res->end - res->start + 1); + + if (!host->addr_va || !host->data_va || !host->cmd_va) { + ret = -ENOMEM; + goto err_unmap; + } + + /* Link all private pointers */ + mtd = &host->mtd; + nand = &host->nand; + mtd->priv = nand; + nand->priv = host; + + host->mtd.owner = THIS_MODULE; + nand->IO_ADDR_R = host->data_va; + nand->IO_ADDR_W = host->data_va; + nand->cmd_ctrl = nomadik_cmd_ctrl; + + /* + * This stanza declares ECC_HW but uses soft routines. It's because + * HW claims to make the calculation but not the correction. However, + * I haven't managed to get the desired data out of it until now. + */ + nand->ecc.mode = NAND_ECC_SOFT; + nand->ecc.layout = &nomadik_ecc_layout; + nand->ecc.hwctl = nomadik_ecc_control; + nand->ecc.size = 512; + nand->ecc.bytes = 3; + + nand->options = pdata->options; + + /* + * Scan to find existance of the device + */ + if (nand_scan(&host->mtd, 1)) { + ret = -ENXIO; + goto err_unmap; + } + +#ifdef CONFIG_MTD_PARTITIONS + add_mtd_partitions(&host->mtd, pdata->parts, pdata->nparts); +#else + pr_info("Registering %s as whole device\n", mtd->name); + add_mtd_device(mtd); +#endif + + platform_set_drvdata(pdev, host); + return 0; + + err_unmap: + if (host->cmd_va) + iounmap(host->cmd_va); + if (host->data_va) + iounmap(host->data_va); + if (host->addr_va) + iounmap(host->addr_va); + err: + kfree(host); + return ret; +} + +/* + * Clean up routine + */ +static int nomadik_nand_remove(struct platform_device *pdev) +{ + struct nomadik_nand_host *host = platform_get_drvdata(pdev); + struct nomadik_nand_platform_data *pdata = pdev->dev.platform_data; + + if (pdata->exit) + pdata->exit(); + + if (host) { + iounmap(host->cmd_va); + iounmap(host->data_va); + iounmap(host->addr_va); + kfree(host); + } + return 0; +} + +static int nomadik_nand_suspend(struct device *dev) +{ + struct nomadik_nand_host *host = dev_get_drvdata(dev); + int ret = 0; + if (host) + ret = host->mtd.suspend(&host->mtd); + return ret; +} + +static int nomadik_nand_resume(struct device *dev) +{ + struct nomadik_nand_host *host = dev_get_drvdata(dev); + if (host) + host->mtd.resume(&host->mtd); + return 0; +} + +static struct dev_pm_ops nomadik_nand_pm_ops = { + .suspend = nomadik_nand_suspend, + .resume = nomadik_nand_resume, +}; + +static struct platform_driver nomadik_nand_driver = { + .probe = nomadik_nand_probe, + .remove = nomadik_nand_remove, + .driver = { + .owner = THIS_MODULE, + .name = "nomadik_nand", + .pm = &nomadik_nand_pm_ops, + }, +}; + +static int __init nand_nomadik_init(void) +{ + pr_info("Nomadik NAND driver\n"); + return platform_driver_register(&nomadik_nand_driver); +} + +static void __exit nand_nomadik_exit(void) +{ + platform_driver_unregister(&nomadik_nand_driver); +} + +module_init(nand_nomadik_init); +module_exit(nand_nomadik_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("ST Microelectronics (sachin.verma@st.com)"); +MODULE_DESCRIPTION("NAND driver for Nomadik Platform"); diff --git a/drivers/mtd/nand/omap2.c b/drivers/mtd/nand/omap2.c index ebd07e95b814..090ab87086b5 100644 --- a/drivers/mtd/nand/omap2.c +++ b/drivers/mtd/nand/omap2.c @@ -18,8 +18,7 @@ #include <linux/mtd/partitions.h> #include <linux/io.h> -#include <asm/dma.h> - +#include <mach/dma.h> #include <mach/gpmc.h> #include <mach/nand.h> @@ -112,6 +111,27 @@ static const char *part_probes[] = { "cmdlinepart", NULL }; #endif +#ifdef CONFIG_MTD_NAND_OMAP_PREFETCH +static int use_prefetch = 1; + +/* "modprobe ... use_prefetch=0" etc */ +module_param(use_prefetch, bool, 0); +MODULE_PARM_DESC(use_prefetch, "enable/disable use of PREFETCH"); + +#ifdef CONFIG_MTD_NAND_OMAP_PREFETCH_DMA +static int use_dma = 1; + +/* "modprobe ... use_dma=0" etc */ +module_param(use_dma, bool, 0); +MODULE_PARM_DESC(use_dma, "enable/disable use of DMA"); +#else +const int use_dma; +#endif +#else +const int use_prefetch; +const int use_dma; +#endif + struct omap_nand_info { struct nand_hw_control controller; struct omap_nand_platform_data *pdata; @@ -124,6 +144,9 @@ struct omap_nand_info { unsigned long phys_base; void __iomem *gpmc_cs_baseaddr; void __iomem *gpmc_baseaddr; + void __iomem *nand_pref_fifo_add; + struct completion comp; + int dma_ch; }; /** @@ -189,6 +212,38 @@ static void omap_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int ctrl) } /** + * omap_read_buf8 - read data from NAND controller into buffer + * @mtd: MTD device structure + * @buf: buffer to store date + * @len: number of bytes to read + */ +static void omap_read_buf8(struct mtd_info *mtd, u_char *buf, int len) +{ + struct nand_chip *nand = mtd->priv; + + ioread8_rep(nand->IO_ADDR_R, buf, len); +} + +/** + * omap_write_buf8 - write buffer to NAND controller + * @mtd: MTD device structure + * @buf: data buffer + * @len: number of bytes to write + */ +static void omap_write_buf8(struct mtd_info *mtd, const u_char *buf, int len) +{ + struct omap_nand_info *info = container_of(mtd, + struct omap_nand_info, mtd); + u_char *p = (u_char *)buf; + + while (len--) { + iowrite8(*p++, info->nand.IO_ADDR_W); + while (GPMC_BUF_EMPTY == (readl(info->gpmc_baseaddr + + GPMC_STATUS) & GPMC_BUF_FULL)); + } +} + +/** * omap_read_buf16 - read data from NAND controller into buffer * @mtd: MTD device structure * @buf: buffer to store date @@ -198,7 +253,7 @@ static void omap_read_buf16(struct mtd_info *mtd, u_char *buf, int len) { struct nand_chip *nand = mtd->priv; - __raw_readsw(nand->IO_ADDR_R, buf, len / 2); + ioread16_rep(nand->IO_ADDR_R, buf, len / 2); } /** @@ -217,13 +272,242 @@ static void omap_write_buf16(struct mtd_info *mtd, const u_char * buf, int len) len >>= 1; while (len--) { - writew(*p++, info->nand.IO_ADDR_W); + iowrite16(*p++, info->nand.IO_ADDR_W); while (GPMC_BUF_EMPTY == (readl(info->gpmc_baseaddr + GPMC_STATUS) & GPMC_BUF_FULL)) ; } } + +/** + * omap_read_buf_pref - read data from NAND controller into buffer + * @mtd: MTD device structure + * @buf: buffer to store date + * @len: number of bytes to read + */ +static void omap_read_buf_pref(struct mtd_info *mtd, u_char *buf, int len) +{ + struct omap_nand_info *info = container_of(mtd, + struct omap_nand_info, mtd); + uint32_t pfpw_status = 0, r_count = 0; + int ret = 0; + u32 *p = (u32 *)buf; + + /* take care of subpage reads */ + for (; len % 4 != 0; ) { + *buf++ = __raw_readb(info->nand.IO_ADDR_R); + len--; + } + p = (u32 *) buf; + + /* configure and start prefetch transfer */ + ret = gpmc_prefetch_enable(info->gpmc_cs, 0x0, len, 0x0); + if (ret) { + /* PFPW engine is busy, use cpu copy method */ + if (info->nand.options & NAND_BUSWIDTH_16) + omap_read_buf16(mtd, buf, len); + else + omap_read_buf8(mtd, buf, len); + } else { + do { + pfpw_status = gpmc_prefetch_status(); + r_count = ((pfpw_status >> 24) & 0x7F) >> 2; + ioread32_rep(info->nand_pref_fifo_add, p, r_count); + p += r_count; + len -= r_count << 2; + } while (len); + + /* disable and stop the PFPW engine */ + gpmc_prefetch_reset(); + } +} + +/** + * omap_write_buf_pref - write buffer to NAND controller + * @mtd: MTD device structure + * @buf: data buffer + * @len: number of bytes to write + */ +static void omap_write_buf_pref(struct mtd_info *mtd, + const u_char *buf, int len) +{ + struct omap_nand_info *info = container_of(mtd, + struct omap_nand_info, mtd); + uint32_t pfpw_status = 0, w_count = 0; + int i = 0, ret = 0; + u16 *p = (u16 *) buf; + + /* take care of subpage writes */ + if (len % 2 != 0) { + writeb(*buf, info->nand.IO_ADDR_R); + p = (u16 *)(buf + 1); + len--; + } + + /* configure and start prefetch transfer */ + ret = gpmc_prefetch_enable(info->gpmc_cs, 0x0, len, 0x1); + if (ret) { + /* PFPW engine is busy, use cpu copy method */ + if (info->nand.options & NAND_BUSWIDTH_16) + omap_write_buf16(mtd, buf, len); + else + omap_write_buf8(mtd, buf, len); + } else { + pfpw_status = gpmc_prefetch_status(); + while (pfpw_status & 0x3FFF) { + w_count = ((pfpw_status >> 24) & 0x7F) >> 1; + for (i = 0; (i < w_count) && len; i++, len -= 2) + iowrite16(*p++, info->nand_pref_fifo_add); + pfpw_status = gpmc_prefetch_status(); + } + + /* disable and stop the PFPW engine */ + gpmc_prefetch_reset(); + } +} + +#ifdef CONFIG_MTD_NAND_OMAP_PREFETCH_DMA +/* + * omap_nand_dma_cb: callback on the completion of dma transfer + * @lch: logical channel + * @ch_satuts: channel status + * @data: pointer to completion data structure + */ +static void omap_nand_dma_cb(int lch, u16 ch_status, void *data) +{ + complete((struct completion *) data); +} + +/* + * omap_nand_dma_transfer: configer and start dma transfer + * @mtd: MTD device structure + * @addr: virtual address in RAM of source/destination + * @len: number of data bytes to be transferred + * @is_write: flag for read/write operation + */ +static inline int omap_nand_dma_transfer(struct mtd_info *mtd, void *addr, + unsigned int len, int is_write) +{ + struct omap_nand_info *info = container_of(mtd, + struct omap_nand_info, mtd); + uint32_t prefetch_status = 0; + enum dma_data_direction dir = is_write ? DMA_TO_DEVICE : + DMA_FROM_DEVICE; + dma_addr_t dma_addr; + int ret; + + /* The fifo depth is 64 bytes. We have a sync at each frame and frame + * length is 64 bytes. + */ + int buf_len = len >> 6; + + if (addr >= high_memory) { + struct page *p1; + + if (((size_t)addr & PAGE_MASK) != + ((size_t)(addr + len - 1) & PAGE_MASK)) + goto out_copy; + p1 = vmalloc_to_page(addr); + if (!p1) + goto out_copy; + addr = page_address(p1) + ((size_t)addr & ~PAGE_MASK); + } + + dma_addr = dma_map_single(&info->pdev->dev, addr, len, dir); + if (dma_mapping_error(&info->pdev->dev, dma_addr)) { + dev_err(&info->pdev->dev, + "Couldn't DMA map a %d byte buffer\n", len); + goto out_copy; + } + + if (is_write) { + omap_set_dma_dest_params(info->dma_ch, 0, OMAP_DMA_AMODE_CONSTANT, + info->phys_base, 0, 0); + omap_set_dma_src_params(info->dma_ch, 0, OMAP_DMA_AMODE_POST_INC, + dma_addr, 0, 0); + omap_set_dma_transfer_params(info->dma_ch, OMAP_DMA_DATA_TYPE_S32, + 0x10, buf_len, OMAP_DMA_SYNC_FRAME, + OMAP24XX_DMA_GPMC, OMAP_DMA_DST_SYNC); + } else { + omap_set_dma_src_params(info->dma_ch, 0, OMAP_DMA_AMODE_CONSTANT, + info->phys_base, 0, 0); + omap_set_dma_dest_params(info->dma_ch, 0, OMAP_DMA_AMODE_POST_INC, + dma_addr, 0, 0); + omap_set_dma_transfer_params(info->dma_ch, OMAP_DMA_DATA_TYPE_S32, + 0x10, buf_len, OMAP_DMA_SYNC_FRAME, + OMAP24XX_DMA_GPMC, OMAP_DMA_SRC_SYNC); + } + /* configure and start prefetch transfer */ + ret = gpmc_prefetch_enable(info->gpmc_cs, 0x1, len, is_write); + if (ret) + /* PFPW engine is busy, use cpu copy methode */ + goto out_copy; + + init_completion(&info->comp); + + omap_start_dma(info->dma_ch); + + /* setup and start DMA using dma_addr */ + wait_for_completion(&info->comp); + + while (0x3fff & (prefetch_status = gpmc_prefetch_status())) + ; + /* disable and stop the PFPW engine */ + gpmc_prefetch_reset(); + + dma_unmap_single(&info->pdev->dev, dma_addr, len, dir); + return 0; + +out_copy: + if (info->nand.options & NAND_BUSWIDTH_16) + is_write == 0 ? omap_read_buf16(mtd, (u_char *) addr, len) + : omap_write_buf16(mtd, (u_char *) addr, len); + else + is_write == 0 ? omap_read_buf8(mtd, (u_char *) addr, len) + : omap_write_buf8(mtd, (u_char *) addr, len); + return 0; +} +#else +static void omap_nand_dma_cb(int lch, u16 ch_status, void *data) {} +static inline int omap_nand_dma_transfer(struct mtd_info *mtd, void *addr, + unsigned int len, int is_write) +{ + return 0; +} +#endif + +/** + * omap_read_buf_dma_pref - read data from NAND controller into buffer + * @mtd: MTD device structure + * @buf: buffer to store date + * @len: number of bytes to read + */ +static void omap_read_buf_dma_pref(struct mtd_info *mtd, u_char *buf, int len) +{ + if (len <= mtd->oobsize) + omap_read_buf_pref(mtd, buf, len); + else + /* start transfer in DMA mode */ + omap_nand_dma_transfer(mtd, buf, len, 0x0); +} + +/** + * omap_write_buf_dma_pref - write buffer to NAND controller + * @mtd: MTD device structure + * @buf: data buffer + * @len: number of bytes to write + */ +static void omap_write_buf_dma_pref(struct mtd_info *mtd, + const u_char *buf, int len) +{ + if (len <= mtd->oobsize) + omap_write_buf_pref(mtd, buf, len); + else + /* start transfer in DMA mode */ + omap_nand_dma_transfer(mtd, buf, len, 0x1); +} + /** * omap_verify_buf - Verify chip data against buffer * @mtd: MTD device structure @@ -658,17 +942,12 @@ static int __devinit omap_nand_probe(struct platform_device *pdev) err = -ENOMEM; goto out_release_mem_region; } + info->nand.controller = &info->controller; info->nand.IO_ADDR_W = info->nand.IO_ADDR_R; info->nand.cmd_ctrl = omap_hwcontrol; - /* REVISIT: only supports 16-bit NAND flash */ - - info->nand.read_buf = omap_read_buf16; - info->nand.write_buf = omap_write_buf16; - info->nand.verify_buf = omap_verify_buf; - /* * If RDY/BSY line is connected to OMAP then use the omap ready * funcrtion and the generic nand_wait function which reads the status @@ -689,6 +968,40 @@ static int __devinit omap_nand_probe(struct platform_device *pdev) == 0x1000) info->nand.options |= NAND_BUSWIDTH_16; + if (use_prefetch) { + /* copy the virtual address of nand base for fifo access */ + info->nand_pref_fifo_add = info->nand.IO_ADDR_R; + + info->nand.read_buf = omap_read_buf_pref; + info->nand.write_buf = omap_write_buf_pref; + if (use_dma) { + err = omap_request_dma(OMAP24XX_DMA_GPMC, "NAND", + omap_nand_dma_cb, &info->comp, &info->dma_ch); + if (err < 0) { + info->dma_ch = -1; + printk(KERN_WARNING "DMA request failed." + " Non-dma data transfer mode\n"); + } else { + omap_set_dma_dest_burst_mode(info->dma_ch, + OMAP_DMA_DATA_BURST_16); + omap_set_dma_src_burst_mode(info->dma_ch, + OMAP_DMA_DATA_BURST_16); + + info->nand.read_buf = omap_read_buf_dma_pref; + info->nand.write_buf = omap_write_buf_dma_pref; + } + } + } else { + if (info->nand.options & NAND_BUSWIDTH_16) { + info->nand.read_buf = omap_read_buf16; + info->nand.write_buf = omap_write_buf16; + } else { + info->nand.read_buf = omap_read_buf8; + info->nand.write_buf = omap_write_buf8; + } + } + info->nand.verify_buf = omap_verify_buf; + #ifdef CONFIG_MTD_NAND_OMAP_HWECC info->nand.ecc.bytes = 3; info->nand.ecc.size = 512; @@ -744,9 +1057,12 @@ static int omap_nand_remove(struct platform_device *pdev) struct omap_nand_info *info = mtd->priv; platform_set_drvdata(pdev, NULL); + if (use_dma) + omap_free_dma(info->dma_ch); + /* Release NAND device, its internal structures and partitions */ nand_release(&info->mtd); - iounmap(info->nand.IO_ADDR_R); + iounmap(info->nand_pref_fifo_add); kfree(&info->mtd); return 0; } @@ -763,6 +1079,15 @@ static struct platform_driver omap_nand_driver = { static int __init omap_nand_init(void) { printk(KERN_INFO "%s driver initializing\n", DRIVER_NAME); + + /* This check is required if driver is being + * loaded run time as a module + */ + if ((1 == use_dma) && (0 == use_prefetch)) { + printk(KERN_INFO"Wrong parameters: 'use_dma' can not be 1 " + "without use_prefetch'. Prefetch will not be" + " used in either mode (mpu or dma)\n"); + } return platform_driver_register(&omap_nand_driver); } diff --git a/drivers/mtd/nand/orion_nand.c b/drivers/mtd/nand/orion_nand.c index 0d9d4bc9c762..f59c07427af3 100644 --- a/drivers/mtd/nand/orion_nand.c +++ b/drivers/mtd/nand/orion_nand.c @@ -171,7 +171,6 @@ static int __devexit orion_nand_remove(struct platform_device *pdev) } static struct platform_driver orion_nand_driver = { - .probe = orion_nand_probe, .remove = __devexit_p(orion_nand_remove), .driver = { .name = "orion_nand", @@ -181,7 +180,7 @@ static struct platform_driver orion_nand_driver = { static int __init orion_nand_init(void) { - return platform_driver_register(&orion_nand_driver); + return platform_driver_probe(&orion_nand_driver, orion_nand_probe); } static void __exit orion_nand_exit(void) diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c index 30a8ce6d3e69..6ea520ae2410 100644 --- a/drivers/mtd/nand/pxa3xx_nand.c +++ b/drivers/mtd/nand/pxa3xx_nand.c @@ -102,6 +102,7 @@ enum { ERR_SENDCMD = -2, ERR_DBERR = -3, ERR_BBERR = -4, + ERR_SBERR = -5, }; enum { @@ -564,11 +565,13 @@ static irqreturn_t pxa3xx_nand_irq(int irq, void *devid) status = nand_readl(info, NDSR); - if (status & (NDSR_RDDREQ | NDSR_DBERR)) { + if (status & (NDSR_RDDREQ | NDSR_DBERR | NDSR_SBERR)) { if (status & NDSR_DBERR) info->retcode = ERR_DBERR; + else if (status & NDSR_SBERR) + info->retcode = ERR_SBERR; - disable_int(info, NDSR_RDDREQ | NDSR_DBERR); + disable_int(info, NDSR_RDDREQ | NDSR_DBERR | NDSR_SBERR); if (info->use_dma) { info->state = STATE_DMA_READING; @@ -670,7 +673,7 @@ static void pxa3xx_nand_cmdfunc(struct mtd_info *mtd, unsigned command, if (prepare_read_prog_cmd(info, cmdset->read1, column, page_addr)) break; - pxa3xx_nand_do_cmd(info, NDSR_RDDREQ | NDSR_DBERR); + pxa3xx_nand_do_cmd(info, NDSR_RDDREQ | NDSR_DBERR | NDSR_SBERR); /* We only are OOB, so if the data has error, does not matter */ if (info->retcode == ERR_DBERR) @@ -687,7 +690,7 @@ static void pxa3xx_nand_cmdfunc(struct mtd_info *mtd, unsigned command, if (prepare_read_prog_cmd(info, cmdset->read1, column, page_addr)) break; - pxa3xx_nand_do_cmd(info, NDSR_RDDREQ | NDSR_DBERR); + pxa3xx_nand_do_cmd(info, NDSR_RDDREQ | NDSR_DBERR | NDSR_SBERR); if (info->retcode == ERR_DBERR) { /* for blank page (all 0xff), HW will calculate its ECC as @@ -861,8 +864,12 @@ static int pxa3xx_nand_ecc_correct(struct mtd_info *mtd, * consider it as a ecc error which will tell the caller the * read fail We have distinguish all the errors, but the * nand_read_ecc only check this function return value + * + * Corrected (single-bit) errors must also be noted. */ - if (info->retcode != ERR_NONE) + if (info->retcode == ERR_SBERR) + return 1; + else if (info->retcode != ERR_NONE) return -1; return 0; diff --git a/drivers/mtd/nand/sh_flctl.c b/drivers/mtd/nand/sh_flctl.c index 2bc896623e2d..02bef21f2e4b 100644 --- a/drivers/mtd/nand/sh_flctl.c +++ b/drivers/mtd/nand/sh_flctl.c @@ -329,7 +329,7 @@ static void set_cmd_regs(struct mtd_info *mtd, uint32_t cmd, uint32_t flcmcdr_va } static int flctl_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, - uint8_t *buf) + uint8_t *buf, int page) { int i, eccsize = chip->ecc.size; int eccbytes = chip->ecc.bytes; @@ -857,7 +857,6 @@ static int __exit flctl_remove(struct platform_device *pdev) } static struct platform_driver flctl_driver = { - .probe = flctl_probe, .remove = flctl_remove, .driver = { .name = "sh_flctl", @@ -867,7 +866,7 @@ static struct platform_driver flctl_driver = { static int __init flctl_nand_init(void) { - return platform_driver_register(&flctl_driver); + return platform_driver_probe(&flctl_driver, flctl_probe); } static void __exit flctl_nand_cleanup(void) diff --git a/drivers/mtd/nand/tmio_nand.c b/drivers/mtd/nand/tmio_nand.c index daa6a4c3b8ce..92c73344a669 100644 --- a/drivers/mtd/nand/tmio_nand.c +++ b/drivers/mtd/nand/tmio_nand.c @@ -301,6 +301,21 @@ static int tmio_nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat, return 0; } +static int tmio_nand_correct_data(struct mtd_info *mtd, unsigned char *buf, + unsigned char *read_ecc, unsigned char *calc_ecc) +{ + int r0, r1; + + /* assume ecc.size = 512 and ecc.bytes = 6 */ + r0 = __nand_correct_data(buf, read_ecc, calc_ecc, 256); + if (r0 < 0) + return r0; + r1 = __nand_correct_data(buf + 256, read_ecc + 3, calc_ecc + 3, 256); + if (r1 < 0) + return r1; + return r0 + r1; +} + static int tmio_hw_init(struct platform_device *dev, struct tmio_nand *tmio) { struct mfd_cell *cell = (struct mfd_cell *)dev->dev.platform_data; @@ -424,7 +439,7 @@ static int tmio_probe(struct platform_device *dev) nand_chip->ecc.bytes = 6; nand_chip->ecc.hwctl = tmio_nand_enable_hwecc; nand_chip->ecc.calculate = tmio_nand_calculate_ecc; - nand_chip->ecc.correct = nand_correct_data; + nand_chip->ecc.correct = tmio_nand_correct_data; if (data) nand_chip->badblock_pattern = data->badblock_pattern; diff --git a/drivers/mtd/nand/txx9ndfmc.c b/drivers/mtd/nand/txx9ndfmc.c index 488088eff2ca..73af8324d0d0 100644 --- a/drivers/mtd/nand/txx9ndfmc.c +++ b/drivers/mtd/nand/txx9ndfmc.c @@ -189,18 +189,43 @@ static int txx9ndfmc_calculate_ecc(struct mtd_info *mtd, const uint8_t *dat, uint8_t *ecc_code) { struct platform_device *dev = mtd_to_platdev(mtd); + struct nand_chip *chip = mtd->priv; + int eccbytes; u32 mcr = txx9ndfmc_read(dev, TXX9_NDFMCR); mcr &= ~TXX9_NDFMCR_ECC_ALL; txx9ndfmc_write(dev, mcr | TXX9_NDFMCR_ECC_OFF, TXX9_NDFMCR); txx9ndfmc_write(dev, mcr | TXX9_NDFMCR_ECC_READ, TXX9_NDFMCR); - ecc_code[1] = txx9ndfmc_read(dev, TXX9_NDFDTR); - ecc_code[0] = txx9ndfmc_read(dev, TXX9_NDFDTR); - ecc_code[2] = txx9ndfmc_read(dev, TXX9_NDFDTR); + for (eccbytes = chip->ecc.bytes; eccbytes > 0; eccbytes -= 3) { + ecc_code[1] = txx9ndfmc_read(dev, TXX9_NDFDTR); + ecc_code[0] = txx9ndfmc_read(dev, TXX9_NDFDTR); + ecc_code[2] = txx9ndfmc_read(dev, TXX9_NDFDTR); + ecc_code += 3; + } txx9ndfmc_write(dev, mcr | TXX9_NDFMCR_ECC_OFF, TXX9_NDFMCR); return 0; } +static int txx9ndfmc_correct_data(struct mtd_info *mtd, unsigned char *buf, + unsigned char *read_ecc, unsigned char *calc_ecc) +{ + struct nand_chip *chip = mtd->priv; + int eccsize; + int corrected = 0; + int stat; + + for (eccsize = chip->ecc.size; eccsize > 0; eccsize -= 256) { + stat = __nand_correct_data(buf, read_ecc, calc_ecc, 256); + if (stat < 0) + return stat; + corrected += stat; + buf += 256; + read_ecc += 3; + calc_ecc += 3; + } + return corrected; +} + static void txx9ndfmc_enable_hwecc(struct mtd_info *mtd, int mode) { struct platform_device *dev = mtd_to_platdev(mtd); @@ -244,6 +269,22 @@ static void txx9ndfmc_initialize(struct platform_device *dev) #define TXX9NDFMC_NS_TO_CYC(gbusclk, ns) \ DIV_ROUND_UP((ns) * DIV_ROUND_UP(gbusclk, 1000), 1000000) +static int txx9ndfmc_nand_scan(struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd->priv; + int ret; + + ret = nand_scan_ident(mtd, 1); + if (!ret) { + if (mtd->writesize >= 512) { + chip->ecc.size = mtd->writesize; + chip->ecc.bytes = 3 * (mtd->writesize / 256); + } + ret = nand_scan_tail(mtd); + } + return ret; +} + static int __init txx9ndfmc_probe(struct platform_device *dev) { struct txx9ndfmc_platform_data *plat = dev->dev.platform_data; @@ -321,9 +362,10 @@ static int __init txx9ndfmc_probe(struct platform_device *dev) chip->cmd_ctrl = txx9ndfmc_cmd_ctrl; chip->dev_ready = txx9ndfmc_dev_ready; chip->ecc.calculate = txx9ndfmc_calculate_ecc; - chip->ecc.correct = nand_correct_data; + chip->ecc.correct = txx9ndfmc_correct_data; chip->ecc.hwctl = txx9ndfmc_enable_hwecc; chip->ecc.mode = NAND_ECC_HW; + /* txx9ndfmc_nand_scan will overwrite ecc.size and ecc.bytes */ chip->ecc.size = 256; chip->ecc.bytes = 3; chip->chip_delay = 100; @@ -349,7 +391,7 @@ static int __init txx9ndfmc_probe(struct platform_device *dev) if (plat->wide_mask & (1 << i)) chip->options |= NAND_BUSWIDTH_16; - if (nand_scan(mtd, 1)) { + if (txx9ndfmc_nand_scan(mtd)) { kfree(txx9_priv->mtdname); kfree(txx9_priv); continue; diff --git a/drivers/mtd/nand/w90p910_nand.c b/drivers/mtd/nand/w90p910_nand.c new file mode 100644 index 000000000000..7680e731348a --- /dev/null +++ b/drivers/mtd/nand/w90p910_nand.c @@ -0,0 +1,382 @@ +/* + * Copyright (c) 2009 Nuvoton technology corporation. + * + * Wan ZongShun <mcuos.com@gmail.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation;version 2 of the License. + * + */ + +#include <linux/slab.h> +#include <linux/init.h> +#include <linux/module.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/platform_device.h> +#include <linux/delay.h> +#include <linux/clk.h> +#include <linux/err.h> + +#include <linux/mtd/mtd.h> +#include <linux/mtd/nand.h> +#include <linux/mtd/partitions.h> + +#define REG_FMICSR 0x00 +#define REG_SMCSR 0xa0 +#define REG_SMISR 0xac +#define REG_SMCMD 0xb0 +#define REG_SMADDR 0xb4 +#define REG_SMDATA 0xb8 + +#define RESET_FMI 0x01 +#define NAND_EN 0x08 +#define READYBUSY (0x01 << 18) + +#define SWRST 0x01 +#define PSIZE (0x01 << 3) +#define DMARWEN (0x03 << 1) +#define BUSWID (0x01 << 4) +#define ECC4EN (0x01 << 5) +#define WP (0x01 << 24) +#define NANDCS (0x01 << 25) +#define ENDADDR (0x01 << 31) + +#define read_data_reg(dev) \ + __raw_readl((dev)->reg + REG_SMDATA) + +#define write_data_reg(dev, val) \ + __raw_writel((val), (dev)->reg + REG_SMDATA) + +#define write_cmd_reg(dev, val) \ + __raw_writel((val), (dev)->reg + REG_SMCMD) + +#define write_addr_reg(dev, val) \ + __raw_writel((val), (dev)->reg + REG_SMADDR) + +struct w90p910_nand { + struct mtd_info mtd; + struct nand_chip chip; + void __iomem *reg; + struct clk *clk; + spinlock_t lock; +}; + +static const struct mtd_partition partitions[] = { + { + .name = "NAND FS 0", + .offset = 0, + .size = 8 * 1024 * 1024 + }, + { + .name = "NAND FS 1", + .offset = MTDPART_OFS_APPEND, + .size = MTDPART_SIZ_FULL + } +}; + +static unsigned char w90p910_nand_read_byte(struct mtd_info *mtd) +{ + unsigned char ret; + struct w90p910_nand *nand; + + nand = container_of(mtd, struct w90p910_nand, mtd); + + ret = (unsigned char)read_data_reg(nand); + + return ret; +} + +static void w90p910_nand_read_buf(struct mtd_info *mtd, + unsigned char *buf, int len) +{ + int i; + struct w90p910_nand *nand; + + nand = container_of(mtd, struct w90p910_nand, mtd); + + for (i = 0; i < len; i++) + buf[i] = (unsigned char)read_data_reg(nand); +} + +static void w90p910_nand_write_buf(struct mtd_info *mtd, + const unsigned char *buf, int len) +{ + int i; + struct w90p910_nand *nand; + + nand = container_of(mtd, struct w90p910_nand, mtd); + + for (i = 0; i < len; i++) + write_data_reg(nand, buf[i]); +} + +static int w90p910_verify_buf(struct mtd_info *mtd, + const unsigned char *buf, int len) +{ + int i; + struct w90p910_nand *nand; + + nand = container_of(mtd, struct w90p910_nand, mtd); + + for (i = 0; i < len; i++) { + if (buf[i] != (unsigned char)read_data_reg(nand)) + return -EFAULT; + } + + return 0; +} + +static int w90p910_check_rb(struct w90p910_nand *nand) +{ + unsigned int val; + spin_lock(&nand->lock); + val = __raw_readl(REG_SMISR); + val &= READYBUSY; + spin_unlock(&nand->lock); + + return val; +} + +static int w90p910_nand_devready(struct mtd_info *mtd) +{ + struct w90p910_nand *nand; + int ready; + + nand = container_of(mtd, struct w90p910_nand, mtd); + + ready = (w90p910_check_rb(nand)) ? 1 : 0; + return ready; +} + +static void w90p910_nand_command_lp(struct mtd_info *mtd, + unsigned int command, int column, int page_addr) +{ + register struct nand_chip *chip = mtd->priv; + struct w90p910_nand *nand; + + nand = container_of(mtd, struct w90p910_nand, mtd); + + if (command == NAND_CMD_READOOB) { + column += mtd->writesize; + command = NAND_CMD_READ0; + } + + write_cmd_reg(nand, command & 0xff); + + if (column != -1 || page_addr != -1) { + + if (column != -1) { + if (chip->options & NAND_BUSWIDTH_16) + column >>= 1; + write_addr_reg(nand, column); + write_addr_reg(nand, column >> 8 | ENDADDR); + } + if (page_addr != -1) { + write_addr_reg(nand, page_addr); + + if (chip->chipsize > (128 << 20)) { + write_addr_reg(nand, page_addr >> 8); + write_addr_reg(nand, page_addr >> 16 | ENDADDR); + } else { + write_addr_reg(nand, page_addr >> 8 | ENDADDR); + } + } + } + + switch (command) { + case NAND_CMD_CACHEDPROG: + case NAND_CMD_PAGEPROG: + case NAND_CMD_ERASE1: + case NAND_CMD_ERASE2: + case NAND_CMD_SEQIN: + case NAND_CMD_RNDIN: + case NAND_CMD_STATUS: + case NAND_CMD_DEPLETE1: + return; + + case NAND_CMD_STATUS_ERROR: + case NAND_CMD_STATUS_ERROR0: + case NAND_CMD_STATUS_ERROR1: + case NAND_CMD_STATUS_ERROR2: + case NAND_CMD_STATUS_ERROR3: + udelay(chip->chip_delay); + return; + + case NAND_CMD_RESET: + if (chip->dev_ready) + break; + udelay(chip->chip_delay); + + write_cmd_reg(nand, NAND_CMD_STATUS); + write_cmd_reg(nand, command); + + while (!w90p910_check_rb(nand)) + ; + + return; + + case NAND_CMD_RNDOUT: + write_cmd_reg(nand, NAND_CMD_RNDOUTSTART); + return; + + case NAND_CMD_READ0: + + write_cmd_reg(nand, NAND_CMD_READSTART); + default: + + if (!chip->dev_ready) { + udelay(chip->chip_delay); + return; + } + } + + /* Apply this short delay always to ensure that we do wait tWB in + * any case on any machine. */ + ndelay(100); + + while (!chip->dev_ready(mtd)) + ; +} + + +static void w90p910_nand_enable(struct w90p910_nand *nand) +{ + unsigned int val; + spin_lock(&nand->lock); + __raw_writel(RESET_FMI, (nand->reg + REG_FMICSR)); + + val = __raw_readl(nand->reg + REG_FMICSR); + + if (!(val & NAND_EN)) + __raw_writel(val | NAND_EN, REG_FMICSR); + + val = __raw_readl(nand->reg + REG_SMCSR); + + val &= ~(SWRST|PSIZE|DMARWEN|BUSWID|ECC4EN|NANDCS); + val |= WP; + + __raw_writel(val, nand->reg + REG_SMCSR); + + spin_unlock(&nand->lock); +} + +static int __devinit w90p910_nand_probe(struct platform_device *pdev) +{ + struct w90p910_nand *w90p910_nand; + struct nand_chip *chip; + int retval; + struct resource *res; + + retval = 0; + + w90p910_nand = kzalloc(sizeof(struct w90p910_nand), GFP_KERNEL); + if (!w90p910_nand) + return -ENOMEM; + chip = &(w90p910_nand->chip); + + w90p910_nand->mtd.priv = chip; + w90p910_nand->mtd.owner = THIS_MODULE; + spin_lock_init(&w90p910_nand->lock); + + w90p910_nand->clk = clk_get(&pdev->dev, NULL); + if (IS_ERR(w90p910_nand->clk)) { + retval = -ENOENT; + goto fail1; + } + clk_enable(w90p910_nand->clk); + + chip->cmdfunc = w90p910_nand_command_lp; + chip->dev_ready = w90p910_nand_devready; + chip->read_byte = w90p910_nand_read_byte; + chip->write_buf = w90p910_nand_write_buf; + chip->read_buf = w90p910_nand_read_buf; + chip->verify_buf = w90p910_verify_buf; + chip->chip_delay = 50; + chip->options = 0; + chip->ecc.mode = NAND_ECC_SOFT; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + retval = -ENXIO; + goto fail1; + } + + if (!request_mem_region(res->start, resource_size(res), pdev->name)) { + retval = -EBUSY; + goto fail1; + } + + w90p910_nand->reg = ioremap(res->start, resource_size(res)); + if (!w90p910_nand->reg) { + retval = -ENOMEM; + goto fail2; + } + + w90p910_nand_enable(w90p910_nand); + + if (nand_scan(&(w90p910_nand->mtd), 1)) { + retval = -ENXIO; + goto fail3; + } + + add_mtd_partitions(&(w90p910_nand->mtd), partitions, + ARRAY_SIZE(partitions)); + + platform_set_drvdata(pdev, w90p910_nand); + + return retval; + +fail3: iounmap(w90p910_nand->reg); +fail2: release_mem_region(res->start, resource_size(res)); +fail1: kfree(w90p910_nand); + return retval; +} + +static int __devexit w90p910_nand_remove(struct platform_device *pdev) +{ + struct w90p910_nand *w90p910_nand = platform_get_drvdata(pdev); + struct resource *res; + + iounmap(w90p910_nand->reg); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + release_mem_region(res->start, resource_size(res)); + + clk_disable(w90p910_nand->clk); + clk_put(w90p910_nand->clk); + + kfree(w90p910_nand); + + platform_set_drvdata(pdev, NULL); + + return 0; +} + +static struct platform_driver w90p910_nand_driver = { + .probe = w90p910_nand_probe, + .remove = __devexit_p(w90p910_nand_remove), + .driver = { + .name = "w90p910-fmi", + .owner = THIS_MODULE, + }, +}; + +static int __init w90p910_nand_init(void) +{ + return platform_driver_register(&w90p910_nand_driver); +} + +static void __exit w90p910_nand_exit(void) +{ + platform_driver_unregister(&w90p910_nand_driver); +} + +module_init(w90p910_nand_init); +module_exit(w90p910_nand_exit); + +MODULE_AUTHOR("Wan ZongShun <mcuos.com@gmail.com>"); +MODULE_DESCRIPTION("w90p910 nand driver!"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:w90p910-fmi"); diff --git a/drivers/mtd/ofpart.c b/drivers/mtd/ofpart.c index 3e164f0c9295..62d6a78c4eee 100644 --- a/drivers/mtd/ofpart.c +++ b/drivers/mtd/ofpart.c @@ -46,21 +46,12 @@ int __devinit of_mtd_parse_partitions(struct device *dev, const u32 *reg; int len; - /* check if this is a partition node */ - partname = of_get_property(pp, "name", &len); - if (strcmp(partname, "partition") != 0) { + reg = of_get_property(pp, "reg", &len); + if (!reg) { nr_parts--; continue; } - reg = of_get_property(pp, "reg", &len); - if (!reg || (len != 2 * sizeof(u32))) { - of_node_put(pp); - dev_err(dev, "Invalid 'reg' on %s\n", node->full_name); - kfree(*pparts); - *pparts = NULL; - return -EINVAL; - } (*pparts)[i].offset = reg[0]; (*pparts)[i].size = reg[1]; @@ -75,6 +66,14 @@ int __devinit of_mtd_parse_partitions(struct device *dev, i++; } + if (!i) { + of_node_put(pp); + dev_err(dev, "No valid partition found on %s\n", node->full_name); + kfree(*pparts); + *pparts = NULL; + return -EINVAL; + } + return nr_parts; } EXPORT_SYMBOL(of_mtd_parse_partitions); diff --git a/drivers/mtd/onenand/Kconfig b/drivers/mtd/onenand/Kconfig index 79fa79e8f8de..a38f580c2bb3 100644 --- a/drivers/mtd/onenand/Kconfig +++ b/drivers/mtd/onenand/Kconfig @@ -5,6 +5,7 @@ menuconfig MTD_ONENAND tristate "OneNAND Device Support" depends on MTD + select MTD_PARTITIONS help This enables support for accessing all type of OneNAND flash devices. For further information see @@ -23,7 +24,6 @@ config MTD_ONENAND_VERIFY_WRITE config MTD_ONENAND_GENERIC tristate "OneNAND Flash device via platform device driver" - depends on ARM help Support for OneNAND flash via platform device driver. @@ -66,7 +66,6 @@ config MTD_ONENAND_2X_PROGRAM config MTD_ONENAND_SIM tristate "OneNAND simulator support" - depends on MTD_PARTITIONS help The simulator may simulate various OneNAND flash chips for the OneNAND MTD layer. diff --git a/drivers/mtd/onenand/generic.c b/drivers/mtd/onenand/generic.c index 3a496c33fb52..e78914938c5c 100644 --- a/drivers/mtd/onenand/generic.c +++ b/drivers/mtd/onenand/generic.c @@ -19,12 +19,16 @@ #include <linux/mtd/mtd.h> #include <linux/mtd/onenand.h> #include <linux/mtd/partitions.h> - #include <asm/io.h> -#include <asm/mach/flash.h> - -#define DRIVER_NAME "onenand" +/* + * Note: Driver name and platform data format have been updated! + * + * This version of the driver is named "onenand-flash" and takes struct + * onenand_platform_data as platform data. The old ARM-specific version + * with the name "onenand" used to take struct flash_platform_data. + */ +#define DRIVER_NAME "onenand-flash" #ifdef CONFIG_MTD_PARTITIONS static const char *part_probes[] = { "cmdlinepart", NULL, }; @@ -39,16 +43,16 @@ struct onenand_info { static int __devinit generic_onenand_probe(struct platform_device *pdev) { struct onenand_info *info; - struct flash_platform_data *pdata = pdev->dev.platform_data; + struct onenand_platform_data *pdata = pdev->dev.platform_data; struct resource *res = pdev->resource; - unsigned long size = res->end - res->start + 1; + unsigned long size = resource_size(res); int err; info = kzalloc(sizeof(struct onenand_info), GFP_KERNEL); if (!info) return -ENOMEM; - if (!request_mem_region(res->start, size, pdev->dev.driver->name)) { + if (!request_mem_region(res->start, size, dev_name(&pdev->dev))) { err = -EBUSY; goto out_free_info; } @@ -59,7 +63,7 @@ static int __devinit generic_onenand_probe(struct platform_device *pdev) goto out_release_mem_region; } - info->onenand.mmcontrol = pdata->mmcontrol; + info->onenand.mmcontrol = pdata ? pdata->mmcontrol : 0; info->onenand.irq = platform_get_irq(pdev, 0); info->mtd.name = dev_name(&pdev->dev); @@ -75,7 +79,7 @@ static int __devinit generic_onenand_probe(struct platform_device *pdev) err = parse_mtd_partitions(&info->mtd, part_probes, &info->parts, 0); if (err > 0) add_mtd_partitions(&info->mtd, info->parts, err); - else if (err <= 0 && pdata->parts) + else if (err <= 0 && pdata && pdata->parts) add_mtd_partitions(&info->mtd, pdata->parts, pdata->nr_parts); else #endif @@ -99,7 +103,7 @@ static int __devexit generic_onenand_remove(struct platform_device *pdev) { struct onenand_info *info = platform_get_drvdata(pdev); struct resource *res = pdev->resource; - unsigned long size = res->end - res->start + 1; + unsigned long size = resource_size(res); platform_set_drvdata(pdev, NULL); diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c index 6e829095ea9d..ff66e4330aa7 100644 --- a/drivers/mtd/onenand/onenand_base.c +++ b/drivers/mtd/onenand/onenand_base.c @@ -1191,7 +1191,7 @@ static int onenand_read_ops_nolock(struct mtd_info *mtd, loff_t from, /* * Chip boundary handling in DDP * Now we issued chip 1 read and pointed chip 1 - * bufferam so we have to point chip 0 bufferam. + * bufferram so we have to point chip 0 bufferram. */ if (ONENAND_IS_DDP(this) && unlikely(from == (this->chipsize >> 1))) { @@ -1867,8 +1867,8 @@ static int onenand_write_ops_nolock(struct mtd_info *mtd, loff_t to, ONENAND_SET_NEXT_BUFFERRAM(this); /* - * 2 PLANE, MLC, and Flex-OneNAND doesn't support - * write-while-programe feature. + * 2 PLANE, MLC, and Flex-OneNAND do not support + * write-while-program feature. */ if (!ONENAND_IS_2PLANE(this) && !first) { ONENAND_SET_PREV_BUFFERRAM(this); @@ -1879,7 +1879,7 @@ static int onenand_write_ops_nolock(struct mtd_info *mtd, loff_t to, onenand_update_bufferram(mtd, prev, !ret && !prev_subpage); if (ret) { written -= prevlen; - printk(KERN_ERR "onenand_write_ops_nolock: write filaed %d\n", ret); + printk(KERN_ERR "onenand_write_ops_nolock: write failed %d\n", ret); break; } @@ -1905,7 +1905,7 @@ static int onenand_write_ops_nolock(struct mtd_info *mtd, loff_t to, /* In partial page write we don't update bufferram */ onenand_update_bufferram(mtd, to, !ret && !subpage); if (ret) { - printk(KERN_ERR "onenand_write_ops_nolock: write filaed %d\n", ret); + printk(KERN_ERR "onenand_write_ops_nolock: write failed %d\n", ret); break; } @@ -2201,7 +2201,7 @@ static int onenand_erase(struct mtd_info *mtd, struct erase_info *instr) /* Grab the lock and see if the device is available */ onenand_get_device(mtd, FL_ERASING); - /* Loop throught the pages */ + /* Loop through the blocks */ instr->state = MTD_ERASING; while (len) { @@ -2328,7 +2328,7 @@ static int onenand_default_block_markbad(struct mtd_info *mtd, loff_t ofs) if (bbm->bbt) bbm->bbt[block >> 2] |= 0x01 << ((block & 0x03) << 1); - /* We write two bytes, so we dont have to mess with 16 bit access */ + /* We write two bytes, so we don't have to mess with 16-bit access */ ofs += mtd->oobsize + (bbm->badblockpos & ~0x01); /* FIXME : What to do when marking SLC block in partition * with MLC erasesize? For now, it is not advisable to @@ -2557,7 +2557,7 @@ static void onenand_unlock_all(struct mtd_info *mtd) #ifdef CONFIG_MTD_ONENAND_OTP -/* Interal OTP operation */ +/* Internal OTP operation */ typedef int (*otp_op_t)(struct mtd_info *mtd, loff_t form, size_t len, size_t *retlen, u_char *buf); @@ -2921,7 +2921,7 @@ static void onenand_check_features(struct mtd_info *mtd) this->options |= ONENAND_HAS_2PLANE; case ONENAND_DEVICE_DENSITY_2Gb: - /* 2Gb DDP don't have 2 plane */ + /* 2Gb DDP does not have 2 plane */ if (!ONENAND_IS_DDP(this)) this->options |= ONENAND_HAS_2PLANE; this->options |= ONENAND_HAS_UNLOCK_ALL; @@ -3364,7 +3364,7 @@ static int onenand_probe(struct mtd_info *mtd) /* It's real page size */ this->writesize = mtd->writesize; - /* REVIST: Multichip handling */ + /* REVISIT: Multichip handling */ if (FLEXONENAND(this)) flexonenand_get_size(mtd); diff --git a/drivers/mtd/tests/mtd_oobtest.c b/drivers/mtd/tests/mtd_oobtest.c index a18e8d2f2557..5553cd4eab20 100644 --- a/drivers/mtd/tests/mtd_oobtest.c +++ b/drivers/mtd/tests/mtd_oobtest.c @@ -512,7 +512,7 @@ static int __init mtd_oobtest_init(void) goto out; addr0 = 0; - for (i = 0; bbt[i] && i < ebcnt; ++i) + for (i = 0; i < ebcnt && bbt[i]; ++i) addr0 += mtd->erasesize; /* Attempt to write off end of OOB */ diff --git a/drivers/mtd/tests/mtd_pagetest.c b/drivers/mtd/tests/mtd_pagetest.c index 9648818b9e2c..103cac480fee 100644 --- a/drivers/mtd/tests/mtd_pagetest.c +++ b/drivers/mtd/tests/mtd_pagetest.c @@ -116,11 +116,11 @@ static int verify_eraseblock(int ebnum) loff_t addr = ebnum * mtd->erasesize; addr0 = 0; - for (i = 0; bbt[i] && i < ebcnt; ++i) + for (i = 0; i < ebcnt && bbt[i]; ++i) addr0 += mtd->erasesize; addrn = mtd->size; - for (i = 0; bbt[ebcnt - i - 1] && i < ebcnt; ++i) + for (i = 0; i < ebcnt && bbt[ebcnt - i - 1]; ++i) addrn -= mtd->erasesize; set_random_data(writebuf, mtd->erasesize); @@ -219,11 +219,11 @@ static int crosstest(void) memset(pp1, 0, pgsize * 4); addr0 = 0; - for (i = 0; bbt[i] && i < ebcnt; ++i) + for (i = 0; i < ebcnt && bbt[i]; ++i) addr0 += mtd->erasesize; addrn = mtd->size; - for (i = 0; bbt[ebcnt - i - 1] && i < ebcnt; ++i) + for (i = 0; i < ebcnt && bbt[ebcnt - i - 1]; ++i) addrn -= mtd->erasesize; /* Read 2nd-to-last page to pp1 */ @@ -317,7 +317,7 @@ static int erasecrosstest(void) ebnum = 0; addr0 = 0; - for (i = 0; bbt[i] && i < ebcnt; ++i) { + for (i = 0; i < ebcnt && bbt[i]; ++i) { addr0 += mtd->erasesize; ebnum += 1; } @@ -413,7 +413,7 @@ static int erasetest(void) ebnum = 0; addr0 = 0; - for (i = 0; bbt[i] && i < ebcnt; ++i) { + for (i = 0; i < ebcnt && bbt[i]; ++i) { addr0 += mtd->erasesize; ebnum += 1; } diff --git a/drivers/mtd/ubi/build.c b/drivers/mtd/ubi/build.c index e1f7d0a78b9d..14cec04c34f9 100644 --- a/drivers/mtd/ubi/build.c +++ b/drivers/mtd/ubi/build.c @@ -42,6 +42,7 @@ #include <linux/log2.h> #include <linux/kthread.h> #include <linux/reboot.h> +#include <linux/kernel.h> #include "ubi.h" /* Maximum length of the 'mtd=' parameter */ @@ -1257,7 +1258,7 @@ static int __init bytes_str_to_int(const char *str) unsigned long result; result = simple_strtoul(str, &endp, 0); - if (str == endp || result < 0) { + if (str == endp || result >= INT_MAX) { printk(KERN_ERR "UBI error: incorrect bytes count: \"%s\"\n", str); return -EINVAL; diff --git a/drivers/mtd/ubi/scan.c b/drivers/mtd/ubi/scan.c index e7161adc419d..90af61a2c3e4 100644 --- a/drivers/mtd/ubi/scan.c +++ b/drivers/mtd/ubi/scan.c @@ -794,16 +794,15 @@ static int process_eb(struct ubi_device *ubi, struct ubi_scan_info *si, * number. */ image_seq = be32_to_cpu(ech->image_seq); - if (!si->image_seq_set) { + if (!ubi->image_seq && image_seq) ubi->image_seq = image_seq; - si->image_seq_set = 1; - } else if (ubi->image_seq && ubi->image_seq != image_seq) { + if (ubi->image_seq && image_seq && + ubi->image_seq != image_seq) { ubi_err("bad image sequence number %d in PEB %d, " "expected %d", image_seq, pnum, ubi->image_seq); ubi_dbg_dump_ec_hdr(ech); return -EINVAL; } - } /* OK, we've done with the EC header, let's look at the VID header */ diff --git a/drivers/mtd/ubi/scan.h b/drivers/mtd/ubi/scan.h index bab31695dace..ff179ad7ca55 100644 --- a/drivers/mtd/ubi/scan.h +++ b/drivers/mtd/ubi/scan.h @@ -103,7 +103,6 @@ struct ubi_scan_volume { * @ec_sum: a temporary variable used when calculating @mean_ec * @ec_count: a temporary variable used when calculating @mean_ec * @corr_count: count of corrupted PEBs - * @image_seq_set: indicates @ubi->image_seq is known * * This data structure contains the result of scanning and may be used by other * UBI sub-systems to build final UBI data structures, further error-recovery @@ -127,7 +126,6 @@ struct ubi_scan_info { uint64_t ec_sum; int ec_count; int corr_count; - int image_seq_set; }; struct ubi_device; diff --git a/drivers/net/3c59x.c b/drivers/net/3c59x.c index 7adff4d0960d..975e25b19ebe 100644 --- a/drivers/net/3c59x.c +++ b/drivers/net/3c59x.c @@ -805,58 +805,54 @@ static void poll_vortex(struct net_device *dev) #ifdef CONFIG_PM -static int vortex_suspend(struct pci_dev *pdev, pm_message_t state) +static int vortex_suspend(struct device *dev) { - struct net_device *dev = pci_get_drvdata(pdev); + struct pci_dev *pdev = to_pci_dev(dev); + struct net_device *ndev = pci_get_drvdata(pdev); + + if (!ndev || !netif_running(ndev)) + return 0; + + netif_device_detach(ndev); + vortex_down(ndev, 1); - if (dev && netdev_priv(dev)) { - if (netif_running(dev)) { - netif_device_detach(dev); - vortex_down(dev, 1); - } - pci_save_state(pdev); - pci_enable_wake(pdev, pci_choose_state(pdev, state), 0); - free_irq(dev->irq, dev); - pci_disable_device(pdev); - pci_set_power_state(pdev, pci_choose_state(pdev, state)); - } return 0; } -static int vortex_resume(struct pci_dev *pdev) +static int vortex_resume(struct device *dev) { - struct net_device *dev = pci_get_drvdata(pdev); - struct vortex_private *vp = netdev_priv(dev); + struct pci_dev *pdev = to_pci_dev(dev); + struct net_device *ndev = pci_get_drvdata(pdev); int err; - if (dev && vp) { - pci_set_power_state(pdev, PCI_D0); - pci_restore_state(pdev); - err = pci_enable_device(pdev); - if (err) { - pr_warning("%s: Could not enable device\n", - dev->name); - return err; - } - pci_set_master(pdev); - if (request_irq(dev->irq, vp->full_bus_master_rx ? - &boomerang_interrupt : &vortex_interrupt, IRQF_SHARED, dev->name, dev)) { - pr_warning("%s: Could not reserve IRQ %d\n", dev->name, dev->irq); - pci_disable_device(pdev); - return -EBUSY; - } - if (netif_running(dev)) { - err = vortex_up(dev); - if (err) - return err; - else - netif_device_attach(dev); - } - } + if (!ndev || !netif_running(ndev)) + return 0; + + err = vortex_up(ndev); + if (err) + return err; + + netif_device_attach(ndev); + return 0; } -#endif /* CONFIG_PM */ +static struct dev_pm_ops vortex_pm_ops = { + .suspend = vortex_suspend, + .resume = vortex_resume, + .freeze = vortex_suspend, + .thaw = vortex_resume, + .poweroff = vortex_suspend, + .restore = vortex_resume, +}; + +#define VORTEX_PM_OPS (&vortex_pm_ops) + +#else /* !CONFIG_PM */ + +#define VORTEX_PM_OPS NULL + +#endif /* !CONFIG_PM */ #ifdef CONFIG_EISA static struct eisa_device_id vortex_eisa_ids[] = { @@ -3205,10 +3201,7 @@ static struct pci_driver vortex_driver = { .probe = vortex_init_one, .remove = __devexit_p(vortex_remove_one), .id_table = vortex_pci_tbl, -#ifdef CONFIG_PM - .suspend = vortex_suspend, - .resume = vortex_resume, -#endif + .driver.pm = VORTEX_PM_OPS, }; diff --git a/drivers/net/8139cp.c b/drivers/net/8139cp.c index 462d9f59c53a..83a1922e68e0 100644 --- a/drivers/net/8139cp.c +++ b/drivers/net/8139cp.c @@ -87,7 +87,7 @@ /* These identify the driver base version and may not be removed. */ static char version[] = -KERN_INFO DRV_NAME ": 10/100 PCI Ethernet driver v" DRV_VERSION " (" DRV_RELDATE ")\n"; +DRV_NAME ": 10/100 PCI Ethernet driver v" DRV_VERSION " (" DRV_RELDATE ")\n"; MODULE_AUTHOR("Jeff Garzik <jgarzik@pobox.com>"); MODULE_DESCRIPTION("RealTek RTL-8139C+ series 10/100 PCI Ethernet driver"); diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig index ed5741b2e701..b2f71f79baaf 100644 --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig @@ -975,7 +975,7 @@ config ENC28J60_WRITEVERIFY config ETHOC tristate "OpenCores 10/100 Mbps Ethernet MAC support" - depends on NET_ETHERNET && HAS_IOMEM + depends on NET_ETHERNET && HAS_IOMEM && HAS_DMA select MII select PHYLIB select CRC32 @@ -1738,6 +1738,14 @@ config KS8851 help SPI driver for Micrel KS8851 SPI attached network chip. +config KS8851_MLL + tristate "Micrel KS8851 MLL" + depends on HAS_IOMEM + select MII + help + This platform driver is for Micrel KS8851 Address/data bus + multiplexed network chip. + config VIA_RHINE tristate "VIA Rhine support" depends on NET_PCI && PCI @@ -1875,7 +1883,7 @@ config 68360_ENET config FEC bool "FEC ethernet controller (of ColdFire and some i.MX CPUs)" - depends on M523x || M527x || M5272 || M528x || M520x || M532x || MACH_MX27 || ARCH_MX35 + depends on M523x || M527x || M5272 || M528x || M520x || M532x || MACH_MX27 || ARCH_MX35 || ARCH_MX25 help Say Y here if you want to use the built-in 10/100 Fast ethernet controller on some Motorola ColdFire and Freescale i.MX processors. @@ -2475,6 +2483,8 @@ config S6GMAC To compile this driver as a module, choose M here. The module will be called s6gmac. +source "drivers/net/stmmac/Kconfig" + endif # NETDEV_1000 # @@ -3223,4 +3233,12 @@ config VIRTIO_NET This is the virtual network driver for virtio. It can be used with lguest or QEMU based VMMs (like KVM or Xen). Say Y or M. +config VMXNET3 + tristate "VMware VMXNET3 ethernet driver" + depends on PCI && X86 && INET + help + This driver supports VMware's vmxnet3 virtual ethernet NIC. + To compile this driver as a module, choose M here: the + module will be called vmxnet3. + endif # NETDEVICES diff --git a/drivers/net/Makefile b/drivers/net/Makefile index ae8cd30f13d6..246323d7f161 100644 --- a/drivers/net/Makefile +++ b/drivers/net/Makefile @@ -2,6 +2,10 @@ # Makefile for the Linux network (ethercard) device drivers. # +obj-$(CONFIG_MII) += mii.o +obj-$(CONFIG_MDIO) += mdio.o +obj-$(CONFIG_PHYLIB) += phy/ + obj-$(CONFIG_TI_DAVINCI_EMAC) += davinci_emac.o obj-$(CONFIG_E1000) += e1000/ @@ -26,6 +30,7 @@ obj-$(CONFIG_TEHUTI) += tehuti.o obj-$(CONFIG_ENIC) += enic/ obj-$(CONFIG_JME) += jme.o obj-$(CONFIG_BE2NET) += benet/ +obj-$(CONFIG_VMXNET3) += vmxnet3/ gianfar_driver-objs := gianfar.o \ gianfar_ethtool.o \ @@ -89,20 +94,18 @@ obj-$(CONFIG_SKY2) += sky2.o obj-$(CONFIG_SKFP) += skfp/ obj-$(CONFIG_KS8842) += ks8842.o obj-$(CONFIG_KS8851) += ks8851.o +obj-$(CONFIG_KS8851_MLL) += ks8851_mll.o obj-$(CONFIG_VIA_RHINE) += via-rhine.o obj-$(CONFIG_VIA_VELOCITY) += via-velocity.o obj-$(CONFIG_ADAPTEC_STARFIRE) += starfire.o obj-$(CONFIG_RIONET) += rionet.o obj-$(CONFIG_SH_ETH) += sh_eth.o +obj-$(CONFIG_STMMAC_ETH) += stmmac/ # # end link order section # -obj-$(CONFIG_MII) += mii.o -obj-$(CONFIG_MDIO) += mdio.o -obj-$(CONFIG_PHYLIB) += phy/ - obj-$(CONFIG_SUNDANCE) += sundance.o obj-$(CONFIG_HAMACHI) += hamachi.o obj-$(CONFIG_NET) += Space.o loopback.o diff --git a/drivers/net/acenic.c b/drivers/net/acenic.c index 5f0b05c2d71f..d82a9a994753 100644 --- a/drivers/net/acenic.c +++ b/drivers/net/acenic.c @@ -1209,7 +1209,8 @@ static int __devinit ace_init(struct net_device *dev) memset(ap->info, 0, sizeof(struct ace_info)); memset(ap->skb, 0, sizeof(struct ace_skb)); - if (ace_load_firmware(dev)) + ecode = ace_load_firmware(dev); + if (ecode) goto init_error; ap->fw_running = 0; diff --git a/drivers/net/arm/ep93xx_eth.c b/drivers/net/arm/ep93xx_eth.c index 2be49c817995..b25467ac895c 100644 --- a/drivers/net/arm/ep93xx_eth.c +++ b/drivers/net/arm/ep93xx_eth.c @@ -628,15 +628,6 @@ static int ep93xx_open(struct net_device *dev) if (ep93xx_alloc_buffers(ep)) return -ENOMEM; - if (is_zero_ether_addr(dev->dev_addr)) { - random_ether_addr(dev->dev_addr); - printk(KERN_INFO "%s: generated random MAC address " - "%.2x:%.2x:%.2x:%.2x:%.2x:%.2x.\n", dev->name, - dev->dev_addr[0], dev->dev_addr[1], - dev->dev_addr[2], dev->dev_addr[3], - dev->dev_addr[4], dev->dev_addr[5]); - } - napi_enable(&ep->napi); if (ep93xx_start_hw(dev)) { @@ -877,6 +868,9 @@ static int ep93xx_eth_probe(struct platform_device *pdev) ep->mii.mdio_write = ep93xx_mdio_write; ep->mdc_divisor = 40; /* Max HCLK 100 MHz, min MDIO clk 2.5 MHz. */ + if (is_zero_ether_addr(dev->dev_addr)) + random_ether_addr(dev->dev_addr); + err = register_netdev(dev); if (err) { dev_err(&pdev->dev, "Failed to register netdev\n"); diff --git a/drivers/net/atl1c/atl1c_main.c b/drivers/net/atl1c/atl1c_main.c index be2c6cfe6e84..1372e9a99f5b 100644 --- a/drivers/net/atl1c/atl1c_main.c +++ b/drivers/net/atl1c/atl1c_main.c @@ -2296,7 +2296,7 @@ static int atl1c_suspend(struct pci_dev *pdev, pm_message_t state) u32 ctrl; u32 mac_ctrl_data; u32 master_ctrl_data; - u32 wol_ctrl_data; + u32 wol_ctrl_data = 0; u16 mii_bmsr_data; u16 save_autoneg_advertised; u16 mii_intr_status_data; diff --git a/drivers/net/au1000_eth.c b/drivers/net/au1000_eth.c index fdf5937233fc..3f4b4300f533 100644 --- a/drivers/net/au1000_eth.c +++ b/drivers/net/au1000_eth.c @@ -34,6 +34,7 @@ * * */ +#include <linux/capability.h> #include <linux/dma-mapping.h> #include <linux/module.h> #include <linux/kernel.h> @@ -721,7 +722,7 @@ static inline void update_rx_stats(struct net_device *dev, u32 status) ps->rx_errors++; if (status & RX_MISSED_FRAME) ps->rx_missed_errors++; - if (status & (RX_OVERLEN | RX_OVERLEN | RX_LEN_ERROR)) + if (status & (RX_OVERLEN | RX_RUNT | RX_LEN_ERROR)) ps->rx_length_errors++; if (status & RX_CRC_ERROR) ps->rx_crc_errors++; @@ -794,8 +795,6 @@ static int au1000_rx(struct net_device *dev) printk("rx len error\n"); if (status & RX_U_CNTRL_FRAME) printk("rx u control frame\n"); - if (status & RX_MISSED_FRAME) - printk("rx miss\n"); } } prxd->buff_stat = (u32)(pDB->dma_addr | RX_DMA_ENABLE); @@ -1089,7 +1088,14 @@ static struct net_device * au1000_probe(int port_num) return NULL; } - if ((err = register_netdev(dev)) != 0) { + dev->base_addr = base; + dev->irq = irq; + dev->netdev_ops = &au1000_netdev_ops; + SET_ETHTOOL_OPS(dev, &au1000_ethtool_ops); + dev->watchdog_timeo = ETH_TX_TIMEOUT; + + err = register_netdev(dev); + if (err != 0) { printk(KERN_ERR "%s: Cannot register net device, error %d\n", DRV_NAME, err); free_netdev(dev); @@ -1210,12 +1216,6 @@ static struct net_device * au1000_probe(int port_num) aup->tx_db_inuse[i] = pDB; } - dev->base_addr = base; - dev->irq = irq; - dev->netdev_ops = &au1000_netdev_ops; - SET_ETHTOOL_OPS(dev, &au1000_ethtool_ops); - dev->watchdog_timeo = ETH_TX_TIMEOUT; - /* * The boot code uses the ethernet controller, so reset it to start * fresh. au1000_init() expects that the device is in reset state. diff --git a/drivers/net/b44.c b/drivers/net/b44.c index e046943ef29d..2a9132343b66 100644 --- a/drivers/net/b44.c +++ b/drivers/net/b44.c @@ -912,9 +912,6 @@ static irqreturn_t b44_interrupt(int irq, void *dev_id) bp->istat = istat; __b44_disable_ints(bp); __napi_schedule(&bp->napi); - } else { - printk(KERN_ERR PFX "%s: Error, poll already scheduled\n", - dev->name); } irq_ack: diff --git a/drivers/net/bcm63xx_enet.c b/drivers/net/bcm63xx_enet.c index 09d270913c50..ba29dc319b34 100644 --- a/drivers/net/bcm63xx_enet.c +++ b/drivers/net/bcm63xx_enet.c @@ -90,7 +90,7 @@ static int do_mdio_op(struct bcm_enet_priv *priv, unsigned int data) if (enet_readl(priv, ENET_IR_REG) & ENET_IR_MII) break; udelay(1); - } while (limit-- >= 0); + } while (limit-- > 0); return (limit < 0) ? 1 : 0; } diff --git a/drivers/net/benet/be.h b/drivers/net/benet/be.h index 684c6fe24c8d..3b79a225628a 100644 --- a/drivers/net/benet/be.h +++ b/drivers/net/benet/be.h @@ -258,6 +258,9 @@ struct be_adapter { bool link_up; u32 port_num; bool promiscuous; + u32 cap; + u32 rx_fc; /* Rx flow control */ + u32 tx_fc; /* Tx flow control */ }; extern const struct ethtool_ops be_ethtool_ops; diff --git a/drivers/net/benet/be_cmds.c b/drivers/net/benet/be_cmds.c index 3dd76c4170bf..28a0eda92680 100644 --- a/drivers/net/benet/be_cmds.c +++ b/drivers/net/benet/be_cmds.c @@ -243,15 +243,26 @@ static int be_POST_stage_get(struct be_adapter *adapter, u16 *stage) int be_cmd_POST(struct be_adapter *adapter) { - u16 stage, error; + u16 stage; + int status, timeout = 0; - error = be_POST_stage_get(adapter, &stage); - if (error || stage != POST_STAGE_ARMFW_RDY) { - dev_err(&adapter->pdev->dev, "POST failed.\n"); - return -1; - } + do { + status = be_POST_stage_get(adapter, &stage); + if (status) { + dev_err(&adapter->pdev->dev, "POST error; stage=0x%x\n", + stage); + return -1; + } else if (stage != POST_STAGE_ARMFW_RDY) { + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(2 * HZ); + timeout += 2; + } else { + return 0; + } + } while (timeout < 20); - return 0; + dev_err(&adapter->pdev->dev, "POST timeout; stage=0x%x\n", stage); + return -1; } static inline void *embedded_payload(struct be_mcc_wrb *wrb) @@ -729,8 +740,8 @@ int be_cmd_q_destroy(struct be_adapter *adapter, struct be_queue_info *q, /* Create an rx filtering policy configuration on an i/f * Uses mbox */ -int be_cmd_if_create(struct be_adapter *adapter, u32 flags, u8 *mac, - bool pmac_invalid, u32 *if_handle, u32 *pmac_id) +int be_cmd_if_create(struct be_adapter *adapter, u32 cap_flags, u32 en_flags, + u8 *mac, bool pmac_invalid, u32 *if_handle, u32 *pmac_id) { struct be_mcc_wrb *wrb; struct be_cmd_req_if_create *req; @@ -746,8 +757,8 @@ int be_cmd_if_create(struct be_adapter *adapter, u32 flags, u8 *mac, be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON, OPCODE_COMMON_NTWK_INTERFACE_CREATE, sizeof(*req)); - req->capability_flags = cpu_to_le32(flags); - req->enable_flags = cpu_to_le32(flags); + req->capability_flags = cpu_to_le32(cap_flags); + req->enable_flags = cpu_to_le32(en_flags); req->pmac_invalid = pmac_invalid; if (!pmac_invalid) memcpy(req->mac_addr, mac, ETH_ALEN); @@ -1068,7 +1079,7 @@ int be_cmd_get_flow_control(struct be_adapter *adapter, u32 *tx_fc, u32 *rx_fc) } /* Uses mbox */ -int be_cmd_query_fw_cfg(struct be_adapter *adapter, u32 *port_num) +int be_cmd_query_fw_cfg(struct be_adapter *adapter, u32 *port_num, u32 *cap) { struct be_mcc_wrb *wrb; struct be_cmd_req_query_fw_cfg *req; @@ -1088,6 +1099,7 @@ int be_cmd_query_fw_cfg(struct be_adapter *adapter, u32 *port_num) if (!status) { struct be_cmd_resp_query_fw_cfg *resp = embedded_payload(wrb); *port_num = le32_to_cpu(resp->phys_port); + *cap = le32_to_cpu(resp->function_cap); } spin_unlock(&adapter->mbox_lock); @@ -1128,7 +1140,6 @@ int be_cmd_write_flashrom(struct be_adapter *adapter, struct be_dma_mem *cmd, spin_lock_bh(&adapter->mcc_lock); wrb = wrb_from_mccq(adapter); - req = embedded_payload(wrb); sge = nonembedded_sgl(wrb); be_wrb_hdr_prepare(wrb, cmd->size, false, 1); diff --git a/drivers/net/benet/be_cmds.h b/drivers/net/benet/be_cmds.h index 93e432f3d926..e5f9676cf1bc 100644 --- a/drivers/net/benet/be_cmds.h +++ b/drivers/net/benet/be_cmds.h @@ -62,13 +62,13 @@ enum { MCC_STATUS_QUEUE_FLUSHING = 0x4, /* The command is completing with a DMA error */ MCC_STATUS_DMA_FAILED = 0x5, - MCC_STATUS_NOT_SUPPORTED = 0x66 + MCC_STATUS_NOT_SUPPORTED = 66 }; #define CQE_STATUS_COMPL_MASK 0xFFFF #define CQE_STATUS_COMPL_SHIFT 0 /* bits 0 - 15 */ #define CQE_STATUS_EXTD_MASK 0xFFFF -#define CQE_STATUS_EXTD_SHIFT 0 /* bits 0 - 15 */ +#define CQE_STATUS_EXTD_SHIFT 16 /* bits 16 - 31 */ struct be_mcc_compl { u32 status; /* dword 0 */ @@ -720,8 +720,9 @@ extern int be_cmd_mac_addr_query(struct be_adapter *adapter, u8 *mac_addr, extern int be_cmd_pmac_add(struct be_adapter *adapter, u8 *mac_addr, u32 if_id, u32 *pmac_id); extern int be_cmd_pmac_del(struct be_adapter *adapter, u32 if_id, u32 pmac_id); -extern int be_cmd_if_create(struct be_adapter *adapter, u32 if_flags, u8 *mac, - bool pmac_invalid, u32 *if_handle, u32 *pmac_id); +extern int be_cmd_if_create(struct be_adapter *adapter, u32 cap_flags, + u32 en_flags, u8 *mac, bool pmac_invalid, + u32 *if_handle, u32 *pmac_id); extern int be_cmd_if_destroy(struct be_adapter *adapter, u32 if_handle); extern int be_cmd_eq_create(struct be_adapter *adapter, struct be_queue_info *eq, int eq_delay); @@ -760,7 +761,8 @@ extern int be_cmd_set_flow_control(struct be_adapter *adapter, u32 tx_fc, u32 rx_fc); extern int be_cmd_get_flow_control(struct be_adapter *adapter, u32 *tx_fc, u32 *rx_fc); -extern int be_cmd_query_fw_cfg(struct be_adapter *adapter, u32 *port_num); +extern int be_cmd_query_fw_cfg(struct be_adapter *adapter, + u32 *port_num, u32 *cap); extern int be_cmd_reset_function(struct be_adapter *adapter); extern int be_process_mcc(struct be_adapter *adapter); extern int be_cmd_write_flashrom(struct be_adapter *adapter, diff --git a/drivers/net/benet/be_ethtool.c b/drivers/net/benet/be_ethtool.c index 11445df3dbc0..f0fd95b43c07 100644 --- a/drivers/net/benet/be_ethtool.c +++ b/drivers/net/benet/be_ethtool.c @@ -323,10 +323,12 @@ be_set_pauseparam(struct net_device *netdev, struct ethtool_pauseparam *ecmd) if (ecmd->autoneg != 0) return -EINVAL; + adapter->tx_fc = ecmd->tx_pause; + adapter->rx_fc = ecmd->rx_pause; - status = be_cmd_set_flow_control(adapter, ecmd->tx_pause, - ecmd->rx_pause); - if (!status) + status = be_cmd_set_flow_control(adapter, + adapter->tx_fc, adapter->rx_fc); + if (status) dev_warn(&adapter->pdev->dev, "Pause param set failed.\n"); return status; @@ -358,7 +360,7 @@ const struct ethtool_ops be_ethtool_ops = { .get_rx_csum = be_get_rx_csum, .set_rx_csum = be_set_rx_csum, .get_tx_csum = ethtool_op_get_tx_csum, - .set_tx_csum = ethtool_op_set_tx_csum, + .set_tx_csum = ethtool_op_set_tx_hw_csum, .get_sg = ethtool_op_get_sg, .set_sg = ethtool_op_set_sg, .get_tso = ethtool_op_get_tso, diff --git a/drivers/net/benet/be_main.c b/drivers/net/benet/be_main.c index 409cf0595903..876b357101fa 100644 --- a/drivers/net/benet/be_main.c +++ b/drivers/net/benet/be_main.c @@ -197,7 +197,7 @@ void netdev_stats_update(struct be_adapter *adapter) /* no space available in linux */ dev_stats->tx_dropped = 0; - dev_stats->multicast = port_stats->tx_multicastframes; + dev_stats->multicast = port_stats->rx_multicast_frames; dev_stats->collisions = 0; /* detailed tx_errors */ @@ -747,9 +747,16 @@ static void be_rx_compl_process(struct be_adapter *adapter, struct be_eth_rx_compl *rxcp) { struct sk_buff *skb; - u32 vtp, vid; + u32 vlanf, vid; + u8 vtm; - vtp = AMAP_GET_BITS(struct amap_eth_rx_compl, vtp, rxcp); + vlanf = AMAP_GET_BITS(struct amap_eth_rx_compl, vtp, rxcp); + vtm = AMAP_GET_BITS(struct amap_eth_rx_compl, vtm, rxcp); + + /* vlanf could be wrongly set in some cards. + * ignore if vtm is not set */ + if ((adapter->cap == 0x400) && !vtm) + vlanf = 0; skb = netdev_alloc_skb(adapter->netdev, BE_HDR_LEN + NET_IP_ALIGN); if (!skb) { @@ -772,7 +779,7 @@ static void be_rx_compl_process(struct be_adapter *adapter, skb->protocol = eth_type_trans(skb, adapter->netdev); skb->dev = adapter->netdev; - if (vtp) { + if (vlanf) { if (!adapter->vlan_grp || adapter->num_vlans == 0) { kfree_skb(skb); return; @@ -797,11 +804,18 @@ static void be_rx_compl_process_gro(struct be_adapter *adapter, struct be_eq_obj *eq_obj = &adapter->rx_eq; u32 num_rcvd, pkt_size, remaining, vlanf, curr_frag_len; u16 i, rxq_idx = 0, vid, j; + u8 vtm; num_rcvd = AMAP_GET_BITS(struct amap_eth_rx_compl, numfrags, rxcp); pkt_size = AMAP_GET_BITS(struct amap_eth_rx_compl, pktsize, rxcp); vlanf = AMAP_GET_BITS(struct amap_eth_rx_compl, vtp, rxcp); rxq_idx = AMAP_GET_BITS(struct amap_eth_rx_compl, fragndx, rxcp); + vtm = AMAP_GET_BITS(struct amap_eth_rx_compl, vtm, rxcp); + + /* vlanf could be wrongly set in some cards. + * ignore if vtm is not set */ + if ((adapter->cap == 0x400) && !vtm) + vlanf = 0; skb = napi_get_frags(&eq_obj->napi); if (!skb) { @@ -1596,29 +1610,42 @@ static int be_open(struct net_device *netdev) status = be_cmd_link_status_query(adapter, &link_up); if (status) - return status; + goto ret_sts; be_link_status_update(adapter, link_up); + status = be_vid_config(adapter); + if (status) + goto ret_sts; + + status = be_cmd_set_flow_control(adapter, + adapter->tx_fc, adapter->rx_fc); + if (status) + goto ret_sts; + schedule_delayed_work(&adapter->work, msecs_to_jiffies(100)); - return 0; +ret_sts: + return status; } static int be_setup(struct be_adapter *adapter) { struct net_device *netdev = adapter->netdev; - u32 if_flags; + u32 cap_flags, en_flags; int status; - if_flags = BE_IF_FLAGS_BROADCAST | BE_IF_FLAGS_PROMISCUOUS | - BE_IF_FLAGS_MCAST_PROMISCUOUS | BE_IF_FLAGS_UNTAGGED | - BE_IF_FLAGS_PASS_L3L4_ERRORS; - status = be_cmd_if_create(adapter, if_flags, netdev->dev_addr, - false/* pmac_invalid */, &adapter->if_handle, - &adapter->pmac_id); + cap_flags = BE_IF_FLAGS_UNTAGGED | BE_IF_FLAGS_BROADCAST | + BE_IF_FLAGS_MCAST_PROMISCUOUS | + BE_IF_FLAGS_PROMISCUOUS | + BE_IF_FLAGS_PASS_L3L4_ERRORS; + en_flags = BE_IF_FLAGS_UNTAGGED | BE_IF_FLAGS_BROADCAST | + BE_IF_FLAGS_PASS_L3L4_ERRORS; + + status = be_cmd_if_create(adapter, cap_flags, en_flags, + netdev->dev_addr, false/* pmac_invalid */, + &adapter->if_handle, &adapter->pmac_id); if (status != 0) goto do_none; - status = be_tx_queues_create(adapter); if (status != 0) goto if_destroy; @@ -1631,17 +1658,8 @@ static int be_setup(struct be_adapter *adapter) if (status != 0) goto rx_qs_destroy; - status = be_vid_config(adapter); - if (status != 0) - goto mccqs_destroy; - - status = be_cmd_set_flow_control(adapter, true, true); - if (status != 0) - goto mccqs_destroy; return 0; -mccqs_destroy: - be_mcc_queues_destroy(adapter); rx_qs_destroy: be_rx_queues_destroy(adapter); tx_qs_destroy: @@ -1885,13 +1903,17 @@ static void be_netdev_init(struct net_device *netdev) struct be_adapter *adapter = netdev_priv(netdev); netdev->features |= NETIF_F_SG | NETIF_F_HW_VLAN_RX | NETIF_F_TSO | - NETIF_F_HW_VLAN_TX | NETIF_F_HW_VLAN_FILTER | NETIF_F_IP_CSUM | - NETIF_F_IPV6_CSUM | NETIF_F_GRO; + NETIF_F_HW_VLAN_TX | NETIF_F_HW_VLAN_FILTER | NETIF_F_HW_CSUM | + NETIF_F_GRO; netdev->flags |= IFF_MULTICAST; adapter->rx_csum = true; + /* Default settings for Rx and Tx flow control */ + adapter->rx_fc = true; + adapter->tx_fc = true; + netif_set_gso_max_size(netdev, 65535); BE_SET_NETDEV_OPS(netdev, &be_netdev_ops); @@ -2041,11 +2063,16 @@ static int be_hw_up(struct be_adapter *adapter) if (status) return status; + status = be_cmd_reset_function(adapter); + if (status) + return status; + status = be_cmd_get_fw_ver(adapter, adapter->fw_ver); if (status) return status; - status = be_cmd_query_fw_cfg(adapter, &adapter->port_num); + status = be_cmd_query_fw_cfg(adapter, + &adapter->port_num, &adapter->cap); return status; } @@ -2093,10 +2120,6 @@ static int __devinit be_probe(struct pci_dev *pdev, if (status) goto free_netdev; - status = be_cmd_reset_function(adapter); - if (status) - goto ctrl_clean; - status = be_stats_init(adapter); if (status) goto ctrl_clean; @@ -2153,6 +2176,7 @@ static int be_suspend(struct pci_dev *pdev, pm_message_t state) be_close(netdev); rtnl_unlock(); } + be_cmd_get_flow_control(adapter, &adapter->tx_fc, &adapter->rx_fc); be_clear(adapter); pci_save_state(pdev); diff --git a/drivers/net/bnx2.h b/drivers/net/bnx2.h index 6c7f795d12de..a4d83409f205 100644 --- a/drivers/net/bnx2.h +++ b/drivers/net/bnx2.h @@ -361,9 +361,12 @@ struct l2_fhdr { #define BNX2_L2CTX_CTX_TYPE_CTX_BD_CHN_TYPE_VALUE (1<<28) #define BNX2_L2CTX_HOST_BDIDX 0x00000004 -#define BNX2_L2CTX_STATUSB_NUM_SHIFT 16 -#define BNX2_L2CTX_STATUSB_NUM(sb_id) \ - (((sb_id) > 0) ? (((sb_id) + 7) << BNX2_L2CTX_STATUSB_NUM_SHIFT) : 0) +#define BNX2_L2CTX_L5_STATUSB_NUM_SHIFT 16 +#define BNX2_L2CTX_L2_STATUSB_NUM_SHIFT 24 +#define BNX2_L2CTX_L5_STATUSB_NUM(sb_id) \ + (((sb_id) > 0) ? (((sb_id) + 7) << BNX2_L2CTX_L5_STATUSB_NUM_SHIFT) : 0) +#define BNX2_L2CTX_L2_STATUSB_NUM(sb_id) \ + (((sb_id) > 0) ? (((sb_id) + 7) << BNX2_L2CTX_L2_STATUSB_NUM_SHIFT) : 0) #define BNX2_L2CTX_HOST_BSEQ 0x00000008 #define BNX2_L2CTX_NX_BSEQ 0x0000000c #define BNX2_L2CTX_NX_BDHADDR_HI 0x00000010 diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c index 69c5b15e22da..40fb5eefc72e 100644 --- a/drivers/net/bonding/bond_main.c +++ b/drivers/net/bonding/bond_main.c @@ -691,7 +691,7 @@ static int bond_check_dev_link(struct bonding *bond, struct net_device *slave_dev, int reporting) { const struct net_device_ops *slave_ops = slave_dev->netdev_ops; - static int (*ioctl)(struct net_device *, struct ifreq *, int); + int (*ioctl)(struct net_device *, struct ifreq *, int); struct ifreq ifr; struct mii_ioctl_data *mii; @@ -3665,10 +3665,10 @@ static int bond_xmit_hash_policy_l23(struct sk_buff *skb, if (skb->protocol == htons(ETH_P_IP)) { return ((ntohl(iph->saddr ^ iph->daddr) & 0xffff) ^ - (data->h_dest[5] ^ bond_dev->dev_addr[5])) % count; + (data->h_dest[5] ^ data->h_source[5])) % count; } - return (data->h_dest[5] ^ bond_dev->dev_addr[5]) % count; + return (data->h_dest[5] ^ data->h_source[5]) % count; } /* @@ -3695,7 +3695,7 @@ static int bond_xmit_hash_policy_l34(struct sk_buff *skb, } - return (data->h_dest[5] ^ bond_dev->dev_addr[5]) % count; + return (data->h_dest[5] ^ data->h_source[5]) % count; } /* @@ -3706,7 +3706,7 @@ static int bond_xmit_hash_policy_l2(struct sk_buff *skb, { struct ethhdr *data = (struct ethhdr *)skb->data; - return (data->h_dest[5] ^ bond_dev->dev_addr[5]) % count; + return (data->h_dest[5] ^ data->h_source[5]) % count; } /*-------------------------- Device entry points ----------------------------*/ diff --git a/drivers/net/bonding/bond_sysfs.c b/drivers/net/bonding/bond_sysfs.c index 6044e12ff9fc..8762a27a2a18 100644 --- a/drivers/net/bonding/bond_sysfs.c +++ b/drivers/net/bonding/bond_sysfs.c @@ -22,6 +22,7 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/device.h> +#include <linux/sched.h> #include <linux/sysdev.h> #include <linux/fs.h> #include <linux/types.h> @@ -1182,6 +1183,7 @@ static ssize_t bonding_store_primary(struct device *d, ": %s: Setting %s as primary slave.\n", bond->dev->name, slave->dev->name); bond->primary_slave = slave; + strcpy(bond->params.primary, slave->dev->name); bond_select_active_slave(bond); goto out; } diff --git a/drivers/net/can/Kconfig b/drivers/net/can/Kconfig index 090074372462..772f6d2489ce 100644 --- a/drivers/net/can/Kconfig +++ b/drivers/net/can/Kconfig @@ -35,52 +35,15 @@ config CAN_CALC_BITTIMING arguments "tq", "prop_seg", "phase_seg1", "phase_seg2" and "sjw". If unsure, say Y. -config CAN_SJA1000 - depends on CAN_DEV && HAS_IOMEM - tristate "Philips SJA1000" +config CAN_AT91 + tristate "Atmel AT91 onchip CAN controller" + depends on CAN_DEV && ARCH_AT91SAM9263 ---help--- - Driver for the SJA1000 CAN controllers from Philips or NXP + This is a driver for the SoC CAN controller in Atmel's AT91SAM9263. -config CAN_SJA1000_ISA - depends on CAN_SJA1000 && ISA - tristate "ISA Bus based legacy SJA1000 driver" - ---help--- - This driver adds legacy support for SJA1000 chips connected to - the ISA bus using I/O port, memory mapped or indirect access. - -config CAN_SJA1000_PLATFORM - depends on CAN_SJA1000 - tristate "Generic Platform Bus based SJA1000 driver" - ---help--- - This driver adds support for the SJA1000 chips connected to - the "platform bus" (Linux abstraction for directly to the - processor attached devices). Which can be found on various - boards from Phytec (http://www.phytec.de) like the PCM027, - PCM038. - -config CAN_SJA1000_OF_PLATFORM - depends on CAN_SJA1000 && PPC_OF - tristate "Generic OF Platform Bus based SJA1000 driver" - ---help--- - This driver adds support for the SJA1000 chips connected to - the OpenFirmware "platform bus" found on embedded systems with - OpenFirmware bindings, e.g. if you have a PowerPC based system - you may want to enable this option. - -config CAN_EMS_PCI - tristate "EMS CPC-PCI, CPC-PCIe and CPC-104P Card" - depends on PCI && CAN_SJA1000 - ---help--- - This driver is for the one, two or four channel CPC-PCI, - CPC-PCIe and CPC-104P cards from EMS Dr. Thomas Wuensche - (http://www.ems-wuensche.de). +source "drivers/net/can/sja1000/Kconfig" -config CAN_KVASER_PCI - tristate "Kvaser PCIcanx and Kvaser PCIcan PCI Cards" - depends on PCI && CAN_SJA1000 - ---help--- - This driver is for the the PCIcanx and PCIcan cards (1, 2 or - 4 channel) from Kvaser (http://www.kvaser.com). +source "drivers/net/can/usb/Kconfig" config CAN_DEBUG_DEVICES bool "CAN devices debugging messages" diff --git a/drivers/net/can/Makefile b/drivers/net/can/Makefile index 523a941b358b..0dea62721f2f 100644 --- a/drivers/net/can/Makefile +++ b/drivers/net/can/Makefile @@ -7,6 +7,9 @@ obj-$(CONFIG_CAN_VCAN) += vcan.o obj-$(CONFIG_CAN_DEV) += can-dev.o can-dev-y := dev.o +obj-y += usb/ + obj-$(CONFIG_CAN_SJA1000) += sja1000/ +obj-$(CONFIG_CAN_AT91) += at91_can.o ccflags-$(CONFIG_CAN_DEBUG_DEVICES) := -DDEBUG diff --git a/drivers/net/can/at91_can.c b/drivers/net/can/at91_can.c new file mode 100644 index 000000000000..f67ae285a35a --- /dev/null +++ b/drivers/net/can/at91_can.c @@ -0,0 +1,1186 @@ +/* + * at91_can.c - CAN network driver for AT91 SoC CAN controller + * + * (C) 2007 by Hans J. Koch <hjk@linutronix.de> + * (C) 2008, 2009 by Marc Kleine-Budde <kernel@pengutronix.de> + * + * This software may be distributed under the terms of the GNU General + * Public License ("GPL") version 2 as distributed in the 'COPYING' + * file from the main directory of the linux kernel source. + * + * Send feedback to <socketcan-users@lists.berlios.de> + * + * + * Your platform definition file should specify something like: + * + * static struct at91_can_data ek_can_data = { + * transceiver_switch = sam9263ek_transceiver_switch, + * }; + * + * at91_add_device_can(&ek_can_data); + * + */ + +#include <linux/clk.h> +#include <linux/errno.h> +#include <linux/if_arp.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/netdevice.h> +#include <linux/platform_device.h> +#include <linux/skbuff.h> +#include <linux/spinlock.h> +#include <linux/string.h> +#include <linux/types.h> + +#include <linux/can.h> +#include <linux/can/dev.h> +#include <linux/can/error.h> + +#include <mach/board.h> + +#define DRV_NAME "at91_can" +#define AT91_NAPI_WEIGHT 12 + +/* + * RX/TX Mailbox split + * don't dare to touch + */ +#define AT91_MB_RX_NUM 12 +#define AT91_MB_TX_SHIFT 2 + +#define AT91_MB_RX_FIRST 0 +#define AT91_MB_RX_LAST (AT91_MB_RX_FIRST + AT91_MB_RX_NUM - 1) + +#define AT91_MB_RX_MASK(i) ((1 << (i)) - 1) +#define AT91_MB_RX_SPLIT 8 +#define AT91_MB_RX_LOW_LAST (AT91_MB_RX_SPLIT - 1) +#define AT91_MB_RX_LOW_MASK (AT91_MB_RX_MASK(AT91_MB_RX_SPLIT)) + +#define AT91_MB_TX_NUM (1 << AT91_MB_TX_SHIFT) +#define AT91_MB_TX_FIRST (AT91_MB_RX_LAST + 1) +#define AT91_MB_TX_LAST (AT91_MB_TX_FIRST + AT91_MB_TX_NUM - 1) + +#define AT91_NEXT_PRIO_SHIFT (AT91_MB_TX_SHIFT) +#define AT91_NEXT_PRIO_MASK (0xf << AT91_MB_TX_SHIFT) +#define AT91_NEXT_MB_MASK (AT91_MB_TX_NUM - 1) +#define AT91_NEXT_MASK ((AT91_MB_TX_NUM - 1) | AT91_NEXT_PRIO_MASK) + +/* Common registers */ +enum at91_reg { + AT91_MR = 0x000, + AT91_IER = 0x004, + AT91_IDR = 0x008, + AT91_IMR = 0x00C, + AT91_SR = 0x010, + AT91_BR = 0x014, + AT91_TIM = 0x018, + AT91_TIMESTP = 0x01C, + AT91_ECR = 0x020, + AT91_TCR = 0x024, + AT91_ACR = 0x028, +}; + +/* Mailbox registers (0 <= i <= 15) */ +#define AT91_MMR(i) (enum at91_reg)(0x200 + ((i) * 0x20)) +#define AT91_MAM(i) (enum at91_reg)(0x204 + ((i) * 0x20)) +#define AT91_MID(i) (enum at91_reg)(0x208 + ((i) * 0x20)) +#define AT91_MFID(i) (enum at91_reg)(0x20C + ((i) * 0x20)) +#define AT91_MSR(i) (enum at91_reg)(0x210 + ((i) * 0x20)) +#define AT91_MDL(i) (enum at91_reg)(0x214 + ((i) * 0x20)) +#define AT91_MDH(i) (enum at91_reg)(0x218 + ((i) * 0x20)) +#define AT91_MCR(i) (enum at91_reg)(0x21C + ((i) * 0x20)) + +/* Register bits */ +#define AT91_MR_CANEN BIT(0) +#define AT91_MR_LPM BIT(1) +#define AT91_MR_ABM BIT(2) +#define AT91_MR_OVL BIT(3) +#define AT91_MR_TEOF BIT(4) +#define AT91_MR_TTM BIT(5) +#define AT91_MR_TIMFRZ BIT(6) +#define AT91_MR_DRPT BIT(7) + +#define AT91_SR_RBSY BIT(29) + +#define AT91_MMR_PRIO_SHIFT (16) + +#define AT91_MID_MIDE BIT(29) + +#define AT91_MSR_MRTR BIT(20) +#define AT91_MSR_MABT BIT(22) +#define AT91_MSR_MRDY BIT(23) +#define AT91_MSR_MMI BIT(24) + +#define AT91_MCR_MRTR BIT(20) +#define AT91_MCR_MTCR BIT(23) + +/* Mailbox Modes */ +enum at91_mb_mode { + AT91_MB_MODE_DISABLED = 0, + AT91_MB_MODE_RX = 1, + AT91_MB_MODE_RX_OVRWR = 2, + AT91_MB_MODE_TX = 3, + AT91_MB_MODE_CONSUMER = 4, + AT91_MB_MODE_PRODUCER = 5, +}; + +/* Interrupt mask bits */ +#define AT91_IRQ_MB_RX ((1 << (AT91_MB_RX_LAST + 1)) \ + - (1 << AT91_MB_RX_FIRST)) +#define AT91_IRQ_MB_TX ((1 << (AT91_MB_TX_LAST + 1)) \ + - (1 << AT91_MB_TX_FIRST)) +#define AT91_IRQ_MB_ALL (AT91_IRQ_MB_RX | AT91_IRQ_MB_TX) + +#define AT91_IRQ_ERRA (1 << 16) +#define AT91_IRQ_WARN (1 << 17) +#define AT91_IRQ_ERRP (1 << 18) +#define AT91_IRQ_BOFF (1 << 19) +#define AT91_IRQ_SLEEP (1 << 20) +#define AT91_IRQ_WAKEUP (1 << 21) +#define AT91_IRQ_TOVF (1 << 22) +#define AT91_IRQ_TSTP (1 << 23) +#define AT91_IRQ_CERR (1 << 24) +#define AT91_IRQ_SERR (1 << 25) +#define AT91_IRQ_AERR (1 << 26) +#define AT91_IRQ_FERR (1 << 27) +#define AT91_IRQ_BERR (1 << 28) + +#define AT91_IRQ_ERR_ALL (0x1fff0000) +#define AT91_IRQ_ERR_FRAME (AT91_IRQ_CERR | AT91_IRQ_SERR | \ + AT91_IRQ_AERR | AT91_IRQ_FERR | AT91_IRQ_BERR) +#define AT91_IRQ_ERR_LINE (AT91_IRQ_ERRA | AT91_IRQ_WARN | \ + AT91_IRQ_ERRP | AT91_IRQ_BOFF) + +#define AT91_IRQ_ALL (0x1fffffff) + +struct at91_priv { + struct can_priv can; /* must be the first member! */ + struct net_device *dev; + struct napi_struct napi; + + void __iomem *reg_base; + + u32 reg_sr; + unsigned int tx_next; + unsigned int tx_echo; + unsigned int rx_next; + + struct clk *clk; + struct at91_can_data *pdata; +}; + +static struct can_bittiming_const at91_bittiming_const = { + .tseg1_min = 4, + .tseg1_max = 16, + .tseg2_min = 2, + .tseg2_max = 8, + .sjw_max = 4, + .brp_min = 2, + .brp_max = 128, + .brp_inc = 1, +}; + +static inline int get_tx_next_mb(const struct at91_priv *priv) +{ + return (priv->tx_next & AT91_NEXT_MB_MASK) + AT91_MB_TX_FIRST; +} + +static inline int get_tx_next_prio(const struct at91_priv *priv) +{ + return (priv->tx_next >> AT91_NEXT_PRIO_SHIFT) & 0xf; +} + +static inline int get_tx_echo_mb(const struct at91_priv *priv) +{ + return (priv->tx_echo & AT91_NEXT_MB_MASK) + AT91_MB_TX_FIRST; +} + +static inline u32 at91_read(const struct at91_priv *priv, enum at91_reg reg) +{ + return readl(priv->reg_base + reg); +} + +static inline void at91_write(const struct at91_priv *priv, enum at91_reg reg, + u32 value) +{ + writel(value, priv->reg_base + reg); +} + +static inline void set_mb_mode_prio(const struct at91_priv *priv, + unsigned int mb, enum at91_mb_mode mode, int prio) +{ + at91_write(priv, AT91_MMR(mb), (mode << 24) | (prio << 16)); +} + +static inline void set_mb_mode(const struct at91_priv *priv, unsigned int mb, + enum at91_mb_mode mode) +{ + set_mb_mode_prio(priv, mb, mode, 0); +} + +static struct sk_buff *alloc_can_skb(struct net_device *dev, + struct can_frame **cf) +{ + struct sk_buff *skb; + + skb = netdev_alloc_skb(dev, sizeof(struct can_frame)); + if (unlikely(!skb)) + return NULL; + + skb->protocol = htons(ETH_P_CAN); + skb->ip_summed = CHECKSUM_UNNECESSARY; + *cf = (struct can_frame *)skb_put(skb, sizeof(struct can_frame)); + + return skb; +} + +static struct sk_buff *alloc_can_err_skb(struct net_device *dev, + struct can_frame **cf) +{ + struct sk_buff *skb; + + skb = alloc_can_skb(dev, cf); + if (unlikely(!skb)) + return NULL; + + memset(*cf, 0, sizeof(struct can_frame)); + (*cf)->can_id = CAN_ERR_FLAG; + (*cf)->can_dlc = CAN_ERR_DLC; + + return skb; +} + +/* + * Swtich transceiver on or off + */ +static void at91_transceiver_switch(const struct at91_priv *priv, int on) +{ + if (priv->pdata && priv->pdata->transceiver_switch) + priv->pdata->transceiver_switch(on); +} + +static void at91_setup_mailboxes(struct net_device *dev) +{ + struct at91_priv *priv = netdev_priv(dev); + unsigned int i; + + /* + * The first 12 mailboxes are used as a reception FIFO. The + * last mailbox is configured with overwrite option. The + * overwrite flag indicates a FIFO overflow. + */ + for (i = AT91_MB_RX_FIRST; i < AT91_MB_RX_LAST; i++) + set_mb_mode(priv, i, AT91_MB_MODE_RX); + set_mb_mode(priv, AT91_MB_RX_LAST, AT91_MB_MODE_RX_OVRWR); + + /* The last 4 mailboxes are used for transmitting. */ + for (i = AT91_MB_TX_FIRST; i <= AT91_MB_TX_LAST; i++) + set_mb_mode_prio(priv, i, AT91_MB_MODE_TX, 0); + + /* Reset tx and rx helper pointers */ + priv->tx_next = priv->tx_echo = priv->rx_next = 0; +} + +static int at91_set_bittiming(struct net_device *dev) +{ + const struct at91_priv *priv = netdev_priv(dev); + const struct can_bittiming *bt = &priv->can.bittiming; + u32 reg_br; + + reg_br = ((priv->can.ctrlmode & CAN_CTRLMODE_3_SAMPLES) << 24) | + ((bt->brp - 1) << 16) | ((bt->sjw - 1) << 12) | + ((bt->prop_seg - 1) << 8) | ((bt->phase_seg1 - 1) << 4) | + ((bt->phase_seg2 - 1) << 0); + + dev_info(dev->dev.parent, "writing AT91_BR: 0x%08x\n", reg_br); + + at91_write(priv, AT91_BR, reg_br); + + return 0; +} + +static void at91_chip_start(struct net_device *dev) +{ + struct at91_priv *priv = netdev_priv(dev); + u32 reg_mr, reg_ier; + + /* disable interrupts */ + at91_write(priv, AT91_IDR, AT91_IRQ_ALL); + + /* disable chip */ + reg_mr = at91_read(priv, AT91_MR); + at91_write(priv, AT91_MR, reg_mr & ~AT91_MR_CANEN); + + at91_setup_mailboxes(dev); + at91_transceiver_switch(priv, 1); + + /* enable chip */ + at91_write(priv, AT91_MR, AT91_MR_CANEN); + + priv->can.state = CAN_STATE_ERROR_ACTIVE; + + /* Enable interrupts */ + reg_ier = AT91_IRQ_MB_RX | AT91_IRQ_ERRP | AT91_IRQ_ERR_FRAME; + at91_write(priv, AT91_IDR, AT91_IRQ_ALL); + at91_write(priv, AT91_IER, reg_ier); +} + +static void at91_chip_stop(struct net_device *dev, enum can_state state) +{ + struct at91_priv *priv = netdev_priv(dev); + u32 reg_mr; + + /* disable interrupts */ + at91_write(priv, AT91_IDR, AT91_IRQ_ALL); + + reg_mr = at91_read(priv, AT91_MR); + at91_write(priv, AT91_MR, reg_mr & ~AT91_MR_CANEN); + + at91_transceiver_switch(priv, 0); + priv->can.state = state; +} + +/* + * theory of operation: + * + * According to the datasheet priority 0 is the highest priority, 15 + * is the lowest. If two mailboxes have the same priority level the + * message of the mailbox with the lowest number is sent first. + * + * We use the first TX mailbox (AT91_MB_TX_FIRST) with prio 0, then + * the next mailbox with prio 0, and so on, until all mailboxes are + * used. Then we start from the beginning with mailbox + * AT91_MB_TX_FIRST, but with prio 1, mailbox AT91_MB_TX_FIRST + 1 + * prio 1. When we reach the last mailbox with prio 15, we have to + * stop sending, waiting for all messages to be delivered, then start + * again with mailbox AT91_MB_TX_FIRST prio 0. + * + * We use the priv->tx_next as counter for the next transmission + * mailbox, but without the offset AT91_MB_TX_FIRST. The lower bits + * encode the mailbox number, the upper 4 bits the mailbox priority: + * + * priv->tx_next = (prio << AT91_NEXT_PRIO_SHIFT) || + * (mb - AT91_MB_TX_FIRST); + * + */ +static netdev_tx_t at91_start_xmit(struct sk_buff *skb, struct net_device *dev) +{ + struct at91_priv *priv = netdev_priv(dev); + struct net_device_stats *stats = &dev->stats; + struct can_frame *cf = (struct can_frame *)skb->data; + unsigned int mb, prio; + u32 reg_mid, reg_mcr; + + mb = get_tx_next_mb(priv); + prio = get_tx_next_prio(priv); + + if (unlikely(!(at91_read(priv, AT91_MSR(mb)) & AT91_MSR_MRDY))) { + netif_stop_queue(dev); + + dev_err(dev->dev.parent, + "BUG! TX buffer full when queue awake!\n"); + return NETDEV_TX_BUSY; + } + + if (cf->can_id & CAN_EFF_FLAG) + reg_mid = (cf->can_id & CAN_EFF_MASK) | AT91_MID_MIDE; + else + reg_mid = (cf->can_id & CAN_SFF_MASK) << 18; + + reg_mcr = ((cf->can_id & CAN_RTR_FLAG) ? AT91_MCR_MRTR : 0) | + (cf->can_dlc << 16) | AT91_MCR_MTCR; + + /* disable MB while writing ID (see datasheet) */ + set_mb_mode(priv, mb, AT91_MB_MODE_DISABLED); + at91_write(priv, AT91_MID(mb), reg_mid); + set_mb_mode_prio(priv, mb, AT91_MB_MODE_TX, prio); + + at91_write(priv, AT91_MDL(mb), *(u32 *)(cf->data + 0)); + at91_write(priv, AT91_MDH(mb), *(u32 *)(cf->data + 4)); + + /* This triggers transmission */ + at91_write(priv, AT91_MCR(mb), reg_mcr); + + stats->tx_bytes += cf->can_dlc; + dev->trans_start = jiffies; + + /* _NOTE_: substract AT91_MB_TX_FIRST offset from mb! */ + can_put_echo_skb(skb, dev, mb - AT91_MB_TX_FIRST); + + /* + * we have to stop the queue and deliver all messages in case + * of a prio+mb counter wrap around. This is the case if + * tx_next buffer prio and mailbox equals 0. + * + * also stop the queue if next buffer is still in use + * (== not ready) + */ + priv->tx_next++; + if (!(at91_read(priv, AT91_MSR(get_tx_next_mb(priv))) & + AT91_MSR_MRDY) || + (priv->tx_next & AT91_NEXT_MASK) == 0) + netif_stop_queue(dev); + + /* Enable interrupt for this mailbox */ + at91_write(priv, AT91_IER, 1 << mb); + + return NETDEV_TX_OK; +} + +/** + * at91_activate_rx_low - activate lower rx mailboxes + * @priv: a91 context + * + * Reenables the lower mailboxes for reception of new CAN messages + */ +static inline void at91_activate_rx_low(const struct at91_priv *priv) +{ + u32 mask = AT91_MB_RX_LOW_MASK; + at91_write(priv, AT91_TCR, mask); +} + +/** + * at91_activate_rx_mb - reactive single rx mailbox + * @priv: a91 context + * @mb: mailbox to reactivate + * + * Reenables given mailbox for reception of new CAN messages + */ +static inline void at91_activate_rx_mb(const struct at91_priv *priv, + unsigned int mb) +{ + u32 mask = 1 << mb; + at91_write(priv, AT91_TCR, mask); +} + +/** + * at91_rx_overflow_err - send error frame due to rx overflow + * @dev: net device + */ +static void at91_rx_overflow_err(struct net_device *dev) +{ + struct net_device_stats *stats = &dev->stats; + struct sk_buff *skb; + struct can_frame *cf; + + dev_dbg(dev->dev.parent, "RX buffer overflow\n"); + stats->rx_over_errors++; + stats->rx_errors++; + + skb = alloc_can_err_skb(dev, &cf); + if (unlikely(!skb)) + return; + + cf->can_id |= CAN_ERR_CRTL; + cf->data[1] = CAN_ERR_CRTL_RX_OVERFLOW; + netif_receive_skb(skb); + + stats->rx_packets++; + stats->rx_bytes += cf->can_dlc; +} + +/** + * at91_read_mb - read CAN msg from mailbox (lowlevel impl) + * @dev: net device + * @mb: mailbox number to read from + * @cf: can frame where to store message + * + * Reads a CAN message from the given mailbox and stores data into + * given can frame. "mb" and "cf" must be valid. + */ +static void at91_read_mb(struct net_device *dev, unsigned int mb, + struct can_frame *cf) +{ + const struct at91_priv *priv = netdev_priv(dev); + u32 reg_msr, reg_mid; + + reg_mid = at91_read(priv, AT91_MID(mb)); + if (reg_mid & AT91_MID_MIDE) + cf->can_id = ((reg_mid >> 0) & CAN_EFF_MASK) | CAN_EFF_FLAG; + else + cf->can_id = (reg_mid >> 18) & CAN_SFF_MASK; + + reg_msr = at91_read(priv, AT91_MSR(mb)); + if (reg_msr & AT91_MSR_MRTR) + cf->can_id |= CAN_RTR_FLAG; + cf->can_dlc = min_t(__u8, (reg_msr >> 16) & 0xf, 8); + + *(u32 *)(cf->data + 0) = at91_read(priv, AT91_MDL(mb)); + *(u32 *)(cf->data + 4) = at91_read(priv, AT91_MDH(mb)); + + if (unlikely(mb == AT91_MB_RX_LAST && reg_msr & AT91_MSR_MMI)) + at91_rx_overflow_err(dev); +} + +/** + * at91_read_msg - read CAN message from mailbox + * @dev: net device + * @mb: mail box to read from + * + * Reads a CAN message from given mailbox, and put into linux network + * RX queue, does all housekeeping chores (stats, ...) + */ +static void at91_read_msg(struct net_device *dev, unsigned int mb) +{ + struct net_device_stats *stats = &dev->stats; + struct can_frame *cf; + struct sk_buff *skb; + + skb = alloc_can_skb(dev, &cf); + if (unlikely(!skb)) { + stats->rx_dropped++; + return; + } + + at91_read_mb(dev, mb, cf); + netif_receive_skb(skb); + + stats->rx_packets++; + stats->rx_bytes += cf->can_dlc; +} + +/** + * at91_poll_rx - read multiple CAN messages from mailboxes + * @dev: net device + * @quota: max number of pkgs we're allowed to receive + * + * Theory of Operation: + * + * 12 of the 16 mailboxes on the chip are reserved for RX. we split + * them into 2 groups. The lower group holds 8 and upper 4 mailboxes. + * + * Like it or not, but the chip always saves a received CAN message + * into the first free mailbox it finds (starting with the + * lowest). This makes it very difficult to read the messages in the + * right order from the chip. This is how we work around that problem: + * + * The first message goes into mb nr. 0 and issues an interrupt. All + * rx ints are disabled in the interrupt handler and a napi poll is + * scheduled. We read the mailbox, but do _not_ reenable the mb (to + * receive another message). + * + * lower mbxs upper + * ______^______ __^__ + * / \ / \ + * +-+-+-+-+-+-+-+-++-+-+-+-+ + * |x|x|x|x|x|x|x|x|| | | | | + * +-+-+-+-+-+-+-+-++-+-+-+-+ + * 0 0 0 0 0 0 0 0 0 0 1 1 \ mail + * 0 1 2 3 4 5 6 7 8 9 0 1 / box + * + * The variable priv->rx_next points to the next mailbox to read a + * message from. As long we're in the lower mailboxes we just read the + * mailbox but not reenable it. + * + * With completion of the last of the lower mailboxes, we reenable the + * whole first group, but continue to look for filled mailboxes in the + * upper mailboxes. Imagine the second group like overflow mailboxes, + * which takes CAN messages if the lower goup is full. While in the + * upper group we reenable the mailbox right after reading it. Giving + * the chip more room to store messages. + * + * After finishing we look again in the lower group if we've still + * quota. + * + */ +static int at91_poll_rx(struct net_device *dev, int quota) +{ + struct at91_priv *priv = netdev_priv(dev); + u32 reg_sr = at91_read(priv, AT91_SR); + const unsigned long *addr = (unsigned long *)®_sr; + unsigned int mb; + int received = 0; + + if (priv->rx_next > AT91_MB_RX_LOW_LAST && + reg_sr & AT91_MB_RX_LOW_MASK) + dev_info(dev->dev.parent, + "order of incoming frames cannot be guaranteed\n"); + + again: + for (mb = find_next_bit(addr, AT91_MB_RX_NUM, priv->rx_next); + mb < AT91_MB_RX_NUM && quota > 0; + reg_sr = at91_read(priv, AT91_SR), + mb = find_next_bit(addr, AT91_MB_RX_NUM, ++priv->rx_next)) { + at91_read_msg(dev, mb); + + /* reactivate mailboxes */ + if (mb == AT91_MB_RX_LOW_LAST) + /* all lower mailboxed, if just finished it */ + at91_activate_rx_low(priv); + else if (mb > AT91_MB_RX_LOW_LAST) + /* only the mailbox we read */ + at91_activate_rx_mb(priv, mb); + + received++; + quota--; + } + + /* upper group completed, look again in lower */ + if (priv->rx_next > AT91_MB_RX_LOW_LAST && + quota > 0 && mb >= AT91_MB_RX_NUM) { + priv->rx_next = 0; + goto again; + } + + return received; +} + +static void at91_poll_err_frame(struct net_device *dev, + struct can_frame *cf, u32 reg_sr) +{ + struct at91_priv *priv = netdev_priv(dev); + + /* CRC error */ + if (reg_sr & AT91_IRQ_CERR) { + dev_dbg(dev->dev.parent, "CERR irq\n"); + dev->stats.rx_errors++; + priv->can.can_stats.bus_error++; + cf->can_id |= CAN_ERR_PROT | CAN_ERR_BUSERROR; + } + + /* Stuffing Error */ + if (reg_sr & AT91_IRQ_SERR) { + dev_dbg(dev->dev.parent, "SERR irq\n"); + dev->stats.rx_errors++; + priv->can.can_stats.bus_error++; + cf->can_id |= CAN_ERR_PROT | CAN_ERR_BUSERROR; + cf->data[2] |= CAN_ERR_PROT_STUFF; + } + + /* Acknowledgement Error */ + if (reg_sr & AT91_IRQ_AERR) { + dev_dbg(dev->dev.parent, "AERR irq\n"); + dev->stats.tx_errors++; + cf->can_id |= CAN_ERR_ACK; + } + + /* Form error */ + if (reg_sr & AT91_IRQ_FERR) { + dev_dbg(dev->dev.parent, "FERR irq\n"); + dev->stats.rx_errors++; + priv->can.can_stats.bus_error++; + cf->can_id |= CAN_ERR_PROT | CAN_ERR_BUSERROR; + cf->data[2] |= CAN_ERR_PROT_FORM; + } + + /* Bit Error */ + if (reg_sr & AT91_IRQ_BERR) { + dev_dbg(dev->dev.parent, "BERR irq\n"); + dev->stats.tx_errors++; + priv->can.can_stats.bus_error++; + cf->can_id |= CAN_ERR_PROT | CAN_ERR_BUSERROR; + cf->data[2] |= CAN_ERR_PROT_BIT; + } +} + +static int at91_poll_err(struct net_device *dev, int quota, u32 reg_sr) +{ + struct sk_buff *skb; + struct can_frame *cf; + + if (quota == 0) + return 0; + + skb = alloc_can_err_skb(dev, &cf); + if (unlikely(!skb)) + return 0; + + at91_poll_err_frame(dev, cf, reg_sr); + netif_receive_skb(skb); + + dev->last_rx = jiffies; + dev->stats.rx_packets++; + dev->stats.rx_bytes += cf->can_dlc; + + return 1; +} + +static int at91_poll(struct napi_struct *napi, int quota) +{ + struct net_device *dev = napi->dev; + const struct at91_priv *priv = netdev_priv(dev); + u32 reg_sr = at91_read(priv, AT91_SR); + int work_done = 0; + + if (reg_sr & AT91_IRQ_MB_RX) + work_done += at91_poll_rx(dev, quota - work_done); + + /* + * The error bits are clear on read, + * so use saved value from irq handler. + */ + reg_sr |= priv->reg_sr; + if (reg_sr & AT91_IRQ_ERR_FRAME) + work_done += at91_poll_err(dev, quota - work_done, reg_sr); + + if (work_done < quota) { + /* enable IRQs for frame errors and all mailboxes >= rx_next */ + u32 reg_ier = AT91_IRQ_ERR_FRAME; + reg_ier |= AT91_IRQ_MB_RX & ~AT91_MB_RX_MASK(priv->rx_next); + + napi_complete(napi); + at91_write(priv, AT91_IER, reg_ier); + } + + return work_done; +} + +/* + * theory of operation: + * + * priv->tx_echo holds the number of the oldest can_frame put for + * transmission into the hardware, but not yet ACKed by the CAN tx + * complete IRQ. + * + * We iterate from priv->tx_echo to priv->tx_next and check if the + * packet has been transmitted, echo it back to the CAN framework. If + * we discover a not yet transmitted package, stop looking for more. + * + */ +static void at91_irq_tx(struct net_device *dev, u32 reg_sr) +{ + struct at91_priv *priv = netdev_priv(dev); + u32 reg_msr; + unsigned int mb; + + /* masking of reg_sr not needed, already done by at91_irq */ + + for (/* nix */; (priv->tx_next - priv->tx_echo) > 0; priv->tx_echo++) { + mb = get_tx_echo_mb(priv); + + /* no event in mailbox? */ + if (!(reg_sr & (1 << mb))) + break; + + /* Disable irq for this TX mailbox */ + at91_write(priv, AT91_IDR, 1 << mb); + + /* + * only echo if mailbox signals us a transfer + * complete (MSR_MRDY). Otherwise it's a tansfer + * abort. "can_bus_off()" takes care about the skbs + * parked in the echo queue. + */ + reg_msr = at91_read(priv, AT91_MSR(mb)); + if (likely(reg_msr & AT91_MSR_MRDY && + ~reg_msr & AT91_MSR_MABT)) { + /* _NOTE_: substract AT91_MB_TX_FIRST offset from mb! */ + can_get_echo_skb(dev, mb - AT91_MB_TX_FIRST); + dev->stats.tx_packets++; + } + } + + /* + * restart queue if we don't have a wrap around but restart if + * we get a TX int for the last can frame directly before a + * wrap around. + */ + if ((priv->tx_next & AT91_NEXT_MASK) != 0 || + (priv->tx_echo & AT91_NEXT_MASK) == 0) + netif_wake_queue(dev); +} + +static void at91_irq_err_state(struct net_device *dev, + struct can_frame *cf, enum can_state new_state) +{ + struct at91_priv *priv = netdev_priv(dev); + u32 reg_idr, reg_ier, reg_ecr; + u8 tec, rec; + + reg_ecr = at91_read(priv, AT91_ECR); + rec = reg_ecr & 0xff; + tec = reg_ecr >> 16; + + switch (priv->can.state) { + case CAN_STATE_ERROR_ACTIVE: + /* + * from: ERROR_ACTIVE + * to : ERROR_WARNING, ERROR_PASSIVE, BUS_OFF + * => : there was a warning int + */ + if (new_state >= CAN_STATE_ERROR_WARNING && + new_state <= CAN_STATE_BUS_OFF) { + dev_dbg(dev->dev.parent, "Error Warning IRQ\n"); + priv->can.can_stats.error_warning++; + + cf->can_id |= CAN_ERR_CRTL; + cf->data[1] = (tec > rec) ? + CAN_ERR_CRTL_TX_WARNING : + CAN_ERR_CRTL_RX_WARNING; + } + case CAN_STATE_ERROR_WARNING: /* fallthrough */ + /* + * from: ERROR_ACTIVE, ERROR_WARNING + * to : ERROR_PASSIVE, BUS_OFF + * => : error passive int + */ + if (new_state >= CAN_STATE_ERROR_PASSIVE && + new_state <= CAN_STATE_BUS_OFF) { + dev_dbg(dev->dev.parent, "Error Passive IRQ\n"); + priv->can.can_stats.error_passive++; + + cf->can_id |= CAN_ERR_CRTL; + cf->data[1] = (tec > rec) ? + CAN_ERR_CRTL_TX_PASSIVE : + CAN_ERR_CRTL_RX_PASSIVE; + } + break; + case CAN_STATE_BUS_OFF: + /* + * from: BUS_OFF + * to : ERROR_ACTIVE, ERROR_WARNING, ERROR_PASSIVE + */ + if (new_state <= CAN_STATE_ERROR_PASSIVE) { + cf->can_id |= CAN_ERR_RESTARTED; + + dev_dbg(dev->dev.parent, "restarted\n"); + priv->can.can_stats.restarts++; + + netif_carrier_on(dev); + netif_wake_queue(dev); + } + break; + default: + break; + } + + + /* process state changes depending on the new state */ + switch (new_state) { + case CAN_STATE_ERROR_ACTIVE: + /* + * actually we want to enable AT91_IRQ_WARN here, but + * it screws up the system under certain + * circumstances. so just enable AT91_IRQ_ERRP, thus + * the "fallthrough" + */ + dev_dbg(dev->dev.parent, "Error Active\n"); + cf->can_id |= CAN_ERR_PROT; + cf->data[2] = CAN_ERR_PROT_ACTIVE; + case CAN_STATE_ERROR_WARNING: /* fallthrough */ + reg_idr = AT91_IRQ_ERRA | AT91_IRQ_WARN | AT91_IRQ_BOFF; + reg_ier = AT91_IRQ_ERRP; + break; + case CAN_STATE_ERROR_PASSIVE: + reg_idr = AT91_IRQ_ERRA | AT91_IRQ_WARN | AT91_IRQ_ERRP; + reg_ier = AT91_IRQ_BOFF; + break; + case CAN_STATE_BUS_OFF: + reg_idr = AT91_IRQ_ERRA | AT91_IRQ_ERRP | + AT91_IRQ_WARN | AT91_IRQ_BOFF; + reg_ier = 0; + + cf->can_id |= CAN_ERR_BUSOFF; + + dev_dbg(dev->dev.parent, "bus-off\n"); + netif_carrier_off(dev); + priv->can.can_stats.bus_off++; + + /* turn off chip, if restart is disabled */ + if (!priv->can.restart_ms) { + at91_chip_stop(dev, CAN_STATE_BUS_OFF); + return; + } + break; + default: + break; + } + + at91_write(priv, AT91_IDR, reg_idr); + at91_write(priv, AT91_IER, reg_ier); +} + +static void at91_irq_err(struct net_device *dev) +{ + struct at91_priv *priv = netdev_priv(dev); + struct sk_buff *skb; + struct can_frame *cf; + enum can_state new_state; + u32 reg_sr; + + reg_sr = at91_read(priv, AT91_SR); + + /* we need to look at the unmasked reg_sr */ + if (unlikely(reg_sr & AT91_IRQ_BOFF)) + new_state = CAN_STATE_BUS_OFF; + else if (unlikely(reg_sr & AT91_IRQ_ERRP)) + new_state = CAN_STATE_ERROR_PASSIVE; + else if (unlikely(reg_sr & AT91_IRQ_WARN)) + new_state = CAN_STATE_ERROR_WARNING; + else if (likely(reg_sr & AT91_IRQ_ERRA)) + new_state = CAN_STATE_ERROR_ACTIVE; + else { + dev_err(dev->dev.parent, "BUG! hardware in undefined state\n"); + return; + } + + /* state hasn't changed */ + if (likely(new_state == priv->can.state)) + return; + + skb = alloc_can_err_skb(dev, &cf); + if (unlikely(!skb)) + return; + + at91_irq_err_state(dev, cf, new_state); + netif_rx(skb); + + dev->last_rx = jiffies; + dev->stats.rx_packets++; + dev->stats.rx_bytes += cf->can_dlc; + + priv->can.state = new_state; +} + +/* + * interrupt handler + */ +static irqreturn_t at91_irq(int irq, void *dev_id) +{ + struct net_device *dev = dev_id; + struct at91_priv *priv = netdev_priv(dev); + irqreturn_t handled = IRQ_NONE; + u32 reg_sr, reg_imr; + + reg_sr = at91_read(priv, AT91_SR); + reg_imr = at91_read(priv, AT91_IMR); + + /* Ignore masked interrupts */ + reg_sr &= reg_imr; + if (!reg_sr) + goto exit; + + handled = IRQ_HANDLED; + + /* Receive or error interrupt? -> napi */ + if (reg_sr & (AT91_IRQ_MB_RX | AT91_IRQ_ERR_FRAME)) { + /* + * The error bits are clear on read, + * save for later use. + */ + priv->reg_sr = reg_sr; + at91_write(priv, AT91_IDR, + AT91_IRQ_MB_RX | AT91_IRQ_ERR_FRAME); + napi_schedule(&priv->napi); + } + + /* Transmission complete interrupt */ + if (reg_sr & AT91_IRQ_MB_TX) + at91_irq_tx(dev, reg_sr); + + at91_irq_err(dev); + + exit: + return handled; +} + +static int at91_open(struct net_device *dev) +{ + struct at91_priv *priv = netdev_priv(dev); + int err; + + clk_enable(priv->clk); + + /* check or determine and set bittime */ + err = open_candev(dev); + if (err) + goto out; + + /* register interrupt handler */ + if (request_irq(dev->irq, at91_irq, IRQF_SHARED, + dev->name, dev)) { + err = -EAGAIN; + goto out_close; + } + + /* start chip and queuing */ + at91_chip_start(dev); + napi_enable(&priv->napi); + netif_start_queue(dev); + + return 0; + + out_close: + close_candev(dev); + out: + clk_disable(priv->clk); + + return err; +} + +/* + * stop CAN bus activity + */ +static int at91_close(struct net_device *dev) +{ + struct at91_priv *priv = netdev_priv(dev); + + netif_stop_queue(dev); + napi_disable(&priv->napi); + at91_chip_stop(dev, CAN_STATE_STOPPED); + + free_irq(dev->irq, dev); + clk_disable(priv->clk); + + close_candev(dev); + + return 0; +} + +static int at91_set_mode(struct net_device *dev, enum can_mode mode) +{ + switch (mode) { + case CAN_MODE_START: + at91_chip_start(dev); + netif_wake_queue(dev); + break; + + default: + return -EOPNOTSUPP; + } + + return 0; +} + +static const struct net_device_ops at91_netdev_ops = { + .ndo_open = at91_open, + .ndo_stop = at91_close, + .ndo_start_xmit = at91_start_xmit, +}; + +static int __init at91_can_probe(struct platform_device *pdev) +{ + struct net_device *dev; + struct at91_priv *priv; + struct resource *res; + struct clk *clk; + void __iomem *addr; + int err, irq; + + clk = clk_get(&pdev->dev, "can_clk"); + if (IS_ERR(clk)) { + dev_err(&pdev->dev, "no clock defined\n"); + err = -ENODEV; + goto exit; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + irq = platform_get_irq(pdev, 0); + if (!res || !irq) { + err = -ENODEV; + goto exit_put; + } + + if (!request_mem_region(res->start, + resource_size(res), + pdev->name)) { + err = -EBUSY; + goto exit_put; + } + + addr = ioremap_nocache(res->start, resource_size(res)); + if (!addr) { + err = -ENOMEM; + goto exit_release; + } + + dev = alloc_candev(sizeof(struct at91_priv)); + if (!dev) { + err = -ENOMEM; + goto exit_iounmap; + } + + dev->netdev_ops = &at91_netdev_ops; + dev->irq = irq; + dev->flags |= IFF_ECHO; + + priv = netdev_priv(dev); + priv->can.clock.freq = clk_get_rate(clk); + priv->can.bittiming_const = &at91_bittiming_const; + priv->can.do_set_bittiming = at91_set_bittiming; + priv->can.do_set_mode = at91_set_mode; + priv->reg_base = addr; + priv->dev = dev; + priv->clk = clk; + priv->pdata = pdev->dev.platform_data; + + netif_napi_add(dev, &priv->napi, at91_poll, AT91_NAPI_WEIGHT); + + dev_set_drvdata(&pdev->dev, dev); + SET_NETDEV_DEV(dev, &pdev->dev); + + err = register_candev(dev); + if (err) { + dev_err(&pdev->dev, "registering netdev failed\n"); + goto exit_free; + } + + dev_info(&pdev->dev, "device registered (reg_base=%p, irq=%d)\n", + priv->reg_base, dev->irq); + + return 0; + + exit_free: + free_netdev(dev); + exit_iounmap: + iounmap(addr); + exit_release: + release_mem_region(res->start, resource_size(res)); + exit_put: + clk_put(clk); + exit: + return err; +} + +static int __devexit at91_can_remove(struct platform_device *pdev) +{ + struct net_device *dev = platform_get_drvdata(pdev); + struct at91_priv *priv = netdev_priv(dev); + struct resource *res; + + unregister_netdev(dev); + + platform_set_drvdata(pdev, NULL); + + free_netdev(dev); + + iounmap(priv->reg_base); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + release_mem_region(res->start, resource_size(res)); + + clk_put(priv->clk); + + return 0; +} + +static struct platform_driver at91_can_driver = { + .probe = at91_can_probe, + .remove = __devexit_p(at91_can_remove), + .driver = { + .name = DRV_NAME, + .owner = THIS_MODULE, + }, +}; + +static int __init at91_can_module_init(void) +{ + printk(KERN_INFO "%s netdevice driver\n", DRV_NAME); + return platform_driver_register(&at91_can_driver); +} + +static void __exit at91_can_module_exit(void) +{ + platform_driver_unregister(&at91_can_driver); + printk(KERN_INFO "%s: driver removed\n", DRV_NAME); +} + +module_init(at91_can_module_init); +module_exit(at91_can_module_exit); + +MODULE_AUTHOR("Marc Kleine-Budde <mkl@pengutronix.de>"); +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION(DRV_NAME " CAN netdevice driver"); diff --git a/drivers/net/can/dev.c b/drivers/net/can/dev.c index f0b9a1e1db46..2868fe842a41 100644 --- a/drivers/net/can/dev.c +++ b/drivers/net/can/dev.c @@ -589,6 +589,22 @@ static int can_changelink(struct net_device *dev, return 0; } +static size_t can_get_size(const struct net_device *dev) +{ + struct can_priv *priv = netdev_priv(dev); + size_t size; + + size = nla_total_size(sizeof(u32)); /* IFLA_CAN_STATE */ + size += sizeof(struct can_ctrlmode); /* IFLA_CAN_CTRLMODE */ + size += nla_total_size(sizeof(u32)); /* IFLA_CAN_RESTART_MS */ + size += sizeof(struct can_bittiming); /* IFLA_CAN_BITTIMING */ + size += sizeof(struct can_clock); /* IFLA_CAN_CLOCK */ + if (priv->bittiming_const) /* IFLA_CAN_BITTIMING_CONST */ + size += sizeof(struct can_bittiming_const); + + return size; +} + static int can_fill_info(struct sk_buff *skb, const struct net_device *dev) { struct can_priv *priv = netdev_priv(dev); @@ -613,6 +629,11 @@ nla_put_failure: return -EMSGSIZE; } +static size_t can_get_xstats_size(const struct net_device *dev) +{ + return sizeof(struct can_device_stats); +} + static int can_fill_xstats(struct sk_buff *skb, const struct net_device *dev) { struct can_priv *priv = netdev_priv(dev); @@ -639,7 +660,9 @@ static struct rtnl_link_ops can_link_ops __read_mostly = { .setup = can_setup, .newlink = can_newlink, .changelink = can_changelink, + .get_size = can_get_size, .fill_info = can_fill_info, + .get_xstats_size = can_get_xstats_size, .fill_xstats = can_fill_xstats, }; diff --git a/drivers/net/can/sja1000/Kconfig b/drivers/net/can/sja1000/Kconfig new file mode 100644 index 000000000000..4c674927f247 --- /dev/null +++ b/drivers/net/can/sja1000/Kconfig @@ -0,0 +1,47 @@ +menuconfig CAN_SJA1000 + tristate "Philips/NXP SJA1000 devices" + depends on CAN_DEV && HAS_IOMEM + +if CAN_SJA1000 + +config CAN_SJA1000_ISA + tristate "ISA Bus based legacy SJA1000 driver" + depends on ISA + ---help--- + This driver adds legacy support for SJA1000 chips connected to + the ISA bus using I/O port, memory mapped or indirect access. + +config CAN_SJA1000_PLATFORM + tristate "Generic Platform Bus based SJA1000 driver" + ---help--- + This driver adds support for the SJA1000 chips connected to + the "platform bus" (Linux abstraction for directly to the + processor attached devices). Which can be found on various + boards from Phytec (http://www.phytec.de) like the PCM027, + PCM038. + +config CAN_SJA1000_OF_PLATFORM + tristate "Generic OF Platform Bus based SJA1000 driver" + depends on PPC_OF + ---help--- + This driver adds support for the SJA1000 chips connected to + the OpenFirmware "platform bus" found on embedded systems with + OpenFirmware bindings, e.g. if you have a PowerPC based system + you may want to enable this option. + +config CAN_EMS_PCI + tristate "EMS CPC-PCI, CPC-PCIe and CPC-104P Card" + depends on PCI + ---help--- + This driver is for the one, two or four channel CPC-PCI, + CPC-PCIe and CPC-104P cards from EMS Dr. Thomas Wuensche + (http://www.ems-wuensche.de). + +config CAN_KVASER_PCI + tristate "Kvaser PCIcanx and Kvaser PCIcan PCI Cards" + depends on PCI + ---help--- + This driver is for the the PCIcanx and PCIcan cards (1, 2 or + 4 channel) from Kvaser (http://www.kvaser.com). + +endif diff --git a/drivers/net/can/sja1000/ems_pci.c b/drivers/net/can/sja1000/ems_pci.c index 7d84b8ac9c1c..fd04789d3370 100644 --- a/drivers/net/can/sja1000/ems_pci.c +++ b/drivers/net/can/sja1000/ems_pci.c @@ -94,12 +94,14 @@ struct ems_pci_card { #define EMS_PCI_CDR (CDR_CBP | CDR_CLKOUT_MASK) #define EMS_PCI_V1_BASE_BAR 1 -#define EMS_PCI_V1_MEM_SIZE 4096 +#define EMS_PCI_V1_CONF_SIZE 4096 /* size of PITA control area */ #define EMS_PCI_V2_BASE_BAR 2 -#define EMS_PCI_V2_MEM_SIZE 128 +#define EMS_PCI_V2_CONF_SIZE 128 /* size of PLX control area */ #define EMS_PCI_CAN_BASE_OFFSET 0x400 /* offset where the controllers starts */ #define EMS_PCI_CAN_CTRL_SIZE 0x200 /* memory size for each controller */ +#define EMS_PCI_BASE_SIZE 4096 /* size of controller area */ + static struct pci_device_id ems_pci_tbl[] = { /* CPC-PCI v1 */ {PCI_VENDOR_ID_SIEMENS, 0x2104, PCI_ANY_ID, PCI_ANY_ID,}, @@ -224,7 +226,7 @@ static int __devinit ems_pci_add_card(struct pci_dev *pdev, struct sja1000_priv *priv; struct net_device *dev; struct ems_pci_card *card; - int max_chan, mem_size, base_bar; + int max_chan, conf_size, base_bar; int err, i; /* Enabling PCI device */ @@ -251,22 +253,22 @@ static int __devinit ems_pci_add_card(struct pci_dev *pdev, card->version = 2; /* CPC-PCI v2 */ max_chan = EMS_PCI_V2_MAX_CHAN; base_bar = EMS_PCI_V2_BASE_BAR; - mem_size = EMS_PCI_V2_MEM_SIZE; + conf_size = EMS_PCI_V2_CONF_SIZE; } else { card->version = 1; /* CPC-PCI v1 */ max_chan = EMS_PCI_V1_MAX_CHAN; base_bar = EMS_PCI_V1_BASE_BAR; - mem_size = EMS_PCI_V1_MEM_SIZE; + conf_size = EMS_PCI_V1_CONF_SIZE; } /* Remap configuration space and controller memory area */ - card->conf_addr = pci_iomap(pdev, 0, mem_size); + card->conf_addr = pci_iomap(pdev, 0, conf_size); if (card->conf_addr == NULL) { err = -ENOMEM; goto failure_cleanup; } - card->base_addr = pci_iomap(pdev, base_bar, mem_size); + card->base_addr = pci_iomap(pdev, base_bar, EMS_PCI_BASE_SIZE); if (card->base_addr == NULL) { err = -ENOMEM; goto failure_cleanup; diff --git a/drivers/net/can/sja1000/sja1000_of_platform.c b/drivers/net/can/sja1000/sja1000_of_platform.c index 3373560405ba..9dd076a626a5 100644 --- a/drivers/net/can/sja1000/sja1000_of_platform.c +++ b/drivers/net/can/sja1000/sja1000_of_platform.c @@ -213,6 +213,7 @@ static struct of_device_id __devinitdata sja1000_ofp_table[] = { {.compatible = "nxp,sja1000"}, {}, }; +MODULE_DEVICE_TABLE(of, sja1000_ofp_table); static struct of_platform_driver sja1000_ofp_driver = { .owner = THIS_MODULE, diff --git a/drivers/net/can/usb/Kconfig b/drivers/net/can/usb/Kconfig new file mode 100644 index 000000000000..bbc78e0b8a15 --- /dev/null +++ b/drivers/net/can/usb/Kconfig @@ -0,0 +1,10 @@ +menu "CAN USB interfaces" + depends on USB && CAN_DEV + +config CAN_EMS_USB + tristate "EMS CPC-USB/ARM7 CAN/USB interface" + ---help--- + This driver is for the one channel CPC-USB/ARM7 CAN/USB interface + from from EMS Dr. Thomas Wuensche (http://www.ems-wuensche.de). + +endmenu diff --git a/drivers/net/can/usb/Makefile b/drivers/net/can/usb/Makefile new file mode 100644 index 000000000000..0afd51d4c7a5 --- /dev/null +++ b/drivers/net/can/usb/Makefile @@ -0,0 +1,7 @@ +# +# Makefile for the Linux Controller Area Network USB drivers. +# + +obj-$(CONFIG_CAN_EMS_USB) += ems_usb.o + +ccflags-$(CONFIG_CAN_DEBUG_DEVICES) := -DDEBUG diff --git a/drivers/net/can/usb/ems_usb.c b/drivers/net/can/usb/ems_usb.c new file mode 100644 index 000000000000..abdbd9c2b788 --- /dev/null +++ b/drivers/net/can/usb/ems_usb.c @@ -0,0 +1,1158 @@ +/* + * CAN driver for EMS Dr. Thomas Wuensche CPC-USB/ARM7 + * + * Copyright (C) 2004-2009 EMS Dr. Thomas Wuensche + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published + * by the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +#include <linux/init.h> +#include <linux/signal.h> +#include <linux/slab.h> +#include <linux/module.h> +#include <linux/netdevice.h> +#include <linux/usb.h> + +#include <linux/can.h> +#include <linux/can/dev.h> +#include <linux/can/error.h> + +MODULE_AUTHOR("Sebastian Haas <haas@ems-wuensche.com>"); +MODULE_DESCRIPTION("CAN driver for EMS Dr. Thomas Wuensche CAN/USB interfaces"); +MODULE_LICENSE("GPL v2"); + +/* Control-Values for CPC_Control() Command Subject Selection */ +#define CONTR_CAN_MESSAGE 0x04 +#define CONTR_CAN_STATE 0x0C +#define CONTR_BUS_ERROR 0x1C + +/* Control Command Actions */ +#define CONTR_CONT_OFF 0 +#define CONTR_CONT_ON 1 +#define CONTR_ONCE 2 + +/* Messages from CPC to PC */ +#define CPC_MSG_TYPE_CAN_FRAME 1 /* CAN data frame */ +#define CPC_MSG_TYPE_RTR_FRAME 8 /* CAN remote frame */ +#define CPC_MSG_TYPE_CAN_PARAMS 12 /* Actual CAN parameters */ +#define CPC_MSG_TYPE_CAN_STATE 14 /* CAN state message */ +#define CPC_MSG_TYPE_EXT_CAN_FRAME 16 /* Extended CAN data frame */ +#define CPC_MSG_TYPE_EXT_RTR_FRAME 17 /* Extended remote frame */ +#define CPC_MSG_TYPE_CONTROL 19 /* change interface behavior */ +#define CPC_MSG_TYPE_CONFIRM 20 /* command processed confirmation */ +#define CPC_MSG_TYPE_OVERRUN 21 /* overrun events */ +#define CPC_MSG_TYPE_CAN_FRAME_ERROR 23 /* detected bus errors */ +#define CPC_MSG_TYPE_ERR_COUNTER 25 /* RX/TX error counter */ + +/* Messages from the PC to the CPC interface */ +#define CPC_CMD_TYPE_CAN_FRAME 1 /* CAN data frame */ +#define CPC_CMD_TYPE_CONTROL 3 /* control of interface behavior */ +#define CPC_CMD_TYPE_CAN_PARAMS 6 /* set CAN parameters */ +#define CPC_CMD_TYPE_RTR_FRAME 13 /* CAN remote frame */ +#define CPC_CMD_TYPE_CAN_STATE 14 /* CAN state message */ +#define CPC_CMD_TYPE_EXT_CAN_FRAME 15 /* Extended CAN data frame */ +#define CPC_CMD_TYPE_EXT_RTR_FRAME 16 /* Extended CAN remote frame */ +#define CPC_CMD_TYPE_CAN_EXIT 200 /* exit the CAN */ + +#define CPC_CMD_TYPE_INQ_ERR_COUNTER 25 /* request the CAN error counters */ +#define CPC_CMD_TYPE_CLEAR_MSG_QUEUE 8 /* clear CPC_MSG queue */ +#define CPC_CMD_TYPE_CLEAR_CMD_QUEUE 28 /* clear CPC_CMD queue */ + +#define CPC_CC_TYPE_SJA1000 2 /* Philips basic CAN controller */ + +#define CPC_CAN_ECODE_ERRFRAME 0x01 /* Ecode type */ + +/* Overrun types */ +#define CPC_OVR_EVENT_CAN 0x01 +#define CPC_OVR_EVENT_CANSTATE 0x02 +#define CPC_OVR_EVENT_BUSERROR 0x04 + +/* + * If the CAN controller lost a message we indicate it with the highest bit + * set in the count field. + */ +#define CPC_OVR_HW 0x80 + +/* Size of the "struct ems_cpc_msg" without the union */ +#define CPC_MSG_HEADER_LEN 11 +#define CPC_CAN_MSG_MIN_SIZE 5 + +/* Define these values to match your devices */ +#define USB_CPCUSB_VENDOR_ID 0x12D6 + +#define USB_CPCUSB_ARM7_PRODUCT_ID 0x0444 + +/* Mode register NXP LPC2119/SJA1000 CAN Controller */ +#define SJA1000_MOD_NORMAL 0x00 +#define SJA1000_MOD_RM 0x01 + +/* ECC register NXP LPC2119/SJA1000 CAN Controller */ +#define SJA1000_ECC_SEG 0x1F +#define SJA1000_ECC_DIR 0x20 +#define SJA1000_ECC_ERR 0x06 +#define SJA1000_ECC_BIT 0x00 +#define SJA1000_ECC_FORM 0x40 +#define SJA1000_ECC_STUFF 0x80 +#define SJA1000_ECC_MASK 0xc0 + +/* Status register content */ +#define SJA1000_SR_BS 0x80 +#define SJA1000_SR_ES 0x40 + +#define SJA1000_DEFAULT_OUTPUT_CONTROL 0xDA + +/* + * The device actually uses a 16MHz clock to generate the CAN clock + * but it expects SJA1000 bit settings based on 8MHz (is internally + * converted). + */ +#define EMS_USB_ARM7_CLOCK 8000000 + +/* + * CAN-Message representation in a CPC_MSG. Message object type is + * CPC_MSG_TYPE_CAN_FRAME or CPC_MSG_TYPE_RTR_FRAME or + * CPC_MSG_TYPE_EXT_CAN_FRAME or CPC_MSG_TYPE_EXT_RTR_FRAME. + */ +struct cpc_can_msg { + u32 id; + u8 length; + u8 msg[8]; +}; + +/* Representation of the CAN parameters for the SJA1000 controller */ +struct cpc_sja1000_params { + u8 mode; + u8 acc_code0; + u8 acc_code1; + u8 acc_code2; + u8 acc_code3; + u8 acc_mask0; + u8 acc_mask1; + u8 acc_mask2; + u8 acc_mask3; + u8 btr0; + u8 btr1; + u8 outp_contr; +}; + +/* CAN params message representation */ +struct cpc_can_params { + u8 cc_type; + + /* Will support M16C CAN controller in the future */ + union { + struct cpc_sja1000_params sja1000; + } cc_params; +}; + +/* Structure for confirmed message handling */ +struct cpc_confirm { + u8 error; /* error code */ +}; + +/* Structure for overrun conditions */ +struct cpc_overrun { + u8 event; + u8 count; +}; + +/* SJA1000 CAN errors (compatible to NXP LPC2119) */ +struct cpc_sja1000_can_error { + u8 ecc; + u8 rxerr; + u8 txerr; +}; + +/* structure for CAN error conditions */ +struct cpc_can_error { + u8 ecode; + + struct { + u8 cc_type; + + /* Other controllers may also provide error code capture regs */ + union { + struct cpc_sja1000_can_error sja1000; + } regs; + } cc; +}; + +/* + * Structure containing RX/TX error counter. This structure is used to request + * the values of the CAN controllers TX and RX error counter. + */ +struct cpc_can_err_counter { + u8 rx; + u8 tx; +}; + +/* Main message type used between library and application */ +struct __attribute__ ((packed)) ems_cpc_msg { + u8 type; /* type of message */ + u8 length; /* length of data within union 'msg' */ + u8 msgid; /* confirmation handle */ + u32 ts_sec; /* timestamp in seconds */ + u32 ts_nsec; /* timestamp in nano seconds */ + + union { + u8 generic[64]; + struct cpc_can_msg can_msg; + struct cpc_can_params can_params; + struct cpc_confirm confirmation; + struct cpc_overrun overrun; + struct cpc_can_error error; + struct cpc_can_err_counter err_counter; + u8 can_state; + } msg; +}; + +/* + * Table of devices that work with this driver + * NOTE: This driver supports only CPC-USB/ARM7 (LPC2119) yet. + */ +static struct usb_device_id ems_usb_table[] = { + {USB_DEVICE(USB_CPCUSB_VENDOR_ID, USB_CPCUSB_ARM7_PRODUCT_ID)}, + {} /* Terminating entry */ +}; + +MODULE_DEVICE_TABLE(usb, ems_usb_table); + +#define RX_BUFFER_SIZE 64 +#define CPC_HEADER_SIZE 4 +#define INTR_IN_BUFFER_SIZE 4 + +#define MAX_RX_URBS 10 +#define MAX_TX_URBS CAN_ECHO_SKB_MAX + +struct ems_usb; + +struct ems_tx_urb_context { + struct ems_usb *dev; + + u32 echo_index; + u8 dlc; +}; + +struct ems_usb { + struct can_priv can; /* must be the first member */ + int open_time; + + struct sk_buff *echo_skb[MAX_TX_URBS]; + + struct usb_device *udev; + struct net_device *netdev; + + atomic_t active_tx_urbs; + struct usb_anchor tx_submitted; + struct ems_tx_urb_context tx_contexts[MAX_TX_URBS]; + + struct usb_anchor rx_submitted; + + struct urb *intr_urb; + + u8 *tx_msg_buffer; + + u8 *intr_in_buffer; + unsigned int free_slots; /* remember number of available slots */ + + struct ems_cpc_msg active_params; /* active controller parameters */ +}; + +static void ems_usb_read_interrupt_callback(struct urb *urb) +{ + struct ems_usb *dev = urb->context; + struct net_device *netdev = dev->netdev; + int err; + + if (!netif_device_present(netdev)) + return; + + switch (urb->status) { + case 0: + dev->free_slots = dev->intr_in_buffer[1]; + break; + + case -ECONNRESET: /* unlink */ + case -ENOENT: + case -ESHUTDOWN: + return; + + default: + dev_info(netdev->dev.parent, "Rx interrupt aborted %d\n", + urb->status); + break; + } + + err = usb_submit_urb(urb, GFP_ATOMIC); + + if (err == -ENODEV) + netif_device_detach(netdev); + else if (err) + dev_err(netdev->dev.parent, + "failed resubmitting intr urb: %d\n", err); + + return; +} + +static void ems_usb_rx_can_msg(struct ems_usb *dev, struct ems_cpc_msg *msg) +{ + struct can_frame *cf; + struct sk_buff *skb; + int i; + struct net_device_stats *stats = &dev->netdev->stats; + + skb = netdev_alloc_skb(dev->netdev, sizeof(struct can_frame)); + if (skb == NULL) + return; + + skb->protocol = htons(ETH_P_CAN); + + cf = (struct can_frame *)skb_put(skb, sizeof(struct can_frame)); + + cf->can_id = le32_to_cpu(msg->msg.can_msg.id); + cf->can_dlc = min_t(u8, msg->msg.can_msg.length, 8); + + if (msg->type == CPC_MSG_TYPE_EXT_CAN_FRAME + || msg->type == CPC_MSG_TYPE_EXT_RTR_FRAME) + cf->can_id |= CAN_EFF_FLAG; + + if (msg->type == CPC_MSG_TYPE_RTR_FRAME + || msg->type == CPC_MSG_TYPE_EXT_RTR_FRAME) { + cf->can_id |= CAN_RTR_FLAG; + } else { + for (i = 0; i < cf->can_dlc; i++) + cf->data[i] = msg->msg.can_msg.msg[i]; + } + + netif_rx(skb); + + stats->rx_packets++; + stats->rx_bytes += cf->can_dlc; +} + +static void ems_usb_rx_err(struct ems_usb *dev, struct ems_cpc_msg *msg) +{ + struct can_frame *cf; + struct sk_buff *skb; + struct net_device_stats *stats = &dev->netdev->stats; + + skb = netdev_alloc_skb(dev->netdev, sizeof(struct can_frame)); + if (skb == NULL) + return; + + skb->protocol = htons(ETH_P_CAN); + + cf = (struct can_frame *)skb_put(skb, sizeof(struct can_frame)); + memset(cf, 0, sizeof(struct can_frame)); + + cf->can_id = CAN_ERR_FLAG; + cf->can_dlc = CAN_ERR_DLC; + + if (msg->type == CPC_MSG_TYPE_CAN_STATE) { + u8 state = msg->msg.can_state; + + if (state & SJA1000_SR_BS) { + dev->can.state = CAN_STATE_BUS_OFF; + cf->can_id |= CAN_ERR_BUSOFF; + + can_bus_off(dev->netdev); + } else if (state & SJA1000_SR_ES) { + dev->can.state = CAN_STATE_ERROR_WARNING; + dev->can.can_stats.error_warning++; + } else { + dev->can.state = CAN_STATE_ERROR_ACTIVE; + dev->can.can_stats.error_passive++; + } + } else if (msg->type == CPC_MSG_TYPE_CAN_FRAME_ERROR) { + u8 ecc = msg->msg.error.cc.regs.sja1000.ecc; + u8 txerr = msg->msg.error.cc.regs.sja1000.txerr; + u8 rxerr = msg->msg.error.cc.regs.sja1000.rxerr; + + /* bus error interrupt */ + dev->can.can_stats.bus_error++; + stats->rx_errors++; + + cf->can_id |= CAN_ERR_PROT | CAN_ERR_BUSERROR; + + switch (ecc & SJA1000_ECC_MASK) { + case SJA1000_ECC_BIT: + cf->data[2] |= CAN_ERR_PROT_BIT; + break; + case SJA1000_ECC_FORM: + cf->data[2] |= CAN_ERR_PROT_FORM; + break; + case SJA1000_ECC_STUFF: + cf->data[2] |= CAN_ERR_PROT_STUFF; + break; + default: + cf->data[2] |= CAN_ERR_PROT_UNSPEC; + cf->data[3] = ecc & SJA1000_ECC_SEG; + break; + } + + /* Error occured during transmission? */ + if ((ecc & SJA1000_ECC_DIR) == 0) + cf->data[2] |= CAN_ERR_PROT_TX; + + if (dev->can.state == CAN_STATE_ERROR_WARNING || + dev->can.state == CAN_STATE_ERROR_PASSIVE) { + cf->data[1] = (txerr > rxerr) ? + CAN_ERR_CRTL_TX_PASSIVE : CAN_ERR_CRTL_RX_PASSIVE; + } + } else if (msg->type == CPC_MSG_TYPE_OVERRUN) { + cf->can_id |= CAN_ERR_CRTL; + cf->data[1] = CAN_ERR_CRTL_RX_OVERFLOW; + + stats->rx_over_errors++; + stats->rx_errors++; + } + + netif_rx(skb); + + stats->rx_packets++; + stats->rx_bytes += cf->can_dlc; +} + +/* + * callback for bulk IN urb + */ +static void ems_usb_read_bulk_callback(struct urb *urb) +{ + struct ems_usb *dev = urb->context; + struct net_device *netdev; + int retval; + + netdev = dev->netdev; + + if (!netif_device_present(netdev)) + return; + + switch (urb->status) { + case 0: /* success */ + break; + + case -ENOENT: + return; + + default: + dev_info(netdev->dev.parent, "Rx URB aborted (%d)\n", + urb->status); + goto resubmit_urb; + } + + if (urb->actual_length > CPC_HEADER_SIZE) { + struct ems_cpc_msg *msg; + u8 *ibuf = urb->transfer_buffer; + u8 msg_count, again, start; + + msg_count = ibuf[0] & ~0x80; + again = ibuf[0] & 0x80; + + start = CPC_HEADER_SIZE; + + while (msg_count) { + msg = (struct ems_cpc_msg *)&ibuf[start]; + + switch (msg->type) { + case CPC_MSG_TYPE_CAN_STATE: + /* Process CAN state changes */ + ems_usb_rx_err(dev, msg); + break; + + case CPC_MSG_TYPE_CAN_FRAME: + case CPC_MSG_TYPE_EXT_CAN_FRAME: + case CPC_MSG_TYPE_RTR_FRAME: + case CPC_MSG_TYPE_EXT_RTR_FRAME: + ems_usb_rx_can_msg(dev, msg); + break; + + case CPC_MSG_TYPE_CAN_FRAME_ERROR: + /* Process errorframe */ + ems_usb_rx_err(dev, msg); + break; + + case CPC_MSG_TYPE_OVERRUN: + /* Message lost while receiving */ + ems_usb_rx_err(dev, msg); + break; + } + + start += CPC_MSG_HEADER_LEN + msg->length; + msg_count--; + + if (start > urb->transfer_buffer_length) { + dev_err(netdev->dev.parent, "format error\n"); + break; + } + } + } + +resubmit_urb: + usb_fill_bulk_urb(urb, dev->udev, usb_rcvbulkpipe(dev->udev, 2), + urb->transfer_buffer, RX_BUFFER_SIZE, + ems_usb_read_bulk_callback, dev); + + retval = usb_submit_urb(urb, GFP_ATOMIC); + + if (retval == -ENODEV) + netif_device_detach(netdev); + else if (retval) + dev_err(netdev->dev.parent, + "failed resubmitting read bulk urb: %d\n", retval); + + return; +} + +/* + * callback for bulk IN urb + */ +static void ems_usb_write_bulk_callback(struct urb *urb) +{ + struct ems_tx_urb_context *context = urb->context; + struct ems_usb *dev; + struct net_device *netdev; + + BUG_ON(!context); + + dev = context->dev; + netdev = dev->netdev; + + /* free up our allocated buffer */ + usb_buffer_free(urb->dev, urb->transfer_buffer_length, + urb->transfer_buffer, urb->transfer_dma); + + atomic_dec(&dev->active_tx_urbs); + + if (!netif_device_present(netdev)) + return; + + if (urb->status) + dev_info(netdev->dev.parent, "Tx URB aborted (%d)\n", + urb->status); + + netdev->trans_start = jiffies; + + /* transmission complete interrupt */ + netdev->stats.tx_packets++; + netdev->stats.tx_bytes += context->dlc; + + can_get_echo_skb(netdev, context->echo_index); + + /* Release context */ + context->echo_index = MAX_TX_URBS; + + if (netif_queue_stopped(netdev)) + netif_wake_queue(netdev); +} + +/* + * Send the given CPC command synchronously + */ +static int ems_usb_command_msg(struct ems_usb *dev, struct ems_cpc_msg *msg) +{ + int actual_length; + + /* Copy payload */ + memcpy(&dev->tx_msg_buffer[CPC_HEADER_SIZE], msg, + msg->length + CPC_MSG_HEADER_LEN); + + /* Clear header */ + memset(&dev->tx_msg_buffer[0], 0, CPC_HEADER_SIZE); + + return usb_bulk_msg(dev->udev, usb_sndbulkpipe(dev->udev, 2), + &dev->tx_msg_buffer[0], + msg->length + CPC_MSG_HEADER_LEN + CPC_HEADER_SIZE, + &actual_length, 1000); +} + +/* + * Change CAN controllers' mode register + */ +static int ems_usb_write_mode(struct ems_usb *dev, u8 mode) +{ + dev->active_params.msg.can_params.cc_params.sja1000.mode = mode; + + return ems_usb_command_msg(dev, &dev->active_params); +} + +/* + * Send a CPC_Control command to change behaviour when interface receives a CAN + * message, bus error or CAN state changed notifications. + */ +static int ems_usb_control_cmd(struct ems_usb *dev, u8 val) +{ + struct ems_cpc_msg cmd; + + cmd.type = CPC_CMD_TYPE_CONTROL; + cmd.length = CPC_MSG_HEADER_LEN + 1; + + cmd.msgid = 0; + + cmd.msg.generic[0] = val; + + return ems_usb_command_msg(dev, &cmd); +} + +/* + * Start interface + */ +static int ems_usb_start(struct ems_usb *dev) +{ + struct net_device *netdev = dev->netdev; + int err, i; + + dev->intr_in_buffer[0] = 0; + dev->free_slots = 15; /* initial size */ + + for (i = 0; i < MAX_RX_URBS; i++) { + struct urb *urb = NULL; + u8 *buf = NULL; + + /* create a URB, and a buffer for it */ + urb = usb_alloc_urb(0, GFP_KERNEL); + if (!urb) { + dev_err(netdev->dev.parent, + "No memory left for URBs\n"); + return -ENOMEM; + } + + buf = usb_buffer_alloc(dev->udev, RX_BUFFER_SIZE, GFP_KERNEL, + &urb->transfer_dma); + if (!buf) { + dev_err(netdev->dev.parent, + "No memory left for USB buffer\n"); + usb_free_urb(urb); + return -ENOMEM; + } + + usb_fill_bulk_urb(urb, dev->udev, usb_rcvbulkpipe(dev->udev, 2), + buf, RX_BUFFER_SIZE, + ems_usb_read_bulk_callback, dev); + urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; + usb_anchor_urb(urb, &dev->rx_submitted); + + err = usb_submit_urb(urb, GFP_KERNEL); + if (err) { + if (err == -ENODEV) + netif_device_detach(dev->netdev); + + usb_unanchor_urb(urb); + usb_buffer_free(dev->udev, RX_BUFFER_SIZE, buf, + urb->transfer_dma); + break; + } + + /* Drop reference, USB core will take care of freeing it */ + usb_free_urb(urb); + } + + /* Did we submit any URBs */ + if (i == 0) { + dev_warn(netdev->dev.parent, "couldn't setup read URBs\n"); + return err; + } + + /* Warn if we've couldn't transmit all the URBs */ + if (i < MAX_RX_URBS) + dev_warn(netdev->dev.parent, "rx performance may be slow\n"); + + /* Setup and start interrupt URB */ + usb_fill_int_urb(dev->intr_urb, dev->udev, + usb_rcvintpipe(dev->udev, 1), + dev->intr_in_buffer, + INTR_IN_BUFFER_SIZE, + ems_usb_read_interrupt_callback, dev, 1); + + err = usb_submit_urb(dev->intr_urb, GFP_KERNEL); + if (err) { + if (err == -ENODEV) + netif_device_detach(dev->netdev); + + dev_warn(netdev->dev.parent, "intr URB submit failed: %d\n", + err); + + return err; + } + + /* CPC-USB will transfer received message to host */ + err = ems_usb_control_cmd(dev, CONTR_CAN_MESSAGE | CONTR_CONT_ON); + if (err) + goto failed; + + /* CPC-USB will transfer CAN state changes to host */ + err = ems_usb_control_cmd(dev, CONTR_CAN_STATE | CONTR_CONT_ON); + if (err) + goto failed; + + /* CPC-USB will transfer bus errors to host */ + err = ems_usb_control_cmd(dev, CONTR_BUS_ERROR | CONTR_CONT_ON); + if (err) + goto failed; + + err = ems_usb_write_mode(dev, SJA1000_MOD_NORMAL); + if (err) + goto failed; + + dev->can.state = CAN_STATE_ERROR_ACTIVE; + + return 0; + +failed: + if (err == -ENODEV) + netif_device_detach(dev->netdev); + + dev_warn(netdev->dev.parent, "couldn't submit control: %d\n", err); + + return err; +} + +static void unlink_all_urbs(struct ems_usb *dev) +{ + int i; + + usb_unlink_urb(dev->intr_urb); + + usb_kill_anchored_urbs(&dev->rx_submitted); + + usb_kill_anchored_urbs(&dev->tx_submitted); + atomic_set(&dev->active_tx_urbs, 0); + + for (i = 0; i < MAX_TX_URBS; i++) + dev->tx_contexts[i].echo_index = MAX_TX_URBS; +} + +static int ems_usb_open(struct net_device *netdev) +{ + struct ems_usb *dev = netdev_priv(netdev); + int err; + + err = ems_usb_write_mode(dev, SJA1000_MOD_RM); + if (err) + return err; + + /* common open */ + err = open_candev(netdev); + if (err) + return err; + + /* finally start device */ + err = ems_usb_start(dev); + if (err) { + if (err == -ENODEV) + netif_device_detach(dev->netdev); + + dev_warn(netdev->dev.parent, "couldn't start device: %d\n", + err); + + close_candev(netdev); + + return err; + } + + dev->open_time = jiffies; + + netif_start_queue(netdev); + + return 0; +} + +static netdev_tx_t ems_usb_start_xmit(struct sk_buff *skb, struct net_device *netdev) +{ + struct ems_usb *dev = netdev_priv(netdev); + struct ems_tx_urb_context *context = NULL; + struct net_device_stats *stats = &netdev->stats; + struct can_frame *cf = (struct can_frame *)skb->data; + struct ems_cpc_msg *msg; + struct urb *urb; + u8 *buf; + int i, err; + size_t size = CPC_HEADER_SIZE + CPC_MSG_HEADER_LEN + + sizeof(struct cpc_can_msg); + + /* create a URB, and a buffer for it, and copy the data to the URB */ + urb = usb_alloc_urb(0, GFP_ATOMIC); + if (!urb) { + dev_err(netdev->dev.parent, "No memory left for URBs\n"); + goto nomem; + } + + buf = usb_buffer_alloc(dev->udev, size, GFP_ATOMIC, &urb->transfer_dma); + if (!buf) { + dev_err(netdev->dev.parent, "No memory left for USB buffer\n"); + usb_free_urb(urb); + goto nomem; + } + + msg = (struct ems_cpc_msg *)&buf[CPC_HEADER_SIZE]; + + msg->msg.can_msg.id = cf->can_id & CAN_ERR_MASK; + msg->msg.can_msg.length = cf->can_dlc; + + if (cf->can_id & CAN_RTR_FLAG) { + msg->type = cf->can_id & CAN_EFF_FLAG ? + CPC_CMD_TYPE_EXT_RTR_FRAME : CPC_CMD_TYPE_RTR_FRAME; + + msg->length = CPC_CAN_MSG_MIN_SIZE; + } else { + msg->type = cf->can_id & CAN_EFF_FLAG ? + CPC_CMD_TYPE_EXT_CAN_FRAME : CPC_CMD_TYPE_CAN_FRAME; + + for (i = 0; i < cf->can_dlc; i++) + msg->msg.can_msg.msg[i] = cf->data[i]; + + msg->length = CPC_CAN_MSG_MIN_SIZE + cf->can_dlc; + } + + /* Respect byte order */ + msg->msg.can_msg.id = cpu_to_le32(msg->msg.can_msg.id); + + for (i = 0; i < MAX_TX_URBS; i++) { + if (dev->tx_contexts[i].echo_index == MAX_TX_URBS) { + context = &dev->tx_contexts[i]; + break; + } + } + + /* + * May never happen! When this happens we'd more URBs in flight as + * allowed (MAX_TX_URBS). + */ + if (!context) { + usb_unanchor_urb(urb); + usb_buffer_free(dev->udev, size, buf, urb->transfer_dma); + + dev_warn(netdev->dev.parent, "couldn't find free context\n"); + + return NETDEV_TX_BUSY; + } + + context->dev = dev; + context->echo_index = i; + context->dlc = cf->can_dlc; + + usb_fill_bulk_urb(urb, dev->udev, usb_sndbulkpipe(dev->udev, 2), buf, + size, ems_usb_write_bulk_callback, context); + urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; + usb_anchor_urb(urb, &dev->tx_submitted); + + can_put_echo_skb(skb, netdev, context->echo_index); + + atomic_inc(&dev->active_tx_urbs); + + err = usb_submit_urb(urb, GFP_ATOMIC); + if (unlikely(err)) { + can_free_echo_skb(netdev, context->echo_index); + + usb_unanchor_urb(urb); + usb_buffer_free(dev->udev, size, buf, urb->transfer_dma); + dev_kfree_skb(skb); + + atomic_dec(&dev->active_tx_urbs); + + if (err == -ENODEV) { + netif_device_detach(netdev); + } else { + dev_warn(netdev->dev.parent, "failed tx_urb %d\n", err); + + stats->tx_dropped++; + } + } else { + netdev->trans_start = jiffies; + + /* Slow down tx path */ + if (atomic_read(&dev->active_tx_urbs) >= MAX_TX_URBS || + dev->free_slots < 5) { + netif_stop_queue(netdev); + } + } + + /* + * Release our reference to this URB, the USB core will eventually free + * it entirely. + */ + usb_free_urb(urb); + + return NETDEV_TX_OK; + +nomem: + if (skb) + dev_kfree_skb(skb); + + stats->tx_dropped++; + + return NETDEV_TX_OK; +} + +static int ems_usb_close(struct net_device *netdev) +{ + struct ems_usb *dev = netdev_priv(netdev); + + /* Stop polling */ + unlink_all_urbs(dev); + + netif_stop_queue(netdev); + + /* Set CAN controller to reset mode */ + if (ems_usb_write_mode(dev, SJA1000_MOD_RM)) + dev_warn(netdev->dev.parent, "couldn't stop device"); + + close_candev(netdev); + + dev->open_time = 0; + + return 0; +} + +static const struct net_device_ops ems_usb_netdev_ops = { + .ndo_open = ems_usb_open, + .ndo_stop = ems_usb_close, + .ndo_start_xmit = ems_usb_start_xmit, +}; + +static struct can_bittiming_const ems_usb_bittiming_const = { + .name = "ems_usb", + .tseg1_min = 1, + .tseg1_max = 16, + .tseg2_min = 1, + .tseg2_max = 8, + .sjw_max = 4, + .brp_min = 1, + .brp_max = 64, + .brp_inc = 1, +}; + +static int ems_usb_set_mode(struct net_device *netdev, enum can_mode mode) +{ + struct ems_usb *dev = netdev_priv(netdev); + + if (!dev->open_time) + return -EINVAL; + + switch (mode) { + case CAN_MODE_START: + if (ems_usb_write_mode(dev, SJA1000_MOD_NORMAL)) + dev_warn(netdev->dev.parent, "couldn't start device"); + + if (netif_queue_stopped(netdev)) + netif_wake_queue(netdev); + break; + + default: + return -EOPNOTSUPP; + } + + return 0; +} + +static int ems_usb_set_bittiming(struct net_device *netdev) +{ + struct ems_usb *dev = netdev_priv(netdev); + struct can_bittiming *bt = &dev->can.bittiming; + u8 btr0, btr1; + + btr0 = ((bt->brp - 1) & 0x3f) | (((bt->sjw - 1) & 0x3) << 6); + btr1 = ((bt->prop_seg + bt->phase_seg1 - 1) & 0xf) | + (((bt->phase_seg2 - 1) & 0x7) << 4); + if (dev->can.ctrlmode & CAN_CTRLMODE_3_SAMPLES) + btr1 |= 0x80; + + dev_info(netdev->dev.parent, "setting BTR0=0x%02x BTR1=0x%02x\n", + btr0, btr1); + + dev->active_params.msg.can_params.cc_params.sja1000.btr0 = btr0; + dev->active_params.msg.can_params.cc_params.sja1000.btr1 = btr1; + + return ems_usb_command_msg(dev, &dev->active_params); +} + +static void init_params_sja1000(struct ems_cpc_msg *msg) +{ + struct cpc_sja1000_params *sja1000 = + &msg->msg.can_params.cc_params.sja1000; + + msg->type = CPC_CMD_TYPE_CAN_PARAMS; + msg->length = sizeof(struct cpc_can_params); + msg->msgid = 0; + + msg->msg.can_params.cc_type = CPC_CC_TYPE_SJA1000; + + /* Acceptance filter open */ + sja1000->acc_code0 = 0x00; + sja1000->acc_code1 = 0x00; + sja1000->acc_code2 = 0x00; + sja1000->acc_code3 = 0x00; + + /* Acceptance filter open */ + sja1000->acc_mask0 = 0xFF; + sja1000->acc_mask1 = 0xFF; + sja1000->acc_mask2 = 0xFF; + sja1000->acc_mask3 = 0xFF; + + sja1000->btr0 = 0; + sja1000->btr1 = 0; + + sja1000->outp_contr = SJA1000_DEFAULT_OUTPUT_CONTROL; + sja1000->mode = SJA1000_MOD_RM; +} + +/* + * probe function for new CPC-USB devices + */ +static int ems_usb_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + struct net_device *netdev; + struct ems_usb *dev; + int i, err = -ENOMEM; + + netdev = alloc_candev(sizeof(struct ems_usb)); + if (!netdev) { + dev_err(netdev->dev.parent, "Couldn't alloc candev\n"); + return -ENOMEM; + } + + dev = netdev_priv(netdev); + + dev->udev = interface_to_usbdev(intf); + dev->netdev = netdev; + + dev->can.state = CAN_STATE_STOPPED; + dev->can.clock.freq = EMS_USB_ARM7_CLOCK; + dev->can.bittiming_const = &ems_usb_bittiming_const; + dev->can.do_set_bittiming = ems_usb_set_bittiming; + dev->can.do_set_mode = ems_usb_set_mode; + + netdev->flags |= IFF_ECHO; /* we support local echo */ + + netdev->netdev_ops = &ems_usb_netdev_ops; + + netdev->flags |= IFF_ECHO; /* we support local echo */ + + init_usb_anchor(&dev->rx_submitted); + + init_usb_anchor(&dev->tx_submitted); + atomic_set(&dev->active_tx_urbs, 0); + + for (i = 0; i < MAX_TX_URBS; i++) + dev->tx_contexts[i].echo_index = MAX_TX_URBS; + + dev->intr_urb = usb_alloc_urb(0, GFP_KERNEL); + if (!dev->intr_urb) { + dev_err(netdev->dev.parent, "Couldn't alloc intr URB\n"); + goto cleanup_candev; + } + + dev->intr_in_buffer = kzalloc(INTR_IN_BUFFER_SIZE, GFP_KERNEL); + if (!dev->intr_in_buffer) { + dev_err(netdev->dev.parent, "Couldn't alloc Intr buffer\n"); + goto cleanup_intr_urb; + } + + dev->tx_msg_buffer = kzalloc(CPC_HEADER_SIZE + + sizeof(struct ems_cpc_msg), GFP_KERNEL); + if (!dev->tx_msg_buffer) { + dev_err(netdev->dev.parent, "Couldn't alloc Tx buffer\n"); + goto cleanup_intr_in_buffer; + } + + usb_set_intfdata(intf, dev); + + SET_NETDEV_DEV(netdev, &intf->dev); + + init_params_sja1000(&dev->active_params); + + err = ems_usb_command_msg(dev, &dev->active_params); + if (err) { + dev_err(netdev->dev.parent, + "couldn't initialize controller: %d\n", err); + goto cleanup_tx_msg_buffer; + } + + err = register_candev(netdev); + if (err) { + dev_err(netdev->dev.parent, + "couldn't register CAN device: %d\n", err); + goto cleanup_tx_msg_buffer; + } + + return 0; + +cleanup_tx_msg_buffer: + kfree(dev->tx_msg_buffer); + +cleanup_intr_in_buffer: + kfree(dev->intr_in_buffer); + +cleanup_intr_urb: + usb_free_urb(dev->intr_urb); + +cleanup_candev: + free_candev(netdev); + + return err; +} + +/* + * called by the usb core when the device is removed from the system + */ +static void ems_usb_disconnect(struct usb_interface *intf) +{ + struct ems_usb *dev = usb_get_intfdata(intf); + + usb_set_intfdata(intf, NULL); + + if (dev) { + unregister_netdev(dev->netdev); + free_candev(dev->netdev); + + unlink_all_urbs(dev); + + usb_free_urb(dev->intr_urb); + + kfree(dev->intr_in_buffer); + } +} + +/* usb specific object needed to register this driver with the usb subsystem */ +static struct usb_driver ems_usb_driver = { + .name = "ems_usb", + .probe = ems_usb_probe, + .disconnect = ems_usb_disconnect, + .id_table = ems_usb_table, +}; + +static int __init ems_usb_init(void) +{ + int err; + + printk(KERN_INFO "CPC-USB kernel driver loaded\n"); + + /* register this driver with the USB subsystem */ + err = usb_register(&ems_usb_driver); + + if (err) { + err("usb_register failed. Error number %d\n", err); + return err; + } + + return 0; +} + +static void __exit ems_usb_exit(void) +{ + /* deregister this driver with the USB subsystem */ + usb_deregister(&ems_usb_driver); +} + +module_init(ems_usb_init); +module_exit(ems_usb_exit); diff --git a/drivers/net/cassini.c b/drivers/net/cassini.c index 05916aafa4f1..f857afe8e488 100644 --- a/drivers/net/cassini.c +++ b/drivers/net/cassini.c @@ -4342,11 +4342,11 @@ static int cas_open(struct net_device *dev) cas_unlock_all_restore(cp, flags); } + err = -ENOMEM; if (cas_tx_tiny_alloc(cp) < 0) - return -ENOMEM; + goto err_unlock; /* alloc rx descriptors */ - err = -ENOMEM; if (cas_alloc_rxds(cp) < 0) goto err_tx_tiny; @@ -4386,6 +4386,7 @@ err_spare: cas_free_rxds(cp); err_tx_tiny: cas_tx_tiny_free(cp); +err_unlock: mutex_unlock(&cp->pm_mutex); return err; } diff --git a/drivers/net/cnic.c b/drivers/net/cnic.c index d45eacb76702..3bf1b04f2cab 100644 --- a/drivers/net/cnic.c +++ b/drivers/net/cnic.c @@ -85,8 +85,6 @@ static int cnic_uio_open(struct uio_info *uinfo, struct inode *inode) cp->uio_dev = iminor(inode); - cnic_shutdown_bnx2_rx_ring(dev); - cnic_init_bnx2_tx_ring(dev); cnic_init_bnx2_rx_ring(dev); @@ -98,6 +96,8 @@ static int cnic_uio_close(struct uio_info *uinfo, struct inode *inode) struct cnic_dev *dev = uinfo->priv; struct cnic_local *cp = dev->cnic_priv; + cnic_shutdown_bnx2_rx_ring(dev); + cp->uio_dev = -1; return 0; } @@ -2264,9 +2264,9 @@ static void cnic_init_bnx2_rx_ring(struct cnic_dev *dev) cnic_ctx_wr(dev, cid_addr, BNX2_L2CTX_CTX_TYPE, val); if (sb_id == 0) - val = 2 << BNX2_L2CTX_STATUSB_NUM_SHIFT; + val = 2 << BNX2_L2CTX_L2_STATUSB_NUM_SHIFT; else - val = BNX2_L2CTX_STATUSB_NUM(sb_id); + val = BNX2_L2CTX_L2_STATUSB_NUM(sb_id); cnic_ctx_wr(dev, cid_addr, BNX2_L2CTX_HOST_BDIDX, val); rxbd = (struct rx_bd *) (cp->l2_ring + BCM_PAGE_SIZE); @@ -2423,7 +2423,7 @@ static int cnic_start_bnx2_hw(struct cnic_dev *dev) cp->int_num = 0; if (ethdev->drv_state & CNIC_DRV_STATE_USING_MSIX) { u32 sb_id = cp->status_blk_num; - u32 sb = BNX2_L2CTX_STATUSB_NUM(sb_id); + u32 sb = BNX2_L2CTX_L5_STATUSB_NUM(sb_id); cp->int_num = sb_id << BNX2_PCICFG_INT_ACK_CMD_INT_NUM_SHIFT; cnic_ctx_wr(dev, cp->kwq_cid_addr, L5_KRNLQ_HOST_QIDX, sb); @@ -2733,7 +2733,8 @@ static int cnic_netdev_event(struct notifier_block *this, unsigned long event, cnic_ulp_init(dev); else if (event == NETDEV_UNREGISTER) cnic_ulp_exit(dev); - else if (event == NETDEV_UP) { + + if (event == NETDEV_UP) { if (cnic_register_netdev(dev) != 0) { cnic_put(dev); goto done; diff --git a/drivers/net/cnic_if.h b/drivers/net/cnic_if.h index a49235739eef..d8b09efdcb52 100644 --- a/drivers/net/cnic_if.h +++ b/drivers/net/cnic_if.h @@ -12,8 +12,8 @@ #ifndef CNIC_IF_H #define CNIC_IF_H -#define CNIC_MODULE_VERSION "2.0.0" -#define CNIC_MODULE_RELDATE "May 21, 2009" +#define CNIC_MODULE_VERSION "2.0.1" +#define CNIC_MODULE_RELDATE "Oct 01, 2009" #define CNIC_ULP_RDMA 0 #define CNIC_ULP_ISCSI 1 diff --git a/drivers/net/cpmac.c b/drivers/net/cpmac.c index 3e3fab8afb1e..61f9da2b4943 100644 --- a/drivers/net/cpmac.c +++ b/drivers/net/cpmac.c @@ -1109,7 +1109,7 @@ static int external_switch; static int __devinit cpmac_probe(struct platform_device *pdev) { int rc, phy_id; - char mdio_bus_id[BUS_ID_SIZE]; + char mdio_bus_id[MII_BUS_ID_SIZE]; struct resource *mem; struct cpmac_priv *priv; struct net_device *dev; @@ -1118,7 +1118,7 @@ static int __devinit cpmac_probe(struct platform_device *pdev) pdata = pdev->dev.platform_data; if (external_switch || dumb_switch) { - strncpy(mdio_bus_id, "0", BUS_ID_SIZE); /* fixed phys bus */ + strncpy(mdio_bus_id, "0", MII_BUS_ID_SIZE); /* fixed phys bus */ phy_id = pdev->id; } else { for (phy_id = 0; phy_id < PHY_MAX_ADDR; phy_id++) { @@ -1126,7 +1126,7 @@ static int __devinit cpmac_probe(struct platform_device *pdev) continue; if (!cpmac_mii->phy_map[phy_id]) continue; - strncpy(mdio_bus_id, cpmac_mii->id, BUS_ID_SIZE); + strncpy(mdio_bus_id, cpmac_mii->id, MII_BUS_ID_SIZE); break; } } @@ -1167,7 +1167,7 @@ static int __devinit cpmac_probe(struct platform_device *pdev) priv->msg_enable = netif_msg_init(debug_level, 0xff); memcpy(dev->dev_addr, pdata->dev_addr, sizeof(dev->dev_addr)); - snprintf(priv->phy_name, BUS_ID_SIZE, PHY_ID_FMT, mdio_bus_id, phy_id); + snprintf(priv->phy_name, MII_BUS_ID_SIZE, PHY_ID_FMT, mdio_bus_id, phy_id); priv->phy = phy_connect(dev, priv->phy_name, &cpmac_adjust_link, 0, PHY_INTERFACE_MODE_MII); diff --git a/drivers/net/cris/eth_v10.c b/drivers/net/cris/eth_v10.c index 15c0195ebd31..a24be34a3f7a 100644 --- a/drivers/net/cris/eth_v10.c +++ b/drivers/net/cris/eth_v10.c @@ -768,10 +768,24 @@ e100_negotiate(struct net_device* dev) e100_set_mdio_reg(dev, np->mii_if.phy_id, MII_ADVERTISE, data); - /* Renegotiate with link partner */ + data = e100_get_mdio_reg(dev, np->mii_if.phy_id, MII_BMCR); if (autoneg_normal) { - data = e100_get_mdio_reg(dev, np->mii_if.phy_id, MII_BMCR); - data |= BMCR_ANENABLE | BMCR_ANRESTART; + /* Renegotiate with link partner */ + data |= BMCR_ANENABLE | BMCR_ANRESTART; + } else { + /* Don't negotiate speed or duplex */ + data &= ~(BMCR_ANENABLE | BMCR_ANRESTART); + + /* Set speed and duplex static */ + if (current_speed_selection == 10) + data &= ~BMCR_SPEED100; + else + data |= BMCR_SPEED100; + + if (current_duplex != full) + data &= ~BMCR_FULLDPLX; + else + data |= BMCR_FULLDPLX; } e100_set_mdio_reg(dev, np->mii_if.phy_id, MII_BMCR, data); } diff --git a/drivers/net/cxgb3/sge.c b/drivers/net/cxgb3/sge.c index f86612857a73..6366061712f4 100644 --- a/drivers/net/cxgb3/sge.c +++ b/drivers/net/cxgb3/sge.c @@ -879,7 +879,7 @@ recycle: pci_dma_sync_single_for_cpu(adap->pdev, dma_addr, len, PCI_DMA_FROMDEVICE); (*sd->pg_chunk.p_cnt)--; - if (!*sd->pg_chunk.p_cnt) + if (!*sd->pg_chunk.p_cnt && sd->pg_chunk.page != fl->pg_chunk.page) pci_unmap_page(adap->pdev, sd->pg_chunk.mapping, fl->alloc_size, @@ -2088,7 +2088,7 @@ static void lro_add_page(struct adapter *adap, struct sge_qset *qs, PCI_DMA_FROMDEVICE); (*sd->pg_chunk.p_cnt)--; - if (!*sd->pg_chunk.p_cnt) + if (!*sd->pg_chunk.p_cnt && sd->pg_chunk.page != fl->pg_chunk.page) pci_unmap_page(adap->pdev, sd->pg_chunk.mapping, fl->alloc_size, diff --git a/drivers/net/davinci_emac.c b/drivers/net/davinci_emac.c index d465eaa796c4..e3478314c002 100644 --- a/drivers/net/davinci_emac.c +++ b/drivers/net/davinci_emac.c @@ -164,16 +164,14 @@ static const char emac_version_string[] = "TI DaVinci EMAC Linux v6.1"; # define EMAC_MBP_MCASTCHAN(ch) ((ch) & 0x7) /* EMAC mac_control register */ -#define EMAC_MACCONTROL_TXPTYPE (0x200) -#define EMAC_MACCONTROL_TXPACEEN (0x40) -#define EMAC_MACCONTROL_MIIEN (0x20) -#define EMAC_MACCONTROL_GIGABITEN (0x80) -#define EMAC_MACCONTROL_GIGABITEN_SHIFT (7) -#define EMAC_MACCONTROL_FULLDUPLEXEN (0x1) +#define EMAC_MACCONTROL_TXPTYPE BIT(9) +#define EMAC_MACCONTROL_TXPACEEN BIT(6) +#define EMAC_MACCONTROL_GMIIEN BIT(5) +#define EMAC_MACCONTROL_GIGABITEN BIT(7) +#define EMAC_MACCONTROL_FULLDUPLEXEN BIT(0) #define EMAC_MACCONTROL_RMIISPEED_MASK BIT(15) /* GIGABIT MODE related bits */ -#define EMAC_DM646X_MACCONTORL_GMIIEN BIT(5) #define EMAC_DM646X_MACCONTORL_GIG BIT(7) #define EMAC_DM646X_MACCONTORL_GIGFORCE BIT(17) @@ -192,14 +190,16 @@ static const char emac_version_string[] = "TI DaVinci EMAC Linux v6.1"; #define EMAC_RX_BUFFER_OFFSET_MASK (0xFFFF) /* MAC_IN_VECTOR (0x180) register bit fields */ -#define EMAC_DM644X_MAC_IN_VECTOR_HOST_INT (0x20000) -#define EMAC_DM644X_MAC_IN_VECTOR_STATPEND_INT (0x10000) -#define EMAC_DM644X_MAC_IN_VECTOR_RX_INT_VEC (0x0100) -#define EMAC_DM644X_MAC_IN_VECTOR_TX_INT_VEC (0x01) +#define EMAC_DM644X_MAC_IN_VECTOR_HOST_INT BIT(17) +#define EMAC_DM644X_MAC_IN_VECTOR_STATPEND_INT BIT(16) +#define EMAC_DM644X_MAC_IN_VECTOR_RX_INT_VEC BIT(8) +#define EMAC_DM644X_MAC_IN_VECTOR_TX_INT_VEC BIT(0) /** NOTE:: For DM646x the IN_VECTOR has changed */ #define EMAC_DM646X_MAC_IN_VECTOR_RX_INT_VEC BIT(EMAC_DEF_RX_CH) #define EMAC_DM646X_MAC_IN_VECTOR_TX_INT_VEC BIT(16 + EMAC_DEF_TX_CH) +#define EMAC_DM646X_MAC_IN_VECTOR_HOST_INT BIT(26) +#define EMAC_DM646X_MAC_IN_VECTOR_STATPEND_INT BIT(27) /* CPPI bit positions */ #define EMAC_CPPI_SOP_BIT BIT(31) @@ -330,6 +330,9 @@ static const char emac_version_string[] = "TI DaVinci EMAC Linux v6.1"; #define EMAC_DM646X_MAC_EOI_C0_RXEN (0x01) #define EMAC_DM646X_MAC_EOI_C0_TXEN (0x02) +/* EMAC Stats Clear Mask */ +#define EMAC_STATS_CLR_MASK (0xFFFFFFFF) + /** net_buf_obj: EMAC network bufferdata structure * * EMAC network buffer data structure @@ -744,8 +747,7 @@ static void emac_update_phystatus(struct emac_priv *priv) if (priv->speed == SPEED_1000 && (priv->version == EMAC_VERSION_2)) { mac_control = emac_read(EMAC_MACCONTROL); - mac_control |= (EMAC_DM646X_MACCONTORL_GMIIEN | - EMAC_DM646X_MACCONTORL_GIG | + mac_control |= (EMAC_DM646X_MACCONTORL_GIG | EMAC_DM646X_MACCONTORL_GIGFORCE); } else { /* Clear the GIG bit and GIGFORCE bit */ @@ -2102,7 +2104,7 @@ static int emac_hw_enable(struct emac_priv *priv) /* Enable MII */ val = emac_read(EMAC_MACCONTROL); - val |= (EMAC_MACCONTROL_MIIEN); + val |= (EMAC_MACCONTROL_GMIIEN); emac_write(EMAC_MACCONTROL, val); /* Enable NAPI and interrupts */ @@ -2134,9 +2136,6 @@ static int emac_poll(struct napi_struct *napi, int budget) u32 status = 0; u32 num_pkts = 0; - if (!netif_running(ndev)) - return 0; - /* Check interrupt vectors and call packet processing */ status = emac_read(EMAC_MACINVECTOR); @@ -2167,7 +2166,11 @@ static int emac_poll(struct napi_struct *napi, int budget) emac_int_enable(priv); } - if (unlikely(status & EMAC_DM644X_MAC_IN_VECTOR_HOST_INT)) { + mask = EMAC_DM644X_MAC_IN_VECTOR_HOST_INT; + if (priv->version == EMAC_VERSION_2) + mask = EMAC_DM646X_MAC_IN_VECTOR_HOST_INT; + + if (unlikely(status & mask)) { u32 ch, cause; dev_err(emac_dev, "DaVinci EMAC: Fatal Hardware Error\n"); netif_stop_queue(ndev); @@ -2211,7 +2214,7 @@ void emac_poll_controller(struct net_device *ndev) struct emac_priv *priv = netdev_priv(ndev); emac_int_disable(priv); - emac_irq(ndev->irq, priv); + emac_irq(ndev->irq, ndev); emac_int_enable(priv); } #endif @@ -2541,40 +2544,49 @@ static int emac_dev_stop(struct net_device *ndev) static struct net_device_stats *emac_dev_getnetstats(struct net_device *ndev) { struct emac_priv *priv = netdev_priv(ndev); + u32 mac_control; + u32 stats_clear_mask; /* update emac hardware stats and reset the registers*/ + mac_control = emac_read(EMAC_MACCONTROL); + + if (mac_control & EMAC_MACCONTROL_GMIIEN) + stats_clear_mask = EMAC_STATS_CLR_MASK; + else + stats_clear_mask = 0; + priv->net_dev_stats.multicast += emac_read(EMAC_RXMCASTFRAMES); - emac_write(EMAC_RXMCASTFRAMES, EMAC_ALL_MULTI_REG_VALUE); + emac_write(EMAC_RXMCASTFRAMES, stats_clear_mask); priv->net_dev_stats.collisions += (emac_read(EMAC_TXCOLLISION) + emac_read(EMAC_TXSINGLECOLL) + emac_read(EMAC_TXMULTICOLL)); - emac_write(EMAC_TXCOLLISION, EMAC_ALL_MULTI_REG_VALUE); - emac_write(EMAC_TXSINGLECOLL, EMAC_ALL_MULTI_REG_VALUE); - emac_write(EMAC_TXMULTICOLL, EMAC_ALL_MULTI_REG_VALUE); + emac_write(EMAC_TXCOLLISION, stats_clear_mask); + emac_write(EMAC_TXSINGLECOLL, stats_clear_mask); + emac_write(EMAC_TXMULTICOLL, stats_clear_mask); priv->net_dev_stats.rx_length_errors += (emac_read(EMAC_RXOVERSIZED) + emac_read(EMAC_RXJABBER) + emac_read(EMAC_RXUNDERSIZED)); - emac_write(EMAC_RXOVERSIZED, EMAC_ALL_MULTI_REG_VALUE); - emac_write(EMAC_RXJABBER, EMAC_ALL_MULTI_REG_VALUE); - emac_write(EMAC_RXUNDERSIZED, EMAC_ALL_MULTI_REG_VALUE); + emac_write(EMAC_RXOVERSIZED, stats_clear_mask); + emac_write(EMAC_RXJABBER, stats_clear_mask); + emac_write(EMAC_RXUNDERSIZED, stats_clear_mask); priv->net_dev_stats.rx_over_errors += (emac_read(EMAC_RXSOFOVERRUNS) + emac_read(EMAC_RXMOFOVERRUNS)); - emac_write(EMAC_RXSOFOVERRUNS, EMAC_ALL_MULTI_REG_VALUE); - emac_write(EMAC_RXMOFOVERRUNS, EMAC_ALL_MULTI_REG_VALUE); + emac_write(EMAC_RXSOFOVERRUNS, stats_clear_mask); + emac_write(EMAC_RXMOFOVERRUNS, stats_clear_mask); priv->net_dev_stats.rx_fifo_errors += emac_read(EMAC_RXDMAOVERRUNS); - emac_write(EMAC_RXDMAOVERRUNS, EMAC_ALL_MULTI_REG_VALUE); + emac_write(EMAC_RXDMAOVERRUNS, stats_clear_mask); priv->net_dev_stats.tx_carrier_errors += emac_read(EMAC_TXCARRIERSENSE); - emac_write(EMAC_TXCARRIERSENSE, EMAC_ALL_MULTI_REG_VALUE); + emac_write(EMAC_TXCARRIERSENSE, stats_clear_mask); priv->net_dev_stats.tx_fifo_errors = emac_read(EMAC_TXUNDERRUN); - emac_write(EMAC_TXUNDERRUN, EMAC_ALL_MULTI_REG_VALUE); + emac_write(EMAC_TXUNDERRUN, stats_clear_mask); return &priv->net_dev_stats; } diff --git a/drivers/net/depca.c b/drivers/net/depca.c index 9686c1fa28f1..7a3bdac84abe 100644 --- a/drivers/net/depca.c +++ b/drivers/net/depca.c @@ -237,6 +237,7 @@ #include <linux/module.h> #include <linux/kernel.h> +#include <linux/sched.h> #include <linux/string.h> #include <linux/errno.h> #include <linux/ioport.h> diff --git a/drivers/net/dm9000.h b/drivers/net/dm9000.h index 80817c2edfb3..fb1c924d79b4 100644 --- a/drivers/net/dm9000.h +++ b/drivers/net/dm9000.h @@ -50,7 +50,7 @@ #define DM9000_RCSR 0x32 #define CHIPR_DM9000A 0x19 -#define CHIPR_DM9000B 0x1B +#define CHIPR_DM9000B 0x1A #define DM9000_MRCMDX 0xF0 #define DM9000_MRCMD 0xF2 diff --git a/drivers/net/e100.c b/drivers/net/e100.c index 679965c2bb86..d269a68ce354 100644 --- a/drivers/net/e100.c +++ b/drivers/net/e100.c @@ -151,11 +151,13 @@ #include <linux/moduleparam.h> #include <linux/kernel.h> #include <linux/types.h> +#include <linux/sched.h> #include <linux/slab.h> #include <linux/delay.h> #include <linux/init.h> #include <linux/pci.h> #include <linux/dma-mapping.h> +#include <linux/dmapool.h> #include <linux/netdevice.h> #include <linux/etherdevice.h> #include <linux/mii.h> @@ -601,6 +603,7 @@ struct nic { struct mem *mem; dma_addr_t dma_addr; + struct pci_pool *cbs_pool; dma_addr_t cbs_dma_addr; u8 adaptive_ifs; u8 tx_threshold; @@ -1426,19 +1429,31 @@ static int e100_phy_init(struct nic *nic) } else DPRINTK(HW, DEBUG, "phy_addr = %d\n", nic->mii.phy_id); - /* Isolate all the PHY ids */ - for (addr = 0; addr < 32; addr++) - mdio_write(netdev, addr, MII_BMCR, BMCR_ISOLATE); - /* Select the discovered PHY */ - bmcr &= ~BMCR_ISOLATE; - mdio_write(netdev, nic->mii.phy_id, MII_BMCR, bmcr); - /* Get phy ID */ id_lo = mdio_read(netdev, nic->mii.phy_id, MII_PHYSID1); id_hi = mdio_read(netdev, nic->mii.phy_id, MII_PHYSID2); nic->phy = (u32)id_hi << 16 | (u32)id_lo; DPRINTK(HW, DEBUG, "phy ID = 0x%08X\n", nic->phy); + /* Select the phy and isolate the rest */ + for (addr = 0; addr < 32; addr++) { + if (addr != nic->mii.phy_id) { + mdio_write(netdev, addr, MII_BMCR, BMCR_ISOLATE); + } else if (nic->phy != phy_82552_v) { + bmcr = mdio_read(netdev, addr, MII_BMCR); + mdio_write(netdev, addr, MII_BMCR, + bmcr & ~BMCR_ISOLATE); + } + } + /* + * Workaround for 82552: + * Clear the ISOLATE bit on selected phy_id last (mirrored on all + * other phy_id's) using bmcr value from addr discovery loop above. + */ + if (nic->phy == phy_82552_v) + mdio_write(netdev, nic->mii.phy_id, MII_BMCR, + bmcr & ~BMCR_ISOLATE); + /* Handle National tx phys */ #define NCS_PHY_MODEL_MASK 0xFFF0FFFF if ((nic->phy & NCS_PHY_MODEL_MASK) == phy_nsc_tx) { @@ -1780,9 +1795,7 @@ static void e100_clean_cbs(struct nic *nic) nic->cb_to_clean = nic->cb_to_clean->next; nic->cbs_avail++; } - pci_free_consistent(nic->pdev, - sizeof(struct cb) * nic->params.cbs.count, - nic->cbs, nic->cbs_dma_addr); + pci_pool_free(nic->cbs_pool, nic->cbs, nic->cbs_dma_addr); nic->cbs = NULL; nic->cbs_avail = 0; } @@ -1800,8 +1813,8 @@ static int e100_alloc_cbs(struct nic *nic) nic->cb_to_use = nic->cb_to_send = nic->cb_to_clean = NULL; nic->cbs_avail = 0; - nic->cbs = pci_alloc_consistent(nic->pdev, - sizeof(struct cb) * count, &nic->cbs_dma_addr); + nic->cbs = pci_pool_alloc(nic->cbs_pool, GFP_KERNEL, + &nic->cbs_dma_addr); if (!nic->cbs) return -ENOMEM; @@ -2828,7 +2841,11 @@ static int __devinit e100_probe(struct pci_dev *pdev, DPRINTK(PROBE, ERR, "Cannot register net device, aborting.\n"); goto err_out_free; } - + nic->cbs_pool = pci_pool_create(netdev->name, + nic->pdev, + nic->params.cbs.count * sizeof(struct cb), + sizeof(u32), + 0); DPRINTK(PROBE, INFO, "addr 0x%llx, irq %d, MAC addr %pM\n", (unsigned long long)pci_resource_start(pdev, use_io ? 1 : 0), pdev->irq, netdev->dev_addr); @@ -2858,6 +2875,7 @@ static void __devexit e100_remove(struct pci_dev *pdev) unregister_netdev(netdev); e100_free(nic); pci_iounmap(pdev, nic->csr); + pci_pool_destroy(nic->cbs_pool); free_netdev(netdev); pci_release_regions(pdev); pci_disable_device(pdev); diff --git a/drivers/net/e1000/e1000.h b/drivers/net/e1000/e1000.h index 1a4f89c66a26..42e2b7e21c29 100644 --- a/drivers/net/e1000/e1000.h +++ b/drivers/net/e1000/e1000.h @@ -149,7 +149,6 @@ do { \ #define AUTO_ALL_MODES 0 #define E1000_EEPROM_82544_APM 0x0004 -#define E1000_EEPROM_ICH8_APME 0x0004 #define E1000_EEPROM_APME 0x0400 #ifndef E1000_MASTER_SLAVE @@ -293,7 +292,6 @@ struct e1000_adapter { u64 hw_csum_err; u64 hw_csum_good; - u64 rx_hdr_split; u32 alloc_rx_buff_failed; u32 rx_int_delay; u32 rx_abs_int_delay; @@ -317,7 +315,6 @@ struct e1000_adapter { struct e1000_rx_ring test_rx_ring; int msg_enable; - bool have_msi; /* to not mess up cache alignment, always add to the bottom */ bool tso_force; diff --git a/drivers/net/e1000/e1000_ethtool.c b/drivers/net/e1000/e1000_ethtool.c index 27f996a2010f..490b2b7cd3ab 100644 --- a/drivers/net/e1000/e1000_ethtool.c +++ b/drivers/net/e1000/e1000_ethtool.c @@ -82,7 +82,6 @@ static const struct e1000_stats e1000_gstrings_stats[] = { { "rx_long_byte_count", E1000_STAT(stats.gorcl) }, { "rx_csum_offload_good", E1000_STAT(hw_csum_good) }, { "rx_csum_offload_errors", E1000_STAT(hw_csum_err) }, - { "rx_header_split", E1000_STAT(rx_hdr_split) }, { "alloc_rx_buff_failed", E1000_STAT(alloc_rx_buff_failed) }, { "tx_smbus", E1000_STAT(stats.mgptc) }, { "rx_smbus", E1000_STAT(stats.mgprc) }, @@ -114,8 +113,6 @@ static int e1000_get_settings(struct net_device *netdev, SUPPORTED_1000baseT_Full| SUPPORTED_Autoneg | SUPPORTED_TP); - if (hw->phy_type == e1000_phy_ife) - ecmd->supported &= ~SUPPORTED_1000baseT_Full; ecmd->advertising = ADVERTISED_TP; if (hw->autoneg == 1) { @@ -178,14 +175,6 @@ static int e1000_set_settings(struct net_device *netdev, struct e1000_adapter *adapter = netdev_priv(netdev); struct e1000_hw *hw = &adapter->hw; - /* When SoL/IDER sessions are active, autoneg/speed/duplex - * cannot be changed */ - if (e1000_check_phy_reset_block(hw)) { - DPRINTK(DRV, ERR, "Cannot change link characteristics " - "when SoL/IDER is active.\n"); - return -EINVAL; - } - while (test_and_set_bit(__E1000_RESETTING, &adapter->flags)) msleep(1); @@ -330,10 +319,7 @@ static int e1000_set_tso(struct net_device *netdev, u32 data) else netdev->features &= ~NETIF_F_TSO; - if (data && (adapter->hw.mac_type > e1000_82547_rev_2)) - netdev->features |= NETIF_F_TSO6; - else - netdev->features &= ~NETIF_F_TSO6; + netdev->features &= ~NETIF_F_TSO6; DPRINTK(PROBE, INFO, "TSO is %s\n", data ? "Enabled" : "Disabled"); adapter->tso_force = true; @@ -441,7 +427,6 @@ static void e1000_get_regs(struct net_device *netdev, struct ethtool_regs *regs, regs_buff[24] = (u32)phy_data; /* phy local receiver status */ regs_buff[25] = regs_buff[24]; /* phy remote receiver status */ if (hw->mac_type >= e1000_82540 && - hw->mac_type < e1000_82571 && hw->media_type == e1000_media_type_copper) { regs_buff[26] = er32(MANC); } @@ -554,10 +539,8 @@ static int e1000_set_eeprom(struct net_device *netdev, ret_val = e1000_write_eeprom(hw, first_word, last_word - first_word + 1, eeprom_buff); - /* Update the checksum over the first part of the EEPROM if needed - * and flush shadow RAM for 82573 conrollers */ - if ((ret_val == 0) && ((first_word <= EEPROM_CHECKSUM_REG) || - (hw->mac_type == e1000_82573))) + /* Update the checksum over the first part of the EEPROM if needed */ + if ((ret_val == 0) && (first_word <= EEPROM_CHECKSUM_REG)) e1000_update_eeprom_checksum(hw); kfree(eeprom_buff); @@ -568,31 +551,12 @@ static void e1000_get_drvinfo(struct net_device *netdev, struct ethtool_drvinfo *drvinfo) { struct e1000_adapter *adapter = netdev_priv(netdev); - struct e1000_hw *hw = &adapter->hw; char firmware_version[32]; - u16 eeprom_data; strncpy(drvinfo->driver, e1000_driver_name, 32); strncpy(drvinfo->version, e1000_driver_version, 32); - /* EEPROM image version # is reported as firmware version # for - * 8257{1|2|3} controllers */ - e1000_read_eeprom(hw, 5, 1, &eeprom_data); - switch (hw->mac_type) { - case e1000_82571: - case e1000_82572: - case e1000_82573: - case e1000_80003es2lan: - case e1000_ich8lan: - sprintf(firmware_version, "%d.%d-%d", - (eeprom_data & 0xF000) >> 12, - (eeprom_data & 0x0FF0) >> 4, - eeprom_data & 0x000F); - break; - default: - sprintf(firmware_version, "N/A"); - } - + sprintf(firmware_version, "N/A"); strncpy(drvinfo->fw_version, firmware_version, 32); strncpy(drvinfo->bus_info, pci_name(adapter->pdev), 32); drvinfo->regdump_len = e1000_get_regs_len(netdev); @@ -781,21 +745,9 @@ static int e1000_reg_test(struct e1000_adapter *adapter, u64 *data) /* The status register is Read Only, so a write should fail. * Some bits that get toggled are ignored. */ - switch (hw->mac_type) { + /* there are several bits on newer hardware that are r/w */ - case e1000_82571: - case e1000_82572: - case e1000_80003es2lan: - toggle = 0x7FFFF3FF; - break; - case e1000_82573: - case e1000_ich8lan: - toggle = 0x7FFFF033; - break; - default: - toggle = 0xFFFFF833; - break; - } + toggle = 0xFFFFF833; before = er32(STATUS); value = (er32(STATUS) & toggle); @@ -810,12 +762,10 @@ static int e1000_reg_test(struct e1000_adapter *adapter, u64 *data) /* restore previous status */ ew32(STATUS, before); - if (hw->mac_type != e1000_ich8lan) { - REG_PATTERN_TEST(FCAL, 0xFFFFFFFF, 0xFFFFFFFF); - REG_PATTERN_TEST(FCAH, 0x0000FFFF, 0xFFFFFFFF); - REG_PATTERN_TEST(FCT, 0x0000FFFF, 0xFFFFFFFF); - REG_PATTERN_TEST(VET, 0x0000FFFF, 0xFFFFFFFF); - } + REG_PATTERN_TEST(FCAL, 0xFFFFFFFF, 0xFFFFFFFF); + REG_PATTERN_TEST(FCAH, 0x0000FFFF, 0xFFFFFFFF); + REG_PATTERN_TEST(FCT, 0x0000FFFF, 0xFFFFFFFF); + REG_PATTERN_TEST(VET, 0x0000FFFF, 0xFFFFFFFF); REG_PATTERN_TEST(RDTR, 0x0000FFFF, 0xFFFFFFFF); REG_PATTERN_TEST(RDBAH, 0xFFFFFFFF, 0xFFFFFFFF); @@ -830,8 +780,7 @@ static int e1000_reg_test(struct e1000_adapter *adapter, u64 *data) REG_SET_AND_CHECK(RCTL, 0xFFFFFFFF, 0x00000000); - before = (hw->mac_type == e1000_ich8lan ? - 0x06C3B33E : 0x06DFB3FE); + before = 0x06DFB3FE; REG_SET_AND_CHECK(RCTL, before, 0x003FFFFB); REG_SET_AND_CHECK(TCTL, 0xFFFFFFFF, 0x00000000); @@ -839,12 +788,10 @@ static int e1000_reg_test(struct e1000_adapter *adapter, u64 *data) REG_SET_AND_CHECK(RCTL, before, 0xFFFFFFFF); REG_PATTERN_TEST(RDBAL, 0xFFFFFFF0, 0xFFFFFFFF); - if (hw->mac_type != e1000_ich8lan) - REG_PATTERN_TEST(TXCW, 0xC000FFFF, 0x0000FFFF); + REG_PATTERN_TEST(TXCW, 0xC000FFFF, 0x0000FFFF); REG_PATTERN_TEST(TDBAL, 0xFFFFFFF0, 0xFFFFFFFF); REG_PATTERN_TEST(TIDV, 0x0000FFFF, 0x0000FFFF); - value = (hw->mac_type == e1000_ich8lan ? - E1000_RAR_ENTRIES_ICH8LAN : E1000_RAR_ENTRIES); + value = E1000_RAR_ENTRIES; for (i = 0; i < value; i++) { REG_PATTERN_TEST(RA + (((i << 1) + 1) << 2), 0x8003FFFF, 0xFFFFFFFF); @@ -859,8 +806,7 @@ static int e1000_reg_test(struct e1000_adapter *adapter, u64 *data) } - value = (hw->mac_type == e1000_ich8lan ? - E1000_MC_TBL_SIZE_ICH8LAN : E1000_MC_TBL_SIZE); + value = E1000_MC_TBL_SIZE; for (i = 0; i < value; i++) REG_PATTERN_TEST(MTA + (i << 2), 0xFFFFFFFF, 0xFFFFFFFF); @@ -933,9 +879,6 @@ static int e1000_intr_test(struct e1000_adapter *adapter, u64 *data) /* Test each interrupt */ for (; i < 10; i++) { - if (hw->mac_type == e1000_ich8lan && i == 8) - continue; - /* Interrupt to test */ mask = 1 << i; @@ -1289,35 +1232,20 @@ static int e1000_integrated_phy_loopback(struct e1000_adapter *adapter) e1000_write_phy_reg(hw, PHY_CTRL, 0x9140); /* autoneg off */ e1000_write_phy_reg(hw, PHY_CTRL, 0x8140); - } else if (hw->phy_type == e1000_phy_gg82563) - e1000_write_phy_reg(hw, - GG82563_PHY_KMRN_MODE_CTRL, - 0x1CC); + } ctrl_reg = er32(CTRL); - if (hw->phy_type == e1000_phy_ife) { - /* force 100, set loopback */ - e1000_write_phy_reg(hw, PHY_CTRL, 0x6100); + /* force 1000, set loopback */ + e1000_write_phy_reg(hw, PHY_CTRL, 0x4140); - /* Now set up the MAC to the same speed/duplex as the PHY. */ - ctrl_reg &= ~E1000_CTRL_SPD_SEL; /* Clear the speed sel bits */ - ctrl_reg |= (E1000_CTRL_FRCSPD | /* Set the Force Speed Bit */ - E1000_CTRL_FRCDPX | /* Set the Force Duplex Bit */ - E1000_CTRL_SPD_100 |/* Force Speed to 100 */ - E1000_CTRL_FD); /* Force Duplex to FULL */ - } else { - /* force 1000, set loopback */ - e1000_write_phy_reg(hw, PHY_CTRL, 0x4140); - - /* Now set up the MAC to the same speed/duplex as the PHY. */ - ctrl_reg = er32(CTRL); - ctrl_reg &= ~E1000_CTRL_SPD_SEL; /* Clear the speed sel bits */ - ctrl_reg |= (E1000_CTRL_FRCSPD | /* Set the Force Speed Bit */ - E1000_CTRL_FRCDPX | /* Set the Force Duplex Bit */ - E1000_CTRL_SPD_1000 |/* Force Speed to 1000 */ - E1000_CTRL_FD); /* Force Duplex to FULL */ - } + /* Now set up the MAC to the same speed/duplex as the PHY. */ + ctrl_reg = er32(CTRL); + ctrl_reg &= ~E1000_CTRL_SPD_SEL; /* Clear the speed sel bits */ + ctrl_reg |= (E1000_CTRL_FRCSPD | /* Set the Force Speed Bit */ + E1000_CTRL_FRCDPX | /* Set the Force Duplex Bit */ + E1000_CTRL_SPD_1000 |/* Force Speed to 1000 */ + E1000_CTRL_FD); /* Force Duplex to FULL */ if (hw->media_type == e1000_media_type_copper && hw->phy_type == e1000_phy_m88) @@ -1373,14 +1301,8 @@ static int e1000_set_phy_loopback(struct e1000_adapter *adapter) case e1000_82541_rev_2: case e1000_82547: case e1000_82547_rev_2: - case e1000_82571: - case e1000_82572: - case e1000_82573: - case e1000_80003es2lan: - case e1000_ich8lan: return e1000_integrated_phy_loopback(adapter); break; - default: /* Default PHY loopback work is to read the MII * control register and assert bit 14 (loopback mode). @@ -1409,14 +1331,6 @@ static int e1000_setup_loopback_test(struct e1000_adapter *adapter) case e1000_82546_rev_3: return e1000_set_phy_loopback(adapter); break; - case e1000_82571: - case e1000_82572: -#define E1000_SERDES_LB_ON 0x410 - e1000_set_phy_loopback(adapter); - ew32(SCTL, E1000_SERDES_LB_ON); - msleep(10); - return 0; - break; default: rctl = er32(RCTL); rctl |= E1000_RCTL_LBM_TCVR; @@ -1440,26 +1354,12 @@ static void e1000_loopback_cleanup(struct e1000_adapter *adapter) ew32(RCTL, rctl); switch (hw->mac_type) { - case e1000_82571: - case e1000_82572: - if (hw->media_type == e1000_media_type_fiber || - hw->media_type == e1000_media_type_internal_serdes) { -#define E1000_SERDES_LB_OFF 0x400 - ew32(SCTL, E1000_SERDES_LB_OFF); - msleep(10); - break; - } - /* Fall Through */ case e1000_82545: case e1000_82546: case e1000_82545_rev_3: case e1000_82546_rev_3: default: hw->autoneg = true; - if (hw->phy_type == e1000_phy_gg82563) - e1000_write_phy_reg(hw, - GG82563_PHY_KMRN_MODE_CTRL, - 0x180); e1000_read_phy_reg(hw, PHY_CTRL, &phy_reg); if (phy_reg & MII_CR_LOOPBACK) { phy_reg &= ~MII_CR_LOOPBACK; @@ -1560,17 +1460,6 @@ static int e1000_run_loopback_test(struct e1000_adapter *adapter) static int e1000_loopback_test(struct e1000_adapter *adapter, u64 *data) { - struct e1000_hw *hw = &adapter->hw; - - /* PHY loopback cannot be performed if SoL/IDER - * sessions are active */ - if (e1000_check_phy_reset_block(hw)) { - DPRINTK(DRV, ERR, "Cannot do PHY loopback test " - "when SoL/IDER is active.\n"); - *data = 0; - goto out; - } - *data = e1000_setup_desc_rings(adapter); if (*data) goto out; @@ -1592,13 +1481,13 @@ static int e1000_link_test(struct e1000_adapter *adapter, u64 *data) *data = 0; if (hw->media_type == e1000_media_type_internal_serdes) { int i = 0; - hw->serdes_link_down = true; + hw->serdes_has_link = false; /* On some blade server designs, link establishment * could take as long as 2-3 minutes */ do { e1000_check_for_link(hw); - if (!hw->serdes_link_down) + if (hw->serdes_has_link) return *data; msleep(20); } while (i++ < 3750); @@ -1716,15 +1605,11 @@ static int e1000_wol_exclusion(struct e1000_adapter *adapter, case E1000_DEV_ID_82545EM_COPPER: case E1000_DEV_ID_82546GB_QUAD_COPPER: case E1000_DEV_ID_82546GB_PCIE: - case E1000_DEV_ID_82571EB_SERDES_QUAD: /* these don't support WoL at all */ wol->supported = 0; break; case E1000_DEV_ID_82546EB_FIBER: case E1000_DEV_ID_82546GB_FIBER: - case E1000_DEV_ID_82571EB_FIBER: - case E1000_DEV_ID_82571EB_SERDES: - case E1000_DEV_ID_82571EB_COPPER: /* Wake events not supported on port B */ if (er32(STATUS) & E1000_STATUS_FUNC_1) { wol->supported = 0; @@ -1733,10 +1618,6 @@ static int e1000_wol_exclusion(struct e1000_adapter *adapter, /* return success for non excluded adapter ports */ retval = 0; break; - case E1000_DEV_ID_82571EB_QUAD_COPPER: - case E1000_DEV_ID_82571EB_QUAD_FIBER: - case E1000_DEV_ID_82571EB_QUAD_COPPER_LOWPROFILE: - case E1000_DEV_ID_82571PT_QUAD_COPPER: case E1000_DEV_ID_82546GB_QUAD_COPPER_KSP3: /* quad port adapters only support WoL on port A */ if (!adapter->quad_port_a) { @@ -1872,30 +1753,15 @@ static int e1000_phys_id(struct net_device *netdev, u32 data) if (!data) data = INT_MAX; - if (hw->mac_type < e1000_82571) { - if (!adapter->blink_timer.function) { - init_timer(&adapter->blink_timer); - adapter->blink_timer.function = e1000_led_blink_callback; - adapter->blink_timer.data = (unsigned long)adapter; - } - e1000_setup_led(hw); - mod_timer(&adapter->blink_timer, jiffies); - msleep_interruptible(data * 1000); - del_timer_sync(&adapter->blink_timer); - } else if (hw->phy_type == e1000_phy_ife) { - if (!adapter->blink_timer.function) { - init_timer(&adapter->blink_timer); - adapter->blink_timer.function = e1000_led_blink_callback; - adapter->blink_timer.data = (unsigned long)adapter; - } - mod_timer(&adapter->blink_timer, jiffies); - msleep_interruptible(data * 1000); - del_timer_sync(&adapter->blink_timer); - e1000_write_phy_reg(&(adapter->hw), IFE_PHY_SPECIAL_CONTROL_LED, 0); - } else { - e1000_blink_led_start(hw); - msleep_interruptible(data * 1000); + if (!adapter->blink_timer.function) { + init_timer(&adapter->blink_timer); + adapter->blink_timer.function = e1000_led_blink_callback; + adapter->blink_timer.data = (unsigned long)adapter; } + e1000_setup_led(hw); + mod_timer(&adapter->blink_timer, jiffies); + msleep_interruptible(data * 1000); + del_timer_sync(&adapter->blink_timer); e1000_led_off(hw); clear_bit(E1000_LED_ON, &adapter->led_status); diff --git a/drivers/net/e1000/e1000_hw.c b/drivers/net/e1000/e1000_hw.c index 45ac225a7aaa..8d7d87f12827 100644 --- a/drivers/net/e1000/e1000_hw.c +++ b/drivers/net/e1000/e1000_hw.c @@ -24,88 +24,34 @@ e1000-devel Mailing List <e1000-devel@lists.sourceforge.net> Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 -*******************************************************************************/ + */ /* e1000_hw.c * Shared functions for accessing and configuring the MAC */ - #include "e1000_hw.h" -static s32 e1000_swfw_sync_acquire(struct e1000_hw *hw, u16 mask); -static void e1000_swfw_sync_release(struct e1000_hw *hw, u16 mask); -static s32 e1000_read_kmrn_reg(struct e1000_hw *hw, u32 reg_addr, u16 *data); -static s32 e1000_write_kmrn_reg(struct e1000_hw *hw, u32 reg_addr, u16 data); -static s32 e1000_get_software_semaphore(struct e1000_hw *hw); -static void e1000_release_software_semaphore(struct e1000_hw *hw); - -static u8 e1000_arc_subsystem_valid(struct e1000_hw *hw); static s32 e1000_check_downshift(struct e1000_hw *hw); static s32 e1000_check_polarity(struct e1000_hw *hw, e1000_rev_polarity *polarity); static void e1000_clear_hw_cntrs(struct e1000_hw *hw); static void e1000_clear_vfta(struct e1000_hw *hw); -static s32 e1000_commit_shadow_ram(struct e1000_hw *hw); static s32 e1000_config_dsp_after_link_change(struct e1000_hw *hw, bool link_up); static s32 e1000_config_fc_after_link_up(struct e1000_hw *hw); static s32 e1000_detect_gig_phy(struct e1000_hw *hw); -static s32 e1000_erase_ich8_4k_segment(struct e1000_hw *hw, u32 bank); static s32 e1000_get_auto_rd_done(struct e1000_hw *hw); static s32 e1000_get_cable_length(struct e1000_hw *hw, u16 *min_length, u16 *max_length); -static s32 e1000_get_hw_eeprom_semaphore(struct e1000_hw *hw); static s32 e1000_get_phy_cfg_done(struct e1000_hw *hw); -static s32 e1000_get_software_flag(struct e1000_hw *hw); -static s32 e1000_ich8_cycle_init(struct e1000_hw *hw); -static s32 e1000_ich8_flash_cycle(struct e1000_hw *hw, u32 timeout); static s32 e1000_id_led_init(struct e1000_hw *hw); -static s32 e1000_init_lcd_from_nvm_config_region(struct e1000_hw *hw, - u32 cnf_base_addr, - u32 cnf_size); -static s32 e1000_init_lcd_from_nvm(struct e1000_hw *hw); static void e1000_init_rx_addrs(struct e1000_hw *hw); -static void e1000_initialize_hardware_bits(struct e1000_hw *hw); -static bool e1000_is_onboard_nvm_eeprom(struct e1000_hw *hw); -static s32 e1000_kumeran_lock_loss_workaround(struct e1000_hw *hw); -static s32 e1000_mng_enable_host_if(struct e1000_hw *hw); -static s32 e1000_mng_host_if_write(struct e1000_hw *hw, u8 *buffer, u16 length, - u16 offset, u8 *sum); -static s32 e1000_mng_write_cmd_header(struct e1000_hw* hw, - struct e1000_host_mng_command_header - *hdr); -static s32 e1000_mng_write_commit(struct e1000_hw *hw); -static s32 e1000_phy_ife_get_info(struct e1000_hw *hw, - struct e1000_phy_info *phy_info); static s32 e1000_phy_igp_get_info(struct e1000_hw *hw, struct e1000_phy_info *phy_info); -static s32 e1000_read_eeprom_eerd(struct e1000_hw *hw, u16 offset, u16 words, - u16 *data); -static s32 e1000_write_eeprom_eewr(struct e1000_hw *hw, u16 offset, u16 words, - u16 *data); -static s32 e1000_poll_eerd_eewr_done(struct e1000_hw *hw, int eerd); static s32 e1000_phy_m88_get_info(struct e1000_hw *hw, struct e1000_phy_info *phy_info); -static void e1000_put_hw_eeprom_semaphore(struct e1000_hw *hw); -static s32 e1000_read_ich8_byte(struct e1000_hw *hw, u32 index, u8 *data); -static s32 e1000_verify_write_ich8_byte(struct e1000_hw *hw, u32 index, - u8 byte); -static s32 e1000_write_ich8_byte(struct e1000_hw *hw, u32 index, u8 byte); -static s32 e1000_read_ich8_word(struct e1000_hw *hw, u32 index, u16 *data); -static s32 e1000_read_ich8_data(struct e1000_hw *hw, u32 index, u32 size, - u16 *data); -static s32 e1000_write_ich8_data(struct e1000_hw *hw, u32 index, u32 size, - u16 data); -static s32 e1000_read_eeprom_ich8(struct e1000_hw *hw, u16 offset, u16 words, - u16 *data); -static s32 e1000_write_eeprom_ich8(struct e1000_hw *hw, u16 offset, u16 words, - u16 *data); -static void e1000_release_software_flag(struct e1000_hw *hw); static s32 e1000_set_d3_lplu_state(struct e1000_hw *hw, bool active); -static s32 e1000_set_d0_lplu_state(struct e1000_hw *hw, bool active); -static s32 e1000_set_pci_ex_no_snoop(struct e1000_hw *hw, u32 no_snoop); -static void e1000_set_pci_express_master_disable(struct e1000_hw *hw); static s32 e1000_wait_autoneg(struct e1000_hw *hw); static void e1000_write_reg_io(struct e1000_hw *hw, u32 offset, u32 value); static s32 e1000_set_phy_type(struct e1000_hw *hw); @@ -117,12 +63,11 @@ static s32 e1000_phy_force_speed_duplex(struct e1000_hw *hw); static s32 e1000_config_mac_to_phy(struct e1000_hw *hw); static void e1000_raise_mdi_clk(struct e1000_hw *hw, u32 *ctrl); static void e1000_lower_mdi_clk(struct e1000_hw *hw, u32 *ctrl); -static void e1000_shift_out_mdi_bits(struct e1000_hw *hw, u32 data, - u16 count); +static void e1000_shift_out_mdi_bits(struct e1000_hw *hw, u32 data, u16 count); static u16 e1000_shift_in_mdi_bits(struct e1000_hw *hw); static s32 e1000_phy_reset_dsp(struct e1000_hw *hw); static s32 e1000_write_eeprom_spi(struct e1000_hw *hw, u16 offset, - u16 words, u16 *data); + u16 words, u16 *data); static s32 e1000_write_eeprom_microwire(struct e1000_hw *hw, u16 offset, u16 words, u16 *data); static s32 e1000_spi_eeprom_ready(struct e1000_hw *hw); @@ -131,7 +76,7 @@ static void e1000_lower_ee_clk(struct e1000_hw *hw, u32 *eecd); static void e1000_shift_out_ee_bits(struct e1000_hw *hw, u16 data, u16 count); static s32 e1000_write_phy_reg_ex(struct e1000_hw *hw, u32 reg_addr, u16 phy_data); -static s32 e1000_read_phy_reg_ex(struct e1000_hw *hw,u32 reg_addr, +static s32 e1000_read_phy_reg_ex(struct e1000_hw *hw, u32 reg_addr, u16 *phy_data); static u16 e1000_shift_in_ee_bits(struct e1000_hw *hw, u16 count); static s32 e1000_acquire_eeprom(struct e1000_hw *hw); @@ -140,188 +85,164 @@ static void e1000_standby_eeprom(struct e1000_hw *hw); static s32 e1000_set_vco_speed(struct e1000_hw *hw); static s32 e1000_polarity_reversal_workaround(struct e1000_hw *hw); static s32 e1000_set_phy_mode(struct e1000_hw *hw); -static s32 e1000_host_if_read_cookie(struct e1000_hw *hw, u8 *buffer); -static u8 e1000_calculate_mng_checksum(char *buffer, u32 length); -static s32 e1000_configure_kmrn_for_10_100(struct e1000_hw *hw, u16 duplex); -static s32 e1000_configure_kmrn_for_1000(struct e1000_hw *hw); -static s32 e1000_do_read_eeprom(struct e1000_hw *hw, u16 offset, u16 words, u16 *data); -static s32 e1000_do_write_eeprom(struct e1000_hw *hw, u16 offset, u16 words, u16 *data); +static s32 e1000_do_read_eeprom(struct e1000_hw *hw, u16 offset, u16 words, + u16 *data); +static s32 e1000_do_write_eeprom(struct e1000_hw *hw, u16 offset, u16 words, + u16 *data); /* IGP cable length table */ static const -u16 e1000_igp_cable_length_table[IGP01E1000_AGC_LENGTH_TABLE_SIZE] = - { 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 10, 10, 10, 10, 10, 10, 10, 20, 20, 20, 20, 20, 25, 25, 25, - 25, 25, 25, 25, 30, 30, 30, 30, 40, 40, 40, 40, 40, 40, 40, 40, - 40, 50, 50, 50, 50, 50, 50, 50, 60, 60, 60, 60, 60, 60, 60, 60, - 60, 70, 70, 70, 70, 70, 70, 80, 80, 80, 80, 80, 80, 90, 90, 90, - 90, 90, 90, 90, 90, 90, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, - 100, 100, 100, 100, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, - 110, 110, 110, 110, 110, 110, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120}; - -static const -u16 e1000_igp_2_cable_length_table[IGP02E1000_AGC_LENGTH_TABLE_SIZE] = - { 0, 0, 0, 0, 0, 0, 0, 0, 3, 5, 8, 11, 13, 16, 18, 21, - 0, 0, 0, 3, 6, 10, 13, 16, 19, 23, 26, 29, 32, 35, 38, 41, - 6, 10, 14, 18, 22, 26, 30, 33, 37, 41, 44, 48, 51, 54, 58, 61, - 21, 26, 31, 35, 40, 44, 49, 53, 57, 61, 65, 68, 72, 75, 79, 82, - 40, 45, 51, 56, 61, 66, 70, 75, 79, 83, 87, 91, 94, 98, 101, 104, - 60, 66, 72, 77, 82, 87, 92, 96, 100, 104, 108, 111, 114, 117, 119, 121, - 83, 89, 95, 100, 105, 109, 113, 116, 119, 122, 124, - 104, 109, 114, 118, 121, 124}; +u16 e1000_igp_cable_length_table[IGP01E1000_AGC_LENGTH_TABLE_SIZE] = { + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 10, 10, 10, 10, 10, 10, 10, 20, 20, 20, 20, 20, 25, 25, 25, + 25, 25, 25, 25, 30, 30, 30, 30, 40, 40, 40, 40, 40, 40, 40, 40, + 40, 50, 50, 50, 50, 50, 50, 50, 60, 60, 60, 60, 60, 60, 60, 60, + 60, 70, 70, 70, 70, 70, 70, 80, 80, 80, 80, 80, 80, 90, 90, 90, + 90, 90, 90, 90, 90, 90, 100, 100, 100, 100, 100, 100, 100, 100, 100, + 100, + 100, 100, 100, 100, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, + 110, 110, + 110, 110, 110, 110, 110, 110, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120 +}; static DEFINE_SPINLOCK(e1000_eeprom_lock); -/****************************************************************************** - * Set the phy type member in the hw struct. - * - * hw - Struct containing variables accessed by shared code - *****************************************************************************/ +/** + * e1000_set_phy_type - Set the phy type member in the hw struct. + * @hw: Struct containing variables accessed by shared code + */ static s32 e1000_set_phy_type(struct e1000_hw *hw) { - DEBUGFUNC("e1000_set_phy_type"); - - if (hw->mac_type == e1000_undefined) - return -E1000_ERR_PHY_TYPE; - - switch (hw->phy_id) { - case M88E1000_E_PHY_ID: - case M88E1000_I_PHY_ID: - case M88E1011_I_PHY_ID: - case M88E1111_I_PHY_ID: - hw->phy_type = e1000_phy_m88; - break; - case IGP01E1000_I_PHY_ID: - if (hw->mac_type == e1000_82541 || - hw->mac_type == e1000_82541_rev_2 || - hw->mac_type == e1000_82547 || - hw->mac_type == e1000_82547_rev_2) { - hw->phy_type = e1000_phy_igp; - break; - } - case IGP03E1000_E_PHY_ID: - hw->phy_type = e1000_phy_igp_3; - break; - case IFE_E_PHY_ID: - case IFE_PLUS_E_PHY_ID: - case IFE_C_E_PHY_ID: - hw->phy_type = e1000_phy_ife; - break; - case GG82563_E_PHY_ID: - if (hw->mac_type == e1000_80003es2lan) { - hw->phy_type = e1000_phy_gg82563; - break; - } - /* Fall Through */ - default: - /* Should never have loaded on this device */ - hw->phy_type = e1000_phy_undefined; - return -E1000_ERR_PHY_TYPE; - } - - return E1000_SUCCESS; -} - -/****************************************************************************** - * IGP phy init script - initializes the GbE PHY - * - * hw - Struct containing variables accessed by shared code - *****************************************************************************/ -static void e1000_phy_init_script(struct e1000_hw *hw) -{ - u32 ret_val; - u16 phy_saved_data; - - DEBUGFUNC("e1000_phy_init_script"); - - if (hw->phy_init_script) { - msleep(20); - - /* Save off the current value of register 0x2F5B to be restored at - * the end of this routine. */ - ret_val = e1000_read_phy_reg(hw, 0x2F5B, &phy_saved_data); - - /* Disabled the PHY transmitter */ - e1000_write_phy_reg(hw, 0x2F5B, 0x0003); + DEBUGFUNC("e1000_set_phy_type"); - msleep(20); + if (hw->mac_type == e1000_undefined) + return -E1000_ERR_PHY_TYPE; - e1000_write_phy_reg(hw,0x0000,0x0140); - - msleep(5); - - switch (hw->mac_type) { - case e1000_82541: - case e1000_82547: - e1000_write_phy_reg(hw, 0x1F95, 0x0001); - - e1000_write_phy_reg(hw, 0x1F71, 0xBD21); - - e1000_write_phy_reg(hw, 0x1F79, 0x0018); - - e1000_write_phy_reg(hw, 0x1F30, 0x1600); - - e1000_write_phy_reg(hw, 0x1F31, 0x0014); - - e1000_write_phy_reg(hw, 0x1F32, 0x161C); - - e1000_write_phy_reg(hw, 0x1F94, 0x0003); - - e1000_write_phy_reg(hw, 0x1F96, 0x003F); - - e1000_write_phy_reg(hw, 0x2010, 0x0008); - break; - - case e1000_82541_rev_2: - case e1000_82547_rev_2: - e1000_write_phy_reg(hw, 0x1F73, 0x0099); - break; - default: - break; - } - - e1000_write_phy_reg(hw, 0x0000, 0x3300); - - msleep(20); - - /* Now enable the transmitter */ - e1000_write_phy_reg(hw, 0x2F5B, phy_saved_data); - - if (hw->mac_type == e1000_82547) { - u16 fused, fine, coarse; - - /* Move to analog registers page */ - e1000_read_phy_reg(hw, IGP01E1000_ANALOG_SPARE_FUSE_STATUS, &fused); - - if (!(fused & IGP01E1000_ANALOG_SPARE_FUSE_ENABLED)) { - e1000_read_phy_reg(hw, IGP01E1000_ANALOG_FUSE_STATUS, &fused); + switch (hw->phy_id) { + case M88E1000_E_PHY_ID: + case M88E1000_I_PHY_ID: + case M88E1011_I_PHY_ID: + case M88E1111_I_PHY_ID: + hw->phy_type = e1000_phy_m88; + break; + case IGP01E1000_I_PHY_ID: + if (hw->mac_type == e1000_82541 || + hw->mac_type == e1000_82541_rev_2 || + hw->mac_type == e1000_82547 || + hw->mac_type == e1000_82547_rev_2) { + hw->phy_type = e1000_phy_igp; + break; + } + default: + /* Should never have loaded on this device */ + hw->phy_type = e1000_phy_undefined; + return -E1000_ERR_PHY_TYPE; + } - fine = fused & IGP01E1000_ANALOG_FUSE_FINE_MASK; - coarse = fused & IGP01E1000_ANALOG_FUSE_COARSE_MASK; + return E1000_SUCCESS; +} - if (coarse > IGP01E1000_ANALOG_FUSE_COARSE_THRESH) { - coarse -= IGP01E1000_ANALOG_FUSE_COARSE_10; - fine -= IGP01E1000_ANALOG_FUSE_FINE_1; - } else if (coarse == IGP01E1000_ANALOG_FUSE_COARSE_THRESH) - fine -= IGP01E1000_ANALOG_FUSE_FINE_10; +/** + * e1000_phy_init_script - IGP phy init script - initializes the GbE PHY + * @hw: Struct containing variables accessed by shared code + */ +static void e1000_phy_init_script(struct e1000_hw *hw) +{ + u32 ret_val; + u16 phy_saved_data; + + DEBUGFUNC("e1000_phy_init_script"); + + if (hw->phy_init_script) { + msleep(20); + + /* Save off the current value of register 0x2F5B to be restored at + * the end of this routine. */ + ret_val = e1000_read_phy_reg(hw, 0x2F5B, &phy_saved_data); + + /* Disabled the PHY transmitter */ + e1000_write_phy_reg(hw, 0x2F5B, 0x0003); + msleep(20); + + e1000_write_phy_reg(hw, 0x0000, 0x0140); + msleep(5); + + switch (hw->mac_type) { + case e1000_82541: + case e1000_82547: + e1000_write_phy_reg(hw, 0x1F95, 0x0001); + e1000_write_phy_reg(hw, 0x1F71, 0xBD21); + e1000_write_phy_reg(hw, 0x1F79, 0x0018); + e1000_write_phy_reg(hw, 0x1F30, 0x1600); + e1000_write_phy_reg(hw, 0x1F31, 0x0014); + e1000_write_phy_reg(hw, 0x1F32, 0x161C); + e1000_write_phy_reg(hw, 0x1F94, 0x0003); + e1000_write_phy_reg(hw, 0x1F96, 0x003F); + e1000_write_phy_reg(hw, 0x2010, 0x0008); + break; - fused = (fused & IGP01E1000_ANALOG_FUSE_POLY_MASK) | - (fine & IGP01E1000_ANALOG_FUSE_FINE_MASK) | - (coarse & IGP01E1000_ANALOG_FUSE_COARSE_MASK); + case e1000_82541_rev_2: + case e1000_82547_rev_2: + e1000_write_phy_reg(hw, 0x1F73, 0x0099); + break; + default: + break; + } - e1000_write_phy_reg(hw, IGP01E1000_ANALOG_FUSE_CONTROL, fused); - e1000_write_phy_reg(hw, IGP01E1000_ANALOG_FUSE_BYPASS, - IGP01E1000_ANALOG_FUSE_ENABLE_SW_CONTROL); - } - } - } + e1000_write_phy_reg(hw, 0x0000, 0x3300); + msleep(20); + + /* Now enable the transmitter */ + e1000_write_phy_reg(hw, 0x2F5B, phy_saved_data); + + if (hw->mac_type == e1000_82547) { + u16 fused, fine, coarse; + + /* Move to analog registers page */ + e1000_read_phy_reg(hw, + IGP01E1000_ANALOG_SPARE_FUSE_STATUS, + &fused); + + if (!(fused & IGP01E1000_ANALOG_SPARE_FUSE_ENABLED)) { + e1000_read_phy_reg(hw, + IGP01E1000_ANALOG_FUSE_STATUS, + &fused); + + fine = fused & IGP01E1000_ANALOG_FUSE_FINE_MASK; + coarse = + fused & IGP01E1000_ANALOG_FUSE_COARSE_MASK; + + if (coarse > + IGP01E1000_ANALOG_FUSE_COARSE_THRESH) { + coarse -= + IGP01E1000_ANALOG_FUSE_COARSE_10; + fine -= IGP01E1000_ANALOG_FUSE_FINE_1; + } else if (coarse == + IGP01E1000_ANALOG_FUSE_COARSE_THRESH) + fine -= IGP01E1000_ANALOG_FUSE_FINE_10; + + fused = + (fused & IGP01E1000_ANALOG_FUSE_POLY_MASK) | + (fine & IGP01E1000_ANALOG_FUSE_FINE_MASK) | + (coarse & + IGP01E1000_ANALOG_FUSE_COARSE_MASK); + + e1000_write_phy_reg(hw, + IGP01E1000_ANALOG_FUSE_CONTROL, + fused); + e1000_write_phy_reg(hw, + IGP01E1000_ANALOG_FUSE_BYPASS, + IGP01E1000_ANALOG_FUSE_ENABLE_SW_CONTROL); + } + } + } } -/****************************************************************************** - * Set the mac type member in the hw struct. - * - * hw - Struct containing variables accessed by shared code - *****************************************************************************/ +/** + * e1000_set_mac_type - Set the mac type member in the hw struct. + * @hw: Struct containing variables accessed by shared code + */ s32 e1000_set_mac_type(struct e1000_hw *hw) { DEBUGFUNC("e1000_set_mac_type"); @@ -397,61 +318,12 @@ s32 e1000_set_mac_type(struct e1000_hw *hw) case E1000_DEV_ID_82547GI: hw->mac_type = e1000_82547_rev_2; break; - case E1000_DEV_ID_82571EB_COPPER: - case E1000_DEV_ID_82571EB_FIBER: - case E1000_DEV_ID_82571EB_SERDES: - case E1000_DEV_ID_82571EB_SERDES_DUAL: - case E1000_DEV_ID_82571EB_SERDES_QUAD: - case E1000_DEV_ID_82571EB_QUAD_COPPER: - case E1000_DEV_ID_82571PT_QUAD_COPPER: - case E1000_DEV_ID_82571EB_QUAD_FIBER: - case E1000_DEV_ID_82571EB_QUAD_COPPER_LOWPROFILE: - hw->mac_type = e1000_82571; - break; - case E1000_DEV_ID_82572EI_COPPER: - case E1000_DEV_ID_82572EI_FIBER: - case E1000_DEV_ID_82572EI_SERDES: - case E1000_DEV_ID_82572EI: - hw->mac_type = e1000_82572; - break; - case E1000_DEV_ID_82573E: - case E1000_DEV_ID_82573E_IAMT: - case E1000_DEV_ID_82573L: - hw->mac_type = e1000_82573; - break; - case E1000_DEV_ID_80003ES2LAN_COPPER_SPT: - case E1000_DEV_ID_80003ES2LAN_SERDES_SPT: - case E1000_DEV_ID_80003ES2LAN_COPPER_DPT: - case E1000_DEV_ID_80003ES2LAN_SERDES_DPT: - hw->mac_type = e1000_80003es2lan; - break; - case E1000_DEV_ID_ICH8_IGP_M_AMT: - case E1000_DEV_ID_ICH8_IGP_AMT: - case E1000_DEV_ID_ICH8_IGP_C: - case E1000_DEV_ID_ICH8_IFE: - case E1000_DEV_ID_ICH8_IFE_GT: - case E1000_DEV_ID_ICH8_IFE_G: - case E1000_DEV_ID_ICH8_IGP_M: - hw->mac_type = e1000_ich8lan; - break; default: /* Should never have loaded on this device */ return -E1000_ERR_MAC_TYPE; } switch (hw->mac_type) { - case e1000_ich8lan: - hw->swfwhw_semaphore_present = true; - hw->asf_firmware_present = true; - break; - case e1000_80003es2lan: - hw->swfw_sync_present = true; - /* fall through */ - case e1000_82571: - case e1000_82572: - case e1000_82573: - hw->eeprom_semaphore_present = true; - /* fall through */ case e1000_82541: case e1000_82547: case e1000_82541_rev_2: @@ -468,6058 +340,4500 @@ s32 e1000_set_mac_type(struct e1000_hw *hw) if (hw->mac_type == e1000_82543) hw->bad_tx_carr_stats_fd = true; - /* capable of receiving management packets to the host */ - if (hw->mac_type >= e1000_82571) - hw->has_manc2h = true; - - /* In rare occasions, ESB2 systems would end up started without - * the RX unit being turned on. - */ - if (hw->mac_type == e1000_80003es2lan) - hw->rx_needs_kicking = true; - if (hw->mac_type > e1000_82544) hw->has_smbus = true; return E1000_SUCCESS; } -/***************************************************************************** - * Set media type and TBI compatibility. - * - * hw - Struct containing variables accessed by shared code - * **************************************************************************/ +/** + * e1000_set_media_type - Set media type and TBI compatibility. + * @hw: Struct containing variables accessed by shared code + */ void e1000_set_media_type(struct e1000_hw *hw) { - u32 status; - - DEBUGFUNC("e1000_set_media_type"); - - if (hw->mac_type != e1000_82543) { - /* tbi_compatibility is only valid on 82543 */ - hw->tbi_compatibility_en = false; - } - - switch (hw->device_id) { - case E1000_DEV_ID_82545GM_SERDES: - case E1000_DEV_ID_82546GB_SERDES: - case E1000_DEV_ID_82571EB_SERDES: - case E1000_DEV_ID_82571EB_SERDES_DUAL: - case E1000_DEV_ID_82571EB_SERDES_QUAD: - case E1000_DEV_ID_82572EI_SERDES: - case E1000_DEV_ID_80003ES2LAN_SERDES_DPT: - hw->media_type = e1000_media_type_internal_serdes; - break; - default: - switch (hw->mac_type) { - case e1000_82542_rev2_0: - case e1000_82542_rev2_1: - hw->media_type = e1000_media_type_fiber; - break; - case e1000_ich8lan: - case e1000_82573: - /* The STATUS_TBIMODE bit is reserved or reused for the this - * device. - */ - hw->media_type = e1000_media_type_copper; - break; - default: - status = er32(STATUS); - if (status & E1000_STATUS_TBIMODE) { - hw->media_type = e1000_media_type_fiber; - /* tbi_compatibility not valid on fiber */ - hw->tbi_compatibility_en = false; - } else { - hw->media_type = e1000_media_type_copper; - } - break; - } - } + u32 status; + + DEBUGFUNC("e1000_set_media_type"); + + if (hw->mac_type != e1000_82543) { + /* tbi_compatibility is only valid on 82543 */ + hw->tbi_compatibility_en = false; + } + + switch (hw->device_id) { + case E1000_DEV_ID_82545GM_SERDES: + case E1000_DEV_ID_82546GB_SERDES: + hw->media_type = e1000_media_type_internal_serdes; + break; + default: + switch (hw->mac_type) { + case e1000_82542_rev2_0: + case e1000_82542_rev2_1: + hw->media_type = e1000_media_type_fiber; + break; + default: + status = er32(STATUS); + if (status & E1000_STATUS_TBIMODE) { + hw->media_type = e1000_media_type_fiber; + /* tbi_compatibility not valid on fiber */ + hw->tbi_compatibility_en = false; + } else { + hw->media_type = e1000_media_type_copper; + } + break; + } + } } -/****************************************************************************** - * Reset the transmit and receive units; mask and clear all interrupts. +/** + * e1000_reset_hw: reset the hardware completely + * @hw: Struct containing variables accessed by shared code * - * hw - Struct containing variables accessed by shared code - *****************************************************************************/ + * Reset the transmit and receive units; mask and clear all interrupts. + */ s32 e1000_reset_hw(struct e1000_hw *hw) { - u32 ctrl; - u32 ctrl_ext; - u32 icr; - u32 manc; - u32 led_ctrl; - u32 timeout; - u32 extcnf_ctrl; - s32 ret_val; - - DEBUGFUNC("e1000_reset_hw"); - - /* For 82542 (rev 2.0), disable MWI before issuing a device reset */ - if (hw->mac_type == e1000_82542_rev2_0) { - DEBUGOUT("Disabling MWI on 82542 rev 2.0\n"); - e1000_pci_clear_mwi(hw); - } - - if (hw->bus_type == e1000_bus_type_pci_express) { - /* Prevent the PCI-E bus from sticking if there is no TLP connection - * on the last TLP read/write transaction when MAC is reset. - */ - if (e1000_disable_pciex_master(hw) != E1000_SUCCESS) { - DEBUGOUT("PCI-E Master disable polling has failed.\n"); - } - } - - /* Clear interrupt mask to stop board from generating interrupts */ - DEBUGOUT("Masking off all interrupts\n"); - ew32(IMC, 0xffffffff); - - /* Disable the Transmit and Receive units. Then delay to allow - * any pending transactions to complete before we hit the MAC with - * the global reset. - */ - ew32(RCTL, 0); - ew32(TCTL, E1000_TCTL_PSP); - E1000_WRITE_FLUSH(); - - /* The tbi_compatibility_on Flag must be cleared when Rctl is cleared. */ - hw->tbi_compatibility_on = false; - - /* Delay to allow any outstanding PCI transactions to complete before - * resetting the device - */ - msleep(10); - - ctrl = er32(CTRL); - - /* Must reset the PHY before resetting the MAC */ - if ((hw->mac_type == e1000_82541) || (hw->mac_type == e1000_82547)) { - ew32(CTRL, (ctrl | E1000_CTRL_PHY_RST)); - msleep(5); - } - - /* Must acquire the MDIO ownership before MAC reset. - * Ownership defaults to firmware after a reset. */ - if (hw->mac_type == e1000_82573) { - timeout = 10; - - extcnf_ctrl = er32(EXTCNF_CTRL); - extcnf_ctrl |= E1000_EXTCNF_CTRL_MDIO_SW_OWNERSHIP; - - do { - ew32(EXTCNF_CTRL, extcnf_ctrl); - extcnf_ctrl = er32(EXTCNF_CTRL); - - if (extcnf_ctrl & E1000_EXTCNF_CTRL_MDIO_SW_OWNERSHIP) - break; - else - extcnf_ctrl |= E1000_EXTCNF_CTRL_MDIO_SW_OWNERSHIP; - - msleep(2); - timeout--; - } while (timeout); - } - - /* Workaround for ICH8 bit corruption issue in FIFO memory */ - if (hw->mac_type == e1000_ich8lan) { - /* Set Tx and Rx buffer allocation to 8k apiece. */ - ew32(PBA, E1000_PBA_8K); - /* Set Packet Buffer Size to 16k. */ - ew32(PBS, E1000_PBS_16K); - } - - /* Issue a global reset to the MAC. This will reset the chip's - * transmit, receive, DMA, and link units. It will not effect - * the current PCI configuration. The global reset bit is self- - * clearing, and should clear within a microsecond. - */ - DEBUGOUT("Issuing a global reset to MAC\n"); - - switch (hw->mac_type) { - case e1000_82544: - case e1000_82540: - case e1000_82545: - case e1000_82546: - case e1000_82541: - case e1000_82541_rev_2: - /* These controllers can't ack the 64-bit write when issuing the - * reset, so use IO-mapping as a workaround to issue the reset */ - E1000_WRITE_REG_IO(hw, CTRL, (ctrl | E1000_CTRL_RST)); - break; - case e1000_82545_rev_3: - case e1000_82546_rev_3: - /* Reset is performed on a shadow of the control register */ - ew32(CTRL_DUP, (ctrl | E1000_CTRL_RST)); - break; - case e1000_ich8lan: - if (!hw->phy_reset_disable && - e1000_check_phy_reset_block(hw) == E1000_SUCCESS) { - /* e1000_ich8lan PHY HW reset requires MAC CORE reset - * at the same time to make sure the interface between - * MAC and the external PHY is reset. - */ - ctrl |= E1000_CTRL_PHY_RST; - } - - e1000_get_software_flag(hw); - ew32(CTRL, (ctrl | E1000_CTRL_RST)); - msleep(5); - break; - default: - ew32(CTRL, (ctrl | E1000_CTRL_RST)); - break; - } - - /* After MAC reset, force reload of EEPROM to restore power-on settings to - * device. Later controllers reload the EEPROM automatically, so just wait - * for reload to complete. - */ - switch (hw->mac_type) { - case e1000_82542_rev2_0: - case e1000_82542_rev2_1: - case e1000_82543: - case e1000_82544: - /* Wait for reset to complete */ - udelay(10); - ctrl_ext = er32(CTRL_EXT); - ctrl_ext |= E1000_CTRL_EXT_EE_RST; - ew32(CTRL_EXT, ctrl_ext); - E1000_WRITE_FLUSH(); - /* Wait for EEPROM reload */ - msleep(2); - break; - case e1000_82541: - case e1000_82541_rev_2: - case e1000_82547: - case e1000_82547_rev_2: - /* Wait for EEPROM reload */ - msleep(20); - break; - case e1000_82573: - if (!e1000_is_onboard_nvm_eeprom(hw)) { - udelay(10); - ctrl_ext = er32(CTRL_EXT); - ctrl_ext |= E1000_CTRL_EXT_EE_RST; - ew32(CTRL_EXT, ctrl_ext); - E1000_WRITE_FLUSH(); - } - /* fall through */ - default: - /* Auto read done will delay 5ms or poll based on mac type */ - ret_val = e1000_get_auto_rd_done(hw); - if (ret_val) - return ret_val; - break; - } - - /* Disable HW ARPs on ASF enabled adapters */ - if (hw->mac_type >= e1000_82540 && hw->mac_type <= e1000_82547_rev_2) { - manc = er32(MANC); - manc &= ~(E1000_MANC_ARP_EN); - ew32(MANC, manc); - } - - if ((hw->mac_type == e1000_82541) || (hw->mac_type == e1000_82547)) { - e1000_phy_init_script(hw); - - /* Configure activity LED after PHY reset */ - led_ctrl = er32(LEDCTL); - led_ctrl &= IGP_ACTIVITY_LED_MASK; - led_ctrl |= (IGP_ACTIVITY_LED_ENABLE | IGP_LED3_MODE); - ew32(LEDCTL, led_ctrl); - } - - /* Clear interrupt mask to stop board from generating interrupts */ - DEBUGOUT("Masking off all interrupts\n"); - ew32(IMC, 0xffffffff); - - /* Clear any pending interrupt events. */ - icr = er32(ICR); - - /* If MWI was previously enabled, reenable it. */ - if (hw->mac_type == e1000_82542_rev2_0) { - if (hw->pci_cmd_word & PCI_COMMAND_INVALIDATE) - e1000_pci_set_mwi(hw); - } - - if (hw->mac_type == e1000_ich8lan) { - u32 kab = er32(KABGTXD); - kab |= E1000_KABGTXD_BGSQLBIAS; - ew32(KABGTXD, kab); - } - - return E1000_SUCCESS; -} + u32 ctrl; + u32 ctrl_ext; + u32 icr; + u32 manc; + u32 led_ctrl; + s32 ret_val; -/****************************************************************************** - * - * Initialize a number of hardware-dependent bits - * - * hw: Struct containing variables accessed by shared code - * - * This function contains hardware limitation workarounds for PCI-E adapters - * - *****************************************************************************/ -static void e1000_initialize_hardware_bits(struct e1000_hw *hw) -{ - if ((hw->mac_type >= e1000_82571) && (!hw->initialize_hw_bits_disable)) { - /* Settings common to all PCI-express silicon */ - u32 reg_ctrl, reg_ctrl_ext; - u32 reg_tarc0, reg_tarc1; - u32 reg_tctl; - u32 reg_txdctl, reg_txdctl1; - - /* link autonegotiation/sync workarounds */ - reg_tarc0 = er32(TARC0); - reg_tarc0 &= ~((1 << 30)|(1 << 29)|(1 << 28)|(1 << 27)); - - /* Enable not-done TX descriptor counting */ - reg_txdctl = er32(TXDCTL); - reg_txdctl |= E1000_TXDCTL_COUNT_DESC; - ew32(TXDCTL, reg_txdctl); - reg_txdctl1 = er32(TXDCTL1); - reg_txdctl1 |= E1000_TXDCTL_COUNT_DESC; - ew32(TXDCTL1, reg_txdctl1); - - switch (hw->mac_type) { - case e1000_82571: - case e1000_82572: - /* Clear PHY TX compatible mode bits */ - reg_tarc1 = er32(TARC1); - reg_tarc1 &= ~((1 << 30)|(1 << 29)); - - /* link autonegotiation/sync workarounds */ - reg_tarc0 |= ((1 << 26)|(1 << 25)|(1 << 24)|(1 << 23)); - - /* TX ring control fixes */ - reg_tarc1 |= ((1 << 26)|(1 << 25)|(1 << 24)); - - /* Multiple read bit is reversed polarity */ - reg_tctl = er32(TCTL); - if (reg_tctl & E1000_TCTL_MULR) - reg_tarc1 &= ~(1 << 28); - else - reg_tarc1 |= (1 << 28); - - ew32(TARC1, reg_tarc1); - break; - case e1000_82573: - reg_ctrl_ext = er32(CTRL_EXT); - reg_ctrl_ext &= ~(1 << 23); - reg_ctrl_ext |= (1 << 22); - - /* TX byte count fix */ - reg_ctrl = er32(CTRL); - reg_ctrl &= ~(1 << 29); - - ew32(CTRL_EXT, reg_ctrl_ext); - ew32(CTRL, reg_ctrl); - break; - case e1000_80003es2lan: - /* improve small packet performace for fiber/serdes */ - if ((hw->media_type == e1000_media_type_fiber) || - (hw->media_type == e1000_media_type_internal_serdes)) { - reg_tarc0 &= ~(1 << 20); - } - - /* Multiple read bit is reversed polarity */ - reg_tctl = er32(TCTL); - reg_tarc1 = er32(TARC1); - if (reg_tctl & E1000_TCTL_MULR) - reg_tarc1 &= ~(1 << 28); - else - reg_tarc1 |= (1 << 28); - - ew32(TARC1, reg_tarc1); - break; - case e1000_ich8lan: - /* Reduce concurrent DMA requests to 3 from 4 */ - if ((hw->revision_id < 3) || - ((hw->device_id != E1000_DEV_ID_ICH8_IGP_M_AMT) && - (hw->device_id != E1000_DEV_ID_ICH8_IGP_M))) - reg_tarc0 |= ((1 << 29)|(1 << 28)); - - reg_ctrl_ext = er32(CTRL_EXT); - reg_ctrl_ext |= (1 << 22); - ew32(CTRL_EXT, reg_ctrl_ext); - - /* workaround TX hang with TSO=on */ - reg_tarc0 |= ((1 << 27)|(1 << 26)|(1 << 24)|(1 << 23)); - - /* Multiple read bit is reversed polarity */ - reg_tctl = er32(TCTL); - reg_tarc1 = er32(TARC1); - if (reg_tctl & E1000_TCTL_MULR) - reg_tarc1 &= ~(1 << 28); - else - reg_tarc1 |= (1 << 28); - - /* workaround TX hang with TSO=on */ - reg_tarc1 |= ((1 << 30)|(1 << 26)|(1 << 24)); - - ew32(TARC1, reg_tarc1); - break; - default: - break; - } - - ew32(TARC0, reg_tarc0); - } + DEBUGFUNC("e1000_reset_hw"); + + /* For 82542 (rev 2.0), disable MWI before issuing a device reset */ + if (hw->mac_type == e1000_82542_rev2_0) { + DEBUGOUT("Disabling MWI on 82542 rev 2.0\n"); + e1000_pci_clear_mwi(hw); + } + + /* Clear interrupt mask to stop board from generating interrupts */ + DEBUGOUT("Masking off all interrupts\n"); + ew32(IMC, 0xffffffff); + + /* Disable the Transmit and Receive units. Then delay to allow + * any pending transactions to complete before we hit the MAC with + * the global reset. + */ + ew32(RCTL, 0); + ew32(TCTL, E1000_TCTL_PSP); + E1000_WRITE_FLUSH(); + + /* The tbi_compatibility_on Flag must be cleared when Rctl is cleared. */ + hw->tbi_compatibility_on = false; + + /* Delay to allow any outstanding PCI transactions to complete before + * resetting the device + */ + msleep(10); + + ctrl = er32(CTRL); + + /* Must reset the PHY before resetting the MAC */ + if ((hw->mac_type == e1000_82541) || (hw->mac_type == e1000_82547)) { + ew32(CTRL, (ctrl | E1000_CTRL_PHY_RST)); + msleep(5); + } + + /* Issue a global reset to the MAC. This will reset the chip's + * transmit, receive, DMA, and link units. It will not effect + * the current PCI configuration. The global reset bit is self- + * clearing, and should clear within a microsecond. + */ + DEBUGOUT("Issuing a global reset to MAC\n"); + + switch (hw->mac_type) { + case e1000_82544: + case e1000_82540: + case e1000_82545: + case e1000_82546: + case e1000_82541: + case e1000_82541_rev_2: + /* These controllers can't ack the 64-bit write when issuing the + * reset, so use IO-mapping as a workaround to issue the reset */ + E1000_WRITE_REG_IO(hw, CTRL, (ctrl | E1000_CTRL_RST)); + break; + case e1000_82545_rev_3: + case e1000_82546_rev_3: + /* Reset is performed on a shadow of the control register */ + ew32(CTRL_DUP, (ctrl | E1000_CTRL_RST)); + break; + default: + ew32(CTRL, (ctrl | E1000_CTRL_RST)); + break; + } + + /* After MAC reset, force reload of EEPROM to restore power-on settings to + * device. Later controllers reload the EEPROM automatically, so just wait + * for reload to complete. + */ + switch (hw->mac_type) { + case e1000_82542_rev2_0: + case e1000_82542_rev2_1: + case e1000_82543: + case e1000_82544: + /* Wait for reset to complete */ + udelay(10); + ctrl_ext = er32(CTRL_EXT); + ctrl_ext |= E1000_CTRL_EXT_EE_RST; + ew32(CTRL_EXT, ctrl_ext); + E1000_WRITE_FLUSH(); + /* Wait for EEPROM reload */ + msleep(2); + break; + case e1000_82541: + case e1000_82541_rev_2: + case e1000_82547: + case e1000_82547_rev_2: + /* Wait for EEPROM reload */ + msleep(20); + break; + default: + /* Auto read done will delay 5ms or poll based on mac type */ + ret_val = e1000_get_auto_rd_done(hw); + if (ret_val) + return ret_val; + break; + } + + /* Disable HW ARPs on ASF enabled adapters */ + if (hw->mac_type >= e1000_82540) { + manc = er32(MANC); + manc &= ~(E1000_MANC_ARP_EN); + ew32(MANC, manc); + } + + if ((hw->mac_type == e1000_82541) || (hw->mac_type == e1000_82547)) { + e1000_phy_init_script(hw); + + /* Configure activity LED after PHY reset */ + led_ctrl = er32(LEDCTL); + led_ctrl &= IGP_ACTIVITY_LED_MASK; + led_ctrl |= (IGP_ACTIVITY_LED_ENABLE | IGP_LED3_MODE); + ew32(LEDCTL, led_ctrl); + } + + /* Clear interrupt mask to stop board from generating interrupts */ + DEBUGOUT("Masking off all interrupts\n"); + ew32(IMC, 0xffffffff); + + /* Clear any pending interrupt events. */ + icr = er32(ICR); + + /* If MWI was previously enabled, reenable it. */ + if (hw->mac_type == e1000_82542_rev2_0) { + if (hw->pci_cmd_word & PCI_COMMAND_INVALIDATE) + e1000_pci_set_mwi(hw); + } + + return E1000_SUCCESS; } -/****************************************************************************** - * Performs basic configuration of the adapter. - * - * hw - Struct containing variables accessed by shared code +/** + * e1000_init_hw: Performs basic configuration of the adapter. + * @hw: Struct containing variables accessed by shared code * * Assumes that the controller has previously been reset and is in a * post-reset uninitialized state. Initializes the receive address registers, * multicast table, and VLAN filter table. Calls routines to setup link * configuration and flow control settings. Clears all on-chip counters. Leaves * the transmit and receive units disabled and uninitialized. - *****************************************************************************/ + */ s32 e1000_init_hw(struct e1000_hw *hw) { - u32 ctrl; - u32 i; - s32 ret_val; - u32 mta_size; - u32 reg_data; - u32 ctrl_ext; - - DEBUGFUNC("e1000_init_hw"); - - /* force full DMA clock frequency for 10/100 on ICH8 A0-B0 */ - if ((hw->mac_type == e1000_ich8lan) && - ((hw->revision_id < 3) || - ((hw->device_id != E1000_DEV_ID_ICH8_IGP_M_AMT) && - (hw->device_id != E1000_DEV_ID_ICH8_IGP_M)))) { - reg_data = er32(STATUS); - reg_data &= ~0x80000000; - ew32(STATUS, reg_data); - } - - /* Initialize Identification LED */ - ret_val = e1000_id_led_init(hw); - if (ret_val) { - DEBUGOUT("Error Initializing Identification LED\n"); - return ret_val; - } - - /* Set the media type and TBI compatibility */ - e1000_set_media_type(hw); - - /* Must be called after e1000_set_media_type because media_type is used */ - e1000_initialize_hardware_bits(hw); - - /* Disabling VLAN filtering. */ - DEBUGOUT("Initializing the IEEE VLAN\n"); - /* VET hardcoded to standard value and VFTA removed in ICH8 LAN */ - if (hw->mac_type != e1000_ich8lan) { - if (hw->mac_type < e1000_82545_rev_3) - ew32(VET, 0); - e1000_clear_vfta(hw); - } - - /* For 82542 (rev 2.0), disable MWI and put the receiver into reset */ - if (hw->mac_type == e1000_82542_rev2_0) { - DEBUGOUT("Disabling MWI on 82542 rev 2.0\n"); - e1000_pci_clear_mwi(hw); - ew32(RCTL, E1000_RCTL_RST); - E1000_WRITE_FLUSH(); - msleep(5); - } - - /* Setup the receive address. This involves initializing all of the Receive - * Address Registers (RARs 0 - 15). - */ - e1000_init_rx_addrs(hw); - - /* For 82542 (rev 2.0), take the receiver out of reset and enable MWI */ - if (hw->mac_type == e1000_82542_rev2_0) { - ew32(RCTL, 0); - E1000_WRITE_FLUSH(); - msleep(1); - if (hw->pci_cmd_word & PCI_COMMAND_INVALIDATE) - e1000_pci_set_mwi(hw); - } - - /* Zero out the Multicast HASH table */ - DEBUGOUT("Zeroing the MTA\n"); - mta_size = E1000_MC_TBL_SIZE; - if (hw->mac_type == e1000_ich8lan) - mta_size = E1000_MC_TBL_SIZE_ICH8LAN; - for (i = 0; i < mta_size; i++) { - E1000_WRITE_REG_ARRAY(hw, MTA, i, 0); - /* use write flush to prevent Memory Write Block (MWB) from - * occuring when accessing our register space */ - E1000_WRITE_FLUSH(); - } - - /* Set the PCI priority bit correctly in the CTRL register. This - * determines if the adapter gives priority to receives, or if it - * gives equal priority to transmits and receives. Valid only on - * 82542 and 82543 silicon. - */ - if (hw->dma_fairness && hw->mac_type <= e1000_82543) { - ctrl = er32(CTRL); - ew32(CTRL, ctrl | E1000_CTRL_PRIOR); - } - - switch (hw->mac_type) { - case e1000_82545_rev_3: - case e1000_82546_rev_3: - break; - default: - /* Workaround for PCI-X problem when BIOS sets MMRBC incorrectly. */ - if (hw->bus_type == e1000_bus_type_pcix && e1000_pcix_get_mmrbc(hw) > 2048) - e1000_pcix_set_mmrbc(hw, 2048); - break; - } - - /* More time needed for PHY to initialize */ - if (hw->mac_type == e1000_ich8lan) - msleep(15); - - /* Call a subroutine to configure the link and setup flow control. */ - ret_val = e1000_setup_link(hw); - - /* Set the transmit descriptor write-back policy */ - if (hw->mac_type > e1000_82544) { - ctrl = er32(TXDCTL); - ctrl = (ctrl & ~E1000_TXDCTL_WTHRESH) | E1000_TXDCTL_FULL_TX_DESC_WB; - ew32(TXDCTL, ctrl); - } - - if (hw->mac_type == e1000_82573) { - e1000_enable_tx_pkt_filtering(hw); - } - - switch (hw->mac_type) { - default: - break; - case e1000_80003es2lan: - /* Enable retransmit on late collisions */ - reg_data = er32(TCTL); - reg_data |= E1000_TCTL_RTLC; - ew32(TCTL, reg_data); - - /* Configure Gigabit Carry Extend Padding */ - reg_data = er32(TCTL_EXT); - reg_data &= ~E1000_TCTL_EXT_GCEX_MASK; - reg_data |= DEFAULT_80003ES2LAN_TCTL_EXT_GCEX; - ew32(TCTL_EXT, reg_data); - - /* Configure Transmit Inter-Packet Gap */ - reg_data = er32(TIPG); - reg_data &= ~E1000_TIPG_IPGT_MASK; - reg_data |= DEFAULT_80003ES2LAN_TIPG_IPGT_1000; - ew32(TIPG, reg_data); - - reg_data = E1000_READ_REG_ARRAY(hw, FFLT, 0x0001); - reg_data &= ~0x00100000; - E1000_WRITE_REG_ARRAY(hw, FFLT, 0x0001, reg_data); - /* Fall through */ - case e1000_82571: - case e1000_82572: - case e1000_ich8lan: - ctrl = er32(TXDCTL1); - ctrl = (ctrl & ~E1000_TXDCTL_WTHRESH) | E1000_TXDCTL_FULL_TX_DESC_WB; - ew32(TXDCTL1, ctrl); - break; - } - - - if (hw->mac_type == e1000_82573) { - u32 gcr = er32(GCR); - gcr |= E1000_GCR_L1_ACT_WITHOUT_L0S_RX; - ew32(GCR, gcr); - } - - /* Clear all of the statistics registers (clear on read). It is - * important that we do this after we have tried to establish link - * because the symbol error count will increment wildly if there - * is no link. - */ - e1000_clear_hw_cntrs(hw); - - /* ICH8 No-snoop bits are opposite polarity. - * Set to snoop by default after reset. */ - if (hw->mac_type == e1000_ich8lan) - e1000_set_pci_ex_no_snoop(hw, PCI_EX_82566_SNOOP_ALL); - - if (hw->device_id == E1000_DEV_ID_82546GB_QUAD_COPPER || - hw->device_id == E1000_DEV_ID_82546GB_QUAD_COPPER_KSP3) { - ctrl_ext = er32(CTRL_EXT); - /* Relaxed ordering must be disabled to avoid a parity - * error crash in a PCI slot. */ - ctrl_ext |= E1000_CTRL_EXT_RO_DIS; - ew32(CTRL_EXT, ctrl_ext); - } - - return ret_val; + u32 ctrl; + u32 i; + s32 ret_val; + u32 mta_size; + u32 ctrl_ext; + + DEBUGFUNC("e1000_init_hw"); + + /* Initialize Identification LED */ + ret_val = e1000_id_led_init(hw); + if (ret_val) { + DEBUGOUT("Error Initializing Identification LED\n"); + return ret_val; + } + + /* Set the media type and TBI compatibility */ + e1000_set_media_type(hw); + + /* Disabling VLAN filtering. */ + DEBUGOUT("Initializing the IEEE VLAN\n"); + if (hw->mac_type < e1000_82545_rev_3) + ew32(VET, 0); + e1000_clear_vfta(hw); + + /* For 82542 (rev 2.0), disable MWI and put the receiver into reset */ + if (hw->mac_type == e1000_82542_rev2_0) { + DEBUGOUT("Disabling MWI on 82542 rev 2.0\n"); + e1000_pci_clear_mwi(hw); + ew32(RCTL, E1000_RCTL_RST); + E1000_WRITE_FLUSH(); + msleep(5); + } + + /* Setup the receive address. This involves initializing all of the Receive + * Address Registers (RARs 0 - 15). + */ + e1000_init_rx_addrs(hw); + + /* For 82542 (rev 2.0), take the receiver out of reset and enable MWI */ + if (hw->mac_type == e1000_82542_rev2_0) { + ew32(RCTL, 0); + E1000_WRITE_FLUSH(); + msleep(1); + if (hw->pci_cmd_word & PCI_COMMAND_INVALIDATE) + e1000_pci_set_mwi(hw); + } + + /* Zero out the Multicast HASH table */ + DEBUGOUT("Zeroing the MTA\n"); + mta_size = E1000_MC_TBL_SIZE; + for (i = 0; i < mta_size; i++) { + E1000_WRITE_REG_ARRAY(hw, MTA, i, 0); + /* use write flush to prevent Memory Write Block (MWB) from + * occurring when accessing our register space */ + E1000_WRITE_FLUSH(); + } + + /* Set the PCI priority bit correctly in the CTRL register. This + * determines if the adapter gives priority to receives, or if it + * gives equal priority to transmits and receives. Valid only on + * 82542 and 82543 silicon. + */ + if (hw->dma_fairness && hw->mac_type <= e1000_82543) { + ctrl = er32(CTRL); + ew32(CTRL, ctrl | E1000_CTRL_PRIOR); + } + + switch (hw->mac_type) { + case e1000_82545_rev_3: + case e1000_82546_rev_3: + break; + default: + /* Workaround for PCI-X problem when BIOS sets MMRBC incorrectly. */ + if (hw->bus_type == e1000_bus_type_pcix + && e1000_pcix_get_mmrbc(hw) > 2048) + e1000_pcix_set_mmrbc(hw, 2048); + break; + } + + /* Call a subroutine to configure the link and setup flow control. */ + ret_val = e1000_setup_link(hw); + + /* Set the transmit descriptor write-back policy */ + if (hw->mac_type > e1000_82544) { + ctrl = er32(TXDCTL); + ctrl = + (ctrl & ~E1000_TXDCTL_WTHRESH) | + E1000_TXDCTL_FULL_TX_DESC_WB; + ew32(TXDCTL, ctrl); + } + + /* Clear all of the statistics registers (clear on read). It is + * important that we do this after we have tried to establish link + * because the symbol error count will increment wildly if there + * is no link. + */ + e1000_clear_hw_cntrs(hw); + + if (hw->device_id == E1000_DEV_ID_82546GB_QUAD_COPPER || + hw->device_id == E1000_DEV_ID_82546GB_QUAD_COPPER_KSP3) { + ctrl_ext = er32(CTRL_EXT); + /* Relaxed ordering must be disabled to avoid a parity + * error crash in a PCI slot. */ + ctrl_ext |= E1000_CTRL_EXT_RO_DIS; + ew32(CTRL_EXT, ctrl_ext); + } + + return ret_val; } -/****************************************************************************** - * Adjust SERDES output amplitude based on EEPROM setting. - * - * hw - Struct containing variables accessed by shared code. - *****************************************************************************/ +/** + * e1000_adjust_serdes_amplitude - Adjust SERDES output amplitude based on EEPROM setting. + * @hw: Struct containing variables accessed by shared code. + */ static s32 e1000_adjust_serdes_amplitude(struct e1000_hw *hw) { - u16 eeprom_data; - s32 ret_val; - - DEBUGFUNC("e1000_adjust_serdes_amplitude"); - - if (hw->media_type != e1000_media_type_internal_serdes) - return E1000_SUCCESS; - - switch (hw->mac_type) { - case e1000_82545_rev_3: - case e1000_82546_rev_3: - break; - default: - return E1000_SUCCESS; - } - - ret_val = e1000_read_eeprom(hw, EEPROM_SERDES_AMPLITUDE, 1, &eeprom_data); - if (ret_val) { - return ret_val; - } - - if (eeprom_data != EEPROM_RESERVED_WORD) { - /* Adjust SERDES output amplitude only. */ - eeprom_data &= EEPROM_SERDES_AMPLITUDE_MASK; - ret_val = e1000_write_phy_reg(hw, M88E1000_PHY_EXT_CTRL, eeprom_data); - if (ret_val) - return ret_val; - } - - return E1000_SUCCESS; + u16 eeprom_data; + s32 ret_val; + + DEBUGFUNC("e1000_adjust_serdes_amplitude"); + + if (hw->media_type != e1000_media_type_internal_serdes) + return E1000_SUCCESS; + + switch (hw->mac_type) { + case e1000_82545_rev_3: + case e1000_82546_rev_3: + break; + default: + return E1000_SUCCESS; + } + + ret_val = e1000_read_eeprom(hw, EEPROM_SERDES_AMPLITUDE, 1, + &eeprom_data); + if (ret_val) { + return ret_val; + } + + if (eeprom_data != EEPROM_RESERVED_WORD) { + /* Adjust SERDES output amplitude only. */ + eeprom_data &= EEPROM_SERDES_AMPLITUDE_MASK; + ret_val = + e1000_write_phy_reg(hw, M88E1000_PHY_EXT_CTRL, eeprom_data); + if (ret_val) + return ret_val; + } + + return E1000_SUCCESS; } -/****************************************************************************** - * Configures flow control and link settings. - * - * hw - Struct containing variables accessed by shared code +/** + * e1000_setup_link - Configures flow control and link settings. + * @hw: Struct containing variables accessed by shared code * - * Determines which flow control settings to use. Calls the apropriate media- + * Determines which flow control settings to use. Calls the appropriate media- * specific link configuration function. Configures the flow control settings. * Assuming the adapter has a valid link partner, a valid link should be * established. Assumes the hardware has previously been reset and the * transmitter and receiver are not enabled. - *****************************************************************************/ + */ s32 e1000_setup_link(struct e1000_hw *hw) { - u32 ctrl_ext; - s32 ret_val; - u16 eeprom_data; - - DEBUGFUNC("e1000_setup_link"); - - /* In the case of the phy reset being blocked, we already have a link. - * We do not have to set it up again. */ - if (e1000_check_phy_reset_block(hw)) - return E1000_SUCCESS; - - /* Read and store word 0x0F of the EEPROM. This word contains bits - * that determine the hardware's default PAUSE (flow control) mode, - * a bit that determines whether the HW defaults to enabling or - * disabling auto-negotiation, and the direction of the - * SW defined pins. If there is no SW over-ride of the flow - * control setting, then the variable hw->fc will - * be initialized based on a value in the EEPROM. - */ - if (hw->fc == E1000_FC_DEFAULT) { - switch (hw->mac_type) { - case e1000_ich8lan: - case e1000_82573: - hw->fc = E1000_FC_FULL; - break; - default: - ret_val = e1000_read_eeprom(hw, EEPROM_INIT_CONTROL2_REG, - 1, &eeprom_data); - if (ret_val) { - DEBUGOUT("EEPROM Read Error\n"); - return -E1000_ERR_EEPROM; - } - if ((eeprom_data & EEPROM_WORD0F_PAUSE_MASK) == 0) - hw->fc = E1000_FC_NONE; - else if ((eeprom_data & EEPROM_WORD0F_PAUSE_MASK) == - EEPROM_WORD0F_ASM_DIR) - hw->fc = E1000_FC_TX_PAUSE; - else - hw->fc = E1000_FC_FULL; - break; - } - } - - /* We want to save off the original Flow Control configuration just - * in case we get disconnected and then reconnected into a different - * hub or switch with different Flow Control capabilities. - */ - if (hw->mac_type == e1000_82542_rev2_0) - hw->fc &= (~E1000_FC_TX_PAUSE); - - if ((hw->mac_type < e1000_82543) && (hw->report_tx_early == 1)) - hw->fc &= (~E1000_FC_RX_PAUSE); - - hw->original_fc = hw->fc; - - DEBUGOUT1("After fix-ups FlowControl is now = %x\n", hw->fc); - - /* Take the 4 bits from EEPROM word 0x0F that determine the initial - * polarity value for the SW controlled pins, and setup the - * Extended Device Control reg with that info. - * This is needed because one of the SW controlled pins is used for - * signal detection. So this should be done before e1000_setup_pcs_link() - * or e1000_phy_setup() is called. - */ - if (hw->mac_type == e1000_82543) { - ret_val = e1000_read_eeprom(hw, EEPROM_INIT_CONTROL2_REG, - 1, &eeprom_data); - if (ret_val) { - DEBUGOUT("EEPROM Read Error\n"); - return -E1000_ERR_EEPROM; - } - ctrl_ext = ((eeprom_data & EEPROM_WORD0F_SWPDIO_EXT) << - SWDPIO__EXT_SHIFT); - ew32(CTRL_EXT, ctrl_ext); - } - - /* Call the necessary subroutine to configure the link. */ - ret_val = (hw->media_type == e1000_media_type_copper) ? - e1000_setup_copper_link(hw) : - e1000_setup_fiber_serdes_link(hw); - - /* Initialize the flow control address, type, and PAUSE timer - * registers to their default values. This is done even if flow - * control is disabled, because it does not hurt anything to - * initialize these registers. - */ - DEBUGOUT("Initializing the Flow Control address, type and timer regs\n"); - - /* FCAL/H and FCT are hardcoded to standard values in e1000_ich8lan. */ - if (hw->mac_type != e1000_ich8lan) { - ew32(FCT, FLOW_CONTROL_TYPE); - ew32(FCAH, FLOW_CONTROL_ADDRESS_HIGH); - ew32(FCAL, FLOW_CONTROL_ADDRESS_LOW); - } - - ew32(FCTTV, hw->fc_pause_time); - - /* Set the flow control receive threshold registers. Normally, - * these registers will be set to a default threshold that may be - * adjusted later by the driver's runtime code. However, if the - * ability to transmit pause frames in not enabled, then these - * registers will be set to 0. - */ - if (!(hw->fc & E1000_FC_TX_PAUSE)) { - ew32(FCRTL, 0); - ew32(FCRTH, 0); - } else { - /* We need to set up the Receive Threshold high and low water marks - * as well as (optionally) enabling the transmission of XON frames. - */ - if (hw->fc_send_xon) { - ew32(FCRTL, (hw->fc_low_water | E1000_FCRTL_XONE)); - ew32(FCRTH, hw->fc_high_water); - } else { - ew32(FCRTL, hw->fc_low_water); - ew32(FCRTH, hw->fc_high_water); - } - } - return ret_val; + u32 ctrl_ext; + s32 ret_val; + u16 eeprom_data; + + DEBUGFUNC("e1000_setup_link"); + + /* Read and store word 0x0F of the EEPROM. This word contains bits + * that determine the hardware's default PAUSE (flow control) mode, + * a bit that determines whether the HW defaults to enabling or + * disabling auto-negotiation, and the direction of the + * SW defined pins. If there is no SW over-ride of the flow + * control setting, then the variable hw->fc will + * be initialized based on a value in the EEPROM. + */ + if (hw->fc == E1000_FC_DEFAULT) { + ret_val = e1000_read_eeprom(hw, EEPROM_INIT_CONTROL2_REG, + 1, &eeprom_data); + if (ret_val) { + DEBUGOUT("EEPROM Read Error\n"); + return -E1000_ERR_EEPROM; + } + if ((eeprom_data & EEPROM_WORD0F_PAUSE_MASK) == 0) + hw->fc = E1000_FC_NONE; + else if ((eeprom_data & EEPROM_WORD0F_PAUSE_MASK) == + EEPROM_WORD0F_ASM_DIR) + hw->fc = E1000_FC_TX_PAUSE; + else + hw->fc = E1000_FC_FULL; + } + + /* We want to save off the original Flow Control configuration just + * in case we get disconnected and then reconnected into a different + * hub or switch with different Flow Control capabilities. + */ + if (hw->mac_type == e1000_82542_rev2_0) + hw->fc &= (~E1000_FC_TX_PAUSE); + + if ((hw->mac_type < e1000_82543) && (hw->report_tx_early == 1)) + hw->fc &= (~E1000_FC_RX_PAUSE); + + hw->original_fc = hw->fc; + + DEBUGOUT1("After fix-ups FlowControl is now = %x\n", hw->fc); + + /* Take the 4 bits from EEPROM word 0x0F that determine the initial + * polarity value for the SW controlled pins, and setup the + * Extended Device Control reg with that info. + * This is needed because one of the SW controlled pins is used for + * signal detection. So this should be done before e1000_setup_pcs_link() + * or e1000_phy_setup() is called. + */ + if (hw->mac_type == e1000_82543) { + ret_val = e1000_read_eeprom(hw, EEPROM_INIT_CONTROL2_REG, + 1, &eeprom_data); + if (ret_val) { + DEBUGOUT("EEPROM Read Error\n"); + return -E1000_ERR_EEPROM; + } + ctrl_ext = ((eeprom_data & EEPROM_WORD0F_SWPDIO_EXT) << + SWDPIO__EXT_SHIFT); + ew32(CTRL_EXT, ctrl_ext); + } + + /* Call the necessary subroutine to configure the link. */ + ret_val = (hw->media_type == e1000_media_type_copper) ? + e1000_setup_copper_link(hw) : e1000_setup_fiber_serdes_link(hw); + + /* Initialize the flow control address, type, and PAUSE timer + * registers to their default values. This is done even if flow + * control is disabled, because it does not hurt anything to + * initialize these registers. + */ + DEBUGOUT + ("Initializing the Flow Control address, type and timer regs\n"); + + ew32(FCT, FLOW_CONTROL_TYPE); + ew32(FCAH, FLOW_CONTROL_ADDRESS_HIGH); + ew32(FCAL, FLOW_CONTROL_ADDRESS_LOW); + + ew32(FCTTV, hw->fc_pause_time); + + /* Set the flow control receive threshold registers. Normally, + * these registers will be set to a default threshold that may be + * adjusted later by the driver's runtime code. However, if the + * ability to transmit pause frames in not enabled, then these + * registers will be set to 0. + */ + if (!(hw->fc & E1000_FC_TX_PAUSE)) { + ew32(FCRTL, 0); + ew32(FCRTH, 0); + } else { + /* We need to set up the Receive Threshold high and low water marks + * as well as (optionally) enabling the transmission of XON frames. + */ + if (hw->fc_send_xon) { + ew32(FCRTL, (hw->fc_low_water | E1000_FCRTL_XONE)); + ew32(FCRTH, hw->fc_high_water); + } else { + ew32(FCRTL, hw->fc_low_water); + ew32(FCRTH, hw->fc_high_water); + } + } + return ret_val; } -/****************************************************************************** - * Sets up link for a fiber based or serdes based adapter - * - * hw - Struct containing variables accessed by shared code +/** + * e1000_setup_fiber_serdes_link - prepare fiber or serdes link + * @hw: Struct containing variables accessed by shared code * * Manipulates Physical Coding Sublayer functions in order to configure * link. Assumes the hardware has been previously reset and the transmitter * and receiver are not enabled. - *****************************************************************************/ + */ static s32 e1000_setup_fiber_serdes_link(struct e1000_hw *hw) { - u32 ctrl; - u32 status; - u32 txcw = 0; - u32 i; - u32 signal = 0; - s32 ret_val; - - DEBUGFUNC("e1000_setup_fiber_serdes_link"); - - /* On 82571 and 82572 Fiber connections, SerDes loopback mode persists - * until explicitly turned off or a power cycle is performed. A read to - * the register does not indicate its status. Therefore, we ensure - * loopback mode is disabled during initialization. - */ - if (hw->mac_type == e1000_82571 || hw->mac_type == e1000_82572) - ew32(SCTL, E1000_DISABLE_SERDES_LOOPBACK); - - /* On adapters with a MAC newer than 82544, SWDP 1 will be - * set when the optics detect a signal. On older adapters, it will be - * cleared when there is a signal. This applies to fiber media only. - * If we're on serdes media, adjust the output amplitude to value - * set in the EEPROM. - */ - ctrl = er32(CTRL); - if (hw->media_type == e1000_media_type_fiber) - signal = (hw->mac_type > e1000_82544) ? E1000_CTRL_SWDPIN1 : 0; - - ret_val = e1000_adjust_serdes_amplitude(hw); - if (ret_val) - return ret_val; - - /* Take the link out of reset */ - ctrl &= ~(E1000_CTRL_LRST); - - /* Adjust VCO speed to improve BER performance */ - ret_val = e1000_set_vco_speed(hw); - if (ret_val) - return ret_val; - - e1000_config_collision_dist(hw); - - /* Check for a software override of the flow control settings, and setup - * the device accordingly. If auto-negotiation is enabled, then software - * will have to set the "PAUSE" bits to the correct value in the Tranmsit - * Config Word Register (TXCW) and re-start auto-negotiation. However, if - * auto-negotiation is disabled, then software will have to manually - * configure the two flow control enable bits in the CTRL register. - * - * The possible values of the "fc" parameter are: - * 0: Flow control is completely disabled - * 1: Rx flow control is enabled (we can receive pause frames, but - * not send pause frames). - * 2: Tx flow control is enabled (we can send pause frames but we do - * not support receiving pause frames). - * 3: Both Rx and TX flow control (symmetric) are enabled. - */ - switch (hw->fc) { - case E1000_FC_NONE: - /* Flow control is completely disabled by a software over-ride. */ - txcw = (E1000_TXCW_ANE | E1000_TXCW_FD); - break; - case E1000_FC_RX_PAUSE: - /* RX Flow control is enabled and TX Flow control is disabled by a - * software over-ride. Since there really isn't a way to advertise - * that we are capable of RX Pause ONLY, we will advertise that we - * support both symmetric and asymmetric RX PAUSE. Later, we will - * disable the adapter's ability to send PAUSE frames. - */ - txcw = (E1000_TXCW_ANE | E1000_TXCW_FD | E1000_TXCW_PAUSE_MASK); - break; - case E1000_FC_TX_PAUSE: - /* TX Flow control is enabled, and RX Flow control is disabled, by a - * software over-ride. - */ - txcw = (E1000_TXCW_ANE | E1000_TXCW_FD | E1000_TXCW_ASM_DIR); - break; - case E1000_FC_FULL: - /* Flow control (both RX and TX) is enabled by a software over-ride. */ - txcw = (E1000_TXCW_ANE | E1000_TXCW_FD | E1000_TXCW_PAUSE_MASK); - break; - default: - DEBUGOUT("Flow control param set incorrectly\n"); - return -E1000_ERR_CONFIG; - break; - } - - /* Since auto-negotiation is enabled, take the link out of reset (the link - * will be in reset, because we previously reset the chip). This will - * restart auto-negotiation. If auto-neogtiation is successful then the - * link-up status bit will be set and the flow control enable bits (RFCE - * and TFCE) will be set according to their negotiated value. - */ - DEBUGOUT("Auto-negotiation enabled\n"); - - ew32(TXCW, txcw); - ew32(CTRL, ctrl); - E1000_WRITE_FLUSH(); - - hw->txcw = txcw; - msleep(1); - - /* If we have a signal (the cable is plugged in) then poll for a "Link-Up" - * indication in the Device Status Register. Time-out if a link isn't - * seen in 500 milliseconds seconds (Auto-negotiation should complete in - * less than 500 milliseconds even if the other end is doing it in SW). - * For internal serdes, we just assume a signal is present, then poll. - */ - if (hw->media_type == e1000_media_type_internal_serdes || - (er32(CTRL) & E1000_CTRL_SWDPIN1) == signal) { - DEBUGOUT("Looking for Link\n"); - for (i = 0; i < (LINK_UP_TIMEOUT / 10); i++) { - msleep(10); - status = er32(STATUS); - if (status & E1000_STATUS_LU) break; - } - if (i == (LINK_UP_TIMEOUT / 10)) { - DEBUGOUT("Never got a valid link from auto-neg!!!\n"); - hw->autoneg_failed = 1; - /* AutoNeg failed to achieve a link, so we'll call - * e1000_check_for_link. This routine will force the link up if - * we detect a signal. This will allow us to communicate with - * non-autonegotiating link partners. - */ - ret_val = e1000_check_for_link(hw); - if (ret_val) { - DEBUGOUT("Error while checking for link\n"); - return ret_val; - } - hw->autoneg_failed = 0; - } else { - hw->autoneg_failed = 0; - DEBUGOUT("Valid Link Found\n"); - } - } else { - DEBUGOUT("No Signal Detected\n"); - } - return E1000_SUCCESS; + u32 ctrl; + u32 status; + u32 txcw = 0; + u32 i; + u32 signal = 0; + s32 ret_val; + + DEBUGFUNC("e1000_setup_fiber_serdes_link"); + + /* On adapters with a MAC newer than 82544, SWDP 1 will be + * set when the optics detect a signal. On older adapters, it will be + * cleared when there is a signal. This applies to fiber media only. + * If we're on serdes media, adjust the output amplitude to value + * set in the EEPROM. + */ + ctrl = er32(CTRL); + if (hw->media_type == e1000_media_type_fiber) + signal = (hw->mac_type > e1000_82544) ? E1000_CTRL_SWDPIN1 : 0; + + ret_val = e1000_adjust_serdes_amplitude(hw); + if (ret_val) + return ret_val; + + /* Take the link out of reset */ + ctrl &= ~(E1000_CTRL_LRST); + + /* Adjust VCO speed to improve BER performance */ + ret_val = e1000_set_vco_speed(hw); + if (ret_val) + return ret_val; + + e1000_config_collision_dist(hw); + + /* Check for a software override of the flow control settings, and setup + * the device accordingly. If auto-negotiation is enabled, then software + * will have to set the "PAUSE" bits to the correct value in the Tranmsit + * Config Word Register (TXCW) and re-start auto-negotiation. However, if + * auto-negotiation is disabled, then software will have to manually + * configure the two flow control enable bits in the CTRL register. + * + * The possible values of the "fc" parameter are: + * 0: Flow control is completely disabled + * 1: Rx flow control is enabled (we can receive pause frames, but + * not send pause frames). + * 2: Tx flow control is enabled (we can send pause frames but we do + * not support receiving pause frames). + * 3: Both Rx and TX flow control (symmetric) are enabled. + */ + switch (hw->fc) { + case E1000_FC_NONE: + /* Flow control is completely disabled by a software over-ride. */ + txcw = (E1000_TXCW_ANE | E1000_TXCW_FD); + break; + case E1000_FC_RX_PAUSE: + /* RX Flow control is enabled and TX Flow control is disabled by a + * software over-ride. Since there really isn't a way to advertise + * that we are capable of RX Pause ONLY, we will advertise that we + * support both symmetric and asymmetric RX PAUSE. Later, we will + * disable the adapter's ability to send PAUSE frames. + */ + txcw = (E1000_TXCW_ANE | E1000_TXCW_FD | E1000_TXCW_PAUSE_MASK); + break; + case E1000_FC_TX_PAUSE: + /* TX Flow control is enabled, and RX Flow control is disabled, by a + * software over-ride. + */ + txcw = (E1000_TXCW_ANE | E1000_TXCW_FD | E1000_TXCW_ASM_DIR); + break; + case E1000_FC_FULL: + /* Flow control (both RX and TX) is enabled by a software over-ride. */ + txcw = (E1000_TXCW_ANE | E1000_TXCW_FD | E1000_TXCW_PAUSE_MASK); + break; + default: + DEBUGOUT("Flow control param set incorrectly\n"); + return -E1000_ERR_CONFIG; + break; + } + + /* Since auto-negotiation is enabled, take the link out of reset (the link + * will be in reset, because we previously reset the chip). This will + * restart auto-negotiation. If auto-negotiation is successful then the + * link-up status bit will be set and the flow control enable bits (RFCE + * and TFCE) will be set according to their negotiated value. + */ + DEBUGOUT("Auto-negotiation enabled\n"); + + ew32(TXCW, txcw); + ew32(CTRL, ctrl); + E1000_WRITE_FLUSH(); + + hw->txcw = txcw; + msleep(1); + + /* If we have a signal (the cable is plugged in) then poll for a "Link-Up" + * indication in the Device Status Register. Time-out if a link isn't + * seen in 500 milliseconds seconds (Auto-negotiation should complete in + * less than 500 milliseconds even if the other end is doing it in SW). + * For internal serdes, we just assume a signal is present, then poll. + */ + if (hw->media_type == e1000_media_type_internal_serdes || + (er32(CTRL) & E1000_CTRL_SWDPIN1) == signal) { + DEBUGOUT("Looking for Link\n"); + for (i = 0; i < (LINK_UP_TIMEOUT / 10); i++) { + msleep(10); + status = er32(STATUS); + if (status & E1000_STATUS_LU) + break; + } + if (i == (LINK_UP_TIMEOUT / 10)) { + DEBUGOUT("Never got a valid link from auto-neg!!!\n"); + hw->autoneg_failed = 1; + /* AutoNeg failed to achieve a link, so we'll call + * e1000_check_for_link. This routine will force the link up if + * we detect a signal. This will allow us to communicate with + * non-autonegotiating link partners. + */ + ret_val = e1000_check_for_link(hw); + if (ret_val) { + DEBUGOUT("Error while checking for link\n"); + return ret_val; + } + hw->autoneg_failed = 0; + } else { + hw->autoneg_failed = 0; + DEBUGOUT("Valid Link Found\n"); + } + } else { + DEBUGOUT("No Signal Detected\n"); + } + return E1000_SUCCESS; } -/****************************************************************************** -* Make sure we have a valid PHY and change PHY mode before link setup. -* -* hw - Struct containing variables accessed by shared code -******************************************************************************/ +/** + * e1000_copper_link_preconfig - early configuration for copper + * @hw: Struct containing variables accessed by shared code + * + * Make sure we have a valid PHY and change PHY mode before link setup. + */ static s32 e1000_copper_link_preconfig(struct e1000_hw *hw) { - u32 ctrl; - s32 ret_val; - u16 phy_data; - - DEBUGFUNC("e1000_copper_link_preconfig"); - - ctrl = er32(CTRL); - /* With 82543, we need to force speed and duplex on the MAC equal to what - * the PHY speed and duplex configuration is. In addition, we need to - * perform a hardware reset on the PHY to take it out of reset. - */ - if (hw->mac_type > e1000_82543) { - ctrl |= E1000_CTRL_SLU; - ctrl &= ~(E1000_CTRL_FRCSPD | E1000_CTRL_FRCDPX); - ew32(CTRL, ctrl); - } else { - ctrl |= (E1000_CTRL_FRCSPD | E1000_CTRL_FRCDPX | E1000_CTRL_SLU); - ew32(CTRL, ctrl); - ret_val = e1000_phy_hw_reset(hw); - if (ret_val) - return ret_val; - } - - /* Make sure we have a valid PHY */ - ret_val = e1000_detect_gig_phy(hw); - if (ret_val) { - DEBUGOUT("Error, did not detect valid phy.\n"); - return ret_val; - } - DEBUGOUT1("Phy ID = %x \n", hw->phy_id); - - /* Set PHY to class A mode (if necessary) */ - ret_val = e1000_set_phy_mode(hw); - if (ret_val) - return ret_val; - - if ((hw->mac_type == e1000_82545_rev_3) || - (hw->mac_type == e1000_82546_rev_3)) { - ret_val = e1000_read_phy_reg(hw, M88E1000_PHY_SPEC_CTRL, &phy_data); - phy_data |= 0x00000008; - ret_val = e1000_write_phy_reg(hw, M88E1000_PHY_SPEC_CTRL, phy_data); - } - - if (hw->mac_type <= e1000_82543 || - hw->mac_type == e1000_82541 || hw->mac_type == e1000_82547 || - hw->mac_type == e1000_82541_rev_2 || hw->mac_type == e1000_82547_rev_2) - hw->phy_reset_disable = false; - - return E1000_SUCCESS; -} + u32 ctrl; + s32 ret_val; + u16 phy_data; + DEBUGFUNC("e1000_copper_link_preconfig"); -/******************************************************************** -* Copper link setup for e1000_phy_igp series. -* -* hw - Struct containing variables accessed by shared code -*********************************************************************/ -static s32 e1000_copper_link_igp_setup(struct e1000_hw *hw) -{ - u32 led_ctrl; - s32 ret_val; - u16 phy_data; - - DEBUGFUNC("e1000_copper_link_igp_setup"); - - if (hw->phy_reset_disable) - return E1000_SUCCESS; - - ret_val = e1000_phy_reset(hw); - if (ret_val) { - DEBUGOUT("Error Resetting the PHY\n"); - return ret_val; - } - - /* Wait 15ms for MAC to configure PHY from eeprom settings */ - msleep(15); - if (hw->mac_type != e1000_ich8lan) { - /* Configure activity LED after PHY reset */ - led_ctrl = er32(LEDCTL); - led_ctrl &= IGP_ACTIVITY_LED_MASK; - led_ctrl |= (IGP_ACTIVITY_LED_ENABLE | IGP_LED3_MODE); - ew32(LEDCTL, led_ctrl); - } - - /* The NVM settings will configure LPLU in D3 for IGP2 and IGP3 PHYs */ - if (hw->phy_type == e1000_phy_igp) { - /* disable lplu d3 during driver init */ - ret_val = e1000_set_d3_lplu_state(hw, false); - if (ret_val) { - DEBUGOUT("Error Disabling LPLU D3\n"); - return ret_val; - } - } - - /* disable lplu d0 during driver init */ - ret_val = e1000_set_d0_lplu_state(hw, false); - if (ret_val) { - DEBUGOUT("Error Disabling LPLU D0\n"); - return ret_val; - } - /* Configure mdi-mdix settings */ - ret_val = e1000_read_phy_reg(hw, IGP01E1000_PHY_PORT_CTRL, &phy_data); - if (ret_val) - return ret_val; - - if ((hw->mac_type == e1000_82541) || (hw->mac_type == e1000_82547)) { - hw->dsp_config_state = e1000_dsp_config_disabled; - /* Force MDI for earlier revs of the IGP PHY */ - phy_data &= ~(IGP01E1000_PSCR_AUTO_MDIX | IGP01E1000_PSCR_FORCE_MDI_MDIX); - hw->mdix = 1; - - } else { - hw->dsp_config_state = e1000_dsp_config_enabled; - phy_data &= ~IGP01E1000_PSCR_AUTO_MDIX; - - switch (hw->mdix) { - case 1: - phy_data &= ~IGP01E1000_PSCR_FORCE_MDI_MDIX; - break; - case 2: - phy_data |= IGP01E1000_PSCR_FORCE_MDI_MDIX; - break; - case 0: - default: - phy_data |= IGP01E1000_PSCR_AUTO_MDIX; - break; - } - } - ret_val = e1000_write_phy_reg(hw, IGP01E1000_PHY_PORT_CTRL, phy_data); - if (ret_val) - return ret_val; - - /* set auto-master slave resolution settings */ - if (hw->autoneg) { - e1000_ms_type phy_ms_setting = hw->master_slave; - - if (hw->ffe_config_state == e1000_ffe_config_active) - hw->ffe_config_state = e1000_ffe_config_enabled; - - if (hw->dsp_config_state == e1000_dsp_config_activated) - hw->dsp_config_state = e1000_dsp_config_enabled; - - /* when autonegotiation advertisment is only 1000Mbps then we - * should disable SmartSpeed and enable Auto MasterSlave - * resolution as hardware default. */ - if (hw->autoneg_advertised == ADVERTISE_1000_FULL) { - /* Disable SmartSpeed */ - ret_val = e1000_read_phy_reg(hw, IGP01E1000_PHY_PORT_CONFIG, - &phy_data); - if (ret_val) - return ret_val; - phy_data &= ~IGP01E1000_PSCFR_SMART_SPEED; - ret_val = e1000_write_phy_reg(hw, IGP01E1000_PHY_PORT_CONFIG, - phy_data); - if (ret_val) - return ret_val; - /* Set auto Master/Slave resolution process */ - ret_val = e1000_read_phy_reg(hw, PHY_1000T_CTRL, &phy_data); - if (ret_val) - return ret_val; - phy_data &= ~CR_1000T_MS_ENABLE; - ret_val = e1000_write_phy_reg(hw, PHY_1000T_CTRL, phy_data); - if (ret_val) - return ret_val; - } - - ret_val = e1000_read_phy_reg(hw, PHY_1000T_CTRL, &phy_data); - if (ret_val) - return ret_val; - - /* load defaults for future use */ - hw->original_master_slave = (phy_data & CR_1000T_MS_ENABLE) ? - ((phy_data & CR_1000T_MS_VALUE) ? - e1000_ms_force_master : - e1000_ms_force_slave) : - e1000_ms_auto; - - switch (phy_ms_setting) { - case e1000_ms_force_master: - phy_data |= (CR_1000T_MS_ENABLE | CR_1000T_MS_VALUE); - break; - case e1000_ms_force_slave: - phy_data |= CR_1000T_MS_ENABLE; - phy_data &= ~(CR_1000T_MS_VALUE); - break; - case e1000_ms_auto: - phy_data &= ~CR_1000T_MS_ENABLE; - default: - break; - } - ret_val = e1000_write_phy_reg(hw, PHY_1000T_CTRL, phy_data); - if (ret_val) - return ret_val; - } - - return E1000_SUCCESS; + ctrl = er32(CTRL); + /* With 82543, we need to force speed and duplex on the MAC equal to what + * the PHY speed and duplex configuration is. In addition, we need to + * perform a hardware reset on the PHY to take it out of reset. + */ + if (hw->mac_type > e1000_82543) { + ctrl |= E1000_CTRL_SLU; + ctrl &= ~(E1000_CTRL_FRCSPD | E1000_CTRL_FRCDPX); + ew32(CTRL, ctrl); + } else { + ctrl |= + (E1000_CTRL_FRCSPD | E1000_CTRL_FRCDPX | E1000_CTRL_SLU); + ew32(CTRL, ctrl); + ret_val = e1000_phy_hw_reset(hw); + if (ret_val) + return ret_val; + } + + /* Make sure we have a valid PHY */ + ret_val = e1000_detect_gig_phy(hw); + if (ret_val) { + DEBUGOUT("Error, did not detect valid phy.\n"); + return ret_val; + } + DEBUGOUT1("Phy ID = %x \n", hw->phy_id); + + /* Set PHY to class A mode (if necessary) */ + ret_val = e1000_set_phy_mode(hw); + if (ret_val) + return ret_val; + + if ((hw->mac_type == e1000_82545_rev_3) || + (hw->mac_type == e1000_82546_rev_3)) { + ret_val = + e1000_read_phy_reg(hw, M88E1000_PHY_SPEC_CTRL, &phy_data); + phy_data |= 0x00000008; + ret_val = + e1000_write_phy_reg(hw, M88E1000_PHY_SPEC_CTRL, phy_data); + } + + if (hw->mac_type <= e1000_82543 || + hw->mac_type == e1000_82541 || hw->mac_type == e1000_82547 || + hw->mac_type == e1000_82541_rev_2 + || hw->mac_type == e1000_82547_rev_2) + hw->phy_reset_disable = false; + + return E1000_SUCCESS; } -/******************************************************************** -* Copper link setup for e1000_phy_gg82563 series. -* -* hw - Struct containing variables accessed by shared code -*********************************************************************/ -static s32 e1000_copper_link_ggp_setup(struct e1000_hw *hw) +/** + * e1000_copper_link_igp_setup - Copper link setup for e1000_phy_igp series. + * @hw: Struct containing variables accessed by shared code + */ +static s32 e1000_copper_link_igp_setup(struct e1000_hw *hw) { - s32 ret_val; - u16 phy_data; - u32 reg_data; - - DEBUGFUNC("e1000_copper_link_ggp_setup"); - - if (!hw->phy_reset_disable) { - - /* Enable CRS on TX for half-duplex operation. */ - ret_val = e1000_read_phy_reg(hw, GG82563_PHY_MAC_SPEC_CTRL, - &phy_data); - if (ret_val) - return ret_val; - - phy_data |= GG82563_MSCR_ASSERT_CRS_ON_TX; - /* Use 25MHz for both link down and 1000BASE-T for Tx clock */ - phy_data |= GG82563_MSCR_TX_CLK_1000MBPS_25MHZ; - - ret_val = e1000_write_phy_reg(hw, GG82563_PHY_MAC_SPEC_CTRL, - phy_data); - if (ret_val) - return ret_val; - - /* Options: - * MDI/MDI-X = 0 (default) - * 0 - Auto for all speeds - * 1 - MDI mode - * 2 - MDI-X mode - * 3 - Auto for 1000Base-T only (MDI-X for 10/100Base-T modes) - */ - ret_val = e1000_read_phy_reg(hw, GG82563_PHY_SPEC_CTRL, &phy_data); - if (ret_val) - return ret_val; - - phy_data &= ~GG82563_PSCR_CROSSOVER_MODE_MASK; - - switch (hw->mdix) { - case 1: - phy_data |= GG82563_PSCR_CROSSOVER_MODE_MDI; - break; - case 2: - phy_data |= GG82563_PSCR_CROSSOVER_MODE_MDIX; - break; - case 0: - default: - phy_data |= GG82563_PSCR_CROSSOVER_MODE_AUTO; - break; - } - - /* Options: - * disable_polarity_correction = 0 (default) - * Automatic Correction for Reversed Cable Polarity - * 0 - Disabled - * 1 - Enabled - */ - phy_data &= ~GG82563_PSCR_POLARITY_REVERSAL_DISABLE; - if (hw->disable_polarity_correction == 1) - phy_data |= GG82563_PSCR_POLARITY_REVERSAL_DISABLE; - ret_val = e1000_write_phy_reg(hw, GG82563_PHY_SPEC_CTRL, phy_data); - - if (ret_val) - return ret_val; - - /* SW Reset the PHY so all changes take effect */ - ret_val = e1000_phy_reset(hw); - if (ret_val) { - DEBUGOUT("Error Resetting the PHY\n"); - return ret_val; - } - } /* phy_reset_disable */ - - if (hw->mac_type == e1000_80003es2lan) { - /* Bypass RX and TX FIFO's */ - ret_val = e1000_write_kmrn_reg(hw, E1000_KUMCTRLSTA_OFFSET_FIFO_CTRL, - E1000_KUMCTRLSTA_FIFO_CTRL_RX_BYPASS | - E1000_KUMCTRLSTA_FIFO_CTRL_TX_BYPASS); - if (ret_val) - return ret_val; - - ret_val = e1000_read_phy_reg(hw, GG82563_PHY_SPEC_CTRL_2, &phy_data); - if (ret_val) - return ret_val; - - phy_data &= ~GG82563_PSCR2_REVERSE_AUTO_NEG; - ret_val = e1000_write_phy_reg(hw, GG82563_PHY_SPEC_CTRL_2, phy_data); - - if (ret_val) - return ret_val; - - reg_data = er32(CTRL_EXT); - reg_data &= ~(E1000_CTRL_EXT_LINK_MODE_MASK); - ew32(CTRL_EXT, reg_data); - - ret_val = e1000_read_phy_reg(hw, GG82563_PHY_PWR_MGMT_CTRL, - &phy_data); - if (ret_val) - return ret_val; - - /* Do not init these registers when the HW is in IAMT mode, since the - * firmware will have already initialized them. We only initialize - * them if the HW is not in IAMT mode. - */ - if (!e1000_check_mng_mode(hw)) { - /* Enable Electrical Idle on the PHY */ - phy_data |= GG82563_PMCR_ENABLE_ELECTRICAL_IDLE; - ret_val = e1000_write_phy_reg(hw, GG82563_PHY_PWR_MGMT_CTRL, - phy_data); - if (ret_val) - return ret_val; - - ret_val = e1000_read_phy_reg(hw, GG82563_PHY_KMRN_MODE_CTRL, - &phy_data); - if (ret_val) - return ret_val; - - phy_data &= ~GG82563_KMCR_PASS_FALSE_CARRIER; - ret_val = e1000_write_phy_reg(hw, GG82563_PHY_KMRN_MODE_CTRL, - phy_data); - - if (ret_val) - return ret_val; - } - - /* Workaround: Disable padding in Kumeran interface in the MAC - * and in the PHY to avoid CRC errors. - */ - ret_val = e1000_read_phy_reg(hw, GG82563_PHY_INBAND_CTRL, - &phy_data); - if (ret_val) - return ret_val; - phy_data |= GG82563_ICR_DIS_PADDING; - ret_val = e1000_write_phy_reg(hw, GG82563_PHY_INBAND_CTRL, - phy_data); - if (ret_val) - return ret_val; - } - - return E1000_SUCCESS; + u32 led_ctrl; + s32 ret_val; + u16 phy_data; + + DEBUGFUNC("e1000_copper_link_igp_setup"); + + if (hw->phy_reset_disable) + return E1000_SUCCESS; + + ret_val = e1000_phy_reset(hw); + if (ret_val) { + DEBUGOUT("Error Resetting the PHY\n"); + return ret_val; + } + + /* Wait 15ms for MAC to configure PHY from eeprom settings */ + msleep(15); + /* Configure activity LED after PHY reset */ + led_ctrl = er32(LEDCTL); + led_ctrl &= IGP_ACTIVITY_LED_MASK; + led_ctrl |= (IGP_ACTIVITY_LED_ENABLE | IGP_LED3_MODE); + ew32(LEDCTL, led_ctrl); + + /* The NVM settings will configure LPLU in D3 for IGP2 and IGP3 PHYs */ + if (hw->phy_type == e1000_phy_igp) { + /* disable lplu d3 during driver init */ + ret_val = e1000_set_d3_lplu_state(hw, false); + if (ret_val) { + DEBUGOUT("Error Disabling LPLU D3\n"); + return ret_val; + } + } + + /* Configure mdi-mdix settings */ + ret_val = e1000_read_phy_reg(hw, IGP01E1000_PHY_PORT_CTRL, &phy_data); + if (ret_val) + return ret_val; + + if ((hw->mac_type == e1000_82541) || (hw->mac_type == e1000_82547)) { + hw->dsp_config_state = e1000_dsp_config_disabled; + /* Force MDI for earlier revs of the IGP PHY */ + phy_data &= + ~(IGP01E1000_PSCR_AUTO_MDIX | + IGP01E1000_PSCR_FORCE_MDI_MDIX); + hw->mdix = 1; + + } else { + hw->dsp_config_state = e1000_dsp_config_enabled; + phy_data &= ~IGP01E1000_PSCR_AUTO_MDIX; + + switch (hw->mdix) { + case 1: + phy_data &= ~IGP01E1000_PSCR_FORCE_MDI_MDIX; + break; + case 2: + phy_data |= IGP01E1000_PSCR_FORCE_MDI_MDIX; + break; + case 0: + default: + phy_data |= IGP01E1000_PSCR_AUTO_MDIX; + break; + } + } + ret_val = e1000_write_phy_reg(hw, IGP01E1000_PHY_PORT_CTRL, phy_data); + if (ret_val) + return ret_val; + + /* set auto-master slave resolution settings */ + if (hw->autoneg) { + e1000_ms_type phy_ms_setting = hw->master_slave; + + if (hw->ffe_config_state == e1000_ffe_config_active) + hw->ffe_config_state = e1000_ffe_config_enabled; + + if (hw->dsp_config_state == e1000_dsp_config_activated) + hw->dsp_config_state = e1000_dsp_config_enabled; + + /* when autonegotiation advertisement is only 1000Mbps then we + * should disable SmartSpeed and enable Auto MasterSlave + * resolution as hardware default. */ + if (hw->autoneg_advertised == ADVERTISE_1000_FULL) { + /* Disable SmartSpeed */ + ret_val = + e1000_read_phy_reg(hw, IGP01E1000_PHY_PORT_CONFIG, + &phy_data); + if (ret_val) + return ret_val; + phy_data &= ~IGP01E1000_PSCFR_SMART_SPEED; + ret_val = + e1000_write_phy_reg(hw, IGP01E1000_PHY_PORT_CONFIG, + phy_data); + if (ret_val) + return ret_val; + /* Set auto Master/Slave resolution process */ + ret_val = + e1000_read_phy_reg(hw, PHY_1000T_CTRL, &phy_data); + if (ret_val) + return ret_val; + phy_data &= ~CR_1000T_MS_ENABLE; + ret_val = + e1000_write_phy_reg(hw, PHY_1000T_CTRL, phy_data); + if (ret_val) + return ret_val; + } + + ret_val = e1000_read_phy_reg(hw, PHY_1000T_CTRL, &phy_data); + if (ret_val) + return ret_val; + + /* load defaults for future use */ + hw->original_master_slave = (phy_data & CR_1000T_MS_ENABLE) ? + ((phy_data & CR_1000T_MS_VALUE) ? + e1000_ms_force_master : + e1000_ms_force_slave) : e1000_ms_auto; + + switch (phy_ms_setting) { + case e1000_ms_force_master: + phy_data |= (CR_1000T_MS_ENABLE | CR_1000T_MS_VALUE); + break; + case e1000_ms_force_slave: + phy_data |= CR_1000T_MS_ENABLE; + phy_data &= ~(CR_1000T_MS_VALUE); + break; + case e1000_ms_auto: + phy_data &= ~CR_1000T_MS_ENABLE; + default: + break; + } + ret_val = e1000_write_phy_reg(hw, PHY_1000T_CTRL, phy_data); + if (ret_val) + return ret_val; + } + + return E1000_SUCCESS; } -/******************************************************************** -* Copper link setup for e1000_phy_m88 series. -* -* hw - Struct containing variables accessed by shared code -*********************************************************************/ +/** + * e1000_copper_link_mgp_setup - Copper link setup for e1000_phy_m88 series. + * @hw: Struct containing variables accessed by shared code + */ static s32 e1000_copper_link_mgp_setup(struct e1000_hw *hw) { - s32 ret_val; - u16 phy_data; - - DEBUGFUNC("e1000_copper_link_mgp_setup"); - - if (hw->phy_reset_disable) - return E1000_SUCCESS; - - /* Enable CRS on TX. This must be set for half-duplex operation. */ - ret_val = e1000_read_phy_reg(hw, M88E1000_PHY_SPEC_CTRL, &phy_data); - if (ret_val) - return ret_val; - - phy_data |= M88E1000_PSCR_ASSERT_CRS_ON_TX; - - /* Options: - * MDI/MDI-X = 0 (default) - * 0 - Auto for all speeds - * 1 - MDI mode - * 2 - MDI-X mode - * 3 - Auto for 1000Base-T only (MDI-X for 10/100Base-T modes) - */ - phy_data &= ~M88E1000_PSCR_AUTO_X_MODE; - - switch (hw->mdix) { - case 1: - phy_data |= M88E1000_PSCR_MDI_MANUAL_MODE; - break; - case 2: - phy_data |= M88E1000_PSCR_MDIX_MANUAL_MODE; - break; - case 3: - phy_data |= M88E1000_PSCR_AUTO_X_1000T; - break; - case 0: - default: - phy_data |= M88E1000_PSCR_AUTO_X_MODE; - break; - } - - /* Options: - * disable_polarity_correction = 0 (default) - * Automatic Correction for Reversed Cable Polarity - * 0 - Disabled - * 1 - Enabled - */ - phy_data &= ~M88E1000_PSCR_POLARITY_REVERSAL; - if (hw->disable_polarity_correction == 1) - phy_data |= M88E1000_PSCR_POLARITY_REVERSAL; - ret_val = e1000_write_phy_reg(hw, M88E1000_PHY_SPEC_CTRL, phy_data); - if (ret_val) - return ret_val; - - if (hw->phy_revision < M88E1011_I_REV_4) { - /* Force TX_CLK in the Extended PHY Specific Control Register - * to 25MHz clock. - */ - ret_val = e1000_read_phy_reg(hw, M88E1000_EXT_PHY_SPEC_CTRL, &phy_data); - if (ret_val) - return ret_val; - - phy_data |= M88E1000_EPSCR_TX_CLK_25; - - if ((hw->phy_revision == E1000_REVISION_2) && - (hw->phy_id == M88E1111_I_PHY_ID)) { - /* Vidalia Phy, set the downshift counter to 5x */ - phy_data &= ~(M88EC018_EPSCR_DOWNSHIFT_COUNTER_MASK); - phy_data |= M88EC018_EPSCR_DOWNSHIFT_COUNTER_5X; - ret_val = e1000_write_phy_reg(hw, - M88E1000_EXT_PHY_SPEC_CTRL, phy_data); - if (ret_val) - return ret_val; - } else { - /* Configure Master and Slave downshift values */ - phy_data &= ~(M88E1000_EPSCR_MASTER_DOWNSHIFT_MASK | - M88E1000_EPSCR_SLAVE_DOWNSHIFT_MASK); - phy_data |= (M88E1000_EPSCR_MASTER_DOWNSHIFT_1X | - M88E1000_EPSCR_SLAVE_DOWNSHIFT_1X); - ret_val = e1000_write_phy_reg(hw, - M88E1000_EXT_PHY_SPEC_CTRL, phy_data); - if (ret_val) - return ret_val; - } - } - - /* SW Reset the PHY so all changes take effect */ - ret_val = e1000_phy_reset(hw); - if (ret_val) { - DEBUGOUT("Error Resetting the PHY\n"); - return ret_val; - } - - return E1000_SUCCESS; + s32 ret_val; + u16 phy_data; + + DEBUGFUNC("e1000_copper_link_mgp_setup"); + + if (hw->phy_reset_disable) + return E1000_SUCCESS; + + /* Enable CRS on TX. This must be set for half-duplex operation. */ + ret_val = e1000_read_phy_reg(hw, M88E1000_PHY_SPEC_CTRL, &phy_data); + if (ret_val) + return ret_val; + + phy_data |= M88E1000_PSCR_ASSERT_CRS_ON_TX; + + /* Options: + * MDI/MDI-X = 0 (default) + * 0 - Auto for all speeds + * 1 - MDI mode + * 2 - MDI-X mode + * 3 - Auto for 1000Base-T only (MDI-X for 10/100Base-T modes) + */ + phy_data &= ~M88E1000_PSCR_AUTO_X_MODE; + + switch (hw->mdix) { + case 1: + phy_data |= M88E1000_PSCR_MDI_MANUAL_MODE; + break; + case 2: + phy_data |= M88E1000_PSCR_MDIX_MANUAL_MODE; + break; + case 3: + phy_data |= M88E1000_PSCR_AUTO_X_1000T; + break; + case 0: + default: + phy_data |= M88E1000_PSCR_AUTO_X_MODE; + break; + } + + /* Options: + * disable_polarity_correction = 0 (default) + * Automatic Correction for Reversed Cable Polarity + * 0 - Disabled + * 1 - Enabled + */ + phy_data &= ~M88E1000_PSCR_POLARITY_REVERSAL; + if (hw->disable_polarity_correction == 1) + phy_data |= M88E1000_PSCR_POLARITY_REVERSAL; + ret_val = e1000_write_phy_reg(hw, M88E1000_PHY_SPEC_CTRL, phy_data); + if (ret_val) + return ret_val; + + if (hw->phy_revision < M88E1011_I_REV_4) { + /* Force TX_CLK in the Extended PHY Specific Control Register + * to 25MHz clock. + */ + ret_val = + e1000_read_phy_reg(hw, M88E1000_EXT_PHY_SPEC_CTRL, + &phy_data); + if (ret_val) + return ret_val; + + phy_data |= M88E1000_EPSCR_TX_CLK_25; + + if ((hw->phy_revision == E1000_REVISION_2) && + (hw->phy_id == M88E1111_I_PHY_ID)) { + /* Vidalia Phy, set the downshift counter to 5x */ + phy_data &= ~(M88EC018_EPSCR_DOWNSHIFT_COUNTER_MASK); + phy_data |= M88EC018_EPSCR_DOWNSHIFT_COUNTER_5X; + ret_val = e1000_write_phy_reg(hw, + M88E1000_EXT_PHY_SPEC_CTRL, + phy_data); + if (ret_val) + return ret_val; + } else { + /* Configure Master and Slave downshift values */ + phy_data &= ~(M88E1000_EPSCR_MASTER_DOWNSHIFT_MASK | + M88E1000_EPSCR_SLAVE_DOWNSHIFT_MASK); + phy_data |= (M88E1000_EPSCR_MASTER_DOWNSHIFT_1X | + M88E1000_EPSCR_SLAVE_DOWNSHIFT_1X); + ret_val = e1000_write_phy_reg(hw, + M88E1000_EXT_PHY_SPEC_CTRL, + phy_data); + if (ret_val) + return ret_val; + } + } + + /* SW Reset the PHY so all changes take effect */ + ret_val = e1000_phy_reset(hw); + if (ret_val) { + DEBUGOUT("Error Resetting the PHY\n"); + return ret_val; + } + + return E1000_SUCCESS; } -/******************************************************************** -* Setup auto-negotiation and flow control advertisements, -* and then perform auto-negotiation. -* -* hw - Struct containing variables accessed by shared code -*********************************************************************/ +/** + * e1000_copper_link_autoneg - setup auto-neg + * @hw: Struct containing variables accessed by shared code + * + * Setup auto-negotiation and flow control advertisements, + * and then perform auto-negotiation. + */ static s32 e1000_copper_link_autoneg(struct e1000_hw *hw) { - s32 ret_val; - u16 phy_data; - - DEBUGFUNC("e1000_copper_link_autoneg"); - - /* Perform some bounds checking on the hw->autoneg_advertised - * parameter. If this variable is zero, then set it to the default. - */ - hw->autoneg_advertised &= AUTONEG_ADVERTISE_SPEED_DEFAULT; - - /* If autoneg_advertised is zero, we assume it was not defaulted - * by the calling code so we set to advertise full capability. - */ - if (hw->autoneg_advertised == 0) - hw->autoneg_advertised = AUTONEG_ADVERTISE_SPEED_DEFAULT; - - /* IFE phy only supports 10/100 */ - if (hw->phy_type == e1000_phy_ife) - hw->autoneg_advertised &= AUTONEG_ADVERTISE_10_100_ALL; - - DEBUGOUT("Reconfiguring auto-neg advertisement params\n"); - ret_val = e1000_phy_setup_autoneg(hw); - if (ret_val) { - DEBUGOUT("Error Setting up Auto-Negotiation\n"); - return ret_val; - } - DEBUGOUT("Restarting Auto-Neg\n"); - - /* Restart auto-negotiation by setting the Auto Neg Enable bit and - * the Auto Neg Restart bit in the PHY control register. - */ - ret_val = e1000_read_phy_reg(hw, PHY_CTRL, &phy_data); - if (ret_val) - return ret_val; - - phy_data |= (MII_CR_AUTO_NEG_EN | MII_CR_RESTART_AUTO_NEG); - ret_val = e1000_write_phy_reg(hw, PHY_CTRL, phy_data); - if (ret_val) - return ret_val; - - /* Does the user want to wait for Auto-Neg to complete here, or - * check at a later time (for example, callback routine). - */ - if (hw->wait_autoneg_complete) { - ret_val = e1000_wait_autoneg(hw); - if (ret_val) { - DEBUGOUT("Error while waiting for autoneg to complete\n"); - return ret_val; - } - } - - hw->get_link_status = true; - - return E1000_SUCCESS; + s32 ret_val; + u16 phy_data; + + DEBUGFUNC("e1000_copper_link_autoneg"); + + /* Perform some bounds checking on the hw->autoneg_advertised + * parameter. If this variable is zero, then set it to the default. + */ + hw->autoneg_advertised &= AUTONEG_ADVERTISE_SPEED_DEFAULT; + + /* If autoneg_advertised is zero, we assume it was not defaulted + * by the calling code so we set to advertise full capability. + */ + if (hw->autoneg_advertised == 0) + hw->autoneg_advertised = AUTONEG_ADVERTISE_SPEED_DEFAULT; + + DEBUGOUT("Reconfiguring auto-neg advertisement params\n"); + ret_val = e1000_phy_setup_autoneg(hw); + if (ret_val) { + DEBUGOUT("Error Setting up Auto-Negotiation\n"); + return ret_val; + } + DEBUGOUT("Restarting Auto-Neg\n"); + + /* Restart auto-negotiation by setting the Auto Neg Enable bit and + * the Auto Neg Restart bit in the PHY control register. + */ + ret_val = e1000_read_phy_reg(hw, PHY_CTRL, &phy_data); + if (ret_val) + return ret_val; + + phy_data |= (MII_CR_AUTO_NEG_EN | MII_CR_RESTART_AUTO_NEG); + ret_val = e1000_write_phy_reg(hw, PHY_CTRL, phy_data); + if (ret_val) + return ret_val; + + /* Does the user want to wait for Auto-Neg to complete here, or + * check at a later time (for example, callback routine). + */ + if (hw->wait_autoneg_complete) { + ret_val = e1000_wait_autoneg(hw); + if (ret_val) { + DEBUGOUT + ("Error while waiting for autoneg to complete\n"); + return ret_val; + } + } + + hw->get_link_status = true; + + return E1000_SUCCESS; } -/****************************************************************************** -* Config the MAC and the PHY after link is up. -* 1) Set up the MAC to the current PHY speed/duplex -* if we are on 82543. If we -* are on newer silicon, we only need to configure -* collision distance in the Transmit Control Register. -* 2) Set up flow control on the MAC to that established with -* the link partner. -* 3) Config DSP to improve Gigabit link quality for some PHY revisions. -* -* hw - Struct containing variables accessed by shared code -******************************************************************************/ +/** + * e1000_copper_link_postconfig - post link setup + * @hw: Struct containing variables accessed by shared code + * + * Config the MAC and the PHY after link is up. + * 1) Set up the MAC to the current PHY speed/duplex + * if we are on 82543. If we + * are on newer silicon, we only need to configure + * collision distance in the Transmit Control Register. + * 2) Set up flow control on the MAC to that established with + * the link partner. + * 3) Config DSP to improve Gigabit link quality for some PHY revisions. + */ static s32 e1000_copper_link_postconfig(struct e1000_hw *hw) { - s32 ret_val; - DEBUGFUNC("e1000_copper_link_postconfig"); - - if (hw->mac_type >= e1000_82544) { - e1000_config_collision_dist(hw); - } else { - ret_val = e1000_config_mac_to_phy(hw); - if (ret_val) { - DEBUGOUT("Error configuring MAC to PHY settings\n"); - return ret_val; - } - } - ret_val = e1000_config_fc_after_link_up(hw); - if (ret_val) { - DEBUGOUT("Error Configuring Flow Control\n"); - return ret_val; - } - - /* Config DSP to improve Giga link quality */ - if (hw->phy_type == e1000_phy_igp) { - ret_val = e1000_config_dsp_after_link_change(hw, true); - if (ret_val) { - DEBUGOUT("Error Configuring DSP after link up\n"); - return ret_val; - } - } - - return E1000_SUCCESS; + s32 ret_val; + DEBUGFUNC("e1000_copper_link_postconfig"); + + if (hw->mac_type >= e1000_82544) { + e1000_config_collision_dist(hw); + } else { + ret_val = e1000_config_mac_to_phy(hw); + if (ret_val) { + DEBUGOUT("Error configuring MAC to PHY settings\n"); + return ret_val; + } + } + ret_val = e1000_config_fc_after_link_up(hw); + if (ret_val) { + DEBUGOUT("Error Configuring Flow Control\n"); + return ret_val; + } + + /* Config DSP to improve Giga link quality */ + if (hw->phy_type == e1000_phy_igp) { + ret_val = e1000_config_dsp_after_link_change(hw, true); + if (ret_val) { + DEBUGOUT("Error Configuring DSP after link up\n"); + return ret_val; + } + } + + return E1000_SUCCESS; } -/****************************************************************************** -* Detects which PHY is present and setup the speed and duplex -* -* hw - Struct containing variables accessed by shared code -******************************************************************************/ +/** + * e1000_setup_copper_link - phy/speed/duplex setting + * @hw: Struct containing variables accessed by shared code + * + * Detects which PHY is present and sets up the speed and duplex + */ static s32 e1000_setup_copper_link(struct e1000_hw *hw) { - s32 ret_val; - u16 i; - u16 phy_data; - u16 reg_data = 0; - - DEBUGFUNC("e1000_setup_copper_link"); - - switch (hw->mac_type) { - case e1000_80003es2lan: - case e1000_ich8lan: - /* Set the mac to wait the maximum time between each - * iteration and increase the max iterations when - * polling the phy; this fixes erroneous timeouts at 10Mbps. */ - ret_val = e1000_write_kmrn_reg(hw, GG82563_REG(0x34, 4), 0xFFFF); - if (ret_val) - return ret_val; - ret_val = e1000_read_kmrn_reg(hw, GG82563_REG(0x34, 9), ®_data); - if (ret_val) - return ret_val; - reg_data |= 0x3F; - ret_val = e1000_write_kmrn_reg(hw, GG82563_REG(0x34, 9), reg_data); - if (ret_val) - return ret_val; - default: - break; - } - - /* Check if it is a valid PHY and set PHY mode if necessary. */ - ret_val = e1000_copper_link_preconfig(hw); - if (ret_val) - return ret_val; - - switch (hw->mac_type) { - case e1000_80003es2lan: - /* Kumeran registers are written-only */ - reg_data = E1000_KUMCTRLSTA_INB_CTRL_LINK_STATUS_TX_TIMEOUT_DEFAULT; - reg_data |= E1000_KUMCTRLSTA_INB_CTRL_DIS_PADDING; - ret_val = e1000_write_kmrn_reg(hw, E1000_KUMCTRLSTA_OFFSET_INB_CTRL, - reg_data); - if (ret_val) - return ret_val; - break; - default: - break; - } - - if (hw->phy_type == e1000_phy_igp || - hw->phy_type == e1000_phy_igp_3 || - hw->phy_type == e1000_phy_igp_2) { - ret_val = e1000_copper_link_igp_setup(hw); - if (ret_val) - return ret_val; - } else if (hw->phy_type == e1000_phy_m88) { - ret_val = e1000_copper_link_mgp_setup(hw); - if (ret_val) - return ret_val; - } else if (hw->phy_type == e1000_phy_gg82563) { - ret_val = e1000_copper_link_ggp_setup(hw); - if (ret_val) - return ret_val; - } - - if (hw->autoneg) { - /* Setup autoneg and flow control advertisement - * and perform autonegotiation */ - ret_val = e1000_copper_link_autoneg(hw); - if (ret_val) - return ret_val; - } else { - /* PHY will be set to 10H, 10F, 100H,or 100F - * depending on value from forced_speed_duplex. */ - DEBUGOUT("Forcing speed and duplex\n"); - ret_val = e1000_phy_force_speed_duplex(hw); - if (ret_val) { - DEBUGOUT("Error Forcing Speed and Duplex\n"); - return ret_val; - } - } - - /* Check link status. Wait up to 100 microseconds for link to become - * valid. - */ - for (i = 0; i < 10; i++) { - ret_val = e1000_read_phy_reg(hw, PHY_STATUS, &phy_data); - if (ret_val) - return ret_val; - ret_val = e1000_read_phy_reg(hw, PHY_STATUS, &phy_data); - if (ret_val) - return ret_val; - - if (phy_data & MII_SR_LINK_STATUS) { - /* Config the MAC and PHY after link is up */ - ret_val = e1000_copper_link_postconfig(hw); - if (ret_val) - return ret_val; - - DEBUGOUT("Valid link established!!!\n"); - return E1000_SUCCESS; - } - udelay(10); - } - - DEBUGOUT("Unable to establish link!!!\n"); - return E1000_SUCCESS; + s32 ret_val; + u16 i; + u16 phy_data; + + DEBUGFUNC("e1000_setup_copper_link"); + + /* Check if it is a valid PHY and set PHY mode if necessary. */ + ret_val = e1000_copper_link_preconfig(hw); + if (ret_val) + return ret_val; + + if (hw->phy_type == e1000_phy_igp) { + ret_val = e1000_copper_link_igp_setup(hw); + if (ret_val) + return ret_val; + } else if (hw->phy_type == e1000_phy_m88) { + ret_val = e1000_copper_link_mgp_setup(hw); + if (ret_val) + return ret_val; + } + + if (hw->autoneg) { + /* Setup autoneg and flow control advertisement + * and perform autonegotiation */ + ret_val = e1000_copper_link_autoneg(hw); + if (ret_val) + return ret_val; + } else { + /* PHY will be set to 10H, 10F, 100H,or 100F + * depending on value from forced_speed_duplex. */ + DEBUGOUT("Forcing speed and duplex\n"); + ret_val = e1000_phy_force_speed_duplex(hw); + if (ret_val) { + DEBUGOUT("Error Forcing Speed and Duplex\n"); + return ret_val; + } + } + + /* Check link status. Wait up to 100 microseconds for link to become + * valid. + */ + for (i = 0; i < 10; i++) { + ret_val = e1000_read_phy_reg(hw, PHY_STATUS, &phy_data); + if (ret_val) + return ret_val; + ret_val = e1000_read_phy_reg(hw, PHY_STATUS, &phy_data); + if (ret_val) + return ret_val; + + if (phy_data & MII_SR_LINK_STATUS) { + /* Config the MAC and PHY after link is up */ + ret_val = e1000_copper_link_postconfig(hw); + if (ret_val) + return ret_val; + + DEBUGOUT("Valid link established!!!\n"); + return E1000_SUCCESS; + } + udelay(10); + } + + DEBUGOUT("Unable to establish link!!!\n"); + return E1000_SUCCESS; } -/****************************************************************************** -* Configure the MAC-to-PHY interface for 10/100Mbps -* -* hw - Struct containing variables accessed by shared code -******************************************************************************/ -static s32 e1000_configure_kmrn_for_10_100(struct e1000_hw *hw, u16 duplex) +/** + * e1000_phy_setup_autoneg - phy settings + * @hw: Struct containing variables accessed by shared code + * + * Configures PHY autoneg and flow control advertisement settings + */ +s32 e1000_phy_setup_autoneg(struct e1000_hw *hw) { - s32 ret_val = E1000_SUCCESS; - u32 tipg; - u16 reg_data; + s32 ret_val; + u16 mii_autoneg_adv_reg; + u16 mii_1000t_ctrl_reg; - DEBUGFUNC("e1000_configure_kmrn_for_10_100"); + DEBUGFUNC("e1000_phy_setup_autoneg"); - reg_data = E1000_KUMCTRLSTA_HD_CTRL_10_100_DEFAULT; - ret_val = e1000_write_kmrn_reg(hw, E1000_KUMCTRLSTA_OFFSET_HD_CTRL, - reg_data); - if (ret_val) - return ret_val; + /* Read the MII Auto-Neg Advertisement Register (Address 4). */ + ret_val = e1000_read_phy_reg(hw, PHY_AUTONEG_ADV, &mii_autoneg_adv_reg); + if (ret_val) + return ret_val; - /* Configure Transmit Inter-Packet Gap */ - tipg = er32(TIPG); - tipg &= ~E1000_TIPG_IPGT_MASK; - tipg |= DEFAULT_80003ES2LAN_TIPG_IPGT_10_100; - ew32(TIPG, tipg); + /* Read the MII 1000Base-T Control Register (Address 9). */ + ret_val = + e1000_read_phy_reg(hw, PHY_1000T_CTRL, &mii_1000t_ctrl_reg); + if (ret_val) + return ret_val; - ret_val = e1000_read_phy_reg(hw, GG82563_PHY_KMRN_MODE_CTRL, ®_data); + /* Need to parse both autoneg_advertised and fc and set up + * the appropriate PHY registers. First we will parse for + * autoneg_advertised software override. Since we can advertise + * a plethora of combinations, we need to check each bit + * individually. + */ - if (ret_val) - return ret_val; + /* First we clear all the 10/100 mb speed bits in the Auto-Neg + * Advertisement Register (Address 4) and the 1000 mb speed bits in + * the 1000Base-T Control Register (Address 9). + */ + mii_autoneg_adv_reg &= ~REG4_SPEED_MASK; + mii_1000t_ctrl_reg &= ~REG9_SPEED_MASK; - if (duplex == HALF_DUPLEX) - reg_data |= GG82563_KMCR_PASS_FALSE_CARRIER; - else - reg_data &= ~GG82563_KMCR_PASS_FALSE_CARRIER; + DEBUGOUT1("autoneg_advertised %x\n", hw->autoneg_advertised); - ret_val = e1000_write_phy_reg(hw, GG82563_PHY_KMRN_MODE_CTRL, reg_data); + /* Do we want to advertise 10 Mb Half Duplex? */ + if (hw->autoneg_advertised & ADVERTISE_10_HALF) { + DEBUGOUT("Advertise 10mb Half duplex\n"); + mii_autoneg_adv_reg |= NWAY_AR_10T_HD_CAPS; + } - return ret_val; -} + /* Do we want to advertise 10 Mb Full Duplex? */ + if (hw->autoneg_advertised & ADVERTISE_10_FULL) { + DEBUGOUT("Advertise 10mb Full duplex\n"); + mii_autoneg_adv_reg |= NWAY_AR_10T_FD_CAPS; + } -static s32 e1000_configure_kmrn_for_1000(struct e1000_hw *hw) -{ - s32 ret_val = E1000_SUCCESS; - u16 reg_data; - u32 tipg; + /* Do we want to advertise 100 Mb Half Duplex? */ + if (hw->autoneg_advertised & ADVERTISE_100_HALF) { + DEBUGOUT("Advertise 100mb Half duplex\n"); + mii_autoneg_adv_reg |= NWAY_AR_100TX_HD_CAPS; + } - DEBUGFUNC("e1000_configure_kmrn_for_1000"); + /* Do we want to advertise 100 Mb Full Duplex? */ + if (hw->autoneg_advertised & ADVERTISE_100_FULL) { + DEBUGOUT("Advertise 100mb Full duplex\n"); + mii_autoneg_adv_reg |= NWAY_AR_100TX_FD_CAPS; + } - reg_data = E1000_KUMCTRLSTA_HD_CTRL_1000_DEFAULT; - ret_val = e1000_write_kmrn_reg(hw, E1000_KUMCTRLSTA_OFFSET_HD_CTRL, - reg_data); - if (ret_val) - return ret_val; + /* We do not allow the Phy to advertise 1000 Mb Half Duplex */ + if (hw->autoneg_advertised & ADVERTISE_1000_HALF) { + DEBUGOUT + ("Advertise 1000mb Half duplex requested, request denied!\n"); + } - /* Configure Transmit Inter-Packet Gap */ - tipg = er32(TIPG); - tipg &= ~E1000_TIPG_IPGT_MASK; - tipg |= DEFAULT_80003ES2LAN_TIPG_IPGT_1000; - ew32(TIPG, tipg); + /* Do we want to advertise 1000 Mb Full Duplex? */ + if (hw->autoneg_advertised & ADVERTISE_1000_FULL) { + DEBUGOUT("Advertise 1000mb Full duplex\n"); + mii_1000t_ctrl_reg |= CR_1000T_FD_CAPS; + } - ret_val = e1000_read_phy_reg(hw, GG82563_PHY_KMRN_MODE_CTRL, ®_data); + /* Check for a software override of the flow control settings, and + * setup the PHY advertisement registers accordingly. If + * auto-negotiation is enabled, then software will have to set the + * "PAUSE" bits to the correct value in the Auto-Negotiation + * Advertisement Register (PHY_AUTONEG_ADV) and re-start auto-negotiation. + * + * The possible values of the "fc" parameter are: + * 0: Flow control is completely disabled + * 1: Rx flow control is enabled (we can receive pause frames + * but not send pause frames). + * 2: Tx flow control is enabled (we can send pause frames + * but we do not support receiving pause frames). + * 3: Both Rx and TX flow control (symmetric) are enabled. + * other: No software override. The flow control configuration + * in the EEPROM is used. + */ + switch (hw->fc) { + case E1000_FC_NONE: /* 0 */ + /* Flow control (RX & TX) is completely disabled by a + * software over-ride. + */ + mii_autoneg_adv_reg &= ~(NWAY_AR_ASM_DIR | NWAY_AR_PAUSE); + break; + case E1000_FC_RX_PAUSE: /* 1 */ + /* RX Flow control is enabled, and TX Flow control is + * disabled, by a software over-ride. + */ + /* Since there really isn't a way to advertise that we are + * capable of RX Pause ONLY, we will advertise that we + * support both symmetric and asymmetric RX PAUSE. Later + * (in e1000_config_fc_after_link_up) we will disable the + *hw's ability to send PAUSE frames. + */ + mii_autoneg_adv_reg |= (NWAY_AR_ASM_DIR | NWAY_AR_PAUSE); + break; + case E1000_FC_TX_PAUSE: /* 2 */ + /* TX Flow control is enabled, and RX Flow control is + * disabled, by a software over-ride. + */ + mii_autoneg_adv_reg |= NWAY_AR_ASM_DIR; + mii_autoneg_adv_reg &= ~NWAY_AR_PAUSE; + break; + case E1000_FC_FULL: /* 3 */ + /* Flow control (both RX and TX) is enabled by a software + * over-ride. + */ + mii_autoneg_adv_reg |= (NWAY_AR_ASM_DIR | NWAY_AR_PAUSE); + break; + default: + DEBUGOUT("Flow control param set incorrectly\n"); + return -E1000_ERR_CONFIG; + } - if (ret_val) - return ret_val; + ret_val = e1000_write_phy_reg(hw, PHY_AUTONEG_ADV, mii_autoneg_adv_reg); + if (ret_val) + return ret_val; - reg_data &= ~GG82563_KMCR_PASS_FALSE_CARRIER; - ret_val = e1000_write_phy_reg(hw, GG82563_PHY_KMRN_MODE_CTRL, reg_data); + DEBUGOUT1("Auto-Neg Advertising %x\n", mii_autoneg_adv_reg); - return ret_val; -} + ret_val = e1000_write_phy_reg(hw, PHY_1000T_CTRL, mii_1000t_ctrl_reg); + if (ret_val) + return ret_val; -/****************************************************************************** -* Configures PHY autoneg and flow control advertisement settings -* -* hw - Struct containing variables accessed by shared code -******************************************************************************/ -s32 e1000_phy_setup_autoneg(struct e1000_hw *hw) -{ - s32 ret_val; - u16 mii_autoneg_adv_reg; - u16 mii_1000t_ctrl_reg; - - DEBUGFUNC("e1000_phy_setup_autoneg"); - - /* Read the MII Auto-Neg Advertisement Register (Address 4). */ - ret_val = e1000_read_phy_reg(hw, PHY_AUTONEG_ADV, &mii_autoneg_adv_reg); - if (ret_val) - return ret_val; - - if (hw->phy_type != e1000_phy_ife) { - /* Read the MII 1000Base-T Control Register (Address 9). */ - ret_val = e1000_read_phy_reg(hw, PHY_1000T_CTRL, &mii_1000t_ctrl_reg); - if (ret_val) - return ret_val; - } else - mii_1000t_ctrl_reg=0; - - /* Need to parse both autoneg_advertised and fc and set up - * the appropriate PHY registers. First we will parse for - * autoneg_advertised software override. Since we can advertise - * a plethora of combinations, we need to check each bit - * individually. - */ - - /* First we clear all the 10/100 mb speed bits in the Auto-Neg - * Advertisement Register (Address 4) and the 1000 mb speed bits in - * the 1000Base-T Control Register (Address 9). - */ - mii_autoneg_adv_reg &= ~REG4_SPEED_MASK; - mii_1000t_ctrl_reg &= ~REG9_SPEED_MASK; - - DEBUGOUT1("autoneg_advertised %x\n", hw->autoneg_advertised); - - /* Do we want to advertise 10 Mb Half Duplex? */ - if (hw->autoneg_advertised & ADVERTISE_10_HALF) { - DEBUGOUT("Advertise 10mb Half duplex\n"); - mii_autoneg_adv_reg |= NWAY_AR_10T_HD_CAPS; - } - - /* Do we want to advertise 10 Mb Full Duplex? */ - if (hw->autoneg_advertised & ADVERTISE_10_FULL) { - DEBUGOUT("Advertise 10mb Full duplex\n"); - mii_autoneg_adv_reg |= NWAY_AR_10T_FD_CAPS; - } - - /* Do we want to advertise 100 Mb Half Duplex? */ - if (hw->autoneg_advertised & ADVERTISE_100_HALF) { - DEBUGOUT("Advertise 100mb Half duplex\n"); - mii_autoneg_adv_reg |= NWAY_AR_100TX_HD_CAPS; - } - - /* Do we want to advertise 100 Mb Full Duplex? */ - if (hw->autoneg_advertised & ADVERTISE_100_FULL) { - DEBUGOUT("Advertise 100mb Full duplex\n"); - mii_autoneg_adv_reg |= NWAY_AR_100TX_FD_CAPS; - } - - /* We do not allow the Phy to advertise 1000 Mb Half Duplex */ - if (hw->autoneg_advertised & ADVERTISE_1000_HALF) { - DEBUGOUT("Advertise 1000mb Half duplex requested, request denied!\n"); - } - - /* Do we want to advertise 1000 Mb Full Duplex? */ - if (hw->autoneg_advertised & ADVERTISE_1000_FULL) { - DEBUGOUT("Advertise 1000mb Full duplex\n"); - mii_1000t_ctrl_reg |= CR_1000T_FD_CAPS; - if (hw->phy_type == e1000_phy_ife) { - DEBUGOUT("e1000_phy_ife is a 10/100 PHY. Gigabit speed is not supported.\n"); - } - } - - /* Check for a software override of the flow control settings, and - * setup the PHY advertisement registers accordingly. If - * auto-negotiation is enabled, then software will have to set the - * "PAUSE" bits to the correct value in the Auto-Negotiation - * Advertisement Register (PHY_AUTONEG_ADV) and re-start auto-negotiation. - * - * The possible values of the "fc" parameter are: - * 0: Flow control is completely disabled - * 1: Rx flow control is enabled (we can receive pause frames - * but not send pause frames). - * 2: Tx flow control is enabled (we can send pause frames - * but we do not support receiving pause frames). - * 3: Both Rx and TX flow control (symmetric) are enabled. - * other: No software override. The flow control configuration - * in the EEPROM is used. - */ - switch (hw->fc) { - case E1000_FC_NONE: /* 0 */ - /* Flow control (RX & TX) is completely disabled by a - * software over-ride. - */ - mii_autoneg_adv_reg &= ~(NWAY_AR_ASM_DIR | NWAY_AR_PAUSE); - break; - case E1000_FC_RX_PAUSE: /* 1 */ - /* RX Flow control is enabled, and TX Flow control is - * disabled, by a software over-ride. - */ - /* Since there really isn't a way to advertise that we are - * capable of RX Pause ONLY, we will advertise that we - * support both symmetric and asymmetric RX PAUSE. Later - * (in e1000_config_fc_after_link_up) we will disable the - *hw's ability to send PAUSE frames. - */ - mii_autoneg_adv_reg |= (NWAY_AR_ASM_DIR | NWAY_AR_PAUSE); - break; - case E1000_FC_TX_PAUSE: /* 2 */ - /* TX Flow control is enabled, and RX Flow control is - * disabled, by a software over-ride. - */ - mii_autoneg_adv_reg |= NWAY_AR_ASM_DIR; - mii_autoneg_adv_reg &= ~NWAY_AR_PAUSE; - break; - case E1000_FC_FULL: /* 3 */ - /* Flow control (both RX and TX) is enabled by a software - * over-ride. - */ - mii_autoneg_adv_reg |= (NWAY_AR_ASM_DIR | NWAY_AR_PAUSE); - break; - default: - DEBUGOUT("Flow control param set incorrectly\n"); - return -E1000_ERR_CONFIG; - } - - ret_val = e1000_write_phy_reg(hw, PHY_AUTONEG_ADV, mii_autoneg_adv_reg); - if (ret_val) - return ret_val; - - DEBUGOUT1("Auto-Neg Advertising %x\n", mii_autoneg_adv_reg); - - if (hw->phy_type != e1000_phy_ife) { - ret_val = e1000_write_phy_reg(hw, PHY_1000T_CTRL, mii_1000t_ctrl_reg); - if (ret_val) - return ret_val; - } - - return E1000_SUCCESS; + return E1000_SUCCESS; } -/****************************************************************************** -* Force PHY speed and duplex settings to hw->forced_speed_duplex -* -* hw - Struct containing variables accessed by shared code -******************************************************************************/ +/** + * e1000_phy_force_speed_duplex - force link settings + * @hw: Struct containing variables accessed by shared code + * + * Force PHY speed and duplex settings to hw->forced_speed_duplex + */ static s32 e1000_phy_force_speed_duplex(struct e1000_hw *hw) { - u32 ctrl; - s32 ret_val; - u16 mii_ctrl_reg; - u16 mii_status_reg; - u16 phy_data; - u16 i; - - DEBUGFUNC("e1000_phy_force_speed_duplex"); - - /* Turn off Flow control if we are forcing speed and duplex. */ - hw->fc = E1000_FC_NONE; - - DEBUGOUT1("hw->fc = %d\n", hw->fc); - - /* Read the Device Control Register. */ - ctrl = er32(CTRL); - - /* Set the bits to Force Speed and Duplex in the Device Ctrl Reg. */ - ctrl |= (E1000_CTRL_FRCSPD | E1000_CTRL_FRCDPX); - ctrl &= ~(DEVICE_SPEED_MASK); - - /* Clear the Auto Speed Detect Enable bit. */ - ctrl &= ~E1000_CTRL_ASDE; - - /* Read the MII Control Register. */ - ret_val = e1000_read_phy_reg(hw, PHY_CTRL, &mii_ctrl_reg); - if (ret_val) - return ret_val; - - /* We need to disable autoneg in order to force link and duplex. */ - - mii_ctrl_reg &= ~MII_CR_AUTO_NEG_EN; - - /* Are we forcing Full or Half Duplex? */ - if (hw->forced_speed_duplex == e1000_100_full || - hw->forced_speed_duplex == e1000_10_full) { - /* We want to force full duplex so we SET the full duplex bits in the - * Device and MII Control Registers. - */ - ctrl |= E1000_CTRL_FD; - mii_ctrl_reg |= MII_CR_FULL_DUPLEX; - DEBUGOUT("Full Duplex\n"); - } else { - /* We want to force half duplex so we CLEAR the full duplex bits in - * the Device and MII Control Registers. - */ - ctrl &= ~E1000_CTRL_FD; - mii_ctrl_reg &= ~MII_CR_FULL_DUPLEX; - DEBUGOUT("Half Duplex\n"); - } - - /* Are we forcing 100Mbps??? */ - if (hw->forced_speed_duplex == e1000_100_full || - hw->forced_speed_duplex == e1000_100_half) { - /* Set the 100Mb bit and turn off the 1000Mb and 10Mb bits. */ - ctrl |= E1000_CTRL_SPD_100; - mii_ctrl_reg |= MII_CR_SPEED_100; - mii_ctrl_reg &= ~(MII_CR_SPEED_1000 | MII_CR_SPEED_10); - DEBUGOUT("Forcing 100mb "); - } else { - /* Set the 10Mb bit and turn off the 1000Mb and 100Mb bits. */ - ctrl &= ~(E1000_CTRL_SPD_1000 | E1000_CTRL_SPD_100); - mii_ctrl_reg |= MII_CR_SPEED_10; - mii_ctrl_reg &= ~(MII_CR_SPEED_1000 | MII_CR_SPEED_100); - DEBUGOUT("Forcing 10mb "); - } - - e1000_config_collision_dist(hw); - - /* Write the configured values back to the Device Control Reg. */ - ew32(CTRL, ctrl); - - if ((hw->phy_type == e1000_phy_m88) || - (hw->phy_type == e1000_phy_gg82563)) { - ret_val = e1000_read_phy_reg(hw, M88E1000_PHY_SPEC_CTRL, &phy_data); - if (ret_val) - return ret_val; - - /* Clear Auto-Crossover to force MDI manually. M88E1000 requires MDI - * forced whenever speed are duplex are forced. - */ - phy_data &= ~M88E1000_PSCR_AUTO_X_MODE; - ret_val = e1000_write_phy_reg(hw, M88E1000_PHY_SPEC_CTRL, phy_data); - if (ret_val) - return ret_val; - - DEBUGOUT1("M88E1000 PSCR: %x \n", phy_data); - - /* Need to reset the PHY or these changes will be ignored */ - mii_ctrl_reg |= MII_CR_RESET; - - /* Disable MDI-X support for 10/100 */ - } else if (hw->phy_type == e1000_phy_ife) { - ret_val = e1000_read_phy_reg(hw, IFE_PHY_MDIX_CONTROL, &phy_data); - if (ret_val) - return ret_val; - - phy_data &= ~IFE_PMC_AUTO_MDIX; - phy_data &= ~IFE_PMC_FORCE_MDIX; - - ret_val = e1000_write_phy_reg(hw, IFE_PHY_MDIX_CONTROL, phy_data); - if (ret_val) - return ret_val; - - } else { - /* Clear Auto-Crossover to force MDI manually. IGP requires MDI - * forced whenever speed or duplex are forced. - */ - ret_val = e1000_read_phy_reg(hw, IGP01E1000_PHY_PORT_CTRL, &phy_data); - if (ret_val) - return ret_val; - - phy_data &= ~IGP01E1000_PSCR_AUTO_MDIX; - phy_data &= ~IGP01E1000_PSCR_FORCE_MDI_MDIX; - - ret_val = e1000_write_phy_reg(hw, IGP01E1000_PHY_PORT_CTRL, phy_data); - if (ret_val) - return ret_val; - } - - /* Write back the modified PHY MII control register. */ - ret_val = e1000_write_phy_reg(hw, PHY_CTRL, mii_ctrl_reg); - if (ret_val) - return ret_val; - - udelay(1); - - /* The wait_autoneg_complete flag may be a little misleading here. - * Since we are forcing speed and duplex, Auto-Neg is not enabled. - * But we do want to delay for a period while forcing only so we - * don't generate false No Link messages. So we will wait here - * only if the user has set wait_autoneg_complete to 1, which is - * the default. - */ - if (hw->wait_autoneg_complete) { - /* We will wait for autoneg to complete. */ - DEBUGOUT("Waiting for forced speed/duplex link.\n"); - mii_status_reg = 0; - - /* We will wait for autoneg to complete or 4.5 seconds to expire. */ - for (i = PHY_FORCE_TIME; i > 0; i--) { - /* Read the MII Status Register and wait for Auto-Neg Complete bit - * to be set. - */ - ret_val = e1000_read_phy_reg(hw, PHY_STATUS, &mii_status_reg); - if (ret_val) - return ret_val; - - ret_val = e1000_read_phy_reg(hw, PHY_STATUS, &mii_status_reg); - if (ret_val) - return ret_val; - - if (mii_status_reg & MII_SR_LINK_STATUS) break; - msleep(100); - } - if ((i == 0) && - ((hw->phy_type == e1000_phy_m88) || - (hw->phy_type == e1000_phy_gg82563))) { - /* We didn't get link. Reset the DSP and wait again for link. */ - ret_val = e1000_phy_reset_dsp(hw); - if (ret_val) { - DEBUGOUT("Error Resetting PHY DSP\n"); - return ret_val; - } - } - /* This loop will early-out if the link condition has been met. */ - for (i = PHY_FORCE_TIME; i > 0; i--) { - if (mii_status_reg & MII_SR_LINK_STATUS) break; - msleep(100); - /* Read the MII Status Register and wait for Auto-Neg Complete bit - * to be set. - */ - ret_val = e1000_read_phy_reg(hw, PHY_STATUS, &mii_status_reg); - if (ret_val) - return ret_val; - - ret_val = e1000_read_phy_reg(hw, PHY_STATUS, &mii_status_reg); - if (ret_val) - return ret_val; - } - } - - if (hw->phy_type == e1000_phy_m88) { - /* Because we reset the PHY above, we need to re-force TX_CLK in the - * Extended PHY Specific Control Register to 25MHz clock. This value - * defaults back to a 2.5MHz clock when the PHY is reset. - */ - ret_val = e1000_read_phy_reg(hw, M88E1000_EXT_PHY_SPEC_CTRL, &phy_data); - if (ret_val) - return ret_val; - - phy_data |= M88E1000_EPSCR_TX_CLK_25; - ret_val = e1000_write_phy_reg(hw, M88E1000_EXT_PHY_SPEC_CTRL, phy_data); - if (ret_val) - return ret_val; - - /* In addition, because of the s/w reset above, we need to enable CRS on - * TX. This must be set for both full and half duplex operation. - */ - ret_val = e1000_read_phy_reg(hw, M88E1000_PHY_SPEC_CTRL, &phy_data); - if (ret_val) - return ret_val; - - phy_data |= M88E1000_PSCR_ASSERT_CRS_ON_TX; - ret_val = e1000_write_phy_reg(hw, M88E1000_PHY_SPEC_CTRL, phy_data); - if (ret_val) - return ret_val; - - if ((hw->mac_type == e1000_82544 || hw->mac_type == e1000_82543) && - (!hw->autoneg) && (hw->forced_speed_duplex == e1000_10_full || - hw->forced_speed_duplex == e1000_10_half)) { - ret_val = e1000_polarity_reversal_workaround(hw); - if (ret_val) - return ret_val; - } - } else if (hw->phy_type == e1000_phy_gg82563) { - /* The TX_CLK of the Extended PHY Specific Control Register defaults - * to 2.5MHz on a reset. We need to re-force it back to 25MHz, if - * we're not in a forced 10/duplex configuration. */ - ret_val = e1000_read_phy_reg(hw, GG82563_PHY_MAC_SPEC_CTRL, &phy_data); - if (ret_val) - return ret_val; - - phy_data &= ~GG82563_MSCR_TX_CLK_MASK; - if ((hw->forced_speed_duplex == e1000_10_full) || - (hw->forced_speed_duplex == e1000_10_half)) - phy_data |= GG82563_MSCR_TX_CLK_10MBPS_2_5MHZ; - else - phy_data |= GG82563_MSCR_TX_CLK_100MBPS_25MHZ; - - /* Also due to the reset, we need to enable CRS on Tx. */ - phy_data |= GG82563_MSCR_ASSERT_CRS_ON_TX; - - ret_val = e1000_write_phy_reg(hw, GG82563_PHY_MAC_SPEC_CTRL, phy_data); - if (ret_val) - return ret_val; - } - return E1000_SUCCESS; + u32 ctrl; + s32 ret_val; + u16 mii_ctrl_reg; + u16 mii_status_reg; + u16 phy_data; + u16 i; + + DEBUGFUNC("e1000_phy_force_speed_duplex"); + + /* Turn off Flow control if we are forcing speed and duplex. */ + hw->fc = E1000_FC_NONE; + + DEBUGOUT1("hw->fc = %d\n", hw->fc); + + /* Read the Device Control Register. */ + ctrl = er32(CTRL); + + /* Set the bits to Force Speed and Duplex in the Device Ctrl Reg. */ + ctrl |= (E1000_CTRL_FRCSPD | E1000_CTRL_FRCDPX); + ctrl &= ~(DEVICE_SPEED_MASK); + + /* Clear the Auto Speed Detect Enable bit. */ + ctrl &= ~E1000_CTRL_ASDE; + + /* Read the MII Control Register. */ + ret_val = e1000_read_phy_reg(hw, PHY_CTRL, &mii_ctrl_reg); + if (ret_val) + return ret_val; + + /* We need to disable autoneg in order to force link and duplex. */ + + mii_ctrl_reg &= ~MII_CR_AUTO_NEG_EN; + + /* Are we forcing Full or Half Duplex? */ + if (hw->forced_speed_duplex == e1000_100_full || + hw->forced_speed_duplex == e1000_10_full) { + /* We want to force full duplex so we SET the full duplex bits in the + * Device and MII Control Registers. + */ + ctrl |= E1000_CTRL_FD; + mii_ctrl_reg |= MII_CR_FULL_DUPLEX; + DEBUGOUT("Full Duplex\n"); + } else { + /* We want to force half duplex so we CLEAR the full duplex bits in + * the Device and MII Control Registers. + */ + ctrl &= ~E1000_CTRL_FD; + mii_ctrl_reg &= ~MII_CR_FULL_DUPLEX; + DEBUGOUT("Half Duplex\n"); + } + + /* Are we forcing 100Mbps??? */ + if (hw->forced_speed_duplex == e1000_100_full || + hw->forced_speed_duplex == e1000_100_half) { + /* Set the 100Mb bit and turn off the 1000Mb and 10Mb bits. */ + ctrl |= E1000_CTRL_SPD_100; + mii_ctrl_reg |= MII_CR_SPEED_100; + mii_ctrl_reg &= ~(MII_CR_SPEED_1000 | MII_CR_SPEED_10); + DEBUGOUT("Forcing 100mb "); + } else { + /* Set the 10Mb bit and turn off the 1000Mb and 100Mb bits. */ + ctrl &= ~(E1000_CTRL_SPD_1000 | E1000_CTRL_SPD_100); + mii_ctrl_reg |= MII_CR_SPEED_10; + mii_ctrl_reg &= ~(MII_CR_SPEED_1000 | MII_CR_SPEED_100); + DEBUGOUT("Forcing 10mb "); + } + + e1000_config_collision_dist(hw); + + /* Write the configured values back to the Device Control Reg. */ + ew32(CTRL, ctrl); + + if (hw->phy_type == e1000_phy_m88) { + ret_val = + e1000_read_phy_reg(hw, M88E1000_PHY_SPEC_CTRL, &phy_data); + if (ret_val) + return ret_val; + + /* Clear Auto-Crossover to force MDI manually. M88E1000 requires MDI + * forced whenever speed are duplex are forced. + */ + phy_data &= ~M88E1000_PSCR_AUTO_X_MODE; + ret_val = + e1000_write_phy_reg(hw, M88E1000_PHY_SPEC_CTRL, phy_data); + if (ret_val) + return ret_val; + + DEBUGOUT1("M88E1000 PSCR: %x \n", phy_data); + + /* Need to reset the PHY or these changes will be ignored */ + mii_ctrl_reg |= MII_CR_RESET; + + /* Disable MDI-X support for 10/100 */ + } else { + /* Clear Auto-Crossover to force MDI manually. IGP requires MDI + * forced whenever speed or duplex are forced. + */ + ret_val = + e1000_read_phy_reg(hw, IGP01E1000_PHY_PORT_CTRL, &phy_data); + if (ret_val) + return ret_val; + + phy_data &= ~IGP01E1000_PSCR_AUTO_MDIX; + phy_data &= ~IGP01E1000_PSCR_FORCE_MDI_MDIX; + + ret_val = + e1000_write_phy_reg(hw, IGP01E1000_PHY_PORT_CTRL, phy_data); + if (ret_val) + return ret_val; + } + + /* Write back the modified PHY MII control register. */ + ret_val = e1000_write_phy_reg(hw, PHY_CTRL, mii_ctrl_reg); + if (ret_val) + return ret_val; + + udelay(1); + + /* The wait_autoneg_complete flag may be a little misleading here. + * Since we are forcing speed and duplex, Auto-Neg is not enabled. + * But we do want to delay for a period while forcing only so we + * don't generate false No Link messages. So we will wait here + * only if the user has set wait_autoneg_complete to 1, which is + * the default. + */ + if (hw->wait_autoneg_complete) { + /* We will wait for autoneg to complete. */ + DEBUGOUT("Waiting for forced speed/duplex link.\n"); + mii_status_reg = 0; + + /* We will wait for autoneg to complete or 4.5 seconds to expire. */ + for (i = PHY_FORCE_TIME; i > 0; i--) { + /* Read the MII Status Register and wait for Auto-Neg Complete bit + * to be set. + */ + ret_val = + e1000_read_phy_reg(hw, PHY_STATUS, &mii_status_reg); + if (ret_val) + return ret_val; + + ret_val = + e1000_read_phy_reg(hw, PHY_STATUS, &mii_status_reg); + if (ret_val) + return ret_val; + + if (mii_status_reg & MII_SR_LINK_STATUS) + break; + msleep(100); + } + if ((i == 0) && (hw->phy_type == e1000_phy_m88)) { + /* We didn't get link. Reset the DSP and wait again for link. */ + ret_val = e1000_phy_reset_dsp(hw); + if (ret_val) { + DEBUGOUT("Error Resetting PHY DSP\n"); + return ret_val; + } + } + /* This loop will early-out if the link condition has been met. */ + for (i = PHY_FORCE_TIME; i > 0; i--) { + if (mii_status_reg & MII_SR_LINK_STATUS) + break; + msleep(100); + /* Read the MII Status Register and wait for Auto-Neg Complete bit + * to be set. + */ + ret_val = + e1000_read_phy_reg(hw, PHY_STATUS, &mii_status_reg); + if (ret_val) + return ret_val; + + ret_val = + e1000_read_phy_reg(hw, PHY_STATUS, &mii_status_reg); + if (ret_val) + return ret_val; + } + } + + if (hw->phy_type == e1000_phy_m88) { + /* Because we reset the PHY above, we need to re-force TX_CLK in the + * Extended PHY Specific Control Register to 25MHz clock. This value + * defaults back to a 2.5MHz clock when the PHY is reset. + */ + ret_val = + e1000_read_phy_reg(hw, M88E1000_EXT_PHY_SPEC_CTRL, + &phy_data); + if (ret_val) + return ret_val; + + phy_data |= M88E1000_EPSCR_TX_CLK_25; + ret_val = + e1000_write_phy_reg(hw, M88E1000_EXT_PHY_SPEC_CTRL, + phy_data); + if (ret_val) + return ret_val; + + /* In addition, because of the s/w reset above, we need to enable CRS on + * TX. This must be set for both full and half duplex operation. + */ + ret_val = + e1000_read_phy_reg(hw, M88E1000_PHY_SPEC_CTRL, &phy_data); + if (ret_val) + return ret_val; + + phy_data |= M88E1000_PSCR_ASSERT_CRS_ON_TX; + ret_val = + e1000_write_phy_reg(hw, M88E1000_PHY_SPEC_CTRL, phy_data); + if (ret_val) + return ret_val; + + if ((hw->mac_type == e1000_82544 || hw->mac_type == e1000_82543) + && (!hw->autoneg) + && (hw->forced_speed_duplex == e1000_10_full + || hw->forced_speed_duplex == e1000_10_half)) { + ret_val = e1000_polarity_reversal_workaround(hw); + if (ret_val) + return ret_val; + } + } + return E1000_SUCCESS; } -/****************************************************************************** -* Sets the collision distance in the Transmit Control register -* -* hw - Struct containing variables accessed by shared code -* -* Link should have been established previously. Reads the speed and duplex -* information from the Device Status register. -******************************************************************************/ +/** + * e1000_config_collision_dist - set collision distance register + * @hw: Struct containing variables accessed by shared code + * + * Sets the collision distance in the Transmit Control register. + * Link should have been established previously. Reads the speed and duplex + * information from the Device Status register. + */ void e1000_config_collision_dist(struct e1000_hw *hw) { - u32 tctl, coll_dist; + u32 tctl, coll_dist; - DEBUGFUNC("e1000_config_collision_dist"); + DEBUGFUNC("e1000_config_collision_dist"); - if (hw->mac_type < e1000_82543) - coll_dist = E1000_COLLISION_DISTANCE_82542; - else - coll_dist = E1000_COLLISION_DISTANCE; + if (hw->mac_type < e1000_82543) + coll_dist = E1000_COLLISION_DISTANCE_82542; + else + coll_dist = E1000_COLLISION_DISTANCE; - tctl = er32(TCTL); + tctl = er32(TCTL); - tctl &= ~E1000_TCTL_COLD; - tctl |= coll_dist << E1000_COLD_SHIFT; + tctl &= ~E1000_TCTL_COLD; + tctl |= coll_dist << E1000_COLD_SHIFT; - ew32(TCTL, tctl); - E1000_WRITE_FLUSH(); + ew32(TCTL, tctl); + E1000_WRITE_FLUSH(); } -/****************************************************************************** -* Sets MAC speed and duplex settings to reflect the those in the PHY -* -* hw - Struct containing variables accessed by shared code -* mii_reg - data to write to the MII control register -* -* The contents of the PHY register containing the needed information need to -* be passed in. -******************************************************************************/ +/** + * e1000_config_mac_to_phy - sync phy and mac settings + * @hw: Struct containing variables accessed by shared code + * @mii_reg: data to write to the MII control register + * + * Sets MAC speed and duplex settings to reflect the those in the PHY + * The contents of the PHY register containing the needed information need to + * be passed in. + */ static s32 e1000_config_mac_to_phy(struct e1000_hw *hw) { - u32 ctrl; - s32 ret_val; - u16 phy_data; - - DEBUGFUNC("e1000_config_mac_to_phy"); - - /* 82544 or newer MAC, Auto Speed Detection takes care of - * MAC speed/duplex configuration.*/ - if (hw->mac_type >= e1000_82544) - return E1000_SUCCESS; - - /* Read the Device Control Register and set the bits to Force Speed - * and Duplex. - */ - ctrl = er32(CTRL); - ctrl |= (E1000_CTRL_FRCSPD | E1000_CTRL_FRCDPX); - ctrl &= ~(E1000_CTRL_SPD_SEL | E1000_CTRL_ILOS); - - /* Set up duplex in the Device Control and Transmit Control - * registers depending on negotiated values. - */ - ret_val = e1000_read_phy_reg(hw, M88E1000_PHY_SPEC_STATUS, &phy_data); - if (ret_val) - return ret_val; - - if (phy_data & M88E1000_PSSR_DPLX) - ctrl |= E1000_CTRL_FD; - else - ctrl &= ~E1000_CTRL_FD; - - e1000_config_collision_dist(hw); - - /* Set up speed in the Device Control register depending on - * negotiated values. - */ - if ((phy_data & M88E1000_PSSR_SPEED) == M88E1000_PSSR_1000MBS) - ctrl |= E1000_CTRL_SPD_1000; - else if ((phy_data & M88E1000_PSSR_SPEED) == M88E1000_PSSR_100MBS) - ctrl |= E1000_CTRL_SPD_100; - - /* Write the configured values back to the Device Control Reg. */ - ew32(CTRL, ctrl); - return E1000_SUCCESS; + u32 ctrl; + s32 ret_val; + u16 phy_data; + + DEBUGFUNC("e1000_config_mac_to_phy"); + + /* 82544 or newer MAC, Auto Speed Detection takes care of + * MAC speed/duplex configuration.*/ + if (hw->mac_type >= e1000_82544) + return E1000_SUCCESS; + + /* Read the Device Control Register and set the bits to Force Speed + * and Duplex. + */ + ctrl = er32(CTRL); + ctrl |= (E1000_CTRL_FRCSPD | E1000_CTRL_FRCDPX); + ctrl &= ~(E1000_CTRL_SPD_SEL | E1000_CTRL_ILOS); + + /* Set up duplex in the Device Control and Transmit Control + * registers depending on negotiated values. + */ + ret_val = e1000_read_phy_reg(hw, M88E1000_PHY_SPEC_STATUS, &phy_data); + if (ret_val) + return ret_val; + + if (phy_data & M88E1000_PSSR_DPLX) + ctrl |= E1000_CTRL_FD; + else + ctrl &= ~E1000_CTRL_FD; + + e1000_config_collision_dist(hw); + + /* Set up speed in the Device Control register depending on + * negotiated values. + */ + if ((phy_data & M88E1000_PSSR_SPEED) == M88E1000_PSSR_1000MBS) + ctrl |= E1000_CTRL_SPD_1000; + else if ((phy_data & M88E1000_PSSR_SPEED) == M88E1000_PSSR_100MBS) + ctrl |= E1000_CTRL_SPD_100; + + /* Write the configured values back to the Device Control Reg. */ + ew32(CTRL, ctrl); + return E1000_SUCCESS; } -/****************************************************************************** - * Forces the MAC's flow control settings. - * - * hw - Struct containing variables accessed by shared code +/** + * e1000_force_mac_fc - force flow control settings + * @hw: Struct containing variables accessed by shared code * + * Forces the MAC's flow control settings. * Sets the TFCE and RFCE bits in the device control register to reflect * the adapter settings. TFCE and RFCE need to be explicitly set by * software when a Copper PHY is used because autonegotiation is managed * by the PHY rather than the MAC. Software must also configure these * bits when link is forced on a fiber connection. - *****************************************************************************/ + */ s32 e1000_force_mac_fc(struct e1000_hw *hw) { - u32 ctrl; - - DEBUGFUNC("e1000_force_mac_fc"); - - /* Get the current configuration of the Device Control Register */ - ctrl = er32(CTRL); - - /* Because we didn't get link via the internal auto-negotiation - * mechanism (we either forced link or we got link via PHY - * auto-neg), we have to manually enable/disable transmit an - * receive flow control. - * - * The "Case" statement below enables/disable flow control - * according to the "hw->fc" parameter. - * - * The possible values of the "fc" parameter are: - * 0: Flow control is completely disabled - * 1: Rx flow control is enabled (we can receive pause - * frames but not send pause frames). - * 2: Tx flow control is enabled (we can send pause frames - * frames but we do not receive pause frames). - * 3: Both Rx and TX flow control (symmetric) is enabled. - * other: No other values should be possible at this point. - */ - - switch (hw->fc) { - case E1000_FC_NONE: - ctrl &= (~(E1000_CTRL_TFCE | E1000_CTRL_RFCE)); - break; - case E1000_FC_RX_PAUSE: - ctrl &= (~E1000_CTRL_TFCE); - ctrl |= E1000_CTRL_RFCE; - break; - case E1000_FC_TX_PAUSE: - ctrl &= (~E1000_CTRL_RFCE); - ctrl |= E1000_CTRL_TFCE; - break; - case E1000_FC_FULL: - ctrl |= (E1000_CTRL_TFCE | E1000_CTRL_RFCE); - break; - default: - DEBUGOUT("Flow control param set incorrectly\n"); - return -E1000_ERR_CONFIG; - } - - /* Disable TX Flow Control for 82542 (rev 2.0) */ - if (hw->mac_type == e1000_82542_rev2_0) - ctrl &= (~E1000_CTRL_TFCE); - - ew32(CTRL, ctrl); - return E1000_SUCCESS; + u32 ctrl; + + DEBUGFUNC("e1000_force_mac_fc"); + + /* Get the current configuration of the Device Control Register */ + ctrl = er32(CTRL); + + /* Because we didn't get link via the internal auto-negotiation + * mechanism (we either forced link or we got link via PHY + * auto-neg), we have to manually enable/disable transmit an + * receive flow control. + * + * The "Case" statement below enables/disable flow control + * according to the "hw->fc" parameter. + * + * The possible values of the "fc" parameter are: + * 0: Flow control is completely disabled + * 1: Rx flow control is enabled (we can receive pause + * frames but not send pause frames). + * 2: Tx flow control is enabled (we can send pause frames + * frames but we do not receive pause frames). + * 3: Both Rx and TX flow control (symmetric) is enabled. + * other: No other values should be possible at this point. + */ + + switch (hw->fc) { + case E1000_FC_NONE: + ctrl &= (~(E1000_CTRL_TFCE | E1000_CTRL_RFCE)); + break; + case E1000_FC_RX_PAUSE: + ctrl &= (~E1000_CTRL_TFCE); + ctrl |= E1000_CTRL_RFCE; + break; + case E1000_FC_TX_PAUSE: + ctrl &= (~E1000_CTRL_RFCE); + ctrl |= E1000_CTRL_TFCE; + break; + case E1000_FC_FULL: + ctrl |= (E1000_CTRL_TFCE | E1000_CTRL_RFCE); + break; + default: + DEBUGOUT("Flow control param set incorrectly\n"); + return -E1000_ERR_CONFIG; + } + + /* Disable TX Flow Control for 82542 (rev 2.0) */ + if (hw->mac_type == e1000_82542_rev2_0) + ctrl &= (~E1000_CTRL_TFCE); + + ew32(CTRL, ctrl); + return E1000_SUCCESS; } -/****************************************************************************** - * Configures flow control settings after link is established - * - * hw - Struct containing variables accessed by shared code +/** + * e1000_config_fc_after_link_up - configure flow control after autoneg + * @hw: Struct containing variables accessed by shared code * + * Configures flow control settings after link is established * Should be called immediately after a valid link has been established. * Forces MAC flow control settings if link was forced. When in MII/GMII mode * and autonegotiation is enabled, the MAC flow control settings will be set * based on the flow control negotiated by the PHY. In TBI mode, the TFCE - * and RFCE bits will be automaticaly set to the negotiated flow control mode. - *****************************************************************************/ + * and RFCE bits will be automatically set to the negotiated flow control mode. + */ static s32 e1000_config_fc_after_link_up(struct e1000_hw *hw) { - s32 ret_val; - u16 mii_status_reg; - u16 mii_nway_adv_reg; - u16 mii_nway_lp_ability_reg; - u16 speed; - u16 duplex; - - DEBUGFUNC("e1000_config_fc_after_link_up"); - - /* Check for the case where we have fiber media and auto-neg failed - * so we had to force link. In this case, we need to force the - * configuration of the MAC to match the "fc" parameter. - */ - if (((hw->media_type == e1000_media_type_fiber) && (hw->autoneg_failed)) || - ((hw->media_type == e1000_media_type_internal_serdes) && - (hw->autoneg_failed)) || - ((hw->media_type == e1000_media_type_copper) && (!hw->autoneg))) { - ret_val = e1000_force_mac_fc(hw); - if (ret_val) { - DEBUGOUT("Error forcing flow control settings\n"); - return ret_val; - } - } - - /* Check for the case where we have copper media and auto-neg is - * enabled. In this case, we need to check and see if Auto-Neg - * has completed, and if so, how the PHY and link partner has - * flow control configured. - */ - if ((hw->media_type == e1000_media_type_copper) && hw->autoneg) { - /* Read the MII Status Register and check to see if AutoNeg - * has completed. We read this twice because this reg has - * some "sticky" (latched) bits. - */ - ret_val = e1000_read_phy_reg(hw, PHY_STATUS, &mii_status_reg); - if (ret_val) - return ret_val; - ret_val = e1000_read_phy_reg(hw, PHY_STATUS, &mii_status_reg); - if (ret_val) - return ret_val; - - if (mii_status_reg & MII_SR_AUTONEG_COMPLETE) { - /* The AutoNeg process has completed, so we now need to - * read both the Auto Negotiation Advertisement Register - * (Address 4) and the Auto_Negotiation Base Page Ability - * Register (Address 5) to determine how flow control was - * negotiated. - */ - ret_val = e1000_read_phy_reg(hw, PHY_AUTONEG_ADV, - &mii_nway_adv_reg); - if (ret_val) - return ret_val; - ret_val = e1000_read_phy_reg(hw, PHY_LP_ABILITY, - &mii_nway_lp_ability_reg); - if (ret_val) - return ret_val; - - /* Two bits in the Auto Negotiation Advertisement Register - * (Address 4) and two bits in the Auto Negotiation Base - * Page Ability Register (Address 5) determine flow control - * for both the PHY and the link partner. The following - * table, taken out of the IEEE 802.3ab/D6.0 dated March 25, - * 1999, describes these PAUSE resolution bits and how flow - * control is determined based upon these settings. - * NOTE: DC = Don't Care - * - * LOCAL DEVICE | LINK PARTNER - * PAUSE | ASM_DIR | PAUSE | ASM_DIR | NIC Resolution - *-------|---------|-------|---------|-------------------- - * 0 | 0 | DC | DC | E1000_FC_NONE - * 0 | 1 | 0 | DC | E1000_FC_NONE - * 0 | 1 | 1 | 0 | E1000_FC_NONE - * 0 | 1 | 1 | 1 | E1000_FC_TX_PAUSE - * 1 | 0 | 0 | DC | E1000_FC_NONE - * 1 | DC | 1 | DC | E1000_FC_FULL - * 1 | 1 | 0 | 0 | E1000_FC_NONE - * 1 | 1 | 0 | 1 | E1000_FC_RX_PAUSE - * - */ - /* Are both PAUSE bits set to 1? If so, this implies - * Symmetric Flow Control is enabled at both ends. The - * ASM_DIR bits are irrelevant per the spec. - * - * For Symmetric Flow Control: - * - * LOCAL DEVICE | LINK PARTNER - * PAUSE | ASM_DIR | PAUSE | ASM_DIR | Result - *-------|---------|-------|---------|-------------------- - * 1 | DC | 1 | DC | E1000_FC_FULL - * - */ - if ((mii_nway_adv_reg & NWAY_AR_PAUSE) && - (mii_nway_lp_ability_reg & NWAY_LPAR_PAUSE)) { - /* Now we need to check if the user selected RX ONLY - * of pause frames. In this case, we had to advertise - * FULL flow control because we could not advertise RX - * ONLY. Hence, we must now check to see if we need to - * turn OFF the TRANSMISSION of PAUSE frames. - */ - if (hw->original_fc == E1000_FC_FULL) { - hw->fc = E1000_FC_FULL; - DEBUGOUT("Flow Control = FULL.\n"); - } else { - hw->fc = E1000_FC_RX_PAUSE; - DEBUGOUT("Flow Control = RX PAUSE frames only.\n"); - } - } - /* For receiving PAUSE frames ONLY. - * - * LOCAL DEVICE | LINK PARTNER - * PAUSE | ASM_DIR | PAUSE | ASM_DIR | Result - *-------|---------|-------|---------|-------------------- - * 0 | 1 | 1 | 1 | E1000_FC_TX_PAUSE - * - */ - else if (!(mii_nway_adv_reg & NWAY_AR_PAUSE) && - (mii_nway_adv_reg & NWAY_AR_ASM_DIR) && - (mii_nway_lp_ability_reg & NWAY_LPAR_PAUSE) && - (mii_nway_lp_ability_reg & NWAY_LPAR_ASM_DIR)) { - hw->fc = E1000_FC_TX_PAUSE; - DEBUGOUT("Flow Control = TX PAUSE frames only.\n"); - } - /* For transmitting PAUSE frames ONLY. - * - * LOCAL DEVICE | LINK PARTNER - * PAUSE | ASM_DIR | PAUSE | ASM_DIR | Result - *-------|---------|-------|---------|-------------------- - * 1 | 1 | 0 | 1 | E1000_FC_RX_PAUSE - * - */ - else if ((mii_nway_adv_reg & NWAY_AR_PAUSE) && - (mii_nway_adv_reg & NWAY_AR_ASM_DIR) && - !(mii_nway_lp_ability_reg & NWAY_LPAR_PAUSE) && - (mii_nway_lp_ability_reg & NWAY_LPAR_ASM_DIR)) { - hw->fc = E1000_FC_RX_PAUSE; - DEBUGOUT("Flow Control = RX PAUSE frames only.\n"); - } - /* Per the IEEE spec, at this point flow control should be - * disabled. However, we want to consider that we could - * be connected to a legacy switch that doesn't advertise - * desired flow control, but can be forced on the link - * partner. So if we advertised no flow control, that is - * what we will resolve to. If we advertised some kind of - * receive capability (Rx Pause Only or Full Flow Control) - * and the link partner advertised none, we will configure - * ourselves to enable Rx Flow Control only. We can do - * this safely for two reasons: If the link partner really - * didn't want flow control enabled, and we enable Rx, no - * harm done since we won't be receiving any PAUSE frames - * anyway. If the intent on the link partner was to have - * flow control enabled, then by us enabling RX only, we - * can at least receive pause frames and process them. - * This is a good idea because in most cases, since we are - * predominantly a server NIC, more times than not we will - * be asked to delay transmission of packets than asking - * our link partner to pause transmission of frames. - */ - else if ((hw->original_fc == E1000_FC_NONE || - hw->original_fc == E1000_FC_TX_PAUSE) || - hw->fc_strict_ieee) { - hw->fc = E1000_FC_NONE; - DEBUGOUT("Flow Control = NONE.\n"); - } else { - hw->fc = E1000_FC_RX_PAUSE; - DEBUGOUT("Flow Control = RX PAUSE frames only.\n"); - } - - /* Now we need to do one last check... If we auto- - * negotiated to HALF DUPLEX, flow control should not be - * enabled per IEEE 802.3 spec. - */ - ret_val = e1000_get_speed_and_duplex(hw, &speed, &duplex); - if (ret_val) { - DEBUGOUT("Error getting link speed and duplex\n"); - return ret_val; - } - - if (duplex == HALF_DUPLEX) - hw->fc = E1000_FC_NONE; - - /* Now we call a subroutine to actually force the MAC - * controller to use the correct flow control settings. - */ - ret_val = e1000_force_mac_fc(hw); - if (ret_val) { - DEBUGOUT("Error forcing flow control settings\n"); - return ret_val; - } - } else { - DEBUGOUT("Copper PHY and Auto Neg has not completed.\n"); - } - } - return E1000_SUCCESS; + s32 ret_val; + u16 mii_status_reg; + u16 mii_nway_adv_reg; + u16 mii_nway_lp_ability_reg; + u16 speed; + u16 duplex; + + DEBUGFUNC("e1000_config_fc_after_link_up"); + + /* Check for the case where we have fiber media and auto-neg failed + * so we had to force link. In this case, we need to force the + * configuration of the MAC to match the "fc" parameter. + */ + if (((hw->media_type == e1000_media_type_fiber) && (hw->autoneg_failed)) + || ((hw->media_type == e1000_media_type_internal_serdes) + && (hw->autoneg_failed)) + || ((hw->media_type == e1000_media_type_copper) + && (!hw->autoneg))) { + ret_val = e1000_force_mac_fc(hw); + if (ret_val) { + DEBUGOUT("Error forcing flow control settings\n"); + return ret_val; + } + } + + /* Check for the case where we have copper media and auto-neg is + * enabled. In this case, we need to check and see if Auto-Neg + * has completed, and if so, how the PHY and link partner has + * flow control configured. + */ + if ((hw->media_type == e1000_media_type_copper) && hw->autoneg) { + /* Read the MII Status Register and check to see if AutoNeg + * has completed. We read this twice because this reg has + * some "sticky" (latched) bits. + */ + ret_val = e1000_read_phy_reg(hw, PHY_STATUS, &mii_status_reg); + if (ret_val) + return ret_val; + ret_val = e1000_read_phy_reg(hw, PHY_STATUS, &mii_status_reg); + if (ret_val) + return ret_val; + + if (mii_status_reg & MII_SR_AUTONEG_COMPLETE) { + /* The AutoNeg process has completed, so we now need to + * read both the Auto Negotiation Advertisement Register + * (Address 4) and the Auto_Negotiation Base Page Ability + * Register (Address 5) to determine how flow control was + * negotiated. + */ + ret_val = e1000_read_phy_reg(hw, PHY_AUTONEG_ADV, + &mii_nway_adv_reg); + if (ret_val) + return ret_val; + ret_val = e1000_read_phy_reg(hw, PHY_LP_ABILITY, + &mii_nway_lp_ability_reg); + if (ret_val) + return ret_val; + + /* Two bits in the Auto Negotiation Advertisement Register + * (Address 4) and two bits in the Auto Negotiation Base + * Page Ability Register (Address 5) determine flow control + * for both the PHY and the link partner. The following + * table, taken out of the IEEE 802.3ab/D6.0 dated March 25, + * 1999, describes these PAUSE resolution bits and how flow + * control is determined based upon these settings. + * NOTE: DC = Don't Care + * + * LOCAL DEVICE | LINK PARTNER + * PAUSE | ASM_DIR | PAUSE | ASM_DIR | NIC Resolution + *-------|---------|-------|---------|-------------------- + * 0 | 0 | DC | DC | E1000_FC_NONE + * 0 | 1 | 0 | DC | E1000_FC_NONE + * 0 | 1 | 1 | 0 | E1000_FC_NONE + * 0 | 1 | 1 | 1 | E1000_FC_TX_PAUSE + * 1 | 0 | 0 | DC | E1000_FC_NONE + * 1 | DC | 1 | DC | E1000_FC_FULL + * 1 | 1 | 0 | 0 | E1000_FC_NONE + * 1 | 1 | 0 | 1 | E1000_FC_RX_PAUSE + * + */ + /* Are both PAUSE bits set to 1? If so, this implies + * Symmetric Flow Control is enabled at both ends. The + * ASM_DIR bits are irrelevant per the spec. + * + * For Symmetric Flow Control: + * + * LOCAL DEVICE | LINK PARTNER + * PAUSE | ASM_DIR | PAUSE | ASM_DIR | Result + *-------|---------|-------|---------|-------------------- + * 1 | DC | 1 | DC | E1000_FC_FULL + * + */ + if ((mii_nway_adv_reg & NWAY_AR_PAUSE) && + (mii_nway_lp_ability_reg & NWAY_LPAR_PAUSE)) { + /* Now we need to check if the user selected RX ONLY + * of pause frames. In this case, we had to advertise + * FULL flow control because we could not advertise RX + * ONLY. Hence, we must now check to see if we need to + * turn OFF the TRANSMISSION of PAUSE frames. + */ + if (hw->original_fc == E1000_FC_FULL) { + hw->fc = E1000_FC_FULL; + DEBUGOUT("Flow Control = FULL.\n"); + } else { + hw->fc = E1000_FC_RX_PAUSE; + DEBUGOUT + ("Flow Control = RX PAUSE frames only.\n"); + } + } + /* For receiving PAUSE frames ONLY. + * + * LOCAL DEVICE | LINK PARTNER + * PAUSE | ASM_DIR | PAUSE | ASM_DIR | Result + *-------|---------|-------|---------|-------------------- + * 0 | 1 | 1 | 1 | E1000_FC_TX_PAUSE + * + */ + else if (!(mii_nway_adv_reg & NWAY_AR_PAUSE) && + (mii_nway_adv_reg & NWAY_AR_ASM_DIR) && + (mii_nway_lp_ability_reg & NWAY_LPAR_PAUSE) && + (mii_nway_lp_ability_reg & NWAY_LPAR_ASM_DIR)) + { + hw->fc = E1000_FC_TX_PAUSE; + DEBUGOUT + ("Flow Control = TX PAUSE frames only.\n"); + } + /* For transmitting PAUSE frames ONLY. + * + * LOCAL DEVICE | LINK PARTNER + * PAUSE | ASM_DIR | PAUSE | ASM_DIR | Result + *-------|---------|-------|---------|-------------------- + * 1 | 1 | 0 | 1 | E1000_FC_RX_PAUSE + * + */ + else if ((mii_nway_adv_reg & NWAY_AR_PAUSE) && + (mii_nway_adv_reg & NWAY_AR_ASM_DIR) && + !(mii_nway_lp_ability_reg & NWAY_LPAR_PAUSE) && + (mii_nway_lp_ability_reg & NWAY_LPAR_ASM_DIR)) + { + hw->fc = E1000_FC_RX_PAUSE; + DEBUGOUT + ("Flow Control = RX PAUSE frames only.\n"); + } + /* Per the IEEE spec, at this point flow control should be + * disabled. However, we want to consider that we could + * be connected to a legacy switch that doesn't advertise + * desired flow control, but can be forced on the link + * partner. So if we advertised no flow control, that is + * what we will resolve to. If we advertised some kind of + * receive capability (Rx Pause Only or Full Flow Control) + * and the link partner advertised none, we will configure + * ourselves to enable Rx Flow Control only. We can do + * this safely for two reasons: If the link partner really + * didn't want flow control enabled, and we enable Rx, no + * harm done since we won't be receiving any PAUSE frames + * anyway. If the intent on the link partner was to have + * flow control enabled, then by us enabling RX only, we + * can at least receive pause frames and process them. + * This is a good idea because in most cases, since we are + * predominantly a server NIC, more times than not we will + * be asked to delay transmission of packets than asking + * our link partner to pause transmission of frames. + */ + else if ((hw->original_fc == E1000_FC_NONE || + hw->original_fc == E1000_FC_TX_PAUSE) || + hw->fc_strict_ieee) { + hw->fc = E1000_FC_NONE; + DEBUGOUT("Flow Control = NONE.\n"); + } else { + hw->fc = E1000_FC_RX_PAUSE; + DEBUGOUT + ("Flow Control = RX PAUSE frames only.\n"); + } + + /* Now we need to do one last check... If we auto- + * negotiated to HALF DUPLEX, flow control should not be + * enabled per IEEE 802.3 spec. + */ + ret_val = + e1000_get_speed_and_duplex(hw, &speed, &duplex); + if (ret_val) { + DEBUGOUT + ("Error getting link speed and duplex\n"); + return ret_val; + } + + if (duplex == HALF_DUPLEX) + hw->fc = E1000_FC_NONE; + + /* Now we call a subroutine to actually force the MAC + * controller to use the correct flow control settings. + */ + ret_val = e1000_force_mac_fc(hw); + if (ret_val) { + DEBUGOUT + ("Error forcing flow control settings\n"); + return ret_val; + } + } else { + DEBUGOUT + ("Copper PHY and Auto Neg has not completed.\n"); + } + } + return E1000_SUCCESS; } -/****************************************************************************** - * Checks to see if the link status of the hardware has changed. +/** + * e1000_check_for_serdes_link_generic - Check for link (Serdes) + * @hw: pointer to the HW structure * - * hw - Struct containing variables accessed by shared code + * Checks for link up on the hardware. If link is not up and we have + * a signal, then we need to force link up. + */ +static s32 e1000_check_for_serdes_link_generic(struct e1000_hw *hw) +{ + u32 rxcw; + u32 ctrl; + u32 status; + s32 ret_val = E1000_SUCCESS; + + DEBUGFUNC("e1000_check_for_serdes_link_generic"); + + ctrl = er32(CTRL); + status = er32(STATUS); + rxcw = er32(RXCW); + + /* + * If we don't have link (auto-negotiation failed or link partner + * cannot auto-negotiate), and our link partner is not trying to + * auto-negotiate with us (we are receiving idles or data), + * we need to force link up. We also need to give auto-negotiation + * time to complete. + */ + /* (ctrl & E1000_CTRL_SWDPIN1) == 1 == have signal */ + if ((!(status & E1000_STATUS_LU)) && (!(rxcw & E1000_RXCW_C))) { + if (hw->autoneg_failed == 0) { + hw->autoneg_failed = 1; + goto out; + } + DEBUGOUT("NOT RXing /C/, disable AutoNeg and force link.\n"); + + /* Disable auto-negotiation in the TXCW register */ + ew32(TXCW, (hw->txcw & ~E1000_TXCW_ANE)); + + /* Force link-up and also force full-duplex. */ + ctrl = er32(CTRL); + ctrl |= (E1000_CTRL_SLU | E1000_CTRL_FD); + ew32(CTRL, ctrl); + + /* Configure Flow Control after forcing link up. */ + ret_val = e1000_config_fc_after_link_up(hw); + if (ret_val) { + DEBUGOUT("Error configuring flow control\n"); + goto out; + } + } else if ((ctrl & E1000_CTRL_SLU) && (rxcw & E1000_RXCW_C)) { + /* + * If we are forcing link and we are receiving /C/ ordered + * sets, re-enable auto-negotiation in the TXCW register + * and disable forced link in the Device Control register + * in an attempt to auto-negotiate with our link partner. + */ + DEBUGOUT("RXing /C/, enable AutoNeg and stop forcing link.\n"); + ew32(TXCW, hw->txcw); + ew32(CTRL, (ctrl & ~E1000_CTRL_SLU)); + + hw->serdes_has_link = true; + } else if (!(E1000_TXCW_ANE & er32(TXCW))) { + /* + * If we force link for non-auto-negotiation switch, check + * link status based on MAC synchronization for internal + * serdes media type. + */ + /* SYNCH bit and IV bit are sticky. */ + udelay(10); + rxcw = er32(RXCW); + if (rxcw & E1000_RXCW_SYNCH) { + if (!(rxcw & E1000_RXCW_IV)) { + hw->serdes_has_link = true; + DEBUGOUT("SERDES: Link up - forced.\n"); + } + } else { + hw->serdes_has_link = false; + DEBUGOUT("SERDES: Link down - force failed.\n"); + } + } + + if (E1000_TXCW_ANE & er32(TXCW)) { + status = er32(STATUS); + if (status & E1000_STATUS_LU) { + /* SYNCH bit and IV bit are sticky, so reread rxcw. */ + udelay(10); + rxcw = er32(RXCW); + if (rxcw & E1000_RXCW_SYNCH) { + if (!(rxcw & E1000_RXCW_IV)) { + hw->serdes_has_link = true; + DEBUGOUT("SERDES: Link up - autoneg " + "completed successfully.\n"); + } else { + hw->serdes_has_link = false; + DEBUGOUT("SERDES: Link down - invalid" + "codewords detected in autoneg.\n"); + } + } else { + hw->serdes_has_link = false; + DEBUGOUT("SERDES: Link down - no sync.\n"); + } + } else { + hw->serdes_has_link = false; + DEBUGOUT("SERDES: Link down - autoneg failed\n"); + } + } + + out: + return ret_val; +} + +/** + * e1000_check_for_link + * @hw: Struct containing variables accessed by shared code * + * Checks to see if the link status of the hardware has changed. * Called by any function that needs to check the link status of the adapter. - *****************************************************************************/ + */ s32 e1000_check_for_link(struct e1000_hw *hw) { - u32 rxcw = 0; - u32 ctrl; - u32 status; - u32 rctl; - u32 icr; - u32 signal = 0; - s32 ret_val; - u16 phy_data; - - DEBUGFUNC("e1000_check_for_link"); - - ctrl = er32(CTRL); - status = er32(STATUS); - - /* On adapters with a MAC newer than 82544, SW Defineable pin 1 will be - * set when the optics detect a signal. On older adapters, it will be - * cleared when there is a signal. This applies to fiber media only. - */ - if ((hw->media_type == e1000_media_type_fiber) || - (hw->media_type == e1000_media_type_internal_serdes)) { - rxcw = er32(RXCW); - - if (hw->media_type == e1000_media_type_fiber) { - signal = (hw->mac_type > e1000_82544) ? E1000_CTRL_SWDPIN1 : 0; - if (status & E1000_STATUS_LU) - hw->get_link_status = false; - } - } - - /* If we have a copper PHY then we only want to go out to the PHY - * registers to see if Auto-Neg has completed and/or if our link - * status has changed. The get_link_status flag will be set if we - * receive a Link Status Change interrupt or we have Rx Sequence - * Errors. - */ - if ((hw->media_type == e1000_media_type_copper) && hw->get_link_status) { - /* First we want to see if the MII Status Register reports - * link. If so, then we want to get the current speed/duplex - * of the PHY. - * Read the register twice since the link bit is sticky. - */ - ret_val = e1000_read_phy_reg(hw, PHY_STATUS, &phy_data); - if (ret_val) - return ret_val; - ret_val = e1000_read_phy_reg(hw, PHY_STATUS, &phy_data); - if (ret_val) - return ret_val; - - if (phy_data & MII_SR_LINK_STATUS) { - hw->get_link_status = false; - /* Check if there was DownShift, must be checked immediately after - * link-up */ - e1000_check_downshift(hw); - - /* If we are on 82544 or 82543 silicon and speed/duplex - * are forced to 10H or 10F, then we will implement the polarity - * reversal workaround. We disable interrupts first, and upon - * returning, place the devices interrupt state to its previous - * value except for the link status change interrupt which will - * happen due to the execution of this workaround. - */ - - if ((hw->mac_type == e1000_82544 || hw->mac_type == e1000_82543) && - (!hw->autoneg) && - (hw->forced_speed_duplex == e1000_10_full || - hw->forced_speed_duplex == e1000_10_half)) { - ew32(IMC, 0xffffffff); - ret_val = e1000_polarity_reversal_workaround(hw); - icr = er32(ICR); - ew32(ICS, (icr & ~E1000_ICS_LSC)); - ew32(IMS, IMS_ENABLE_MASK); - } - - } else { - /* No link detected */ - e1000_config_dsp_after_link_change(hw, false); - return 0; - } - - /* If we are forcing speed/duplex, then we simply return since - * we have already determined whether we have link or not. - */ - if (!hw->autoneg) return -E1000_ERR_CONFIG; - - /* optimize the dsp settings for the igp phy */ - e1000_config_dsp_after_link_change(hw, true); - - /* We have a M88E1000 PHY and Auto-Neg is enabled. If we - * have Si on board that is 82544 or newer, Auto - * Speed Detection takes care of MAC speed/duplex - * configuration. So we only need to configure Collision - * Distance in the MAC. Otherwise, we need to force - * speed/duplex on the MAC to the current PHY speed/duplex - * settings. - */ - if (hw->mac_type >= e1000_82544) - e1000_config_collision_dist(hw); - else { - ret_val = e1000_config_mac_to_phy(hw); - if (ret_val) { - DEBUGOUT("Error configuring MAC to PHY settings\n"); - return ret_val; - } - } - - /* Configure Flow Control now that Auto-Neg has completed. First, we - * need to restore the desired flow control settings because we may - * have had to re-autoneg with a different link partner. - */ - ret_val = e1000_config_fc_after_link_up(hw); - if (ret_val) { - DEBUGOUT("Error configuring flow control\n"); - return ret_val; - } - - /* At this point we know that we are on copper and we have - * auto-negotiated link. These are conditions for checking the link - * partner capability register. We use the link speed to determine if - * TBI compatibility needs to be turned on or off. If the link is not - * at gigabit speed, then TBI compatibility is not needed. If we are - * at gigabit speed, we turn on TBI compatibility. - */ - if (hw->tbi_compatibility_en) { - u16 speed, duplex; - ret_val = e1000_get_speed_and_duplex(hw, &speed, &duplex); - if (ret_val) { - DEBUGOUT("Error getting link speed and duplex\n"); - return ret_val; - } - if (speed != SPEED_1000) { - /* If link speed is not set to gigabit speed, we do not need - * to enable TBI compatibility. - */ - if (hw->tbi_compatibility_on) { - /* If we previously were in the mode, turn it off. */ - rctl = er32(RCTL); - rctl &= ~E1000_RCTL_SBP; - ew32(RCTL, rctl); - hw->tbi_compatibility_on = false; - } - } else { - /* If TBI compatibility is was previously off, turn it on. For - * compatibility with a TBI link partner, we will store bad - * packets. Some frames have an additional byte on the end and - * will look like CRC errors to the hardware. - */ - if (!hw->tbi_compatibility_on) { - hw->tbi_compatibility_on = true; - rctl = er32(RCTL); - rctl |= E1000_RCTL_SBP; - ew32(RCTL, rctl); - } - } - } - } - /* If we don't have link (auto-negotiation failed or link partner cannot - * auto-negotiate), the cable is plugged in (we have signal), and our - * link partner is not trying to auto-negotiate with us (we are receiving - * idles or data), we need to force link up. We also need to give - * auto-negotiation time to complete, in case the cable was just plugged - * in. The autoneg_failed flag does this. - */ - else if ((((hw->media_type == e1000_media_type_fiber) && - ((ctrl & E1000_CTRL_SWDPIN1) == signal)) || - (hw->media_type == e1000_media_type_internal_serdes)) && - (!(status & E1000_STATUS_LU)) && - (!(rxcw & E1000_RXCW_C))) { - if (hw->autoneg_failed == 0) { - hw->autoneg_failed = 1; - return 0; - } - DEBUGOUT("NOT RXing /C/, disable AutoNeg and force link.\n"); - - /* Disable auto-negotiation in the TXCW register */ - ew32(TXCW, (hw->txcw & ~E1000_TXCW_ANE)); - - /* Force link-up and also force full-duplex. */ - ctrl = er32(CTRL); - ctrl |= (E1000_CTRL_SLU | E1000_CTRL_FD); - ew32(CTRL, ctrl); - - /* Configure Flow Control after forcing link up. */ - ret_val = e1000_config_fc_after_link_up(hw); - if (ret_val) { - DEBUGOUT("Error configuring flow control\n"); - return ret_val; - } - } - /* If we are forcing link and we are receiving /C/ ordered sets, re-enable - * auto-negotiation in the TXCW register and disable forced link in the - * Device Control register in an attempt to auto-negotiate with our link - * partner. - */ - else if (((hw->media_type == e1000_media_type_fiber) || - (hw->media_type == e1000_media_type_internal_serdes)) && - (ctrl & E1000_CTRL_SLU) && (rxcw & E1000_RXCW_C)) { - DEBUGOUT("RXing /C/, enable AutoNeg and stop forcing link.\n"); - ew32(TXCW, hw->txcw); - ew32(CTRL, (ctrl & ~E1000_CTRL_SLU)); - - hw->serdes_link_down = false; - } - /* If we force link for non-auto-negotiation switch, check link status - * based on MAC synchronization for internal serdes media type. - */ - else if ((hw->media_type == e1000_media_type_internal_serdes) && - !(E1000_TXCW_ANE & er32(TXCW))) { - /* SYNCH bit and IV bit are sticky. */ - udelay(10); - if (E1000_RXCW_SYNCH & er32(RXCW)) { - if (!(rxcw & E1000_RXCW_IV)) { - hw->serdes_link_down = false; - DEBUGOUT("SERDES: Link is up.\n"); - } - } else { - hw->serdes_link_down = true; - DEBUGOUT("SERDES: Link is down.\n"); - } - } - if ((hw->media_type == e1000_media_type_internal_serdes) && - (E1000_TXCW_ANE & er32(TXCW))) { - hw->serdes_link_down = !(E1000_STATUS_LU & er32(STATUS)); - } - return E1000_SUCCESS; + u32 rxcw = 0; + u32 ctrl; + u32 status; + u32 rctl; + u32 icr; + u32 signal = 0; + s32 ret_val; + u16 phy_data; + + DEBUGFUNC("e1000_check_for_link"); + + ctrl = er32(CTRL); + status = er32(STATUS); + + /* On adapters with a MAC newer than 82544, SW Definable pin 1 will be + * set when the optics detect a signal. On older adapters, it will be + * cleared when there is a signal. This applies to fiber media only. + */ + if ((hw->media_type == e1000_media_type_fiber) || + (hw->media_type == e1000_media_type_internal_serdes)) { + rxcw = er32(RXCW); + + if (hw->media_type == e1000_media_type_fiber) { + signal = + (hw->mac_type > + e1000_82544) ? E1000_CTRL_SWDPIN1 : 0; + if (status & E1000_STATUS_LU) + hw->get_link_status = false; + } + } + + /* If we have a copper PHY then we only want to go out to the PHY + * registers to see if Auto-Neg has completed and/or if our link + * status has changed. The get_link_status flag will be set if we + * receive a Link Status Change interrupt or we have Rx Sequence + * Errors. + */ + if ((hw->media_type == e1000_media_type_copper) && hw->get_link_status) { + /* First we want to see if the MII Status Register reports + * link. If so, then we want to get the current speed/duplex + * of the PHY. + * Read the register twice since the link bit is sticky. + */ + ret_val = e1000_read_phy_reg(hw, PHY_STATUS, &phy_data); + if (ret_val) + return ret_val; + ret_val = e1000_read_phy_reg(hw, PHY_STATUS, &phy_data); + if (ret_val) + return ret_val; + + if (phy_data & MII_SR_LINK_STATUS) { + hw->get_link_status = false; + /* Check if there was DownShift, must be checked immediately after + * link-up */ + e1000_check_downshift(hw); + + /* If we are on 82544 or 82543 silicon and speed/duplex + * are forced to 10H or 10F, then we will implement the polarity + * reversal workaround. We disable interrupts first, and upon + * returning, place the devices interrupt state to its previous + * value except for the link status change interrupt which will + * happen due to the execution of this workaround. + */ + + if ((hw->mac_type == e1000_82544 + || hw->mac_type == e1000_82543) && (!hw->autoneg) + && (hw->forced_speed_duplex == e1000_10_full + || hw->forced_speed_duplex == e1000_10_half)) { + ew32(IMC, 0xffffffff); + ret_val = + e1000_polarity_reversal_workaround(hw); + icr = er32(ICR); + ew32(ICS, (icr & ~E1000_ICS_LSC)); + ew32(IMS, IMS_ENABLE_MASK); + } + + } else { + /* No link detected */ + e1000_config_dsp_after_link_change(hw, false); + return 0; + } + + /* If we are forcing speed/duplex, then we simply return since + * we have already determined whether we have link or not. + */ + if (!hw->autoneg) + return -E1000_ERR_CONFIG; + + /* optimize the dsp settings for the igp phy */ + e1000_config_dsp_after_link_change(hw, true); + + /* We have a M88E1000 PHY and Auto-Neg is enabled. If we + * have Si on board that is 82544 or newer, Auto + * Speed Detection takes care of MAC speed/duplex + * configuration. So we only need to configure Collision + * Distance in the MAC. Otherwise, we need to force + * speed/duplex on the MAC to the current PHY speed/duplex + * settings. + */ + if (hw->mac_type >= e1000_82544) + e1000_config_collision_dist(hw); + else { + ret_val = e1000_config_mac_to_phy(hw); + if (ret_val) { + DEBUGOUT + ("Error configuring MAC to PHY settings\n"); + return ret_val; + } + } + + /* Configure Flow Control now that Auto-Neg has completed. First, we + * need to restore the desired flow control settings because we may + * have had to re-autoneg with a different link partner. + */ + ret_val = e1000_config_fc_after_link_up(hw); + if (ret_val) { + DEBUGOUT("Error configuring flow control\n"); + return ret_val; + } + + /* At this point we know that we are on copper and we have + * auto-negotiated link. These are conditions for checking the link + * partner capability register. We use the link speed to determine if + * TBI compatibility needs to be turned on or off. If the link is not + * at gigabit speed, then TBI compatibility is not needed. If we are + * at gigabit speed, we turn on TBI compatibility. + */ + if (hw->tbi_compatibility_en) { + u16 speed, duplex; + ret_val = + e1000_get_speed_and_duplex(hw, &speed, &duplex); + if (ret_val) { + DEBUGOUT + ("Error getting link speed and duplex\n"); + return ret_val; + } + if (speed != SPEED_1000) { + /* If link speed is not set to gigabit speed, we do not need + * to enable TBI compatibility. + */ + if (hw->tbi_compatibility_on) { + /* If we previously were in the mode, turn it off. */ + rctl = er32(RCTL); + rctl &= ~E1000_RCTL_SBP; + ew32(RCTL, rctl); + hw->tbi_compatibility_on = false; + } + } else { + /* If TBI compatibility is was previously off, turn it on. For + * compatibility with a TBI link partner, we will store bad + * packets. Some frames have an additional byte on the end and + * will look like CRC errors to to the hardware. + */ + if (!hw->tbi_compatibility_on) { + hw->tbi_compatibility_on = true; + rctl = er32(RCTL); + rctl |= E1000_RCTL_SBP; + ew32(RCTL, rctl); + } + } + } + } + + if ((hw->media_type == e1000_media_type_fiber) || + (hw->media_type == e1000_media_type_internal_serdes)) + e1000_check_for_serdes_link_generic(hw); + + return E1000_SUCCESS; } -/****************************************************************************** +/** + * e1000_get_speed_and_duplex + * @hw: Struct containing variables accessed by shared code + * @speed: Speed of the connection + * @duplex: Duplex setting of the connection + * Detects the current speed and duplex settings of the hardware. - * - * hw - Struct containing variables accessed by shared code - * speed - Speed of the connection - * duplex - Duplex setting of the connection - *****************************************************************************/ + */ s32 e1000_get_speed_and_duplex(struct e1000_hw *hw, u16 *speed, u16 *duplex) { - u32 status; - s32 ret_val; - u16 phy_data; - - DEBUGFUNC("e1000_get_speed_and_duplex"); - - if (hw->mac_type >= e1000_82543) { - status = er32(STATUS); - if (status & E1000_STATUS_SPEED_1000) { - *speed = SPEED_1000; - DEBUGOUT("1000 Mbs, "); - } else if (status & E1000_STATUS_SPEED_100) { - *speed = SPEED_100; - DEBUGOUT("100 Mbs, "); - } else { - *speed = SPEED_10; - DEBUGOUT("10 Mbs, "); - } - - if (status & E1000_STATUS_FD) { - *duplex = FULL_DUPLEX; - DEBUGOUT("Full Duplex\n"); - } else { - *duplex = HALF_DUPLEX; - DEBUGOUT(" Half Duplex\n"); - } - } else { - DEBUGOUT("1000 Mbs, Full Duplex\n"); - *speed = SPEED_1000; - *duplex = FULL_DUPLEX; - } - - /* IGP01 PHY may advertise full duplex operation after speed downgrade even - * if it is operating at half duplex. Here we set the duplex settings to - * match the duplex in the link partner's capabilities. - */ - if (hw->phy_type == e1000_phy_igp && hw->speed_downgraded) { - ret_val = e1000_read_phy_reg(hw, PHY_AUTONEG_EXP, &phy_data); - if (ret_val) - return ret_val; - - if (!(phy_data & NWAY_ER_LP_NWAY_CAPS)) - *duplex = HALF_DUPLEX; - else { - ret_val = e1000_read_phy_reg(hw, PHY_LP_ABILITY, &phy_data); - if (ret_val) - return ret_val; - if ((*speed == SPEED_100 && !(phy_data & NWAY_LPAR_100TX_FD_CAPS)) || - (*speed == SPEED_10 && !(phy_data & NWAY_LPAR_10T_FD_CAPS))) - *duplex = HALF_DUPLEX; - } - } - - if ((hw->mac_type == e1000_80003es2lan) && - (hw->media_type == e1000_media_type_copper)) { - if (*speed == SPEED_1000) - ret_val = e1000_configure_kmrn_for_1000(hw); - else - ret_val = e1000_configure_kmrn_for_10_100(hw, *duplex); - if (ret_val) - return ret_val; - } - - if ((hw->phy_type == e1000_phy_igp_3) && (*speed == SPEED_1000)) { - ret_val = e1000_kumeran_lock_loss_workaround(hw); - if (ret_val) - return ret_val; - } - - return E1000_SUCCESS; + u32 status; + s32 ret_val; + u16 phy_data; + + DEBUGFUNC("e1000_get_speed_and_duplex"); + + if (hw->mac_type >= e1000_82543) { + status = er32(STATUS); + if (status & E1000_STATUS_SPEED_1000) { + *speed = SPEED_1000; + DEBUGOUT("1000 Mbs, "); + } else if (status & E1000_STATUS_SPEED_100) { + *speed = SPEED_100; + DEBUGOUT("100 Mbs, "); + } else { + *speed = SPEED_10; + DEBUGOUT("10 Mbs, "); + } + + if (status & E1000_STATUS_FD) { + *duplex = FULL_DUPLEX; + DEBUGOUT("Full Duplex\n"); + } else { + *duplex = HALF_DUPLEX; + DEBUGOUT(" Half Duplex\n"); + } + } else { + DEBUGOUT("1000 Mbs, Full Duplex\n"); + *speed = SPEED_1000; + *duplex = FULL_DUPLEX; + } + + /* IGP01 PHY may advertise full duplex operation after speed downgrade even + * if it is operating at half duplex. Here we set the duplex settings to + * match the duplex in the link partner's capabilities. + */ + if (hw->phy_type == e1000_phy_igp && hw->speed_downgraded) { + ret_val = e1000_read_phy_reg(hw, PHY_AUTONEG_EXP, &phy_data); + if (ret_val) + return ret_val; + + if (!(phy_data & NWAY_ER_LP_NWAY_CAPS)) + *duplex = HALF_DUPLEX; + else { + ret_val = + e1000_read_phy_reg(hw, PHY_LP_ABILITY, &phy_data); + if (ret_val) + return ret_val; + if ((*speed == SPEED_100 + && !(phy_data & NWAY_LPAR_100TX_FD_CAPS)) + || (*speed == SPEED_10 + && !(phy_data & NWAY_LPAR_10T_FD_CAPS))) + *duplex = HALF_DUPLEX; + } + } + + return E1000_SUCCESS; } -/****************************************************************************** -* Blocks until autoneg completes or times out (~4.5 seconds) -* -* hw - Struct containing variables accessed by shared code -******************************************************************************/ +/** + * e1000_wait_autoneg + * @hw: Struct containing variables accessed by shared code + * + * Blocks until autoneg completes or times out (~4.5 seconds) + */ static s32 e1000_wait_autoneg(struct e1000_hw *hw) { - s32 ret_val; - u16 i; - u16 phy_data; - - DEBUGFUNC("e1000_wait_autoneg"); - DEBUGOUT("Waiting for Auto-Neg to complete.\n"); - - /* We will wait for autoneg to complete or 4.5 seconds to expire. */ - for (i = PHY_AUTO_NEG_TIME; i > 0; i--) { - /* Read the MII Status Register and wait for Auto-Neg - * Complete bit to be set. - */ - ret_val = e1000_read_phy_reg(hw, PHY_STATUS, &phy_data); - if (ret_val) - return ret_val; - ret_val = e1000_read_phy_reg(hw, PHY_STATUS, &phy_data); - if (ret_val) - return ret_val; - if (phy_data & MII_SR_AUTONEG_COMPLETE) { - return E1000_SUCCESS; - } - msleep(100); - } - return E1000_SUCCESS; + s32 ret_val; + u16 i; + u16 phy_data; + + DEBUGFUNC("e1000_wait_autoneg"); + DEBUGOUT("Waiting for Auto-Neg to complete.\n"); + + /* We will wait for autoneg to complete or 4.5 seconds to expire. */ + for (i = PHY_AUTO_NEG_TIME; i > 0; i--) { + /* Read the MII Status Register and wait for Auto-Neg + * Complete bit to be set. + */ + ret_val = e1000_read_phy_reg(hw, PHY_STATUS, &phy_data); + if (ret_val) + return ret_val; + ret_val = e1000_read_phy_reg(hw, PHY_STATUS, &phy_data); + if (ret_val) + return ret_val; + if (phy_data & MII_SR_AUTONEG_COMPLETE) { + return E1000_SUCCESS; + } + msleep(100); + } + return E1000_SUCCESS; } -/****************************************************************************** -* Raises the Management Data Clock -* -* hw - Struct containing variables accessed by shared code -* ctrl - Device control register's current value -******************************************************************************/ +/** + * e1000_raise_mdi_clk - Raises the Management Data Clock + * @hw: Struct containing variables accessed by shared code + * @ctrl: Device control register's current value + */ static void e1000_raise_mdi_clk(struct e1000_hw *hw, u32 *ctrl) { - /* Raise the clock input to the Management Data Clock (by setting the MDC - * bit), and then delay 10 microseconds. - */ - ew32(CTRL, (*ctrl | E1000_CTRL_MDC)); - E1000_WRITE_FLUSH(); - udelay(10); + /* Raise the clock input to the Management Data Clock (by setting the MDC + * bit), and then delay 10 microseconds. + */ + ew32(CTRL, (*ctrl | E1000_CTRL_MDC)); + E1000_WRITE_FLUSH(); + udelay(10); } -/****************************************************************************** -* Lowers the Management Data Clock -* -* hw - Struct containing variables accessed by shared code -* ctrl - Device control register's current value -******************************************************************************/ +/** + * e1000_lower_mdi_clk - Lowers the Management Data Clock + * @hw: Struct containing variables accessed by shared code + * @ctrl: Device control register's current value + */ static void e1000_lower_mdi_clk(struct e1000_hw *hw, u32 *ctrl) { - /* Lower the clock input to the Management Data Clock (by clearing the MDC - * bit), and then delay 10 microseconds. - */ - ew32(CTRL, (*ctrl & ~E1000_CTRL_MDC)); - E1000_WRITE_FLUSH(); - udelay(10); + /* Lower the clock input to the Management Data Clock (by clearing the MDC + * bit), and then delay 10 microseconds. + */ + ew32(CTRL, (*ctrl & ~E1000_CTRL_MDC)); + E1000_WRITE_FLUSH(); + udelay(10); } -/****************************************************************************** -* Shifts data bits out to the PHY -* -* hw - Struct containing variables accessed by shared code -* data - Data to send out to the PHY -* count - Number of bits to shift out -* -* Bits are shifted out in MSB to LSB order. -******************************************************************************/ +/** + * e1000_shift_out_mdi_bits - Shifts data bits out to the PHY + * @hw: Struct containing variables accessed by shared code + * @data: Data to send out to the PHY + * @count: Number of bits to shift out + * + * Bits are shifted out in MSB to LSB order. + */ static void e1000_shift_out_mdi_bits(struct e1000_hw *hw, u32 data, u16 count) { - u32 ctrl; - u32 mask; - - /* We need to shift "count" number of bits out to the PHY. So, the value - * in the "data" parameter will be shifted out to the PHY one bit at a - * time. In order to do this, "data" must be broken down into bits. - */ - mask = 0x01; - mask <<= (count - 1); - - ctrl = er32(CTRL); - - /* Set MDIO_DIR and MDC_DIR direction bits to be used as output pins. */ - ctrl |= (E1000_CTRL_MDIO_DIR | E1000_CTRL_MDC_DIR); - - while (mask) { - /* A "1" is shifted out to the PHY by setting the MDIO bit to "1" and - * then raising and lowering the Management Data Clock. A "0" is - * shifted out to the PHY by setting the MDIO bit to "0" and then - * raising and lowering the clock. - */ - if (data & mask) - ctrl |= E1000_CTRL_MDIO; - else - ctrl &= ~E1000_CTRL_MDIO; - - ew32(CTRL, ctrl); - E1000_WRITE_FLUSH(); - - udelay(10); - - e1000_raise_mdi_clk(hw, &ctrl); - e1000_lower_mdi_clk(hw, &ctrl); - - mask = mask >> 1; - } -} - -/****************************************************************************** -* Shifts data bits in from the PHY -* -* hw - Struct containing variables accessed by shared code -* -* Bits are shifted in in MSB to LSB order. -******************************************************************************/ -static u16 e1000_shift_in_mdi_bits(struct e1000_hw *hw) -{ - u32 ctrl; - u16 data = 0; - u8 i; - - /* In order to read a register from the PHY, we need to shift in a total - * of 18 bits from the PHY. The first two bit (turnaround) times are used - * to avoid contention on the MDIO pin when a read operation is performed. - * These two bits are ignored by us and thrown away. Bits are "shifted in" - * by raising the input to the Management Data Clock (setting the MDC bit), - * and then reading the value of the MDIO bit. - */ - ctrl = er32(CTRL); - - /* Clear MDIO_DIR (SWDPIO1) to indicate this bit is to be used as input. */ - ctrl &= ~E1000_CTRL_MDIO_DIR; - ctrl &= ~E1000_CTRL_MDIO; - - ew32(CTRL, ctrl); - E1000_WRITE_FLUSH(); - - /* Raise and Lower the clock before reading in the data. This accounts for - * the turnaround bits. The first clock occurred when we clocked out the - * last bit of the Register Address. - */ - e1000_raise_mdi_clk(hw, &ctrl); - e1000_lower_mdi_clk(hw, &ctrl); - - for (data = 0, i = 0; i < 16; i++) { - data = data << 1; - e1000_raise_mdi_clk(hw, &ctrl); - ctrl = er32(CTRL); - /* Check to see if we shifted in a "1". */ - if (ctrl & E1000_CTRL_MDIO) - data |= 1; - e1000_lower_mdi_clk(hw, &ctrl); - } - - e1000_raise_mdi_clk(hw, &ctrl); - e1000_lower_mdi_clk(hw, &ctrl); - - return data; -} - -static s32 e1000_swfw_sync_acquire(struct e1000_hw *hw, u16 mask) -{ - u32 swfw_sync = 0; - u32 swmask = mask; - u32 fwmask = mask << 16; - s32 timeout = 200; + u32 ctrl; + u32 mask; - DEBUGFUNC("e1000_swfw_sync_acquire"); - - if (hw->swfwhw_semaphore_present) - return e1000_get_software_flag(hw); + /* We need to shift "count" number of bits out to the PHY. So, the value + * in the "data" parameter will be shifted out to the PHY one bit at a + * time. In order to do this, "data" must be broken down into bits. + */ + mask = 0x01; + mask <<= (count - 1); - if (!hw->swfw_sync_present) - return e1000_get_hw_eeprom_semaphore(hw); + ctrl = er32(CTRL); - while (timeout) { - if (e1000_get_hw_eeprom_semaphore(hw)) - return -E1000_ERR_SWFW_SYNC; + /* Set MDIO_DIR and MDC_DIR direction bits to be used as output pins. */ + ctrl |= (E1000_CTRL_MDIO_DIR | E1000_CTRL_MDC_DIR); - swfw_sync = er32(SW_FW_SYNC); - if (!(swfw_sync & (fwmask | swmask))) { - break; - } + while (mask) { + /* A "1" is shifted out to the PHY by setting the MDIO bit to "1" and + * then raising and lowering the Management Data Clock. A "0" is + * shifted out to the PHY by setting the MDIO bit to "0" and then + * raising and lowering the clock. + */ + if (data & mask) + ctrl |= E1000_CTRL_MDIO; + else + ctrl &= ~E1000_CTRL_MDIO; - /* firmware currently using resource (fwmask) */ - /* or other software thread currently using resource (swmask) */ - e1000_put_hw_eeprom_semaphore(hw); - mdelay(5); - timeout--; - } + ew32(CTRL, ctrl); + E1000_WRITE_FLUSH(); - if (!timeout) { - DEBUGOUT("Driver can't access resource, SW_FW_SYNC timeout.\n"); - return -E1000_ERR_SWFW_SYNC; - } + udelay(10); - swfw_sync |= swmask; - ew32(SW_FW_SYNC, swfw_sync); + e1000_raise_mdi_clk(hw, &ctrl); + e1000_lower_mdi_clk(hw, &ctrl); - e1000_put_hw_eeprom_semaphore(hw); - return E1000_SUCCESS; + mask = mask >> 1; + } } -static void e1000_swfw_sync_release(struct e1000_hw *hw, u16 mask) +/** + * e1000_shift_in_mdi_bits - Shifts data bits in from the PHY + * @hw: Struct containing variables accessed by shared code + * + * Bits are shifted in in MSB to LSB order. + */ +static u16 e1000_shift_in_mdi_bits(struct e1000_hw *hw) { - u32 swfw_sync; - u32 swmask = mask; + u32 ctrl; + u16 data = 0; + u8 i; - DEBUGFUNC("e1000_swfw_sync_release"); + /* In order to read a register from the PHY, we need to shift in a total + * of 18 bits from the PHY. The first two bit (turnaround) times are used + * to avoid contention on the MDIO pin when a read operation is performed. + * These two bits are ignored by us and thrown away. Bits are "shifted in" + * by raising the input to the Management Data Clock (setting the MDC bit), + * and then reading the value of the MDIO bit. + */ + ctrl = er32(CTRL); - if (hw->swfwhw_semaphore_present) { - e1000_release_software_flag(hw); - return; - } + /* Clear MDIO_DIR (SWDPIO1) to indicate this bit is to be used as input. */ + ctrl &= ~E1000_CTRL_MDIO_DIR; + ctrl &= ~E1000_CTRL_MDIO; - if (!hw->swfw_sync_present) { - e1000_put_hw_eeprom_semaphore(hw); - return; - } + ew32(CTRL, ctrl); + E1000_WRITE_FLUSH(); - /* if (e1000_get_hw_eeprom_semaphore(hw)) - * return -E1000_ERR_SWFW_SYNC; */ - while (e1000_get_hw_eeprom_semaphore(hw) != E1000_SUCCESS); - /* empty */ + /* Raise and Lower the clock before reading in the data. This accounts for + * the turnaround bits. The first clock occurred when we clocked out the + * last bit of the Register Address. + */ + e1000_raise_mdi_clk(hw, &ctrl); + e1000_lower_mdi_clk(hw, &ctrl); + + for (data = 0, i = 0; i < 16; i++) { + data = data << 1; + e1000_raise_mdi_clk(hw, &ctrl); + ctrl = er32(CTRL); + /* Check to see if we shifted in a "1". */ + if (ctrl & E1000_CTRL_MDIO) + data |= 1; + e1000_lower_mdi_clk(hw, &ctrl); + } - swfw_sync = er32(SW_FW_SYNC); - swfw_sync &= ~swmask; - ew32(SW_FW_SYNC, swfw_sync); + e1000_raise_mdi_clk(hw, &ctrl); + e1000_lower_mdi_clk(hw, &ctrl); - e1000_put_hw_eeprom_semaphore(hw); + return data; } -/***************************************************************************** -* Reads the value from a PHY register, if the value is on a specific non zero -* page, sets the page first. -* hw - Struct containing variables accessed by shared code -* reg_addr - address of the PHY register to read -******************************************************************************/ + +/** + * e1000_read_phy_reg - read a phy register + * @hw: Struct containing variables accessed by shared code + * @reg_addr: address of the PHY register to read + * + * Reads the value from a PHY register, if the value is on a specific non zero + * page, sets the page first. + */ s32 e1000_read_phy_reg(struct e1000_hw *hw, u32 reg_addr, u16 *phy_data) { - u32 ret_val; - u16 swfw; - - DEBUGFUNC("e1000_read_phy_reg"); - - if ((hw->mac_type == e1000_80003es2lan) && - (er32(STATUS) & E1000_STATUS_FUNC_1)) { - swfw = E1000_SWFW_PHY1_SM; - } else { - swfw = E1000_SWFW_PHY0_SM; - } - if (e1000_swfw_sync_acquire(hw, swfw)) - return -E1000_ERR_SWFW_SYNC; - - if ((hw->phy_type == e1000_phy_igp || - hw->phy_type == e1000_phy_igp_3 || - hw->phy_type == e1000_phy_igp_2) && - (reg_addr > MAX_PHY_MULTI_PAGE_REG)) { - ret_val = e1000_write_phy_reg_ex(hw, IGP01E1000_PHY_PAGE_SELECT, - (u16)reg_addr); - if (ret_val) { - e1000_swfw_sync_release(hw, swfw); - return ret_val; - } - } else if (hw->phy_type == e1000_phy_gg82563) { - if (((reg_addr & MAX_PHY_REG_ADDRESS) > MAX_PHY_MULTI_PAGE_REG) || - (hw->mac_type == e1000_80003es2lan)) { - /* Select Configuration Page */ - if ((reg_addr & MAX_PHY_REG_ADDRESS) < GG82563_MIN_ALT_REG) { - ret_val = e1000_write_phy_reg_ex(hw, GG82563_PHY_PAGE_SELECT, - (u16)((u16)reg_addr >> GG82563_PAGE_SHIFT)); - } else { - /* Use Alternative Page Select register to access - * registers 30 and 31 - */ - ret_val = e1000_write_phy_reg_ex(hw, - GG82563_PHY_PAGE_SELECT_ALT, - (u16)((u16)reg_addr >> GG82563_PAGE_SHIFT)); - } - - if (ret_val) { - e1000_swfw_sync_release(hw, swfw); - return ret_val; - } - } - } - - ret_val = e1000_read_phy_reg_ex(hw, MAX_PHY_REG_ADDRESS & reg_addr, - phy_data); - - e1000_swfw_sync_release(hw, swfw); - return ret_val; + u32 ret_val; + + DEBUGFUNC("e1000_read_phy_reg"); + + if ((hw->phy_type == e1000_phy_igp) && + (reg_addr > MAX_PHY_MULTI_PAGE_REG)) { + ret_val = e1000_write_phy_reg_ex(hw, IGP01E1000_PHY_PAGE_SELECT, + (u16) reg_addr); + if (ret_val) + return ret_val; + } + + ret_val = e1000_read_phy_reg_ex(hw, MAX_PHY_REG_ADDRESS & reg_addr, + phy_data); + + return ret_val; } static s32 e1000_read_phy_reg_ex(struct e1000_hw *hw, u32 reg_addr, u16 *phy_data) { - u32 i; - u32 mdic = 0; - const u32 phy_addr = 1; - - DEBUGFUNC("e1000_read_phy_reg_ex"); - - if (reg_addr > MAX_PHY_REG_ADDRESS) { - DEBUGOUT1("PHY Address %d is out of range\n", reg_addr); - return -E1000_ERR_PARAM; - } - - if (hw->mac_type > e1000_82543) { - /* Set up Op-code, Phy Address, and register address in the MDI - * Control register. The MAC will take care of interfacing with the - * PHY to retrieve the desired data. - */ - mdic = ((reg_addr << E1000_MDIC_REG_SHIFT) | - (phy_addr << E1000_MDIC_PHY_SHIFT) | - (E1000_MDIC_OP_READ)); - - ew32(MDIC, mdic); - - /* Poll the ready bit to see if the MDI read completed */ - for (i = 0; i < 64; i++) { - udelay(50); - mdic = er32(MDIC); - if (mdic & E1000_MDIC_READY) break; - } - if (!(mdic & E1000_MDIC_READY)) { - DEBUGOUT("MDI Read did not complete\n"); - return -E1000_ERR_PHY; - } - if (mdic & E1000_MDIC_ERROR) { - DEBUGOUT("MDI Error\n"); - return -E1000_ERR_PHY; - } - *phy_data = (u16)mdic; - } else { - /* We must first send a preamble through the MDIO pin to signal the - * beginning of an MII instruction. This is done by sending 32 - * consecutive "1" bits. - */ - e1000_shift_out_mdi_bits(hw, PHY_PREAMBLE, PHY_PREAMBLE_SIZE); - - /* Now combine the next few fields that are required for a read - * operation. We use this method instead of calling the - * e1000_shift_out_mdi_bits routine five different times. The format of - * a MII read instruction consists of a shift out of 14 bits and is - * defined as follows: - * <Preamble><SOF><Op Code><Phy Addr><Reg Addr> - * followed by a shift in of 18 bits. This first two bits shifted in - * are TurnAround bits used to avoid contention on the MDIO pin when a - * READ operation is performed. These two bits are thrown away - * followed by a shift in of 16 bits which contains the desired data. - */ - mdic = ((reg_addr) | (phy_addr << 5) | - (PHY_OP_READ << 10) | (PHY_SOF << 12)); - - e1000_shift_out_mdi_bits(hw, mdic, 14); - - /* Now that we've shifted out the read command to the MII, we need to - * "shift in" the 16-bit value (18 total bits) of the requested PHY - * register address. - */ - *phy_data = e1000_shift_in_mdi_bits(hw); - } - return E1000_SUCCESS; + u32 i; + u32 mdic = 0; + const u32 phy_addr = 1; + + DEBUGFUNC("e1000_read_phy_reg_ex"); + + if (reg_addr > MAX_PHY_REG_ADDRESS) { + DEBUGOUT1("PHY Address %d is out of range\n", reg_addr); + return -E1000_ERR_PARAM; + } + + if (hw->mac_type > e1000_82543) { + /* Set up Op-code, Phy Address, and register address in the MDI + * Control register. The MAC will take care of interfacing with the + * PHY to retrieve the desired data. + */ + mdic = ((reg_addr << E1000_MDIC_REG_SHIFT) | + (phy_addr << E1000_MDIC_PHY_SHIFT) | + (E1000_MDIC_OP_READ)); + + ew32(MDIC, mdic); + + /* Poll the ready bit to see if the MDI read completed */ + for (i = 0; i < 64; i++) { + udelay(50); + mdic = er32(MDIC); + if (mdic & E1000_MDIC_READY) + break; + } + if (!(mdic & E1000_MDIC_READY)) { + DEBUGOUT("MDI Read did not complete\n"); + return -E1000_ERR_PHY; + } + if (mdic & E1000_MDIC_ERROR) { + DEBUGOUT("MDI Error\n"); + return -E1000_ERR_PHY; + } + *phy_data = (u16) mdic; + } else { + /* We must first send a preamble through the MDIO pin to signal the + * beginning of an MII instruction. This is done by sending 32 + * consecutive "1" bits. + */ + e1000_shift_out_mdi_bits(hw, PHY_PREAMBLE, PHY_PREAMBLE_SIZE); + + /* Now combine the next few fields that are required for a read + * operation. We use this method instead of calling the + * e1000_shift_out_mdi_bits routine five different times. The format of + * a MII read instruction consists of a shift out of 14 bits and is + * defined as follows: + * <Preamble><SOF><Op Code><Phy Addr><Reg Addr> + * followed by a shift in of 18 bits. This first two bits shifted in + * are TurnAround bits used to avoid contention on the MDIO pin when a + * READ operation is performed. These two bits are thrown away + * followed by a shift in of 16 bits which contains the desired data. + */ + mdic = ((reg_addr) | (phy_addr << 5) | + (PHY_OP_READ << 10) | (PHY_SOF << 12)); + + e1000_shift_out_mdi_bits(hw, mdic, 14); + + /* Now that we've shifted out the read command to the MII, we need to + * "shift in" the 16-bit value (18 total bits) of the requested PHY + * register address. + */ + *phy_data = e1000_shift_in_mdi_bits(hw); + } + return E1000_SUCCESS; } -/****************************************************************************** -* Writes a value to a PHY register -* -* hw - Struct containing variables accessed by shared code -* reg_addr - address of the PHY register to write -* data - data to write to the PHY -******************************************************************************/ +/** + * e1000_write_phy_reg - write a phy register + * + * @hw: Struct containing variables accessed by shared code + * @reg_addr: address of the PHY register to write + * @data: data to write to the PHY + + * Writes a value to a PHY register + */ s32 e1000_write_phy_reg(struct e1000_hw *hw, u32 reg_addr, u16 phy_data) { - u32 ret_val; - u16 swfw; - - DEBUGFUNC("e1000_write_phy_reg"); - - if ((hw->mac_type == e1000_80003es2lan) && - (er32(STATUS) & E1000_STATUS_FUNC_1)) { - swfw = E1000_SWFW_PHY1_SM; - } else { - swfw = E1000_SWFW_PHY0_SM; - } - if (e1000_swfw_sync_acquire(hw, swfw)) - return -E1000_ERR_SWFW_SYNC; - - if ((hw->phy_type == e1000_phy_igp || - hw->phy_type == e1000_phy_igp_3 || - hw->phy_type == e1000_phy_igp_2) && - (reg_addr > MAX_PHY_MULTI_PAGE_REG)) { - ret_val = e1000_write_phy_reg_ex(hw, IGP01E1000_PHY_PAGE_SELECT, - (u16)reg_addr); - if (ret_val) { - e1000_swfw_sync_release(hw, swfw); - return ret_val; - } - } else if (hw->phy_type == e1000_phy_gg82563) { - if (((reg_addr & MAX_PHY_REG_ADDRESS) > MAX_PHY_MULTI_PAGE_REG) || - (hw->mac_type == e1000_80003es2lan)) { - /* Select Configuration Page */ - if ((reg_addr & MAX_PHY_REG_ADDRESS) < GG82563_MIN_ALT_REG) { - ret_val = e1000_write_phy_reg_ex(hw, GG82563_PHY_PAGE_SELECT, - (u16)((u16)reg_addr >> GG82563_PAGE_SHIFT)); - } else { - /* Use Alternative Page Select register to access - * registers 30 and 31 - */ - ret_val = e1000_write_phy_reg_ex(hw, - GG82563_PHY_PAGE_SELECT_ALT, - (u16)((u16)reg_addr >> GG82563_PAGE_SHIFT)); - } - - if (ret_val) { - e1000_swfw_sync_release(hw, swfw); - return ret_val; - } - } - } - - ret_val = e1000_write_phy_reg_ex(hw, MAX_PHY_REG_ADDRESS & reg_addr, - phy_data); - - e1000_swfw_sync_release(hw, swfw); - return ret_val; + u32 ret_val; + + DEBUGFUNC("e1000_write_phy_reg"); + + if ((hw->phy_type == e1000_phy_igp) && + (reg_addr > MAX_PHY_MULTI_PAGE_REG)) { + ret_val = e1000_write_phy_reg_ex(hw, IGP01E1000_PHY_PAGE_SELECT, + (u16) reg_addr); + if (ret_val) + return ret_val; + } + + ret_val = e1000_write_phy_reg_ex(hw, MAX_PHY_REG_ADDRESS & reg_addr, + phy_data); + + return ret_val; } static s32 e1000_write_phy_reg_ex(struct e1000_hw *hw, u32 reg_addr, u16 phy_data) { - u32 i; - u32 mdic = 0; - const u32 phy_addr = 1; - - DEBUGFUNC("e1000_write_phy_reg_ex"); - - if (reg_addr > MAX_PHY_REG_ADDRESS) { - DEBUGOUT1("PHY Address %d is out of range\n", reg_addr); - return -E1000_ERR_PARAM; - } - - if (hw->mac_type > e1000_82543) { - /* Set up Op-code, Phy Address, register address, and data intended - * for the PHY register in the MDI Control register. The MAC will take - * care of interfacing with the PHY to send the desired data. - */ - mdic = (((u32)phy_data) | - (reg_addr << E1000_MDIC_REG_SHIFT) | - (phy_addr << E1000_MDIC_PHY_SHIFT) | - (E1000_MDIC_OP_WRITE)); - - ew32(MDIC, mdic); - - /* Poll the ready bit to see if the MDI read completed */ - for (i = 0; i < 641; i++) { - udelay(5); - mdic = er32(MDIC); - if (mdic & E1000_MDIC_READY) break; - } - if (!(mdic & E1000_MDIC_READY)) { - DEBUGOUT("MDI Write did not complete\n"); - return -E1000_ERR_PHY; - } - } else { - /* We'll need to use the SW defined pins to shift the write command - * out to the PHY. We first send a preamble to the PHY to signal the - * beginning of the MII instruction. This is done by sending 32 - * consecutive "1" bits. - */ - e1000_shift_out_mdi_bits(hw, PHY_PREAMBLE, PHY_PREAMBLE_SIZE); - - /* Now combine the remaining required fields that will indicate a - * write operation. We use this method instead of calling the - * e1000_shift_out_mdi_bits routine for each field in the command. The - * format of a MII write instruction is as follows: - * <Preamble><SOF><Op Code><Phy Addr><Reg Addr><Turnaround><Data>. - */ - mdic = ((PHY_TURNAROUND) | (reg_addr << 2) | (phy_addr << 7) | - (PHY_OP_WRITE << 12) | (PHY_SOF << 14)); - mdic <<= 16; - mdic |= (u32)phy_data; - - e1000_shift_out_mdi_bits(hw, mdic, 32); - } - - return E1000_SUCCESS; -} + u32 i; + u32 mdic = 0; + const u32 phy_addr = 1; -static s32 e1000_read_kmrn_reg(struct e1000_hw *hw, u32 reg_addr, u16 *data) -{ - u32 reg_val; - u16 swfw; - DEBUGFUNC("e1000_read_kmrn_reg"); - - if ((hw->mac_type == e1000_80003es2lan) && - (er32(STATUS) & E1000_STATUS_FUNC_1)) { - swfw = E1000_SWFW_PHY1_SM; - } else { - swfw = E1000_SWFW_PHY0_SM; - } - if (e1000_swfw_sync_acquire(hw, swfw)) - return -E1000_ERR_SWFW_SYNC; - - /* Write register address */ - reg_val = ((reg_addr << E1000_KUMCTRLSTA_OFFSET_SHIFT) & - E1000_KUMCTRLSTA_OFFSET) | - E1000_KUMCTRLSTA_REN; - ew32(KUMCTRLSTA, reg_val); - udelay(2); - - /* Read the data returned */ - reg_val = er32(KUMCTRLSTA); - *data = (u16)reg_val; - - e1000_swfw_sync_release(hw, swfw); - return E1000_SUCCESS; -} + DEBUGFUNC("e1000_write_phy_reg_ex"); -static s32 e1000_write_kmrn_reg(struct e1000_hw *hw, u32 reg_addr, u16 data) -{ - u32 reg_val; - u16 swfw; - DEBUGFUNC("e1000_write_kmrn_reg"); - - if ((hw->mac_type == e1000_80003es2lan) && - (er32(STATUS) & E1000_STATUS_FUNC_1)) { - swfw = E1000_SWFW_PHY1_SM; - } else { - swfw = E1000_SWFW_PHY0_SM; - } - if (e1000_swfw_sync_acquire(hw, swfw)) - return -E1000_ERR_SWFW_SYNC; - - reg_val = ((reg_addr << E1000_KUMCTRLSTA_OFFSET_SHIFT) & - E1000_KUMCTRLSTA_OFFSET) | data; - ew32(KUMCTRLSTA, reg_val); - udelay(2); - - e1000_swfw_sync_release(hw, swfw); - return E1000_SUCCESS; + if (reg_addr > MAX_PHY_REG_ADDRESS) { + DEBUGOUT1("PHY Address %d is out of range\n", reg_addr); + return -E1000_ERR_PARAM; + } + + if (hw->mac_type > e1000_82543) { + /* Set up Op-code, Phy Address, register address, and data intended + * for the PHY register in the MDI Control register. The MAC will take + * care of interfacing with the PHY to send the desired data. + */ + mdic = (((u32) phy_data) | + (reg_addr << E1000_MDIC_REG_SHIFT) | + (phy_addr << E1000_MDIC_PHY_SHIFT) | + (E1000_MDIC_OP_WRITE)); + + ew32(MDIC, mdic); + + /* Poll the ready bit to see if the MDI read completed */ + for (i = 0; i < 641; i++) { + udelay(5); + mdic = er32(MDIC); + if (mdic & E1000_MDIC_READY) + break; + } + if (!(mdic & E1000_MDIC_READY)) { + DEBUGOUT("MDI Write did not complete\n"); + return -E1000_ERR_PHY; + } + } else { + /* We'll need to use the SW defined pins to shift the write command + * out to the PHY. We first send a preamble to the PHY to signal the + * beginning of the MII instruction. This is done by sending 32 + * consecutive "1" bits. + */ + e1000_shift_out_mdi_bits(hw, PHY_PREAMBLE, PHY_PREAMBLE_SIZE); + + /* Now combine the remaining required fields that will indicate a + * write operation. We use this method instead of calling the + * e1000_shift_out_mdi_bits routine for each field in the command. The + * format of a MII write instruction is as follows: + * <Preamble><SOF><Op Code><Phy Addr><Reg Addr><Turnaround><Data>. + */ + mdic = ((PHY_TURNAROUND) | (reg_addr << 2) | (phy_addr << 7) | + (PHY_OP_WRITE << 12) | (PHY_SOF << 14)); + mdic <<= 16; + mdic |= (u32) phy_data; + + e1000_shift_out_mdi_bits(hw, mdic, 32); + } + + return E1000_SUCCESS; } -/****************************************************************************** -* Returns the PHY to the power-on reset state -* -* hw - Struct containing variables accessed by shared code -******************************************************************************/ +/** + * e1000_phy_hw_reset - reset the phy, hardware style + * @hw: Struct containing variables accessed by shared code + * + * Returns the PHY to the power-on reset state + */ s32 e1000_phy_hw_reset(struct e1000_hw *hw) { - u32 ctrl, ctrl_ext; - u32 led_ctrl; - s32 ret_val; - u16 swfw; - - DEBUGFUNC("e1000_phy_hw_reset"); - - /* In the case of the phy reset being blocked, it's not an error, we - * simply return success without performing the reset. */ - ret_val = e1000_check_phy_reset_block(hw); - if (ret_val) - return E1000_SUCCESS; - - DEBUGOUT("Resetting Phy...\n"); - - if (hw->mac_type > e1000_82543) { - if ((hw->mac_type == e1000_80003es2lan) && - (er32(STATUS) & E1000_STATUS_FUNC_1)) { - swfw = E1000_SWFW_PHY1_SM; - } else { - swfw = E1000_SWFW_PHY0_SM; - } - if (e1000_swfw_sync_acquire(hw, swfw)) { - DEBUGOUT("Unable to acquire swfw sync\n"); - return -E1000_ERR_SWFW_SYNC; - } - /* Read the device control register and assert the E1000_CTRL_PHY_RST - * bit. Then, take it out of reset. - * For pre-e1000_82571 hardware, we delay for 10ms between the assert - * and deassert. For e1000_82571 hardware and later, we instead delay - * for 50us between and 10ms after the deassertion. - */ - ctrl = er32(CTRL); - ew32(CTRL, ctrl | E1000_CTRL_PHY_RST); - E1000_WRITE_FLUSH(); - - if (hw->mac_type < e1000_82571) - msleep(10); - else - udelay(100); - - ew32(CTRL, ctrl); - E1000_WRITE_FLUSH(); - - if (hw->mac_type >= e1000_82571) - mdelay(10); - - e1000_swfw_sync_release(hw, swfw); - } else { - /* Read the Extended Device Control Register, assert the PHY_RESET_DIR - * bit to put the PHY into reset. Then, take it out of reset. - */ - ctrl_ext = er32(CTRL_EXT); - ctrl_ext |= E1000_CTRL_EXT_SDP4_DIR; - ctrl_ext &= ~E1000_CTRL_EXT_SDP4_DATA; - ew32(CTRL_EXT, ctrl_ext); - E1000_WRITE_FLUSH(); - msleep(10); - ctrl_ext |= E1000_CTRL_EXT_SDP4_DATA; - ew32(CTRL_EXT, ctrl_ext); - E1000_WRITE_FLUSH(); - } - udelay(150); - - if ((hw->mac_type == e1000_82541) || (hw->mac_type == e1000_82547)) { - /* Configure activity LED after PHY reset */ - led_ctrl = er32(LEDCTL); - led_ctrl &= IGP_ACTIVITY_LED_MASK; - led_ctrl |= (IGP_ACTIVITY_LED_ENABLE | IGP_LED3_MODE); - ew32(LEDCTL, led_ctrl); - } - - /* Wait for FW to finish PHY configuration. */ - ret_val = e1000_get_phy_cfg_done(hw); - if (ret_val != E1000_SUCCESS) - return ret_val; - e1000_release_software_semaphore(hw); - - if ((hw->mac_type == e1000_ich8lan) && (hw->phy_type == e1000_phy_igp_3)) - ret_val = e1000_init_lcd_from_nvm(hw); - - return ret_val; + u32 ctrl, ctrl_ext; + u32 led_ctrl; + s32 ret_val; + + DEBUGFUNC("e1000_phy_hw_reset"); + + DEBUGOUT("Resetting Phy...\n"); + + if (hw->mac_type > e1000_82543) { + /* Read the device control register and assert the E1000_CTRL_PHY_RST + * bit. Then, take it out of reset. + * For e1000 hardware, we delay for 10ms between the assert + * and deassert. + */ + ctrl = er32(CTRL); + ew32(CTRL, ctrl | E1000_CTRL_PHY_RST); + E1000_WRITE_FLUSH(); + + msleep(10); + + ew32(CTRL, ctrl); + E1000_WRITE_FLUSH(); + + } else { + /* Read the Extended Device Control Register, assert the PHY_RESET_DIR + * bit to put the PHY into reset. Then, take it out of reset. + */ + ctrl_ext = er32(CTRL_EXT); + ctrl_ext |= E1000_CTRL_EXT_SDP4_DIR; + ctrl_ext &= ~E1000_CTRL_EXT_SDP4_DATA; + ew32(CTRL_EXT, ctrl_ext); + E1000_WRITE_FLUSH(); + msleep(10); + ctrl_ext |= E1000_CTRL_EXT_SDP4_DATA; + ew32(CTRL_EXT, ctrl_ext); + E1000_WRITE_FLUSH(); + } + udelay(150); + + if ((hw->mac_type == e1000_82541) || (hw->mac_type == e1000_82547)) { + /* Configure activity LED after PHY reset */ + led_ctrl = er32(LEDCTL); + led_ctrl &= IGP_ACTIVITY_LED_MASK; + led_ctrl |= (IGP_ACTIVITY_LED_ENABLE | IGP_LED3_MODE); + ew32(LEDCTL, led_ctrl); + } + + /* Wait for FW to finish PHY configuration. */ + ret_val = e1000_get_phy_cfg_done(hw); + if (ret_val != E1000_SUCCESS) + return ret_val; + + return ret_val; } -/****************************************************************************** -* Resets the PHY -* -* hw - Struct containing variables accessed by shared code -* -* Sets bit 15 of the MII Control register -******************************************************************************/ +/** + * e1000_phy_reset - reset the phy to commit settings + * @hw: Struct containing variables accessed by shared code + * + * Resets the PHY + * Sets bit 15 of the MII Control register + */ s32 e1000_phy_reset(struct e1000_hw *hw) { - s32 ret_val; - u16 phy_data; - - DEBUGFUNC("e1000_phy_reset"); - - /* In the case of the phy reset being blocked, it's not an error, we - * simply return success without performing the reset. */ - ret_val = e1000_check_phy_reset_block(hw); - if (ret_val) - return E1000_SUCCESS; - - switch (hw->phy_type) { - case e1000_phy_igp: - case e1000_phy_igp_2: - case e1000_phy_igp_3: - case e1000_phy_ife: - ret_val = e1000_phy_hw_reset(hw); - if (ret_val) - return ret_val; - break; - default: - ret_val = e1000_read_phy_reg(hw, PHY_CTRL, &phy_data); - if (ret_val) - return ret_val; - - phy_data |= MII_CR_RESET; - ret_val = e1000_write_phy_reg(hw, PHY_CTRL, phy_data); - if (ret_val) - return ret_val; - - udelay(1); - break; - } - - if (hw->phy_type == e1000_phy_igp || hw->phy_type == e1000_phy_igp_2) - e1000_phy_init_script(hw); - - return E1000_SUCCESS; -} + s32 ret_val; + u16 phy_data; -/****************************************************************************** -* Work-around for 82566 power-down: on D3 entry- -* 1) disable gigabit link -* 2) write VR power-down enable -* 3) read it back -* if successful continue, else issue LCD reset and repeat -* -* hw - struct containing variables accessed by shared code -******************************************************************************/ -void e1000_phy_powerdown_workaround(struct e1000_hw *hw) -{ - s32 reg; - u16 phy_data; - s32 retry = 0; + DEBUGFUNC("e1000_phy_reset"); - DEBUGFUNC("e1000_phy_powerdown_workaround"); + switch (hw->phy_type) { + case e1000_phy_igp: + ret_val = e1000_phy_hw_reset(hw); + if (ret_val) + return ret_val; + break; + default: + ret_val = e1000_read_phy_reg(hw, PHY_CTRL, &phy_data); + if (ret_val) + return ret_val; - if (hw->phy_type != e1000_phy_igp_3) - return; + phy_data |= MII_CR_RESET; + ret_val = e1000_write_phy_reg(hw, PHY_CTRL, phy_data); + if (ret_val) + return ret_val; - do { - /* Disable link */ - reg = er32(PHY_CTRL); - ew32(PHY_CTRL, reg | E1000_PHY_CTRL_GBE_DISABLE | - E1000_PHY_CTRL_NOND0A_GBE_DISABLE); + udelay(1); + break; + } - /* Write VR power-down enable - bits 9:8 should be 10b */ - e1000_read_phy_reg(hw, IGP3_VR_CTRL, &phy_data); - phy_data |= (1 << 9); - phy_data &= ~(1 << 8); - e1000_write_phy_reg(hw, IGP3_VR_CTRL, phy_data); + if (hw->phy_type == e1000_phy_igp) + e1000_phy_init_script(hw); - /* Read it back and test */ - e1000_read_phy_reg(hw, IGP3_VR_CTRL, &phy_data); - if (((phy_data & IGP3_VR_CTRL_MODE_MASK) == IGP3_VR_CTRL_MODE_SHUT) || retry) - break; + return E1000_SUCCESS; +} - /* Issue PHY reset and repeat at most one more time */ - reg = er32(CTRL); - ew32(CTRL, reg | E1000_CTRL_PHY_RST); - retry++; - } while (retry); +/** + * e1000_detect_gig_phy - check the phy type + * @hw: Struct containing variables accessed by shared code + * + * Probes the expected PHY address for known PHY IDs + */ +static s32 e1000_detect_gig_phy(struct e1000_hw *hw) +{ + s32 phy_init_status, ret_val; + u16 phy_id_high, phy_id_low; + bool match = false; - return; + DEBUGFUNC("e1000_detect_gig_phy"); -} + if (hw->phy_id != 0) + return E1000_SUCCESS; -/****************************************************************************** -* Work-around for 82566 Kumeran PCS lock loss: -* On link status change (i.e. PCI reset, speed change) and link is up and -* speed is gigabit- -* 0) if workaround is optionally disabled do nothing -* 1) wait 1ms for Kumeran link to come up -* 2) check Kumeran Diagnostic register PCS lock loss bit -* 3) if not set the link is locked (all is good), otherwise... -* 4) reset the PHY -* 5) repeat up to 10 times -* Note: this is only called for IGP3 copper when speed is 1gb. -* -* hw - struct containing variables accessed by shared code -******************************************************************************/ -static s32 e1000_kumeran_lock_loss_workaround(struct e1000_hw *hw) -{ - s32 ret_val; - s32 reg; - s32 cnt; - u16 phy_data; - - if (hw->kmrn_lock_loss_workaround_disabled) - return E1000_SUCCESS; - - /* Make sure link is up before proceeding. If not just return. - * Attempting this while link is negotiating fouled up link - * stability */ - ret_val = e1000_read_phy_reg(hw, PHY_STATUS, &phy_data); - ret_val = e1000_read_phy_reg(hw, PHY_STATUS, &phy_data); - - if (phy_data & MII_SR_LINK_STATUS) { - for (cnt = 0; cnt < 10; cnt++) { - /* read once to clear */ - ret_val = e1000_read_phy_reg(hw, IGP3_KMRN_DIAG, &phy_data); - if (ret_val) - return ret_val; - /* and again to get new status */ - ret_val = e1000_read_phy_reg(hw, IGP3_KMRN_DIAG, &phy_data); - if (ret_val) - return ret_val; - - /* check for PCS lock */ - if (!(phy_data & IGP3_KMRN_DIAG_PCS_LOCK_LOSS)) - return E1000_SUCCESS; - - /* Issue PHY reset */ - e1000_phy_hw_reset(hw); - mdelay(5); - } - /* Disable GigE link negotiation */ - reg = er32(PHY_CTRL); - ew32(PHY_CTRL, reg | E1000_PHY_CTRL_GBE_DISABLE | - E1000_PHY_CTRL_NOND0A_GBE_DISABLE); - - /* unable to acquire PCS lock */ - return E1000_ERR_PHY; - } - - return E1000_SUCCESS; -} + /* Read the PHY ID Registers to identify which PHY is onboard. */ + ret_val = e1000_read_phy_reg(hw, PHY_ID1, &phy_id_high); + if (ret_val) + return ret_val; -/****************************************************************************** -* Probes the expected PHY address for known PHY IDs -* -* hw - Struct containing variables accessed by shared code -******************************************************************************/ -static s32 e1000_detect_gig_phy(struct e1000_hw *hw) -{ - s32 phy_init_status, ret_val; - u16 phy_id_high, phy_id_low; - bool match = false; - - DEBUGFUNC("e1000_detect_gig_phy"); - - if (hw->phy_id != 0) - return E1000_SUCCESS; - - /* The 82571 firmware may still be configuring the PHY. In this - * case, we cannot access the PHY until the configuration is done. So - * we explicitly set the PHY values. */ - if (hw->mac_type == e1000_82571 || - hw->mac_type == e1000_82572) { - hw->phy_id = IGP01E1000_I_PHY_ID; - hw->phy_type = e1000_phy_igp_2; - return E1000_SUCCESS; - } - - /* ESB-2 PHY reads require e1000_phy_gg82563 to be set because of a work- - * around that forces PHY page 0 to be set or the reads fail. The rest of - * the code in this routine uses e1000_read_phy_reg to read the PHY ID. - * So for ESB-2 we need to have this set so our reads won't fail. If the - * attached PHY is not a e1000_phy_gg82563, the routines below will figure - * this out as well. */ - if (hw->mac_type == e1000_80003es2lan) - hw->phy_type = e1000_phy_gg82563; - - /* Read the PHY ID Registers to identify which PHY is onboard. */ - ret_val = e1000_read_phy_reg(hw, PHY_ID1, &phy_id_high); - if (ret_val) - return ret_val; - - hw->phy_id = (u32)(phy_id_high << 16); - udelay(20); - ret_val = e1000_read_phy_reg(hw, PHY_ID2, &phy_id_low); - if (ret_val) - return ret_val; - - hw->phy_id |= (u32)(phy_id_low & PHY_REVISION_MASK); - hw->phy_revision = (u32)phy_id_low & ~PHY_REVISION_MASK; - - switch (hw->mac_type) { - case e1000_82543: - if (hw->phy_id == M88E1000_E_PHY_ID) match = true; - break; - case e1000_82544: - if (hw->phy_id == M88E1000_I_PHY_ID) match = true; - break; - case e1000_82540: - case e1000_82545: - case e1000_82545_rev_3: - case e1000_82546: - case e1000_82546_rev_3: - if (hw->phy_id == M88E1011_I_PHY_ID) match = true; - break; - case e1000_82541: - case e1000_82541_rev_2: - case e1000_82547: - case e1000_82547_rev_2: - if (hw->phy_id == IGP01E1000_I_PHY_ID) match = true; - break; - case e1000_82573: - if (hw->phy_id == M88E1111_I_PHY_ID) match = true; - break; - case e1000_80003es2lan: - if (hw->phy_id == GG82563_E_PHY_ID) match = true; - break; - case e1000_ich8lan: - if (hw->phy_id == IGP03E1000_E_PHY_ID) match = true; - if (hw->phy_id == IFE_E_PHY_ID) match = true; - if (hw->phy_id == IFE_PLUS_E_PHY_ID) match = true; - if (hw->phy_id == IFE_C_E_PHY_ID) match = true; - break; - default: - DEBUGOUT1("Invalid MAC type %d\n", hw->mac_type); - return -E1000_ERR_CONFIG; - } - phy_init_status = e1000_set_phy_type(hw); - - if ((match) && (phy_init_status == E1000_SUCCESS)) { - DEBUGOUT1("PHY ID 0x%X detected\n", hw->phy_id); - return E1000_SUCCESS; - } - DEBUGOUT1("Invalid PHY ID 0x%X\n", hw->phy_id); - return -E1000_ERR_PHY; + hw->phy_id = (u32) (phy_id_high << 16); + udelay(20); + ret_val = e1000_read_phy_reg(hw, PHY_ID2, &phy_id_low); + if (ret_val) + return ret_val; + + hw->phy_id |= (u32) (phy_id_low & PHY_REVISION_MASK); + hw->phy_revision = (u32) phy_id_low & ~PHY_REVISION_MASK; + + switch (hw->mac_type) { + case e1000_82543: + if (hw->phy_id == M88E1000_E_PHY_ID) + match = true; + break; + case e1000_82544: + if (hw->phy_id == M88E1000_I_PHY_ID) + match = true; + break; + case e1000_82540: + case e1000_82545: + case e1000_82545_rev_3: + case e1000_82546: + case e1000_82546_rev_3: + if (hw->phy_id == M88E1011_I_PHY_ID) + match = true; + break; + case e1000_82541: + case e1000_82541_rev_2: + case e1000_82547: + case e1000_82547_rev_2: + if (hw->phy_id == IGP01E1000_I_PHY_ID) + match = true; + break; + default: + DEBUGOUT1("Invalid MAC type %d\n", hw->mac_type); + return -E1000_ERR_CONFIG; + } + phy_init_status = e1000_set_phy_type(hw); + + if ((match) && (phy_init_status == E1000_SUCCESS)) { + DEBUGOUT1("PHY ID 0x%X detected\n", hw->phy_id); + return E1000_SUCCESS; + } + DEBUGOUT1("Invalid PHY ID 0x%X\n", hw->phy_id); + return -E1000_ERR_PHY; } -/****************************************************************************** -* Resets the PHY's DSP -* -* hw - Struct containing variables accessed by shared code -******************************************************************************/ +/** + * e1000_phy_reset_dsp - reset DSP + * @hw: Struct containing variables accessed by shared code + * + * Resets the PHY's DSP + */ static s32 e1000_phy_reset_dsp(struct e1000_hw *hw) { - s32 ret_val; - DEBUGFUNC("e1000_phy_reset_dsp"); - - do { - if (hw->phy_type != e1000_phy_gg82563) { - ret_val = e1000_write_phy_reg(hw, 29, 0x001d); - if (ret_val) break; - } - ret_val = e1000_write_phy_reg(hw, 30, 0x00c1); - if (ret_val) break; - ret_val = e1000_write_phy_reg(hw, 30, 0x0000); - if (ret_val) break; - ret_val = E1000_SUCCESS; - } while (0); - - return ret_val; + s32 ret_val; + DEBUGFUNC("e1000_phy_reset_dsp"); + + do { + ret_val = e1000_write_phy_reg(hw, 29, 0x001d); + if (ret_val) + break; + ret_val = e1000_write_phy_reg(hw, 30, 0x00c1); + if (ret_val) + break; + ret_val = e1000_write_phy_reg(hw, 30, 0x0000); + if (ret_val) + break; + ret_val = E1000_SUCCESS; + } while (0); + + return ret_val; } -/****************************************************************************** -* Get PHY information from various PHY registers for igp PHY only. -* -* hw - Struct containing variables accessed by shared code -* phy_info - PHY information structure -******************************************************************************/ +/** + * e1000_phy_igp_get_info - get igp specific registers + * @hw: Struct containing variables accessed by shared code + * @phy_info: PHY information structure + * + * Get PHY information from various PHY registers for igp PHY only. + */ static s32 e1000_phy_igp_get_info(struct e1000_hw *hw, struct e1000_phy_info *phy_info) { - s32 ret_val; - u16 phy_data, min_length, max_length, average; - e1000_rev_polarity polarity; - - DEBUGFUNC("e1000_phy_igp_get_info"); - - /* The downshift status is checked only once, after link is established, - * and it stored in the hw->speed_downgraded parameter. */ - phy_info->downshift = (e1000_downshift)hw->speed_downgraded; - - /* IGP01E1000 does not need to support it. */ - phy_info->extended_10bt_distance = e1000_10bt_ext_dist_enable_normal; - - /* IGP01E1000 always correct polarity reversal */ - phy_info->polarity_correction = e1000_polarity_reversal_enabled; - - /* Check polarity status */ - ret_val = e1000_check_polarity(hw, &polarity); - if (ret_val) - return ret_val; - - phy_info->cable_polarity = polarity; - - ret_val = e1000_read_phy_reg(hw, IGP01E1000_PHY_PORT_STATUS, &phy_data); - if (ret_val) - return ret_val; - - phy_info->mdix_mode = (e1000_auto_x_mode)((phy_data & IGP01E1000_PSSR_MDIX) >> - IGP01E1000_PSSR_MDIX_SHIFT); - - if ((phy_data & IGP01E1000_PSSR_SPEED_MASK) == - IGP01E1000_PSSR_SPEED_1000MBPS) { - /* Local/Remote Receiver Information are only valid at 1000 Mbps */ - ret_val = e1000_read_phy_reg(hw, PHY_1000T_STATUS, &phy_data); - if (ret_val) - return ret_val; - - phy_info->local_rx = ((phy_data & SR_1000T_LOCAL_RX_STATUS) >> - SR_1000T_LOCAL_RX_STATUS_SHIFT) ? - e1000_1000t_rx_status_ok : e1000_1000t_rx_status_not_ok; - phy_info->remote_rx = ((phy_data & SR_1000T_REMOTE_RX_STATUS) >> - SR_1000T_REMOTE_RX_STATUS_SHIFT) ? - e1000_1000t_rx_status_ok : e1000_1000t_rx_status_not_ok; - - /* Get cable length */ - ret_val = e1000_get_cable_length(hw, &min_length, &max_length); - if (ret_val) - return ret_val; - - /* Translate to old method */ - average = (max_length + min_length) / 2; - - if (average <= e1000_igp_cable_length_50) - phy_info->cable_length = e1000_cable_length_50; - else if (average <= e1000_igp_cable_length_80) - phy_info->cable_length = e1000_cable_length_50_80; - else if (average <= e1000_igp_cable_length_110) - phy_info->cable_length = e1000_cable_length_80_110; - else if (average <= e1000_igp_cable_length_140) - phy_info->cable_length = e1000_cable_length_110_140; - else - phy_info->cable_length = e1000_cable_length_140; - } - - return E1000_SUCCESS; -} + s32 ret_val; + u16 phy_data, min_length, max_length, average; + e1000_rev_polarity polarity; + + DEBUGFUNC("e1000_phy_igp_get_info"); + + /* The downshift status is checked only once, after link is established, + * and it stored in the hw->speed_downgraded parameter. */ + phy_info->downshift = (e1000_downshift) hw->speed_downgraded; + + /* IGP01E1000 does not need to support it. */ + phy_info->extended_10bt_distance = e1000_10bt_ext_dist_enable_normal; + + /* IGP01E1000 always correct polarity reversal */ + phy_info->polarity_correction = e1000_polarity_reversal_enabled; + + /* Check polarity status */ + ret_val = e1000_check_polarity(hw, &polarity); + if (ret_val) + return ret_val; + + phy_info->cable_polarity = polarity; + + ret_val = e1000_read_phy_reg(hw, IGP01E1000_PHY_PORT_STATUS, &phy_data); + if (ret_val) + return ret_val; + + phy_info->mdix_mode = + (e1000_auto_x_mode) ((phy_data & IGP01E1000_PSSR_MDIX) >> + IGP01E1000_PSSR_MDIX_SHIFT); + + if ((phy_data & IGP01E1000_PSSR_SPEED_MASK) == + IGP01E1000_PSSR_SPEED_1000MBPS) { + /* Local/Remote Receiver Information are only valid at 1000 Mbps */ + ret_val = e1000_read_phy_reg(hw, PHY_1000T_STATUS, &phy_data); + if (ret_val) + return ret_val; + + phy_info->local_rx = ((phy_data & SR_1000T_LOCAL_RX_STATUS) >> + SR_1000T_LOCAL_RX_STATUS_SHIFT) ? + e1000_1000t_rx_status_ok : e1000_1000t_rx_status_not_ok; + phy_info->remote_rx = ((phy_data & SR_1000T_REMOTE_RX_STATUS) >> + SR_1000T_REMOTE_RX_STATUS_SHIFT) ? + e1000_1000t_rx_status_ok : e1000_1000t_rx_status_not_ok; + + /* Get cable length */ + ret_val = e1000_get_cable_length(hw, &min_length, &max_length); + if (ret_val) + return ret_val; + + /* Translate to old method */ + average = (max_length + min_length) / 2; + + if (average <= e1000_igp_cable_length_50) + phy_info->cable_length = e1000_cable_length_50; + else if (average <= e1000_igp_cable_length_80) + phy_info->cable_length = e1000_cable_length_50_80; + else if (average <= e1000_igp_cable_length_110) + phy_info->cable_length = e1000_cable_length_80_110; + else if (average <= e1000_igp_cable_length_140) + phy_info->cable_length = e1000_cable_length_110_140; + else + phy_info->cable_length = e1000_cable_length_140; + } -/****************************************************************************** -* Get PHY information from various PHY registers for ife PHY only. -* -* hw - Struct containing variables accessed by shared code -* phy_info - PHY information structure -******************************************************************************/ -static s32 e1000_phy_ife_get_info(struct e1000_hw *hw, - struct e1000_phy_info *phy_info) -{ - s32 ret_val; - u16 phy_data; - e1000_rev_polarity polarity; - - DEBUGFUNC("e1000_phy_ife_get_info"); - - phy_info->downshift = (e1000_downshift)hw->speed_downgraded; - phy_info->extended_10bt_distance = e1000_10bt_ext_dist_enable_normal; - - ret_val = e1000_read_phy_reg(hw, IFE_PHY_SPECIAL_CONTROL, &phy_data); - if (ret_val) - return ret_val; - phy_info->polarity_correction = - ((phy_data & IFE_PSC_AUTO_POLARITY_DISABLE) >> - IFE_PSC_AUTO_POLARITY_DISABLE_SHIFT) ? - e1000_polarity_reversal_disabled : e1000_polarity_reversal_enabled; - - if (phy_info->polarity_correction == e1000_polarity_reversal_enabled) { - ret_val = e1000_check_polarity(hw, &polarity); - if (ret_val) - return ret_val; - } else { - /* Polarity is forced. */ - polarity = ((phy_data & IFE_PSC_FORCE_POLARITY) >> - IFE_PSC_FORCE_POLARITY_SHIFT) ? - e1000_rev_polarity_reversed : e1000_rev_polarity_normal; - } - phy_info->cable_polarity = polarity; - - ret_val = e1000_read_phy_reg(hw, IFE_PHY_MDIX_CONTROL, &phy_data); - if (ret_val) - return ret_val; - - phy_info->mdix_mode = (e1000_auto_x_mode) - ((phy_data & (IFE_PMC_AUTO_MDIX | IFE_PMC_FORCE_MDIX)) >> - IFE_PMC_MDIX_MODE_SHIFT); - - return E1000_SUCCESS; + return E1000_SUCCESS; } -/****************************************************************************** -* Get PHY information from various PHY registers fot m88 PHY only. -* -* hw - Struct containing variables accessed by shared code -* phy_info - PHY information structure -******************************************************************************/ +/** + * e1000_phy_m88_get_info - get m88 specific registers + * @hw: Struct containing variables accessed by shared code + * @phy_info: PHY information structure + * + * Get PHY information from various PHY registers for m88 PHY only. + */ static s32 e1000_phy_m88_get_info(struct e1000_hw *hw, struct e1000_phy_info *phy_info) { - s32 ret_val; - u16 phy_data; - e1000_rev_polarity polarity; - - DEBUGFUNC("e1000_phy_m88_get_info"); - - /* The downshift status is checked only once, after link is established, - * and it stored in the hw->speed_downgraded parameter. */ - phy_info->downshift = (e1000_downshift)hw->speed_downgraded; - - ret_val = e1000_read_phy_reg(hw, M88E1000_PHY_SPEC_CTRL, &phy_data); - if (ret_val) - return ret_val; - - phy_info->extended_10bt_distance = - ((phy_data & M88E1000_PSCR_10BT_EXT_DIST_ENABLE) >> - M88E1000_PSCR_10BT_EXT_DIST_ENABLE_SHIFT) ? - e1000_10bt_ext_dist_enable_lower : e1000_10bt_ext_dist_enable_normal; - - phy_info->polarity_correction = - ((phy_data & M88E1000_PSCR_POLARITY_REVERSAL) >> - M88E1000_PSCR_POLARITY_REVERSAL_SHIFT) ? - e1000_polarity_reversal_disabled : e1000_polarity_reversal_enabled; - - /* Check polarity status */ - ret_val = e1000_check_polarity(hw, &polarity); - if (ret_val) - return ret_val; - phy_info->cable_polarity = polarity; - - ret_val = e1000_read_phy_reg(hw, M88E1000_PHY_SPEC_STATUS, &phy_data); - if (ret_val) - return ret_val; - - phy_info->mdix_mode = (e1000_auto_x_mode)((phy_data & M88E1000_PSSR_MDIX) >> - M88E1000_PSSR_MDIX_SHIFT); - - if ((phy_data & M88E1000_PSSR_SPEED) == M88E1000_PSSR_1000MBS) { - /* Cable Length Estimation and Local/Remote Receiver Information - * are only valid at 1000 Mbps. - */ - if (hw->phy_type != e1000_phy_gg82563) { - phy_info->cable_length = (e1000_cable_length)((phy_data & M88E1000_PSSR_CABLE_LENGTH) >> - M88E1000_PSSR_CABLE_LENGTH_SHIFT); - } else { - ret_val = e1000_read_phy_reg(hw, GG82563_PHY_DSP_DISTANCE, - &phy_data); - if (ret_val) - return ret_val; - - phy_info->cable_length = (e1000_cable_length)(phy_data & GG82563_DSPD_CABLE_LENGTH); - } - - ret_val = e1000_read_phy_reg(hw, PHY_1000T_STATUS, &phy_data); - if (ret_val) - return ret_val; - - phy_info->local_rx = ((phy_data & SR_1000T_LOCAL_RX_STATUS) >> - SR_1000T_LOCAL_RX_STATUS_SHIFT) ? - e1000_1000t_rx_status_ok : e1000_1000t_rx_status_not_ok; - phy_info->remote_rx = ((phy_data & SR_1000T_REMOTE_RX_STATUS) >> - SR_1000T_REMOTE_RX_STATUS_SHIFT) ? - e1000_1000t_rx_status_ok : e1000_1000t_rx_status_not_ok; - - } - - return E1000_SUCCESS; + s32 ret_val; + u16 phy_data; + e1000_rev_polarity polarity; + + DEBUGFUNC("e1000_phy_m88_get_info"); + + /* The downshift status is checked only once, after link is established, + * and it stored in the hw->speed_downgraded parameter. */ + phy_info->downshift = (e1000_downshift) hw->speed_downgraded; + + ret_val = e1000_read_phy_reg(hw, M88E1000_PHY_SPEC_CTRL, &phy_data); + if (ret_val) + return ret_val; + + phy_info->extended_10bt_distance = + ((phy_data & M88E1000_PSCR_10BT_EXT_DIST_ENABLE) >> + M88E1000_PSCR_10BT_EXT_DIST_ENABLE_SHIFT) ? + e1000_10bt_ext_dist_enable_lower : + e1000_10bt_ext_dist_enable_normal; + + phy_info->polarity_correction = + ((phy_data & M88E1000_PSCR_POLARITY_REVERSAL) >> + M88E1000_PSCR_POLARITY_REVERSAL_SHIFT) ? + e1000_polarity_reversal_disabled : e1000_polarity_reversal_enabled; + + /* Check polarity status */ + ret_val = e1000_check_polarity(hw, &polarity); + if (ret_val) + return ret_val; + phy_info->cable_polarity = polarity; + + ret_val = e1000_read_phy_reg(hw, M88E1000_PHY_SPEC_STATUS, &phy_data); + if (ret_val) + return ret_val; + + phy_info->mdix_mode = + (e1000_auto_x_mode) ((phy_data & M88E1000_PSSR_MDIX) >> + M88E1000_PSSR_MDIX_SHIFT); + + if ((phy_data & M88E1000_PSSR_SPEED) == M88E1000_PSSR_1000MBS) { + /* Cable Length Estimation and Local/Remote Receiver Information + * are only valid at 1000 Mbps. + */ + phy_info->cable_length = + (e1000_cable_length) ((phy_data & + M88E1000_PSSR_CABLE_LENGTH) >> + M88E1000_PSSR_CABLE_LENGTH_SHIFT); + + ret_val = e1000_read_phy_reg(hw, PHY_1000T_STATUS, &phy_data); + if (ret_val) + return ret_val; + + phy_info->local_rx = ((phy_data & SR_1000T_LOCAL_RX_STATUS) >> + SR_1000T_LOCAL_RX_STATUS_SHIFT) ? + e1000_1000t_rx_status_ok : e1000_1000t_rx_status_not_ok; + phy_info->remote_rx = ((phy_data & SR_1000T_REMOTE_RX_STATUS) >> + SR_1000T_REMOTE_RX_STATUS_SHIFT) ? + e1000_1000t_rx_status_ok : e1000_1000t_rx_status_not_ok; + + } + + return E1000_SUCCESS; } -/****************************************************************************** -* Get PHY information from various PHY registers -* -* hw - Struct containing variables accessed by shared code -* phy_info - PHY information structure -******************************************************************************/ +/** + * e1000_phy_get_info - request phy info + * @hw: Struct containing variables accessed by shared code + * @phy_info: PHY information structure + * + * Get PHY information from various PHY registers + */ s32 e1000_phy_get_info(struct e1000_hw *hw, struct e1000_phy_info *phy_info) { - s32 ret_val; - u16 phy_data; - - DEBUGFUNC("e1000_phy_get_info"); - - phy_info->cable_length = e1000_cable_length_undefined; - phy_info->extended_10bt_distance = e1000_10bt_ext_dist_enable_undefined; - phy_info->cable_polarity = e1000_rev_polarity_undefined; - phy_info->downshift = e1000_downshift_undefined; - phy_info->polarity_correction = e1000_polarity_reversal_undefined; - phy_info->mdix_mode = e1000_auto_x_mode_undefined; - phy_info->local_rx = e1000_1000t_rx_status_undefined; - phy_info->remote_rx = e1000_1000t_rx_status_undefined; - - if (hw->media_type != e1000_media_type_copper) { - DEBUGOUT("PHY info is only valid for copper media\n"); - return -E1000_ERR_CONFIG; - } - - ret_val = e1000_read_phy_reg(hw, PHY_STATUS, &phy_data); - if (ret_val) - return ret_val; - - ret_val = e1000_read_phy_reg(hw, PHY_STATUS, &phy_data); - if (ret_val) - return ret_val; - - if ((phy_data & MII_SR_LINK_STATUS) != MII_SR_LINK_STATUS) { - DEBUGOUT("PHY info is only valid if link is up\n"); - return -E1000_ERR_CONFIG; - } - - if (hw->phy_type == e1000_phy_igp || - hw->phy_type == e1000_phy_igp_3 || - hw->phy_type == e1000_phy_igp_2) - return e1000_phy_igp_get_info(hw, phy_info); - else if (hw->phy_type == e1000_phy_ife) - return e1000_phy_ife_get_info(hw, phy_info); - else - return e1000_phy_m88_get_info(hw, phy_info); + s32 ret_val; + u16 phy_data; + + DEBUGFUNC("e1000_phy_get_info"); + + phy_info->cable_length = e1000_cable_length_undefined; + phy_info->extended_10bt_distance = e1000_10bt_ext_dist_enable_undefined; + phy_info->cable_polarity = e1000_rev_polarity_undefined; + phy_info->downshift = e1000_downshift_undefined; + phy_info->polarity_correction = e1000_polarity_reversal_undefined; + phy_info->mdix_mode = e1000_auto_x_mode_undefined; + phy_info->local_rx = e1000_1000t_rx_status_undefined; + phy_info->remote_rx = e1000_1000t_rx_status_undefined; + + if (hw->media_type != e1000_media_type_copper) { + DEBUGOUT("PHY info is only valid for copper media\n"); + return -E1000_ERR_CONFIG; + } + + ret_val = e1000_read_phy_reg(hw, PHY_STATUS, &phy_data); + if (ret_val) + return ret_val; + + ret_val = e1000_read_phy_reg(hw, PHY_STATUS, &phy_data); + if (ret_val) + return ret_val; + + if ((phy_data & MII_SR_LINK_STATUS) != MII_SR_LINK_STATUS) { + DEBUGOUT("PHY info is only valid if link is up\n"); + return -E1000_ERR_CONFIG; + } + + if (hw->phy_type == e1000_phy_igp) + return e1000_phy_igp_get_info(hw, phy_info); + else + return e1000_phy_m88_get_info(hw, phy_info); } s32 e1000_validate_mdi_setting(struct e1000_hw *hw) { - DEBUGFUNC("e1000_validate_mdi_settings"); - - if (!hw->autoneg && (hw->mdix == 0 || hw->mdix == 3)) { - DEBUGOUT("Invalid MDI setting detected\n"); - hw->mdix = 1; - return -E1000_ERR_CONFIG; - } - return E1000_SUCCESS; -} + DEBUGFUNC("e1000_validate_mdi_settings"); + if (!hw->autoneg && (hw->mdix == 0 || hw->mdix == 3)) { + DEBUGOUT("Invalid MDI setting detected\n"); + hw->mdix = 1; + return -E1000_ERR_CONFIG; + } + return E1000_SUCCESS; +} -/****************************************************************************** - * Sets up eeprom variables in the hw struct. Must be called after mac_type - * is configured. Additionally, if this is ICH8, the flash controller GbE - * registers must be mapped, or this will crash. +/** + * e1000_init_eeprom_params - initialize sw eeprom vars + * @hw: Struct containing variables accessed by shared code * - * hw - Struct containing variables accessed by shared code - *****************************************************************************/ + * Sets up eeprom variables in the hw struct. Must be called after mac_type + * is configured. + */ s32 e1000_init_eeprom_params(struct e1000_hw *hw) { - struct e1000_eeprom_info *eeprom = &hw->eeprom; - u32 eecd = er32(EECD); - s32 ret_val = E1000_SUCCESS; - u16 eeprom_size; - - DEBUGFUNC("e1000_init_eeprom_params"); - - switch (hw->mac_type) { - case e1000_82542_rev2_0: - case e1000_82542_rev2_1: - case e1000_82543: - case e1000_82544: - eeprom->type = e1000_eeprom_microwire; - eeprom->word_size = 64; - eeprom->opcode_bits = 3; - eeprom->address_bits = 6; - eeprom->delay_usec = 50; - eeprom->use_eerd = false; - eeprom->use_eewr = false; - break; - case e1000_82540: - case e1000_82545: - case e1000_82545_rev_3: - case e1000_82546: - case e1000_82546_rev_3: - eeprom->type = e1000_eeprom_microwire; - eeprom->opcode_bits = 3; - eeprom->delay_usec = 50; - if (eecd & E1000_EECD_SIZE) { - eeprom->word_size = 256; - eeprom->address_bits = 8; - } else { - eeprom->word_size = 64; - eeprom->address_bits = 6; - } - eeprom->use_eerd = false; - eeprom->use_eewr = false; - break; - case e1000_82541: - case e1000_82541_rev_2: - case e1000_82547: - case e1000_82547_rev_2: - if (eecd & E1000_EECD_TYPE) { - eeprom->type = e1000_eeprom_spi; - eeprom->opcode_bits = 8; - eeprom->delay_usec = 1; - if (eecd & E1000_EECD_ADDR_BITS) { - eeprom->page_size = 32; - eeprom->address_bits = 16; - } else { - eeprom->page_size = 8; - eeprom->address_bits = 8; - } - } else { - eeprom->type = e1000_eeprom_microwire; - eeprom->opcode_bits = 3; - eeprom->delay_usec = 50; - if (eecd & E1000_EECD_ADDR_BITS) { - eeprom->word_size = 256; - eeprom->address_bits = 8; - } else { - eeprom->word_size = 64; - eeprom->address_bits = 6; - } - } - eeprom->use_eerd = false; - eeprom->use_eewr = false; - break; - case e1000_82571: - case e1000_82572: - eeprom->type = e1000_eeprom_spi; - eeprom->opcode_bits = 8; - eeprom->delay_usec = 1; - if (eecd & E1000_EECD_ADDR_BITS) { - eeprom->page_size = 32; - eeprom->address_bits = 16; - } else { - eeprom->page_size = 8; - eeprom->address_bits = 8; - } - eeprom->use_eerd = false; - eeprom->use_eewr = false; - break; - case e1000_82573: - eeprom->type = e1000_eeprom_spi; - eeprom->opcode_bits = 8; - eeprom->delay_usec = 1; - if (eecd & E1000_EECD_ADDR_BITS) { - eeprom->page_size = 32; - eeprom->address_bits = 16; - } else { - eeprom->page_size = 8; - eeprom->address_bits = 8; - } - eeprom->use_eerd = true; - eeprom->use_eewr = true; - if (!e1000_is_onboard_nvm_eeprom(hw)) { - eeprom->type = e1000_eeprom_flash; - eeprom->word_size = 2048; - - /* Ensure that the Autonomous FLASH update bit is cleared due to - * Flash update issue on parts which use a FLASH for NVM. */ - eecd &= ~E1000_EECD_AUPDEN; - ew32(EECD, eecd); - } - break; - case e1000_80003es2lan: - eeprom->type = e1000_eeprom_spi; - eeprom->opcode_bits = 8; - eeprom->delay_usec = 1; - if (eecd & E1000_EECD_ADDR_BITS) { - eeprom->page_size = 32; - eeprom->address_bits = 16; - } else { - eeprom->page_size = 8; - eeprom->address_bits = 8; - } - eeprom->use_eerd = true; - eeprom->use_eewr = false; - break; - case e1000_ich8lan: - { - s32 i = 0; - u32 flash_size = E1000_READ_ICH_FLASH_REG(hw, ICH_FLASH_GFPREG); - - eeprom->type = e1000_eeprom_ich8; - eeprom->use_eerd = false; - eeprom->use_eewr = false; - eeprom->word_size = E1000_SHADOW_RAM_WORDS; - - /* Zero the shadow RAM structure. But don't load it from NVM - * so as to save time for driver init */ - if (hw->eeprom_shadow_ram != NULL) { - for (i = 0; i < E1000_SHADOW_RAM_WORDS; i++) { - hw->eeprom_shadow_ram[i].modified = false; - hw->eeprom_shadow_ram[i].eeprom_word = 0xFFFF; - } - } - - hw->flash_base_addr = (flash_size & ICH_GFPREG_BASE_MASK) * - ICH_FLASH_SECTOR_SIZE; - - hw->flash_bank_size = ((flash_size >> 16) & ICH_GFPREG_BASE_MASK) + 1; - hw->flash_bank_size -= (flash_size & ICH_GFPREG_BASE_MASK); - - hw->flash_bank_size *= ICH_FLASH_SECTOR_SIZE; - - hw->flash_bank_size /= 2 * sizeof(u16); - - break; - } - default: - break; - } - - if (eeprom->type == e1000_eeprom_spi) { - /* eeprom_size will be an enum [0..8] that maps to eeprom sizes 128B to - * 32KB (incremented by powers of 2). - */ - if (hw->mac_type <= e1000_82547_rev_2) { - /* Set to default value for initial eeprom read. */ - eeprom->word_size = 64; - ret_val = e1000_read_eeprom(hw, EEPROM_CFG, 1, &eeprom_size); - if (ret_val) - return ret_val; - eeprom_size = (eeprom_size & EEPROM_SIZE_MASK) >> EEPROM_SIZE_SHIFT; - /* 256B eeprom size was not supported in earlier hardware, so we - * bump eeprom_size up one to ensure that "1" (which maps to 256B) - * is never the result used in the shifting logic below. */ - if (eeprom_size) - eeprom_size++; - } else { - eeprom_size = (u16)((eecd & E1000_EECD_SIZE_EX_MASK) >> - E1000_EECD_SIZE_EX_SHIFT); - } - - eeprom->word_size = 1 << (eeprom_size + EEPROM_WORD_SIZE_SHIFT); - } - return ret_val; + struct e1000_eeprom_info *eeprom = &hw->eeprom; + u32 eecd = er32(EECD); + s32 ret_val = E1000_SUCCESS; + u16 eeprom_size; + + DEBUGFUNC("e1000_init_eeprom_params"); + + switch (hw->mac_type) { + case e1000_82542_rev2_0: + case e1000_82542_rev2_1: + case e1000_82543: + case e1000_82544: + eeprom->type = e1000_eeprom_microwire; + eeprom->word_size = 64; + eeprom->opcode_bits = 3; + eeprom->address_bits = 6; + eeprom->delay_usec = 50; + break; + case e1000_82540: + case e1000_82545: + case e1000_82545_rev_3: + case e1000_82546: + case e1000_82546_rev_3: + eeprom->type = e1000_eeprom_microwire; + eeprom->opcode_bits = 3; + eeprom->delay_usec = 50; + if (eecd & E1000_EECD_SIZE) { + eeprom->word_size = 256; + eeprom->address_bits = 8; + } else { + eeprom->word_size = 64; + eeprom->address_bits = 6; + } + break; + case e1000_82541: + case e1000_82541_rev_2: + case e1000_82547: + case e1000_82547_rev_2: + if (eecd & E1000_EECD_TYPE) { + eeprom->type = e1000_eeprom_spi; + eeprom->opcode_bits = 8; + eeprom->delay_usec = 1; + if (eecd & E1000_EECD_ADDR_BITS) { + eeprom->page_size = 32; + eeprom->address_bits = 16; + } else { + eeprom->page_size = 8; + eeprom->address_bits = 8; + } + } else { + eeprom->type = e1000_eeprom_microwire; + eeprom->opcode_bits = 3; + eeprom->delay_usec = 50; + if (eecd & E1000_EECD_ADDR_BITS) { + eeprom->word_size = 256; + eeprom->address_bits = 8; + } else { + eeprom->word_size = 64; + eeprom->address_bits = 6; + } + } + break; + default: + break; + } + + if (eeprom->type == e1000_eeprom_spi) { + /* eeprom_size will be an enum [0..8] that maps to eeprom sizes 128B to + * 32KB (incremented by powers of 2). + */ + /* Set to default value for initial eeprom read. */ + eeprom->word_size = 64; + ret_val = e1000_read_eeprom(hw, EEPROM_CFG, 1, &eeprom_size); + if (ret_val) + return ret_val; + eeprom_size = + (eeprom_size & EEPROM_SIZE_MASK) >> EEPROM_SIZE_SHIFT; + /* 256B eeprom size was not supported in earlier hardware, so we + * bump eeprom_size up one to ensure that "1" (which maps to 256B) + * is never the result used in the shifting logic below. */ + if (eeprom_size) + eeprom_size++; + + eeprom->word_size = 1 << (eeprom_size + EEPROM_WORD_SIZE_SHIFT); + } + return ret_val; } -/****************************************************************************** - * Raises the EEPROM's clock input. - * - * hw - Struct containing variables accessed by shared code - * eecd - EECD's current value - *****************************************************************************/ +/** + * e1000_raise_ee_clk - Raises the EEPROM's clock input. + * @hw: Struct containing variables accessed by shared code + * @eecd: EECD's current value + */ static void e1000_raise_ee_clk(struct e1000_hw *hw, u32 *eecd) { - /* Raise the clock input to the EEPROM (by setting the SK bit), and then - * wait <delay> microseconds. - */ - *eecd = *eecd | E1000_EECD_SK; - ew32(EECD, *eecd); - E1000_WRITE_FLUSH(); - udelay(hw->eeprom.delay_usec); + /* Raise the clock input to the EEPROM (by setting the SK bit), and then + * wait <delay> microseconds. + */ + *eecd = *eecd | E1000_EECD_SK; + ew32(EECD, *eecd); + E1000_WRITE_FLUSH(); + udelay(hw->eeprom.delay_usec); } -/****************************************************************************** - * Lowers the EEPROM's clock input. - * - * hw - Struct containing variables accessed by shared code - * eecd - EECD's current value - *****************************************************************************/ +/** + * e1000_lower_ee_clk - Lowers the EEPROM's clock input. + * @hw: Struct containing variables accessed by shared code + * @eecd: EECD's current value + */ static void e1000_lower_ee_clk(struct e1000_hw *hw, u32 *eecd) { - /* Lower the clock input to the EEPROM (by clearing the SK bit), and then - * wait 50 microseconds. - */ - *eecd = *eecd & ~E1000_EECD_SK; - ew32(EECD, *eecd); - E1000_WRITE_FLUSH(); - udelay(hw->eeprom.delay_usec); + /* Lower the clock input to the EEPROM (by clearing the SK bit), and then + * wait 50 microseconds. + */ + *eecd = *eecd & ~E1000_EECD_SK; + ew32(EECD, *eecd); + E1000_WRITE_FLUSH(); + udelay(hw->eeprom.delay_usec); } -/****************************************************************************** - * Shift data bits out to the EEPROM. - * - * hw - Struct containing variables accessed by shared code - * data - data to send to the EEPROM - * count - number of bits to shift out - *****************************************************************************/ +/** + * e1000_shift_out_ee_bits - Shift data bits out to the EEPROM. + * @hw: Struct containing variables accessed by shared code + * @data: data to send to the EEPROM + * @count: number of bits to shift out + */ static void e1000_shift_out_ee_bits(struct e1000_hw *hw, u16 data, u16 count) { - struct e1000_eeprom_info *eeprom = &hw->eeprom; - u32 eecd; - u32 mask; - - /* We need to shift "count" bits out to the EEPROM. So, value in the - * "data" parameter will be shifted out to the EEPROM one bit at a time. - * In order to do this, "data" must be broken down into bits. - */ - mask = 0x01 << (count - 1); - eecd = er32(EECD); - if (eeprom->type == e1000_eeprom_microwire) { - eecd &= ~E1000_EECD_DO; - } else if (eeprom->type == e1000_eeprom_spi) { - eecd |= E1000_EECD_DO; - } - do { - /* A "1" is shifted out to the EEPROM by setting bit "DI" to a "1", - * and then raising and then lowering the clock (the SK bit controls - * the clock input to the EEPROM). A "0" is shifted out to the EEPROM - * by setting "DI" to "0" and then raising and then lowering the clock. - */ - eecd &= ~E1000_EECD_DI; - - if (data & mask) - eecd |= E1000_EECD_DI; - - ew32(EECD, eecd); - E1000_WRITE_FLUSH(); - - udelay(eeprom->delay_usec); - - e1000_raise_ee_clk(hw, &eecd); - e1000_lower_ee_clk(hw, &eecd); - - mask = mask >> 1; - - } while (mask); - - /* We leave the "DI" bit set to "0" when we leave this routine. */ - eecd &= ~E1000_EECD_DI; - ew32(EECD, eecd); + struct e1000_eeprom_info *eeprom = &hw->eeprom; + u32 eecd; + u32 mask; + + /* We need to shift "count" bits out to the EEPROM. So, value in the + * "data" parameter will be shifted out to the EEPROM one bit at a time. + * In order to do this, "data" must be broken down into bits. + */ + mask = 0x01 << (count - 1); + eecd = er32(EECD); + if (eeprom->type == e1000_eeprom_microwire) { + eecd &= ~E1000_EECD_DO; + } else if (eeprom->type == e1000_eeprom_spi) { + eecd |= E1000_EECD_DO; + } + do { + /* A "1" is shifted out to the EEPROM by setting bit "DI" to a "1", + * and then raising and then lowering the clock (the SK bit controls + * the clock input to the EEPROM). A "0" is shifted out to the EEPROM + * by setting "DI" to "0" and then raising and then lowering the clock. + */ + eecd &= ~E1000_EECD_DI; + + if (data & mask) + eecd |= E1000_EECD_DI; + + ew32(EECD, eecd); + E1000_WRITE_FLUSH(); + + udelay(eeprom->delay_usec); + + e1000_raise_ee_clk(hw, &eecd); + e1000_lower_ee_clk(hw, &eecd); + + mask = mask >> 1; + + } while (mask); + + /* We leave the "DI" bit set to "0" when we leave this routine. */ + eecd &= ~E1000_EECD_DI; + ew32(EECD, eecd); } -/****************************************************************************** - * Shift data bits in from the EEPROM - * - * hw - Struct containing variables accessed by shared code - *****************************************************************************/ +/** + * e1000_shift_in_ee_bits - Shift data bits in from the EEPROM + * @hw: Struct containing variables accessed by shared code + * @count: number of bits to shift in + */ static u16 e1000_shift_in_ee_bits(struct e1000_hw *hw, u16 count) { - u32 eecd; - u32 i; - u16 data; + u32 eecd; + u32 i; + u16 data; - /* In order to read a register from the EEPROM, we need to shift 'count' - * bits in from the EEPROM. Bits are "shifted in" by raising the clock - * input to the EEPROM (setting the SK bit), and then reading the value of - * the "DO" bit. During this "shifting in" process the "DI" bit should - * always be clear. - */ + /* In order to read a register from the EEPROM, we need to shift 'count' + * bits in from the EEPROM. Bits are "shifted in" by raising the clock + * input to the EEPROM (setting the SK bit), and then reading the value of + * the "DO" bit. During this "shifting in" process the "DI" bit should + * always be clear. + */ - eecd = er32(EECD); + eecd = er32(EECD); - eecd &= ~(E1000_EECD_DO | E1000_EECD_DI); - data = 0; + eecd &= ~(E1000_EECD_DO | E1000_EECD_DI); + data = 0; - for (i = 0; i < count; i++) { - data = data << 1; - e1000_raise_ee_clk(hw, &eecd); + for (i = 0; i < count; i++) { + data = data << 1; + e1000_raise_ee_clk(hw, &eecd); - eecd = er32(EECD); + eecd = er32(EECD); - eecd &= ~(E1000_EECD_DI); - if (eecd & E1000_EECD_DO) - data |= 1; + eecd &= ~(E1000_EECD_DI); + if (eecd & E1000_EECD_DO) + data |= 1; - e1000_lower_ee_clk(hw, &eecd); - } + e1000_lower_ee_clk(hw, &eecd); + } - return data; + return data; } -/****************************************************************************** - * Prepares EEPROM for access - * - * hw - Struct containing variables accessed by shared code +/** + * e1000_acquire_eeprom - Prepares EEPROM for access + * @hw: Struct containing variables accessed by shared code * * Lowers EEPROM clock. Clears input pin. Sets the chip select pin. This * function should be called before issuing a command to the EEPROM. - *****************************************************************************/ + */ static s32 e1000_acquire_eeprom(struct e1000_hw *hw) { - struct e1000_eeprom_info *eeprom = &hw->eeprom; - u32 eecd, i=0; - - DEBUGFUNC("e1000_acquire_eeprom"); - - if (e1000_swfw_sync_acquire(hw, E1000_SWFW_EEP_SM)) - return -E1000_ERR_SWFW_SYNC; - eecd = er32(EECD); - - if (hw->mac_type != e1000_82573) { - /* Request EEPROM Access */ - if (hw->mac_type > e1000_82544) { - eecd |= E1000_EECD_REQ; - ew32(EECD, eecd); - eecd = er32(EECD); - while ((!(eecd & E1000_EECD_GNT)) && - (i < E1000_EEPROM_GRANT_ATTEMPTS)) { - i++; - udelay(5); - eecd = er32(EECD); - } - if (!(eecd & E1000_EECD_GNT)) { - eecd &= ~E1000_EECD_REQ; - ew32(EECD, eecd); - DEBUGOUT("Could not acquire EEPROM grant\n"); - e1000_swfw_sync_release(hw, E1000_SWFW_EEP_SM); - return -E1000_ERR_EEPROM; - } - } - } - - /* Setup EEPROM for Read/Write */ - - if (eeprom->type == e1000_eeprom_microwire) { - /* Clear SK and DI */ - eecd &= ~(E1000_EECD_DI | E1000_EECD_SK); - ew32(EECD, eecd); - - /* Set CS */ - eecd |= E1000_EECD_CS; - ew32(EECD, eecd); - } else if (eeprom->type == e1000_eeprom_spi) { - /* Clear SK and CS */ - eecd &= ~(E1000_EECD_CS | E1000_EECD_SK); - ew32(EECD, eecd); - udelay(1); - } - - return E1000_SUCCESS; + struct e1000_eeprom_info *eeprom = &hw->eeprom; + u32 eecd, i = 0; + + DEBUGFUNC("e1000_acquire_eeprom"); + + eecd = er32(EECD); + + /* Request EEPROM Access */ + if (hw->mac_type > e1000_82544) { + eecd |= E1000_EECD_REQ; + ew32(EECD, eecd); + eecd = er32(EECD); + while ((!(eecd & E1000_EECD_GNT)) && + (i < E1000_EEPROM_GRANT_ATTEMPTS)) { + i++; + udelay(5); + eecd = er32(EECD); + } + if (!(eecd & E1000_EECD_GNT)) { + eecd &= ~E1000_EECD_REQ; + ew32(EECD, eecd); + DEBUGOUT("Could not acquire EEPROM grant\n"); + return -E1000_ERR_EEPROM; + } + } + + /* Setup EEPROM for Read/Write */ + + if (eeprom->type == e1000_eeprom_microwire) { + /* Clear SK and DI */ + eecd &= ~(E1000_EECD_DI | E1000_EECD_SK); + ew32(EECD, eecd); + + /* Set CS */ + eecd |= E1000_EECD_CS; + ew32(EECD, eecd); + } else if (eeprom->type == e1000_eeprom_spi) { + /* Clear SK and CS */ + eecd &= ~(E1000_EECD_CS | E1000_EECD_SK); + ew32(EECD, eecd); + udelay(1); + } + + return E1000_SUCCESS; } -/****************************************************************************** - * Returns EEPROM to a "standby" state - * - * hw - Struct containing variables accessed by shared code - *****************************************************************************/ +/** + * e1000_standby_eeprom - Returns EEPROM to a "standby" state + * @hw: Struct containing variables accessed by shared code + */ static void e1000_standby_eeprom(struct e1000_hw *hw) { - struct e1000_eeprom_info *eeprom = &hw->eeprom; - u32 eecd; - - eecd = er32(EECD); - - if (eeprom->type == e1000_eeprom_microwire) { - eecd &= ~(E1000_EECD_CS | E1000_EECD_SK); - ew32(EECD, eecd); - E1000_WRITE_FLUSH(); - udelay(eeprom->delay_usec); - - /* Clock high */ - eecd |= E1000_EECD_SK; - ew32(EECD, eecd); - E1000_WRITE_FLUSH(); - udelay(eeprom->delay_usec); - - /* Select EEPROM */ - eecd |= E1000_EECD_CS; - ew32(EECD, eecd); - E1000_WRITE_FLUSH(); - udelay(eeprom->delay_usec); - - /* Clock low */ - eecd &= ~E1000_EECD_SK; - ew32(EECD, eecd); - E1000_WRITE_FLUSH(); - udelay(eeprom->delay_usec); - } else if (eeprom->type == e1000_eeprom_spi) { - /* Toggle CS to flush commands */ - eecd |= E1000_EECD_CS; - ew32(EECD, eecd); - E1000_WRITE_FLUSH(); - udelay(eeprom->delay_usec); - eecd &= ~E1000_EECD_CS; - ew32(EECD, eecd); - E1000_WRITE_FLUSH(); - udelay(eeprom->delay_usec); - } + struct e1000_eeprom_info *eeprom = &hw->eeprom; + u32 eecd; + + eecd = er32(EECD); + + if (eeprom->type == e1000_eeprom_microwire) { + eecd &= ~(E1000_EECD_CS | E1000_EECD_SK); + ew32(EECD, eecd); + E1000_WRITE_FLUSH(); + udelay(eeprom->delay_usec); + + /* Clock high */ + eecd |= E1000_EECD_SK; + ew32(EECD, eecd); + E1000_WRITE_FLUSH(); + udelay(eeprom->delay_usec); + + /* Select EEPROM */ + eecd |= E1000_EECD_CS; + ew32(EECD, eecd); + E1000_WRITE_FLUSH(); + udelay(eeprom->delay_usec); + + /* Clock low */ + eecd &= ~E1000_EECD_SK; + ew32(EECD, eecd); + E1000_WRITE_FLUSH(); + udelay(eeprom->delay_usec); + } else if (eeprom->type == e1000_eeprom_spi) { + /* Toggle CS to flush commands */ + eecd |= E1000_EECD_CS; + ew32(EECD, eecd); + E1000_WRITE_FLUSH(); + udelay(eeprom->delay_usec); + eecd &= ~E1000_EECD_CS; + ew32(EECD, eecd); + E1000_WRITE_FLUSH(); + udelay(eeprom->delay_usec); + } } -/****************************************************************************** - * Terminates a command by inverting the EEPROM's chip select pin +/** + * e1000_release_eeprom - drop chip select + * @hw: Struct containing variables accessed by shared code * - * hw - Struct containing variables accessed by shared code - *****************************************************************************/ + * Terminates a command by inverting the EEPROM's chip select pin + */ static void e1000_release_eeprom(struct e1000_hw *hw) { - u32 eecd; - - DEBUGFUNC("e1000_release_eeprom"); + u32 eecd; - eecd = er32(EECD); + DEBUGFUNC("e1000_release_eeprom"); - if (hw->eeprom.type == e1000_eeprom_spi) { - eecd |= E1000_EECD_CS; /* Pull CS high */ - eecd &= ~E1000_EECD_SK; /* Lower SCK */ + eecd = er32(EECD); - ew32(EECD, eecd); + if (hw->eeprom.type == e1000_eeprom_spi) { + eecd |= E1000_EECD_CS; /* Pull CS high */ + eecd &= ~E1000_EECD_SK; /* Lower SCK */ - udelay(hw->eeprom.delay_usec); - } else if (hw->eeprom.type == e1000_eeprom_microwire) { - /* cleanup eeprom */ + ew32(EECD, eecd); - /* CS on Microwire is active-high */ - eecd &= ~(E1000_EECD_CS | E1000_EECD_DI); + udelay(hw->eeprom.delay_usec); + } else if (hw->eeprom.type == e1000_eeprom_microwire) { + /* cleanup eeprom */ - ew32(EECD, eecd); + /* CS on Microwire is active-high */ + eecd &= ~(E1000_EECD_CS | E1000_EECD_DI); - /* Rising edge of clock */ - eecd |= E1000_EECD_SK; - ew32(EECD, eecd); - E1000_WRITE_FLUSH(); - udelay(hw->eeprom.delay_usec); + ew32(EECD, eecd); - /* Falling edge of clock */ - eecd &= ~E1000_EECD_SK; - ew32(EECD, eecd); - E1000_WRITE_FLUSH(); - udelay(hw->eeprom.delay_usec); - } + /* Rising edge of clock */ + eecd |= E1000_EECD_SK; + ew32(EECD, eecd); + E1000_WRITE_FLUSH(); + udelay(hw->eeprom.delay_usec); - /* Stop requesting EEPROM access */ - if (hw->mac_type > e1000_82544) { - eecd &= ~E1000_EECD_REQ; - ew32(EECD, eecd); - } + /* Falling edge of clock */ + eecd &= ~E1000_EECD_SK; + ew32(EECD, eecd); + E1000_WRITE_FLUSH(); + udelay(hw->eeprom.delay_usec); + } - e1000_swfw_sync_release(hw, E1000_SWFW_EEP_SM); + /* Stop requesting EEPROM access */ + if (hw->mac_type > e1000_82544) { + eecd &= ~E1000_EECD_REQ; + ew32(EECD, eecd); + } } -/****************************************************************************** - * Reads a 16 bit word from the EEPROM. - * - * hw - Struct containing variables accessed by shared code - *****************************************************************************/ +/** + * e1000_spi_eeprom_ready - Reads a 16 bit word from the EEPROM. + * @hw: Struct containing variables accessed by shared code + */ static s32 e1000_spi_eeprom_ready(struct e1000_hw *hw) { - u16 retry_count = 0; - u8 spi_stat_reg; - - DEBUGFUNC("e1000_spi_eeprom_ready"); - - /* Read "Status Register" repeatedly until the LSB is cleared. The - * EEPROM will signal that the command has been completed by clearing - * bit 0 of the internal status register. If it's not cleared within - * 5 milliseconds, then error out. - */ - retry_count = 0; - do { - e1000_shift_out_ee_bits(hw, EEPROM_RDSR_OPCODE_SPI, - hw->eeprom.opcode_bits); - spi_stat_reg = (u8)e1000_shift_in_ee_bits(hw, 8); - if (!(spi_stat_reg & EEPROM_STATUS_RDY_SPI)) - break; - - udelay(5); - retry_count += 5; - - e1000_standby_eeprom(hw); - } while (retry_count < EEPROM_MAX_RETRY_SPI); - - /* ATMEL SPI write time could vary from 0-20mSec on 3.3V devices (and - * only 0-5mSec on 5V devices) - */ - if (retry_count >= EEPROM_MAX_RETRY_SPI) { - DEBUGOUT("SPI EEPROM Status error\n"); - return -E1000_ERR_EEPROM; - } - - return E1000_SUCCESS; -} - -/****************************************************************************** - * Reads a 16 bit word from the EEPROM. - * - * hw - Struct containing variables accessed by shared code - * offset - offset of word in the EEPROM to read - * data - word read from the EEPROM - * words - number of words to read - *****************************************************************************/ -s32 e1000_read_eeprom(struct e1000_hw *hw, u16 offset, u16 words, u16 *data) -{ - s32 ret; - spin_lock(&e1000_eeprom_lock); - ret = e1000_do_read_eeprom(hw, offset, words, data); - spin_unlock(&e1000_eeprom_lock); - return ret; -} - -static s32 e1000_do_read_eeprom(struct e1000_hw *hw, u16 offset, u16 words, u16 *data) -{ - struct e1000_eeprom_info *eeprom = &hw->eeprom; - u32 i = 0; - - DEBUGFUNC("e1000_read_eeprom"); - - /* If eeprom is not yet detected, do so now */ - if (eeprom->word_size == 0) - e1000_init_eeprom_params(hw); - - /* A check for invalid values: offset too large, too many words, and not - * enough words. - */ - if ((offset >= eeprom->word_size) || (words > eeprom->word_size - offset) || - (words == 0)) { - DEBUGOUT2("\"words\" parameter out of bounds. Words = %d, size = %d\n", offset, eeprom->word_size); - return -E1000_ERR_EEPROM; - } - - /* EEPROM's that don't use EERD to read require us to bit-bang the SPI - * directly. In this case, we need to acquire the EEPROM so that - * FW or other port software does not interrupt. - */ - if (e1000_is_onboard_nvm_eeprom(hw) && !hw->eeprom.use_eerd) { - /* Prepare the EEPROM for bit-bang reading */ - if (e1000_acquire_eeprom(hw) != E1000_SUCCESS) - return -E1000_ERR_EEPROM; - } - - /* Eerd register EEPROM access requires no eeprom aquire/release */ - if (eeprom->use_eerd) - return e1000_read_eeprom_eerd(hw, offset, words, data); - - /* ICH EEPROM access is done via the ICH flash controller */ - if (eeprom->type == e1000_eeprom_ich8) - return e1000_read_eeprom_ich8(hw, offset, words, data); - - /* Set up the SPI or Microwire EEPROM for bit-bang reading. We have - * acquired the EEPROM at this point, so any returns should relase it */ - if (eeprom->type == e1000_eeprom_spi) { - u16 word_in; - u8 read_opcode = EEPROM_READ_OPCODE_SPI; - - if (e1000_spi_eeprom_ready(hw)) { - e1000_release_eeprom(hw); - return -E1000_ERR_EEPROM; - } - - e1000_standby_eeprom(hw); - - /* Some SPI eeproms use the 8th address bit embedded in the opcode */ - if ((eeprom->address_bits == 8) && (offset >= 128)) - read_opcode |= EEPROM_A8_OPCODE_SPI; - - /* Send the READ command (opcode + addr) */ - e1000_shift_out_ee_bits(hw, read_opcode, eeprom->opcode_bits); - e1000_shift_out_ee_bits(hw, (u16)(offset*2), eeprom->address_bits); - - /* Read the data. The address of the eeprom internally increments with - * each byte (spi) being read, saving on the overhead of eeprom setup - * and tear-down. The address counter will roll over if reading beyond - * the size of the eeprom, thus allowing the entire memory to be read - * starting from any offset. */ - for (i = 0; i < words; i++) { - word_in = e1000_shift_in_ee_bits(hw, 16); - data[i] = (word_in >> 8) | (word_in << 8); - } - } else if (eeprom->type == e1000_eeprom_microwire) { - for (i = 0; i < words; i++) { - /* Send the READ command (opcode + addr) */ - e1000_shift_out_ee_bits(hw, EEPROM_READ_OPCODE_MICROWIRE, - eeprom->opcode_bits); - e1000_shift_out_ee_bits(hw, (u16)(offset + i), - eeprom->address_bits); - - /* Read the data. For microwire, each word requires the overhead - * of eeprom setup and tear-down. */ - data[i] = e1000_shift_in_ee_bits(hw, 16); - e1000_standby_eeprom(hw); - } - } - - /* End this read operation */ - e1000_release_eeprom(hw); - - return E1000_SUCCESS; -} + u16 retry_count = 0; + u8 spi_stat_reg; -/****************************************************************************** - * Reads a 16 bit word from the EEPROM using the EERD register. - * - * hw - Struct containing variables accessed by shared code - * offset - offset of word in the EEPROM to read - * data - word read from the EEPROM - * words - number of words to read - *****************************************************************************/ -static s32 e1000_read_eeprom_eerd(struct e1000_hw *hw, u16 offset, u16 words, - u16 *data) -{ - u32 i, eerd = 0; - s32 error = 0; + DEBUGFUNC("e1000_spi_eeprom_ready"); - for (i = 0; i < words; i++) { - eerd = ((offset+i) << E1000_EEPROM_RW_ADDR_SHIFT) + - E1000_EEPROM_RW_REG_START; + /* Read "Status Register" repeatedly until the LSB is cleared. The + * EEPROM will signal that the command has been completed by clearing + * bit 0 of the internal status register. If it's not cleared within + * 5 milliseconds, then error out. + */ + retry_count = 0; + do { + e1000_shift_out_ee_bits(hw, EEPROM_RDSR_OPCODE_SPI, + hw->eeprom.opcode_bits); + spi_stat_reg = (u8) e1000_shift_in_ee_bits(hw, 8); + if (!(spi_stat_reg & EEPROM_STATUS_RDY_SPI)) + break; - ew32(EERD, eerd); - error = e1000_poll_eerd_eewr_done(hw, E1000_EEPROM_POLL_READ); + udelay(5); + retry_count += 5; - if (error) { - break; - } - data[i] = (er32(EERD) >> E1000_EEPROM_RW_REG_DATA); + e1000_standby_eeprom(hw); + } while (retry_count < EEPROM_MAX_RETRY_SPI); - } + /* ATMEL SPI write time could vary from 0-20mSec on 3.3V devices (and + * only 0-5mSec on 5V devices) + */ + if (retry_count >= EEPROM_MAX_RETRY_SPI) { + DEBUGOUT("SPI EEPROM Status error\n"); + return -E1000_ERR_EEPROM; + } - return error; + return E1000_SUCCESS; } -/****************************************************************************** - * Writes a 16 bit word from the EEPROM using the EEWR register. - * - * hw - Struct containing variables accessed by shared code - * offset - offset of word in the EEPROM to read - * data - word read from the EEPROM - * words - number of words to read - *****************************************************************************/ -static s32 e1000_write_eeprom_eewr(struct e1000_hw *hw, u16 offset, u16 words, - u16 *data) +/** + * e1000_read_eeprom - Reads a 16 bit word from the EEPROM. + * @hw: Struct containing variables accessed by shared code + * @offset: offset of word in the EEPROM to read + * @data: word read from the EEPROM + * @words: number of words to read + */ +s32 e1000_read_eeprom(struct e1000_hw *hw, u16 offset, u16 words, u16 *data) { - u32 register_value = 0; - u32 i = 0; - s32 error = 0; - - if (e1000_swfw_sync_acquire(hw, E1000_SWFW_EEP_SM)) - return -E1000_ERR_SWFW_SYNC; - - for (i = 0; i < words; i++) { - register_value = (data[i] << E1000_EEPROM_RW_REG_DATA) | - ((offset+i) << E1000_EEPROM_RW_ADDR_SHIFT) | - E1000_EEPROM_RW_REG_START; - - error = e1000_poll_eerd_eewr_done(hw, E1000_EEPROM_POLL_WRITE); - if (error) { - break; - } - - ew32(EEWR, register_value); - - error = e1000_poll_eerd_eewr_done(hw, E1000_EEPROM_POLL_WRITE); - - if (error) { - break; - } - } - - e1000_swfw_sync_release(hw, E1000_SWFW_EEP_SM); - return error; + s32 ret; + spin_lock(&e1000_eeprom_lock); + ret = e1000_do_read_eeprom(hw, offset, words, data); + spin_unlock(&e1000_eeprom_lock); + return ret; } -/****************************************************************************** - * Polls the status bit (bit 1) of the EERD to determine when the read is done. - * - * hw - Struct containing variables accessed by shared code - *****************************************************************************/ -static s32 e1000_poll_eerd_eewr_done(struct e1000_hw *hw, int eerd) +static s32 e1000_do_read_eeprom(struct e1000_hw *hw, u16 offset, u16 words, + u16 *data) { - u32 attempts = 100000; - u32 i, reg = 0; - s32 done = E1000_ERR_EEPROM; - - for (i = 0; i < attempts; i++) { - if (eerd == E1000_EEPROM_POLL_READ) - reg = er32(EERD); - else - reg = er32(EEWR); - - if (reg & E1000_EEPROM_RW_REG_DONE) { - done = E1000_SUCCESS; - break; - } - udelay(5); - } - - return done; -} + struct e1000_eeprom_info *eeprom = &hw->eeprom; + u32 i = 0; -/*************************************************************************** -* Description: Determines if the onboard NVM is FLASH or EEPROM. -* -* hw - Struct containing variables accessed by shared code -****************************************************************************/ -static bool e1000_is_onboard_nvm_eeprom(struct e1000_hw *hw) -{ - u32 eecd = 0; + DEBUGFUNC("e1000_read_eeprom"); - DEBUGFUNC("e1000_is_onboard_nvm_eeprom"); + /* If eeprom is not yet detected, do so now */ + if (eeprom->word_size == 0) + e1000_init_eeprom_params(hw); + + /* A check for invalid values: offset too large, too many words, and not + * enough words. + */ + if ((offset >= eeprom->word_size) + || (words > eeprom->word_size - offset) || (words == 0)) { + DEBUGOUT2 + ("\"words\" parameter out of bounds. Words = %d, size = %d\n", + offset, eeprom->word_size); + return -E1000_ERR_EEPROM; + } - if (hw->mac_type == e1000_ich8lan) - return false; + /* EEPROM's that don't use EERD to read require us to bit-bang the SPI + * directly. In this case, we need to acquire the EEPROM so that + * FW or other port software does not interrupt. + */ + /* Prepare the EEPROM for bit-bang reading */ + if (e1000_acquire_eeprom(hw) != E1000_SUCCESS) + return -E1000_ERR_EEPROM; + + /* Set up the SPI or Microwire EEPROM for bit-bang reading. We have + * acquired the EEPROM at this point, so any returns should release it */ + if (eeprom->type == e1000_eeprom_spi) { + u16 word_in; + u8 read_opcode = EEPROM_READ_OPCODE_SPI; + + if (e1000_spi_eeprom_ready(hw)) { + e1000_release_eeprom(hw); + return -E1000_ERR_EEPROM; + } - if (hw->mac_type == e1000_82573) { - eecd = er32(EECD); + e1000_standby_eeprom(hw); + + /* Some SPI eeproms use the 8th address bit embedded in the opcode */ + if ((eeprom->address_bits == 8) && (offset >= 128)) + read_opcode |= EEPROM_A8_OPCODE_SPI; + + /* Send the READ command (opcode + addr) */ + e1000_shift_out_ee_bits(hw, read_opcode, eeprom->opcode_bits); + e1000_shift_out_ee_bits(hw, (u16) (offset * 2), + eeprom->address_bits); + + /* Read the data. The address of the eeprom internally increments with + * each byte (spi) being read, saving on the overhead of eeprom setup + * and tear-down. The address counter will roll over if reading beyond + * the size of the eeprom, thus allowing the entire memory to be read + * starting from any offset. */ + for (i = 0; i < words; i++) { + word_in = e1000_shift_in_ee_bits(hw, 16); + data[i] = (word_in >> 8) | (word_in << 8); + } + } else if (eeprom->type == e1000_eeprom_microwire) { + for (i = 0; i < words; i++) { + /* Send the READ command (opcode + addr) */ + e1000_shift_out_ee_bits(hw, + EEPROM_READ_OPCODE_MICROWIRE, + eeprom->opcode_bits); + e1000_shift_out_ee_bits(hw, (u16) (offset + i), + eeprom->address_bits); + + /* Read the data. For microwire, each word requires the overhead + * of eeprom setup and tear-down. */ + data[i] = e1000_shift_in_ee_bits(hw, 16); + e1000_standby_eeprom(hw); + } + } - /* Isolate bits 15 & 16 */ - eecd = ((eecd >> 15) & 0x03); + /* End this read operation */ + e1000_release_eeprom(hw); - /* If both bits are set, device is Flash type */ - if (eecd == 0x03) { - return false; - } - } - return true; + return E1000_SUCCESS; } -/****************************************************************************** - * Verifies that the EEPROM has a valid checksum - * - * hw - Struct containing variables accessed by shared code +/** + * e1000_validate_eeprom_checksum - Verifies that the EEPROM has a valid checksum + * @hw: Struct containing variables accessed by shared code * * Reads the first 64 16 bit words of the EEPROM and sums the values read. * If the the sum of the 64 16 bit words is 0xBABA, the EEPROM's checksum is * valid. - *****************************************************************************/ + */ s32 e1000_validate_eeprom_checksum(struct e1000_hw *hw) { - u16 checksum = 0; - u16 i, eeprom_data; - - DEBUGFUNC("e1000_validate_eeprom_checksum"); - - if ((hw->mac_type == e1000_82573) && !e1000_is_onboard_nvm_eeprom(hw)) { - /* Check bit 4 of word 10h. If it is 0, firmware is done updating - * 10h-12h. Checksum may need to be fixed. */ - e1000_read_eeprom(hw, 0x10, 1, &eeprom_data); - if ((eeprom_data & 0x10) == 0) { - /* Read 0x23 and check bit 15. This bit is a 1 when the checksum - * has already been fixed. If the checksum is still wrong and this - * bit is a 1, we need to return bad checksum. Otherwise, we need - * to set this bit to a 1 and update the checksum. */ - e1000_read_eeprom(hw, 0x23, 1, &eeprom_data); - if ((eeprom_data & 0x8000) == 0) { - eeprom_data |= 0x8000; - e1000_write_eeprom(hw, 0x23, 1, &eeprom_data); - e1000_update_eeprom_checksum(hw); - } - } - } - - if (hw->mac_type == e1000_ich8lan) { - /* Drivers must allocate the shadow ram structure for the - * EEPROM checksum to be updated. Otherwise, this bit as well - * as the checksum must both be set correctly for this - * validation to pass. - */ - e1000_read_eeprom(hw, 0x19, 1, &eeprom_data); - if ((eeprom_data & 0x40) == 0) { - eeprom_data |= 0x40; - e1000_write_eeprom(hw, 0x19, 1, &eeprom_data); - e1000_update_eeprom_checksum(hw); - } - } - - for (i = 0; i < (EEPROM_CHECKSUM_REG + 1); i++) { - if (e1000_read_eeprom(hw, i, 1, &eeprom_data) < 0) { - DEBUGOUT("EEPROM Read Error\n"); - return -E1000_ERR_EEPROM; - } - checksum += eeprom_data; - } - - if (checksum == (u16)EEPROM_SUM) - return E1000_SUCCESS; - else { - DEBUGOUT("EEPROM Checksum Invalid\n"); - return -E1000_ERR_EEPROM; - } + u16 checksum = 0; + u16 i, eeprom_data; + + DEBUGFUNC("e1000_validate_eeprom_checksum"); + + for (i = 0; i < (EEPROM_CHECKSUM_REG + 1); i++) { + if (e1000_read_eeprom(hw, i, 1, &eeprom_data) < 0) { + DEBUGOUT("EEPROM Read Error\n"); + return -E1000_ERR_EEPROM; + } + checksum += eeprom_data; + } + + if (checksum == (u16) EEPROM_SUM) + return E1000_SUCCESS; + else { + DEBUGOUT("EEPROM Checksum Invalid\n"); + return -E1000_ERR_EEPROM; + } } -/****************************************************************************** - * Calculates the EEPROM checksum and writes it to the EEPROM - * - * hw - Struct containing variables accessed by shared code +/** + * e1000_update_eeprom_checksum - Calculates/writes the EEPROM checksum + * @hw: Struct containing variables accessed by shared code * * Sums the first 63 16 bit words of the EEPROM. Subtracts the sum from 0xBABA. * Writes the difference to word offset 63 of the EEPROM. - *****************************************************************************/ + */ s32 e1000_update_eeprom_checksum(struct e1000_hw *hw) { - u32 ctrl_ext; - u16 checksum = 0; - u16 i, eeprom_data; - - DEBUGFUNC("e1000_update_eeprom_checksum"); - - for (i = 0; i < EEPROM_CHECKSUM_REG; i++) { - if (e1000_read_eeprom(hw, i, 1, &eeprom_data) < 0) { - DEBUGOUT("EEPROM Read Error\n"); - return -E1000_ERR_EEPROM; - } - checksum += eeprom_data; - } - checksum = (u16)EEPROM_SUM - checksum; - if (e1000_write_eeprom(hw, EEPROM_CHECKSUM_REG, 1, &checksum) < 0) { - DEBUGOUT("EEPROM Write Error\n"); - return -E1000_ERR_EEPROM; - } else if (hw->eeprom.type == e1000_eeprom_flash) { - e1000_commit_shadow_ram(hw); - } else if (hw->eeprom.type == e1000_eeprom_ich8) { - e1000_commit_shadow_ram(hw); - /* Reload the EEPROM, or else modifications will not appear - * until after next adapter reset. */ - ctrl_ext = er32(CTRL_EXT); - ctrl_ext |= E1000_CTRL_EXT_EE_RST; - ew32(CTRL_EXT, ctrl_ext); - msleep(10); - } - return E1000_SUCCESS; + u16 checksum = 0; + u16 i, eeprom_data; + + DEBUGFUNC("e1000_update_eeprom_checksum"); + + for (i = 0; i < EEPROM_CHECKSUM_REG; i++) { + if (e1000_read_eeprom(hw, i, 1, &eeprom_data) < 0) { + DEBUGOUT("EEPROM Read Error\n"); + return -E1000_ERR_EEPROM; + } + checksum += eeprom_data; + } + checksum = (u16) EEPROM_SUM - checksum; + if (e1000_write_eeprom(hw, EEPROM_CHECKSUM_REG, 1, &checksum) < 0) { + DEBUGOUT("EEPROM Write Error\n"); + return -E1000_ERR_EEPROM; + } + return E1000_SUCCESS; } -/****************************************************************************** - * Parent function for writing words to the different EEPROM types. - * - * hw - Struct containing variables accessed by shared code - * offset - offset within the EEPROM to be written to - * words - number of words to write - * data - 16 bit word to be written to the EEPROM +/** + * e1000_write_eeprom - write words to the different EEPROM types. + * @hw: Struct containing variables accessed by shared code + * @offset: offset within the EEPROM to be written to + * @words: number of words to write + * @data: 16 bit word to be written to the EEPROM * * If e1000_update_eeprom_checksum is not called after this function, the * EEPROM will most likely contain an invalid checksum. - *****************************************************************************/ + */ s32 e1000_write_eeprom(struct e1000_hw *hw, u16 offset, u16 words, u16 *data) { - s32 ret; - spin_lock(&e1000_eeprom_lock); - ret = e1000_do_write_eeprom(hw, offset, words, data); - spin_unlock(&e1000_eeprom_lock); - return ret; + s32 ret; + spin_lock(&e1000_eeprom_lock); + ret = e1000_do_write_eeprom(hw, offset, words, data); + spin_unlock(&e1000_eeprom_lock); + return ret; } - -static s32 e1000_do_write_eeprom(struct e1000_hw *hw, u16 offset, u16 words, u16 *data) +static s32 e1000_do_write_eeprom(struct e1000_hw *hw, u16 offset, u16 words, + u16 *data) { - struct e1000_eeprom_info *eeprom = &hw->eeprom; - s32 status = 0; - - DEBUGFUNC("e1000_write_eeprom"); - - /* If eeprom is not yet detected, do so now */ - if (eeprom->word_size == 0) - e1000_init_eeprom_params(hw); - - /* A check for invalid values: offset too large, too many words, and not - * enough words. - */ - if ((offset >= eeprom->word_size) || (words > eeprom->word_size - offset) || - (words == 0)) { - DEBUGOUT("\"words\" parameter out of bounds\n"); - return -E1000_ERR_EEPROM; - } - - /* 82573 writes only through eewr */ - if (eeprom->use_eewr) - return e1000_write_eeprom_eewr(hw, offset, words, data); - - if (eeprom->type == e1000_eeprom_ich8) - return e1000_write_eeprom_ich8(hw, offset, words, data); - - /* Prepare the EEPROM for writing */ - if (e1000_acquire_eeprom(hw) != E1000_SUCCESS) - return -E1000_ERR_EEPROM; - - if (eeprom->type == e1000_eeprom_microwire) { - status = e1000_write_eeprom_microwire(hw, offset, words, data); - } else { - status = e1000_write_eeprom_spi(hw, offset, words, data); - msleep(10); - } - - /* Done with writing */ - e1000_release_eeprom(hw); - - return status; + struct e1000_eeprom_info *eeprom = &hw->eeprom; + s32 status = 0; + + DEBUGFUNC("e1000_write_eeprom"); + + /* If eeprom is not yet detected, do so now */ + if (eeprom->word_size == 0) + e1000_init_eeprom_params(hw); + + /* A check for invalid values: offset too large, too many words, and not + * enough words. + */ + if ((offset >= eeprom->word_size) + || (words > eeprom->word_size - offset) || (words == 0)) { + DEBUGOUT("\"words\" parameter out of bounds\n"); + return -E1000_ERR_EEPROM; + } + + /* Prepare the EEPROM for writing */ + if (e1000_acquire_eeprom(hw) != E1000_SUCCESS) + return -E1000_ERR_EEPROM; + + if (eeprom->type == e1000_eeprom_microwire) { + status = e1000_write_eeprom_microwire(hw, offset, words, data); + } else { + status = e1000_write_eeprom_spi(hw, offset, words, data); + msleep(10); + } + + /* Done with writing */ + e1000_release_eeprom(hw); + + return status; } -/****************************************************************************** - * Writes a 16 bit word to a given offset in an SPI EEPROM. - * - * hw - Struct containing variables accessed by shared code - * offset - offset within the EEPROM to be written to - * words - number of words to write - * data - pointer to array of 8 bit words to be written to the EEPROM - * - *****************************************************************************/ +/** + * e1000_write_eeprom_spi - Writes a 16 bit word to a given offset in an SPI EEPROM. + * @hw: Struct containing variables accessed by shared code + * @offset: offset within the EEPROM to be written to + * @words: number of words to write + * @data: pointer to array of 8 bit words to be written to the EEPROM + */ static s32 e1000_write_eeprom_spi(struct e1000_hw *hw, u16 offset, u16 words, u16 *data) { - struct e1000_eeprom_info *eeprom = &hw->eeprom; - u16 widx = 0; + struct e1000_eeprom_info *eeprom = &hw->eeprom; + u16 widx = 0; - DEBUGFUNC("e1000_write_eeprom_spi"); + DEBUGFUNC("e1000_write_eeprom_spi"); - while (widx < words) { - u8 write_opcode = EEPROM_WRITE_OPCODE_SPI; + while (widx < words) { + u8 write_opcode = EEPROM_WRITE_OPCODE_SPI; - if (e1000_spi_eeprom_ready(hw)) return -E1000_ERR_EEPROM; + if (e1000_spi_eeprom_ready(hw)) + return -E1000_ERR_EEPROM; - e1000_standby_eeprom(hw); + e1000_standby_eeprom(hw); - /* Send the WRITE ENABLE command (8 bit opcode ) */ - e1000_shift_out_ee_bits(hw, EEPROM_WREN_OPCODE_SPI, - eeprom->opcode_bits); + /* Send the WRITE ENABLE command (8 bit opcode ) */ + e1000_shift_out_ee_bits(hw, EEPROM_WREN_OPCODE_SPI, + eeprom->opcode_bits); - e1000_standby_eeprom(hw); + e1000_standby_eeprom(hw); - /* Some SPI eeproms use the 8th address bit embedded in the opcode */ - if ((eeprom->address_bits == 8) && (offset >= 128)) - write_opcode |= EEPROM_A8_OPCODE_SPI; + /* Some SPI eeproms use the 8th address bit embedded in the opcode */ + if ((eeprom->address_bits == 8) && (offset >= 128)) + write_opcode |= EEPROM_A8_OPCODE_SPI; - /* Send the Write command (8-bit opcode + addr) */ - e1000_shift_out_ee_bits(hw, write_opcode, eeprom->opcode_bits); + /* Send the Write command (8-bit opcode + addr) */ + e1000_shift_out_ee_bits(hw, write_opcode, eeprom->opcode_bits); - e1000_shift_out_ee_bits(hw, (u16)((offset + widx)*2), - eeprom->address_bits); + e1000_shift_out_ee_bits(hw, (u16) ((offset + widx) * 2), + eeprom->address_bits); - /* Send the data */ + /* Send the data */ - /* Loop to allow for up to whole page write (32 bytes) of eeprom */ - while (widx < words) { - u16 word_out = data[widx]; - word_out = (word_out >> 8) | (word_out << 8); - e1000_shift_out_ee_bits(hw, word_out, 16); - widx++; + /* Loop to allow for up to whole page write (32 bytes) of eeprom */ + while (widx < words) { + u16 word_out = data[widx]; + word_out = (word_out >> 8) | (word_out << 8); + e1000_shift_out_ee_bits(hw, word_out, 16); + widx++; - /* Some larger eeprom sizes are capable of a 32-byte PAGE WRITE - * operation, while the smaller eeproms are capable of an 8-byte - * PAGE WRITE operation. Break the inner loop to pass new address - */ - if ((((offset + widx)*2) % eeprom->page_size) == 0) { - e1000_standby_eeprom(hw); - break; - } - } - } + /* Some larger eeprom sizes are capable of a 32-byte PAGE WRITE + * operation, while the smaller eeproms are capable of an 8-byte + * PAGE WRITE operation. Break the inner loop to pass new address + */ + if ((((offset + widx) * 2) % eeprom->page_size) == 0) { + e1000_standby_eeprom(hw); + break; + } + } + } - return E1000_SUCCESS; + return E1000_SUCCESS; } -/****************************************************************************** - * Writes a 16 bit word to a given offset in a Microwire EEPROM. - * - * hw - Struct containing variables accessed by shared code - * offset - offset within the EEPROM to be written to - * words - number of words to write - * data - pointer to array of 16 bit words to be written to the EEPROM - * - *****************************************************************************/ +/** + * e1000_write_eeprom_microwire - Writes a 16 bit word to a given offset in a Microwire EEPROM. + * @hw: Struct containing variables accessed by shared code + * @offset: offset within the EEPROM to be written to + * @words: number of words to write + * @data: pointer to array of 8 bit words to be written to the EEPROM + */ static s32 e1000_write_eeprom_microwire(struct e1000_hw *hw, u16 offset, u16 words, u16 *data) { - struct e1000_eeprom_info *eeprom = &hw->eeprom; - u32 eecd; - u16 words_written = 0; - u16 i = 0; - - DEBUGFUNC("e1000_write_eeprom_microwire"); - - /* Send the write enable command to the EEPROM (3-bit opcode plus - * 6/8-bit dummy address beginning with 11). It's less work to include - * the 11 of the dummy address as part of the opcode than it is to shift - * it over the correct number of bits for the address. This puts the - * EEPROM into write/erase mode. - */ - e1000_shift_out_ee_bits(hw, EEPROM_EWEN_OPCODE_MICROWIRE, - (u16)(eeprom->opcode_bits + 2)); - - e1000_shift_out_ee_bits(hw, 0, (u16)(eeprom->address_bits - 2)); - - /* Prepare the EEPROM */ - e1000_standby_eeprom(hw); - - while (words_written < words) { - /* Send the Write command (3-bit opcode + addr) */ - e1000_shift_out_ee_bits(hw, EEPROM_WRITE_OPCODE_MICROWIRE, - eeprom->opcode_bits); - - e1000_shift_out_ee_bits(hw, (u16)(offset + words_written), - eeprom->address_bits); - - /* Send the data */ - e1000_shift_out_ee_bits(hw, data[words_written], 16); - - /* Toggle the CS line. This in effect tells the EEPROM to execute - * the previous command. - */ - e1000_standby_eeprom(hw); - - /* Read DO repeatedly until it is high (equal to '1'). The EEPROM will - * signal that the command has been completed by raising the DO signal. - * If DO does not go high in 10 milliseconds, then error out. - */ - for (i = 0; i < 200; i++) { - eecd = er32(EECD); - if (eecd & E1000_EECD_DO) break; - udelay(50); - } - if (i == 200) { - DEBUGOUT("EEPROM Write did not complete\n"); - return -E1000_ERR_EEPROM; - } - - /* Recover from write */ - e1000_standby_eeprom(hw); - - words_written++; - } - - /* Send the write disable command to the EEPROM (3-bit opcode plus - * 6/8-bit dummy address beginning with 10). It's less work to include - * the 10 of the dummy address as part of the opcode than it is to shift - * it over the correct number of bits for the address. This takes the - * EEPROM out of write/erase mode. - */ - e1000_shift_out_ee_bits(hw, EEPROM_EWDS_OPCODE_MICROWIRE, - (u16)(eeprom->opcode_bits + 2)); - - e1000_shift_out_ee_bits(hw, 0, (u16)(eeprom->address_bits - 2)); - - return E1000_SUCCESS; -} + struct e1000_eeprom_info *eeprom = &hw->eeprom; + u32 eecd; + u16 words_written = 0; + u16 i = 0; -/****************************************************************************** - * Flushes the cached eeprom to NVM. This is done by saving the modified values - * in the eeprom cache and the non modified values in the currently active bank - * to the new bank. - * - * hw - Struct containing variables accessed by shared code - * offset - offset of word in the EEPROM to read - * data - word read from the EEPROM - * words - number of words to read - *****************************************************************************/ -static s32 e1000_commit_shadow_ram(struct e1000_hw *hw) -{ - u32 attempts = 100000; - u32 eecd = 0; - u32 flop = 0; - u32 i = 0; - s32 error = E1000_SUCCESS; - u32 old_bank_offset = 0; - u32 new_bank_offset = 0; - u8 low_byte = 0; - u8 high_byte = 0; - bool sector_write_failed = false; - - if (hw->mac_type == e1000_82573) { - /* The flop register will be used to determine if flash type is STM */ - flop = er32(FLOP); - for (i=0; i < attempts; i++) { - eecd = er32(EECD); - if ((eecd & E1000_EECD_FLUPD) == 0) { - break; - } - udelay(5); - } - - if (i == attempts) { - return -E1000_ERR_EEPROM; - } - - /* If STM opcode located in bits 15:8 of flop, reset firmware */ - if ((flop & 0xFF00) == E1000_STM_OPCODE) { - ew32(HICR, E1000_HICR_FW_RESET); - } - - /* Perform the flash update */ - ew32(EECD, eecd | E1000_EECD_FLUPD); - - for (i=0; i < attempts; i++) { - eecd = er32(EECD); - if ((eecd & E1000_EECD_FLUPD) == 0) { - break; - } - udelay(5); - } - - if (i == attempts) { - return -E1000_ERR_EEPROM; - } - } - - if (hw->mac_type == e1000_ich8lan && hw->eeprom_shadow_ram != NULL) { - /* We're writing to the opposite bank so if we're on bank 1, - * write to bank 0 etc. We also need to erase the segment that - * is going to be written */ - if (!(er32(EECD) & E1000_EECD_SEC1VAL)) { - new_bank_offset = hw->flash_bank_size * 2; - old_bank_offset = 0; - e1000_erase_ich8_4k_segment(hw, 1); - } else { - old_bank_offset = hw->flash_bank_size * 2; - new_bank_offset = 0; - e1000_erase_ich8_4k_segment(hw, 0); - } - - sector_write_failed = false; - /* Loop for every byte in the shadow RAM, - * which is in units of words. */ - for (i = 0; i < E1000_SHADOW_RAM_WORDS; i++) { - /* Determine whether to write the value stored - * in the other NVM bank or a modified value stored - * in the shadow RAM */ - if (hw->eeprom_shadow_ram[i].modified) { - low_byte = (u8)hw->eeprom_shadow_ram[i].eeprom_word; - udelay(100); - error = e1000_verify_write_ich8_byte(hw, - (i << 1) + new_bank_offset, low_byte); - - if (error != E1000_SUCCESS) - sector_write_failed = true; - else { - high_byte = - (u8)(hw->eeprom_shadow_ram[i].eeprom_word >> 8); - udelay(100); - } - } else { - e1000_read_ich8_byte(hw, (i << 1) + old_bank_offset, - &low_byte); - udelay(100); - error = e1000_verify_write_ich8_byte(hw, - (i << 1) + new_bank_offset, low_byte); - - if (error != E1000_SUCCESS) - sector_write_failed = true; - else { - e1000_read_ich8_byte(hw, (i << 1) + old_bank_offset + 1, - &high_byte); - udelay(100); - } - } - - /* If the write of the low byte was successful, go ahead and - * write the high byte while checking to make sure that if it - * is the signature byte, then it is handled properly */ - if (!sector_write_failed) { - /* If the word is 0x13, then make sure the signature bits - * (15:14) are 11b until the commit has completed. - * This will allow us to write 10b which indicates the - * signature is valid. We want to do this after the write - * has completed so that we don't mark the segment valid - * while the write is still in progress */ - if (i == E1000_ICH_NVM_SIG_WORD) - high_byte = E1000_ICH_NVM_SIG_MASK | high_byte; - - error = e1000_verify_write_ich8_byte(hw, - (i << 1) + new_bank_offset + 1, high_byte); - if (error != E1000_SUCCESS) - sector_write_failed = true; - - } else { - /* If the write failed then break from the loop and - * return an error */ - break; - } - } - - /* Don't bother writing the segment valid bits if sector - * programming failed. */ - if (!sector_write_failed) { - /* Finally validate the new segment by setting bit 15:14 - * to 10b in word 0x13 , this can be done without an - * erase as well since these bits are 11 to start with - * and we need to change bit 14 to 0b */ - e1000_read_ich8_byte(hw, - E1000_ICH_NVM_SIG_WORD * 2 + 1 + new_bank_offset, - &high_byte); - high_byte &= 0xBF; - error = e1000_verify_write_ich8_byte(hw, - E1000_ICH_NVM_SIG_WORD * 2 + 1 + new_bank_offset, high_byte); - /* And invalidate the previously valid segment by setting - * its signature word (0x13) high_byte to 0b. This can be - * done without an erase because flash erase sets all bits - * to 1's. We can write 1's to 0's without an erase */ - if (error == E1000_SUCCESS) { - error = e1000_verify_write_ich8_byte(hw, - E1000_ICH_NVM_SIG_WORD * 2 + 1 + old_bank_offset, 0); - } - - /* Clear the now not used entry in the cache */ - for (i = 0; i < E1000_SHADOW_RAM_WORDS; i++) { - hw->eeprom_shadow_ram[i].modified = false; - hw->eeprom_shadow_ram[i].eeprom_word = 0xFFFF; - } - } - } - - return error; + DEBUGFUNC("e1000_write_eeprom_microwire"); + + /* Send the write enable command to the EEPROM (3-bit opcode plus + * 6/8-bit dummy address beginning with 11). It's less work to include + * the 11 of the dummy address as part of the opcode than it is to shift + * it over the correct number of bits for the address. This puts the + * EEPROM into write/erase mode. + */ + e1000_shift_out_ee_bits(hw, EEPROM_EWEN_OPCODE_MICROWIRE, + (u16) (eeprom->opcode_bits + 2)); + + e1000_shift_out_ee_bits(hw, 0, (u16) (eeprom->address_bits - 2)); + + /* Prepare the EEPROM */ + e1000_standby_eeprom(hw); + + while (words_written < words) { + /* Send the Write command (3-bit opcode + addr) */ + e1000_shift_out_ee_bits(hw, EEPROM_WRITE_OPCODE_MICROWIRE, + eeprom->opcode_bits); + + e1000_shift_out_ee_bits(hw, (u16) (offset + words_written), + eeprom->address_bits); + + /* Send the data */ + e1000_shift_out_ee_bits(hw, data[words_written], 16); + + /* Toggle the CS line. This in effect tells the EEPROM to execute + * the previous command. + */ + e1000_standby_eeprom(hw); + + /* Read DO repeatedly until it is high (equal to '1'). The EEPROM will + * signal that the command has been completed by raising the DO signal. + * If DO does not go high in 10 milliseconds, then error out. + */ + for (i = 0; i < 200; i++) { + eecd = er32(EECD); + if (eecd & E1000_EECD_DO) + break; + udelay(50); + } + if (i == 200) { + DEBUGOUT("EEPROM Write did not complete\n"); + return -E1000_ERR_EEPROM; + } + + /* Recover from write */ + e1000_standby_eeprom(hw); + + words_written++; + } + + /* Send the write disable command to the EEPROM (3-bit opcode plus + * 6/8-bit dummy address beginning with 10). It's less work to include + * the 10 of the dummy address as part of the opcode than it is to shift + * it over the correct number of bits for the address. This takes the + * EEPROM out of write/erase mode. + */ + e1000_shift_out_ee_bits(hw, EEPROM_EWDS_OPCODE_MICROWIRE, + (u16) (eeprom->opcode_bits + 2)); + + e1000_shift_out_ee_bits(hw, 0, (u16) (eeprom->address_bits - 2)); + + return E1000_SUCCESS; } -/****************************************************************************** +/** + * e1000_read_mac_addr - read the adapters MAC from eeprom + * @hw: Struct containing variables accessed by shared code + * * Reads the adapter's MAC address from the EEPROM and inverts the LSB for the * second function of dual function devices - * - * hw - Struct containing variables accessed by shared code - *****************************************************************************/ + */ s32 e1000_read_mac_addr(struct e1000_hw *hw) { - u16 offset; - u16 eeprom_data, i; - - DEBUGFUNC("e1000_read_mac_addr"); - - for (i = 0; i < NODE_ADDRESS_SIZE; i += 2) { - offset = i >> 1; - if (e1000_read_eeprom(hw, offset, 1, &eeprom_data) < 0) { - DEBUGOUT("EEPROM Read Error\n"); - return -E1000_ERR_EEPROM; - } - hw->perm_mac_addr[i] = (u8)(eeprom_data & 0x00FF); - hw->perm_mac_addr[i+1] = (u8)(eeprom_data >> 8); - } - - switch (hw->mac_type) { - default: - break; - case e1000_82546: - case e1000_82546_rev_3: - case e1000_82571: - case e1000_80003es2lan: - if (er32(STATUS) & E1000_STATUS_FUNC_1) - hw->perm_mac_addr[5] ^= 0x01; - break; - } - - for (i = 0; i < NODE_ADDRESS_SIZE; i++) - hw->mac_addr[i] = hw->perm_mac_addr[i]; - return E1000_SUCCESS; + u16 offset; + u16 eeprom_data, i; + + DEBUGFUNC("e1000_read_mac_addr"); + + for (i = 0; i < NODE_ADDRESS_SIZE; i += 2) { + offset = i >> 1; + if (e1000_read_eeprom(hw, offset, 1, &eeprom_data) < 0) { + DEBUGOUT("EEPROM Read Error\n"); + return -E1000_ERR_EEPROM; + } + hw->perm_mac_addr[i] = (u8) (eeprom_data & 0x00FF); + hw->perm_mac_addr[i + 1] = (u8) (eeprom_data >> 8); + } + + switch (hw->mac_type) { + default: + break; + case e1000_82546: + case e1000_82546_rev_3: + if (er32(STATUS) & E1000_STATUS_FUNC_1) + hw->perm_mac_addr[5] ^= 0x01; + break; + } + + for (i = 0; i < NODE_ADDRESS_SIZE; i++) + hw->mac_addr[i] = hw->perm_mac_addr[i]; + return E1000_SUCCESS; } -/****************************************************************************** - * Initializes receive address filters. - * - * hw - Struct containing variables accessed by shared code +/** + * e1000_init_rx_addrs - Initializes receive address filters. + * @hw: Struct containing variables accessed by shared code * * Places the MAC address in receive address register 0 and clears the rest - * of the receive addresss registers. Clears the multicast table. Assumes + * of the receive address registers. Clears the multicast table. Assumes * the receiver is in reset when the routine is called. - *****************************************************************************/ + */ static void e1000_init_rx_addrs(struct e1000_hw *hw) { - u32 i; - u32 rar_num; - - DEBUGFUNC("e1000_init_rx_addrs"); - - /* Setup the receive address. */ - DEBUGOUT("Programming MAC Address into RAR[0]\n"); - - e1000_rar_set(hw, hw->mac_addr, 0); - - rar_num = E1000_RAR_ENTRIES; - - /* Reserve a spot for the Locally Administered Address to work around - * an 82571 issue in which a reset on one port will reload the MAC on - * the other port. */ - if ((hw->mac_type == e1000_82571) && (hw->laa_is_present)) - rar_num -= 1; - if (hw->mac_type == e1000_ich8lan) - rar_num = E1000_RAR_ENTRIES_ICH8LAN; - - /* Zero out the other 15 receive addresses. */ - DEBUGOUT("Clearing RAR[1-15]\n"); - for (i = 1; i < rar_num; i++) { - E1000_WRITE_REG_ARRAY(hw, RA, (i << 1), 0); - E1000_WRITE_FLUSH(); - E1000_WRITE_REG_ARRAY(hw, RA, ((i << 1) + 1), 0); - E1000_WRITE_FLUSH(); - } + u32 i; + u32 rar_num; + + DEBUGFUNC("e1000_init_rx_addrs"); + + /* Setup the receive address. */ + DEBUGOUT("Programming MAC Address into RAR[0]\n"); + + e1000_rar_set(hw, hw->mac_addr, 0); + + rar_num = E1000_RAR_ENTRIES; + + /* Zero out the other 15 receive addresses. */ + DEBUGOUT("Clearing RAR[1-15]\n"); + for (i = 1; i < rar_num; i++) { + E1000_WRITE_REG_ARRAY(hw, RA, (i << 1), 0); + E1000_WRITE_FLUSH(); + E1000_WRITE_REG_ARRAY(hw, RA, ((i << 1) + 1), 0); + E1000_WRITE_FLUSH(); + } } -/****************************************************************************** - * Hashes an address to determine its location in the multicast table - * - * hw - Struct containing variables accessed by shared code - * mc_addr - the multicast address to hash - *****************************************************************************/ +/** + * e1000_hash_mc_addr - Hashes an address to determine its location in the multicast table + * @hw: Struct containing variables accessed by shared code + * @mc_addr: the multicast address to hash + */ u32 e1000_hash_mc_addr(struct e1000_hw *hw, u8 *mc_addr) { - u32 hash_value = 0; - - /* The portion of the address that is used for the hash table is - * determined by the mc_filter_type setting. - */ - switch (hw->mc_filter_type) { - /* [0] [1] [2] [3] [4] [5] - * 01 AA 00 12 34 56 - * LSB MSB - */ - case 0: - if (hw->mac_type == e1000_ich8lan) { - /* [47:38] i.e. 0x158 for above example address */ - hash_value = ((mc_addr[4] >> 6) | (((u16)mc_addr[5]) << 2)); - } else { - /* [47:36] i.e. 0x563 for above example address */ - hash_value = ((mc_addr[4] >> 4) | (((u16)mc_addr[5]) << 4)); - } - break; - case 1: - if (hw->mac_type == e1000_ich8lan) { - /* [46:37] i.e. 0x2B1 for above example address */ - hash_value = ((mc_addr[4] >> 5) | (((u16)mc_addr[5]) << 3)); - } else { - /* [46:35] i.e. 0xAC6 for above example address */ - hash_value = ((mc_addr[4] >> 3) | (((u16)mc_addr[5]) << 5)); - } - break; - case 2: - if (hw->mac_type == e1000_ich8lan) { - /*[45:36] i.e. 0x163 for above example address */ - hash_value = ((mc_addr[4] >> 4) | (((u16)mc_addr[5]) << 4)); - } else { - /* [45:34] i.e. 0x5D8 for above example address */ - hash_value = ((mc_addr[4] >> 2) | (((u16)mc_addr[5]) << 6)); - } - break; - case 3: - if (hw->mac_type == e1000_ich8lan) { - /* [43:34] i.e. 0x18D for above example address */ - hash_value = ((mc_addr[4] >> 2) | (((u16)mc_addr[5]) << 6)); - } else { - /* [43:32] i.e. 0x634 for above example address */ - hash_value = ((mc_addr[4]) | (((u16)mc_addr[5]) << 8)); - } - break; - } - - hash_value &= 0xFFF; - if (hw->mac_type == e1000_ich8lan) - hash_value &= 0x3FF; - - return hash_value; + u32 hash_value = 0; + + /* The portion of the address that is used for the hash table is + * determined by the mc_filter_type setting. + */ + switch (hw->mc_filter_type) { + /* [0] [1] [2] [3] [4] [5] + * 01 AA 00 12 34 56 + * LSB MSB + */ + case 0: + /* [47:36] i.e. 0x563 for above example address */ + hash_value = ((mc_addr[4] >> 4) | (((u16) mc_addr[5]) << 4)); + break; + case 1: + /* [46:35] i.e. 0xAC6 for above example address */ + hash_value = ((mc_addr[4] >> 3) | (((u16) mc_addr[5]) << 5)); + break; + case 2: + /* [45:34] i.e. 0x5D8 for above example address */ + hash_value = ((mc_addr[4] >> 2) | (((u16) mc_addr[5]) << 6)); + break; + case 3: + /* [43:32] i.e. 0x634 for above example address */ + hash_value = ((mc_addr[4]) | (((u16) mc_addr[5]) << 8)); + break; + } + + hash_value &= 0xFFF; + return hash_value; } -/****************************************************************************** - * Puts an ethernet address into a receive address register. - * - * hw - Struct containing variables accessed by shared code - * addr - Address to put into receive address register - * index - Receive address register to write - *****************************************************************************/ +/** + * e1000_rar_set - Puts an ethernet address into a receive address register. + * @hw: Struct containing variables accessed by shared code + * @addr: Address to put into receive address register + * @index: Receive address register to write + */ void e1000_rar_set(struct e1000_hw *hw, u8 *addr, u32 index) { - u32 rar_low, rar_high; - - /* HW expects these in little endian so we reverse the byte order - * from network order (big endian) to little endian - */ - rar_low = ((u32)addr[0] | ((u32)addr[1] << 8) | - ((u32)addr[2] << 16) | ((u32)addr[3] << 24)); - rar_high = ((u32)addr[4] | ((u32)addr[5] << 8)); - - /* Disable Rx and flush all Rx frames before enabling RSS to avoid Rx - * unit hang. - * - * Description: - * If there are any Rx frames queued up or otherwise present in the HW - * before RSS is enabled, and then we enable RSS, the HW Rx unit will - * hang. To work around this issue, we have to disable receives and - * flush out all Rx frames before we enable RSS. To do so, we modify we - * redirect all Rx traffic to manageability and then reset the HW. - * This flushes away Rx frames, and (since the redirections to - * manageability persists across resets) keeps new ones from coming in - * while we work. Then, we clear the Address Valid AV bit for all MAC - * addresses and undo the re-direction to manageability. - * Now, frames are coming in again, but the MAC won't accept them, so - * far so good. We now proceed to initialize RSS (if necessary) and - * configure the Rx unit. Last, we re-enable the AV bits and continue - * on our merry way. - */ - switch (hw->mac_type) { - case e1000_82571: - case e1000_82572: - case e1000_80003es2lan: - if (hw->leave_av_bit_off) - break; - default: - /* Indicate to hardware the Address is Valid. */ - rar_high |= E1000_RAH_AV; - break; - } - - E1000_WRITE_REG_ARRAY(hw, RA, (index << 1), rar_low); - E1000_WRITE_FLUSH(); - E1000_WRITE_REG_ARRAY(hw, RA, ((index << 1) + 1), rar_high); - E1000_WRITE_FLUSH(); + u32 rar_low, rar_high; + + /* HW expects these in little endian so we reverse the byte order + * from network order (big endian) to little endian + */ + rar_low = ((u32) addr[0] | ((u32) addr[1] << 8) | + ((u32) addr[2] << 16) | ((u32) addr[3] << 24)); + rar_high = ((u32) addr[4] | ((u32) addr[5] << 8)); + + /* Disable Rx and flush all Rx frames before enabling RSS to avoid Rx + * unit hang. + * + * Description: + * If there are any Rx frames queued up or otherwise present in the HW + * before RSS is enabled, and then we enable RSS, the HW Rx unit will + * hang. To work around this issue, we have to disable receives and + * flush out all Rx frames before we enable RSS. To do so, we modify we + * redirect all Rx traffic to manageability and then reset the HW. + * This flushes away Rx frames, and (since the redirections to + * manageability persists across resets) keeps new ones from coming in + * while we work. Then, we clear the Address Valid AV bit for all MAC + * addresses and undo the re-direction to manageability. + * Now, frames are coming in again, but the MAC won't accept them, so + * far so good. We now proceed to initialize RSS (if necessary) and + * configure the Rx unit. Last, we re-enable the AV bits and continue + * on our merry way. + */ + switch (hw->mac_type) { + default: + /* Indicate to hardware the Address is Valid. */ + rar_high |= E1000_RAH_AV; + break; + } + + E1000_WRITE_REG_ARRAY(hw, RA, (index << 1), rar_low); + E1000_WRITE_FLUSH(); + E1000_WRITE_REG_ARRAY(hw, RA, ((index << 1) + 1), rar_high); + E1000_WRITE_FLUSH(); } -/****************************************************************************** - * Writes a value to the specified offset in the VLAN filter table. - * - * hw - Struct containing variables accessed by shared code - * offset - Offset in VLAN filer table to write - * value - Value to write into VLAN filter table - *****************************************************************************/ +/** + * e1000_write_vfta - Writes a value to the specified offset in the VLAN filter table. + * @hw: Struct containing variables accessed by shared code + * @offset: Offset in VLAN filer table to write + * @value: Value to write into VLAN filter table + */ void e1000_write_vfta(struct e1000_hw *hw, u32 offset, u32 value) { - u32 temp; - - if (hw->mac_type == e1000_ich8lan) - return; - - if ((hw->mac_type == e1000_82544) && ((offset & 0x1) == 1)) { - temp = E1000_READ_REG_ARRAY(hw, VFTA, (offset - 1)); - E1000_WRITE_REG_ARRAY(hw, VFTA, offset, value); - E1000_WRITE_FLUSH(); - E1000_WRITE_REG_ARRAY(hw, VFTA, (offset - 1), temp); - E1000_WRITE_FLUSH(); - } else { - E1000_WRITE_REG_ARRAY(hw, VFTA, offset, value); - E1000_WRITE_FLUSH(); - } + u32 temp; + + if ((hw->mac_type == e1000_82544) && ((offset & 0x1) == 1)) { + temp = E1000_READ_REG_ARRAY(hw, VFTA, (offset - 1)); + E1000_WRITE_REG_ARRAY(hw, VFTA, offset, value); + E1000_WRITE_FLUSH(); + E1000_WRITE_REG_ARRAY(hw, VFTA, (offset - 1), temp); + E1000_WRITE_FLUSH(); + } else { + E1000_WRITE_REG_ARRAY(hw, VFTA, offset, value); + E1000_WRITE_FLUSH(); + } } -/****************************************************************************** - * Clears the VLAN filer table - * - * hw - Struct containing variables accessed by shared code - *****************************************************************************/ +/** + * e1000_clear_vfta - Clears the VLAN filer table + * @hw: Struct containing variables accessed by shared code + */ static void e1000_clear_vfta(struct e1000_hw *hw) { - u32 offset; - u32 vfta_value = 0; - u32 vfta_offset = 0; - u32 vfta_bit_in_reg = 0; - - if (hw->mac_type == e1000_ich8lan) - return; - - if (hw->mac_type == e1000_82573) { - if (hw->mng_cookie.vlan_id != 0) { - /* The VFTA is a 4096b bit-field, each identifying a single VLAN - * ID. The following operations determine which 32b entry - * (i.e. offset) into the array we want to set the VLAN ID - * (i.e. bit) of the manageability unit. */ - vfta_offset = (hw->mng_cookie.vlan_id >> - E1000_VFTA_ENTRY_SHIFT) & - E1000_VFTA_ENTRY_MASK; - vfta_bit_in_reg = 1 << (hw->mng_cookie.vlan_id & - E1000_VFTA_ENTRY_BIT_SHIFT_MASK); - } - } - for (offset = 0; offset < E1000_VLAN_FILTER_TBL_SIZE; offset++) { - /* If the offset we want to clear is the same offset of the - * manageability VLAN ID, then clear all bits except that of the - * manageability unit */ - vfta_value = (offset == vfta_offset) ? vfta_bit_in_reg : 0; - E1000_WRITE_REG_ARRAY(hw, VFTA, offset, vfta_value); - E1000_WRITE_FLUSH(); - } + u32 offset; + u32 vfta_value = 0; + u32 vfta_offset = 0; + u32 vfta_bit_in_reg = 0; + + for (offset = 0; offset < E1000_VLAN_FILTER_TBL_SIZE; offset++) { + /* If the offset we want to clear is the same offset of the + * manageability VLAN ID, then clear all bits except that of the + * manageability unit */ + vfta_value = (offset == vfta_offset) ? vfta_bit_in_reg : 0; + E1000_WRITE_REG_ARRAY(hw, VFTA, offset, vfta_value); + E1000_WRITE_FLUSH(); + } } static s32 e1000_id_led_init(struct e1000_hw *hw) { - u32 ledctl; - const u32 ledctl_mask = 0x000000FF; - const u32 ledctl_on = E1000_LEDCTL_MODE_LED_ON; - const u32 ledctl_off = E1000_LEDCTL_MODE_LED_OFF; - u16 eeprom_data, i, temp; - const u16 led_mask = 0x0F; - - DEBUGFUNC("e1000_id_led_init"); - - if (hw->mac_type < e1000_82540) { - /* Nothing to do */ - return E1000_SUCCESS; - } - - ledctl = er32(LEDCTL); - hw->ledctl_default = ledctl; - hw->ledctl_mode1 = hw->ledctl_default; - hw->ledctl_mode2 = hw->ledctl_default; - - if (e1000_read_eeprom(hw, EEPROM_ID_LED_SETTINGS, 1, &eeprom_data) < 0) { - DEBUGOUT("EEPROM Read Error\n"); - return -E1000_ERR_EEPROM; - } - - if ((hw->mac_type == e1000_82573) && - (eeprom_data == ID_LED_RESERVED_82573)) - eeprom_data = ID_LED_DEFAULT_82573; - else if ((eeprom_data == ID_LED_RESERVED_0000) || - (eeprom_data == ID_LED_RESERVED_FFFF)) { - if (hw->mac_type == e1000_ich8lan) - eeprom_data = ID_LED_DEFAULT_ICH8LAN; - else - eeprom_data = ID_LED_DEFAULT; - } - - for (i = 0; i < 4; i++) { - temp = (eeprom_data >> (i << 2)) & led_mask; - switch (temp) { - case ID_LED_ON1_DEF2: - case ID_LED_ON1_ON2: - case ID_LED_ON1_OFF2: - hw->ledctl_mode1 &= ~(ledctl_mask << (i << 3)); - hw->ledctl_mode1 |= ledctl_on << (i << 3); - break; - case ID_LED_OFF1_DEF2: - case ID_LED_OFF1_ON2: - case ID_LED_OFF1_OFF2: - hw->ledctl_mode1 &= ~(ledctl_mask << (i << 3)); - hw->ledctl_mode1 |= ledctl_off << (i << 3); - break; - default: - /* Do nothing */ - break; - } - switch (temp) { - case ID_LED_DEF1_ON2: - case ID_LED_ON1_ON2: - case ID_LED_OFF1_ON2: - hw->ledctl_mode2 &= ~(ledctl_mask << (i << 3)); - hw->ledctl_mode2 |= ledctl_on << (i << 3); - break; - case ID_LED_DEF1_OFF2: - case ID_LED_ON1_OFF2: - case ID_LED_OFF1_OFF2: - hw->ledctl_mode2 &= ~(ledctl_mask << (i << 3)); - hw->ledctl_mode2 |= ledctl_off << (i << 3); - break; - default: - /* Do nothing */ - break; - } - } - return E1000_SUCCESS; + u32 ledctl; + const u32 ledctl_mask = 0x000000FF; + const u32 ledctl_on = E1000_LEDCTL_MODE_LED_ON; + const u32 ledctl_off = E1000_LEDCTL_MODE_LED_OFF; + u16 eeprom_data, i, temp; + const u16 led_mask = 0x0F; + + DEBUGFUNC("e1000_id_led_init"); + + if (hw->mac_type < e1000_82540) { + /* Nothing to do */ + return E1000_SUCCESS; + } + + ledctl = er32(LEDCTL); + hw->ledctl_default = ledctl; + hw->ledctl_mode1 = hw->ledctl_default; + hw->ledctl_mode2 = hw->ledctl_default; + + if (e1000_read_eeprom(hw, EEPROM_ID_LED_SETTINGS, 1, &eeprom_data) < 0) { + DEBUGOUT("EEPROM Read Error\n"); + return -E1000_ERR_EEPROM; + } + + if ((eeprom_data == ID_LED_RESERVED_0000) || + (eeprom_data == ID_LED_RESERVED_FFFF)) { + eeprom_data = ID_LED_DEFAULT; + } + + for (i = 0; i < 4; i++) { + temp = (eeprom_data >> (i << 2)) & led_mask; + switch (temp) { + case ID_LED_ON1_DEF2: + case ID_LED_ON1_ON2: + case ID_LED_ON1_OFF2: + hw->ledctl_mode1 &= ~(ledctl_mask << (i << 3)); + hw->ledctl_mode1 |= ledctl_on << (i << 3); + break; + case ID_LED_OFF1_DEF2: + case ID_LED_OFF1_ON2: + case ID_LED_OFF1_OFF2: + hw->ledctl_mode1 &= ~(ledctl_mask << (i << 3)); + hw->ledctl_mode1 |= ledctl_off << (i << 3); + break; + default: + /* Do nothing */ + break; + } + switch (temp) { + case ID_LED_DEF1_ON2: + case ID_LED_ON1_ON2: + case ID_LED_OFF1_ON2: + hw->ledctl_mode2 &= ~(ledctl_mask << (i << 3)); + hw->ledctl_mode2 |= ledctl_on << (i << 3); + break; + case ID_LED_DEF1_OFF2: + case ID_LED_ON1_OFF2: + case ID_LED_OFF1_OFF2: + hw->ledctl_mode2 &= ~(ledctl_mask << (i << 3)); + hw->ledctl_mode2 |= ledctl_off << (i << 3); + break; + default: + /* Do nothing */ + break; + } + } + return E1000_SUCCESS; } -/****************************************************************************** - * Prepares SW controlable LED for use and saves the current state of the LED. +/** + * e1000_setup_led + * @hw: Struct containing variables accessed by shared code * - * hw - Struct containing variables accessed by shared code - *****************************************************************************/ + * Prepares SW controlable LED for use and saves the current state of the LED. + */ s32 e1000_setup_led(struct e1000_hw *hw) { - u32 ledctl; - s32 ret_val = E1000_SUCCESS; - - DEBUGFUNC("e1000_setup_led"); - - switch (hw->mac_type) { - case e1000_82542_rev2_0: - case e1000_82542_rev2_1: - case e1000_82543: - case e1000_82544: - /* No setup necessary */ - break; - case e1000_82541: - case e1000_82547: - case e1000_82541_rev_2: - case e1000_82547_rev_2: - /* Turn off PHY Smart Power Down (if enabled) */ - ret_val = e1000_read_phy_reg(hw, IGP01E1000_GMII_FIFO, - &hw->phy_spd_default); - if (ret_val) - return ret_val; - ret_val = e1000_write_phy_reg(hw, IGP01E1000_GMII_FIFO, - (u16)(hw->phy_spd_default & - ~IGP01E1000_GMII_SPD)); - if (ret_val) - return ret_val; - /* Fall Through */ - default: - if (hw->media_type == e1000_media_type_fiber) { - ledctl = er32(LEDCTL); - /* Save current LEDCTL settings */ - hw->ledctl_default = ledctl; - /* Turn off LED0 */ - ledctl &= ~(E1000_LEDCTL_LED0_IVRT | - E1000_LEDCTL_LED0_BLINK | - E1000_LEDCTL_LED0_MODE_MASK); - ledctl |= (E1000_LEDCTL_MODE_LED_OFF << - E1000_LEDCTL_LED0_MODE_SHIFT); - ew32(LEDCTL, ledctl); - } else if (hw->media_type == e1000_media_type_copper) - ew32(LEDCTL, hw->ledctl_mode1); - break; - } - - return E1000_SUCCESS; -} + u32 ledctl; + s32 ret_val = E1000_SUCCESS; + DEBUGFUNC("e1000_setup_led"); -/****************************************************************************** - * Used on 82571 and later Si that has LED blink bits. - * Callers must use their own timer and should have already called - * e1000_id_led_init() - * Call e1000_cleanup led() to stop blinking - * - * hw - Struct containing variables accessed by shared code - *****************************************************************************/ -s32 e1000_blink_led_start(struct e1000_hw *hw) -{ - s16 i; - u32 ledctl_blink = 0; - - DEBUGFUNC("e1000_id_led_blink_on"); - - if (hw->mac_type < e1000_82571) { - /* Nothing to do */ - return E1000_SUCCESS; - } - if (hw->media_type == e1000_media_type_fiber) { - /* always blink LED0 for PCI-E fiber */ - ledctl_blink = E1000_LEDCTL_LED0_BLINK | - (E1000_LEDCTL_MODE_LED_ON << E1000_LEDCTL_LED0_MODE_SHIFT); - } else { - /* set the blink bit for each LED that's "on" (0x0E) in ledctl_mode2 */ - ledctl_blink = hw->ledctl_mode2; - for (i=0; i < 4; i++) - if (((hw->ledctl_mode2 >> (i * 8)) & 0xFF) == - E1000_LEDCTL_MODE_LED_ON) - ledctl_blink |= (E1000_LEDCTL_LED0_BLINK << (i * 8)); - } - - ew32(LEDCTL, ledctl_blink); - - return E1000_SUCCESS; + switch (hw->mac_type) { + case e1000_82542_rev2_0: + case e1000_82542_rev2_1: + case e1000_82543: + case e1000_82544: + /* No setup necessary */ + break; + case e1000_82541: + case e1000_82547: + case e1000_82541_rev_2: + case e1000_82547_rev_2: + /* Turn off PHY Smart Power Down (if enabled) */ + ret_val = e1000_read_phy_reg(hw, IGP01E1000_GMII_FIFO, + &hw->phy_spd_default); + if (ret_val) + return ret_val; + ret_val = e1000_write_phy_reg(hw, IGP01E1000_GMII_FIFO, + (u16) (hw->phy_spd_default & + ~IGP01E1000_GMII_SPD)); + if (ret_val) + return ret_val; + /* Fall Through */ + default: + if (hw->media_type == e1000_media_type_fiber) { + ledctl = er32(LEDCTL); + /* Save current LEDCTL settings */ + hw->ledctl_default = ledctl; + /* Turn off LED0 */ + ledctl &= ~(E1000_LEDCTL_LED0_IVRT | + E1000_LEDCTL_LED0_BLINK | + E1000_LEDCTL_LED0_MODE_MASK); + ledctl |= (E1000_LEDCTL_MODE_LED_OFF << + E1000_LEDCTL_LED0_MODE_SHIFT); + ew32(LEDCTL, ledctl); + } else if (hw->media_type == e1000_media_type_copper) + ew32(LEDCTL, hw->ledctl_mode1); + break; + } + + return E1000_SUCCESS; } -/****************************************************************************** - * Restores the saved state of the SW controlable LED. - * - * hw - Struct containing variables accessed by shared code - *****************************************************************************/ +/** + * e1000_cleanup_led - Restores the saved state of the SW controlable LED. + * @hw: Struct containing variables accessed by shared code + */ s32 e1000_cleanup_led(struct e1000_hw *hw) { - s32 ret_val = E1000_SUCCESS; - - DEBUGFUNC("e1000_cleanup_led"); - - switch (hw->mac_type) { - case e1000_82542_rev2_0: - case e1000_82542_rev2_1: - case e1000_82543: - case e1000_82544: - /* No cleanup necessary */ - break; - case e1000_82541: - case e1000_82547: - case e1000_82541_rev_2: - case e1000_82547_rev_2: - /* Turn on PHY Smart Power Down (if previously enabled) */ - ret_val = e1000_write_phy_reg(hw, IGP01E1000_GMII_FIFO, - hw->phy_spd_default); - if (ret_val) - return ret_val; - /* Fall Through */ - default: - if (hw->phy_type == e1000_phy_ife) { - e1000_write_phy_reg(hw, IFE_PHY_SPECIAL_CONTROL_LED, 0); - break; - } - /* Restore LEDCTL settings */ - ew32(LEDCTL, hw->ledctl_default); - break; - } - - return E1000_SUCCESS; + s32 ret_val = E1000_SUCCESS; + + DEBUGFUNC("e1000_cleanup_led"); + + switch (hw->mac_type) { + case e1000_82542_rev2_0: + case e1000_82542_rev2_1: + case e1000_82543: + case e1000_82544: + /* No cleanup necessary */ + break; + case e1000_82541: + case e1000_82547: + case e1000_82541_rev_2: + case e1000_82547_rev_2: + /* Turn on PHY Smart Power Down (if previously enabled) */ + ret_val = e1000_write_phy_reg(hw, IGP01E1000_GMII_FIFO, + hw->phy_spd_default); + if (ret_val) + return ret_val; + /* Fall Through */ + default: + /* Restore LEDCTL settings */ + ew32(LEDCTL, hw->ledctl_default); + break; + } + + return E1000_SUCCESS; } -/****************************************************************************** - * Turns on the software controllable LED - * - * hw - Struct containing variables accessed by shared code - *****************************************************************************/ +/** + * e1000_led_on - Turns on the software controllable LED + * @hw: Struct containing variables accessed by shared code + */ s32 e1000_led_on(struct e1000_hw *hw) { - u32 ctrl = er32(CTRL); - - DEBUGFUNC("e1000_led_on"); - - switch (hw->mac_type) { - case e1000_82542_rev2_0: - case e1000_82542_rev2_1: - case e1000_82543: - /* Set SW Defineable Pin 0 to turn on the LED */ - ctrl |= E1000_CTRL_SWDPIN0; - ctrl |= E1000_CTRL_SWDPIO0; - break; - case e1000_82544: - if (hw->media_type == e1000_media_type_fiber) { - /* Set SW Defineable Pin 0 to turn on the LED */ - ctrl |= E1000_CTRL_SWDPIN0; - ctrl |= E1000_CTRL_SWDPIO0; - } else { - /* Clear SW Defineable Pin 0 to turn on the LED */ - ctrl &= ~E1000_CTRL_SWDPIN0; - ctrl |= E1000_CTRL_SWDPIO0; - } - break; - default: - if (hw->media_type == e1000_media_type_fiber) { - /* Clear SW Defineable Pin 0 to turn on the LED */ - ctrl &= ~E1000_CTRL_SWDPIN0; - ctrl |= E1000_CTRL_SWDPIO0; - } else if (hw->phy_type == e1000_phy_ife) { - e1000_write_phy_reg(hw, IFE_PHY_SPECIAL_CONTROL_LED, - (IFE_PSCL_PROBE_MODE | IFE_PSCL_PROBE_LEDS_ON)); - } else if (hw->media_type == e1000_media_type_copper) { - ew32(LEDCTL, hw->ledctl_mode2); - return E1000_SUCCESS; - } - break; - } - - ew32(CTRL, ctrl); - - return E1000_SUCCESS; + u32 ctrl = er32(CTRL); + + DEBUGFUNC("e1000_led_on"); + + switch (hw->mac_type) { + case e1000_82542_rev2_0: + case e1000_82542_rev2_1: + case e1000_82543: + /* Set SW Defineable Pin 0 to turn on the LED */ + ctrl |= E1000_CTRL_SWDPIN0; + ctrl |= E1000_CTRL_SWDPIO0; + break; + case e1000_82544: + if (hw->media_type == e1000_media_type_fiber) { + /* Set SW Defineable Pin 0 to turn on the LED */ + ctrl |= E1000_CTRL_SWDPIN0; + ctrl |= E1000_CTRL_SWDPIO0; + } else { + /* Clear SW Defineable Pin 0 to turn on the LED */ + ctrl &= ~E1000_CTRL_SWDPIN0; + ctrl |= E1000_CTRL_SWDPIO0; + } + break; + default: + if (hw->media_type == e1000_media_type_fiber) { + /* Clear SW Defineable Pin 0 to turn on the LED */ + ctrl &= ~E1000_CTRL_SWDPIN0; + ctrl |= E1000_CTRL_SWDPIO0; + } else if (hw->media_type == e1000_media_type_copper) { + ew32(LEDCTL, hw->ledctl_mode2); + return E1000_SUCCESS; + } + break; + } + + ew32(CTRL, ctrl); + + return E1000_SUCCESS; } -/****************************************************************************** - * Turns off the software controllable LED - * - * hw - Struct containing variables accessed by shared code - *****************************************************************************/ +/** + * e1000_led_off - Turns off the software controllable LED + * @hw: Struct containing variables accessed by shared code + */ s32 e1000_led_off(struct e1000_hw *hw) { - u32 ctrl = er32(CTRL); - - DEBUGFUNC("e1000_led_off"); - - switch (hw->mac_type) { - case e1000_82542_rev2_0: - case e1000_82542_rev2_1: - case e1000_82543: - /* Clear SW Defineable Pin 0 to turn off the LED */ - ctrl &= ~E1000_CTRL_SWDPIN0; - ctrl |= E1000_CTRL_SWDPIO0; - break; - case e1000_82544: - if (hw->media_type == e1000_media_type_fiber) { - /* Clear SW Defineable Pin 0 to turn off the LED */ - ctrl &= ~E1000_CTRL_SWDPIN0; - ctrl |= E1000_CTRL_SWDPIO0; - } else { - /* Set SW Defineable Pin 0 to turn off the LED */ - ctrl |= E1000_CTRL_SWDPIN0; - ctrl |= E1000_CTRL_SWDPIO0; - } - break; - default: - if (hw->media_type == e1000_media_type_fiber) { - /* Set SW Defineable Pin 0 to turn off the LED */ - ctrl |= E1000_CTRL_SWDPIN0; - ctrl |= E1000_CTRL_SWDPIO0; - } else if (hw->phy_type == e1000_phy_ife) { - e1000_write_phy_reg(hw, IFE_PHY_SPECIAL_CONTROL_LED, - (IFE_PSCL_PROBE_MODE | IFE_PSCL_PROBE_LEDS_OFF)); - } else if (hw->media_type == e1000_media_type_copper) { - ew32(LEDCTL, hw->ledctl_mode1); - return E1000_SUCCESS; - } - break; - } - - ew32(CTRL, ctrl); - - return E1000_SUCCESS; + u32 ctrl = er32(CTRL); + + DEBUGFUNC("e1000_led_off"); + + switch (hw->mac_type) { + case e1000_82542_rev2_0: + case e1000_82542_rev2_1: + case e1000_82543: + /* Clear SW Defineable Pin 0 to turn off the LED */ + ctrl &= ~E1000_CTRL_SWDPIN0; + ctrl |= E1000_CTRL_SWDPIO0; + break; + case e1000_82544: + if (hw->media_type == e1000_media_type_fiber) { + /* Clear SW Defineable Pin 0 to turn off the LED */ + ctrl &= ~E1000_CTRL_SWDPIN0; + ctrl |= E1000_CTRL_SWDPIO0; + } else { + /* Set SW Defineable Pin 0 to turn off the LED */ + ctrl |= E1000_CTRL_SWDPIN0; + ctrl |= E1000_CTRL_SWDPIO0; + } + break; + default: + if (hw->media_type == e1000_media_type_fiber) { + /* Set SW Defineable Pin 0 to turn off the LED */ + ctrl |= E1000_CTRL_SWDPIN0; + ctrl |= E1000_CTRL_SWDPIO0; + } else if (hw->media_type == e1000_media_type_copper) { + ew32(LEDCTL, hw->ledctl_mode1); + return E1000_SUCCESS; + } + break; + } + + ew32(CTRL, ctrl); + + return E1000_SUCCESS; } -/****************************************************************************** - * Clears all hardware statistics counters. - * - * hw - Struct containing variables accessed by shared code - *****************************************************************************/ +/** + * e1000_clear_hw_cntrs - Clears all hardware statistics counters. + * @hw: Struct containing variables accessed by shared code + */ static void e1000_clear_hw_cntrs(struct e1000_hw *hw) { - volatile u32 temp; - - temp = er32(CRCERRS); - temp = er32(SYMERRS); - temp = er32(MPC); - temp = er32(SCC); - temp = er32(ECOL); - temp = er32(MCC); - temp = er32(LATECOL); - temp = er32(COLC); - temp = er32(DC); - temp = er32(SEC); - temp = er32(RLEC); - temp = er32(XONRXC); - temp = er32(XONTXC); - temp = er32(XOFFRXC); - temp = er32(XOFFTXC); - temp = er32(FCRUC); - - if (hw->mac_type != e1000_ich8lan) { - temp = er32(PRC64); - temp = er32(PRC127); - temp = er32(PRC255); - temp = er32(PRC511); - temp = er32(PRC1023); - temp = er32(PRC1522); - } - - temp = er32(GPRC); - temp = er32(BPRC); - temp = er32(MPRC); - temp = er32(GPTC); - temp = er32(GORCL); - temp = er32(GORCH); - temp = er32(GOTCL); - temp = er32(GOTCH); - temp = er32(RNBC); - temp = er32(RUC); - temp = er32(RFC); - temp = er32(ROC); - temp = er32(RJC); - temp = er32(TORL); - temp = er32(TORH); - temp = er32(TOTL); - temp = er32(TOTH); - temp = er32(TPR); - temp = er32(TPT); - - if (hw->mac_type != e1000_ich8lan) { - temp = er32(PTC64); - temp = er32(PTC127); - temp = er32(PTC255); - temp = er32(PTC511); - temp = er32(PTC1023); - temp = er32(PTC1522); - } - - temp = er32(MPTC); - temp = er32(BPTC); - - if (hw->mac_type < e1000_82543) return; - - temp = er32(ALGNERRC); - temp = er32(RXERRC); - temp = er32(TNCRS); - temp = er32(CEXTERR); - temp = er32(TSCTC); - temp = er32(TSCTFC); - - if (hw->mac_type <= e1000_82544) return; - - temp = er32(MGTPRC); - temp = er32(MGTPDC); - temp = er32(MGTPTC); - - if (hw->mac_type <= e1000_82547_rev_2) return; - - temp = er32(IAC); - temp = er32(ICRXOC); - - if (hw->mac_type == e1000_ich8lan) return; - - temp = er32(ICRXPTC); - temp = er32(ICRXATC); - temp = er32(ICTXPTC); - temp = er32(ICTXATC); - temp = er32(ICTXQEC); - temp = er32(ICTXQMTC); - temp = er32(ICRXDMTC); -} - -/****************************************************************************** - * Resets Adaptive IFS to its default state. - * - * hw - Struct containing variables accessed by shared code + volatile u32 temp; + + temp = er32(CRCERRS); + temp = er32(SYMERRS); + temp = er32(MPC); + temp = er32(SCC); + temp = er32(ECOL); + temp = er32(MCC); + temp = er32(LATECOL); + temp = er32(COLC); + temp = er32(DC); + temp = er32(SEC); + temp = er32(RLEC); + temp = er32(XONRXC); + temp = er32(XONTXC); + temp = er32(XOFFRXC); + temp = er32(XOFFTXC); + temp = er32(FCRUC); + + temp = er32(PRC64); + temp = er32(PRC127); + temp = er32(PRC255); + temp = er32(PRC511); + temp = er32(PRC1023); + temp = er32(PRC1522); + + temp = er32(GPRC); + temp = er32(BPRC); + temp = er32(MPRC); + temp = er32(GPTC); + temp = er32(GORCL); + temp = er32(GORCH); + temp = er32(GOTCL); + temp = er32(GOTCH); + temp = er32(RNBC); + temp = er32(RUC); + temp = er32(RFC); + temp = er32(ROC); + temp = er32(RJC); + temp = er32(TORL); + temp = er32(TORH); + temp = er32(TOTL); + temp = er32(TOTH); + temp = er32(TPR); + temp = er32(TPT); + + temp = er32(PTC64); + temp = er32(PTC127); + temp = er32(PTC255); + temp = er32(PTC511); + temp = er32(PTC1023); + temp = er32(PTC1522); + + temp = er32(MPTC); + temp = er32(BPTC); + + if (hw->mac_type < e1000_82543) + return; + + temp = er32(ALGNERRC); + temp = er32(RXERRC); + temp = er32(TNCRS); + temp = er32(CEXTERR); + temp = er32(TSCTC); + temp = er32(TSCTFC); + + if (hw->mac_type <= e1000_82544) + return; + + temp = er32(MGTPRC); + temp = er32(MGTPDC); + temp = er32(MGTPTC); +} + +/** + * e1000_reset_adaptive - Resets Adaptive IFS to its default state. + * @hw: Struct containing variables accessed by shared code * * Call this after e1000_init_hw. You may override the IFS defaults by setting * hw->ifs_params_forced to true. However, you must initialize hw-> * current_ifs_val, ifs_min_val, ifs_max_val, ifs_step_size, and ifs_ratio * before calling this function. - *****************************************************************************/ + */ void e1000_reset_adaptive(struct e1000_hw *hw) { - DEBUGFUNC("e1000_reset_adaptive"); - - if (hw->adaptive_ifs) { - if (!hw->ifs_params_forced) { - hw->current_ifs_val = 0; - hw->ifs_min_val = IFS_MIN; - hw->ifs_max_val = IFS_MAX; - hw->ifs_step_size = IFS_STEP; - hw->ifs_ratio = IFS_RATIO; - } - hw->in_ifs_mode = false; - ew32(AIT, 0); - } else { - DEBUGOUT("Not in Adaptive IFS mode!\n"); - } + DEBUGFUNC("e1000_reset_adaptive"); + + if (hw->adaptive_ifs) { + if (!hw->ifs_params_forced) { + hw->current_ifs_val = 0; + hw->ifs_min_val = IFS_MIN; + hw->ifs_max_val = IFS_MAX; + hw->ifs_step_size = IFS_STEP; + hw->ifs_ratio = IFS_RATIO; + } + hw->in_ifs_mode = false; + ew32(AIT, 0); + } else { + DEBUGOUT("Not in Adaptive IFS mode!\n"); + } } -/****************************************************************************** +/** + * e1000_update_adaptive - update adaptive IFS + * @hw: Struct containing variables accessed by shared code + * @tx_packets: Number of transmits since last callback + * @total_collisions: Number of collisions since last callback + * * Called during the callback/watchdog routine to update IFS value based on * the ratio of transmits to collisions. - * - * hw - Struct containing variables accessed by shared code - * tx_packets - Number of transmits since last callback - * total_collisions - Number of collisions since last callback - *****************************************************************************/ + */ void e1000_update_adaptive(struct e1000_hw *hw) { - DEBUGFUNC("e1000_update_adaptive"); - - if (hw->adaptive_ifs) { - if ((hw->collision_delta * hw->ifs_ratio) > hw->tx_packet_delta) { - if (hw->tx_packet_delta > MIN_NUM_XMITS) { - hw->in_ifs_mode = true; - if (hw->current_ifs_val < hw->ifs_max_val) { - if (hw->current_ifs_val == 0) - hw->current_ifs_val = hw->ifs_min_val; - else - hw->current_ifs_val += hw->ifs_step_size; - ew32(AIT, hw->current_ifs_val); - } - } - } else { - if (hw->in_ifs_mode && (hw->tx_packet_delta <= MIN_NUM_XMITS)) { - hw->current_ifs_val = 0; - hw->in_ifs_mode = false; - ew32(AIT, 0); - } - } - } else { - DEBUGOUT("Not in Adaptive IFS mode!\n"); - } + DEBUGFUNC("e1000_update_adaptive"); + + if (hw->adaptive_ifs) { + if ((hw->collision_delta *hw->ifs_ratio) > hw->tx_packet_delta) { + if (hw->tx_packet_delta > MIN_NUM_XMITS) { + hw->in_ifs_mode = true; + if (hw->current_ifs_val < hw->ifs_max_val) { + if (hw->current_ifs_val == 0) + hw->current_ifs_val = + hw->ifs_min_val; + else + hw->current_ifs_val += + hw->ifs_step_size; + ew32(AIT, hw->current_ifs_val); + } + } + } else { + if (hw->in_ifs_mode + && (hw->tx_packet_delta <= MIN_NUM_XMITS)) { + hw->current_ifs_val = 0; + hw->in_ifs_mode = false; + ew32(AIT, 0); + } + } + } else { + DEBUGOUT("Not in Adaptive IFS mode!\n"); + } } -/****************************************************************************** - * Adjusts the statistic counters when a frame is accepted by TBI_ACCEPT +/** + * e1000_tbi_adjust_stats + * @hw: Struct containing variables accessed by shared code + * @frame_len: The length of the frame in question + * @mac_addr: The Ethernet destination address of the frame in question * - * hw - Struct containing variables accessed by shared code - * frame_len - The length of the frame in question - * mac_addr - The Ethernet destination address of the frame in question - *****************************************************************************/ + * Adjusts the statistic counters when a frame is accepted by TBI_ACCEPT + */ void e1000_tbi_adjust_stats(struct e1000_hw *hw, struct e1000_hw_stats *stats, u32 frame_len, u8 *mac_addr) { - u64 carry_bit; - - /* First adjust the frame length. */ - frame_len--; - /* We need to adjust the statistics counters, since the hardware - * counters overcount this packet as a CRC error and undercount - * the packet as a good packet - */ - /* This packet should not be counted as a CRC error. */ - stats->crcerrs--; - /* This packet does count as a Good Packet Received. */ - stats->gprc++; - - /* Adjust the Good Octets received counters */ - carry_bit = 0x80000000 & stats->gorcl; - stats->gorcl += frame_len; - /* If the high bit of Gorcl (the low 32 bits of the Good Octets - * Received Count) was one before the addition, - * AND it is zero after, then we lost the carry out, - * need to add one to Gorch (Good Octets Received Count High). - * This could be simplified if all environments supported - * 64-bit integers. - */ - if (carry_bit && ((stats->gorcl & 0x80000000) == 0)) - stats->gorch++; - /* Is this a broadcast or multicast? Check broadcast first, - * since the test for a multicast frame will test positive on - * a broadcast frame. - */ - if ((mac_addr[0] == (u8)0xff) && (mac_addr[1] == (u8)0xff)) - /* Broadcast packet */ - stats->bprc++; - else if (*mac_addr & 0x01) - /* Multicast packet */ - stats->mprc++; - - if (frame_len == hw->max_frame_size) { - /* In this case, the hardware has overcounted the number of - * oversize frames. - */ - if (stats->roc > 0) - stats->roc--; - } - - /* Adjust the bin counters when the extra byte put the frame in the - * wrong bin. Remember that the frame_len was adjusted above. - */ - if (frame_len == 64) { - stats->prc64++; - stats->prc127--; - } else if (frame_len == 127) { - stats->prc127++; - stats->prc255--; - } else if (frame_len == 255) { - stats->prc255++; - stats->prc511--; - } else if (frame_len == 511) { - stats->prc511++; - stats->prc1023--; - } else if (frame_len == 1023) { - stats->prc1023++; - stats->prc1522--; - } else if (frame_len == 1522) { - stats->prc1522++; - } + u64 carry_bit; + + /* First adjust the frame length. */ + frame_len--; + /* We need to adjust the statistics counters, since the hardware + * counters overcount this packet as a CRC error and undercount + * the packet as a good packet + */ + /* This packet should not be counted as a CRC error. */ + stats->crcerrs--; + /* This packet does count as a Good Packet Received. */ + stats->gprc++; + + /* Adjust the Good Octets received counters */ + carry_bit = 0x80000000 & stats->gorcl; + stats->gorcl += frame_len; + /* If the high bit of Gorcl (the low 32 bits of the Good Octets + * Received Count) was one before the addition, + * AND it is zero after, then we lost the carry out, + * need to add one to Gorch (Good Octets Received Count High). + * This could be simplified if all environments supported + * 64-bit integers. + */ + if (carry_bit && ((stats->gorcl & 0x80000000) == 0)) + stats->gorch++; + /* Is this a broadcast or multicast? Check broadcast first, + * since the test for a multicast frame will test positive on + * a broadcast frame. + */ + if ((mac_addr[0] == (u8) 0xff) && (mac_addr[1] == (u8) 0xff)) + /* Broadcast packet */ + stats->bprc++; + else if (*mac_addr & 0x01) + /* Multicast packet */ + stats->mprc++; + + if (frame_len == hw->max_frame_size) { + /* In this case, the hardware has overcounted the number of + * oversize frames. + */ + if (stats->roc > 0) + stats->roc--; + } + + /* Adjust the bin counters when the extra byte put the frame in the + * wrong bin. Remember that the frame_len was adjusted above. + */ + if (frame_len == 64) { + stats->prc64++; + stats->prc127--; + } else if (frame_len == 127) { + stats->prc127++; + stats->prc255--; + } else if (frame_len == 255) { + stats->prc255++; + stats->prc511--; + } else if (frame_len == 511) { + stats->prc511++; + stats->prc1023--; + } else if (frame_len == 1023) { + stats->prc1023++; + stats->prc1522--; + } else if (frame_len == 1522) { + stats->prc1522++; + } } -/****************************************************************************** - * Gets the current PCI bus type, speed, and width of the hardware +/** + * e1000_get_bus_info + * @hw: Struct containing variables accessed by shared code * - * hw - Struct containing variables accessed by shared code - *****************************************************************************/ + * Gets the current PCI bus type, speed, and width of the hardware + */ void e1000_get_bus_info(struct e1000_hw *hw) { - s32 ret_val; - u16 pci_ex_link_status; - u32 status; - - switch (hw->mac_type) { - case e1000_82542_rev2_0: - case e1000_82542_rev2_1: - hw->bus_type = e1000_bus_type_pci; - hw->bus_speed = e1000_bus_speed_unknown; - hw->bus_width = e1000_bus_width_unknown; - break; - case e1000_82571: - case e1000_82572: - case e1000_82573: - case e1000_80003es2lan: - hw->bus_type = e1000_bus_type_pci_express; - hw->bus_speed = e1000_bus_speed_2500; - ret_val = e1000_read_pcie_cap_reg(hw, - PCI_EX_LINK_STATUS, - &pci_ex_link_status); - if (ret_val) - hw->bus_width = e1000_bus_width_unknown; - else - hw->bus_width = (pci_ex_link_status & PCI_EX_LINK_WIDTH_MASK) >> - PCI_EX_LINK_WIDTH_SHIFT; - break; - case e1000_ich8lan: - hw->bus_type = e1000_bus_type_pci_express; - hw->bus_speed = e1000_bus_speed_2500; - hw->bus_width = e1000_bus_width_pciex_1; - break; - default: - status = er32(STATUS); - hw->bus_type = (status & E1000_STATUS_PCIX_MODE) ? - e1000_bus_type_pcix : e1000_bus_type_pci; - - if (hw->device_id == E1000_DEV_ID_82546EB_QUAD_COPPER) { - hw->bus_speed = (hw->bus_type == e1000_bus_type_pci) ? - e1000_bus_speed_66 : e1000_bus_speed_120; - } else if (hw->bus_type == e1000_bus_type_pci) { - hw->bus_speed = (status & E1000_STATUS_PCI66) ? - e1000_bus_speed_66 : e1000_bus_speed_33; - } else { - switch (status & E1000_STATUS_PCIX_SPEED) { - case E1000_STATUS_PCIX_SPEED_66: - hw->bus_speed = e1000_bus_speed_66; - break; - case E1000_STATUS_PCIX_SPEED_100: - hw->bus_speed = e1000_bus_speed_100; - break; - case E1000_STATUS_PCIX_SPEED_133: - hw->bus_speed = e1000_bus_speed_133; - break; - default: - hw->bus_speed = e1000_bus_speed_reserved; - break; - } - } - hw->bus_width = (status & E1000_STATUS_BUS64) ? - e1000_bus_width_64 : e1000_bus_width_32; - break; - } + u32 status; + + switch (hw->mac_type) { + case e1000_82542_rev2_0: + case e1000_82542_rev2_1: + hw->bus_type = e1000_bus_type_pci; + hw->bus_speed = e1000_bus_speed_unknown; + hw->bus_width = e1000_bus_width_unknown; + break; + default: + status = er32(STATUS); + hw->bus_type = (status & E1000_STATUS_PCIX_MODE) ? + e1000_bus_type_pcix : e1000_bus_type_pci; + + if (hw->device_id == E1000_DEV_ID_82546EB_QUAD_COPPER) { + hw->bus_speed = (hw->bus_type == e1000_bus_type_pci) ? + e1000_bus_speed_66 : e1000_bus_speed_120; + } else if (hw->bus_type == e1000_bus_type_pci) { + hw->bus_speed = (status & E1000_STATUS_PCI66) ? + e1000_bus_speed_66 : e1000_bus_speed_33; + } else { + switch (status & E1000_STATUS_PCIX_SPEED) { + case E1000_STATUS_PCIX_SPEED_66: + hw->bus_speed = e1000_bus_speed_66; + break; + case E1000_STATUS_PCIX_SPEED_100: + hw->bus_speed = e1000_bus_speed_100; + break; + case E1000_STATUS_PCIX_SPEED_133: + hw->bus_speed = e1000_bus_speed_133; + break; + default: + hw->bus_speed = e1000_bus_speed_reserved; + break; + } + } + hw->bus_width = (status & E1000_STATUS_BUS64) ? + e1000_bus_width_64 : e1000_bus_width_32; + break; + } } -/****************************************************************************** +/** + * e1000_write_reg_io + * @hw: Struct containing variables accessed by shared code + * @offset: offset to write to + * @value: value to write + * * Writes a value to one of the devices registers using port I/O (as opposed to * memory mapped I/O). Only 82544 and newer devices support port I/O. - * - * hw - Struct containing variables accessed by shared code - * offset - offset to write to - * value - value to write - *****************************************************************************/ + */ static void e1000_write_reg_io(struct e1000_hw *hw, u32 offset, u32 value) { - unsigned long io_addr = hw->io_base; - unsigned long io_data = hw->io_base + 4; + unsigned long io_addr = hw->io_base; + unsigned long io_data = hw->io_base + 4; - e1000_io_write(hw, io_addr, offset); - e1000_io_write(hw, io_data, value); + e1000_io_write(hw, io_addr, offset); + e1000_io_write(hw, io_data, value); } -/****************************************************************************** - * Estimates the cable length. - * - * hw - Struct containing variables accessed by shared code - * min_length - The estimated minimum length - * max_length - The estimated maximum length +/** + * e1000_get_cable_length - Estimates the cable length. + * @hw: Struct containing variables accessed by shared code + * @min_length: The estimated minimum length + * @max_length: The estimated maximum length * * returns: - E1000_ERR_XXX * E1000_SUCCESS @@ -6528,185 +4842,115 @@ static void e1000_write_reg_io(struct e1000_hw *hw, u32 offset, u32 value) * So for M88 phy's, this function interprets the one value returned from the * register to the minimum and maximum range. * For IGP phy's, the function calculates the range by the AGC registers. - *****************************************************************************/ + */ static s32 e1000_get_cable_length(struct e1000_hw *hw, u16 *min_length, u16 *max_length) { - s32 ret_val; - u16 agc_value = 0; - u16 i, phy_data; - u16 cable_length; - - DEBUGFUNC("e1000_get_cable_length"); - - *min_length = *max_length = 0; - - /* Use old method for Phy older than IGP */ - if (hw->phy_type == e1000_phy_m88) { - - ret_val = e1000_read_phy_reg(hw, M88E1000_PHY_SPEC_STATUS, - &phy_data); - if (ret_val) - return ret_val; - cable_length = (phy_data & M88E1000_PSSR_CABLE_LENGTH) >> - M88E1000_PSSR_CABLE_LENGTH_SHIFT; - - /* Convert the enum value to ranged values */ - switch (cable_length) { - case e1000_cable_length_50: - *min_length = 0; - *max_length = e1000_igp_cable_length_50; - break; - case e1000_cable_length_50_80: - *min_length = e1000_igp_cable_length_50; - *max_length = e1000_igp_cable_length_80; - break; - case e1000_cable_length_80_110: - *min_length = e1000_igp_cable_length_80; - *max_length = e1000_igp_cable_length_110; - break; - case e1000_cable_length_110_140: - *min_length = e1000_igp_cable_length_110; - *max_length = e1000_igp_cable_length_140; - break; - case e1000_cable_length_140: - *min_length = e1000_igp_cable_length_140; - *max_length = e1000_igp_cable_length_170; - break; - default: - return -E1000_ERR_PHY; - break; - } - } else if (hw->phy_type == e1000_phy_gg82563) { - ret_val = e1000_read_phy_reg(hw, GG82563_PHY_DSP_DISTANCE, - &phy_data); - if (ret_val) - return ret_val; - cable_length = phy_data & GG82563_DSPD_CABLE_LENGTH; - - switch (cable_length) { - case e1000_gg_cable_length_60: - *min_length = 0; - *max_length = e1000_igp_cable_length_60; - break; - case e1000_gg_cable_length_60_115: - *min_length = e1000_igp_cable_length_60; - *max_length = e1000_igp_cable_length_115; - break; - case e1000_gg_cable_length_115_150: - *min_length = e1000_igp_cable_length_115; - *max_length = e1000_igp_cable_length_150; - break; - case e1000_gg_cable_length_150: - *min_length = e1000_igp_cable_length_150; - *max_length = e1000_igp_cable_length_180; - break; - default: - return -E1000_ERR_PHY; - break; - } - } else if (hw->phy_type == e1000_phy_igp) { /* For IGP PHY */ - u16 cur_agc_value; - u16 min_agc_value = IGP01E1000_AGC_LENGTH_TABLE_SIZE; - u16 agc_reg_array[IGP01E1000_PHY_CHANNEL_NUM] = - {IGP01E1000_PHY_AGC_A, - IGP01E1000_PHY_AGC_B, - IGP01E1000_PHY_AGC_C, - IGP01E1000_PHY_AGC_D}; - /* Read the AGC registers for all channels */ - for (i = 0; i < IGP01E1000_PHY_CHANNEL_NUM; i++) { - - ret_val = e1000_read_phy_reg(hw, agc_reg_array[i], &phy_data); - if (ret_val) - return ret_val; - - cur_agc_value = phy_data >> IGP01E1000_AGC_LENGTH_SHIFT; - - /* Value bound check. */ - if ((cur_agc_value >= IGP01E1000_AGC_LENGTH_TABLE_SIZE - 1) || - (cur_agc_value == 0)) - return -E1000_ERR_PHY; - - agc_value += cur_agc_value; - - /* Update minimal AGC value. */ - if (min_agc_value > cur_agc_value) - min_agc_value = cur_agc_value; - } - - /* Remove the minimal AGC result for length < 50m */ - if (agc_value < IGP01E1000_PHY_CHANNEL_NUM * e1000_igp_cable_length_50) { - agc_value -= min_agc_value; - - /* Get the average length of the remaining 3 channels */ - agc_value /= (IGP01E1000_PHY_CHANNEL_NUM - 1); - } else { - /* Get the average length of all the 4 channels. */ - agc_value /= IGP01E1000_PHY_CHANNEL_NUM; - } - - /* Set the range of the calculated length. */ - *min_length = ((e1000_igp_cable_length_table[agc_value] - - IGP01E1000_AGC_RANGE) > 0) ? - (e1000_igp_cable_length_table[agc_value] - - IGP01E1000_AGC_RANGE) : 0; - *max_length = e1000_igp_cable_length_table[agc_value] + - IGP01E1000_AGC_RANGE; - } else if (hw->phy_type == e1000_phy_igp_2 || - hw->phy_type == e1000_phy_igp_3) { - u16 cur_agc_index, max_agc_index = 0; - u16 min_agc_index = IGP02E1000_AGC_LENGTH_TABLE_SIZE - 1; - u16 agc_reg_array[IGP02E1000_PHY_CHANNEL_NUM] = - {IGP02E1000_PHY_AGC_A, - IGP02E1000_PHY_AGC_B, - IGP02E1000_PHY_AGC_C, - IGP02E1000_PHY_AGC_D}; - /* Read the AGC registers for all channels */ - for (i = 0; i < IGP02E1000_PHY_CHANNEL_NUM; i++) { - ret_val = e1000_read_phy_reg(hw, agc_reg_array[i], &phy_data); - if (ret_val) - return ret_val; - - /* Getting bits 15:9, which represent the combination of course and - * fine gain values. The result is a number that can be put into - * the lookup table to obtain the approximate cable length. */ - cur_agc_index = (phy_data >> IGP02E1000_AGC_LENGTH_SHIFT) & - IGP02E1000_AGC_LENGTH_MASK; - - /* Array index bound check. */ - if ((cur_agc_index >= IGP02E1000_AGC_LENGTH_TABLE_SIZE) || - (cur_agc_index == 0)) - return -E1000_ERR_PHY; - - /* Remove min & max AGC values from calculation. */ - if (e1000_igp_2_cable_length_table[min_agc_index] > - e1000_igp_2_cable_length_table[cur_agc_index]) - min_agc_index = cur_agc_index; - if (e1000_igp_2_cable_length_table[max_agc_index] < - e1000_igp_2_cable_length_table[cur_agc_index]) - max_agc_index = cur_agc_index; - - agc_value += e1000_igp_2_cable_length_table[cur_agc_index]; - } - - agc_value -= (e1000_igp_2_cable_length_table[min_agc_index] + - e1000_igp_2_cable_length_table[max_agc_index]); - agc_value /= (IGP02E1000_PHY_CHANNEL_NUM - 2); - - /* Calculate cable length with the error range of +/- 10 meters. */ - *min_length = ((agc_value - IGP02E1000_AGC_RANGE) > 0) ? - (agc_value - IGP02E1000_AGC_RANGE) : 0; - *max_length = agc_value + IGP02E1000_AGC_RANGE; - } - - return E1000_SUCCESS; + s32 ret_val; + u16 agc_value = 0; + u16 i, phy_data; + u16 cable_length; + + DEBUGFUNC("e1000_get_cable_length"); + + *min_length = *max_length = 0; + + /* Use old method for Phy older than IGP */ + if (hw->phy_type == e1000_phy_m88) { + + ret_val = e1000_read_phy_reg(hw, M88E1000_PHY_SPEC_STATUS, + &phy_data); + if (ret_val) + return ret_val; + cable_length = (phy_data & M88E1000_PSSR_CABLE_LENGTH) >> + M88E1000_PSSR_CABLE_LENGTH_SHIFT; + + /* Convert the enum value to ranged values */ + switch (cable_length) { + case e1000_cable_length_50: + *min_length = 0; + *max_length = e1000_igp_cable_length_50; + break; + case e1000_cable_length_50_80: + *min_length = e1000_igp_cable_length_50; + *max_length = e1000_igp_cable_length_80; + break; + case e1000_cable_length_80_110: + *min_length = e1000_igp_cable_length_80; + *max_length = e1000_igp_cable_length_110; + break; + case e1000_cable_length_110_140: + *min_length = e1000_igp_cable_length_110; + *max_length = e1000_igp_cable_length_140; + break; + case e1000_cable_length_140: + *min_length = e1000_igp_cable_length_140; + *max_length = e1000_igp_cable_length_170; + break; + default: + return -E1000_ERR_PHY; + break; + } + } else if (hw->phy_type == e1000_phy_igp) { /* For IGP PHY */ + u16 cur_agc_value; + u16 min_agc_value = IGP01E1000_AGC_LENGTH_TABLE_SIZE; + u16 agc_reg_array[IGP01E1000_PHY_CHANNEL_NUM] = + { IGP01E1000_PHY_AGC_A, + IGP01E1000_PHY_AGC_B, + IGP01E1000_PHY_AGC_C, + IGP01E1000_PHY_AGC_D + }; + /* Read the AGC registers for all channels */ + for (i = 0; i < IGP01E1000_PHY_CHANNEL_NUM; i++) { + + ret_val = + e1000_read_phy_reg(hw, agc_reg_array[i], &phy_data); + if (ret_val) + return ret_val; + + cur_agc_value = phy_data >> IGP01E1000_AGC_LENGTH_SHIFT; + + /* Value bound check. */ + if ((cur_agc_value >= + IGP01E1000_AGC_LENGTH_TABLE_SIZE - 1) + || (cur_agc_value == 0)) + return -E1000_ERR_PHY; + + agc_value += cur_agc_value; + + /* Update minimal AGC value. */ + if (min_agc_value > cur_agc_value) + min_agc_value = cur_agc_value; + } + + /* Remove the minimal AGC result for length < 50m */ + if (agc_value < + IGP01E1000_PHY_CHANNEL_NUM * e1000_igp_cable_length_50) { + agc_value -= min_agc_value; + + /* Get the average length of the remaining 3 channels */ + agc_value /= (IGP01E1000_PHY_CHANNEL_NUM - 1); + } else { + /* Get the average length of all the 4 channels. */ + agc_value /= IGP01E1000_PHY_CHANNEL_NUM; + } + + /* Set the range of the calculated length. */ + *min_length = ((e1000_igp_cable_length_table[agc_value] - + IGP01E1000_AGC_RANGE) > 0) ? + (e1000_igp_cable_length_table[agc_value] - + IGP01E1000_AGC_RANGE) : 0; + *max_length = e1000_igp_cable_length_table[agc_value] + + IGP01E1000_AGC_RANGE; + } + + return E1000_SUCCESS; } -/****************************************************************************** - * Check the cable polarity - * - * hw - Struct containing variables accessed by shared code - * polarity - output parameter : 0 - Polarity is not reversed +/** + * e1000_check_polarity - Check the cable polarity + * @hw: Struct containing variables accessed by shared code + * @polarity: output parameter : 0 - Polarity is not reversed * 1 - Polarity is reversed. * * returns: - E1000_ERR_XXX @@ -6717,73 +4961,65 @@ static s32 e1000_get_cable_length(struct e1000_hw *hw, u16 *min_length, * 10 Mbps. If the link speed is 100 Mbps there is no polarity so this bit will * return 0. If the link speed is 1000 Mbps the polarity status is in the * IGP01E1000_PHY_PCS_INIT_REG. - *****************************************************************************/ + */ static s32 e1000_check_polarity(struct e1000_hw *hw, e1000_rev_polarity *polarity) { - s32 ret_val; - u16 phy_data; - - DEBUGFUNC("e1000_check_polarity"); - - if ((hw->phy_type == e1000_phy_m88) || - (hw->phy_type == e1000_phy_gg82563)) { - /* return the Polarity bit in the Status register. */ - ret_val = e1000_read_phy_reg(hw, M88E1000_PHY_SPEC_STATUS, - &phy_data); - if (ret_val) - return ret_val; - *polarity = ((phy_data & M88E1000_PSSR_REV_POLARITY) >> - M88E1000_PSSR_REV_POLARITY_SHIFT) ? - e1000_rev_polarity_reversed : e1000_rev_polarity_normal; - - } else if (hw->phy_type == e1000_phy_igp || - hw->phy_type == e1000_phy_igp_3 || - hw->phy_type == e1000_phy_igp_2) { - /* Read the Status register to check the speed */ - ret_val = e1000_read_phy_reg(hw, IGP01E1000_PHY_PORT_STATUS, - &phy_data); - if (ret_val) - return ret_val; - - /* If speed is 1000 Mbps, must read the IGP01E1000_PHY_PCS_INIT_REG to - * find the polarity status */ - if ((phy_data & IGP01E1000_PSSR_SPEED_MASK) == - IGP01E1000_PSSR_SPEED_1000MBPS) { - - /* Read the GIG initialization PCS register (0x00B4) */ - ret_val = e1000_read_phy_reg(hw, IGP01E1000_PHY_PCS_INIT_REG, - &phy_data); - if (ret_val) - return ret_val; - - /* Check the polarity bits */ - *polarity = (phy_data & IGP01E1000_PHY_POLARITY_MASK) ? - e1000_rev_polarity_reversed : e1000_rev_polarity_normal; - } else { - /* For 10 Mbps, read the polarity bit in the status register. (for - * 100 Mbps this bit is always 0) */ - *polarity = (phy_data & IGP01E1000_PSSR_POLARITY_REVERSED) ? - e1000_rev_polarity_reversed : e1000_rev_polarity_normal; - } - } else if (hw->phy_type == e1000_phy_ife) { - ret_val = e1000_read_phy_reg(hw, IFE_PHY_EXTENDED_STATUS_CONTROL, - &phy_data); - if (ret_val) - return ret_val; - *polarity = ((phy_data & IFE_PESC_POLARITY_REVERSED) >> - IFE_PESC_POLARITY_REVERSED_SHIFT) ? - e1000_rev_polarity_reversed : e1000_rev_polarity_normal; - } - return E1000_SUCCESS; + s32 ret_val; + u16 phy_data; + + DEBUGFUNC("e1000_check_polarity"); + + if (hw->phy_type == e1000_phy_m88) { + /* return the Polarity bit in the Status register. */ + ret_val = e1000_read_phy_reg(hw, M88E1000_PHY_SPEC_STATUS, + &phy_data); + if (ret_val) + return ret_val; + *polarity = ((phy_data & M88E1000_PSSR_REV_POLARITY) >> + M88E1000_PSSR_REV_POLARITY_SHIFT) ? + e1000_rev_polarity_reversed : e1000_rev_polarity_normal; + + } else if (hw->phy_type == e1000_phy_igp) { + /* Read the Status register to check the speed */ + ret_val = e1000_read_phy_reg(hw, IGP01E1000_PHY_PORT_STATUS, + &phy_data); + if (ret_val) + return ret_val; + + /* If speed is 1000 Mbps, must read the IGP01E1000_PHY_PCS_INIT_REG to + * find the polarity status */ + if ((phy_data & IGP01E1000_PSSR_SPEED_MASK) == + IGP01E1000_PSSR_SPEED_1000MBPS) { + + /* Read the GIG initialization PCS register (0x00B4) */ + ret_val = + e1000_read_phy_reg(hw, IGP01E1000_PHY_PCS_INIT_REG, + &phy_data); + if (ret_val) + return ret_val; + + /* Check the polarity bits */ + *polarity = (phy_data & IGP01E1000_PHY_POLARITY_MASK) ? + e1000_rev_polarity_reversed : + e1000_rev_polarity_normal; + } else { + /* For 10 Mbps, read the polarity bit in the status register. (for + * 100 Mbps this bit is always 0) */ + *polarity = + (phy_data & IGP01E1000_PSSR_POLARITY_REVERSED) ? + e1000_rev_polarity_reversed : + e1000_rev_polarity_normal; + } + } + return E1000_SUCCESS; } -/****************************************************************************** - * Check if Downshift occured - * - * hw - Struct containing variables accessed by shared code - * downshift - output parameter : 0 - No Downshift ocured. - * 1 - Downshift ocured. +/** + * e1000_check_downshift - Check if Downshift occurred + * @hw: Struct containing variables accessed by shared code + * @downshift: output parameter : 0 - No Downshift occurred. + * 1 - Downshift occurred. * * returns: - E1000_ERR_XXX * E1000_SUCCESS @@ -6792,2041 +5028,607 @@ static s32 e1000_check_polarity(struct e1000_hw *hw, * Specific Status register. For IGP phy's, it reads the Downgrade bit in the * Link Health register. In IGP this bit is latched high, so the driver must * read it immediately after link is established. - *****************************************************************************/ + */ static s32 e1000_check_downshift(struct e1000_hw *hw) { - s32 ret_val; - u16 phy_data; - - DEBUGFUNC("e1000_check_downshift"); - - if (hw->phy_type == e1000_phy_igp || - hw->phy_type == e1000_phy_igp_3 || - hw->phy_type == e1000_phy_igp_2) { - ret_val = e1000_read_phy_reg(hw, IGP01E1000_PHY_LINK_HEALTH, - &phy_data); - if (ret_val) - return ret_val; - - hw->speed_downgraded = (phy_data & IGP01E1000_PLHR_SS_DOWNGRADE) ? 1 : 0; - } else if ((hw->phy_type == e1000_phy_m88) || - (hw->phy_type == e1000_phy_gg82563)) { - ret_val = e1000_read_phy_reg(hw, M88E1000_PHY_SPEC_STATUS, - &phy_data); - if (ret_val) - return ret_val; - - hw->speed_downgraded = (phy_data & M88E1000_PSSR_DOWNSHIFT) >> - M88E1000_PSSR_DOWNSHIFT_SHIFT; - } else if (hw->phy_type == e1000_phy_ife) { - /* e1000_phy_ife supports 10/100 speed only */ - hw->speed_downgraded = false; - } - - return E1000_SUCCESS; -} + s32 ret_val; + u16 phy_data; -/***************************************************************************** - * - * 82541_rev_2 & 82547_rev_2 have the capability to configure the DSP when a - * gigabit link is achieved to improve link quality. - * - * hw: Struct containing variables accessed by shared code - * - * returns: - E1000_ERR_PHY if fail to read/write the PHY - * E1000_SUCCESS at any other case. - * - ****************************************************************************/ + DEBUGFUNC("e1000_check_downshift"); -static s32 e1000_config_dsp_after_link_change(struct e1000_hw *hw, bool link_up) -{ - s32 ret_val; - u16 phy_data, phy_saved_data, speed, duplex, i; - u16 dsp_reg_array[IGP01E1000_PHY_CHANNEL_NUM] = - {IGP01E1000_PHY_AGC_PARAM_A, - IGP01E1000_PHY_AGC_PARAM_B, - IGP01E1000_PHY_AGC_PARAM_C, - IGP01E1000_PHY_AGC_PARAM_D}; - u16 min_length, max_length; - - DEBUGFUNC("e1000_config_dsp_after_link_change"); - - if (hw->phy_type != e1000_phy_igp) - return E1000_SUCCESS; - - if (link_up) { - ret_val = e1000_get_speed_and_duplex(hw, &speed, &duplex); - if (ret_val) { - DEBUGOUT("Error getting link speed and duplex\n"); - return ret_val; - } - - if (speed == SPEED_1000) { - - ret_val = e1000_get_cable_length(hw, &min_length, &max_length); - if (ret_val) - return ret_val; - - if ((hw->dsp_config_state == e1000_dsp_config_enabled) && - min_length >= e1000_igp_cable_length_50) { - - for (i = 0; i < IGP01E1000_PHY_CHANNEL_NUM; i++) { - ret_val = e1000_read_phy_reg(hw, dsp_reg_array[i], - &phy_data); - if (ret_val) - return ret_val; - - phy_data &= ~IGP01E1000_PHY_EDAC_MU_INDEX; - - ret_val = e1000_write_phy_reg(hw, dsp_reg_array[i], - phy_data); - if (ret_val) - return ret_val; - } - hw->dsp_config_state = e1000_dsp_config_activated; - } - - if ((hw->ffe_config_state == e1000_ffe_config_enabled) && - (min_length < e1000_igp_cable_length_50)) { - - u16 ffe_idle_err_timeout = FFE_IDLE_ERR_COUNT_TIMEOUT_20; - u32 idle_errs = 0; - - /* clear previous idle error counts */ - ret_val = e1000_read_phy_reg(hw, PHY_1000T_STATUS, - &phy_data); - if (ret_val) - return ret_val; - - for (i = 0; i < ffe_idle_err_timeout; i++) { - udelay(1000); - ret_val = e1000_read_phy_reg(hw, PHY_1000T_STATUS, - &phy_data); - if (ret_val) - return ret_val; - - idle_errs += (phy_data & SR_1000T_IDLE_ERROR_CNT); - if (idle_errs > SR_1000T_PHY_EXCESSIVE_IDLE_ERR_COUNT) { - hw->ffe_config_state = e1000_ffe_config_active; - - ret_val = e1000_write_phy_reg(hw, - IGP01E1000_PHY_DSP_FFE, - IGP01E1000_PHY_DSP_FFE_CM_CP); - if (ret_val) - return ret_val; - break; - } - - if (idle_errs) - ffe_idle_err_timeout = FFE_IDLE_ERR_COUNT_TIMEOUT_100; - } - } - } - } else { - if (hw->dsp_config_state == e1000_dsp_config_activated) { - /* Save off the current value of register 0x2F5B to be restored at - * the end of the routines. */ - ret_val = e1000_read_phy_reg(hw, 0x2F5B, &phy_saved_data); - - if (ret_val) - return ret_val; - - /* Disable the PHY transmitter */ - ret_val = e1000_write_phy_reg(hw, 0x2F5B, 0x0003); - - if (ret_val) - return ret_val; - - mdelay(20); - - ret_val = e1000_write_phy_reg(hw, 0x0000, - IGP01E1000_IEEE_FORCE_GIGA); - if (ret_val) - return ret_val; - for (i = 0; i < IGP01E1000_PHY_CHANNEL_NUM; i++) { - ret_val = e1000_read_phy_reg(hw, dsp_reg_array[i], &phy_data); - if (ret_val) - return ret_val; - - phy_data &= ~IGP01E1000_PHY_EDAC_MU_INDEX; - phy_data |= IGP01E1000_PHY_EDAC_SIGN_EXT_9_BITS; - - ret_val = e1000_write_phy_reg(hw,dsp_reg_array[i], phy_data); - if (ret_val) - return ret_val; - } - - ret_val = e1000_write_phy_reg(hw, 0x0000, - IGP01E1000_IEEE_RESTART_AUTONEG); - if (ret_val) - return ret_val; - - mdelay(20); - - /* Now enable the transmitter */ - ret_val = e1000_write_phy_reg(hw, 0x2F5B, phy_saved_data); - - if (ret_val) - return ret_val; - - hw->dsp_config_state = e1000_dsp_config_enabled; - } - - if (hw->ffe_config_state == e1000_ffe_config_active) { - /* Save off the current value of register 0x2F5B to be restored at - * the end of the routines. */ - ret_val = e1000_read_phy_reg(hw, 0x2F5B, &phy_saved_data); - - if (ret_val) - return ret_val; - - /* Disable the PHY transmitter */ - ret_val = e1000_write_phy_reg(hw, 0x2F5B, 0x0003); - - if (ret_val) - return ret_val; - - mdelay(20); - - ret_val = e1000_write_phy_reg(hw, 0x0000, - IGP01E1000_IEEE_FORCE_GIGA); - if (ret_val) - return ret_val; - ret_val = e1000_write_phy_reg(hw, IGP01E1000_PHY_DSP_FFE, - IGP01E1000_PHY_DSP_FFE_DEFAULT); - if (ret_val) - return ret_val; - - ret_val = e1000_write_phy_reg(hw, 0x0000, - IGP01E1000_IEEE_RESTART_AUTONEG); - if (ret_val) - return ret_val; - - mdelay(20); - - /* Now enable the transmitter */ - ret_val = e1000_write_phy_reg(hw, 0x2F5B, phy_saved_data); - - if (ret_val) - return ret_val; - - hw->ffe_config_state = e1000_ffe_config_enabled; - } - } - return E1000_SUCCESS; -} + if (hw->phy_type == e1000_phy_igp) { + ret_val = e1000_read_phy_reg(hw, IGP01E1000_PHY_LINK_HEALTH, + &phy_data); + if (ret_val) + return ret_val; -/***************************************************************************** - * Set PHY to class A mode - * Assumes the following operations will follow to enable the new class mode. - * 1. Do a PHY soft reset - * 2. Restart auto-negotiation or force link. - * - * hw - Struct containing variables accessed by shared code - ****************************************************************************/ -static s32 e1000_set_phy_mode(struct e1000_hw *hw) -{ - s32 ret_val; - u16 eeprom_data; - - DEBUGFUNC("e1000_set_phy_mode"); - - if ((hw->mac_type == e1000_82545_rev_3) && - (hw->media_type == e1000_media_type_copper)) { - ret_val = e1000_read_eeprom(hw, EEPROM_PHY_CLASS_WORD, 1, &eeprom_data); - if (ret_val) { - return ret_val; - } - - if ((eeprom_data != EEPROM_RESERVED_WORD) && - (eeprom_data & EEPROM_PHY_CLASS_A)) { - ret_val = e1000_write_phy_reg(hw, M88E1000_PHY_PAGE_SELECT, 0x000B); - if (ret_val) - return ret_val; - ret_val = e1000_write_phy_reg(hw, M88E1000_PHY_GEN_CONTROL, 0x8104); - if (ret_val) - return ret_val; - - hw->phy_reset_disable = false; - } - } - - return E1000_SUCCESS; -} + hw->speed_downgraded = + (phy_data & IGP01E1000_PLHR_SS_DOWNGRADE) ? 1 : 0; + } else if (hw->phy_type == e1000_phy_m88) { + ret_val = e1000_read_phy_reg(hw, M88E1000_PHY_SPEC_STATUS, + &phy_data); + if (ret_val) + return ret_val; -/***************************************************************************** - * - * This function sets the lplu state according to the active flag. When - * activating lplu this function also disables smart speed and vise versa. - * lplu will not be activated unless the device autonegotiation advertisment - * meets standards of either 10 or 10/100 or 10/100/1000 at all duplexes. - * hw: Struct containing variables accessed by shared code - * active - true to enable lplu false to disable lplu. - * - * returns: - E1000_ERR_PHY if fail to read/write the PHY - * E1000_SUCCESS at any other case. - * - ****************************************************************************/ + hw->speed_downgraded = (phy_data & M88E1000_PSSR_DOWNSHIFT) >> + M88E1000_PSSR_DOWNSHIFT_SHIFT; + } -static s32 e1000_set_d3_lplu_state(struct e1000_hw *hw, bool active) -{ - u32 phy_ctrl = 0; - s32 ret_val; - u16 phy_data; - DEBUGFUNC("e1000_set_d3_lplu_state"); - - if (hw->phy_type != e1000_phy_igp && hw->phy_type != e1000_phy_igp_2 - && hw->phy_type != e1000_phy_igp_3) - return E1000_SUCCESS; - - /* During driver activity LPLU should not be used or it will attain link - * from the lowest speeds starting from 10Mbps. The capability is used for - * Dx transitions and states */ - if (hw->mac_type == e1000_82541_rev_2 || hw->mac_type == e1000_82547_rev_2) { - ret_val = e1000_read_phy_reg(hw, IGP01E1000_GMII_FIFO, &phy_data); - if (ret_val) - return ret_val; - } else if (hw->mac_type == e1000_ich8lan) { - /* MAC writes into PHY register based on the state transition - * and start auto-negotiation. SW driver can overwrite the settings - * in CSR PHY power control E1000_PHY_CTRL register. */ - phy_ctrl = er32(PHY_CTRL); - } else { - ret_val = e1000_read_phy_reg(hw, IGP02E1000_PHY_POWER_MGMT, &phy_data); - if (ret_val) - return ret_val; - } - - if (!active) { - if (hw->mac_type == e1000_82541_rev_2 || - hw->mac_type == e1000_82547_rev_2) { - phy_data &= ~IGP01E1000_GMII_FLEX_SPD; - ret_val = e1000_write_phy_reg(hw, IGP01E1000_GMII_FIFO, phy_data); - if (ret_val) - return ret_val; - } else { - if (hw->mac_type == e1000_ich8lan) { - phy_ctrl &= ~E1000_PHY_CTRL_NOND0A_LPLU; - ew32(PHY_CTRL, phy_ctrl); - } else { - phy_data &= ~IGP02E1000_PM_D3_LPLU; - ret_val = e1000_write_phy_reg(hw, IGP02E1000_PHY_POWER_MGMT, - phy_data); - if (ret_val) - return ret_val; - } - } - - /* LPLU and SmartSpeed are mutually exclusive. LPLU is used during - * Dx states where the power conservation is most important. During - * driver activity we should enable SmartSpeed, so performance is - * maintained. */ - if (hw->smart_speed == e1000_smart_speed_on) { - ret_val = e1000_read_phy_reg(hw, IGP01E1000_PHY_PORT_CONFIG, - &phy_data); - if (ret_val) - return ret_val; - - phy_data |= IGP01E1000_PSCFR_SMART_SPEED; - ret_val = e1000_write_phy_reg(hw, IGP01E1000_PHY_PORT_CONFIG, - phy_data); - if (ret_val) - return ret_val; - } else if (hw->smart_speed == e1000_smart_speed_off) { - ret_val = e1000_read_phy_reg(hw, IGP01E1000_PHY_PORT_CONFIG, - &phy_data); - if (ret_val) - return ret_val; - - phy_data &= ~IGP01E1000_PSCFR_SMART_SPEED; - ret_val = e1000_write_phy_reg(hw, IGP01E1000_PHY_PORT_CONFIG, - phy_data); - if (ret_val) - return ret_val; - } - - } else if ((hw->autoneg_advertised == AUTONEG_ADVERTISE_SPEED_DEFAULT) || - (hw->autoneg_advertised == AUTONEG_ADVERTISE_10_ALL ) || - (hw->autoneg_advertised == AUTONEG_ADVERTISE_10_100_ALL)) { - - if (hw->mac_type == e1000_82541_rev_2 || - hw->mac_type == e1000_82547_rev_2) { - phy_data |= IGP01E1000_GMII_FLEX_SPD; - ret_val = e1000_write_phy_reg(hw, IGP01E1000_GMII_FIFO, phy_data); - if (ret_val) - return ret_val; - } else { - if (hw->mac_type == e1000_ich8lan) { - phy_ctrl |= E1000_PHY_CTRL_NOND0A_LPLU; - ew32(PHY_CTRL, phy_ctrl); - } else { - phy_data |= IGP02E1000_PM_D3_LPLU; - ret_val = e1000_write_phy_reg(hw, IGP02E1000_PHY_POWER_MGMT, - phy_data); - if (ret_val) - return ret_val; - } - } - - /* When LPLU is enabled we should disable SmartSpeed */ - ret_val = e1000_read_phy_reg(hw, IGP01E1000_PHY_PORT_CONFIG, &phy_data); - if (ret_val) - return ret_val; - - phy_data &= ~IGP01E1000_PSCFR_SMART_SPEED; - ret_val = e1000_write_phy_reg(hw, IGP01E1000_PHY_PORT_CONFIG, phy_data); - if (ret_val) - return ret_val; - - } - return E1000_SUCCESS; + return E1000_SUCCESS; } -/***************************************************************************** - * - * This function sets the lplu d0 state according to the active flag. When - * activating lplu this function also disables smart speed and vise versa. - * lplu will not be activated unless the device autonegotiation advertisment - * meets standards of either 10 or 10/100 or 10/100/1000 at all duplexes. - * hw: Struct containing variables accessed by shared code - * active - true to enable lplu false to disable lplu. +/** + * e1000_config_dsp_after_link_change + * @hw: Struct containing variables accessed by shared code + * @link_up: was link up at the time this was called * * returns: - E1000_ERR_PHY if fail to read/write the PHY * E1000_SUCCESS at any other case. * - ****************************************************************************/ - -static s32 e1000_set_d0_lplu_state(struct e1000_hw *hw, bool active) -{ - u32 phy_ctrl = 0; - s32 ret_val; - u16 phy_data; - DEBUGFUNC("e1000_set_d0_lplu_state"); - - if (hw->mac_type <= e1000_82547_rev_2) - return E1000_SUCCESS; - - if (hw->mac_type == e1000_ich8lan) { - phy_ctrl = er32(PHY_CTRL); - } else { - ret_val = e1000_read_phy_reg(hw, IGP02E1000_PHY_POWER_MGMT, &phy_data); - if (ret_val) - return ret_val; - } - - if (!active) { - if (hw->mac_type == e1000_ich8lan) { - phy_ctrl &= ~E1000_PHY_CTRL_D0A_LPLU; - ew32(PHY_CTRL, phy_ctrl); - } else { - phy_data &= ~IGP02E1000_PM_D0_LPLU; - ret_val = e1000_write_phy_reg(hw, IGP02E1000_PHY_POWER_MGMT, phy_data); - if (ret_val) - return ret_val; - } - - /* LPLU and SmartSpeed are mutually exclusive. LPLU is used during - * Dx states where the power conservation is most important. During - * driver activity we should enable SmartSpeed, so performance is - * maintained. */ - if (hw->smart_speed == e1000_smart_speed_on) { - ret_val = e1000_read_phy_reg(hw, IGP01E1000_PHY_PORT_CONFIG, - &phy_data); - if (ret_val) - return ret_val; - - phy_data |= IGP01E1000_PSCFR_SMART_SPEED; - ret_val = e1000_write_phy_reg(hw, IGP01E1000_PHY_PORT_CONFIG, - phy_data); - if (ret_val) - return ret_val; - } else if (hw->smart_speed == e1000_smart_speed_off) { - ret_val = e1000_read_phy_reg(hw, IGP01E1000_PHY_PORT_CONFIG, - &phy_data); - if (ret_val) - return ret_val; - - phy_data &= ~IGP01E1000_PSCFR_SMART_SPEED; - ret_val = e1000_write_phy_reg(hw, IGP01E1000_PHY_PORT_CONFIG, - phy_data); - if (ret_val) - return ret_val; - } - - - } else { - - if (hw->mac_type == e1000_ich8lan) { - phy_ctrl |= E1000_PHY_CTRL_D0A_LPLU; - ew32(PHY_CTRL, phy_ctrl); - } else { - phy_data |= IGP02E1000_PM_D0_LPLU; - ret_val = e1000_write_phy_reg(hw, IGP02E1000_PHY_POWER_MGMT, phy_data); - if (ret_val) - return ret_val; - } - - /* When LPLU is enabled we should disable SmartSpeed */ - ret_val = e1000_read_phy_reg(hw, IGP01E1000_PHY_PORT_CONFIG, &phy_data); - if (ret_val) - return ret_val; - - phy_data &= ~IGP01E1000_PSCFR_SMART_SPEED; - ret_val = e1000_write_phy_reg(hw, IGP01E1000_PHY_PORT_CONFIG, phy_data); - if (ret_val) - return ret_val; - - } - return E1000_SUCCESS; -} + * 82541_rev_2 & 82547_rev_2 have the capability to configure the DSP when a + * gigabit link is achieved to improve link quality. + */ -/****************************************************************************** - * Change VCO speed register to improve Bit Error Rate performance of SERDES. - * - * hw - Struct containing variables accessed by shared code - *****************************************************************************/ -static s32 e1000_set_vco_speed(struct e1000_hw *hw) +static s32 e1000_config_dsp_after_link_change(struct e1000_hw *hw, bool link_up) { - s32 ret_val; - u16 default_page = 0; - u16 phy_data; - - DEBUGFUNC("e1000_set_vco_speed"); + s32 ret_val; + u16 phy_data, phy_saved_data, speed, duplex, i; + u16 dsp_reg_array[IGP01E1000_PHY_CHANNEL_NUM] = + { IGP01E1000_PHY_AGC_PARAM_A, + IGP01E1000_PHY_AGC_PARAM_B, + IGP01E1000_PHY_AGC_PARAM_C, + IGP01E1000_PHY_AGC_PARAM_D + }; + u16 min_length, max_length; + + DEBUGFUNC("e1000_config_dsp_after_link_change"); + + if (hw->phy_type != e1000_phy_igp) + return E1000_SUCCESS; + + if (link_up) { + ret_val = e1000_get_speed_and_duplex(hw, &speed, &duplex); + if (ret_val) { + DEBUGOUT("Error getting link speed and duplex\n"); + return ret_val; + } - switch (hw->mac_type) { - case e1000_82545_rev_3: - case e1000_82546_rev_3: - break; - default: - return E1000_SUCCESS; - } + if (speed == SPEED_1000) { + + ret_val = + e1000_get_cable_length(hw, &min_length, + &max_length); + if (ret_val) + return ret_val; + + if ((hw->dsp_config_state == e1000_dsp_config_enabled) + && min_length >= e1000_igp_cable_length_50) { + + for (i = 0; i < IGP01E1000_PHY_CHANNEL_NUM; i++) { + ret_val = + e1000_read_phy_reg(hw, + dsp_reg_array[i], + &phy_data); + if (ret_val) + return ret_val; + + phy_data &= + ~IGP01E1000_PHY_EDAC_MU_INDEX; + + ret_val = + e1000_write_phy_reg(hw, + dsp_reg_array + [i], phy_data); + if (ret_val) + return ret_val; + } + hw->dsp_config_state = + e1000_dsp_config_activated; + } + + if ((hw->ffe_config_state == e1000_ffe_config_enabled) + && (min_length < e1000_igp_cable_length_50)) { + + u16 ffe_idle_err_timeout = + FFE_IDLE_ERR_COUNT_TIMEOUT_20; + u32 idle_errs = 0; + + /* clear previous idle error counts */ + ret_val = + e1000_read_phy_reg(hw, PHY_1000T_STATUS, + &phy_data); + if (ret_val) + return ret_val; + + for (i = 0; i < ffe_idle_err_timeout; i++) { + udelay(1000); + ret_val = + e1000_read_phy_reg(hw, + PHY_1000T_STATUS, + &phy_data); + if (ret_val) + return ret_val; + + idle_errs += + (phy_data & + SR_1000T_IDLE_ERROR_CNT); + if (idle_errs > + SR_1000T_PHY_EXCESSIVE_IDLE_ERR_COUNT) + { + hw->ffe_config_state = + e1000_ffe_config_active; + + ret_val = + e1000_write_phy_reg(hw, + IGP01E1000_PHY_DSP_FFE, + IGP01E1000_PHY_DSP_FFE_CM_CP); + if (ret_val) + return ret_val; + break; + } + + if (idle_errs) + ffe_idle_err_timeout = + FFE_IDLE_ERR_COUNT_TIMEOUT_100; + } + } + } + } else { + if (hw->dsp_config_state == e1000_dsp_config_activated) { + /* Save off the current value of register 0x2F5B to be restored at + * the end of the routines. */ + ret_val = + e1000_read_phy_reg(hw, 0x2F5B, &phy_saved_data); + + if (ret_val) + return ret_val; + + /* Disable the PHY transmitter */ + ret_val = e1000_write_phy_reg(hw, 0x2F5B, 0x0003); + + if (ret_val) + return ret_val; + + mdelay(20); + + ret_val = e1000_write_phy_reg(hw, 0x0000, + IGP01E1000_IEEE_FORCE_GIGA); + if (ret_val) + return ret_val; + for (i = 0; i < IGP01E1000_PHY_CHANNEL_NUM; i++) { + ret_val = + e1000_read_phy_reg(hw, dsp_reg_array[i], + &phy_data); + if (ret_val) + return ret_val; + + phy_data &= ~IGP01E1000_PHY_EDAC_MU_INDEX; + phy_data |= IGP01E1000_PHY_EDAC_SIGN_EXT_9_BITS; + + ret_val = + e1000_write_phy_reg(hw, dsp_reg_array[i], + phy_data); + if (ret_val) + return ret_val; + } + + ret_val = e1000_write_phy_reg(hw, 0x0000, + IGP01E1000_IEEE_RESTART_AUTONEG); + if (ret_val) + return ret_val; + + mdelay(20); + + /* Now enable the transmitter */ + ret_val = + e1000_write_phy_reg(hw, 0x2F5B, phy_saved_data); + + if (ret_val) + return ret_val; + + hw->dsp_config_state = e1000_dsp_config_enabled; + } - /* Set PHY register 30, page 5, bit 8 to 0 */ + if (hw->ffe_config_state == e1000_ffe_config_active) { + /* Save off the current value of register 0x2F5B to be restored at + * the end of the routines. */ + ret_val = + e1000_read_phy_reg(hw, 0x2F5B, &phy_saved_data); - ret_val = e1000_read_phy_reg(hw, M88E1000_PHY_PAGE_SELECT, &default_page); - if (ret_val) - return ret_val; + if (ret_val) + return ret_val; - ret_val = e1000_write_phy_reg(hw, M88E1000_PHY_PAGE_SELECT, 0x0005); - if (ret_val) - return ret_val; + /* Disable the PHY transmitter */ + ret_val = e1000_write_phy_reg(hw, 0x2F5B, 0x0003); - ret_val = e1000_read_phy_reg(hw, M88E1000_PHY_GEN_CONTROL, &phy_data); - if (ret_val) - return ret_val; + if (ret_val) + return ret_val; - phy_data &= ~M88E1000_PHY_VCO_REG_BIT8; - ret_val = e1000_write_phy_reg(hw, M88E1000_PHY_GEN_CONTROL, phy_data); - if (ret_val) - return ret_val; + mdelay(20); - /* Set PHY register 30, page 4, bit 11 to 1 */ + ret_val = e1000_write_phy_reg(hw, 0x0000, + IGP01E1000_IEEE_FORCE_GIGA); + if (ret_val) + return ret_val; + ret_val = + e1000_write_phy_reg(hw, IGP01E1000_PHY_DSP_FFE, + IGP01E1000_PHY_DSP_FFE_DEFAULT); + if (ret_val) + return ret_val; - ret_val = e1000_write_phy_reg(hw, M88E1000_PHY_PAGE_SELECT, 0x0004); - if (ret_val) - return ret_val; + ret_val = e1000_write_phy_reg(hw, 0x0000, + IGP01E1000_IEEE_RESTART_AUTONEG); + if (ret_val) + return ret_val; - ret_val = e1000_read_phy_reg(hw, M88E1000_PHY_GEN_CONTROL, &phy_data); - if (ret_val) - return ret_val; + mdelay(20); - phy_data |= M88E1000_PHY_VCO_REG_BIT11; - ret_val = e1000_write_phy_reg(hw, M88E1000_PHY_GEN_CONTROL, phy_data); - if (ret_val) - return ret_val; + /* Now enable the transmitter */ + ret_val = + e1000_write_phy_reg(hw, 0x2F5B, phy_saved_data); - ret_val = e1000_write_phy_reg(hw, M88E1000_PHY_PAGE_SELECT, default_page); - if (ret_val) - return ret_val; + if (ret_val) + return ret_val; - return E1000_SUCCESS; + hw->ffe_config_state = e1000_ffe_config_enabled; + } + } + return E1000_SUCCESS; } - -/***************************************************************************** - * This function reads the cookie from ARC ram. +/** + * e1000_set_phy_mode - Set PHY to class A mode + * @hw: Struct containing variables accessed by shared code * - * returns: - E1000_SUCCESS . - ****************************************************************************/ -static s32 e1000_host_if_read_cookie(struct e1000_hw *hw, u8 *buffer) + * Assumes the following operations will follow to enable the new class mode. + * 1. Do a PHY soft reset + * 2. Restart auto-negotiation or force link. + */ +static s32 e1000_set_phy_mode(struct e1000_hw *hw) { - u8 i; - u32 offset = E1000_MNG_DHCP_COOKIE_OFFSET; - u8 length = E1000_MNG_DHCP_COOKIE_LENGTH; - - length = (length >> 2); - offset = (offset >> 2); - - for (i = 0; i < length; i++) { - *((u32 *)buffer + i) = - E1000_READ_REG_ARRAY_DWORD(hw, HOST_IF, offset + i); - } - return E1000_SUCCESS; -} + s32 ret_val; + u16 eeprom_data; + DEBUGFUNC("e1000_set_phy_mode"); -/***************************************************************************** - * This function checks whether the HOST IF is enabled for command operaton - * and also checks whether the previous command is completed. - * It busy waits in case of previous command is not completed. - * - * returns: - E1000_ERR_HOST_INTERFACE_COMMAND in case if is not ready or - * timeout - * - E1000_SUCCESS for success. - ****************************************************************************/ -static s32 e1000_mng_enable_host_if(struct e1000_hw *hw) -{ - u32 hicr; - u8 i; - - /* Check that the host interface is enabled. */ - hicr = er32(HICR); - if ((hicr & E1000_HICR_EN) == 0) { - DEBUGOUT("E1000_HOST_EN bit disabled.\n"); - return -E1000_ERR_HOST_INTERFACE_COMMAND; - } - /* check the previous command is completed */ - for (i = 0; i < E1000_MNG_DHCP_COMMAND_TIMEOUT; i++) { - hicr = er32(HICR); - if (!(hicr & E1000_HICR_C)) - break; - mdelay(1); - } - - if (i == E1000_MNG_DHCP_COMMAND_TIMEOUT) { - DEBUGOUT("Previous command timeout failed .\n"); - return -E1000_ERR_HOST_INTERFACE_COMMAND; - } - return E1000_SUCCESS; -} + if ((hw->mac_type == e1000_82545_rev_3) && + (hw->media_type == e1000_media_type_copper)) { + ret_val = + e1000_read_eeprom(hw, EEPROM_PHY_CLASS_WORD, 1, + &eeprom_data); + if (ret_val) { + return ret_val; + } -/***************************************************************************** - * This function writes the buffer content at the offset given on the host if. - * It also does alignment considerations to do the writes in most efficient way. - * Also fills up the sum of the buffer in *buffer parameter. - * - * returns - E1000_SUCCESS for success. - ****************************************************************************/ -static s32 e1000_mng_host_if_write(struct e1000_hw *hw, u8 *buffer, u16 length, - u16 offset, u8 *sum) -{ - u8 *tmp; - u8 *bufptr = buffer; - u32 data = 0; - u16 remaining, i, j, prev_bytes; - - /* sum = only sum of the data and it is not checksum */ - - if (length == 0 || offset + length > E1000_HI_MAX_MNG_DATA_LENGTH) { - return -E1000_ERR_PARAM; - } - - tmp = (u8 *)&data; - prev_bytes = offset & 0x3; - offset &= 0xFFFC; - offset >>= 2; - - if (prev_bytes) { - data = E1000_READ_REG_ARRAY_DWORD(hw, HOST_IF, offset); - for (j = prev_bytes; j < sizeof(u32); j++) { - *(tmp + j) = *bufptr++; - *sum += *(tmp + j); - } - E1000_WRITE_REG_ARRAY_DWORD(hw, HOST_IF, offset, data); - length -= j - prev_bytes; - offset++; - } - - remaining = length & 0x3; - length -= remaining; - - /* Calculate length in DWORDs */ - length >>= 2; - - /* The device driver writes the relevant command block into the - * ram area. */ - for (i = 0; i < length; i++) { - for (j = 0; j < sizeof(u32); j++) { - *(tmp + j) = *bufptr++; - *sum += *(tmp + j); - } - - E1000_WRITE_REG_ARRAY_DWORD(hw, HOST_IF, offset + i, data); - } - if (remaining) { - for (j = 0; j < sizeof(u32); j++) { - if (j < remaining) - *(tmp + j) = *bufptr++; - else - *(tmp + j) = 0; - - *sum += *(tmp + j); - } - E1000_WRITE_REG_ARRAY_DWORD(hw, HOST_IF, offset + i, data); - } - - return E1000_SUCCESS; -} + if ((eeprom_data != EEPROM_RESERVED_WORD) && + (eeprom_data & EEPROM_PHY_CLASS_A)) { + ret_val = + e1000_write_phy_reg(hw, M88E1000_PHY_PAGE_SELECT, + 0x000B); + if (ret_val) + return ret_val; + ret_val = + e1000_write_phy_reg(hw, M88E1000_PHY_GEN_CONTROL, + 0x8104); + if (ret_val) + return ret_val; + + hw->phy_reset_disable = false; + } + } + return E1000_SUCCESS; +} -/***************************************************************************** - * This function writes the command header after does the checksum calculation. +/** + * e1000_set_d3_lplu_state - set d3 link power state + * @hw: Struct containing variables accessed by shared code + * @active: true to enable lplu false to disable lplu. + * + * This function sets the lplu state according to the active flag. When + * activating lplu this function also disables smart speed and vise versa. + * lplu will not be activated unless the device autonegotiation advertisement + * meets standards of either 10 or 10/100 or 10/100/1000 at all duplexes. * - * returns - E1000_SUCCESS for success. - ****************************************************************************/ -static s32 e1000_mng_write_cmd_header(struct e1000_hw *hw, - struct e1000_host_mng_command_header *hdr) + * returns: - E1000_ERR_PHY if fail to read/write the PHY + * E1000_SUCCESS at any other case. + */ +static s32 e1000_set_d3_lplu_state(struct e1000_hw *hw, bool active) { - u16 i; - u8 sum; - u8 *buffer; - - /* Write the whole command header structure which includes sum of - * the buffer */ - - u16 length = sizeof(struct e1000_host_mng_command_header); + s32 ret_val; + u16 phy_data; + DEBUGFUNC("e1000_set_d3_lplu_state"); + + if (hw->phy_type != e1000_phy_igp) + return E1000_SUCCESS; + + /* During driver activity LPLU should not be used or it will attain link + * from the lowest speeds starting from 10Mbps. The capability is used for + * Dx transitions and states */ + if (hw->mac_type == e1000_82541_rev_2 + || hw->mac_type == e1000_82547_rev_2) { + ret_val = + e1000_read_phy_reg(hw, IGP01E1000_GMII_FIFO, &phy_data); + if (ret_val) + return ret_val; + } - sum = hdr->checksum; - hdr->checksum = 0; + if (!active) { + if (hw->mac_type == e1000_82541_rev_2 || + hw->mac_type == e1000_82547_rev_2) { + phy_data &= ~IGP01E1000_GMII_FLEX_SPD; + ret_val = + e1000_write_phy_reg(hw, IGP01E1000_GMII_FIFO, + phy_data); + if (ret_val) + return ret_val; + } - buffer = (u8 *)hdr; - i = length; - while (i--) - sum += buffer[i]; + /* LPLU and SmartSpeed are mutually exclusive. LPLU is used during + * Dx states where the power conservation is most important. During + * driver activity we should enable SmartSpeed, so performance is + * maintained. */ + if (hw->smart_speed == e1000_smart_speed_on) { + ret_val = + e1000_read_phy_reg(hw, IGP01E1000_PHY_PORT_CONFIG, + &phy_data); + if (ret_val) + return ret_val; + + phy_data |= IGP01E1000_PSCFR_SMART_SPEED; + ret_val = + e1000_write_phy_reg(hw, IGP01E1000_PHY_PORT_CONFIG, + phy_data); + if (ret_val) + return ret_val; + } else if (hw->smart_speed == e1000_smart_speed_off) { + ret_val = + e1000_read_phy_reg(hw, IGP01E1000_PHY_PORT_CONFIG, + &phy_data); + if (ret_val) + return ret_val; + + phy_data &= ~IGP01E1000_PSCFR_SMART_SPEED; + ret_val = + e1000_write_phy_reg(hw, IGP01E1000_PHY_PORT_CONFIG, + phy_data); + if (ret_val) + return ret_val; + } + } else if ((hw->autoneg_advertised == AUTONEG_ADVERTISE_SPEED_DEFAULT) + || (hw->autoneg_advertised == AUTONEG_ADVERTISE_10_ALL) + || (hw->autoneg_advertised == + AUTONEG_ADVERTISE_10_100_ALL)) { + + if (hw->mac_type == e1000_82541_rev_2 || + hw->mac_type == e1000_82547_rev_2) { + phy_data |= IGP01E1000_GMII_FLEX_SPD; + ret_val = + e1000_write_phy_reg(hw, IGP01E1000_GMII_FIFO, + phy_data); + if (ret_val) + return ret_val; + } - hdr->checksum = 0 - sum; + /* When LPLU is enabled we should disable SmartSpeed */ + ret_val = + e1000_read_phy_reg(hw, IGP01E1000_PHY_PORT_CONFIG, + &phy_data); + if (ret_val) + return ret_val; - length >>= 2; - /* The device driver writes the relevant command block into the ram area. */ - for (i = 0; i < length; i++) { - E1000_WRITE_REG_ARRAY_DWORD(hw, HOST_IF, i, *((u32 *)hdr + i)); - E1000_WRITE_FLUSH(); - } + phy_data &= ~IGP01E1000_PSCFR_SMART_SPEED; + ret_val = + e1000_write_phy_reg(hw, IGP01E1000_PHY_PORT_CONFIG, + phy_data); + if (ret_val) + return ret_val; - return E1000_SUCCESS; + } + return E1000_SUCCESS; } - -/***************************************************************************** - * This function indicates to ARC that a new command is pending which completes - * one write operation by the driver. +/** + * e1000_set_vco_speed + * @hw: Struct containing variables accessed by shared code * - * returns - E1000_SUCCESS for success. - ****************************************************************************/ -static s32 e1000_mng_write_commit(struct e1000_hw *hw) + * Change VCO speed register to improve Bit Error Rate performance of SERDES. + */ +static s32 e1000_set_vco_speed(struct e1000_hw *hw) { - u32 hicr; + s32 ret_val; + u16 default_page = 0; + u16 phy_data; - hicr = er32(HICR); - /* Setting this bit tells the ARC that a new command is pending. */ - ew32(HICR, hicr | E1000_HICR_C); + DEBUGFUNC("e1000_set_vco_speed"); - return E1000_SUCCESS; -} + switch (hw->mac_type) { + case e1000_82545_rev_3: + case e1000_82546_rev_3: + break; + default: + return E1000_SUCCESS; + } + /* Set PHY register 30, page 5, bit 8 to 0 */ -/***************************************************************************** - * This function checks the mode of the firmware. - * - * returns - true when the mode is IAMT or false. - ****************************************************************************/ -bool e1000_check_mng_mode(struct e1000_hw *hw) -{ - u32 fwsm; - - fwsm = er32(FWSM); + ret_val = + e1000_read_phy_reg(hw, M88E1000_PHY_PAGE_SELECT, &default_page); + if (ret_val) + return ret_val; - if (hw->mac_type == e1000_ich8lan) { - if ((fwsm & E1000_FWSM_MODE_MASK) == - (E1000_MNG_ICH_IAMT_MODE << E1000_FWSM_MODE_SHIFT)) - return true; - } else if ((fwsm & E1000_FWSM_MODE_MASK) == - (E1000_MNG_IAMT_MODE << E1000_FWSM_MODE_SHIFT)) - return true; + ret_val = e1000_write_phy_reg(hw, M88E1000_PHY_PAGE_SELECT, 0x0005); + if (ret_val) + return ret_val; - return false; -} + ret_val = e1000_read_phy_reg(hw, M88E1000_PHY_GEN_CONTROL, &phy_data); + if (ret_val) + return ret_val; + phy_data &= ~M88E1000_PHY_VCO_REG_BIT8; + ret_val = e1000_write_phy_reg(hw, M88E1000_PHY_GEN_CONTROL, phy_data); + if (ret_val) + return ret_val; -/***************************************************************************** - * This function writes the dhcp info . - ****************************************************************************/ -s32 e1000_mng_write_dhcp_info(struct e1000_hw *hw, u8 *buffer, u16 length) -{ - s32 ret_val; - struct e1000_host_mng_command_header hdr; - - hdr.command_id = E1000_MNG_DHCP_TX_PAYLOAD_CMD; - hdr.command_length = length; - hdr.reserved1 = 0; - hdr.reserved2 = 0; - hdr.checksum = 0; - - ret_val = e1000_mng_enable_host_if(hw); - if (ret_val == E1000_SUCCESS) { - ret_val = e1000_mng_host_if_write(hw, buffer, length, sizeof(hdr), - &(hdr.checksum)); - if (ret_val == E1000_SUCCESS) { - ret_val = e1000_mng_write_cmd_header(hw, &hdr); - if (ret_val == E1000_SUCCESS) - ret_val = e1000_mng_write_commit(hw); - } - } - return ret_val; -} + /* Set PHY register 30, page 4, bit 11 to 1 */ + ret_val = e1000_write_phy_reg(hw, M88E1000_PHY_PAGE_SELECT, 0x0004); + if (ret_val) + return ret_val; -/***************************************************************************** - * This function calculates the checksum. - * - * returns - checksum of buffer contents. - ****************************************************************************/ -static u8 e1000_calculate_mng_checksum(char *buffer, u32 length) -{ - u8 sum = 0; - u32 i; + ret_val = e1000_read_phy_reg(hw, M88E1000_PHY_GEN_CONTROL, &phy_data); + if (ret_val) + return ret_val; - if (!buffer) - return 0; + phy_data |= M88E1000_PHY_VCO_REG_BIT11; + ret_val = e1000_write_phy_reg(hw, M88E1000_PHY_GEN_CONTROL, phy_data); + if (ret_val) + return ret_val; - for (i=0; i < length; i++) - sum += buffer[i]; + ret_val = + e1000_write_phy_reg(hw, M88E1000_PHY_PAGE_SELECT, default_page); + if (ret_val) + return ret_val; - return (u8)(0 - sum); + return E1000_SUCCESS; } -/***************************************************************************** - * This function checks whether tx pkt filtering needs to be enabled or not. - * - * returns - true for packet filtering or false. - ****************************************************************************/ -bool e1000_enable_tx_pkt_filtering(struct e1000_hw *hw) -{ - /* called in init as well as watchdog timer functions */ - - s32 ret_val, checksum; - bool tx_filter = false; - struct e1000_host_mng_dhcp_cookie *hdr = &(hw->mng_cookie); - u8 *buffer = (u8 *) &(hw->mng_cookie); - - if (e1000_check_mng_mode(hw)) { - ret_val = e1000_mng_enable_host_if(hw); - if (ret_val == E1000_SUCCESS) { - ret_val = e1000_host_if_read_cookie(hw, buffer); - if (ret_val == E1000_SUCCESS) { - checksum = hdr->checksum; - hdr->checksum = 0; - if ((hdr->signature == E1000_IAMT_SIGNATURE) && - checksum == e1000_calculate_mng_checksum((char *)buffer, - E1000_MNG_DHCP_COOKIE_LENGTH)) { - if (hdr->status & - E1000_MNG_DHCP_COOKIE_STATUS_PARSING_SUPPORT) - tx_filter = true; - } else - tx_filter = true; - } else - tx_filter = true; - } - } - - hw->tx_pkt_filtering = tx_filter; - return tx_filter; -} -/****************************************************************************** - * Verifies the hardware needs to allow ARPs to be processed by the host - * - * hw - Struct containing variables accessed by shared code +/** + * e1000_enable_mng_pass_thru - check for bmc pass through + * @hw: Struct containing variables accessed by shared code * + * Verifies the hardware needs to allow ARPs to be processed by the host * returns: - true/false - * - *****************************************************************************/ + */ u32 e1000_enable_mng_pass_thru(struct e1000_hw *hw) { - u32 manc; - u32 fwsm, factps; - - if (hw->asf_firmware_present) { - manc = er32(MANC); - - if (!(manc & E1000_MANC_RCV_TCO_EN) || - !(manc & E1000_MANC_EN_MAC_ADDR_FILTER)) - return false; - if (e1000_arc_subsystem_valid(hw)) { - fwsm = er32(FWSM); - factps = er32(FACTPS); - - if ((((fwsm & E1000_FWSM_MODE_MASK) >> E1000_FWSM_MODE_SHIFT) == - e1000_mng_mode_pt) && !(factps & E1000_FACTPS_MNGCG)) - return true; - } else - if ((manc & E1000_MANC_SMBUS_EN) && !(manc & E1000_MANC_ASF_EN)) - return true; - } - return false; -} + u32 manc; -static s32 e1000_polarity_reversal_workaround(struct e1000_hw *hw) -{ - s32 ret_val; - u16 mii_status_reg; - u16 i; - - /* Polarity reversal workaround for forced 10F/10H links. */ - - /* Disable the transmitter on the PHY */ - - ret_val = e1000_write_phy_reg(hw, M88E1000_PHY_PAGE_SELECT, 0x0019); - if (ret_val) - return ret_val; - ret_val = e1000_write_phy_reg(hw, M88E1000_PHY_GEN_CONTROL, 0xFFFF); - if (ret_val) - return ret_val; - - ret_val = e1000_write_phy_reg(hw, M88E1000_PHY_PAGE_SELECT, 0x0000); - if (ret_val) - return ret_val; - - /* This loop will early-out if the NO link condition has been met. */ - for (i = PHY_FORCE_TIME; i > 0; i--) { - /* Read the MII Status Register and wait for Link Status bit - * to be clear. - */ - - ret_val = e1000_read_phy_reg(hw, PHY_STATUS, &mii_status_reg); - if (ret_val) - return ret_val; - - ret_val = e1000_read_phy_reg(hw, PHY_STATUS, &mii_status_reg); - if (ret_val) - return ret_val; - - if ((mii_status_reg & ~MII_SR_LINK_STATUS) == 0) break; - mdelay(100); - } - - /* Recommended delay time after link has been lost */ - mdelay(1000); - - /* Now we will re-enable th transmitter on the PHY */ - - ret_val = e1000_write_phy_reg(hw, M88E1000_PHY_PAGE_SELECT, 0x0019); - if (ret_val) - return ret_val; - mdelay(50); - ret_val = e1000_write_phy_reg(hw, M88E1000_PHY_GEN_CONTROL, 0xFFF0); - if (ret_val) - return ret_val; - mdelay(50); - ret_val = e1000_write_phy_reg(hw, M88E1000_PHY_GEN_CONTROL, 0xFF00); - if (ret_val) - return ret_val; - mdelay(50); - ret_val = e1000_write_phy_reg(hw, M88E1000_PHY_GEN_CONTROL, 0x0000); - if (ret_val) - return ret_val; - - ret_val = e1000_write_phy_reg(hw, M88E1000_PHY_PAGE_SELECT, 0x0000); - if (ret_val) - return ret_val; - - /* This loop will early-out if the link condition has been met. */ - for (i = PHY_FORCE_TIME; i > 0; i--) { - /* Read the MII Status Register and wait for Link Status bit - * to be set. - */ - - ret_val = e1000_read_phy_reg(hw, PHY_STATUS, &mii_status_reg); - if (ret_val) - return ret_val; - - ret_val = e1000_read_phy_reg(hw, PHY_STATUS, &mii_status_reg); - if (ret_val) - return ret_val; - - if (mii_status_reg & MII_SR_LINK_STATUS) break; - mdelay(100); - } - return E1000_SUCCESS; + if (hw->asf_firmware_present) { + manc = er32(MANC); + + if (!(manc & E1000_MANC_RCV_TCO_EN) || + !(manc & E1000_MANC_EN_MAC_ADDR_FILTER)) + return false; + if ((manc & E1000_MANC_SMBUS_EN) && !(manc & E1000_MANC_ASF_EN)) + return true; + } + return false; } -/*************************************************************************** - * - * Disables PCI-Express master access. - * - * hw: Struct containing variables accessed by shared code - * - * returns: - none. - * - ***************************************************************************/ -static void e1000_set_pci_express_master_disable(struct e1000_hw *hw) +static s32 e1000_polarity_reversal_workaround(struct e1000_hw *hw) { - u32 ctrl; + s32 ret_val; + u16 mii_status_reg; + u16 i; - DEBUGFUNC("e1000_set_pci_express_master_disable"); + /* Polarity reversal workaround for forced 10F/10H links. */ - if (hw->bus_type != e1000_bus_type_pci_express) - return; + /* Disable the transmitter on the PHY */ - ctrl = er32(CTRL); - ctrl |= E1000_CTRL_GIO_MASTER_DISABLE; - ew32(CTRL, ctrl); -} + ret_val = e1000_write_phy_reg(hw, M88E1000_PHY_PAGE_SELECT, 0x0019); + if (ret_val) + return ret_val; + ret_val = e1000_write_phy_reg(hw, M88E1000_PHY_GEN_CONTROL, 0xFFFF); + if (ret_val) + return ret_val; -/******************************************************************************* - * - * Disables PCI-Express master access and verifies there are no pending requests - * - * hw: Struct containing variables accessed by shared code - * - * returns: - E1000_ERR_MASTER_REQUESTS_PENDING if master disable bit hasn't - * caused the master requests to be disabled. - * E1000_SUCCESS master requests disabled. - * - ******************************************************************************/ -s32 e1000_disable_pciex_master(struct e1000_hw *hw) -{ - s32 timeout = MASTER_DISABLE_TIMEOUT; /* 80ms */ + ret_val = e1000_write_phy_reg(hw, M88E1000_PHY_PAGE_SELECT, 0x0000); + if (ret_val) + return ret_val; - DEBUGFUNC("e1000_disable_pciex_master"); + /* This loop will early-out if the NO link condition has been met. */ + for (i = PHY_FORCE_TIME; i > 0; i--) { + /* Read the MII Status Register and wait for Link Status bit + * to be clear. + */ - if (hw->bus_type != e1000_bus_type_pci_express) - return E1000_SUCCESS; + ret_val = e1000_read_phy_reg(hw, PHY_STATUS, &mii_status_reg); + if (ret_val) + return ret_val; - e1000_set_pci_express_master_disable(hw); + ret_val = e1000_read_phy_reg(hw, PHY_STATUS, &mii_status_reg); + if (ret_val) + return ret_val; - while (timeout) { - if (!(er32(STATUS) & E1000_STATUS_GIO_MASTER_ENABLE)) - break; - else - udelay(100); - timeout--; - } - - if (!timeout) { - DEBUGOUT("Master requests are pending.\n"); - return -E1000_ERR_MASTER_REQUESTS_PENDING; - } + if ((mii_status_reg & ~MII_SR_LINK_STATUS) == 0) + break; + mdelay(100); + } - return E1000_SUCCESS; + /* Recommended delay time after link has been lost */ + mdelay(1000); + + /* Now we will re-enable th transmitter on the PHY */ + + ret_val = e1000_write_phy_reg(hw, M88E1000_PHY_PAGE_SELECT, 0x0019); + if (ret_val) + return ret_val; + mdelay(50); + ret_val = e1000_write_phy_reg(hw, M88E1000_PHY_GEN_CONTROL, 0xFFF0); + if (ret_val) + return ret_val; + mdelay(50); + ret_val = e1000_write_phy_reg(hw, M88E1000_PHY_GEN_CONTROL, 0xFF00); + if (ret_val) + return ret_val; + mdelay(50); + ret_val = e1000_write_phy_reg(hw, M88E1000_PHY_GEN_CONTROL, 0x0000); + if (ret_val) + return ret_val; + + ret_val = e1000_write_phy_reg(hw, M88E1000_PHY_PAGE_SELECT, 0x0000); + if (ret_val) + return ret_val; + + /* This loop will early-out if the link condition has been met. */ + for (i = PHY_FORCE_TIME; i > 0; i--) { + /* Read the MII Status Register and wait for Link Status bit + * to be set. + */ + + ret_val = e1000_read_phy_reg(hw, PHY_STATUS, &mii_status_reg); + if (ret_val) + return ret_val; + + ret_val = e1000_read_phy_reg(hw, PHY_STATUS, &mii_status_reg); + if (ret_val) + return ret_val; + + if (mii_status_reg & MII_SR_LINK_STATUS) + break; + mdelay(100); + } + return E1000_SUCCESS; } -/******************************************************************************* +/** + * e1000_get_auto_rd_done + * @hw: Struct containing variables accessed by shared code * * Check for EEPROM Auto Read bit done. - * - * hw: Struct containing variables accessed by shared code - * * returns: - E1000_ERR_RESET if fail to reset MAC * E1000_SUCCESS at any other case. - * - ******************************************************************************/ + */ static s32 e1000_get_auto_rd_done(struct e1000_hw *hw) { - s32 timeout = AUTO_READ_DONE_TIMEOUT; - - DEBUGFUNC("e1000_get_auto_rd_done"); - - switch (hw->mac_type) { - default: - msleep(5); - break; - case e1000_82571: - case e1000_82572: - case e1000_82573: - case e1000_80003es2lan: - case e1000_ich8lan: - while (timeout) { - if (er32(EECD) & E1000_EECD_AUTO_RD) - break; - else msleep(1); - timeout--; - } - - if (!timeout) { - DEBUGOUT("Auto read by HW from EEPROM has not completed.\n"); - return -E1000_ERR_RESET; - } - break; - } - - /* PHY configuration from NVM just starts after EECD_AUTO_RD sets to high. - * Need to wait for PHY configuration completion before accessing NVM - * and PHY. */ - if (hw->mac_type == e1000_82573) - msleep(25); - - return E1000_SUCCESS; + DEBUGFUNC("e1000_get_auto_rd_done"); + msleep(5); + return E1000_SUCCESS; } -/*************************************************************************** - * Checks if the PHY configuration is done - * - * hw: Struct containing variables accessed by shared code +/** + * e1000_get_phy_cfg_done + * @hw: Struct containing variables accessed by shared code * + * Checks if the PHY configuration is done * returns: - E1000_ERR_RESET if fail to reset MAC * E1000_SUCCESS at any other case. - * - ***************************************************************************/ + */ static s32 e1000_get_phy_cfg_done(struct e1000_hw *hw) { - s32 timeout = PHY_CFG_TIMEOUT; - u32 cfg_mask = E1000_EEPROM_CFG_DONE; - - DEBUGFUNC("e1000_get_phy_cfg_done"); - - switch (hw->mac_type) { - default: - mdelay(10); - break; - case e1000_80003es2lan: - /* Separate *_CFG_DONE_* bit for each port */ - if (er32(STATUS) & E1000_STATUS_FUNC_1) - cfg_mask = E1000_EEPROM_CFG_DONE_PORT_1; - /* Fall Through */ - case e1000_82571: - case e1000_82572: - while (timeout) { - if (er32(EEMNGCTL) & cfg_mask) - break; - else - msleep(1); - timeout--; - } - if (!timeout) { - DEBUGOUT("MNG configuration cycle has not completed.\n"); - return -E1000_ERR_RESET; - } - break; - } - - return E1000_SUCCESS; -} - -/*************************************************************************** - * - * Using the combination of SMBI and SWESMBI semaphore bits when resetting - * adapter or Eeprom access. - * - * hw: Struct containing variables accessed by shared code - * - * returns: - E1000_ERR_EEPROM if fail to access EEPROM. - * E1000_SUCCESS at any other case. - * - ***************************************************************************/ -static s32 e1000_get_hw_eeprom_semaphore(struct e1000_hw *hw) -{ - s32 timeout; - u32 swsm; - - DEBUGFUNC("e1000_get_hw_eeprom_semaphore"); - - if (!hw->eeprom_semaphore_present) - return E1000_SUCCESS; - - if (hw->mac_type == e1000_80003es2lan) { - /* Get the SW semaphore. */ - if (e1000_get_software_semaphore(hw) != E1000_SUCCESS) - return -E1000_ERR_EEPROM; - } - - /* Get the FW semaphore. */ - timeout = hw->eeprom.word_size + 1; - while (timeout) { - swsm = er32(SWSM); - swsm |= E1000_SWSM_SWESMBI; - ew32(SWSM, swsm); - /* if we managed to set the bit we got the semaphore. */ - swsm = er32(SWSM); - if (swsm & E1000_SWSM_SWESMBI) - break; - - udelay(50); - timeout--; - } - - if (!timeout) { - /* Release semaphores */ - e1000_put_hw_eeprom_semaphore(hw); - DEBUGOUT("Driver can't access the Eeprom - SWESMBI bit is set.\n"); - return -E1000_ERR_EEPROM; - } - - return E1000_SUCCESS; -} - -/*************************************************************************** - * This function clears HW semaphore bits. - * - * hw: Struct containing variables accessed by shared code - * - * returns: - None. - * - ***************************************************************************/ -static void e1000_put_hw_eeprom_semaphore(struct e1000_hw *hw) -{ - u32 swsm; - - DEBUGFUNC("e1000_put_hw_eeprom_semaphore"); - - if (!hw->eeprom_semaphore_present) - return; - - swsm = er32(SWSM); - if (hw->mac_type == e1000_80003es2lan) { - /* Release both semaphores. */ - swsm &= ~(E1000_SWSM_SMBI | E1000_SWSM_SWESMBI); - } else - swsm &= ~(E1000_SWSM_SWESMBI); - ew32(SWSM, swsm); -} - -/*************************************************************************** - * - * Obtaining software semaphore bit (SMBI) before resetting PHY. - * - * hw: Struct containing variables accessed by shared code - * - * returns: - E1000_ERR_RESET if fail to obtain semaphore. - * E1000_SUCCESS at any other case. - * - ***************************************************************************/ -static s32 e1000_get_software_semaphore(struct e1000_hw *hw) -{ - s32 timeout = hw->eeprom.word_size + 1; - u32 swsm; - - DEBUGFUNC("e1000_get_software_semaphore"); - - if (hw->mac_type != e1000_80003es2lan) { - return E1000_SUCCESS; - } - - while (timeout) { - swsm = er32(SWSM); - /* If SMBI bit cleared, it is now set and we hold the semaphore */ - if (!(swsm & E1000_SWSM_SMBI)) - break; - mdelay(1); - timeout--; - } - - if (!timeout) { - DEBUGOUT("Driver can't access device - SMBI bit is set.\n"); - return -E1000_ERR_RESET; - } - - return E1000_SUCCESS; -} - -/*************************************************************************** - * - * Release semaphore bit (SMBI). - * - * hw: Struct containing variables accessed by shared code - * - ***************************************************************************/ -static void e1000_release_software_semaphore(struct e1000_hw *hw) -{ - u32 swsm; - - DEBUGFUNC("e1000_release_software_semaphore"); - - if (hw->mac_type != e1000_80003es2lan) { - return; - } - - swsm = er32(SWSM); - /* Release the SW semaphores.*/ - swsm &= ~E1000_SWSM_SMBI; - ew32(SWSM, swsm); -} - -/****************************************************************************** - * Checks if PHY reset is blocked due to SOL/IDER session, for example. - * Returning E1000_BLK_PHY_RESET isn't necessarily an error. But it's up to - * the caller to figure out how to deal with it. - * - * hw - Struct containing variables accessed by shared code - * - * returns: - E1000_BLK_PHY_RESET - * E1000_SUCCESS - * - *****************************************************************************/ -s32 e1000_check_phy_reset_block(struct e1000_hw *hw) -{ - u32 manc = 0; - u32 fwsm = 0; - - if (hw->mac_type == e1000_ich8lan) { - fwsm = er32(FWSM); - return (fwsm & E1000_FWSM_RSPCIPHY) ? E1000_SUCCESS - : E1000_BLK_PHY_RESET; - } - - if (hw->mac_type > e1000_82547_rev_2) - manc = er32(MANC); - return (manc & E1000_MANC_BLK_PHY_RST_ON_IDE) ? - E1000_BLK_PHY_RESET : E1000_SUCCESS; -} - -static u8 e1000_arc_subsystem_valid(struct e1000_hw *hw) -{ - u32 fwsm; - - /* On 8257x silicon, registers in the range of 0x8800 - 0x8FFC - * may not be provided a DMA clock when no manageability features are - * enabled. We do not want to perform any reads/writes to these registers - * if this is the case. We read FWSM to determine the manageability mode. - */ - switch (hw->mac_type) { - case e1000_82571: - case e1000_82572: - case e1000_82573: - case e1000_80003es2lan: - fwsm = er32(FWSM); - if ((fwsm & E1000_FWSM_MODE_MASK) != 0) - return true; - break; - case e1000_ich8lan: - return true; - default: - break; - } - return false; -} - - -/****************************************************************************** - * Configure PCI-Ex no-snoop - * - * hw - Struct containing variables accessed by shared code. - * no_snoop - Bitmap of no-snoop events. - * - * returns: E1000_SUCCESS - * - *****************************************************************************/ -static s32 e1000_set_pci_ex_no_snoop(struct e1000_hw *hw, u32 no_snoop) -{ - u32 gcr_reg = 0; - - DEBUGFUNC("e1000_set_pci_ex_no_snoop"); - - if (hw->bus_type == e1000_bus_type_unknown) - e1000_get_bus_info(hw); - - if (hw->bus_type != e1000_bus_type_pci_express) - return E1000_SUCCESS; - - if (no_snoop) { - gcr_reg = er32(GCR); - gcr_reg &= ~(PCI_EX_NO_SNOOP_ALL); - gcr_reg |= no_snoop; - ew32(GCR, gcr_reg); - } - if (hw->mac_type == e1000_ich8lan) { - u32 ctrl_ext; - - ew32(GCR, PCI_EX_82566_SNOOP_ALL); - - ctrl_ext = er32(CTRL_EXT); - ctrl_ext |= E1000_CTRL_EXT_RO_DIS; - ew32(CTRL_EXT, ctrl_ext); - } - - return E1000_SUCCESS; -} - -/*************************************************************************** - * - * Get software semaphore FLAG bit (SWFLAG). - * SWFLAG is used to synchronize the access to all shared resource between - * SW, FW and HW. - * - * hw: Struct containing variables accessed by shared code - * - ***************************************************************************/ -static s32 e1000_get_software_flag(struct e1000_hw *hw) -{ - s32 timeout = PHY_CFG_TIMEOUT; - u32 extcnf_ctrl; - - DEBUGFUNC("e1000_get_software_flag"); - - if (hw->mac_type == e1000_ich8lan) { - while (timeout) { - extcnf_ctrl = er32(EXTCNF_CTRL); - extcnf_ctrl |= E1000_EXTCNF_CTRL_SWFLAG; - ew32(EXTCNF_CTRL, extcnf_ctrl); - - extcnf_ctrl = er32(EXTCNF_CTRL); - if (extcnf_ctrl & E1000_EXTCNF_CTRL_SWFLAG) - break; - mdelay(1); - timeout--; - } - - if (!timeout) { - DEBUGOUT("FW or HW locks the resource too long.\n"); - return -E1000_ERR_CONFIG; - } - } - - return E1000_SUCCESS; -} - -/*************************************************************************** - * - * Release software semaphore FLAG bit (SWFLAG). - * SWFLAG is used to synchronize the access to all shared resource between - * SW, FW and HW. - * - * hw: Struct containing variables accessed by shared code - * - ***************************************************************************/ -static void e1000_release_software_flag(struct e1000_hw *hw) -{ - u32 extcnf_ctrl; - - DEBUGFUNC("e1000_release_software_flag"); - - if (hw->mac_type == e1000_ich8lan) { - extcnf_ctrl= er32(EXTCNF_CTRL); - extcnf_ctrl &= ~E1000_EXTCNF_CTRL_SWFLAG; - ew32(EXTCNF_CTRL, extcnf_ctrl); - } - - return; -} - -/****************************************************************************** - * Reads a 16 bit word or words from the EEPROM using the ICH8's flash access - * register. - * - * hw - Struct containing variables accessed by shared code - * offset - offset of word in the EEPROM to read - * data - word read from the EEPROM - * words - number of words to read - *****************************************************************************/ -static s32 e1000_read_eeprom_ich8(struct e1000_hw *hw, u16 offset, u16 words, - u16 *data) -{ - s32 error = E1000_SUCCESS; - u32 flash_bank = 0; - u32 act_offset = 0; - u32 bank_offset = 0; - u16 word = 0; - u16 i = 0; - - /* We need to know which is the valid flash bank. In the event - * that we didn't allocate eeprom_shadow_ram, we may not be - * managing flash_bank. So it cannot be trusted and needs - * to be updated with each read. - */ - /* Value of bit 22 corresponds to the flash bank we're on. */ - flash_bank = (er32(EECD) & E1000_EECD_SEC1VAL) ? 1 : 0; - - /* Adjust offset appropriately if we're on bank 1 - adjust for word size */ - bank_offset = flash_bank * (hw->flash_bank_size * 2); - - error = e1000_get_software_flag(hw); - if (error != E1000_SUCCESS) - return error; - - for (i = 0; i < words; i++) { - if (hw->eeprom_shadow_ram != NULL && - hw->eeprom_shadow_ram[offset+i].modified) { - data[i] = hw->eeprom_shadow_ram[offset+i].eeprom_word; - } else { - /* The NVM part needs a byte offset, hence * 2 */ - act_offset = bank_offset + ((offset + i) * 2); - error = e1000_read_ich8_word(hw, act_offset, &word); - if (error != E1000_SUCCESS) - break; - data[i] = word; - } - } - - e1000_release_software_flag(hw); - - return error; -} - -/****************************************************************************** - * Writes a 16 bit word or words to the EEPROM using the ICH8's flash access - * register. Actually, writes are written to the shadow ram cache in the hw - * structure hw->e1000_shadow_ram. e1000_commit_shadow_ram flushes this to - * the NVM, which occurs when the NVM checksum is updated. - * - * hw - Struct containing variables accessed by shared code - * offset - offset of word in the EEPROM to write - * words - number of words to write - * data - words to write to the EEPROM - *****************************************************************************/ -static s32 e1000_write_eeprom_ich8(struct e1000_hw *hw, u16 offset, u16 words, - u16 *data) -{ - u32 i = 0; - s32 error = E1000_SUCCESS; - - error = e1000_get_software_flag(hw); - if (error != E1000_SUCCESS) - return error; - - /* A driver can write to the NVM only if it has eeprom_shadow_ram - * allocated. Subsequent reads to the modified words are read from - * this cached structure as well. Writes will only go into this - * cached structure unless it's followed by a call to - * e1000_update_eeprom_checksum() where it will commit the changes - * and clear the "modified" field. - */ - if (hw->eeprom_shadow_ram != NULL) { - for (i = 0; i < words; i++) { - if ((offset + i) < E1000_SHADOW_RAM_WORDS) { - hw->eeprom_shadow_ram[offset+i].modified = true; - hw->eeprom_shadow_ram[offset+i].eeprom_word = data[i]; - } else { - error = -E1000_ERR_EEPROM; - break; - } - } - } else { - /* Drivers have the option to not allocate eeprom_shadow_ram as long - * as they don't perform any NVM writes. An attempt in doing so - * will result in this error. - */ - error = -E1000_ERR_EEPROM; - } - - e1000_release_software_flag(hw); - - return error; -} - -/****************************************************************************** - * This function does initial flash setup so that a new read/write/erase cycle - * can be started. - * - * hw - The pointer to the hw structure - ****************************************************************************/ -static s32 e1000_ich8_cycle_init(struct e1000_hw *hw) -{ - union ich8_hws_flash_status hsfsts; - s32 error = E1000_ERR_EEPROM; - s32 i = 0; - - DEBUGFUNC("e1000_ich8_cycle_init"); - - hsfsts.regval = E1000_READ_ICH_FLASH_REG16(hw, ICH_FLASH_HSFSTS); - - /* May be check the Flash Des Valid bit in Hw status */ - if (hsfsts.hsf_status.fldesvalid == 0) { - DEBUGOUT("Flash descriptor invalid. SW Sequencing must be used."); - return error; - } - - /* Clear FCERR in Hw status by writing 1 */ - /* Clear DAEL in Hw status by writing a 1 */ - hsfsts.hsf_status.flcerr = 1; - hsfsts.hsf_status.dael = 1; - - E1000_WRITE_ICH_FLASH_REG16(hw, ICH_FLASH_HSFSTS, hsfsts.regval); - - /* Either we should have a hardware SPI cycle in progress bit to check - * against, in order to start a new cycle or FDONE bit should be changed - * in the hardware so that it is 1 after harware reset, which can then be - * used as an indication whether a cycle is in progress or has been - * completed .. we should also have some software semaphore mechanism to - * guard FDONE or the cycle in progress bit so that two threads access to - * those bits can be sequentiallized or a way so that 2 threads dont - * start the cycle at the same time */ - - if (hsfsts.hsf_status.flcinprog == 0) { - /* There is no cycle running at present, so we can start a cycle */ - /* Begin by setting Flash Cycle Done. */ - hsfsts.hsf_status.flcdone = 1; - E1000_WRITE_ICH_FLASH_REG16(hw, ICH_FLASH_HSFSTS, hsfsts.regval); - error = E1000_SUCCESS; - } else { - /* otherwise poll for sometime so the current cycle has a chance - * to end before giving up. */ - for (i = 0; i < ICH_FLASH_COMMAND_TIMEOUT; i++) { - hsfsts.regval = E1000_READ_ICH_FLASH_REG16(hw, ICH_FLASH_HSFSTS); - if (hsfsts.hsf_status.flcinprog == 0) { - error = E1000_SUCCESS; - break; - } - udelay(1); - } - if (error == E1000_SUCCESS) { - /* Successful in waiting for previous cycle to timeout, - * now set the Flash Cycle Done. */ - hsfsts.hsf_status.flcdone = 1; - E1000_WRITE_ICH_FLASH_REG16(hw, ICH_FLASH_HSFSTS, hsfsts.regval); - } else { - DEBUGOUT("Flash controller busy, cannot get access"); - } - } - return error; -} - -/****************************************************************************** - * This function starts a flash cycle and waits for its completion - * - * hw - The pointer to the hw structure - ****************************************************************************/ -static s32 e1000_ich8_flash_cycle(struct e1000_hw *hw, u32 timeout) -{ - union ich8_hws_flash_ctrl hsflctl; - union ich8_hws_flash_status hsfsts; - s32 error = E1000_ERR_EEPROM; - u32 i = 0; - - /* Start a cycle by writing 1 in Flash Cycle Go in Hw Flash Control */ - hsflctl.regval = E1000_READ_ICH_FLASH_REG16(hw, ICH_FLASH_HSFCTL); - hsflctl.hsf_ctrl.flcgo = 1; - E1000_WRITE_ICH_FLASH_REG16(hw, ICH_FLASH_HSFCTL, hsflctl.regval); - - /* wait till FDONE bit is set to 1 */ - do { - hsfsts.regval = E1000_READ_ICH_FLASH_REG16(hw, ICH_FLASH_HSFSTS); - if (hsfsts.hsf_status.flcdone == 1) - break; - udelay(1); - i++; - } while (i < timeout); - if (hsfsts.hsf_status.flcdone == 1 && hsfsts.hsf_status.flcerr == 0) { - error = E1000_SUCCESS; - } - return error; -} - -/****************************************************************************** - * Reads a byte or word from the NVM using the ICH8 flash access registers. - * - * hw - The pointer to the hw structure - * index - The index of the byte or word to read. - * size - Size of data to read, 1=byte 2=word - * data - Pointer to the word to store the value read. - *****************************************************************************/ -static s32 e1000_read_ich8_data(struct e1000_hw *hw, u32 index, u32 size, - u16 *data) -{ - union ich8_hws_flash_status hsfsts; - union ich8_hws_flash_ctrl hsflctl; - u32 flash_linear_address; - u32 flash_data = 0; - s32 error = -E1000_ERR_EEPROM; - s32 count = 0; - - DEBUGFUNC("e1000_read_ich8_data"); - - if (size < 1 || size > 2 || data == NULL || - index > ICH_FLASH_LINEAR_ADDR_MASK) - return error; - - flash_linear_address = (ICH_FLASH_LINEAR_ADDR_MASK & index) + - hw->flash_base_addr; - - do { - udelay(1); - /* Steps */ - error = e1000_ich8_cycle_init(hw); - if (error != E1000_SUCCESS) - break; - - hsflctl.regval = E1000_READ_ICH_FLASH_REG16(hw, ICH_FLASH_HSFCTL); - /* 0b/1b corresponds to 1 or 2 byte size, respectively. */ - hsflctl.hsf_ctrl.fldbcount = size - 1; - hsflctl.hsf_ctrl.flcycle = ICH_CYCLE_READ; - E1000_WRITE_ICH_FLASH_REG16(hw, ICH_FLASH_HSFCTL, hsflctl.regval); - - /* Write the last 24 bits of index into Flash Linear address field in - * Flash Address */ - /* TODO: TBD maybe check the index against the size of flash */ - - E1000_WRITE_ICH_FLASH_REG(hw, ICH_FLASH_FADDR, flash_linear_address); - - error = e1000_ich8_flash_cycle(hw, ICH_FLASH_COMMAND_TIMEOUT); - - /* Check if FCERR is set to 1, if set to 1, clear it and try the whole - * sequence a few more times, else read in (shift in) the Flash Data0, - * the order is least significant byte first msb to lsb */ - if (error == E1000_SUCCESS) { - flash_data = E1000_READ_ICH_FLASH_REG(hw, ICH_FLASH_FDATA0); - if (size == 1) { - *data = (u8)(flash_data & 0x000000FF); - } else if (size == 2) { - *data = (u16)(flash_data & 0x0000FFFF); - } - break; - } else { - /* If we've gotten here, then things are probably completely hosed, - * but if the error condition is detected, it won't hurt to give - * it another try...ICH_FLASH_CYCLE_REPEAT_COUNT times. - */ - hsfsts.regval = E1000_READ_ICH_FLASH_REG16(hw, ICH_FLASH_HSFSTS); - if (hsfsts.hsf_status.flcerr == 1) { - /* Repeat for some time before giving up. */ - continue; - } else if (hsfsts.hsf_status.flcdone == 0) { - DEBUGOUT("Timeout error - flash cycle did not complete."); - break; - } - } - } while (count++ < ICH_FLASH_CYCLE_REPEAT_COUNT); - - return error; -} - -/****************************************************************************** - * Writes One /two bytes to the NVM using the ICH8 flash access registers. - * - * hw - The pointer to the hw structure - * index - The index of the byte/word to read. - * size - Size of data to read, 1=byte 2=word - * data - The byte(s) to write to the NVM. - *****************************************************************************/ -static s32 e1000_write_ich8_data(struct e1000_hw *hw, u32 index, u32 size, - u16 data) -{ - union ich8_hws_flash_status hsfsts; - union ich8_hws_flash_ctrl hsflctl; - u32 flash_linear_address; - u32 flash_data = 0; - s32 error = -E1000_ERR_EEPROM; - s32 count = 0; - - DEBUGFUNC("e1000_write_ich8_data"); - - if (size < 1 || size > 2 || data > size * 0xff || - index > ICH_FLASH_LINEAR_ADDR_MASK) - return error; - - flash_linear_address = (ICH_FLASH_LINEAR_ADDR_MASK & index) + - hw->flash_base_addr; - - do { - udelay(1); - /* Steps */ - error = e1000_ich8_cycle_init(hw); - if (error != E1000_SUCCESS) - break; - - hsflctl.regval = E1000_READ_ICH_FLASH_REG16(hw, ICH_FLASH_HSFCTL); - /* 0b/1b corresponds to 1 or 2 byte size, respectively. */ - hsflctl.hsf_ctrl.fldbcount = size -1; - hsflctl.hsf_ctrl.flcycle = ICH_CYCLE_WRITE; - E1000_WRITE_ICH_FLASH_REG16(hw, ICH_FLASH_HSFCTL, hsflctl.regval); - - /* Write the last 24 bits of index into Flash Linear address field in - * Flash Address */ - E1000_WRITE_ICH_FLASH_REG(hw, ICH_FLASH_FADDR, flash_linear_address); - - if (size == 1) - flash_data = (u32)data & 0x00FF; - else - flash_data = (u32)data; - - E1000_WRITE_ICH_FLASH_REG(hw, ICH_FLASH_FDATA0, flash_data); - - /* check if FCERR is set to 1 , if set to 1, clear it and try the whole - * sequence a few more times else done */ - error = e1000_ich8_flash_cycle(hw, ICH_FLASH_COMMAND_TIMEOUT); - if (error == E1000_SUCCESS) { - break; - } else { - /* If we're here, then things are most likely completely hosed, - * but if the error condition is detected, it won't hurt to give - * it another try...ICH_FLASH_CYCLE_REPEAT_COUNT times. - */ - hsfsts.regval = E1000_READ_ICH_FLASH_REG16(hw, ICH_FLASH_HSFSTS); - if (hsfsts.hsf_status.flcerr == 1) { - /* Repeat for some time before giving up. */ - continue; - } else if (hsfsts.hsf_status.flcdone == 0) { - DEBUGOUT("Timeout error - flash cycle did not complete."); - break; - } - } - } while (count++ < ICH_FLASH_CYCLE_REPEAT_COUNT); - - return error; -} - -/****************************************************************************** - * Reads a single byte from the NVM using the ICH8 flash access registers. - * - * hw - pointer to e1000_hw structure - * index - The index of the byte to read. - * data - Pointer to a byte to store the value read. - *****************************************************************************/ -static s32 e1000_read_ich8_byte(struct e1000_hw *hw, u32 index, u8 *data) -{ - s32 status = E1000_SUCCESS; - u16 word = 0; - - status = e1000_read_ich8_data(hw, index, 1, &word); - if (status == E1000_SUCCESS) { - *data = (u8)word; - } - - return status; -} - -/****************************************************************************** - * Writes a single byte to the NVM using the ICH8 flash access registers. - * Performs verification by reading back the value and then going through - * a retry algorithm before giving up. - * - * hw - pointer to e1000_hw structure - * index - The index of the byte to write. - * byte - The byte to write to the NVM. - *****************************************************************************/ -static s32 e1000_verify_write_ich8_byte(struct e1000_hw *hw, u32 index, u8 byte) -{ - s32 error = E1000_SUCCESS; - s32 program_retries = 0; - - DEBUGOUT2("Byte := %2.2X Offset := %d\n", byte, index); - - error = e1000_write_ich8_byte(hw, index, byte); - - if (error != E1000_SUCCESS) { - for (program_retries = 0; program_retries < 100; program_retries++) { - DEBUGOUT2("Retrying \t Byte := %2.2X Offset := %d\n", byte, index); - error = e1000_write_ich8_byte(hw, index, byte); - udelay(100); - if (error == E1000_SUCCESS) - break; - } - } - - if (program_retries == 100) - error = E1000_ERR_EEPROM; - - return error; -} - -/****************************************************************************** - * Writes a single byte to the NVM using the ICH8 flash access registers. - * - * hw - pointer to e1000_hw structure - * index - The index of the byte to read. - * data - The byte to write to the NVM. - *****************************************************************************/ -static s32 e1000_write_ich8_byte(struct e1000_hw *hw, u32 index, u8 data) -{ - s32 status = E1000_SUCCESS; - u16 word = (u16)data; - - status = e1000_write_ich8_data(hw, index, 1, word); - - return status; -} - -/****************************************************************************** - * Reads a word from the NVM using the ICH8 flash access registers. - * - * hw - pointer to e1000_hw structure - * index - The starting byte index of the word to read. - * data - Pointer to a word to store the value read. - *****************************************************************************/ -static s32 e1000_read_ich8_word(struct e1000_hw *hw, u32 index, u16 *data) -{ - s32 status = E1000_SUCCESS; - status = e1000_read_ich8_data(hw, index, 2, data); - return status; -} - -/****************************************************************************** - * Erases the bank specified. Each bank may be a 4, 8 or 64k block. Banks are 0 - * based. - * - * hw - pointer to e1000_hw structure - * bank - 0 for first bank, 1 for second bank - * - * Note that this function may actually erase as much as 8 or 64 KBytes. The - * amount of NVM used in each bank is a *minimum* of 4 KBytes, but in fact the - * bank size may be 4, 8 or 64 KBytes - *****************************************************************************/ -static s32 e1000_erase_ich8_4k_segment(struct e1000_hw *hw, u32 bank) -{ - union ich8_hws_flash_status hsfsts; - union ich8_hws_flash_ctrl hsflctl; - u32 flash_linear_address; - s32 count = 0; - s32 error = E1000_ERR_EEPROM; - s32 iteration; - s32 sub_sector_size = 0; - s32 bank_size; - s32 j = 0; - s32 error_flag = 0; - - hsfsts.regval = E1000_READ_ICH_FLASH_REG16(hw, ICH_FLASH_HSFSTS); - - /* Determine HW Sector size: Read BERASE bits of Hw flash Status register */ - /* 00: The Hw sector is 256 bytes, hence we need to erase 16 - * consecutive sectors. The start index for the nth Hw sector can be - * calculated as bank * 4096 + n * 256 - * 01: The Hw sector is 4K bytes, hence we need to erase 1 sector. - * The start index for the nth Hw sector can be calculated - * as bank * 4096 - * 10: The HW sector is 8K bytes - * 11: The Hw sector size is 64K bytes */ - if (hsfsts.hsf_status.berasesz == 0x0) { - /* Hw sector size 256 */ - sub_sector_size = ICH_FLASH_SEG_SIZE_256; - bank_size = ICH_FLASH_SECTOR_SIZE; - iteration = ICH_FLASH_SECTOR_SIZE / ICH_FLASH_SEG_SIZE_256; - } else if (hsfsts.hsf_status.berasesz == 0x1) { - bank_size = ICH_FLASH_SEG_SIZE_4K; - iteration = 1; - } else if (hsfsts.hsf_status.berasesz == 0x3) { - bank_size = ICH_FLASH_SEG_SIZE_64K; - iteration = 1; - } else { - return error; - } - - for (j = 0; j < iteration ; j++) { - do { - count++; - /* Steps */ - error = e1000_ich8_cycle_init(hw); - if (error != E1000_SUCCESS) { - error_flag = 1; - break; - } - - /* Write a value 11 (block Erase) in Flash Cycle field in Hw flash - * Control */ - hsflctl.regval = E1000_READ_ICH_FLASH_REG16(hw, ICH_FLASH_HSFCTL); - hsflctl.hsf_ctrl.flcycle = ICH_CYCLE_ERASE; - E1000_WRITE_ICH_FLASH_REG16(hw, ICH_FLASH_HSFCTL, hsflctl.regval); - - /* Write the last 24 bits of an index within the block into Flash - * Linear address field in Flash Address. This probably needs to - * be calculated here based off the on-chip erase sector size and - * the software bank size (4, 8 or 64 KBytes) */ - flash_linear_address = bank * bank_size + j * sub_sector_size; - flash_linear_address += hw->flash_base_addr; - flash_linear_address &= ICH_FLASH_LINEAR_ADDR_MASK; - - E1000_WRITE_ICH_FLASH_REG(hw, ICH_FLASH_FADDR, flash_linear_address); - - error = e1000_ich8_flash_cycle(hw, ICH_FLASH_ERASE_TIMEOUT); - /* Check if FCERR is set to 1. If 1, clear it and try the whole - * sequence a few more times else Done */ - if (error == E1000_SUCCESS) { - break; - } else { - hsfsts.regval = E1000_READ_ICH_FLASH_REG16(hw, ICH_FLASH_HSFSTS); - if (hsfsts.hsf_status.flcerr == 1) { - /* repeat for some time before giving up */ - continue; - } else if (hsfsts.hsf_status.flcdone == 0) { - error_flag = 1; - break; - } - } - } while ((count < ICH_FLASH_CYCLE_REPEAT_COUNT) && !error_flag); - if (error_flag == 1) - break; - } - if (error_flag != 1) - error = E1000_SUCCESS; - return error; -} - -static s32 e1000_init_lcd_from_nvm_config_region(struct e1000_hw *hw, - u32 cnf_base_addr, - u32 cnf_size) -{ - u32 ret_val = E1000_SUCCESS; - u16 word_addr, reg_data, reg_addr; - u16 i; - - /* cnf_base_addr is in DWORD */ - word_addr = (u16)(cnf_base_addr << 1); - - /* cnf_size is returned in size of dwords */ - for (i = 0; i < cnf_size; i++) { - ret_val = e1000_read_eeprom(hw, (word_addr + i*2), 1, ®_data); - if (ret_val) - return ret_val; - - ret_val = e1000_read_eeprom(hw, (word_addr + i*2 + 1), 1, ®_addr); - if (ret_val) - return ret_val; - - ret_val = e1000_get_software_flag(hw); - if (ret_val != E1000_SUCCESS) - return ret_val; - - ret_val = e1000_write_phy_reg_ex(hw, (u32)reg_addr, reg_data); - - e1000_release_software_flag(hw); - } - - return ret_val; -} - - -/****************************************************************************** - * This function initializes the PHY from the NVM on ICH8 platforms. This - * is needed due to an issue where the NVM configuration is not properly - * autoloaded after power transitions. Therefore, after each PHY reset, we - * will load the configuration data out of the NVM manually. - * - * hw: Struct containing variables accessed by shared code - *****************************************************************************/ -static s32 e1000_init_lcd_from_nvm(struct e1000_hw *hw) -{ - u32 reg_data, cnf_base_addr, cnf_size, ret_val, loop; - - if (hw->phy_type != e1000_phy_igp_3) - return E1000_SUCCESS; - - /* Check if SW needs configure the PHY */ - reg_data = er32(FEXTNVM); - if (!(reg_data & FEXTNVM_SW_CONFIG)) - return E1000_SUCCESS; - - /* Wait for basic configuration completes before proceeding*/ - loop = 0; - do { - reg_data = er32(STATUS) & E1000_STATUS_LAN_INIT_DONE; - udelay(100); - loop++; - } while ((!reg_data) && (loop < 50)); - - /* Clear the Init Done bit for the next init event */ - reg_data = er32(STATUS); - reg_data &= ~E1000_STATUS_LAN_INIT_DONE; - ew32(STATUS, reg_data); - - /* Make sure HW does not configure LCD from PHY extended configuration - before SW configuration */ - reg_data = er32(EXTCNF_CTRL); - if ((reg_data & E1000_EXTCNF_CTRL_LCD_WRITE_ENABLE) == 0x0000) { - reg_data = er32(EXTCNF_SIZE); - cnf_size = reg_data & E1000_EXTCNF_SIZE_EXT_PCIE_LENGTH; - cnf_size >>= 16; - if (cnf_size) { - reg_data = er32(EXTCNF_CTRL); - cnf_base_addr = reg_data & E1000_EXTCNF_CTRL_EXT_CNF_POINTER; - /* cnf_base_addr is in DWORD */ - cnf_base_addr >>= 16; - - /* Configure LCD from extended configuration region. */ - ret_val = e1000_init_lcd_from_nvm_config_region(hw, cnf_base_addr, - cnf_size); - if (ret_val) - return ret_val; - } - } - - return E1000_SUCCESS; + DEBUGFUNC("e1000_get_phy_cfg_done"); + mdelay(10); + return E1000_SUCCESS; } - diff --git a/drivers/net/e1000/e1000_hw.h b/drivers/net/e1000/e1000_hw.h index a8866bdbb671..9acfddb0dafb 100644 --- a/drivers/net/e1000/e1000_hw.h +++ b/drivers/net/e1000/e1000_hw.h @@ -35,7 +35,6 @@ #include "e1000_osdep.h" - /* Forward declarations of structures used by the shared code */ struct e1000_hw; struct e1000_hw_stats; @@ -43,252 +42,231 @@ struct e1000_hw_stats; /* Enumerated types specific to the e1000 hardware */ /* Media Access Controlers */ typedef enum { - e1000_undefined = 0, - e1000_82542_rev2_0, - e1000_82542_rev2_1, - e1000_82543, - e1000_82544, - e1000_82540, - e1000_82545, - e1000_82545_rev_3, - e1000_82546, - e1000_82546_rev_3, - e1000_82541, - e1000_82541_rev_2, - e1000_82547, - e1000_82547_rev_2, - e1000_82571, - e1000_82572, - e1000_82573, - e1000_80003es2lan, - e1000_ich8lan, - e1000_num_macs + e1000_undefined = 0, + e1000_82542_rev2_0, + e1000_82542_rev2_1, + e1000_82543, + e1000_82544, + e1000_82540, + e1000_82545, + e1000_82545_rev_3, + e1000_82546, + e1000_82546_rev_3, + e1000_82541, + e1000_82541_rev_2, + e1000_82547, + e1000_82547_rev_2, + e1000_num_macs } e1000_mac_type; typedef enum { - e1000_eeprom_uninitialized = 0, - e1000_eeprom_spi, - e1000_eeprom_microwire, - e1000_eeprom_flash, - e1000_eeprom_ich8, - e1000_eeprom_none, /* No NVM support */ - e1000_num_eeprom_types + e1000_eeprom_uninitialized = 0, + e1000_eeprom_spi, + e1000_eeprom_microwire, + e1000_eeprom_flash, + e1000_eeprom_none, /* No NVM support */ + e1000_num_eeprom_types } e1000_eeprom_type; /* Media Types */ typedef enum { - e1000_media_type_copper = 0, - e1000_media_type_fiber = 1, - e1000_media_type_internal_serdes = 2, - e1000_num_media_types + e1000_media_type_copper = 0, + e1000_media_type_fiber = 1, + e1000_media_type_internal_serdes = 2, + e1000_num_media_types } e1000_media_type; typedef enum { - e1000_10_half = 0, - e1000_10_full = 1, - e1000_100_half = 2, - e1000_100_full = 3 + e1000_10_half = 0, + e1000_10_full = 1, + e1000_100_half = 2, + e1000_100_full = 3 } e1000_speed_duplex_type; /* Flow Control Settings */ typedef enum { - E1000_FC_NONE = 0, - E1000_FC_RX_PAUSE = 1, - E1000_FC_TX_PAUSE = 2, - E1000_FC_FULL = 3, - E1000_FC_DEFAULT = 0xFF + E1000_FC_NONE = 0, + E1000_FC_RX_PAUSE = 1, + E1000_FC_TX_PAUSE = 2, + E1000_FC_FULL = 3, + E1000_FC_DEFAULT = 0xFF } e1000_fc_type; struct e1000_shadow_ram { - u16 eeprom_word; - bool modified; + u16 eeprom_word; + bool modified; }; /* PCI bus types */ typedef enum { - e1000_bus_type_unknown = 0, - e1000_bus_type_pci, - e1000_bus_type_pcix, - e1000_bus_type_pci_express, - e1000_bus_type_reserved + e1000_bus_type_unknown = 0, + e1000_bus_type_pci, + e1000_bus_type_pcix, + e1000_bus_type_reserved } e1000_bus_type; /* PCI bus speeds */ typedef enum { - e1000_bus_speed_unknown = 0, - e1000_bus_speed_33, - e1000_bus_speed_66, - e1000_bus_speed_100, - e1000_bus_speed_120, - e1000_bus_speed_133, - e1000_bus_speed_2500, - e1000_bus_speed_reserved + e1000_bus_speed_unknown = 0, + e1000_bus_speed_33, + e1000_bus_speed_66, + e1000_bus_speed_100, + e1000_bus_speed_120, + e1000_bus_speed_133, + e1000_bus_speed_reserved } e1000_bus_speed; /* PCI bus widths */ typedef enum { - e1000_bus_width_unknown = 0, - /* These PCIe values should literally match the possible return values - * from config space */ - e1000_bus_width_pciex_1 = 1, - e1000_bus_width_pciex_2 = 2, - e1000_bus_width_pciex_4 = 4, - e1000_bus_width_32, - e1000_bus_width_64, - e1000_bus_width_reserved + e1000_bus_width_unknown = 0, + e1000_bus_width_32, + e1000_bus_width_64, + e1000_bus_width_reserved } e1000_bus_width; /* PHY status info structure and supporting enums */ typedef enum { - e1000_cable_length_50 = 0, - e1000_cable_length_50_80, - e1000_cable_length_80_110, - e1000_cable_length_110_140, - e1000_cable_length_140, - e1000_cable_length_undefined = 0xFF + e1000_cable_length_50 = 0, + e1000_cable_length_50_80, + e1000_cable_length_80_110, + e1000_cable_length_110_140, + e1000_cable_length_140, + e1000_cable_length_undefined = 0xFF } e1000_cable_length; typedef enum { - e1000_gg_cable_length_60 = 0, - e1000_gg_cable_length_60_115 = 1, - e1000_gg_cable_length_115_150 = 2, - e1000_gg_cable_length_150 = 4 + e1000_gg_cable_length_60 = 0, + e1000_gg_cable_length_60_115 = 1, + e1000_gg_cable_length_115_150 = 2, + e1000_gg_cable_length_150 = 4 } e1000_gg_cable_length; typedef enum { - e1000_igp_cable_length_10 = 10, - e1000_igp_cable_length_20 = 20, - e1000_igp_cable_length_30 = 30, - e1000_igp_cable_length_40 = 40, - e1000_igp_cable_length_50 = 50, - e1000_igp_cable_length_60 = 60, - e1000_igp_cable_length_70 = 70, - e1000_igp_cable_length_80 = 80, - e1000_igp_cable_length_90 = 90, - e1000_igp_cable_length_100 = 100, - e1000_igp_cable_length_110 = 110, - e1000_igp_cable_length_115 = 115, - e1000_igp_cable_length_120 = 120, - e1000_igp_cable_length_130 = 130, - e1000_igp_cable_length_140 = 140, - e1000_igp_cable_length_150 = 150, - e1000_igp_cable_length_160 = 160, - e1000_igp_cable_length_170 = 170, - e1000_igp_cable_length_180 = 180 + e1000_igp_cable_length_10 = 10, + e1000_igp_cable_length_20 = 20, + e1000_igp_cable_length_30 = 30, + e1000_igp_cable_length_40 = 40, + e1000_igp_cable_length_50 = 50, + e1000_igp_cable_length_60 = 60, + e1000_igp_cable_length_70 = 70, + e1000_igp_cable_length_80 = 80, + e1000_igp_cable_length_90 = 90, + e1000_igp_cable_length_100 = 100, + e1000_igp_cable_length_110 = 110, + e1000_igp_cable_length_115 = 115, + e1000_igp_cable_length_120 = 120, + e1000_igp_cable_length_130 = 130, + e1000_igp_cable_length_140 = 140, + e1000_igp_cable_length_150 = 150, + e1000_igp_cable_length_160 = 160, + e1000_igp_cable_length_170 = 170, + e1000_igp_cable_length_180 = 180 } e1000_igp_cable_length; typedef enum { - e1000_10bt_ext_dist_enable_normal = 0, - e1000_10bt_ext_dist_enable_lower, - e1000_10bt_ext_dist_enable_undefined = 0xFF + e1000_10bt_ext_dist_enable_normal = 0, + e1000_10bt_ext_dist_enable_lower, + e1000_10bt_ext_dist_enable_undefined = 0xFF } e1000_10bt_ext_dist_enable; typedef enum { - e1000_rev_polarity_normal = 0, - e1000_rev_polarity_reversed, - e1000_rev_polarity_undefined = 0xFF + e1000_rev_polarity_normal = 0, + e1000_rev_polarity_reversed, + e1000_rev_polarity_undefined = 0xFF } e1000_rev_polarity; typedef enum { - e1000_downshift_normal = 0, - e1000_downshift_activated, - e1000_downshift_undefined = 0xFF + e1000_downshift_normal = 0, + e1000_downshift_activated, + e1000_downshift_undefined = 0xFF } e1000_downshift; typedef enum { - e1000_smart_speed_default = 0, - e1000_smart_speed_on, - e1000_smart_speed_off + e1000_smart_speed_default = 0, + e1000_smart_speed_on, + e1000_smart_speed_off } e1000_smart_speed; typedef enum { - e1000_polarity_reversal_enabled = 0, - e1000_polarity_reversal_disabled, - e1000_polarity_reversal_undefined = 0xFF + e1000_polarity_reversal_enabled = 0, + e1000_polarity_reversal_disabled, + e1000_polarity_reversal_undefined = 0xFF } e1000_polarity_reversal; typedef enum { - e1000_auto_x_mode_manual_mdi = 0, - e1000_auto_x_mode_manual_mdix, - e1000_auto_x_mode_auto1, - e1000_auto_x_mode_auto2, - e1000_auto_x_mode_undefined = 0xFF + e1000_auto_x_mode_manual_mdi = 0, + e1000_auto_x_mode_manual_mdix, + e1000_auto_x_mode_auto1, + e1000_auto_x_mode_auto2, + e1000_auto_x_mode_undefined = 0xFF } e1000_auto_x_mode; typedef enum { - e1000_1000t_rx_status_not_ok = 0, - e1000_1000t_rx_status_ok, - e1000_1000t_rx_status_undefined = 0xFF + e1000_1000t_rx_status_not_ok = 0, + e1000_1000t_rx_status_ok, + e1000_1000t_rx_status_undefined = 0xFF } e1000_1000t_rx_status; typedef enum { e1000_phy_m88 = 0, e1000_phy_igp, - e1000_phy_igp_2, - e1000_phy_gg82563, - e1000_phy_igp_3, - e1000_phy_ife, e1000_phy_undefined = 0xFF } e1000_phy_type; typedef enum { - e1000_ms_hw_default = 0, - e1000_ms_force_master, - e1000_ms_force_slave, - e1000_ms_auto + e1000_ms_hw_default = 0, + e1000_ms_force_master, + e1000_ms_force_slave, + e1000_ms_auto } e1000_ms_type; typedef enum { - e1000_ffe_config_enabled = 0, - e1000_ffe_config_active, - e1000_ffe_config_blocked + e1000_ffe_config_enabled = 0, + e1000_ffe_config_active, + e1000_ffe_config_blocked } e1000_ffe_config; typedef enum { - e1000_dsp_config_disabled = 0, - e1000_dsp_config_enabled, - e1000_dsp_config_activated, - e1000_dsp_config_undefined = 0xFF + e1000_dsp_config_disabled = 0, + e1000_dsp_config_enabled, + e1000_dsp_config_activated, + e1000_dsp_config_undefined = 0xFF } e1000_dsp_config; struct e1000_phy_info { - e1000_cable_length cable_length; - e1000_10bt_ext_dist_enable extended_10bt_distance; - e1000_rev_polarity cable_polarity; - e1000_downshift downshift; - e1000_polarity_reversal polarity_correction; - e1000_auto_x_mode mdix_mode; - e1000_1000t_rx_status local_rx; - e1000_1000t_rx_status remote_rx; + e1000_cable_length cable_length; + e1000_10bt_ext_dist_enable extended_10bt_distance; + e1000_rev_polarity cable_polarity; + e1000_downshift downshift; + e1000_polarity_reversal polarity_correction; + e1000_auto_x_mode mdix_mode; + e1000_1000t_rx_status local_rx; + e1000_1000t_rx_status remote_rx; }; struct e1000_phy_stats { - u32 idle_errors; - u32 receive_errors; + u32 idle_errors; + u32 receive_errors; }; struct e1000_eeprom_info { - e1000_eeprom_type type; - u16 word_size; - u16 opcode_bits; - u16 address_bits; - u16 delay_usec; - u16 page_size; - bool use_eerd; - bool use_eewr; + e1000_eeprom_type type; + u16 word_size; + u16 opcode_bits; + u16 address_bits; + u16 delay_usec; + u16 page_size; }; /* Flex ASF Information */ #define E1000_HOST_IF_MAX_SIZE 2048 typedef enum { - e1000_byte_align = 0, - e1000_word_align = 1, - e1000_dword_align = 2 + e1000_byte_align = 0, + e1000_word_align = 1, + e1000_dword_align = 2 } e1000_align_type; - - /* Error Codes */ #define E1000_SUCCESS 0 #define E1000_ERR_EEPROM 1 @@ -301,7 +279,6 @@ typedef enum { #define E1000_ERR_MASTER_REQUESTS_PENDING 10 #define E1000_ERR_HOST_INTERFACE_COMMAND 11 #define E1000_BLK_PHY_RESET 12 -#define E1000_ERR_SWFW_SYNC 13 #define E1000_BYTE_SWAP_WORD(_value) ((((_value) & 0x00ff) << 8) | \ (((_value) & 0xff00) >> 8)) @@ -318,19 +295,17 @@ s32 e1000_setup_link(struct e1000_hw *hw); s32 e1000_phy_setup_autoneg(struct e1000_hw *hw); void e1000_config_collision_dist(struct e1000_hw *hw); s32 e1000_check_for_link(struct e1000_hw *hw); -s32 e1000_get_speed_and_duplex(struct e1000_hw *hw, u16 *speed, u16 *duplex); +s32 e1000_get_speed_and_duplex(struct e1000_hw *hw, u16 * speed, u16 * duplex); s32 e1000_force_mac_fc(struct e1000_hw *hw); /* PHY */ -s32 e1000_read_phy_reg(struct e1000_hw *hw, u32 reg_addr, u16 *phy_data); +s32 e1000_read_phy_reg(struct e1000_hw *hw, u32 reg_addr, u16 * phy_data); s32 e1000_write_phy_reg(struct e1000_hw *hw, u32 reg_addr, u16 data); s32 e1000_phy_hw_reset(struct e1000_hw *hw); s32 e1000_phy_reset(struct e1000_hw *hw); s32 e1000_phy_get_info(struct e1000_hw *hw, struct e1000_phy_info *phy_info); s32 e1000_validate_mdi_setting(struct e1000_hw *hw); -void e1000_phy_powerdown_workaround(struct e1000_hw *hw); - /* EEPROM Functions */ s32 e1000_init_eeprom_params(struct e1000_hw *hw); @@ -338,66 +313,63 @@ s32 e1000_init_eeprom_params(struct e1000_hw *hw); u32 e1000_enable_mng_pass_thru(struct e1000_hw *hw); #define E1000_MNG_DHCP_TX_PAYLOAD_CMD 64 -#define E1000_HI_MAX_MNG_DATA_LENGTH 0x6F8 /* Host Interface data length */ +#define E1000_HI_MAX_MNG_DATA_LENGTH 0x6F8 /* Host Interface data length */ -#define E1000_MNG_DHCP_COMMAND_TIMEOUT 10 /* Time in ms to process MNG command */ -#define E1000_MNG_DHCP_COOKIE_OFFSET 0x6F0 /* Cookie offset */ -#define E1000_MNG_DHCP_COOKIE_LENGTH 0x10 /* Cookie length */ +#define E1000_MNG_DHCP_COMMAND_TIMEOUT 10 /* Time in ms to process MNG command */ +#define E1000_MNG_DHCP_COOKIE_OFFSET 0x6F0 /* Cookie offset */ +#define E1000_MNG_DHCP_COOKIE_LENGTH 0x10 /* Cookie length */ #define E1000_MNG_IAMT_MODE 0x3 #define E1000_MNG_ICH_IAMT_MODE 0x2 -#define E1000_IAMT_SIGNATURE 0x544D4149 /* Intel(R) Active Management Technology signature */ +#define E1000_IAMT_SIGNATURE 0x544D4149 /* Intel(R) Active Management Technology signature */ -#define E1000_MNG_DHCP_COOKIE_STATUS_PARSING_SUPPORT 0x1 /* DHCP parsing enabled */ -#define E1000_MNG_DHCP_COOKIE_STATUS_VLAN_SUPPORT 0x2 /* DHCP parsing enabled */ +#define E1000_MNG_DHCP_COOKIE_STATUS_PARSING_SUPPORT 0x1 /* DHCP parsing enabled */ +#define E1000_MNG_DHCP_COOKIE_STATUS_VLAN_SUPPORT 0x2 /* DHCP parsing enabled */ #define E1000_VFTA_ENTRY_SHIFT 0x5 #define E1000_VFTA_ENTRY_MASK 0x7F #define E1000_VFTA_ENTRY_BIT_SHIFT_MASK 0x1F struct e1000_host_mng_command_header { - u8 command_id; - u8 checksum; - u16 reserved1; - u16 reserved2; - u16 command_length; + u8 command_id; + u8 checksum; + u16 reserved1; + u16 reserved2; + u16 command_length; }; struct e1000_host_mng_command_info { - struct e1000_host_mng_command_header command_header; /* Command Head/Command Result Head has 4 bytes */ - u8 command_data[E1000_HI_MAX_MNG_DATA_LENGTH]; /* Command data can length 0..0x658*/ + struct e1000_host_mng_command_header command_header; /* Command Head/Command Result Head has 4 bytes */ + u8 command_data[E1000_HI_MAX_MNG_DATA_LENGTH]; /* Command data can length 0..0x658 */ }; #ifdef __BIG_ENDIAN -struct e1000_host_mng_dhcp_cookie{ - u32 signature; - u16 vlan_id; - u8 reserved0; - u8 status; - u32 reserved1; - u8 checksum; - u8 reserved3; - u16 reserved2; +struct e1000_host_mng_dhcp_cookie { + u32 signature; + u16 vlan_id; + u8 reserved0; + u8 status; + u32 reserved1; + u8 checksum; + u8 reserved3; + u16 reserved2; }; #else -struct e1000_host_mng_dhcp_cookie{ - u32 signature; - u8 status; - u8 reserved0; - u16 vlan_id; - u32 reserved1; - u16 reserved2; - u8 reserved3; - u8 checksum; +struct e1000_host_mng_dhcp_cookie { + u32 signature; + u8 status; + u8 reserved0; + u16 vlan_id; + u32 reserved1; + u16 reserved2; + u8 reserved3; + u8 checksum; }; #endif -s32 e1000_mng_write_dhcp_info(struct e1000_hw *hw, u8 *buffer, - u16 length); bool e1000_check_mng_mode(struct e1000_hw *hw); -bool e1000_enable_tx_pkt_filtering(struct e1000_hw *hw); -s32 e1000_read_eeprom(struct e1000_hw *hw, u16 reg, u16 words, u16 *data); +s32 e1000_read_eeprom(struct e1000_hw *hw, u16 reg, u16 words, u16 * data); s32 e1000_validate_eeprom_checksum(struct e1000_hw *hw); s32 e1000_update_eeprom_checksum(struct e1000_hw *hw); -s32 e1000_write_eeprom(struct e1000_hw *hw, u16 reg, u16 words, u16 *data); -s32 e1000_read_mac_addr(struct e1000_hw * hw); +s32 e1000_write_eeprom(struct e1000_hw *hw, u16 reg, u16 words, u16 * data); +s32 e1000_read_mac_addr(struct e1000_hw *hw); /* Filters (multicast, vlan, receive) */ u32 e1000_hash_mc_addr(struct e1000_hw *hw, u8 * mc_addr); @@ -417,18 +389,15 @@ s32 e1000_blink_led_start(struct e1000_hw *hw); /* Everything else */ void e1000_reset_adaptive(struct e1000_hw *hw); void e1000_update_adaptive(struct e1000_hw *hw); -void e1000_tbi_adjust_stats(struct e1000_hw *hw, struct e1000_hw_stats *stats, u32 frame_len, u8 * mac_addr); +void e1000_tbi_adjust_stats(struct e1000_hw *hw, struct e1000_hw_stats *stats, + u32 frame_len, u8 * mac_addr); void e1000_get_bus_info(struct e1000_hw *hw); void e1000_pci_set_mwi(struct e1000_hw *hw); void e1000_pci_clear_mwi(struct e1000_hw *hw); -s32 e1000_read_pcie_cap_reg(struct e1000_hw *hw, u32 reg, u16 *value); void e1000_pcix_set_mmrbc(struct e1000_hw *hw, int mmrbc); int e1000_pcix_get_mmrbc(struct e1000_hw *hw); /* Port I/O is only supported on 82544 and newer */ void e1000_io_write(struct e1000_hw *hw, unsigned long port, u32 value); -s32 e1000_disable_pciex_master(struct e1000_hw *hw); -s32 e1000_check_phy_reset_block(struct e1000_hw *hw); - #define E1000_READ_REG_IO(a, reg) \ e1000_read_reg_io((a), E1000_##reg) @@ -471,36 +440,7 @@ s32 e1000_check_phy_reset_block(struct e1000_hw *hw); #define E1000_DEV_ID_82546GB_QUAD_COPPER 0x1099 #define E1000_DEV_ID_82547EI 0x1019 #define E1000_DEV_ID_82547EI_MOBILE 0x101A -#define E1000_DEV_ID_82571EB_COPPER 0x105E -#define E1000_DEV_ID_82571EB_FIBER 0x105F -#define E1000_DEV_ID_82571EB_SERDES 0x1060 -#define E1000_DEV_ID_82571EB_QUAD_COPPER 0x10A4 -#define E1000_DEV_ID_82571PT_QUAD_COPPER 0x10D5 -#define E1000_DEV_ID_82571EB_QUAD_FIBER 0x10A5 -#define E1000_DEV_ID_82571EB_QUAD_COPPER_LOWPROFILE 0x10BC -#define E1000_DEV_ID_82571EB_SERDES_DUAL 0x10D9 -#define E1000_DEV_ID_82571EB_SERDES_QUAD 0x10DA -#define E1000_DEV_ID_82572EI_COPPER 0x107D -#define E1000_DEV_ID_82572EI_FIBER 0x107E -#define E1000_DEV_ID_82572EI_SERDES 0x107F -#define E1000_DEV_ID_82572EI 0x10B9 -#define E1000_DEV_ID_82573E 0x108B -#define E1000_DEV_ID_82573E_IAMT 0x108C -#define E1000_DEV_ID_82573L 0x109A #define E1000_DEV_ID_82546GB_QUAD_COPPER_KSP3 0x10B5 -#define E1000_DEV_ID_80003ES2LAN_COPPER_DPT 0x1096 -#define E1000_DEV_ID_80003ES2LAN_SERDES_DPT 0x1098 -#define E1000_DEV_ID_80003ES2LAN_COPPER_SPT 0x10BA -#define E1000_DEV_ID_80003ES2LAN_SERDES_SPT 0x10BB - -#define E1000_DEV_ID_ICH8_IGP_M_AMT 0x1049 -#define E1000_DEV_ID_ICH8_IGP_AMT 0x104A -#define E1000_DEV_ID_ICH8_IGP_C 0x104B -#define E1000_DEV_ID_ICH8_IFE 0x104C -#define E1000_DEV_ID_ICH8_IFE_GT 0x10C4 -#define E1000_DEV_ID_ICH8_IFE_G 0x10C5 -#define E1000_DEV_ID_ICH8_IGP_M 0x104D - #define NODE_ADDRESS_SIZE 6 #define ETH_LENGTH_OF_ADDRESS 6 @@ -523,21 +463,20 @@ s32 e1000_check_phy_reset_block(struct e1000_hw *hw); /* The sizes (in bytes) of a ethernet packet */ #define ENET_HEADER_SIZE 14 -#define MINIMUM_ETHERNET_FRAME_SIZE 64 /* With FCS */ +#define MINIMUM_ETHERNET_FRAME_SIZE 64 /* With FCS */ #define ETHERNET_FCS_SIZE 4 #define MINIMUM_ETHERNET_PACKET_SIZE \ (MINIMUM_ETHERNET_FRAME_SIZE - ETHERNET_FCS_SIZE) #define CRC_LENGTH ETHERNET_FCS_SIZE #define MAX_JUMBO_FRAME_SIZE 0x3F00 - /* 802.1q VLAN Packet Sizes */ -#define VLAN_TAG_SIZE 4 /* 802.3ac tag (not DMAed) */ +#define VLAN_TAG_SIZE 4 /* 802.3ac tag (not DMAed) */ /* Ethertype field values */ -#define ETHERNET_IEEE_VLAN_TYPE 0x8100 /* 802.3ac packet */ -#define ETHERNET_IP_TYPE 0x0800 /* IP packets */ -#define ETHERNET_ARP_TYPE 0x0806 /* Address Resolution Protocol (ARP) */ +#define ETHERNET_IEEE_VLAN_TYPE 0x8100 /* 802.3ac packet */ +#define ETHERNET_IP_TYPE 0x0800 /* IP packets */ +#define ETHERNET_ARP_TYPE 0x0806 /* Address Resolution Protocol (ARP) */ /* Packet Header defines */ #define IP_PROTOCOL_TCP 6 @@ -567,15 +506,6 @@ s32 e1000_check_phy_reset_block(struct e1000_hw *hw); E1000_IMS_RXSEQ | \ E1000_IMS_LSC) -/* Additional interrupts need to be handled for e1000_ich8lan: - DSW = The FW changed the status of the DISSW bit in FWSM - PHYINT = The LAN connected device generates an interrupt - EPRST = Manageability reset event */ -#define IMS_ICH8LAN_ENABLE_MASK (\ - E1000_IMS_DSW | \ - E1000_IMS_PHYINT | \ - E1000_IMS_EPRST) - /* Number of high/low register pairs in the RAR. The RAR (Receive Address * Registers) holds the directed and multicast addresses that we monitor. We * reserve one of these spots for our directed address, allowing us room for @@ -583,100 +513,98 @@ s32 e1000_check_phy_reset_block(struct e1000_hw *hw); */ #define E1000_RAR_ENTRIES 15 -#define E1000_RAR_ENTRIES_ICH8LAN 6 - #define MIN_NUMBER_OF_DESCRIPTORS 8 #define MAX_NUMBER_OF_DESCRIPTORS 0xFFF8 /* Receive Descriptor */ struct e1000_rx_desc { - __le64 buffer_addr; /* Address of the descriptor's data buffer */ - __le16 length; /* Length of data DMAed into data buffer */ - __le16 csum; /* Packet checksum */ - u8 status; /* Descriptor status */ - u8 errors; /* Descriptor Errors */ - __le16 special; + __le64 buffer_addr; /* Address of the descriptor's data buffer */ + __le16 length; /* Length of data DMAed into data buffer */ + __le16 csum; /* Packet checksum */ + u8 status; /* Descriptor status */ + u8 errors; /* Descriptor Errors */ + __le16 special; }; /* Receive Descriptor - Extended */ union e1000_rx_desc_extended { - struct { - __le64 buffer_addr; - __le64 reserved; - } read; - struct { - struct { - __le32 mrq; /* Multiple Rx Queues */ - union { - __le32 rss; /* RSS Hash */ - struct { - __le16 ip_id; /* IP id */ - __le16 csum; /* Packet Checksum */ - } csum_ip; - } hi_dword; - } lower; - struct { - __le32 status_error; /* ext status/error */ - __le16 length; - __le16 vlan; /* VLAN tag */ - } upper; - } wb; /* writeback */ + struct { + __le64 buffer_addr; + __le64 reserved; + } read; + struct { + struct { + __le32 mrq; /* Multiple Rx Queues */ + union { + __le32 rss; /* RSS Hash */ + struct { + __le16 ip_id; /* IP id */ + __le16 csum; /* Packet Checksum */ + } csum_ip; + } hi_dword; + } lower; + struct { + __le32 status_error; /* ext status/error */ + __le16 length; + __le16 vlan; /* VLAN tag */ + } upper; + } wb; /* writeback */ }; #define MAX_PS_BUFFERS 4 /* Receive Descriptor - Packet Split */ union e1000_rx_desc_packet_split { - struct { - /* one buffer for protocol header(s), three data buffers */ - __le64 buffer_addr[MAX_PS_BUFFERS]; - } read; - struct { - struct { - __le32 mrq; /* Multiple Rx Queues */ - union { - __le32 rss; /* RSS Hash */ - struct { - __le16 ip_id; /* IP id */ - __le16 csum; /* Packet Checksum */ - } csum_ip; - } hi_dword; - } lower; - struct { - __le32 status_error; /* ext status/error */ - __le16 length0; /* length of buffer 0 */ - __le16 vlan; /* VLAN tag */ - } middle; - struct { - __le16 header_status; - __le16 length[3]; /* length of buffers 1-3 */ - } upper; - __le64 reserved; - } wb; /* writeback */ + struct { + /* one buffer for protocol header(s), three data buffers */ + __le64 buffer_addr[MAX_PS_BUFFERS]; + } read; + struct { + struct { + __le32 mrq; /* Multiple Rx Queues */ + union { + __le32 rss; /* RSS Hash */ + struct { + __le16 ip_id; /* IP id */ + __le16 csum; /* Packet Checksum */ + } csum_ip; + } hi_dword; + } lower; + struct { + __le32 status_error; /* ext status/error */ + __le16 length0; /* length of buffer 0 */ + __le16 vlan; /* VLAN tag */ + } middle; + struct { + __le16 header_status; + __le16 length[3]; /* length of buffers 1-3 */ + } upper; + __le64 reserved; + } wb; /* writeback */ }; -/* Receive Decriptor bit definitions */ -#define E1000_RXD_STAT_DD 0x01 /* Descriptor Done */ -#define E1000_RXD_STAT_EOP 0x02 /* End of Packet */ -#define E1000_RXD_STAT_IXSM 0x04 /* Ignore checksum */ -#define E1000_RXD_STAT_VP 0x08 /* IEEE VLAN Packet */ -#define E1000_RXD_STAT_UDPCS 0x10 /* UDP xsum caculated */ -#define E1000_RXD_STAT_TCPCS 0x20 /* TCP xsum calculated */ -#define E1000_RXD_STAT_IPCS 0x40 /* IP xsum calculated */ -#define E1000_RXD_STAT_PIF 0x80 /* passed in-exact filter */ -#define E1000_RXD_STAT_IPIDV 0x200 /* IP identification valid */ -#define E1000_RXD_STAT_UDPV 0x400 /* Valid UDP checksum */ -#define E1000_RXD_STAT_ACK 0x8000 /* ACK Packet indication */ -#define E1000_RXD_ERR_CE 0x01 /* CRC Error */ -#define E1000_RXD_ERR_SE 0x02 /* Symbol Error */ -#define E1000_RXD_ERR_SEQ 0x04 /* Sequence Error */ -#define E1000_RXD_ERR_CXE 0x10 /* Carrier Extension Error */ -#define E1000_RXD_ERR_TCPE 0x20 /* TCP/UDP Checksum Error */ -#define E1000_RXD_ERR_IPE 0x40 /* IP Checksum Error */ -#define E1000_RXD_ERR_RXE 0x80 /* Rx Data Error */ -#define E1000_RXD_SPC_VLAN_MASK 0x0FFF /* VLAN ID is in lower 12 bits */ -#define E1000_RXD_SPC_PRI_MASK 0xE000 /* Priority is in upper 3 bits */ +/* Receive Descriptor bit definitions */ +#define E1000_RXD_STAT_DD 0x01 /* Descriptor Done */ +#define E1000_RXD_STAT_EOP 0x02 /* End of Packet */ +#define E1000_RXD_STAT_IXSM 0x04 /* Ignore checksum */ +#define E1000_RXD_STAT_VP 0x08 /* IEEE VLAN Packet */ +#define E1000_RXD_STAT_UDPCS 0x10 /* UDP xsum calculated */ +#define E1000_RXD_STAT_TCPCS 0x20 /* TCP xsum calculated */ +#define E1000_RXD_STAT_IPCS 0x40 /* IP xsum calculated */ +#define E1000_RXD_STAT_PIF 0x80 /* passed in-exact filter */ +#define E1000_RXD_STAT_IPIDV 0x200 /* IP identification valid */ +#define E1000_RXD_STAT_UDPV 0x400 /* Valid UDP checksum */ +#define E1000_RXD_STAT_ACK 0x8000 /* ACK Packet indication */ +#define E1000_RXD_ERR_CE 0x01 /* CRC Error */ +#define E1000_RXD_ERR_SE 0x02 /* Symbol Error */ +#define E1000_RXD_ERR_SEQ 0x04 /* Sequence Error */ +#define E1000_RXD_ERR_CXE 0x10 /* Carrier Extension Error */ +#define E1000_RXD_ERR_TCPE 0x20 /* TCP/UDP Checksum Error */ +#define E1000_RXD_ERR_IPE 0x40 /* IP Checksum Error */ +#define E1000_RXD_ERR_RXE 0x80 /* Rx Data Error */ +#define E1000_RXD_SPC_VLAN_MASK 0x0FFF /* VLAN ID is in lower 12 bits */ +#define E1000_RXD_SPC_PRI_MASK 0xE000 /* Priority is in upper 3 bits */ #define E1000_RXD_SPC_PRI_SHIFT 13 -#define E1000_RXD_SPC_CFI_MASK 0x1000 /* CFI is bit 12 */ +#define E1000_RXD_SPC_CFI_MASK 0x1000 /* CFI is bit 12 */ #define E1000_RXD_SPC_CFI_SHIFT 12 #define E1000_RXDEXT_STATERR_CE 0x01000000 @@ -698,7 +626,6 @@ union e1000_rx_desc_packet_split { E1000_RXD_ERR_CXE | \ E1000_RXD_ERR_RXE) - /* Same mask, but for extended and packet split descriptors */ #define E1000_RXDEXT_ERR_FRAME_ERR_MASK ( \ E1000_RXDEXT_STATERR_CE | \ @@ -707,152 +634,145 @@ union e1000_rx_desc_packet_split { E1000_RXDEXT_STATERR_CXE | \ E1000_RXDEXT_STATERR_RXE) - /* Transmit Descriptor */ struct e1000_tx_desc { - __le64 buffer_addr; /* Address of the descriptor's data buffer */ - union { - __le32 data; - struct { - __le16 length; /* Data buffer length */ - u8 cso; /* Checksum offset */ - u8 cmd; /* Descriptor control */ - } flags; - } lower; - union { - __le32 data; - struct { - u8 status; /* Descriptor status */ - u8 css; /* Checksum start */ - __le16 special; - } fields; - } upper; + __le64 buffer_addr; /* Address of the descriptor's data buffer */ + union { + __le32 data; + struct { + __le16 length; /* Data buffer length */ + u8 cso; /* Checksum offset */ + u8 cmd; /* Descriptor control */ + } flags; + } lower; + union { + __le32 data; + struct { + u8 status; /* Descriptor status */ + u8 css; /* Checksum start */ + __le16 special; + } fields; + } upper; }; /* Transmit Descriptor bit definitions */ -#define E1000_TXD_DTYP_D 0x00100000 /* Data Descriptor */ -#define E1000_TXD_DTYP_C 0x00000000 /* Context Descriptor */ -#define E1000_TXD_POPTS_IXSM 0x01 /* Insert IP checksum */ -#define E1000_TXD_POPTS_TXSM 0x02 /* Insert TCP/UDP checksum */ -#define E1000_TXD_CMD_EOP 0x01000000 /* End of Packet */ -#define E1000_TXD_CMD_IFCS 0x02000000 /* Insert FCS (Ethernet CRC) */ -#define E1000_TXD_CMD_IC 0x04000000 /* Insert Checksum */ -#define E1000_TXD_CMD_RS 0x08000000 /* Report Status */ -#define E1000_TXD_CMD_RPS 0x10000000 /* Report Packet Sent */ -#define E1000_TXD_CMD_DEXT 0x20000000 /* Descriptor extension (0 = legacy) */ -#define E1000_TXD_CMD_VLE 0x40000000 /* Add VLAN tag */ -#define E1000_TXD_CMD_IDE 0x80000000 /* Enable Tidv register */ -#define E1000_TXD_STAT_DD 0x00000001 /* Descriptor Done */ -#define E1000_TXD_STAT_EC 0x00000002 /* Excess Collisions */ -#define E1000_TXD_STAT_LC 0x00000004 /* Late Collisions */ -#define E1000_TXD_STAT_TU 0x00000008 /* Transmit underrun */ -#define E1000_TXD_CMD_TCP 0x01000000 /* TCP packet */ -#define E1000_TXD_CMD_IP 0x02000000 /* IP packet */ -#define E1000_TXD_CMD_TSE 0x04000000 /* TCP Seg enable */ -#define E1000_TXD_STAT_TC 0x00000004 /* Tx Underrun */ +#define E1000_TXD_DTYP_D 0x00100000 /* Data Descriptor */ +#define E1000_TXD_DTYP_C 0x00000000 /* Context Descriptor */ +#define E1000_TXD_POPTS_IXSM 0x01 /* Insert IP checksum */ +#define E1000_TXD_POPTS_TXSM 0x02 /* Insert TCP/UDP checksum */ +#define E1000_TXD_CMD_EOP 0x01000000 /* End of Packet */ +#define E1000_TXD_CMD_IFCS 0x02000000 /* Insert FCS (Ethernet CRC) */ +#define E1000_TXD_CMD_IC 0x04000000 /* Insert Checksum */ +#define E1000_TXD_CMD_RS 0x08000000 /* Report Status */ +#define E1000_TXD_CMD_RPS 0x10000000 /* Report Packet Sent */ +#define E1000_TXD_CMD_DEXT 0x20000000 /* Descriptor extension (0 = legacy) */ +#define E1000_TXD_CMD_VLE 0x40000000 /* Add VLAN tag */ +#define E1000_TXD_CMD_IDE 0x80000000 /* Enable Tidv register */ +#define E1000_TXD_STAT_DD 0x00000001 /* Descriptor Done */ +#define E1000_TXD_STAT_EC 0x00000002 /* Excess Collisions */ +#define E1000_TXD_STAT_LC 0x00000004 /* Late Collisions */ +#define E1000_TXD_STAT_TU 0x00000008 /* Transmit underrun */ +#define E1000_TXD_CMD_TCP 0x01000000 /* TCP packet */ +#define E1000_TXD_CMD_IP 0x02000000 /* IP packet */ +#define E1000_TXD_CMD_TSE 0x04000000 /* TCP Seg enable */ +#define E1000_TXD_STAT_TC 0x00000004 /* Tx Underrun */ /* Offload Context Descriptor */ struct e1000_context_desc { - union { - __le32 ip_config; - struct { - u8 ipcss; /* IP checksum start */ - u8 ipcso; /* IP checksum offset */ - __le16 ipcse; /* IP checksum end */ - } ip_fields; - } lower_setup; - union { - __le32 tcp_config; - struct { - u8 tucss; /* TCP checksum start */ - u8 tucso; /* TCP checksum offset */ - __le16 tucse; /* TCP checksum end */ - } tcp_fields; - } upper_setup; - __le32 cmd_and_length; /* */ - union { - __le32 data; - struct { - u8 status; /* Descriptor status */ - u8 hdr_len; /* Header length */ - __le16 mss; /* Maximum segment size */ - } fields; - } tcp_seg_setup; + union { + __le32 ip_config; + struct { + u8 ipcss; /* IP checksum start */ + u8 ipcso; /* IP checksum offset */ + __le16 ipcse; /* IP checksum end */ + } ip_fields; + } lower_setup; + union { + __le32 tcp_config; + struct { + u8 tucss; /* TCP checksum start */ + u8 tucso; /* TCP checksum offset */ + __le16 tucse; /* TCP checksum end */ + } tcp_fields; + } upper_setup; + __le32 cmd_and_length; /* */ + union { + __le32 data; + struct { + u8 status; /* Descriptor status */ + u8 hdr_len; /* Header length */ + __le16 mss; /* Maximum segment size */ + } fields; + } tcp_seg_setup; }; /* Offload data descriptor */ struct e1000_data_desc { - __le64 buffer_addr; /* Address of the descriptor's buffer address */ - union { - __le32 data; - struct { - __le16 length; /* Data buffer length */ - u8 typ_len_ext; /* */ - u8 cmd; /* */ - } flags; - } lower; - union { - __le32 data; - struct { - u8 status; /* Descriptor status */ - u8 popts; /* Packet Options */ - __le16 special; /* */ - } fields; - } upper; + __le64 buffer_addr; /* Address of the descriptor's buffer address */ + union { + __le32 data; + struct { + __le16 length; /* Data buffer length */ + u8 typ_len_ext; /* */ + u8 cmd; /* */ + } flags; + } lower; + union { + __le32 data; + struct { + u8 status; /* Descriptor status */ + u8 popts; /* Packet Options */ + __le16 special; /* */ + } fields; + } upper; }; /* Filters */ -#define E1000_NUM_UNICAST 16 /* Unicast filter entries */ -#define E1000_MC_TBL_SIZE 128 /* Multicast Filter Table (4096 bits) */ -#define E1000_VLAN_FILTER_TBL_SIZE 128 /* VLAN Filter Table (4096 bits) */ - -#define E1000_NUM_UNICAST_ICH8LAN 7 -#define E1000_MC_TBL_SIZE_ICH8LAN 32 - +#define E1000_NUM_UNICAST 16 /* Unicast filter entries */ +#define E1000_MC_TBL_SIZE 128 /* Multicast Filter Table (4096 bits) */ +#define E1000_VLAN_FILTER_TBL_SIZE 128 /* VLAN Filter Table (4096 bits) */ /* Receive Address Register */ struct e1000_rar { - volatile __le32 low; /* receive address low */ - volatile __le32 high; /* receive address high */ + volatile __le32 low; /* receive address low */ + volatile __le32 high; /* receive address high */ }; /* Number of entries in the Multicast Table Array (MTA). */ #define E1000_NUM_MTA_REGISTERS 128 -#define E1000_NUM_MTA_REGISTERS_ICH8LAN 32 /* IPv4 Address Table Entry */ struct e1000_ipv4_at_entry { - volatile u32 ipv4_addr; /* IP Address (RW) */ - volatile u32 reserved; + volatile u32 ipv4_addr; /* IP Address (RW) */ + volatile u32 reserved; }; /* Four wakeup IP addresses are supported */ #define E1000_WAKEUP_IP_ADDRESS_COUNT_MAX 4 #define E1000_IP4AT_SIZE E1000_WAKEUP_IP_ADDRESS_COUNT_MAX -#define E1000_IP4AT_SIZE_ICH8LAN 3 #define E1000_IP6AT_SIZE 1 /* IPv6 Address Table Entry */ struct e1000_ipv6_at_entry { - volatile u8 ipv6_addr[16]; + volatile u8 ipv6_addr[16]; }; /* Flexible Filter Length Table Entry */ struct e1000_fflt_entry { - volatile u32 length; /* Flexible Filter Length (RW) */ - volatile u32 reserved; + volatile u32 length; /* Flexible Filter Length (RW) */ + volatile u32 reserved; }; /* Flexible Filter Mask Table Entry */ struct e1000_ffmt_entry { - volatile u32 mask; /* Flexible Filter Mask (RW) */ - volatile u32 reserved; + volatile u32 mask; /* Flexible Filter Mask (RW) */ + volatile u32 reserved; }; /* Flexible Filter Value Table Entry */ struct e1000_ffvt_entry { - volatile u32 value; /* Flexible Filter Value (RW) */ - volatile u32 reserved; + volatile u32 value; /* Flexible Filter Value (RW) */ + volatile u32 reserved; }; /* Four Flexible Filters are supported */ @@ -879,211 +799,211 @@ struct e1000_ffvt_entry { * R/clr - register is read only and is cleared when read * A - register array */ -#define E1000_CTRL 0x00000 /* Device Control - RW */ -#define E1000_CTRL_DUP 0x00004 /* Device Control Duplicate (Shadow) - RW */ -#define E1000_STATUS 0x00008 /* Device Status - RO */ -#define E1000_EECD 0x00010 /* EEPROM/Flash Control - RW */ -#define E1000_EERD 0x00014 /* EEPROM Read - RW */ -#define E1000_CTRL_EXT 0x00018 /* Extended Device Control - RW */ -#define E1000_FLA 0x0001C /* Flash Access - RW */ -#define E1000_MDIC 0x00020 /* MDI Control - RW */ -#define E1000_SCTL 0x00024 /* SerDes Control - RW */ -#define E1000_FEXTNVM 0x00028 /* Future Extended NVM register */ -#define E1000_FCAL 0x00028 /* Flow Control Address Low - RW */ -#define E1000_FCAH 0x0002C /* Flow Control Address High -RW */ -#define E1000_FCT 0x00030 /* Flow Control Type - RW */ -#define E1000_VET 0x00038 /* VLAN Ether Type - RW */ -#define E1000_ICR 0x000C0 /* Interrupt Cause Read - R/clr */ -#define E1000_ITR 0x000C4 /* Interrupt Throttling Rate - RW */ -#define E1000_ICS 0x000C8 /* Interrupt Cause Set - WO */ -#define E1000_IMS 0x000D0 /* Interrupt Mask Set - RW */ -#define E1000_IMC 0x000D8 /* Interrupt Mask Clear - WO */ -#define E1000_IAM 0x000E0 /* Interrupt Acknowledge Auto Mask */ -#define E1000_RCTL 0x00100 /* RX Control - RW */ -#define E1000_RDTR1 0x02820 /* RX Delay Timer (1) - RW */ -#define E1000_RDBAL1 0x02900 /* RX Descriptor Base Address Low (1) - RW */ -#define E1000_RDBAH1 0x02904 /* RX Descriptor Base Address High (1) - RW */ -#define E1000_RDLEN1 0x02908 /* RX Descriptor Length (1) - RW */ -#define E1000_RDH1 0x02910 /* RX Descriptor Head (1) - RW */ -#define E1000_RDT1 0x02918 /* RX Descriptor Tail (1) - RW */ -#define E1000_FCTTV 0x00170 /* Flow Control Transmit Timer Value - RW */ -#define E1000_TXCW 0x00178 /* TX Configuration Word - RW */ -#define E1000_RXCW 0x00180 /* RX Configuration Word - RO */ -#define E1000_TCTL 0x00400 /* TX Control - RW */ -#define E1000_TCTL_EXT 0x00404 /* Extended TX Control - RW */ -#define E1000_TIPG 0x00410 /* TX Inter-packet gap -RW */ -#define E1000_TBT 0x00448 /* TX Burst Timer - RW */ -#define E1000_AIT 0x00458 /* Adaptive Interframe Spacing Throttle - RW */ -#define E1000_LEDCTL 0x00E00 /* LED Control - RW */ -#define E1000_EXTCNF_CTRL 0x00F00 /* Extended Configuration Control */ -#define E1000_EXTCNF_SIZE 0x00F08 /* Extended Configuration Size */ -#define E1000_PHY_CTRL 0x00F10 /* PHY Control Register in CSR */ +#define E1000_CTRL 0x00000 /* Device Control - RW */ +#define E1000_CTRL_DUP 0x00004 /* Device Control Duplicate (Shadow) - RW */ +#define E1000_STATUS 0x00008 /* Device Status - RO */ +#define E1000_EECD 0x00010 /* EEPROM/Flash Control - RW */ +#define E1000_EERD 0x00014 /* EEPROM Read - RW */ +#define E1000_CTRL_EXT 0x00018 /* Extended Device Control - RW */ +#define E1000_FLA 0x0001C /* Flash Access - RW */ +#define E1000_MDIC 0x00020 /* MDI Control - RW */ +#define E1000_SCTL 0x00024 /* SerDes Control - RW */ +#define E1000_FEXTNVM 0x00028 /* Future Extended NVM register */ +#define E1000_FCAL 0x00028 /* Flow Control Address Low - RW */ +#define E1000_FCAH 0x0002C /* Flow Control Address High -RW */ +#define E1000_FCT 0x00030 /* Flow Control Type - RW */ +#define E1000_VET 0x00038 /* VLAN Ether Type - RW */ +#define E1000_ICR 0x000C0 /* Interrupt Cause Read - R/clr */ +#define E1000_ITR 0x000C4 /* Interrupt Throttling Rate - RW */ +#define E1000_ICS 0x000C8 /* Interrupt Cause Set - WO */ +#define E1000_IMS 0x000D0 /* Interrupt Mask Set - RW */ +#define E1000_IMC 0x000D8 /* Interrupt Mask Clear - WO */ +#define E1000_IAM 0x000E0 /* Interrupt Acknowledge Auto Mask */ +#define E1000_RCTL 0x00100 /* RX Control - RW */ +#define E1000_RDTR1 0x02820 /* RX Delay Timer (1) - RW */ +#define E1000_RDBAL1 0x02900 /* RX Descriptor Base Address Low (1) - RW */ +#define E1000_RDBAH1 0x02904 /* RX Descriptor Base Address High (1) - RW */ +#define E1000_RDLEN1 0x02908 /* RX Descriptor Length (1) - RW */ +#define E1000_RDH1 0x02910 /* RX Descriptor Head (1) - RW */ +#define E1000_RDT1 0x02918 /* RX Descriptor Tail (1) - RW */ +#define E1000_FCTTV 0x00170 /* Flow Control Transmit Timer Value - RW */ +#define E1000_TXCW 0x00178 /* TX Configuration Word - RW */ +#define E1000_RXCW 0x00180 /* RX Configuration Word - RO */ +#define E1000_TCTL 0x00400 /* TX Control - RW */ +#define E1000_TCTL_EXT 0x00404 /* Extended TX Control - RW */ +#define E1000_TIPG 0x00410 /* TX Inter-packet gap -RW */ +#define E1000_TBT 0x00448 /* TX Burst Timer - RW */ +#define E1000_AIT 0x00458 /* Adaptive Interframe Spacing Throttle - RW */ +#define E1000_LEDCTL 0x00E00 /* LED Control - RW */ +#define E1000_EXTCNF_CTRL 0x00F00 /* Extended Configuration Control */ +#define E1000_EXTCNF_SIZE 0x00F08 /* Extended Configuration Size */ +#define E1000_PHY_CTRL 0x00F10 /* PHY Control Register in CSR */ #define FEXTNVM_SW_CONFIG 0x0001 -#define E1000_PBA 0x01000 /* Packet Buffer Allocation - RW */ -#define E1000_PBS 0x01008 /* Packet Buffer Size */ -#define E1000_EEMNGCTL 0x01010 /* MNG EEprom Control */ +#define E1000_PBA 0x01000 /* Packet Buffer Allocation - RW */ +#define E1000_PBS 0x01008 /* Packet Buffer Size */ +#define E1000_EEMNGCTL 0x01010 /* MNG EEprom Control */ #define E1000_FLASH_UPDATES 1000 -#define E1000_EEARBC 0x01024 /* EEPROM Auto Read Bus Control */ -#define E1000_FLASHT 0x01028 /* FLASH Timer Register */ -#define E1000_EEWR 0x0102C /* EEPROM Write Register - RW */ -#define E1000_FLSWCTL 0x01030 /* FLASH control register */ -#define E1000_FLSWDATA 0x01034 /* FLASH data register */ -#define E1000_FLSWCNT 0x01038 /* FLASH Access Counter */ -#define E1000_FLOP 0x0103C /* FLASH Opcode Register */ -#define E1000_ERT 0x02008 /* Early Rx Threshold - RW */ -#define E1000_FCRTL 0x02160 /* Flow Control Receive Threshold Low - RW */ -#define E1000_FCRTH 0x02168 /* Flow Control Receive Threshold High - RW */ -#define E1000_PSRCTL 0x02170 /* Packet Split Receive Control - RW */ -#define E1000_RDBAL 0x02800 /* RX Descriptor Base Address Low - RW */ -#define E1000_RDBAH 0x02804 /* RX Descriptor Base Address High - RW */ -#define E1000_RDLEN 0x02808 /* RX Descriptor Length - RW */ -#define E1000_RDH 0x02810 /* RX Descriptor Head - RW */ -#define E1000_RDT 0x02818 /* RX Descriptor Tail - RW */ -#define E1000_RDTR 0x02820 /* RX Delay Timer - RW */ -#define E1000_RDBAL0 E1000_RDBAL /* RX Desc Base Address Low (0) - RW */ -#define E1000_RDBAH0 E1000_RDBAH /* RX Desc Base Address High (0) - RW */ -#define E1000_RDLEN0 E1000_RDLEN /* RX Desc Length (0) - RW */ -#define E1000_RDH0 E1000_RDH /* RX Desc Head (0) - RW */ -#define E1000_RDT0 E1000_RDT /* RX Desc Tail (0) - RW */ -#define E1000_RDTR0 E1000_RDTR /* RX Delay Timer (0) - RW */ -#define E1000_RXDCTL 0x02828 /* RX Descriptor Control queue 0 - RW */ -#define E1000_RXDCTL1 0x02928 /* RX Descriptor Control queue 1 - RW */ -#define E1000_RADV 0x0282C /* RX Interrupt Absolute Delay Timer - RW */ -#define E1000_RSRPD 0x02C00 /* RX Small Packet Detect - RW */ -#define E1000_RAID 0x02C08 /* Receive Ack Interrupt Delay - RW */ -#define E1000_TXDMAC 0x03000 /* TX DMA Control - RW */ -#define E1000_KABGTXD 0x03004 /* AFE Band Gap Transmit Ref Data */ -#define E1000_TDFH 0x03410 /* TX Data FIFO Head - RW */ -#define E1000_TDFT 0x03418 /* TX Data FIFO Tail - RW */ -#define E1000_TDFHS 0x03420 /* TX Data FIFO Head Saved - RW */ -#define E1000_TDFTS 0x03428 /* TX Data FIFO Tail Saved - RW */ -#define E1000_TDFPC 0x03430 /* TX Data FIFO Packet Count - RW */ -#define E1000_TDBAL 0x03800 /* TX Descriptor Base Address Low - RW */ -#define E1000_TDBAH 0x03804 /* TX Descriptor Base Address High - RW */ -#define E1000_TDLEN 0x03808 /* TX Descriptor Length - RW */ -#define E1000_TDH 0x03810 /* TX Descriptor Head - RW */ -#define E1000_TDT 0x03818 /* TX Descripotr Tail - RW */ -#define E1000_TIDV 0x03820 /* TX Interrupt Delay Value - RW */ -#define E1000_TXDCTL 0x03828 /* TX Descriptor Control - RW */ -#define E1000_TADV 0x0382C /* TX Interrupt Absolute Delay Val - RW */ -#define E1000_TSPMT 0x03830 /* TCP Segmentation PAD & Min Threshold - RW */ -#define E1000_TARC0 0x03840 /* TX Arbitration Count (0) */ -#define E1000_TDBAL1 0x03900 /* TX Desc Base Address Low (1) - RW */ -#define E1000_TDBAH1 0x03904 /* TX Desc Base Address High (1) - RW */ -#define E1000_TDLEN1 0x03908 /* TX Desc Length (1) - RW */ -#define E1000_TDH1 0x03910 /* TX Desc Head (1) - RW */ -#define E1000_TDT1 0x03918 /* TX Desc Tail (1) - RW */ -#define E1000_TXDCTL1 0x03928 /* TX Descriptor Control (1) - RW */ -#define E1000_TARC1 0x03940 /* TX Arbitration Count (1) */ -#define E1000_CRCERRS 0x04000 /* CRC Error Count - R/clr */ -#define E1000_ALGNERRC 0x04004 /* Alignment Error Count - R/clr */ -#define E1000_SYMERRS 0x04008 /* Symbol Error Count - R/clr */ -#define E1000_RXERRC 0x0400C /* Receive Error Count - R/clr */ -#define E1000_MPC 0x04010 /* Missed Packet Count - R/clr */ -#define E1000_SCC 0x04014 /* Single Collision Count - R/clr */ -#define E1000_ECOL 0x04018 /* Excessive Collision Count - R/clr */ -#define E1000_MCC 0x0401C /* Multiple Collision Count - R/clr */ -#define E1000_LATECOL 0x04020 /* Late Collision Count - R/clr */ -#define E1000_COLC 0x04028 /* Collision Count - R/clr */ -#define E1000_DC 0x04030 /* Defer Count - R/clr */ -#define E1000_TNCRS 0x04034 /* TX-No CRS - R/clr */ -#define E1000_SEC 0x04038 /* Sequence Error Count - R/clr */ -#define E1000_CEXTERR 0x0403C /* Carrier Extension Error Count - R/clr */ -#define E1000_RLEC 0x04040 /* Receive Length Error Count - R/clr */ -#define E1000_XONRXC 0x04048 /* XON RX Count - R/clr */ -#define E1000_XONTXC 0x0404C /* XON TX Count - R/clr */ -#define E1000_XOFFRXC 0x04050 /* XOFF RX Count - R/clr */ -#define E1000_XOFFTXC 0x04054 /* XOFF TX Count - R/clr */ -#define E1000_FCRUC 0x04058 /* Flow Control RX Unsupported Count- R/clr */ -#define E1000_PRC64 0x0405C /* Packets RX (64 bytes) - R/clr */ -#define E1000_PRC127 0x04060 /* Packets RX (65-127 bytes) - R/clr */ -#define E1000_PRC255 0x04064 /* Packets RX (128-255 bytes) - R/clr */ -#define E1000_PRC511 0x04068 /* Packets RX (255-511 bytes) - R/clr */ -#define E1000_PRC1023 0x0406C /* Packets RX (512-1023 bytes) - R/clr */ -#define E1000_PRC1522 0x04070 /* Packets RX (1024-1522 bytes) - R/clr */ -#define E1000_GPRC 0x04074 /* Good Packets RX Count - R/clr */ -#define E1000_BPRC 0x04078 /* Broadcast Packets RX Count - R/clr */ -#define E1000_MPRC 0x0407C /* Multicast Packets RX Count - R/clr */ -#define E1000_GPTC 0x04080 /* Good Packets TX Count - R/clr */ -#define E1000_GORCL 0x04088 /* Good Octets RX Count Low - R/clr */ -#define E1000_GORCH 0x0408C /* Good Octets RX Count High - R/clr */ -#define E1000_GOTCL 0x04090 /* Good Octets TX Count Low - R/clr */ -#define E1000_GOTCH 0x04094 /* Good Octets TX Count High - R/clr */ -#define E1000_RNBC 0x040A0 /* RX No Buffers Count - R/clr */ -#define E1000_RUC 0x040A4 /* RX Undersize Count - R/clr */ -#define E1000_RFC 0x040A8 /* RX Fragment Count - R/clr */ -#define E1000_ROC 0x040AC /* RX Oversize Count - R/clr */ -#define E1000_RJC 0x040B0 /* RX Jabber Count - R/clr */ -#define E1000_MGTPRC 0x040B4 /* Management Packets RX Count - R/clr */ -#define E1000_MGTPDC 0x040B8 /* Management Packets Dropped Count - R/clr */ -#define E1000_MGTPTC 0x040BC /* Management Packets TX Count - R/clr */ -#define E1000_TORL 0x040C0 /* Total Octets RX Low - R/clr */ -#define E1000_TORH 0x040C4 /* Total Octets RX High - R/clr */ -#define E1000_TOTL 0x040C8 /* Total Octets TX Low - R/clr */ -#define E1000_TOTH 0x040CC /* Total Octets TX High - R/clr */ -#define E1000_TPR 0x040D0 /* Total Packets RX - R/clr */ -#define E1000_TPT 0x040D4 /* Total Packets TX - R/clr */ -#define E1000_PTC64 0x040D8 /* Packets TX (64 bytes) - R/clr */ -#define E1000_PTC127 0x040DC /* Packets TX (65-127 bytes) - R/clr */ -#define E1000_PTC255 0x040E0 /* Packets TX (128-255 bytes) - R/clr */ -#define E1000_PTC511 0x040E4 /* Packets TX (256-511 bytes) - R/clr */ -#define E1000_PTC1023 0x040E8 /* Packets TX (512-1023 bytes) - R/clr */ -#define E1000_PTC1522 0x040EC /* Packets TX (1024-1522 Bytes) - R/clr */ -#define E1000_MPTC 0x040F0 /* Multicast Packets TX Count - R/clr */ -#define E1000_BPTC 0x040F4 /* Broadcast Packets TX Count - R/clr */ -#define E1000_TSCTC 0x040F8 /* TCP Segmentation Context TX - R/clr */ -#define E1000_TSCTFC 0x040FC /* TCP Segmentation Context TX Fail - R/clr */ -#define E1000_IAC 0x04100 /* Interrupt Assertion Count */ -#define E1000_ICRXPTC 0x04104 /* Interrupt Cause Rx Packet Timer Expire Count */ -#define E1000_ICRXATC 0x04108 /* Interrupt Cause Rx Absolute Timer Expire Count */ -#define E1000_ICTXPTC 0x0410C /* Interrupt Cause Tx Packet Timer Expire Count */ -#define E1000_ICTXATC 0x04110 /* Interrupt Cause Tx Absolute Timer Expire Count */ -#define E1000_ICTXQEC 0x04118 /* Interrupt Cause Tx Queue Empty Count */ -#define E1000_ICTXQMTC 0x0411C /* Interrupt Cause Tx Queue Minimum Threshold Count */ -#define E1000_ICRXDMTC 0x04120 /* Interrupt Cause Rx Descriptor Minimum Threshold Count */ -#define E1000_ICRXOC 0x04124 /* Interrupt Cause Receiver Overrun Count */ -#define E1000_RXCSUM 0x05000 /* RX Checksum Control - RW */ -#define E1000_RFCTL 0x05008 /* Receive Filter Control*/ -#define E1000_MTA 0x05200 /* Multicast Table Array - RW Array */ -#define E1000_RA 0x05400 /* Receive Address - RW Array */ -#define E1000_VFTA 0x05600 /* VLAN Filter Table Array - RW Array */ -#define E1000_WUC 0x05800 /* Wakeup Control - RW */ -#define E1000_WUFC 0x05808 /* Wakeup Filter Control - RW */ -#define E1000_WUS 0x05810 /* Wakeup Status - RO */ -#define E1000_MANC 0x05820 /* Management Control - RW */ -#define E1000_IPAV 0x05838 /* IP Address Valid - RW */ -#define E1000_IP4AT 0x05840 /* IPv4 Address Table - RW Array */ -#define E1000_IP6AT 0x05880 /* IPv6 Address Table - RW Array */ -#define E1000_WUPL 0x05900 /* Wakeup Packet Length - RW */ -#define E1000_WUPM 0x05A00 /* Wakeup Packet Memory - RO A */ -#define E1000_FFLT 0x05F00 /* Flexible Filter Length Table - RW Array */ -#define E1000_HOST_IF 0x08800 /* Host Interface */ -#define E1000_FFMT 0x09000 /* Flexible Filter Mask Table - RW Array */ -#define E1000_FFVT 0x09800 /* Flexible Filter Value Table - RW Array */ - -#define E1000_KUMCTRLSTA 0x00034 /* MAC-PHY interface - RW */ -#define E1000_MDPHYA 0x0003C /* PHY address - RW */ -#define E1000_MANC2H 0x05860 /* Managment Control To Host - RW */ -#define E1000_SW_FW_SYNC 0x05B5C /* Software-Firmware Synchronization - RW */ - -#define E1000_GCR 0x05B00 /* PCI-Ex Control */ -#define E1000_GSCL_1 0x05B10 /* PCI-Ex Statistic Control #1 */ -#define E1000_GSCL_2 0x05B14 /* PCI-Ex Statistic Control #2 */ -#define E1000_GSCL_3 0x05B18 /* PCI-Ex Statistic Control #3 */ -#define E1000_GSCL_4 0x05B1C /* PCI-Ex Statistic Control #4 */ -#define E1000_FACTPS 0x05B30 /* Function Active and Power State to MNG */ -#define E1000_SWSM 0x05B50 /* SW Semaphore */ -#define E1000_FWSM 0x05B54 /* FW Semaphore */ -#define E1000_FFLT_DBG 0x05F04 /* Debug Register */ -#define E1000_HICR 0x08F00 /* Host Inteface Control */ +#define E1000_EEARBC 0x01024 /* EEPROM Auto Read Bus Control */ +#define E1000_FLASHT 0x01028 /* FLASH Timer Register */ +#define E1000_EEWR 0x0102C /* EEPROM Write Register - RW */ +#define E1000_FLSWCTL 0x01030 /* FLASH control register */ +#define E1000_FLSWDATA 0x01034 /* FLASH data register */ +#define E1000_FLSWCNT 0x01038 /* FLASH Access Counter */ +#define E1000_FLOP 0x0103C /* FLASH Opcode Register */ +#define E1000_ERT 0x02008 /* Early Rx Threshold - RW */ +#define E1000_FCRTL 0x02160 /* Flow Control Receive Threshold Low - RW */ +#define E1000_FCRTH 0x02168 /* Flow Control Receive Threshold High - RW */ +#define E1000_PSRCTL 0x02170 /* Packet Split Receive Control - RW */ +#define E1000_RDBAL 0x02800 /* RX Descriptor Base Address Low - RW */ +#define E1000_RDBAH 0x02804 /* RX Descriptor Base Address High - RW */ +#define E1000_RDLEN 0x02808 /* RX Descriptor Length - RW */ +#define E1000_RDH 0x02810 /* RX Descriptor Head - RW */ +#define E1000_RDT 0x02818 /* RX Descriptor Tail - RW */ +#define E1000_RDTR 0x02820 /* RX Delay Timer - RW */ +#define E1000_RDBAL0 E1000_RDBAL /* RX Desc Base Address Low (0) - RW */ +#define E1000_RDBAH0 E1000_RDBAH /* RX Desc Base Address High (0) - RW */ +#define E1000_RDLEN0 E1000_RDLEN /* RX Desc Length (0) - RW */ +#define E1000_RDH0 E1000_RDH /* RX Desc Head (0) - RW */ +#define E1000_RDT0 E1000_RDT /* RX Desc Tail (0) - RW */ +#define E1000_RDTR0 E1000_RDTR /* RX Delay Timer (0) - RW */ +#define E1000_RXDCTL 0x02828 /* RX Descriptor Control queue 0 - RW */ +#define E1000_RXDCTL1 0x02928 /* RX Descriptor Control queue 1 - RW */ +#define E1000_RADV 0x0282C /* RX Interrupt Absolute Delay Timer - RW */ +#define E1000_RSRPD 0x02C00 /* RX Small Packet Detect - RW */ +#define E1000_RAID 0x02C08 /* Receive Ack Interrupt Delay - RW */ +#define E1000_TXDMAC 0x03000 /* TX DMA Control - RW */ +#define E1000_KABGTXD 0x03004 /* AFE Band Gap Transmit Ref Data */ +#define E1000_TDFH 0x03410 /* TX Data FIFO Head - RW */ +#define E1000_TDFT 0x03418 /* TX Data FIFO Tail - RW */ +#define E1000_TDFHS 0x03420 /* TX Data FIFO Head Saved - RW */ +#define E1000_TDFTS 0x03428 /* TX Data FIFO Tail Saved - RW */ +#define E1000_TDFPC 0x03430 /* TX Data FIFO Packet Count - RW */ +#define E1000_TDBAL 0x03800 /* TX Descriptor Base Address Low - RW */ +#define E1000_TDBAH 0x03804 /* TX Descriptor Base Address High - RW */ +#define E1000_TDLEN 0x03808 /* TX Descriptor Length - RW */ +#define E1000_TDH 0x03810 /* TX Descriptor Head - RW */ +#define E1000_TDT 0x03818 /* TX Descripotr Tail - RW */ +#define E1000_TIDV 0x03820 /* TX Interrupt Delay Value - RW */ +#define E1000_TXDCTL 0x03828 /* TX Descriptor Control - RW */ +#define E1000_TADV 0x0382C /* TX Interrupt Absolute Delay Val - RW */ +#define E1000_TSPMT 0x03830 /* TCP Segmentation PAD & Min Threshold - RW */ +#define E1000_TARC0 0x03840 /* TX Arbitration Count (0) */ +#define E1000_TDBAL1 0x03900 /* TX Desc Base Address Low (1) - RW */ +#define E1000_TDBAH1 0x03904 /* TX Desc Base Address High (1) - RW */ +#define E1000_TDLEN1 0x03908 /* TX Desc Length (1) - RW */ +#define E1000_TDH1 0x03910 /* TX Desc Head (1) - RW */ +#define E1000_TDT1 0x03918 /* TX Desc Tail (1) - RW */ +#define E1000_TXDCTL1 0x03928 /* TX Descriptor Control (1) - RW */ +#define E1000_TARC1 0x03940 /* TX Arbitration Count (1) */ +#define E1000_CRCERRS 0x04000 /* CRC Error Count - R/clr */ +#define E1000_ALGNERRC 0x04004 /* Alignment Error Count - R/clr */ +#define E1000_SYMERRS 0x04008 /* Symbol Error Count - R/clr */ +#define E1000_RXERRC 0x0400C /* Receive Error Count - R/clr */ +#define E1000_MPC 0x04010 /* Missed Packet Count - R/clr */ +#define E1000_SCC 0x04014 /* Single Collision Count - R/clr */ +#define E1000_ECOL 0x04018 /* Excessive Collision Count - R/clr */ +#define E1000_MCC 0x0401C /* Multiple Collision Count - R/clr */ +#define E1000_LATECOL 0x04020 /* Late Collision Count - R/clr */ +#define E1000_COLC 0x04028 /* Collision Count - R/clr */ +#define E1000_DC 0x04030 /* Defer Count - R/clr */ +#define E1000_TNCRS 0x04034 /* TX-No CRS - R/clr */ +#define E1000_SEC 0x04038 /* Sequence Error Count - R/clr */ +#define E1000_CEXTERR 0x0403C /* Carrier Extension Error Count - R/clr */ +#define E1000_RLEC 0x04040 /* Receive Length Error Count - R/clr */ +#define E1000_XONRXC 0x04048 /* XON RX Count - R/clr */ +#define E1000_XONTXC 0x0404C /* XON TX Count - R/clr */ +#define E1000_XOFFRXC 0x04050 /* XOFF RX Count - R/clr */ +#define E1000_XOFFTXC 0x04054 /* XOFF TX Count - R/clr */ +#define E1000_FCRUC 0x04058 /* Flow Control RX Unsupported Count- R/clr */ +#define E1000_PRC64 0x0405C /* Packets RX (64 bytes) - R/clr */ +#define E1000_PRC127 0x04060 /* Packets RX (65-127 bytes) - R/clr */ +#define E1000_PRC255 0x04064 /* Packets RX (128-255 bytes) - R/clr */ +#define E1000_PRC511 0x04068 /* Packets RX (255-511 bytes) - R/clr */ +#define E1000_PRC1023 0x0406C /* Packets RX (512-1023 bytes) - R/clr */ +#define E1000_PRC1522 0x04070 /* Packets RX (1024-1522 bytes) - R/clr */ +#define E1000_GPRC 0x04074 /* Good Packets RX Count - R/clr */ +#define E1000_BPRC 0x04078 /* Broadcast Packets RX Count - R/clr */ +#define E1000_MPRC 0x0407C /* Multicast Packets RX Count - R/clr */ +#define E1000_GPTC 0x04080 /* Good Packets TX Count - R/clr */ +#define E1000_GORCL 0x04088 /* Good Octets RX Count Low - R/clr */ +#define E1000_GORCH 0x0408C /* Good Octets RX Count High - R/clr */ +#define E1000_GOTCL 0x04090 /* Good Octets TX Count Low - R/clr */ +#define E1000_GOTCH 0x04094 /* Good Octets TX Count High - R/clr */ +#define E1000_RNBC 0x040A0 /* RX No Buffers Count - R/clr */ +#define E1000_RUC 0x040A4 /* RX Undersize Count - R/clr */ +#define E1000_RFC 0x040A8 /* RX Fragment Count - R/clr */ +#define E1000_ROC 0x040AC /* RX Oversize Count - R/clr */ +#define E1000_RJC 0x040B0 /* RX Jabber Count - R/clr */ +#define E1000_MGTPRC 0x040B4 /* Management Packets RX Count - R/clr */ +#define E1000_MGTPDC 0x040B8 /* Management Packets Dropped Count - R/clr */ +#define E1000_MGTPTC 0x040BC /* Management Packets TX Count - R/clr */ +#define E1000_TORL 0x040C0 /* Total Octets RX Low - R/clr */ +#define E1000_TORH 0x040C4 /* Total Octets RX High - R/clr */ +#define E1000_TOTL 0x040C8 /* Total Octets TX Low - R/clr */ +#define E1000_TOTH 0x040CC /* Total Octets TX High - R/clr */ +#define E1000_TPR 0x040D0 /* Total Packets RX - R/clr */ +#define E1000_TPT 0x040D4 /* Total Packets TX - R/clr */ +#define E1000_PTC64 0x040D8 /* Packets TX (64 bytes) - R/clr */ +#define E1000_PTC127 0x040DC /* Packets TX (65-127 bytes) - R/clr */ +#define E1000_PTC255 0x040E0 /* Packets TX (128-255 bytes) - R/clr */ +#define E1000_PTC511 0x040E4 /* Packets TX (256-511 bytes) - R/clr */ +#define E1000_PTC1023 0x040E8 /* Packets TX (512-1023 bytes) - R/clr */ +#define E1000_PTC1522 0x040EC /* Packets TX (1024-1522 Bytes) - R/clr */ +#define E1000_MPTC 0x040F0 /* Multicast Packets TX Count - R/clr */ +#define E1000_BPTC 0x040F4 /* Broadcast Packets TX Count - R/clr */ +#define E1000_TSCTC 0x040F8 /* TCP Segmentation Context TX - R/clr */ +#define E1000_TSCTFC 0x040FC /* TCP Segmentation Context TX Fail - R/clr */ +#define E1000_IAC 0x04100 /* Interrupt Assertion Count */ +#define E1000_ICRXPTC 0x04104 /* Interrupt Cause Rx Packet Timer Expire Count */ +#define E1000_ICRXATC 0x04108 /* Interrupt Cause Rx Absolute Timer Expire Count */ +#define E1000_ICTXPTC 0x0410C /* Interrupt Cause Tx Packet Timer Expire Count */ +#define E1000_ICTXATC 0x04110 /* Interrupt Cause Tx Absolute Timer Expire Count */ +#define E1000_ICTXQEC 0x04118 /* Interrupt Cause Tx Queue Empty Count */ +#define E1000_ICTXQMTC 0x0411C /* Interrupt Cause Tx Queue Minimum Threshold Count */ +#define E1000_ICRXDMTC 0x04120 /* Interrupt Cause Rx Descriptor Minimum Threshold Count */ +#define E1000_ICRXOC 0x04124 /* Interrupt Cause Receiver Overrun Count */ +#define E1000_RXCSUM 0x05000 /* RX Checksum Control - RW */ +#define E1000_RFCTL 0x05008 /* Receive Filter Control */ +#define E1000_MTA 0x05200 /* Multicast Table Array - RW Array */ +#define E1000_RA 0x05400 /* Receive Address - RW Array */ +#define E1000_VFTA 0x05600 /* VLAN Filter Table Array - RW Array */ +#define E1000_WUC 0x05800 /* Wakeup Control - RW */ +#define E1000_WUFC 0x05808 /* Wakeup Filter Control - RW */ +#define E1000_WUS 0x05810 /* Wakeup Status - RO */ +#define E1000_MANC 0x05820 /* Management Control - RW */ +#define E1000_IPAV 0x05838 /* IP Address Valid - RW */ +#define E1000_IP4AT 0x05840 /* IPv4 Address Table - RW Array */ +#define E1000_IP6AT 0x05880 /* IPv6 Address Table - RW Array */ +#define E1000_WUPL 0x05900 /* Wakeup Packet Length - RW */ +#define E1000_WUPM 0x05A00 /* Wakeup Packet Memory - RO A */ +#define E1000_FFLT 0x05F00 /* Flexible Filter Length Table - RW Array */ +#define E1000_HOST_IF 0x08800 /* Host Interface */ +#define E1000_FFMT 0x09000 /* Flexible Filter Mask Table - RW Array */ +#define E1000_FFVT 0x09800 /* Flexible Filter Value Table - RW Array */ + +#define E1000_KUMCTRLSTA 0x00034 /* MAC-PHY interface - RW */ +#define E1000_MDPHYA 0x0003C /* PHY address - RW */ +#define E1000_MANC2H 0x05860 /* Managment Control To Host - RW */ +#define E1000_SW_FW_SYNC 0x05B5C /* Software-Firmware Synchronization - RW */ + +#define E1000_GCR 0x05B00 /* PCI-Ex Control */ +#define E1000_GSCL_1 0x05B10 /* PCI-Ex Statistic Control #1 */ +#define E1000_GSCL_2 0x05B14 /* PCI-Ex Statistic Control #2 */ +#define E1000_GSCL_3 0x05B18 /* PCI-Ex Statistic Control #3 */ +#define E1000_GSCL_4 0x05B1C /* PCI-Ex Statistic Control #4 */ +#define E1000_FACTPS 0x05B30 /* Function Active and Power State to MNG */ +#define E1000_SWSM 0x05B50 /* SW Semaphore */ +#define E1000_FWSM 0x05B54 /* FW Semaphore */ +#define E1000_FFLT_DBG 0x05F04 /* Debug Register */ +#define E1000_HICR 0x08F00 /* Host Interface Control */ /* RSS registers */ -#define E1000_CPUVEC 0x02C10 /* CPU Vector Register - RW */ -#define E1000_MRQC 0x05818 /* Multiple Receive Control - RW */ -#define E1000_RETA 0x05C00 /* Redirection Table - RW Array */ -#define E1000_RSSRK 0x05C80 /* RSS Random Key - RW Array */ -#define E1000_RSSIM 0x05864 /* RSS Interrupt Mask */ -#define E1000_RSSIR 0x05868 /* RSS Interrupt Request */ +#define E1000_CPUVEC 0x02C10 /* CPU Vector Register - RW */ +#define E1000_MRQC 0x05818 /* Multiple Receive Control - RW */ +#define E1000_RETA 0x05C00 /* Redirection Table - RW Array */ +#define E1000_RSSRK 0x05C80 /* RSS Random Key - RW Array */ +#define E1000_RSSIM 0x05864 /* RSS Interrupt Mask */ +#define E1000_RSSIR 0x05868 /* RSS Interrupt Request */ /* Register Set (82542) * * Some of the 82542 registers are located at different offsets than they are @@ -1123,19 +1043,19 @@ struct e1000_ffvt_entry { #define E1000_82542_RDLEN0 E1000_82542_RDLEN #define E1000_82542_RDH0 E1000_82542_RDH #define E1000_82542_RDT0 E1000_82542_RDT -#define E1000_82542_SRRCTL(_n) (0x280C + ((_n) << 8)) /* Split and Replication - * RX Control - RW */ +#define E1000_82542_SRRCTL(_n) (0x280C + ((_n) << 8)) /* Split and Replication + * RX Control - RW */ #define E1000_82542_DCA_RXCTRL(_n) (0x02814 + ((_n) << 8)) -#define E1000_82542_RDBAH3 0x02B04 /* RX Desc Base High Queue 3 - RW */ -#define E1000_82542_RDBAL3 0x02B00 /* RX Desc Low Queue 3 - RW */ -#define E1000_82542_RDLEN3 0x02B08 /* RX Desc Length Queue 3 - RW */ -#define E1000_82542_RDH3 0x02B10 /* RX Desc Head Queue 3 - RW */ -#define E1000_82542_RDT3 0x02B18 /* RX Desc Tail Queue 3 - RW */ -#define E1000_82542_RDBAL2 0x02A00 /* RX Desc Base Low Queue 2 - RW */ -#define E1000_82542_RDBAH2 0x02A04 /* RX Desc Base High Queue 2 - RW */ -#define E1000_82542_RDLEN2 0x02A08 /* RX Desc Length Queue 2 - RW */ -#define E1000_82542_RDH2 0x02A10 /* RX Desc Head Queue 2 - RW */ -#define E1000_82542_RDT2 0x02A18 /* RX Desc Tail Queue 2 - RW */ +#define E1000_82542_RDBAH3 0x02B04 /* RX Desc Base High Queue 3 - RW */ +#define E1000_82542_RDBAL3 0x02B00 /* RX Desc Low Queue 3 - RW */ +#define E1000_82542_RDLEN3 0x02B08 /* RX Desc Length Queue 3 - RW */ +#define E1000_82542_RDH3 0x02B10 /* RX Desc Head Queue 3 - RW */ +#define E1000_82542_RDT3 0x02B18 /* RX Desc Tail Queue 3 - RW */ +#define E1000_82542_RDBAL2 0x02A00 /* RX Desc Base Low Queue 2 - RW */ +#define E1000_82542_RDBAH2 0x02A04 /* RX Desc Base High Queue 2 - RW */ +#define E1000_82542_RDLEN2 0x02A08 /* RX Desc Length Queue 2 - RW */ +#define E1000_82542_RDH2 0x02A10 /* RX Desc Head Queue 2 - RW */ +#define E1000_82542_RDT2 0x02A18 /* RX Desc Tail Queue 2 - RW */ #define E1000_82542_RDTR1 0x00130 #define E1000_82542_RDBAL1 0x00138 #define E1000_82542_RDBAH1 0x0013C @@ -1302,288 +1222,281 @@ struct e1000_ffvt_entry { #define E1000_82542_RSSIR E1000_RSSIR #define E1000_82542_KUMCTRLSTA E1000_KUMCTRLSTA #define E1000_82542_SW_FW_SYNC E1000_SW_FW_SYNC -#define E1000_82542_MANC2H E1000_MANC2H /* Statistics counters collected by the MAC */ struct e1000_hw_stats { - u64 crcerrs; - u64 algnerrc; - u64 symerrs; - u64 rxerrc; - u64 txerrc; - u64 mpc; - u64 scc; - u64 ecol; - u64 mcc; - u64 latecol; - u64 colc; - u64 dc; - u64 tncrs; - u64 sec; - u64 cexterr; - u64 rlec; - u64 xonrxc; - u64 xontxc; - u64 xoffrxc; - u64 xofftxc; - u64 fcruc; - u64 prc64; - u64 prc127; - u64 prc255; - u64 prc511; - u64 prc1023; - u64 prc1522; - u64 gprc; - u64 bprc; - u64 mprc; - u64 gptc; - u64 gorcl; - u64 gorch; - u64 gotcl; - u64 gotch; - u64 rnbc; - u64 ruc; - u64 rfc; - u64 roc; - u64 rlerrc; - u64 rjc; - u64 mgprc; - u64 mgpdc; - u64 mgptc; - u64 torl; - u64 torh; - u64 totl; - u64 toth; - u64 tpr; - u64 tpt; - u64 ptc64; - u64 ptc127; - u64 ptc255; - u64 ptc511; - u64 ptc1023; - u64 ptc1522; - u64 mptc; - u64 bptc; - u64 tsctc; - u64 tsctfc; - u64 iac; - u64 icrxptc; - u64 icrxatc; - u64 ictxptc; - u64 ictxatc; - u64 ictxqec; - u64 ictxqmtc; - u64 icrxdmtc; - u64 icrxoc; + u64 crcerrs; + u64 algnerrc; + u64 symerrs; + u64 rxerrc; + u64 txerrc; + u64 mpc; + u64 scc; + u64 ecol; + u64 mcc; + u64 latecol; + u64 colc; + u64 dc; + u64 tncrs; + u64 sec; + u64 cexterr; + u64 rlec; + u64 xonrxc; + u64 xontxc; + u64 xoffrxc; + u64 xofftxc; + u64 fcruc; + u64 prc64; + u64 prc127; + u64 prc255; + u64 prc511; + u64 prc1023; + u64 prc1522; + u64 gprc; + u64 bprc; + u64 mprc; + u64 gptc; + u64 gorcl; + u64 gorch; + u64 gotcl; + u64 gotch; + u64 rnbc; + u64 ruc; + u64 rfc; + u64 roc; + u64 rlerrc; + u64 rjc; + u64 mgprc; + u64 mgpdc; + u64 mgptc; + u64 torl; + u64 torh; + u64 totl; + u64 toth; + u64 tpr; + u64 tpt; + u64 ptc64; + u64 ptc127; + u64 ptc255; + u64 ptc511; + u64 ptc1023; + u64 ptc1522; + u64 mptc; + u64 bptc; + u64 tsctc; + u64 tsctfc; + u64 iac; + u64 icrxptc; + u64 icrxatc; + u64 ictxptc; + u64 ictxatc; + u64 ictxqec; + u64 ictxqmtc; + u64 icrxdmtc; + u64 icrxoc; }; /* Structure containing variables used by the shared code (e1000_hw.c) */ struct e1000_hw { - u8 __iomem *hw_addr; - u8 __iomem *flash_address; - e1000_mac_type mac_type; - e1000_phy_type phy_type; - u32 phy_init_script; - e1000_media_type media_type; - void *back; - struct e1000_shadow_ram *eeprom_shadow_ram; - u32 flash_bank_size; - u32 flash_base_addr; - e1000_fc_type fc; - e1000_bus_speed bus_speed; - e1000_bus_width bus_width; - e1000_bus_type bus_type; + u8 __iomem *hw_addr; + u8 __iomem *flash_address; + e1000_mac_type mac_type; + e1000_phy_type phy_type; + u32 phy_init_script; + e1000_media_type media_type; + void *back; + struct e1000_shadow_ram *eeprom_shadow_ram; + u32 flash_bank_size; + u32 flash_base_addr; + e1000_fc_type fc; + e1000_bus_speed bus_speed; + e1000_bus_width bus_width; + e1000_bus_type bus_type; struct e1000_eeprom_info eeprom; - e1000_ms_type master_slave; - e1000_ms_type original_master_slave; - e1000_ffe_config ffe_config_state; - u32 asf_firmware_present; - u32 eeprom_semaphore_present; - u32 swfw_sync_present; - u32 swfwhw_semaphore_present; - unsigned long io_base; - u32 phy_id; - u32 phy_revision; - u32 phy_addr; - u32 original_fc; - u32 txcw; - u32 autoneg_failed; - u32 max_frame_size; - u32 min_frame_size; - u32 mc_filter_type; - u32 num_mc_addrs; - u32 collision_delta; - u32 tx_packet_delta; - u32 ledctl_default; - u32 ledctl_mode1; - u32 ledctl_mode2; - bool tx_pkt_filtering; + e1000_ms_type master_slave; + e1000_ms_type original_master_slave; + e1000_ffe_config ffe_config_state; + u32 asf_firmware_present; + u32 eeprom_semaphore_present; + unsigned long io_base; + u32 phy_id; + u32 phy_revision; + u32 phy_addr; + u32 original_fc; + u32 txcw; + u32 autoneg_failed; + u32 max_frame_size; + u32 min_frame_size; + u32 mc_filter_type; + u32 num_mc_addrs; + u32 collision_delta; + u32 tx_packet_delta; + u32 ledctl_default; + u32 ledctl_mode1; + u32 ledctl_mode2; + bool tx_pkt_filtering; struct e1000_host_mng_dhcp_cookie mng_cookie; - u16 phy_spd_default; - u16 autoneg_advertised; - u16 pci_cmd_word; - u16 fc_high_water; - u16 fc_low_water; - u16 fc_pause_time; - u16 current_ifs_val; - u16 ifs_min_val; - u16 ifs_max_val; - u16 ifs_step_size; - u16 ifs_ratio; - u16 device_id; - u16 vendor_id; - u16 subsystem_id; - u16 subsystem_vendor_id; - u8 revision_id; - u8 autoneg; - u8 mdix; - u8 forced_speed_duplex; - u8 wait_autoneg_complete; - u8 dma_fairness; - u8 mac_addr[NODE_ADDRESS_SIZE]; - u8 perm_mac_addr[NODE_ADDRESS_SIZE]; - bool disable_polarity_correction; - bool speed_downgraded; - e1000_smart_speed smart_speed; - e1000_dsp_config dsp_config_state; - bool get_link_status; - bool serdes_link_down; - bool tbi_compatibility_en; - bool tbi_compatibility_on; - bool laa_is_present; - bool phy_reset_disable; - bool initialize_hw_bits_disable; - bool fc_send_xon; - bool fc_strict_ieee; - bool report_tx_early; - bool adaptive_ifs; - bool ifs_params_forced; - bool in_ifs_mode; - bool mng_reg_access_disabled; - bool leave_av_bit_off; - bool kmrn_lock_loss_workaround_disabled; - bool bad_tx_carr_stats_fd; - bool has_manc2h; - bool rx_needs_kicking; - bool has_smbus; + u16 phy_spd_default; + u16 autoneg_advertised; + u16 pci_cmd_word; + u16 fc_high_water; + u16 fc_low_water; + u16 fc_pause_time; + u16 current_ifs_val; + u16 ifs_min_val; + u16 ifs_max_val; + u16 ifs_step_size; + u16 ifs_ratio; + u16 device_id; + u16 vendor_id; + u16 subsystem_id; + u16 subsystem_vendor_id; + u8 revision_id; + u8 autoneg; + u8 mdix; + u8 forced_speed_duplex; + u8 wait_autoneg_complete; + u8 dma_fairness; + u8 mac_addr[NODE_ADDRESS_SIZE]; + u8 perm_mac_addr[NODE_ADDRESS_SIZE]; + bool disable_polarity_correction; + bool speed_downgraded; + e1000_smart_speed smart_speed; + e1000_dsp_config dsp_config_state; + bool get_link_status; + bool serdes_has_link; + bool tbi_compatibility_en; + bool tbi_compatibility_on; + bool laa_is_present; + bool phy_reset_disable; + bool initialize_hw_bits_disable; + bool fc_send_xon; + bool fc_strict_ieee; + bool report_tx_early; + bool adaptive_ifs; + bool ifs_params_forced; + bool in_ifs_mode; + bool mng_reg_access_disabled; + bool leave_av_bit_off; + bool bad_tx_carr_stats_fd; + bool has_smbus; }; - -#define E1000_EEPROM_SWDPIN0 0x0001 /* SWDPIN 0 EEPROM Value */ -#define E1000_EEPROM_LED_LOGIC 0x0020 /* Led Logic Word */ -#define E1000_EEPROM_RW_REG_DATA 16 /* Offset to data in EEPROM read/write registers */ -#define E1000_EEPROM_RW_REG_DONE 2 /* Offset to READ/WRITE done bit */ -#define E1000_EEPROM_RW_REG_START 1 /* First bit for telling part to start operation */ -#define E1000_EEPROM_RW_ADDR_SHIFT 2 /* Shift to the address bits */ -#define E1000_EEPROM_POLL_WRITE 1 /* Flag for polling for write complete */ -#define E1000_EEPROM_POLL_READ 0 /* Flag for polling for read complete */ +#define E1000_EEPROM_SWDPIN0 0x0001 /* SWDPIN 0 EEPROM Value */ +#define E1000_EEPROM_LED_LOGIC 0x0020 /* Led Logic Word */ +#define E1000_EEPROM_RW_REG_DATA 16 /* Offset to data in EEPROM read/write registers */ +#define E1000_EEPROM_RW_REG_DONE 2 /* Offset to READ/WRITE done bit */ +#define E1000_EEPROM_RW_REG_START 1 /* First bit for telling part to start operation */ +#define E1000_EEPROM_RW_ADDR_SHIFT 2 /* Shift to the address bits */ +#define E1000_EEPROM_POLL_WRITE 1 /* Flag for polling for write complete */ +#define E1000_EEPROM_POLL_READ 0 /* Flag for polling for read complete */ /* Register Bit Masks */ /* Device Control */ -#define E1000_CTRL_FD 0x00000001 /* Full duplex.0=half; 1=full */ -#define E1000_CTRL_BEM 0x00000002 /* Endian Mode.0=little,1=big */ -#define E1000_CTRL_PRIOR 0x00000004 /* Priority on PCI. 0=rx,1=fair */ -#define E1000_CTRL_GIO_MASTER_DISABLE 0x00000004 /*Blocks new Master requests */ -#define E1000_CTRL_LRST 0x00000008 /* Link reset. 0=normal,1=reset */ -#define E1000_CTRL_TME 0x00000010 /* Test mode. 0=normal,1=test */ -#define E1000_CTRL_SLE 0x00000020 /* Serial Link on 0=dis,1=en */ -#define E1000_CTRL_ASDE 0x00000020 /* Auto-speed detect enable */ -#define E1000_CTRL_SLU 0x00000040 /* Set link up (Force Link) */ -#define E1000_CTRL_ILOS 0x00000080 /* Invert Loss-Of Signal */ -#define E1000_CTRL_SPD_SEL 0x00000300 /* Speed Select Mask */ -#define E1000_CTRL_SPD_10 0x00000000 /* Force 10Mb */ -#define E1000_CTRL_SPD_100 0x00000100 /* Force 100Mb */ -#define E1000_CTRL_SPD_1000 0x00000200 /* Force 1Gb */ -#define E1000_CTRL_BEM32 0x00000400 /* Big Endian 32 mode */ -#define E1000_CTRL_FRCSPD 0x00000800 /* Force Speed */ -#define E1000_CTRL_FRCDPX 0x00001000 /* Force Duplex */ -#define E1000_CTRL_D_UD_EN 0x00002000 /* Dock/Undock enable */ -#define E1000_CTRL_D_UD_POLARITY 0x00004000 /* Defined polarity of Dock/Undock indication in SDP[0] */ -#define E1000_CTRL_FORCE_PHY_RESET 0x00008000 /* Reset both PHY ports, through PHYRST_N pin */ -#define E1000_CTRL_EXT_LINK_EN 0x00010000 /* enable link status from external LINK_0 and LINK_1 pins */ -#define E1000_CTRL_SWDPIN0 0x00040000 /* SWDPIN 0 value */ -#define E1000_CTRL_SWDPIN1 0x00080000 /* SWDPIN 1 value */ -#define E1000_CTRL_SWDPIN2 0x00100000 /* SWDPIN 2 value */ -#define E1000_CTRL_SWDPIN3 0x00200000 /* SWDPIN 3 value */ -#define E1000_CTRL_SWDPIO0 0x00400000 /* SWDPIN 0 Input or output */ -#define E1000_CTRL_SWDPIO1 0x00800000 /* SWDPIN 1 input or output */ -#define E1000_CTRL_SWDPIO2 0x01000000 /* SWDPIN 2 input or output */ -#define E1000_CTRL_SWDPIO3 0x02000000 /* SWDPIN 3 input or output */ -#define E1000_CTRL_RST 0x04000000 /* Global reset */ -#define E1000_CTRL_RFCE 0x08000000 /* Receive Flow Control enable */ -#define E1000_CTRL_TFCE 0x10000000 /* Transmit flow control enable */ -#define E1000_CTRL_RTE 0x20000000 /* Routing tag enable */ -#define E1000_CTRL_VME 0x40000000 /* IEEE VLAN mode enable */ -#define E1000_CTRL_PHY_RST 0x80000000 /* PHY Reset */ -#define E1000_CTRL_SW2FW_INT 0x02000000 /* Initiate an interrupt to manageability engine */ +#define E1000_CTRL_FD 0x00000001 /* Full duplex.0=half; 1=full */ +#define E1000_CTRL_BEM 0x00000002 /* Endian Mode.0=little,1=big */ +#define E1000_CTRL_PRIOR 0x00000004 /* Priority on PCI. 0=rx,1=fair */ +#define E1000_CTRL_GIO_MASTER_DISABLE 0x00000004 /*Blocks new Master requests */ +#define E1000_CTRL_LRST 0x00000008 /* Link reset. 0=normal,1=reset */ +#define E1000_CTRL_TME 0x00000010 /* Test mode. 0=normal,1=test */ +#define E1000_CTRL_SLE 0x00000020 /* Serial Link on 0=dis,1=en */ +#define E1000_CTRL_ASDE 0x00000020 /* Auto-speed detect enable */ +#define E1000_CTRL_SLU 0x00000040 /* Set link up (Force Link) */ +#define E1000_CTRL_ILOS 0x00000080 /* Invert Loss-Of Signal */ +#define E1000_CTRL_SPD_SEL 0x00000300 /* Speed Select Mask */ +#define E1000_CTRL_SPD_10 0x00000000 /* Force 10Mb */ +#define E1000_CTRL_SPD_100 0x00000100 /* Force 100Mb */ +#define E1000_CTRL_SPD_1000 0x00000200 /* Force 1Gb */ +#define E1000_CTRL_BEM32 0x00000400 /* Big Endian 32 mode */ +#define E1000_CTRL_FRCSPD 0x00000800 /* Force Speed */ +#define E1000_CTRL_FRCDPX 0x00001000 /* Force Duplex */ +#define E1000_CTRL_D_UD_EN 0x00002000 /* Dock/Undock enable */ +#define E1000_CTRL_D_UD_POLARITY 0x00004000 /* Defined polarity of Dock/Undock indication in SDP[0] */ +#define E1000_CTRL_FORCE_PHY_RESET 0x00008000 /* Reset both PHY ports, through PHYRST_N pin */ +#define E1000_CTRL_EXT_LINK_EN 0x00010000 /* enable link status from external LINK_0 and LINK_1 pins */ +#define E1000_CTRL_SWDPIN0 0x00040000 /* SWDPIN 0 value */ +#define E1000_CTRL_SWDPIN1 0x00080000 /* SWDPIN 1 value */ +#define E1000_CTRL_SWDPIN2 0x00100000 /* SWDPIN 2 value */ +#define E1000_CTRL_SWDPIN3 0x00200000 /* SWDPIN 3 value */ +#define E1000_CTRL_SWDPIO0 0x00400000 /* SWDPIN 0 Input or output */ +#define E1000_CTRL_SWDPIO1 0x00800000 /* SWDPIN 1 input or output */ +#define E1000_CTRL_SWDPIO2 0x01000000 /* SWDPIN 2 input or output */ +#define E1000_CTRL_SWDPIO3 0x02000000 /* SWDPIN 3 input or output */ +#define E1000_CTRL_RST 0x04000000 /* Global reset */ +#define E1000_CTRL_RFCE 0x08000000 /* Receive Flow Control enable */ +#define E1000_CTRL_TFCE 0x10000000 /* Transmit flow control enable */ +#define E1000_CTRL_RTE 0x20000000 /* Routing tag enable */ +#define E1000_CTRL_VME 0x40000000 /* IEEE VLAN mode enable */ +#define E1000_CTRL_PHY_RST 0x80000000 /* PHY Reset */ +#define E1000_CTRL_SW2FW_INT 0x02000000 /* Initiate an interrupt to manageability engine */ /* Device Status */ -#define E1000_STATUS_FD 0x00000001 /* Full duplex.0=half,1=full */ -#define E1000_STATUS_LU 0x00000002 /* Link up.0=no,1=link */ -#define E1000_STATUS_FUNC_MASK 0x0000000C /* PCI Function Mask */ +#define E1000_STATUS_FD 0x00000001 /* Full duplex.0=half,1=full */ +#define E1000_STATUS_LU 0x00000002 /* Link up.0=no,1=link */ +#define E1000_STATUS_FUNC_MASK 0x0000000C /* PCI Function Mask */ #define E1000_STATUS_FUNC_SHIFT 2 -#define E1000_STATUS_FUNC_0 0x00000000 /* Function 0 */ -#define E1000_STATUS_FUNC_1 0x00000004 /* Function 1 */ -#define E1000_STATUS_TXOFF 0x00000010 /* transmission paused */ -#define E1000_STATUS_TBIMODE 0x00000020 /* TBI mode */ +#define E1000_STATUS_FUNC_0 0x00000000 /* Function 0 */ +#define E1000_STATUS_FUNC_1 0x00000004 /* Function 1 */ +#define E1000_STATUS_TXOFF 0x00000010 /* transmission paused */ +#define E1000_STATUS_TBIMODE 0x00000020 /* TBI mode */ #define E1000_STATUS_SPEED_MASK 0x000000C0 -#define E1000_STATUS_SPEED_10 0x00000000 /* Speed 10Mb/s */ -#define E1000_STATUS_SPEED_100 0x00000040 /* Speed 100Mb/s */ -#define E1000_STATUS_SPEED_1000 0x00000080 /* Speed 1000Mb/s */ -#define E1000_STATUS_LAN_INIT_DONE 0x00000200 /* Lan Init Completion - by EEPROM/Flash */ -#define E1000_STATUS_ASDV 0x00000300 /* Auto speed detect value */ -#define E1000_STATUS_DOCK_CI 0x00000800 /* Change in Dock/Undock state. Clear on write '0'. */ -#define E1000_STATUS_GIO_MASTER_ENABLE 0x00080000 /* Status of Master requests. */ -#define E1000_STATUS_MTXCKOK 0x00000400 /* MTX clock running OK */ -#define E1000_STATUS_PCI66 0x00000800 /* In 66Mhz slot */ -#define E1000_STATUS_BUS64 0x00001000 /* In 64 bit slot */ -#define E1000_STATUS_PCIX_MODE 0x00002000 /* PCI-X mode */ -#define E1000_STATUS_PCIX_SPEED 0x0000C000 /* PCI-X bus speed */ -#define E1000_STATUS_BMC_SKU_0 0x00100000 /* BMC USB redirect disabled */ -#define E1000_STATUS_BMC_SKU_1 0x00200000 /* BMC SRAM disabled */ -#define E1000_STATUS_BMC_SKU_2 0x00400000 /* BMC SDRAM disabled */ -#define E1000_STATUS_BMC_CRYPTO 0x00800000 /* BMC crypto disabled */ -#define E1000_STATUS_BMC_LITE 0x01000000 /* BMC external code execution disabled */ -#define E1000_STATUS_RGMII_ENABLE 0x02000000 /* RGMII disabled */ +#define E1000_STATUS_SPEED_10 0x00000000 /* Speed 10Mb/s */ +#define E1000_STATUS_SPEED_100 0x00000040 /* Speed 100Mb/s */ +#define E1000_STATUS_SPEED_1000 0x00000080 /* Speed 1000Mb/s */ +#define E1000_STATUS_LAN_INIT_DONE 0x00000200 /* Lan Init Completion + by EEPROM/Flash */ +#define E1000_STATUS_ASDV 0x00000300 /* Auto speed detect value */ +#define E1000_STATUS_DOCK_CI 0x00000800 /* Change in Dock/Undock state. Clear on write '0'. */ +#define E1000_STATUS_GIO_MASTER_ENABLE 0x00080000 /* Status of Master requests. */ +#define E1000_STATUS_MTXCKOK 0x00000400 /* MTX clock running OK */ +#define E1000_STATUS_PCI66 0x00000800 /* In 66Mhz slot */ +#define E1000_STATUS_BUS64 0x00001000 /* In 64 bit slot */ +#define E1000_STATUS_PCIX_MODE 0x00002000 /* PCI-X mode */ +#define E1000_STATUS_PCIX_SPEED 0x0000C000 /* PCI-X bus speed */ +#define E1000_STATUS_BMC_SKU_0 0x00100000 /* BMC USB redirect disabled */ +#define E1000_STATUS_BMC_SKU_1 0x00200000 /* BMC SRAM disabled */ +#define E1000_STATUS_BMC_SKU_2 0x00400000 /* BMC SDRAM disabled */ +#define E1000_STATUS_BMC_CRYPTO 0x00800000 /* BMC crypto disabled */ +#define E1000_STATUS_BMC_LITE 0x01000000 /* BMC external code execution disabled */ +#define E1000_STATUS_RGMII_ENABLE 0x02000000 /* RGMII disabled */ #define E1000_STATUS_FUSE_8 0x04000000 #define E1000_STATUS_FUSE_9 0x08000000 -#define E1000_STATUS_SERDES0_DIS 0x10000000 /* SERDES disabled on port 0 */ -#define E1000_STATUS_SERDES1_DIS 0x20000000 /* SERDES disabled on port 1 */ +#define E1000_STATUS_SERDES0_DIS 0x10000000 /* SERDES disabled on port 0 */ +#define E1000_STATUS_SERDES1_DIS 0x20000000 /* SERDES disabled on port 1 */ -/* Constants used to intrepret the masked PCI-X bus speed. */ -#define E1000_STATUS_PCIX_SPEED_66 0x00000000 /* PCI-X bus speed 50-66 MHz */ -#define E1000_STATUS_PCIX_SPEED_100 0x00004000 /* PCI-X bus speed 66-100 MHz */ -#define E1000_STATUS_PCIX_SPEED_133 0x00008000 /* PCI-X bus speed 100-133 MHz */ +/* Constants used to interpret the masked PCI-X bus speed. */ +#define E1000_STATUS_PCIX_SPEED_66 0x00000000 /* PCI-X bus speed 50-66 MHz */ +#define E1000_STATUS_PCIX_SPEED_100 0x00004000 /* PCI-X bus speed 66-100 MHz */ +#define E1000_STATUS_PCIX_SPEED_133 0x00008000 /* PCI-X bus speed 100-133 MHz */ /* EEPROM/Flash Control */ -#define E1000_EECD_SK 0x00000001 /* EEPROM Clock */ -#define E1000_EECD_CS 0x00000002 /* EEPROM Chip Select */ -#define E1000_EECD_DI 0x00000004 /* EEPROM Data In */ -#define E1000_EECD_DO 0x00000008 /* EEPROM Data Out */ +#define E1000_EECD_SK 0x00000001 /* EEPROM Clock */ +#define E1000_EECD_CS 0x00000002 /* EEPROM Chip Select */ +#define E1000_EECD_DI 0x00000004 /* EEPROM Data In */ +#define E1000_EECD_DO 0x00000008 /* EEPROM Data Out */ #define E1000_EECD_FWE_MASK 0x00000030 -#define E1000_EECD_FWE_DIS 0x00000010 /* Disable FLASH writes */ -#define E1000_EECD_FWE_EN 0x00000020 /* Enable FLASH writes */ +#define E1000_EECD_FWE_DIS 0x00000010 /* Disable FLASH writes */ +#define E1000_EECD_FWE_EN 0x00000020 /* Enable FLASH writes */ #define E1000_EECD_FWE_SHIFT 4 -#define E1000_EECD_REQ 0x00000040 /* EEPROM Access Request */ -#define E1000_EECD_GNT 0x00000080 /* EEPROM Access Grant */ -#define E1000_EECD_PRES 0x00000100 /* EEPROM Present */ -#define E1000_EECD_SIZE 0x00000200 /* EEPROM Size (0=64 word 1=256 word) */ -#define E1000_EECD_ADDR_BITS 0x00000400 /* EEPROM Addressing bits based on type - * (0-small, 1-large) */ -#define E1000_EECD_TYPE 0x00002000 /* EEPROM Type (1-SPI, 0-Microwire) */ +#define E1000_EECD_REQ 0x00000040 /* EEPROM Access Request */ +#define E1000_EECD_GNT 0x00000080 /* EEPROM Access Grant */ +#define E1000_EECD_PRES 0x00000100 /* EEPROM Present */ +#define E1000_EECD_SIZE 0x00000200 /* EEPROM Size (0=64 word 1=256 word) */ +#define E1000_EECD_ADDR_BITS 0x00000400 /* EEPROM Addressing bits based on type + * (0-small, 1-large) */ +#define E1000_EECD_TYPE 0x00002000 /* EEPROM Type (1-SPI, 0-Microwire) */ #ifndef E1000_EEPROM_GRANT_ATTEMPTS -#define E1000_EEPROM_GRANT_ATTEMPTS 1000 /* EEPROM # attempts to gain grant */ +#define E1000_EEPROM_GRANT_ATTEMPTS 1000 /* EEPROM # attempts to gain grant */ #endif -#define E1000_EECD_AUTO_RD 0x00000200 /* EEPROM Auto Read done */ -#define E1000_EECD_SIZE_EX_MASK 0x00007800 /* EEprom Size */ +#define E1000_EECD_AUTO_RD 0x00000200 /* EEPROM Auto Read done */ +#define E1000_EECD_SIZE_EX_MASK 0x00007800 /* EEprom Size */ #define E1000_EECD_SIZE_EX_SHIFT 11 -#define E1000_EECD_NVADDS 0x00018000 /* NVM Address Size */ -#define E1000_EECD_SELSHAD 0x00020000 /* Select Shadow RAM */ -#define E1000_EECD_INITSRAM 0x00040000 /* Initialize Shadow RAM */ -#define E1000_EECD_FLUPD 0x00080000 /* Update FLASH */ -#define E1000_EECD_AUPDEN 0x00100000 /* Enable Autonomous FLASH update */ -#define E1000_EECD_SHADV 0x00200000 /* Shadow RAM Data Valid */ -#define E1000_EECD_SEC1VAL 0x00400000 /* Sector One Valid */ +#define E1000_EECD_NVADDS 0x00018000 /* NVM Address Size */ +#define E1000_EECD_SELSHAD 0x00020000 /* Select Shadow RAM */ +#define E1000_EECD_INITSRAM 0x00040000 /* Initialize Shadow RAM */ +#define E1000_EECD_FLUPD 0x00080000 /* Update FLASH */ +#define E1000_EECD_AUPDEN 0x00100000 /* Enable Autonomous FLASH update */ +#define E1000_EECD_SHADV 0x00200000 /* Shadow RAM Data Valid */ +#define E1000_EECD_SEC1VAL 0x00400000 /* Sector One Valid */ #define E1000_EECD_SECVAL_SHIFT 22 #define E1000_STM_OPCODE 0xDB00 #define E1000_HICR_FW_RESET 0xC0 @@ -1593,12 +1506,12 @@ struct e1000_hw { #define E1000_ICH_NVM_SIG_MASK 0xC0 /* EEPROM Read */ -#define E1000_EERD_START 0x00000001 /* Start Read */ -#define E1000_EERD_DONE 0x00000010 /* Read Done */ +#define E1000_EERD_START 0x00000001 /* Start Read */ +#define E1000_EERD_DONE 0x00000010 /* Read Done */ #define E1000_EERD_ADDR_SHIFT 8 -#define E1000_EERD_ADDR_MASK 0x0000FF00 /* Read Address */ +#define E1000_EERD_ADDR_MASK 0x0000FF00 /* Read Address */ #define E1000_EERD_DATA_SHIFT 16 -#define E1000_EERD_DATA_MASK 0xFFFF0000 /* Read Data */ +#define E1000_EERD_DATA_MASK 0xFFFF0000 /* Read Data */ /* SPI EEPROM Status Register */ #define EEPROM_STATUS_RDY_SPI 0x01 @@ -1608,25 +1521,25 @@ struct e1000_hw { #define EEPROM_STATUS_WPEN_SPI 0x80 /* Extended Device Control */ -#define E1000_CTRL_EXT_GPI0_EN 0x00000001 /* Maps SDP4 to GPI0 */ -#define E1000_CTRL_EXT_GPI1_EN 0x00000002 /* Maps SDP5 to GPI1 */ +#define E1000_CTRL_EXT_GPI0_EN 0x00000001 /* Maps SDP4 to GPI0 */ +#define E1000_CTRL_EXT_GPI1_EN 0x00000002 /* Maps SDP5 to GPI1 */ #define E1000_CTRL_EXT_PHYINT_EN E1000_CTRL_EXT_GPI1_EN -#define E1000_CTRL_EXT_GPI2_EN 0x00000004 /* Maps SDP6 to GPI2 */ -#define E1000_CTRL_EXT_GPI3_EN 0x00000008 /* Maps SDP7 to GPI3 */ -#define E1000_CTRL_EXT_SDP4_DATA 0x00000010 /* Value of SW Defineable Pin 4 */ -#define E1000_CTRL_EXT_SDP5_DATA 0x00000020 /* Value of SW Defineable Pin 5 */ +#define E1000_CTRL_EXT_GPI2_EN 0x00000004 /* Maps SDP6 to GPI2 */ +#define E1000_CTRL_EXT_GPI3_EN 0x00000008 /* Maps SDP7 to GPI3 */ +#define E1000_CTRL_EXT_SDP4_DATA 0x00000010 /* Value of SW Defineable Pin 4 */ +#define E1000_CTRL_EXT_SDP5_DATA 0x00000020 /* Value of SW Defineable Pin 5 */ #define E1000_CTRL_EXT_PHY_INT E1000_CTRL_EXT_SDP5_DATA -#define E1000_CTRL_EXT_SDP6_DATA 0x00000040 /* Value of SW Defineable Pin 6 */ -#define E1000_CTRL_EXT_SDP7_DATA 0x00000080 /* Value of SW Defineable Pin 7 */ -#define E1000_CTRL_EXT_SDP4_DIR 0x00000100 /* Direction of SDP4 0=in 1=out */ -#define E1000_CTRL_EXT_SDP5_DIR 0x00000200 /* Direction of SDP5 0=in 1=out */ -#define E1000_CTRL_EXT_SDP6_DIR 0x00000400 /* Direction of SDP6 0=in 1=out */ -#define E1000_CTRL_EXT_SDP7_DIR 0x00000800 /* Direction of SDP7 0=in 1=out */ -#define E1000_CTRL_EXT_ASDCHK 0x00001000 /* Initiate an ASD sequence */ -#define E1000_CTRL_EXT_EE_RST 0x00002000 /* Reinitialize from EEPROM */ -#define E1000_CTRL_EXT_IPS 0x00004000 /* Invert Power State */ -#define E1000_CTRL_EXT_SPD_BYPS 0x00008000 /* Speed Select Bypass */ -#define E1000_CTRL_EXT_RO_DIS 0x00020000 /* Relaxed Ordering disable */ +#define E1000_CTRL_EXT_SDP6_DATA 0x00000040 /* Value of SW Defineable Pin 6 */ +#define E1000_CTRL_EXT_SDP7_DATA 0x00000080 /* Value of SW Defineable Pin 7 */ +#define E1000_CTRL_EXT_SDP4_DIR 0x00000100 /* Direction of SDP4 0=in 1=out */ +#define E1000_CTRL_EXT_SDP5_DIR 0x00000200 /* Direction of SDP5 0=in 1=out */ +#define E1000_CTRL_EXT_SDP6_DIR 0x00000400 /* Direction of SDP6 0=in 1=out */ +#define E1000_CTRL_EXT_SDP7_DIR 0x00000800 /* Direction of SDP7 0=in 1=out */ +#define E1000_CTRL_EXT_ASDCHK 0x00001000 /* Initiate an ASD sequence */ +#define E1000_CTRL_EXT_EE_RST 0x00002000 /* Reinitialize from EEPROM */ +#define E1000_CTRL_EXT_IPS 0x00004000 /* Invert Power State */ +#define E1000_CTRL_EXT_SPD_BYPS 0x00008000 /* Speed Select Bypass */ +#define E1000_CTRL_EXT_RO_DIS 0x00020000 /* Relaxed Ordering disable */ #define E1000_CTRL_EXT_LINK_MODE_MASK 0x00C00000 #define E1000_CTRL_EXT_LINK_MODE_GMII 0x00000000 #define E1000_CTRL_EXT_LINK_MODE_TBI 0x00C00000 @@ -1638,11 +1551,11 @@ struct e1000_hw { #define E1000_CTRL_EXT_WR_WMARK_320 0x01000000 #define E1000_CTRL_EXT_WR_WMARK_384 0x02000000 #define E1000_CTRL_EXT_WR_WMARK_448 0x03000000 -#define E1000_CTRL_EXT_DRV_LOAD 0x10000000 /* Driver loaded bit for FW */ -#define E1000_CTRL_EXT_IAME 0x08000000 /* Interrupt acknowledge Auto-mask */ -#define E1000_CTRL_EXT_INT_TIMER_CLR 0x20000000 /* Clear Interrupt timers after IMS clear */ -#define E1000_CRTL_EXT_PB_PAREN 0x01000000 /* packet buffer parity error detection enabled */ -#define E1000_CTRL_EXT_DF_PAREN 0x02000000 /* descriptor FIFO parity error detection enable */ +#define E1000_CTRL_EXT_DRV_LOAD 0x10000000 /* Driver loaded bit for FW */ +#define E1000_CTRL_EXT_IAME 0x08000000 /* Interrupt acknowledge Auto-mask */ +#define E1000_CTRL_EXT_INT_TIMER_CLR 0x20000000 /* Clear Interrupt timers after IMS clear */ +#define E1000_CRTL_EXT_PB_PAREN 0x01000000 /* packet buffer parity error detection enabled */ +#define E1000_CTRL_EXT_DF_PAREN 0x02000000 /* descriptor FIFO parity error detection enable */ #define E1000_CTRL_EXT_GHOST_PAREN 0x40000000 /* MDI Control */ @@ -1742,167 +1655,167 @@ struct e1000_hw { #define E1000_LEDCTL_MODE_LED_OFF 0xF /* Receive Address */ -#define E1000_RAH_AV 0x80000000 /* Receive descriptor valid */ +#define E1000_RAH_AV 0x80000000 /* Receive descriptor valid */ /* Interrupt Cause Read */ -#define E1000_ICR_TXDW 0x00000001 /* Transmit desc written back */ -#define E1000_ICR_TXQE 0x00000002 /* Transmit Queue empty */ -#define E1000_ICR_LSC 0x00000004 /* Link Status Change */ -#define E1000_ICR_RXSEQ 0x00000008 /* rx sequence error */ -#define E1000_ICR_RXDMT0 0x00000010 /* rx desc min. threshold (0) */ -#define E1000_ICR_RXO 0x00000040 /* rx overrun */ -#define E1000_ICR_RXT0 0x00000080 /* rx timer intr (ring 0) */ -#define E1000_ICR_MDAC 0x00000200 /* MDIO access complete */ -#define E1000_ICR_RXCFG 0x00000400 /* RX /c/ ordered set */ -#define E1000_ICR_GPI_EN0 0x00000800 /* GP Int 0 */ -#define E1000_ICR_GPI_EN1 0x00001000 /* GP Int 1 */ -#define E1000_ICR_GPI_EN2 0x00002000 /* GP Int 2 */ -#define E1000_ICR_GPI_EN3 0x00004000 /* GP Int 3 */ +#define E1000_ICR_TXDW 0x00000001 /* Transmit desc written back */ +#define E1000_ICR_TXQE 0x00000002 /* Transmit Queue empty */ +#define E1000_ICR_LSC 0x00000004 /* Link Status Change */ +#define E1000_ICR_RXSEQ 0x00000008 /* rx sequence error */ +#define E1000_ICR_RXDMT0 0x00000010 /* rx desc min. threshold (0) */ +#define E1000_ICR_RXO 0x00000040 /* rx overrun */ +#define E1000_ICR_RXT0 0x00000080 /* rx timer intr (ring 0) */ +#define E1000_ICR_MDAC 0x00000200 /* MDIO access complete */ +#define E1000_ICR_RXCFG 0x00000400 /* RX /c/ ordered set */ +#define E1000_ICR_GPI_EN0 0x00000800 /* GP Int 0 */ +#define E1000_ICR_GPI_EN1 0x00001000 /* GP Int 1 */ +#define E1000_ICR_GPI_EN2 0x00002000 /* GP Int 2 */ +#define E1000_ICR_GPI_EN3 0x00004000 /* GP Int 3 */ #define E1000_ICR_TXD_LOW 0x00008000 #define E1000_ICR_SRPD 0x00010000 -#define E1000_ICR_ACK 0x00020000 /* Receive Ack frame */ -#define E1000_ICR_MNG 0x00040000 /* Manageability event */ -#define E1000_ICR_DOCK 0x00080000 /* Dock/Undock */ -#define E1000_ICR_INT_ASSERTED 0x80000000 /* If this bit asserted, the driver should claim the interrupt */ -#define E1000_ICR_RXD_FIFO_PAR0 0x00100000 /* queue 0 Rx descriptor FIFO parity error */ -#define E1000_ICR_TXD_FIFO_PAR0 0x00200000 /* queue 0 Tx descriptor FIFO parity error */ -#define E1000_ICR_HOST_ARB_PAR 0x00400000 /* host arb read buffer parity error */ -#define E1000_ICR_PB_PAR 0x00800000 /* packet buffer parity error */ -#define E1000_ICR_RXD_FIFO_PAR1 0x01000000 /* queue 1 Rx descriptor FIFO parity error */ -#define E1000_ICR_TXD_FIFO_PAR1 0x02000000 /* queue 1 Tx descriptor FIFO parity error */ -#define E1000_ICR_ALL_PARITY 0x03F00000 /* all parity error bits */ -#define E1000_ICR_DSW 0x00000020 /* FW changed the status of DISSW bit in the FWSM */ -#define E1000_ICR_PHYINT 0x00001000 /* LAN connected device generates an interrupt */ -#define E1000_ICR_EPRST 0x00100000 /* ME handware reset occurs */ +#define E1000_ICR_ACK 0x00020000 /* Receive Ack frame */ +#define E1000_ICR_MNG 0x00040000 /* Manageability event */ +#define E1000_ICR_DOCK 0x00080000 /* Dock/Undock */ +#define E1000_ICR_INT_ASSERTED 0x80000000 /* If this bit asserted, the driver should claim the interrupt */ +#define E1000_ICR_RXD_FIFO_PAR0 0x00100000 /* queue 0 Rx descriptor FIFO parity error */ +#define E1000_ICR_TXD_FIFO_PAR0 0x00200000 /* queue 0 Tx descriptor FIFO parity error */ +#define E1000_ICR_HOST_ARB_PAR 0x00400000 /* host arb read buffer parity error */ +#define E1000_ICR_PB_PAR 0x00800000 /* packet buffer parity error */ +#define E1000_ICR_RXD_FIFO_PAR1 0x01000000 /* queue 1 Rx descriptor FIFO parity error */ +#define E1000_ICR_TXD_FIFO_PAR1 0x02000000 /* queue 1 Tx descriptor FIFO parity error */ +#define E1000_ICR_ALL_PARITY 0x03F00000 /* all parity error bits */ +#define E1000_ICR_DSW 0x00000020 /* FW changed the status of DISSW bit in the FWSM */ +#define E1000_ICR_PHYINT 0x00001000 /* LAN connected device generates an interrupt */ +#define E1000_ICR_EPRST 0x00100000 /* ME hardware reset occurs */ /* Interrupt Cause Set */ -#define E1000_ICS_TXDW E1000_ICR_TXDW /* Transmit desc written back */ -#define E1000_ICS_TXQE E1000_ICR_TXQE /* Transmit Queue empty */ -#define E1000_ICS_LSC E1000_ICR_LSC /* Link Status Change */ -#define E1000_ICS_RXSEQ E1000_ICR_RXSEQ /* rx sequence error */ -#define E1000_ICS_RXDMT0 E1000_ICR_RXDMT0 /* rx desc min. threshold */ -#define E1000_ICS_RXO E1000_ICR_RXO /* rx overrun */ -#define E1000_ICS_RXT0 E1000_ICR_RXT0 /* rx timer intr */ -#define E1000_ICS_MDAC E1000_ICR_MDAC /* MDIO access complete */ -#define E1000_ICS_RXCFG E1000_ICR_RXCFG /* RX /c/ ordered set */ -#define E1000_ICS_GPI_EN0 E1000_ICR_GPI_EN0 /* GP Int 0 */ -#define E1000_ICS_GPI_EN1 E1000_ICR_GPI_EN1 /* GP Int 1 */ -#define E1000_ICS_GPI_EN2 E1000_ICR_GPI_EN2 /* GP Int 2 */ -#define E1000_ICS_GPI_EN3 E1000_ICR_GPI_EN3 /* GP Int 3 */ +#define E1000_ICS_TXDW E1000_ICR_TXDW /* Transmit desc written back */ +#define E1000_ICS_TXQE E1000_ICR_TXQE /* Transmit Queue empty */ +#define E1000_ICS_LSC E1000_ICR_LSC /* Link Status Change */ +#define E1000_ICS_RXSEQ E1000_ICR_RXSEQ /* rx sequence error */ +#define E1000_ICS_RXDMT0 E1000_ICR_RXDMT0 /* rx desc min. threshold */ +#define E1000_ICS_RXO E1000_ICR_RXO /* rx overrun */ +#define E1000_ICS_RXT0 E1000_ICR_RXT0 /* rx timer intr */ +#define E1000_ICS_MDAC E1000_ICR_MDAC /* MDIO access complete */ +#define E1000_ICS_RXCFG E1000_ICR_RXCFG /* RX /c/ ordered set */ +#define E1000_ICS_GPI_EN0 E1000_ICR_GPI_EN0 /* GP Int 0 */ +#define E1000_ICS_GPI_EN1 E1000_ICR_GPI_EN1 /* GP Int 1 */ +#define E1000_ICS_GPI_EN2 E1000_ICR_GPI_EN2 /* GP Int 2 */ +#define E1000_ICS_GPI_EN3 E1000_ICR_GPI_EN3 /* GP Int 3 */ #define E1000_ICS_TXD_LOW E1000_ICR_TXD_LOW #define E1000_ICS_SRPD E1000_ICR_SRPD -#define E1000_ICS_ACK E1000_ICR_ACK /* Receive Ack frame */ -#define E1000_ICS_MNG E1000_ICR_MNG /* Manageability event */ -#define E1000_ICS_DOCK E1000_ICR_DOCK /* Dock/Undock */ -#define E1000_ICS_RXD_FIFO_PAR0 E1000_ICR_RXD_FIFO_PAR0 /* queue 0 Rx descriptor FIFO parity error */ -#define E1000_ICS_TXD_FIFO_PAR0 E1000_ICR_TXD_FIFO_PAR0 /* queue 0 Tx descriptor FIFO parity error */ -#define E1000_ICS_HOST_ARB_PAR E1000_ICR_HOST_ARB_PAR /* host arb read buffer parity error */ -#define E1000_ICS_PB_PAR E1000_ICR_PB_PAR /* packet buffer parity error */ -#define E1000_ICS_RXD_FIFO_PAR1 E1000_ICR_RXD_FIFO_PAR1 /* queue 1 Rx descriptor FIFO parity error */ -#define E1000_ICS_TXD_FIFO_PAR1 E1000_ICR_TXD_FIFO_PAR1 /* queue 1 Tx descriptor FIFO parity error */ +#define E1000_ICS_ACK E1000_ICR_ACK /* Receive Ack frame */ +#define E1000_ICS_MNG E1000_ICR_MNG /* Manageability event */ +#define E1000_ICS_DOCK E1000_ICR_DOCK /* Dock/Undock */ +#define E1000_ICS_RXD_FIFO_PAR0 E1000_ICR_RXD_FIFO_PAR0 /* queue 0 Rx descriptor FIFO parity error */ +#define E1000_ICS_TXD_FIFO_PAR0 E1000_ICR_TXD_FIFO_PAR0 /* queue 0 Tx descriptor FIFO parity error */ +#define E1000_ICS_HOST_ARB_PAR E1000_ICR_HOST_ARB_PAR /* host arb read buffer parity error */ +#define E1000_ICS_PB_PAR E1000_ICR_PB_PAR /* packet buffer parity error */ +#define E1000_ICS_RXD_FIFO_PAR1 E1000_ICR_RXD_FIFO_PAR1 /* queue 1 Rx descriptor FIFO parity error */ +#define E1000_ICS_TXD_FIFO_PAR1 E1000_ICR_TXD_FIFO_PAR1 /* queue 1 Tx descriptor FIFO parity error */ #define E1000_ICS_DSW E1000_ICR_DSW #define E1000_ICS_PHYINT E1000_ICR_PHYINT #define E1000_ICS_EPRST E1000_ICR_EPRST /* Interrupt Mask Set */ -#define E1000_IMS_TXDW E1000_ICR_TXDW /* Transmit desc written back */ -#define E1000_IMS_TXQE E1000_ICR_TXQE /* Transmit Queue empty */ -#define E1000_IMS_LSC E1000_ICR_LSC /* Link Status Change */ -#define E1000_IMS_RXSEQ E1000_ICR_RXSEQ /* rx sequence error */ -#define E1000_IMS_RXDMT0 E1000_ICR_RXDMT0 /* rx desc min. threshold */ -#define E1000_IMS_RXO E1000_ICR_RXO /* rx overrun */ -#define E1000_IMS_RXT0 E1000_ICR_RXT0 /* rx timer intr */ -#define E1000_IMS_MDAC E1000_ICR_MDAC /* MDIO access complete */ -#define E1000_IMS_RXCFG E1000_ICR_RXCFG /* RX /c/ ordered set */ -#define E1000_IMS_GPI_EN0 E1000_ICR_GPI_EN0 /* GP Int 0 */ -#define E1000_IMS_GPI_EN1 E1000_ICR_GPI_EN1 /* GP Int 1 */ -#define E1000_IMS_GPI_EN2 E1000_ICR_GPI_EN2 /* GP Int 2 */ -#define E1000_IMS_GPI_EN3 E1000_ICR_GPI_EN3 /* GP Int 3 */ +#define E1000_IMS_TXDW E1000_ICR_TXDW /* Transmit desc written back */ +#define E1000_IMS_TXQE E1000_ICR_TXQE /* Transmit Queue empty */ +#define E1000_IMS_LSC E1000_ICR_LSC /* Link Status Change */ +#define E1000_IMS_RXSEQ E1000_ICR_RXSEQ /* rx sequence error */ +#define E1000_IMS_RXDMT0 E1000_ICR_RXDMT0 /* rx desc min. threshold */ +#define E1000_IMS_RXO E1000_ICR_RXO /* rx overrun */ +#define E1000_IMS_RXT0 E1000_ICR_RXT0 /* rx timer intr */ +#define E1000_IMS_MDAC E1000_ICR_MDAC /* MDIO access complete */ +#define E1000_IMS_RXCFG E1000_ICR_RXCFG /* RX /c/ ordered set */ +#define E1000_IMS_GPI_EN0 E1000_ICR_GPI_EN0 /* GP Int 0 */ +#define E1000_IMS_GPI_EN1 E1000_ICR_GPI_EN1 /* GP Int 1 */ +#define E1000_IMS_GPI_EN2 E1000_ICR_GPI_EN2 /* GP Int 2 */ +#define E1000_IMS_GPI_EN3 E1000_ICR_GPI_EN3 /* GP Int 3 */ #define E1000_IMS_TXD_LOW E1000_ICR_TXD_LOW #define E1000_IMS_SRPD E1000_ICR_SRPD -#define E1000_IMS_ACK E1000_ICR_ACK /* Receive Ack frame */ -#define E1000_IMS_MNG E1000_ICR_MNG /* Manageability event */ -#define E1000_IMS_DOCK E1000_ICR_DOCK /* Dock/Undock */ -#define E1000_IMS_RXD_FIFO_PAR0 E1000_ICR_RXD_FIFO_PAR0 /* queue 0 Rx descriptor FIFO parity error */ -#define E1000_IMS_TXD_FIFO_PAR0 E1000_ICR_TXD_FIFO_PAR0 /* queue 0 Tx descriptor FIFO parity error */ -#define E1000_IMS_HOST_ARB_PAR E1000_ICR_HOST_ARB_PAR /* host arb read buffer parity error */ -#define E1000_IMS_PB_PAR E1000_ICR_PB_PAR /* packet buffer parity error */ -#define E1000_IMS_RXD_FIFO_PAR1 E1000_ICR_RXD_FIFO_PAR1 /* queue 1 Rx descriptor FIFO parity error */ -#define E1000_IMS_TXD_FIFO_PAR1 E1000_ICR_TXD_FIFO_PAR1 /* queue 1 Tx descriptor FIFO parity error */ +#define E1000_IMS_ACK E1000_ICR_ACK /* Receive Ack frame */ +#define E1000_IMS_MNG E1000_ICR_MNG /* Manageability event */ +#define E1000_IMS_DOCK E1000_ICR_DOCK /* Dock/Undock */ +#define E1000_IMS_RXD_FIFO_PAR0 E1000_ICR_RXD_FIFO_PAR0 /* queue 0 Rx descriptor FIFO parity error */ +#define E1000_IMS_TXD_FIFO_PAR0 E1000_ICR_TXD_FIFO_PAR0 /* queue 0 Tx descriptor FIFO parity error */ +#define E1000_IMS_HOST_ARB_PAR E1000_ICR_HOST_ARB_PAR /* host arb read buffer parity error */ +#define E1000_IMS_PB_PAR E1000_ICR_PB_PAR /* packet buffer parity error */ +#define E1000_IMS_RXD_FIFO_PAR1 E1000_ICR_RXD_FIFO_PAR1 /* queue 1 Rx descriptor FIFO parity error */ +#define E1000_IMS_TXD_FIFO_PAR1 E1000_ICR_TXD_FIFO_PAR1 /* queue 1 Tx descriptor FIFO parity error */ #define E1000_IMS_DSW E1000_ICR_DSW #define E1000_IMS_PHYINT E1000_ICR_PHYINT #define E1000_IMS_EPRST E1000_ICR_EPRST /* Interrupt Mask Clear */ -#define E1000_IMC_TXDW E1000_ICR_TXDW /* Transmit desc written back */ -#define E1000_IMC_TXQE E1000_ICR_TXQE /* Transmit Queue empty */ -#define E1000_IMC_LSC E1000_ICR_LSC /* Link Status Change */ -#define E1000_IMC_RXSEQ E1000_ICR_RXSEQ /* rx sequence error */ -#define E1000_IMC_RXDMT0 E1000_ICR_RXDMT0 /* rx desc min. threshold */ -#define E1000_IMC_RXO E1000_ICR_RXO /* rx overrun */ -#define E1000_IMC_RXT0 E1000_ICR_RXT0 /* rx timer intr */ -#define E1000_IMC_MDAC E1000_ICR_MDAC /* MDIO access complete */ -#define E1000_IMC_RXCFG E1000_ICR_RXCFG /* RX /c/ ordered set */ -#define E1000_IMC_GPI_EN0 E1000_ICR_GPI_EN0 /* GP Int 0 */ -#define E1000_IMC_GPI_EN1 E1000_ICR_GPI_EN1 /* GP Int 1 */ -#define E1000_IMC_GPI_EN2 E1000_ICR_GPI_EN2 /* GP Int 2 */ -#define E1000_IMC_GPI_EN3 E1000_ICR_GPI_EN3 /* GP Int 3 */ +#define E1000_IMC_TXDW E1000_ICR_TXDW /* Transmit desc written back */ +#define E1000_IMC_TXQE E1000_ICR_TXQE /* Transmit Queue empty */ +#define E1000_IMC_LSC E1000_ICR_LSC /* Link Status Change */ +#define E1000_IMC_RXSEQ E1000_ICR_RXSEQ /* rx sequence error */ +#define E1000_IMC_RXDMT0 E1000_ICR_RXDMT0 /* rx desc min. threshold */ +#define E1000_IMC_RXO E1000_ICR_RXO /* rx overrun */ +#define E1000_IMC_RXT0 E1000_ICR_RXT0 /* rx timer intr */ +#define E1000_IMC_MDAC E1000_ICR_MDAC /* MDIO access complete */ +#define E1000_IMC_RXCFG E1000_ICR_RXCFG /* RX /c/ ordered set */ +#define E1000_IMC_GPI_EN0 E1000_ICR_GPI_EN0 /* GP Int 0 */ +#define E1000_IMC_GPI_EN1 E1000_ICR_GPI_EN1 /* GP Int 1 */ +#define E1000_IMC_GPI_EN2 E1000_ICR_GPI_EN2 /* GP Int 2 */ +#define E1000_IMC_GPI_EN3 E1000_ICR_GPI_EN3 /* GP Int 3 */ #define E1000_IMC_TXD_LOW E1000_ICR_TXD_LOW #define E1000_IMC_SRPD E1000_ICR_SRPD -#define E1000_IMC_ACK E1000_ICR_ACK /* Receive Ack frame */ -#define E1000_IMC_MNG E1000_ICR_MNG /* Manageability event */ -#define E1000_IMC_DOCK E1000_ICR_DOCK /* Dock/Undock */ -#define E1000_IMC_RXD_FIFO_PAR0 E1000_ICR_RXD_FIFO_PAR0 /* queue 0 Rx descriptor FIFO parity error */ -#define E1000_IMC_TXD_FIFO_PAR0 E1000_ICR_TXD_FIFO_PAR0 /* queue 0 Tx descriptor FIFO parity error */ -#define E1000_IMC_HOST_ARB_PAR E1000_ICR_HOST_ARB_PAR /* host arb read buffer parity error */ -#define E1000_IMC_PB_PAR E1000_ICR_PB_PAR /* packet buffer parity error */ -#define E1000_IMC_RXD_FIFO_PAR1 E1000_ICR_RXD_FIFO_PAR1 /* queue 1 Rx descriptor FIFO parity error */ -#define E1000_IMC_TXD_FIFO_PAR1 E1000_ICR_TXD_FIFO_PAR1 /* queue 1 Tx descriptor FIFO parity error */ +#define E1000_IMC_ACK E1000_ICR_ACK /* Receive Ack frame */ +#define E1000_IMC_MNG E1000_ICR_MNG /* Manageability event */ +#define E1000_IMC_DOCK E1000_ICR_DOCK /* Dock/Undock */ +#define E1000_IMC_RXD_FIFO_PAR0 E1000_ICR_RXD_FIFO_PAR0 /* queue 0 Rx descriptor FIFO parity error */ +#define E1000_IMC_TXD_FIFO_PAR0 E1000_ICR_TXD_FIFO_PAR0 /* queue 0 Tx descriptor FIFO parity error */ +#define E1000_IMC_HOST_ARB_PAR E1000_ICR_HOST_ARB_PAR /* host arb read buffer parity error */ +#define E1000_IMC_PB_PAR E1000_ICR_PB_PAR /* packet buffer parity error */ +#define E1000_IMC_RXD_FIFO_PAR1 E1000_ICR_RXD_FIFO_PAR1 /* queue 1 Rx descriptor FIFO parity error */ +#define E1000_IMC_TXD_FIFO_PAR1 E1000_ICR_TXD_FIFO_PAR1 /* queue 1 Tx descriptor FIFO parity error */ #define E1000_IMC_DSW E1000_ICR_DSW #define E1000_IMC_PHYINT E1000_ICR_PHYINT #define E1000_IMC_EPRST E1000_ICR_EPRST /* Receive Control */ -#define E1000_RCTL_RST 0x00000001 /* Software reset */ -#define E1000_RCTL_EN 0x00000002 /* enable */ -#define E1000_RCTL_SBP 0x00000004 /* store bad packet */ -#define E1000_RCTL_UPE 0x00000008 /* unicast promiscuous enable */ -#define E1000_RCTL_MPE 0x00000010 /* multicast promiscuous enab */ -#define E1000_RCTL_LPE 0x00000020 /* long packet enable */ -#define E1000_RCTL_LBM_NO 0x00000000 /* no loopback mode */ -#define E1000_RCTL_LBM_MAC 0x00000040 /* MAC loopback mode */ -#define E1000_RCTL_LBM_SLP 0x00000080 /* serial link loopback mode */ -#define E1000_RCTL_LBM_TCVR 0x000000C0 /* tcvr loopback mode */ -#define E1000_RCTL_DTYP_MASK 0x00000C00 /* Descriptor type mask */ -#define E1000_RCTL_DTYP_PS 0x00000400 /* Packet Split descriptor */ -#define E1000_RCTL_RDMTS_HALF 0x00000000 /* rx desc min threshold size */ -#define E1000_RCTL_RDMTS_QUAT 0x00000100 /* rx desc min threshold size */ -#define E1000_RCTL_RDMTS_EIGTH 0x00000200 /* rx desc min threshold size */ -#define E1000_RCTL_MO_SHIFT 12 /* multicast offset shift */ -#define E1000_RCTL_MO_0 0x00000000 /* multicast offset 11:0 */ -#define E1000_RCTL_MO_1 0x00001000 /* multicast offset 12:1 */ -#define E1000_RCTL_MO_2 0x00002000 /* multicast offset 13:2 */ -#define E1000_RCTL_MO_3 0x00003000 /* multicast offset 15:4 */ -#define E1000_RCTL_MDR 0x00004000 /* multicast desc ring 0 */ -#define E1000_RCTL_BAM 0x00008000 /* broadcast enable */ +#define E1000_RCTL_RST 0x00000001 /* Software reset */ +#define E1000_RCTL_EN 0x00000002 /* enable */ +#define E1000_RCTL_SBP 0x00000004 /* store bad packet */ +#define E1000_RCTL_UPE 0x00000008 /* unicast promiscuous enable */ +#define E1000_RCTL_MPE 0x00000010 /* multicast promiscuous enab */ +#define E1000_RCTL_LPE 0x00000020 /* long packet enable */ +#define E1000_RCTL_LBM_NO 0x00000000 /* no loopback mode */ +#define E1000_RCTL_LBM_MAC 0x00000040 /* MAC loopback mode */ +#define E1000_RCTL_LBM_SLP 0x00000080 /* serial link loopback mode */ +#define E1000_RCTL_LBM_TCVR 0x000000C0 /* tcvr loopback mode */ +#define E1000_RCTL_DTYP_MASK 0x00000C00 /* Descriptor type mask */ +#define E1000_RCTL_DTYP_PS 0x00000400 /* Packet Split descriptor */ +#define E1000_RCTL_RDMTS_HALF 0x00000000 /* rx desc min threshold size */ +#define E1000_RCTL_RDMTS_QUAT 0x00000100 /* rx desc min threshold size */ +#define E1000_RCTL_RDMTS_EIGTH 0x00000200 /* rx desc min threshold size */ +#define E1000_RCTL_MO_SHIFT 12 /* multicast offset shift */ +#define E1000_RCTL_MO_0 0x00000000 /* multicast offset 11:0 */ +#define E1000_RCTL_MO_1 0x00001000 /* multicast offset 12:1 */ +#define E1000_RCTL_MO_2 0x00002000 /* multicast offset 13:2 */ +#define E1000_RCTL_MO_3 0x00003000 /* multicast offset 15:4 */ +#define E1000_RCTL_MDR 0x00004000 /* multicast desc ring 0 */ +#define E1000_RCTL_BAM 0x00008000 /* broadcast enable */ /* these buffer sizes are valid if E1000_RCTL_BSEX is 0 */ -#define E1000_RCTL_SZ_2048 0x00000000 /* rx buffer size 2048 */ -#define E1000_RCTL_SZ_1024 0x00010000 /* rx buffer size 1024 */ -#define E1000_RCTL_SZ_512 0x00020000 /* rx buffer size 512 */ -#define E1000_RCTL_SZ_256 0x00030000 /* rx buffer size 256 */ +#define E1000_RCTL_SZ_2048 0x00000000 /* rx buffer size 2048 */ +#define E1000_RCTL_SZ_1024 0x00010000 /* rx buffer size 1024 */ +#define E1000_RCTL_SZ_512 0x00020000 /* rx buffer size 512 */ +#define E1000_RCTL_SZ_256 0x00030000 /* rx buffer size 256 */ /* these buffer sizes are valid if E1000_RCTL_BSEX is 1 */ -#define E1000_RCTL_SZ_16384 0x00010000 /* rx buffer size 16384 */ -#define E1000_RCTL_SZ_8192 0x00020000 /* rx buffer size 8192 */ -#define E1000_RCTL_SZ_4096 0x00030000 /* rx buffer size 4096 */ -#define E1000_RCTL_VFE 0x00040000 /* vlan filter enable */ -#define E1000_RCTL_CFIEN 0x00080000 /* canonical form enable */ -#define E1000_RCTL_CFI 0x00100000 /* canonical form indicator */ -#define E1000_RCTL_DPF 0x00400000 /* discard pause frames */ -#define E1000_RCTL_PMCF 0x00800000 /* pass MAC control frames */ -#define E1000_RCTL_BSEX 0x02000000 /* Buffer size extension */ -#define E1000_RCTL_SECRC 0x04000000 /* Strip Ethernet CRC */ -#define E1000_RCTL_FLXBUF_MASK 0x78000000 /* Flexible buffer size */ -#define E1000_RCTL_FLXBUF_SHIFT 27 /* Flexible buffer shift */ +#define E1000_RCTL_SZ_16384 0x00010000 /* rx buffer size 16384 */ +#define E1000_RCTL_SZ_8192 0x00020000 /* rx buffer size 8192 */ +#define E1000_RCTL_SZ_4096 0x00030000 /* rx buffer size 4096 */ +#define E1000_RCTL_VFE 0x00040000 /* vlan filter enable */ +#define E1000_RCTL_CFIEN 0x00080000 /* canonical form enable */ +#define E1000_RCTL_CFI 0x00100000 /* canonical form indicator */ +#define E1000_RCTL_DPF 0x00400000 /* discard pause frames */ +#define E1000_RCTL_PMCF 0x00800000 /* pass MAC control frames */ +#define E1000_RCTL_BSEX 0x02000000 /* Buffer size extension */ +#define E1000_RCTL_SECRC 0x04000000 /* Strip Ethernet CRC */ +#define E1000_RCTL_FLXBUF_MASK 0x78000000 /* Flexible buffer size */ +#define E1000_RCTL_FLXBUF_SHIFT 27 /* Flexible buffer shift */ /* Use byte values for the following shift parameters * Usage: @@ -1925,10 +1838,10 @@ struct e1000_hw { #define E1000_PSRCTL_BSIZE2_MASK 0x003F0000 #define E1000_PSRCTL_BSIZE3_MASK 0x3F000000 -#define E1000_PSRCTL_BSIZE0_SHIFT 7 /* Shift _right_ 7 */ -#define E1000_PSRCTL_BSIZE1_SHIFT 2 /* Shift _right_ 2 */ -#define E1000_PSRCTL_BSIZE2_SHIFT 6 /* Shift _left_ 6 */ -#define E1000_PSRCTL_BSIZE3_SHIFT 14 /* Shift _left_ 14 */ +#define E1000_PSRCTL_BSIZE0_SHIFT 7 /* Shift _right_ 7 */ +#define E1000_PSRCTL_BSIZE1_SHIFT 2 /* Shift _right_ 2 */ +#define E1000_PSRCTL_BSIZE2_SHIFT 6 /* Shift _left_ 6 */ +#define E1000_PSRCTL_BSIZE3_SHIFT 14 /* Shift _left_ 14 */ /* SW_W_SYNC definitions */ #define E1000_SWFW_EEP_SM 0x0001 @@ -1937,17 +1850,17 @@ struct e1000_hw { #define E1000_SWFW_MAC_CSR_SM 0x0008 /* Receive Descriptor */ -#define E1000_RDT_DELAY 0x0000ffff /* Delay timer (1=1024us) */ -#define E1000_RDT_FPDB 0x80000000 /* Flush descriptor block */ -#define E1000_RDLEN_LEN 0x0007ff80 /* descriptor length */ -#define E1000_RDH_RDH 0x0000ffff /* receive descriptor head */ -#define E1000_RDT_RDT 0x0000ffff /* receive descriptor tail */ +#define E1000_RDT_DELAY 0x0000ffff /* Delay timer (1=1024us) */ +#define E1000_RDT_FPDB 0x80000000 /* Flush descriptor block */ +#define E1000_RDLEN_LEN 0x0007ff80 /* descriptor length */ +#define E1000_RDH_RDH 0x0000ffff /* receive descriptor head */ +#define E1000_RDT_RDT 0x0000ffff /* receive descriptor tail */ /* Flow Control */ -#define E1000_FCRTH_RTH 0x0000FFF8 /* Mask Bits[15:3] for RTH */ -#define E1000_FCRTH_XFCE 0x80000000 /* External Flow Control Enable */ -#define E1000_FCRTL_RTL 0x0000FFF8 /* Mask Bits[15:3] for RTL */ -#define E1000_FCRTL_XONE 0x80000000 /* Enable XON frame transmission */ +#define E1000_FCRTH_RTH 0x0000FFF8 /* Mask Bits[15:3] for RTH */ +#define E1000_FCRTH_XFCE 0x80000000 /* External Flow Control Enable */ +#define E1000_FCRTL_RTL 0x0000FFF8 /* Mask Bits[15:3] for RTL */ +#define E1000_FCRTL_XONE 0x80000000 /* Enable XON frame transmission */ /* Header split receive */ #define E1000_RFCTL_ISCSI_DIS 0x00000001 @@ -1967,66 +1880,64 @@ struct e1000_hw { #define E1000_RFCTL_NEW_IPV6_EXT_DIS 0x00020000 /* Receive Descriptor Control */ -#define E1000_RXDCTL_PTHRESH 0x0000003F /* RXDCTL Prefetch Threshold */ -#define E1000_RXDCTL_HTHRESH 0x00003F00 /* RXDCTL Host Threshold */ -#define E1000_RXDCTL_WTHRESH 0x003F0000 /* RXDCTL Writeback Threshold */ -#define E1000_RXDCTL_GRAN 0x01000000 /* RXDCTL Granularity */ +#define E1000_RXDCTL_PTHRESH 0x0000003F /* RXDCTL Prefetch Threshold */ +#define E1000_RXDCTL_HTHRESH 0x00003F00 /* RXDCTL Host Threshold */ +#define E1000_RXDCTL_WTHRESH 0x003F0000 /* RXDCTL Writeback Threshold */ +#define E1000_RXDCTL_GRAN 0x01000000 /* RXDCTL Granularity */ /* Transmit Descriptor Control */ -#define E1000_TXDCTL_PTHRESH 0x0000003F /* TXDCTL Prefetch Threshold */ -#define E1000_TXDCTL_HTHRESH 0x00003F00 /* TXDCTL Host Threshold */ -#define E1000_TXDCTL_WTHRESH 0x003F0000 /* TXDCTL Writeback Threshold */ -#define E1000_TXDCTL_GRAN 0x01000000 /* TXDCTL Granularity */ -#define E1000_TXDCTL_LWTHRESH 0xFE000000 /* TXDCTL Low Threshold */ -#define E1000_TXDCTL_FULL_TX_DESC_WB 0x01010000 /* GRAN=1, WTHRESH=1 */ -#define E1000_TXDCTL_COUNT_DESC 0x00400000 /* Enable the counting of desc. - still to be processed. */ +#define E1000_TXDCTL_PTHRESH 0x0000003F /* TXDCTL Prefetch Threshold */ +#define E1000_TXDCTL_HTHRESH 0x00003F00 /* TXDCTL Host Threshold */ +#define E1000_TXDCTL_WTHRESH 0x003F0000 /* TXDCTL Writeback Threshold */ +#define E1000_TXDCTL_GRAN 0x01000000 /* TXDCTL Granularity */ +#define E1000_TXDCTL_LWTHRESH 0xFE000000 /* TXDCTL Low Threshold */ +#define E1000_TXDCTL_FULL_TX_DESC_WB 0x01010000 /* GRAN=1, WTHRESH=1 */ +#define E1000_TXDCTL_COUNT_DESC 0x00400000 /* Enable the counting of desc. + still to be processed. */ /* Transmit Configuration Word */ -#define E1000_TXCW_FD 0x00000020 /* TXCW full duplex */ -#define E1000_TXCW_HD 0x00000040 /* TXCW half duplex */ -#define E1000_TXCW_PAUSE 0x00000080 /* TXCW sym pause request */ -#define E1000_TXCW_ASM_DIR 0x00000100 /* TXCW astm pause direction */ -#define E1000_TXCW_PAUSE_MASK 0x00000180 /* TXCW pause request mask */ -#define E1000_TXCW_RF 0x00003000 /* TXCW remote fault */ -#define E1000_TXCW_NP 0x00008000 /* TXCW next page */ -#define E1000_TXCW_CW 0x0000ffff /* TxConfigWord mask */ -#define E1000_TXCW_TXC 0x40000000 /* Transmit Config control */ -#define E1000_TXCW_ANE 0x80000000 /* Auto-neg enable */ +#define E1000_TXCW_FD 0x00000020 /* TXCW full duplex */ +#define E1000_TXCW_HD 0x00000040 /* TXCW half duplex */ +#define E1000_TXCW_PAUSE 0x00000080 /* TXCW sym pause request */ +#define E1000_TXCW_ASM_DIR 0x00000100 /* TXCW astm pause direction */ +#define E1000_TXCW_PAUSE_MASK 0x00000180 /* TXCW pause request mask */ +#define E1000_TXCW_RF 0x00003000 /* TXCW remote fault */ +#define E1000_TXCW_NP 0x00008000 /* TXCW next page */ +#define E1000_TXCW_CW 0x0000ffff /* TxConfigWord mask */ +#define E1000_TXCW_TXC 0x40000000 /* Transmit Config control */ +#define E1000_TXCW_ANE 0x80000000 /* Auto-neg enable */ /* Receive Configuration Word */ -#define E1000_RXCW_CW 0x0000ffff /* RxConfigWord mask */ -#define E1000_RXCW_NC 0x04000000 /* Receive config no carrier */ -#define E1000_RXCW_IV 0x08000000 /* Receive config invalid */ -#define E1000_RXCW_CC 0x10000000 /* Receive config change */ -#define E1000_RXCW_C 0x20000000 /* Receive config */ -#define E1000_RXCW_SYNCH 0x40000000 /* Receive config synch */ -#define E1000_RXCW_ANC 0x80000000 /* Auto-neg complete */ +#define E1000_RXCW_CW 0x0000ffff /* RxConfigWord mask */ +#define E1000_RXCW_NC 0x04000000 /* Receive config no carrier */ +#define E1000_RXCW_IV 0x08000000 /* Receive config invalid */ +#define E1000_RXCW_CC 0x10000000 /* Receive config change */ +#define E1000_RXCW_C 0x20000000 /* Receive config */ +#define E1000_RXCW_SYNCH 0x40000000 /* Receive config synch */ +#define E1000_RXCW_ANC 0x80000000 /* Auto-neg complete */ /* Transmit Control */ -#define E1000_TCTL_RST 0x00000001 /* software reset */ -#define E1000_TCTL_EN 0x00000002 /* enable tx */ -#define E1000_TCTL_BCE 0x00000004 /* busy check enable */ -#define E1000_TCTL_PSP 0x00000008 /* pad short packets */ -#define E1000_TCTL_CT 0x00000ff0 /* collision threshold */ -#define E1000_TCTL_COLD 0x003ff000 /* collision distance */ -#define E1000_TCTL_SWXOFF 0x00400000 /* SW Xoff transmission */ -#define E1000_TCTL_PBE 0x00800000 /* Packet Burst Enable */ -#define E1000_TCTL_RTLC 0x01000000 /* Re-transmit on late collision */ -#define E1000_TCTL_NRTU 0x02000000 /* No Re-transmit on underrun */ -#define E1000_TCTL_MULR 0x10000000 /* Multiple request support */ +#define E1000_TCTL_RST 0x00000001 /* software reset */ +#define E1000_TCTL_EN 0x00000002 /* enable tx */ +#define E1000_TCTL_BCE 0x00000004 /* busy check enable */ +#define E1000_TCTL_PSP 0x00000008 /* pad short packets */ +#define E1000_TCTL_CT 0x00000ff0 /* collision threshold */ +#define E1000_TCTL_COLD 0x003ff000 /* collision distance */ +#define E1000_TCTL_SWXOFF 0x00400000 /* SW Xoff transmission */ +#define E1000_TCTL_PBE 0x00800000 /* Packet Burst Enable */ +#define E1000_TCTL_RTLC 0x01000000 /* Re-transmit on late collision */ +#define E1000_TCTL_NRTU 0x02000000 /* No Re-transmit on underrun */ +#define E1000_TCTL_MULR 0x10000000 /* Multiple request support */ /* Extended Transmit Control */ -#define E1000_TCTL_EXT_BST_MASK 0x000003FF /* Backoff Slot Time */ -#define E1000_TCTL_EXT_GCEX_MASK 0x000FFC00 /* Gigabit Carry Extend Padding */ - -#define DEFAULT_80003ES2LAN_TCTL_EXT_GCEX 0x00010000 +#define E1000_TCTL_EXT_BST_MASK 0x000003FF /* Backoff Slot Time */ +#define E1000_TCTL_EXT_GCEX_MASK 0x000FFC00 /* Gigabit Carry Extend Padding */ /* Receive Checksum Control */ -#define E1000_RXCSUM_PCSS_MASK 0x000000FF /* Packet Checksum Start */ -#define E1000_RXCSUM_IPOFL 0x00000100 /* IPv4 checksum offload */ -#define E1000_RXCSUM_TUOFL 0x00000200 /* TCP / UDP checksum offload */ -#define E1000_RXCSUM_IPV6OFL 0x00000400 /* IPv6 checksum offload */ -#define E1000_RXCSUM_IPPCSE 0x00001000 /* IP payload checksum enable */ -#define E1000_RXCSUM_PCSD 0x00002000 /* packet checksum disabled */ +#define E1000_RXCSUM_PCSS_MASK 0x000000FF /* Packet Checksum Start */ +#define E1000_RXCSUM_IPOFL 0x00000100 /* IPv4 checksum offload */ +#define E1000_RXCSUM_TUOFL 0x00000200 /* TCP / UDP checksum offload */ +#define E1000_RXCSUM_IPV6OFL 0x00000400 /* IPv6 checksum offload */ +#define E1000_RXCSUM_IPPCSE 0x00001000 /* IP payload checksum enable */ +#define E1000_RXCSUM_PCSD 0x00002000 /* packet checksum disabled */ /* Multiple Receive Queue Control */ #define E1000_MRQC_ENABLE_MASK 0x00000003 @@ -2042,141 +1953,141 @@ struct e1000_hw { /* Definitions for power management and wakeup registers */ /* Wake Up Control */ -#define E1000_WUC_APME 0x00000001 /* APM Enable */ -#define E1000_WUC_PME_EN 0x00000002 /* PME Enable */ -#define E1000_WUC_PME_STATUS 0x00000004 /* PME Status */ -#define E1000_WUC_APMPME 0x00000008 /* Assert PME on APM Wakeup */ -#define E1000_WUC_SPM 0x80000000 /* Enable SPM */ +#define E1000_WUC_APME 0x00000001 /* APM Enable */ +#define E1000_WUC_PME_EN 0x00000002 /* PME Enable */ +#define E1000_WUC_PME_STATUS 0x00000004 /* PME Status */ +#define E1000_WUC_APMPME 0x00000008 /* Assert PME on APM Wakeup */ +#define E1000_WUC_SPM 0x80000000 /* Enable SPM */ /* Wake Up Filter Control */ -#define E1000_WUFC_LNKC 0x00000001 /* Link Status Change Wakeup Enable */ -#define E1000_WUFC_MAG 0x00000002 /* Magic Packet Wakeup Enable */ -#define E1000_WUFC_EX 0x00000004 /* Directed Exact Wakeup Enable */ -#define E1000_WUFC_MC 0x00000008 /* Directed Multicast Wakeup Enable */ -#define E1000_WUFC_BC 0x00000010 /* Broadcast Wakeup Enable */ -#define E1000_WUFC_ARP 0x00000020 /* ARP Request Packet Wakeup Enable */ -#define E1000_WUFC_IPV4 0x00000040 /* Directed IPv4 Packet Wakeup Enable */ -#define E1000_WUFC_IPV6 0x00000080 /* Directed IPv6 Packet Wakeup Enable */ -#define E1000_WUFC_IGNORE_TCO 0x00008000 /* Ignore WakeOn TCO packets */ -#define E1000_WUFC_FLX0 0x00010000 /* Flexible Filter 0 Enable */ -#define E1000_WUFC_FLX1 0x00020000 /* Flexible Filter 1 Enable */ -#define E1000_WUFC_FLX2 0x00040000 /* Flexible Filter 2 Enable */ -#define E1000_WUFC_FLX3 0x00080000 /* Flexible Filter 3 Enable */ -#define E1000_WUFC_ALL_FILTERS 0x000F00FF /* Mask for all wakeup filters */ -#define E1000_WUFC_FLX_OFFSET 16 /* Offset to the Flexible Filters bits */ -#define E1000_WUFC_FLX_FILTERS 0x000F0000 /* Mask for the 4 flexible filters */ +#define E1000_WUFC_LNKC 0x00000001 /* Link Status Change Wakeup Enable */ +#define E1000_WUFC_MAG 0x00000002 /* Magic Packet Wakeup Enable */ +#define E1000_WUFC_EX 0x00000004 /* Directed Exact Wakeup Enable */ +#define E1000_WUFC_MC 0x00000008 /* Directed Multicast Wakeup Enable */ +#define E1000_WUFC_BC 0x00000010 /* Broadcast Wakeup Enable */ +#define E1000_WUFC_ARP 0x00000020 /* ARP Request Packet Wakeup Enable */ +#define E1000_WUFC_IPV4 0x00000040 /* Directed IPv4 Packet Wakeup Enable */ +#define E1000_WUFC_IPV6 0x00000080 /* Directed IPv6 Packet Wakeup Enable */ +#define E1000_WUFC_IGNORE_TCO 0x00008000 /* Ignore WakeOn TCO packets */ +#define E1000_WUFC_FLX0 0x00010000 /* Flexible Filter 0 Enable */ +#define E1000_WUFC_FLX1 0x00020000 /* Flexible Filter 1 Enable */ +#define E1000_WUFC_FLX2 0x00040000 /* Flexible Filter 2 Enable */ +#define E1000_WUFC_FLX3 0x00080000 /* Flexible Filter 3 Enable */ +#define E1000_WUFC_ALL_FILTERS 0x000F00FF /* Mask for all wakeup filters */ +#define E1000_WUFC_FLX_OFFSET 16 /* Offset to the Flexible Filters bits */ +#define E1000_WUFC_FLX_FILTERS 0x000F0000 /* Mask for the 4 flexible filters */ /* Wake Up Status */ -#define E1000_WUS_LNKC 0x00000001 /* Link Status Changed */ -#define E1000_WUS_MAG 0x00000002 /* Magic Packet Received */ -#define E1000_WUS_EX 0x00000004 /* Directed Exact Received */ -#define E1000_WUS_MC 0x00000008 /* Directed Multicast Received */ -#define E1000_WUS_BC 0x00000010 /* Broadcast Received */ -#define E1000_WUS_ARP 0x00000020 /* ARP Request Packet Received */ -#define E1000_WUS_IPV4 0x00000040 /* Directed IPv4 Packet Wakeup Received */ -#define E1000_WUS_IPV6 0x00000080 /* Directed IPv6 Packet Wakeup Received */ -#define E1000_WUS_FLX0 0x00010000 /* Flexible Filter 0 Match */ -#define E1000_WUS_FLX1 0x00020000 /* Flexible Filter 1 Match */ -#define E1000_WUS_FLX2 0x00040000 /* Flexible Filter 2 Match */ -#define E1000_WUS_FLX3 0x00080000 /* Flexible Filter 3 Match */ -#define E1000_WUS_FLX_FILTERS 0x000F0000 /* Mask for the 4 flexible filters */ +#define E1000_WUS_LNKC 0x00000001 /* Link Status Changed */ +#define E1000_WUS_MAG 0x00000002 /* Magic Packet Received */ +#define E1000_WUS_EX 0x00000004 /* Directed Exact Received */ +#define E1000_WUS_MC 0x00000008 /* Directed Multicast Received */ +#define E1000_WUS_BC 0x00000010 /* Broadcast Received */ +#define E1000_WUS_ARP 0x00000020 /* ARP Request Packet Received */ +#define E1000_WUS_IPV4 0x00000040 /* Directed IPv4 Packet Wakeup Received */ +#define E1000_WUS_IPV6 0x00000080 /* Directed IPv6 Packet Wakeup Received */ +#define E1000_WUS_FLX0 0x00010000 /* Flexible Filter 0 Match */ +#define E1000_WUS_FLX1 0x00020000 /* Flexible Filter 1 Match */ +#define E1000_WUS_FLX2 0x00040000 /* Flexible Filter 2 Match */ +#define E1000_WUS_FLX3 0x00080000 /* Flexible Filter 3 Match */ +#define E1000_WUS_FLX_FILTERS 0x000F0000 /* Mask for the 4 flexible filters */ /* Management Control */ -#define E1000_MANC_SMBUS_EN 0x00000001 /* SMBus Enabled - RO */ -#define E1000_MANC_ASF_EN 0x00000002 /* ASF Enabled - RO */ -#define E1000_MANC_R_ON_FORCE 0x00000004 /* Reset on Force TCO - RO */ -#define E1000_MANC_RMCP_EN 0x00000100 /* Enable RCMP 026Fh Filtering */ -#define E1000_MANC_0298_EN 0x00000200 /* Enable RCMP 0298h Filtering */ -#define E1000_MANC_IPV4_EN 0x00000400 /* Enable IPv4 */ -#define E1000_MANC_IPV6_EN 0x00000800 /* Enable IPv6 */ -#define E1000_MANC_SNAP_EN 0x00001000 /* Accept LLC/SNAP */ -#define E1000_MANC_ARP_EN 0x00002000 /* Enable ARP Request Filtering */ -#define E1000_MANC_NEIGHBOR_EN 0x00004000 /* Enable Neighbor Discovery - * Filtering */ -#define E1000_MANC_ARP_RES_EN 0x00008000 /* Enable ARP response Filtering */ -#define E1000_MANC_TCO_RESET 0x00010000 /* TCO Reset Occurred */ -#define E1000_MANC_RCV_TCO_EN 0x00020000 /* Receive TCO Packets Enabled */ -#define E1000_MANC_REPORT_STATUS 0x00040000 /* Status Reporting Enabled */ -#define E1000_MANC_RCV_ALL 0x00080000 /* Receive All Enabled */ -#define E1000_MANC_BLK_PHY_RST_ON_IDE 0x00040000 /* Block phy resets */ -#define E1000_MANC_EN_MAC_ADDR_FILTER 0x00100000 /* Enable MAC address - * filtering */ -#define E1000_MANC_EN_MNG2HOST 0x00200000 /* Enable MNG packets to host - * memory */ -#define E1000_MANC_EN_IP_ADDR_FILTER 0x00400000 /* Enable IP address - * filtering */ -#define E1000_MANC_EN_XSUM_FILTER 0x00800000 /* Enable checksum filtering */ -#define E1000_MANC_BR_EN 0x01000000 /* Enable broadcast filtering */ -#define E1000_MANC_SMB_REQ 0x01000000 /* SMBus Request */ -#define E1000_MANC_SMB_GNT 0x02000000 /* SMBus Grant */ -#define E1000_MANC_SMB_CLK_IN 0x04000000 /* SMBus Clock In */ -#define E1000_MANC_SMB_DATA_IN 0x08000000 /* SMBus Data In */ -#define E1000_MANC_SMB_DATA_OUT 0x10000000 /* SMBus Data Out */ -#define E1000_MANC_SMB_CLK_OUT 0x20000000 /* SMBus Clock Out */ - -#define E1000_MANC_SMB_DATA_OUT_SHIFT 28 /* SMBus Data Out Shift */ -#define E1000_MANC_SMB_CLK_OUT_SHIFT 29 /* SMBus Clock Out Shift */ +#define E1000_MANC_SMBUS_EN 0x00000001 /* SMBus Enabled - RO */ +#define E1000_MANC_ASF_EN 0x00000002 /* ASF Enabled - RO */ +#define E1000_MANC_R_ON_FORCE 0x00000004 /* Reset on Force TCO - RO */ +#define E1000_MANC_RMCP_EN 0x00000100 /* Enable RCMP 026Fh Filtering */ +#define E1000_MANC_0298_EN 0x00000200 /* Enable RCMP 0298h Filtering */ +#define E1000_MANC_IPV4_EN 0x00000400 /* Enable IPv4 */ +#define E1000_MANC_IPV6_EN 0x00000800 /* Enable IPv6 */ +#define E1000_MANC_SNAP_EN 0x00001000 /* Accept LLC/SNAP */ +#define E1000_MANC_ARP_EN 0x00002000 /* Enable ARP Request Filtering */ +#define E1000_MANC_NEIGHBOR_EN 0x00004000 /* Enable Neighbor Discovery + * Filtering */ +#define E1000_MANC_ARP_RES_EN 0x00008000 /* Enable ARP response Filtering */ +#define E1000_MANC_TCO_RESET 0x00010000 /* TCO Reset Occurred */ +#define E1000_MANC_RCV_TCO_EN 0x00020000 /* Receive TCO Packets Enabled */ +#define E1000_MANC_REPORT_STATUS 0x00040000 /* Status Reporting Enabled */ +#define E1000_MANC_RCV_ALL 0x00080000 /* Receive All Enabled */ +#define E1000_MANC_BLK_PHY_RST_ON_IDE 0x00040000 /* Block phy resets */ +#define E1000_MANC_EN_MAC_ADDR_FILTER 0x00100000 /* Enable MAC address + * filtering */ +#define E1000_MANC_EN_MNG2HOST 0x00200000 /* Enable MNG packets to host + * memory */ +#define E1000_MANC_EN_IP_ADDR_FILTER 0x00400000 /* Enable IP address + * filtering */ +#define E1000_MANC_EN_XSUM_FILTER 0x00800000 /* Enable checksum filtering */ +#define E1000_MANC_BR_EN 0x01000000 /* Enable broadcast filtering */ +#define E1000_MANC_SMB_REQ 0x01000000 /* SMBus Request */ +#define E1000_MANC_SMB_GNT 0x02000000 /* SMBus Grant */ +#define E1000_MANC_SMB_CLK_IN 0x04000000 /* SMBus Clock In */ +#define E1000_MANC_SMB_DATA_IN 0x08000000 /* SMBus Data In */ +#define E1000_MANC_SMB_DATA_OUT 0x10000000 /* SMBus Data Out */ +#define E1000_MANC_SMB_CLK_OUT 0x20000000 /* SMBus Clock Out */ + +#define E1000_MANC_SMB_DATA_OUT_SHIFT 28 /* SMBus Data Out Shift */ +#define E1000_MANC_SMB_CLK_OUT_SHIFT 29 /* SMBus Clock Out Shift */ /* SW Semaphore Register */ -#define E1000_SWSM_SMBI 0x00000001 /* Driver Semaphore bit */ -#define E1000_SWSM_SWESMBI 0x00000002 /* FW Semaphore bit */ -#define E1000_SWSM_WMNG 0x00000004 /* Wake MNG Clock */ -#define E1000_SWSM_DRV_LOAD 0x00000008 /* Driver Loaded Bit */ +#define E1000_SWSM_SMBI 0x00000001 /* Driver Semaphore bit */ +#define E1000_SWSM_SWESMBI 0x00000002 /* FW Semaphore bit */ +#define E1000_SWSM_WMNG 0x00000004 /* Wake MNG Clock */ +#define E1000_SWSM_DRV_LOAD 0x00000008 /* Driver Loaded Bit */ /* FW Semaphore Register */ -#define E1000_FWSM_MODE_MASK 0x0000000E /* FW mode */ +#define E1000_FWSM_MODE_MASK 0x0000000E /* FW mode */ #define E1000_FWSM_MODE_SHIFT 1 -#define E1000_FWSM_FW_VALID 0x00008000 /* FW established a valid mode */ +#define E1000_FWSM_FW_VALID 0x00008000 /* FW established a valid mode */ -#define E1000_FWSM_RSPCIPHY 0x00000040 /* Reset PHY on PCI reset */ -#define E1000_FWSM_DISSW 0x10000000 /* FW disable SW Write Access */ -#define E1000_FWSM_SKUSEL_MASK 0x60000000 /* LAN SKU select */ +#define E1000_FWSM_RSPCIPHY 0x00000040 /* Reset PHY on PCI reset */ +#define E1000_FWSM_DISSW 0x10000000 /* FW disable SW Write Access */ +#define E1000_FWSM_SKUSEL_MASK 0x60000000 /* LAN SKU select */ #define E1000_FWSM_SKUEL_SHIFT 29 -#define E1000_FWSM_SKUSEL_EMB 0x0 /* Embedded SKU */ -#define E1000_FWSM_SKUSEL_CONS 0x1 /* Consumer SKU */ -#define E1000_FWSM_SKUSEL_PERF_100 0x2 /* Perf & Corp 10/100 SKU */ -#define E1000_FWSM_SKUSEL_PERF_GBE 0x3 /* Perf & Copr GbE SKU */ +#define E1000_FWSM_SKUSEL_EMB 0x0 /* Embedded SKU */ +#define E1000_FWSM_SKUSEL_CONS 0x1 /* Consumer SKU */ +#define E1000_FWSM_SKUSEL_PERF_100 0x2 /* Perf & Corp 10/100 SKU */ +#define E1000_FWSM_SKUSEL_PERF_GBE 0x3 /* Perf & Copr GbE SKU */ /* FFLT Debug Register */ -#define E1000_FFLT_DBG_INVC 0x00100000 /* Invalid /C/ code handling */ +#define E1000_FFLT_DBG_INVC 0x00100000 /* Invalid /C/ code handling */ typedef enum { - e1000_mng_mode_none = 0, - e1000_mng_mode_asf, - e1000_mng_mode_pt, - e1000_mng_mode_ipmi, - e1000_mng_mode_host_interface_only + e1000_mng_mode_none = 0, + e1000_mng_mode_asf, + e1000_mng_mode_pt, + e1000_mng_mode_ipmi, + e1000_mng_mode_host_interface_only } e1000_mng_mode; -/* Host Inteface Control Register */ -#define E1000_HICR_EN 0x00000001 /* Enable Bit - RO */ -#define E1000_HICR_C 0x00000002 /* Driver sets this bit when done - * to put command in RAM */ -#define E1000_HICR_SV 0x00000004 /* Status Validity */ -#define E1000_HICR_FWR 0x00000080 /* FW reset. Set by the Host */ +/* Host Interface Control Register */ +#define E1000_HICR_EN 0x00000001 /* Enable Bit - RO */ +#define E1000_HICR_C 0x00000002 /* Driver sets this bit when done + * to put command in RAM */ +#define E1000_HICR_SV 0x00000004 /* Status Validity */ +#define E1000_HICR_FWR 0x00000080 /* FW reset. Set by the Host */ /* Host Interface Command Interface - Address range 0x8800-0x8EFF */ -#define E1000_HI_MAX_DATA_LENGTH 252 /* Host Interface data length */ -#define E1000_HI_MAX_BLOCK_BYTE_LENGTH 1792 /* Number of bytes in range */ -#define E1000_HI_MAX_BLOCK_DWORD_LENGTH 448 /* Number of dwords in range */ -#define E1000_HI_COMMAND_TIMEOUT 500 /* Time in ms to process HI command */ +#define E1000_HI_MAX_DATA_LENGTH 252 /* Host Interface data length */ +#define E1000_HI_MAX_BLOCK_BYTE_LENGTH 1792 /* Number of bytes in range */ +#define E1000_HI_MAX_BLOCK_DWORD_LENGTH 448 /* Number of dwords in range */ +#define E1000_HI_COMMAND_TIMEOUT 500 /* Time in ms to process HI command */ struct e1000_host_command_header { - u8 command_id; - u8 command_length; - u8 command_options; /* I/F bits for command, status for return */ - u8 checksum; + u8 command_id; + u8 command_length; + u8 command_options; /* I/F bits for command, status for return */ + u8 checksum; }; struct e1000_host_command_info { - struct e1000_host_command_header command_header; /* Command Head/Command Result Head has 4 bytes */ - u8 command_data[E1000_HI_MAX_DATA_LENGTH]; /* Command data can length 0..252 */ + struct e1000_host_command_header command_header; /* Command Head/Command Result Head has 4 bytes */ + u8 command_data[E1000_HI_MAX_DATA_LENGTH]; /* Command data can length 0..252 */ }; /* Host SMB register #0 */ -#define E1000_HSMC0R_CLKIN 0x00000001 /* SMB Clock in */ -#define E1000_HSMC0R_DATAIN 0x00000002 /* SMB Data in */ -#define E1000_HSMC0R_DATAOUT 0x00000004 /* SMB Data out */ -#define E1000_HSMC0R_CLKOUT 0x00000008 /* SMB Clock out */ +#define E1000_HSMC0R_CLKIN 0x00000001 /* SMB Clock in */ +#define E1000_HSMC0R_DATAIN 0x00000002 /* SMB Data in */ +#define E1000_HSMC0R_DATAOUT 0x00000004 /* SMB Data out */ +#define E1000_HSMC0R_CLKOUT 0x00000008 /* SMB Clock out */ /* Host SMB register #1 */ #define E1000_HSMC1R_CLKIN E1000_HSMC0R_CLKIN @@ -2185,10 +2096,10 @@ struct e1000_host_command_info { #define E1000_HSMC1R_CLKOUT E1000_HSMC0R_CLKOUT /* FW Status Register */ -#define E1000_FWSTS_FWS_MASK 0x000000FF /* FW Status */ +#define E1000_FWSTS_FWS_MASK 0x000000FF /* FW Status */ /* Wake Up Packet Length */ -#define E1000_WUPL_LENGTH_MASK 0x0FFF /* Only the lower 12 bits are valid */ +#define E1000_WUPL_LENGTH_MASK 0x0FFF /* Only the lower 12 bits are valid */ #define E1000_MDALIGN 4096 @@ -2242,24 +2153,24 @@ struct e1000_host_command_info { #define PCI_EX_LINK_WIDTH_SHIFT 4 /* EEPROM Commands - Microwire */ -#define EEPROM_READ_OPCODE_MICROWIRE 0x6 /* EEPROM read opcode */ -#define EEPROM_WRITE_OPCODE_MICROWIRE 0x5 /* EEPROM write opcode */ -#define EEPROM_ERASE_OPCODE_MICROWIRE 0x7 /* EEPROM erase opcode */ -#define EEPROM_EWEN_OPCODE_MICROWIRE 0x13 /* EEPROM erase/write enable */ -#define EEPROM_EWDS_OPCODE_MICROWIRE 0x10 /* EEPROM erast/write disable */ +#define EEPROM_READ_OPCODE_MICROWIRE 0x6 /* EEPROM read opcode */ +#define EEPROM_WRITE_OPCODE_MICROWIRE 0x5 /* EEPROM write opcode */ +#define EEPROM_ERASE_OPCODE_MICROWIRE 0x7 /* EEPROM erase opcode */ +#define EEPROM_EWEN_OPCODE_MICROWIRE 0x13 /* EEPROM erase/write enable */ +#define EEPROM_EWDS_OPCODE_MICROWIRE 0x10 /* EEPROM erase/write disable */ /* EEPROM Commands - SPI */ -#define EEPROM_MAX_RETRY_SPI 5000 /* Max wait of 5ms, for RDY signal */ -#define EEPROM_READ_OPCODE_SPI 0x03 /* EEPROM read opcode */ -#define EEPROM_WRITE_OPCODE_SPI 0x02 /* EEPROM write opcode */ -#define EEPROM_A8_OPCODE_SPI 0x08 /* opcode bit-3 = address bit-8 */ -#define EEPROM_WREN_OPCODE_SPI 0x06 /* EEPROM set Write Enable latch */ -#define EEPROM_WRDI_OPCODE_SPI 0x04 /* EEPROM reset Write Enable latch */ -#define EEPROM_RDSR_OPCODE_SPI 0x05 /* EEPROM read Status register */ -#define EEPROM_WRSR_OPCODE_SPI 0x01 /* EEPROM write Status register */ -#define EEPROM_ERASE4K_OPCODE_SPI 0x20 /* EEPROM ERASE 4KB */ -#define EEPROM_ERASE64K_OPCODE_SPI 0xD8 /* EEPROM ERASE 64KB */ -#define EEPROM_ERASE256_OPCODE_SPI 0xDB /* EEPROM ERASE 256B */ +#define EEPROM_MAX_RETRY_SPI 5000 /* Max wait of 5ms, for RDY signal */ +#define EEPROM_READ_OPCODE_SPI 0x03 /* EEPROM read opcode */ +#define EEPROM_WRITE_OPCODE_SPI 0x02 /* EEPROM write opcode */ +#define EEPROM_A8_OPCODE_SPI 0x08 /* opcode bit-3 = address bit-8 */ +#define EEPROM_WREN_OPCODE_SPI 0x06 /* EEPROM set Write Enable latch */ +#define EEPROM_WRDI_OPCODE_SPI 0x04 /* EEPROM reset Write Enable latch */ +#define EEPROM_RDSR_OPCODE_SPI 0x05 /* EEPROM read Status register */ +#define EEPROM_WRSR_OPCODE_SPI 0x01 /* EEPROM write Status register */ +#define EEPROM_ERASE4K_OPCODE_SPI 0x20 /* EEPROM ERASE 4KB */ +#define EEPROM_ERASE64K_OPCODE_SPI 0xD8 /* EEPROM ERASE 64KB */ +#define EEPROM_ERASE256_OPCODE_SPI 0xDB /* EEPROM ERASE 256B */ /* EEPROM Size definitions */ #define EEPROM_WORD_SIZE_SHIFT 6 @@ -2270,7 +2181,7 @@ struct e1000_host_command_info { #define EEPROM_COMPAT 0x0003 #define EEPROM_ID_LED_SETTINGS 0x0004 #define EEPROM_VERSION 0x0005 -#define EEPROM_SERDES_AMPLITUDE 0x0006 /* For SERDES output amplitude adjustment. */ +#define EEPROM_SERDES_AMPLITUDE 0x0006 /* For SERDES output amplitude adjustment. */ #define EEPROM_PHY_CLASS_WORD 0x0007 #define EEPROM_INIT_CONTROL1_REG 0x000A #define EEPROM_INIT_CONTROL2_REG 0x000F @@ -2283,22 +2194,16 @@ struct e1000_host_command_info { #define EEPROM_FLASH_VERSION 0x0032 #define EEPROM_CHECKSUM_REG 0x003F -#define E1000_EEPROM_CFG_DONE 0x00040000 /* MNG config cycle done */ -#define E1000_EEPROM_CFG_DONE_PORT_1 0x00080000 /* ...for second port */ +#define E1000_EEPROM_CFG_DONE 0x00040000 /* MNG config cycle done */ +#define E1000_EEPROM_CFG_DONE_PORT_1 0x00080000 /* ...for second port */ /* Word definitions for ID LED Settings */ #define ID_LED_RESERVED_0000 0x0000 #define ID_LED_RESERVED_FFFF 0xFFFF -#define ID_LED_RESERVED_82573 0xF746 -#define ID_LED_DEFAULT_82573 0x1811 #define ID_LED_DEFAULT ((ID_LED_OFF1_ON2 << 12) | \ (ID_LED_OFF1_OFF2 << 8) | \ (ID_LED_DEF1_DEF2 << 4) | \ (ID_LED_DEF1_DEF2)) -#define ID_LED_DEFAULT_ICH8LAN ((ID_LED_DEF1_DEF2 << 12) | \ - (ID_LED_DEF1_OFF2 << 8) | \ - (ID_LED_DEF1_ON2 << 4) | \ - (ID_LED_DEF1_DEF2)) #define ID_LED_DEF1_DEF2 0x1 #define ID_LED_DEF1_ON2 0x2 #define ID_LED_DEF1_OFF2 0x3 @@ -2313,7 +2218,6 @@ struct e1000_host_command_info { #define IGP_ACTIVITY_LED_ENABLE 0x0300 #define IGP_LED3_MODE 0x07000000 - /* Mask bits for SERDES amplitude adjustment in Word 6 of the EEPROM */ #define EEPROM_SERDES_AMPLITUDE_MASK 0x000F @@ -2384,11 +2288,8 @@ struct e1000_host_command_info { #define DEFAULT_82542_TIPG_IPGR2 10 #define DEFAULT_82543_TIPG_IPGR2 6 -#define DEFAULT_80003ES2LAN_TIPG_IPGR2 7 #define E1000_TIPG_IPGR2_SHIFT 20 -#define DEFAULT_80003ES2LAN_TIPG_IPGT_10_100 0x00000009 -#define DEFAULT_80003ES2LAN_TIPG_IPGT_1000 0x00000008 #define E1000_TXDMAC_DPP 0x00000001 /* Adaptive IFS defines */ @@ -2421,9 +2322,9 @@ struct e1000_host_command_info { #define E1000_EXTCNF_CTRL_SWFLAG 0x00000020 /* PBA constants */ -#define E1000_PBA_8K 0x0008 /* 8KB, default Rx allocation */ -#define E1000_PBA_12K 0x000C /* 12KB, default Rx allocation */ -#define E1000_PBA_16K 0x0010 /* 16KB, default TX allocation */ +#define E1000_PBA_8K 0x0008 /* 8KB, default Rx allocation */ +#define E1000_PBA_12K 0x000C /* 12KB, default Rx allocation */ +#define E1000_PBA_16K 0x0010 /* 16KB, default TX allocation */ #define E1000_PBA_20K 0x0014 #define E1000_PBA_22K 0x0016 #define E1000_PBA_24K 0x0018 @@ -2432,7 +2333,7 @@ struct e1000_host_command_info { #define E1000_PBA_34K 0x0022 #define E1000_PBA_38K 0x0026 #define E1000_PBA_40K 0x0028 -#define E1000_PBA_48K 0x0030 /* 48KB, default RX allocation */ +#define E1000_PBA_48K 0x0030 /* 48KB, default RX allocation */ #define E1000_PBS_16K E1000_PBA_16K @@ -2442,9 +2343,9 @@ struct e1000_host_command_info { #define FLOW_CONTROL_TYPE 0x8808 /* The historical defaults for the flow control values are given below. */ -#define FC_DEFAULT_HI_THRESH (0x8000) /* 32KB */ -#define FC_DEFAULT_LO_THRESH (0x4000) /* 16KB */ -#define FC_DEFAULT_TX_TIMER (0x100) /* ~130 us */ +#define FC_DEFAULT_HI_THRESH (0x8000) /* 32KB */ +#define FC_DEFAULT_LO_THRESH (0x4000) /* 16KB */ +#define FC_DEFAULT_TX_TIMER (0x100) /* ~130 us */ /* PCIX Config space */ #define PCIX_COMMAND_REGISTER 0xE6 @@ -2458,7 +2359,6 @@ struct e1000_host_command_info { #define PCIX_STATUS_HI_MMRBC_4K 0x3 #define PCIX_STATUS_HI_MMRBC_2K 0x2 - /* Number of bits required to shift right the "pause" bits from the * EEPROM (bits 13:12) to the "pause" (bits 8:7) field in the TXCW register. */ @@ -2479,14 +2379,11 @@ struct e1000_host_command_info { */ #define ILOS_SHIFT 3 - #define RECEIVE_BUFFER_ALIGN_SIZE (256) /* Number of milliseconds we wait for auto-negotiation to complete */ #define LINK_UP_TIMEOUT 500 -/* Number of 100 microseconds we wait for PCI Express master disable */ -#define MASTER_DISABLE_TIMEOUT 800 /* Number of milliseconds we wait for Eeprom auto read bit done after MAC reset */ #define AUTO_READ_DONE_TIMEOUT 10 /* Number of milliseconds we wait for PHY configuration done after MAC reset */ @@ -2534,7 +2431,6 @@ struct e1000_host_command_info { (((length) > (adapter)->min_frame_size) && \ ((length) <= ((adapter)->max_frame_size + VLAN_TAG_SIZE + 1))))) - /* Structures, enums, and macros for the PHY */ /* Bit definitions for the Management Data IO (MDIO) and Management Data @@ -2551,49 +2447,49 @@ struct e1000_host_command_info { /* PHY 1000 MII Register/Bit Definitions */ /* PHY Registers defined by IEEE */ -#define PHY_CTRL 0x00 /* Control Register */ -#define PHY_STATUS 0x01 /* Status Regiser */ -#define PHY_ID1 0x02 /* Phy Id Reg (word 1) */ -#define PHY_ID2 0x03 /* Phy Id Reg (word 2) */ -#define PHY_AUTONEG_ADV 0x04 /* Autoneg Advertisement */ -#define PHY_LP_ABILITY 0x05 /* Link Partner Ability (Base Page) */ -#define PHY_AUTONEG_EXP 0x06 /* Autoneg Expansion Reg */ -#define PHY_NEXT_PAGE_TX 0x07 /* Next Page TX */ -#define PHY_LP_NEXT_PAGE 0x08 /* Link Partner Next Page */ -#define PHY_1000T_CTRL 0x09 /* 1000Base-T Control Reg */ -#define PHY_1000T_STATUS 0x0A /* 1000Base-T Status Reg */ -#define PHY_EXT_STATUS 0x0F /* Extended Status Reg */ - -#define MAX_PHY_REG_ADDRESS 0x1F /* 5 bit address bus (0-0x1F) */ -#define MAX_PHY_MULTI_PAGE_REG 0xF /* Registers equal on all pages */ +#define PHY_CTRL 0x00 /* Control Register */ +#define PHY_STATUS 0x01 /* Status Register */ +#define PHY_ID1 0x02 /* Phy Id Reg (word 1) */ +#define PHY_ID2 0x03 /* Phy Id Reg (word 2) */ +#define PHY_AUTONEG_ADV 0x04 /* Autoneg Advertisement */ +#define PHY_LP_ABILITY 0x05 /* Link Partner Ability (Base Page) */ +#define PHY_AUTONEG_EXP 0x06 /* Autoneg Expansion Reg */ +#define PHY_NEXT_PAGE_TX 0x07 /* Next Page TX */ +#define PHY_LP_NEXT_PAGE 0x08 /* Link Partner Next Page */ +#define PHY_1000T_CTRL 0x09 /* 1000Base-T Control Reg */ +#define PHY_1000T_STATUS 0x0A /* 1000Base-T Status Reg */ +#define PHY_EXT_STATUS 0x0F /* Extended Status Reg */ + +#define MAX_PHY_REG_ADDRESS 0x1F /* 5 bit address bus (0-0x1F) */ +#define MAX_PHY_MULTI_PAGE_REG 0xF /* Registers equal on all pages */ /* M88E1000 Specific Registers */ -#define M88E1000_PHY_SPEC_CTRL 0x10 /* PHY Specific Control Register */ -#define M88E1000_PHY_SPEC_STATUS 0x11 /* PHY Specific Status Register */ -#define M88E1000_INT_ENABLE 0x12 /* Interrupt Enable Register */ -#define M88E1000_INT_STATUS 0x13 /* Interrupt Status Register */ -#define M88E1000_EXT_PHY_SPEC_CTRL 0x14 /* Extended PHY Specific Control */ -#define M88E1000_RX_ERR_CNTR 0x15 /* Receive Error Counter */ - -#define M88E1000_PHY_EXT_CTRL 0x1A /* PHY extend control register */ -#define M88E1000_PHY_PAGE_SELECT 0x1D /* Reg 29 for page number setting */ -#define M88E1000_PHY_GEN_CONTROL 0x1E /* Its meaning depends on reg 29 */ -#define M88E1000_PHY_VCO_REG_BIT8 0x100 /* Bits 8 & 11 are adjusted for */ -#define M88E1000_PHY_VCO_REG_BIT11 0x800 /* improved BER performance */ +#define M88E1000_PHY_SPEC_CTRL 0x10 /* PHY Specific Control Register */ +#define M88E1000_PHY_SPEC_STATUS 0x11 /* PHY Specific Status Register */ +#define M88E1000_INT_ENABLE 0x12 /* Interrupt Enable Register */ +#define M88E1000_INT_STATUS 0x13 /* Interrupt Status Register */ +#define M88E1000_EXT_PHY_SPEC_CTRL 0x14 /* Extended PHY Specific Control */ +#define M88E1000_RX_ERR_CNTR 0x15 /* Receive Error Counter */ + +#define M88E1000_PHY_EXT_CTRL 0x1A /* PHY extend control register */ +#define M88E1000_PHY_PAGE_SELECT 0x1D /* Reg 29 for page number setting */ +#define M88E1000_PHY_GEN_CONTROL 0x1E /* Its meaning depends on reg 29 */ +#define M88E1000_PHY_VCO_REG_BIT8 0x100 /* Bits 8 & 11 are adjusted for */ +#define M88E1000_PHY_VCO_REG_BIT11 0x800 /* improved BER performance */ #define IGP01E1000_IEEE_REGS_PAGE 0x0000 #define IGP01E1000_IEEE_RESTART_AUTONEG 0x3300 #define IGP01E1000_IEEE_FORCE_GIGA 0x0140 /* IGP01E1000 Specific Registers */ -#define IGP01E1000_PHY_PORT_CONFIG 0x10 /* PHY Specific Port Config Register */ -#define IGP01E1000_PHY_PORT_STATUS 0x11 /* PHY Specific Status Register */ -#define IGP01E1000_PHY_PORT_CTRL 0x12 /* PHY Specific Control Register */ -#define IGP01E1000_PHY_LINK_HEALTH 0x13 /* PHY Link Health Register */ -#define IGP01E1000_GMII_FIFO 0x14 /* GMII FIFO Register */ -#define IGP01E1000_PHY_CHANNEL_QUALITY 0x15 /* PHY Channel Quality Register */ +#define IGP01E1000_PHY_PORT_CONFIG 0x10 /* PHY Specific Port Config Register */ +#define IGP01E1000_PHY_PORT_STATUS 0x11 /* PHY Specific Status Register */ +#define IGP01E1000_PHY_PORT_CTRL 0x12 /* PHY Specific Control Register */ +#define IGP01E1000_PHY_LINK_HEALTH 0x13 /* PHY Link Health Register */ +#define IGP01E1000_GMII_FIFO 0x14 /* GMII FIFO Register */ +#define IGP01E1000_PHY_CHANNEL_QUALITY 0x15 /* PHY Channel Quality Register */ #define IGP02E1000_PHY_POWER_MGMT 0x19 -#define IGP01E1000_PHY_PAGE_SELECT 0x1F /* PHY Page Select Core Register */ +#define IGP01E1000_PHY_PAGE_SELECT 0x1F /* PHY Page Select Core Register */ /* IGP01E1000 AGC Registers - stores the cable length values*/ #define IGP01E1000_PHY_AGC_A 0x1172 @@ -2636,192 +2532,119 @@ struct e1000_host_command_info { #define IGP01E1000_ANALOG_REGS_PAGE 0x20C0 -/* Bits... - * 15-5: page - * 4-0: register offset - */ -#define GG82563_PAGE_SHIFT 5 -#define GG82563_REG(page, reg) \ - (((page) << GG82563_PAGE_SHIFT) | ((reg) & MAX_PHY_REG_ADDRESS)) -#define GG82563_MIN_ALT_REG 30 - -/* GG82563 Specific Registers */ -#define GG82563_PHY_SPEC_CTRL \ - GG82563_REG(0, 16) /* PHY Specific Control */ -#define GG82563_PHY_SPEC_STATUS \ - GG82563_REG(0, 17) /* PHY Specific Status */ -#define GG82563_PHY_INT_ENABLE \ - GG82563_REG(0, 18) /* Interrupt Enable */ -#define GG82563_PHY_SPEC_STATUS_2 \ - GG82563_REG(0, 19) /* PHY Specific Status 2 */ -#define GG82563_PHY_RX_ERR_CNTR \ - GG82563_REG(0, 21) /* Receive Error Counter */ -#define GG82563_PHY_PAGE_SELECT \ - GG82563_REG(0, 22) /* Page Select */ -#define GG82563_PHY_SPEC_CTRL_2 \ - GG82563_REG(0, 26) /* PHY Specific Control 2 */ -#define GG82563_PHY_PAGE_SELECT_ALT \ - GG82563_REG(0, 29) /* Alternate Page Select */ -#define GG82563_PHY_TEST_CLK_CTRL \ - GG82563_REG(0, 30) /* Test Clock Control (use reg. 29 to select) */ - -#define GG82563_PHY_MAC_SPEC_CTRL \ - GG82563_REG(2, 21) /* MAC Specific Control Register */ -#define GG82563_PHY_MAC_SPEC_CTRL_2 \ - GG82563_REG(2, 26) /* MAC Specific Control 2 */ - -#define GG82563_PHY_DSP_DISTANCE \ - GG82563_REG(5, 26) /* DSP Distance */ - -/* Page 193 - Port Control Registers */ -#define GG82563_PHY_KMRN_MODE_CTRL \ - GG82563_REG(193, 16) /* Kumeran Mode Control */ -#define GG82563_PHY_PORT_RESET \ - GG82563_REG(193, 17) /* Port Reset */ -#define GG82563_PHY_REVISION_ID \ - GG82563_REG(193, 18) /* Revision ID */ -#define GG82563_PHY_DEVICE_ID \ - GG82563_REG(193, 19) /* Device ID */ -#define GG82563_PHY_PWR_MGMT_CTRL \ - GG82563_REG(193, 20) /* Power Management Control */ -#define GG82563_PHY_RATE_ADAPT_CTRL \ - GG82563_REG(193, 25) /* Rate Adaptation Control */ - -/* Page 194 - KMRN Registers */ -#define GG82563_PHY_KMRN_FIFO_CTRL_STAT \ - GG82563_REG(194, 16) /* FIFO's Control/Status */ -#define GG82563_PHY_KMRN_CTRL \ - GG82563_REG(194, 17) /* Control */ -#define GG82563_PHY_INBAND_CTRL \ - GG82563_REG(194, 18) /* Inband Control */ -#define GG82563_PHY_KMRN_DIAGNOSTIC \ - GG82563_REG(194, 19) /* Diagnostic */ -#define GG82563_PHY_ACK_TIMEOUTS \ - GG82563_REG(194, 20) /* Acknowledge Timeouts */ -#define GG82563_PHY_ADV_ABILITY \ - GG82563_REG(194, 21) /* Advertised Ability */ -#define GG82563_PHY_LINK_PARTNER_ADV_ABILITY \ - GG82563_REG(194, 23) /* Link Partner Advertised Ability */ -#define GG82563_PHY_ADV_NEXT_PAGE \ - GG82563_REG(194, 24) /* Advertised Next Page */ -#define GG82563_PHY_LINK_PARTNER_ADV_NEXT_PAGE \ - GG82563_REG(194, 25) /* Link Partner Advertised Next page */ -#define GG82563_PHY_KMRN_MISC \ - GG82563_REG(194, 26) /* Misc. */ - /* PHY Control Register */ -#define MII_CR_SPEED_SELECT_MSB 0x0040 /* bits 6,13: 10=1000, 01=100, 00=10 */ -#define MII_CR_COLL_TEST_ENABLE 0x0080 /* Collision test enable */ -#define MII_CR_FULL_DUPLEX 0x0100 /* FDX =1, half duplex =0 */ -#define MII_CR_RESTART_AUTO_NEG 0x0200 /* Restart auto negotiation */ -#define MII_CR_ISOLATE 0x0400 /* Isolate PHY from MII */ -#define MII_CR_POWER_DOWN 0x0800 /* Power down */ -#define MII_CR_AUTO_NEG_EN 0x1000 /* Auto Neg Enable */ -#define MII_CR_SPEED_SELECT_LSB 0x2000 /* bits 6,13: 10=1000, 01=100, 00=10 */ -#define MII_CR_LOOPBACK 0x4000 /* 0 = normal, 1 = loopback */ -#define MII_CR_RESET 0x8000 /* 0 = normal, 1 = PHY reset */ +#define MII_CR_SPEED_SELECT_MSB 0x0040 /* bits 6,13: 10=1000, 01=100, 00=10 */ +#define MII_CR_COLL_TEST_ENABLE 0x0080 /* Collision test enable */ +#define MII_CR_FULL_DUPLEX 0x0100 /* FDX =1, half duplex =0 */ +#define MII_CR_RESTART_AUTO_NEG 0x0200 /* Restart auto negotiation */ +#define MII_CR_ISOLATE 0x0400 /* Isolate PHY from MII */ +#define MII_CR_POWER_DOWN 0x0800 /* Power down */ +#define MII_CR_AUTO_NEG_EN 0x1000 /* Auto Neg Enable */ +#define MII_CR_SPEED_SELECT_LSB 0x2000 /* bits 6,13: 10=1000, 01=100, 00=10 */ +#define MII_CR_LOOPBACK 0x4000 /* 0 = normal, 1 = loopback */ +#define MII_CR_RESET 0x8000 /* 0 = normal, 1 = PHY reset */ /* PHY Status Register */ -#define MII_SR_EXTENDED_CAPS 0x0001 /* Extended register capabilities */ -#define MII_SR_JABBER_DETECT 0x0002 /* Jabber Detected */ -#define MII_SR_LINK_STATUS 0x0004 /* Link Status 1 = link */ -#define MII_SR_AUTONEG_CAPS 0x0008 /* Auto Neg Capable */ -#define MII_SR_REMOTE_FAULT 0x0010 /* Remote Fault Detect */ -#define MII_SR_AUTONEG_COMPLETE 0x0020 /* Auto Neg Complete */ -#define MII_SR_PREAMBLE_SUPPRESS 0x0040 /* Preamble may be suppressed */ -#define MII_SR_EXTENDED_STATUS 0x0100 /* Ext. status info in Reg 0x0F */ -#define MII_SR_100T2_HD_CAPS 0x0200 /* 100T2 Half Duplex Capable */ -#define MII_SR_100T2_FD_CAPS 0x0400 /* 100T2 Full Duplex Capable */ -#define MII_SR_10T_HD_CAPS 0x0800 /* 10T Half Duplex Capable */ -#define MII_SR_10T_FD_CAPS 0x1000 /* 10T Full Duplex Capable */ -#define MII_SR_100X_HD_CAPS 0x2000 /* 100X Half Duplex Capable */ -#define MII_SR_100X_FD_CAPS 0x4000 /* 100X Full Duplex Capable */ -#define MII_SR_100T4_CAPS 0x8000 /* 100T4 Capable */ +#define MII_SR_EXTENDED_CAPS 0x0001 /* Extended register capabilities */ +#define MII_SR_JABBER_DETECT 0x0002 /* Jabber Detected */ +#define MII_SR_LINK_STATUS 0x0004 /* Link Status 1 = link */ +#define MII_SR_AUTONEG_CAPS 0x0008 /* Auto Neg Capable */ +#define MII_SR_REMOTE_FAULT 0x0010 /* Remote Fault Detect */ +#define MII_SR_AUTONEG_COMPLETE 0x0020 /* Auto Neg Complete */ +#define MII_SR_PREAMBLE_SUPPRESS 0x0040 /* Preamble may be suppressed */ +#define MII_SR_EXTENDED_STATUS 0x0100 /* Ext. status info in Reg 0x0F */ +#define MII_SR_100T2_HD_CAPS 0x0200 /* 100T2 Half Duplex Capable */ +#define MII_SR_100T2_FD_CAPS 0x0400 /* 100T2 Full Duplex Capable */ +#define MII_SR_10T_HD_CAPS 0x0800 /* 10T Half Duplex Capable */ +#define MII_SR_10T_FD_CAPS 0x1000 /* 10T Full Duplex Capable */ +#define MII_SR_100X_HD_CAPS 0x2000 /* 100X Half Duplex Capable */ +#define MII_SR_100X_FD_CAPS 0x4000 /* 100X Full Duplex Capable */ +#define MII_SR_100T4_CAPS 0x8000 /* 100T4 Capable */ /* Autoneg Advertisement Register */ -#define NWAY_AR_SELECTOR_FIELD 0x0001 /* indicates IEEE 802.3 CSMA/CD */ -#define NWAY_AR_10T_HD_CAPS 0x0020 /* 10T Half Duplex Capable */ -#define NWAY_AR_10T_FD_CAPS 0x0040 /* 10T Full Duplex Capable */ -#define NWAY_AR_100TX_HD_CAPS 0x0080 /* 100TX Half Duplex Capable */ -#define NWAY_AR_100TX_FD_CAPS 0x0100 /* 100TX Full Duplex Capable */ -#define NWAY_AR_100T4_CAPS 0x0200 /* 100T4 Capable */ -#define NWAY_AR_PAUSE 0x0400 /* Pause operation desired */ -#define NWAY_AR_ASM_DIR 0x0800 /* Asymmetric Pause Direction bit */ -#define NWAY_AR_REMOTE_FAULT 0x2000 /* Remote Fault detected */ -#define NWAY_AR_NEXT_PAGE 0x8000 /* Next Page ability supported */ +#define NWAY_AR_SELECTOR_FIELD 0x0001 /* indicates IEEE 802.3 CSMA/CD */ +#define NWAY_AR_10T_HD_CAPS 0x0020 /* 10T Half Duplex Capable */ +#define NWAY_AR_10T_FD_CAPS 0x0040 /* 10T Full Duplex Capable */ +#define NWAY_AR_100TX_HD_CAPS 0x0080 /* 100TX Half Duplex Capable */ +#define NWAY_AR_100TX_FD_CAPS 0x0100 /* 100TX Full Duplex Capable */ +#define NWAY_AR_100T4_CAPS 0x0200 /* 100T4 Capable */ +#define NWAY_AR_PAUSE 0x0400 /* Pause operation desired */ +#define NWAY_AR_ASM_DIR 0x0800 /* Asymmetric Pause Direction bit */ +#define NWAY_AR_REMOTE_FAULT 0x2000 /* Remote Fault detected */ +#define NWAY_AR_NEXT_PAGE 0x8000 /* Next Page ability supported */ /* Link Partner Ability Register (Base Page) */ -#define NWAY_LPAR_SELECTOR_FIELD 0x0000 /* LP protocol selector field */ -#define NWAY_LPAR_10T_HD_CAPS 0x0020 /* LP is 10T Half Duplex Capable */ -#define NWAY_LPAR_10T_FD_CAPS 0x0040 /* LP is 10T Full Duplex Capable */ -#define NWAY_LPAR_100TX_HD_CAPS 0x0080 /* LP is 100TX Half Duplex Capable */ -#define NWAY_LPAR_100TX_FD_CAPS 0x0100 /* LP is 100TX Full Duplex Capable */ -#define NWAY_LPAR_100T4_CAPS 0x0200 /* LP is 100T4 Capable */ -#define NWAY_LPAR_PAUSE 0x0400 /* LP Pause operation desired */ -#define NWAY_LPAR_ASM_DIR 0x0800 /* LP Asymmetric Pause Direction bit */ -#define NWAY_LPAR_REMOTE_FAULT 0x2000 /* LP has detected Remote Fault */ -#define NWAY_LPAR_ACKNOWLEDGE 0x4000 /* LP has rx'd link code word */ -#define NWAY_LPAR_NEXT_PAGE 0x8000 /* Next Page ability supported */ +#define NWAY_LPAR_SELECTOR_FIELD 0x0000 /* LP protocol selector field */ +#define NWAY_LPAR_10T_HD_CAPS 0x0020 /* LP is 10T Half Duplex Capable */ +#define NWAY_LPAR_10T_FD_CAPS 0x0040 /* LP is 10T Full Duplex Capable */ +#define NWAY_LPAR_100TX_HD_CAPS 0x0080 /* LP is 100TX Half Duplex Capable */ +#define NWAY_LPAR_100TX_FD_CAPS 0x0100 /* LP is 100TX Full Duplex Capable */ +#define NWAY_LPAR_100T4_CAPS 0x0200 /* LP is 100T4 Capable */ +#define NWAY_LPAR_PAUSE 0x0400 /* LP Pause operation desired */ +#define NWAY_LPAR_ASM_DIR 0x0800 /* LP Asymmetric Pause Direction bit */ +#define NWAY_LPAR_REMOTE_FAULT 0x2000 /* LP has detected Remote Fault */ +#define NWAY_LPAR_ACKNOWLEDGE 0x4000 /* LP has rx'd link code word */ +#define NWAY_LPAR_NEXT_PAGE 0x8000 /* Next Page ability supported */ /* Autoneg Expansion Register */ -#define NWAY_ER_LP_NWAY_CAPS 0x0001 /* LP has Auto Neg Capability */ -#define NWAY_ER_PAGE_RXD 0x0002 /* LP is 10T Half Duplex Capable */ -#define NWAY_ER_NEXT_PAGE_CAPS 0x0004 /* LP is 10T Full Duplex Capable */ -#define NWAY_ER_LP_NEXT_PAGE_CAPS 0x0008 /* LP is 100TX Half Duplex Capable */ -#define NWAY_ER_PAR_DETECT_FAULT 0x0010 /* LP is 100TX Full Duplex Capable */ +#define NWAY_ER_LP_NWAY_CAPS 0x0001 /* LP has Auto Neg Capability */ +#define NWAY_ER_PAGE_RXD 0x0002 /* LP is 10T Half Duplex Capable */ +#define NWAY_ER_NEXT_PAGE_CAPS 0x0004 /* LP is 10T Full Duplex Capable */ +#define NWAY_ER_LP_NEXT_PAGE_CAPS 0x0008 /* LP is 100TX Half Duplex Capable */ +#define NWAY_ER_PAR_DETECT_FAULT 0x0010 /* LP is 100TX Full Duplex Capable */ /* Next Page TX Register */ -#define NPTX_MSG_CODE_FIELD 0x0001 /* NP msg code or unformatted data */ -#define NPTX_TOGGLE 0x0800 /* Toggles between exchanges - * of different NP - */ -#define NPTX_ACKNOWLDGE2 0x1000 /* 1 = will comply with msg - * 0 = cannot comply with msg - */ -#define NPTX_MSG_PAGE 0x2000 /* formatted(1)/unformatted(0) pg */ -#define NPTX_NEXT_PAGE 0x8000 /* 1 = addition NP will follow - * 0 = sending last NP - */ +#define NPTX_MSG_CODE_FIELD 0x0001 /* NP msg code or unformatted data */ +#define NPTX_TOGGLE 0x0800 /* Toggles between exchanges + * of different NP + */ +#define NPTX_ACKNOWLDGE2 0x1000 /* 1 = will comply with msg + * 0 = cannot comply with msg + */ +#define NPTX_MSG_PAGE 0x2000 /* formatted(1)/unformatted(0) pg */ +#define NPTX_NEXT_PAGE 0x8000 /* 1 = addition NP will follow + * 0 = sending last NP + */ /* Link Partner Next Page Register */ -#define LP_RNPR_MSG_CODE_FIELD 0x0001 /* NP msg code or unformatted data */ -#define LP_RNPR_TOGGLE 0x0800 /* Toggles between exchanges - * of different NP - */ -#define LP_RNPR_ACKNOWLDGE2 0x1000 /* 1 = will comply with msg - * 0 = cannot comply with msg - */ -#define LP_RNPR_MSG_PAGE 0x2000 /* formatted(1)/unformatted(0) pg */ -#define LP_RNPR_ACKNOWLDGE 0x4000 /* 1 = ACK / 0 = NO ACK */ -#define LP_RNPR_NEXT_PAGE 0x8000 /* 1 = addition NP will follow - * 0 = sending last NP - */ +#define LP_RNPR_MSG_CODE_FIELD 0x0001 /* NP msg code or unformatted data */ +#define LP_RNPR_TOGGLE 0x0800 /* Toggles between exchanges + * of different NP + */ +#define LP_RNPR_ACKNOWLDGE2 0x1000 /* 1 = will comply with msg + * 0 = cannot comply with msg + */ +#define LP_RNPR_MSG_PAGE 0x2000 /* formatted(1)/unformatted(0) pg */ +#define LP_RNPR_ACKNOWLDGE 0x4000 /* 1 = ACK / 0 = NO ACK */ +#define LP_RNPR_NEXT_PAGE 0x8000 /* 1 = addition NP will follow + * 0 = sending last NP + */ /* 1000BASE-T Control Register */ -#define CR_1000T_ASYM_PAUSE 0x0080 /* Advertise asymmetric pause bit */ -#define CR_1000T_HD_CAPS 0x0100 /* Advertise 1000T HD capability */ -#define CR_1000T_FD_CAPS 0x0200 /* Advertise 1000T FD capability */ -#define CR_1000T_REPEATER_DTE 0x0400 /* 1=Repeater/switch device port */ - /* 0=DTE device */ -#define CR_1000T_MS_VALUE 0x0800 /* 1=Configure PHY as Master */ - /* 0=Configure PHY as Slave */ -#define CR_1000T_MS_ENABLE 0x1000 /* 1=Master/Slave manual config value */ - /* 0=Automatic Master/Slave config */ -#define CR_1000T_TEST_MODE_NORMAL 0x0000 /* Normal Operation */ -#define CR_1000T_TEST_MODE_1 0x2000 /* Transmit Waveform test */ -#define CR_1000T_TEST_MODE_2 0x4000 /* Master Transmit Jitter test */ -#define CR_1000T_TEST_MODE_3 0x6000 /* Slave Transmit Jitter test */ -#define CR_1000T_TEST_MODE_4 0x8000 /* Transmitter Distortion test */ +#define CR_1000T_ASYM_PAUSE 0x0080 /* Advertise asymmetric pause bit */ +#define CR_1000T_HD_CAPS 0x0100 /* Advertise 1000T HD capability */ +#define CR_1000T_FD_CAPS 0x0200 /* Advertise 1000T FD capability */ +#define CR_1000T_REPEATER_DTE 0x0400 /* 1=Repeater/switch device port */ + /* 0=DTE device */ +#define CR_1000T_MS_VALUE 0x0800 /* 1=Configure PHY as Master */ + /* 0=Configure PHY as Slave */ +#define CR_1000T_MS_ENABLE 0x1000 /* 1=Master/Slave manual config value */ + /* 0=Automatic Master/Slave config */ +#define CR_1000T_TEST_MODE_NORMAL 0x0000 /* Normal Operation */ +#define CR_1000T_TEST_MODE_1 0x2000 /* Transmit Waveform test */ +#define CR_1000T_TEST_MODE_2 0x4000 /* Master Transmit Jitter test */ +#define CR_1000T_TEST_MODE_3 0x6000 /* Slave Transmit Jitter test */ +#define CR_1000T_TEST_MODE_4 0x8000 /* Transmitter Distortion test */ /* 1000BASE-T Status Register */ -#define SR_1000T_IDLE_ERROR_CNT 0x00FF /* Num idle errors since last read */ -#define SR_1000T_ASYM_PAUSE_DIR 0x0100 /* LP asymmetric pause direction bit */ -#define SR_1000T_LP_HD_CAPS 0x0400 /* LP is 1000T HD capable */ -#define SR_1000T_LP_FD_CAPS 0x0800 /* LP is 1000T FD capable */ -#define SR_1000T_REMOTE_RX_STATUS 0x1000 /* Remote receiver OK */ -#define SR_1000T_LOCAL_RX_STATUS 0x2000 /* Local receiver OK */ -#define SR_1000T_MS_CONFIG_RES 0x4000 /* 1=Local TX is Master, 0=Slave */ -#define SR_1000T_MS_CONFIG_FAULT 0x8000 /* Master/Slave config fault */ +#define SR_1000T_IDLE_ERROR_CNT 0x00FF /* Num idle errors since last read */ +#define SR_1000T_ASYM_PAUSE_DIR 0x0100 /* LP asymmetric pause direction bit */ +#define SR_1000T_LP_HD_CAPS 0x0400 /* LP is 1000T HD capable */ +#define SR_1000T_LP_FD_CAPS 0x0800 /* LP is 1000T FD capable */ +#define SR_1000T_REMOTE_RX_STATUS 0x1000 /* Remote receiver OK */ +#define SR_1000T_LOCAL_RX_STATUS 0x2000 /* Local receiver OK */ +#define SR_1000T_MS_CONFIG_RES 0x4000 /* 1=Local TX is Master, 0=Slave */ +#define SR_1000T_MS_CONFIG_FAULT 0x8000 /* Master/Slave config fault */ #define SR_1000T_REMOTE_RX_STATUS_SHIFT 12 #define SR_1000T_LOCAL_RX_STATUS_SHIFT 13 #define SR_1000T_PHY_EXCESSIVE_IDLE_ERR_COUNT 5 @@ -2829,64 +2652,64 @@ struct e1000_host_command_info { #define FFE_IDLE_ERR_COUNT_TIMEOUT_100 100 /* Extended Status Register */ -#define IEEE_ESR_1000T_HD_CAPS 0x1000 /* 1000T HD capable */ -#define IEEE_ESR_1000T_FD_CAPS 0x2000 /* 1000T FD capable */ -#define IEEE_ESR_1000X_HD_CAPS 0x4000 /* 1000X HD capable */ -#define IEEE_ESR_1000X_FD_CAPS 0x8000 /* 1000X FD capable */ +#define IEEE_ESR_1000T_HD_CAPS 0x1000 /* 1000T HD capable */ +#define IEEE_ESR_1000T_FD_CAPS 0x2000 /* 1000T FD capable */ +#define IEEE_ESR_1000X_HD_CAPS 0x4000 /* 1000X HD capable */ +#define IEEE_ESR_1000X_FD_CAPS 0x8000 /* 1000X FD capable */ -#define PHY_TX_POLARITY_MASK 0x0100 /* register 10h bit 8 (polarity bit) */ -#define PHY_TX_NORMAL_POLARITY 0 /* register 10h bit 8 (normal polarity) */ +#define PHY_TX_POLARITY_MASK 0x0100 /* register 10h bit 8 (polarity bit) */ +#define PHY_TX_NORMAL_POLARITY 0 /* register 10h bit 8 (normal polarity) */ -#define AUTO_POLARITY_DISABLE 0x0010 /* register 11h bit 4 */ - /* (0=enable, 1=disable) */ +#define AUTO_POLARITY_DISABLE 0x0010 /* register 11h bit 4 */ + /* (0=enable, 1=disable) */ /* M88E1000 PHY Specific Control Register */ -#define M88E1000_PSCR_JABBER_DISABLE 0x0001 /* 1=Jabber Function disabled */ -#define M88E1000_PSCR_POLARITY_REVERSAL 0x0002 /* 1=Polarity Reversal enabled */ -#define M88E1000_PSCR_SQE_TEST 0x0004 /* 1=SQE Test enabled */ -#define M88E1000_PSCR_CLK125_DISABLE 0x0010 /* 1=CLK125 low, - * 0=CLK125 toggling - */ -#define M88E1000_PSCR_MDI_MANUAL_MODE 0x0000 /* MDI Crossover Mode bits 6:5 */ - /* Manual MDI configuration */ -#define M88E1000_PSCR_MDIX_MANUAL_MODE 0x0020 /* Manual MDIX configuration */ -#define M88E1000_PSCR_AUTO_X_1000T 0x0040 /* 1000BASE-T: Auto crossover, - * 100BASE-TX/10BASE-T: - * MDI Mode - */ -#define M88E1000_PSCR_AUTO_X_MODE 0x0060 /* Auto crossover enabled - * all speeds. - */ +#define M88E1000_PSCR_JABBER_DISABLE 0x0001 /* 1=Jabber Function disabled */ +#define M88E1000_PSCR_POLARITY_REVERSAL 0x0002 /* 1=Polarity Reversal enabled */ +#define M88E1000_PSCR_SQE_TEST 0x0004 /* 1=SQE Test enabled */ +#define M88E1000_PSCR_CLK125_DISABLE 0x0010 /* 1=CLK125 low, + * 0=CLK125 toggling + */ +#define M88E1000_PSCR_MDI_MANUAL_MODE 0x0000 /* MDI Crossover Mode bits 6:5 */ + /* Manual MDI configuration */ +#define M88E1000_PSCR_MDIX_MANUAL_MODE 0x0020 /* Manual MDIX configuration */ +#define M88E1000_PSCR_AUTO_X_1000T 0x0040 /* 1000BASE-T: Auto crossover, + * 100BASE-TX/10BASE-T: + * MDI Mode + */ +#define M88E1000_PSCR_AUTO_X_MODE 0x0060 /* Auto crossover enabled + * all speeds. + */ #define M88E1000_PSCR_10BT_EXT_DIST_ENABLE 0x0080 - /* 1=Enable Extended 10BASE-T distance - * (Lower 10BASE-T RX Threshold) - * 0=Normal 10BASE-T RX Threshold */ + /* 1=Enable Extended 10BASE-T distance + * (Lower 10BASE-T RX Threshold) + * 0=Normal 10BASE-T RX Threshold */ #define M88E1000_PSCR_MII_5BIT_ENABLE 0x0100 - /* 1=5-Bit interface in 100BASE-TX - * 0=MII interface in 100BASE-TX */ -#define M88E1000_PSCR_SCRAMBLER_DISABLE 0x0200 /* 1=Scrambler disable */ -#define M88E1000_PSCR_FORCE_LINK_GOOD 0x0400 /* 1=Force link good */ -#define M88E1000_PSCR_ASSERT_CRS_ON_TX 0x0800 /* 1=Assert CRS on Transmit */ + /* 1=5-Bit interface in 100BASE-TX + * 0=MII interface in 100BASE-TX */ +#define M88E1000_PSCR_SCRAMBLER_DISABLE 0x0200 /* 1=Scrambler disable */ +#define M88E1000_PSCR_FORCE_LINK_GOOD 0x0400 /* 1=Force link good */ +#define M88E1000_PSCR_ASSERT_CRS_ON_TX 0x0800 /* 1=Assert CRS on Transmit */ #define M88E1000_PSCR_POLARITY_REVERSAL_SHIFT 1 #define M88E1000_PSCR_AUTO_X_MODE_SHIFT 5 #define M88E1000_PSCR_10BT_EXT_DIST_ENABLE_SHIFT 7 /* M88E1000 PHY Specific Status Register */ -#define M88E1000_PSSR_JABBER 0x0001 /* 1=Jabber */ -#define M88E1000_PSSR_REV_POLARITY 0x0002 /* 1=Polarity reversed */ -#define M88E1000_PSSR_DOWNSHIFT 0x0020 /* 1=Downshifted */ -#define M88E1000_PSSR_MDIX 0x0040 /* 1=MDIX; 0=MDI */ -#define M88E1000_PSSR_CABLE_LENGTH 0x0380 /* 0=<50M;1=50-80M;2=80-110M; - * 3=110-140M;4=>140M */ -#define M88E1000_PSSR_LINK 0x0400 /* 1=Link up, 0=Link down */ -#define M88E1000_PSSR_SPD_DPLX_RESOLVED 0x0800 /* 1=Speed & Duplex resolved */ -#define M88E1000_PSSR_PAGE_RCVD 0x1000 /* 1=Page received */ -#define M88E1000_PSSR_DPLX 0x2000 /* 1=Duplex 0=Half Duplex */ -#define M88E1000_PSSR_SPEED 0xC000 /* Speed, bits 14:15 */ -#define M88E1000_PSSR_10MBS 0x0000 /* 00=10Mbs */ -#define M88E1000_PSSR_100MBS 0x4000 /* 01=100Mbs */ -#define M88E1000_PSSR_1000MBS 0x8000 /* 10=1000Mbs */ +#define M88E1000_PSSR_JABBER 0x0001 /* 1=Jabber */ +#define M88E1000_PSSR_REV_POLARITY 0x0002 /* 1=Polarity reversed */ +#define M88E1000_PSSR_DOWNSHIFT 0x0020 /* 1=Downshifted */ +#define M88E1000_PSSR_MDIX 0x0040 /* 1=MDIX; 0=MDI */ +#define M88E1000_PSSR_CABLE_LENGTH 0x0380 /* 0=<50M;1=50-80M;2=80-110M; + * 3=110-140M;4=>140M */ +#define M88E1000_PSSR_LINK 0x0400 /* 1=Link up, 0=Link down */ +#define M88E1000_PSSR_SPD_DPLX_RESOLVED 0x0800 /* 1=Speed & Duplex resolved */ +#define M88E1000_PSSR_PAGE_RCVD 0x1000 /* 1=Page received */ +#define M88E1000_PSSR_DPLX 0x2000 /* 1=Duplex 0=Half Duplex */ +#define M88E1000_PSSR_SPEED 0xC000 /* Speed, bits 14:15 */ +#define M88E1000_PSSR_10MBS 0x0000 /* 00=10Mbs */ +#define M88E1000_PSSR_100MBS 0x4000 /* 01=100Mbs */ +#define M88E1000_PSSR_1000MBS 0x8000 /* 10=1000Mbs */ #define M88E1000_PSSR_REV_POLARITY_SHIFT 1 #define M88E1000_PSSR_DOWNSHIFT_SHIFT 5 @@ -2894,12 +2717,12 @@ struct e1000_host_command_info { #define M88E1000_PSSR_CABLE_LENGTH_SHIFT 7 /* M88E1000 Extended PHY Specific Control Register */ -#define M88E1000_EPSCR_FIBER_LOOPBACK 0x4000 /* 1=Fiber loopback */ -#define M88E1000_EPSCR_DOWN_NO_IDLE 0x8000 /* 1=Lost lock detect enabled. - * Will assert lost lock and bring - * link down if idle not seen - * within 1ms in 1000BASE-T - */ +#define M88E1000_EPSCR_FIBER_LOOPBACK 0x4000 /* 1=Fiber loopback */ +#define M88E1000_EPSCR_DOWN_NO_IDLE 0x8000 /* 1=Lost lock detect enabled. + * Will assert lost lock and bring + * link down if idle not seen + * within 1ms in 1000BASE-T + */ /* Number of times we will attempt to autonegotiate before downshifting if we * are the master */ #define M88E1000_EPSCR_MASTER_DOWNSHIFT_MASK 0x0C00 @@ -2914,9 +2737,9 @@ struct e1000_host_command_info { #define M88E1000_EPSCR_SLAVE_DOWNSHIFT_1X 0x0100 #define M88E1000_EPSCR_SLAVE_DOWNSHIFT_2X 0x0200 #define M88E1000_EPSCR_SLAVE_DOWNSHIFT_3X 0x0300 -#define M88E1000_EPSCR_TX_CLK_2_5 0x0060 /* 2.5 MHz TX_CLK */ -#define M88E1000_EPSCR_TX_CLK_25 0x0070 /* 25 MHz TX_CLK */ -#define M88E1000_EPSCR_TX_CLK_0 0x0000 /* NO TX_CLK */ +#define M88E1000_EPSCR_TX_CLK_2_5 0x0060 /* 2.5 MHz TX_CLK */ +#define M88E1000_EPSCR_TX_CLK_25 0x0070 /* 25 MHz TX_CLK */ +#define M88E1000_EPSCR_TX_CLK_0 0x0000 /* NO TX_CLK */ /* M88EC018 Rev 2 specific DownShift settings */ #define M88EC018_EPSCR_DOWNSHIFT_COUNTER_MASK 0x0E00 @@ -2938,18 +2761,18 @@ struct e1000_host_command_info { #define IGP01E1000_PSCFR_DISABLE_TRANSMIT 0x2000 /* IGP01E1000 Specific Port Status Register - R/O */ -#define IGP01E1000_PSSR_AUTONEG_FAILED 0x0001 /* RO LH SC */ +#define IGP01E1000_PSSR_AUTONEG_FAILED 0x0001 /* RO LH SC */ #define IGP01E1000_PSSR_POLARITY_REVERSED 0x0002 #define IGP01E1000_PSSR_CABLE_LENGTH 0x007C #define IGP01E1000_PSSR_FULL_DUPLEX 0x0200 #define IGP01E1000_PSSR_LINK_UP 0x0400 #define IGP01E1000_PSSR_MDIX 0x0800 -#define IGP01E1000_PSSR_SPEED_MASK 0xC000 /* speed bits mask */ +#define IGP01E1000_PSSR_SPEED_MASK 0xC000 /* speed bits mask */ #define IGP01E1000_PSSR_SPEED_10MBPS 0x4000 #define IGP01E1000_PSSR_SPEED_100MBPS 0x8000 #define IGP01E1000_PSSR_SPEED_1000MBPS 0xC000 -#define IGP01E1000_PSSR_CABLE_LENGTH_SHIFT 0x0002 /* shift right 2 */ -#define IGP01E1000_PSSR_MDIX_SHIFT 0x000B /* shift right 11 */ +#define IGP01E1000_PSSR_CABLE_LENGTH_SHIFT 0x0002 /* shift right 2 */ +#define IGP01E1000_PSSR_MDIX_SHIFT 0x000B /* shift right 11 */ /* IGP01E1000 Specific Port Control Register - R/W */ #define IGP01E1000_PSCR_TP_LOOPBACK 0x0010 @@ -2957,16 +2780,16 @@ struct e1000_host_command_info { #define IGP01E1000_PSCR_TEN_CRS_SELECT 0x0400 #define IGP01E1000_PSCR_FLIP_CHIP 0x0800 #define IGP01E1000_PSCR_AUTO_MDIX 0x1000 -#define IGP01E1000_PSCR_FORCE_MDI_MDIX 0x2000 /* 0-MDI, 1-MDIX */ +#define IGP01E1000_PSCR_FORCE_MDI_MDIX 0x2000 /* 0-MDI, 1-MDIX */ /* IGP01E1000 Specific Port Link Health Register */ #define IGP01E1000_PLHR_SS_DOWNGRADE 0x8000 #define IGP01E1000_PLHR_GIG_SCRAMBLER_ERROR 0x4000 #define IGP01E1000_PLHR_MASTER_FAULT 0x2000 #define IGP01E1000_PLHR_MASTER_RESOLUTION 0x1000 -#define IGP01E1000_PLHR_GIG_REM_RCVR_NOK 0x0800 /* LH */ -#define IGP01E1000_PLHR_IDLE_ERROR_CNT_OFLOW 0x0400 /* LH */ -#define IGP01E1000_PLHR_DATA_ERR_1 0x0200 /* LH */ +#define IGP01E1000_PLHR_GIG_REM_RCVR_NOK 0x0800 /* LH */ +#define IGP01E1000_PLHR_IDLE_ERROR_CNT_OFLOW 0x0400 /* LH */ +#define IGP01E1000_PLHR_DATA_ERR_1 0x0200 /* LH */ #define IGP01E1000_PLHR_DATA_ERR_0 0x0100 #define IGP01E1000_PLHR_AUTONEG_FAULT 0x0040 #define IGP01E1000_PLHR_AUTONEG_ACTIVE 0x0010 @@ -2981,9 +2804,9 @@ struct e1000_host_command_info { #define IGP01E1000_MSE_CHANNEL_B 0x0F00 #define IGP01E1000_MSE_CHANNEL_A 0xF000 -#define IGP02E1000_PM_SPD 0x0001 /* Smart Power Down */ -#define IGP02E1000_PM_D3_LPLU 0x0004 /* Enable LPLU in non-D0a modes */ -#define IGP02E1000_PM_D0_LPLU 0x0002 /* Enable LPLU in D0a mode */ +#define IGP02E1000_PM_SPD 0x0001 /* Smart Power Down */ +#define IGP02E1000_PM_D3_LPLU 0x0004 /* Enable LPLU in non-D0a modes */ +#define IGP02E1000_PM_D0_LPLU 0x0002 /* Enable LPLU in D0a mode */ /* IGP01E1000 DSP reset macros */ #define DSP_RESET_ENABLE 0x0 @@ -2992,8 +2815,8 @@ struct e1000_host_command_info { /* IGP01E1000 & IGP02E1000 AGC Registers */ -#define IGP01E1000_AGC_LENGTH_SHIFT 7 /* Coarse - 13:11, Fine - 10:7 */ -#define IGP02E1000_AGC_LENGTH_SHIFT 9 /* Coarse - 15:13, Fine - 12:9 */ +#define IGP01E1000_AGC_LENGTH_SHIFT 7 /* Coarse - 13:11, Fine - 10:7 */ +#define IGP02E1000_AGC_LENGTH_SHIFT 9 /* Coarse - 15:13, Fine - 12:9 */ /* IGP02E1000 AGC Register Length 9-bit mask */ #define IGP02E1000_AGC_LENGTH_MASK 0x7F @@ -3011,9 +2834,9 @@ struct e1000_host_command_info { #define IGP01E1000_PHY_POLARITY_MASK 0x0078 /* IGP01E1000 GMII FIFO Register */ -#define IGP01E1000_GMII_FLEX_SPD 0x10 /* Enable flexible speed - * on Link-Up */ -#define IGP01E1000_GMII_SPD 0x20 /* Enable SPD */ +#define IGP01E1000_GMII_FLEX_SPD 0x10 /* Enable flexible speed + * on Link-Up */ +#define IGP01E1000_GMII_SPD 0x20 /* Enable SPD */ /* IGP01E1000 Analog Register */ #define IGP01E1000_ANALOG_SPARE_FUSE_STATUS 0x20D1 @@ -3032,114 +2855,6 @@ struct e1000_host_command_info { #define IGP01E1000_ANALOG_FUSE_FINE_1 0x0080 #define IGP01E1000_ANALOG_FUSE_FINE_10 0x0500 -/* GG82563 PHY Specific Status Register (Page 0, Register 16 */ -#define GG82563_PSCR_DISABLE_JABBER 0x0001 /* 1=Disable Jabber */ -#define GG82563_PSCR_POLARITY_REVERSAL_DISABLE 0x0002 /* 1=Polarity Reversal Disabled */ -#define GG82563_PSCR_POWER_DOWN 0x0004 /* 1=Power Down */ -#define GG82563_PSCR_COPPER_TRANSMITER_DISABLE 0x0008 /* 1=Transmitter Disabled */ -#define GG82563_PSCR_CROSSOVER_MODE_MASK 0x0060 -#define GG82563_PSCR_CROSSOVER_MODE_MDI 0x0000 /* 00=Manual MDI configuration */ -#define GG82563_PSCR_CROSSOVER_MODE_MDIX 0x0020 /* 01=Manual MDIX configuration */ -#define GG82563_PSCR_CROSSOVER_MODE_AUTO 0x0060 /* 11=Automatic crossover */ -#define GG82563_PSCR_ENALBE_EXTENDED_DISTANCE 0x0080 /* 1=Enable Extended Distance */ -#define GG82563_PSCR_ENERGY_DETECT_MASK 0x0300 -#define GG82563_PSCR_ENERGY_DETECT_OFF 0x0000 /* 00,01=Off */ -#define GG82563_PSCR_ENERGY_DETECT_RX 0x0200 /* 10=Sense on Rx only (Energy Detect) */ -#define GG82563_PSCR_ENERGY_DETECT_RX_TM 0x0300 /* 11=Sense and Tx NLP */ -#define GG82563_PSCR_FORCE_LINK_GOOD 0x0400 /* 1=Force Link Good */ -#define GG82563_PSCR_DOWNSHIFT_ENABLE 0x0800 /* 1=Enable Downshift */ -#define GG82563_PSCR_DOWNSHIFT_COUNTER_MASK 0x7000 -#define GG82563_PSCR_DOWNSHIFT_COUNTER_SHIFT 12 - -/* PHY Specific Status Register (Page 0, Register 17) */ -#define GG82563_PSSR_JABBER 0x0001 /* 1=Jabber */ -#define GG82563_PSSR_POLARITY 0x0002 /* 1=Polarity Reversed */ -#define GG82563_PSSR_LINK 0x0008 /* 1=Link is Up */ -#define GG82563_PSSR_ENERGY_DETECT 0x0010 /* 1=Sleep, 0=Active */ -#define GG82563_PSSR_DOWNSHIFT 0x0020 /* 1=Downshift */ -#define GG82563_PSSR_CROSSOVER_STATUS 0x0040 /* 1=MDIX, 0=MDI */ -#define GG82563_PSSR_RX_PAUSE_ENABLED 0x0100 /* 1=Receive Pause Enabled */ -#define GG82563_PSSR_TX_PAUSE_ENABLED 0x0200 /* 1=Transmit Pause Enabled */ -#define GG82563_PSSR_LINK_UP 0x0400 /* 1=Link Up */ -#define GG82563_PSSR_SPEED_DUPLEX_RESOLVED 0x0800 /* 1=Resolved */ -#define GG82563_PSSR_PAGE_RECEIVED 0x1000 /* 1=Page Received */ -#define GG82563_PSSR_DUPLEX 0x2000 /* 1-Full-Duplex */ -#define GG82563_PSSR_SPEED_MASK 0xC000 -#define GG82563_PSSR_SPEED_10MBPS 0x0000 /* 00=10Mbps */ -#define GG82563_PSSR_SPEED_100MBPS 0x4000 /* 01=100Mbps */ -#define GG82563_PSSR_SPEED_1000MBPS 0x8000 /* 10=1000Mbps */ - -/* PHY Specific Status Register 2 (Page 0, Register 19) */ -#define GG82563_PSSR2_JABBER 0x0001 /* 1=Jabber */ -#define GG82563_PSSR2_POLARITY_CHANGED 0x0002 /* 1=Polarity Changed */ -#define GG82563_PSSR2_ENERGY_DETECT_CHANGED 0x0010 /* 1=Energy Detect Changed */ -#define GG82563_PSSR2_DOWNSHIFT_INTERRUPT 0x0020 /* 1=Downshift Detected */ -#define GG82563_PSSR2_MDI_CROSSOVER_CHANGE 0x0040 /* 1=Crossover Changed */ -#define GG82563_PSSR2_FALSE_CARRIER 0x0100 /* 1=False Carrier */ -#define GG82563_PSSR2_SYMBOL_ERROR 0x0200 /* 1=Symbol Error */ -#define GG82563_PSSR2_LINK_STATUS_CHANGED 0x0400 /* 1=Link Status Changed */ -#define GG82563_PSSR2_AUTO_NEG_COMPLETED 0x0800 /* 1=Auto-Neg Completed */ -#define GG82563_PSSR2_PAGE_RECEIVED 0x1000 /* 1=Page Received */ -#define GG82563_PSSR2_DUPLEX_CHANGED 0x2000 /* 1=Duplex Changed */ -#define GG82563_PSSR2_SPEED_CHANGED 0x4000 /* 1=Speed Changed */ -#define GG82563_PSSR2_AUTO_NEG_ERROR 0x8000 /* 1=Auto-Neg Error */ - -/* PHY Specific Control Register 2 (Page 0, Register 26) */ -#define GG82563_PSCR2_10BT_POLARITY_FORCE 0x0002 /* 1=Force Negative Polarity */ -#define GG82563_PSCR2_1000MB_TEST_SELECT_MASK 0x000C -#define GG82563_PSCR2_1000MB_TEST_SELECT_NORMAL 0x0000 /* 00,01=Normal Operation */ -#define GG82563_PSCR2_1000MB_TEST_SELECT_112NS 0x0008 /* 10=Select 112ns Sequence */ -#define GG82563_PSCR2_1000MB_TEST_SELECT_16NS 0x000C /* 11=Select 16ns Sequence */ -#define GG82563_PSCR2_REVERSE_AUTO_NEG 0x2000 /* 1=Reverse Auto-Negotiation */ -#define GG82563_PSCR2_1000BT_DISABLE 0x4000 /* 1=Disable 1000BASE-T */ -#define GG82563_PSCR2_TRANSMITER_TYPE_MASK 0x8000 -#define GG82563_PSCR2_TRANSMITTER_TYPE_CLASS_B 0x0000 /* 0=Class B */ -#define GG82563_PSCR2_TRANSMITTER_TYPE_CLASS_A 0x8000 /* 1=Class A */ - -/* MAC Specific Control Register (Page 2, Register 21) */ -/* Tx clock speed for Link Down and 1000BASE-T for the following speeds */ -#define GG82563_MSCR_TX_CLK_MASK 0x0007 -#define GG82563_MSCR_TX_CLK_10MBPS_2_5MHZ 0x0004 -#define GG82563_MSCR_TX_CLK_100MBPS_25MHZ 0x0005 -#define GG82563_MSCR_TX_CLK_1000MBPS_2_5MHZ 0x0006 -#define GG82563_MSCR_TX_CLK_1000MBPS_25MHZ 0x0007 - -#define GG82563_MSCR_ASSERT_CRS_ON_TX 0x0010 /* 1=Assert */ - -/* DSP Distance Register (Page 5, Register 26) */ -#define GG82563_DSPD_CABLE_LENGTH 0x0007 /* 0 = <50M; - 1 = 50-80M; - 2 = 80-110M; - 3 = 110-140M; - 4 = >140M */ - -/* Kumeran Mode Control Register (Page 193, Register 16) */ -#define GG82563_KMCR_PHY_LEDS_EN 0x0020 /* 1=PHY LEDs, 0=Kumeran Inband LEDs */ -#define GG82563_KMCR_FORCE_LINK_UP 0x0040 /* 1=Force Link Up */ -#define GG82563_KMCR_SUPPRESS_SGMII_EPD_EXT 0x0080 -#define GG82563_KMCR_MDIO_BUS_SPEED_SELECT_MASK 0x0400 -#define GG82563_KMCR_MDIO_BUS_SPEED_SELECT 0x0400 /* 1=6.25MHz, 0=0.8MHz */ -#define GG82563_KMCR_PASS_FALSE_CARRIER 0x0800 - -/* Power Management Control Register (Page 193, Register 20) */ -#define GG82563_PMCR_ENABLE_ELECTRICAL_IDLE 0x0001 /* 1=Enalbe SERDES Electrical Idle */ -#define GG82563_PMCR_DISABLE_PORT 0x0002 /* 1=Disable Port */ -#define GG82563_PMCR_DISABLE_SERDES 0x0004 /* 1=Disable SERDES */ -#define GG82563_PMCR_REVERSE_AUTO_NEG 0x0008 /* 1=Enable Reverse Auto-Negotiation */ -#define GG82563_PMCR_DISABLE_1000_NON_D0 0x0010 /* 1=Disable 1000Mbps Auto-Neg in non D0 */ -#define GG82563_PMCR_DISABLE_1000 0x0020 /* 1=Disable 1000Mbps Auto-Neg Always */ -#define GG82563_PMCR_REVERSE_AUTO_NEG_D0A 0x0040 /* 1=Enable D0a Reverse Auto-Negotiation */ -#define GG82563_PMCR_FORCE_POWER_STATE 0x0080 /* 1=Force Power State */ -#define GG82563_PMCR_PROGRAMMED_POWER_STATE_MASK 0x0300 -#define GG82563_PMCR_PROGRAMMED_POWER_STATE_DR 0x0000 /* 00=Dr */ -#define GG82563_PMCR_PROGRAMMED_POWER_STATE_D0U 0x0100 /* 01=D0u */ -#define GG82563_PMCR_PROGRAMMED_POWER_STATE_D0A 0x0200 /* 10=D0a */ -#define GG82563_PMCR_PROGRAMMED_POWER_STATE_D3 0x0300 /* 11=D3 */ - -/* In-Band Control Register (Page 194, Register 18) */ -#define GG82563_ICR_DIS_PADDING 0x0010 /* Disable Padding Use */ - - /* Bit definitions for valid PHY IDs. */ /* I = Integrated * E = External @@ -3154,8 +2869,6 @@ struct e1000_host_command_info { #define M88E1011_I_REV_4 0x04 #define M88E1111_I_PHY_ID 0x01410CC0 #define L1LXT971A_PHY_ID 0x001378E0 -#define GG82563_E_PHY_ID 0x01410CA0 - /* Bits... * 15-5: page @@ -3166,41 +2879,41 @@ struct e1000_host_command_info { (((page) << PHY_PAGE_SHIFT) | ((reg) & MAX_PHY_REG_ADDRESS)) #define IGP3_PHY_PORT_CTRL \ - PHY_REG(769, 17) /* Port General Configuration */ + PHY_REG(769, 17) /* Port General Configuration */ #define IGP3_PHY_RATE_ADAPT_CTRL \ - PHY_REG(769, 25) /* Rate Adapter Control Register */ + PHY_REG(769, 25) /* Rate Adapter Control Register */ #define IGP3_KMRN_FIFO_CTRL_STATS \ - PHY_REG(770, 16) /* KMRN FIFO's control/status register */ + PHY_REG(770, 16) /* KMRN FIFO's control/status register */ #define IGP3_KMRN_POWER_MNG_CTRL \ - PHY_REG(770, 17) /* KMRN Power Management Control Register */ + PHY_REG(770, 17) /* KMRN Power Management Control Register */ #define IGP3_KMRN_INBAND_CTRL \ - PHY_REG(770, 18) /* KMRN Inband Control Register */ + PHY_REG(770, 18) /* KMRN Inband Control Register */ #define IGP3_KMRN_DIAG \ - PHY_REG(770, 19) /* KMRN Diagnostic register */ -#define IGP3_KMRN_DIAG_PCS_LOCK_LOSS 0x0002 /* RX PCS is not synced */ + PHY_REG(770, 19) /* KMRN Diagnostic register */ +#define IGP3_KMRN_DIAG_PCS_LOCK_LOSS 0x0002 /* RX PCS is not synced */ #define IGP3_KMRN_ACK_TIMEOUT \ - PHY_REG(770, 20) /* KMRN Acknowledge Timeouts register */ + PHY_REG(770, 20) /* KMRN Acknowledge Timeouts register */ #define IGP3_VR_CTRL \ - PHY_REG(776, 18) /* Voltage regulator control register */ -#define IGP3_VR_CTRL_MODE_SHUT 0x0200 /* Enter powerdown, shutdown VRs */ -#define IGP3_VR_CTRL_MODE_MASK 0x0300 /* Shutdown VR Mask */ + PHY_REG(776, 18) /* Voltage regulator control register */ +#define IGP3_VR_CTRL_MODE_SHUT 0x0200 /* Enter powerdown, shutdown VRs */ +#define IGP3_VR_CTRL_MODE_MASK 0x0300 /* Shutdown VR Mask */ #define IGP3_CAPABILITY \ - PHY_REG(776, 19) /* IGP3 Capability Register */ + PHY_REG(776, 19) /* IGP3 Capability Register */ /* Capabilities for SKU Control */ -#define IGP3_CAP_INITIATE_TEAM 0x0001 /* Able to initiate a team */ -#define IGP3_CAP_WFM 0x0002 /* Support WoL and PXE */ -#define IGP3_CAP_ASF 0x0004 /* Support ASF */ -#define IGP3_CAP_LPLU 0x0008 /* Support Low Power Link Up */ -#define IGP3_CAP_DC_AUTO_SPEED 0x0010 /* Support AC/DC Auto Link Speed */ -#define IGP3_CAP_SPD 0x0020 /* Support Smart Power Down */ -#define IGP3_CAP_MULT_QUEUE 0x0040 /* Support 2 tx & 2 rx queues */ -#define IGP3_CAP_RSS 0x0080 /* Support RSS */ -#define IGP3_CAP_8021PQ 0x0100 /* Support 802.1Q & 802.1p */ -#define IGP3_CAP_AMT_CB 0x0200 /* Support active manageability and circuit breaker */ +#define IGP3_CAP_INITIATE_TEAM 0x0001 /* Able to initiate a team */ +#define IGP3_CAP_WFM 0x0002 /* Support WoL and PXE */ +#define IGP3_CAP_ASF 0x0004 /* Support ASF */ +#define IGP3_CAP_LPLU 0x0008 /* Support Low Power Link Up */ +#define IGP3_CAP_DC_AUTO_SPEED 0x0010 /* Support AC/DC Auto Link Speed */ +#define IGP3_CAP_SPD 0x0020 /* Support Smart Power Down */ +#define IGP3_CAP_MULT_QUEUE 0x0040 /* Support 2 tx & 2 rx queues */ +#define IGP3_CAP_RSS 0x0080 /* Support RSS */ +#define IGP3_CAP_8021PQ 0x0100 /* Support 802.1Q & 802.1p */ +#define IGP3_CAP_AMT_CB 0x0200 /* Support active manageability and circuit breaker */ #define IGP3_PPC_JORDAN_EN 0x0001 #define IGP3_PPC_JORDAN_GIGA_SPEED 0x0002 @@ -3210,69 +2923,69 @@ struct e1000_host_command_info { #define IGP3_KMRN_PMC_K0S_MODE1_EN_GIGA 0x0020 #define IGP3_KMRN_PMC_K0S_MODE1_EN_100 0x0040 -#define IGP3E1000_PHY_MISC_CTRL 0x1B /* Misc. Ctrl register */ -#define IGP3_PHY_MISC_DUPLEX_MANUAL_SET 0x1000 /* Duplex Manual Set */ +#define IGP3E1000_PHY_MISC_CTRL 0x1B /* Misc. Ctrl register */ +#define IGP3_PHY_MISC_DUPLEX_MANUAL_SET 0x1000 /* Duplex Manual Set */ #define IGP3_KMRN_EXT_CTRL PHY_REG(770, 18) #define IGP3_KMRN_EC_DIS_INBAND 0x0080 #define IGP03E1000_E_PHY_ID 0x02A80390 -#define IFE_E_PHY_ID 0x02A80330 /* 10/100 PHY */ +#define IFE_E_PHY_ID 0x02A80330 /* 10/100 PHY */ #define IFE_PLUS_E_PHY_ID 0x02A80320 #define IFE_C_E_PHY_ID 0x02A80310 -#define IFE_PHY_EXTENDED_STATUS_CONTROL 0x10 /* 100BaseTx Extended Status, Control and Address */ -#define IFE_PHY_SPECIAL_CONTROL 0x11 /* 100BaseTx PHY special control register */ -#define IFE_PHY_RCV_FALSE_CARRIER 0x13 /* 100BaseTx Receive False Carrier Counter */ -#define IFE_PHY_RCV_DISCONNECT 0x14 /* 100BaseTx Receive Disconnet Counter */ -#define IFE_PHY_RCV_ERROT_FRAME 0x15 /* 100BaseTx Receive Error Frame Counter */ -#define IFE_PHY_RCV_SYMBOL_ERR 0x16 /* Receive Symbol Error Counter */ -#define IFE_PHY_PREM_EOF_ERR 0x17 /* 100BaseTx Receive Premature End Of Frame Error Counter */ -#define IFE_PHY_RCV_EOF_ERR 0x18 /* 10BaseT Receive End Of Frame Error Counter */ -#define IFE_PHY_TX_JABBER_DETECT 0x19 /* 10BaseT Transmit Jabber Detect Counter */ -#define IFE_PHY_EQUALIZER 0x1A /* PHY Equalizer Control and Status */ -#define IFE_PHY_SPECIAL_CONTROL_LED 0x1B /* PHY special control and LED configuration */ -#define IFE_PHY_MDIX_CONTROL 0x1C /* MDI/MDI-X Control register */ -#define IFE_PHY_HWI_CONTROL 0x1D /* Hardware Integrity Control (HWI) */ - -#define IFE_PESC_REDUCED_POWER_DOWN_DISABLE 0x2000 /* Defaut 1 = Disable auto reduced power down */ -#define IFE_PESC_100BTX_POWER_DOWN 0x0400 /* Indicates the power state of 100BASE-TX */ -#define IFE_PESC_10BTX_POWER_DOWN 0x0200 /* Indicates the power state of 10BASE-T */ -#define IFE_PESC_POLARITY_REVERSED 0x0100 /* Indicates 10BASE-T polarity */ -#define IFE_PESC_PHY_ADDR_MASK 0x007C /* Bit 6:2 for sampled PHY address */ -#define IFE_PESC_SPEED 0x0002 /* Auto-negotiation speed result 1=100Mbs, 0=10Mbs */ -#define IFE_PESC_DUPLEX 0x0001 /* Auto-negotiation duplex result 1=Full, 0=Half */ +#define IFE_PHY_EXTENDED_STATUS_CONTROL 0x10 /* 100BaseTx Extended Status, Control and Address */ +#define IFE_PHY_SPECIAL_CONTROL 0x11 /* 100BaseTx PHY special control register */ +#define IFE_PHY_RCV_FALSE_CARRIER 0x13 /* 100BaseTx Receive False Carrier Counter */ +#define IFE_PHY_RCV_DISCONNECT 0x14 /* 100BaseTx Receive Disconnect Counter */ +#define IFE_PHY_RCV_ERROT_FRAME 0x15 /* 100BaseTx Receive Error Frame Counter */ +#define IFE_PHY_RCV_SYMBOL_ERR 0x16 /* Receive Symbol Error Counter */ +#define IFE_PHY_PREM_EOF_ERR 0x17 /* 100BaseTx Receive Premature End Of Frame Error Counter */ +#define IFE_PHY_RCV_EOF_ERR 0x18 /* 10BaseT Receive End Of Frame Error Counter */ +#define IFE_PHY_TX_JABBER_DETECT 0x19 /* 10BaseT Transmit Jabber Detect Counter */ +#define IFE_PHY_EQUALIZER 0x1A /* PHY Equalizer Control and Status */ +#define IFE_PHY_SPECIAL_CONTROL_LED 0x1B /* PHY special control and LED configuration */ +#define IFE_PHY_MDIX_CONTROL 0x1C /* MDI/MDI-X Control register */ +#define IFE_PHY_HWI_CONTROL 0x1D /* Hardware Integrity Control (HWI) */ + +#define IFE_PESC_REDUCED_POWER_DOWN_DISABLE 0x2000 /* Default 1 = Disable auto reduced power down */ +#define IFE_PESC_100BTX_POWER_DOWN 0x0400 /* Indicates the power state of 100BASE-TX */ +#define IFE_PESC_10BTX_POWER_DOWN 0x0200 /* Indicates the power state of 10BASE-T */ +#define IFE_PESC_POLARITY_REVERSED 0x0100 /* Indicates 10BASE-T polarity */ +#define IFE_PESC_PHY_ADDR_MASK 0x007C /* Bit 6:2 for sampled PHY address */ +#define IFE_PESC_SPEED 0x0002 /* Auto-negotiation speed result 1=100Mbs, 0=10Mbs */ +#define IFE_PESC_DUPLEX 0x0001 /* Auto-negotiation duplex result 1=Full, 0=Half */ #define IFE_PESC_POLARITY_REVERSED_SHIFT 8 -#define IFE_PSC_DISABLE_DYNAMIC_POWER_DOWN 0x0100 /* 1 = Dyanmic Power Down disabled */ -#define IFE_PSC_FORCE_POLARITY 0x0020 /* 1=Reversed Polarity, 0=Normal */ -#define IFE_PSC_AUTO_POLARITY_DISABLE 0x0010 /* 1=Auto Polarity Disabled, 0=Enabled */ -#define IFE_PSC_JABBER_FUNC_DISABLE 0x0001 /* 1=Jabber Disabled, 0=Normal Jabber Operation */ +#define IFE_PSC_DISABLE_DYNAMIC_POWER_DOWN 0x0100 /* 1 = Dynamic Power Down disabled */ +#define IFE_PSC_FORCE_POLARITY 0x0020 /* 1=Reversed Polarity, 0=Normal */ +#define IFE_PSC_AUTO_POLARITY_DISABLE 0x0010 /* 1=Auto Polarity Disabled, 0=Enabled */ +#define IFE_PSC_JABBER_FUNC_DISABLE 0x0001 /* 1=Jabber Disabled, 0=Normal Jabber Operation */ #define IFE_PSC_FORCE_POLARITY_SHIFT 5 #define IFE_PSC_AUTO_POLARITY_DISABLE_SHIFT 4 -#define IFE_PMC_AUTO_MDIX 0x0080 /* 1=enable MDI/MDI-X feature, default 0=disabled */ -#define IFE_PMC_FORCE_MDIX 0x0040 /* 1=force MDIX-X, 0=force MDI */ -#define IFE_PMC_MDIX_STATUS 0x0020 /* 1=MDI-X, 0=MDI */ -#define IFE_PMC_AUTO_MDIX_COMPLETE 0x0010 /* Resolution algorithm is completed */ +#define IFE_PMC_AUTO_MDIX 0x0080 /* 1=enable MDI/MDI-X feature, default 0=disabled */ +#define IFE_PMC_FORCE_MDIX 0x0040 /* 1=force MDIX-X, 0=force MDI */ +#define IFE_PMC_MDIX_STATUS 0x0020 /* 1=MDI-X, 0=MDI */ +#define IFE_PMC_AUTO_MDIX_COMPLETE 0x0010 /* Resolution algorithm is completed */ #define IFE_PMC_MDIX_MODE_SHIFT 6 -#define IFE_PHC_MDIX_RESET_ALL_MASK 0x0000 /* Disable auto MDI-X */ - -#define IFE_PHC_HWI_ENABLE 0x8000 /* Enable the HWI feature */ -#define IFE_PHC_ABILITY_CHECK 0x4000 /* 1= Test Passed, 0=failed */ -#define IFE_PHC_TEST_EXEC 0x2000 /* PHY launch test pulses on the wire */ -#define IFE_PHC_HIGHZ 0x0200 /* 1 = Open Circuit */ -#define IFE_PHC_LOWZ 0x0400 /* 1 = Short Circuit */ -#define IFE_PHC_LOW_HIGH_Z_MASK 0x0600 /* Mask for indication type of problem on the line */ -#define IFE_PHC_DISTANCE_MASK 0x01FF /* Mask for distance to the cable problem, in 80cm granularity */ -#define IFE_PHC_RESET_ALL_MASK 0x0000 /* Disable HWI */ -#define IFE_PSCL_PROBE_MODE 0x0020 /* LED Probe mode */ -#define IFE_PSCL_PROBE_LEDS_OFF 0x0006 /* Force LEDs 0 and 2 off */ -#define IFE_PSCL_PROBE_LEDS_ON 0x0007 /* Force LEDs 0 and 2 on */ - -#define ICH_FLASH_COMMAND_TIMEOUT 5000 /* 5000 uSecs - adjusted */ -#define ICH_FLASH_ERASE_TIMEOUT 3000000 /* Up to 3 seconds - worst case */ -#define ICH_FLASH_CYCLE_REPEAT_COUNT 10 /* 10 cycles */ +#define IFE_PHC_MDIX_RESET_ALL_MASK 0x0000 /* Disable auto MDI-X */ + +#define IFE_PHC_HWI_ENABLE 0x8000 /* Enable the HWI feature */ +#define IFE_PHC_ABILITY_CHECK 0x4000 /* 1= Test Passed, 0=failed */ +#define IFE_PHC_TEST_EXEC 0x2000 /* PHY launch test pulses on the wire */ +#define IFE_PHC_HIGHZ 0x0200 /* 1 = Open Circuit */ +#define IFE_PHC_LOWZ 0x0400 /* 1 = Short Circuit */ +#define IFE_PHC_LOW_HIGH_Z_MASK 0x0600 /* Mask for indication type of problem on the line */ +#define IFE_PHC_DISTANCE_MASK 0x01FF /* Mask for distance to the cable problem, in 80cm granularity */ +#define IFE_PHC_RESET_ALL_MASK 0x0000 /* Disable HWI */ +#define IFE_PSCL_PROBE_MODE 0x0020 /* LED Probe mode */ +#define IFE_PSCL_PROBE_LEDS_OFF 0x0006 /* Force LEDs 0 and 2 off */ +#define IFE_PSCL_PROBE_LEDS_ON 0x0007 /* Force LEDs 0 and 2 on */ + +#define ICH_FLASH_COMMAND_TIMEOUT 5000 /* 5000 uSecs - adjusted */ +#define ICH_FLASH_ERASE_TIMEOUT 3000000 /* Up to 3 seconds - worst case */ +#define ICH_FLASH_CYCLE_REPEAT_COUNT 10 /* 10 cycles */ #define ICH_FLASH_SEG_SIZE_256 256 #define ICH_FLASH_SEG_SIZE_4K 4096 #define ICH_FLASH_SEG_SIZE_64K 65536 @@ -3305,74 +3018,6 @@ struct e1000_host_command_info { #define ICH_GFPREG_BASE_MASK 0x1FFF #define ICH_FLASH_LINEAR_ADDR_MASK 0x00FFFFFF -/* ICH8 GbE Flash Hardware Sequencing Flash Status Register bit breakdown */ -/* Offset 04h HSFSTS */ -union ich8_hws_flash_status { - struct ich8_hsfsts { -#ifdef __BIG_ENDIAN - u16 reserved2 :6; - u16 fldesvalid :1; - u16 flockdn :1; - u16 flcdone :1; - u16 flcerr :1; - u16 dael :1; - u16 berasesz :2; - u16 flcinprog :1; - u16 reserved1 :2; -#else - u16 flcdone :1; /* bit 0 Flash Cycle Done */ - u16 flcerr :1; /* bit 1 Flash Cycle Error */ - u16 dael :1; /* bit 2 Direct Access error Log */ - u16 berasesz :2; /* bit 4:3 Block/Sector Erase Size */ - u16 flcinprog :1; /* bit 5 flash SPI cycle in Progress */ - u16 reserved1 :2; /* bit 13:6 Reserved */ - u16 reserved2 :6; /* bit 13:6 Reserved */ - u16 fldesvalid :1; /* bit 14 Flash Descriptor Valid */ - u16 flockdn :1; /* bit 15 Flash Configuration Lock-Down */ -#endif - } hsf_status; - u16 regval; -}; - -/* ICH8 GbE Flash Hardware Sequencing Flash control Register bit breakdown */ -/* Offset 06h FLCTL */ -union ich8_hws_flash_ctrl { - struct ich8_hsflctl { -#ifdef __BIG_ENDIAN - u16 fldbcount :2; - u16 flockdn :6; - u16 flcgo :1; - u16 flcycle :2; - u16 reserved :5; -#else - u16 flcgo :1; /* 0 Flash Cycle Go */ - u16 flcycle :2; /* 2:1 Flash Cycle */ - u16 reserved :5; /* 7:3 Reserved */ - u16 fldbcount :2; /* 9:8 Flash Data Byte Count */ - u16 flockdn :6; /* 15:10 Reserved */ -#endif - } hsf_ctrl; - u16 regval; -}; - -/* ICH8 Flash Region Access Permissions */ -union ich8_hws_flash_regacc { - struct ich8_flracc { -#ifdef __BIG_ENDIAN - u32 gmwag :8; - u32 gmrag :8; - u32 grwa :8; - u32 grra :8; -#else - u32 grra :8; /* 0:7 GbE region Read Access */ - u32 grwa :8; /* 8:15 GbE region Write Access */ - u32 gmrag :8; /* 23:16 GbE Master Read Access Grant */ - u32 gmwag :8; /* 31:24 GbE Master Write Access Grant */ -#endif - } hsf_flregacc; - u16 regval; -}; - /* Miscellaneous PHY bit definitions. */ #define PHY_PREAMBLE 0xFFFFFFFF #define PHY_SOF 0x01 @@ -3384,10 +3029,10 @@ union ich8_hws_flash_regacc { #define MII_CR_SPEED_100 0x2000 #define MII_CR_SPEED_10 0x0000 #define E1000_PHY_ADDRESS 0x01 -#define PHY_AUTO_NEG_TIME 45 /* 4.5 Seconds */ -#define PHY_FORCE_TIME 20 /* 2.0 Seconds */ +#define PHY_AUTO_NEG_TIME 45 /* 4.5 Seconds */ +#define PHY_FORCE_TIME 20 /* 2.0 Seconds */ #define PHY_REVISION_MASK 0xFFFFFFF0 -#define DEVICE_SPEED_MASK 0x00000300 /* Device Ctrl Reg Speed Mask */ +#define DEVICE_SPEED_MASK 0x00000300 /* Device Ctrl Reg Speed Mask */ #define REG4_SPEED_MASK 0x01E0 #define REG9_SPEED_MASK 0x0300 #define ADVERTISE_10_HALF 0x0001 @@ -3396,8 +3041,8 @@ union ich8_hws_flash_regacc { #define ADVERTISE_100_FULL 0x0008 #define ADVERTISE_1000_HALF 0x0010 #define ADVERTISE_1000_FULL 0x0020 -#define AUTONEG_ADVERTISE_SPEED_DEFAULT 0x002F /* Everything but 1000-Half */ -#define AUTONEG_ADVERTISE_10_100_ALL 0x000F /* All 10/100 speeds*/ -#define AUTONEG_ADVERTISE_10_ALL 0x0003 /* 10Mbps Full & Half speeds*/ +#define AUTONEG_ADVERTISE_SPEED_DEFAULT 0x002F /* Everything but 1000-Half */ +#define AUTONEG_ADVERTISE_10_100_ALL 0x000F /* All 10/100 speeds */ +#define AUTONEG_ADVERTISE_10_ALL 0x0003 /* 10Mbps Full & Half speeds */ #endif /* _E1000_HW_H_ */ diff --git a/drivers/net/e1000/e1000_main.c b/drivers/net/e1000/e1000_main.c index c66dd4f9437c..bcd192ca47b0 100644 --- a/drivers/net/e1000/e1000_main.c +++ b/drivers/net/e1000/e1000_main.c @@ -31,7 +31,7 @@ char e1000_driver_name[] = "e1000"; static char e1000_driver_string[] = "Intel(R) PRO/1000 Network Driver"; -#define DRV_VERSION "7.3.21-k3-NAPI" +#define DRV_VERSION "7.3.21-k5-NAPI" const char e1000_driver_version[] = DRV_VERSION; static const char e1000_copyright[] = "Copyright (c) 1999-2006 Intel Corporation."; @@ -131,7 +131,6 @@ static struct net_device_stats * e1000_get_stats(struct net_device *netdev); static int e1000_change_mtu(struct net_device *netdev, int new_mtu); static int e1000_set_mac(struct net_device *netdev, void *p); static irqreturn_t e1000_intr(int irq, void *data); -static irqreturn_t e1000_intr_msi(int irq, void *data); static bool e1000_clean_tx_irq(struct e1000_adapter *adapter, struct e1000_tx_ring *tx_ring); static int e1000_clean(struct napi_struct *napi, int budget); @@ -258,25 +257,14 @@ module_exit(e1000_exit_module); static int e1000_request_irq(struct e1000_adapter *adapter) { - struct e1000_hw *hw = &adapter->hw; struct net_device *netdev = adapter->netdev; irq_handler_t handler = e1000_intr; int irq_flags = IRQF_SHARED; int err; - if (hw->mac_type >= e1000_82571) { - adapter->have_msi = !pci_enable_msi(adapter->pdev); - if (adapter->have_msi) { - handler = e1000_intr_msi; - irq_flags = 0; - } - } - err = request_irq(adapter->pdev->irq, handler, irq_flags, netdev->name, netdev); if (err) { - if (adapter->have_msi) - pci_disable_msi(adapter->pdev); DPRINTK(PROBE, ERR, "Unable to allocate interrupt Error: %d\n", err); } @@ -289,9 +277,6 @@ static void e1000_free_irq(struct e1000_adapter *adapter) struct net_device *netdev = adapter->netdev; free_irq(adapter->pdev->irq, netdev); - - if (adapter->have_msi) - pci_disable_msi(adapter->pdev); } /** @@ -345,76 +330,6 @@ static void e1000_update_mng_vlan(struct e1000_adapter *adapter) } } -/** - * e1000_release_hw_control - release control of the h/w to f/w - * @adapter: address of board private structure - * - * e1000_release_hw_control resets {CTRL_EXT|FWSM}:DRV_LOAD bit. - * For ASF and Pass Through versions of f/w this means that the - * driver is no longer loaded. For AMT version (only with 82573) i - * of the f/w this means that the network i/f is closed. - * - **/ - -static void e1000_release_hw_control(struct e1000_adapter *adapter) -{ - u32 ctrl_ext; - u32 swsm; - struct e1000_hw *hw = &adapter->hw; - - /* Let firmware taken over control of h/w */ - switch (hw->mac_type) { - case e1000_82573: - swsm = er32(SWSM); - ew32(SWSM, swsm & ~E1000_SWSM_DRV_LOAD); - break; - case e1000_82571: - case e1000_82572: - case e1000_80003es2lan: - case e1000_ich8lan: - ctrl_ext = er32(CTRL_EXT); - ew32(CTRL_EXT, ctrl_ext & ~E1000_CTRL_EXT_DRV_LOAD); - break; - default: - break; - } -} - -/** - * e1000_get_hw_control - get control of the h/w from f/w - * @adapter: address of board private structure - * - * e1000_get_hw_control sets {CTRL_EXT|FWSM}:DRV_LOAD bit. - * For ASF and Pass Through versions of f/w this means that - * the driver is loaded. For AMT version (only with 82573) - * of the f/w this means that the network i/f is open. - * - **/ - -static void e1000_get_hw_control(struct e1000_adapter *adapter) -{ - u32 ctrl_ext; - u32 swsm; - struct e1000_hw *hw = &adapter->hw; - - /* Let firmware know the driver has taken over */ - switch (hw->mac_type) { - case e1000_82573: - swsm = er32(SWSM); - ew32(SWSM, swsm | E1000_SWSM_DRV_LOAD); - break; - case e1000_82571: - case e1000_82572: - case e1000_80003es2lan: - case e1000_ich8lan: - ctrl_ext = er32(CTRL_EXT); - ew32(CTRL_EXT, ctrl_ext | E1000_CTRL_EXT_DRV_LOAD); - break; - default: - break; - } -} - static void e1000_init_manageability(struct e1000_adapter *adapter) { struct e1000_hw *hw = &adapter->hw; @@ -425,20 +340,6 @@ static void e1000_init_manageability(struct e1000_adapter *adapter) /* disable hardware interception of ARP */ manc &= ~(E1000_MANC_ARP_EN); - /* enable receiving management packets to the host */ - /* this will probably generate destination unreachable messages - * from the host OS, but the packets will be handled on SMBUS */ - if (hw->has_manc2h) { - u32 manc2h = er32(MANC2H); - - manc |= E1000_MANC_EN_MNG2HOST; -#define E1000_MNG2HOST_PORT_623 (1 << 5) -#define E1000_MNG2HOST_PORT_664 (1 << 6) - manc2h |= E1000_MNG2HOST_PORT_623; - manc2h |= E1000_MNG2HOST_PORT_664; - ew32(MANC2H, manc2h); - } - ew32(MANC, manc); } } @@ -453,12 +354,6 @@ static void e1000_release_manageability(struct e1000_adapter *adapter) /* re-enable hardware interception of ARP */ manc |= E1000_MANC_ARP_EN; - if (hw->has_manc2h) - manc &= ~E1000_MANC_EN_MNG2HOST; - - /* don't explicitly have to mess with MANC2H since - * MANC has an enable disable that gates MANC2H */ - ew32(MANC, manc); } } @@ -563,15 +458,6 @@ static void e1000_power_down_phy(struct e1000_adapter *adapter) if (er32(MANC) & E1000_MANC_SMBUS_EN) goto out; break; - case e1000_82571: - case e1000_82572: - case e1000_82573: - case e1000_80003es2lan: - case e1000_ich8lan: - if (e1000_check_mng_mode(hw) || - e1000_check_phy_reset_block(hw)) - goto out; - break; default: goto out; } @@ -599,8 +485,7 @@ void e1000_down(struct e1000_adapter *adapter) ew32(RCTL, rctl & ~E1000_RCTL_EN); /* flush and sleep below */ - /* can be netif_tx_disable when NETIF_F_LLTX is removed */ - netif_stop_queue(netdev); + netif_tx_disable(netdev); /* disable transmits in the hardware */ tctl = er32(TCTL); @@ -671,16 +556,6 @@ void e1000_reset(struct e1000_adapter *adapter) legacy_pba_adjust = true; pba = E1000_PBA_30K; break; - case e1000_82571: - case e1000_82572: - case e1000_80003es2lan: - pba = E1000_PBA_38K; - break; - case e1000_82573: - pba = E1000_PBA_20K; - break; - case e1000_ich8lan: - pba = E1000_PBA_8K; case e1000_undefined: case e1000_num_macs: break; @@ -744,16 +619,8 @@ void e1000_reset(struct e1000_adapter *adapter) /* if short on rx space, rx wins and must trump tx * adjustment or use Early Receive if available */ - if (pba < min_rx_space) { - switch (hw->mac_type) { - case e1000_82573: - /* ERT enabled in e1000_configure_rx */ - break; - default: - pba = min_rx_space; - break; - } - } + if (pba < min_rx_space) + pba = min_rx_space; } } @@ -789,7 +656,6 @@ void e1000_reset(struct e1000_adapter *adapter) /* if (adapter->hwflags & HWFLAGS_PHY_PWR_BIT) { */ if (hw->mac_type >= e1000_82544 && - hw->mac_type <= e1000_82547_rev_2 && hw->autoneg == 1 && hw->autoneg_advertised == ADVERTISE_1000_FULL) { u32 ctrl = er32(CTRL); @@ -806,20 +672,6 @@ void e1000_reset(struct e1000_adapter *adapter) e1000_reset_adaptive(hw); e1000_phy_get_info(hw, &adapter->phy_info); - if (!adapter->smart_power_down && - (hw->mac_type == e1000_82571 || - hw->mac_type == e1000_82572)) { - u16 phy_data = 0; - /* speed up time to link by disabling smart power down, ignore - * the return value of this function because there is nothing - * different we would do if it failed */ - e1000_read_phy_reg(hw, IGP02E1000_PHY_POWER_MGMT, - &phy_data); - phy_data &= ~IGP02E1000_PM_SPD; - e1000_write_phy_reg(hw, IGP02E1000_PHY_POWER_MGMT, - phy_data); - } - e1000_release_manageability(adapter); } @@ -1046,17 +898,6 @@ static int __devinit e1000_probe(struct pci_dev *pdev, goto err_sw_init; err = -EIO; - /* Flash BAR mapping must happen after e1000_sw_init - * because it depends on mac_type */ - if ((hw->mac_type == e1000_ich8lan) && - (pci_resource_flags(pdev, 1) & IORESOURCE_MEM)) { - hw->flash_address = pci_ioremap_bar(pdev, 1); - if (!hw->flash_address) - goto err_flashmap; - } - - if (e1000_check_phy_reset_block(hw)) - DPRINTK(PROBE, INFO, "PHY reset is blocked due to SOL/IDER session.\n"); if (hw->mac_type >= e1000_82543) { netdev->features = NETIF_F_SG | @@ -1064,21 +905,16 @@ static int __devinit e1000_probe(struct pci_dev *pdev, NETIF_F_HW_VLAN_TX | NETIF_F_HW_VLAN_RX | NETIF_F_HW_VLAN_FILTER; - if (hw->mac_type == e1000_ich8lan) - netdev->features &= ~NETIF_F_HW_VLAN_FILTER; } if ((hw->mac_type >= e1000_82544) && (hw->mac_type != e1000_82547)) netdev->features |= NETIF_F_TSO; - if (hw->mac_type > e1000_82547_rev_2) - netdev->features |= NETIF_F_TSO6; if (pci_using_dac) netdev->features |= NETIF_F_HIGHDMA; netdev->vlan_features |= NETIF_F_TSO; - netdev->vlan_features |= NETIF_F_TSO6; netdev->vlan_features |= NETIF_F_HW_CSUM; netdev->vlan_features |= NETIF_F_SG; @@ -1153,15 +989,8 @@ static int __devinit e1000_probe(struct pci_dev *pdev, EEPROM_INIT_CONTROL2_REG, 1, &eeprom_data); eeprom_apme_mask = E1000_EEPROM_82544_APM; break; - case e1000_ich8lan: - e1000_read_eeprom(hw, - EEPROM_INIT_CONTROL1_REG, 1, &eeprom_data); - eeprom_apme_mask = E1000_EEPROM_ICH8_APME; - break; case e1000_82546: case e1000_82546_rev_3: - case e1000_82571: - case e1000_80003es2lan: if (er32(STATUS) & E1000_STATUS_FUNC_1){ e1000_read_eeprom(hw, EEPROM_INIT_CONTROL3_PORT_B, 1, &eeprom_data); @@ -1185,17 +1014,12 @@ static int __devinit e1000_probe(struct pci_dev *pdev, break; case E1000_DEV_ID_82546EB_FIBER: case E1000_DEV_ID_82546GB_FIBER: - case E1000_DEV_ID_82571EB_FIBER: /* Wake events only supported on port A for dual fiber * regardless of eeprom setting */ if (er32(STATUS) & E1000_STATUS_FUNC_1) adapter->eeprom_wol = 0; break; case E1000_DEV_ID_82546GB_QUAD_COPPER_KSP3: - case E1000_DEV_ID_82571EB_QUAD_COPPER: - case E1000_DEV_ID_82571EB_QUAD_FIBER: - case E1000_DEV_ID_82571EB_QUAD_COPPER_LOWPROFILE: - case E1000_DEV_ID_82571PT_QUAD_COPPER: /* if quad port adapter, disable WoL on all but port A */ if (global_quad_port_a != 0) adapter->eeprom_wol = 0; @@ -1213,39 +1037,18 @@ static int __devinit e1000_probe(struct pci_dev *pdev, /* print bus type/speed/width info */ DPRINTK(PROBE, INFO, "(PCI%s:%s:%s) ", - ((hw->bus_type == e1000_bus_type_pcix) ? "-X" : - (hw->bus_type == e1000_bus_type_pci_express ? " Express":"")), - ((hw->bus_speed == e1000_bus_speed_2500) ? "2.5Gb/s" : - (hw->bus_speed == e1000_bus_speed_133) ? "133MHz" : + ((hw->bus_type == e1000_bus_type_pcix) ? "-X" : ""), + ((hw->bus_speed == e1000_bus_speed_133) ? "133MHz" : (hw->bus_speed == e1000_bus_speed_120) ? "120MHz" : (hw->bus_speed == e1000_bus_speed_100) ? "100MHz" : (hw->bus_speed == e1000_bus_speed_66) ? "66MHz" : "33MHz"), - ((hw->bus_width == e1000_bus_width_64) ? "64-bit" : - (hw->bus_width == e1000_bus_width_pciex_4) ? "Width x4" : - (hw->bus_width == e1000_bus_width_pciex_1) ? "Width x1" : - "32-bit")); + ((hw->bus_width == e1000_bus_width_64) ? "64-bit" : "32-bit")); printk("%pM\n", netdev->dev_addr); - if (hw->bus_type == e1000_bus_type_pci_express) { - DPRINTK(PROBE, WARNING, "This device (id %04x:%04x) will no " - "longer be supported by this driver in the future.\n", - pdev->vendor, pdev->device); - DPRINTK(PROBE, WARNING, "please use the \"e1000e\" " - "driver instead.\n"); - } - /* reset the hardware with the new settings */ e1000_reset(adapter); - /* If the controller is 82573 and f/w is AMT, do not set - * DRV_LOAD until the interface is up. For all other cases, - * let the f/w know that the h/w is now under the control - * of the driver. */ - if (hw->mac_type != e1000_82573 || - !e1000_check_mng_mode(hw)) - e1000_get_hw_control(adapter); - strcpy(netdev->name, "eth%d"); err = register_netdev(netdev); if (err) @@ -1260,14 +1063,11 @@ static int __devinit e1000_probe(struct pci_dev *pdev, return 0; err_register: - e1000_release_hw_control(adapter); err_eeprom: - if (!e1000_check_phy_reset_block(hw)) - e1000_phy_hw_reset(hw); + e1000_phy_hw_reset(hw); if (hw->flash_address) iounmap(hw->flash_address); -err_flashmap: kfree(adapter->tx_ring); kfree(adapter->rx_ring); err_sw_init: @@ -1298,18 +1098,18 @@ static void __devexit e1000_remove(struct pci_dev *pdev) struct e1000_adapter *adapter = netdev_priv(netdev); struct e1000_hw *hw = &adapter->hw; + set_bit(__E1000_DOWN, &adapter->flags); + del_timer_sync(&adapter->tx_fifo_stall_timer); + del_timer_sync(&adapter->watchdog_timer); + del_timer_sync(&adapter->phy_info_timer); + cancel_work_sync(&adapter->reset_task); e1000_release_manageability(adapter); - /* Release control of h/w to f/w. If f/w is AMT enabled, this - * would have already happened in close and is redundant. */ - e1000_release_hw_control(adapter); - unregister_netdev(netdev); - if (!e1000_check_phy_reset_block(hw)) - e1000_phy_hw_reset(hw); + e1000_phy_hw_reset(hw); kfree(adapter->tx_ring); kfree(adapter->rx_ring); @@ -1472,12 +1272,6 @@ static int e1000_open(struct net_device *netdev) e1000_update_mng_vlan(adapter); } - /* If AMT is enabled, let the firmware know that the network - * interface is now open */ - if (hw->mac_type == e1000_82573 && - e1000_check_mng_mode(hw)) - e1000_get_hw_control(adapter); - /* before we allocate an interrupt, we must be ready to handle it. * Setting DEBUG_SHIRQ in the kernel makes it fire an interrupt * as soon as we call pci_request_irq, so we have to setup our @@ -1503,7 +1297,6 @@ static int e1000_open(struct net_device *netdev) return E1000_SUCCESS; err_req_irq: - e1000_release_hw_control(adapter); e1000_power_down_phy(adapter); e1000_free_all_rx_resources(adapter); err_setup_rx: @@ -1548,12 +1341,6 @@ static int e1000_close(struct net_device *netdev) e1000_vlan_rx_kill_vid(netdev, adapter->mng_vlan_id); } - /* If AMT is enabled, let the firmware know that the network - * interface is now closed */ - if (hw->mac_type == e1000_82573 && - e1000_check_mng_mode(hw)) - e1000_release_hw_control(adapter); - return 0; } @@ -1692,7 +1479,7 @@ static void e1000_configure_tx(struct e1000_adapter *adapter) { u64 tdba; struct e1000_hw *hw = &adapter->hw; - u32 tdlen, tctl, tipg, tarc; + u32 tdlen, tctl, tipg; u32 ipgr1, ipgr2; /* Setup the HW Tx Head and Tail descriptor pointers */ @@ -1714,8 +1501,7 @@ static void e1000_configure_tx(struct e1000_adapter *adapter) } /* Set the default values for the Tx Inter Packet Gap timer */ - if (hw->mac_type <= e1000_82547_rev_2 && - (hw->media_type == e1000_media_type_fiber || + if ((hw->media_type == e1000_media_type_fiber || hw->media_type == e1000_media_type_internal_serdes)) tipg = DEFAULT_82543_TIPG_IPGT_FIBER; else @@ -1728,10 +1514,6 @@ static void e1000_configure_tx(struct e1000_adapter *adapter) ipgr1 = DEFAULT_82542_TIPG_IPGR1; ipgr2 = DEFAULT_82542_TIPG_IPGR2; break; - case e1000_80003es2lan: - ipgr1 = DEFAULT_82543_TIPG_IPGR1; - ipgr2 = DEFAULT_80003ES2LAN_TIPG_IPGR2; - break; default: ipgr1 = DEFAULT_82543_TIPG_IPGR1; ipgr2 = DEFAULT_82543_TIPG_IPGR2; @@ -1754,21 +1536,6 @@ static void e1000_configure_tx(struct e1000_adapter *adapter) tctl |= E1000_TCTL_PSP | E1000_TCTL_RTLC | (E1000_COLLISION_THRESHOLD << E1000_CT_SHIFT); - if (hw->mac_type == e1000_82571 || hw->mac_type == e1000_82572) { - tarc = er32(TARC0); - /* set the speed mode bit, we'll clear it if we're not at - * gigabit link later */ - tarc |= (1 << 21); - ew32(TARC0, tarc); - } else if (hw->mac_type == e1000_80003es2lan) { - tarc = er32(TARC0); - tarc |= 1; - ew32(TARC0, tarc); - tarc = er32(TARC1); - tarc |= 1; - ew32(TARC1, tarc); - } - e1000_config_collision_dist(hw); /* Setup Transmit Descriptor Settings for eop descriptor */ @@ -1804,7 +1571,6 @@ static void e1000_configure_tx(struct e1000_adapter *adapter) static int e1000_setup_rx_resources(struct e1000_adapter *adapter, struct e1000_rx_ring *rxdr) { - struct e1000_hw *hw = &adapter->hw; struct pci_dev *pdev = adapter->pdev; int size, desc_len; @@ -1817,10 +1583,7 @@ static int e1000_setup_rx_resources(struct e1000_adapter *adapter, } memset(rxdr->buffer_info, 0, size); - if (hw->mac_type <= e1000_82547_rev_2) - desc_len = sizeof(struct e1000_rx_desc); - else - desc_len = sizeof(union e1000_rx_desc_packet_split); + desc_len = sizeof(struct e1000_rx_desc); /* Round up to nearest 4K */ @@ -1977,7 +1740,7 @@ static void e1000_configure_rx(struct e1000_adapter *adapter) { u64 rdba; struct e1000_hw *hw = &adapter->hw; - u32 rdlen, rctl, rxcsum, ctrl_ext; + u32 rdlen, rctl, rxcsum; if (adapter->netdev->mtu > ETH_DATA_LEN) { rdlen = adapter->rx_ring[0].count * @@ -2004,17 +1767,6 @@ static void e1000_configure_rx(struct e1000_adapter *adapter) ew32(ITR, 1000000000 / (adapter->itr * 256)); } - if (hw->mac_type >= e1000_82571) { - ctrl_ext = er32(CTRL_EXT); - /* Reset delay timers after every interrupt */ - ctrl_ext |= E1000_CTRL_EXT_INT_TIMER_CLR; - /* Auto-Mask interrupts upon ICR access */ - ctrl_ext |= E1000_CTRL_EXT_IAME; - ew32(IAM, 0xffffffff); - ew32(CTRL_EXT, ctrl_ext); - E1000_WRITE_FLUSH(); - } - /* Setup the HW Rx Head and Tail Descriptor Pointers and * the Base and Length of the Rx Descriptor Ring */ switch (adapter->num_rx_queues) { @@ -2329,22 +2081,6 @@ static int e1000_set_mac(struct net_device *netdev, void *p) e1000_rar_set(hw, hw->mac_addr, 0); - /* With 82571 controllers, LAA may be overwritten (with the default) - * due to controller reset from the other port. */ - if (hw->mac_type == e1000_82571) { - /* activate the work around */ - hw->laa_is_present = 1; - - /* Hold a copy of the LAA in RAR[14] This is done so that - * between the time RAR[0] gets clobbered and the time it - * gets fixed (in e1000_watchdog), the actual LAA is in one - * of the RARs and no incoming packets directed to this port - * are dropped. Eventaully the LAA will be in RAR[0] and - * RAR[14] */ - e1000_rar_set(hw, hw->mac_addr, - E1000_RAR_ENTRIES - 1); - } - if (hw->mac_type == e1000_82542_rev2_0) e1000_leave_82542_rst(adapter); @@ -2371,9 +2107,7 @@ static void e1000_set_rx_mode(struct net_device *netdev) u32 rctl; u32 hash_value; int i, rar_entries = E1000_RAR_ENTRIES; - int mta_reg_count = (hw->mac_type == e1000_ich8lan) ? - E1000_NUM_MTA_REGISTERS_ICH8LAN : - E1000_NUM_MTA_REGISTERS; + int mta_reg_count = E1000_NUM_MTA_REGISTERS; u32 *mcarray = kcalloc(mta_reg_count, sizeof(u32), GFP_ATOMIC); if (!mcarray) { @@ -2381,13 +2115,6 @@ static void e1000_set_rx_mode(struct net_device *netdev) return; } - if (hw->mac_type == e1000_ich8lan) - rar_entries = E1000_RAR_ENTRIES_ICH8LAN; - - /* reserve RAR[14] for LAA over-write work-around */ - if (hw->mac_type == e1000_82571) - rar_entries--; - /* Check for Promiscuous and All Multicast modes */ rctl = er32(RCTL); @@ -2396,15 +2123,13 @@ static void e1000_set_rx_mode(struct net_device *netdev) rctl |= (E1000_RCTL_UPE | E1000_RCTL_MPE); rctl &= ~E1000_RCTL_VFE; } else { - if (netdev->flags & IFF_ALLMULTI) { + if (netdev->flags & IFF_ALLMULTI) rctl |= E1000_RCTL_MPE; - } else { + else rctl &= ~E1000_RCTL_MPE; - } - if (adapter->hw.mac_type != e1000_ich8lan) - /* Enable VLAN filter if there is a VLAN */ - if (adapter->vlgrp) - rctl |= E1000_RCTL_VFE; + /* Enable VLAN filter if there is a VLAN */ + if (adapter->vlgrp) + rctl |= E1000_RCTL_VFE; } if (netdev->uc.count > rar_entries - 1) { @@ -2427,7 +2152,6 @@ static void e1000_set_rx_mode(struct net_device *netdev) * * RAR 0 is used for the station MAC adddress * if there are not 14 addresses, go ahead and clear the filters - * -- with 82571 controllers only 0-13 entries are filled here */ i = 1; if (use_uc) @@ -2521,12 +2245,46 @@ static void e1000_82547_tx_fifo_stall(unsigned long data) adapter->tx_fifo_head = 0; atomic_set(&adapter->tx_fifo_stall, 0); netif_wake_queue(netdev); - } else { + } else if (!test_bit(__E1000_DOWN, &adapter->flags)) { mod_timer(&adapter->tx_fifo_stall_timer, jiffies + 1); } } } +static bool e1000_has_link(struct e1000_adapter *adapter) +{ + struct e1000_hw *hw = &adapter->hw; + bool link_active = false; + + /* get_link_status is set on LSC (link status) interrupt or + * rx sequence error interrupt. get_link_status will stay + * false until the e1000_check_for_link establishes link + * for copper adapters ONLY + */ + switch (hw->media_type) { + case e1000_media_type_copper: + if (hw->get_link_status) { + e1000_check_for_link(hw); + link_active = !hw->get_link_status; + } else { + link_active = true; + } + break; + case e1000_media_type_fiber: + e1000_check_for_link(hw); + link_active = !!(er32(STATUS) & E1000_STATUS_LU); + break; + case e1000_media_type_internal_serdes: + e1000_check_for_link(hw); + link_active = hw->serdes_has_link; + break; + default: + break; + } + + return link_active; +} + /** * e1000_watchdog - Timer Call-back * @data: pointer to adapter cast into an unsigned long @@ -2538,33 +2296,16 @@ static void e1000_watchdog(unsigned long data) struct net_device *netdev = adapter->netdev; struct e1000_tx_ring *txdr = adapter->tx_ring; u32 link, tctl; - s32 ret_val; - - ret_val = e1000_check_for_link(hw); - if ((ret_val == E1000_ERR_PHY) && - (hw->phy_type == e1000_phy_igp_3) && - (er32(CTRL) & E1000_PHY_CTRL_GBE_DISABLE)) { - /* See e1000_kumeran_lock_loss_workaround() */ - DPRINTK(LINK, INFO, - "Gigabit has been disabled, downgrading speed\n"); - } - if (hw->mac_type == e1000_82573) { - e1000_enable_tx_pkt_filtering(hw); - if (adapter->mng_vlan_id != hw->mng_cookie.vlan_id) - e1000_update_mng_vlan(adapter); - } - - if ((hw->media_type == e1000_media_type_internal_serdes) && - !(er32(TXCW) & E1000_TXCW_ANE)) - link = !hw->serdes_link_down; - else - link = er32(STATUS) & E1000_STATUS_LU; + link = e1000_has_link(adapter); + if ((netif_carrier_ok(netdev)) && link) + goto link_up; if (link) { if (!netif_carrier_ok(netdev)) { u32 ctrl; bool txb2b = true; + /* update snapshot of PHY registers on LSC */ e1000_get_speed_and_duplex(hw, &adapter->link_speed, &adapter->link_duplex); @@ -2589,7 +2330,7 @@ static void e1000_watchdog(unsigned long data) case SPEED_10: txb2b = false; netdev->tx_queue_len = 10; - adapter->tx_timeout_factor = 8; + adapter->tx_timeout_factor = 16; break; case SPEED_100: txb2b = false; @@ -2598,52 +2339,16 @@ static void e1000_watchdog(unsigned long data) break; } - if ((hw->mac_type == e1000_82571 || - hw->mac_type == e1000_82572) && - !txb2b) { - u32 tarc0; - tarc0 = er32(TARC0); - tarc0 &= ~(1 << 21); - ew32(TARC0, tarc0); - } - - /* disable TSO for pcie and 10/100 speeds, to avoid - * some hardware issues */ - if (!adapter->tso_force && - hw->bus_type == e1000_bus_type_pci_express){ - switch (adapter->link_speed) { - case SPEED_10: - case SPEED_100: - DPRINTK(PROBE,INFO, - "10/100 speed: disabling TSO\n"); - netdev->features &= ~NETIF_F_TSO; - netdev->features &= ~NETIF_F_TSO6; - break; - case SPEED_1000: - netdev->features |= NETIF_F_TSO; - netdev->features |= NETIF_F_TSO6; - break; - default: - /* oops */ - break; - } - } - - /* enable transmits in the hardware, need to do this - * after setting TARC0 */ + /* enable transmits in the hardware */ tctl = er32(TCTL); tctl |= E1000_TCTL_EN; ew32(TCTL, tctl); netif_carrier_on(netdev); - mod_timer(&adapter->phy_info_timer, round_jiffies(jiffies + 2 * HZ)); + if (!test_bit(__E1000_DOWN, &adapter->flags)) + mod_timer(&adapter->phy_info_timer, + round_jiffies(jiffies + 2 * HZ)); adapter->smartspeed = 0; - } else { - /* make sure the receive unit is started */ - if (hw->rx_needs_kicking) { - u32 rctl = er32(RCTL); - ew32(RCTL, rctl | E1000_RCTL_EN); - } } } else { if (netif_carrier_ok(netdev)) { @@ -2652,21 +2357,16 @@ static void e1000_watchdog(unsigned long data) printk(KERN_INFO "e1000: %s NIC Link is Down\n", netdev->name); netif_carrier_off(netdev); - mod_timer(&adapter->phy_info_timer, round_jiffies(jiffies + 2 * HZ)); - - /* 80003ES2LAN workaround-- - * For packet buffer work-around on link down event; - * disable receives in the ISR and - * reset device here in the watchdog - */ - if (hw->mac_type == e1000_80003es2lan) - /* reset device */ - schedule_work(&adapter->reset_task); + + if (!test_bit(__E1000_DOWN, &adapter->flags)) + mod_timer(&adapter->phy_info_timer, + round_jiffies(jiffies + 2 * HZ)); } e1000_smartspeed(adapter); } +link_up: e1000_update_stats(adapter); hw->tx_packet_delta = adapter->stats.tpt - adapter->tpt_old; @@ -2700,13 +2400,10 @@ static void e1000_watchdog(unsigned long data) /* Force detection of hung controller every watchdog period */ adapter->detect_tx_hung = true; - /* With 82571 controllers, LAA may be overwritten due to controller - * reset from the other port. Set the appropriate LAA in RAR[0] */ - if (hw->mac_type == e1000_82571 && hw->laa_is_present) - e1000_rar_set(hw, hw->mac_addr, 0); - /* Reset the timer */ - mod_timer(&adapter->watchdog_timer, round_jiffies(jiffies + 2 * HZ)); + if (!test_bit(__E1000_DOWN, &adapter->flags)) + mod_timer(&adapter->watchdog_timer, + round_jiffies(jiffies + 2 * HZ)); } enum latency_range { @@ -2718,6 +2415,11 @@ enum latency_range { /** * e1000_update_itr - update the dynamic ITR value based on statistics + * @adapter: pointer to adapter + * @itr_setting: current adapter->itr + * @packets: the number of packets during this measurement interval + * @bytes: the number of bytes during this measurement interval + * * Stores a new ITR value based on packets and byte * counts during the last interrupt. The advantage of per interrupt * computation is faster updates and more accurate ITR for the current @@ -2727,10 +2429,6 @@ enum latency_range { * while increasing bulk throughput. * this functionality is controlled by the InterruptThrottleRate module * parameter (see e1000_param.c) - * @adapter: pointer to adapter - * @itr_setting: current adapter->itr - * @packets: the number of packets during this measurement interval - * @bytes: the number of bytes during this measurement interval **/ static unsigned int e1000_update_itr(struct e1000_adapter *adapter, u16 itr_setting, int packets, int bytes) @@ -3035,8 +2733,9 @@ static int e1000_tx_map(struct e1000_adapter *adapter, size -= 4; buffer_info->length = size; - buffer_info->dma = skb_shinfo(skb)->dma_head + offset; + /* set time_stamp *before* dma to help avoid a possible race */ buffer_info->time_stamp = jiffies; + buffer_info->dma = skb_shinfo(skb)->dma_head + offset; buffer_info->next_to_watch = i; len -= size; @@ -3071,13 +2770,14 @@ static int e1000_tx_map(struct e1000_adapter *adapter, * Avoid terminating buffers within evenly-aligned * dwords. */ if (unlikely(adapter->pcix_82544 && - !((unsigned long)(frag->page+offset+size-1) & 4) && - size > 4)) + !((unsigned long)(page_to_phys(frag->page) + offset + + size - 1) & 4) && + size > 4)) size -= 4; buffer_info->length = size; - buffer_info->dma = map[f] + offset; buffer_info->time_stamp = jiffies; + buffer_info->dma = map[f] + offset; buffer_info->next_to_watch = i; len -= size; @@ -3186,41 +2886,6 @@ no_fifo_stall_required: return 0; } -#define MINIMUM_DHCP_PACKET_SIZE 282 -static int e1000_transfer_dhcp_info(struct e1000_adapter *adapter, - struct sk_buff *skb) -{ - struct e1000_hw *hw = &adapter->hw; - u16 length, offset; - if (vlan_tx_tag_present(skb)) { - if (!((vlan_tx_tag_get(skb) == hw->mng_cookie.vlan_id) && - ( hw->mng_cookie.status & - E1000_MNG_DHCP_COOKIE_STATUS_VLAN_SUPPORT)) ) - return 0; - } - if (skb->len > MINIMUM_DHCP_PACKET_SIZE) { - struct ethhdr *eth = (struct ethhdr *)skb->data; - if ((htons(ETH_P_IP) == eth->h_proto)) { - const struct iphdr *ip = - (struct iphdr *)((u8 *)skb->data+14); - if (IPPROTO_UDP == ip->protocol) { - struct udphdr *udp = - (struct udphdr *)((u8 *)ip + - (ip->ihl << 2)); - if (ntohs(udp->dest) == 67) { - offset = (u8 *)udp + 8 - skb->data; - length = skb->len - offset; - - return e1000_mng_write_dhcp_info(hw, - (u8 *)udp + 8, - length); - } - } - } - } - return 0; -} - static int __e1000_maybe_stop_tx(struct net_device *netdev, int size) { struct e1000_adapter *adapter = netdev_priv(netdev); @@ -3279,11 +2944,6 @@ static netdev_tx_t e1000_xmit_frame(struct sk_buff *skb, return NETDEV_TX_OK; } - /* 82571 and newer doesn't need the workaround that limited descriptor - * length to 4kB */ - if (hw->mac_type >= e1000_82571) - max_per_txd = 8192; - mss = skb_shinfo(skb)->gso_size; /* The controller does a simple calculation to * make sure there is enough room in the FIFO before @@ -3296,9 +2956,6 @@ static netdev_tx_t e1000_xmit_frame(struct sk_buff *skb, max_per_txd = min(mss << 2, max_per_txd); max_txd_pwr = fls(max_per_txd) - 1; - /* TSO Workaround for 82571/2/3 Controllers -- if skb->data - * points to just header, pull a few bytes of payload from - * frags into skb->data */ hdr_len = skb_transport_offset(skb) + tcp_hdrlen(skb); if (skb->data_len && hdr_len == len) { switch (hw->mac_type) { @@ -3313,10 +2970,6 @@ static netdev_tx_t e1000_xmit_frame(struct sk_buff *skb, if ((unsigned long)(skb_tail_pointer(skb) - 1) & 4) break; /* fall through */ - case e1000_82571: - case e1000_82572: - case e1000_82573: - case e1000_ich8lan: pull_size = min((unsigned int)4, skb->data_len); if (!__pskb_pull_tail(skb, pull_size)) { DPRINTK(DRV, ERR, @@ -3361,11 +3014,6 @@ static netdev_tx_t e1000_xmit_frame(struct sk_buff *skb, if (adapter->pcix_82544) count += nr_frags; - - if (hw->tx_pkt_filtering && - (hw->mac_type == e1000_82573)) - e1000_transfer_dhcp_info(adapter, skb); - /* need: count + 2 desc gap to keep tail from touching * head, otherwise try next time */ if (unlikely(e1000_maybe_stop_tx(netdev, tx_ring, count + 2))) @@ -3374,7 +3022,9 @@ static netdev_tx_t e1000_xmit_frame(struct sk_buff *skb, if (unlikely(hw->mac_type == e1000_82547)) { if (unlikely(e1000_82547_fifo_workaround(adapter, skb))) { netif_stop_queue(netdev); - mod_timer(&adapter->tx_fifo_stall_timer, jiffies + 1); + if (!test_bit(__E1000_DOWN, &adapter->flags)) + mod_timer(&adapter->tx_fifo_stall_timer, + jiffies + 1); return NETDEV_TX_BUSY; } } @@ -3393,14 +3043,12 @@ static netdev_tx_t e1000_xmit_frame(struct sk_buff *skb, } if (likely(tso)) { - tx_ring->last_tx_tso = 1; + if (likely(hw->mac_type != e1000_82544)) + tx_ring->last_tx_tso = 1; tx_flags |= E1000_TX_FLAGS_TSO; } else if (likely(e1000_tx_csum(adapter, tx_ring, skb))) tx_flags |= E1000_TX_FLAGS_CSUM; - /* Old method was to assume IPv4 packet by default if TSO was enabled. - * 82571 hardware supports TSO capabilities for IPv6 as well... - * no longer assume, we must. */ if (likely(skb->protocol == htons(ETH_P_IP))) tx_flags |= E1000_TX_FLAGS_IPV4; @@ -3472,7 +3120,6 @@ static int e1000_change_mtu(struct net_device *netdev, int new_mtu) struct e1000_adapter *adapter = netdev_priv(netdev); struct e1000_hw *hw = &adapter->hw; int max_frame = new_mtu + ENET_HEADER_SIZE + ETHERNET_FCS_SIZE; - u16 eeprom_data = 0; if ((max_frame < MINIMUM_ETHERNET_FRAME_SIZE) || (max_frame > MAX_JUMBO_FRAME_SIZE)) { @@ -3483,44 +3130,23 @@ static int e1000_change_mtu(struct net_device *netdev, int new_mtu) /* Adapter-specific max frame size limits. */ switch (hw->mac_type) { case e1000_undefined ... e1000_82542_rev2_1: - case e1000_ich8lan: if (max_frame > (ETH_FRAME_LEN + ETH_FCS_LEN)) { DPRINTK(PROBE, ERR, "Jumbo Frames not supported.\n"); return -EINVAL; } break; - case e1000_82573: - /* Jumbo Frames not supported if: - * - this is not an 82573L device - * - ASPM is enabled in any way (0x1A bits 3:2) */ - e1000_read_eeprom(hw, EEPROM_INIT_3GIO_3, 1, - &eeprom_data); - if ((hw->device_id != E1000_DEV_ID_82573L) || - (eeprom_data & EEPROM_WORD1A_ASPM_MASK)) { - if (max_frame > (ETH_FRAME_LEN + ETH_FCS_LEN)) { - DPRINTK(PROBE, ERR, - "Jumbo Frames not supported.\n"); - return -EINVAL; - } - break; - } - /* ERT will be enabled later to enable wire speed receives */ - - /* fall through to get support */ - case e1000_82571: - case e1000_82572: - case e1000_80003es2lan: -#define MAX_STD_JUMBO_FRAME_SIZE 9234 - if (max_frame > MAX_STD_JUMBO_FRAME_SIZE) { - DPRINTK(PROBE, ERR, "MTU > 9216 not supported.\n"); - return -EINVAL; - } - break; default: /* Capable of supporting up to MAX_JUMBO_FRAME_SIZE limit. */ break; } + while (test_and_set_bit(__E1000_RESETTING, &adapter->flags)) + msleep(1); + /* e1000_down has a dependency on max_frame_size */ + hw->max_frame_size = max_frame; + if (netif_running(netdev)) + e1000_down(adapter); + /* NOTE: netdev_alloc_skb reserves 16 bytes, and typically NET_IP_ALIGN * means we reserve 2 more, this pushes us to allocate from the next * larger slab size. @@ -3549,11 +3175,16 @@ static int e1000_change_mtu(struct net_device *netdev, int new_mtu) (max_frame == MAXIMUM_ETHERNET_VLAN_SIZE))) adapter->rx_buffer_len = MAXIMUM_ETHERNET_VLAN_SIZE; + printk(KERN_INFO "e1000: %s changing MTU from %d to %d\n", + netdev->name, netdev->mtu, new_mtu); netdev->mtu = new_mtu; - hw->max_frame_size = max_frame; if (netif_running(netdev)) - e1000_reinit_locked(adapter); + e1000_up(adapter); + else + e1000_reset(adapter); + + clear_bit(__E1000_RESETTING, &adapter->flags); return 0; } @@ -3596,14 +3227,12 @@ void e1000_update_stats(struct e1000_adapter *adapter) adapter->stats.mprc += er32(MPRC); adapter->stats.roc += er32(ROC); - if (hw->mac_type != e1000_ich8lan) { - adapter->stats.prc64 += er32(PRC64); - adapter->stats.prc127 += er32(PRC127); - adapter->stats.prc255 += er32(PRC255); - adapter->stats.prc511 += er32(PRC511); - adapter->stats.prc1023 += er32(PRC1023); - adapter->stats.prc1522 += er32(PRC1522); - } + adapter->stats.prc64 += er32(PRC64); + adapter->stats.prc127 += er32(PRC127); + adapter->stats.prc255 += er32(PRC255); + adapter->stats.prc511 += er32(PRC511); + adapter->stats.prc1023 += er32(PRC1023); + adapter->stats.prc1522 += er32(PRC1522); adapter->stats.symerrs += er32(SYMERRS); adapter->stats.mpc += er32(MPC); @@ -3632,14 +3261,12 @@ void e1000_update_stats(struct e1000_adapter *adapter) adapter->stats.toth += er32(TOTH); adapter->stats.tpr += er32(TPR); - if (hw->mac_type != e1000_ich8lan) { - adapter->stats.ptc64 += er32(PTC64); - adapter->stats.ptc127 += er32(PTC127); - adapter->stats.ptc255 += er32(PTC255); - adapter->stats.ptc511 += er32(PTC511); - adapter->stats.ptc1023 += er32(PTC1023); - adapter->stats.ptc1522 += er32(PTC1522); - } + adapter->stats.ptc64 += er32(PTC64); + adapter->stats.ptc127 += er32(PTC127); + adapter->stats.ptc255 += er32(PTC255); + adapter->stats.ptc511 += er32(PTC511); + adapter->stats.ptc1023 += er32(PTC1023); + adapter->stats.ptc1522 += er32(PTC1522); adapter->stats.mptc += er32(MPTC); adapter->stats.bptc += er32(BPTC); @@ -3659,20 +3286,6 @@ void e1000_update_stats(struct e1000_adapter *adapter) adapter->stats.tsctc += er32(TSCTC); adapter->stats.tsctfc += er32(TSCTFC); } - if (hw->mac_type > e1000_82547_rev_2) { - adapter->stats.iac += er32(IAC); - adapter->stats.icrxoc += er32(ICRXOC); - - if (hw->mac_type != e1000_ich8lan) { - adapter->stats.icrxptc += er32(ICRXPTC); - adapter->stats.icrxatc += er32(ICRXATC); - adapter->stats.ictxptc += er32(ICTXPTC); - adapter->stats.ictxatc += er32(ICTXATC); - adapter->stats.ictxqec += er32(ICTXQEC); - adapter->stats.ictxqmtc += er32(ICTXQMTC); - adapter->stats.icrxdmtc += er32(ICRXDMTC); - } - } /* Fill out the OS statistics structure */ adapter->net_stats.multicast = adapter->stats.mprc; @@ -3731,49 +3344,6 @@ void e1000_update_stats(struct e1000_adapter *adapter) } /** - * e1000_intr_msi - Interrupt Handler - * @irq: interrupt number - * @data: pointer to a network interface device structure - **/ - -static irqreturn_t e1000_intr_msi(int irq, void *data) -{ - struct net_device *netdev = data; - struct e1000_adapter *adapter = netdev_priv(netdev); - struct e1000_hw *hw = &adapter->hw; - u32 icr = er32(ICR); - - /* in NAPI mode read ICR disables interrupts using IAM */ - - if (icr & (E1000_ICR_RXSEQ | E1000_ICR_LSC)) { - hw->get_link_status = 1; - /* 80003ES2LAN workaround-- For packet buffer work-around on - * link down event; disable receives here in the ISR and reset - * adapter in watchdog */ - if (netif_carrier_ok(netdev) && - (hw->mac_type == e1000_80003es2lan)) { - /* disable receives */ - u32 rctl = er32(RCTL); - ew32(RCTL, rctl & ~E1000_RCTL_EN); - } - /* guard against interrupt when we're going down */ - if (!test_bit(__E1000_DOWN, &adapter->flags)) - mod_timer(&adapter->watchdog_timer, jiffies + 1); - } - - if (likely(napi_schedule_prep(&adapter->napi))) { - adapter->total_tx_bytes = 0; - adapter->total_tx_packets = 0; - adapter->total_rx_bytes = 0; - adapter->total_rx_packets = 0; - __napi_schedule(&adapter->napi); - } else - e1000_irq_enable(adapter); - - return IRQ_HANDLED; -} - -/** * e1000_intr - Interrupt Handler * @irq: interrupt number * @data: pointer to a network interface device structure @@ -3784,43 +3354,22 @@ static irqreturn_t e1000_intr(int irq, void *data) struct net_device *netdev = data; struct e1000_adapter *adapter = netdev_priv(netdev); struct e1000_hw *hw = &adapter->hw; - u32 rctl, icr = er32(ICR); + u32 icr = er32(ICR); if (unlikely((!icr) || test_bit(__E1000_DOWN, &adapter->flags))) return IRQ_NONE; /* Not our interrupt */ - /* IMS will not auto-mask if INT_ASSERTED is not set, and if it is - * not set, then the adapter didn't send an interrupt */ - if (unlikely(hw->mac_type >= e1000_82571 && - !(icr & E1000_ICR_INT_ASSERTED))) - return IRQ_NONE; - - /* Interrupt Auto-Mask...upon reading ICR, interrupts are masked. No - * need for the IMC write */ - if (unlikely(icr & (E1000_ICR_RXSEQ | E1000_ICR_LSC))) { hw->get_link_status = 1; - /* 80003ES2LAN workaround-- - * For packet buffer work-around on link down event; - * disable receives here in the ISR and - * reset adapter in watchdog - */ - if (netif_carrier_ok(netdev) && - (hw->mac_type == e1000_80003es2lan)) { - /* disable receives */ - rctl = er32(RCTL); - ew32(RCTL, rctl & ~E1000_RCTL_EN); - } /* guard against interrupt when we're going down */ if (!test_bit(__E1000_DOWN, &adapter->flags)) mod_timer(&adapter->watchdog_timer, jiffies + 1); } - if (unlikely(hw->mac_type < e1000_82571)) { - /* disable interrupts, without the synchronize_irq bit */ - ew32(IMC, ~0); - E1000_WRITE_FLUSH(); - } + /* disable interrupts, without the synchronize_irq bit */ + ew32(IMC, ~0); + E1000_WRITE_FLUSH(); + if (likely(napi_schedule_prep(&adapter->napi))) { adapter->total_tx_bytes = 0; adapter->total_tx_packets = 0; @@ -3844,17 +3393,13 @@ static irqreturn_t e1000_intr(int irq, void *data) static int e1000_clean(struct napi_struct *napi, int budget) { struct e1000_adapter *adapter = container_of(napi, struct e1000_adapter, napi); - struct net_device *poll_dev = adapter->netdev; - int tx_cleaned = 0, work_done = 0; - - adapter = netdev_priv(poll_dev); + int tx_clean_complete = 0, work_done = 0; - tx_cleaned = e1000_clean_tx_irq(adapter, &adapter->tx_ring[0]); + tx_clean_complete = e1000_clean_tx_irq(adapter, &adapter->tx_ring[0]); - adapter->clean_rx(adapter, &adapter->rx_ring[0], - &work_done, budget); + adapter->clean_rx(adapter, &adapter->rx_ring[0], &work_done, budget); - if (!tx_cleaned) + if (!tx_clean_complete) work_done = budget; /* If budget not fully consumed, exit the polling mode */ @@ -3925,7 +3470,9 @@ static bool e1000_clean_tx_irq(struct e1000_adapter *adapter, * sees the new next_to_clean. */ smp_mb(); - if (netif_queue_stopped(netdev)) { + + if (netif_queue_stopped(netdev) && + !(test_bit(__E1000_DOWN, &adapter->flags))) { netif_wake_queue(netdev); ++adapter->restart_queue; } @@ -3935,8 +3482,8 @@ static bool e1000_clean_tx_irq(struct e1000_adapter *adapter, /* Detect a transmit hang in hardware, this serializes the * check with the clearing of time_stamp and movement of i */ adapter->detect_tx_hung = false; - if (tx_ring->buffer_info[i].time_stamp && - time_after(jiffies, tx_ring->buffer_info[i].time_stamp + + if (tx_ring->buffer_info[eop].time_stamp && + time_after(jiffies, tx_ring->buffer_info[eop].time_stamp + (adapter->tx_timeout_factor * HZ)) && !(er32(STATUS) & E1000_STATUS_TXOFF)) { @@ -3958,7 +3505,7 @@ static bool e1000_clean_tx_irq(struct e1000_adapter *adapter, readl(hw->hw_addr + tx_ring->tdt), tx_ring->next_to_use, tx_ring->next_to_clean, - tx_ring->buffer_info[i].time_stamp, + tx_ring->buffer_info[eop].time_stamp, eop, jiffies, eop_desc->upper.fields.status); @@ -3999,25 +3546,13 @@ static void e1000_rx_checksum(struct e1000_adapter *adapter, u32 status_err, return; } /* TCP/UDP Checksum has not been calculated */ - if (hw->mac_type <= e1000_82547_rev_2) { - if (!(status & E1000_RXD_STAT_TCPCS)) - return; - } else { - if (!(status & (E1000_RXD_STAT_TCPCS | E1000_RXD_STAT_UDPCS))) - return; - } + if (!(status & E1000_RXD_STAT_TCPCS)) + return; + /* It must be a TCP or UDP packet with a valid checksum */ if (likely(status & E1000_RXD_STAT_TCPCS)) { /* TCP checksum is good */ skb->ip_summed = CHECKSUM_UNNECESSARY; - } else if (hw->mac_type > e1000_82547_rev_2) { - /* IP fragment with UDP payload */ - /* Hardware complements the payload checksum, so we undo it - * and then put the value in host order for further stack use. - */ - __sum16 sum = (__force __sum16)htons(csum); - skb->csum = csum_unfold(~sum); - skb->ip_summed = CHECKSUM_COMPLETE; } adapter->hw_csum_good++; } @@ -4814,20 +4349,6 @@ void e1000_pcix_set_mmrbc(struct e1000_hw *hw, int mmrbc) pcix_set_mmrbc(adapter->pdev, mmrbc); } -s32 e1000_read_pcie_cap_reg(struct e1000_hw *hw, u32 reg, u16 *value) -{ - struct e1000_adapter *adapter = hw->back; - u16 cap_offset; - - cap_offset = pci_find_capability(adapter->pdev, PCI_CAP_ID_EXP); - if (!cap_offset) - return -E1000_ERR_CONFIG; - - pci_read_config_word(adapter->pdev, cap_offset + reg, value); - - return E1000_SUCCESS; -} - void e1000_io_write(struct e1000_hw *hw, unsigned long port, u32 value) { outl(value, port); @@ -4850,33 +4371,27 @@ static void e1000_vlan_rx_register(struct net_device *netdev, ctrl |= E1000_CTRL_VME; ew32(CTRL, ctrl); - if (adapter->hw.mac_type != e1000_ich8lan) { - /* enable VLAN receive filtering */ - rctl = er32(RCTL); - rctl &= ~E1000_RCTL_CFIEN; - if (!(netdev->flags & IFF_PROMISC)) - rctl |= E1000_RCTL_VFE; - ew32(RCTL, rctl); - e1000_update_mng_vlan(adapter); - } + /* enable VLAN receive filtering */ + rctl = er32(RCTL); + rctl &= ~E1000_RCTL_CFIEN; + if (!(netdev->flags & IFF_PROMISC)) + rctl |= E1000_RCTL_VFE; + ew32(RCTL, rctl); + e1000_update_mng_vlan(adapter); } else { /* disable VLAN tag insert/strip */ ctrl = er32(CTRL); ctrl &= ~E1000_CTRL_VME; ew32(CTRL, ctrl); - if (adapter->hw.mac_type != e1000_ich8lan) { - /* disable VLAN receive filtering */ - rctl = er32(RCTL); - rctl &= ~E1000_RCTL_VFE; - ew32(RCTL, rctl); + /* disable VLAN receive filtering */ + rctl = er32(RCTL); + rctl &= ~E1000_RCTL_VFE; + ew32(RCTL, rctl); - if (adapter->mng_vlan_id != - (u16)E1000_MNG_VLAN_NONE) { - e1000_vlan_rx_kill_vid(netdev, - adapter->mng_vlan_id); - adapter->mng_vlan_id = E1000_MNG_VLAN_NONE; - } + if (adapter->mng_vlan_id != (u16)E1000_MNG_VLAN_NONE) { + e1000_vlan_rx_kill_vid(netdev, adapter->mng_vlan_id); + adapter->mng_vlan_id = E1000_MNG_VLAN_NONE; } } @@ -4913,14 +4428,6 @@ static void e1000_vlan_rx_kill_vid(struct net_device *netdev, u16 vid) if (!test_bit(__E1000_DOWN, &adapter->flags)) e1000_irq_enable(adapter); - if ((hw->mng_cookie.status & - E1000_MNG_DHCP_COOKIE_STATUS_VLAN_SUPPORT) && - (vid == adapter->mng_vlan_id)) { - /* release control to f/w */ - e1000_release_hw_control(adapter); - return; - } - /* remove VID from filter table */ index = (vid >> 5) & 0x7F; vfta = E1000_READ_REG_ARRAY(hw, VFTA, index); @@ -5031,16 +4538,13 @@ static int __e1000_shutdown(struct pci_dev *pdev, bool *enable_wake) } if (hw->media_type == e1000_media_type_fiber || - hw->media_type == e1000_media_type_internal_serdes) { + hw->media_type == e1000_media_type_internal_serdes) { /* keep the laser running in D3 */ ctrl_ext = er32(CTRL_EXT); ctrl_ext |= E1000_CTRL_EXT_SDP7_DATA; ew32(CTRL_EXT, ctrl_ext); } - /* Allow time for pending master requests to run */ - e1000_disable_pciex_master(hw); - ew32(WUC, E1000_WUC_PME_EN); ew32(WUFC, wufc); } else { @@ -5056,16 +4560,9 @@ static int __e1000_shutdown(struct pci_dev *pdev, bool *enable_wake) if (adapter->en_mng_pt) *enable_wake = true; - if (hw->phy_type == e1000_phy_igp_3) - e1000_phy_powerdown_workaround(hw); - if (netif_running(netdev)) e1000_free_irq(adapter); - /* Release control of h/w to f/w. If f/w is AMT enabled, this - * would have already happened in close and is redundant. */ - e1000_release_hw_control(adapter); - pci_disable_device(pdev); return 0; @@ -5131,14 +4628,6 @@ static int e1000_resume(struct pci_dev *pdev) netif_device_attach(netdev); - /* If the controller is 82573 and f/w is AMT, do not set - * DRV_LOAD until the interface is up. For all other cases, - * let the f/w know that the h/w is now under the control - * of the driver. */ - if (hw->mac_type != e1000_82573 || - !e1000_check_mng_mode(hw)) - e1000_get_hw_control(adapter); - return 0; } #endif @@ -5174,7 +4663,7 @@ static void e1000_netpoll(struct net_device *netdev) /** * e1000_io_error_detected - called when PCI error is detected * @pdev: Pointer to PCI device - * @state: The current pci conneection state + * @state: The current pci connection state * * This function is called after a PCI bus error affecting * this device has been detected. @@ -5243,7 +4732,6 @@ static void e1000_io_resume(struct pci_dev *pdev) { struct net_device *netdev = pci_get_drvdata(pdev); struct e1000_adapter *adapter = netdev_priv(netdev); - struct e1000_hw *hw = &adapter->hw; e1000_init_manageability(adapter); @@ -5255,15 +4743,6 @@ static void e1000_io_resume(struct pci_dev *pdev) } netif_device_attach(netdev); - - /* If the controller is 82573 and f/w is AMT, do not set - * DRV_LOAD until the interface is up. For all other cases, - * let the f/w know that the h/w is now under the control - * of the driver. */ - if (hw->mac_type != e1000_82573 || - !e1000_check_mng_mode(hw)) - e1000_get_hw_control(adapter); - } /* e1000_main.c */ diff --git a/drivers/net/e1000/e1000_param.c b/drivers/net/e1000/e1000_param.c index 213437d13154..38d2741ccae9 100644 --- a/drivers/net/e1000/e1000_param.c +++ b/drivers/net/e1000/e1000_param.c @@ -518,22 +518,6 @@ void __devinit e1000_check_options(struct e1000_adapter *adapter) adapter->smart_power_down = opt.def; } } - { /* Kumeran Lock Loss Workaround */ - opt = (struct e1000_option) { - .type = enable_option, - .name = "Kumeran Lock Loss Workaround", - .err = "defaulting to Enabled", - .def = OPTION_ENABLED - }; - - if (num_KumeranLockLoss > bd) { - unsigned int kmrn_lock_loss = KumeranLockLoss[bd]; - e1000_validate_option(&kmrn_lock_loss, &opt, adapter); - adapter->hw.kmrn_lock_loss_workaround_disabled = !kmrn_lock_loss; - } else { - adapter->hw.kmrn_lock_loss_workaround_disabled = !opt.def; - } - } switch (adapter->hw.media_type) { case e1000_media_type_fiber: @@ -626,12 +610,6 @@ static void __devinit e1000_check_copper_options(struct e1000_adapter *adapter) .p = dplx_list }} }; - if (e1000_check_phy_reset_block(&adapter->hw)) { - DPRINTK(PROBE, INFO, - "Link active due to SoL/IDER Session. " - "Speed/Duplex/AutoNeg parameter ignored.\n"); - return; - } if (num_Duplex > bd) { dplx = Duplex[bd]; e1000_validate_option(&dplx, &opt, adapter); diff --git a/drivers/net/e1000e/82571.c b/drivers/net/e1000e/82571.c index b53b40ba88a8..d1e0563a67df 100644 --- a/drivers/net/e1000e/82571.c +++ b/drivers/net/e1000e/82571.c @@ -1803,7 +1803,7 @@ struct e1000_info e1000_82574_info = { | FLAG_HAS_AMT | FLAG_HAS_CTRLEXT_ON_LOAD, .pba = 20, - .max_hw_frame_size = ETH_FRAME_LEN + ETH_FCS_LEN, + .max_hw_frame_size = DEFAULT_JUMBO, .get_variants = e1000_get_variants_82571, .mac_ops = &e82571_mac_ops, .phy_ops = &e82_phy_ops_bm, @@ -1820,7 +1820,7 @@ struct e1000_info e1000_82583_info = { | FLAG_HAS_AMT | FLAG_HAS_CTRLEXT_ON_LOAD, .pba = 20, - .max_hw_frame_size = DEFAULT_JUMBO, + .max_hw_frame_size = ETH_FRAME_LEN + ETH_FCS_LEN, .get_variants = e1000_get_variants_82571, .mac_ops = &e82571_mac_ops, .phy_ops = &e82_phy_ops_bm, diff --git a/drivers/net/e1000e/defines.h b/drivers/net/e1000e/defines.h index c0f185beb8bc..1190167a8b3d 100644 --- a/drivers/net/e1000e/defines.h +++ b/drivers/net/e1000e/defines.h @@ -76,6 +76,7 @@ /* Extended Device Control */ #define E1000_CTRL_EXT_SDP7_DATA 0x00000080 /* Value of SW Definable Pin 7 */ #define E1000_CTRL_EXT_EE_RST 0x00002000 /* Reinitialize from EEPROM */ +#define E1000_CTRL_EXT_SPD_BYPS 0x00008000 /* Speed Select Bypass */ #define E1000_CTRL_EXT_RO_DIS 0x00020000 /* Relaxed Ordering disable */ #define E1000_CTRL_EXT_DMA_DYN_CLK_EN 0x00080000 /* DMA Dynamic Clock Gating */ #define E1000_CTRL_EXT_LINK_MODE_MASK 0x00C00000 @@ -347,6 +348,7 @@ /* Extended Configuration Control and Size */ #define E1000_EXTCNF_CTRL_MDIO_SW_OWNERSHIP 0x00000020 #define E1000_EXTCNF_CTRL_LCD_WRITE_ENABLE 0x00000001 +#define E1000_EXTCNF_CTRL_OEM_WRITE_ENABLE 0x00000008 #define E1000_EXTCNF_CTRL_SWFLAG 0x00000020 #define E1000_EXTCNF_SIZE_EXT_PCIE_LENGTH_MASK 0x00FF0000 #define E1000_EXTCNF_SIZE_EXT_PCIE_LENGTH_SHIFT 16 diff --git a/drivers/net/e1000e/e1000.h b/drivers/net/e1000e/e1000.h index 981936c1fb46..3e187b0e4203 100644 --- a/drivers/net/e1000e/e1000.h +++ b/drivers/net/e1000e/e1000.h @@ -141,6 +141,22 @@ struct e1000_info; #define HV_TNCRS_UPPER PHY_REG(778, 29) /* Transmit with no CRS */ #define HV_TNCRS_LOWER PHY_REG(778, 30) +#define E1000_FCRTV_PCH 0x05F40 /* PCH Flow Control Refresh Timer Value */ + +/* BM PHY Copper Specific Status */ +#define BM_CS_STATUS 17 +#define BM_CS_STATUS_LINK_UP 0x0400 +#define BM_CS_STATUS_RESOLVED 0x0800 +#define BM_CS_STATUS_SPEED_MASK 0xC000 +#define BM_CS_STATUS_SPEED_1000 0x8000 + +/* 82577 Mobile Phy Status Register */ +#define HV_M_STATUS 26 +#define HV_M_STATUS_AUTONEG_COMPLETE 0x1000 +#define HV_M_STATUS_SPEED_MASK 0x0300 +#define HV_M_STATUS_SPEED_1000 0x0200 +#define HV_M_STATUS_LINK_UP 0x0040 + enum e1000_boards { board_82571, board_82572, @@ -519,9 +535,13 @@ extern s32 e1000e_phy_force_speed_duplex_igp(struct e1000_hw *hw); extern s32 e1000e_get_cable_length_igp_2(struct e1000_hw *hw); extern s32 e1000e_get_phy_info_igp(struct e1000_hw *hw); extern s32 e1000e_read_phy_reg_igp(struct e1000_hw *hw, u32 offset, u16 *data); +extern s32 e1000e_read_phy_reg_igp_locked(struct e1000_hw *hw, u32 offset, + u16 *data); extern s32 e1000e_phy_hw_reset_generic(struct e1000_hw *hw); extern s32 e1000e_set_d3_lplu_state(struct e1000_hw *hw, bool active); extern s32 e1000e_write_phy_reg_igp(struct e1000_hw *hw, u32 offset, u16 data); +extern s32 e1000e_write_phy_reg_igp_locked(struct e1000_hw *hw, u32 offset, + u16 data); extern s32 e1000e_phy_sw_reset(struct e1000_hw *hw); extern s32 e1000e_phy_force_speed_duplex_m88(struct e1000_hw *hw); extern s32 e1000e_get_cfg_done(struct e1000_hw *hw); @@ -538,7 +558,11 @@ extern s32 e1000e_read_phy_reg_bm2(struct e1000_hw *hw, u32 offset, u16 *data); extern s32 e1000e_write_phy_reg_bm2(struct e1000_hw *hw, u32 offset, u16 data); extern void e1000e_phy_force_speed_duplex_setup(struct e1000_hw *hw, u16 *phy_ctrl); extern s32 e1000e_write_kmrn_reg(struct e1000_hw *hw, u32 offset, u16 data); +extern s32 e1000e_write_kmrn_reg_locked(struct e1000_hw *hw, u32 offset, + u16 data); extern s32 e1000e_read_kmrn_reg(struct e1000_hw *hw, u32 offset, u16 *data); +extern s32 e1000e_read_kmrn_reg_locked(struct e1000_hw *hw, u32 offset, + u16 *data); extern s32 e1000e_phy_has_link_generic(struct e1000_hw *hw, u32 iterations, u32 usec_interval, bool *success); extern s32 e1000e_phy_reset_dsp(struct e1000_hw *hw); @@ -546,7 +570,11 @@ extern s32 e1000e_read_phy_reg_mdic(struct e1000_hw *hw, u32 offset, u16 *data); extern s32 e1000e_write_phy_reg_mdic(struct e1000_hw *hw, u32 offset, u16 data); extern s32 e1000e_check_downshift(struct e1000_hw *hw); extern s32 e1000_read_phy_reg_hv(struct e1000_hw *hw, u32 offset, u16 *data); +extern s32 e1000_read_phy_reg_hv_locked(struct e1000_hw *hw, u32 offset, + u16 *data); extern s32 e1000_write_phy_reg_hv(struct e1000_hw *hw, u32 offset, u16 data); +extern s32 e1000_write_phy_reg_hv_locked(struct e1000_hw *hw, u32 offset, + u16 data); extern s32 e1000_set_mdio_slow_mode_hv(struct e1000_hw *hw, bool slow); extern s32 e1000_link_stall_workaround_hv(struct e1000_hw *hw); extern s32 e1000_copper_link_setup_82577(struct e1000_hw *hw); diff --git a/drivers/net/e1000e/ethtool.c b/drivers/net/e1000e/ethtool.c index 1bf4d2a5d34f..e82638ecae88 100644 --- a/drivers/net/e1000e/ethtool.c +++ b/drivers/net/e1000e/ethtool.c @@ -327,10 +327,18 @@ static int e1000_set_pauseparam(struct net_device *netdev, hw->fc.current_mode = hw->fc.requested_mode; - retval = ((hw->phy.media_type == e1000_media_type_fiber) ? - hw->mac.ops.setup_link(hw) : e1000e_force_mac_fc(hw)); + if (hw->phy.media_type == e1000_media_type_fiber) { + retval = hw->mac.ops.setup_link(hw); + /* implicit goto out */ + } else { + retval = e1000e_force_mac_fc(hw); + if (retval) + goto out; + e1000e_set_fc_watermarks(hw); + } } +out: clear_bit(__E1000_RESETTING, &adapter->state); return retval; } diff --git a/drivers/net/e1000e/hw.h b/drivers/net/e1000e/hw.h index fd44d9f90769..aaea41ef794d 100644 --- a/drivers/net/e1000e/hw.h +++ b/drivers/net/e1000e/hw.h @@ -764,11 +764,13 @@ struct e1000_phy_operations { s32 (*get_cable_length)(struct e1000_hw *); s32 (*get_phy_info)(struct e1000_hw *); s32 (*read_phy_reg)(struct e1000_hw *, u32, u16 *); + s32 (*read_phy_reg_locked)(struct e1000_hw *, u32, u16 *); void (*release_phy)(struct e1000_hw *); s32 (*reset_phy)(struct e1000_hw *); s32 (*set_d0_lplu_state)(struct e1000_hw *, bool); s32 (*set_d3_lplu_state)(struct e1000_hw *, bool); s32 (*write_phy_reg)(struct e1000_hw *, u32, u16); + s32 (*write_phy_reg_locked)(struct e1000_hw *, u32, u16); s32 (*cfg_on_link_up)(struct e1000_hw *); }; @@ -901,6 +903,7 @@ struct e1000_shadow_ram { struct e1000_dev_spec_ich8lan { bool kmrn_lock_loss_workaround_enabled; struct e1000_shadow_ram shadow_ram[E1000_ICH8_SHADOW_RAM_WORDS]; + bool nvm_k1_enabled; }; struct e1000_hw { diff --git a/drivers/net/e1000e/ich8lan.c b/drivers/net/e1000e/ich8lan.c index 99df2abf82a9..eff3f4783655 100644 --- a/drivers/net/e1000e/ich8lan.c +++ b/drivers/net/e1000e/ich8lan.c @@ -122,6 +122,27 @@ #define HV_LED_CONFIG PHY_REG(768, 30) /* LED Configuration */ +#define SW_FLAG_TIMEOUT 1000 /* SW Semaphore flag timeout in milliseconds */ + +/* SMBus Address Phy Register */ +#define HV_SMB_ADDR PHY_REG(768, 26) +#define HV_SMB_ADDR_PEC_EN 0x0200 +#define HV_SMB_ADDR_VALID 0x0080 + +/* Strapping Option Register - RO */ +#define E1000_STRAP 0x0000C +#define E1000_STRAP_SMBUS_ADDRESS_MASK 0x00FE0000 +#define E1000_STRAP_SMBUS_ADDRESS_SHIFT 17 + +/* OEM Bits Phy Register */ +#define HV_OEM_BITS PHY_REG(768, 25) +#define HV_OEM_BITS_LPLU 0x0004 /* Low Power Link Up */ +#define HV_OEM_BITS_GBE_DIS 0x0040 /* Gigabit Disable */ +#define HV_OEM_BITS_RESTART_AN 0x0400 /* Restart Auto-negotiation */ + +#define E1000_NVM_K1_CONFIG 0x1B /* NVM K1 Config Word */ +#define E1000_NVM_K1_ENABLE 0x1 /* NVM Enable K1 bit */ + /* ICH GbE Flash Hardware Sequencing Flash Status Register bit breakdown */ /* Offset 04h HSFSTS */ union ich8_hws_flash_status { @@ -200,6 +221,10 @@ static s32 e1000_setup_led_pchlan(struct e1000_hw *hw); static s32 e1000_cleanup_led_pchlan(struct e1000_hw *hw); static s32 e1000_led_on_pchlan(struct e1000_hw *hw); static s32 e1000_led_off_pchlan(struct e1000_hw *hw); +static s32 e1000_set_lplu_state_pchlan(struct e1000_hw *hw, bool active); +static void e1000_lan_init_done_ich8lan(struct e1000_hw *hw); +static s32 e1000_k1_gig_workaround_hv(struct e1000_hw *hw, bool link); +static s32 e1000_configure_k1_ich8lan(struct e1000_hw *hw, bool k1_enable); static inline u16 __er16flash(struct e1000_hw *hw, unsigned long reg) { @@ -242,7 +267,11 @@ static s32 e1000_init_phy_params_pchlan(struct e1000_hw *hw) phy->ops.check_polarity = e1000_check_polarity_ife_ich8lan; phy->ops.read_phy_reg = e1000_read_phy_reg_hv; + phy->ops.read_phy_reg_locked = e1000_read_phy_reg_hv_locked; + phy->ops.set_d0_lplu_state = e1000_set_lplu_state_pchlan; + phy->ops.set_d3_lplu_state = e1000_set_lplu_state_pchlan; phy->ops.write_phy_reg = e1000_write_phy_reg_hv; + phy->ops.write_phy_reg_locked = e1000_write_phy_reg_hv_locked; phy->autoneg_mask = AUTONEG_ADVERTISE_SPEED_DEFAULT; phy->id = e1000_phy_unknown; @@ -303,6 +332,8 @@ static s32 e1000_init_phy_params_ich8lan(struct e1000_hw *hw) case IGP03E1000_E_PHY_ID: phy->type = e1000_phy_igp_3; phy->autoneg_mask = AUTONEG_ADVERTISE_SPEED_DEFAULT; + phy->ops.read_phy_reg_locked = e1000e_read_phy_reg_igp_locked; + phy->ops.write_phy_reg_locked = e1000e_write_phy_reg_igp_locked; break; case IFE_E_PHY_ID: case IFE_PLUS_E_PHY_ID: @@ -469,14 +500,6 @@ static s32 e1000_check_for_copper_link_ich8lan(struct e1000_hw *hw) goto out; } - if (hw->mac.type == e1000_pchlan) { - ret_val = e1000e_write_kmrn_reg(hw, - E1000_KMRNCTRLSTA_K1_CONFIG, - E1000_KMRNCTRLSTA_K1_ENABLE); - if (ret_val) - goto out; - } - /* * First we want to see if the MII Status Register reports * link. If so, then we want to get the current speed/duplex @@ -486,6 +509,12 @@ static s32 e1000_check_for_copper_link_ich8lan(struct e1000_hw *hw) if (ret_val) goto out; + if (hw->mac.type == e1000_pchlan) { + ret_val = e1000_k1_gig_workaround_hv(hw, link); + if (ret_val) + goto out; + } + if (!link) goto out; /* No link detected */ @@ -568,12 +597,39 @@ static s32 e1000_get_variants_ich8lan(struct e1000_adapter *adapter) static DEFINE_MUTEX(nvm_mutex); /** + * e1000_acquire_nvm_ich8lan - Acquire NVM mutex + * @hw: pointer to the HW structure + * + * Acquires the mutex for performing NVM operations. + **/ +static s32 e1000_acquire_nvm_ich8lan(struct e1000_hw *hw) +{ + mutex_lock(&nvm_mutex); + + return 0; +} + +/** + * e1000_release_nvm_ich8lan - Release NVM mutex + * @hw: pointer to the HW structure + * + * Releases the mutex used while performing NVM operations. + **/ +static void e1000_release_nvm_ich8lan(struct e1000_hw *hw) +{ + mutex_unlock(&nvm_mutex); + + return; +} + +static DEFINE_MUTEX(swflag_mutex); + +/** * e1000_acquire_swflag_ich8lan - Acquire software control flag * @hw: pointer to the HW structure * - * Acquires the software control flag for performing NVM and PHY - * operations. This is a function pointer entry point only called by - * read/write routines for the PHY and NVM parts. + * Acquires the software control flag for performing PHY and select + * MAC CSR accesses. **/ static s32 e1000_acquire_swflag_ich8lan(struct e1000_hw *hw) { @@ -582,7 +638,7 @@ static s32 e1000_acquire_swflag_ich8lan(struct e1000_hw *hw) might_sleep(); - mutex_lock(&nvm_mutex); + mutex_lock(&swflag_mutex); while (timeout) { extcnf_ctrl = er32(EXTCNF_CTRL); @@ -599,7 +655,7 @@ static s32 e1000_acquire_swflag_ich8lan(struct e1000_hw *hw) goto out; } - timeout = PHY_CFG_TIMEOUT * 2; + timeout = SW_FLAG_TIMEOUT; extcnf_ctrl |= E1000_EXTCNF_CTRL_SWFLAG; ew32(EXTCNF_CTRL, extcnf_ctrl); @@ -623,7 +679,7 @@ static s32 e1000_acquire_swflag_ich8lan(struct e1000_hw *hw) out: if (ret_val) - mutex_unlock(&nvm_mutex); + mutex_unlock(&swflag_mutex); return ret_val; } @@ -632,9 +688,8 @@ out: * e1000_release_swflag_ich8lan - Release software control flag * @hw: pointer to the HW structure * - * Releases the software control flag for performing NVM and PHY operations. - * This is a function pointer entry point only called by read/write - * routines for the PHY and NVM parts. + * Releases the software control flag for performing PHY and select + * MAC CSR accesses. **/ static void e1000_release_swflag_ich8lan(struct e1000_hw *hw) { @@ -644,7 +699,9 @@ static void e1000_release_swflag_ich8lan(struct e1000_hw *hw) extcnf_ctrl &= ~E1000_EXTCNF_CTRL_SWFLAG; ew32(EXTCNF_CTRL, extcnf_ctrl); - mutex_unlock(&nvm_mutex); + mutex_unlock(&swflag_mutex); + + return; } /** @@ -752,6 +809,327 @@ static s32 e1000_phy_force_speed_duplex_ich8lan(struct e1000_hw *hw) } /** + * e1000_sw_lcd_config_ich8lan - SW-based LCD Configuration + * @hw: pointer to the HW structure + * + * SW should configure the LCD from the NVM extended configuration region + * as a workaround for certain parts. + **/ +static s32 e1000_sw_lcd_config_ich8lan(struct e1000_hw *hw) +{ + struct e1000_phy_info *phy = &hw->phy; + u32 i, data, cnf_size, cnf_base_addr, sw_cfg_mask; + s32 ret_val; + u16 word_addr, reg_data, reg_addr, phy_page = 0; + + ret_val = hw->phy.ops.acquire_phy(hw); + if (ret_val) + return ret_val; + + /* + * Initialize the PHY from the NVM on ICH platforms. This + * is needed due to an issue where the NVM configuration is + * not properly autoloaded after power transitions. + * Therefore, after each PHY reset, we will load the + * configuration data out of the NVM manually. + */ + if ((hw->mac.type == e1000_ich8lan && phy->type == e1000_phy_igp_3) || + (hw->mac.type == e1000_pchlan)) { + struct e1000_adapter *adapter = hw->adapter; + + /* Check if SW needs to configure the PHY */ + if ((adapter->pdev->device == E1000_DEV_ID_ICH8_IGP_M_AMT) || + (adapter->pdev->device == E1000_DEV_ID_ICH8_IGP_M) || + (hw->mac.type == e1000_pchlan)) + sw_cfg_mask = E1000_FEXTNVM_SW_CONFIG_ICH8M; + else + sw_cfg_mask = E1000_FEXTNVM_SW_CONFIG; + + data = er32(FEXTNVM); + if (!(data & sw_cfg_mask)) + goto out; + + /* Wait for basic configuration completes before proceeding */ + e1000_lan_init_done_ich8lan(hw); + + /* + * Make sure HW does not configure LCD from PHY + * extended configuration before SW configuration + */ + data = er32(EXTCNF_CTRL); + if (data & E1000_EXTCNF_CTRL_LCD_WRITE_ENABLE) + goto out; + + cnf_size = er32(EXTCNF_SIZE); + cnf_size &= E1000_EXTCNF_SIZE_EXT_PCIE_LENGTH_MASK; + cnf_size >>= E1000_EXTCNF_SIZE_EXT_PCIE_LENGTH_SHIFT; + if (!cnf_size) + goto out; + + cnf_base_addr = data & E1000_EXTCNF_CTRL_EXT_CNF_POINTER_MASK; + cnf_base_addr >>= E1000_EXTCNF_CTRL_EXT_CNF_POINTER_SHIFT; + + if (!(data & E1000_EXTCNF_CTRL_OEM_WRITE_ENABLE) && + (hw->mac.type == e1000_pchlan)) { + /* + * HW configures the SMBus address and LEDs when the + * OEM and LCD Write Enable bits are set in the NVM. + * When both NVM bits are cleared, SW will configure + * them instead. + */ + data = er32(STRAP); + data &= E1000_STRAP_SMBUS_ADDRESS_MASK; + reg_data = data >> E1000_STRAP_SMBUS_ADDRESS_SHIFT; + reg_data |= HV_SMB_ADDR_PEC_EN | HV_SMB_ADDR_VALID; + ret_val = e1000_write_phy_reg_hv_locked(hw, HV_SMB_ADDR, + reg_data); + if (ret_val) + goto out; + + data = er32(LEDCTL); + ret_val = e1000_write_phy_reg_hv_locked(hw, + HV_LED_CONFIG, + (u16)data); + if (ret_val) + goto out; + } + /* Configure LCD from extended configuration region. */ + + /* cnf_base_addr is in DWORD */ + word_addr = (u16)(cnf_base_addr << 1); + + for (i = 0; i < cnf_size; i++) { + ret_val = e1000_read_nvm(hw, (word_addr + i * 2), 1, + ®_data); + if (ret_val) + goto out; + + ret_val = e1000_read_nvm(hw, (word_addr + i * 2 + 1), + 1, ®_addr); + if (ret_val) + goto out; + + /* Save off the PHY page for future writes. */ + if (reg_addr == IGP01E1000_PHY_PAGE_SELECT) { + phy_page = reg_data; + continue; + } + + reg_addr &= PHY_REG_MASK; + reg_addr |= phy_page; + + ret_val = phy->ops.write_phy_reg_locked(hw, + (u32)reg_addr, + reg_data); + if (ret_val) + goto out; + } + } + +out: + hw->phy.ops.release_phy(hw); + return ret_val; +} + +/** + * e1000_k1_gig_workaround_hv - K1 Si workaround + * @hw: pointer to the HW structure + * @link: link up bool flag + * + * If K1 is enabled for 1Gbps, the MAC might stall when transitioning + * from a lower speed. This workaround disables K1 whenever link is at 1Gig + * If link is down, the function will restore the default K1 setting located + * in the NVM. + **/ +static s32 e1000_k1_gig_workaround_hv(struct e1000_hw *hw, bool link) +{ + s32 ret_val = 0; + u16 status_reg = 0; + bool k1_enable = hw->dev_spec.ich8lan.nvm_k1_enabled; + + if (hw->mac.type != e1000_pchlan) + goto out; + + /* Wrap the whole flow with the sw flag */ + ret_val = hw->phy.ops.acquire_phy(hw); + if (ret_val) + goto out; + + /* Disable K1 when link is 1Gbps, otherwise use the NVM setting */ + if (link) { + if (hw->phy.type == e1000_phy_82578) { + ret_val = hw->phy.ops.read_phy_reg_locked(hw, + BM_CS_STATUS, + &status_reg); + if (ret_val) + goto release; + + status_reg &= BM_CS_STATUS_LINK_UP | + BM_CS_STATUS_RESOLVED | + BM_CS_STATUS_SPEED_MASK; + + if (status_reg == (BM_CS_STATUS_LINK_UP | + BM_CS_STATUS_RESOLVED | + BM_CS_STATUS_SPEED_1000)) + k1_enable = false; + } + + if (hw->phy.type == e1000_phy_82577) { + ret_val = hw->phy.ops.read_phy_reg_locked(hw, + HV_M_STATUS, + &status_reg); + if (ret_val) + goto release; + + status_reg &= HV_M_STATUS_LINK_UP | + HV_M_STATUS_AUTONEG_COMPLETE | + HV_M_STATUS_SPEED_MASK; + + if (status_reg == (HV_M_STATUS_LINK_UP | + HV_M_STATUS_AUTONEG_COMPLETE | + HV_M_STATUS_SPEED_1000)) + k1_enable = false; + } + + /* Link stall fix for link up */ + ret_val = hw->phy.ops.write_phy_reg_locked(hw, PHY_REG(770, 19), + 0x0100); + if (ret_val) + goto release; + + } else { + /* Link stall fix for link down */ + ret_val = hw->phy.ops.write_phy_reg_locked(hw, PHY_REG(770, 19), + 0x4100); + if (ret_val) + goto release; + } + + ret_val = e1000_configure_k1_ich8lan(hw, k1_enable); + +release: + hw->phy.ops.release_phy(hw); +out: + return ret_val; +} + +/** + * e1000_configure_k1_ich8lan - Configure K1 power state + * @hw: pointer to the HW structure + * @enable: K1 state to configure + * + * Configure the K1 power state based on the provided parameter. + * Assumes semaphore already acquired. + * + * Success returns 0, Failure returns -E1000_ERR_PHY (-2) + **/ +static s32 e1000_configure_k1_ich8lan(struct e1000_hw *hw, bool k1_enable) +{ + s32 ret_val = 0; + u32 ctrl_reg = 0; + u32 ctrl_ext = 0; + u32 reg = 0; + u16 kmrn_reg = 0; + + ret_val = e1000e_read_kmrn_reg_locked(hw, + E1000_KMRNCTRLSTA_K1_CONFIG, + &kmrn_reg); + if (ret_val) + goto out; + + if (k1_enable) + kmrn_reg |= E1000_KMRNCTRLSTA_K1_ENABLE; + else + kmrn_reg &= ~E1000_KMRNCTRLSTA_K1_ENABLE; + + ret_val = e1000e_write_kmrn_reg_locked(hw, + E1000_KMRNCTRLSTA_K1_CONFIG, + kmrn_reg); + if (ret_val) + goto out; + + udelay(20); + ctrl_ext = er32(CTRL_EXT); + ctrl_reg = er32(CTRL); + + reg = ctrl_reg & ~(E1000_CTRL_SPD_1000 | E1000_CTRL_SPD_100); + reg |= E1000_CTRL_FRCSPD; + ew32(CTRL, reg); + + ew32(CTRL_EXT, ctrl_ext | E1000_CTRL_EXT_SPD_BYPS); + udelay(20); + ew32(CTRL, ctrl_reg); + ew32(CTRL_EXT, ctrl_ext); + udelay(20); + +out: + return ret_val; +} + +/** + * e1000_oem_bits_config_ich8lan - SW-based LCD Configuration + * @hw: pointer to the HW structure + * @d0_state: boolean if entering d0 or d3 device state + * + * SW will configure Gbe Disable and LPLU based on the NVM. The four bits are + * collectively called OEM bits. The OEM Write Enable bit and SW Config bit + * in NVM determines whether HW should configure LPLU and Gbe Disable. + **/ +static s32 e1000_oem_bits_config_ich8lan(struct e1000_hw *hw, bool d0_state) +{ + s32 ret_val = 0; + u32 mac_reg; + u16 oem_reg; + + if (hw->mac.type != e1000_pchlan) + return ret_val; + + ret_val = hw->phy.ops.acquire_phy(hw); + if (ret_val) + return ret_val; + + mac_reg = er32(EXTCNF_CTRL); + if (mac_reg & E1000_EXTCNF_CTRL_OEM_WRITE_ENABLE) + goto out; + + mac_reg = er32(FEXTNVM); + if (!(mac_reg & E1000_FEXTNVM_SW_CONFIG_ICH8M)) + goto out; + + mac_reg = er32(PHY_CTRL); + + ret_val = hw->phy.ops.read_phy_reg_locked(hw, HV_OEM_BITS, &oem_reg); + if (ret_val) + goto out; + + oem_reg &= ~(HV_OEM_BITS_GBE_DIS | HV_OEM_BITS_LPLU); + + if (d0_state) { + if (mac_reg & E1000_PHY_CTRL_GBE_DISABLE) + oem_reg |= HV_OEM_BITS_GBE_DIS; + + if (mac_reg & E1000_PHY_CTRL_D0A_LPLU) + oem_reg |= HV_OEM_BITS_LPLU; + } else { + if (mac_reg & E1000_PHY_CTRL_NOND0A_GBE_DISABLE) + oem_reg |= HV_OEM_BITS_GBE_DIS; + + if (mac_reg & E1000_PHY_CTRL_NOND0A_LPLU) + oem_reg |= HV_OEM_BITS_LPLU; + } + /* Restart auto-neg to activate the bits */ + if (!e1000_check_reset_block(hw)) + oem_reg |= HV_OEM_BITS_RESTART_AN; + ret_val = hw->phy.ops.write_phy_reg_locked(hw, HV_OEM_BITS, oem_reg); + +out: + hw->phy.ops.release_phy(hw); + + return ret_val; +} + + +/** * e1000_hv_phy_workarounds_ich8lan - A series of Phy workarounds to be * done after every PHY reset. **/ @@ -791,10 +1169,20 @@ static s32 e1000_hv_phy_workarounds_ich8lan(struct e1000_hw *hw) ret_val = hw->phy.ops.acquire_phy(hw); if (ret_val) return ret_val; + hw->phy.addr = 1; - e1000e_write_phy_reg_mdic(hw, IGP01E1000_PHY_PAGE_SELECT, 0); + ret_val = e1000e_write_phy_reg_mdic(hw, IGP01E1000_PHY_PAGE_SELECT, 0); + if (ret_val) + goto out; hw->phy.ops.release_phy(hw); + /* + * Configure the K1 Si workaround during phy reset assuming there is + * link so that it disables K1 if link is in 1Gbps. + */ + ret_val = e1000_k1_gig_workaround_hv(hw, true); + +out: return ret_val; } @@ -840,11 +1228,8 @@ static void e1000_lan_init_done_ich8lan(struct e1000_hw *hw) **/ static s32 e1000_phy_hw_reset_ich8lan(struct e1000_hw *hw) { - struct e1000_phy_info *phy = &hw->phy; - u32 i; - u32 data, cnf_size, cnf_base_addr, sw_cfg_mask; - s32 ret_val; - u16 word_addr, reg_data, reg_addr, phy_page = 0; + s32 ret_val = 0; + u16 reg; ret_val = e1000e_phy_hw_reset_generic(hw); if (ret_val) @@ -859,81 +1244,20 @@ static s32 e1000_phy_hw_reset_ich8lan(struct e1000_hw *hw) return ret_val; } - /* - * Initialize the PHY from the NVM on ICH platforms. This - * is needed due to an issue where the NVM configuration is - * not properly autoloaded after power transitions. - * Therefore, after each PHY reset, we will load the - * configuration data out of the NVM manually. - */ - if (hw->mac.type == e1000_ich8lan && phy->type == e1000_phy_igp_3) { - struct e1000_adapter *adapter = hw->adapter; - - /* Check if SW needs configure the PHY */ - if ((adapter->pdev->device == E1000_DEV_ID_ICH8_IGP_M_AMT) || - (adapter->pdev->device == E1000_DEV_ID_ICH8_IGP_M)) - sw_cfg_mask = E1000_FEXTNVM_SW_CONFIG_ICH8M; - else - sw_cfg_mask = E1000_FEXTNVM_SW_CONFIG; - - data = er32(FEXTNVM); - if (!(data & sw_cfg_mask)) - return 0; - - /* Wait for basic configuration completes before proceeding */ - e1000_lan_init_done_ich8lan(hw); - - /* - * Make sure HW does not configure LCD from PHY - * extended configuration before SW configuration - */ - data = er32(EXTCNF_CTRL); - if (data & E1000_EXTCNF_CTRL_LCD_WRITE_ENABLE) - return 0; - - cnf_size = er32(EXTCNF_SIZE); - cnf_size &= E1000_EXTCNF_SIZE_EXT_PCIE_LENGTH_MASK; - cnf_size >>= E1000_EXTCNF_SIZE_EXT_PCIE_LENGTH_SHIFT; - if (!cnf_size) - return 0; - - cnf_base_addr = data & E1000_EXTCNF_CTRL_EXT_CNF_POINTER_MASK; - cnf_base_addr >>= E1000_EXTCNF_CTRL_EXT_CNF_POINTER_SHIFT; - - /* Configure LCD from extended configuration region. */ - - /* cnf_base_addr is in DWORD */ - word_addr = (u16)(cnf_base_addr << 1); - - for (i = 0; i < cnf_size; i++) { - ret_val = e1000_read_nvm(hw, - (word_addr + i * 2), - 1, - ®_data); - if (ret_val) - return ret_val; - - ret_val = e1000_read_nvm(hw, - (word_addr + i * 2 + 1), - 1, - ®_addr); - if (ret_val) - return ret_val; - - /* Save off the PHY page for future writes. */ - if (reg_addr == IGP01E1000_PHY_PAGE_SELECT) { - phy_page = reg_data; - continue; - } + /* Dummy read to clear the phy wakeup bit after lcd reset */ + if (hw->mac.type == e1000_pchlan) + e1e_rphy(hw, BM_WUC, ®); - reg_addr |= phy_page; + /* Configure the LCD with the extended configuration region in NVM */ + ret_val = e1000_sw_lcd_config_ich8lan(hw); + if (ret_val) + goto out; - ret_val = e1e_wphy(hw, (u32)reg_addr, reg_data); - if (ret_val) - return ret_val; - } - } + /* Configure the LCD with the OEM bits in NVM */ + if (hw->mac.type == e1000_pchlan) + ret_val = e1000_oem_bits_config_ich8lan(hw, true); +out: return 0; } @@ -1054,6 +1378,38 @@ static s32 e1000_check_polarity_ife_ich8lan(struct e1000_hw *hw) } /** + * e1000_set_lplu_state_pchlan - Set Low Power Link Up state + * @hw: pointer to the HW structure + * @active: true to enable LPLU, false to disable + * + * Sets the LPLU state according to the active flag. For PCH, if OEM write + * bit are disabled in the NVM, writing the LPLU bits in the MAC will not set + * the phy speed. This function will manually set the LPLU bit and restart + * auto-neg as hw would do. D3 and D0 LPLU will call the same function + * since it configures the same bit. + **/ +static s32 e1000_set_lplu_state_pchlan(struct e1000_hw *hw, bool active) +{ + s32 ret_val = 0; + u16 oem_reg; + + ret_val = e1e_rphy(hw, HV_OEM_BITS, &oem_reg); + if (ret_val) + goto out; + + if (active) + oem_reg |= HV_OEM_BITS_LPLU; + else + oem_reg &= ~HV_OEM_BITS_LPLU; + + oem_reg |= HV_OEM_BITS_RESTART_AN; + ret_val = e1e_wphy(hw, HV_OEM_BITS, oem_reg); + +out: + return ret_val; +} + +/** * e1000_set_d0_lplu_state_ich8lan - Set Low Power Linkup D0 state * @hw: pointer to the HW structure * @active: TRUE to enable LPLU, FALSE to disable @@ -1314,12 +1670,11 @@ static s32 e1000_read_nvm_ich8lan(struct e1000_hw *hw, u16 offset, u16 words, if ((offset >= nvm->word_size) || (words > nvm->word_size - offset) || (words == 0)) { hw_dbg(hw, "nvm parameter(s) out of bounds\n"); - return -E1000_ERR_NVM; + ret_val = -E1000_ERR_NVM; + goto out; } - ret_val = e1000_acquire_swflag_ich8lan(hw); - if (ret_val) - goto out; + nvm->ops.acquire_nvm(hw); ret_val = e1000_valid_nvm_bank_detect_ich8lan(hw, &bank); if (ret_val) { @@ -1345,7 +1700,7 @@ static s32 e1000_read_nvm_ich8lan(struct e1000_hw *hw, u16 offset, u16 words, } } - e1000_release_swflag_ich8lan(hw); + nvm->ops.release_nvm(hw); out: if (ret_val) @@ -1603,11 +1958,15 @@ static s32 e1000_write_nvm_ich8lan(struct e1000_hw *hw, u16 offset, u16 words, return -E1000_ERR_NVM; } + nvm->ops.acquire_nvm(hw); + for (i = 0; i < words; i++) { dev_spec->shadow_ram[offset+i].modified = 1; dev_spec->shadow_ram[offset+i].value = data[i]; } + nvm->ops.release_nvm(hw); + return 0; } @@ -1637,9 +1996,7 @@ static s32 e1000_update_nvm_checksum_ich8lan(struct e1000_hw *hw) if (nvm->type != e1000_nvm_flash_sw) goto out; - ret_val = e1000_acquire_swflag_ich8lan(hw); - if (ret_val) - goto out; + nvm->ops.acquire_nvm(hw); /* * We're writing to the opposite bank so if we're on bank 1, @@ -1657,7 +2014,7 @@ static s32 e1000_update_nvm_checksum_ich8lan(struct e1000_hw *hw) old_bank_offset = 0; ret_val = e1000_erase_flash_bank_ich8lan(hw, 1); if (ret_val) { - e1000_release_swflag_ich8lan(hw); + nvm->ops.release_nvm(hw); goto out; } } else { @@ -1665,7 +2022,7 @@ static s32 e1000_update_nvm_checksum_ich8lan(struct e1000_hw *hw) new_bank_offset = 0; ret_val = e1000_erase_flash_bank_ich8lan(hw, 0); if (ret_val) { - e1000_release_swflag_ich8lan(hw); + nvm->ops.release_nvm(hw); goto out; } } @@ -1723,7 +2080,7 @@ static s32 e1000_update_nvm_checksum_ich8lan(struct e1000_hw *hw) if (ret_val) { /* Possibly read-only, see e1000e_write_protect_nvm_ich8lan() */ hw_dbg(hw, "Flash commit failed.\n"); - e1000_release_swflag_ich8lan(hw); + nvm->ops.release_nvm(hw); goto out; } @@ -1736,7 +2093,7 @@ static s32 e1000_update_nvm_checksum_ich8lan(struct e1000_hw *hw) act_offset = new_bank_offset + E1000_ICH_NVM_SIG_WORD; ret_val = e1000_read_flash_word_ich8lan(hw, act_offset, &data); if (ret_val) { - e1000_release_swflag_ich8lan(hw); + nvm->ops.release_nvm(hw); goto out; } data &= 0xBFFF; @@ -1744,7 +2101,7 @@ static s32 e1000_update_nvm_checksum_ich8lan(struct e1000_hw *hw) act_offset * 2 + 1, (u8)(data >> 8)); if (ret_val) { - e1000_release_swflag_ich8lan(hw); + nvm->ops.release_nvm(hw); goto out; } @@ -1757,7 +2114,7 @@ static s32 e1000_update_nvm_checksum_ich8lan(struct e1000_hw *hw) act_offset = (old_bank_offset + E1000_ICH_NVM_SIG_WORD) * 2 + 1; ret_val = e1000_retry_write_flash_byte_ich8lan(hw, act_offset, 0); if (ret_val) { - e1000_release_swflag_ich8lan(hw); + nvm->ops.release_nvm(hw); goto out; } @@ -1767,7 +2124,7 @@ static s32 e1000_update_nvm_checksum_ich8lan(struct e1000_hw *hw) dev_spec->shadow_ram[i].value = 0xFFFF; } - e1000_release_swflag_ich8lan(hw); + nvm->ops.release_nvm(hw); /* * Reload the EEPROM, or else modifications will not appear @@ -1831,14 +2188,12 @@ static s32 e1000_validate_nvm_checksum_ich8lan(struct e1000_hw *hw) **/ void e1000e_write_protect_nvm_ich8lan(struct e1000_hw *hw) { + struct e1000_nvm_info *nvm = &hw->nvm; union ich8_flash_protected_range pr0; union ich8_hws_flash_status hsfsts; u32 gfpreg; - s32 ret_val; - ret_val = e1000_acquire_swflag_ich8lan(hw); - if (ret_val) - return; + nvm->ops.acquire_nvm(hw); gfpreg = er32flash(ICH_FLASH_GFPREG); @@ -1859,7 +2214,7 @@ void e1000e_write_protect_nvm_ich8lan(struct e1000_hw *hw) hsfsts.hsf_status.flockdn = true; ew32flash(ICH_FLASH_HSFSTS, hsfsts.regval); - e1000_release_swflag_ich8lan(hw); + nvm->ops.release_nvm(hw); } /** @@ -2229,6 +2584,8 @@ static s32 e1000_get_bus_info_ich8lan(struct e1000_hw *hw) **/ static s32 e1000_reset_hw_ich8lan(struct e1000_hw *hw) { + struct e1000_dev_spec_ich8lan *dev_spec = &hw->dev_spec.ich8lan; + u16 reg; u32 ctrl, icr, kab; s32 ret_val; @@ -2263,6 +2620,18 @@ static s32 e1000_reset_hw_ich8lan(struct e1000_hw *hw) ew32(PBS, E1000_PBS_16K); } + if (hw->mac.type == e1000_pchlan) { + /* Save the NVM K1 bit setting*/ + ret_val = e1000_read_nvm(hw, E1000_NVM_K1_CONFIG, 1, ®); + if (ret_val) + return ret_val; + + if (reg & E1000_NVM_K1_ENABLE) + dev_spec->nvm_k1_enabled = true; + else + dev_spec->nvm_k1_enabled = false; + } + ctrl = er32(CTRL); if (!e1000_check_reset_block(hw)) { @@ -2304,7 +2673,19 @@ static s32 e1000_reset_hw_ich8lan(struct e1000_hw *hw) hw_dbg(hw, "Auto Read Done did not complete\n"); } } + /* Dummy read to clear the phy wakeup bit after lcd reset */ + if (hw->mac.type == e1000_pchlan) + e1e_rphy(hw, BM_WUC, ®); + ret_val = e1000_sw_lcd_config_ich8lan(hw); + if (ret_val) + goto out; + + if (hw->mac.type == e1000_pchlan) { + ret_val = e1000_oem_bits_config_ich8lan(hw, true); + if (ret_val) + goto out; + } /* * For PCH, this write will make sure that any noise * will be detected as a CRC error and be dropped rather than show up @@ -2323,6 +2704,7 @@ static s32 e1000_reset_hw_ich8lan(struct e1000_hw *hw) if (hw->mac.type == e1000_pchlan) ret_val = e1000_hv_phy_workarounds_ich8lan(hw); +out: return ret_val; } @@ -2627,14 +3009,6 @@ static s32 e1000_get_link_up_info_ich8lan(struct e1000_hw *hw, u16 *speed, if (ret_val) return ret_val; - if ((hw->mac.type == e1000_pchlan) && (*speed == SPEED_1000)) { - ret_val = e1000e_write_kmrn_reg(hw, - E1000_KMRNCTRLSTA_K1_CONFIG, - E1000_KMRNCTRLSTA_K1_DISABLE); - if (ret_val) - return ret_val; - } - if ((hw->mac.type == e1000_ich8lan) && (hw->phy.type == e1000_phy_igp_3) && (*speed == SPEED_1000)) { @@ -2843,9 +3217,8 @@ void e1000e_disable_gig_wol_ich8lan(struct e1000_hw *hw) E1000_PHY_CTRL_GBE_DISABLE; ew32(PHY_CTRL, phy_ctrl); - /* Workaround SWFLAG unexpectedly set during S0->Sx */ if (hw->mac.type == e1000_pchlan) - udelay(500); + e1000_phy_hw_reset_ich8lan(hw); default: break; } @@ -3113,9 +3486,9 @@ static struct e1000_phy_operations ich8_phy_ops = { }; static struct e1000_nvm_operations ich8_nvm_ops = { - .acquire_nvm = e1000_acquire_swflag_ich8lan, + .acquire_nvm = e1000_acquire_nvm_ich8lan, .read_nvm = e1000_read_nvm_ich8lan, - .release_nvm = e1000_release_swflag_ich8lan, + .release_nvm = e1000_release_nvm_ich8lan, .update_nvm = e1000_update_nvm_checksum_ich8lan, .valid_led_default = e1000_valid_led_default_ich8lan, .validate_nvm = e1000_validate_nvm_checksum_ich8lan, @@ -3186,6 +3559,7 @@ struct e1000_info e1000_pch_info = { | FLAG_HAS_AMT | FLAG_HAS_FLASH | FLAG_HAS_JUMBO_FRAMES + | FLAG_DISABLE_FC_PAUSE_TIME /* errata */ | FLAG_APME_IN_WUC, .pba = 26, .max_hw_frame_size = 4096, diff --git a/drivers/net/e1000e/netdev.c b/drivers/net/e1000e/netdev.c index 16c193a6c95c..fad8f9ea0043 100644 --- a/drivers/net/e1000e/netdev.c +++ b/drivers/net/e1000e/netdev.c @@ -2769,25 +2769,38 @@ void e1000e_reset(struct e1000_adapter *adapter) /* * flow control settings * - * The high water mark must be low enough to fit two full frame + * The high water mark must be low enough to fit one full frame * (or the size used for early receive) above it in the Rx FIFO. * Set it to the lower of: * - 90% of the Rx FIFO size, and * - the full Rx FIFO size minus the early receive size (for parts * with ERT support assuming ERT set to E1000_ERT_2048), or - * - the full Rx FIFO size minus two full frames + * - the full Rx FIFO size minus one full frame */ - if ((adapter->flags & FLAG_HAS_ERT) && - (adapter->netdev->mtu > ETH_DATA_LEN)) - hwm = min(((pba << 10) * 9 / 10), - ((pba << 10) - (E1000_ERT_2048 << 3))); - else - hwm = min(((pba << 10) * 9 / 10), - ((pba << 10) - (2 * adapter->max_frame_size))); + if (hw->mac.type == e1000_pchlan) { + /* + * Workaround PCH LOM adapter hangs with certain network + * loads. If hangs persist, try disabling Tx flow control. + */ + if (adapter->netdev->mtu > ETH_DATA_LEN) { + fc->high_water = 0x3500; + fc->low_water = 0x1500; + } else { + fc->high_water = 0x5000; + fc->low_water = 0x3000; + } + } else { + if ((adapter->flags & FLAG_HAS_ERT) && + (adapter->netdev->mtu > ETH_DATA_LEN)) + hwm = min(((pba << 10) * 9 / 10), + ((pba << 10) - (E1000_ERT_2048 << 3))); + else + hwm = min(((pba << 10) * 9 / 10), + ((pba << 10) - adapter->max_frame_size)); - fc->high_water = hwm & E1000_FCRTH_RTH; /* 8-byte granularity */ - fc->low_water = (fc->high_water - (2 * adapter->max_frame_size)); - fc->low_water &= E1000_FCRTL_RTL; /* 8-byte granularity */ + fc->high_water = hwm & E1000_FCRTH_RTH; /* 8-byte granularity */ + fc->low_water = fc->high_water - 8; + } if (adapter->flags & FLAG_DISABLE_FC_PAUSE_TIME) fc->pause_time = 0xFFFF; @@ -2813,6 +2826,10 @@ void e1000e_reset(struct e1000_adapter *adapter) if (mac->ops.init_hw(hw)) e_err("Hardware Error\n"); + /* additional part of the flow-control workaround above */ + if (hw->mac.type == e1000_pchlan) + ew32(FCRTV_PCH, 0x1000); + e1000_update_mng_vlan(adapter); /* Enable h/w to recognize an 802.1Q VLAN Ethernet packet */ @@ -3610,7 +3627,7 @@ static void e1000_watchdog_task(struct work_struct *work) case SPEED_100: txb2b = 0; netdev->tx_queue_len = 100; - /* maybe add some timeout factor ? */ + adapter->tx_timeout_factor = 10; break; } @@ -4288,8 +4305,10 @@ static int e1000_change_mtu(struct net_device *netdev, int new_mtu) while (test_and_set_bit(__E1000_RESETTING, &adapter->state)) msleep(1); - /* e1000e_down has a dependency on max_frame_size */ + /* e1000e_down -> e1000e_reset dependent on max_frame_size & mtu */ adapter->max_frame_size = max_frame; + e_info("changing MTU from %d to %d\n", netdev->mtu, new_mtu); + netdev->mtu = new_mtu; if (netif_running(netdev)) e1000e_down(adapter); @@ -4319,9 +4338,6 @@ static int e1000_change_mtu(struct net_device *netdev, int new_mtu) adapter->rx_buffer_len = ETH_FRAME_LEN + VLAN_HLEN + ETH_FCS_LEN; - e_info("changing MTU from %d to %d\n", netdev->mtu, new_mtu); - netdev->mtu = new_mtu; - if (netif_running(netdev)) e1000e_up(adapter); else @@ -4982,12 +4998,7 @@ static int __devinit e1000_probe(struct pci_dev *pdev, goto err_pci_reg; /* AER (Advanced Error Reporting) hooks */ - err = pci_enable_pcie_error_reporting(pdev); - if (err) { - dev_err(&pdev->dev, "pci_enable_pcie_error_reporting failed " - "0x%x\n", err); - /* non-fatal, continue */ - } + pci_enable_pcie_error_reporting(pdev); pci_set_master(pdev); /* PCI config space info */ @@ -5263,7 +5274,6 @@ static void __devexit e1000_remove(struct pci_dev *pdev) { struct net_device *netdev = pci_get_drvdata(pdev); struct e1000_adapter *adapter = netdev_priv(netdev); - int err; /* * flush_scheduled work may reschedule our watchdog task, so @@ -5299,10 +5309,7 @@ static void __devexit e1000_remove(struct pci_dev *pdev) free_netdev(netdev); /* AER disable */ - err = pci_disable_pcie_error_reporting(pdev); - if (err) - dev_err(&pdev->dev, - "pci_disable_pcie_error_reporting failed 0x%x\n", err); + pci_disable_pcie_error_reporting(pdev); pci_disable_device(pdev); } diff --git a/drivers/net/e1000e/phy.c b/drivers/net/e1000e/phy.c index 994401fd0664..85f955f70417 100644 --- a/drivers/net/e1000e/phy.c +++ b/drivers/net/e1000e/phy.c @@ -71,7 +71,6 @@ static const u16 e1000_igp_2_cable_length_table[] = #define I82577_CFG_ASSERT_CRS_ON_TX (1 << 15) #define I82577_CFG_ENABLE_DOWNSHIFT (3 << 10) /* auto downshift 100/10 */ #define I82577_CTRL_REG 23 -#define I82577_CTRL_DOWNSHIFT_MASK (7 << 10) /* 82577 specific PHY registers */ #define I82577_PHY_CTRL_2 18 @@ -95,13 +94,6 @@ static const u16 e1000_igp_2_cable_length_table[] = /* BM PHY Copper Specific Control 1 */ #define BM_CS_CTRL1 16 -/* BM PHY Copper Specific Status */ -#define BM_CS_STATUS 17 -#define BM_CS_STATUS_LINK_UP 0x0400 -#define BM_CS_STATUS_RESOLVED 0x0800 -#define BM_CS_STATUS_SPEED_MASK 0xC000 -#define BM_CS_STATUS_SPEED_1000 0x8000 - #define HV_MUX_DATA_CTRL PHY_REG(776, 16) #define HV_MUX_DATA_CTRL_GEN_TO_MAC 0x0400 #define HV_MUX_DATA_CTRL_FORCE_SPEED 0x0004 @@ -164,16 +156,25 @@ s32 e1000e_get_phy_id(struct e1000_hw *hw) * MDIC mode. No harm in trying again in this case since * the PHY ID is unknown at this point anyway */ + ret_val = phy->ops.acquire_phy(hw); + if (ret_val) + goto out; ret_val = e1000_set_mdio_slow_mode_hv(hw, true); if (ret_val) goto out; + phy->ops.release_phy(hw); retry_count++; } out: /* Revert to MDIO fast mode, if applicable */ - if (retry_count) + if (retry_count) { + ret_val = phy->ops.acquire_phy(hw); + if (ret_val) + return ret_val; ret_val = e1000_set_mdio_slow_mode_hv(hw, false); + phy->ops.release_phy(hw); + } return ret_val; } @@ -354,94 +355,173 @@ s32 e1000e_write_phy_reg_m88(struct e1000_hw *hw, u32 offset, u16 data) } /** - * e1000e_read_phy_reg_igp - Read igp PHY register + * __e1000e_read_phy_reg_igp - Read igp PHY register * @hw: pointer to the HW structure * @offset: register offset to be read * @data: pointer to the read data + * @locked: semaphore has already been acquired or not * * Acquires semaphore, if necessary, then reads the PHY register at offset - * and storing the retrieved information in data. Release any acquired + * and stores the retrieved information in data. Release any acquired * semaphores before exiting. **/ -s32 e1000e_read_phy_reg_igp(struct e1000_hw *hw, u32 offset, u16 *data) +static s32 __e1000e_read_phy_reg_igp(struct e1000_hw *hw, u32 offset, u16 *data, + bool locked) { - s32 ret_val; + s32 ret_val = 0; - ret_val = hw->phy.ops.acquire_phy(hw); - if (ret_val) - return ret_val; + if (!locked) { + if (!(hw->phy.ops.acquire_phy)) + goto out; + + ret_val = hw->phy.ops.acquire_phy(hw); + if (ret_val) + goto out; + } if (offset > MAX_PHY_MULTI_PAGE_REG) { ret_val = e1000e_write_phy_reg_mdic(hw, IGP01E1000_PHY_PAGE_SELECT, (u16)offset); - if (ret_val) { - hw->phy.ops.release_phy(hw); - return ret_val; - } + if (ret_val) + goto release; } ret_val = e1000e_read_phy_reg_mdic(hw, MAX_PHY_REG_ADDRESS & offset, - data); - - hw->phy.ops.release_phy(hw); + data); +release: + if (!locked) + hw->phy.ops.release_phy(hw); +out: return ret_val; } /** + * e1000e_read_phy_reg_igp - Read igp PHY register + * @hw: pointer to the HW structure + * @offset: register offset to be read + * @data: pointer to the read data + * + * Acquires semaphore then reads the PHY register at offset and stores the + * retrieved information in data. + * Release the acquired semaphore before exiting. + **/ +s32 e1000e_read_phy_reg_igp(struct e1000_hw *hw, u32 offset, u16 *data) +{ + return __e1000e_read_phy_reg_igp(hw, offset, data, false); +} + +/** + * e1000e_read_phy_reg_igp_locked - Read igp PHY register + * @hw: pointer to the HW structure + * @offset: register offset to be read + * @data: pointer to the read data + * + * Reads the PHY register at offset and stores the retrieved information + * in data. Assumes semaphore already acquired. + **/ +s32 e1000e_read_phy_reg_igp_locked(struct e1000_hw *hw, u32 offset, u16 *data) +{ + return __e1000e_read_phy_reg_igp(hw, offset, data, true); +} + +/** * e1000e_write_phy_reg_igp - Write igp PHY register * @hw: pointer to the HW structure * @offset: register offset to write to * @data: data to write at register offset + * @locked: semaphore has already been acquired or not * * Acquires semaphore, if necessary, then writes the data to PHY register * at the offset. Release any acquired semaphores before exiting. **/ -s32 e1000e_write_phy_reg_igp(struct e1000_hw *hw, u32 offset, u16 data) +static s32 __e1000e_write_phy_reg_igp(struct e1000_hw *hw, u32 offset, u16 data, + bool locked) { - s32 ret_val; + s32 ret_val = 0; - ret_val = hw->phy.ops.acquire_phy(hw); - if (ret_val) - return ret_val; + if (!locked) { + if (!(hw->phy.ops.acquire_phy)) + goto out; + + ret_val = hw->phy.ops.acquire_phy(hw); + if (ret_val) + goto out; + } if (offset > MAX_PHY_MULTI_PAGE_REG) { ret_val = e1000e_write_phy_reg_mdic(hw, IGP01E1000_PHY_PAGE_SELECT, (u16)offset); - if (ret_val) { - hw->phy.ops.release_phy(hw); - return ret_val; - } + if (ret_val) + goto release; } ret_val = e1000e_write_phy_reg_mdic(hw, MAX_PHY_REG_ADDRESS & offset, data); - hw->phy.ops.release_phy(hw); +release: + if (!locked) + hw->phy.ops.release_phy(hw); +out: return ret_val; } /** - * e1000e_read_kmrn_reg - Read kumeran register + * e1000e_write_phy_reg_igp - Write igp PHY register + * @hw: pointer to the HW structure + * @offset: register offset to write to + * @data: data to write at register offset + * + * Acquires semaphore then writes the data to PHY register + * at the offset. Release any acquired semaphores before exiting. + **/ +s32 e1000e_write_phy_reg_igp(struct e1000_hw *hw, u32 offset, u16 data) +{ + return __e1000e_write_phy_reg_igp(hw, offset, data, false); +} + +/** + * e1000e_write_phy_reg_igp_locked - Write igp PHY register + * @hw: pointer to the HW structure + * @offset: register offset to write to + * @data: data to write at register offset + * + * Writes the data to PHY register at the offset. + * Assumes semaphore already acquired. + **/ +s32 e1000e_write_phy_reg_igp_locked(struct e1000_hw *hw, u32 offset, u16 data) +{ + return __e1000e_write_phy_reg_igp(hw, offset, data, true); +} + +/** + * __e1000_read_kmrn_reg - Read kumeran register * @hw: pointer to the HW structure * @offset: register offset to be read * @data: pointer to the read data + * @locked: semaphore has already been acquired or not * * Acquires semaphore, if necessary. Then reads the PHY register at offset * using the kumeran interface. The information retrieved is stored in data. * Release any acquired semaphores before exiting. **/ -s32 e1000e_read_kmrn_reg(struct e1000_hw *hw, u32 offset, u16 *data) +static s32 __e1000_read_kmrn_reg(struct e1000_hw *hw, u32 offset, u16 *data, + bool locked) { u32 kmrnctrlsta; - s32 ret_val; + s32 ret_val = 0; - ret_val = hw->phy.ops.acquire_phy(hw); - if (ret_val) - return ret_val; + if (!locked) { + if (!(hw->phy.ops.acquire_phy)) + goto out; + + ret_val = hw->phy.ops.acquire_phy(hw); + if (ret_val) + goto out; + } kmrnctrlsta = ((offset << E1000_KMRNCTRLSTA_OFFSET_SHIFT) & E1000_KMRNCTRLSTA_OFFSET) | E1000_KMRNCTRLSTA_REN; @@ -452,41 +532,111 @@ s32 e1000e_read_kmrn_reg(struct e1000_hw *hw, u32 offset, u16 *data) kmrnctrlsta = er32(KMRNCTRLSTA); *data = (u16)kmrnctrlsta; - hw->phy.ops.release_phy(hw); + if (!locked) + hw->phy.ops.release_phy(hw); +out: return ret_val; } /** - * e1000e_write_kmrn_reg - Write kumeran register + * e1000e_read_kmrn_reg - Read kumeran register + * @hw: pointer to the HW structure + * @offset: register offset to be read + * @data: pointer to the read data + * + * Acquires semaphore then reads the PHY register at offset using the + * kumeran interface. The information retrieved is stored in data. + * Release the acquired semaphore before exiting. + **/ +s32 e1000e_read_kmrn_reg(struct e1000_hw *hw, u32 offset, u16 *data) +{ + return __e1000_read_kmrn_reg(hw, offset, data, false); +} + +/** + * e1000e_read_kmrn_reg_locked - Read kumeran register + * @hw: pointer to the HW structure + * @offset: register offset to be read + * @data: pointer to the read data + * + * Reads the PHY register at offset using the kumeran interface. The + * information retrieved is stored in data. + * Assumes semaphore already acquired. + **/ +s32 e1000e_read_kmrn_reg_locked(struct e1000_hw *hw, u32 offset, u16 *data) +{ + return __e1000_read_kmrn_reg(hw, offset, data, true); +} + +/** + * __e1000_write_kmrn_reg - Write kumeran register * @hw: pointer to the HW structure * @offset: register offset to write to * @data: data to write at register offset + * @locked: semaphore has already been acquired or not * * Acquires semaphore, if necessary. Then write the data to PHY register * at the offset using the kumeran interface. Release any acquired semaphores * before exiting. **/ -s32 e1000e_write_kmrn_reg(struct e1000_hw *hw, u32 offset, u16 data) +static s32 __e1000_write_kmrn_reg(struct e1000_hw *hw, u32 offset, u16 data, + bool locked) { u32 kmrnctrlsta; - s32 ret_val; + s32 ret_val = 0; - ret_val = hw->phy.ops.acquire_phy(hw); - if (ret_val) - return ret_val; + if (!locked) { + if (!(hw->phy.ops.acquire_phy)) + goto out; + + ret_val = hw->phy.ops.acquire_phy(hw); + if (ret_val) + goto out; + } kmrnctrlsta = ((offset << E1000_KMRNCTRLSTA_OFFSET_SHIFT) & E1000_KMRNCTRLSTA_OFFSET) | data; ew32(KMRNCTRLSTA, kmrnctrlsta); udelay(2); - hw->phy.ops.release_phy(hw); + if (!locked) + hw->phy.ops.release_phy(hw); + +out: return ret_val; } /** + * e1000e_write_kmrn_reg - Write kumeran register + * @hw: pointer to the HW structure + * @offset: register offset to write to + * @data: data to write at register offset + * + * Acquires semaphore then writes the data to the PHY register at the offset + * using the kumeran interface. Release the acquired semaphore before exiting. + **/ +s32 e1000e_write_kmrn_reg(struct e1000_hw *hw, u32 offset, u16 data) +{ + return __e1000_write_kmrn_reg(hw, offset, data, false); +} + +/** + * e1000e_write_kmrn_reg_locked - Write kumeran register + * @hw: pointer to the HW structure + * @offset: register offset to write to + * @data: data to write at register offset + * + * Write the data to PHY register at the offset using the kumeran interface. + * Assumes semaphore already acquired. + **/ +s32 e1000e_write_kmrn_reg_locked(struct e1000_hw *hw, u32 offset, u16 data) +{ + return __e1000_write_kmrn_reg(hw, offset, data, true); +} + +/** * e1000_copper_link_setup_82577 - Setup 82577 PHY for copper link * @hw: pointer to the HW structure * @@ -509,15 +659,6 @@ s32 e1000_copper_link_setup_82577(struct e1000_hw *hw) phy_data |= I82577_CFG_ENABLE_DOWNSHIFT; ret_val = phy->ops.write_phy_reg(hw, I82577_CFG_REG, phy_data); - if (ret_val) - goto out; - - /* Set number of link attempts before downshift */ - ret_val = phy->ops.read_phy_reg(hw, I82577_CTRL_REG, &phy_data); - if (ret_val) - goto out; - phy_data &= ~I82577_CTRL_DOWNSHIFT_MASK; - ret_val = phy->ops.write_phy_reg(hw, I82577_CTRL_REG, phy_data); out: return ret_val; @@ -2105,6 +2246,10 @@ s32 e1000e_write_phy_reg_bm(struct e1000_hw *hw, u32 offset, u16 data) u32 page = offset >> IGP_PAGE_SHIFT; u32 page_shift = 0; + ret_val = hw->phy.ops.acquire_phy(hw); + if (ret_val) + return ret_val; + /* Page 800 works differently than the rest so it has its own func */ if (page == BM_WUC_PAGE) { ret_val = e1000_access_phy_wakeup_reg_bm(hw, offset, &data, @@ -2112,10 +2257,6 @@ s32 e1000e_write_phy_reg_bm(struct e1000_hw *hw, u32 offset, u16 data) goto out; } - ret_val = hw->phy.ops.acquire_phy(hw); - if (ret_val) - goto out; - hw->phy.addr = e1000_get_phy_addr_for_bm_page(page, offset); if (offset > MAX_PHY_MULTI_PAGE_REG) { @@ -2135,18 +2276,15 @@ s32 e1000e_write_phy_reg_bm(struct e1000_hw *hw, u32 offset, u16 data) /* Page is shifted left, PHY expects (page x 32) */ ret_val = e1000e_write_phy_reg_mdic(hw, page_select, (page << page_shift)); - if (ret_val) { - hw->phy.ops.release_phy(hw); + if (ret_val) goto out; - } } ret_val = e1000e_write_phy_reg_mdic(hw, MAX_PHY_REG_ADDRESS & offset, data); - hw->phy.ops.release_phy(hw); - out: + hw->phy.ops.release_phy(hw); return ret_val; } @@ -2167,6 +2305,10 @@ s32 e1000e_read_phy_reg_bm(struct e1000_hw *hw, u32 offset, u16 *data) u32 page = offset >> IGP_PAGE_SHIFT; u32 page_shift = 0; + ret_val = hw->phy.ops.acquire_phy(hw); + if (ret_val) + return ret_val; + /* Page 800 works differently than the rest so it has its own func */ if (page == BM_WUC_PAGE) { ret_val = e1000_access_phy_wakeup_reg_bm(hw, offset, data, @@ -2174,10 +2316,6 @@ s32 e1000e_read_phy_reg_bm(struct e1000_hw *hw, u32 offset, u16 *data) goto out; } - ret_val = hw->phy.ops.acquire_phy(hw); - if (ret_val) - goto out; - hw->phy.addr = e1000_get_phy_addr_for_bm_page(page, offset); if (offset > MAX_PHY_MULTI_PAGE_REG) { @@ -2197,17 +2335,14 @@ s32 e1000e_read_phy_reg_bm(struct e1000_hw *hw, u32 offset, u16 *data) /* Page is shifted left, PHY expects (page x 32) */ ret_val = e1000e_write_phy_reg_mdic(hw, page_select, (page << page_shift)); - if (ret_val) { - hw->phy.ops.release_phy(hw); + if (ret_val) goto out; - } } ret_val = e1000e_read_phy_reg_mdic(hw, MAX_PHY_REG_ADDRESS & offset, data); - hw->phy.ops.release_phy(hw); - out: + hw->phy.ops.release_phy(hw); return ret_val; } @@ -2226,17 +2361,17 @@ s32 e1000e_read_phy_reg_bm2(struct e1000_hw *hw, u32 offset, u16 *data) s32 ret_val; u16 page = (u16)(offset >> IGP_PAGE_SHIFT); + ret_val = hw->phy.ops.acquire_phy(hw); + if (ret_val) + return ret_val; + /* Page 800 works differently than the rest so it has its own func */ if (page == BM_WUC_PAGE) { ret_val = e1000_access_phy_wakeup_reg_bm(hw, offset, data, true); - return ret_val; + goto out; } - ret_val = hw->phy.ops.acquire_phy(hw); - if (ret_val) - return ret_val; - hw->phy.addr = 1; if (offset > MAX_PHY_MULTI_PAGE_REG) { @@ -2245,16 +2380,14 @@ s32 e1000e_read_phy_reg_bm2(struct e1000_hw *hw, u32 offset, u16 *data) ret_val = e1000e_write_phy_reg_mdic(hw, BM_PHY_PAGE_SELECT, page); - if (ret_val) { - hw->phy.ops.release_phy(hw); - return ret_val; - } + if (ret_val) + goto out; } ret_val = e1000e_read_phy_reg_mdic(hw, MAX_PHY_REG_ADDRESS & offset, data); +out: hw->phy.ops.release_phy(hw); - return ret_val; } @@ -2272,17 +2405,17 @@ s32 e1000e_write_phy_reg_bm2(struct e1000_hw *hw, u32 offset, u16 data) s32 ret_val; u16 page = (u16)(offset >> IGP_PAGE_SHIFT); + ret_val = hw->phy.ops.acquire_phy(hw); + if (ret_val) + return ret_val; + /* Page 800 works differently than the rest so it has its own func */ if (page == BM_WUC_PAGE) { ret_val = e1000_access_phy_wakeup_reg_bm(hw, offset, &data, false); - return ret_val; + goto out; } - ret_val = hw->phy.ops.acquire_phy(hw); - if (ret_val) - return ret_val; - hw->phy.addr = 1; if (offset > MAX_PHY_MULTI_PAGE_REG) { @@ -2290,17 +2423,15 @@ s32 e1000e_write_phy_reg_bm2(struct e1000_hw *hw, u32 offset, u16 data) ret_val = e1000e_write_phy_reg_mdic(hw, BM_PHY_PAGE_SELECT, page); - if (ret_val) { - hw->phy.ops.release_phy(hw); - return ret_val; - } + if (ret_val) + goto out; } ret_val = e1000e_write_phy_reg_mdic(hw, MAX_PHY_REG_ADDRESS & offset, data); +out: hw->phy.ops.release_phy(hw); - return ret_val; } @@ -2320,6 +2451,8 @@ s32 e1000e_write_phy_reg_bm2(struct e1000_hw *hw, u32 offset, u16 data) * 3) Write the address using the address opcode (0x11) * 4) Read or write the data using the data opcode (0x12) * 5) Restore 769_17.2 to its original value + * + * Assumes semaphore already acquired. **/ static s32 e1000_access_phy_wakeup_reg_bm(struct e1000_hw *hw, u32 offset, u16 *data, bool read) @@ -2327,20 +2460,12 @@ static s32 e1000_access_phy_wakeup_reg_bm(struct e1000_hw *hw, u32 offset, s32 ret_val; u16 reg = BM_PHY_REG_NUM(offset); u16 phy_reg = 0; - u8 phy_acquired = 1; - /* Gig must be disabled for MDIO accesses to page 800 */ if ((hw->mac.type == e1000_pchlan) && (!(er32(PHY_CTRL) & E1000_PHY_CTRL_GBE_DISABLE))) hw_dbg(hw, "Attempting to access page 800 while gig enabled\n"); - ret_val = hw->phy.ops.acquire_phy(hw); - if (ret_val) { - phy_acquired = 0; - goto out; - } - /* All operations in this function are phy address 1 */ hw->phy.addr = 1; @@ -2397,8 +2522,6 @@ static s32 e1000_access_phy_wakeup_reg_bm(struct e1000_hw *hw, u32 offset, ret_val = e1000e_write_phy_reg_mdic(hw, BM_WUC_ENABLE_REG, phy_reg); out: - if (phy_acquired == 1) - hw->phy.ops.release_phy(hw); return ret_val; } @@ -2439,52 +2562,63 @@ static s32 e1000_set_d0_lplu_state(struct e1000_hw *hw, bool active) return 0; } +/** + * e1000_set_mdio_slow_mode_hv - Set slow MDIO access mode + * @hw: pointer to the HW structure + * @slow: true for slow mode, false for normal mode + * + * Assumes semaphore already acquired. + **/ s32 e1000_set_mdio_slow_mode_hv(struct e1000_hw *hw, bool slow) { s32 ret_val = 0; u16 data = 0; - ret_val = hw->phy.ops.acquire_phy(hw); - if (ret_val) - return ret_val; - /* Set MDIO mode - page 769, register 16: 0x2580==slow, 0x2180==fast */ hw->phy.addr = 1; ret_val = e1000e_write_phy_reg_mdic(hw, IGP01E1000_PHY_PAGE_SELECT, (BM_PORT_CTRL_PAGE << IGP_PAGE_SHIFT)); - if (ret_val) { - hw->phy.ops.release_phy(hw); - return ret_val; - } + if (ret_val) + goto out; + ret_val = e1000e_write_phy_reg_mdic(hw, BM_CS_CTRL1, (0x2180 | (slow << 10))); + if (ret_val) + goto out; /* dummy read when reverting to fast mode - throw away result */ if (!slow) - e1000e_read_phy_reg_mdic(hw, BM_CS_CTRL1, &data); - - hw->phy.ops.release_phy(hw); + ret_val = e1000e_read_phy_reg_mdic(hw, BM_CS_CTRL1, &data); +out: return ret_val; } /** - * e1000_read_phy_reg_hv - Read HV PHY register + * __e1000_read_phy_reg_hv - Read HV PHY register * @hw: pointer to the HW structure * @offset: register offset to be read * @data: pointer to the read data + * @locked: semaphore has already been acquired or not * * Acquires semaphore, if necessary, then reads the PHY register at offset - * and storing the retrieved information in data. Release any acquired + * and stores the retrieved information in data. Release any acquired * semaphore before exiting. **/ -s32 e1000_read_phy_reg_hv(struct e1000_hw *hw, u32 offset, u16 *data) +static s32 __e1000_read_phy_reg_hv(struct e1000_hw *hw, u32 offset, u16 *data, + bool locked) { s32 ret_val; u16 page = BM_PHY_REG_PAGE(offset); u16 reg = BM_PHY_REG_NUM(offset); bool in_slow_mode = false; + if (!locked) { + ret_val = hw->phy.ops.acquire_phy(hw); + if (ret_val) + return ret_val; + } + /* Workaround failure in MDIO access while cable is disconnected */ if ((hw->phy.type == e1000_phy_82577) && !(er32(STATUS) & E1000_STATUS_LU)) { @@ -2508,63 +2642,92 @@ s32 e1000_read_phy_reg_hv(struct e1000_hw *hw, u32 offset, u16 *data) goto out; } - ret_val = hw->phy.ops.acquire_phy(hw); - if (ret_val) - goto out; - hw->phy.addr = e1000_get_phy_addr_for_hv_page(page); if (page == HV_INTC_FC_PAGE_START) page = 0; if (reg > MAX_PHY_MULTI_PAGE_REG) { - if ((hw->phy.type != e1000_phy_82578) || - ((reg != I82578_ADDR_REG) && - (reg != I82578_ADDR_REG + 1))) { - u32 phy_addr = hw->phy.addr; - - hw->phy.addr = 1; - - /* Page is shifted left, PHY expects (page x 32) */ - ret_val = e1000e_write_phy_reg_mdic(hw, - IGP01E1000_PHY_PAGE_SELECT, - (page << IGP_PAGE_SHIFT)); - if (ret_val) { - hw->phy.ops.release_phy(hw); - goto out; - } - hw->phy.addr = phy_addr; - } + u32 phy_addr = hw->phy.addr; + + hw->phy.addr = 1; + + /* Page is shifted left, PHY expects (page x 32) */ + ret_val = e1000e_write_phy_reg_mdic(hw, + IGP01E1000_PHY_PAGE_SELECT, + (page << IGP_PAGE_SHIFT)); + hw->phy.addr = phy_addr; + + if (ret_val) + goto out; } ret_val = e1000e_read_phy_reg_mdic(hw, MAX_PHY_REG_ADDRESS & reg, data); - hw->phy.ops.release_phy(hw); - out: /* Revert to MDIO fast mode, if applicable */ if ((hw->phy.type == e1000_phy_82577) && in_slow_mode) - ret_val = e1000_set_mdio_slow_mode_hv(hw, false); + ret_val |= e1000_set_mdio_slow_mode_hv(hw, false); + + if (!locked) + hw->phy.ops.release_phy(hw); return ret_val; } /** - * e1000_write_phy_reg_hv - Write HV PHY register + * e1000_read_phy_reg_hv - Read HV PHY register + * @hw: pointer to the HW structure + * @offset: register offset to be read + * @data: pointer to the read data + * + * Acquires semaphore then reads the PHY register at offset and stores + * the retrieved information in data. Release the acquired semaphore + * before exiting. + **/ +s32 e1000_read_phy_reg_hv(struct e1000_hw *hw, u32 offset, u16 *data) +{ + return __e1000_read_phy_reg_hv(hw, offset, data, false); +} + +/** + * e1000_read_phy_reg_hv_locked - Read HV PHY register + * @hw: pointer to the HW structure + * @offset: register offset to be read + * @data: pointer to the read data + * + * Reads the PHY register at offset and stores the retrieved information + * in data. Assumes semaphore already acquired. + **/ +s32 e1000_read_phy_reg_hv_locked(struct e1000_hw *hw, u32 offset, u16 *data) +{ + return __e1000_read_phy_reg_hv(hw, offset, data, true); +} + +/** + * __e1000_write_phy_reg_hv - Write HV PHY register * @hw: pointer to the HW structure * @offset: register offset to write to * @data: data to write at register offset + * @locked: semaphore has already been acquired or not * * Acquires semaphore, if necessary, then writes the data to PHY register * at the offset. Release any acquired semaphores before exiting. **/ -s32 e1000_write_phy_reg_hv(struct e1000_hw *hw, u32 offset, u16 data) +static s32 __e1000_write_phy_reg_hv(struct e1000_hw *hw, u32 offset, u16 data, + bool locked) { s32 ret_val; u16 page = BM_PHY_REG_PAGE(offset); u16 reg = BM_PHY_REG_NUM(offset); bool in_slow_mode = false; + if (!locked) { + ret_val = hw->phy.ops.acquire_phy(hw); + if (ret_val) + return ret_val; + } + /* Workaround failure in MDIO access while cable is disconnected */ if ((hw->phy.type == e1000_phy_82577) && !(er32(STATUS) & E1000_STATUS_LU)) { @@ -2588,10 +2751,6 @@ s32 e1000_write_phy_reg_hv(struct e1000_hw *hw, u32 offset, u16 data) goto out; } - ret_val = hw->phy.ops.acquire_phy(hw); - if (ret_val) - goto out; - hw->phy.addr = e1000_get_phy_addr_for_hv_page(page); if (page == HV_INTC_FC_PAGE_START) @@ -2607,50 +2766,70 @@ s32 e1000_write_phy_reg_hv(struct e1000_hw *hw, u32 offset, u16 data) ((MAX_PHY_REG_ADDRESS & reg) == 0) && (data & (1 << 11))) { u16 data2 = 0x7EFF; - hw->phy.ops.release_phy(hw); ret_val = e1000_access_phy_debug_regs_hv(hw, (1 << 6) | 0x3, &data2, false); if (ret_val) goto out; - - ret_val = hw->phy.ops.acquire_phy(hw); - if (ret_val) - goto out; } if (reg > MAX_PHY_MULTI_PAGE_REG) { - if ((hw->phy.type != e1000_phy_82578) || - ((reg != I82578_ADDR_REG) && - (reg != I82578_ADDR_REG + 1))) { - u32 phy_addr = hw->phy.addr; - - hw->phy.addr = 1; - - /* Page is shifted left, PHY expects (page x 32) */ - ret_val = e1000e_write_phy_reg_mdic(hw, - IGP01E1000_PHY_PAGE_SELECT, - (page << IGP_PAGE_SHIFT)); - if (ret_val) { - hw->phy.ops.release_phy(hw); - goto out; - } - hw->phy.addr = phy_addr; - } + u32 phy_addr = hw->phy.addr; + + hw->phy.addr = 1; + + /* Page is shifted left, PHY expects (page x 32) */ + ret_val = e1000e_write_phy_reg_mdic(hw, + IGP01E1000_PHY_PAGE_SELECT, + (page << IGP_PAGE_SHIFT)); + hw->phy.addr = phy_addr; + + if (ret_val) + goto out; } ret_val = e1000e_write_phy_reg_mdic(hw, MAX_PHY_REG_ADDRESS & reg, data); - hw->phy.ops.release_phy(hw); out: /* Revert to MDIO fast mode, if applicable */ if ((hw->phy.type == e1000_phy_82577) && in_slow_mode) - ret_val = e1000_set_mdio_slow_mode_hv(hw, false); + ret_val |= e1000_set_mdio_slow_mode_hv(hw, false); + + if (!locked) + hw->phy.ops.release_phy(hw); return ret_val; } /** + * e1000_write_phy_reg_hv - Write HV PHY register + * @hw: pointer to the HW structure + * @offset: register offset to write to + * @data: data to write at register offset + * + * Acquires semaphore then writes the data to PHY register at the offset. + * Release the acquired semaphores before exiting. + **/ +s32 e1000_write_phy_reg_hv(struct e1000_hw *hw, u32 offset, u16 data) +{ + return __e1000_write_phy_reg_hv(hw, offset, data, false); +} + +/** + * e1000_write_phy_reg_hv_locked - Write HV PHY register + * @hw: pointer to the HW structure + * @offset: register offset to write to + * @data: data to write at register offset + * + * Writes the data to PHY register at the offset. Assumes semaphore + * already acquired. + **/ +s32 e1000_write_phy_reg_hv_locked(struct e1000_hw *hw, u32 offset, u16 data) +{ + return __e1000_write_phy_reg_hv(hw, offset, data, true); +} + +/** * e1000_get_phy_addr_for_hv_page - Get PHY adrress based on page * @page: page to be accessed **/ @@ -2671,10 +2850,9 @@ static u32 e1000_get_phy_addr_for_hv_page(u32 page) * @data: pointer to the data to be read or written * @read: determines if operation is read or written * - * Acquires semaphore, if necessary, then reads the PHY register at offset - * and storing the retreived information in data. Release any acquired - * semaphores before exiting. Note that the procedure to read these regs - * uses the address port and data port to read/write. + * Reads the PHY register at offset and stores the retreived information + * in data. Assumes semaphore already acquired. Note that the procedure + * to read these regs uses the address port and data port to read/write. **/ static s32 e1000_access_phy_debug_regs_hv(struct e1000_hw *hw, u32 offset, u16 *data, bool read) @@ -2682,20 +2860,12 @@ static s32 e1000_access_phy_debug_regs_hv(struct e1000_hw *hw, u32 offset, s32 ret_val; u32 addr_reg = 0; u32 data_reg = 0; - u8 phy_acquired = 1; /* This takes care of the difference with desktop vs mobile phy */ addr_reg = (hw->phy.type == e1000_phy_82578) ? I82578_ADDR_REG : I82577_ADDR_REG; data_reg = addr_reg + 1; - ret_val = hw->phy.ops.acquire_phy(hw); - if (ret_val) { - hw_dbg(hw, "Could not acquire PHY\n"); - phy_acquired = 0; - goto out; - } - /* All operations in this function are phy address 2 */ hw->phy.addr = 2; @@ -2718,8 +2888,6 @@ static s32 e1000_access_phy_debug_regs_hv(struct e1000_hw *hw, u32 offset, } out: - if (phy_acquired == 1) - hw->phy.ops.release_phy(hw); return ret_val; } diff --git a/drivers/net/ehea/ehea_main.c b/drivers/net/ehea/ehea_main.c index 977c3d358279..41bd7aeafd82 100644 --- a/drivers/net/ehea/ehea_main.c +++ b/drivers/net/ehea/ehea_main.c @@ -3083,7 +3083,6 @@ static const struct net_device_ops ehea_netdev_ops = { .ndo_poll_controller = ehea_netpoll, #endif .ndo_get_stats = ehea_get_stats, - .ndo_change_mtu = eth_change_mtu, .ndo_set_mac_address = ehea_set_mac_addr, .ndo_validate_addr = eth_validate_addr, .ndo_set_multicast_list = ehea_set_multicast_list, diff --git a/drivers/net/ehea/ehea_qmr.c b/drivers/net/ehea/ehea_qmr.c index 3747457f5e69..bc7c5b7abb88 100644 --- a/drivers/net/ehea/ehea_qmr.c +++ b/drivers/net/ehea/ehea_qmr.c @@ -751,7 +751,7 @@ int ehea_create_busmap(void) mutex_lock(&ehea_busmap_mutex); ehea_mr_len = 0; - ret = walk_memory_resource(0, 1ULL << MAX_PHYSMEM_BITS, NULL, + ret = walk_system_ram_range(0, 1ULL << MAX_PHYSMEM_BITS, NULL, ehea_create_busmap_callback); mutex_unlock(&ehea_busmap_mutex); return ret; diff --git a/drivers/net/enc28j60.c b/drivers/net/enc28j60.c index 117fc6c12e34..66813c91a720 100644 --- a/drivers/net/enc28j60.c +++ b/drivers/net/enc28j60.c @@ -1666,3 +1666,4 @@ MODULE_AUTHOR("Claudio Lanconelli <lanconelli.claudio@eptar.com>"); MODULE_LICENSE("GPL"); module_param_named(debug, debug.msg_enable, int, 0); MODULE_PARM_DESC(debug, "Debug verbosity level (0=none, ..., ffff=all)"); +MODULE_ALIAS("spi:" DRV_NAME); diff --git a/drivers/net/eql.c b/drivers/net/eql.c index d4d9a3eda695..f5b96cadeb25 100644 --- a/drivers/net/eql.c +++ b/drivers/net/eql.c @@ -111,6 +111,7 @@ * Sorry, I had to rewrite most of this for 2.5.x -DaveM */ +#include <linux/capability.h> #include <linux/module.h> #include <linux/kernel.h> #include <linux/init.h> diff --git a/drivers/net/ethoc.c b/drivers/net/ethoc.c index b7311bc00258..f7d9ac8324cb 100644 --- a/drivers/net/ethoc.c +++ b/drivers/net/ethoc.c @@ -17,8 +17,13 @@ #include <linux/mii.h> #include <linux/phy.h> #include <linux/platform_device.h> +#include <linux/sched.h> #include <net/ethoc.h> +static int buffer_size = 0x8000; /* 32 KBytes */ +module_param(buffer_size, int, 0); +MODULE_PARM_DESC(buffer_size, "DMA buffer allocation size"); + /* register offsets */ #define MODER 0x00 #define INT_SOURCE 0x04 @@ -167,6 +172,7 @@ * struct ethoc - driver-private device structure * @iobase: pointer to I/O memory region * @membase: pointer to buffer memory region + * @dma_alloc: dma allocated buffer size * @num_tx: number of send buffers * @cur_tx: last send buffer written * @dty_tx: last buffer actually sent @@ -185,6 +191,7 @@ struct ethoc { void __iomem *iobase; void __iomem *membase; + int dma_alloc; unsigned int num_tx; unsigned int cur_tx; @@ -216,24 +223,25 @@ struct ethoc_bd { u32 addr; }; -static u32 ethoc_read(struct ethoc *dev, loff_t offset) +static inline u32 ethoc_read(struct ethoc *dev, loff_t offset) { return ioread32(dev->iobase + offset); } -static void ethoc_write(struct ethoc *dev, loff_t offset, u32 data) +static inline void ethoc_write(struct ethoc *dev, loff_t offset, u32 data) { iowrite32(data, dev->iobase + offset); } -static void ethoc_read_bd(struct ethoc *dev, int index, struct ethoc_bd *bd) +static inline void ethoc_read_bd(struct ethoc *dev, int index, + struct ethoc_bd *bd) { loff_t offset = ETHOC_BD_BASE + (index * sizeof(struct ethoc_bd)); bd->stat = ethoc_read(dev, offset + 0); bd->addr = ethoc_read(dev, offset + 4); } -static void ethoc_write_bd(struct ethoc *dev, int index, +static inline void ethoc_write_bd(struct ethoc *dev, int index, const struct ethoc_bd *bd) { loff_t offset = ETHOC_BD_BASE + (index * sizeof(struct ethoc_bd)); @@ -241,33 +249,33 @@ static void ethoc_write_bd(struct ethoc *dev, int index, ethoc_write(dev, offset + 4, bd->addr); } -static void ethoc_enable_irq(struct ethoc *dev, u32 mask) +static inline void ethoc_enable_irq(struct ethoc *dev, u32 mask) { u32 imask = ethoc_read(dev, INT_MASK); imask |= mask; ethoc_write(dev, INT_MASK, imask); } -static void ethoc_disable_irq(struct ethoc *dev, u32 mask) +static inline void ethoc_disable_irq(struct ethoc *dev, u32 mask) { u32 imask = ethoc_read(dev, INT_MASK); imask &= ~mask; ethoc_write(dev, INT_MASK, imask); } -static void ethoc_ack_irq(struct ethoc *dev, u32 mask) +static inline void ethoc_ack_irq(struct ethoc *dev, u32 mask) { ethoc_write(dev, INT_SOURCE, mask); } -static void ethoc_enable_rx_and_tx(struct ethoc *dev) +static inline void ethoc_enable_rx_and_tx(struct ethoc *dev) { u32 mode = ethoc_read(dev, MODER); mode |= MODER_RXEN | MODER_TXEN; ethoc_write(dev, MODER, mode); } -static void ethoc_disable_rx_and_tx(struct ethoc *dev) +static inline void ethoc_disable_rx_and_tx(struct ethoc *dev) { u32 mode = ethoc_read(dev, MODER); mode &= ~(MODER_RXEN | MODER_TXEN); @@ -284,7 +292,7 @@ static int ethoc_init_ring(struct ethoc *dev) dev->cur_rx = 0; /* setup transmission buffers */ - bd.addr = 0; + bd.addr = virt_to_phys(dev->membase); bd.stat = TX_BD_IRQ | TX_BD_CRC; for (i = 0; i < dev->num_tx; i++) { @@ -295,7 +303,6 @@ static int ethoc_init_ring(struct ethoc *dev) bd.addr += ETHOC_BUFSIZ; } - bd.addr = dev->num_tx * ETHOC_BUFSIZ; bd.stat = RX_BD_EMPTY | RX_BD_IRQ; for (i = 0; i < dev->num_rx; i++) { @@ -400,8 +407,12 @@ static int ethoc_rx(struct net_device *dev, int limit) if (ethoc_update_rx_stats(priv, &bd) == 0) { int size = bd.stat >> 16; struct sk_buff *skb = netdev_alloc_skb(dev, size); + + size -= 4; /* strip the CRC */ + skb_reserve(skb, 2); /* align TCP/IP header */ + if (likely(skb)) { - void *src = priv->membase + bd.addr; + void *src = phys_to_virt(bd.addr); memcpy_fromio(skb_put(skb, size), src, size); skb->protocol = eth_type_trans(skb, dev); priv->stats.rx_packets++; @@ -498,7 +509,7 @@ static irqreturn_t ethoc_interrupt(int irq, void *dev_id) return IRQ_NONE; } - ethoc_ack_irq(priv, INT_MASK_ALL); + ethoc_ack_irq(priv, pending); if (pending & INT_MASK_BUSY) { dev_err(&dev->dev, "packet dropped\n"); @@ -653,9 +664,10 @@ static int ethoc_open(struct net_device *dev) if (ret) return ret; - /* calculate the number of TX/RX buffers */ - num_bd = (dev->mem_end - dev->mem_start + 1) / ETHOC_BUFSIZ; - priv->num_tx = min(min_tx, num_bd / 4); + /* calculate the number of TX/RX buffers, maximum 128 supported */ + num_bd = min_t(unsigned int, + 128, (dev->mem_end - dev->mem_start + 1) / ETHOC_BUFSIZ); + priv->num_tx = max(min_tx, num_bd / 4); priv->num_rx = num_bd - priv->num_tx; ethoc_write(priv, TX_BD_NUM, priv->num_tx); @@ -823,7 +835,7 @@ static netdev_tx_t ethoc_start_xmit(struct sk_buff *skb, struct net_device *dev) else bd.stat &= ~TX_BD_PAD; - dest = priv->membase + bd.addr; + dest = phys_to_virt(bd.addr); memcpy_toio(dest, skb->data, skb->len); bd.stat &= ~(TX_BD_STATS | TX_BD_LEN_MASK); @@ -903,22 +915,19 @@ static int ethoc_probe(struct platform_device *pdev) /* obtain buffer memory space */ res = platform_get_resource(pdev, IORESOURCE_MEM, 1); - if (!res) { - dev_err(&pdev->dev, "cannot obtain memory space\n"); - ret = -ENXIO; - goto free; - } - - mem = devm_request_mem_region(&pdev->dev, res->start, + if (res) { + mem = devm_request_mem_region(&pdev->dev, res->start, res->end - res->start + 1, res->name); - if (!mem) { - dev_err(&pdev->dev, "cannot request memory space\n"); - ret = -ENXIO; - goto free; + if (!mem) { + dev_err(&pdev->dev, "cannot request memory space\n"); + ret = -ENXIO; + goto free; + } + + netdev->mem_start = mem->start; + netdev->mem_end = mem->end; } - netdev->mem_start = mem->start; - netdev->mem_end = mem->end; /* obtain device IRQ number */ res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); @@ -933,6 +942,7 @@ static int ethoc_probe(struct platform_device *pdev) /* setup driver-private data */ priv = netdev_priv(netdev); priv->netdev = netdev; + priv->dma_alloc = 0; priv->iobase = devm_ioremap_nocache(&pdev->dev, netdev->base_addr, mmio->end - mmio->start + 1); @@ -942,12 +952,27 @@ static int ethoc_probe(struct platform_device *pdev) goto error; } - priv->membase = devm_ioremap_nocache(&pdev->dev, netdev->mem_start, - mem->end - mem->start + 1); - if (!priv->membase) { - dev_err(&pdev->dev, "cannot remap memory space\n"); - ret = -ENXIO; - goto error; + if (netdev->mem_end) { + priv->membase = devm_ioremap_nocache(&pdev->dev, + netdev->mem_start, mem->end - mem->start + 1); + if (!priv->membase) { + dev_err(&pdev->dev, "cannot remap memory space\n"); + ret = -ENXIO; + goto error; + } + } else { + /* Allocate buffer memory */ + priv->membase = dma_alloc_coherent(NULL, + buffer_size, (void *)&netdev->mem_start, + GFP_KERNEL); + if (!priv->membase) { + dev_err(&pdev->dev, "cannot allocate %dB buffer\n", + buffer_size); + ret = -ENOMEM; + goto error; + } + netdev->mem_end = netdev->mem_start + buffer_size; + priv->dma_alloc = buffer_size; } /* Allow the platform setup code to pass in a MAC address. */ @@ -1034,6 +1059,9 @@ free_mdio: kfree(priv->mdio->irq); mdiobus_free(priv->mdio); free: + if (priv->dma_alloc) + dma_free_coherent(NULL, priv->dma_alloc, priv->membase, + netdev->mem_start); free_netdev(netdev); out: return ret; @@ -1059,7 +1087,9 @@ static int ethoc_remove(struct platform_device *pdev) kfree(priv->mdio->irq); mdiobus_free(priv->mdio); } - + if (priv->dma_alloc) + dma_free_coherent(NULL, priv->dma_alloc, priv->membase, + netdev->mem_start); unregister_netdev(netdev); free_netdev(netdev); } diff --git a/drivers/net/ewrk3.c b/drivers/net/ewrk3.c index b2a5ec8f3721..dd4ba01fd92d 100644 --- a/drivers/net/ewrk3.c +++ b/drivers/net/ewrk3.c @@ -145,6 +145,7 @@ #include <linux/module.h> #include <linux/kernel.h> +#include <linux/sched.h> #include <linux/string.h> #include <linux/errno.h> #include <linux/ioport.h> diff --git a/drivers/net/fec.c b/drivers/net/fec.c index 29234380e6c6..16a1d58419d9 100644 --- a/drivers/net/fec.c +++ b/drivers/net/fec.c @@ -1654,7 +1654,7 @@ static const struct net_device_ops fec_netdev_ops = { * * index is only used in legacy code */ -int __init fec_enet_init(struct net_device *dev, int index) +static int fec_enet_init(struct net_device *dev, int index) { struct fec_enet_private *fep = netdev_priv(dev); struct bufdesc *cbd_base; diff --git a/drivers/net/fec_mpc52xx.c b/drivers/net/fec_mpc52xx.c index c40113f58963..66dace6d324f 100644 --- a/drivers/net/fec_mpc52xx.c +++ b/drivers/net/fec_mpc52xx.c @@ -759,12 +759,6 @@ static void mpc52xx_fec_reset(struct net_device *dev) mpc52xx_fec_hw_init(dev); - if (priv->phydev) { - phy_stop(priv->phydev); - phy_write(priv->phydev, MII_BMCR, BMCR_RESET); - phy_start(priv->phydev); - } - bcom_fec_rx_reset(priv->rx_dmatsk); bcom_fec_tx_reset(priv->tx_dmatsk); diff --git a/drivers/net/fec_mpc52xx_phy.c b/drivers/net/fec_mpc52xx_phy.c index 31e6d62b785d..ee0f3c6d3f88 100644 --- a/drivers/net/fec_mpc52xx_phy.c +++ b/drivers/net/fec_mpc52xx_phy.c @@ -155,6 +155,7 @@ static struct of_device_id mpc52xx_fec_mdio_match[] = { { .compatible = "mpc5200b-fec-phy", }, {} }; +MODULE_DEVICE_TABLE(of, mpc52xx_fec_mdio_match); struct of_platform_driver mpc52xx_fec_mdio_driver = { .name = "mpc5200b-fec-phy", diff --git a/drivers/net/forcedeth.c b/drivers/net/forcedeth.c index 0a1c2bb27d4d..3116601dbfea 100644 --- a/drivers/net/forcedeth.c +++ b/drivers/net/forcedeth.c @@ -49,6 +49,7 @@ #include <linux/netdevice.h> #include <linux/etherdevice.h> #include <linux/delay.h> +#include <linux/sched.h> #include <linux/spinlock.h> #include <linux/ethtool.h> #include <linux/timer.h> @@ -5820,10 +5821,7 @@ static int __devinit nv_probe(struct pci_dev *pci_dev, const struct pci_device_i dev->dev_addr); dev_printk(KERN_ERR, &pci_dev->dev, "Please complain to your hardware vendor. Switching to a random MAC.\n"); - dev->dev_addr[0] = 0x00; - dev->dev_addr[1] = 0x00; - dev->dev_addr[2] = 0x6c; - get_random_bytes(&dev->dev_addr[3], 3); + random_ether_addr(dev->dev_addr); } dprintk(KERN_DEBUG "%s: MAC Address %pM\n", diff --git a/drivers/net/fs_enet/fs_enet-main.c b/drivers/net/fs_enet/fs_enet-main.c index 2bc2d2b20644..ec2f5034457f 100644 --- a/drivers/net/fs_enet/fs_enet-main.c +++ b/drivers/net/fs_enet/fs_enet-main.c @@ -1110,6 +1110,7 @@ static struct of_device_id fs_enet_match[] = { #endif {} }; +MODULE_DEVICE_TABLE(of, fs_enet_match); static struct of_platform_driver fs_enet_driver = { .name = "fs_enet", diff --git a/drivers/net/fs_enet/mii-bitbang.c b/drivers/net/fs_enet/mii-bitbang.c index 93b481b0e3c7..24ff9f43a62b 100644 --- a/drivers/net/fs_enet/mii-bitbang.c +++ b/drivers/net/fs_enet/mii-bitbang.c @@ -221,6 +221,7 @@ static struct of_device_id fs_enet_mdio_bb_match[] = { }, {}, }; +MODULE_DEVICE_TABLE(of, fs_enet_mdio_bb_match); static struct of_platform_driver fs_enet_bb_mdio_driver = { .name = "fsl-bb-mdio", diff --git a/drivers/net/fs_enet/mii-fec.c b/drivers/net/fs_enet/mii-fec.c index a2d69c1cd07e..96eba4280c5c 100644 --- a/drivers/net/fs_enet/mii-fec.c +++ b/drivers/net/fs_enet/mii-fec.c @@ -219,6 +219,7 @@ static struct of_device_id fs_enet_mdio_fec_match[] = { #endif {}, }; +MODULE_DEVICE_TABLE(of, fs_enet_mdio_fec_match); static struct of_platform_driver fs_enet_fec_mdio_driver = { .name = "fsl-fec-mdio", diff --git a/drivers/net/fsl_pq_mdio.c b/drivers/net/fsl_pq_mdio.c index d167090248e2..efbf67689eca 100644 --- a/drivers/net/fsl_pq_mdio.c +++ b/drivers/net/fsl_pq_mdio.c @@ -407,6 +407,7 @@ static struct of_device_id fsl_pq_mdio_match[] = { }, {}, }; +MODULE_DEVICE_TABLE(of, fsl_pq_mdio_match); static struct of_platform_driver fsl_pq_mdio_driver = { .name = "fsl-pq_mdio", @@ -426,3 +427,4 @@ void fsl_pq_mdio_exit(void) of_unregister_platform_driver(&fsl_pq_mdio_driver); } module_exit(fsl_pq_mdio_exit); +MODULE_LICENSE("GPL"); diff --git a/drivers/net/gianfar.c b/drivers/net/gianfar.c index 1e5289ffef6f..5bf31f1509c9 100644 --- a/drivers/net/gianfar.c +++ b/drivers/net/gianfar.c @@ -2325,9 +2325,6 @@ static irqreturn_t gfar_error(int irq, void *dev_id) return IRQ_HANDLED; } -/* work with hotplug and coldplug */ -MODULE_ALIAS("platform:fsl-gianfar"); - static struct of_device_id gfar_match[] = { { @@ -2336,6 +2333,7 @@ static struct of_device_id gfar_match[] = }, {}, }; +MODULE_DEVICE_TABLE(of, gfar_match); /* Structure for a device driver */ static struct of_platform_driver gfar_driver = { diff --git a/drivers/net/hamachi.c b/drivers/net/hamachi.c index 1d5064a09aca..f7519a594945 100644 --- a/drivers/net/hamachi.c +++ b/drivers/net/hamachi.c @@ -145,6 +145,7 @@ static int tx_params[MAX_UNITS] = {-1, -1, -1, -1, -1, -1, -1, -1}; /* Time in jiffies before concluding the transmitter is hung. */ #define TX_TIMEOUT (5*HZ) +#include <linux/capability.h> #include <linux/module.h> #include <linux/kernel.h> #include <linux/string.h> diff --git a/drivers/net/hamradio/baycom_epp.c b/drivers/net/hamradio/baycom_epp.c index 7bcaf7c66243..e344c84c0ef9 100644 --- a/drivers/net/hamradio/baycom_epp.c +++ b/drivers/net/hamradio/baycom_epp.c @@ -44,6 +44,7 @@ #include <linux/module.h> #include <linux/kernel.h> #include <linux/init.h> +#include <linux/sched.h> #include <linux/string.h> #include <linux/workqueue.h> #include <linux/fs.h> diff --git a/drivers/net/hamradio/baycom_ser_fdx.c b/drivers/net/hamradio/baycom_ser_fdx.c index aa4488e871b2..ed60fd664273 100644 --- a/drivers/net/hamradio/baycom_ser_fdx.c +++ b/drivers/net/hamradio/baycom_ser_fdx.c @@ -71,6 +71,7 @@ /*****************************************************************************/ +#include <linux/capability.h> #include <linux/module.h> #include <linux/ioport.h> #include <linux/string.h> diff --git a/drivers/net/hamradio/baycom_ser_hdx.c b/drivers/net/hamradio/baycom_ser_hdx.c index 88c593596020..1686f6dcbbce 100644 --- a/drivers/net/hamradio/baycom_ser_hdx.c +++ b/drivers/net/hamradio/baycom_ser_hdx.c @@ -61,6 +61,7 @@ /*****************************************************************************/ +#include <linux/capability.h> #include <linux/module.h> #include <linux/ioport.h> #include <linux/string.h> diff --git a/drivers/net/hamradio/hdlcdrv.c b/drivers/net/hamradio/hdlcdrv.c index 0013c409782c..91c5790c9581 100644 --- a/drivers/net/hamradio/hdlcdrv.c +++ b/drivers/net/hamradio/hdlcdrv.c @@ -42,6 +42,7 @@ /*****************************************************************************/ +#include <linux/capability.h> #include <linux/module.h> #include <linux/types.h> #include <linux/net.h> diff --git a/drivers/net/hamradio/mkiss.c b/drivers/net/hamradio/mkiss.c index 33b55f729742..db4b7f1603f6 100644 --- a/drivers/net/hamradio/mkiss.c +++ b/drivers/net/hamradio/mkiss.c @@ -258,7 +258,7 @@ static void ax_bump(struct mkiss *ax) } if (ax->crcmode != CRC_MODE_SMACK && ax->crcauto) { printk(KERN_INFO - "mkiss: %s: Switchting to crc-smack\n", + "mkiss: %s: Switching to crc-smack\n", ax->dev->name); ax->crcmode = CRC_MODE_SMACK; } @@ -272,7 +272,7 @@ static void ax_bump(struct mkiss *ax) } if (ax->crcmode != CRC_MODE_FLEX && ax->crcauto) { printk(KERN_INFO - "mkiss: %s: Switchting to crc-flexnet\n", + "mkiss: %s: Switching to crc-flexnet\n", ax->dev->name); ax->crcmode = CRC_MODE_FLEX; } diff --git a/drivers/net/hp100.c b/drivers/net/hp100.c index a9a1a99f02dd..dd8665138062 100644 --- a/drivers/net/hp100.c +++ b/drivers/net/hp100.c @@ -98,6 +98,7 @@ #include <linux/module.h> #include <linux/kernel.h> +#include <linux/sched.h> #include <linux/string.h> #include <linux/errno.h> #include <linux/ioport.h> diff --git a/drivers/net/ibm_newemac/core.c b/drivers/net/ibm_newemac/core.c index 89c82c5e63e4..3fae87559791 100644 --- a/drivers/net/ibm_newemac/core.c +++ b/drivers/net/ibm_newemac/core.c @@ -24,6 +24,7 @@ * */ +#include <linux/module.h> #include <linux/sched.h> #include <linux/string.h> #include <linux/errno.h> @@ -443,7 +444,7 @@ static u32 __emac_calc_base_mr1(struct emac_instance *dev, int tx_size, int rx_s ret |= EMAC_MR1_TFS_2K; break; default: - printk(KERN_WARNING "%s: Unknown Rx FIFO size %d\n", + printk(KERN_WARNING "%s: Unknown Tx FIFO size %d\n", dev->ndev->name, tx_size); } @@ -470,6 +471,9 @@ static u32 __emac4_calc_base_mr1(struct emac_instance *dev, int tx_size, int rx_ DBG2(dev, "__emac4_calc_base_mr1" NL); switch(tx_size) { + case 16384: + ret |= EMAC4_MR1_TFS_16K; + break; case 4096: ret |= EMAC4_MR1_TFS_4K; break; @@ -477,7 +481,7 @@ static u32 __emac4_calc_base_mr1(struct emac_instance *dev, int tx_size, int rx_ ret |= EMAC4_MR1_TFS_2K; break; default: - printk(KERN_WARNING "%s: Unknown Rx FIFO size %d\n", + printk(KERN_WARNING "%s: Unknown Tx FIFO size %d\n", dev->ndev->name, tx_size); } @@ -2985,6 +2989,7 @@ static struct of_device_id emac_match[] = }, {}, }; +MODULE_DEVICE_TABLE(of, emac_match); static struct of_platform_driver emac_driver = { .name = "emac", diff --git a/drivers/net/ibm_newemac/emac.h b/drivers/net/ibm_newemac/emac.h index 0afc2cf5c52b..8a61b597a169 100644 --- a/drivers/net/ibm_newemac/emac.h +++ b/drivers/net/ibm_newemac/emac.h @@ -153,6 +153,7 @@ struct emac_regs { #define EMAC4_MR1_RFS_16K 0x00280000 #define EMAC4_MR1_TFS_2K 0x00020000 #define EMAC4_MR1_TFS_4K 0x00030000 +#define EMAC4_MR1_TFS_16K 0x00050000 #define EMAC4_MR1_TR 0x00008000 #define EMAC4_MR1_MWSW_001 0x00001000 #define EMAC4_MR1_JPSM 0x00000800 @@ -262,8 +263,8 @@ struct emac_regs { /* EMACx_TRTR */ -#define EMAC_TRTR_SHIFT_EMAC4 27 -#define EMAC_TRTR_SHIFT 24 +#define EMAC_TRTR_SHIFT_EMAC4 24 +#define EMAC_TRTR_SHIFT 27 /* EMAC specific TX descriptor control fields (write access) */ #define EMAC_TX_CTRL_GFCS 0x0200 diff --git a/drivers/net/ifb.c b/drivers/net/ifb.c index 801f088c134f..030913f8bd26 100644 --- a/drivers/net/ifb.c +++ b/drivers/net/ifb.c @@ -98,12 +98,13 @@ static void ri_tasklet(unsigned long dev) stats->tx_packets++; stats->tx_bytes +=skb->len; - skb->dev = __dev_get_by_index(&init_net, skb->iif); + skb->dev = dev_get_by_index(&init_net, skb->iif); if (!skb->dev) { dev_kfree_skb(skb); stats->tx_dropped++; break; } + dev_put(skb->dev); skb->iif = _dev->ifindex; if (from & AT_EGRESS) { diff --git a/drivers/net/igb/e1000_mac.c b/drivers/net/igb/e1000_mac.c index a0231cd079f1..7d76bb085e10 100644 --- a/drivers/net/igb/e1000_mac.c +++ b/drivers/net/igb/e1000_mac.c @@ -286,41 +286,6 @@ void igb_mta_set(struct e1000_hw *hw, u32 hash_value) } /** - * igb_update_mc_addr_list - Update Multicast addresses - * @hw: pointer to the HW structure - * @mc_addr_list: array of multicast addresses to program - * @mc_addr_count: number of multicast addresses to program - * - * Updates entire Multicast Table Array. - * The caller must have a packed mc_addr_list of multicast addresses. - **/ -void igb_update_mc_addr_list(struct e1000_hw *hw, - u8 *mc_addr_list, u32 mc_addr_count) -{ - u32 hash_value, hash_bit, hash_reg; - int i; - - /* clear mta_shadow */ - memset(&hw->mac.mta_shadow, 0, sizeof(hw->mac.mta_shadow)); - - /* update mta_shadow from mc_addr_list */ - for (i = 0; (u32) i < mc_addr_count; i++) { - hash_value = igb_hash_mc_addr(hw, mc_addr_list); - - hash_reg = (hash_value >> 5) & (hw->mac.mta_reg_count - 1); - hash_bit = hash_value & 0x1F; - - hw->mac.mta_shadow[hash_reg] |= (1 << hash_bit); - mc_addr_list += (ETH_ALEN); - } - - /* replace the entire MTA table */ - for (i = hw->mac.mta_reg_count - 1; i >= 0; i--) - array_wr32(E1000_MTA, i, hw->mac.mta_shadow[i]); - wrfl(); -} - -/** * igb_hash_mc_addr - Generate a multicast hash value * @hw: pointer to the HW structure * @mc_addr: pointer to a multicast address @@ -329,7 +294,7 @@ void igb_update_mc_addr_list(struct e1000_hw *hw, * the multicast filter table array address and new table value. See * igb_mta_set() **/ -u32 igb_hash_mc_addr(struct e1000_hw *hw, u8 *mc_addr) +static u32 igb_hash_mc_addr(struct e1000_hw *hw, u8 *mc_addr) { u32 hash_value, hash_mask; u8 bit_shift = 0; @@ -392,6 +357,41 @@ u32 igb_hash_mc_addr(struct e1000_hw *hw, u8 *mc_addr) } /** + * igb_update_mc_addr_list - Update Multicast addresses + * @hw: pointer to the HW structure + * @mc_addr_list: array of multicast addresses to program + * @mc_addr_count: number of multicast addresses to program + * + * Updates entire Multicast Table Array. + * The caller must have a packed mc_addr_list of multicast addresses. + **/ +void igb_update_mc_addr_list(struct e1000_hw *hw, + u8 *mc_addr_list, u32 mc_addr_count) +{ + u32 hash_value, hash_bit, hash_reg; + int i; + + /* clear mta_shadow */ + memset(&hw->mac.mta_shadow, 0, sizeof(hw->mac.mta_shadow)); + + /* update mta_shadow from mc_addr_list */ + for (i = 0; (u32) i < mc_addr_count; i++) { + hash_value = igb_hash_mc_addr(hw, mc_addr_list); + + hash_reg = (hash_value >> 5) & (hw->mac.mta_reg_count - 1); + hash_bit = hash_value & 0x1F; + + hw->mac.mta_shadow[hash_reg] |= (1 << hash_bit); + mc_addr_list += (ETH_ALEN); + } + + /* replace the entire MTA table */ + for (i = hw->mac.mta_reg_count - 1; i >= 0; i--) + array_wr32(E1000_MTA, i, hw->mac.mta_shadow[i]); + wrfl(); +} + +/** * igb_clear_hw_cntrs_base - Clear base hardware counters * @hw: pointer to the HW structure * diff --git a/drivers/net/igb/e1000_mac.h b/drivers/net/igb/e1000_mac.h index 7518af8cbbf5..bca17d882417 100644 --- a/drivers/net/igb/e1000_mac.h +++ b/drivers/net/igb/e1000_mac.h @@ -88,6 +88,5 @@ enum e1000_mng_mode { #define E1000_MNG_DHCP_COOKIE_STATUS_VLAN 0x2 extern void e1000_init_function_pointers_82575(struct e1000_hw *hw); -extern u32 igb_hash_mc_addr(struct e1000_hw *hw, u8 *mc_addr); #endif diff --git a/drivers/net/igb/igb_ethtool.c b/drivers/net/igb/igb_ethtool.c index d004c359244c..b243ed3b0c36 100644 --- a/drivers/net/igb/igb_ethtool.c +++ b/drivers/net/igb/igb_ethtool.c @@ -34,6 +34,7 @@ #include <linux/interrupt.h> #include <linux/if_ether.h> #include <linux/ethtool.h> +#include <linux/sched.h> #include "igb.h" @@ -731,7 +732,7 @@ static int igb_set_ringparam(struct net_device *netdev, { struct igb_adapter *adapter = netdev_priv(netdev); struct igb_ring *temp_ring; - int i, err; + int i, err = 0; u32 new_rx_count, new_tx_count; if ((ring->rx_mini_pending) || (ring->rx_jumbo_pending)) @@ -751,18 +752,30 @@ static int igb_set_ringparam(struct net_device *netdev, return 0; } + while (test_and_set_bit(__IGB_RESETTING, &adapter->state)) + msleep(1); + + if (!netif_running(adapter->netdev)) { + for (i = 0; i < adapter->num_tx_queues; i++) + adapter->tx_ring[i].count = new_tx_count; + for (i = 0; i < adapter->num_rx_queues; i++) + adapter->rx_ring[i].count = new_rx_count; + adapter->tx_ring_count = new_tx_count; + adapter->rx_ring_count = new_rx_count; + goto clear_reset; + } + if (adapter->num_tx_queues > adapter->num_rx_queues) temp_ring = vmalloc(adapter->num_tx_queues * sizeof(struct igb_ring)); else temp_ring = vmalloc(adapter->num_rx_queues * sizeof(struct igb_ring)); - if (!temp_ring) - return -ENOMEM; - while (test_and_set_bit(__IGB_RESETTING, &adapter->state)) - msleep(1); + if (!temp_ring) { + err = -ENOMEM; + goto clear_reset; + } - if (netif_running(adapter->netdev)) - igb_down(adapter); + igb_down(adapter); /* * We can't just free everything and then setup again, @@ -819,14 +832,11 @@ static int igb_set_ringparam(struct net_device *netdev, adapter->rx_ring_count = new_rx_count; } - - err = 0; err_setup: - if (netif_running(adapter->netdev)) - igb_up(adapter); - - clear_bit(__IGB_RESETTING, &adapter->state); + igb_up(adapter); vfree(temp_ring); +clear_reset: + clear_bit(__IGB_RESETTING, &adapter->state); return err; } diff --git a/drivers/net/igb/igb_main.c b/drivers/net/igb/igb_main.c index 5d6c1530a8c0..714c3a4a44ef 100644 --- a/drivers/net/igb/igb_main.c +++ b/drivers/net/igb/igb_main.c @@ -1246,12 +1246,7 @@ static int __devinit igb_probe(struct pci_dev *pdev, if (err) goto err_pci_reg; - err = pci_enable_pcie_error_reporting(pdev); - if (err) { - dev_err(&pdev->dev, "pci_enable_pcie_error_reporting failed " - "0x%x\n", err); - /* non-fatal, continue */ - } + pci_enable_pcie_error_reporting(pdev); pci_set_master(pdev); pci_save_state(pdev); @@ -1628,7 +1623,6 @@ static void __devexit igb_remove(struct pci_dev *pdev) struct net_device *netdev = pci_get_drvdata(pdev); struct igb_adapter *adapter = netdev_priv(netdev); struct e1000_hw *hw = &adapter->hw; - int err; /* flush_scheduled work may reschedule our watchdog task, so * explicitly disable watchdog tasks from being rescheduled */ @@ -1682,10 +1676,7 @@ static void __devexit igb_remove(struct pci_dev *pdev) free_netdev(netdev); - err = pci_disable_pcie_error_reporting(pdev); - if (err) - dev_err(&pdev->dev, - "pci_disable_pcie_error_reporting failed 0x%x\n", err); + pci_disable_pcie_error_reporting(pdev); pci_disable_device(pdev); } diff --git a/drivers/net/igbvf/ethtool.c b/drivers/net/igbvf/ethtool.c index ee17a097d1ca..c68265bd0d1a 100644 --- a/drivers/net/igbvf/ethtool.c +++ b/drivers/net/igbvf/ethtool.c @@ -279,7 +279,7 @@ static int igbvf_set_ringparam(struct net_device *netdev, { struct igbvf_adapter *adapter = netdev_priv(netdev); struct igbvf_ring *temp_ring; - int err; + int err = 0; u32 new_rx_count, new_tx_count; if ((ring->rx_mini_pending) || (ring->rx_jumbo_pending)) @@ -299,15 +299,22 @@ static int igbvf_set_ringparam(struct net_device *netdev, return 0; } - temp_ring = vmalloc(sizeof(struct igbvf_ring)); - if (!temp_ring) - return -ENOMEM; - while (test_and_set_bit(__IGBVF_RESETTING, &adapter->state)) msleep(1); - if (netif_running(adapter->netdev)) - igbvf_down(adapter); + if (!netif_running(adapter->netdev)) { + adapter->tx_ring->count = new_tx_count; + adapter->rx_ring->count = new_rx_count; + goto clear_reset; + } + + temp_ring = vmalloc(sizeof(struct igbvf_ring)); + if (!temp_ring) { + err = -ENOMEM; + goto clear_reset; + } + + igbvf_down(adapter); /* * We can't just free everything and then setup again, @@ -339,14 +346,11 @@ static int igbvf_set_ringparam(struct net_device *netdev, memcpy(adapter->rx_ring, temp_ring,sizeof(struct igbvf_ring)); } - - err = 0; err_setup: - if (netif_running(adapter->netdev)) - igbvf_up(adapter); - - clear_bit(__IGBVF_RESETTING, &adapter->state); + igbvf_up(adapter); vfree(temp_ring); +clear_reset: + clear_bit(__IGBVF_RESETTING, &adapter->state); return err; } diff --git a/drivers/net/irda/kingsun-sir.c b/drivers/net/irda/kingsun-sir.c index 2fc30b449eea..cb90d640007a 100644 --- a/drivers/net/irda/kingsun-sir.c +++ b/drivers/net/irda/kingsun-sir.c @@ -66,7 +66,6 @@ #include <linux/errno.h> #include <linux/init.h> #include <linux/slab.h> -#include <linux/kref.h> #include <linux/usb.h> #include <linux/device.h> #include <linux/crc32.h> diff --git a/drivers/net/irda/ks959-sir.c b/drivers/net/irda/ks959-sir.c index f4d13fc51cbc..b54d3b48045e 100644 --- a/drivers/net/irda/ks959-sir.c +++ b/drivers/net/irda/ks959-sir.c @@ -118,7 +118,6 @@ #include <linux/errno.h> #include <linux/init.h> #include <linux/slab.h> -#include <linux/kref.h> #include <linux/usb.h> #include <linux/device.h> #include <linux/crc32.h> diff --git a/drivers/net/irda/ksdazzle-sir.c b/drivers/net/irda/ksdazzle-sir.c index 5f9d73353972..8d713ebac15b 100644 --- a/drivers/net/irda/ksdazzle-sir.c +++ b/drivers/net/irda/ksdazzle-sir.c @@ -82,7 +82,6 @@ #include <linux/errno.h> #include <linux/init.h> #include <linux/slab.h> -#include <linux/kref.h> #include <linux/usb.h> #include <linux/device.h> #include <linux/crc32.h> diff --git a/drivers/net/irda/mcs7780.c b/drivers/net/irda/mcs7780.c index b3d30bcb88e7..c0e0bb9401d3 100644 --- a/drivers/net/irda/mcs7780.c +++ b/drivers/net/irda/mcs7780.c @@ -50,7 +50,6 @@ #include <linux/errno.h> #include <linux/init.h> #include <linux/slab.h> -#include <linux/kref.h> #include <linux/usb.h> #include <linux/device.h> #include <linux/crc32.h> diff --git a/drivers/net/irda/pxaficp_ir.c b/drivers/net/irda/pxaficp_ir.c index 1445e5865196..84db145d2b59 100644 --- a/drivers/net/irda/pxaficp_ir.c +++ b/drivers/net/irda/pxaficp_ir.c @@ -17,6 +17,7 @@ #include <linux/etherdevice.h> #include <linux/platform_device.h> #include <linux/clk.h> +#include <linux/gpio.h> #include <net/irda/irda.h> #include <net/irda/irmod.h> @@ -163,6 +164,22 @@ inline static void pxa_irda_fir_dma_tx_start(struct pxa_irda *si) } /* + * Set the IrDA communications mode. + */ +static void pxa_irda_set_mode(struct pxa_irda *si, int mode) +{ + if (si->pdata->transceiver_mode) + si->pdata->transceiver_mode(si->dev, mode); + else { + if (gpio_is_valid(si->pdata->gpio_pwdown)) + gpio_set_value(si->pdata->gpio_pwdown, + !(mode & IR_OFF) ^ + !si->pdata->gpio_pwdown_inverted); + pxa2xx_transceiver_mode(si->dev, mode); + } +} + +/* * Set the IrDA communications speed. */ static int pxa_irda_set_speed(struct pxa_irda *si, int speed) @@ -188,7 +205,7 @@ static int pxa_irda_set_speed(struct pxa_irda *si, int speed) pxa_irda_disable_clk(si); /* set board transceiver to SIR mode */ - si->pdata->transceiver_mode(si->dev, IR_SIRMODE); + pxa_irda_set_mode(si, IR_SIRMODE); /* enable the STUART clock */ pxa_irda_enable_sirclk(si); @@ -222,7 +239,7 @@ static int pxa_irda_set_speed(struct pxa_irda *si, int speed) ICCR0 = 0; /* set board transceiver to FIR mode */ - si->pdata->transceiver_mode(si->dev, IR_FIRMODE); + pxa_irda_set_mode(si, IR_FIRMODE); /* enable the FICP clock */ pxa_irda_enable_firclk(si); @@ -641,7 +658,7 @@ static void pxa_irda_shutdown(struct pxa_irda *si) local_irq_restore(flags); /* power off board transceiver */ - si->pdata->transceiver_mode(si->dev, IR_OFF); + pxa_irda_set_mode(si, IR_OFF); printk(KERN_DEBUG "pxa_ir: irda shutdown\n"); } @@ -849,10 +866,26 @@ static int pxa_irda_probe(struct platform_device *pdev) if (err) goto err_mem_5; - if (si->pdata->startup) + if (gpio_is_valid(si->pdata->gpio_pwdown)) { + err = gpio_request(si->pdata->gpio_pwdown, "IrDA switch"); + if (err) + goto err_startup; + err = gpio_direction_output(si->pdata->gpio_pwdown, + !si->pdata->gpio_pwdown_inverted); + if (err) { + gpio_free(si->pdata->gpio_pwdown); + goto err_startup; + } + } + + if (si->pdata->startup) { err = si->pdata->startup(si->dev); - if (err) - goto err_startup; + if (err) + goto err_startup; + } + + if (gpio_is_valid(si->pdata->gpio_pwdown) && si->pdata->startup) + dev_warn(si->dev, "gpio_pwdown and startup() both defined!\n"); dev->netdev_ops = &pxa_irda_netdev_ops; @@ -903,6 +936,8 @@ static int pxa_irda_remove(struct platform_device *_dev) if (dev) { struct pxa_irda *si = netdev_priv(dev); unregister_netdev(dev); + if (gpio_is_valid(si->pdata->gpio_pwdown)) + gpio_free(si->pdata->gpio_pwdown); if (si->pdata->shutdown) si->pdata->shutdown(si->dev); kfree(si->tx_buff.head); diff --git a/drivers/net/irda/sa1100_ir.c b/drivers/net/irda/sa1100_ir.c index 38bf7cf2256d..c412e8026173 100644 --- a/drivers/net/irda/sa1100_ir.c +++ b/drivers/net/irda/sa1100_ir.c @@ -232,8 +232,11 @@ static int sa1100_irda_startup(struct sa1100_irda *si) /* * Ensure that the ports for this device are setup correctly. */ - if (si->pdata->startup) - si->pdata->startup(si->dev); + if (si->pdata->startup) { + ret = si->pdata->startup(si->dev); + if (ret) + return ret; + } /* * Configure PPC for IRDA - we want to drive TXD2 low. diff --git a/drivers/net/irda/toim3232-sir.c b/drivers/net/irda/toim3232-sir.c index fcf287b749db..99e1ec02a011 100644 --- a/drivers/net/irda/toim3232-sir.c +++ b/drivers/net/irda/toim3232-sir.c @@ -120,6 +120,7 @@ #include <linux/module.h> #include <linux/delay.h> #include <linux/init.h> +#include <linux/sched.h> #include <net/irda/irda.h> diff --git a/drivers/net/iseries_veth.c b/drivers/net/iseries_veth.c index e36e951cbc65..aa7286bc4364 100644 --- a/drivers/net/iseries_veth.c +++ b/drivers/net/iseries_veth.c @@ -495,7 +495,7 @@ static void veth_take_cap_ack(struct veth_lpar_connection *cnx, cnx->remote_lp); } else { memcpy(&cnx->cap_ack_event, event, - sizeof(&cnx->cap_ack_event)); + sizeof(cnx->cap_ack_event)); cnx->state |= VETH_STATE_GOTCAPACK; veth_kick_statemachine(cnx); } diff --git a/drivers/net/ixgbe/ixgbe.h b/drivers/net/ixgbe/ixgbe.h index dd688d45e9cd..385be6016667 100644 --- a/drivers/net/ixgbe/ixgbe.h +++ b/drivers/net/ixgbe/ixgbe.h @@ -267,7 +267,8 @@ struct ixgbe_adapter { enum ixgbe_fc_mode last_lfc_mode; /* Interrupt Throttle Rate */ - u32 itr_setting; + u32 rx_itr_setting; + u32 tx_itr_setting; u16 eitr_low; u16 eitr_high; @@ -351,7 +352,8 @@ struct ixgbe_adapter { struct ixgbe_hw_stats stats; /* Interrupt Throttle Rate */ - u32 eitr_param; + u32 rx_eitr_param; + u32 tx_eitr_param; unsigned long state; u64 tx_busy; diff --git a/drivers/net/ixgbe/ixgbe_82598.c b/drivers/net/ixgbe/ixgbe_82598.c index 56b12f3192f1..e2d5343f1275 100644 --- a/drivers/net/ixgbe/ixgbe_82598.c +++ b/drivers/net/ixgbe/ixgbe_82598.c @@ -425,7 +425,7 @@ static s32 ixgbe_fc_enable_82598(struct ixgbe_hw *hw, s32 packetbuf_num) #endif /* CONFIG_DCB */ default: hw_dbg(hw, "Flow control param set incorrectly\n"); - ret_val = -IXGBE_ERR_CONFIG; + ret_val = IXGBE_ERR_CONFIG; goto out; break; } diff --git a/drivers/net/ixgbe/ixgbe_82599.c b/drivers/net/ixgbe/ixgbe_82599.c index 2ec58dcdb82b..34b04924c8a1 100644 --- a/drivers/net/ixgbe/ixgbe_82599.c +++ b/drivers/net/ixgbe/ixgbe_82599.c @@ -330,6 +330,8 @@ static enum ixgbe_media_type ixgbe_get_media_type_82599(struct ixgbe_hw *hw) switch (hw->device_id) { case IXGBE_DEV_ID_82599_KX4: + case IXGBE_DEV_ID_82599_KX4_MEZZ: + case IXGBE_DEV_ID_82599_COMBO_BACKPLANE: case IXGBE_DEV_ID_82599_XAUI_LOM: /* Default device ID is mezzanine card KX/KX4 */ media_type = ixgbe_media_type_backplane; diff --git a/drivers/net/ixgbe/ixgbe_common.c b/drivers/net/ixgbe/ixgbe_common.c index 6621e172df3d..40ff120a9ad4 100644 --- a/drivers/net/ixgbe/ixgbe_common.c +++ b/drivers/net/ixgbe/ixgbe_common.c @@ -1355,9 +1355,7 @@ static void ixgbe_add_uc_addr(struct ixgbe_hw *hw, u8 *addr, u32 vmdq) /** * ixgbe_update_uc_addr_list_generic - Updates MAC list of secondary addresses * @hw: pointer to hardware structure - * @addr_list: the list of new addresses - * @addr_count: number of addresses - * @next: iterator function to walk the address list + * @uc_list: the list of new addresses * * The given list replaces any existing list. Clears the secondary addrs from * receive address registers. Uses unused receive address registers for the @@ -1663,7 +1661,7 @@ s32 ixgbe_fc_enable_generic(struct ixgbe_hw *hw, s32 packetbuf_num) #endif /* CONFIG_DCB */ default: hw_dbg(hw, "Flow control param set incorrectly\n"); - ret_val = -IXGBE_ERR_CONFIG; + ret_val = IXGBE_ERR_CONFIG; goto out; break; } @@ -1734,75 +1732,140 @@ s32 ixgbe_fc_autoneg(struct ixgbe_hw *hw) s32 ret_val = 0; ixgbe_link_speed speed; u32 pcs_anadv_reg, pcs_lpab_reg, linkstat; + u32 links2, anlp1_reg, autoc_reg, links; bool link_up; /* * AN should have completed when the cable was plugged in. * Look for reasons to bail out. Bail out if: * - FC autoneg is disabled, or if - * - we don't have multispeed fiber, or if - * - we're not running at 1G, or if - * - link is not up, or if - * - link is up but AN did not complete, or if - * - link is up and AN completed but timed out + * - link is not up. * - * Since we're being called from an LSC, link is already know to be up. + * Since we're being called from an LSC, link is already known to be up. * So use link_up_wait_to_complete=false. */ hw->mac.ops.check_link(hw, &speed, &link_up, false); - linkstat = IXGBE_READ_REG(hw, IXGBE_PCS1GLSTA); - - if (hw->fc.disable_fc_autoneg || - !hw->phy.multispeed_fiber || - (speed != IXGBE_LINK_SPEED_1GB_FULL) || - !link_up || - ((linkstat & IXGBE_PCS1GLSTA_AN_COMPLETE) == 0) || - ((linkstat & IXGBE_PCS1GLSTA_AN_TIMED_OUT) == 1)) { + + if (hw->fc.disable_fc_autoneg || (!link_up)) { hw->fc.fc_was_autonegged = false; hw->fc.current_mode = hw->fc.requested_mode; - hw_dbg(hw, "Autoneg FC was skipped.\n"); goto out; } /* + * On backplane, bail out if + * - backplane autoneg was not completed, or if + * - link partner is not AN enabled + */ + if (hw->phy.media_type == ixgbe_media_type_backplane) { + links = IXGBE_READ_REG(hw, IXGBE_LINKS); + links2 = IXGBE_READ_REG(hw, IXGBE_LINKS2); + if (((links & IXGBE_LINKS_KX_AN_COMP) == 0) || + ((links2 & IXGBE_LINKS2_AN_SUPPORTED) == 0)) { + hw->fc.fc_was_autonegged = false; + hw->fc.current_mode = hw->fc.requested_mode; + goto out; + } + } + + /* + * On multispeed fiber at 1g, bail out if + * - link is up but AN did not complete, or if + * - link is up and AN completed but timed out + */ + if (hw->phy.multispeed_fiber && (speed == IXGBE_LINK_SPEED_1GB_FULL)) { + linkstat = IXGBE_READ_REG(hw, IXGBE_PCS1GLSTA); + if (((linkstat & IXGBE_PCS1GLSTA_AN_COMPLETE) == 0) || + ((linkstat & IXGBE_PCS1GLSTA_AN_TIMED_OUT) == 1)) { + hw->fc.fc_was_autonegged = false; + hw->fc.current_mode = hw->fc.requested_mode; + goto out; + } + } + + /* * Read the AN advertisement and LP ability registers and resolve * local flow control settings accordingly */ - pcs_anadv_reg = IXGBE_READ_REG(hw, IXGBE_PCS1GANA); - pcs_lpab_reg = IXGBE_READ_REG(hw, IXGBE_PCS1GANLP); - if ((pcs_anadv_reg & IXGBE_PCS1GANA_SYM_PAUSE) && - (pcs_lpab_reg & IXGBE_PCS1GANA_SYM_PAUSE)) { + if ((speed == IXGBE_LINK_SPEED_1GB_FULL) && + (hw->phy.media_type != ixgbe_media_type_backplane)) { + pcs_anadv_reg = IXGBE_READ_REG(hw, IXGBE_PCS1GANA); + pcs_lpab_reg = IXGBE_READ_REG(hw, IXGBE_PCS1GANLP); + if ((pcs_anadv_reg & IXGBE_PCS1GANA_SYM_PAUSE) && + (pcs_lpab_reg & IXGBE_PCS1GANA_SYM_PAUSE)) { + /* + * Now we need to check if the user selected Rx ONLY + * of pause frames. In this case, we had to advertise + * FULL flow control because we could not advertise RX + * ONLY. Hence, we must now check to see if we need to + * turn OFF the TRANSMISSION of PAUSE frames. + */ + if (hw->fc.requested_mode == ixgbe_fc_full) { + hw->fc.current_mode = ixgbe_fc_full; + hw_dbg(hw, "Flow Control = FULL.\n"); + } else { + hw->fc.current_mode = ixgbe_fc_rx_pause; + hw_dbg(hw, "Flow Control=RX PAUSE only\n"); + } + } else if (!(pcs_anadv_reg & IXGBE_PCS1GANA_SYM_PAUSE) && + (pcs_anadv_reg & IXGBE_PCS1GANA_ASM_PAUSE) && + (pcs_lpab_reg & IXGBE_PCS1GANA_SYM_PAUSE) && + (pcs_lpab_reg & IXGBE_PCS1GANA_ASM_PAUSE)) { + hw->fc.current_mode = ixgbe_fc_tx_pause; + hw_dbg(hw, "Flow Control = TX PAUSE frames only.\n"); + } else if ((pcs_anadv_reg & IXGBE_PCS1GANA_SYM_PAUSE) && + (pcs_anadv_reg & IXGBE_PCS1GANA_ASM_PAUSE) && + !(pcs_lpab_reg & IXGBE_PCS1GANA_SYM_PAUSE) && + (pcs_lpab_reg & IXGBE_PCS1GANA_ASM_PAUSE)) { + hw->fc.current_mode = ixgbe_fc_rx_pause; + hw_dbg(hw, "Flow Control = RX PAUSE frames only.\n"); + } else { + hw->fc.current_mode = ixgbe_fc_none; + hw_dbg(hw, "Flow Control = NONE.\n"); + } + } + + if (hw->phy.media_type == ixgbe_media_type_backplane) { /* - * Now we need to check if the user selected Rx ONLY - * of pause frames. In this case, we had to advertise - * FULL flow control because we could not advertise RX - * ONLY. Hence, we must now check to see if we need to - * turn OFF the TRANSMISSION of PAUSE frames. + * Read the 10g AN autoc and LP ability registers and resolve + * local flow control settings accordingly */ - if (hw->fc.requested_mode == ixgbe_fc_full) { - hw->fc.current_mode = ixgbe_fc_full; - hw_dbg(hw, "Flow Control = FULL.\n"); - } else { + autoc_reg = IXGBE_READ_REG(hw, IXGBE_AUTOC); + anlp1_reg = IXGBE_READ_REG(hw, IXGBE_ANLP1); + + if ((autoc_reg & IXGBE_AUTOC_SYM_PAUSE) && + (anlp1_reg & IXGBE_ANLP1_SYM_PAUSE)) { + /* + * Now we need to check if the user selected Rx ONLY + * of pause frames. In this case, we had to advertise + * FULL flow control because we could not advertise RX + * ONLY. Hence, we must now check to see if we need to + * turn OFF the TRANSMISSION of PAUSE frames. + */ + if (hw->fc.requested_mode == ixgbe_fc_full) { + hw->fc.current_mode = ixgbe_fc_full; + hw_dbg(hw, "Flow Control = FULL.\n"); + } else { + hw->fc.current_mode = ixgbe_fc_rx_pause; + hw_dbg(hw, "Flow Control=RX PAUSE only\n"); + } + } else if (!(autoc_reg & IXGBE_AUTOC_SYM_PAUSE) && + (autoc_reg & IXGBE_AUTOC_ASM_PAUSE) && + (anlp1_reg & IXGBE_ANLP1_SYM_PAUSE) && + (anlp1_reg & IXGBE_ANLP1_ASM_PAUSE)) { + hw->fc.current_mode = ixgbe_fc_tx_pause; + hw_dbg(hw, "Flow Control = TX PAUSE frames only.\n"); + } else if ((autoc_reg & IXGBE_AUTOC_SYM_PAUSE) && + (autoc_reg & IXGBE_AUTOC_ASM_PAUSE) && + !(anlp1_reg & IXGBE_ANLP1_SYM_PAUSE) && + (anlp1_reg & IXGBE_ANLP1_ASM_PAUSE)) { hw->fc.current_mode = ixgbe_fc_rx_pause; hw_dbg(hw, "Flow Control = RX PAUSE frames only.\n"); + } else { + hw->fc.current_mode = ixgbe_fc_none; + hw_dbg(hw, "Flow Control = NONE.\n"); } - } else if (!(pcs_anadv_reg & IXGBE_PCS1GANA_SYM_PAUSE) && - (pcs_anadv_reg & IXGBE_PCS1GANA_ASM_PAUSE) && - (pcs_lpab_reg & IXGBE_PCS1GANA_SYM_PAUSE) && - (pcs_lpab_reg & IXGBE_PCS1GANA_ASM_PAUSE)) { - hw->fc.current_mode = ixgbe_fc_tx_pause; - hw_dbg(hw, "Flow Control = TX PAUSE frames only.\n"); - } else if ((pcs_anadv_reg & IXGBE_PCS1GANA_SYM_PAUSE) && - (pcs_anadv_reg & IXGBE_PCS1GANA_ASM_PAUSE) && - !(pcs_lpab_reg & IXGBE_PCS1GANA_SYM_PAUSE) && - (pcs_lpab_reg & IXGBE_PCS1GANA_ASM_PAUSE)) { - hw->fc.current_mode = ixgbe_fc_rx_pause; - hw_dbg(hw, "Flow Control = RX PAUSE frames only.\n"); - } else { - hw->fc.current_mode = ixgbe_fc_none; - hw_dbg(hw, "Flow Control = NONE.\n"); } - /* Record that current_mode is the result of a successful autoneg */ hw->fc.fc_was_autonegged = true; @@ -1919,7 +1982,7 @@ static s32 ixgbe_setup_fc(struct ixgbe_hw *hw, s32 packetbuf_num) #endif /* CONFIG_DCB */ default: hw_dbg(hw, "Flow control param set incorrectly\n"); - ret_val = -IXGBE_ERR_CONFIG; + ret_val = IXGBE_ERR_CONFIG; goto out; break; } @@ -1927,9 +1990,6 @@ static s32 ixgbe_setup_fc(struct ixgbe_hw *hw, s32 packetbuf_num) IXGBE_WRITE_REG(hw, IXGBE_PCS1GANA, reg); reg = IXGBE_READ_REG(hw, IXGBE_PCS1GLCTL); - /* Enable and restart autoneg to inform the link partner */ - reg |= IXGBE_PCS1GLCTL_AN_ENABLE | IXGBE_PCS1GLCTL_AN_RESTART; - /* Disable AN timeout */ if (hw->fc.strict_ieee) reg &= ~IXGBE_PCS1GLCTL_AN_1G_TIMEOUT_EN; @@ -1937,6 +1997,70 @@ static s32 ixgbe_setup_fc(struct ixgbe_hw *hw, s32 packetbuf_num) IXGBE_WRITE_REG(hw, IXGBE_PCS1GLCTL, reg); hw_dbg(hw, "Set up FC; PCS1GLCTL = 0x%08X\n", reg); + /* + * Set up the 10G flow control advertisement registers so the HW + * can do fc autoneg once the cable is plugged in. If we end up + * using 1g instead, this is harmless. + */ + reg = IXGBE_READ_REG(hw, IXGBE_AUTOC); + + /* + * The possible values of fc.requested_mode are: + * 0: Flow control is completely disabled + * 1: Rx flow control is enabled (we can receive pause frames, + * but not send pause frames). + * 2: Tx flow control is enabled (we can send pause frames but + * we do not support receiving pause frames). + * 3: Both Rx and Tx flow control (symmetric) are enabled. + * other: Invalid. + */ + switch (hw->fc.requested_mode) { + case ixgbe_fc_none: + /* Flow control completely disabled by software override. */ + reg &= ~(IXGBE_AUTOC_SYM_PAUSE | IXGBE_AUTOC_ASM_PAUSE); + break; + case ixgbe_fc_rx_pause: + /* + * Rx Flow control is enabled and Tx Flow control is + * disabled by software override. Since there really + * isn't a way to advertise that we are capable of RX + * Pause ONLY, we will advertise that we support both + * symmetric and asymmetric Rx PAUSE. Later, we will + * disable the adapter's ability to send PAUSE frames. + */ + reg |= (IXGBE_AUTOC_SYM_PAUSE | IXGBE_AUTOC_ASM_PAUSE); + break; + case ixgbe_fc_tx_pause: + /* + * Tx Flow control is enabled, and Rx Flow control is + * disabled by software override. + */ + reg |= (IXGBE_AUTOC_ASM_PAUSE); + reg &= ~(IXGBE_AUTOC_SYM_PAUSE); + break; + case ixgbe_fc_full: + /* Flow control (both Rx and Tx) is enabled by SW override. */ + reg |= (IXGBE_AUTOC_SYM_PAUSE | IXGBE_AUTOC_ASM_PAUSE); + break; +#ifdef CONFIG_DCB + case ixgbe_fc_pfc: + goto out; + break; +#endif /* CONFIG_DCB */ + default: + hw_dbg(hw, "Flow control param set incorrectly\n"); + ret_val = IXGBE_ERR_CONFIG; + goto out; + break; + } + /* + * AUTOC restart handles negotiation of 1G and 10G. There is + * no need to set the PCS1GCTL register. + */ + reg |= IXGBE_AUTOC_AN_RESTART; + IXGBE_WRITE_REG(hw, IXGBE_AUTOC, reg); + hw_dbg(hw, "Set up FC; IXGBE_AUTOC = 0x%08X\n", reg); + out: return ret_val; } @@ -2000,7 +2124,7 @@ s32 ixgbe_acquire_swfw_sync(struct ixgbe_hw *hw, u16 mask) while (timeout) { if (ixgbe_get_eeprom_semaphore(hw)) - return -IXGBE_ERR_SWFW_SYNC; + return IXGBE_ERR_SWFW_SYNC; gssr = IXGBE_READ_REG(hw, IXGBE_GSSR); if (!(gssr & (fwmask | swmask))) @@ -2017,7 +2141,7 @@ s32 ixgbe_acquire_swfw_sync(struct ixgbe_hw *hw, u16 mask) if (!timeout) { hw_dbg(hw, "Driver can't access resource, GSSR timeout.\n"); - return -IXGBE_ERR_SWFW_SYNC; + return IXGBE_ERR_SWFW_SYNC; } gssr |= swmask; diff --git a/drivers/net/ixgbe/ixgbe_ethtool.c b/drivers/net/ixgbe/ixgbe_ethtool.c index 026e94a99849..856c18c207f3 100644 --- a/drivers/net/ixgbe/ixgbe_ethtool.c +++ b/drivers/net/ixgbe/ixgbe_ethtool.c @@ -53,6 +53,10 @@ static struct ixgbe_stats ixgbe_gstrings_stats[] = { {"tx_packets", IXGBE_STAT(net_stats.tx_packets)}, {"rx_bytes", IXGBE_STAT(net_stats.rx_bytes)}, {"tx_bytes", IXGBE_STAT(net_stats.tx_bytes)}, + {"rx_pkts_nic", IXGBE_STAT(stats.gprc)}, + {"tx_pkts_nic", IXGBE_STAT(stats.gptc)}, + {"rx_bytes_nic", IXGBE_STAT(stats.gorc)}, + {"tx_bytes_nic", IXGBE_STAT(stats.gotc)}, {"lsc_int", IXGBE_STAT(lsc_int)}, {"tx_busy", IXGBE_STAT(tx_busy)}, {"non_eop_descs", IXGBE_STAT(non_eop_descs)}, @@ -794,7 +798,7 @@ static int ixgbe_set_ringparam(struct net_device *netdev, { struct ixgbe_adapter *adapter = netdev_priv(netdev); struct ixgbe_ring *temp_tx_ring, *temp_rx_ring; - int i, err; + int i, err = 0; u32 new_rx_count, new_tx_count; bool need_update = false; @@ -818,6 +822,16 @@ static int ixgbe_set_ringparam(struct net_device *netdev, while (test_and_set_bit(__IXGBE_RESETTING, &adapter->state)) msleep(1); + if (!netif_running(adapter->netdev)) { + for (i = 0; i < adapter->num_tx_queues; i++) + adapter->tx_ring[i].count = new_tx_count; + for (i = 0; i < adapter->num_rx_queues; i++) + adapter->rx_ring[i].count = new_rx_count; + adapter->tx_ring_count = new_tx_count; + adapter->rx_ring_count = new_rx_count; + goto err_setup; + } + temp_tx_ring = kcalloc(adapter->num_tx_queues, sizeof(struct ixgbe_ring), GFP_KERNEL); if (!temp_tx_ring) { @@ -875,8 +889,7 @@ static int ixgbe_set_ringparam(struct net_device *netdev, /* if rings need to be updated, here's the place to do it in one shot */ if (need_update) { - if (netif_running(netdev)) - ixgbe_down(adapter); + ixgbe_down(adapter); /* tx */ if (new_tx_count != adapter->tx_ring_count) { @@ -893,13 +906,8 @@ static int ixgbe_set_ringparam(struct net_device *netdev, temp_rx_ring = NULL; adapter->rx_ring_count = new_rx_count; } - } - - /* success! */ - err = 0; - if (netif_running(netdev)) ixgbe_up(adapter); - + } err_setup: clear_bit(__IXGBE_RESETTING, &adapter->state); return err; @@ -1929,7 +1937,7 @@ static int ixgbe_get_coalesce(struct net_device *netdev, ec->tx_max_coalesced_frames_irq = adapter->tx_ring[0].work_limit; /* only valid if in constant ITR mode */ - switch (adapter->itr_setting) { + switch (adapter->rx_itr_setting) { case 0: /* throttling disabled */ ec->rx_coalesce_usecs = 0; @@ -1940,9 +1948,25 @@ static int ixgbe_get_coalesce(struct net_device *netdev, break; default: /* fixed interrupt rate mode */ - ec->rx_coalesce_usecs = 1000000/adapter->eitr_param; + ec->rx_coalesce_usecs = 1000000/adapter->rx_eitr_param; + break; + } + + /* only valid if in constant ITR mode */ + switch (adapter->tx_itr_setting) { + case 0: + /* throttling disabled */ + ec->tx_coalesce_usecs = 0; + break; + case 1: + /* dynamic ITR mode */ + ec->tx_coalesce_usecs = 1; + break; + default: + ec->tx_coalesce_usecs = 1000000/adapter->tx_eitr_param; break; } + return 0; } @@ -1953,6 +1977,14 @@ static int ixgbe_set_coalesce(struct net_device *netdev, struct ixgbe_q_vector *q_vector; int i; + /* + * don't accept tx specific changes if we've got mixed RxTx vectors + * test and jump out here if needed before changing the rx numbers + */ + if ((1000000/ec->tx_coalesce_usecs) != adapter->tx_eitr_param && + adapter->q_vector[0]->txr_count && adapter->q_vector[0]->rxr_count) + return -EINVAL; + if (ec->tx_max_coalesced_frames_irq) adapter->tx_ring[0].work_limit = ec->tx_max_coalesced_frames_irq; @@ -1963,26 +1995,49 @@ static int ixgbe_set_coalesce(struct net_device *netdev, return -EINVAL; /* store the value in ints/second */ - adapter->eitr_param = 1000000/ec->rx_coalesce_usecs; + adapter->rx_eitr_param = 1000000/ec->rx_coalesce_usecs; /* static value of interrupt rate */ - adapter->itr_setting = adapter->eitr_param; + adapter->rx_itr_setting = adapter->rx_eitr_param; /* clear the lower bit as its used for dynamic state */ - adapter->itr_setting &= ~1; + adapter->rx_itr_setting &= ~1; } else if (ec->rx_coalesce_usecs == 1) { /* 1 means dynamic mode */ - adapter->eitr_param = 20000; - adapter->itr_setting = 1; + adapter->rx_eitr_param = 20000; + adapter->rx_itr_setting = 1; } else { /* * any other value means disable eitr, which is best * served by setting the interrupt rate very high */ if (adapter->flags2 & IXGBE_FLAG2_RSC_ENABLED) - adapter->eitr_param = IXGBE_MAX_RSC_INT_RATE; + adapter->rx_eitr_param = IXGBE_MAX_RSC_INT_RATE; else - adapter->eitr_param = IXGBE_MAX_INT_RATE; - adapter->itr_setting = 0; + adapter->rx_eitr_param = IXGBE_MAX_INT_RATE; + adapter->rx_itr_setting = 0; + } + + if (ec->tx_coalesce_usecs > 1) { + /* check the limits */ + if ((1000000/ec->tx_coalesce_usecs > IXGBE_MAX_INT_RATE) || + (1000000/ec->tx_coalesce_usecs < IXGBE_MIN_INT_RATE)) + return -EINVAL; + + /* store the value in ints/second */ + adapter->tx_eitr_param = 1000000/ec->tx_coalesce_usecs; + + /* static value of interrupt rate */ + adapter->tx_itr_setting = adapter->tx_eitr_param; + + /* clear the lower bit as its used for dynamic state */ + adapter->tx_itr_setting &= ~1; + } else if (ec->tx_coalesce_usecs == 1) { + /* 1 means dynamic mode */ + adapter->tx_eitr_param = 10000; + adapter->tx_itr_setting = 1; + } else { + adapter->tx_eitr_param = IXGBE_MAX_INT_RATE; + adapter->tx_itr_setting = 0; } /* MSI/MSIx Interrupt Mode */ @@ -1992,17 +2047,17 @@ static int ixgbe_set_coalesce(struct net_device *netdev, for (i = 0; i < num_vectors; i++) { q_vector = adapter->q_vector[i]; if (q_vector->txr_count && !q_vector->rxr_count) - /* tx vector gets half the rate */ - q_vector->eitr = (adapter->eitr_param >> 1); + /* tx only */ + q_vector->eitr = adapter->tx_eitr_param; else /* rx only or mixed */ - q_vector->eitr = adapter->eitr_param; + q_vector->eitr = adapter->rx_eitr_param; ixgbe_write_eitr(q_vector); } /* Legacy Interrupt Mode */ } else { q_vector = adapter->q_vector[0]; - q_vector->eitr = adapter->eitr_param; + q_vector->eitr = adapter->rx_eitr_param; ixgbe_write_eitr(q_vector); } diff --git a/drivers/net/ixgbe/ixgbe_main.c b/drivers/net/ixgbe/ixgbe_main.c index 59ad9590e700..a456578b8578 100644 --- a/drivers/net/ixgbe/ixgbe_main.c +++ b/drivers/net/ixgbe/ixgbe_main.c @@ -44,12 +44,13 @@ #include "ixgbe.h" #include "ixgbe_common.h" +#include "ixgbe_dcb_82599.h" char ixgbe_driver_name[] = "ixgbe"; static const char ixgbe_driver_string[] = "Intel(R) 10 Gigabit PCI Express Network Driver"; -#define DRV_VERSION "2.0.37-k2" +#define DRV_VERSION "2.0.44-k2" const char ixgbe_driver_version[] = DRV_VERSION; static char ixgbe_copyright[] = "Copyright (c) 1999-2009 Intel Corporation."; @@ -97,8 +98,12 @@ static struct pci_device_id ixgbe_pci_tbl[] = { board_82599 }, {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_82599_SFP), board_82599 }, + {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_82599_KX4_MEZZ), + board_82599 }, {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_82599_CX4), board_82599 }, + {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_82599_COMBO_BACKPLANE), + board_82599 }, /* required last entry */ {0, } @@ -222,6 +227,56 @@ static void ixgbe_unmap_and_free_tx_resource(struct ixgbe_adapter *adapter, /* tx_buffer_info must be completely set up in the transmit path */ } +/** + * ixgbe_tx_is_paused - check if the tx ring is paused + * @adapter: the ixgbe adapter + * @tx_ring: the corresponding tx_ring + * + * If not in DCB mode, checks TFCS.TXOFF, otherwise, find out the + * corresponding TC of this tx_ring when checking TFCS. + * + * Returns : true if paused + */ +static inline bool ixgbe_tx_is_paused(struct ixgbe_adapter *adapter, + struct ixgbe_ring *tx_ring) +{ + u32 txoff = IXGBE_TFCS_TXOFF; + +#ifdef CONFIG_IXGBE_DCB + if (adapter->flags & IXGBE_FLAG_DCB_ENABLED) { + int tc; + int reg_idx = tx_ring->reg_idx; + int dcb_i = adapter->ring_feature[RING_F_DCB].indices; + + if (adapter->hw.mac.type == ixgbe_mac_82598EB) { + tc = reg_idx >> 2; + txoff = IXGBE_TFCS_TXOFF0; + } else if (adapter->hw.mac.type == ixgbe_mac_82599EB) { + tc = 0; + txoff = IXGBE_TFCS_TXOFF; + if (dcb_i == 8) { + /* TC0, TC1 */ + tc = reg_idx >> 5; + if (tc == 2) /* TC2, TC3 */ + tc += (reg_idx - 64) >> 4; + else if (tc == 3) /* TC4, TC5, TC6, TC7 */ + tc += 1 + ((reg_idx - 96) >> 3); + } else if (dcb_i == 4) { + /* TC0, TC1 */ + tc = reg_idx >> 6; + if (tc == 1) { + tc += (reg_idx - 64) >> 5; + if (tc == 2) /* TC2, TC3 */ + tc += (reg_idx - 96) >> 4; + } + } + } + txoff <<= tc; + } +#endif + return IXGBE_READ_REG(&adapter->hw, IXGBE_TFCS) & txoff; +} + static inline bool ixgbe_check_tx_hang(struct ixgbe_adapter *adapter, struct ixgbe_ring *tx_ring, unsigned int eop) @@ -233,7 +288,7 @@ static inline bool ixgbe_check_tx_hang(struct ixgbe_adapter *adapter, adapter->detect_tx_hung = false; if (tx_ring->tx_buffer_info[eop].time_stamp && time_after(jiffies, tx_ring->tx_buffer_info[eop].time_stamp + HZ) && - !(IXGBE_READ_REG(&adapter->hw, IXGBE_TFCS) & IXGBE_TFCS_TXOFF)) { + !ixgbe_tx_is_paused(adapter, tx_ring)) { /* detected Tx unit hang */ union ixgbe_adv_tx_desc *tx_desc; tx_desc = IXGBE_TX_DESC_ADV(*tx_ring, eop); @@ -408,19 +463,23 @@ static void ixgbe_update_tx_dca(struct ixgbe_adapter *adapter, u32 txctrl; int cpu = get_cpu(); int q = tx_ring - adapter->tx_ring; + struct ixgbe_hw *hw = &adapter->hw; if (tx_ring->cpu != cpu) { - txctrl = IXGBE_READ_REG(&adapter->hw, IXGBE_DCA_TXCTRL(q)); if (adapter->hw.mac.type == ixgbe_mac_82598EB) { + txctrl = IXGBE_READ_REG(hw, IXGBE_DCA_TXCTRL(q)); txctrl &= ~IXGBE_DCA_TXCTRL_CPUID_MASK; txctrl |= dca3_get_tag(&adapter->pdev->dev, cpu); + txctrl |= IXGBE_DCA_TXCTRL_DESC_DCA_EN; + IXGBE_WRITE_REG(hw, IXGBE_DCA_TXCTRL(q), txctrl); } else if (adapter->hw.mac.type == ixgbe_mac_82599EB) { + txctrl = IXGBE_READ_REG(hw, IXGBE_DCA_TXCTRL_82599(q)); txctrl &= ~IXGBE_DCA_TXCTRL_CPUID_MASK_82599; txctrl |= (dca3_get_tag(&adapter->pdev->dev, cpu) << - IXGBE_DCA_TXCTRL_CPUID_SHIFT_82599); + IXGBE_DCA_TXCTRL_CPUID_SHIFT_82599); + txctrl |= IXGBE_DCA_TXCTRL_DESC_DCA_EN; + IXGBE_WRITE_REG(hw, IXGBE_DCA_TXCTRL_82599(q), txctrl); } - txctrl |= IXGBE_DCA_TXCTRL_DESC_DCA_EN; - IXGBE_WRITE_REG(&adapter->hw, IXGBE_DCA_TXCTRL(q), txctrl); tx_ring->cpu = cpu; } put_cpu(); @@ -926,12 +985,12 @@ static void ixgbe_configure_msix(struct ixgbe_adapter *adapter) r_idx + 1); } - /* if this is a tx only vector halve the interrupt rate */ if (q_vector->txr_count && !q_vector->rxr_count) - q_vector->eitr = (adapter->eitr_param >> 1); + /* tx only */ + q_vector->eitr = adapter->tx_eitr_param; else if (q_vector->rxr_count) - /* rx only */ - q_vector->eitr = adapter->eitr_param; + /* rx or mixed */ + q_vector->eitr = adapter->rx_eitr_param; ixgbe_write_eitr(q_vector); } @@ -1359,7 +1418,7 @@ static int ixgbe_clean_rxonly(struct napi_struct *napi, int budget) /* If all Rx work done, exit the polling mode */ if (work_done < budget) { napi_complete(napi); - if (adapter->itr_setting & 1) + if (adapter->rx_itr_setting & 1) ixgbe_set_itr_msix(q_vector); if (!test_bit(__IXGBE_DOWN, &adapter->state)) ixgbe_irq_enable_queues(adapter, @@ -1420,7 +1479,7 @@ static int ixgbe_clean_rxtx_many(struct napi_struct *napi, int budget) /* If all Rx work done, exit the polling mode */ if (work_done < budget) { napi_complete(napi); - if (adapter->itr_setting & 1) + if (adapter->rx_itr_setting & 1) ixgbe_set_itr_msix(q_vector); if (!test_bit(__IXGBE_DOWN, &adapter->state)) ixgbe_irq_enable_queues(adapter, @@ -1458,10 +1517,10 @@ static int ixgbe_clean_txonly(struct napi_struct *napi, int budget) if (!ixgbe_clean_tx_irq(q_vector, tx_ring)) work_done = budget; - /* If all Rx work done, exit the polling mode */ + /* If all Tx work done, exit the polling mode */ if (work_done < budget) { napi_complete(napi); - if (adapter->itr_setting & 1) + if (adapter->tx_itr_setting & 1) ixgbe_set_itr_msix(q_vector); if (!test_bit(__IXGBE_DOWN, &adapter->state)) ixgbe_irq_enable_queues(adapter, ((u64)1 << q_vector->v_idx)); @@ -1848,7 +1907,7 @@ static void ixgbe_configure_msi_and_legacy(struct ixgbe_adapter *adapter) struct ixgbe_hw *hw = &adapter->hw; IXGBE_WRITE_REG(hw, IXGBE_EITR(0), - EITR_INTS_PER_SEC_TO_REG(adapter->eitr_param)); + EITR_INTS_PER_SEC_TO_REG(adapter->rx_eitr_param)); ixgbe_set_ivar(adapter, 0, 0, 0); ixgbe_set_ivar(adapter, 1, 0, 0); @@ -1885,18 +1944,49 @@ static void ixgbe_configure_tx(struct ixgbe_adapter *adapter) IXGBE_WRITE_REG(hw, IXGBE_TDT(j), 0); adapter->tx_ring[i].head = IXGBE_TDH(j); adapter->tx_ring[i].tail = IXGBE_TDT(j); - /* Disable Tx Head Writeback RO bit, since this hoses + /* + * Disable Tx Head Writeback RO bit, since this hoses * bookkeeping if things aren't delivered in order. */ - txctrl = IXGBE_READ_REG(hw, IXGBE_DCA_TXCTRL(j)); + switch (hw->mac.type) { + case ixgbe_mac_82598EB: + txctrl = IXGBE_READ_REG(hw, IXGBE_DCA_TXCTRL(j)); + break; + case ixgbe_mac_82599EB: + default: + txctrl = IXGBE_READ_REG(hw, IXGBE_DCA_TXCTRL_82599(j)); + break; + } txctrl &= ~IXGBE_DCA_TXCTRL_TX_WB_RO_EN; - IXGBE_WRITE_REG(hw, IXGBE_DCA_TXCTRL(j), txctrl); + switch (hw->mac.type) { + case ixgbe_mac_82598EB: + IXGBE_WRITE_REG(hw, IXGBE_DCA_TXCTRL(j), txctrl); + break; + case ixgbe_mac_82599EB: + default: + IXGBE_WRITE_REG(hw, IXGBE_DCA_TXCTRL_82599(j), txctrl); + break; + } } + if (hw->mac.type == ixgbe_mac_82599EB) { + u32 rttdcs; + + /* disable the arbiter while setting MTQC */ + rttdcs = IXGBE_READ_REG(hw, IXGBE_RTTDCS); + rttdcs |= IXGBE_RTTDCS_ARBDIS; + IXGBE_WRITE_REG(hw, IXGBE_RTTDCS, rttdcs); + /* We enable 8 traffic classes, DCB only */ if (adapter->flags & IXGBE_FLAG_DCB_ENABLED) IXGBE_WRITE_REG(hw, IXGBE_MTQC, (IXGBE_MTQC_RT_ENA | IXGBE_MTQC_8TC_8TQ)); + else + IXGBE_WRITE_REG(hw, IXGBE_MTQC, IXGBE_MTQC_64Q_1PB); + + /* re-eable the arbiter */ + rttdcs &= ~IXGBE_RTTDCS_ARBDIS; + IXGBE_WRITE_REG(hw, IXGBE_RTTDCS, rttdcs); } } @@ -1970,6 +2060,50 @@ static u32 ixgbe_setup_mrqc(struct ixgbe_adapter *adapter) } /** + * ixgbe_configure_rscctl - enable RSC for the indicated ring + * @adapter: address of board private structure + * @index: index of ring to set + * @rx_buf_len: rx buffer length + **/ +static void ixgbe_configure_rscctl(struct ixgbe_adapter *adapter, int index, + int rx_buf_len) +{ + struct ixgbe_ring *rx_ring; + struct ixgbe_hw *hw = &adapter->hw; + int j; + u32 rscctrl; + + rx_ring = &adapter->rx_ring[index]; + j = rx_ring->reg_idx; + rscctrl = IXGBE_READ_REG(hw, IXGBE_RSCCTL(j)); + rscctrl |= IXGBE_RSCCTL_RSCEN; + /* + * we must limit the number of descriptors so that the + * total size of max desc * buf_len is not greater + * than 65535 + */ + if (rx_ring->flags & IXGBE_RING_RX_PS_ENABLED) { +#if (MAX_SKB_FRAGS > 16) + rscctrl |= IXGBE_RSCCTL_MAXDESC_16; +#elif (MAX_SKB_FRAGS > 8) + rscctrl |= IXGBE_RSCCTL_MAXDESC_8; +#elif (MAX_SKB_FRAGS > 4) + rscctrl |= IXGBE_RSCCTL_MAXDESC_4; +#else + rscctrl |= IXGBE_RSCCTL_MAXDESC_1; +#endif + } else { + if (rx_buf_len < IXGBE_RXBUFFER_4096) + rscctrl |= IXGBE_RSCCTL_MAXDESC_16; + else if (rx_buf_len < IXGBE_RXBUFFER_8192) + rscctrl |= IXGBE_RSCCTL_MAXDESC_8; + else + rscctrl |= IXGBE_RSCCTL_MAXDESC_4; + } + IXGBE_WRITE_REG(hw, IXGBE_RSCCTL(j), rscctrl); +} + +/** * ixgbe_configure_rx - Configure 8259x Receive Unit after Reset * @adapter: board private structure * @@ -1990,7 +2124,6 @@ static void ixgbe_configure_rx(struct ixgbe_adapter *adapter) u32 fctrl, hlreg0; u32 reta = 0, mrqc = 0; u32 rdrxctl; - u32 rscctrl; int rx_buf_len; /* Decide whether to use packet split mode or not */ @@ -2148,36 +2281,9 @@ static void ixgbe_configure_rx(struct ixgbe_adapter *adapter) if (adapter->flags2 & IXGBE_FLAG2_RSC_ENABLED) { /* Enable 82599 HW-RSC */ - for (i = 0; i < adapter->num_rx_queues; i++) { - rx_ring = &adapter->rx_ring[i]; - j = rx_ring->reg_idx; - rscctrl = IXGBE_READ_REG(hw, IXGBE_RSCCTL(j)); - rscctrl |= IXGBE_RSCCTL_RSCEN; - /* - * we must limit the number of descriptors so that the - * total size of max desc * buf_len is not greater - * than 65535 - */ - if (rx_ring->flags & IXGBE_RING_RX_PS_ENABLED) { -#if (MAX_SKB_FRAGS > 16) - rscctrl |= IXGBE_RSCCTL_MAXDESC_16; -#elif (MAX_SKB_FRAGS > 8) - rscctrl |= IXGBE_RSCCTL_MAXDESC_8; -#elif (MAX_SKB_FRAGS > 4) - rscctrl |= IXGBE_RSCCTL_MAXDESC_4; -#else - rscctrl |= IXGBE_RSCCTL_MAXDESC_1; -#endif - } else { - if (rx_buf_len < IXGBE_RXBUFFER_4096) - rscctrl |= IXGBE_RSCCTL_MAXDESC_16; - else if (rx_buf_len < IXGBE_RXBUFFER_8192) - rscctrl |= IXGBE_RSCCTL_MAXDESC_8; - else - rscctrl |= IXGBE_RSCCTL_MAXDESC_4; - } - IXGBE_WRITE_REG(hw, IXGBE_RSCCTL(j), rscctrl); - } + for (i = 0; i < adapter->num_rx_queues; i++) + ixgbe_configure_rscctl(adapter, i, rx_buf_len); + /* Disable RSC for ACK packets */ IXGBE_WRITE_REG(hw, IXGBE_RSCDBU, (IXGBE_RSCDBU_RSCACKDIS | IXGBE_READ_REG(hw, IXGBE_RSCDBU))); @@ -2434,7 +2540,10 @@ static void ixgbe_configure(struct ixgbe_adapter *adapter) ixgbe_restore_vlan(adapter); #ifdef CONFIG_IXGBE_DCB if (adapter->flags & IXGBE_FLAG_DCB_ENABLED) { - netif_set_gso_max_size(netdev, 32768); + if (hw->mac.type == ixgbe_mac_82598EB) + netif_set_gso_max_size(netdev, 32768); + else + netif_set_gso_max_size(netdev, 65536); ixgbe_configure_dcb(adapter); } else { netif_set_gso_max_size(netdev, 65536); @@ -2926,6 +3035,8 @@ void ixgbe_down(struct ixgbe_adapter *adapter) ixgbe_napi_disable_all(adapter); + clear_bit(__IXGBE_SFP_MODULE_NOT_FOUND, &adapter->state); + del_timer_sync(&adapter->sfp_timer); del_timer_sync(&adapter->watchdog_timer); cancel_work_sync(&adapter->watchdog_task); @@ -2989,7 +3100,7 @@ static int ixgbe_poll(struct napi_struct *napi, int budget) /* If budget not fully consumed, exit the polling mode */ if (work_done < budget) { napi_complete(napi); - if (adapter->itr_setting & 1) + if (adapter->rx_itr_setting & 1) ixgbe_set_itr(adapter); if (!test_bit(__IXGBE_DOWN, &adapter->state)) ixgbe_irq_enable_queues(adapter, IXGBE_EIMS_RTX_QUEUE); @@ -3599,7 +3710,10 @@ static int ixgbe_alloc_q_vectors(struct ixgbe_adapter *adapter) if (!q_vector) goto err_out; q_vector->adapter = adapter; - q_vector->eitr = adapter->eitr_param; + if (q_vector->txr_count && !q_vector->rxr_count) + q_vector->eitr = adapter->tx_eitr_param; + else + q_vector->eitr = adapter->rx_eitr_param; q_vector->v_idx = q_idx; netif_napi_add(adapter->netdev, &q_vector->napi, (*poll), 64); adapter->q_vector[q_idx] = q_vector; @@ -3868,8 +3982,10 @@ static int __devinit ixgbe_sw_init(struct ixgbe_adapter *adapter) hw->fc.disable_fc_autoneg = false; /* enable itr by default in dynamic mode */ - adapter->itr_setting = 1; - adapter->eitr_param = 20000; + adapter->rx_itr_setting = 1; + adapter->rx_eitr_param = 20000; + adapter->tx_itr_setting = 1; + adapter->tx_eitr_param = 10000; /* set defaults for eitr in MegaBytes */ adapter->eitr_low = 10; @@ -4409,10 +4525,13 @@ void ixgbe_update_stats(struct ixgbe_adapter *adapter) /* 82598 hardware only has a 32 bit counter in the high register */ if (hw->mac.type == ixgbe_mac_82599EB) { + u64 tmp; adapter->stats.gorc += IXGBE_READ_REG(hw, IXGBE_GORCL); - IXGBE_READ_REG(hw, IXGBE_GORCH); /* to clear */ + tmp = IXGBE_READ_REG(hw, IXGBE_GORCH) & 0xF; /* 4 high bits of GORC */ + adapter->stats.gorc += (tmp << 32); adapter->stats.gotc += IXGBE_READ_REG(hw, IXGBE_GOTCL); - IXGBE_READ_REG(hw, IXGBE_GOTCH); /* to clear */ + tmp = IXGBE_READ_REG(hw, IXGBE_GOTCH) & 0xF; /* 4 high bits of GOTC */ + adapter->stats.gotc += (tmp << 32); adapter->stats.tor += IXGBE_READ_REG(hw, IXGBE_TORL); IXGBE_READ_REG(hw, IXGBE_TORH); /* to clear */ adapter->stats.lxonrxc += IXGBE_READ_REG(hw, IXGBE_LXONRXCNT); @@ -5048,7 +5167,6 @@ static void ixgbe_atr(struct ixgbe_adapter *adapter, struct sk_buff *skb, /* Right now, we support IPv4 only */ struct ixgbe_atr_input atr_input; struct tcphdr *th; - struct udphdr *uh; struct iphdr *iph = ip_hdr(skb); struct ethhdr *eth = (struct ethhdr *)skb->data; u16 vlan_id, src_port, dst_port, flex_bytes; @@ -5062,12 +5180,6 @@ static void ixgbe_atr(struct ixgbe_adapter *adapter, struct sk_buff *skb, dst_port = th->dest; l4type |= IXGBE_ATR_L4TYPE_TCP; /* l4type IPv4 type is 0, no need to assign */ - } else if(iph->protocol == IPPROTO_UDP) { - uh = udp_hdr(skb); - src_port = uh->source; - dst_port = uh->dest; - l4type |= IXGBE_ATR_L4TYPE_UDP; - /* l4type IPv4 type is 0, no need to assign */ } else { /* Unsupported L4 header, just bail here */ return; @@ -5471,12 +5583,7 @@ static int __devinit ixgbe_probe(struct pci_dev *pdev, goto err_pci_reg; } - err = pci_enable_pcie_error_reporting(pdev); - if (err) { - dev_err(&pdev->dev, "pci_enable_pcie_error_reporting failed " - "0x%x\n", err); - /* non-fatal, continue */ - } + pci_enable_pcie_error_reporting(pdev); pci_set_master(pdev); pci_save_state(pdev); @@ -5785,7 +5892,6 @@ static void __devexit ixgbe_remove(struct pci_dev *pdev) { struct net_device *netdev = pci_get_drvdata(pdev); struct ixgbe_adapter *adapter = netdev_priv(netdev); - int err; set_bit(__IXGBE_DOWN, &adapter->state); /* clear the module not found bit to make sure the worker won't @@ -5836,10 +5942,7 @@ static void __devexit ixgbe_remove(struct pci_dev *pdev) free_netdev(netdev); - err = pci_disable_pcie_error_reporting(pdev); - if (err) - dev_err(&pdev->dev, - "pci_disable_pcie_error_reporting failed 0x%x\n", err); + pci_disable_pcie_error_reporting(pdev); pci_disable_device(pdev); } @@ -5891,6 +5994,7 @@ static pci_ers_result_t ixgbe_io_slot_reset(struct pci_dev *pdev) } else { pci_set_master(pdev); pci_restore_state(pdev); + pci_save_state(pdev); pci_wake_from_d3(pdev, false); diff --git a/drivers/net/ixgbe/ixgbe_type.h b/drivers/net/ixgbe/ixgbe_type.h index 8761d7899f7d..ef4bdd58e016 100644 --- a/drivers/net/ixgbe/ixgbe_type.h +++ b/drivers/net/ixgbe/ixgbe_type.h @@ -49,9 +49,11 @@ #define IXGBE_DEV_ID_82598_SR_DUAL_PORT_EM 0x10E1 #define IXGBE_DEV_ID_82598EB_XF_LR 0x10F4 #define IXGBE_DEV_ID_82599_KX4 0x10F7 +#define IXGBE_DEV_ID_82599_KX4_MEZZ 0x1514 #define IXGBE_DEV_ID_82599_CX4 0x10F9 #define IXGBE_DEV_ID_82599_SFP 0x10FB #define IXGBE_DEV_ID_82599_XAUI_LOM 0x10FC +#define IXGBE_DEV_ID_82599_COMBO_BACKPLANE 0x10F8 /* General Registers */ #define IXGBE_CTRL 0x00000 @@ -1336,6 +1338,8 @@ #define IXGBE_AUTOC_KX4_SUPP 0x80000000 #define IXGBE_AUTOC_KX_SUPP 0x40000000 #define IXGBE_AUTOC_PAUSE 0x30000000 +#define IXGBE_AUTOC_ASM_PAUSE 0x20000000 +#define IXGBE_AUTOC_SYM_PAUSE 0x10000000 #define IXGBE_AUTOC_RF 0x08000000 #define IXGBE_AUTOC_PD_TMR 0x06000000 #define IXGBE_AUTOC_AN_RX_LOOSE 0x01000000 @@ -1404,6 +1408,8 @@ #define IXGBE_LINK_UP_TIME 90 /* 9.0 Seconds */ #define IXGBE_AUTO_NEG_TIME 45 /* 4.5 Seconds */ +#define IXGBE_LINKS2_AN_SUPPORTED 0x00000040 + /* PCS1GLSTA Bit Masks */ #define IXGBE_PCS1GLSTA_LINK_OK 1 #define IXGBE_PCS1GLSTA_SYNK_OK 0x10 @@ -1424,6 +1430,11 @@ #define IXGBE_PCS1GLCTL_AN_ENABLE 0x10000 #define IXGBE_PCS1GLCTL_AN_RESTART 0x20000 +/* ANLP1 Bit Masks */ +#define IXGBE_ANLP1_PAUSE 0x0C00 +#define IXGBE_ANLP1_SYM_PAUSE 0x0400 +#define IXGBE_ANLP1_ASM_PAUSE 0x0800 + /* SW Semaphore Register bitmasks */ #define IXGBE_SWSM_SMBI 0x00000001 /* Driver Semaphore bit */ #define IXGBE_SWSM_SWESMBI 0x00000002 /* FW Semaphore bit */ diff --git a/drivers/net/ixp2000/enp2611.c b/drivers/net/ixp2000/enp2611.c index b02a981c87a8..34a6cfd17930 100644 --- a/drivers/net/ixp2000/enp2611.c +++ b/drivers/net/ixp2000/enp2611.c @@ -119,24 +119,9 @@ static struct ixp2400_msf_parameters enp2611_msf_parameters = } }; -struct enp2611_ixpdev_priv -{ - struct ixpdev_priv ixpdev_priv; - struct net_device_stats stats; -}; - static struct net_device *nds[3]; static struct timer_list link_check_timer; -static struct net_device_stats *enp2611_get_stats(struct net_device *dev) -{ - struct enp2611_ixpdev_priv *ip = netdev_priv(dev); - - pm3386_get_stats(ip->ixpdev_priv.channel, &(ip->stats)); - - return &(ip->stats); -} - /* @@@ Poll the SFP moddef0 line too. */ /* @@@ Try to use the pm3386 DOOL interrupt as well. */ static void enp2611_check_link_status(unsigned long __dummy) @@ -203,14 +188,13 @@ static int __init enp2611_init_module(void) ports = pm3386_port_count(); for (i = 0; i < ports; i++) { - nds[i] = ixpdev_alloc(i, sizeof(struct enp2611_ixpdev_priv)); + nds[i] = ixpdev_alloc(i, sizeof(struct ixpdev_priv)); if (nds[i] == NULL) { while (--i >= 0) free_netdev(nds[i]); return -ENOMEM; } - nds[i]->get_stats = enp2611_get_stats; pm3386_init_port(i); pm3386_get_mac(i, nds[i]->dev_addr); } diff --git a/drivers/net/ixp2000/ixpdev.c b/drivers/net/ixp2000/ixpdev.c index 127243461a51..9aee0cc922c9 100644 --- a/drivers/net/ixp2000/ixpdev.c +++ b/drivers/net/ixp2000/ixpdev.c @@ -21,6 +21,7 @@ #include "ixp2400_tx.ucode" #include "ixpdev_priv.h" #include "ixpdev.h" +#include "pm3386.h" #define DRV_MODULE_VERSION "0.2" @@ -271,6 +272,15 @@ static int ixpdev_close(struct net_device *dev) return 0; } +static struct net_device_stats *ixpdev_get_stats(struct net_device *dev) +{ + struct ixpdev_priv *ip = netdev_priv(dev); + + pm3386_get_stats(ip->channel, &(dev->stats)); + + return &(dev->stats); +} + static const struct net_device_ops ixpdev_netdev_ops = { .ndo_open = ixpdev_open, .ndo_stop = ixpdev_close, @@ -278,6 +288,7 @@ static const struct net_device_ops ixpdev_netdev_ops = { .ndo_change_mtu = eth_change_mtu, .ndo_validate_addr = eth_validate_addr, .ndo_set_mac_address = eth_mac_addr, + .ndo_get_stats = ixpdev_get_stats, #ifdef CONFIG_NET_POLL_CONTROLLER .ndo_poll_controller = ixpdev_poll_controller, #endif diff --git a/drivers/net/ks8851.c b/drivers/net/ks8851.c index 547ac7c7479c..a23f739d222f 100644 --- a/drivers/net/ks8851.c +++ b/drivers/net/ks8851.c @@ -171,6 +171,36 @@ static void ks8851_wrreg16(struct ks8851_net *ks, unsigned reg, unsigned val) } /** + * ks8851_wrreg8 - write 8bit register value to chip + * @ks: The chip state + * @reg: The register address + * @val: The value to write + * + * Issue a write to put the value @val into the register specified in @reg. + */ +static void ks8851_wrreg8(struct ks8851_net *ks, unsigned reg, unsigned val) +{ + struct spi_transfer *xfer = &ks->spi_xfer1; + struct spi_message *msg = &ks->spi_msg1; + __le16 txb[2]; + int ret; + int bit; + + bit = 1 << (reg & 3); + + txb[0] = cpu_to_le16(MK_OP(bit, reg) | KS_SPIOP_WR); + txb[1] = val; + + xfer->tx_buf = txb; + xfer->rx_buf = NULL; + xfer->len = 3; + + ret = spi_sync(ks->spidev, msg); + if (ret < 0) + ks_err(ks, "spi_sync() failed\n"); +} + +/** * ks8851_rx_1msg - select whether to use one or two messages for spi read * @ks: The device structure * @@ -322,13 +352,12 @@ static void ks8851_soft_reset(struct ks8851_net *ks, unsigned op) static int ks8851_write_mac_addr(struct net_device *dev) { struct ks8851_net *ks = netdev_priv(dev); - u16 *mcp = (u16 *)dev->dev_addr; + int i; mutex_lock(&ks->lock); - ks8851_wrreg16(ks, KS_MARL, mcp[0]); - ks8851_wrreg16(ks, KS_MARM, mcp[1]); - ks8851_wrreg16(ks, KS_MARH, mcp[2]); + for (i = 0; i < ETH_ALEN; i++) + ks8851_wrreg8(ks, KS_MAR(i), dev->dev_addr[i]); mutex_unlock(&ks->lock); @@ -951,7 +980,7 @@ static void ks8851_set_rx_mode(struct net_device *dev) mcptr = mcptr->next; } - rxctrl.rxcr1 = RXCR1_RXME | RXCR1_RXAE | RXCR1_RXPAFMA; + rxctrl.rxcr1 = RXCR1_RXME | RXCR1_RXPAFMA; } else { /* just accept broadcast / unicast */ rxctrl.rxcr1 = RXCR1_RXPAFMA; @@ -1239,6 +1268,9 @@ static int __devinit ks8851_probe(struct spi_device *spi) ndev->netdev_ops = &ks8851_netdev_ops; ndev->irq = spi->irq; + /* issue a global soft reset to reset the device. */ + ks8851_soft_reset(ks, GRR_GSR); + /* simple check for a valid chip being connected to the bus */ if ((ks8851_rdreg16(ks, KS_CIDER) & ~CIDER_REV_MASK) != CIDER_ID) { @@ -1321,3 +1353,4 @@ MODULE_LICENSE("GPL"); module_param_named(message, msg_enable, int, 0); MODULE_PARM_DESC(message, "Message verbosity level (0=none, 31=all)"); +MODULE_ALIAS("spi:ks8851"); diff --git a/drivers/net/ks8851.h b/drivers/net/ks8851.h index 85abe147afbf..f52c312cc356 100644 --- a/drivers/net/ks8851.h +++ b/drivers/net/ks8851.h @@ -16,6 +16,7 @@ #define CCR_32PIN (1 << 0) /* MAC address registers */ +#define KS_MAR(_m) 0x15 - (_m) #define KS_MARL 0x10 #define KS_MARM 0x12 #define KS_MARH 0x14 diff --git a/drivers/net/ks8851_mll.c b/drivers/net/ks8851_mll.c new file mode 100644 index 000000000000..c146304d8d6c --- /dev/null +++ b/drivers/net/ks8851_mll.c @@ -0,0 +1,1691 @@ +/** + * drivers/net/ks8851_mll.c + * Copyright (c) 2009 Micrel Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/** + * Supports: + * KS8851 16bit MLL chip from Micrel Inc. + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/netdevice.h> +#include <linux/etherdevice.h> +#include <linux/ethtool.h> +#include <linux/cache.h> +#include <linux/crc32.h> +#include <linux/mii.h> +#include <linux/platform_device.h> +#include <linux/delay.h> + +#define DRV_NAME "ks8851_mll" + +static u8 KS_DEFAULT_MAC_ADDRESS[] = { 0x00, 0x10, 0xA1, 0x86, 0x95, 0x11 }; +#define MAX_RECV_FRAMES 32 +#define MAX_BUF_SIZE 2048 +#define TX_BUF_SIZE 2000 +#define RX_BUF_SIZE 2000 + +#define KS_CCR 0x08 +#define CCR_EEPROM (1 << 9) +#define CCR_SPI (1 << 8) +#define CCR_8BIT (1 << 7) +#define CCR_16BIT (1 << 6) +#define CCR_32BIT (1 << 5) +#define CCR_SHARED (1 << 4) +#define CCR_32PIN (1 << 0) + +/* MAC address registers */ +#define KS_MARL 0x10 +#define KS_MARM 0x12 +#define KS_MARH 0x14 + +#define KS_OBCR 0x20 +#define OBCR_ODS_16MA (1 << 6) + +#define KS_EEPCR 0x22 +#define EEPCR_EESA (1 << 4) +#define EEPCR_EESB (1 << 3) +#define EEPCR_EEDO (1 << 2) +#define EEPCR_EESCK (1 << 1) +#define EEPCR_EECS (1 << 0) + +#define KS_MBIR 0x24 +#define MBIR_TXMBF (1 << 12) +#define MBIR_TXMBFA (1 << 11) +#define MBIR_RXMBF (1 << 4) +#define MBIR_RXMBFA (1 << 3) + +#define KS_GRR 0x26 +#define GRR_QMU (1 << 1) +#define GRR_GSR (1 << 0) + +#define KS_WFCR 0x2A +#define WFCR_MPRXE (1 << 7) +#define WFCR_WF3E (1 << 3) +#define WFCR_WF2E (1 << 2) +#define WFCR_WF1E (1 << 1) +#define WFCR_WF0E (1 << 0) + +#define KS_WF0CRC0 0x30 +#define KS_WF0CRC1 0x32 +#define KS_WF0BM0 0x34 +#define KS_WF0BM1 0x36 +#define KS_WF0BM2 0x38 +#define KS_WF0BM3 0x3A + +#define KS_WF1CRC0 0x40 +#define KS_WF1CRC1 0x42 +#define KS_WF1BM0 0x44 +#define KS_WF1BM1 0x46 +#define KS_WF1BM2 0x48 +#define KS_WF1BM3 0x4A + +#define KS_WF2CRC0 0x50 +#define KS_WF2CRC1 0x52 +#define KS_WF2BM0 0x54 +#define KS_WF2BM1 0x56 +#define KS_WF2BM2 0x58 +#define KS_WF2BM3 0x5A + +#define KS_WF3CRC0 0x60 +#define KS_WF3CRC1 0x62 +#define KS_WF3BM0 0x64 +#define KS_WF3BM1 0x66 +#define KS_WF3BM2 0x68 +#define KS_WF3BM3 0x6A + +#define KS_TXCR 0x70 +#define TXCR_TCGICMP (1 << 8) +#define TXCR_TCGUDP (1 << 7) +#define TXCR_TCGTCP (1 << 6) +#define TXCR_TCGIP (1 << 5) +#define TXCR_FTXQ (1 << 4) +#define TXCR_TXFCE (1 << 3) +#define TXCR_TXPE (1 << 2) +#define TXCR_TXCRC (1 << 1) +#define TXCR_TXE (1 << 0) + +#define KS_TXSR 0x72 +#define TXSR_TXLC (1 << 13) +#define TXSR_TXMC (1 << 12) +#define TXSR_TXFID_MASK (0x3f << 0) +#define TXSR_TXFID_SHIFT (0) +#define TXSR_TXFID_GET(_v) (((_v) >> 0) & 0x3f) + + +#define KS_RXCR1 0x74 +#define RXCR1_FRXQ (1 << 15) +#define RXCR1_RXUDPFCC (1 << 14) +#define RXCR1_RXTCPFCC (1 << 13) +#define RXCR1_RXIPFCC (1 << 12) +#define RXCR1_RXPAFMA (1 << 11) +#define RXCR1_RXFCE (1 << 10) +#define RXCR1_RXEFE (1 << 9) +#define RXCR1_RXMAFMA (1 << 8) +#define RXCR1_RXBE (1 << 7) +#define RXCR1_RXME (1 << 6) +#define RXCR1_RXUE (1 << 5) +#define RXCR1_RXAE (1 << 4) +#define RXCR1_RXINVF (1 << 1) +#define RXCR1_RXE (1 << 0) +#define RXCR1_FILTER_MASK (RXCR1_RXINVF | RXCR1_RXAE | \ + RXCR1_RXMAFMA | RXCR1_RXPAFMA) + +#define KS_RXCR2 0x76 +#define RXCR2_SRDBL_MASK (0x7 << 5) +#define RXCR2_SRDBL_SHIFT (5) +#define RXCR2_SRDBL_4B (0x0 << 5) +#define RXCR2_SRDBL_8B (0x1 << 5) +#define RXCR2_SRDBL_16B (0x2 << 5) +#define RXCR2_SRDBL_32B (0x3 << 5) +/* #define RXCR2_SRDBL_FRAME (0x4 << 5) */ +#define RXCR2_IUFFP (1 << 4) +#define RXCR2_RXIUFCEZ (1 << 3) +#define RXCR2_UDPLFE (1 << 2) +#define RXCR2_RXICMPFCC (1 << 1) +#define RXCR2_RXSAF (1 << 0) + +#define KS_TXMIR 0x78 + +#define KS_RXFHSR 0x7C +#define RXFSHR_RXFV (1 << 15) +#define RXFSHR_RXICMPFCS (1 << 13) +#define RXFSHR_RXIPFCS (1 << 12) +#define RXFSHR_RXTCPFCS (1 << 11) +#define RXFSHR_RXUDPFCS (1 << 10) +#define RXFSHR_RXBF (1 << 7) +#define RXFSHR_RXMF (1 << 6) +#define RXFSHR_RXUF (1 << 5) +#define RXFSHR_RXMR (1 << 4) +#define RXFSHR_RXFT (1 << 3) +#define RXFSHR_RXFTL (1 << 2) +#define RXFSHR_RXRF (1 << 1) +#define RXFSHR_RXCE (1 << 0) +#define RXFSHR_ERR (RXFSHR_RXCE | RXFSHR_RXRF |\ + RXFSHR_RXFTL | RXFSHR_RXMR |\ + RXFSHR_RXICMPFCS | RXFSHR_RXIPFCS |\ + RXFSHR_RXTCPFCS) +#define KS_RXFHBCR 0x7E +#define RXFHBCR_CNT_MASK 0x0FFF + +#define KS_TXQCR 0x80 +#define TXQCR_AETFE (1 << 2) +#define TXQCR_TXQMAM (1 << 1) +#define TXQCR_METFE (1 << 0) + +#define KS_RXQCR 0x82 +#define RXQCR_RXDTTS (1 << 12) +#define RXQCR_RXDBCTS (1 << 11) +#define RXQCR_RXFCTS (1 << 10) +#define RXQCR_RXIPHTOE (1 << 9) +#define RXQCR_RXDTTE (1 << 7) +#define RXQCR_RXDBCTE (1 << 6) +#define RXQCR_RXFCTE (1 << 5) +#define RXQCR_ADRFE (1 << 4) +#define RXQCR_SDA (1 << 3) +#define RXQCR_RRXEF (1 << 0) +#define RXQCR_CMD_CNTL (RXQCR_RXFCTE|RXQCR_ADRFE) + +#define KS_TXFDPR 0x84 +#define TXFDPR_TXFPAI (1 << 14) +#define TXFDPR_TXFP_MASK (0x7ff << 0) +#define TXFDPR_TXFP_SHIFT (0) + +#define KS_RXFDPR 0x86 +#define RXFDPR_RXFPAI (1 << 14) + +#define KS_RXDTTR 0x8C +#define KS_RXDBCTR 0x8E + +#define KS_IER 0x90 +#define KS_ISR 0x92 +#define IRQ_LCI (1 << 15) +#define IRQ_TXI (1 << 14) +#define IRQ_RXI (1 << 13) +#define IRQ_RXOI (1 << 11) +#define IRQ_TXPSI (1 << 9) +#define IRQ_RXPSI (1 << 8) +#define IRQ_TXSAI (1 << 6) +#define IRQ_RXWFDI (1 << 5) +#define IRQ_RXMPDI (1 << 4) +#define IRQ_LDI (1 << 3) +#define IRQ_EDI (1 << 2) +#define IRQ_SPIBEI (1 << 1) +#define IRQ_DEDI (1 << 0) + +#define KS_RXFCTR 0x9C +#define RXFCTR_THRESHOLD_MASK 0x00FF + +#define KS_RXFC 0x9D +#define RXFCTR_RXFC_MASK (0xff << 8) +#define RXFCTR_RXFC_SHIFT (8) +#define RXFCTR_RXFC_GET(_v) (((_v) >> 8) & 0xff) +#define RXFCTR_RXFCT_MASK (0xff << 0) +#define RXFCTR_RXFCT_SHIFT (0) + +#define KS_TXNTFSR 0x9E + +#define KS_MAHTR0 0xA0 +#define KS_MAHTR1 0xA2 +#define KS_MAHTR2 0xA4 +#define KS_MAHTR3 0xA6 + +#define KS_FCLWR 0xB0 +#define KS_FCHWR 0xB2 +#define KS_FCOWR 0xB4 + +#define KS_CIDER 0xC0 +#define CIDER_ID 0x8870 +#define CIDER_REV_MASK (0x7 << 1) +#define CIDER_REV_SHIFT (1) +#define CIDER_REV_GET(_v) (((_v) >> 1) & 0x7) + +#define KS_CGCR 0xC6 +#define KS_IACR 0xC8 +#define IACR_RDEN (1 << 12) +#define IACR_TSEL_MASK (0x3 << 10) +#define IACR_TSEL_SHIFT (10) +#define IACR_TSEL_MIB (0x3 << 10) +#define IACR_ADDR_MASK (0x1f << 0) +#define IACR_ADDR_SHIFT (0) + +#define KS_IADLR 0xD0 +#define KS_IAHDR 0xD2 + +#define KS_PMECR 0xD4 +#define PMECR_PME_DELAY (1 << 14) +#define PMECR_PME_POL (1 << 12) +#define PMECR_WOL_WAKEUP (1 << 11) +#define PMECR_WOL_MAGICPKT (1 << 10) +#define PMECR_WOL_LINKUP (1 << 9) +#define PMECR_WOL_ENERGY (1 << 8) +#define PMECR_AUTO_WAKE_EN (1 << 7) +#define PMECR_WAKEUP_NORMAL (1 << 6) +#define PMECR_WKEVT_MASK (0xf << 2) +#define PMECR_WKEVT_SHIFT (2) +#define PMECR_WKEVT_GET(_v) (((_v) >> 2) & 0xf) +#define PMECR_WKEVT_ENERGY (0x1 << 2) +#define PMECR_WKEVT_LINK (0x2 << 2) +#define PMECR_WKEVT_MAGICPKT (0x4 << 2) +#define PMECR_WKEVT_FRAME (0x8 << 2) +#define PMECR_PM_MASK (0x3 << 0) +#define PMECR_PM_SHIFT (0) +#define PMECR_PM_NORMAL (0x0 << 0) +#define PMECR_PM_ENERGY (0x1 << 0) +#define PMECR_PM_SOFTDOWN (0x2 << 0) +#define PMECR_PM_POWERSAVE (0x3 << 0) + +/* Standard MII PHY data */ +#define KS_P1MBCR 0xE4 +#define P1MBCR_FORCE_FDX (1 << 8) + +#define KS_P1MBSR 0xE6 +#define P1MBSR_AN_COMPLETE (1 << 5) +#define P1MBSR_AN_CAPABLE (1 << 3) +#define P1MBSR_LINK_UP (1 << 2) + +#define KS_PHY1ILR 0xE8 +#define KS_PHY1IHR 0xEA +#define KS_P1ANAR 0xEC +#define KS_P1ANLPR 0xEE + +#define KS_P1SCLMD 0xF4 +#define P1SCLMD_LEDOFF (1 << 15) +#define P1SCLMD_TXIDS (1 << 14) +#define P1SCLMD_RESTARTAN (1 << 13) +#define P1SCLMD_DISAUTOMDIX (1 << 10) +#define P1SCLMD_FORCEMDIX (1 << 9) +#define P1SCLMD_AUTONEGEN (1 << 7) +#define P1SCLMD_FORCE100 (1 << 6) +#define P1SCLMD_FORCEFDX (1 << 5) +#define P1SCLMD_ADV_FLOW (1 << 4) +#define P1SCLMD_ADV_100BT_FDX (1 << 3) +#define P1SCLMD_ADV_100BT_HDX (1 << 2) +#define P1SCLMD_ADV_10BT_FDX (1 << 1) +#define P1SCLMD_ADV_10BT_HDX (1 << 0) + +#define KS_P1CR 0xF6 +#define P1CR_HP_MDIX (1 << 15) +#define P1CR_REV_POL (1 << 13) +#define P1CR_OP_100M (1 << 10) +#define P1CR_OP_FDX (1 << 9) +#define P1CR_OP_MDI (1 << 7) +#define P1CR_AN_DONE (1 << 6) +#define P1CR_LINK_GOOD (1 << 5) +#define P1CR_PNTR_FLOW (1 << 4) +#define P1CR_PNTR_100BT_FDX (1 << 3) +#define P1CR_PNTR_100BT_HDX (1 << 2) +#define P1CR_PNTR_10BT_FDX (1 << 1) +#define P1CR_PNTR_10BT_HDX (1 << 0) + +/* TX Frame control */ + +#define TXFR_TXIC (1 << 15) +#define TXFR_TXFID_MASK (0x3f << 0) +#define TXFR_TXFID_SHIFT (0) + +#define KS_P1SR 0xF8 +#define P1SR_HP_MDIX (1 << 15) +#define P1SR_REV_POL (1 << 13) +#define P1SR_OP_100M (1 << 10) +#define P1SR_OP_FDX (1 << 9) +#define P1SR_OP_MDI (1 << 7) +#define P1SR_AN_DONE (1 << 6) +#define P1SR_LINK_GOOD (1 << 5) +#define P1SR_PNTR_FLOW (1 << 4) +#define P1SR_PNTR_100BT_FDX (1 << 3) +#define P1SR_PNTR_100BT_HDX (1 << 2) +#define P1SR_PNTR_10BT_FDX (1 << 1) +#define P1SR_PNTR_10BT_HDX (1 << 0) + +#define ENUM_BUS_NONE 0 +#define ENUM_BUS_8BIT 1 +#define ENUM_BUS_16BIT 2 +#define ENUM_BUS_32BIT 3 + +#define MAX_MCAST_LST 32 +#define HW_MCAST_SIZE 8 +#define MAC_ADDR_LEN 6 + +/** + * union ks_tx_hdr - tx header data + * @txb: The header as bytes + * @txw: The header as 16bit, little-endian words + * + * A dual representation of the tx header data to allow + * access to individual bytes, and to allow 16bit accesses + * with 16bit alignment. + */ +union ks_tx_hdr { + u8 txb[4]; + __le16 txw[2]; +}; + +/** + * struct ks_net - KS8851 driver private data + * @net_device : The network device we're bound to + * @hw_addr : start address of data register. + * @hw_addr_cmd : start address of command register. + * @txh : temporaly buffer to save status/length. + * @lock : Lock to ensure that the device is not accessed when busy. + * @pdev : Pointer to platform device. + * @mii : The MII state information for the mii calls. + * @frame_head_info : frame header information for multi-pkt rx. + * @statelock : Lock on this structure for tx list. + * @msg_enable : The message flags controlling driver output (see ethtool). + * @frame_cnt : number of frames received. + * @bus_width : i/o bus width. + * @irq : irq number assigned to this device. + * @rc_rxqcr : Cached copy of KS_RXQCR. + * @rc_txcr : Cached copy of KS_TXCR. + * @rc_ier : Cached copy of KS_IER. + * @sharedbus : Multipex(addr and data bus) mode indicator. + * @cmd_reg_cache : command register cached. + * @cmd_reg_cache_int : command register cached. Used in the irq handler. + * @promiscuous : promiscuous mode indicator. + * @all_mcast : mutlicast indicator. + * @mcast_lst_size : size of multicast list. + * @mcast_lst : multicast list. + * @mcast_bits : multicast enabed. + * @mac_addr : MAC address assigned to this device. + * @fid : frame id. + * @extra_byte : number of extra byte prepended rx pkt. + * @enabled : indicator this device works. + * + * The @lock ensures that the chip is protected when certain operations are + * in progress. When the read or write packet transfer is in progress, most + * of the chip registers are not accessible until the transfer is finished and + * the DMA has been de-asserted. + * + * The @statelock is used to protect information in the structure which may + * need to be accessed via several sources, such as the network driver layer + * or one of the work queues. + * + */ + +/* Receive multiplex framer header info */ +struct type_frame_head { + u16 sts; /* Frame status */ + u16 len; /* Byte count */ +}; + +struct ks_net { + struct net_device *netdev; + void __iomem *hw_addr; + void __iomem *hw_addr_cmd; + union ks_tx_hdr txh ____cacheline_aligned; + struct mutex lock; /* spinlock to be interrupt safe */ + struct platform_device *pdev; + struct mii_if_info mii; + struct type_frame_head *frame_head_info; + spinlock_t statelock; + u32 msg_enable; + u32 frame_cnt; + int bus_width; + int irq; + + u16 rc_rxqcr; + u16 rc_txcr; + u16 rc_ier; + u16 sharedbus; + u16 cmd_reg_cache; + u16 cmd_reg_cache_int; + u16 promiscuous; + u16 all_mcast; + u16 mcast_lst_size; + u8 mcast_lst[MAX_MCAST_LST][MAC_ADDR_LEN]; + u8 mcast_bits[HW_MCAST_SIZE]; + u8 mac_addr[6]; + u8 fid; + u8 extra_byte; + u8 enabled; +}; + +static int msg_enable; + +#define ks_info(_ks, _msg...) dev_info(&(_ks)->pdev->dev, _msg) +#define ks_warn(_ks, _msg...) dev_warn(&(_ks)->pdev->dev, _msg) +#define ks_dbg(_ks, _msg...) dev_dbg(&(_ks)->pdev->dev, _msg) +#define ks_err(_ks, _msg...) dev_err(&(_ks)->pdev->dev, _msg) + +#define BE3 0x8000 /* Byte Enable 3 */ +#define BE2 0x4000 /* Byte Enable 2 */ +#define BE1 0x2000 /* Byte Enable 1 */ +#define BE0 0x1000 /* Byte Enable 0 */ + +/** + * register read/write calls. + * + * All these calls issue transactions to access the chip's registers. They + * all require that the necessary lock is held to prevent accesses when the + * chip is busy transfering packet data (RX/TX FIFO accesses). + */ + +/** + * ks_rdreg8 - read 8 bit register from device + * @ks : The chip information + * @offset: The register address + * + * Read a 8bit register from the chip, returning the result + */ +static u8 ks_rdreg8(struct ks_net *ks, int offset) +{ + u16 data; + u8 shift_bit = offset & 0x03; + u8 shift_data = (offset & 1) << 3; + ks->cmd_reg_cache = (u16) offset | (u16)(BE0 << shift_bit); + iowrite16(ks->cmd_reg_cache, ks->hw_addr_cmd); + data = ioread16(ks->hw_addr); + return (u8)(data >> shift_data); +} + +/** + * ks_rdreg16 - read 16 bit register from device + * @ks : The chip information + * @offset: The register address + * + * Read a 16bit register from the chip, returning the result + */ + +static u16 ks_rdreg16(struct ks_net *ks, int offset) +{ + ks->cmd_reg_cache = (u16)offset | ((BE1 | BE0) << (offset & 0x02)); + iowrite16(ks->cmd_reg_cache, ks->hw_addr_cmd); + return ioread16(ks->hw_addr); +} + +/** + * ks_wrreg8 - write 8bit register value to chip + * @ks: The chip information + * @offset: The register address + * @value: The value to write + * + */ +static void ks_wrreg8(struct ks_net *ks, int offset, u8 value) +{ + u8 shift_bit = (offset & 0x03); + u16 value_write = (u16)(value << ((offset & 1) << 3)); + ks->cmd_reg_cache = (u16)offset | (BE0 << shift_bit); + iowrite16(ks->cmd_reg_cache, ks->hw_addr_cmd); + iowrite16(value_write, ks->hw_addr); +} + +/** + * ks_wrreg16 - write 16bit register value to chip + * @ks: The chip information + * @offset: The register address + * @value: The value to write + * + */ + +static void ks_wrreg16(struct ks_net *ks, int offset, u16 value) +{ + ks->cmd_reg_cache = (u16)offset | ((BE1 | BE0) << (offset & 0x02)); + iowrite16(ks->cmd_reg_cache, ks->hw_addr_cmd); + iowrite16(value, ks->hw_addr); +} + +/** + * ks_inblk - read a block of data from QMU. This is called after sudo DMA mode enabled. + * @ks: The chip state + * @wptr: buffer address to save data + * @len: length in byte to read + * + */ +static inline void ks_inblk(struct ks_net *ks, u16 *wptr, u32 len) +{ + len >>= 1; + while (len--) + *wptr++ = (u16)ioread16(ks->hw_addr); +} + +/** + * ks_outblk - write data to QMU. This is called after sudo DMA mode enabled. + * @ks: The chip information + * @wptr: buffer address + * @len: length in byte to write + * + */ +static inline void ks_outblk(struct ks_net *ks, u16 *wptr, u32 len) +{ + len >>= 1; + while (len--) + iowrite16(*wptr++, ks->hw_addr); +} + +static void ks_disable_int(struct ks_net *ks) +{ + ks_wrreg16(ks, KS_IER, 0x0000); +} /* ks_disable_int */ + +static void ks_enable_int(struct ks_net *ks) +{ + ks_wrreg16(ks, KS_IER, ks->rc_ier); +} /* ks_enable_int */ + +/** + * ks_tx_fifo_space - return the available hardware buffer size. + * @ks: The chip information + * + */ +static inline u16 ks_tx_fifo_space(struct ks_net *ks) +{ + return ks_rdreg16(ks, KS_TXMIR) & 0x1fff; +} + +/** + * ks_save_cmd_reg - save the command register from the cache. + * @ks: The chip information + * + */ +static inline void ks_save_cmd_reg(struct ks_net *ks) +{ + /*ks8851 MLL has a bug to read back the command register. + * So rely on software to save the content of command register. + */ + ks->cmd_reg_cache_int = ks->cmd_reg_cache; +} + +/** + * ks_restore_cmd_reg - restore the command register from the cache and + * write to hardware register. + * @ks: The chip information + * + */ +static inline void ks_restore_cmd_reg(struct ks_net *ks) +{ + ks->cmd_reg_cache = ks->cmd_reg_cache_int; + iowrite16(ks->cmd_reg_cache, ks->hw_addr_cmd); +} + +/** + * ks_set_powermode - set power mode of the device + * @ks: The chip information + * @pwrmode: The power mode value to write to KS_PMECR. + * + * Change the power mode of the chip. + */ +static void ks_set_powermode(struct ks_net *ks, unsigned pwrmode) +{ + unsigned pmecr; + + if (netif_msg_hw(ks)) + ks_dbg(ks, "setting power mode %d\n", pwrmode); + + ks_rdreg16(ks, KS_GRR); + pmecr = ks_rdreg16(ks, KS_PMECR); + pmecr &= ~PMECR_PM_MASK; + pmecr |= pwrmode; + + ks_wrreg16(ks, KS_PMECR, pmecr); +} + +/** + * ks_read_config - read chip configuration of bus width. + * @ks: The chip information + * + */ +static void ks_read_config(struct ks_net *ks) +{ + u16 reg_data = 0; + + /* Regardless of bus width, 8 bit read should always work.*/ + reg_data = ks_rdreg8(ks, KS_CCR) & 0x00FF; + reg_data |= ks_rdreg8(ks, KS_CCR+1) << 8; + + /* addr/data bus are multiplexed */ + ks->sharedbus = (reg_data & CCR_SHARED) == CCR_SHARED; + + /* There are garbage data when reading data from QMU, + depending on bus-width. + */ + + if (reg_data & CCR_8BIT) { + ks->bus_width = ENUM_BUS_8BIT; + ks->extra_byte = 1; + } else if (reg_data & CCR_16BIT) { + ks->bus_width = ENUM_BUS_16BIT; + ks->extra_byte = 2; + } else { + ks->bus_width = ENUM_BUS_32BIT; + ks->extra_byte = 4; + } +} + +/** + * ks_soft_reset - issue one of the soft reset to the device + * @ks: The device state. + * @op: The bit(s) to set in the GRR + * + * Issue the relevant soft-reset command to the device's GRR register + * specified by @op. + * + * Note, the delays are in there as a caution to ensure that the reset + * has time to take effect and then complete. Since the datasheet does + * not currently specify the exact sequence, we have chosen something + * that seems to work with our device. + */ +static void ks_soft_reset(struct ks_net *ks, unsigned op) +{ + /* Disable interrupt first */ + ks_wrreg16(ks, KS_IER, 0x0000); + ks_wrreg16(ks, KS_GRR, op); + mdelay(10); /* wait a short time to effect reset */ + ks_wrreg16(ks, KS_GRR, 0); + mdelay(1); /* wait for condition to clear */ +} + + +void ks_enable_qmu(struct ks_net *ks) +{ + u16 w; + + w = ks_rdreg16(ks, KS_TXCR); + /* Enables QMU Transmit (TXCR). */ + ks_wrreg16(ks, KS_TXCR, w | TXCR_TXE); + + /* + * RX Frame Count Threshold Enable and Auto-Dequeue RXQ Frame + * Enable + */ + + w = ks_rdreg16(ks, KS_RXQCR); + ks_wrreg16(ks, KS_RXQCR, w | RXQCR_RXFCTE); + + /* Enables QMU Receive (RXCR1). */ + w = ks_rdreg16(ks, KS_RXCR1); + ks_wrreg16(ks, KS_RXCR1, w | RXCR1_RXE); + ks->enabled = true; +} /* ks_enable_qmu */ + +static void ks_disable_qmu(struct ks_net *ks) +{ + u16 w; + + w = ks_rdreg16(ks, KS_TXCR); + + /* Disables QMU Transmit (TXCR). */ + w &= ~TXCR_TXE; + ks_wrreg16(ks, KS_TXCR, w); + + /* Disables QMU Receive (RXCR1). */ + w = ks_rdreg16(ks, KS_RXCR1); + w &= ~RXCR1_RXE ; + ks_wrreg16(ks, KS_RXCR1, w); + + ks->enabled = false; + +} /* ks_disable_qmu */ + +/** + * ks_read_qmu - read 1 pkt data from the QMU. + * @ks: The chip information + * @buf: buffer address to save 1 pkt + * @len: Pkt length + * Here is the sequence to read 1 pkt: + * 1. set sudo DMA mode + * 2. read prepend data + * 3. read pkt data + * 4. reset sudo DMA Mode + */ +static inline void ks_read_qmu(struct ks_net *ks, u16 *buf, u32 len) +{ + u32 r = ks->extra_byte & 0x1 ; + u32 w = ks->extra_byte - r; + + /* 1. set sudo DMA mode */ + ks_wrreg16(ks, KS_RXFDPR, RXFDPR_RXFPAI); + ks_wrreg8(ks, KS_RXQCR, (ks->rc_rxqcr | RXQCR_SDA) & 0xff); + + /* 2. read prepend data */ + /** + * read 4 + extra bytes and discard them. + * extra bytes for dummy, 2 for status, 2 for len + */ + + /* use likely(r) for 8 bit access for performance */ + if (unlikely(r)) + ioread8(ks->hw_addr); + ks_inblk(ks, buf, w + 2 + 2); + + /* 3. read pkt data */ + ks_inblk(ks, buf, ALIGN(len, 4)); + + /* 4. reset sudo DMA Mode */ + ks_wrreg8(ks, KS_RXQCR, ks->rc_rxqcr); +} + +/** + * ks_rcv - read multiple pkts data from the QMU. + * @ks: The chip information + * @netdev: The network device being opened. + * + * Read all of header information before reading pkt content. + * It is not allowed only port of pkts in QMU after issuing + * interrupt ack. + */ +static void ks_rcv(struct ks_net *ks, struct net_device *netdev) +{ + u32 i; + struct type_frame_head *frame_hdr = ks->frame_head_info; + struct sk_buff *skb; + + ks->frame_cnt = ks_rdreg16(ks, KS_RXFCTR) >> 8; + + /* read all header information */ + for (i = 0; i < ks->frame_cnt; i++) { + /* Checking Received packet status */ + frame_hdr->sts = ks_rdreg16(ks, KS_RXFHSR); + /* Get packet len from hardware */ + frame_hdr->len = ks_rdreg16(ks, KS_RXFHBCR); + frame_hdr++; + } + + frame_hdr = ks->frame_head_info; + while (ks->frame_cnt--) { + skb = dev_alloc_skb(frame_hdr->len + 16); + if (likely(skb && (frame_hdr->sts & RXFSHR_RXFV) && + (frame_hdr->len < RX_BUF_SIZE) && frame_hdr->len)) { + skb_reserve(skb, 2); + /* read data block including CRC 4 bytes */ + ks_read_qmu(ks, (u16 *)skb->data, frame_hdr->len); + skb_put(skb, frame_hdr->len); + skb->dev = netdev; + skb->protocol = eth_type_trans(skb, netdev); + netif_rx(skb); + } else { + printk(KERN_ERR "%s: err:skb alloc\n", __func__); + ks_wrreg16(ks, KS_RXQCR, (ks->rc_rxqcr | RXQCR_RRXEF)); + if (skb) + dev_kfree_skb_irq(skb); + } + frame_hdr++; + } +} + +/** + * ks_update_link_status - link status update. + * @netdev: The network device being opened. + * @ks: The chip information + * + */ + +static void ks_update_link_status(struct net_device *netdev, struct ks_net *ks) +{ + /* check the status of the link */ + u32 link_up_status; + if (ks_rdreg16(ks, KS_P1SR) & P1SR_LINK_GOOD) { + netif_carrier_on(netdev); + link_up_status = true; + } else { + netif_carrier_off(netdev); + link_up_status = false; + } + if (netif_msg_link(ks)) + ks_dbg(ks, "%s: %s\n", + __func__, link_up_status ? "UP" : "DOWN"); +} + +/** + * ks_irq - device interrupt handler + * @irq: Interrupt number passed from the IRQ hnalder. + * @pw: The private word passed to register_irq(), our struct ks_net. + * + * This is the handler invoked to find out what happened + * + * Read the interrupt status, work out what needs to be done and then clear + * any of the interrupts that are not needed. + */ + +static irqreturn_t ks_irq(int irq, void *pw) +{ + struct ks_net *ks = pw; + struct net_device *netdev = ks->netdev; + u16 status; + + /*this should be the first in IRQ handler */ + ks_save_cmd_reg(ks); + + status = ks_rdreg16(ks, KS_ISR); + if (unlikely(!status)) { + ks_restore_cmd_reg(ks); + return IRQ_NONE; + } + + ks_wrreg16(ks, KS_ISR, status); + + if (likely(status & IRQ_RXI)) + ks_rcv(ks, netdev); + + if (unlikely(status & IRQ_LCI)) + ks_update_link_status(netdev, ks); + + if (unlikely(status & IRQ_TXI)) + netif_wake_queue(netdev); + + if (unlikely(status & IRQ_LDI)) { + + u16 pmecr = ks_rdreg16(ks, KS_PMECR); + pmecr &= ~PMECR_WKEVT_MASK; + ks_wrreg16(ks, KS_PMECR, pmecr | PMECR_WKEVT_LINK); + } + + /* this should be the last in IRQ handler*/ + ks_restore_cmd_reg(ks); + return IRQ_HANDLED; +} + + +/** + * ks_net_open - open network device + * @netdev: The network device being opened. + * + * Called when the network device is marked active, such as a user executing + * 'ifconfig up' on the device. + */ +static int ks_net_open(struct net_device *netdev) +{ + struct ks_net *ks = netdev_priv(netdev); + int err; + +#define KS_INT_FLAGS (IRQF_DISABLED|IRQF_TRIGGER_LOW) + /* lock the card, even if we may not actually do anything + * else at the moment. + */ + + if (netif_msg_ifup(ks)) + ks_dbg(ks, "%s - entry\n", __func__); + + /* reset the HW */ + err = request_irq(ks->irq, ks_irq, KS_INT_FLAGS, DRV_NAME, netdev); + + if (err) { + printk(KERN_ERR "Failed to request IRQ: %d: %d\n", + ks->irq, err); + return err; + } + + /* wake up powermode to normal mode */ + ks_set_powermode(ks, PMECR_PM_NORMAL); + mdelay(1); /* wait for normal mode to take effect */ + + ks_wrreg16(ks, KS_ISR, 0xffff); + ks_enable_int(ks); + ks_enable_qmu(ks); + netif_start_queue(ks->netdev); + + if (netif_msg_ifup(ks)) + ks_dbg(ks, "network device %s up\n", netdev->name); + + return 0; +} + +/** + * ks_net_stop - close network device + * @netdev: The device being closed. + * + * Called to close down a network device which has been active. Cancell any + * work, shutdown the RX and TX process and then place the chip into a low + * power state whilst it is not being used. + */ +static int ks_net_stop(struct net_device *netdev) +{ + struct ks_net *ks = netdev_priv(netdev); + + if (netif_msg_ifdown(ks)) + ks_info(ks, "%s: shutting down\n", netdev->name); + + netif_stop_queue(netdev); + + mutex_lock(&ks->lock); + + /* turn off the IRQs and ack any outstanding */ + ks_wrreg16(ks, KS_IER, 0x0000); + ks_wrreg16(ks, KS_ISR, 0xffff); + + /* shutdown RX/TX QMU */ + ks_disable_qmu(ks); + + /* set powermode to soft power down to save power */ + ks_set_powermode(ks, PMECR_PM_SOFTDOWN); + free_irq(ks->irq, netdev); + mutex_unlock(&ks->lock); + return 0; +} + + +/** + * ks_write_qmu - write 1 pkt data to the QMU. + * @ks: The chip information + * @pdata: buffer address to save 1 pkt + * @len: Pkt length in byte + * Here is the sequence to write 1 pkt: + * 1. set sudo DMA mode + * 2. write status/length + * 3. write pkt data + * 4. reset sudo DMA Mode + * 5. reset sudo DMA mode + * 6. Wait until pkt is out + */ +static void ks_write_qmu(struct ks_net *ks, u8 *pdata, u16 len) +{ + /* start header at txb[0] to align txw entries */ + ks->txh.txw[0] = 0; + ks->txh.txw[1] = cpu_to_le16(len); + + /* 1. set sudo-DMA mode */ + ks_wrreg8(ks, KS_RXQCR, (ks->rc_rxqcr | RXQCR_SDA) & 0xff); + /* 2. write status/lenth info */ + ks_outblk(ks, ks->txh.txw, 4); + /* 3. write pkt data */ + ks_outblk(ks, (u16 *)pdata, ALIGN(len, 4)); + /* 4. reset sudo-DMA mode */ + ks_wrreg8(ks, KS_RXQCR, ks->rc_rxqcr); + /* 5. Enqueue Tx(move the pkt from TX buffer into TXQ) */ + ks_wrreg16(ks, KS_TXQCR, TXQCR_METFE); + /* 6. wait until TXQCR_METFE is auto-cleared */ + while (ks_rdreg16(ks, KS_TXQCR) & TXQCR_METFE) + ; +} + +/** + * ks_start_xmit - transmit packet + * @skb : The buffer to transmit + * @netdev : The device used to transmit the packet. + * + * Called by the network layer to transmit the @skb. + * spin_lock_irqsave is required because tx and rx should be mutual exclusive. + * So while tx is in-progress, prevent IRQ interrupt from happenning. + */ +static int ks_start_xmit(struct sk_buff *skb, struct net_device *netdev) +{ + int retv = NETDEV_TX_OK; + struct ks_net *ks = netdev_priv(netdev); + + disable_irq(netdev->irq); + ks_disable_int(ks); + spin_lock(&ks->statelock); + + /* Extra space are required: + * 4 byte for alignment, 4 for status/length, 4 for CRC + */ + + if (likely(ks_tx_fifo_space(ks) >= skb->len + 12)) { + ks_write_qmu(ks, skb->data, skb->len); + dev_kfree_skb(skb); + } else + retv = NETDEV_TX_BUSY; + spin_unlock(&ks->statelock); + ks_enable_int(ks); + enable_irq(netdev->irq); + return retv; +} + +/** + * ks_start_rx - ready to serve pkts + * @ks : The chip information + * + */ +static void ks_start_rx(struct ks_net *ks) +{ + u16 cntl; + + /* Enables QMU Receive (RXCR1). */ + cntl = ks_rdreg16(ks, KS_RXCR1); + cntl |= RXCR1_RXE ; + ks_wrreg16(ks, KS_RXCR1, cntl); +} /* ks_start_rx */ + +/** + * ks_stop_rx - stop to serve pkts + * @ks : The chip information + * + */ +static void ks_stop_rx(struct ks_net *ks) +{ + u16 cntl; + + /* Disables QMU Receive (RXCR1). */ + cntl = ks_rdreg16(ks, KS_RXCR1); + cntl &= ~RXCR1_RXE ; + ks_wrreg16(ks, KS_RXCR1, cntl); + +} /* ks_stop_rx */ + +static unsigned long const ethernet_polynomial = 0x04c11db7U; + +static unsigned long ether_gen_crc(int length, u8 *data) +{ + long crc = -1; + while (--length >= 0) { + u8 current_octet = *data++; + int bit; + + for (bit = 0; bit < 8; bit++, current_octet >>= 1) { + crc = (crc << 1) ^ + ((crc < 0) ^ (current_octet & 1) ? + ethernet_polynomial : 0); + } + } + return (unsigned long)crc; +} /* ether_gen_crc */ + +/** +* ks_set_grpaddr - set multicast information +* @ks : The chip information +*/ + +static void ks_set_grpaddr(struct ks_net *ks) +{ + u8 i; + u32 index, position, value; + + memset(ks->mcast_bits, 0, sizeof(u8) * HW_MCAST_SIZE); + + for (i = 0; i < ks->mcast_lst_size; i++) { + position = (ether_gen_crc(6, ks->mcast_lst[i]) >> 26) & 0x3f; + index = position >> 3; + value = 1 << (position & 7); + ks->mcast_bits[index] |= (u8)value; + } + + for (i = 0; i < HW_MCAST_SIZE; i++) { + if (i & 1) { + ks_wrreg16(ks, (u16)((KS_MAHTR0 + i) & ~1), + (ks->mcast_bits[i] << 8) | + ks->mcast_bits[i - 1]); + } + } +} /* ks_set_grpaddr */ + +/* +* ks_clear_mcast - clear multicast information +* +* @ks : The chip information +* This routine removes all mcast addresses set in the hardware. +*/ + +static void ks_clear_mcast(struct ks_net *ks) +{ + u16 i, mcast_size; + for (i = 0; i < HW_MCAST_SIZE; i++) + ks->mcast_bits[i] = 0; + + mcast_size = HW_MCAST_SIZE >> 2; + for (i = 0; i < mcast_size; i++) + ks_wrreg16(ks, KS_MAHTR0 + (2*i), 0); +} + +static void ks_set_promis(struct ks_net *ks, u16 promiscuous_mode) +{ + u16 cntl; + ks->promiscuous = promiscuous_mode; + ks_stop_rx(ks); /* Stop receiving for reconfiguration */ + cntl = ks_rdreg16(ks, KS_RXCR1); + + cntl &= ~RXCR1_FILTER_MASK; + if (promiscuous_mode) + /* Enable Promiscuous mode */ + cntl |= RXCR1_RXAE | RXCR1_RXINVF; + else + /* Disable Promiscuous mode (default normal mode) */ + cntl |= RXCR1_RXPAFMA; + + ks_wrreg16(ks, KS_RXCR1, cntl); + + if (ks->enabled) + ks_start_rx(ks); + +} /* ks_set_promis */ + +static void ks_set_mcast(struct ks_net *ks, u16 mcast) +{ + u16 cntl; + + ks->all_mcast = mcast; + ks_stop_rx(ks); /* Stop receiving for reconfiguration */ + cntl = ks_rdreg16(ks, KS_RXCR1); + cntl &= ~RXCR1_FILTER_MASK; + if (mcast) + /* Enable "Perfect with Multicast address passed mode" */ + cntl |= (RXCR1_RXAE | RXCR1_RXMAFMA | RXCR1_RXPAFMA); + else + /** + * Disable "Perfect with Multicast address passed + * mode" (normal mode). + */ + cntl |= RXCR1_RXPAFMA; + + ks_wrreg16(ks, KS_RXCR1, cntl); + + if (ks->enabled) + ks_start_rx(ks); +} /* ks_set_mcast */ + +static void ks_set_rx_mode(struct net_device *netdev) +{ + struct ks_net *ks = netdev_priv(netdev); + struct dev_mc_list *ptr; + + /* Turn on/off promiscuous mode. */ + if ((netdev->flags & IFF_PROMISC) == IFF_PROMISC) + ks_set_promis(ks, + (u16)((netdev->flags & IFF_PROMISC) == IFF_PROMISC)); + /* Turn on/off all mcast mode. */ + else if ((netdev->flags & IFF_ALLMULTI) == IFF_ALLMULTI) + ks_set_mcast(ks, + (u16)((netdev->flags & IFF_ALLMULTI) == IFF_ALLMULTI)); + else + ks_set_promis(ks, false); + + if ((netdev->flags & IFF_MULTICAST) && netdev->mc_count) { + if (netdev->mc_count <= MAX_MCAST_LST) { + int i = 0; + for (ptr = netdev->mc_list; ptr; ptr = ptr->next) { + if (!(*ptr->dmi_addr & 1)) + continue; + if (i >= MAX_MCAST_LST) + break; + memcpy(ks->mcast_lst[i++], ptr->dmi_addr, + MAC_ADDR_LEN); + } + ks->mcast_lst_size = (u8)i; + ks_set_grpaddr(ks); + } else { + /** + * List too big to support so + * turn on all mcast mode. + */ + ks->mcast_lst_size = MAX_MCAST_LST; + ks_set_mcast(ks, true); + } + } else { + ks->mcast_lst_size = 0; + ks_clear_mcast(ks); + } +} /* ks_set_rx_mode */ + +static void ks_set_mac(struct ks_net *ks, u8 *data) +{ + u16 *pw = (u16 *)data; + u16 w, u; + + ks_stop_rx(ks); /* Stop receiving for reconfiguration */ + + u = *pw++; + w = ((u & 0xFF) << 8) | ((u >> 8) & 0xFF); + ks_wrreg16(ks, KS_MARH, w); + + u = *pw++; + w = ((u & 0xFF) << 8) | ((u >> 8) & 0xFF); + ks_wrreg16(ks, KS_MARM, w); + + u = *pw; + w = ((u & 0xFF) << 8) | ((u >> 8) & 0xFF); + ks_wrreg16(ks, KS_MARL, w); + + memcpy(ks->mac_addr, data, 6); + + if (ks->enabled) + ks_start_rx(ks); +} + +static int ks_set_mac_address(struct net_device *netdev, void *paddr) +{ + struct ks_net *ks = netdev_priv(netdev); + struct sockaddr *addr = paddr; + u8 *da; + + memcpy(netdev->dev_addr, addr->sa_data, netdev->addr_len); + + da = (u8 *)netdev->dev_addr; + + ks_set_mac(ks, da); + return 0; +} + +static int ks_net_ioctl(struct net_device *netdev, struct ifreq *req, int cmd) +{ + struct ks_net *ks = netdev_priv(netdev); + + if (!netif_running(netdev)) + return -EINVAL; + + return generic_mii_ioctl(&ks->mii, if_mii(req), cmd, NULL); +} + +static const struct net_device_ops ks_netdev_ops = { + .ndo_open = ks_net_open, + .ndo_stop = ks_net_stop, + .ndo_do_ioctl = ks_net_ioctl, + .ndo_start_xmit = ks_start_xmit, + .ndo_set_mac_address = ks_set_mac_address, + .ndo_set_rx_mode = ks_set_rx_mode, + .ndo_change_mtu = eth_change_mtu, + .ndo_validate_addr = eth_validate_addr, +}; + +/* ethtool support */ + +static void ks_get_drvinfo(struct net_device *netdev, + struct ethtool_drvinfo *di) +{ + strlcpy(di->driver, DRV_NAME, sizeof(di->driver)); + strlcpy(di->version, "1.00", sizeof(di->version)); + strlcpy(di->bus_info, dev_name(netdev->dev.parent), + sizeof(di->bus_info)); +} + +static u32 ks_get_msglevel(struct net_device *netdev) +{ + struct ks_net *ks = netdev_priv(netdev); + return ks->msg_enable; +} + +static void ks_set_msglevel(struct net_device *netdev, u32 to) +{ + struct ks_net *ks = netdev_priv(netdev); + ks->msg_enable = to; +} + +static int ks_get_settings(struct net_device *netdev, struct ethtool_cmd *cmd) +{ + struct ks_net *ks = netdev_priv(netdev); + return mii_ethtool_gset(&ks->mii, cmd); +} + +static int ks_set_settings(struct net_device *netdev, struct ethtool_cmd *cmd) +{ + struct ks_net *ks = netdev_priv(netdev); + return mii_ethtool_sset(&ks->mii, cmd); +} + +static u32 ks_get_link(struct net_device *netdev) +{ + struct ks_net *ks = netdev_priv(netdev); + return mii_link_ok(&ks->mii); +} + +static int ks_nway_reset(struct net_device *netdev) +{ + struct ks_net *ks = netdev_priv(netdev); + return mii_nway_restart(&ks->mii); +} + +static const struct ethtool_ops ks_ethtool_ops = { + .get_drvinfo = ks_get_drvinfo, + .get_msglevel = ks_get_msglevel, + .set_msglevel = ks_set_msglevel, + .get_settings = ks_get_settings, + .set_settings = ks_set_settings, + .get_link = ks_get_link, + .nway_reset = ks_nway_reset, +}; + +/* MII interface controls */ + +/** + * ks_phy_reg - convert MII register into a KS8851 register + * @reg: MII register number. + * + * Return the KS8851 register number for the corresponding MII PHY register + * if possible. Return zero if the MII register has no direct mapping to the + * KS8851 register set. + */ +static int ks_phy_reg(int reg) +{ + switch (reg) { + case MII_BMCR: + return KS_P1MBCR; + case MII_BMSR: + return KS_P1MBSR; + case MII_PHYSID1: + return KS_PHY1ILR; + case MII_PHYSID2: + return KS_PHY1IHR; + case MII_ADVERTISE: + return KS_P1ANAR; + case MII_LPA: + return KS_P1ANLPR; + } + + return 0x0; +} + +/** + * ks_phy_read - MII interface PHY register read. + * @netdev: The network device the PHY is on. + * @phy_addr: Address of PHY (ignored as we only have one) + * @reg: The register to read. + * + * This call reads data from the PHY register specified in @reg. Since the + * device does not support all the MII registers, the non-existant values + * are always returned as zero. + * + * We return zero for unsupported registers as the MII code does not check + * the value returned for any error status, and simply returns it to the + * caller. The mii-tool that the driver was tested with takes any -ve error + * as real PHY capabilities, thus displaying incorrect data to the user. + */ +static int ks_phy_read(struct net_device *netdev, int phy_addr, int reg) +{ + struct ks_net *ks = netdev_priv(netdev); + int ksreg; + int result; + + ksreg = ks_phy_reg(reg); + if (!ksreg) + return 0x0; /* no error return allowed, so use zero */ + + mutex_lock(&ks->lock); + result = ks_rdreg16(ks, ksreg); + mutex_unlock(&ks->lock); + + return result; +} + +static void ks_phy_write(struct net_device *netdev, + int phy, int reg, int value) +{ + struct ks_net *ks = netdev_priv(netdev); + int ksreg; + + ksreg = ks_phy_reg(reg); + if (ksreg) { + mutex_lock(&ks->lock); + ks_wrreg16(ks, ksreg, value); + mutex_unlock(&ks->lock); + } +} + +/** + * ks_read_selftest - read the selftest memory info. + * @ks: The device state + * + * Read and check the TX/RX memory selftest information. + */ +static int ks_read_selftest(struct ks_net *ks) +{ + unsigned both_done = MBIR_TXMBF | MBIR_RXMBF; + int ret = 0; + unsigned rd; + + rd = ks_rdreg16(ks, KS_MBIR); + + if ((rd & both_done) != both_done) { + ks_warn(ks, "Memory selftest not finished\n"); + return 0; + } + + if (rd & MBIR_TXMBFA) { + ks_err(ks, "TX memory selftest fails\n"); + ret |= 1; + } + + if (rd & MBIR_RXMBFA) { + ks_err(ks, "RX memory selftest fails\n"); + ret |= 2; + } + + ks_info(ks, "the selftest passes\n"); + return ret; +} + +static void ks_setup(struct ks_net *ks) +{ + u16 w; + + /** + * Configure QMU Transmit + */ + + /* Setup Transmit Frame Data Pointer Auto-Increment (TXFDPR) */ + ks_wrreg16(ks, KS_TXFDPR, TXFDPR_TXFPAI); + + /* Setup Receive Frame Data Pointer Auto-Increment */ + ks_wrreg16(ks, KS_RXFDPR, RXFDPR_RXFPAI); + + /* Setup Receive Frame Threshold - 1 frame (RXFCTFC) */ + ks_wrreg16(ks, KS_RXFCTR, 1 & RXFCTR_THRESHOLD_MASK); + + /* Setup RxQ Command Control (RXQCR) */ + ks->rc_rxqcr = RXQCR_CMD_CNTL; + ks_wrreg16(ks, KS_RXQCR, ks->rc_rxqcr); + + /** + * set the force mode to half duplex, default is full duplex + * because if the auto-negotiation fails, most switch uses + * half-duplex. + */ + + w = ks_rdreg16(ks, KS_P1MBCR); + w &= ~P1MBCR_FORCE_FDX; + ks_wrreg16(ks, KS_P1MBCR, w); + + w = TXCR_TXFCE | TXCR_TXPE | TXCR_TXCRC | TXCR_TCGIP; + ks_wrreg16(ks, KS_TXCR, w); + + w = RXCR1_RXFCE | RXCR1_RXBE | RXCR1_RXUE | RXCR1_RXME | RXCR1_RXIPFCC; + + if (ks->promiscuous) /* bPromiscuous */ + w |= (RXCR1_RXAE | RXCR1_RXINVF); + else if (ks->all_mcast) /* Multicast address passed mode */ + w |= (RXCR1_RXAE | RXCR1_RXMAFMA | RXCR1_RXPAFMA); + else /* Normal mode */ + w |= RXCR1_RXPAFMA; + + ks_wrreg16(ks, KS_RXCR1, w); +} /*ks_setup */ + + +static void ks_setup_int(struct ks_net *ks) +{ + ks->rc_ier = 0x00; + /* Clear the interrupts status of the hardware. */ + ks_wrreg16(ks, KS_ISR, 0xffff); + + /* Enables the interrupts of the hardware. */ + ks->rc_ier = (IRQ_LCI | IRQ_TXI | IRQ_RXI); +} /* ks_setup_int */ + +static int ks_hw_init(struct ks_net *ks) +{ +#define MHEADER_SIZE (sizeof(struct type_frame_head) * MAX_RECV_FRAMES) + ks->promiscuous = 0; + ks->all_mcast = 0; + ks->mcast_lst_size = 0; + + ks->frame_head_info = (struct type_frame_head *) \ + kmalloc(MHEADER_SIZE, GFP_KERNEL); + if (!ks->frame_head_info) { + printk(KERN_ERR "Error: Fail to allocate frame memory\n"); + return false; + } + + ks_set_mac(ks, KS_DEFAULT_MAC_ADDRESS); + return true; +} + + +static int __devinit ks8851_probe(struct platform_device *pdev) +{ + int err = -ENOMEM; + struct resource *io_d, *io_c; + struct net_device *netdev; + struct ks_net *ks; + u16 id, data; + + io_d = platform_get_resource(pdev, IORESOURCE_MEM, 0); + io_c = platform_get_resource(pdev, IORESOURCE_MEM, 1); + + if (!request_mem_region(io_d->start, resource_size(io_d), DRV_NAME)) + goto err_mem_region; + + if (!request_mem_region(io_c->start, resource_size(io_c), DRV_NAME)) + goto err_mem_region1; + + netdev = alloc_etherdev(sizeof(struct ks_net)); + if (!netdev) + goto err_alloc_etherdev; + + SET_NETDEV_DEV(netdev, &pdev->dev); + + ks = netdev_priv(netdev); + ks->netdev = netdev; + ks->hw_addr = ioremap(io_d->start, resource_size(io_d)); + + if (!ks->hw_addr) + goto err_ioremap; + + ks->hw_addr_cmd = ioremap(io_c->start, resource_size(io_c)); + if (!ks->hw_addr_cmd) + goto err_ioremap1; + + ks->irq = platform_get_irq(pdev, 0); + + if (ks->irq < 0) { + err = ks->irq; + goto err_get_irq; + } + + ks->pdev = pdev; + + mutex_init(&ks->lock); + spin_lock_init(&ks->statelock); + + netdev->netdev_ops = &ks_netdev_ops; + netdev->ethtool_ops = &ks_ethtool_ops; + + /* setup mii state */ + ks->mii.dev = netdev; + ks->mii.phy_id = 1, + ks->mii.phy_id_mask = 1; + ks->mii.reg_num_mask = 0xf; + ks->mii.mdio_read = ks_phy_read; + ks->mii.mdio_write = ks_phy_write; + + ks_info(ks, "message enable is %d\n", msg_enable); + /* set the default message enable */ + ks->msg_enable = netif_msg_init(msg_enable, (NETIF_MSG_DRV | + NETIF_MSG_PROBE | + NETIF_MSG_LINK)); + ks_read_config(ks); + + /* simple check for a valid chip being connected to the bus */ + if ((ks_rdreg16(ks, KS_CIDER) & ~CIDER_REV_MASK) != CIDER_ID) { + ks_err(ks, "failed to read device ID\n"); + err = -ENODEV; + goto err_register; + } + + if (ks_read_selftest(ks)) { + ks_err(ks, "failed to read device ID\n"); + err = -ENODEV; + goto err_register; + } + + err = register_netdev(netdev); + if (err) + goto err_register; + + platform_set_drvdata(pdev, netdev); + + ks_soft_reset(ks, GRR_GSR); + ks_hw_init(ks); + ks_disable_qmu(ks); + ks_setup(ks); + ks_setup_int(ks); + memcpy(netdev->dev_addr, ks->mac_addr, 6); + + data = ks_rdreg16(ks, KS_OBCR); + ks_wrreg16(ks, KS_OBCR, data | OBCR_ODS_16MA); + + /** + * If you want to use the default MAC addr, + * comment out the 2 functions below. + */ + + random_ether_addr(netdev->dev_addr); + ks_set_mac(ks, netdev->dev_addr); + + id = ks_rdreg16(ks, KS_CIDER); + + printk(KERN_INFO DRV_NAME + " Found chip, family: 0x%x, id: 0x%x, rev: 0x%x\n", + (id >> 8) & 0xff, (id >> 4) & 0xf, (id >> 1) & 0x7); + return 0; + +err_register: +err_get_irq: + iounmap(ks->hw_addr_cmd); +err_ioremap1: + iounmap(ks->hw_addr); +err_ioremap: + free_netdev(netdev); +err_alloc_etherdev: + release_mem_region(io_c->start, resource_size(io_c)); +err_mem_region1: + release_mem_region(io_d->start, resource_size(io_d)); +err_mem_region: + return err; +} + +static int __devexit ks8851_remove(struct platform_device *pdev) +{ + struct net_device *netdev = platform_get_drvdata(pdev); + struct ks_net *ks = netdev_priv(netdev); + struct resource *iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + + kfree(ks->frame_head_info); + unregister_netdev(netdev); + iounmap(ks->hw_addr); + free_netdev(netdev); + release_mem_region(iomem->start, resource_size(iomem)); + platform_set_drvdata(pdev, NULL); + return 0; + +} + +static struct platform_driver ks8851_platform_driver = { + .driver = { + .name = DRV_NAME, + .owner = THIS_MODULE, + }, + .probe = ks8851_probe, + .remove = __devexit_p(ks8851_remove), +}; + +static int __init ks8851_init(void) +{ + return platform_driver_register(&ks8851_platform_driver); +} + +static void __exit ks8851_exit(void) +{ + platform_driver_unregister(&ks8851_platform_driver); +} + +module_init(ks8851_init); +module_exit(ks8851_exit); + +MODULE_DESCRIPTION("KS8851 MLL Network driver"); +MODULE_AUTHOR("David Choi <david.choi@micrel.com>"); +MODULE_LICENSE("GPL"); +module_param_named(message, msg_enable, int, 0); +MODULE_PARM_DESC(message, "Message verbosity level (0=none, 31=all)"); + diff --git a/drivers/net/macsonic.c b/drivers/net/macsonic.c index 61eabcac734c..b3d7d8d77f46 100644 --- a/drivers/net/macsonic.c +++ b/drivers/net/macsonic.c @@ -223,69 +223,73 @@ static int __devinit macsonic_init(struct net_device *dev) return 0; } -static int __devinit mac_onboard_sonic_ethernet_addr(struct net_device *dev) +#define INVALID_MAC(mac) (memcmp(mac, "\x08\x00\x07", 3) && \ + memcmp(mac, "\x00\xA0\x40", 3) && \ + memcmp(mac, "\x00\x80\x19", 3) && \ + memcmp(mac, "\x00\x05\x02", 3)) + +static void __devinit mac_onboard_sonic_ethernet_addr(struct net_device *dev) { struct sonic_local *lp = netdev_priv(dev); const int prom_addr = ONBOARD_SONIC_PROM_BASE; - int i; + unsigned short val; - /* On NuBus boards we can sometimes look in the ROM resources. - No such luck for comm-slot/onboard. */ - for(i = 0; i < 6; i++) - dev->dev_addr[i] = SONIC_READ_PROM(i); + /* + * On NuBus boards we can sometimes look in the ROM resources. + * No such luck for comm-slot/onboard. + * On the PowerBook 520, the PROM base address is a mystery. + */ + if (hwreg_present((void *)prom_addr)) { + int i; + + for (i = 0; i < 6; i++) + dev->dev_addr[i] = SONIC_READ_PROM(i); + if (!INVALID_MAC(dev->dev_addr)) + return; - /* Most of the time, the address is bit-reversed. The NetBSD - source has a rather long and detailed historical account of - why this is so. */ - if (memcmp(dev->dev_addr, "\x08\x00\x07", 3) && - memcmp(dev->dev_addr, "\x00\xA0\x40", 3) && - memcmp(dev->dev_addr, "\x00\x80\x19", 3) && - memcmp(dev->dev_addr, "\x00\x05\x02", 3)) + /* + * Most of the time, the address is bit-reversed. The NetBSD + * source has a rather long and detailed historical account of + * why this is so. + */ bit_reverse_addr(dev->dev_addr); - else - return 0; - - /* If we still have what seems to be a bogus address, we'll - look in the CAM. The top entry should be ours. */ - /* Danger! This only works if MacOS has already initialized - the card... */ - if (memcmp(dev->dev_addr, "\x08\x00\x07", 3) && - memcmp(dev->dev_addr, "\x00\xA0\x40", 3) && - memcmp(dev->dev_addr, "\x00\x80\x19", 3) && - memcmp(dev->dev_addr, "\x00\x05\x02", 3)) - { - unsigned short val; - - printk(KERN_INFO "macsonic: PROM seems to be wrong, trying CAM entry 15\n"); - - SONIC_WRITE(SONIC_CMD, SONIC_CR_RST); - SONIC_WRITE(SONIC_CEP, 15); - - val = SONIC_READ(SONIC_CAP2); - dev->dev_addr[5] = val >> 8; - dev->dev_addr[4] = val & 0xff; - val = SONIC_READ(SONIC_CAP1); - dev->dev_addr[3] = val >> 8; - dev->dev_addr[2] = val & 0xff; - val = SONIC_READ(SONIC_CAP0); - dev->dev_addr[1] = val >> 8; - dev->dev_addr[0] = val & 0xff; - - printk(KERN_INFO "HW Address from CAM 15: %pM\n", - dev->dev_addr); - } else return 0; - - if (memcmp(dev->dev_addr, "\x08\x00\x07", 3) && - memcmp(dev->dev_addr, "\x00\xA0\x40", 3) && - memcmp(dev->dev_addr, "\x00\x80\x19", 3) && - memcmp(dev->dev_addr, "\x00\x05\x02", 3)) - { + if (!INVALID_MAC(dev->dev_addr)) + return; + /* - * Still nonsense ... messed up someplace! + * If we still have what seems to be a bogus address, we'll + * look in the CAM. The top entry should be ours. */ - printk(KERN_ERR "macsonic: ERROR (INVALID MAC)\n"); - return -EIO; - } else return 0; + printk(KERN_WARNING "macsonic: MAC address in PROM seems " + "to be invalid, trying CAM\n"); + } else { + printk(KERN_WARNING "macsonic: cannot read MAC address from " + "PROM, trying CAM\n"); + } + + /* This only works if MacOS has already initialized the card. */ + + SONIC_WRITE(SONIC_CMD, SONIC_CR_RST); + SONIC_WRITE(SONIC_CEP, 15); + + val = SONIC_READ(SONIC_CAP2); + dev->dev_addr[5] = val >> 8; + dev->dev_addr[4] = val & 0xff; + val = SONIC_READ(SONIC_CAP1); + dev->dev_addr[3] = val >> 8; + dev->dev_addr[2] = val & 0xff; + val = SONIC_READ(SONIC_CAP0); + dev->dev_addr[1] = val >> 8; + dev->dev_addr[0] = val & 0xff; + + if (!INVALID_MAC(dev->dev_addr)) + return; + + /* Still nonsense ... messed up someplace! */ + + printk(KERN_WARNING "macsonic: MAC address in CAM entry 15 " + "seems invalid, will use a random MAC\n"); + random_ether_addr(dev->dev_addr); } static int __devinit mac_onboard_sonic_probe(struct net_device *dev) @@ -402,8 +406,7 @@ static int __devinit mac_onboard_sonic_probe(struct net_device *dev) SONIC_WRITE(SONIC_ISR, 0x7fff); /* Now look for the MAC address. */ - if (mac_onboard_sonic_ethernet_addr(dev) != 0) - return -ENODEV; + mac_onboard_sonic_ethernet_addr(dev); /* Shared init code */ return macsonic_init(dev); diff --git a/drivers/net/macvlan.c b/drivers/net/macvlan.c index 3aabfd9dd212..2490aa39804c 100644 --- a/drivers/net/macvlan.c +++ b/drivers/net/macvlan.c @@ -360,6 +360,7 @@ static int macvlan_init(struct net_device *dev) dev->state = (dev->state & ~MACVLAN_STATE_MASK) | (lowerdev->state & MACVLAN_STATE_MASK); dev->features = lowerdev->features & MACVLAN_FEATURES; + dev->gso_max_size = lowerdev->gso_max_size; dev->iflink = lowerdev->ifindex; dev->hard_header_len = lowerdev->hard_header_len; @@ -596,6 +597,7 @@ static int macvlan_device_event(struct notifier_block *unused, case NETDEV_FEAT_CHANGE: list_for_each_entry(vlan, &port->vlans, list) { vlan->dev->features = dev->features & MACVLAN_FEATURES; + vlan->dev->gso_max_size = dev->gso_max_size; netdev_features_change(vlan->dev); } break; diff --git a/drivers/net/meth.c b/drivers/net/meth.c index 92ceb689b4d4..2af81735386b 100644 --- a/drivers/net/meth.c +++ b/drivers/net/meth.c @@ -828,7 +828,7 @@ static int __exit meth_remove(struct platform_device *pdev) static struct platform_driver meth_driver = { .probe = meth_probe, - .remove = __devexit_p(meth_remove), + .remove = __exit_p(meth_remove), .driver = { .name = "meth", .owner = THIS_MODULE, diff --git a/drivers/net/mlx4/fw.c b/drivers/net/mlx4/fw.c index cee199ceba2f..3c16602172fc 100644 --- a/drivers/net/mlx4/fw.c +++ b/drivers/net/mlx4/fw.c @@ -33,6 +33,7 @@ */ #include <linux/mlx4/cmd.h> +#include <linux/cache.h> #include "fw.h" #include "icm.h" @@ -698,6 +699,7 @@ int mlx4_INIT_HCA(struct mlx4_dev *dev, struct mlx4_init_hca_param *param) #define INIT_HCA_IN_SIZE 0x200 #define INIT_HCA_VERSION_OFFSET 0x000 #define INIT_HCA_VERSION 2 +#define INIT_HCA_CACHELINE_SZ_OFFSET 0x0e #define INIT_HCA_FLAGS_OFFSET 0x014 #define INIT_HCA_QPC_OFFSET 0x020 #define INIT_HCA_QPC_BASE_OFFSET (INIT_HCA_QPC_OFFSET + 0x10) @@ -735,6 +737,9 @@ int mlx4_INIT_HCA(struct mlx4_dev *dev, struct mlx4_init_hca_param *param) *((u8 *) mailbox->buf + INIT_HCA_VERSION_OFFSET) = INIT_HCA_VERSION; + *((u8 *) mailbox->buf + INIT_HCA_CACHELINE_SZ_OFFSET) = + (ilog2(cache_line_size()) - 4) << 5; + #if defined(__LITTLE_ENDIAN) *(inbox + INIT_HCA_FLAGS_OFFSET / 4) &= ~cpu_to_be32(1 << 1); #elif defined(__BIG_ENDIAN) diff --git a/drivers/net/mlx4/main.c b/drivers/net/mlx4/main.c index 3dd481e77f92..291a505fd4fc 100644 --- a/drivers/net/mlx4/main.c +++ b/drivers/net/mlx4/main.c @@ -1282,6 +1282,8 @@ static struct pci_device_id mlx4_pci_table[] = { { PCI_VDEVICE(MELLANOX, 0x6372) }, /* MT25458 ConnectX EN 10GBASE-T 10GigE */ { PCI_VDEVICE(MELLANOX, 0x675a) }, /* MT25458 ConnectX EN 10GBASE-T+Gen2 10GigE */ { PCI_VDEVICE(MELLANOX, 0x6764) }, /* MT26468 ConnectX EN 10GigE PCIe gen2*/ + { PCI_VDEVICE(MELLANOX, 0x6746) }, /* MT26438 ConnectX EN 40GigE PCIe gen2 5GT/s */ + { PCI_VDEVICE(MELLANOX, 0x676e) }, /* MT26478 ConnectX2 40GigE PCIe gen2 */ { 0, } }; diff --git a/drivers/net/myri10ge/myri10ge.c b/drivers/net/myri10ge/myri10ge.c index 6930c87f362e..f3624517cb0e 100644 --- a/drivers/net/myri10ge/myri10ge.c +++ b/drivers/net/myri10ge/myri10ge.c @@ -75,7 +75,7 @@ #include "myri10ge_mcp.h" #include "myri10ge_mcp_gen_header.h" -#define MYRI10GE_VERSION_STR "1.5.0-1.432" +#define MYRI10GE_VERSION_STR "1.5.1-1.451" MODULE_DESCRIPTION("Myricom 10G driver (10GbE)"); MODULE_AUTHOR("Maintainer: help@myri.com"); @@ -1624,10 +1624,21 @@ myri10ge_get_settings(struct net_device *netdev, struct ethtool_cmd *cmd) return 0; } } - if (*ptr == 'R' || *ptr == 'Q') { - /* We've found either an XFP or quad ribbon fiber */ + if (*ptr == '2') + ptr++; + if (*ptr == 'R' || *ptr == 'Q' || *ptr == 'S') { + /* We've found either an XFP, quad ribbon fiber, or SFP+ */ cmd->port = PORT_FIBRE; + cmd->supported |= SUPPORTED_FIBRE; + cmd->advertising |= ADVERTISED_FIBRE; + } else { + cmd->port = PORT_OTHER; } + if (*ptr == 'R' || *ptr == 'S') + cmd->transceiver = XCVR_EXTERNAL; + else + cmd->transceiver = XCVR_INTERNAL; + return 0; } diff --git a/drivers/net/netxen/netxen_nic.h b/drivers/net/netxen/netxen_nic.h index 7384f59df615..e1237b802872 100644 --- a/drivers/net/netxen/netxen_nic.h +++ b/drivers/net/netxen/netxen_nic.h @@ -1163,6 +1163,8 @@ struct netxen_adapter { u32 int_vec_bit; u32 heartbit; + u8 mac_addr[ETH_ALEN]; + struct netxen_adapter_stats stats; struct netxen_recv_context recv_ctx; diff --git a/drivers/net/netxen/netxen_nic_hdr.h b/drivers/net/netxen/netxen_nic_hdr.h index 7a7177421d7c..17bb3818d84e 100644 --- a/drivers/net/netxen/netxen_nic_hdr.h +++ b/drivers/net/netxen/netxen_nic_hdr.h @@ -419,6 +419,7 @@ enum { #define NETXEN_CRB_ROMUSB \ NETXEN_PCI_CRB_WINDOW(NETXEN_HW_PX_MAP_CRB_ROMUSB) #define NETXEN_CRB_I2Q NETXEN_PCI_CRB_WINDOW(NETXEN_HW_PX_MAP_CRB_I2Q) +#define NETXEN_CRB_I2C0 NETXEN_PCI_CRB_WINDOW(NETXEN_HW_PX_MAP_CRB_I2C0) #define NETXEN_CRB_SMB NETXEN_PCI_CRB_WINDOW(NETXEN_HW_PX_MAP_CRB_SMB) #define NETXEN_CRB_MAX NETXEN_PCI_CRB_WINDOW(64) @@ -544,6 +545,8 @@ enum { #define NETXEN_NIU_TEST_MUX_CTL (NETXEN_CRB_NIU + 0x00094) #define NETXEN_NIU_XG_PAUSE_CTL (NETXEN_CRB_NIU + 0x00098) #define NETXEN_NIU_XG_PAUSE_LEVEL (NETXEN_CRB_NIU + 0x000dc) +#define NETXEN_NIU_FRAME_COUNT_SELECT (NETXEN_CRB_NIU + 0x000ac) +#define NETXEN_NIU_FRAME_COUNT (NETXEN_CRB_NIU + 0x000b0) #define NETXEN_NIU_XG_SEL (NETXEN_CRB_NIU + 0x00128) #define NETXEN_NIU_GB_PAUSE_CTL (NETXEN_CRB_NIU + 0x0030c) diff --git a/drivers/net/netxen/netxen_nic_hw.c b/drivers/net/netxen/netxen_nic_hw.c index 32314000dfcd..52a3798d8d94 100644 --- a/drivers/net/netxen/netxen_nic_hw.c +++ b/drivers/net/netxen/netxen_nic_hw.c @@ -383,24 +383,51 @@ int netxen_niu_disable_xg_port(struct netxen_adapter *adapter) int netxen_p2_nic_set_promisc(struct netxen_adapter *adapter, u32 mode) { - __u32 reg; + u32 mac_cfg; + u32 cnt = 0; + __u32 reg = 0x0200; u32 port = adapter->physical_port; + u16 board_type = adapter->ahw.board_type; if (port > NETXEN_NIU_MAX_XG_PORTS) return -EINVAL; - reg = NXRD32(adapter, NETXEN_NIU_XGE_CONFIG_1 + (0x10000 * port)); - if (mode == NETXEN_NIU_PROMISC_MODE) - reg = (reg | 0x2000UL); - else - reg = (reg & ~0x2000UL); + mac_cfg = NXRD32(adapter, NETXEN_NIU_XGE_CONFIG_0 + (0x10000 * port)); + mac_cfg &= ~0x4; + NXWR32(adapter, NETXEN_NIU_XGE_CONFIG_0 + (0x10000 * port), mac_cfg); - if (mode == NETXEN_NIU_ALLMULTI_MODE) - reg = (reg | 0x1000UL); - else - reg = (reg & ~0x1000UL); + if ((board_type == NETXEN_BRDTYPE_P2_SB31_10G_IMEZ) || + (board_type == NETXEN_BRDTYPE_P2_SB31_10G_HMEZ)) + reg = (0x20 << port); + + NXWR32(adapter, NETXEN_NIU_FRAME_COUNT_SELECT, reg); + + mdelay(10); + + while (NXRD32(adapter, NETXEN_NIU_FRAME_COUNT) && ++cnt < 20) + mdelay(10); + + if (cnt < 20) { + + reg = NXRD32(adapter, + NETXEN_NIU_XGE_CONFIG_1 + (0x10000 * port)); - NXWR32(adapter, NETXEN_NIU_XGE_CONFIG_1 + (0x10000 * port), reg); + if (mode == NETXEN_NIU_PROMISC_MODE) + reg = (reg | 0x2000UL); + else + reg = (reg & ~0x2000UL); + + if (mode == NETXEN_NIU_ALLMULTI_MODE) + reg = (reg | 0x1000UL); + else + reg = (reg & ~0x1000UL); + + NXWR32(adapter, + NETXEN_NIU_XGE_CONFIG_1 + (0x10000 * port), reg); + } + + mac_cfg |= 0x4; + NXWR32(adapter, NETXEN_NIU_XGE_CONFIG_0 + (0x10000 * port), mac_cfg); return 0; } @@ -436,7 +463,7 @@ netxen_nic_enable_mcast_filter(struct netxen_adapter *adapter) { u32 val = 0; u16 port = adapter->physical_port; - u8 *addr = adapter->netdev->dev_addr; + u8 *addr = adapter->mac_addr; if (adapter->mc_enabled) return 0; @@ -465,7 +492,7 @@ netxen_nic_disable_mcast_filter(struct netxen_adapter *adapter) { u32 val = 0; u16 port = adapter->physical_port; - u8 *addr = adapter->netdev->dev_addr; + u8 *addr = adapter->mac_addr; if (!adapter->mc_enabled) return 0; @@ -660,7 +687,7 @@ void netxen_p3_nic_set_multi(struct net_device *netdev) list_splice_tail_init(&adapter->mac_list, &del_list); - nx_p3_nic_add_mac(adapter, netdev->dev_addr, &del_list); + nx_p3_nic_add_mac(adapter, adapter->mac_addr, &del_list); nx_p3_nic_add_mac(adapter, bcast_addr, &del_list); if (netdev->flags & IFF_PROMISC) { @@ -1901,22 +1928,16 @@ netxen_setup_hwops(struct netxen_adapter *adapter) int netxen_nic_get_board_info(struct netxen_adapter *adapter) { - int offset, board_type, magic, header_version; + int offset, board_type, magic; struct pci_dev *pdev = adapter->pdev; offset = NX_FW_MAGIC_OFFSET; if (netxen_rom_fast_read(adapter, offset, &magic)) return -EIO; - offset = NX_HDR_VERSION_OFFSET; - if (netxen_rom_fast_read(adapter, offset, &header_version)) - return -EIO; - - if (magic != NETXEN_BDINFO_MAGIC || - header_version != NETXEN_BDINFO_VERSION) { - dev_err(&pdev->dev, - "invalid board config, magic=%08x, version=%08x\n", - magic, header_version); + if (magic != NETXEN_BDINFO_MAGIC) { + dev_err(&pdev->dev, "invalid board config, magic=%08x\n", + magic); return -EIO; } diff --git a/drivers/net/netxen/netxen_nic_init.c b/drivers/net/netxen/netxen_nic_init.c index 91c2bc61c8eb..8a0904368e08 100644 --- a/drivers/net/netxen/netxen_nic_init.c +++ b/drivers/net/netxen/netxen_nic_init.c @@ -531,6 +531,8 @@ int netxen_pinit_from_rom(struct netxen_adapter *adapter, int verbose) continue; if (NX_IS_REVISION_P3(adapter->ahw.revision_id)) { + if (off == (NETXEN_CRB_I2C0 + 0x1c)) + continue; /* do not reset PCI */ if (off == (ROMUSB_GLB + 0xbc)) continue; @@ -542,6 +544,8 @@ int netxen_pinit_from_rom(struct netxen_adapter *adapter, int verbose) continue; if (off == (ROMUSB_GLB + 0x1c)) /* MS clock */ continue; + if ((off & 0x0ff00000) == NETXEN_CRB_DDR_NET) + continue; if (off == (NETXEN_CRB_PEG_NET_1 + 0x18)) buf[i].data = 0x1020; /* skip the function enable register */ @@ -553,12 +557,6 @@ int netxen_pinit_from_rom(struct netxen_adapter *adapter, int verbose) continue; } - if (off == NETXEN_ADDR_ERROR) { - printk(KERN_ERR "%s: Err: Unknown addr: 0x%08x\n", - netxen_nic_driver_name, buf[i].addr); - continue; - } - init_delay = 1; /* After writing this register, HW needs time for CRB */ /* to quiet down (else crb_window returns 0xffffffff) */ diff --git a/drivers/net/netxen/netxen_nic_main.c b/drivers/net/netxen/netxen_nic_main.c index f7bdde111dfc..3bf78dbfbf0f 100644 --- a/drivers/net/netxen/netxen_nic_main.c +++ b/drivers/net/netxen/netxen_nic_main.c @@ -437,6 +437,7 @@ netxen_read_mac_addr(struct netxen_adapter *adapter) netdev->dev_addr[i] = *(p + 5 - i); memcpy(netdev->perm_addr, netdev->dev_addr, netdev->addr_len); + memcpy(adapter->mac_addr, netdev->dev_addr, netdev->addr_len); /* set station address */ @@ -459,6 +460,7 @@ int netxen_nic_set_mac(struct net_device *netdev, void *p) netxen_napi_disable(adapter); } + memcpy(adapter->mac_addr, addr->sa_data, netdev->addr_len); memcpy(netdev->dev_addr, addr->sa_data, netdev->addr_len); adapter->macaddr_set(adapter, addr->sa_data); @@ -595,7 +597,8 @@ netxen_setup_pci_map(struct netxen_adapter *adapter) void __iomem *mem_ptr2 = NULL; void __iomem *db_ptr = NULL; - unsigned long mem_base, mem_len, db_base, db_len = 0, pci_len0 = 0; + resource_size_t mem_base, db_base; + unsigned long mem_len, db_len = 0, pci_len0 = 0; struct pci_dev *pdev = adapter->pdev; int pci_func = adapter->ahw.pci_func; @@ -955,7 +958,7 @@ netxen_nic_up(struct netxen_adapter *adapter, struct net_device *netdev) return err; } if (NX_IS_REVISION_P2(adapter->ahw.revision_id)) - adapter->macaddr_set(adapter, netdev->dev_addr); + adapter->macaddr_set(adapter, adapter->mac_addr); adapter->set_multi(netdev); adapter->set_mtu(adapter, netdev->mtu); @@ -1469,6 +1472,7 @@ netxen_nic_resume(struct pci_dev *pdev) } netxen_schedule_work(adapter, netxen_fw_poll_work, FW_POLL_DELAY); + return 0; err_out_detach: netxen_nic_detach(adapter); @@ -1713,7 +1717,7 @@ netxen_nic_xmit_frame(struct sk_buff *skb, struct net_device *netdev) /* 4 fragments per cmd des */ no_of_desc = (frag_count + 3) >> 2; - if (unlikely(no_of_desc + 2) > netxen_tx_avail(tx_ring)) { + if (unlikely(no_of_desc + 2 > netxen_tx_avail(tx_ring))) { netif_stop_queue(netdev); return NETDEV_TX_BUSY; } @@ -1903,12 +1907,13 @@ static void netxen_tx_timeout_task(struct work_struct *work) netif_wake_queue(adapter->netdev); - goto done; + clear_bit(__NX_RESETTING, &adapter->state); } else { + clear_bit(__NX_RESETTING, &adapter->state); if (!netxen_nic_reset_context(adapter)) { adapter->netdev->trans_start = jiffies; - goto done; + return; } /* context reset failed, fall through for fw reset */ @@ -1916,7 +1921,6 @@ static void netxen_tx_timeout_task(struct work_struct *work) request_reset: adapter->need_fw_reset = 1; -done: clear_bit(__NX_RESETTING, &adapter->state); } diff --git a/drivers/net/niu.c b/drivers/net/niu.c index 76cc2614f480..d6c7ac68f6ea 100644 --- a/drivers/net/niu.c +++ b/drivers/net/niu.c @@ -3545,7 +3545,7 @@ static int niu_process_rx_pkt(struct napi_struct *napi, struct niu *np, rp->rcr_index = index; skb_reserve(skb, NET_IP_ALIGN); - __pskb_pull_tail(skb, min(len, NIU_RXPULL_MAX)); + __pskb_pull_tail(skb, min(len, VLAN_ETH_HLEN)); rp->rx_packets++; rp->rx_bytes += skb->len; @@ -5615,7 +5615,7 @@ static void niu_init_tx_mac(struct niu *np) /* The XMAC_MIN register only accepts values for TX min which * have the low 3 bits cleared. */ - BUILD_BUG_ON(min & 0x7); + BUG_ON(min & 0x7); if (np->flags & NIU_FLAGS_XMAC) niu_init_tx_xmac(np, min, max); diff --git a/drivers/net/ns83820.c b/drivers/net/ns83820.c index c594e1946476..57fd483dbb1f 100644 --- a/drivers/net/ns83820.c +++ b/drivers/net/ns83820.c @@ -111,6 +111,7 @@ #include <linux/compiler.h> #include <linux/prefetch.h> #include <linux/ethtool.h> +#include <linux/sched.h> #include <linux/timer.h> #include <linux/if_vlan.h> #include <linux/rtnetlink.h> diff --git a/drivers/net/pasemi_mac_ethtool.c b/drivers/net/pasemi_mac_ethtool.c index 064a4fe1dd90..28a86224879d 100644 --- a/drivers/net/pasemi_mac_ethtool.c +++ b/drivers/net/pasemi_mac_ethtool.c @@ -71,6 +71,9 @@ pasemi_mac_ethtool_get_settings(struct net_device *netdev, struct pasemi_mac *mac = netdev_priv(netdev); struct phy_device *phydev = mac->phydev; + if (!phydev) + return -EOPNOTSUPP; + return phy_ethtool_gset(phydev, cmd); } diff --git a/drivers/net/pcmcia/3c574_cs.c b/drivers/net/pcmcia/3c574_cs.c index ee8ad3e180dd..b58965a2b3ae 100644 --- a/drivers/net/pcmcia/3c574_cs.c +++ b/drivers/net/pcmcia/3c574_cs.c @@ -251,6 +251,7 @@ static void el3_tx_timeout(struct net_device *dev); static int el3_ioctl(struct net_device *dev, struct ifreq *rq, int cmd); static const struct ethtool_ops netdev_ethtool_ops; static void set_rx_mode(struct net_device *dev); +static void set_multicast_list(struct net_device *dev); static void tc574_detach(struct pcmcia_device *p_dev); @@ -266,7 +267,7 @@ static const struct net_device_ops el3_netdev_ops = { .ndo_tx_timeout = el3_tx_timeout, .ndo_get_stats = el3_get_stats, .ndo_do_ioctl = el3_ioctl, - .ndo_set_multicast_list = set_rx_mode, + .ndo_set_multicast_list = set_multicast_list, .ndo_change_mtu = eth_change_mtu, .ndo_set_mac_address = eth_mac_addr, .ndo_validate_addr = eth_validate_addr, @@ -1161,6 +1162,16 @@ static void set_rx_mode(struct net_device *dev) outw(SetRxFilter | RxStation | RxBroadcast, ioaddr + EL3_CMD); } +static void set_multicast_list(struct net_device *dev) +{ + struct el3_private *lp = netdev_priv(dev); + unsigned long flags; + + spin_lock_irqsave(&lp->window_lock, flags); + set_rx_mode(dev); + spin_unlock_irqrestore(&lp->window_lock, flags); +} + static int el3_close(struct net_device *dev) { unsigned int ioaddr = dev->base_addr; diff --git a/drivers/net/pcmcia/pcnet_cs.c b/drivers/net/pcmcia/pcnet_cs.c index 97db1c732342..94c9ad2746bc 100644 --- a/drivers/net/pcmcia/pcnet_cs.c +++ b/drivers/net/pcmcia/pcnet_cs.c @@ -340,12 +340,11 @@ static hw_info_t *get_hwinfo(struct pcmcia_device *link) base = &virt[hw_info[i].offset & (req.Size-1)]; if ((readb(base+0) == hw_info[i].a0) && (readb(base+2) == hw_info[i].a1) && - (readb(base+4) == hw_info[i].a2)) - break; - } - if (i < NR_INFO) { - for (j = 0; j < 6; j++) - dev->dev_addr[j] = readb(base + (j<<1)); + (readb(base+4) == hw_info[i].a2)) { + for (j = 0; j < 6; j++) + dev->dev_addr[j] = readb(base + (j<<1)); + break; + } } iounmap(virt); @@ -1755,14 +1754,14 @@ static struct pcmcia_device_id pcnet_ids[] = { PCMCIA_PFC_DEVICE_CIS_PROD_ID12(0, "Psion Dacom", "Gold Card V34 Ethernet", 0xf5f025c2, 0x338e8155, "cis/PCMLM28.cis"), PCMCIA_PFC_DEVICE_CIS_PROD_ID12(0, "Psion Dacom", "Gold Card V34 Ethernet GSM", 0xf5f025c2, 0x4ae85d35, "cis/PCMLM28.cis"), PCMCIA_PFC_DEVICE_CIS_PROD_ID12(0, "LINKSYS", "PCMLM28", 0xf7cb0b07, 0x66881874, "cis/PCMLM28.cis"), - PCMCIA_MFC_DEVICE_CIS_PROD_ID12(0, "DAYNA COMMUNICATIONS", "LAN AND MODEM MULTIFUNCTION", 0x8fdf8f89, 0xdd5ed9e8, "DP83903.cis"), - PCMCIA_MFC_DEVICE_CIS_PROD_ID4(0, "NSC MF LAN/Modem", 0x58fc6056, "DP83903.cis"), - PCMCIA_MFC_DEVICE_CIS_MANF_CARD(0, 0x0175, 0x0000, "DP83903.cis"), + PCMCIA_MFC_DEVICE_CIS_PROD_ID12(0, "DAYNA COMMUNICATIONS", "LAN AND MODEM MULTIFUNCTION", 0x8fdf8f89, 0xdd5ed9e8, "cis/DP83903.cis"), + PCMCIA_MFC_DEVICE_CIS_PROD_ID4(0, "NSC MF LAN/Modem", 0x58fc6056, "cis/DP83903.cis"), + PCMCIA_MFC_DEVICE_CIS_MANF_CARD(0, 0x0175, 0x0000, "cis/DP83903.cis"), PCMCIA_DEVICE_CIS_MANF_CARD(0xc00f, 0x0002, "cis/LA-PCM.cis"), PCMCIA_DEVICE_CIS_PROD_ID12("KTI", "PE520 PLUS", 0xad180345, 0x9d58d392, "PE520.cis"), - PCMCIA_DEVICE_CIS_PROD_ID12("NDC", "Ethernet", 0x01c43ae1, 0x00b2e941, "NE2K.cis"), - PCMCIA_DEVICE_CIS_PROD_ID12("PMX ", "PE-200", 0x34f3f1c8, 0x10b59f8c, "PE-200.cis"), - PCMCIA_DEVICE_CIS_PROD_ID12("TAMARACK", "Ethernet", 0xcf434fba, 0x00b2e941, "tamarack.cis"), + PCMCIA_DEVICE_CIS_PROD_ID12("NDC", "Ethernet", 0x01c43ae1, 0x00b2e941, "cis/NE2K.cis"), + PCMCIA_DEVICE_CIS_PROD_ID12("PMX ", "PE-200", 0x34f3f1c8, 0x10b59f8c, "cis/PE-200.cis"), + PCMCIA_DEVICE_CIS_PROD_ID12("TAMARACK", "Ethernet", 0xcf434fba, 0x00b2e941, "cis/tamarack.cis"), PCMCIA_DEVICE_PROD_ID12("Ethernet", "CF Size PC Card", 0x00b2e941, 0x43ac239b), PCMCIA_DEVICE_PROD_ID123("Fast Ethernet", "CF Size PC Card", "1.0", 0xb4be14e3, 0x43ac239b, 0x0877b627), diff --git a/drivers/net/pcnet32.c b/drivers/net/pcnet32.c index 6d28b18e7e28..c1b3f09f452c 100644 --- a/drivers/net/pcnet32.c +++ b/drivers/net/pcnet32.c @@ -31,6 +31,7 @@ static const char *const version = #include <linux/module.h> #include <linux/kernel.h> +#include <linux/sched.h> #include <linux/string.h> #include <linux/errno.h> #include <linux/ioport.h> diff --git a/drivers/net/phy/mdio-gpio.c b/drivers/net/phy/mdio-gpio.c index 250e10f2c35b..35897134a5dd 100644 --- a/drivers/net/phy/mdio-gpio.c +++ b/drivers/net/phy/mdio-gpio.c @@ -139,7 +139,7 @@ out: return NULL; } -static void __devinit mdio_gpio_bus_deinit(struct device *dev) +static void mdio_gpio_bus_deinit(struct device *dev) { struct mii_bus *bus = dev_get_drvdata(dev); struct mdio_gpio_info *bitbang = bus->priv; @@ -238,6 +238,7 @@ static struct of_device_id mdio_ofgpio_match[] = { }, {}, }; +MODULE_DEVICE_TABLE(of, mdio_ofgpio_match); static struct of_platform_driver mdio_ofgpio_driver = { .name = "mdio-gpio", diff --git a/drivers/net/ppp_generic.c b/drivers/net/ppp_generic.c index 9bf2a6be9031..965adb6174c3 100644 --- a/drivers/net/ppp_generic.c +++ b/drivers/net/ppp_generic.c @@ -1944,8 +1944,15 @@ ppp_receive_mp_frame(struct ppp *ppp, struct sk_buff *skb, struct channel *pch) } /* Pull completed packets off the queue and receive them. */ - while ((skb = ppp_mp_reconstruct(ppp))) - ppp_receive_nonmp_frame(ppp, skb); + while ((skb = ppp_mp_reconstruct(ppp))) { + if (pskb_may_pull(skb, 2)) + ppp_receive_nonmp_frame(ppp, skb); + else { + ++ppp->dev->stats.rx_length_errors; + kfree_skb(skb); + ppp_receive_error(ppp); + } + } return; diff --git a/drivers/net/pppoe.c b/drivers/net/pppoe.c index 7cbf6f9b51de..2559991eea6a 100644 --- a/drivers/net/pppoe.c +++ b/drivers/net/pppoe.c @@ -111,9 +111,6 @@ struct pppoe_net { rwlock_t hash_lock; }; -/* to eliminate a race btw pppoe_flush_dev and pppoe_release */ -static DEFINE_SPINLOCK(flush_lock); - /* * PPPoE could be in the following stages: * 1) Discovery stage (to obtain remote MAC and Session ID) @@ -303,45 +300,48 @@ static void pppoe_flush_dev(struct net_device *dev) write_lock_bh(&pn->hash_lock); for (i = 0; i < PPPOE_HASH_SIZE; i++) { struct pppox_sock *po = pn->hash_table[i]; + struct sock *sk; - while (po != NULL) { - struct sock *sk; - if (po->pppoe_dev != dev) { + while (po) { + while (po && po->pppoe_dev != dev) { po = po->next; - continue; } + + if (!po) + break; + sk = sk_pppox(po); - spin_lock(&flush_lock); - po->pppoe_dev = NULL; - spin_unlock(&flush_lock); - dev_put(dev); /* We always grab the socket lock, followed by the - * hash_lock, in that order. Since we should - * hold the sock lock while doing any unbinding, - * we need to release the lock we're holding. - * Hold a reference to the sock so it doesn't disappear - * as we're jumping between locks. + * hash_lock, in that order. Since we should hold the + * sock lock while doing any unbinding, we need to + * release the lock we're holding. Hold a reference to + * the sock so it doesn't disappear as we're jumping + * between locks. */ sock_hold(sk); - write_unlock_bh(&pn->hash_lock); lock_sock(sk); - if (sk->sk_state & (PPPOX_CONNECTED | PPPOX_BOUND)) { + if (po->pppoe_dev == dev + && sk->sk_state & (PPPOX_CONNECTED | PPPOX_BOUND)) { pppox_unbind_sock(sk); sk->sk_state = PPPOX_ZOMBIE; sk->sk_state_change(sk); + po->pppoe_dev = NULL; + dev_put(dev); } release_sock(sk); sock_put(sk); - /* Restart scan at the beginning of this hash chain. - * While the lock was dropped the chain contents may - * have changed. + /* Restart the process from the start of the current + * hash chain. We dropped locks so the world may have + * change from underneath us. */ + + BUG_ON(pppoe_pernet(dev_net(dev)) == NULL); write_lock_bh(&pn->hash_lock); po = pn->hash_table[i]; } @@ -388,11 +388,16 @@ static int pppoe_rcv_core(struct sock *sk, struct sk_buff *skb) struct pppox_sock *po = pppox_sk(sk); struct pppox_sock *relay_po; + /* Backlog receive. Semantics of backlog rcv preclude any code from + * executing in lock_sock()/release_sock() bounds; meaning sk->sk_state + * can't change. + */ + if (sk->sk_state & PPPOX_BOUND) { ppp_input(&po->chan, skb); } else if (sk->sk_state & PPPOX_RELAY) { - relay_po = get_item_by_addr(dev_net(po->pppoe_dev), - &po->pppoe_relay); + relay_po = get_item_by_addr(sock_net(sk), + &po->pppoe_relay); if (relay_po == NULL) goto abort_kfree; @@ -447,6 +452,10 @@ static int pppoe_rcv(struct sk_buff *skb, struct net_device *dev, goto drop; pn = pppoe_pernet(dev_net(dev)); + + /* Note that get_item does a sock_hold(), so sk_pppox(po) + * is known to be safe. + */ po = get_item(pn, ph->sid, eth_hdr(skb)->h_source, dev->ifindex); if (!po) goto drop; @@ -561,6 +570,7 @@ static int pppoe_release(struct socket *sock) struct sock *sk = sock->sk; struct pppox_sock *po; struct pppoe_net *pn; + struct net *net = NULL; if (!sk) return 0; @@ -571,44 +581,28 @@ static int pppoe_release(struct socket *sock) return -EBADF; } + po = pppox_sk(sk); + + if (sk->sk_state & (PPPOX_CONNECTED | PPPOX_BOUND)) { + dev_put(po->pppoe_dev); + po->pppoe_dev = NULL; + } + pppox_unbind_sock(sk); /* Signal the death of the socket. */ sk->sk_state = PPPOX_DEAD; - /* - * pppoe_flush_dev could lead to a race with - * this routine so we use flush_lock to eliminate - * such a case (we only need per-net specific data) - */ - spin_lock(&flush_lock); - po = pppox_sk(sk); - if (!po->pppoe_dev) { - spin_unlock(&flush_lock); - goto out; - } - pn = pppoe_pernet(dev_net(po->pppoe_dev)); - spin_unlock(&flush_lock); + net = sock_net(sk); + pn = pppoe_pernet(net); /* * protect "po" from concurrent updates * on pppoe_flush_dev */ - write_lock_bh(&pn->hash_lock); + delete_item(pn, po->pppoe_pa.sid, po->pppoe_pa.remote, + po->pppoe_ifindex); - po = pppox_sk(sk); - if (stage_session(po->pppoe_pa.sid)) - __delete_item(pn, po->pppoe_pa.sid, po->pppoe_pa.remote, - po->pppoe_ifindex); - - if (po->pppoe_dev) { - dev_put(po->pppoe_dev); - po->pppoe_dev = NULL; - } - - write_unlock_bh(&pn->hash_lock); - -out: sock_orphan(sk); sock->sk = NULL; @@ -625,8 +619,9 @@ static int pppoe_connect(struct socket *sock, struct sockaddr *uservaddr, struct sock *sk = sock->sk; struct sockaddr_pppox *sp = (struct sockaddr_pppox *)uservaddr; struct pppox_sock *po = pppox_sk(sk); - struct net_device *dev; + struct net_device *dev = NULL; struct pppoe_net *pn; + struct net *net = NULL; int error; lock_sock(sk); @@ -652,12 +647,14 @@ static int pppoe_connect(struct socket *sock, struct sockaddr *uservaddr, /* Delete the old binding */ if (stage_session(po->pppoe_pa.sid)) { pppox_unbind_sock(sk); + pn = pppoe_pernet(sock_net(sk)); + delete_item(pn, po->pppoe_pa.sid, + po->pppoe_pa.remote, po->pppoe_ifindex); if (po->pppoe_dev) { - pn = pppoe_pernet(dev_net(po->pppoe_dev)); - delete_item(pn, po->pppoe_pa.sid, - po->pppoe_pa.remote, po->pppoe_ifindex); dev_put(po->pppoe_dev); + po->pppoe_dev = NULL; } + memset(sk_pppox(po) + 1, 0, sizeof(struct pppox_sock) - sizeof(struct sock)); sk->sk_state = PPPOX_NONE; @@ -666,16 +663,15 @@ static int pppoe_connect(struct socket *sock, struct sockaddr *uservaddr, /* Re-bind in session stage only */ if (stage_session(sp->sa_addr.pppoe.sid)) { error = -ENODEV; - dev = dev_get_by_name(sock_net(sk), sp->sa_addr.pppoe.dev); + net = sock_net(sk); + dev = dev_get_by_name(net, sp->sa_addr.pppoe.dev); if (!dev) - goto end; + goto err_put; po->pppoe_dev = dev; po->pppoe_ifindex = dev->ifindex; - pn = pppoe_pernet(dev_net(dev)); - write_lock_bh(&pn->hash_lock); + pn = pppoe_pernet(net); if (!(dev->flags & IFF_UP)) { - write_unlock_bh(&pn->hash_lock); goto err_put; } @@ -683,6 +679,7 @@ static int pppoe_connect(struct socket *sock, struct sockaddr *uservaddr, &sp->sa_addr.pppoe, sizeof(struct pppoe_addr)); + write_lock_bh(&pn->hash_lock); error = __set_item(pn, po); write_unlock_bh(&pn->hash_lock); if (error < 0) @@ -696,8 +693,11 @@ static int pppoe_connect(struct socket *sock, struct sockaddr *uservaddr, po->chan.ops = &pppoe_chan_ops; error = ppp_register_net_channel(dev_net(dev), &po->chan); - if (error) + if (error) { + delete_item(pn, po->pppoe_pa.sid, + po->pppoe_pa.remote, po->pppoe_ifindex); goto err_put; + } sk->sk_state = PPPOX_CONNECTED; } @@ -915,6 +915,14 @@ static int __pppoe_xmit(struct sock *sk, struct sk_buff *skb) struct pppoe_hdr *ph; int data_len = skb->len; + /* The higher-level PPP code (ppp_unregister_channel()) ensures the PPP + * xmit operations conclude prior to an unregistration call. Thus + * sk->sk_state cannot change, so we don't need to do lock_sock(). + * But, we also can't do a lock_sock since that introduces a potential + * deadlock as we'd reverse the lock ordering used when calling + * ppp_unregister_channel(). + */ + if (sock_flag(sk, SOCK_DEAD) || !(sk->sk_state & PPPOX_CONNECTED)) goto abort; @@ -944,7 +952,6 @@ static int __pppoe_xmit(struct sock *sk, struct sk_buff *skb) po->pppoe_pa.remote, NULL, data_len); dev_queue_xmit(skb); - return 1; abort: diff --git a/drivers/net/pppol2tp.c b/drivers/net/pppol2tp.c index cc394d073755..5910df60c93e 100644 --- a/drivers/net/pppol2tp.c +++ b/drivers/net/pppol2tp.c @@ -2179,7 +2179,7 @@ static int pppol2tp_session_setsockopt(struct sock *sk, * session or the special tunnel type. */ static int pppol2tp_setsockopt(struct socket *sock, int level, int optname, - char __user *optval, int optlen) + char __user *optval, unsigned int optlen) { struct sock *sk = sock->sk; struct pppol2tp_session *session = sk->sk_user_data; diff --git a/drivers/net/qlge/qlge.h b/drivers/net/qlge/qlge.h index a9845a2f243f..c2383adcd527 100644 --- a/drivers/net/qlge/qlge.h +++ b/drivers/net/qlge/qlge.h @@ -9,6 +9,7 @@ #include <linux/pci.h> #include <linux/netdevice.h> +#include <linux/rtnetlink.h> /* * General definitions... @@ -94,6 +95,7 @@ enum { /* Misc. stuff */ MAILBOX_COUNT = 16, + MAILBOX_TIMEOUT = 5, PROC_ADDR_RDY = (1 << 31), PROC_ADDR_R = (1 << 30), @@ -135,9 +137,9 @@ enum { RST_FO_TFO = (1 << 0), RST_FO_RR_MASK = 0x00060000, RST_FO_RR_CQ_CAM = 0x00000000, - RST_FO_RR_DROP = 0x00000001, - RST_FO_RR_DQ = 0x00000002, - RST_FO_RR_RCV_FUNC_CQ = 0x00000003, + RST_FO_RR_DROP = 0x00000002, + RST_FO_RR_DQ = 0x00000004, + RST_FO_RR_RCV_FUNC_CQ = 0x00000006, RST_FO_FRB = (1 << 12), RST_FO_MOP = (1 << 13), RST_FO_REG = (1 << 14), @@ -802,6 +804,12 @@ enum { MB_CMD_SET_PORT_CFG = 0x00000122, MB_CMD_GET_PORT_CFG = 0x00000123, MB_CMD_GET_LINK_STS = 0x00000124, + MB_CMD_SET_MGMNT_TFK_CTL = 0x00000160, /* Set Mgmnt Traffic Control */ + MB_SET_MPI_TFK_STOP = (1 << 0), + MB_SET_MPI_TFK_RESUME = (1 << 1), + MB_CMD_GET_MGMNT_TFK_CTL = 0x00000161, /* Get Mgmnt Traffic Control */ + MB_GET_MPI_TFK_STOPPED = (1 << 0), + MB_GET_MPI_TFK_FIFO_EMPTY = (1 << 1), /* Mailbox Command Status. */ MB_CMD_STS_GOOD = 0x00004000, /* Success. */ @@ -1167,7 +1175,7 @@ struct ricb { #define RSS_RI6 0x40 #define RSS_RT6 0x80 __le16 mask; - __le32 hash_cq_id[256]; + u8 hash_cq_id[1024]; __le32 ipv6_hash_key[10]; __le32 ipv4_hash_key[4]; } __attribute((packed)); @@ -1381,15 +1389,15 @@ struct intr_context { /* adapter flags definitions. */ enum { - QL_ADAPTER_UP = (1 << 0), /* Adapter has been brought up. */ - QL_LEGACY_ENABLED = (1 << 3), - QL_MSI_ENABLED = (1 << 3), - QL_MSIX_ENABLED = (1 << 4), - QL_DMA64 = (1 << 5), - QL_PROMISCUOUS = (1 << 6), - QL_ALLMULTI = (1 << 7), - QL_PORT_CFG = (1 << 8), - QL_CAM_RT_SET = (1 << 9), + QL_ADAPTER_UP = 0, /* Adapter has been brought up. */ + QL_LEGACY_ENABLED = 1, + QL_MSI_ENABLED = 2, + QL_MSIX_ENABLED = 3, + QL_DMA64 = 4, + QL_PROMISCUOUS = 5, + QL_ALLMULTI = 6, + QL_PORT_CFG = 7, + QL_CAM_RT_SET = 8, }; /* link_status bit definitions */ @@ -1477,7 +1485,6 @@ struct ql_adapter { u32 mailbox_in; u32 mailbox_out; struct mbox_params idc_mbc; - struct mutex mpi_mutex; int tx_ring_size; int rx_ring_size; @@ -1606,6 +1613,8 @@ int ql_read_mpi_reg(struct ql_adapter *qdev, u32 reg, u32 *data); int ql_mb_about_fw(struct ql_adapter *qdev); void ql_link_on(struct ql_adapter *qdev); void ql_link_off(struct ql_adapter *qdev); +int ql_mb_set_mgmnt_traffic_ctl(struct ql_adapter *qdev, u32 control); +int ql_wait_fifo_empty(struct ql_adapter *qdev); #if 1 #define QL_ALL_DUMP diff --git a/drivers/net/qlge/qlge_ethtool.c b/drivers/net/qlge/qlge_ethtool.c index 68f9bd280f86..52073946bce3 100644 --- a/drivers/net/qlge/qlge_ethtool.c +++ b/drivers/net/qlge/qlge_ethtool.c @@ -45,7 +45,6 @@ static int ql_update_ring_coalescing(struct ql_adapter *qdev) if (!netif_running(qdev->ndev)) return status; - spin_lock(&qdev->hw_lock); /* Skip the default queue, and update the outbound handler * queues if they changed. */ @@ -92,7 +91,6 @@ static int ql_update_ring_coalescing(struct ql_adapter *qdev) } } exit: - spin_unlock(&qdev->hw_lock); return status; } diff --git a/drivers/net/qlge/qlge_main.c b/drivers/net/qlge/qlge_main.c index 7783c5db81dc..a2fc70a0d0cc 100644 --- a/drivers/net/qlge/qlge_main.c +++ b/drivers/net/qlge/qlge_main.c @@ -34,7 +34,6 @@ #include <linux/etherdevice.h> #include <linux/ethtool.h> #include <linux/skbuff.h> -#include <linux/rtnetlink.h> #include <linux/if_vlan.h> #include <linux/delay.h> #include <linux/mm.h> @@ -321,6 +320,37 @@ static int ql_set_mac_addr_reg(struct ql_adapter *qdev, u8 *addr, u32 type, switch (type) { case MAC_ADDR_TYPE_MULTI_MAC: + { + u32 upper = (addr[0] << 8) | addr[1]; + u32 lower = (addr[2] << 24) | (addr[3] << 16) | + (addr[4] << 8) | (addr[5]); + + status = + ql_wait_reg_rdy(qdev, + MAC_ADDR_IDX, MAC_ADDR_MW, 0); + if (status) + goto exit; + ql_write32(qdev, MAC_ADDR_IDX, (offset++) | + (index << MAC_ADDR_IDX_SHIFT) | + type | MAC_ADDR_E); + ql_write32(qdev, MAC_ADDR_DATA, lower); + status = + ql_wait_reg_rdy(qdev, + MAC_ADDR_IDX, MAC_ADDR_MW, 0); + if (status) + goto exit; + ql_write32(qdev, MAC_ADDR_IDX, (offset++) | + (index << MAC_ADDR_IDX_SHIFT) | + type | MAC_ADDR_E); + + ql_write32(qdev, MAC_ADDR_DATA, upper); + status = + ql_wait_reg_rdy(qdev, + MAC_ADDR_IDX, MAC_ADDR_MW, 0); + if (status) + goto exit; + break; + } case MAC_ADDR_TYPE_CAM_MAC: { u32 cam_output; @@ -366,16 +396,14 @@ static int ql_set_mac_addr_reg(struct ql_adapter *qdev, u8 *addr, u32 type, and possibly the function id. Right now we hardcode the route field to NIC core. */ - if (type == MAC_ADDR_TYPE_CAM_MAC) { - cam_output = (CAM_OUT_ROUTE_NIC | - (qdev-> - func << CAM_OUT_FUNC_SHIFT) | - (0 << CAM_OUT_CQ_ID_SHIFT)); - if (qdev->vlgrp) - cam_output |= CAM_OUT_RV; - /* route to NIC core */ - ql_write32(qdev, MAC_ADDR_DATA, cam_output); - } + cam_output = (CAM_OUT_ROUTE_NIC | + (qdev-> + func << CAM_OUT_FUNC_SHIFT) | + (0 << CAM_OUT_CQ_ID_SHIFT)); + if (qdev->vlgrp) + cam_output |= CAM_OUT_RV; + /* route to NIC core */ + ql_write32(qdev, MAC_ADDR_DATA, cam_output); break; } case MAC_ADDR_TYPE_VLAN: @@ -547,14 +575,14 @@ static int ql_set_routing_reg(struct ql_adapter *qdev, u32 index, u32 mask, } case RT_IDX_MCAST: /* Pass up All Multicast frames. */ { - value = RT_IDX_DST_CAM_Q | /* dest */ + value = RT_IDX_DST_DFLT_Q | /* dest */ RT_IDX_TYPE_NICQ | /* type */ (RT_IDX_ALLMULTI_SLOT << RT_IDX_IDX_SHIFT);/* index */ break; } case RT_IDX_MCAST_MATCH: /* Pass up matched Multicast frames. */ { - value = RT_IDX_DST_CAM_Q | /* dest */ + value = RT_IDX_DST_DFLT_Q | /* dest */ RT_IDX_TYPE_NICQ | /* type */ (RT_IDX_MCAST_MATCH_SLOT << RT_IDX_IDX_SHIFT);/* index */ break; @@ -1926,12 +1954,10 @@ static void ql_vlan_rx_add_vid(struct net_device *ndev, u16 vid) status = ql_sem_spinlock(qdev, SEM_MAC_ADDR_MASK); if (status) return; - spin_lock(&qdev->hw_lock); if (ql_set_mac_addr_reg (qdev, (u8 *) &enable_bit, MAC_ADDR_TYPE_VLAN, vid)) { QPRINTK(qdev, IFUP, ERR, "Failed to init vlan address.\n"); } - spin_unlock(&qdev->hw_lock); ql_sem_unlock(qdev, SEM_MAC_ADDR_MASK); } @@ -1945,12 +1971,10 @@ static void ql_vlan_rx_kill_vid(struct net_device *ndev, u16 vid) if (status) return; - spin_lock(&qdev->hw_lock); if (ql_set_mac_addr_reg (qdev, (u8 *) &enable_bit, MAC_ADDR_TYPE_VLAN, vid)) { QPRINTK(qdev, IFUP, ERR, "Failed to clear vlan address.\n"); } - spin_unlock(&qdev->hw_lock); ql_sem_unlock(qdev, SEM_MAC_ADDR_MASK); } @@ -2001,15 +2025,17 @@ static irqreturn_t qlge_isr(int irq, void *dev_id) /* * Check MPI processor activity. */ - if (var & STS_PI) { + if ((var & STS_PI) && + (ql_read32(qdev, INTR_MASK) & INTR_MASK_PI)) { /* * We've got an async event or mailbox completion. * Handle it and clear the source of the interrupt. */ QPRINTK(qdev, INTR, ERR, "Got MPI processor interrupt.\n"); ql_disable_completion_interrupt(qdev, intr_context->intr); - queue_delayed_work_on(smp_processor_id(), qdev->workqueue, - &qdev->mpi_work, 0); + ql_write32(qdev, INTR_MASK, (INTR_MASK_PI << 16)); + queue_delayed_work_on(smp_processor_id(), + qdev->workqueue, &qdev->mpi_work, 0); work_done++; } @@ -3080,6 +3106,12 @@ err_irq: static int ql_start_rss(struct ql_adapter *qdev) { + u8 init_hash_seed[] = {0x6d, 0x5a, 0x56, 0xda, 0x25, 0x5b, 0x0e, 0xc2, + 0x41, 0x67, 0x25, 0x3d, 0x43, 0xa3, 0x8f, + 0xb0, 0xd0, 0xca, 0x2b, 0xcb, 0xae, 0x7b, + 0x30, 0xb4, 0x77, 0xcb, 0x2d, 0xa3, 0x80, + 0x30, 0xf2, 0x0c, 0x6a, 0x42, 0xb7, 0x3b, + 0xbe, 0xac, 0x01, 0xfa}; struct ricb *ricb = &qdev->ricb; int status = 0; int i; @@ -3089,21 +3121,17 @@ static int ql_start_rss(struct ql_adapter *qdev) ricb->base_cq = RSS_L4K; ricb->flags = - (RSS_L6K | RSS_LI | RSS_LB | RSS_LM | RSS_RI4 | RSS_RI6 | RSS_RT4 | - RSS_RT6); - ricb->mask = cpu_to_le16(qdev->rss_ring_count - 1); + (RSS_L6K | RSS_LI | RSS_LB | RSS_LM | RSS_RT4 | RSS_RT6); + ricb->mask = cpu_to_le16((u16)(0x3ff)); /* * Fill out the Indirection Table. */ - for (i = 0; i < 256; i++) - hash_id[i] = i & (qdev->rss_ring_count - 1); + for (i = 0; i < 1024; i++) + hash_id[i] = (i & (qdev->rss_ring_count - 1)); - /* - * Random values for the IPv6 and IPv4 Hash Keys. - */ - get_random_bytes((void *)&ricb->ipv6_hash_key[0], 40); - get_random_bytes((void *)&ricb->ipv4_hash_key[0], 16); + memcpy((void *)&ricb->ipv6_hash_key[0], init_hash_seed, 40); + memcpy((void *)&ricb->ipv4_hash_key[0], init_hash_seed, 16); QPRINTK(qdev, IFUP, DEBUG, "Initializing RSS.\n"); @@ -3142,14 +3170,14 @@ static int ql_route_initialize(struct ql_adapter *qdev) { int status = 0; - status = ql_sem_spinlock(qdev, SEM_RT_IDX_MASK); + /* Clear all the entries in the routing table. */ + status = ql_clear_routing_entries(qdev); if (status) return status; - /* Clear all the entries in the routing table. */ - status = ql_clear_routing_entries(qdev); + status = ql_sem_spinlock(qdev, SEM_RT_IDX_MASK); if (status) - goto exit; + return status; status = ql_set_routing_reg(qdev, RT_IDX_ALL_ERR_SLOT, RT_IDX_ERR, 1); if (status) { @@ -3242,6 +3270,13 @@ static int ql_adapter_initialize(struct ql_adapter *qdev) ql_write32(qdev, SPLT_HDR, SPLT_HDR_EP | min(SMALL_BUFFER_SIZE, MAX_SPLIT_SIZE)); + /* Set RX packet routing to use port/pci function on which the + * packet arrived on in addition to usual frame routing. + * This is helpful on bonding where both interfaces can have + * the same MAC address. + */ + ql_write32(qdev, RST_FO, RST_FO_RR_MASK | RST_FO_RR_RCV_FUNC_CQ); + /* Start up the rx queues. */ for (i = 0; i < qdev->rx_ring_count; i++) { status = ql_start_rx_ring(qdev, &qdev->rx_ring[i]); @@ -3314,6 +3349,13 @@ static int ql_adapter_reset(struct ql_adapter *qdev) end_jiffies = jiffies + max((unsigned long)1, usecs_to_jiffies(30)); + + /* Stop management traffic. */ + ql_mb_set_mgmnt_traffic_ctl(qdev, MB_SET_MPI_TFK_STOP); + + /* Wait for the NIC and MGMNT FIFOs to empty. */ + ql_wait_fifo_empty(qdev); + ql_write32(qdev, RST_FO, (RST_FO_FR << 16) | RST_FO_FR); do { @@ -3329,6 +3371,8 @@ static int ql_adapter_reset(struct ql_adapter *qdev) status = -ETIMEDOUT; } + /* Resume management traffic. */ + ql_mb_set_mgmnt_traffic_ctl(qdev, MB_SET_MPI_TFK_RESUME); return status; } @@ -3380,12 +3424,10 @@ static int ql_adapter_down(struct ql_adapter *qdev) ql_free_rx_buffers(qdev); - spin_lock(&qdev->hw_lock); status = ql_adapter_reset(qdev); if (status) QPRINTK(qdev, IFDOWN, ERR, "reset(func #%d) FAILED!\n", qdev->func); - spin_unlock(&qdev->hw_lock); return status; } @@ -3587,7 +3629,6 @@ static void qlge_set_multicast_list(struct net_device *ndev) status = ql_sem_spinlock(qdev, SEM_RT_IDX_MASK); if (status) return; - spin_lock(&qdev->hw_lock); /* * Set or clear promiscuous mode if a * transition is taking place. @@ -3664,7 +3705,6 @@ static void qlge_set_multicast_list(struct net_device *ndev) } } exit: - spin_unlock(&qdev->hw_lock); ql_sem_unlock(qdev, SEM_RT_IDX_MASK); } @@ -3684,10 +3724,8 @@ static int qlge_set_mac_address(struct net_device *ndev, void *p) status = ql_sem_spinlock(qdev, SEM_MAC_ADDR_MASK); if (status) return status; - spin_lock(&qdev->hw_lock); status = ql_set_mac_addr_reg(qdev, (u8 *) ndev->dev_addr, MAC_ADDR_TYPE_CAM_MAC, qdev->func * MAX_CQ); - spin_unlock(&qdev->hw_lock); if (status) QPRINTK(qdev, HW, ERR, "Failed to load MAC address.\n"); ql_sem_unlock(qdev, SEM_MAC_ADDR_MASK); @@ -3705,7 +3743,7 @@ static void ql_asic_reset_work(struct work_struct *work) struct ql_adapter *qdev = container_of(work, struct ql_adapter, asic_reset_work.work); int status; - + rtnl_lock(); status = ql_adapter_down(qdev); if (status) goto error; @@ -3714,11 +3752,17 @@ static void ql_asic_reset_work(struct work_struct *work) if (status) goto error; + /* Restore rx mode. */ + clear_bit(QL_ALLMULTI, &qdev->flags); + clear_bit(QL_PROMISCUOUS, &qdev->flags); + qlge_set_multicast_list(qdev->ndev); + + rtnl_unlock(); return; error: QPRINTK(qdev, IFUP, ALERT, "Driver up/down cycle failed, closing device\n"); - rtnl_lock(); + set_bit(QL_ADAPTER_UP, &qdev->flags); dev_close(qdev->ndev); rtnl_unlock(); @@ -3834,11 +3878,14 @@ static int __devinit ql_init_device(struct pci_dev *pdev, return err; } + qdev->ndev = ndev; + qdev->pdev = pdev; + pci_set_drvdata(pdev, ndev); pos = pci_find_capability(pdev, PCI_CAP_ID_EXP); if (pos <= 0) { dev_err(&pdev->dev, PFX "Cannot find PCI Express capability, " "aborting.\n"); - goto err_out; + return pos; } else { pci_read_config_word(pdev, pos + PCI_EXP_DEVCTL, &val16); val16 &= ~PCI_EXP_DEVCTL_NOSNOOP_EN; @@ -3851,7 +3898,7 @@ static int __devinit ql_init_device(struct pci_dev *pdev, err = pci_request_regions(pdev, DRV_NAME); if (err) { dev_err(&pdev->dev, "PCI region request failed.\n"); - goto err_out; + return err; } pci_set_master(pdev); @@ -3869,7 +3916,9 @@ static int __devinit ql_init_device(struct pci_dev *pdev, goto err_out; } - pci_set_drvdata(pdev, ndev); + /* Set PCIe reset type for EEH to fundamental. */ + pdev->needs_freset = 1; + pci_save_state(pdev); qdev->reg_base = ioremap_nocache(pci_resource_start(pdev, 1), pci_resource_len(pdev, 1)); @@ -3889,8 +3938,6 @@ static int __devinit ql_init_device(struct pci_dev *pdev, goto err_out; } - qdev->ndev = ndev; - qdev->pdev = pdev; err = ql_get_board_info(qdev); if (err) { dev_err(&pdev->dev, "Register access failed.\n"); @@ -3930,7 +3977,6 @@ static int __devinit ql_init_device(struct pci_dev *pdev, INIT_DELAYED_WORK(&qdev->mpi_work, ql_mpi_work); INIT_DELAYED_WORK(&qdev->mpi_port_cfg_work, ql_mpi_port_cfg_work); INIT_DELAYED_WORK(&qdev->mpi_idc_work, ql_mpi_idc_work); - mutex_init(&qdev->mpi_mutex); init_completion(&qdev->ide_completion); if (!cards_found) { @@ -4027,6 +4073,33 @@ static void __devexit qlge_remove(struct pci_dev *pdev) free_netdev(ndev); } +/* Clean up resources without touching hardware. */ +static void ql_eeh_close(struct net_device *ndev) +{ + int i; + struct ql_adapter *qdev = netdev_priv(ndev); + + if (netif_carrier_ok(ndev)) { + netif_carrier_off(ndev); + netif_stop_queue(ndev); + } + + if (test_bit(QL_ADAPTER_UP, &qdev->flags)) + cancel_delayed_work_sync(&qdev->asic_reset_work); + cancel_delayed_work_sync(&qdev->mpi_reset_work); + cancel_delayed_work_sync(&qdev->mpi_work); + cancel_delayed_work_sync(&qdev->mpi_idc_work); + cancel_delayed_work_sync(&qdev->mpi_port_cfg_work); + + for (i = 0; i < qdev->rss_ring_count; i++) + netif_napi_del(&qdev->rx_ring[i].napi); + + clear_bit(QL_ADAPTER_UP, &qdev->flags); + ql_tx_ring_clean(qdev); + ql_free_rx_buffers(qdev); + ql_release_adapter_resources(qdev); +} + /* * This callback is called by the PCI subsystem whenever * a PCI bus error is detected. @@ -4035,17 +4108,21 @@ static pci_ers_result_t qlge_io_error_detected(struct pci_dev *pdev, enum pci_channel_state state) { struct net_device *ndev = pci_get_drvdata(pdev); - struct ql_adapter *qdev = netdev_priv(ndev); - - netif_device_detach(ndev); - if (state == pci_channel_io_perm_failure) + switch (state) { + case pci_channel_io_normal: + return PCI_ERS_RESULT_CAN_RECOVER; + case pci_channel_io_frozen: + netif_device_detach(ndev); + if (netif_running(ndev)) + ql_eeh_close(ndev); + pci_disable_device(pdev); + return PCI_ERS_RESULT_NEED_RESET; + case pci_channel_io_perm_failure: + dev_err(&pdev->dev, + "%s: pci_channel_io_perm_failure.\n", __func__); return PCI_ERS_RESULT_DISCONNECT; - - if (netif_running(ndev)) - ql_adapter_down(qdev); - - pci_disable_device(pdev); + } /* Request a slot reset. */ return PCI_ERS_RESULT_NEED_RESET; @@ -4062,25 +4139,15 @@ static pci_ers_result_t qlge_io_slot_reset(struct pci_dev *pdev) struct net_device *ndev = pci_get_drvdata(pdev); struct ql_adapter *qdev = netdev_priv(ndev); + pdev->error_state = pci_channel_io_normal; + + pci_restore_state(pdev); if (pci_enable_device(pdev)) { QPRINTK(qdev, IFUP, ERR, "Cannot re-enable PCI device after reset.\n"); return PCI_ERS_RESULT_DISCONNECT; } - pci_set_master(pdev); - - netif_carrier_off(ndev); - ql_adapter_reset(qdev); - - /* Make sure the EEPROM is good */ - memcpy(ndev->perm_addr, ndev->dev_addr, ndev->addr_len); - - if (!is_valid_ether_addr(ndev->perm_addr)) { - QPRINTK(qdev, IFUP, ERR, "After reset, invalid MAC address.\n"); - return PCI_ERS_RESULT_DISCONNECT; - } - return PCI_ERS_RESULT_RECOVERED; } @@ -4088,17 +4155,21 @@ static void qlge_io_resume(struct pci_dev *pdev) { struct net_device *ndev = pci_get_drvdata(pdev); struct ql_adapter *qdev = netdev_priv(ndev); + int err = 0; - pci_set_master(pdev); - + if (ql_adapter_reset(qdev)) + QPRINTK(qdev, DRV, ERR, "reset FAILED!\n"); if (netif_running(ndev)) { - if (ql_adapter_up(qdev)) { + err = qlge_open(ndev); + if (err) { QPRINTK(qdev, IFUP, ERR, "Device initialization failed after reset.\n"); return; } + } else { + QPRINTK(qdev, IFUP, ERR, + "Device was not running prior to EEH.\n"); } - netif_device_attach(ndev); } diff --git a/drivers/net/qlge/qlge_mpi.c b/drivers/net/qlge/qlge_mpi.c index 6685bd97da91..aec05f266107 100644 --- a/drivers/net/qlge/qlge_mpi.c +++ b/drivers/net/qlge/qlge_mpi.c @@ -470,9 +470,9 @@ end: */ static int ql_mailbox_command(struct ql_adapter *qdev, struct mbox_params *mbcp) { - int status, count; + int status; + unsigned long count; - mutex_lock(&qdev->mpi_mutex); /* Begin polled mode for MPI */ ql_write32(qdev, INTR_MASK, (INTR_MASK_PI << 16)); @@ -492,14 +492,14 @@ static int ql_mailbox_command(struct ql_adapter *qdev, struct mbox_params *mbcp) /* Wait for the command to complete. We loop * here because some AEN might arrive while * we're waiting for the mailbox command to - * complete. If more than 5 arrive then we can + * complete. If more than 5 seconds expire we can * assume something is wrong. */ - count = 5; + count = jiffies + HZ * MAILBOX_TIMEOUT; do { /* Wait for the interrupt to come in. */ status = ql_wait_mbx_cmd_cmplt(qdev); if (status) - goto end; + continue; /* Process the event. If it's an AEN, it * will be handled in-line or a worker @@ -518,15 +518,15 @@ static int ql_mailbox_command(struct ql_adapter *qdev, struct mbox_params *mbcp) MB_CMD_STS_GOOD) || ((mbcp->mbox_out[0] & 0x0000f000) == MB_CMD_STS_INTRMDT)) - break; - } while (--count); + goto done; + } while (time_before(jiffies, count)); - if (!count) { - QPRINTK(qdev, DRV, ERR, - "Timed out waiting for mailbox complete.\n"); - status = -ETIMEDOUT; - goto end; - } + QPRINTK(qdev, DRV, ERR, + "Timed out waiting for mailbox complete.\n"); + status = -ETIMEDOUT; + goto end; + +done: /* Now we can clear the interrupt condition * and look at our status. @@ -541,7 +541,6 @@ static int ql_mailbox_command(struct ql_adapter *qdev, struct mbox_params *mbcp) status = -EIO; } end: - mutex_unlock(&qdev->mpi_mutex); /* End polled mode for MPI */ ql_write32(qdev, INTR_MASK, (INTR_MASK_PI << 16) | INTR_MASK_PI); return status; @@ -770,13 +769,104 @@ static int ql_idc_wait(struct ql_adapter *qdev) return status; } +int ql_mb_set_mgmnt_traffic_ctl(struct ql_adapter *qdev, u32 control) +{ + struct mbox_params mbc; + struct mbox_params *mbcp = &mbc; + int status; + + memset(mbcp, 0, sizeof(struct mbox_params)); + + mbcp->in_count = 1; + mbcp->out_count = 2; + + mbcp->mbox_in[0] = MB_CMD_SET_MGMNT_TFK_CTL; + mbcp->mbox_in[1] = control; + + status = ql_mailbox_command(qdev, mbcp); + if (status) + return status; + + if (mbcp->mbox_out[0] == MB_CMD_STS_GOOD) + return status; + + if (mbcp->mbox_out[0] == MB_CMD_STS_INVLD_CMD) { + QPRINTK(qdev, DRV, ERR, + "Command not supported by firmware.\n"); + status = -EINVAL; + } else if (mbcp->mbox_out[0] == MB_CMD_STS_ERR) { + /* This indicates that the firmware is + * already in the state we are trying to + * change it to. + */ + QPRINTK(qdev, DRV, ERR, + "Command parameters make no change.\n"); + } + return status; +} + +/* Returns a negative error code or the mailbox command status. */ +static int ql_mb_get_mgmnt_traffic_ctl(struct ql_adapter *qdev, u32 *control) +{ + struct mbox_params mbc; + struct mbox_params *mbcp = &mbc; + int status; + + memset(mbcp, 0, sizeof(struct mbox_params)); + *control = 0; + + mbcp->in_count = 1; + mbcp->out_count = 1; + + mbcp->mbox_in[0] = MB_CMD_GET_MGMNT_TFK_CTL; + + status = ql_mailbox_command(qdev, mbcp); + if (status) + return status; + + if (mbcp->mbox_out[0] == MB_CMD_STS_GOOD) { + *control = mbcp->mbox_in[1]; + return status; + } + + if (mbcp->mbox_out[0] == MB_CMD_STS_INVLD_CMD) { + QPRINTK(qdev, DRV, ERR, + "Command not supported by firmware.\n"); + status = -EINVAL; + } else if (mbcp->mbox_out[0] == MB_CMD_STS_ERR) { + QPRINTK(qdev, DRV, ERR, + "Failed to get MPI traffic control.\n"); + status = -EIO; + } + return status; +} + +int ql_wait_fifo_empty(struct ql_adapter *qdev) +{ + int count = 5; + u32 mgmnt_fifo_empty; + u32 nic_fifo_empty; + + do { + nic_fifo_empty = ql_read32(qdev, STS) & STS_NFE; + ql_mb_get_mgmnt_traffic_ctl(qdev, &mgmnt_fifo_empty); + mgmnt_fifo_empty &= MB_GET_MPI_TFK_FIFO_EMPTY; + if (nic_fifo_empty && mgmnt_fifo_empty) + return 0; + msleep(100); + } while (count-- > 0); + return -ETIMEDOUT; +} + /* API called in work thread context to set new TX/RX * maximum frame size values to match MTU. */ static int ql_set_port_cfg(struct ql_adapter *qdev) { int status; + rtnl_lock(); status = ql_mb_set_port_cfg(qdev); + rtnl_unlock(); if (status) return status; status = ql_idc_wait(qdev); @@ -797,7 +887,9 @@ void ql_mpi_port_cfg_work(struct work_struct *work) container_of(work, struct ql_adapter, mpi_port_cfg_work.work); int status; + rtnl_lock(); status = ql_mb_get_port_cfg(qdev); + rtnl_unlock(); if (status) { QPRINTK(qdev, DRV, ERR, "Bug: Failed to get port config data.\n"); @@ -855,7 +947,9 @@ void ql_mpi_idc_work(struct work_struct *work) * needs to be set. * */ set_bit(QL_CAM_RT_SET, &qdev->flags); + rtnl_lock(); status = ql_mb_idc_ack(qdev); + rtnl_unlock(); if (status) { QPRINTK(qdev, DRV, ERR, "Bug: No pending IDC!\n"); @@ -871,7 +965,9 @@ void ql_mpi_work(struct work_struct *work) struct mbox_params *mbcp = &mbc; int err = 0; - mutex_lock(&qdev->mpi_mutex); + rtnl_lock(); + /* Begin polled mode for MPI */ + ql_write32(qdev, INTR_MASK, (INTR_MASK_PI << 16)); while (ql_read32(qdev, STS) & STS_PI) { memset(mbcp, 0, sizeof(struct mbox_params)); @@ -884,7 +980,9 @@ void ql_mpi_work(struct work_struct *work) break; } - mutex_unlock(&qdev->mpi_mutex); + /* End polled mode for MPI */ + ql_write32(qdev, INTR_MASK, (INTR_MASK_PI << 16) | INTR_MASK_PI); + rtnl_unlock(); ql_enable_completion_interrupt(qdev, 0); } diff --git a/drivers/net/r6040.c b/drivers/net/r6040.c index 7dfcb58b0eb4..8b14c6eda7c3 100644 --- a/drivers/net/r6040.c +++ b/drivers/net/r6040.c @@ -1085,7 +1085,7 @@ static int __devinit r6040_init_one(struct pci_dev *pdev, int bar = 0; u16 *adrp; - printk(KERN_INFO "%s\n", version); + printk("%s\n", version); err = pci_enable_device(pdev); if (err) diff --git a/drivers/net/r8169.c b/drivers/net/r8169.c index 50c6a3cfe439..0fe2fc90f207 100644 --- a/drivers/net/r8169.c +++ b/drivers/net/r8169.c @@ -115,7 +115,9 @@ enum mac_version { RTL_GIGA_MAC_VER_22 = 0x16, // 8168C RTL_GIGA_MAC_VER_23 = 0x17, // 8168CP RTL_GIGA_MAC_VER_24 = 0x18, // 8168CP - RTL_GIGA_MAC_VER_25 = 0x19 // 8168D + RTL_GIGA_MAC_VER_25 = 0x19, // 8168D + RTL_GIGA_MAC_VER_26 = 0x1a, // 8168D + RTL_GIGA_MAC_VER_27 = 0x1b // 8168DP }; #define _R(NAME,MAC,MASK) \ @@ -150,7 +152,9 @@ static const struct { _R("RTL8168c/8111c", RTL_GIGA_MAC_VER_22, 0xff7e1880), // PCI-E _R("RTL8168cp/8111cp", RTL_GIGA_MAC_VER_23, 0xff7e1880), // PCI-E _R("RTL8168cp/8111cp", RTL_GIGA_MAC_VER_24, 0xff7e1880), // PCI-E - _R("RTL8168d/8111d", RTL_GIGA_MAC_VER_25, 0xff7e1880) // PCI-E + _R("RTL8168d/8111d", RTL_GIGA_MAC_VER_25, 0xff7e1880), // PCI-E + _R("RTL8168d/8111d", RTL_GIGA_MAC_VER_26, 0xff7e1880), // PCI-E + _R("RTL8168dp/8111dp", RTL_GIGA_MAC_VER_27, 0xff7e1880) // PCI-E }; #undef _R @@ -253,6 +257,13 @@ enum rtl8168_8101_registers { DBG_REG = 0xd1, #define FIX_NAK_1 (1 << 4) #define FIX_NAK_2 (1 << 3) + EFUSEAR = 0xdc, +#define EFUSEAR_FLAG 0x80000000 +#define EFUSEAR_WRITE_CMD 0x80000000 +#define EFUSEAR_READ_CMD 0x00000000 +#define EFUSEAR_REG_MASK 0x03ff +#define EFUSEAR_REG_SHIFT 8 +#define EFUSEAR_DATA_MASK 0xff }; enum rtl_register_content { @@ -568,6 +579,14 @@ static void mdio_patch(void __iomem *ioaddr, int reg_addr, int value) mdio_write(ioaddr, reg_addr, mdio_read(ioaddr, reg_addr) | value); } +static void mdio_plus_minus(void __iomem *ioaddr, int reg_addr, int p, int m) +{ + int val; + + val = mdio_read(ioaddr, reg_addr); + mdio_write(ioaddr, reg_addr, (val | p) & ~m); +} + static void rtl_mdio_write(struct net_device *dev, int phy_id, int location, int val) { @@ -651,6 +670,24 @@ static u32 rtl_csi_read(void __iomem *ioaddr, int addr) return value; } +static u8 rtl8168d_efuse_read(void __iomem *ioaddr, int reg_addr) +{ + u8 value = 0xff; + unsigned int i; + + RTL_W32(EFUSEAR, (reg_addr & EFUSEAR_REG_MASK) << EFUSEAR_REG_SHIFT); + + for (i = 0; i < 300; i++) { + if (RTL_R32(EFUSEAR) & EFUSEAR_FLAG) { + value = RTL_R32(EFUSEAR) & EFUSEAR_DATA_MASK; + break; + } + udelay(100); + } + + return value; +} + static void rtl8169_irq_mask_and_ack(void __iomem *ioaddr) { RTL_W16(IntrMask, 0x0000); @@ -992,7 +1029,10 @@ static void rtl8169_vlan_rx_register(struct net_device *dev, spin_lock_irqsave(&tp->lock, flags); tp->vlgrp = grp; - if (tp->vlgrp) + /* + * Do not disable RxVlan on 8110SCd. + */ + if (tp->vlgrp || (tp->mac_version == RTL_GIGA_MAC_VER_05)) tp->cp_cmd |= RxVlan; else tp->cp_cmd &= ~RxVlan; @@ -1243,7 +1283,10 @@ static void rtl8169_get_mac_version(struct rtl8169_private *tp, int mac_version; } mac_info[] = { /* 8168D family. */ - { 0x7c800000, 0x28000000, RTL_GIGA_MAC_VER_25 }, + { 0x7cf00000, 0x28300000, RTL_GIGA_MAC_VER_26 }, + { 0x7cf00000, 0x28100000, RTL_GIGA_MAC_VER_25 }, + { 0x7c800000, 0x28800000, RTL_GIGA_MAC_VER_27 }, + { 0x7c800000, 0x28000000, RTL_GIGA_MAC_VER_26 }, /* 8168C family. */ { 0x7cf00000, 0x3ca00000, RTL_GIGA_MAC_VER_24 }, @@ -1648,74 +1691,903 @@ static void rtl8168c_4_hw_phy_config(void __iomem *ioaddr) rtl8168c_3_hw_phy_config(ioaddr); } -static void rtl8168d_hw_phy_config(void __iomem *ioaddr) +static void rtl8168d_1_hw_phy_config(void __iomem *ioaddr) { - struct phy_reg phy_reg_init_0[] = { + static struct phy_reg phy_reg_init_0[] = { { 0x1f, 0x0001 }, - { 0x09, 0x2770 }, - { 0x08, 0x04d0 }, - { 0x0b, 0xad15 }, - { 0x0c, 0x5bf0 }, - { 0x1c, 0xf101 }, + { 0x06, 0x4064 }, + { 0x07, 0x2863 }, + { 0x08, 0x059c }, + { 0x09, 0x26b4 }, + { 0x0a, 0x6a19 }, + { 0x0b, 0xdcc8 }, + { 0x10, 0xf06d }, + { 0x14, 0x7f68 }, + { 0x18, 0x7fd9 }, + { 0x1c, 0xf0ff }, + { 0x1d, 0x3d9c }, { 0x1f, 0x0003 }, - { 0x14, 0x94d7 }, - { 0x12, 0xf4d6 }, - { 0x09, 0xca0f }, - { 0x1f, 0x0002 }, - { 0x0b, 0x0b10 }, - { 0x0c, 0xd1f7 }, - { 0x1f, 0x0002 }, - { 0x06, 0x5461 }, + { 0x12, 0xf49f }, + { 0x13, 0x070b }, + { 0x1a, 0x05ad }, + { 0x14, 0x94c0 } + }; + static struct phy_reg phy_reg_init_1[] = { { 0x1f, 0x0002 }, - { 0x05, 0x6662 }, + { 0x06, 0x5561 }, + { 0x1f, 0x0005 }, + { 0x05, 0x8332 }, + { 0x06, 0x5561 } + }; + static struct phy_reg phy_reg_init_2[] = { + { 0x1f, 0x0005 }, + { 0x05, 0xffc2 }, + { 0x1f, 0x0005 }, + { 0x05, 0x8000 }, + { 0x06, 0xf8f9 }, + { 0x06, 0xfaef }, + { 0x06, 0x59ee }, + { 0x06, 0xf8ea }, + { 0x06, 0x00ee }, + { 0x06, 0xf8eb }, + { 0x06, 0x00e0 }, + { 0x06, 0xf87c }, + { 0x06, 0xe1f8 }, + { 0x06, 0x7d59 }, + { 0x06, 0x0fef }, + { 0x06, 0x0139 }, + { 0x06, 0x029e }, + { 0x06, 0x06ef }, + { 0x06, 0x1039 }, + { 0x06, 0x089f }, + { 0x06, 0x2aee }, + { 0x06, 0xf8ea }, + { 0x06, 0x00ee }, + { 0x06, 0xf8eb }, + { 0x06, 0x01e0 }, + { 0x06, 0xf87c }, + { 0x06, 0xe1f8 }, + { 0x06, 0x7d58 }, + { 0x06, 0x409e }, + { 0x06, 0x0f39 }, + { 0x06, 0x46aa }, + { 0x06, 0x0bbf }, + { 0x06, 0x8290 }, + { 0x06, 0xd682 }, + { 0x06, 0x9802 }, + { 0x06, 0x014f }, + { 0x06, 0xae09 }, + { 0x06, 0xbf82 }, + { 0x06, 0x98d6 }, + { 0x06, 0x82a0 }, + { 0x06, 0x0201 }, + { 0x06, 0x4fef }, + { 0x06, 0x95fe }, + { 0x06, 0xfdfc }, + { 0x06, 0x05f8 }, + { 0x06, 0xf9fa }, + { 0x06, 0xeef8 }, + { 0x06, 0xea00 }, + { 0x06, 0xeef8 }, + { 0x06, 0xeb00 }, + { 0x06, 0xe2f8 }, + { 0x06, 0x7ce3 }, + { 0x06, 0xf87d }, + { 0x06, 0xa511 }, + { 0x06, 0x1112 }, + { 0x06, 0xd240 }, + { 0x06, 0xd644 }, + { 0x06, 0x4402 }, + { 0x06, 0x8217 }, + { 0x06, 0xd2a0 }, + { 0x06, 0xd6aa }, + { 0x06, 0xaa02 }, + { 0x06, 0x8217 }, + { 0x06, 0xae0f }, + { 0x06, 0xa544 }, + { 0x06, 0x4402 }, + { 0x06, 0xae4d }, + { 0x06, 0xa5aa }, + { 0x06, 0xaa02 }, + { 0x06, 0xae47 }, + { 0x06, 0xaf82 }, + { 0x06, 0x13ee }, + { 0x06, 0x834e }, + { 0x06, 0x00ee }, + { 0x06, 0x834d }, + { 0x06, 0x0fee }, + { 0x06, 0x834c }, + { 0x06, 0x0fee }, + { 0x06, 0x834f }, + { 0x06, 0x00ee }, + { 0x06, 0x8351 }, + { 0x06, 0x00ee }, + { 0x06, 0x834a }, + { 0x06, 0xffee }, + { 0x06, 0x834b }, + { 0x06, 0xffe0 }, + { 0x06, 0x8330 }, + { 0x06, 0xe183 }, + { 0x06, 0x3158 }, + { 0x06, 0xfee4 }, + { 0x06, 0xf88a }, + { 0x06, 0xe5f8 }, + { 0x06, 0x8be0 }, + { 0x06, 0x8332 }, + { 0x06, 0xe183 }, + { 0x06, 0x3359 }, + { 0x06, 0x0fe2 }, + { 0x06, 0x834d }, + { 0x06, 0x0c24 }, + { 0x06, 0x5af0 }, + { 0x06, 0x1e12 }, + { 0x06, 0xe4f8 }, + { 0x06, 0x8ce5 }, + { 0x06, 0xf88d }, + { 0x06, 0xaf82 }, + { 0x06, 0x13e0 }, + { 0x06, 0x834f }, + { 0x06, 0x10e4 }, + { 0x06, 0x834f }, + { 0x06, 0xe083 }, + { 0x06, 0x4e78 }, + { 0x06, 0x009f }, + { 0x06, 0x0ae0 }, + { 0x06, 0x834f }, + { 0x06, 0xa010 }, + { 0x06, 0xa5ee }, + { 0x06, 0x834e }, + { 0x06, 0x01e0 }, + { 0x06, 0x834e }, + { 0x06, 0x7805 }, + { 0x06, 0x9e9a }, + { 0x06, 0xe083 }, + { 0x06, 0x4e78 }, + { 0x06, 0x049e }, + { 0x06, 0x10e0 }, + { 0x06, 0x834e }, + { 0x06, 0x7803 }, + { 0x06, 0x9e0f }, + { 0x06, 0xe083 }, + { 0x06, 0x4e78 }, + { 0x06, 0x019e }, + { 0x06, 0x05ae }, + { 0x06, 0x0caf }, + { 0x06, 0x81f8 }, + { 0x06, 0xaf81 }, + { 0x06, 0xa3af }, + { 0x06, 0x81dc }, + { 0x06, 0xaf82 }, + { 0x06, 0x13ee }, + { 0x06, 0x8348 }, + { 0x06, 0x00ee }, + { 0x06, 0x8349 }, + { 0x06, 0x00e0 }, + { 0x06, 0x8351 }, + { 0x06, 0x10e4 }, + { 0x06, 0x8351 }, + { 0x06, 0x5801 }, + { 0x06, 0x9fea }, + { 0x06, 0xd000 }, + { 0x06, 0xd180 }, + { 0x06, 0x1f66 }, + { 0x06, 0xe2f8 }, + { 0x06, 0xeae3 }, + { 0x06, 0xf8eb }, + { 0x06, 0x5af8 }, + { 0x06, 0x1e20 }, + { 0x06, 0xe6f8 }, + { 0x06, 0xeae5 }, + { 0x06, 0xf8eb }, + { 0x06, 0xd302 }, + { 0x06, 0xb3fe }, + { 0x06, 0xe2f8 }, + { 0x06, 0x7cef }, + { 0x06, 0x325b }, + { 0x06, 0x80e3 }, + { 0x06, 0xf87d }, + { 0x06, 0x9e03 }, + { 0x06, 0x7dff }, + { 0x06, 0xff0d }, + { 0x06, 0x581c }, + { 0x06, 0x551a }, + { 0x06, 0x6511 }, + { 0x06, 0xa190 }, + { 0x06, 0xd3e2 }, + { 0x06, 0x8348 }, + { 0x06, 0xe383 }, + { 0x06, 0x491b }, + { 0x06, 0x56ab }, + { 0x06, 0x08ef }, + { 0x06, 0x56e6 }, + { 0x06, 0x8348 }, + { 0x06, 0xe783 }, + { 0x06, 0x4910 }, + { 0x06, 0xd180 }, + { 0x06, 0x1f66 }, + { 0x06, 0xa004 }, + { 0x06, 0xb9e2 }, + { 0x06, 0x8348 }, + { 0x06, 0xe383 }, + { 0x06, 0x49ef }, + { 0x06, 0x65e2 }, + { 0x06, 0x834a }, + { 0x06, 0xe383 }, + { 0x06, 0x4b1b }, + { 0x06, 0x56aa }, + { 0x06, 0x0eef }, + { 0x06, 0x56e6 }, + { 0x06, 0x834a }, + { 0x06, 0xe783 }, + { 0x06, 0x4be2 }, + { 0x06, 0x834d }, + { 0x06, 0xe683 }, + { 0x06, 0x4ce0 }, + { 0x06, 0x834d }, + { 0x06, 0xa000 }, + { 0x06, 0x0caf }, + { 0x06, 0x81dc }, + { 0x06, 0xe083 }, + { 0x06, 0x4d10 }, + { 0x06, 0xe483 }, + { 0x06, 0x4dae }, + { 0x06, 0x0480 }, + { 0x06, 0xe483 }, + { 0x06, 0x4de0 }, + { 0x06, 0x834e }, + { 0x06, 0x7803 }, + { 0x06, 0x9e0b }, + { 0x06, 0xe083 }, + { 0x06, 0x4e78 }, + { 0x06, 0x049e }, + { 0x06, 0x04ee }, + { 0x06, 0x834e }, + { 0x06, 0x02e0 }, + { 0x06, 0x8332 }, + { 0x06, 0xe183 }, + { 0x06, 0x3359 }, + { 0x06, 0x0fe2 }, + { 0x06, 0x834d }, + { 0x06, 0x0c24 }, + { 0x06, 0x5af0 }, + { 0x06, 0x1e12 }, + { 0x06, 0xe4f8 }, + { 0x06, 0x8ce5 }, + { 0x06, 0xf88d }, + { 0x06, 0xe083 }, + { 0x06, 0x30e1 }, + { 0x06, 0x8331 }, + { 0x06, 0x6801 }, + { 0x06, 0xe4f8 }, + { 0x06, 0x8ae5 }, + { 0x06, 0xf88b }, + { 0x06, 0xae37 }, + { 0x06, 0xee83 }, + { 0x06, 0x4e03 }, + { 0x06, 0xe083 }, + { 0x06, 0x4ce1 }, + { 0x06, 0x834d }, + { 0x06, 0x1b01 }, + { 0x06, 0x9e04 }, + { 0x06, 0xaaa1 }, + { 0x06, 0xaea8 }, + { 0x06, 0xee83 }, + { 0x06, 0x4e04 }, + { 0x06, 0xee83 }, + { 0x06, 0x4f00 }, + { 0x06, 0xaeab }, + { 0x06, 0xe083 }, + { 0x06, 0x4f78 }, + { 0x06, 0x039f }, + { 0x06, 0x14ee }, + { 0x06, 0x834e }, + { 0x06, 0x05d2 }, + { 0x06, 0x40d6 }, + { 0x06, 0x5554 }, + { 0x06, 0x0282 }, + { 0x06, 0x17d2 }, + { 0x06, 0xa0d6 }, + { 0x06, 0xba00 }, + { 0x06, 0x0282 }, + { 0x06, 0x17fe }, + { 0x06, 0xfdfc }, + { 0x06, 0x05f8 }, + { 0x06, 0xe0f8 }, + { 0x06, 0x60e1 }, + { 0x06, 0xf861 }, + { 0x06, 0x6802 }, + { 0x06, 0xe4f8 }, + { 0x06, 0x60e5 }, + { 0x06, 0xf861 }, + { 0x06, 0xe0f8 }, + { 0x06, 0x48e1 }, + { 0x06, 0xf849 }, + { 0x06, 0x580f }, + { 0x06, 0x1e02 }, + { 0x06, 0xe4f8 }, + { 0x06, 0x48e5 }, + { 0x06, 0xf849 }, + { 0x06, 0xd000 }, + { 0x06, 0x0282 }, + { 0x06, 0x5bbf }, + { 0x06, 0x8350 }, + { 0x06, 0xef46 }, + { 0x06, 0xdc19 }, + { 0x06, 0xddd0 }, + { 0x06, 0x0102 }, + { 0x06, 0x825b }, + { 0x06, 0x0282 }, + { 0x06, 0x77e0 }, + { 0x06, 0xf860 }, + { 0x06, 0xe1f8 }, + { 0x06, 0x6158 }, + { 0x06, 0xfde4 }, + { 0x06, 0xf860 }, + { 0x06, 0xe5f8 }, + { 0x06, 0x61fc }, + { 0x06, 0x04f9 }, + { 0x06, 0xfafb }, + { 0x06, 0xc6bf }, + { 0x06, 0xf840 }, + { 0x06, 0xbe83 }, + { 0x06, 0x50a0 }, + { 0x06, 0x0101 }, + { 0x06, 0x071b }, + { 0x06, 0x89cf }, + { 0x06, 0xd208 }, + { 0x06, 0xebdb }, + { 0x06, 0x19b2 }, + { 0x06, 0xfbff }, + { 0x06, 0xfefd }, + { 0x06, 0x04f8 }, + { 0x06, 0xe0f8 }, + { 0x06, 0x48e1 }, + { 0x06, 0xf849 }, + { 0x06, 0x6808 }, + { 0x06, 0xe4f8 }, + { 0x06, 0x48e5 }, + { 0x06, 0xf849 }, + { 0x06, 0x58f7 }, + { 0x06, 0xe4f8 }, + { 0x06, 0x48e5 }, + { 0x06, 0xf849 }, + { 0x06, 0xfc04 }, + { 0x06, 0x4d20 }, + { 0x06, 0x0002 }, + { 0x06, 0x4e22 }, + { 0x06, 0x0002 }, + { 0x06, 0x4ddf }, + { 0x06, 0xff01 }, + { 0x06, 0x4edd }, + { 0x06, 0xff01 }, + { 0x05, 0x83d4 }, + { 0x06, 0x8000 }, + { 0x05, 0x83d8 }, + { 0x06, 0x8051 }, + { 0x02, 0x6010 }, + { 0x03, 0xdc00 }, + { 0x05, 0xfff6 }, + { 0x06, 0x00fc }, { 0x1f, 0x0000 }, - { 0x14, 0x0060 }, + { 0x1f, 0x0000 }, - { 0x0d, 0xf8a0 }, + { 0x0d, 0xf880 }, + { 0x1f, 0x0000 } + }; + + rtl_phy_write(ioaddr, phy_reg_init_0, ARRAY_SIZE(phy_reg_init_0)); + + mdio_write(ioaddr, 0x1f, 0x0002); + mdio_plus_minus(ioaddr, 0x0b, 0x0010, 0x00ef); + mdio_plus_minus(ioaddr, 0x0c, 0xa200, 0x5d00); + + rtl_phy_write(ioaddr, phy_reg_init_1, ARRAY_SIZE(phy_reg_init_1)); + + if (rtl8168d_efuse_read(ioaddr, 0x01) == 0xb1) { + struct phy_reg phy_reg_init[] = { + { 0x1f, 0x0002 }, + { 0x05, 0x669a }, + { 0x1f, 0x0005 }, + { 0x05, 0x8330 }, + { 0x06, 0x669a }, + { 0x1f, 0x0002 } + }; + int val; + + rtl_phy_write(ioaddr, phy_reg_init, ARRAY_SIZE(phy_reg_init)); + + val = mdio_read(ioaddr, 0x0d); + + if ((val & 0x00ff) != 0x006c) { + u32 set[] = { + 0x0065, 0x0066, 0x0067, 0x0068, + 0x0069, 0x006a, 0x006b, 0x006c + }; + int i; + + mdio_write(ioaddr, 0x1f, 0x0002); + + val &= 0xff00; + for (i = 0; i < ARRAY_SIZE(set); i++) + mdio_write(ioaddr, 0x0d, val | set[i]); + } + } else { + struct phy_reg phy_reg_init[] = { + { 0x1f, 0x0002 }, + { 0x05, 0x6662 }, + { 0x1f, 0x0005 }, + { 0x05, 0x8330 }, + { 0x06, 0x6662 } + }; + + rtl_phy_write(ioaddr, phy_reg_init, ARRAY_SIZE(phy_reg_init)); + } + + mdio_write(ioaddr, 0x1f, 0x0002); + mdio_patch(ioaddr, 0x0d, 0x0300); + mdio_patch(ioaddr, 0x0f, 0x0010); + + mdio_write(ioaddr, 0x1f, 0x0002); + mdio_plus_minus(ioaddr, 0x02, 0x0100, 0x0600); + mdio_plus_minus(ioaddr, 0x03, 0x0000, 0xe000); + + rtl_phy_write(ioaddr, phy_reg_init_2, ARRAY_SIZE(phy_reg_init_2)); +} + +static void rtl8168d_2_hw_phy_config(void __iomem *ioaddr) +{ + static struct phy_reg phy_reg_init_0[] = { + { 0x1f, 0x0001 }, + { 0x06, 0x4064 }, + { 0x07, 0x2863 }, + { 0x08, 0x059c }, + { 0x09, 0x26b4 }, + { 0x0a, 0x6a19 }, + { 0x0b, 0xdcc8 }, + { 0x10, 0xf06d }, + { 0x14, 0x7f68 }, + { 0x18, 0x7fd9 }, + { 0x1c, 0xf0ff }, + { 0x1d, 0x3d9c }, + { 0x1f, 0x0003 }, + { 0x12, 0xf49f }, + { 0x13, 0x070b }, + { 0x1a, 0x05ad }, + { 0x14, 0x94c0 }, + + { 0x1f, 0x0002 }, + { 0x06, 0x5561 }, { 0x1f, 0x0005 }, - { 0x05, 0xffc2 } + { 0x05, 0x8332 }, + { 0x06, 0x5561 } + }; + static struct phy_reg phy_reg_init_1[] = { + { 0x1f, 0x0005 }, + { 0x05, 0xffc2 }, + { 0x1f, 0x0005 }, + { 0x05, 0x8000 }, + { 0x06, 0xf8f9 }, + { 0x06, 0xfaee }, + { 0x06, 0xf8ea }, + { 0x06, 0x00ee }, + { 0x06, 0xf8eb }, + { 0x06, 0x00e2 }, + { 0x06, 0xf87c }, + { 0x06, 0xe3f8 }, + { 0x06, 0x7da5 }, + { 0x06, 0x1111 }, + { 0x06, 0x12d2 }, + { 0x06, 0x40d6 }, + { 0x06, 0x4444 }, + { 0x06, 0x0281 }, + { 0x06, 0xc6d2 }, + { 0x06, 0xa0d6 }, + { 0x06, 0xaaaa }, + { 0x06, 0x0281 }, + { 0x06, 0xc6ae }, + { 0x06, 0x0fa5 }, + { 0x06, 0x4444 }, + { 0x06, 0x02ae }, + { 0x06, 0x4da5 }, + { 0x06, 0xaaaa }, + { 0x06, 0x02ae }, + { 0x06, 0x47af }, + { 0x06, 0x81c2 }, + { 0x06, 0xee83 }, + { 0x06, 0x4e00 }, + { 0x06, 0xee83 }, + { 0x06, 0x4d0f }, + { 0x06, 0xee83 }, + { 0x06, 0x4c0f }, + { 0x06, 0xee83 }, + { 0x06, 0x4f00 }, + { 0x06, 0xee83 }, + { 0x06, 0x5100 }, + { 0x06, 0xee83 }, + { 0x06, 0x4aff }, + { 0x06, 0xee83 }, + { 0x06, 0x4bff }, + { 0x06, 0xe083 }, + { 0x06, 0x30e1 }, + { 0x06, 0x8331 }, + { 0x06, 0x58fe }, + { 0x06, 0xe4f8 }, + { 0x06, 0x8ae5 }, + { 0x06, 0xf88b }, + { 0x06, 0xe083 }, + { 0x06, 0x32e1 }, + { 0x06, 0x8333 }, + { 0x06, 0x590f }, + { 0x06, 0xe283 }, + { 0x06, 0x4d0c }, + { 0x06, 0x245a }, + { 0x06, 0xf01e }, + { 0x06, 0x12e4 }, + { 0x06, 0xf88c }, + { 0x06, 0xe5f8 }, + { 0x06, 0x8daf }, + { 0x06, 0x81c2 }, + { 0x06, 0xe083 }, + { 0x06, 0x4f10 }, + { 0x06, 0xe483 }, + { 0x06, 0x4fe0 }, + { 0x06, 0x834e }, + { 0x06, 0x7800 }, + { 0x06, 0x9f0a }, + { 0x06, 0xe083 }, + { 0x06, 0x4fa0 }, + { 0x06, 0x10a5 }, + { 0x06, 0xee83 }, + { 0x06, 0x4e01 }, + { 0x06, 0xe083 }, + { 0x06, 0x4e78 }, + { 0x06, 0x059e }, + { 0x06, 0x9ae0 }, + { 0x06, 0x834e }, + { 0x06, 0x7804 }, + { 0x06, 0x9e10 }, + { 0x06, 0xe083 }, + { 0x06, 0x4e78 }, + { 0x06, 0x039e }, + { 0x06, 0x0fe0 }, + { 0x06, 0x834e }, + { 0x06, 0x7801 }, + { 0x06, 0x9e05 }, + { 0x06, 0xae0c }, + { 0x06, 0xaf81 }, + { 0x06, 0xa7af }, + { 0x06, 0x8152 }, + { 0x06, 0xaf81 }, + { 0x06, 0x8baf }, + { 0x06, 0x81c2 }, + { 0x06, 0xee83 }, + { 0x06, 0x4800 }, + { 0x06, 0xee83 }, + { 0x06, 0x4900 }, + { 0x06, 0xe083 }, + { 0x06, 0x5110 }, + { 0x06, 0xe483 }, + { 0x06, 0x5158 }, + { 0x06, 0x019f }, + { 0x06, 0xead0 }, + { 0x06, 0x00d1 }, + { 0x06, 0x801f }, + { 0x06, 0x66e2 }, + { 0x06, 0xf8ea }, + { 0x06, 0xe3f8 }, + { 0x06, 0xeb5a }, + { 0x06, 0xf81e }, + { 0x06, 0x20e6 }, + { 0x06, 0xf8ea }, + { 0x06, 0xe5f8 }, + { 0x06, 0xebd3 }, + { 0x06, 0x02b3 }, + { 0x06, 0xfee2 }, + { 0x06, 0xf87c }, + { 0x06, 0xef32 }, + { 0x06, 0x5b80 }, + { 0x06, 0xe3f8 }, + { 0x06, 0x7d9e }, + { 0x06, 0x037d }, + { 0x06, 0xffff }, + { 0x06, 0x0d58 }, + { 0x06, 0x1c55 }, + { 0x06, 0x1a65 }, + { 0x06, 0x11a1 }, + { 0x06, 0x90d3 }, + { 0x06, 0xe283 }, + { 0x06, 0x48e3 }, + { 0x06, 0x8349 }, + { 0x06, 0x1b56 }, + { 0x06, 0xab08 }, + { 0x06, 0xef56 }, + { 0x06, 0xe683 }, + { 0x06, 0x48e7 }, + { 0x06, 0x8349 }, + { 0x06, 0x10d1 }, + { 0x06, 0x801f }, + { 0x06, 0x66a0 }, + { 0x06, 0x04b9 }, + { 0x06, 0xe283 }, + { 0x06, 0x48e3 }, + { 0x06, 0x8349 }, + { 0x06, 0xef65 }, + { 0x06, 0xe283 }, + { 0x06, 0x4ae3 }, + { 0x06, 0x834b }, + { 0x06, 0x1b56 }, + { 0x06, 0xaa0e }, + { 0x06, 0xef56 }, + { 0x06, 0xe683 }, + { 0x06, 0x4ae7 }, + { 0x06, 0x834b }, + { 0x06, 0xe283 }, + { 0x06, 0x4de6 }, + { 0x06, 0x834c }, + { 0x06, 0xe083 }, + { 0x06, 0x4da0 }, + { 0x06, 0x000c }, + { 0x06, 0xaf81 }, + { 0x06, 0x8be0 }, + { 0x06, 0x834d }, + { 0x06, 0x10e4 }, + { 0x06, 0x834d }, + { 0x06, 0xae04 }, + { 0x06, 0x80e4 }, + { 0x06, 0x834d }, + { 0x06, 0xe083 }, + { 0x06, 0x4e78 }, + { 0x06, 0x039e }, + { 0x06, 0x0be0 }, + { 0x06, 0x834e }, + { 0x06, 0x7804 }, + { 0x06, 0x9e04 }, + { 0x06, 0xee83 }, + { 0x06, 0x4e02 }, + { 0x06, 0xe083 }, + { 0x06, 0x32e1 }, + { 0x06, 0x8333 }, + { 0x06, 0x590f }, + { 0x06, 0xe283 }, + { 0x06, 0x4d0c }, + { 0x06, 0x245a }, + { 0x06, 0xf01e }, + { 0x06, 0x12e4 }, + { 0x06, 0xf88c }, + { 0x06, 0xe5f8 }, + { 0x06, 0x8de0 }, + { 0x06, 0x8330 }, + { 0x06, 0xe183 }, + { 0x06, 0x3168 }, + { 0x06, 0x01e4 }, + { 0x06, 0xf88a }, + { 0x06, 0xe5f8 }, + { 0x06, 0x8bae }, + { 0x06, 0x37ee }, + { 0x06, 0x834e }, + { 0x06, 0x03e0 }, + { 0x06, 0x834c }, + { 0x06, 0xe183 }, + { 0x06, 0x4d1b }, + { 0x06, 0x019e }, + { 0x06, 0x04aa }, + { 0x06, 0xa1ae }, + { 0x06, 0xa8ee }, + { 0x06, 0x834e }, + { 0x06, 0x04ee }, + { 0x06, 0x834f }, + { 0x06, 0x00ae }, + { 0x06, 0xabe0 }, + { 0x06, 0x834f }, + { 0x06, 0x7803 }, + { 0x06, 0x9f14 }, + { 0x06, 0xee83 }, + { 0x06, 0x4e05 }, + { 0x06, 0xd240 }, + { 0x06, 0xd655 }, + { 0x06, 0x5402 }, + { 0x06, 0x81c6 }, + { 0x06, 0xd2a0 }, + { 0x06, 0xd6ba }, + { 0x06, 0x0002 }, + { 0x06, 0x81c6 }, + { 0x06, 0xfefd }, + { 0x06, 0xfc05 }, + { 0x06, 0xf8e0 }, + { 0x06, 0xf860 }, + { 0x06, 0xe1f8 }, + { 0x06, 0x6168 }, + { 0x06, 0x02e4 }, + { 0x06, 0xf860 }, + { 0x06, 0xe5f8 }, + { 0x06, 0x61e0 }, + { 0x06, 0xf848 }, + { 0x06, 0xe1f8 }, + { 0x06, 0x4958 }, + { 0x06, 0x0f1e }, + { 0x06, 0x02e4 }, + { 0x06, 0xf848 }, + { 0x06, 0xe5f8 }, + { 0x06, 0x49d0 }, + { 0x06, 0x0002 }, + { 0x06, 0x820a }, + { 0x06, 0xbf83 }, + { 0x06, 0x50ef }, + { 0x06, 0x46dc }, + { 0x06, 0x19dd }, + { 0x06, 0xd001 }, + { 0x06, 0x0282 }, + { 0x06, 0x0a02 }, + { 0x06, 0x8226 }, + { 0x06, 0xe0f8 }, + { 0x06, 0x60e1 }, + { 0x06, 0xf861 }, + { 0x06, 0x58fd }, + { 0x06, 0xe4f8 }, + { 0x06, 0x60e5 }, + { 0x06, 0xf861 }, + { 0x06, 0xfc04 }, + { 0x06, 0xf9fa }, + { 0x06, 0xfbc6 }, + { 0x06, 0xbff8 }, + { 0x06, 0x40be }, + { 0x06, 0x8350 }, + { 0x06, 0xa001 }, + { 0x06, 0x0107 }, + { 0x06, 0x1b89 }, + { 0x06, 0xcfd2 }, + { 0x06, 0x08eb }, + { 0x06, 0xdb19 }, + { 0x06, 0xb2fb }, + { 0x06, 0xfffe }, + { 0x06, 0xfd04 }, + { 0x06, 0xf8e0 }, + { 0x06, 0xf848 }, + { 0x06, 0xe1f8 }, + { 0x06, 0x4968 }, + { 0x06, 0x08e4 }, + { 0x06, 0xf848 }, + { 0x06, 0xe5f8 }, + { 0x06, 0x4958 }, + { 0x06, 0xf7e4 }, + { 0x06, 0xf848 }, + { 0x06, 0xe5f8 }, + { 0x06, 0x49fc }, + { 0x06, 0x044d }, + { 0x06, 0x2000 }, + { 0x06, 0x024e }, + { 0x06, 0x2200 }, + { 0x06, 0x024d }, + { 0x06, 0xdfff }, + { 0x06, 0x014e }, + { 0x06, 0xddff }, + { 0x06, 0x0100 }, + { 0x05, 0x83d8 }, + { 0x06, 0x8000 }, + { 0x03, 0xdc00 }, + { 0x05, 0xfff6 }, + { 0x06, 0x00fc }, + { 0x1f, 0x0000 }, + + { 0x1f, 0x0000 }, + { 0x0d, 0xf880 }, + { 0x1f, 0x0000 } }; rtl_phy_write(ioaddr, phy_reg_init_0, ARRAY_SIZE(phy_reg_init_0)); - if (mdio_read(ioaddr, 0x06) == 0xc400) { - struct phy_reg phy_reg_init_1[] = { + if (rtl8168d_efuse_read(ioaddr, 0x01) == 0xb1) { + struct phy_reg phy_reg_init[] = { + { 0x1f, 0x0002 }, + { 0x05, 0x669a }, { 0x1f, 0x0005 }, - { 0x01, 0x0300 }, - { 0x1f, 0x0000 }, - { 0x11, 0x401c }, - { 0x16, 0x4100 }, + { 0x05, 0x8330 }, + { 0x06, 0x669a }, + + { 0x1f, 0x0002 } + }; + int val; + + rtl_phy_write(ioaddr, phy_reg_init, ARRAY_SIZE(phy_reg_init)); + + val = mdio_read(ioaddr, 0x0d); + if ((val & 0x00ff) != 0x006c) { + u32 set[] = { + 0x0065, 0x0066, 0x0067, 0x0068, + 0x0069, 0x006a, 0x006b, 0x006c + }; + int i; + + mdio_write(ioaddr, 0x1f, 0x0002); + + val &= 0xff00; + for (i = 0; i < ARRAY_SIZE(set); i++) + mdio_write(ioaddr, 0x0d, val | set[i]); + } + } else { + struct phy_reg phy_reg_init[] = { + { 0x1f, 0x0002 }, + { 0x05, 0x2642 }, { 0x1f, 0x0005 }, - { 0x07, 0x0010 }, - { 0x05, 0x83dc }, - { 0x06, 0x087d }, - { 0x05, 0x8300 }, - { 0x06, 0x0101 }, - { 0x06, 0x05f8 }, - { 0x06, 0xf9fa }, - { 0x06, 0xfbef }, - { 0x06, 0x79e2 }, - { 0x06, 0x835f }, - { 0x06, 0xe0f8 }, - { 0x06, 0x9ae1 }, - { 0x06, 0xf89b }, - { 0x06, 0xef31 }, - { 0x06, 0x3b65 }, - { 0x06, 0xaa07 }, - { 0x06, 0x81e4 }, - { 0x06, 0xf89a }, - { 0x06, 0xe5f8 }, - { 0x06, 0x9baf }, - { 0x06, 0x06ae }, - { 0x05, 0x83dc }, - { 0x06, 0x8300 }, + { 0x05, 0x8330 }, + { 0x06, 0x2642 } }; - rtl_phy_write(ioaddr, phy_reg_init_1, - ARRAY_SIZE(phy_reg_init_1)); + rtl_phy_write(ioaddr, phy_reg_init, ARRAY_SIZE(phy_reg_init)); } - mdio_write(ioaddr, 0x1f, 0x0000); + mdio_write(ioaddr, 0x1f, 0x0002); + mdio_plus_minus(ioaddr, 0x02, 0x0100, 0x0600); + mdio_plus_minus(ioaddr, 0x03, 0x0000, 0xe000); + + mdio_write(ioaddr, 0x1f, 0x0001); + mdio_write(ioaddr, 0x17, 0x0cc0); + + mdio_write(ioaddr, 0x1f, 0x0002); + mdio_patch(ioaddr, 0x0f, 0x0017); + + rtl_phy_write(ioaddr, phy_reg_init_1, ARRAY_SIZE(phy_reg_init_1)); +} + +static void rtl8168d_3_hw_phy_config(void __iomem *ioaddr) +{ + struct phy_reg phy_reg_init[] = { + { 0x1f, 0x0002 }, + { 0x10, 0x0008 }, + { 0x0d, 0x006c }, + + { 0x1f, 0x0000 }, + { 0x0d, 0xf880 }, + + { 0x1f, 0x0001 }, + { 0x17, 0x0cc0 }, + + { 0x1f, 0x0001 }, + { 0x0b, 0xa4d8 }, + { 0x09, 0x281c }, + { 0x07, 0x2883 }, + { 0x0a, 0x6b35 }, + { 0x1d, 0x3da4 }, + { 0x1c, 0xeffd }, + { 0x14, 0x7f52 }, + { 0x18, 0x7fc6 }, + { 0x08, 0x0601 }, + { 0x06, 0x4063 }, + { 0x10, 0xf074 }, + { 0x1f, 0x0003 }, + { 0x13, 0x0789 }, + { 0x12, 0xf4bd }, + { 0x1a, 0x04fd }, + { 0x14, 0x84b0 }, + { 0x1f, 0x0000 }, + { 0x00, 0x9200 }, + + { 0x1f, 0x0005 }, + { 0x01, 0x0340 }, + { 0x1f, 0x0001 }, + { 0x04, 0x4000 }, + { 0x03, 0x1d21 }, + { 0x02, 0x0c32 }, + { 0x01, 0x0200 }, + { 0x00, 0x5554 }, + { 0x04, 0x4800 }, + { 0x04, 0x4000 }, + { 0x04, 0xf000 }, + { 0x03, 0xdf01 }, + { 0x02, 0xdf20 }, + { 0x01, 0x101a }, + { 0x00, 0xa0ff }, + { 0x04, 0xf800 }, + { 0x04, 0xf000 }, + { 0x1f, 0x0000 }, + + { 0x1f, 0x0007 }, + { 0x1e, 0x0023 }, + { 0x16, 0x0000 }, + { 0x1f, 0x0000 } + }; + + rtl_phy_write(ioaddr, phy_reg_init, ARRAY_SIZE(phy_reg_init)); } static void rtl8102e_hw_phy_config(void __iomem *ioaddr) @@ -1792,7 +2664,13 @@ static void rtl_hw_phy_config(struct net_device *dev) rtl8168cp_2_hw_phy_config(ioaddr); break; case RTL_GIGA_MAC_VER_25: - rtl8168d_hw_phy_config(ioaddr); + rtl8168d_1_hw_phy_config(ioaddr); + break; + case RTL_GIGA_MAC_VER_26: + rtl8168d_2_hw_phy_config(ioaddr); + break; + case RTL_GIGA_MAC_VER_27: + rtl8168d_3_hw_phy_config(ioaddr); break; default: @@ -2322,6 +3200,14 @@ rtl8169_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) } rtl8169_init_phy(dev, tp); + + /* + * Pretend we are using VLANs; This bypasses a nasty bug where + * Interrupts stop flowing on high load on 8110SCd controllers. + */ + if (tp->mac_version == RTL_GIGA_MAC_VER_05) + RTL_W16(CPlusCmd, RTL_R16(CPlusCmd) | RxVlan); + device_set_wakeup_enable(&pdev->dev, tp->features & RTL_FEATURE_WOL); out: @@ -2349,6 +3235,10 @@ static void __devexit rtl8169_remove_one(struct pci_dev *pdev) flush_scheduled_work(); unregister_netdev(dev); + + /* restore original MAC address */ + rtl_rar_set(tp, dev->perm_addr); + rtl_disable_msi(pdev, tp); rtl8169_release_board(pdev, dev, tp->mmio_addr); pci_set_drvdata(pdev, NULL); @@ -2357,9 +3247,9 @@ static void __devexit rtl8169_remove_one(struct pci_dev *pdev) static void rtl8169_set_rxbufsize(struct rtl8169_private *tp, struct net_device *dev) { - unsigned int mtu = dev->mtu; + unsigned int max_frame = dev->mtu + VLAN_ETH_HLEN + ETH_FCS_LEN; - tp->rx_buf_sz = (mtu > RX_BUF_SIZE) ? mtu + ETH_HLEN + 8 : RX_BUF_SIZE; + tp->rx_buf_sz = (max_frame > RX_BUF_SIZE) ? max_frame : RX_BUF_SIZE; } static int rtl8169_open(struct net_device *dev) @@ -2493,7 +3383,7 @@ static u16 rtl_rw_cpluscmd(void __iomem *ioaddr) static void rtl_set_rx_max_size(void __iomem *ioaddr, unsigned int rx_buf_sz) { /* Low hurts. Let's disable the filtering. */ - RTL_W16(RxMaxSize, rx_buf_sz); + RTL_W16(RxMaxSize, rx_buf_sz + 1); } static void rtl8169_set_magic_reg(void __iomem *ioaddr, unsigned mac_version) @@ -2863,6 +3753,8 @@ static void rtl_hw_start_8168(struct net_device *dev) break; case RTL_GIGA_MAC_VER_25: + case RTL_GIGA_MAC_VER_26: + case RTL_GIGA_MAC_VER_27: rtl_hw_start_8168d(ioaddr, pdev); break; @@ -3993,6 +4885,9 @@ static void rtl_shutdown(struct pci_dev *pdev) rtl8169_net_suspend(dev); + /* restore original MAC address */ + rtl_rar_set(tp, dev->perm_addr); + spin_lock_irq(&tp->lock); rtl8169_asic_down(ioaddr); diff --git a/drivers/net/s2io.c b/drivers/net/s2io.c index ddccf5fa56b6..0dd7839322bc 100644 --- a/drivers/net/s2io.c +++ b/drivers/net/s2io.c @@ -3494,6 +3494,7 @@ static void s2io_reset(struct s2io_nic *sp) /* Restore the PCI state saved during initialization. */ pci_restore_state(sp->pdev); + pci_save_state(sp->pdev); pci_read_config_word(sp->pdev, 0x2, &val16); if (check_pci_device_id(val16) != (u16)PCI_ANY_ID) break; diff --git a/drivers/net/sb1000.c b/drivers/net/sb1000.c index ee366c5a8fa3..c9c70ab0cce0 100644 --- a/drivers/net/sb1000.c +++ b/drivers/net/sb1000.c @@ -36,6 +36,7 @@ static char version[] = "sb1000.c:v1.1.2 6/01/98 (fventuri@mediaone.net)\n"; #include <linux/module.h> #include <linux/kernel.h> +#include <linux/sched.h> #include <linux/string.h> #include <linux/interrupt.h> #include <linux/errno.h> diff --git a/drivers/net/sfc/efx.c b/drivers/net/sfc/efx.c index 07a7e4b8f8fc..cc4b2f99989d 100644 --- a/drivers/net/sfc/efx.c +++ b/drivers/net/sfc/efx.c @@ -884,13 +884,12 @@ static int efx_wanted_rx_queues(void) int count; int cpu; - if (unlikely(!alloc_cpumask_var(&core_mask, GFP_KERNEL))) { + if (unlikely(!zalloc_cpumask_var(&core_mask, GFP_KERNEL))) { printk(KERN_WARNING "sfc: RSS disabled due to allocation failure\n"); return 1; } - cpumask_clear(core_mask); count = 0; for_each_online_cpu(cpu) { if (!cpumask_test_cpu(cpu, core_mask)) { diff --git a/drivers/net/sfc/rx.c b/drivers/net/sfc/rx.c index 01f9432c31ef..98bff5ada09a 100644 --- a/drivers/net/sfc/rx.c +++ b/drivers/net/sfc/rx.c @@ -444,7 +444,8 @@ static void efx_rx_packet__check_len(struct efx_rx_queue *rx_queue, * the appropriate LRO method */ static void efx_rx_packet_lro(struct efx_channel *channel, - struct efx_rx_buffer *rx_buf) + struct efx_rx_buffer *rx_buf, + bool checksummed) { struct napi_struct *napi = &channel->napi_str; @@ -466,7 +467,8 @@ static void efx_rx_packet_lro(struct efx_channel *channel, skb->len = rx_buf->len; skb->data_len = rx_buf->len; skb->truesize += rx_buf->len; - skb->ip_summed = CHECKSUM_UNNECESSARY; + skb->ip_summed = + checksummed ? CHECKSUM_UNNECESSARY : CHECKSUM_NONE; napi_gro_frags(napi); @@ -475,6 +477,7 @@ out: rx_buf->page = NULL; } else { EFX_BUG_ON_PARANOID(!rx_buf->skb); + EFX_BUG_ON_PARANOID(!checksummed); napi_gro_receive(napi, rx_buf->skb); rx_buf->skb = NULL; @@ -570,7 +573,7 @@ void __efx_rx_packet(struct efx_channel *channel, } if (likely(checksummed || rx_buf->page)) { - efx_rx_packet_lro(channel, rx_buf); + efx_rx_packet_lro(channel, rx_buf, checksummed); goto done; } diff --git a/drivers/net/sfc/sfe4001.c b/drivers/net/sfc/sfe4001.c index cee00ad49b57..49eb91b5f50c 100644 --- a/drivers/net/sfc/sfe4001.c +++ b/drivers/net/sfc/sfe4001.c @@ -188,7 +188,7 @@ static int sfn4111t_reset(struct efx_nic *efx) efx_oword_t reg; /* GPIO 3 and the GPIO register are shared with I2C, so block that */ - mutex_lock(&efx->i2c_adap.bus_lock); + i2c_lock_adapter(&efx->i2c_adap); /* Pull RST_N (GPIO 2) low then let it up again, setting the * FLASH_CFG_1 strap (GPIO 3) appropriately. Only change the @@ -204,7 +204,7 @@ static int sfn4111t_reset(struct efx_nic *efx) falcon_write(efx, ®, GPIO_CTL_REG_KER); msleep(1); - mutex_unlock(&efx->i2c_adap.bus_lock); + i2c_unlock_adapter(&efx->i2c_adap); ssleep(1); return 0; diff --git a/drivers/net/sgiseeq.c b/drivers/net/sgiseeq.c index ecf3279fbef5..f4dfd1f679a9 100644 --- a/drivers/net/sgiseeq.c +++ b/drivers/net/sgiseeq.c @@ -826,7 +826,7 @@ static int __exit sgiseeq_remove(struct platform_device *pdev) static struct platform_driver sgiseeq_driver = { .probe = sgiseeq_probe, - .remove = __devexit_p(sgiseeq_remove), + .remove = __exit_p(sgiseeq_remove), .driver = { .name = "sgiseeq", .owner = THIS_MODULE, diff --git a/drivers/net/sh_eth.c b/drivers/net/sh_eth.c index f49d0800c1d1..528b912a4b0d 100644 --- a/drivers/net/sh_eth.c +++ b/drivers/net/sh_eth.c @@ -30,6 +30,7 @@ #include <linux/phy.h> #include <linux/cache.h> #include <linux/io.h> +#include <asm/cacheflush.h> #include "sh_eth.h" diff --git a/drivers/net/sis900.c b/drivers/net/sis900.c index 97949d0a699b..c072f7f36acf 100644 --- a/drivers/net/sis900.c +++ b/drivers/net/sis900.c @@ -52,6 +52,7 @@ #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/kernel.h> +#include <linux/sched.h> #include <linux/string.h> #include <linux/timer.h> #include <linux/errno.h> diff --git a/drivers/net/skfp/skfddi.c b/drivers/net/skfp/skfddi.c index 38a508b4aad9..b27156eaf267 100644 --- a/drivers/net/skfp/skfddi.c +++ b/drivers/net/skfp/skfddi.c @@ -73,6 +73,7 @@ static const char * const boot_msg = /* Include files */ +#include <linux/capability.h> #include <linux/module.h> #include <linux/kernel.h> #include <linux/errno.h> diff --git a/drivers/net/skge.c b/drivers/net/skge.c index 55bad4081966..8f5414348e86 100644 --- a/drivers/net/skge.c +++ b/drivers/net/skge.c @@ -37,6 +37,7 @@ #include <linux/crc32.h> #include <linux/dma-mapping.h> #include <linux/debugfs.h> +#include <linux/sched.h> #include <linux/seq_file.h> #include <linux/mii.h> #include <asm/irq.h> @@ -3935,11 +3936,14 @@ static int __devinit skge_probe(struct pci_dev *pdev, #endif err = -ENOMEM; - hw = kzalloc(sizeof(*hw), GFP_KERNEL); + /* space for skge@pci:0000:04:00.0 */ + hw = kzalloc(sizeof(*hw) + strlen(DRV_NAME "@pci:" ) + + strlen(pci_name(pdev)) + 1, GFP_KERNEL); if (!hw) { dev_err(&pdev->dev, "cannot allocate hardware struct\n"); goto err_out_free_regions; } + sprintf(hw->irq_name, DRV_NAME "@pci:%s", pci_name(pdev)); hw->pdev = pdev; spin_lock_init(&hw->hw_lock); @@ -3974,7 +3978,7 @@ static int __devinit skge_probe(struct pci_dev *pdev, goto err_out_free_netdev; } - err = request_irq(pdev->irq, skge_intr, IRQF_SHARED, dev->name, hw); + err = request_irq(pdev->irq, skge_intr, IRQF_SHARED, hw->irq_name, hw); if (err) { dev_err(&pdev->dev, "%s: cannot assign irq %d\n", dev->name, pdev->irq); @@ -3982,14 +3986,17 @@ static int __devinit skge_probe(struct pci_dev *pdev, } skge_show_addr(dev); - if (hw->ports > 1 && (dev1 = skge_devinit(hw, 1, using_dac))) { - if (register_netdev(dev1) == 0) + if (hw->ports > 1) { + dev1 = skge_devinit(hw, 1, using_dac); + if (dev1 && register_netdev(dev1) == 0) skge_show_addr(dev1); else { /* Failure to register second port need not be fatal */ dev_warn(&pdev->dev, "register of second port failed\n"); hw->dev[1] = NULL; - free_netdev(dev1); + hw->ports = 1; + if (dev1) + free_netdev(dev1); } } pci_set_drvdata(pdev, hw); diff --git a/drivers/net/skge.h b/drivers/net/skge.h index 17caccbb7685..831de1b6e96e 100644 --- a/drivers/net/skge.h +++ b/drivers/net/skge.h @@ -2423,6 +2423,8 @@ struct skge_hw { u16 phy_addr; spinlock_t phy_lock; struct tasklet_struct phy_task; + + char irq_name[0]; /* skge@pci:000:04:00.0 */ }; enum pause_control { diff --git a/drivers/net/sky2.c b/drivers/net/sky2.c index 15140f9f2e92..6a10d7ba5877 100644 --- a/drivers/net/sky2.c +++ b/drivers/net/sky2.c @@ -1497,7 +1497,6 @@ static int sky2_up(struct net_device *dev) if (ramsize > 0) { u32 rxspace; - hw->flags |= SKY2_HW_RAM_BUFFER; pr_debug(PFX "%s: ram buffer %dK\n", dev->name, ramsize); if (ramsize < 16) rxspace = ramsize / 2; @@ -2926,6 +2925,9 @@ static int __devinit sky2_init(struct sky2_hw *hw) ++hw->ports; } + if (sky2_read8(hw, B2_E_0)) + hw->flags |= SKY2_HW_RAM_BUFFER; + return 0; } @@ -4485,13 +4487,16 @@ static int __devinit sky2_probe(struct pci_dev *pdev, wol_default = device_may_wakeup(&pdev->dev) ? WAKE_MAGIC : 0; err = -ENOMEM; - hw = kzalloc(sizeof(*hw), GFP_KERNEL); + + hw = kzalloc(sizeof(*hw) + strlen(DRV_NAME "@pci:") + + strlen(pci_name(pdev)) + 1, GFP_KERNEL); if (!hw) { dev_err(&pdev->dev, "cannot allocate hardware struct\n"); goto err_out_free_regions; } hw->pdev = pdev; + sprintf(hw->irq_name, DRV_NAME "@pci:%s", pci_name(pdev)); hw->regs = ioremap_nocache(pci_resource_start(pdev, 0), 0x4000); if (!hw->regs) { @@ -4533,11 +4538,13 @@ static int __devinit sky2_probe(struct pci_dev *pdev, goto err_out_free_netdev; } + netif_carrier_off(dev); + netif_napi_add(dev, &hw->napi, sky2_poll, NAPI_WEIGHT); err = request_irq(pdev->irq, sky2_intr, (hw->flags & SKY2_HW_USE_MSI) ? 0 : IRQF_SHARED, - dev->name, hw); + hw->irq_name, hw); if (err) { dev_err(&pdev->dev, "cannot assign irq %d\n", pdev->irq); goto err_out_unregister; diff --git a/drivers/net/sky2.h b/drivers/net/sky2.h index e0f23a101043..ed54129698b4 100644 --- a/drivers/net/sky2.h +++ b/drivers/net/sky2.h @@ -2085,6 +2085,8 @@ struct sky2_hw { struct timer_list watchdog_timer; struct work_struct restart_work; wait_queue_head_t msi_wait; + + char irq_name[0]; }; static inline int sky2_is_copper(const struct sky2_hw *hw) diff --git a/drivers/net/slip.c b/drivers/net/slip.c index e17c535a577e..fe3cebb984de 100644 --- a/drivers/net/slip.c +++ b/drivers/net/slip.c @@ -67,6 +67,7 @@ #include <asm/system.h> #include <asm/uaccess.h> #include <linux/bitops.h> +#include <linux/sched.h> #include <linux/string.h> #include <linux/mm.h> #include <linux/interrupt.h> diff --git a/drivers/net/smc91x.c b/drivers/net/smc91x.c index 05c91ee6921e..f12206bdbb75 100644 --- a/drivers/net/smc91x.c +++ b/drivers/net/smc91x.c @@ -2283,7 +2283,7 @@ static int __devinit smc_drv_probe(struct platform_device *pdev) ndev->irq = ires->start; - if (ires->flags & IRQF_TRIGGER_MASK) + if (irq_flags == -1 || ires->flags & IRQF_TRIGGER_MASK) irq_flags = ires->flags & IRQF_TRIGGER_MASK; ret = smc_request_attrib(pdev, ndev); diff --git a/drivers/net/smsc911x.c b/drivers/net/smsc911x.c index ccdd196f5297..f9cdcbcb77d4 100644 --- a/drivers/net/smsc911x.c +++ b/drivers/net/smsc911x.c @@ -986,7 +986,7 @@ static int smsc911x_poll(struct napi_struct *napi, int budget) struct net_device *dev = pdata->dev; int npackets = 0; - while (likely(netif_running(dev)) && (npackets < budget)) { + while (npackets < budget) { unsigned int pktlength; unsigned int pktwords; struct sk_buff *skb; diff --git a/drivers/net/smsc9420.c b/drivers/net/smsc9420.c index b4909a2dec66..0f7909276237 100644 --- a/drivers/net/smsc9420.c +++ b/drivers/net/smsc9420.c @@ -252,6 +252,9 @@ static int smsc9420_ethtool_get_settings(struct net_device *dev, { struct smsc9420_pdata *pd = netdev_priv(dev); + if (!pd->phy_dev) + return -ENODEV; + cmd->maxtxpkt = 1; cmd->maxrxpkt = 1; return phy_ethtool_gset(pd->phy_dev, cmd); @@ -262,6 +265,9 @@ static int smsc9420_ethtool_set_settings(struct net_device *dev, { struct smsc9420_pdata *pd = netdev_priv(dev); + if (!pd->phy_dev) + return -ENODEV; + return phy_ethtool_sset(pd->phy_dev, cmd); } @@ -290,6 +296,10 @@ static void smsc9420_ethtool_set_msglevel(struct net_device *netdev, u32 data) static int smsc9420_ethtool_nway_reset(struct net_device *netdev) { struct smsc9420_pdata *pd = netdev_priv(netdev); + + if (!pd->phy_dev) + return -ENODEV; + return phy_start_aneg(pd->phy_dev); } @@ -312,6 +322,10 @@ smsc9420_ethtool_getregs(struct net_device *dev, struct ethtool_regs *regs, for (i = 0; i < 0x100; i += (sizeof(u32))) data[j++] = smsc9420_reg_read(pd, i); + // cannot read phy registers if the net device is down + if (!phy_dev) + return; + for (i = 0; i <= 31; i++) data[j++] = smsc9420_mii_read(phy_dev->bus, phy_dev->addr, i); } diff --git a/drivers/net/stmmac/Kconfig b/drivers/net/stmmac/Kconfig new file mode 100644 index 000000000000..35eaa5251d7f --- /dev/null +++ b/drivers/net/stmmac/Kconfig @@ -0,0 +1,53 @@ +config STMMAC_ETH + tristate "STMicroelectronics 10/100/1000 Ethernet driver" + select MII + select PHYLIB + depends on NETDEVICES && CPU_SUBTYPE_ST40 + help + This is the driver for the ST MAC 10/100/1000 on-chip Ethernet + controllers. ST Ethernet IPs are built around a Synopsys IP Core. + +if STMMAC_ETH + +config STMMAC_DA + bool "STMMAC DMA arbitration scheme" + default n + help + Selecting this option, rx has priority over Tx (only for Giga + Ethernet device). + By default, the DMA arbitration scheme is based on Round-robin + (rx:tx priority is 1:1). + +config STMMAC_DUAL_MAC + bool "STMMAC: dual mac support (EXPERIMENTAL)" + default n + depends on EXPERIMENTAL && STMMAC_ETH && !STMMAC_TIMER + help + Some ST SoCs (for example the stx7141 and stx7200c2) have two + Ethernet Controllers. This option turns on the second Ethernet + device on this kind of platforms. + +config STMMAC_TIMER + bool "STMMAC Timer optimisation" + default n + help + Use an external timer for mitigating the number of network + interrupts. + +choice + prompt "Select Timer device" + depends on STMMAC_TIMER + +config STMMAC_TMU_TIMER + bool "TMU channel 2" + depends on CPU_SH4 + help + +config STMMAC_RTC_TIMER + bool "Real time clock" + depends on RTC_CLASS + help + +endchoice + +endif diff --git a/drivers/net/stmmac/Makefile b/drivers/net/stmmac/Makefile new file mode 100644 index 000000000000..b2d7a5564dfa --- /dev/null +++ b/drivers/net/stmmac/Makefile @@ -0,0 +1,4 @@ +obj-$(CONFIG_STMMAC_ETH) += stmmac.o +stmmac-$(CONFIG_STMMAC_TIMER) += stmmac_timer.o +stmmac-objs:= stmmac_main.o stmmac_ethtool.o stmmac_mdio.o \ + mac100.o gmac.o $(stmmac-y) diff --git a/drivers/net/stmmac/common.h b/drivers/net/stmmac/common.h new file mode 100644 index 000000000000..e49e5188e887 --- /dev/null +++ b/drivers/net/stmmac/common.h @@ -0,0 +1,330 @@ +/******************************************************************************* + STMMAC Common Header File + + Copyright (C) 2007-2009 STMicroelectronics Ltd + + This program is free software; you can redistribute it and/or modify it + under the terms and conditions of the GNU General Public License, + version 2, as published by the Free Software Foundation. + + This program is distributed in the hope it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. + + You should have received a copy of the GNU General Public License along with + this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + + The full GNU General Public License is included in this distribution in + the file called "COPYING". + + Author: Giuseppe Cavallaro <peppe.cavallaro@st.com> +*******************************************************************************/ + +#include "descs.h" +#include <linux/io.h> + +/* ********************************************* + DMA CRS Control and Status Register Mapping + * *********************************************/ +#define DMA_BUS_MODE 0x00001000 /* Bus Mode */ +#define DMA_XMT_POLL_DEMAND 0x00001004 /* Transmit Poll Demand */ +#define DMA_RCV_POLL_DEMAND 0x00001008 /* Received Poll Demand */ +#define DMA_RCV_BASE_ADDR 0x0000100c /* Receive List Base */ +#define DMA_TX_BASE_ADDR 0x00001010 /* Transmit List Base */ +#define DMA_STATUS 0x00001014 /* Status Register */ +#define DMA_CONTROL 0x00001018 /* Ctrl (Operational Mode) */ +#define DMA_INTR_ENA 0x0000101c /* Interrupt Enable */ +#define DMA_MISSED_FRAME_CTR 0x00001020 /* Missed Frame Counter */ +#define DMA_CUR_TX_BUF_ADDR 0x00001050 /* Current Host Tx Buffer */ +#define DMA_CUR_RX_BUF_ADDR 0x00001054 /* Current Host Rx Buffer */ + +/* ******************************** + DMA Control register defines + * ********************************/ +#define DMA_CONTROL_ST 0x00002000 /* Start/Stop Transmission */ +#define DMA_CONTROL_SR 0x00000002 /* Start/Stop Receive */ + +/* ************************************** + DMA Interrupt Enable register defines + * **************************************/ +/**** NORMAL INTERRUPT ****/ +#define DMA_INTR_ENA_NIE 0x00010000 /* Normal Summary */ +#define DMA_INTR_ENA_TIE 0x00000001 /* Transmit Interrupt */ +#define DMA_INTR_ENA_TUE 0x00000004 /* Transmit Buffer Unavailable */ +#define DMA_INTR_ENA_RIE 0x00000040 /* Receive Interrupt */ +#define DMA_INTR_ENA_ERE 0x00004000 /* Early Receive */ + +#define DMA_INTR_NORMAL (DMA_INTR_ENA_NIE | DMA_INTR_ENA_RIE | \ + DMA_INTR_ENA_TIE) + +/**** ABNORMAL INTERRUPT ****/ +#define DMA_INTR_ENA_AIE 0x00008000 /* Abnormal Summary */ +#define DMA_INTR_ENA_FBE 0x00002000 /* Fatal Bus Error */ +#define DMA_INTR_ENA_ETE 0x00000400 /* Early Transmit */ +#define DMA_INTR_ENA_RWE 0x00000200 /* Receive Watchdog */ +#define DMA_INTR_ENA_RSE 0x00000100 /* Receive Stopped */ +#define DMA_INTR_ENA_RUE 0x00000080 /* Receive Buffer Unavailable */ +#define DMA_INTR_ENA_UNE 0x00000020 /* Tx Underflow */ +#define DMA_INTR_ENA_OVE 0x00000010 /* Receive Overflow */ +#define DMA_INTR_ENA_TJE 0x00000008 /* Transmit Jabber */ +#define DMA_INTR_ENA_TSE 0x00000002 /* Transmit Stopped */ + +#define DMA_INTR_ABNORMAL (DMA_INTR_ENA_AIE | DMA_INTR_ENA_FBE | \ + DMA_INTR_ENA_UNE) + +/* DMA default interrupt mask */ +#define DMA_INTR_DEFAULT_MASK (DMA_INTR_NORMAL | DMA_INTR_ABNORMAL) + +/* **************************** + * DMA Status register defines + * ****************************/ +#define DMA_STATUS_GPI 0x10000000 /* PMT interrupt */ +#define DMA_STATUS_GMI 0x08000000 /* MMC interrupt */ +#define DMA_STATUS_GLI 0x04000000 /* GMAC Line interface int. */ +#define DMA_STATUS_GMI 0x08000000 +#define DMA_STATUS_GLI 0x04000000 +#define DMA_STATUS_EB_MASK 0x00380000 /* Error Bits Mask */ +#define DMA_STATUS_EB_TX_ABORT 0x00080000 /* Error Bits - TX Abort */ +#define DMA_STATUS_EB_RX_ABORT 0x00100000 /* Error Bits - RX Abort */ +#define DMA_STATUS_TS_MASK 0x00700000 /* Transmit Process State */ +#define DMA_STATUS_TS_SHIFT 20 +#define DMA_STATUS_RS_MASK 0x000e0000 /* Receive Process State */ +#define DMA_STATUS_RS_SHIFT 17 +#define DMA_STATUS_NIS 0x00010000 /* Normal Interrupt Summary */ +#define DMA_STATUS_AIS 0x00008000 /* Abnormal Interrupt Summary */ +#define DMA_STATUS_ERI 0x00004000 /* Early Receive Interrupt */ +#define DMA_STATUS_FBI 0x00002000 /* Fatal Bus Error Interrupt */ +#define DMA_STATUS_ETI 0x00000400 /* Early Transmit Interrupt */ +#define DMA_STATUS_RWT 0x00000200 /* Receive Watchdog Timeout */ +#define DMA_STATUS_RPS 0x00000100 /* Receive Process Stopped */ +#define DMA_STATUS_RU 0x00000080 /* Receive Buffer Unavailable */ +#define DMA_STATUS_RI 0x00000040 /* Receive Interrupt */ +#define DMA_STATUS_UNF 0x00000020 /* Transmit Underflow */ +#define DMA_STATUS_OVF 0x00000010 /* Receive Overflow */ +#define DMA_STATUS_TJT 0x00000008 /* Transmit Jabber Timeout */ +#define DMA_STATUS_TU 0x00000004 /* Transmit Buffer Unavailable */ +#define DMA_STATUS_TPS 0x00000002 /* Transmit Process Stopped */ +#define DMA_STATUS_TI 0x00000001 /* Transmit Interrupt */ + +/* Other defines */ +#define HASH_TABLE_SIZE 64 +#define PAUSE_TIME 0x200 + +/* Flow Control defines */ +#define FLOW_OFF 0 +#define FLOW_RX 1 +#define FLOW_TX 2 +#define FLOW_AUTO (FLOW_TX | FLOW_RX) + +/* DMA STORE-AND-FORWARD Operation Mode */ +#define SF_DMA_MODE 1 + +#define HW_CSUM 1 +#define NO_HW_CSUM 0 + +/* GMAC TX FIFO is 8K, Rx FIFO is 16K */ +#define BUF_SIZE_16KiB 16384 +#define BUF_SIZE_8KiB 8192 +#define BUF_SIZE_4KiB 4096 +#define BUF_SIZE_2KiB 2048 + +/* Power Down and WOL */ +#define PMT_NOT_SUPPORTED 0 +#define PMT_SUPPORTED 1 + +/* Common MAC defines */ +#define MAC_CTRL_REG 0x00000000 /* MAC Control */ +#define MAC_ENABLE_TX 0x00000008 /* Transmitter Enable */ +#define MAC_RNABLE_RX 0x00000004 /* Receiver Enable */ + +/* MAC Management Counters register */ +#define MMC_CONTROL 0x00000100 /* MMC Control */ +#define MMC_HIGH_INTR 0x00000104 /* MMC High Interrupt */ +#define MMC_LOW_INTR 0x00000108 /* MMC Low Interrupt */ +#define MMC_HIGH_INTR_MASK 0x0000010c /* MMC High Interrupt Mask */ +#define MMC_LOW_INTR_MASK 0x00000110 /* MMC Low Interrupt Mask */ + +#define MMC_CONTROL_MAX_FRM_MASK 0x0003ff8 /* Maximum Frame Size */ +#define MMC_CONTROL_MAX_FRM_SHIFT 3 +#define MMC_CONTROL_MAX_FRAME 0x7FF + +struct stmmac_extra_stats { + /* Transmit errors */ + unsigned long tx_underflow ____cacheline_aligned; + unsigned long tx_carrier; + unsigned long tx_losscarrier; + unsigned long tx_heartbeat; + unsigned long tx_deferred; + unsigned long tx_vlan; + unsigned long tx_jabber; + unsigned long tx_frame_flushed; + unsigned long tx_payload_error; + unsigned long tx_ip_header_error; + /* Receive errors */ + unsigned long rx_desc; + unsigned long rx_partial; + unsigned long rx_runt; + unsigned long rx_toolong; + unsigned long rx_collision; + unsigned long rx_crc; + unsigned long rx_lenght; + unsigned long rx_mii; + unsigned long rx_multicast; + unsigned long rx_gmac_overflow; + unsigned long rx_watchdog; + unsigned long da_rx_filter_fail; + unsigned long sa_rx_filter_fail; + unsigned long rx_missed_cntr; + unsigned long rx_overflow_cntr; + unsigned long rx_vlan; + /* Tx/Rx IRQ errors */ + unsigned long tx_undeflow_irq; + unsigned long tx_process_stopped_irq; + unsigned long tx_jabber_irq; + unsigned long rx_overflow_irq; + unsigned long rx_buf_unav_irq; + unsigned long rx_process_stopped_irq; + unsigned long rx_watchdog_irq; + unsigned long tx_early_irq; + unsigned long fatal_bus_error_irq; + /* Extra info */ + unsigned long threshold; + unsigned long tx_pkt_n; + unsigned long rx_pkt_n; + unsigned long poll_n; + unsigned long sched_timer_n; + unsigned long normal_irq_n; +}; + +/* GMAC core can compute the checksums in HW. */ +enum rx_frame_status { + good_frame = 0, + discard_frame = 1, + csum_none = 2, +}; + +static inline void stmmac_set_mac_addr(unsigned long ioaddr, u8 addr[6], + unsigned int high, unsigned int low) +{ + unsigned long data; + + data = (addr[5] << 8) | addr[4]; + writel(data, ioaddr + high); + data = (addr[3] << 24) | (addr[2] << 16) | (addr[1] << 8) | addr[0]; + writel(data, ioaddr + low); + + return; +} + +static inline void stmmac_get_mac_addr(unsigned long ioaddr, + unsigned char *addr, unsigned int high, + unsigned int low) +{ + unsigned int hi_addr, lo_addr; + + /* Read the MAC address from the hardware */ + hi_addr = readl(ioaddr + high); + lo_addr = readl(ioaddr + low); + + /* Extract the MAC address from the high and low words */ + addr[0] = lo_addr & 0xff; + addr[1] = (lo_addr >> 8) & 0xff; + addr[2] = (lo_addr >> 16) & 0xff; + addr[3] = (lo_addr >> 24) & 0xff; + addr[4] = hi_addr & 0xff; + addr[5] = (hi_addr >> 8) & 0xff; + + return; +} + +struct stmmac_ops { + /* MAC core initialization */ + void (*core_init) (unsigned long ioaddr) ____cacheline_aligned; + /* DMA core initialization */ + int (*dma_init) (unsigned long ioaddr, int pbl, u32 dma_tx, u32 dma_rx); + /* Dump MAC registers */ + void (*dump_mac_regs) (unsigned long ioaddr); + /* Dump DMA registers */ + void (*dump_dma_regs) (unsigned long ioaddr); + /* Set tx/rx threshold in the csr6 register + * An invalid value enables the store-and-forward mode */ + void (*dma_mode) (unsigned long ioaddr, int txmode, int rxmode); + /* To track extra statistic (if supported) */ + void (*dma_diagnostic_fr) (void *data, struct stmmac_extra_stats *x, + unsigned long ioaddr); + /* RX descriptor ring initialization */ + void (*init_rx_desc) (struct dma_desc *p, unsigned int ring_size, + int disable_rx_ic); + /* TX descriptor ring initialization */ + void (*init_tx_desc) (struct dma_desc *p, unsigned int ring_size); + + /* Invoked by the xmit function to prepare the tx descriptor */ + void (*prepare_tx_desc) (struct dma_desc *p, int is_fs, int len, + int csum_flag); + /* Set/get the owner of the descriptor */ + void (*set_tx_owner) (struct dma_desc *p); + int (*get_tx_owner) (struct dma_desc *p); + /* Invoked by the xmit function to close the tx descriptor */ + void (*close_tx_desc) (struct dma_desc *p); + /* Clean the tx descriptor as soon as the tx irq is received */ + void (*release_tx_desc) (struct dma_desc *p); + /* Clear interrupt on tx frame completion. When this bit is + * set an interrupt happens as soon as the frame is transmitted */ + void (*clear_tx_ic) (struct dma_desc *p); + /* Last tx segment reports the transmit status */ + int (*get_tx_ls) (struct dma_desc *p); + /* Return the transmit status looking at the TDES1 */ + int (*tx_status) (void *data, struct stmmac_extra_stats *x, + struct dma_desc *p, unsigned long ioaddr); + /* Get the buffer size from the descriptor */ + int (*get_tx_len) (struct dma_desc *p); + /* Handle extra events on specific interrupts hw dependent */ + void (*host_irq_status) (unsigned long ioaddr); + int (*get_rx_owner) (struct dma_desc *p); + void (*set_rx_owner) (struct dma_desc *p); + /* Get the receive frame size */ + int (*get_rx_frame_len) (struct dma_desc *p); + /* Return the reception status looking at the RDES1 */ + int (*rx_status) (void *data, struct stmmac_extra_stats *x, + struct dma_desc *p); + /* Multicast filter setting */ + void (*set_filter) (struct net_device *dev); + /* Flow control setting */ + void (*flow_ctrl) (unsigned long ioaddr, unsigned int duplex, + unsigned int fc, unsigned int pause_time); + /* Set power management mode (e.g. magic frame) */ + void (*pmt) (unsigned long ioaddr, unsigned long mode); + /* Set/Get Unicast MAC addresses */ + void (*set_umac_addr) (unsigned long ioaddr, unsigned char *addr, + unsigned int reg_n); + void (*get_umac_addr) (unsigned long ioaddr, unsigned char *addr, + unsigned int reg_n); +}; + +struct mac_link { + int port; + int duplex; + int speed; +}; + +struct mii_regs { + unsigned int addr; /* MII Address */ + unsigned int data; /* MII Data */ +}; + +struct hw_cap { + unsigned int version; /* Core Version register (GMAC) */ + unsigned int pmt; /* Power-Down mode (GMAC) */ + struct mac_link link; + struct mii_regs mii; +}; + +struct mac_device_info { + struct hw_cap hw; + struct stmmac_ops *ops; +}; + +struct mac_device_info *gmac_setup(unsigned long addr); +struct mac_device_info *mac100_setup(unsigned long addr); diff --git a/drivers/net/stmmac/descs.h b/drivers/net/stmmac/descs.h new file mode 100644 index 000000000000..6d2a0b2f5e57 --- /dev/null +++ b/drivers/net/stmmac/descs.h @@ -0,0 +1,163 @@ +/******************************************************************************* + Header File to describe the DMA descriptors + Use enhanced descriptors in case of GMAC Cores. + + This program is free software; you can redistribute it and/or modify it + under the terms and conditions of the GNU General Public License, + version 2, as published by the Free Software Foundation. + + This program is distributed in the hope it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. + + You should have received a copy of the GNU General Public License along with + this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + + The full GNU General Public License is included in this distribution in + the file called "COPYING". + + Author: Giuseppe Cavallaro <peppe.cavallaro@st.com> +*******************************************************************************/ +struct dma_desc { + /* Receive descriptor */ + union { + struct { + /* RDES0 */ + u32 reserved1:1; + u32 crc_error:1; + u32 dribbling:1; + u32 mii_error:1; + u32 receive_watchdog:1; + u32 frame_type:1; + u32 collision:1; + u32 frame_too_long:1; + u32 last_descriptor:1; + u32 first_descriptor:1; + u32 multicast_frame:1; + u32 run_frame:1; + u32 length_error:1; + u32 partial_frame_error:1; + u32 descriptor_error:1; + u32 error_summary:1; + u32 frame_length:14; + u32 filtering_fail:1; + u32 own:1; + /* RDES1 */ + u32 buffer1_size:11; + u32 buffer2_size:11; + u32 reserved2:2; + u32 second_address_chained:1; + u32 end_ring:1; + u32 reserved3:5; + u32 disable_ic:1; + } rx; + struct { + /* RDES0 */ + u32 payload_csum_error:1; + u32 crc_error:1; + u32 dribbling:1; + u32 error_gmii:1; + u32 receive_watchdog:1; + u32 frame_type:1; + u32 late_collision:1; + u32 ipc_csum_error:1; + u32 last_descriptor:1; + u32 first_descriptor:1; + u32 vlan_tag:1; + u32 overflow_error:1; + u32 length_error:1; + u32 sa_filter_fail:1; + u32 descriptor_error:1; + u32 error_summary:1; + u32 frame_length:14; + u32 da_filter_fail:1; + u32 own:1; + /* RDES1 */ + u32 buffer1_size:13; + u32 reserved1:1; + u32 second_address_chained:1; + u32 end_ring:1; + u32 buffer2_size:13; + u32 reserved2:2; + u32 disable_ic:1; + } erx; /* -- enhanced -- */ + + /* Transmit descriptor */ + struct { + /* TDES0 */ + u32 deferred:1; + u32 underflow_error:1; + u32 excessive_deferral:1; + u32 collision_count:4; + u32 heartbeat_fail:1; + u32 excessive_collisions:1; + u32 late_collision:1; + u32 no_carrier:1; + u32 loss_carrier:1; + u32 reserved1:3; + u32 error_summary:1; + u32 reserved2:15; + u32 own:1; + /* TDES1 */ + u32 buffer1_size:11; + u32 buffer2_size:11; + u32 reserved3:1; + u32 disable_padding:1; + u32 second_address_chained:1; + u32 end_ring:1; + u32 crc_disable:1; + u32 reserved4:2; + u32 first_segment:1; + u32 last_segment:1; + u32 interrupt:1; + } tx; + struct { + /* TDES0 */ + u32 deferred:1; + u32 underflow_error:1; + u32 excessive_deferral:1; + u32 collision_count:4; + u32 vlan_frame:1; + u32 excessive_collisions:1; + u32 late_collision:1; + u32 no_carrier:1; + u32 loss_carrier:1; + u32 payload_error:1; + u32 frame_flushed:1; + u32 jabber_timeout:1; + u32 error_summary:1; + u32 ip_header_error:1; + u32 time_stamp_status:1; + u32 reserved1:2; + u32 second_address_chained:1; + u32 end_ring:1; + u32 checksum_insertion:2; + u32 reserved2:1; + u32 time_stamp_enable:1; + u32 disable_padding:1; + u32 crc_disable:1; + u32 first_segment:1; + u32 last_segment:1; + u32 interrupt:1; + u32 own:1; + /* TDES1 */ + u32 buffer1_size:13; + u32 reserved3:3; + u32 buffer2_size:13; + u32 reserved4:3; + } etx; /* -- enhanced -- */ + } des01; + unsigned int des2; + unsigned int des3; +}; + +/* Transmit checksum insertion control */ +enum tdes_csum_insertion { + cic_disabled = 0, /* Checksum Insertion Control */ + cic_only_ip = 1, /* Only IP header */ + cic_no_pseudoheader = 2, /* IP header but pseudoheader + * is not calculated */ + cic_full = 3, /* IP header and pseudoheader */ +}; diff --git a/drivers/net/stmmac/gmac.c b/drivers/net/stmmac/gmac.c new file mode 100644 index 000000000000..b624bb5bae0a --- /dev/null +++ b/drivers/net/stmmac/gmac.c @@ -0,0 +1,693 @@ +/******************************************************************************* + This is the driver for the GMAC on-chip Ethernet controller for ST SoCs. + DWC Ether MAC 10/100/1000 Universal version 3.41a has been used for + developing this code. + + Copyright (C) 2007-2009 STMicroelectronics Ltd + + This program is free software; you can redistribute it and/or modify it + under the terms and conditions of the GNU General Public License, + version 2, as published by the Free Software Foundation. + + This program is distributed in the hope it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. + + You should have received a copy of the GNU General Public License along with + this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + + The full GNU General Public License is included in this distribution in + the file called "COPYING". + + Author: Giuseppe Cavallaro <peppe.cavallaro@st.com> +*******************************************************************************/ + +#include <linux/netdevice.h> +#include <linux/crc32.h> +#include <linux/mii.h> +#include <linux/phy.h> + +#include "stmmac.h" +#include "gmac.h" + +#undef GMAC_DEBUG +/*#define GMAC_DEBUG*/ +#undef FRAME_FILTER_DEBUG +/*#define FRAME_FILTER_DEBUG*/ +#ifdef GMAC_DEBUG +#define DBG(fmt, args...) printk(fmt, ## args) +#else +#define DBG(fmt, args...) do { } while (0) +#endif + +static void gmac_dump_regs(unsigned long ioaddr) +{ + int i; + pr_info("\t----------------------------------------------\n" + "\t GMAC registers (base addr = 0x%8x)\n" + "\t----------------------------------------------\n", + (unsigned int)ioaddr); + + for (i = 0; i < 55; i++) { + int offset = i * 4; + pr_info("\tReg No. %d (offset 0x%x): 0x%08x\n", i, + offset, readl(ioaddr + offset)); + } + return; +} + +static int gmac_dma_init(unsigned long ioaddr, int pbl, u32 dma_tx, u32 dma_rx) +{ + u32 value = readl(ioaddr + DMA_BUS_MODE); + /* DMA SW reset */ + value |= DMA_BUS_MODE_SFT_RESET; + writel(value, ioaddr + DMA_BUS_MODE); + do {} while ((readl(ioaddr + DMA_BUS_MODE) & DMA_BUS_MODE_SFT_RESET)); + + value = /* DMA_BUS_MODE_FB | */ DMA_BUS_MODE_4PBL | + ((pbl << DMA_BUS_MODE_PBL_SHIFT) | + (pbl << DMA_BUS_MODE_RPBL_SHIFT)); + +#ifdef CONFIG_STMMAC_DA + value |= DMA_BUS_MODE_DA; /* Rx has priority over tx */ +#endif + writel(value, ioaddr + DMA_BUS_MODE); + + /* Mask interrupts by writing to CSR7 */ + writel(DMA_INTR_DEFAULT_MASK, ioaddr + DMA_INTR_ENA); + + /* The base address of the RX/TX descriptor lists must be written into + * DMA CSR3 and CSR4, respectively. */ + writel(dma_tx, ioaddr + DMA_TX_BASE_ADDR); + writel(dma_rx, ioaddr + DMA_RCV_BASE_ADDR); + + return 0; +} + +/* Transmit FIFO flush operation */ +static void gmac_flush_tx_fifo(unsigned long ioaddr) +{ + u32 csr6 = readl(ioaddr + DMA_CONTROL); + writel((csr6 | DMA_CONTROL_FTF), ioaddr + DMA_CONTROL); + + do {} while ((readl(ioaddr + DMA_CONTROL) & DMA_CONTROL_FTF)); +} + +static void gmac_dma_operation_mode(unsigned long ioaddr, int txmode, + int rxmode) +{ + u32 csr6 = readl(ioaddr + DMA_CONTROL); + + if (txmode == SF_DMA_MODE) { + DBG(KERN_DEBUG "GMAC: enabling TX store and forward mode\n"); + /* Transmit COE type 2 cannot be done in cut-through mode. */ + csr6 |= DMA_CONTROL_TSF; + /* Operating on second frame increase the performance + * especially when transmit store-and-forward is used.*/ + csr6 |= DMA_CONTROL_OSF; + } else { + DBG(KERN_DEBUG "GMAC: disabling TX store and forward mode" + " (threshold = %d)\n", txmode); + csr6 &= ~DMA_CONTROL_TSF; + csr6 &= DMA_CONTROL_TC_TX_MASK; + /* Set the transmit threashold */ + if (txmode <= 32) + csr6 |= DMA_CONTROL_TTC_32; + else if (txmode <= 64) + csr6 |= DMA_CONTROL_TTC_64; + else if (txmode <= 128) + csr6 |= DMA_CONTROL_TTC_128; + else if (txmode <= 192) + csr6 |= DMA_CONTROL_TTC_192; + else + csr6 |= DMA_CONTROL_TTC_256; + } + + if (rxmode == SF_DMA_MODE) { + DBG(KERN_DEBUG "GMAC: enabling RX store and forward mode\n"); + csr6 |= DMA_CONTROL_RSF; + } else { + DBG(KERN_DEBUG "GMAC: disabling RX store and forward mode" + " (threshold = %d)\n", rxmode); + csr6 &= ~DMA_CONTROL_RSF; + csr6 &= DMA_CONTROL_TC_RX_MASK; + if (rxmode <= 32) + csr6 |= DMA_CONTROL_RTC_32; + else if (rxmode <= 64) + csr6 |= DMA_CONTROL_RTC_64; + else if (rxmode <= 96) + csr6 |= DMA_CONTROL_RTC_96; + else + csr6 |= DMA_CONTROL_RTC_128; + } + + writel(csr6, ioaddr + DMA_CONTROL); + return; +} + +/* Not yet implemented --- no RMON module */ +static void gmac_dma_diagnostic_fr(void *data, struct stmmac_extra_stats *x, + unsigned long ioaddr) +{ + return; +} + +static void gmac_dump_dma_regs(unsigned long ioaddr) +{ + int i; + pr_info(" DMA registers\n"); + for (i = 0; i < 22; i++) { + if ((i < 9) || (i > 17)) { + int offset = i * 4; + pr_err("\t Reg No. %d (offset 0x%x): 0x%08x\n", i, + (DMA_BUS_MODE + offset), + readl(ioaddr + DMA_BUS_MODE + offset)); + } + } + return; +} + +static int gmac_get_tx_frame_status(void *data, struct stmmac_extra_stats *x, + struct dma_desc *p, unsigned long ioaddr) +{ + int ret = 0; + struct net_device_stats *stats = (struct net_device_stats *)data; + + if (unlikely(p->des01.etx.error_summary)) { + DBG(KERN_ERR "GMAC TX error... 0x%08x\n", p->des01.etx); + if (unlikely(p->des01.etx.jabber_timeout)) { + DBG(KERN_ERR "\tjabber_timeout error\n"); + x->tx_jabber++; + } + + if (unlikely(p->des01.etx.frame_flushed)) { + DBG(KERN_ERR "\tframe_flushed error\n"); + x->tx_frame_flushed++; + gmac_flush_tx_fifo(ioaddr); + } + + if (unlikely(p->des01.etx.loss_carrier)) { + DBG(KERN_ERR "\tloss_carrier error\n"); + x->tx_losscarrier++; + stats->tx_carrier_errors++; + } + if (unlikely(p->des01.etx.no_carrier)) { + DBG(KERN_ERR "\tno_carrier error\n"); + x->tx_carrier++; + stats->tx_carrier_errors++; + } + if (unlikely(p->des01.etx.late_collision)) { + DBG(KERN_ERR "\tlate_collision error\n"); + stats->collisions += p->des01.etx.collision_count; + } + if (unlikely(p->des01.etx.excessive_collisions)) { + DBG(KERN_ERR "\texcessive_collisions\n"); + stats->collisions += p->des01.etx.collision_count; + } + if (unlikely(p->des01.etx.excessive_deferral)) { + DBG(KERN_INFO "\texcessive tx_deferral\n"); + x->tx_deferred++; + } + + if (unlikely(p->des01.etx.underflow_error)) { + DBG(KERN_ERR "\tunderflow error\n"); + gmac_flush_tx_fifo(ioaddr); + x->tx_underflow++; + } + + if (unlikely(p->des01.etx.ip_header_error)) { + DBG(KERN_ERR "\tTX IP header csum error\n"); + x->tx_ip_header_error++; + } + + if (unlikely(p->des01.etx.payload_error)) { + DBG(KERN_ERR "\tAddr/Payload csum error\n"); + x->tx_payload_error++; + gmac_flush_tx_fifo(ioaddr); + } + + ret = -1; + } + + if (unlikely(p->des01.etx.deferred)) { + DBG(KERN_INFO "GMAC TX status: tx deferred\n"); + x->tx_deferred++; + } +#ifdef STMMAC_VLAN_TAG_USED + if (p->des01.etx.vlan_frame) { + DBG(KERN_INFO "GMAC TX status: VLAN frame\n"); + x->tx_vlan++; + } +#endif + + return ret; +} + +static int gmac_get_tx_len(struct dma_desc *p) +{ + return p->des01.etx.buffer1_size; +} + +static int gmac_coe_rdes0(int ipc_err, int type, int payload_err) +{ + int ret = good_frame; + u32 status = (type << 2 | ipc_err << 1 | payload_err) & 0x7; + + /* bits 5 7 0 | Frame status + * ---------------------------------------------------------- + * 0 0 0 | IEEE 802.3 Type frame (lenght < 1536 octects) + * 1 0 0 | IPv4/6 No CSUM errorS. + * 1 0 1 | IPv4/6 CSUM PAYLOAD error + * 1 1 0 | IPv4/6 CSUM IP HR error + * 1 1 1 | IPv4/6 IP PAYLOAD AND HEADER errorS + * 0 0 1 | IPv4/6 unsupported IP PAYLOAD + * 0 1 1 | COE bypassed.. no IPv4/6 frame + * 0 1 0 | Reserved. + */ + if (status == 0x0) { + DBG(KERN_INFO "RX Des0 status: IEEE 802.3 Type frame.\n"); + ret = good_frame; + } else if (status == 0x4) { + DBG(KERN_INFO "RX Des0 status: IPv4/6 No CSUM errorS.\n"); + ret = good_frame; + } else if (status == 0x5) { + DBG(KERN_ERR "RX Des0 status: IPv4/6 Payload Error.\n"); + ret = csum_none; + } else if (status == 0x6) { + DBG(KERN_ERR "RX Des0 status: IPv4/6 Header Error.\n"); + ret = csum_none; + } else if (status == 0x7) { + DBG(KERN_ERR + "RX Des0 status: IPv4/6 Header and Payload Error.\n"); + ret = csum_none; + } else if (status == 0x1) { + DBG(KERN_ERR + "RX Des0 status: IPv4/6 unsupported IP PAYLOAD.\n"); + ret = discard_frame; + } else if (status == 0x3) { + DBG(KERN_ERR "RX Des0 status: No IPv4, IPv6 frame.\n"); + ret = discard_frame; + } + return ret; +} + +static int gmac_get_rx_frame_status(void *data, struct stmmac_extra_stats *x, + struct dma_desc *p) +{ + int ret = good_frame; + struct net_device_stats *stats = (struct net_device_stats *)data; + + if (unlikely(p->des01.erx.error_summary)) { + DBG(KERN_ERR "GMAC RX Error Summary... 0x%08x\n", p->des01.erx); + if (unlikely(p->des01.erx.descriptor_error)) { + DBG(KERN_ERR "\tdescriptor error\n"); + x->rx_desc++; + stats->rx_length_errors++; + } + if (unlikely(p->des01.erx.overflow_error)) { + DBG(KERN_ERR "\toverflow error\n"); + x->rx_gmac_overflow++; + } + + if (unlikely(p->des01.erx.ipc_csum_error)) + DBG(KERN_ERR "\tIPC Csum Error/Giant frame\n"); + + if (unlikely(p->des01.erx.late_collision)) { + DBG(KERN_ERR "\tlate_collision error\n"); + stats->collisions++; + stats->collisions++; + } + if (unlikely(p->des01.erx.receive_watchdog)) { + DBG(KERN_ERR "\treceive_watchdog error\n"); + x->rx_watchdog++; + } + if (unlikely(p->des01.erx.error_gmii)) { + DBG(KERN_ERR "\tReceive Error\n"); + x->rx_mii++; + } + if (unlikely(p->des01.erx.crc_error)) { + DBG(KERN_ERR "\tCRC error\n"); + x->rx_crc++; + stats->rx_crc_errors++; + } + ret = discard_frame; + } + + /* After a payload csum error, the ES bit is set. + * It doesn't match with the information reported into the databook. + * At any rate, we need to understand if the CSUM hw computation is ok + * and report this info to the upper layers. */ + ret = gmac_coe_rdes0(p->des01.erx.ipc_csum_error, + p->des01.erx.frame_type, p->des01.erx.payload_csum_error); + + if (unlikely(p->des01.erx.dribbling)) { + DBG(KERN_ERR "GMAC RX: dribbling error\n"); + ret = discard_frame; + } + if (unlikely(p->des01.erx.sa_filter_fail)) { + DBG(KERN_ERR "GMAC RX : Source Address filter fail\n"); + x->sa_rx_filter_fail++; + ret = discard_frame; + } + if (unlikely(p->des01.erx.da_filter_fail)) { + DBG(KERN_ERR "GMAC RX : Destination Address filter fail\n"); + x->da_rx_filter_fail++; + ret = discard_frame; + } + if (unlikely(p->des01.erx.length_error)) { + DBG(KERN_ERR "GMAC RX: length_error error\n"); + x->rx_lenght++; + ret = discard_frame; + } +#ifdef STMMAC_VLAN_TAG_USED + if (p->des01.erx.vlan_tag) { + DBG(KERN_INFO "GMAC RX: VLAN frame tagged\n"); + x->rx_vlan++; + } +#endif + return ret; +} + +static void gmac_irq_status(unsigned long ioaddr) +{ + u32 intr_status = readl(ioaddr + GMAC_INT_STATUS); + + /* Not used events (e.g. MMC interrupts) are not handled. */ + if ((intr_status & mmc_tx_irq)) + DBG(KERN_DEBUG "GMAC: MMC tx interrupt: 0x%08x\n", + readl(ioaddr + GMAC_MMC_TX_INTR)); + if (unlikely(intr_status & mmc_rx_irq)) + DBG(KERN_DEBUG "GMAC: MMC rx interrupt: 0x%08x\n", + readl(ioaddr + GMAC_MMC_RX_INTR)); + if (unlikely(intr_status & mmc_rx_csum_offload_irq)) + DBG(KERN_DEBUG "GMAC: MMC rx csum offload: 0x%08x\n", + readl(ioaddr + GMAC_MMC_RX_CSUM_OFFLOAD)); + if (unlikely(intr_status & pmt_irq)) { + DBG(KERN_DEBUG "GMAC: received Magic frame\n"); + /* clear the PMT bits 5 and 6 by reading the PMT + * status register. */ + readl(ioaddr + GMAC_PMT); + } + + return; +} + +static void gmac_core_init(unsigned long ioaddr) +{ + u32 value = readl(ioaddr + GMAC_CONTROL); + value |= GMAC_CORE_INIT; + writel(value, ioaddr + GMAC_CONTROL); + + /* STBus Bridge Configuration */ + /*writel(0xc5608, ioaddr + 0x00007000);*/ + + /* Freeze MMC counters */ + writel(0x8, ioaddr + GMAC_MMC_CTRL); + /* Mask GMAC interrupts */ + writel(0x207, ioaddr + GMAC_INT_MASK); + +#ifdef STMMAC_VLAN_TAG_USED + /* Tag detection without filtering */ + writel(0x0, ioaddr + GMAC_VLAN_TAG); +#endif + return; +} + +static void gmac_set_umac_addr(unsigned long ioaddr, unsigned char *addr, + unsigned int reg_n) +{ + stmmac_set_mac_addr(ioaddr, addr, GMAC_ADDR_HIGH(reg_n), + GMAC_ADDR_LOW(reg_n)); +} + +static void gmac_get_umac_addr(unsigned long ioaddr, unsigned char *addr, + unsigned int reg_n) +{ + stmmac_get_mac_addr(ioaddr, addr, GMAC_ADDR_HIGH(reg_n), + GMAC_ADDR_LOW(reg_n)); +} + +static void gmac_set_filter(struct net_device *dev) +{ + unsigned long ioaddr = dev->base_addr; + unsigned int value = 0; + + DBG(KERN_INFO "%s: # mcasts %d, # unicast %d\n", + __func__, dev->mc_count, dev->uc_count); + + if (dev->flags & IFF_PROMISC) + value = GMAC_FRAME_FILTER_PR; + else if ((dev->mc_count > HASH_TABLE_SIZE) + || (dev->flags & IFF_ALLMULTI)) { + value = GMAC_FRAME_FILTER_PM; /* pass all multi */ + writel(0xffffffff, ioaddr + GMAC_HASH_HIGH); + writel(0xffffffff, ioaddr + GMAC_HASH_LOW); + } else if (dev->mc_count > 0) { + int i; + u32 mc_filter[2]; + struct dev_mc_list *mclist; + + /* Hash filter for multicast */ + value = GMAC_FRAME_FILTER_HMC; + + memset(mc_filter, 0, sizeof(mc_filter)); + for (i = 0, mclist = dev->mc_list; + mclist && i < dev->mc_count; i++, mclist = mclist->next) { + /* The upper 6 bits of the calculated CRC are used to + index the contens of the hash table */ + int bit_nr = + bitrev32(~crc32_le(~0, mclist->dmi_addr, 6)) >> 26; + /* The most significant bit determines the register to + * use (H/L) while the other 5 bits determine the bit + * within the register. */ + mc_filter[bit_nr >> 5] |= 1 << (bit_nr & 31); + } + writel(mc_filter[0], ioaddr + GMAC_HASH_LOW); + writel(mc_filter[1], ioaddr + GMAC_HASH_HIGH); + } + + /* Handle multiple unicast addresses (perfect filtering)*/ + if (dev->uc_count > GMAC_MAX_UNICAST_ADDRESSES) + /* Switch to promiscuous mode is more than 16 addrs + are required */ + value |= GMAC_FRAME_FILTER_PR; + else { + int i; + struct dev_addr_list *uc_ptr = dev->uc_list; + + for (i = 0; i < dev->uc_count; i++) { + gmac_set_umac_addr(ioaddr, uc_ptr->da_addr, + i + 1); + + DBG(KERN_INFO "\t%d " + "- Unicast addr %02x:%02x:%02x:%02x:%02x:" + "%02x\n", i + 1, + uc_ptr->da_addr[0], uc_ptr->da_addr[1], + uc_ptr->da_addr[2], uc_ptr->da_addr[3], + uc_ptr->da_addr[4], uc_ptr->da_addr[5]); + uc_ptr = uc_ptr->next; + } + } + +#ifdef FRAME_FILTER_DEBUG + /* Enable Receive all mode (to debug filtering_fail errors) */ + value |= GMAC_FRAME_FILTER_RA; +#endif + writel(value, ioaddr + GMAC_FRAME_FILTER); + + DBG(KERN_INFO "\tFrame Filter reg: 0x%08x\n\tHash regs: " + "HI 0x%08x, LO 0x%08x\n", readl(ioaddr + GMAC_FRAME_FILTER), + readl(ioaddr + GMAC_HASH_HIGH), readl(ioaddr + GMAC_HASH_LOW)); + + return; +} + +static void gmac_flow_ctrl(unsigned long ioaddr, unsigned int duplex, + unsigned int fc, unsigned int pause_time) +{ + unsigned int flow = 0; + + DBG(KERN_DEBUG "GMAC Flow-Control:\n"); + if (fc & FLOW_RX) { + DBG(KERN_DEBUG "\tReceive Flow-Control ON\n"); + flow |= GMAC_FLOW_CTRL_RFE; + } + if (fc & FLOW_TX) { + DBG(KERN_DEBUG "\tTransmit Flow-Control ON\n"); + flow |= GMAC_FLOW_CTRL_TFE; + } + + if (duplex) { + DBG(KERN_DEBUG "\tduplex mode: pause time: %d\n", pause_time); + flow |= (pause_time << GMAC_FLOW_CTRL_PT_SHIFT); + } + + writel(flow, ioaddr + GMAC_FLOW_CTRL); + return; +} + +static void gmac_pmt(unsigned long ioaddr, unsigned long mode) +{ + unsigned int pmt = 0; + + if (mode == WAKE_MAGIC) { + DBG(KERN_DEBUG "GMAC: WOL Magic frame\n"); + pmt |= power_down | magic_pkt_en; + } else if (mode == WAKE_UCAST) { + DBG(KERN_DEBUG "GMAC: WOL on global unicast\n"); + pmt |= global_unicast; + } + + writel(pmt, ioaddr + GMAC_PMT); + return; +} + +static void gmac_init_rx_desc(struct dma_desc *p, unsigned int ring_size, + int disable_rx_ic) +{ + int i; + for (i = 0; i < ring_size; i++) { + p->des01.erx.own = 1; + p->des01.erx.buffer1_size = BUF_SIZE_8KiB - 1; + /* To support jumbo frames */ + p->des01.erx.buffer2_size = BUF_SIZE_8KiB - 1; + if (i == ring_size - 1) + p->des01.erx.end_ring = 1; + if (disable_rx_ic) + p->des01.erx.disable_ic = 1; + p++; + } + return; +} + +static void gmac_init_tx_desc(struct dma_desc *p, unsigned int ring_size) +{ + int i; + + for (i = 0; i < ring_size; i++) { + p->des01.etx.own = 0; + if (i == ring_size - 1) + p->des01.etx.end_ring = 1; + p++; + } + + return; +} + +static int gmac_get_tx_owner(struct dma_desc *p) +{ + return p->des01.etx.own; +} + +static int gmac_get_rx_owner(struct dma_desc *p) +{ + return p->des01.erx.own; +} + +static void gmac_set_tx_owner(struct dma_desc *p) +{ + p->des01.etx.own = 1; +} + +static void gmac_set_rx_owner(struct dma_desc *p) +{ + p->des01.erx.own = 1; +} + +static int gmac_get_tx_ls(struct dma_desc *p) +{ + return p->des01.etx.last_segment; +} + +static void gmac_release_tx_desc(struct dma_desc *p) +{ + int ter = p->des01.etx.end_ring; + + memset(p, 0, sizeof(struct dma_desc)); + p->des01.etx.end_ring = ter; + + return; +} + +static void gmac_prepare_tx_desc(struct dma_desc *p, int is_fs, int len, + int csum_flag) +{ + p->des01.etx.first_segment = is_fs; + if (unlikely(len > BUF_SIZE_4KiB)) { + p->des01.etx.buffer1_size = BUF_SIZE_4KiB; + p->des01.etx.buffer2_size = len - BUF_SIZE_4KiB; + } else { + p->des01.etx.buffer1_size = len; + } + if (likely(csum_flag)) + p->des01.etx.checksum_insertion = cic_full; +} + +static void gmac_clear_tx_ic(struct dma_desc *p) +{ + p->des01.etx.interrupt = 0; +} + +static void gmac_close_tx_desc(struct dma_desc *p) +{ + p->des01.etx.last_segment = 1; + p->des01.etx.interrupt = 1; +} + +static int gmac_get_rx_frame_len(struct dma_desc *p) +{ + return p->des01.erx.frame_length; +} + +struct stmmac_ops gmac_driver = { + .core_init = gmac_core_init, + .dump_mac_regs = gmac_dump_regs, + .dma_init = gmac_dma_init, + .dump_dma_regs = gmac_dump_dma_regs, + .dma_mode = gmac_dma_operation_mode, + .dma_diagnostic_fr = gmac_dma_diagnostic_fr, + .tx_status = gmac_get_tx_frame_status, + .rx_status = gmac_get_rx_frame_status, + .get_tx_len = gmac_get_tx_len, + .set_filter = gmac_set_filter, + .flow_ctrl = gmac_flow_ctrl, + .pmt = gmac_pmt, + .init_rx_desc = gmac_init_rx_desc, + .init_tx_desc = gmac_init_tx_desc, + .get_tx_owner = gmac_get_tx_owner, + .get_rx_owner = gmac_get_rx_owner, + .release_tx_desc = gmac_release_tx_desc, + .prepare_tx_desc = gmac_prepare_tx_desc, + .clear_tx_ic = gmac_clear_tx_ic, + .close_tx_desc = gmac_close_tx_desc, + .get_tx_ls = gmac_get_tx_ls, + .set_tx_owner = gmac_set_tx_owner, + .set_rx_owner = gmac_set_rx_owner, + .get_rx_frame_len = gmac_get_rx_frame_len, + .host_irq_status = gmac_irq_status, + .set_umac_addr = gmac_set_umac_addr, + .get_umac_addr = gmac_get_umac_addr, +}; + +struct mac_device_info *gmac_setup(unsigned long ioaddr) +{ + struct mac_device_info *mac; + u32 uid = readl(ioaddr + GMAC_VERSION); + + pr_info("\tGMAC - user ID: 0x%x, Synopsys ID: 0x%x\n", + ((uid & 0x0000ff00) >> 8), (uid & 0x000000ff)); + + mac = kzalloc(sizeof(const struct mac_device_info), GFP_KERNEL); + + mac->ops = &gmac_driver; + mac->hw.pmt = PMT_SUPPORTED; + mac->hw.link.port = GMAC_CONTROL_PS; + mac->hw.link.duplex = GMAC_CONTROL_DM; + mac->hw.link.speed = GMAC_CONTROL_FES; + mac->hw.mii.addr = GMAC_MII_ADDR; + mac->hw.mii.data = GMAC_MII_DATA; + + return mac; +} diff --git a/drivers/net/stmmac/gmac.h b/drivers/net/stmmac/gmac.h new file mode 100644 index 000000000000..684a363120a9 --- /dev/null +++ b/drivers/net/stmmac/gmac.h @@ -0,0 +1,204 @@ +/******************************************************************************* + Copyright (C) 2007-2009 STMicroelectronics Ltd + + This program is free software; you can redistribute it and/or modify it + under the terms and conditions of the GNU General Public License, + version 2, as published by the Free Software Foundation. + + This program is distributed in the hope it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. + + You should have received a copy of the GNU General Public License along with + this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + + The full GNU General Public License is included in this distribution in + the file called "COPYING". + + Author: Giuseppe Cavallaro <peppe.cavallaro@st.com> +*******************************************************************************/ + +#define GMAC_CONTROL 0x00000000 /* Configuration */ +#define GMAC_FRAME_FILTER 0x00000004 /* Frame Filter */ +#define GMAC_HASH_HIGH 0x00000008 /* Multicast Hash Table High */ +#define GMAC_HASH_LOW 0x0000000c /* Multicast Hash Table Low */ +#define GMAC_MII_ADDR 0x00000010 /* MII Address */ +#define GMAC_MII_DATA 0x00000014 /* MII Data */ +#define GMAC_FLOW_CTRL 0x00000018 /* Flow Control */ +#define GMAC_VLAN_TAG 0x0000001c /* VLAN Tag */ +#define GMAC_VERSION 0x00000020 /* GMAC CORE Version */ +#define GMAC_WAKEUP_FILTER 0x00000028 /* Wake-up Frame Filter */ + +#define GMAC_INT_STATUS 0x00000038 /* interrupt status register */ +enum gmac_irq_status { + time_stamp_irq = 0x0200, + mmc_rx_csum_offload_irq = 0x0080, + mmc_tx_irq = 0x0040, + mmc_rx_irq = 0x0020, + mmc_irq = 0x0010, + pmt_irq = 0x0008, + pcs_ane_irq = 0x0004, + pcs_link_irq = 0x0002, + rgmii_irq = 0x0001, +}; +#define GMAC_INT_MASK 0x0000003c /* interrupt mask register */ + +/* PMT Control and Status */ +#define GMAC_PMT 0x0000002c +enum power_event { + pointer_reset = 0x80000000, + global_unicast = 0x00000200, + wake_up_rx_frame = 0x00000040, + magic_frame = 0x00000020, + wake_up_frame_en = 0x00000004, + magic_pkt_en = 0x00000002, + power_down = 0x00000001, +}; + +/* GMAC HW ADDR regs */ +#define GMAC_ADDR_HIGH(reg) (0x00000040+(reg * 8)) +#define GMAC_ADDR_LOW(reg) (0x00000044+(reg * 8)) +#define GMAC_MAX_UNICAST_ADDRESSES 16 + +#define GMAC_AN_CTRL 0x000000c0 /* AN control */ +#define GMAC_AN_STATUS 0x000000c4 /* AN status */ +#define GMAC_ANE_ADV 0x000000c8 /* Auto-Neg. Advertisement */ +#define GMAC_ANE_LINK 0x000000cc /* Auto-Neg. link partener ability */ +#define GMAC_ANE_EXP 0x000000d0 /* ANE expansion */ +#define GMAC_TBI 0x000000d4 /* TBI extend status */ +#define GMAC_GMII_STATUS 0x000000d8 /* S/R-GMII status */ + +/* GMAC Configuration defines */ +#define GMAC_CONTROL_TC 0x01000000 /* Transmit Conf. in RGMII/SGMII */ +#define GMAC_CONTROL_WD 0x00800000 /* Disable Watchdog on receive */ +#define GMAC_CONTROL_JD 0x00400000 /* Jabber disable */ +#define GMAC_CONTROL_BE 0x00200000 /* Frame Burst Enable */ +#define GMAC_CONTROL_JE 0x00100000 /* Jumbo frame */ +enum inter_frame_gap { + GMAC_CONTROL_IFG_88 = 0x00040000, + GMAC_CONTROL_IFG_80 = 0x00020000, + GMAC_CONTROL_IFG_40 = 0x000e0000, +}; +#define GMAC_CONTROL_DCRS 0x00010000 /* Disable carrier sense during tx */ +#define GMAC_CONTROL_PS 0x00008000 /* Port Select 0:GMI 1:MII */ +#define GMAC_CONTROL_FES 0x00004000 /* Speed 0:10 1:100 */ +#define GMAC_CONTROL_DO 0x00002000 /* Disable Rx Own */ +#define GMAC_CONTROL_LM 0x00001000 /* Loop-back mode */ +#define GMAC_CONTROL_DM 0x00000800 /* Duplex Mode */ +#define GMAC_CONTROL_IPC 0x00000400 /* Checksum Offload */ +#define GMAC_CONTROL_DR 0x00000200 /* Disable Retry */ +#define GMAC_CONTROL_LUD 0x00000100 /* Link up/down */ +#define GMAC_CONTROL_ACS 0x00000080 /* Automatic Pad Stripping */ +#define GMAC_CONTROL_DC 0x00000010 /* Deferral Check */ +#define GMAC_CONTROL_TE 0x00000008 /* Transmitter Enable */ +#define GMAC_CONTROL_RE 0x00000004 /* Receiver Enable */ + +#define GMAC_CORE_INIT (GMAC_CONTROL_JD | GMAC_CONTROL_PS | GMAC_CONTROL_ACS | \ + GMAC_CONTROL_IPC | GMAC_CONTROL_JE | GMAC_CONTROL_BE) + +/* GMAC Frame Filter defines */ +#define GMAC_FRAME_FILTER_PR 0x00000001 /* Promiscuous Mode */ +#define GMAC_FRAME_FILTER_HUC 0x00000002 /* Hash Unicast */ +#define GMAC_FRAME_FILTER_HMC 0x00000004 /* Hash Multicast */ +#define GMAC_FRAME_FILTER_DAIF 0x00000008 /* DA Inverse Filtering */ +#define GMAC_FRAME_FILTER_PM 0x00000010 /* Pass all multicast */ +#define GMAC_FRAME_FILTER_DBF 0x00000020 /* Disable Broadcast frames */ +#define GMAC_FRAME_FILTER_SAIF 0x00000100 /* Inverse Filtering */ +#define GMAC_FRAME_FILTER_SAF 0x00000200 /* Source Address Filter */ +#define GMAC_FRAME_FILTER_HPF 0x00000400 /* Hash or perfect Filter */ +#define GMAC_FRAME_FILTER_RA 0x80000000 /* Receive all mode */ +/* GMII ADDR defines */ +#define GMAC_MII_ADDR_WRITE 0x00000002 /* MII Write */ +#define GMAC_MII_ADDR_BUSY 0x00000001 /* MII Busy */ +/* GMAC FLOW CTRL defines */ +#define GMAC_FLOW_CTRL_PT_MASK 0xffff0000 /* Pause Time Mask */ +#define GMAC_FLOW_CTRL_PT_SHIFT 16 +#define GMAC_FLOW_CTRL_RFE 0x00000004 /* Rx Flow Control Enable */ +#define GMAC_FLOW_CTRL_TFE 0x00000002 /* Tx Flow Control Enable */ +#define GMAC_FLOW_CTRL_FCB_BPA 0x00000001 /* Flow Control Busy ... */ + +/*--- DMA BLOCK defines ---*/ +/* DMA Bus Mode register defines */ +#define DMA_BUS_MODE_SFT_RESET 0x00000001 /* Software Reset */ +#define DMA_BUS_MODE_DA 0x00000002 /* Arbitration scheme */ +#define DMA_BUS_MODE_DSL_MASK 0x0000007c /* Descriptor Skip Length */ +#define DMA_BUS_MODE_DSL_SHIFT 2 /* (in DWORDS) */ +/* Programmable burst length (passed thorugh platform)*/ +#define DMA_BUS_MODE_PBL_MASK 0x00003f00 /* Programmable Burst Len */ +#define DMA_BUS_MODE_PBL_SHIFT 8 + +enum rx_tx_priority_ratio { + double_ratio = 0x00004000, /*2:1 */ + triple_ratio = 0x00008000, /*3:1 */ + quadruple_ratio = 0x0000c000, /*4:1 */ +}; + +#define DMA_BUS_MODE_FB 0x00010000 /* Fixed burst */ +#define DMA_BUS_MODE_RPBL_MASK 0x003e0000 /* Rx-Programmable Burst Len */ +#define DMA_BUS_MODE_RPBL_SHIFT 17 +#define DMA_BUS_MODE_USP 0x00800000 +#define DMA_BUS_MODE_4PBL 0x01000000 +#define DMA_BUS_MODE_AAL 0x02000000 + +/* DMA CRS Control and Status Register Mapping */ +#define DMA_HOST_TX_DESC 0x00001048 /* Current Host Tx descriptor */ +#define DMA_HOST_RX_DESC 0x0000104c /* Current Host Rx descriptor */ +/* DMA Bus Mode register defines */ +#define DMA_BUS_PR_RATIO_MASK 0x0000c000 /* Rx/Tx priority ratio */ +#define DMA_BUS_PR_RATIO_SHIFT 14 +#define DMA_BUS_FB 0x00010000 /* Fixed Burst */ + +/* DMA operation mode defines (start/stop tx/rx are placed in common header)*/ +#define DMA_CONTROL_DT 0x04000000 /* Disable Drop TCP/IP csum error */ +#define DMA_CONTROL_RSF 0x02000000 /* Receive Store and Forward */ +#define DMA_CONTROL_DFF 0x01000000 /* Disaable flushing */ +/* Theshold for Activating the FC */ +enum rfa { + act_full_minus_1 = 0x00800000, + act_full_minus_2 = 0x00800200, + act_full_minus_3 = 0x00800400, + act_full_minus_4 = 0x00800600, +}; +/* Theshold for Deactivating the FC */ +enum rfd { + deac_full_minus_1 = 0x00400000, + deac_full_minus_2 = 0x00400800, + deac_full_minus_3 = 0x00401000, + deac_full_minus_4 = 0x00401800, +}; +#define DMA_CONTROL_TSF 0x00200000 /* Transmit Store and Forward */ +#define DMA_CONTROL_FTF 0x00100000 /* Flush transmit FIFO */ + +enum ttc_control { + DMA_CONTROL_TTC_64 = 0x00000000, + DMA_CONTROL_TTC_128 = 0x00004000, + DMA_CONTROL_TTC_192 = 0x00008000, + DMA_CONTROL_TTC_256 = 0x0000c000, + DMA_CONTROL_TTC_40 = 0x00010000, + DMA_CONTROL_TTC_32 = 0x00014000, + DMA_CONTROL_TTC_24 = 0x00018000, + DMA_CONTROL_TTC_16 = 0x0001c000, +}; +#define DMA_CONTROL_TC_TX_MASK 0xfffe3fff + +#define DMA_CONTROL_EFC 0x00000100 +#define DMA_CONTROL_FEF 0x00000080 +#define DMA_CONTROL_FUF 0x00000040 + +enum rtc_control { + DMA_CONTROL_RTC_64 = 0x00000000, + DMA_CONTROL_RTC_32 = 0x00000008, + DMA_CONTROL_RTC_96 = 0x00000010, + DMA_CONTROL_RTC_128 = 0x00000018, +}; +#define DMA_CONTROL_TC_RX_MASK 0xffffffe7 + +#define DMA_CONTROL_OSF 0x00000004 /* Operate on second frame */ + +/* MMC registers offset */ +#define GMAC_MMC_CTRL 0x100 +#define GMAC_MMC_RX_INTR 0x104 +#define GMAC_MMC_TX_INTR 0x108 +#define GMAC_MMC_RX_CSUM_OFFLOAD 0x208 diff --git a/drivers/net/stmmac/mac100.c b/drivers/net/stmmac/mac100.c new file mode 100644 index 000000000000..625171b6062b --- /dev/null +++ b/drivers/net/stmmac/mac100.c @@ -0,0 +1,517 @@ +/******************************************************************************* + This is the driver for the MAC 10/100 on-chip Ethernet controller + currently tested on all the ST boards based on STb7109 and stx7200 SoCs. + + DWC Ether MAC 10/100 Universal version 4.0 has been used for developing + this code. + + Copyright (C) 2007-2009 STMicroelectronics Ltd + + This program is free software; you can redistribute it and/or modify it + under the terms and conditions of the GNU General Public License, + version 2, as published by the Free Software Foundation. + + This program is distributed in the hope it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. + + You should have received a copy of the GNU General Public License along with + this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + + The full GNU General Public License is included in this distribution in + the file called "COPYING". + + Author: Giuseppe Cavallaro <peppe.cavallaro@st.com> +*******************************************************************************/ + +#include <linux/netdevice.h> +#include <linux/crc32.h> +#include <linux/mii.h> +#include <linux/phy.h> + +#include "common.h" +#include "mac100.h" + +#undef MAC100_DEBUG +/*#define MAC100_DEBUG*/ +#ifdef MAC100_DEBUG +#define DBG(fmt, args...) printk(fmt, ## args) +#else +#define DBG(fmt, args...) do { } while (0) +#endif + +static void mac100_core_init(unsigned long ioaddr) +{ + u32 value = readl(ioaddr + MAC_CONTROL); + + writel((value | MAC_CORE_INIT), ioaddr + MAC_CONTROL); + +#ifdef STMMAC_VLAN_TAG_USED + writel(ETH_P_8021Q, ioaddr + MAC_VLAN1); +#endif + return; +} + +static void mac100_dump_mac_regs(unsigned long ioaddr) +{ + pr_info("\t----------------------------------------------\n" + "\t MAC100 CSR (base addr = 0x%8x)\n" + "\t----------------------------------------------\n", + (unsigned int)ioaddr); + pr_info("\tcontrol reg (offset 0x%x): 0x%08x\n", MAC_CONTROL, + readl(ioaddr + MAC_CONTROL)); + pr_info("\taddr HI (offset 0x%x): 0x%08x\n ", MAC_ADDR_HIGH, + readl(ioaddr + MAC_ADDR_HIGH)); + pr_info("\taddr LO (offset 0x%x): 0x%08x\n", MAC_ADDR_LOW, + readl(ioaddr + MAC_ADDR_LOW)); + pr_info("\tmulticast hash HI (offset 0x%x): 0x%08x\n", + MAC_HASH_HIGH, readl(ioaddr + MAC_HASH_HIGH)); + pr_info("\tmulticast hash LO (offset 0x%x): 0x%08x\n", + MAC_HASH_LOW, readl(ioaddr + MAC_HASH_LOW)); + pr_info("\tflow control (offset 0x%x): 0x%08x\n", + MAC_FLOW_CTRL, readl(ioaddr + MAC_FLOW_CTRL)); + pr_info("\tVLAN1 tag (offset 0x%x): 0x%08x\n", MAC_VLAN1, + readl(ioaddr + MAC_VLAN1)); + pr_info("\tVLAN2 tag (offset 0x%x): 0x%08x\n", MAC_VLAN2, + readl(ioaddr + MAC_VLAN2)); + pr_info("\n\tMAC management counter registers\n"); + pr_info("\t MMC crtl (offset 0x%x): 0x%08x\n", + MMC_CONTROL, readl(ioaddr + MMC_CONTROL)); + pr_info("\t MMC High Interrupt (offset 0x%x): 0x%08x\n", + MMC_HIGH_INTR, readl(ioaddr + MMC_HIGH_INTR)); + pr_info("\t MMC Low Interrupt (offset 0x%x): 0x%08x\n", + MMC_LOW_INTR, readl(ioaddr + MMC_LOW_INTR)); + pr_info("\t MMC High Interrupt Mask (offset 0x%x): 0x%08x\n", + MMC_HIGH_INTR_MASK, readl(ioaddr + MMC_HIGH_INTR_MASK)); + pr_info("\t MMC Low Interrupt Mask (offset 0x%x): 0x%08x\n", + MMC_LOW_INTR_MASK, readl(ioaddr + MMC_LOW_INTR_MASK)); + return; +} + +static int mac100_dma_init(unsigned long ioaddr, int pbl, u32 dma_tx, + u32 dma_rx) +{ + u32 value = readl(ioaddr + DMA_BUS_MODE); + /* DMA SW reset */ + value |= DMA_BUS_MODE_SFT_RESET; + writel(value, ioaddr + DMA_BUS_MODE); + do {} while ((readl(ioaddr + DMA_BUS_MODE) & DMA_BUS_MODE_SFT_RESET)); + + /* Enable Application Access by writing to DMA CSR0 */ + writel(DMA_BUS_MODE_DEFAULT | (pbl << DMA_BUS_MODE_PBL_SHIFT), + ioaddr + DMA_BUS_MODE); + + /* Mask interrupts by writing to CSR7 */ + writel(DMA_INTR_DEFAULT_MASK, ioaddr + DMA_INTR_ENA); + + /* The base address of the RX/TX descriptor lists must be written into + * DMA CSR3 and CSR4, respectively. */ + writel(dma_tx, ioaddr + DMA_TX_BASE_ADDR); + writel(dma_rx, ioaddr + DMA_RCV_BASE_ADDR); + + return 0; +} + +/* Store and Forward capability is not used at all.. + * The transmit threshold can be programmed by + * setting the TTC bits in the DMA control register.*/ +static void mac100_dma_operation_mode(unsigned long ioaddr, int txmode, + int rxmode) +{ + u32 csr6 = readl(ioaddr + DMA_CONTROL); + + if (txmode <= 32) + csr6 |= DMA_CONTROL_TTC_32; + else if (txmode <= 64) + csr6 |= DMA_CONTROL_TTC_64; + else + csr6 |= DMA_CONTROL_TTC_128; + + writel(csr6, ioaddr + DMA_CONTROL); + + return; +} + +static void mac100_dump_dma_regs(unsigned long ioaddr) +{ + int i; + + DBG(KERN_DEBUG "MAC100 DMA CSR \n"); + for (i = 0; i < 9; i++) + pr_debug("\t CSR%d (offset 0x%x): 0x%08x\n", i, + (DMA_BUS_MODE + i * 4), + readl(ioaddr + DMA_BUS_MODE + i * 4)); + DBG(KERN_DEBUG "\t CSR20 (offset 0x%x): 0x%08x\n", + DMA_CUR_TX_BUF_ADDR, readl(ioaddr + DMA_CUR_TX_BUF_ADDR)); + DBG(KERN_DEBUG "\t CSR21 (offset 0x%x): 0x%08x\n", + DMA_CUR_RX_BUF_ADDR, readl(ioaddr + DMA_CUR_RX_BUF_ADDR)); + return; +} + +/* DMA controller has two counters to track the number of + the receive missed frames. */ +static void mac100_dma_diagnostic_fr(void *data, struct stmmac_extra_stats *x, + unsigned long ioaddr) +{ + struct net_device_stats *stats = (struct net_device_stats *)data; + u32 csr8 = readl(ioaddr + DMA_MISSED_FRAME_CTR); + + if (unlikely(csr8)) { + if (csr8 & DMA_MISSED_FRAME_OVE) { + stats->rx_over_errors += 0x800; + x->rx_overflow_cntr += 0x800; + } else { + unsigned int ove_cntr; + ove_cntr = ((csr8 & DMA_MISSED_FRAME_OVE_CNTR) >> 17); + stats->rx_over_errors += ove_cntr; + x->rx_overflow_cntr += ove_cntr; + } + + if (csr8 & DMA_MISSED_FRAME_OVE_M) { + stats->rx_missed_errors += 0xffff; + x->rx_missed_cntr += 0xffff; + } else { + unsigned int miss_f = (csr8 & DMA_MISSED_FRAME_M_CNTR); + stats->rx_missed_errors += miss_f; + x->rx_missed_cntr += miss_f; + } + } + return; +} + +static int mac100_get_tx_frame_status(void *data, struct stmmac_extra_stats *x, + struct dma_desc *p, unsigned long ioaddr) +{ + int ret = 0; + struct net_device_stats *stats = (struct net_device_stats *)data; + + if (unlikely(p->des01.tx.error_summary)) { + if (unlikely(p->des01.tx.underflow_error)) { + x->tx_underflow++; + stats->tx_fifo_errors++; + } + if (unlikely(p->des01.tx.no_carrier)) { + x->tx_carrier++; + stats->tx_carrier_errors++; + } + if (unlikely(p->des01.tx.loss_carrier)) { + x->tx_losscarrier++; + stats->tx_carrier_errors++; + } + if (unlikely((p->des01.tx.excessive_deferral) || + (p->des01.tx.excessive_collisions) || + (p->des01.tx.late_collision))) + stats->collisions += p->des01.tx.collision_count; + ret = -1; + } + if (unlikely(p->des01.tx.heartbeat_fail)) { + x->tx_heartbeat++; + stats->tx_heartbeat_errors++; + ret = -1; + } + if (unlikely(p->des01.tx.deferred)) + x->tx_deferred++; + + return ret; +} + +static int mac100_get_tx_len(struct dma_desc *p) +{ + return p->des01.tx.buffer1_size; +} + +/* This function verifies if each incoming frame has some errors + * and, if required, updates the multicast statistics. + * In case of success, it returns csum_none becasue the device + * is not able to compute the csum in HW. */ +static int mac100_get_rx_frame_status(void *data, struct stmmac_extra_stats *x, + struct dma_desc *p) +{ + int ret = csum_none; + struct net_device_stats *stats = (struct net_device_stats *)data; + + if (unlikely(p->des01.rx.last_descriptor == 0)) { + pr_warning("mac100 Error: Oversized Ethernet " + "frame spanned multiple buffers\n"); + stats->rx_length_errors++; + return discard_frame; + } + + if (unlikely(p->des01.rx.error_summary)) { + if (unlikely(p->des01.rx.descriptor_error)) + x->rx_desc++; + if (unlikely(p->des01.rx.partial_frame_error)) + x->rx_partial++; + if (unlikely(p->des01.rx.run_frame)) + x->rx_runt++; + if (unlikely(p->des01.rx.frame_too_long)) + x->rx_toolong++; + if (unlikely(p->des01.rx.collision)) { + x->rx_collision++; + stats->collisions++; + } + if (unlikely(p->des01.rx.crc_error)) { + x->rx_crc++; + stats->rx_crc_errors++; + } + ret = discard_frame; + } + if (unlikely(p->des01.rx.dribbling)) + ret = discard_frame; + + if (unlikely(p->des01.rx.length_error)) { + x->rx_lenght++; + ret = discard_frame; + } + if (unlikely(p->des01.rx.mii_error)) { + x->rx_mii++; + ret = discard_frame; + } + if (p->des01.rx.multicast_frame) { + x->rx_multicast++; + stats->multicast++; + } + return ret; +} + +static void mac100_irq_status(unsigned long ioaddr) +{ + return; +} + +static void mac100_set_umac_addr(unsigned long ioaddr, unsigned char *addr, + unsigned int reg_n) +{ + stmmac_set_mac_addr(ioaddr, addr, MAC_ADDR_HIGH, MAC_ADDR_LOW); +} + +static void mac100_get_umac_addr(unsigned long ioaddr, unsigned char *addr, + unsigned int reg_n) +{ + stmmac_get_mac_addr(ioaddr, addr, MAC_ADDR_HIGH, MAC_ADDR_LOW); +} + +static void mac100_set_filter(struct net_device *dev) +{ + unsigned long ioaddr = dev->base_addr; + u32 value = readl(ioaddr + MAC_CONTROL); + + if (dev->flags & IFF_PROMISC) { + value |= MAC_CONTROL_PR; + value &= ~(MAC_CONTROL_PM | MAC_CONTROL_IF | MAC_CONTROL_HO | + MAC_CONTROL_HP); + } else if ((dev->mc_count > HASH_TABLE_SIZE) + || (dev->flags & IFF_ALLMULTI)) { + value |= MAC_CONTROL_PM; + value &= ~(MAC_CONTROL_PR | MAC_CONTROL_IF | MAC_CONTROL_HO); + writel(0xffffffff, ioaddr + MAC_HASH_HIGH); + writel(0xffffffff, ioaddr + MAC_HASH_LOW); + } else if (dev->mc_count == 0) { /* no multicast */ + value &= ~(MAC_CONTROL_PM | MAC_CONTROL_PR | MAC_CONTROL_IF | + MAC_CONTROL_HO | MAC_CONTROL_HP); + } else { + int i; + u32 mc_filter[2]; + struct dev_mc_list *mclist; + + /* Perfect filter mode for physical address and Hash + filter for multicast */ + value |= MAC_CONTROL_HP; + value &= ~(MAC_CONTROL_PM | MAC_CONTROL_PR | MAC_CONTROL_IF + | MAC_CONTROL_HO); + + memset(mc_filter, 0, sizeof(mc_filter)); + for (i = 0, mclist = dev->mc_list; + mclist && i < dev->mc_count; i++, mclist = mclist->next) { + /* The upper 6 bits of the calculated CRC are used to + * index the contens of the hash table */ + int bit_nr = + ether_crc(ETH_ALEN, mclist->dmi_addr) >> 26; + /* The most significant bit determines the register to + * use (H/L) while the other 5 bits determine the bit + * within the register. */ + mc_filter[bit_nr >> 5] |= 1 << (bit_nr & 31); + } + writel(mc_filter[0], ioaddr + MAC_HASH_LOW); + writel(mc_filter[1], ioaddr + MAC_HASH_HIGH); + } + + writel(value, ioaddr + MAC_CONTROL); + + DBG(KERN_INFO "%s: CTRL reg: 0x%08x Hash regs: " + "HI 0x%08x, LO 0x%08x\n", + __func__, readl(ioaddr + MAC_CONTROL), + readl(ioaddr + MAC_HASH_HIGH), readl(ioaddr + MAC_HASH_LOW)); + return; +} + +static void mac100_flow_ctrl(unsigned long ioaddr, unsigned int duplex, + unsigned int fc, unsigned int pause_time) +{ + unsigned int flow = MAC_FLOW_CTRL_ENABLE; + + if (duplex) + flow |= (pause_time << MAC_FLOW_CTRL_PT_SHIFT); + writel(flow, ioaddr + MAC_FLOW_CTRL); + + return; +} + +/* No PMT module supported in our SoC for the Ethernet Controller. */ +static void mac100_pmt(unsigned long ioaddr, unsigned long mode) +{ + return; +} + +static void mac100_init_rx_desc(struct dma_desc *p, unsigned int ring_size, + int disable_rx_ic) +{ + int i; + for (i = 0; i < ring_size; i++) { + p->des01.rx.own = 1; + p->des01.rx.buffer1_size = BUF_SIZE_2KiB - 1; + if (i == ring_size - 1) + p->des01.rx.end_ring = 1; + if (disable_rx_ic) + p->des01.rx.disable_ic = 1; + p++; + } + return; +} + +static void mac100_init_tx_desc(struct dma_desc *p, unsigned int ring_size) +{ + int i; + for (i = 0; i < ring_size; i++) { + p->des01.tx.own = 0; + if (i == ring_size - 1) + p->des01.tx.end_ring = 1; + p++; + } + return; +} + +static int mac100_get_tx_owner(struct dma_desc *p) +{ + return p->des01.tx.own; +} + +static int mac100_get_rx_owner(struct dma_desc *p) +{ + return p->des01.rx.own; +} + +static void mac100_set_tx_owner(struct dma_desc *p) +{ + p->des01.tx.own = 1; +} + +static void mac100_set_rx_owner(struct dma_desc *p) +{ + p->des01.rx.own = 1; +} + +static int mac100_get_tx_ls(struct dma_desc *p) +{ + return p->des01.tx.last_segment; +} + +static void mac100_release_tx_desc(struct dma_desc *p) +{ + int ter = p->des01.tx.end_ring; + + /* clean field used within the xmit */ + p->des01.tx.first_segment = 0; + p->des01.tx.last_segment = 0; + p->des01.tx.buffer1_size = 0; + + /* clean status reported */ + p->des01.tx.error_summary = 0; + p->des01.tx.underflow_error = 0; + p->des01.tx.no_carrier = 0; + p->des01.tx.loss_carrier = 0; + p->des01.tx.excessive_deferral = 0; + p->des01.tx.excessive_collisions = 0; + p->des01.tx.late_collision = 0; + p->des01.tx.heartbeat_fail = 0; + p->des01.tx.deferred = 0; + + /* set termination field */ + p->des01.tx.end_ring = ter; + + return; +} + +static void mac100_prepare_tx_desc(struct dma_desc *p, int is_fs, int len, + int csum_flag) +{ + p->des01.tx.first_segment = is_fs; + p->des01.tx.buffer1_size = len; +} + +static void mac100_clear_tx_ic(struct dma_desc *p) +{ + p->des01.tx.interrupt = 0; +} + +static void mac100_close_tx_desc(struct dma_desc *p) +{ + p->des01.tx.last_segment = 1; + p->des01.tx.interrupt = 1; +} + +static int mac100_get_rx_frame_len(struct dma_desc *p) +{ + return p->des01.rx.frame_length; +} + +struct stmmac_ops mac100_driver = { + .core_init = mac100_core_init, + .dump_mac_regs = mac100_dump_mac_regs, + .dma_init = mac100_dma_init, + .dump_dma_regs = mac100_dump_dma_regs, + .dma_mode = mac100_dma_operation_mode, + .dma_diagnostic_fr = mac100_dma_diagnostic_fr, + .tx_status = mac100_get_tx_frame_status, + .rx_status = mac100_get_rx_frame_status, + .get_tx_len = mac100_get_tx_len, + .set_filter = mac100_set_filter, + .flow_ctrl = mac100_flow_ctrl, + .pmt = mac100_pmt, + .init_rx_desc = mac100_init_rx_desc, + .init_tx_desc = mac100_init_tx_desc, + .get_tx_owner = mac100_get_tx_owner, + .get_rx_owner = mac100_get_rx_owner, + .release_tx_desc = mac100_release_tx_desc, + .prepare_tx_desc = mac100_prepare_tx_desc, + .clear_tx_ic = mac100_clear_tx_ic, + .close_tx_desc = mac100_close_tx_desc, + .get_tx_ls = mac100_get_tx_ls, + .set_tx_owner = mac100_set_tx_owner, + .set_rx_owner = mac100_set_rx_owner, + .get_rx_frame_len = mac100_get_rx_frame_len, + .host_irq_status = mac100_irq_status, + .set_umac_addr = mac100_set_umac_addr, + .get_umac_addr = mac100_get_umac_addr, +}; + +struct mac_device_info *mac100_setup(unsigned long ioaddr) +{ + struct mac_device_info *mac; + + mac = kzalloc(sizeof(const struct mac_device_info), GFP_KERNEL); + + pr_info("\tMAC 10/100\n"); + + mac->ops = &mac100_driver; + mac->hw.pmt = PMT_NOT_SUPPORTED; + mac->hw.link.port = MAC_CONTROL_PS; + mac->hw.link.duplex = MAC_CONTROL_F; + mac->hw.link.speed = 0; + mac->hw.mii.addr = MAC_MII_ADDR; + mac->hw.mii.data = MAC_MII_DATA; + + return mac; +} diff --git a/drivers/net/stmmac/mac100.h b/drivers/net/stmmac/mac100.h new file mode 100644 index 000000000000..0f8f110d004a --- /dev/null +++ b/drivers/net/stmmac/mac100.h @@ -0,0 +1,116 @@ +/******************************************************************************* + MAC 10/100 Header File + + Copyright (C) 2007-2009 STMicroelectronics Ltd + + This program is free software; you can redistribute it and/or modify it + under the terms and conditions of the GNU General Public License, + version 2, as published by the Free Software Foundation. + + This program is distributed in the hope it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. + + You should have received a copy of the GNU General Public License along with + this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + + The full GNU General Public License is included in this distribution in + the file called "COPYING". + + Author: Giuseppe Cavallaro <peppe.cavallaro@st.com> +*******************************************************************************/ + +/*---------------------------------------------------------------------------- + * MAC BLOCK defines + *---------------------------------------------------------------------------*/ +/* MAC CSR offset */ +#define MAC_CONTROL 0x00000000 /* MAC Control */ +#define MAC_ADDR_HIGH 0x00000004 /* MAC Address High */ +#define MAC_ADDR_LOW 0x00000008 /* MAC Address Low */ +#define MAC_HASH_HIGH 0x0000000c /* Multicast Hash Table High */ +#define MAC_HASH_LOW 0x00000010 /* Multicast Hash Table Low */ +#define MAC_MII_ADDR 0x00000014 /* MII Address */ +#define MAC_MII_DATA 0x00000018 /* MII Data */ +#define MAC_FLOW_CTRL 0x0000001c /* Flow Control */ +#define MAC_VLAN1 0x00000020 /* VLAN1 Tag */ +#define MAC_VLAN2 0x00000024 /* VLAN2 Tag */ + +/* MAC CTRL defines */ +#define MAC_CONTROL_RA 0x80000000 /* Receive All Mode */ +#define MAC_CONTROL_BLE 0x40000000 /* Endian Mode */ +#define MAC_CONTROL_HBD 0x10000000 /* Heartbeat Disable */ +#define MAC_CONTROL_PS 0x08000000 /* Port Select */ +#define MAC_CONTROL_DRO 0x00800000 /* Disable Receive Own */ +#define MAC_CONTROL_EXT_LOOPBACK 0x00400000 /* Reserved (ext loopback?) */ +#define MAC_CONTROL_OM 0x00200000 /* Loopback Operating Mode */ +#define MAC_CONTROL_F 0x00100000 /* Full Duplex Mode */ +#define MAC_CONTROL_PM 0x00080000 /* Pass All Multicast */ +#define MAC_CONTROL_PR 0x00040000 /* Promiscuous Mode */ +#define MAC_CONTROL_IF 0x00020000 /* Inverse Filtering */ +#define MAC_CONTROL_PB 0x00010000 /* Pass Bad Frames */ +#define MAC_CONTROL_HO 0x00008000 /* Hash Only Filtering Mode */ +#define MAC_CONTROL_HP 0x00002000 /* Hash/Perfect Filtering Mode */ +#define MAC_CONTROL_LCC 0x00001000 /* Late Collision Control */ +#define MAC_CONTROL_DBF 0x00000800 /* Disable Broadcast Frames */ +#define MAC_CONTROL_DRTY 0x00000400 /* Disable Retry */ +#define MAC_CONTROL_ASTP 0x00000100 /* Automatic Pad Stripping */ +#define MAC_CONTROL_BOLMT_10 0x00000000 /* Back Off Limit 10 */ +#define MAC_CONTROL_BOLMT_8 0x00000040 /* Back Off Limit 8 */ +#define MAC_CONTROL_BOLMT_4 0x00000080 /* Back Off Limit 4 */ +#define MAC_CONTROL_BOLMT_1 0x000000c0 /* Back Off Limit 1 */ +#define MAC_CONTROL_DC 0x00000020 /* Deferral Check */ +#define MAC_CONTROL_TE 0x00000008 /* Transmitter Enable */ +#define MAC_CONTROL_RE 0x00000004 /* Receiver Enable */ + +#define MAC_CORE_INIT (MAC_CONTROL_HBD | MAC_CONTROL_ASTP) + +/* MAC FLOW CTRL defines */ +#define MAC_FLOW_CTRL_PT_MASK 0xffff0000 /* Pause Time Mask */ +#define MAC_FLOW_CTRL_PT_SHIFT 16 +#define MAC_FLOW_CTRL_PASS 0x00000004 /* Pass Control Frames */ +#define MAC_FLOW_CTRL_ENABLE 0x00000002 /* Flow Control Enable */ +#define MAC_FLOW_CTRL_PAUSE 0x00000001 /* Flow Control Busy ... */ + +/* MII ADDR defines */ +#define MAC_MII_ADDR_WRITE 0x00000002 /* MII Write */ +#define MAC_MII_ADDR_BUSY 0x00000001 /* MII Busy */ + +/*---------------------------------------------------------------------------- + * DMA BLOCK defines + *---------------------------------------------------------------------------*/ + +/* DMA Bus Mode register defines */ +#define DMA_BUS_MODE_DBO 0x00100000 /* Descriptor Byte Ordering */ +#define DMA_BUS_MODE_BLE 0x00000080 /* Big Endian/Little Endian */ +#define DMA_BUS_MODE_PBL_MASK 0x00003f00 /* Programmable Burst Len */ +#define DMA_BUS_MODE_PBL_SHIFT 8 +#define DMA_BUS_MODE_DSL_MASK 0x0000007c /* Descriptor Skip Length */ +#define DMA_BUS_MODE_DSL_SHIFT 2 /* (in DWORDS) */ +#define DMA_BUS_MODE_BAR_BUS 0x00000002 /* Bar-Bus Arbitration */ +#define DMA_BUS_MODE_SFT_RESET 0x00000001 /* Software Reset */ +#define DMA_BUS_MODE_DEFAULT 0x00000000 + +/* DMA Control register defines */ +#define DMA_CONTROL_SF 0x00200000 /* Store And Forward */ + +/* Transmit Threshold Control */ +enum ttc_control { + DMA_CONTROL_TTC_DEFAULT = 0x00000000, /* Threshold is 32 DWORDS */ + DMA_CONTROL_TTC_64 = 0x00004000, /* Threshold is 64 DWORDS */ + DMA_CONTROL_TTC_128 = 0x00008000, /* Threshold is 128 DWORDS */ + DMA_CONTROL_TTC_256 = 0x0000c000, /* Threshold is 256 DWORDS */ + DMA_CONTROL_TTC_18 = 0x00400000, /* Threshold is 18 DWORDS */ + DMA_CONTROL_TTC_24 = 0x00404000, /* Threshold is 24 DWORDS */ + DMA_CONTROL_TTC_32 = 0x00408000, /* Threshold is 32 DWORDS */ + DMA_CONTROL_TTC_40 = 0x0040c000, /* Threshold is 40 DWORDS */ + DMA_CONTROL_SE = 0x00000008, /* Stop On Empty */ + DMA_CONTROL_OSF = 0x00000004, /* Operate On 2nd Frame */ +}; + +/* STMAC110 DMA Missed Frame Counter register defines */ +#define DMA_MISSED_FRAME_OVE 0x10000000 /* FIFO Overflow Overflow */ +#define DMA_MISSED_FRAME_OVE_CNTR 0x0ffe0000 /* Overflow Frame Counter */ +#define DMA_MISSED_FRAME_OVE_M 0x00010000 /* Missed Frame Overflow */ +#define DMA_MISSED_FRAME_M_CNTR 0x0000ffff /* Missed Frame Couinter */ diff --git a/drivers/net/stmmac/stmmac.h b/drivers/net/stmmac/stmmac.h new file mode 100644 index 000000000000..6d2eae3040e5 --- /dev/null +++ b/drivers/net/stmmac/stmmac.h @@ -0,0 +1,98 @@ +/******************************************************************************* + Copyright (C) 2007-2009 STMicroelectronics Ltd + + This program is free software; you can redistribute it and/or modify it + under the terms and conditions of the GNU General Public License, + version 2, as published by the Free Software Foundation. + + This program is distributed in the hope it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. + + You should have received a copy of the GNU General Public License along with + this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + + The full GNU General Public License is included in this distribution in + the file called "COPYING". + + Author: Giuseppe Cavallaro <peppe.cavallaro@st.com> +*******************************************************************************/ + +#define DRV_MODULE_VERSION "Oct_09" + +#if defined(CONFIG_VLAN_8021Q) || defined(CONFIG_VLAN_8021Q_MODULE) +#define STMMAC_VLAN_TAG_USED +#include <linux/if_vlan.h> +#endif + +#include "common.h" +#ifdef CONFIG_STMMAC_TIMER +#include "stmmac_timer.h" +#endif + +struct stmmac_priv { + /* Frequently used values are kept adjacent for cache effect */ + struct dma_desc *dma_tx ____cacheline_aligned; + dma_addr_t dma_tx_phy; + struct sk_buff **tx_skbuff; + unsigned int cur_tx; + unsigned int dirty_tx; + unsigned int dma_tx_size; + int tx_coe; + int tx_coalesce; + + struct dma_desc *dma_rx ; + unsigned int cur_rx; + unsigned int dirty_rx; + struct sk_buff **rx_skbuff; + dma_addr_t *rx_skbuff_dma; + struct sk_buff_head rx_recycle; + + struct net_device *dev; + int is_gmac; + dma_addr_t dma_rx_phy; + unsigned int dma_rx_size; + int rx_csum; + unsigned int dma_buf_sz; + struct device *device; + struct mac_device_info *mac_type; + + struct stmmac_extra_stats xstats; + struct napi_struct napi; + + phy_interface_t phy_interface; + int pbl; + int bus_id; + int phy_addr; + int phy_mask; + int (*phy_reset) (void *priv); + void (*fix_mac_speed) (void *priv, unsigned int speed); + void *bsp_priv; + + int phy_irq; + struct phy_device *phydev; + int oldlink; + int speed; + int oldduplex; + unsigned int flow_ctrl; + unsigned int pause; + struct mii_bus *mii; + + u32 msg_enable; + spinlock_t lock; + int wolopts; + int wolenabled; + int shutdown; +#ifdef CONFIG_STMMAC_TIMER + struct stmmac_timer *tm; +#endif +#ifdef STMMAC_VLAN_TAG_USED + struct vlan_group *vlgrp; +#endif +}; + +extern int stmmac_mdio_unregister(struct net_device *ndev); +extern int stmmac_mdio_register(struct net_device *ndev); +extern void stmmac_set_ethtool_ops(struct net_device *netdev); diff --git a/drivers/net/stmmac/stmmac_ethtool.c b/drivers/net/stmmac/stmmac_ethtool.c new file mode 100644 index 000000000000..694ebe6a0758 --- /dev/null +++ b/drivers/net/stmmac/stmmac_ethtool.c @@ -0,0 +1,395 @@ +/******************************************************************************* + STMMAC Ethtool support + + Copyright (C) 2007-2009 STMicroelectronics Ltd + + This program is free software; you can redistribute it and/or modify it + under the terms and conditions of the GNU General Public License, + version 2, as published by the Free Software Foundation. + + This program is distributed in the hope it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. + + You should have received a copy of the GNU General Public License along with + this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + + The full GNU General Public License is included in this distribution in + the file called "COPYING". + + Author: Giuseppe Cavallaro <peppe.cavallaro@st.com> +*******************************************************************************/ + +#include <linux/etherdevice.h> +#include <linux/ethtool.h> +#include <linux/mii.h> +#include <linux/phy.h> + +#include "stmmac.h" + +#define REG_SPACE_SIZE 0x1054 +#define MAC100_ETHTOOL_NAME "st_mac100" +#define GMAC_ETHTOOL_NAME "st_gmac" + +struct stmmac_stats { + char stat_string[ETH_GSTRING_LEN]; + int sizeof_stat; + int stat_offset; +}; + +#define STMMAC_STAT(m) \ + { #m, FIELD_SIZEOF(struct stmmac_extra_stats, m), \ + offsetof(struct stmmac_priv, xstats.m)} + +static const struct stmmac_stats stmmac_gstrings_stats[] = { + STMMAC_STAT(tx_underflow), + STMMAC_STAT(tx_carrier), + STMMAC_STAT(tx_losscarrier), + STMMAC_STAT(tx_heartbeat), + STMMAC_STAT(tx_deferred), + STMMAC_STAT(tx_vlan), + STMMAC_STAT(rx_vlan), + STMMAC_STAT(tx_jabber), + STMMAC_STAT(tx_frame_flushed), + STMMAC_STAT(tx_payload_error), + STMMAC_STAT(tx_ip_header_error), + STMMAC_STAT(rx_desc), + STMMAC_STAT(rx_partial), + STMMAC_STAT(rx_runt), + STMMAC_STAT(rx_toolong), + STMMAC_STAT(rx_collision), + STMMAC_STAT(rx_crc), + STMMAC_STAT(rx_lenght), + STMMAC_STAT(rx_mii), + STMMAC_STAT(rx_multicast), + STMMAC_STAT(rx_gmac_overflow), + STMMAC_STAT(rx_watchdog), + STMMAC_STAT(da_rx_filter_fail), + STMMAC_STAT(sa_rx_filter_fail), + STMMAC_STAT(rx_missed_cntr), + STMMAC_STAT(rx_overflow_cntr), + STMMAC_STAT(tx_undeflow_irq), + STMMAC_STAT(tx_process_stopped_irq), + STMMAC_STAT(tx_jabber_irq), + STMMAC_STAT(rx_overflow_irq), + STMMAC_STAT(rx_buf_unav_irq), + STMMAC_STAT(rx_process_stopped_irq), + STMMAC_STAT(rx_watchdog_irq), + STMMAC_STAT(tx_early_irq), + STMMAC_STAT(fatal_bus_error_irq), + STMMAC_STAT(threshold), + STMMAC_STAT(tx_pkt_n), + STMMAC_STAT(rx_pkt_n), + STMMAC_STAT(poll_n), + STMMAC_STAT(sched_timer_n), + STMMAC_STAT(normal_irq_n), +}; +#define STMMAC_STATS_LEN ARRAY_SIZE(stmmac_gstrings_stats) + +void stmmac_ethtool_getdrvinfo(struct net_device *dev, + struct ethtool_drvinfo *info) +{ + struct stmmac_priv *priv = netdev_priv(dev); + + if (!priv->is_gmac) + strcpy(info->driver, MAC100_ETHTOOL_NAME); + else + strcpy(info->driver, GMAC_ETHTOOL_NAME); + + strcpy(info->version, DRV_MODULE_VERSION); + info->fw_version[0] = '\0'; + info->n_stats = STMMAC_STATS_LEN; + return; +} + +int stmmac_ethtool_getsettings(struct net_device *dev, struct ethtool_cmd *cmd) +{ + struct stmmac_priv *priv = netdev_priv(dev); + struct phy_device *phy = priv->phydev; + int rc; + if (phy == NULL) { + pr_err("%s: %s: PHY is not registered\n", + __func__, dev->name); + return -ENODEV; + } + if (!netif_running(dev)) { + pr_err("%s: interface is disabled: we cannot track " + "link speed / duplex setting\n", dev->name); + return -EBUSY; + } + cmd->transceiver = XCVR_INTERNAL; + spin_lock_irq(&priv->lock); + rc = phy_ethtool_gset(phy, cmd); + spin_unlock_irq(&priv->lock); + return rc; +} + +int stmmac_ethtool_setsettings(struct net_device *dev, struct ethtool_cmd *cmd) +{ + struct stmmac_priv *priv = netdev_priv(dev); + struct phy_device *phy = priv->phydev; + int rc; + + spin_lock(&priv->lock); + rc = phy_ethtool_sset(phy, cmd); + spin_unlock(&priv->lock); + + return rc; +} + +u32 stmmac_ethtool_getmsglevel(struct net_device *dev) +{ + struct stmmac_priv *priv = netdev_priv(dev); + return priv->msg_enable; +} + +void stmmac_ethtool_setmsglevel(struct net_device *dev, u32 level) +{ + struct stmmac_priv *priv = netdev_priv(dev); + priv->msg_enable = level; + +} + +int stmmac_check_if_running(struct net_device *dev) +{ + if (!netif_running(dev)) + return -EBUSY; + return 0; +} + +int stmmac_ethtool_get_regs_len(struct net_device *dev) +{ + return REG_SPACE_SIZE; +} + +void stmmac_ethtool_gregs(struct net_device *dev, + struct ethtool_regs *regs, void *space) +{ + int i; + u32 *reg_space = (u32 *) space; + + struct stmmac_priv *priv = netdev_priv(dev); + + memset(reg_space, 0x0, REG_SPACE_SIZE); + + if (!priv->is_gmac) { + /* MAC registers */ + for (i = 0; i < 12; i++) + reg_space[i] = readl(dev->base_addr + (i * 4)); + /* DMA registers */ + for (i = 0; i < 9; i++) + reg_space[i + 12] = + readl(dev->base_addr + (DMA_BUS_MODE + (i * 4))); + reg_space[22] = readl(dev->base_addr + DMA_CUR_TX_BUF_ADDR); + reg_space[23] = readl(dev->base_addr + DMA_CUR_RX_BUF_ADDR); + } else { + /* MAC registers */ + for (i = 0; i < 55; i++) + reg_space[i] = readl(dev->base_addr + (i * 4)); + /* DMA registers */ + for (i = 0; i < 22; i++) + reg_space[i + 55] = + readl(dev->base_addr + (DMA_BUS_MODE + (i * 4))); + } + + return; +} + +int stmmac_ethtool_set_tx_csum(struct net_device *netdev, u32 data) +{ + if (data) + netdev->features |= NETIF_F_HW_CSUM; + else + netdev->features &= ~NETIF_F_HW_CSUM; + + return 0; +} + +u32 stmmac_ethtool_get_rx_csum(struct net_device *dev) +{ + struct stmmac_priv *priv = netdev_priv(dev); + + return priv->rx_csum; +} + +static void +stmmac_get_pauseparam(struct net_device *netdev, + struct ethtool_pauseparam *pause) +{ + struct stmmac_priv *priv = netdev_priv(netdev); + + spin_lock(&priv->lock); + + pause->rx_pause = 0; + pause->tx_pause = 0; + pause->autoneg = priv->phydev->autoneg; + + if (priv->flow_ctrl & FLOW_RX) + pause->rx_pause = 1; + if (priv->flow_ctrl & FLOW_TX) + pause->tx_pause = 1; + + spin_unlock(&priv->lock); + return; +} + +static int +stmmac_set_pauseparam(struct net_device *netdev, + struct ethtool_pauseparam *pause) +{ + struct stmmac_priv *priv = netdev_priv(netdev); + struct phy_device *phy = priv->phydev; + int new_pause = FLOW_OFF; + int ret = 0; + + spin_lock(&priv->lock); + + if (pause->rx_pause) + new_pause |= FLOW_RX; + if (pause->tx_pause) + new_pause |= FLOW_TX; + + priv->flow_ctrl = new_pause; + + if (phy->autoneg) { + if (netif_running(netdev)) { + struct ethtool_cmd cmd; + /* auto-negotiation automatically restarted */ + cmd.cmd = ETHTOOL_NWAY_RST; + cmd.supported = phy->supported; + cmd.advertising = phy->advertising; + cmd.autoneg = phy->autoneg; + cmd.speed = phy->speed; + cmd.duplex = phy->duplex; + cmd.phy_address = phy->addr; + ret = phy_ethtool_sset(phy, &cmd); + } + } else { + unsigned long ioaddr = netdev->base_addr; + priv->mac_type->ops->flow_ctrl(ioaddr, phy->duplex, + priv->flow_ctrl, priv->pause); + } + spin_unlock(&priv->lock); + return ret; +} + +static void stmmac_get_ethtool_stats(struct net_device *dev, + struct ethtool_stats *dummy, u64 *data) +{ + struct stmmac_priv *priv = netdev_priv(dev); + unsigned long ioaddr = dev->base_addr; + int i; + + /* Update HW stats if supported */ + priv->mac_type->ops->dma_diagnostic_fr(&dev->stats, &priv->xstats, + ioaddr); + + for (i = 0; i < STMMAC_STATS_LEN; i++) { + char *p = (char *)priv + stmmac_gstrings_stats[i].stat_offset; + data[i] = (stmmac_gstrings_stats[i].sizeof_stat == + sizeof(u64)) ? (*(u64 *)p) : (*(u32 *)p); + } + + return; +} + +static int stmmac_get_sset_count(struct net_device *netdev, int sset) +{ + switch (sset) { + case ETH_SS_STATS: + return STMMAC_STATS_LEN; + default: + return -EOPNOTSUPP; + } +} + +static void stmmac_get_strings(struct net_device *dev, u32 stringset, u8 *data) +{ + int i; + u8 *p = data; + + switch (stringset) { + case ETH_SS_STATS: + for (i = 0; i < STMMAC_STATS_LEN; i++) { + memcpy(p, stmmac_gstrings_stats[i].stat_string, + ETH_GSTRING_LEN); + p += ETH_GSTRING_LEN; + } + break; + default: + WARN_ON(1); + break; + } + return; +} + +/* Currently only support WOL through Magic packet. */ +static void stmmac_get_wol(struct net_device *dev, struct ethtool_wolinfo *wol) +{ + struct stmmac_priv *priv = netdev_priv(dev); + + spin_lock_irq(&priv->lock); + if (priv->wolenabled == PMT_SUPPORTED) { + wol->supported = WAKE_MAGIC; + wol->wolopts = priv->wolopts; + } + spin_unlock_irq(&priv->lock); +} + +static int stmmac_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol) +{ + struct stmmac_priv *priv = netdev_priv(dev); + u32 support = WAKE_MAGIC; + + if (priv->wolenabled == PMT_NOT_SUPPORTED) + return -EINVAL; + + if (wol->wolopts & ~support) + return -EINVAL; + + if (wol->wolopts == 0) + device_set_wakeup_enable(priv->device, 0); + else + device_set_wakeup_enable(priv->device, 1); + + spin_lock_irq(&priv->lock); + priv->wolopts = wol->wolopts; + spin_unlock_irq(&priv->lock); + + return 0; +} + +static struct ethtool_ops stmmac_ethtool_ops = { + .begin = stmmac_check_if_running, + .get_drvinfo = stmmac_ethtool_getdrvinfo, + .get_settings = stmmac_ethtool_getsettings, + .set_settings = stmmac_ethtool_setsettings, + .get_msglevel = stmmac_ethtool_getmsglevel, + .set_msglevel = stmmac_ethtool_setmsglevel, + .get_regs = stmmac_ethtool_gregs, + .get_regs_len = stmmac_ethtool_get_regs_len, + .get_link = ethtool_op_get_link, + .get_rx_csum = stmmac_ethtool_get_rx_csum, + .get_tx_csum = ethtool_op_get_tx_csum, + .set_tx_csum = stmmac_ethtool_set_tx_csum, + .get_sg = ethtool_op_get_sg, + .set_sg = ethtool_op_set_sg, + .get_pauseparam = stmmac_get_pauseparam, + .set_pauseparam = stmmac_set_pauseparam, + .get_ethtool_stats = stmmac_get_ethtool_stats, + .get_strings = stmmac_get_strings, + .get_wol = stmmac_get_wol, + .set_wol = stmmac_set_wol, + .get_sset_count = stmmac_get_sset_count, +#ifdef NETIF_F_TSO + .get_tso = ethtool_op_get_tso, + .set_tso = ethtool_op_set_tso, +#endif +}; + +void stmmac_set_ethtool_ops(struct net_device *netdev) +{ + SET_ETHTOOL_OPS(netdev, &stmmac_ethtool_ops); +} diff --git a/drivers/net/stmmac/stmmac_main.c b/drivers/net/stmmac/stmmac_main.c new file mode 100644 index 000000000000..9542995ba667 --- /dev/null +++ b/drivers/net/stmmac/stmmac_main.c @@ -0,0 +1,2210 @@ +/******************************************************************************* + This is the driver for the ST MAC 10/100/1000 on-chip Ethernet controllers. + ST Ethernet IPs are built around a Synopsys IP Core. + + Copyright (C) 2007-2009 STMicroelectronics Ltd + + This program is free software; you can redistribute it and/or modify it + under the terms and conditions of the GNU General Public License, + version 2, as published by the Free Software Foundation. + + This program is distributed in the hope it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. + + You should have received a copy of the GNU General Public License along with + this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + + The full GNU General Public License is included in this distribution in + the file called "COPYING". + + Author: Giuseppe Cavallaro <peppe.cavallaro@st.com> + + Documentation available at: + http://www.stlinux.com + Support available at: + https://bugzilla.stlinux.com/ +*******************************************************************************/ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/interrupt.h> +#include <linux/netdevice.h> +#include <linux/etherdevice.h> +#include <linux/platform_device.h> +#include <linux/ip.h> +#include <linux/tcp.h> +#include <linux/skbuff.h> +#include <linux/ethtool.h> +#include <linux/if_ether.h> +#include <linux/crc32.h> +#include <linux/mii.h> +#include <linux/phy.h> +#include <linux/if_vlan.h> +#include <linux/dma-mapping.h> +#include <linux/stm/soc.h> +#include "stmmac.h" + +#define STMMAC_RESOURCE_NAME "stmmaceth" +#define PHY_RESOURCE_NAME "stmmacphy" + +#undef STMMAC_DEBUG +/*#define STMMAC_DEBUG*/ +#ifdef STMMAC_DEBUG +#define DBG(nlevel, klevel, fmt, args...) \ + ((void)(netif_msg_##nlevel(priv) && \ + printk(KERN_##klevel fmt, ## args))) +#else +#define DBG(nlevel, klevel, fmt, args...) do { } while (0) +#endif + +#undef STMMAC_RX_DEBUG +/*#define STMMAC_RX_DEBUG*/ +#ifdef STMMAC_RX_DEBUG +#define RX_DBG(fmt, args...) printk(fmt, ## args) +#else +#define RX_DBG(fmt, args...) do { } while (0) +#endif + +#undef STMMAC_XMIT_DEBUG +/*#define STMMAC_XMIT_DEBUG*/ +#ifdef STMMAC_TX_DEBUG +#define TX_DBG(fmt, args...) printk(fmt, ## args) +#else +#define TX_DBG(fmt, args...) do { } while (0) +#endif + +#define STMMAC_ALIGN(x) L1_CACHE_ALIGN(x) +#define JUMBO_LEN 9000 + +/* Module parameters */ +#define TX_TIMEO 5000 /* default 5 seconds */ +static int watchdog = TX_TIMEO; +module_param(watchdog, int, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(watchdog, "Transmit timeout in milliseconds"); + +static int debug = -1; /* -1: default, 0: no output, 16: all */ +module_param(debug, int, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(debug, "Message Level (0: no output, 16: all)"); + +static int phyaddr = -1; +module_param(phyaddr, int, S_IRUGO); +MODULE_PARM_DESC(phyaddr, "Physical device address"); + +#define DMA_TX_SIZE 256 +static int dma_txsize = DMA_TX_SIZE; +module_param(dma_txsize, int, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(dma_txsize, "Number of descriptors in the TX list"); + +#define DMA_RX_SIZE 256 +static int dma_rxsize = DMA_RX_SIZE; +module_param(dma_rxsize, int, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(dma_rxsize, "Number of descriptors in the RX list"); + +static int flow_ctrl = FLOW_OFF; +module_param(flow_ctrl, int, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(flow_ctrl, "Flow control ability [on/off]"); + +static int pause = PAUSE_TIME; +module_param(pause, int, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(pause, "Flow Control Pause Time"); + +#define TC_DEFAULT 64 +static int tc = TC_DEFAULT; +module_param(tc, int, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(tc, "DMA threshold control value"); + +#define RX_NO_COALESCE 1 /* Always interrupt on completion */ +#define TX_NO_COALESCE -1 /* No moderation by default */ + +/* Pay attention to tune this parameter; take care of both + * hardware capability and network stabitily/performance impact. + * Many tests showed that ~4ms latency seems to be good enough. */ +#ifdef CONFIG_STMMAC_TIMER +#define DEFAULT_PERIODIC_RATE 256 +static int tmrate = DEFAULT_PERIODIC_RATE; +module_param(tmrate, int, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(tmrate, "External timer freq. (default: 256Hz)"); +#endif + +#define DMA_BUFFER_SIZE BUF_SIZE_2KiB +static int buf_sz = DMA_BUFFER_SIZE; +module_param(buf_sz, int, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(buf_sz, "DMA buffer size"); + +/* In case of Giga ETH, we can enable/disable the COE for the + * transmit HW checksum computation. + * Note that, if tx csum is off in HW, SG will be still supported. */ +static int tx_coe = HW_CSUM; +module_param(tx_coe, int, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(tx_coe, "GMAC COE type 2 [on/off]"); + +static const u32 default_msg_level = (NETIF_MSG_DRV | NETIF_MSG_PROBE | + NETIF_MSG_LINK | NETIF_MSG_IFUP | + NETIF_MSG_IFDOWN | NETIF_MSG_TIMER); + +static irqreturn_t stmmac_interrupt(int irq, void *dev_id); +static netdev_tx_t stmmac_xmit(struct sk_buff *skb, struct net_device *dev); + +/** + * stmmac_verify_args - verify the driver parameters. + * Description: it verifies if some wrong parameter is passed to the driver. + * Note that wrong parameters are replaced with the default values. + */ +static void stmmac_verify_args(void) +{ + if (unlikely(watchdog < 0)) + watchdog = TX_TIMEO; + if (unlikely(dma_rxsize < 0)) + dma_rxsize = DMA_RX_SIZE; + if (unlikely(dma_txsize < 0)) + dma_txsize = DMA_TX_SIZE; + if (unlikely((buf_sz < DMA_BUFFER_SIZE) || (buf_sz > BUF_SIZE_16KiB))) + buf_sz = DMA_BUFFER_SIZE; + if (unlikely(flow_ctrl > 1)) + flow_ctrl = FLOW_AUTO; + else if (likely(flow_ctrl < 0)) + flow_ctrl = FLOW_OFF; + if (unlikely((pause < 0) || (pause > 0xffff))) + pause = PAUSE_TIME; + + return; +} + +#if defined(STMMAC_XMIT_DEBUG) || defined(STMMAC_RX_DEBUG) +static void print_pkt(unsigned char *buf, int len) +{ + int j; + pr_info("len = %d byte, buf addr: 0x%p", len, buf); + for (j = 0; j < len; j++) { + if ((j % 16) == 0) + pr_info("\n %03x:", j); + pr_info(" %02x", buf[j]); + } + pr_info("\n"); + return; +} +#endif + +/* minimum number of free TX descriptors required to wake up TX process */ +#define STMMAC_TX_THRESH(x) (x->dma_tx_size/4) + +static inline u32 stmmac_tx_avail(struct stmmac_priv *priv) +{ + return priv->dirty_tx + priv->dma_tx_size - priv->cur_tx - 1; +} + +/** + * stmmac_adjust_link + * @dev: net device structure + * Description: it adjusts the link parameters. + */ +static void stmmac_adjust_link(struct net_device *dev) +{ + struct stmmac_priv *priv = netdev_priv(dev); + struct phy_device *phydev = priv->phydev; + unsigned long ioaddr = dev->base_addr; + unsigned long flags; + int new_state = 0; + unsigned int fc = priv->flow_ctrl, pause_time = priv->pause; + + if (phydev == NULL) + return; + + DBG(probe, DEBUG, "stmmac_adjust_link: called. address %d link %d\n", + phydev->addr, phydev->link); + + spin_lock_irqsave(&priv->lock, flags); + if (phydev->link) { + u32 ctrl = readl(ioaddr + MAC_CTRL_REG); + + /* Now we make sure that we can be in full duplex mode. + * If not, we operate in half-duplex mode. */ + if (phydev->duplex != priv->oldduplex) { + new_state = 1; + if (!(phydev->duplex)) + ctrl &= ~priv->mac_type->hw.link.duplex; + else + ctrl |= priv->mac_type->hw.link.duplex; + priv->oldduplex = phydev->duplex; + } + /* Flow Control operation */ + if (phydev->pause) + priv->mac_type->ops->flow_ctrl(ioaddr, phydev->duplex, + fc, pause_time); + + if (phydev->speed != priv->speed) { + new_state = 1; + switch (phydev->speed) { + case 1000: + if (likely(priv->is_gmac)) + ctrl &= ~priv->mac_type->hw.link.port; + break; + case 100: + case 10: + if (priv->is_gmac) { + ctrl |= priv->mac_type->hw.link.port; + if (phydev->speed == SPEED_100) { + ctrl |= + priv->mac_type->hw.link. + speed; + } else { + ctrl &= + ~(priv->mac_type->hw. + link.speed); + } + } else { + ctrl &= ~priv->mac_type->hw.link.port; + } + priv->fix_mac_speed(priv->bsp_priv, + phydev->speed); + break; + default: + if (netif_msg_link(priv)) + pr_warning("%s: Speed (%d) is not 10" + " or 100!\n", dev->name, phydev->speed); + break; + } + + priv->speed = phydev->speed; + } + + writel(ctrl, ioaddr + MAC_CTRL_REG); + + if (!priv->oldlink) { + new_state = 1; + priv->oldlink = 1; + } + } else if (priv->oldlink) { + new_state = 1; + priv->oldlink = 0; + priv->speed = 0; + priv->oldduplex = -1; + } + + if (new_state && netif_msg_link(priv)) + phy_print_status(phydev); + + spin_unlock_irqrestore(&priv->lock, flags); + + DBG(probe, DEBUG, "stmmac_adjust_link: exiting\n"); +} + +/** + * stmmac_init_phy - PHY initialization + * @dev: net device structure + * Description: it initializes the driver's PHY state, and attaches the PHY + * to the mac driver. + * Return value: + * 0 on success + */ +static int stmmac_init_phy(struct net_device *dev) +{ + struct stmmac_priv *priv = netdev_priv(dev); + struct phy_device *phydev; + char phy_id[BUS_ID_SIZE]; /* PHY to connect */ + char bus_id[BUS_ID_SIZE]; + + priv->oldlink = 0; + priv->speed = 0; + priv->oldduplex = -1; + + if (priv->phy_addr == -1) { + /* We don't have a PHY, so do nothing */ + return 0; + } + + snprintf(bus_id, MII_BUS_ID_SIZE, "%x", priv->bus_id); + snprintf(phy_id, BUS_ID_SIZE, PHY_ID_FMT, bus_id, priv->phy_addr); + pr_debug("stmmac_init_phy: trying to attach to %s\n", phy_id); + + phydev = phy_connect(dev, phy_id, &stmmac_adjust_link, 0, + priv->phy_interface); + + if (IS_ERR(phydev)) { + pr_err("%s: Could not attach to PHY\n", dev->name); + return PTR_ERR(phydev); + } + + /* + * Broken HW is sometimes missing the pull-up resistor on the + * MDIO line, which results in reads to non-existent devices returning + * 0 rather than 0xffff. Catch this here and treat 0 as a non-existent + * device as well. + * Note: phydev->phy_id is the result of reading the UID PHY registers. + */ + if (phydev->phy_id == 0) { + phy_disconnect(phydev); + return -ENODEV; + } + pr_debug("stmmac_init_phy: %s: attached to PHY (UID 0x%x)" + " Link = %d\n", dev->name, phydev->phy_id, phydev->link); + + priv->phydev = phydev; + + return 0; +} + +static inline void stmmac_mac_enable_rx(unsigned long ioaddr) +{ + u32 value = readl(ioaddr + MAC_CTRL_REG); + value |= MAC_RNABLE_RX; + /* Set the RE (receive enable bit into the MAC CTRL register). */ + writel(value, ioaddr + MAC_CTRL_REG); +} + +static inline void stmmac_mac_enable_tx(unsigned long ioaddr) +{ + u32 value = readl(ioaddr + MAC_CTRL_REG); + value |= MAC_ENABLE_TX; + /* Set the TE (transmit enable bit into the MAC CTRL register). */ + writel(value, ioaddr + MAC_CTRL_REG); +} + +static inline void stmmac_mac_disable_rx(unsigned long ioaddr) +{ + u32 value = readl(ioaddr + MAC_CTRL_REG); + value &= ~MAC_RNABLE_RX; + writel(value, ioaddr + MAC_CTRL_REG); +} + +static inline void stmmac_mac_disable_tx(unsigned long ioaddr) +{ + u32 value = readl(ioaddr + MAC_CTRL_REG); + value &= ~MAC_ENABLE_TX; + writel(value, ioaddr + MAC_CTRL_REG); +} + +/** + * display_ring + * @p: pointer to the ring. + * @size: size of the ring. + * Description: display all the descriptors within the ring. + */ +static void display_ring(struct dma_desc *p, int size) +{ + struct tmp_s { + u64 a; + unsigned int b; + unsigned int c; + }; + int i; + for (i = 0; i < size; i++) { + struct tmp_s *x = (struct tmp_s *)(p + i); + pr_info("\t%d [0x%x]: DES0=0x%x DES1=0x%x BUF1=0x%x BUF2=0x%x", + i, (unsigned int)virt_to_phys(&p[i]), + (unsigned int)(x->a), (unsigned int)((x->a) >> 32), + x->b, x->c); + pr_info("\n"); + } +} + +/** + * init_dma_desc_rings - init the RX/TX descriptor rings + * @dev: net device structure + * Description: this function initializes the DMA RX/TX descriptors + * and allocates the socket buffers. + */ +static void init_dma_desc_rings(struct net_device *dev) +{ + int i; + struct stmmac_priv *priv = netdev_priv(dev); + struct sk_buff *skb; + unsigned int txsize = priv->dma_tx_size; + unsigned int rxsize = priv->dma_rx_size; + unsigned int bfsize = priv->dma_buf_sz; + int buff2_needed = 0, dis_ic = 0; + + /* Set the Buffer size according to the MTU; + * indeed, in case of jumbo we need to bump-up the buffer sizes. + */ + if (unlikely(dev->mtu >= BUF_SIZE_8KiB)) + bfsize = BUF_SIZE_16KiB; + else if (unlikely(dev->mtu >= BUF_SIZE_4KiB)) + bfsize = BUF_SIZE_8KiB; + else if (unlikely(dev->mtu >= BUF_SIZE_2KiB)) + bfsize = BUF_SIZE_4KiB; + else if (unlikely(dev->mtu >= DMA_BUFFER_SIZE)) + bfsize = BUF_SIZE_2KiB; + else + bfsize = DMA_BUFFER_SIZE; + +#ifdef CONFIG_STMMAC_TIMER + /* Disable interrupts on completion for the reception if timer is on */ + if (likely(priv->tm->enable)) + dis_ic = 1; +#endif + /* If the MTU exceeds 8k so use the second buffer in the chain */ + if (bfsize >= BUF_SIZE_8KiB) + buff2_needed = 1; + + DBG(probe, INFO, "stmmac: txsize %d, rxsize %d, bfsize %d\n", + txsize, rxsize, bfsize); + + priv->rx_skbuff_dma = kmalloc(rxsize * sizeof(dma_addr_t), GFP_KERNEL); + priv->rx_skbuff = + kmalloc(sizeof(struct sk_buff *) * rxsize, GFP_KERNEL); + priv->dma_rx = + (struct dma_desc *)dma_alloc_coherent(priv->device, + rxsize * + sizeof(struct dma_desc), + &priv->dma_rx_phy, + GFP_KERNEL); + priv->tx_skbuff = kmalloc(sizeof(struct sk_buff *) * txsize, + GFP_KERNEL); + priv->dma_tx = + (struct dma_desc *)dma_alloc_coherent(priv->device, + txsize * + sizeof(struct dma_desc), + &priv->dma_tx_phy, + GFP_KERNEL); + + if ((priv->dma_rx == NULL) || (priv->dma_tx == NULL)) { + pr_err("%s:ERROR allocating the DMA Tx/Rx desc\n", __func__); + return; + } + + DBG(probe, INFO, "stmmac (%s) DMA desc rings: virt addr (Rx %p, " + "Tx %p)\n\tDMA phy addr (Rx 0x%08x, Tx 0x%08x)\n", + dev->name, priv->dma_rx, priv->dma_tx, + (unsigned int)priv->dma_rx_phy, (unsigned int)priv->dma_tx_phy); + + /* RX INITIALIZATION */ + DBG(probe, INFO, "stmmac: SKB addresses:\n" + "skb\t\tskb data\tdma data\n"); + + for (i = 0; i < rxsize; i++) { + struct dma_desc *p = priv->dma_rx + i; + + skb = netdev_alloc_skb_ip_align(dev, bfsize); + if (unlikely(skb == NULL)) { + pr_err("%s: Rx init fails; skb is NULL\n", __func__); + break; + } + priv->rx_skbuff[i] = skb; + priv->rx_skbuff_dma[i] = dma_map_single(priv->device, skb->data, + bfsize, DMA_FROM_DEVICE); + + p->des2 = priv->rx_skbuff_dma[i]; + if (unlikely(buff2_needed)) + p->des3 = p->des2 + BUF_SIZE_8KiB; + DBG(probe, INFO, "[%p]\t[%p]\t[%x]\n", priv->rx_skbuff[i], + priv->rx_skbuff[i]->data, priv->rx_skbuff_dma[i]); + } + priv->cur_rx = 0; + priv->dirty_rx = (unsigned int)(i - rxsize); + priv->dma_buf_sz = bfsize; + buf_sz = bfsize; + + /* TX INITIALIZATION */ + for (i = 0; i < txsize; i++) { + priv->tx_skbuff[i] = NULL; + priv->dma_tx[i].des2 = 0; + } + priv->dirty_tx = 0; + priv->cur_tx = 0; + + /* Clear the Rx/Tx descriptors */ + priv->mac_type->ops->init_rx_desc(priv->dma_rx, rxsize, dis_ic); + priv->mac_type->ops->init_tx_desc(priv->dma_tx, txsize); + + if (netif_msg_hw(priv)) { + pr_info("RX descriptor ring:\n"); + display_ring(priv->dma_rx, rxsize); + pr_info("TX descriptor ring:\n"); + display_ring(priv->dma_tx, txsize); + } + return; +} + +static void dma_free_rx_skbufs(struct stmmac_priv *priv) +{ + int i; + + for (i = 0; i < priv->dma_rx_size; i++) { + if (priv->rx_skbuff[i]) { + dma_unmap_single(priv->device, priv->rx_skbuff_dma[i], + priv->dma_buf_sz, DMA_FROM_DEVICE); + dev_kfree_skb_any(priv->rx_skbuff[i]); + } + priv->rx_skbuff[i] = NULL; + } + return; +} + +static void dma_free_tx_skbufs(struct stmmac_priv *priv) +{ + int i; + + for (i = 0; i < priv->dma_tx_size; i++) { + if (priv->tx_skbuff[i] != NULL) { + struct dma_desc *p = priv->dma_tx + i; + if (p->des2) + dma_unmap_single(priv->device, p->des2, + priv->mac_type->ops->get_tx_len(p), + DMA_TO_DEVICE); + dev_kfree_skb_any(priv->tx_skbuff[i]); + priv->tx_skbuff[i] = NULL; + } + } + return; +} + +static void free_dma_desc_resources(struct stmmac_priv *priv) +{ + /* Release the DMA TX/RX socket buffers */ + dma_free_rx_skbufs(priv); + dma_free_tx_skbufs(priv); + + /* Free the region of consistent memory previously allocated for + * the DMA */ + dma_free_coherent(priv->device, + priv->dma_tx_size * sizeof(struct dma_desc), + priv->dma_tx, priv->dma_tx_phy); + dma_free_coherent(priv->device, + priv->dma_rx_size * sizeof(struct dma_desc), + priv->dma_rx, priv->dma_rx_phy); + kfree(priv->rx_skbuff_dma); + kfree(priv->rx_skbuff); + kfree(priv->tx_skbuff); + + return; +} + +/** + * stmmac_dma_start_tx + * @ioaddr: device I/O address + * Description: this function starts the DMA tx process. + */ +static void stmmac_dma_start_tx(unsigned long ioaddr) +{ + u32 value = readl(ioaddr + DMA_CONTROL); + value |= DMA_CONTROL_ST; + writel(value, ioaddr + DMA_CONTROL); + return; +} + +static void stmmac_dma_stop_tx(unsigned long ioaddr) +{ + u32 value = readl(ioaddr + DMA_CONTROL); + value &= ~DMA_CONTROL_ST; + writel(value, ioaddr + DMA_CONTROL); + return; +} + +/** + * stmmac_dma_start_rx + * @ioaddr: device I/O address + * Description: this function starts the DMA rx process. + */ +static void stmmac_dma_start_rx(unsigned long ioaddr) +{ + u32 value = readl(ioaddr + DMA_CONTROL); + value |= DMA_CONTROL_SR; + writel(value, ioaddr + DMA_CONTROL); + + return; +} + +static void stmmac_dma_stop_rx(unsigned long ioaddr) +{ + u32 value = readl(ioaddr + DMA_CONTROL); + value &= ~DMA_CONTROL_SR; + writel(value, ioaddr + DMA_CONTROL); + + return; +} + +/** + * stmmac_dma_operation_mode - HW DMA operation mode + * @priv : pointer to the private device structure. + * Description: it sets the DMA operation mode: tx/rx DMA thresholds + * or Store-And-Forward capability. It also verifies the COE for the + * transmission in case of Giga ETH. + */ +static void stmmac_dma_operation_mode(struct stmmac_priv *priv) +{ + if (!priv->is_gmac) { + /* MAC 10/100 */ + priv->mac_type->ops->dma_mode(priv->dev->base_addr, tc, 0); + priv->tx_coe = NO_HW_CSUM; + } else { + if ((priv->dev->mtu <= ETH_DATA_LEN) && (tx_coe)) { + priv->mac_type->ops->dma_mode(priv->dev->base_addr, + SF_DMA_MODE, SF_DMA_MODE); + tc = SF_DMA_MODE; + priv->tx_coe = HW_CSUM; + } else { + /* Checksum computation is performed in software. */ + priv->mac_type->ops->dma_mode(priv->dev->base_addr, tc, + SF_DMA_MODE); + priv->tx_coe = NO_HW_CSUM; + } + } + tx_coe = priv->tx_coe; + + return; +} + +#ifdef STMMAC_DEBUG +/** + * show_tx_process_state + * @status: tx descriptor status field + * Description: it shows the Transmit Process State for CSR5[22:20] + */ +static void show_tx_process_state(unsigned int status) +{ + unsigned int state; + state = (status & DMA_STATUS_TS_MASK) >> DMA_STATUS_TS_SHIFT; + + switch (state) { + case 0: + pr_info("- TX (Stopped): Reset or Stop command\n"); + break; + case 1: + pr_info("- TX (Running):Fetching the Tx desc\n"); + break; + case 2: + pr_info("- TX (Running): Waiting for end of tx\n"); + break; + case 3: + pr_info("- TX (Running): Reading the data " + "and queuing the data into the Tx buf\n"); + break; + case 6: + pr_info("- TX (Suspended): Tx Buff Underflow " + "or an unavailable Transmit descriptor\n"); + break; + case 7: + pr_info("- TX (Running): Closing Tx descriptor\n"); + break; + default: + break; + } + return; +} + +/** + * show_rx_process_state + * @status: rx descriptor status field + * Description: it shows the Receive Process State for CSR5[19:17] + */ +static void show_rx_process_state(unsigned int status) +{ + unsigned int state; + state = (status & DMA_STATUS_RS_MASK) >> DMA_STATUS_RS_SHIFT; + + switch (state) { + case 0: + pr_info("- RX (Stopped): Reset or Stop command\n"); + break; + case 1: + pr_info("- RX (Running): Fetching the Rx desc\n"); + break; + case 2: + pr_info("- RX (Running):Checking for end of pkt\n"); + break; + case 3: + pr_info("- RX (Running): Waiting for Rx pkt\n"); + break; + case 4: + pr_info("- RX (Suspended): Unavailable Rx buf\n"); + break; + case 5: + pr_info("- RX (Running): Closing Rx descriptor\n"); + break; + case 6: + pr_info("- RX(Running): Flushing the current frame" + " from the Rx buf\n"); + break; + case 7: + pr_info("- RX (Running): Queuing the Rx frame" + " from the Rx buf into memory\n"); + break; + default: + break; + } + return; +} +#endif + +/** + * stmmac_tx: + * @priv: private driver structure + * Description: it reclaims resources after transmission completes. + */ +static void stmmac_tx(struct stmmac_priv *priv) +{ + unsigned int txsize = priv->dma_tx_size; + unsigned long ioaddr = priv->dev->base_addr; + + while (priv->dirty_tx != priv->cur_tx) { + int last; + unsigned int entry = priv->dirty_tx % txsize; + struct sk_buff *skb = priv->tx_skbuff[entry]; + struct dma_desc *p = priv->dma_tx + entry; + + /* Check if the descriptor is owned by the DMA. */ + if (priv->mac_type->ops->get_tx_owner(p)) + break; + + /* Verify tx error by looking at the last segment */ + last = priv->mac_type->ops->get_tx_ls(p); + if (likely(last)) { + int tx_error = + priv->mac_type->ops->tx_status(&priv->dev->stats, + &priv->xstats, + p, ioaddr); + if (likely(tx_error == 0)) { + priv->dev->stats.tx_packets++; + priv->xstats.tx_pkt_n++; + } else + priv->dev->stats.tx_errors++; + } + TX_DBG("%s: curr %d, dirty %d\n", __func__, + priv->cur_tx, priv->dirty_tx); + + if (likely(p->des2)) + dma_unmap_single(priv->device, p->des2, + priv->mac_type->ops->get_tx_len(p), + DMA_TO_DEVICE); + if (unlikely(p->des3)) + p->des3 = 0; + + if (likely(skb != NULL)) { + /* + * If there's room in the queue (limit it to size) + * we add this skb back into the pool, + * if it's the right size. + */ + if ((skb_queue_len(&priv->rx_recycle) < + priv->dma_rx_size) && + skb_recycle_check(skb, priv->dma_buf_sz)) + __skb_queue_head(&priv->rx_recycle, skb); + else + dev_kfree_skb(skb); + + priv->tx_skbuff[entry] = NULL; + } + + priv->mac_type->ops->release_tx_desc(p); + + entry = (++priv->dirty_tx) % txsize; + } + if (unlikely(netif_queue_stopped(priv->dev) && + stmmac_tx_avail(priv) > STMMAC_TX_THRESH(priv))) { + netif_tx_lock(priv->dev); + if (netif_queue_stopped(priv->dev) && + stmmac_tx_avail(priv) > STMMAC_TX_THRESH(priv)) { + TX_DBG("%s: restart transmit\n", __func__); + netif_wake_queue(priv->dev); + } + netif_tx_unlock(priv->dev); + } + return; +} + +static inline void stmmac_enable_irq(struct stmmac_priv *priv) +{ +#ifdef CONFIG_STMMAC_TIMER + if (likely(priv->tm->enable)) + priv->tm->timer_start(tmrate); + else +#endif + writel(DMA_INTR_DEFAULT_MASK, priv->dev->base_addr + DMA_INTR_ENA); +} + +static inline void stmmac_disable_irq(struct stmmac_priv *priv) +{ +#ifdef CONFIG_STMMAC_TIMER + if (likely(priv->tm->enable)) + priv->tm->timer_stop(); + else +#endif + writel(0, priv->dev->base_addr + DMA_INTR_ENA); +} + +static int stmmac_has_work(struct stmmac_priv *priv) +{ + unsigned int has_work = 0; + int rxret, tx_work = 0; + + rxret = priv->mac_type->ops->get_rx_owner(priv->dma_rx + + (priv->cur_rx % priv->dma_rx_size)); + + if (priv->dirty_tx != priv->cur_tx) + tx_work = 1; + + if (likely(!rxret || tx_work)) + has_work = 1; + + return has_work; +} + +static inline void _stmmac_schedule(struct stmmac_priv *priv) +{ + if (likely(stmmac_has_work(priv))) { + stmmac_disable_irq(priv); + napi_schedule(&priv->napi); + } +} + +#ifdef CONFIG_STMMAC_TIMER +void stmmac_schedule(struct net_device *dev) +{ + struct stmmac_priv *priv = netdev_priv(dev); + + priv->xstats.sched_timer_n++; + + _stmmac_schedule(priv); + + return; +} + +static void stmmac_no_timer_started(unsigned int x) +{; +}; + +static void stmmac_no_timer_stopped(void) +{; +}; +#endif + +/** + * stmmac_tx_err: + * @priv: pointer to the private device structure + * Description: it cleans the descriptors and restarts the transmission + * in case of errors. + */ +static void stmmac_tx_err(struct stmmac_priv *priv) +{ + netif_stop_queue(priv->dev); + + stmmac_dma_stop_tx(priv->dev->base_addr); + dma_free_tx_skbufs(priv); + priv->mac_type->ops->init_tx_desc(priv->dma_tx, priv->dma_tx_size); + priv->dirty_tx = 0; + priv->cur_tx = 0; + stmmac_dma_start_tx(priv->dev->base_addr); + + priv->dev->stats.tx_errors++; + netif_wake_queue(priv->dev); + + return; +} + +/** + * stmmac_dma_interrupt - Interrupt handler for the driver + * @dev: net device structure + * Description: Interrupt handler for the driver (DMA). + */ +static void stmmac_dma_interrupt(struct net_device *dev) +{ + unsigned long ioaddr = dev->base_addr; + struct stmmac_priv *priv = netdev_priv(dev); + /* read the status register (CSR5) */ + u32 intr_status = readl(ioaddr + DMA_STATUS); + + DBG(intr, INFO, "%s: [CSR5: 0x%08x]\n", __func__, intr_status); + +#ifdef STMMAC_DEBUG + /* It displays the DMA transmit process state (CSR5 register) */ + if (netif_msg_tx_done(priv)) + show_tx_process_state(intr_status); + if (netif_msg_rx_status(priv)) + show_rx_process_state(intr_status); +#endif + /* ABNORMAL interrupts */ + if (unlikely(intr_status & DMA_STATUS_AIS)) { + DBG(intr, INFO, "CSR5[15] DMA ABNORMAL IRQ: "); + if (unlikely(intr_status & DMA_STATUS_UNF)) { + DBG(intr, INFO, "transmit underflow\n"); + if (unlikely(tc != SF_DMA_MODE) + && (tc <= 256)) { + /* Try to bump up the threshold */ + tc += 64; + priv->mac_type->ops->dma_mode(ioaddr, tc, + SF_DMA_MODE); + priv->xstats.threshold = tc; + } + stmmac_tx_err(priv); + priv->xstats.tx_undeflow_irq++; + } + if (unlikely(intr_status & DMA_STATUS_TJT)) { + DBG(intr, INFO, "transmit jabber\n"); + priv->xstats.tx_jabber_irq++; + } + if (unlikely(intr_status & DMA_STATUS_OVF)) { + DBG(intr, INFO, "recv overflow\n"); + priv->xstats.rx_overflow_irq++; + } + if (unlikely(intr_status & DMA_STATUS_RU)) { + DBG(intr, INFO, "receive buffer unavailable\n"); + priv->xstats.rx_buf_unav_irq++; + } + if (unlikely(intr_status & DMA_STATUS_RPS)) { + DBG(intr, INFO, "receive process stopped\n"); + priv->xstats.rx_process_stopped_irq++; + } + if (unlikely(intr_status & DMA_STATUS_RWT)) { + DBG(intr, INFO, "receive watchdog\n"); + priv->xstats.rx_watchdog_irq++; + } + if (unlikely(intr_status & DMA_STATUS_ETI)) { + DBG(intr, INFO, "transmit early interrupt\n"); + priv->xstats.tx_early_irq++; + } + if (unlikely(intr_status & DMA_STATUS_TPS)) { + DBG(intr, INFO, "transmit process stopped\n"); + priv->xstats.tx_process_stopped_irq++; + stmmac_tx_err(priv); + } + if (unlikely(intr_status & DMA_STATUS_FBI)) { + DBG(intr, INFO, "fatal bus error\n"); + priv->xstats.fatal_bus_error_irq++; + stmmac_tx_err(priv); + } + } + + /* TX/RX NORMAL interrupts */ + if (intr_status & DMA_STATUS_NIS) { + priv->xstats.normal_irq_n++; + if (likely((intr_status & DMA_STATUS_RI) || + (intr_status & (DMA_STATUS_TI)))) + _stmmac_schedule(priv); + } + + /* Optional hardware blocks, interrupts should be disabled */ + if (unlikely(intr_status & + (DMA_STATUS_GPI | DMA_STATUS_GMI | DMA_STATUS_GLI))) + pr_info("%s: unexpected status %08x\n", __func__, intr_status); + + /* Clear the interrupt by writing a logic 1 to the CSR5[15-0] */ + writel((intr_status & 0x1ffff), ioaddr + DMA_STATUS); + + DBG(intr, INFO, "\n\n"); + + return; +} + +/** + * stmmac_open - open entry point of the driver + * @dev : pointer to the device structure. + * Description: + * This function is the open entry point of the driver. + * Return value: + * 0 on success and an appropriate (-)ve integer as defined in errno.h + * file on failure. + */ +static int stmmac_open(struct net_device *dev) +{ + struct stmmac_priv *priv = netdev_priv(dev); + unsigned long ioaddr = dev->base_addr; + int ret; + + /* Check that the MAC address is valid. If its not, refuse + * to bring the device up. The user must specify an + * address using the following linux command: + * ifconfig eth0 hw ether xx:xx:xx:xx:xx:xx */ + if (!is_valid_ether_addr(dev->dev_addr)) { + random_ether_addr(dev->dev_addr); + pr_warning("%s: generated random MAC address %pM\n", dev->name, + dev->dev_addr); + } + + stmmac_verify_args(); + + ret = stmmac_init_phy(dev); + if (unlikely(ret)) { + pr_err("%s: Cannot attach to PHY (error: %d)\n", __func__, ret); + return ret; + } + + /* Request the IRQ lines */ + ret = request_irq(dev->irq, &stmmac_interrupt, + IRQF_SHARED, dev->name, dev); + if (unlikely(ret < 0)) { + pr_err("%s: ERROR: allocating the IRQ %d (error: %d)\n", + __func__, dev->irq, ret); + return ret; + } + +#ifdef CONFIG_STMMAC_TIMER + priv->tm = kzalloc(sizeof(struct stmmac_timer *), GFP_KERNEL); + if (unlikely(priv->tm == NULL)) { + pr_err("%s: ERROR: timer memory alloc failed \n", __func__); + return -ENOMEM; + } + priv->tm->freq = tmrate; + + /* Test if the external timer can be actually used. + * In case of failure continue without timer. */ + if (unlikely((stmmac_open_ext_timer(dev, priv->tm)) < 0)) { + pr_warning("stmmaceth: cannot attach the external timer.\n"); + tmrate = 0; + priv->tm->freq = 0; + priv->tm->timer_start = stmmac_no_timer_started; + priv->tm->timer_stop = stmmac_no_timer_stopped; + } else + priv->tm->enable = 1; +#endif + + /* Create and initialize the TX/RX descriptors chains. */ + priv->dma_tx_size = STMMAC_ALIGN(dma_txsize); + priv->dma_rx_size = STMMAC_ALIGN(dma_rxsize); + priv->dma_buf_sz = STMMAC_ALIGN(buf_sz); + init_dma_desc_rings(dev); + + /* DMA initialization and SW reset */ + if (unlikely(priv->mac_type->ops->dma_init(ioaddr, + priv->pbl, priv->dma_tx_phy, priv->dma_rx_phy) < 0)) { + + pr_err("%s: DMA initialization failed\n", __func__); + return -1; + } + + /* Copy the MAC addr into the HW */ + priv->mac_type->ops->set_umac_addr(ioaddr, dev->dev_addr, 0); + /* Initialize the MAC Core */ + priv->mac_type->ops->core_init(ioaddr); + + priv->shutdown = 0; + + /* Initialise the MMC (if present) to disable all interrupts. */ + writel(0xffffffff, ioaddr + MMC_HIGH_INTR_MASK); + writel(0xffffffff, ioaddr + MMC_LOW_INTR_MASK); + + /* Enable the MAC Rx/Tx */ + stmmac_mac_enable_rx(ioaddr); + stmmac_mac_enable_tx(ioaddr); + + /* Set the HW DMA mode and the COE */ + stmmac_dma_operation_mode(priv); + + /* Extra statistics */ + memset(&priv->xstats, 0, sizeof(struct stmmac_extra_stats)); + priv->xstats.threshold = tc; + + /* Start the ball rolling... */ + DBG(probe, DEBUG, "%s: DMA RX/TX processes started...\n", dev->name); + stmmac_dma_start_tx(ioaddr); + stmmac_dma_start_rx(ioaddr); + +#ifdef CONFIG_STMMAC_TIMER + priv->tm->timer_start(tmrate); +#endif + /* Dump DMA/MAC registers */ + if (netif_msg_hw(priv)) { + priv->mac_type->ops->dump_mac_regs(ioaddr); + priv->mac_type->ops->dump_dma_regs(ioaddr); + } + + if (priv->phydev) + phy_start(priv->phydev); + + napi_enable(&priv->napi); + skb_queue_head_init(&priv->rx_recycle); + netif_start_queue(dev); + return 0; +} + +/** + * stmmac_release - close entry point of the driver + * @dev : device pointer. + * Description: + * This is the stop entry point of the driver. + */ +static int stmmac_release(struct net_device *dev) +{ + struct stmmac_priv *priv = netdev_priv(dev); + + /* Stop and disconnect the PHY */ + if (priv->phydev) { + phy_stop(priv->phydev); + phy_disconnect(priv->phydev); + priv->phydev = NULL; + } + + netif_stop_queue(dev); + +#ifdef CONFIG_STMMAC_TIMER + /* Stop and release the timer */ + stmmac_close_ext_timer(); + if (priv->tm != NULL) + kfree(priv->tm); +#endif + napi_disable(&priv->napi); + skb_queue_purge(&priv->rx_recycle); + + /* Free the IRQ lines */ + free_irq(dev->irq, dev); + + /* Stop TX/RX DMA and clear the descriptors */ + stmmac_dma_stop_tx(dev->base_addr); + stmmac_dma_stop_rx(dev->base_addr); + + /* Release and free the Rx/Tx resources */ + free_dma_desc_resources(priv); + + /* Disable the MAC core */ + stmmac_mac_disable_tx(dev->base_addr); + stmmac_mac_disable_rx(dev->base_addr); + + netif_carrier_off(dev); + + return 0; +} + +/* + * To perform emulated hardware segmentation on skb. + */ +static int stmmac_sw_tso(struct stmmac_priv *priv, struct sk_buff *skb) +{ + struct sk_buff *segs, *curr_skb; + int gso_segs = skb_shinfo(skb)->gso_segs; + + /* Estimate the number of fragments in the worst case */ + if (unlikely(stmmac_tx_avail(priv) < gso_segs)) { + netif_stop_queue(priv->dev); + TX_DBG(KERN_ERR "%s: TSO BUG! Tx Ring full when queue awake\n", + __func__); + if (stmmac_tx_avail(priv) < gso_segs) + return NETDEV_TX_BUSY; + + netif_wake_queue(priv->dev); + } + TX_DBG("\tstmmac_sw_tso: segmenting: skb %p (len %d)\n", + skb, skb->len); + + segs = skb_gso_segment(skb, priv->dev->features & ~NETIF_F_TSO); + if (unlikely(IS_ERR(segs))) + goto sw_tso_end; + + do { + curr_skb = segs; + segs = segs->next; + TX_DBG("\t\tcurrent skb->len: %d, *curr %p," + "*next %p\n", curr_skb->len, curr_skb, segs); + curr_skb->next = NULL; + stmmac_xmit(curr_skb, priv->dev); + } while (segs); + +sw_tso_end: + dev_kfree_skb(skb); + + return NETDEV_TX_OK; +} + +static unsigned int stmmac_handle_jumbo_frames(struct sk_buff *skb, + struct net_device *dev, + int csum_insertion) +{ + struct stmmac_priv *priv = netdev_priv(dev); + unsigned int nopaged_len = skb_headlen(skb); + unsigned int txsize = priv->dma_tx_size; + unsigned int entry = priv->cur_tx % txsize; + struct dma_desc *desc = priv->dma_tx + entry; + + if (nopaged_len > BUF_SIZE_8KiB) { + + int buf2_size = nopaged_len - BUF_SIZE_8KiB; + + desc->des2 = dma_map_single(priv->device, skb->data, + BUF_SIZE_8KiB, DMA_TO_DEVICE); + desc->des3 = desc->des2 + BUF_SIZE_4KiB; + priv->mac_type->ops->prepare_tx_desc(desc, 1, BUF_SIZE_8KiB, + csum_insertion); + + entry = (++priv->cur_tx) % txsize; + desc = priv->dma_tx + entry; + + desc->des2 = dma_map_single(priv->device, + skb->data + BUF_SIZE_8KiB, + buf2_size, DMA_TO_DEVICE); + desc->des3 = desc->des2 + BUF_SIZE_4KiB; + priv->mac_type->ops->prepare_tx_desc(desc, 0, + buf2_size, csum_insertion); + priv->mac_type->ops->set_tx_owner(desc); + priv->tx_skbuff[entry] = NULL; + } else { + desc->des2 = dma_map_single(priv->device, skb->data, + nopaged_len, DMA_TO_DEVICE); + desc->des3 = desc->des2 + BUF_SIZE_4KiB; + priv->mac_type->ops->prepare_tx_desc(desc, 1, nopaged_len, + csum_insertion); + } + return entry; +} + +/** + * stmmac_xmit: + * @skb : the socket buffer + * @dev : device pointer + * Description : Tx entry point of the driver. + */ +static netdev_tx_t stmmac_xmit(struct sk_buff *skb, struct net_device *dev) +{ + struct stmmac_priv *priv = netdev_priv(dev); + unsigned int txsize = priv->dma_tx_size; + unsigned int entry; + int i, csum_insertion = 0; + int nfrags = skb_shinfo(skb)->nr_frags; + struct dma_desc *desc, *first; + + if (unlikely(stmmac_tx_avail(priv) < nfrags + 1)) { + if (!netif_queue_stopped(dev)) { + netif_stop_queue(dev); + /* This is a hard error, log it. */ + pr_err("%s: BUG! Tx Ring full when queue awake\n", + __func__); + } + return NETDEV_TX_BUSY; + } + + entry = priv->cur_tx % txsize; + +#ifdef STMMAC_XMIT_DEBUG + if ((skb->len > ETH_FRAME_LEN) || nfrags) + pr_info("stmmac xmit:\n" + "\tskb addr %p - len: %d - nopaged_len: %d\n" + "\tn_frags: %d - ip_summed: %d - %s gso\n", + skb, skb->len, skb_headlen(skb), nfrags, skb->ip_summed, + !skb_is_gso(skb) ? "isn't" : "is"); +#endif + + if (unlikely(skb_is_gso(skb))) + return stmmac_sw_tso(priv, skb); + + if (likely((skb->ip_summed == CHECKSUM_PARTIAL))) { + if (likely(priv->tx_coe == NO_HW_CSUM)) + skb_checksum_help(skb); + else + csum_insertion = 1; + } + + desc = priv->dma_tx + entry; + first = desc; + +#ifdef STMMAC_XMIT_DEBUG + if ((nfrags > 0) || (skb->len > ETH_FRAME_LEN)) + pr_debug("stmmac xmit: skb len: %d, nopaged_len: %d,\n" + "\t\tn_frags: %d, ip_summed: %d\n", + skb->len, skb_headlen(skb), nfrags, skb->ip_summed); +#endif + priv->tx_skbuff[entry] = skb; + if (unlikely(skb->len >= BUF_SIZE_4KiB)) { + entry = stmmac_handle_jumbo_frames(skb, dev, csum_insertion); + desc = priv->dma_tx + entry; + } else { + unsigned int nopaged_len = skb_headlen(skb); + desc->des2 = dma_map_single(priv->device, skb->data, + nopaged_len, DMA_TO_DEVICE); + priv->mac_type->ops->prepare_tx_desc(desc, 1, nopaged_len, + csum_insertion); + } + + for (i = 0; i < nfrags; i++) { + skb_frag_t *frag = &skb_shinfo(skb)->frags[i]; + int len = frag->size; + + entry = (++priv->cur_tx) % txsize; + desc = priv->dma_tx + entry; + + TX_DBG("\t[entry %d] segment len: %d\n", entry, len); + desc->des2 = dma_map_page(priv->device, frag->page, + frag->page_offset, + len, DMA_TO_DEVICE); + priv->tx_skbuff[entry] = NULL; + priv->mac_type->ops->prepare_tx_desc(desc, 0, len, + csum_insertion); + priv->mac_type->ops->set_tx_owner(desc); + } + + /* Interrupt on completition only for the latest segment */ + priv->mac_type->ops->close_tx_desc(desc); + +#ifdef CONFIG_STMMAC_TIMER + /* Clean IC while using timer */ + if (likely(priv->tm->enable)) + priv->mac_type->ops->clear_tx_ic(desc); +#endif + /* To avoid raise condition */ + priv->mac_type->ops->set_tx_owner(first); + + priv->cur_tx++; + +#ifdef STMMAC_XMIT_DEBUG + if (netif_msg_pktdata(priv)) { + pr_info("stmmac xmit: current=%d, dirty=%d, entry=%d, " + "first=%p, nfrags=%d\n", + (priv->cur_tx % txsize), (priv->dirty_tx % txsize), + entry, first, nfrags); + display_ring(priv->dma_tx, txsize); + pr_info(">>> frame to be transmitted: "); + print_pkt(skb->data, skb->len); + } +#endif + if (unlikely(stmmac_tx_avail(priv) <= (MAX_SKB_FRAGS + 1))) { + TX_DBG("%s: stop transmitted packets\n", __func__); + netif_stop_queue(dev); + } + + dev->stats.tx_bytes += skb->len; + + /* CSR1 enables the transmit DMA to check for new descriptor */ + writel(1, dev->base_addr + DMA_XMT_POLL_DEMAND); + + return NETDEV_TX_OK; +} + +static inline void stmmac_rx_refill(struct stmmac_priv *priv) +{ + unsigned int rxsize = priv->dma_rx_size; + int bfsize = priv->dma_buf_sz; + struct dma_desc *p = priv->dma_rx; + + for (; priv->cur_rx - priv->dirty_rx > 0; priv->dirty_rx++) { + unsigned int entry = priv->dirty_rx % rxsize; + if (likely(priv->rx_skbuff[entry] == NULL)) { + struct sk_buff *skb; + + skb = __skb_dequeue(&priv->rx_recycle); + if (skb == NULL) + skb = netdev_alloc_skb_ip_align(priv->dev, + bfsize); + + if (unlikely(skb == NULL)) + break; + + priv->rx_skbuff[entry] = skb; + priv->rx_skbuff_dma[entry] = + dma_map_single(priv->device, skb->data, bfsize, + DMA_FROM_DEVICE); + + (p + entry)->des2 = priv->rx_skbuff_dma[entry]; + if (unlikely(priv->is_gmac)) { + if (bfsize >= BUF_SIZE_8KiB) + (p + entry)->des3 = + (p + entry)->des2 + BUF_SIZE_8KiB; + } + RX_DBG(KERN_INFO "\trefill entry #%d\n", entry); + } + priv->mac_type->ops->set_rx_owner(p + entry); + } + return; +} + +static int stmmac_rx(struct stmmac_priv *priv, int limit) +{ + unsigned int rxsize = priv->dma_rx_size; + unsigned int entry = priv->cur_rx % rxsize; + unsigned int next_entry; + unsigned int count = 0; + struct dma_desc *p = priv->dma_rx + entry; + struct dma_desc *p_next; + +#ifdef STMMAC_RX_DEBUG + if (netif_msg_hw(priv)) { + pr_debug(">>> stmmac_rx: descriptor ring:\n"); + display_ring(priv->dma_rx, rxsize); + } +#endif + count = 0; + while (!priv->mac_type->ops->get_rx_owner(p)) { + int status; + + if (count >= limit) + break; + + count++; + + next_entry = (++priv->cur_rx) % rxsize; + p_next = priv->dma_rx + next_entry; + prefetch(p_next); + + /* read the status of the incoming frame */ + status = (priv->mac_type->ops->rx_status(&priv->dev->stats, + &priv->xstats, p)); + if (unlikely(status == discard_frame)) + priv->dev->stats.rx_errors++; + else { + struct sk_buff *skb; + /* Length should omit the CRC */ + int frame_len = + priv->mac_type->ops->get_rx_frame_len(p) - 4; + +#ifdef STMMAC_RX_DEBUG + if (frame_len > ETH_FRAME_LEN) + pr_debug("\tRX frame size %d, COE status: %d\n", + frame_len, status); + + if (netif_msg_hw(priv)) + pr_debug("\tdesc: %p [entry %d] buff=0x%x\n", + p, entry, p->des2); +#endif + skb = priv->rx_skbuff[entry]; + if (unlikely(!skb)) { + pr_err("%s: Inconsistent Rx descriptor chain\n", + priv->dev->name); + priv->dev->stats.rx_dropped++; + break; + } + prefetch(skb->data - NET_IP_ALIGN); + priv->rx_skbuff[entry] = NULL; + + skb_put(skb, frame_len); + dma_unmap_single(priv->device, + priv->rx_skbuff_dma[entry], + priv->dma_buf_sz, DMA_FROM_DEVICE); +#ifdef STMMAC_RX_DEBUG + if (netif_msg_pktdata(priv)) { + pr_info(" frame received (%dbytes)", frame_len); + print_pkt(skb->data, frame_len); + } +#endif + skb->protocol = eth_type_trans(skb, priv->dev); + + if (unlikely(status == csum_none)) { + /* always for the old mac 10/100 */ + skb->ip_summed = CHECKSUM_NONE; + netif_receive_skb(skb); + } else { + skb->ip_summed = CHECKSUM_UNNECESSARY; + napi_gro_receive(&priv->napi, skb); + } + + priv->dev->stats.rx_packets++; + priv->dev->stats.rx_bytes += frame_len; + priv->dev->last_rx = jiffies; + } + entry = next_entry; + p = p_next; /* use prefetched values */ + } + + stmmac_rx_refill(priv); + + priv->xstats.rx_pkt_n += count; + + return count; +} + +/** + * stmmac_poll - stmmac poll method (NAPI) + * @napi : pointer to the napi structure. + * @budget : maximum number of packets that the current CPU can receive from + * all interfaces. + * Description : + * This function implements the the reception process. + * Also it runs the TX completion thread + */ +static int stmmac_poll(struct napi_struct *napi, int budget) +{ + struct stmmac_priv *priv = container_of(napi, struct stmmac_priv, napi); + int work_done = 0; + + priv->xstats.poll_n++; + stmmac_tx(priv); + work_done = stmmac_rx(priv, budget); + + if (work_done < budget) { + napi_complete(napi); + stmmac_enable_irq(priv); + } + return work_done; +} + +/** + * stmmac_tx_timeout + * @dev : Pointer to net device structure + * Description: this function is called when a packet transmission fails to + * complete within a reasonable tmrate. The driver will mark the error in the + * netdev structure and arrange for the device to be reset to a sane state + * in order to transmit a new packet. + */ +static void stmmac_tx_timeout(struct net_device *dev) +{ + struct stmmac_priv *priv = netdev_priv(dev); + + /* Clear Tx resources and restart transmitting again */ + stmmac_tx_err(priv); + return; +} + +/* Configuration changes (passed on by ifconfig) */ +static int stmmac_config(struct net_device *dev, struct ifmap *map) +{ + if (dev->flags & IFF_UP) /* can't act on a running interface */ + return -EBUSY; + + /* Don't allow changing the I/O address */ + if (map->base_addr != dev->base_addr) { + pr_warning("%s: can't change I/O address\n", dev->name); + return -EOPNOTSUPP; + } + + /* Don't allow changing the IRQ */ + if (map->irq != dev->irq) { + pr_warning("%s: can't change IRQ number %d\n", + dev->name, dev->irq); + return -EOPNOTSUPP; + } + + /* ignore other fields */ + return 0; +} + +/** + * stmmac_multicast_list - entry point for multicast addressing + * @dev : pointer to the device structure + * Description: + * This function is a driver entry point which gets called by the kernel + * whenever multicast addresses must be enabled/disabled. + * Return value: + * void. + */ +static void stmmac_multicast_list(struct net_device *dev) +{ + struct stmmac_priv *priv = netdev_priv(dev); + + spin_lock(&priv->lock); + priv->mac_type->ops->set_filter(dev); + spin_unlock(&priv->lock); + return; +} + +/** + * stmmac_change_mtu - entry point to change MTU size for the device. + * @dev : device pointer. + * @new_mtu : the new MTU size for the device. + * Description: the Maximum Transfer Unit (MTU) is used by the network layer + * to drive packet transmission. Ethernet has an MTU of 1500 octets + * (ETH_DATA_LEN). This value can be changed with ifconfig. + * Return value: + * 0 on success and an appropriate (-)ve integer as defined in errno.h + * file on failure. + */ +static int stmmac_change_mtu(struct net_device *dev, int new_mtu) +{ + struct stmmac_priv *priv = netdev_priv(dev); + int max_mtu; + + if (netif_running(dev)) { + pr_err("%s: must be stopped to change its MTU\n", dev->name); + return -EBUSY; + } + + if (priv->is_gmac) + max_mtu = JUMBO_LEN; + else + max_mtu = ETH_DATA_LEN; + + if ((new_mtu < 46) || (new_mtu > max_mtu)) { + pr_err("%s: invalid MTU, max MTU is: %d\n", dev->name, max_mtu); + return -EINVAL; + } + + dev->mtu = new_mtu; + + return 0; +} + +static irqreturn_t stmmac_interrupt(int irq, void *dev_id) +{ + struct net_device *dev = (struct net_device *)dev_id; + struct stmmac_priv *priv = netdev_priv(dev); + + if (unlikely(!dev)) { + pr_err("%s: invalid dev pointer\n", __func__); + return IRQ_NONE; + } + + if (priv->is_gmac) { + unsigned long ioaddr = dev->base_addr; + /* To handle GMAC own interrupts */ + priv->mac_type->ops->host_irq_status(ioaddr); + } + stmmac_dma_interrupt(dev); + + return IRQ_HANDLED; +} + +#ifdef CONFIG_NET_POLL_CONTROLLER +/* Polling receive - used by NETCONSOLE and other diagnostic tools + * to allow network I/O with interrupts disabled. */ +static void stmmac_poll_controller(struct net_device *dev) +{ + disable_irq(dev->irq); + stmmac_interrupt(dev->irq, dev); + enable_irq(dev->irq); +} +#endif + +/** + * stmmac_ioctl - Entry point for the Ioctl + * @dev: Device pointer. + * @rq: An IOCTL specefic structure, that can contain a pointer to + * a proprietary structure used to pass information to the driver. + * @cmd: IOCTL command + * Description: + * Currently there are no special functionality supported in IOCTL, just the + * phy_mii_ioctl(...) can be invoked. + */ +static int stmmac_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) +{ + struct stmmac_priv *priv = netdev_priv(dev); + int ret = -EOPNOTSUPP; + + if (!netif_running(dev)) + return -EINVAL; + + switch (cmd) { + case SIOCGMIIPHY: + case SIOCGMIIREG: + case SIOCSMIIREG: + if (!priv->phydev) + return -EINVAL; + + spin_lock(&priv->lock); + ret = phy_mii_ioctl(priv->phydev, if_mii(rq), cmd); + spin_unlock(&priv->lock); + default: + break; + } + return ret; +} + +#ifdef STMMAC_VLAN_TAG_USED +static void stmmac_vlan_rx_register(struct net_device *dev, + struct vlan_group *grp) +{ + struct stmmac_priv *priv = netdev_priv(dev); + + DBG(probe, INFO, "%s: Setting vlgrp to %p\n", dev->name, grp); + + spin_lock(&priv->lock); + priv->vlgrp = grp; + spin_unlock(&priv->lock); + + return; +} +#endif + +static const struct net_device_ops stmmac_netdev_ops = { + .ndo_open = stmmac_open, + .ndo_start_xmit = stmmac_xmit, + .ndo_stop = stmmac_release, + .ndo_change_mtu = stmmac_change_mtu, + .ndo_set_multicast_list = stmmac_multicast_list, + .ndo_tx_timeout = stmmac_tx_timeout, + .ndo_do_ioctl = stmmac_ioctl, + .ndo_set_config = stmmac_config, +#ifdef STMMAC_VLAN_TAG_USED + .ndo_vlan_rx_register = stmmac_vlan_rx_register, +#endif +#ifdef CONFIG_NET_POLL_CONTROLLER + .ndo_poll_controller = stmmac_poll_controller, +#endif + .ndo_set_mac_address = eth_mac_addr, +}; + +/** + * stmmac_probe - Initialization of the adapter . + * @dev : device pointer + * Description: The function initializes the network device structure for + * the STMMAC driver. It also calls the low level routines + * in order to init the HW (i.e. the DMA engine) + */ +static int stmmac_probe(struct net_device *dev) +{ + int ret = 0; + struct stmmac_priv *priv = netdev_priv(dev); + + ether_setup(dev); + + dev->netdev_ops = &stmmac_netdev_ops; + stmmac_set_ethtool_ops(dev); + + dev->features |= (NETIF_F_SG | NETIF_F_HW_CSUM | NETIF_F_HIGHDMA); + dev->watchdog_timeo = msecs_to_jiffies(watchdog); +#ifdef STMMAC_VLAN_TAG_USED + /* Both mac100 and gmac support receive VLAN tag detection */ + dev->features |= NETIF_F_HW_VLAN_RX; +#endif + priv->msg_enable = netif_msg_init(debug, default_msg_level); + + if (priv->is_gmac) + priv->rx_csum = 1; + + if (flow_ctrl) + priv->flow_ctrl = FLOW_AUTO; /* RX/TX pause on */ + + priv->pause = pause; + netif_napi_add(dev, &priv->napi, stmmac_poll, 64); + + /* Get the MAC address */ + priv->mac_type->ops->get_umac_addr(dev->base_addr, dev->dev_addr, 0); + + if (!is_valid_ether_addr(dev->dev_addr)) + pr_warning("\tno valid MAC address;" + "please, use ifconfig or nwhwconfig!\n"); + + ret = register_netdev(dev); + if (ret) { + pr_err("%s: ERROR %i registering the device\n", + __func__, ret); + return -ENODEV; + } + + DBG(probe, DEBUG, "%s: Scatter/Gather: %s - HW checksums: %s\n", + dev->name, (dev->features & NETIF_F_SG) ? "on" : "off", + (dev->features & NETIF_F_HW_CSUM) ? "on" : "off"); + + spin_lock_init(&priv->lock); + + return ret; +} + +/** + * stmmac_mac_device_setup + * @dev : device pointer + * Description: select and initialise the mac device (mac100 or Gmac). + */ +static int stmmac_mac_device_setup(struct net_device *dev) +{ + struct stmmac_priv *priv = netdev_priv(dev); + unsigned long ioaddr = dev->base_addr; + + struct mac_device_info *device; + + if (priv->is_gmac) + device = gmac_setup(ioaddr); + else + device = mac100_setup(ioaddr); + + if (!device) + return -ENOMEM; + + priv->mac_type = device; + + priv->wolenabled = priv->mac_type->hw.pmt; /* PMT supported */ + if (priv->wolenabled == PMT_SUPPORTED) + priv->wolopts = WAKE_MAGIC; /* Magic Frame */ + + return 0; +} + +static int stmmacphy_dvr_probe(struct platform_device *pdev) +{ + struct plat_stmmacphy_data *plat_dat; + plat_dat = (struct plat_stmmacphy_data *)((pdev->dev).platform_data); + + pr_debug("stmmacphy_dvr_probe: added phy for bus %d\n", + plat_dat->bus_id); + + return 0; +} + +static int stmmacphy_dvr_remove(struct platform_device *pdev) +{ + return 0; +} + +static struct platform_driver stmmacphy_driver = { + .driver = { + .name = PHY_RESOURCE_NAME, + }, + .probe = stmmacphy_dvr_probe, + .remove = stmmacphy_dvr_remove, +}; + +/** + * stmmac_associate_phy + * @dev: pointer to device structure + * @data: points to the private structure. + * Description: Scans through all the PHYs we have registered and checks if + * any are associated with our MAC. If so, then just fill in + * the blanks in our local context structure + */ +static int stmmac_associate_phy(struct device *dev, void *data) +{ + struct stmmac_priv *priv = (struct stmmac_priv *)data; + struct plat_stmmacphy_data *plat_dat; + + plat_dat = (struct plat_stmmacphy_data *)(dev->platform_data); + + DBG(probe, DEBUG, "%s: checking phy for bus %d\n", __func__, + plat_dat->bus_id); + + /* Check that this phy is for the MAC being initialised */ + if (priv->bus_id != plat_dat->bus_id) + return 0; + + /* OK, this PHY is connected to the MAC. + Go ahead and get the parameters */ + DBG(probe, DEBUG, "%s: OK. Found PHY config\n", __func__); + priv->phy_irq = + platform_get_irq_byname(to_platform_device(dev), "phyirq"); + DBG(probe, DEBUG, "%s: PHY irq on bus %d is %d\n", __func__, + plat_dat->bus_id, priv->phy_irq); + + /* Override with kernel parameters if supplied XXX CRS XXX + * this needs to have multiple instances */ + if ((phyaddr >= 0) && (phyaddr <= 31)) + plat_dat->phy_addr = phyaddr; + + priv->phy_addr = plat_dat->phy_addr; + priv->phy_mask = plat_dat->phy_mask; + priv->phy_interface = plat_dat->interface; + priv->phy_reset = plat_dat->phy_reset; + + DBG(probe, DEBUG, "%s: exiting\n", __func__); + return 1; /* forces exit of driver_for_each_device() */ +} + +/** + * stmmac_dvr_probe + * @pdev: platform device pointer + * Description: the driver is initialized through platform_device. + */ +static int stmmac_dvr_probe(struct platform_device *pdev) +{ + int ret = 0; + struct resource *res; + unsigned int *addr = NULL; + struct net_device *ndev = NULL; + struct stmmac_priv *priv; + struct plat_stmmacenet_data *plat_dat; + + pr_info("STMMAC driver:\n\tplatform registration... "); + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + ret = -ENODEV; + goto out; + } + pr_info("done!\n"); + + if (!request_mem_region(res->start, (res->end - res->start), + pdev->name)) { + pr_err("%s: ERROR: memory allocation failed" + "cannot get the I/O addr 0x%x\n", + __func__, (unsigned int)res->start); + ret = -EBUSY; + goto out; + } + + addr = ioremap(res->start, (res->end - res->start)); + if (!addr) { + pr_err("%s: ERROR: memory mapping failed \n", __func__); + ret = -ENOMEM; + goto out; + } + + ndev = alloc_etherdev(sizeof(struct stmmac_priv)); + if (!ndev) { + pr_err("%s: ERROR: allocating the device\n", __func__); + ret = -ENOMEM; + goto out; + } + + SET_NETDEV_DEV(ndev, &pdev->dev); + + /* Get the MAC information */ + ndev->irq = platform_get_irq_byname(pdev, "macirq"); + if (ndev->irq == -ENXIO) { + pr_err("%s: ERROR: MAC IRQ configuration " + "information not found\n", __func__); + ret = -ENODEV; + goto out; + } + + priv = netdev_priv(ndev); + priv->device = &(pdev->dev); + priv->dev = ndev; + plat_dat = (struct plat_stmmacenet_data *)((pdev->dev).platform_data); + priv->bus_id = plat_dat->bus_id; + priv->pbl = plat_dat->pbl; /* TLI */ + priv->is_gmac = plat_dat->has_gmac; /* GMAC is on board */ + + platform_set_drvdata(pdev, ndev); + + /* Set the I/O base addr */ + ndev->base_addr = (unsigned long)addr; + + /* MAC HW revice detection */ + ret = stmmac_mac_device_setup(ndev); + if (ret < 0) + goto out; + + /* Network Device Registration */ + ret = stmmac_probe(ndev); + if (ret < 0) + goto out; + + /* associate a PHY - it is provided by another platform bus */ + if (!driver_for_each_device + (&(stmmacphy_driver.driver), NULL, (void *)priv, + stmmac_associate_phy)) { + pr_err("No PHY device is associated with this MAC!\n"); + ret = -ENODEV; + goto out; + } + + priv->fix_mac_speed = plat_dat->fix_mac_speed; + priv->bsp_priv = plat_dat->bsp_priv; + + pr_info("\t%s - (dev. name: %s - id: %d, IRQ #%d\n" + "\tIO base addr: 0x%08x)\n", ndev->name, pdev->name, + pdev->id, ndev->irq, (unsigned int)addr); + + /* MDIO bus Registration */ + pr_debug("\tMDIO bus (id: %d)...", priv->bus_id); + ret = stmmac_mdio_register(ndev); + if (ret < 0) + goto out; + pr_debug("registered!\n"); + +out: + if (ret < 0) { + platform_set_drvdata(pdev, NULL); + release_mem_region(res->start, (res->end - res->start)); + if (addr != NULL) + iounmap(addr); + } + + return ret; +} + +/** + * stmmac_dvr_remove + * @pdev: platform device pointer + * Description: this function resets the TX/RX processes, disables the MAC RX/TX + * changes the link status, releases the DMA descriptor rings, + * unregisters the MDIO bus and unmaps the allocated memory. + */ +static int stmmac_dvr_remove(struct platform_device *pdev) +{ + struct net_device *ndev = platform_get_drvdata(pdev); + struct resource *res; + + pr_info("%s:\n\tremoving driver", __func__); + + stmmac_dma_stop_rx(ndev->base_addr); + stmmac_dma_stop_tx(ndev->base_addr); + + stmmac_mac_disable_rx(ndev->base_addr); + stmmac_mac_disable_tx(ndev->base_addr); + + netif_carrier_off(ndev); + + stmmac_mdio_unregister(ndev); + + platform_set_drvdata(pdev, NULL); + unregister_netdev(ndev); + + iounmap((void *)ndev->base_addr); + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + release_mem_region(res->start, (res->end - res->start)); + + free_netdev(ndev); + + return 0; +} + +#ifdef CONFIG_PM +static int stmmac_suspend(struct platform_device *pdev, pm_message_t state) +{ + struct net_device *dev = platform_get_drvdata(pdev); + struct stmmac_priv *priv = netdev_priv(dev); + int dis_ic = 0; + + if (!dev || !netif_running(dev)) + return 0; + + spin_lock(&priv->lock); + + if (state.event == PM_EVENT_SUSPEND) { + netif_device_detach(dev); + netif_stop_queue(dev); + if (priv->phydev) + phy_stop(priv->phydev); + +#ifdef CONFIG_STMMAC_TIMER + priv->tm->timer_stop(); + if (likely(priv->tm->enable)) + dis_ic = 1; +#endif + napi_disable(&priv->napi); + + /* Stop TX/RX DMA */ + stmmac_dma_stop_tx(dev->base_addr); + stmmac_dma_stop_rx(dev->base_addr); + /* Clear the Rx/Tx descriptors */ + priv->mac_type->ops->init_rx_desc(priv->dma_rx, + priv->dma_rx_size, dis_ic); + priv->mac_type->ops->init_tx_desc(priv->dma_tx, + priv->dma_tx_size); + + stmmac_mac_disable_tx(dev->base_addr); + + if (device_may_wakeup(&(pdev->dev))) { + /* Enable Power down mode by programming the PMT regs */ + if (priv->wolenabled == PMT_SUPPORTED) + priv->mac_type->ops->pmt(dev->base_addr, + priv->wolopts); + } else { + stmmac_mac_disable_rx(dev->base_addr); + } + } else { + priv->shutdown = 1; + /* Although this can appear slightly redundant it actually + * makes fast the standby operation and guarantees the driver + * working if hibernation is on media. */ + stmmac_release(dev); + } + + spin_unlock(&priv->lock); + return 0; +} + +static int stmmac_resume(struct platform_device *pdev) +{ + struct net_device *dev = platform_get_drvdata(pdev); + struct stmmac_priv *priv = netdev_priv(dev); + unsigned long ioaddr = dev->base_addr; + + if (!netif_running(dev)) + return 0; + + spin_lock(&priv->lock); + + if (priv->shutdown) { + /* Re-open the interface and re-init the MAC/DMA + and the rings. */ + stmmac_open(dev); + goto out_resume; + } + + /* Power Down bit, into the PM register, is cleared + * automatically as soon as a magic packet or a Wake-up frame + * is received. Anyway, it's better to manually clear + * this bit because it can generate problems while resuming + * from another devices (e.g. serial console). */ + if (device_may_wakeup(&(pdev->dev))) + if (priv->wolenabled == PMT_SUPPORTED) + priv->mac_type->ops->pmt(dev->base_addr, 0); + + netif_device_attach(dev); + + /* Enable the MAC and DMA */ + stmmac_mac_enable_rx(ioaddr); + stmmac_mac_enable_tx(ioaddr); + stmmac_dma_start_tx(ioaddr); + stmmac_dma_start_rx(ioaddr); + +#ifdef CONFIG_STMMAC_TIMER + priv->tm->timer_start(tmrate); +#endif + napi_enable(&priv->napi); + + if (priv->phydev) + phy_start(priv->phydev); + + netif_start_queue(dev); + +out_resume: + spin_unlock(&priv->lock); + return 0; +} +#endif + +static struct platform_driver stmmac_driver = { + .driver = { + .name = STMMAC_RESOURCE_NAME, + }, + .probe = stmmac_dvr_probe, + .remove = stmmac_dvr_remove, +#ifdef CONFIG_PM + .suspend = stmmac_suspend, + .resume = stmmac_resume, +#endif + +}; + +/** + * stmmac_init_module - Entry point for the driver + * Description: This function is the entry point for the driver. + */ +static int __init stmmac_init_module(void) +{ + int ret; + + if (platform_driver_register(&stmmacphy_driver)) { + pr_err("No PHY devices registered!\n"); + return -ENODEV; + } + + ret = platform_driver_register(&stmmac_driver); + return ret; +} + +/** + * stmmac_cleanup_module - Cleanup routine for the driver + * Description: This function is the cleanup routine for the driver. + */ +static void __exit stmmac_cleanup_module(void) +{ + platform_driver_unregister(&stmmacphy_driver); + platform_driver_unregister(&stmmac_driver); +} + +#ifndef MODULE +static int __init stmmac_cmdline_opt(char *str) +{ + char *opt; + + if (!str || !*str) + return -EINVAL; + while ((opt = strsep(&str, ",")) != NULL) { + if (!strncmp(opt, "debug:", 6)) + strict_strtoul(opt + 6, 0, (unsigned long *)&debug); + else if (!strncmp(opt, "phyaddr:", 8)) + strict_strtoul(opt + 8, 0, (unsigned long *)&phyaddr); + else if (!strncmp(opt, "dma_txsize:", 11)) + strict_strtoul(opt + 11, 0, + (unsigned long *)&dma_txsize); + else if (!strncmp(opt, "dma_rxsize:", 11)) + strict_strtoul(opt + 11, 0, + (unsigned long *)&dma_rxsize); + else if (!strncmp(opt, "buf_sz:", 7)) + strict_strtoul(opt + 7, 0, (unsigned long *)&buf_sz); + else if (!strncmp(opt, "tc:", 3)) + strict_strtoul(opt + 3, 0, (unsigned long *)&tc); + else if (!strncmp(opt, "tx_coe:", 7)) + strict_strtoul(opt + 7, 0, (unsigned long *)&tx_coe); + else if (!strncmp(opt, "watchdog:", 9)) + strict_strtoul(opt + 9, 0, (unsigned long *)&watchdog); + else if (!strncmp(opt, "flow_ctrl:", 10)) + strict_strtoul(opt + 10, 0, + (unsigned long *)&flow_ctrl); + else if (!strncmp(opt, "pause:", 6)) + strict_strtoul(opt + 6, 0, (unsigned long *)&pause); +#ifdef CONFIG_STMMAC_TIMER + else if (!strncmp(opt, "tmrate:", 7)) + strict_strtoul(opt + 7, 0, (unsigned long *)&tmrate); +#endif + } + return 0; +} + +__setup("stmmaceth=", stmmac_cmdline_opt); +#endif + +module_init(stmmac_init_module); +module_exit(stmmac_cleanup_module); + +MODULE_DESCRIPTION("STMMAC 10/100/1000 Ethernet driver"); +MODULE_AUTHOR("Giuseppe Cavallaro <peppe.cavallaro@st.com>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/net/stmmac/stmmac_mdio.c b/drivers/net/stmmac/stmmac_mdio.c new file mode 100644 index 000000000000..8498552a22fc --- /dev/null +++ b/drivers/net/stmmac/stmmac_mdio.c @@ -0,0 +1,217 @@ +/******************************************************************************* + STMMAC Ethernet Driver -- MDIO bus implementation + Provides Bus interface for MII registers + + Copyright (C) 2007-2009 STMicroelectronics Ltd + + This program is free software; you can redistribute it and/or modify it + under the terms and conditions of the GNU General Public License, + version 2, as published by the Free Software Foundation. + + This program is distributed in the hope it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. + + You should have received a copy of the GNU General Public License along with + this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + + The full GNU General Public License is included in this distribution in + the file called "COPYING". + + Author: Carl Shaw <carl.shaw@st.com> + Maintainer: Giuseppe Cavallaro <peppe.cavallaro@st.com> +*******************************************************************************/ + +#include <linux/netdevice.h> +#include <linux/mii.h> +#include <linux/phy.h> + +#include "stmmac.h" + +#define MII_BUSY 0x00000001 +#define MII_WRITE 0x00000002 + +/** + * stmmac_mdio_read + * @bus: points to the mii_bus structure + * @phyaddr: MII addr reg bits 15-11 + * @phyreg: MII addr reg bits 10-6 + * Description: it reads data from the MII register from within the phy device. + * For the 7111 GMAC, we must set the bit 0 in the MII address register while + * accessing the PHY registers. + * Fortunately, it seems this has no drawback for the 7109 MAC. + */ +static int stmmac_mdio_read(struct mii_bus *bus, int phyaddr, int phyreg) +{ + struct net_device *ndev = bus->priv; + struct stmmac_priv *priv = netdev_priv(ndev); + unsigned long ioaddr = ndev->base_addr; + unsigned int mii_address = priv->mac_type->hw.mii.addr; + unsigned int mii_data = priv->mac_type->hw.mii.data; + + int data; + u16 regValue = (((phyaddr << 11) & (0x0000F800)) | + ((phyreg << 6) & (0x000007C0))); + regValue |= MII_BUSY; /* in case of GMAC */ + + do {} while (((readl(ioaddr + mii_address)) & MII_BUSY) == 1); + writel(regValue, ioaddr + mii_address); + do {} while (((readl(ioaddr + mii_address)) & MII_BUSY) == 1); + + /* Read the data from the MII data register */ + data = (int)readl(ioaddr + mii_data); + + return data; +} + +/** + * stmmac_mdio_write + * @bus: points to the mii_bus structure + * @phyaddr: MII addr reg bits 15-11 + * @phyreg: MII addr reg bits 10-6 + * @phydata: phy data + * Description: it writes the data into the MII register from within the device. + */ +static int stmmac_mdio_write(struct mii_bus *bus, int phyaddr, int phyreg, + u16 phydata) +{ + struct net_device *ndev = bus->priv; + struct stmmac_priv *priv = netdev_priv(ndev); + unsigned long ioaddr = ndev->base_addr; + unsigned int mii_address = priv->mac_type->hw.mii.addr; + unsigned int mii_data = priv->mac_type->hw.mii.data; + + u16 value = + (((phyaddr << 11) & (0x0000F800)) | ((phyreg << 6) & (0x000007C0))) + | MII_WRITE; + + value |= MII_BUSY; + + /* Wait until any existing MII operation is complete */ + do {} while (((readl(ioaddr + mii_address)) & MII_BUSY) == 1); + + /* Set the MII address register to write */ + writel(phydata, ioaddr + mii_data); + writel(value, ioaddr + mii_address); + + /* Wait until any existing MII operation is complete */ + do {} while (((readl(ioaddr + mii_address)) & MII_BUSY) == 1); + + return 0; +} + +/** + * stmmac_mdio_reset + * @bus: points to the mii_bus structure + * Description: reset the MII bus + */ +static int stmmac_mdio_reset(struct mii_bus *bus) +{ + struct net_device *ndev = bus->priv; + struct stmmac_priv *priv = netdev_priv(ndev); + unsigned long ioaddr = ndev->base_addr; + unsigned int mii_address = priv->mac_type->hw.mii.addr; + + if (priv->phy_reset) { + pr_debug("stmmac_mdio_reset: calling phy_reset\n"); + priv->phy_reset(priv->bsp_priv); + } + + /* This is a workaround for problems with the STE101P PHY. + * It doesn't complete its reset until at least one clock cycle + * on MDC, so perform a dummy mdio read. + */ + writel(0, ioaddr + mii_address); + + return 0; +} + +/** + * stmmac_mdio_register + * @ndev: net device structure + * Description: it registers the MII bus + */ +int stmmac_mdio_register(struct net_device *ndev) +{ + int err = 0; + struct mii_bus *new_bus; + int *irqlist; + struct stmmac_priv *priv = netdev_priv(ndev); + int addr, found; + + new_bus = mdiobus_alloc(); + if (new_bus == NULL) + return -ENOMEM; + + irqlist = kzalloc(sizeof(int) * PHY_MAX_ADDR, GFP_KERNEL); + if (irqlist == NULL) { + err = -ENOMEM; + goto irqlist_alloc_fail; + } + + /* Assign IRQ to phy at address phy_addr */ + if (priv->phy_addr != -1) + irqlist[priv->phy_addr] = priv->phy_irq; + + new_bus->name = "STMMAC MII Bus"; + new_bus->read = &stmmac_mdio_read; + new_bus->write = &stmmac_mdio_write; + new_bus->reset = &stmmac_mdio_reset; + snprintf(new_bus->id, MII_BUS_ID_SIZE, "%x", priv->bus_id); + new_bus->priv = ndev; + new_bus->irq = irqlist; + new_bus->phy_mask = priv->phy_mask; + new_bus->parent = priv->device; + err = mdiobus_register(new_bus); + if (err != 0) { + pr_err("%s: Cannot register as MDIO bus\n", new_bus->name); + goto bus_register_fail; + } + + priv->mii = new_bus; + + found = 0; + for (addr = 0; addr < 32; addr++) { + struct phy_device *phydev = new_bus->phy_map[addr]; + if (phydev) { + if (priv->phy_addr == -1) { + priv->phy_addr = addr; + phydev->irq = priv->phy_irq; + irqlist[addr] = priv->phy_irq; + } + pr_info("%s: PHY ID %08x at %d IRQ %d (%s)%s\n", + ndev->name, phydev->phy_id, addr, + phydev->irq, dev_name(&phydev->dev), + (addr == priv->phy_addr) ? " active" : ""); + found = 1; + } + } + + if (!found) + pr_warning("%s: No PHY found\n", ndev->name); + + return 0; +bus_register_fail: + kfree(irqlist); +irqlist_alloc_fail: + kfree(new_bus); + return err; +} + +/** + * stmmac_mdio_unregister + * @ndev: net device structure + * Description: it unregisters the MII bus + */ +int stmmac_mdio_unregister(struct net_device *ndev) +{ + struct stmmac_priv *priv = netdev_priv(ndev); + + mdiobus_unregister(priv->mii); + priv->mii->priv = NULL; + kfree(priv->mii); + + return 0; +} diff --git a/drivers/net/stmmac/stmmac_timer.c b/drivers/net/stmmac/stmmac_timer.c new file mode 100644 index 000000000000..679f61ffb1f8 --- /dev/null +++ b/drivers/net/stmmac/stmmac_timer.c @@ -0,0 +1,140 @@ +/******************************************************************************* + STMMAC external timer support. + + Copyright (C) 2007-2009 STMicroelectronics Ltd + + This program is free software; you can redistribute it and/or modify it + under the terms and conditions of the GNU General Public License, + version 2, as published by the Free Software Foundation. + + This program is distributed in the hope it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. + + You should have received a copy of the GNU General Public License along with + this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + + The full GNU General Public License is included in this distribution in + the file called "COPYING". + + Author: Giuseppe Cavallaro <peppe.cavallaro@st.com> +*******************************************************************************/ + +#include <linux/kernel.h> +#include <linux/etherdevice.h> +#include "stmmac_timer.h" + +static void stmmac_timer_handler(void *data) +{ + struct net_device *dev = (struct net_device *)data; + + stmmac_schedule(dev); + + return; +} + +#define STMMAC_TIMER_MSG(timer, freq) \ +printk(KERN_INFO "stmmac_timer: %s Timer ON (freq %dHz)\n", timer, freq); + +#if defined(CONFIG_STMMAC_RTC_TIMER) +#include <linux/rtc.h> +static struct rtc_device *stmmac_rtc; +static rtc_task_t stmmac_task; + +static void stmmac_rtc_start(unsigned int new_freq) +{ + rtc_irq_set_freq(stmmac_rtc, &stmmac_task, new_freq); + rtc_irq_set_state(stmmac_rtc, &stmmac_task, 1); + return; +} + +static void stmmac_rtc_stop(void) +{ + rtc_irq_set_state(stmmac_rtc, &stmmac_task, 0); + return; +} + +int stmmac_open_ext_timer(struct net_device *dev, struct stmmac_timer *tm) +{ + stmmac_task.private_data = dev; + stmmac_task.func = stmmac_timer_handler; + + stmmac_rtc = rtc_class_open(CONFIG_RTC_HCTOSYS_DEVICE); + if (stmmac_rtc == NULL) { + pr_err("open rtc device failed\n"); + return -ENODEV; + } + + rtc_irq_register(stmmac_rtc, &stmmac_task); + + /* Periodic mode is not supported */ + if ((rtc_irq_set_freq(stmmac_rtc, &stmmac_task, tm->freq) < 0)) { + pr_err("set periodic failed\n"); + rtc_irq_unregister(stmmac_rtc, &stmmac_task); + rtc_class_close(stmmac_rtc); + return -1; + } + + STMMAC_TIMER_MSG(CONFIG_RTC_HCTOSYS_DEVICE, tm->freq); + + tm->timer_start = stmmac_rtc_start; + tm->timer_stop = stmmac_rtc_stop; + + return 0; +} + +int stmmac_close_ext_timer(void) +{ + rtc_irq_set_state(stmmac_rtc, &stmmac_task, 0); + rtc_irq_unregister(stmmac_rtc, &stmmac_task); + rtc_class_close(stmmac_rtc); + return 0; +} + +#elif defined(CONFIG_STMMAC_TMU_TIMER) +#include <linux/clk.h> +#define TMU_CHANNEL "tmu2_clk" +static struct clk *timer_clock; + +static void stmmac_tmu_start(unsigned int new_freq) +{ + clk_set_rate(timer_clock, new_freq); + clk_enable(timer_clock); + return; +} + +static void stmmac_tmu_stop(void) +{ + clk_disable(timer_clock); + return; +} + +int stmmac_open_ext_timer(struct net_device *dev, struct stmmac_timer *tm) +{ + timer_clock = clk_get(NULL, TMU_CHANNEL); + + if (timer_clock == NULL) + return -1; + + if (tmu2_register_user(stmmac_timer_handler, (void *)dev) < 0) { + timer_clock = NULL; + return -1; + } + + STMMAC_TIMER_MSG("TMU2", tm->freq); + tm->timer_start = stmmac_tmu_start; + tm->timer_stop = stmmac_tmu_stop; + + return 0; +} + +int stmmac_close_ext_timer(void) +{ + clk_disable(timer_clock); + tmu2_unregister_user(); + clk_put(timer_clock); + return 0; +} +#endif diff --git a/drivers/net/stmmac/stmmac_timer.h b/drivers/net/stmmac/stmmac_timer.h new file mode 100644 index 000000000000..6863590d184b --- /dev/null +++ b/drivers/net/stmmac/stmmac_timer.h @@ -0,0 +1,42 @@ +/******************************************************************************* + STMMAC external timer Header File. + + Copyright (C) 2007-2009 STMicroelectronics Ltd + + This program is free software; you can redistribute it and/or modify it + under the terms and conditions of the GNU General Public License, + version 2, as published by the Free Software Foundation. + + This program is distributed in the hope it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. + + You should have received a copy of the GNU General Public License along with + this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + + The full GNU General Public License is included in this distribution in + the file called "COPYING". + + Author: Giuseppe Cavallaro <peppe.cavallaro@st.com> +*******************************************************************************/ + +struct stmmac_timer { + void (*timer_start) (unsigned int new_freq); + void (*timer_stop) (void); + unsigned int freq; + unsigned int enable; +}; + +/* Open the HW timer device and return 0 in case of success */ +int stmmac_open_ext_timer(struct net_device *dev, struct stmmac_timer *tm); +/* Stop the timer and release it */ +int stmmac_close_ext_timer(void); +/* Function used for scheduling task within the stmmac */ +void stmmac_schedule(struct net_device *dev); + +#if defined(CONFIG_STMMAC_TMU_TIMER) +extern int tmu2_register_user(void *fnt, void *data); +extern void tmu2_unregister_user(void); +#endif diff --git a/drivers/net/sungem.c b/drivers/net/sungem.c index 305ec3d783db..61640b99b705 100644 --- a/drivers/net/sungem.c +++ b/drivers/net/sungem.c @@ -38,6 +38,7 @@ #include <linux/interrupt.h> #include <linux/ioport.h> #include <linux/in.h> +#include <linux/sched.h> #include <linux/slab.h> #include <linux/string.h> #include <linux/delay.h> @@ -2062,7 +2063,15 @@ static int gem_check_invariants(struct gem *gp) mif_cfg &= ~MIF_CFG_PSELECT; writel(mif_cfg, gp->regs + MIF_CFG); } else { - gp->phy_type = phy_serialink; +#ifdef CONFIG_SPARC + const char *p; + + p = of_get_property(gp->of_node, "shared-pins", NULL); + if (p && !strcmp(p, "serdes")) + gp->phy_type = phy_serdes; + else +#endif + gp->phy_type = phy_serialink; } if (gp->phy_type == phy_mii_mdio1 || gp->phy_type == phy_mii_mdio0) { diff --git a/drivers/net/sunvnet.c b/drivers/net/sunvnet.c index f1e5e4542c2a..bc74db0d12f3 100644 --- a/drivers/net/sunvnet.c +++ b/drivers/net/sunvnet.c @@ -1016,7 +1016,6 @@ static const struct net_device_ops vnet_ops = { .ndo_open = vnet_open, .ndo_stop = vnet_close, .ndo_set_multicast_list = vnet_set_rx_mode, - .ndo_change_mtu = eth_change_mtu, .ndo_set_mac_address = vnet_set_mac_addr, .ndo_validate_addr = eth_validate_addr, .ndo_tx_timeout = vnet_tx_timeout, diff --git a/drivers/net/tg3.c b/drivers/net/tg3.c index f09bc5dfe8b2..ba5d3fe753b6 100644 --- a/drivers/net/tg3.c +++ b/drivers/net/tg3.c @@ -902,11 +902,12 @@ static int tg3_mdio_read(struct mii_bus *bp, int mii_id, int reg) struct tg3 *tp = bp->priv; u32 val; - if (tp->tg3_flags3 & TG3_FLG3_MDIOBUS_PAUSED) - return -EAGAIN; + spin_lock_bh(&tp->lock); if (tg3_readphy(tp, reg, &val)) - return -EIO; + val = -EIO; + + spin_unlock_bh(&tp->lock); return val; } @@ -914,14 +915,16 @@ static int tg3_mdio_read(struct mii_bus *bp, int mii_id, int reg) static int tg3_mdio_write(struct mii_bus *bp, int mii_id, int reg, u16 val) { struct tg3 *tp = bp->priv; + u32 ret = 0; - if (tp->tg3_flags3 & TG3_FLG3_MDIOBUS_PAUSED) - return -EAGAIN; + spin_lock_bh(&tp->lock); if (tg3_writephy(tp, reg, val)) - return -EIO; + ret = -EIO; - return 0; + spin_unlock_bh(&tp->lock); + + return ret; } static int tg3_mdio_reset(struct mii_bus *bp) @@ -1011,12 +1014,6 @@ static void tg3_mdio_config_5785(struct tg3 *tp) static void tg3_mdio_start(struct tg3 *tp) { - if (tp->tg3_flags3 & TG3_FLG3_MDIOBUS_INITED) { - mutex_lock(&tp->mdio_bus->mdio_lock); - tp->tg3_flags3 &= ~TG3_FLG3_MDIOBUS_PAUSED; - mutex_unlock(&tp->mdio_bus->mdio_lock); - } - tp->mi_mode &= ~MAC_MI_MODE_AUTO_POLL; tw32_f(MAC_MI_MODE, tp->mi_mode); udelay(80); @@ -1041,15 +1038,6 @@ static void tg3_mdio_start(struct tg3 *tp) tg3_mdio_config_5785(tp); } -static void tg3_mdio_stop(struct tg3 *tp) -{ - if (tp->tg3_flags3 & TG3_FLG3_MDIOBUS_INITED) { - mutex_lock(&tp->mdio_bus->mdio_lock); - tp->tg3_flags3 |= TG3_FLG3_MDIOBUS_PAUSED; - mutex_unlock(&tp->mdio_bus->mdio_lock); - } -} - static int tg3_mdio_init(struct tg3 *tp) { int i; @@ -1141,7 +1129,6 @@ static void tg3_mdio_fini(struct tg3 *tp) tp->tg3_flags3 &= ~TG3_FLG3_MDIOBUS_INITED; mdiobus_unregister(tp->mdio_bus); mdiobus_free(tp->mdio_bus); - tp->tg3_flags3 &= ~TG3_FLG3_MDIOBUS_PAUSED; } } @@ -1363,7 +1350,7 @@ static void tg3_adjust_link(struct net_device *dev) struct tg3 *tp = netdev_priv(dev); struct phy_device *phydev = tp->mdio_bus->phy_map[PHY_ADDR]; - spin_lock(&tp->lock); + spin_lock_bh(&tp->lock); mac_mode = tp->mac_mode & ~(MAC_MODE_PORT_MODE_MASK | MAC_MODE_HALF_DUPLEX); @@ -1431,7 +1418,7 @@ static void tg3_adjust_link(struct net_device *dev) tp->link_config.active_speed = phydev->speed; tp->link_config.active_duplex = phydev->duplex; - spin_unlock(&tp->lock); + spin_unlock_bh(&tp->lock); if (linkmesg) tg3_link_report(tp); @@ -6392,8 +6379,6 @@ static int tg3_chip_reset(struct tg3 *tp) tg3_nvram_lock(tp); - tg3_mdio_stop(tp); - tg3_ape_lock(tp, TG3_APE_LOCK_GRC); /* No matching tg3_nvram_unlock() after this because @@ -8698,6 +8683,8 @@ static int tg3_close(struct net_device *dev) del_timer_sync(&tp->timer); + tg3_phy_stop(tp); + tg3_full_lock(tp, 1); #if 0 tg3_dump_state(tp); diff --git a/drivers/net/tg3.h b/drivers/net/tg3.h index 82b45d8797b4..bab7940158e6 100644 --- a/drivers/net/tg3.h +++ b/drivers/net/tg3.h @@ -2412,7 +2412,6 @@ struct ring_info { struct tx_ring_info { struct sk_buff *skb; - u32 prev_vlan_tag; }; struct tg3_config_info { @@ -2749,7 +2748,6 @@ struct tg3 { #define TG3_FLG3_5701_DMA_BUG 0x00000008 #define TG3_FLG3_USE_PHYLIB 0x00000010 #define TG3_FLG3_MDIOBUS_INITED 0x00000020 -#define TG3_FLG3_MDIOBUS_PAUSED 0x00000040 #define TG3_FLG3_PHY_CONNECTED 0x00000080 #define TG3_FLG3_RGMII_STD_IBND_DISABLE 0x00000100 #define TG3_FLG3_RGMII_EXT_IBND_RX_EN 0x00000200 diff --git a/drivers/net/tokenring/ibmtr.c b/drivers/net/tokenring/ibmtr.c index 525bbc5b9c9d..75fa32e34fd0 100644 --- a/drivers/net/tokenring/ibmtr.c +++ b/drivers/net/tokenring/ibmtr.c @@ -108,6 +108,7 @@ in the event that chatty debug messages are desired - jjs 12/30/98 */ #define IBMTR_DEBUG_MESSAGES 0 #include <linux/module.h> +#include <linux/sched.h> #ifdef PCMCIA /* required for ibmtr_cs.c to build */ #undef MODULE /* yes, really */ @@ -1143,9 +1144,16 @@ static void dir_open_adapter (struct net_device *dev) } else { char **prphase = printphase; char **prerror = printerror; + int pnr = err / 16 - 1; + int enr = err % 16 - 1; DPRINTK("TR Adapter misc open failure, error code = "); - printk("0x%x, Phase: %s, Error: %s\n", - err, prphase[err/16 -1], prerror[err%16 -1]); + if (pnr < 0 || pnr >= ARRAY_SIZE(printphase) || + enr < 0 || + enr >= ARRAY_SIZE(printerror)) + printk("0x%x, invalid Phase/Error.", err); + else + printk("0x%x, Phase: %s, Error: %s\n", err, + prphase[pnr], prerror[enr]); printk(" retrying after %ds delay...\n", TR_RETRY_INTERVAL/HZ); } diff --git a/drivers/net/tun.c b/drivers/net/tun.c index d3ee1994b02f..4fdfa2ae5418 100644 --- a/drivers/net/tun.c +++ b/drivers/net/tun.c @@ -946,8 +946,6 @@ static int tun_set_iff(struct net *net, struct file *file, struct ifreq *ifr) char *name; unsigned long flags = 0; - err = -EINVAL; - if (!capable(CAP_NET_ADMIN)) return -EPERM; err = security_tun_dev_create(); @@ -964,7 +962,7 @@ static int tun_set_iff(struct net *net, struct file *file, struct ifreq *ifr) flags |= TUN_TAP_DEV; name = "tap%d"; } else - goto failed; + return -EINVAL; if (*ifr->ifr_name) name = ifr->ifr_name; diff --git a/drivers/net/typhoon.c b/drivers/net/typhoon.c index d6d345229fe9..5921f5bdd764 100644 --- a/drivers/net/typhoon.c +++ b/drivers/net/typhoon.c @@ -108,6 +108,7 @@ static const int multicast_filter_limit = 32; #include <linux/module.h> #include <linux/kernel.h> +#include <linux/sched.h> #include <linux/string.h> #include <linux/timer.h> #include <linux/errno.h> diff --git a/drivers/net/usb/Kconfig b/drivers/net/usb/Kconfig index c47237c2d638..32d93564a74d 100644 --- a/drivers/net/usb/Kconfig +++ b/drivers/net/usb/Kconfig @@ -174,7 +174,7 @@ config USB_NET_CDCETHER * Ericsson Mobile Broadband Module (all variants) * Motorola (DM100 and SB4100) * Broadcom Cable Modem (reference design) - * Toshiba (PCX1100U and F3507g) + * Toshiba (PCX1100U and F3507g/F3607gw) * ... This driver creates an interface named "ethX", where X depends on diff --git a/drivers/net/usb/cdc_eem.c b/drivers/net/usb/cdc_eem.c index 45cebfb302cf..23300656c266 100644 --- a/drivers/net/usb/cdc_eem.c +++ b/drivers/net/usb/cdc_eem.c @@ -300,20 +300,23 @@ static int eem_rx_fixup(struct usbnet *dev, struct sk_buff *skb) return 0; } - crc = get_unaligned_le32(skb2->data - + len - ETH_FCS_LEN); - skb_trim(skb2, len - ETH_FCS_LEN); - /* * The bmCRC helps to denote when the CRC field in * the Ethernet frame contains a calculated CRC: * bmCRC = 1 : CRC is calculated * bmCRC = 0 : CRC = 0xDEADBEEF */ - if (header & BIT(14)) - crc2 = ~crc32_le(~0, skb2->data, skb2->len); - else + if (header & BIT(14)) { + crc = get_unaligned_le32(skb2->data + + len - ETH_FCS_LEN); + crc2 = ~crc32_le(~0, skb2->data, skb2->len + - ETH_FCS_LEN); + } else { + crc = get_unaligned_be32(skb2->data + + len - ETH_FCS_LEN); crc2 = 0xdeadbeef; + } + skb_trim(skb2, len - ETH_FCS_LEN); if (is_last) return crc == crc2; diff --git a/drivers/net/usb/cdc_ether.c b/drivers/net/usb/cdc_ether.c index 4a6aff579403..21e1ba160008 100644 --- a/drivers/net/usb/cdc_ether.c +++ b/drivers/net/usb/cdc_ether.c @@ -544,20 +544,60 @@ static const struct usb_device_id products [] = { USB_CDC_SUBCLASS_MDLM, USB_CDC_PROTO_NONE), .driver_info = (unsigned long) &cdc_info, }, { - /* Ericsson F3307 */ + /* Ericsson F3607gw ver 2 */ + USB_DEVICE_AND_INTERFACE_INFO(0x0bdb, 0x1905, USB_CLASS_COMM, + USB_CDC_SUBCLASS_MDLM, USB_CDC_PROTO_NONE), + .driver_info = (unsigned long) &cdc_info, +}, { + /* Ericsson F3607gw ver 3 */ USB_DEVICE_AND_INTERFACE_INFO(0x0bdb, 0x1906, USB_CLASS_COMM, USB_CDC_SUBCLASS_MDLM, USB_CDC_PROTO_NONE), .driver_info = (unsigned long) &cdc_info, }, { + /* Ericsson F3307 */ + USB_DEVICE_AND_INTERFACE_INFO(0x0bdb, 0x190a, USB_CLASS_COMM, + USB_CDC_SUBCLASS_MDLM, USB_CDC_PROTO_NONE), + .driver_info = (unsigned long) &cdc_info, +}, { + /* Ericsson F3307 ver 2 */ + USB_DEVICE_AND_INTERFACE_INFO(0x0bdb, 0x1909, USB_CLASS_COMM, + USB_CDC_SUBCLASS_MDLM, USB_CDC_PROTO_NONE), + .driver_info = (unsigned long) &cdc_info, +}, { + /* Ericsson C3607w */ + USB_DEVICE_AND_INTERFACE_INFO(0x0bdb, 0x1049, USB_CLASS_COMM, + USB_CDC_SUBCLASS_MDLM, USB_CDC_PROTO_NONE), + .driver_info = (unsigned long) &cdc_info, +}, { /* Toshiba F3507g */ USB_DEVICE_AND_INTERFACE_INFO(0x0930, 0x130b, USB_CLASS_COMM, USB_CDC_SUBCLASS_MDLM, USB_CDC_PROTO_NONE), .driver_info = (unsigned long) &cdc_info, }, { + /* Toshiba F3607gw */ + USB_DEVICE_AND_INTERFACE_INFO(0x0930, 0x130c, USB_CLASS_COMM, + USB_CDC_SUBCLASS_MDLM, USB_CDC_PROTO_NONE), + .driver_info = (unsigned long) &cdc_info, +}, { + /* Toshiba F3607gw ver 2 */ + USB_DEVICE_AND_INTERFACE_INFO(0x0930, 0x1311, USB_CLASS_COMM, + USB_CDC_SUBCLASS_MDLM, USB_CDC_PROTO_NONE), + .driver_info = (unsigned long) &cdc_info, +}, { /* Dell F3507g */ USB_DEVICE_AND_INTERFACE_INFO(0x413c, 0x8147, USB_CLASS_COMM, USB_CDC_SUBCLASS_MDLM, USB_CDC_PROTO_NONE), .driver_info = (unsigned long) &cdc_info, +}, { + /* Dell F3607gw */ + USB_DEVICE_AND_INTERFACE_INFO(0x413c, 0x8183, USB_CLASS_COMM, + USB_CDC_SUBCLASS_MDLM, USB_CDC_PROTO_NONE), + .driver_info = (unsigned long) &cdc_info, +}, { + /* Dell F3607gw ver 2 */ + USB_DEVICE_AND_INTERFACE_INFO(0x413c, 0x8184, USB_CLASS_COMM, + USB_CDC_SUBCLASS_MDLM, USB_CDC_PROTO_NONE), + .driver_info = (unsigned long) &cdc_info, }, { }, // END }; diff --git a/drivers/net/usb/dm9601.c b/drivers/net/usb/dm9601.c index 72470f77f556..a2b30a10064f 100644 --- a/drivers/net/usb/dm9601.c +++ b/drivers/net/usb/dm9601.c @@ -649,6 +649,10 @@ static const struct usb_device_id products[] = { USB_DEVICE(0x0fe6, 0x8101), /* DM9601 USB to Fast Ethernet Adapter */ .driver_info = (unsigned long)&dm9601_info, }, + { + USB_DEVICE(0x0a46, 0x9000), /* DM9000E */ + .driver_info = (unsigned long)&dm9601_info, + }, {}, // END }; diff --git a/drivers/net/usb/hso.c b/drivers/net/usb/hso.c index fa4e58196c21..43bc3fcc0d85 100644 --- a/drivers/net/usb/hso.c +++ b/drivers/net/usb/hso.c @@ -378,7 +378,7 @@ static void dbg_dump(int line_count, const char *func_name, unsigned char *buf, } #define DUMP(buf_, len_) \ - dbg_dump(__LINE__, __func__, buf_, len_) + dbg_dump(__LINE__, __func__, (unsigned char *)buf_, len_) #define DUMP1(buf_, len_) \ do { \ @@ -1363,7 +1363,7 @@ static void hso_serial_close(struct tty_struct *tty, struct file *filp) /* reset the rts and dtr */ /* do the actual close */ serial->open_count--; - kref_put(&serial->parent->ref, hso_serial_ref_free); + if (serial->open_count <= 0) { serial->open_count = 0; spin_lock_irq(&serial->serial_lock); @@ -1383,6 +1383,8 @@ static void hso_serial_close(struct tty_struct *tty, struct file *filp) usb_autopm_put_interface(serial->parent->interface); mutex_unlock(&serial->parent->mutex); + + kref_put(&serial->parent->ref, hso_serial_ref_free); } /* close the requested serial port */ @@ -1527,7 +1529,7 @@ static void tiocmget_intr_callback(struct urb *urb) dev_warn(&usb->dev, "hso received invalid serial state notification\n"); DUMP(serial_state_notification, - sizeof(hso_serial_state_notifation)) + sizeof(struct hso_serial_state_notification)); } else { UART_state_bitmap = le16_to_cpu(serial_state_notification-> diff --git a/drivers/net/usb/kaweth.c b/drivers/net/usb/kaweth.c index e2a39b9be96e..e391ef969c28 100644 --- a/drivers/net/usb/kaweth.c +++ b/drivers/net/usb/kaweth.c @@ -263,6 +263,7 @@ static int kaweth_control(struct kaweth_device *kaweth, int timeout) { struct usb_ctrlrequest *dr; + int retval; dbg("kaweth_control()"); @@ -278,18 +279,21 @@ static int kaweth_control(struct kaweth_device *kaweth, return -ENOMEM; } - dr->bRequestType= requesttype; + dr->bRequestType = requesttype; dr->bRequest = request; dr->wValue = cpu_to_le16(value); dr->wIndex = cpu_to_le16(index); dr->wLength = cpu_to_le16(size); - return kaweth_internal_control_msg(kaweth->dev, - pipe, - dr, - data, - size, - timeout); + retval = kaweth_internal_control_msg(kaweth->dev, + pipe, + dr, + data, + size, + timeout); + + kfree(dr); + return retval; } /**************************************************************** diff --git a/drivers/net/usb/pegasus.c b/drivers/net/usb/pegasus.c index 6fdaba8674b9..ed4a508ef262 100644 --- a/drivers/net/usb/pegasus.c +++ b/drivers/net/usb/pegasus.c @@ -62,8 +62,11 @@ static char *devid=NULL; static struct usb_eth_dev usb_dev_id[] = { #define PEGASUS_DEV(pn, vid, pid, flags) \ {.name = pn, .vendor = vid, .device = pid, .private = flags}, +#define PEGASUS_DEV_CLASS(pn, vid, pid, dclass, flags) \ + PEGASUS_DEV(pn, vid, pid, flags) #include "pegasus.h" #undef PEGASUS_DEV +#undef PEGASUS_DEV_CLASS {NULL, 0, 0, 0}, {NULL, 0, 0, 0} }; @@ -71,8 +74,18 @@ static struct usb_eth_dev usb_dev_id[] = { static struct usb_device_id pegasus_ids[] = { #define PEGASUS_DEV(pn, vid, pid, flags) \ {.match_flags = USB_DEVICE_ID_MATCH_DEVICE, .idVendor = vid, .idProduct = pid}, +/* + * The Belkin F8T012xx1 bluetooth adaptor has the same vendor and product + * IDs as the Belkin F5D5050, so we need to teach the pegasus driver to + * ignore adaptors belonging to the "Wireless" class 0xE0. For this one + * case anyway, seeing as the pegasus is for "Wired" adaptors. + */ +#define PEGASUS_DEV_CLASS(pn, vid, pid, dclass, flags) \ + {.match_flags = (USB_DEVICE_ID_MATCH_DEVICE | USB_DEVICE_ID_MATCH_DEV_CLASS), \ + .idVendor = vid, .idProduct = pid, .bDeviceClass = dclass}, #include "pegasus.h" #undef PEGASUS_DEV +#undef PEGASUS_DEV_CLASS {}, {} }; diff --git a/drivers/net/usb/pegasus.h b/drivers/net/usb/pegasus.h index f968c834ff63..5d02f0200737 100644 --- a/drivers/net/usb/pegasus.h +++ b/drivers/net/usb/pegasus.h @@ -202,7 +202,11 @@ PEGASUS_DEV( "AEI USB Fast Ethernet Adapter", VENDOR_AEILAB, 0x1701, DEFAULT_GPIO_RESET | PEGASUS_II ) PEGASUS_DEV( "Allied Telesyn Int. AT-USB100", VENDOR_ALLIEDTEL, 0xb100, DEFAULT_GPIO_RESET | PEGASUS_II ) -PEGASUS_DEV( "Belkin F5D5050 USB Ethernet", VENDOR_BELKIN, 0x0121, +/* + * Distinguish between this Belkin adaptor and the Belkin bluetooth adaptors + * with the same product IDs by checking the device class too. + */ +PEGASUS_DEV_CLASS( "Belkin F5D5050 USB Ethernet", VENDOR_BELKIN, 0x0121, 0x00, DEFAULT_GPIO_RESET | PEGASUS_II ) PEGASUS_DEV( "Billionton USB-100", VENDOR_BILLIONTON, 0x0986, DEFAULT_GPIO_RESET ) diff --git a/drivers/net/usb/rndis_host.c b/drivers/net/usb/rndis_host.c index d032bba9bc4c..f56dec6119c3 100644 --- a/drivers/net/usb/rndis_host.c +++ b/drivers/net/usb/rndis_host.c @@ -362,12 +362,12 @@ generic_rndis_bind(struct usbnet *dev, struct usb_interface *intf, int flags) retval = -EINVAL; goto halt_fail_and_release; } - dev->hard_mtu = tmp; - net->mtu = dev->hard_mtu - net->hard_header_len; dev_warn(&intf->dev, "dev can't take %u byte packets (max %u), " "adjusting MTU to %u\n", - dev->hard_mtu, tmp, net->mtu); + dev->hard_mtu, tmp, tmp - net->hard_header_len); + dev->hard_mtu = tmp; + net->mtu = dev->hard_mtu - net->hard_header_len; } /* REVISIT: peripheral "alignment" request is ignored ... */ @@ -418,6 +418,7 @@ generic_rndis_bind(struct usbnet *dev, struct usb_interface *intf, int flags) goto halt_fail_and_release; } memcpy(net->dev_addr, bp, ETH_ALEN); + memcpy(net->perm_addr, bp, ETH_ALEN); /* set a nonzero filter to enable data transfers */ memset(u.set, 0, sizeof *u.set); diff --git a/drivers/net/usb/smsc95xx.c b/drivers/net/usb/smsc95xx.c index 938fb3530a7a..c6c922247d05 100644 --- a/drivers/net/usb/smsc95xx.c +++ b/drivers/net/usb/smsc95xx.c @@ -1227,7 +1227,7 @@ static const struct driver_info smsc95xx_info = { .rx_fixup = smsc95xx_rx_fixup, .tx_fixup = smsc95xx_tx_fixup, .status = smsc95xx_status, - .flags = FLAG_ETHER, + .flags = FLAG_ETHER | FLAG_SEND_ZLP, }; static const struct usb_device_id products[] = { @@ -1237,10 +1237,75 @@ static const struct usb_device_id products[] = { .driver_info = (unsigned long) &smsc95xx_info, }, { + /* SMSC9505 USB Ethernet Device */ + USB_DEVICE(0x0424, 0x9505), + .driver_info = (unsigned long) &smsc95xx_info, + }, + { + /* SMSC9500A USB Ethernet Device */ + USB_DEVICE(0x0424, 0x9E00), + .driver_info = (unsigned long) &smsc95xx_info, + }, + { + /* SMSC9505A USB Ethernet Device */ + USB_DEVICE(0x0424, 0x9E01), + .driver_info = (unsigned long) &smsc95xx_info, + }, + { /* SMSC9512/9514 USB Hub & Ethernet Device */ USB_DEVICE(0x0424, 0xec00), .driver_info = (unsigned long) &smsc95xx_info, }, + { + /* SMSC9500 USB Ethernet Device (SAL10) */ + USB_DEVICE(0x0424, 0x9900), + .driver_info = (unsigned long) &smsc95xx_info, + }, + { + /* SMSC9505 USB Ethernet Device (SAL10) */ + USB_DEVICE(0x0424, 0x9901), + .driver_info = (unsigned long) &smsc95xx_info, + }, + { + /* SMSC9500A USB Ethernet Device (SAL10) */ + USB_DEVICE(0x0424, 0x9902), + .driver_info = (unsigned long) &smsc95xx_info, + }, + { + /* SMSC9505A USB Ethernet Device (SAL10) */ + USB_DEVICE(0x0424, 0x9903), + .driver_info = (unsigned long) &smsc95xx_info, + }, + { + /* SMSC9512/9514 USB Hub & Ethernet Device (SAL10) */ + USB_DEVICE(0x0424, 0x9904), + .driver_info = (unsigned long) &smsc95xx_info, + }, + { + /* SMSC9500A USB Ethernet Device (HAL) */ + USB_DEVICE(0x0424, 0x9905), + .driver_info = (unsigned long) &smsc95xx_info, + }, + { + /* SMSC9505A USB Ethernet Device (HAL) */ + USB_DEVICE(0x0424, 0x9906), + .driver_info = (unsigned long) &smsc95xx_info, + }, + { + /* SMSC9500 USB Ethernet Device (Alternate ID) */ + USB_DEVICE(0x0424, 0x9907), + .driver_info = (unsigned long) &smsc95xx_info, + }, + { + /* SMSC9500A USB Ethernet Device (Alternate ID) */ + USB_DEVICE(0x0424, 0x9908), + .driver_info = (unsigned long) &smsc95xx_info, + }, + { + /* SMSC9512/9514 USB Hub & Ethernet Device (Alternate ID) */ + USB_DEVICE(0x0424, 0x9909), + .driver_info = (unsigned long) &smsc95xx_info, + }, { }, /* END */ }; MODULE_DEVICE_TABLE(usb, products); diff --git a/drivers/net/usb/usbnet.c b/drivers/net/usb/usbnet.c index 24b36f795151..ca5ca5ae061d 100644 --- a/drivers/net/usb/usbnet.c +++ b/drivers/net/usb/usbnet.c @@ -1049,7 +1049,7 @@ netdev_tx_t usbnet_start_xmit (struct sk_buff *skb, * NOTE: strictly conforming cdc-ether devices should expect * the ZLP here, but ignore the one-byte packet. */ - if ((length % dev->maxpacket) == 0) { + if (!(info->flags & FLAG_SEND_ZLP) && (length % dev->maxpacket) == 0) { urb->transfer_buffer_length++; if (skb_tailroom(skb)) { skb->data[skb->len] = 0; diff --git a/drivers/net/veth.c b/drivers/net/veth.c index ade5b344f75d..52af5017c46b 100644 --- a/drivers/net/veth.c +++ b/drivers/net/veth.c @@ -210,32 +210,29 @@ rx_drop: static struct net_device_stats *veth_get_stats(struct net_device *dev) { struct veth_priv *priv; - struct net_device_stats *dev_stats; int cpu; - struct veth_net_stats *stats; + struct veth_net_stats *stats, total = {0}; priv = netdev_priv(dev); - dev_stats = &dev->stats; - - dev_stats->rx_packets = 0; - dev_stats->tx_packets = 0; - dev_stats->rx_bytes = 0; - dev_stats->tx_bytes = 0; - dev_stats->tx_dropped = 0; - dev_stats->rx_dropped = 0; - for_each_online_cpu(cpu) { + for_each_possible_cpu(cpu) { stats = per_cpu_ptr(priv->stats, cpu); - dev_stats->rx_packets += stats->rx_packets; - dev_stats->tx_packets += stats->tx_packets; - dev_stats->rx_bytes += stats->rx_bytes; - dev_stats->tx_bytes += stats->tx_bytes; - dev_stats->tx_dropped += stats->tx_dropped; - dev_stats->rx_dropped += stats->rx_dropped; + total.rx_packets += stats->rx_packets; + total.tx_packets += stats->tx_packets; + total.rx_bytes += stats->rx_bytes; + total.tx_bytes += stats->tx_bytes; + total.tx_dropped += stats->tx_dropped; + total.rx_dropped += stats->rx_dropped; } - - return dev_stats; + dev->stats.rx_packets = total.rx_packets; + dev->stats.tx_packets = total.tx_packets; + dev->stats.rx_bytes = total.rx_bytes; + dev->stats.tx_bytes = total.tx_bytes; + dev->stats.tx_dropped = total.tx_dropped; + dev->stats.rx_dropped = total.rx_dropped; + + return &dev->stats; } static int veth_open(struct net_device *dev) diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c index 32266fb89c20..b9e002fccbca 100644 --- a/drivers/net/virtio_net.c +++ b/drivers/net/virtio_net.c @@ -1,4 +1,4 @@ -/* A simple network driver using virtio. +/* A network driver using virtio. * * Copyright 2007 Rusty Russell <rusty@rustcorp.com.au> IBM Corporation * @@ -47,19 +47,9 @@ struct virtnet_info struct napi_struct napi; unsigned int status; - /* The skb we couldn't send because buffers were full. */ - struct sk_buff *last_xmit_skb; - - /* If we need to free in a timer, this is it. */ - struct timer_list xmit_free_timer; - /* Number of input buffers, and max we've ever had. */ unsigned int num, max; - /* For cleaning up after transmission. */ - struct tasklet_struct tasklet; - bool free_in_tasklet; - /* I like... big packets and I cannot lie! */ bool big_packets; @@ -77,9 +67,17 @@ struct virtnet_info struct page *pages; }; -static inline void *skb_vnet_hdr(struct sk_buff *skb) +struct skb_vnet_hdr { + union { + struct virtio_net_hdr hdr; + struct virtio_net_hdr_mrg_rxbuf mhdr; + }; + unsigned int num_sg; +}; + +static inline struct skb_vnet_hdr *skb_vnet_hdr(struct sk_buff *skb) { - return (struct virtio_net_hdr *)skb->cb; + return (struct skb_vnet_hdr *)skb->cb; } static void give_a_page(struct virtnet_info *vi, struct page *page) @@ -118,17 +116,13 @@ static void skb_xmit_done(struct virtqueue *svq) /* We were probably waiting for more output buffers. */ netif_wake_queue(vi->dev); - - /* Make sure we re-xmit last_xmit_skb: if there are no more packets - * queued, start_xmit won't be called. */ - tasklet_schedule(&vi->tasklet); } static void receive_skb(struct net_device *dev, struct sk_buff *skb, unsigned len) { struct virtnet_info *vi = netdev_priv(dev); - struct virtio_net_hdr *hdr = skb_vnet_hdr(skb); + struct skb_vnet_hdr *hdr = skb_vnet_hdr(skb); int err; int i; @@ -139,7 +133,6 @@ static void receive_skb(struct net_device *dev, struct sk_buff *skb, } if (vi->mergeable_rx_bufs) { - struct virtio_net_hdr_mrg_rxbuf *mhdr = skb_vnet_hdr(skb); unsigned int copy; char *p = page_address(skb_shinfo(skb)->frags[0].page); @@ -147,8 +140,8 @@ static void receive_skb(struct net_device *dev, struct sk_buff *skb, len = PAGE_SIZE; len -= sizeof(struct virtio_net_hdr_mrg_rxbuf); - memcpy(hdr, p, sizeof(*mhdr)); - p += sizeof(*mhdr); + memcpy(&hdr->mhdr, p, sizeof(hdr->mhdr)); + p += sizeof(hdr->mhdr); copy = len; if (copy > skb_tailroom(skb)) @@ -163,13 +156,13 @@ static void receive_skb(struct net_device *dev, struct sk_buff *skb, skb_shinfo(skb)->nr_frags--; } else { skb_shinfo(skb)->frags[0].page_offset += - sizeof(*mhdr) + copy; + sizeof(hdr->mhdr) + copy; skb_shinfo(skb)->frags[0].size = len; skb->data_len += len; skb->len += len; } - while (--mhdr->num_buffers) { + while (--hdr->mhdr.num_buffers) { struct sk_buff *nskb; i = skb_shinfo(skb)->nr_frags; @@ -183,7 +176,7 @@ static void receive_skb(struct net_device *dev, struct sk_buff *skb, nskb = vi->rvq->vq_ops->get_buf(vi->rvq, &len); if (!nskb) { pr_debug("%s: rx error: %d buffers missing\n", - dev->name, mhdr->num_buffers); + dev->name, hdr->mhdr.num_buffers); dev->stats.rx_length_errors++; goto drop; } @@ -204,7 +197,7 @@ static void receive_skb(struct net_device *dev, struct sk_buff *skb, skb->len += len; } } else { - len -= sizeof(struct virtio_net_hdr); + len -= sizeof(hdr->hdr); if (len <= MAX_PACKET_LEN) trim_pages(vi, skb); @@ -222,9 +215,11 @@ static void receive_skb(struct net_device *dev, struct sk_buff *skb, dev->stats.rx_bytes += skb->len; dev->stats.rx_packets++; - if (hdr->flags & VIRTIO_NET_HDR_F_NEEDS_CSUM) { + if (hdr->hdr.flags & VIRTIO_NET_HDR_F_NEEDS_CSUM) { pr_debug("Needs csum!\n"); - if (!skb_partial_csum_set(skb,hdr->csum_start,hdr->csum_offset)) + if (!skb_partial_csum_set(skb, + hdr->hdr.csum_start, + hdr->hdr.csum_offset)) goto frame_err; } @@ -232,9 +227,9 @@ static void receive_skb(struct net_device *dev, struct sk_buff *skb, pr_debug("Receiving skb proto 0x%04x len %i type %i\n", ntohs(skb->protocol), skb->len, skb->pkt_type); - if (hdr->gso_type != VIRTIO_NET_HDR_GSO_NONE) { + if (hdr->hdr.gso_type != VIRTIO_NET_HDR_GSO_NONE) { pr_debug("GSO!\n"); - switch (hdr->gso_type & ~VIRTIO_NET_HDR_GSO_ECN) { + switch (hdr->hdr.gso_type & ~VIRTIO_NET_HDR_GSO_ECN) { case VIRTIO_NET_HDR_GSO_TCPV4: skb_shinfo(skb)->gso_type = SKB_GSO_TCPV4; break; @@ -247,14 +242,14 @@ static void receive_skb(struct net_device *dev, struct sk_buff *skb, default: if (net_ratelimit()) printk(KERN_WARNING "%s: bad gso type %u.\n", - dev->name, hdr->gso_type); + dev->name, hdr->hdr.gso_type); goto frame_err; } - if (hdr->gso_type & VIRTIO_NET_HDR_GSO_ECN) + if (hdr->hdr.gso_type & VIRTIO_NET_HDR_GSO_ECN) skb_shinfo(skb)->gso_type |= SKB_GSO_TCP_ECN; - skb_shinfo(skb)->gso_size = hdr->gso_size; + skb_shinfo(skb)->gso_size = hdr->hdr.gso_size; if (skb_shinfo(skb)->gso_size == 0) { if (net_ratelimit()) printk(KERN_WARNING "%s: zero gso size.\n", @@ -284,8 +279,8 @@ static bool try_fill_recv_maxbufs(struct virtnet_info *vi, gfp_t gfp) bool oom = false; sg_init_table(sg, 2+MAX_SKB_FRAGS); - for (;;) { - struct virtio_net_hdr *hdr; + do { + struct skb_vnet_hdr *hdr; skb = netdev_alloc_skb(vi->dev, MAX_PACKET_LEN + NET_IP_ALIGN); if (unlikely(!skb)) { @@ -297,7 +292,7 @@ static bool try_fill_recv_maxbufs(struct virtnet_info *vi, gfp_t gfp) skb_put(skb, MAX_PACKET_LEN); hdr = skb_vnet_hdr(skb); - sg_set_buf(sg, hdr, sizeof(*hdr)); + sg_set_buf(sg, &hdr->hdr, sizeof(hdr->hdr)); if (vi->big_packets) { for (i = 0; i < MAX_SKB_FRAGS; i++) { @@ -320,14 +315,14 @@ static bool try_fill_recv_maxbufs(struct virtnet_info *vi, gfp_t gfp) skb_queue_head(&vi->recv, skb); err = vi->rvq->vq_ops->add_buf(vi->rvq, sg, 0, num, skb); - if (err) { + if (err < 0) { skb_unlink(skb, &vi->recv); trim_pages(vi, skb); kfree_skb(skb); break; } vi->num++; - } + } while (err >= num); if (unlikely(vi->num > vi->max)) vi->max = vi->num; vi->rvq->vq_ops->kick(vi->rvq); @@ -345,7 +340,7 @@ static bool try_fill_recv(struct virtnet_info *vi, gfp_t gfp) if (!vi->mergeable_rx_bufs) return try_fill_recv_maxbufs(vi, gfp); - for (;;) { + do { skb_frag_t *f; skb = netdev_alloc_skb(vi->dev, GOOD_COPY_LEN + NET_IP_ALIGN); @@ -373,13 +368,13 @@ static bool try_fill_recv(struct virtnet_info *vi, gfp_t gfp) skb_queue_head(&vi->recv, skb); err = vi->rvq->vq_ops->add_buf(vi->rvq, sg, 0, 1, skb); - if (err) { + if (err < 0) { skb_unlink(skb, &vi->recv); kfree_skb(skb); break; } vi->num++; - } + } while (err > 0); if (unlikely(vi->num > vi->max)) vi->max = vi->num; vi->rvq->vq_ops->kick(vi->rvq); @@ -447,42 +442,26 @@ again: return received; } -static void free_old_xmit_skbs(struct virtnet_info *vi) +static unsigned int free_old_xmit_skbs(struct virtnet_info *vi) { struct sk_buff *skb; - unsigned int len; + unsigned int len, tot_sgs = 0; while ((skb = vi->svq->vq_ops->get_buf(vi->svq, &len)) != NULL) { pr_debug("Sent skb %p\n", skb); __skb_unlink(skb, &vi->send); vi->dev->stats.tx_bytes += skb->len; vi->dev->stats.tx_packets++; - kfree_skb(skb); + tot_sgs += skb_vnet_hdr(skb)->num_sg; + dev_kfree_skb_any(skb); } -} - -/* If the virtio transport doesn't always notify us when all in-flight packets - * are consumed, we fall back to using this function on a timer to free them. */ -static void xmit_free(unsigned long data) -{ - struct virtnet_info *vi = (void *)data; - - netif_tx_lock(vi->dev); - - free_old_xmit_skbs(vi); - - if (!skb_queue_empty(&vi->send)) - mod_timer(&vi->xmit_free_timer, jiffies + (HZ/10)); - - netif_tx_unlock(vi->dev); + return tot_sgs; } static int xmit_skb(struct virtnet_info *vi, struct sk_buff *skb) { - int num, err; struct scatterlist sg[2+MAX_SKB_FRAGS]; - struct virtio_net_hdr_mrg_rxbuf *mhdr = skb_vnet_hdr(skb); - struct virtio_net_hdr *hdr = skb_vnet_hdr(skb); + struct skb_vnet_hdr *hdr = skb_vnet_hdr(skb); const unsigned char *dest = ((struct ethhdr *)skb->data)->h_dest; sg_init_table(sg, 2+MAX_SKB_FRAGS); @@ -490,108 +469,97 @@ static int xmit_skb(struct virtnet_info *vi, struct sk_buff *skb) pr_debug("%s: xmit %p %pM\n", vi->dev->name, skb, dest); if (skb->ip_summed == CHECKSUM_PARTIAL) { - hdr->flags = VIRTIO_NET_HDR_F_NEEDS_CSUM; - hdr->csum_start = skb->csum_start - skb_headroom(skb); - hdr->csum_offset = skb->csum_offset; + hdr->hdr.flags = VIRTIO_NET_HDR_F_NEEDS_CSUM; + hdr->hdr.csum_start = skb->csum_start - skb_headroom(skb); + hdr->hdr.csum_offset = skb->csum_offset; } else { - hdr->flags = 0; - hdr->csum_offset = hdr->csum_start = 0; + hdr->hdr.flags = 0; + hdr->hdr.csum_offset = hdr->hdr.csum_start = 0; } if (skb_is_gso(skb)) { - hdr->hdr_len = skb_headlen(skb); - hdr->gso_size = skb_shinfo(skb)->gso_size; + hdr->hdr.hdr_len = skb_headlen(skb); + hdr->hdr.gso_size = skb_shinfo(skb)->gso_size; if (skb_shinfo(skb)->gso_type & SKB_GSO_TCPV4) - hdr->gso_type = VIRTIO_NET_HDR_GSO_TCPV4; + hdr->hdr.gso_type = VIRTIO_NET_HDR_GSO_TCPV4; else if (skb_shinfo(skb)->gso_type & SKB_GSO_TCPV6) - hdr->gso_type = VIRTIO_NET_HDR_GSO_TCPV6; + hdr->hdr.gso_type = VIRTIO_NET_HDR_GSO_TCPV6; else if (skb_shinfo(skb)->gso_type & SKB_GSO_UDP) - hdr->gso_type = VIRTIO_NET_HDR_GSO_UDP; + hdr->hdr.gso_type = VIRTIO_NET_HDR_GSO_UDP; else BUG(); if (skb_shinfo(skb)->gso_type & SKB_GSO_TCP_ECN) - hdr->gso_type |= VIRTIO_NET_HDR_GSO_ECN; + hdr->hdr.gso_type |= VIRTIO_NET_HDR_GSO_ECN; } else { - hdr->gso_type = VIRTIO_NET_HDR_GSO_NONE; - hdr->gso_size = hdr->hdr_len = 0; + hdr->hdr.gso_type = VIRTIO_NET_HDR_GSO_NONE; + hdr->hdr.gso_size = hdr->hdr.hdr_len = 0; } - mhdr->num_buffers = 0; + hdr->mhdr.num_buffers = 0; /* Encode metadata header at front. */ if (vi->mergeable_rx_bufs) - sg_set_buf(sg, mhdr, sizeof(*mhdr)); + sg_set_buf(sg, &hdr->mhdr, sizeof(hdr->mhdr)); else - sg_set_buf(sg, hdr, sizeof(*hdr)); - - num = skb_to_sgvec(skb, sg+1, 0, skb->len) + 1; + sg_set_buf(sg, &hdr->hdr, sizeof(hdr->hdr)); - err = vi->svq->vq_ops->add_buf(vi->svq, sg, num, 0, skb); - if (!err && !vi->free_in_tasklet) - mod_timer(&vi->xmit_free_timer, jiffies + (HZ/10)); - - return err; -} - -static void xmit_tasklet(unsigned long data) -{ - struct virtnet_info *vi = (void *)data; - - netif_tx_lock_bh(vi->dev); - if (vi->last_xmit_skb && xmit_skb(vi, vi->last_xmit_skb) == 0) { - vi->svq->vq_ops->kick(vi->svq); - vi->last_xmit_skb = NULL; - } - if (vi->free_in_tasklet) - free_old_xmit_skbs(vi); - netif_tx_unlock_bh(vi->dev); + hdr->num_sg = skb_to_sgvec(skb, sg+1, 0, skb->len) + 1; + return vi->svq->vq_ops->add_buf(vi->svq, sg, hdr->num_sg, 0, skb); } static netdev_tx_t start_xmit(struct sk_buff *skb, struct net_device *dev) { struct virtnet_info *vi = netdev_priv(dev); + int capacity; again: /* Free up any pending old buffers before queueing new ones. */ free_old_xmit_skbs(vi); - /* If we has a buffer left over from last time, send it now. */ - if (unlikely(vi->last_xmit_skb) && - xmit_skb(vi, vi->last_xmit_skb) != 0) - goto stop_queue; - - vi->last_xmit_skb = NULL; + /* Try to transmit */ + capacity = xmit_skb(vi, skb); - /* Put new one in send queue and do transmit */ - if (likely(skb)) { - __skb_queue_head(&vi->send, skb); - if (xmit_skb(vi, skb) != 0) { - vi->last_xmit_skb = skb; - skb = NULL; - goto stop_queue; + /* This can happen with OOM and indirect buffers. */ + if (unlikely(capacity < 0)) { + netif_stop_queue(dev); + dev_warn(&dev->dev, "Unexpected full queue\n"); + if (unlikely(!vi->svq->vq_ops->enable_cb(vi->svq))) { + vi->svq->vq_ops->disable_cb(vi->svq); + netif_start_queue(dev); + goto again; } + return NETDEV_TX_BUSY; } -done: vi->svq->vq_ops->kick(vi->svq); - return NETDEV_TX_OK; - -stop_queue: - pr_debug("%s: virtio not prepared to send\n", dev->name); - netif_stop_queue(dev); - /* Activate callback for using skbs: if this returns false it - * means some were used in the meantime. */ - if (unlikely(!vi->svq->vq_ops->enable_cb(vi->svq))) { - vi->svq->vq_ops->disable_cb(vi->svq); - netif_start_queue(dev); - goto again; - } - if (skb) { - /* Drop this skb: we only queue one. */ - vi->dev->stats.tx_dropped++; - kfree_skb(skb); + /* + * Put new one in send queue. You'd expect we'd need this before + * xmit_skb calls add_buf(), since the callback can be triggered + * immediately after that. But since the callback just triggers + * another call back here, normal network xmit locking prevents the + * race. + */ + __skb_queue_head(&vi->send, skb); + + /* Don't wait up for transmitted skbs to be freed. */ + skb_orphan(skb); + nf_reset(skb); + + /* Apparently nice girls don't return TX_BUSY; stop the queue + * before it gets out of hand. Naturally, this wastes entries. */ + if (capacity < 2+MAX_SKB_FRAGS) { + netif_stop_queue(dev); + if (unlikely(!vi->svq->vq_ops->enable_cb(vi->svq))) { + /* More just got used, free them then recheck. */ + capacity += free_old_xmit_skbs(vi); + if (capacity >= 2+MAX_SKB_FRAGS) { + netif_start_queue(dev); + vi->svq->vq_ops->disable_cb(vi->svq); + } + } } - goto done; + + return NETDEV_TX_OK; } static int virtnet_set_mac_address(struct net_device *dev, void *p) @@ -668,7 +636,7 @@ static bool virtnet_send_command(struct virtnet_info *vi, u8 class, u8 cmd, sg_set_buf(&sg[i + 1], sg_virt(s), s->length); sg_set_buf(&sg[out + in - 1], &status, sizeof(status)); - BUG_ON(vi->cvq->vq_ops->add_buf(vi->cvq, sg, out, in, vi)); + BUG_ON(vi->cvq->vq_ops->add_buf(vi->cvq, sg, out, in, vi) < 0); vi->cvq->vq_ops->kick(vi->cvq); @@ -924,10 +892,6 @@ static int virtnet_probe(struct virtio_device *vdev) vi->pages = NULL; INIT_DELAYED_WORK(&vi->refill, refill_work); - /* If they give us a callback when all buffers are done, we don't need - * the timer. */ - vi->free_in_tasklet = virtio_has_feature(vdev,VIRTIO_F_NOTIFY_ON_EMPTY); - /* If we can receive ANY GSO packets, we must allocate large ones. */ if (virtio_has_feature(vdev, VIRTIO_NET_F_GUEST_TSO4) || virtio_has_feature(vdev, VIRTIO_NET_F_GUEST_TSO6) @@ -959,11 +923,6 @@ static int virtnet_probe(struct virtio_device *vdev) skb_queue_head_init(&vi->recv); skb_queue_head_init(&vi->send); - tasklet_init(&vi->tasklet, xmit_tasklet, (unsigned long)vi); - - if (!vi->free_in_tasklet) - setup_timer(&vi->xmit_free_timer, xmit_free, (unsigned long)vi); - err = register_netdev(dev); if (err) { pr_debug("virtio_net: registering device failed\n"); @@ -996,7 +955,7 @@ free: return err; } -static void virtnet_remove(struct virtio_device *vdev) +static void __devexit virtnet_remove(struct virtio_device *vdev) { struct virtnet_info *vi = vdev->priv; struct sk_buff *skb; @@ -1004,9 +963,6 @@ static void virtnet_remove(struct virtio_device *vdev) /* Stop all the virtqueues. */ vdev->config->reset(vdev); - if (!vi->free_in_tasklet) - del_timer_sync(&vi->xmit_free_timer); - /* Free our skbs in send and recv queues, if any. */ while ((skb = __skb_dequeue(&vi->recv)) != NULL) { kfree_skb(skb); @@ -1040,10 +996,9 @@ static unsigned int features[] = { VIRTIO_NET_F_GUEST_ECN, VIRTIO_NET_F_GUEST_UFO, VIRTIO_NET_F_MRG_RXBUF, VIRTIO_NET_F_STATUS, VIRTIO_NET_F_CTRL_VQ, VIRTIO_NET_F_CTRL_RX, VIRTIO_NET_F_CTRL_VLAN, - VIRTIO_F_NOTIFY_ON_EMPTY, }; -static struct virtio_driver virtio_net = { +static struct virtio_driver virtio_net_driver = { .feature_table = features, .feature_table_size = ARRAY_SIZE(features), .driver.name = KBUILD_MODNAME, @@ -1056,12 +1011,12 @@ static struct virtio_driver virtio_net = { static int __init init(void) { - return register_virtio_driver(&virtio_net); + return register_virtio_driver(&virtio_net_driver); } static void __exit fini(void) { - unregister_virtio_driver(&virtio_net); + unregister_virtio_driver(&virtio_net_driver); } module_init(init); module_exit(fini); diff --git a/drivers/net/vmxnet3/Makefile b/drivers/net/vmxnet3/Makefile new file mode 100644 index 000000000000..880f5098eac9 --- /dev/null +++ b/drivers/net/vmxnet3/Makefile @@ -0,0 +1,35 @@ +################################################################################ +# +# Linux driver for VMware's vmxnet3 ethernet NIC. +# +# Copyright (C) 2007-2009, VMware, Inc. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; version 2 of the License and no later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or +# NON INFRINGEMENT. See the GNU General Public License for more +# details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# The full GNU General Public License is included in this distribution in +# the file called "COPYING". +# +# Maintained by: Shreyas Bhatewara <pv-drivers@vmware.com> +# +# +################################################################################ + +# +# Makefile for the VMware vmxnet3 ethernet NIC driver +# + +obj-$(CONFIG_VMXNET3) += vmxnet3.o + +vmxnet3-objs := vmxnet3_drv.o vmxnet3_ethtool.o diff --git a/drivers/net/vmxnet3/upt1_defs.h b/drivers/net/vmxnet3/upt1_defs.h new file mode 100644 index 000000000000..37108fb226d3 --- /dev/null +++ b/drivers/net/vmxnet3/upt1_defs.h @@ -0,0 +1,96 @@ +/* + * Linux driver for VMware's vmxnet3 ethernet NIC. + * + * Copyright (C) 2008-2009, VMware, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; version 2 of the License and no later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or + * NON INFRINGEMENT. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * The full GNU General Public License is included in this distribution in + * the file called "COPYING". + * + * Maintained by: Shreyas Bhatewara <pv-drivers@vmware.com> + * + */ + +#ifndef _UPT1_DEFS_H +#define _UPT1_DEFS_H + +struct UPT1_TxStats { + u64 TSOPktsTxOK; /* TSO pkts post-segmentation */ + u64 TSOBytesTxOK; + u64 ucastPktsTxOK; + u64 ucastBytesTxOK; + u64 mcastPktsTxOK; + u64 mcastBytesTxOK; + u64 bcastPktsTxOK; + u64 bcastBytesTxOK; + u64 pktsTxError; + u64 pktsTxDiscard; +}; + +struct UPT1_RxStats { + u64 LROPktsRxOK; /* LRO pkts */ + u64 LROBytesRxOK; /* bytes from LRO pkts */ + /* the following counters are for pkts from the wire, i.e., pre-LRO */ + u64 ucastPktsRxOK; + u64 ucastBytesRxOK; + u64 mcastPktsRxOK; + u64 mcastBytesRxOK; + u64 bcastPktsRxOK; + u64 bcastBytesRxOK; + u64 pktsRxOutOfBuf; + u64 pktsRxError; +}; + +/* interrupt moderation level */ +enum { + UPT1_IML_NONE = 0, /* no interrupt moderation */ + UPT1_IML_HIGHEST = 7, /* least intr generated */ + UPT1_IML_ADAPTIVE = 8, /* adpative intr moderation */ +}; +/* values for UPT1_RSSConf.hashFunc */ +enum { + UPT1_RSS_HASH_TYPE_NONE = 0x0, + UPT1_RSS_HASH_TYPE_IPV4 = 0x01, + UPT1_RSS_HASH_TYPE_TCP_IPV4 = 0x02, + UPT1_RSS_HASH_TYPE_IPV6 = 0x04, + UPT1_RSS_HASH_TYPE_TCP_IPV6 = 0x08, +}; + +enum { + UPT1_RSS_HASH_FUNC_NONE = 0x0, + UPT1_RSS_HASH_FUNC_TOEPLITZ = 0x01, +}; + +#define UPT1_RSS_MAX_KEY_SIZE 40 +#define UPT1_RSS_MAX_IND_TABLE_SIZE 128 + +struct UPT1_RSSConf { + u16 hashType; + u16 hashFunc; + u16 hashKeySize; + u16 indTableSize; + u8 hashKey[UPT1_RSS_MAX_KEY_SIZE]; + u8 indTable[UPT1_RSS_MAX_IND_TABLE_SIZE]; +}; + +/* features */ +enum { + UPT1_F_RXCSUM = 0x0001, /* rx csum verification */ + UPT1_F_RSS = 0x0002, + UPT1_F_RXVLAN = 0x0004, /* VLAN tag stripping */ + UPT1_F_LRO = 0x0008, +}; +#endif diff --git a/drivers/net/vmxnet3/vmxnet3_defs.h b/drivers/net/vmxnet3/vmxnet3_defs.h new file mode 100644 index 000000000000..dc8ee4438a4f --- /dev/null +++ b/drivers/net/vmxnet3/vmxnet3_defs.h @@ -0,0 +1,535 @@ +/* + * Linux driver for VMware's vmxnet3 ethernet NIC. + * + * Copyright (C) 2008-2009, VMware, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; version 2 of the License and no later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or + * NON INFRINGEMENT. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * The full GNU General Public License is included in this distribution in + * the file called "COPYING". + * + * Maintained by: Shreyas Bhatewara <pv-drivers@vmware.com> + * + */ + +#ifndef _VMXNET3_DEFS_H_ +#define _VMXNET3_DEFS_H_ + +#include "upt1_defs.h" + +/* all registers are 32 bit wide */ +/* BAR 1 */ +enum { + VMXNET3_REG_VRRS = 0x0, /* Vmxnet3 Revision Report Selection */ + VMXNET3_REG_UVRS = 0x8, /* UPT Version Report Selection */ + VMXNET3_REG_DSAL = 0x10, /* Driver Shared Address Low */ + VMXNET3_REG_DSAH = 0x18, /* Driver Shared Address High */ + VMXNET3_REG_CMD = 0x20, /* Command */ + VMXNET3_REG_MACL = 0x28, /* MAC Address Low */ + VMXNET3_REG_MACH = 0x30, /* MAC Address High */ + VMXNET3_REG_ICR = 0x38, /* Interrupt Cause Register */ + VMXNET3_REG_ECR = 0x40 /* Event Cause Register */ +}; + +/* BAR 0 */ +enum { + VMXNET3_REG_IMR = 0x0, /* Interrupt Mask Register */ + VMXNET3_REG_TXPROD = 0x600, /* Tx Producer Index */ + VMXNET3_REG_RXPROD = 0x800, /* Rx Producer Index for ring 1 */ + VMXNET3_REG_RXPROD2 = 0xA00 /* Rx Producer Index for ring 2 */ +}; + +#define VMXNET3_PT_REG_SIZE 4096 /* BAR 0 */ +#define VMXNET3_VD_REG_SIZE 4096 /* BAR 1 */ + +#define VMXNET3_REG_ALIGN 8 /* All registers are 8-byte aligned. */ +#define VMXNET3_REG_ALIGN_MASK 0x7 + +/* I/O Mapped access to registers */ +#define VMXNET3_IO_TYPE_PT 0 +#define VMXNET3_IO_TYPE_VD 1 +#define VMXNET3_IO_ADDR(type, reg) (((type) << 24) | ((reg) & 0xFFFFFF)) +#define VMXNET3_IO_TYPE(addr) ((addr) >> 24) +#define VMXNET3_IO_REG(addr) ((addr) & 0xFFFFFF) + +enum { + VMXNET3_CMD_FIRST_SET = 0xCAFE0000, + VMXNET3_CMD_ACTIVATE_DEV = VMXNET3_CMD_FIRST_SET, + VMXNET3_CMD_QUIESCE_DEV, + VMXNET3_CMD_RESET_DEV, + VMXNET3_CMD_UPDATE_RX_MODE, + VMXNET3_CMD_UPDATE_MAC_FILTERS, + VMXNET3_CMD_UPDATE_VLAN_FILTERS, + VMXNET3_CMD_UPDATE_RSSIDT, + VMXNET3_CMD_UPDATE_IML, + VMXNET3_CMD_UPDATE_PMCFG, + VMXNET3_CMD_UPDATE_FEATURE, + VMXNET3_CMD_LOAD_PLUGIN, + + VMXNET3_CMD_FIRST_GET = 0xF00D0000, + VMXNET3_CMD_GET_QUEUE_STATUS = VMXNET3_CMD_FIRST_GET, + VMXNET3_CMD_GET_STATS, + VMXNET3_CMD_GET_LINK, + VMXNET3_CMD_GET_PERM_MAC_LO, + VMXNET3_CMD_GET_PERM_MAC_HI, + VMXNET3_CMD_GET_DID_LO, + VMXNET3_CMD_GET_DID_HI, + VMXNET3_CMD_GET_DEV_EXTRA_INFO, + VMXNET3_CMD_GET_CONF_INTR +}; + +struct Vmxnet3_TxDesc { + u64 addr; + + u32 len:14; + u32 gen:1; /* generation bit */ + u32 rsvd:1; + u32 dtype:1; /* descriptor type */ + u32 ext1:1; + u32 msscof:14; /* MSS, checksum offset, flags */ + + u32 hlen:10; /* header len */ + u32 om:2; /* offload mode */ + u32 eop:1; /* End Of Packet */ + u32 cq:1; /* completion request */ + u32 ext2:1; + u32 ti:1; /* VLAN Tag Insertion */ + u32 tci:16; /* Tag to Insert */ +}; + +/* TxDesc.OM values */ +#define VMXNET3_OM_NONE 0 +#define VMXNET3_OM_CSUM 2 +#define VMXNET3_OM_TSO 3 + +/* fields in TxDesc we access w/o using bit fields */ +#define VMXNET3_TXD_EOP_SHIFT 12 +#define VMXNET3_TXD_CQ_SHIFT 13 +#define VMXNET3_TXD_GEN_SHIFT 14 + +#define VMXNET3_TXD_CQ (1 << VMXNET3_TXD_CQ_SHIFT) +#define VMXNET3_TXD_EOP (1 << VMXNET3_TXD_EOP_SHIFT) +#define VMXNET3_TXD_GEN (1 << VMXNET3_TXD_GEN_SHIFT) + +#define VMXNET3_HDR_COPY_SIZE 128 + + +struct Vmxnet3_TxDataDesc { + u8 data[VMXNET3_HDR_COPY_SIZE]; +}; + + +struct Vmxnet3_TxCompDesc { + u32 txdIdx:12; /* Index of the EOP TxDesc */ + u32 ext1:20; + + u32 ext2; + u32 ext3; + + u32 rsvd:24; + u32 type:7; /* completion type */ + u32 gen:1; /* generation bit */ +}; + + +struct Vmxnet3_RxDesc { + u64 addr; + + u32 len:14; + u32 btype:1; /* Buffer Type */ + u32 dtype:1; /* Descriptor type */ + u32 rsvd:15; + u32 gen:1; /* Generation bit */ + + u32 ext1; +}; + +/* values of RXD.BTYPE */ +#define VMXNET3_RXD_BTYPE_HEAD 0 /* head only */ +#define VMXNET3_RXD_BTYPE_BODY 1 /* body only */ + +/* fields in RxDesc we access w/o using bit fields */ +#define VMXNET3_RXD_BTYPE_SHIFT 14 +#define VMXNET3_RXD_GEN_SHIFT 31 + + +struct Vmxnet3_RxCompDesc { + u32 rxdIdx:12; /* Index of the RxDesc */ + u32 ext1:2; + u32 eop:1; /* End of Packet */ + u32 sop:1; /* Start of Packet */ + u32 rqID:10; /* rx queue/ring ID */ + u32 rssType:4; /* RSS hash type used */ + u32 cnc:1; /* Checksum Not Calculated */ + u32 ext2:1; + + u32 rssHash; /* RSS hash value */ + + u32 len:14; /* data length */ + u32 err:1; /* Error */ + u32 ts:1; /* Tag is stripped */ + u32 tci:16; /* Tag stripped */ + + u32 csum:16; + u32 tuc:1; /* TCP/UDP Checksum Correct */ + u32 udp:1; /* UDP packet */ + u32 tcp:1; /* TCP packet */ + u32 ipc:1; /* IP Checksum Correct */ + u32 v6:1; /* IPv6 */ + u32 v4:1; /* IPv4 */ + u32 frg:1; /* IP Fragment */ + u32 fcs:1; /* Frame CRC correct */ + u32 type:7; /* completion type */ + u32 gen:1; /* generation bit */ +}; + +/* fields in RxCompDesc we access via Vmxnet3_GenericDesc.dword[3] */ +#define VMXNET3_RCD_TUC_SHIFT 16 +#define VMXNET3_RCD_IPC_SHIFT 19 + +/* fields in RxCompDesc we access via Vmxnet3_GenericDesc.qword[1] */ +#define VMXNET3_RCD_TYPE_SHIFT 56 +#define VMXNET3_RCD_GEN_SHIFT 63 + +/* csum OK for TCP/UDP pkts over IP */ +#define VMXNET3_RCD_CSUM_OK (1 << VMXNET3_RCD_TUC_SHIFT | \ + 1 << VMXNET3_RCD_IPC_SHIFT) + +/* value of RxCompDesc.rssType */ +enum { + VMXNET3_RCD_RSS_TYPE_NONE = 0, + VMXNET3_RCD_RSS_TYPE_IPV4 = 1, + VMXNET3_RCD_RSS_TYPE_TCPIPV4 = 2, + VMXNET3_RCD_RSS_TYPE_IPV6 = 3, + VMXNET3_RCD_RSS_TYPE_TCPIPV6 = 4, +}; + + +/* a union for accessing all cmd/completion descriptors */ +union Vmxnet3_GenericDesc { + u64 qword[2]; + u32 dword[4]; + u16 word[8]; + struct Vmxnet3_TxDesc txd; + struct Vmxnet3_RxDesc rxd; + struct Vmxnet3_TxCompDesc tcd; + struct Vmxnet3_RxCompDesc rcd; +}; + +#define VMXNET3_INIT_GEN 1 + +/* Max size of a single tx buffer */ +#define VMXNET3_MAX_TX_BUF_SIZE (1 << 14) + +/* # of tx desc needed for a tx buffer size */ +#define VMXNET3_TXD_NEEDED(size) (((size) + VMXNET3_MAX_TX_BUF_SIZE - 1) / \ + VMXNET3_MAX_TX_BUF_SIZE) + +/* max # of tx descs for a non-tso pkt */ +#define VMXNET3_MAX_TXD_PER_PKT 16 + +/* Max size of a single rx buffer */ +#define VMXNET3_MAX_RX_BUF_SIZE ((1 << 14) - 1) +/* Minimum size of a type 0 buffer */ +#define VMXNET3_MIN_T0_BUF_SIZE 128 +#define VMXNET3_MAX_CSUM_OFFSET 1024 + +/* Ring base address alignment */ +#define VMXNET3_RING_BA_ALIGN 512 +#define VMXNET3_RING_BA_MASK (VMXNET3_RING_BA_ALIGN - 1) + +/* Ring size must be a multiple of 32 */ +#define VMXNET3_RING_SIZE_ALIGN 32 +#define VMXNET3_RING_SIZE_MASK (VMXNET3_RING_SIZE_ALIGN - 1) + +/* Max ring size */ +#define VMXNET3_TX_RING_MAX_SIZE 4096 +#define VMXNET3_TC_RING_MAX_SIZE 4096 +#define VMXNET3_RX_RING_MAX_SIZE 4096 +#define VMXNET3_RC_RING_MAX_SIZE 8192 + +/* a list of reasons for queue stop */ + +enum { + VMXNET3_ERR_NOEOP = 0x80000000, /* cannot find the EOP desc of a pkt */ + VMXNET3_ERR_TXD_REUSE = 0x80000001, /* reuse TxDesc before tx completion */ + VMXNET3_ERR_BIG_PKT = 0x80000002, /* too many TxDesc for a pkt */ + VMXNET3_ERR_DESC_NOT_SPT = 0x80000003, /* descriptor type not supported */ + VMXNET3_ERR_SMALL_BUF = 0x80000004, /* type 0 buffer too small */ + VMXNET3_ERR_STRESS = 0x80000005, /* stress option firing in vmkernel */ + VMXNET3_ERR_SWITCH = 0x80000006, /* mode switch failure */ + VMXNET3_ERR_TXD_INVALID = 0x80000007, /* invalid TxDesc */ +}; + +/* completion descriptor types */ +#define VMXNET3_CDTYPE_TXCOMP 0 /* Tx Completion Descriptor */ +#define VMXNET3_CDTYPE_RXCOMP 3 /* Rx Completion Descriptor */ + +enum { + VMXNET3_GOS_BITS_UNK = 0, /* unknown */ + VMXNET3_GOS_BITS_32 = 1, + VMXNET3_GOS_BITS_64 = 2, +}; + +#define VMXNET3_GOS_TYPE_LINUX 1 + + +struct Vmxnet3_GOSInfo { + u32 gosBits:2; /* 32-bit or 64-bit? */ + u32 gosType:4; /* which guest */ + u32 gosVer:16; /* gos version */ + u32 gosMisc:10; /* other info about gos */ +}; + + +struct Vmxnet3_DriverInfo { + u32 version; + struct Vmxnet3_GOSInfo gos; + u32 vmxnet3RevSpt; + u32 uptVerSpt; +}; + + +#define VMXNET3_REV1_MAGIC 0xbabefee1 + +/* + * QueueDescPA must be 128 bytes aligned. It points to an array of + * Vmxnet3_TxQueueDesc followed by an array of Vmxnet3_RxQueueDesc. + * The number of Vmxnet3_TxQueueDesc/Vmxnet3_RxQueueDesc are specified by + * Vmxnet3_MiscConf.numTxQueues/numRxQueues, respectively. + */ +#define VMXNET3_QUEUE_DESC_ALIGN 128 + + +struct Vmxnet3_MiscConf { + struct Vmxnet3_DriverInfo driverInfo; + u64 uptFeatures; + u64 ddPA; /* driver data PA */ + u64 queueDescPA; /* queue descriptor table PA */ + u32 ddLen; /* driver data len */ + u32 queueDescLen; /* queue desc. table len in bytes */ + u32 mtu; + u16 maxNumRxSG; + u8 numTxQueues; + u8 numRxQueues; + u32 reserved[4]; +}; + + +struct Vmxnet3_TxQueueConf { + u64 txRingBasePA; + u64 dataRingBasePA; + u64 compRingBasePA; + u64 ddPA; /* driver data */ + u64 reserved; + u32 txRingSize; /* # of tx desc */ + u32 dataRingSize; /* # of data desc */ + u32 compRingSize; /* # of comp desc */ + u32 ddLen; /* size of driver data */ + u8 intrIdx; + u8 _pad[7]; +}; + + +struct Vmxnet3_RxQueueConf { + u64 rxRingBasePA[2]; + u64 compRingBasePA; + u64 ddPA; /* driver data */ + u64 reserved; + u32 rxRingSize[2]; /* # of rx desc */ + u32 compRingSize; /* # of rx comp desc */ + u32 ddLen; /* size of driver data */ + u8 intrIdx; + u8 _pad[7]; +}; + + +enum vmxnet3_intr_mask_mode { + VMXNET3_IMM_AUTO = 0, + VMXNET3_IMM_ACTIVE = 1, + VMXNET3_IMM_LAZY = 2 +}; + +enum vmxnet3_intr_type { + VMXNET3_IT_AUTO = 0, + VMXNET3_IT_INTX = 1, + VMXNET3_IT_MSI = 2, + VMXNET3_IT_MSIX = 3 +}; + +#define VMXNET3_MAX_TX_QUEUES 8 +#define VMXNET3_MAX_RX_QUEUES 16 +/* addition 1 for events */ +#define VMXNET3_MAX_INTRS 25 + + +struct Vmxnet3_IntrConf { + bool autoMask; + u8 numIntrs; /* # of interrupts */ + u8 eventIntrIdx; + u8 modLevels[VMXNET3_MAX_INTRS]; /* moderation level for + * each intr */ + u32 reserved[3]; +}; + +/* one bit per VLAN ID, the size is in the units of u32 */ +#define VMXNET3_VFT_SIZE (4096 / (sizeof(u32) * 8)) + + +struct Vmxnet3_QueueStatus { + bool stopped; + u8 _pad[3]; + u32 error; +}; + + +struct Vmxnet3_TxQueueCtrl { + u32 txNumDeferred; + u32 txThreshold; + u64 reserved; +}; + + +struct Vmxnet3_RxQueueCtrl { + bool updateRxProd; + u8 _pad[7]; + u64 reserved; +}; + +enum { + VMXNET3_RXM_UCAST = 0x01, /* unicast only */ + VMXNET3_RXM_MCAST = 0x02, /* multicast passing the filters */ + VMXNET3_RXM_BCAST = 0x04, /* broadcast only */ + VMXNET3_RXM_ALL_MULTI = 0x08, /* all multicast */ + VMXNET3_RXM_PROMISC = 0x10 /* promiscuous */ +}; + +struct Vmxnet3_RxFilterConf { + u32 rxMode; /* VMXNET3_RXM_xxx */ + u16 mfTableLen; /* size of the multicast filter table */ + u16 _pad1; + u64 mfTablePA; /* PA of the multicast filters table */ + u32 vfTable[VMXNET3_VFT_SIZE]; /* vlan filter */ +}; + + +#define VMXNET3_PM_MAX_FILTERS 6 +#define VMXNET3_PM_MAX_PATTERN_SIZE 128 +#define VMXNET3_PM_MAX_MASK_SIZE (VMXNET3_PM_MAX_PATTERN_SIZE / 8) + +#define VMXNET3_PM_WAKEUP_MAGIC 0x01 /* wake up on magic pkts */ +#define VMXNET3_PM_WAKEUP_FILTER 0x02 /* wake up on pkts matching + * filters */ + + +struct Vmxnet3_PM_PktFilter { + u8 maskSize; + u8 patternSize; + u8 mask[VMXNET3_PM_MAX_MASK_SIZE]; + u8 pattern[VMXNET3_PM_MAX_PATTERN_SIZE]; + u8 pad[6]; +}; + + +struct Vmxnet3_PMConf { + u16 wakeUpEvents; /* VMXNET3_PM_WAKEUP_xxx */ + u8 numFilters; + u8 pad[5]; + struct Vmxnet3_PM_PktFilter filters[VMXNET3_PM_MAX_FILTERS]; +}; + + +struct Vmxnet3_VariableLenConfDesc { + u32 confVer; + u32 confLen; + u64 confPA; +}; + + +struct Vmxnet3_TxQueueDesc { + struct Vmxnet3_TxQueueCtrl ctrl; + struct Vmxnet3_TxQueueConf conf; + + /* Driver read after a GET command */ + struct Vmxnet3_QueueStatus status; + struct UPT1_TxStats stats; + u8 _pad[88]; /* 128 aligned */ +}; + + +struct Vmxnet3_RxQueueDesc { + struct Vmxnet3_RxQueueCtrl ctrl; + struct Vmxnet3_RxQueueConf conf; + /* Driver read after a GET commad */ + struct Vmxnet3_QueueStatus status; + struct UPT1_RxStats stats; + u8 __pad[88]; /* 128 aligned */ +}; + + +struct Vmxnet3_DSDevRead { + /* read-only region for device, read by dev in response to a SET cmd */ + struct Vmxnet3_MiscConf misc; + struct Vmxnet3_IntrConf intrConf; + struct Vmxnet3_RxFilterConf rxFilterConf; + struct Vmxnet3_VariableLenConfDesc rssConfDesc; + struct Vmxnet3_VariableLenConfDesc pmConfDesc; + struct Vmxnet3_VariableLenConfDesc pluginConfDesc; +}; + +/* All structures in DriverShared are padded to multiples of 8 bytes */ +struct Vmxnet3_DriverShared { + u32 magic; + /* make devRead start at 64bit boundaries */ + u32 pad; + struct Vmxnet3_DSDevRead devRead; + u32 ecr; + u32 reserved[5]; +}; + + +#define VMXNET3_ECR_RQERR (1 << 0) +#define VMXNET3_ECR_TQERR (1 << 1) +#define VMXNET3_ECR_LINK (1 << 2) +#define VMXNET3_ECR_DIC (1 << 3) +#define VMXNET3_ECR_DEBUG (1 << 4) + +/* flip the gen bit of a ring */ +#define VMXNET3_FLIP_RING_GEN(gen) ((gen) = (gen) ^ 0x1) + +/* only use this if moving the idx won't affect the gen bit */ +#define VMXNET3_INC_RING_IDX_ONLY(idx, ring_size) \ + do {\ + (idx)++;\ + if (unlikely((idx) == (ring_size))) {\ + (idx) = 0;\ + } \ + } while (0) + +#define VMXNET3_SET_VFTABLE_ENTRY(vfTable, vid) \ + (vfTable[vid >> 5] |= (1 << (vid & 31))) +#define VMXNET3_CLEAR_VFTABLE_ENTRY(vfTable, vid) \ + (vfTable[vid >> 5] &= ~(1 << (vid & 31))) + +#define VMXNET3_VFTABLE_ENTRY_IS_SET(vfTable, vid) \ + ((vfTable[vid >> 5] & (1 << (vid & 31))) != 0) + +#define VMXNET3_MAX_MTU 9000 +#define VMXNET3_MIN_MTU 60 + +#define VMXNET3_LINK_UP (10000 << 16 | 1) /* 10 Gbps, up */ +#define VMXNET3_LINK_DOWN 0 + +#endif /* _VMXNET3_DEFS_H_ */ diff --git a/drivers/net/vmxnet3/vmxnet3_drv.c b/drivers/net/vmxnet3/vmxnet3_drv.c new file mode 100644 index 000000000000..004353a46af0 --- /dev/null +++ b/drivers/net/vmxnet3/vmxnet3_drv.c @@ -0,0 +1,2574 @@ +/* + * Linux driver for VMware's vmxnet3 ethernet NIC. + * + * Copyright (C) 2008-2009, VMware, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; version 2 of the License and no later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or + * NON INFRINGEMENT. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * The full GNU General Public License is included in this distribution in + * the file called "COPYING". + * + * Maintained by: Shreyas Bhatewara <pv-drivers@vmware.com> + * + */ + +#include "vmxnet3_int.h" + +char vmxnet3_driver_name[] = "vmxnet3"; +#define VMXNET3_DRIVER_DESC "VMware vmxnet3 virtual NIC driver" + + +/* + * PCI Device ID Table + * Last entry must be all 0s + */ +static const struct pci_device_id vmxnet3_pciid_table[] = { + {PCI_VDEVICE(VMWARE, PCI_DEVICE_ID_VMWARE_VMXNET3)}, + {0} +}; + +MODULE_DEVICE_TABLE(pci, vmxnet3_pciid_table); + +static atomic_t devices_found; + + +/* + * Enable/Disable the given intr + */ +static void +vmxnet3_enable_intr(struct vmxnet3_adapter *adapter, unsigned intr_idx) +{ + VMXNET3_WRITE_BAR0_REG(adapter, VMXNET3_REG_IMR + intr_idx * 8, 0); +} + + +static void +vmxnet3_disable_intr(struct vmxnet3_adapter *adapter, unsigned intr_idx) +{ + VMXNET3_WRITE_BAR0_REG(adapter, VMXNET3_REG_IMR + intr_idx * 8, 1); +} + + +/* + * Enable/Disable all intrs used by the device + */ +static void +vmxnet3_enable_all_intrs(struct vmxnet3_adapter *adapter) +{ + int i; + + for (i = 0; i < adapter->intr.num_intrs; i++) + vmxnet3_enable_intr(adapter, i); +} + + +static void +vmxnet3_disable_all_intrs(struct vmxnet3_adapter *adapter) +{ + int i; + + for (i = 0; i < adapter->intr.num_intrs; i++) + vmxnet3_disable_intr(adapter, i); +} + + +static void +vmxnet3_ack_events(struct vmxnet3_adapter *adapter, u32 events) +{ + VMXNET3_WRITE_BAR1_REG(adapter, VMXNET3_REG_ECR, events); +} + + +static bool +vmxnet3_tq_stopped(struct vmxnet3_tx_queue *tq, struct vmxnet3_adapter *adapter) +{ + return netif_queue_stopped(adapter->netdev); +} + + +static void +vmxnet3_tq_start(struct vmxnet3_tx_queue *tq, struct vmxnet3_adapter *adapter) +{ + tq->stopped = false; + netif_start_queue(adapter->netdev); +} + + +static void +vmxnet3_tq_wake(struct vmxnet3_tx_queue *tq, struct vmxnet3_adapter *adapter) +{ + tq->stopped = false; + netif_wake_queue(adapter->netdev); +} + + +static void +vmxnet3_tq_stop(struct vmxnet3_tx_queue *tq, struct vmxnet3_adapter *adapter) +{ + tq->stopped = true; + tq->num_stop++; + netif_stop_queue(adapter->netdev); +} + + +/* + * Check the link state. This may start or stop the tx queue. + */ +static void +vmxnet3_check_link(struct vmxnet3_adapter *adapter) +{ + u32 ret; + + VMXNET3_WRITE_BAR1_REG(adapter, VMXNET3_REG_CMD, VMXNET3_CMD_GET_LINK); + ret = VMXNET3_READ_BAR1_REG(adapter, VMXNET3_REG_CMD); + adapter->link_speed = ret >> 16; + if (ret & 1) { /* Link is up. */ + printk(KERN_INFO "%s: NIC Link is Up %d Mbps\n", + adapter->netdev->name, adapter->link_speed); + if (!netif_carrier_ok(adapter->netdev)) + netif_carrier_on(adapter->netdev); + + vmxnet3_tq_start(&adapter->tx_queue, adapter); + } else { + printk(KERN_INFO "%s: NIC Link is Down\n", + adapter->netdev->name); + if (netif_carrier_ok(adapter->netdev)) + netif_carrier_off(adapter->netdev); + + vmxnet3_tq_stop(&adapter->tx_queue, adapter); + } +} + + +static void +vmxnet3_process_events(struct vmxnet3_adapter *adapter) +{ + u32 events = adapter->shared->ecr; + if (!events) + return; + + vmxnet3_ack_events(adapter, events); + + /* Check if link state has changed */ + if (events & VMXNET3_ECR_LINK) + vmxnet3_check_link(adapter); + + /* Check if there is an error on xmit/recv queues */ + if (events & (VMXNET3_ECR_TQERR | VMXNET3_ECR_RQERR)) { + VMXNET3_WRITE_BAR1_REG(adapter, VMXNET3_REG_CMD, + VMXNET3_CMD_GET_QUEUE_STATUS); + + if (adapter->tqd_start->status.stopped) { + printk(KERN_ERR "%s: tq error 0x%x\n", + adapter->netdev->name, + adapter->tqd_start->status.error); + } + if (adapter->rqd_start->status.stopped) { + printk(KERN_ERR "%s: rq error 0x%x\n", + adapter->netdev->name, + adapter->rqd_start->status.error); + } + + schedule_work(&adapter->work); + } +} + + +static void +vmxnet3_unmap_tx_buf(struct vmxnet3_tx_buf_info *tbi, + struct pci_dev *pdev) +{ + if (tbi->map_type == VMXNET3_MAP_SINGLE) + pci_unmap_single(pdev, tbi->dma_addr, tbi->len, + PCI_DMA_TODEVICE); + else if (tbi->map_type == VMXNET3_MAP_PAGE) + pci_unmap_page(pdev, tbi->dma_addr, tbi->len, + PCI_DMA_TODEVICE); + else + BUG_ON(tbi->map_type != VMXNET3_MAP_NONE); + + tbi->map_type = VMXNET3_MAP_NONE; /* to help debugging */ +} + + +static int +vmxnet3_unmap_pkt(u32 eop_idx, struct vmxnet3_tx_queue *tq, + struct pci_dev *pdev, struct vmxnet3_adapter *adapter) +{ + struct sk_buff *skb; + int entries = 0; + + /* no out of order completion */ + BUG_ON(tq->buf_info[eop_idx].sop_idx != tq->tx_ring.next2comp); + BUG_ON(tq->tx_ring.base[eop_idx].txd.eop != 1); + + skb = tq->buf_info[eop_idx].skb; + BUG_ON(skb == NULL); + tq->buf_info[eop_idx].skb = NULL; + + VMXNET3_INC_RING_IDX_ONLY(eop_idx, tq->tx_ring.size); + + while (tq->tx_ring.next2comp != eop_idx) { + vmxnet3_unmap_tx_buf(tq->buf_info + tq->tx_ring.next2comp, + pdev); + + /* update next2comp w/o tx_lock. Since we are marking more, + * instead of less, tx ring entries avail, the worst case is + * that the tx routine incorrectly re-queues a pkt due to + * insufficient tx ring entries. + */ + vmxnet3_cmd_ring_adv_next2comp(&tq->tx_ring); + entries++; + } + + dev_kfree_skb_any(skb); + return entries; +} + + +static int +vmxnet3_tq_tx_complete(struct vmxnet3_tx_queue *tq, + struct vmxnet3_adapter *adapter) +{ + int completed = 0; + union Vmxnet3_GenericDesc *gdesc; + + gdesc = tq->comp_ring.base + tq->comp_ring.next2proc; + while (gdesc->tcd.gen == tq->comp_ring.gen) { + completed += vmxnet3_unmap_pkt(gdesc->tcd.txdIdx, tq, + adapter->pdev, adapter); + + vmxnet3_comp_ring_adv_next2proc(&tq->comp_ring); + gdesc = tq->comp_ring.base + tq->comp_ring.next2proc; + } + + if (completed) { + spin_lock(&tq->tx_lock); + if (unlikely(vmxnet3_tq_stopped(tq, adapter) && + vmxnet3_cmd_ring_desc_avail(&tq->tx_ring) > + VMXNET3_WAKE_QUEUE_THRESHOLD(tq) && + netif_carrier_ok(adapter->netdev))) { + vmxnet3_tq_wake(tq, adapter); + } + spin_unlock(&tq->tx_lock); + } + return completed; +} + + +static void +vmxnet3_tq_cleanup(struct vmxnet3_tx_queue *tq, + struct vmxnet3_adapter *adapter) +{ + int i; + + while (tq->tx_ring.next2comp != tq->tx_ring.next2fill) { + struct vmxnet3_tx_buf_info *tbi; + union Vmxnet3_GenericDesc *gdesc; + + tbi = tq->buf_info + tq->tx_ring.next2comp; + gdesc = tq->tx_ring.base + tq->tx_ring.next2comp; + + vmxnet3_unmap_tx_buf(tbi, adapter->pdev); + if (tbi->skb) { + dev_kfree_skb_any(tbi->skb); + tbi->skb = NULL; + } + vmxnet3_cmd_ring_adv_next2comp(&tq->tx_ring); + } + + /* sanity check, verify all buffers are indeed unmapped and freed */ + for (i = 0; i < tq->tx_ring.size; i++) { + BUG_ON(tq->buf_info[i].skb != NULL || + tq->buf_info[i].map_type != VMXNET3_MAP_NONE); + } + + tq->tx_ring.gen = VMXNET3_INIT_GEN; + tq->tx_ring.next2fill = tq->tx_ring.next2comp = 0; + + tq->comp_ring.gen = VMXNET3_INIT_GEN; + tq->comp_ring.next2proc = 0; +} + + +void +vmxnet3_tq_destroy(struct vmxnet3_tx_queue *tq, + struct vmxnet3_adapter *adapter) +{ + if (tq->tx_ring.base) { + pci_free_consistent(adapter->pdev, tq->tx_ring.size * + sizeof(struct Vmxnet3_TxDesc), + tq->tx_ring.base, tq->tx_ring.basePA); + tq->tx_ring.base = NULL; + } + if (tq->data_ring.base) { + pci_free_consistent(adapter->pdev, tq->data_ring.size * + sizeof(struct Vmxnet3_TxDataDesc), + tq->data_ring.base, tq->data_ring.basePA); + tq->data_ring.base = NULL; + } + if (tq->comp_ring.base) { + pci_free_consistent(adapter->pdev, tq->comp_ring.size * + sizeof(struct Vmxnet3_TxCompDesc), + tq->comp_ring.base, tq->comp_ring.basePA); + tq->comp_ring.base = NULL; + } + kfree(tq->buf_info); + tq->buf_info = NULL; +} + + +static void +vmxnet3_tq_init(struct vmxnet3_tx_queue *tq, + struct vmxnet3_adapter *adapter) +{ + int i; + + /* reset the tx ring contents to 0 and reset the tx ring states */ + memset(tq->tx_ring.base, 0, tq->tx_ring.size * + sizeof(struct Vmxnet3_TxDesc)); + tq->tx_ring.next2fill = tq->tx_ring.next2comp = 0; + tq->tx_ring.gen = VMXNET3_INIT_GEN; + + memset(tq->data_ring.base, 0, tq->data_ring.size * + sizeof(struct Vmxnet3_TxDataDesc)); + + /* reset the tx comp ring contents to 0 and reset comp ring states */ + memset(tq->comp_ring.base, 0, tq->comp_ring.size * + sizeof(struct Vmxnet3_TxCompDesc)); + tq->comp_ring.next2proc = 0; + tq->comp_ring.gen = VMXNET3_INIT_GEN; + + /* reset the bookkeeping data */ + memset(tq->buf_info, 0, sizeof(tq->buf_info[0]) * tq->tx_ring.size); + for (i = 0; i < tq->tx_ring.size; i++) + tq->buf_info[i].map_type = VMXNET3_MAP_NONE; + + /* stats are not reset */ +} + + +static int +vmxnet3_tq_create(struct vmxnet3_tx_queue *tq, + struct vmxnet3_adapter *adapter) +{ + BUG_ON(tq->tx_ring.base || tq->data_ring.base || + tq->comp_ring.base || tq->buf_info); + + tq->tx_ring.base = pci_alloc_consistent(adapter->pdev, tq->tx_ring.size + * sizeof(struct Vmxnet3_TxDesc), + &tq->tx_ring.basePA); + if (!tq->tx_ring.base) { + printk(KERN_ERR "%s: failed to allocate tx ring\n", + adapter->netdev->name); + goto err; + } + + tq->data_ring.base = pci_alloc_consistent(adapter->pdev, + tq->data_ring.size * + sizeof(struct Vmxnet3_TxDataDesc), + &tq->data_ring.basePA); + if (!tq->data_ring.base) { + printk(KERN_ERR "%s: failed to allocate data ring\n", + adapter->netdev->name); + goto err; + } + + tq->comp_ring.base = pci_alloc_consistent(adapter->pdev, + tq->comp_ring.size * + sizeof(struct Vmxnet3_TxCompDesc), + &tq->comp_ring.basePA); + if (!tq->comp_ring.base) { + printk(KERN_ERR "%s: failed to allocate tx comp ring\n", + adapter->netdev->name); + goto err; + } + + tq->buf_info = kcalloc(tq->tx_ring.size, sizeof(tq->buf_info[0]), + GFP_KERNEL); + if (!tq->buf_info) { + printk(KERN_ERR "%s: failed to allocate tx bufinfo\n", + adapter->netdev->name); + goto err; + } + + return 0; + +err: + vmxnet3_tq_destroy(tq, adapter); + return -ENOMEM; +} + + +/* + * starting from ring->next2fill, allocate rx buffers for the given ring + * of the rx queue and update the rx desc. stop after @num_to_alloc buffers + * are allocated or allocation fails + */ + +static int +vmxnet3_rq_alloc_rx_buf(struct vmxnet3_rx_queue *rq, u32 ring_idx, + int num_to_alloc, struct vmxnet3_adapter *adapter) +{ + int num_allocated = 0; + struct vmxnet3_rx_buf_info *rbi_base = rq->buf_info[ring_idx]; + struct vmxnet3_cmd_ring *ring = &rq->rx_ring[ring_idx]; + u32 val; + + while (num_allocated < num_to_alloc) { + struct vmxnet3_rx_buf_info *rbi; + union Vmxnet3_GenericDesc *gd; + + rbi = rbi_base + ring->next2fill; + gd = ring->base + ring->next2fill; + + if (rbi->buf_type == VMXNET3_RX_BUF_SKB) { + if (rbi->skb == NULL) { + rbi->skb = dev_alloc_skb(rbi->len + + NET_IP_ALIGN); + if (unlikely(rbi->skb == NULL)) { + rq->stats.rx_buf_alloc_failure++; + break; + } + rbi->skb->dev = adapter->netdev; + + skb_reserve(rbi->skb, NET_IP_ALIGN); + rbi->dma_addr = pci_map_single(adapter->pdev, + rbi->skb->data, rbi->len, + PCI_DMA_FROMDEVICE); + } else { + /* rx buffer skipped by the device */ + } + val = VMXNET3_RXD_BTYPE_HEAD << VMXNET3_RXD_BTYPE_SHIFT; + } else { + BUG_ON(rbi->buf_type != VMXNET3_RX_BUF_PAGE || + rbi->len != PAGE_SIZE); + + if (rbi->page == NULL) { + rbi->page = alloc_page(GFP_ATOMIC); + if (unlikely(rbi->page == NULL)) { + rq->stats.rx_buf_alloc_failure++; + break; + } + rbi->dma_addr = pci_map_page(adapter->pdev, + rbi->page, 0, PAGE_SIZE, + PCI_DMA_FROMDEVICE); + } else { + /* rx buffers skipped by the device */ + } + val = VMXNET3_RXD_BTYPE_BODY << VMXNET3_RXD_BTYPE_SHIFT; + } + + BUG_ON(rbi->dma_addr == 0); + gd->rxd.addr = rbi->dma_addr; + gd->dword[2] = (ring->gen << VMXNET3_RXD_GEN_SHIFT) | val | + rbi->len; + + num_allocated++; + vmxnet3_cmd_ring_adv_next2fill(ring); + } + rq->uncommitted[ring_idx] += num_allocated; + + dev_dbg(&adapter->netdev->dev, + "alloc_rx_buf: %d allocated, next2fill %u, next2comp " + "%u, uncommited %u\n", num_allocated, ring->next2fill, + ring->next2comp, rq->uncommitted[ring_idx]); + + /* so that the device can distinguish a full ring and an empty ring */ + BUG_ON(num_allocated != 0 && ring->next2fill == ring->next2comp); + + return num_allocated; +} + + +static void +vmxnet3_append_frag(struct sk_buff *skb, struct Vmxnet3_RxCompDesc *rcd, + struct vmxnet3_rx_buf_info *rbi) +{ + struct skb_frag_struct *frag = skb_shinfo(skb)->frags + + skb_shinfo(skb)->nr_frags; + + BUG_ON(skb_shinfo(skb)->nr_frags >= MAX_SKB_FRAGS); + + frag->page = rbi->page; + frag->page_offset = 0; + frag->size = rcd->len; + skb->data_len += frag->size; + skb_shinfo(skb)->nr_frags++; +} + + +static void +vmxnet3_map_pkt(struct sk_buff *skb, struct vmxnet3_tx_ctx *ctx, + struct vmxnet3_tx_queue *tq, struct pci_dev *pdev, + struct vmxnet3_adapter *adapter) +{ + u32 dw2, len; + unsigned long buf_offset; + int i; + union Vmxnet3_GenericDesc *gdesc; + struct vmxnet3_tx_buf_info *tbi = NULL; + + BUG_ON(ctx->copy_size > skb_headlen(skb)); + + /* use the previous gen bit for the SOP desc */ + dw2 = (tq->tx_ring.gen ^ 0x1) << VMXNET3_TXD_GEN_SHIFT; + + ctx->sop_txd = tq->tx_ring.base + tq->tx_ring.next2fill; + gdesc = ctx->sop_txd; /* both loops below can be skipped */ + + /* no need to map the buffer if headers are copied */ + if (ctx->copy_size) { + ctx->sop_txd->txd.addr = tq->data_ring.basePA + + tq->tx_ring.next2fill * + sizeof(struct Vmxnet3_TxDataDesc); + ctx->sop_txd->dword[2] = dw2 | ctx->copy_size; + ctx->sop_txd->dword[3] = 0; + + tbi = tq->buf_info + tq->tx_ring.next2fill; + tbi->map_type = VMXNET3_MAP_NONE; + + dev_dbg(&adapter->netdev->dev, + "txd[%u]: 0x%Lx 0x%x 0x%x\n", + tq->tx_ring.next2fill, ctx->sop_txd->txd.addr, + ctx->sop_txd->dword[2], ctx->sop_txd->dword[3]); + vmxnet3_cmd_ring_adv_next2fill(&tq->tx_ring); + + /* use the right gen for non-SOP desc */ + dw2 = tq->tx_ring.gen << VMXNET3_TXD_GEN_SHIFT; + } + + /* linear part can use multiple tx desc if it's big */ + len = skb_headlen(skb) - ctx->copy_size; + buf_offset = ctx->copy_size; + while (len) { + u32 buf_size; + + buf_size = len > VMXNET3_MAX_TX_BUF_SIZE ? + VMXNET3_MAX_TX_BUF_SIZE : len; + + tbi = tq->buf_info + tq->tx_ring.next2fill; + tbi->map_type = VMXNET3_MAP_SINGLE; + tbi->dma_addr = pci_map_single(adapter->pdev, + skb->data + buf_offset, buf_size, + PCI_DMA_TODEVICE); + + tbi->len = buf_size; /* this automatically convert 2^14 to 0 */ + + gdesc = tq->tx_ring.base + tq->tx_ring.next2fill; + BUG_ON(gdesc->txd.gen == tq->tx_ring.gen); + + gdesc->txd.addr = tbi->dma_addr; + gdesc->dword[2] = dw2 | buf_size; + gdesc->dword[3] = 0; + + dev_dbg(&adapter->netdev->dev, + "txd[%u]: 0x%Lx 0x%x 0x%x\n", + tq->tx_ring.next2fill, gdesc->txd.addr, + gdesc->dword[2], gdesc->dword[3]); + vmxnet3_cmd_ring_adv_next2fill(&tq->tx_ring); + dw2 = tq->tx_ring.gen << VMXNET3_TXD_GEN_SHIFT; + + len -= buf_size; + buf_offset += buf_size; + } + + for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) { + struct skb_frag_struct *frag = &skb_shinfo(skb)->frags[i]; + + tbi = tq->buf_info + tq->tx_ring.next2fill; + tbi->map_type = VMXNET3_MAP_PAGE; + tbi->dma_addr = pci_map_page(adapter->pdev, frag->page, + frag->page_offset, frag->size, + PCI_DMA_TODEVICE); + + tbi->len = frag->size; + + gdesc = tq->tx_ring.base + tq->tx_ring.next2fill; + BUG_ON(gdesc->txd.gen == tq->tx_ring.gen); + + gdesc->txd.addr = tbi->dma_addr; + gdesc->dword[2] = dw2 | frag->size; + gdesc->dword[3] = 0; + + dev_dbg(&adapter->netdev->dev, + "txd[%u]: 0x%llu %u %u\n", + tq->tx_ring.next2fill, gdesc->txd.addr, + gdesc->dword[2], gdesc->dword[3]); + vmxnet3_cmd_ring_adv_next2fill(&tq->tx_ring); + dw2 = tq->tx_ring.gen << VMXNET3_TXD_GEN_SHIFT; + } + + ctx->eop_txd = gdesc; + + /* set the last buf_info for the pkt */ + tbi->skb = skb; + tbi->sop_idx = ctx->sop_txd - tq->tx_ring.base; +} + + +/* + * parse and copy relevant protocol headers: + * For a tso pkt, relevant headers are L2/3/4 including options + * For a pkt requesting csum offloading, they are L2/3 and may include L4 + * if it's a TCP/UDP pkt + * + * Returns: + * -1: error happens during parsing + * 0: protocol headers parsed, but too big to be copied + * 1: protocol headers parsed and copied + * + * Other effects: + * 1. related *ctx fields are updated. + * 2. ctx->copy_size is # of bytes copied + * 3. the portion copied is guaranteed to be in the linear part + * + */ +static int +vmxnet3_parse_and_copy_hdr(struct sk_buff *skb, struct vmxnet3_tx_queue *tq, + struct vmxnet3_tx_ctx *ctx, + struct vmxnet3_adapter *adapter) +{ + struct Vmxnet3_TxDataDesc *tdd; + + if (ctx->mss) { + ctx->eth_ip_hdr_size = skb_transport_offset(skb); + ctx->l4_hdr_size = ((struct tcphdr *) + skb_transport_header(skb))->doff * 4; + ctx->copy_size = ctx->eth_ip_hdr_size + ctx->l4_hdr_size; + } else { + unsigned int pull_size; + + if (skb->ip_summed == CHECKSUM_PARTIAL) { + ctx->eth_ip_hdr_size = skb_transport_offset(skb); + + if (ctx->ipv4) { + struct iphdr *iph = (struct iphdr *) + skb_network_header(skb); + if (iph->protocol == IPPROTO_TCP) { + pull_size = ctx->eth_ip_hdr_size + + sizeof(struct tcphdr); + + if (unlikely(!pskb_may_pull(skb, + pull_size))) { + goto err; + } + ctx->l4_hdr_size = ((struct tcphdr *) + skb_transport_header(skb))->doff * 4; + } else if (iph->protocol == IPPROTO_UDP) { + ctx->l4_hdr_size = + sizeof(struct udphdr); + } else { + ctx->l4_hdr_size = 0; + } + } else { + /* for simplicity, don't copy L4 headers */ + ctx->l4_hdr_size = 0; + } + ctx->copy_size = ctx->eth_ip_hdr_size + + ctx->l4_hdr_size; + } else { + ctx->eth_ip_hdr_size = 0; + ctx->l4_hdr_size = 0; + /* copy as much as allowed */ + ctx->copy_size = min((unsigned int)VMXNET3_HDR_COPY_SIZE + , skb_headlen(skb)); + } + + /* make sure headers are accessible directly */ + if (unlikely(!pskb_may_pull(skb, ctx->copy_size))) + goto err; + } + + if (unlikely(ctx->copy_size > VMXNET3_HDR_COPY_SIZE)) { + tq->stats.oversized_hdr++; + ctx->copy_size = 0; + return 0; + } + + tdd = tq->data_ring.base + tq->tx_ring.next2fill; + + memcpy(tdd->data, skb->data, ctx->copy_size); + dev_dbg(&adapter->netdev->dev, + "copy %u bytes to dataRing[%u]\n", + ctx->copy_size, tq->tx_ring.next2fill); + return 1; + +err: + return -1; +} + + +static void +vmxnet3_prepare_tso(struct sk_buff *skb, + struct vmxnet3_tx_ctx *ctx) +{ + struct tcphdr *tcph = (struct tcphdr *)skb_transport_header(skb); + if (ctx->ipv4) { + struct iphdr *iph = (struct iphdr *)skb_network_header(skb); + iph->check = 0; + tcph->check = ~csum_tcpudp_magic(iph->saddr, iph->daddr, 0, + IPPROTO_TCP, 0); + } else { + struct ipv6hdr *iph = (struct ipv6hdr *)skb_network_header(skb); + tcph->check = ~csum_ipv6_magic(&iph->saddr, &iph->daddr, 0, + IPPROTO_TCP, 0); + } +} + + +/* + * Transmits a pkt thru a given tq + * Returns: + * NETDEV_TX_OK: descriptors are setup successfully + * NETDEV_TX_OK: error occured, the pkt is dropped + * NETDEV_TX_BUSY: tx ring is full, queue is stopped + * + * Side-effects: + * 1. tx ring may be changed + * 2. tq stats may be updated accordingly + * 3. shared->txNumDeferred may be updated + */ + +static int +vmxnet3_tq_xmit(struct sk_buff *skb, struct vmxnet3_tx_queue *tq, + struct vmxnet3_adapter *adapter, struct net_device *netdev) +{ + int ret; + u32 count; + unsigned long flags; + struct vmxnet3_tx_ctx ctx; + union Vmxnet3_GenericDesc *gdesc; + + /* conservatively estimate # of descriptors to use */ + count = VMXNET3_TXD_NEEDED(skb_headlen(skb)) + + skb_shinfo(skb)->nr_frags + 1; + + ctx.ipv4 = (skb->protocol == __constant_ntohs(ETH_P_IP)); + + ctx.mss = skb_shinfo(skb)->gso_size; + if (ctx.mss) { + if (skb_header_cloned(skb)) { + if (unlikely(pskb_expand_head(skb, 0, 0, + GFP_ATOMIC) != 0)) { + tq->stats.drop_tso++; + goto drop_pkt; + } + tq->stats.copy_skb_header++; + } + vmxnet3_prepare_tso(skb, &ctx); + } else { + if (unlikely(count > VMXNET3_MAX_TXD_PER_PKT)) { + + /* non-tso pkts must not use more than + * VMXNET3_MAX_TXD_PER_PKT entries + */ + if (skb_linearize(skb) != 0) { + tq->stats.drop_too_many_frags++; + goto drop_pkt; + } + tq->stats.linearized++; + + /* recalculate the # of descriptors to use */ + count = VMXNET3_TXD_NEEDED(skb_headlen(skb)) + 1; + } + } + + ret = vmxnet3_parse_and_copy_hdr(skb, tq, &ctx, adapter); + if (ret >= 0) { + BUG_ON(ret <= 0 && ctx.copy_size != 0); + /* hdrs parsed, check against other limits */ + if (ctx.mss) { + if (unlikely(ctx.eth_ip_hdr_size + ctx.l4_hdr_size > + VMXNET3_MAX_TX_BUF_SIZE)) { + goto hdr_too_big; + } + } else { + if (skb->ip_summed == CHECKSUM_PARTIAL) { + if (unlikely(ctx.eth_ip_hdr_size + + skb->csum_offset > + VMXNET3_MAX_CSUM_OFFSET)) { + goto hdr_too_big; + } + } + } + } else { + tq->stats.drop_hdr_inspect_err++; + goto drop_pkt; + } + + spin_lock_irqsave(&tq->tx_lock, flags); + + if (count > vmxnet3_cmd_ring_desc_avail(&tq->tx_ring)) { + tq->stats.tx_ring_full++; + dev_dbg(&adapter->netdev->dev, + "tx queue stopped on %s, next2comp %u" + " next2fill %u\n", adapter->netdev->name, + tq->tx_ring.next2comp, tq->tx_ring.next2fill); + + vmxnet3_tq_stop(tq, adapter); + spin_unlock_irqrestore(&tq->tx_lock, flags); + return NETDEV_TX_BUSY; + } + + /* fill tx descs related to addr & len */ + vmxnet3_map_pkt(skb, &ctx, tq, adapter->pdev, adapter); + + /* setup the EOP desc */ + ctx.eop_txd->dword[3] = VMXNET3_TXD_CQ | VMXNET3_TXD_EOP; + + /* setup the SOP desc */ + gdesc = ctx.sop_txd; + if (ctx.mss) { + gdesc->txd.hlen = ctx.eth_ip_hdr_size + ctx.l4_hdr_size; + gdesc->txd.om = VMXNET3_OM_TSO; + gdesc->txd.msscof = ctx.mss; + tq->shared->txNumDeferred += (skb->len - gdesc->txd.hlen + + ctx.mss - 1) / ctx.mss; + } else { + if (skb->ip_summed == CHECKSUM_PARTIAL) { + gdesc->txd.hlen = ctx.eth_ip_hdr_size; + gdesc->txd.om = VMXNET3_OM_CSUM; + gdesc->txd.msscof = ctx.eth_ip_hdr_size + + skb->csum_offset; + } else { + gdesc->txd.om = 0; + gdesc->txd.msscof = 0; + } + tq->shared->txNumDeferred++; + } + + if (vlan_tx_tag_present(skb)) { + gdesc->txd.ti = 1; + gdesc->txd.tci = vlan_tx_tag_get(skb); + } + + wmb(); + + /* finally flips the GEN bit of the SOP desc */ + gdesc->dword[2] ^= VMXNET3_TXD_GEN; + dev_dbg(&adapter->netdev->dev, + "txd[%u]: SOP 0x%Lx 0x%x 0x%x\n", + (u32)((union Vmxnet3_GenericDesc *)ctx.sop_txd - + tq->tx_ring.base), gdesc->txd.addr, gdesc->dword[2], + gdesc->dword[3]); + + spin_unlock_irqrestore(&tq->tx_lock, flags); + + if (tq->shared->txNumDeferred >= tq->shared->txThreshold) { + tq->shared->txNumDeferred = 0; + VMXNET3_WRITE_BAR0_REG(adapter, VMXNET3_REG_TXPROD, + tq->tx_ring.next2fill); + } + netdev->trans_start = jiffies; + + return NETDEV_TX_OK; + +hdr_too_big: + tq->stats.drop_oversized_hdr++; +drop_pkt: + tq->stats.drop_total++; + dev_kfree_skb(skb); + return NETDEV_TX_OK; +} + + +static netdev_tx_t +vmxnet3_xmit_frame(struct sk_buff *skb, struct net_device *netdev) +{ + struct vmxnet3_adapter *adapter = netdev_priv(netdev); + struct vmxnet3_tx_queue *tq = &adapter->tx_queue; + + return vmxnet3_tq_xmit(skb, tq, adapter, netdev); +} + + +static void +vmxnet3_rx_csum(struct vmxnet3_adapter *adapter, + struct sk_buff *skb, + union Vmxnet3_GenericDesc *gdesc) +{ + if (!gdesc->rcd.cnc && adapter->rxcsum) { + /* typical case: TCP/UDP over IP and both csums are correct */ + if ((gdesc->dword[3] & VMXNET3_RCD_CSUM_OK) == + VMXNET3_RCD_CSUM_OK) { + skb->ip_summed = CHECKSUM_UNNECESSARY; + BUG_ON(!(gdesc->rcd.tcp || gdesc->rcd.udp)); + BUG_ON(!(gdesc->rcd.v4 || gdesc->rcd.v6)); + BUG_ON(gdesc->rcd.frg); + } else { + if (gdesc->rcd.csum) { + skb->csum = htons(gdesc->rcd.csum); + skb->ip_summed = CHECKSUM_PARTIAL; + } else { + skb->ip_summed = CHECKSUM_NONE; + } + } + } else { + skb->ip_summed = CHECKSUM_NONE; + } +} + + +static void +vmxnet3_rx_error(struct vmxnet3_rx_queue *rq, struct Vmxnet3_RxCompDesc *rcd, + struct vmxnet3_rx_ctx *ctx, struct vmxnet3_adapter *adapter) +{ + rq->stats.drop_err++; + if (!rcd->fcs) + rq->stats.drop_fcs++; + + rq->stats.drop_total++; + + /* + * We do not unmap and chain the rx buffer to the skb. + * We basically pretend this buffer is not used and will be recycled + * by vmxnet3_rq_alloc_rx_buf() + */ + + /* + * ctx->skb may be NULL if this is the first and the only one + * desc for the pkt + */ + if (ctx->skb) + dev_kfree_skb_irq(ctx->skb); + + ctx->skb = NULL; +} + + +static int +vmxnet3_rq_rx_complete(struct vmxnet3_rx_queue *rq, + struct vmxnet3_adapter *adapter, int quota) +{ + static u32 rxprod_reg[2] = {VMXNET3_REG_RXPROD, VMXNET3_REG_RXPROD2}; + u32 num_rxd = 0; + struct Vmxnet3_RxCompDesc *rcd; + struct vmxnet3_rx_ctx *ctx = &rq->rx_ctx; + + rcd = &rq->comp_ring.base[rq->comp_ring.next2proc].rcd; + while (rcd->gen == rq->comp_ring.gen) { + struct vmxnet3_rx_buf_info *rbi; + struct sk_buff *skb; + int num_to_alloc; + struct Vmxnet3_RxDesc *rxd; + u32 idx, ring_idx; + + if (num_rxd >= quota) { + /* we may stop even before we see the EOP desc of + * the current pkt + */ + break; + } + num_rxd++; + + idx = rcd->rxdIdx; + ring_idx = rcd->rqID == rq->qid ? 0 : 1; + + rxd = &rq->rx_ring[ring_idx].base[idx].rxd; + rbi = rq->buf_info[ring_idx] + idx; + + BUG_ON(rxd->addr != rbi->dma_addr || rxd->len != rbi->len); + + if (unlikely(rcd->eop && rcd->err)) { + vmxnet3_rx_error(rq, rcd, ctx, adapter); + goto rcd_done; + } + + if (rcd->sop) { /* first buf of the pkt */ + BUG_ON(rxd->btype != VMXNET3_RXD_BTYPE_HEAD || + rcd->rqID != rq->qid); + + BUG_ON(rbi->buf_type != VMXNET3_RX_BUF_SKB); + BUG_ON(ctx->skb != NULL || rbi->skb == NULL); + + if (unlikely(rcd->len == 0)) { + /* Pretend the rx buffer is skipped. */ + BUG_ON(!(rcd->sop && rcd->eop)); + dev_dbg(&adapter->netdev->dev, + "rxRing[%u][%u] 0 length\n", + ring_idx, idx); + goto rcd_done; + } + + ctx->skb = rbi->skb; + rbi->skb = NULL; + + pci_unmap_single(adapter->pdev, rbi->dma_addr, rbi->len, + PCI_DMA_FROMDEVICE); + + skb_put(ctx->skb, rcd->len); + } else { + BUG_ON(ctx->skb == NULL); + /* non SOP buffer must be type 1 in most cases */ + if (rbi->buf_type == VMXNET3_RX_BUF_PAGE) { + BUG_ON(rxd->btype != VMXNET3_RXD_BTYPE_BODY); + + if (rcd->len) { + pci_unmap_page(adapter->pdev, + rbi->dma_addr, rbi->len, + PCI_DMA_FROMDEVICE); + + vmxnet3_append_frag(ctx->skb, rcd, rbi); + rbi->page = NULL; + } + } else { + /* + * The only time a non-SOP buffer is type 0 is + * when it's EOP and error flag is raised, which + * has already been handled. + */ + BUG_ON(true); + } + } + + skb = ctx->skb; + if (rcd->eop) { + skb->len += skb->data_len; + skb->truesize += skb->data_len; + + vmxnet3_rx_csum(adapter, skb, + (union Vmxnet3_GenericDesc *)rcd); + skb->protocol = eth_type_trans(skb, adapter->netdev); + + if (unlikely(adapter->vlan_grp && rcd->ts)) { + vlan_hwaccel_receive_skb(skb, + adapter->vlan_grp, rcd->tci); + } else { + netif_receive_skb(skb); + } + + adapter->netdev->last_rx = jiffies; + ctx->skb = NULL; + } + +rcd_done: + /* device may skip some rx descs */ + rq->rx_ring[ring_idx].next2comp = idx; + VMXNET3_INC_RING_IDX_ONLY(rq->rx_ring[ring_idx].next2comp, + rq->rx_ring[ring_idx].size); + + /* refill rx buffers frequently to avoid starving the h/w */ + num_to_alloc = vmxnet3_cmd_ring_desc_avail(rq->rx_ring + + ring_idx); + if (unlikely(num_to_alloc > VMXNET3_RX_ALLOC_THRESHOLD(rq, + ring_idx, adapter))) { + vmxnet3_rq_alloc_rx_buf(rq, ring_idx, num_to_alloc, + adapter); + + /* if needed, update the register */ + if (unlikely(rq->shared->updateRxProd)) { + VMXNET3_WRITE_BAR0_REG(adapter, + rxprod_reg[ring_idx] + rq->qid * 8, + rq->rx_ring[ring_idx].next2fill); + rq->uncommitted[ring_idx] = 0; + } + } + + vmxnet3_comp_ring_adv_next2proc(&rq->comp_ring); + rcd = &rq->comp_ring.base[rq->comp_ring.next2proc].rcd; + } + + return num_rxd; +} + + +static void +vmxnet3_rq_cleanup(struct vmxnet3_rx_queue *rq, + struct vmxnet3_adapter *adapter) +{ + u32 i, ring_idx; + struct Vmxnet3_RxDesc *rxd; + + for (ring_idx = 0; ring_idx < 2; ring_idx++) { + for (i = 0; i < rq->rx_ring[ring_idx].size; i++) { + rxd = &rq->rx_ring[ring_idx].base[i].rxd; + + if (rxd->btype == VMXNET3_RXD_BTYPE_HEAD && + rq->buf_info[ring_idx][i].skb) { + pci_unmap_single(adapter->pdev, rxd->addr, + rxd->len, PCI_DMA_FROMDEVICE); + dev_kfree_skb(rq->buf_info[ring_idx][i].skb); + rq->buf_info[ring_idx][i].skb = NULL; + } else if (rxd->btype == VMXNET3_RXD_BTYPE_BODY && + rq->buf_info[ring_idx][i].page) { + pci_unmap_page(adapter->pdev, rxd->addr, + rxd->len, PCI_DMA_FROMDEVICE); + put_page(rq->buf_info[ring_idx][i].page); + rq->buf_info[ring_idx][i].page = NULL; + } + } + + rq->rx_ring[ring_idx].gen = VMXNET3_INIT_GEN; + rq->rx_ring[ring_idx].next2fill = + rq->rx_ring[ring_idx].next2comp = 0; + rq->uncommitted[ring_idx] = 0; + } + + rq->comp_ring.gen = VMXNET3_INIT_GEN; + rq->comp_ring.next2proc = 0; +} + + +void vmxnet3_rq_destroy(struct vmxnet3_rx_queue *rq, + struct vmxnet3_adapter *adapter) +{ + int i; + int j; + + /* all rx buffers must have already been freed */ + for (i = 0; i < 2; i++) { + if (rq->buf_info[i]) { + for (j = 0; j < rq->rx_ring[i].size; j++) + BUG_ON(rq->buf_info[i][j].page != NULL); + } + } + + + kfree(rq->buf_info[0]); + + for (i = 0; i < 2; i++) { + if (rq->rx_ring[i].base) { + pci_free_consistent(adapter->pdev, rq->rx_ring[i].size + * sizeof(struct Vmxnet3_RxDesc), + rq->rx_ring[i].base, + rq->rx_ring[i].basePA); + rq->rx_ring[i].base = NULL; + } + rq->buf_info[i] = NULL; + } + + if (rq->comp_ring.base) { + pci_free_consistent(adapter->pdev, rq->comp_ring.size * + sizeof(struct Vmxnet3_RxCompDesc), + rq->comp_ring.base, rq->comp_ring.basePA); + rq->comp_ring.base = NULL; + } +} + + +static int +vmxnet3_rq_init(struct vmxnet3_rx_queue *rq, + struct vmxnet3_adapter *adapter) +{ + int i; + + /* initialize buf_info */ + for (i = 0; i < rq->rx_ring[0].size; i++) { + + /* 1st buf for a pkt is skbuff */ + if (i % adapter->rx_buf_per_pkt == 0) { + rq->buf_info[0][i].buf_type = VMXNET3_RX_BUF_SKB; + rq->buf_info[0][i].len = adapter->skb_buf_size; + } else { /* subsequent bufs for a pkt is frag */ + rq->buf_info[0][i].buf_type = VMXNET3_RX_BUF_PAGE; + rq->buf_info[0][i].len = PAGE_SIZE; + } + } + for (i = 0; i < rq->rx_ring[1].size; i++) { + rq->buf_info[1][i].buf_type = VMXNET3_RX_BUF_PAGE; + rq->buf_info[1][i].len = PAGE_SIZE; + } + + /* reset internal state and allocate buffers for both rings */ + for (i = 0; i < 2; i++) { + rq->rx_ring[i].next2fill = rq->rx_ring[i].next2comp = 0; + rq->uncommitted[i] = 0; + + memset(rq->rx_ring[i].base, 0, rq->rx_ring[i].size * + sizeof(struct Vmxnet3_RxDesc)); + rq->rx_ring[i].gen = VMXNET3_INIT_GEN; + } + if (vmxnet3_rq_alloc_rx_buf(rq, 0, rq->rx_ring[0].size - 1, + adapter) == 0) { + /* at least has 1 rx buffer for the 1st ring */ + return -ENOMEM; + } + vmxnet3_rq_alloc_rx_buf(rq, 1, rq->rx_ring[1].size - 1, adapter); + + /* reset the comp ring */ + rq->comp_ring.next2proc = 0; + memset(rq->comp_ring.base, 0, rq->comp_ring.size * + sizeof(struct Vmxnet3_RxCompDesc)); + rq->comp_ring.gen = VMXNET3_INIT_GEN; + + /* reset rxctx */ + rq->rx_ctx.skb = NULL; + + /* stats are not reset */ + return 0; +} + + +static int +vmxnet3_rq_create(struct vmxnet3_rx_queue *rq, struct vmxnet3_adapter *adapter) +{ + int i; + size_t sz; + struct vmxnet3_rx_buf_info *bi; + + for (i = 0; i < 2; i++) { + + sz = rq->rx_ring[i].size * sizeof(struct Vmxnet3_RxDesc); + rq->rx_ring[i].base = pci_alloc_consistent(adapter->pdev, sz, + &rq->rx_ring[i].basePA); + if (!rq->rx_ring[i].base) { + printk(KERN_ERR "%s: failed to allocate rx ring %d\n", + adapter->netdev->name, i); + goto err; + } + } + + sz = rq->comp_ring.size * sizeof(struct Vmxnet3_RxCompDesc); + rq->comp_ring.base = pci_alloc_consistent(adapter->pdev, sz, + &rq->comp_ring.basePA); + if (!rq->comp_ring.base) { + printk(KERN_ERR "%s: failed to allocate rx comp ring\n", + adapter->netdev->name); + goto err; + } + + sz = sizeof(struct vmxnet3_rx_buf_info) * (rq->rx_ring[0].size + + rq->rx_ring[1].size); + bi = kmalloc(sz, GFP_KERNEL); + if (!bi) { + printk(KERN_ERR "%s: failed to allocate rx bufinfo\n", + adapter->netdev->name); + goto err; + } + memset(bi, 0, sz); + rq->buf_info[0] = bi; + rq->buf_info[1] = bi + rq->rx_ring[0].size; + + return 0; + +err: + vmxnet3_rq_destroy(rq, adapter); + return -ENOMEM; +} + + +static int +vmxnet3_do_poll(struct vmxnet3_adapter *adapter, int budget) +{ + if (unlikely(adapter->shared->ecr)) + vmxnet3_process_events(adapter); + + vmxnet3_tq_tx_complete(&adapter->tx_queue, adapter); + return vmxnet3_rq_rx_complete(&adapter->rx_queue, adapter, budget); +} + + +static int +vmxnet3_poll(struct napi_struct *napi, int budget) +{ + struct vmxnet3_adapter *adapter = container_of(napi, + struct vmxnet3_adapter, napi); + int rxd_done; + + rxd_done = vmxnet3_do_poll(adapter, budget); + + if (rxd_done < budget) { + napi_complete(napi); + vmxnet3_enable_intr(adapter, 0); + } + return rxd_done; +} + + +/* Interrupt handler for vmxnet3 */ +static irqreturn_t +vmxnet3_intr(int irq, void *dev_id) +{ + struct net_device *dev = dev_id; + struct vmxnet3_adapter *adapter = netdev_priv(dev); + + if (unlikely(adapter->intr.type == VMXNET3_IT_INTX)) { + u32 icr = VMXNET3_READ_BAR1_REG(adapter, VMXNET3_REG_ICR); + if (unlikely(icr == 0)) + /* not ours */ + return IRQ_NONE; + } + + + /* disable intr if needed */ + if (adapter->intr.mask_mode == VMXNET3_IMM_ACTIVE) + vmxnet3_disable_intr(adapter, 0); + + napi_schedule(&adapter->napi); + + return IRQ_HANDLED; +} + +#ifdef CONFIG_NET_POLL_CONTROLLER + + +/* netpoll callback. */ +static void +vmxnet3_netpoll(struct net_device *netdev) +{ + struct vmxnet3_adapter *adapter = netdev_priv(netdev); + int irq; + +#ifdef CONFIG_PCI_MSI + if (adapter->intr.type == VMXNET3_IT_MSIX) + irq = adapter->intr.msix_entries[0].vector; + else +#endif + irq = adapter->pdev->irq; + + disable_irq(irq); + vmxnet3_intr(irq, netdev); + enable_irq(irq); +} +#endif + +static int +vmxnet3_request_irqs(struct vmxnet3_adapter *adapter) +{ + int err; + +#ifdef CONFIG_PCI_MSI + if (adapter->intr.type == VMXNET3_IT_MSIX) { + /* we only use 1 MSI-X vector */ + err = request_irq(adapter->intr.msix_entries[0].vector, + vmxnet3_intr, 0, adapter->netdev->name, + adapter->netdev); + } else +#endif + if (adapter->intr.type == VMXNET3_IT_MSI) { + err = request_irq(adapter->pdev->irq, vmxnet3_intr, 0, + adapter->netdev->name, adapter->netdev); + } else { + err = request_irq(adapter->pdev->irq, vmxnet3_intr, + IRQF_SHARED, adapter->netdev->name, + adapter->netdev); + } + + if (err) + printk(KERN_ERR "Failed to request irq %s (intr type:%d), error" + ":%d\n", adapter->netdev->name, adapter->intr.type, err); + + + if (!err) { + int i; + /* init our intr settings */ + for (i = 0; i < adapter->intr.num_intrs; i++) + adapter->intr.mod_levels[i] = UPT1_IML_ADAPTIVE; + + /* next setup intr index for all intr sources */ + adapter->tx_queue.comp_ring.intr_idx = 0; + adapter->rx_queue.comp_ring.intr_idx = 0; + adapter->intr.event_intr_idx = 0; + + printk(KERN_INFO "%s: intr type %u, mode %u, %u vectors " + "allocated\n", adapter->netdev->name, adapter->intr.type, + adapter->intr.mask_mode, adapter->intr.num_intrs); + } + + return err; +} + + +static void +vmxnet3_free_irqs(struct vmxnet3_adapter *adapter) +{ + BUG_ON(adapter->intr.type == VMXNET3_IT_AUTO || + adapter->intr.num_intrs <= 0); + + switch (adapter->intr.type) { +#ifdef CONFIG_PCI_MSI + case VMXNET3_IT_MSIX: + { + int i; + + for (i = 0; i < adapter->intr.num_intrs; i++) + free_irq(adapter->intr.msix_entries[i].vector, + adapter->netdev); + break; + } +#endif + case VMXNET3_IT_MSI: + free_irq(adapter->pdev->irq, adapter->netdev); + break; + case VMXNET3_IT_INTX: + free_irq(adapter->pdev->irq, adapter->netdev); + break; + default: + BUG_ON(true); + } +} + + +static void +vmxnet3_vlan_rx_register(struct net_device *netdev, struct vlan_group *grp) +{ + struct vmxnet3_adapter *adapter = netdev_priv(netdev); + struct Vmxnet3_DriverShared *shared = adapter->shared; + u32 *vfTable = adapter->shared->devRead.rxFilterConf.vfTable; + + if (grp) { + /* add vlan rx stripping. */ + if (adapter->netdev->features & NETIF_F_HW_VLAN_RX) { + int i; + struct Vmxnet3_DSDevRead *devRead = &shared->devRead; + adapter->vlan_grp = grp; + + /* update FEATURES to device */ + devRead->misc.uptFeatures |= UPT1_F_RXVLAN; + VMXNET3_WRITE_BAR1_REG(adapter, VMXNET3_REG_CMD, + VMXNET3_CMD_UPDATE_FEATURE); + /* + * Clear entire vfTable; then enable untagged pkts. + * Note: setting one entry in vfTable to non-zero turns + * on VLAN rx filtering. + */ + for (i = 0; i < VMXNET3_VFT_SIZE; i++) + vfTable[i] = 0; + + VMXNET3_SET_VFTABLE_ENTRY(vfTable, 0); + VMXNET3_WRITE_BAR1_REG(adapter, VMXNET3_REG_CMD, + VMXNET3_CMD_UPDATE_VLAN_FILTERS); + } else { + printk(KERN_ERR "%s: vlan_rx_register when device has " + "no NETIF_F_HW_VLAN_RX\n", netdev->name); + } + } else { + /* remove vlan rx stripping. */ + struct Vmxnet3_DSDevRead *devRead = &shared->devRead; + adapter->vlan_grp = NULL; + + if (devRead->misc.uptFeatures & UPT1_F_RXVLAN) { + int i; + + for (i = 0; i < VMXNET3_VFT_SIZE; i++) { + /* clear entire vfTable; this also disables + * VLAN rx filtering + */ + vfTable[i] = 0; + } + VMXNET3_WRITE_BAR1_REG(adapter, VMXNET3_REG_CMD, + VMXNET3_CMD_UPDATE_VLAN_FILTERS); + + /* update FEATURES to device */ + devRead->misc.uptFeatures &= ~UPT1_F_RXVLAN; + VMXNET3_WRITE_BAR1_REG(adapter, VMXNET3_REG_CMD, + VMXNET3_CMD_UPDATE_FEATURE); + } + } +} + + +static void +vmxnet3_restore_vlan(struct vmxnet3_adapter *adapter) +{ + if (adapter->vlan_grp) { + u16 vid; + u32 *vfTable = adapter->shared->devRead.rxFilterConf.vfTable; + bool activeVlan = false; + + for (vid = 0; vid < VLAN_GROUP_ARRAY_LEN; vid++) { + if (vlan_group_get_device(adapter->vlan_grp, vid)) { + VMXNET3_SET_VFTABLE_ENTRY(vfTable, vid); + activeVlan = true; + } + } + if (activeVlan) { + /* continue to allow untagged pkts */ + VMXNET3_SET_VFTABLE_ENTRY(vfTable, 0); + } + } +} + + +static void +vmxnet3_vlan_rx_add_vid(struct net_device *netdev, u16 vid) +{ + struct vmxnet3_adapter *adapter = netdev_priv(netdev); + u32 *vfTable = adapter->shared->devRead.rxFilterConf.vfTable; + + VMXNET3_SET_VFTABLE_ENTRY(vfTable, vid); + VMXNET3_WRITE_BAR1_REG(adapter, VMXNET3_REG_CMD, + VMXNET3_CMD_UPDATE_VLAN_FILTERS); +} + + +static void +vmxnet3_vlan_rx_kill_vid(struct net_device *netdev, u16 vid) +{ + struct vmxnet3_adapter *adapter = netdev_priv(netdev); + u32 *vfTable = adapter->shared->devRead.rxFilterConf.vfTable; + + VMXNET3_CLEAR_VFTABLE_ENTRY(vfTable, vid); + VMXNET3_WRITE_BAR1_REG(adapter, VMXNET3_REG_CMD, + VMXNET3_CMD_UPDATE_VLAN_FILTERS); +} + + +static u8 * +vmxnet3_copy_mc(struct net_device *netdev) +{ + u8 *buf = NULL; + u32 sz = netdev->mc_count * ETH_ALEN; + + /* struct Vmxnet3_RxFilterConf.mfTableLen is u16. */ + if (sz <= 0xffff) { + /* We may be called with BH disabled */ + buf = kmalloc(sz, GFP_ATOMIC); + if (buf) { + int i; + struct dev_mc_list *mc = netdev->mc_list; + + for (i = 0; i < netdev->mc_count; i++) { + BUG_ON(!mc); + memcpy(buf + i * ETH_ALEN, mc->dmi_addr, + ETH_ALEN); + mc = mc->next; + } + } + } + return buf; +} + + +static void +vmxnet3_set_mc(struct net_device *netdev) +{ + struct vmxnet3_adapter *adapter = netdev_priv(netdev); + struct Vmxnet3_RxFilterConf *rxConf = + &adapter->shared->devRead.rxFilterConf; + u8 *new_table = NULL; + u32 new_mode = VMXNET3_RXM_UCAST; + + if (netdev->flags & IFF_PROMISC) + new_mode |= VMXNET3_RXM_PROMISC; + + if (netdev->flags & IFF_BROADCAST) + new_mode |= VMXNET3_RXM_BCAST; + + if (netdev->flags & IFF_ALLMULTI) + new_mode |= VMXNET3_RXM_ALL_MULTI; + else + if (netdev->mc_count > 0) { + new_table = vmxnet3_copy_mc(netdev); + if (new_table) { + new_mode |= VMXNET3_RXM_MCAST; + rxConf->mfTableLen = netdev->mc_count * + ETH_ALEN; + rxConf->mfTablePA = virt_to_phys(new_table); + } else { + printk(KERN_INFO "%s: failed to copy mcast list" + ", setting ALL_MULTI\n", netdev->name); + new_mode |= VMXNET3_RXM_ALL_MULTI; + } + } + + + if (!(new_mode & VMXNET3_RXM_MCAST)) { + rxConf->mfTableLen = 0; + rxConf->mfTablePA = 0; + } + + if (new_mode != rxConf->rxMode) { + rxConf->rxMode = new_mode; + VMXNET3_WRITE_BAR1_REG(adapter, VMXNET3_REG_CMD, + VMXNET3_CMD_UPDATE_RX_MODE); + } + + VMXNET3_WRITE_BAR1_REG(adapter, VMXNET3_REG_CMD, + VMXNET3_CMD_UPDATE_MAC_FILTERS); + + kfree(new_table); +} + + +/* + * Set up driver_shared based on settings in adapter. + */ + +static void +vmxnet3_setup_driver_shared(struct vmxnet3_adapter *adapter) +{ + struct Vmxnet3_DriverShared *shared = adapter->shared; + struct Vmxnet3_DSDevRead *devRead = &shared->devRead; + struct Vmxnet3_TxQueueConf *tqc; + struct Vmxnet3_RxQueueConf *rqc; + int i; + + memset(shared, 0, sizeof(*shared)); + + /* driver settings */ + shared->magic = VMXNET3_REV1_MAGIC; + devRead->misc.driverInfo.version = VMXNET3_DRIVER_VERSION_NUM; + devRead->misc.driverInfo.gos.gosBits = (sizeof(void *) == 4 ? + VMXNET3_GOS_BITS_32 : VMXNET3_GOS_BITS_64); + devRead->misc.driverInfo.gos.gosType = VMXNET3_GOS_TYPE_LINUX; + devRead->misc.driverInfo.vmxnet3RevSpt = 1; + devRead->misc.driverInfo.uptVerSpt = 1; + + devRead->misc.ddPA = virt_to_phys(adapter); + devRead->misc.ddLen = sizeof(struct vmxnet3_adapter); + + /* set up feature flags */ + if (adapter->rxcsum) + devRead->misc.uptFeatures |= UPT1_F_RXCSUM; + + if (adapter->lro) { + devRead->misc.uptFeatures |= UPT1_F_LRO; + devRead->misc.maxNumRxSG = 1 + MAX_SKB_FRAGS; + } + if ((adapter->netdev->features & NETIF_F_HW_VLAN_RX) + && adapter->vlan_grp) { + devRead->misc.uptFeatures |= UPT1_F_RXVLAN; + } + + devRead->misc.mtu = adapter->netdev->mtu; + devRead->misc.queueDescPA = adapter->queue_desc_pa; + devRead->misc.queueDescLen = sizeof(struct Vmxnet3_TxQueueDesc) + + sizeof(struct Vmxnet3_RxQueueDesc); + + /* tx queue settings */ + BUG_ON(adapter->tx_queue.tx_ring.base == NULL); + + devRead->misc.numTxQueues = 1; + tqc = &adapter->tqd_start->conf; + tqc->txRingBasePA = adapter->tx_queue.tx_ring.basePA; + tqc->dataRingBasePA = adapter->tx_queue.data_ring.basePA; + tqc->compRingBasePA = adapter->tx_queue.comp_ring.basePA; + tqc->ddPA = virt_to_phys(adapter->tx_queue.buf_info); + tqc->txRingSize = adapter->tx_queue.tx_ring.size; + tqc->dataRingSize = adapter->tx_queue.data_ring.size; + tqc->compRingSize = adapter->tx_queue.comp_ring.size; + tqc->ddLen = sizeof(struct vmxnet3_tx_buf_info) * + tqc->txRingSize; + tqc->intrIdx = adapter->tx_queue.comp_ring.intr_idx; + + /* rx queue settings */ + devRead->misc.numRxQueues = 1; + rqc = &adapter->rqd_start->conf; + rqc->rxRingBasePA[0] = adapter->rx_queue.rx_ring[0].basePA; + rqc->rxRingBasePA[1] = adapter->rx_queue.rx_ring[1].basePA; + rqc->compRingBasePA = adapter->rx_queue.comp_ring.basePA; + rqc->ddPA = virt_to_phys(adapter->rx_queue.buf_info); + rqc->rxRingSize[0] = adapter->rx_queue.rx_ring[0].size; + rqc->rxRingSize[1] = adapter->rx_queue.rx_ring[1].size; + rqc->compRingSize = adapter->rx_queue.comp_ring.size; + rqc->ddLen = sizeof(struct vmxnet3_rx_buf_info) * + (rqc->rxRingSize[0] + rqc->rxRingSize[1]); + rqc->intrIdx = adapter->rx_queue.comp_ring.intr_idx; + + /* intr settings */ + devRead->intrConf.autoMask = adapter->intr.mask_mode == + VMXNET3_IMM_AUTO; + devRead->intrConf.numIntrs = adapter->intr.num_intrs; + for (i = 0; i < adapter->intr.num_intrs; i++) + devRead->intrConf.modLevels[i] = adapter->intr.mod_levels[i]; + + devRead->intrConf.eventIntrIdx = adapter->intr.event_intr_idx; + + /* rx filter settings */ + devRead->rxFilterConf.rxMode = 0; + vmxnet3_restore_vlan(adapter); + /* the rest are already zeroed */ +} + + +int +vmxnet3_activate_dev(struct vmxnet3_adapter *adapter) +{ + int err; + u32 ret; + + dev_dbg(&adapter->netdev->dev, + "%s: skb_buf_size %d, rx_buf_per_pkt %d, ring sizes" + " %u %u %u\n", adapter->netdev->name, adapter->skb_buf_size, + adapter->rx_buf_per_pkt, adapter->tx_queue.tx_ring.size, + adapter->rx_queue.rx_ring[0].size, + adapter->rx_queue.rx_ring[1].size); + + vmxnet3_tq_init(&adapter->tx_queue, adapter); + err = vmxnet3_rq_init(&adapter->rx_queue, adapter); + if (err) { + printk(KERN_ERR "Failed to init rx queue for %s: error %d\n", + adapter->netdev->name, err); + goto rq_err; + } + + err = vmxnet3_request_irqs(adapter); + if (err) { + printk(KERN_ERR "Failed to setup irq for %s: error %d\n", + adapter->netdev->name, err); + goto irq_err; + } + + vmxnet3_setup_driver_shared(adapter); + + VMXNET3_WRITE_BAR1_REG(adapter, VMXNET3_REG_DSAL, + VMXNET3_GET_ADDR_LO(adapter->shared_pa)); + VMXNET3_WRITE_BAR1_REG(adapter, VMXNET3_REG_DSAH, + VMXNET3_GET_ADDR_HI(adapter->shared_pa)); + + VMXNET3_WRITE_BAR1_REG(adapter, VMXNET3_REG_CMD, + VMXNET3_CMD_ACTIVATE_DEV); + ret = VMXNET3_READ_BAR1_REG(adapter, VMXNET3_REG_CMD); + + if (ret != 0) { + printk(KERN_ERR "Failed to activate dev %s: error %u\n", + adapter->netdev->name, ret); + err = -EINVAL; + goto activate_err; + } + VMXNET3_WRITE_BAR0_REG(adapter, VMXNET3_REG_RXPROD, + adapter->rx_queue.rx_ring[0].next2fill); + VMXNET3_WRITE_BAR0_REG(adapter, VMXNET3_REG_RXPROD2, + adapter->rx_queue.rx_ring[1].next2fill); + + /* Apply the rx filter settins last. */ + vmxnet3_set_mc(adapter->netdev); + + /* + * Check link state when first activating device. It will start the + * tx queue if the link is up. + */ + vmxnet3_check_link(adapter); + + napi_enable(&adapter->napi); + vmxnet3_enable_all_intrs(adapter); + clear_bit(VMXNET3_STATE_BIT_QUIESCED, &adapter->state); + return 0; + +activate_err: + VMXNET3_WRITE_BAR1_REG(adapter, VMXNET3_REG_DSAL, 0); + VMXNET3_WRITE_BAR1_REG(adapter, VMXNET3_REG_DSAH, 0); + vmxnet3_free_irqs(adapter); +irq_err: +rq_err: + /* free up buffers we allocated */ + vmxnet3_rq_cleanup(&adapter->rx_queue, adapter); + return err; +} + + +void +vmxnet3_reset_dev(struct vmxnet3_adapter *adapter) +{ + VMXNET3_WRITE_BAR1_REG(adapter, VMXNET3_REG_CMD, VMXNET3_CMD_RESET_DEV); +} + + +int +vmxnet3_quiesce_dev(struct vmxnet3_adapter *adapter) +{ + if (test_and_set_bit(VMXNET3_STATE_BIT_QUIESCED, &adapter->state)) + return 0; + + + VMXNET3_WRITE_BAR1_REG(adapter, VMXNET3_REG_CMD, + VMXNET3_CMD_QUIESCE_DEV); + vmxnet3_disable_all_intrs(adapter); + + napi_disable(&adapter->napi); + netif_tx_disable(adapter->netdev); + adapter->link_speed = 0; + netif_carrier_off(adapter->netdev); + + vmxnet3_tq_cleanup(&adapter->tx_queue, adapter); + vmxnet3_rq_cleanup(&adapter->rx_queue, adapter); + vmxnet3_free_irqs(adapter); + return 0; +} + + +static void +vmxnet3_write_mac_addr(struct vmxnet3_adapter *adapter, u8 *mac) +{ + u32 tmp; + + tmp = *(u32 *)mac; + VMXNET3_WRITE_BAR1_REG(adapter, VMXNET3_REG_MACL, tmp); + + tmp = (mac[5] << 8) | mac[4]; + VMXNET3_WRITE_BAR1_REG(adapter, VMXNET3_REG_MACH, tmp); +} + + +static int +vmxnet3_set_mac_addr(struct net_device *netdev, void *p) +{ + struct sockaddr *addr = p; + struct vmxnet3_adapter *adapter = netdev_priv(netdev); + + memcpy(netdev->dev_addr, addr->sa_data, netdev->addr_len); + vmxnet3_write_mac_addr(adapter, addr->sa_data); + + return 0; +} + + +/* ==================== initialization and cleanup routines ============ */ + +static int +vmxnet3_alloc_pci_resources(struct vmxnet3_adapter *adapter, bool *dma64) +{ + int err; + unsigned long mmio_start, mmio_len; + struct pci_dev *pdev = adapter->pdev; + + err = pci_enable_device(pdev); + if (err) { + printk(KERN_ERR "Failed to enable adapter %s: error %d\n", + pci_name(pdev), err); + return err; + } + + if (pci_set_dma_mask(pdev, DMA_BIT_MASK(64)) == 0) { + if (pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(64)) != 0) { + printk(KERN_ERR "pci_set_consistent_dma_mask failed " + "for adapter %s\n", pci_name(pdev)); + err = -EIO; + goto err_set_mask; + } + *dma64 = true; + } else { + if (pci_set_dma_mask(pdev, DMA_BIT_MASK(32)) != 0) { + printk(KERN_ERR "pci_set_dma_mask failed for adapter " + "%s\n", pci_name(pdev)); + err = -EIO; + goto err_set_mask; + } + *dma64 = false; + } + + err = pci_request_selected_regions(pdev, (1 << 2) - 1, + vmxnet3_driver_name); + if (err) { + printk(KERN_ERR "Failed to request region for adapter %s: " + "error %d\n", pci_name(pdev), err); + goto err_set_mask; + } + + pci_set_master(pdev); + + mmio_start = pci_resource_start(pdev, 0); + mmio_len = pci_resource_len(pdev, 0); + adapter->hw_addr0 = ioremap(mmio_start, mmio_len); + if (!adapter->hw_addr0) { + printk(KERN_ERR "Failed to map bar0 for adapter %s\n", + pci_name(pdev)); + err = -EIO; + goto err_ioremap; + } + + mmio_start = pci_resource_start(pdev, 1); + mmio_len = pci_resource_len(pdev, 1); + adapter->hw_addr1 = ioremap(mmio_start, mmio_len); + if (!adapter->hw_addr1) { + printk(KERN_ERR "Failed to map bar1 for adapter %s\n", + pci_name(pdev)); + err = -EIO; + goto err_bar1; + } + return 0; + +err_bar1: + iounmap(adapter->hw_addr0); +err_ioremap: + pci_release_selected_regions(pdev, (1 << 2) - 1); +err_set_mask: + pci_disable_device(pdev); + return err; +} + + +static void +vmxnet3_free_pci_resources(struct vmxnet3_adapter *adapter) +{ + BUG_ON(!adapter->pdev); + + iounmap(adapter->hw_addr0); + iounmap(adapter->hw_addr1); + pci_release_selected_regions(adapter->pdev, (1 << 2) - 1); + pci_disable_device(adapter->pdev); +} + + +static void +vmxnet3_adjust_rx_ring_size(struct vmxnet3_adapter *adapter) +{ + size_t sz; + + if (adapter->netdev->mtu <= VMXNET3_MAX_SKB_BUF_SIZE - + VMXNET3_MAX_ETH_HDR_SIZE) { + adapter->skb_buf_size = adapter->netdev->mtu + + VMXNET3_MAX_ETH_HDR_SIZE; + if (adapter->skb_buf_size < VMXNET3_MIN_T0_BUF_SIZE) + adapter->skb_buf_size = VMXNET3_MIN_T0_BUF_SIZE; + + adapter->rx_buf_per_pkt = 1; + } else { + adapter->skb_buf_size = VMXNET3_MAX_SKB_BUF_SIZE; + sz = adapter->netdev->mtu - VMXNET3_MAX_SKB_BUF_SIZE + + VMXNET3_MAX_ETH_HDR_SIZE; + adapter->rx_buf_per_pkt = 1 + (sz + PAGE_SIZE - 1) / PAGE_SIZE; + } + + /* + * for simplicity, force the ring0 size to be a multiple of + * rx_buf_per_pkt * VMXNET3_RING_SIZE_ALIGN + */ + sz = adapter->rx_buf_per_pkt * VMXNET3_RING_SIZE_ALIGN; + adapter->rx_queue.rx_ring[0].size = (adapter->rx_queue.rx_ring[0].size + + sz - 1) / sz * sz; + adapter->rx_queue.rx_ring[0].size = min_t(u32, + adapter->rx_queue.rx_ring[0].size, + VMXNET3_RX_RING_MAX_SIZE / sz * sz); +} + + +int +vmxnet3_create_queues(struct vmxnet3_adapter *adapter, u32 tx_ring_size, + u32 rx_ring_size, u32 rx_ring2_size) +{ + int err; + + adapter->tx_queue.tx_ring.size = tx_ring_size; + adapter->tx_queue.data_ring.size = tx_ring_size; + adapter->tx_queue.comp_ring.size = tx_ring_size; + adapter->tx_queue.shared = &adapter->tqd_start->ctrl; + adapter->tx_queue.stopped = true; + err = vmxnet3_tq_create(&adapter->tx_queue, adapter); + if (err) + return err; + + adapter->rx_queue.rx_ring[0].size = rx_ring_size; + adapter->rx_queue.rx_ring[1].size = rx_ring2_size; + vmxnet3_adjust_rx_ring_size(adapter); + adapter->rx_queue.comp_ring.size = adapter->rx_queue.rx_ring[0].size + + adapter->rx_queue.rx_ring[1].size; + adapter->rx_queue.qid = 0; + adapter->rx_queue.qid2 = 1; + adapter->rx_queue.shared = &adapter->rqd_start->ctrl; + err = vmxnet3_rq_create(&adapter->rx_queue, adapter); + if (err) + vmxnet3_tq_destroy(&adapter->tx_queue, adapter); + + return err; +} + +static int +vmxnet3_open(struct net_device *netdev) +{ + struct vmxnet3_adapter *adapter; + int err; + + adapter = netdev_priv(netdev); + + spin_lock_init(&adapter->tx_queue.tx_lock); + + err = vmxnet3_create_queues(adapter, VMXNET3_DEF_TX_RING_SIZE, + VMXNET3_DEF_RX_RING_SIZE, + VMXNET3_DEF_RX_RING_SIZE); + if (err) + goto queue_err; + + err = vmxnet3_activate_dev(adapter); + if (err) + goto activate_err; + + return 0; + +activate_err: + vmxnet3_rq_destroy(&adapter->rx_queue, adapter); + vmxnet3_tq_destroy(&adapter->tx_queue, adapter); +queue_err: + return err; +} + + +static int +vmxnet3_close(struct net_device *netdev) +{ + struct vmxnet3_adapter *adapter = netdev_priv(netdev); + + /* + * Reset_work may be in the middle of resetting the device, wait for its + * completion. + */ + while (test_and_set_bit(VMXNET3_STATE_BIT_RESETTING, &adapter->state)) + msleep(1); + + vmxnet3_quiesce_dev(adapter); + + vmxnet3_rq_destroy(&adapter->rx_queue, adapter); + vmxnet3_tq_destroy(&adapter->tx_queue, adapter); + + clear_bit(VMXNET3_STATE_BIT_RESETTING, &adapter->state); + + + return 0; +} + + +void +vmxnet3_force_close(struct vmxnet3_adapter *adapter) +{ + /* + * we must clear VMXNET3_STATE_BIT_RESETTING, otherwise + * vmxnet3_close() will deadlock. + */ + BUG_ON(test_bit(VMXNET3_STATE_BIT_RESETTING, &adapter->state)); + + /* we need to enable NAPI, otherwise dev_close will deadlock */ + napi_enable(&adapter->napi); + dev_close(adapter->netdev); +} + + +static int +vmxnet3_change_mtu(struct net_device *netdev, int new_mtu) +{ + struct vmxnet3_adapter *adapter = netdev_priv(netdev); + int err = 0; + + if (new_mtu < VMXNET3_MIN_MTU || new_mtu > VMXNET3_MAX_MTU) + return -EINVAL; + + if (new_mtu > 1500 && !adapter->jumbo_frame) + return -EINVAL; + + netdev->mtu = new_mtu; + + /* + * Reset_work may be in the middle of resetting the device, wait for its + * completion. + */ + while (test_and_set_bit(VMXNET3_STATE_BIT_RESETTING, &adapter->state)) + msleep(1); + + if (netif_running(netdev)) { + vmxnet3_quiesce_dev(adapter); + vmxnet3_reset_dev(adapter); + + /* we need to re-create the rx queue based on the new mtu */ + vmxnet3_rq_destroy(&adapter->rx_queue, adapter); + vmxnet3_adjust_rx_ring_size(adapter); + adapter->rx_queue.comp_ring.size = + adapter->rx_queue.rx_ring[0].size + + adapter->rx_queue.rx_ring[1].size; + err = vmxnet3_rq_create(&adapter->rx_queue, adapter); + if (err) { + printk(KERN_ERR "%s: failed to re-create rx queue," + " error %d. Closing it.\n", netdev->name, err); + goto out; + } + + err = vmxnet3_activate_dev(adapter); + if (err) { + printk(KERN_ERR "%s: failed to re-activate, error %d. " + "Closing it\n", netdev->name, err); + goto out; + } + } + +out: + clear_bit(VMXNET3_STATE_BIT_RESETTING, &adapter->state); + if (err) + vmxnet3_force_close(adapter); + + return err; +} + + +static void +vmxnet3_declare_features(struct vmxnet3_adapter *adapter, bool dma64) +{ + struct net_device *netdev = adapter->netdev; + + netdev->features = NETIF_F_SG | + NETIF_F_HW_CSUM | + NETIF_F_HW_VLAN_TX | + NETIF_F_HW_VLAN_RX | + NETIF_F_HW_VLAN_FILTER | + NETIF_F_TSO | + NETIF_F_TSO6 | + NETIF_F_LRO; + + printk(KERN_INFO "features: sg csum vlan jf tso tsoIPv6 lro"); + + adapter->rxcsum = true; + adapter->jumbo_frame = true; + adapter->lro = true; + + if (dma64) { + netdev->features |= NETIF_F_HIGHDMA; + printk(" highDMA"); + } + + netdev->vlan_features = netdev->features; + printk("\n"); +} + + +static void +vmxnet3_read_mac_addr(struct vmxnet3_adapter *adapter, u8 *mac) +{ + u32 tmp; + + tmp = VMXNET3_READ_BAR1_REG(adapter, VMXNET3_REG_MACL); + *(u32 *)mac = tmp; + + tmp = VMXNET3_READ_BAR1_REG(adapter, VMXNET3_REG_MACH); + mac[4] = tmp & 0xff; + mac[5] = (tmp >> 8) & 0xff; +} + + +static void +vmxnet3_alloc_intr_resources(struct vmxnet3_adapter *adapter) +{ + u32 cfg; + + /* intr settings */ + VMXNET3_WRITE_BAR1_REG(adapter, VMXNET3_REG_CMD, + VMXNET3_CMD_GET_CONF_INTR); + cfg = VMXNET3_READ_BAR1_REG(adapter, VMXNET3_REG_CMD); + adapter->intr.type = cfg & 0x3; + adapter->intr.mask_mode = (cfg >> 2) & 0x3; + + if (adapter->intr.type == VMXNET3_IT_AUTO) { + int err; + +#ifdef CONFIG_PCI_MSI + adapter->intr.msix_entries[0].entry = 0; + err = pci_enable_msix(adapter->pdev, adapter->intr.msix_entries, + VMXNET3_LINUX_MAX_MSIX_VECT); + if (!err) { + adapter->intr.num_intrs = 1; + adapter->intr.type = VMXNET3_IT_MSIX; + return; + } +#endif + + err = pci_enable_msi(adapter->pdev); + if (!err) { + adapter->intr.num_intrs = 1; + adapter->intr.type = VMXNET3_IT_MSI; + return; + } + } + + adapter->intr.type = VMXNET3_IT_INTX; + + /* INT-X related setting */ + adapter->intr.num_intrs = 1; +} + + +static void +vmxnet3_free_intr_resources(struct vmxnet3_adapter *adapter) +{ + if (adapter->intr.type == VMXNET3_IT_MSIX) + pci_disable_msix(adapter->pdev); + else if (adapter->intr.type == VMXNET3_IT_MSI) + pci_disable_msi(adapter->pdev); + else + BUG_ON(adapter->intr.type != VMXNET3_IT_INTX); +} + + +static void +vmxnet3_tx_timeout(struct net_device *netdev) +{ + struct vmxnet3_adapter *adapter = netdev_priv(netdev); + adapter->tx_timeout_count++; + + printk(KERN_ERR "%s: tx hang\n", adapter->netdev->name); + schedule_work(&adapter->work); +} + + +static void +vmxnet3_reset_work(struct work_struct *data) +{ + struct vmxnet3_adapter *adapter; + + adapter = container_of(data, struct vmxnet3_adapter, work); + + /* if another thread is resetting the device, no need to proceed */ + if (test_and_set_bit(VMXNET3_STATE_BIT_RESETTING, &adapter->state)) + return; + + /* if the device is closed, we must leave it alone */ + if (netif_running(adapter->netdev)) { + printk(KERN_INFO "%s: resetting\n", adapter->netdev->name); + vmxnet3_quiesce_dev(adapter); + vmxnet3_reset_dev(adapter); + vmxnet3_activate_dev(adapter); + } else { + printk(KERN_INFO "%s: already closed\n", adapter->netdev->name); + } + + clear_bit(VMXNET3_STATE_BIT_RESETTING, &adapter->state); +} + + +static int __devinit +vmxnet3_probe_device(struct pci_dev *pdev, + const struct pci_device_id *id) +{ + static const struct net_device_ops vmxnet3_netdev_ops = { + .ndo_open = vmxnet3_open, + .ndo_stop = vmxnet3_close, + .ndo_start_xmit = vmxnet3_xmit_frame, + .ndo_set_mac_address = vmxnet3_set_mac_addr, + .ndo_change_mtu = vmxnet3_change_mtu, + .ndo_get_stats = vmxnet3_get_stats, + .ndo_tx_timeout = vmxnet3_tx_timeout, + .ndo_set_multicast_list = vmxnet3_set_mc, + .ndo_vlan_rx_register = vmxnet3_vlan_rx_register, + .ndo_vlan_rx_add_vid = vmxnet3_vlan_rx_add_vid, + .ndo_vlan_rx_kill_vid = vmxnet3_vlan_rx_kill_vid, +#ifdef CONFIG_NET_POLL_CONTROLLER + .ndo_poll_controller = vmxnet3_netpoll, +#endif + }; + int err; + bool dma64 = false; /* stupid gcc */ + u32 ver; + struct net_device *netdev; + struct vmxnet3_adapter *adapter; + u8 mac[ETH_ALEN]; + + netdev = alloc_etherdev(sizeof(struct vmxnet3_adapter)); + if (!netdev) { + printk(KERN_ERR "Failed to alloc ethernet device for adapter " + "%s\n", pci_name(pdev)); + return -ENOMEM; + } + + pci_set_drvdata(pdev, netdev); + adapter = netdev_priv(netdev); + adapter->netdev = netdev; + adapter->pdev = pdev; + + adapter->shared = pci_alloc_consistent(adapter->pdev, + sizeof(struct Vmxnet3_DriverShared), + &adapter->shared_pa); + if (!adapter->shared) { + printk(KERN_ERR "Failed to allocate memory for %s\n", + pci_name(pdev)); + err = -ENOMEM; + goto err_alloc_shared; + } + + adapter->tqd_start = pci_alloc_consistent(adapter->pdev, + sizeof(struct Vmxnet3_TxQueueDesc) + + sizeof(struct Vmxnet3_RxQueueDesc), + &adapter->queue_desc_pa); + + if (!adapter->tqd_start) { + printk(KERN_ERR "Failed to allocate memory for %s\n", + pci_name(pdev)); + err = -ENOMEM; + goto err_alloc_queue_desc; + } + adapter->rqd_start = (struct Vmxnet3_RxQueueDesc *)(adapter->tqd_start + + 1); + + adapter->pm_conf = kmalloc(sizeof(struct Vmxnet3_PMConf), GFP_KERNEL); + if (adapter->pm_conf == NULL) { + printk(KERN_ERR "Failed to allocate memory for %s\n", + pci_name(pdev)); + err = -ENOMEM; + goto err_alloc_pm; + } + + err = vmxnet3_alloc_pci_resources(adapter, &dma64); + if (err < 0) + goto err_alloc_pci; + + ver = VMXNET3_READ_BAR1_REG(adapter, VMXNET3_REG_VRRS); + if (ver & 1) { + VMXNET3_WRITE_BAR1_REG(adapter, VMXNET3_REG_VRRS, 1); + } else { + printk(KERN_ERR "Incompatible h/w version (0x%x) for adapter" + " %s\n", ver, pci_name(pdev)); + err = -EBUSY; + goto err_ver; + } + + ver = VMXNET3_READ_BAR1_REG(adapter, VMXNET3_REG_UVRS); + if (ver & 1) { + VMXNET3_WRITE_BAR1_REG(adapter, VMXNET3_REG_UVRS, 1); + } else { + printk(KERN_ERR "Incompatible upt version (0x%x) for " + "adapter %s\n", ver, pci_name(pdev)); + err = -EBUSY; + goto err_ver; + } + + vmxnet3_declare_features(adapter, dma64); + + adapter->dev_number = atomic_read(&devices_found); + vmxnet3_alloc_intr_resources(adapter); + + vmxnet3_read_mac_addr(adapter, mac); + memcpy(netdev->dev_addr, mac, netdev->addr_len); + + netdev->netdev_ops = &vmxnet3_netdev_ops; + netdev->watchdog_timeo = 5 * HZ; + vmxnet3_set_ethtool_ops(netdev); + + INIT_WORK(&adapter->work, vmxnet3_reset_work); + + netif_napi_add(netdev, &adapter->napi, vmxnet3_poll, 64); + SET_NETDEV_DEV(netdev, &pdev->dev); + err = register_netdev(netdev); + + if (err) { + printk(KERN_ERR "Failed to register adapter %s\n", + pci_name(pdev)); + goto err_register; + } + + set_bit(VMXNET3_STATE_BIT_QUIESCED, &adapter->state); + atomic_inc(&devices_found); + return 0; + +err_register: + vmxnet3_free_intr_resources(adapter); +err_ver: + vmxnet3_free_pci_resources(adapter); +err_alloc_pci: + kfree(adapter->pm_conf); +err_alloc_pm: + pci_free_consistent(adapter->pdev, sizeof(struct Vmxnet3_TxQueueDesc) + + sizeof(struct Vmxnet3_RxQueueDesc), + adapter->tqd_start, adapter->queue_desc_pa); +err_alloc_queue_desc: + pci_free_consistent(adapter->pdev, sizeof(struct Vmxnet3_DriverShared), + adapter->shared, adapter->shared_pa); +err_alloc_shared: + pci_set_drvdata(pdev, NULL); + free_netdev(netdev); + return err; +} + + +static void __devexit +vmxnet3_remove_device(struct pci_dev *pdev) +{ + struct net_device *netdev = pci_get_drvdata(pdev); + struct vmxnet3_adapter *adapter = netdev_priv(netdev); + + flush_scheduled_work(); + + unregister_netdev(netdev); + + vmxnet3_free_intr_resources(adapter); + vmxnet3_free_pci_resources(adapter); + kfree(adapter->pm_conf); + pci_free_consistent(adapter->pdev, sizeof(struct Vmxnet3_TxQueueDesc) + + sizeof(struct Vmxnet3_RxQueueDesc), + adapter->tqd_start, adapter->queue_desc_pa); + pci_free_consistent(adapter->pdev, sizeof(struct Vmxnet3_DriverShared), + adapter->shared, adapter->shared_pa); + free_netdev(netdev); +} + + +#ifdef CONFIG_PM + +static int +vmxnet3_suspend(struct device *device) +{ + struct pci_dev *pdev = to_pci_dev(device); + struct net_device *netdev = pci_get_drvdata(pdev); + struct vmxnet3_adapter *adapter = netdev_priv(netdev); + struct Vmxnet3_PMConf *pmConf; + struct ethhdr *ehdr; + struct arphdr *ahdr; + u8 *arpreq; + struct in_device *in_dev; + struct in_ifaddr *ifa; + int i = 0; + + if (!netif_running(netdev)) + return 0; + + vmxnet3_disable_all_intrs(adapter); + vmxnet3_free_irqs(adapter); + vmxnet3_free_intr_resources(adapter); + + netif_device_detach(netdev); + netif_stop_queue(netdev); + + /* Create wake-up filters. */ + pmConf = adapter->pm_conf; + memset(pmConf, 0, sizeof(*pmConf)); + + if (adapter->wol & WAKE_UCAST) { + pmConf->filters[i].patternSize = ETH_ALEN; + pmConf->filters[i].maskSize = 1; + memcpy(pmConf->filters[i].pattern, netdev->dev_addr, ETH_ALEN); + pmConf->filters[i].mask[0] = 0x3F; /* LSB ETH_ALEN bits */ + + pmConf->wakeUpEvents |= VMXNET3_PM_WAKEUP_FILTER; + i++; + } + + if (adapter->wol & WAKE_ARP) { + in_dev = in_dev_get(netdev); + if (!in_dev) + goto skip_arp; + + ifa = (struct in_ifaddr *)in_dev->ifa_list; + if (!ifa) + goto skip_arp; + + pmConf->filters[i].patternSize = ETH_HLEN + /* Ethernet header*/ + sizeof(struct arphdr) + /* ARP header */ + 2 * ETH_ALEN + /* 2 Ethernet addresses*/ + 2 * sizeof(u32); /*2 IPv4 addresses */ + pmConf->filters[i].maskSize = + (pmConf->filters[i].patternSize - 1) / 8 + 1; + + /* ETH_P_ARP in Ethernet header. */ + ehdr = (struct ethhdr *)pmConf->filters[i].pattern; + ehdr->h_proto = htons(ETH_P_ARP); + + /* ARPOP_REQUEST in ARP header. */ + ahdr = (struct arphdr *)&pmConf->filters[i].pattern[ETH_HLEN]; + ahdr->ar_op = htons(ARPOP_REQUEST); + arpreq = (u8 *)(ahdr + 1); + + /* The Unicast IPv4 address in 'tip' field. */ + arpreq += 2 * ETH_ALEN + sizeof(u32); + *(u32 *)arpreq = ifa->ifa_address; + + /* The mask for the relevant bits. */ + pmConf->filters[i].mask[0] = 0x00; + pmConf->filters[i].mask[1] = 0x30; /* ETH_P_ARP */ + pmConf->filters[i].mask[2] = 0x30; /* ARPOP_REQUEST */ + pmConf->filters[i].mask[3] = 0x00; + pmConf->filters[i].mask[4] = 0xC0; /* IPv4 TIP */ + pmConf->filters[i].mask[5] = 0x03; /* IPv4 TIP */ + in_dev_put(in_dev); + + pmConf->wakeUpEvents |= VMXNET3_PM_WAKEUP_FILTER; + i++; + } + +skip_arp: + if (adapter->wol & WAKE_MAGIC) + pmConf->wakeUpEvents |= VMXNET3_PM_WAKEUP_MAGIC; + + pmConf->numFilters = i; + + adapter->shared->devRead.pmConfDesc.confVer = 1; + adapter->shared->devRead.pmConfDesc.confLen = sizeof(*pmConf); + adapter->shared->devRead.pmConfDesc.confPA = virt_to_phys(pmConf); + + VMXNET3_WRITE_BAR1_REG(adapter, VMXNET3_REG_CMD, + VMXNET3_CMD_UPDATE_PMCFG); + + pci_save_state(pdev); + pci_enable_wake(pdev, pci_choose_state(pdev, PMSG_SUSPEND), + adapter->wol); + pci_disable_device(pdev); + pci_set_power_state(pdev, pci_choose_state(pdev, PMSG_SUSPEND)); + + return 0; +} + + +static int +vmxnet3_resume(struct device *device) +{ + int err; + struct pci_dev *pdev = to_pci_dev(device); + struct net_device *netdev = pci_get_drvdata(pdev); + struct vmxnet3_adapter *adapter = netdev_priv(netdev); + struct Vmxnet3_PMConf *pmConf; + + if (!netif_running(netdev)) + return 0; + + /* Destroy wake-up filters. */ + pmConf = adapter->pm_conf; + memset(pmConf, 0, sizeof(*pmConf)); + + adapter->shared->devRead.pmConfDesc.confVer = 1; + adapter->shared->devRead.pmConfDesc.confLen = sizeof(*pmConf); + adapter->shared->devRead.pmConfDesc.confPA = virt_to_phys(pmConf); + + netif_device_attach(netdev); + pci_set_power_state(pdev, PCI_D0); + pci_restore_state(pdev); + err = pci_enable_device_mem(pdev); + if (err != 0) + return err; + + pci_enable_wake(pdev, PCI_D0, 0); + + VMXNET3_WRITE_BAR1_REG(adapter, VMXNET3_REG_CMD, + VMXNET3_CMD_UPDATE_PMCFG); + vmxnet3_alloc_intr_resources(adapter); + vmxnet3_request_irqs(adapter); + vmxnet3_enable_all_intrs(adapter); + + return 0; +} + +static struct dev_pm_ops vmxnet3_pm_ops = { + .suspend = vmxnet3_suspend, + .resume = vmxnet3_resume, +}; +#endif + +static struct pci_driver vmxnet3_driver = { + .name = vmxnet3_driver_name, + .id_table = vmxnet3_pciid_table, + .probe = vmxnet3_probe_device, + .remove = __devexit_p(vmxnet3_remove_device), +#ifdef CONFIG_PM + .driver.pm = &vmxnet3_pm_ops, +#endif +}; + + +static int __init +vmxnet3_init_module(void) +{ + printk(KERN_INFO "%s - version %s\n", VMXNET3_DRIVER_DESC, + VMXNET3_DRIVER_VERSION_REPORT); + return pci_register_driver(&vmxnet3_driver); +} + +module_init(vmxnet3_init_module); + + +static void +vmxnet3_exit_module(void) +{ + pci_unregister_driver(&vmxnet3_driver); +} + +module_exit(vmxnet3_exit_module); + +MODULE_AUTHOR("VMware, Inc."); +MODULE_DESCRIPTION(VMXNET3_DRIVER_DESC); +MODULE_LICENSE("GPL v2"); +MODULE_VERSION(VMXNET3_DRIVER_VERSION_STRING); diff --git a/drivers/net/vmxnet3/vmxnet3_ethtool.c b/drivers/net/vmxnet3/vmxnet3_ethtool.c new file mode 100644 index 000000000000..c2c15e4cafc7 --- /dev/null +++ b/drivers/net/vmxnet3/vmxnet3_ethtool.c @@ -0,0 +1,566 @@ +/* + * Linux driver for VMware's vmxnet3 ethernet NIC. + * + * Copyright (C) 2008-2009, VMware, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; version 2 of the License and no later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or + * NON INFRINGEMENT. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * The full GNU General Public License is included in this distribution in + * the file called "COPYING". + * + * Maintained by: Shreyas Bhatewara <pv-drivers@vmware.com> + * + */ + + +#include "vmxnet3_int.h" + +struct vmxnet3_stat_desc { + char desc[ETH_GSTRING_LEN]; + int offset; +}; + + +static u32 +vmxnet3_get_rx_csum(struct net_device *netdev) +{ + struct vmxnet3_adapter *adapter = netdev_priv(netdev); + return adapter->rxcsum; +} + + +static int +vmxnet3_set_rx_csum(struct net_device *netdev, u32 val) +{ + struct vmxnet3_adapter *adapter = netdev_priv(netdev); + + if (adapter->rxcsum != val) { + adapter->rxcsum = val; + if (netif_running(netdev)) { + if (val) + adapter->shared->devRead.misc.uptFeatures |= + UPT1_F_RXCSUM; + else + adapter->shared->devRead.misc.uptFeatures &= + ~UPT1_F_RXCSUM; + + VMXNET3_WRITE_BAR1_REG(adapter, VMXNET3_REG_CMD, + VMXNET3_CMD_UPDATE_FEATURE); + } + } + return 0; +} + + +/* per tq stats maintained by the device */ +static const struct vmxnet3_stat_desc +vmxnet3_tq_dev_stats[] = { + /* description, offset */ + { "TSO pkts tx", offsetof(struct UPT1_TxStats, TSOPktsTxOK) }, + { "TSO bytes tx", offsetof(struct UPT1_TxStats, TSOBytesTxOK) }, + { "ucast pkts tx", offsetof(struct UPT1_TxStats, ucastPktsTxOK) }, + { "ucast bytes tx", offsetof(struct UPT1_TxStats, ucastBytesTxOK) }, + { "mcast pkts tx", offsetof(struct UPT1_TxStats, mcastPktsTxOK) }, + { "mcast bytes tx", offsetof(struct UPT1_TxStats, mcastBytesTxOK) }, + { "bcast pkts tx", offsetof(struct UPT1_TxStats, bcastPktsTxOK) }, + { "bcast bytes tx", offsetof(struct UPT1_TxStats, bcastBytesTxOK) }, + { "pkts tx err", offsetof(struct UPT1_TxStats, pktsTxError) }, + { "pkts tx discard", offsetof(struct UPT1_TxStats, pktsTxDiscard) }, +}; + +/* per tq stats maintained by the driver */ +static const struct vmxnet3_stat_desc +vmxnet3_tq_driver_stats[] = { + /* description, offset */ + {"drv dropped tx total", offsetof(struct vmxnet3_tq_driver_stats, + drop_total) }, + { " too many frags", offsetof(struct vmxnet3_tq_driver_stats, + drop_too_many_frags) }, + { " giant hdr", offsetof(struct vmxnet3_tq_driver_stats, + drop_oversized_hdr) }, + { " hdr err", offsetof(struct vmxnet3_tq_driver_stats, + drop_hdr_inspect_err) }, + { " tso", offsetof(struct vmxnet3_tq_driver_stats, + drop_tso) }, + { "ring full", offsetof(struct vmxnet3_tq_driver_stats, + tx_ring_full) }, + { "pkts linearized", offsetof(struct vmxnet3_tq_driver_stats, + linearized) }, + { "hdr cloned", offsetof(struct vmxnet3_tq_driver_stats, + copy_skb_header) }, + { "giant hdr", offsetof(struct vmxnet3_tq_driver_stats, + oversized_hdr) }, +}; + +/* per rq stats maintained by the device */ +static const struct vmxnet3_stat_desc +vmxnet3_rq_dev_stats[] = { + { "LRO pkts rx", offsetof(struct UPT1_RxStats, LROPktsRxOK) }, + { "LRO byte rx", offsetof(struct UPT1_RxStats, LROBytesRxOK) }, + { "ucast pkts rx", offsetof(struct UPT1_RxStats, ucastPktsRxOK) }, + { "ucast bytes rx", offsetof(struct UPT1_RxStats, ucastBytesRxOK) }, + { "mcast pkts rx", offsetof(struct UPT1_RxStats, mcastPktsRxOK) }, + { "mcast bytes rx", offsetof(struct UPT1_RxStats, mcastBytesRxOK) }, + { "bcast pkts rx", offsetof(struct UPT1_RxStats, bcastPktsRxOK) }, + { "bcast bytes rx", offsetof(struct UPT1_RxStats, bcastBytesRxOK) }, + { "pkts rx out of buf", offsetof(struct UPT1_RxStats, pktsRxOutOfBuf) }, + { "pkts rx err", offsetof(struct UPT1_RxStats, pktsRxError) }, +}; + +/* per rq stats maintained by the driver */ +static const struct vmxnet3_stat_desc +vmxnet3_rq_driver_stats[] = { + /* description, offset */ + { "drv dropped rx total", offsetof(struct vmxnet3_rq_driver_stats, + drop_total) }, + { " err", offsetof(struct vmxnet3_rq_driver_stats, + drop_err) }, + { " fcs", offsetof(struct vmxnet3_rq_driver_stats, + drop_fcs) }, + { "rx buf alloc fail", offsetof(struct vmxnet3_rq_driver_stats, + rx_buf_alloc_failure) }, +}; + +/* gloabl stats maintained by the driver */ +static const struct vmxnet3_stat_desc +vmxnet3_global_stats[] = { + /* description, offset */ + { "tx timeout count", offsetof(struct vmxnet3_adapter, + tx_timeout_count) } +}; + + +struct net_device_stats * +vmxnet3_get_stats(struct net_device *netdev) +{ + struct vmxnet3_adapter *adapter; + struct vmxnet3_tq_driver_stats *drvTxStats; + struct vmxnet3_rq_driver_stats *drvRxStats; + struct UPT1_TxStats *devTxStats; + struct UPT1_RxStats *devRxStats; + struct net_device_stats *net_stats = &netdev->stats; + + adapter = netdev_priv(netdev); + + /* Collect the dev stats into the shared area */ + VMXNET3_WRITE_BAR1_REG(adapter, VMXNET3_REG_CMD, VMXNET3_CMD_GET_STATS); + + /* Assuming that we have a single queue device */ + devTxStats = &adapter->tqd_start->stats; + devRxStats = &adapter->rqd_start->stats; + + /* Get access to the driver stats per queue */ + drvTxStats = &adapter->tx_queue.stats; + drvRxStats = &adapter->rx_queue.stats; + + memset(net_stats, 0, sizeof(*net_stats)); + + net_stats->rx_packets = devRxStats->ucastPktsRxOK + + devRxStats->mcastPktsRxOK + + devRxStats->bcastPktsRxOK; + + net_stats->tx_packets = devTxStats->ucastPktsTxOK + + devTxStats->mcastPktsTxOK + + devTxStats->bcastPktsTxOK; + + net_stats->rx_bytes = devRxStats->ucastBytesRxOK + + devRxStats->mcastBytesRxOK + + devRxStats->bcastBytesRxOK; + + net_stats->tx_bytes = devTxStats->ucastBytesTxOK + + devTxStats->mcastBytesTxOK + + devTxStats->bcastBytesTxOK; + + net_stats->rx_errors = devRxStats->pktsRxError; + net_stats->tx_errors = devTxStats->pktsTxError; + net_stats->rx_dropped = drvRxStats->drop_total; + net_stats->tx_dropped = drvTxStats->drop_total; + net_stats->multicast = devRxStats->mcastPktsRxOK; + + return net_stats; +} + +static int +vmxnet3_get_sset_count(struct net_device *netdev, int sset) +{ + switch (sset) { + case ETH_SS_STATS: + return ARRAY_SIZE(vmxnet3_tq_dev_stats) + + ARRAY_SIZE(vmxnet3_tq_driver_stats) + + ARRAY_SIZE(vmxnet3_rq_dev_stats) + + ARRAY_SIZE(vmxnet3_rq_driver_stats) + + ARRAY_SIZE(vmxnet3_global_stats); + default: + return -EOPNOTSUPP; + } +} + + +static int +vmxnet3_get_regs_len(struct net_device *netdev) +{ + return 20 * sizeof(u32); +} + + +static void +vmxnet3_get_drvinfo(struct net_device *netdev, struct ethtool_drvinfo *drvinfo) +{ + struct vmxnet3_adapter *adapter = netdev_priv(netdev); + + strlcpy(drvinfo->driver, vmxnet3_driver_name, sizeof(drvinfo->driver)); + drvinfo->driver[sizeof(drvinfo->driver) - 1] = '\0'; + + strlcpy(drvinfo->version, VMXNET3_DRIVER_VERSION_REPORT, + sizeof(drvinfo->version)); + drvinfo->driver[sizeof(drvinfo->version) - 1] = '\0'; + + strlcpy(drvinfo->fw_version, "N/A", sizeof(drvinfo->fw_version)); + drvinfo->fw_version[sizeof(drvinfo->fw_version) - 1] = '\0'; + + strlcpy(drvinfo->bus_info, pci_name(adapter->pdev), + ETHTOOL_BUSINFO_LEN); + drvinfo->n_stats = vmxnet3_get_sset_count(netdev, ETH_SS_STATS); + drvinfo->testinfo_len = 0; + drvinfo->eedump_len = 0; + drvinfo->regdump_len = vmxnet3_get_regs_len(netdev); +} + + +static void +vmxnet3_get_strings(struct net_device *netdev, u32 stringset, u8 *buf) +{ + if (stringset == ETH_SS_STATS) { + int i; + + for (i = 0; i < ARRAY_SIZE(vmxnet3_tq_dev_stats); i++) { + memcpy(buf, vmxnet3_tq_dev_stats[i].desc, + ETH_GSTRING_LEN); + buf += ETH_GSTRING_LEN; + } + for (i = 0; i < ARRAY_SIZE(vmxnet3_tq_driver_stats); i++) { + memcpy(buf, vmxnet3_tq_driver_stats[i].desc, + ETH_GSTRING_LEN); + buf += ETH_GSTRING_LEN; + } + for (i = 0; i < ARRAY_SIZE(vmxnet3_rq_dev_stats); i++) { + memcpy(buf, vmxnet3_rq_dev_stats[i].desc, + ETH_GSTRING_LEN); + buf += ETH_GSTRING_LEN; + } + for (i = 0; i < ARRAY_SIZE(vmxnet3_rq_driver_stats); i++) { + memcpy(buf, vmxnet3_rq_driver_stats[i].desc, + ETH_GSTRING_LEN); + buf += ETH_GSTRING_LEN; + } + for (i = 0; i < ARRAY_SIZE(vmxnet3_global_stats); i++) { + memcpy(buf, vmxnet3_global_stats[i].desc, + ETH_GSTRING_LEN); + buf += ETH_GSTRING_LEN; + } + } +} + +static u32 +vmxnet3_get_flags(struct net_device *netdev) { + return netdev->features; +} + +static int +vmxnet3_set_flags(struct net_device *netdev, u32 data) { + struct vmxnet3_adapter *adapter = netdev_priv(netdev); + u8 lro_requested = (data & ETH_FLAG_LRO) == 0 ? 0 : 1; + u8 lro_present = (netdev->features & NETIF_F_LRO) == 0 ? 0 : 1; + + if (lro_requested ^ lro_present) { + /* toggle the LRO feature*/ + netdev->features ^= NETIF_F_LRO; + + /* update harware LRO capability accordingly */ + if (lro_requested) + adapter->shared->devRead.misc.uptFeatures &= UPT1_F_LRO; + else + adapter->shared->devRead.misc.uptFeatures &= + ~UPT1_F_LRO; + VMXNET3_WRITE_BAR1_REG(adapter, VMXNET3_REG_CMD, + VMXNET3_CMD_UPDATE_FEATURE); + } + return 0; +} + +static void +vmxnet3_get_ethtool_stats(struct net_device *netdev, + struct ethtool_stats *stats, u64 *buf) +{ + struct vmxnet3_adapter *adapter = netdev_priv(netdev); + u8 *base; + int i; + + VMXNET3_WRITE_BAR1_REG(adapter, VMXNET3_REG_CMD, VMXNET3_CMD_GET_STATS); + + /* this does assume each counter is 64-bit wide */ + + base = (u8 *)&adapter->tqd_start->stats; + for (i = 0; i < ARRAY_SIZE(vmxnet3_tq_dev_stats); i++) + *buf++ = *(u64 *)(base + vmxnet3_tq_dev_stats[i].offset); + + base = (u8 *)&adapter->tx_queue.stats; + for (i = 0; i < ARRAY_SIZE(vmxnet3_tq_driver_stats); i++) + *buf++ = *(u64 *)(base + vmxnet3_tq_driver_stats[i].offset); + + base = (u8 *)&adapter->rqd_start->stats; + for (i = 0; i < ARRAY_SIZE(vmxnet3_rq_dev_stats); i++) + *buf++ = *(u64 *)(base + vmxnet3_rq_dev_stats[i].offset); + + base = (u8 *)&adapter->rx_queue.stats; + for (i = 0; i < ARRAY_SIZE(vmxnet3_rq_driver_stats); i++) + *buf++ = *(u64 *)(base + vmxnet3_rq_driver_stats[i].offset); + + base = (u8 *)adapter; + for (i = 0; i < ARRAY_SIZE(vmxnet3_global_stats); i++) + *buf++ = *(u64 *)(base + vmxnet3_global_stats[i].offset); +} + + +static void +vmxnet3_get_regs(struct net_device *netdev, struct ethtool_regs *regs, void *p) +{ + struct vmxnet3_adapter *adapter = netdev_priv(netdev); + u32 *buf = p; + + memset(p, 0, vmxnet3_get_regs_len(netdev)); + + regs->version = 1; + + /* Update vmxnet3_get_regs_len if we want to dump more registers */ + + /* make each ring use multiple of 16 bytes */ + buf[0] = adapter->tx_queue.tx_ring.next2fill; + buf[1] = adapter->tx_queue.tx_ring.next2comp; + buf[2] = adapter->tx_queue.tx_ring.gen; + buf[3] = 0; + + buf[4] = adapter->tx_queue.comp_ring.next2proc; + buf[5] = adapter->tx_queue.comp_ring.gen; + buf[6] = adapter->tx_queue.stopped; + buf[7] = 0; + + buf[8] = adapter->rx_queue.rx_ring[0].next2fill; + buf[9] = adapter->rx_queue.rx_ring[0].next2comp; + buf[10] = adapter->rx_queue.rx_ring[0].gen; + buf[11] = 0; + + buf[12] = adapter->rx_queue.rx_ring[1].next2fill; + buf[13] = adapter->rx_queue.rx_ring[1].next2comp; + buf[14] = adapter->rx_queue.rx_ring[1].gen; + buf[15] = 0; + + buf[16] = adapter->rx_queue.comp_ring.next2proc; + buf[17] = adapter->rx_queue.comp_ring.gen; + buf[18] = 0; + buf[19] = 0; +} + + +static void +vmxnet3_get_wol(struct net_device *netdev, struct ethtool_wolinfo *wol) +{ + struct vmxnet3_adapter *adapter = netdev_priv(netdev); + + wol->supported = WAKE_UCAST | WAKE_ARP | WAKE_MAGIC; + wol->wolopts = adapter->wol; +} + + +static int +vmxnet3_set_wol(struct net_device *netdev, struct ethtool_wolinfo *wol) +{ + struct vmxnet3_adapter *adapter = netdev_priv(netdev); + + if (wol->wolopts & (WAKE_PHY | WAKE_MCAST | WAKE_BCAST | + WAKE_MAGICSECURE)) { + return -EOPNOTSUPP; + } + + adapter->wol = wol->wolopts; + + device_set_wakeup_enable(&adapter->pdev->dev, adapter->wol); + + return 0; +} + + +static int +vmxnet3_get_settings(struct net_device *netdev, struct ethtool_cmd *ecmd) +{ + struct vmxnet3_adapter *adapter = netdev_priv(netdev); + + ecmd->supported = SUPPORTED_10000baseT_Full | SUPPORTED_1000baseT_Full | + SUPPORTED_TP; + ecmd->advertising = ADVERTISED_TP; + ecmd->port = PORT_TP; + ecmd->transceiver = XCVR_INTERNAL; + + if (adapter->link_speed) { + ecmd->speed = adapter->link_speed; + ecmd->duplex = DUPLEX_FULL; + } else { + ecmd->speed = -1; + ecmd->duplex = -1; + } + return 0; +} + + +static void +vmxnet3_get_ringparam(struct net_device *netdev, + struct ethtool_ringparam *param) +{ + struct vmxnet3_adapter *adapter = netdev_priv(netdev); + + param->rx_max_pending = VMXNET3_RX_RING_MAX_SIZE; + param->tx_max_pending = VMXNET3_TX_RING_MAX_SIZE; + param->rx_mini_max_pending = 0; + param->rx_jumbo_max_pending = 0; + + param->rx_pending = adapter->rx_queue.rx_ring[0].size; + param->tx_pending = adapter->tx_queue.tx_ring.size; + param->rx_mini_pending = 0; + param->rx_jumbo_pending = 0; +} + + +static int +vmxnet3_set_ringparam(struct net_device *netdev, + struct ethtool_ringparam *param) +{ + struct vmxnet3_adapter *adapter = netdev_priv(netdev); + u32 new_tx_ring_size, new_rx_ring_size; + u32 sz; + int err = 0; + + if (param->tx_pending == 0 || param->tx_pending > + VMXNET3_TX_RING_MAX_SIZE) + return -EINVAL; + + if (param->rx_pending == 0 || param->rx_pending > + VMXNET3_RX_RING_MAX_SIZE) + return -EINVAL; + + + /* round it up to a multiple of VMXNET3_RING_SIZE_ALIGN */ + new_tx_ring_size = (param->tx_pending + VMXNET3_RING_SIZE_MASK) & + ~VMXNET3_RING_SIZE_MASK; + new_tx_ring_size = min_t(u32, new_tx_ring_size, + VMXNET3_TX_RING_MAX_SIZE); + if (new_tx_ring_size > VMXNET3_TX_RING_MAX_SIZE || (new_tx_ring_size % + VMXNET3_RING_SIZE_ALIGN) != 0) + return -EINVAL; + + /* ring0 has to be a multiple of + * rx_buf_per_pkt * VMXNET3_RING_SIZE_ALIGN + */ + sz = adapter->rx_buf_per_pkt * VMXNET3_RING_SIZE_ALIGN; + new_rx_ring_size = (param->rx_pending + sz - 1) / sz * sz; + new_rx_ring_size = min_t(u32, new_rx_ring_size, + VMXNET3_RX_RING_MAX_SIZE / sz * sz); + if (new_rx_ring_size > VMXNET3_RX_RING_MAX_SIZE || (new_rx_ring_size % + sz) != 0) + return -EINVAL; + + if (new_tx_ring_size == adapter->tx_queue.tx_ring.size && + new_rx_ring_size == adapter->rx_queue.rx_ring[0].size) { + return 0; + } + + /* + * Reset_work may be in the middle of resetting the device, wait for its + * completion. + */ + while (test_and_set_bit(VMXNET3_STATE_BIT_RESETTING, &adapter->state)) + msleep(1); + + if (netif_running(netdev)) { + vmxnet3_quiesce_dev(adapter); + vmxnet3_reset_dev(adapter); + + /* recreate the rx queue and the tx queue based on the + * new sizes */ + vmxnet3_tq_destroy(&adapter->tx_queue, adapter); + vmxnet3_rq_destroy(&adapter->rx_queue, adapter); + + err = vmxnet3_create_queues(adapter, new_tx_ring_size, + new_rx_ring_size, VMXNET3_DEF_RX_RING_SIZE); + if (err) { + /* failed, most likely because of OOM, try default + * size */ + printk(KERN_ERR "%s: failed to apply new sizes, try the" + " default ones\n", netdev->name); + err = vmxnet3_create_queues(adapter, + VMXNET3_DEF_TX_RING_SIZE, + VMXNET3_DEF_RX_RING_SIZE, + VMXNET3_DEF_RX_RING_SIZE); + if (err) { + printk(KERN_ERR "%s: failed to create queues " + "with default sizes. Closing it\n", + netdev->name); + goto out; + } + } + + err = vmxnet3_activate_dev(adapter); + if (err) + printk(KERN_ERR "%s: failed to re-activate, error %d." + " Closing it\n", netdev->name, err); + } + +out: + clear_bit(VMXNET3_STATE_BIT_RESETTING, &adapter->state); + if (err) + vmxnet3_force_close(adapter); + + return err; +} + + +static struct ethtool_ops vmxnet3_ethtool_ops = { + .get_settings = vmxnet3_get_settings, + .get_drvinfo = vmxnet3_get_drvinfo, + .get_regs_len = vmxnet3_get_regs_len, + .get_regs = vmxnet3_get_regs, + .get_wol = vmxnet3_get_wol, + .set_wol = vmxnet3_set_wol, + .get_link = ethtool_op_get_link, + .get_rx_csum = vmxnet3_get_rx_csum, + .set_rx_csum = vmxnet3_set_rx_csum, + .get_tx_csum = ethtool_op_get_tx_csum, + .set_tx_csum = ethtool_op_set_tx_hw_csum, + .get_sg = ethtool_op_get_sg, + .set_sg = ethtool_op_set_sg, + .get_tso = ethtool_op_get_tso, + .set_tso = ethtool_op_set_tso, + .get_strings = vmxnet3_get_strings, + .get_flags = vmxnet3_get_flags, + .set_flags = vmxnet3_set_flags, + .get_sset_count = vmxnet3_get_sset_count, + .get_ethtool_stats = vmxnet3_get_ethtool_stats, + .get_ringparam = vmxnet3_get_ringparam, + .set_ringparam = vmxnet3_set_ringparam, +}; + +void vmxnet3_set_ethtool_ops(struct net_device *netdev) +{ + SET_ETHTOOL_OPS(netdev, &vmxnet3_ethtool_ops); +} diff --git a/drivers/net/vmxnet3/vmxnet3_int.h b/drivers/net/vmxnet3/vmxnet3_int.h new file mode 100644 index 000000000000..3c0d70d58111 --- /dev/null +++ b/drivers/net/vmxnet3/vmxnet3_int.h @@ -0,0 +1,389 @@ +/* + * Linux driver for VMware's vmxnet3 ethernet NIC. + * + * Copyright (C) 2008-2009, VMware, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; version 2 of the License and no later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or + * NON INFRINGEMENT. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * The full GNU General Public License is included in this distribution in + * the file called "COPYING". + * + * Maintained by: Shreyas Bhatewara <pv-drivers@vmware.com> + * + */ + +#ifndef _VMXNET3_INT_H +#define _VMXNET3_INT_H + +#include <linux/types.h> +#include <linux/ethtool.h> +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/netdevice.h> +#include <linux/pci.h> +#include <linux/ethtool.h> +#include <linux/compiler.h> +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/slab.h> +#include <linux/spinlock.h> +#include <linux/ioport.h> +#include <linux/highmem.h> +#include <linux/init.h> +#include <linux/timer.h> +#include <linux/skbuff.h> +#include <linux/interrupt.h> +#include <linux/workqueue.h> +#include <linux/uaccess.h> +#include <asm/dma.h> +#include <asm/page.h> + +#include <linux/tcp.h> +#include <linux/udp.h> +#include <linux/ip.h> +#include <linux/ipv6.h> +#include <linux/in.h> +#include <linux/etherdevice.h> +#include <asm/checksum.h> +#include <linux/if_vlan.h> +#include <linux/if_arp.h> +#include <linux/inetdevice.h> + +#include "vmxnet3_defs.h" + +#ifdef DEBUG +# define VMXNET3_DRIVER_VERSION_REPORT VMXNET3_DRIVER_VERSION_STRING"-NAPI(debug)" +#else +# define VMXNET3_DRIVER_VERSION_REPORT VMXNET3_DRIVER_VERSION_STRING"-NAPI" +#endif + + +/* + * Version numbers + */ +#define VMXNET3_DRIVER_VERSION_STRING "1.0.5.0-k" + +/* a 32-bit int, each byte encode a verion number in VMXNET3_DRIVER_VERSION */ +#define VMXNET3_DRIVER_VERSION_NUM 0x01000500 + + +/* + * Capabilities + */ + +enum { + VMNET_CAP_SG = 0x0001, /* Can do scatter-gather transmits. */ + VMNET_CAP_IP4_CSUM = 0x0002, /* Can checksum only TCP/UDP over + * IPv4 */ + VMNET_CAP_HW_CSUM = 0x0004, /* Can checksum all packets. */ + VMNET_CAP_HIGH_DMA = 0x0008, /* Can DMA to high memory. */ + VMNET_CAP_TOE = 0x0010, /* Supports TCP/IP offload. */ + VMNET_CAP_TSO = 0x0020, /* Supports TCP Segmentation + * offload */ + VMNET_CAP_SW_TSO = 0x0040, /* Supports SW TCP Segmentation */ + VMNET_CAP_VMXNET_APROM = 0x0080, /* Vmxnet APROM support */ + VMNET_CAP_HW_TX_VLAN = 0x0100, /* Can we do VLAN tagging in HW */ + VMNET_CAP_HW_RX_VLAN = 0x0200, /* Can we do VLAN untagging in HW */ + VMNET_CAP_SW_VLAN = 0x0400, /* VLAN tagging/untagging in SW */ + VMNET_CAP_WAKE_PCKT_RCV = 0x0800, /* Can wake on network packet recv? */ + VMNET_CAP_ENABLE_INT_INLINE = 0x1000, /* Enable Interrupt Inline */ + VMNET_CAP_ENABLE_HEADER_COPY = 0x2000, /* copy header for vmkernel */ + VMNET_CAP_TX_CHAIN = 0x4000, /* Guest can use multiple tx entries + * for a pkt */ + VMNET_CAP_RX_CHAIN = 0x8000, /* pkt can span multiple rx entries */ + VMNET_CAP_LPD = 0x10000, /* large pkt delivery */ + VMNET_CAP_BPF = 0x20000, /* BPF Support in VMXNET Virtual HW*/ + VMNET_CAP_SG_SPAN_PAGES = 0x40000, /* Scatter-gather can span multiple*/ + /* pages transmits */ + VMNET_CAP_IP6_CSUM = 0x80000, /* Can do IPv6 csum offload. */ + VMNET_CAP_TSO6 = 0x100000, /* TSO seg. offload for IPv6 pkts. */ + VMNET_CAP_TSO256k = 0x200000, /* Can do TSO seg offload for */ + /* pkts up to 256kB. */ + VMNET_CAP_UPT = 0x400000 /* Support UPT */ +}; + +/* + * PCI vendor and device IDs. + */ +#define PCI_VENDOR_ID_VMWARE 0x15AD +#define PCI_DEVICE_ID_VMWARE_VMXNET3 0x07B0 +#define MAX_ETHERNET_CARDS 10 +#define MAX_PCI_PASSTHRU_DEVICE 6 + +struct vmxnet3_cmd_ring { + union Vmxnet3_GenericDesc *base; + u32 size; + u32 next2fill; + u32 next2comp; + u8 gen; + dma_addr_t basePA; +}; + +static inline void +vmxnet3_cmd_ring_adv_next2fill(struct vmxnet3_cmd_ring *ring) +{ + ring->next2fill++; + if (unlikely(ring->next2fill == ring->size)) { + ring->next2fill = 0; + VMXNET3_FLIP_RING_GEN(ring->gen); + } +} + +static inline void +vmxnet3_cmd_ring_adv_next2comp(struct vmxnet3_cmd_ring *ring) +{ + VMXNET3_INC_RING_IDX_ONLY(ring->next2comp, ring->size); +} + +static inline int +vmxnet3_cmd_ring_desc_avail(struct vmxnet3_cmd_ring *ring) +{ + return (ring->next2comp > ring->next2fill ? 0 : ring->size) + + ring->next2comp - ring->next2fill - 1; +} + +struct vmxnet3_comp_ring { + union Vmxnet3_GenericDesc *base; + u32 size; + u32 next2proc; + u8 gen; + u8 intr_idx; + dma_addr_t basePA; +}; + +static inline void +vmxnet3_comp_ring_adv_next2proc(struct vmxnet3_comp_ring *ring) +{ + ring->next2proc++; + if (unlikely(ring->next2proc == ring->size)) { + ring->next2proc = 0; + VMXNET3_FLIP_RING_GEN(ring->gen); + } +} + +struct vmxnet3_tx_data_ring { + struct Vmxnet3_TxDataDesc *base; + u32 size; + dma_addr_t basePA; +}; + +enum vmxnet3_buf_map_type { + VMXNET3_MAP_INVALID = 0, + VMXNET3_MAP_NONE, + VMXNET3_MAP_SINGLE, + VMXNET3_MAP_PAGE, +}; + +struct vmxnet3_tx_buf_info { + u32 map_type; + u16 len; + u16 sop_idx; + dma_addr_t dma_addr; + struct sk_buff *skb; +}; + +struct vmxnet3_tq_driver_stats { + u64 drop_total; /* # of pkts dropped by the driver, the + * counters below track droppings due to + * different reasons + */ + u64 drop_too_many_frags; + u64 drop_oversized_hdr; + u64 drop_hdr_inspect_err; + u64 drop_tso; + + u64 tx_ring_full; + u64 linearized; /* # of pkts linearized */ + u64 copy_skb_header; /* # of times we have to copy skb header */ + u64 oversized_hdr; +}; + +struct vmxnet3_tx_ctx { + bool ipv4; + u16 mss; + u32 eth_ip_hdr_size; /* only valid for pkts requesting tso or csum + * offloading + */ + u32 l4_hdr_size; /* only valid if mss != 0 */ + u32 copy_size; /* # of bytes copied into the data ring */ + union Vmxnet3_GenericDesc *sop_txd; + union Vmxnet3_GenericDesc *eop_txd; +}; + +struct vmxnet3_tx_queue { + spinlock_t tx_lock; + struct vmxnet3_cmd_ring tx_ring; + struct vmxnet3_tx_buf_info *buf_info; + struct vmxnet3_tx_data_ring data_ring; + struct vmxnet3_comp_ring comp_ring; + struct Vmxnet3_TxQueueCtrl *shared; + struct vmxnet3_tq_driver_stats stats; + bool stopped; + int num_stop; /* # of times the queue is + * stopped */ +} __attribute__((__aligned__(SMP_CACHE_BYTES))); + +enum vmxnet3_rx_buf_type { + VMXNET3_RX_BUF_NONE = 0, + VMXNET3_RX_BUF_SKB = 1, + VMXNET3_RX_BUF_PAGE = 2 +}; + +struct vmxnet3_rx_buf_info { + enum vmxnet3_rx_buf_type buf_type; + u16 len; + union { + struct sk_buff *skb; + struct page *page; + }; + dma_addr_t dma_addr; +}; + +struct vmxnet3_rx_ctx { + struct sk_buff *skb; + u32 sop_idx; +}; + +struct vmxnet3_rq_driver_stats { + u64 drop_total; + u64 drop_err; + u64 drop_fcs; + u64 rx_buf_alloc_failure; +}; + +struct vmxnet3_rx_queue { + struct vmxnet3_cmd_ring rx_ring[2]; + struct vmxnet3_comp_ring comp_ring; + struct vmxnet3_rx_ctx rx_ctx; + u32 qid; /* rqID in RCD for buffer from 1st ring */ + u32 qid2; /* rqID in RCD for buffer from 2nd ring */ + u32 uncommitted[2]; /* # of buffers allocated since last RXPROD + * update */ + struct vmxnet3_rx_buf_info *buf_info[2]; + struct Vmxnet3_RxQueueCtrl *shared; + struct vmxnet3_rq_driver_stats stats; +} __attribute__((__aligned__(SMP_CACHE_BYTES))); + +#define VMXNET3_LINUX_MAX_MSIX_VECT 1 + +struct vmxnet3_intr { + enum vmxnet3_intr_mask_mode mask_mode; + enum vmxnet3_intr_type type; /* MSI-X, MSI, or INTx? */ + u8 num_intrs; /* # of intr vectors */ + u8 event_intr_idx; /* idx of the intr vector for event */ + u8 mod_levels[VMXNET3_LINUX_MAX_MSIX_VECT]; /* moderation level */ +#ifdef CONFIG_PCI_MSI + struct msix_entry msix_entries[VMXNET3_LINUX_MAX_MSIX_VECT]; +#endif +}; + +#define VMXNET3_STATE_BIT_RESETTING 0 +#define VMXNET3_STATE_BIT_QUIESCED 1 +struct vmxnet3_adapter { + struct vmxnet3_tx_queue tx_queue; + struct vmxnet3_rx_queue rx_queue; + struct napi_struct napi; + struct vlan_group *vlan_grp; + + struct vmxnet3_intr intr; + + struct Vmxnet3_DriverShared *shared; + struct Vmxnet3_PMConf *pm_conf; + struct Vmxnet3_TxQueueDesc *tqd_start; /* first tx queue desc */ + struct Vmxnet3_RxQueueDesc *rqd_start; /* first rx queue desc */ + struct net_device *netdev; + struct pci_dev *pdev; + + u8 *hw_addr0; /* for BAR 0 */ + u8 *hw_addr1; /* for BAR 1 */ + + /* feature control */ + bool rxcsum; + bool lro; + bool jumbo_frame; + + /* rx buffer related */ + unsigned skb_buf_size; + int rx_buf_per_pkt; /* only apply to the 1st ring */ + dma_addr_t shared_pa; + dma_addr_t queue_desc_pa; + + /* Wake-on-LAN */ + u32 wol; + + /* Link speed */ + u32 link_speed; /* in mbps */ + + u64 tx_timeout_count; + struct work_struct work; + + unsigned long state; /* VMXNET3_STATE_BIT_xxx */ + + int dev_number; +}; + +#define VMXNET3_WRITE_BAR0_REG(adapter, reg, val) \ + writel((val), (adapter)->hw_addr0 + (reg)) +#define VMXNET3_READ_BAR0_REG(adapter, reg) \ + readl((adapter)->hw_addr0 + (reg)) + +#define VMXNET3_WRITE_BAR1_REG(adapter, reg, val) \ + writel((val), (adapter)->hw_addr1 + (reg)) +#define VMXNET3_READ_BAR1_REG(adapter, reg) \ + readl((adapter)->hw_addr1 + (reg)) + +#define VMXNET3_WAKE_QUEUE_THRESHOLD(tq) (5) +#define VMXNET3_RX_ALLOC_THRESHOLD(rq, ring_idx, adapter) \ + ((rq)->rx_ring[ring_idx].size >> 3) + +#define VMXNET3_GET_ADDR_LO(dma) ((u32)(dma)) +#define VMXNET3_GET_ADDR_HI(dma) ((u32)(((u64)(dma)) >> 32)) + +/* must be a multiple of VMXNET3_RING_SIZE_ALIGN */ +#define VMXNET3_DEF_TX_RING_SIZE 512 +#define VMXNET3_DEF_RX_RING_SIZE 256 + +#define VMXNET3_MAX_ETH_HDR_SIZE 22 +#define VMXNET3_MAX_SKB_BUF_SIZE (3*1024) + +int +vmxnet3_quiesce_dev(struct vmxnet3_adapter *adapter); + +int +vmxnet3_activate_dev(struct vmxnet3_adapter *adapter); + +void +vmxnet3_force_close(struct vmxnet3_adapter *adapter); + +void +vmxnet3_reset_dev(struct vmxnet3_adapter *adapter); + +void +vmxnet3_tq_destroy(struct vmxnet3_tx_queue *tq, + struct vmxnet3_adapter *adapter); + +void +vmxnet3_rq_destroy(struct vmxnet3_rx_queue *rq, + struct vmxnet3_adapter *adapter); + +int +vmxnet3_create_queues(struct vmxnet3_adapter *adapter, + u32 tx_ring_size, u32 rx_ring_size, u32 rx_ring2_size); + +extern void vmxnet3_set_ethtool_ops(struct net_device *netdev); +extern struct net_device_stats *vmxnet3_get_stats(struct net_device *netdev); + +extern char vmxnet3_driver_name[]; +#endif diff --git a/drivers/net/wan/c101.c b/drivers/net/wan/c101.c index 9693b0fd323d..0bd898c94759 100644 --- a/drivers/net/wan/c101.c +++ b/drivers/net/wan/c101.c @@ -16,6 +16,7 @@ #include <linux/module.h> #include <linux/kernel.h> +#include <linux/capability.h> #include <linux/slab.h> #include <linux/types.h> #include <linux/string.h> diff --git a/drivers/net/wan/cosa.c b/drivers/net/wan/cosa.c index 66360a2a14c2..8e25ca7080c7 100644 --- a/drivers/net/wan/cosa.c +++ b/drivers/net/wan/cosa.c @@ -76,6 +76,7 @@ #include <linux/module.h> #include <linux/kernel.h> +#include <linux/sched.h> #include <linux/slab.h> #include <linux/poll.h> #include <linux/fs.h> @@ -906,6 +907,7 @@ static ssize_t cosa_write(struct file *file, current->state = TASK_RUNNING; chan->tx_status = 1; spin_unlock_irqrestore(&cosa->lock, flags); + up(&chan->wsem); return -ERESTARTSYS; } } diff --git a/drivers/net/wan/cycx_x25.c b/drivers/net/wan/cycx_x25.c index 2573c18b6aa5..cd8cb95c5bd7 100644 --- a/drivers/net/wan/cycx_x25.c +++ b/drivers/net/wan/cycx_x25.c @@ -84,6 +84,7 @@ #include <linux/kernel.h> /* printk(), and other useful stuff */ #include <linux/module.h> #include <linux/string.h> /* inline memset(), etc. */ +#include <linux/sched.h> #include <linux/slab.h> /* kmalloc(), kfree() */ #include <linux/stddef.h> /* offsetof(), etc. */ #include <linux/wanrouter.h> /* WAN router definitions */ diff --git a/drivers/net/wan/dscc4.c b/drivers/net/wan/dscc4.c index 81c8aec9df92..07d00b4cf48a 100644 --- a/drivers/net/wan/dscc4.c +++ b/drivers/net/wan/dscc4.c @@ -81,6 +81,7 @@ */ #include <linux/module.h> +#include <linux/sched.h> #include <linux/types.h> #include <linux/errno.h> #include <linux/list.h> diff --git a/drivers/net/wan/farsync.c b/drivers/net/wan/farsync.c index 3e90eb816181..beda387f2fc7 100644 --- a/drivers/net/wan/farsync.c +++ b/drivers/net/wan/farsync.c @@ -19,6 +19,7 @@ #include <linux/kernel.h> #include <linux/version.h> #include <linux/pci.h> +#include <linux/sched.h> #include <linux/ioport.h> #include <linux/init.h> #include <linux/if.h> diff --git a/drivers/net/wan/hdlc_cisco.c b/drivers/net/wan/hdlc_cisco.c index cf5fd17ad707..f1bff98acd1f 100644 --- a/drivers/net/wan/hdlc_cisco.c +++ b/drivers/net/wan/hdlc_cisco.c @@ -58,8 +58,7 @@ struct cisco_state { spinlock_t lock; unsigned long last_poll; int up; - int request_sent; - u32 txseq; /* TX sequence number */ + u32 txseq; /* TX sequence number, 0 = none */ u32 rxseq; /* RX sequence number */ }; @@ -163,6 +162,7 @@ static int cisco_rx(struct sk_buff *skb) struct cisco_packet *cisco_data; struct in_device *in_dev; __be32 addr, mask; + u32 ack; if (skb->len < sizeof(struct hdlc_header)) goto rx_error; @@ -223,8 +223,10 @@ static int cisco_rx(struct sk_buff *skb) case CISCO_KEEPALIVE_REQ: spin_lock(&st->lock); st->rxseq = ntohl(cisco_data->par1); - if (st->request_sent && - ntohl(cisco_data->par2) == st->txseq) { + ack = ntohl(cisco_data->par2); + if (ack && (ack == st->txseq || + /* our current REQ may be in transit */ + ack == st->txseq - 1)) { st->last_poll = jiffies; if (!st->up) { u32 sec, min, hrs, days; @@ -275,7 +277,6 @@ static void cisco_timer(unsigned long arg) cisco_keepalive_send(dev, CISCO_KEEPALIVE_REQ, htonl(++st->txseq), htonl(st->rxseq)); - st->request_sent = 1; spin_unlock(&st->lock); st->timer.expires = jiffies + st->settings.interval * HZ; @@ -293,9 +294,7 @@ static void cisco_start(struct net_device *dev) unsigned long flags; spin_lock_irqsave(&st->lock, flags); - st->up = 0; - st->request_sent = 0; - st->txseq = st->rxseq = 0; + st->up = st->txseq = st->rxseq = 0; spin_unlock_irqrestore(&st->lock, flags); init_timer(&st->timer); @@ -317,8 +316,7 @@ static void cisco_stop(struct net_device *dev) spin_lock_irqsave(&st->lock, flags); netif_dormant_on(dev); - st->up = 0; - st->request_sent = 0; + st->up = st->txseq = 0; spin_unlock_irqrestore(&st->lock, flags); } diff --git a/drivers/net/wan/n2.c b/drivers/net/wan/n2.c index 83da596e2052..58c66819f39b 100644 --- a/drivers/net/wan/n2.c +++ b/drivers/net/wan/n2.c @@ -18,6 +18,7 @@ #include <linux/module.h> #include <linux/kernel.h> +#include <linux/capability.h> #include <linux/slab.h> #include <linux/types.h> #include <linux/fcntl.h> diff --git a/drivers/net/wan/pci200syn.c b/drivers/net/wan/pci200syn.c index a52f29c72c33..f1340faaf022 100644 --- a/drivers/net/wan/pci200syn.c +++ b/drivers/net/wan/pci200syn.c @@ -16,6 +16,7 @@ #include <linux/module.h> #include <linux/kernel.h> +#include <linux/capability.h> #include <linux/slab.h> #include <linux/types.h> #include <linux/fcntl.h> diff --git a/drivers/net/wireless/Kconfig b/drivers/net/wireless/Kconfig index 49ea9c92b7e6..d7a764a2fc1a 100644 --- a/drivers/net/wireless/Kconfig +++ b/drivers/net/wireless/Kconfig @@ -31,13 +31,12 @@ config STRIP ---help--- Say Y if you have a Metricom radio and intend to use Starmode Radio IP. STRIP is a radio protocol developed for the MosquitoNet project - (on the WWW at <http://mosquitonet.stanford.edu/>) to send Internet - traffic using Metricom radios. Metricom radios are small, battery - powered, 100kbit/sec packet radio transceivers, about the size and - weight of a cellular telephone. (You may also have heard them called - "Metricom modems" but we avoid the term "modem" because it misleads - many people into thinking that you can plug a Metricom modem into a - phone line and use it as a modem.) + to send Internet traffic using Metricom radios. Metricom radios are + small, battery powered, 100kbit/sec packet radio transceivers, about + the size and weight of a cellular telephone. (You may also have heard + them called "Metricom modems" but we avoid the term "modem" because + it misleads many people into thinking that you can plug a Metricom + modem into a phone line and use it as a modem.) You can use STRIP on any Linux machine with a serial port, although it is obviously most useful for people with laptop computers. If you diff --git a/drivers/net/wireless/adm8211.h b/drivers/net/wireless/adm8211.h index 4f6ab1322189..b07e4d3a6b4d 100644 --- a/drivers/net/wireless/adm8211.h +++ b/drivers/net/wireless/adm8211.h @@ -266,7 +266,7 @@ do { \ #define ADM8211_SYNCTL_CS1 (1 << 28) #define ADM8211_SYNCTL_CAL (1 << 27) #define ADM8211_SYNCTL_SELCAL (1 << 26) -#define ADM8211_SYNCTL_RFtype ((1 << 24) || (1 << 23) || (1 << 22)) +#define ADM8211_SYNCTL_RFtype ((1 << 24) | (1 << 23) | (1 << 22)) #define ADM8211_SYNCTL_RFMD (1 << 22) #define ADM8211_SYNCTL_GENERAL (0x7 << 22) /* SYNCTL 21:0 Data (Si4126: 18-bit data, 4-bit address) */ diff --git a/drivers/net/wireless/airo.c b/drivers/net/wireless/airo.c index 7116a1aa20ce..abf896a7390e 100644 --- a/drivers/net/wireless/airo.c +++ b/drivers/net/wireless/airo.c @@ -4790,9 +4790,8 @@ static int proc_stats_rid_open( struct inode *inode, static int get_dec_u16( char *buffer, int *start, int limit ) { u16 value; int valid = 0; - for( value = 0; buffer[*start] >= '0' && - buffer[*start] <= '9' && - *start < limit; (*start)++ ) { + for (value = 0; *start < limit && buffer[*start] >= '0' && + buffer[*start] <= '9'; (*start)++) { valid = 1; value *= 10; value += buffer[*start] - '0'; diff --git a/drivers/net/wireless/arlan-proc.c b/drivers/net/wireless/arlan-proc.c index 2ab1d59870f4..a8b689635a3b 100644 --- a/drivers/net/wireless/arlan-proc.c +++ b/drivers/net/wireless/arlan-proc.c @@ -402,7 +402,7 @@ static int arlan_setup_card_by_book(struct net_device *dev) static char arlan_drive_info[ARLAN_STR_SIZE] = "A655\n\0"; -static int arlan_sysctl_info(ctl_table * ctl, int write, struct file *filp, +static int arlan_sysctl_info(ctl_table * ctl, int write, void __user *buffer, size_t * lenp, loff_t *ppos) { int i; @@ -629,7 +629,7 @@ final: *lenp = pos; if (!write) - retv = proc_dostring(ctl, write, filp, buffer, lenp, ppos); + retv = proc_dostring(ctl, write, buffer, lenp, ppos); else { *lenp = 0; @@ -639,7 +639,7 @@ final: } -static int arlan_sysctl_info161719(ctl_table * ctl, int write, struct file *filp, +static int arlan_sysctl_info161719(ctl_table * ctl, int write, void __user *buffer, size_t * lenp, loff_t *ppos) { int i; @@ -669,11 +669,11 @@ static int arlan_sysctl_info161719(ctl_table * ctl, int write, struct file *filp final: *lenp = pos; - retv = proc_dostring(ctl, write, filp, buffer, lenp, ppos); + retv = proc_dostring(ctl, write, buffer, lenp, ppos); return retv; } -static int arlan_sysctl_infotxRing(ctl_table * ctl, int write, struct file *filp, +static int arlan_sysctl_infotxRing(ctl_table * ctl, int write, void __user *buffer, size_t * lenp, loff_t *ppos) { int i; @@ -698,11 +698,11 @@ static int arlan_sysctl_infotxRing(ctl_table * ctl, int write, struct file *filp SARLBNpln(u_char, txBuffer, 0x800); final: *lenp = pos; - retv = proc_dostring(ctl, write, filp, buffer, lenp, ppos); + retv = proc_dostring(ctl, write, buffer, lenp, ppos); return retv; } -static int arlan_sysctl_inforxRing(ctl_table * ctl, int write, struct file *filp, +static int arlan_sysctl_inforxRing(ctl_table * ctl, int write, void __user *buffer, size_t * lenp, loff_t *ppos) { int i; @@ -726,11 +726,11 @@ static int arlan_sysctl_inforxRing(ctl_table * ctl, int write, struct file *filp SARLBNpln(u_char, rxBuffer, 0x800); final: *lenp = pos; - retv = proc_dostring(ctl, write, filp, buffer, lenp, ppos); + retv = proc_dostring(ctl, write, buffer, lenp, ppos); return retv; } -static int arlan_sysctl_info18(ctl_table * ctl, int write, struct file *filp, +static int arlan_sysctl_info18(ctl_table * ctl, int write, void __user *buffer, size_t * lenp, loff_t *ppos) { int i; @@ -756,7 +756,7 @@ static int arlan_sysctl_info18(ctl_table * ctl, int write, struct file *filp, final: *lenp = pos; - retv = proc_dostring(ctl, write, filp, buffer, lenp, ppos); + retv = proc_dostring(ctl, write, buffer, lenp, ppos); return retv; } @@ -766,7 +766,7 @@ final: static char conf_reset_result[200]; -static int arlan_configure(ctl_table * ctl, int write, struct file *filp, +static int arlan_configure(ctl_table * ctl, int write, void __user *buffer, size_t * lenp, loff_t *ppos) { int pos = 0; @@ -788,10 +788,10 @@ static int arlan_configure(ctl_table * ctl, int write, struct file *filp, return -1; *lenp = pos; - return proc_dostring(ctl, write, filp, buffer, lenp, ppos); + return proc_dostring(ctl, write, buffer, lenp, ppos); } -static int arlan_sysctl_reset(ctl_table * ctl, int write, struct file *filp, +static int arlan_sysctl_reset(ctl_table * ctl, int write, void __user *buffer, size_t * lenp, loff_t *ppos) { int pos = 0; @@ -811,7 +811,7 @@ static int arlan_sysctl_reset(ctl_table * ctl, int write, struct file *filp, } else return -1; *lenp = pos + 3; - return proc_dostring(ctl, write, filp, buffer, lenp, ppos); + return proc_dostring(ctl, write, buffer, lenp, ppos); } diff --git a/drivers/net/wireless/ath/ar9170/phy.c b/drivers/net/wireless/ath/ar9170/phy.c index b3e5cf3735b0..dbd488da18b1 100644 --- a/drivers/net/wireless/ath/ar9170/phy.c +++ b/drivers/net/wireless/ath/ar9170/phy.c @@ -1141,7 +1141,8 @@ static int ar9170_set_freq_cal_data(struct ar9170 *ar, u8 vpds[2][AR5416_PD_GAIN_ICEPTS]; u8 pwrs[2][AR5416_PD_GAIN_ICEPTS]; int chain, idx, i; - u8 f; + u32 phy_data = 0; + u8 f, tmp; switch (channel->band) { case IEEE80211_BAND_2GHZ: @@ -1208,9 +1209,6 @@ static int ar9170_set_freq_cal_data(struct ar9170 *ar, } for (i = 0; i < 76; i++) { - u32 phy_data; - u8 tmp; - if (i < 25) { tmp = ar9170_interpolate_val(i, &pwrs[0][0], &vpds[0][0]); diff --git a/drivers/net/wireless/ath/ar9170/usb.c b/drivers/net/wireless/ath/ar9170/usb.c index e0138ac8bf50..e974e5829e1a 100644 --- a/drivers/net/wireless/ath/ar9170/usb.c +++ b/drivers/net/wireless/ath/ar9170/usb.c @@ -64,6 +64,8 @@ static struct usb_device_id ar9170_usb_ids[] = { { USB_DEVICE(0x0cf3, 0x9170) }, /* Atheros TG121N */ { USB_DEVICE(0x0cf3, 0x1001) }, + /* TP-Link TL-WN821N v2 */ + { USB_DEVICE(0x0cf3, 0x1002) }, /* Cace Airpcap NX */ { USB_DEVICE(0xcace, 0x0300) }, /* D-Link DWA 160A */ diff --git a/drivers/net/wireless/ath/ath5k/base.c b/drivers/net/wireless/ath/ath5k/base.c index 9c6ab5378f6e..95a8e232b58f 100644 --- a/drivers/net/wireless/ath/ath5k/base.c +++ b/drivers/net/wireless/ath/ath5k/base.c @@ -1125,7 +1125,6 @@ ath5k_mode_setup(struct ath5k_softc *sc) /* configure operational mode */ ath5k_hw_set_opmode(ah); - ath5k_hw_set_mcast_filter(ah, 0, 0); ATH5K_DBG(sc, ATH5K_DEBUG_MODE, "RX filter 0x%x\n", rfilt); } diff --git a/drivers/net/wireless/ath/ath5k/led.c b/drivers/net/wireless/ath/ath5k/led.c index b767c3b67b24..b548c8eaaae1 100644 --- a/drivers/net/wireless/ath/ath5k/led.c +++ b/drivers/net/wireless/ath/ath5k/led.c @@ -63,12 +63,16 @@ static const struct pci_device_id ath5k_led_devices[] = { { ATH_SDEVICE(PCI_VENDOR_ID_AMBIT, 0x0422), ATH_LED(1, 1) }, /* E-machines E510 (tuliom@gmail.com) */ { ATH_SDEVICE(PCI_VENDOR_ID_AMBIT, 0x0428), ATH_LED(3, 0) }, + /* BenQ Joybook R55v (nowymarluk@wp.pl) */ + { ATH_SDEVICE(PCI_VENDOR_ID_QMI, 0x0100), ATH_LED(1, 0) }, /* Acer Extensa 5620z (nekoreeve@gmail.com) */ { ATH_SDEVICE(PCI_VENDOR_ID_QMI, 0x0105), ATH_LED(3, 0) }, /* Fukato Datacask Jupiter 1014a (mrb74@gmx.at) */ { ATH_SDEVICE(PCI_VENDOR_ID_AZWAVE, 0x1026), ATH_LED(3, 0) }, /* IBM ThinkPad AR5BXB6 (legovini@spiro.fisica.unipd.it) */ { ATH_SDEVICE(PCI_VENDOR_ID_IBM, 0x058a), ATH_LED(1, 0) }, + /* HP Compaq CQ60-206US (ddreggors@jumptv.com) */ + { ATH_SDEVICE(PCI_VENDOR_ID_HP, 0x0137a), ATH_LED(3, 1) }, /* HP Compaq C700 (nitrousnrg@gmail.com) */ { ATH_SDEVICE(PCI_VENDOR_ID_HP, 0x0137b), ATH_LED(3, 1) }, /* IBM-specific AR5212 (all others) */ diff --git a/drivers/net/wireless/ath/ath9k/calib.c b/drivers/net/wireless/ath/ath9k/calib.c index 3234995e8881..0ad6d0b76e9e 100644 --- a/drivers/net/wireless/ath/ath9k/calib.c +++ b/drivers/net/wireless/ath/ath9k/calib.c @@ -609,14 +609,24 @@ void ath9k_hw_loadnf(struct ath_hw *ah, struct ath9k_channel *chan) AR_PHY_CH1_EXT_CCA, AR_PHY_CH2_EXT_CCA }; - u8 chainmask; + u8 chainmask, rx_chain_status; + rx_chain_status = REG_READ(ah, AR_PHY_RX_CHAINMASK); if (AR_SREV_9285(ah)) chainmask = 0x9; - else if (AR_SREV_9280(ah) || AR_SREV_9287(ah)) - chainmask = 0x1B; - else - chainmask = 0x3F; + else if (AR_SREV_9280(ah) || AR_SREV_9287(ah)) { + if ((rx_chain_status & 0x2) || (rx_chain_status & 0x4)) + chainmask = 0x1B; + else + chainmask = 0x09; + } else { + if (rx_chain_status & 0x4) + chainmask = 0x3F; + else if (rx_chain_status & 0x2) + chainmask = 0x1B; + else + chainmask = 0x09; + } h = ah->nfCalHist; @@ -697,6 +707,8 @@ void ath9k_init_nfcal_hist_buffer(struct ath_hw *ah) noise_floor = AR_PHY_CCA_MAX_AR9280_GOOD_VALUE; else if (AR_SREV_9285(ah)) noise_floor = AR_PHY_CCA_MAX_AR9285_GOOD_VALUE; + else if (AR_SREV_9287(ah)) + noise_floor = AR_PHY_CCA_MAX_AR9287_GOOD_VALUE; else noise_floor = AR_PHY_CCA_MAX_AR5416_GOOD_VALUE; @@ -924,6 +936,7 @@ static inline void ath9k_hw_9285_pa_cal(struct ath_hw *ah, bool is_reset) regVal |= (1 << (19 + i)); REG_WRITE(ah, 0x7834, regVal); udelay(1); + regVal = REG_READ(ah, 0x7834); regVal &= (~(0x1 << (19 + i))); reg_field = MS(REG_READ(ah, 0x7840), AR9285_AN_RXTXBB1_SPARE9); regVal |= (reg_field << (19 + i)); diff --git a/drivers/net/wireless/ath/ath9k/calib.h b/drivers/net/wireless/ath/ath9k/calib.h index 019bcbba40ed..9028ab193e42 100644 --- a/drivers/net/wireless/ath/ath9k/calib.h +++ b/drivers/net/wireless/ath/ath9k/calib.h @@ -28,6 +28,7 @@ extern const struct ath9k_percal_data adc_init_dc_cal; #define AR_PHY_CCA_MAX_AR5416_GOOD_VALUE -85 #define AR_PHY_CCA_MAX_AR9280_GOOD_VALUE -112 #define AR_PHY_CCA_MAX_AR9285_GOOD_VALUE -118 +#define AR_PHY_CCA_MAX_AR9287_GOOD_VALUE -118 #define AR_PHY_CCA_MAX_HIGH_VALUE -62 #define AR_PHY_CCA_MIN_BAD_VALUE -140 #define AR_PHY_CCA_FILTERWINDOW_LENGTH_INIT 3 diff --git a/drivers/net/wireless/ath/ath9k/eeprom_def.c b/drivers/net/wireless/ath/ath9k/eeprom_def.c index ae7fb5dcb266..4071fc91da0a 100644 --- a/drivers/net/wireless/ath/ath9k/eeprom_def.c +++ b/drivers/net/wireless/ath/ath9k/eeprom_def.c @@ -509,6 +509,8 @@ static void ath9k_hw_def_set_board_values(struct ath_hw *ah, REG_RMW_FIELD(ah, AR_AN_TOP1, AR_AN_TOP1_DACIPMODE, eep->baseEepHeader.dacLpMode); + udelay(100); + REG_RMW_FIELD(ah, AR_PHY_FRAME_CTL, AR_PHY_FRAME_CTL_TX_CLIP, pModal->miscBits >> 2); @@ -902,7 +904,7 @@ static void ath9k_hw_set_def_power_per_rate_table(struct ath_hw *ah, u16 powerLimit) { #define REDUCE_SCALED_POWER_BY_TWO_CHAIN 6 /* 10*log10(2)*2 */ -#define REDUCE_SCALED_POWER_BY_THREE_CHAIN 10 /* 10*log10(3)*2 */ +#define REDUCE_SCALED_POWER_BY_THREE_CHAIN 9 /* 10*log10(3)*2 */ struct ath_regulatory *regulatory = ath9k_hw_regulatory(ah); struct ar5416_eeprom_def *pEepData = &ah->eeprom.def; diff --git a/drivers/net/wireless/ath/ath9k/hw.c b/drivers/net/wireless/ath/ath9k/hw.c index b6c6cca07812..ca7694caf364 100644 --- a/drivers/net/wireless/ath/ath9k/hw.c +++ b/drivers/net/wireless/ath/ath9k/hw.c @@ -842,7 +842,7 @@ static void ath9k_hw_init_mode_regs(struct ath_hw *ah) static void ath9k_hw_init_mode_gain_regs(struct ath_hw *ah) { - if (AR_SREV_9287_11(ah)) + if (AR_SREV_9287_11_OR_LATER(ah)) INIT_INI_ARRAY(&ah->iniModesRxGain, ar9287Modes_rx_gain_9287_1_1, ARRAY_SIZE(ar9287Modes_rx_gain_9287_1_1), 6); @@ -853,7 +853,7 @@ static void ath9k_hw_init_mode_gain_regs(struct ath_hw *ah) else if (AR_SREV_9280_20(ah)) ath9k_hw_init_rxgain_ini(ah); - if (AR_SREV_9287_11(ah)) { + if (AR_SREV_9287_11_OR_LATER(ah)) { INIT_INI_ARRAY(&ah->iniModesTxGain, ar9287Modes_tx_gain_9287_1_1, ARRAY_SIZE(ar9287Modes_tx_gain_9287_1_1), 6); @@ -965,7 +965,7 @@ int ath9k_hw_init(struct ath_hw *ah) ath9k_hw_init_mode_regs(ah); if (ah->is_pciexpress) - ath9k_hw_configpcipowersave(ah, 0); + ath9k_hw_configpcipowersave(ah, 0, 0); else ath9k_hw_disablepcie(ah); @@ -1273,6 +1273,15 @@ static void ath9k_hw_override_ini(struct ath_hw *ah, */ REG_SET_BIT(ah, AR_DIAG_SW, (AR_DIAG_RX_DIS | AR_DIAG_RX_ABORT)); + if (AR_SREV_9280_10_OR_LATER(ah)) { + val = REG_READ(ah, AR_PCU_MISC_MODE2) & + (~AR_PCU_MISC_MODE2_HWWAR1); + + if (AR_SREV_9287_10_OR_LATER(ah)) + val = val & (~AR_PCU_MISC_MODE2_HWWAR2); + + REG_WRITE(ah, AR_PCU_MISC_MODE2, val); + } if (!AR_SREV_5416_20_OR_LATER(ah) || AR_SREV_9280_10_OR_LATER(ah)) @@ -1784,7 +1793,7 @@ static void ath9k_hw_set_regs(struct ath_hw *ah, struct ath9k_channel *chan, static bool ath9k_hw_chip_reset(struct ath_hw *ah, struct ath9k_channel *chan) { - if (OLC_FOR_AR9280_20_LATER) { + if (AR_SREV_9280(ah) && ah->eep_ops->get_eeprom(ah, EEP_OL_PWRCTRL)) { if (!ath9k_hw_set_reset_reg(ah, ATH9K_RESET_POWER_ON)) return false; } else if (!ath9k_hw_set_reset_reg(ah, ATH9K_RESET_WARM)) @@ -2338,6 +2347,7 @@ int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan, struct ath9k_channel *curchan = ah->curchan; u32 saveDefAntenna; u32 macStaId1; + u64 tsf = 0; int i, rx_chainmask, r; ah->extprotspacing = sc->ht_extprotspacing; @@ -2347,7 +2357,7 @@ int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan, if (!ath9k_hw_setpower(ah, ATH9K_PM_AWAKE)) return -EIO; - if (curchan) + if (curchan && !ah->chip_fullsleep) ath9k_hw_getnf(ah, curchan); if (bChannelChange && @@ -2356,8 +2366,8 @@ int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan, (chan->channel != ah->curchan->channel) && ((chan->channelFlags & CHANNEL_ALL) == (ah->curchan->channelFlags & CHANNEL_ALL)) && - (!AR_SREV_9280(ah) || (!IS_CHAN_A_5MHZ_SPACED(chan) && - !IS_CHAN_A_5MHZ_SPACED(ah->curchan)))) { + !(AR_SREV_9280(ah) || IS_CHAN_A_5MHZ_SPACED(chan) || + IS_CHAN_A_5MHZ_SPACED(ah->curchan))) { if (ath9k_hw_channel_change(ah, chan, sc->tx_chan_width)) { ath9k_hw_loadnf(ah, ah->curchan); @@ -2372,6 +2382,10 @@ int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan, macStaId1 = REG_READ(ah, AR_STA_ID1) & AR_STA_ID1_BASE_RATE_11B; + /* For chips on which RTC reset is done, save TSF before it gets cleared */ + if (AR_SREV_9280(ah) && ah->eep_ops->get_eeprom(ah, EEP_OL_PWRCTRL)) + tsf = ath9k_hw_gettsf64(ah); + saveLedState = REG_READ(ah, AR_CFG_LED) & (AR_CFG_LED_ASSOC_CTL | AR_CFG_LED_MODE_SEL | AR_CFG_LED_BLINK_THRESH_SEL | AR_CFG_LED_BLINK_SLOW); @@ -2398,6 +2412,10 @@ int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan, udelay(50); } + /* Restore TSF */ + if (tsf && AR_SREV_9280(ah) && ah->eep_ops->get_eeprom(ah, EEP_OL_PWRCTRL)) + ath9k_hw_settsf64(ah, tsf); + if (AR_SREV_9280_10_OR_LATER(ah)) REG_SET_BIT(ah, AR_GPIO_INPUT_EN_VAL, AR_GPIO_JTAG_DISABLE); @@ -3005,9 +3023,10 @@ void ath9k_ps_restore(struct ath_softc *sc) * Programming the SerDes must go through the same 288 bit serial shift * register as the other analog registers. Hence the 9 writes. */ -void ath9k_hw_configpcipowersave(struct ath_hw *ah, int restore) +void ath9k_hw_configpcipowersave(struct ath_hw *ah, int restore, int power_off) { u8 i; + u32 val; if (ah->is_pciexpress != true) return; @@ -3017,84 +3036,113 @@ void ath9k_hw_configpcipowersave(struct ath_hw *ah, int restore) return; /* Nothing to do on restore for 11N */ - if (restore) - return; + if (!restore) { + if (AR_SREV_9280_20_OR_LATER(ah)) { + /* + * AR9280 2.0 or later chips use SerDes values from the + * initvals.h initialized depending on chipset during + * ath9k_hw_init() + */ + for (i = 0; i < ah->iniPcieSerdes.ia_rows; i++) { + REG_WRITE(ah, INI_RA(&ah->iniPcieSerdes, i, 0), + INI_RA(&ah->iniPcieSerdes, i, 1)); + } + } else if (AR_SREV_9280(ah) && + (ah->hw_version.macRev == AR_SREV_REVISION_9280_10)) { + REG_WRITE(ah, AR_PCIE_SERDES, 0x9248fd00); + REG_WRITE(ah, AR_PCIE_SERDES, 0x24924924); + + /* RX shut off when elecidle is asserted */ + REG_WRITE(ah, AR_PCIE_SERDES, 0xa8000019); + REG_WRITE(ah, AR_PCIE_SERDES, 0x13160820); + REG_WRITE(ah, AR_PCIE_SERDES, 0xe5980560); + + /* Shut off CLKREQ active in L1 */ + if (ah->config.pcie_clock_req) + REG_WRITE(ah, AR_PCIE_SERDES, 0x401deffc); + else + REG_WRITE(ah, AR_PCIE_SERDES, 0x401deffd); - if (AR_SREV_9280_20_OR_LATER(ah)) { - /* - * AR9280 2.0 or later chips use SerDes values from the - * initvals.h initialized depending on chipset during - * ath9k_hw_init() - */ - for (i = 0; i < ah->iniPcieSerdes.ia_rows; i++) { - REG_WRITE(ah, INI_RA(&ah->iniPcieSerdes, i, 0), - INI_RA(&ah->iniPcieSerdes, i, 1)); - } - } else if (AR_SREV_9280(ah) && - (ah->hw_version.macRev == AR_SREV_REVISION_9280_10)) { - REG_WRITE(ah, AR_PCIE_SERDES, 0x9248fd00); - REG_WRITE(ah, AR_PCIE_SERDES, 0x24924924); + REG_WRITE(ah, AR_PCIE_SERDES, 0x1aaabe40); + REG_WRITE(ah, AR_PCIE_SERDES, 0xbe105554); + REG_WRITE(ah, AR_PCIE_SERDES, 0x00043007); - /* RX shut off when elecidle is asserted */ - REG_WRITE(ah, AR_PCIE_SERDES, 0xa8000019); - REG_WRITE(ah, AR_PCIE_SERDES, 0x13160820); - REG_WRITE(ah, AR_PCIE_SERDES, 0xe5980560); + /* Load the new settings */ + REG_WRITE(ah, AR_PCIE_SERDES2, 0x00000000); - /* Shut off CLKREQ active in L1 */ - if (ah->config.pcie_clock_req) - REG_WRITE(ah, AR_PCIE_SERDES, 0x401deffc); - else - REG_WRITE(ah, AR_PCIE_SERDES, 0x401deffd); - - REG_WRITE(ah, AR_PCIE_SERDES, 0x1aaabe40); - REG_WRITE(ah, AR_PCIE_SERDES, 0xbe105554); - REG_WRITE(ah, AR_PCIE_SERDES, 0x00043007); + } else { + REG_WRITE(ah, AR_PCIE_SERDES, 0x9248fc00); + REG_WRITE(ah, AR_PCIE_SERDES, 0x24924924); - /* Load the new settings */ - REG_WRITE(ah, AR_PCIE_SERDES2, 0x00000000); + /* RX shut off when elecidle is asserted */ + REG_WRITE(ah, AR_PCIE_SERDES, 0x28000039); + REG_WRITE(ah, AR_PCIE_SERDES, 0x53160824); + REG_WRITE(ah, AR_PCIE_SERDES, 0xe5980579); - } else { - REG_WRITE(ah, AR_PCIE_SERDES, 0x9248fc00); - REG_WRITE(ah, AR_PCIE_SERDES, 0x24924924); + /* + * Ignore ah->ah_config.pcie_clock_req setting for + * pre-AR9280 11n + */ + REG_WRITE(ah, AR_PCIE_SERDES, 0x001defff); - /* RX shut off when elecidle is asserted */ - REG_WRITE(ah, AR_PCIE_SERDES, 0x28000039); - REG_WRITE(ah, AR_PCIE_SERDES, 0x53160824); - REG_WRITE(ah, AR_PCIE_SERDES, 0xe5980579); + REG_WRITE(ah, AR_PCIE_SERDES, 0x1aaabe40); + REG_WRITE(ah, AR_PCIE_SERDES, 0xbe105554); + REG_WRITE(ah, AR_PCIE_SERDES, 0x000e3007); - /* - * Ignore ah->ah_config.pcie_clock_req setting for - * pre-AR9280 11n - */ - REG_WRITE(ah, AR_PCIE_SERDES, 0x001defff); + /* Load the new settings */ + REG_WRITE(ah, AR_PCIE_SERDES2, 0x00000000); + } - REG_WRITE(ah, AR_PCIE_SERDES, 0x1aaabe40); - REG_WRITE(ah, AR_PCIE_SERDES, 0xbe105554); - REG_WRITE(ah, AR_PCIE_SERDES, 0x000e3007); + udelay(1000); - /* Load the new settings */ - REG_WRITE(ah, AR_PCIE_SERDES2, 0x00000000); - } + /* set bit 19 to allow forcing of pcie core into L1 state */ + REG_SET_BIT(ah, AR_PCIE_PM_CTRL, AR_PCIE_PM_CTRL_ENA); - udelay(1000); + /* Several PCIe massages to ensure proper behaviour */ + if (ah->config.pcie_waen) { + val = ah->config.pcie_waen; + if (!power_off) + val &= (~AR_WA_D3_L1_DISABLE); + } else { + if (AR_SREV_9285(ah) || AR_SREV_9271(ah) || + AR_SREV_9287(ah)) { + val = AR9285_WA_DEFAULT; + if (!power_off) + val &= (~AR_WA_D3_L1_DISABLE); + } else if (AR_SREV_9280(ah)) { + /* + * On AR9280 chips bit 22 of 0x4004 needs to be + * set otherwise card may disappear. + */ + val = AR9280_WA_DEFAULT; + if (!power_off) + val &= (~AR_WA_D3_L1_DISABLE); + } else + val = AR_WA_DEFAULT; + } - /* set bit 19 to allow forcing of pcie core into L1 state */ - REG_SET_BIT(ah, AR_PCIE_PM_CTRL, AR_PCIE_PM_CTRL_ENA); + REG_WRITE(ah, AR_WA, val); + } - /* Several PCIe massages to ensure proper behaviour */ - if (ah->config.pcie_waen) { - REG_WRITE(ah, AR_WA, ah->config.pcie_waen); - } else { - if (AR_SREV_9285(ah) || AR_SREV_9271(ah) || AR_SREV_9287(ah)) - REG_WRITE(ah, AR_WA, AR9285_WA_DEFAULT); + if (power_off) { /* - * On AR9280 chips bit 22 of 0x4004 needs to be set to - * otherwise card may disappear. + * Set PCIe workaround bits + * bit 14 in WA register (disable L1) should only + * be set when device enters D3 and be cleared + * when device comes back to D0. */ - else if (AR_SREV_9280(ah)) - REG_WRITE(ah, AR_WA, AR9280_WA_DEFAULT); - else - REG_WRITE(ah, AR_WA, AR_WA_DEFAULT); + if (ah->config.pcie_waen) { + if (ah->config.pcie_waen & AR_WA_D3_L1_DISABLE) + REG_SET_BIT(ah, AR_WA, AR_WA_D3_L1_DISABLE); + } else { + if (((AR_SREV_9285(ah) || AR_SREV_9271(ah) || + AR_SREV_9287(ah)) && + (AR9285_WA_DEFAULT & AR_WA_D3_L1_DISABLE)) || + (AR_SREV_9280(ah) && + (AR9280_WA_DEFAULT & AR_WA_D3_L1_DISABLE))) { + REG_SET_BIT(ah, AR_WA, AR_WA_D3_L1_DISABLE); + } + } } } @@ -3652,15 +3700,7 @@ void ath9k_hw_fill_cap_info(struct ath_hw *ah) } #endif - if ((ah->hw_version.macVersion == AR_SREV_VERSION_5416_PCI) || - (ah->hw_version.macVersion == AR_SREV_VERSION_5416_PCIE) || - (ah->hw_version.macVersion == AR_SREV_VERSION_9160) || - (ah->hw_version.macVersion == AR_SREV_VERSION_9100) || - (ah->hw_version.macVersion == AR_SREV_VERSION_9280) || - (ah->hw_version.macVersion == AR_SREV_VERSION_9285)) - pCap->hw_caps &= ~ATH9K_HW_CAP_AUTOSLEEP; - else - pCap->hw_caps |= ATH9K_HW_CAP_AUTOSLEEP; + pCap->hw_caps &= ~ATH9K_HW_CAP_AUTOSLEEP; if (AR_SREV_9280(ah) || AR_SREV_9285(ah)) pCap->hw_caps &= ~ATH9K_HW_CAP_4KB_SPLITTRANS; diff --git a/drivers/net/wireless/ath/ath9k/hw.h b/drivers/net/wireless/ath/ath9k/hw.h index 9106a0b537dd..b89234571829 100644 --- a/drivers/net/wireless/ath/ath9k/hw.h +++ b/drivers/net/wireless/ath/ath9k/hw.h @@ -106,7 +106,7 @@ #define AH_TSF_WRITE_TIMEOUT 100 /* (us) */ #define AH_TIME_QUANTUM 10 #define AR_KEYTABLE_SIZE 128 -#define POWER_UP_TIME 200000 +#define POWER_UP_TIME 10000 #define SPUR_RSSI_THRESH 40 #define CAB_TIMEOUT_VAL 10 @@ -650,7 +650,7 @@ void ath9k_hw_set_sta_beacon_timers(struct ath_hw *ah, const struct ath9k_beacon_state *bs); bool ath9k_hw_setpower(struct ath_hw *ah, enum ath9k_power_mode mode); -void ath9k_hw_configpcipowersave(struct ath_hw *ah, int restore); +void ath9k_hw_configpcipowersave(struct ath_hw *ah, int restore, int power_off); /* Interrupt Handling */ bool ath9k_hw_intrpend(struct ath_hw *ah); diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index 3dc7b5a13e64..43d2be9867fc 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -1131,7 +1131,7 @@ void ath_radio_enable(struct ath_softc *sc) int r; ath9k_ps_wakeup(sc); - ath9k_hw_configpcipowersave(ah, 0); + ath9k_hw_configpcipowersave(ah, 0, 0); if (!ah->curchan) ah->curchan = ath_get_curchannel(sc, sc->hw); @@ -1202,7 +1202,7 @@ void ath_radio_disable(struct ath_softc *sc) spin_unlock_bh(&sc->sc_resetlock); ath9k_hw_phy_disable(ah); - ath9k_hw_configpcipowersave(ah, 1); + ath9k_hw_configpcipowersave(ah, 1, 1); ath9k_ps_restore(sc); ath9k_hw_setpower(ah, ATH9K_PM_FULL_SLEEP); } @@ -1226,11 +1226,6 @@ static void ath9k_rfkill_poll_state(struct ieee80211_hw *hw) bool blocked = !!ath_is_rfkill_set(sc); wiphy_rfkill_set_hw_state(hw->wiphy, blocked); - - if (blocked) - ath_radio_disable(sc); - else - ath_radio_enable(sc); } static void ath_start_rfkill_poll(struct ath_softc *sc) @@ -1260,6 +1255,7 @@ void ath_detach(struct ath_softc *sc) DPRINTF(sc, ATH_DBG_CONFIG, "Detach ATH hw\n"); ath_deinit_leds(sc); + wiphy_rfkill_stop_polling(sc->hw->wiphy); for (i = 0; i < sc->num_sec_wiphy; i++) { struct ath_wiphy *aphy = sc->sec_wiphy[i]; @@ -1559,6 +1555,8 @@ void ath_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw) BIT(NL80211_IFTYPE_ADHOC) | BIT(NL80211_IFTYPE_MESH_POINT); + hw->wiphy->ps_default = false; + hw->queues = 4; hw->max_rates = 4; hw->channel_change_time = 5000; @@ -1942,7 +1940,7 @@ static int ath9k_start(struct ieee80211_hw *hw) init_channel = ath_get_curchannel(sc, hw); /* Reset SERDES registers */ - ath9k_hw_configpcipowersave(sc->sc_ah, 0); + ath9k_hw_configpcipowersave(sc->sc_ah, 0, 0); /* * The basic interface to setting the hardware in a good @@ -2166,11 +2164,9 @@ static void ath9k_stop(struct ieee80211_hw *hw) } else sc->rx.rxlink = NULL; - wiphy_rfkill_stop_polling(sc->hw->wiphy); - /* disable HAL and put h/w to sleep */ ath9k_hw_disable(sc->sc_ah); - ath9k_hw_configpcipowersave(sc->sc_ah, 1); + ath9k_hw_configpcipowersave(sc->sc_ah, 1, 1); ath9k_hw_setpower(sc->sc_ah, ATH9K_PM_FULL_SLEEP); sc->sc_flags |= SC_OP_INVALID; diff --git a/drivers/net/wireless/ath/ath9k/rc.c b/drivers/net/wireless/ath/ath9k/rc.c index 16a271787b85..1895d63aad0a 100644 --- a/drivers/net/wireless/ath/ath9k/rc.c +++ b/drivers/net/wireless/ath/ath9k/rc.c @@ -679,7 +679,7 @@ static u8 ath_rc_get_highest_rix(struct ath_softc *sc, return rate; if (rate_table->info[rate].valid_single_stream && - !(ath_rc_priv->ht_cap & WLAN_RC_DS_FLAG)); + !(ath_rc_priv->ht_cap & WLAN_RC_DS_FLAG)) return rate; /* This should not happen */ diff --git a/drivers/net/wireless/ath/ath9k/reg.h b/drivers/net/wireless/ath/ath9k/reg.h index e5c29eb86e80..d83b77f821e9 100644 --- a/drivers/net/wireless/ath/ath9k/reg.h +++ b/drivers/net/wireless/ath/ath9k/reg.h @@ -676,8 +676,9 @@ #define AR_RC_HOSTIF 0x00000100 #define AR_WA 0x4004 +#define AR_WA_D3_L1_DISABLE (1 << 14) #define AR9285_WA_DEFAULT 0x004a05cb -#define AR9280_WA_DEFAULT 0x0040073f +#define AR9280_WA_DEFAULT 0x0040073b #define AR_WA_DEFAULT 0x0000073f diff --git a/drivers/net/wireless/b43/Kconfig b/drivers/net/wireless/b43/Kconfig index 83e38134accb..54ea61c15d8b 100644 --- a/drivers/net/wireless/b43/Kconfig +++ b/drivers/net/wireless/b43/Kconfig @@ -61,11 +61,28 @@ config B43_PCMCIA If unsure, say N. +config B43_SDIO + bool "Broadcom 43xx SDIO device support (EXPERIMENTAL)" + depends on B43 && SSB_SDIOHOST_POSSIBLE && EXPERIMENTAL + select SSB_SDIOHOST + ---help--- + Broadcom 43xx device support for Soft-MAC SDIO devices. + + With this config option you can drive Soft-MAC b43 cards with a + Secure Digital I/O interface. + This includes the WLAN daughter card found on the Nintendo Wii + video game console. + Note that this does not support Broadcom 43xx Full-MAC devices. + + It's safe to select Y here, even if you don't have a B43 SDIO device. + + If unsure, say N. + # Data transfers to the device via PIO -# This is only needed on PCMCIA devices. All others can do DMA properly. +# This is only needed on PCMCIA and SDIO devices. All others can do DMA properly. config B43_PIO bool - depends on B43 && (B43_PCMCIA || B43_FORCE_PIO) + depends on B43 && (B43_SDIO || B43_PCMCIA || B43_FORCE_PIO) select SSB_BLOCKIO default y diff --git a/drivers/net/wireless/b43/Makefile b/drivers/net/wireless/b43/Makefile index da379f4b0c3a..84772a2542dc 100644 --- a/drivers/net/wireless/b43/Makefile +++ b/drivers/net/wireless/b43/Makefile @@ -16,6 +16,7 @@ b43-$(CONFIG_B43_PIO) += pio.o b43-y += rfkill.o b43-$(CONFIG_B43_LEDS) += leds.o b43-$(CONFIG_B43_PCMCIA) += pcmcia.o +b43-$(CONFIG_B43_SDIO) += sdio.o b43-$(CONFIG_B43_DEBUG) += debugfs.o obj-$(CONFIG_B43) += b43.o diff --git a/drivers/net/wireless/b43/b43.h b/drivers/net/wireless/b43/b43.h index 09cfe68537b6..660716214d49 100644 --- a/drivers/net/wireless/b43/b43.h +++ b/drivers/net/wireless/b43/b43.h @@ -607,86 +607,7 @@ struct b43_qos_params { struct ieee80211_tx_queue_params p; }; -struct b43_wldev; - -/* Data structure for the WLAN parts (802.11 cores) of the b43 chip. */ -struct b43_wl { - /* Pointer to the active wireless device on this chip */ - struct b43_wldev *current_dev; - /* Pointer to the ieee80211 hardware data structure */ - struct ieee80211_hw *hw; - - /* Global driver mutex. Every operation must run with this mutex locked. */ - struct mutex mutex; - /* Hard-IRQ spinlock. This lock protects things used in the hard-IRQ - * handler, only. This basically is just the IRQ mask register. */ - spinlock_t hardirq_lock; - - /* The number of queues that were registered with the mac80211 subsystem - * initially. This is a backup copy of hw->queues in case hw->queues has - * to be dynamically lowered at runtime (Firmware does not support QoS). - * hw->queues has to be restored to the original value before unregistering - * from the mac80211 subsystem. */ - u16 mac80211_initially_registered_queues; - - /* R/W lock for data transmission. - * Transmissions on 2+ queues can run concurrently, but somebody else - * might sync with TX by write_lock_irqsave()'ing. */ - rwlock_t tx_lock; - /* Lock for LEDs access. */ - spinlock_t leds_lock; - - /* We can only have one operating interface (802.11 core) - * at a time. General information about this interface follows. - */ - - struct ieee80211_vif *vif; - /* The MAC address of the operating interface. */ - u8 mac_addr[ETH_ALEN]; - /* Current BSSID */ - u8 bssid[ETH_ALEN]; - /* Interface type. (NL80211_IFTYPE_XXX) */ - int if_type; - /* Is the card operating in AP, STA or IBSS mode? */ - bool operating; - /* filter flags */ - unsigned int filter_flags; - /* Stats about the wireless interface */ - struct ieee80211_low_level_stats ieee_stats; - -#ifdef CONFIG_B43_HWRNG - struct hwrng rng; - bool rng_initialized; - char rng_name[30 + 1]; -#endif /* CONFIG_B43_HWRNG */ - - /* List of all wireless devices on this chip */ - struct list_head devlist; - u8 nr_devs; - - bool radiotap_enabled; - bool radio_enabled; - - /* The beacon we are currently using (AP or IBSS mode). */ - struct sk_buff *current_beacon; - bool beacon0_uploaded; - bool beacon1_uploaded; - bool beacon_templates_virgin; /* Never wrote the templates? */ - struct work_struct beacon_update_trigger; - - /* The current QOS parameters for the 4 queues. */ - struct b43_qos_params qos_params[4]; - - /* Work for adjustment of the transmission power. - * This is scheduled when we determine that the actual TX output - * power doesn't match what we want. */ - struct work_struct txpower_adjust_work; - - /* Packet transmit work */ - struct work_struct tx_work; - /* Queue of packets to be transmitted. */ - struct sk_buff_head tx_queue; -}; +struct b43_wl; /* The type of the firmware file. */ enum b43_firmware_file_type { @@ -768,13 +689,10 @@ struct b43_wldev { /* The device initialization status. * Use b43_status() to query. */ atomic_t __init_status; - /* Saved init status for handling suspend. */ - int suspend_init_status; bool bad_frames_preempt; /* Use "Bad Frames Preemption" (default off) */ bool dfq_valid; /* Directed frame queue valid (IBSS PS mode, ATIM) */ bool radio_hw_enable; /* saved state of radio hardware enabled state */ - bool suspend_in_progress; /* TRUE, if we are in a suspend/resume cycle */ bool qos_enabled; /* TRUE, if QoS is used. */ bool hwcrypto_enabled; /* TRUE, if HW crypto acceleration is enabled. */ @@ -794,12 +712,6 @@ struct b43_wldev { /* Various statistics about the physical device. */ struct b43_stats stats; - /* The device LEDs. */ - struct b43_led led_tx; - struct b43_led led_rx; - struct b43_led led_assoc; - struct b43_led led_radio; - /* Reason code of the last interrupt. */ u32 irq_reason; u32 dma_reason[6]; @@ -830,9 +742,104 @@ struct b43_wldev { /* Debugging stuff follows. */ #ifdef CONFIG_B43_DEBUG struct b43_dfsentry *dfsentry; + unsigned int irq_count; + unsigned int irq_bit_count[32]; + unsigned int tx_count; + unsigned int rx_count; #endif }; +/* + * Include goes here to avoid a dependency problem. + * A better fix would be to integrate xmit.h into b43.h. + */ +#include "xmit.h" + +/* Data structure for the WLAN parts (802.11 cores) of the b43 chip. */ +struct b43_wl { + /* Pointer to the active wireless device on this chip */ + struct b43_wldev *current_dev; + /* Pointer to the ieee80211 hardware data structure */ + struct ieee80211_hw *hw; + + /* Global driver mutex. Every operation must run with this mutex locked. */ + struct mutex mutex; + /* Hard-IRQ spinlock. This lock protects things used in the hard-IRQ + * handler, only. This basically is just the IRQ mask register. */ + spinlock_t hardirq_lock; + + /* The number of queues that were registered with the mac80211 subsystem + * initially. This is a backup copy of hw->queues in case hw->queues has + * to be dynamically lowered at runtime (Firmware does not support QoS). + * hw->queues has to be restored to the original value before unregistering + * from the mac80211 subsystem. */ + u16 mac80211_initially_registered_queues; + + /* We can only have one operating interface (802.11 core) + * at a time. General information about this interface follows. + */ + + struct ieee80211_vif *vif; + /* The MAC address of the operating interface. */ + u8 mac_addr[ETH_ALEN]; + /* Current BSSID */ + u8 bssid[ETH_ALEN]; + /* Interface type. (NL80211_IFTYPE_XXX) */ + int if_type; + /* Is the card operating in AP, STA or IBSS mode? */ + bool operating; + /* filter flags */ + unsigned int filter_flags; + /* Stats about the wireless interface */ + struct ieee80211_low_level_stats ieee_stats; + +#ifdef CONFIG_B43_HWRNG + struct hwrng rng; + bool rng_initialized; + char rng_name[30 + 1]; +#endif /* CONFIG_B43_HWRNG */ + + /* List of all wireless devices on this chip */ + struct list_head devlist; + u8 nr_devs; + + bool radiotap_enabled; + bool radio_enabled; + + /* The beacon we are currently using (AP or IBSS mode). */ + struct sk_buff *current_beacon; + bool beacon0_uploaded; + bool beacon1_uploaded; + bool beacon_templates_virgin; /* Never wrote the templates? */ + struct work_struct beacon_update_trigger; + + /* The current QOS parameters for the 4 queues. */ + struct b43_qos_params qos_params[4]; + + /* Work for adjustment of the transmission power. + * This is scheduled when we determine that the actual TX output + * power doesn't match what we want. */ + struct work_struct txpower_adjust_work; + + /* Packet transmit work */ + struct work_struct tx_work; + /* Queue of packets to be transmitted. */ + struct sk_buff_head tx_queue; + + /* The device LEDs. */ + struct b43_leds leds; + +#ifdef CONFIG_B43_PIO + /* + * RX/TX header/tail buffers used by the frame transmit functions. + */ + struct b43_rxhdr_fw4 rxhdr; + struct b43_txhdr txhdr; + u8 rx_tail[4]; + u8 tx_tail[4]; +#endif /* CONFIG_B43_PIO */ +}; + static inline struct b43_wl *hw_to_b43_wl(struct ieee80211_hw *hw) { return hw->priv; diff --git a/drivers/net/wireless/b43/debugfs.c b/drivers/net/wireless/b43/debugfs.c index 8f64943e3f60..80b19a44a407 100644 --- a/drivers/net/wireless/b43/debugfs.c +++ b/drivers/net/wireless/b43/debugfs.c @@ -689,6 +689,7 @@ static void b43_add_dynamic_debug(struct b43_wldev *dev) add_dyn_dbg("debug_lo", B43_DBG_LO, 0); add_dyn_dbg("debug_firmware", B43_DBG_FIRMWARE, 0); add_dyn_dbg("debug_keys", B43_DBG_KEYS, 0); + add_dyn_dbg("debug_verbose_stats", B43_DBG_VERBOSESTATS, 0); #undef add_dyn_dbg } diff --git a/drivers/net/wireless/b43/debugfs.h b/drivers/net/wireless/b43/debugfs.h index e47b4b488b04..822aad8842f4 100644 --- a/drivers/net/wireless/b43/debugfs.h +++ b/drivers/net/wireless/b43/debugfs.h @@ -13,6 +13,7 @@ enum b43_dyndbg { /* Dynamic debugging features */ B43_DBG_LO, B43_DBG_FIRMWARE, B43_DBG_KEYS, + B43_DBG_VERBOSESTATS, __B43_NR_DYNDBG, }; diff --git a/drivers/net/wireless/b43/dma.c b/drivers/net/wireless/b43/dma.c index a467ee260a19..de4e804bedf0 100644 --- a/drivers/net/wireless/b43/dma.c +++ b/drivers/net/wireless/b43/dma.c @@ -1157,8 +1157,9 @@ struct b43_dmaring *parse_cookie(struct b43_wldev *dev, u16 cookie, int *slot) } static int dma_tx_fragment(struct b43_dmaring *ring, - struct sk_buff *skb) + struct sk_buff **in_skb) { + struct sk_buff *skb = *in_skb; const struct b43_dma_ops *ops = ring->ops; struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); u8 *header; @@ -1224,8 +1225,14 @@ static int dma_tx_fragment(struct b43_dmaring *ring, } memcpy(skb_put(bounce_skb, skb->len), skb->data, skb->len); + memcpy(bounce_skb->cb, skb->cb, sizeof(skb->cb)); + bounce_skb->dev = skb->dev; + skb_set_queue_mapping(bounce_skb, skb_get_queue_mapping(skb)); + info = IEEE80211_SKB_CB(bounce_skb); + dev_kfree_skb_any(skb); skb = bounce_skb; + *in_skb = bounce_skb; meta->skb = skb; meta->dmaaddr = map_descbuffer(ring, skb->data, skb->len, 1); if (b43_dma_mapping_error(ring, meta->dmaaddr, skb->len, 1)) { @@ -1355,7 +1362,11 @@ int b43_dma_tx(struct b43_wldev *dev, struct sk_buff *skb) * static, so we don't need to store it per frame. */ ring->queue_prio = skb_get_queue_mapping(skb); - err = dma_tx_fragment(ring, skb); + /* dma_tx_fragment might reallocate the skb, so invalidate pointers pointing + * into the skb data or cb now. */ + hdr = NULL; + info = NULL; + err = dma_tx_fragment(ring, &skb); if (unlikely(err == -ENOKEY)) { /* Drop this packet, as we don't have the encryption key * anymore and must not transmit it unencrypted. */ @@ -1428,9 +1439,9 @@ void b43_dma_handle_txstatus(struct b43_wldev *dev, ring->nr_failed_tx_packets++; ring->nr_total_packet_tries += status->frame_count; #endif /* DEBUG */ - ieee80211_tx_status_irqsafe(dev->wl->hw, meta->skb); + ieee80211_tx_status(dev->wl->hw, meta->skb); - /* skb is freed by ieee80211_tx_status_irqsafe() */ + /* skb is freed by ieee80211_tx_status() */ meta->skb = NULL; } else { /* No need to call free_descriptor_buffer here, as diff --git a/drivers/net/wireless/b43/leds.c b/drivers/net/wireless/b43/leds.c index c8b317094c31..1e8dba488004 100644 --- a/drivers/net/wireless/b43/leds.c +++ b/drivers/net/wireless/b43/leds.c @@ -34,57 +34,88 @@ static void b43_led_turn_on(struct b43_wldev *dev, u8 led_index, bool activelow) { - struct b43_wl *wl = dev->wl; - unsigned long flags; u16 ctl; - spin_lock_irqsave(&wl->leds_lock, flags); ctl = b43_read16(dev, B43_MMIO_GPIO_CONTROL); if (activelow) ctl &= ~(1 << led_index); else ctl |= (1 << led_index); b43_write16(dev, B43_MMIO_GPIO_CONTROL, ctl); - spin_unlock_irqrestore(&wl->leds_lock, flags); } static void b43_led_turn_off(struct b43_wldev *dev, u8 led_index, bool activelow) { - struct b43_wl *wl = dev->wl; - unsigned long flags; u16 ctl; - spin_lock_irqsave(&wl->leds_lock, flags); ctl = b43_read16(dev, B43_MMIO_GPIO_CONTROL); if (activelow) ctl |= (1 << led_index); else ctl &= ~(1 << led_index); b43_write16(dev, B43_MMIO_GPIO_CONTROL, ctl); - spin_unlock_irqrestore(&wl->leds_lock, flags); } -/* Callback from the LED subsystem. */ -static void b43_led_brightness_set(struct led_classdev *led_dev, - enum led_brightness brightness) +static void b43_led_update(struct b43_wldev *dev, + struct b43_led *led) { - struct b43_led *led = container_of(led_dev, struct b43_led, led_dev); - struct b43_wldev *dev = led->dev; bool radio_enabled; + bool turn_on; - if (unlikely(b43_status(dev) < B43_STAT_INITIALIZED)) + if (!led->wl) return; - /* Checking the radio-enabled status here is slightly racy, - * but we want to avoid the locking overhead and we don't care - * whether the LED has the wrong state for a second. */ radio_enabled = (dev->phy.radio_on && dev->radio_hw_enable); - if (brightness == LED_OFF || !radio_enabled) - b43_led_turn_off(dev, led->index, led->activelow); + /* The led->state read is racy, but we don't care. In case we raced + * with the brightness_set handler, we will be called again soon + * to fixup our state. */ + if (radio_enabled) + turn_on = atomic_read(&led->state) != LED_OFF; else + turn_on = 0; + if (turn_on == led->hw_state) + return; + led->hw_state = turn_on; + + if (turn_on) b43_led_turn_on(dev, led->index, led->activelow); + else + b43_led_turn_off(dev, led->index, led->activelow); +} + +static void b43_leds_work(struct work_struct *work) +{ + struct b43_leds *leds = container_of(work, struct b43_leds, work); + struct b43_wl *wl = container_of(leds, struct b43_wl, leds); + struct b43_wldev *dev; + + mutex_lock(&wl->mutex); + dev = wl->current_dev; + if (unlikely(!dev || b43_status(dev) < B43_STAT_STARTED)) + goto out_unlock; + + b43_led_update(dev, &wl->leds.led_tx); + b43_led_update(dev, &wl->leds.led_rx); + b43_led_update(dev, &wl->leds.led_radio); + b43_led_update(dev, &wl->leds.led_assoc); + +out_unlock: + mutex_unlock(&wl->mutex); +} + +/* Callback from the LED subsystem. */ +static void b43_led_brightness_set(struct led_classdev *led_dev, + enum led_brightness brightness) +{ + struct b43_led *led = container_of(led_dev, struct b43_led, led_dev); + struct b43_wl *wl = led->wl; + + if (likely(!wl->leds.stop)) { + atomic_set(&led->state, brightness); + ieee80211_queue_work(wl->hw, &wl->leds.work); + } } static int b43_register_led(struct b43_wldev *dev, struct b43_led *led, @@ -93,15 +124,15 @@ static int b43_register_led(struct b43_wldev *dev, struct b43_led *led, { int err; - b43_led_turn_off(dev, led_index, activelow); - if (led->dev) + if (led->wl) return -EEXIST; if (!default_trigger) return -EINVAL; - led->dev = dev; + led->wl = dev->wl; led->index = led_index; led->activelow = activelow; strncpy(led->name, name, sizeof(led->name)); + atomic_set(&led->state, 0); led->led_dev.name = led->name; led->led_dev.default_trigger = default_trigger; @@ -110,19 +141,19 @@ static int b43_register_led(struct b43_wldev *dev, struct b43_led *led, err = led_classdev_register(dev->dev->dev, &led->led_dev); if (err) { b43warn(dev->wl, "LEDs: Failed to register %s\n", name); - led->dev = NULL; + led->wl = NULL; return err; } + return 0; } static void b43_unregister_led(struct b43_led *led) { - if (!led->dev) + if (!led->wl) return; led_classdev_unregister(&led->led_dev); - b43_led_turn_off(led->dev, led->index, led->activelow); - led->dev = NULL; + led->wl = NULL; } static void b43_map_led(struct b43_wldev *dev, @@ -137,24 +168,20 @@ static void b43_map_led(struct b43_wldev *dev, * generic LED triggers. */ switch (behaviour) { case B43_LED_INACTIVE: - break; case B43_LED_OFF: - b43_led_turn_off(dev, led_index, activelow); - break; case B43_LED_ON: - b43_led_turn_on(dev, led_index, activelow); break; case B43_LED_ACTIVITY: case B43_LED_TRANSFER: case B43_LED_APTRANSFER: snprintf(name, sizeof(name), "b43-%s::tx", wiphy_name(hw->wiphy)); - b43_register_led(dev, &dev->led_tx, name, + b43_register_led(dev, &dev->wl->leds.led_tx, name, ieee80211_get_tx_led_name(hw), led_index, activelow); snprintf(name, sizeof(name), "b43-%s::rx", wiphy_name(hw->wiphy)); - b43_register_led(dev, &dev->led_rx, name, + b43_register_led(dev, &dev->wl->leds.led_rx, name, ieee80211_get_rx_led_name(hw), led_index, activelow); break; @@ -164,18 +191,15 @@ static void b43_map_led(struct b43_wldev *dev, case B43_LED_MODE_BG: snprintf(name, sizeof(name), "b43-%s::radio", wiphy_name(hw->wiphy)); - b43_register_led(dev, &dev->led_radio, name, + b43_register_led(dev, &dev->wl->leds.led_radio, name, ieee80211_get_radio_led_name(hw), led_index, activelow); - /* Sync the RF-kill LED state with radio and switch states. */ - if (dev->phy.radio_on && b43_is_hw_radio_enabled(dev)) - b43_led_turn_on(dev, led_index, activelow); break; case B43_LED_WEIRD: case B43_LED_ASSOC: snprintf(name, sizeof(name), "b43-%s::assoc", wiphy_name(hw->wiphy)); - b43_register_led(dev, &dev->led_assoc, name, + b43_register_led(dev, &dev->wl->leds.led_assoc, name, ieee80211_get_assoc_led_name(hw), led_index, activelow); break; @@ -186,58 +210,150 @@ static void b43_map_led(struct b43_wldev *dev, } } -void b43_leds_init(struct b43_wldev *dev) +static void b43_led_get_sprominfo(struct b43_wldev *dev, + unsigned int led_index, + enum b43_led_behaviour *behaviour, + bool *activelow) { struct ssb_bus *bus = dev->dev->bus; u8 sprom[4]; - int i; - enum b43_led_behaviour behaviour; - bool activelow; sprom[0] = bus->sprom.gpio0; sprom[1] = bus->sprom.gpio1; sprom[2] = bus->sprom.gpio2; sprom[3] = bus->sprom.gpio3; - for (i = 0; i < 4; i++) { - if (sprom[i] == 0xFF) { - /* There is no LED information in the SPROM - * for this LED. Hardcode it here. */ - activelow = 0; - switch (i) { - case 0: - behaviour = B43_LED_ACTIVITY; - activelow = 1; - if (bus->boardinfo.vendor == PCI_VENDOR_ID_COMPAQ) - behaviour = B43_LED_RADIO_ALL; - break; - case 1: - behaviour = B43_LED_RADIO_B; - if (bus->boardinfo.vendor == PCI_VENDOR_ID_ASUSTEK) - behaviour = B43_LED_ASSOC; - break; - case 2: - behaviour = B43_LED_RADIO_A; - break; - case 3: - behaviour = B43_LED_OFF; - break; - default: - B43_WARN_ON(1); - return; - } + if (sprom[led_index] == 0xFF) { + /* There is no LED information in the SPROM + * for this LED. Hardcode it here. */ + *activelow = 0; + switch (led_index) { + case 0: + *behaviour = B43_LED_ACTIVITY; + *activelow = 1; + if (bus->boardinfo.vendor == PCI_VENDOR_ID_COMPAQ) + *behaviour = B43_LED_RADIO_ALL; + break; + case 1: + *behaviour = B43_LED_RADIO_B; + if (bus->boardinfo.vendor == PCI_VENDOR_ID_ASUSTEK) + *behaviour = B43_LED_ASSOC; + break; + case 2: + *behaviour = B43_LED_RADIO_A; + break; + case 3: + *behaviour = B43_LED_OFF; + break; + default: + B43_WARN_ON(1); + return; + } + } else { + *behaviour = sprom[led_index] & B43_LED_BEHAVIOUR; + *activelow = !!(sprom[led_index] & B43_LED_ACTIVELOW); + } +} + +void b43_leds_init(struct b43_wldev *dev) +{ + struct b43_led *led; + unsigned int i; + enum b43_led_behaviour behaviour; + bool activelow; + + /* Sync the RF-kill LED state (if we have one) with radio and switch states. */ + led = &dev->wl->leds.led_radio; + if (led->wl) { + if (dev->phy.radio_on && b43_is_hw_radio_enabled(dev)) { + b43_led_turn_on(dev, led->index, led->activelow); + led->hw_state = 1; + atomic_set(&led->state, 1); } else { - behaviour = sprom[i] & B43_LED_BEHAVIOUR; - activelow = !!(sprom[i] & B43_LED_ACTIVELOW); + b43_led_turn_off(dev, led->index, led->activelow); + led->hw_state = 0; + atomic_set(&led->state, 0); } - b43_map_led(dev, i, behaviour, activelow); } + + /* Initialize TX/RX/ASSOC leds */ + led = &dev->wl->leds.led_tx; + if (led->wl) { + b43_led_turn_off(dev, led->index, led->activelow); + led->hw_state = 0; + atomic_set(&led->state, 0); + } + led = &dev->wl->leds.led_rx; + if (led->wl) { + b43_led_turn_off(dev, led->index, led->activelow); + led->hw_state = 0; + atomic_set(&led->state, 0); + } + led = &dev->wl->leds.led_assoc; + if (led->wl) { + b43_led_turn_off(dev, led->index, led->activelow); + led->hw_state = 0; + atomic_set(&led->state, 0); + } + + /* Initialize other LED states. */ + for (i = 0; i < B43_MAX_NR_LEDS; i++) { + b43_led_get_sprominfo(dev, i, &behaviour, &activelow); + switch (behaviour) { + case B43_LED_OFF: + b43_led_turn_off(dev, i, activelow); + break; + case B43_LED_ON: + b43_led_turn_on(dev, i, activelow); + break; + default: + /* Leave others as-is. */ + break; + } + } + + dev->wl->leds.stop = 0; } void b43_leds_exit(struct b43_wldev *dev) { - b43_unregister_led(&dev->led_tx); - b43_unregister_led(&dev->led_rx); - b43_unregister_led(&dev->led_assoc); - b43_unregister_led(&dev->led_radio); + struct b43_leds *leds = &dev->wl->leds; + + b43_led_turn_off(dev, leds->led_tx.index, leds->led_tx.activelow); + b43_led_turn_off(dev, leds->led_rx.index, leds->led_rx.activelow); + b43_led_turn_off(dev, leds->led_assoc.index, leds->led_assoc.activelow); + b43_led_turn_off(dev, leds->led_radio.index, leds->led_radio.activelow); +} + +void b43_leds_stop(struct b43_wldev *dev) +{ + struct b43_leds *leds = &dev->wl->leds; + + leds->stop = 1; + cancel_work_sync(&leds->work); +} + +void b43_leds_register(struct b43_wldev *dev) +{ + unsigned int i; + enum b43_led_behaviour behaviour; + bool activelow; + + INIT_WORK(&dev->wl->leds.work, b43_leds_work); + + /* Register the LEDs to the LED subsystem. */ + for (i = 0; i < B43_MAX_NR_LEDS; i++) { + b43_led_get_sprominfo(dev, i, &behaviour, &activelow); + b43_map_led(dev, i, behaviour, activelow); + } +} + +void b43_leds_unregister(struct b43_wl *wl) +{ + struct b43_leds *leds = &wl->leds; + + b43_unregister_led(&leds->led_tx); + b43_unregister_led(&leds->led_rx); + b43_unregister_led(&leds->led_assoc); + b43_unregister_led(&leds->led_radio); } diff --git a/drivers/net/wireless/b43/leds.h b/drivers/net/wireless/b43/leds.h index b8b1dd521243..32b66d53cdac 100644 --- a/drivers/net/wireless/b43/leds.h +++ b/drivers/net/wireless/b43/leds.h @@ -1,18 +1,20 @@ #ifndef B43_LEDS_H_ #define B43_LEDS_H_ +struct b43_wl; struct b43_wldev; #ifdef CONFIG_B43_LEDS #include <linux/types.h> #include <linux/leds.h> +#include <linux/workqueue.h> #define B43_LED_MAX_NAME_LEN 31 struct b43_led { - struct b43_wldev *dev; + struct b43_wl *wl; /* The LED class device */ struct led_classdev led_dev; /* The index number of the LED. */ @@ -22,8 +24,24 @@ struct b43_led { bool activelow; /* The unique name string for this LED device. */ char name[B43_LED_MAX_NAME_LEN + 1]; + /* The current status of the LED. This is updated locklessly. */ + atomic_t state; + /* The active state in hardware. */ + bool hw_state; }; +struct b43_leds { + struct b43_led led_tx; + struct b43_led led_rx; + struct b43_led led_radio; + struct b43_led led_assoc; + + bool stop; + struct work_struct work; +}; + +#define B43_MAX_NR_LEDS 4 + #define B43_LED_BEHAVIOUR 0x7F #define B43_LED_ACTIVELOW 0x80 /* LED behaviour values */ @@ -42,23 +60,35 @@ enum b43_led_behaviour { B43_LED_INACTIVE, }; +void b43_leds_register(struct b43_wldev *dev); +void b43_leds_unregister(struct b43_wl *wl); void b43_leds_init(struct b43_wldev *dev); void b43_leds_exit(struct b43_wldev *dev); +void b43_leds_stop(struct b43_wldev *dev); #else /* CONFIG_B43_LEDS */ /* LED support disabled */ -struct b43_led { +struct b43_leds { /* empty */ }; +static inline void b43_leds_register(struct b43_wldev *dev) +{ +} +static inline void b43_leds_unregister(struct b43_wl *wl) +{ +} static inline void b43_leds_init(struct b43_wldev *dev) { } static inline void b43_leds_exit(struct b43_wldev *dev) { } +static inline void b43_leds_stop(struct b43_wldev *dev) +{ +} #endif /* CONFIG_B43_LEDS */ #endif /* B43_LEDS_H_ */ diff --git a/drivers/net/wireless/b43/main.c b/drivers/net/wireless/b43/main.c index e789792a36bc..098dda1a67c1 100644 --- a/drivers/net/wireless/b43/main.c +++ b/drivers/net/wireless/b43/main.c @@ -8,6 +8,9 @@ Copyright (c) 2005 Danny van Dyk <kugelfang@gentoo.org> Copyright (c) 2005 Andreas Jaggi <andreas.jaggi@waterwave.ch> + SDIO support + Copyright (c) 2009 Albert Herranz <albert_herranz@yahoo.es> + Some parts of the code in this file are derived from the ipw2200 driver Copyright(c) 2003 - 2004 Intel Corporation. @@ -53,6 +56,8 @@ #include "xmit.h" #include "lo.h" #include "pcmcia.h" +#include "sdio.h" +#include <linux/mmc/sdio_func.h> MODULE_DESCRIPTION("Broadcom B43 wireless driver"); MODULE_AUTHOR("Martin Langer"); @@ -1587,7 +1592,7 @@ static void b43_beacon_update_trigger_work(struct work_struct *work) mutex_lock(&wl->mutex); dev = wl->current_dev; if (likely(dev && (b43_status(dev) >= B43_STAT_INITIALIZED))) { - if (0 /*FIXME dev->dev->bus->bustype == SSB_BUSTYPE_SDIO*/) { + if (dev->dev->bus->bustype == SSB_BUSTYPE_SDIO) { /* wl->mutex is enough. */ b43_do_beacon_update_trigger_work(dev); mmiowb(); @@ -1825,6 +1830,16 @@ static void b43_do_interrupt_thread(struct b43_wldev *dev) /* Re-enable interrupts on the device by restoring the current interrupt mask. */ b43_write32(dev, B43_MMIO_GEN_IRQ_MASK, dev->irq_mask); + +#if B43_DEBUG + if (b43_debug(dev, B43_DBG_VERBOSESTATS)) { + dev->irq_count++; + for (i = 0; i < ARRAY_SIZE(dev->irq_bit_count); i++) { + if (reason & (1 << i)) + dev->irq_bit_count[i]++; + } + } +#endif } /* Interrupt thread handler. Handles device interrupts in thread context. */ @@ -1905,6 +1920,21 @@ static irqreturn_t b43_interrupt_handler(int irq, void *dev_id) return ret; } +/* SDIO interrupt handler. This runs in process context. */ +static void b43_sdio_interrupt_handler(struct b43_wldev *dev) +{ + struct b43_wl *wl = dev->wl; + irqreturn_t ret; + + mutex_lock(&wl->mutex); + + ret = b43_do_interrupt(dev); + if (ret == IRQ_WAKE_THREAD) + b43_do_interrupt_thread(dev); + + mutex_unlock(&wl->mutex); +} + void b43_do_release_fw(struct b43_firmware_file *fw) { release_firmware(fw->data); @@ -2645,6 +2675,20 @@ static void b43_adjust_opmode(struct b43_wldev *dev) cfp_pretbtt = 50; } b43_write16(dev, 0x612, cfp_pretbtt); + + /* FIXME: We don't currently implement the PMQ mechanism, + * so always disable it. If we want to implement PMQ, + * we need to enable it here (clear DISCPMQ) in AP mode. + */ + if (0 /* ctl & B43_MACCTL_AP */) { + b43_write32(dev, B43_MMIO_MACCTL, + b43_read32(dev, B43_MMIO_MACCTL) + & ~B43_MACCTL_DISCPMQ); + } else { + b43_write32(dev, B43_MMIO_MACCTL, + b43_read32(dev, B43_MMIO_MACCTL) + | B43_MACCTL_DISCPMQ); + } } static void b43_rate_memory_write(struct b43_wldev *dev, u16 rate, int is_ofdm) @@ -2873,6 +2917,27 @@ static void b43_periodic_every15sec(struct b43_wldev *dev) atomic_set(&phy->txerr_cnt, B43_PHY_TX_BADNESS_LIMIT); wmb(); + +#if B43_DEBUG + if (b43_debug(dev, B43_DBG_VERBOSESTATS)) { + unsigned int i; + + b43dbg(dev->wl, "Stats: %7u IRQs/sec, %7u TX/sec, %7u RX/sec\n", + dev->irq_count / 15, + dev->tx_count / 15, + dev->rx_count / 15); + dev->irq_count = 0; + dev->tx_count = 0; + dev->rx_count = 0; + for (i = 0; i < ARRAY_SIZE(dev->irq_bit_count); i++) { + if (dev->irq_bit_count[i]) { + b43dbg(dev->wl, "Stats: %7u IRQ-%02u/sec (0x%08X)\n", + dev->irq_bit_count[i] / 15, i, (1 << i)); + dev->irq_bit_count[i] = 0; + } + } + } +#endif } static void do_periodic_work(struct b43_wldev *dev) @@ -3002,14 +3067,18 @@ static void b43_security_init(struct b43_wldev *dev) static int b43_rng_read(struct hwrng *rng, u32 *data) { struct b43_wl *wl = (struct b43_wl *)rng->priv; + struct b43_wldev *dev; + int count = -ENODEV; - /* FIXME: We need to take wl->mutex here to make sure the device - * is not going away from under our ass. However it could deadlock - * with hwrng internal locking. */ - - *data = b43_read16(wl->current_dev, B43_MMIO_RNG); + mutex_lock(&wl->mutex); + dev = wl->current_dev; + if (likely(dev && b43_status(dev) >= B43_STAT_INITIALIZED)) { + *data = b43_read16(dev, B43_MMIO_RNG); + count = sizeof(u16); + } + mutex_unlock(&wl->mutex); - return (sizeof(u16)); + return count; } #endif /* CONFIG_B43_HWRNG */ @@ -3068,6 +3137,9 @@ static void b43_tx_work(struct work_struct *work) dev_kfree_skb(skb); /* Drop it */ } +#if B43_DEBUG + dev->tx_count++; +#endif mutex_unlock(&wl->mutex); } @@ -3802,6 +3874,7 @@ static struct b43_wldev * b43_wireless_core_stop(struct b43_wldev *dev) { struct b43_wl *wl = dev->wl; struct b43_wldev *orig_dev; + u32 mask; redo: if (!dev || b43_status(dev) < B43_STAT_STARTED) @@ -3820,7 +3893,7 @@ redo: /* Disable interrupts on the device. */ b43_set_status(dev, B43_STAT_INITIALIZED); - if (0 /*FIXME dev->dev->bus->bustype == SSB_BUSTYPE_SDIO*/) { + if (dev->dev->bus->bustype == SSB_BUSTYPE_SDIO) { /* wl->mutex is locked. That is enough. */ b43_write32(dev, B43_MMIO_GEN_IRQ_MASK, 0); b43_read32(dev, B43_MMIO_GEN_IRQ_MASK); /* Flush */ @@ -3830,10 +3903,15 @@ redo: b43_read32(dev, B43_MMIO_GEN_IRQ_MASK); /* Flush */ spin_unlock_irq(&wl->hardirq_lock); } - /* Synchronize the interrupt handlers. Unlock to avoid deadlocks. */ + /* Synchronize and free the interrupt handlers. Unlock to avoid deadlocks. */ orig_dev = dev; mutex_unlock(&wl->mutex); - synchronize_irq(dev->dev->irq); + if (dev->dev->bus->bustype == SSB_BUSTYPE_SDIO) { + b43_sdio_free_irq(dev); + } else { + synchronize_irq(dev->dev->irq); + free_irq(dev->dev->irq, dev); + } mutex_lock(&wl->mutex); dev = wl->current_dev; if (!dev) @@ -3843,14 +3921,15 @@ redo: goto redo; return dev; } - B43_WARN_ON(b43_read32(dev, B43_MMIO_GEN_IRQ_MASK)); + mask = b43_read32(dev, B43_MMIO_GEN_IRQ_MASK); + B43_WARN_ON(mask != 0xFFFFFFFF && mask); /* Drain the TX queue */ while (skb_queue_len(&wl->tx_queue)) dev_kfree_skb(skb_dequeue(&wl->tx_queue)); b43_mac_suspend(dev); - free_irq(dev->dev->irq, dev); + b43_leds_exit(dev); b43dbg(wl, "Wireless interface stopped\n"); return dev; @@ -3864,12 +3943,20 @@ static int b43_wireless_core_start(struct b43_wldev *dev) B43_WARN_ON(b43_status(dev) != B43_STAT_INITIALIZED); drain_txstatus_queue(dev); - err = request_threaded_irq(dev->dev->irq, b43_interrupt_handler, - b43_interrupt_thread_handler, - IRQF_SHARED, KBUILD_MODNAME, dev); - if (err) { - b43err(dev->wl, "Cannot request IRQ-%d\n", dev->dev->irq); - goto out; + if (dev->dev->bus->bustype == SSB_BUSTYPE_SDIO) { + err = b43_sdio_request_irq(dev, b43_sdio_interrupt_handler); + if (err) { + b43err(dev->wl, "Cannot request SDIO IRQ\n"); + goto out; + } + } else { + err = request_threaded_irq(dev->dev->irq, b43_interrupt_handler, + b43_interrupt_thread_handler, + IRQF_SHARED, KBUILD_MODNAME, dev); + if (err) { + b43err(dev->wl, "Cannot request IRQ-%d\n", dev->dev->irq); + goto out; + } } /* We are ready to run. */ @@ -3882,8 +3969,10 @@ static int b43_wireless_core_start(struct b43_wldev *dev) /* Start maintainance work */ b43_periodic_tasks_setup(dev); + b43_leds_init(dev); + b43dbg(dev->wl, "Wireless interface started\n"); - out: +out: return err; } @@ -4160,10 +4249,6 @@ static void b43_wireless_core_exit(struct b43_wldev *dev) macctl |= B43_MACCTL_PSM_JMP0; b43_write32(dev, B43_MMIO_MACCTL, macctl); - if (!dev->suspend_in_progress) { - b43_leds_exit(dev); - b43_rng_exit(dev->wl); - } b43_dma_free(dev); b43_pio_free(dev); b43_chip_exit(dev); @@ -4180,7 +4265,6 @@ static void b43_wireless_core_exit(struct b43_wldev *dev) /* Initialize a wireless core */ static int b43_wireless_core_init(struct b43_wldev *dev) { - struct b43_wl *wl = dev->wl; struct ssb_bus *bus = dev->dev->bus; struct ssb_sprom *sprom = &bus->sprom; struct b43_phy *phy = &dev->phy; @@ -4264,7 +4348,9 @@ static int b43_wireless_core_init(struct b43_wldev *dev) /* Maximum Contention Window */ b43_shm_write16(dev, B43_SHM_SCRATCH, B43_SHM_SC_MAXCONT, 0x3FF); - if ((dev->dev->bus->bustype == SSB_BUSTYPE_PCMCIA) || B43_FORCE_PIO) { + if ((dev->dev->bus->bustype == SSB_BUSTYPE_PCMCIA) || + (dev->dev->bus->bustype == SSB_BUSTYPE_SDIO) || + B43_FORCE_PIO) { dev->__using_pio_transfers = 1; err = b43_pio_init(dev); } else { @@ -4280,15 +4366,13 @@ static int b43_wireless_core_init(struct b43_wldev *dev) ssb_bus_powerup(bus, !(sprom->boardflags_lo & B43_BFL_XTAL_NOSLOW)); b43_upload_card_macaddress(dev); b43_security_init(dev); - if (!dev->suspend_in_progress) - b43_rng_init(wl); + + ieee80211_wake_queues(dev->wl->hw); ieee80211_wake_queues(dev->wl->hw); b43_set_status(dev, B43_STAT_INITIALIZED); - if (!dev->suspend_in_progress) - b43_leds_init(dev); out: return err; @@ -4437,9 +4521,8 @@ static int b43_op_beacon_set_tim(struct ieee80211_hw *hw, { struct b43_wl *wl = hw_to_b43_wl(hw); - mutex_lock(&wl->mutex); + /* FIXME: add locking */ b43_update_templates(wl); - mutex_unlock(&wl->mutex); return 0; } @@ -4837,7 +4920,6 @@ static int b43_wireless_init(struct ssb_device *dev) /* Initialize struct b43_wl */ wl->hw = hw; - spin_lock_init(&wl->leds_lock); mutex_init(&wl->mutex); spin_lock_init(&wl->hardirq_lock); INIT_LIST_HEAD(&wl->devlist); @@ -4878,6 +4960,8 @@ static int b43_probe(struct ssb_device *dev, const struct ssb_device_id *id) err = ieee80211_register_hw(wl->hw); if (err) goto err_one_core_detach; + b43_leds_register(wl->current_dev); + b43_rng_init(wl); } out: @@ -4906,12 +4990,15 @@ static void b43_remove(struct ssb_device *dev) * might have modified it. Restoring is important, so the networking * stack can properly free resources. */ wl->hw->queues = wl->mac80211_initially_registered_queues; + b43_leds_stop(wldev); ieee80211_unregister_hw(wl->hw); } b43_one_core_detach(dev); if (list_empty(&wl->devlist)) { + b43_rng_exit(wl); + b43_leds_unregister(wl); /* Last core on the chip unregistered. * We can destroy common struct b43_wl. */ @@ -4929,80 +5016,17 @@ void b43_controller_restart(struct b43_wldev *dev, const char *reason) ieee80211_queue_work(dev->wl->hw, &dev->restart_work); } -#ifdef CONFIG_PM - -static int b43_suspend(struct ssb_device *dev, pm_message_t state) -{ - struct b43_wldev *wldev = ssb_get_drvdata(dev); - struct b43_wl *wl = wldev->wl; - - b43dbg(wl, "Suspending...\n"); - - mutex_lock(&wl->mutex); - wldev->suspend_in_progress = true; - wldev->suspend_init_status = b43_status(wldev); - if (wldev->suspend_init_status >= B43_STAT_STARTED) - wldev = b43_wireless_core_stop(wldev); - if (wldev && wldev->suspend_init_status >= B43_STAT_INITIALIZED) - b43_wireless_core_exit(wldev); - mutex_unlock(&wl->mutex); - - b43dbg(wl, "Device suspended.\n"); - - return 0; -} - -static int b43_resume(struct ssb_device *dev) -{ - struct b43_wldev *wldev = ssb_get_drvdata(dev); - struct b43_wl *wl = wldev->wl; - int err = 0; - - b43dbg(wl, "Resuming...\n"); - - mutex_lock(&wl->mutex); - if (wldev->suspend_init_status >= B43_STAT_INITIALIZED) { - err = b43_wireless_core_init(wldev); - if (err) { - b43err(wl, "Resume failed at core init\n"); - goto out; - } - } - if (wldev->suspend_init_status >= B43_STAT_STARTED) { - err = b43_wireless_core_start(wldev); - if (err) { - b43_leds_exit(wldev); - b43_rng_exit(wldev->wl); - b43_wireless_core_exit(wldev); - b43err(wl, "Resume failed at core start\n"); - goto out; - } - } - b43dbg(wl, "Device resumed.\n"); - out: - wldev->suspend_in_progress = false; - mutex_unlock(&wl->mutex); - return err; -} - -#else /* CONFIG_PM */ -# define b43_suspend NULL -# define b43_resume NULL -#endif /* CONFIG_PM */ - static struct ssb_driver b43_ssb_driver = { .name = KBUILD_MODNAME, .id_table = b43_ssb_tbl, .probe = b43_probe, .remove = b43_remove, - .suspend = b43_suspend, - .resume = b43_resume, }; static void b43_print_driverinfo(void) { const char *feat_pci = "", *feat_pcmcia = "", *feat_nphy = "", - *feat_leds = ""; + *feat_leds = "", *feat_sdio = ""; #ifdef CONFIG_B43_PCI_AUTOSELECT feat_pci = "P"; @@ -5016,11 +5040,14 @@ static void b43_print_driverinfo(void) #ifdef CONFIG_B43_LEDS feat_leds = "L"; #endif +#ifdef CONFIG_B43_SDIO + feat_sdio = "S"; +#endif printk(KERN_INFO "Broadcom 43xx driver loaded " - "[ Features: %s%s%s%s, Firmware-ID: " + "[ Features: %s%s%s%s%s, Firmware-ID: " B43_SUPPORTED_FIRMWARE_ID " ]\n", feat_pci, feat_pcmcia, feat_nphy, - feat_leds); + feat_leds, feat_sdio); } static int __init b43_init(void) @@ -5031,13 +5058,18 @@ static int __init b43_init(void) err = b43_pcmcia_init(); if (err) goto err_dfs_exit; - err = ssb_driver_register(&b43_ssb_driver); + err = b43_sdio_init(); if (err) goto err_pcmcia_exit; + err = ssb_driver_register(&b43_ssb_driver); + if (err) + goto err_sdio_exit; b43_print_driverinfo(); return err; +err_sdio_exit: + b43_sdio_exit(); err_pcmcia_exit: b43_pcmcia_exit(); err_dfs_exit: @@ -5048,6 +5080,7 @@ err_dfs_exit: static void __exit b43_exit(void) { ssb_driver_unregister(&b43_ssb_driver); + b43_sdio_exit(); b43_pcmcia_exit(); b43_debugfs_exit(); } diff --git a/drivers/net/wireless/b43/phy_lp.c b/drivers/net/wireless/b43/phy_lp.c index 3e02d969f683..1e318d815a5b 100644 --- a/drivers/net/wireless/b43/phy_lp.c +++ b/drivers/net/wireless/b43/phy_lp.c @@ -2228,6 +2228,16 @@ static enum b43_txpwr_result b43_lpphy_op_recalc_txpower(struct b43_wldev *dev, return B43_TXPWR_RES_DONE; } +void b43_lpphy_op_switch_analog(struct b43_wldev *dev, bool on) +{ + if (on) { + b43_phy_mask(dev, B43_LPPHY_AFE_CTL_OVR, 0xfff8); + } else { + b43_phy_set(dev, B43_LPPHY_AFE_CTL_OVRVAL, 0x0007); + b43_phy_set(dev, B43_LPPHY_AFE_CTL_OVR, 0x0007); + } +} + const struct b43_phy_operations b43_phyops_lp = { .allocate = b43_lpphy_op_allocate, .free = b43_lpphy_op_free, @@ -2239,7 +2249,7 @@ const struct b43_phy_operations b43_phyops_lp = { .radio_read = b43_lpphy_op_radio_read, .radio_write = b43_lpphy_op_radio_write, .software_rfkill = b43_lpphy_op_software_rfkill, - .switch_analog = b43_phyop_switch_analog_generic, + .switch_analog = b43_lpphy_op_switch_analog, .switch_channel = b43_lpphy_op_switch_channel, .get_default_chan = b43_lpphy_op_get_default_chan, .set_rx_antenna = b43_lpphy_op_set_rx_antenna, diff --git a/drivers/net/wireless/b43/pio.c b/drivers/net/wireless/b43/pio.c index 3498b68385e7..9b9044400218 100644 --- a/drivers/net/wireless/b43/pio.c +++ b/drivers/net/wireless/b43/pio.c @@ -30,6 +30,7 @@ #include "xmit.h" #include <linux/delay.h> +#include <linux/sched.h> static u16 generate_cookie(struct b43_pio_txqueue *q, @@ -331,6 +332,7 @@ static u16 tx_write_2byte_queue(struct b43_pio_txqueue *q, unsigned int data_len) { struct b43_wldev *dev = q->dev; + struct b43_wl *wl = dev->wl; const u8 *data = _data; ctl |= B43_PIO_TXCTL_WRITELO | B43_PIO_TXCTL_WRITEHI; @@ -343,7 +345,11 @@ static u16 tx_write_2byte_queue(struct b43_pio_txqueue *q, /* Write the last byte. */ ctl &= ~B43_PIO_TXCTL_WRITEHI; b43_piotx_write16(q, B43_PIO_TXCTL, ctl); - b43_piotx_write16(q, B43_PIO_TXDATA, data[data_len - 1]); + wl->tx_tail[0] = data[data_len - 1]; + wl->tx_tail[1] = 0; + ssb_block_write(dev->dev, wl->tx_tail, 2, + q->mmio_base + B43_PIO_TXDATA, + sizeof(u16)); } return ctl; @@ -376,6 +382,7 @@ static u32 tx_write_4byte_queue(struct b43_pio_txqueue *q, unsigned int data_len) { struct b43_wldev *dev = q->dev; + struct b43_wl *wl = dev->wl; const u8 *data = _data; ctl |= B43_PIO8_TXCTL_0_7 | B43_PIO8_TXCTL_8_15 | @@ -386,26 +393,33 @@ static u32 tx_write_4byte_queue(struct b43_pio_txqueue *q, q->mmio_base + B43_PIO8_TXDATA, sizeof(u32)); if (data_len & 3) { - u32 value = 0; - + wl->tx_tail[3] = 0; /* Write the last few bytes. */ ctl &= ~(B43_PIO8_TXCTL_8_15 | B43_PIO8_TXCTL_16_23 | B43_PIO8_TXCTL_24_31); - data = &(data[data_len - 1]); switch (data_len & 3) { case 3: - ctl |= B43_PIO8_TXCTL_16_23; - value |= (u32)(*data) << 16; - data--; + ctl |= B43_PIO8_TXCTL_16_23 | B43_PIO8_TXCTL_8_15; + wl->tx_tail[0] = data[data_len - 3]; + wl->tx_tail[1] = data[data_len - 2]; + wl->tx_tail[2] = data[data_len - 1]; + break; case 2: ctl |= B43_PIO8_TXCTL_8_15; - value |= (u32)(*data) << 8; - data--; + wl->tx_tail[0] = data[data_len - 2]; + wl->tx_tail[1] = data[data_len - 1]; + wl->tx_tail[2] = 0; + break; case 1: - value |= (u32)(*data); + wl->tx_tail[0] = data[data_len - 1]; + wl->tx_tail[1] = 0; + wl->tx_tail[2] = 0; + break; } b43_piotx_write32(q, B43_PIO8_TXCTL, ctl); - b43_piotx_write32(q, B43_PIO8_TXDATA, value); + ssb_block_write(dev->dev, wl->tx_tail, 4, + q->mmio_base + B43_PIO8_TXDATA, + sizeof(u32)); } return ctl; @@ -435,8 +449,9 @@ static void pio_tx_frame_4byte_queue(struct b43_pio_txpacket *pack, static int pio_tx_frame(struct b43_pio_txqueue *q, struct sk_buff *skb) { + struct b43_wldev *dev = q->dev; + struct b43_wl *wl = dev->wl; struct b43_pio_txpacket *pack; - struct b43_txhdr txhdr; u16 cookie; int err; unsigned int hdrlen; @@ -447,8 +462,8 @@ static int pio_tx_frame(struct b43_pio_txqueue *q, struct b43_pio_txpacket, list); cookie = generate_cookie(q, pack); - hdrlen = b43_txhdr_size(q->dev); - err = b43_generate_txhdr(q->dev, (u8 *)&txhdr, skb, + hdrlen = b43_txhdr_size(dev); + err = b43_generate_txhdr(dev, (u8 *)&wl->txhdr, skb, info, cookie); if (err) return err; @@ -456,15 +471,15 @@ static int pio_tx_frame(struct b43_pio_txqueue *q, if (info->flags & IEEE80211_TX_CTL_SEND_AFTER_DTIM) { /* Tell the firmware about the cookie of the last * mcast frame, so it can clear the more-data bit in it. */ - b43_shm_write16(q->dev, B43_SHM_SHARED, + b43_shm_write16(dev, B43_SHM_SHARED, B43_SHM_SH_MCASTCOOKIE, cookie); } pack->skb = skb; if (q->rev >= 8) - pio_tx_frame_4byte_queue(pack, (const u8 *)&txhdr, hdrlen); + pio_tx_frame_4byte_queue(pack, (const u8 *)&wl->txhdr, hdrlen); else - pio_tx_frame_2byte_queue(pack, (const u8 *)&txhdr, hdrlen); + pio_tx_frame_2byte_queue(pack, (const u8 *)&wl->txhdr, hdrlen); /* Remove it from the list of available packet slots. * It will be put back when we receive the status report. */ @@ -574,7 +589,7 @@ void b43_pio_handle_txstatus(struct b43_wldev *dev, q->buffer_used -= total_len; q->free_packet_slots += 1; - ieee80211_tx_status_irqsafe(dev->wl->hw, pack->skb); + ieee80211_tx_status(dev->wl->hw, pack->skb); pack->skb = NULL; list_add(&pack->list, &q->packets_list); @@ -604,14 +619,14 @@ void b43_pio_get_tx_stats(struct b43_wldev *dev, static bool pio_rx_frame(struct b43_pio_rxqueue *q) { struct b43_wldev *dev = q->dev; - struct b43_rxhdr_fw4 rxhdr; + struct b43_wl *wl = dev->wl; u16 len; u32 macstat; unsigned int i, padding; struct sk_buff *skb; const char *err_msg = NULL; - memset(&rxhdr, 0, sizeof(rxhdr)); + memset(&wl->rxhdr, 0, sizeof(wl->rxhdr)); /* Check if we have data and wait for it to get ready. */ if (q->rev >= 8) { @@ -649,16 +664,16 @@ data_ready: /* Get the preamble (RX header) */ if (q->rev >= 8) { - ssb_block_read(dev->dev, &rxhdr, sizeof(rxhdr), + ssb_block_read(dev->dev, &wl->rxhdr, sizeof(wl->rxhdr), q->mmio_base + B43_PIO8_RXDATA, sizeof(u32)); } else { - ssb_block_read(dev->dev, &rxhdr, sizeof(rxhdr), + ssb_block_read(dev->dev, &wl->rxhdr, sizeof(wl->rxhdr), q->mmio_base + B43_PIO_RXDATA, sizeof(u16)); } /* Sanity checks. */ - len = le16_to_cpu(rxhdr.frame_len); + len = le16_to_cpu(wl->rxhdr.frame_len); if (unlikely(len > 0x700)) { err_msg = "len > 0x700"; goto rx_error; @@ -668,7 +683,7 @@ data_ready: goto rx_error; } - macstat = le32_to_cpu(rxhdr.mac_status); + macstat = le32_to_cpu(wl->rxhdr.mac_status); if (macstat & B43_RX_MAC_FCSERR) { if (!(q->dev->wl->filter_flags & FIF_FCSFAIL)) { /* Drop frames with failed FCS. */ @@ -693,21 +708,23 @@ data_ready: q->mmio_base + B43_PIO8_RXDATA, sizeof(u32)); if (len & 3) { - u32 value; - char *data; - /* Read the last few bytes. */ - value = b43_piorx_read32(q, B43_PIO8_RXDATA); - data = &(skb->data[len + padding - 1]); + ssb_block_read(dev->dev, wl->rx_tail, 4, + q->mmio_base + B43_PIO8_RXDATA, + sizeof(u32)); switch (len & 3) { case 3: - *data = (value >> 16); - data--; + skb->data[len + padding - 3] = wl->rx_tail[0]; + skb->data[len + padding - 2] = wl->rx_tail[1]; + skb->data[len + padding - 1] = wl->rx_tail[2]; + break; case 2: - *data = (value >> 8); - data--; + skb->data[len + padding - 2] = wl->rx_tail[0]; + skb->data[len + padding - 1] = wl->rx_tail[1]; + break; case 1: - *data = value; + skb->data[len + padding - 1] = wl->rx_tail[0]; + break; } } } else { @@ -715,15 +732,15 @@ data_ready: q->mmio_base + B43_PIO_RXDATA, sizeof(u16)); if (len & 1) { - u16 value; - /* Read the last byte. */ - value = b43_piorx_read16(q, B43_PIO_RXDATA); - skb->data[len + padding - 1] = value; + ssb_block_read(dev->dev, wl->rx_tail, 2, + q->mmio_base + B43_PIO_RXDATA, + sizeof(u16)); + skb->data[len + padding - 1] = wl->rx_tail[0]; } } - b43_rx(q->dev, skb, &rxhdr); + b43_rx(q->dev, skb, &wl->rxhdr); return 1; diff --git a/drivers/net/wireless/b43/rfkill.c b/drivers/net/wireless/b43/rfkill.c index 31e55999893f..ffdce6f3c909 100644 --- a/drivers/net/wireless/b43/rfkill.c +++ b/drivers/net/wireless/b43/rfkill.c @@ -28,12 +28,13 @@ /* Returns TRUE, if the radio is enabled in hardware. */ bool b43_is_hw_radio_enabled(struct b43_wldev *dev) { - if (dev->phy.rev >= 3) { + if (dev->phy.rev >= 3 || dev->phy.type == B43_PHYTYPE_LP) { if (!(b43_read32(dev, B43_MMIO_RADIO_HWENABLED_HI) & B43_MMIO_RADIO_HWENABLED_HI_MASK)) return 1; } else { - if (b43_read16(dev, B43_MMIO_RADIO_HWENABLED_LO) + if (b43_status(dev) >= B43_STAT_STARTED && + b43_read16(dev, B43_MMIO_RADIO_HWENABLED_LO) & B43_MMIO_RADIO_HWENABLED_LO_MASK) return 1; } diff --git a/drivers/net/wireless/b43/sdio.c b/drivers/net/wireless/b43/sdio.c new file mode 100644 index 000000000000..0d3ac64147a5 --- /dev/null +++ b/drivers/net/wireless/b43/sdio.c @@ -0,0 +1,202 @@ +/* + * Broadcom B43 wireless driver + * + * SDIO over Sonics Silicon Backplane bus glue for b43. + * + * Copyright (C) 2009 Albert Herranz + * Copyright (C) 2009 Michael Buesch <mb@bu3sch.de> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + */ + +#include <linux/kernel.h> +#include <linux/mmc/card.h> +#include <linux/mmc/sdio_func.h> +#include <linux/mmc/sdio_ids.h> +#include <linux/ssb/ssb.h> + +#include "sdio.h" +#include "b43.h" + + +#define HNBU_CHIPID 0x01 /* vendor & device id */ + +#define B43_SDIO_BLOCK_SIZE 64 /* rx fifo max size in bytes */ + + +static const struct b43_sdio_quirk { + u16 vendor; + u16 device; + unsigned int quirks; +} b43_sdio_quirks[] = { + { 0x14E4, 0x4318, SSB_QUIRK_SDIO_READ_AFTER_WRITE32, }, + { }, +}; + + +static unsigned int b43_sdio_get_quirks(u16 vendor, u16 device) +{ + const struct b43_sdio_quirk *q; + + for (q = b43_sdio_quirks; q->quirks; q++) { + if (vendor == q->vendor && device == q->device) + return q->quirks; + } + + return 0; +} + +static void b43_sdio_interrupt_dispatcher(struct sdio_func *func) +{ + struct b43_sdio *sdio = sdio_get_drvdata(func); + struct b43_wldev *dev = sdio->irq_handler_opaque; + + if (unlikely(b43_status(dev) < B43_STAT_STARTED)) + return; + + sdio_release_host(func); + sdio->irq_handler(dev); + sdio_claim_host(func); +} + +int b43_sdio_request_irq(struct b43_wldev *dev, + void (*handler)(struct b43_wldev *dev)) +{ + struct ssb_bus *bus = dev->dev->bus; + struct sdio_func *func = bus->host_sdio; + struct b43_sdio *sdio = sdio_get_drvdata(func); + int err; + + sdio->irq_handler_opaque = dev; + sdio->irq_handler = handler; + sdio_claim_host(func); + err = sdio_claim_irq(func, b43_sdio_interrupt_dispatcher); + sdio_release_host(func); + + return err; +} + +void b43_sdio_free_irq(struct b43_wldev *dev) +{ + struct ssb_bus *bus = dev->dev->bus; + struct sdio_func *func = bus->host_sdio; + struct b43_sdio *sdio = sdio_get_drvdata(func); + + sdio_claim_host(func); + sdio_release_irq(func); + sdio_release_host(func); + sdio->irq_handler_opaque = NULL; + sdio->irq_handler = NULL; +} + +static int b43_sdio_probe(struct sdio_func *func, + const struct sdio_device_id *id) +{ + struct b43_sdio *sdio; + struct sdio_func_tuple *tuple; + u16 vendor = 0, device = 0; + int error; + + /* Look for the card chip identifier. */ + tuple = func->tuples; + while (tuple) { + switch (tuple->code) { + case 0x80: + switch (tuple->data[0]) { + case HNBU_CHIPID: + if (tuple->size != 5) + break; + vendor = tuple->data[1] | (tuple->data[2]<<8); + device = tuple->data[3] | (tuple->data[4]<<8); + dev_info(&func->dev, "Chip ID %04x:%04x\n", + vendor, device); + break; + default: + break; + } + break; + default: + break; + } + tuple = tuple->next; + } + if (!vendor || !device) { + error = -ENODEV; + goto out; + } + + sdio_claim_host(func); + error = sdio_set_block_size(func, B43_SDIO_BLOCK_SIZE); + if (error) { + dev_err(&func->dev, "failed to set block size to %u bytes," + " error %d\n", B43_SDIO_BLOCK_SIZE, error); + goto err_release_host; + } + error = sdio_enable_func(func); + if (error) { + dev_err(&func->dev, "failed to enable func, error %d\n", error); + goto err_release_host; + } + sdio_release_host(func); + + sdio = kzalloc(sizeof(*sdio), GFP_KERNEL); + if (!sdio) { + error = -ENOMEM; + dev_err(&func->dev, "failed to allocate ssb bus\n"); + goto err_disable_func; + } + error = ssb_bus_sdiobus_register(&sdio->ssb, func, + b43_sdio_get_quirks(vendor, device)); + if (error) { + dev_err(&func->dev, "failed to register ssb sdio bus," + " error %d\n", error); + goto err_free_ssb; + } + sdio_set_drvdata(func, sdio); + + return 0; + +err_free_ssb: + kfree(sdio); +err_disable_func: + sdio_disable_func(func); +err_release_host: + sdio_release_host(func); +out: + return error; +} + +static void b43_sdio_remove(struct sdio_func *func) +{ + struct b43_sdio *sdio = sdio_get_drvdata(func); + + ssb_bus_unregister(&sdio->ssb); + sdio_disable_func(func); + kfree(sdio); + sdio_set_drvdata(func, NULL); +} + +static const struct sdio_device_id b43_sdio_ids[] = { + { SDIO_DEVICE(0x02d0, 0x044b) }, /* Nintendo Wii WLAN daughter card */ + { }, +}; + +static struct sdio_driver b43_sdio_driver = { + .name = "b43-sdio", + .id_table = b43_sdio_ids, + .probe = b43_sdio_probe, + .remove = b43_sdio_remove, +}; + +int b43_sdio_init(void) +{ + return sdio_register_driver(&b43_sdio_driver); +} + +void b43_sdio_exit(void) +{ + sdio_unregister_driver(&b43_sdio_driver); +} diff --git a/drivers/net/wireless/b43/sdio.h b/drivers/net/wireless/b43/sdio.h new file mode 100644 index 000000000000..fb633094403a --- /dev/null +++ b/drivers/net/wireless/b43/sdio.h @@ -0,0 +1,45 @@ +#ifndef B43_SDIO_H_ +#define B43_SDIO_H_ + +#include <linux/ssb/ssb.h> + +struct b43_wldev; + + +#ifdef CONFIG_B43_SDIO + +struct b43_sdio { + struct ssb_bus ssb; + void *irq_handler_opaque; + void (*irq_handler)(struct b43_wldev *dev); +}; + +int b43_sdio_request_irq(struct b43_wldev *dev, + void (*handler)(struct b43_wldev *dev)); +void b43_sdio_free_irq(struct b43_wldev *dev); + +int b43_sdio_init(void); +void b43_sdio_exit(void); + + +#else /* CONFIG_B43_SDIO */ + + +int b43_sdio_request_irq(struct b43_wldev *dev, + void (*handler)(struct b43_wldev *dev)) +{ + return -ENODEV; +} +void b43_sdio_free_irq(struct b43_wldev *dev) +{ +} +static inline int b43_sdio_init(void) +{ + return 0; +} +static inline void b43_sdio_exit(void) +{ +} + +#endif /* CONFIG_B43_SDIO */ +#endif /* B43_SDIO_H_ */ diff --git a/drivers/net/wireless/b43/xmit.c b/drivers/net/wireless/b43/xmit.c index 14f541248b5c..f4e9695ec186 100644 --- a/drivers/net/wireless/b43/xmit.c +++ b/drivers/net/wireless/b43/xmit.c @@ -27,7 +27,7 @@ */ -#include "xmit.h" +#include "b43.h" #include "phy_common.h" #include "dma.h" #include "pio.h" @@ -690,8 +690,14 @@ void b43_rx(struct b43_wldev *dev, struct sk_buff *skb, const void *_rxhdr) } memcpy(IEEE80211_SKB_RXCB(skb), &status, sizeof(status)); - ieee80211_rx_irqsafe(dev->wl->hw, skb); + local_bh_disable(); + ieee80211_rx(dev->wl->hw, skb); + local_bh_enable(); + +#if B43_DEBUG + dev->rx_count++; +#endif return; drop: b43dbg(dev->wl, "RX: Packet dropped\n"); diff --git a/drivers/net/wireless/b43legacy/main.c b/drivers/net/wireless/b43legacy/main.c index 1d9223b3d4c4..4b60148a5e61 100644 --- a/drivers/net/wireless/b43legacy/main.c +++ b/drivers/net/wireless/b43legacy/main.c @@ -37,6 +37,7 @@ #include <linux/firmware.h> #include <linux/wireless.h> #include <linux/workqueue.h> +#include <linux/sched.h> #include <linux/skbuff.h> #include <linux/dma-mapping.h> #include <net/dst.h> diff --git a/drivers/net/wireless/b43legacy/phy.c b/drivers/net/wireless/b43legacy/phy.c index 11319ec2d64a..aaf227203a98 100644 --- a/drivers/net/wireless/b43legacy/phy.c +++ b/drivers/net/wireless/b43legacy/phy.c @@ -31,6 +31,7 @@ #include <linux/delay.h> #include <linux/pci.h> +#include <linux/sched.h> #include <linux/types.h> #include "b43legacy.h" diff --git a/drivers/net/wireless/hostap/hostap_info.c b/drivers/net/wireless/hostap/hostap_info.c index 6fa14a4e4b53..4dfb40a84c96 100644 --- a/drivers/net/wireless/hostap/hostap_info.c +++ b/drivers/net/wireless/hostap/hostap_info.c @@ -1,6 +1,7 @@ /* Host AP driver Info Frame processing (part of hostap.o module) */ #include <linux/if_arp.h> +#include <linux/sched.h> #include "hostap_wlan.h" #include "hostap.h" #include "hostap_ap.h" diff --git a/drivers/net/wireless/hostap/hostap_ioctl.c b/drivers/net/wireless/hostap/hostap_ioctl.c index 3f2bda881a4f..9419cebca8a5 100644 --- a/drivers/net/wireless/hostap/hostap_ioctl.c +++ b/drivers/net/wireless/hostap/hostap_ioctl.c @@ -1,6 +1,7 @@ /* ioctl() (mostly Linux Wireless Extensions) routines for Host AP driver */ #include <linux/types.h> +#include <linux/sched.h> #include <linux/ethtool.h> #include <linux/if_arp.h> #include <net/lib80211.h> diff --git a/drivers/net/wireless/ipw2x00/ipw2100.c b/drivers/net/wireless/ipw2x00/ipw2100.c index 240cff1e6979..6e2fc0cb6f8a 100644 --- a/drivers/net/wireless/ipw2x00/ipw2100.c +++ b/drivers/net/wireless/ipw2x00/ipw2100.c @@ -6029,7 +6029,7 @@ static struct net_device *ipw2100_alloc_device(struct pci_dev *pci_dev, struct ipw2100_priv *priv; struct net_device *dev; - dev = alloc_ieee80211(sizeof(struct ipw2100_priv), 0); + dev = alloc_ieee80211(sizeof(struct ipw2100_priv)); if (!dev) return NULL; priv = libipw_priv(dev); @@ -6342,7 +6342,7 @@ static int ipw2100_pci_init_one(struct pci_dev *pci_dev, sysfs_remove_group(&pci_dev->dev.kobj, &ipw2100_attribute_group); - free_ieee80211(dev, 0); + free_ieee80211(dev); pci_set_drvdata(pci_dev, NULL); } @@ -6400,7 +6400,7 @@ static void __devexit ipw2100_pci_remove_one(struct pci_dev *pci_dev) if (dev->base_addr) iounmap((void __iomem *)dev->base_addr); - free_ieee80211(dev, 0); + free_ieee80211(dev); } pci_release_regions(pci_dev); diff --git a/drivers/net/wireless/ipw2x00/ipw2200.c b/drivers/net/wireless/ipw2x00/ipw2200.c index 8d58e6ed4e7d..a6ca536e44f8 100644 --- a/drivers/net/wireless/ipw2x00/ipw2200.c +++ b/drivers/net/wireless/ipw2x00/ipw2200.c @@ -30,6 +30,7 @@ ******************************************************************************/ +#include <linux/sched.h> #include "ipw2200.h" @@ -103,25 +104,6 @@ static int antenna = CFG_SYS_ANTENNA_BOTH; static int rtap_iface = 0; /* def: 0 -- do not create rtap interface */ #endif -static struct ieee80211_rate ipw2200_rates[] = { - { .bitrate = 10 }, - { .bitrate = 20, .flags = IEEE80211_RATE_SHORT_PREAMBLE }, - { .bitrate = 55, .flags = IEEE80211_RATE_SHORT_PREAMBLE }, - { .bitrate = 110, .flags = IEEE80211_RATE_SHORT_PREAMBLE }, - { .bitrate = 60 }, - { .bitrate = 90 }, - { .bitrate = 120 }, - { .bitrate = 180 }, - { .bitrate = 240 }, - { .bitrate = 360 }, - { .bitrate = 480 }, - { .bitrate = 540 } -}; - -#define ipw2200_a_rates (ipw2200_rates + 4) -#define ipw2200_num_a_rates 8 -#define ipw2200_bg_rates (ipw2200_rates + 0) -#define ipw2200_num_bg_rates 12 #ifdef CONFIG_IPW2200_QOS static int qos_enable = 0; @@ -8673,6 +8655,24 @@ static int ipw_sw_reset(struct ipw_priv *priv, int option) * */ +static int ipw_wx_get_name(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct ipw_priv *priv = libipw_priv(dev); + mutex_lock(&priv->mutex); + if (priv->status & STATUS_RF_KILL_MASK) + strcpy(wrqu->name, "radio off"); + else if (!(priv->status & STATUS_ASSOCIATED)) + strcpy(wrqu->name, "unassociated"); + else + snprintf(wrqu->name, IFNAMSIZ, "IEEE 802.11%c", + ipw_modes[priv->assoc_request.ieee_mode]); + IPW_DEBUG_WX("Name: %s\n", wrqu->name); + mutex_unlock(&priv->mutex); + return 0; +} + static int ipw_set_channel(struct ipw_priv *priv, u8 channel) { if (channel == 0) { @@ -9972,7 +9972,7 @@ static int ipw_wx_sw_reset(struct net_device *dev, /* Rebase the WE IOCTLs to zero for the handler array */ #define IW_IOCTL(x) [(x)-SIOCSIWCOMMIT] static iw_handler ipw_wx_handlers[] = { - IW_IOCTL(SIOCGIWNAME) = (iw_handler) cfg80211_wext_giwname, + IW_IOCTL(SIOCGIWNAME) = ipw_wx_get_name, IW_IOCTL(SIOCSIWFREQ) = ipw_wx_set_freq, IW_IOCTL(SIOCGIWFREQ) = ipw_wx_get_freq, IW_IOCTL(SIOCSIWMODE) = ipw_wx_set_mode, @@ -11416,100 +11416,16 @@ static void ipw_bg_down(struct work_struct *work) /* Called by register_netdev() */ static int ipw_net_init(struct net_device *dev) { - int i, rc = 0; struct ipw_priv *priv = libipw_priv(dev); - const struct libipw_geo *geo = libipw_get_geo(priv->ieee); - struct wireless_dev *wdev = &priv->ieee->wdev; mutex_lock(&priv->mutex); if (ipw_up(priv)) { - rc = -EIO; - goto out; - } - - memcpy(wdev->wiphy->perm_addr, priv->mac_addr, ETH_ALEN); - - /* fill-out priv->ieee->bg_band */ - if (geo->bg_channels) { - struct ieee80211_supported_band *bg_band = &priv->ieee->bg_band; - - bg_band->band = IEEE80211_BAND_2GHZ; - bg_band->n_channels = geo->bg_channels; - bg_band->channels = - kzalloc(geo->bg_channels * - sizeof(struct ieee80211_channel), GFP_KERNEL); - /* translate geo->bg to bg_band.channels */ - for (i = 0; i < geo->bg_channels; i++) { - bg_band->channels[i].band = IEEE80211_BAND_2GHZ; - bg_band->channels[i].center_freq = geo->bg[i].freq; - bg_band->channels[i].hw_value = geo->bg[i].channel; - bg_band->channels[i].max_power = geo->bg[i].max_power; - if (geo->bg[i].flags & LIBIPW_CH_PASSIVE_ONLY) - bg_band->channels[i].flags |= - IEEE80211_CHAN_PASSIVE_SCAN; - if (geo->bg[i].flags & LIBIPW_CH_NO_IBSS) - bg_band->channels[i].flags |= - IEEE80211_CHAN_NO_IBSS; - if (geo->bg[i].flags & LIBIPW_CH_RADAR_DETECT) - bg_band->channels[i].flags |= - IEEE80211_CHAN_RADAR; - /* No equivalent for LIBIPW_CH_80211H_RULES, - LIBIPW_CH_UNIFORM_SPREADING, or - LIBIPW_CH_B_ONLY... */ - } - /* point at bitrate info */ - bg_band->bitrates = ipw2200_bg_rates; - bg_band->n_bitrates = ipw2200_num_bg_rates; - - wdev->wiphy->bands[IEEE80211_BAND_2GHZ] = bg_band; - } - - /* fill-out priv->ieee->a_band */ - if (geo->a_channels) { - struct ieee80211_supported_band *a_band = &priv->ieee->a_band; - - a_band->band = IEEE80211_BAND_5GHZ; - a_band->n_channels = geo->a_channels; - a_band->channels = - kzalloc(geo->a_channels * - sizeof(struct ieee80211_channel), GFP_KERNEL); - /* translate geo->bg to a_band.channels */ - for (i = 0; i < geo->a_channels; i++) { - a_band->channels[i].band = IEEE80211_BAND_2GHZ; - a_band->channels[i].center_freq = geo->a[i].freq; - a_band->channels[i].hw_value = geo->a[i].channel; - a_band->channels[i].max_power = geo->a[i].max_power; - if (geo->a[i].flags & LIBIPW_CH_PASSIVE_ONLY) - a_band->channels[i].flags |= - IEEE80211_CHAN_PASSIVE_SCAN; - if (geo->a[i].flags & LIBIPW_CH_NO_IBSS) - a_band->channels[i].flags |= - IEEE80211_CHAN_NO_IBSS; - if (geo->a[i].flags & LIBIPW_CH_RADAR_DETECT) - a_band->channels[i].flags |= - IEEE80211_CHAN_RADAR; - /* No equivalent for LIBIPW_CH_80211H_RULES, - LIBIPW_CH_UNIFORM_SPREADING, or - LIBIPW_CH_B_ONLY... */ - } - /* point at bitrate info */ - a_band->bitrates = ipw2200_a_rates; - a_band->n_bitrates = ipw2200_num_a_rates; - - wdev->wiphy->bands[IEEE80211_BAND_5GHZ] = a_band; - } - - set_wiphy_dev(wdev->wiphy, &priv->pci_dev->dev); - - /* With that information in place, we can now register the wiphy... */ - if (wiphy_register(wdev->wiphy)) { - rc = -EIO; - goto out; + mutex_unlock(&priv->mutex); + return -EIO; } -out: mutex_unlock(&priv->mutex); - return rc; + return 0; } /* PCI driver stuff */ @@ -11640,7 +11556,7 @@ static int ipw_prom_alloc(struct ipw_priv *priv) if (priv->prom_net_dev) return -EPERM; - priv->prom_net_dev = alloc_ieee80211(sizeof(struct ipw_prom_priv), 1); + priv->prom_net_dev = alloc_ieee80211(sizeof(struct ipw_prom_priv)); if (priv->prom_net_dev == NULL) return -ENOMEM; @@ -11659,7 +11575,7 @@ static int ipw_prom_alloc(struct ipw_priv *priv) rc = register_netdev(priv->prom_net_dev); if (rc) { - free_ieee80211(priv->prom_net_dev, 1); + free_ieee80211(priv->prom_net_dev); priv->prom_net_dev = NULL; return rc; } @@ -11673,7 +11589,7 @@ static void ipw_prom_free(struct ipw_priv *priv) return; unregister_netdev(priv->prom_net_dev); - free_ieee80211(priv->prom_net_dev, 1); + free_ieee80211(priv->prom_net_dev); priv->prom_net_dev = NULL; } @@ -11701,7 +11617,7 @@ static int __devinit ipw_pci_probe(struct pci_dev *pdev, struct ipw_priv *priv; int i; - net_dev = alloc_ieee80211(sizeof(struct ipw_priv), 0); + net_dev = alloc_ieee80211(sizeof(struct ipw_priv)); if (net_dev == NULL) { err = -ENOMEM; goto out; @@ -11849,7 +11765,7 @@ static int __devinit ipw_pci_probe(struct pci_dev *pdev, pci_disable_device(pdev); pci_set_drvdata(pdev, NULL); out_free_ieee80211: - free_ieee80211(priv->net_dev, 0); + free_ieee80211(priv->net_dev); out: return err; } @@ -11916,7 +11832,7 @@ static void __devexit ipw_pci_remove(struct pci_dev *pdev) pci_release_regions(pdev); pci_disable_device(pdev); pci_set_drvdata(pdev, NULL); - free_ieee80211(priv->net_dev, 0); + free_ieee80211(priv->net_dev); free_firmware(); } diff --git a/drivers/net/wireless/ipw2x00/libipw.h b/drivers/net/wireless/ipw2x00/libipw.h index bf45391172f3..1e334ff6bd52 100644 --- a/drivers/net/wireless/ipw2x00/libipw.h +++ b/drivers/net/wireless/ipw2x00/libipw.h @@ -31,7 +31,6 @@ #include <linux/ieee80211.h> #include <net/lib80211.h> -#include <net/cfg80211.h> #define LIBIPW_VERSION "git-1.1.13" @@ -784,15 +783,12 @@ struct libipw_geo { struct libipw_device { struct net_device *dev; - struct wireless_dev wdev; struct libipw_security sec; /* Bookkeeping structures */ struct libipw_stats ieee_stats; struct libipw_geo geo; - struct ieee80211_supported_band bg_band; - struct ieee80211_supported_band a_band; /* Probe / Beacon management */ struct list_head network_free_list; @@ -1018,8 +1014,8 @@ static inline int libipw_is_cck_rate(u8 rate) } /* ieee80211.c */ -extern void free_ieee80211(struct net_device *dev, int monitor); -extern struct net_device *alloc_ieee80211(int sizeof_priv, int monitor); +extern void free_ieee80211(struct net_device *dev); +extern struct net_device *alloc_ieee80211(int sizeof_priv); extern int libipw_change_mtu(struct net_device *dev, int new_mtu); extern void libipw_networks_age(struct libipw_device *ieee, diff --git a/drivers/net/wireless/ipw2x00/libipw_module.c b/drivers/net/wireless/ipw2x00/libipw_module.c index a0e9f6aed7da..eb2b60834c17 100644 --- a/drivers/net/wireless/ipw2x00/libipw_module.c +++ b/drivers/net/wireless/ipw2x00/libipw_module.c @@ -62,9 +62,6 @@ MODULE_DESCRIPTION(DRV_DESCRIPTION); MODULE_AUTHOR(DRV_COPYRIGHT); MODULE_LICENSE("GPL"); -struct cfg80211_ops libipw_config_ops = { }; -void *libipw_wiphy_privid = &libipw_wiphy_privid; - static int libipw_networks_allocate(struct libipw_device *ieee) { if (ieee->networks) @@ -143,7 +140,7 @@ int libipw_change_mtu(struct net_device *dev, int new_mtu) } EXPORT_SYMBOL(libipw_change_mtu); -struct net_device *alloc_ieee80211(int sizeof_priv, int monitor) +struct net_device *alloc_ieee80211(int sizeof_priv) { struct libipw_device *ieee; struct net_device *dev; @@ -160,31 +157,10 @@ struct net_device *alloc_ieee80211(int sizeof_priv, int monitor) ieee->dev = dev; - if (!monitor) { - ieee->wdev.wiphy = wiphy_new(&libipw_config_ops, 0); - if (!ieee->wdev.wiphy) { - LIBIPW_ERROR("Unable to allocate wiphy.\n"); - goto failed_free_netdev; - } - - ieee->dev->ieee80211_ptr = &ieee->wdev; - ieee->wdev.iftype = NL80211_IFTYPE_STATION; - - /* Fill-out wiphy structure bits we know... Not enough info - here to call set_wiphy_dev or set MAC address or channel info - -- have to do that in ->ndo_init... */ - ieee->wdev.wiphy->privid = libipw_wiphy_privid; - - ieee->wdev.wiphy->max_scan_ssids = 1; - ieee->wdev.wiphy->max_scan_ie_len = 0; - ieee->wdev.wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) - | BIT(NL80211_IFTYPE_ADHOC); - } - err = libipw_networks_allocate(ieee); if (err) { LIBIPW_ERROR("Unable to allocate beacon storage: %d\n", err); - goto failed_free_wiphy; + goto failed_free_netdev; } libipw_networks_initialize(ieee); @@ -217,31 +193,19 @@ struct net_device *alloc_ieee80211(int sizeof_priv, int monitor) return dev; -failed_free_wiphy: - if (!monitor) - wiphy_free(ieee->wdev.wiphy); failed_free_netdev: free_netdev(dev); failed: return NULL; } -void free_ieee80211(struct net_device *dev, int monitor) +void free_ieee80211(struct net_device *dev) { struct libipw_device *ieee = netdev_priv(dev); lib80211_crypt_info_free(&ieee->crypt_info); libipw_networks_free(ieee); - - /* free cfg80211 resources */ - if (!monitor) { - wiphy_unregister(ieee->wdev.wiphy); - kfree(ieee->a_band.channels); - kfree(ieee->bg_band.channels); - wiphy_free(ieee->wdev.wiphy); - } - free_netdev(dev); } diff --git a/drivers/net/wireless/iwlwifi/iwl-1000.c b/drivers/net/wireless/iwlwifi/iwl-1000.c index a95caa014143..950267ab556a 100644 --- a/drivers/net/wireless/iwlwifi/iwl-1000.c +++ b/drivers/net/wireless/iwlwifi/iwl-1000.c @@ -99,6 +99,8 @@ static struct iwl_lib_ops iwl1000_lib = { .setup_deferred_work = iwl5000_setup_deferred_work, .is_valid_rtc_data_addr = iwl5000_hw_valid_rtc_data_addr, .load_ucode = iwl5000_load_ucode, + .dump_nic_event_log = iwl_dump_nic_event_log, + .dump_nic_error_log = iwl_dump_nic_error_log, .init_alive_start = iwl5000_init_alive_start, .alive_notify = iwl5000_alive_notify, .send_tx_power = iwl5000_send_tx_power, @@ -159,5 +161,6 @@ struct iwl_cfg iwl1000_bgn_cfg = { .max_ll_items = OTP_MAX_LL_ITEMS_1000, .shadow_ram_support = false, .ht_greenfield_support = true, + .use_rts_for_ht = true, /* use rts/cts protection */ }; diff --git a/drivers/net/wireless/iwlwifi/iwl-3945-rs.c b/drivers/net/wireless/iwlwifi/iwl-3945-rs.c index a16bd4147eac..cbb0585083a9 100644 --- a/drivers/net/wireless/iwlwifi/iwl-3945-rs.c +++ b/drivers/net/wireless/iwlwifi/iwl-3945-rs.c @@ -702,7 +702,7 @@ static void rs_get_rate(void *priv_r, struct ieee80211_sta *sta, u8 sta_id = iwl_find_station(priv, hdr->addr1); if (sta_id == IWL_INVALID_STATION) { - IWL_DEBUG_RATE(priv, "LQ: ADD station %pm\n", + IWL_DEBUG_RATE(priv, "LQ: ADD station %pM\n", hdr->addr1); sta_id = iwl_add_station(priv, hdr->addr1, false, CMD_ASYNC, NULL); diff --git a/drivers/net/wireless/iwlwifi/iwl-3945.c b/drivers/net/wireless/iwlwifi/iwl-3945.c index e9a685d8e3a1..f059b49dc691 100644 --- a/drivers/net/wireless/iwlwifi/iwl-3945.c +++ b/drivers/net/wireless/iwlwifi/iwl-3945.c @@ -30,6 +30,7 @@ #include <linux/pci.h> #include <linux/dma-mapping.h> #include <linux/delay.h> +#include <linux/sched.h> #include <linux/skbuff.h> #include <linux/netdevice.h> #include <linux/wireless.h> @@ -610,7 +611,7 @@ static void iwl3945_rx_reply_rx(struct iwl_priv *priv, if (rx_status.band == IEEE80211_BAND_5GHZ) rx_status.rate_idx -= IWL_FIRST_OFDM_RATE; - rx_status.antenna = le16_to_cpu(rx_hdr->phy_flags & + rx_status.antenna = (le16_to_cpu(rx_hdr->phy_flags) & RX_RES_PHY_FLAGS_ANTENNA_MSK) >> 4; /* set the preamble flag if appropriate */ @@ -2839,6 +2840,8 @@ static struct iwl_lib_ops iwl3945_lib = { .txq_free_tfd = iwl3945_hw_txq_free_tfd, .txq_init = iwl3945_hw_tx_queue_init, .load_ucode = iwl3945_load_bsm, + .dump_nic_event_log = iwl3945_dump_nic_event_log, + .dump_nic_error_log = iwl3945_dump_nic_error_log, .apm_ops = { .init = iwl3945_apm_init, .reset = iwl3945_apm_reset, diff --git a/drivers/net/wireless/iwlwifi/iwl-3945.h b/drivers/net/wireless/iwlwifi/iwl-3945.h index f24036909916..21679bf3a1aa 100644 --- a/drivers/net/wireless/iwlwifi/iwl-3945.h +++ b/drivers/net/wireless/iwlwifi/iwl-3945.h @@ -209,6 +209,8 @@ extern int __must_check iwl3945_send_cmd(struct iwl_priv *priv, struct iwl_host_cmd *cmd); extern unsigned int iwl3945_fill_beacon_frame(struct iwl_priv *priv, struct ieee80211_hdr *hdr,int left); +extern void iwl3945_dump_nic_event_log(struct iwl_priv *priv); +extern void iwl3945_dump_nic_error_log(struct iwl_priv *priv); /* * Currently used by iwl-3945-rs... look at restructuring so that it doesn't diff --git a/drivers/net/wireless/iwlwifi/iwl-4965.c b/drivers/net/wireless/iwlwifi/iwl-4965.c index ca61d3796cef..6f703a041847 100644 --- a/drivers/net/wireless/iwlwifi/iwl-4965.c +++ b/drivers/net/wireless/iwlwifi/iwl-4965.c @@ -30,6 +30,7 @@ #include <linux/pci.h> #include <linux/dma-mapping.h> #include <linux/delay.h> +#include <linux/sched.h> #include <linux/skbuff.h> #include <linux/netdevice.h> #include <linux/wireless.h> @@ -2021,6 +2022,12 @@ static int iwl4965_tx_status_reply_tx(struct iwl_priv *priv, agg->frame_count, txq_id, idx); hdr = iwl_tx_queue_get_hdr(priv, txq_id, idx); + if (!hdr) { + IWL_ERR(priv, + "BUG_ON idx doesn't point to valid skb" + " idx=%d, txq_id=%d\n", idx, txq_id); + return -1; + } sc = le16_to_cpu(hdr->seq_ctrl); if (idx != (SEQ_TO_SN(sc) & 0xff)) { @@ -2292,6 +2299,8 @@ static struct iwl_lib_ops iwl4965_lib = { .alive_notify = iwl4965_alive_notify, .init_alive_start = iwl4965_init_alive_start, .load_ucode = iwl4965_load_bsm, + .dump_nic_event_log = iwl_dump_nic_event_log, + .dump_nic_error_log = iwl_dump_nic_error_log, .apm_ops = { .init = iwl4965_apm_init, .reset = iwl4965_apm_reset, diff --git a/drivers/net/wireless/iwlwifi/iwl-5000.c b/drivers/net/wireless/iwlwifi/iwl-5000.c index 1d539e3b8db1..6e6f516ba404 100644 --- a/drivers/net/wireless/iwlwifi/iwl-5000.c +++ b/drivers/net/wireless/iwlwifi/iwl-5000.c @@ -29,6 +29,7 @@ #include <linux/pci.h> #include <linux/dma-mapping.h> #include <linux/delay.h> +#include <linux/sched.h> #include <linux/skbuff.h> #include <linux/netdevice.h> #include <linux/wireless.h> @@ -317,7 +318,7 @@ static void iwl5000_gain_computation(struct iwl_priv *priv, (s32)average_noise[i])) / 1500; /* bound gain by 2 bits value max, 3rd bit is sign */ data->delta_gain_code[i] = - min(abs(delta_g), CHAIN_NOISE_MAX_DELTA_GAIN_CODE); + min(abs(delta_g), (long) CHAIN_NOISE_MAX_DELTA_GAIN_CODE); if (delta_g < 0) /* set negative sign */ @@ -1163,6 +1164,12 @@ static int iwl5000_tx_status_reply_tx(struct iwl_priv *priv, agg->frame_count, txq_id, idx); hdr = iwl_tx_queue_get_hdr(priv, txq_id, idx); + if (!hdr) { + IWL_ERR(priv, + "BUG_ON idx doesn't point to valid skb" + " idx=%d, txq_id=%d\n", idx, txq_id); + return -1; + } sc = le16_to_cpu(hdr->seq_ctrl); if (idx != (SEQ_TO_SN(sc) & 0xff)) { @@ -1529,6 +1536,8 @@ struct iwl_lib_ops iwl5000_lib = { .rx_handler_setup = iwl5000_rx_handler_setup, .setup_deferred_work = iwl5000_setup_deferred_work, .is_valid_rtc_data_addr = iwl5000_hw_valid_rtc_data_addr, + .dump_nic_event_log = iwl_dump_nic_event_log, + .dump_nic_error_log = iwl_dump_nic_error_log, .load_ucode = iwl5000_load_ucode, .init_alive_start = iwl5000_init_alive_start, .alive_notify = iwl5000_alive_notify, @@ -1579,6 +1588,8 @@ static struct iwl_lib_ops iwl5150_lib = { .rx_handler_setup = iwl5000_rx_handler_setup, .setup_deferred_work = iwl5000_setup_deferred_work, .is_valid_rtc_data_addr = iwl5000_hw_valid_rtc_data_addr, + .dump_nic_event_log = iwl_dump_nic_event_log, + .dump_nic_error_log = iwl_dump_nic_error_log, .load_ucode = iwl5000_load_ucode, .init_alive_start = iwl5000_init_alive_start, .alive_notify = iwl5000_alive_notify, diff --git a/drivers/net/wireless/iwlwifi/iwl-6000.c b/drivers/net/wireless/iwlwifi/iwl-6000.c index 82b9c93dff54..1473452ba22f 100644 --- a/drivers/net/wireless/iwlwifi/iwl-6000.c +++ b/drivers/net/wireless/iwlwifi/iwl-6000.c @@ -100,6 +100,8 @@ static struct iwl_lib_ops iwl6000_lib = { .setup_deferred_work = iwl5000_setup_deferred_work, .is_valid_rtc_data_addr = iwl5000_hw_valid_rtc_data_addr, .load_ucode = iwl5000_load_ucode, + .dump_nic_event_log = iwl_dump_nic_event_log, + .dump_nic_error_log = iwl_dump_nic_error_log, .init_alive_start = iwl5000_init_alive_start, .alive_notify = iwl5000_alive_notify, .send_tx_power = iwl5000_send_tx_power, @@ -173,6 +175,7 @@ struct iwl_cfg iwl6000h_2agn_cfg = { .max_ll_items = OTP_MAX_LL_ITEMS_6x00, .shadow_ram_support = true, .ht_greenfield_support = true, + .use_rts_for_ht = true, /* use rts/cts protection */ }; /* @@ -196,6 +199,7 @@ struct iwl_cfg iwl6000i_2agn_cfg = { .max_ll_items = OTP_MAX_LL_ITEMS_6x00, .shadow_ram_support = true, .ht_greenfield_support = true, + .use_rts_for_ht = true, /* use rts/cts protection */ }; struct iwl_cfg iwl6050_2agn_cfg = { @@ -216,6 +220,7 @@ struct iwl_cfg iwl6050_2agn_cfg = { .max_ll_items = OTP_MAX_LL_ITEMS_6x00, .shadow_ram_support = true, .ht_greenfield_support = true, + .use_rts_for_ht = true, /* use rts/cts protection */ }; struct iwl_cfg iwl6000_3agn_cfg = { @@ -236,6 +241,7 @@ struct iwl_cfg iwl6000_3agn_cfg = { .max_ll_items = OTP_MAX_LL_ITEMS_6x00, .shadow_ram_support = true, .ht_greenfield_support = true, + .use_rts_for_ht = true, /* use rts/cts protection */ }; struct iwl_cfg iwl6050_3agn_cfg = { @@ -256,6 +262,7 @@ struct iwl_cfg iwl6050_3agn_cfg = { .max_ll_items = OTP_MAX_LL_ITEMS_6x00, .shadow_ram_support = true, .ht_greenfield_support = true, + .use_rts_for_ht = true, /* use rts/cts protection */ }; MODULE_FIRMWARE(IWL6000_MODULE_FIRMWARE(IWL6000_UCODE_API_MAX)); diff --git a/drivers/net/wireless/iwlwifi/iwl-agn-rs.c b/drivers/net/wireless/iwlwifi/iwl-agn-rs.c index 346dc06fa7b7..81726ee32858 100644 --- a/drivers/net/wireless/iwlwifi/iwl-agn-rs.c +++ b/drivers/net/wireless/iwlwifi/iwl-agn-rs.c @@ -418,6 +418,15 @@ static void rs_tl_turn_on_agg(struct iwl_priv *priv, u8 tid, else if (tid == IWL_AGG_ALL_TID) for (tid = 0; tid < TID_MAX_LOAD_COUNT; tid++) rs_tl_turn_on_agg_for_tid(priv, lq_data, tid, sta); + if (priv->cfg->use_rts_for_ht) { + /* + * switch to RTS/CTS if it is the prefer protection method + * for HT traffic + */ + IWL_DEBUG_HT(priv, "use RTS/CTS protection for HT\n"); + priv->staging_rxon.flags &= ~RXON_FLG_SELF_CTS_EN; + iwlcore_commit_rxon(priv); + } } static inline int get_num_of_ant_from_rate(u32 rate_n_flags) diff --git a/drivers/net/wireless/iwlwifi/iwl-agn.c b/drivers/net/wireless/iwlwifi/iwl-agn.c index 00457bff1ed1..921dc4a26fe2 100644 --- a/drivers/net/wireless/iwlwifi/iwl-agn.c +++ b/drivers/net/wireless/iwlwifi/iwl-agn.c @@ -33,6 +33,7 @@ #include <linux/pci.h> #include <linux/dma-mapping.h> #include <linux/delay.h> +#include <linux/sched.h> #include <linux/skbuff.h> #include <linux/netdevice.h> #include <linux/wireless.h> @@ -115,9 +116,6 @@ int iwl_commit_rxon(struct iwl_priv *priv) /* always get timestamp with Rx frame */ priv->staging_rxon.flags |= RXON_FLG_TSF2HOST_MSK; - /* allow CTS-to-self if possible. this is relevant only for - * 5000, but will not damage 4965 */ - priv->staging_rxon.flags |= RXON_FLG_SELF_CTS_EN; ret = iwl_check_rxon_cmd(priv); if (ret) { @@ -217,6 +215,13 @@ int iwl_commit_rxon(struct iwl_priv *priv) "Could not send WEP static key.\n"); } + /* + * allow CTS-to-self if possible for new association. + * this is relevant only for 5000 series and up, + * but will not damage 4965 + */ + priv->staging_rxon.flags |= RXON_FLG_SELF_CTS_EN; + /* Apply the new configuration * RXON assoc doesn't clear the station table in uCode, */ @@ -1526,6 +1531,191 @@ static int iwl_read_ucode(struct iwl_priv *priv) return ret; } +#ifdef CONFIG_IWLWIFI_DEBUG +static const char *desc_lookup_text[] = { + "OK", + "FAIL", + "BAD_PARAM", + "BAD_CHECKSUM", + "NMI_INTERRUPT_WDG", + "SYSASSERT", + "FATAL_ERROR", + "BAD_COMMAND", + "HW_ERROR_TUNE_LOCK", + "HW_ERROR_TEMPERATURE", + "ILLEGAL_CHAN_FREQ", + "VCC_NOT_STABLE", + "FH_ERROR", + "NMI_INTERRUPT_HOST", + "NMI_INTERRUPT_ACTION_PT", + "NMI_INTERRUPT_UNKNOWN", + "UCODE_VERSION_MISMATCH", + "HW_ERROR_ABS_LOCK", + "HW_ERROR_CAL_LOCK_FAIL", + "NMI_INTERRUPT_INST_ACTION_PT", + "NMI_INTERRUPT_DATA_ACTION_PT", + "NMI_TRM_HW_ER", + "NMI_INTERRUPT_TRM", + "NMI_INTERRUPT_BREAK_POINT" + "DEBUG_0", + "DEBUG_1", + "DEBUG_2", + "DEBUG_3", + "UNKNOWN" +}; + +static const char *desc_lookup(int i) +{ + int max = ARRAY_SIZE(desc_lookup_text) - 1; + + if (i < 0 || i > max) + i = max; + + return desc_lookup_text[i]; +} + +#define ERROR_START_OFFSET (1 * sizeof(u32)) +#define ERROR_ELEM_SIZE (7 * sizeof(u32)) + +void iwl_dump_nic_error_log(struct iwl_priv *priv) +{ + u32 data2, line; + u32 desc, time, count, base, data1; + u32 blink1, blink2, ilink1, ilink2; + + if (priv->ucode_type == UCODE_INIT) + base = le32_to_cpu(priv->card_alive_init.error_event_table_ptr); + else + base = le32_to_cpu(priv->card_alive.error_event_table_ptr); + + if (!priv->cfg->ops->lib->is_valid_rtc_data_addr(base)) { + IWL_ERR(priv, "Not valid error log pointer 0x%08X\n", base); + return; + } + + count = iwl_read_targ_mem(priv, base); + + if (ERROR_START_OFFSET <= count * ERROR_ELEM_SIZE) { + IWL_ERR(priv, "Start IWL Error Log Dump:\n"); + IWL_ERR(priv, "Status: 0x%08lX, count: %d\n", + priv->status, count); + } + + desc = iwl_read_targ_mem(priv, base + 1 * sizeof(u32)); + blink1 = iwl_read_targ_mem(priv, base + 3 * sizeof(u32)); + blink2 = iwl_read_targ_mem(priv, base + 4 * sizeof(u32)); + ilink1 = iwl_read_targ_mem(priv, base + 5 * sizeof(u32)); + ilink2 = iwl_read_targ_mem(priv, base + 6 * sizeof(u32)); + data1 = iwl_read_targ_mem(priv, base + 7 * sizeof(u32)); + data2 = iwl_read_targ_mem(priv, base + 8 * sizeof(u32)); + line = iwl_read_targ_mem(priv, base + 9 * sizeof(u32)); + time = iwl_read_targ_mem(priv, base + 11 * sizeof(u32)); + + IWL_ERR(priv, "Desc Time " + "data1 data2 line\n"); + IWL_ERR(priv, "%-28s (#%02d) %010u 0x%08X 0x%08X %u\n", + desc_lookup(desc), desc, time, data1, data2, line); + IWL_ERR(priv, "blink1 blink2 ilink1 ilink2\n"); + IWL_ERR(priv, "0x%05X 0x%05X 0x%05X 0x%05X\n", blink1, blink2, + ilink1, ilink2); + +} + +#define EVENT_START_OFFSET (4 * sizeof(u32)) + +/** + * iwl_print_event_log - Dump error event log to syslog + * + */ +static void iwl_print_event_log(struct iwl_priv *priv, u32 start_idx, + u32 num_events, u32 mode) +{ + u32 i; + u32 base; /* SRAM byte address of event log header */ + u32 event_size; /* 2 u32s, or 3 u32s if timestamp recorded */ + u32 ptr; /* SRAM byte address of log data */ + u32 ev, time, data; /* event log data */ + + if (num_events == 0) + return; + if (priv->ucode_type == UCODE_INIT) + base = le32_to_cpu(priv->card_alive_init.log_event_table_ptr); + else + base = le32_to_cpu(priv->card_alive.log_event_table_ptr); + + if (mode == 0) + event_size = 2 * sizeof(u32); + else + event_size = 3 * sizeof(u32); + + ptr = base + EVENT_START_OFFSET + (start_idx * event_size); + + /* "time" is actually "data" for mode 0 (no timestamp). + * place event id # at far right for easier visual parsing. */ + for (i = 0; i < num_events; i++) { + ev = iwl_read_targ_mem(priv, ptr); + ptr += sizeof(u32); + time = iwl_read_targ_mem(priv, ptr); + ptr += sizeof(u32); + if (mode == 0) { + /* data, ev */ + IWL_ERR(priv, "EVT_LOG:0x%08x:%04u\n", time, ev); + } else { + data = iwl_read_targ_mem(priv, ptr); + ptr += sizeof(u32); + IWL_ERR(priv, "EVT_LOGT:%010u:0x%08x:%04u\n", + time, data, ev); + } + } +} + +void iwl_dump_nic_event_log(struct iwl_priv *priv) +{ + u32 base; /* SRAM byte address of event log header */ + u32 capacity; /* event log capacity in # entries */ + u32 mode; /* 0 - no timestamp, 1 - timestamp recorded */ + u32 num_wraps; /* # times uCode wrapped to top of log */ + u32 next_entry; /* index of next entry to be written by uCode */ + u32 size; /* # entries that we'll print */ + + if (priv->ucode_type == UCODE_INIT) + base = le32_to_cpu(priv->card_alive_init.log_event_table_ptr); + else + base = le32_to_cpu(priv->card_alive.log_event_table_ptr); + + if (!priv->cfg->ops->lib->is_valid_rtc_data_addr(base)) { + IWL_ERR(priv, "Invalid event log pointer 0x%08X\n", base); + return; + } + + /* event log header */ + capacity = iwl_read_targ_mem(priv, base); + mode = iwl_read_targ_mem(priv, base + (1 * sizeof(u32))); + num_wraps = iwl_read_targ_mem(priv, base + (2 * sizeof(u32))); + next_entry = iwl_read_targ_mem(priv, base + (3 * sizeof(u32))); + + size = num_wraps ? capacity : next_entry; + + /* bail out if nothing in log */ + if (size == 0) { + IWL_ERR(priv, "Start IWL Event Log Dump: nothing in log\n"); + return; + } + + IWL_ERR(priv, "Start IWL Event Log Dump: display count %d, wraps %d\n", + size, num_wraps); + + /* if uCode has wrapped back to top of log, start at the oldest entry, + * i.e the next one that uCode would fill. */ + if (num_wraps) + iwl_print_event_log(priv, next_entry, + capacity - next_entry, mode); + /* (then/else) start at top of log */ + iwl_print_event_log(priv, 0, next_entry, mode); + +} +#endif + /** * iwl_alive_start - called after REPLY_ALIVE notification received * from protocol/runtime uCode (initialization uCode's @@ -2920,8 +3110,8 @@ static int iwl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) out_pci_disable_device: pci_disable_device(pdev); out_ieee80211_free_hw: - ieee80211_free_hw(priv->hw); iwl_free_traffic_mem(priv); + ieee80211_free_hw(priv->hw); out: return err; } diff --git a/drivers/net/wireless/iwlwifi/iwl-commands.h b/drivers/net/wireless/iwlwifi/iwl-commands.h index 2c5c88fc38f5..4afaf773aeac 100644 --- a/drivers/net/wireless/iwlwifi/iwl-commands.h +++ b/drivers/net/wireless/iwlwifi/iwl-commands.h @@ -1154,7 +1154,7 @@ struct iwl_wep_cmd { #define RX_RES_PHY_FLAGS_MOD_CCK_MSK cpu_to_le16(1 << 1) #define RX_RES_PHY_FLAGS_SHORT_PREAMBLE_MSK cpu_to_le16(1 << 2) #define RX_RES_PHY_FLAGS_NARROW_BAND_MSK cpu_to_le16(1 << 3) -#define RX_RES_PHY_FLAGS_ANTENNA_MSK cpu_to_le16(0xf0) +#define RX_RES_PHY_FLAGS_ANTENNA_MSK 0xf0 #define RX_RES_PHY_FLAGS_ANTENNA_POS 4 #define RX_RES_STATUS_SEC_TYPE_MSK (0x7 << 8) diff --git a/drivers/net/wireless/iwlwifi/iwl-core.c b/drivers/net/wireless/iwlwifi/iwl-core.c index fd26c0dc9c54..2dc928755454 100644 --- a/drivers/net/wireless/iwlwifi/iwl-core.c +++ b/drivers/net/wireless/iwlwifi/iwl-core.c @@ -29,6 +29,7 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/etherdevice.h> +#include <linux/sched.h> #include <net/mac80211.h> #include "iwl-eeprom.h" @@ -1309,189 +1310,6 @@ static void iwl_print_rx_config_cmd(struct iwl_priv *priv) IWL_DEBUG_RADIO(priv, "u8[6] bssid_addr: %pM\n", rxon->bssid_addr); IWL_DEBUG_RADIO(priv, "u16 assoc_id: 0x%x\n", le16_to_cpu(rxon->assoc_id)); } - -static const char *desc_lookup_text[] = { - "OK", - "FAIL", - "BAD_PARAM", - "BAD_CHECKSUM", - "NMI_INTERRUPT_WDG", - "SYSASSERT", - "FATAL_ERROR", - "BAD_COMMAND", - "HW_ERROR_TUNE_LOCK", - "HW_ERROR_TEMPERATURE", - "ILLEGAL_CHAN_FREQ", - "VCC_NOT_STABLE", - "FH_ERROR", - "NMI_INTERRUPT_HOST", - "NMI_INTERRUPT_ACTION_PT", - "NMI_INTERRUPT_UNKNOWN", - "UCODE_VERSION_MISMATCH", - "HW_ERROR_ABS_LOCK", - "HW_ERROR_CAL_LOCK_FAIL", - "NMI_INTERRUPT_INST_ACTION_PT", - "NMI_INTERRUPT_DATA_ACTION_PT", - "NMI_TRM_HW_ER", - "NMI_INTERRUPT_TRM", - "NMI_INTERRUPT_BREAK_POINT" - "DEBUG_0", - "DEBUG_1", - "DEBUG_2", - "DEBUG_3", - "UNKNOWN" -}; - -static const char *desc_lookup(int i) -{ - int max = ARRAY_SIZE(desc_lookup_text) - 1; - - if (i < 0 || i > max) - i = max; - - return desc_lookup_text[i]; -} - -#define ERROR_START_OFFSET (1 * sizeof(u32)) -#define ERROR_ELEM_SIZE (7 * sizeof(u32)) - -static void iwl_dump_nic_error_log(struct iwl_priv *priv) -{ - u32 data2, line; - u32 desc, time, count, base, data1; - u32 blink1, blink2, ilink1, ilink2; - - if (priv->ucode_type == UCODE_INIT) - base = le32_to_cpu(priv->card_alive_init.error_event_table_ptr); - else - base = le32_to_cpu(priv->card_alive.error_event_table_ptr); - - if (!priv->cfg->ops->lib->is_valid_rtc_data_addr(base)) { - IWL_ERR(priv, "Not valid error log pointer 0x%08X\n", base); - return; - } - - count = iwl_read_targ_mem(priv, base); - - if (ERROR_START_OFFSET <= count * ERROR_ELEM_SIZE) { - IWL_ERR(priv, "Start IWL Error Log Dump:\n"); - IWL_ERR(priv, "Status: 0x%08lX, count: %d\n", - priv->status, count); - } - - desc = iwl_read_targ_mem(priv, base + 1 * sizeof(u32)); - blink1 = iwl_read_targ_mem(priv, base + 3 * sizeof(u32)); - blink2 = iwl_read_targ_mem(priv, base + 4 * sizeof(u32)); - ilink1 = iwl_read_targ_mem(priv, base + 5 * sizeof(u32)); - ilink2 = iwl_read_targ_mem(priv, base + 6 * sizeof(u32)); - data1 = iwl_read_targ_mem(priv, base + 7 * sizeof(u32)); - data2 = iwl_read_targ_mem(priv, base + 8 * sizeof(u32)); - line = iwl_read_targ_mem(priv, base + 9 * sizeof(u32)); - time = iwl_read_targ_mem(priv, base + 11 * sizeof(u32)); - - IWL_ERR(priv, "Desc Time " - "data1 data2 line\n"); - IWL_ERR(priv, "%-28s (#%02d) %010u 0x%08X 0x%08X %u\n", - desc_lookup(desc), desc, time, data1, data2, line); - IWL_ERR(priv, "blink1 blink2 ilink1 ilink2\n"); - IWL_ERR(priv, "0x%05X 0x%05X 0x%05X 0x%05X\n", blink1, blink2, - ilink1, ilink2); - -} - -#define EVENT_START_OFFSET (4 * sizeof(u32)) - -/** - * iwl_print_event_log - Dump error event log to syslog - * - */ -static void iwl_print_event_log(struct iwl_priv *priv, u32 start_idx, - u32 num_events, u32 mode) -{ - u32 i; - u32 base; /* SRAM byte address of event log header */ - u32 event_size; /* 2 u32s, or 3 u32s if timestamp recorded */ - u32 ptr; /* SRAM byte address of log data */ - u32 ev, time, data; /* event log data */ - - if (num_events == 0) - return; - if (priv->ucode_type == UCODE_INIT) - base = le32_to_cpu(priv->card_alive_init.log_event_table_ptr); - else - base = le32_to_cpu(priv->card_alive.log_event_table_ptr); - - if (mode == 0) - event_size = 2 * sizeof(u32); - else - event_size = 3 * sizeof(u32); - - ptr = base + EVENT_START_OFFSET + (start_idx * event_size); - - /* "time" is actually "data" for mode 0 (no timestamp). - * place event id # at far right for easier visual parsing. */ - for (i = 0; i < num_events; i++) { - ev = iwl_read_targ_mem(priv, ptr); - ptr += sizeof(u32); - time = iwl_read_targ_mem(priv, ptr); - ptr += sizeof(u32); - if (mode == 0) { - /* data, ev */ - IWL_ERR(priv, "EVT_LOG:0x%08x:%04u\n", time, ev); - } else { - data = iwl_read_targ_mem(priv, ptr); - ptr += sizeof(u32); - IWL_ERR(priv, "EVT_LOGT:%010u:0x%08x:%04u\n", - time, data, ev); - } - } -} - -void iwl_dump_nic_event_log(struct iwl_priv *priv) -{ - u32 base; /* SRAM byte address of event log header */ - u32 capacity; /* event log capacity in # entries */ - u32 mode; /* 0 - no timestamp, 1 - timestamp recorded */ - u32 num_wraps; /* # times uCode wrapped to top of log */ - u32 next_entry; /* index of next entry to be written by uCode */ - u32 size; /* # entries that we'll print */ - - if (priv->ucode_type == UCODE_INIT) - base = le32_to_cpu(priv->card_alive_init.log_event_table_ptr); - else - base = le32_to_cpu(priv->card_alive.log_event_table_ptr); - - if (!priv->cfg->ops->lib->is_valid_rtc_data_addr(base)) { - IWL_ERR(priv, "Invalid event log pointer 0x%08X\n", base); - return; - } - - /* event log header */ - capacity = iwl_read_targ_mem(priv, base); - mode = iwl_read_targ_mem(priv, base + (1 * sizeof(u32))); - num_wraps = iwl_read_targ_mem(priv, base + (2 * sizeof(u32))); - next_entry = iwl_read_targ_mem(priv, base + (3 * sizeof(u32))); - - size = num_wraps ? capacity : next_entry; - - /* bail out if nothing in log */ - if (size == 0) { - IWL_ERR(priv, "Start IWL Event Log Dump: nothing in log\n"); - return; - } - - IWL_ERR(priv, "Start IWL Event Log Dump: display count %d, wraps %d\n", - size, num_wraps); - - /* if uCode has wrapped back to top of log, start at the oldest entry, - * i.e the next one that uCode would fill. */ - if (num_wraps) - iwl_print_event_log(priv, next_entry, - capacity - next_entry, mode); - /* (then/else) start at top of log */ - iwl_print_event_log(priv, 0, next_entry, mode); - -} #endif /** * iwl_irq_handle_error - called for HW or SW error interrupt from card @@ -1506,8 +1324,8 @@ void iwl_irq_handle_error(struct iwl_priv *priv) #ifdef CONFIG_IWLWIFI_DEBUG if (iwl_get_debug_level(priv) & IWL_DL_FW_ERRORS) { - iwl_dump_nic_error_log(priv); - iwl_dump_nic_event_log(priv); + priv->cfg->ops->lib->dump_nic_error_log(priv); + priv->cfg->ops->lib->dump_nic_event_log(priv); iwl_print_rx_config_cmd(priv); } #endif diff --git a/drivers/net/wireless/iwlwifi/iwl-core.h b/drivers/net/wireless/iwlwifi/iwl-core.h index 7ff9ffb2b702..7754538c2194 100644 --- a/drivers/net/wireless/iwlwifi/iwl-core.h +++ b/drivers/net/wireless/iwlwifi/iwl-core.h @@ -166,6 +166,8 @@ struct iwl_lib_ops { int (*is_valid_rtc_data_addr)(u32 addr); /* 1st ucode load */ int (*load_ucode)(struct iwl_priv *priv); + void (*dump_nic_event_log)(struct iwl_priv *priv); + void (*dump_nic_error_log)(struct iwl_priv *priv); /* power management */ struct iwl_apm_ops apm_ops; @@ -211,6 +213,7 @@ struct iwl_mod_params { * @pa_type: used by 6000 series only to identify the type of Power Amplifier * @max_ll_items: max number of OTP blocks * @shadow_ram_support: shadow support for OTP memory + * @use_rts_for_ht: use rts/cts protection for HT traffic * * We enable the driver to be backward compatible wrt API version. The * driver specifies which APIs it supports (with @ucode_api_max being the @@ -253,6 +256,7 @@ struct iwl_cfg { const bool shadow_ram_support; const bool ht_greenfield_support; const bool broken_powersave; + bool use_rts_for_ht; }; /*************************** @@ -540,7 +544,19 @@ int iwl_pci_resume(struct pci_dev *pdev); /***************************************************** * Error Handling Debugging ******************************************************/ +#ifdef CONFIG_IWLWIFI_DEBUG void iwl_dump_nic_event_log(struct iwl_priv *priv); +void iwl_dump_nic_error_log(struct iwl_priv *priv); +#else +static inline void iwl_dump_nic_event_log(struct iwl_priv *priv) +{ +} + +static inline void iwl_dump_nic_error_log(struct iwl_priv *priv) +{ +} +#endif + void iwl_clear_isr_stats(struct iwl_priv *priv); /***************************************************** diff --git a/drivers/net/wireless/iwlwifi/iwl-debugfs.c b/drivers/net/wireless/iwlwifi/iwl-debugfs.c index fb844859a443..a198bcf61022 100644 --- a/drivers/net/wireless/iwlwifi/iwl-debugfs.c +++ b/drivers/net/wireless/iwlwifi/iwl-debugfs.c @@ -410,7 +410,7 @@ static ssize_t iwl_dbgfs_nvm_read(struct file *file, pos += scnprintf(buf + pos, buf_size - pos, "0x%.4x ", ofs); hex_dump_to_buffer(ptr + ofs, 16 , 16, 2, buf + pos, buf_size - pos, 0); - pos += strlen(buf); + pos += strlen(buf + pos); if (buf_size - pos > 0) buf[pos++] = '\n'; } @@ -436,7 +436,7 @@ static ssize_t iwl_dbgfs_log_event_write(struct file *file, if (sscanf(buf, "%d", &event_log_flag) != 1) return -EFAULT; if (event_log_flag == 1) - iwl_dump_nic_event_log(priv); + priv->cfg->ops->lib->dump_nic_event_log(priv); return count; } @@ -909,7 +909,7 @@ static ssize_t iwl_dbgfs_traffic_log_read(struct file *file, "0x%.4x ", ofs); hex_dump_to_buffer(ptr + ofs, 16, 16, 2, buf + pos, bufsz - pos, 0); - pos += strlen(buf); + pos += strlen(buf + pos); if (bufsz - pos > 0) buf[pos++] = '\n'; } @@ -932,7 +932,7 @@ static ssize_t iwl_dbgfs_traffic_log_read(struct file *file, "0x%.4x ", ofs); hex_dump_to_buffer(ptr + ofs, 16, 16, 2, buf + pos, bufsz - pos, 0); - pos += strlen(buf); + pos += strlen(buf + pos); if (bufsz - pos > 0) buf[pos++] = '\n'; } diff --git a/drivers/net/wireless/iwlwifi/iwl-eeprom.c b/drivers/net/wireless/iwlwifi/iwl-eeprom.c index 3d2b93a61e62..e14c9952a935 100644 --- a/drivers/net/wireless/iwlwifi/iwl-eeprom.c +++ b/drivers/net/wireless/iwlwifi/iwl-eeprom.c @@ -410,7 +410,6 @@ static int iwl_find_otp_image(struct iwl_priv *priv, u16 *validblockaddr) { u16 next_link_addr = 0, link_value = 0, valid_addr; - int ret = 0; int usedblocks = 0; /* set addressing mode to absolute to traverse the link list */ @@ -430,29 +429,29 @@ static int iwl_find_otp_image(struct iwl_priv *priv, * check for more block on the link list */ valid_addr = next_link_addr; - next_link_addr = link_value; + next_link_addr = link_value * sizeof(u16); IWL_DEBUG_INFO(priv, "OTP blocks %d addr 0x%x\n", usedblocks, next_link_addr); if (iwl_read_otp_word(priv, next_link_addr, &link_value)) return -EINVAL; if (!link_value) { /* - * reach the end of link list, + * reach the end of link list, return success and * set address point to the starting address * of the image */ - goto done; + *validblockaddr = valid_addr; + /* skip first 2 bytes (link list pointer) */ + *validblockaddr += 2; + return 0; } /* more in the link list, continue */ usedblocks++; - } while (usedblocks < priv->cfg->max_ll_items); - /* OTP full, use last block */ - IWL_DEBUG_INFO(priv, "OTP is full, use last block\n"); -done: - *validblockaddr = valid_addr; - /* skip first 2 bytes (link list pointer) */ - *validblockaddr += 2; - return ret; + } while (usedblocks <= priv->cfg->max_ll_items); + + /* OTP has no valid blocks */ + IWL_DEBUG_INFO(priv, "OTP has no valid blocks\n"); + return -EINVAL; } /** diff --git a/drivers/net/wireless/iwlwifi/iwl-eeprom.h b/drivers/net/wireless/iwlwifi/iwl-eeprom.h index 6b68db7b1b81..80b9e45d9b9c 100644 --- a/drivers/net/wireless/iwlwifi/iwl-eeprom.h +++ b/drivers/net/wireless/iwlwifi/iwl-eeprom.h @@ -220,35 +220,35 @@ struct iwl_eeprom_enhanced_txpwr { * Section 10: 2.4 GHz 40MHz channels: 132, 44 (_above_) */ /* 2.4 GHz band: CCK */ -#define EEPROM_LB_CCK_20_COMMON ((0xAA)\ +#define EEPROM_LB_CCK_20_COMMON ((0xA8)\ | INDIRECT_ADDRESS | INDIRECT_REGULATORY) /* 8 bytes */ /* 2.4 GHz band: 20MHz-Legacy, 20MHz-HT, 40MHz-HT */ -#define EEPROM_LB_OFDM_COMMON ((0xB2)\ +#define EEPROM_LB_OFDM_COMMON ((0xB0)\ | INDIRECT_ADDRESS | INDIRECT_REGULATORY) /* 24 bytes */ /* 5.2 GHz band: 20MHz-Legacy, 20MHz-HT, 40MHz-HT */ -#define EEPROM_HB_OFDM_COMMON ((0xCA)\ +#define EEPROM_HB_OFDM_COMMON ((0xC8)\ | INDIRECT_ADDRESS | INDIRECT_REGULATORY) /* 24 bytes */ /* 2.4GHz band channels: * 1Legacy, 1HT, 2Legacy, 2HT, 10Legacy, 10HT, 11Legacy, 11HT */ -#define EEPROM_LB_OFDM_20_BAND ((0xE2)\ +#define EEPROM_LB_OFDM_20_BAND ((0xE0)\ | INDIRECT_ADDRESS | INDIRECT_REGULATORY) /* 64 bytes */ /* 2.4 GHz band HT40 channels: (1,+1) (2,+1) (6,+1) (7,+1) (9,+1) */ -#define EEPROM_LB_OFDM_HT40_BAND ((0x122)\ +#define EEPROM_LB_OFDM_HT40_BAND ((0x120)\ | INDIRECT_ADDRESS | INDIRECT_REGULATORY) /* 40 bytes */ /* 5.2GHz band channels: 36Legacy, 36HT, 64Legacy, 64HT, 100Legacy, 100HT */ -#define EEPROM_HB_OFDM_20_BAND ((0x14A)\ +#define EEPROM_HB_OFDM_20_BAND ((0x148)\ | INDIRECT_ADDRESS | INDIRECT_REGULATORY) /* 48 bytes */ /* 5.2 GHz band HT40 channels: (36,+1) (60,+1) (100,+1) */ -#define EEPROM_HB_OFDM_HT40_BAND ((0x17A)\ +#define EEPROM_HB_OFDM_HT40_BAND ((0x178)\ | INDIRECT_ADDRESS | INDIRECT_REGULATORY) /* 24 bytes */ /* 2.4 GHz band, channnel 13: Legacy, HT */ -#define EEPROM_LB_OFDM_20_CHANNEL_13 ((0x192)\ +#define EEPROM_LB_OFDM_20_CHANNEL_13 ((0x190)\ | INDIRECT_ADDRESS | INDIRECT_REGULATORY) /* 16 bytes */ /* 5.2 GHz band, channnel 140: Legacy, HT */ -#define EEPROM_HB_OFDM_20_CHANNEL_140 ((0x1A2)\ +#define EEPROM_HB_OFDM_20_CHANNEL_140 ((0x1A0)\ | INDIRECT_ADDRESS | INDIRECT_REGULATORY) /* 16 bytes */ /* 5.2 GHz band, HT40 channnels (132,+1) (44,+1) */ -#define EEPROM_HB_OFDM_HT40_BAND_1 ((0x1B2)\ +#define EEPROM_HB_OFDM_HT40_BAND_1 ((0x1B0)\ | INDIRECT_ADDRESS | INDIRECT_REGULATORY) /* 16 bytes */ diff --git a/drivers/net/wireless/iwlwifi/iwl-hcmd.c b/drivers/net/wireless/iwlwifi/iwl-hcmd.c index 532c8d6cd8da..a6856daf14cb 100644 --- a/drivers/net/wireless/iwlwifi/iwl-hcmd.c +++ b/drivers/net/wireless/iwlwifi/iwl-hcmd.c @@ -28,6 +28,7 @@ #include <linux/kernel.h> #include <linux/module.h> +#include <linux/sched.h> #include <net/mac80211.h> #include "iwl-dev.h" /* FIXME: remove */ diff --git a/drivers/net/wireless/iwlwifi/iwl-rx.c b/drivers/net/wireless/iwlwifi/iwl-rx.c index b90adcb73b06..493626bcd3ec 100644 --- a/drivers/net/wireless/iwlwifi/iwl-rx.c +++ b/drivers/net/wireless/iwlwifi/iwl-rx.c @@ -250,12 +250,20 @@ void iwl_rx_allocate(struct iwl_priv *priv, gfp_t priority) } spin_unlock_irqrestore(&rxq->lock, flags); + if (rxq->free_count > RX_LOW_WATERMARK) + priority |= __GFP_NOWARN; /* Alloc a new receive buffer */ skb = alloc_skb(priv->hw_params.rx_buf_size + 256, priority); if (!skb) { - IWL_CRIT(priv, "Can not allocate SKB buffers\n"); + if (net_ratelimit()) + IWL_DEBUG_INFO(priv, "Failed to allocate SKB buffer.\n"); + if ((rxq->free_count <= RX_LOW_WATERMARK) && + net_ratelimit()) + IWL_CRIT(priv, "Failed to allocate SKB buffer with %s. Only %u free buffers remaining.\n", + priority == GFP_ATOMIC ? "GFP_ATOMIC" : "GFP_KERNEL", + rxq->free_count); /* We don't reschedule replenish work here -- we will * call the restock method and if it still needs * more buffers it will schedule replenish */ @@ -1036,7 +1044,7 @@ void iwl_rx_reply_rx(struct iwl_priv *priv, * as a bitmask. */ rx_status.antenna = - le16_to_cpu(phy_res->phy_flags & RX_RES_PHY_FLAGS_ANTENNA_MSK) + (le16_to_cpu(phy_res->phy_flags) & RX_RES_PHY_FLAGS_ANTENNA_MSK) >> RX_RES_PHY_FLAGS_ANTENNA_POS; /* set the preamble flag if appropriate */ diff --git a/drivers/net/wireless/iwlwifi/iwl-sta.c b/drivers/net/wireless/iwlwifi/iwl-sta.c index a2b9ec82b965..c6633fec8216 100644 --- a/drivers/net/wireless/iwlwifi/iwl-sta.c +++ b/drivers/net/wireless/iwlwifi/iwl-sta.c @@ -520,7 +520,7 @@ int iwl_send_static_wepkey_cmd(struct iwl_priv *priv, u8 send_if_empty) struct iwl_host_cmd cmd = { .id = REPLY_WEPKEY, .data = wep_cmd, - .flags = CMD_SYNC, + .flags = CMD_ASYNC, }; memset(wep_cmd, 0, cmd_size + diff --git a/drivers/net/wireless/iwlwifi/iwl-tx.c b/drivers/net/wireless/iwlwifi/iwl-tx.c index a7422e52d883..fb9bcfa6d947 100644 --- a/drivers/net/wireless/iwlwifi/iwl-tx.c +++ b/drivers/net/wireless/iwlwifi/iwl-tx.c @@ -28,6 +28,7 @@ *****************************************************************************/ #include <linux/etherdevice.h> +#include <linux/sched.h> #include <net/mac80211.h> #include "iwl-eeprom.h" #include "iwl-dev.h" @@ -197,6 +198,12 @@ void iwl_cmd_queue_free(struct iwl_priv *priv) pci_free_consistent(dev, priv->hw_params.tfd_size * txq->q.n_bd, txq->tfds, txq->q.dma_addr); + /* deallocate arrays */ + kfree(txq->cmd); + kfree(txq->meta); + txq->cmd = NULL; + txq->meta = NULL; + /* 0-fill queue descriptor structure */ memset(txq, 0, sizeof(*txq)); } diff --git a/drivers/net/wireless/iwlwifi/iwl3945-base.c b/drivers/net/wireless/iwlwifi/iwl3945-base.c index 090966837f3c..d00a80334095 100644 --- a/drivers/net/wireless/iwlwifi/iwl3945-base.c +++ b/drivers/net/wireless/iwlwifi/iwl3945-base.c @@ -33,6 +33,7 @@ #include <linux/pci.h> #include <linux/dma-mapping.h> #include <linux/delay.h> +#include <linux/sched.h> #include <linux/skbuff.h> #include <linux/netdevice.h> #include <linux/wireless.h> @@ -1146,11 +1147,18 @@ static void iwl3945_rx_allocate(struct iwl_priv *priv, gfp_t priority) } spin_unlock_irqrestore(&rxq->lock, flags); + if (rxq->free_count > RX_LOW_WATERMARK) + priority |= __GFP_NOWARN; /* Alloc a new receive buffer */ skb = alloc_skb(priv->hw_params.rx_buf_size, priority); if (!skb) { if (net_ratelimit()) - IWL_CRIT(priv, ": Can not allocate SKB buffers\n"); + IWL_DEBUG_INFO(priv, "Failed to allocate SKB buffer.\n"); + if ((rxq->free_count <= RX_LOW_WATERMARK) && + net_ratelimit()) + IWL_CRIT(priv, "Failed to allocate SKB buffer with %s. Only %u free buffers remaining.\n", + priority == GFP_ATOMIC ? "GFP_ATOMIC" : "GFP_KERNEL", + rxq->free_count); /* We don't reschedule replenish work here -- we will * call the restock method and if it still needs * more buffers it will schedule replenish */ @@ -1474,6 +1482,7 @@ static inline void iwl_synchronize_irq(struct iwl_priv *priv) tasklet_kill(&priv->irq_tasklet); } +#ifdef CONFIG_IWLWIFI_DEBUG static const char *desc_lookup(int i) { switch (i) { @@ -1497,7 +1506,7 @@ static const char *desc_lookup(int i) #define ERROR_START_OFFSET (1 * sizeof(u32)) #define ERROR_ELEM_SIZE (7 * sizeof(u32)) -static void iwl3945_dump_nic_error_log(struct iwl_priv *priv) +void iwl3945_dump_nic_error_log(struct iwl_priv *priv) { u32 i; u32 desc, time, count, base, data1; @@ -1591,7 +1600,7 @@ static void iwl3945_print_event_log(struct iwl_priv *priv, u32 start_idx, } } -static void iwl3945_dump_nic_event_log(struct iwl_priv *priv) +void iwl3945_dump_nic_event_log(struct iwl_priv *priv) { u32 base; /* SRAM byte address of event log header */ u32 capacity; /* event log capacity in # entries */ @@ -1633,6 +1642,16 @@ static void iwl3945_dump_nic_event_log(struct iwl_priv *priv) iwl3945_print_event_log(priv, 0, next_entry, mode); } +#else +void iwl3945_dump_nic_event_log(struct iwl_priv *priv) +{ +} + +void iwl3945_dump_nic_error_log(struct iwl_priv *priv) +{ +} + +#endif static void iwl3945_irq_tasklet(struct iwl_priv *priv) { @@ -3676,21 +3695,6 @@ static ssize_t dump_error_log(struct device *d, static DEVICE_ATTR(dump_errors, S_IWUSR, NULL, dump_error_log); -static ssize_t dump_event_log(struct device *d, - struct device_attribute *attr, - const char *buf, size_t count) -{ - struct iwl_priv *priv = dev_get_drvdata(d); - char *p = (char *)buf; - - if (p[0] == '1') - iwl3945_dump_nic_event_log(priv); - - return strnlen(buf, count); -} - -static DEVICE_ATTR(dump_events, S_IWUSR, NULL, dump_event_log); - /***************************************************************************** * * driver setup and tear down @@ -3735,7 +3739,6 @@ static struct attribute *iwl3945_sysfs_entries[] = { &dev_attr_antenna.attr, &dev_attr_channels.attr, &dev_attr_dump_errors.attr, - &dev_attr_dump_events.attr, &dev_attr_flags.attr, &dev_attr_filter_flags.attr, #ifdef CONFIG_IWL3945_SPECTRUM_MEASUREMENT @@ -4094,8 +4097,8 @@ static int iwl3945_pci_probe(struct pci_dev *pdev, const struct pci_device_id *e pci_set_drvdata(pdev, NULL); pci_disable_device(pdev); out_ieee80211_free_hw: - ieee80211_free_hw(priv->hw); iwl_free_traffic_mem(priv); + ieee80211_free_hw(priv->hw); out: return err; } diff --git a/drivers/net/wireless/iwmc3200wifi/cfg80211.c b/drivers/net/wireless/iwmc3200wifi/cfg80211.c index a56a2b0ac99a..f3c55658225b 100644 --- a/drivers/net/wireless/iwmc3200wifi/cfg80211.c +++ b/drivers/net/wireless/iwmc3200wifi/cfg80211.c @@ -23,6 +23,7 @@ #include <linux/kernel.h> #include <linux/netdevice.h> +#include <linux/sched.h> #include <linux/etherdevice.h> #include <linux/wireless.h> #include <linux/ieee80211.h> diff --git a/drivers/net/wireless/iwmc3200wifi/commands.c b/drivers/net/wireless/iwmc3200wifi/commands.c index 23b52fa2605f..84158b6d35d8 100644 --- a/drivers/net/wireless/iwmc3200wifi/commands.c +++ b/drivers/net/wireless/iwmc3200wifi/commands.c @@ -40,6 +40,7 @@ #include <linux/wireless.h> #include <linux/etherdevice.h> #include <linux/ieee80211.h> +#include <linux/sched.h> #include "iwm.h" #include "bus.h" diff --git a/drivers/net/wireless/iwmc3200wifi/main.c b/drivers/net/wireless/iwmc3200wifi/main.c index d668e4756324..222eb2cf1b30 100644 --- a/drivers/net/wireless/iwmc3200wifi/main.c +++ b/drivers/net/wireless/iwmc3200wifi/main.c @@ -38,6 +38,7 @@ #include <linux/kernel.h> #include <linux/netdevice.h> +#include <linux/sched.h> #include <linux/ieee80211.h> #include <linux/wireless.h> diff --git a/drivers/net/wireless/iwmc3200wifi/rx.c b/drivers/net/wireless/iwmc3200wifi/rx.c index 40dbcbc16593..771a301003c9 100644 --- a/drivers/net/wireless/iwmc3200wifi/rx.c +++ b/drivers/net/wireless/iwmc3200wifi/rx.c @@ -38,6 +38,7 @@ #include <linux/kernel.h> #include <linux/netdevice.h> +#include <linux/sched.h> #include <linux/etherdevice.h> #include <linux/wireless.h> #include <linux/ieee80211.h> diff --git a/drivers/net/wireless/libertas/cmd.c b/drivers/net/wireless/libertas/cmd.c index 685098148e10..0a324dcd264c 100644 --- a/drivers/net/wireless/libertas/cmd.c +++ b/drivers/net/wireless/libertas/cmd.c @@ -6,6 +6,7 @@ #include <net/iw_handler.h> #include <net/lib80211.h> #include <linux/kfifo.h> +#include <linux/sched.h> #include "host.h" #include "hostcmd.h" #include "decl.h" diff --git a/drivers/net/wireless/libertas/cmdresp.c b/drivers/net/wireless/libertas/cmdresp.c index c42d3faa2660..23f684337fdd 100644 --- a/drivers/net/wireless/libertas/cmdresp.c +++ b/drivers/net/wireless/libertas/cmdresp.c @@ -3,6 +3,7 @@ * responses as well as events generated by firmware. */ #include <linux/delay.h> +#include <linux/sched.h> #include <linux/if_arp.h> #include <linux/netdevice.h> #include <asm/unaligned.h> diff --git a/drivers/net/wireless/libertas/ethtool.c b/drivers/net/wireless/libertas/ethtool.c index 039b555e4d76..53d56ab83c03 100644 --- a/drivers/net/wireless/libertas/ethtool.c +++ b/drivers/net/wireless/libertas/ethtool.c @@ -169,16 +169,19 @@ static int lbs_ethtool_set_wol(struct net_device *dev, struct lbs_private *priv = dev->ml_priv; uint32_t criteria = 0; - if (priv->wol_criteria == 0xffffffff && wol->wolopts) - return -EOPNOTSUPP; - if (wol->wolopts & ~(WAKE_UCAST|WAKE_MCAST|WAKE_BCAST|WAKE_PHY)) return -EOPNOTSUPP; - if (wol->wolopts & WAKE_UCAST) criteria |= EHS_WAKE_ON_UNICAST_DATA; - if (wol->wolopts & WAKE_MCAST) criteria |= EHS_WAKE_ON_MULTICAST_DATA; - if (wol->wolopts & WAKE_BCAST) criteria |= EHS_WAKE_ON_BROADCAST_DATA; - if (wol->wolopts & WAKE_PHY) criteria |= EHS_WAKE_ON_MAC_EVENT; + if (wol->wolopts & WAKE_UCAST) + criteria |= EHS_WAKE_ON_UNICAST_DATA; + if (wol->wolopts & WAKE_MCAST) + criteria |= EHS_WAKE_ON_MULTICAST_DATA; + if (wol->wolopts & WAKE_BCAST) + criteria |= EHS_WAKE_ON_BROADCAST_DATA; + if (wol->wolopts & WAKE_PHY) + criteria |= EHS_WAKE_ON_MAC_EVENT; + if (wol->wolopts == 0) + criteria |= EHS_REMOVE_WAKEUP; return lbs_host_sleep_cfg(priv, criteria, (struct wol_config *)NULL); } diff --git a/drivers/net/wireless/libertas/if_spi.c b/drivers/net/wireless/libertas/if_spi.c index 446e327180f8..5b3672c4d0cc 100644 --- a/drivers/net/wireless/libertas/if_spi.c +++ b/drivers/net/wireless/libertas/if_spi.c @@ -134,7 +134,7 @@ static void spu_transaction_finish(struct if_spi_card *card) static int spu_write(struct if_spi_card *card, u16 reg, const u8 *buf, int len) { int err = 0; - u16 reg_out = cpu_to_le16(reg | IF_SPI_WRITE_OPERATION_MASK); + __le16 reg_out = cpu_to_le16(reg | IF_SPI_WRITE_OPERATION_MASK); struct spi_message m; struct spi_transfer reg_trans; struct spi_transfer data_trans; @@ -166,7 +166,7 @@ static int spu_write(struct if_spi_card *card, u16 reg, const u8 *buf, int len) static inline int spu_write_u16(struct if_spi_card *card, u16 reg, u16 val) { - u16 buff; + __le16 buff; buff = cpu_to_le16(val); return spu_write(card, reg, (u8 *)&buff, sizeof(u16)); @@ -188,7 +188,7 @@ static int spu_read(struct if_spi_card *card, u16 reg, u8 *buf, int len) { unsigned int delay; int err = 0; - u16 reg_out = cpu_to_le16(reg | IF_SPI_READ_OPERATION_MASK); + __le16 reg_out = cpu_to_le16(reg | IF_SPI_READ_OPERATION_MASK); struct spi_message m; struct spi_transfer reg_trans; struct spi_transfer dummy_trans; @@ -235,7 +235,7 @@ static int spu_read(struct if_spi_card *card, u16 reg, u8 *buf, int len) /* Read 16 bits from an SPI register */ static inline int spu_read_u16(struct if_spi_card *card, u16 reg, u16 *val) { - u16 buf; + __le16 buf; int ret; ret = spu_read(card, reg, (u8 *)&buf, sizeof(buf)); @@ -248,7 +248,7 @@ static inline int spu_read_u16(struct if_spi_card *card, u16 reg, u16 *val) * The low 16 bits are read first. */ static int spu_read_u32(struct if_spi_card *card, u16 reg, u32 *val) { - u32 buf; + __le32 buf; int err; err = spu_read(card, reg, (u8 *)&buf, sizeof(buf)); @@ -1222,3 +1222,4 @@ MODULE_DESCRIPTION("Libertas SPI WLAN Driver"); MODULE_AUTHOR("Andrey Yurovsky <andrey@cozybit.com>, " "Colin McCabe <colin@cozybit.com>"); MODULE_LICENSE("GPL"); +MODULE_ALIAS("spi:libertas_spi"); diff --git a/drivers/net/wireless/libertas/if_usb.c b/drivers/net/wireless/libertas/if_usb.c index 92bc8c5f1ca2..3fac4efa5ac8 100644 --- a/drivers/net/wireless/libertas/if_usb.c +++ b/drivers/net/wireless/libertas/if_usb.c @@ -508,7 +508,7 @@ static int __if_usb_submit_rx_urb(struct if_usb_card *cardp, /* Fill the receive configuration URB and initialise the Rx call back */ usb_fill_bulk_urb(cardp->rx_urb, cardp->udev, usb_rcvbulkpipe(cardp->udev, cardp->ep_in), - (void *) (skb->tail + (size_t) IPFIELD_ALIGN_OFFSET), + skb->data + IPFIELD_ALIGN_OFFSET, MRVDRV_ETH_RX_PACKET_BUFFER_SIZE, callbackfn, cardp); diff --git a/drivers/net/wireless/libertas/tx.c b/drivers/net/wireless/libertas/tx.c index 4c018f7a0a8d..8c3766a6e8e7 100644 --- a/drivers/net/wireless/libertas/tx.c +++ b/drivers/net/wireless/libertas/tx.c @@ -3,6 +3,7 @@ */ #include <linux/netdevice.h> #include <linux/etherdevice.h> +#include <linux/sched.h> #include "hostcmd.h" #include "radiotap.h" diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c index 896f532182f0..38cfd79e0590 100644 --- a/drivers/net/wireless/mac80211_hwsim.c +++ b/drivers/net/wireless/mac80211_hwsim.c @@ -631,6 +631,9 @@ static void mac80211_hwsim_bss_info_changed(struct ieee80211_hw *hw, data->beacon_int = 1024 * info->beacon_int / 1000 * HZ / 1000; if (WARN_ON(!data->beacon_int)) data->beacon_int = 1; + if (data->started) + mod_timer(&data->beacon_timer, + jiffies + data->beacon_int); } if (changed & BSS_CHANGED_ERP_CTS_PROT) { diff --git a/drivers/net/wireless/p54/p54spi.c b/drivers/net/wireless/p54/p54spi.c index 05458d9249ce..afd26bf06649 100644 --- a/drivers/net/wireless/p54/p54spi.c +++ b/drivers/net/wireless/p54/p54spi.c @@ -731,3 +731,4 @@ module_exit(p54spi_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Christian Lamparter <chunkeey@web.de>"); +MODULE_ALIAS("spi:cx3110x"); diff --git a/drivers/net/wireless/p54/p54usb.c b/drivers/net/wireless/p54/p54usb.c index 17e199546eeb..92af9b96bb7a 100644 --- a/drivers/net/wireless/p54/p54usb.c +++ b/drivers/net/wireless/p54/p54usb.c @@ -426,12 +426,16 @@ static const char p54u_romboot_3887[] = "~~~~"; static int p54u_firmware_reset_3887(struct ieee80211_hw *dev) { struct p54u_priv *priv = dev->priv; - u8 buf[4]; + u8 *buf; int ret; - memcpy(&buf, p54u_romboot_3887, sizeof(buf)); + buf = kmalloc(4, GFP_KERNEL); + if (!buf) + return -ENOMEM; + memcpy(buf, p54u_romboot_3887, 4); ret = p54u_bulk_msg(priv, P54U_PIPE_DATA, - buf, sizeof(buf)); + buf, 4); + kfree(buf); if (ret) dev_err(&priv->udev->dev, "(p54usb) unable to jump to " "boot ROM (%d)!\n", ret); diff --git a/drivers/net/wireless/prism54/isl_ioctl.c b/drivers/net/wireless/prism54/isl_ioctl.c index 4c97c6ad6f5d..bc08464d8323 100644 --- a/drivers/net/wireless/prism54/isl_ioctl.c +++ b/drivers/net/wireless/prism54/isl_ioctl.c @@ -19,6 +19,7 @@ * */ +#include <linux/capability.h> #include <linux/module.h> #include <linux/kernel.h> #include <linux/if_arp.h> diff --git a/drivers/net/wireless/prism54/islpci_dev.c b/drivers/net/wireless/prism54/islpci_dev.c index e26d7b3ceab5..2505be56ae39 100644 --- a/drivers/net/wireless/prism54/islpci_dev.c +++ b/drivers/net/wireless/prism54/islpci_dev.c @@ -23,6 +23,7 @@ #include <linux/netdevice.h> #include <linux/ethtool.h> #include <linux/pci.h> +#include <linux/sched.h> #include <linux/etherdevice.h> #include <linux/delay.h> #include <linux/if_arp.h> diff --git a/drivers/net/wireless/prism54/islpci_mgt.c b/drivers/net/wireless/prism54/islpci_mgt.c index f7c677e2094d..69d2f882fd06 100644 --- a/drivers/net/wireless/prism54/islpci_mgt.c +++ b/drivers/net/wireless/prism54/islpci_mgt.c @@ -20,6 +20,7 @@ #include <linux/netdevice.h> #include <linux/module.h> #include <linux/pci.h> +#include <linux/sched.h> #include <asm/io.h> #include <asm/system.h> diff --git a/drivers/net/wireless/ray_cs.c b/drivers/net/wireless/ray_cs.c index 88cd58eb3b9f..1c88c2ea59aa 100644 --- a/drivers/net/wireless/ray_cs.c +++ b/drivers/net/wireless/ray_cs.c @@ -2879,7 +2879,7 @@ static int write_essid(struct file *file, const char __user *buffer, unsigned long count, void *data) { static char proc_essid[33]; - int len = count; + unsigned int len = count; if (len > 32) len = 32; diff --git a/drivers/net/wireless/rt2x00/rt2800usb.c b/drivers/net/wireless/rt2x00/rt2800usb.c index a084077a1c61..9fe770f7d7bb 100644 --- a/drivers/net/wireless/rt2x00/rt2800usb.c +++ b/drivers/net/wireless/rt2x00/rt2800usb.c @@ -1994,7 +1994,7 @@ static void rt2800usb_write_tx_desc(struct rt2x00_dev *rt2x00dev, rt2x00_set_field32(&word, TXWI_W1_BW_WIN_SIZE, txdesc->ba_size); rt2x00_set_field32(&word, TXWI_W1_WIRELESS_CLI_ID, test_bit(ENTRY_TXD_ENCRYPT, &txdesc->flags) ? - (skbdesc->entry->entry_idx + 1) : 0xff); + txdesc->key_idx : 0xff); rt2x00_set_field32(&word, TXWI_W1_MPDU_TOTAL_BYTE_COUNT, skb->len - txdesc->l2pad); rt2x00_set_field32(&word, TXWI_W1_PACKETID, diff --git a/drivers/net/wireless/rt2x00/rt2x00debug.c b/drivers/net/wireless/rt2x00/rt2x00debug.c index 7b3ee8c2eaef..68bc9bb1dbf9 100644 --- a/drivers/net/wireless/rt2x00/rt2x00debug.c +++ b/drivers/net/wireless/rt2x00/rt2x00debug.c @@ -27,6 +27,7 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/poll.h> +#include <linux/sched.h> #include <linux/uaccess.h> #include "rt2x00.h" diff --git a/drivers/net/wireless/rt2x00/rt2x00dev.c b/drivers/net/wireless/rt2x00/rt2x00dev.c index 71761b343839..73bbec58341e 100644 --- a/drivers/net/wireless/rt2x00/rt2x00dev.c +++ b/drivers/net/wireless/rt2x00/rt2x00dev.c @@ -815,6 +815,8 @@ int rt2x00lib_probe_dev(struct rt2x00_dev *rt2x00dev) mutex_init(&rt2x00dev->csr_mutex); + set_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags); + /* * Make room for rt2x00_intf inside the per-interface * structure ieee80211_vif. @@ -871,8 +873,6 @@ int rt2x00lib_probe_dev(struct rt2x00_dev *rt2x00dev) rt2x00leds_register(rt2x00dev); rt2x00debug_register(rt2x00dev); - set_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags); - return 0; exit: diff --git a/drivers/net/wireless/rt2x00/rt2x00lib.h b/drivers/net/wireless/rt2x00/rt2x00lib.h index 5462cb5ad994..567f029a8cda 100644 --- a/drivers/net/wireless/rt2x00/rt2x00lib.h +++ b/drivers/net/wireless/rt2x00/rt2x00lib.h @@ -380,7 +380,7 @@ static inline void rt2x00crypto_tx_insert_iv(struct sk_buff *skb, { } -static inline void rt2x00crypto_rx_insert_iv(struct sk_buff *skb, bool l2pad, +static inline void rt2x00crypto_rx_insert_iv(struct sk_buff *skb, unsigned int header_length, struct rxdone_entry_desc *rxdesc) { diff --git a/drivers/net/wireless/rt2x00/rt2x00link.c b/drivers/net/wireless/rt2x00/rt2x00link.c index c64db0ba7f40..c708d0be9155 100644 --- a/drivers/net/wireless/rt2x00/rt2x00link.c +++ b/drivers/net/wireless/rt2x00/rt2x00link.c @@ -362,8 +362,9 @@ void rt2x00link_start_tuner(struct rt2x00_dev *rt2x00dev) rt2x00link_reset_tuner(rt2x00dev, false); - ieee80211_queue_delayed_work(rt2x00dev->hw, - &link->work, LINK_TUNE_INTERVAL); + if (test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags)) + ieee80211_queue_delayed_work(rt2x00dev->hw, + &link->work, LINK_TUNE_INTERVAL); } void rt2x00link_stop_tuner(struct rt2x00_dev *rt2x00dev) @@ -469,8 +470,10 @@ static void rt2x00link_tuner(struct work_struct *work) * Increase tuner counter, and reschedule the next link tuner run. */ link->count++; - ieee80211_queue_delayed_work(rt2x00dev->hw, - &link->work, LINK_TUNE_INTERVAL); + + if (test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags)) + ieee80211_queue_delayed_work(rt2x00dev->hw, + &link->work, LINK_TUNE_INTERVAL); } void rt2x00link_register(struct rt2x00_dev *rt2x00dev) diff --git a/drivers/net/wireless/rt2x00/rt2x00usb.c b/drivers/net/wireless/rt2x00/rt2x00usb.c index 501544882c2c..f02b48a90593 100644 --- a/drivers/net/wireless/rt2x00/rt2x00usb.c +++ b/drivers/net/wireless/rt2x00/rt2x00usb.c @@ -47,6 +47,8 @@ int rt2x00usb_vendor_request(struct rt2x00_dev *rt2x00dev, (requesttype == USB_VENDOR_REQUEST_IN) ? usb_rcvctrlpipe(usb_dev, 0) : usb_sndctrlpipe(usb_dev, 0); + if (!test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags)) + return -ENODEV; for (i = 0; i < REGISTER_BUSY_COUNT; i++) { status = usb_control_msg(usb_dev, pipe, request, requesttype, @@ -60,8 +62,10 @@ int rt2x00usb_vendor_request(struct rt2x00_dev *rt2x00dev, * -ENODEV: Device has disappeared, no point continuing. * All other errors: Try again. */ - else if (status == -ENODEV) + else if (status == -ENODEV) { + clear_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags); break; + } } ERROR(rt2x00dev, @@ -161,6 +165,9 @@ int rt2x00usb_regbusy_read(struct rt2x00_dev *rt2x00dev, { unsigned int i; + if (!test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags)) + return -ENODEV; + for (i = 0; i < REGISTER_BUSY_COUNT; i++) { rt2x00usb_register_read_lock(rt2x00dev, offset, reg); if (!rt2x00_get_field32(*reg, field)) diff --git a/drivers/net/wireless/rt2x00/rt73usb.c b/drivers/net/wireless/rt2x00/rt73usb.c index 1cbd9b4a3efc..14e7bb210075 100644 --- a/drivers/net/wireless/rt2x00/rt73usb.c +++ b/drivers/net/wireless/rt2x00/rt73usb.c @@ -2381,6 +2381,7 @@ static struct usb_device_id rt73usb_device_table[] = { /* Huawei-3Com */ { USB_DEVICE(0x1472, 0x0009), USB_DEVICE_DATA(&rt73usb_ops) }, /* Hercules */ + { USB_DEVICE(0x06f8, 0xe002), USB_DEVICE_DATA(&rt73usb_ops) }, { USB_DEVICE(0x06f8, 0xe010), USB_DEVICE_DATA(&rt73usb_ops) }, { USB_DEVICE(0x06f8, 0xe020), USB_DEVICE_DATA(&rt73usb_ops) }, /* Linksys */ @@ -2388,10 +2389,13 @@ static struct usb_device_id rt73usb_device_table[] = { { USB_DEVICE(0x13b1, 0x0023), USB_DEVICE_DATA(&rt73usb_ops) }, { USB_DEVICE(0x13b1, 0x0028), USB_DEVICE_DATA(&rt73usb_ops) }, /* MSI */ + { USB_DEVICE(0x0db0, 0x4600), USB_DEVICE_DATA(&rt73usb_ops) }, { USB_DEVICE(0x0db0, 0x6877), USB_DEVICE_DATA(&rt73usb_ops) }, { USB_DEVICE(0x0db0, 0x6874), USB_DEVICE_DATA(&rt73usb_ops) }, { USB_DEVICE(0x0db0, 0xa861), USB_DEVICE_DATA(&rt73usb_ops) }, { USB_DEVICE(0x0db0, 0xa874), USB_DEVICE_DATA(&rt73usb_ops) }, + /* Ovislink */ + { USB_DEVICE(0x1b75, 0x7318), USB_DEVICE_DATA(&rt73usb_ops) }, /* Ralink */ { USB_DEVICE(0x04bb, 0x093d), USB_DEVICE_DATA(&rt73usb_ops) }, { USB_DEVICE(0x148f, 0x2573), USB_DEVICE_DATA(&rt73usb_ops) }, @@ -2419,6 +2423,8 @@ static struct usb_device_id rt73usb_device_table[] = { /* Planex */ { USB_DEVICE(0x2019, 0xab01), USB_DEVICE_DATA(&rt73usb_ops) }, { USB_DEVICE(0x2019, 0xab50), USB_DEVICE_DATA(&rt73usb_ops) }, + /* WideTell */ + { USB_DEVICE(0x7167, 0x3840), USB_DEVICE_DATA(&rt73usb_ops) }, /* Zcom */ { USB_DEVICE(0x0cde, 0x001c), USB_DEVICE_DATA(&rt73usb_ops) }, /* ZyXEL */ diff --git a/drivers/net/wireless/rtl818x/rtl8187_leds.c b/drivers/net/wireless/rtl818x/rtl8187_leds.c index a1c670fc1552..cf8a4a40fdf6 100644 --- a/drivers/net/wireless/rtl818x/rtl8187_leds.c +++ b/drivers/net/wireless/rtl818x/rtl8187_leds.c @@ -210,10 +210,10 @@ void rtl8187_leds_exit(struct ieee80211_hw *dev) /* turn the LED off before exiting */ ieee80211_queue_delayed_work(dev, &priv->led_off, 0); - cancel_delayed_work_sync(&priv->led_off); - cancel_delayed_work_sync(&priv->led_on); rtl8187_unregister_led(&priv->led_rx); rtl8187_unregister_led(&priv->led_tx); + cancel_delayed_work_sync(&priv->led_off); + cancel_delayed_work_sync(&priv->led_on); } #endif /* def CONFIG_RTL8187_LED */ diff --git a/drivers/net/wireless/rtl818x/rtl8187_rfkill.c b/drivers/net/wireless/rtl818x/rtl8187_rfkill.c index 9fab13e4004e..cad8037ab2af 100644 --- a/drivers/net/wireless/rtl818x/rtl8187_rfkill.c +++ b/drivers/net/wireless/rtl818x/rtl8187_rfkill.c @@ -18,6 +18,7 @@ #include <net/mac80211.h> #include "rtl8187.h" +#include "rtl8187_rfkill.h" static bool rtl8187_is_radio_enabled(struct rtl8187_priv *priv) { diff --git a/drivers/net/wireless/wl12xx/Kconfig b/drivers/net/wireless/wl12xx/Kconfig index 7b14d5bc63d6..88060e117541 100644 --- a/drivers/net/wireless/wl12xx/Kconfig +++ b/drivers/net/wireless/wl12xx/Kconfig @@ -1,5 +1,5 @@ menuconfig WL12XX - boolean "TI wl12xx driver support" + tristate "TI wl12xx driver support" depends on MAC80211 && WLAN_80211 && EXPERIMENTAL ---help--- This will enable TI wl12xx driver support. The drivers make diff --git a/drivers/net/wireless/wl12xx/wl1251_main.c b/drivers/net/wireless/wl12xx/wl1251_main.c index 5809ef5b18f8..1103256ad989 100644 --- a/drivers/net/wireless/wl12xx/wl1251_main.c +++ b/drivers/net/wireless/wl12xx/wl1251_main.c @@ -1426,3 +1426,4 @@ EXPORT_SYMBOL_GPL(wl1251_free_hw); MODULE_DESCRIPTION("TI wl1251 Wireles LAN Driver Core"); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Kalle Valo <kalle.valo@nokia.com>"); +MODULE_ALIAS("spi:wl12xx"); diff --git a/drivers/net/wireless/zd1211rw/zd_usb.c b/drivers/net/wireless/zd1211rw/zd_usb.c index 38688847d568..23a6a6d4863b 100644 --- a/drivers/net/wireless/zd1211rw/zd_usb.c +++ b/drivers/net/wireless/zd1211rw/zd_usb.c @@ -1070,7 +1070,7 @@ static int eject_installer(struct usb_interface *intf) /* Find bulk out endpoint */ endpoint = &iface_desc->endpoint[1].desc; - if ((endpoint->bEndpointAddress & USB_TYPE_MASK) == USB_DIR_OUT && + if (usb_endpoint_dir_out(endpoint) && usb_endpoint_xfer_bulk(endpoint)) { bulk_out_ep = endpoint->bEndpointAddress; } else { diff --git a/drivers/net/xilinx_emaclite.c b/drivers/net/xilinx_emaclite.c index dc22782633a5..83a044dbd1d7 100644 --- a/drivers/net/xilinx_emaclite.c +++ b/drivers/net/xilinx_emaclite.c @@ -134,18 +134,15 @@ static void xemaclite_enable_interrupts(struct net_local *drvdata) } /* Enable the Rx interrupts for the first buffer */ - reg_data = in_be32(drvdata->base_addr + XEL_RSR_OFFSET); out_be32(drvdata->base_addr + XEL_RSR_OFFSET, - reg_data | XEL_RSR_RECV_IE_MASK); + XEL_RSR_RECV_IE_MASK); /* Enable the Rx interrupts for the second Buffer if * configured in HW */ if (drvdata->rx_ping_pong != 0) { - reg_data = in_be32(drvdata->base_addr + XEL_BUFFER_OFFSET + - XEL_RSR_OFFSET); out_be32(drvdata->base_addr + XEL_BUFFER_OFFSET + XEL_RSR_OFFSET, - reg_data | XEL_RSR_RECV_IE_MASK); + XEL_RSR_RECV_IE_MASK); } /* Enable the Global Interrupt Enable */ diff --git a/drivers/net/znet.c b/drivers/net/znet.c index a0384b6f09b6..b42347333750 100644 --- a/drivers/net/znet.c +++ b/drivers/net/znet.c @@ -169,7 +169,6 @@ static void znet_tx_timeout (struct net_device *dev); static int znet_request_resources (struct net_device *dev) { struct znet_private *znet = netdev_priv(dev); - unsigned long flags; if (request_irq (dev->irq, &znet_interrupt, 0, "ZNet", dev)) goto failed; @@ -187,13 +186,9 @@ static int znet_request_resources (struct net_device *dev) free_sia: release_region (znet->sia_base, znet->sia_size); free_tx_dma: - flags = claim_dma_lock(); free_dma (znet->tx_dma); - release_dma_lock (flags); free_rx_dma: - flags = claim_dma_lock(); free_dma (znet->rx_dma); - release_dma_lock (flags); free_irq: free_irq (dev->irq, dev); failed: @@ -203,14 +198,11 @@ static int znet_request_resources (struct net_device *dev) static void znet_release_resources (struct net_device *dev) { struct znet_private *znet = netdev_priv(dev); - unsigned long flags; release_region (znet->sia_base, znet->sia_size); release_region (dev->base_addr, znet->io_size); - flags = claim_dma_lock(); free_dma (znet->tx_dma); free_dma (znet->rx_dma); - release_dma_lock (flags); free_irq (dev->irq, dev); } diff --git a/drivers/of/base.c b/drivers/of/base.c index 69f85c07d17f..ddf224d456b2 100644 --- a/drivers/of/base.c +++ b/drivers/of/base.c @@ -447,7 +447,6 @@ struct of_modalias_table { static struct of_modalias_table of_modalias_table[] = { { "fsl,mcu-mpc8349emitx", "mcu-mpc8349emitx" }, { "mmc-spi-slot", "mmc_spi" }, - { "stm,m25p40", "m25p80" }, }; /** diff --git a/drivers/of/of_mdio.c b/drivers/of/of_mdio.c index bacaa536fd51..4b22ba568b19 100644 --- a/drivers/of/of_mdio.c +++ b/drivers/of/of_mdio.c @@ -97,6 +97,12 @@ int of_mdiobus_register(struct mii_bus *mdio, struct device_node *np) } EXPORT_SYMBOL(of_mdiobus_register); +/* Helper function for of_phy_find_device */ +static int of_phy_match(struct device *dev, void *phy_np) +{ + return dev_archdata_get_node(&dev->archdata) == phy_np; +} + /** * of_phy_find_device - Give a PHY node, find the phy_device * @phy_np: Pointer to the phy's device tree node @@ -106,15 +112,10 @@ EXPORT_SYMBOL(of_mdiobus_register); struct phy_device *of_phy_find_device(struct device_node *phy_np) { struct device *d; - int match(struct device *dev, void *phy_np) - { - return dev_archdata_get_node(&dev->archdata) == phy_np; - } - if (!phy_np) return NULL; - d = bus_find_device(&mdio_bus_type, NULL, phy_np, match); + d = bus_find_device(&mdio_bus_type, NULL, phy_np, of_phy_match); return d ? to_phy_device(d) : NULL; } EXPORT_SYMBOL(of_phy_find_device); diff --git a/drivers/oprofile/buffer_sync.c b/drivers/oprofile/buffer_sync.c index 8574622e36a5..c9e2ae90f195 100644 --- a/drivers/oprofile/buffer_sync.c +++ b/drivers/oprofile/buffer_sync.c @@ -154,9 +154,8 @@ int sync_start(void) { int err; - if (!alloc_cpumask_var(&marked_cpus, GFP_KERNEL)) + if (!zalloc_cpumask_var(&marked_cpus, GFP_KERNEL)) return -ENOMEM; - cpumask_clear(marked_cpus); start_cpu_work(); diff --git a/drivers/oprofile/event_buffer.c b/drivers/oprofile/event_buffer.c index 2b7ae366ceb1..5df60a6b6776 100644 --- a/drivers/oprofile/event_buffer.c +++ b/drivers/oprofile/event_buffer.c @@ -35,12 +35,23 @@ static size_t buffer_pos; /* atomic_t because wait_event checks it outside of buffer_mutex */ static atomic_t buffer_ready = ATOMIC_INIT(0); -/* Add an entry to the event buffer. When we - * get near to the end we wake up the process - * sleeping on the read() of the file. +/* + * Add an entry to the event buffer. When we get near to the end we + * wake up the process sleeping on the read() of the file. To protect + * the event_buffer this function may only be called when buffer_mutex + * is set. */ void add_event_entry(unsigned long value) { + /* + * This shouldn't happen since all workqueues or handlers are + * canceled or flushed before the event buffer is freed. + */ + if (!event_buffer) { + WARN_ON_ONCE(1); + return; + } + if (buffer_pos == buffer_size) { atomic_inc(&oprofile_stats.event_lost_overflow); return; @@ -69,7 +80,6 @@ void wake_up_buffer_waiter(void) int alloc_event_buffer(void) { - int err = -ENOMEM; unsigned long flags; spin_lock_irqsave(&oprofilefs_lock, flags); @@ -80,21 +90,22 @@ int alloc_event_buffer(void) if (buffer_watershed >= buffer_size) return -EINVAL; + buffer_pos = 0; event_buffer = vmalloc(sizeof(unsigned long) * buffer_size); if (!event_buffer) - goto out; + return -ENOMEM; - err = 0; -out: - return err; + return 0; } void free_event_buffer(void) { + mutex_lock(&buffer_mutex); vfree(event_buffer); - + buffer_pos = 0; event_buffer = NULL; + mutex_unlock(&buffer_mutex); } @@ -167,6 +178,12 @@ static ssize_t event_buffer_read(struct file *file, char __user *buf, mutex_lock(&buffer_mutex); + /* May happen if the buffer is freed during pending reads. */ + if (!event_buffer) { + retval = -EINTR; + goto out; + } + atomic_set(&buffer_ready, 0); retval = -EFAULT; diff --git a/drivers/parport/procfs.c b/drivers/parport/procfs.c index 554e11f9e1ce..8eefe56f1cbe 100644 --- a/drivers/parport/procfs.c +++ b/drivers/parport/procfs.c @@ -31,7 +31,7 @@ #define PARPORT_MIN_SPINTIME_VALUE 1 #define PARPORT_MAX_SPINTIME_VALUE 1000 -static int do_active_device(ctl_table *table, int write, struct file *filp, +static int do_active_device(ctl_table *table, int write, void __user *result, size_t *lenp, loff_t *ppos) { struct parport *port = (struct parport *)table->extra1; @@ -68,7 +68,7 @@ static int do_active_device(ctl_table *table, int write, struct file *filp, } #ifdef CONFIG_PARPORT_1284 -static int do_autoprobe(ctl_table *table, int write, struct file *filp, +static int do_autoprobe(ctl_table *table, int write, void __user *result, size_t *lenp, loff_t *ppos) { struct parport_device_info *info = table->extra2; @@ -111,7 +111,7 @@ static int do_autoprobe(ctl_table *table, int write, struct file *filp, #endif /* IEEE1284.3 support. */ static int do_hardware_base_addr (ctl_table *table, int write, - struct file *filp, void __user *result, + void __user *result, size_t *lenp, loff_t *ppos) { struct parport *port = (struct parport *)table->extra1; @@ -139,7 +139,7 @@ static int do_hardware_base_addr (ctl_table *table, int write, } static int do_hardware_irq (ctl_table *table, int write, - struct file *filp, void __user *result, + void __user *result, size_t *lenp, loff_t *ppos) { struct parport *port = (struct parport *)table->extra1; @@ -167,7 +167,7 @@ static int do_hardware_irq (ctl_table *table, int write, } static int do_hardware_dma (ctl_table *table, int write, - struct file *filp, void __user *result, + void __user *result, size_t *lenp, loff_t *ppos) { struct parport *port = (struct parport *)table->extra1; @@ -195,7 +195,7 @@ static int do_hardware_dma (ctl_table *table, int write, } static int do_hardware_modes (ctl_table *table, int write, - struct file *filp, void __user *result, + void __user *result, size_t *lenp, loff_t *ppos) { struct parport *port = (struct parport *)table->extra1; diff --git a/drivers/pci/dmar.c b/drivers/pci/dmar.c index ab99783dccec..b952ebc7a78b 100644 --- a/drivers/pci/dmar.c +++ b/drivers/pci/dmar.c @@ -34,9 +34,9 @@ #include <linux/irq.h> #include <linux/interrupt.h> #include <linux/tboot.h> +#include <linux/dmi.h> -#undef PREFIX -#define PREFIX "DMAR:" +#define PREFIX "DMAR: " /* No locks are needed as DMA remapping hardware unit * list is constructed at boot time and hotplug of @@ -175,15 +175,6 @@ dmar_parse_one_drhd(struct acpi_dmar_header *header) int ret = 0; drhd = (struct acpi_dmar_hardware_unit *)header; - if (!drhd->address) { - /* Promote an attitude of violence to a BIOS engineer today */ - WARN(1, "Your BIOS is broken; DMAR reported at address zero!\n" - "BIOS vendor: %s; Ver: %s; Product Version: %s\n", - dmi_get_system_info(DMI_BIOS_VENDOR), - dmi_get_system_info(DMI_BIOS_VERSION), - dmi_get_system_info(DMI_PRODUCT_VERSION)); - return -ENODEV; - } dmaru = kzalloc(sizeof(*dmaru), GFP_KERNEL); if (!dmaru) return -ENOMEM; @@ -354,6 +345,7 @@ dmar_table_print_dmar_entry(struct acpi_dmar_header *header) struct acpi_dmar_hardware_unit *drhd; struct acpi_dmar_reserved_memory *rmrr; struct acpi_dmar_atsr *atsr; + struct acpi_dmar_rhsa *rhsa; switch (header->type) { case ACPI_DMAR_TYPE_HARDWARE_UNIT: @@ -375,6 +367,12 @@ dmar_table_print_dmar_entry(struct acpi_dmar_header *header) atsr = container_of(header, struct acpi_dmar_atsr, header); printk(KERN_INFO PREFIX "ATSR flags: %#x\n", atsr->flags); break; + case ACPI_DMAR_HARDWARE_AFFINITY: + rhsa = container_of(header, struct acpi_dmar_rhsa, header); + printk(KERN_INFO PREFIX "RHSA base: %#016Lx proximity domain: %#x\n", + (unsigned long long)rhsa->base_address, + rhsa->proximity_domain); + break; } } @@ -459,9 +457,13 @@ parse_dmar_table(void) ret = dmar_parse_one_atsr(entry_header); #endif break; + case ACPI_DMAR_HARDWARE_AFFINITY: + /* We don't do anything with RHSA (yet?) */ + break; default: printk(KERN_WARNING PREFIX - "Unknown DMAR structure type\n"); + "Unknown DMAR structure type %d\n", + entry_header->type); ret = 0; /* for forward compatibility */ break; } @@ -577,18 +579,56 @@ int __init dmar_table_init(void) printk(KERN_INFO PREFIX "No ATSR found\n"); #endif -#ifdef CONFIG_INTR_REMAP - parse_ioapics_under_ir(); -#endif return 0; } +int __init check_zero_address(void) +{ + struct acpi_table_dmar *dmar; + struct acpi_dmar_header *entry_header; + struct acpi_dmar_hardware_unit *drhd; + + dmar = (struct acpi_table_dmar *)dmar_tbl; + entry_header = (struct acpi_dmar_header *)(dmar + 1); + + while (((unsigned long)entry_header) < + (((unsigned long)dmar) + dmar_tbl->length)) { + /* Avoid looping forever on bad ACPI tables */ + if (entry_header->length == 0) { + printk(KERN_WARNING PREFIX + "Invalid 0-length structure\n"); + return 0; + } + + if (entry_header->type == ACPI_DMAR_TYPE_HARDWARE_UNIT) { + drhd = (void *)entry_header; + if (!drhd->address) { + /* Promote an attitude of violence to a BIOS engineer today */ + WARN(1, "Your BIOS is broken; DMAR reported at address zero!\n" + "BIOS vendor: %s; Ver: %s; Product Version: %s\n", + dmi_get_system_info(DMI_BIOS_VENDOR), + dmi_get_system_info(DMI_BIOS_VERSION), + dmi_get_system_info(DMI_PRODUCT_VERSION)); +#ifdef CONFIG_DMAR + dmar_disabled = 1; +#endif + return 0; + } + break; + } + + entry_header = ((void *)entry_header + entry_header->length); + } + return 1; +} + void __init detect_intel_iommu(void) { int ret; ret = dmar_table_detect(); - + if (ret) + ret = check_zero_address(); { #ifdef CONFIG_INTR_REMAP struct acpi_table_dmar *dmar; @@ -639,20 +679,31 @@ int alloc_iommu(struct dmar_drhd_unit *drhd) iommu->cap = dmar_readq(iommu->reg + DMAR_CAP_REG); iommu->ecap = dmar_readq(iommu->reg + DMAR_ECAP_REG); + if (iommu->cap == (uint64_t)-1 && iommu->ecap == (uint64_t)-1) { + /* Promote an attitude of violence to a BIOS engineer today */ + WARN(1, "Your BIOS is broken; DMAR reported at address %llx returns all ones!\n" + "BIOS vendor: %s; Ver: %s; Product Version: %s\n", + drhd->reg_base_addr, + dmi_get_system_info(DMI_BIOS_VENDOR), + dmi_get_system_info(DMI_BIOS_VERSION), + dmi_get_system_info(DMI_PRODUCT_VERSION)); + goto err_unmap; + } + #ifdef CONFIG_DMAR agaw = iommu_calculate_agaw(iommu); if (agaw < 0) { printk(KERN_ERR "Cannot get a valid agaw for iommu (seq_id = %d)\n", iommu->seq_id); - goto error; + goto err_unmap; } msagaw = iommu_calculate_max_sagaw(iommu); if (msagaw < 0) { printk(KERN_ERR "Cannot get a valid max agaw for iommu (seq_id = %d)\n", iommu->seq_id); - goto error; + goto err_unmap; } #endif iommu->agaw = agaw; @@ -672,7 +723,7 @@ int alloc_iommu(struct dmar_drhd_unit *drhd) } ver = readl(iommu->reg + DMAR_VER_REG); - pr_debug("IOMMU %llx: ver %d:%d cap %llx ecap %llx\n", + pr_info("IOMMU %llx: ver %d:%d cap %llx ecap %llx\n", (unsigned long long)drhd->reg_base_addr, DMAR_VER_MAJOR(ver), DMAR_VER_MINOR(ver), (unsigned long long)iommu->cap, @@ -682,7 +733,10 @@ int alloc_iommu(struct dmar_drhd_unit *drhd) drhd->iommu = iommu; return 0; -error: + + err_unmap: + iounmap(iommu->reg); + error: kfree(iommu); return -1; } @@ -1219,7 +1273,7 @@ irqreturn_t dmar_fault(int irq, void *dev_id) source_id, guest_addr); fault_index++; - if (fault_index > cap_num_fault_regs(iommu->cap)) + if (fault_index >= cap_num_fault_regs(iommu->cap)) fault_index = 0; spin_lock_irqsave(&iommu->register_lock, flag); } @@ -1312,3 +1366,13 @@ int dmar_reenable_qi(struct intel_iommu *iommu) return 0; } + +/* + * Check interrupt remapping support in DMAR table description. + */ +int dmar_ir_support(void) +{ + struct acpi_table_dmar *dmar; + dmar = (struct acpi_table_dmar *)dmar_tbl; + return dmar->flags & 0x1; +} diff --git a/drivers/pci/hotplug/acpiphp_ibm.c b/drivers/pci/hotplug/acpiphp_ibm.c index 5befa7e379b7..e7be66dbac21 100644 --- a/drivers/pci/hotplug/acpiphp_ibm.c +++ b/drivers/pci/hotplug/acpiphp_ibm.c @@ -398,23 +398,20 @@ static acpi_status __init ibm_find_acpi_device(acpi_handle handle, acpi_handle *phandle = (acpi_handle *)context; acpi_status status; struct acpi_device_info *info; - struct acpi_buffer info_buffer = { ACPI_ALLOCATE_BUFFER, NULL }; int retval = 0; - status = acpi_get_object_info(handle, &info_buffer); + status = acpi_get_object_info(handle, &info); if (ACPI_FAILURE(status)) { err("%s: Failed to get device information status=0x%x\n", __func__, status); return retval; } - info = info_buffer.pointer; - info->hardware_id.value[sizeof(info->hardware_id.value) - 1] = '\0'; if (info->current_status && (info->valid & ACPI_VALID_HID) && - (!strcmp(info->hardware_id.value, IBM_HARDWARE_ID1) || - !strcmp(info->hardware_id.value, IBM_HARDWARE_ID2))) { + (!strcmp(info->hardware_id.string, IBM_HARDWARE_ID1) || + !strcmp(info->hardware_id.string, IBM_HARDWARE_ID2))) { dbg("found hardware: %s, handle: %p\n", - info->hardware_id.value, handle); + info->hardware_id.string, handle); *phandle = handle; /* returning non-zero causes the search to stop * and returns this value to the caller of diff --git a/drivers/pci/hotplug/cpqphp.h b/drivers/pci/hotplug/cpqphp.h index 53836001d511..9c6a9fd26812 100644 --- a/drivers/pci/hotplug/cpqphp.h +++ b/drivers/pci/hotplug/cpqphp.h @@ -32,6 +32,7 @@ #include <asm/io.h> /* for read? and write? functions */ #include <linux/delay.h> /* for delays */ #include <linux/mutex.h> +#include <linux/sched.h> /* for signal_pending() */ #define MY_NAME "cpqphp" diff --git a/drivers/pci/hotplug/pciehp.h b/drivers/pci/hotplug/pciehp.h index 36faa9a8e18f..3070f77eb56a 100644 --- a/drivers/pci/hotplug/pciehp.h +++ b/drivers/pci/hotplug/pciehp.h @@ -72,15 +72,9 @@ do { \ #define SLOT_NAME_SIZE 10 struct slot { - u8 bus; - u8 device; u8 state; - u8 hp_slot; - u32 number; struct controller *ctrl; - struct hpc_ops *hpc_ops; struct hotplug_slot *hotplug_slot; - struct list_head slot_list; struct delayed_work work; /* work for button event */ struct mutex lock; }; @@ -92,18 +86,10 @@ struct event_info { }; struct controller { - struct mutex crit_sect; /* critical section mutex */ struct mutex ctrl_lock; /* controller lock */ - int num_slots; /* Number of slots on ctlr */ - int slot_num_inc; /* 1 or -1 */ - struct pci_dev *pci_dev; struct pcie_device *pcie; /* PCI Express port service */ - struct list_head slot_list; - struct hpc_ops *hpc_ops; + struct slot *slot; wait_queue_head_t queue; /* sleep & wake process */ - u8 slot_device_offset; - u32 first_slot; /* First physical slot number */ /* PCIE only has 1 slot */ - u8 slot_bus; /* Bus where the slots handled by this controller sit */ u32 slot_cap; u8 cap_base; struct timer_list poll_timer; @@ -131,40 +117,20 @@ struct controller { #define POWERON_STATE 3 #define POWEROFF_STATE 4 -/* Error messages */ -#define INTERLOCK_OPEN 0x00000002 -#define ADD_NOT_SUPPORTED 0x00000003 -#define CARD_FUNCTIONING 0x00000005 -#define ADAPTER_NOT_SAME 0x00000006 -#define NO_ADAPTER_PRESENT 0x00000009 -#define NOT_ENOUGH_RESOURCES 0x0000000B -#define DEVICE_TYPE_NOT_SUPPORTED 0x0000000C -#define WRONG_BUS_FREQUENCY 0x0000000D -#define POWER_FAILURE 0x0000000E - -/* Field definitions in Slot Capabilities Register */ -#define ATTN_BUTTN_PRSN 0x00000001 -#define PWR_CTRL_PRSN 0x00000002 -#define MRL_SENS_PRSN 0x00000004 -#define ATTN_LED_PRSN 0x00000008 -#define PWR_LED_PRSN 0x00000010 -#define HP_SUPR_RM_SUP 0x00000020 -#define EMI_PRSN 0x00020000 -#define NO_CMD_CMPL_SUP 0x00040000 - -#define ATTN_BUTTN(ctrl) ((ctrl)->slot_cap & ATTN_BUTTN_PRSN) -#define POWER_CTRL(ctrl) ((ctrl)->slot_cap & PWR_CTRL_PRSN) -#define MRL_SENS(ctrl) ((ctrl)->slot_cap & MRL_SENS_PRSN) -#define ATTN_LED(ctrl) ((ctrl)->slot_cap & ATTN_LED_PRSN) -#define PWR_LED(ctrl) ((ctrl)->slot_cap & PWR_LED_PRSN) -#define HP_SUPR_RM(ctrl) ((ctrl)->slot_cap & HP_SUPR_RM_SUP) -#define EMI(ctrl) ((ctrl)->slot_cap & EMI_PRSN) -#define NO_CMD_CMPL(ctrl) ((ctrl)->slot_cap & NO_CMD_CMPL_SUP) +#define ATTN_BUTTN(ctrl) ((ctrl)->slot_cap & PCI_EXP_SLTCAP_ABP) +#define POWER_CTRL(ctrl) ((ctrl)->slot_cap & PCI_EXP_SLTCAP_PCP) +#define MRL_SENS(ctrl) ((ctrl)->slot_cap & PCI_EXP_SLTCAP_MRLSP) +#define ATTN_LED(ctrl) ((ctrl)->slot_cap & PCI_EXP_SLTCAP_AIP) +#define PWR_LED(ctrl) ((ctrl)->slot_cap & PCI_EXP_SLTCAP_PIP) +#define HP_SUPR_RM(ctrl) ((ctrl)->slot_cap & PCI_EXP_SLTCAP_HPS) +#define EMI(ctrl) ((ctrl)->slot_cap & PCI_EXP_SLTCAP_EIP) +#define NO_CMD_CMPL(ctrl) ((ctrl)->slot_cap & PCI_EXP_SLTCAP_NCCS) +#define PSN(ctrl) ((ctrl)->slot_cap >> 19) extern int pciehp_sysfs_enable_slot(struct slot *slot); extern int pciehp_sysfs_disable_slot(struct slot *slot); extern u8 pciehp_handle_attention_button(struct slot *p_slot); - extern u8 pciehp_handle_switch_change(struct slot *p_slot); +extern u8 pciehp_handle_switch_change(struct slot *p_slot); extern u8 pciehp_handle_presence_change(struct slot *p_slot); extern u8 pciehp_handle_power_fault(struct slot *p_slot); extern int pciehp_configure_device(struct slot *p_slot); @@ -175,45 +141,30 @@ int pcie_init_notification(struct controller *ctrl); int pciehp_enable_slot(struct slot *p_slot); int pciehp_disable_slot(struct slot *p_slot); int pcie_enable_notification(struct controller *ctrl); +int pciehp_power_on_slot(struct slot *slot); +int pciehp_power_off_slot(struct slot *slot); +int pciehp_get_power_status(struct slot *slot, u8 *status); +int pciehp_get_attention_status(struct slot *slot, u8 *status); + +int pciehp_set_attention_status(struct slot *slot, u8 status); +int pciehp_get_latch_status(struct slot *slot, u8 *status); +int pciehp_get_adapter_status(struct slot *slot, u8 *status); +int pciehp_get_max_link_speed(struct slot *slot, enum pci_bus_speed *speed); +int pciehp_get_max_link_width(struct slot *slot, enum pcie_link_width *val); +int pciehp_get_cur_link_speed(struct slot *slot, enum pci_bus_speed *speed); +int pciehp_get_cur_link_width(struct slot *slot, enum pcie_link_width *val); +int pciehp_query_power_fault(struct slot *slot); +void pciehp_green_led_on(struct slot *slot); +void pciehp_green_led_off(struct slot *slot); +void pciehp_green_led_blink(struct slot *slot); +int pciehp_check_link_status(struct controller *ctrl); +void pciehp_release_ctrl(struct controller *ctrl); static inline const char *slot_name(struct slot *slot) { return hotplug_slot_name(slot->hotplug_slot); } -static inline struct slot *pciehp_find_slot(struct controller *ctrl, u8 device) -{ - struct slot *slot; - - list_for_each_entry(slot, &ctrl->slot_list, slot_list) { - if (slot->device == device) - return slot; - } - - ctrl_err(ctrl, "Slot (device=0x%02x) not found\n", device); - return NULL; -} - -struct hpc_ops { - int (*power_on_slot)(struct slot *slot); - int (*power_off_slot)(struct slot *slot); - int (*get_power_status)(struct slot *slot, u8 *status); - int (*get_attention_status)(struct slot *slot, u8 *status); - int (*set_attention_status)(struct slot *slot, u8 status); - int (*get_latch_status)(struct slot *slot, u8 *status); - int (*get_adapter_status)(struct slot *slot, u8 *status); - int (*get_max_bus_speed)(struct slot *slot, enum pci_bus_speed *speed); - int (*get_cur_bus_speed)(struct slot *slot, enum pci_bus_speed *speed); - int (*get_max_lnk_width)(struct slot *slot, enum pcie_link_width *val); - int (*get_cur_lnk_width)(struct slot *slot, enum pcie_link_width *val); - int (*query_power_fault)(struct slot *slot); - void (*green_led_on)(struct slot *slot); - void (*green_led_off)(struct slot *slot); - void (*green_led_blink)(struct slot *slot); - void (*release_ctlr)(struct controller *ctrl); - int (*check_lnk_status)(struct controller *ctrl); -}; - #ifdef CONFIG_ACPI #include <acpi/acpi.h> #include <acpi/acpi_bus.h> diff --git a/drivers/pci/hotplug/pciehp_acpi.c b/drivers/pci/hotplug/pciehp_acpi.c index 7163e6a6cfae..37c8d3d0323e 100644 --- a/drivers/pci/hotplug/pciehp_acpi.c +++ b/drivers/pci/hotplug/pciehp_acpi.c @@ -33,6 +33,11 @@ #define PCIEHP_DETECT_AUTO (2) #define PCIEHP_DETECT_DEFAULT PCIEHP_DETECT_AUTO +struct dummy_slot { + u32 number; + struct list_head list; +}; + static int slot_detection_mode; static char *pciehp_detect_mode; module_param(pciehp_detect_mode, charp, 0444); @@ -77,7 +82,7 @@ static int __init dummy_probe(struct pcie_device *dev) int pos; u32 slot_cap; acpi_handle handle; - struct slot *slot, *tmp; + struct dummy_slot *slot, *tmp; struct pci_dev *pdev = dev->port; /* Note: pciehp_detect_mode != PCIEHP_DETECT_ACPI here */ if (pciehp_get_hp_hw_control_from_firmware(pdev)) @@ -89,11 +94,11 @@ static int __init dummy_probe(struct pcie_device *dev) if (!slot) return -ENOMEM; slot->number = slot_cap >> 19; - list_for_each_entry(tmp, &dummy_slots, slot_list) { + list_for_each_entry(tmp, &dummy_slots, list) { if (tmp->number == slot->number) dup_slot_id++; } - list_add_tail(&slot->slot_list, &dummy_slots); + list_add_tail(&slot->list, &dummy_slots); handle = DEVICE_ACPI_HANDLE(&pdev->dev); if (!acpi_slot_detected && acpi_pci_detect_ejectable(handle)) acpi_slot_detected = 1; @@ -109,11 +114,11 @@ static struct pcie_port_service_driver __initdata dummy_driver = { static int __init select_detection_mode(void) { - struct slot *slot, *tmp; + struct dummy_slot *slot, *tmp; pcie_port_service_register(&dummy_driver); pcie_port_service_unregister(&dummy_driver); - list_for_each_entry_safe(slot, tmp, &dummy_slots, slot_list) { - list_del(&slot->slot_list); + list_for_each_entry_safe(slot, tmp, &dummy_slots, list) { + list_del(&slot->list); kfree(slot); } if (acpi_slot_detected && dup_slot_id) diff --git a/drivers/pci/hotplug/pciehp_core.c b/drivers/pci/hotplug/pciehp_core.c index 2317557fdee6..bc234719b1df 100644 --- a/drivers/pci/hotplug/pciehp_core.c +++ b/drivers/pci/hotplug/pciehp_core.c @@ -99,65 +99,55 @@ static void release_slot(struct hotplug_slot *hotplug_slot) kfree(hotplug_slot); } -static int init_slots(struct controller *ctrl) +static int init_slot(struct controller *ctrl) { - struct slot *slot; - struct hotplug_slot *hotplug_slot; - struct hotplug_slot_info *info; + struct slot *slot = ctrl->slot; + struct hotplug_slot *hotplug = NULL; + struct hotplug_slot_info *info = NULL; char name[SLOT_NAME_SIZE]; int retval = -ENOMEM; - list_for_each_entry(slot, &ctrl->slot_list, slot_list) { - hotplug_slot = kzalloc(sizeof(*hotplug_slot), GFP_KERNEL); - if (!hotplug_slot) - goto error; - - info = kzalloc(sizeof(*info), GFP_KERNEL); - if (!info) - goto error_hpslot; - - /* register this slot with the hotplug pci core */ - hotplug_slot->info = info; - hotplug_slot->private = slot; - hotplug_slot->release = &release_slot; - hotplug_slot->ops = &pciehp_hotplug_slot_ops; - slot->hotplug_slot = hotplug_slot; - snprintf(name, SLOT_NAME_SIZE, "%u", slot->number); - - ctrl_dbg(ctrl, "Registering domain:bus:dev=%04x:%02x:%02x " - "hp_slot=%x sun=%x slot_device_offset=%x\n", - pci_domain_nr(ctrl->pci_dev->subordinate), - slot->bus, slot->device, slot->hp_slot, slot->number, - ctrl->slot_device_offset); - retval = pci_hp_register(hotplug_slot, - ctrl->pci_dev->subordinate, - slot->device, - name); - if (retval) { - ctrl_err(ctrl, "pci_hp_register failed with error %d\n", - retval); - goto error_info; - } - get_power_status(hotplug_slot, &info->power_status); - get_attention_status(hotplug_slot, &info->attention_status); - get_latch_status(hotplug_slot, &info->latch_status); - get_adapter_status(hotplug_slot, &info->adapter_status); + hotplug = kzalloc(sizeof(*hotplug), GFP_KERNEL); + if (!hotplug) + goto out; + + info = kzalloc(sizeof(*info), GFP_KERNEL); + if (!info) + goto out; + + /* register this slot with the hotplug pci core */ + hotplug->info = info; + hotplug->private = slot; + hotplug->release = &release_slot; + hotplug->ops = &pciehp_hotplug_slot_ops; + slot->hotplug_slot = hotplug; + snprintf(name, SLOT_NAME_SIZE, "%u", PSN(ctrl)); + + ctrl_dbg(ctrl, "Registering domain:bus:dev=%04x:%02x:00 sun=%x\n", + pci_domain_nr(ctrl->pcie->port->subordinate), + ctrl->pcie->port->subordinate->number, PSN(ctrl)); + retval = pci_hp_register(hotplug, + ctrl->pcie->port->subordinate, 0, name); + if (retval) { + ctrl_err(ctrl, + "pci_hp_register failed with error %d\n", retval); + goto out; + } + get_power_status(hotplug, &info->power_status); + get_attention_status(hotplug, &info->attention_status); + get_latch_status(hotplug, &info->latch_status); + get_adapter_status(hotplug, &info->adapter_status); +out: + if (retval) { + kfree(info); + kfree(hotplug); } - - return 0; -error_info: - kfree(info); -error_hpslot: - kfree(hotplug_slot); -error: return retval; } -static void cleanup_slots(struct controller *ctrl) +static void cleanup_slot(struct controller *ctrl) { - struct slot *slot; - list_for_each_entry(slot, &ctrl->slot_list, slot_list) - pci_hp_deregister(slot->hotplug_slot); + pci_hp_deregister(ctrl->slot->hotplug_slot); } /* @@ -173,7 +163,7 @@ static int set_attention_status(struct hotplug_slot *hotplug_slot, u8 status) hotplug_slot->info->attention_status = status; if (ATTN_LED(slot->ctrl)) - slot->hpc_ops->set_attention_status(slot, status); + pciehp_set_attention_status(slot, status); return 0; } @@ -208,7 +198,7 @@ static int get_power_status(struct hotplug_slot *hotplug_slot, u8 *value) ctrl_dbg(slot->ctrl, "%s: physical_slot = %s\n", __func__, slot_name(slot)); - retval = slot->hpc_ops->get_power_status(slot, value); + retval = pciehp_get_power_status(slot, value); if (retval < 0) *value = hotplug_slot->info->power_status; @@ -223,7 +213,7 @@ static int get_attention_status(struct hotplug_slot *hotplug_slot, u8 *value) ctrl_dbg(slot->ctrl, "%s: physical_slot = %s\n", __func__, slot_name(slot)); - retval = slot->hpc_ops->get_attention_status(slot, value); + retval = pciehp_get_attention_status(slot, value); if (retval < 0) *value = hotplug_slot->info->attention_status; @@ -238,7 +228,7 @@ static int get_latch_status(struct hotplug_slot *hotplug_slot, u8 *value) ctrl_dbg(slot->ctrl, "%s: physical_slot = %s\n", __func__, slot_name(slot)); - retval = slot->hpc_ops->get_latch_status(slot, value); + retval = pciehp_get_latch_status(slot, value); if (retval < 0) *value = hotplug_slot->info->latch_status; @@ -253,7 +243,7 @@ static int get_adapter_status(struct hotplug_slot *hotplug_slot, u8 *value) ctrl_dbg(slot->ctrl, "%s: physical_slot = %s\n", __func__, slot_name(slot)); - retval = slot->hpc_ops->get_adapter_status(slot, value); + retval = pciehp_get_adapter_status(slot, value); if (retval < 0) *value = hotplug_slot->info->adapter_status; @@ -269,7 +259,7 @@ static int get_max_bus_speed(struct hotplug_slot *hotplug_slot, ctrl_dbg(slot->ctrl, "%s: physical_slot = %s\n", __func__, slot_name(slot)); - retval = slot->hpc_ops->get_max_bus_speed(slot, value); + retval = pciehp_get_max_link_speed(slot, value); if (retval < 0) *value = PCI_SPEED_UNKNOWN; @@ -284,7 +274,7 @@ static int get_cur_bus_speed(struct hotplug_slot *hotplug_slot, enum pci_bus_spe ctrl_dbg(slot->ctrl, "%s: physical_slot = %s\n", __func__, slot_name(slot)); - retval = slot->hpc_ops->get_cur_bus_speed(slot, value); + retval = pciehp_get_cur_link_speed(slot, value); if (retval < 0) *value = PCI_SPEED_UNKNOWN; @@ -295,7 +285,7 @@ static int pciehp_probe(struct pcie_device *dev) { int rc; struct controller *ctrl; - struct slot *t_slot; + struct slot *slot; u8 value; struct pci_dev *pdev = dev->port; @@ -314,7 +304,7 @@ static int pciehp_probe(struct pcie_device *dev) set_service_data(dev, ctrl); /* Setup the slot information structures */ - rc = init_slots(ctrl); + rc = init_slot(ctrl); if (rc) { if (rc == -EBUSY) ctrl_warn(ctrl, "Slot already registered by another " @@ -332,15 +322,15 @@ static int pciehp_probe(struct pcie_device *dev) } /* Check if slot is occupied */ - t_slot = pciehp_find_slot(ctrl, ctrl->slot_device_offset); - t_slot->hpc_ops->get_adapter_status(t_slot, &value); + slot = ctrl->slot; + pciehp_get_adapter_status(slot, &value); if (value) { if (pciehp_force) - pciehp_enable_slot(t_slot); + pciehp_enable_slot(slot); } else { /* Power off slot if not occupied */ if (POWER_CTRL(ctrl)) { - rc = t_slot->hpc_ops->power_off_slot(t_slot); + rc = pciehp_power_off_slot(slot); if (rc) goto err_out_free_ctrl_slot; } @@ -349,19 +339,19 @@ static int pciehp_probe(struct pcie_device *dev) return 0; err_out_free_ctrl_slot: - cleanup_slots(ctrl); + cleanup_slot(ctrl); err_out_release_ctlr: - ctrl->hpc_ops->release_ctlr(ctrl); + pciehp_release_ctrl(ctrl); err_out_none: return -ENODEV; } -static void pciehp_remove (struct pcie_device *dev) +static void pciehp_remove(struct pcie_device *dev) { struct controller *ctrl = get_service_data(dev); - cleanup_slots(ctrl); - ctrl->hpc_ops->release_ctlr(ctrl); + cleanup_slot(ctrl); + pciehp_release_ctrl(ctrl); } #ifdef CONFIG_PM @@ -376,20 +366,20 @@ static int pciehp_resume (struct pcie_device *dev) dev_info(&dev->device, "%s ENTRY\n", __func__); if (pciehp_force) { struct controller *ctrl = get_service_data(dev); - struct slot *t_slot; + struct slot *slot; u8 status; /* reinitialize the chipset's event detection logic */ pcie_enable_notification(ctrl); - t_slot = pciehp_find_slot(ctrl, ctrl->slot_device_offset); + slot = ctrl->slot; /* Check if slot is occupied */ - t_slot->hpc_ops->get_adapter_status(t_slot, &status); + pciehp_get_adapter_status(slot, &status); if (status) - pciehp_enable_slot(t_slot); + pciehp_enable_slot(slot); else - pciehp_disable_slot(t_slot); + pciehp_disable_slot(slot); } return 0; } diff --git a/drivers/pci/hotplug/pciehp_ctrl.c b/drivers/pci/hotplug/pciehp_ctrl.c index b97cb4c3e0fe..84487d126e4d 100644 --- a/drivers/pci/hotplug/pciehp_ctrl.c +++ b/drivers/pci/hotplug/pciehp_ctrl.c @@ -82,7 +82,7 @@ u8 pciehp_handle_switch_change(struct slot *p_slot) /* Switch Change */ ctrl_dbg(ctrl, "Switch interrupt received\n"); - p_slot->hpc_ops->get_latch_status(p_slot, &getstatus); + pciehp_get_latch_status(p_slot, &getstatus); if (getstatus) { /* * Switch opened @@ -114,7 +114,7 @@ u8 pciehp_handle_presence_change(struct slot *p_slot) /* Switch is open, assume a presence change * Save the presence state */ - p_slot->hpc_ops->get_adapter_status(p_slot, &presence_save); + pciehp_get_adapter_status(p_slot, &presence_save); if (presence_save) { /* * Card Present @@ -143,7 +143,7 @@ u8 pciehp_handle_power_fault(struct slot *p_slot) /* power fault */ ctrl_dbg(ctrl, "Power fault interrupt received\n"); - if ( !(p_slot->hpc_ops->query_power_fault(p_slot))) { + if (!pciehp_query_power_fault(p_slot)) { /* * power fault Cleared */ @@ -172,7 +172,7 @@ static void set_slot_off(struct controller *ctrl, struct slot * pslot) { /* turn off slot, turn on Amber LED, turn off Green LED if supported*/ if (POWER_CTRL(ctrl)) { - if (pslot->hpc_ops->power_off_slot(pslot)) { + if (pciehp_power_off_slot(pslot)) { ctrl_err(ctrl, "Issue of Slot Power Off command failed\n"); return; @@ -186,10 +186,10 @@ static void set_slot_off(struct controller *ctrl, struct slot * pslot) } if (PWR_LED(ctrl)) - pslot->hpc_ops->green_led_off(pslot); + pciehp_green_led_off(pslot); if (ATTN_LED(ctrl)) { - if (pslot->hpc_ops->set_attention_status(pslot, 1)) { + if (pciehp_set_attention_status(pslot, 1)) { ctrl_err(ctrl, "Issue of Set Attention Led command failed\n"); return; @@ -208,24 +208,20 @@ static int board_added(struct slot *p_slot) { int retval = 0; struct controller *ctrl = p_slot->ctrl; - struct pci_bus *parent = ctrl->pci_dev->subordinate; - - ctrl_dbg(ctrl, "%s: slot device, slot offset, hp slot = %d, %d, %d\n", - __func__, p_slot->device, ctrl->slot_device_offset, - p_slot->hp_slot); + struct pci_bus *parent = ctrl->pcie->port->subordinate; if (POWER_CTRL(ctrl)) { /* Power on slot */ - retval = p_slot->hpc_ops->power_on_slot(p_slot); + retval = pciehp_power_on_slot(p_slot); if (retval) return retval; } if (PWR_LED(ctrl)) - p_slot->hpc_ops->green_led_blink(p_slot); + pciehp_green_led_blink(p_slot); /* Check link training status */ - retval = p_slot->hpc_ops->check_lnk_status(ctrl); + retval = pciehp_check_link_status(ctrl); if (retval) { ctrl_err(ctrl, "Failed to check link status\n"); set_slot_off(ctrl, p_slot); @@ -233,21 +229,21 @@ static int board_added(struct slot *p_slot) } /* Check for a power fault */ - if (p_slot->hpc_ops->query_power_fault(p_slot)) { + if (pciehp_query_power_fault(p_slot)) { ctrl_dbg(ctrl, "Power fault detected\n"); - retval = POWER_FAILURE; + retval = -EIO; goto err_exit; } retval = pciehp_configure_device(p_slot); if (retval) { - ctrl_err(ctrl, "Cannot add device at %04x:%02x:%02x\n", - pci_domain_nr(parent), p_slot->bus, p_slot->device); + ctrl_err(ctrl, "Cannot add device at %04x:%02x:00\n", + pci_domain_nr(parent), parent->number); goto err_exit; } if (PWR_LED(ctrl)) - p_slot->hpc_ops->green_led_on(p_slot); + pciehp_green_led_on(p_slot); return 0; @@ -269,11 +265,9 @@ static int remove_board(struct slot *p_slot) if (retval) return retval; - ctrl_dbg(ctrl, "%s: hp_slot = %d\n", __func__, p_slot->hp_slot); - if (POWER_CTRL(ctrl)) { /* power off slot */ - retval = p_slot->hpc_ops->power_off_slot(p_slot); + retval = pciehp_power_off_slot(p_slot); if (retval) { ctrl_err(ctrl, "Issue of Slot Disable command failed\n"); @@ -287,9 +281,9 @@ static int remove_board(struct slot *p_slot) msleep(1000); } + /* turn off Green LED */ if (PWR_LED(ctrl)) - /* turn off Green LED */ - p_slot->hpc_ops->green_led_off(p_slot); + pciehp_green_led_off(p_slot); return 0; } @@ -317,18 +311,17 @@ static void pciehp_power_thread(struct work_struct *work) case POWEROFF_STATE: mutex_unlock(&p_slot->lock); ctrl_dbg(p_slot->ctrl, - "Disabling domain:bus:device=%04x:%02x:%02x\n", - pci_domain_nr(p_slot->ctrl->pci_dev->subordinate), - p_slot->bus, p_slot->device); + "Disabling domain:bus:device=%04x:%02x:00\n", + pci_domain_nr(p_slot->ctrl->pcie->port->subordinate), + p_slot->ctrl->pcie->port->subordinate->number); pciehp_disable_slot(p_slot); mutex_lock(&p_slot->lock); p_slot->state = STATIC_STATE; break; case POWERON_STATE: mutex_unlock(&p_slot->lock); - if (pciehp_enable_slot(p_slot) && - PWR_LED(p_slot->ctrl)) - p_slot->hpc_ops->green_led_off(p_slot); + if (pciehp_enable_slot(p_slot) && PWR_LED(p_slot->ctrl)) + pciehp_green_led_off(p_slot); mutex_lock(&p_slot->lock); p_slot->state = STATIC_STATE; break; @@ -379,10 +372,10 @@ static int update_slot_info(struct slot *slot) if (!info) return -ENOMEM; - slot->hpc_ops->get_power_status(slot, &(info->power_status)); - slot->hpc_ops->get_attention_status(slot, &(info->attention_status)); - slot->hpc_ops->get_latch_status(slot, &(info->latch_status)); - slot->hpc_ops->get_adapter_status(slot, &(info->adapter_status)); + pciehp_get_power_status(slot, &info->power_status); + pciehp_get_attention_status(slot, &info->attention_status); + pciehp_get_latch_status(slot, &info->latch_status); + pciehp_get_adapter_status(slot, &info->adapter_status); result = pci_hp_change_slot_info(slot->hotplug_slot, info); kfree (info); @@ -399,7 +392,7 @@ static void handle_button_press_event(struct slot *p_slot) switch (p_slot->state) { case STATIC_STATE: - p_slot->hpc_ops->get_power_status(p_slot, &getstatus); + pciehp_get_power_status(p_slot, &getstatus); if (getstatus) { p_slot->state = BLINKINGOFF_STATE; ctrl_info(ctrl, @@ -413,9 +406,9 @@ static void handle_button_press_event(struct slot *p_slot) } /* blink green LED and turn off amber */ if (PWR_LED(ctrl)) - p_slot->hpc_ops->green_led_blink(p_slot); + pciehp_green_led_blink(p_slot); if (ATTN_LED(ctrl)) - p_slot->hpc_ops->set_attention_status(p_slot, 0); + pciehp_set_attention_status(p_slot, 0); schedule_delayed_work(&p_slot->work, 5*HZ); break; @@ -430,13 +423,13 @@ static void handle_button_press_event(struct slot *p_slot) cancel_delayed_work(&p_slot->work); if (p_slot->state == BLINKINGOFF_STATE) { if (PWR_LED(ctrl)) - p_slot->hpc_ops->green_led_on(p_slot); + pciehp_green_led_on(p_slot); } else { if (PWR_LED(ctrl)) - p_slot->hpc_ops->green_led_off(p_slot); + pciehp_green_led_off(p_slot); } if (ATTN_LED(ctrl)) - p_slot->hpc_ops->set_attention_status(p_slot, 0); + pciehp_set_attention_status(p_slot, 0); ctrl_info(ctrl, "PCI slot #%s - action canceled " "due to button press\n", slot_name(p_slot)); p_slot->state = STATIC_STATE; @@ -474,7 +467,7 @@ static void handle_surprise_event(struct slot *p_slot) info->p_slot = p_slot; INIT_WORK(&info->work, pciehp_power_thread); - p_slot->hpc_ops->get_adapter_status(p_slot, &getstatus); + pciehp_get_adapter_status(p_slot, &getstatus); if (!getstatus) p_slot->state = POWEROFF_STATE; else @@ -498,9 +491,9 @@ static void interrupt_event_handler(struct work_struct *work) if (!POWER_CTRL(ctrl)) break; if (ATTN_LED(ctrl)) - p_slot->hpc_ops->set_attention_status(p_slot, 1); + pciehp_set_attention_status(p_slot, 1); if (PWR_LED(ctrl)) - p_slot->hpc_ops->green_led_off(p_slot); + pciehp_green_led_off(p_slot); break; case INT_PRESENCE_ON: case INT_PRESENCE_OFF: @@ -525,45 +518,38 @@ int pciehp_enable_slot(struct slot *p_slot) int rc; struct controller *ctrl = p_slot->ctrl; - /* Check to see if (latch closed, card present, power off) */ - mutex_lock(&p_slot->ctrl->crit_sect); - - rc = p_slot->hpc_ops->get_adapter_status(p_slot, &getstatus); + rc = pciehp_get_adapter_status(p_slot, &getstatus); if (rc || !getstatus) { ctrl_info(ctrl, "No adapter on slot(%s)\n", slot_name(p_slot)); - mutex_unlock(&p_slot->ctrl->crit_sect); return -ENODEV; } if (MRL_SENS(p_slot->ctrl)) { - rc = p_slot->hpc_ops->get_latch_status(p_slot, &getstatus); + rc = pciehp_get_latch_status(p_slot, &getstatus); if (rc || getstatus) { ctrl_info(ctrl, "Latch open on slot(%s)\n", slot_name(p_slot)); - mutex_unlock(&p_slot->ctrl->crit_sect); return -ENODEV; } } if (POWER_CTRL(p_slot->ctrl)) { - rc = p_slot->hpc_ops->get_power_status(p_slot, &getstatus); + rc = pciehp_get_power_status(p_slot, &getstatus); if (rc || getstatus) { ctrl_info(ctrl, "Already enabled on slot(%s)\n", slot_name(p_slot)); - mutex_unlock(&p_slot->ctrl->crit_sect); return -EINVAL; } } - p_slot->hpc_ops->get_latch_status(p_slot, &getstatus); + pciehp_get_latch_status(p_slot, &getstatus); rc = board_added(p_slot); if (rc) { - p_slot->hpc_ops->get_latch_status(p_slot, &getstatus); + pciehp_get_latch_status(p_slot, &getstatus); } update_slot_info(p_slot); - mutex_unlock(&p_slot->ctrl->crit_sect); return rc; } @@ -577,35 +563,29 @@ int pciehp_disable_slot(struct slot *p_slot) if (!p_slot->ctrl) return 1; - /* Check to see if (latch closed, card present, power on) */ - mutex_lock(&p_slot->ctrl->crit_sect); - if (!HP_SUPR_RM(p_slot->ctrl)) { - ret = p_slot->hpc_ops->get_adapter_status(p_slot, &getstatus); + ret = pciehp_get_adapter_status(p_slot, &getstatus); if (ret || !getstatus) { ctrl_info(ctrl, "No adapter on slot(%s)\n", slot_name(p_slot)); - mutex_unlock(&p_slot->ctrl->crit_sect); return -ENODEV; } } if (MRL_SENS(p_slot->ctrl)) { - ret = p_slot->hpc_ops->get_latch_status(p_slot, &getstatus); + ret = pciehp_get_latch_status(p_slot, &getstatus); if (ret || getstatus) { ctrl_info(ctrl, "Latch open on slot(%s)\n", slot_name(p_slot)); - mutex_unlock(&p_slot->ctrl->crit_sect); return -ENODEV; } } if (POWER_CTRL(p_slot->ctrl)) { - ret = p_slot->hpc_ops->get_power_status(p_slot, &getstatus); + ret = pciehp_get_power_status(p_slot, &getstatus); if (ret || !getstatus) { ctrl_info(ctrl, "Already disabled on slot(%s)\n", slot_name(p_slot)); - mutex_unlock(&p_slot->ctrl->crit_sect); return -EINVAL; } } @@ -613,7 +593,6 @@ int pciehp_disable_slot(struct slot *p_slot) ret = remove_board(p_slot); update_slot_info(p_slot); - mutex_unlock(&p_slot->ctrl->crit_sect); return ret; } diff --git a/drivers/pci/hotplug/pciehp_hpc.c b/drivers/pci/hotplug/pciehp_hpc.c index 271f917b6f2c..9ef4605c1ef6 100644 --- a/drivers/pci/hotplug/pciehp_hpc.c +++ b/drivers/pci/hotplug/pciehp_hpc.c @@ -44,25 +44,25 @@ static atomic_t pciehp_num_controllers = ATOMIC_INIT(0); static inline int pciehp_readw(struct controller *ctrl, int reg, u16 *value) { - struct pci_dev *dev = ctrl->pci_dev; + struct pci_dev *dev = ctrl->pcie->port; return pci_read_config_word(dev, ctrl->cap_base + reg, value); } static inline int pciehp_readl(struct controller *ctrl, int reg, u32 *value) { - struct pci_dev *dev = ctrl->pci_dev; + struct pci_dev *dev = ctrl->pcie->port; return pci_read_config_dword(dev, ctrl->cap_base + reg, value); } static inline int pciehp_writew(struct controller *ctrl, int reg, u16 value) { - struct pci_dev *dev = ctrl->pci_dev; + struct pci_dev *dev = ctrl->pcie->port; return pci_write_config_word(dev, ctrl->cap_base + reg, value); } static inline int pciehp_writel(struct controller *ctrl, int reg, u32 value) { - struct pci_dev *dev = ctrl->pci_dev; + struct pci_dev *dev = ctrl->pcie->port; return pci_write_config_dword(dev, ctrl->cap_base + reg, value); } @@ -266,7 +266,7 @@ static void pcie_wait_link_active(struct controller *ctrl) ctrl_dbg(ctrl, "Data Link Layer Link Active not set in 1000 msec\n"); } -static int hpc_check_lnk_status(struct controller *ctrl) +int pciehp_check_link_status(struct controller *ctrl) { u16 lnk_status; int retval = 0; @@ -305,7 +305,7 @@ static int hpc_check_lnk_status(struct controller *ctrl) return retval; } -static int hpc_get_attention_status(struct slot *slot, u8 *status) +int pciehp_get_attention_status(struct slot *slot, u8 *status) { struct controller *ctrl = slot->ctrl; u16 slot_ctrl; @@ -344,7 +344,7 @@ static int hpc_get_attention_status(struct slot *slot, u8 *status) return 0; } -static int hpc_get_power_status(struct slot *slot, u8 *status) +int pciehp_get_power_status(struct slot *slot, u8 *status) { struct controller *ctrl = slot->ctrl; u16 slot_ctrl; @@ -376,7 +376,7 @@ static int hpc_get_power_status(struct slot *slot, u8 *status) return retval; } -static int hpc_get_latch_status(struct slot *slot, u8 *status) +int pciehp_get_latch_status(struct slot *slot, u8 *status) { struct controller *ctrl = slot->ctrl; u16 slot_status; @@ -392,7 +392,7 @@ static int hpc_get_latch_status(struct slot *slot, u8 *status) return 0; } -static int hpc_get_adapter_status(struct slot *slot, u8 *status) +int pciehp_get_adapter_status(struct slot *slot, u8 *status) { struct controller *ctrl = slot->ctrl; u16 slot_status; @@ -408,7 +408,7 @@ static int hpc_get_adapter_status(struct slot *slot, u8 *status) return 0; } -static int hpc_query_power_fault(struct slot *slot) +int pciehp_query_power_fault(struct slot *slot) { struct controller *ctrl = slot->ctrl; u16 slot_status; @@ -422,7 +422,7 @@ static int hpc_query_power_fault(struct slot *slot) return !!(slot_status & PCI_EXP_SLTSTA_PFD); } -static int hpc_set_attention_status(struct slot *slot, u8 value) +int pciehp_set_attention_status(struct slot *slot, u8 value) { struct controller *ctrl = slot->ctrl; u16 slot_cmd; @@ -450,7 +450,7 @@ static int hpc_set_attention_status(struct slot *slot, u8 value) return rc; } -static void hpc_set_green_led_on(struct slot *slot) +void pciehp_green_led_on(struct slot *slot) { struct controller *ctrl = slot->ctrl; u16 slot_cmd; @@ -463,7 +463,7 @@ static void hpc_set_green_led_on(struct slot *slot) __func__, ctrl->cap_base + PCI_EXP_SLTCTL, slot_cmd); } -static void hpc_set_green_led_off(struct slot *slot) +void pciehp_green_led_off(struct slot *slot) { struct controller *ctrl = slot->ctrl; u16 slot_cmd; @@ -476,7 +476,7 @@ static void hpc_set_green_led_off(struct slot *slot) __func__, ctrl->cap_base + PCI_EXP_SLTCTL, slot_cmd); } -static void hpc_set_green_led_blink(struct slot *slot) +void pciehp_green_led_blink(struct slot *slot) { struct controller *ctrl = slot->ctrl; u16 slot_cmd; @@ -489,7 +489,7 @@ static void hpc_set_green_led_blink(struct slot *slot) __func__, ctrl->cap_base + PCI_EXP_SLTCTL, slot_cmd); } -static int hpc_power_on_slot(struct slot * slot) +int pciehp_power_on_slot(struct slot * slot) { struct controller *ctrl = slot->ctrl; u16 slot_cmd; @@ -497,8 +497,6 @@ static int hpc_power_on_slot(struct slot * slot) u16 slot_status; int retval = 0; - ctrl_dbg(ctrl, "%s: slot->hp_slot %x\n", __func__, slot->hp_slot); - /* Clear sticky power-fault bit from previous power failures */ retval = pciehp_readw(ctrl, PCI_EXP_SLTSTA, &slot_status); if (retval) { @@ -539,7 +537,7 @@ static int hpc_power_on_slot(struct slot * slot) static inline int pcie_mask_bad_dllp(struct controller *ctrl) { - struct pci_dev *dev = ctrl->pci_dev; + struct pci_dev *dev = ctrl->pcie->port; int pos; u32 reg; @@ -556,7 +554,7 @@ static inline int pcie_mask_bad_dllp(struct controller *ctrl) static inline void pcie_unmask_bad_dllp(struct controller *ctrl) { - struct pci_dev *dev = ctrl->pci_dev; + struct pci_dev *dev = ctrl->pcie->port; u32 reg; int pos; @@ -570,7 +568,7 @@ static inline void pcie_unmask_bad_dllp(struct controller *ctrl) pci_write_config_dword(dev, pos + PCI_ERR_COR_MASK, reg); } -static int hpc_power_off_slot(struct slot * slot) +int pciehp_power_off_slot(struct slot * slot) { struct controller *ctrl = slot->ctrl; u16 slot_cmd; @@ -578,8 +576,6 @@ static int hpc_power_off_slot(struct slot * slot) int retval = 0; int changed; - ctrl_dbg(ctrl, "%s: slot->hp_slot %x\n", __func__, slot->hp_slot); - /* * Set Bad DLLP Mask bit in Correctable Error Mask * Register. This is the workaround against Bad DLLP error @@ -614,8 +610,8 @@ static int hpc_power_off_slot(struct slot * slot) static irqreturn_t pcie_isr(int irq, void *dev_id) { struct controller *ctrl = (struct controller *)dev_id; + struct slot *slot = ctrl->slot; u16 detected, intr_loc; - struct slot *p_slot; /* * In order to guarantee that all interrupt events are @@ -656,29 +652,27 @@ static irqreturn_t pcie_isr(int irq, void *dev_id) if (!(intr_loc & ~PCI_EXP_SLTSTA_CC)) return IRQ_HANDLED; - p_slot = pciehp_find_slot(ctrl, ctrl->slot_device_offset); - /* Check MRL Sensor Changed */ if (intr_loc & PCI_EXP_SLTSTA_MRLSC) - pciehp_handle_switch_change(p_slot); + pciehp_handle_switch_change(slot); /* Check Attention Button Pressed */ if (intr_loc & PCI_EXP_SLTSTA_ABP) - pciehp_handle_attention_button(p_slot); + pciehp_handle_attention_button(slot); /* Check Presence Detect Changed */ if (intr_loc & PCI_EXP_SLTSTA_PDC) - pciehp_handle_presence_change(p_slot); + pciehp_handle_presence_change(slot); /* Check Power Fault Detected */ if ((intr_loc & PCI_EXP_SLTSTA_PFD) && !ctrl->power_fault_detected) { ctrl->power_fault_detected = 1; - pciehp_handle_power_fault(p_slot); + pciehp_handle_power_fault(slot); } return IRQ_HANDLED; } -static int hpc_get_max_lnk_speed(struct slot *slot, enum pci_bus_speed *value) +int pciehp_get_max_link_speed(struct slot *slot, enum pci_bus_speed *value) { struct controller *ctrl = slot->ctrl; enum pcie_link_speed lnk_speed; @@ -709,7 +703,7 @@ static int hpc_get_max_lnk_speed(struct slot *slot, enum pci_bus_speed *value) return retval; } -static int hpc_get_max_lnk_width(struct slot *slot, +int pciehp_get_max_lnk_width(struct slot *slot, enum pcie_link_width *value) { struct controller *ctrl = slot->ctrl; @@ -759,7 +753,7 @@ static int hpc_get_max_lnk_width(struct slot *slot, return retval; } -static int hpc_get_cur_lnk_speed(struct slot *slot, enum pci_bus_speed *value) +int pciehp_get_cur_link_speed(struct slot *slot, enum pci_bus_speed *value) { struct controller *ctrl = slot->ctrl; enum pcie_link_speed lnk_speed = PCI_SPEED_UNKNOWN; @@ -791,7 +785,7 @@ static int hpc_get_cur_lnk_speed(struct slot *slot, enum pci_bus_speed *value) return retval; } -static int hpc_get_cur_lnk_width(struct slot *slot, +int pciehp_get_cur_lnk_width(struct slot *slot, enum pcie_link_width *value) { struct controller *ctrl = slot->ctrl; @@ -842,30 +836,6 @@ static int hpc_get_cur_lnk_width(struct slot *slot, return retval; } -static void pcie_release_ctrl(struct controller *ctrl); -static struct hpc_ops pciehp_hpc_ops = { - .power_on_slot = hpc_power_on_slot, - .power_off_slot = hpc_power_off_slot, - .set_attention_status = hpc_set_attention_status, - .get_power_status = hpc_get_power_status, - .get_attention_status = hpc_get_attention_status, - .get_latch_status = hpc_get_latch_status, - .get_adapter_status = hpc_get_adapter_status, - - .get_max_bus_speed = hpc_get_max_lnk_speed, - .get_cur_bus_speed = hpc_get_cur_lnk_speed, - .get_max_lnk_width = hpc_get_max_lnk_width, - .get_cur_lnk_width = hpc_get_cur_lnk_width, - - .query_power_fault = hpc_query_power_fault, - .green_led_on = hpc_set_green_led_on, - .green_led_off = hpc_set_green_led_off, - .green_led_blink = hpc_set_green_led_blink, - - .release_ctlr = pcie_release_ctrl, - .check_lnk_status = hpc_check_lnk_status, -}; - int pcie_enable_notification(struct controller *ctrl) { u16 cmd, mask; @@ -930,23 +900,16 @@ static int pcie_init_slot(struct controller *ctrl) if (!slot) return -ENOMEM; - slot->hp_slot = 0; slot->ctrl = ctrl; - slot->bus = ctrl->pci_dev->subordinate->number; - slot->device = ctrl->slot_device_offset + slot->hp_slot; - slot->hpc_ops = ctrl->hpc_ops; - slot->number = ctrl->first_slot; mutex_init(&slot->lock); INIT_DELAYED_WORK(&slot->work, pciehp_queue_pushbutton_work); - list_add(&slot->slot_list, &ctrl->slot_list); + ctrl->slot = slot; return 0; } static void pcie_cleanup_slot(struct controller *ctrl) { - struct slot *slot; - slot = list_first_entry(&ctrl->slot_list, struct slot, slot_list); - list_del(&slot->slot_list); + struct slot *slot = ctrl->slot; cancel_delayed_work(&slot->work); flush_scheduled_work(); flush_workqueue(pciehp_wq); @@ -957,7 +920,7 @@ static inline void dbg_ctrl(struct controller *ctrl) { int i; u16 reg16; - struct pci_dev *pdev = ctrl->pci_dev; + struct pci_dev *pdev = ctrl->pcie->port; if (!pciehp_debug) return; @@ -980,7 +943,7 @@ static inline void dbg_ctrl(struct controller *ctrl) (unsigned long long)pci_resource_start(pdev, i)); } ctrl_info(ctrl, "Slot Capabilities : 0x%08x\n", ctrl->slot_cap); - ctrl_info(ctrl, " Physical Slot Number : %d\n", ctrl->first_slot); + ctrl_info(ctrl, " Physical Slot Number : %d\n", PSN(ctrl)); ctrl_info(ctrl, " Attention Button : %3s\n", ATTN_BUTTN(ctrl) ? "yes" : "no"); ctrl_info(ctrl, " Power Controller : %3s\n", @@ -1014,10 +977,7 @@ struct controller *pcie_init(struct pcie_device *dev) dev_err(&dev->device, "%s: Out of memory\n", __func__); goto abort; } - INIT_LIST_HEAD(&ctrl->slot_list); - ctrl->pcie = dev; - ctrl->pci_dev = pdev; ctrl->cap_base = pci_find_capability(pdev, PCI_CAP_ID_EXP); if (!ctrl->cap_base) { ctrl_err(ctrl, "Cannot find PCI Express capability\n"); @@ -1029,11 +989,6 @@ struct controller *pcie_init(struct pcie_device *dev) } ctrl->slot_cap = slot_cap; - ctrl->first_slot = slot_cap >> 19; - ctrl->slot_device_offset = 0; - ctrl->num_slots = 1; - ctrl->hpc_ops = &pciehp_hpc_ops; - mutex_init(&ctrl->crit_sect); mutex_init(&ctrl->ctrl_lock); init_waitqueue_head(&ctrl->queue); dbg_ctrl(ctrl); @@ -1089,7 +1044,7 @@ abort: return NULL; } -void pcie_release_ctrl(struct controller *ctrl) +void pciehp_release_ctrl(struct controller *ctrl) { pcie_shutdown_notification(ctrl); pcie_cleanup_slot(ctrl); diff --git a/drivers/pci/hotplug/pciehp_pci.c b/drivers/pci/hotplug/pciehp_pci.c index 02e24d63b3ee..21733108adde 100644 --- a/drivers/pci/hotplug/pciehp_pci.c +++ b/drivers/pci/hotplug/pciehp_pci.c @@ -63,27 +63,27 @@ static int __ref pciehp_add_bridge(struct pci_dev *dev) int pciehp_configure_device(struct slot *p_slot) { struct pci_dev *dev; - struct pci_bus *parent = p_slot->ctrl->pci_dev->subordinate; + struct pci_bus *parent = p_slot->ctrl->pcie->port->subordinate; int num, fn; struct controller *ctrl = p_slot->ctrl; - dev = pci_get_slot(parent, PCI_DEVFN(p_slot->device, 0)); + dev = pci_get_slot(parent, PCI_DEVFN(0, 0)); if (dev) { ctrl_err(ctrl, "Device %s already exists " - "at %04x:%02x:%02x, cannot hot-add\n", pci_name(dev), - pci_domain_nr(parent), p_slot->bus, p_slot->device); + "at %04x:%02x:00, cannot hot-add\n", pci_name(dev), + pci_domain_nr(parent), parent->number); pci_dev_put(dev); return -EINVAL; } - num = pci_scan_slot(parent, PCI_DEVFN(p_slot->device, 0)); + num = pci_scan_slot(parent, PCI_DEVFN(0, 0)); if (num == 0) { ctrl_err(ctrl, "No new device found\n"); return -ENODEV; } for (fn = 0; fn < 8; fn++) { - dev = pci_get_slot(parent, PCI_DEVFN(p_slot->device, fn)); + dev = pci_get_slot(parent, PCI_DEVFN(0, fn)); if (!dev) continue; if ((dev->class >> 16) == PCI_BASE_CLASS_DISPLAY) { @@ -111,19 +111,18 @@ int pciehp_unconfigure_device(struct slot *p_slot) int j; u8 bctl = 0; u8 presence = 0; - struct pci_bus *parent = p_slot->ctrl->pci_dev->subordinate; + struct pci_bus *parent = p_slot->ctrl->pcie->port->subordinate; u16 command; struct controller *ctrl = p_slot->ctrl; - ctrl_dbg(ctrl, "%s: domain:bus:dev = %04x:%02x:%02x\n", - __func__, pci_domain_nr(parent), p_slot->bus, p_slot->device); - ret = p_slot->hpc_ops->get_adapter_status(p_slot, &presence); + ctrl_dbg(ctrl, "%s: domain:bus:dev = %04x:%02x:00\n", + __func__, pci_domain_nr(parent), parent->number); + ret = pciehp_get_adapter_status(p_slot, &presence); if (ret) presence = 0; for (j = 0; j < 8; j++) { - struct pci_dev* temp = pci_get_slot(parent, - (p_slot->device << 3) | j); + struct pci_dev* temp = pci_get_slot(parent, PCI_DEVFN(0, j)); if (!temp) continue; if ((temp->class >> 16) == PCI_BASE_CLASS_DISPLAY) { diff --git a/drivers/pci/intel-iommu.c b/drivers/pci/intel-iommu.c index 562221e11917..1840a0578a42 100644 --- a/drivers/pci/intel-iommu.c +++ b/drivers/pci/intel-iommu.c @@ -38,6 +38,7 @@ #include <linux/intel-iommu.h> #include <linux/sysdev.h> #include <linux/tboot.h> +#include <linux/dmi.h> #include <asm/cacheflush.h> #include <asm/iommu.h> #include "pci.h" @@ -47,6 +48,7 @@ #define IS_GFX_DEVICE(pdev) ((pdev->class >> 16) == PCI_BASE_CLASS_DISPLAY) #define IS_ISA_DEVICE(pdev) ((pdev->class >> 8) == PCI_CLASS_BRIDGE_ISA) +#define IS_AZALIA(pdev) ((pdev)->vendor == 0x8086 && (pdev)->device == 0x3a3e) #define IOAPIC_RANGE_START (0xfee00000) #define IOAPIC_RANGE_END (0xfeefffff) @@ -56,8 +58,14 @@ #define MAX_AGAW_WIDTH 64 -#define DOMAIN_MAX_ADDR(gaw) ((((u64)1) << gaw) - 1) -#define DOMAIN_MAX_PFN(gaw) ((((u64)1) << (gaw-VTD_PAGE_SHIFT)) - 1) +#define __DOMAIN_MAX_PFN(gaw) ((((uint64_t)1) << (gaw-VTD_PAGE_SHIFT)) - 1) +#define __DOMAIN_MAX_ADDR(gaw) ((((uint64_t)1) << gaw) - 1) + +/* We limit DOMAIN_MAX_PFN to fit in an unsigned long, and DOMAIN_MAX_ADDR + to match. That way, we can use 'unsigned long' for PFNs with impunity. */ +#define DOMAIN_MAX_PFN(gaw) ((unsigned long) min_t(uint64_t, \ + __DOMAIN_MAX_PFN(gaw), (unsigned long)-1)) +#define DOMAIN_MAX_ADDR(gaw) (((uint64_t)__DOMAIN_MAX_PFN(gaw)) << VTD_PAGE_SHIFT) #define IOVA_PFN(addr) ((addr) >> PAGE_SHIFT) #define DMA_32BIT_PFN IOVA_PFN(DMA_BIT_MASK(32)) @@ -87,6 +95,7 @@ static inline unsigned long virt_to_dma_pfn(void *p) /* global iommu list, set NULL for ignored DMAR units */ static struct intel_iommu **g_iommus; +static void __init check_tylersburg_isoch(void); static int rwbf_quirk; /* @@ -252,7 +261,8 @@ static inline int first_pte_in_page(struct dma_pte *pte) * 2. It maps to each iommu if successful. * 3. Each iommu mapps to this domain if successful. */ -struct dmar_domain *si_domain; +static struct dmar_domain *si_domain; +static int hw_pass_through = 1; /* devices under the same p2p bridge are owned in one domain */ #define DOMAIN_FLAG_P2P_MULTIPLE_DEVICES (1 << 0) @@ -728,7 +738,7 @@ static struct dma_pte *pfn_to_dma_pte(struct dmar_domain *domain, return NULL; domain_flush_cache(domain, tmp_page, VTD_PAGE_SIZE); - pteval = (virt_to_dma_pfn(tmp_page) << VTD_PAGE_SHIFT) | DMA_PTE_READ | DMA_PTE_WRITE; + pteval = ((uint64_t)virt_to_dma_pfn(tmp_page) << VTD_PAGE_SHIFT) | DMA_PTE_READ | DMA_PTE_WRITE; if (cmpxchg64(&pte->val, 0ULL, pteval)) { /* Someone else set it while we were thinking; use theirs. */ free_pgtable_page(tmp_page); @@ -778,9 +788,10 @@ static void dma_pte_clear_range(struct dmar_domain *domain, BUG_ON(addr_width < BITS_PER_LONG && start_pfn >> addr_width); BUG_ON(addr_width < BITS_PER_LONG && last_pfn >> addr_width); + BUG_ON(start_pfn > last_pfn); /* we don't need lock here; nobody else touches the iova range */ - while (start_pfn <= last_pfn) { + do { first_pte = pte = dma_pfn_level_pte(domain, start_pfn, 1); if (!pte) { start_pfn = align_to_level(start_pfn + 1, 2); @@ -794,7 +805,8 @@ static void dma_pte_clear_range(struct dmar_domain *domain, domain_flush_cache(domain, first_pte, (void *)pte - (void *)first_pte); - } + + } while (start_pfn && start_pfn <= last_pfn); } /* free page table pages. last level pte should already be cleared */ @@ -810,6 +822,7 @@ static void dma_pte_free_pagetable(struct dmar_domain *domain, BUG_ON(addr_width < BITS_PER_LONG && start_pfn >> addr_width); BUG_ON(addr_width < BITS_PER_LONG && last_pfn >> addr_width); + BUG_ON(start_pfn > last_pfn); /* We don't need lock here; nobody else touches the iova range */ level = 2; @@ -820,7 +833,7 @@ static void dma_pte_free_pagetable(struct dmar_domain *domain, if (tmp + level_size(level) - 1 > last_pfn) return; - while (tmp + level_size(level) - 1 <= last_pfn) { + do { first_pte = pte = dma_pfn_level_pte(domain, tmp, level); if (!pte) { tmp = align_to_level(tmp + 1, level + 1); @@ -839,7 +852,7 @@ static void dma_pte_free_pagetable(struct dmar_domain *domain, domain_flush_cache(domain, first_pte, (void *)pte - (void *)first_pte); - } + } while (tmp && tmp + level_size(level) - 1 <= last_pfn); level++; } /* free pgd */ @@ -1158,6 +1171,8 @@ static int iommu_init_domains(struct intel_iommu *iommu) pr_debug("Number of Domains supportd <%ld>\n", ndomains); nlongs = BITS_TO_LONGS(ndomains); + spin_lock_init(&iommu->lock); + /* TBD: there might be 64K domains, * consider other allocation for future chip */ @@ -1170,12 +1185,9 @@ static int iommu_init_domains(struct intel_iommu *iommu) GFP_KERNEL); if (!iommu->domains) { printk(KERN_ERR "Allocating domain array failed\n"); - kfree(iommu->domain_ids); return -ENOMEM; } - spin_lock_init(&iommu->lock); - /* * if Caching mode is set, then invalid translations are tagged * with domainid 0. Hence we need to pre-allocate it. @@ -1195,22 +1207,24 @@ void free_dmar_iommu(struct intel_iommu *iommu) int i; unsigned long flags; - i = find_first_bit(iommu->domain_ids, cap_ndoms(iommu->cap)); - for (; i < cap_ndoms(iommu->cap); ) { - domain = iommu->domains[i]; - clear_bit(i, iommu->domain_ids); + if ((iommu->domains) && (iommu->domain_ids)) { + i = find_first_bit(iommu->domain_ids, cap_ndoms(iommu->cap)); + for (; i < cap_ndoms(iommu->cap); ) { + domain = iommu->domains[i]; + clear_bit(i, iommu->domain_ids); + + spin_lock_irqsave(&domain->iommu_lock, flags); + if (--domain->iommu_count == 0) { + if (domain->flags & DOMAIN_FLAG_VIRTUAL_MACHINE) + vm_domain_exit(domain); + else + domain_exit(domain); + } + spin_unlock_irqrestore(&domain->iommu_lock, flags); - spin_lock_irqsave(&domain->iommu_lock, flags); - if (--domain->iommu_count == 0) { - if (domain->flags & DOMAIN_FLAG_VIRTUAL_MACHINE) - vm_domain_exit(domain); - else - domain_exit(domain); + i = find_next_bit(iommu->domain_ids, + cap_ndoms(iommu->cap), i+1); } - spin_unlock_irqrestore(&domain->iommu_lock, flags); - - i = find_next_bit(iommu->domain_ids, - cap_ndoms(iommu->cap), i+1); } if (iommu->gcmd & DMA_GCMD_TE) @@ -1310,7 +1324,6 @@ static void iommu_detach_domain(struct dmar_domain *domain, } static struct iova_domain reserved_iova_list; -static struct lock_class_key reserved_alloc_key; static struct lock_class_key reserved_rbtree_key; static void dmar_init_reserved_ranges(void) @@ -1321,8 +1334,6 @@ static void dmar_init_reserved_ranges(void) init_iova_domain(&reserved_iova_list, DMA_32BIT_PFN); - lockdep_set_class(&reserved_iova_list.iova_alloc_lock, - &reserved_alloc_key); lockdep_set_class(&reserved_iova_list.iova_rbtree_lock, &reserved_rbtree_key); @@ -1925,6 +1936,9 @@ error: } static int iommu_identity_mapping; +#define IDENTMAP_ALL 1 +#define IDENTMAP_GFX 2 +#define IDENTMAP_AZALIA 4 static int iommu_domain_identity_map(struct dmar_domain *domain, unsigned long long start, @@ -1959,14 +1973,35 @@ static int iommu_prepare_identity_map(struct pci_dev *pdev, struct dmar_domain *domain; int ret; - printk(KERN_INFO - "IOMMU: Setting identity map for device %s [0x%Lx - 0x%Lx]\n", - pci_name(pdev), start, end); - domain = get_domain_for_dev(pdev, DEFAULT_DOMAIN_ADDRESS_WIDTH); if (!domain) return -ENOMEM; + /* For _hardware_ passthrough, don't bother. But for software + passthrough, we do it anyway -- it may indicate a memory + range which is reserved in E820, so which didn't get set + up to start with in si_domain */ + if (domain == si_domain && hw_pass_through) { + printk("Ignoring identity map for HW passthrough device %s [0x%Lx - 0x%Lx]\n", + pci_name(pdev), start, end); + return 0; + } + + printk(KERN_INFO + "IOMMU: Setting identity map for device %s [0x%Lx - 0x%Lx]\n", + pci_name(pdev), start, end); + + if (end >> agaw_to_width(domain->agaw)) { + WARN(1, "Your BIOS is broken; RMRR exceeds permitted address width (%d bits)\n" + "BIOS vendor: %s; Ver: %s; Product Version: %s\n", + agaw_to_width(domain->agaw), + dmi_get_system_info(DMI_BIOS_VENDOR), + dmi_get_system_info(DMI_BIOS_VERSION), + dmi_get_system_info(DMI_PRODUCT_VERSION)); + ret = -EIO; + goto error; + } + ret = iommu_domain_identity_map(domain, start, end); if (ret) goto error; @@ -2017,23 +2052,6 @@ static inline void iommu_prepare_isa(void) } #endif /* !CONFIG_DMAR_FLPY_WA */ -/* Initialize each context entry as pass through.*/ -static int __init init_context_pass_through(void) -{ - struct pci_dev *pdev = NULL; - struct dmar_domain *domain; - int ret; - - for_each_pci_dev(pdev) { - domain = get_domain_for_dev(pdev, DEFAULT_DOMAIN_ADDRESS_WIDTH); - ret = domain_context_mapping(domain, pdev, - CONTEXT_TT_PASS_THROUGH); - if (ret) - return ret; - } - return 0; -} - static int md_domain_init(struct dmar_domain *domain, int guest_width); static int __init si_domain_work_fn(unsigned long start_pfn, @@ -2048,7 +2066,7 @@ static int __init si_domain_work_fn(unsigned long start_pfn, } -static int si_domain_init(void) +static int __init si_domain_init(int hw) { struct dmar_drhd_unit *drhd; struct intel_iommu *iommu; @@ -2075,6 +2093,9 @@ static int si_domain_init(void) si_domain->flags = DOMAIN_FLAG_STATIC_IDENTITY; + if (hw) + return 0; + for_each_online_node(nid) { work_with_active_regions(nid, si_domain_work_fn, &ret); if (ret) @@ -2101,15 +2122,23 @@ static int identity_mapping(struct pci_dev *pdev) } static int domain_add_dev_info(struct dmar_domain *domain, - struct pci_dev *pdev) + struct pci_dev *pdev, + int translation) { struct device_domain_info *info; unsigned long flags; + int ret; info = alloc_devinfo_mem(); if (!info) return -ENOMEM; + ret = domain_context_mapping(domain, pdev, translation); + if (ret) { + free_devinfo_mem(info); + return ret; + } + info->segment = pci_domain_nr(pdev->bus); info->bus = pdev->bus->number; info->devfn = pdev->devfn; @@ -2127,8 +2156,14 @@ static int domain_add_dev_info(struct dmar_domain *domain, static int iommu_should_identity_map(struct pci_dev *pdev, int startup) { - if (iommu_identity_mapping == 2) - return IS_GFX_DEVICE(pdev); + if ((iommu_identity_mapping & IDENTMAP_AZALIA) && IS_AZALIA(pdev)) + return 1; + + if ((iommu_identity_mapping & IDENTMAP_GFX) && IS_GFX_DEVICE(pdev)) + return 1; + + if (!(iommu_identity_mapping & IDENTMAP_ALL)) + return 0; /* * We want to start off with all devices in the 1:1 domain, and @@ -2166,27 +2201,25 @@ static int iommu_should_identity_map(struct pci_dev *pdev, int startup) return 1; } -static int iommu_prepare_static_identity_mapping(void) +static int __init iommu_prepare_static_identity_mapping(int hw) { struct pci_dev *pdev = NULL; int ret; - ret = si_domain_init(); + ret = si_domain_init(hw); if (ret) return -EFAULT; for_each_pci_dev(pdev) { if (iommu_should_identity_map(pdev, 1)) { - printk(KERN_INFO "IOMMU: identity mapping for device %s\n", - pci_name(pdev)); + printk(KERN_INFO "IOMMU: %s identity mapping for device %s\n", + hw ? "hardware" : "software", pci_name(pdev)); - ret = domain_context_mapping(si_domain, pdev, + ret = domain_add_dev_info(si_domain, pdev, + hw ? CONTEXT_TT_PASS_THROUGH : CONTEXT_TT_MULTI_LEVEL); if (ret) return ret; - ret = domain_add_dev_info(si_domain, pdev); - if (ret) - return ret; } } @@ -2200,14 +2233,6 @@ int __init init_dmars(void) struct pci_dev *pdev; struct intel_iommu *iommu; int i, ret; - int pass_through = 1; - - /* - * In case pass through can not be enabled, iommu tries to use identity - * mapping. - */ - if (iommu_pass_through) - iommu_identity_mapping = 1; /* * for each drhd @@ -2235,7 +2260,6 @@ int __init init_dmars(void) deferred_flush = kzalloc(g_num_of_iommus * sizeof(struct deferred_flush_tables), GFP_KERNEL); if (!deferred_flush) { - kfree(g_iommus); ret = -ENOMEM; goto error; } @@ -2262,14 +2286,8 @@ int __init init_dmars(void) goto error; } if (!ecap_pass_through(iommu->ecap)) - pass_through = 0; + hw_pass_through = 0; } - if (iommu_pass_through) - if (!pass_through) { - printk(KERN_INFO - "Pass Through is not supported by hardware.\n"); - iommu_pass_through = 0; - } /* * Start from the sane iommu hardware state. @@ -2324,64 +2342,60 @@ int __init init_dmars(void) } } + if (iommu_pass_through) + iommu_identity_mapping |= IDENTMAP_ALL; + +#ifdef CONFIG_DMAR_BROKEN_GFX_WA + iommu_identity_mapping |= IDENTMAP_GFX; +#endif + + check_tylersburg_isoch(); + /* - * If pass through is set and enabled, context entries of all pci - * devices are intialized by pass through translation type. + * If pass through is not set or not enabled, setup context entries for + * identity mappings for rmrr, gfx, and isa and may fall back to static + * identity mapping if iommu_identity_mapping is set. */ - if (iommu_pass_through) { - ret = init_context_pass_through(); + if (iommu_identity_mapping) { + ret = iommu_prepare_static_identity_mapping(hw_pass_through); if (ret) { - printk(KERN_ERR "IOMMU: Pass through init failed.\n"); - iommu_pass_through = 0; + printk(KERN_CRIT "Failed to setup IOMMU pass-through\n"); + goto error; } } - /* - * If pass through is not set or not enabled, setup context entries for - * identity mappings for rmrr, gfx, and isa and may fall back to static - * identity mapping if iommu_identity_mapping is set. + * For each rmrr + * for each dev attached to rmrr + * do + * locate drhd for dev, alloc domain for dev + * allocate free domain + * allocate page table entries for rmrr + * if context not allocated for bus + * allocate and init context + * set present in root table for this bus + * init context with domain, translation etc + * endfor + * endfor */ - if (!iommu_pass_through) { -#ifdef CONFIG_DMAR_BROKEN_GFX_WA - if (!iommu_identity_mapping) - iommu_identity_mapping = 2; -#endif - if (iommu_identity_mapping) - iommu_prepare_static_identity_mapping(); - /* - * For each rmrr - * for each dev attached to rmrr - * do - * locate drhd for dev, alloc domain for dev - * allocate free domain - * allocate page table entries for rmrr - * if context not allocated for bus - * allocate and init context - * set present in root table for this bus - * init context with domain, translation etc - * endfor - * endfor - */ - printk(KERN_INFO "IOMMU: Setting RMRR:\n"); - for_each_rmrr_units(rmrr) { - for (i = 0; i < rmrr->devices_cnt; i++) { - pdev = rmrr->devices[i]; - /* - * some BIOS lists non-exist devices in DMAR - * table. - */ - if (!pdev) - continue; - ret = iommu_prepare_rmrr_dev(rmrr, pdev); - if (ret) - printk(KERN_ERR - "IOMMU: mapping reserved region failed\n"); - } + printk(KERN_INFO "IOMMU: Setting RMRR:\n"); + for_each_rmrr_units(rmrr) { + for (i = 0; i < rmrr->devices_cnt; i++) { + pdev = rmrr->devices[i]; + /* + * some BIOS lists non-exist devices in DMAR + * table. + */ + if (!pdev) + continue; + ret = iommu_prepare_rmrr_dev(rmrr, pdev); + if (ret) + printk(KERN_ERR + "IOMMU: mapping reserved region failed\n"); } - - iommu_prepare_isa(); } + iommu_prepare_isa(); + /* * for each drhd * enable fault log @@ -2404,11 +2418,12 @@ int __init init_dmars(void) iommu->flush.flush_context(iommu, 0, 0, 0, DMA_CCMD_GLOBAL_INVL); iommu->flush.flush_iotlb(iommu, 0, 0, 0, DMA_TLB_GLOBAL_FLUSH); - iommu_disable_protect_mem_regions(iommu); ret = iommu_enable_translation(iommu); if (ret) goto error; + + iommu_disable_protect_mem_regions(iommu); } return 0; @@ -2455,8 +2470,7 @@ static struct iova *intel_alloc_iova(struct device *dev, return iova; } -static struct dmar_domain * -get_valid_domain_for_dev(struct pci_dev *pdev) +static struct dmar_domain *__get_valid_domain_for_dev(struct pci_dev *pdev) { struct dmar_domain *domain; int ret; @@ -2484,6 +2498,18 @@ get_valid_domain_for_dev(struct pci_dev *pdev) return domain; } +static inline struct dmar_domain *get_valid_domain_for_dev(struct pci_dev *dev) +{ + struct device_domain_info *info; + + /* No lock here, assumes no domain exit in normal case */ + info = dev->dev.archdata.iommu; + if (likely(info)) + return info->domain; + + return __get_valid_domain_for_dev(dev); +} + static int iommu_dummy(struct pci_dev *pdev) { return pdev->dev.archdata.iommu == DUMMY_DEVICE_DOMAIN_INFO; @@ -2526,10 +2552,10 @@ static int iommu_no_mapping(struct device *dev) */ if (iommu_should_identity_map(pdev, 0)) { int ret; - ret = domain_add_dev_info(si_domain, pdev); - if (ret) - return 0; - ret = domain_context_mapping(si_domain, pdev, CONTEXT_TT_MULTI_LEVEL); + ret = domain_add_dev_info(si_domain, pdev, + hw_pass_through ? + CONTEXT_TT_PASS_THROUGH : + CONTEXT_TT_MULTI_LEVEL); if (!ret) { printk(KERN_INFO "64bit %s uses identity mapping\n", pci_name(pdev)); @@ -2638,10 +2664,9 @@ static void flush_unmaps(void) unsigned long mask; struct iova *iova = deferred_flush[i].iova[j]; - mask = (iova->pfn_hi - iova->pfn_lo + 1) << PAGE_SHIFT; - mask = ilog2(mask >> VTD_PAGE_SHIFT); + mask = ilog2(mm_to_dma_pfn(iova->pfn_hi - iova->pfn_lo + 1)); iommu_flush_dev_iotlb(deferred_flush[i].domain[j], - iova->pfn_lo << PAGE_SHIFT, mask); + (uint64_t)iova->pfn_lo << PAGE_SHIFT, mask); __free_iova(&deferred_flush[i].domain[j]->iovad, iova); } deferred_flush[i].next = 0; @@ -2734,12 +2759,6 @@ static void intel_unmap_page(struct device *dev, dma_addr_t dev_addr, } } -static void intel_unmap_single(struct device *dev, dma_addr_t dev_addr, size_t size, - int dir) -{ - intel_unmap_page(dev, dev_addr, size, dir, NULL); -} - static void *intel_alloc_coherent(struct device *hwdev, size_t size, dma_addr_t *dma_handle, gfp_t flags) { @@ -2748,7 +2767,15 @@ static void *intel_alloc_coherent(struct device *hwdev, size_t size, size = PAGE_ALIGN(size); order = get_order(size); - flags &= ~(GFP_DMA | GFP_DMA32); + + if (!iommu_no_mapping(hwdev)) + flags &= ~(GFP_DMA | GFP_DMA32); + else if (hwdev->coherent_dma_mask < dma_get_required_mask(hwdev)) { + if (hwdev->coherent_dma_mask < DMA_BIT_MASK(32)) + flags |= GFP_DMA; + else + flags |= GFP_DMA32; + } vaddr = (void *)__get_free_pages(flags, order); if (!vaddr) @@ -2772,7 +2799,7 @@ static void intel_free_coherent(struct device *hwdev, size_t size, void *vaddr, size = PAGE_ALIGN(size); order = get_order(size); - intel_unmap_single(hwdev, dma_handle, size, DMA_BIDIRECTIONAL); + intel_unmap_page(hwdev, dma_handle, size, DMA_BIDIRECTIONAL, NULL); free_pages((unsigned long)vaddr, order); } @@ -2808,11 +2835,18 @@ static void intel_unmap_sg(struct device *hwdev, struct scatterlist *sglist, /* free page tables */ dma_pte_free_pagetable(domain, start_pfn, last_pfn); - iommu_flush_iotlb_psi(iommu, domain->id, start_pfn, - (last_pfn - start_pfn + 1)); - - /* free iova */ - __free_iova(&domain->iovad, iova); + if (intel_iommu_strict) { + iommu_flush_iotlb_psi(iommu, domain->id, start_pfn, + last_pfn - start_pfn + 1); + /* free iova */ + __free_iova(&domain->iovad, iova); + } else { + add_unmap(domain, iova); + /* + * queue up the release of the unmap to save the 1/6th of the + * cpu used up by the iotlb flush operation... + */ + } } static int intel_nontranslate_map_sg(struct device *hddev, @@ -3056,8 +3090,8 @@ static int init_iommu_hw(void) DMA_CCMD_GLOBAL_INVL); iommu->flush.flush_iotlb(iommu, 0, 0, 0, DMA_TLB_GLOBAL_FLUSH); - iommu_disable_protect_mem_regions(iommu); iommu_enable_translation(iommu); + iommu_disable_protect_mem_regions(iommu); } return 0; @@ -3181,6 +3215,33 @@ static int __init init_iommu_sysfs(void) } #endif /* CONFIG_PM */ +/* + * Here we only respond to action of unbound device from driver. + * + * Added device is not attached to its DMAR domain here yet. That will happen + * when mapping the device to iova. + */ +static int device_notifier(struct notifier_block *nb, + unsigned long action, void *data) +{ + struct device *dev = data; + struct pci_dev *pdev = to_pci_dev(dev); + struct dmar_domain *domain; + + domain = find_domain(pdev); + if (!domain) + return 0; + + if (action == BUS_NOTIFY_UNBOUND_DRIVER && !iommu_pass_through) + domain_remove_one_dev_info(domain, pdev); + + return 0; +} + +static struct notifier_block device_nb = { + .notifier_call = device_notifier, +}; + int __init intel_iommu_init(void) { int ret = 0; @@ -3205,7 +3266,7 @@ int __init intel_iommu_init(void) * Check the need for DMA-remapping initialization now. * Above initialization will also be used by Interrupt-remapping. */ - if (no_iommu || (swiotlb && !iommu_pass_through) || dmar_disabled) + if (no_iommu || swiotlb || dmar_disabled) return -ENODEV; iommu_init_mempool(); @@ -3227,19 +3288,14 @@ int __init intel_iommu_init(void) init_timer(&unmap_timer); force_iommu = 1; - - if (!iommu_pass_through) { - printk(KERN_INFO - "Multi-level page-table translation for DMAR.\n"); - dma_ops = &intel_dma_ops; - } else - printk(KERN_INFO - "DMAR: Pass through translation for DMAR.\n"); + dma_ops = &intel_dma_ops; init_iommu_sysfs(); register_iommu(&intel_iommu_ops); + bus_register_notifier(&pci_bus_type, &device_nb); + return 0; } @@ -3517,7 +3573,6 @@ static int intel_iommu_attach_device(struct iommu_domain *domain, struct intel_iommu *iommu; int addr_width; u64 end; - int ret; /* normally pdev is not mapped */ if (unlikely(domain_context_mapped(pdev))) { @@ -3549,12 +3604,7 @@ static int intel_iommu_attach_device(struct iommu_domain *domain, return -EFAULT; } - ret = domain_add_dev_info(dmar_domain, pdev); - if (ret) - return ret; - - ret = domain_context_mapping(dmar_domain, pdev, CONTEXT_TT_MULTI_LEVEL); - return ret; + return domain_add_dev_info(dmar_domain, pdev, CONTEXT_TT_MULTI_LEVEL); } static void intel_iommu_detach_device(struct iommu_domain *domain, @@ -3671,3 +3721,61 @@ static void __devinit quirk_iommu_rwbf(struct pci_dev *dev) } DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x2a40, quirk_iommu_rwbf); + +/* On Tylersburg chipsets, some BIOSes have been known to enable the + ISOCH DMAR unit for the Azalia sound device, but not give it any + TLB entries, which causes it to deadlock. Check for that. We do + this in a function called from init_dmars(), instead of in a PCI + quirk, because we don't want to print the obnoxious "BIOS broken" + message if VT-d is actually disabled. +*/ +static void __init check_tylersburg_isoch(void) +{ + struct pci_dev *pdev; + uint32_t vtisochctrl; + + /* If there's no Azalia in the system anyway, forget it. */ + pdev = pci_get_device(PCI_VENDOR_ID_INTEL, 0x3a3e, NULL); + if (!pdev) + return; + pci_dev_put(pdev); + + /* System Management Registers. Might be hidden, in which case + we can't do the sanity check. But that's OK, because the + known-broken BIOSes _don't_ actually hide it, so far. */ + pdev = pci_get_device(PCI_VENDOR_ID_INTEL, 0x342e, NULL); + if (!pdev) + return; + + if (pci_read_config_dword(pdev, 0x188, &vtisochctrl)) { + pci_dev_put(pdev); + return; + } + + pci_dev_put(pdev); + + /* If Azalia DMA is routed to the non-isoch DMAR unit, fine. */ + if (vtisochctrl & 1) + return; + + /* Drop all bits other than the number of TLB entries */ + vtisochctrl &= 0x1c; + + /* If we have the recommended number of TLB entries (16), fine. */ + if (vtisochctrl == 0x10) + return; + + /* Zero TLB entries? You get to ride the short bus to school. */ + if (!vtisochctrl) { + WARN(1, "Your BIOS is broken; DMA routed to ISOCH DMAR unit but no TLB space.\n" + "BIOS vendor: %s; Ver: %s; Product Version: %s\n", + dmi_get_system_info(DMI_BIOS_VENDOR), + dmi_get_system_info(DMI_BIOS_VERSION), + dmi_get_system_info(DMI_PRODUCT_VERSION)); + iommu_identity_mapping |= IDENTMAP_AZALIA; + return; + } + + printk(KERN_WARNING "DMAR: Recommended TLB entries for ISOCH unit is 16; your BIOS set %d\n", + vtisochctrl); +} diff --git a/drivers/pci/intr_remapping.c b/drivers/pci/intr_remapping.c index 44803644ca05..0ed78a764ded 100644 --- a/drivers/pci/intr_remapping.c +++ b/drivers/pci/intr_remapping.c @@ -603,6 +603,9 @@ int __init intr_remapping_supported(void) if (disable_intremap) return 0; + if (!dmar_ir_support()) + return 0; + for_each_drhd_unit(drhd) { struct intel_iommu *iommu = drhd->iommu; @@ -618,6 +621,11 @@ int __init enable_intr_remapping(int eim) struct dmar_drhd_unit *drhd; int setup = 0; + if (parse_ioapics_under_ir() != 1) { + printk(KERN_INFO "Not enable interrupt remapping\n"); + return -1; + } + for_each_drhd_unit(drhd) { struct intel_iommu *iommu = drhd->iommu; diff --git a/drivers/pci/iova.c b/drivers/pci/iova.c index 46dd440e2315..7914951ef29a 100644 --- a/drivers/pci/iova.c +++ b/drivers/pci/iova.c @@ -22,7 +22,6 @@ void init_iova_domain(struct iova_domain *iovad, unsigned long pfn_32bit) { - spin_lock_init(&iovad->iova_alloc_lock); spin_lock_init(&iovad->iova_rbtree_lock); iovad->rbroot = RB_ROOT; iovad->cached32_node = NULL; @@ -205,7 +204,6 @@ alloc_iova(struct iova_domain *iovad, unsigned long size, unsigned long limit_pfn, bool size_aligned) { - unsigned long flags; struct iova *new_iova; int ret; @@ -219,11 +217,9 @@ alloc_iova(struct iova_domain *iovad, unsigned long size, if (size_aligned) size = __roundup_pow_of_two(size); - spin_lock_irqsave(&iovad->iova_alloc_lock, flags); ret = __alloc_and_insert_iova_range(iovad, size, limit_pfn, new_iova, size_aligned); - spin_unlock_irqrestore(&iovad->iova_alloc_lock, flags); if (ret) { free_iova_mem(new_iova); return NULL; @@ -381,8 +377,7 @@ reserve_iova(struct iova_domain *iovad, struct iova *iova; unsigned int overlap = 0; - spin_lock_irqsave(&iovad->iova_alloc_lock, flags); - spin_lock(&iovad->iova_rbtree_lock); + spin_lock_irqsave(&iovad->iova_rbtree_lock, flags); for (node = rb_first(&iovad->rbroot); node; node = rb_next(node)) { if (__is_range_overlap(node, pfn_lo, pfn_hi)) { iova = container_of(node, struct iova, node); @@ -402,8 +397,7 @@ reserve_iova(struct iova_domain *iovad, iova = __insert_new_range(iovad, pfn_lo, pfn_hi); finish: - spin_unlock(&iovad->iova_rbtree_lock); - spin_unlock_irqrestore(&iovad->iova_alloc_lock, flags); + spin_unlock_irqrestore(&iovad->iova_rbtree_lock, flags); return iova; } @@ -420,8 +414,7 @@ copy_reserved_iova(struct iova_domain *from, struct iova_domain *to) unsigned long flags; struct rb_node *node; - spin_lock_irqsave(&from->iova_alloc_lock, flags); - spin_lock(&from->iova_rbtree_lock); + spin_lock_irqsave(&from->iova_rbtree_lock, flags); for (node = rb_first(&from->rbroot); node; node = rb_next(node)) { struct iova *iova = container_of(node, struct iova, node); struct iova *new_iova; @@ -430,6 +423,5 @@ copy_reserved_iova(struct iova_domain *from, struct iova_domain *to) printk(KERN_ERR "Reserve iova range %lx@%lx failed\n", iova->pfn_lo, iova->pfn_lo); } - spin_unlock(&from->iova_rbtree_lock); - spin_unlock_irqrestore(&from->iova_alloc_lock, flags); + spin_unlock_irqrestore(&from->iova_rbtree_lock, flags); } diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 6edecff0b419..4e4c295a049f 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -513,7 +513,11 @@ static int pci_raw_set_power_state(struct pci_dev *dev, pci_power_t state) else if (state == PCI_D2 || dev->current_state == PCI_D2) udelay(PCI_PM_D2_DELAY); - dev->current_state = state; + pci_read_config_word(dev, dev->pm_cap + PCI_PM_CTRL, &pmcsr); + dev->current_state = (pmcsr & PCI_PM_CTRL_STATE_MASK); + if (dev->current_state != state && printk_ratelimit()) + dev_info(&dev->dev, "Refused to change power state, " + "currently in D%d\n", dev->current_state); /* According to section 5.4.1 of the "PCI BUS POWER MANAGEMENT * INTERFACE SPECIFICATION, REV. 1.2", a device transitioning @@ -2542,10 +2546,10 @@ int pci_resource_bar(struct pci_dev *dev, int resno, enum pci_bar_type *type) /** * pci_set_vga_state - set VGA decode state on device and parents if requested - * @dev the PCI device - * @decode - true = enable decoding, false = disable decoding - * @command_bits PCI_COMMAND_IO and/or PCI_COMMAND_MEMORY - * @change_bridge - traverse ancestors and change bridges + * @dev: the PCI device + * @decode: true = enable decoding, false = disable decoding + * @command_bits: PCI_COMMAND_IO and/or PCI_COMMAND_MEMORY + * @change_bridge: traverse ancestors and change bridges */ int pci_set_vga_state(struct pci_dev *dev, bool decode, unsigned int command_bits, bool change_bridge) @@ -2719,17 +2723,6 @@ int __attribute__ ((weak)) pci_ext_cfg_avail(struct pci_dev *dev) return 1; } -static int __devinit pci_init(void) -{ - struct pci_dev *dev = NULL; - - while ((dev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, dev)) != NULL) { - pci_fixup_device(pci_fixup_final, dev); - } - - return 0; -} - static int __init pci_setup(char *str) { while (str) { @@ -2767,8 +2760,6 @@ static int __init pci_setup(char *str) } early_param("pci", pci_setup); -device_initcall(pci_init); - EXPORT_SYMBOL(pci_reenable_device); EXPORT_SYMBOL(pci_enable_device_io); EXPORT_SYMBOL(pci_enable_device_mem); diff --git a/drivers/pci/pcie/aer/aerdrv.c b/drivers/pci/pcie/aer/aerdrv.c index 10c0e62bd5a8..40c3cc5d1caf 100644 --- a/drivers/pci/pcie/aer/aerdrv.c +++ b/drivers/pci/pcie/aer/aerdrv.c @@ -17,6 +17,7 @@ #include <linux/module.h> #include <linux/pci.h> +#include <linux/sched.h> #include <linux/kernel.h> #include <linux/errno.h> #include <linux/pm.h> @@ -52,7 +53,7 @@ static struct pci_error_handlers aer_error_handlers = { static struct pcie_port_service_driver aerdriver = { .name = "aer", - .port_type = PCIE_ANY_PORT, + .port_type = PCIE_RC_PORT, .service = PCIE_PORT_SERVICE_AER, .probe = aer_probe, @@ -318,6 +319,8 @@ static int __init aer_service_init(void) { if (pcie_aer_disable) return -ENXIO; + if (!pci_msi_enabled()) + return -ENXIO; return pcie_port_service_register(&aerdriver); } diff --git a/drivers/pci/pcie/aspm.c b/drivers/pci/pcie/aspm.c index f289ca9bf18d..5b7056cec00c 100644 --- a/drivers/pci/pcie/aspm.c +++ b/drivers/pci/pcie/aspm.c @@ -303,9 +303,6 @@ static void pcie_get_aspm_reg(struct pci_dev *pdev, pos = pci_find_capability(pdev, PCI_CAP_ID_EXP); pci_read_config_dword(pdev, pos + PCI_EXP_LNKCAP, ®32); info->support = (reg32 & PCI_EXP_LNKCAP_ASPMS) >> 10; - /* 00b and 10b are defined as "Reserved". */ - if (info->support == PCIE_LINK_STATE_L1) - info->support = 0; info->latency_encoding_l0s = (reg32 & PCI_EXP_LNKCAP_L0SEL) >> 12; info->latency_encoding_l1 = (reg32 & PCI_EXP_LNKCAP_L1EL) >> 15; pci_read_config_word(pdev, pos + PCI_EXP_LNKCTL, ®16); @@ -659,8 +656,10 @@ void pcie_aspm_exit_link_state(struct pci_dev *pdev) free_link_state(link); /* Recheck latencies and configure upstream links */ - pcie_update_aspm_capable(root); - pcie_config_aspm_path(parent_link); + if (parent_link) { + pcie_update_aspm_capable(root); + pcie_config_aspm_path(parent_link); + } out: mutex_unlock(&aspm_lock); up_read(&pci_bus_sem); diff --git a/drivers/pci/pcie/portdrv_pci.c b/drivers/pci/pcie/portdrv_pci.c index 6df5c984a791..f635e476d632 100644 --- a/drivers/pci/pcie/portdrv_pci.c +++ b/drivers/pci/pcie/portdrv_pci.c @@ -30,7 +30,6 @@ MODULE_DESCRIPTION(DRIVER_DESC); MODULE_LICENSE("GPL"); /* global data */ -static const char device_name[] = "pcieport-driver"; static int pcie_portdrv_restore_config(struct pci_dev *dev) { @@ -262,7 +261,7 @@ static struct pci_error_handlers pcie_portdrv_err_handler = { }; static struct pci_driver pcie_portdriver = { - .name = (char *)device_name, + .name = "pcieport", .id_table = &port_pci_ids[0], .probe = pcie_portdrv_probe, diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index 6099facecd79..245d2cdb4765 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -670,6 +670,25 @@ static void __devinit quirk_vt8235_acpi(struct pci_dev *dev) } DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_8235, quirk_vt8235_acpi); +/* + * TI XIO2000a PCIe-PCI Bridge erroneously reports it supports fast back-to-back: + * Disable fast back-to-back on the secondary bus segment + */ +static void __devinit quirk_xio2000a(struct pci_dev *dev) +{ + struct pci_dev *pdev; + u16 command; + + dev_warn(&dev->dev, "TI XIO2000a quirk detected; " + "secondary bus fast back-to-back transfers disabled\n"); + list_for_each_entry(pdev, &dev->subordinate->devices, bus_list) { + pci_read_config_word(pdev, PCI_COMMAND, &command); + if (command & PCI_COMMAND_FAST_BACK) + pci_write_config_word(pdev, PCI_COMMAND, command & ~PCI_COMMAND_FAST_BACK); + } +} +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_TI, PCI_DEVICE_ID_TI_XIO2000A, + quirk_xio2000a); #ifdef CONFIG_X86_IO_APIC @@ -990,7 +1009,7 @@ DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82454NX, static void __devinit quirk_amd_ide_mode(struct pci_dev *pdev) { - /* set SBX00 SATA in IDE mode to AHCI mode */ + /* set SBX00/Hudson-2 SATA in IDE mode to AHCI mode */ u8 tmp; pci_read_config_byte(pdev, PCI_CLASS_DEVICE, &tmp); @@ -1009,8 +1028,8 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_IXP600_SATA, quirk DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_IXP600_SATA, quirk_amd_ide_mode); DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_IXP700_SATA, quirk_amd_ide_mode); DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_IXP700_SATA, quirk_amd_ide_mode); -DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_SB900_SATA_IDE, quirk_amd_ide_mode); -DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_SB900_SATA_IDE, quirk_amd_ide_mode); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_HUDSON2_SATA_IDE, quirk_amd_ide_mode); +DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_HUDSON2_SATA_IDE, quirk_amd_ide_mode); /* * Serverworks CSB5 IDE does not fully support native mode @@ -2572,6 +2591,19 @@ void pci_fixup_device(enum pci_fixup_pass pass, struct pci_dev *dev) } pci_do_fixups(dev, start, end); } + +static int __init pci_apply_final_quirks(void) +{ + struct pci_dev *dev = NULL; + + while ((dev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, dev)) != NULL) { + pci_fixup_device(pci_fixup_final, dev); + } + + return 0; +} + +fs_initcall_sync(pci_apply_final_quirks); #else void pci_fixup_device(enum pci_fixup_pass pass, struct pci_dev *dev) {} #endif diff --git a/drivers/pci/setup-res.c b/drivers/pci/setup-res.c index 706f82d8111f..c54526b206b5 100644 --- a/drivers/pci/setup-res.c +++ b/drivers/pci/setup-res.c @@ -205,43 +205,6 @@ int pci_assign_resource(struct pci_dev *dev, int resno) return ret; } -#if 0 -int pci_assign_resource_fixed(struct pci_dev *dev, int resno) -{ - struct pci_bus *bus = dev->bus; - struct resource *res = dev->resource + resno; - unsigned int type_mask; - int i, ret = -EBUSY; - - type_mask = IORESOURCE_IO | IORESOURCE_MEM | IORESOURCE_PREFETCH; - - for (i = 0; i < PCI_BUS_NUM_RESOURCES; i++) { - struct resource *r = bus->resource[i]; - if (!r) - continue; - - /* type_mask must match */ - if ((res->flags ^ r->flags) & type_mask) - continue; - - ret = request_resource(r, res); - - if (ret == 0) - break; - } - - if (ret) { - dev_err(&dev->dev, "BAR %d: can't allocate %s resource %pR\n", - resno, res->flags & IORESOURCE_IO ? "I/O" : "mem", res); - } else if (resno < PCI_BRIDGE_RESOURCES) { - pci_update_resource(dev, resno); - } - - return ret; -} -EXPORT_SYMBOL_GPL(pci_assign_resource_fixed); -#endif - /* Sort resources by alignment */ void pdev_sort_resources(struct pci_dev *dev, struct resource_list *head) { diff --git a/drivers/pcmcia/Kconfig b/drivers/pcmcia/Kconfig index fbf965b31c14..17f38a781d47 100644 --- a/drivers/pcmcia/Kconfig +++ b/drivers/pcmcia/Kconfig @@ -192,6 +192,10 @@ config PCMCIA_AU1X00 tristate "Au1x00 pcmcia support" depends on SOC_AU1X00 && PCMCIA +config PCMCIA_BCM63XX + tristate "bcm63xx pcmcia support" + depends on BCM63XX && PCMCIA + config PCMCIA_SA1100 tristate "SA1100 support" depends on ARM && ARCH_SA1100 && PCMCIA diff --git a/drivers/pcmcia/Makefile b/drivers/pcmcia/Makefile index 047394d98ac2..a03a38acd77d 100644 --- a/drivers/pcmcia/Makefile +++ b/drivers/pcmcia/Makefile @@ -27,6 +27,7 @@ obj-$(CONFIG_PCMCIA_SA1111) += sa11xx_core.o sa1111_cs.o obj-$(CONFIG_M32R_PCC) += m32r_pcc.o obj-$(CONFIG_M32R_CFC) += m32r_cfc.o obj-$(CONFIG_PCMCIA_AU1X00) += au1x00_ss.o +obj-$(CONFIG_PCMCIA_BCM63XX) += bcm63xx_pcmcia.o obj-$(CONFIG_PCMCIA_VRC4171) += vrc4171_card.o obj-$(CONFIG_PCMCIA_VRC4173) += vrc4173_cardu.o obj-$(CONFIG_OMAP_CF) += omap_cf.o @@ -71,6 +72,7 @@ pxa2xx-obj-$(CONFIG_MACH_ARMCORE) += pxa2xx_cm_x2xx_cs.o pxa2xx-obj-$(CONFIG_ARCH_VIPER) += pxa2xx_viper.o pxa2xx-obj-$(CONFIG_TRIZEPS_PCMCIA) += pxa2xx_trizeps4.o pxa2xx-obj-$(CONFIG_MACH_PALMTX) += pxa2xx_palmtx.o +pxa2xx-obj-$(CONFIG_MACH_PALMTC) += pxa2xx_palmtc.o pxa2xx-obj-$(CONFIG_MACH_PALMLD) += pxa2xx_palmld.o pxa2xx-obj-$(CONFIG_MACH_E740) += pxa2xx_e740.o pxa2xx-obj-$(CONFIG_MACH_STARGATE2) += pxa2xx_stargate2.o diff --git a/drivers/pcmcia/at91_cf.c b/drivers/pcmcia/at91_cf.c index 9e1140f085fd..e1dccedc5960 100644 --- a/drivers/pcmcia/at91_cf.c +++ b/drivers/pcmcia/at91_cf.c @@ -363,7 +363,7 @@ static int at91_cf_suspend(struct platform_device *pdev, pm_message_t mesg) struct at91_cf_socket *cf = platform_get_drvdata(pdev); struct at91_cf_data *board = cf->board; - pcmcia_socket_dev_suspend(&pdev->dev, mesg); + pcmcia_socket_dev_suspend(&pdev->dev); if (device_may_wakeup(&pdev->dev)) { enable_irq_wake(board->det_pin); if (board->irq_pin) diff --git a/drivers/pcmcia/au1000_generic.c b/drivers/pcmcia/au1000_generic.c index 90013341cd5f..02088704ac2c 100644 --- a/drivers/pcmcia/au1000_generic.c +++ b/drivers/pcmcia/au1000_generic.c @@ -515,7 +515,7 @@ static int au1x00_drv_pcmcia_probe(struct platform_device *dev) static int au1x00_drv_pcmcia_suspend(struct platform_device *dev, pm_message_t state) { - return pcmcia_socket_dev_suspend(&dev->dev, state); + return pcmcia_socket_dev_suspend(&dev->dev); } static int au1x00_drv_pcmcia_resume(struct platform_device *dev) diff --git a/drivers/pcmcia/bcm63xx_pcmcia.c b/drivers/pcmcia/bcm63xx_pcmcia.c new file mode 100644 index 000000000000..bc88a3b19bb3 --- /dev/null +++ b/drivers/pcmcia/bcm63xx_pcmcia.c @@ -0,0 +1,536 @@ +/* + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Copyright (C) 2008 Maxime Bizon <mbizon@freebox.fr> + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/ioport.h> +#include <linux/timer.h> +#include <linux/platform_device.h> +#include <linux/delay.h> +#include <linux/pci.h> +#include <linux/gpio.h> + +#include <bcm63xx_regs.h> +#include <bcm63xx_io.h> +#include "bcm63xx_pcmcia.h" + +#define PFX "bcm63xx_pcmcia: " + +#ifdef CONFIG_CARDBUS +/* if cardbus is used, platform device needs reference to actual pci + * device */ +static struct pci_dev *bcm63xx_cb_dev; +#endif + +/* + * read/write helper for pcmcia regs + */ +static inline u32 pcmcia_readl(struct bcm63xx_pcmcia_socket *skt, u32 off) +{ + return bcm_readl(skt->base + off); +} + +static inline void pcmcia_writel(struct bcm63xx_pcmcia_socket *skt, + u32 val, u32 off) +{ + bcm_writel(val, skt->base + off); +} + +/* + * This callback should (re-)initialise the socket, turn on status + * interrupts and PCMCIA bus, and wait for power to stabilise so that + * the card status signals report correctly. + * + * Hardware cannot do that. + */ +static int bcm63xx_pcmcia_sock_init(struct pcmcia_socket *sock) +{ + return 0; +} + +/* + * This callback should remove power on the socket, disable IRQs from + * the card, turn off status interrupts, and disable the PCMCIA bus. + * + * Hardware cannot do that. + */ +static int bcm63xx_pcmcia_suspend(struct pcmcia_socket *sock) +{ + return 0; +} + +/* + * Implements the set_socket() operation for the in-kernel PCMCIA + * service (formerly SS_SetSocket in Card Services). We more or + * less punt all of this work and let the kernel handle the details + * of power configuration, reset, &c. We also record the value of + * `state' in order to regurgitate it to the PCMCIA core later. + */ +static int bcm63xx_pcmcia_set_socket(struct pcmcia_socket *sock, + socket_state_t *state) +{ + struct bcm63xx_pcmcia_socket *skt; + unsigned long flags; + u32 val; + + skt = sock->driver_data; + + spin_lock_irqsave(&skt->lock, flags); + + /* note: hardware cannot control socket power, so we will + * always report SS_POWERON */ + + /* apply socket reset */ + val = pcmcia_readl(skt, PCMCIA_C1_REG); + if (state->flags & SS_RESET) + val |= PCMCIA_C1_RESET_MASK; + else + val &= ~PCMCIA_C1_RESET_MASK; + + /* reverse reset logic for cardbus card */ + if (skt->card_detected && (skt->card_type & CARD_CARDBUS)) + val ^= PCMCIA_C1_RESET_MASK; + + pcmcia_writel(skt, val, PCMCIA_C1_REG); + + /* keep requested state for event reporting */ + skt->requested_state = *state; + + spin_unlock_irqrestore(&skt->lock, flags); + + return 0; +} + +/* + * identity cardtype from VS[12] input, CD[12] input while only VS2 is + * floating, and CD[12] input while only VS1 is floating + */ +enum { + IN_VS1 = (1 << 0), + IN_VS2 = (1 << 1), + IN_CD1_VS2H = (1 << 2), + IN_CD2_VS2H = (1 << 3), + IN_CD1_VS1H = (1 << 4), + IN_CD2_VS1H = (1 << 5), +}; + +static const u8 vscd_to_cardtype[] = { + + /* VS1 float, VS2 float */ + [IN_VS1 | IN_VS2] = (CARD_PCCARD | CARD_5V), + + /* VS1 grounded, VS2 float */ + [IN_VS2] = (CARD_PCCARD | CARD_5V | CARD_3V), + + /* VS1 grounded, VS2 grounded */ + [0] = (CARD_PCCARD | CARD_5V | CARD_3V | CARD_XV), + + /* VS1 tied to CD1, VS2 float */ + [IN_VS1 | IN_VS2 | IN_CD1_VS1H] = (CARD_CARDBUS | CARD_3V), + + /* VS1 grounded, VS2 tied to CD2 */ + [IN_VS2 | IN_CD2_VS2H] = (CARD_CARDBUS | CARD_3V | CARD_XV), + + /* VS1 tied to CD2, VS2 grounded */ + [IN_VS1 | IN_CD2_VS1H] = (CARD_CARDBUS | CARD_3V | CARD_XV | CARD_YV), + + /* VS1 float, VS2 grounded */ + [IN_VS1] = (CARD_PCCARD | CARD_XV), + + /* VS1 float, VS2 tied to CD2 */ + [IN_VS1 | IN_VS2 | IN_CD2_VS2H] = (CARD_CARDBUS | CARD_3V), + + /* VS1 float, VS2 tied to CD1 */ + [IN_VS1 | IN_VS2 | IN_CD1_VS2H] = (CARD_CARDBUS | CARD_XV | CARD_YV), + + /* VS1 tied to CD2, VS2 float */ + [IN_VS1 | IN_VS2 | IN_CD2_VS1H] = (CARD_CARDBUS | CARD_YV), + + /* VS2 grounded, VS1 is tied to CD1, CD2 is grounded */ + [IN_VS1 | IN_CD1_VS1H] = 0, /* ignore cardbay */ +}; + +/* + * poll hardware to check card insertion status + */ +static unsigned int __get_socket_status(struct bcm63xx_pcmcia_socket *skt) +{ + unsigned int stat; + u32 val; + + stat = 0; + + /* check CD for card presence */ + val = pcmcia_readl(skt, PCMCIA_C1_REG); + + if (!(val & PCMCIA_C1_CD1_MASK) && !(val & PCMCIA_C1_CD2_MASK)) + stat |= SS_DETECT; + + /* if new insertion, detect cardtype */ + if ((stat & SS_DETECT) && !skt->card_detected) { + unsigned int stat = 0; + + /* float VS1, float VS2 */ + val |= PCMCIA_C1_VS1OE_MASK; + val |= PCMCIA_C1_VS2OE_MASK; + pcmcia_writel(skt, val, PCMCIA_C1_REG); + + /* wait for output to stabilize and read VS[12] */ + udelay(10); + val = pcmcia_readl(skt, PCMCIA_C1_REG); + stat |= (val & PCMCIA_C1_VS1_MASK) ? IN_VS1 : 0; + stat |= (val & PCMCIA_C1_VS2_MASK) ? IN_VS2 : 0; + + /* drive VS1 low, float VS2 */ + val &= ~PCMCIA_C1_VS1OE_MASK; + val |= PCMCIA_C1_VS2OE_MASK; + pcmcia_writel(skt, val, PCMCIA_C1_REG); + + /* wait for output to stabilize and read CD[12] */ + udelay(10); + val = pcmcia_readl(skt, PCMCIA_C1_REG); + stat |= (val & PCMCIA_C1_CD1_MASK) ? IN_CD1_VS2H : 0; + stat |= (val & PCMCIA_C1_CD2_MASK) ? IN_CD2_VS2H : 0; + + /* float VS1, drive VS2 low */ + val |= PCMCIA_C1_VS1OE_MASK; + val &= ~PCMCIA_C1_VS2OE_MASK; + pcmcia_writel(skt, val, PCMCIA_C1_REG); + + /* wait for output to stabilize and read CD[12] */ + udelay(10); + val = pcmcia_readl(skt, PCMCIA_C1_REG); + stat |= (val & PCMCIA_C1_CD1_MASK) ? IN_CD1_VS1H : 0; + stat |= (val & PCMCIA_C1_CD2_MASK) ? IN_CD2_VS1H : 0; + + /* guess cardtype from all this */ + skt->card_type = vscd_to_cardtype[stat]; + if (!skt->card_type) + dev_err(&skt->socket.dev, "unsupported card type\n"); + + /* drive both VS pin to 0 again */ + val &= ~(PCMCIA_C1_VS1OE_MASK | PCMCIA_C1_VS2OE_MASK); + + /* enable correct logic */ + val &= ~(PCMCIA_C1_EN_PCMCIA_MASK | PCMCIA_C1_EN_CARDBUS_MASK); + if (skt->card_type & CARD_PCCARD) + val |= PCMCIA_C1_EN_PCMCIA_MASK; + else + val |= PCMCIA_C1_EN_CARDBUS_MASK; + + pcmcia_writel(skt, val, PCMCIA_C1_REG); + } + skt->card_detected = (stat & SS_DETECT) ? 1 : 0; + + /* report card type/voltage */ + if (skt->card_type & CARD_CARDBUS) + stat |= SS_CARDBUS; + if (skt->card_type & CARD_3V) + stat |= SS_3VCARD; + if (skt->card_type & CARD_XV) + stat |= SS_XVCARD; + stat |= SS_POWERON; + + if (gpio_get_value(skt->pd->ready_gpio)) + stat |= SS_READY; + + return stat; +} + +/* + * core request to get current socket status + */ +static int bcm63xx_pcmcia_get_status(struct pcmcia_socket *sock, + unsigned int *status) +{ + struct bcm63xx_pcmcia_socket *skt; + + skt = sock->driver_data; + + spin_lock_bh(&skt->lock); + *status = __get_socket_status(skt); + spin_unlock_bh(&skt->lock); + + return 0; +} + +/* + * socket polling timer callback + */ +static void bcm63xx_pcmcia_poll(unsigned long data) +{ + struct bcm63xx_pcmcia_socket *skt; + unsigned int stat, events; + + skt = (struct bcm63xx_pcmcia_socket *)data; + + spin_lock_bh(&skt->lock); + + stat = __get_socket_status(skt); + + /* keep only changed bits, and mask with required one from the + * core */ + events = (stat ^ skt->old_status) & skt->requested_state.csc_mask; + skt->old_status = stat; + spin_unlock_bh(&skt->lock); + + if (events) + pcmcia_parse_events(&skt->socket, events); + + mod_timer(&skt->timer, + jiffies + msecs_to_jiffies(BCM63XX_PCMCIA_POLL_RATE)); +} + +static int bcm63xx_pcmcia_set_io_map(struct pcmcia_socket *sock, + struct pccard_io_map *map) +{ + /* this doesn't seem to be called by pcmcia layer if static + * mapping is used */ + return 0; +} + +static int bcm63xx_pcmcia_set_mem_map(struct pcmcia_socket *sock, + struct pccard_mem_map *map) +{ + struct bcm63xx_pcmcia_socket *skt; + struct resource *res; + + skt = sock->driver_data; + if (map->flags & MAP_ATTRIB) + res = skt->attr_res; + else + res = skt->common_res; + + map->static_start = res->start + map->card_start; + return 0; +} + +static struct pccard_operations bcm63xx_pcmcia_operations = { + .init = bcm63xx_pcmcia_sock_init, + .suspend = bcm63xx_pcmcia_suspend, + .get_status = bcm63xx_pcmcia_get_status, + .set_socket = bcm63xx_pcmcia_set_socket, + .set_io_map = bcm63xx_pcmcia_set_io_map, + .set_mem_map = bcm63xx_pcmcia_set_mem_map, +}; + +/* + * register pcmcia socket to core + */ +static int __devinit bcm63xx_drv_pcmcia_probe(struct platform_device *pdev) +{ + struct bcm63xx_pcmcia_socket *skt; + struct pcmcia_socket *sock; + struct resource *res, *irq_res; + unsigned int regmem_size = 0, iomem_size = 0; + u32 val; + int ret; + + skt = kzalloc(sizeof(*skt), GFP_KERNEL); + if (!skt) + return -ENOMEM; + spin_lock_init(&skt->lock); + sock = &skt->socket; + sock->driver_data = skt; + + /* make sure we have all resources we need */ + skt->common_res = platform_get_resource(pdev, IORESOURCE_MEM, 1); + skt->attr_res = platform_get_resource(pdev, IORESOURCE_MEM, 2); + irq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + skt->pd = pdev->dev.platform_data; + if (!skt->common_res || !skt->attr_res || !irq_res || !skt->pd) { + ret = -EINVAL; + goto err; + } + + /* remap pcmcia registers */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + regmem_size = resource_size(res); + if (!request_mem_region(res->start, regmem_size, "bcm63xx_pcmcia")) { + ret = -EINVAL; + goto err; + } + skt->reg_res = res; + + skt->base = ioremap(res->start, regmem_size); + if (!skt->base) { + ret = -ENOMEM; + goto err; + } + + /* remap io registers */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 3); + iomem_size = resource_size(res); + skt->io_base = ioremap(res->start, iomem_size); + if (!skt->io_base) { + ret = -ENOMEM; + goto err; + } + + /* resources are static */ + sock->resource_ops = &pccard_static_ops; + sock->ops = &bcm63xx_pcmcia_operations; + sock->owner = THIS_MODULE; + sock->dev.parent = &pdev->dev; + sock->features = SS_CAP_STATIC_MAP | SS_CAP_PCCARD; + sock->io_offset = (unsigned long)skt->io_base; + sock->pci_irq = irq_res->start; + +#ifdef CONFIG_CARDBUS + sock->cb_dev = bcm63xx_cb_dev; + if (bcm63xx_cb_dev) + sock->features |= SS_CAP_CARDBUS; +#endif + + /* assume common & attribute memory have the same size */ + sock->map_size = resource_size(skt->common_res); + + /* initialize polling timer */ + setup_timer(&skt->timer, bcm63xx_pcmcia_poll, (unsigned long)skt); + + /* initialize pcmcia control register, drive VS[12] to 0, + * leave CB IDSEL to the old value since it is set by the PCI + * layer */ + val = pcmcia_readl(skt, PCMCIA_C1_REG); + val &= PCMCIA_C1_CBIDSEL_MASK; + val |= PCMCIA_C1_EN_PCMCIA_GPIO_MASK; + pcmcia_writel(skt, val, PCMCIA_C1_REG); + + /* + * Hardware has only one set of timings registers, not one for + * each memory access type, so we configure them for the + * slowest one: attribute memory. + */ + val = PCMCIA_C2_DATA16_MASK; + val |= 10 << PCMCIA_C2_RWCOUNT_SHIFT; + val |= 6 << PCMCIA_C2_INACTIVE_SHIFT; + val |= 3 << PCMCIA_C2_SETUP_SHIFT; + val |= 3 << PCMCIA_C2_HOLD_SHIFT; + pcmcia_writel(skt, val, PCMCIA_C2_REG); + + ret = pcmcia_register_socket(sock); + if (ret) + goto err; + + /* start polling socket */ + mod_timer(&skt->timer, + jiffies + msecs_to_jiffies(BCM63XX_PCMCIA_POLL_RATE)); + + platform_set_drvdata(pdev, skt); + return 0; + +err: + if (skt->io_base) + iounmap(skt->io_base); + if (skt->base) + iounmap(skt->base); + if (skt->reg_res) + release_mem_region(skt->reg_res->start, regmem_size); + kfree(skt); + return ret; +} + +static int __devexit bcm63xx_drv_pcmcia_remove(struct platform_device *pdev) +{ + struct bcm63xx_pcmcia_socket *skt; + struct resource *res; + + skt = platform_get_drvdata(pdev); + del_timer_sync(&skt->timer); + iounmap(skt->base); + iounmap(skt->io_base); + res = skt->reg_res; + release_mem_region(res->start, resource_size(res)); + kfree(skt); + return 0; +} + +struct platform_driver bcm63xx_pcmcia_driver = { + .probe = bcm63xx_drv_pcmcia_probe, + .remove = __devexit_p(bcm63xx_drv_pcmcia_remove), + .driver = { + .name = "bcm63xx_pcmcia", + .owner = THIS_MODULE, + }, +}; + +#ifdef CONFIG_CARDBUS +static int __devinit bcm63xx_cb_probe(struct pci_dev *dev, + const struct pci_device_id *id) +{ + /* keep pci device */ + bcm63xx_cb_dev = dev; + return platform_driver_register(&bcm63xx_pcmcia_driver); +} + +static void __devexit bcm63xx_cb_exit(struct pci_dev *dev) +{ + platform_driver_unregister(&bcm63xx_pcmcia_driver); + bcm63xx_cb_dev = NULL; +} + +static struct pci_device_id bcm63xx_cb_table[] = { + { + .vendor = PCI_VENDOR_ID_BROADCOM, + .device = BCM6348_CPU_ID, + .subvendor = PCI_VENDOR_ID_BROADCOM, + .subdevice = PCI_ANY_ID, + .class = PCI_CLASS_BRIDGE_CARDBUS << 8, + .class_mask = ~0, + }, + + { + .vendor = PCI_VENDOR_ID_BROADCOM, + .device = BCM6358_CPU_ID, + .subvendor = PCI_VENDOR_ID_BROADCOM, + .subdevice = PCI_ANY_ID, + .class = PCI_CLASS_BRIDGE_CARDBUS << 8, + .class_mask = ~0, + }, + + { }, +}; + +MODULE_DEVICE_TABLE(pci, bcm63xx_cb_table); + +static struct pci_driver bcm63xx_cardbus_driver = { + .name = "bcm63xx_cardbus", + .id_table = bcm63xx_cb_table, + .probe = bcm63xx_cb_probe, + .remove = __devexit_p(bcm63xx_cb_exit), +}; +#endif + +/* + * if cardbus support is enabled, register our platform device after + * our fake cardbus bridge has been registered + */ +static int __init bcm63xx_pcmcia_init(void) +{ +#ifdef CONFIG_CARDBUS + return pci_register_driver(&bcm63xx_cardbus_driver); +#else + return platform_driver_register(&bcm63xx_pcmcia_driver); +#endif +} + +static void __exit bcm63xx_pcmcia_exit(void) +{ +#ifdef CONFIG_CARDBUS + return pci_unregister_driver(&bcm63xx_cardbus_driver); +#else + platform_driver_unregister(&bcm63xx_pcmcia_driver); +#endif +} + +module_init(bcm63xx_pcmcia_init); +module_exit(bcm63xx_pcmcia_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Maxime Bizon <mbizon@freebox.fr>"); +MODULE_DESCRIPTION("Linux PCMCIA Card Services: bcm63xx Socket Controller"); diff --git a/drivers/pcmcia/bcm63xx_pcmcia.h b/drivers/pcmcia/bcm63xx_pcmcia.h new file mode 100644 index 000000000000..ed957399d863 --- /dev/null +++ b/drivers/pcmcia/bcm63xx_pcmcia.h @@ -0,0 +1,60 @@ +#ifndef BCM63XX_PCMCIA_H_ +#define BCM63XX_PCMCIA_H_ + +#include <linux/types.h> +#include <linux/timer.h> +#include <pcmcia/ss.h> +#include <bcm63xx_dev_pcmcia.h> + +/* socket polling rate in ms */ +#define BCM63XX_PCMCIA_POLL_RATE 500 + +enum { + CARD_CARDBUS = (1 << 0), + CARD_PCCARD = (1 << 1), + CARD_5V = (1 << 2), + CARD_3V = (1 << 3), + CARD_XV = (1 << 4), + CARD_YV = (1 << 5), +}; + +struct bcm63xx_pcmcia_socket { + struct pcmcia_socket socket; + + /* platform specific data */ + struct bcm63xx_pcmcia_platform_data *pd; + + /* all regs access are protected by this spinlock */ + spinlock_t lock; + + /* pcmcia registers resource */ + struct resource *reg_res; + + /* base remapped address of registers */ + void __iomem *base; + + /* whether a card is detected at the moment */ + int card_detected; + + /* type of detected card (mask of above enum) */ + u8 card_type; + + /* keep last socket status to implement event reporting */ + unsigned int old_status; + + /* backup of requested socket state */ + socket_state_t requested_state; + + /* timer used for socket status polling */ + struct timer_list timer; + + /* attribute/common memory resources */ + struct resource *attr_res; + struct resource *common_res; + struct resource *io_res; + + /* base address of io memory */ + void __iomem *io_base; +}; + +#endif /* BCM63XX_PCMCIA_H_ */ diff --git a/drivers/pcmcia/bfin_cf_pcmcia.c b/drivers/pcmcia/bfin_cf_pcmcia.c index b59d4115d20f..300b368605c9 100644 --- a/drivers/pcmcia/bfin_cf_pcmcia.c +++ b/drivers/pcmcia/bfin_cf_pcmcia.c @@ -302,7 +302,7 @@ static int __devexit bfin_cf_remove(struct platform_device *pdev) static int bfin_cf_suspend(struct platform_device *pdev, pm_message_t mesg) { - return pcmcia_socket_dev_suspend(&pdev->dev, mesg); + return pcmcia_socket_dev_suspend(&pdev->dev); } static int bfin_cf_resume(struct platform_device *pdev) diff --git a/drivers/pcmcia/cistpl.c b/drivers/pcmcia/cistpl.c index 4a110b7b2673..6c4a4fc83630 100644 --- a/drivers/pcmcia/cistpl.c +++ b/drivers/pcmcia/cistpl.c @@ -1463,7 +1463,9 @@ int pccard_read_tuple(struct pcmcia_socket *s, unsigned int function, cisdata_t return -ENOMEM; } tuple.DesiredTuple = code; - tuple.Attributes = TUPLE_RETURN_COMMON; + tuple.Attributes = 0; + if (function == BIND_FN_ALL) + tuple.Attributes = TUPLE_RETURN_COMMON; ret = pccard_get_first_tuple(s, function, &tuple); if (ret != 0) goto done; @@ -1490,7 +1492,7 @@ EXPORT_SYMBOL(pccard_read_tuple); ======================================================================*/ -int pccard_validate_cis(struct pcmcia_socket *s, unsigned int function, unsigned int *info) +int pccard_validate_cis(struct pcmcia_socket *s, unsigned int *info) { tuple_t *tuple; cisparse_t *p; @@ -1515,30 +1517,30 @@ int pccard_validate_cis(struct pcmcia_socket *s, unsigned int function, unsigned count = reserved = 0; tuple->DesiredTuple = RETURN_FIRST_TUPLE; tuple->Attributes = TUPLE_RETURN_COMMON; - ret = pccard_get_first_tuple(s, function, tuple); + ret = pccard_get_first_tuple(s, BIND_FN_ALL, tuple); if (ret != 0) goto done; /* First tuple should be DEVICE; we should really have either that or a CFTABLE_ENTRY of some sort */ if ((tuple->TupleCode == CISTPL_DEVICE) || - (pccard_read_tuple(s, function, CISTPL_CFTABLE_ENTRY, p) == 0) || - (pccard_read_tuple(s, function, CISTPL_CFTABLE_ENTRY_CB, p) == 0)) + (pccard_read_tuple(s, BIND_FN_ALL, CISTPL_CFTABLE_ENTRY, p) == 0) || + (pccard_read_tuple(s, BIND_FN_ALL, CISTPL_CFTABLE_ENTRY_CB, p) == 0)) dev_ok++; /* All cards should have a MANFID tuple, and/or a VERS_1 or VERS_2 tuple, for card identification. Certain old D-Link and Linksys cards have only a broken VERS_2 tuple; hence the bogus test. */ - if ((pccard_read_tuple(s, function, CISTPL_MANFID, p) == 0) || - (pccard_read_tuple(s, function, CISTPL_VERS_1, p) == 0) || - (pccard_read_tuple(s, function, CISTPL_VERS_2, p) != -ENOSPC)) + if ((pccard_read_tuple(s, BIND_FN_ALL, CISTPL_MANFID, p) == 0) || + (pccard_read_tuple(s, BIND_FN_ALL, CISTPL_VERS_1, p) == 0) || + (pccard_read_tuple(s, BIND_FN_ALL, CISTPL_VERS_2, p) != -ENOSPC)) ident_ok++; if (!dev_ok && !ident_ok) goto done; for (count = 1; count < MAX_TUPLES; count++) { - ret = pccard_get_next_tuple(s, function, tuple); + ret = pccard_get_next_tuple(s, BIND_FN_ALL, tuple); if (ret != 0) break; if (((tuple->TupleCode > 0x23) && (tuple->TupleCode < 0x40)) || diff --git a/drivers/pcmcia/cs.c b/drivers/pcmcia/cs.c index 0660ad182589..698d75cda084 100644 --- a/drivers/pcmcia/cs.c +++ b/drivers/pcmcia/cs.c @@ -98,10 +98,13 @@ EXPORT_SYMBOL(pcmcia_socket_list_rwsem); * These functions check for the appropriate struct pcmcia_soket arrays, * and pass them to the low-level functions pcmcia_{suspend,resume}_socket */ +static int socket_early_resume(struct pcmcia_socket *skt); +static int socket_late_resume(struct pcmcia_socket *skt); static int socket_resume(struct pcmcia_socket *skt); static int socket_suspend(struct pcmcia_socket *skt); -int pcmcia_socket_dev_suspend(struct device *dev, pm_message_t state) +static void pcmcia_socket_dev_run(struct device *dev, + int (*cb)(struct pcmcia_socket *)) { struct pcmcia_socket *socket; @@ -110,29 +113,34 @@ int pcmcia_socket_dev_suspend(struct device *dev, pm_message_t state) if (socket->dev.parent != dev) continue; mutex_lock(&socket->skt_mutex); - socket_suspend(socket); + cb(socket); mutex_unlock(&socket->skt_mutex); } up_read(&pcmcia_socket_list_rwsem); +} +int pcmcia_socket_dev_suspend(struct device *dev) +{ + pcmcia_socket_dev_run(dev, socket_suspend); return 0; } EXPORT_SYMBOL(pcmcia_socket_dev_suspend); -int pcmcia_socket_dev_resume(struct device *dev) +void pcmcia_socket_dev_early_resume(struct device *dev) { - struct pcmcia_socket *socket; + pcmcia_socket_dev_run(dev, socket_early_resume); +} +EXPORT_SYMBOL(pcmcia_socket_dev_early_resume); - down_read(&pcmcia_socket_list_rwsem); - list_for_each_entry(socket, &pcmcia_socket_list, socket_list) { - if (socket->dev.parent != dev) - continue; - mutex_lock(&socket->skt_mutex); - socket_resume(socket); - mutex_unlock(&socket->skt_mutex); - } - up_read(&pcmcia_socket_list_rwsem); +void pcmcia_socket_dev_late_resume(struct device *dev) +{ + pcmcia_socket_dev_run(dev, socket_late_resume); +} +EXPORT_SYMBOL(pcmcia_socket_dev_late_resume); +int pcmcia_socket_dev_resume(struct device *dev) +{ + pcmcia_socket_dev_run(dev, socket_resume); return 0; } EXPORT_SYMBOL(pcmcia_socket_dev_resume); @@ -546,29 +554,24 @@ static int socket_suspend(struct pcmcia_socket *skt) return 0; } -/* - * Resume a socket. If a card is present, verify its CIS against - * our cached copy. If they are different, the card has been - * replaced, and we need to tell the drivers. - */ -static int socket_resume(struct pcmcia_socket *skt) +static int socket_early_resume(struct pcmcia_socket *skt) { - int ret; - - if (!(skt->state & SOCKET_SUSPEND)) - return -EBUSY; - skt->socket = dead_socket; skt->ops->init(skt); skt->ops->set_socket(skt, &skt->socket); + if (skt->state & SOCKET_PRESENT) + skt->resume_status = socket_setup(skt, resume_delay); + return 0; +} +static int socket_late_resume(struct pcmcia_socket *skt) +{ if (!(skt->state & SOCKET_PRESENT)) { skt->state &= ~SOCKET_SUSPEND; return socket_insert(skt); } - ret = socket_setup(skt, resume_delay); - if (ret == 0) { + if (skt->resume_status == 0) { /* * FIXME: need a better check here for cardbus cards. */ @@ -596,6 +599,20 @@ static int socket_resume(struct pcmcia_socket *skt) return 0; } +/* + * Resume a socket. If a card is present, verify its CIS against + * our cached copy. If they are different, the card has been + * replaced, and we need to tell the drivers. + */ +static int socket_resume(struct pcmcia_socket *skt) +{ + if (!(skt->state & SOCKET_SUSPEND)) + return -EBUSY; + + socket_early_resume(skt); + return socket_late_resume(skt); +} + static void socket_remove(struct pcmcia_socket *skt) { dev_printk(KERN_NOTICE, &skt->dev, diff --git a/drivers/pcmcia/cs_internal.h b/drivers/pcmcia/cs_internal.h index 79615e6d540b..1f4098f1354d 100644 --- a/drivers/pcmcia/cs_internal.h +++ b/drivers/pcmcia/cs_internal.h @@ -197,8 +197,7 @@ int pccard_read_tuple(struct pcmcia_socket *s, unsigned int function, cisdata_t code, void *parse); int pcmcia_replace_cis(struct pcmcia_socket *s, const u8 *data, const size_t len); -int pccard_validate_cis(struct pcmcia_socket *s, unsigned int function, - unsigned int *count); +int pccard_validate_cis(struct pcmcia_socket *s, unsigned int *count); /* rsrc_mgr.c */ int pcmcia_validate_mem(struct pcmcia_socket *s); diff --git a/drivers/pcmcia/ds.c b/drivers/pcmcia/ds.c index 9f300d3cb125..f5b7079f13d3 100644 --- a/drivers/pcmcia/ds.c +++ b/drivers/pcmcia/ds.c @@ -547,7 +547,7 @@ static int pcmcia_device_query(struct pcmcia_device *p_dev) if (!vers1) return -ENOMEM; - if (!pccard_read_tuple(p_dev->socket, p_dev->func, + if (!pccard_read_tuple(p_dev->socket, BIND_FN_ALL, CISTPL_MANFID, &manf_id)) { p_dev->manf_id = manf_id.manf; p_dev->card_id = manf_id.card; @@ -581,9 +581,9 @@ static int pcmcia_device_query(struct pcmcia_device *p_dev) kfree(devgeo); } - if (!pccard_read_tuple(p_dev->socket, p_dev->func, CISTPL_VERS_1, + if (!pccard_read_tuple(p_dev->socket, BIND_FN_ALL, CISTPL_VERS_1, vers1)) { - for (i=0; i < vers1->ns; i++) { + for (i = 0; i < min_t(unsigned int, 4, vers1->ns); i++) { char *tmp; unsigned int length; @@ -733,7 +733,7 @@ static int pcmcia_card_add(struct pcmcia_socket *s) return -EAGAIN; /* try again, but later... */ } - ret = pccard_validate_cis(s, BIND_FN_ALL, &no_chains); + ret = pccard_validate_cis(s, &no_chains); if (ret || !no_chains) { ds_dev_dbg(0, &s->dev, "invalid CIS or invalid resources\n"); return -ENODEV; diff --git a/drivers/pcmcia/i82092.c b/drivers/pcmcia/i82092.c index 46561face128..a04f21c8170f 100644 --- a/drivers/pcmcia/i82092.c +++ b/drivers/pcmcia/i82092.c @@ -42,7 +42,7 @@ MODULE_DEVICE_TABLE(pci, i82092aa_pci_ids); #ifdef CONFIG_PM static int i82092aa_socket_suspend (struct pci_dev *dev, pm_message_t state) { - return pcmcia_socket_dev_suspend(&dev->dev, state); + return pcmcia_socket_dev_suspend(&dev->dev); } static int i82092aa_socket_resume (struct pci_dev *dev) diff --git a/drivers/pcmcia/i82365.c b/drivers/pcmcia/i82365.c index 40d4953e4b12..a4aacb830b80 100644 --- a/drivers/pcmcia/i82365.c +++ b/drivers/pcmcia/i82365.c @@ -1053,8 +1053,8 @@ static int i365_set_io_map(u_short sock, struct pccard_io_map *io) u_char map, ioctl; debug(1, "SetIOMap(%d, %d, %#2.2x, %d ns, " - "%#x-%#x)\n", sock, io->map, io->flags, - io->speed, io->start, io->stop); + "%#llx-%#llx)\n", sock, io->map, io->flags, io->speed, + (unsigned long long)io->start, (unsigned long long)io->stop); map = io->map; if ((map > 1) || (io->start > 0xffff) || (io->stop > 0xffff) || (io->stop < io->start)) return -EINVAL; @@ -1241,7 +1241,7 @@ static int pcic_init(struct pcmcia_socket *s) static int i82365_drv_pcmcia_suspend(struct platform_device *dev, pm_message_t state) { - return pcmcia_socket_dev_suspend(&dev->dev, state); + return pcmcia_socket_dev_suspend(&dev->dev); } static int i82365_drv_pcmcia_resume(struct platform_device *dev) diff --git a/drivers/pcmcia/m32r_cfc.c b/drivers/pcmcia/m32r_cfc.c index 62b4ecc97c46..7dfbee1dcd76 100644 --- a/drivers/pcmcia/m32r_cfc.c +++ b/drivers/pcmcia/m32r_cfc.c @@ -537,8 +537,9 @@ static int _pcc_set_io_map(u_short sock, struct pccard_io_map *io) u_char map; debug(3, "m32r_cfc: SetIOMap(%d, %d, %#2.2x, %d ns, " - "%#lx-%#lx)\n", sock, io->map, io->flags, - io->speed, io->start, io->stop); + "%#llx-%#llx)\n", sock, io->map, io->flags, + io->speed, (unsigned long long)io->start, + (unsigned long long)io->stop); map = io->map; return 0; @@ -554,8 +555,9 @@ static int _pcc_set_mem_map(u_short sock, struct pccard_mem_map *mem) pcc_socket_t *t = &socket[sock]; debug(3, "m32r_cfc: SetMemMap(%d, %d, %#2.2x, %d ns, " - "%#lx, %#x)\n", sock, map, mem->flags, - mem->speed, mem->static_start, mem->card_start); + "%#llx, %#x)\n", sock, map, mem->flags, + mem->speed, (unsigned long long)mem->static_start, + mem->card_start); /* * sanity check @@ -699,7 +701,7 @@ static struct pccard_operations pcc_operations = { static int cfc_drv_pcmcia_suspend(struct platform_device *dev, pm_message_t state) { - return pcmcia_socket_dev_suspend(&dev->dev, state); + return pcmcia_socket_dev_suspend(&dev->dev); } static int cfc_drv_pcmcia_resume(struct platform_device *dev) diff --git a/drivers/pcmcia/m32r_pcc.c b/drivers/pcmcia/m32r_pcc.c index 12034b41d196..c6524f99ccc3 100644 --- a/drivers/pcmcia/m32r_pcc.c +++ b/drivers/pcmcia/m32r_pcc.c @@ -492,8 +492,9 @@ static int _pcc_set_io_map(u_short sock, struct pccard_io_map *io) u_char map; debug(3, "m32r-pcc: SetIOMap(%d, %d, %#2.2x, %d ns, " - "%#x-%#x)\n", sock, io->map, io->flags, - io->speed, io->start, io->stop); + "%#llx-%#llx)\n", sock, io->map, io->flags, + io->speed, (unsigned long long)io->start, + (unsigned long long)io->stop); map = io->map; return 0; @@ -515,8 +516,9 @@ static int _pcc_set_mem_map(u_short sock, struct pccard_mem_map *mem) #endif debug(3, "m32r-pcc: SetMemMap(%d, %d, %#2.2x, %d ns, " - "%#lx, %#x)\n", sock, map, mem->flags, - mem->speed, mem->static_start, mem->card_start); + "%#llx, %#x)\n", sock, map, mem->flags, + mem->speed, (unsigned long long)mem->static_start, + mem->card_start); /* * sanity check @@ -675,7 +677,7 @@ static struct pccard_operations pcc_operations = { static int pcc_drv_pcmcia_suspend(struct platform_device *dev, pm_message_t state) { - return pcmcia_socket_dev_suspend(&dev->dev, state); + return pcmcia_socket_dev_suspend(&dev->dev); } static int pcc_drv_pcmcia_resume(struct platform_device *dev) diff --git a/drivers/pcmcia/m8xx_pcmcia.c b/drivers/pcmcia/m8xx_pcmcia.c index d1ad0966392d..403559ba49dd 100644 --- a/drivers/pcmcia/m8xx_pcmcia.c +++ b/drivers/pcmcia/m8xx_pcmcia.c @@ -975,8 +975,9 @@ static int m8xx_set_io_map(struct pcmcia_socket *sock, struct pccard_io_map *io) #define M8XX_BASE (PCMCIA_IO_WIN_BASE + io->start) dprintk("SetIOMap(%d, %d, %#2.2x, %d ns, " - "%#4.4x-%#4.4x)\n", lsock, io->map, io->flags, - io->speed, io->start, io->stop); + "%#4.4llx-%#4.4llx)\n", lsock, io->map, io->flags, + io->speed, (unsigned long long)io->start, + (unsigned long long)io->stop); if ((io->map >= PCMCIA_IO_WIN_NO) || (io->start > 0xffff) || (io->stop > 0xffff) || (io->stop < io->start)) @@ -1055,8 +1056,9 @@ static int m8xx_set_mem_map(struct pcmcia_socket *sock, pcmconf8xx_t *pcmcia = s->pcmcia; dprintk("SetMemMap(%d, %d, %#2.2x, %d ns, " - "%#5.5lx, %#5.5x)\n", lsock, mem->map, mem->flags, - mem->speed, mem->static_start, mem->card_start); + "%#5.5llx, %#5.5x)\n", lsock, mem->map, mem->flags, + mem->speed, (unsigned long long)mem->static_start, + mem->card_start); if ((mem->map >= PCMCIA_MEM_WIN_NO) // || ((mem->s) >= PCMCIA_MEM_WIN_SIZE) @@ -1107,8 +1109,9 @@ static int m8xx_set_mem_map(struct pcmcia_socket *sock, } dprintk("SetMemMap(%d, %d, %#2.2x, %d ns, " - "%#5.5lx, %#5.5x)\n", lsock, mem->map, mem->flags, - mem->speed, mem->static_start, mem->card_start); + "%#5.5llx, %#5.5x)\n", lsock, mem->map, mem->flags, + mem->speed, (unsigned long long)mem->static_start, + mem->card_start); /* copy the struct and modify the copy */ @@ -1296,7 +1299,7 @@ static int m8xx_remove(struct of_device *ofdev) #ifdef CONFIG_PM static int m8xx_suspend(struct platform_device *pdev, pm_message_t state) { - return pcmcia_socket_dev_suspend(&pdev->dev, state); + return pcmcia_socket_dev_suspend(&pdev->dev); } static int m8xx_resume(struct platform_device *pdev) diff --git a/drivers/pcmcia/omap_cf.c b/drivers/pcmcia/omap_cf.c index f3736398900e..68570bc3ac86 100644 --- a/drivers/pcmcia/omap_cf.c +++ b/drivers/pcmcia/omap_cf.c @@ -334,7 +334,7 @@ static int __exit omap_cf_remove(struct platform_device *pdev) static int omap_cf_suspend(struct platform_device *pdev, pm_message_t mesg) { - return pcmcia_socket_dev_suspend(&pdev->dev, mesg); + return pcmcia_socket_dev_suspend(&pdev->dev); } static int omap_cf_resume(struct platform_device *pdev) diff --git a/drivers/pcmcia/pcmcia_ioctl.c b/drivers/pcmcia/pcmcia_ioctl.c index 32c44040c1e8..30cf71d2ee23 100644 --- a/drivers/pcmcia/pcmcia_ioctl.c +++ b/drivers/pcmcia/pcmcia_ioctl.c @@ -881,7 +881,7 @@ static int ds_ioctl(struct inode * inode, struct file * file, mutex_lock(&s->skt_mutex); pcmcia_validate_mem(s); mutex_unlock(&s->skt_mutex); - ret = pccard_validate_cis(s, BIND_FN_ALL, &buf->cisinfo.Chains); + ret = pccard_validate_cis(s, &buf->cisinfo.Chains); break; case DS_SUSPEND_CARD: ret = pcmcia_suspend_card(s); diff --git a/drivers/pcmcia/pd6729.c b/drivers/pcmcia/pd6729.c index 8bed1dab9039..70a33468bcd0 100644 --- a/drivers/pcmcia/pd6729.c +++ b/drivers/pcmcia/pd6729.c @@ -641,6 +641,12 @@ static int __devinit pd6729_pci_probe(struct pci_dev *dev, if ((ret = pci_enable_device(dev))) goto err_out_free_mem; + if (!pci_resource_start(dev, 0)) { + printk(KERN_INFO "pd6729: refusing to load the driver " + "as the io_base is 0.\n"); + goto err_out_free_mem; + } + printk(KERN_INFO "pd6729: Cirrus PD6729 PCI to PCMCIA Bridge " "at 0x%llx on irq %d\n", (unsigned long long)pci_resource_start(dev, 0), dev->irq); @@ -758,7 +764,7 @@ static void __devexit pd6729_pci_remove(struct pci_dev *dev) #ifdef CONFIG_PM static int pd6729_socket_suspend(struct pci_dev *dev, pm_message_t state) { - return pcmcia_socket_dev_suspend(&dev->dev, state); + return pcmcia_socket_dev_suspend(&dev->dev); } static int pd6729_socket_resume(struct pci_dev *dev) diff --git a/drivers/pcmcia/pxa2xx_base.c b/drivers/pcmcia/pxa2xx_base.c index c49a7269f6d1..0e35acb1366b 100644 --- a/drivers/pcmcia/pxa2xx_base.c +++ b/drivers/pcmcia/pxa2xx_base.c @@ -300,25 +300,29 @@ static int pxa2xx_drv_pcmcia_remove(struct platform_device *dev) return soc_common_drv_pcmcia_remove(&dev->dev); } -static int pxa2xx_drv_pcmcia_suspend(struct platform_device *dev, pm_message_t state) +static int pxa2xx_drv_pcmcia_suspend(struct device *dev) { - return pcmcia_socket_dev_suspend(&dev->dev, state); + return pcmcia_socket_dev_suspend(dev); } -static int pxa2xx_drv_pcmcia_resume(struct platform_device *dev) +static int pxa2xx_drv_pcmcia_resume(struct device *dev) { - pxa2xx_configure_sockets(&dev->dev); - return pcmcia_socket_dev_resume(&dev->dev); + pxa2xx_configure_sockets(dev); + return pcmcia_socket_dev_resume(dev); } +static struct dev_pm_ops pxa2xx_drv_pcmcia_pm_ops = { + .suspend = pxa2xx_drv_pcmcia_suspend, + .resume = pxa2xx_drv_pcmcia_resume, +}; + static struct platform_driver pxa2xx_pcmcia_driver = { .probe = pxa2xx_drv_pcmcia_probe, .remove = pxa2xx_drv_pcmcia_remove, - .suspend = pxa2xx_drv_pcmcia_suspend, - .resume = pxa2xx_drv_pcmcia_resume, .driver = { .name = "pxa2xx-pcmcia", .owner = THIS_MODULE, + .pm = &pxa2xx_drv_pcmcia_pm_ops, }, }; diff --git a/drivers/pcmcia/pxa2xx_palmtc.c b/drivers/pcmcia/pxa2xx_palmtc.c new file mode 100644 index 000000000000..3a8993ed5621 --- /dev/null +++ b/drivers/pcmcia/pxa2xx_palmtc.c @@ -0,0 +1,230 @@ +/* + * linux/drivers/pcmcia/pxa2xx_palmtc.c + * + * Driver for Palm Tungsten|C PCMCIA + * + * Copyright (C) 2008 Alex Osborne <ato@meshy.org> + * Copyright (C) 2009 Marek Vasut <marek.vasut@gmail.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/gpio.h> +#include <linux/delay.h> + +#include <asm/mach-types.h> +#include <mach/palmtc.h> +#include "soc_common.h" + +static int palmtc_pcmcia_hw_init(struct soc_pcmcia_socket *skt) +{ + int ret; + + ret = gpio_request(GPIO_NR_PALMTC_PCMCIA_POWER1, "PCMCIA PWR1"); + if (ret) + goto err1; + ret = gpio_direction_output(GPIO_NR_PALMTC_PCMCIA_POWER1, 0); + if (ret) + goto err2; + + ret = gpio_request(GPIO_NR_PALMTC_PCMCIA_POWER2, "PCMCIA PWR2"); + if (ret) + goto err2; + ret = gpio_direction_output(GPIO_NR_PALMTC_PCMCIA_POWER2, 0); + if (ret) + goto err3; + + ret = gpio_request(GPIO_NR_PALMTC_PCMCIA_POWER3, "PCMCIA PWR3"); + if (ret) + goto err3; + ret = gpio_direction_output(GPIO_NR_PALMTC_PCMCIA_POWER3, 0); + if (ret) + goto err4; + + ret = gpio_request(GPIO_NR_PALMTC_PCMCIA_RESET, "PCMCIA RST"); + if (ret) + goto err4; + ret = gpio_direction_output(GPIO_NR_PALMTC_PCMCIA_RESET, 1); + if (ret) + goto err5; + + ret = gpio_request(GPIO_NR_PALMTC_PCMCIA_READY, "PCMCIA RDY"); + if (ret) + goto err5; + ret = gpio_direction_input(GPIO_NR_PALMTC_PCMCIA_READY); + if (ret) + goto err6; + + ret = gpio_request(GPIO_NR_PALMTC_PCMCIA_PWRREADY, "PCMCIA PWRRDY"); + if (ret) + goto err6; + ret = gpio_direction_input(GPIO_NR_PALMTC_PCMCIA_PWRREADY); + if (ret) + goto err7; + + skt->irq = IRQ_GPIO(GPIO_NR_PALMTC_PCMCIA_READY); + return 0; + +err7: + gpio_free(GPIO_NR_PALMTC_PCMCIA_PWRREADY); +err6: + gpio_free(GPIO_NR_PALMTC_PCMCIA_READY); +err5: + gpio_free(GPIO_NR_PALMTC_PCMCIA_RESET); +err4: + gpio_free(GPIO_NR_PALMTC_PCMCIA_POWER3); +err3: + gpio_free(GPIO_NR_PALMTC_PCMCIA_POWER2); +err2: + gpio_free(GPIO_NR_PALMTC_PCMCIA_POWER1); +err1: + return ret; +} + +static void palmtc_pcmcia_hw_shutdown(struct soc_pcmcia_socket *skt) +{ + gpio_free(GPIO_NR_PALMTC_PCMCIA_PWRREADY); + gpio_free(GPIO_NR_PALMTC_PCMCIA_READY); + gpio_free(GPIO_NR_PALMTC_PCMCIA_RESET); + gpio_free(GPIO_NR_PALMTC_PCMCIA_POWER3); + gpio_free(GPIO_NR_PALMTC_PCMCIA_POWER2); + gpio_free(GPIO_NR_PALMTC_PCMCIA_POWER1); +} + +static void palmtc_pcmcia_socket_state(struct soc_pcmcia_socket *skt, + struct pcmcia_state *state) +{ + state->detect = 1; /* always inserted */ + state->ready = !!gpio_get_value(GPIO_NR_PALMTC_PCMCIA_READY); + state->bvd1 = 1; + state->bvd2 = 1; + state->wrprot = 0; + state->vs_3v = 1; + state->vs_Xv = 0; +} + +static int palmtc_wifi_powerdown(void) +{ + gpio_set_value(GPIO_NR_PALMTC_PCMCIA_RESET, 1); + gpio_set_value(GPIO_NR_PALMTC_PCMCIA_POWER2, 0); + mdelay(40); + gpio_set_value(GPIO_NR_PALMTC_PCMCIA_POWER1, 0); + return 0; +} + +static int palmtc_wifi_powerup(void) +{ + int timeout = 50; + + gpio_set_value(GPIO_NR_PALMTC_PCMCIA_POWER3, 1); + mdelay(50); + + /* Power up the card, 1.8V first, after a while 3.3V */ + gpio_set_value(GPIO_NR_PALMTC_PCMCIA_POWER1, 1); + mdelay(100); + gpio_set_value(GPIO_NR_PALMTC_PCMCIA_POWER2, 1); + + /* Wait till the card is ready */ + while (!gpio_get_value(GPIO_NR_PALMTC_PCMCIA_PWRREADY) && + timeout) { + mdelay(1); + timeout--; + } + + /* Power down the WiFi in case of error */ + if (!timeout) { + palmtc_wifi_powerdown(); + return 1; + } + + /* Reset the card */ + gpio_set_value(GPIO_NR_PALMTC_PCMCIA_RESET, 1); + mdelay(20); + gpio_set_value(GPIO_NR_PALMTC_PCMCIA_RESET, 0); + mdelay(25); + + gpio_set_value(GPIO_NR_PALMTC_PCMCIA_POWER3, 0); + + return 0; +} + +static int palmtc_pcmcia_configure_socket(struct soc_pcmcia_socket *skt, + const socket_state_t *state) +{ + int ret = 1; + + if (state->Vcc == 0) + ret = palmtc_wifi_powerdown(); + else if (state->Vcc == 33) + ret = palmtc_wifi_powerup(); + + return ret; +} + +static void palmtc_pcmcia_socket_init(struct soc_pcmcia_socket *skt) +{ +} + +static void palmtc_pcmcia_socket_suspend(struct soc_pcmcia_socket *skt) +{ +} + +static struct pcmcia_low_level palmtc_pcmcia_ops = { + .owner = THIS_MODULE, + + .first = 0, + .nr = 1, + + .hw_init = palmtc_pcmcia_hw_init, + .hw_shutdown = palmtc_pcmcia_hw_shutdown, + + .socket_state = palmtc_pcmcia_socket_state, + .configure_socket = palmtc_pcmcia_configure_socket, + + .socket_init = palmtc_pcmcia_socket_init, + .socket_suspend = palmtc_pcmcia_socket_suspend, +}; + +static struct platform_device *palmtc_pcmcia_device; + +static int __init palmtc_pcmcia_init(void) +{ + int ret; + + if (!machine_is_palmtc()) + return -ENODEV; + + palmtc_pcmcia_device = platform_device_alloc("pxa2xx-pcmcia", -1); + if (!palmtc_pcmcia_device) + return -ENOMEM; + + ret = platform_device_add_data(palmtc_pcmcia_device, &palmtc_pcmcia_ops, + sizeof(palmtc_pcmcia_ops)); + + if (!ret) + ret = platform_device_add(palmtc_pcmcia_device); + + if (ret) + platform_device_put(palmtc_pcmcia_device); + + return ret; +} + +static void __exit palmtc_pcmcia_exit(void) +{ + platform_device_unregister(palmtc_pcmcia_device); +} + +module_init(palmtc_pcmcia_init); +module_exit(palmtc_pcmcia_exit); + +MODULE_AUTHOR("Alex Osborne <ato@meshy.org>," + " Marek Vasut <marek.vasut@gmail.com>"); +MODULE_DESCRIPTION("PCMCIA support for Palm Tungsten|C"); +MODULE_ALIAS("platform:pxa2xx-pcmcia"); +MODULE_LICENSE("GPL"); diff --git a/drivers/pcmcia/rsrc_nonstatic.c b/drivers/pcmcia/rsrc_nonstatic.c index 9ca22c7aafb2..7039f3cf5b77 100644 --- a/drivers/pcmcia/rsrc_nonstatic.c +++ b/drivers/pcmcia/rsrc_nonstatic.c @@ -206,6 +206,7 @@ static void do_io_probe(struct pcmcia_socket *s, unsigned int base, /* First, what does a floating port look like? */ b = kzalloc(256, GFP_KERNEL); if (!b) { + printk("\n"); dev_printk(KERN_ERR, &s->dev, "do_io_probe: unable to kmalloc 256 bytes"); return; @@ -275,7 +276,7 @@ static int readable(struct pcmcia_socket *s, struct resource *res, s->cis_mem.res = res; s->cis_virt = ioremap(res->start, s->map_size); if (s->cis_virt) { - ret = pccard_validate_cis(s, BIND_FN_ALL, count); + ret = pccard_validate_cis(s, count); /* invalidate mapping and CIS cache */ iounmap(s->cis_virt); s->cis_virt = NULL; diff --git a/drivers/pcmcia/sa1100_assabet.c b/drivers/pcmcia/sa1100_assabet.c index f424146a2bc9..ac8aa09ba0da 100644 --- a/drivers/pcmcia/sa1100_assabet.c +++ b/drivers/pcmcia/sa1100_assabet.c @@ -130,7 +130,7 @@ static struct pcmcia_low_level assabet_pcmcia_ops = { .socket_suspend = assabet_pcmcia_socket_suspend, }; -int __init pcmcia_assabet_init(struct device *dev) +int pcmcia_assabet_init(struct device *dev) { int ret = -ENODEV; diff --git a/drivers/pcmcia/sa1100_generic.c b/drivers/pcmcia/sa1100_generic.c index d8da5ac844e9..2d0e99751530 100644 --- a/drivers/pcmcia/sa1100_generic.c +++ b/drivers/pcmcia/sa1100_generic.c @@ -89,7 +89,7 @@ static int sa11x0_drv_pcmcia_remove(struct platform_device *dev) static int sa11x0_drv_pcmcia_suspend(struct platform_device *dev, pm_message_t state) { - return pcmcia_socket_dev_suspend(&dev->dev, state); + return pcmcia_socket_dev_suspend(&dev->dev); } static int sa11x0_drv_pcmcia_resume(struct platform_device *dev) diff --git a/drivers/pcmcia/sa1100_neponset.c b/drivers/pcmcia/sa1100_neponset.c index 4c41e86ccff9..0c76d337815b 100644 --- a/drivers/pcmcia/sa1100_neponset.c +++ b/drivers/pcmcia/sa1100_neponset.c @@ -123,7 +123,7 @@ static struct pcmcia_low_level neponset_pcmcia_ops = { .socket_suspend = sa1111_pcmcia_socket_suspend, }; -int __init pcmcia_neponset_init(struct sa1111_dev *sadev) +int pcmcia_neponset_init(struct sa1111_dev *sadev) { int ret = -ENODEV; diff --git a/drivers/pcmcia/sa1111_generic.c b/drivers/pcmcia/sa1111_generic.c index 401052a21ce8..4be4e172ffa1 100644 --- a/drivers/pcmcia/sa1111_generic.c +++ b/drivers/pcmcia/sa1111_generic.c @@ -159,7 +159,7 @@ static int __devexit pcmcia_remove(struct sa1111_dev *dev) static int pcmcia_suspend(struct sa1111_dev *dev, pm_message_t state) { - return pcmcia_socket_dev_suspend(&dev->dev, state); + return pcmcia_socket_dev_suspend(&dev->dev); } static int pcmcia_resume(struct sa1111_dev *dev) diff --git a/drivers/pcmcia/soc_common.c b/drivers/pcmcia/soc_common.c index 163cf98e2386..ef7e9e58782b 100644 --- a/drivers/pcmcia/soc_common.c +++ b/drivers/pcmcia/soc_common.c @@ -336,8 +336,9 @@ soc_common_pcmcia_set_io_map(struct pcmcia_socket *sock, struct pccard_io_map *m struct soc_pcmcia_socket *skt = to_soc_pcmcia_socket(sock); unsigned short speed = map->speed; - debug(skt, 2, "map %u speed %u start 0x%08x stop 0x%08x\n", - map->map, map->speed, map->start, map->stop); + debug(skt, 2, "map %u speed %u start 0x%08llx stop 0x%08llx\n", + map->map, map->speed, (unsigned long long)map->start, + (unsigned long long)map->stop); debug(skt, 2, "flags: %s%s%s%s%s%s%s%s\n", (map->flags==0)?"<NONE>":"", (map->flags&MAP_ACTIVE)?"ACTIVE ":"", diff --git a/drivers/pcmcia/socket_sysfs.c b/drivers/pcmcia/socket_sysfs.c index ff9a3bb3c88d..78d5aab542f7 100644 --- a/drivers/pcmcia/socket_sysfs.c +++ b/drivers/pcmcia/socket_sysfs.c @@ -300,7 +300,7 @@ static ssize_t pccard_show_cis(struct kobject *kobj, if (!(s->state & SOCKET_PRESENT)) return -ENODEV; - if (pccard_validate_cis(s, BIND_FN_ALL, &chains)) + if (pccard_validate_cis(s, &chains)) return -EIO; if (!chains) return -ENODATA; diff --git a/drivers/pcmcia/tcic.c b/drivers/pcmcia/tcic.c index 8eb04230fec7..6918849d511e 100644 --- a/drivers/pcmcia/tcic.c +++ b/drivers/pcmcia/tcic.c @@ -366,7 +366,7 @@ static int __init get_tcic_id(void) static int tcic_drv_pcmcia_suspend(struct platform_device *dev, pm_message_t state) { - return pcmcia_socket_dev_suspend(&dev->dev, state); + return pcmcia_socket_dev_suspend(&dev->dev); } static int tcic_drv_pcmcia_resume(struct platform_device *dev) @@ -732,8 +732,8 @@ static int tcic_set_io_map(struct pcmcia_socket *sock, struct pccard_io_map *io) u_short base, len, ioctl; debug(1, "SetIOMap(%d, %d, %#2.2x, %d ns, " - "%#x-%#x)\n", psock, io->map, io->flags, - io->speed, io->start, io->stop); + "%#llx-%#llx)\n", psock, io->map, io->flags, io->speed, + (unsigned long long)io->start, (unsigned long long)io->stop); if ((io->map > 1) || (io->start > 0xffff) || (io->stop > 0xffff) || (io->stop < io->start)) return -EINVAL; tcic_setw(TCIC_ADDR+2, TCIC_ADR2_INDREG | (psock << TCIC_SS_SHFT)); diff --git a/drivers/pcmcia/vrc4171_card.c b/drivers/pcmcia/vrc4171_card.c index d4ad50d737b0..c9fcbdc164ea 100644 --- a/drivers/pcmcia/vrc4171_card.c +++ b/drivers/pcmcia/vrc4171_card.c @@ -707,7 +707,7 @@ __setup("vrc4171_card=", vrc4171_card_setup); static int vrc4171_card_suspend(struct platform_device *dev, pm_message_t state) { - return pcmcia_socket_dev_suspend(&dev->dev, state); + return pcmcia_socket_dev_suspend(&dev->dev); } static int vrc4171_card_resume(struct platform_device *dev) diff --git a/drivers/pcmcia/yenta_socket.c b/drivers/pcmcia/yenta_socket.c index b459e87a30ac..8be4cc447a17 100644 --- a/drivers/pcmcia/yenta_socket.c +++ b/drivers/pcmcia/yenta_socket.c @@ -1225,60 +1225,81 @@ static int __devinit yenta_probe (struct pci_dev *dev, const struct pci_device_i } #ifdef CONFIG_PM -static int yenta_dev_suspend (struct pci_dev *dev, pm_message_t state) +static int yenta_dev_suspend_noirq(struct device *dev) { - struct yenta_socket *socket = pci_get_drvdata(dev); + struct pci_dev *pdev = to_pci_dev(dev); + struct yenta_socket *socket = pci_get_drvdata(pdev); int ret; - ret = pcmcia_socket_dev_suspend(&dev->dev, state); + ret = pcmcia_socket_dev_suspend(dev); - if (socket) { - if (socket->type && socket->type->save_state) - socket->type->save_state(socket); + if (!socket) + return ret; - /* FIXME: pci_save_state needs to have a better interface */ - pci_save_state(dev); - pci_read_config_dword(dev, 16*4, &socket->saved_state[0]); - pci_read_config_dword(dev, 17*4, &socket->saved_state[1]); - pci_disable_device(dev); + if (socket->type && socket->type->save_state) + socket->type->save_state(socket); - /* - * Some laptops (IBM T22) do not like us putting the Cardbus - * bridge into D3. At a guess, some other laptop will - * probably require this, so leave it commented out for now. - */ - /* pci_set_power_state(dev, 3); */ - } + pci_save_state(pdev); + pci_read_config_dword(pdev, 16*4, &socket->saved_state[0]); + pci_read_config_dword(pdev, 17*4, &socket->saved_state[1]); + pci_disable_device(pdev); + + /* + * Some laptops (IBM T22) do not like us putting the Cardbus + * bridge into D3. At a guess, some other laptop will + * probably require this, so leave it commented out for now. + */ + /* pci_set_power_state(dev, 3); */ return ret; } - -static int yenta_dev_resume (struct pci_dev *dev) +static int yenta_dev_resume_noirq(struct device *dev) { - struct yenta_socket *socket = pci_get_drvdata(dev); + struct pci_dev *pdev = to_pci_dev(dev); + struct yenta_socket *socket = pci_get_drvdata(pdev); + int ret; - if (socket) { - int rc; + if (!socket) + return 0; - pci_set_power_state(dev, 0); - /* FIXME: pci_restore_state needs to have a better interface */ - pci_restore_state(dev); - pci_write_config_dword(dev, 16*4, socket->saved_state[0]); - pci_write_config_dword(dev, 17*4, socket->saved_state[1]); + pci_write_config_dword(pdev, 16*4, socket->saved_state[0]); + pci_write_config_dword(pdev, 17*4, socket->saved_state[1]); - rc = pci_enable_device(dev); - if (rc) - return rc; + ret = pci_enable_device(pdev); + if (ret) + return ret; - pci_set_master(dev); + pci_set_master(pdev); - if (socket->type && socket->type->restore_state) - socket->type->restore_state(socket); - } + if (socket->type && socket->type->restore_state) + socket->type->restore_state(socket); + + pcmcia_socket_dev_early_resume(dev); + return 0; +} - return pcmcia_socket_dev_resume(&dev->dev); +static int yenta_dev_resume(struct device *dev) +{ + pcmcia_socket_dev_late_resume(dev); + return 0; } + +static struct dev_pm_ops yenta_pm_ops = { + .suspend_noirq = yenta_dev_suspend_noirq, + .resume_noirq = yenta_dev_resume_noirq, + .resume = yenta_dev_resume, + .freeze_noirq = yenta_dev_suspend_noirq, + .thaw_noirq = yenta_dev_resume_noirq, + .thaw = yenta_dev_resume, + .poweroff_noirq = yenta_dev_suspend_noirq, + .restore_noirq = yenta_dev_resume_noirq, + .restore = yenta_dev_resume, +}; + +#define YENTA_PM_OPS (¥ta_pm_ops) +#else +#define YENTA_PM_OPS NULL #endif #define CB_ID(vend,dev,type) \ @@ -1376,10 +1397,7 @@ static struct pci_driver yenta_cardbus_driver = { .id_table = yenta_table, .probe = yenta_probe, .remove = __devexit_p(yenta_close), -#ifdef CONFIG_PM - .suspend = yenta_dev_suspend, - .resume = yenta_dev_resume, -#endif + .driver.pm = YENTA_PM_OPS, }; diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index 77c6097ced80..55ca39dea42e 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig @@ -99,6 +99,7 @@ config FUJITSU_LAPTOP depends on ACPI depends on INPUT depends on BACKLIGHT_CLASS_DEVICE + depends on LEDS_CLASS || LEDS_CLASS=n ---help--- This is a driver for laptops built by Fujitsu: @@ -396,6 +397,15 @@ config ACPI_ASUS NOTE: This driver is deprecated and will probably be removed soon, use asus-laptop instead. +config TOPSTAR_LAPTOP + tristate "Topstar Laptop Extras" + depends on ACPI + depends on INPUT + ---help--- + This driver adds support for hotkeys found on Topstar laptops. + + If you have a Topstar laptop, say Y or M here. + config ACPI_TOSHIBA tristate "Toshiba Laptop Extras" depends on ACPI diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile index 641b8bfa5538..d1c16210a512 100644 --- a/drivers/platform/x86/Makefile +++ b/drivers/platform/x86/Makefile @@ -19,4 +19,5 @@ obj-$(CONFIG_PANASONIC_LAPTOP) += panasonic-laptop.o obj-$(CONFIG_INTEL_MENLOW) += intel_menlow.o obj-$(CONFIG_ACPI_WMI) += wmi.o obj-$(CONFIG_ACPI_ASUS) += asus_acpi.o +obj-$(CONFIG_TOPSTAR_LAPTOP) += topstar-laptop.o obj-$(CONFIG_ACPI_TOSHIBA) += toshiba_acpi.o diff --git a/drivers/platform/x86/acer-wmi.c b/drivers/platform/x86/acer-wmi.c index fb45f5ee8df1..454970d2d701 100644 --- a/drivers/platform/x86/acer-wmi.c +++ b/drivers/platform/x86/acer-wmi.c @@ -746,7 +746,9 @@ static acpi_status WMID_set_u32(u32 value, u32 cap, struct wmi_interface *iface) return AE_BAD_PARAMETER; if (quirks->mailled == 1) { param = value ? 0x92 : 0x93; + i8042_lock_chip(); i8042_command(¶m, 0x1059); + i8042_unlock_chip(); return 0; } break; diff --git a/drivers/platform/x86/acerhdf.c b/drivers/platform/x86/acerhdf.c index bdfee177eefb..ab64522aaa64 100644 --- a/drivers/platform/x86/acerhdf.c +++ b/drivers/platform/x86/acerhdf.c @@ -52,7 +52,7 @@ */ #undef START_IN_KERNEL_MODE -#define DRV_VER "0.5.13" +#define DRV_VER "0.5.18" /* * According to the Atom N270 datasheet, @@ -61,7 +61,7 @@ * measured by the on-die thermal monitor are within 0 <= Tj <= 90. So, * assume 89°C is critical temperature. */ -#define ACERHDF_TEMP_CRIT 89 +#define ACERHDF_TEMP_CRIT 89000 #define ACERHDF_FAN_OFF 0 #define ACERHDF_FAN_AUTO 1 @@ -69,7 +69,7 @@ * No matter what value the user puts into the fanon variable, turn on the fan * at 80 degree Celsius to prevent hardware damage */ -#define ACERHDF_MAX_FANON 80 +#define ACERHDF_MAX_FANON 80000 /* * Maximum interval between two temperature checks is 15 seconds, as the die @@ -85,11 +85,12 @@ static int kernelmode; #endif static unsigned int interval = 10; -static unsigned int fanon = 63; -static unsigned int fanoff = 58; +static unsigned int fanon = 63000; +static unsigned int fanoff = 58000; static unsigned int verbose; static unsigned int fanstate = ACERHDF_FAN_AUTO; static char force_bios[16]; +static char force_product[16]; static unsigned int prev_interval; struct thermal_zone_device *thz_dev; struct thermal_cooling_device *cl_dev; @@ -107,34 +108,62 @@ module_param(verbose, uint, 0600); MODULE_PARM_DESC(verbose, "Enable verbose dmesg output"); module_param_string(force_bios, force_bios, 16, 0); MODULE_PARM_DESC(force_bios, "Force BIOS version and omit BIOS check"); +module_param_string(force_product, force_product, 16, 0); +MODULE_PARM_DESC(force_product, "Force BIOS product and omit BIOS check"); + +/* + * cmd_off: to switch the fan completely off / to check if the fan is off + * cmd_auto: to set the BIOS in control of the fan. The BIOS regulates then + * the fan speed depending on the temperature + */ +struct fancmd { + u8 cmd_off; + u8 cmd_auto; +}; /* BIOS settings */ struct bios_settings_t { const char *vendor; + const char *product; const char *version; unsigned char fanreg; unsigned char tempreg; - unsigned char fancmd[2]; /* fan off and auto commands */ + struct fancmd cmd; }; /* Register addresses and values for different BIOS versions */ static const struct bios_settings_t bios_tbl[] = { - {"Acer", "v0.3109", 0x55, 0x58, {0x1f, 0x00} }, - {"Acer", "v0.3114", 0x55, 0x58, {0x1f, 0x00} }, - {"Acer", "v0.3301", 0x55, 0x58, {0xaf, 0x00} }, - {"Acer", "v0.3304", 0x55, 0x58, {0xaf, 0x00} }, - {"Acer", "v0.3305", 0x55, 0x58, {0xaf, 0x00} }, - {"Acer", "v0.3308", 0x55, 0x58, {0x21, 0x00} }, - {"Acer", "v0.3309", 0x55, 0x58, {0x21, 0x00} }, - {"Acer", "v0.3310", 0x55, 0x58, {0x21, 0x00} }, - {"Gateway", "v0.3103", 0x55, 0x58, {0x21, 0x00} }, - {"Packard Bell", "v0.3105", 0x55, 0x58, {0x21, 0x00} }, - {"", "", 0, 0, {0, 0} } + /* AOA110 */ + {"Acer", "AOA110", "v0.3109", 0x55, 0x58, {0x1f, 0x00} }, + {"Acer", "AOA110", "v0.3114", 0x55, 0x58, {0x1f, 0x00} }, + {"Acer", "AOA110", "v0.3301", 0x55, 0x58, {0xaf, 0x00} }, + {"Acer", "AOA110", "v0.3304", 0x55, 0x58, {0xaf, 0x00} }, + {"Acer", "AOA110", "v0.3305", 0x55, 0x58, {0xaf, 0x00} }, + {"Acer", "AOA110", "v0.3307", 0x55, 0x58, {0xaf, 0x00} }, + {"Acer", "AOA110", "v0.3308", 0x55, 0x58, {0x21, 0x00} }, + {"Acer", "AOA110", "v0.3309", 0x55, 0x58, {0x21, 0x00} }, + {"Acer", "AOA110", "v0.3310", 0x55, 0x58, {0x21, 0x00} }, + /* AOA150 */ + {"Acer", "AOA150", "v0.3114", 0x55, 0x58, {0x20, 0x00} }, + {"Acer", "AOA150", "v0.3301", 0x55, 0x58, {0x20, 0x00} }, + {"Acer", "AOA150", "v0.3304", 0x55, 0x58, {0x20, 0x00} }, + {"Acer", "AOA150", "v0.3305", 0x55, 0x58, {0x20, 0x00} }, + {"Acer", "AOA150", "v0.3307", 0x55, 0x58, {0x20, 0x00} }, + {"Acer", "AOA150", "v0.3308", 0x55, 0x58, {0x20, 0x00} }, + {"Acer", "AOA150", "v0.3309", 0x55, 0x58, {0x20, 0x00} }, + {"Acer", "AOA150", "v0.3310", 0x55, 0x58, {0x20, 0x00} }, + /* special BIOS / other */ + {"Gateway", "AOA110", "v0.3103", 0x55, 0x58, {0x21, 0x00} }, + {"Gateway", "AOA150", "v0.3103", 0x55, 0x58, {0x20, 0x00} }, + {"Packard Bell", "DOA150", "v0.3104", 0x55, 0x58, {0x21, 0x00} }, + {"Packard Bell", "AOA110", "v0.3105", 0x55, 0x58, {0x21, 0x00} }, + {"Packard Bell", "AOA150", "v0.3105", 0x55, 0x58, {0x20, 0x00} }, + /* pewpew-terminator */ + {"", "", "", 0, 0, {0, 0} } }; static const struct bios_settings_t *bios_cfg __read_mostly; - static int acerhdf_get_temp(int *temp) { u8 read_temp; @@ -142,7 +171,7 @@ static int acerhdf_get_temp(int *temp) if (ec_read(bios_cfg->tempreg, &read_temp)) return -EINVAL; - *temp = read_temp; + *temp = read_temp * 1000; return 0; } @@ -150,13 +179,14 @@ static int acerhdf_get_temp(int *temp) static int acerhdf_get_fanstate(int *state) { u8 fan; - bool tmp; if (ec_read(bios_cfg->fanreg, &fan)) return -EINVAL; - tmp = (fan == bios_cfg->fancmd[ACERHDF_FAN_OFF]); - *state = tmp ? ACERHDF_FAN_OFF : ACERHDF_FAN_AUTO; + if (fan != bios_cfg->cmd.cmd_off) + *state = ACERHDF_FAN_AUTO; + else + *state = ACERHDF_FAN_OFF; return 0; } @@ -175,7 +205,8 @@ static void acerhdf_change_fanstate(int state) state = ACERHDF_FAN_AUTO; } - cmd = bios_cfg->fancmd[state]; + cmd = (state == ACERHDF_FAN_OFF) ? bios_cfg->cmd.cmd_off + : bios_cfg->cmd.cmd_auto; fanstate = state; ec_write(bios_cfg->fanreg, cmd); @@ -408,7 +439,7 @@ struct thermal_cooling_device_ops acerhdf_cooling_ops = { }; /* suspend / resume functionality */ -static int acerhdf_suspend(struct platform_device *dev, pm_message_t state) +static int acerhdf_suspend(struct device *dev) { if (kernelmode) acerhdf_change_fanstate(ACERHDF_FAN_AUTO); @@ -419,14 +450,6 @@ static int acerhdf_suspend(struct platform_device *dev, pm_message_t state) return 0; } -static int acerhdf_resume(struct platform_device *device) -{ - if (verbose) - pr_notice("resuming\n"); - - return 0; -} - static int __devinit acerhdf_probe(struct platform_device *device) { return 0; @@ -437,15 +460,19 @@ static int acerhdf_remove(struct platform_device *device) return 0; } -struct platform_driver acerhdf_drv = { +static struct dev_pm_ops acerhdf_pm_ops = { + .suspend = acerhdf_suspend, + .freeze = acerhdf_suspend, +}; + +static struct platform_driver acerhdf_driver = { .driver = { - .name = "acerhdf", + .name = "acerhdf", .owner = THIS_MODULE, + .pm = &acerhdf_pm_ops, }, .probe = acerhdf_probe, .remove = acerhdf_remove, - .suspend = acerhdf_suspend, - .resume = acerhdf_resume, }; @@ -454,32 +481,40 @@ static int acerhdf_check_hardware(void) { char const *vendor, *version, *product; int i; + unsigned long prod_len = 0; /* get BIOS data */ vendor = dmi_get_system_info(DMI_SYS_VENDOR); version = dmi_get_system_info(DMI_BIOS_VERSION); product = dmi_get_system_info(DMI_PRODUCT_NAME); + pr_info("Acer Aspire One Fan driver, v.%s\n", DRV_VER); - if (!force_bios[0]) { - if (strncmp(product, "AO", 2)) { - pr_err("no Aspire One hardware found\n"); - return -EINVAL; - } - } else { - pr_info("forcing BIOS version: %s\n", version); + if (force_bios[0]) { version = force_bios; + pr_info("forcing BIOS version: %s\n", version); + kernelmode = 0; + } + + if (force_product[0]) { + product = force_product; + pr_info("forcing BIOS product: %s\n", product); kernelmode = 0; } + prod_len = strlen(product); + if (verbose) pr_info("BIOS info: %s %s, product: %s\n", vendor, version, product); /* search BIOS version and vendor in BIOS settings table */ for (i = 0; bios_tbl[i].version[0]; i++) { - if (!strcmp(bios_tbl[i].vendor, vendor) && + if (strlen(bios_tbl[i].product) >= prod_len && + !strncmp(bios_tbl[i].product, product, + strlen(bios_tbl[i].product)) && + !strcmp(bios_tbl[i].vendor, vendor) && !strcmp(bios_tbl[i].version, version)) { bios_cfg = &bios_tbl[i]; break; @@ -487,8 +522,8 @@ static int acerhdf_check_hardware(void) } if (!bios_cfg) { - pr_err("unknown (unsupported) BIOS version %s/%s, " - "please report, aborting!\n", vendor, version); + pr_err("unknown (unsupported) BIOS version %s/%s/%s, " + "please report, aborting!\n", vendor, product, version); return -EINVAL; } @@ -509,7 +544,7 @@ static int acerhdf_register_platform(void) { int err = 0; - err = platform_driver_register(&acerhdf_drv); + err = platform_driver_register(&acerhdf_driver); if (err) return err; @@ -525,7 +560,7 @@ static void acerhdf_unregister_platform(void) return; platform_device_del(acerhdf_dev); - platform_driver_unregister(&acerhdf_drv); + platform_driver_unregister(&acerhdf_driver); } static int acerhdf_register_thermal(void) diff --git a/drivers/platform/x86/asus-laptop.c b/drivers/platform/x86/asus-laptop.c index db657bbeec90..b39d2bb3e75b 100644 --- a/drivers/platform/x86/asus-laptop.c +++ b/drivers/platform/x86/asus-laptop.c @@ -77,15 +77,16 @@ * Flags for hotk status * WL_ON and BT_ON are also used for wireless_status() */ -#define WL_ON 0x01 //internal Wifi -#define BT_ON 0x02 //internal Bluetooth -#define MLED_ON 0x04 //mail LED -#define TLED_ON 0x08 //touchpad LED -#define RLED_ON 0x10 //Record LED -#define PLED_ON 0x20 //Phone LED -#define GLED_ON 0x40 //Gaming LED -#define LCD_ON 0x80 //LCD backlight -#define GPS_ON 0x100 //GPS +#define WL_ON 0x01 /* internal Wifi */ +#define BT_ON 0x02 /* internal Bluetooth */ +#define MLED_ON 0x04 /* mail LED */ +#define TLED_ON 0x08 /* touchpad LED */ +#define RLED_ON 0x10 /* Record LED */ +#define PLED_ON 0x20 /* Phone LED */ +#define GLED_ON 0x40 /* Gaming LED */ +#define LCD_ON 0x80 /* LCD backlight */ +#define GPS_ON 0x100 /* GPS */ +#define KEY_ON 0x200 /* Keyboard backlight */ #define ASUS_LOG ASUS_HOTK_FILE ": " #define ASUS_ERR KERN_ERR ASUS_LOG @@ -98,7 +99,8 @@ MODULE_AUTHOR("Julien Lerouge, Karol Kozimor, Corentin Chary"); MODULE_DESCRIPTION(ASUS_HOTK_NAME); MODULE_LICENSE("GPL"); -/* WAPF defines the behavior of the Fn+Fx wlan key +/* + * WAPF defines the behavior of the Fn+Fx wlan key * The significance of values is yet to be found, but * most of the time: * 0x0 will do nothing @@ -125,7 +127,8 @@ ASUS_HANDLE(gled_set, ASUS_HOTK_PREFIX "GLED"); /* G1, G2 (probably) */ /* LEDD */ ASUS_HANDLE(ledd_set, ASUS_HOTK_PREFIX "SLCM"); -/* Bluetooth and WLAN +/* + * Bluetooth and WLAN * WLED and BLED are not handled like other XLED, because in some dsdt * they also control the WLAN/Bluetooth device. */ @@ -149,22 +152,32 @@ ASUS_HANDLE(lcd_switch, "\\_SB.PCI0.SBRG.EC0._Q10", /* All new models */ /* Display */ ASUS_HANDLE(display_set, ASUS_HOTK_PREFIX "SDSP"); -ASUS_HANDLE(display_get, "\\_SB.PCI0.P0P1.VGA.GETD", /* A6B, A6K A6R A7D F3JM L4R M6R A3G - M6A M6V VX-1 V6J V6V W3Z */ - "\\_SB.PCI0.P0P2.VGA.GETD", /* A3E A4K, A4D A4L A6J A7J A8J Z71V M9V - S5A M5A z33A W1Jc W2V G1 */ - "\\_SB.PCI0.P0P3.VGA.GETD", /* A6V A6Q */ - "\\_SB.PCI0.P0PA.VGA.GETD", /* A6T, A6M */ - "\\_SB.PCI0.PCI1.VGAC.NMAP", /* L3C */ - "\\_SB.PCI0.VGA.GETD", /* Z96F */ - "\\ACTD", /* A2D */ - "\\ADVG", /* A4G Z71A W1N W5A W5F M2N M3N M5N M6N S1N S5N */ - "\\DNXT", /* P30 */ - "\\INFB", /* A2H D1 L2D L3D L3H L2E L5D L5C M1A M2E L4L W3V */ - "\\SSTE"); /* A3F A6F A3N A3L M6N W3N W6A */ - -ASUS_HANDLE(ls_switch, ASUS_HOTK_PREFIX "ALSC"); /* Z71A Z71V */ -ASUS_HANDLE(ls_level, ASUS_HOTK_PREFIX "ALSL"); /* Z71A Z71V */ +ASUS_HANDLE(display_get, + /* A6B, A6K A6R A7D F3JM L4R M6R A3G M6A M6V VX-1 V6J V6V W3Z */ + "\\_SB.PCI0.P0P1.VGA.GETD", + /* A3E A4K, A4D A4L A6J A7J A8J Z71V M9V S5A M5A z33A W1Jc W2V G1 */ + "\\_SB.PCI0.P0P2.VGA.GETD", + /* A6V A6Q */ + "\\_SB.PCI0.P0P3.VGA.GETD", + /* A6T, A6M */ + "\\_SB.PCI0.P0PA.VGA.GETD", + /* L3C */ + "\\_SB.PCI0.PCI1.VGAC.NMAP", + /* Z96F */ + "\\_SB.PCI0.VGA.GETD", + /* A2D */ + "\\ACTD", + /* A4G Z71A W1N W5A W5F M2N M3N M5N M6N S1N S5N */ + "\\ADVG", + /* P30 */ + "\\DNXT", + /* A2H D1 L2D L3D L3H L2E L5D L5C M1A M2E L4L W3V */ + "\\INFB", + /* A3F A6F A3N A3L M6N W3N W6A */ + "\\SSTE"); + +ASUS_HANDLE(ls_switch, ASUS_HOTK_PREFIX "ALSC"); /* Z71A Z71V */ +ASUS_HANDLE(ls_level, ASUS_HOTK_PREFIX "ALSL"); /* Z71A Z71V */ /* GPS */ /* R2H use different handle for GPS on/off */ @@ -172,19 +185,23 @@ ASUS_HANDLE(gps_on, ASUS_HOTK_PREFIX "SDON"); /* R2H */ ASUS_HANDLE(gps_off, ASUS_HOTK_PREFIX "SDOF"); /* R2H */ ASUS_HANDLE(gps_status, ASUS_HOTK_PREFIX "GPST"); +/* Keyboard light */ +ASUS_HANDLE(kled_set, ASUS_HOTK_PREFIX "SLKB"); +ASUS_HANDLE(kled_get, ASUS_HOTK_PREFIX "GLKB"); + /* * This is the main structure, we can use it to store anything interesting * about the hotk device */ struct asus_hotk { - char *name; //laptop name - struct acpi_device *device; //the device we are in - acpi_handle handle; //the handle of the hotk device - char status; //status of the hotk, for LEDs, ... - u32 ledd_status; //status of the LED display - u8 light_level; //light sensor level - u8 light_switch; //light sensor switch value - u16 event_count[128]; //count for each event TODO make this better + char *name; /* laptop name */ + struct acpi_device *device; /* the device we are in */ + acpi_handle handle; /* the handle of the hotk device */ + char status; /* status of the hotk, for LEDs, ... */ + u32 ledd_status; /* status of the LED display */ + u8 light_level; /* light sensor level */ + u8 light_switch; /* light sensor switch value */ + u16 event_count[128]; /* count for each event TODO make this better */ struct input_dev *inputdev; u16 *keycode_map; }; @@ -237,28 +254,35 @@ static struct backlight_ops asusbl_ops = { .update_status = update_bl_status, }; -/* These functions actually update the LED's, and are called from a +/* + * These functions actually update the LED's, and are called from a * workqueue. By doing this as separate work rather than when the LED * subsystem asks, we avoid messing with the Asus ACPI stuff during a - * potentially bad time, such as a timer interrupt. */ + * potentially bad time, such as a timer interrupt. + */ static struct workqueue_struct *led_workqueue; -#define ASUS_LED(object, ledname) \ +#define ASUS_LED(object, ledname, max) \ static void object##_led_set(struct led_classdev *led_cdev, \ enum led_brightness value); \ + static enum led_brightness object##_led_get( \ + struct led_classdev *led_cdev); \ static void object##_led_update(struct work_struct *ignored); \ static int object##_led_wk; \ static DECLARE_WORK(object##_led_work, object##_led_update); \ static struct led_classdev object##_led = { \ .name = "asus::" ledname, \ .brightness_set = object##_led_set, \ + .brightness_get = object##_led_get, \ + .max_brightness = max \ } -ASUS_LED(mled, "mail"); -ASUS_LED(tled, "touchpad"); -ASUS_LED(rled, "record"); -ASUS_LED(pled, "phone"); -ASUS_LED(gled, "gaming"); +ASUS_LED(mled, "mail", 1); +ASUS_LED(tled, "touchpad", 1); +ASUS_LED(rled, "record", 1); +ASUS_LED(pled, "phone", 1); +ASUS_LED(gled, "gaming", 1); +ASUS_LED(kled, "kbd_backlight", 3); struct key_entry { char type; @@ -278,16 +302,23 @@ static struct key_entry asus_keymap[] = { {KE_KEY, 0x41, KEY_NEXTSONG}, {KE_KEY, 0x43, KEY_STOPCD}, {KE_KEY, 0x45, KEY_PLAYPAUSE}, + {KE_KEY, 0x4c, KEY_MEDIA}, {KE_KEY, 0x50, KEY_EMAIL}, {KE_KEY, 0x51, KEY_WWW}, + {KE_KEY, 0x55, KEY_CALC}, {KE_KEY, 0x5C, KEY_SCREENLOCK}, /* Screenlock */ {KE_KEY, 0x5D, KEY_WLAN}, + {KE_KEY, 0x5E, KEY_WLAN}, + {KE_KEY, 0x5F, KEY_WLAN}, + {KE_KEY, 0x60, KEY_SWITCHVIDEOMODE}, {KE_KEY, 0x61, KEY_SWITCHVIDEOMODE}, {KE_KEY, 0x6B, BTN_TOUCH}, /* Lock Mouse */ {KE_KEY, 0x82, KEY_CAMERA}, {KE_KEY, 0x8A, KEY_PROG1}, {KE_KEY, 0x95, KEY_MEDIA}, {KE_KEY, 0x99, KEY_PHONE}, + {KE_KEY, 0xc4, KEY_KBDILLUMUP}, + {KE_KEY, 0xc5, KEY_KBDILLUMDOWN}, {KE_END, 0}, }; @@ -301,8 +332,8 @@ static struct key_entry asus_keymap[] = { static int write_acpi_int(acpi_handle handle, const char *method, int val, struct acpi_buffer *output) { - struct acpi_object_list params; //list of input parameters (an int here) - union acpi_object in_obj; //the only param we use + struct acpi_object_list params; /* list of input parameters (an int) */ + union acpi_object in_obj; /* the only param we use */ acpi_status status; if (!handle) @@ -399,6 +430,11 @@ static void write_status(acpi_handle handle, int out, int mask) { \ int value = object##_led_wk; \ write_status(object##_set_handle, value, (mask)); \ + } \ + static enum led_brightness object##_led_get( \ + struct led_classdev *led_cdev) \ + { \ + return led_cdev->brightness; \ } ASUS_LED_HANDLER(mled, MLED_ON); @@ -407,6 +443,60 @@ ASUS_LED_HANDLER(rled, RLED_ON); ASUS_LED_HANDLER(tled, TLED_ON); ASUS_LED_HANDLER(gled, GLED_ON); +/* + * Keyboard backlight + */ +static int get_kled_lvl(void) +{ + unsigned long long kblv; + struct acpi_object_list params; + union acpi_object in_obj; + acpi_status rv; + + params.count = 1; + params.pointer = &in_obj; + in_obj.type = ACPI_TYPE_INTEGER; + in_obj.integer.value = 2; + + rv = acpi_evaluate_integer(kled_get_handle, NULL, ¶ms, &kblv); + if (ACPI_FAILURE(rv)) { + pr_warning("Error reading kled level\n"); + return 0; + } + return kblv; +} + +static int set_kled_lvl(int kblv) +{ + if (kblv > 0) + kblv = (1 << 7) | (kblv & 0x7F); + else + kblv = 0; + + if (write_acpi_int(kled_set_handle, NULL, kblv, NULL)) { + pr_warning("Keyboard LED display write failed\n"); + return -EINVAL; + } + return 0; +} + +static void kled_led_set(struct led_classdev *led_cdev, + enum led_brightness value) +{ + kled_led_wk = value; + queue_work(led_workqueue, &kled_led_work); +} + +static void kled_led_update(struct work_struct *ignored) +{ + set_kled_lvl(kled_led_wk); +} + +static enum led_brightness kled_led_get(struct led_classdev *led_cdev) +{ + return get_kled_lvl(); +} + static int get_lcd_state(void) { return read_status(LCD_ON); @@ -498,7 +588,7 @@ static ssize_t show_infos(struct device *dev, { int len = 0; unsigned long long temp; - char buf[16]; //enough for all info + char buf[16]; /* enough for all info */ acpi_status rv = AE_OK; /* @@ -516,7 +606,17 @@ static ssize_t show_infos(struct device *dev, */ rv = acpi_evaluate_integer(hotk->handle, "SFUN", NULL, &temp); if (!ACPI_FAILURE(rv)) - len += sprintf(page + len, "SFUN value : 0x%04x\n", + len += sprintf(page + len, "SFUN value : %#x\n", + (uint) temp); + /* + * The HWRS method return informations about the hardware. + * 0x80 bit is for WLAN, 0x100 for Bluetooth. + * The significance of others is yet to be found. + * If we don't find the method, we assume the device are present. + */ + rv = acpi_evaluate_integer(hotk->handle, "HRWS", NULL, &temp); + if (!ACPI_FAILURE(rv)) + len += sprintf(page + len, "HRWS value : %#x\n", (uint) temp); /* * Another value for userspace: the ASYM method returns 0x02 for @@ -527,7 +627,7 @@ static ssize_t show_infos(struct device *dev, */ rv = acpi_evaluate_integer(hotk->handle, "ASYM", NULL, &temp); if (!ACPI_FAILURE(rv)) - len += sprintf(page + len, "ASYM value : 0x%04x\n", + len += sprintf(page + len, "ASYM value : %#x\n", (uint) temp); if (asus_info) { snprintf(buf, 16, "%d", asus_info->length); @@ -648,8 +748,10 @@ static int read_display(void) unsigned long long value = 0; acpi_status rv = AE_OK; - /* In most of the case, we know how to set the display, but sometime - we can't read it */ + /* + * In most of the case, we know how to set the display, but sometime + * we can't read it + */ if (display_get_handle) { rv = acpi_evaluate_integer(display_get_handle, NULL, NULL, &value); @@ -1037,6 +1139,9 @@ static int asus_hotk_get_info(void) ASUS_HANDLE_INIT(ledd_set); + ASUS_HANDLE_INIT(kled_set); + ASUS_HANDLE_INIT(kled_get); + /* * The HWRS method return informations about the hardware. * 0x80 bit is for WLAN, 0x100 for Bluetooth. @@ -1063,8 +1168,10 @@ static int asus_hotk_get_info(void) ASUS_HANDLE_INIT(display_set); ASUS_HANDLE_INIT(display_get); - /* There is a lot of models with "ALSL", but a few get - a real light sens, so we need to check it. */ + /* + * There is a lot of models with "ALSL", but a few get + * a real light sens, so we need to check it. + */ if (!ASUS_HANDLE_INIT(ls_switch)) ASUS_HANDLE_INIT(ls_level); @@ -1168,6 +1275,10 @@ static int asus_hotk_add(struct acpi_device *device) /* LCD Backlight is on by default */ write_status(NULL, 1, LCD_ON); + /* Keyboard Backlight is on by default */ + if (kled_set_handle) + set_kled_lvl(1); + /* LED display is off by default */ hotk->ledd_status = 0xFFF; @@ -1222,6 +1333,7 @@ static void asus_led_exit(void) ASUS_LED_UNREGISTER(pled); ASUS_LED_UNREGISTER(rled); ASUS_LED_UNREGISTER(gled); + ASUS_LED_UNREGISTER(kled); } static void asus_input_exit(void) @@ -1301,13 +1413,20 @@ static int asus_led_init(struct device *dev) if (rv) goto out4; + if (kled_set_handle && kled_get_handle) + rv = ASUS_LED_REGISTER(kled, dev); + if (rv) + goto out5; + led_workqueue = create_singlethread_workqueue("led_workqueue"); if (!led_workqueue) - goto out5; + goto out6; return 0; -out5: +out6: rv = -ENOMEM; + ASUS_LED_UNREGISTER(kled); +out5: ASUS_LED_UNREGISTER(gled); out4: ASUS_LED_UNREGISTER(pled); diff --git a/drivers/platform/x86/eeepc-laptop.c b/drivers/platform/x86/eeepc-laptop.c index 222ffb892f22..4226e5352738 100644 --- a/drivers/platform/x86/eeepc-laptop.c +++ b/drivers/platform/x86/eeepc-laptop.c @@ -142,18 +142,28 @@ struct eeepc_hotk { struct rfkill *wlan_rfkill; struct rfkill *bluetooth_rfkill; struct rfkill *wwan3g_rfkill; + struct rfkill *wimax_rfkill; struct hotplug_slot *hotplug_slot; - struct work_struct hotplug_work; + struct mutex hotplug_lock; }; /* The actual device the driver binds to */ static struct eeepc_hotk *ehotk; /* Platform device/driver */ +static int eeepc_hotk_thaw(struct device *device); +static int eeepc_hotk_restore(struct device *device); + +static struct dev_pm_ops eeepc_pm_ops = { + .thaw = eeepc_hotk_thaw, + .restore = eeepc_hotk_restore, +}; + static struct platform_driver platform_driver = { .driver = { .name = EEEPC_HOTK_FILE, .owner = THIS_MODULE, + .pm = &eeepc_pm_ops, } }; @@ -192,7 +202,6 @@ static struct key_entry eeepc_keymap[] = { */ static int eeepc_hotk_add(struct acpi_device *device); static int eeepc_hotk_remove(struct acpi_device *device, int type); -static int eeepc_hotk_resume(struct acpi_device *device); static void eeepc_hotk_notify(struct acpi_device *device, u32 event); static const struct acpi_device_id eeepc_device_ids[] = { @@ -209,7 +218,6 @@ static struct acpi_driver eeepc_hotk_driver = { .ops = { .add = eeepc_hotk_add, .remove = eeepc_hotk_remove, - .resume = eeepc_hotk_resume, .notify = eeepc_hotk_notify, }, }; @@ -342,13 +350,14 @@ static const struct rfkill_ops eeepc_rfkill_ops = { .set_block = eeepc_rfkill_set, }; -static void __init eeepc_enable_camera(void) +static void __devinit eeepc_enable_camera(void) { /* * If the following call to set_acpi() fails, it's because there's no * camera so we can ignore the error. */ - set_acpi(CM_ASL_CAMERA, 1); + if (get_acpi(CM_ASL_CAMERA) == 0) + set_acpi(CM_ASL_CAMERA, 1); } /* @@ -579,7 +588,6 @@ static void cmsg_quirks(void) static int eeepc_hotk_check(void) { - const struct key_entry *key; struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; int result; @@ -604,31 +612,6 @@ static int eeepc_hotk_check(void) pr_info("Get control methods supported: 0x%x\n", ehotk->cm_supported); } - ehotk->inputdev = input_allocate_device(); - if (!ehotk->inputdev) { - pr_info("Unable to allocate input device\n"); - return 0; - } - ehotk->inputdev->name = "Asus EeePC extra buttons"; - ehotk->inputdev->phys = EEEPC_HOTK_FILE "/input0"; - ehotk->inputdev->id.bustype = BUS_HOST; - ehotk->inputdev->getkeycode = eeepc_getkeycode; - ehotk->inputdev->setkeycode = eeepc_setkeycode; - - for (key = eeepc_keymap; key->type != KE_END; key++) { - switch (key->type) { - case KE_KEY: - set_bit(EV_KEY, ehotk->inputdev->evbit); - set_bit(key->keycode, ehotk->inputdev->keybit); - break; - } - } - result = input_register_device(ehotk->inputdev); - if (result) { - pr_info("Unable to register input device\n"); - input_free_device(ehotk->inputdev); - return 0; - } } else { pr_err("Hotkey device not present, aborting\n"); return -EINVAL; @@ -642,7 +625,7 @@ static int notify_brn(void) struct backlight_device *bd = eeepc_backlight_device; if (bd) { int old = bd->props.brightness; - bd->props.brightness = read_brightness(bd); + backlight_force_update(bd, BACKLIGHT_UPDATE_HOTKEY); return old; } return -1; @@ -661,40 +644,48 @@ static int eeepc_get_adapter_status(struct hotplug_slot *hotplug_slot, return 0; } -static void eeepc_hotplug_work(struct work_struct *work) +static void eeepc_rfkill_hotplug(void) { struct pci_dev *dev; - struct pci_bus *bus = pci_find_bus(0, 1); - bool blocked; + struct pci_bus *bus; + bool blocked = eeepc_wlan_rfkill_blocked(); - if (!bus) { - pr_warning("Unable to find PCI bus 1?\n"); - return; - } + if (ehotk->wlan_rfkill) + rfkill_set_sw_state(ehotk->wlan_rfkill, blocked); - blocked = eeepc_wlan_rfkill_blocked(); - if (!blocked) { - dev = pci_get_slot(bus, 0); - if (dev) { - /* Device already present */ - pci_dev_put(dev); - return; - } - dev = pci_scan_single_device(bus, 0); - if (dev) { - pci_bus_assign_resources(bus); - if (pci_bus_add_device(dev)) - pr_err("Unable to hotplug wifi\n"); + mutex_lock(&ehotk->hotplug_lock); + + if (ehotk->hotplug_slot) { + bus = pci_find_bus(0, 1); + if (!bus) { + pr_warning("Unable to find PCI bus 1?\n"); + goto out_unlock; } - } else { - dev = pci_get_slot(bus, 0); - if (dev) { - pci_remove_bus_device(dev); - pci_dev_put(dev); + + if (!blocked) { + dev = pci_get_slot(bus, 0); + if (dev) { + /* Device already present */ + pci_dev_put(dev); + goto out_unlock; + } + dev = pci_scan_single_device(bus, 0); + if (dev) { + pci_bus_assign_resources(bus); + if (pci_bus_add_device(dev)) + pr_err("Unable to hotplug wifi\n"); + } + } else { + dev = pci_get_slot(bus, 0); + if (dev) { + pci_remove_bus_device(dev); + pci_dev_put(dev); + } } } - rfkill_set_sw_state(ehotk->wlan_rfkill, blocked); +out_unlock: + mutex_unlock(&ehotk->hotplug_lock); } static void eeepc_rfkill_notify(acpi_handle handle, u32 event, void *data) @@ -702,7 +693,7 @@ static void eeepc_rfkill_notify(acpi_handle handle, u32 event, void *data) if (event != ACPI_NOTIFY_BUS_CHECK) return; - schedule_work(&ehotk->hotplug_work); + eeepc_rfkill_hotplug(); } static void eeepc_hotk_notify(struct acpi_device *device, u32 event) @@ -839,66 +830,38 @@ error_slot: return ret; } -static int eeepc_hotk_add(struct acpi_device *device) -{ - int result; - - if (!device) - return -EINVAL; - pr_notice(EEEPC_HOTK_NAME "\n"); - ehotk = kzalloc(sizeof(struct eeepc_hotk), GFP_KERNEL); - if (!ehotk) - return -ENOMEM; - ehotk->init_flag = DISABLE_ASL_WLAN | DISABLE_ASL_DISPLAYSWITCH; - ehotk->handle = device->handle; - strcpy(acpi_device_name(device), EEEPC_HOTK_DEVICE_NAME); - strcpy(acpi_device_class(device), EEEPC_HOTK_CLASS); - device->driver_data = ehotk; - ehotk->device = device; - result = eeepc_hotk_check(); - if (result) - goto ehotk_fail; - - return 0; - - ehotk_fail: - kfree(ehotk); - ehotk = NULL; - - return result; -} - -static int eeepc_hotk_remove(struct acpi_device *device, int type) -{ - if (!device || !acpi_driver_data(device)) - return -EINVAL; - - kfree(ehotk); - return 0; -} - -static int eeepc_hotk_resume(struct acpi_device *device) +static int eeepc_hotk_thaw(struct device *device) { if (ehotk->wlan_rfkill) { bool wlan; - /* Workaround - it seems that _PTS disables the wireless - without notification or changing the value read by WLAN. - Normally this is fine because the correct value is restored - from the non-volatile storage on resume, but we need to do - it ourself if case suspend is aborted, or we lose wireless. + /* + * Work around bios bug - acpi _PTS turns off the wireless led + * during suspend. Normally it restores it on resume, but + * we should kick it ourselves in case hibernation is aborted. */ wlan = get_acpi(CM_ASL_WLAN); set_acpi(CM_ASL_WLAN, wlan); + } - rfkill_set_sw_state(ehotk->wlan_rfkill, wlan != 1); + return 0; +} - schedule_work(&ehotk->hotplug_work); - } +static int eeepc_hotk_restore(struct device *device) +{ + /* Refresh both wlan rfkill state and pci hotplug */ + if (ehotk->wlan_rfkill) + eeepc_rfkill_hotplug(); if (ehotk->bluetooth_rfkill) rfkill_set_sw_state(ehotk->bluetooth_rfkill, get_acpi(CM_ASL_BLUETOOTH) != 1); + if (ehotk->wwan3g_rfkill) + rfkill_set_sw_state(ehotk->wwan3g_rfkill, + get_acpi(CM_ASL_3G) != 1); + if (ehotk->wimax_rfkill) + rfkill_set_sw_state(ehotk->wimax_rfkill, + get_acpi(CM_ASL_WIMAX) != 1); return 0; } @@ -1019,16 +982,37 @@ static void eeepc_backlight_exit(void) static void eeepc_rfkill_exit(void) { + eeepc_unregister_rfkill_notifier("\\_SB.PCI0.P0P5"); eeepc_unregister_rfkill_notifier("\\_SB.PCI0.P0P6"); eeepc_unregister_rfkill_notifier("\\_SB.PCI0.P0P7"); - if (ehotk->wlan_rfkill) + if (ehotk->wlan_rfkill) { rfkill_unregister(ehotk->wlan_rfkill); - if (ehotk->bluetooth_rfkill) - rfkill_unregister(ehotk->bluetooth_rfkill); - if (ehotk->wwan3g_rfkill) - rfkill_unregister(ehotk->wwan3g_rfkill); + rfkill_destroy(ehotk->wlan_rfkill); + ehotk->wlan_rfkill = NULL; + } + /* + * Refresh pci hotplug in case the rfkill state was changed after + * eeepc_unregister_rfkill_notifier() + */ + eeepc_rfkill_hotplug(); if (ehotk->hotplug_slot) pci_hp_deregister(ehotk->hotplug_slot); + + if (ehotk->bluetooth_rfkill) { + rfkill_unregister(ehotk->bluetooth_rfkill); + rfkill_destroy(ehotk->bluetooth_rfkill); + ehotk->bluetooth_rfkill = NULL; + } + if (ehotk->wwan3g_rfkill) { + rfkill_unregister(ehotk->wwan3g_rfkill); + rfkill_destroy(ehotk->wwan3g_rfkill); + ehotk->wwan3g_rfkill = NULL; + } + if (ehotk->wimax_rfkill) { + rfkill_unregister(ehotk->wimax_rfkill); + rfkill_destroy(ehotk->wimax_rfkill); + ehotk->wimax_rfkill = NULL; + } } static void eeepc_input_exit(void) @@ -1050,19 +1034,6 @@ static void eeepc_hwmon_exit(void) eeepc_hwmon_device = NULL; } -static void __exit eeepc_laptop_exit(void) -{ - eeepc_backlight_exit(); - eeepc_rfkill_exit(); - eeepc_input_exit(); - eeepc_hwmon_exit(); - acpi_bus_unregister_driver(&eeepc_hotk_driver); - sysfs_remove_group(&platform_device->dev.kobj, - &platform_attribute_group); - platform_device_unregister(platform_device); - platform_driver_unregister(&platform_driver); -} - static int eeepc_new_rfkill(struct rfkill **rfkill, const char *name, struct device *dev, enum rfkill_type type, int cm) @@ -1094,10 +1065,7 @@ static int eeepc_rfkill_init(struct device *dev) { int result = 0; - INIT_WORK(&ehotk->hotplug_work, eeepc_hotplug_work); - - eeepc_register_rfkill_notifier("\\_SB.PCI0.P0P6"); - eeepc_register_rfkill_notifier("\\_SB.PCI0.P0P7"); + mutex_init(&ehotk->hotplug_lock); result = eeepc_new_rfkill(&ehotk->wlan_rfkill, "eeepc-wlan", dev, @@ -1120,6 +1088,13 @@ static int eeepc_rfkill_init(struct device *dev) if (result && result != -ENODEV) goto exit; + result = eeepc_new_rfkill(&ehotk->wimax_rfkill, + "eeepc-wimax", dev, + RFKILL_TYPE_WIMAX, CM_ASL_WIMAX); + + if (result && result != -ENODEV) + goto exit; + result = eeepc_setup_pci_hotplug(); /* * If we get -EBUSY then something else is handling the PCI hotplug - @@ -1128,6 +1103,15 @@ static int eeepc_rfkill_init(struct device *dev) if (result == -EBUSY) result = 0; + eeepc_register_rfkill_notifier("\\_SB.PCI0.P0P5"); + eeepc_register_rfkill_notifier("\\_SB.PCI0.P0P6"); + eeepc_register_rfkill_notifier("\\_SB.PCI0.P0P7"); + /* + * Refresh pci hotplug in case the rfkill state was changed during + * setup. + */ + eeepc_rfkill_hotplug(); + exit: if (result && result != -ENODEV) eeepc_rfkill_exit(); @@ -1172,21 +1156,61 @@ static int eeepc_hwmon_init(struct device *dev) return result; } -static int __init eeepc_laptop_init(void) +static int eeepc_input_init(struct device *dev) { - struct device *dev; + const struct key_entry *key; int result; - if (acpi_disabled) - return -ENODEV; - result = acpi_bus_register_driver(&eeepc_hotk_driver); - if (result < 0) + ehotk->inputdev = input_allocate_device(); + if (!ehotk->inputdev) { + pr_info("Unable to allocate input device\n"); + return -ENOMEM; + } + ehotk->inputdev->name = "Asus EeePC extra buttons"; + ehotk->inputdev->dev.parent = dev; + ehotk->inputdev->phys = EEEPC_HOTK_FILE "/input0"; + ehotk->inputdev->id.bustype = BUS_HOST; + ehotk->inputdev->getkeycode = eeepc_getkeycode; + ehotk->inputdev->setkeycode = eeepc_setkeycode; + + for (key = eeepc_keymap; key->type != KE_END; key++) { + switch (key->type) { + case KE_KEY: + set_bit(EV_KEY, ehotk->inputdev->evbit); + set_bit(key->keycode, ehotk->inputdev->keybit); + break; + } + } + result = input_register_device(ehotk->inputdev); + if (result) { + pr_info("Unable to register input device\n"); + input_free_device(ehotk->inputdev); return result; - if (!ehotk) { - acpi_bus_unregister_driver(&eeepc_hotk_driver); - return -ENODEV; } + return 0; +} + +static int __devinit eeepc_hotk_add(struct acpi_device *device) +{ + struct device *dev; + int result; + if (!device) + return -EINVAL; + pr_notice(EEEPC_HOTK_NAME "\n"); + ehotk = kzalloc(sizeof(struct eeepc_hotk), GFP_KERNEL); + if (!ehotk) + return -ENOMEM; + ehotk->init_flag = DISABLE_ASL_WLAN | DISABLE_ASL_DISPLAYSWITCH; + ehotk->handle = device->handle; + strcpy(acpi_device_name(device), EEEPC_HOTK_DEVICE_NAME); + strcpy(acpi_device_class(device), EEEPC_HOTK_CLASS); + device->driver_data = ehotk; + ehotk->device = device; + + result = eeepc_hotk_check(); + if (result) + goto fail_platform_driver; eeepc_enable_camera(); /* Register platform stuff */ @@ -1216,6 +1240,10 @@ static int __init eeepc_laptop_init(void) pr_info("Backlight controlled by ACPI video " "driver\n"); + result = eeepc_input_init(dev); + if (result) + goto fail_input; + result = eeepc_hwmon_init(dev); if (result) goto fail_hwmon; @@ -1225,9 +1253,12 @@ static int __init eeepc_laptop_init(void) goto fail_rfkill; return 0; + fail_rfkill: eeepc_hwmon_exit(); fail_hwmon: + eeepc_input_exit(); +fail_input: eeepc_backlight_exit(); fail_backlight: sysfs_remove_group(&platform_device->dev.kobj, @@ -1239,9 +1270,49 @@ fail_platform_device2: fail_platform_device1: platform_driver_unregister(&platform_driver); fail_platform_driver: - eeepc_input_exit(); + kfree(ehotk); + return result; } +static int eeepc_hotk_remove(struct acpi_device *device, int type) +{ + if (!device || !acpi_driver_data(device)) + return -EINVAL; + + eeepc_backlight_exit(); + eeepc_rfkill_exit(); + eeepc_input_exit(); + eeepc_hwmon_exit(); + sysfs_remove_group(&platform_device->dev.kobj, + &platform_attribute_group); + platform_device_unregister(platform_device); + platform_driver_unregister(&platform_driver); + + kfree(ehotk); + return 0; +} + +static int __init eeepc_laptop_init(void) +{ + int result; + + if (acpi_disabled) + return -ENODEV; + result = acpi_bus_register_driver(&eeepc_hotk_driver); + if (result < 0) + return result; + if (!ehotk) { + acpi_bus_unregister_driver(&eeepc_hotk_driver); + return -ENODEV; + } + return 0; +} + +static void __exit eeepc_laptop_exit(void) +{ + acpi_bus_unregister_driver(&eeepc_hotk_driver); +} + module_init(eeepc_laptop_init); module_exit(eeepc_laptop_exit); diff --git a/drivers/platform/x86/fujitsu-laptop.c b/drivers/platform/x86/fujitsu-laptop.c index 218b9a16ac3f..bcd4ba8be7db 100644 --- a/drivers/platform/x86/fujitsu-laptop.c +++ b/drivers/platform/x86/fujitsu-laptop.c @@ -66,11 +66,11 @@ #include <linux/kfifo.h> #include <linux/video_output.h> #include <linux/platform_device.h> -#ifdef CONFIG_LEDS_CLASS +#if defined(CONFIG_LEDS_CLASS) || defined(CONFIG_LEDS_CLASS_MODULE) #include <linux/leds.h> #endif -#define FUJITSU_DRIVER_VERSION "0.5.0" +#define FUJITSU_DRIVER_VERSION "0.6.0" #define FUJITSU_LCD_N_LEVELS 8 @@ -96,7 +96,7 @@ /* FUNC interface - responses */ #define UNSUPPORTED_CMD 0x80000000 -#ifdef CONFIG_LEDS_CLASS +#if defined(CONFIG_LEDS_CLASS) || defined(CONFIG_LEDS_CLASS_MODULE) /* FUNC interface - LED control */ #define FUNC_LED_OFF 0x1 #define FUNC_LED_ON 0x30001 @@ -176,7 +176,7 @@ static struct fujitsu_hotkey_t *fujitsu_hotkey; static void acpi_fujitsu_hotkey_notify(struct acpi_device *device, u32 event); -#ifdef CONFIG_LEDS_CLASS +#if defined(CONFIG_LEDS_CLASS) || defined(CONFIG_LEDS_CLASS_MODULE) static enum led_brightness logolamp_get(struct led_classdev *cdev); static void logolamp_set(struct led_classdev *cdev, enum led_brightness brightness); @@ -257,7 +257,7 @@ static int call_fext_func(int cmd, int arg0, int arg1, int arg2) return out_obj.integer.value; } -#ifdef CONFIG_LEDS_CLASS +#if defined(CONFIG_LEDS_CLASS) || defined(CONFIG_LEDS_CLASS_MODULE) /* LED class callbacks */ static void logolamp_set(struct led_classdev *cdev, @@ -324,9 +324,6 @@ static int set_lcd_level(int level) if (level < 0 || level >= fujitsu->max_brightness) return -EINVAL; - if (!fujitsu) - return -EINVAL; - status = acpi_get_handle(fujitsu->acpi_handle, "SBLL", &handle); if (ACPI_FAILURE(status)) { vdbg_printk(FUJLAPTOP_DBG_ERROR, "SBLL not present\n"); @@ -355,9 +352,6 @@ static int set_lcd_level_alt(int level) if (level < 0 || level >= fujitsu->max_brightness) return -EINVAL; - if (!fujitsu) - return -EINVAL; - status = acpi_get_handle(fujitsu->acpi_handle, "SBL2", &handle); if (ACPI_FAILURE(status)) { vdbg_printk(FUJLAPTOP_DBG_ERROR, "SBL2 not present\n"); @@ -697,10 +691,10 @@ static int acpi_fujitsu_add(struct acpi_device *device) result = acpi_bus_get_power(fujitsu->acpi_handle, &state); if (result) { printk(KERN_ERR "Error reading power state\n"); - goto end; + goto err_unregister_input_dev; } - printk(KERN_INFO PREFIX "%s [%s] (%s)\n", + printk(KERN_INFO "ACPI: %s [%s] (%s)\n", acpi_device_name(device), acpi_device_bid(device), !device->power.state ? "on" : "off"); @@ -728,25 +722,22 @@ static int acpi_fujitsu_add(struct acpi_device *device) return result; -end: +err_unregister_input_dev: + input_unregister_device(input); err_free_input_dev: input_free_device(input); err_stop: - return result; } static int acpi_fujitsu_remove(struct acpi_device *device, int type) { - struct fujitsu_t *fujitsu = NULL; + struct fujitsu_t *fujitsu = acpi_driver_data(device); + struct input_dev *input = fujitsu->input; - if (!device || !acpi_driver_data(device)) - return -EINVAL; - - fujitsu = acpi_driver_data(device); + input_unregister_device(input); - if (!device || !acpi_driver_data(device)) - return -EINVAL; + input_free_device(input); fujitsu->acpi_handle = NULL; @@ -871,10 +862,10 @@ static int acpi_fujitsu_hotkey_add(struct acpi_device *device) result = acpi_bus_get_power(fujitsu_hotkey->acpi_handle, &state); if (result) { printk(KERN_ERR "Error reading power state\n"); - goto end; + goto err_unregister_input_dev; } - printk(KERN_INFO PREFIX "%s [%s] (%s)\n", + printk(KERN_INFO "ACPI: %s [%s] (%s)\n", acpi_device_name(device), acpi_device_bid(device), !device->power.state ? "on" : "off"); @@ -911,7 +902,7 @@ static int acpi_fujitsu_hotkey_add(struct acpi_device *device) printk(KERN_INFO "fujitsu-laptop: BTNI: [0x%x]\n", call_fext_func(FUNC_BUTTONS, 0x0, 0x0, 0x0)); - #ifdef CONFIG_LEDS_CLASS +#if defined(CONFIG_LEDS_CLASS) || defined(CONFIG_LEDS_CLASS_MODULE) if (call_fext_func(FUNC_LEDS, 0x0, 0x0, 0x0) & LOGOLAMP_POWERON) { result = led_classdev_register(&fujitsu->pf_device->dev, &logolamp_led); @@ -934,33 +925,41 @@ static int acpi_fujitsu_hotkey_add(struct acpi_device *device) "LED handler for keyboard lamps, error %i\n", result); } } - #endif +#endif return result; -end: +err_unregister_input_dev: + input_unregister_device(input); err_free_input_dev: input_free_device(input); err_free_fifo: kfifo_free(fujitsu_hotkey->fifo); err_stop: - return result; } static int acpi_fujitsu_hotkey_remove(struct acpi_device *device, int type) { - struct fujitsu_hotkey_t *fujitsu_hotkey = NULL; + struct fujitsu_hotkey_t *fujitsu_hotkey = acpi_driver_data(device); + struct input_dev *input = fujitsu_hotkey->input; - if (!device || !acpi_driver_data(device)) - return -EINVAL; +#if defined(CONFIG_LEDS_CLASS) || defined(CONFIG_LEDS_CLASS_MODULE) + if (fujitsu_hotkey->logolamp_registered) + led_classdev_unregister(&logolamp_led); - fujitsu_hotkey = acpi_driver_data(device); + if (fujitsu_hotkey->kblamps_registered) + led_classdev_unregister(&kblamps_led); +#endif - fujitsu_hotkey->acpi_handle = NULL; + input_unregister_device(input); + + input_free_device(input); kfifo_free(fujitsu_hotkey->fifo); + fujitsu_hotkey->acpi_handle = NULL; + return 0; } @@ -1130,8 +1129,11 @@ static int __init fujitsu_init(void) fujitsu->bl_device = backlight_device_register("fujitsu-laptop", NULL, NULL, &fujitsubl_ops); - if (IS_ERR(fujitsu->bl_device)) - return PTR_ERR(fujitsu->bl_device); + if (IS_ERR(fujitsu->bl_device)) { + ret = PTR_ERR(fujitsu->bl_device); + fujitsu->bl_device = NULL; + goto fail_sysfs_group; + } max_brightness = fujitsu->max_brightness; fujitsu->bl_device->props.max_brightness = max_brightness - 1; fujitsu->bl_device->props.brightness = fujitsu->brightness_level; @@ -1171,32 +1173,22 @@ static int __init fujitsu_init(void) return 0; fail_hotkey1: - kfree(fujitsu_hotkey); - fail_hotkey: - platform_driver_unregister(&fujitsupf_driver); - fail_backlight: - if (fujitsu->bl_device) backlight_device_unregister(fujitsu->bl_device); - +fail_sysfs_group: + sysfs_remove_group(&fujitsu->pf_device->dev.kobj, + &fujitsupf_attribute_group); fail_platform_device2: - platform_device_del(fujitsu->pf_device); - fail_platform_device1: - platform_device_put(fujitsu->pf_device); - fail_platform_driver: - acpi_bus_unregister_driver(&acpi_fujitsu_driver); - fail_acpi: - kfree(fujitsu); return ret; @@ -1204,28 +1196,23 @@ fail_acpi: static void __exit fujitsu_cleanup(void) { - #ifdef CONFIG_LEDS_CLASS - if (fujitsu_hotkey->logolamp_registered != 0) - led_classdev_unregister(&logolamp_led); + acpi_bus_unregister_driver(&acpi_fujitsu_hotkey_driver); - if (fujitsu_hotkey->kblamps_registered != 0) - led_classdev_unregister(&kblamps_led); - #endif + kfree(fujitsu_hotkey); - sysfs_remove_group(&fujitsu->pf_device->dev.kobj, - &fujitsupf_attribute_group); - platform_device_unregister(fujitsu->pf_device); platform_driver_unregister(&fujitsupf_driver); + if (fujitsu->bl_device) backlight_device_unregister(fujitsu->bl_device); - acpi_bus_unregister_driver(&acpi_fujitsu_driver); + sysfs_remove_group(&fujitsu->pf_device->dev.kobj, + &fujitsupf_attribute_group); - kfree(fujitsu); + platform_device_unregister(fujitsu->pf_device); - acpi_bus_unregister_driver(&acpi_fujitsu_hotkey_driver); + acpi_bus_unregister_driver(&acpi_fujitsu_driver); - kfree(fujitsu_hotkey); + kfree(fujitsu); printk(KERN_INFO "fujitsu-laptop: driver unloaded.\n"); } diff --git a/drivers/platform/x86/hp-wmi.c b/drivers/platform/x86/hp-wmi.c index af04f5b049db..c2842171cec6 100644 --- a/drivers/platform/x86/hp-wmi.c +++ b/drivers/platform/x86/hp-wmi.c @@ -507,7 +507,7 @@ static int __exit hp_wmi_bios_remove(struct platform_device *device) } if (bluetooth_rfkill) { rfkill_unregister(bluetooth_rfkill); - rfkill_destroy(wifi_rfkill); + rfkill_destroy(bluetooth_rfkill); } if (wwan_rfkill) { rfkill_unregister(wwan_rfkill); diff --git a/drivers/platform/x86/sony-laptop.c b/drivers/platform/x86/sony-laptop.c index dafaa4a92df5..a2a742c8ff7e 100644 --- a/drivers/platform/x86/sony-laptop.c +++ b/drivers/platform/x86/sony-laptop.c @@ -976,15 +976,12 @@ static acpi_status sony_walk_callback(acpi_handle handle, u32 level, void *context, void **return_value) { struct acpi_device_info *info; - struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL}; - - if (ACPI_SUCCESS(acpi_get_object_info(handle, &buffer))) { - info = buffer.pointer; + if (ACPI_SUCCESS(acpi_get_object_info(handle, &info))) { printk(KERN_WARNING DRV_PFX "method: name: %4.4s, args %X\n", (char *)&info->name, info->param_count); - kfree(buffer.pointer); + kfree(info); } return AE_OK; @@ -1044,6 +1041,9 @@ static int sony_nc_resume(struct acpi_device *device) sony_backlight_update_status(sony_backlight_device) < 0) printk(KERN_WARNING DRV_PFX "unable to restore brightness level\n"); + /* re-read rfkill state */ + sony_nc_rfkill_update(); + return 0; } @@ -1081,6 +1081,8 @@ static int sony_nc_setup_rfkill(struct acpi_device *device, struct rfkill *rfk; enum rfkill_type type; const char *name; + int result; + bool hwblock; switch (nc_type) { case SONY_WIFI: @@ -1108,6 +1110,10 @@ static int sony_nc_setup_rfkill(struct acpi_device *device, if (!rfk) return -ENOMEM; + sony_call_snc_handle(0x124, 0x200, &result); + hwblock = !(result & 0x1); + rfkill_set_hw_state(rfk, hwblock); + err = rfkill_register(rfk); if (err) { rfkill_destroy(rfk); @@ -1205,15 +1211,6 @@ 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"); - } - if (ACPI_SUCCESS(acpi_get_handle(sony_nc_acpi_handle, "ECON", &handle))) { if (acpi_callsetfunc(sony_nc_acpi_handle, "ECON", 1, NULL)) @@ -1393,27 +1390,20 @@ struct sonypi_eventtypes { struct sonypi_event *events; }; -struct device_ctrl { +struct sony_pic_dev { + struct acpi_device *acpi_dev; + struct sony_pic_irq *cur_irq; + struct sony_pic_ioport *cur_ioport; + struct list_head interrupts; + struct list_head ioports; + struct mutex lock; + struct sonypi_eventtypes *event_types; + int (*handle_irq)(const u8, const u8); int model; - int (*handle_irq)(const u8, const u8); u16 evport_offset; - u8 has_camera; - u8 has_bluetooth; - u8 has_wwan; - struct sonypi_eventtypes *event_types; -}; - -struct sony_pic_dev { - struct device_ctrl *control; - struct acpi_device *acpi_dev; - struct sony_pic_irq *cur_irq; - struct sony_pic_ioport *cur_ioport; - struct list_head interrupts; - struct list_head ioports; - struct mutex lock; - u8 camera_power; - u8 bluetooth_power; - u8 wwan_power; + u8 camera_power; + u8 bluetooth_power; + u8 wwan_power; }; static struct sony_pic_dev spic_dev = { @@ -1421,6 +1411,8 @@ static struct sony_pic_dev spic_dev = { .ioports = LIST_HEAD_INIT(spic_dev.ioports), }; +static int spic_drv_registered; + /* Event masks */ #define SONYPI_JOGGER_MASK 0x00000001 #define SONYPI_CAPTURE_MASK 0x00000002 @@ -1718,27 +1710,6 @@ static int type3_handle_irq(const u8 data_mask, const u8 ev) return 1; } -static struct device_ctrl spic_types[] = { - { - .model = SONYPI_DEVICE_TYPE1, - .handle_irq = NULL, - .evport_offset = SONYPI_TYPE1_OFFSET, - .event_types = type1_events, - }, - { - .model = SONYPI_DEVICE_TYPE2, - .handle_irq = NULL, - .evport_offset = SONYPI_TYPE2_OFFSET, - .event_types = type2_events, - }, - { - .model = SONYPI_DEVICE_TYPE3, - .handle_irq = type3_handle_irq, - .evport_offset = SONYPI_TYPE3_OFFSET, - .event_types = type3_events, - }, -}; - static void sony_pic_detect_device_type(struct sony_pic_dev *dev) { struct pci_dev *pcidev; @@ -1746,48 +1717,63 @@ static void sony_pic_detect_device_type(struct sony_pic_dev *dev) pcidev = pci_get_device(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371AB_3, NULL); if (pcidev) { - dev->control = &spic_types[0]; + dev->model = SONYPI_DEVICE_TYPE1; + dev->evport_offset = SONYPI_TYPE1_OFFSET; + dev->event_types = type1_events; goto out; } pcidev = pci_get_device(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH6_1, NULL); if (pcidev) { - dev->control = &spic_types[2]; + dev->model = SONYPI_DEVICE_TYPE2; + dev->evport_offset = SONYPI_TYPE2_OFFSET; + dev->event_types = type2_events; goto out; } pcidev = pci_get_device(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH7_1, NULL); if (pcidev) { - dev->control = &spic_types[2]; + dev->model = SONYPI_DEVICE_TYPE3; + dev->handle_irq = type3_handle_irq; + dev->evport_offset = SONYPI_TYPE3_OFFSET; + dev->event_types = type3_events; goto out; } pcidev = pci_get_device(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH8_4, NULL); if (pcidev) { - dev->control = &spic_types[2]; + dev->model = SONYPI_DEVICE_TYPE3; + dev->handle_irq = type3_handle_irq; + dev->evport_offset = SONYPI_TYPE3_OFFSET; + dev->event_types = type3_events; goto out; } pcidev = pci_get_device(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH9_1, NULL); if (pcidev) { - dev->control = &spic_types[2]; + dev->model = SONYPI_DEVICE_TYPE3; + dev->handle_irq = type3_handle_irq; + dev->evport_offset = SONYPI_TYPE3_OFFSET; + dev->event_types = type3_events; goto out; } /* default */ - dev->control = &spic_types[1]; + dev->model = SONYPI_DEVICE_TYPE2; + dev->evport_offset = SONYPI_TYPE2_OFFSET; + dev->event_types = type2_events; out: if (pcidev) pci_dev_put(pcidev); printk(KERN_INFO DRV_PFX "detected Type%d model\n", - dev->control->model == SONYPI_DEVICE_TYPE1 ? 1 : - dev->control->model == SONYPI_DEVICE_TYPE2 ? 2 : 3); + dev->model == SONYPI_DEVICE_TYPE1 ? 1 : + dev->model == SONYPI_DEVICE_TYPE2 ? 2 : 3); } /* camera tests and poweron/poweroff */ @@ -2560,7 +2546,7 @@ static int sony_pic_enable(struct acpi_device *device, buffer.pointer = resource; /* setup Type 1 resources */ - if (spic_dev.control->model == SONYPI_DEVICE_TYPE1) { + if (spic_dev.model == SONYPI_DEVICE_TYPE1) { /* setup io resources */ resource->res1.type = ACPI_RESOURCE_TYPE_IO; @@ -2643,29 +2629,28 @@ static irqreturn_t sony_pic_irq(int irq, void *dev_id) data_mask = inb_p(dev->cur_ioport->io2.minimum); else data_mask = inb_p(dev->cur_ioport->io1.minimum + - dev->control->evport_offset); + dev->evport_offset); dprintk("event ([%.2x] [%.2x]) at port 0x%.4x(+0x%.2x)\n", ev, data_mask, dev->cur_ioport->io1.minimum, - dev->control->evport_offset); + dev->evport_offset); if (ev == 0x00 || ev == 0xff) return IRQ_HANDLED; - for (i = 0; dev->control->event_types[i].mask; i++) { + for (i = 0; dev->event_types[i].mask; i++) { - if ((data_mask & dev->control->event_types[i].data) != - dev->control->event_types[i].data) + if ((data_mask & dev->event_types[i].data) != + dev->event_types[i].data) continue; - if (!(mask & dev->control->event_types[i].mask)) + if (!(mask & dev->event_types[i].mask)) continue; - for (j = 0; dev->control->event_types[i].events[j].event; j++) { - if (ev == dev->control->event_types[i].events[j].data) { + for (j = 0; dev->event_types[i].events[j].event; j++) { + if (ev == dev->event_types[i].events[j].data) { device_event = - dev->control-> - event_types[i].events[j].event; + dev->event_types[i].events[j].event; goto found; } } @@ -2673,13 +2658,12 @@ static irqreturn_t sony_pic_irq(int irq, void *dev_id) /* Still not able to decode the event try to pass * it over to the minidriver */ - if (dev->control->handle_irq && - dev->control->handle_irq(data_mask, ev) == 0) + if (dev->handle_irq && dev->handle_irq(data_mask, ev) == 0) return IRQ_HANDLED; dprintk("unknown event ([%.2x] [%.2x]) at port 0x%.4x(+0x%.2x)\n", ev, data_mask, dev->cur_ioport->io1.minimum, - dev->control->evport_offset); + dev->evport_offset); return IRQ_HANDLED; found: @@ -2810,7 +2794,7 @@ static int sony_pic_add(struct acpi_device *device) /* request IRQ */ list_for_each_entry_reverse(irq, &spic_dev.interrupts, list) { if (!request_irq(irq->irq.interrupts[0], sony_pic_irq, - IRQF_SHARED, "sony-laptop", &spic_dev)) { + IRQF_DISABLED, "sony-laptop", &spic_dev)) { dprintk("IRQ: %d - triggering: %d - " "polarity: %d - shr: %d\n", irq->irq.interrupts[0], @@ -2943,6 +2927,7 @@ static int __init sony_laptop_init(void) "Unable to register SPIC driver."); goto out; } + spic_drv_registered = 1; } result = acpi_bus_register_driver(&sony_nc_driver); @@ -2954,7 +2939,7 @@ static int __init sony_laptop_init(void) return 0; out_unregister_pic: - if (!no_spic) + if (spic_drv_registered) acpi_bus_unregister_driver(&sony_pic_driver); out: return result; @@ -2963,7 +2948,7 @@ out: static void __exit sony_laptop_exit(void) { acpi_bus_unregister_driver(&sony_nc_driver); - if (!no_spic) + if (spic_drv_registered) acpi_bus_unregister_driver(&sony_pic_driver); } diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c index e85600852502..a848c7e20aeb 100644 --- a/drivers/platform/x86/thinkpad_acpi.c +++ b/drivers/platform/x86/thinkpad_acpi.c @@ -22,7 +22,7 @@ */ #define TPACPI_VERSION "0.23" -#define TPACPI_SYSFS_VERSION 0x020400 +#define TPACPI_SYSFS_VERSION 0x020500 /* * Changelog: @@ -145,6 +145,51 @@ enum { TP_ACPI_WGSV_STATE_UWBPWR = 0x0020, /* UWB radio enabled */ }; +/* HKEY events */ +enum tpacpi_hkey_event_t { + /* Hotkey-related */ + TP_HKEY_EV_HOTKEY_BASE = 0x1001, /* first hotkey (FN+F1) */ + TP_HKEY_EV_BRGHT_UP = 0x1010, /* Brightness up */ + TP_HKEY_EV_BRGHT_DOWN = 0x1011, /* Brightness down */ + TP_HKEY_EV_VOL_UP = 0x1015, /* Volume up or unmute */ + TP_HKEY_EV_VOL_DOWN = 0x1016, /* Volume down or unmute */ + TP_HKEY_EV_VOL_MUTE = 0x1017, /* Mixer output mute */ + + /* Reasons for waking up from S3/S4 */ + TP_HKEY_EV_WKUP_S3_UNDOCK = 0x2304, /* undock requested, S3 */ + TP_HKEY_EV_WKUP_S4_UNDOCK = 0x2404, /* undock requested, S4 */ + TP_HKEY_EV_WKUP_S3_BAYEJ = 0x2305, /* bay ejection req, S3 */ + TP_HKEY_EV_WKUP_S4_BAYEJ = 0x2405, /* bay ejection req, S4 */ + TP_HKEY_EV_WKUP_S3_BATLOW = 0x2313, /* battery empty, S3 */ + TP_HKEY_EV_WKUP_S4_BATLOW = 0x2413, /* battery empty, S4 */ + + /* Auto-sleep after eject request */ + TP_HKEY_EV_BAYEJ_ACK = 0x3003, /* bay ejection complete */ + TP_HKEY_EV_UNDOCK_ACK = 0x4003, /* undock complete */ + + /* Misc bay events */ + TP_HKEY_EV_OPTDRV_EJ = 0x3006, /* opt. drive tray ejected */ + + /* User-interface events */ + TP_HKEY_EV_LID_CLOSE = 0x5001, /* laptop lid closed */ + TP_HKEY_EV_LID_OPEN = 0x5002, /* laptop lid opened */ + TP_HKEY_EV_TABLET_TABLET = 0x5009, /* tablet swivel up */ + TP_HKEY_EV_TABLET_NOTEBOOK = 0x500a, /* tablet swivel down */ + TP_HKEY_EV_PEN_INSERTED = 0x500b, /* tablet pen inserted */ + TP_HKEY_EV_PEN_REMOVED = 0x500c, /* tablet pen removed */ + TP_HKEY_EV_BRGHT_CHANGED = 0x5010, /* backlight control event */ + + /* Thermal events */ + TP_HKEY_EV_ALARM_BAT_HOT = 0x6011, /* battery too hot */ + TP_HKEY_EV_ALARM_BAT_XHOT = 0x6012, /* battery critically hot */ + TP_HKEY_EV_ALARM_SENSOR_HOT = 0x6021, /* sensor too hot */ + TP_HKEY_EV_ALARM_SENSOR_XHOT = 0x6022, /* sensor critically hot */ + TP_HKEY_EV_THM_TABLE_CHANGED = 0x6030, /* thermal table changed */ + + /* Misc */ + TP_HKEY_EV_RFKILL_CHANGED = 0x7000, /* rfkill switch changed */ +}; + /**************************************************************************** * Main driver */ @@ -1278,6 +1323,7 @@ static void tpacpi_destroy_rfkill(const enum tpacpi_rfk_id id) tp_rfk = tpacpi_rfkill_switches[id]; if (tp_rfk) { rfkill_unregister(tp_rfk->rfkill); + rfkill_destroy(tp_rfk->rfkill); tpacpi_rfkill_switches[id] = NULL; kfree(tp_rfk); } @@ -1601,6 +1647,208 @@ static void tpacpi_remove_driver_attributes(struct device_driver *drv) #endif } +/************************************************************************* + * Firmware Data + */ + +/* + * Table of recommended minimum BIOS versions + * + * Reasons for listing: + * 1. Stable BIOS, listed because the unknown ammount of + * bugs and bad ACPI behaviour on older versions + * + * 2. BIOS or EC fw with known bugs that trigger on Linux + * + * 3. BIOS with known reduced functionality in older versions + * + * We recommend the latest BIOS and EC version. + * We only support the latest BIOS and EC fw version as a rule. + * + * Sources: IBM ThinkPad Public Web Documents (update changelogs), + * Information from users in ThinkWiki + * + * WARNING: we use this table also to detect that the machine is + * a ThinkPad in some cases, so don't remove entries lightly. + */ + +#define TPV_Q(__v, __id1, __id2, __bv1, __bv2) \ + { .vendor = (__v), \ + .bios = TPID(__id1, __id2), \ + .ec = TPACPI_MATCH_ANY, \ + .quirks = TPACPI_MATCH_ANY << 16 \ + | (__bv1) << 8 | (__bv2) } + +#define TPV_Q_X(__v, __bid1, __bid2, __bv1, __bv2, \ + __eid, __ev1, __ev2) \ + { .vendor = (__v), \ + .bios = TPID(__bid1, __bid2), \ + .ec = __eid, \ + .quirks = (__ev1) << 24 | (__ev2) << 16 \ + | (__bv1) << 8 | (__bv2) } + +#define TPV_QI0(__id1, __id2, __bv1, __bv2) \ + TPV_Q(PCI_VENDOR_ID_IBM, __id1, __id2, __bv1, __bv2) + +/* Outdated IBM BIOSes often lack the EC id string */ +#define TPV_QI1(__id1, __id2, __bv1, __bv2, __ev1, __ev2) \ + TPV_Q_X(PCI_VENDOR_ID_IBM, __id1, __id2, \ + __bv1, __bv2, TPID(__id1, __id2), \ + __ev1, __ev2), \ + TPV_Q_X(PCI_VENDOR_ID_IBM, __id1, __id2, \ + __bv1, __bv2, TPACPI_MATCH_UNKNOWN, \ + __ev1, __ev2) + +/* Outdated IBM BIOSes often lack the EC id string */ +#define TPV_QI2(__bid1, __bid2, __bv1, __bv2, \ + __eid1, __eid2, __ev1, __ev2) \ + TPV_Q_X(PCI_VENDOR_ID_IBM, __bid1, __bid2, \ + __bv1, __bv2, TPID(__eid1, __eid2), \ + __ev1, __ev2), \ + TPV_Q_X(PCI_VENDOR_ID_IBM, __bid1, __bid2, \ + __bv1, __bv2, TPACPI_MATCH_UNKNOWN, \ + __ev1, __ev2) + +#define TPV_QL0(__id1, __id2, __bv1, __bv2) \ + TPV_Q(PCI_VENDOR_ID_LENOVO, __id1, __id2, __bv1, __bv2) + +#define TPV_QL1(__id1, __id2, __bv1, __bv2, __ev1, __ev2) \ + TPV_Q_X(PCI_VENDOR_ID_LENOVO, __id1, __id2, \ + __bv1, __bv2, TPID(__id1, __id2), \ + __ev1, __ev2) + +#define TPV_QL2(__bid1, __bid2, __bv1, __bv2, \ + __eid1, __eid2, __ev1, __ev2) \ + TPV_Q_X(PCI_VENDOR_ID_LENOVO, __bid1, __bid2, \ + __bv1, __bv2, TPID(__eid1, __eid2), \ + __ev1, __ev2) + +static const struct tpacpi_quirk tpacpi_bios_version_qtable[] __initconst = { + /* Numeric models ------------------ */ + /* FW MODEL BIOS VERS */ + TPV_QI0('I', 'M', '6', '5'), /* 570 */ + TPV_QI0('I', 'U', '2', '6'), /* 570E */ + TPV_QI0('I', 'B', '5', '4'), /* 600 */ + TPV_QI0('I', 'H', '4', '7'), /* 600E */ + TPV_QI0('I', 'N', '3', '6'), /* 600E */ + TPV_QI0('I', 'T', '5', '5'), /* 600X */ + TPV_QI0('I', 'D', '4', '8'), /* 770, 770E, 770ED */ + TPV_QI0('I', 'I', '4', '2'), /* 770X */ + TPV_QI0('I', 'O', '2', '3'), /* 770Z */ + + /* A-series ------------------------- */ + /* FW MODEL BIOS VERS EC VERS */ + TPV_QI0('I', 'W', '5', '9'), /* A20m */ + TPV_QI0('I', 'V', '6', '9'), /* A20p */ + TPV_QI0('1', '0', '2', '6'), /* A21e, A22e */ + TPV_QI0('K', 'U', '3', '6'), /* A21e */ + TPV_QI0('K', 'X', '3', '6'), /* A21m, A22m */ + TPV_QI0('K', 'Y', '3', '8'), /* A21p, A22p */ + TPV_QI0('1', 'B', '1', '7'), /* A22e */ + TPV_QI0('1', '3', '2', '0'), /* A22m */ + TPV_QI0('1', 'E', '7', '3'), /* A30/p (0) */ + TPV_QI1('1', 'G', '4', '1', '1', '7'), /* A31/p (0) */ + TPV_QI1('1', 'N', '1', '6', '0', '7'), /* A31/p (0) */ + + /* G-series ------------------------- */ + /* FW MODEL BIOS VERS */ + TPV_QI0('1', 'T', 'A', '6'), /* G40 */ + TPV_QI0('1', 'X', '5', '7'), /* G41 */ + + /* R-series, T-series --------------- */ + /* FW MODEL BIOS VERS EC VERS */ + TPV_QI0('1', 'C', 'F', '0'), /* R30 */ + TPV_QI0('1', 'F', 'F', '1'), /* R31 */ + TPV_QI0('1', 'M', '9', '7'), /* R32 */ + TPV_QI0('1', 'O', '6', '1'), /* R40 */ + TPV_QI0('1', 'P', '6', '5'), /* R40 */ + TPV_QI0('1', 'S', '7', '0'), /* R40e */ + TPV_QI1('1', 'R', 'D', 'R', '7', '1'), /* R50/p, R51, + T40/p, T41/p, T42/p (1) */ + TPV_QI1('1', 'V', '7', '1', '2', '8'), /* R50e, R51 (1) */ + TPV_QI1('7', '8', '7', '1', '0', '6'), /* R51e (1) */ + TPV_QI1('7', '6', '6', '9', '1', '6'), /* R52 (1) */ + TPV_QI1('7', '0', '6', '9', '2', '8'), /* R52, T43 (1) */ + + TPV_QI0('I', 'Y', '6', '1'), /* T20 */ + TPV_QI0('K', 'Z', '3', '4'), /* T21 */ + TPV_QI0('1', '6', '3', '2'), /* T22 */ + TPV_QI1('1', 'A', '6', '4', '2', '3'), /* T23 (0) */ + TPV_QI1('1', 'I', '7', '1', '2', '0'), /* T30 (0) */ + TPV_QI1('1', 'Y', '6', '5', '2', '9'), /* T43/p (1) */ + + TPV_QL1('7', '9', 'E', '3', '5', '0'), /* T60/p */ + TPV_QL1('7', 'C', 'D', '2', '2', '2'), /* R60, R60i */ + TPV_QL0('7', 'E', 'D', '0'), /* R60e, R60i */ + + /* BIOS FW BIOS VERS EC FW EC VERS */ + TPV_QI2('1', 'W', '9', '0', '1', 'V', '2', '8'), /* R50e (1) */ + TPV_QL2('7', 'I', '3', '4', '7', '9', '5', '0'), /* T60/p wide */ + + /* X-series ------------------------- */ + /* FW MODEL BIOS VERS EC VERS */ + TPV_QI0('I', 'Z', '9', 'D'), /* X20, X21 */ + TPV_QI0('1', 'D', '7', '0'), /* X22, X23, X24 */ + TPV_QI1('1', 'K', '4', '8', '1', '8'), /* X30 (0) */ + TPV_QI1('1', 'Q', '9', '7', '2', '3'), /* X31, X32 (0) */ + TPV_QI1('1', 'U', 'D', '3', 'B', '2'), /* X40 (0) */ + TPV_QI1('7', '4', '6', '4', '2', '7'), /* X41 (0) */ + TPV_QI1('7', '5', '6', '0', '2', '0'), /* X41t (0) */ + + TPV_QL0('7', 'B', 'D', '7'), /* X60/s */ + TPV_QL0('7', 'J', '3', '0'), /* X60t */ + + /* (0) - older versions lack DMI EC fw string and functionality */ + /* (1) - older versions known to lack functionality */ +}; + +#undef TPV_QL1 +#undef TPV_QL0 +#undef TPV_QI2 +#undef TPV_QI1 +#undef TPV_QI0 +#undef TPV_Q_X +#undef TPV_Q + +static void __init tpacpi_check_outdated_fw(void) +{ + unsigned long fwvers; + u16 ec_version, bios_version; + + fwvers = tpacpi_check_quirks(tpacpi_bios_version_qtable, + ARRAY_SIZE(tpacpi_bios_version_qtable)); + + if (!fwvers) + return; + + bios_version = fwvers & 0xffffU; + ec_version = (fwvers >> 16) & 0xffffU; + + /* note that unknown versions are set to 0x0000 and we use that */ + if ((bios_version > thinkpad_id.bios_release) || + (ec_version > thinkpad_id.ec_release && + ec_version != TPACPI_MATCH_ANY)) { + /* + * The changelogs would let us track down the exact + * reason, but it is just too much of a pain to track + * it. We only list BIOSes that are either really + * broken, or really stable to begin with, so it is + * best if the user upgrades the firmware anyway. + */ + printk(TPACPI_WARN + "WARNING: Outdated ThinkPad BIOS/EC firmware\n"); + printk(TPACPI_WARN + "WARNING: This firmware may be missing critical bug " + "fixes and/or important features\n"); + } +} + +static bool __init tpacpi_is_fw_known(void) +{ + return tpacpi_check_quirks(tpacpi_bios_version_qtable, + ARRAY_SIZE(tpacpi_bios_version_qtable)) != 0; +} + /**************************************************************************** **************************************************************************** * @@ -1634,6 +1882,7 @@ static int __init thinkpad_acpi_driver_init(struct ibm_init_struct *iibm) (thinkpad_id.nummodel_str) ? thinkpad_id.nummodel_str : "unknown"); + tpacpi_check_outdated_fw(); return 0; } @@ -1656,6 +1905,27 @@ static struct ibm_struct thinkpad_acpi_driver_data = { * Hotkey subdriver */ +/* + * ThinkPad firmware event model + * + * The ThinkPad firmware has two main event interfaces: normal ACPI + * notifications (which follow the ACPI standard), and a private event + * interface. + * + * The private event interface also issues events for the hotkeys. As + * the driver gained features, the event handling code ended up being + * built around the hotkey subdriver. This will need to be refactored + * to a more formal event API eventually. + * + * Some "hotkeys" are actually supposed to be used as event reports, + * such as "brightness has changed", "volume has changed", depending on + * the ThinkPad model and how the firmware is operating. + * + * Unlike other classes, hotkey-class events have mask/unmask control on + * non-ancient firmware. However, how it behaves changes a lot with the + * firmware model and version. + */ + enum { /* hot key scan codes (derived from ACPI DSDT) */ TP_ACPI_HOTKEYSCAN_FNF1 = 0, TP_ACPI_HOTKEYSCAN_FNF2, @@ -1683,7 +1953,7 @@ enum { /* hot key scan codes (derived from ACPI DSDT) */ TP_ACPI_HOTKEYSCAN_THINKPAD, }; -enum { /* Keys available through NVRAM polling */ +enum { /* Keys/events available through NVRAM polling */ TPACPI_HKEY_NVRAM_KNOWN_MASK = 0x00fb88c0U, TPACPI_HKEY_NVRAM_GOOD_MASK = 0x00fb8000U, }; @@ -1731,16 +2001,50 @@ struct tp_nvram_state { u8 volume_level; }; +/* kthread for the hotkey poller */ static struct task_struct *tpacpi_hotkey_task; -static u32 hotkey_source_mask; /* bit mask 0=ACPI,1=NVRAM */ -static int hotkey_poll_freq = 10; /* Hz */ + +/* Acquired while the poller kthread is running, use to sync start/stop */ static struct mutex hotkey_thread_mutex; + +/* + * Acquire mutex to write poller control variables as an + * atomic block. + * + * Increment hotkey_config_change when changing them if you + * want the kthread to forget old state. + * + * See HOTKEY_CONFIG_CRITICAL_START/HOTKEY_CONFIG_CRITICAL_END + */ static struct mutex hotkey_thread_data_mutex; static unsigned int hotkey_config_change; +/* + * hotkey poller control variables + * + * Must be atomic or readers will also need to acquire mutex + * + * HOTKEY_CONFIG_CRITICAL_START/HOTKEY_CONFIG_CRITICAL_END + * should be used only when the changes need to be taken as + * a block, OR when one needs to force the kthread to forget + * old state. + */ +static u32 hotkey_source_mask; /* bit mask 0=ACPI,1=NVRAM */ +static unsigned int hotkey_poll_freq = 10; /* Hz */ + +#define HOTKEY_CONFIG_CRITICAL_START \ + do { \ + mutex_lock(&hotkey_thread_data_mutex); \ + hotkey_config_change++; \ + } while (0); +#define HOTKEY_CONFIG_CRITICAL_END \ + mutex_unlock(&hotkey_thread_data_mutex); + #else /* CONFIG_THINKPAD_ACPI_HOTKEY_POLL */ #define hotkey_source_mask 0U +#define HOTKEY_CONFIG_CRITICAL_START +#define HOTKEY_CONFIG_CRITICAL_END #endif /* CONFIG_THINKPAD_ACPI_HOTKEY_POLL */ @@ -1754,10 +2058,12 @@ static enum { /* Reasons for waking up */ static int hotkey_autosleep_ack; -static u32 hotkey_orig_mask; -static u32 hotkey_all_mask; -static u32 hotkey_reserved_mask; -static u32 hotkey_mask; +static u32 hotkey_orig_mask; /* events the BIOS had enabled */ +static u32 hotkey_all_mask; /* all events supported in fw */ +static u32 hotkey_reserved_mask; /* events better left disabled */ +static u32 hotkey_driver_mask; /* events needed by the driver */ +static u32 hotkey_user_mask; /* events visible to userspace */ +static u32 hotkey_acpi_mask; /* events enabled in firmware */ static unsigned int hotkey_report_mode; @@ -1765,18 +2071,8 @@ static u16 *hotkey_keycode_map; static struct attribute_set *hotkey_dev_attributes; -#ifdef CONFIG_THINKPAD_ACPI_HOTKEY_POLL -#define HOTKEY_CONFIG_CRITICAL_START \ - do { \ - mutex_lock(&hotkey_thread_data_mutex); \ - hotkey_config_change++; \ - } while (0); -#define HOTKEY_CONFIG_CRITICAL_END \ - mutex_unlock(&hotkey_thread_data_mutex); -#else -#define HOTKEY_CONFIG_CRITICAL_START -#define HOTKEY_CONFIG_CRITICAL_END -#endif /* CONFIG_THINKPAD_ACPI_HOTKEY_POLL */ +static void tpacpi_driver_event(const unsigned int hkey_event); +static void hotkey_driver_event(const unsigned int scancode); /* HKEY.MHKG() return bits */ #define TP_HOTKEY_TABLET_MASK (1 << 3) @@ -1812,22 +2108,53 @@ static int hotkey_get_tablet_mode(int *status) } /* + * Reads current event mask from firmware, and updates + * hotkey_acpi_mask accordingly. Also resets any bits + * from hotkey_user_mask that are unavailable to be + * delivered (shadow requirement of the userspace ABI). + * * Call with hotkey_mutex held */ static int hotkey_mask_get(void) { - u32 m = 0; - if (tp_features.hotkey_mask) { + u32 m = 0; + if (!acpi_evalf(hkey_handle, &m, "DHKN", "d")) return -EIO; + + hotkey_acpi_mask = m; + } else { + /* no mask support doesn't mean no event support... */ + hotkey_acpi_mask = hotkey_all_mask; } - hotkey_mask = m | (hotkey_source_mask & hotkey_mask); + + /* sync userspace-visible mask */ + hotkey_user_mask &= (hotkey_acpi_mask | hotkey_source_mask); return 0; } +void static hotkey_mask_warn_incomplete_mask(void) +{ + /* log only what the user can fix... */ + const u32 wantedmask = hotkey_driver_mask & + ~(hotkey_acpi_mask | hotkey_source_mask) & + (hotkey_all_mask | TPACPI_HKEY_NVRAM_KNOWN_MASK); + + if (wantedmask) + printk(TPACPI_NOTICE + "required events 0x%08x not enabled!\n", + wantedmask); +} + /* + * Set the firmware mask when supported + * + * Also calls hotkey_mask_get to update hotkey_acpi_mask. + * + * NOTE: does not set bits in hotkey_user_mask, but may reset them. + * * Call with hotkey_mutex held */ static int hotkey_mask_set(u32 mask) @@ -1835,66 +2162,100 @@ static int hotkey_mask_set(u32 mask) int i; int rc = 0; - if (tp_features.hotkey_mask) { - if (!tp_warned.hotkey_mask_ff && - (mask == 0xffff || mask == 0xffffff || - mask == 0xffffffff)) { - tp_warned.hotkey_mask_ff = 1; - printk(TPACPI_NOTICE - "setting the hotkey mask to 0x%08x is likely " - "not the best way to go about it\n", mask); - printk(TPACPI_NOTICE - "please consider using the driver defaults, " - "and refer to up-to-date thinkpad-acpi " - "documentation\n"); - } + const u32 fwmask = mask & ~hotkey_source_mask; - HOTKEY_CONFIG_CRITICAL_START + if (tp_features.hotkey_mask) { for (i = 0; i < 32; i++) { - u32 m = 1 << i; - /* enable in firmware mask only keys not in NVRAM - * mode, but enable the key in the cached hotkey_mask - * regardless of mode, or the key will end up - * disabled by hotkey_mask_get() */ if (!acpi_evalf(hkey_handle, NULL, "MHKM", "vdd", i + 1, - !!((mask & ~hotkey_source_mask) & m))) { + !!(mask & (1 << i)))) { rc = -EIO; break; - } else { - hotkey_mask = (hotkey_mask & ~m) | (mask & m); } } - HOTKEY_CONFIG_CRITICAL_END + } - /* hotkey_mask_get must be called unconditionally below */ - if (!hotkey_mask_get() && !rc && - (hotkey_mask & ~hotkey_source_mask) != - (mask & ~hotkey_source_mask)) { - printk(TPACPI_NOTICE - "requested hot key mask 0x%08x, but " - "firmware forced it to 0x%08x\n", - mask, hotkey_mask); - } - } else { -#ifdef CONFIG_THINKPAD_ACPI_HOTKEY_POLL - HOTKEY_CONFIG_CRITICAL_START - hotkey_mask = mask & hotkey_source_mask; - HOTKEY_CONFIG_CRITICAL_END - hotkey_mask_get(); - if (hotkey_mask != mask) { - printk(TPACPI_NOTICE - "requested hot key mask 0x%08x, " - "forced to 0x%08x (NVRAM poll mask is " - "0x%08x): no firmware mask support\n", - mask, hotkey_mask, hotkey_source_mask); - } -#else - hotkey_mask_get(); - rc = -ENXIO; -#endif /* CONFIG_THINKPAD_ACPI_HOTKEY_POLL */ + /* + * We *must* make an inconditional call to hotkey_mask_get to + * refresh hotkey_acpi_mask and update hotkey_user_mask + * + * Take the opportunity to also log when we cannot _enable_ + * a given event. + */ + if (!hotkey_mask_get() && !rc && (fwmask & ~hotkey_acpi_mask)) { + printk(TPACPI_NOTICE + "asked for hotkey mask 0x%08x, but " + "firmware forced it to 0x%08x\n", + fwmask, hotkey_acpi_mask); } + hotkey_mask_warn_incomplete_mask(); + + return rc; +} + +/* + * Sets hotkey_user_mask and tries to set the firmware mask + * + * Call with hotkey_mutex held + */ +static int hotkey_user_mask_set(const u32 mask) +{ + int rc; + + /* Give people a chance to notice they are doing something that + * is bound to go boom on their users sooner or later */ + if (!tp_warned.hotkey_mask_ff && + (mask == 0xffff || mask == 0xffffff || + mask == 0xffffffff)) { + tp_warned.hotkey_mask_ff = 1; + printk(TPACPI_NOTICE + "setting the hotkey mask to 0x%08x is likely " + "not the best way to go about it\n", mask); + printk(TPACPI_NOTICE + "please consider using the driver defaults, " + "and refer to up-to-date thinkpad-acpi " + "documentation\n"); + } + + /* Try to enable what the user asked for, plus whatever we need. + * this syncs everything but won't enable bits in hotkey_user_mask */ + rc = hotkey_mask_set((mask | hotkey_driver_mask) & ~hotkey_source_mask); + + /* Enable the available bits in hotkey_user_mask */ + hotkey_user_mask = mask & (hotkey_acpi_mask | hotkey_source_mask); + + return rc; +} + +/* + * Sets the driver hotkey mask. + * + * Can be called even if the hotkey subdriver is inactive + */ +static int tpacpi_hotkey_driver_mask_set(const u32 mask) +{ + int rc; + + /* Do the right thing if hotkey_init has not been called yet */ + if (!tp_features.hotkey) { + hotkey_driver_mask = mask; + return 0; + } + + mutex_lock(&hotkey_mutex); + + HOTKEY_CONFIG_CRITICAL_START + hotkey_driver_mask = mask; +#ifdef CONFIG_THINKPAD_ACPI_HOTKEY_POLL + hotkey_source_mask |= (mask & ~hotkey_all_mask); +#endif + HOTKEY_CONFIG_CRITICAL_END + + rc = hotkey_mask_set((hotkey_acpi_mask | hotkey_driver_mask) & + ~hotkey_source_mask); + mutex_unlock(&hotkey_mutex); + return rc; } @@ -1930,11 +2291,10 @@ static void tpacpi_input_send_tabletsw(void) } } -static void tpacpi_input_send_key(unsigned int scancode) +/* Do NOT call without validating scancode first */ +static void tpacpi_input_send_key(const unsigned int scancode) { - unsigned int keycode; - - keycode = hotkey_keycode_map[scancode]; + const unsigned int keycode = hotkey_keycode_map[scancode]; if (keycode != KEY_RESERVED) { mutex_lock(&tpacpi_inputdev_send_mutex); @@ -1955,19 +2315,28 @@ static void tpacpi_input_send_key(unsigned int scancode) } } +/* Do NOT call without validating scancode first */ +static void tpacpi_input_send_key_masked(const unsigned int scancode) +{ + hotkey_driver_event(scancode); + if (hotkey_user_mask & (1 << scancode)) + tpacpi_input_send_key(scancode); +} + #ifdef CONFIG_THINKPAD_ACPI_HOTKEY_POLL static struct tp_acpi_drv_struct ibm_hotkey_acpidriver; +/* Do NOT call without validating scancode first */ static void tpacpi_hotkey_send_key(unsigned int scancode) { - tpacpi_input_send_key(scancode); + tpacpi_input_send_key_masked(scancode); if (hotkey_report_mode < 2) { acpi_bus_generate_proc_event(ibm_hotkey_acpidriver.device, - 0x80, 0x1001 + scancode); + 0x80, TP_HKEY_EV_HOTKEY_BASE + scancode); } } -static void hotkey_read_nvram(struct tp_nvram_state *n, u32 m) +static void hotkey_read_nvram(struct tp_nvram_state *n, const u32 m) { u8 d; @@ -2003,21 +2372,24 @@ static void hotkey_read_nvram(struct tp_nvram_state *n, u32 m) } } +static void hotkey_compare_and_issue_event(struct tp_nvram_state *oldn, + struct tp_nvram_state *newn, + const u32 event_mask) +{ + #define TPACPI_COMPARE_KEY(__scancode, __member) \ do { \ - if ((mask & (1 << __scancode)) && \ + if ((event_mask & (1 << __scancode)) && \ oldn->__member != newn->__member) \ - tpacpi_hotkey_send_key(__scancode); \ + tpacpi_hotkey_send_key(__scancode); \ } while (0) #define TPACPI_MAY_SEND_KEY(__scancode) \ - do { if (mask & (1 << __scancode)) \ - tpacpi_hotkey_send_key(__scancode); } while (0) + do { \ + if (event_mask & (1 << __scancode)) \ + tpacpi_hotkey_send_key(__scancode); \ + } while (0) -static void hotkey_compare_and_issue_event(struct tp_nvram_state *oldn, - struct tp_nvram_state *newn, - u32 mask) -{ TPACPI_COMPARE_KEY(TP_ACPI_HOTKEYSCAN_THINKPAD, thinkpad_toggle); TPACPI_COMPARE_KEY(TP_ACPI_HOTKEYSCAN_FNSPACE, zoom_toggle); TPACPI_COMPARE_KEY(TP_ACPI_HOTKEYSCAN_FNF7, display_toggle); @@ -2063,18 +2435,26 @@ static void hotkey_compare_and_issue_event(struct tp_nvram_state *oldn, } } } -} #undef TPACPI_COMPARE_KEY #undef TPACPI_MAY_SEND_KEY +} +/* + * Polling driver + * + * We track all events in hotkey_source_mask all the time, since + * most of them are edge-based. We only issue those requested by + * hotkey_user_mask or hotkey_driver_mask, though. + */ static int hotkey_kthread(void *data) { struct tp_nvram_state s[2]; - u32 mask; + u32 poll_mask, event_mask; unsigned int si, so; unsigned long t; unsigned int change_detector, must_reset; + unsigned int poll_freq; mutex_lock(&hotkey_thread_mutex); @@ -2090,13 +2470,20 @@ static int hotkey_kthread(void *data) /* Initial state for compares */ mutex_lock(&hotkey_thread_data_mutex); change_detector = hotkey_config_change; - mask = hotkey_source_mask & hotkey_mask; + poll_mask = hotkey_source_mask; + event_mask = hotkey_source_mask & + (hotkey_driver_mask | hotkey_user_mask); + poll_freq = hotkey_poll_freq; mutex_unlock(&hotkey_thread_data_mutex); - hotkey_read_nvram(&s[so], mask); + hotkey_read_nvram(&s[so], poll_mask); - while (!kthread_should_stop() && hotkey_poll_freq) { - if (t == 0) - t = 1000/hotkey_poll_freq; + while (!kthread_should_stop()) { + if (t == 0) { + if (likely(poll_freq)) + t = 1000/poll_freq; + else + t = 100; /* should never happen... */ + } t = msleep_interruptible(t); if (unlikely(kthread_should_stop())) break; @@ -2111,14 +2498,17 @@ static int hotkey_kthread(void *data) t = 0; change_detector = hotkey_config_change; } - mask = hotkey_source_mask & hotkey_mask; + poll_mask = hotkey_source_mask; + event_mask = hotkey_source_mask & + (hotkey_driver_mask | hotkey_user_mask); + poll_freq = hotkey_poll_freq; mutex_unlock(&hotkey_thread_data_mutex); - if (likely(mask)) { - hotkey_read_nvram(&s[si], mask); + if (likely(poll_mask)) { + hotkey_read_nvram(&s[si], poll_mask); if (likely(si != so)) { hotkey_compare_and_issue_event(&s[so], &s[si], - mask); + event_mask); } } @@ -2131,6 +2521,7 @@ exit: return 0; } +/* call with hotkey_mutex held */ static void hotkey_poll_stop_sync(void) { if (tpacpi_hotkey_task) { @@ -2147,11 +2538,14 @@ static void hotkey_poll_stop_sync(void) } /* call with hotkey_mutex held */ -static void hotkey_poll_setup(int may_warn) +static void hotkey_poll_setup(bool may_warn) { - if ((hotkey_source_mask & hotkey_mask) != 0 && - hotkey_poll_freq > 0 && - (tpacpi_inputdev->users > 0 || hotkey_report_mode < 2)) { + const u32 poll_driver_mask = hotkey_driver_mask & hotkey_source_mask; + const u32 poll_user_mask = hotkey_user_mask & hotkey_source_mask; + + if (hotkey_poll_freq > 0 && + (poll_driver_mask || + (poll_user_mask && tpacpi_inputdev->users > 0))) { if (!tpacpi_hotkey_task) { tpacpi_hotkey_task = kthread_run(hotkey_kthread, NULL, TPACPI_NVRAM_KTHREAD_NAME); @@ -2164,26 +2558,36 @@ static void hotkey_poll_setup(int may_warn) } } else { hotkey_poll_stop_sync(); - if (may_warn && - hotkey_source_mask != 0 && hotkey_poll_freq == 0) { + if (may_warn && (poll_driver_mask || poll_user_mask) && + hotkey_poll_freq == 0) { printk(TPACPI_NOTICE - "hot keys 0x%08x require polling, " - "which is currently disabled\n", - hotkey_source_mask); + "hot keys 0x%08x and/or events 0x%08x " + "require polling, which is currently " + "disabled\n", + poll_user_mask, poll_driver_mask); } } } -static void hotkey_poll_setup_safe(int may_warn) +static void hotkey_poll_setup_safe(bool may_warn) { mutex_lock(&hotkey_mutex); hotkey_poll_setup(may_warn); mutex_unlock(&hotkey_mutex); } +/* call with hotkey_mutex held */ +static void hotkey_poll_set_freq(unsigned int freq) +{ + if (!freq) + hotkey_poll_stop_sync(); + + hotkey_poll_freq = freq; +} + #else /* CONFIG_THINKPAD_ACPI_HOTKEY_POLL */ -static void hotkey_poll_setup_safe(int __unused) +static void hotkey_poll_setup_safe(bool __unused) { } @@ -2201,7 +2605,7 @@ static int hotkey_inputdev_open(struct input_dev *dev) case TPACPI_LIFE_EXITING: return -EBUSY; case TPACPI_LIFE_RUNNING: - hotkey_poll_setup_safe(0); + hotkey_poll_setup_safe(false); return 0; } @@ -2213,8 +2617,9 @@ static int hotkey_inputdev_open(struct input_dev *dev) static void hotkey_inputdev_close(struct input_dev *dev) { /* disable hotkey polling when possible */ - if (tpacpi_lifecycle == TPACPI_LIFE_RUNNING) - hotkey_poll_setup_safe(0); + if (tpacpi_lifecycle == TPACPI_LIFE_RUNNING && + !(hotkey_source_mask & hotkey_driver_mask)) + hotkey_poll_setup_safe(false); } /* sysfs hotkey enable ------------------------------------------------- */ @@ -2261,15 +2666,7 @@ static ssize_t hotkey_mask_show(struct device *dev, struct device_attribute *attr, char *buf) { - int res; - - if (mutex_lock_killable(&hotkey_mutex)) - return -ERESTARTSYS; - res = hotkey_mask_get(); - mutex_unlock(&hotkey_mutex); - - return (res)? - res : snprintf(buf, PAGE_SIZE, "0x%08x\n", hotkey_mask); + return snprintf(buf, PAGE_SIZE, "0x%08x\n", hotkey_user_mask); } static ssize_t hotkey_mask_store(struct device *dev, @@ -2285,10 +2682,10 @@ static ssize_t hotkey_mask_store(struct device *dev, if (mutex_lock_killable(&hotkey_mutex)) return -ERESTARTSYS; - res = hotkey_mask_set(t); + res = hotkey_user_mask_set(t); #ifdef CONFIG_THINKPAD_ACPI_HOTKEY_POLL - hotkey_poll_setup(1); + hotkey_poll_setup(true); #endif mutex_unlock(&hotkey_mutex); @@ -2318,6 +2715,8 @@ static ssize_t hotkey_bios_mask_show(struct device *dev, struct device_attribute *attr, char *buf) { + printk_deprecated_attribute("hotkey_bios_mask", + "This attribute is useless."); return snprintf(buf, PAGE_SIZE, "0x%08x\n", hotkey_orig_mask); } @@ -2365,6 +2764,8 @@ static ssize_t hotkey_source_mask_store(struct device *dev, const char *buf, size_t count) { unsigned long t; + u32 r_ev; + int rc; if (parse_strtoul(buf, 0xffffffffUL, &t) || ((t & ~TPACPI_HKEY_NVRAM_KNOWN_MASK) != 0)) @@ -2377,13 +2778,28 @@ static ssize_t hotkey_source_mask_store(struct device *dev, hotkey_source_mask = t; HOTKEY_CONFIG_CRITICAL_END - hotkey_poll_setup(1); + rc = hotkey_mask_set((hotkey_user_mask | hotkey_driver_mask) & + ~hotkey_source_mask); + hotkey_poll_setup(true); + + /* check if events needed by the driver got disabled */ + r_ev = hotkey_driver_mask & ~(hotkey_acpi_mask & hotkey_all_mask) + & ~hotkey_source_mask & TPACPI_HKEY_NVRAM_KNOWN_MASK; mutex_unlock(&hotkey_mutex); + if (rc < 0) + printk(TPACPI_ERR "hotkey_source_mask: failed to update the" + "firmware event mask!\n"); + + if (r_ev) + printk(TPACPI_NOTICE "hotkey_source_mask: " + "some important events were disabled: " + "0x%04x\n", r_ev); + tpacpi_disclose_usertask("hotkey_source_mask", "set to 0x%08lx\n", t); - return count; + return (rc < 0) ? rc : count; } static struct device_attribute dev_attr_hotkey_source_mask = @@ -2410,9 +2826,9 @@ static ssize_t hotkey_poll_freq_store(struct device *dev, if (mutex_lock_killable(&hotkey_mutex)) return -ERESTARTSYS; - hotkey_poll_freq = t; + hotkey_poll_set_freq(t); + hotkey_poll_setup(true); - hotkey_poll_setup(1); mutex_unlock(&hotkey_mutex); tpacpi_disclose_usertask("hotkey_poll_freq", "set to %lu\n", t); @@ -2501,9 +2917,8 @@ static struct device_attribute dev_attr_hotkey_wakeup_reason = static void hotkey_wakeup_reason_notify_change(void) { - if (tp_features.hotkey_mask) - sysfs_notify(&tpacpi_pdev->dev.kobj, NULL, - "wakeup_reason"); + sysfs_notify(&tpacpi_pdev->dev.kobj, NULL, + "wakeup_reason"); } /* sysfs wakeup hotunplug_complete (pollable) -------------------------- */ @@ -2520,9 +2935,8 @@ static struct device_attribute dev_attr_hotkey_wakeup_hotunplug_complete = static void hotkey_wakeup_hotunplug_complete_notify_change(void) { - if (tp_features.hotkey_mask) - sysfs_notify(&tpacpi_pdev->dev.kobj, NULL, - "wakeup_hotunplug_complete"); + sysfs_notify(&tpacpi_pdev->dev.kobj, NULL, + "wakeup_hotunplug_complete"); } /* --------------------------------------------------------------------- */ @@ -2530,27 +2944,19 @@ static void hotkey_wakeup_hotunplug_complete_notify_change(void) static struct attribute *hotkey_attributes[] __initdata = { &dev_attr_hotkey_enable.attr, &dev_attr_hotkey_bios_enabled.attr, + &dev_attr_hotkey_bios_mask.attr, &dev_attr_hotkey_report_mode.attr, -#ifdef CONFIG_THINKPAD_ACPI_HOTKEY_POLL + &dev_attr_hotkey_wakeup_reason.attr, + &dev_attr_hotkey_wakeup_hotunplug_complete.attr, &dev_attr_hotkey_mask.attr, &dev_attr_hotkey_all_mask.attr, &dev_attr_hotkey_recommended_mask.attr, +#ifdef CONFIG_THINKPAD_ACPI_HOTKEY_POLL &dev_attr_hotkey_source_mask.attr, &dev_attr_hotkey_poll_freq.attr, #endif }; -static struct attribute *hotkey_mask_attributes[] __initdata = { - &dev_attr_hotkey_bios_mask.attr, -#ifndef CONFIG_THINKPAD_ACPI_HOTKEY_POLL - &dev_attr_hotkey_mask.attr, - &dev_attr_hotkey_all_mask.attr, - &dev_attr_hotkey_recommended_mask.attr, -#endif - &dev_attr_hotkey_wakeup_reason.attr, - &dev_attr_hotkey_wakeup_hotunplug_complete.attr, -}; - /* * Sync both the hw and sw blocking state of all switches */ @@ -2603,7 +3009,9 @@ static void tpacpi_send_radiosw_update(void) static void hotkey_exit(void) { #ifdef CONFIG_THINKPAD_ACPI_HOTKEY_POLL + mutex_lock(&hotkey_mutex); hotkey_poll_stop_sync(); + mutex_unlock(&hotkey_mutex); #endif if (hotkey_dev_attributes) @@ -2611,18 +3019,56 @@ static void hotkey_exit(void) kfree(hotkey_keycode_map); - if (tp_features.hotkey) { - dbg_printk(TPACPI_DBG_EXIT | TPACPI_DBG_HKEY, - "restoring original hot key mask\n"); - /* no short-circuit boolean operator below! */ - if ((hotkey_mask_set(hotkey_orig_mask) | - hotkey_status_set(false)) != 0) - printk(TPACPI_ERR - "failed to restore hot key mask " - "to BIOS defaults\n"); + dbg_printk(TPACPI_DBG_EXIT | TPACPI_DBG_HKEY, + "restoring original HKEY status and mask\n"); + /* yes, there is a bitwise or below, we want the + * functions to be called even if one of them fail */ + if (((tp_features.hotkey_mask && + hotkey_mask_set(hotkey_orig_mask)) | + hotkey_status_set(false)) != 0) + printk(TPACPI_ERR + "failed to restore hot key mask " + "to BIOS defaults\n"); +} + +static void __init hotkey_unmap(const unsigned int scancode) +{ + if (hotkey_keycode_map[scancode] != KEY_RESERVED) { + clear_bit(hotkey_keycode_map[scancode], + tpacpi_inputdev->keybit); + hotkey_keycode_map[scancode] = KEY_RESERVED; } } +/* + * HKEY quirks: + * TPACPI_HK_Q_INIMASK: Supports FN+F3,FN+F4,FN+F12 + */ + +#define TPACPI_HK_Q_INIMASK 0x0001 + +static const struct tpacpi_quirk tpacpi_hotkey_qtable[] __initconst = { + TPACPI_Q_IBM('I', 'H', TPACPI_HK_Q_INIMASK), /* 600E */ + TPACPI_Q_IBM('I', 'N', TPACPI_HK_Q_INIMASK), /* 600E */ + TPACPI_Q_IBM('I', 'D', TPACPI_HK_Q_INIMASK), /* 770, 770E, 770ED */ + TPACPI_Q_IBM('I', 'W', TPACPI_HK_Q_INIMASK), /* A20m */ + TPACPI_Q_IBM('I', 'V', TPACPI_HK_Q_INIMASK), /* A20p */ + TPACPI_Q_IBM('1', '0', TPACPI_HK_Q_INIMASK), /* A21e, A22e */ + TPACPI_Q_IBM('K', 'U', TPACPI_HK_Q_INIMASK), /* A21e */ + TPACPI_Q_IBM('K', 'X', TPACPI_HK_Q_INIMASK), /* A21m, A22m */ + TPACPI_Q_IBM('K', 'Y', TPACPI_HK_Q_INIMASK), /* A21p, A22p */ + TPACPI_Q_IBM('1', 'B', TPACPI_HK_Q_INIMASK), /* A22e */ + TPACPI_Q_IBM('1', '3', TPACPI_HK_Q_INIMASK), /* A22m */ + TPACPI_Q_IBM('1', 'E', TPACPI_HK_Q_INIMASK), /* A30/p (0) */ + TPACPI_Q_IBM('1', 'C', TPACPI_HK_Q_INIMASK), /* R30 */ + TPACPI_Q_IBM('1', 'F', TPACPI_HK_Q_INIMASK), /* R31 */ + TPACPI_Q_IBM('I', 'Y', TPACPI_HK_Q_INIMASK), /* T20 */ + TPACPI_Q_IBM('K', 'Z', TPACPI_HK_Q_INIMASK), /* T21 */ + TPACPI_Q_IBM('1', '6', TPACPI_HK_Q_INIMASK), /* T22 */ + TPACPI_Q_IBM('I', 'Z', TPACPI_HK_Q_INIMASK), /* X20, X21 */ + TPACPI_Q_IBM('1', 'D', TPACPI_HK_Q_INIMASK), /* X22, X23, X24 */ +}; + static int __init hotkey_init(struct ibm_init_struct *iibm) { /* Requirements for changing the default keymaps: @@ -2665,9 +3111,7 @@ static int __init hotkey_init(struct ibm_init_struct *iibm) KEY_UNKNOWN, /* 0x0D: FN+INSERT */ KEY_UNKNOWN, /* 0x0E: FN+DELETE */ - /* brightness: firmware always reacts to them, unless - * X.org did some tricks in the radeon BIOS scratch - * registers of *some* models */ + /* brightness: firmware always reacts to them */ KEY_RESERVED, /* 0x0F: FN+HOME (brightness up) */ KEY_RESERVED, /* 0x10: FN+END (brightness down) */ @@ -2701,11 +3145,11 @@ static int __init hotkey_init(struct ibm_init_struct *iibm) KEY_UNKNOWN, /* 0x0D: FN+INSERT */ KEY_UNKNOWN, /* 0x0E: FN+DELETE */ - /* These either have to go through ACPI video, or - * act like in the IBM ThinkPads, so don't ever - * enable them by default */ - KEY_RESERVED, /* 0x0F: FN+HOME (brightness up) */ - KEY_RESERVED, /* 0x10: FN+END (brightness down) */ + /* These should be enabled --only-- when ACPI video + * is disabled (i.e. in "vendor" mode), and are handled + * in a special way by the init code */ + KEY_BRIGHTNESSUP, /* 0x0F: FN+HOME (brightness up) */ + KEY_BRIGHTNESSDOWN, /* 0x10: FN+END (brightness down) */ KEY_RESERVED, /* 0x11: FN+PGUP (thinklight toggle) */ @@ -2742,6 +3186,8 @@ static int __init hotkey_init(struct ibm_init_struct *iibm) int status; int hkeyv; + unsigned long quirks; + vdbg_printk(TPACPI_DBG_INIT | TPACPI_DBG_HKEY, "initializing hotkey subdriver\n"); @@ -2767,9 +3213,16 @@ static int __init hotkey_init(struct ibm_init_struct *iibm) if (!tp_features.hotkey) return 1; + quirks = tpacpi_check_quirks(tpacpi_hotkey_qtable, + ARRAY_SIZE(tpacpi_hotkey_qtable)); + tpacpi_disable_brightness_delay(); - hotkey_dev_attributes = create_attr_set(13, NULL); + /* MUST have enough space for all attributes to be added to + * hotkey_dev_attributes */ + hotkey_dev_attributes = create_attr_set( + ARRAY_SIZE(hotkey_attributes) + 2, + NULL); if (!hotkey_dev_attributes) return -ENOMEM; res = add_many_to_attr_set(hotkey_dev_attributes, @@ -2778,7 +3231,7 @@ static int __init hotkey_init(struct ibm_init_struct *iibm) if (res) goto err_exit; - /* mask not supported on 570, 600e/x, 770e, 770x, A21e, A2xm/p, + /* mask not supported on 600e/x, 770e, 770x, A21e, A2xm/p, A30, R30, R31, T20-22, X20-21, X22-24. Detected by checking for HKEY interface version 0x100 */ if (acpi_evalf(hkey_handle, &hkeyv, "MHKV", "qd")) { @@ -2792,10 +3245,22 @@ static int __init hotkey_init(struct ibm_init_struct *iibm) * MHKV 0x100 in A31, R40, R40e, * T4x, X31, and later */ - tp_features.hotkey_mask = 1; vdbg_printk(TPACPI_DBG_INIT | TPACPI_DBG_HKEY, "firmware HKEY interface version: 0x%x\n", hkeyv); + + /* Paranoia check AND init hotkey_all_mask */ + if (!acpi_evalf(hkey_handle, &hotkey_all_mask, + "MHKA", "qd")) { + printk(TPACPI_ERR + "missing MHKA handler, " + "please report this to %s\n", + TPACPI_MAIL); + /* Fallback: pre-init for FN+F3,F4,F12 */ + hotkey_all_mask = 0x080cU; + } else { + tp_features.hotkey_mask = 1; + } } } @@ -2803,47 +3268,25 @@ static int __init hotkey_init(struct ibm_init_struct *iibm) "hotkey masks are %s\n", str_supported(tp_features.hotkey_mask)); - if (tp_features.hotkey_mask) { - if (!acpi_evalf(hkey_handle, &hotkey_all_mask, - "MHKA", "qd")) { - printk(TPACPI_ERR - "missing MHKA handler, " - "please report this to %s\n", - TPACPI_MAIL); - /* FN+F12, FN+F4, FN+F3 */ - hotkey_all_mask = 0x080cU; - } - } + /* Init hotkey_all_mask if not initialized yet */ + if (!tp_features.hotkey_mask && !hotkey_all_mask && + (quirks & TPACPI_HK_Q_INIMASK)) + hotkey_all_mask = 0x080cU; /* FN+F12, FN+F4, FN+F3 */ - /* hotkey_source_mask *must* be zero for - * the first hotkey_mask_get */ + /* Init hotkey_acpi_mask and hotkey_orig_mask */ if (tp_features.hotkey_mask) { + /* hotkey_source_mask *must* be zero for + * the first hotkey_mask_get to return hotkey_orig_mask */ res = hotkey_mask_get(); if (res) goto err_exit; - hotkey_orig_mask = hotkey_mask; - res = add_many_to_attr_set( - hotkey_dev_attributes, - hotkey_mask_attributes, - ARRAY_SIZE(hotkey_mask_attributes)); - if (res) - goto err_exit; - } - -#ifdef CONFIG_THINKPAD_ACPI_HOTKEY_POLL - if (tp_features.hotkey_mask) { - hotkey_source_mask = TPACPI_HKEY_NVRAM_GOOD_MASK - & ~hotkey_all_mask; + hotkey_orig_mask = hotkey_acpi_mask; } else { - hotkey_source_mask = TPACPI_HKEY_NVRAM_GOOD_MASK; + hotkey_orig_mask = hotkey_all_mask; + hotkey_acpi_mask = hotkey_all_mask; } - vdbg_printk(TPACPI_DBG_INIT | TPACPI_DBG_HKEY, - "hotkey source mask 0x%08x, polling freq %d\n", - hotkey_source_mask, hotkey_poll_freq); -#endif - #ifdef CONFIG_THINKPAD_ACPI_DEBUGFACILITIES if (dbg_wlswemul) { tp_features.hotkey_wlsw = 1; @@ -2944,17 +3387,26 @@ static int __init hotkey_init(struct ibm_init_struct *iibm) "Disabling thinkpad-acpi brightness events " "by default...\n"); - /* The hotkey_reserved_mask change below is not - * necessary while the keys are at KEY_RESERVED in the - * default map, but better safe than sorry, leave it - * here as a marker of what we have to do, especially - * when we finally become able to set this at runtime - * on response to X.org requests */ + /* Disable brightness up/down on Lenovo thinkpads when + * ACPI is handling them, otherwise it is plain impossible + * for userspace to do something even remotely sane */ hotkey_reserved_mask |= (1 << TP_ACPI_HOTKEYSCAN_FNHOME) | (1 << TP_ACPI_HOTKEYSCAN_FNEND); + hotkey_unmap(TP_ACPI_HOTKEYSCAN_FNHOME); + hotkey_unmap(TP_ACPI_HOTKEYSCAN_FNEND); } +#ifdef CONFIG_THINKPAD_ACPI_HOTKEY_POLL + hotkey_source_mask = TPACPI_HKEY_NVRAM_GOOD_MASK + & ~hotkey_all_mask + & ~hotkey_reserved_mask; + + vdbg_printk(TPACPI_DBG_INIT | TPACPI_DBG_HKEY, + "hotkey source mask 0x%08x, polling freq %u\n", + hotkey_source_mask, hotkey_poll_freq); +#endif + dbg_printk(TPACPI_DBG_INIT | TPACPI_DBG_HKEY, "enabling firmware HKEY event interface...\n"); res = hotkey_status_set(true); @@ -2962,13 +3414,18 @@ static int __init hotkey_init(struct ibm_init_struct *iibm) hotkey_exit(); return res; } - res = hotkey_mask_set(((hotkey_all_mask | hotkey_source_mask) - & ~hotkey_reserved_mask) - | hotkey_orig_mask); + res = hotkey_mask_set(((hotkey_all_mask & ~hotkey_reserved_mask) + | hotkey_driver_mask) + & ~hotkey_source_mask); if (res < 0 && res != -ENXIO) { hotkey_exit(); return res; } + hotkey_user_mask = (hotkey_acpi_mask | hotkey_source_mask) + & ~hotkey_reserved_mask; + vdbg_printk(TPACPI_DBG_INIT | TPACPI_DBG_HKEY, + "initial masks: user=0x%08x, fw=0x%08x, poll=0x%08x\n", + hotkey_user_mask, hotkey_acpi_mask, hotkey_source_mask); dbg_printk(TPACPI_DBG_INIT | TPACPI_DBG_HKEY, "legacy ibm/hotkey event reporting over procfs %s\n", @@ -2978,7 +3435,7 @@ static int __init hotkey_init(struct ibm_init_struct *iibm) tpacpi_inputdev->open = &hotkey_inputdev_open; tpacpi_inputdev->close = &hotkey_inputdev_close; - hotkey_poll_setup_safe(1); + hotkey_poll_setup_safe(true); tpacpi_send_radiosw_update(); tpacpi_input_send_tabletsw(); @@ -3003,7 +3460,7 @@ static bool hotkey_notify_hotkey(const u32 hkey, if (scancode > 0 && scancode < 0x21) { scancode--; if (!(hotkey_source_mask & (1 << scancode))) { - tpacpi_input_send_key(scancode); + tpacpi_input_send_key_masked(scancode); *send_acpi_ev = false; } else { *ignore_acpi_ev = true; @@ -3022,20 +3479,20 @@ static bool hotkey_notify_wakeup(const u32 hkey, *ignore_acpi_ev = false; switch (hkey) { - case 0x2304: /* suspend, undock */ - case 0x2404: /* hibernation, undock */ + case TP_HKEY_EV_WKUP_S3_UNDOCK: /* suspend, undock */ + case TP_HKEY_EV_WKUP_S4_UNDOCK: /* hibernation, undock */ hotkey_wakeup_reason = TP_ACPI_WAKEUP_UNDOCK; *ignore_acpi_ev = true; break; - case 0x2305: /* suspend, bay eject */ - case 0x2405: /* hibernation, bay eject */ + case TP_HKEY_EV_WKUP_S3_BAYEJ: /* suspend, bay eject */ + case TP_HKEY_EV_WKUP_S4_BAYEJ: /* hibernation, bay eject */ hotkey_wakeup_reason = TP_ACPI_WAKEUP_BAYEJ; *ignore_acpi_ev = true; break; - case 0x2313: /* Battery on critical low level (S3) */ - case 0x2413: /* Battery on critical low level (S4) */ + case TP_HKEY_EV_WKUP_S3_BATLOW: /* Battery on critical low level/S3 */ + case TP_HKEY_EV_WKUP_S4_BATLOW: /* Battery on critical low level/S4 */ printk(TPACPI_ALERT "EMERGENCY WAKEUP: battery almost empty\n"); /* how to auto-heal: */ @@ -3065,21 +3522,21 @@ static bool hotkey_notify_usrevent(const u32 hkey, *ignore_acpi_ev = false; switch (hkey) { - case 0x5010: /* Lenovo new BIOS: brightness changed */ - case 0x500b: /* X61t: tablet pen inserted into bay */ - case 0x500c: /* X61t: tablet pen removed from bay */ + case TP_HKEY_EV_PEN_INSERTED: /* X61t: tablet pen inserted into bay */ + case TP_HKEY_EV_PEN_REMOVED: /* X61t: tablet pen removed from bay */ return true; - case 0x5009: /* X41t-X61t: swivel up (tablet mode) */ - case 0x500a: /* X41t-X61t: swivel down (normal mode) */ + case TP_HKEY_EV_TABLET_TABLET: /* X41t-X61t: tablet mode */ + case TP_HKEY_EV_TABLET_NOTEBOOK: /* X41t-X61t: normal mode */ tpacpi_input_send_tabletsw(); hotkey_tablet_mode_notify_change(); *send_acpi_ev = false; return true; - case 0x5001: - case 0x5002: - /* LID switch events. Do not propagate */ + case TP_HKEY_EV_LID_CLOSE: /* Lid closed */ + case TP_HKEY_EV_LID_OPEN: /* Lid opened */ + case TP_HKEY_EV_BRGHT_CHANGED: /* brightness changed */ + /* do not propagate these events */ *ignore_acpi_ev = true; return true; @@ -3097,30 +3554,30 @@ static bool hotkey_notify_thermal(const u32 hkey, *ignore_acpi_ev = false; switch (hkey) { - case 0x6011: + case TP_HKEY_EV_ALARM_BAT_HOT: printk(TPACPI_CRIT "THERMAL ALARM: battery is too hot!\n"); /* recommended action: warn user through gui */ return true; - case 0x6012: + case TP_HKEY_EV_ALARM_BAT_XHOT: printk(TPACPI_ALERT "THERMAL EMERGENCY: battery is extremely hot!\n"); /* recommended action: immediate sleep/hibernate */ return true; - case 0x6021: + case TP_HKEY_EV_ALARM_SENSOR_HOT: printk(TPACPI_CRIT "THERMAL ALARM: " "a sensor reports something is too hot!\n"); /* recommended action: warn user through gui, that */ /* some internal component is too hot */ return true; - case 0x6022: + case TP_HKEY_EV_ALARM_SENSOR_XHOT: printk(TPACPI_ALERT "THERMAL EMERGENCY: " "a sensor reports something is extremely hot!\n"); /* recommended action: immediate sleep/hibernate */ return true; - case 0x6030: + case TP_HKEY_EV_THM_TABLE_CHANGED: printk(TPACPI_INFO "EC reports that Thermal Table has changed\n"); /* recommended action: do nothing, we don't have @@ -3178,7 +3635,7 @@ static void hotkey_notify(struct ibm_struct *ibm, u32 event) break; case 3: /* 0x3000-0x3FFF: bay-related wakeups */ - if (hkey == 0x3003) { + if (hkey == TP_HKEY_EV_BAYEJ_ACK) { hotkey_autosleep_ack = 1; printk(TPACPI_INFO "bay ejected\n"); @@ -3190,7 +3647,7 @@ static void hotkey_notify(struct ibm_struct *ibm, u32 event) break; case 4: /* 0x4000-0x4FFF: dock-related wakeups */ - if (hkey == 0x4003) { + if (hkey == TP_HKEY_EV_UNDOCK_ACK) { hotkey_autosleep_ack = 1; printk(TPACPI_INFO "undocked\n"); @@ -3212,7 +3669,8 @@ static void hotkey_notify(struct ibm_struct *ibm, u32 event) break; case 7: /* 0x7000-0x7FFF: misc */ - if (tp_features.hotkey_wlsw && hkey == 0x7000) { + if (tp_features.hotkey_wlsw && + hkey == TP_HKEY_EV_RFKILL_CHANGED) { tpacpi_send_radiosw_update(); send_acpi_ev = 0; known_ev = true; @@ -3258,15 +3716,17 @@ static void hotkey_resume(void) { tpacpi_disable_brightness_delay(); - if (hotkey_mask_get()) + if (hotkey_status_set(true) < 0 || + hotkey_mask_set(hotkey_acpi_mask) < 0) printk(TPACPI_ERR - "error while trying to read hot key mask " - "from firmware\n"); + "error while attempting to reset the event " + "firmware interface\n"); + tpacpi_send_radiosw_update(); hotkey_tablet_mode_notify_change(); hotkey_wakeup_reason_notify_change(); hotkey_wakeup_hotunplug_complete_notify_change(); - hotkey_poll_setup_safe(0); + hotkey_poll_setup_safe(false); } /* procfs -------------------------------------------------------------- */ @@ -3290,8 +3750,8 @@ static int hotkey_read(char *p) return res; len += sprintf(p + len, "status:\t\t%s\n", enabled(status, 0)); - if (tp_features.hotkey_mask) { - len += sprintf(p + len, "mask:\t\t0x%08x\n", hotkey_mask); + if (hotkey_all_mask) { + len += sprintf(p + len, "mask:\t\t0x%08x\n", hotkey_user_mask); len += sprintf(p + len, "commands:\tenable, disable, reset, <mask>\n"); } else { @@ -3328,7 +3788,7 @@ static int hotkey_write(char *buf) if (mutex_lock_killable(&hotkey_mutex)) return -ERESTARTSYS; - mask = hotkey_mask; + mask = hotkey_user_mask; res = 0; while ((cmd = next_cmd(&buf))) { @@ -3338,7 +3798,8 @@ static int hotkey_write(char *buf) hotkey_enabledisable_warn(0); res = -EPERM; } else if (strlencmp(cmd, "reset") == 0) { - mask = hotkey_orig_mask; + mask = (hotkey_all_mask | hotkey_source_mask) + & ~hotkey_reserved_mask; } else if (sscanf(cmd, "0x%x", &mask) == 1) { /* mask set */ } else if (sscanf(cmd, "%x", &mask) == 1) { @@ -3349,12 +3810,11 @@ static int hotkey_write(char *buf) } } - if (!res) + if (!res) { tpacpi_disclose_usertask("procfs hotkey", "set mask to 0x%08x\n", mask); - - if (!res && mask != hotkey_mask) - res = hotkey_mask_set(mask); + res = hotkey_user_mask_set(mask); + } errexit: mutex_unlock(&hotkey_mutex); @@ -5655,16 +6115,16 @@ static const struct tpacpi_quirk brightness_quirk_table[] __initconst = { /* Models with ATI GPUs known to require ECNVRAM mode */ TPACPI_Q_IBM('1', 'Y', TPACPI_BRGHT_Q_EC), /* T43/p ATI */ - /* Models with ATI GPUs (waiting confirmation) */ - TPACPI_Q_IBM('1', 'R', TPACPI_BRGHT_Q_ASK|TPACPI_BRGHT_Q_EC), + /* Models with ATI GPUs that can use ECNVRAM */ + TPACPI_Q_IBM('1', 'R', TPACPI_BRGHT_Q_EC), TPACPI_Q_IBM('1', 'Q', TPACPI_BRGHT_Q_ASK|TPACPI_BRGHT_Q_EC), TPACPI_Q_IBM('7', '6', TPACPI_BRGHT_Q_ASK|TPACPI_BRGHT_Q_EC), TPACPI_Q_IBM('7', '8', TPACPI_BRGHT_Q_ASK|TPACPI_BRGHT_Q_EC), - /* Models with Intel Extreme Graphics 2 (waiting confirmation) */ + /* Models with Intel Extreme Graphics 2 */ + TPACPI_Q_IBM('1', 'U', TPACPI_BRGHT_Q_NOEC), TPACPI_Q_IBM('1', 'V', TPACPI_BRGHT_Q_ASK|TPACPI_BRGHT_Q_NOEC), TPACPI_Q_IBM('1', 'W', TPACPI_BRGHT_Q_ASK|TPACPI_BRGHT_Q_NOEC), - TPACPI_Q_IBM('1', 'U', TPACPI_BRGHT_Q_ASK|TPACPI_BRGHT_Q_NOEC), /* Models with Intel GMA900 */ TPACPI_Q_IBM('7', '0', TPACPI_BRGHT_Q_NOEC), /* T43, R52 */ @@ -5767,8 +6227,10 @@ static int __init brightness_init(struct ibm_init_struct *iibm) TPACPI_BACKLIGHT_DEV_NAME, NULL, NULL, &ibm_backlight_data); if (IS_ERR(ibm_backlight_device)) { + int rc = PTR_ERR(ibm_backlight_device); + ibm_backlight_device = NULL; printk(TPACPI_ERR "Could not register backlight device\n"); - return PTR_ERR(ibm_backlight_device); + return rc; } vdbg_printk(TPACPI_DBG_INIT | TPACPI_DBG_BRGHT, "brightness is supported\n"); @@ -5863,7 +6325,7 @@ static int brightness_write(char *buf) * Doing it this way makes the syscall restartable in case of EINTR */ rc = brightness_set(level); - return (rc == -EINTR)? ERESTARTSYS : rc; + return (rc == -EINTR)? -ERESTARTSYS : rc; } static struct ibm_struct brightness_driver_data = { @@ -7256,6 +7718,21 @@ static struct ibm_struct fan_driver_data = { **************************************************************************** ****************************************************************************/ +/* + * HKEY event callout for other subdrivers go here + * (yes, it is ugly, but it is quick, safe, and gets the job done + */ +static void tpacpi_driver_event(const unsigned int hkey_event) +{ +} + + + +static void hotkey_driver_event(const unsigned int scancode) +{ + tpacpi_driver_event(TP_HKEY_EV_HOTKEY_BASE + scancode); +} + /* sysfs name ---------------------------------------------------------- */ static ssize_t thinkpad_acpi_pdev_name_show(struct device *dev, struct device_attribute *attr, @@ -7524,9 +8001,11 @@ static int __init probe_for_thinkpad(void) /* * Non-ancient models have better DMI tagging, but very old models - * don't. + * don't. tpacpi_is_fw_known() is a cheat to help in that case. */ - is_thinkpad = (thinkpad_id.model_str != NULL); + is_thinkpad = (thinkpad_id.model_str != NULL) || + (thinkpad_id.ec_model != 0) || + tpacpi_is_fw_known(); /* ec is required because many other handles are relative to it */ TPACPI_ACPIHANDLE_INIT(ec); @@ -7537,13 +8016,6 @@ static int __init probe_for_thinkpad(void) return -ENODEV; } - /* - * Risks a regression on very old machines, but reduces potential - * false positives a damn great deal - */ - if (!is_thinkpad) - is_thinkpad = (thinkpad_id.vendor == PCI_VENDOR_ID_IBM); - if (!is_thinkpad && !force_load) return -ENODEV; diff --git a/drivers/platform/x86/topstar-laptop.c b/drivers/platform/x86/topstar-laptop.c new file mode 100644 index 000000000000..02f3d4e9e666 --- /dev/null +++ b/drivers/platform/x86/topstar-laptop.c @@ -0,0 +1,265 @@ +/* + * ACPI driver for Topstar notebooks (hotkeys support only) + * + * Copyright (c) 2009 Herton Ronaldo Krzesinski <herton@mandriva.com.br> + * + * Implementation inspired by existing x86 platform drivers, in special + * asus/eepc/fujitsu-laptop, thanks to their authors + * + * 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. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/acpi.h> +#include <linux/input.h> + +#define ACPI_TOPSTAR_CLASS "topstar" + +struct topstar_hkey { + struct input_dev *inputdev; +}; + +struct tps_key_entry { + u8 code; + u16 keycode; +}; + +static struct tps_key_entry topstar_keymap[] = { + { 0x80, KEY_BRIGHTNESSUP }, + { 0x81, KEY_BRIGHTNESSDOWN }, + { 0x83, KEY_VOLUMEUP }, + { 0x84, KEY_VOLUMEDOWN }, + { 0x85, KEY_MUTE }, + { 0x86, KEY_SWITCHVIDEOMODE }, + { 0x87, KEY_F13 }, /* touchpad enable/disable key */ + { 0x88, KEY_WLAN }, + { 0x8a, KEY_WWW }, + { 0x8b, KEY_MAIL }, + { 0x8c, KEY_MEDIA }, + { 0x96, KEY_F14 }, /* G key? */ + { } +}; + +static struct tps_key_entry *tps_get_key_by_scancode(int code) +{ + struct tps_key_entry *key; + + for (key = topstar_keymap; key->code; key++) + if (code == key->code) + return key; + + return NULL; +} + +static struct tps_key_entry *tps_get_key_by_keycode(int code) +{ + struct tps_key_entry *key; + + for (key = topstar_keymap; key->code; key++) + if (code == key->keycode) + return key; + + return NULL; +} + +static void acpi_topstar_notify(struct acpi_device *device, u32 event) +{ + struct tps_key_entry *key; + static bool dup_evnt[2]; + bool *dup; + struct topstar_hkey *hkey = acpi_driver_data(device); + + /* 0x83 and 0x84 key events comes duplicated... */ + if (event == 0x83 || event == 0x84) { + dup = &dup_evnt[event - 0x83]; + if (*dup) { + *dup = false; + return; + } + *dup = true; + } + + /* + * 'G key' generate two event codes, convert to only + * one event/key code for now (3G switch?) + */ + if (event == 0x97) + event = 0x96; + + key = tps_get_key_by_scancode(event); + if (key) { + input_report_key(hkey->inputdev, key->keycode, 1); + input_sync(hkey->inputdev); + input_report_key(hkey->inputdev, key->keycode, 0); + input_sync(hkey->inputdev); + return; + } + + /* Known non hotkey events don't handled or that we don't care yet */ + if (event == 0x8e || event == 0x8f || event == 0x90) + return; + + pr_info("unknown event = 0x%02x\n", event); +} + +static int acpi_topstar_fncx_switch(struct acpi_device *device, bool state) +{ + acpi_status status; + union acpi_object fncx_params[1] = { + { .type = ACPI_TYPE_INTEGER } + }; + struct acpi_object_list fncx_arg_list = { 1, &fncx_params[0] }; + + fncx_params[0].integer.value = state ? 0x86 : 0x87; + status = acpi_evaluate_object(device->handle, "FNCX", &fncx_arg_list, NULL); + if (ACPI_FAILURE(status)) { + pr_err("Unable to switch FNCX notifications\n"); + return -ENODEV; + } + + return 0; +} + +static int topstar_getkeycode(struct input_dev *dev, int scancode, int *keycode) +{ + struct tps_key_entry *key = tps_get_key_by_scancode(scancode); + + if (!key) + return -EINVAL; + + *keycode = key->keycode; + return 0; +} + +static int topstar_setkeycode(struct input_dev *dev, int scancode, int keycode) +{ + struct tps_key_entry *key; + int old_keycode; + + if (keycode < 0 || keycode > KEY_MAX) + return -EINVAL; + + key = tps_get_key_by_scancode(scancode); + + if (!key) + return -EINVAL; + + old_keycode = key->keycode; + key->keycode = keycode; + set_bit(keycode, dev->keybit); + if (!tps_get_key_by_keycode(old_keycode)) + clear_bit(old_keycode, dev->keybit); + return 0; +} + +static int acpi_topstar_init_hkey(struct topstar_hkey *hkey) +{ + struct tps_key_entry *key; + + hkey->inputdev = input_allocate_device(); + if (!hkey->inputdev) { + pr_err("Unable to allocate input device\n"); + return -ENODEV; + } + hkey->inputdev->name = "Topstar Laptop extra buttons"; + hkey->inputdev->phys = "topstar/input0"; + hkey->inputdev->id.bustype = BUS_HOST; + hkey->inputdev->getkeycode = topstar_getkeycode; + hkey->inputdev->setkeycode = topstar_setkeycode; + for (key = topstar_keymap; key->code; key++) { + set_bit(EV_KEY, hkey->inputdev->evbit); + set_bit(key->keycode, hkey->inputdev->keybit); + } + if (input_register_device(hkey->inputdev)) { + pr_err("Unable to register input device\n"); + input_free_device(hkey->inputdev); + return -ENODEV; + } + + return 0; +} + +static int acpi_topstar_add(struct acpi_device *device) +{ + struct topstar_hkey *tps_hkey; + + tps_hkey = kzalloc(sizeof(struct topstar_hkey), GFP_KERNEL); + if (!tps_hkey) + return -ENOMEM; + + strcpy(acpi_device_name(device), "Topstar TPSACPI"); + strcpy(acpi_device_class(device), ACPI_TOPSTAR_CLASS); + + if (acpi_topstar_fncx_switch(device, true)) + goto add_err; + + if (acpi_topstar_init_hkey(tps_hkey)) + goto add_err; + + device->driver_data = tps_hkey; + return 0; + +add_err: + kfree(tps_hkey); + return -ENODEV; +} + +static int acpi_topstar_remove(struct acpi_device *device, int type) +{ + struct topstar_hkey *tps_hkey = acpi_driver_data(device); + + acpi_topstar_fncx_switch(device, false); + + input_unregister_device(tps_hkey->inputdev); + kfree(tps_hkey); + + return 0; +} + +static const struct acpi_device_id topstar_device_ids[] = { + { "TPSACPI01", 0 }, + { "", 0 }, +}; +MODULE_DEVICE_TABLE(acpi, topstar_device_ids); + +static struct acpi_driver acpi_topstar_driver = { + .name = "Topstar laptop ACPI driver", + .class = ACPI_TOPSTAR_CLASS, + .ids = topstar_device_ids, + .ops = { + .add = acpi_topstar_add, + .remove = acpi_topstar_remove, + .notify = acpi_topstar_notify, + }, +}; + +static int __init topstar_laptop_init(void) +{ + int ret; + + ret = acpi_bus_register_driver(&acpi_topstar_driver); + if (ret < 0) + return ret; + + printk(KERN_INFO "Topstar Laptop ACPI extras driver loaded\n"); + + return 0; +} + +static void __exit topstar_laptop_exit(void) +{ + acpi_bus_unregister_driver(&acpi_topstar_driver); +} + +module_init(topstar_laptop_init); +module_exit(topstar_laptop_exit); + +MODULE_AUTHOR("Herton Ronaldo Krzesinski"); +MODULE_DESCRIPTION("Topstar Laptop ACPI Extras driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/platform/x86/wmi.c b/drivers/platform/x86/wmi.c index f215a5919192..177f8d767df4 100644 --- a/drivers/platform/x86/wmi.c +++ b/drivers/platform/x86/wmi.c @@ -42,7 +42,6 @@ MODULE_LICENSE("GPL"); #define ACPI_WMI_CLASS "wmi" -#undef PREFIX #define PREFIX "ACPI: WMI: " static DEFINE_MUTEX(wmi_data_lock); diff --git a/drivers/pnp/pnpacpi/core.c b/drivers/pnp/pnpacpi/core.c index 9496494f340e..83b8b5ac49c9 100644 --- a/drivers/pnp/pnpacpi/core.c +++ b/drivers/pnp/pnpacpi/core.c @@ -153,6 +153,7 @@ static int __init pnpacpi_add_device(struct acpi_device *device) acpi_handle temp = NULL; acpi_status status; struct pnp_dev *dev; + struct acpi_hardware_id *id; /* * If a PnPacpi device is not present , the device @@ -193,15 +194,12 @@ static int __init pnpacpi_add_device(struct acpi_device *device) if (dev->capabilities & PNP_CONFIGURABLE) pnpacpi_parse_resource_option_data(dev); - if (device->flags.compatible_ids) { - struct acpi_compatible_id_list *cid_list = device->pnp.cid_list; - int i; - - for (i = 0; i < cid_list->count; i++) { - if (!ispnpidacpi(cid_list->id[i].value)) - continue; - pnp_add_id(dev, cid_list->id[i].value); - } + list_for_each_entry(id, &device->pnp.ids, list) { + if (!strcmp(id->id, acpi_device_hid(device))) + continue; + if (!ispnpidacpi(id->id)) + continue; + pnp_add_id(dev, id->id); } /* clear out the damaged flags */ @@ -232,9 +230,8 @@ static int __init acpi_pnp_match(struct device *dev, void *_pnp) struct pnp_dev *pnp = _pnp; /* true means it matched */ - return acpi->flags.hardware_id - && !acpi_get_physical_device(acpi->handle) - && compare_pnp_id(pnp->id, acpi->pnp.hardware_id); + return !acpi_get_physical_device(acpi->handle) + && compare_pnp_id(pnp->id, acpi_device_hid(acpi)); } static int __init acpi_pnp_find_device(struct device *dev, acpi_handle * handle) diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig index bdbc4f73fcdc..cea6cef27e89 100644 --- a/drivers/power/Kconfig +++ b/drivers/power/Kconfig @@ -29,6 +29,13 @@ config APM_POWER Say Y here to enable support APM status emulation using battery class devices. +config WM831X_POWER + tristate "WM831X PMU support" + depends on MFD_WM831X + help + Say Y here to enable support for the power management unit + provided by Wolfson Microelectronics WM831x PMICs. + config WM8350_POWER tristate "WM8350 PMU support" depends on MFD_WM8350 diff --git a/drivers/power/Makefile b/drivers/power/Makefile index 380d17c9ae29..b96f29d91c28 100644 --- a/drivers/power/Makefile +++ b/drivers/power/Makefile @@ -16,6 +16,7 @@ obj-$(CONFIG_POWER_SUPPLY) += power_supply.o obj-$(CONFIG_PDA_POWER) += pda_power.o obj-$(CONFIG_APM_POWER) += apm_power.o +obj-$(CONFIG_WM831X_POWER) += wm831x_power.o obj-$(CONFIG_WM8350_POWER) += wm8350_power.o obj-$(CONFIG_BATTERY_DS2760) += ds2760_battery.o diff --git a/drivers/power/ds2760_battery.c b/drivers/power/ds2760_battery.c index 520b5c49ff30..6f1dba5a519d 100644 --- a/drivers/power/ds2760_battery.c +++ b/drivers/power/ds2760_battery.c @@ -56,6 +56,7 @@ struct ds2760_device_info { struct device *w1_dev; struct workqueue_struct *monitor_wqueue; struct delayed_work monitor_work; + struct delayed_work set_charged_work; }; static unsigned int cache_time = 1000; @@ -66,6 +67,14 @@ static unsigned int pmod_enabled; module_param(pmod_enabled, bool, 0644); MODULE_PARM_DESC(pmod_enabled, "PMOD enable bit"); +static unsigned int rated_capacity; +module_param(rated_capacity, uint, 0644); +MODULE_PARM_DESC(rated_capacity, "rated battery capacity, 10*mAh or index"); + +static unsigned int current_accum; +module_param(current_accum, uint, 0644); +MODULE_PARM_DESC(current_accum, "current accumulator value"); + /* Some batteries have their rated capacity stored a N * 10 mAh, while * others use an index into this table. */ static int rated_capacities[] = { @@ -168,8 +177,13 @@ static int ds2760_battery_read_status(struct ds2760_device_info *di) di->full_active_uAh = di->raw[DS2760_ACTIVE_FULL] << 8 | di->raw[DS2760_ACTIVE_FULL + 1]; - scale[0] = di->raw[DS2760_ACTIVE_FULL] << 8 | - di->raw[DS2760_ACTIVE_FULL + 1]; + /* If the full_active_uAh value is not given, fall back to the rated + * capacity. This is likely to happen when chips are not part of the + * battery pack and is therefore not bootstrapped. */ + if (di->full_active_uAh == 0) + di->full_active_uAh = di->rated_capacity / 1000L; + + scale[0] = di->full_active_uAh; for (i = 1; i < 5; i++) scale[i] = scale[i - 1] + di->raw[DS2760_ACTIVE_FULL + 2 + i]; @@ -197,15 +211,31 @@ static int ds2760_battery_read_status(struct ds2760_device_info *di) if (di->rem_capacity > 100) di->rem_capacity = 100; - if (di->current_uA) - di->life_sec = -((di->accum_current_uAh - di->empty_uAh) * - 3600L) / di->current_uA; + if (di->current_uA >= 100L) + di->life_sec = -((di->accum_current_uAh - di->empty_uAh) * 36L) + / (di->current_uA / 100L); else di->life_sec = 0; return 0; } +static void ds2760_battery_set_current_accum(struct ds2760_device_info *di, + unsigned int acr_val) +{ + unsigned char acr[2]; + + /* acr is in units of 0.25 mAh */ + acr_val *= 4L; + acr_val /= 1000; + + acr[0] = acr_val >> 8; + acr[1] = acr_val & 0xff; + + if (w1_ds2760_write(di->w1_dev, acr, DS2760_CURRENT_ACCUM_MSB, 2) < 2) + dev_warn(di->dev, "ACR write failed\n"); +} + static void ds2760_battery_update_status(struct ds2760_device_info *di) { int old_charge_status = di->charge_status; @@ -237,21 +267,9 @@ static void ds2760_battery_update_status(struct ds2760_device_info *di) if (di->full_counter < 2) { di->charge_status = POWER_SUPPLY_STATUS_CHARGING; } else { - unsigned char acr[2]; - int acr_val; - - /* acr is in units of 0.25 mAh */ - acr_val = di->full_active_uAh * 4L / 1000; - - acr[0] = acr_val >> 8; - acr[1] = acr_val & 0xff; - - if (w1_ds2760_write(di->w1_dev, acr, - DS2760_CURRENT_ACCUM_MSB, 2) < 2) - dev_warn(di->dev, - "ACR reset failed\n"); - di->charge_status = POWER_SUPPLY_STATUS_FULL; + ds2760_battery_set_current_accum(di, + di->full_active_uAh); } } } else { @@ -274,6 +292,17 @@ static void ds2760_battery_write_status(struct ds2760_device_info *di, w1_ds2760_recall_eeprom(di->w1_dev, DS2760_EEPROM_BLOCK1); } +static void ds2760_battery_write_rated_capacity(struct ds2760_device_info *di, + unsigned char rated_capacity) +{ + if (rated_capacity == di->raw[DS2760_RATED_CAPACITY]) + return; + + w1_ds2760_write(di->w1_dev, &rated_capacity, DS2760_RATED_CAPACITY, 1); + w1_ds2760_store_eeprom(di->w1_dev, DS2760_EEPROM_BLOCK1); + w1_ds2760_recall_eeprom(di->w1_dev, DS2760_EEPROM_BLOCK1); +} + static void ds2760_battery_work(struct work_struct *work) { struct ds2760_device_info *di = container_of(work, @@ -299,6 +328,52 @@ static void ds2760_battery_external_power_changed(struct power_supply *psy) queue_delayed_work(di->monitor_wqueue, &di->monitor_work, HZ/10); } + +static void ds2760_battery_set_charged_work(struct work_struct *work) +{ + char bias; + struct ds2760_device_info *di = container_of(work, + struct ds2760_device_info, set_charged_work.work); + + dev_dbg(di->dev, "%s\n", __func__); + + ds2760_battery_read_status(di); + + /* When we get notified by external circuitry that the battery is + * considered fully charged now, we know that there is no current + * flow any more. However, the ds2760's internal current meter is + * too inaccurate to rely on - spec say something ~15% failure. + * Hence, we use the current offset bias register to compensate + * that error. + */ + + if (!power_supply_am_i_supplied(&di->bat)) + return; + + bias = (signed char) di->current_raw + + (signed char) di->raw[DS2760_CURRENT_OFFSET_BIAS]; + + dev_dbg(di->dev, "%s: bias = %d\n", __func__, bias); + + w1_ds2760_write(di->w1_dev, &bias, DS2760_CURRENT_OFFSET_BIAS, 1); + w1_ds2760_store_eeprom(di->w1_dev, DS2760_EEPROM_BLOCK1); + w1_ds2760_recall_eeprom(di->w1_dev, DS2760_EEPROM_BLOCK1); + + /* Write to the di->raw[] buffer directly - the CURRENT_OFFSET_BIAS + * value won't be read back by ds2760_battery_read_status() */ + di->raw[DS2760_CURRENT_OFFSET_BIAS] = bias; +} + +static void ds2760_battery_set_charged(struct power_supply *psy) +{ + struct ds2760_device_info *di = to_ds2760_device_info(psy); + + /* postpone the actual work by 20 secs. This is for debouncing GPIO + * signals and to let the current value settle. See AN4188. */ + cancel_delayed_work(&di->set_charged_work); + queue_delayed_work(di->monitor_wqueue, &di->set_charged_work, HZ * 20); +} + static int ds2760_battery_get_property(struct power_supply *psy, enum power_supply_property psp, union power_supply_propval *val) @@ -337,6 +412,12 @@ static int ds2760_battery_get_property(struct power_supply *psy, case POWER_SUPPLY_PROP_TEMP: val->intval = di->temp_C; break; + case POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW: + val->intval = di->life_sec; + break; + case POWER_SUPPLY_PROP_CAPACITY: + val->intval = di->rem_capacity; + break; default: return -EINVAL; } @@ -353,6 +434,8 @@ static enum power_supply_property ds2760_battery_props[] = { POWER_SUPPLY_PROP_CHARGE_EMPTY, POWER_SUPPLY_PROP_CHARGE_NOW, POWER_SUPPLY_PROP_TEMP, + POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW, + POWER_SUPPLY_PROP_CAPACITY, }; static int ds2760_battery_probe(struct platform_device *pdev) @@ -376,17 +459,12 @@ static int ds2760_battery_probe(struct platform_device *pdev) di->bat.properties = ds2760_battery_props; di->bat.num_properties = ARRAY_SIZE(ds2760_battery_props); di->bat.get_property = ds2760_battery_get_property; + di->bat.set_charged = ds2760_battery_set_charged; di->bat.external_power_changed = ds2760_battery_external_power_changed; di->charge_status = POWER_SUPPLY_STATUS_UNKNOWN; - retval = power_supply_register(&pdev->dev, &di->bat); - if (retval) { - dev_err(di->dev, "failed to register battery\n"); - goto batt_failed; - } - /* enable sleep mode feature */ ds2760_battery_read_status(di); status = di->raw[DS2760_STATUS_REG]; @@ -397,7 +475,24 @@ static int ds2760_battery_probe(struct platform_device *pdev) ds2760_battery_write_status(di, status); + /* set rated capacity from module param */ + if (rated_capacity) + ds2760_battery_write_rated_capacity(di, rated_capacity); + + /* set current accumulator if given as parameter. + * this should only be done for bootstrapping the value */ + if (current_accum) + ds2760_battery_set_current_accum(di, current_accum); + + retval = power_supply_register(&pdev->dev, &di->bat); + if (retval) { + dev_err(di->dev, "failed to register battery\n"); + goto batt_failed; + } + INIT_DELAYED_WORK(&di->monitor_work, ds2760_battery_work); + INIT_DELAYED_WORK(&di->set_charged_work, + ds2760_battery_set_charged_work); di->monitor_wqueue = create_singlethread_workqueue(dev_name(&pdev->dev)); if (!di->monitor_wqueue) { retval = -ESRCH; @@ -422,6 +517,8 @@ static int ds2760_battery_remove(struct platform_device *pdev) cancel_rearming_delayed_workqueue(di->monitor_wqueue, &di->monitor_work); + cancel_rearming_delayed_workqueue(di->monitor_wqueue, + &di->set_charged_work); destroy_workqueue(di->monitor_wqueue); power_supply_unregister(&di->bat); diff --git a/drivers/power/olpc_battery.c b/drivers/power/olpc_battery.c index 58e419299cd6..8fefe5a73558 100644 --- a/drivers/power/olpc_battery.c +++ b/drivers/power/olpc_battery.c @@ -10,7 +10,9 @@ #include <linux/kernel.h> #include <linux/module.h> +#include <linux/types.h> #include <linux/err.h> +#include <linux/device.h> #include <linux/platform_device.h> #include <linux/power_supply.h> #include <linux/jiffies.h> @@ -231,6 +233,14 @@ static int olpc_bat_get_property(struct power_supply *psy, if (ret) return ret; break; + case POWER_SUPPLY_PROP_CHARGE_TYPE: + if (ec_byte & BAT_STAT_TRICKLE) + val->intval = POWER_SUPPLY_CHARGE_TYPE_TRICKLE; + else if (ec_byte & BAT_STAT_CHARGING) + val->intval = POWER_SUPPLY_CHARGE_TYPE_FAST; + else + val->intval = POWER_SUPPLY_CHARGE_TYPE_NONE; + break; case POWER_SUPPLY_PROP_PRESENT: val->intval = !!(ec_byte & (BAT_STAT_PRESENT | BAT_STAT_TRICKLE)); @@ -276,6 +286,14 @@ static int olpc_bat_get_property(struct power_supply *psy, return ret; val->intval = ec_byte; break; + case POWER_SUPPLY_PROP_CAPACITY_LEVEL: + if (ec_byte & BAT_STAT_FULL) + val->intval = POWER_SUPPLY_CAPACITY_LEVEL_FULL; + else if (ec_byte & BAT_STAT_LOW) + val->intval = POWER_SUPPLY_CAPACITY_LEVEL_LOW; + else + val->intval = POWER_SUPPLY_CAPACITY_LEVEL_NORMAL; + break; case POWER_SUPPLY_PROP_TEMP: ret = olpc_ec_cmd(EC_BAT_TEMP, NULL, 0, (void *)&ec_word, 2); if (ret) @@ -315,12 +333,14 @@ static int olpc_bat_get_property(struct power_supply *psy, static enum power_supply_property olpc_bat_props[] = { POWER_SUPPLY_PROP_STATUS, + POWER_SUPPLY_PROP_CHARGE_TYPE, POWER_SUPPLY_PROP_PRESENT, POWER_SUPPLY_PROP_HEALTH, POWER_SUPPLY_PROP_TECHNOLOGY, POWER_SUPPLY_PROP_VOLTAGE_AVG, POWER_SUPPLY_PROP_CURRENT_AVG, POWER_SUPPLY_PROP_CAPACITY, + POWER_SUPPLY_PROP_CAPACITY_LEVEL, POWER_SUPPLY_PROP_TEMP, POWER_SUPPLY_PROP_TEMP_AMBIENT, POWER_SUPPLY_PROP_MANUFACTURER, @@ -370,6 +390,29 @@ static struct bin_attribute olpc_bat_eeprom = { .read = olpc_bat_eeprom_read, }; +/* Allow userspace to see the specific error value pulled from the EC */ + +static ssize_t olpc_bat_error_read(struct device *dev, + struct device_attribute *attr, char *buf) +{ + uint8_t ec_byte; + ssize_t ret; + + ret = olpc_ec_cmd(EC_BAT_ERRCODE, NULL, 0, &ec_byte, 1); + if (ret < 0) + return ret; + + return sprintf(buf, "%d\n", ec_byte); +} + +static struct device_attribute olpc_bat_error = { + .attr = { + .name = "error", + .mode = S_IRUGO, + }, + .show = olpc_bat_error_read, +}; + /********************************************************************* * Initialisation *********************************************************************/ @@ -433,8 +476,14 @@ static int __init olpc_bat_init(void) if (ret) goto eeprom_failed; + ret = device_create_file(olpc_bat.dev, &olpc_bat_error); + if (ret) + goto error_failed; + goto success; +error_failed: + device_remove_bin_file(olpc_bat.dev, &olpc_bat_eeprom); eeprom_failed: power_supply_unregister(&olpc_bat); battery_failed: @@ -447,6 +496,7 @@ success: static void __exit olpc_bat_exit(void) { + device_remove_file(olpc_bat.dev, &olpc_bat_error); device_remove_bin_file(olpc_bat.dev, &olpc_bat_eeprom); power_supply_unregister(&olpc_bat); power_supply_unregister(&olpc_ac); diff --git a/drivers/power/power_supply_core.c b/drivers/power/power_supply_core.c index 5520040449c4..cce75b40b435 100644 --- a/drivers/power/power_supply_core.c +++ b/drivers/power/power_supply_core.c @@ -18,7 +18,9 @@ #include <linux/power_supply.h> #include "power_supply.h" +/* exported for the APM Power driver, APM emulation */ struct class *power_supply_class; +EXPORT_SYMBOL_GPL(power_supply_class); static int __power_supply_changed_work(struct device *dev, void *data) { @@ -55,6 +57,7 @@ void power_supply_changed(struct power_supply *psy) schedule_work(&psy->changed_work); } +EXPORT_SYMBOL_GPL(power_supply_changed); static int __power_supply_am_i_supplied(struct device *dev, void *data) { @@ -86,6 +89,7 @@ int power_supply_am_i_supplied(struct power_supply *psy) return error; } +EXPORT_SYMBOL_GPL(power_supply_am_i_supplied); static int __power_supply_is_system_supplied(struct device *dev, void *data) { @@ -110,6 +114,35 @@ int power_supply_is_system_supplied(void) return error; } +EXPORT_SYMBOL_GPL(power_supply_is_system_supplied); + +int power_supply_set_battery_charged(struct power_supply *psy) +{ + if (psy->type == POWER_SUPPLY_TYPE_BATTERY && psy->set_charged) { + psy->set_charged(psy); + return 0; + } + + return -EINVAL; +} +EXPORT_SYMBOL_GPL(power_supply_set_battery_charged); + +static int power_supply_match_device_by_name(struct device *dev, void *data) +{ + const char *name = data; + struct power_supply *psy = dev_get_drvdata(dev); + + return strcmp(psy->name, name) == 0; +} + +struct power_supply *power_supply_get_by_name(char *name) +{ + struct device *dev = class_find_device(power_supply_class, NULL, name, + power_supply_match_device_by_name); + + return dev ? dev_get_drvdata(dev) : NULL; +} +EXPORT_SYMBOL_GPL(power_supply_get_by_name); int power_supply_register(struct device *parent, struct power_supply *psy) { @@ -144,6 +177,7 @@ dev_create_failed: success: return rc; } +EXPORT_SYMBOL_GPL(power_supply_register); void power_supply_unregister(struct power_supply *psy) { @@ -152,6 +186,7 @@ void power_supply_unregister(struct power_supply *psy) power_supply_remove_attrs(psy); device_unregister(psy->dev); } +EXPORT_SYMBOL_GPL(power_supply_unregister); static int __init power_supply_class_init(void) { @@ -170,15 +205,6 @@ static void __exit power_supply_class_exit(void) class_destroy(power_supply_class); } -EXPORT_SYMBOL_GPL(power_supply_changed); -EXPORT_SYMBOL_GPL(power_supply_am_i_supplied); -EXPORT_SYMBOL_GPL(power_supply_is_system_supplied); -EXPORT_SYMBOL_GPL(power_supply_register); -EXPORT_SYMBOL_GPL(power_supply_unregister); - -/* exported for the APM Power driver, APM emulation */ -EXPORT_SYMBOL_GPL(power_supply_class); - subsys_initcall(power_supply_class_init); module_exit(power_supply_class_exit); diff --git a/drivers/power/power_supply_sysfs.c b/drivers/power/power_supply_sysfs.c index da73591017f9..08144393d64b 100644 --- a/drivers/power/power_supply_sysfs.c +++ b/drivers/power/power_supply_sysfs.c @@ -43,6 +43,9 @@ static ssize_t power_supply_show_property(struct device *dev, static char *status_text[] = { "Unknown", "Charging", "Discharging", "Not charging", "Full" }; + static char *charge_type[] = { + "Unknown", "N/A", "Trickle", "Fast" + }; static char *health_text[] = { "Unknown", "Good", "Overheat", "Dead", "Over voltage", "Unspecified failure", "Cold", @@ -51,6 +54,9 @@ static ssize_t power_supply_show_property(struct device *dev, "Unknown", "NiMH", "Li-ion", "Li-poly", "LiFe", "NiCd", "LiMn" }; + static char *capacity_level_text[] = { + "Unknown", "Critical", "Low", "Normal", "High", "Full" + }; ssize_t ret; struct power_supply *psy = dev_get_drvdata(dev); const ptrdiff_t off = attr - power_supply_attrs; @@ -67,10 +73,14 @@ static ssize_t power_supply_show_property(struct device *dev, if (off == POWER_SUPPLY_PROP_STATUS) return sprintf(buf, "%s\n", status_text[value.intval]); + else if (off == POWER_SUPPLY_PROP_CHARGE_TYPE) + return sprintf(buf, "%s\n", charge_type[value.intval]); else if (off == POWER_SUPPLY_PROP_HEALTH) return sprintf(buf, "%s\n", health_text[value.intval]); else if (off == POWER_SUPPLY_PROP_TECHNOLOGY) return sprintf(buf, "%s\n", technology_text[value.intval]); + else if (off == POWER_SUPPLY_PROP_CAPACITY_LEVEL) + return sprintf(buf, "%s\n", capacity_level_text[value.intval]); else if (off >= POWER_SUPPLY_PROP_MODEL_NAME) return sprintf(buf, "%s\n", value.strval); @@ -81,6 +91,7 @@ static ssize_t power_supply_show_property(struct device *dev, static struct device_attribute power_supply_attrs[] = { /* Properties of type `int' */ POWER_SUPPLY_ATTR(status), + POWER_SUPPLY_ATTR(charge_type), POWER_SUPPLY_ATTR(health), POWER_SUPPLY_ATTR(present), POWER_SUPPLY_ATTR(online), @@ -109,6 +120,7 @@ static struct device_attribute power_supply_attrs[] = { POWER_SUPPLY_ATTR(energy_now), POWER_SUPPLY_ATTR(energy_avg), POWER_SUPPLY_ATTR(capacity), + POWER_SUPPLY_ATTR(capacity_level), POWER_SUPPLY_ATTR(temp), POWER_SUPPLY_ATTR(temp_ambient), POWER_SUPPLY_ATTR(time_to_empty_now), diff --git a/drivers/power/wm831x_power.c b/drivers/power/wm831x_power.c new file mode 100644 index 000000000000..2a4c8b0b829c --- /dev/null +++ b/drivers/power/wm831x_power.c @@ -0,0 +1,779 @@ +/* + * PMU driver for Wolfson Microelectronics wm831x PMICs + * + * Copyright 2009 Wolfson Microelectronics PLC. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/module.h> +#include <linux/err.h> +#include <linux/platform_device.h> +#include <linux/power_supply.h> + +#include <linux/mfd/wm831x/core.h> +#include <linux/mfd/wm831x/auxadc.h> +#include <linux/mfd/wm831x/pmu.h> +#include <linux/mfd/wm831x/pdata.h> + +struct wm831x_power { + struct wm831x *wm831x; + struct power_supply wall; + struct power_supply backup; + struct power_supply usb; + struct power_supply battery; +}; + +static int wm831x_power_check_online(struct wm831x *wm831x, int supply, + union power_supply_propval *val) +{ + int ret; + + ret = wm831x_reg_read(wm831x, WM831X_SYSTEM_STATUS); + if (ret < 0) + return ret; + + if (ret & supply) + val->intval = 1; + else + val->intval = 0; + + return 0; +} + +static int wm831x_power_read_voltage(struct wm831x *wm831x, + enum wm831x_auxadc src, + union power_supply_propval *val) +{ + int ret; + + ret = wm831x_auxadc_read_uv(wm831x, src); + if (ret >= 0) + val->intval = ret; + + return ret; +} + +/********************************************************************* + * WALL Power + *********************************************************************/ +static int wm831x_wall_get_prop(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + struct wm831x_power *wm831x_power = dev_get_drvdata(psy->dev->parent); + struct wm831x *wm831x = wm831x_power->wm831x; + int ret = 0; + + switch (psp) { + case POWER_SUPPLY_PROP_ONLINE: + ret = wm831x_power_check_online(wm831x, WM831X_PWR_WALL, val); + break; + case POWER_SUPPLY_PROP_VOLTAGE_NOW: + ret = wm831x_power_read_voltage(wm831x, WM831X_AUX_WALL, val); + break; + default: + ret = -EINVAL; + break; + } + + return ret; +} + +static enum power_supply_property wm831x_wall_props[] = { + POWER_SUPPLY_PROP_ONLINE, + POWER_SUPPLY_PROP_VOLTAGE_NOW, +}; + +/********************************************************************* + * USB Power + *********************************************************************/ +static int wm831x_usb_get_prop(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + struct wm831x_power *wm831x_power = dev_get_drvdata(psy->dev->parent); + struct wm831x *wm831x = wm831x_power->wm831x; + int ret = 0; + + switch (psp) { + case POWER_SUPPLY_PROP_ONLINE: + ret = wm831x_power_check_online(wm831x, WM831X_PWR_USB, val); + break; + case POWER_SUPPLY_PROP_VOLTAGE_NOW: + ret = wm831x_power_read_voltage(wm831x, WM831X_AUX_USB, val); + break; + default: + ret = -EINVAL; + break; + } + + return ret; +} + +static enum power_supply_property wm831x_usb_props[] = { + POWER_SUPPLY_PROP_ONLINE, + POWER_SUPPLY_PROP_VOLTAGE_NOW, +}; + +/********************************************************************* + * Battery properties + *********************************************************************/ + +struct chg_map { + int val; + int reg_val; +}; + +static struct chg_map trickle_ilims[] = { + { 50, 0 << WM831X_CHG_TRKL_ILIM_SHIFT }, + { 100, 1 << WM831X_CHG_TRKL_ILIM_SHIFT }, + { 150, 2 << WM831X_CHG_TRKL_ILIM_SHIFT }, + { 200, 3 << WM831X_CHG_TRKL_ILIM_SHIFT }, +}; + +static struct chg_map vsels[] = { + { 4050, 0 << WM831X_CHG_VSEL_SHIFT }, + { 4100, 1 << WM831X_CHG_VSEL_SHIFT }, + { 4150, 2 << WM831X_CHG_VSEL_SHIFT }, + { 4200, 3 << WM831X_CHG_VSEL_SHIFT }, +}; + +static struct chg_map fast_ilims[] = { + { 0, 0 << WM831X_CHG_FAST_ILIM_SHIFT }, + { 50, 1 << WM831X_CHG_FAST_ILIM_SHIFT }, + { 100, 2 << WM831X_CHG_FAST_ILIM_SHIFT }, + { 150, 3 << WM831X_CHG_FAST_ILIM_SHIFT }, + { 200, 4 << WM831X_CHG_FAST_ILIM_SHIFT }, + { 250, 5 << WM831X_CHG_FAST_ILIM_SHIFT }, + { 300, 6 << WM831X_CHG_FAST_ILIM_SHIFT }, + { 350, 7 << WM831X_CHG_FAST_ILIM_SHIFT }, + { 400, 8 << WM831X_CHG_FAST_ILIM_SHIFT }, + { 450, 9 << WM831X_CHG_FAST_ILIM_SHIFT }, + { 500, 10 << WM831X_CHG_FAST_ILIM_SHIFT }, + { 600, 11 << WM831X_CHG_FAST_ILIM_SHIFT }, + { 700, 12 << WM831X_CHG_FAST_ILIM_SHIFT }, + { 800, 13 << WM831X_CHG_FAST_ILIM_SHIFT }, + { 900, 14 << WM831X_CHG_FAST_ILIM_SHIFT }, + { 1000, 15 << WM831X_CHG_FAST_ILIM_SHIFT }, +}; + +static struct chg_map eoc_iterms[] = { + { 20, 0 << WM831X_CHG_ITERM_SHIFT }, + { 30, 1 << WM831X_CHG_ITERM_SHIFT }, + { 40, 2 << WM831X_CHG_ITERM_SHIFT }, + { 50, 3 << WM831X_CHG_ITERM_SHIFT }, + { 60, 4 << WM831X_CHG_ITERM_SHIFT }, + { 70, 5 << WM831X_CHG_ITERM_SHIFT }, + { 80, 6 << WM831X_CHG_ITERM_SHIFT }, + { 90, 7 << WM831X_CHG_ITERM_SHIFT }, +}; + +static struct chg_map chg_times[] = { + { 60, 0 << WM831X_CHG_TIME_SHIFT }, + { 90, 1 << WM831X_CHG_TIME_SHIFT }, + { 120, 2 << WM831X_CHG_TIME_SHIFT }, + { 150, 3 << WM831X_CHG_TIME_SHIFT }, + { 180, 4 << WM831X_CHG_TIME_SHIFT }, + { 210, 5 << WM831X_CHG_TIME_SHIFT }, + { 240, 6 << WM831X_CHG_TIME_SHIFT }, + { 270, 7 << WM831X_CHG_TIME_SHIFT }, + { 300, 8 << WM831X_CHG_TIME_SHIFT }, + { 330, 9 << WM831X_CHG_TIME_SHIFT }, + { 360, 10 << WM831X_CHG_TIME_SHIFT }, + { 390, 11 << WM831X_CHG_TIME_SHIFT }, + { 420, 12 << WM831X_CHG_TIME_SHIFT }, + { 450, 13 << WM831X_CHG_TIME_SHIFT }, + { 480, 14 << WM831X_CHG_TIME_SHIFT }, + { 510, 15 << WM831X_CHG_TIME_SHIFT }, +}; + +static void wm831x_battey_apply_config(struct wm831x *wm831x, + struct chg_map *map, int count, int val, + int *reg, const char *name, + const char *units) +{ + int i; + + for (i = 0; i < count; i++) + if (val == map[i].val) + break; + if (i == count) { + dev_err(wm831x->dev, "Invalid %s %d%s\n", + name, val, units); + } else { + *reg |= map[i].reg_val; + dev_dbg(wm831x->dev, "Set %s of %d%s\n", name, val, units); + } +} + +static void wm831x_config_battery(struct wm831x *wm831x) +{ + struct wm831x_pdata *wm831x_pdata = wm831x->dev->platform_data; + struct wm831x_battery_pdata *pdata; + int ret, reg1, reg2; + + if (!wm831x_pdata || !wm831x_pdata->battery) { + dev_warn(wm831x->dev, + "No battery charger configuration\n"); + return; + } + + pdata = wm831x_pdata->battery; + + reg1 = 0; + reg2 = 0; + + if (!pdata->enable) { + dev_info(wm831x->dev, "Battery charger disabled\n"); + return; + } + + reg1 |= WM831X_CHG_ENA; + if (pdata->off_mask) + reg2 |= WM831X_CHG_OFF_MSK; + if (pdata->fast_enable) + reg1 |= WM831X_CHG_FAST; + + wm831x_battey_apply_config(wm831x, trickle_ilims, + ARRAY_SIZE(trickle_ilims), + pdata->trickle_ilim, ®2, + "trickle charge current limit", "mA"); + + wm831x_battey_apply_config(wm831x, vsels, ARRAY_SIZE(vsels), + pdata->vsel, ®2, + "target voltage", "mV"); + + wm831x_battey_apply_config(wm831x, fast_ilims, ARRAY_SIZE(fast_ilims), + pdata->fast_ilim, ®2, + "fast charge current limit", "mA"); + + wm831x_battey_apply_config(wm831x, eoc_iterms, ARRAY_SIZE(eoc_iterms), + pdata->eoc_iterm, ®1, + "end of charge current threshold", "mA"); + + wm831x_battey_apply_config(wm831x, chg_times, ARRAY_SIZE(chg_times), + pdata->timeout, ®2, + "charger timeout", "min"); + + ret = wm831x_reg_unlock(wm831x); + if (ret != 0) { + dev_err(wm831x->dev, "Failed to unlock registers: %d\n", ret); + return; + } + + ret = wm831x_set_bits(wm831x, WM831X_CHARGER_CONTROL_1, + WM831X_CHG_ENA_MASK | + WM831X_CHG_FAST_MASK | + WM831X_CHG_ITERM_MASK | + WM831X_CHG_ITERM_MASK, + reg1); + if (ret != 0) + dev_err(wm831x->dev, "Failed to set charger control 1: %d\n", + ret); + + ret = wm831x_set_bits(wm831x, WM831X_CHARGER_CONTROL_2, + WM831X_CHG_OFF_MSK | + WM831X_CHG_TIME_MASK | + WM831X_CHG_FAST_ILIM_MASK | + WM831X_CHG_TRKL_ILIM_MASK | + WM831X_CHG_VSEL_MASK, + reg2); + if (ret != 0) + dev_err(wm831x->dev, "Failed to set charger control 2: %d\n", + ret); + + wm831x_reg_lock(wm831x); +} + +static int wm831x_bat_check_status(struct wm831x *wm831x, int *status) +{ + int ret; + + ret = wm831x_reg_read(wm831x, WM831X_SYSTEM_STATUS); + if (ret < 0) + return ret; + + if (ret & WM831X_PWR_SRC_BATT) { + *status = POWER_SUPPLY_STATUS_DISCHARGING; + return 0; + } + + ret = wm831x_reg_read(wm831x, WM831X_CHARGER_STATUS); + if (ret < 0) + return ret; + + switch (ret & WM831X_CHG_STATE_MASK) { + case WM831X_CHG_STATE_OFF: + *status = POWER_SUPPLY_STATUS_NOT_CHARGING; + break; + case WM831X_CHG_STATE_TRICKLE: + case WM831X_CHG_STATE_FAST: + *status = POWER_SUPPLY_STATUS_CHARGING; + break; + + default: + *status = POWER_SUPPLY_STATUS_UNKNOWN; + break; + } + + return 0; +} + +static int wm831x_bat_check_type(struct wm831x *wm831x, int *type) +{ + int ret; + + ret = wm831x_reg_read(wm831x, WM831X_CHARGER_STATUS); + if (ret < 0) + return ret; + + switch (ret & WM831X_CHG_STATE_MASK) { + case WM831X_CHG_STATE_TRICKLE: + case WM831X_CHG_STATE_TRICKLE_OT: + *type = POWER_SUPPLY_CHARGE_TYPE_TRICKLE; + break; + case WM831X_CHG_STATE_FAST: + case WM831X_CHG_STATE_FAST_OT: + *type = POWER_SUPPLY_CHARGE_TYPE_FAST; + break; + default: + *type = POWER_SUPPLY_CHARGE_TYPE_NONE; + break; + } + + return 0; +} + +static int wm831x_bat_check_health(struct wm831x *wm831x, int *health) +{ + int ret; + + ret = wm831x_reg_read(wm831x, WM831X_CHARGER_STATUS); + if (ret < 0) + return ret; + + if (ret & WM831X_BATT_HOT_STS) { + *health = POWER_SUPPLY_HEALTH_OVERHEAT; + return 0; + } + + if (ret & WM831X_BATT_COLD_STS) { + *health = POWER_SUPPLY_HEALTH_COLD; + return 0; + } + + if (ret & WM831X_BATT_OV_STS) { + *health = POWER_SUPPLY_HEALTH_OVERVOLTAGE; + return 0; + } + + switch (ret & WM831X_CHG_STATE_MASK) { + case WM831X_CHG_STATE_TRICKLE_OT: + case WM831X_CHG_STATE_FAST_OT: + *health = POWER_SUPPLY_HEALTH_OVERHEAT; + break; + case WM831X_CHG_STATE_DEFECTIVE: + *health = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE; + break; + default: + *health = POWER_SUPPLY_HEALTH_GOOD; + break; + } + + return 0; +} + +static int wm831x_bat_get_prop(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + struct wm831x_power *wm831x_power = dev_get_drvdata(psy->dev->parent); + struct wm831x *wm831x = wm831x_power->wm831x; + int ret = 0; + + switch (psp) { + case POWER_SUPPLY_PROP_STATUS: + ret = wm831x_bat_check_status(wm831x, &val->intval); + break; + case POWER_SUPPLY_PROP_ONLINE: + ret = wm831x_power_check_online(wm831x, WM831X_PWR_SRC_BATT, + val); + break; + case POWER_SUPPLY_PROP_VOLTAGE_NOW: + ret = wm831x_power_read_voltage(wm831x, WM831X_AUX_BATT, val); + break; + case POWER_SUPPLY_PROP_HEALTH: + ret = wm831x_bat_check_health(wm831x, &val->intval); + break; + case POWER_SUPPLY_PROP_CHARGE_TYPE: + ret = wm831x_bat_check_type(wm831x, &val->intval); + break; + default: + ret = -EINVAL; + break; + } + + return ret; +} + +static enum power_supply_property wm831x_bat_props[] = { + POWER_SUPPLY_PROP_STATUS, + POWER_SUPPLY_PROP_ONLINE, + POWER_SUPPLY_PROP_VOLTAGE_NOW, + POWER_SUPPLY_PROP_HEALTH, + POWER_SUPPLY_PROP_CHARGE_TYPE, +}; + +static const char *wm831x_bat_irqs[] = { + "BATT HOT", + "BATT COLD", + "BATT FAIL", + "OV", + "END", + "TO", + "MODE", + "START", +}; + +static irqreturn_t wm831x_bat_irq(int irq, void *data) +{ + struct wm831x_power *wm831x_power = data; + struct wm831x *wm831x = wm831x_power->wm831x; + + dev_dbg(wm831x->dev, "Battery status changed: %d\n", irq); + + /* The battery charger is autonomous so we don't need to do + * anything except kick user space */ + power_supply_changed(&wm831x_power->battery); + + return IRQ_HANDLED; +} + + +/********************************************************************* + * Backup supply properties + *********************************************************************/ + +static void wm831x_config_backup(struct wm831x *wm831x) +{ + struct wm831x_pdata *wm831x_pdata = wm831x->dev->platform_data; + struct wm831x_backup_pdata *pdata; + int ret, reg; + + if (!wm831x_pdata || !wm831x_pdata->backup) { + dev_warn(wm831x->dev, + "No backup battery charger configuration\n"); + return; + } + + pdata = wm831x_pdata->backup; + + reg = 0; + + if (pdata->charger_enable) + reg |= WM831X_BKUP_CHG_ENA | WM831X_BKUP_BATT_DET_ENA; + if (pdata->no_constant_voltage) + reg |= WM831X_BKUP_CHG_MODE; + + switch (pdata->vlim) { + case 2500: + break; + case 3100: + reg |= WM831X_BKUP_CHG_VLIM; + break; + default: + dev_err(wm831x->dev, "Invalid backup voltage limit %dmV\n", + pdata->vlim); + } + + switch (pdata->ilim) { + case 100: + break; + case 200: + reg |= 1; + break; + case 300: + reg |= 2; + break; + case 400: + reg |= 3; + break; + default: + dev_err(wm831x->dev, "Invalid backup current limit %duA\n", + pdata->ilim); + } + + ret = wm831x_reg_unlock(wm831x); + if (ret != 0) { + dev_err(wm831x->dev, "Failed to unlock registers: %d\n", ret); + return; + } + + ret = wm831x_set_bits(wm831x, WM831X_BACKUP_CHARGER_CONTROL, + WM831X_BKUP_CHG_ENA_MASK | + WM831X_BKUP_CHG_MODE_MASK | + WM831X_BKUP_BATT_DET_ENA_MASK | + WM831X_BKUP_CHG_VLIM_MASK | + WM831X_BKUP_CHG_ILIM_MASK, + reg); + if (ret != 0) + dev_err(wm831x->dev, + "Failed to set backup charger config: %d\n", ret); + + wm831x_reg_lock(wm831x); +} + +static int wm831x_backup_get_prop(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + struct wm831x_power *wm831x_power = dev_get_drvdata(psy->dev->parent); + struct wm831x *wm831x = wm831x_power->wm831x; + int ret = 0; + + ret = wm831x_reg_read(wm831x, WM831X_BACKUP_CHARGER_CONTROL); + if (ret < 0) + return ret; + + switch (psp) { + case POWER_SUPPLY_PROP_STATUS: + if (ret & WM831X_BKUP_CHG_STS) + val->intval = POWER_SUPPLY_STATUS_CHARGING; + else + val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING; + break; + + case POWER_SUPPLY_PROP_VOLTAGE_NOW: + ret = wm831x_power_read_voltage(wm831x, WM831X_AUX_BKUP_BATT, + val); + break; + + case POWER_SUPPLY_PROP_PRESENT: + if (ret & WM831X_BKUP_CHG_STS) + val->intval = 1; + else + val->intval = 0; + break; + + default: + ret = -EINVAL; + break; + } + + return ret; +} + +static enum power_supply_property wm831x_backup_props[] = { + POWER_SUPPLY_PROP_STATUS, + POWER_SUPPLY_PROP_VOLTAGE_NOW, + POWER_SUPPLY_PROP_PRESENT, +}; + +/********************************************************************* + * Initialisation + *********************************************************************/ + +static irqreturn_t wm831x_syslo_irq(int irq, void *data) +{ + struct wm831x_power *wm831x_power = data; + struct wm831x *wm831x = wm831x_power->wm831x; + + /* Not much we can actually *do* but tell people for + * posterity, we're probably about to run out of power. */ + dev_crit(wm831x->dev, "SYSVDD under voltage\n"); + + return IRQ_HANDLED; +} + +static irqreturn_t wm831x_pwr_src_irq(int irq, void *data) +{ + struct wm831x_power *wm831x_power = data; + struct wm831x *wm831x = wm831x_power->wm831x; + + dev_dbg(wm831x->dev, "Power source changed\n"); + + /* Just notify for everything - little harm in overnotifying. + * The backup battery is not a power source while the system + * is running so skip that. + */ + power_supply_changed(&wm831x_power->battery); + power_supply_changed(&wm831x_power->usb); + power_supply_changed(&wm831x_power->wall); + + return IRQ_HANDLED; +} + +static __devinit int wm831x_power_probe(struct platform_device *pdev) +{ + struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent); + struct wm831x_power *power; + struct power_supply *usb; + struct power_supply *battery; + struct power_supply *wall; + struct power_supply *backup; + int ret, irq, i; + + power = kzalloc(sizeof(struct wm831x_power), GFP_KERNEL); + if (power == NULL) + return -ENOMEM; + + power->wm831x = wm831x; + platform_set_drvdata(pdev, power); + + usb = &power->usb; + battery = &power->battery; + wall = &power->wall; + backup = &power->backup; + + /* We ignore configuration failures since we can still read back + * the status without enabling either of the chargers. + */ + wm831x_config_battery(wm831x); + wm831x_config_backup(wm831x); + + wall->name = "wm831x-wall"; + wall->type = POWER_SUPPLY_TYPE_MAINS; + wall->properties = wm831x_wall_props; + wall->num_properties = ARRAY_SIZE(wm831x_wall_props); + wall->get_property = wm831x_wall_get_prop; + ret = power_supply_register(&pdev->dev, wall); + if (ret) + goto err_kmalloc; + + battery->name = "wm831x-battery"; + battery->properties = wm831x_bat_props; + battery->num_properties = ARRAY_SIZE(wm831x_bat_props); + battery->get_property = wm831x_bat_get_prop; + battery->use_for_apm = 1; + ret = power_supply_register(&pdev->dev, battery); + if (ret) + goto err_wall; + + usb->name = "wm831x-usb", + usb->type = POWER_SUPPLY_TYPE_USB; + usb->properties = wm831x_usb_props; + usb->num_properties = ARRAY_SIZE(wm831x_usb_props); + usb->get_property = wm831x_usb_get_prop; + ret = power_supply_register(&pdev->dev, usb); + if (ret) + goto err_battery; + + backup->name = "wm831x-backup"; + backup->type = POWER_SUPPLY_TYPE_BATTERY; + backup->properties = wm831x_backup_props; + backup->num_properties = ARRAY_SIZE(wm831x_backup_props); + backup->get_property = wm831x_backup_get_prop; + ret = power_supply_register(&pdev->dev, backup); + if (ret) + goto err_usb; + + irq = platform_get_irq_byname(pdev, "SYSLO"); + ret = wm831x_request_irq(wm831x, irq, wm831x_syslo_irq, + IRQF_TRIGGER_RISING, "SYSLO", + power); + if (ret != 0) { + dev_err(&pdev->dev, "Failed to request SYSLO IRQ %d: %d\n", + irq, ret); + goto err_backup; + } + + irq = platform_get_irq_byname(pdev, "PWR SRC"); + ret = wm831x_request_irq(wm831x, irq, wm831x_pwr_src_irq, + IRQF_TRIGGER_RISING, "Power source", + power); + if (ret != 0) { + dev_err(&pdev->dev, "Failed to request PWR SRC IRQ %d: %d\n", + irq, ret); + goto err_syslo; + } + + for (i = 0; i < ARRAY_SIZE(wm831x_bat_irqs); i++) { + irq = platform_get_irq_byname(pdev, wm831x_bat_irqs[i]); + ret = wm831x_request_irq(wm831x, irq, wm831x_bat_irq, + IRQF_TRIGGER_RISING, + wm831x_bat_irqs[i], + power); + if (ret != 0) { + dev_err(&pdev->dev, + "Failed to request %s IRQ %d: %d\n", + wm831x_bat_irqs[i], irq, ret); + goto err_bat_irq; + } + } + + return ret; + +err_bat_irq: + for (; i >= 0; i--) { + irq = platform_get_irq_byname(pdev, wm831x_bat_irqs[i]); + wm831x_free_irq(wm831x, irq, power); + } + irq = platform_get_irq_byname(pdev, "PWR SRC"); + wm831x_free_irq(wm831x, irq, power); +err_syslo: + irq = platform_get_irq_byname(pdev, "SYSLO"); + wm831x_free_irq(wm831x, irq, power); +err_backup: + power_supply_unregister(backup); +err_usb: + power_supply_unregister(usb); +err_battery: + power_supply_unregister(battery); +err_wall: + power_supply_unregister(wall); +err_kmalloc: + kfree(power); + return ret; +} + +static __devexit int wm831x_power_remove(struct platform_device *pdev) +{ + struct wm831x_power *wm831x_power = platform_get_drvdata(pdev); + struct wm831x *wm831x = wm831x_power->wm831x; + int irq, i; + + for (i = 0; i < ARRAY_SIZE(wm831x_bat_irqs); i++) { + irq = platform_get_irq_byname(pdev, wm831x_bat_irqs[i]); + wm831x_free_irq(wm831x, irq, wm831x_power); + } + + irq = platform_get_irq_byname(pdev, "PWR SRC"); + wm831x_free_irq(wm831x, irq, wm831x_power); + + irq = platform_get_irq_byname(pdev, "SYSLO"); + wm831x_free_irq(wm831x, irq, wm831x_power); + + power_supply_unregister(&wm831x_power->backup); + power_supply_unregister(&wm831x_power->battery); + power_supply_unregister(&wm831x_power->wall); + power_supply_unregister(&wm831x_power->usb); + return 0; +} + +static struct platform_driver wm831x_power_driver = { + .probe = wm831x_power_probe, + .remove = __devexit_p(wm831x_power_remove), + .driver = { + .name = "wm831x-power", + }, +}; + +static int __init wm831x_power_init(void) +{ + return platform_driver_register(&wm831x_power_driver); +} +module_init(wm831x_power_init); + +static void __exit wm831x_power_exit(void) +{ + platform_driver_unregister(&wm831x_power_driver); +} +module_exit(wm831x_power_exit); + +MODULE_DESCRIPTION("Power supply driver for WM831x PMICs"); +MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:wm831x-power"); diff --git a/drivers/power/wm8350_power.c b/drivers/power/wm8350_power.c index 1b16bf343f2f..28b0299c0043 100644 --- a/drivers/power/wm8350_power.c +++ b/drivers/power/wm8350_power.c @@ -321,6 +321,24 @@ static int wm8350_bat_check_health(struct wm8350 *wm8350) return POWER_SUPPLY_HEALTH_GOOD; } +static int wm8350_bat_get_charge_type(struct wm8350 *wm8350) +{ + int state; + + state = wm8350_reg_read(wm8350, WM8350_BATTERY_CHARGER_CONTROL_2) & + WM8350_CHG_STS_MASK; + switch (state) { + case WM8350_CHG_STS_OFF: + return POWER_SUPPLY_CHARGE_TYPE_NONE; + case WM8350_CHG_STS_TRICKLE: + return POWER_SUPPLY_CHARGE_TYPE_TRICKLE; + case WM8350_CHG_STS_FAST: + return POWER_SUPPLY_CHARGE_TYPE_FAST; + default: + return POWER_SUPPLY_CHARGE_TYPE_UNKNOWN; + } +} + static int wm8350_bat_get_property(struct power_supply *psy, enum power_supply_property psp, union power_supply_propval *val) @@ -342,6 +360,9 @@ static int wm8350_bat_get_property(struct power_supply *psy, case POWER_SUPPLY_PROP_HEALTH: val->intval = wm8350_bat_check_health(wm8350); break; + case POWER_SUPPLY_PROP_CHARGE_TYPE: + val->intval = wm8350_bat_get_charge_type(wm8350); + break; default: ret = -EINVAL; break; @@ -355,6 +376,7 @@ static enum power_supply_property wm8350_bat_props[] = { POWER_SUPPLY_PROP_ONLINE, POWER_SUPPLY_PROP_VOLTAGE_NOW, POWER_SUPPLY_PROP_HEALTH, + POWER_SUPPLY_PROP_CHARGE_TYPE, }; /********************************************************************* diff --git a/drivers/power/wm97xx_battery.c b/drivers/power/wm97xx_battery.c index b787335a8419..f2bfd296dbae 100644 --- a/drivers/power/wm97xx_battery.c +++ b/drivers/power/wm97xx_battery.c @@ -22,17 +22,20 @@ #include <linux/spinlock.h> #include <linux/interrupt.h> #include <linux/gpio.h> -#include <linux/wm97xx_batt.h> +#include <linux/irq.h> static DEFINE_MUTEX(bat_lock); static struct work_struct bat_work; struct mutex work_lock; static int bat_status = POWER_SUPPLY_STATUS_UNKNOWN; -static struct wm97xx_batt_info *pdata; +static struct wm97xx_batt_info *gpdata; static enum power_supply_property *prop; static unsigned long wm97xx_read_bat(struct power_supply *bat_ps) { + struct wm97xx_pdata *wmdata = bat_ps->dev->parent->platform_data; + struct wm97xx_batt_pdata *pdata = wmdata->batt_pdata; + return wm97xx_read_aux_adc(dev_get_drvdata(bat_ps->dev->parent), pdata->batt_aux) * pdata->batt_mult / pdata->batt_div; @@ -40,6 +43,9 @@ static unsigned long wm97xx_read_bat(struct power_supply *bat_ps) static unsigned long wm97xx_read_temp(struct power_supply *bat_ps) { + struct wm97xx_pdata *wmdata = bat_ps->dev->parent->platform_data; + struct wm97xx_batt_pdata *pdata = wmdata->batt_pdata; + return wm97xx_read_aux_adc(dev_get_drvdata(bat_ps->dev->parent), pdata->temp_aux) * pdata->temp_mult / pdata->temp_div; @@ -49,6 +55,9 @@ static int wm97xx_bat_get_property(struct power_supply *bat_ps, enum power_supply_property psp, union power_supply_propval *val) { + struct wm97xx_pdata *wmdata = bat_ps->dev->parent->platform_data; + struct wm97xx_batt_pdata *pdata = wmdata->batt_pdata; + switch (psp) { case POWER_SUPPLY_PROP_STATUS: val->intval = bat_status; @@ -97,6 +106,8 @@ static void wm97xx_bat_external_power_changed(struct power_supply *bat_ps) static void wm97xx_bat_update(struct power_supply *bat_ps) { int old_status = bat_status; + struct wm97xx_pdata *wmdata = bat_ps->dev->parent->platform_data; + struct wm97xx_batt_pdata *pdata = wmdata->batt_pdata; mutex_lock(&work_lock); @@ -127,21 +138,29 @@ static void wm97xx_bat_work(struct work_struct *work) wm97xx_bat_update(&bat_ps); } +static irqreturn_t wm97xx_chrg_irq(int irq, void *data) +{ + schedule_work(&bat_work); + return IRQ_HANDLED; +} + #ifdef CONFIG_PM -static int wm97xx_bat_suspend(struct platform_device *dev, pm_message_t state) +static int wm97xx_bat_suspend(struct device *dev) { flush_scheduled_work(); return 0; } -static int wm97xx_bat_resume(struct platform_device *dev) +static int wm97xx_bat_resume(struct device *dev) { schedule_work(&bat_work); return 0; } -#else -#define wm97xx_bat_suspend NULL -#define wm97xx_bat_resume NULL + +static struct dev_pm_ops wm97xx_bat_pm_ops = { + .suspend = wm97xx_bat_suspend, + .resume = wm97xx_bat_resume, +}; #endif static int __devinit wm97xx_bat_probe(struct platform_device *dev) @@ -149,6 +168,15 @@ static int __devinit wm97xx_bat_probe(struct platform_device *dev) int ret = 0; int props = 1; /* POWER_SUPPLY_PROP_PRESENT */ int i = 0; + struct wm97xx_pdata *wmdata = dev->dev.platform_data; + struct wm97xx_batt_pdata *pdata; + + if (gpdata) { + dev_err(&dev->dev, "Do not pass platform_data through " + "wm97xx_bat_set_pdata!\n"); + return -EINVAL; + } else + pdata = wmdata->batt_pdata; if (dev->id != -1) return -EINVAL; @@ -156,17 +184,22 @@ static int __devinit wm97xx_bat_probe(struct platform_device *dev) mutex_init(&work_lock); if (!pdata) { - dev_err(&dev->dev, "Please use wm97xx_bat_set_pdata\n"); + dev_err(&dev->dev, "No platform_data supplied\n"); return -EINVAL; } - if (pdata->charge_gpio >= 0 && gpio_is_valid(pdata->charge_gpio)) { + if (gpio_is_valid(pdata->charge_gpio)) { ret = gpio_request(pdata->charge_gpio, "BATT CHRG"); if (ret) goto err; ret = gpio_direction_input(pdata->charge_gpio); if (ret) goto err2; + ret = request_irq(gpio_to_irq(pdata->charge_gpio), + wm97xx_chrg_irq, IRQF_DISABLED, + "AC Detect", 0); + if (ret) + goto err2; props++; /* POWER_SUPPLY_PROP_STATUS */ } @@ -183,7 +216,7 @@ static int __devinit wm97xx_bat_probe(struct platform_device *dev) prop = kzalloc(props * sizeof(*prop), GFP_KERNEL); if (!prop) - goto err2; + goto err3; prop[i++] = POWER_SUPPLY_PROP_PRESENT; if (pdata->charge_gpio >= 0) @@ -216,21 +249,30 @@ static int __devinit wm97xx_bat_probe(struct platform_device *dev) if (!ret) schedule_work(&bat_work); else - goto err3; + goto err4; return 0; -err3: +err4: kfree(prop); +err3: + if (gpio_is_valid(pdata->charge_gpio)) + free_irq(gpio_to_irq(pdata->charge_gpio), dev); err2: - gpio_free(pdata->charge_gpio); + if (gpio_is_valid(pdata->charge_gpio)) + gpio_free(pdata->charge_gpio); err: return ret; } static int __devexit wm97xx_bat_remove(struct platform_device *dev) { - if (pdata && pdata->charge_gpio && pdata->charge_gpio >= 0) + struct wm97xx_pdata *wmdata = dev->dev.platform_data; + struct wm97xx_batt_pdata *pdata = wmdata->batt_pdata; + + if (pdata && gpio_is_valid(pdata->charge_gpio)) { + free_irq(gpio_to_irq(pdata->charge_gpio), dev); gpio_free(pdata->charge_gpio); + } flush_scheduled_work(); power_supply_unregister(&bat_ps); kfree(prop); @@ -241,11 +283,12 @@ static struct platform_driver wm97xx_bat_driver = { .driver = { .name = "wm97xx-battery", .owner = THIS_MODULE, +#ifdef CONFIG_PM + .pm = &wm97xx_bat_pm_ops, +#endif }, .probe = wm97xx_bat_probe, .remove = __devexit_p(wm97xx_bat_remove), - .suspend = wm97xx_bat_suspend, - .resume = wm97xx_bat_resume, }; static int __init wm97xx_bat_init(void) @@ -258,9 +301,9 @@ static void __exit wm97xx_bat_exit(void) platform_driver_unregister(&wm97xx_bat_driver); } -void __init wm97xx_bat_set_pdata(struct wm97xx_batt_info *data) +void wm97xx_bat_set_pdata(struct wm97xx_batt_info *data) { - pdata = data; + gpdata = data; } EXPORT_SYMBOL_GPL(wm97xx_bat_set_pdata); diff --git a/drivers/pps/kapi.c b/drivers/pps/kapi.c index 35a0b192d768..2d414e23d390 100644 --- a/drivers/pps/kapi.c +++ b/drivers/pps/kapi.c @@ -271,6 +271,7 @@ void pps_event(int source, struct pps_ktime *ts, int event, void *data) { struct pps_device *pps; unsigned long flags; + int captured = 0; if ((event & (PPS_CAPTUREASSERT | PPS_CAPTURECLEAR)) == 0) { printk(KERN_ERR "pps: unknown event (%x) for source %d\n", @@ -293,7 +294,8 @@ void pps_event(int source, struct pps_ktime *ts, int event, void *data) /* Check the event */ pps->current_mode = pps->params.mode; - if (event & PPS_CAPTUREASSERT) { + if ((event & PPS_CAPTUREASSERT) & + (pps->params.mode & PPS_CAPTUREASSERT)) { /* We have to add an offset? */ if (pps->params.mode & PPS_OFFSETASSERT) pps_add_offset(ts, &pps->params.assert_off_tu); @@ -303,8 +305,11 @@ void pps_event(int source, struct pps_ktime *ts, int event, void *data) pps->assert_sequence++; pr_debug("capture assert seq #%u for source %d\n", pps->assert_sequence, source); + + captured = ~0; } - if (event & PPS_CAPTURECLEAR) { + if ((event & PPS_CAPTURECLEAR) & + (pps->params.mode & PPS_CAPTURECLEAR)) { /* We have to add an offset? */ if (pps->params.mode & PPS_OFFSETCLEAR) pps_add_offset(ts, &pps->params.clear_off_tu); @@ -314,12 +319,17 @@ void pps_event(int source, struct pps_ktime *ts, int event, void *data) pps->clear_sequence++; pr_debug("capture clear seq #%u for source %d\n", pps->clear_sequence, source); + + captured = ~0; } - pps->go = ~0; - wake_up_interruptible(&pps->queue); + /* Wake up iif captured somthing */ + if (captured) { + pps->go = ~0; + wake_up_interruptible(&pps->queue); - kill_fasync(&pps->async_queue, SIGIO, POLL_IN); + kill_fasync(&pps->async_queue, SIGIO, POLL_IN); + } spin_unlock_irqrestore(&pps->lock, flags); diff --git a/drivers/pps/pps.c b/drivers/pps/pps.c index fea17e7805e9..ca5183bdad85 100644 --- a/drivers/pps/pps.c +++ b/drivers/pps/pps.c @@ -71,9 +71,14 @@ static long pps_cdev_ioctl(struct file *file, case PPS_GETPARAMS: pr_debug("PPS_GETPARAMS: source %d\n", pps->id); - /* Return current parameters */ - err = copy_to_user(uarg, &pps->params, - sizeof(struct pps_kparams)); + spin_lock_irq(&pps->lock); + + /* Get the current parameters */ + params = pps->params; + + spin_unlock_irq(&pps->lock); + + err = copy_to_user(uarg, ¶ms, sizeof(struct pps_kparams)); if (err) return -EFAULT; diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig index 2dc42bbf6fe9..bcbb161bde0b 100644 --- a/drivers/regulator/Kconfig +++ b/drivers/regulator/Kconfig @@ -1,6 +1,5 @@ menuconfig REGULATOR bool "Voltage and Current Regulator Support" - default n help Generic Voltage and Current Regulator support. @@ -30,7 +29,6 @@ config REGULATOR_DEBUG config REGULATOR_FIXED_VOLTAGE tristate "Fixed voltage regulator support" - default n help This driver provides support for fixed voltage regulators, useful for systems which use a combination of software @@ -38,7 +36,6 @@ config REGULATOR_FIXED_VOLTAGE config REGULATOR_VIRTUAL_CONSUMER tristate "Virtual regulator consumer support" - default n help This driver provides a virtual consumer for the voltage and current regulator API which provides sysfs controls for @@ -49,17 +46,15 @@ config REGULATOR_VIRTUAL_CONSUMER config REGULATOR_USERSPACE_CONSUMER tristate "Userspace regulator consumer support" - default n help There are some classes of devices that are controlled entirely - from user space. Usersapce consumer driver provides ability to + from user space. Userspace consumer driver provides ability to control power supplies for such devices. If unsure, say no. config REGULATOR_BQ24022 tristate "TI bq24022 Dual Input 1-Cell Li-Ion Charger IC" - default n help This driver controls a TI bq24022 Charger attached via GPIOs. The provided current regulator can enable/disable @@ -69,7 +64,6 @@ config REGULATOR_BQ24022 config REGULATOR_MAX1586 tristate "Maxim 1586/1587 voltage regulator" depends on I2C - default n help This driver controls a Maxim 1586 or 1587 voltage output regulator via I2C bus. The provided regulator is suitable @@ -147,5 +141,21 @@ config REGULATOR_AB3100 AB3100 analog baseband dealing with power regulators for the system. +config REGULATOR_TPS65023 + tristate "TI TPS65023 Power regulators" + depends on I2C + help + This driver supports TPS65023 voltage regulator chips. TPS65023 provides + three step-down converters and two general-purpose LDO voltage regulators. + It supports TI's software based Class-2 SmartReflex implementation. + +config REGULATOR_TPS6507X + tristate "TI TPS6507X Power regulators" + depends on I2C + help + This driver supports TPS6507X voltage regulator chips. TPS6507X provides + three step-down converters and two general-purpose LDO voltage regulators. + It supports TI's software based Class-2 SmartReflex implementation. + endif diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile index 768b3316d6eb..4257a8683778 100644 --- a/drivers/regulator/Makefile +++ b/drivers/regulator/Makefile @@ -23,4 +23,7 @@ obj-$(CONFIG_REGULATOR_PCAP) += pcap-regulator.o obj-$(CONFIG_REGULATOR_MC13783) += mc13783.o obj-$(CONFIG_REGULATOR_AB3100) += ab3100.o +obj-$(CONFIG_REGULATOR_TPS65023) += tps65023-regulator.o +obj-$(CONFIG_REGULATOR_TPS6507X) += tps6507x-regulator.o + ccflags-$(CONFIG_REGULATOR_DEBUG) += -DDEBUG diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index 91ba9bfaa706..efe568deda12 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -37,7 +37,7 @@ static int has_full_constraints; */ struct regulator_map { struct list_head list; - struct device *dev; + const char *dev_name; /* The dev_name() for the consumer */ const char *supply; struct regulator_dev *regulator; }; @@ -232,7 +232,7 @@ static ssize_t regulator_name_show(struct device *dev, struct regulator_dev *rdev = dev_get_drvdata(dev); const char *name; - if (rdev->constraints->name) + if (rdev->constraints && rdev->constraints->name) name = rdev->constraints->name; else if (rdev->desc->name) name = rdev->desc->name; @@ -280,8 +280,13 @@ static ssize_t regulator_state_show(struct device *dev, struct device_attribute *attr, char *buf) { struct regulator_dev *rdev = dev_get_drvdata(dev); + ssize_t ret; + + mutex_lock(&rdev->mutex); + ret = regulator_print_state(buf, _regulator_is_enabled(rdev)); + mutex_unlock(&rdev->mutex); - return regulator_print_state(buf, _regulator_is_enabled(rdev)); + return ret; } static DEVICE_ATTR(state, 0444, regulator_state_show, NULL); @@ -857,23 +862,39 @@ out: * set_consumer_device_supply: Bind a regulator to a symbolic supply * @rdev: regulator source * @consumer_dev: device the supply applies to + * @consumer_dev_name: dev_name() string for device supply applies to * @supply: symbolic name for supply * * Allows platform initialisation code to map physical regulator * sources to symbolic names for supplies for use by devices. Devices * should use these symbolic names to request regulators, avoiding the * need to provide board-specific regulator names as platform data. + * + * Only one of consumer_dev and consumer_dev_name may be specified. */ static int set_consumer_device_supply(struct regulator_dev *rdev, - struct device *consumer_dev, const char *supply) + struct device *consumer_dev, const char *consumer_dev_name, + const char *supply) { struct regulator_map *node; + int has_dev; + + if (consumer_dev && consumer_dev_name) + return -EINVAL; + + if (!consumer_dev_name && consumer_dev) + consumer_dev_name = dev_name(consumer_dev); if (supply == NULL) return -EINVAL; + if (consumer_dev_name != NULL) + has_dev = 1; + else + has_dev = 0; + list_for_each_entry(node, ®ulator_map_list, list) { - if (consumer_dev != node->dev) + if (consumer_dev_name != node->dev_name) continue; if (strcmp(node->supply, supply) != 0) continue; @@ -886,30 +907,45 @@ static int set_consumer_device_supply(struct regulator_dev *rdev, return -EBUSY; } - node = kmalloc(sizeof(struct regulator_map), GFP_KERNEL); + node = kzalloc(sizeof(struct regulator_map), GFP_KERNEL); if (node == NULL) return -ENOMEM; node->regulator = rdev; - node->dev = consumer_dev; node->supply = supply; + if (has_dev) { + node->dev_name = kstrdup(consumer_dev_name, GFP_KERNEL); + if (node->dev_name == NULL) { + kfree(node); + return -ENOMEM; + } + } + list_add(&node->list, ®ulator_map_list); return 0; } static void unset_consumer_device_supply(struct regulator_dev *rdev, - struct device *consumer_dev) + const char *consumer_dev_name, struct device *consumer_dev) { struct regulator_map *node, *n; + if (consumer_dev && !consumer_dev_name) + consumer_dev_name = dev_name(consumer_dev); + list_for_each_entry_safe(node, n, ®ulator_map_list, list) { - if (rdev == node->regulator && - consumer_dev == node->dev) { - list_del(&node->list); - kfree(node); - return; - } + if (rdev != node->regulator) + continue; + + if (consumer_dev_name && node->dev_name && + strcmp(consumer_dev_name, node->dev_name)) + continue; + + list_del(&node->list); + kfree(node->dev_name); + kfree(node); + return; } } @@ -920,6 +956,7 @@ static void unset_regulator_supplies(struct regulator_dev *rdev) list_for_each_entry_safe(node, n, ®ulator_map_list, list) { if (rdev == node->regulator) { list_del(&node->list); + kfree(node->dev_name); kfree(node); return; } @@ -1001,35 +1038,33 @@ overflow_err: return NULL; } -/** - * regulator_get - lookup and obtain a reference to a regulator. - * @dev: device for regulator "consumer" - * @id: Supply name or regulator ID. - * - * Returns a struct regulator corresponding to the regulator producer, - * or IS_ERR() condition containing errno. - * - * Use of supply names configured via regulator_set_device_supply() is - * strongly encouraged. It is recommended that the supply name used - * should match the name used for the supply and/or the relevant - * device pins in the datasheet. - */ -struct regulator *regulator_get(struct device *dev, const char *id) +/* Internal regulator request function */ +static struct regulator *_regulator_get(struct device *dev, const char *id, + int exclusive) { struct regulator_dev *rdev; struct regulator_map *map; struct regulator *regulator = ERR_PTR(-ENODEV); + const char *devname = NULL; + int ret; if (id == NULL) { printk(KERN_ERR "regulator: get() with no identifier\n"); return regulator; } + if (dev) + devname = dev_name(dev); + mutex_lock(®ulator_list_mutex); list_for_each_entry(map, ®ulator_map_list, list) { - if (dev == map->dev && - strcmp(map->supply, id) == 0) { + /* If the mapping has a device set up it must match */ + if (map->dev_name && + (!devname || strcmp(map->dev_name, devname))) + continue; + + if (strcmp(map->supply, id) == 0) { rdev = map->regulator; goto found; } @@ -1038,6 +1073,16 @@ struct regulator *regulator_get(struct device *dev, const char *id) return regulator; found: + if (rdev->exclusive) { + regulator = ERR_PTR(-EPERM); + goto out; + } + + if (exclusive && rdev->open_count) { + regulator = ERR_PTR(-EBUSY); + goto out; + } + if (!try_module_get(rdev->owner)) goto out; @@ -1047,13 +1092,70 @@ found: module_put(rdev->owner); } + rdev->open_count++; + if (exclusive) { + rdev->exclusive = 1; + + ret = _regulator_is_enabled(rdev); + if (ret > 0) + rdev->use_count = 1; + else + rdev->use_count = 0; + } + out: mutex_unlock(®ulator_list_mutex); + return regulator; } + +/** + * regulator_get - lookup and obtain a reference to a regulator. + * @dev: device for regulator "consumer" + * @id: Supply name or regulator ID. + * + * Returns a struct regulator corresponding to the regulator producer, + * or IS_ERR() condition containing errno. + * + * Use of supply names configured via regulator_set_device_supply() is + * strongly encouraged. It is recommended that the supply name used + * should match the name used for the supply and/or the relevant + * device pins in the datasheet. + */ +struct regulator *regulator_get(struct device *dev, const char *id) +{ + return _regulator_get(dev, id, 0); +} EXPORT_SYMBOL_GPL(regulator_get); /** + * regulator_get_exclusive - obtain exclusive access to a regulator. + * @dev: device for regulator "consumer" + * @id: Supply name or regulator ID. + * + * Returns a struct regulator corresponding to the regulator producer, + * or IS_ERR() condition containing errno. Other consumers will be + * unable to obtain this reference is held and the use count for the + * regulator will be initialised to reflect the current state of the + * regulator. + * + * This is intended for use by consumers which cannot tolerate shared + * use of the regulator such as those which need to force the + * regulator off for correct operation of the hardware they are + * controlling. + * + * Use of supply names configured via regulator_set_device_supply() is + * strongly encouraged. It is recommended that the supply name used + * should match the name used for the supply and/or the relevant + * device pins in the datasheet. + */ +struct regulator *regulator_get_exclusive(struct device *dev, const char *id) +{ + return _regulator_get(dev, id, 1); +} +EXPORT_SYMBOL_GPL(regulator_get_exclusive); + +/** * regulator_put - "free" the regulator source * @regulator: regulator source * @@ -1081,21 +1183,29 @@ void regulator_put(struct regulator *regulator) list_del(®ulator->list); kfree(regulator); + rdev->open_count--; + rdev->exclusive = 0; + module_put(rdev->owner); mutex_unlock(®ulator_list_mutex); } EXPORT_SYMBOL_GPL(regulator_put); +static int _regulator_can_change_status(struct regulator_dev *rdev) +{ + if (!rdev->constraints) + return 0; + + if (rdev->constraints->valid_ops_mask & REGULATOR_CHANGE_STATUS) + return 1; + else + return 0; +} + /* locks held by regulator_enable() */ static int _regulator_enable(struct regulator_dev *rdev) { - int ret = -EINVAL; - - if (!rdev->constraints) { - printk(KERN_ERR "%s: %s has no constraints\n", - __func__, rdev->desc->name); - return ret; - } + int ret; /* do we need to enable the supply regulator first */ if (rdev->supply) { @@ -1108,24 +1218,35 @@ static int _regulator_enable(struct regulator_dev *rdev) } /* check voltage and requested load before enabling */ - if (rdev->desc->ops->enable) { - - if (rdev->constraints && - (rdev->constraints->valid_ops_mask & - REGULATOR_CHANGE_DRMS)) - drms_uA_update(rdev); - - ret = rdev->desc->ops->enable(rdev); - if (ret < 0) { - printk(KERN_ERR "%s: failed to enable %s: %d\n", + if (rdev->constraints && + (rdev->constraints->valid_ops_mask & REGULATOR_CHANGE_DRMS)) + drms_uA_update(rdev); + + if (rdev->use_count == 0) { + /* The regulator may on if it's not switchable or left on */ + ret = _regulator_is_enabled(rdev); + if (ret == -EINVAL || ret == 0) { + if (!_regulator_can_change_status(rdev)) + return -EPERM; + + if (rdev->desc->ops->enable) { + ret = rdev->desc->ops->enable(rdev); + if (ret < 0) + return ret; + } else { + return -EINVAL; + } + } else if (ret < 0) { + printk(KERN_ERR "%s: is_enabled() failed for %s: %d\n", __func__, rdev->desc->name, ret); return ret; } - rdev->use_count++; - return ret; + /* Fallthrough on positive return values - already enabled */ } - return ret; + rdev->use_count++; + + return 0; } /** @@ -1162,10 +1283,12 @@ static int _regulator_disable(struct regulator_dev *rdev) return -EIO; /* are we the last user and permitted to disable ? */ - if (rdev->use_count == 1 && !rdev->constraints->always_on) { + if (rdev->use_count == 1 && + (rdev->constraints && !rdev->constraints->always_on)) { /* we are last user */ - if (rdev->desc->ops->disable) { + if (_regulator_can_change_status(rdev) && + rdev->desc->ops->disable) { ret = rdev->desc->ops->disable(rdev); if (ret < 0) { printk(KERN_ERR "%s: failed to disable %s\n", @@ -1265,20 +1388,11 @@ EXPORT_SYMBOL_GPL(regulator_force_disable); static int _regulator_is_enabled(struct regulator_dev *rdev) { - int ret; - - mutex_lock(&rdev->mutex); - /* sanity check */ - if (!rdev->desc->ops->is_enabled) { - ret = -EINVAL; - goto out; - } + if (!rdev->desc->ops->is_enabled) + return -EINVAL; - ret = rdev->desc->ops->is_enabled(rdev); -out: - mutex_unlock(&rdev->mutex); - return ret; + return rdev->desc->ops->is_enabled(rdev); } /** @@ -1295,7 +1409,13 @@ out: */ int regulator_is_enabled(struct regulator *regulator) { - return _regulator_is_enabled(regulator->rdev); + int ret; + + mutex_lock(®ulator->rdev->mutex); + ret = _regulator_is_enabled(regulator->rdev); + mutex_unlock(®ulator->rdev->mutex); + + return ret; } EXPORT_SYMBOL_GPL(regulator_is_enabled); @@ -1350,6 +1470,35 @@ int regulator_list_voltage(struct regulator *regulator, unsigned selector) EXPORT_SYMBOL_GPL(regulator_list_voltage); /** + * regulator_is_supported_voltage - check if a voltage range can be supported + * + * @regulator: Regulator to check. + * @min_uV: Minimum required voltage in uV. + * @max_uV: Maximum required voltage in uV. + * + * Returns a boolean or a negative error code. + */ +int regulator_is_supported_voltage(struct regulator *regulator, + int min_uV, int max_uV) +{ + int i, voltages, ret; + + ret = regulator_count_voltages(regulator); + if (ret < 0) + return ret; + voltages = ret; + + for (i = 0; i < voltages; i++) { + ret = regulator_list_voltage(regulator, i); + + if (ret >= min_uV && ret <= max_uV) + return 1; + } + + return 0; +} + +/** * regulator_set_voltage - set regulator output voltage * @regulator: regulator source * @min_uV: Minimum required voltage in uV @@ -2091,11 +2240,13 @@ struct regulator_dev *regulator_register(struct regulator_desc *regulator_desc, for (i = 0; i < init_data->num_consumer_supplies; i++) { ret = set_consumer_device_supply(rdev, init_data->consumer_supplies[i].dev, + init_data->consumer_supplies[i].dev_name, init_data->consumer_supplies[i].supply); if (ret < 0) { for (--i; i >= 0; i--) unset_consumer_device_supply(rdev, - init_data->consumer_supplies[i].dev); + init_data->consumer_supplies[i].dev_name, + init_data->consumer_supplies[i].dev); goto scrub; } } @@ -2130,6 +2281,7 @@ void regulator_unregister(struct regulator_dev *rdev) return; mutex_lock(®ulator_list_mutex); + WARN_ON(rdev->open_count); unset_regulator_supplies(rdev); list_del(&rdev->list); if (rdev->supply) @@ -2277,14 +2429,14 @@ static int __init regulator_init_complete(void) ops = rdev->desc->ops; c = rdev->constraints; - if (c->name) + if (c && c->name) name = c->name; else if (rdev->desc->name) name = rdev->desc->name; else name = "regulator"; - if (!ops->disable || c->always_on) + if (!ops->disable || (c && c->always_on)) continue; mutex_lock(&rdev->mutex); diff --git a/drivers/regulator/da903x.c b/drivers/regulator/da903x.c index b8b89ef10a84..aa224d936e0d 100644 --- a/drivers/regulator/da903x.c +++ b/drivers/regulator/da903x.c @@ -64,6 +64,14 @@ #define DA9034_MDTV2 (0x33) #define DA9034_MVRC (0x34) +/* DA9035 Registers. DA9034 Registers are comptabile to DA9035. */ +#define DA9035_OVER3 (0x12) +#define DA9035_VCC2 (0x1f) +#define DA9035_3DTV1 (0x2c) +#define DA9035_3DTV2 (0x2d) +#define DA9035_3VRC (0x2e) +#define DA9035_AUTOSKIP (0x2f) + struct da903x_regulator_info { struct regulator_desc desc; @@ -79,6 +87,10 @@ struct da903x_regulator_info { int enable_bit; }; +static int da9034_ldo12_data[] = { 1700, 1750, 1800, 1850, 1900, 1950, + 2000, 2050, 2700, 2750, 2800, 2850, + 2900, 2950, 3000, 3050 }; + static inline struct device *to_da903x_dev(struct regulator_dev *rdev) { return rdev_get_dev(rdev)->parent->parent; @@ -162,6 +174,17 @@ static int da903x_is_enabled(struct regulator_dev *rdev) return !!(reg_val & (1 << info->enable_bit)); } +static int da903x_list_voltage(struct regulator_dev *rdev, unsigned selector) +{ + struct da903x_regulator_info *info = rdev_get_drvdata(rdev); + int ret; + + ret = info->min_uV + info->step_uV * selector; + if (ret > info->max_uV) + return -EINVAL; + return ret; +} + /* DA9030 specific operations */ static int da9030_set_ldo1_15_voltage(struct regulator_dev *rdev, int min_uV, int max_uV) @@ -278,7 +301,7 @@ static int da9034_set_ldo12_voltage(struct regulator_dev *rdev, } val = (min_uV - info->min_uV + info->step_uV - 1) / info->step_uV; - val = (val > 7 || val < 20) ? 8 : val - 12; + val = (val >= 20) ? val - 12 : ((val > 7) ? 8 : val); val <<= info->vol_shift; mask = ((1 << info->vol_nbits) - 1) << info->vol_shift; @@ -305,9 +328,18 @@ static int da9034_get_ldo12_voltage(struct regulator_dev *rdev) return info->min_uV + info->step_uV * val; } +static int da9034_list_ldo12_voltage(struct regulator_dev *rdev, + unsigned selector) +{ + if (selector > ARRAY_SIZE(da9034_ldo12_data)) + return -EINVAL; + return da9034_ldo12_data[selector] * 1000; +} + static struct regulator_ops da903x_regulator_ldo_ops = { .set_voltage = da903x_set_ldo_voltage, .get_voltage = da903x_get_voltage, + .list_voltage = da903x_list_voltage, .enable = da903x_enable, .disable = da903x_disable, .is_enabled = da903x_is_enabled, @@ -317,6 +349,7 @@ static struct regulator_ops da903x_regulator_ldo_ops = { static struct regulator_ops da9030_regulator_ldo14_ops = { .set_voltage = da9030_set_ldo14_voltage, .get_voltage = da9030_get_ldo14_voltage, + .list_voltage = da903x_list_voltage, .enable = da903x_enable, .disable = da903x_disable, .is_enabled = da903x_is_enabled, @@ -326,6 +359,7 @@ static struct regulator_ops da9030_regulator_ldo14_ops = { static struct regulator_ops da9030_regulator_ldo1_15_ops = { .set_voltage = da9030_set_ldo1_15_voltage, .get_voltage = da903x_get_voltage, + .list_voltage = da903x_list_voltage, .enable = da903x_enable, .disable = da903x_disable, .is_enabled = da903x_is_enabled, @@ -334,6 +368,7 @@ static struct regulator_ops da9030_regulator_ldo1_15_ops = { static struct regulator_ops da9034_regulator_dvc_ops = { .set_voltage = da9034_set_dvc_voltage, .get_voltage = da903x_get_voltage, + .list_voltage = da903x_list_voltage, .enable = da903x_enable, .disable = da903x_disable, .is_enabled = da903x_is_enabled, @@ -343,6 +378,7 @@ static struct regulator_ops da9034_regulator_dvc_ops = { static struct regulator_ops da9034_regulator_ldo12_ops = { .set_voltage = da9034_set_ldo12_voltage, .get_voltage = da9034_get_ldo12_voltage, + .list_voltage = da9034_list_ldo12_voltage, .enable = da903x_enable, .disable = da903x_disable, .is_enabled = da903x_is_enabled, @@ -355,6 +391,7 @@ static struct regulator_ops da9034_regulator_ldo12_ops = { .ops = &da903x_regulator_ldo_ops, \ .type = REGULATOR_VOLTAGE, \ .id = _pmic##_ID_LDO##_id, \ + .n_voltages = (step) ? ((max - min) / step + 1) : 1, \ .owner = THIS_MODULE, \ }, \ .min_uV = (min) * 1000, \ @@ -367,24 +404,25 @@ static struct regulator_ops da9034_regulator_ldo12_ops = { .enable_bit = (ebit), \ } -#define DA9034_DVC(_id, min, max, step, vreg, nbits, ureg, ubit, ereg, ebit) \ +#define DA903x_DVC(_pmic, _id, min, max, step, vreg, nbits, ureg, ubit, ereg, ebit) \ { \ .desc = { \ .name = #_id, \ .ops = &da9034_regulator_dvc_ops, \ .type = REGULATOR_VOLTAGE, \ - .id = DA9034_ID_##_id, \ + .id = _pmic##_ID_##_id, \ + .n_voltages = (step) ? ((max - min) / step + 1) : 1, \ .owner = THIS_MODULE, \ }, \ .min_uV = (min) * 1000, \ .max_uV = (max) * 1000, \ .step_uV = (step) * 1000, \ - .vol_reg = DA9034_##vreg, \ + .vol_reg = _pmic##_##vreg, \ .vol_shift = (0), \ .vol_nbits = (nbits), \ - .update_reg = DA9034_##ureg, \ + .update_reg = _pmic##_##ureg, \ .update_bit = (ubit), \ - .enable_reg = DA9034_##ereg, \ + .enable_reg = _pmic##_##ereg, \ .enable_bit = (ebit), \ } @@ -394,8 +432,22 @@ static struct regulator_ops da9034_regulator_ldo12_ops = { #define DA9030_LDO(_id, min, max, step, vreg, shift, nbits, ereg, ebit) \ DA903x_LDO(DA9030, _id, min, max, step, vreg, shift, nbits, ereg, ebit) +#define DA9030_DVC(_id, min, max, step, vreg, nbits, ureg, ubit, ereg, ebit) \ + DA903x_DVC(DA9030, _id, min, max, step, vreg, nbits, ureg, ubit, \ + ereg, ebit) + +#define DA9034_DVC(_id, min, max, step, vreg, nbits, ureg, ubit, ereg, ebit) \ + DA903x_DVC(DA9034, _id, min, max, step, vreg, nbits, ureg, ubit, \ + ereg, ebit) + +#define DA9035_DVC(_id, min, max, step, vreg, nbits, ureg, ubit, ereg, ebit) \ + DA903x_DVC(DA9035, _id, min, max, step, vreg, nbits, ureg, ubit, \ + ereg, ebit) + static struct da903x_regulator_info da903x_regulator_info[] = { /* DA9030 */ + DA9030_DVC(BUCK2, 850, 1625, 25, BUCK2DVM1, 5, BUCK2DVM1, 7, RCTL11, 0), + DA9030_LDO( 1, 1200, 3200, 100, LDO1, 0, 5, RCTL12, 1), DA9030_LDO( 2, 1800, 3200, 100, LDO23, 0, 4, RCTL12, 2), DA9030_LDO( 3, 1800, 3200, 100, LDO23, 4, 4, RCTL12, 3), @@ -417,9 +469,9 @@ static struct da903x_regulator_info da903x_regulator_info[] = { DA9030_LDO(13, 2100, 2100, 0, INVAL, 0, 0, RCTL11, 3), /* fixed @2.1V */ /* DA9034 */ - DA9034_DVC(BUCK1, 725, 1500, 25, ADTV1, 5, VCC1, 0, OVER1, 0), - DA9034_DVC(BUCK2, 725, 1500, 25, CDTV1, 5, VCC1, 2, OVER1, 1), - DA9034_DVC(LDO2, 725, 1500, 25, SDTV1, 5, VCC1, 4, OVER1, 2), + DA9034_DVC(BUCK1, 725, 1500, 25, ADTV2, 5, VCC1, 0, OVER1, 0), + DA9034_DVC(BUCK2, 725, 1500, 25, CDTV2, 5, VCC1, 2, OVER1, 1), + DA9034_DVC(LDO2, 725, 1500, 25, SDTV2, 5, VCC1, 4, OVER1, 2), DA9034_DVC(LDO1, 1700, 2075, 25, MDTV1, 4, VCC1, 6, OVER3, 4), DA9034_LDO( 3, 1800, 3300, 100, LDO643, 0, 4, OVER3, 5), @@ -435,6 +487,9 @@ static struct da903x_regulator_info da903x_regulator_info[] = { DA9034_LDO(14, 1800, 3300, 100, LDO1514, 0, 4, OVER3, 0), DA9034_LDO(15, 1800, 3300, 100, LDO1514, 4, 4, OVER3, 1), DA9034_LDO(5, 3100, 3100, 0, INVAL, 0, 0, OVER3, 7), /* fixed @3.1V */ + + /* DA9035 */ + DA9035_DVC(BUCK3, 1800, 2200, 100, 3DTV1, 3, VCC2, 0, OVER3, 3), }; static inline struct da903x_regulator_info *find_regulator_info(int id) @@ -462,8 +517,10 @@ static int __devinit da903x_regulator_probe(struct platform_device *pdev) } /* Workaround for the weird LDO12 voltage setting */ - if (ri->desc.id == DA9034_ID_LDO12) + if (ri->desc.id == DA9034_ID_LDO12) { ri->desc.ops = &da9034_regulator_ldo12_ops; + ri->desc.n_voltages = ARRAY_SIZE(da9034_ldo12_data); + } if (ri->desc.id == DA9030_ID_LDO14) ri->desc.ops = &da9030_regulator_ldo14_ops; diff --git a/drivers/regulator/fixed.c b/drivers/regulator/fixed.c index cdc674fb46c3..f9f516a3028a 100644 --- a/drivers/regulator/fixed.c +++ b/drivers/regulator/fixed.c @@ -5,6 +5,9 @@ * * Author: Mark Brown <broonie@opensource.wolfsonmicro.com> * + * Copyright (c) 2009 Nokia Corporation + * Roger Quadros <ext-roger.quadros@nokia.com> + * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the @@ -20,20 +23,45 @@ #include <linux/platform_device.h> #include <linux/regulator/driver.h> #include <linux/regulator/fixed.h> +#include <linux/gpio.h> struct fixed_voltage_data { struct regulator_desc desc; struct regulator_dev *dev; int microvolts; + int gpio; + unsigned enable_high:1; + unsigned is_enabled:1; }; static int fixed_voltage_is_enabled(struct regulator_dev *dev) { - return 1; + struct fixed_voltage_data *data = rdev_get_drvdata(dev); + + return data->is_enabled; } static int fixed_voltage_enable(struct regulator_dev *dev) { + struct fixed_voltage_data *data = rdev_get_drvdata(dev); + + if (gpio_is_valid(data->gpio)) { + gpio_set_value_cansleep(data->gpio, data->enable_high); + data->is_enabled = 1; + } + + return 0; +} + +static int fixed_voltage_disable(struct regulator_dev *dev) +{ + struct fixed_voltage_data *data = rdev_get_drvdata(dev); + + if (gpio_is_valid(data->gpio)) { + gpio_set_value_cansleep(data->gpio, !data->enable_high); + data->is_enabled = 0; + } + return 0; } @@ -58,6 +86,7 @@ static int fixed_voltage_list_voltage(struct regulator_dev *dev, static struct regulator_ops fixed_voltage_ops = { .is_enabled = fixed_voltage_is_enabled, .enable = fixed_voltage_enable, + .disable = fixed_voltage_disable, .get_voltage = fixed_voltage_get_voltage, .list_voltage = fixed_voltage_list_voltage, }; @@ -70,12 +99,14 @@ static int regulator_fixed_voltage_probe(struct platform_device *pdev) drvdata = kzalloc(sizeof(struct fixed_voltage_data), GFP_KERNEL); if (drvdata == NULL) { + dev_err(&pdev->dev, "Failed to allocate device data\n"); ret = -ENOMEM; goto err; } drvdata->desc.name = kstrdup(config->supply_name, GFP_KERNEL); if (drvdata->desc.name == NULL) { + dev_err(&pdev->dev, "Failed to allocate supply name\n"); ret = -ENOMEM; goto err; } @@ -85,12 +116,62 @@ static int regulator_fixed_voltage_probe(struct platform_device *pdev) drvdata->desc.n_voltages = 1; drvdata->microvolts = config->microvolts; + drvdata->gpio = config->gpio; + + if (gpio_is_valid(config->gpio)) { + drvdata->enable_high = config->enable_high; + + /* FIXME: Remove below print warning + * + * config->gpio must be set to -EINVAL by platform code if + * GPIO control is not required. However, early adopters + * not requiring GPIO control may forget to initialize + * config->gpio to -EINVAL. This will cause GPIO 0 to be used + * for GPIO control. + * + * This warning will be removed once there are a couple of users + * for this driver. + */ + if (!config->gpio) + dev_warn(&pdev->dev, + "using GPIO 0 for regulator enable control\n"); + + ret = gpio_request(config->gpio, config->supply_name); + if (ret) { + dev_err(&pdev->dev, + "Could not obtain regulator enable GPIO %d: %d\n", + config->gpio, ret); + goto err_name; + } + + /* set output direction without changing state + * to prevent glitch + */ + drvdata->is_enabled = config->enabled_at_boot; + ret = drvdata->is_enabled ? + config->enable_high : !config->enable_high; + + ret = gpio_direction_output(config->gpio, ret); + if (ret) { + dev_err(&pdev->dev, + "Could not configure regulator enable GPIO %d direction: %d\n", + config->gpio, ret); + goto err_gpio; + } + + } else { + /* Regulator without GPIO control is considered + * always enabled + */ + drvdata->is_enabled = 1; + } drvdata->dev = regulator_register(&drvdata->desc, &pdev->dev, config->init_data, drvdata); if (IS_ERR(drvdata->dev)) { ret = PTR_ERR(drvdata->dev); - goto err_name; + dev_err(&pdev->dev, "Failed to register regulator: %d\n", ret); + goto err_gpio; } platform_set_drvdata(pdev, drvdata); @@ -100,6 +181,9 @@ static int regulator_fixed_voltage_probe(struct platform_device *pdev) return 0; +err_gpio: + if (gpio_is_valid(config->gpio)) + gpio_free(config->gpio); err_name: kfree(drvdata->desc.name); err: @@ -112,6 +196,8 @@ static int regulator_fixed_voltage_remove(struct platform_device *pdev) struct fixed_voltage_data *drvdata = platform_get_drvdata(pdev); regulator_unregister(drvdata->dev); + if (gpio_is_valid(drvdata->gpio)) + gpio_free(drvdata->gpio); kfree(drvdata->desc.name); kfree(drvdata); diff --git a/drivers/regulator/lp3971.c b/drivers/regulator/lp3971.c index a61018a27698..7803a320543b 100644 --- a/drivers/regulator/lp3971.c +++ b/drivers/regulator/lp3971.c @@ -541,7 +541,7 @@ static struct i2c_driver lp3971_i2c_driver = { static int __init lp3971_module_init(void) { - int ret = -ENODEV; + int ret; ret = i2c_add_driver(&lp3971_i2c_driver); if (ret != 0) diff --git a/drivers/regulator/pcf50633-regulator.c b/drivers/regulator/pcf50633-regulator.c index 8e14900eb686..0803ffe6236d 100644 --- a/drivers/regulator/pcf50633-regulator.c +++ b/drivers/regulator/pcf50633-regulator.c @@ -24,11 +24,12 @@ #include <linux/mfd/pcf50633/core.h> #include <linux/mfd/pcf50633/pmic.h> -#define PCF50633_REGULATOR(_name, _id) \ +#define PCF50633_REGULATOR(_name, _id, _n) \ { \ .name = _name, \ .id = _id, \ .ops = &pcf50633_regulator_ops, \ + .n_voltages = _n, \ .type = REGULATOR_VOLTAGE, \ .owner = THIS_MODULE, \ } @@ -149,33 +150,20 @@ static int pcf50633_regulator_set_voltage(struct regulator_dev *rdev, return pcf50633_reg_write(pcf, regnr, volt_bits); } -static int pcf50633_regulator_get_voltage(struct regulator_dev *rdev) +static int pcf50633_regulator_voltage_value(enum pcf50633_regulator_id id, + u8 bits) { - struct pcf50633 *pcf; - int regulator_id, millivolts, volt_bits; - u8 regnr; - - pcf = rdev_get_drvdata(rdev);; + int millivolts; - regulator_id = rdev_get_id(rdev); - if (regulator_id >= PCF50633_NUM_REGULATORS) - return -EINVAL; - - regnr = pcf50633_regulator_registers[regulator_id]; - - volt_bits = pcf50633_reg_read(pcf, regnr); - if (volt_bits < 0) - return -1; - - switch (regulator_id) { + switch (id) { case PCF50633_REGULATOR_AUTO: - millivolts = auto_voltage_value(volt_bits); + millivolts = auto_voltage_value(bits); break; case PCF50633_REGULATOR_DOWN1: - millivolts = down_voltage_value(volt_bits); + millivolts = down_voltage_value(bits); break; case PCF50633_REGULATOR_DOWN2: - millivolts = down_voltage_value(volt_bits); + millivolts = down_voltage_value(bits); break; case PCF50633_REGULATOR_LDO1: case PCF50633_REGULATOR_LDO2: @@ -184,7 +172,7 @@ static int pcf50633_regulator_get_voltage(struct regulator_dev *rdev) case PCF50633_REGULATOR_LDO5: case PCF50633_REGULATOR_LDO6: case PCF50633_REGULATOR_HCLDO: - millivolts = ldo_voltage_value(volt_bits); + millivolts = ldo_voltage_value(bits); break; default: return -EINVAL; @@ -193,6 +181,49 @@ static int pcf50633_regulator_get_voltage(struct regulator_dev *rdev) return millivolts * 1000; } +static int pcf50633_regulator_get_voltage(struct regulator_dev *rdev) +{ + struct pcf50633 *pcf; + int regulator_id; + u8 volt_bits, regnr; + + pcf = rdev_get_drvdata(rdev); + + regulator_id = rdev_get_id(rdev); + if (regulator_id >= PCF50633_NUM_REGULATORS) + return -EINVAL; + + regnr = pcf50633_regulator_registers[regulator_id]; + + volt_bits = pcf50633_reg_read(pcf, regnr); + + return pcf50633_regulator_voltage_value(regulator_id, volt_bits); +} + +static int pcf50633_regulator_list_voltage(struct regulator_dev *rdev, + unsigned int index) +{ + struct pcf50633 *pcf; + int regulator_id; + + pcf = rdev_get_drvdata(rdev); + + regulator_id = rdev_get_id(rdev); + + switch (regulator_id) { + case PCF50633_REGULATOR_AUTO: + index += 0x2f; + break; + case PCF50633_REGULATOR_HCLDO: + index += 0x01; + break; + default: + break; + } + + return pcf50633_regulator_voltage_value(regulator_id, index); +} + static int pcf50633_regulator_enable(struct regulator_dev *rdev) { struct pcf50633 *pcf = rdev_get_drvdata(rdev); @@ -246,6 +277,7 @@ static int pcf50633_regulator_is_enabled(struct regulator_dev *rdev) static struct regulator_ops pcf50633_regulator_ops = { .set_voltage = pcf50633_regulator_set_voltage, .get_voltage = pcf50633_regulator_get_voltage, + .list_voltage = pcf50633_regulator_list_voltage, .enable = pcf50633_regulator_enable, .disable = pcf50633_regulator_disable, .is_enabled = pcf50633_regulator_is_enabled, @@ -253,27 +285,27 @@ static struct regulator_ops pcf50633_regulator_ops = { static struct regulator_desc regulators[] = { [PCF50633_REGULATOR_AUTO] = - PCF50633_REGULATOR("auto", PCF50633_REGULATOR_AUTO), + PCF50633_REGULATOR("auto", PCF50633_REGULATOR_AUTO, 80), [PCF50633_REGULATOR_DOWN1] = - PCF50633_REGULATOR("down1", PCF50633_REGULATOR_DOWN1), + PCF50633_REGULATOR("down1", PCF50633_REGULATOR_DOWN1, 95), [PCF50633_REGULATOR_DOWN2] = - PCF50633_REGULATOR("down2", PCF50633_REGULATOR_DOWN2), + PCF50633_REGULATOR("down2", PCF50633_REGULATOR_DOWN2, 95), [PCF50633_REGULATOR_LDO1] = - PCF50633_REGULATOR("ldo1", PCF50633_REGULATOR_LDO1), + PCF50633_REGULATOR("ldo1", PCF50633_REGULATOR_LDO1, 27), [PCF50633_REGULATOR_LDO2] = - PCF50633_REGULATOR("ldo2", PCF50633_REGULATOR_LDO2), + PCF50633_REGULATOR("ldo2", PCF50633_REGULATOR_LDO2, 27), [PCF50633_REGULATOR_LDO3] = - PCF50633_REGULATOR("ldo3", PCF50633_REGULATOR_LDO3), + PCF50633_REGULATOR("ldo3", PCF50633_REGULATOR_LDO3, 27), [PCF50633_REGULATOR_LDO4] = - PCF50633_REGULATOR("ldo4", PCF50633_REGULATOR_LDO4), + PCF50633_REGULATOR("ldo4", PCF50633_REGULATOR_LDO4, 27), [PCF50633_REGULATOR_LDO5] = - PCF50633_REGULATOR("ldo5", PCF50633_REGULATOR_LDO5), + PCF50633_REGULATOR("ldo5", PCF50633_REGULATOR_LDO5, 27), [PCF50633_REGULATOR_LDO6] = - PCF50633_REGULATOR("ldo6", PCF50633_REGULATOR_LDO6), + PCF50633_REGULATOR("ldo6", PCF50633_REGULATOR_LDO6, 27), [PCF50633_REGULATOR_HCLDO] = - PCF50633_REGULATOR("hcldo", PCF50633_REGULATOR_HCLDO), + PCF50633_REGULATOR("hcldo", PCF50633_REGULATOR_HCLDO, 26), [PCF50633_REGULATOR_MEMLDO] = - PCF50633_REGULATOR("memldo", PCF50633_REGULATOR_MEMLDO), + PCF50633_REGULATOR("memldo", PCF50633_REGULATOR_MEMLDO, 0), }; static int __devinit pcf50633_regulator_probe(struct platform_device *pdev) diff --git a/drivers/regulator/tps65023-regulator.c b/drivers/regulator/tps65023-regulator.c new file mode 100644 index 000000000000..07fda0a75adf --- /dev/null +++ b/drivers/regulator/tps65023-regulator.c @@ -0,0 +1,632 @@ +/* + * tps65023-regulator.c + * + * Supports TPS65023 Regulator + * + * Copyright (C) 2009 Texas Instrument Incorporated - http://www.ti.com/ + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation version 2. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any kind, + * whether express or implied; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/err.h> +#include <linux/platform_device.h> +#include <linux/regulator/driver.h> +#include <linux/regulator/machine.h> +#include <linux/i2c.h> +#include <linux/delay.h> + +/* Register definitions */ +#define TPS65023_REG_VERSION 0 +#define TPS65023_REG_PGOODZ 1 +#define TPS65023_REG_MASK 2 +#define TPS65023_REG_REG_CTRL 3 +#define TPS65023_REG_CON_CTRL 4 +#define TPS65023_REG_CON_CTRL2 5 +#define TPS65023_REG_DEF_CORE 6 +#define TPS65023_REG_DEFSLEW 7 +#define TPS65023_REG_LDO_CTRL 8 + +/* PGOODZ bitfields */ +#define TPS65023_PGOODZ_PWRFAILZ BIT(7) +#define TPS65023_PGOODZ_LOWBATTZ BIT(6) +#define TPS65023_PGOODZ_VDCDC1 BIT(5) +#define TPS65023_PGOODZ_VDCDC2 BIT(4) +#define TPS65023_PGOODZ_VDCDC3 BIT(3) +#define TPS65023_PGOODZ_LDO2 BIT(2) +#define TPS65023_PGOODZ_LDO1 BIT(1) + +/* MASK bitfields */ +#define TPS65023_MASK_PWRFAILZ BIT(7) +#define TPS65023_MASK_LOWBATTZ BIT(6) +#define TPS65023_MASK_VDCDC1 BIT(5) +#define TPS65023_MASK_VDCDC2 BIT(4) +#define TPS65023_MASK_VDCDC3 BIT(3) +#define TPS65023_MASK_LDO2 BIT(2) +#define TPS65023_MASK_LDO1 BIT(1) + +/* REG_CTRL bitfields */ +#define TPS65023_REG_CTRL_VDCDC1_EN BIT(5) +#define TPS65023_REG_CTRL_VDCDC2_EN BIT(4) +#define TPS65023_REG_CTRL_VDCDC3_EN BIT(3) +#define TPS65023_REG_CTRL_LDO2_EN BIT(2) +#define TPS65023_REG_CTRL_LDO1_EN BIT(1) + +/* LDO_CTRL bitfields */ +#define TPS65023_LDO_CTRL_LDOx_SHIFT(ldo_id) ((ldo_id)*4) +#define TPS65023_LDO_CTRL_LDOx_MASK(ldo_id) (0xF0 >> ((ldo_id)*4)) + +/* Number of step-down converters available */ +#define TPS65023_NUM_DCDC 3 +/* Number of LDO voltage regulators available */ +#define TPS65023_NUM_LDO 2 +/* Number of total regulators available */ +#define TPS65023_NUM_REGULATOR (TPS65023_NUM_DCDC + TPS65023_NUM_LDO) + +/* DCDCs */ +#define TPS65023_DCDC_1 0 +#define TPS65023_DCDC_2 1 +#define TPS65023_DCDC_3 2 +/* LDOs */ +#define TPS65023_LDO_1 3 +#define TPS65023_LDO_2 4 + +#define TPS65023_MAX_REG_ID TPS65023_LDO_2 + +/* Supported voltage values for regulators */ +static const u16 VDCDC1_VSEL_table[] = { + 800, 825, 850, 875, + 900, 925, 950, 975, + 1000, 1025, 1050, 1075, + 1100, 1125, 1150, 1175, + 1200, 1225, 1250, 1275, + 1300, 1325, 1350, 1375, + 1400, 1425, 1450, 1475, + 1500, 1525, 1550, 1600, +}; + +static const u16 LDO1_VSEL_table[] = { + 1000, 1100, 1300, 1800, + 2200, 2600, 2800, 3150, +}; + +static const u16 LDO2_VSEL_table[] = { + 1050, 1200, 1300, 1800, + 2500, 2800, 3000, 3300, +}; + +static unsigned int num_voltages[] = {ARRAY_SIZE(VDCDC1_VSEL_table), + 0, 0, ARRAY_SIZE(LDO1_VSEL_table), + ARRAY_SIZE(LDO2_VSEL_table)}; + +/* Regulator specific details */ +struct tps_info { + const char *name; + unsigned min_uV; + unsigned max_uV; + bool fixed; + u8 table_len; + const u16 *table; +}; + +/* PMIC details */ +struct tps_pmic { + struct regulator_desc desc[TPS65023_NUM_REGULATOR]; + struct i2c_client *client; + struct regulator_dev *rdev[TPS65023_NUM_REGULATOR]; + const struct tps_info *info[TPS65023_NUM_REGULATOR]; + struct mutex io_lock; +}; + +static inline int tps_65023_read(struct tps_pmic *tps, u8 reg) +{ + return i2c_smbus_read_byte_data(tps->client, reg); +} + +static inline int tps_65023_write(struct tps_pmic *tps, u8 reg, u8 val) +{ + return i2c_smbus_write_byte_data(tps->client, reg, val); +} + +static int tps_65023_set_bits(struct tps_pmic *tps, u8 reg, u8 mask) +{ + int err, data; + + mutex_lock(&tps->io_lock); + + data = tps_65023_read(tps, reg); + if (data < 0) { + dev_err(&tps->client->dev, "Read from reg 0x%x failed\n", reg); + err = data; + goto out; + } + + data |= mask; + err = tps_65023_write(tps, reg, data); + if (err) + dev_err(&tps->client->dev, "Write for reg 0x%x failed\n", reg); + +out: + mutex_unlock(&tps->io_lock); + return err; +} + +static int tps_65023_clear_bits(struct tps_pmic *tps, u8 reg, u8 mask) +{ + int err, data; + + mutex_lock(&tps->io_lock); + + data = tps_65023_read(tps, reg); + if (data < 0) { + dev_err(&tps->client->dev, "Read from reg 0x%x failed\n", reg); + err = data; + goto out; + } + + data &= ~mask; + + err = tps_65023_write(tps, reg, data); + if (err) + dev_err(&tps->client->dev, "Write for reg 0x%x failed\n", reg); + +out: + mutex_unlock(&tps->io_lock); + return err; + +} + +static int tps_65023_reg_read(struct tps_pmic *tps, u8 reg) +{ + int data; + + mutex_lock(&tps->io_lock); + + data = tps_65023_read(tps, reg); + if (data < 0) + dev_err(&tps->client->dev, "Read from reg 0x%x failed\n", reg); + + mutex_unlock(&tps->io_lock); + return data; +} + +static int tps_65023_reg_write(struct tps_pmic *tps, u8 reg, u8 val) +{ + int err; + + mutex_lock(&tps->io_lock); + + err = tps_65023_write(tps, reg, val); + if (err < 0) + dev_err(&tps->client->dev, "Write for reg 0x%x failed\n", reg); + + mutex_unlock(&tps->io_lock); + return err; +} + +static int tps65023_dcdc_is_enabled(struct regulator_dev *dev) +{ + struct tps_pmic *tps = rdev_get_drvdata(dev); + int data, dcdc = rdev_get_id(dev); + u8 shift; + + if (dcdc < TPS65023_DCDC_1 || dcdc > TPS65023_DCDC_3) + return -EINVAL; + + shift = TPS65023_NUM_REGULATOR - dcdc; + data = tps_65023_reg_read(tps, TPS65023_REG_REG_CTRL); + + if (data < 0) + return data; + else + return (data & 1<<shift) ? 1 : 0; +} + +static int tps65023_ldo_is_enabled(struct regulator_dev *dev) +{ + struct tps_pmic *tps = rdev_get_drvdata(dev); + int data, ldo = rdev_get_id(dev); + u8 shift; + + if (ldo < TPS65023_LDO_1 || ldo > TPS65023_LDO_2) + return -EINVAL; + + shift = (ldo == TPS65023_LDO_1 ? 1 : 2); + data = tps_65023_reg_read(tps, TPS65023_REG_REG_CTRL); + + if (data < 0) + return data; + else + return (data & 1<<shift) ? 1 : 0; +} + +static int tps65023_dcdc_enable(struct regulator_dev *dev) +{ + struct tps_pmic *tps = rdev_get_drvdata(dev); + int dcdc = rdev_get_id(dev); + u8 shift; + + if (dcdc < TPS65023_DCDC_1 || dcdc > TPS65023_DCDC_3) + return -EINVAL; + + shift = TPS65023_NUM_REGULATOR - dcdc; + return tps_65023_set_bits(tps, TPS65023_REG_REG_CTRL, 1 << shift); +} + +static int tps65023_dcdc_disable(struct regulator_dev *dev) +{ + struct tps_pmic *tps = rdev_get_drvdata(dev); + int dcdc = rdev_get_id(dev); + u8 shift; + + if (dcdc < TPS65023_DCDC_1 || dcdc > TPS65023_DCDC_3) + return -EINVAL; + + shift = TPS65023_NUM_REGULATOR - dcdc; + return tps_65023_clear_bits(tps, TPS65023_REG_REG_CTRL, 1 << shift); +} + +static int tps65023_ldo_enable(struct regulator_dev *dev) +{ + struct tps_pmic *tps = rdev_get_drvdata(dev); + int ldo = rdev_get_id(dev); + u8 shift; + + if (ldo < TPS65023_LDO_1 || ldo > TPS65023_LDO_2) + return -EINVAL; + + shift = (ldo == TPS65023_LDO_1 ? 1 : 2); + return tps_65023_set_bits(tps, TPS65023_REG_REG_CTRL, 1 << shift); +} + +static int tps65023_ldo_disable(struct regulator_dev *dev) +{ + struct tps_pmic *tps = rdev_get_drvdata(dev); + int ldo = rdev_get_id(dev); + u8 shift; + + if (ldo < TPS65023_LDO_1 || ldo > TPS65023_LDO_2) + return -EINVAL; + + shift = (ldo == TPS65023_LDO_1 ? 1 : 2); + return tps_65023_clear_bits(tps, TPS65023_REG_REG_CTRL, 1 << shift); +} + +static int tps65023_dcdc_get_voltage(struct regulator_dev *dev) +{ + struct tps_pmic *tps = rdev_get_drvdata(dev); + int data, dcdc = rdev_get_id(dev); + + if (dcdc < TPS65023_DCDC_1 || dcdc > TPS65023_DCDC_3) + return -EINVAL; + + if (dcdc == TPS65023_DCDC_1) { + data = tps_65023_reg_read(tps, TPS65023_REG_DEF_CORE); + if (data < 0) + return data; + data &= (tps->info[dcdc]->table_len - 1); + return tps->info[dcdc]->table[data] * 1000; + } else + return tps->info[dcdc]->min_uV; +} + +static int tps65023_dcdc_set_voltage(struct regulator_dev *dev, + int min_uV, int max_uV) +{ + struct tps_pmic *tps = rdev_get_drvdata(dev); + int dcdc = rdev_get_id(dev); + int vsel; + + if (dcdc != TPS65023_DCDC_1) + return -EINVAL; + + if (min_uV < tps->info[dcdc]->min_uV + || min_uV > tps->info[dcdc]->max_uV) + return -EINVAL; + if (max_uV < tps->info[dcdc]->min_uV + || max_uV > tps->info[dcdc]->max_uV) + return -EINVAL; + + for (vsel = 0; vsel < tps->info[dcdc]->table_len; vsel++) { + int mV = tps->info[dcdc]->table[vsel]; + int uV = mV * 1000; + + /* Break at the first in-range value */ + if (min_uV <= uV && uV <= max_uV) + break; + } + + /* write to the register in case we found a match */ + if (vsel == tps->info[dcdc]->table_len) + return -EINVAL; + else + return tps_65023_reg_write(tps, TPS65023_REG_DEF_CORE, vsel); +} + +static int tps65023_ldo_get_voltage(struct regulator_dev *dev) +{ + struct tps_pmic *tps = rdev_get_drvdata(dev); + int data, ldo = rdev_get_id(dev); + + if (ldo < TPS65023_LDO_1 || ldo > TPS65023_LDO_2) + return -EINVAL; + + data = tps_65023_reg_read(tps, TPS65023_REG_LDO_CTRL); + if (data < 0) + return data; + + data >>= (TPS65023_LDO_CTRL_LDOx_SHIFT(ldo - TPS65023_LDO_1)); + data &= (tps->info[ldo]->table_len - 1); + return tps->info[ldo]->table[data] * 1000; +} + +static int tps65023_ldo_set_voltage(struct regulator_dev *dev, + int min_uV, int max_uV) +{ + struct tps_pmic *tps = rdev_get_drvdata(dev); + int data, vsel, ldo = rdev_get_id(dev); + + if (ldo < TPS65023_LDO_1 || ldo > TPS65023_LDO_2) + return -EINVAL; + + if (min_uV < tps->info[ldo]->min_uV || min_uV > tps->info[ldo]->max_uV) + return -EINVAL; + if (max_uV < tps->info[ldo]->min_uV || max_uV > tps->info[ldo]->max_uV) + return -EINVAL; + + for (vsel = 0; vsel < tps->info[ldo]->table_len; vsel++) { + int mV = tps->info[ldo]->table[vsel]; + int uV = mV * 1000; + + /* Break at the first in-range value */ + if (min_uV <= uV && uV <= max_uV) + break; + } + + if (vsel == tps->info[ldo]->table_len) + return -EINVAL; + + data = tps_65023_reg_read(tps, TPS65023_REG_LDO_CTRL); + if (data < 0) + return data; + + data &= TPS65023_LDO_CTRL_LDOx_MASK(ldo - TPS65023_LDO_1); + data |= (vsel << (TPS65023_LDO_CTRL_LDOx_SHIFT(ldo - TPS65023_LDO_1))); + return tps_65023_reg_write(tps, TPS65023_REG_LDO_CTRL, data); +} + +static int tps65023_dcdc_list_voltage(struct regulator_dev *dev, + unsigned selector) +{ + struct tps_pmic *tps = rdev_get_drvdata(dev); + int dcdc = rdev_get_id(dev); + + if (dcdc < TPS65023_DCDC_1 || dcdc > TPS65023_DCDC_3) + return -EINVAL; + + if (dcdc == TPS65023_DCDC_1) { + if (selector >= tps->info[dcdc]->table_len) + return -EINVAL; + else + return tps->info[dcdc]->table[selector] * 1000; + } else + return tps->info[dcdc]->min_uV; +} + +static int tps65023_ldo_list_voltage(struct regulator_dev *dev, + unsigned selector) +{ + struct tps_pmic *tps = rdev_get_drvdata(dev); + int ldo = rdev_get_id(dev); + + if (ldo < TPS65023_LDO_1 || ldo > TPS65023_LDO_2) + return -EINVAL; + + if (selector >= tps->info[ldo]->table_len) + return -EINVAL; + else + return tps->info[ldo]->table[selector] * 1000; +} + +/* Operations permitted on VDCDCx */ +static struct regulator_ops tps65023_dcdc_ops = { + .is_enabled = tps65023_dcdc_is_enabled, + .enable = tps65023_dcdc_enable, + .disable = tps65023_dcdc_disable, + .get_voltage = tps65023_dcdc_get_voltage, + .set_voltage = tps65023_dcdc_set_voltage, + .list_voltage = tps65023_dcdc_list_voltage, +}; + +/* Operations permitted on LDOx */ +static struct regulator_ops tps65023_ldo_ops = { + .is_enabled = tps65023_ldo_is_enabled, + .enable = tps65023_ldo_enable, + .disable = tps65023_ldo_disable, + .get_voltage = tps65023_ldo_get_voltage, + .set_voltage = tps65023_ldo_set_voltage, + .list_voltage = tps65023_ldo_list_voltage, +}; + +static +int tps_65023_probe(struct i2c_client *client, const struct i2c_device_id *id) +{ + static int desc_id; + const struct tps_info *info = (void *)id->driver_data; + struct regulator_init_data *init_data; + struct regulator_dev *rdev; + struct tps_pmic *tps; + int i; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + return -EIO; + + /** + * init_data points to array of regulator_init structures + * coming from the board-evm file. + */ + init_data = client->dev.platform_data; + + if (!init_data) + return -EIO; + + tps = kzalloc(sizeof(*tps), GFP_KERNEL); + if (!tps) + return -ENOMEM; + + mutex_init(&tps->io_lock); + + /* common for all regulators */ + tps->client = client; + + for (i = 0; i < TPS65023_NUM_REGULATOR; i++, info++, init_data++) { + /* Store regulator specific information */ + tps->info[i] = info; + + tps->desc[i].name = info->name; + tps->desc[i].id = desc_id++; + tps->desc[i].n_voltages = num_voltages[i]; + tps->desc[i].ops = (i > TPS65023_DCDC_3 ? + &tps65023_ldo_ops : &tps65023_dcdc_ops); + tps->desc[i].type = REGULATOR_VOLTAGE; + tps->desc[i].owner = THIS_MODULE; + + /* Register the regulators */ + rdev = regulator_register(&tps->desc[i], &client->dev, + init_data, tps); + if (IS_ERR(rdev)) { + dev_err(&client->dev, "failed to register %s\n", + id->name); + + /* Unregister */ + while (i) + regulator_unregister(tps->rdev[--i]); + + tps->client = NULL; + + /* clear the client data in i2c */ + i2c_set_clientdata(client, NULL); + kfree(tps); + return PTR_ERR(rdev); + } + + /* Save regulator for cleanup */ + tps->rdev[i] = rdev; + } + + i2c_set_clientdata(client, tps); + + return 0; +} + +/** + * tps_65023_remove - TPS65023 driver i2c remove handler + * @client: i2c driver client device structure + * + * Unregister TPS driver as an i2c client device driver + */ +static int __devexit tps_65023_remove(struct i2c_client *client) +{ + struct tps_pmic *tps = i2c_get_clientdata(client); + int i; + + for (i = 0; i < TPS65023_NUM_REGULATOR; i++) + regulator_unregister(tps->rdev[i]); + + tps->client = NULL; + + /* clear the client data in i2c */ + i2c_set_clientdata(client, NULL); + kfree(tps); + + return 0; +} + +static const struct tps_info tps65023_regs[] = { + { + .name = "VDCDC1", + .min_uV = 800000, + .max_uV = 1600000, + .table_len = ARRAY_SIZE(VDCDC1_VSEL_table), + .table = VDCDC1_VSEL_table, + }, + { + .name = "VDCDC2", + .min_uV = 3300000, + .max_uV = 3300000, + .fixed = 1, + }, + { + .name = "VDCDC3", + .min_uV = 1800000, + .max_uV = 1800000, + .fixed = 1, + }, + { + .name = "LDO1", + .min_uV = 1000000, + .max_uV = 3150000, + .table_len = ARRAY_SIZE(LDO1_VSEL_table), + .table = LDO1_VSEL_table, + }, + { + .name = "LDO2", + .min_uV = 1050000, + .max_uV = 3300000, + .table_len = ARRAY_SIZE(LDO2_VSEL_table), + .table = LDO2_VSEL_table, + }, +}; + +static const struct i2c_device_id tps_65023_id[] = { + {.name = "tps65023", + .driver_data = (unsigned long) tps65023_regs,}, + { }, +}; + +MODULE_DEVICE_TABLE(i2c, tps_65023_id); + +static struct i2c_driver tps_65023_i2c_driver = { + .driver = { + .name = "tps65023", + .owner = THIS_MODULE, + }, + .probe = tps_65023_probe, + .remove = __devexit_p(tps_65023_remove), + .id_table = tps_65023_id, +}; + +/** + * tps_65023_init + * + * Module init function + */ +static int __init tps_65023_init(void) +{ + return i2c_add_driver(&tps_65023_i2c_driver); +} +subsys_initcall(tps_65023_init); + +/** + * tps_65023_cleanup + * + * Module exit function + */ +static void __exit tps_65023_cleanup(void) +{ + i2c_del_driver(&tps_65023_i2c_driver); +} +module_exit(tps_65023_cleanup); + +MODULE_AUTHOR("Texas Instruments"); +MODULE_DESCRIPTION("TPS65023 voltage regulator driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/regulator/tps6507x-regulator.c b/drivers/regulator/tps6507x-regulator.c new file mode 100644 index 000000000000..f8a6dfbef751 --- /dev/null +++ b/drivers/regulator/tps6507x-regulator.c @@ -0,0 +1,714 @@ +/* + * tps6507x-regulator.c + * + * Regulator driver for TPS65073 PMIC + * + * Copyright (C) 2009 Texas Instrument Incorporated - http://www.ti.com/ + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation version 2. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any kind, + * whether express or implied; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/err.h> +#include <linux/platform_device.h> +#include <linux/regulator/driver.h> +#include <linux/regulator/machine.h> +#include <linux/i2c.h> +#include <linux/delay.h> + +/* Register definitions */ +#define TPS6507X_REG_PPATH1 0X01 +#define TPS6507X_REG_INT 0X02 +#define TPS6507X_REG_CHGCONFIG0 0X03 +#define TPS6507X_REG_CHGCONFIG1 0X04 +#define TPS6507X_REG_CHGCONFIG2 0X05 +#define TPS6507X_REG_CHGCONFIG3 0X06 +#define TPS6507X_REG_REG_ADCONFIG 0X07 +#define TPS6507X_REG_TSCMODE 0X08 +#define TPS6507X_REG_ADRESULT_1 0X09 +#define TPS6507X_REG_ADRESULT_2 0X0A +#define TPS6507X_REG_PGOOD 0X0B +#define TPS6507X_REG_PGOODMASK 0X0C +#define TPS6507X_REG_CON_CTRL1 0X0D +#define TPS6507X_REG_CON_CTRL2 0X0E +#define TPS6507X_REG_CON_CTRL3 0X0F +#define TPS6507X_REG_DEFDCDC1 0X10 +#define TPS6507X_REG_DEFDCDC2_LOW 0X11 +#define TPS6507X_REG_DEFDCDC2_HIGH 0X12 +#define TPS6507X_REG_DEFDCDC3_LOW 0X13 +#define TPS6507X_REG_DEFDCDC3_HIGH 0X14 +#define TPS6507X_REG_DEFSLEW 0X15 +#define TPS6507X_REG_LDO_CTRL1 0X16 +#define TPS6507X_REG_DEFLDO2 0X17 +#define TPS6507X_REG_WLED_CTRL1 0X18 +#define TPS6507X_REG_WLED_CTRL2 0X19 + +/* CON_CTRL1 bitfields */ +#define TPS6507X_CON_CTRL1_DCDC1_ENABLE BIT(4) +#define TPS6507X_CON_CTRL1_DCDC2_ENABLE BIT(3) +#define TPS6507X_CON_CTRL1_DCDC3_ENABLE BIT(2) +#define TPS6507X_CON_CTRL1_LDO1_ENABLE BIT(1) +#define TPS6507X_CON_CTRL1_LDO2_ENABLE BIT(0) + +/* DEFDCDC1 bitfields */ +#define TPS6507X_DEFDCDC1_DCDC1_EXT_ADJ_EN BIT(7) +#define TPS6507X_DEFDCDC1_DCDC1_MASK 0X3F + +/* DEFDCDC2_LOW bitfields */ +#define TPS6507X_DEFDCDC2_LOW_DCDC2_MASK 0X3F + +/* DEFDCDC2_HIGH bitfields */ +#define TPS6507X_DEFDCDC2_HIGH_DCDC2_MASK 0X3F + +/* DEFDCDC3_LOW bitfields */ +#define TPS6507X_DEFDCDC3_LOW_DCDC3_MASK 0X3F + +/* DEFDCDC3_HIGH bitfields */ +#define TPS6507X_DEFDCDC3_HIGH_DCDC3_MASK 0X3F + +/* TPS6507X_REG_LDO_CTRL1 bitfields */ +#define TPS6507X_REG_LDO_CTRL1_LDO1_MASK 0X0F + +/* TPS6507X_REG_DEFLDO2 bitfields */ +#define TPS6507X_REG_DEFLDO2_LDO2_MASK 0X3F + +/* VDCDC MASK */ +#define TPS6507X_DEFDCDCX_DCDC_MASK 0X3F + +/* DCDC's */ +#define TPS6507X_DCDC_1 0 +#define TPS6507X_DCDC_2 1 +#define TPS6507X_DCDC_3 2 +/* LDOs */ +#define TPS6507X_LDO_1 3 +#define TPS6507X_LDO_2 4 + +#define TPS6507X_MAX_REG_ID TPS6507X_LDO_2 + +/* Number of step-down converters available */ +#define TPS6507X_NUM_DCDC 3 +/* Number of LDO voltage regulators available */ +#define TPS6507X_NUM_LDO 2 +/* Number of total regulators available */ +#define TPS6507X_NUM_REGULATOR (TPS6507X_NUM_DCDC + TPS6507X_NUM_LDO) + +/* Supported voltage values for regulators (in milliVolts) */ +static const u16 VDCDCx_VSEL_table[] = { + 725, 750, 775, 800, + 825, 850, 875, 900, + 925, 950, 975, 1000, + 1025, 1050, 1075, 1100, + 1125, 1150, 1175, 1200, + 1225, 1250, 1275, 1300, + 1325, 1350, 1375, 1400, + 1425, 1450, 1475, 1500, + 1550, 1600, 1650, 1700, + 1750, 1800, 1850, 1900, + 1950, 2000, 2050, 2100, + 2150, 2200, 2250, 2300, + 2350, 2400, 2450, 2500, + 2550, 2600, 2650, 2700, + 2750, 2800, 2850, 2900, + 3000, 3100, 3200, 3300, +}; + +static const u16 LDO1_VSEL_table[] = { + 1000, 1100, 1200, 1250, + 1300, 1350, 1400, 1500, + 1600, 1800, 2500, 2750, + 2800, 3000, 3100, 3300, +}; + +static const u16 LDO2_VSEL_table[] = { + 725, 750, 775, 800, + 825, 850, 875, 900, + 925, 950, 975, 1000, + 1025, 1050, 1075, 1100, + 1125, 1150, 1175, 1200, + 1225, 1250, 1275, 1300, + 1325, 1350, 1375, 1400, + 1425, 1450, 1475, 1500, + 1550, 1600, 1650, 1700, + 1750, 1800, 1850, 1900, + 1950, 2000, 2050, 2100, + 2150, 2200, 2250, 2300, + 2350, 2400, 2450, 2500, + 2550, 2600, 2650, 2700, + 2750, 2800, 2850, 2900, + 3000, 3100, 3200, 3300, +}; + +static unsigned int num_voltages[] = {ARRAY_SIZE(VDCDCx_VSEL_table), + ARRAY_SIZE(VDCDCx_VSEL_table), + ARRAY_SIZE(VDCDCx_VSEL_table), + ARRAY_SIZE(LDO1_VSEL_table), + ARRAY_SIZE(LDO2_VSEL_table)}; + +struct tps_info { + const char *name; + unsigned min_uV; + unsigned max_uV; + u8 table_len; + const u16 *table; +}; + +struct tps_pmic { + struct regulator_desc desc[TPS6507X_NUM_REGULATOR]; + struct i2c_client *client; + struct regulator_dev *rdev[TPS6507X_NUM_REGULATOR]; + const struct tps_info *info[TPS6507X_NUM_REGULATOR]; + struct mutex io_lock; +}; + +static inline int tps_6507x_read(struct tps_pmic *tps, u8 reg) +{ + return i2c_smbus_read_byte_data(tps->client, reg); +} + +static inline int tps_6507x_write(struct tps_pmic *tps, u8 reg, u8 val) +{ + return i2c_smbus_write_byte_data(tps->client, reg, val); +} + +static int tps_6507x_set_bits(struct tps_pmic *tps, u8 reg, u8 mask) +{ + int err, data; + + mutex_lock(&tps->io_lock); + + data = tps_6507x_read(tps, reg); + if (data < 0) { + dev_err(&tps->client->dev, "Read from reg 0x%x failed\n", reg); + err = data; + goto out; + } + + data |= mask; + err = tps_6507x_write(tps, reg, data); + if (err) + dev_err(&tps->client->dev, "Write for reg 0x%x failed\n", reg); + +out: + mutex_unlock(&tps->io_lock); + return err; +} + +static int tps_6507x_clear_bits(struct tps_pmic *tps, u8 reg, u8 mask) +{ + int err, data; + + mutex_lock(&tps->io_lock); + + data = tps_6507x_read(tps, reg); + if (data < 0) { + dev_err(&tps->client->dev, "Read from reg 0x%x failed\n", reg); + err = data; + goto out; + } + + data &= ~mask; + err = tps_6507x_write(tps, reg, data); + if (err) + dev_err(&tps->client->dev, "Write for reg 0x%x failed\n", reg); + +out: + mutex_unlock(&tps->io_lock); + return err; +} + +static int tps_6507x_reg_read(struct tps_pmic *tps, u8 reg) +{ + int data; + + mutex_lock(&tps->io_lock); + + data = tps_6507x_read(tps, reg); + if (data < 0) + dev_err(&tps->client->dev, "Read from reg 0x%x failed\n", reg); + + mutex_unlock(&tps->io_lock); + return data; +} + +static int tps_6507x_reg_write(struct tps_pmic *tps, u8 reg, u8 val) +{ + int err; + + mutex_lock(&tps->io_lock); + + err = tps_6507x_write(tps, reg, val); + if (err < 0) + dev_err(&tps->client->dev, "Write for reg 0x%x failed\n", reg); + + mutex_unlock(&tps->io_lock); + return err; +} + +static int tps6507x_dcdc_is_enabled(struct regulator_dev *dev) +{ + struct tps_pmic *tps = rdev_get_drvdata(dev); + int data, dcdc = rdev_get_id(dev); + u8 shift; + + if (dcdc < TPS6507X_DCDC_1 || dcdc > TPS6507X_DCDC_3) + return -EINVAL; + + shift = TPS6507X_MAX_REG_ID - dcdc; + data = tps_6507x_reg_read(tps, TPS6507X_REG_CON_CTRL1); + + if (data < 0) + return data; + else + return (data & 1<<shift) ? 1 : 0; +} + +static int tps6507x_ldo_is_enabled(struct regulator_dev *dev) +{ + struct tps_pmic *tps = rdev_get_drvdata(dev); + int data, ldo = rdev_get_id(dev); + u8 shift; + + if (ldo < TPS6507X_LDO_1 || ldo > TPS6507X_LDO_2) + return -EINVAL; + + shift = TPS6507X_MAX_REG_ID - ldo; + data = tps_6507x_reg_read(tps, TPS6507X_REG_CON_CTRL1); + + if (data < 0) + return data; + else + return (data & 1<<shift) ? 1 : 0; +} + +static int tps6507x_dcdc_enable(struct regulator_dev *dev) +{ + struct tps_pmic *tps = rdev_get_drvdata(dev); + int dcdc = rdev_get_id(dev); + u8 shift; + + if (dcdc < TPS6507X_DCDC_1 || dcdc > TPS6507X_DCDC_3) + return -EINVAL; + + shift = TPS6507X_MAX_REG_ID - dcdc; + return tps_6507x_set_bits(tps, TPS6507X_REG_CON_CTRL1, 1 << shift); +} + +static int tps6507x_dcdc_disable(struct regulator_dev *dev) +{ + struct tps_pmic *tps = rdev_get_drvdata(dev); + int dcdc = rdev_get_id(dev); + u8 shift; + + if (dcdc < TPS6507X_DCDC_1 || dcdc > TPS6507X_DCDC_3) + return -EINVAL; + + shift = TPS6507X_MAX_REG_ID - dcdc; + return tps_6507x_clear_bits(tps, TPS6507X_REG_CON_CTRL1, 1 << shift); +} + +static int tps6507x_ldo_enable(struct regulator_dev *dev) +{ + struct tps_pmic *tps = rdev_get_drvdata(dev); + int ldo = rdev_get_id(dev); + u8 shift; + + if (ldo < TPS6507X_LDO_1 || ldo > TPS6507X_LDO_2) + return -EINVAL; + + shift = TPS6507X_MAX_REG_ID - ldo; + return tps_6507x_set_bits(tps, TPS6507X_REG_CON_CTRL1, 1 << shift); +} + +static int tps6507x_ldo_disable(struct regulator_dev *dev) +{ + struct tps_pmic *tps = rdev_get_drvdata(dev); + int ldo = rdev_get_id(dev); + u8 shift; + + if (ldo < TPS6507X_LDO_1 || ldo > TPS6507X_LDO_2) + return -EINVAL; + + shift = TPS6507X_MAX_REG_ID - ldo; + return tps_6507x_clear_bits(tps, TPS6507X_REG_CON_CTRL1, 1 << shift); +} + +static int tps6507x_dcdc_get_voltage(struct regulator_dev *dev) +{ + struct tps_pmic *tps = rdev_get_drvdata(dev); + int data, dcdc = rdev_get_id(dev); + u8 reg; + + switch (dcdc) { + case TPS6507X_DCDC_1: + reg = TPS6507X_REG_DEFDCDC1; + break; + case TPS6507X_DCDC_2: + reg = TPS6507X_REG_DEFDCDC2_LOW; + break; + case TPS6507X_DCDC_3: + reg = TPS6507X_REG_DEFDCDC3_LOW; + break; + default: + return -EINVAL; + } + + data = tps_6507x_reg_read(tps, reg); + if (data < 0) + return data; + + data &= TPS6507X_DEFDCDCX_DCDC_MASK; + return tps->info[dcdc]->table[data] * 1000; +} + +static int tps6507x_dcdc_set_voltage(struct regulator_dev *dev, + int min_uV, int max_uV) +{ + struct tps_pmic *tps = rdev_get_drvdata(dev); + int data, vsel, dcdc = rdev_get_id(dev); + u8 reg; + + switch (dcdc) { + case TPS6507X_DCDC_1: + reg = TPS6507X_REG_DEFDCDC1; + break; + case TPS6507X_DCDC_2: + reg = TPS6507X_REG_DEFDCDC2_LOW; + break; + case TPS6507X_DCDC_3: + reg = TPS6507X_REG_DEFDCDC3_LOW; + break; + default: + return -EINVAL; + } + + if (min_uV < tps->info[dcdc]->min_uV + || min_uV > tps->info[dcdc]->max_uV) + return -EINVAL; + if (max_uV < tps->info[dcdc]->min_uV + || max_uV > tps->info[dcdc]->max_uV) + return -EINVAL; + + for (vsel = 0; vsel < tps->info[dcdc]->table_len; vsel++) { + int mV = tps->info[dcdc]->table[vsel]; + int uV = mV * 1000; + + /* Break at the first in-range value */ + if (min_uV <= uV && uV <= max_uV) + break; + } + + /* write to the register in case we found a match */ + if (vsel == tps->info[dcdc]->table_len) + return -EINVAL; + + data = tps_6507x_reg_read(tps, reg); + if (data < 0) + return data; + + data &= ~TPS6507X_DEFDCDCX_DCDC_MASK; + data |= vsel; + + return tps_6507x_reg_write(tps, reg, data); +} + +static int tps6507x_ldo_get_voltage(struct regulator_dev *dev) +{ + struct tps_pmic *tps = rdev_get_drvdata(dev); + int data, ldo = rdev_get_id(dev); + u8 reg, mask; + + if (ldo < TPS6507X_LDO_1 || ldo > TPS6507X_LDO_2) + return -EINVAL; + else { + reg = (ldo == TPS6507X_LDO_1 ? + TPS6507X_REG_LDO_CTRL1 : TPS6507X_REG_DEFLDO2); + mask = (ldo == TPS6507X_LDO_1 ? + TPS6507X_REG_LDO_CTRL1_LDO1_MASK : + TPS6507X_REG_DEFLDO2_LDO2_MASK); + } + + data = tps_6507x_reg_read(tps, reg); + if (data < 0) + return data; + + data &= mask; + return tps->info[ldo]->table[data] * 1000; +} + +static int tps6507x_ldo_set_voltage(struct regulator_dev *dev, + int min_uV, int max_uV) +{ + struct tps_pmic *tps = rdev_get_drvdata(dev); + int data, vsel, ldo = rdev_get_id(dev); + u8 reg, mask; + + if (ldo < TPS6507X_LDO_1 || ldo > TPS6507X_LDO_2) + return -EINVAL; + else { + reg = (ldo == TPS6507X_LDO_1 ? + TPS6507X_REG_LDO_CTRL1 : TPS6507X_REG_DEFLDO2); + mask = (ldo == TPS6507X_LDO_1 ? + TPS6507X_REG_LDO_CTRL1_LDO1_MASK : + TPS6507X_REG_DEFLDO2_LDO2_MASK); + } + + if (min_uV < tps->info[ldo]->min_uV || min_uV > tps->info[ldo]->max_uV) + return -EINVAL; + if (max_uV < tps->info[ldo]->min_uV || max_uV > tps->info[ldo]->max_uV) + return -EINVAL; + + for (vsel = 0; vsel < tps->info[ldo]->table_len; vsel++) { + int mV = tps->info[ldo]->table[vsel]; + int uV = mV * 1000; + + /* Break at the first in-range value */ + if (min_uV <= uV && uV <= max_uV) + break; + } + + if (vsel == tps->info[ldo]->table_len) + return -EINVAL; + + data = tps_6507x_reg_read(tps, reg); + if (data < 0) + return data; + + data &= ~mask; + data |= vsel; + + return tps_6507x_reg_write(tps, reg, data); +} + +static int tps6507x_dcdc_list_voltage(struct regulator_dev *dev, + unsigned selector) +{ + struct tps_pmic *tps = rdev_get_drvdata(dev); + int dcdc = rdev_get_id(dev); + + if (dcdc < TPS6507X_DCDC_1 || dcdc > TPS6507X_DCDC_3) + return -EINVAL; + + if (selector >= tps->info[dcdc]->table_len) + return -EINVAL; + else + return tps->info[dcdc]->table[selector] * 1000; +} + +static int tps6507x_ldo_list_voltage(struct regulator_dev *dev, + unsigned selector) +{ + struct tps_pmic *tps = rdev_get_drvdata(dev); + int ldo = rdev_get_id(dev); + + if (ldo < TPS6507X_LDO_1 || ldo > TPS6507X_LDO_2) + return -EINVAL; + + if (selector >= tps->info[ldo]->table_len) + return -EINVAL; + else + return tps->info[ldo]->table[selector] * 1000; +} + +/* Operations permitted on VDCDCx */ +static struct regulator_ops tps6507x_dcdc_ops = { + .is_enabled = tps6507x_dcdc_is_enabled, + .enable = tps6507x_dcdc_enable, + .disable = tps6507x_dcdc_disable, + .get_voltage = tps6507x_dcdc_get_voltage, + .set_voltage = tps6507x_dcdc_set_voltage, + .list_voltage = tps6507x_dcdc_list_voltage, +}; + +/* Operations permitted on LDOx */ +static struct regulator_ops tps6507x_ldo_ops = { + .is_enabled = tps6507x_ldo_is_enabled, + .enable = tps6507x_ldo_enable, + .disable = tps6507x_ldo_disable, + .get_voltage = tps6507x_ldo_get_voltage, + .set_voltage = tps6507x_ldo_set_voltage, + .list_voltage = tps6507x_ldo_list_voltage, +}; + +static +int tps_6507x_probe(struct i2c_client *client, const struct i2c_device_id *id) +{ + static int desc_id; + const struct tps_info *info = (void *)id->driver_data; + struct regulator_init_data *init_data; + struct regulator_dev *rdev; + struct tps_pmic *tps; + int i; + + if (!i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_BYTE_DATA)) + return -EIO; + + /** + * init_data points to array of regulator_init structures + * coming from the board-evm file. + */ + init_data = client->dev.platform_data; + + if (!init_data) + return -EIO; + + tps = kzalloc(sizeof(*tps), GFP_KERNEL); + if (!tps) + return -ENOMEM; + + mutex_init(&tps->io_lock); + + /* common for all regulators */ + tps->client = client; + + for (i = 0; i < TPS6507X_NUM_REGULATOR; i++, info++, init_data++) { + /* Register the regulators */ + tps->info[i] = info; + tps->desc[i].name = info->name; + tps->desc[i].id = desc_id++; + tps->desc[i].n_voltages = num_voltages[i]; + tps->desc[i].ops = (i > TPS6507X_DCDC_3 ? + &tps6507x_ldo_ops : &tps6507x_dcdc_ops); + tps->desc[i].type = REGULATOR_VOLTAGE; + tps->desc[i].owner = THIS_MODULE; + + rdev = regulator_register(&tps->desc[i], + &client->dev, init_data, tps); + if (IS_ERR(rdev)) { + dev_err(&client->dev, "failed to register %s\n", + id->name); + + /* Unregister */ + while (i) + regulator_unregister(tps->rdev[--i]); + + tps->client = NULL; + + /* clear the client data in i2c */ + i2c_set_clientdata(client, NULL); + + kfree(tps); + return PTR_ERR(rdev); + } + + /* Save regulator for cleanup */ + tps->rdev[i] = rdev; + } + + i2c_set_clientdata(client, tps); + + return 0; +} + +/** + * tps_6507x_remove - TPS6507x driver i2c remove handler + * @client: i2c driver client device structure + * + * Unregister TPS driver as an i2c client device driver + */ +static int __devexit tps_6507x_remove(struct i2c_client *client) +{ + struct tps_pmic *tps = i2c_get_clientdata(client); + int i; + + for (i = 0; i < TPS6507X_NUM_REGULATOR; i++) + regulator_unregister(tps->rdev[i]); + + tps->client = NULL; + + /* clear the client data in i2c */ + i2c_set_clientdata(client, NULL); + kfree(tps); + + return 0; +} + +static const struct tps_info tps6507x_regs[] = { + { + .name = "VDCDC1", + .min_uV = 725000, + .max_uV = 3300000, + .table_len = ARRAY_SIZE(VDCDCx_VSEL_table), + .table = VDCDCx_VSEL_table, + }, + { + .name = "VDCDC2", + .min_uV = 725000, + .max_uV = 3300000, + .table_len = ARRAY_SIZE(VDCDCx_VSEL_table), + .table = VDCDCx_VSEL_table, + }, + { + .name = "VDCDC3", + .min_uV = 725000, + .max_uV = 3300000, + .table_len = ARRAY_SIZE(VDCDCx_VSEL_table), + .table = VDCDCx_VSEL_table, + }, + { + .name = "LDO1", + .min_uV = 1000000, + .max_uV = 3300000, + .table_len = ARRAY_SIZE(LDO1_VSEL_table), + .table = LDO1_VSEL_table, + }, + { + .name = "LDO2", + .min_uV = 725000, + .max_uV = 3300000, + .table_len = ARRAY_SIZE(LDO2_VSEL_table), + .table = LDO2_VSEL_table, + }, +}; + +static const struct i2c_device_id tps_6507x_id[] = { + {.name = "tps6507x", + .driver_data = (unsigned long) tps6507x_regs,}, + { }, +}; +MODULE_DEVICE_TABLE(i2c, tps_6507x_id); + +static struct i2c_driver tps_6507x_i2c_driver = { + .driver = { + .name = "tps6507x", + .owner = THIS_MODULE, + }, + .probe = tps_6507x_probe, + .remove = __devexit_p(tps_6507x_remove), + .id_table = tps_6507x_id, +}; + +/** + * tps_6507x_init + * + * Module init function + */ +static int __init tps_6507x_init(void) +{ + return i2c_add_driver(&tps_6507x_i2c_driver); +} +subsys_initcall(tps_6507x_init); + +/** + * tps_6507x_cleanup + * + * Module exit function + */ +static void __exit tps_6507x_cleanup(void) +{ + i2c_del_driver(&tps_6507x_i2c_driver); +} +module_exit(tps_6507x_cleanup); + +MODULE_AUTHOR("Texas Instruments"); +MODULE_DESCRIPTION("TPS6507x voltage regulator driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/regulator/userspace-consumer.c b/drivers/regulator/userspace-consumer.c index 06d2fa96a8b4..44917da4ac97 100644 --- a/drivers/regulator/userspace-consumer.c +++ b/drivers/regulator/userspace-consumer.c @@ -93,16 +93,21 @@ static ssize_t reg_set_state(struct device *dev, struct device_attribute *attr, static DEVICE_ATTR(name, 0444, reg_show_name, NULL); static DEVICE_ATTR(state, 0644, reg_show_state, reg_set_state); -static struct device_attribute *attributes[] = { - &dev_attr_name, - &dev_attr_state, +static struct attribute *attributes[] = { + &dev_attr_name.attr, + &dev_attr_state.attr, + NULL, +}; + +static const struct attribute_group attr_group = { + .attrs = attributes, }; static int regulator_userspace_consumer_probe(struct platform_device *pdev) { struct regulator_userspace_consumer_data *pdata; struct userspace_consumer_data *drvdata; - int ret, i; + int ret; pdata = pdev->dev.platform_data; if (!pdata) @@ -125,31 +130,29 @@ static int regulator_userspace_consumer_probe(struct platform_device *pdev) goto err_alloc_supplies; } - for (i = 0; i < ARRAY_SIZE(attributes); i++) { - ret = device_create_file(&pdev->dev, attributes[i]); - if (ret != 0) - goto err_create_attrs; - } + ret = sysfs_create_group(&pdev->dev.kobj, &attr_group); + if (ret != 0) + goto err_create_attrs; - if (pdata->init_on) + if (pdata->init_on) { ret = regulator_bulk_enable(drvdata->num_supplies, drvdata->supplies); - - drvdata->enabled = pdata->init_on; - - if (ret) { - dev_err(&pdev->dev, "Failed to set initial state: %d\n", ret); - goto err_create_attrs; + if (ret) { + dev_err(&pdev->dev, + "Failed to set initial state: %d\n", ret); + goto err_enable; + } } + drvdata->enabled = pdata->init_on; platform_set_drvdata(pdev, drvdata); return 0; -err_create_attrs: - for (i = 0; i < ARRAY_SIZE(attributes); i++) - device_remove_file(&pdev->dev, attributes[i]); +err_enable: + sysfs_remove_group(&pdev->dev.kobj, &attr_group); +err_create_attrs: regulator_bulk_free(drvdata->num_supplies, drvdata->supplies); err_alloc_supplies: @@ -160,10 +163,8 @@ err_alloc_supplies: static int regulator_userspace_consumer_remove(struct platform_device *pdev) { struct userspace_consumer_data *data = platform_get_drvdata(pdev); - int i; - for (i = 0; i < ARRAY_SIZE(attributes); i++) - device_remove_file(&pdev->dev, attributes[i]); + sysfs_remove_group(&pdev->dev.kobj, &attr_group); if (data->enabled) regulator_bulk_disable(data->num_supplies, data->supplies); diff --git a/drivers/regulator/virtual.c b/drivers/regulator/virtual.c index e7db5664722e..addc032c84bf 100644 --- a/drivers/regulator/virtual.c +++ b/drivers/regulator/virtual.c @@ -27,71 +27,81 @@ struct virtual_consumer_data { unsigned int mode; }; -static void update_voltage_constraints(struct virtual_consumer_data *data) +static void update_voltage_constraints(struct device *dev, + struct virtual_consumer_data *data) { int ret; if (data->min_uV && data->max_uV && data->min_uV <= data->max_uV) { + dev_dbg(dev, "Requesting %d-%duV\n", + data->min_uV, data->max_uV); ret = regulator_set_voltage(data->regulator, - data->min_uV, data->max_uV); + data->min_uV, data->max_uV); if (ret != 0) { - printk(KERN_ERR "regulator_set_voltage() failed: %d\n", - ret); + dev_err(dev, + "regulator_set_voltage() failed: %d\n", ret); return; } } if (data->min_uV && data->max_uV && !data->enabled) { + dev_dbg(dev, "Enabling regulator\n"); ret = regulator_enable(data->regulator); if (ret == 0) data->enabled = 1; else - printk(KERN_ERR "regulator_enable() failed: %d\n", + dev_err(dev, "regulator_enable() failed: %d\n", ret); } if (!(data->min_uV && data->max_uV) && data->enabled) { + dev_dbg(dev, "Disabling regulator\n"); ret = regulator_disable(data->regulator); if (ret == 0) data->enabled = 0; else - printk(KERN_ERR "regulator_disable() failed: %d\n", + dev_err(dev, "regulator_disable() failed: %d\n", ret); } } -static void update_current_limit_constraints(struct virtual_consumer_data - *data) +static void update_current_limit_constraints(struct device *dev, + struct virtual_consumer_data *data) { int ret; if (data->max_uA && data->min_uA <= data->max_uA) { + dev_dbg(dev, "Requesting %d-%duA\n", + data->min_uA, data->max_uA); ret = regulator_set_current_limit(data->regulator, data->min_uA, data->max_uA); if (ret != 0) { - pr_err("regulator_set_current_limit() failed: %d\n", - ret); + dev_err(dev, + "regulator_set_current_limit() failed: %d\n", + ret); return; } } if (data->max_uA && !data->enabled) { + dev_dbg(dev, "Enabling regulator\n"); ret = regulator_enable(data->regulator); if (ret == 0) data->enabled = 1; else - printk(KERN_ERR "regulator_enable() failed: %d\n", + dev_err(dev, "regulator_enable() failed: %d\n", ret); } if (!(data->min_uA && data->max_uA) && data->enabled) { + dev_dbg(dev, "Disabling regulator\n"); ret = regulator_disable(data->regulator); if (ret == 0) data->enabled = 0; else - printk(KERN_ERR "regulator_disable() failed: %d\n", + dev_err(dev, "regulator_disable() failed: %d\n", ret); } } @@ -115,7 +125,7 @@ static ssize_t set_min_uV(struct device *dev, struct device_attribute *attr, mutex_lock(&data->lock); data->min_uV = val; - update_voltage_constraints(data); + update_voltage_constraints(dev, data); mutex_unlock(&data->lock); @@ -141,7 +151,7 @@ static ssize_t set_max_uV(struct device *dev, struct device_attribute *attr, mutex_lock(&data->lock); data->max_uV = val; - update_voltage_constraints(data); + update_voltage_constraints(dev, data); mutex_unlock(&data->lock); @@ -167,7 +177,7 @@ static ssize_t set_min_uA(struct device *dev, struct device_attribute *attr, mutex_lock(&data->lock); data->min_uA = val; - update_current_limit_constraints(data); + update_current_limit_constraints(dev, data); mutex_unlock(&data->lock); @@ -193,7 +203,7 @@ static ssize_t set_max_uA(struct device *dev, struct device_attribute *attr, mutex_lock(&data->lock); data->max_uA = val; - update_current_limit_constraints(data); + update_current_limit_constraints(dev, data); mutex_unlock(&data->lock); @@ -276,8 +286,7 @@ static int regulator_virtual_consumer_probe(struct platform_device *pdev) drvdata = kzalloc(sizeof(struct virtual_consumer_data), GFP_KERNEL); if (drvdata == NULL) { - ret = -ENOMEM; - goto err; + return -ENOMEM; } mutex_init(&drvdata->lock); @@ -285,13 +294,18 @@ static int regulator_virtual_consumer_probe(struct platform_device *pdev) drvdata->regulator = regulator_get(&pdev->dev, reg_id); if (IS_ERR(drvdata->regulator)) { ret = PTR_ERR(drvdata->regulator); + dev_err(&pdev->dev, "Failed to obtain supply '%s': %d\n", + reg_id, ret); goto err; } for (i = 0; i < ARRAY_SIZE(attributes); i++) { ret = device_create_file(&pdev->dev, attributes[i]); - if (ret != 0) - goto err; + if (ret != 0) { + dev_err(&pdev->dev, "Failed to create attr %d: %d\n", + i, ret); + goto err_regulator; + } } drvdata->mode = regulator_get_mode(drvdata->regulator); @@ -300,6 +314,8 @@ static int regulator_virtual_consumer_probe(struct platform_device *pdev) return 0; +err_regulator: + regulator_put(drvdata->regulator); err: for (i = 0; i < ARRAY_SIZE(attributes); i++) device_remove_file(&pdev->dev, attributes[i]); diff --git a/drivers/regulator/wm831x-ldo.c b/drivers/regulator/wm831x-ldo.c index bb61aede4801..902db56ce099 100644 --- a/drivers/regulator/wm831x-ldo.c +++ b/drivers/regulator/wm831x-ldo.c @@ -175,18 +175,18 @@ static unsigned int wm831x_gp_ldo_get_mode(struct regulator_dev *rdev) struct wm831x *wm831x = ldo->wm831x; int ctrl_reg = ldo->base + WM831X_LDO_CONTROL; int on_reg = ldo->base + WM831X_LDO_ON_CONTROL; - unsigned int ret; + int ret; ret = wm831x_reg_read(wm831x, on_reg); if (ret < 0) - return 0; + return ret; if (!(ret & WM831X_LDO1_ON_MODE)) return REGULATOR_MODE_NORMAL; ret = wm831x_reg_read(wm831x, ctrl_reg); if (ret < 0) - return 0; + return ret; if (ret & WM831X_LDO1_LP_MODE) return REGULATOR_MODE_STANDBY; diff --git a/drivers/regulator/wm8350-regulator.c b/drivers/regulator/wm8350-regulator.c index 17a00b0fafd1..768bd0e5b48b 100644 --- a/drivers/regulator/wm8350-regulator.c +++ b/drivers/regulator/wm8350-regulator.c @@ -1419,6 +1419,8 @@ int wm8350_register_regulator(struct wm8350 *wm8350, int reg, { struct platform_device *pdev; int ret; + if (reg < 0 || reg >= NUM_WM8350_REGULATORS) + return -EINVAL; if (wm8350->pmic.pdev[reg]) return -EBUSY; diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index 73771b09fbd3..3c20dae43ce2 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig @@ -378,6 +378,15 @@ config RTC_DRV_DS3234 This driver can also be built as a module. If so, the module will be called rtc-ds3234. +config RTC_DRV_PCF2123 + tristate "NXP PCF2123" + help + If you say yes here you get support for the NXP PCF2123 + RTC chip. + + This driver can also be built as a module. If so, the module + will be called rtc-pcf2123. + endif # SPI_MASTER comment "Platform RTC drivers" @@ -500,6 +509,17 @@ config RTC_DRV_M48T59 This driver can also be built as a module, if so, the module will be called "rtc-m48t59". +config RTC_MXC + tristate "Freescale MXC Real Time Clock" + depends on ARCH_MXC + depends on RTC_CLASS + help + If you say yes here you get support for the Freescale MXC + RTC module. + + This driver can also be built as a module, if so, the module + will be called "rtc-mxc". + config RTC_DRV_BQ4802 tristate "TI BQ4802" help @@ -778,4 +798,33 @@ config RTC_DRV_PS3 This driver can also be built as a module. If so, the module will be called rtc-ps3. +config RTC_DRV_COH901331 + tristate "ST-Ericsson COH 901 331 RTC" + depends on ARCH_U300 + help + If you say Y here you will get access to ST-Ericsson + COH 901 331 RTC clock found in some ST-Ericsson Mobile + Platforms. + + This driver can also be built as a module. If so, the module + will be called "rtc-coh901331". + + +config RTC_DRV_STMP + tristate "Freescale STMP3xxx RTC" + depends on ARCH_STMP3XXX + help + If you say yes here you will get support for the onboard + STMP3xxx RTC. + + This driver can also be built as a module. If so, the module + will be called rtc-stmp3xxx. + +config RTC_DRV_PCAP + tristate "PCAP RTC" + depends on EZX_PCAP + help + If you say Y here you will get support for the RTC found on + the PCAP2 ASIC used on some Motorola phones. + endif # RTC_CLASS diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile index 5e152ffe5058..aa3fbd5517a1 100644 --- a/drivers/rtc/Makefile +++ b/drivers/rtc/Makefile @@ -23,7 +23,9 @@ obj-$(CONFIG_RTC_DRV_AT91RM9200)+= rtc-at91rm9200.o obj-$(CONFIG_RTC_DRV_AT91SAM9) += rtc-at91sam9.o obj-$(CONFIG_RTC_DRV_AU1XXX) += rtc-au1xxx.o obj-$(CONFIG_RTC_DRV_BFIN) += rtc-bfin.o +obj-$(CONFIG_RTC_DRV_BQ4802) += rtc-bq4802.o obj-$(CONFIG_RTC_DRV_CMOS) += rtc-cmos.o +obj-$(CONFIG_RTC_DRV_COH901331) += rtc-coh901331.o obj-$(CONFIG_RTC_DRV_DM355EVM) += rtc-dm355evm.o obj-$(CONFIG_RTC_DRV_DS1216) += rtc-ds1216.o obj-$(CONFIG_RTC_DRV_DS1286) += rtc-ds1286.o @@ -40,24 +42,26 @@ obj-$(CONFIG_RTC_DRV_DS3234) += rtc-ds3234.o obj-$(CONFIG_RTC_DRV_EFI) += rtc-efi.o obj-$(CONFIG_RTC_DRV_EP93XX) += rtc-ep93xx.o obj-$(CONFIG_RTC_DRV_FM3130) += rtc-fm3130.o +obj-$(CONFIG_RTC_DRV_GENERIC) += rtc-generic.o obj-$(CONFIG_RTC_DRV_ISL1208) += rtc-isl1208.o obj-$(CONFIG_RTC_DRV_M41T80) += rtc-m41t80.o obj-$(CONFIG_RTC_DRV_M41T94) += rtc-m41t94.o obj-$(CONFIG_RTC_DRV_M48T35) += rtc-m48t35.o obj-$(CONFIG_RTC_DRV_M48T59) += rtc-m48t59.o obj-$(CONFIG_RTC_DRV_M48T86) += rtc-m48t86.o -obj-$(CONFIG_RTC_DRV_BQ4802) += rtc-bq4802.o -obj-$(CONFIG_RTC_DRV_SUN4V) += rtc-sun4v.o -obj-$(CONFIG_RTC_DRV_STARFIRE) += rtc-starfire.o +obj-$(CONFIG_RTC_MXC) += rtc-mxc.o obj-$(CONFIG_RTC_DRV_MAX6900) += rtc-max6900.o obj-$(CONFIG_RTC_DRV_MAX6902) += rtc-max6902.o obj-$(CONFIG_RTC_DRV_MV) += rtc-mv.o obj-$(CONFIG_RTC_DRV_OMAP) += rtc-omap.o +obj-$(CONFIG_RTC_DRV_PCAP) += rtc-pcap.o obj-$(CONFIG_RTC_DRV_PCF8563) += rtc-pcf8563.o obj-$(CONFIG_RTC_DRV_PCF8583) += rtc-pcf8583.o +obj-$(CONFIG_RTC_DRV_PCF2123) += rtc-pcf2123.o +obj-$(CONFIG_RTC_DRV_PCF50633) += rtc-pcf50633.o obj-$(CONFIG_RTC_DRV_PL030) += rtc-pl030.o obj-$(CONFIG_RTC_DRV_PL031) += rtc-pl031.o -obj-$(CONFIG_RTC_DRV_GENERIC) += rtc-generic.o +obj-$(CONFIG_RTC_DRV_PS3) += rtc-ps3.o obj-$(CONFIG_RTC_DRV_PXA) += rtc-pxa.o obj-$(CONFIG_RTC_DRV_R9701) += rtc-r9701.o obj-$(CONFIG_RTC_DRV_RS5C313) += rtc-rs5c313.o @@ -69,7 +73,10 @@ obj-$(CONFIG_RTC_DRV_S35390A) += rtc-s35390a.o obj-$(CONFIG_RTC_DRV_S3C) += rtc-s3c.o obj-$(CONFIG_RTC_DRV_SA1100) += rtc-sa1100.o obj-$(CONFIG_RTC_DRV_SH) += rtc-sh.o +obj-$(CONFIG_RTC_DRV_STARFIRE) += rtc-starfire.o obj-$(CONFIG_RTC_DRV_STK17TA8) += rtc-stk17ta8.o +obj-$(CONFIG_RTC_DRV_STMP) += rtc-stmp3xxx.o +obj-$(CONFIG_RTC_DRV_SUN4V) += rtc-sun4v.o obj-$(CONFIG_RTC_DRV_TEST) += rtc-test.o obj-$(CONFIG_RTC_DRV_TWL4030) += rtc-twl4030.o obj-$(CONFIG_RTC_DRV_TX4939) += rtc-tx4939.o @@ -78,5 +85,3 @@ obj-$(CONFIG_RTC_DRV_VR41XX) += rtc-vr41xx.o obj-$(CONFIG_RTC_DRV_WM831X) += rtc-wm831x.o obj-$(CONFIG_RTC_DRV_WM8350) += rtc-wm8350.o obj-$(CONFIG_RTC_DRV_X1205) += rtc-x1205.o -obj-$(CONFIG_RTC_DRV_PCF50633) += rtc-pcf50633.o -obj-$(CONFIG_RTC_DRV_PS3) += rtc-ps3.o diff --git a/drivers/rtc/interface.c b/drivers/rtc/interface.c index 4cdb31a362ca..a0c816238aa9 100644 --- a/drivers/rtc/interface.c +++ b/drivers/rtc/interface.c @@ -12,6 +12,7 @@ */ #include <linux/rtc.h> +#include <linux/sched.h> #include <linux/log2.h> int rtc_read_time(struct rtc_device *rtc, struct rtc_time *tm) diff --git a/drivers/rtc/rtc-at91rm9200.c b/drivers/rtc/rtc-at91rm9200.c index b5bf93706913..bc8bbca9a2e2 100644 --- a/drivers/rtc/rtc-at91rm9200.c +++ b/drivers/rtc/rtc-at91rm9200.c @@ -289,7 +289,7 @@ static int __init at91_rtc_probe(struct platform_device *pdev) AT91_RTC_CALEV); ret = request_irq(AT91_ID_SYS, at91_rtc_interrupt, - IRQF_DISABLED | IRQF_SHARED, + IRQF_SHARED, "at91_rtc", pdev); if (ret) { printk(KERN_ERR "at91_rtc: IRQ %d already in use.\n", @@ -340,7 +340,7 @@ static int __exit at91_rtc_remove(struct platform_device *pdev) static u32 at91_rtc_imr; -static int at91_rtc_suspend(struct platform_device *pdev, pm_message_t state) +static int at91_rtc_suspend(struct device *dev) { /* this IRQ is shared with DBGU and other hardware which isn't * necessarily doing PM like we are... @@ -348,7 +348,7 @@ static int at91_rtc_suspend(struct platform_device *pdev, pm_message_t state) at91_rtc_imr = at91_sys_read(AT91_RTC_IMR) & (AT91_RTC_ALARM|AT91_RTC_SECEV); if (at91_rtc_imr) { - if (device_may_wakeup(&pdev->dev)) + if (device_may_wakeup(dev)) enable_irq_wake(AT91_ID_SYS); else at91_sys_write(AT91_RTC_IDR, at91_rtc_imr); @@ -356,28 +356,34 @@ static int at91_rtc_suspend(struct platform_device *pdev, pm_message_t state) return 0; } -static int at91_rtc_resume(struct platform_device *pdev) +static int at91_rtc_resume(struct device *dev) { if (at91_rtc_imr) { - if (device_may_wakeup(&pdev->dev)) + if (device_may_wakeup(dev)) disable_irq_wake(AT91_ID_SYS); else at91_sys_write(AT91_RTC_IER, at91_rtc_imr); } return 0; } + +static const struct dev_pm_ops at91_rtc_pm = { + .suspend = at91_rtc_suspend, + .resume = at91_rtc_resume, +}; + +#define at91_rtc_pm_ptr &at91_rtc_pm + #else -#define at91_rtc_suspend NULL -#define at91_rtc_resume NULL +#define at91_rtc_pm_ptr NULL #endif static struct platform_driver at91_rtc_driver = { .remove = __exit_p(at91_rtc_remove), - .suspend = at91_rtc_suspend, - .resume = at91_rtc_resume, .driver = { .name = "at91_rtc", .owner = THIS_MODULE, + .pm = at91_rtc_pm_ptr, }, }; diff --git a/drivers/rtc/rtc-bfin.c b/drivers/rtc/rtc-bfin.c index a118eb0f1e67..b11485b9f21c 100644 --- a/drivers/rtc/rtc-bfin.c +++ b/drivers/rtc/rtc-bfin.c @@ -383,7 +383,7 @@ static int __devinit bfin_rtc_probe(struct platform_device *pdev) } /* Grab the IRQ and init the hardware */ - ret = request_irq(IRQ_RTC, bfin_rtc_interrupt, IRQF_SHARED, pdev->name, dev); + ret = request_irq(IRQ_RTC, bfin_rtc_interrupt, 0, pdev->name, dev); if (unlikely(ret)) goto err_reg; /* sometimes the bootloader touched things, but the write complete was not diff --git a/drivers/rtc/rtc-coh901331.c b/drivers/rtc/rtc-coh901331.c new file mode 100644 index 000000000000..03ea530981d1 --- /dev/null +++ b/drivers/rtc/rtc-coh901331.c @@ -0,0 +1,322 @@ +/* + * Copyright (C) 2007-2009 ST-Ericsson AB + * License terms: GNU General Public License (GPL) version 2 + * Real Time Clock interface for ST-Ericsson AB COH 901 331 RTC. + * Author: Linus Walleij <linus.walleij@stericsson.com> + * Based on rtc-pl031.c by Deepak Saxena <dsaxena@plexity.net> + * Copyright 2006 (c) MontaVista Software, Inc. + */ +#include <linux/init.h> +#include <linux/module.h> +#include <linux/rtc.h> +#include <linux/clk.h> +#include <linux/interrupt.h> +#include <linux/pm.h> +#include <linux/platform_device.h> +#include <linux/io.h> + +/* + * Registers in the COH 901 331 + */ +/* Alarm value 32bit (R/W) */ +#define COH901331_ALARM 0x00U +/* Used to set current time 32bit (R/W) */ +#define COH901331_SET_TIME 0x04U +/* Indication if current time is valid 32bit (R/-) */ +#define COH901331_VALID 0x08U +/* Read the current time 32bit (R/-) */ +#define COH901331_CUR_TIME 0x0cU +/* Event register for the "alarm" interrupt */ +#define COH901331_IRQ_EVENT 0x10U +/* Mask register for the "alarm" interrupt */ +#define COH901331_IRQ_MASK 0x14U +/* Force register for the "alarm" interrupt */ +#define COH901331_IRQ_FORCE 0x18U + +/* + * Reference to RTC block clock + * Notice that the frequent clk_enable()/clk_disable() on this + * clock is mainly to be able to turn on/off other clocks in the + * hierarchy as needed, the RTC clock is always on anyway. + */ +struct coh901331_port { + struct rtc_device *rtc; + struct clk *clk; + u32 phybase; + u32 physize; + void __iomem *virtbase; + int irq; +#ifdef CONFIG_PM + u32 irqmaskstore; +#endif +}; + +static irqreturn_t coh901331_interrupt(int irq, void *data) +{ + struct coh901331_port *rtap = data; + + clk_enable(rtap->clk); + /* Ack IRQ */ + writel(1, rtap->virtbase + COH901331_IRQ_EVENT); + /* + * Disable the interrupt. This is necessary because + * the RTC lives on a lower-clocked line and will + * not release the IRQ line until after a few (slower) + * clock cycles. The interrupt will be re-enabled when + * a new alarm is set anyway. + */ + writel(0, rtap->virtbase + COH901331_IRQ_MASK); + clk_disable(rtap->clk); + + /* Set alarm flag */ + rtc_update_irq(rtap->rtc, 1, RTC_AF); + + return IRQ_HANDLED; +} + +static int coh901331_read_time(struct device *dev, struct rtc_time *tm) +{ + struct coh901331_port *rtap = dev_get_drvdata(dev); + + clk_enable(rtap->clk); + /* Check if the time is valid */ + if (readl(rtap->virtbase + COH901331_VALID)) { + rtc_time_to_tm(readl(rtap->virtbase + COH901331_CUR_TIME), tm); + clk_disable(rtap->clk); + return rtc_valid_tm(tm); + } + clk_disable(rtap->clk); + return -EINVAL; +} + +static int coh901331_set_mmss(struct device *dev, unsigned long secs) +{ + struct coh901331_port *rtap = dev_get_drvdata(dev); + + clk_enable(rtap->clk); + writel(secs, rtap->virtbase + COH901331_SET_TIME); + clk_disable(rtap->clk); + + return 0; +} + +static int coh901331_read_alarm(struct device *dev, struct rtc_wkalrm *alarm) +{ + struct coh901331_port *rtap = dev_get_drvdata(dev); + + clk_enable(rtap->clk); + rtc_time_to_tm(readl(rtap->virtbase + COH901331_ALARM), &alarm->time); + alarm->pending = readl(rtap->virtbase + COH901331_IRQ_EVENT) & 1U; + alarm->enabled = readl(rtap->virtbase + COH901331_IRQ_MASK) & 1U; + clk_disable(rtap->clk); + + return 0; +} + +static int coh901331_set_alarm(struct device *dev, struct rtc_wkalrm *alarm) +{ + struct coh901331_port *rtap = dev_get_drvdata(dev); + unsigned long time; + + rtc_tm_to_time(&alarm->time, &time); + clk_enable(rtap->clk); + writel(time, rtap->virtbase + COH901331_ALARM); + writel(alarm->enabled, rtap->virtbase + COH901331_IRQ_MASK); + clk_disable(rtap->clk); + + return 0; +} + +static int coh901331_alarm_irq_enable(struct device *dev, unsigned int enabled) +{ + struct coh901331_port *rtap = dev_get_drvdata(dev); + + clk_enable(rtap->clk); + if (enabled) + writel(1, rtap->virtbase + COH901331_IRQ_MASK); + else + writel(0, rtap->virtbase + COH901331_IRQ_MASK); + clk_disable(rtap->clk); + + return 0; +} + +static struct rtc_class_ops coh901331_ops = { + .read_time = coh901331_read_time, + .set_mmss = coh901331_set_mmss, + .read_alarm = coh901331_read_alarm, + .set_alarm = coh901331_set_alarm, + .alarm_irq_enable = coh901331_alarm_irq_enable, +}; + +static int __exit coh901331_remove(struct platform_device *pdev) +{ + struct coh901331_port *rtap = dev_get_drvdata(&pdev->dev); + + if (rtap) { + free_irq(rtap->irq, rtap); + rtc_device_unregister(rtap->rtc); + clk_put(rtap->clk); + iounmap(rtap->virtbase); + release_mem_region(rtap->phybase, rtap->physize); + platform_set_drvdata(pdev, NULL); + kfree(rtap); + } + + return 0; +} + + +static int __init coh901331_probe(struct platform_device *pdev) +{ + int ret; + struct coh901331_port *rtap; + struct resource *res; + + rtap = kzalloc(sizeof(struct coh901331_port), GFP_KERNEL); + if (!rtap) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + ret = -ENOENT; + goto out_no_resource; + } + rtap->phybase = res->start; + rtap->physize = resource_size(res); + + if (request_mem_region(rtap->phybase, rtap->physize, + "rtc-coh901331") == NULL) { + ret = -EBUSY; + goto out_no_memregion; + } + + rtap->virtbase = ioremap(rtap->phybase, rtap->physize); + if (!rtap->virtbase) { + ret = -ENOMEM; + goto out_no_remap; + } + + rtap->irq = platform_get_irq(pdev, 0); + if (request_irq(rtap->irq, coh901331_interrupt, IRQF_DISABLED, + "RTC COH 901 331 Alarm", rtap)) { + ret = -EIO; + goto out_no_irq; + } + + rtap->clk = clk_get(&pdev->dev, NULL); + if (IS_ERR(rtap->clk)) { + ret = PTR_ERR(rtap->clk); + dev_err(&pdev->dev, "could not get clock\n"); + goto out_no_clk; + } + + /* We enable/disable the clock only to assure it works */ + ret = clk_enable(rtap->clk); + if (ret) { + dev_err(&pdev->dev, "could not enable clock\n"); + goto out_no_clk_enable; + } + clk_disable(rtap->clk); + + rtap->rtc = rtc_device_register("coh901331", &pdev->dev, &coh901331_ops, + THIS_MODULE); + if (IS_ERR(rtap->rtc)) { + ret = PTR_ERR(rtap->rtc); + goto out_no_rtc; + } + + platform_set_drvdata(pdev, rtap); + + return 0; + + out_no_rtc: + out_no_clk_enable: + clk_put(rtap->clk); + out_no_clk: + free_irq(rtap->irq, rtap); + out_no_irq: + iounmap(rtap->virtbase); + out_no_remap: + platform_set_drvdata(pdev, NULL); + out_no_memregion: + release_mem_region(rtap->phybase, SZ_4K); + out_no_resource: + kfree(rtap); + return ret; +} + +#ifdef CONFIG_PM +static int coh901331_suspend(struct platform_device *pdev, pm_message_t state) +{ + struct coh901331_port *rtap = dev_get_drvdata(&pdev->dev); + + /* + * If this RTC alarm will be used for waking the system up, + * don't disable it of course. Else we just disable the alarm + * and await suspension. + */ + if (device_may_wakeup(&pdev->dev)) { + enable_irq_wake(rtap->irq); + } else { + clk_enable(rtap->clk); + rtap->irqmaskstore = readl(rtap->virtbase + COH901331_IRQ_MASK); + writel(0, rtap->virtbase + COH901331_IRQ_MASK); + clk_disable(rtap->clk); + } + return 0; +} + +static int coh901331_resume(struct platform_device *pdev) +{ + struct coh901331_port *rtap = dev_get_drvdata(&pdev->dev); + + if (device_may_wakeup(&pdev->dev)) + disable_irq_wake(rtap->irq); + else + clk_enable(rtap->clk); + writel(rtap->irqmaskstore, rtap->virtbase + COH901331_IRQ_MASK); + clk_disable(rtap->clk); + return 0; +} +#else +#define coh901331_suspend NULL +#define coh901331_resume NULL +#endif + +static void coh901331_shutdown(struct platform_device *pdev) +{ + struct coh901331_port *rtap = dev_get_drvdata(&pdev->dev); + + clk_enable(rtap->clk); + writel(0, rtap->virtbase + COH901331_IRQ_MASK); + clk_disable(rtap->clk); +} + +static struct platform_driver coh901331_driver = { + .driver = { + .name = "rtc-coh901331", + .owner = THIS_MODULE, + }, + .remove = __exit_p(coh901331_remove), + .suspend = coh901331_suspend, + .resume = coh901331_resume, + .shutdown = coh901331_shutdown, +}; + +static int __init coh901331_init(void) +{ + return platform_driver_probe(&coh901331_driver, coh901331_probe); +} + +static void __exit coh901331_exit(void) +{ + platform_driver_unregister(&coh901331_driver); +} + +module_init(coh901331_init); +module_exit(coh901331_exit); + +MODULE_AUTHOR("Linus Walleij <linus.walleij@stericsson.com>"); +MODULE_DESCRIPTION("ST-Ericsson AB COH 901 331 RTC Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/rtc/rtc-dev.c b/drivers/rtc/rtc-dev.c index 8a11de9552cd..62227cd52410 100644 --- a/drivers/rtc/rtc-dev.c +++ b/drivers/rtc/rtc-dev.c @@ -13,6 +13,7 @@ #include <linux/module.h> #include <linux/rtc.h> +#include <linux/sched.h> #include "rtc-core.h" static dev_t rtc_devt; diff --git a/drivers/rtc/rtc-ds1305.c b/drivers/rtc/rtc-ds1305.c index 8f410e59d9f5..2736b11a1b1e 100644 --- a/drivers/rtc/rtc-ds1305.c +++ b/drivers/rtc/rtc-ds1305.c @@ -841,3 +841,4 @@ module_exit(ds1305_exit); MODULE_DESCRIPTION("RTC driver for DS1305 and DS1306 chips"); MODULE_LICENSE("GPL"); +MODULE_ALIAS("spi:rtc-ds1305"); diff --git a/drivers/rtc/rtc-ds1307.c b/drivers/rtc/rtc-ds1307.c index 47a93c022d91..eb99ee4fa0f5 100644 --- a/drivers/rtc/rtc-ds1307.c +++ b/drivers/rtc/rtc-ds1307.c @@ -896,8 +896,7 @@ read_rtc: return 0; exit_irq: - if (ds1307->rtc) - rtc_device_unregister(ds1307->rtc); + rtc_device_unregister(ds1307->rtc); exit_free: kfree(ds1307); return err; diff --git a/drivers/rtc/rtc-ds1390.c b/drivers/rtc/rtc-ds1390.c index e01b955db077..cdb705057091 100644 --- a/drivers/rtc/rtc-ds1390.c +++ b/drivers/rtc/rtc-ds1390.c @@ -189,3 +189,4 @@ module_exit(ds1390_exit); MODULE_DESCRIPTION("Dallas/Maxim DS1390/93/94 SPI RTC driver"); MODULE_AUTHOR("Mark Jackson <mpfj@mimc.co.uk>"); MODULE_LICENSE("GPL"); +MODULE_ALIAS("spi:rtc-ds1390"); diff --git a/drivers/rtc/rtc-ds3234.c b/drivers/rtc/rtc-ds3234.c index c51589ede5b7..a774ca35b5f7 100644 --- a/drivers/rtc/rtc-ds3234.c +++ b/drivers/rtc/rtc-ds3234.c @@ -188,3 +188,4 @@ module_exit(ds3234_exit); MODULE_DESCRIPTION("DS3234 SPI RTC driver"); MODULE_AUTHOR("Dennis Aberilla <denzzzhome@yahoo.com>"); MODULE_LICENSE("GPL"); +MODULE_ALIAS("spi:ds3234"); diff --git a/drivers/rtc/rtc-ep93xx.c b/drivers/rtc/rtc-ep93xx.c index 551332e4ed02..9da02d108b73 100644 --- a/drivers/rtc/rtc-ep93xx.c +++ b/drivers/rtc/rtc-ep93xx.c @@ -128,12 +128,16 @@ static int __init ep93xx_rtc_probe(struct platform_device *pdev) return -ENOMEM; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (res == NULL) - return -ENXIO; + if (res == NULL) { + err = -ENXIO; + goto fail_free; + } res = request_mem_region(res->start, resource_size(res), pdev->name); - if (res == NULL) - return -EBUSY; + if (res == NULL) { + err = -EBUSY; + goto fail_free; + } ep93xx_rtc->mmio_base = ioremap(res->start, resource_size(res)); if (ep93xx_rtc->mmio_base == NULL) { @@ -169,6 +173,8 @@ fail: pdev->dev.platform_data = NULL; } release_mem_region(res->start, resource_size(res)); +fail_free: + kfree(ep93xx_rtc); return err; } diff --git a/drivers/rtc/rtc-m41t94.c b/drivers/rtc/rtc-m41t94.c index c3a18c58daf6..c8c97a4169d4 100644 --- a/drivers/rtc/rtc-m41t94.c +++ b/drivers/rtc/rtc-m41t94.c @@ -171,3 +171,4 @@ module_exit(m41t94_exit); MODULE_AUTHOR("Kim B. Heino <Kim.Heino@bluegiga.com>"); MODULE_DESCRIPTION("Driver for ST M41T94 SPI RTC"); MODULE_LICENSE("GPL"); +MODULE_ALIAS("spi:rtc-m41t94"); diff --git a/drivers/rtc/rtc-max6902.c b/drivers/rtc/rtc-max6902.c index 36a8ea9ed8ba..657403ebd54a 100644 --- a/drivers/rtc/rtc-max6902.c +++ b/drivers/rtc/rtc-max6902.c @@ -175,3 +175,4 @@ module_exit(max6902_exit); MODULE_DESCRIPTION ("max6902 spi RTC driver"); MODULE_AUTHOR ("Raphael Assenat"); MODULE_LICENSE ("GPL"); +MODULE_ALIAS("spi:rtc-max6902"); diff --git a/drivers/rtc/rtc-mxc.c b/drivers/rtc/rtc-mxc.c new file mode 100644 index 000000000000..6bd5072d4eb7 --- /dev/null +++ b/drivers/rtc/rtc-mxc.c @@ -0,0 +1,507 @@ +/* + * Copyright 2004-2008 Freescale Semiconductor, Inc. All Rights Reserved. + * + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +#include <linux/io.h> +#include <linux/rtc.h> +#include <linux/module.h> +#include <linux/interrupt.h> +#include <linux/platform_device.h> +#include <linux/clk.h> + +#include <mach/hardware.h> + +#define RTC_INPUT_CLK_32768HZ (0x00 << 5) +#define RTC_INPUT_CLK_32000HZ (0x01 << 5) +#define RTC_INPUT_CLK_38400HZ (0x02 << 5) + +#define RTC_SW_BIT (1 << 0) +#define RTC_ALM_BIT (1 << 2) +#define RTC_1HZ_BIT (1 << 4) +#define RTC_2HZ_BIT (1 << 7) +#define RTC_SAM0_BIT (1 << 8) +#define RTC_SAM1_BIT (1 << 9) +#define RTC_SAM2_BIT (1 << 10) +#define RTC_SAM3_BIT (1 << 11) +#define RTC_SAM4_BIT (1 << 12) +#define RTC_SAM5_BIT (1 << 13) +#define RTC_SAM6_BIT (1 << 14) +#define RTC_SAM7_BIT (1 << 15) +#define PIT_ALL_ON (RTC_2HZ_BIT | RTC_SAM0_BIT | RTC_SAM1_BIT | \ + RTC_SAM2_BIT | RTC_SAM3_BIT | RTC_SAM4_BIT | \ + RTC_SAM5_BIT | RTC_SAM6_BIT | RTC_SAM7_BIT) + +#define RTC_ENABLE_BIT (1 << 7) + +#define MAX_PIE_NUM 9 +#define MAX_PIE_FREQ 512 +static const u32 PIE_BIT_DEF[MAX_PIE_NUM][2] = { + { 2, RTC_2HZ_BIT }, + { 4, RTC_SAM0_BIT }, + { 8, RTC_SAM1_BIT }, + { 16, RTC_SAM2_BIT }, + { 32, RTC_SAM3_BIT }, + { 64, RTC_SAM4_BIT }, + { 128, RTC_SAM5_BIT }, + { 256, RTC_SAM6_BIT }, + { MAX_PIE_FREQ, RTC_SAM7_BIT }, +}; + +/* Those are the bits from a classic RTC we want to mimic */ +#define RTC_IRQF 0x80 /* any of the following 3 is active */ +#define RTC_PF 0x40 /* Periodic interrupt */ +#define RTC_AF 0x20 /* Alarm interrupt */ +#define RTC_UF 0x10 /* Update interrupt for 1Hz RTC */ + +#define MXC_RTC_TIME 0 +#define MXC_RTC_ALARM 1 + +#define RTC_HOURMIN 0x00 /* 32bit rtc hour/min counter reg */ +#define RTC_SECOND 0x04 /* 32bit rtc seconds counter reg */ +#define RTC_ALRM_HM 0x08 /* 32bit rtc alarm hour/min reg */ +#define RTC_ALRM_SEC 0x0C /* 32bit rtc alarm seconds reg */ +#define RTC_RTCCTL 0x10 /* 32bit rtc control reg */ +#define RTC_RTCISR 0x14 /* 32bit rtc interrupt status reg */ +#define RTC_RTCIENR 0x18 /* 32bit rtc interrupt enable reg */ +#define RTC_STPWCH 0x1C /* 32bit rtc stopwatch min reg */ +#define RTC_DAYR 0x20 /* 32bit rtc days counter reg */ +#define RTC_DAYALARM 0x24 /* 32bit rtc day alarm reg */ +#define RTC_TEST1 0x28 /* 32bit rtc test reg 1 */ +#define RTC_TEST2 0x2C /* 32bit rtc test reg 2 */ +#define RTC_TEST3 0x30 /* 32bit rtc test reg 3 */ + +struct rtc_plat_data { + struct rtc_device *rtc; + void __iomem *ioaddr; + int irq; + struct clk *clk; + unsigned int irqen; + int alrm_sec; + int alrm_min; + int alrm_hour; + int alrm_mday; + struct timespec mxc_rtc_delta; + struct rtc_time g_rtc_alarm; +}; + +/* + * This function is used to obtain the RTC time or the alarm value in + * second. + */ +static u32 get_alarm_or_time(struct device *dev, int time_alarm) +{ + struct platform_device *pdev = to_platform_device(dev); + struct rtc_plat_data *pdata = platform_get_drvdata(pdev); + void __iomem *ioaddr = pdata->ioaddr; + u32 day = 0, hr = 0, min = 0, sec = 0, hr_min = 0; + + switch (time_alarm) { + case MXC_RTC_TIME: + day = readw(ioaddr + RTC_DAYR); + hr_min = readw(ioaddr + RTC_HOURMIN); + sec = readw(ioaddr + RTC_SECOND); + break; + case MXC_RTC_ALARM: + day = readw(ioaddr + RTC_DAYALARM); + hr_min = readw(ioaddr + RTC_ALRM_HM) & 0xffff; + sec = readw(ioaddr + RTC_ALRM_SEC); + break; + } + + hr = hr_min >> 8; + min = hr_min & 0xff; + + return (((day * 24 + hr) * 60) + min) * 60 + sec; +} + +/* + * This function sets the RTC alarm value or the time value. + */ +static void set_alarm_or_time(struct device *dev, int time_alarm, u32 time) +{ + u32 day, hr, min, sec, temp; + struct platform_device *pdev = to_platform_device(dev); + struct rtc_plat_data *pdata = platform_get_drvdata(pdev); + void __iomem *ioaddr = pdata->ioaddr; + + day = time / 86400; + time -= day * 86400; + + /* time is within a day now */ + hr = time / 3600; + time -= hr * 3600; + + /* time is within an hour now */ + min = time / 60; + sec = time - min * 60; + + temp = (hr << 8) + min; + + switch (time_alarm) { + case MXC_RTC_TIME: + writew(day, ioaddr + RTC_DAYR); + writew(sec, ioaddr + RTC_SECOND); + writew(temp, ioaddr + RTC_HOURMIN); + break; + case MXC_RTC_ALARM: + writew(day, ioaddr + RTC_DAYALARM); + writew(sec, ioaddr + RTC_ALRM_SEC); + writew(temp, ioaddr + RTC_ALRM_HM); + break; + } +} + +/* + * This function updates the RTC alarm registers and then clears all the + * interrupt status bits. + */ +static int rtc_update_alarm(struct device *dev, struct rtc_time *alrm) +{ + struct rtc_time alarm_tm, now_tm; + unsigned long now, time; + int ret; + struct platform_device *pdev = to_platform_device(dev); + struct rtc_plat_data *pdata = platform_get_drvdata(pdev); + void __iomem *ioaddr = pdata->ioaddr; + + now = get_alarm_or_time(dev, MXC_RTC_TIME); + rtc_time_to_tm(now, &now_tm); + alarm_tm.tm_year = now_tm.tm_year; + alarm_tm.tm_mon = now_tm.tm_mon; + alarm_tm.tm_mday = now_tm.tm_mday; + alarm_tm.tm_hour = alrm->tm_hour; + alarm_tm.tm_min = alrm->tm_min; + alarm_tm.tm_sec = alrm->tm_sec; + rtc_tm_to_time(&now_tm, &now); + rtc_tm_to_time(&alarm_tm, &time); + + if (time < now) { + time += 60 * 60 * 24; + rtc_time_to_tm(time, &alarm_tm); + } + + ret = rtc_tm_to_time(&alarm_tm, &time); + + /* clear all the interrupt status bits */ + writew(readw(ioaddr + RTC_RTCISR), ioaddr + RTC_RTCISR); + set_alarm_or_time(dev, MXC_RTC_ALARM, time); + + return ret; +} + +/* This function is the RTC interrupt service routine. */ +static irqreturn_t mxc_rtc_interrupt(int irq, void *dev_id) +{ + struct platform_device *pdev = dev_id; + struct rtc_plat_data *pdata = platform_get_drvdata(pdev); + void __iomem *ioaddr = pdata->ioaddr; + u32 status; + u32 events = 0; + + spin_lock_irq(&pdata->rtc->irq_lock); + status = readw(ioaddr + RTC_RTCISR) & readw(ioaddr + RTC_RTCIENR); + /* clear interrupt sources */ + writew(status, ioaddr + RTC_RTCISR); + + /* clear alarm interrupt if it has occurred */ + if (status & RTC_ALM_BIT) + status &= ~RTC_ALM_BIT; + + /* update irq data & counter */ + if (status & RTC_ALM_BIT) + events |= (RTC_AF | RTC_IRQF); + + if (status & RTC_1HZ_BIT) + events |= (RTC_UF | RTC_IRQF); + + if (status & PIT_ALL_ON) + events |= (RTC_PF | RTC_IRQF); + + if ((status & RTC_ALM_BIT) && rtc_valid_tm(&pdata->g_rtc_alarm)) + rtc_update_alarm(&pdev->dev, &pdata->g_rtc_alarm); + + rtc_update_irq(pdata->rtc, 1, events); + spin_unlock_irq(&pdata->rtc->irq_lock); + + return IRQ_HANDLED; +} + +/* + * Clear all interrupts and release the IRQ + */ +static void mxc_rtc_release(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct rtc_plat_data *pdata = platform_get_drvdata(pdev); + void __iomem *ioaddr = pdata->ioaddr; + + spin_lock_irq(&pdata->rtc->irq_lock); + + /* Disable all rtc interrupts */ + writew(0, ioaddr + RTC_RTCIENR); + + /* Clear all interrupt status */ + writew(0xffffffff, ioaddr + RTC_RTCISR); + + spin_unlock_irq(&pdata->rtc->irq_lock); +} + +static void mxc_rtc_irq_enable(struct device *dev, unsigned int bit, + unsigned int enabled) +{ + struct platform_device *pdev = to_platform_device(dev); + struct rtc_plat_data *pdata = platform_get_drvdata(pdev); + void __iomem *ioaddr = pdata->ioaddr; + u32 reg; + + spin_lock_irq(&pdata->rtc->irq_lock); + reg = readw(ioaddr + RTC_RTCIENR); + + if (enabled) + reg |= bit; + else + reg &= ~bit; + + writew(reg, ioaddr + RTC_RTCIENR); + spin_unlock_irq(&pdata->rtc->irq_lock); +} + +static int mxc_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled) +{ + mxc_rtc_irq_enable(dev, RTC_ALM_BIT, enabled); + return 0; +} + +static int mxc_rtc_update_irq_enable(struct device *dev, unsigned int enabled) +{ + mxc_rtc_irq_enable(dev, RTC_1HZ_BIT, enabled); + return 0; +} + +/* + * This function reads the current RTC time into tm in Gregorian date. + */ +static int mxc_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + u32 val; + + /* Avoid roll-over from reading the different registers */ + do { + val = get_alarm_or_time(dev, MXC_RTC_TIME); + } while (val != get_alarm_or_time(dev, MXC_RTC_TIME)); + + rtc_time_to_tm(val, tm); + + return 0; +} + +/* + * This function sets the internal RTC time based on tm in Gregorian date. + */ +static int mxc_rtc_set_mmss(struct device *dev, unsigned long time) +{ + /* Avoid roll-over from reading the different registers */ + do { + set_alarm_or_time(dev, MXC_RTC_TIME, time); + } while (time != get_alarm_or_time(dev, MXC_RTC_TIME)); + + return 0; +} + +/* + * This function reads the current alarm value into the passed in 'alrm' + * argument. It updates the alrm's pending field value based on the whether + * an alarm interrupt occurs or not. + */ +static int mxc_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct platform_device *pdev = to_platform_device(dev); + struct rtc_plat_data *pdata = platform_get_drvdata(pdev); + void __iomem *ioaddr = pdata->ioaddr; + + rtc_time_to_tm(get_alarm_or_time(dev, MXC_RTC_ALARM), &alrm->time); + alrm->pending = ((readw(ioaddr + RTC_RTCISR) & RTC_ALM_BIT)) ? 1 : 0; + + return 0; +} + +/* + * This function sets the RTC alarm based on passed in alrm. + */ +static int mxc_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct platform_device *pdev = to_platform_device(dev); + struct rtc_plat_data *pdata = platform_get_drvdata(pdev); + int ret; + + if (rtc_valid_tm(&alrm->time)) { + if (alrm->time.tm_sec > 59 || + alrm->time.tm_hour > 23 || + alrm->time.tm_min > 59) + return -EINVAL; + + ret = rtc_update_alarm(dev, &alrm->time); + } else { + ret = rtc_valid_tm(&alrm->time); + if (ret) + return ret; + + ret = rtc_update_alarm(dev, &alrm->time); + } + + if (ret) + return ret; + + memcpy(&pdata->g_rtc_alarm, &alrm->time, sizeof(struct rtc_time)); + mxc_rtc_irq_enable(dev, RTC_ALM_BIT, alrm->enabled); + + return 0; +} + +/* RTC layer */ +static struct rtc_class_ops mxc_rtc_ops = { + .release = mxc_rtc_release, + .read_time = mxc_rtc_read_time, + .set_mmss = mxc_rtc_set_mmss, + .read_alarm = mxc_rtc_read_alarm, + .set_alarm = mxc_rtc_set_alarm, + .alarm_irq_enable = mxc_rtc_alarm_irq_enable, + .update_irq_enable = mxc_rtc_update_irq_enable, +}; + +static int __init mxc_rtc_probe(struct platform_device *pdev) +{ + struct clk *clk; + struct resource *res; + struct rtc_device *rtc; + struct rtc_plat_data *pdata = NULL; + u32 reg; + int ret, rate; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + return -ENODEV; + + pdata = kzalloc(sizeof(*pdata), GFP_KERNEL); + if (!pdata) + return -ENOMEM; + + pdata->ioaddr = ioremap(res->start, resource_size(res)); + + clk = clk_get(&pdev->dev, "ckil"); + if (IS_ERR(clk)) + return PTR_ERR(clk); + + rate = clk_get_rate(clk); + clk_put(clk); + + if (rate == 32768) + reg = RTC_INPUT_CLK_32768HZ; + else if (rate == 32000) + reg = RTC_INPUT_CLK_32000HZ; + else if (rate == 38400) + reg = RTC_INPUT_CLK_38400HZ; + else { + dev_err(&pdev->dev, "rtc clock is not valid (%lu)\n", + clk_get_rate(clk)); + ret = -EINVAL; + goto exit_free_pdata; + } + + reg |= RTC_ENABLE_BIT; + writew(reg, (pdata->ioaddr + RTC_RTCCTL)); + if (((readw(pdata->ioaddr + RTC_RTCCTL)) & RTC_ENABLE_BIT) == 0) { + dev_err(&pdev->dev, "hardware module can't be enabled!\n"); + ret = -EIO; + goto exit_free_pdata; + } + + pdata->clk = clk_get(&pdev->dev, "rtc"); + if (IS_ERR(pdata->clk)) { + dev_err(&pdev->dev, "unable to get clock!\n"); + ret = PTR_ERR(pdata->clk); + goto exit_free_pdata; + } + + clk_enable(pdata->clk); + + rtc = rtc_device_register(pdev->name, &pdev->dev, &mxc_rtc_ops, + THIS_MODULE); + if (IS_ERR(rtc)) { + ret = PTR_ERR(rtc); + goto exit_put_clk; + } + + pdata->rtc = rtc; + platform_set_drvdata(pdev, pdata); + + /* Configure and enable the RTC */ + pdata->irq = platform_get_irq(pdev, 0); + + if (pdata->irq >= 0 && + request_irq(pdata->irq, mxc_rtc_interrupt, IRQF_SHARED, + pdev->name, pdev) < 0) { + dev_warn(&pdev->dev, "interrupt not available.\n"); + pdata->irq = -1; + } + + return 0; + +exit_put_clk: + clk_put(pdata->clk); + +exit_free_pdata: + kfree(pdata); + + return ret; +} + +static int __exit mxc_rtc_remove(struct platform_device *pdev) +{ + struct rtc_plat_data *pdata = platform_get_drvdata(pdev); + + rtc_device_unregister(pdata->rtc); + + if (pdata->irq >= 0) + free_irq(pdata->irq, pdev); + + clk_disable(pdata->clk); + clk_put(pdata->clk); + kfree(pdata); + platform_set_drvdata(pdev, NULL); + + return 0; +} + +static struct platform_driver mxc_rtc_driver = { + .driver = { + .name = "mxc_rtc", + .owner = THIS_MODULE, + }, + .remove = __exit_p(mxc_rtc_remove), +}; + +static int __init mxc_rtc_init(void) +{ + return platform_driver_probe(&mxc_rtc_driver, mxc_rtc_probe); +} + +static void __exit mxc_rtc_exit(void) +{ + platform_driver_unregister(&mxc_rtc_driver); +} + +module_init(mxc_rtc_init); +module_exit(mxc_rtc_exit); + +MODULE_AUTHOR("Daniel Mack <daniel@caiaq.de>"); +MODULE_DESCRIPTION("RTC driver for Freescale MXC"); +MODULE_LICENSE("GPL"); + diff --git a/drivers/rtc/rtc-pcap.c b/drivers/rtc/rtc-pcap.c new file mode 100644 index 000000000000..a99c28992d21 --- /dev/null +++ b/drivers/rtc/rtc-pcap.c @@ -0,0 +1,224 @@ +/* + * pcap rtc code for Motorola EZX phones + * + * Copyright (c) 2008 guiming zhuo <gmzhuo@gmail.com> + * Copyright (c) 2009 Daniel Ribeiro <drwyrm@gmail.com> + * + * Based on Motorola's rtc.c Copyright (c) 2003-2005 Motorola + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/mfd/ezx-pcap.h> +#include <linux/rtc.h> +#include <linux/platform_device.h> + +struct pcap_rtc { + struct pcap_chip *pcap; + struct rtc_device *rtc; +}; + +static irqreturn_t pcap_rtc_irq(int irq, void *_pcap_rtc) +{ + struct pcap_rtc *pcap_rtc = _pcap_rtc; + unsigned long rtc_events; + + if (irq == pcap_to_irq(pcap_rtc->pcap, PCAP_IRQ_1HZ)) + rtc_events = RTC_IRQF | RTC_UF; + else if (irq == pcap_to_irq(pcap_rtc->pcap, PCAP_IRQ_TODA)) + rtc_events = RTC_IRQF | RTC_AF; + else + rtc_events = 0; + + rtc_update_irq(pcap_rtc->rtc, 1, rtc_events); + return IRQ_HANDLED; +} + +static int pcap_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct platform_device *pdev = to_platform_device(dev); + struct pcap_rtc *pcap_rtc = platform_get_drvdata(pdev); + struct rtc_time *tm = &alrm->time; + unsigned long secs; + u32 tod; /* time of day, seconds since midnight */ + u32 days; /* days since 1/1/1970 */ + + ezx_pcap_read(pcap_rtc->pcap, PCAP_REG_RTC_TODA, &tod); + secs = tod & PCAP_RTC_TOD_MASK; + + ezx_pcap_read(pcap_rtc->pcap, PCAP_REG_RTC_DAYA, &days); + secs += (days & PCAP_RTC_DAY_MASK) * SEC_PER_DAY; + + rtc_time_to_tm(secs, tm); + + return 0; +} + +static int pcap_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct platform_device *pdev = to_platform_device(dev); + struct pcap_rtc *pcap_rtc = platform_get_drvdata(pdev); + struct rtc_time *tm = &alrm->time; + unsigned long secs; + u32 tod, days; + + rtc_tm_to_time(tm, &secs); + + tod = secs % SEC_PER_DAY; + ezx_pcap_write(pcap_rtc->pcap, PCAP_REG_RTC_TODA, tod); + + days = secs / SEC_PER_DAY; + ezx_pcap_write(pcap_rtc->pcap, PCAP_REG_RTC_DAYA, days); + + return 0; +} + +static int pcap_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + struct platform_device *pdev = to_platform_device(dev); + struct pcap_rtc *pcap_rtc = platform_get_drvdata(pdev); + unsigned long secs; + u32 tod, days; + + ezx_pcap_read(pcap_rtc->pcap, PCAP_REG_RTC_TOD, &tod); + secs = tod & PCAP_RTC_TOD_MASK; + + ezx_pcap_read(pcap_rtc->pcap, PCAP_REG_RTC_DAY, &days); + secs += (days & PCAP_RTC_DAY_MASK) * SEC_PER_DAY; + + rtc_time_to_tm(secs, tm); + + return rtc_valid_tm(tm); +} + +static int pcap_rtc_set_mmss(struct device *dev, unsigned long secs) +{ + struct platform_device *pdev = to_platform_device(dev); + struct pcap_rtc *pcap_rtc = platform_get_drvdata(pdev); + u32 tod, days; + + tod = secs % SEC_PER_DAY; + ezx_pcap_write(pcap_rtc->pcap, PCAP_REG_RTC_TOD, tod); + + days = secs / SEC_PER_DAY; + ezx_pcap_write(pcap_rtc->pcap, PCAP_REG_RTC_DAY, days); + + return 0; +} + +static int pcap_rtc_irq_enable(struct device *dev, int pirq, unsigned int en) +{ + struct platform_device *pdev = to_platform_device(dev); + struct pcap_rtc *pcap_rtc = platform_get_drvdata(pdev); + + if (en) + enable_irq(pcap_to_irq(pcap_rtc->pcap, pirq)); + else + disable_irq(pcap_to_irq(pcap_rtc->pcap, pirq)); + + return 0; +} + +static int pcap_rtc_alarm_irq_enable(struct device *dev, unsigned int en) +{ + return pcap_rtc_irq_enable(dev, PCAP_IRQ_TODA, en); +} + +static int pcap_rtc_update_irq_enable(struct device *dev, unsigned int en) +{ + return pcap_rtc_irq_enable(dev, PCAP_IRQ_1HZ, en); +} + +static const struct rtc_class_ops pcap_rtc_ops = { + .read_time = pcap_rtc_read_time, + .read_alarm = pcap_rtc_read_alarm, + .set_alarm = pcap_rtc_set_alarm, + .set_mmss = pcap_rtc_set_mmss, + .alarm_irq_enable = pcap_rtc_alarm_irq_enable, + .update_irq_enable = pcap_rtc_update_irq_enable, +}; + +static int __devinit pcap_rtc_probe(struct platform_device *pdev) +{ + struct pcap_rtc *pcap_rtc; + int timer_irq, alarm_irq; + int err = -ENOMEM; + + pcap_rtc = kmalloc(sizeof(struct pcap_rtc), GFP_KERNEL); + if (!pcap_rtc) + return err; + + pcap_rtc->pcap = dev_get_drvdata(pdev->dev.parent); + + pcap_rtc->rtc = rtc_device_register("pcap", &pdev->dev, + &pcap_rtc_ops, THIS_MODULE); + if (IS_ERR(pcap_rtc->rtc)) { + err = PTR_ERR(pcap_rtc->rtc); + goto fail_rtc; + } + + platform_set_drvdata(pdev, pcap_rtc); + + timer_irq = pcap_to_irq(pcap_rtc->pcap, PCAP_IRQ_1HZ); + alarm_irq = pcap_to_irq(pcap_rtc->pcap, PCAP_IRQ_TODA); + + err = request_irq(timer_irq, pcap_rtc_irq, 0, "RTC Timer", pcap_rtc); + if (err) + goto fail_timer; + + err = request_irq(alarm_irq, pcap_rtc_irq, 0, "RTC Alarm", pcap_rtc); + if (err) + goto fail_alarm; + + return 0; +fail_alarm: + free_irq(timer_irq, pcap_rtc); +fail_timer: + rtc_device_unregister(pcap_rtc->rtc); +fail_rtc: + kfree(pcap_rtc); + return err; +} + +static int __devexit pcap_rtc_remove(struct platform_device *pdev) +{ + struct pcap_rtc *pcap_rtc = platform_get_drvdata(pdev); + + free_irq(pcap_to_irq(pcap_rtc->pcap, PCAP_IRQ_1HZ), pcap_rtc); + free_irq(pcap_to_irq(pcap_rtc->pcap, PCAP_IRQ_TODA), pcap_rtc); + rtc_device_unregister(pcap_rtc->rtc); + kfree(pcap_rtc); + + return 0; +} + +static struct platform_driver pcap_rtc_driver = { + .remove = __devexit_p(pcap_rtc_remove), + .driver = { + .name = "pcap-rtc", + .owner = THIS_MODULE, + }, +}; + +static int __init rtc_pcap_init(void) +{ + return platform_driver_probe(&pcap_rtc_driver, pcap_rtc_probe); +} + +static void __exit rtc_pcap_exit(void) +{ + platform_driver_unregister(&pcap_rtc_driver); +} + +module_init(rtc_pcap_init); +module_exit(rtc_pcap_exit); + +MODULE_DESCRIPTION("Motorola pcap rtc driver"); +MODULE_AUTHOR("guiming zhuo <gmzhuo@gmail.com>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/rtc/rtc-pcf2123.c b/drivers/rtc/rtc-pcf2123.c new file mode 100644 index 000000000000..e75df9d50e27 --- /dev/null +++ b/drivers/rtc/rtc-pcf2123.c @@ -0,0 +1,364 @@ +/* + * An SPI driver for the Philips PCF2123 RTC + * Copyright 2009 Cyber Switching, Inc. + * + * Author: Chris Verges <chrisv@cyberswitching.com> + * Maintainers: http://www.cyberswitching.com + * + * based on the RS5C348 driver in this same directory. + * + * Thanks to Christian Pellegrin <chripell@fsfe.org> for + * the sysfs contributions to this driver. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Please note that the CS is active high, so platform data + * should look something like: + * + * static struct spi_board_info ek_spi_devices[] = { + * ... + * { + * .modalias = "rtc-pcf2123", + * .chip_select = 1, + * .controller_data = (void *)AT91_PIN_PA10, + * .max_speed_hz = 1000 * 1000, + * .mode = SPI_CS_HIGH, + * .bus_num = 0, + * }, + * ... + *}; + * + */ + +#include <linux/bcd.h> +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/errno.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/string.h> +#include <linux/rtc.h> +#include <linux/spi/spi.h> + +#define DRV_VERSION "0.6" + +#define PCF2123_REG_CTRL1 (0x00) /* Control Register 1 */ +#define PCF2123_REG_CTRL2 (0x01) /* Control Register 2 */ +#define PCF2123_REG_SC (0x02) /* datetime */ +#define PCF2123_REG_MN (0x03) +#define PCF2123_REG_HR (0x04) +#define PCF2123_REG_DM (0x05) +#define PCF2123_REG_DW (0x06) +#define PCF2123_REG_MO (0x07) +#define PCF2123_REG_YR (0x08) + +#define PCF2123_SUBADDR (1 << 4) +#define PCF2123_WRITE ((0 << 7) | PCF2123_SUBADDR) +#define PCF2123_READ ((1 << 7) | PCF2123_SUBADDR) + +static struct spi_driver pcf2123_driver; + +struct pcf2123_sysfs_reg { + struct device_attribute attr; + char name[2]; +}; + +struct pcf2123_plat_data { + struct rtc_device *rtc; + struct pcf2123_sysfs_reg regs[16]; +}; + +/* + * Causes a 30 nanosecond delay to ensure that the PCF2123 chip select + * is released properly after an SPI write. This function should be + * called after EVERY read/write call over SPI. + */ +static inline void pcf2123_delay_trec(void) +{ + ndelay(30); +} + +static ssize_t pcf2123_show(struct device *dev, struct device_attribute *attr, + char *buffer) +{ + struct spi_device *spi = to_spi_device(dev); + struct pcf2123_sysfs_reg *r; + u8 txbuf[1], rxbuf[1]; + unsigned long reg; + int ret; + + r = container_of(attr, struct pcf2123_sysfs_reg, attr); + + if (strict_strtoul(r->name, 16, ®)) + return -EINVAL; + + txbuf[0] = PCF2123_READ | reg; + ret = spi_write_then_read(spi, txbuf, 1, rxbuf, 1); + if (ret < 0) + return -EIO; + pcf2123_delay_trec(); + return sprintf(buffer, "0x%x\n", rxbuf[0]); +} + +static ssize_t pcf2123_store(struct device *dev, struct device_attribute *attr, + const char *buffer, size_t count) { + struct spi_device *spi = to_spi_device(dev); + struct pcf2123_sysfs_reg *r; + u8 txbuf[2]; + unsigned long reg; + unsigned long val; + + int ret; + + r = container_of(attr, struct pcf2123_sysfs_reg, attr); + + if (strict_strtoul(r->name, 16, ®) + || strict_strtoul(buffer, 10, &val)) + return -EINVAL; + + txbuf[0] = PCF2123_WRITE | reg; + txbuf[1] = val; + ret = spi_write(spi, txbuf, sizeof(txbuf)); + if (ret < 0) + return -EIO; + pcf2123_delay_trec(); + return count; +} + +static int pcf2123_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + struct spi_device *spi = to_spi_device(dev); + u8 txbuf[1], rxbuf[7]; + int ret; + + txbuf[0] = PCF2123_READ | PCF2123_REG_SC; + ret = spi_write_then_read(spi, txbuf, sizeof(txbuf), + rxbuf, sizeof(rxbuf)); + if (ret < 0) + return ret; + pcf2123_delay_trec(); + + tm->tm_sec = bcd2bin(rxbuf[0] & 0x7F); + tm->tm_min = bcd2bin(rxbuf[1] & 0x7F); + tm->tm_hour = bcd2bin(rxbuf[2] & 0x3F); /* rtc hr 0-23 */ + tm->tm_mday = bcd2bin(rxbuf[3] & 0x3F); + tm->tm_wday = rxbuf[4] & 0x07; + tm->tm_mon = bcd2bin(rxbuf[5] & 0x1F) - 1; /* rtc mn 1-12 */ + tm->tm_year = bcd2bin(rxbuf[6]); + if (tm->tm_year < 70) + tm->tm_year += 100; /* assume we are in 1970...2069 */ + + dev_dbg(dev, "%s: tm is secs=%d, mins=%d, hours=%d, " + "mday=%d, mon=%d, year=%d, wday=%d\n", + __func__, + tm->tm_sec, tm->tm_min, tm->tm_hour, + tm->tm_mday, tm->tm_mon, tm->tm_year, tm->tm_wday); + + /* the clock can give out invalid datetime, but we cannot return + * -EINVAL otherwise hwclock will refuse to set the time on bootup. + */ + if (rtc_valid_tm(tm) < 0) + dev_err(dev, "retrieved date/time is not valid.\n"); + + return 0; +} + +static int pcf2123_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + struct spi_device *spi = to_spi_device(dev); + u8 txbuf[8]; + int ret; + + dev_dbg(dev, "%s: tm is secs=%d, mins=%d, hours=%d, " + "mday=%d, mon=%d, year=%d, wday=%d\n", + __func__, + tm->tm_sec, tm->tm_min, tm->tm_hour, + tm->tm_mday, tm->tm_mon, tm->tm_year, tm->tm_wday); + + /* Stop the counter first */ + txbuf[0] = PCF2123_WRITE | PCF2123_REG_CTRL1; + txbuf[1] = 0x20; + ret = spi_write(spi, txbuf, 2); + if (ret < 0) + return ret; + pcf2123_delay_trec(); + + /* Set the new time */ + txbuf[0] = PCF2123_WRITE | PCF2123_REG_SC; + txbuf[1] = bin2bcd(tm->tm_sec & 0x7F); + txbuf[2] = bin2bcd(tm->tm_min & 0x7F); + txbuf[3] = bin2bcd(tm->tm_hour & 0x3F); + txbuf[4] = bin2bcd(tm->tm_mday & 0x3F); + txbuf[5] = tm->tm_wday & 0x07; + txbuf[6] = bin2bcd((tm->tm_mon + 1) & 0x1F); /* rtc mn 1-12 */ + txbuf[7] = bin2bcd(tm->tm_year < 100 ? tm->tm_year : tm->tm_year - 100); + + ret = spi_write(spi, txbuf, sizeof(txbuf)); + if (ret < 0) + return ret; + pcf2123_delay_trec(); + + /* Start the counter */ + txbuf[0] = PCF2123_WRITE | PCF2123_REG_CTRL1; + txbuf[1] = 0x00; + ret = spi_write(spi, txbuf, 2); + if (ret < 0) + return ret; + pcf2123_delay_trec(); + + return 0; +} + +static const struct rtc_class_ops pcf2123_rtc_ops = { + .read_time = pcf2123_rtc_read_time, + .set_time = pcf2123_rtc_set_time, +}; + +static int __devinit pcf2123_probe(struct spi_device *spi) +{ + struct rtc_device *rtc; + struct pcf2123_plat_data *pdata; + u8 txbuf[2], rxbuf[2]; + int ret, i; + + pdata = kzalloc(sizeof(struct pcf2123_plat_data), GFP_KERNEL); + if (!pdata) + return -ENOMEM; + spi->dev.platform_data = pdata; + + /* Send a software reset command */ + txbuf[0] = PCF2123_WRITE | PCF2123_REG_CTRL1; + txbuf[1] = 0x58; + dev_dbg(&spi->dev, "resetting RTC (0x%02X 0x%02X)\n", + txbuf[0], txbuf[1]); + ret = spi_write(spi, txbuf, 2 * sizeof(u8)); + if (ret < 0) + goto kfree_exit; + pcf2123_delay_trec(); + + /* Stop the counter */ + txbuf[0] = PCF2123_WRITE | PCF2123_REG_CTRL1; + txbuf[1] = 0x20; + dev_dbg(&spi->dev, "stopping RTC (0x%02X 0x%02X)\n", + txbuf[0], txbuf[1]); + ret = spi_write(spi, txbuf, 2 * sizeof(u8)); + if (ret < 0) + goto kfree_exit; + pcf2123_delay_trec(); + + /* See if the counter was actually stopped */ + txbuf[0] = PCF2123_READ | PCF2123_REG_CTRL1; + dev_dbg(&spi->dev, "checking for presence of RTC (0x%02X)\n", + txbuf[0]); + ret = spi_write_then_read(spi, txbuf, 1 * sizeof(u8), + rxbuf, 2 * sizeof(u8)); + dev_dbg(&spi->dev, "received data from RTC (0x%02X 0x%02X)\n", + rxbuf[0], rxbuf[1]); + if (ret < 0) + goto kfree_exit; + pcf2123_delay_trec(); + + if (!(rxbuf[0] & 0x20)) { + dev_err(&spi->dev, "chip not found\n"); + goto kfree_exit; + } + + dev_info(&spi->dev, "chip found, driver version " DRV_VERSION "\n"); + dev_info(&spi->dev, "spiclk %u KHz.\n", + (spi->max_speed_hz + 500) / 1000); + + /* Start the counter */ + txbuf[0] = PCF2123_WRITE | PCF2123_REG_CTRL1; + txbuf[1] = 0x00; + ret = spi_write(spi, txbuf, sizeof(txbuf)); + if (ret < 0) + goto kfree_exit; + pcf2123_delay_trec(); + + /* Finalize the initialization */ + rtc = rtc_device_register(pcf2123_driver.driver.name, &spi->dev, + &pcf2123_rtc_ops, THIS_MODULE); + + if (IS_ERR(rtc)) { + dev_err(&spi->dev, "failed to register.\n"); + ret = PTR_ERR(rtc); + goto kfree_exit; + } + + pdata->rtc = rtc; + + for (i = 0; i < 16; i++) { + sprintf(pdata->regs[i].name, "%1x", i); + pdata->regs[i].attr.attr.mode = S_IRUGO | S_IWUSR; + pdata->regs[i].attr.attr.name = pdata->regs[i].name; + pdata->regs[i].attr.show = pcf2123_show; + pdata->regs[i].attr.store = pcf2123_store; + ret = device_create_file(&spi->dev, &pdata->regs[i].attr); + if (ret) { + dev_err(&spi->dev, "Unable to create sysfs %s\n", + pdata->regs[i].name); + goto sysfs_exit; + } + } + + return 0; + +sysfs_exit: + for (i--; i >= 0; i--) + device_remove_file(&spi->dev, &pdata->regs[i].attr); + +kfree_exit: + kfree(pdata); + spi->dev.platform_data = NULL; + return ret; +} + +static int pcf2123_remove(struct spi_device *spi) +{ + struct pcf2123_plat_data *pdata = spi->dev.platform_data; + int i; + + if (pdata) { + struct rtc_device *rtc = pdata->rtc; + + if (rtc) + rtc_device_unregister(rtc); + for (i = 0; i < 16; i++) + if (pdata->regs[i].name[0]) + device_remove_file(&spi->dev, + &pdata->regs[i].attr); + kfree(pdata); + } + + return 0; +} + +static struct spi_driver pcf2123_driver = { + .driver = { + .name = "rtc-pcf2123", + .bus = &spi_bus_type, + .owner = THIS_MODULE, + }, + .probe = pcf2123_probe, + .remove = __devexit_p(pcf2123_remove), +}; + +static int __init pcf2123_init(void) +{ + return spi_register_driver(&pcf2123_driver); +} + +static void __exit pcf2123_exit(void) +{ + spi_unregister_driver(&pcf2123_driver); +} + +MODULE_AUTHOR("Chris Verges <chrisv@cyberswitching.com>"); +MODULE_DESCRIPTION("NXP PCF2123 RTC driver"); +MODULE_LICENSE("GPL"); +MODULE_VERSION(DRV_VERSION); + +module_init(pcf2123_init); +module_exit(pcf2123_exit); diff --git a/drivers/rtc/rtc-pcf50633.c b/drivers/rtc/rtc-pcf50633.c index f4dd87e29075..33a10c47260e 100644 --- a/drivers/rtc/rtc-pcf50633.c +++ b/drivers/rtc/rtc-pcf50633.c @@ -70,7 +70,7 @@ static void pcf2rtc_time(struct rtc_time *rtc, struct pcf50633_time *pcf) rtc->tm_hour = bcd2bin(pcf->time[PCF50633_TI_HOUR]); rtc->tm_wday = bcd2bin(pcf->time[PCF50633_TI_WKDAY]); rtc->tm_mday = bcd2bin(pcf->time[PCF50633_TI_DAY]); - rtc->tm_mon = bcd2bin(pcf->time[PCF50633_TI_MONTH]); + rtc->tm_mon = bcd2bin(pcf->time[PCF50633_TI_MONTH]) - 1; rtc->tm_year = bcd2bin(pcf->time[PCF50633_TI_YEAR]) + 100; } @@ -81,7 +81,7 @@ static void rtc2pcf_time(struct pcf50633_time *pcf, struct rtc_time *rtc) pcf->time[PCF50633_TI_HOUR] = bin2bcd(rtc->tm_hour); pcf->time[PCF50633_TI_WKDAY] = bin2bcd(rtc->tm_wday); pcf->time[PCF50633_TI_DAY] = bin2bcd(rtc->tm_mday); - pcf->time[PCF50633_TI_MONTH] = bin2bcd(rtc->tm_mon); + pcf->time[PCF50633_TI_MONTH] = bin2bcd(rtc->tm_mon + 1); pcf->time[PCF50633_TI_YEAR] = bin2bcd(rtc->tm_year % 100); } @@ -245,8 +245,9 @@ static int pcf50633_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) ret = pcf50633_write_block(rtc->pcf, PCF50633_REG_RTCSCA, PCF50633_TI_EXTENT, &pcf_tm.time[0]); - if (!alarm_masked) + if (!alarm_masked || alrm->enabled) pcf50633_irq_unmask(rtc->pcf, PCF50633_IRQ_ALARM); + rtc->alarm_enabled = alrm->enabled; return ret; } diff --git a/drivers/rtc/rtc-pxa.c b/drivers/rtc/rtc-pxa.c index bb8cc05605ac..747ca194fad4 100644 --- a/drivers/rtc/rtc-pxa.c +++ b/drivers/rtc/rtc-pxa.c @@ -438,34 +438,37 @@ static int __exit pxa_rtc_remove(struct platform_device *pdev) } #ifdef CONFIG_PM -static int pxa_rtc_suspend(struct platform_device *pdev, pm_message_t state) +static int pxa_rtc_suspend(struct device *dev) { - struct pxa_rtc *pxa_rtc = platform_get_drvdata(pdev); + struct pxa_rtc *pxa_rtc = dev_get_drvdata(dev); - if (device_may_wakeup(&pdev->dev)) + if (device_may_wakeup(dev)) enable_irq_wake(pxa_rtc->irq_Alrm); return 0; } -static int pxa_rtc_resume(struct platform_device *pdev) +static int pxa_rtc_resume(struct device *dev) { - struct pxa_rtc *pxa_rtc = platform_get_drvdata(pdev); + struct pxa_rtc *pxa_rtc = dev_get_drvdata(dev); - if (device_may_wakeup(&pdev->dev)) + if (device_may_wakeup(dev)) disable_irq_wake(pxa_rtc->irq_Alrm); return 0; } -#else -#define pxa_rtc_suspend NULL -#define pxa_rtc_resume NULL + +static struct dev_pm_ops pxa_rtc_pm_ops = { + .suspend = pxa_rtc_suspend, + .resume = pxa_rtc_resume, +}; #endif static struct platform_driver pxa_rtc_driver = { .remove = __exit_p(pxa_rtc_remove), - .suspend = pxa_rtc_suspend, - .resume = pxa_rtc_resume, .driver = { - .name = "pxa-rtc", + .name = "pxa-rtc", +#ifdef CONFIG_PM + .pm = &pxa_rtc_pm_ops, +#endif }, }; diff --git a/drivers/rtc/rtc-r9701.c b/drivers/rtc/rtc-r9701.c index 42028f233bef..9beba49c3c5b 100644 --- a/drivers/rtc/rtc-r9701.c +++ b/drivers/rtc/rtc-r9701.c @@ -174,3 +174,4 @@ module_exit(r9701_exit); MODULE_DESCRIPTION("r9701 spi RTC driver"); MODULE_AUTHOR("Magnus Damm <damm@opensource.se>"); MODULE_LICENSE("GPL"); +MODULE_ALIAS("spi:rtc-r9701"); diff --git a/drivers/rtc/rtc-rs5c348.c b/drivers/rtc/rtc-rs5c348.c index dd1e2bc7a472..2099037cb3ea 100644 --- a/drivers/rtc/rtc-rs5c348.c +++ b/drivers/rtc/rtc-rs5c348.c @@ -251,3 +251,4 @@ MODULE_AUTHOR("Atsushi Nemoto <anemo@mba.ocn.ne.jp>"); MODULE_DESCRIPTION("Ricoh RS5C348 RTC driver"); MODULE_LICENSE("GPL"); MODULE_VERSION(DRV_VERSION); +MODULE_ALIAS("spi:rtc-rs5c348"); diff --git a/drivers/rtc/rtc-sa1100.c b/drivers/rtc/rtc-sa1100.c index 021b2928f0b9..29f98a70586e 100644 --- a/drivers/rtc/rtc-sa1100.c +++ b/drivers/rtc/rtc-sa1100.c @@ -393,31 +393,34 @@ static int sa1100_rtc_remove(struct platform_device *pdev) } #ifdef CONFIG_PM -static int sa1100_rtc_suspend(struct platform_device *pdev, pm_message_t state) +static int sa1100_rtc_suspend(struct device *dev) { - if (device_may_wakeup(&pdev->dev)) + if (device_may_wakeup(dev)) enable_irq_wake(IRQ_RTCAlrm); return 0; } -static int sa1100_rtc_resume(struct platform_device *pdev) +static int sa1100_rtc_resume(struct device *dev) { - if (device_may_wakeup(&pdev->dev)) + if (device_may_wakeup(dev)) disable_irq_wake(IRQ_RTCAlrm); return 0; } -#else -#define sa1100_rtc_suspend NULL -#define sa1100_rtc_resume NULL + +static struct dev_pm_ops sa1100_rtc_pm_ops = { + .suspend = sa1100_rtc_suspend, + .resume = sa1100_rtc_resume, +}; #endif static struct platform_driver sa1100_rtc_driver = { .probe = sa1100_rtc_probe, .remove = sa1100_rtc_remove, - .suspend = sa1100_rtc_suspend, - .resume = sa1100_rtc_resume, .driver = { - .name = "sa1100-rtc", + .name = "sa1100-rtc", +#ifdef CONFIG_PM + .pm = &sa1100_rtc_pm_ops, +#endif }, }; diff --git a/drivers/rtc/rtc-stmp3xxx.c b/drivers/rtc/rtc-stmp3xxx.c new file mode 100644 index 000000000000..d7ce1a5c857d --- /dev/null +++ b/drivers/rtc/rtc-stmp3xxx.c @@ -0,0 +1,304 @@ +/* + * Freescale STMP37XX/STMP378X Real Time Clock driver + * + * Copyright (c) 2007 Sigmatel, Inc. + * Peter Hartley, <peter.hartley@sigmatel.com> + * + * Copyright 2008 Freescale Semiconductor, Inc. All Rights Reserved. + * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved. + */ + +/* + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/platform_device.h> +#include <linux/interrupt.h> +#include <linux/rtc.h> + +#include <mach/platform.h> +#include <mach/stmp3xxx.h> +#include <mach/regs-rtc.h> + +struct stmp3xxx_rtc_data { + struct rtc_device *rtc; + unsigned irq_count; + void __iomem *io; + int irq_alarm, irq_1msec; +}; + +static void stmp3xxx_wait_time(struct stmp3xxx_rtc_data *rtc_data) +{ + /* + * The datasheet doesn't say which way round the + * NEW_REGS/STALE_REGS bitfields go. In fact it's 0x1=P0, + * 0x2=P1, .., 0x20=P5, 0x40=ALARM, 0x80=SECONDS + */ + while (__raw_readl(rtc_data->io + HW_RTC_STAT) & + BF(0x80, RTC_STAT_STALE_REGS)) + cpu_relax(); +} + +/* Time read/write */ +static int stmp3xxx_rtc_gettime(struct device *dev, struct rtc_time *rtc_tm) +{ + struct stmp3xxx_rtc_data *rtc_data = dev_get_drvdata(dev); + + stmp3xxx_wait_time(rtc_data); + rtc_time_to_tm(__raw_readl(rtc_data->io + HW_RTC_SECONDS), rtc_tm); + return 0; +} + +static int stmp3xxx_rtc_set_mmss(struct device *dev, unsigned long t) +{ + struct stmp3xxx_rtc_data *rtc_data = dev_get_drvdata(dev); + + __raw_writel(t, rtc_data->io + HW_RTC_SECONDS); + stmp3xxx_wait_time(rtc_data); + return 0; +} + +/* interrupt(s) handler */ +static irqreturn_t stmp3xxx_rtc_interrupt(int irq, void *dev_id) +{ + struct stmp3xxx_rtc_data *rtc_data = dev_get_drvdata(dev_id); + u32 status; + u32 events = 0; + + status = __raw_readl(rtc_data->io + HW_RTC_CTRL) & + (BM_RTC_CTRL_ALARM_IRQ | BM_RTC_CTRL_ONEMSEC_IRQ); + + if (status & BM_RTC_CTRL_ALARM_IRQ) { + stmp3xxx_clearl(BM_RTC_CTRL_ALARM_IRQ, + rtc_data->io + HW_RTC_CTRL); + events |= RTC_AF | RTC_IRQF; + } + + if (status & BM_RTC_CTRL_ONEMSEC_IRQ) { + stmp3xxx_clearl(BM_RTC_CTRL_ONEMSEC_IRQ, + rtc_data->io + HW_RTC_CTRL); + if (++rtc_data->irq_count % 1000 == 0) { + events |= RTC_UF | RTC_IRQF; + rtc_data->irq_count = 0; + } + } + + if (events) + rtc_update_irq(rtc_data->rtc, 1, events); + + return IRQ_HANDLED; +} + +static int stmp3xxx_alarm_irq_enable(struct device *dev, unsigned int enabled) +{ + struct stmp3xxx_rtc_data *rtc_data = dev_get_drvdata(dev); + void __iomem *p = rtc_data->io + HW_RTC_PERSISTENT0, + *ctl = rtc_data->io + HW_RTC_CTRL; + + if (enabled) { + stmp3xxx_setl(BM_RTC_PERSISTENT0_ALARM_EN | + BM_RTC_PERSISTENT0_ALARM_WAKE_EN, p); + stmp3xxx_setl(BM_RTC_CTRL_ALARM_IRQ_EN, ctl); + } else { + stmp3xxx_clearl(BM_RTC_PERSISTENT0_ALARM_EN | + BM_RTC_PERSISTENT0_ALARM_WAKE_EN, p); + stmp3xxx_clearl(BM_RTC_CTRL_ALARM_IRQ_EN, ctl); + } + return 0; +} + +static int stmp3xxx_update_irq_enable(struct device *dev, unsigned int enabled) +{ + struct stmp3xxx_rtc_data *rtc_data = dev_get_drvdata(dev); + + if (enabled) + stmp3xxx_setl(BM_RTC_CTRL_ONEMSEC_IRQ_EN, + rtc_data->io + HW_RTC_CTRL); + else + stmp3xxx_clearl(BM_RTC_CTRL_ONEMSEC_IRQ_EN, + rtc_data->io + HW_RTC_CTRL); + return 0; +} + +static int stmp3xxx_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alm) +{ + struct stmp3xxx_rtc_data *rtc_data = dev_get_drvdata(dev); + + rtc_time_to_tm(__raw_readl(rtc_data->io + HW_RTC_ALARM), &alm->time); + return 0; +} + +static int stmp3xxx_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alm) +{ + unsigned long t; + struct stmp3xxx_rtc_data *rtc_data = dev_get_drvdata(dev); + + rtc_tm_to_time(&alm->time, &t); + __raw_writel(t, rtc_data->io + HW_RTC_ALARM); + return 0; +} + +static struct rtc_class_ops stmp3xxx_rtc_ops = { + .alarm_irq_enable = + stmp3xxx_alarm_irq_enable, + .update_irq_enable = + stmp3xxx_update_irq_enable, + .read_time = stmp3xxx_rtc_gettime, + .set_mmss = stmp3xxx_rtc_set_mmss, + .read_alarm = stmp3xxx_rtc_read_alarm, + .set_alarm = stmp3xxx_rtc_set_alarm, +}; + +static int stmp3xxx_rtc_remove(struct platform_device *pdev) +{ + struct stmp3xxx_rtc_data *rtc_data = platform_get_drvdata(pdev); + + if (!rtc_data) + return 0; + + stmp3xxx_clearl(BM_RTC_CTRL_ONEMSEC_IRQ_EN | BM_RTC_CTRL_ALARM_IRQ_EN, + rtc_data->io + HW_RTC_CTRL); + free_irq(rtc_data->irq_alarm, &pdev->dev); + free_irq(rtc_data->irq_1msec, &pdev->dev); + rtc_device_unregister(rtc_data->rtc); + iounmap(rtc_data->io); + kfree(rtc_data); + + return 0; +} + +static int stmp3xxx_rtc_probe(struct platform_device *pdev) +{ + struct stmp3xxx_rtc_data *rtc_data; + struct resource *r; + int err; + + rtc_data = kzalloc(sizeof *rtc_data, GFP_KERNEL); + if (!rtc_data) + return -ENOMEM; + + r = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!r) { + dev_err(&pdev->dev, "failed to get resource\n"); + err = -ENXIO; + goto out_free; + } + + rtc_data->io = ioremap(r->start, resource_size(r)); + if (!rtc_data->io) { + dev_err(&pdev->dev, "ioremap failed\n"); + err = -EIO; + goto out_free; + } + + rtc_data->irq_alarm = platform_get_irq(pdev, 0); + rtc_data->irq_1msec = platform_get_irq(pdev, 1); + + if (!(__raw_readl(HW_RTC_STAT + rtc_data->io) & + BM_RTC_STAT_RTC_PRESENT)) { + dev_err(&pdev->dev, "no device onboard\n"); + err = -ENODEV; + goto out_remap; + } + + stmp3xxx_reset_block(rtc_data->io, true); + stmp3xxx_clearl(BM_RTC_PERSISTENT0_ALARM_EN | + BM_RTC_PERSISTENT0_ALARM_WAKE_EN | + BM_RTC_PERSISTENT0_ALARM_WAKE, + rtc_data->io + HW_RTC_PERSISTENT0); + rtc_data->rtc = rtc_device_register(pdev->name, &pdev->dev, + &stmp3xxx_rtc_ops, THIS_MODULE); + if (IS_ERR(rtc_data->rtc)) { + err = PTR_ERR(rtc_data->rtc); + goto out_remap; + } + + rtc_data->irq_count = 0; + err = request_irq(rtc_data->irq_alarm, stmp3xxx_rtc_interrupt, + IRQF_DISABLED, "RTC alarm", &pdev->dev); + if (err) { + dev_err(&pdev->dev, "Cannot claim IRQ%d\n", + rtc_data->irq_alarm); + goto out_irq_alarm; + } + err = request_irq(rtc_data->irq_1msec, stmp3xxx_rtc_interrupt, + IRQF_DISABLED, "RTC tick", &pdev->dev); + if (err) { + dev_err(&pdev->dev, "Cannot claim IRQ%d\n", + rtc_data->irq_1msec); + goto out_irq1; + } + + platform_set_drvdata(pdev, rtc_data); + + return 0; + +out_irq1: + free_irq(rtc_data->irq_alarm, &pdev->dev); +out_irq_alarm: + stmp3xxx_clearl(BM_RTC_CTRL_ONEMSEC_IRQ_EN | BM_RTC_CTRL_ALARM_IRQ_EN, + rtc_data->io + HW_RTC_CTRL); + rtc_device_unregister(rtc_data->rtc); +out_remap: + iounmap(rtc_data->io); +out_free: + kfree(rtc_data); + return err; +} + +#ifdef CONFIG_PM +static int stmp3xxx_rtc_suspend(struct platform_device *dev, pm_message_t state) +{ + return 0; +} + +static int stmp3xxx_rtc_resume(struct platform_device *dev) +{ + struct stmp3xxx_rtc_data *rtc_data = platform_get_drvdata(dev); + + stmp3xxx_reset_block(rtc_data->io, true); + stmp3xxx_clearl(BM_RTC_PERSISTENT0_ALARM_EN | + BM_RTC_PERSISTENT0_ALARM_WAKE_EN | + BM_RTC_PERSISTENT0_ALARM_WAKE, + rtc_data->io + HW_RTC_PERSISTENT0); + return 0; +} +#else +#define stmp3xxx_rtc_suspend NULL +#define stmp3xxx_rtc_resume NULL +#endif + +static struct platform_driver stmp3xxx_rtcdrv = { + .probe = stmp3xxx_rtc_probe, + .remove = stmp3xxx_rtc_remove, + .suspend = stmp3xxx_rtc_suspend, + .resume = stmp3xxx_rtc_resume, + .driver = { + .name = "stmp3xxx-rtc", + .owner = THIS_MODULE, + }, +}; + +static int __init stmp3xxx_rtc_init(void) +{ + return platform_driver_register(&stmp3xxx_rtcdrv); +} + +static void __exit stmp3xxx_rtc_exit(void) +{ + platform_driver_unregister(&stmp3xxx_rtcdrv); +} + +module_init(stmp3xxx_rtc_init); +module_exit(stmp3xxx_rtc_exit); + +MODULE_DESCRIPTION("STMP3xxx RTC Driver"); +MODULE_AUTHOR("dmitry pervushin <dpervushin@embeddedalley.com>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/rtc/rtc-sysfs.c b/drivers/rtc/rtc-sysfs.c index 2531ce4c9db0..7dd23a6fc825 100644 --- a/drivers/rtc/rtc-sysfs.c +++ b/drivers/rtc/rtc-sysfs.c @@ -102,6 +102,19 @@ rtc_sysfs_set_max_user_freq(struct device *dev, struct device_attribute *attr, return n; } +static ssize_t +rtc_sysfs_show_hctosys(struct device *dev, struct device_attribute *attr, + char *buf) +{ +#ifdef CONFIG_RTC_HCTOSYS_DEVICE + if (strcmp(dev_name(&to_rtc_device(dev)->dev), + CONFIG_RTC_HCTOSYS_DEVICE) == 0) + return sprintf(buf, "1\n"); + else +#endif + return sprintf(buf, "0\n"); +} + static struct device_attribute rtc_attrs[] = { __ATTR(name, S_IRUGO, rtc_sysfs_show_name, NULL), __ATTR(date, S_IRUGO, rtc_sysfs_show_date, NULL), @@ -109,6 +122,7 @@ static struct device_attribute rtc_attrs[] = { __ATTR(since_epoch, S_IRUGO, rtc_sysfs_show_since_epoch, NULL), __ATTR(max_user_freq, S_IRUGO | S_IWUSR, rtc_sysfs_show_max_user_freq, rtc_sysfs_set_max_user_freq), + __ATTR(hctosys, S_IRUGO, rtc_sysfs_show_hctosys, NULL), { }, }; diff --git a/drivers/rtc/rtc-v3020.c b/drivers/rtc/rtc-v3020.c index ad164056feb6..423cd5a30b10 100644 --- a/drivers/rtc/rtc-v3020.c +++ b/drivers/rtc/rtc-v3020.c @@ -96,7 +96,7 @@ static void v3020_mmio_write_bit(struct v3020 *chip, unsigned char bit) static unsigned char v3020_mmio_read_bit(struct v3020 *chip) { - return readl(chip->ioaddress) & (1 << chip->leftshift); + return !!(readl(chip->ioaddress) & (1 << chip->leftshift)); } static struct v3020_chip_ops v3020_mmio_ops = { diff --git a/drivers/rtc/rtc-vr41xx.c b/drivers/rtc/rtc-vr41xx.c index 2c839d0d21bd..fadddac1e5a4 100644 --- a/drivers/rtc/rtc-vr41xx.c +++ b/drivers/rtc/rtc-vr41xx.c @@ -209,19 +209,18 @@ static int vr41xx_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *wkalrm) static int vr41xx_rtc_irq_set_freq(struct device *dev, int freq) { - unsigned long count; + u64 count; if (!is_power_of_2(freq)) return -EINVAL; count = RTC_FREQUENCY; do_div(count, freq); - periodic_count = count; - spin_lock_irq(&rtc_lock); - rtc1_write(RTCL1LREG, count); - rtc1_write(RTCL1HREG, count >> 16); + periodic_count = count; + rtc1_write(RTCL1LREG, periodic_count); + rtc1_write(RTCL1HREG, periodic_count >> 16); spin_unlock_irq(&rtc_lock); diff --git a/drivers/s390/block/dasd.c b/drivers/s390/block/dasd.c index dad0449475b6..aaccc8ecfa8f 100644 --- a/drivers/s390/block/dasd.c +++ b/drivers/s390/block/dasd.c @@ -2508,8 +2508,6 @@ int dasd_generic_restore_device(struct ccw_device *cdev) device->stopped &= ~DASD_UNRESUMED_PM; dasd_schedule_device_bh(device); - if (device->block) - dasd_schedule_block_bh(device->block); if (device->discipline->restore) rc = device->discipline->restore(device); @@ -2520,6 +2518,9 @@ int dasd_generic_restore_device(struct ccw_device *cdev) */ device->stopped |= DASD_UNRESUMED_PM; + if (device->block) + dasd_schedule_block_bh(device->block); + dasd_put_device(device); return 0; } @@ -2532,6 +2533,7 @@ static struct dasd_ccw_req *dasd_generic_build_rdc(struct dasd_device *device, { struct dasd_ccw_req *cqr; struct ccw1 *ccw; + unsigned long *idaw; cqr = dasd_smalloc_request(magic, 1 /* RDC */, rdc_buffer_size, device); @@ -2545,9 +2547,17 @@ static struct dasd_ccw_req *dasd_generic_build_rdc(struct dasd_device *device, ccw = cqr->cpaddr; ccw->cmd_code = CCW_CMD_RDC; - ccw->cda = (__u32)(addr_t)rdc_buffer; - ccw->count = rdc_buffer_size; + if (idal_is_needed(rdc_buffer, rdc_buffer_size)) { + idaw = (unsigned long *) (cqr->data); + ccw->cda = (__u32)(addr_t) idaw; + ccw->flags = CCW_FLAG_IDA; + idaw = idal_create_words(idaw, rdc_buffer, rdc_buffer_size); + } else { + ccw->cda = (__u32)(addr_t) rdc_buffer; + ccw->flags = 0; + } + ccw->count = rdc_buffer_size; cqr->startdev = device; cqr->memdev = device; cqr->expires = 10*HZ; diff --git a/drivers/s390/block/dasd_eckd.c b/drivers/s390/block/dasd_eckd.c index bd9fe2e36dce..417b97cd3f94 100644 --- a/drivers/s390/block/dasd_eckd.c +++ b/drivers/s390/block/dasd_eckd.c @@ -935,6 +935,7 @@ static int dasd_eckd_read_features(struct dasd_device *device) struct dasd_eckd_private *private; private = (struct dasd_eckd_private *) device->private; + memset(&private->features, 0, sizeof(struct dasd_rssd_features)); cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, 1 /* PSF */ + 1 /* RSSD */, (sizeof(struct dasd_psf_prssd_data) + sizeof(struct dasd_rssd_features)), @@ -982,7 +983,9 @@ static int dasd_eckd_read_features(struct dasd_device *device) features = (struct dasd_rssd_features *) (prssdp + 1); memcpy(&private->features, features, sizeof(struct dasd_rssd_features)); - } + } else + dev_warn(&device->cdev->dev, "Reading device feature codes" + " failed with rc=%d\n", rc); dasd_sfree_request(cqr, cqr->memdev); return rc; } @@ -1144,9 +1147,7 @@ dasd_eckd_check_characteristics(struct dasd_device *device) } /* Read Feature Codes */ - rc = dasd_eckd_read_features(device); - if (rc) - goto out_err3; + dasd_eckd_read_features(device); /* Read Device Characteristics */ rc = dasd_generic_read_dev_chars(device, DASD_ECKD_MAGIC, @@ -2337,6 +2338,8 @@ static struct dasd_ccw_req *dasd_eckd_build_cp(struct dasd_device *startdev, /* Calculate number of blocks/records per track. */ blksize = block->bp_block; blk_per_trk = recs_per_track(&private->rdc_data, 0, blksize); + if (blk_per_trk == 0) + return ERR_PTR(-EINVAL); /* Calculate record id of first and last block. */ first_rec = first_trk = blk_rq_pos(req) >> block->s2b_shift; first_offs = sector_div(first_trk, blk_per_trk); @@ -3210,8 +3213,10 @@ int dasd_eckd_pm_freeze(struct dasd_device *device) int dasd_eckd_restore_device(struct dasd_device *device) { struct dasd_eckd_private *private; + struct dasd_eckd_characteristics temp_rdc_data; int is_known, rc; struct dasd_uid temp_uid; + unsigned long flags; private = (struct dasd_eckd_private *) device->private; @@ -3224,7 +3229,8 @@ int dasd_eckd_restore_device(struct dasd_device *device) rc = dasd_eckd_generate_uid(device, &private->uid); dasd_get_uid(device->cdev, &temp_uid); if (memcmp(&private->uid, &temp_uid, sizeof(struct dasd_uid)) != 0) - dev_err(&device->cdev->dev, "The UID of the DASD has changed\n"); + dev_err(&device->cdev->dev, "The UID of the DASD has " + "changed\n"); if (rc) goto out_err; dasd_set_uid(device->cdev, &private->uid); @@ -3241,20 +3247,20 @@ int dasd_eckd_restore_device(struct dasd_device *device) } /* Read Feature Codes */ - rc = dasd_eckd_read_features(device); - if (rc) - goto out_err; + dasd_eckd_read_features(device); /* Read Device Characteristics */ - memset(&private->rdc_data, 0, sizeof(private->rdc_data)); rc = dasd_generic_read_dev_chars(device, DASD_ECKD_MAGIC, - &private->rdc_data, 64); + &temp_rdc_data, 64); if (rc) { DBF_EVENT(DBF_WARNING, "Read device characteristics failed, rc=%d for " "device: %s", rc, dev_name(&device->cdev->dev)); goto out_err; } + spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags); + memcpy(&private->rdc_data, &temp_rdc_data, sizeof(temp_rdc_data)); + spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags); /* add device to alias management */ dasd_alias_add_device(device); diff --git a/drivers/s390/char/monreader.c b/drivers/s390/char/monreader.c index 89ece1c235aa..66e21dd23154 100644 --- a/drivers/s390/char/monreader.c +++ b/drivers/s390/char/monreader.c @@ -357,6 +357,7 @@ static int mon_close(struct inode *inode, struct file *filp) atomic_set(&monpriv->msglim_count, 0); monpriv->write_index = 0; monpriv->read_index = 0; + dev_set_drvdata(monreader_device, NULL); for (i = 0; i < MON_MSGLIM; i++) kfree(monpriv->msg_array[i]); diff --git a/drivers/s390/char/raw3270.c b/drivers/s390/char/raw3270.c index d6a022f55e92..62ddf5202b79 100644 --- a/drivers/s390/char/raw3270.c +++ b/drivers/s390/char/raw3270.c @@ -1361,11 +1361,13 @@ static int raw3270_pm_start(struct ccw_device *cdev) void raw3270_pm_unfreeze(struct raw3270_view *view) { +#ifdef CONFIG_TN3270_CONSOLE struct raw3270 *rp; rp = view->dev; if (rp && test_bit(RAW3270_FLAGS_FROZEN, &rp->flags)) ccw_device_force_console(); +#endif } static struct ccw_device_id raw3270_id[] = { diff --git a/drivers/s390/char/sclp_async.c b/drivers/s390/char/sclp_async.c index daaec185ed36..b44462a6c6d3 100644 --- a/drivers/s390/char/sclp_async.c +++ b/drivers/s390/char/sclp_async.c @@ -26,7 +26,6 @@ static struct sclp_async_sccb *sccb; static int sclp_async_send_wait(char *message); static struct ctl_table_header *callhome_sysctl_header; static DEFINE_SPINLOCK(sclp_async_lock); -static char nodename[64]; #define SCLP_NORMAL_WRITE 0x00 struct async_evbuf { @@ -52,9 +51,10 @@ static struct sclp_register sclp_async_register = { static int call_home_on_panic(struct notifier_block *self, unsigned long event, void *data) { - strncat(data, nodename, strlen(nodename)); - sclp_async_send_wait(data); - return NOTIFY_DONE; + strncat(data, init_utsname()->nodename, + sizeof(init_utsname()->nodename)); + sclp_async_send_wait(data); + return NOTIFY_DONE; } static struct notifier_block call_home_panic_nb = { @@ -62,21 +62,20 @@ static struct notifier_block call_home_panic_nb = { .priority = INT_MAX, }; -static int proc_handler_callhome(ctl_table *ctl, int write, struct file *filp, +static int proc_handler_callhome(struct ctl_table *ctl, int write, void __user *buffer, size_t *count, loff_t *ppos) { unsigned long val; int len, rc; - char buf[2]; + char buf[3]; - if (!*count | (*ppos && !write)) { + if (!*count || (*ppos && !write)) { *count = 0; return 0; } if (!write) { - len = sprintf(buf, "%d\n", callhome_enabled); - buf[len] = '\0'; + len = snprintf(buf, sizeof(buf), "%d\n", callhome_enabled); rc = copy_to_user(buffer, buf, sizeof(buf)); if (rc != 0) return -EFAULT; @@ -100,7 +99,7 @@ static struct ctl_table callhome_table[] = { { .procname = "callhome", .mode = 0644, - .proc_handler = &proc_handler_callhome, + .proc_handler = proc_handler_callhome, }, { .ctl_name = 0 } }; @@ -171,39 +170,29 @@ static int __init sclp_async_init(void) rc = sclp_register(&sclp_async_register); if (rc) return rc; - callhome_sysctl_header = register_sysctl_table(kern_dir_table); - if (!callhome_sysctl_header) { - rc = -ENOMEM; - goto out_sclp; - } - if (!(sclp_async_register.sclp_receive_mask & EVTYP_ASYNC_MASK)) { - rc = -EOPNOTSUPP; + rc = -EOPNOTSUPP; + if (!(sclp_async_register.sclp_receive_mask & EVTYP_ASYNC_MASK)) goto out_sclp; - } rc = -ENOMEM; + callhome_sysctl_header = register_sysctl_table(kern_dir_table); + if (!callhome_sysctl_header) + goto out_sclp; request = kzalloc(sizeof(struct sclp_req), GFP_KERNEL); - if (!request) - goto out_sys; sccb = (struct sclp_async_sccb *) get_zeroed_page(GFP_KERNEL | GFP_DMA); - if (!sccb) + if (!request || !sccb) goto out_mem; - rc = atomic_notifier_chain_register(&panic_notifier_list, - &call_home_panic_nb); - if (rc) - goto out_mem; - - strncpy(nodename, init_utsname()->nodename, 64); - return 0; - + rc = atomic_notifier_chain_register(&panic_notifier_list, + &call_home_panic_nb); + if (!rc) + goto out; out_mem: kfree(request); free_page((unsigned long) sccb); -out_sys: unregister_sysctl_table(callhome_sysctl_header); out_sclp: sclp_unregister(&sclp_async_register); +out: return rc; - } module_init(sclp_async_init); diff --git a/drivers/s390/char/sclp_quiesce.c b/drivers/s390/char/sclp_quiesce.c index 84c191c1cd62..05909a7df8b3 100644 --- a/drivers/s390/char/sclp_quiesce.c +++ b/drivers/s390/char/sclp_quiesce.c @@ -20,9 +20,12 @@ #include "sclp.h" +static void (*old_machine_restart)(char *); +static void (*old_machine_halt)(void); +static void (*old_machine_power_off)(void); + /* Shutdown handler. Signal completion of shutdown by loading special PSW. */ -static void -do_machine_quiesce(void) +static void do_machine_quiesce(void) { psw_t quiesce_psw; @@ -33,23 +36,48 @@ do_machine_quiesce(void) } /* Handler for quiesce event. Start shutdown procedure. */ -static void -sclp_quiesce_handler(struct evbuf_header *evbuf) +static void sclp_quiesce_handler(struct evbuf_header *evbuf) { - _machine_restart = (void *) do_machine_quiesce; - _machine_halt = do_machine_quiesce; - _machine_power_off = do_machine_quiesce; + if (_machine_restart != (void *) do_machine_quiesce) { + old_machine_restart = _machine_restart; + old_machine_halt = _machine_halt; + old_machine_power_off = _machine_power_off; + _machine_restart = (void *) do_machine_quiesce; + _machine_halt = do_machine_quiesce; + _machine_power_off = do_machine_quiesce; + } ctrl_alt_del(); } +/* Undo machine restart/halt/power_off modification on resume */ +static void sclp_quiesce_pm_event(struct sclp_register *reg, + enum sclp_pm_event sclp_pm_event) +{ + switch (sclp_pm_event) { + case SCLP_PM_EVENT_RESTORE: + if (old_machine_restart) { + _machine_restart = old_machine_restart; + _machine_halt = old_machine_halt; + _machine_power_off = old_machine_power_off; + old_machine_restart = NULL; + old_machine_halt = NULL; + old_machine_power_off = NULL; + } + break; + case SCLP_PM_EVENT_FREEZE: + case SCLP_PM_EVENT_THAW: + break; + } +} + static struct sclp_register sclp_quiesce_event = { .receive_mask = EVTYP_SIGQUIESCE_MASK, - .receiver_fn = sclp_quiesce_handler + .receiver_fn = sclp_quiesce_handler, + .pm_event_fn = sclp_quiesce_pm_event }; /* Initialize quiesce driver. */ -static int __init -sclp_quiesce_init(void) +static int __init sclp_quiesce_init(void) { return sclp_register(&sclp_quiesce_event); } diff --git a/drivers/s390/char/sclp_vt220.c b/drivers/s390/char/sclp_vt220.c index 178724f2a4c3..b9d2a007e93b 100644 --- a/drivers/s390/char/sclp_vt220.c +++ b/drivers/s390/char/sclp_vt220.c @@ -705,21 +705,6 @@ out_driver: } __initcall(sclp_vt220_tty_init); -#ifdef CONFIG_SCLP_VT220_CONSOLE - -static void -sclp_vt220_con_write(struct console *con, const char *buf, unsigned int count) -{ - __sclp_vt220_write((const unsigned char *) buf, count, 1, 1, 0); -} - -static struct tty_driver * -sclp_vt220_con_device(struct console *c, int *index) -{ - *index = 0; - return sclp_vt220_driver; -} - static void __sclp_vt220_flush_buffer(void) { unsigned long flags; @@ -776,6 +761,21 @@ static void sclp_vt220_pm_event_fn(struct sclp_register *reg, } } +#ifdef CONFIG_SCLP_VT220_CONSOLE + +static void +sclp_vt220_con_write(struct console *con, const char *buf, unsigned int count) +{ + __sclp_vt220_write((const unsigned char *) buf, count, 1, 1, 0); +} + +static struct tty_driver * +sclp_vt220_con_device(struct console *c, int *index) +{ + *index = 0; + return sclp_vt220_driver; +} + static int sclp_vt220_notify(struct notifier_block *self, unsigned long event, void *data) diff --git a/drivers/s390/char/tape_block.c b/drivers/s390/char/tape_block.c index 64f57ef2763c..0c0705b91c28 100644 --- a/drivers/s390/char/tape_block.c +++ b/drivers/s390/char/tape_block.c @@ -162,9 +162,10 @@ tapeblock_requeue(struct work_struct *work) { spin_lock_irq(&device->blk_data.request_queue_lock); while ( !blk_queue_plugged(queue) && - (req = blk_fetch_request(queue)) && + blk_peek_request(queue) && nr_queued < TAPEBLOCK_MIN_REQUEUE ) { + req = blk_fetch_request(queue); if (rq_data_dir(req) == WRITE) { DBF_EVENT(1, "TBLOCK: Rejecting write request\n"); spin_unlock_irq(&device->blk_data.request_queue_lock); diff --git a/drivers/s390/char/zcore.c b/drivers/s390/char/zcore.c index c431198bdbc4..82daa3c1dc9c 100644 --- a/drivers/s390/char/zcore.c +++ b/drivers/s390/char/zcore.c @@ -14,7 +14,6 @@ #include <linux/init.h> #include <linux/miscdevice.h> -#include <linux/utsname.h> #include <linux/debugfs.h> #include <asm/ipl.h> #include <asm/sclp.h> diff --git a/drivers/s390/cio/blacklist.c b/drivers/s390/cio/blacklist.c index 6565f027791e..7eab9ab9f406 100644 --- a/drivers/s390/cio/blacklist.c +++ b/drivers/s390/cio/blacklist.c @@ -265,13 +265,11 @@ struct ccwdev_iter { static void * cio_ignore_proc_seq_start(struct seq_file *s, loff_t *offset) { - struct ccwdev_iter *iter; + struct ccwdev_iter *iter = s->private; if (*offset >= (__MAX_SUBCHANNEL + 1) * (__MAX_SSID + 1)) return NULL; - iter = kzalloc(sizeof(struct ccwdev_iter), GFP_KERNEL); - if (!iter) - return ERR_PTR(-ENOMEM); + memset(iter, 0, sizeof(*iter)); iter->ssid = *offset / (__MAX_SUBCHANNEL + 1); iter->devno = *offset % (__MAX_SUBCHANNEL + 1); return iter; @@ -280,8 +278,6 @@ cio_ignore_proc_seq_start(struct seq_file *s, loff_t *offset) static void cio_ignore_proc_seq_stop(struct seq_file *s, void *it) { - if (!IS_ERR(it)) - kfree(it); } static void * @@ -378,14 +374,15 @@ static const struct seq_operations cio_ignore_proc_seq_ops = { static int cio_ignore_proc_open(struct inode *inode, struct file *file) { - return seq_open(file, &cio_ignore_proc_seq_ops); + return seq_open_private(file, &cio_ignore_proc_seq_ops, + sizeof(struct ccwdev_iter)); } static const struct file_operations cio_ignore_proc_fops = { .open = cio_ignore_proc_open, .read = seq_read, .llseek = seq_lseek, - .release = seq_release, + .release = seq_release_private, .write = cio_ignore_write, }; diff --git a/drivers/s390/cio/chp.c b/drivers/s390/cio/chp.c index 40002830d48a..8ab51608da55 100644 --- a/drivers/s390/cio/chp.c +++ b/drivers/s390/cio/chp.c @@ -393,7 +393,6 @@ int chp_new(struct chp_id chpid) chp->state = 1; chp->dev.parent = &channel_subsystems[chpid.cssid]->device; chp->dev.release = chp_release; - dev_set_name(&chp->dev, "chp%x.%02x", chpid.cssid, chpid.id); /* Obtain channel path description and fill it in. */ ret = chsc_determine_base_channel_path_desc(chpid, &chp->desc); @@ -411,6 +410,7 @@ int chp_new(struct chp_id chpid) } else { chp->cmg = -1; } + dev_set_name(&chp->dev, "chp%x.%02x", chpid.cssid, chpid.id); /* make it known to the system */ ret = device_register(&chp->dev); diff --git a/drivers/s390/cio/css.c b/drivers/s390/cio/css.c index 393c73c47f87..91c25706fa83 100644 --- a/drivers/s390/cio/css.c +++ b/drivers/s390/cio/css.c @@ -31,8 +31,7 @@ #include "chp.h" int css_init_done = 0; -static int need_reprobe = 0; -static int max_ssid = 0; +int max_ssid; struct channel_subsystem *channel_subsystems[__MAX_CSSID + 1]; @@ -315,12 +314,18 @@ int css_probe_device(struct subchannel_id schid) int ret; struct subchannel *sch; - sch = css_alloc_subchannel(schid); - if (IS_ERR(sch)) - return PTR_ERR(sch); + if (cio_is_console(schid)) + sch = cio_get_console_subchannel(); + else { + sch = css_alloc_subchannel(schid); + if (IS_ERR(sch)) + return PTR_ERR(sch); + } ret = css_register_subchannel(sch); - if (ret) - put_device(&sch->dev); + if (ret) { + if (!cio_is_console(schid)) + put_device(&sch->dev); + } return ret; } @@ -409,10 +414,14 @@ static void css_evaluate_subchannel(struct subchannel_id schid, int slow) static struct idset *slow_subchannel_set; static spinlock_t slow_subchannel_lock; +static wait_queue_head_t css_eval_wq; +static atomic_t css_eval_scheduled; static int __init slow_subchannel_init(void) { spin_lock_init(&slow_subchannel_lock); + atomic_set(&css_eval_scheduled, 0); + init_waitqueue_head(&css_eval_wq); slow_subchannel_set = idset_sch_new(); if (!slow_subchannel_set) { CIO_MSG_EVENT(0, "could not allocate slow subchannel set\n"); @@ -468,9 +477,17 @@ static int slow_eval_unknown_fn(struct subchannel_id schid, void *data) static void css_slow_path_func(struct work_struct *unused) { + unsigned long flags; + CIO_TRACE_EVENT(4, "slowpath"); for_each_subchannel_staged(slow_eval_known_fn, slow_eval_unknown_fn, NULL); + spin_lock_irqsave(&slow_subchannel_lock, flags); + if (idset_is_empty(slow_subchannel_set)) { + atomic_set(&css_eval_scheduled, 0); + wake_up(&css_eval_wq); + } + spin_unlock_irqrestore(&slow_subchannel_lock, flags); } static DECLARE_WORK(slow_path_work, css_slow_path_func); @@ -482,6 +499,7 @@ void css_schedule_eval(struct subchannel_id schid) spin_lock_irqsave(&slow_subchannel_lock, flags); idset_sch_add(slow_subchannel_set, schid); + atomic_set(&css_eval_scheduled, 1); queue_work(slow_path_wq, &slow_path_work); spin_unlock_irqrestore(&slow_subchannel_lock, flags); } @@ -492,80 +510,53 @@ void css_schedule_eval_all(void) spin_lock_irqsave(&slow_subchannel_lock, flags); idset_fill(slow_subchannel_set); + atomic_set(&css_eval_scheduled, 1); queue_work(slow_path_wq, &slow_path_work); spin_unlock_irqrestore(&slow_subchannel_lock, flags); } -void css_wait_for_slow_path(void) +static int __unset_registered(struct device *dev, void *data) { - flush_workqueue(slow_path_wq); -} - -/* Reprobe subchannel if unregistered. */ -static int reprobe_subchannel(struct subchannel_id schid, void *data) -{ - int ret; - - CIO_MSG_EVENT(6, "cio: reprobe 0.%x.%04x\n", - schid.ssid, schid.sch_no); - if (need_reprobe) - return -EAGAIN; - - ret = css_probe_device(schid); - switch (ret) { - case 0: - break; - case -ENXIO: - case -ENOMEM: - case -EIO: - /* These should abort looping */ - break; - default: - ret = 0; - } - - return ret; -} + struct idset *set = data; + struct subchannel *sch = to_subchannel(dev); -static void reprobe_after_idle(struct work_struct *unused) -{ - /* Make sure initial subchannel scan is done. */ - wait_event(ccw_device_init_wq, - atomic_read(&ccw_device_init_count) == 0); - if (need_reprobe) - css_schedule_reprobe(); + idset_sch_del(set, sch->schid); + return 0; } -static DECLARE_WORK(reprobe_idle_work, reprobe_after_idle); - -/* Work function used to reprobe all unregistered subchannels. */ -static void reprobe_all(struct work_struct *unused) +void css_schedule_eval_all_unreg(void) { - int ret; - - CIO_MSG_EVENT(4, "reprobe start\n"); + unsigned long flags; + struct idset *unreg_set; - /* Make sure initial subchannel scan is done. */ - if (atomic_read(&ccw_device_init_count) != 0) { - queue_work(ccw_device_work, &reprobe_idle_work); + /* Find unregistered subchannels. */ + unreg_set = idset_sch_new(); + if (!unreg_set) { + /* Fallback. */ + css_schedule_eval_all(); return; } - need_reprobe = 0; - ret = for_each_subchannel_staged(NULL, reprobe_subchannel, NULL); - - CIO_MSG_EVENT(4, "reprobe done (rc=%d, need_reprobe=%d)\n", ret, - need_reprobe); + idset_fill(unreg_set); + bus_for_each_dev(&css_bus_type, NULL, unreg_set, __unset_registered); + /* Apply to slow_subchannel_set. */ + spin_lock_irqsave(&slow_subchannel_lock, flags); + idset_add_set(slow_subchannel_set, unreg_set); + atomic_set(&css_eval_scheduled, 1); + queue_work(slow_path_wq, &slow_path_work); + spin_unlock_irqrestore(&slow_subchannel_lock, flags); + idset_free(unreg_set); } -static DECLARE_WORK(css_reprobe_work, reprobe_all); +void css_wait_for_slow_path(void) +{ + flush_workqueue(slow_path_wq); +} /* Schedule reprobing of all unregistered subchannels. */ void css_schedule_reprobe(void) { - need_reprobe = 1; - queue_work(slow_path_wq, &css_reprobe_work); + css_schedule_eval_all_unreg(); } - EXPORT_SYMBOL_GPL(css_schedule_reprobe); /* @@ -601,49 +592,6 @@ static void css_process_crw(struct crw *crw0, struct crw *crw1, int overflow) css_evaluate_subchannel(mchk_schid, 0); } -static int __init -__init_channel_subsystem(struct subchannel_id schid, void *data) -{ - struct subchannel *sch; - int ret; - - if (cio_is_console(schid)) - sch = cio_get_console_subchannel(); - else { - sch = css_alloc_subchannel(schid); - if (IS_ERR(sch)) - ret = PTR_ERR(sch); - else - ret = 0; - switch (ret) { - case 0: - break; - case -ENOMEM: - panic("Out of memory in init_channel_subsystem\n"); - /* -ENXIO: no more subchannels. */ - case -ENXIO: - return ret; - /* -EIO: this subchannel set not supported. */ - case -EIO: - return ret; - default: - return 0; - } - } - /* - * We register ALL valid subchannels in ioinfo, even those - * that have been present before init_channel_subsystem. - * These subchannels can't have been registered yet (kmalloc - * not working) so we do it now. This is true e.g. for the - * console subchannel. - */ - if (css_register_subchannel(sch)) { - if (!cio_is_console(schid)) - put_device(&sch->dev); - } - return 0; -} - static void __init css_generate_pgid(struct channel_subsystem *css, u32 tod_high) { @@ -854,19 +802,30 @@ static struct notifier_block css_power_notifier = { * The struct subchannel's are created during probing (except for the * static console subchannel). */ -static int __init -init_channel_subsystem (void) +static int __init css_bus_init(void) { int ret, i; ret = chsc_determine_css_characteristics(); if (ret == -ENOMEM) - goto out; /* No need to continue. */ + goto out; ret = chsc_alloc_sei_area(); if (ret) goto out; + /* Try to enable MSS. */ + ret = chsc_enable_facility(CHSC_SDA_OC_MSS); + switch (ret) { + case 0: /* Success. */ + max_ssid = __MAX_SSID; + break; + case -ENOMEM: + goto out; + default: + max_ssid = 0; + } + ret = slow_subchannel_init(); if (ret) goto out; @@ -878,17 +837,6 @@ init_channel_subsystem (void) if ((ret = bus_register(&css_bus_type))) goto out; - /* Try to enable MSS. */ - ret = chsc_enable_facility(CHSC_SDA_OC_MSS); - switch (ret) { - case 0: /* Success. */ - max_ssid = __MAX_SSID; - break; - case -ENOMEM: - goto out_bus; - default: - max_ssid = 0; - } /* Setup css structure. */ for (i = 0; i <= __MAX_CSSID; i++) { struct channel_subsystem *css; @@ -934,7 +882,6 @@ init_channel_subsystem (void) /* Enable default isc for I/O subchannels. */ isc_register(IO_SCH_ISC); - for_each_subchannel(__init_channel_subsystem, NULL); return 0; out_file: if (css_chsc_characteristics.secm) @@ -955,17 +902,76 @@ out_unregister: &dev_attr_cm_enable); device_unregister(&css->device); } -out_bus: bus_unregister(&css_bus_type); out: crw_unregister_handler(CRW_RSC_CSS); chsc_free_sei_area(); - kfree(slow_subchannel_set); + idset_free(slow_subchannel_set); pr_alert("The CSS device driver initialization failed with " "errno=%d\n", ret); return ret; } +static void __init css_bus_cleanup(void) +{ + struct channel_subsystem *css; + int i; + + for (i = 0; i <= __MAX_CSSID; i++) { + css = channel_subsystems[i]; + device_unregister(&css->pseudo_subchannel->dev); + css->pseudo_subchannel = NULL; + if (css_chsc_characteristics.secm) + device_remove_file(&css->device, &dev_attr_cm_enable); + device_unregister(&css->device); + } + bus_unregister(&css_bus_type); + crw_unregister_handler(CRW_RSC_CSS); + chsc_free_sei_area(); + idset_free(slow_subchannel_set); + isc_unregister(IO_SCH_ISC); +} + +static int __init channel_subsystem_init(void) +{ + int ret; + + ret = css_bus_init(); + if (ret) + return ret; + + ret = io_subchannel_init(); + if (ret) + css_bus_cleanup(); + + return ret; +} +subsys_initcall(channel_subsystem_init); + +static int css_settle(struct device_driver *drv, void *unused) +{ + struct css_driver *cssdrv = to_cssdriver(drv); + + if (cssdrv->settle) + cssdrv->settle(); + return 0; +} + +/* + * Wait for the initialization of devices to finish, to make sure we are + * done with our setup if the search for the root device starts. + */ +static int __init channel_subsystem_init_sync(void) +{ + /* Start initial subchannel evaluation. */ + css_schedule_eval_all(); + /* Wait for the evaluation of subchannels to finish. */ + wait_event(css_eval_wq, atomic_read(&css_eval_scheduled) == 0); + /* Wait for the subchannel type specific initialization to finish */ + return bus_for_each_drv(&css_bus_type, NULL, NULL, css_settle); +} +subsys_initcall_sync(channel_subsystem_init_sync); + int sch_is_pseudo_sch(struct subchannel *sch) { return sch == to_css(sch->dev.parent)->pseudo_subchannel; @@ -1135,7 +1141,5 @@ void css_driver_unregister(struct css_driver *cdrv) } EXPORT_SYMBOL_GPL(css_driver_unregister); -subsys_initcall(init_channel_subsystem); - MODULE_LICENSE("GPL"); EXPORT_SYMBOL(css_bus_type); diff --git a/drivers/s390/cio/css.h b/drivers/s390/cio/css.h index 9763eeec7458..68d6b0bf151c 100644 --- a/drivers/s390/cio/css.h +++ b/drivers/s390/cio/css.h @@ -75,6 +75,7 @@ struct chp_link; * @freeze: callback for freezing during hibernation snapshotting * @thaw: undo work done in @freeze * @restore: callback for restoring after hibernation + * @settle: wait for asynchronous work to finish * @name: name of the device driver */ struct css_driver { @@ -92,6 +93,7 @@ struct css_driver { int (*freeze)(struct subchannel *); int (*thaw) (struct subchannel *); int (*restore)(struct subchannel *); + void (*settle)(void); const char *name; }; @@ -109,6 +111,7 @@ extern void css_sch_device_unregister(struct subchannel *); extern int css_probe_device(struct subchannel_id); extern struct subchannel *get_subchannel_by_schid(struct subchannel_id); extern int css_init_done; +extern int max_ssid; int for_each_subchannel_staged(int (*fn_known)(struct subchannel *, void *), int (*fn_unknown)(struct subchannel_id, void *), void *data); diff --git a/drivers/s390/cio/device.c b/drivers/s390/cio/device.c index 6527f3f34493..2490b741e16a 100644 --- a/drivers/s390/cio/device.c +++ b/drivers/s390/cio/device.c @@ -131,6 +131,10 @@ static void io_subchannel_shutdown(struct subchannel *); static int io_subchannel_sch_event(struct subchannel *, int); static int io_subchannel_chp_event(struct subchannel *, struct chp_link *, int); +static void recovery_func(unsigned long data); +struct workqueue_struct *ccw_device_work; +wait_queue_head_t ccw_device_init_wq; +atomic_t ccw_device_init_count; static struct css_device_id io_subchannel_ids[] = { { .match_flags = 0x1, .type = SUBCHANNEL_TYPE_IO, }, @@ -151,6 +155,13 @@ static int io_subchannel_prepare(struct subchannel *sch) return 0; } +static void io_subchannel_settle(void) +{ + wait_event(ccw_device_init_wq, + atomic_read(&ccw_device_init_count) == 0); + flush_workqueue(ccw_device_work); +} + static struct css_driver io_subchannel_driver = { .owner = THIS_MODULE, .subchannel_type = io_subchannel_ids, @@ -162,16 +173,10 @@ static struct css_driver io_subchannel_driver = { .remove = io_subchannel_remove, .shutdown = io_subchannel_shutdown, .prepare = io_subchannel_prepare, + .settle = io_subchannel_settle, }; -struct workqueue_struct *ccw_device_work; -wait_queue_head_t ccw_device_init_wq; -atomic_t ccw_device_init_count; - -static void recovery_func(unsigned long data); - -static int __init -init_ccw_bus_type (void) +int __init io_subchannel_init(void) { int ret; @@ -181,10 +186,10 @@ init_ccw_bus_type (void) ccw_device_work = create_singlethread_workqueue("cio"); if (!ccw_device_work) - return -ENOMEM; /* FIXME: better errno ? */ + return -ENOMEM; slow_path_wq = create_singlethread_workqueue("kslowcrw"); if (!slow_path_wq) { - ret = -ENOMEM; /* FIXME: better errno ? */ + ret = -ENOMEM; goto out_err; } if ((ret = bus_register (&ccw_bus_type))) @@ -194,9 +199,6 @@ init_ccw_bus_type (void) if (ret) goto out_err; - wait_event(ccw_device_init_wq, - atomic_read(&ccw_device_init_count) == 0); - flush_workqueue(ccw_device_work); return 0; out_err: if (ccw_device_work) @@ -206,16 +208,6 @@ out_err: return ret; } -static void __exit -cleanup_ccw_bus_type (void) -{ - css_driver_unregister(&io_subchannel_driver); - bus_unregister(&ccw_bus_type); - destroy_workqueue(ccw_device_work); -} - -subsys_initcall(init_ccw_bus_type); -module_exit(cleanup_ccw_bus_type); /************************ device handling **************************/ @@ -1258,8 +1250,7 @@ static int io_subchannel_probe(struct subchannel *sch) unsigned long flags; struct ccw_dev_id dev_id; - cdev = sch_get_cdev(sch); - if (cdev) { + if (cio_is_console(sch->schid)) { rc = sysfs_create_group(&sch->dev.kobj, &io_subchannel_attr_group); if (rc) @@ -1268,13 +1259,13 @@ static int io_subchannel_probe(struct subchannel *sch) "0.%x.%04x (rc=%d)\n", sch->schid.ssid, sch->schid.sch_no, rc); /* - * This subchannel already has an associated ccw_device. + * The console subchannel already has an associated ccw_device. * Throw the delayed uevent for the subchannel, register - * the ccw_device and exit. This happens for all early - * devices, e.g. the console. + * the ccw_device and exit. */ dev_set_uevent_suppress(&sch->dev, 0); kobject_uevent(&sch->dev.kobj, KOBJ_ADD); + cdev = sch_get_cdev(sch); cdev->dev.groups = ccwdev_attr_groups; device_initialize(&cdev->dev); ccw_device_register(cdev); @@ -1617,7 +1608,7 @@ int ccw_purge_blacklisted(void) return 0; } -static void device_set_disconnected(struct ccw_device *cdev) +void ccw_device_set_disconnected(struct ccw_device *cdev) { if (!cdev) return; @@ -1713,7 +1704,7 @@ static int io_subchannel_sch_event(struct subchannel *sch, int slow) ccw_device_trigger_reprobe(cdev); break; case DISC: - device_set_disconnected(cdev); + ccw_device_set_disconnected(cdev); break; default: break; diff --git a/drivers/s390/cio/device.h b/drivers/s390/cio/device.h index e3975107a578..246c6482842c 100644 --- a/drivers/s390/cio/device.h +++ b/drivers/s390/cio/device.h @@ -74,6 +74,7 @@ dev_fsm_final_state(struct ccw_device *cdev) extern struct workqueue_struct *ccw_device_work; extern wait_queue_head_t ccw_device_init_wq; extern atomic_t ccw_device_init_count; +int __init io_subchannel_init(void); void io_subchannel_recog_done(struct ccw_device *cdev); void io_subchannel_init_config(struct subchannel *sch); @@ -124,6 +125,7 @@ int ccw_device_stlck(struct ccw_device *); void ccw_device_trigger_reprobe(struct ccw_device *); void ccw_device_kill_io(struct ccw_device *); int ccw_device_notify(struct ccw_device *, int); +void ccw_device_set_disconnected(struct ccw_device *cdev); void ccw_device_set_notoper(struct ccw_device *cdev); /* qdio needs this. */ diff --git a/drivers/s390/cio/device_fsm.c b/drivers/s390/cio/device_fsm.c index e728ce447f6e..b9613d7df9ef 100644 --- a/drivers/s390/cio/device_fsm.c +++ b/drivers/s390/cio/device_fsm.c @@ -387,19 +387,35 @@ ccw_device_done(struct ccw_device *cdev, int state) cdev->private->state = state; - if (state == DEV_STATE_BOXED) { + switch (state) { + case DEV_STATE_BOXED: CIO_MSG_EVENT(0, "Boxed device %04x on subchannel %04x\n", cdev->private->dev_id.devno, sch->schid.sch_no); if (cdev->online && !ccw_device_notify(cdev, CIO_BOXED)) ccw_device_schedule_sch_unregister(cdev); cdev->private->flags.donotify = 0; - } - if (state == DEV_STATE_NOT_OPER) { + break; + case DEV_STATE_NOT_OPER: CIO_MSG_EVENT(0, "Device %04x gone on subchannel %04x\n", cdev->private->dev_id.devno, sch->schid.sch_no); if (!ccw_device_notify(cdev, CIO_GONE)) ccw_device_schedule_sch_unregister(cdev); + else + ccw_device_set_disconnected(cdev); cdev->private->flags.donotify = 0; + break; + case DEV_STATE_DISCONNECTED: + CIO_MSG_EVENT(0, "Disconnected device %04x on subchannel " + "%04x\n", cdev->private->dev_id.devno, + sch->schid.sch_no); + if (!ccw_device_notify(cdev, CIO_NO_PATH)) + ccw_device_schedule_sch_unregister(cdev); + else + ccw_device_set_disconnected(cdev); + cdev->private->flags.donotify = 0; + break; + default: + break; } if (cdev->private->flags.donotify) { @@ -671,6 +687,10 @@ ccw_device_offline(struct ccw_device *cdev) ccw_device_done(cdev, DEV_STATE_NOT_OPER); return 0; } + if (cdev->private->state == DEV_STATE_BOXED) { + ccw_device_done(cdev, DEV_STATE_BOXED); + return 0; + } if (ccw_device_is_orphan(cdev)) { ccw_device_done(cdev, DEV_STATE_OFFLINE); return 0; @@ -730,11 +750,10 @@ ccw_device_recog_notoper(struct ccw_device *cdev, enum dev_event dev_event) static void ccw_device_generic_notoper(struct ccw_device *cdev, enum dev_event dev_event) { - struct subchannel *sch; - - ccw_device_set_notoper(cdev); - sch = to_subchannel(cdev->dev.parent); - css_schedule_eval(sch->schid); + if (!ccw_device_notify(cdev, CIO_GONE)) + ccw_device_schedule_sch_unregister(cdev); + else + ccw_device_set_disconnected(cdev); } /* diff --git a/drivers/s390/cio/idset.c b/drivers/s390/cio/idset.c index cf8f24a4b5eb..4d10981c7cc1 100644 --- a/drivers/s390/cio/idset.c +++ b/drivers/s390/cio/idset.c @@ -78,7 +78,7 @@ static inline int idset_get_first(struct idset *set, int *ssid, int *id) struct idset *idset_sch_new(void) { - return idset_new(__MAX_SSID + 1, __MAX_SUBCHANNEL + 1); + return idset_new(max_ssid + 1, __MAX_SUBCHANNEL + 1); } void idset_sch_add(struct idset *set, struct subchannel_id schid) @@ -110,3 +110,23 @@ int idset_sch_get_first(struct idset *set, struct subchannel_id *schid) } return rc; } + +int idset_is_empty(struct idset *set) +{ + int bitnum; + + bitnum = find_first_bit(set->bitmap, set->num_ssid * set->num_id); + if (bitnum >= set->num_ssid * set->num_id) + return 1; + return 0; +} + +void idset_add_set(struct idset *to, struct idset *from) +{ + unsigned long i, len; + + len = min(__BITOPS_WORDS(to->num_ssid * to->num_id), + __BITOPS_WORDS(from->num_ssid * from->num_id)); + for (i = 0; i < len ; i++) + to->bitmap[i] |= from->bitmap[i]; +} diff --git a/drivers/s390/cio/idset.h b/drivers/s390/cio/idset.h index 528065cb5021..7543da4529f9 100644 --- a/drivers/s390/cio/idset.h +++ b/drivers/s390/cio/idset.h @@ -21,5 +21,7 @@ void idset_sch_add(struct idset *set, struct subchannel_id id); void idset_sch_del(struct idset *set, struct subchannel_id id); int idset_sch_contains(struct idset *set, struct subchannel_id id); int idset_sch_get_first(struct idset *set, struct subchannel_id *id); +int idset_is_empty(struct idset *set); +void idset_add_set(struct idset *to, struct idset *from); #endif /* S390_IDSET_H */ diff --git a/drivers/s390/cio/qdio_debug.c b/drivers/s390/cio/qdio_debug.c index 1b78f639ead3..76769978285f 100644 --- a/drivers/s390/cio/qdio_debug.c +++ b/drivers/s390/cio/qdio_debug.c @@ -125,7 +125,7 @@ static int qstat_seq_open(struct inode *inode, struct file *filp) filp->f_path.dentry->d_inode->i_private); } -static struct file_operations debugfs_fops = { +static const struct file_operations debugfs_fops = { .owner = THIS_MODULE, .open = qstat_seq_open, .read = seq_read, diff --git a/drivers/s390/cio/qdio_main.c b/drivers/s390/cio/qdio_main.c index 9aef402a5f1b..4be6e84b9599 100644 --- a/drivers/s390/cio/qdio_main.c +++ b/drivers/s390/cio/qdio_main.c @@ -401,7 +401,7 @@ static void announce_buffer_error(struct qdio_q *q, int count) if ((!q->is_input_q && (q->sbal[q->first_to_check]->element[15].flags & 0xff) == 0x10)) { qdio_perf_stat_inc(&perf_stats.outbound_target_full); - DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "OUTFULL FTC:%3d", + DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "OUTFULL FTC:%02x", q->first_to_check); return; } @@ -418,7 +418,7 @@ static inline void inbound_primed(struct qdio_q *q, int count) { int new; - DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "in prim: %3d", count); + DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "in prim: %02x", count); /* for QEBSM the ACK was already set by EQBS */ if (is_qebsm(q)) { @@ -455,6 +455,8 @@ static inline void inbound_primed(struct qdio_q *q, int count) count--; if (!count) return; + /* need to change ALL buffers to get more interrupts */ + set_buf_states(q, q->first_to_check, SLSB_P_INPUT_NOT_INIT, count); } static int get_inbound_buffer_frontier(struct qdio_q *q) @@ -545,7 +547,7 @@ static inline int qdio_inbound_q_done(struct qdio_q *q) * has (probably) not moved (see qdio_inbound_processing). */ if (get_usecs() > q->u.in.timestamp + QDIO_INPUT_THRESHOLD) { - DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "in done:%3d", + DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "in done:%02x", q->first_to_check); return 1; } else @@ -565,11 +567,10 @@ static void qdio_kick_handler(struct qdio_q *q) if (q->is_input_q) { qdio_perf_stat_inc(&perf_stats.inbound_handler); - DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "kih s:%3d c:%3d", start, count); - } else { - DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "koh: nr:%1d", q->nr); - DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "s:%3d c:%3d", start, count); - } + DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "kih s:%02x c:%02x", start, count); + } else + DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "koh: s:%02x c:%02x", + start, count); q->handler(q->irq_ptr->cdev, q->qdio_error, q->nr, start, count, q->irq_ptr->int_parm); @@ -633,7 +634,7 @@ static int get_outbound_buffer_frontier(struct qdio_q *q) switch (state) { case SLSB_P_OUTPUT_EMPTY: /* the adapter got it */ - DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "out empty:%1d %3d", q->nr, count); + DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "out empty:%1d %02x", q->nr, count); atomic_sub(count, &q->nr_buf_used); q->first_to_check = add_buf(q->first_to_check, count); @@ -1481,10 +1482,9 @@ static int handle_outbound(struct qdio_q *q, unsigned int callflags, get_buf_state(q, prev_buf(bufnr), &state, 0); if (state != SLSB_CU_OUTPUT_PRIMED) rc = qdio_kick_outbound_q(q); - else { - DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "fast-req"); + else qdio_perf_stat_inc(&perf_stats.fast_requeue); - } + out: tasklet_schedule(&q->tasklet); return rc; @@ -1510,12 +1510,8 @@ int do_QDIO(struct ccw_device *cdev, unsigned int callflags, if (!irq_ptr) return -ENODEV; - if (callflags & QDIO_FLAG_SYNC_INPUT) - DBF_DEV_EVENT(DBF_INFO, irq_ptr, "doQDIO input"); - else - DBF_DEV_EVENT(DBF_INFO, irq_ptr, "doQDIO output"); - DBF_DEV_EVENT(DBF_INFO, irq_ptr, "q:%1d flag:%4x", q_nr, callflags); - DBF_DEV_EVENT(DBF_INFO, irq_ptr, "buf:%2d cnt:%3d", bufnr, count); + DBF_DEV_EVENT(DBF_INFO, irq_ptr, + "do%02x b:%02x c:%02x", callflags, bufnr, count); if (irq_ptr->state != QDIO_IRQ_STATE_ACTIVE) return -EBUSY; diff --git a/drivers/s390/cio/qdio_perf.c b/drivers/s390/cio/qdio_perf.c index eff943923c6f..968e3c7c2632 100644 --- a/drivers/s390/cio/qdio_perf.c +++ b/drivers/s390/cio/qdio_perf.c @@ -84,7 +84,7 @@ static int qdio_perf_seq_open(struct inode *inode, struct file *filp) return single_open(filp, qdio_perf_proc_show, NULL); } -static struct file_operations qdio_perf_proc_fops = { +static const struct file_operations qdio_perf_proc_fops = { .owner = THIS_MODULE, .open = qdio_perf_seq_open, .read = seq_read, diff --git a/drivers/s390/crypto/ap_bus.c b/drivers/s390/crypto/ap_bus.c index 090b32a339c6..1294876bf7b4 100644 --- a/drivers/s390/crypto/ap_bus.c +++ b/drivers/s390/crypto/ap_bus.c @@ -60,6 +60,7 @@ static int ap_device_probe(struct device *dev); static void ap_interrupt_handler(void *unused1, void *unused2); static void ap_reset(struct ap_device *ap_dev); static void ap_config_timeout(unsigned long ptr); +static int ap_select_domain(void); /* * Module description. @@ -109,6 +110,10 @@ static unsigned long long poll_timeout = 250000; /* Suspend flag */ static int ap_suspend_flag; +/* Flag to check if domain was set through module parameter domain=. This is + * important when supsend and resume is done in a z/VM environment where the + * domain might change. */ +static int user_set_domain = 0; static struct bus_type ap_bus_type; /** @@ -643,6 +648,7 @@ static int ap_bus_suspend(struct device *dev, pm_message_t state) destroy_workqueue(ap_work_queue); ap_work_queue = NULL; } + tasklet_disable(&ap_tasklet); } /* Poll on the device until all requests are finished. */ @@ -653,7 +659,10 @@ static int ap_bus_suspend(struct device *dev, pm_message_t state) spin_unlock_bh(&ap_dev->lock); } while ((flags & 1) || (flags & 2)); - ap_device_remove(dev); + spin_lock_bh(&ap_dev->lock); + ap_dev->unregistered = 1; + spin_unlock_bh(&ap_dev->lock); + return 0; } @@ -666,11 +675,10 @@ static int ap_bus_resume(struct device *dev) ap_suspend_flag = 0; if (!ap_interrupts_available()) ap_interrupt_indicator = NULL; - ap_device_probe(dev); - ap_reset(ap_dev); - setup_timer(&ap_dev->timeout, ap_request_timeout, - (unsigned long) ap_dev); - ap_scan_bus(NULL); + if (!user_set_domain) { + ap_domain_index = -1; + ap_select_domain(); + } init_timer(&ap_config_timer); ap_config_timer.function = ap_config_timeout; ap_config_timer.data = 0; @@ -686,12 +694,14 @@ static int ap_bus_resume(struct device *dev) tasklet_schedule(&ap_tasklet); if (ap_thread_flag) rc = ap_poll_thread_start(); - } else { - ap_device_probe(dev); - ap_reset(ap_dev); - setup_timer(&ap_dev->timeout, ap_request_timeout, - (unsigned long) ap_dev); } + if (AP_QID_QUEUE(ap_dev->qid) != ap_domain_index) { + spin_lock_bh(&ap_dev->lock); + ap_dev->qid = AP_MKQID(AP_QID_DEVICE(ap_dev->qid), + ap_domain_index); + spin_unlock_bh(&ap_dev->lock); + } + queue_work(ap_work_queue, &ap_config_work); return rc; } @@ -1079,6 +1089,8 @@ static void ap_scan_bus(struct work_struct *unused) spin_lock_bh(&ap_dev->lock); if (rc || ap_dev->unregistered) { spin_unlock_bh(&ap_dev->lock); + if (ap_dev->unregistered) + i--; device_unregister(dev); put_device(dev); continue; @@ -1586,6 +1598,12 @@ int __init ap_module_init(void) ap_domain_index); return -EINVAL; } + /* In resume callback we need to know if the user had set the domain. + * If so, we can not just reset it. + */ + if (ap_domain_index >= 0) + user_set_domain = 1; + if (ap_instructions_available() != 0) { pr_warning("The hardware system does not support " "AP instructions\n"); diff --git a/drivers/s390/crypto/zcrypt_pcixcc.c b/drivers/s390/crypto/zcrypt_pcixcc.c index c20d4790258e..5677b40e4ac0 100644 --- a/drivers/s390/crypto/zcrypt_pcixcc.c +++ b/drivers/s390/crypto/zcrypt_pcixcc.c @@ -361,7 +361,7 @@ static void rng_type6CPRB_msgX(struct ap_device *ap_dev, .ToCardLen1 = sizeof *msg - sizeof(msg->hdr), .FromCardLen1 = sizeof *msg - sizeof(msg->hdr), }; - static struct CPRBX static_cprbx = { + static struct CPRBX local_cprbx = { .cprb_len = 0x00dc, .cprb_ver_id = 0x02, .func_id = {0x54, 0x32}, @@ -372,7 +372,7 @@ static void rng_type6CPRB_msgX(struct ap_device *ap_dev, msg->hdr = static_type6_hdrX; msg->hdr.FromCardLen2 = random_number_length, - msg->cprbx = static_cprbx; + msg->cprbx = local_cprbx; msg->cprbx.rpl_datal = random_number_length, msg->cprbx.domain = AP_QID_QUEUE(ap_dev->qid); memcpy(msg->function_code, msg->hdr.function_code, 0x02); @@ -561,7 +561,8 @@ static int convert_response_ica(struct zcrypt_device *zdev, if (msg->cprbx.cprb_ver_id == 0x02) return convert_type86_ica(zdev, reply, outputdata, outputdatalength); - /* no break, incorrect cprb version is an unknown response */ + /* Fall through, no break, incorrect cprb version is an unknown + * response */ default: /* Unknown response type, this should NEVER EVER happen */ zdev->online = 0; return -EAGAIN; /* repeat the request on a different device. */ @@ -587,7 +588,8 @@ static int convert_response_xcrb(struct zcrypt_device *zdev, } if (msg->cprbx.cprb_ver_id == 0x02) return convert_type86_xcrb(zdev, reply, xcRB); - /* no break, incorrect cprb version is an unknown response */ + /* Fall through, no break, incorrect cprb version is an unknown + * response */ default: /* Unknown response type, this should NEVER EVER happen */ xcRB->status = 0x0008044DL; /* HDD_InvalidParm */ zdev->online = 0; @@ -610,7 +612,8 @@ static int convert_response_rng(struct zcrypt_device *zdev, return -EINVAL; if (msg->cprbx.cprb_ver_id == 0x02) return convert_type86_rng(zdev, reply, data); - /* no break, incorrect cprb version is an unknown response */ + /* Fall through, no break, incorrect cprb version is an unknown + * response */ default: /* Unknown response type, this should NEVER EVER happen */ zdev->online = 0; return -EAGAIN; /* repeat the request on a different device. */ diff --git a/drivers/s390/net/smsgiucv.c b/drivers/s390/net/smsgiucv.c index 102000d1af6f..3012355f8304 100644 --- a/drivers/s390/net/smsgiucv.c +++ b/drivers/s390/net/smsgiucv.c @@ -158,7 +158,12 @@ static int smsg_pm_restore_thaw(struct device *dev) smsg_path->flags = 0; rc = iucv_path_connect(smsg_path, &smsg_handler, "*MSG ", NULL, NULL, NULL); - printk(KERN_ERR "iucv_path_connect returned with rc %i\n", rc); +#ifdef CONFIG_PM_DEBUG + if (rc) + printk(KERN_ERR + "iucv_path_connect returned with rc %i\n", rc); +#endif + cpcmd("SET SMSG IUCV", NULL, 0, NULL); } return 0; } diff --git a/drivers/s390/scsi/zfcp_aux.c b/drivers/s390/scsi/zfcp_aux.c index 1be6bf7e8ce6..2889e5f2dfd3 100644 --- a/drivers/s390/scsi/zfcp_aux.c +++ b/drivers/s390/scsi/zfcp_aux.c @@ -80,28 +80,35 @@ int zfcp_reqlist_isempty(struct zfcp_adapter *adapter) static void __init zfcp_init_device_configure(char *busid, u64 wwpn, u64 lun) { + struct ccw_device *ccwdev; struct zfcp_adapter *adapter; struct zfcp_port *port; struct zfcp_unit *unit; - mutex_lock(&zfcp_data.config_mutex); - read_lock_irq(&zfcp_data.config_lock); - adapter = zfcp_get_adapter_by_busid(busid); - if (adapter) - zfcp_adapter_get(adapter); - read_unlock_irq(&zfcp_data.config_lock); + ccwdev = get_ccwdev_by_busid(&zfcp_ccw_driver, busid); + if (!ccwdev) + return; + + if (ccw_device_set_online(ccwdev)) + goto out_ccwdev; + mutex_lock(&zfcp_data.config_mutex); + adapter = dev_get_drvdata(&ccwdev->dev); if (!adapter) - goto out_adapter; - port = zfcp_port_enqueue(adapter, wwpn, 0, 0); - if (IS_ERR(port)) + goto out_unlock; + zfcp_adapter_get(adapter); + + port = zfcp_get_port_by_wwpn(adapter, wwpn); + if (!port) goto out_port; + + zfcp_port_get(port); unit = zfcp_unit_enqueue(port, lun); if (IS_ERR(unit)) goto out_unit; mutex_unlock(&zfcp_data.config_mutex); - ccw_device_set_online(adapter->ccw_device); + zfcp_erp_unit_reopen(unit, 0, "auidc_1", NULL); zfcp_erp_wait(adapter); flush_work(&unit->scsi_work); @@ -111,20 +118,23 @@ out_unit: zfcp_port_put(port); out_port: zfcp_adapter_put(adapter); -out_adapter: +out_unlock: mutex_unlock(&zfcp_data.config_mutex); +out_ccwdev: + put_device(&ccwdev->dev); return; } static void __init zfcp_init_device_setup(char *devstr) { char *token; - char *str; + char *str, *str_saved; char busid[ZFCP_BUS_ID_SIZE]; u64 wwpn, lun; /* duplicate devstr and keep the original for sysfs presentation*/ - str = kmalloc(strlen(devstr) + 1, GFP_KERNEL); + str_saved = kmalloc(strlen(devstr) + 1, GFP_KERNEL); + str = str_saved; if (!str) return; @@ -143,12 +153,12 @@ static void __init zfcp_init_device_setup(char *devstr) if (!token || strict_strtoull(token, 0, (unsigned long long *) &lun)) goto err_out; - kfree(str); + kfree(str_saved); zfcp_init_device_configure(busid, wwpn, lun); return; - err_out: - kfree(str); +err_out: + kfree(str_saved); pr_err("%s is not a valid SCSI device\n", devstr); } @@ -593,10 +603,8 @@ void zfcp_adapter_dequeue(struct zfcp_adapter *adapter) int retval = 0; unsigned long flags; - cancel_work_sync(&adapter->scan_work); cancel_work_sync(&adapter->stat_work); zfcp_fc_wka_ports_force_offline(adapter->gs); - zfcp_adapter_scsi_unregister(adapter); sysfs_remove_group(&adapter->ccw_device->dev.kobj, &zfcp_sysfs_adapter_attrs); dev_set_drvdata(&adapter->ccw_device->dev, NULL); diff --git a/drivers/s390/scsi/zfcp_ccw.c b/drivers/s390/scsi/zfcp_ccw.c index 0c90f8e71605..e08339428ecf 100644 --- a/drivers/s390/scsi/zfcp_ccw.c +++ b/drivers/s390/scsi/zfcp_ccw.c @@ -102,6 +102,14 @@ static void zfcp_ccw_remove(struct ccw_device *ccw_device) adapter = dev_get_drvdata(&ccw_device->dev); if (!adapter) goto out; + mutex_unlock(&zfcp_data.config_mutex); + + cancel_work_sync(&adapter->scan_work); + + mutex_lock(&zfcp_data.config_mutex); + + /* this also removes the scsi devices, so call it first */ + zfcp_adapter_scsi_unregister(adapter); write_lock_irq(&zfcp_data.config_lock); list_for_each_entry_safe(port, p, &adapter->port_list_head, list) { @@ -117,11 +125,8 @@ static void zfcp_ccw_remove(struct ccw_device *ccw_device) write_unlock_irq(&zfcp_data.config_lock); list_for_each_entry_safe(port, p, &port_remove_lh, list) { - list_for_each_entry_safe(unit, u, &unit_remove_lh, list) { - if (unit->device) - scsi_remove_device(unit->device); + list_for_each_entry_safe(unit, u, &unit_remove_lh, list) zfcp_unit_dequeue(unit); - } zfcp_port_dequeue(port); } wait_event(adapter->remove_wq, atomic_read(&adapter->refcount) == 0); @@ -192,13 +197,9 @@ static int zfcp_ccw_set_offline(struct ccw_device *ccw_device) mutex_lock(&zfcp_data.config_mutex); adapter = dev_get_drvdata(&ccw_device->dev); - if (!adapter) - goto out; - zfcp_erp_adapter_shutdown(adapter, 0, "ccsoff1", NULL); zfcp_erp_wait(adapter); mutex_unlock(&zfcp_data.config_mutex); -out: return 0; } @@ -253,13 +254,17 @@ static void zfcp_ccw_shutdown(struct ccw_device *cdev) mutex_lock(&zfcp_data.config_mutex); adapter = dev_get_drvdata(&cdev->dev); + if (!adapter) + goto out; + zfcp_erp_adapter_shutdown(adapter, 0, "ccshut1", NULL); zfcp_erp_wait(adapter); zfcp_erp_thread_kill(adapter); +out: mutex_unlock(&zfcp_data.config_mutex); } -static struct ccw_driver zfcp_ccw_driver = { +struct ccw_driver zfcp_ccw_driver = { .owner = THIS_MODULE, .name = "zfcp", .ids = zfcp_ccw_device_id, @@ -284,20 +289,3 @@ int __init zfcp_ccw_register(void) { return ccw_driver_register(&zfcp_ccw_driver); } - -/** - * zfcp_get_adapter_by_busid - find zfcp_adapter struct - * @busid: bus id string of zfcp adapter to find - */ -struct zfcp_adapter *zfcp_get_adapter_by_busid(char *busid) -{ - struct ccw_device *ccw_device; - struct zfcp_adapter *adapter = NULL; - - ccw_device = get_ccwdev_by_busid(&zfcp_ccw_driver, busid); - if (ccw_device) { - adapter = dev_get_drvdata(&ccw_device->dev); - put_device(&ccw_device->dev); - } - return adapter; -} diff --git a/drivers/s390/scsi/zfcp_cfdc.c b/drivers/s390/scsi/zfcp_cfdc.c index 8305c874e86f..ef681dfed0cc 100644 --- a/drivers/s390/scsi/zfcp_cfdc.c +++ b/drivers/s390/scsi/zfcp_cfdc.c @@ -86,8 +86,23 @@ static int zfcp_cfdc_copy_to_user(void __user *user_buffer, static struct zfcp_adapter *zfcp_cfdc_get_adapter(u32 devno) { char busid[9]; + struct ccw_device *ccwdev; + struct zfcp_adapter *adapter = NULL; + snprintf(busid, sizeof(busid), "0.0.%04x", devno); - return zfcp_get_adapter_by_busid(busid); + ccwdev = get_ccwdev_by_busid(&zfcp_ccw_driver, busid); + if (!ccwdev) + goto out; + + adapter = dev_get_drvdata(&ccwdev->dev); + if (!adapter) + goto out_put; + + zfcp_adapter_get(adapter); +out_put: + put_device(&ccwdev->dev); +out: + return adapter; } static int zfcp_cfdc_set_fsf(struct zfcp_fsf_cfdc *fsf_cfdc, int command) diff --git a/drivers/s390/scsi/zfcp_erp.c b/drivers/s390/scsi/zfcp_erp.c index 73d366ba31e5..f73e2180f333 100644 --- a/drivers/s390/scsi/zfcp_erp.c +++ b/drivers/s390/scsi/zfcp_erp.c @@ -858,10 +858,7 @@ static int zfcp_erp_port_strategy_open_common(struct zfcp_erp_action *act) if (fc_host_port_type(adapter->scsi_host) == FC_PORTTYPE_PTP) return zfcp_erp_open_ptp_port(act); if (!port->d_id) { - zfcp_port_get(port); - if (!queue_work(adapter->work_queue, - &port->gid_pn_work)) - zfcp_port_put(port); + zfcp_fc_trigger_did_lookup(port); return ZFCP_ERP_EXIT; } return zfcp_erp_port_strategy_open_port(act); @@ -869,12 +866,11 @@ static int zfcp_erp_port_strategy_open_common(struct zfcp_erp_action *act) case ZFCP_ERP_STEP_PORT_OPENING: /* D_ID might have changed during open */ if (p_status & ZFCP_STATUS_COMMON_OPEN) { - if (port->d_id) - return ZFCP_ERP_SUCCEEDED; - else { - act->step = ZFCP_ERP_STEP_PORT_CLOSING; - return ZFCP_ERP_CONTINUES; + if (!port->d_id) { + zfcp_fc_trigger_did_lookup(port); + return ZFCP_ERP_EXIT; } + return ZFCP_ERP_SUCCEEDED; } if (port->d_id && !(p_status & ZFCP_STATUS_COMMON_NOESC)) { port->d_id = 0; @@ -889,19 +885,21 @@ static int zfcp_erp_port_strategy_open_common(struct zfcp_erp_action *act) static int zfcp_erp_port_strategy(struct zfcp_erp_action *erp_action) { struct zfcp_port *port = erp_action->port; + int p_status = atomic_read(&port->status); - if (atomic_read(&port->status) & ZFCP_STATUS_COMMON_NOESC) + if ((p_status & ZFCP_STATUS_COMMON_NOESC) && + !(p_status & ZFCP_STATUS_COMMON_OPEN)) goto close_init_done; switch (erp_action->step) { case ZFCP_ERP_STEP_UNINITIALIZED: zfcp_erp_port_strategy_clearstati(port); - if (atomic_read(&port->status) & ZFCP_STATUS_COMMON_OPEN) + if (p_status & ZFCP_STATUS_COMMON_OPEN) return zfcp_erp_port_strategy_close(erp_action); break; case ZFCP_ERP_STEP_PORT_CLOSING: - if (atomic_read(&port->status) & ZFCP_STATUS_COMMON_OPEN) + if (p_status & ZFCP_STATUS_COMMON_OPEN) return ZFCP_ERP_FAILED; break; } diff --git a/drivers/s390/scsi/zfcp_ext.h b/drivers/s390/scsi/zfcp_ext.h index 36935bc0818f..b3f28deb4505 100644 --- a/drivers/s390/scsi/zfcp_ext.h +++ b/drivers/s390/scsi/zfcp_ext.h @@ -28,7 +28,7 @@ extern int zfcp_sg_setup_table(struct scatterlist *, int); /* zfcp_ccw.c */ extern int zfcp_ccw_register(void); extern int zfcp_ccw_priv_sch(struct zfcp_adapter *); -extern struct zfcp_adapter *zfcp_get_adapter_by_busid(char *); +extern struct ccw_driver zfcp_ccw_driver; /* zfcp_cfdc.c */ extern struct miscdevice zfcp_cfdc_misc; @@ -96,6 +96,7 @@ extern int zfcp_fc_scan_ports(struct zfcp_adapter *); extern void _zfcp_fc_scan_ports_later(struct work_struct *); extern void zfcp_fc_incoming_els(struct zfcp_fsf_req *); extern void zfcp_fc_port_did_lookup(struct work_struct *); +extern void zfcp_fc_trigger_did_lookup(struct zfcp_port *); extern void zfcp_fc_plogi_evaluate(struct zfcp_port *, struct fsf_plogi *); extern void zfcp_fc_test_link(struct zfcp_port *); extern void zfcp_fc_link_test_work(struct work_struct *); diff --git a/drivers/s390/scsi/zfcp_fc.c b/drivers/s390/scsi/zfcp_fc.c index 722f22de8753..df23bcead23d 100644 --- a/drivers/s390/scsi/zfcp_fc.c +++ b/drivers/s390/scsi/zfcp_fc.c @@ -361,6 +361,17 @@ out: } /** + * zfcp_fc_trigger_did_lookup - trigger the d_id lookup using a GID_PN request + * @port: The zfcp_port to lookup the d_id for. + */ +void zfcp_fc_trigger_did_lookup(struct zfcp_port *port) +{ + zfcp_port_get(port); + if (!queue_work(port->adapter->work_queue, &port->gid_pn_work)) + zfcp_port_put(port); +} + +/** * zfcp_fc_plogi_evaluate - evaluate PLOGI playload * @port: zfcp_port structure * @plogi: plogi payload diff --git a/drivers/s390/scsi/zfcp_fsf.c b/drivers/s390/scsi/zfcp_fsf.c index f09c863dc6bd..4e41baa0c141 100644 --- a/drivers/s390/scsi/zfcp_fsf.c +++ b/drivers/s390/scsi/zfcp_fsf.c @@ -1058,14 +1058,28 @@ static int zfcp_fsf_setup_ct_els_sbals(struct zfcp_fsf_req *req, bytes = zfcp_qdio_sbals_from_sg(adapter->qdio, &req->queue_req, SBAL_FLAGS0_TYPE_WRITE_READ, sg_resp, max_sbals); + req->qtcb->bottom.support.resp_buf_length = bytes; if (bytes <= 0) return -EIO; + return 0; +} + +static int zfcp_fsf_setup_ct_els(struct zfcp_fsf_req *req, + struct scatterlist *sg_req, + struct scatterlist *sg_resp, + int max_sbals) +{ + int ret; + + ret = zfcp_fsf_setup_ct_els_sbals(req, sg_req, sg_resp, max_sbals); + if (ret) + return ret; + /* common settings for ct/gs and els requests */ - req->qtcb->bottom.support.resp_buf_length = bytes; req->qtcb->bottom.support.service_class = FSF_CLASS_3; req->qtcb->bottom.support.timeout = 2 * R_A_TOV; - zfcp_fsf_start_timer(req, 2 * R_A_TOV + 10); + zfcp_fsf_start_timer(req, (2 * R_A_TOV + 10) * HZ); return 0; } @@ -1094,8 +1108,8 @@ int zfcp_fsf_send_ct(struct zfcp_send_ct *ct, mempool_t *pool) } req->status |= ZFCP_STATUS_FSFREQ_CLEANUP; - ret = zfcp_fsf_setup_ct_els_sbals(req, ct->req, ct->resp, - FSF_MAX_SBALS_PER_REQ); + ret = zfcp_fsf_setup_ct_els(req, ct->req, ct->resp, + FSF_MAX_SBALS_PER_REQ); if (ret) goto failed_send; @@ -1192,7 +1206,7 @@ int zfcp_fsf_send_els(struct zfcp_send_els *els) } req->status |= ZFCP_STATUS_FSFREQ_CLEANUP; - ret = zfcp_fsf_setup_ct_els_sbals(req, els->req, els->resp, 2); + ret = zfcp_fsf_setup_ct_els(req, els->req, els->resp, 2); if (ret) goto failed_send; @@ -1461,9 +1475,16 @@ static void zfcp_fsf_open_port_handler(struct zfcp_fsf_req *req) plogi = (struct fsf_plogi *) req->qtcb->bottom.support.els; if (req->qtcb->bottom.support.els1_length >= FSF_PLOGI_MIN_LEN) { - if (plogi->serv_param.wwpn != port->wwpn) + if (plogi->serv_param.wwpn != port->wwpn) { port->d_id = 0; - else { + dev_warn(&port->adapter->ccw_device->dev, + "A port opened with WWPN 0x%016Lx " + "returned data that identifies it as " + "WWPN 0x%016Lx\n", + (unsigned long long) port->wwpn, + (unsigned long long) + plogi->serv_param.wwpn); + } else { port->wwnn = plogi->serv_param.wwnn; zfcp_fc_plogi_evaluate(port, plogi); } diff --git a/drivers/s390/scsi/zfcp_sysfs.c b/drivers/s390/scsi/zfcp_sysfs.c index 079a8cf518a3..d31000886ca8 100644 --- a/drivers/s390/scsi/zfcp_sysfs.c +++ b/drivers/s390/scsi/zfcp_sysfs.c @@ -224,6 +224,7 @@ static ssize_t zfcp_sysfs_unit_add_store(struct device *dev, zfcp_erp_unit_reopen(unit, 0, "syuas_1", NULL); zfcp_erp_wait(unit->port->adapter); + flush_work(&unit->scsi_work); zfcp_unit_put(unit); out: mutex_unlock(&zfcp_data.config_mutex); diff --git a/drivers/scsi/Kconfig b/drivers/scsi/Kconfig index 82bb3b2d207a..e11cca4c784c 100644 --- a/drivers/scsi/Kconfig +++ b/drivers/scsi/Kconfig @@ -366,6 +366,7 @@ config ISCSI_TCP source "drivers/scsi/cxgb3i/Kconfig" source "drivers/scsi/bnx2i/Kconfig" +source "drivers/scsi/be2iscsi/Kconfig" config SGIWD93_SCSI tristate "SGI WD93C93 SCSI Driver" @@ -1827,6 +1828,16 @@ config SCSI_SRP To compile this driver as a module, choose M here: the module will be called libsrp. +config SCSI_BFA_FC + tristate "Brocade BFA Fibre Channel Support" + depends on PCI && SCSI + select SCSI_FC_ATTRS + help + This bfa driver supports all Brocade PCIe FC/FCOE host adapters. + + To compile this driver as a module, choose M here. The module will + be called bfa. + endif # SCSI_LOWLEVEL source "drivers/scsi/pcmcia/Kconfig" diff --git a/drivers/scsi/Makefile b/drivers/scsi/Makefile index 61a94af3cee7..3ad61db5e3fa 100644 --- a/drivers/scsi/Makefile +++ b/drivers/scsi/Makefile @@ -86,6 +86,7 @@ obj-$(CONFIG_SCSI_QLOGIC_1280) += qla1280.o obj-$(CONFIG_SCSI_QLA_FC) += qla2xxx/ obj-$(CONFIG_SCSI_QLA_ISCSI) += qla4xxx/ obj-$(CONFIG_SCSI_LPFC) += lpfc/ +obj-$(CONFIG_SCSI_BFA_FC) += bfa/ obj-$(CONFIG_SCSI_PAS16) += pas16.o obj-$(CONFIG_SCSI_T128) += t128.o obj-$(CONFIG_SCSI_DMX3191D) += dmx3191d.o @@ -130,6 +131,7 @@ obj-$(CONFIG_SCSI_MVSAS) += mvsas/ obj-$(CONFIG_PS3_ROM) += ps3rom.o obj-$(CONFIG_SCSI_CXGB3_ISCSI) += libiscsi.o libiscsi_tcp.o cxgb3i/ obj-$(CONFIG_SCSI_BNX2_ISCSI) += libiscsi.o bnx2i/ +obj-$(CONFIG_BE2ISCSI) += libiscsi.o be2iscsi/ obj-$(CONFIG_SCSI_PMCRAID) += pmcraid.o obj-$(CONFIG_ARM) += arm/ diff --git a/drivers/scsi/be2iscsi/Kconfig b/drivers/scsi/be2iscsi/Kconfig new file mode 100644 index 000000000000..2952fcd008ea --- /dev/null +++ b/drivers/scsi/be2iscsi/Kconfig @@ -0,0 +1,8 @@ +config BE2ISCSI + tristate "ServerEngines' 10Gbps iSCSI - BladeEngine 2" + depends on PCI && SCSI + select SCSI_ISCSI_ATTRS + + help + This driver implements the iSCSI functionality for ServerEngines' + 10Gbps Storage adapter - BladeEngine 2. diff --git a/drivers/scsi/be2iscsi/Makefile b/drivers/scsi/be2iscsi/Makefile new file mode 100644 index 000000000000..c11f443e3f83 --- /dev/null +++ b/drivers/scsi/be2iscsi/Makefile @@ -0,0 +1,8 @@ +# +# Makefile to build the iSCSI driver for ServerEngine's BladeEngine. +# +# + +obj-$(CONFIG_BE2ISCSI) += be2iscsi.o + +be2iscsi-y := be_iscsi.o be_main.o be_mgmt.o be_cmds.o diff --git a/drivers/scsi/be2iscsi/be.h b/drivers/scsi/be2iscsi/be.h new file mode 100644 index 000000000000..b36020dcf012 --- /dev/null +++ b/drivers/scsi/be2iscsi/be.h @@ -0,0 +1,183 @@ +/** + * Copyright (C) 2005 - 2009 ServerEngines + * All rights reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. The full GNU General + * Public License is included in this distribution in the file called COPYING. + * + * Contact Information: + * linux-drivers@serverengines.com + * + * ServerEngines + * 209 N. Fair Oaks Ave + * Sunnyvale, CA 94085 + */ + +#ifndef BEISCSI_H +#define BEISCSI_H + +#include <linux/pci.h> +#include <linux/if_vlan.h> + +#define FW_VER_LEN 32 + +struct be_dma_mem { + void *va; + dma_addr_t dma; + u32 size; +}; + +struct be_queue_info { + struct be_dma_mem dma_mem; + u16 len; + u16 entry_size; /* Size of an element in the queue */ + u16 id; + u16 tail, head; + bool created; + atomic_t used; /* Number of valid elements in the queue */ +}; + +static inline u32 MODULO(u16 val, u16 limit) +{ + WARN_ON(limit & (limit - 1)); + return val & (limit - 1); +} + +static inline void index_inc(u16 *index, u16 limit) +{ + *index = MODULO((*index + 1), limit); +} + +static inline void *queue_head_node(struct be_queue_info *q) +{ + return q->dma_mem.va + q->head * q->entry_size; +} + +static inline void *queue_tail_node(struct be_queue_info *q) +{ + return q->dma_mem.va + q->tail * q->entry_size; +} + +static inline void queue_head_inc(struct be_queue_info *q) +{ + index_inc(&q->head, q->len); +} + +static inline void queue_tail_inc(struct be_queue_info *q) +{ + index_inc(&q->tail, q->len); +} + +/*ISCSI */ + +struct be_eq_obj { + struct be_queue_info q; + char desc[32]; + + /* Adaptive interrupt coalescing (AIC) info */ + bool enable_aic; + u16 min_eqd; /* in usecs */ + u16 max_eqd; /* in usecs */ + u16 cur_eqd; /* in usecs */ +}; + +struct be_mcc_obj { + struct be_queue_info *q; + struct be_queue_info *cq; +}; + +struct be_ctrl_info { + u8 __iomem *csr; + u8 __iomem *db; /* Door Bell */ + u8 __iomem *pcicfg; /* PCI config space */ + struct pci_dev *pdev; + + /* Mbox used for cmd request/response */ + spinlock_t mbox_lock; /* For serializing mbox cmds to BE card */ + struct be_dma_mem mbox_mem; + /* Mbox mem is adjusted to align to 16 bytes. The allocated addr + * is stored for freeing purpose */ + struct be_dma_mem mbox_mem_alloced; + + /* MCC Rings */ + struct be_mcc_obj mcc_obj; + spinlock_t mcc_lock; /* For serializing mcc cmds to BE card */ + spinlock_t mcc_cq_lock; + + /* MCC Async callback */ + void (*async_cb) (void *adapter, bool link_up); + void *adapter_ctxt; +}; + +#include "be_cmds.h" + +#define PAGE_SHIFT_4K 12 +#define PAGE_SIZE_4K (1 << PAGE_SHIFT_4K) + +/* Returns number of pages spanned by the data starting at the given addr */ +#define PAGES_4K_SPANNED(_address, size) \ + ((u32)((((size_t)(_address) & (PAGE_SIZE_4K - 1)) + \ + (size) + (PAGE_SIZE_4K - 1)) >> PAGE_SHIFT_4K)) + +/* Byte offset into the page corresponding to given address */ +#define OFFSET_IN_PAGE(addr) \ + ((size_t)(addr) & (PAGE_SIZE_4K-1)) + +/* Returns bit offset within a DWORD of a bitfield */ +#define AMAP_BIT_OFFSET(_struct, field) \ + (((size_t)&(((_struct *)0)->field))%32) + +/* Returns the bit mask of the field that is NOT shifted into location. */ +static inline u32 amap_mask(u32 bitsize) +{ + return (bitsize == 32 ? 0xFFFFFFFF : (1 << bitsize) - 1); +} + +static inline void amap_set(void *ptr, u32 dw_offset, u32 mask, + u32 offset, u32 value) +{ + u32 *dw = (u32 *) ptr + dw_offset; + *dw &= ~(mask << offset); + *dw |= (mask & value) << offset; +} + +#define AMAP_SET_BITS(_struct, field, ptr, val) \ + amap_set(ptr, \ + offsetof(_struct, field)/32, \ + amap_mask(sizeof(((_struct *)0)->field)), \ + AMAP_BIT_OFFSET(_struct, field), \ + val) + +static inline u32 amap_get(void *ptr, u32 dw_offset, u32 mask, u32 offset) +{ + u32 *dw = ptr; + return mask & (*(dw + dw_offset) >> offset); +} + +#define AMAP_GET_BITS(_struct, field, ptr) \ + amap_get(ptr, \ + offsetof(_struct, field)/32, \ + amap_mask(sizeof(((_struct *)0)->field)), \ + AMAP_BIT_OFFSET(_struct, field)) + +#define be_dws_cpu_to_le(wrb, len) swap_dws(wrb, len) +#define be_dws_le_to_cpu(wrb, len) swap_dws(wrb, len) +static inline void swap_dws(void *wrb, int len) +{ +#ifdef __BIG_ENDIAN + u32 *dw = wrb; + WARN_ON(len % 4); + do { + *dw = cpu_to_le32(*dw); + dw++; + len -= 4; + } while (len); +#endif /* __BIG_ENDIAN */ +} + +extern void beiscsi_cq_notify(struct be_ctrl_info *ctrl, u16 qid, bool arm, + u16 num_popped); + +#endif /* BEISCSI_H */ diff --git a/drivers/scsi/be2iscsi/be_cmds.c b/drivers/scsi/be2iscsi/be_cmds.c new file mode 100644 index 000000000000..08007b6e42df --- /dev/null +++ b/drivers/scsi/be2iscsi/be_cmds.c @@ -0,0 +1,523 @@ +/** + * Copyright (C) 2005 - 2009 ServerEngines + * All rights reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. The full GNU General + * Public License is included in this distribution in the file called COPYING. + * + * Contact Information: + * linux-drivers@serverengines.com + * + * ServerEngines + * 209 N. Fair Oaks Ave + * Sunnyvale, CA 94085 + */ + +#include "be.h" +#include "be_mgmt.h" +#include "be_main.h" + +static inline bool be_mcc_compl_is_new(struct be_mcc_compl *compl) +{ + if (compl->flags != 0) { + compl->flags = le32_to_cpu(compl->flags); + WARN_ON((compl->flags & CQE_FLAGS_VALID_MASK) == 0); + return true; + } else + return false; +} + +static inline void be_mcc_compl_use(struct be_mcc_compl *compl) +{ + compl->flags = 0; +} + +static int be_mcc_compl_process(struct be_ctrl_info *ctrl, + struct be_mcc_compl *compl) +{ + u16 compl_status, extd_status; + + be_dws_le_to_cpu(compl, 4); + + compl_status = (compl->status >> CQE_STATUS_COMPL_SHIFT) & + CQE_STATUS_COMPL_MASK; + if (compl_status != MCC_STATUS_SUCCESS) { + extd_status = (compl->status >> CQE_STATUS_EXTD_SHIFT) & + CQE_STATUS_EXTD_MASK; + dev_err(&ctrl->pdev->dev, + "error in cmd completion: status(compl/extd)=%d/%d\n", + compl_status, extd_status); + return -1; + } + return 0; +} + +static inline bool is_link_state_evt(u32 trailer) +{ + return (((trailer >> ASYNC_TRAILER_EVENT_CODE_SHIFT) & + ASYNC_TRAILER_EVENT_CODE_MASK) == ASYNC_EVENT_CODE_LINK_STATE); +} + +void beiscsi_cq_notify(struct be_ctrl_info *ctrl, u16 qid, bool arm, + u16 num_popped) +{ + u32 val = 0; + val |= qid & DB_CQ_RING_ID_MASK; + if (arm) + val |= 1 << DB_CQ_REARM_SHIFT; + val |= num_popped << DB_CQ_NUM_POPPED_SHIFT; + iowrite32(val, ctrl->db + DB_CQ_OFFSET); +} + +static int be_mbox_db_ready_wait(struct be_ctrl_info *ctrl) +{ +#define long_delay 2000 + void __iomem *db = ctrl->db + MPU_MAILBOX_DB_OFFSET; + int cnt = 0, wait = 5; /* in usecs */ + u32 ready; + + do { + ready = ioread32(db) & MPU_MAILBOX_DB_RDY_MASK; + if (ready) + break; + + if (cnt > 6000000) { + dev_err(&ctrl->pdev->dev, "mbox_db poll timed out\n"); + return -1; + } + + if (cnt > 50) { + wait = long_delay; + mdelay(long_delay / 1000); + } else + udelay(wait); + cnt += wait; + } while (true); + return 0; +} + +int be_mbox_notify(struct be_ctrl_info *ctrl) +{ + int status; + u32 val = 0; + void __iomem *db = ctrl->db + MPU_MAILBOX_DB_OFFSET; + struct be_dma_mem *mbox_mem = &ctrl->mbox_mem; + struct be_mcc_mailbox *mbox = mbox_mem->va; + struct be_mcc_compl *compl = &mbox->compl; + + val &= ~MPU_MAILBOX_DB_RDY_MASK; + val |= MPU_MAILBOX_DB_HI_MASK; + val |= (upper_32_bits(mbox_mem->dma) >> 2) << 2; + iowrite32(val, db); + + status = be_mbox_db_ready_wait(ctrl); + if (status != 0) { + SE_DEBUG(DBG_LVL_1, " be_mbox_db_ready_wait failed 1\n"); + return status; + } + val = 0; + val &= ~MPU_MAILBOX_DB_RDY_MASK; + val &= ~MPU_MAILBOX_DB_HI_MASK; + val |= (u32) (mbox_mem->dma >> 4) << 2; + iowrite32(val, db); + + status = be_mbox_db_ready_wait(ctrl); + if (status != 0) { + SE_DEBUG(DBG_LVL_1, " be_mbox_db_ready_wait failed 2\n"); + return status; + } + if (be_mcc_compl_is_new(compl)) { + status = be_mcc_compl_process(ctrl, &mbox->compl); + be_mcc_compl_use(compl); + if (status) { + SE_DEBUG(DBG_LVL_1, "After be_mcc_compl_process \n"); + return status; + } + } else { + dev_err(&ctrl->pdev->dev, "invalid mailbox completion\n"); + return -1; + } + return 0; +} + +void be_wrb_hdr_prepare(struct be_mcc_wrb *wrb, int payload_len, + bool embedded, u8 sge_cnt) +{ + if (embedded) + wrb->embedded |= MCC_WRB_EMBEDDED_MASK; + else + wrb->embedded |= (sge_cnt & MCC_WRB_SGE_CNT_MASK) << + MCC_WRB_SGE_CNT_SHIFT; + wrb->payload_length = payload_len; + be_dws_cpu_to_le(wrb, 8); +} + +void be_cmd_hdr_prepare(struct be_cmd_req_hdr *req_hdr, + u8 subsystem, u8 opcode, int cmd_len) +{ + req_hdr->opcode = opcode; + req_hdr->subsystem = subsystem; + req_hdr->request_length = cpu_to_le32(cmd_len - sizeof(*req_hdr)); +} + +static void be_cmd_page_addrs_prepare(struct phys_addr *pages, u32 max_pages, + struct be_dma_mem *mem) +{ + int i, buf_pages; + u64 dma = (u64) mem->dma; + + buf_pages = min(PAGES_4K_SPANNED(mem->va, mem->size), max_pages); + for (i = 0; i < buf_pages; i++) { + pages[i].lo = cpu_to_le32(dma & 0xFFFFFFFF); + pages[i].hi = cpu_to_le32(upper_32_bits(dma)); + dma += PAGE_SIZE_4K; + } +} + +static u32 eq_delay_to_mult(u32 usec_delay) +{ +#define MAX_INTR_RATE 651042 + const u32 round = 10; + u32 multiplier; + + if (usec_delay == 0) + multiplier = 0; + else { + u32 interrupt_rate = 1000000 / usec_delay; + if (interrupt_rate == 0) + multiplier = 1023; + else { + multiplier = (MAX_INTR_RATE - interrupt_rate) * round; + multiplier /= interrupt_rate; + multiplier = (multiplier + round / 2) / round; + multiplier = min(multiplier, (u32) 1023); + } + } + return multiplier; +} + +struct be_mcc_wrb *wrb_from_mbox(struct be_dma_mem *mbox_mem) +{ + return &((struct be_mcc_mailbox *)(mbox_mem->va))->wrb; +} + +int beiscsi_cmd_eq_create(struct be_ctrl_info *ctrl, + struct be_queue_info *eq, int eq_delay) +{ + struct be_mcc_wrb *wrb = wrb_from_mbox(&ctrl->mbox_mem); + struct be_cmd_req_eq_create *req = embedded_payload(wrb); + struct be_cmd_resp_eq_create *resp = embedded_payload(wrb); + struct be_dma_mem *q_mem = &eq->dma_mem; + int status; + + spin_lock(&ctrl->mbox_lock); + memset(wrb, 0, sizeof(*wrb)); + + be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0); + + be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON, + OPCODE_COMMON_EQ_CREATE, sizeof(*req)); + + req->num_pages = cpu_to_le16(PAGES_4K_SPANNED(q_mem->va, q_mem->size)); + + AMAP_SET_BITS(struct amap_eq_context, func, req->context, + PCI_FUNC(ctrl->pdev->devfn)); + AMAP_SET_BITS(struct amap_eq_context, valid, req->context, 1); + AMAP_SET_BITS(struct amap_eq_context, size, req->context, 0); + AMAP_SET_BITS(struct amap_eq_context, count, req->context, + __ilog2_u32(eq->len / 256)); + AMAP_SET_BITS(struct amap_eq_context, delaymult, req->context, + eq_delay_to_mult(eq_delay)); + be_dws_cpu_to_le(req->context, sizeof(req->context)); + + be_cmd_page_addrs_prepare(req->pages, ARRAY_SIZE(req->pages), q_mem); + + status = be_mbox_notify(ctrl); + if (!status) { + eq->id = le16_to_cpu(resp->eq_id); + eq->created = true; + } + spin_unlock(&ctrl->mbox_lock); + return status; +} + +int be_cmd_fw_initialize(struct be_ctrl_info *ctrl) +{ + struct be_mcc_wrb *wrb = wrb_from_mbox(&ctrl->mbox_mem); + int status; + u8 *endian_check; + + spin_lock(&ctrl->mbox_lock); + memset(wrb, 0, sizeof(*wrb)); + + endian_check = (u8 *) wrb; + *endian_check++ = 0xFF; + *endian_check++ = 0x12; + *endian_check++ = 0x34; + *endian_check++ = 0xFF; + *endian_check++ = 0xFF; + *endian_check++ = 0x56; + *endian_check++ = 0x78; + *endian_check++ = 0xFF; + be_dws_cpu_to_le(wrb, sizeof(*wrb)); + + status = be_mbox_notify(ctrl); + if (status) + SE_DEBUG(DBG_LVL_1, "be_cmd_fw_initialize Failed \n"); + + spin_unlock(&ctrl->mbox_lock); + return status; +} + +int beiscsi_cmd_cq_create(struct be_ctrl_info *ctrl, + struct be_queue_info *cq, struct be_queue_info *eq, + bool sol_evts, bool no_delay, int coalesce_wm) +{ + struct be_mcc_wrb *wrb = wrb_from_mbox(&ctrl->mbox_mem); + struct be_cmd_req_cq_create *req = embedded_payload(wrb); + struct be_cmd_resp_cq_create *resp = embedded_payload(wrb); + struct be_dma_mem *q_mem = &cq->dma_mem; + void *ctxt = &req->context; + int status; + + spin_lock(&ctrl->mbox_lock); + memset(wrb, 0, sizeof(*wrb)); + + be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0); + + be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON, + OPCODE_COMMON_CQ_CREATE, sizeof(*req)); + + if (!q_mem->va) + SE_DEBUG(DBG_LVL_1, "uninitialized q_mem->va\n"); + + req->num_pages = cpu_to_le16(PAGES_4K_SPANNED(q_mem->va, q_mem->size)); + + AMAP_SET_BITS(struct amap_cq_context, coalescwm, ctxt, coalesce_wm); + AMAP_SET_BITS(struct amap_cq_context, nodelay, ctxt, no_delay); + AMAP_SET_BITS(struct amap_cq_context, count, ctxt, + __ilog2_u32(cq->len / 256)); + AMAP_SET_BITS(struct amap_cq_context, valid, ctxt, 1); + AMAP_SET_BITS(struct amap_cq_context, solevent, ctxt, sol_evts); + AMAP_SET_BITS(struct amap_cq_context, eventable, ctxt, 1); + AMAP_SET_BITS(struct amap_cq_context, eqid, ctxt, eq->id); + AMAP_SET_BITS(struct amap_cq_context, armed, ctxt, 1); + AMAP_SET_BITS(struct amap_cq_context, func, ctxt, + PCI_FUNC(ctrl->pdev->devfn)); + be_dws_cpu_to_le(ctxt, sizeof(req->context)); + + be_cmd_page_addrs_prepare(req->pages, ARRAY_SIZE(req->pages), q_mem); + + status = be_mbox_notify(ctrl); + if (!status) { + cq->id = le16_to_cpu(resp->cq_id); + cq->created = true; + } else + SE_DEBUG(DBG_LVL_1, "In be_cmd_cq_create, status=ox%08x \n", + status); + spin_unlock(&ctrl->mbox_lock); + + return status; +} + +static u32 be_encoded_q_len(int q_len) +{ + u32 len_encoded = fls(q_len); /* log2(len) + 1 */ + if (len_encoded == 16) + len_encoded = 0; + return len_encoded; +} +int beiscsi_cmd_q_destroy(struct be_ctrl_info *ctrl, struct be_queue_info *q, + int queue_type) +{ + struct be_mcc_wrb *wrb = wrb_from_mbox(&ctrl->mbox_mem); + struct be_cmd_req_q_destroy *req = embedded_payload(wrb); + u8 subsys = 0, opcode = 0; + int status; + + spin_lock(&ctrl->mbox_lock); + memset(wrb, 0, sizeof(*wrb)); + be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0); + + switch (queue_type) { + case QTYPE_EQ: + subsys = CMD_SUBSYSTEM_COMMON; + opcode = OPCODE_COMMON_EQ_DESTROY; + break; + case QTYPE_CQ: + subsys = CMD_SUBSYSTEM_COMMON; + opcode = OPCODE_COMMON_CQ_DESTROY; + break; + case QTYPE_WRBQ: + subsys = CMD_SUBSYSTEM_ISCSI; + opcode = OPCODE_COMMON_ISCSI_WRBQ_DESTROY; + break; + case QTYPE_DPDUQ: + subsys = CMD_SUBSYSTEM_ISCSI; + opcode = OPCODE_COMMON_ISCSI_DEFQ_DESTROY; + break; + case QTYPE_SGL: + subsys = CMD_SUBSYSTEM_ISCSI; + opcode = OPCODE_COMMON_ISCSI_CFG_REMOVE_SGL_PAGES; + break; + default: + spin_unlock(&ctrl->mbox_lock); + BUG(); + return -1; + } + be_cmd_hdr_prepare(&req->hdr, subsys, opcode, sizeof(*req)); + if (queue_type != QTYPE_SGL) + req->id = cpu_to_le16(q->id); + + status = be_mbox_notify(ctrl); + + spin_unlock(&ctrl->mbox_lock); + return status; +} + +int be_cmd_get_mac_addr(struct be_ctrl_info *ctrl, u8 *mac_addr) +{ + struct be_mcc_wrb *wrb = wrb_from_mbox(&ctrl->mbox_mem); + struct be_cmd_req_get_mac_addr *req = embedded_payload(wrb); + int status; + + spin_lock(&ctrl->mbox_lock); + memset(wrb, 0, sizeof(*wrb)); + be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0); + be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_ISCSI, + OPCODE_COMMON_ISCSI_NTWK_GET_NIC_CONFIG, + sizeof(*req)); + + status = be_mbox_notify(ctrl); + if (!status) { + struct be_cmd_resp_get_mac_addr *resp = embedded_payload(wrb); + + memcpy(mac_addr, resp->mac_address, ETH_ALEN); + } + + spin_unlock(&ctrl->mbox_lock); + return status; +} + +int be_cmd_create_default_pdu_queue(struct be_ctrl_info *ctrl, + struct be_queue_info *cq, + struct be_queue_info *dq, int length, + int entry_size) +{ + struct be_mcc_wrb *wrb = wrb_from_mbox(&ctrl->mbox_mem); + struct be_defq_create_req *req = embedded_payload(wrb); + struct be_dma_mem *q_mem = &dq->dma_mem; + void *ctxt = &req->context; + int status; + + spin_lock(&ctrl->mbox_lock); + memset(wrb, 0, sizeof(*wrb)); + + be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0); + + be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_ISCSI, + OPCODE_COMMON_ISCSI_DEFQ_CREATE, sizeof(*req)); + + req->num_pages = PAGES_4K_SPANNED(q_mem->va, q_mem->size); + AMAP_SET_BITS(struct amap_be_default_pdu_context, rx_pdid, ctxt, 0); + AMAP_SET_BITS(struct amap_be_default_pdu_context, rx_pdid_valid, ctxt, + 1); + AMAP_SET_BITS(struct amap_be_default_pdu_context, pci_func_id, ctxt, + PCI_FUNC(ctrl->pdev->devfn)); + AMAP_SET_BITS(struct amap_be_default_pdu_context, ring_size, ctxt, + be_encoded_q_len(length / sizeof(struct phys_addr))); + AMAP_SET_BITS(struct amap_be_default_pdu_context, default_buffer_size, + ctxt, entry_size); + AMAP_SET_BITS(struct amap_be_default_pdu_context, cq_id_recv, ctxt, + cq->id); + + be_dws_cpu_to_le(ctxt, sizeof(req->context)); + + be_cmd_page_addrs_prepare(req->pages, ARRAY_SIZE(req->pages), q_mem); + + status = be_mbox_notify(ctrl); + if (!status) { + struct be_defq_create_resp *resp = embedded_payload(wrb); + + dq->id = le16_to_cpu(resp->id); + dq->created = true; + } + spin_unlock(&ctrl->mbox_lock); + + return status; +} + +int be_cmd_wrbq_create(struct be_ctrl_info *ctrl, struct be_dma_mem *q_mem, + struct be_queue_info *wrbq) +{ + struct be_mcc_wrb *wrb = wrb_from_mbox(&ctrl->mbox_mem); + struct be_wrbq_create_req *req = embedded_payload(wrb); + struct be_wrbq_create_resp *resp = embedded_payload(wrb); + int status; + + spin_lock(&ctrl->mbox_lock); + memset(wrb, 0, sizeof(*wrb)); + + be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0); + + be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_ISCSI, + OPCODE_COMMON_ISCSI_WRBQ_CREATE, sizeof(*req)); + req->num_pages = PAGES_4K_SPANNED(q_mem->va, q_mem->size); + be_cmd_page_addrs_prepare(req->pages, ARRAY_SIZE(req->pages), q_mem); + + status = be_mbox_notify(ctrl); + if (!status) + wrbq->id = le16_to_cpu(resp->cid); + spin_unlock(&ctrl->mbox_lock); + return status; +} + +int be_cmd_iscsi_post_sgl_pages(struct be_ctrl_info *ctrl, + struct be_dma_mem *q_mem, + u32 page_offset, u32 num_pages) +{ + struct be_mcc_wrb *wrb = wrb_from_mbox(&ctrl->mbox_mem); + struct be_post_sgl_pages_req *req = embedded_payload(wrb); + int status; + unsigned int curr_pages; + u32 internal_page_offset = 0; + u32 temp_num_pages = num_pages; + + if (num_pages == 0xff) + num_pages = 1; + + spin_lock(&ctrl->mbox_lock); + do { + memset(wrb, 0, sizeof(*wrb)); + be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0); + be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_ISCSI, + OPCODE_COMMON_ISCSI_CFG_POST_SGL_PAGES, + sizeof(*req)); + curr_pages = BE_NUMBER_OF_FIELD(struct be_post_sgl_pages_req, + pages); + req->num_pages = min(num_pages, curr_pages); + req->page_offset = page_offset; + be_cmd_page_addrs_prepare(req->pages, req->num_pages, q_mem); + q_mem->dma = q_mem->dma + (req->num_pages * PAGE_SIZE); + internal_page_offset += req->num_pages; + page_offset += req->num_pages; + num_pages -= req->num_pages; + + if (temp_num_pages == 0xff) + req->num_pages = temp_num_pages; + + status = be_mbox_notify(ctrl); + if (status) { + SE_DEBUG(DBG_LVL_1, + "FW CMD to map iscsi frags failed.\n"); + goto error; + } + } while (num_pages > 0); +error: + spin_unlock(&ctrl->mbox_lock); + if (status != 0) + beiscsi_cmd_q_destroy(ctrl, NULL, QTYPE_SGL); + return status; +} diff --git a/drivers/scsi/be2iscsi/be_cmds.h b/drivers/scsi/be2iscsi/be_cmds.h new file mode 100644 index 000000000000..c20d686cbb43 --- /dev/null +++ b/drivers/scsi/be2iscsi/be_cmds.h @@ -0,0 +1,877 @@ +/** + * Copyright (C) 2005 - 2009 ServerEngines + * All rights reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. The full GNU General + * Public License is included in this distribution in the file called COPYING. + * + * Contact Information: + * linux-drivers@serverengines.com + * + * ServerEngines + * 209 N. Fair Oaks Ave + * Sunnyvale, CA 94085 + */ + +#ifndef BEISCSI_CMDS_H +#define BEISCSI_CMDS_H + +/** + * The driver sends configuration and managements command requests to the + * firmware in the BE. These requests are communicated to the processor + * using Work Request Blocks (WRBs) submitted to the MCC-WRB ring or via one + * WRB inside a MAILBOX. + * The commands are serviced by the ARM processor in the BladeEngine's MPU. + */ +struct be_sge { + u32 pa_lo; + u32 pa_hi; + u32 len; +}; + +#define MCC_WRB_SGE_CNT_SHIFT 3 /* bits 3 - 7 of dword 0 */ +#define MCC_WRB_SGE_CNT_MASK 0x1F /* bits 3 - 7 of dword 0 */ +struct be_mcc_wrb { + u32 embedded; /* dword 0 */ + u32 payload_length; /* dword 1 */ + u32 tag0; /* dword 2 */ + u32 tag1; /* dword 3 */ + u32 rsvd; /* dword 4 */ + union { + u8 embedded_payload[236]; /* used by embedded cmds */ + struct be_sge sgl[19]; /* used by non-embedded cmds */ + } payload; +}; + +#define CQE_FLAGS_VALID_MASK (1 << 31) +#define CQE_FLAGS_ASYNC_MASK (1 << 30) + +/* Completion Status */ +#define MCC_STATUS_SUCCESS 0x0 + +#define CQE_STATUS_COMPL_MASK 0xFFFF +#define CQE_STATUS_COMPL_SHIFT 0 /* bits 0 - 15 */ +#define CQE_STATUS_EXTD_MASK 0xFFFF +#define CQE_STATUS_EXTD_SHIFT 0 /* bits 0 - 15 */ + +struct be_mcc_compl { + u32 status; /* dword 0 */ + u32 tag0; /* dword 1 */ + u32 tag1; /* dword 2 */ + u32 flags; /* dword 3 */ +}; + +/********* Mailbox door bell *************/ +/** + * Used for driver communication with the FW. + * The software must write this register twice to post any command. First, + * it writes the register with hi=1 and the upper bits of the physical address + * for the MAILBOX structure. Software must poll the ready bit until this + * is acknowledged. Then, sotware writes the register with hi=0 with the lower + * bits in the address. It must poll the ready bit until the command is + * complete. Upon completion, the MAILBOX will contain a valid completion + * queue entry. + */ +#define MPU_MAILBOX_DB_OFFSET 0x160 +#define MPU_MAILBOX_DB_RDY_MASK 0x1 /* bit 0 */ +#define MPU_MAILBOX_DB_HI_MASK 0x2 /* bit 1 */ + +/********** MPU semphore ******************/ +#define MPU_EP_SEMAPHORE_OFFSET 0xac +#define EP_SEMAPHORE_POST_STAGE_MASK 0x0000FFFF +#define EP_SEMAPHORE_POST_ERR_MASK 0x1 +#define EP_SEMAPHORE_POST_ERR_SHIFT 31 + +/********** MCC door bell ************/ +#define DB_MCCQ_OFFSET 0x140 +#define DB_MCCQ_RING_ID_MASK 0x7FF /* bits 0 - 10 */ +/* Number of entries posted */ +#define DB_MCCQ_NUM_POSTED_SHIFT 16 /* bits 16 - 29 */ + +/* MPU semphore POST stage values */ +#define POST_STAGE_ARMFW_RDY 0xc000 /* FW is done with POST */ + +/** + * When the async bit of mcc_compl is set, the last 4 bytes of + * mcc_compl is interpreted as follows: + */ +#define ASYNC_TRAILER_EVENT_CODE_SHIFT 8 /* bits 8 - 15 */ +#define ASYNC_TRAILER_EVENT_CODE_MASK 0xFF +#define ASYNC_EVENT_CODE_LINK_STATE 0x1 +struct be_async_event_trailer { + u32 code; +}; + +enum { + ASYNC_EVENT_LINK_DOWN = 0x0, + ASYNC_EVENT_LINK_UP = 0x1 +}; + +/** + * When the event code of an async trailer is link-state, the mcc_compl + * must be interpreted as follows + */ +struct be_async_event_link_state { + u8 physical_port; + u8 port_link_status; + u8 port_duplex; + u8 port_speed; + u8 port_fault; + u8 rsvd0[7]; + struct be_async_event_trailer trailer; +} __packed; + +struct be_mcc_mailbox { + struct be_mcc_wrb wrb; + struct be_mcc_compl compl; +}; + +/* Type of subsystems supported by FW */ +#define CMD_SUBSYSTEM_COMMON 0x1 +#define CMD_SUBSYSTEM_ISCSI 0x2 +#define CMD_SUBSYSTEM_ETH 0x3 +#define CMD_SUBSYSTEM_ISCSI_INI 0x6 +#define CMD_COMMON_TCP_UPLOAD 0x1 + +/** + * List of common opcodes subsystem CMD_SUBSYSTEM_COMMON + * These opcodes are unique for each subsystem defined above + */ +#define OPCODE_COMMON_CQ_CREATE 12 +#define OPCODE_COMMON_EQ_CREATE 13 +#define OPCODE_COMMON_MCC_CREATE 21 +#define OPCODE_COMMON_GET_CNTL_ATTRIBUTES 32 +#define OPCODE_COMMON_GET_FW_VERSION 35 +#define OPCODE_COMMON_MODIFY_EQ_DELAY 41 +#define OPCODE_COMMON_FIRMWARE_CONFIG 42 +#define OPCODE_COMMON_MCC_DESTROY 53 +#define OPCODE_COMMON_CQ_DESTROY 54 +#define OPCODE_COMMON_EQ_DESTROY 55 +#define OPCODE_COMMON_QUERY_FIRMWARE_CONFIG 58 +#define OPCODE_COMMON_FUNCTION_RESET 61 + +/** + * LIST of opcodes that are common between Initiator and Target + * used by CMD_SUBSYSTEM_ISCSI + * These opcodes are unique for each subsystem defined above + */ +#define OPCODE_COMMON_ISCSI_CFG_POST_SGL_PAGES 2 +#define OPCODE_COMMON_ISCSI_CFG_REMOVE_SGL_PAGES 3 +#define OPCODE_COMMON_ISCSI_NTWK_GET_NIC_CONFIG 7 +#define OPCODE_COMMON_ISCSI_SET_FRAGNUM_BITS_FOR_SGL_CRA 61 +#define OPCODE_COMMON_ISCSI_DEFQ_CREATE 64 +#define OPCODE_COMMON_ISCSI_DEFQ_DESTROY 65 +#define OPCODE_COMMON_ISCSI_WRBQ_CREATE 66 +#define OPCODE_COMMON_ISCSI_WRBQ_DESTROY 67 + +struct be_cmd_req_hdr { + u8 opcode; /* dword 0 */ + u8 subsystem; /* dword 0 */ + u8 port_number; /* dword 0 */ + u8 domain; /* dword 0 */ + u32 timeout; /* dword 1 */ + u32 request_length; /* dword 2 */ + u32 rsvd; /* dword 3 */ +}; + +struct be_cmd_resp_hdr { + u32 info; /* dword 0 */ + u32 status; /* dword 1 */ + u32 response_length; /* dword 2 */ + u32 actual_resp_len; /* dword 3 */ +}; + +struct phys_addr { + u32 lo; + u32 hi; +}; + +/************************** + * BE Command definitions * + **************************/ + +/** + * Pseudo amap definition in which each bit of the actual structure is defined + * as a byte - used to calculate offset/shift/mask of each field + */ +struct amap_eq_context { + u8 cidx[13]; /* dword 0 */ + u8 rsvd0[3]; /* dword 0 */ + u8 epidx[13]; /* dword 0 */ + u8 valid; /* dword 0 */ + u8 rsvd1; /* dword 0 */ + u8 size; /* dword 0 */ + u8 pidx[13]; /* dword 1 */ + u8 rsvd2[3]; /* dword 1 */ + u8 pd[10]; /* dword 1 */ + u8 count[3]; /* dword 1 */ + u8 solevent; /* dword 1 */ + u8 stalled; /* dword 1 */ + u8 armed; /* dword 1 */ + u8 rsvd3[4]; /* dword 2 */ + u8 func[8]; /* dword 2 */ + u8 rsvd4; /* dword 2 */ + u8 delaymult[10]; /* dword 2 */ + u8 rsvd5[2]; /* dword 2 */ + u8 phase[2]; /* dword 2 */ + u8 nodelay; /* dword 2 */ + u8 rsvd6[4]; /* dword 2 */ + u8 rsvd7[32]; /* dword 3 */ +} __packed; + +struct be_cmd_req_eq_create { + struct be_cmd_req_hdr hdr; /* dw[4] */ + u16 num_pages; /* sword */ + u16 rsvd0; /* sword */ + u8 context[sizeof(struct amap_eq_context) / 8]; /* dw[4] */ + struct phys_addr pages[8]; +} __packed; + +struct be_cmd_resp_eq_create { + struct be_cmd_resp_hdr resp_hdr; + u16 eq_id; /* sword */ + u16 rsvd0; /* sword */ +} __packed; + +struct mac_addr { + u16 size_of_struct; + u8 addr[ETH_ALEN]; +} __packed; + +struct be_cmd_req_mac_query { + struct be_cmd_req_hdr hdr; + u8 type; + u8 permanent; + u16 if_id; +} __packed; + +struct be_cmd_resp_mac_query { + struct be_cmd_resp_hdr hdr; + struct mac_addr mac; +}; + +/******************** Create CQ ***************************/ +/** + * Pseudo amap definition in which each bit of the actual structure is defined + * as a byte - used to calculate offset/shift/mask of each field + */ +struct amap_cq_context { + u8 cidx[11]; /* dword 0 */ + u8 rsvd0; /* dword 0 */ + u8 coalescwm[2]; /* dword 0 */ + u8 nodelay; /* dword 0 */ + u8 epidx[11]; /* dword 0 */ + u8 rsvd1; /* dword 0 */ + u8 count[2]; /* dword 0 */ + u8 valid; /* dword 0 */ + u8 solevent; /* dword 0 */ + u8 eventable; /* dword 0 */ + u8 pidx[11]; /* dword 1 */ + u8 rsvd2; /* dword 1 */ + u8 pd[10]; /* dword 1 */ + u8 eqid[8]; /* dword 1 */ + u8 stalled; /* dword 1 */ + u8 armed; /* dword 1 */ + u8 rsvd3[4]; /* dword 2 */ + u8 func[8]; /* dword 2 */ + u8 rsvd4[20]; /* dword 2 */ + u8 rsvd5[32]; /* dword 3 */ +} __packed; + +struct be_cmd_req_cq_create { + struct be_cmd_req_hdr hdr; + u16 num_pages; + u16 rsvd0; + u8 context[sizeof(struct amap_cq_context) / 8]; + struct phys_addr pages[4]; +} __packed; + +struct be_cmd_resp_cq_create { + struct be_cmd_resp_hdr hdr; + u16 cq_id; + u16 rsvd0; +} __packed; + +/******************** Create MCCQ ***************************/ +/** + * Pseudo amap definition in which each bit of the actual structure is defined + * as a byte - used to calculate offset/shift/mask of each field + */ +struct amap_mcc_context { + u8 con_index[14]; + u8 rsvd0[2]; + u8 ring_size[4]; + u8 fetch_wrb; + u8 fetch_r2t; + u8 cq_id[10]; + u8 prod_index[14]; + u8 fid[8]; + u8 pdid[9]; + u8 valid; + u8 rsvd1[32]; + u8 rsvd2[32]; +} __packed; + +struct be_cmd_req_mcc_create { + struct be_cmd_req_hdr hdr; + u16 num_pages; + u16 rsvd0; + u8 context[sizeof(struct amap_mcc_context) / 8]; + struct phys_addr pages[8]; +} __packed; + +struct be_cmd_resp_mcc_create { + struct be_cmd_resp_hdr hdr; + u16 id; + u16 rsvd0; +} __packed; + +/******************** Q Destroy ***************************/ +/* Type of Queue to be destroyed */ +enum { + QTYPE_EQ = 1, + QTYPE_CQ, + QTYPE_MCCQ, + QTYPE_WRBQ, + QTYPE_DPDUQ, + QTYPE_SGL +}; + +struct be_cmd_req_q_destroy { + struct be_cmd_req_hdr hdr; + u16 id; + u16 bypass_flush; /* valid only for rx q destroy */ +} __packed; + +struct macaddr { + u8 byte[ETH_ALEN]; +}; + +struct be_cmd_req_mcast_mac_config { + struct be_cmd_req_hdr hdr; + u16 num_mac; + u8 promiscuous; + u8 interface_id; + struct macaddr mac[32]; +} __packed; + +static inline void *embedded_payload(struct be_mcc_wrb *wrb) +{ + return wrb->payload.embedded_payload; +} + +static inline struct be_sge *nonembedded_sgl(struct be_mcc_wrb *wrb) +{ + return &wrb->payload.sgl[0]; +} + +/******************** Modify EQ Delay *******************/ +struct be_cmd_req_modify_eq_delay { + struct be_cmd_req_hdr hdr; + u32 num_eq; + struct { + u32 eq_id; + u32 phase; + u32 delay_multiplier; + } delay[8]; +} __packed; + +/******************** Get MAC ADDR *******************/ + +#define ETH_ALEN 6 + + +struct be_cmd_req_get_mac_addr { + struct be_cmd_req_hdr hdr; + u32 nic_port_count; + u32 speed; + u32 max_speed; + u32 link_state; + u32 max_frame_size; + u16 size_of_structure; + u8 mac_address[ETH_ALEN]; + u32 rsvd[23]; +}; + +struct be_cmd_resp_get_mac_addr { + struct be_cmd_resp_hdr hdr; + u32 nic_port_count; + u32 speed; + u32 max_speed; + u32 link_state; + u32 max_frame_size; + u16 size_of_structure; + u8 mac_address[6]; + u32 rsvd[23]; +}; + +int beiscsi_cmd_eq_create(struct be_ctrl_info *ctrl, + struct be_queue_info *eq, int eq_delay); + +int beiscsi_cmd_cq_create(struct be_ctrl_info *ctrl, + struct be_queue_info *cq, struct be_queue_info *eq, + bool sol_evts, bool no_delay, + int num_cqe_dma_coalesce); + +int beiscsi_cmd_q_destroy(struct be_ctrl_info *ctrl, struct be_queue_info *q, + int type); +int be_poll_mcc(struct be_ctrl_info *ctrl); +unsigned char mgmt_check_supported_fw(struct be_ctrl_info *ctrl); +int be_cmd_get_mac_addr(struct be_ctrl_info *ctrl, u8 *mac_addr); + +/*ISCSI Functuions */ +int be_cmd_fw_initialize(struct be_ctrl_info *ctrl); + +struct be_mcc_wrb *wrb_from_mbox(struct be_dma_mem *mbox_mem); + +int be_mbox_notify(struct be_ctrl_info *ctrl); + +int be_cmd_create_default_pdu_queue(struct be_ctrl_info *ctrl, + struct be_queue_info *cq, + struct be_queue_info *dq, int length, + int entry_size); + +int be_cmd_iscsi_post_sgl_pages(struct be_ctrl_info *ctrl, + struct be_dma_mem *q_mem, u32 page_offset, + u32 num_pages); + +int be_cmd_wrbq_create(struct be_ctrl_info *ctrl, struct be_dma_mem *q_mem, + struct be_queue_info *wrbq); + +struct be_default_pdu_context { + u32 dw[4]; +} __packed; + +struct amap_be_default_pdu_context { + u8 dbuf_cindex[13]; /* dword 0 */ + u8 rsvd0[3]; /* dword 0 */ + u8 ring_size[4]; /* dword 0 */ + u8 ring_state[4]; /* dword 0 */ + u8 rsvd1[8]; /* dword 0 */ + u8 dbuf_pindex[13]; /* dword 1 */ + u8 rsvd2; /* dword 1 */ + u8 pci_func_id[8]; /* dword 1 */ + u8 rx_pdid[9]; /* dword 1 */ + u8 rx_pdid_valid; /* dword 1 */ + u8 default_buffer_size[16]; /* dword 2 */ + u8 cq_id_recv[10]; /* dword 2 */ + u8 rx_pdid_not_valid; /* dword 2 */ + u8 rsvd3[5]; /* dword 2 */ + u8 rsvd4[32]; /* dword 3 */ +} __packed; + +struct be_defq_create_req { + struct be_cmd_req_hdr hdr; + u16 num_pages; + u8 ulp_num; + u8 rsvd0; + struct be_default_pdu_context context; + struct phys_addr pages[8]; +} __packed; + +struct be_defq_create_resp { + struct be_cmd_req_hdr hdr; + u16 id; + u16 rsvd0; +} __packed; + +struct be_post_sgl_pages_req { + struct be_cmd_req_hdr hdr; + u16 num_pages; + u16 page_offset; + u32 rsvd0; + struct phys_addr pages[26]; + u32 rsvd1; +} __packed; + +struct be_wrbq_create_req { + struct be_cmd_req_hdr hdr; + u16 num_pages; + u8 ulp_num; + u8 rsvd0; + struct phys_addr pages[8]; +} __packed; + +struct be_wrbq_create_resp { + struct be_cmd_resp_hdr resp_hdr; + u16 cid; + u16 rsvd0; +} __packed; + +#define SOL_CID_MASK 0x0000FFC0 +#define SOL_CODE_MASK 0x0000003F +#define SOL_WRB_INDEX_MASK 0x00FF0000 +#define SOL_CMD_WND_MASK 0xFF000000 +#define SOL_RES_CNT_MASK 0x7FFFFFFF +#define SOL_EXP_CMD_SN_MASK 0xFFFFFFFF +#define SOL_HW_STS_MASK 0x000000FF +#define SOL_STS_MASK 0x0000FF00 +#define SOL_RESP_MASK 0x00FF0000 +#define SOL_FLAGS_MASK 0x7F000000 +#define SOL_S_MASK 0x80000000 + +struct sol_cqe { + u32 dw[4]; +}; + +struct amap_sol_cqe { + u8 hw_sts[8]; /* dword 0 */ + u8 i_sts[8]; /* dword 0 */ + u8 i_resp[8]; /* dword 0 */ + u8 i_flags[7]; /* dword 0 */ + u8 s; /* dword 0 */ + u8 i_exp_cmd_sn[32]; /* dword 1 */ + u8 code[6]; /* dword 2 */ + u8 cid[10]; /* dword 2 */ + u8 wrb_index[8]; /* dword 2 */ + u8 i_cmd_wnd[8]; /* dword 2 */ + u8 i_res_cnt[31]; /* dword 3 */ + u8 valid; /* dword 3 */ +} __packed; + + +/** + * Post WRB Queue Doorbell Register used by the host Storage + * stack to notify the + * controller of a posted Work Request Block + */ +#define DB_WRB_POST_CID_MASK 0x3FF /* bits 0 - 9 */ +#define DB_DEF_PDU_WRB_INDEX_MASK 0xFF /* bits 0 - 9 */ + +#define DB_DEF_PDU_WRB_INDEX_SHIFT 16 +#define DB_DEF_PDU_NUM_POSTED_SHIFT 24 + +struct fragnum_bits_for_sgl_cra_in { + struct be_cmd_req_hdr hdr; + u32 num_bits; +} __packed; + +struct iscsi_cleanup_req { + struct be_cmd_req_hdr hdr; + u16 chute; + u8 hdr_ring_id; + u8 data_ring_id; + +} __packed; + +struct eq_delay { + u32 eq_id; + u32 phase; + u32 delay_multiplier; +} __packed; + +struct be_eq_delay_params_in { + struct be_cmd_req_hdr hdr; + u32 num_eq; + struct eq_delay delay[8]; +} __packed; + +struct ip_address_format { + u16 size_of_structure; + u8 reserved; + u8 ip_type; + u8 ip_address[16]; + u32 rsvd0; +} __packed; + +struct tcp_connect_and_offload_in { + struct be_cmd_req_hdr hdr; + struct ip_address_format ip_address; + u16 tcp_port; + u16 cid; + u16 cq_id; + u16 defq_id; + struct phys_addr dataout_template_pa; + u16 hdr_ring_id; + u16 data_ring_id; + u8 do_offload; + u8 rsvd0[3]; +} __packed; + +struct tcp_connect_and_offload_out { + struct be_cmd_resp_hdr hdr; + u32 connection_handle; + u16 cid; + u16 rsvd0; + +} __packed; + +struct be_mcc_wrb_context { + struct MCC_WRB *wrb; + int *users_final_status; +} __packed; + +#define DB_DEF_PDU_RING_ID_MASK 0x3FF /* bits 0 - 9 */ +#define DB_DEF_PDU_CQPROC_MASK 0x3FFF /* bits 0 - 9 */ +#define DB_DEF_PDU_REARM_SHIFT 14 +#define DB_DEF_PDU_EVENT_SHIFT 15 +#define DB_DEF_PDU_CQPROC_SHIFT 16 + +struct dmsg_cqe { + u32 dw[4]; +} __packed; + +struct tcp_upload_params_in { + struct be_cmd_req_hdr hdr; + u16 id; + u16 upload_type; + u32 reset_seq; +} __packed; + +struct tcp_upload_params_out { + u32 dw[32]; +} __packed; + +union tcp_upload_params { + struct tcp_upload_params_in request; + struct tcp_upload_params_out response; +} __packed; + +struct be_ulp_fw_cfg { + u32 ulp_mode; + u32 etx_base; + u32 etx_count; + u32 sq_base; + u32 sq_count; + u32 rq_base; + u32 rq_count; + u32 dq_base; + u32 dq_count; + u32 lro_base; + u32 lro_count; + u32 icd_base; + u32 icd_count; +}; + +struct be_fw_cfg { + struct be_cmd_req_hdr hdr; + u32 be_config_number; + u32 asic_revision; + u32 phys_port; + u32 function_mode; + struct be_ulp_fw_cfg ulp[2]; + u32 function_caps; +} __packed; + +#define CMD_ISCSI_COMMAND_INVALIDATE 1 +#define ISCSI_OPCODE_SCSI_DATA_OUT 5 +#define OPCODE_COMMON_ISCSI_TCP_CONNECT_AND_OFFLOAD 70 +#define OPCODE_ISCSI_INI_DRIVER_OFFLOAD_SESSION 41 +#define OPCODE_COMMON_MODIFY_EQ_DELAY 41 +#define OPCODE_COMMON_ISCSI_CLEANUP 59 +#define OPCODE_COMMON_TCP_UPLOAD 56 +#define OPCODE_COMMON_ISCSI_ERROR_RECOVERY_INVALIDATE_COMMANDS 1 +/* --- CMD_ISCSI_INVALIDATE_CONNECTION_TYPE --- */ +#define CMD_ISCSI_CONNECTION_INVALIDATE 1 +#define CMD_ISCSI_CONNECTION_ISSUE_TCP_RST 2 +#define OPCODE_ISCSI_INI_DRIVER_INVALIDATE_CONNECTION 42 + +#define INI_WR_CMD 1 /* Initiator write command */ +#define INI_TMF_CMD 2 /* Initiator TMF command */ +#define INI_NOPOUT_CMD 3 /* Initiator; Send a NOP-OUT */ +#define INI_RD_CMD 5 /* Initiator requesting to send + * a read command + */ +#define TGT_CTX_UPDT_CMD 7 /* Target context update */ +#define TGT_STS_CMD 8 /* Target R2T and other BHS + * where only the status number + * need to be updated + */ +#define TGT_DATAIN_CMD 9 /* Target Data-Ins in response + * to read command + */ +#define TGT_SOS_PDU 10 /* Target:standalone status + * response + */ +#define TGT_DM_CMD 11 /* Indicates that the bhs + * preparedby + * driver should not be touched + */ +/* --- CMD_CHUTE_TYPE --- */ +#define CMD_CONNECTION_CHUTE_0 1 +#define CMD_CONNECTION_CHUTE_1 2 +#define CMD_CONNECTION_CHUTE_2 3 + +#define EQ_MAJOR_CODE_COMPLETION 0 + +#define CMD_ISCSI_SESSION_DEL_CFG_FROM_FLASH 0 +#define CMD_ISCSI_SESSION_SAVE_CFG_ON_FLASH 1 + +/* --- CONNECTION_UPLOAD_PARAMS --- */ +/* These parameters are used to define the type of upload desired. */ +#define CONNECTION_UPLOAD_GRACEFUL 1 /* Graceful upload */ +#define CONNECTION_UPLOAD_ABORT_RESET 2 /* Abortive upload with + * reset + */ +#define CONNECTION_UPLOAD_ABORT 3 /* Abortive upload without + * reset + */ +#define CONNECTION_UPLOAD_ABORT_WITH_SEQ 4 /* Abortive upload with reset, + * sequence number by driver */ + +/* Returns byte size of given field with a structure. */ + +/* Returns the number of items in the field array. */ +#define BE_NUMBER_OF_FIELD(_type_, _field_) \ + (FIELD_SIZEOF(_type_, _field_)/sizeof((((_type_ *)0)->_field_[0])))\ + +/** + * Different types of iSCSI completions to host driver for both initiator + * and taget mode + * of operation. + */ +#define SOL_CMD_COMPLETE 1 /* Solicited command completed + * normally + */ +#define SOL_CMD_KILLED_DATA_DIGEST_ERR 2 /* Solicited command got + * invalidated internally due + * to Data Digest error + */ +#define CXN_KILLED_PDU_SIZE_EXCEEDS_DSL 3 /* Connection got invalidated + * internally + * due to a recieved PDU + * size > DSL + */ +#define CXN_KILLED_BURST_LEN_MISMATCH 4 /* Connection got invalidated + * internally due ti received + * PDU sequence size > + * FBL/MBL. + */ +#define CXN_KILLED_AHS_RCVD 5 /* Connection got invalidated + * internally due to a recieved + * PDU Hdr that has + * AHS */ +#define CXN_KILLED_HDR_DIGEST_ERR 6 /* Connection got invalidated + * internally due to Hdr Digest + * error + */ +#define CXN_KILLED_UNKNOWN_HDR 7 /* Connection got invalidated + * internally + * due to a bad opcode in the + * pdu hdr + */ +#define CXN_KILLED_STALE_ITT_TTT_RCVD 8 /* Connection got invalidated + * internally due to a recieved + * ITT/TTT that does not belong + * to this Connection + */ +#define CXN_KILLED_INVALID_ITT_TTT_RCVD 9 /* Connection got invalidated + * internally due to recieved + * ITT/TTT value > Max + * Supported ITTs/TTTs + */ +#define CXN_KILLED_RST_RCVD 10 /* Connection got invalidated + * internally due to an + * incoming TCP RST + */ +#define CXN_KILLED_TIMED_OUT 11 /* Connection got invalidated + * internally due to timeout on + * tcp segment 12 retransmit + * attempts failed + */ +#define CXN_KILLED_RST_SENT 12 /* Connection got invalidated + * internally due to TCP RST + * sent by the Tx side + */ +#define CXN_KILLED_FIN_RCVD 13 /* Connection got invalidated + * internally due to an + * incoming TCP FIN. + */ +#define CXN_KILLED_BAD_UNSOL_PDU_RCVD 14 /* Connection got invalidated + * internally due to bad + * unsolicited PDU Unsolicited + * PDUs are PDUs with + * ITT=0xffffffff + */ +#define CXN_KILLED_BAD_WRB_INDEX_ERROR 15 /* Connection got invalidated + * internally due to bad WRB + * index. + */ +#define CXN_KILLED_OVER_RUN_RESIDUAL 16 /* Command got invalidated + * internally due to recived + * command has residual + * over run bytes. + */ +#define CXN_KILLED_UNDER_RUN_RESIDUAL 17 /* Command got invalidated + * internally due to recived + * command has residual under + * run bytes. + */ +#define CMD_KILLED_INVALID_STATSN_RCVD 18 /* Command got invalidated + * internally due to a recieved + * PDU has an invalid StatusSN + */ +#define CMD_KILLED_INVALID_R2T_RCVD 19 /* Command got invalidated + * internally due to a recieved + * an R2T with some invalid + * fields in it + */ +#define CMD_CXN_KILLED_LUN_INVALID 20 /* Command got invalidated + * internally due to received + * PDU has an invalid LUN. + */ +#define CMD_CXN_KILLED_ICD_INVALID 21 /* Command got invalidated + * internally due to the + * corresponding ICD not in a + * valid state + */ +#define CMD_CXN_KILLED_ITT_INVALID 22 /* Command got invalidated due + * to received PDU has an + * invalid ITT. + */ +#define CMD_CXN_KILLED_SEQ_OUTOFORDER 23 /* Command got invalidated due + * to received sequence buffer + * offset is out of order. + */ +#define CMD_CXN_KILLED_INVALID_DATASN_RCVD 24 /* Command got invalidated + * internally due to a + * recieved PDU has an invalid + * DataSN + */ +#define CXN_INVALIDATE_NOTIFY 25 /* Connection invalidation + * completion notify. + */ +#define CXN_INVALIDATE_INDEX_NOTIFY 26 /* Connection invalidation + * completion + * with data PDU index. + */ +#define CMD_INVALIDATED_NOTIFY 27 /* Command invalidation + * completionnotifify. + */ +#define UNSOL_HDR_NOTIFY 28 /* Unsolicited header notify.*/ +#define UNSOL_DATA_NOTIFY 29 /* Unsolicited data notify.*/ +#define UNSOL_DATA_DIGEST_ERROR_NOTIFY 30 /* Unsolicited data digest + * error notify. + */ +#define DRIVERMSG_NOTIFY 31 /* TCP acknowledge based + * notification. + */ +#define CXN_KILLED_CMND_DATA_NOT_ON_SAME_CONN 32 /* Connection got invalidated + * internally due to command + * and data are not on same + * connection. + */ +#define SOL_CMD_KILLED_DIF_ERR 33 /* Solicited command got + * invalidated internally due + * to DIF error + */ +#define CXN_KILLED_SYN_RCVD 34 /* Connection got invalidated + * internally due to incoming + * TCP SYN + */ +#define CXN_KILLED_IMM_DATA_RCVD 35 /* Connection got invalidated + * internally due to an + * incoming Unsolicited PDU + * that has immediate data on + * the cxn + */ + +void be_wrb_hdr_prepare(struct be_mcc_wrb *wrb, int payload_len, + bool embedded, u8 sge_cnt); + +void be_cmd_hdr_prepare(struct be_cmd_req_hdr *req_hdr, + u8 subsystem, u8 opcode, int cmd_len); + +#endif /* !BEISCSI_CMDS_H */ diff --git a/drivers/scsi/be2iscsi/be_iscsi.c b/drivers/scsi/be2iscsi/be_iscsi.c new file mode 100644 index 000000000000..2fd25442cfaf --- /dev/null +++ b/drivers/scsi/be2iscsi/be_iscsi.c @@ -0,0 +1,638 @@ +/** + * Copyright (C) 2005 - 2009 ServerEngines + * All rights reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. The full GNU General + * Public License is included in this distribution in the file called COPYING. + * + * Written by: Jayamohan Kallickal (jayamohank@serverengines.com) + * + * Contact Information: + * linux-drivers@serverengines.com + * + * ServerEngines + * 209 N. Fair Oaks Ave + * Sunnyvale, CA 94085 + * + */ + +#include <scsi/libiscsi.h> +#include <scsi/scsi_transport_iscsi.h> +#include <scsi/scsi_transport.h> +#include <scsi/scsi_cmnd.h> +#include <scsi/scsi_device.h> +#include <scsi/scsi_host.h> +#include <scsi/scsi.h> + +#include "be_iscsi.h" + +extern struct iscsi_transport beiscsi_iscsi_transport; + +/** + * beiscsi_session_create - creates a new iscsi session + * @cmds_max: max commands supported + * @qdepth: max queue depth supported + * @initial_cmdsn: initial iscsi CMDSN + */ +struct iscsi_cls_session *beiscsi_session_create(struct iscsi_endpoint *ep, + u16 cmds_max, + u16 qdepth, + u32 initial_cmdsn) +{ + struct Scsi_Host *shost; + struct beiscsi_endpoint *beiscsi_ep; + struct iscsi_cls_session *cls_session; + struct beiscsi_hba *phba; + struct iscsi_session *sess; + struct beiscsi_session *beiscsi_sess; + struct beiscsi_io_task *io_task; + + SE_DEBUG(DBG_LVL_8, "In beiscsi_session_create\n"); + + if (!ep) { + SE_DEBUG(DBG_LVL_1, "beiscsi_session_create: invalid ep \n"); + return NULL; + } + beiscsi_ep = ep->dd_data; + phba = beiscsi_ep->phba; + shost = phba->shost; + if (cmds_max > beiscsi_ep->phba->params.wrbs_per_cxn) { + shost_printk(KERN_ERR, shost, "Cannot handle %d cmds." + "Max cmds per session supported is %d. Using %d. " + "\n", cmds_max, + beiscsi_ep->phba->params.wrbs_per_cxn, + beiscsi_ep->phba->params.wrbs_per_cxn); + cmds_max = beiscsi_ep->phba->params.wrbs_per_cxn; + } + + cls_session = iscsi_session_setup(&beiscsi_iscsi_transport, + shost, cmds_max, + sizeof(*beiscsi_sess), + sizeof(*io_task), + initial_cmdsn, ISCSI_MAX_TARGET); + if (!cls_session) + return NULL; + sess = cls_session->dd_data; + beiscsi_sess = sess->dd_data; + beiscsi_sess->bhs_pool = pci_pool_create("beiscsi_bhs_pool", + phba->pcidev, + sizeof(struct be_cmd_bhs), + 64, 0); + if (!beiscsi_sess->bhs_pool) + goto destroy_sess; + + return cls_session; +destroy_sess: + iscsi_session_teardown(cls_session); + return NULL; +} + +/** + * beiscsi_session_destroy - destroys iscsi session + * @cls_session: pointer to iscsi cls session + * + * Destroys iSCSI session instance and releases + * resources allocated for it. + */ +void beiscsi_session_destroy(struct iscsi_cls_session *cls_session) +{ + struct iscsi_session *sess = cls_session->dd_data; + struct beiscsi_session *beiscsi_sess = sess->dd_data; + + pci_pool_destroy(beiscsi_sess->bhs_pool); + iscsi_session_teardown(cls_session); +} + +/** + * beiscsi_conn_create - create an instance of iscsi connection + * @cls_session: ptr to iscsi_cls_session + * @cid: iscsi cid + */ +struct iscsi_cls_conn * +beiscsi_conn_create(struct iscsi_cls_session *cls_session, u32 cid) +{ + struct beiscsi_hba *phba; + struct Scsi_Host *shost; + struct iscsi_cls_conn *cls_conn; + struct beiscsi_conn *beiscsi_conn; + struct iscsi_conn *conn; + struct iscsi_session *sess; + struct beiscsi_session *beiscsi_sess; + + SE_DEBUG(DBG_LVL_8, "In beiscsi_conn_create ,cid" + "from iscsi layer=%d\n", cid); + shost = iscsi_session_to_shost(cls_session); + phba = iscsi_host_priv(shost); + + cls_conn = iscsi_conn_setup(cls_session, sizeof(*beiscsi_conn), cid); + if (!cls_conn) + return NULL; + + conn = cls_conn->dd_data; + beiscsi_conn = conn->dd_data; + beiscsi_conn->ep = NULL; + beiscsi_conn->phba = phba; + beiscsi_conn->conn = conn; + sess = cls_session->dd_data; + beiscsi_sess = sess->dd_data; + beiscsi_conn->beiscsi_sess = beiscsi_sess; + return cls_conn; +} + +/** + * beiscsi_bindconn_cid - Bind the beiscsi_conn with phba connection table + * @beiscsi_conn: The pointer to beiscsi_conn structure + * @phba: The phba instance + * @cid: The cid to free + */ +static int beiscsi_bindconn_cid(struct beiscsi_hba *phba, + struct beiscsi_conn *beiscsi_conn, + unsigned int cid) +{ + if (phba->conn_table[cid]) { + SE_DEBUG(DBG_LVL_1, + "Connection table already occupied. Detected clash\n"); + return -EINVAL; + } else { + SE_DEBUG(DBG_LVL_8, "phba->conn_table[%d]=%p(beiscsi_conn) \n", + cid, beiscsi_conn); + phba->conn_table[cid] = beiscsi_conn; + } + return 0; +} + +/** + * beiscsi_conn_bind - Binds iscsi session/connection with TCP connection + * @cls_session: pointer to iscsi cls session + * @cls_conn: pointer to iscsi cls conn + * @transport_fd: EP handle(64 bit) + * + * This function binds the TCP Conn with iSCSI Connection and Session. + */ +int beiscsi_conn_bind(struct iscsi_cls_session *cls_session, + struct iscsi_cls_conn *cls_conn, + u64 transport_fd, int is_leading) +{ + struct iscsi_conn *conn = cls_conn->dd_data; + struct beiscsi_conn *beiscsi_conn = conn->dd_data; + struct Scsi_Host *shost = + (struct Scsi_Host *)iscsi_session_to_shost(cls_session); + struct beiscsi_hba *phba = (struct beiscsi_hba *)iscsi_host_priv(shost); + struct beiscsi_endpoint *beiscsi_ep; + struct iscsi_endpoint *ep; + + SE_DEBUG(DBG_LVL_8, "In beiscsi_conn_bind\n"); + ep = iscsi_lookup_endpoint(transport_fd); + if (!ep) + return -EINVAL; + + beiscsi_ep = ep->dd_data; + + if (iscsi_conn_bind(cls_session, cls_conn, is_leading)) + return -EINVAL; + + if (beiscsi_ep->phba != phba) { + SE_DEBUG(DBG_LVL_8, + "beiscsi_ep->hba=%p not equal to phba=%p \n", + beiscsi_ep->phba, phba); + return -EEXIST; + } + + beiscsi_conn->beiscsi_conn_cid = beiscsi_ep->ep_cid; + beiscsi_conn->ep = beiscsi_ep; + beiscsi_ep->conn = beiscsi_conn; + SE_DEBUG(DBG_LVL_8, "beiscsi_conn=%p conn=%p ep_cid=%d \n", + beiscsi_conn, conn, beiscsi_ep->ep_cid); + return beiscsi_bindconn_cid(phba, beiscsi_conn, beiscsi_ep->ep_cid); +} + +/** + * beiscsi_conn_get_param - get the iscsi parameter + * @cls_conn: pointer to iscsi cls conn + * @param: parameter type identifier + * @buf: buffer pointer + * + * returns iscsi parameter + */ +int beiscsi_conn_get_param(struct iscsi_cls_conn *cls_conn, + enum iscsi_param param, char *buf) +{ + struct beiscsi_endpoint *beiscsi_ep; + struct iscsi_conn *conn = cls_conn->dd_data; + struct beiscsi_conn *beiscsi_conn = conn->dd_data; + int len = 0; + + beiscsi_ep = beiscsi_conn->ep; + if (!beiscsi_ep) { + SE_DEBUG(DBG_LVL_1, + "In beiscsi_conn_get_param , no beiscsi_ep\n"); + return -1; + } + + switch (param) { + case ISCSI_PARAM_CONN_PORT: + len = sprintf(buf, "%hu\n", beiscsi_ep->dst_tcpport); + break; + case ISCSI_PARAM_CONN_ADDRESS: + if (beiscsi_ep->ip_type == BE2_IPV4) + len = sprintf(buf, "%pI4\n", &beiscsi_ep->dst_addr); + else + len = sprintf(buf, "%pI6\n", &beiscsi_ep->dst6_addr); + break; + default: + return iscsi_conn_get_param(cls_conn, param, buf); + } + return len; +} + +int beiscsi_set_param(struct iscsi_cls_conn *cls_conn, + enum iscsi_param param, char *buf, int buflen) +{ + struct iscsi_conn *conn = cls_conn->dd_data; + struct iscsi_session *session = conn->session; + int ret; + + ret = iscsi_set_param(cls_conn, param, buf, buflen); + if (ret) + return ret; + /* + * If userspace tried to set the value to higher than we can + * support override here. + */ + switch (param) { + case ISCSI_PARAM_FIRST_BURST: + if (session->first_burst > 8192) + session->first_burst = 8192; + break; + case ISCSI_PARAM_MAX_RECV_DLENGTH: + if (conn->max_recv_dlength > 65536) + conn->max_recv_dlength = 65536; + break; + case ISCSI_PARAM_MAX_BURST: + if (session->first_burst > 262144) + session->first_burst = 262144; + break; + default: + return 0; + } + + return 0; +} + +/** + * beiscsi_get_host_param - get the iscsi parameter + * @shost: pointer to scsi_host structure + * @param: parameter type identifier + * @buf: buffer pointer + * + * returns host parameter + */ +int beiscsi_get_host_param(struct Scsi_Host *shost, + enum iscsi_host_param param, char *buf) +{ + struct beiscsi_hba *phba = (struct beiscsi_hba *)iscsi_host_priv(shost); + int len = 0; + + switch (param) { + case ISCSI_HOST_PARAM_HWADDRESS: + be_cmd_get_mac_addr(&phba->ctrl, phba->mac_address); + len = sysfs_format_mac(buf, phba->mac_address, ETH_ALEN); + break; + default: + return iscsi_host_get_param(shost, param, buf); + } + return len; +} + +/** + * beiscsi_conn_get_stats - get the iscsi stats + * @cls_conn: pointer to iscsi cls conn + * @stats: pointer to iscsi_stats structure + * + * returns iscsi stats + */ +void beiscsi_conn_get_stats(struct iscsi_cls_conn *cls_conn, + struct iscsi_stats *stats) +{ + struct iscsi_conn *conn = cls_conn->dd_data; + + SE_DEBUG(DBG_LVL_8, "In beiscsi_conn_get_stats\n"); + stats->txdata_octets = conn->txdata_octets; + stats->rxdata_octets = conn->rxdata_octets; + stats->dataout_pdus = conn->dataout_pdus_cnt; + stats->scsirsp_pdus = conn->scsirsp_pdus_cnt; + stats->scsicmd_pdus = conn->scsicmd_pdus_cnt; + stats->datain_pdus = conn->datain_pdus_cnt; + stats->tmfrsp_pdus = conn->tmfrsp_pdus_cnt; + stats->tmfcmd_pdus = conn->tmfcmd_pdus_cnt; + stats->r2t_pdus = conn->r2t_pdus_cnt; + stats->digest_err = 0; + stats->timeout_err = 0; + stats->custom_length = 0; + strcpy(stats->custom[0].desc, "eh_abort_cnt"); + stats->custom[0].value = conn->eh_abort_cnt; +} + +/** + * beiscsi_set_params_for_offld - get the parameters for offload + * @beiscsi_conn: pointer to beiscsi_conn + * @params: pointer to offload_params structure + */ +static void beiscsi_set_params_for_offld(struct beiscsi_conn *beiscsi_conn, + struct beiscsi_offload_params *params) +{ + struct iscsi_conn *conn = beiscsi_conn->conn; + struct iscsi_session *session = conn->session; + + AMAP_SET_BITS(struct amap_beiscsi_offload_params, max_burst_length, + params, session->max_burst); + AMAP_SET_BITS(struct amap_beiscsi_offload_params, + max_send_data_segment_length, params, + conn->max_xmit_dlength); + AMAP_SET_BITS(struct amap_beiscsi_offload_params, first_burst_length, + params, session->first_burst); + AMAP_SET_BITS(struct amap_beiscsi_offload_params, erl, params, + session->erl); + AMAP_SET_BITS(struct amap_beiscsi_offload_params, dde, params, + conn->datadgst_en); + AMAP_SET_BITS(struct amap_beiscsi_offload_params, hde, params, + conn->hdrdgst_en); + AMAP_SET_BITS(struct amap_beiscsi_offload_params, ir2t, params, + session->initial_r2t_en); + AMAP_SET_BITS(struct amap_beiscsi_offload_params, imd, params, + session->imm_data_en); + AMAP_SET_BITS(struct amap_beiscsi_offload_params, exp_statsn, params, + (conn->exp_statsn - 1)); +} + +/** + * beiscsi_conn_start - offload of session to chip + * @cls_conn: pointer to beiscsi_conn + */ +int beiscsi_conn_start(struct iscsi_cls_conn *cls_conn) +{ + struct iscsi_conn *conn = cls_conn->dd_data; + struct beiscsi_conn *beiscsi_conn = conn->dd_data; + struct beiscsi_endpoint *beiscsi_ep; + struct beiscsi_offload_params params; + struct iscsi_session *session = conn->session; + struct Scsi_Host *shost = iscsi_session_to_shost(session->cls_session); + struct beiscsi_hba *phba = iscsi_host_priv(shost); + + memset(¶ms, 0, sizeof(struct beiscsi_offload_params)); + beiscsi_ep = beiscsi_conn->ep; + if (!beiscsi_ep) + SE_DEBUG(DBG_LVL_1, "In beiscsi_conn_start , no beiscsi_ep\n"); + + free_mgmt_sgl_handle(phba, beiscsi_conn->plogin_sgl_handle); + beiscsi_conn->login_in_progress = 0; + beiscsi_set_params_for_offld(beiscsi_conn, ¶ms); + beiscsi_offload_connection(beiscsi_conn, ¶ms); + iscsi_conn_start(cls_conn); + return 0; +} + +/** + * beiscsi_get_cid - Allocate a cid + * @phba: The phba instance + */ +static int beiscsi_get_cid(struct beiscsi_hba *phba) +{ + unsigned short cid = 0xFFFF; + + if (!phba->avlbl_cids) + return cid; + + cid = phba->cid_array[phba->cid_alloc++]; + if (phba->cid_alloc == phba->params.cxns_per_ctrl) + phba->cid_alloc = 0; + phba->avlbl_cids--; + return cid; +} + +/** + * beiscsi_open_conn - Ask FW to open a TCP connection + * @ep: endpoint to be used + * @src_addr: The source IP address + * @dst_addr: The Destination IP address + * + * Asks the FW to open a TCP connection + */ +static int beiscsi_open_conn(struct iscsi_endpoint *ep, + struct sockaddr *src_addr, + struct sockaddr *dst_addr, int non_blocking) +{ + struct beiscsi_endpoint *beiscsi_ep = ep->dd_data; + struct beiscsi_hba *phba = beiscsi_ep->phba; + int ret = -1; + + beiscsi_ep->ep_cid = beiscsi_get_cid(phba); + if (beiscsi_ep->ep_cid == 0xFFFF) { + SE_DEBUG(DBG_LVL_1, "No free cid available\n"); + return ret; + } + SE_DEBUG(DBG_LVL_8, "In beiscsi_open_conn, ep_cid=%d ", + beiscsi_ep->ep_cid); + phba->ep_array[beiscsi_ep->ep_cid] = ep; + if (beiscsi_ep->ep_cid > + (phba->fw_config.iscsi_cid_start + phba->params.cxns_per_ctrl)) { + SE_DEBUG(DBG_LVL_1, "Failed in allocate iscsi cid\n"); + return ret; + } + + beiscsi_ep->cid_vld = 0; + return mgmt_open_connection(phba, dst_addr, beiscsi_ep); +} + +/** + * beiscsi_put_cid - Free the cid + * @phba: The phba for which the cid is being freed + * @cid: The cid to free + */ +static void beiscsi_put_cid(struct beiscsi_hba *phba, unsigned short cid) +{ + phba->avlbl_cids++; + phba->cid_array[phba->cid_free++] = cid; + if (phba->cid_free == phba->params.cxns_per_ctrl) + phba->cid_free = 0; +} + +/** + * beiscsi_free_ep - free endpoint + * @ep: pointer to iscsi endpoint structure + */ +static void beiscsi_free_ep(struct iscsi_endpoint *ep) +{ + struct beiscsi_endpoint *beiscsi_ep = ep->dd_data; + struct beiscsi_hba *phba = beiscsi_ep->phba; + + beiscsi_put_cid(phba, beiscsi_ep->ep_cid); + beiscsi_ep->phba = NULL; + iscsi_destroy_endpoint(ep); +} + +/** + * beiscsi_ep_connect - Ask chip to create TCP Conn + * @scsi_host: Pointer to scsi_host structure + * @dst_addr: The IP address of Target + * @non_blocking: blocking or non-blocking call + * + * This routines first asks chip to create a connection and then allocates an EP + */ +struct iscsi_endpoint * +beiscsi_ep_connect(struct Scsi_Host *shost, struct sockaddr *dst_addr, + int non_blocking) +{ + struct beiscsi_hba *phba; + struct beiscsi_endpoint *beiscsi_ep; + struct iscsi_endpoint *ep; + int ret; + + SE_DEBUG(DBG_LVL_8, "In beiscsi_ep_connect \n"); + if (shost) + phba = iscsi_host_priv(shost); + else { + ret = -ENXIO; + SE_DEBUG(DBG_LVL_1, "shost is NULL \n"); + return ERR_PTR(ret); + } + ep = iscsi_create_endpoint(sizeof(struct beiscsi_endpoint)); + if (!ep) { + ret = -ENOMEM; + return ERR_PTR(ret); + } + + beiscsi_ep = ep->dd_data; + beiscsi_ep->phba = phba; + + if (beiscsi_open_conn(ep, NULL, dst_addr, non_blocking)) { + SE_DEBUG(DBG_LVL_1, "Failed in allocate iscsi cid\n"); + ret = -ENOMEM; + goto free_ep; + } + + return ep; + +free_ep: + beiscsi_free_ep(ep); + return ERR_PTR(ret); +} + +/** + * beiscsi_ep_poll - Poll to see if connection is established + * @ep: endpoint to be used + * @timeout_ms: timeout specified in millisecs + * + * Poll to see if TCP connection established + */ +int beiscsi_ep_poll(struct iscsi_endpoint *ep, int timeout_ms) +{ + struct beiscsi_endpoint *beiscsi_ep = ep->dd_data; + + SE_DEBUG(DBG_LVL_8, "In beiscsi_ep_poll\n"); + if (beiscsi_ep->cid_vld == 1) + return 1; + else + return 0; +} + +/** + * beiscsi_close_conn - Upload the connection + * @ep: The iscsi endpoint + * @flag: The type of connection closure + */ +static int beiscsi_close_conn(struct iscsi_endpoint *ep, int flag) +{ + int ret = 0; + struct beiscsi_endpoint *beiscsi_ep = ep->dd_data; + struct beiscsi_hba *phba = beiscsi_ep->phba; + + if (MGMT_STATUS_SUCCESS != + mgmt_upload_connection(phba, beiscsi_ep->ep_cid, + CONNECTION_UPLOAD_GRACEFUL)) { + SE_DEBUG(DBG_LVL_8, "upload failed for cid 0x%x", + beiscsi_ep->ep_cid); + ret = -1; + } + + return ret; +} + +/** + * beiscsi_ep_disconnect - Tears down the TCP connection + * @ep: endpoint to be used + * + * Tears down the TCP connection + */ +void beiscsi_ep_disconnect(struct iscsi_endpoint *ep) +{ + struct beiscsi_conn *beiscsi_conn; + struct beiscsi_endpoint *beiscsi_ep; + struct beiscsi_hba *phba; + int flag = 0; + + beiscsi_ep = ep->dd_data; + phba = beiscsi_ep->phba; + SE_DEBUG(DBG_LVL_8, "In beiscsi_ep_disconnect\n"); + + if (beiscsi_ep->conn) { + beiscsi_conn = beiscsi_ep->conn; + iscsi_suspend_queue(beiscsi_conn->conn); + beiscsi_close_conn(ep, flag); + } + + beiscsi_free_ep(ep); +} + +/** + * beiscsi_unbind_conn_to_cid - Unbind the beiscsi_conn from phba conn table + * @phba: The phba instance + * @cid: The cid to free + */ +static int beiscsi_unbind_conn_to_cid(struct beiscsi_hba *phba, + unsigned int cid) +{ + if (phba->conn_table[cid]) + phba->conn_table[cid] = NULL; + else { + SE_DEBUG(DBG_LVL_8, "Connection table Not occupied. \n"); + return -EINVAL; + } + return 0; +} + +/** + * beiscsi_conn_stop - Invalidate and stop the connection + * @cls_conn: pointer to get iscsi_conn + * @flag: The type of connection closure + */ +void beiscsi_conn_stop(struct iscsi_cls_conn *cls_conn, int flag) +{ + struct iscsi_conn *conn = cls_conn->dd_data; + struct beiscsi_conn *beiscsi_conn = conn->dd_data; + struct beiscsi_endpoint *beiscsi_ep; + struct iscsi_session *session = conn->session; + struct Scsi_Host *shost = iscsi_session_to_shost(session->cls_session); + struct beiscsi_hba *phba = iscsi_host_priv(shost); + unsigned int status; + unsigned short savecfg_flag = CMD_ISCSI_SESSION_SAVE_CFG_ON_FLASH; + + SE_DEBUG(DBG_LVL_8, "In beiscsi_conn_stop\n"); + beiscsi_ep = beiscsi_conn->ep; + if (!beiscsi_ep) { + SE_DEBUG(DBG_LVL_8, "In beiscsi_conn_stop , no beiscsi_ep\n"); + return; + } + status = mgmt_invalidate_connection(phba, beiscsi_ep, + beiscsi_ep->ep_cid, 1, + savecfg_flag); + if (status != MGMT_STATUS_SUCCESS) { + SE_DEBUG(DBG_LVL_1, + "mgmt_invalidate_connection Failed for cid=%d \n", + beiscsi_ep->ep_cid); + } + beiscsi_unbind_conn_to_cid(phba, beiscsi_ep->ep_cid); + iscsi_conn_stop(cls_conn, flag); +} diff --git a/drivers/scsi/be2iscsi/be_iscsi.h b/drivers/scsi/be2iscsi/be_iscsi.h new file mode 100644 index 000000000000..f92ffc5349fb --- /dev/null +++ b/drivers/scsi/be2iscsi/be_iscsi.h @@ -0,0 +1,75 @@ +/** + * Copyright (C) 2005 - 2009 ServerEngines + * All rights reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. The full GNU General + * Public License is included in this distribution in the file called COPYING. + * + * Written by: Jayamohan Kallickal (jayamohank@serverengines.com) + * + * Contact Information: + * linux-drivers@serverengines.com + * + * ServerEngines + * 209 N. Fair Oaks Ave + * Sunnyvale, CA 94085 + * + */ + +#ifndef _BE_ISCSI_ +#define _BE_ISCSI_ + +#include "be_main.h" +#include "be_mgmt.h" + +#define BE2_IPV4 0x1 +#define BE2_IPV6 0x10 + +void beiscsi_offload_connection(struct beiscsi_conn *beiscsi_conn, + struct beiscsi_offload_params *params); + +void beiscsi_offload_iscsi(struct beiscsi_hba *phba, struct iscsi_conn *conn, + struct beiscsi_conn *beiscsi_conn, + unsigned int fw_handle); + +struct iscsi_cls_session *beiscsi_session_create(struct iscsi_endpoint *ep, + uint16_t cmds_max, + uint16_t qdepth, + uint32_t initial_cmdsn); + +void beiscsi_session_destroy(struct iscsi_cls_session *cls_session); + +struct iscsi_cls_conn *beiscsi_conn_create(struct iscsi_cls_session + *cls_session, uint32_t cid); + +int beiscsi_conn_bind(struct iscsi_cls_session *cls_session, + struct iscsi_cls_conn *cls_conn, + uint64_t transport_fd, int is_leading); + +int beiscsi_conn_get_param(struct iscsi_cls_conn *cls_conn, + enum iscsi_param param, char *buf); + +int beiscsi_get_host_param(struct Scsi_Host *shost, + enum iscsi_host_param param, char *buf); + +int beiscsi_set_param(struct iscsi_cls_conn *cls_conn, + enum iscsi_param param, char *buf, int buflen); + +int beiscsi_conn_start(struct iscsi_cls_conn *cls_conn); + +void beiscsi_conn_stop(struct iscsi_cls_conn *cls_conn, int flag); + +struct iscsi_endpoint *beiscsi_ep_connect(struct Scsi_Host *shost, + struct sockaddr *dst_addr, + int non_blocking); + +int beiscsi_ep_poll(struct iscsi_endpoint *ep, int timeout_ms); + +void beiscsi_ep_disconnect(struct iscsi_endpoint *ep); + +void beiscsi_conn_get_stats(struct iscsi_cls_conn *cls_conn, + struct iscsi_stats *stats); + +#endif diff --git a/drivers/scsi/be2iscsi/be_main.c b/drivers/scsi/be2iscsi/be_main.c new file mode 100644 index 000000000000..4f1aca346e38 --- /dev/null +++ b/drivers/scsi/be2iscsi/be_main.c @@ -0,0 +1,3390 @@ +/** + * Copyright (C) 2005 - 2009 ServerEngines + * All rights reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. The full GNU General + * Public License is included in this distribution in the file called COPYING. + * + * Written by: Jayamohan Kallickal (jayamohank@serverengines.com) + * + * Contact Information: + * linux-drivers@serverengines.com + * + * ServerEngines + * 209 N. Fair Oaks Ave + * Sunnyvale, CA 94085 + * + */ +#include <linux/reboot.h> +#include <linux/delay.h> +#include <linux/interrupt.h> +#include <linux/blkdev.h> +#include <linux/pci.h> +#include <linux/string.h> +#include <linux/kernel.h> +#include <linux/semaphore.h> + +#include <scsi/libiscsi.h> +#include <scsi/scsi_transport_iscsi.h> +#include <scsi/scsi_transport.h> +#include <scsi/scsi_cmnd.h> +#include <scsi/scsi_device.h> +#include <scsi/scsi_host.h> +#include <scsi/scsi.h> +#include "be_main.h" +#include "be_iscsi.h" +#include "be_mgmt.h" + +static unsigned int be_iopoll_budget = 10; +static unsigned int be_max_phys_size = 64; +static unsigned int enable_msix; + +MODULE_DEVICE_TABLE(pci, beiscsi_pci_id_table); +MODULE_DESCRIPTION(DRV_DESC " " BUILD_STR); +MODULE_AUTHOR("ServerEngines Corporation"); +MODULE_LICENSE("GPL"); +module_param(be_iopoll_budget, int, 0); +module_param(enable_msix, int, 0); +module_param(be_max_phys_size, uint, S_IRUGO); +MODULE_PARM_DESC(be_max_phys_size, "Maximum Size (In Kilobytes) of physically" + "contiguous memory that can be allocated." + "Range is 16 - 128"); + +static int beiscsi_slave_configure(struct scsi_device *sdev) +{ + blk_queue_max_segment_size(sdev->request_queue, 65536); + return 0; +} + +static struct scsi_host_template beiscsi_sht = { + .module = THIS_MODULE, + .name = "ServerEngines 10Gbe open-iscsi Initiator Driver", + .proc_name = DRV_NAME, + .queuecommand = iscsi_queuecommand, + .eh_abort_handler = iscsi_eh_abort, + .change_queue_depth = iscsi_change_queue_depth, + .slave_configure = beiscsi_slave_configure, + .target_alloc = iscsi_target_alloc, + .eh_device_reset_handler = iscsi_eh_device_reset, + .eh_target_reset_handler = iscsi_eh_target_reset, + .sg_tablesize = BEISCSI_SGLIST_ELEMENTS, + .can_queue = BE2_IO_DEPTH, + .this_id = -1, + .max_sectors = BEISCSI_MAX_SECTORS, + .cmd_per_lun = BEISCSI_CMD_PER_LUN, + .use_clustering = ENABLE_CLUSTERING, +}; +static struct scsi_transport_template *beiscsi_scsi_transport; + +/*------------------- PCI Driver operations and data ----------------- */ +static DEFINE_PCI_DEVICE_TABLE(beiscsi_pci_id_table) = { + { PCI_DEVICE(BE_VENDOR_ID, BE_DEVICE_ID1) }, + { PCI_DEVICE(BE_VENDOR_ID, OC_DEVICE_ID1) }, + { PCI_DEVICE(BE_VENDOR_ID, OC_DEVICE_ID2) }, + { 0 } +}; +MODULE_DEVICE_TABLE(pci, beiscsi_pci_id_table); + +static struct beiscsi_hba *beiscsi_hba_alloc(struct pci_dev *pcidev) +{ + struct beiscsi_hba *phba; + struct Scsi_Host *shost; + + shost = iscsi_host_alloc(&beiscsi_sht, sizeof(*phba), 0); + if (!shost) { + dev_err(&pcidev->dev, "beiscsi_hba_alloc -" + "iscsi_host_alloc failed \n"); + return NULL; + } + shost->dma_boundary = pcidev->dma_mask; + shost->max_id = BE2_MAX_SESSIONS; + shost->max_channel = 0; + shost->max_cmd_len = BEISCSI_MAX_CMD_LEN; + shost->max_lun = BEISCSI_NUM_MAX_LUN; + shost->transportt = beiscsi_scsi_transport; + + phba = iscsi_host_priv(shost); + memset(phba, 0, sizeof(*phba)); + phba->shost = shost; + phba->pcidev = pci_dev_get(pcidev); + + if (iscsi_host_add(shost, &phba->pcidev->dev)) + goto free_devices; + return phba; + +free_devices: + pci_dev_put(phba->pcidev); + iscsi_host_free(phba->shost); + return NULL; +} + +static void beiscsi_unmap_pci_function(struct beiscsi_hba *phba) +{ + if (phba->csr_va) { + iounmap(phba->csr_va); + phba->csr_va = NULL; + } + if (phba->db_va) { + iounmap(phba->db_va); + phba->db_va = NULL; + } + if (phba->pci_va) { + iounmap(phba->pci_va); + phba->pci_va = NULL; + } +} + +static int beiscsi_map_pci_bars(struct beiscsi_hba *phba, + struct pci_dev *pcidev) +{ + u8 __iomem *addr; + + addr = ioremap_nocache(pci_resource_start(pcidev, 2), + pci_resource_len(pcidev, 2)); + if (addr == NULL) + return -ENOMEM; + phba->ctrl.csr = addr; + phba->csr_va = addr; + phba->csr_pa.u.a64.address = pci_resource_start(pcidev, 2); + + addr = ioremap_nocache(pci_resource_start(pcidev, 4), 128 * 1024); + if (addr == NULL) + goto pci_map_err; + phba->ctrl.db = addr; + phba->db_va = addr; + phba->db_pa.u.a64.address = pci_resource_start(pcidev, 4); + + addr = ioremap_nocache(pci_resource_start(pcidev, 1), + pci_resource_len(pcidev, 1)); + if (addr == NULL) + goto pci_map_err; + phba->ctrl.pcicfg = addr; + phba->pci_va = addr; + phba->pci_pa.u.a64.address = pci_resource_start(pcidev, 1); + return 0; + +pci_map_err: + beiscsi_unmap_pci_function(phba); + return -ENOMEM; +} + +static int beiscsi_enable_pci(struct pci_dev *pcidev) +{ + int ret; + + ret = pci_enable_device(pcidev); + if (ret) { + dev_err(&pcidev->dev, "beiscsi_enable_pci - enable device " + "failed. Returning -ENODEV\n"); + return ret; + } + + if (pci_set_consistent_dma_mask(pcidev, DMA_BIT_MASK(64))) { + ret = pci_set_consistent_dma_mask(pcidev, DMA_BIT_MASK(32)); + if (ret) { + dev_err(&pcidev->dev, "Could not set PCI DMA Mask\n"); + pci_disable_device(pcidev); + return ret; + } + } + return 0; +} + +static int be_ctrl_init(struct beiscsi_hba *phba, struct pci_dev *pdev) +{ + struct be_ctrl_info *ctrl = &phba->ctrl; + struct be_dma_mem *mbox_mem_alloc = &ctrl->mbox_mem_alloced; + struct be_dma_mem *mbox_mem_align = &ctrl->mbox_mem; + int status = 0; + + ctrl->pdev = pdev; + status = beiscsi_map_pci_bars(phba, pdev); + if (status) + return status; + + mbox_mem_alloc->size = sizeof(struct be_mcc_mailbox) + 16; + mbox_mem_alloc->va = pci_alloc_consistent(pdev, + mbox_mem_alloc->size, + &mbox_mem_alloc->dma); + if (!mbox_mem_alloc->va) { + beiscsi_unmap_pci_function(phba); + status = -ENOMEM; + return status; + } + + mbox_mem_align->size = sizeof(struct be_mcc_mailbox); + mbox_mem_align->va = PTR_ALIGN(mbox_mem_alloc->va, 16); + mbox_mem_align->dma = PTR_ALIGN(mbox_mem_alloc->dma, 16); + memset(mbox_mem_align->va, 0, sizeof(struct be_mcc_mailbox)); + spin_lock_init(&ctrl->mbox_lock); + return status; +} + +static void beiscsi_get_params(struct beiscsi_hba *phba) +{ + phba->params.ios_per_ctrl = BE2_IO_DEPTH; + phba->params.cxns_per_ctrl = BE2_MAX_SESSIONS; + phba->params.asyncpdus_per_ctrl = BE2_ASYNCPDUS; + phba->params.icds_per_ctrl = BE2_MAX_ICDS / 2; + phba->params.num_sge_per_io = BE2_SGE; + phba->params.defpdu_hdr_sz = BE2_DEFPDU_HDR_SZ; + phba->params.defpdu_data_sz = BE2_DEFPDU_DATA_SZ; + phba->params.eq_timer = 64; + phba->params.num_eq_entries = + (((BE2_CMDS_PER_CXN * 2 + BE2_LOGOUTS + BE2_TMFS + BE2_ASYNCPDUS) / + 512) + 1) * 512; + phba->params.num_eq_entries = (phba->params.num_eq_entries < 1024) + ? 1024 : phba->params.num_eq_entries; + SE_DEBUG(DBG_LVL_8, "phba->params.num_eq_entries=%d \n", + phba->params.num_eq_entries); + phba->params.num_cq_entries = + (((BE2_CMDS_PER_CXN * 2 + BE2_LOGOUTS + BE2_TMFS + BE2_ASYNCPDUS) / + 512) + 1) * 512; + SE_DEBUG(DBG_LVL_8, + "phba->params.num_cq_entries=%d BE2_CMDS_PER_CXN=%d" + "BE2_LOGOUTS=%d BE2_TMFS=%d BE2_ASYNCPDUS=%d \n", + phba->params.num_cq_entries, BE2_CMDS_PER_CXN, + BE2_LOGOUTS, BE2_TMFS, BE2_ASYNCPDUS); + phba->params.wrbs_per_cxn = 256; +} + +static void hwi_ring_eq_db(struct beiscsi_hba *phba, + unsigned int id, unsigned int clr_interrupt, + unsigned int num_processed, + unsigned char rearm, unsigned char event) +{ + u32 val = 0; + val |= id & DB_EQ_RING_ID_MASK; + if (rearm) + val |= 1 << DB_EQ_REARM_SHIFT; + if (clr_interrupt) + val |= 1 << DB_EQ_CLR_SHIFT; + if (event) + val |= 1 << DB_EQ_EVNT_SHIFT; + val |= num_processed << DB_EQ_NUM_POPPED_SHIFT; + iowrite32(val, phba->db_va + DB_EQ_OFFSET); +} + +/** + * be_isr - The isr routine of the driver. + * @irq: Not used + * @dev_id: Pointer to host adapter structure + */ +static irqreturn_t be_isr(int irq, void *dev_id) +{ + struct beiscsi_hba *phba; + struct hwi_controller *phwi_ctrlr; + struct hwi_context_memory *phwi_context; + struct be_eq_entry *eqe = NULL; + struct be_queue_info *eq; + struct be_queue_info *cq; + unsigned long flags, index; + unsigned int num_eq_processed; + struct be_ctrl_info *ctrl; + int isr; + + phba = dev_id; + if (!enable_msix) { + ctrl = &phba->ctrl;; + isr = ioread32(ctrl->csr + CEV_ISR0_OFFSET + + (PCI_FUNC(ctrl->pdev->devfn) * CEV_ISR_SIZE)); + if (!isr) + return IRQ_NONE; + } + + phwi_ctrlr = phba->phwi_ctrlr; + phwi_context = phwi_ctrlr->phwi_ctxt; + eq = &phwi_context->be_eq.q; + cq = &phwi_context->be_cq; + index = 0; + eqe = queue_tail_node(eq); + if (!eqe) + SE_DEBUG(DBG_LVL_1, "eqe is NULL\n"); + + num_eq_processed = 0; + if (blk_iopoll_enabled) { + while (eqe->dw[offsetof(struct amap_eq_entry, valid) / 32] + & EQE_VALID_MASK) { + if (!blk_iopoll_sched_prep(&phba->iopoll)) + blk_iopoll_sched(&phba->iopoll); + + AMAP_SET_BITS(struct amap_eq_entry, valid, eqe, 0); + queue_tail_inc(eq); + eqe = queue_tail_node(eq); + num_eq_processed++; + SE_DEBUG(DBG_LVL_8, "Valid EQE\n"); + } + if (num_eq_processed) { + hwi_ring_eq_db(phba, eq->id, 0, num_eq_processed, 0, 1); + return IRQ_HANDLED; + } else + return IRQ_NONE; + } else { + while (eqe->dw[offsetof(struct amap_eq_entry, valid) / 32] + & EQE_VALID_MASK) { + + if (((eqe->dw[offsetof(struct amap_eq_entry, + resource_id) / 32] & + EQE_RESID_MASK) >> 16) != cq->id) { + spin_lock_irqsave(&phba->isr_lock, flags); + phba->todo_mcc_cq = 1; + spin_unlock_irqrestore(&phba->isr_lock, flags); + } else { + spin_lock_irqsave(&phba->isr_lock, flags); + phba->todo_cq = 1; + spin_unlock_irqrestore(&phba->isr_lock, flags); + } + AMAP_SET_BITS(struct amap_eq_entry, valid, eqe, 0); + queue_tail_inc(eq); + eqe = queue_tail_node(eq); + num_eq_processed++; + } + if (phba->todo_cq || phba->todo_mcc_cq) + queue_work(phba->wq, &phba->work_cqs); + + if (num_eq_processed) { + hwi_ring_eq_db(phba, eq->id, 0, num_eq_processed, 1, 1); + return IRQ_HANDLED; + } else + return IRQ_NONE; + } +} + +static int beiscsi_init_irqs(struct beiscsi_hba *phba) +{ + struct pci_dev *pcidev = phba->pcidev; + int ret; + + ret = request_irq(pcidev->irq, be_isr, IRQF_SHARED, "beiscsi", phba); + if (ret) { + shost_printk(KERN_ERR, phba->shost, "beiscsi_init_irqs-" + "Failed to register irq\\n"); + return ret; + } + return 0; +} + +static void hwi_ring_cq_db(struct beiscsi_hba *phba, + unsigned int id, unsigned int num_processed, + unsigned char rearm, unsigned char event) +{ + u32 val = 0; + val |= id & DB_CQ_RING_ID_MASK; + if (rearm) + val |= 1 << DB_CQ_REARM_SHIFT; + val |= num_processed << DB_CQ_NUM_POPPED_SHIFT; + iowrite32(val, phba->db_va + DB_CQ_OFFSET); +} + +/* + * async pdus include + * a. unsolicited NOP-In (target initiated NOP-In) + * b. Async Messages + * c. Reject PDU + * d. Login response + * These headers arrive unprocessed by the EP firmware and iSCSI layer + * process them + */ +static unsigned int +beiscsi_process_async_pdu(struct beiscsi_conn *beiscsi_conn, + struct beiscsi_hba *phba, + unsigned short cid, + struct pdu_base *ppdu, + unsigned long pdu_len, + void *pbuffer, unsigned long buf_len) +{ + struct iscsi_conn *conn = beiscsi_conn->conn; + struct iscsi_session *session = conn->session; + + switch (ppdu->dw[offsetof(struct amap_pdu_base, opcode) / 32] & + PDUBASE_OPCODE_MASK) { + case ISCSI_OP_NOOP_IN: + pbuffer = NULL; + buf_len = 0; + break; + case ISCSI_OP_ASYNC_EVENT: + break; + case ISCSI_OP_REJECT: + WARN_ON(!pbuffer); + WARN_ON(!(buf_len == 48)); + SE_DEBUG(DBG_LVL_1, "In ISCSI_OP_REJECT\n"); + break; + case ISCSI_OP_LOGIN_RSP: + break; + default: + shost_printk(KERN_WARNING, phba->shost, + "Unrecognized opcode 0x%x in async msg \n", + (ppdu-> + dw[offsetof(struct amap_pdu_base, opcode) / 32] + & PDUBASE_OPCODE_MASK)); + return 1; + } + + spin_lock_bh(&session->lock); + __iscsi_complete_pdu(conn, (struct iscsi_hdr *)ppdu, pbuffer, buf_len); + spin_unlock_bh(&session->lock); + return 0; +} + +static struct sgl_handle *alloc_io_sgl_handle(struct beiscsi_hba *phba) +{ + struct sgl_handle *psgl_handle; + + if (phba->io_sgl_hndl_avbl) { + SE_DEBUG(DBG_LVL_8, + "In alloc_io_sgl_handle,io_sgl_alloc_index=%d \n", + phba->io_sgl_alloc_index); + psgl_handle = phba->io_sgl_hndl_base[phba-> + io_sgl_alloc_index]; + phba->io_sgl_hndl_base[phba->io_sgl_alloc_index] = NULL; + phba->io_sgl_hndl_avbl--; + if (phba->io_sgl_alloc_index == (phba->params.ios_per_ctrl - 1)) + phba->io_sgl_alloc_index = 0; + else + phba->io_sgl_alloc_index++; + } else + psgl_handle = NULL; + return psgl_handle; +} + +static void +free_io_sgl_handle(struct beiscsi_hba *phba, struct sgl_handle *psgl_handle) +{ + SE_DEBUG(DBG_LVL_8, "In free_,io_sgl_free_index=%d \n", + phba->io_sgl_free_index); + if (phba->io_sgl_hndl_base[phba->io_sgl_free_index]) { + /* + * this can happen if clean_task is called on a task that + * failed in xmit_task or alloc_pdu. + */ + SE_DEBUG(DBG_LVL_8, + "Double Free in IO SGL io_sgl_free_index=%d," + "value there=%p \n", phba->io_sgl_free_index, + phba->io_sgl_hndl_base[phba->io_sgl_free_index]); + return; + } + phba->io_sgl_hndl_base[phba->io_sgl_free_index] = psgl_handle; + phba->io_sgl_hndl_avbl++; + if (phba->io_sgl_free_index == (phba->params.ios_per_ctrl - 1)) + phba->io_sgl_free_index = 0; + else + phba->io_sgl_free_index++; +} + +/** + * alloc_wrb_handle - To allocate a wrb handle + * @phba: The hba pointer + * @cid: The cid to use for allocation + * @index: index allocation and wrb index + * + * This happens under session_lock until submission to chip + */ +struct wrb_handle *alloc_wrb_handle(struct beiscsi_hba *phba, unsigned int cid, + int index) +{ + struct hwi_wrb_context *pwrb_context; + struct hwi_controller *phwi_ctrlr; + struct wrb_handle *pwrb_handle; + + phwi_ctrlr = phba->phwi_ctrlr; + pwrb_context = &phwi_ctrlr->wrb_context[cid]; + pwrb_handle = pwrb_context->pwrb_handle_base[index]; + pwrb_handle->wrb_index = index; + pwrb_handle->nxt_wrb_index = index; + return pwrb_handle; +} + +/** + * free_wrb_handle - To free the wrb handle back to pool + * @phba: The hba pointer + * @pwrb_context: The context to free from + * @pwrb_handle: The wrb_handle to free + * + * This happens under session_lock until submission to chip + */ +static void +free_wrb_handle(struct beiscsi_hba *phba, struct hwi_wrb_context *pwrb_context, + struct wrb_handle *pwrb_handle) +{ + SE_DEBUG(DBG_LVL_8, + "FREE WRB: pwrb_handle=%p free_index=%d=0x%x" + "wrb_handles_available=%d \n", + pwrb_handle, pwrb_context->free_index, + pwrb_context->free_index, pwrb_context->wrb_handles_available); +} + +static struct sgl_handle *alloc_mgmt_sgl_handle(struct beiscsi_hba *phba) +{ + struct sgl_handle *psgl_handle; + + if (phba->eh_sgl_hndl_avbl) { + psgl_handle = phba->eh_sgl_hndl_base[phba->eh_sgl_alloc_index]; + phba->eh_sgl_hndl_base[phba->eh_sgl_alloc_index] = NULL; + SE_DEBUG(DBG_LVL_8, "mgmt_sgl_alloc_index=%d=0x%x \n", + phba->eh_sgl_alloc_index, phba->eh_sgl_alloc_index); + phba->eh_sgl_hndl_avbl--; + if (phba->eh_sgl_alloc_index == + (phba->params.icds_per_ctrl - phba->params.ios_per_ctrl - + 1)) + phba->eh_sgl_alloc_index = 0; + else + phba->eh_sgl_alloc_index++; + } else + psgl_handle = NULL; + return psgl_handle; +} + +void +free_mgmt_sgl_handle(struct beiscsi_hba *phba, struct sgl_handle *psgl_handle) +{ + + if (phba->eh_sgl_hndl_base[phba->eh_sgl_free_index]) { + /* + * this can happen if clean_task is called on a task that + * failed in xmit_task or alloc_pdu. + */ + SE_DEBUG(DBG_LVL_8, + "Double Free in eh SGL ,eh_sgl_free_index=%d \n", + phba->eh_sgl_free_index); + return; + } + phba->eh_sgl_hndl_base[phba->eh_sgl_free_index] = psgl_handle; + phba->eh_sgl_hndl_avbl++; + if (phba->eh_sgl_free_index == + (phba->params.icds_per_ctrl - phba->params.ios_per_ctrl - 1)) + phba->eh_sgl_free_index = 0; + else + phba->eh_sgl_free_index++; +} + +static void +be_complete_io(struct beiscsi_conn *beiscsi_conn, + struct iscsi_task *task, struct sol_cqe *psol) +{ + struct beiscsi_io_task *io_task = task->dd_data; + struct be_status_bhs *sts_bhs = + (struct be_status_bhs *)io_task->cmd_bhs; + struct iscsi_conn *conn = beiscsi_conn->conn; + unsigned int sense_len; + unsigned char *sense; + u32 resid = 0, exp_cmdsn, max_cmdsn; + u8 rsp, status, flags; + + exp_cmdsn = be32_to_cpu(psol-> + dw[offsetof(struct amap_sol_cqe, i_exp_cmd_sn) / 32] + & SOL_EXP_CMD_SN_MASK); + max_cmdsn = be32_to_cpu((psol-> + dw[offsetof(struct amap_sol_cqe, i_exp_cmd_sn) / 32] + & SOL_EXP_CMD_SN_MASK) + + ((psol->dw[offsetof(struct amap_sol_cqe, i_cmd_wnd) + / 32] & SOL_CMD_WND_MASK) >> 24) - 1); + rsp = ((psol->dw[offsetof(struct amap_sol_cqe, i_resp) / 32] + & SOL_RESP_MASK) >> 16); + status = ((psol->dw[offsetof(struct amap_sol_cqe, i_sts) / 32] + & SOL_STS_MASK) >> 8); + flags = ((psol->dw[offsetof(struct amap_sol_cqe, i_flags) / 32] + & SOL_FLAGS_MASK) >> 24) | 0x80; + + task->sc->result = (DID_OK << 16) | status; + if (rsp != ISCSI_STATUS_CMD_COMPLETED) { + task->sc->result = DID_ERROR << 16; + goto unmap; + } + + /* bidi not initially supported */ + if (flags & (ISCSI_FLAG_CMD_UNDERFLOW | ISCSI_FLAG_CMD_OVERFLOW)) { + resid = (psol->dw[offsetof(struct amap_sol_cqe, i_res_cnt) / + 32] & SOL_RES_CNT_MASK); + + if (!status && (flags & ISCSI_FLAG_CMD_OVERFLOW)) + task->sc->result = DID_ERROR << 16; + + if (flags & ISCSI_FLAG_CMD_UNDERFLOW) { + scsi_set_resid(task->sc, resid); + if (!status && (scsi_bufflen(task->sc) - resid < + task->sc->underflow)) + task->sc->result = DID_ERROR << 16; + } + } + + if (status == SAM_STAT_CHECK_CONDITION) { + sense = sts_bhs->sense_info + sizeof(unsigned short); + sense_len = + cpu_to_be16((unsigned short)(sts_bhs->sense_info[0])); + memcpy(task->sc->sense_buffer, sense, + min_t(u16, sense_len, SCSI_SENSE_BUFFERSIZE)); + } + if (io_task->cmd_bhs->iscsi_hdr.flags & ISCSI_FLAG_CMD_READ) { + if (psol->dw[offsetof(struct amap_sol_cqe, i_res_cnt) / 32] + & SOL_RES_CNT_MASK) + conn->rxdata_octets += (psol-> + dw[offsetof(struct amap_sol_cqe, i_res_cnt) / 32] + & SOL_RES_CNT_MASK); + } +unmap: + scsi_dma_unmap(io_task->scsi_cmnd); + iscsi_complete_scsi_task(task, exp_cmdsn, max_cmdsn); +} + +static void +be_complete_logout(struct beiscsi_conn *beiscsi_conn, + struct iscsi_task *task, struct sol_cqe *psol) +{ + struct iscsi_logout_rsp *hdr; + struct iscsi_conn *conn = beiscsi_conn->conn; + + hdr = (struct iscsi_logout_rsp *)task->hdr; + hdr->t2wait = 5; + hdr->t2retain = 0; + hdr->flags = ((psol->dw[offsetof(struct amap_sol_cqe, i_flags) / 32] + & SOL_FLAGS_MASK) >> 24) | 0x80; + hdr->response = (psol->dw[offsetof(struct amap_sol_cqe, i_resp) / + 32] & SOL_RESP_MASK); + hdr->exp_cmdsn = cpu_to_be32(psol-> + dw[offsetof(struct amap_sol_cqe, i_exp_cmd_sn) / 32] + & SOL_EXP_CMD_SN_MASK); + hdr->max_cmdsn = be32_to_cpu((psol-> + dw[offsetof(struct amap_sol_cqe, i_exp_cmd_sn) / 32] + & SOL_EXP_CMD_SN_MASK) + + ((psol->dw[offsetof(struct amap_sol_cqe, i_cmd_wnd) + / 32] & SOL_CMD_WND_MASK) >> 24) - 1); + hdr->hlength = 0; + + __iscsi_complete_pdu(conn, (struct iscsi_hdr *)hdr, NULL, 0); +} + +static void +be_complete_tmf(struct beiscsi_conn *beiscsi_conn, + struct iscsi_task *task, struct sol_cqe *psol) +{ + struct iscsi_tm_rsp *hdr; + struct iscsi_conn *conn = beiscsi_conn->conn; + + hdr = (struct iscsi_tm_rsp *)task->hdr; + hdr->flags = ((psol->dw[offsetof(struct amap_sol_cqe, i_flags) / 32] + & SOL_FLAGS_MASK) >> 24) | 0x80; + hdr->response = (psol->dw[offsetof(struct amap_sol_cqe, i_resp) / + 32] & SOL_RESP_MASK); + hdr->exp_cmdsn = cpu_to_be32(psol->dw[offsetof(struct amap_sol_cqe, + i_exp_cmd_sn) / 32] & SOL_EXP_CMD_SN_MASK); + hdr->max_cmdsn = be32_to_cpu((psol->dw[offsetof(struct amap_sol_cqe, + i_exp_cmd_sn) / 32] & SOL_EXP_CMD_SN_MASK) + + ((psol->dw[offsetof(struct amap_sol_cqe, i_cmd_wnd) + / 32] & SOL_CMD_WND_MASK) >> 24) - 1); + __iscsi_complete_pdu(conn, (struct iscsi_hdr *)hdr, NULL, 0); +} + +static void +hwi_complete_drvr_msgs(struct beiscsi_conn *beiscsi_conn, + struct beiscsi_hba *phba, struct sol_cqe *psol) +{ + struct hwi_wrb_context *pwrb_context; + struct wrb_handle *pwrb_handle; + struct hwi_controller *phwi_ctrlr; + struct iscsi_conn *conn = beiscsi_conn->conn; + struct iscsi_session *session = conn->session; + + phwi_ctrlr = phba->phwi_ctrlr; + pwrb_context = &phwi_ctrlr->wrb_context[((psol-> + dw[offsetof(struct amap_sol_cqe, cid) / 32] & + SOL_CID_MASK) >> 6)]; + pwrb_handle = pwrb_context->pwrb_handle_basestd[((psol-> + dw[offsetof(struct amap_sol_cqe, wrb_index) / + 32] & SOL_WRB_INDEX_MASK) >> 16)]; + spin_lock_bh(&session->lock); + free_wrb_handle(phba, pwrb_context, pwrb_handle); + spin_unlock_bh(&session->lock); +} + +static void +be_complete_nopin_resp(struct beiscsi_conn *beiscsi_conn, + struct iscsi_task *task, struct sol_cqe *psol) +{ + struct iscsi_nopin *hdr; + struct iscsi_conn *conn = beiscsi_conn->conn; + + hdr = (struct iscsi_nopin *)task->hdr; + hdr->flags = ((psol->dw[offsetof(struct amap_sol_cqe, i_flags) / 32] + & SOL_FLAGS_MASK) >> 24) | 0x80; + hdr->exp_cmdsn = cpu_to_be32(psol->dw[offsetof(struct amap_sol_cqe, + i_exp_cmd_sn) / 32] & SOL_EXP_CMD_SN_MASK); + hdr->max_cmdsn = be32_to_cpu((psol->dw[offsetof(struct amap_sol_cqe, + i_exp_cmd_sn) / 32] & SOL_EXP_CMD_SN_MASK) + + ((psol->dw[offsetof(struct amap_sol_cqe, i_cmd_wnd) + / 32] & SOL_CMD_WND_MASK) >> 24) - 1); + hdr->opcode = ISCSI_OP_NOOP_IN; + __iscsi_complete_pdu(conn, (struct iscsi_hdr *)hdr, NULL, 0); +} + +static void hwi_complete_cmd(struct beiscsi_conn *beiscsi_conn, + struct beiscsi_hba *phba, struct sol_cqe *psol) +{ + struct hwi_wrb_context *pwrb_context; + struct wrb_handle *pwrb_handle; + struct iscsi_wrb *pwrb = NULL; + struct hwi_controller *phwi_ctrlr; + struct iscsi_task *task; + struct beiscsi_io_task *io_task; + struct iscsi_conn *conn = beiscsi_conn->conn; + struct iscsi_session *session = conn->session; + + phwi_ctrlr = phba->phwi_ctrlr; + + pwrb_context = &phwi_ctrlr-> + wrb_context[((psol->dw[offsetof(struct amap_sol_cqe, cid) / 32] + & SOL_CID_MASK) >> 6)]; + pwrb_handle = pwrb_context->pwrb_handle_basestd[((psol-> + dw[offsetof(struct amap_sol_cqe, wrb_index) / + 32] & SOL_WRB_INDEX_MASK) >> 16)]; + + task = pwrb_handle->pio_handle; + io_task = task->dd_data; + spin_lock_bh(&session->lock); + pwrb = pwrb_handle->pwrb; + switch ((pwrb->dw[offsetof(struct amap_iscsi_wrb, type) / 32] & + WRB_TYPE_MASK) >> 28) { + case HWH_TYPE_IO: + case HWH_TYPE_IO_RD: + if ((task->hdr->opcode & ISCSI_OPCODE_MASK) == + ISCSI_OP_NOOP_OUT) { + be_complete_nopin_resp(beiscsi_conn, task, psol); + } else + be_complete_io(beiscsi_conn, task, psol); + break; + + case HWH_TYPE_LOGOUT: + be_complete_logout(beiscsi_conn, task, psol); + break; + + case HWH_TYPE_LOGIN: + SE_DEBUG(DBG_LVL_1, + "\t\t No HWH_TYPE_LOGIN Expected in hwi_complete_cmd" + "- Solicited path \n"); + break; + + case HWH_TYPE_TMF: + be_complete_tmf(beiscsi_conn, task, psol); + break; + + case HWH_TYPE_NOP: + be_complete_nopin_resp(beiscsi_conn, task, psol); + break; + + default: + shost_printk(KERN_WARNING, phba->shost, + "wrb_index 0x%x CID 0x%x\n", + ((psol->dw[offsetof(struct amap_iscsi_wrb, type) / + 32] & SOL_WRB_INDEX_MASK) >> 16), + ((psol->dw[offsetof(struct amap_sol_cqe, cid) / 32] + & SOL_CID_MASK) >> 6)); + break; + } + + spin_unlock_bh(&session->lock); +} + +static struct list_head *hwi_get_async_busy_list(struct hwi_async_pdu_context + *pasync_ctx, unsigned int is_header, + unsigned int host_write_ptr) +{ + if (is_header) + return &pasync_ctx->async_entry[host_write_ptr]. + header_busy_list; + else + return &pasync_ctx->async_entry[host_write_ptr].data_busy_list; +} + +static struct async_pdu_handle * +hwi_get_async_handle(struct beiscsi_hba *phba, + struct beiscsi_conn *beiscsi_conn, + struct hwi_async_pdu_context *pasync_ctx, + struct i_t_dpdu_cqe *pdpdu_cqe, unsigned int *pcq_index) +{ + struct be_bus_address phys_addr; + struct list_head *pbusy_list; + struct async_pdu_handle *pasync_handle = NULL; + int buffer_len = 0; + unsigned char buffer_index = -1; + unsigned char is_header = 0; + + phys_addr.u.a32.address_lo = + pdpdu_cqe->dw[offsetof(struct amap_i_t_dpdu_cqe, db_addr_lo) / 32] - + ((pdpdu_cqe->dw[offsetof(struct amap_i_t_dpdu_cqe, dpl) / 32] + & PDUCQE_DPL_MASK) >> 16); + phys_addr.u.a32.address_hi = + pdpdu_cqe->dw[offsetof(struct amap_i_t_dpdu_cqe, db_addr_hi) / 32]; + + phys_addr.u.a64.address = + *((unsigned long long *)(&phys_addr.u.a64.address)); + + switch (pdpdu_cqe->dw[offsetof(struct amap_i_t_dpdu_cqe, code) / 32] + & PDUCQE_CODE_MASK) { + case UNSOL_HDR_NOTIFY: + is_header = 1; + + pbusy_list = hwi_get_async_busy_list(pasync_ctx, 1, + (pdpdu_cqe->dw[offsetof(struct amap_i_t_dpdu_cqe, + index) / 32] & PDUCQE_INDEX_MASK)); + + buffer_len = (unsigned int)(phys_addr.u.a64.address - + pasync_ctx->async_header.pa_base.u.a64.address); + + buffer_index = buffer_len / + pasync_ctx->async_header.buffer_size; + + break; + case UNSOL_DATA_NOTIFY: + pbusy_list = hwi_get_async_busy_list(pasync_ctx, 0, (pdpdu_cqe-> + dw[offsetof(struct amap_i_t_dpdu_cqe, + index) / 32] & PDUCQE_INDEX_MASK)); + buffer_len = (unsigned long)(phys_addr.u.a64.address - + pasync_ctx->async_data.pa_base.u. + a64.address); + buffer_index = buffer_len / pasync_ctx->async_data.buffer_size; + break; + default: + pbusy_list = NULL; + shost_printk(KERN_WARNING, phba->shost, + "Unexpected code=%d \n", + pdpdu_cqe->dw[offsetof(struct amap_i_t_dpdu_cqe, + code) / 32] & PDUCQE_CODE_MASK); + return NULL; + } + + WARN_ON(!(buffer_index <= pasync_ctx->async_data.num_entries)); + WARN_ON(list_empty(pbusy_list)); + list_for_each_entry(pasync_handle, pbusy_list, link) { + WARN_ON(pasync_handle->consumed); + if (pasync_handle->index == buffer_index) + break; + } + + WARN_ON(!pasync_handle); + + pasync_handle->cri = (unsigned short)beiscsi_conn->beiscsi_conn_cid; + pasync_handle->is_header = is_header; + pasync_handle->buffer_len = ((pdpdu_cqe-> + dw[offsetof(struct amap_i_t_dpdu_cqe, dpl) / 32] + & PDUCQE_DPL_MASK) >> 16); + + *pcq_index = (pdpdu_cqe->dw[offsetof(struct amap_i_t_dpdu_cqe, + index) / 32] & PDUCQE_INDEX_MASK); + return pasync_handle; +} + +static unsigned int +hwi_update_async_writables(struct hwi_async_pdu_context *pasync_ctx, + unsigned int is_header, unsigned int cq_index) +{ + struct list_head *pbusy_list; + struct async_pdu_handle *pasync_handle; + unsigned int num_entries, writables = 0; + unsigned int *pep_read_ptr, *pwritables; + + + if (is_header) { + pep_read_ptr = &pasync_ctx->async_header.ep_read_ptr; + pwritables = &pasync_ctx->async_header.writables; + num_entries = pasync_ctx->async_header.num_entries; + } else { + pep_read_ptr = &pasync_ctx->async_data.ep_read_ptr; + pwritables = &pasync_ctx->async_data.writables; + num_entries = pasync_ctx->async_data.num_entries; + } + + while ((*pep_read_ptr) != cq_index) { + (*pep_read_ptr)++; + *pep_read_ptr = (*pep_read_ptr) % num_entries; + + pbusy_list = hwi_get_async_busy_list(pasync_ctx, is_header, + *pep_read_ptr); + if (writables == 0) + WARN_ON(list_empty(pbusy_list)); + + if (!list_empty(pbusy_list)) { + pasync_handle = list_entry(pbusy_list->next, + struct async_pdu_handle, + link); + WARN_ON(!pasync_handle); + pasync_handle->consumed = 1; + } + + writables++; + } + + if (!writables) { + SE_DEBUG(DBG_LVL_1, + "Duplicate notification received - index 0x%x!!\n", + cq_index); + WARN_ON(1); + } + + *pwritables = *pwritables + writables; + return 0; +} + +static unsigned int hwi_free_async_msg(struct beiscsi_hba *phba, + unsigned int cri) +{ + struct hwi_controller *phwi_ctrlr; + struct hwi_async_pdu_context *pasync_ctx; + struct async_pdu_handle *pasync_handle, *tmp_handle; + struct list_head *plist; + unsigned int i = 0; + + phwi_ctrlr = phba->phwi_ctrlr; + pasync_ctx = HWI_GET_ASYNC_PDU_CTX(phwi_ctrlr); + + plist = &pasync_ctx->async_entry[cri].wait_queue.list; + + list_for_each_entry_safe(pasync_handle, tmp_handle, plist, link) { + list_del(&pasync_handle->link); + + if (i == 0) { + list_add_tail(&pasync_handle->link, + &pasync_ctx->async_header.free_list); + pasync_ctx->async_header.free_entries++; + i++; + } else { + list_add_tail(&pasync_handle->link, + &pasync_ctx->async_data.free_list); + pasync_ctx->async_data.free_entries++; + i++; + } + } + + INIT_LIST_HEAD(&pasync_ctx->async_entry[cri].wait_queue.list); + pasync_ctx->async_entry[cri].wait_queue.hdr_received = 0; + pasync_ctx->async_entry[cri].wait_queue.bytes_received = 0; + return 0; +} + +static struct phys_addr * +hwi_get_ring_address(struct hwi_async_pdu_context *pasync_ctx, + unsigned int is_header, unsigned int host_write_ptr) +{ + struct phys_addr *pasync_sge = NULL; + + if (is_header) + pasync_sge = pasync_ctx->async_header.ring_base; + else + pasync_sge = pasync_ctx->async_data.ring_base; + + return pasync_sge + host_write_ptr; +} + +static void hwi_post_async_buffers(struct beiscsi_hba *phba, + unsigned int is_header) +{ + struct hwi_controller *phwi_ctrlr; + struct hwi_async_pdu_context *pasync_ctx; + struct async_pdu_handle *pasync_handle; + struct list_head *pfree_link, *pbusy_list; + struct phys_addr *pasync_sge; + unsigned int ring_id, num_entries; + unsigned int host_write_num; + unsigned int writables; + unsigned int i = 0; + u32 doorbell = 0; + + phwi_ctrlr = phba->phwi_ctrlr; + pasync_ctx = HWI_GET_ASYNC_PDU_CTX(phwi_ctrlr); + + if (is_header) { + num_entries = pasync_ctx->async_header.num_entries; + writables = min(pasync_ctx->async_header.writables, + pasync_ctx->async_header.free_entries); + pfree_link = pasync_ctx->async_header.free_list.next; + host_write_num = pasync_ctx->async_header.host_write_ptr; + ring_id = phwi_ctrlr->default_pdu_hdr.id; + } else { + num_entries = pasync_ctx->async_data.num_entries; + writables = min(pasync_ctx->async_data.writables, + pasync_ctx->async_data.free_entries); + pfree_link = pasync_ctx->async_data.free_list.next; + host_write_num = pasync_ctx->async_data.host_write_ptr; + ring_id = phwi_ctrlr->default_pdu_data.id; + } + + writables = (writables / 8) * 8; + if (writables) { + for (i = 0; i < writables; i++) { + pbusy_list = + hwi_get_async_busy_list(pasync_ctx, is_header, + host_write_num); + pasync_handle = + list_entry(pfree_link, struct async_pdu_handle, + link); + WARN_ON(!pasync_handle); + pasync_handle->consumed = 0; + + pfree_link = pfree_link->next; + + pasync_sge = hwi_get_ring_address(pasync_ctx, + is_header, host_write_num); + + pasync_sge->hi = pasync_handle->pa.u.a32.address_lo; + pasync_sge->lo = pasync_handle->pa.u.a32.address_hi; + + list_move(&pasync_handle->link, pbusy_list); + + host_write_num++; + host_write_num = host_write_num % num_entries; + } + + if (is_header) { + pasync_ctx->async_header.host_write_ptr = + host_write_num; + pasync_ctx->async_header.free_entries -= writables; + pasync_ctx->async_header.writables -= writables; + pasync_ctx->async_header.busy_entries += writables; + } else { + pasync_ctx->async_data.host_write_ptr = host_write_num; + pasync_ctx->async_data.free_entries -= writables; + pasync_ctx->async_data.writables -= writables; + pasync_ctx->async_data.busy_entries += writables; + } + + doorbell |= ring_id & DB_DEF_PDU_RING_ID_MASK; + doorbell |= 1 << DB_DEF_PDU_REARM_SHIFT; + doorbell |= 0 << DB_DEF_PDU_EVENT_SHIFT; + doorbell |= (writables & DB_DEF_PDU_CQPROC_MASK) + << DB_DEF_PDU_CQPROC_SHIFT; + + iowrite32(doorbell, phba->db_va + DB_RXULP0_OFFSET); + } +} + +static void hwi_flush_default_pdu_buffer(struct beiscsi_hba *phba, + struct beiscsi_conn *beiscsi_conn, + struct i_t_dpdu_cqe *pdpdu_cqe) +{ + struct hwi_controller *phwi_ctrlr; + struct hwi_async_pdu_context *pasync_ctx; + struct async_pdu_handle *pasync_handle = NULL; + unsigned int cq_index = -1; + + phwi_ctrlr = phba->phwi_ctrlr; + pasync_ctx = HWI_GET_ASYNC_PDU_CTX(phwi_ctrlr); + + pasync_handle = hwi_get_async_handle(phba, beiscsi_conn, pasync_ctx, + pdpdu_cqe, &cq_index); + BUG_ON(pasync_handle->is_header != 0); + if (pasync_handle->consumed == 0) + hwi_update_async_writables(pasync_ctx, pasync_handle->is_header, + cq_index); + + hwi_free_async_msg(phba, pasync_handle->cri); + hwi_post_async_buffers(phba, pasync_handle->is_header); +} + +static unsigned int +hwi_fwd_async_msg(struct beiscsi_conn *beiscsi_conn, + struct beiscsi_hba *phba, + struct hwi_async_pdu_context *pasync_ctx, unsigned short cri) +{ + struct list_head *plist; + struct async_pdu_handle *pasync_handle; + void *phdr = NULL; + unsigned int hdr_len = 0, buf_len = 0; + unsigned int status, index = 0, offset = 0; + void *pfirst_buffer = NULL; + unsigned int num_buf = 0; + + plist = &pasync_ctx->async_entry[cri].wait_queue.list; + + list_for_each_entry(pasync_handle, plist, link) { + if (index == 0) { + phdr = pasync_handle->pbuffer; + hdr_len = pasync_handle->buffer_len; + } else { + buf_len = pasync_handle->buffer_len; + if (!num_buf) { + pfirst_buffer = pasync_handle->pbuffer; + num_buf++; + } + memcpy(pfirst_buffer + offset, + pasync_handle->pbuffer, buf_len); + offset = buf_len; + } + index++; + } + + status = beiscsi_process_async_pdu(beiscsi_conn, phba, + beiscsi_conn->beiscsi_conn_cid, + phdr, hdr_len, pfirst_buffer, + buf_len); + + if (status == 0) + hwi_free_async_msg(phba, cri); + return 0; +} + +static unsigned int +hwi_gather_async_pdu(struct beiscsi_conn *beiscsi_conn, + struct beiscsi_hba *phba, + struct async_pdu_handle *pasync_handle) +{ + struct hwi_async_pdu_context *pasync_ctx; + struct hwi_controller *phwi_ctrlr; + unsigned int bytes_needed = 0, status = 0; + unsigned short cri = pasync_handle->cri; + struct pdu_base *ppdu; + + phwi_ctrlr = phba->phwi_ctrlr; + pasync_ctx = HWI_GET_ASYNC_PDU_CTX(phwi_ctrlr); + + list_del(&pasync_handle->link); + if (pasync_handle->is_header) { + pasync_ctx->async_header.busy_entries--; + if (pasync_ctx->async_entry[cri].wait_queue.hdr_received) { + hwi_free_async_msg(phba, cri); + BUG(); + } + + pasync_ctx->async_entry[cri].wait_queue.bytes_received = 0; + pasync_ctx->async_entry[cri].wait_queue.hdr_received = 1; + pasync_ctx->async_entry[cri].wait_queue.hdr_len = + (unsigned short)pasync_handle->buffer_len; + list_add_tail(&pasync_handle->link, + &pasync_ctx->async_entry[cri].wait_queue.list); + + ppdu = pasync_handle->pbuffer; + bytes_needed = ((((ppdu->dw[offsetof(struct amap_pdu_base, + data_len_hi) / 32] & PDUBASE_DATALENHI_MASK) << 8) & + 0xFFFF0000) | ((be16_to_cpu((ppdu-> + dw[offsetof(struct amap_pdu_base, data_len_lo) / 32] + & PDUBASE_DATALENLO_MASK) >> 16)) & 0x0000FFFF)); + + if (status == 0) { + pasync_ctx->async_entry[cri].wait_queue.bytes_needed = + bytes_needed; + + if (bytes_needed == 0) + status = hwi_fwd_async_msg(beiscsi_conn, phba, + pasync_ctx, cri); + } + } else { + pasync_ctx->async_data.busy_entries--; + if (pasync_ctx->async_entry[cri].wait_queue.hdr_received) { + list_add_tail(&pasync_handle->link, + &pasync_ctx->async_entry[cri].wait_queue. + list); + pasync_ctx->async_entry[cri].wait_queue. + bytes_received += + (unsigned short)pasync_handle->buffer_len; + + if (pasync_ctx->async_entry[cri].wait_queue. + bytes_received >= + pasync_ctx->async_entry[cri].wait_queue. + bytes_needed) + status = hwi_fwd_async_msg(beiscsi_conn, phba, + pasync_ctx, cri); + } + } + return status; +} + +static void hwi_process_default_pdu_ring(struct beiscsi_conn *beiscsi_conn, + struct beiscsi_hba *phba, + struct i_t_dpdu_cqe *pdpdu_cqe) +{ + struct hwi_controller *phwi_ctrlr; + struct hwi_async_pdu_context *pasync_ctx; + struct async_pdu_handle *pasync_handle = NULL; + unsigned int cq_index = -1; + + phwi_ctrlr = phba->phwi_ctrlr; + pasync_ctx = HWI_GET_ASYNC_PDU_CTX(phwi_ctrlr); + pasync_handle = hwi_get_async_handle(phba, beiscsi_conn, pasync_ctx, + pdpdu_cqe, &cq_index); + + if (pasync_handle->consumed == 0) + hwi_update_async_writables(pasync_ctx, pasync_handle->is_header, + cq_index); + hwi_gather_async_pdu(beiscsi_conn, phba, pasync_handle); + hwi_post_async_buffers(phba, pasync_handle->is_header); +} + +static unsigned int beiscsi_process_cq(struct beiscsi_hba *phba) +{ + struct hwi_controller *phwi_ctrlr; + struct hwi_context_memory *phwi_context; + struct be_queue_info *cq; + struct sol_cqe *sol; + struct dmsg_cqe *dmsg; + unsigned int num_processed = 0; + unsigned int tot_nump = 0; + struct beiscsi_conn *beiscsi_conn; + + phwi_ctrlr = phba->phwi_ctrlr; + phwi_context = phwi_ctrlr->phwi_ctxt; + cq = &phwi_context->be_cq; + sol = queue_tail_node(cq); + + while (sol->dw[offsetof(struct amap_sol_cqe, valid) / 32] & + CQE_VALID_MASK) { + be_dws_le_to_cpu(sol, sizeof(struct sol_cqe)); + + beiscsi_conn = phba->conn_table[(u32) (sol-> + dw[offsetof(struct amap_sol_cqe, cid) / 32] & + SOL_CID_MASK) >> 6]; + + if (!beiscsi_conn || !beiscsi_conn->ep) { + shost_printk(KERN_WARNING, phba->shost, + "Connection table empty for cid = %d\n", + (u32)(sol->dw[offsetof(struct amap_sol_cqe, + cid) / 32] & SOL_CID_MASK) >> 6); + return 0; + } + + if (num_processed >= 32) { + hwi_ring_cq_db(phba, phwi_context->be_cq.id, + num_processed, 0, 0); + tot_nump += num_processed; + num_processed = 0; + } + + switch ((u32) sol->dw[offsetof(struct amap_sol_cqe, code) / + 32] & CQE_CODE_MASK) { + case SOL_CMD_COMPLETE: + hwi_complete_cmd(beiscsi_conn, phba, sol); + break; + case DRIVERMSG_NOTIFY: + SE_DEBUG(DBG_LVL_8, "Received DRIVERMSG_NOTIFY \n"); + dmsg = (struct dmsg_cqe *)sol; + hwi_complete_drvr_msgs(beiscsi_conn, phba, sol); + break; + case UNSOL_HDR_NOTIFY: + case UNSOL_DATA_NOTIFY: + SE_DEBUG(DBG_LVL_8, "Received UNSOL_HDR/DATA_NOTIFY\n"); + hwi_process_default_pdu_ring(beiscsi_conn, phba, + (struct i_t_dpdu_cqe *)sol); + break; + case CXN_INVALIDATE_INDEX_NOTIFY: + case CMD_INVALIDATED_NOTIFY: + case CXN_INVALIDATE_NOTIFY: + SE_DEBUG(DBG_LVL_1, + "Ignoring CQ Error notification for cmd/cxn" + "invalidate\n"); + break; + case SOL_CMD_KILLED_DATA_DIGEST_ERR: + case CMD_KILLED_INVALID_STATSN_RCVD: + case CMD_KILLED_INVALID_R2T_RCVD: + case CMD_CXN_KILLED_LUN_INVALID: + case CMD_CXN_KILLED_ICD_INVALID: + case CMD_CXN_KILLED_ITT_INVALID: + case CMD_CXN_KILLED_SEQ_OUTOFORDER: + case CMD_CXN_KILLED_INVALID_DATASN_RCVD: + SE_DEBUG(DBG_LVL_1, + "CQ Error notification for cmd.. " + "code %d cid 0x%x\n", + sol->dw[offsetof(struct amap_sol_cqe, code) / + 32] & CQE_CODE_MASK, + (sol->dw[offsetof(struct amap_sol_cqe, cid) / + 32] & SOL_CID_MASK)); + break; + case UNSOL_DATA_DIGEST_ERROR_NOTIFY: + SE_DEBUG(DBG_LVL_1, + "Digest error on def pdu ring, dropping..\n"); + hwi_flush_default_pdu_buffer(phba, beiscsi_conn, + (struct i_t_dpdu_cqe *) sol); + break; + case CXN_KILLED_PDU_SIZE_EXCEEDS_DSL: + case CXN_KILLED_BURST_LEN_MISMATCH: + case CXN_KILLED_AHS_RCVD: + case CXN_KILLED_HDR_DIGEST_ERR: + case CXN_KILLED_UNKNOWN_HDR: + case CXN_KILLED_STALE_ITT_TTT_RCVD: + case CXN_KILLED_INVALID_ITT_TTT_RCVD: + case CXN_KILLED_TIMED_OUT: + case CXN_KILLED_FIN_RCVD: + case CXN_KILLED_BAD_UNSOL_PDU_RCVD: + case CXN_KILLED_BAD_WRB_INDEX_ERROR: + case CXN_KILLED_OVER_RUN_RESIDUAL: + case CXN_KILLED_UNDER_RUN_RESIDUAL: + case CXN_KILLED_CMND_DATA_NOT_ON_SAME_CONN: + SE_DEBUG(DBG_LVL_1, "CQ Error %d, resetting CID " + "0x%x...\n", + sol->dw[offsetof(struct amap_sol_cqe, code) / + 32] & CQE_CODE_MASK, + sol->dw[offsetof(struct amap_sol_cqe, cid) / + 32] & CQE_CID_MASK); + iscsi_conn_failure(beiscsi_conn->conn, + ISCSI_ERR_CONN_FAILED); + break; + case CXN_KILLED_RST_SENT: + case CXN_KILLED_RST_RCVD: + SE_DEBUG(DBG_LVL_1, "CQ Error %d, reset received/sent " + "on CID 0x%x...\n", + sol->dw[offsetof(struct amap_sol_cqe, code) / + 32] & CQE_CODE_MASK, + sol->dw[offsetof(struct amap_sol_cqe, cid) / + 32] & CQE_CID_MASK); + iscsi_conn_failure(beiscsi_conn->conn, + ISCSI_ERR_CONN_FAILED); + break; + default: + SE_DEBUG(DBG_LVL_1, "CQ Error Invalid code= %d " + "received on CID 0x%x...\n", + sol->dw[offsetof(struct amap_sol_cqe, code) / + 32] & CQE_CODE_MASK, + sol->dw[offsetof(struct amap_sol_cqe, cid) / + 32] & CQE_CID_MASK); + break; + } + + AMAP_SET_BITS(struct amap_sol_cqe, valid, sol, 0); + queue_tail_inc(cq); + sol = queue_tail_node(cq); + num_processed++; + } + + if (num_processed > 0) { + tot_nump += num_processed; + hwi_ring_cq_db(phba, phwi_context->be_cq.id, num_processed, + 1, 0); + } + return tot_nump; +} + +static void beiscsi_process_all_cqs(struct work_struct *work) +{ + unsigned long flags; + struct beiscsi_hba *phba = + container_of(work, struct beiscsi_hba, work_cqs); + + if (phba->todo_mcc_cq) { + spin_lock_irqsave(&phba->isr_lock, flags); + phba->todo_mcc_cq = 0; + spin_unlock_irqrestore(&phba->isr_lock, flags); + SE_DEBUG(DBG_LVL_1, "MCC Interrupt Not expected \n"); + } + + if (phba->todo_cq) { + spin_lock_irqsave(&phba->isr_lock, flags); + phba->todo_cq = 0; + spin_unlock_irqrestore(&phba->isr_lock, flags); + beiscsi_process_cq(phba); + } +} + +static int be_iopoll(struct blk_iopoll *iop, int budget) +{ + static unsigned int ret; + struct beiscsi_hba *phba; + + phba = container_of(iop, struct beiscsi_hba, iopoll); + + ret = beiscsi_process_cq(phba); + if (ret < budget) { + struct hwi_controller *phwi_ctrlr; + struct hwi_context_memory *phwi_context; + + phwi_ctrlr = phba->phwi_ctrlr; + phwi_context = phwi_ctrlr->phwi_ctxt; + blk_iopoll_complete(iop); + hwi_ring_eq_db(phba, phwi_context->be_eq.q.id, 0, + 0, 1, 1); + } + return ret; +} + +static void +hwi_write_sgl(struct iscsi_wrb *pwrb, struct scatterlist *sg, + unsigned int num_sg, struct beiscsi_io_task *io_task) +{ + struct iscsi_sge *psgl; + unsigned short sg_len, index; + unsigned int sge_len = 0; + unsigned long long addr; + struct scatterlist *l_sg; + unsigned int offset; + + AMAP_SET_BITS(struct amap_iscsi_wrb, iscsi_bhs_addr_lo, pwrb, + io_task->bhs_pa.u.a32.address_lo); + AMAP_SET_BITS(struct amap_iscsi_wrb, iscsi_bhs_addr_hi, pwrb, + io_task->bhs_pa.u.a32.address_hi); + + l_sg = sg; + for (index = 0; (index < num_sg) && (index < 2); index++, sg_next(sg)) { + if (index == 0) { + sg_len = sg_dma_len(sg); + addr = (u64) sg_dma_address(sg); + AMAP_SET_BITS(struct amap_iscsi_wrb, sge0_addr_lo, pwrb, + (addr & 0xFFFFFFFF)); + AMAP_SET_BITS(struct amap_iscsi_wrb, sge0_addr_hi, pwrb, + (addr >> 32)); + AMAP_SET_BITS(struct amap_iscsi_wrb, sge0_len, pwrb, + sg_len); + sge_len = sg_len; + AMAP_SET_BITS(struct amap_iscsi_wrb, sge0_last, pwrb, + 1); + } else { + AMAP_SET_BITS(struct amap_iscsi_wrb, sge0_last, pwrb, + 0); + AMAP_SET_BITS(struct amap_iscsi_wrb, sge1_r2t_offset, + pwrb, sge_len); + sg_len = sg_dma_len(sg); + addr = (u64) sg_dma_address(sg); + AMAP_SET_BITS(struct amap_iscsi_wrb, sge1_addr_lo, pwrb, + (addr & 0xFFFFFFFF)); + AMAP_SET_BITS(struct amap_iscsi_wrb, sge1_addr_hi, pwrb, + (addr >> 32)); + AMAP_SET_BITS(struct amap_iscsi_wrb, sge1_len, pwrb, + sg_len); + } + } + psgl = (struct iscsi_sge *)io_task->psgl_handle->pfrag; + memset(psgl, 0, sizeof(*psgl) * BE2_SGE); + + AMAP_SET_BITS(struct amap_iscsi_sge, len, psgl, io_task->bhs_len - 2); + + AMAP_SET_BITS(struct amap_iscsi_sge, addr_hi, psgl, + io_task->bhs_pa.u.a32.address_hi); + AMAP_SET_BITS(struct amap_iscsi_sge, addr_lo, psgl, + io_task->bhs_pa.u.a32.address_lo); + + if (num_sg == 2) + AMAP_SET_BITS(struct amap_iscsi_wrb, sge1_last, pwrb, 1); + sg = l_sg; + psgl++; + psgl++; + offset = 0; + for (index = 0; index < num_sg; index++, sg_next(sg), psgl++) { + sg_len = sg_dma_len(sg); + addr = (u64) sg_dma_address(sg); + AMAP_SET_BITS(struct amap_iscsi_sge, addr_lo, psgl, + (addr & 0xFFFFFFFF)); + AMAP_SET_BITS(struct amap_iscsi_sge, addr_hi, psgl, + (addr >> 32)); + AMAP_SET_BITS(struct amap_iscsi_sge, len, psgl, sg_len); + AMAP_SET_BITS(struct amap_iscsi_sge, sge_offset, psgl, offset); + AMAP_SET_BITS(struct amap_iscsi_sge, last_sge, psgl, 0); + offset += sg_len; + } + psgl--; + AMAP_SET_BITS(struct amap_iscsi_sge, last_sge, psgl, 1); +} + +static void hwi_write_buffer(struct iscsi_wrb *pwrb, struct iscsi_task *task) +{ + struct iscsi_sge *psgl; + unsigned long long addr; + struct beiscsi_io_task *io_task = task->dd_data; + struct beiscsi_conn *beiscsi_conn = io_task->conn; + struct beiscsi_hba *phba = beiscsi_conn->phba; + + io_task->bhs_len = sizeof(struct be_nonio_bhs) - 2; + AMAP_SET_BITS(struct amap_iscsi_wrb, iscsi_bhs_addr_lo, pwrb, + io_task->bhs_pa.u.a32.address_lo); + AMAP_SET_BITS(struct amap_iscsi_wrb, iscsi_bhs_addr_hi, pwrb, + io_task->bhs_pa.u.a32.address_hi); + + if (task->data) { + if (task->data_count) { + AMAP_SET_BITS(struct amap_iscsi_wrb, dsp, pwrb, 1); + addr = (u64) pci_map_single(phba->pcidev, + task->data, + task->data_count, 1); + } else { + AMAP_SET_BITS(struct amap_iscsi_wrb, dsp, pwrb, 0); + addr = 0; + } + AMAP_SET_BITS(struct amap_iscsi_wrb, sge0_addr_lo, pwrb, + (addr & 0xFFFFFFFF)); + AMAP_SET_BITS(struct amap_iscsi_wrb, sge0_addr_hi, pwrb, + (addr >> 32)); + AMAP_SET_BITS(struct amap_iscsi_wrb, sge0_len, pwrb, + task->data_count); + + AMAP_SET_BITS(struct amap_iscsi_wrb, sge0_last, pwrb, 1); + } else { + AMAP_SET_BITS(struct amap_iscsi_wrb, dsp, pwrb, 0); + addr = 0; + } + + psgl = (struct iscsi_sge *)io_task->psgl_handle->pfrag; + + AMAP_SET_BITS(struct amap_iscsi_sge, len, psgl, io_task->bhs_len); + + AMAP_SET_BITS(struct amap_iscsi_sge, addr_hi, psgl, + io_task->bhs_pa.u.a32.address_hi); + AMAP_SET_BITS(struct amap_iscsi_sge, addr_lo, psgl, + io_task->bhs_pa.u.a32.address_lo); + if (task->data) { + psgl++; + AMAP_SET_BITS(struct amap_iscsi_sge, addr_hi, psgl, 0); + AMAP_SET_BITS(struct amap_iscsi_sge, addr_lo, psgl, 0); + AMAP_SET_BITS(struct amap_iscsi_sge, len, psgl, 0); + AMAP_SET_BITS(struct amap_iscsi_sge, sge_offset, psgl, 0); + AMAP_SET_BITS(struct amap_iscsi_sge, rsvd0, psgl, 0); + AMAP_SET_BITS(struct amap_iscsi_sge, last_sge, psgl, 0); + + psgl++; + if (task->data) { + AMAP_SET_BITS(struct amap_iscsi_sge, addr_lo, psgl, + (addr & 0xFFFFFFFF)); + AMAP_SET_BITS(struct amap_iscsi_sge, addr_hi, psgl, + (addr >> 32)); + } + AMAP_SET_BITS(struct amap_iscsi_sge, len, psgl, 0x106); + } + AMAP_SET_BITS(struct amap_iscsi_sge, last_sge, psgl, 1); +} + +static void beiscsi_find_mem_req(struct beiscsi_hba *phba) +{ + unsigned int num_cq_pages, num_eq_pages, num_async_pdu_buf_pages; + unsigned int num_async_pdu_data_pages, wrb_sz_per_cxn; + unsigned int num_async_pdu_buf_sgl_pages, num_async_pdu_data_sgl_pages; + + num_cq_pages = PAGES_REQUIRED(phba->params.num_cq_entries * \ + sizeof(struct sol_cqe)); + num_eq_pages = PAGES_REQUIRED(phba->params.num_eq_entries * \ + sizeof(struct be_eq_entry)); + num_async_pdu_buf_pages = + PAGES_REQUIRED(phba->params.asyncpdus_per_ctrl * \ + phba->params.defpdu_hdr_sz); + num_async_pdu_buf_sgl_pages = + PAGES_REQUIRED(phba->params.asyncpdus_per_ctrl * \ + sizeof(struct phys_addr)); + num_async_pdu_data_pages = + PAGES_REQUIRED(phba->params.asyncpdus_per_ctrl * \ + phba->params.defpdu_data_sz); + num_async_pdu_data_sgl_pages = + PAGES_REQUIRED(phba->params.asyncpdus_per_ctrl * \ + sizeof(struct phys_addr)); + + phba->params.hwi_ws_sz = sizeof(struct hwi_controller); + + phba->mem_req[ISCSI_MEM_GLOBAL_HEADER] = 2 * + BE_ISCSI_PDU_HEADER_SIZE; + phba->mem_req[HWI_MEM_ADDN_CONTEXT] = + sizeof(struct hwi_context_memory); + + phba->mem_req[HWI_MEM_CQ] = num_cq_pages * PAGE_SIZE; + phba->mem_req[HWI_MEM_EQ] = num_eq_pages * PAGE_SIZE; + + phba->mem_req[HWI_MEM_WRB] = sizeof(struct iscsi_wrb) + * (phba->params.wrbs_per_cxn) + * phba->params.cxns_per_ctrl; + wrb_sz_per_cxn = sizeof(struct wrb_handle) * + (phba->params.wrbs_per_cxn); + phba->mem_req[HWI_MEM_WRBH] = roundup_pow_of_two((wrb_sz_per_cxn) * + phba->params.cxns_per_ctrl); + + phba->mem_req[HWI_MEM_SGLH] = sizeof(struct sgl_handle) * + phba->params.icds_per_ctrl; + phba->mem_req[HWI_MEM_SGE] = sizeof(struct iscsi_sge) * + phba->params.num_sge_per_io * phba->params.icds_per_ctrl; + + phba->mem_req[HWI_MEM_ASYNC_HEADER_BUF] = + num_async_pdu_buf_pages * PAGE_SIZE; + phba->mem_req[HWI_MEM_ASYNC_DATA_BUF] = + num_async_pdu_data_pages * PAGE_SIZE; + phba->mem_req[HWI_MEM_ASYNC_HEADER_RING] = + num_async_pdu_buf_sgl_pages * PAGE_SIZE; + phba->mem_req[HWI_MEM_ASYNC_DATA_RING] = + num_async_pdu_data_sgl_pages * PAGE_SIZE; + phba->mem_req[HWI_MEM_ASYNC_HEADER_HANDLE] = + phba->params.asyncpdus_per_ctrl * + sizeof(struct async_pdu_handle); + phba->mem_req[HWI_MEM_ASYNC_DATA_HANDLE] = + phba->params.asyncpdus_per_ctrl * + sizeof(struct async_pdu_handle); + phba->mem_req[HWI_MEM_ASYNC_PDU_CONTEXT] = + sizeof(struct hwi_async_pdu_context) + + (phba->params.cxns_per_ctrl * sizeof(struct hwi_async_entry)); +} + +static int beiscsi_alloc_mem(struct beiscsi_hba *phba) +{ + struct be_mem_descriptor *mem_descr; + dma_addr_t bus_add; + struct mem_array *mem_arr, *mem_arr_orig; + unsigned int i, j, alloc_size, curr_alloc_size; + + phba->phwi_ctrlr = kmalloc(phba->params.hwi_ws_sz, GFP_KERNEL); + if (!phba->phwi_ctrlr) + return -ENOMEM; + + phba->init_mem = kcalloc(SE_MEM_MAX, sizeof(*mem_descr), + GFP_KERNEL); + if (!phba->init_mem) { + kfree(phba->phwi_ctrlr); + return -ENOMEM; + } + + mem_arr_orig = kmalloc(sizeof(*mem_arr_orig) * BEISCSI_MAX_FRAGS_INIT, + GFP_KERNEL); + if (!mem_arr_orig) { + kfree(phba->init_mem); + kfree(phba->phwi_ctrlr); + return -ENOMEM; + } + + mem_descr = phba->init_mem; + for (i = 0; i < SE_MEM_MAX; i++) { + j = 0; + mem_arr = mem_arr_orig; + alloc_size = phba->mem_req[i]; + memset(mem_arr, 0, sizeof(struct mem_array) * + BEISCSI_MAX_FRAGS_INIT); + curr_alloc_size = min(be_max_phys_size * 1024, alloc_size); + do { + mem_arr->virtual_address = pci_alloc_consistent( + phba->pcidev, + curr_alloc_size, + &bus_add); + if (!mem_arr->virtual_address) { + if (curr_alloc_size <= BE_MIN_MEM_SIZE) + goto free_mem; + if (curr_alloc_size - + rounddown_pow_of_two(curr_alloc_size)) + curr_alloc_size = rounddown_pow_of_two + (curr_alloc_size); + else + curr_alloc_size = curr_alloc_size / 2; + } else { + mem_arr->bus_address.u. + a64.address = (__u64) bus_add; + mem_arr->size = curr_alloc_size; + alloc_size -= curr_alloc_size; + curr_alloc_size = min(be_max_phys_size * + 1024, alloc_size); + j++; + mem_arr++; + } + } while (alloc_size); + mem_descr->num_elements = j; + mem_descr->size_in_bytes = phba->mem_req[i]; + mem_descr->mem_array = kmalloc(sizeof(*mem_arr) * j, + GFP_KERNEL); + if (!mem_descr->mem_array) + goto free_mem; + + memcpy(mem_descr->mem_array, mem_arr_orig, + sizeof(struct mem_array) * j); + mem_descr++; + } + kfree(mem_arr_orig); + return 0; +free_mem: + mem_descr->num_elements = j; + while ((i) || (j)) { + for (j = mem_descr->num_elements; j > 0; j--) { + pci_free_consistent(phba->pcidev, + mem_descr->mem_array[j - 1].size, + mem_descr->mem_array[j - 1]. + virtual_address, + mem_descr->mem_array[j - 1]. + bus_address.u.a64.address); + } + if (i) { + i--; + kfree(mem_descr->mem_array); + mem_descr--; + } + } + kfree(mem_arr_orig); + kfree(phba->init_mem); + kfree(phba->phwi_ctrlr); + return -ENOMEM; +} + +static int beiscsi_get_memory(struct beiscsi_hba *phba) +{ + beiscsi_find_mem_req(phba); + return beiscsi_alloc_mem(phba); +} + +static void iscsi_init_global_templates(struct beiscsi_hba *phba) +{ + struct pdu_data_out *pdata_out; + struct pdu_nop_out *pnop_out; + struct be_mem_descriptor *mem_descr; + + mem_descr = phba->init_mem; + mem_descr += ISCSI_MEM_GLOBAL_HEADER; + pdata_out = + (struct pdu_data_out *)mem_descr->mem_array[0].virtual_address; + memset(pdata_out, 0, BE_ISCSI_PDU_HEADER_SIZE); + + AMAP_SET_BITS(struct amap_pdu_data_out, opcode, pdata_out, + IIOC_SCSI_DATA); + + pnop_out = + (struct pdu_nop_out *)((unsigned char *)mem_descr->mem_array[0]. + virtual_address + BE_ISCSI_PDU_HEADER_SIZE); + + memset(pnop_out, 0, BE_ISCSI_PDU_HEADER_SIZE); + AMAP_SET_BITS(struct amap_pdu_nop_out, ttt, pnop_out, 0xFFFFFFFF); + AMAP_SET_BITS(struct amap_pdu_nop_out, f_bit, pnop_out, 1); + AMAP_SET_BITS(struct amap_pdu_nop_out, i_bit, pnop_out, 0); +} + +static void beiscsi_init_wrb_handle(struct beiscsi_hba *phba) +{ + struct be_mem_descriptor *mem_descr_wrbh, *mem_descr_wrb; + struct wrb_handle *pwrb_handle; + struct hwi_controller *phwi_ctrlr; + struct hwi_wrb_context *pwrb_context; + struct iscsi_wrb *pwrb; + unsigned int num_cxn_wrbh; + unsigned int num_cxn_wrb, j, idx, index; + + mem_descr_wrbh = phba->init_mem; + mem_descr_wrbh += HWI_MEM_WRBH; + + mem_descr_wrb = phba->init_mem; + mem_descr_wrb += HWI_MEM_WRB; + + idx = 0; + pwrb_handle = mem_descr_wrbh->mem_array[idx].virtual_address; + num_cxn_wrbh = ((mem_descr_wrbh->mem_array[idx].size) / + ((sizeof(struct wrb_handle)) * + phba->params.wrbs_per_cxn)); + phwi_ctrlr = phba->phwi_ctrlr; + + for (index = 0; index < phba->params.cxns_per_ctrl * 2; index += 2) { + pwrb_context = &phwi_ctrlr->wrb_context[index]; + SE_DEBUG(DBG_LVL_8, "cid=%d pwrb_context=%p \n", index, + pwrb_context); + pwrb_context->pwrb_handle_base = + kzalloc(sizeof(struct wrb_handle *) * + phba->params.wrbs_per_cxn, GFP_KERNEL); + pwrb_context->pwrb_handle_basestd = + kzalloc(sizeof(struct wrb_handle *) * + phba->params.wrbs_per_cxn, GFP_KERNEL); + if (num_cxn_wrbh) { + pwrb_context->alloc_index = 0; + pwrb_context->wrb_handles_available = 0; + for (j = 0; j < phba->params.wrbs_per_cxn; j++) { + pwrb_context->pwrb_handle_base[j] = pwrb_handle; + pwrb_context->pwrb_handle_basestd[j] = + pwrb_handle; + pwrb_context->wrb_handles_available++; + pwrb_handle++; + } + pwrb_context->free_index = 0; + num_cxn_wrbh--; + } else { + idx++; + pwrb_handle = + mem_descr_wrbh->mem_array[idx].virtual_address; + num_cxn_wrbh = + ((mem_descr_wrbh->mem_array[idx].size) / + ((sizeof(struct wrb_handle)) * + phba->params.wrbs_per_cxn)); + pwrb_context->alloc_index = 0; + for (j = 0; j < phba->params.wrbs_per_cxn; j++) { + pwrb_context->pwrb_handle_base[j] = pwrb_handle; + pwrb_context->pwrb_handle_basestd[j] = + pwrb_handle; + pwrb_context->wrb_handles_available++; + pwrb_handle++; + } + pwrb_context->free_index = 0; + num_cxn_wrbh--; + } + } + idx = 0; + pwrb = mem_descr_wrb->mem_array[idx].virtual_address; + num_cxn_wrb = + ((mem_descr_wrb->mem_array[idx].size) / (sizeof(struct iscsi_wrb)) * + phba->params.wrbs_per_cxn); + + for (index = 0; index < phba->params.cxns_per_ctrl; index += 2) { + pwrb_context = &phwi_ctrlr->wrb_context[index]; + if (num_cxn_wrb) { + for (j = 0; j < phba->params.wrbs_per_cxn; j++) { + pwrb_handle = pwrb_context->pwrb_handle_base[j]; + pwrb_handle->pwrb = pwrb; + pwrb++; + } + num_cxn_wrb--; + } else { + idx++; + pwrb = mem_descr_wrb->mem_array[idx].virtual_address; + num_cxn_wrb = ((mem_descr_wrb->mem_array[idx].size) / + (sizeof(struct iscsi_wrb)) * + phba->params.wrbs_per_cxn); + for (j = 0; j < phba->params.wrbs_per_cxn; j++) { + pwrb_handle = pwrb_context->pwrb_handle_base[j]; + pwrb_handle->pwrb = pwrb; + pwrb++; + } + num_cxn_wrb--; + } + } +} + +static void hwi_init_async_pdu_ctx(struct beiscsi_hba *phba) +{ + struct hwi_controller *phwi_ctrlr; + struct hba_parameters *p = &phba->params; + struct hwi_async_pdu_context *pasync_ctx; + struct async_pdu_handle *pasync_header_h, *pasync_data_h; + unsigned int index; + struct be_mem_descriptor *mem_descr; + + mem_descr = (struct be_mem_descriptor *)phba->init_mem; + mem_descr += HWI_MEM_ASYNC_PDU_CONTEXT; + + phwi_ctrlr = phba->phwi_ctrlr; + phwi_ctrlr->phwi_ctxt->pasync_ctx = (struct hwi_async_pdu_context *) + mem_descr->mem_array[0].virtual_address; + pasync_ctx = phwi_ctrlr->phwi_ctxt->pasync_ctx; + memset(pasync_ctx, 0, sizeof(*pasync_ctx)); + + pasync_ctx->async_header.num_entries = p->asyncpdus_per_ctrl; + pasync_ctx->async_header.buffer_size = p->defpdu_hdr_sz; + pasync_ctx->async_data.buffer_size = p->defpdu_data_sz; + pasync_ctx->async_data.num_entries = p->asyncpdus_per_ctrl; + + mem_descr = (struct be_mem_descriptor *)phba->init_mem; + mem_descr += HWI_MEM_ASYNC_HEADER_BUF; + if (mem_descr->mem_array[0].virtual_address) { + SE_DEBUG(DBG_LVL_8, + "hwi_init_async_pdu_ctx HWI_MEM_ASYNC_HEADER_BUF" + "va=%p \n", mem_descr->mem_array[0].virtual_address); + } else + shost_printk(KERN_WARNING, phba->shost, + "No Virtual address \n"); + + pasync_ctx->async_header.va_base = + mem_descr->mem_array[0].virtual_address; + + pasync_ctx->async_header.pa_base.u.a64.address = + mem_descr->mem_array[0].bus_address.u.a64.address; + + mem_descr = (struct be_mem_descriptor *)phba->init_mem; + mem_descr += HWI_MEM_ASYNC_HEADER_RING; + if (mem_descr->mem_array[0].virtual_address) { + SE_DEBUG(DBG_LVL_8, + "hwi_init_async_pdu_ctx HWI_MEM_ASYNC_HEADER_RING" + "va=%p \n", mem_descr->mem_array[0].virtual_address); + } else + shost_printk(KERN_WARNING, phba->shost, + "No Virtual address \n"); + pasync_ctx->async_header.ring_base = + mem_descr->mem_array[0].virtual_address; + + mem_descr = (struct be_mem_descriptor *)phba->init_mem; + mem_descr += HWI_MEM_ASYNC_HEADER_HANDLE; + if (mem_descr->mem_array[0].virtual_address) { + SE_DEBUG(DBG_LVL_8, + "hwi_init_async_pdu_ctx HWI_MEM_ASYNC_HEADER_HANDLE" + "va=%p \n", mem_descr->mem_array[0].virtual_address); + } else + shost_printk(KERN_WARNING, phba->shost, + "No Virtual address \n"); + + pasync_ctx->async_header.handle_base = + mem_descr->mem_array[0].virtual_address; + pasync_ctx->async_header.writables = 0; + INIT_LIST_HEAD(&pasync_ctx->async_header.free_list); + + mem_descr = (struct be_mem_descriptor *)phba->init_mem; + mem_descr += HWI_MEM_ASYNC_DATA_BUF; + if (mem_descr->mem_array[0].virtual_address) { + SE_DEBUG(DBG_LVL_8, + "hwi_init_async_pdu_ctx HWI_MEM_ASYNC_DATA_BUF" + "va=%p \n", mem_descr->mem_array[0].virtual_address); + } else + shost_printk(KERN_WARNING, phba->shost, + "No Virtual address \n"); + pasync_ctx->async_data.va_base = + mem_descr->mem_array[0].virtual_address; + pasync_ctx->async_data.pa_base.u.a64.address = + mem_descr->mem_array[0].bus_address.u.a64.address; + + mem_descr = (struct be_mem_descriptor *)phba->init_mem; + mem_descr += HWI_MEM_ASYNC_DATA_RING; + if (mem_descr->mem_array[0].virtual_address) { + SE_DEBUG(DBG_LVL_8, + "hwi_init_async_pdu_ctx HWI_MEM_ASYNC_DATA_RING" + "va=%p \n", mem_descr->mem_array[0].virtual_address); + } else + shost_printk(KERN_WARNING, phba->shost, + "No Virtual address \n"); + + pasync_ctx->async_data.ring_base = + mem_descr->mem_array[0].virtual_address; + + mem_descr = (struct be_mem_descriptor *)phba->init_mem; + mem_descr += HWI_MEM_ASYNC_DATA_HANDLE; + if (!mem_descr->mem_array[0].virtual_address) + shost_printk(KERN_WARNING, phba->shost, + "No Virtual address \n"); + + pasync_ctx->async_data.handle_base = + mem_descr->mem_array[0].virtual_address; + pasync_ctx->async_data.writables = 0; + INIT_LIST_HEAD(&pasync_ctx->async_data.free_list); + + pasync_header_h = + (struct async_pdu_handle *)pasync_ctx->async_header.handle_base; + pasync_data_h = + (struct async_pdu_handle *)pasync_ctx->async_data.handle_base; + + for (index = 0; index < p->asyncpdus_per_ctrl; index++) { + pasync_header_h->cri = -1; + pasync_header_h->index = (char)index; + INIT_LIST_HEAD(&pasync_header_h->link); + pasync_header_h->pbuffer = + (void *)((unsigned long) + (pasync_ctx->async_header.va_base) + + (p->defpdu_hdr_sz * index)); + + pasync_header_h->pa.u.a64.address = + pasync_ctx->async_header.pa_base.u.a64.address + + (p->defpdu_hdr_sz * index); + + list_add_tail(&pasync_header_h->link, + &pasync_ctx->async_header.free_list); + pasync_header_h++; + pasync_ctx->async_header.free_entries++; + pasync_ctx->async_header.writables++; + + INIT_LIST_HEAD(&pasync_ctx->async_entry[index].wait_queue.list); + INIT_LIST_HEAD(&pasync_ctx->async_entry[index]. + header_busy_list); + pasync_data_h->cri = -1; + pasync_data_h->index = (char)index; + INIT_LIST_HEAD(&pasync_data_h->link); + pasync_data_h->pbuffer = + (void *)((unsigned long) + (pasync_ctx->async_data.va_base) + + (p->defpdu_data_sz * index)); + + pasync_data_h->pa.u.a64.address = + pasync_ctx->async_data.pa_base.u.a64.address + + (p->defpdu_data_sz * index); + + list_add_tail(&pasync_data_h->link, + &pasync_ctx->async_data.free_list); + pasync_data_h++; + pasync_ctx->async_data.free_entries++; + pasync_ctx->async_data.writables++; + + INIT_LIST_HEAD(&pasync_ctx->async_entry[index].data_busy_list); + } + + pasync_ctx->async_header.host_write_ptr = 0; + pasync_ctx->async_header.ep_read_ptr = -1; + pasync_ctx->async_data.host_write_ptr = 0; + pasync_ctx->async_data.ep_read_ptr = -1; +} + +static int +be_sgl_create_contiguous(void *virtual_address, + u64 physical_address, u32 length, + struct be_dma_mem *sgl) +{ + WARN_ON(!virtual_address); + WARN_ON(!physical_address); + WARN_ON(!length > 0); + WARN_ON(!sgl); + + sgl->va = virtual_address; + sgl->dma = physical_address; + sgl->size = length; + + return 0; +} + +static void be_sgl_destroy_contiguous(struct be_dma_mem *sgl) +{ + memset(sgl, 0, sizeof(*sgl)); +} + +static void +hwi_build_be_sgl_arr(struct beiscsi_hba *phba, + struct mem_array *pmem, struct be_dma_mem *sgl) +{ + if (sgl->va) + be_sgl_destroy_contiguous(sgl); + + be_sgl_create_contiguous(pmem->virtual_address, + pmem->bus_address.u.a64.address, + pmem->size, sgl); +} + +static void +hwi_build_be_sgl_by_offset(struct beiscsi_hba *phba, + struct mem_array *pmem, struct be_dma_mem *sgl) +{ + if (sgl->va) + be_sgl_destroy_contiguous(sgl); + + be_sgl_create_contiguous((unsigned char *)pmem->virtual_address, + pmem->bus_address.u.a64.address, + pmem->size, sgl); +} + +static int be_fill_queue(struct be_queue_info *q, + u16 len, u16 entry_size, void *vaddress) +{ + struct be_dma_mem *mem = &q->dma_mem; + + memset(q, 0, sizeof(*q)); + q->len = len; + q->entry_size = entry_size; + mem->size = len * entry_size; + mem->va = vaddress; + if (!mem->va) + return -ENOMEM; + memset(mem->va, 0, mem->size); + return 0; +} + +static int beiscsi_create_eq(struct beiscsi_hba *phba, + struct hwi_context_memory *phwi_context) +{ + unsigned int idx; + int ret; + struct be_queue_info *eq; + struct be_dma_mem *mem; + struct be_mem_descriptor *mem_descr; + void *eq_vaddress; + + idx = 0; + eq = &phwi_context->be_eq.q; + mem = &eq->dma_mem; + mem_descr = phba->init_mem; + mem_descr += HWI_MEM_EQ; + eq_vaddress = mem_descr->mem_array[idx].virtual_address; + + ret = be_fill_queue(eq, phba->params.num_eq_entries, + sizeof(struct be_eq_entry), eq_vaddress); + if (ret) { + shost_printk(KERN_ERR, phba->shost, + "be_fill_queue Failed for EQ \n"); + return ret; + } + + mem->dma = mem_descr->mem_array[idx].bus_address.u.a64.address; + + ret = beiscsi_cmd_eq_create(&phba->ctrl, eq, + phwi_context->be_eq.cur_eqd); + if (ret) { + shost_printk(KERN_ERR, phba->shost, "beiscsi_cmd_eq_create" + "Failedfor EQ \n"); + return ret; + } + SE_DEBUG(DBG_LVL_8, "eq id is %d\n", phwi_context->be_eq.q.id); + return 0; +} + +static int beiscsi_create_cq(struct beiscsi_hba *phba, + struct hwi_context_memory *phwi_context) +{ + unsigned int idx; + int ret; + struct be_queue_info *cq, *eq; + struct be_dma_mem *mem; + struct be_mem_descriptor *mem_descr; + void *cq_vaddress; + + idx = 0; + cq = &phwi_context->be_cq; + eq = &phwi_context->be_eq.q; + mem = &cq->dma_mem; + mem_descr = phba->init_mem; + mem_descr += HWI_MEM_CQ; + cq_vaddress = mem_descr->mem_array[idx].virtual_address; + ret = be_fill_queue(cq, phba->params.icds_per_ctrl / 2, + sizeof(struct sol_cqe), cq_vaddress); + if (ret) { + shost_printk(KERN_ERR, phba->shost, + "be_fill_queue Failed for ISCSI CQ \n"); + return ret; + } + + mem->dma = mem_descr->mem_array[idx].bus_address.u.a64.address; + ret = beiscsi_cmd_cq_create(&phba->ctrl, cq, eq, false, false, 0); + if (ret) { + shost_printk(KERN_ERR, phba->shost, + "beiscsi_cmd_eq_create Failed for ISCSI CQ \n"); + return ret; + } + SE_DEBUG(DBG_LVL_8, "iscsi cq id is %d\n", phwi_context->be_cq.id); + SE_DEBUG(DBG_LVL_8, "ISCSI CQ CREATED\n"); + return 0; +} + +static int +beiscsi_create_def_hdr(struct beiscsi_hba *phba, + struct hwi_context_memory *phwi_context, + struct hwi_controller *phwi_ctrlr, + unsigned int def_pdu_ring_sz) +{ + unsigned int idx; + int ret; + struct be_queue_info *dq, *cq; + struct be_dma_mem *mem; + struct be_mem_descriptor *mem_descr; + void *dq_vaddress; + + idx = 0; + dq = &phwi_context->be_def_hdrq; + cq = &phwi_context->be_cq; + mem = &dq->dma_mem; + mem_descr = phba->init_mem; + mem_descr += HWI_MEM_ASYNC_HEADER_RING; + dq_vaddress = mem_descr->mem_array[idx].virtual_address; + ret = be_fill_queue(dq, mem_descr->mem_array[0].size / + sizeof(struct phys_addr), + sizeof(struct phys_addr), dq_vaddress); + if (ret) { + shost_printk(KERN_ERR, phba->shost, + "be_fill_queue Failed for DEF PDU HDR\n"); + return ret; + } + mem->dma = mem_descr->mem_array[idx].bus_address.u.a64.address; + ret = be_cmd_create_default_pdu_queue(&phba->ctrl, cq, dq, + def_pdu_ring_sz, + phba->params.defpdu_hdr_sz); + if (ret) { + shost_printk(KERN_ERR, phba->shost, + "be_cmd_create_default_pdu_queue Failed DEFHDR\n"); + return ret; + } + phwi_ctrlr->default_pdu_hdr.id = phwi_context->be_def_hdrq.id; + SE_DEBUG(DBG_LVL_8, "iscsi def pdu id is %d\n", + phwi_context->be_def_hdrq.id); + hwi_post_async_buffers(phba, 1); + return 0; +} + +static int +beiscsi_create_def_data(struct beiscsi_hba *phba, + struct hwi_context_memory *phwi_context, + struct hwi_controller *phwi_ctrlr, + unsigned int def_pdu_ring_sz) +{ + unsigned int idx; + int ret; + struct be_queue_info *dataq, *cq; + struct be_dma_mem *mem; + struct be_mem_descriptor *mem_descr; + void *dq_vaddress; + + idx = 0; + dataq = &phwi_context->be_def_dataq; + cq = &phwi_context->be_cq; + mem = &dataq->dma_mem; + mem_descr = phba->init_mem; + mem_descr += HWI_MEM_ASYNC_DATA_RING; + dq_vaddress = mem_descr->mem_array[idx].virtual_address; + ret = be_fill_queue(dataq, mem_descr->mem_array[0].size / + sizeof(struct phys_addr), + sizeof(struct phys_addr), dq_vaddress); + if (ret) { + shost_printk(KERN_ERR, phba->shost, + "be_fill_queue Failed for DEF PDU DATA\n"); + return ret; + } + mem->dma = mem_descr->mem_array[idx].bus_address.u.a64.address; + ret = be_cmd_create_default_pdu_queue(&phba->ctrl, cq, dataq, + def_pdu_ring_sz, + phba->params.defpdu_data_sz); + if (ret) { + shost_printk(KERN_ERR, phba->shost, + "be_cmd_create_default_pdu_queue Failed" + " for DEF PDU DATA\n"); + return ret; + } + phwi_ctrlr->default_pdu_data.id = phwi_context->be_def_dataq.id; + SE_DEBUG(DBG_LVL_8, "iscsi def data id is %d\n", + phwi_context->be_def_dataq.id); + hwi_post_async_buffers(phba, 0); + SE_DEBUG(DBG_LVL_8, "DEFAULT PDU DATA RING CREATED \n"); + return 0; +} + +static int +beiscsi_post_pages(struct beiscsi_hba *phba) +{ + struct be_mem_descriptor *mem_descr; + struct mem_array *pm_arr; + unsigned int page_offset, i; + struct be_dma_mem sgl; + int status; + + mem_descr = phba->init_mem; + mem_descr += HWI_MEM_SGE; + pm_arr = mem_descr->mem_array; + + page_offset = (sizeof(struct iscsi_sge) * phba->params.num_sge_per_io * + phba->fw_config.iscsi_icd_start) / PAGE_SIZE; + for (i = 0; i < mem_descr->num_elements; i++) { + hwi_build_be_sgl_arr(phba, pm_arr, &sgl); + status = be_cmd_iscsi_post_sgl_pages(&phba->ctrl, &sgl, + page_offset, + (pm_arr->size / PAGE_SIZE)); + page_offset += pm_arr->size / PAGE_SIZE; + if (status != 0) { + shost_printk(KERN_ERR, phba->shost, + "post sgl failed.\n"); + return status; + } + pm_arr++; + } + SE_DEBUG(DBG_LVL_8, "POSTED PAGES \n"); + return 0; +} + +static int +beiscsi_create_wrb_rings(struct beiscsi_hba *phba, + struct hwi_context_memory *phwi_context, + struct hwi_controller *phwi_ctrlr) +{ + unsigned int wrb_mem_index, offset, size, num_wrb_rings; + u64 pa_addr_lo; + unsigned int idx, num, i; + struct mem_array *pwrb_arr; + void *wrb_vaddr; + struct be_dma_mem sgl; + struct be_mem_descriptor *mem_descr; + int status; + + idx = 0; + mem_descr = phba->init_mem; + mem_descr += HWI_MEM_WRB; + pwrb_arr = kmalloc(sizeof(*pwrb_arr) * phba->params.cxns_per_ctrl, + GFP_KERNEL); + if (!pwrb_arr) { + shost_printk(KERN_ERR, phba->shost, + "Memory alloc failed in create wrb ring.\n"); + return -ENOMEM; + } + wrb_vaddr = mem_descr->mem_array[idx].virtual_address; + pa_addr_lo = mem_descr->mem_array[idx].bus_address.u.a64.address; + num_wrb_rings = mem_descr->mem_array[idx].size / + (phba->params.wrbs_per_cxn * sizeof(struct iscsi_wrb)); + + for (num = 0; num < phba->params.cxns_per_ctrl; num++) { + if (num_wrb_rings) { + pwrb_arr[num].virtual_address = wrb_vaddr; + pwrb_arr[num].bus_address.u.a64.address = pa_addr_lo; + pwrb_arr[num].size = phba->params.wrbs_per_cxn * + sizeof(struct iscsi_wrb); + wrb_vaddr += pwrb_arr[num].size; + pa_addr_lo += pwrb_arr[num].size; + num_wrb_rings--; + } else { + idx++; + wrb_vaddr = mem_descr->mem_array[idx].virtual_address; + pa_addr_lo = mem_descr->mem_array[idx].\ + bus_address.u.a64.address; + num_wrb_rings = mem_descr->mem_array[idx].size / + (phba->params.wrbs_per_cxn * + sizeof(struct iscsi_wrb)); + pwrb_arr[num].virtual_address = wrb_vaddr; + pwrb_arr[num].bus_address.u.a64.address\ + = pa_addr_lo; + pwrb_arr[num].size = phba->params.wrbs_per_cxn * + sizeof(struct iscsi_wrb); + wrb_vaddr += pwrb_arr[num].size; + pa_addr_lo += pwrb_arr[num].size; + num_wrb_rings--; + } + } + for (i = 0; i < phba->params.cxns_per_ctrl; i++) { + wrb_mem_index = 0; + offset = 0; + size = 0; + + hwi_build_be_sgl_by_offset(phba, &pwrb_arr[i], &sgl); + status = be_cmd_wrbq_create(&phba->ctrl, &sgl, + &phwi_context->be_wrbq[i]); + if (status != 0) { + shost_printk(KERN_ERR, phba->shost, + "wrbq create failed."); + return status; + } + phwi_ctrlr->wrb_context[i].cid = phwi_context->be_wrbq[i].id; + } + kfree(pwrb_arr); + return 0; +} + +static void free_wrb_handles(struct beiscsi_hba *phba) +{ + unsigned int index; + struct hwi_controller *phwi_ctrlr; + struct hwi_wrb_context *pwrb_context; + + phwi_ctrlr = phba->phwi_ctrlr; + for (index = 0; index < phba->params.cxns_per_ctrl * 2; index += 2) { + pwrb_context = &phwi_ctrlr->wrb_context[index]; + kfree(pwrb_context->pwrb_handle_base); + kfree(pwrb_context->pwrb_handle_basestd); + } +} + +static void hwi_cleanup(struct beiscsi_hba *phba) +{ + struct be_queue_info *q; + struct be_ctrl_info *ctrl = &phba->ctrl; + struct hwi_controller *phwi_ctrlr; + struct hwi_context_memory *phwi_context; + int i; + + phwi_ctrlr = phba->phwi_ctrlr; + phwi_context = phwi_ctrlr->phwi_ctxt; + for (i = 0; i < phba->params.cxns_per_ctrl; i++) { + q = &phwi_context->be_wrbq[i]; + if (q->created) + beiscsi_cmd_q_destroy(ctrl, q, QTYPE_WRBQ); + } + + free_wrb_handles(phba); + + q = &phwi_context->be_def_hdrq; + if (q->created) + beiscsi_cmd_q_destroy(ctrl, q, QTYPE_DPDUQ); + + q = &phwi_context->be_def_dataq; + if (q->created) + beiscsi_cmd_q_destroy(ctrl, q, QTYPE_DPDUQ); + + beiscsi_cmd_q_destroy(ctrl, NULL, QTYPE_SGL); + + q = &phwi_context->be_cq; + if (q->created) + beiscsi_cmd_q_destroy(ctrl, q, QTYPE_CQ); + + q = &phwi_context->be_eq.q; + if (q->created) + beiscsi_cmd_q_destroy(ctrl, q, QTYPE_EQ); +} + +static int hwi_init_port(struct beiscsi_hba *phba) +{ + struct hwi_controller *phwi_ctrlr; + struct hwi_context_memory *phwi_context; + unsigned int def_pdu_ring_sz; + struct be_ctrl_info *ctrl = &phba->ctrl; + int status; + + def_pdu_ring_sz = + phba->params.asyncpdus_per_ctrl * sizeof(struct phys_addr); + phwi_ctrlr = phba->phwi_ctrlr; + + phwi_context = phwi_ctrlr->phwi_ctxt; + phwi_context->be_eq.max_eqd = 0; + phwi_context->be_eq.min_eqd = 0; + phwi_context->be_eq.cur_eqd = 64; + phwi_context->be_eq.enable_aic = false; + be_cmd_fw_initialize(&phba->ctrl); + status = beiscsi_create_eq(phba, phwi_context); + if (status != 0) { + shost_printk(KERN_ERR, phba->shost, "EQ not created \n"); + goto error; + } + + status = mgmt_check_supported_fw(ctrl); + if (status != 0) { + shost_printk(KERN_ERR, phba->shost, + "Unsupported fw version \n"); + goto error; + } + + status = mgmt_get_fw_config(ctrl, phba); + if (status != 0) { + shost_printk(KERN_ERR, phba->shost, + "Error getting fw config\n"); + goto error; + } + + status = beiscsi_create_cq(phba, phwi_context); + if (status != 0) { + shost_printk(KERN_ERR, phba->shost, "CQ not created\n"); + goto error; + } + + status = beiscsi_create_def_hdr(phba, phwi_context, phwi_ctrlr, + def_pdu_ring_sz); + if (status != 0) { + shost_printk(KERN_ERR, phba->shost, + "Default Header not created\n"); + goto error; + } + + status = beiscsi_create_def_data(phba, phwi_context, + phwi_ctrlr, def_pdu_ring_sz); + if (status != 0) { + shost_printk(KERN_ERR, phba->shost, + "Default Data not created\n"); + goto error; + } + + status = beiscsi_post_pages(phba); + if (status != 0) { + shost_printk(KERN_ERR, phba->shost, "Post SGL Pages Failed\n"); + goto error; + } + + status = beiscsi_create_wrb_rings(phba, phwi_context, phwi_ctrlr); + if (status != 0) { + shost_printk(KERN_ERR, phba->shost, + "WRB Rings not created\n"); + goto error; + } + + SE_DEBUG(DBG_LVL_8, "hwi_init_port success\n"); + return 0; + +error: + shost_printk(KERN_ERR, phba->shost, "hwi_init_port failed"); + hwi_cleanup(phba); + return -ENOMEM; +} + + +static int hwi_init_controller(struct beiscsi_hba *phba) +{ + struct hwi_controller *phwi_ctrlr; + + phwi_ctrlr = phba->phwi_ctrlr; + if (1 == phba->init_mem[HWI_MEM_ADDN_CONTEXT].num_elements) { + phwi_ctrlr->phwi_ctxt = (struct hwi_context_memory *)phba-> + init_mem[HWI_MEM_ADDN_CONTEXT].mem_array[0].virtual_address; + SE_DEBUG(DBG_LVL_8, " phwi_ctrlr->phwi_ctxt=%p \n", + phwi_ctrlr->phwi_ctxt); + } else { + shost_printk(KERN_ERR, phba->shost, + "HWI_MEM_ADDN_CONTEXT is more than one element." + "Failing to load\n"); + return -ENOMEM; + } + + iscsi_init_global_templates(phba); + beiscsi_init_wrb_handle(phba); + hwi_init_async_pdu_ctx(phba); + if (hwi_init_port(phba) != 0) { + shost_printk(KERN_ERR, phba->shost, + "hwi_init_controller failed\n"); + return -ENOMEM; + } + return 0; +} + +static void beiscsi_free_mem(struct beiscsi_hba *phba) +{ + struct be_mem_descriptor *mem_descr; + int i, j; + + mem_descr = phba->init_mem; + i = 0; + j = 0; + for (i = 0; i < SE_MEM_MAX; i++) { + for (j = mem_descr->num_elements; j > 0; j--) { + pci_free_consistent(phba->pcidev, + mem_descr->mem_array[j - 1].size, + mem_descr->mem_array[j - 1].virtual_address, + mem_descr->mem_array[j - 1].bus_address. + u.a64.address); + } + kfree(mem_descr->mem_array); + mem_descr++; + } + kfree(phba->init_mem); + kfree(phba->phwi_ctrlr); +} + +static int beiscsi_init_controller(struct beiscsi_hba *phba) +{ + int ret = -ENOMEM; + + ret = beiscsi_get_memory(phba); + if (ret < 0) { + shost_printk(KERN_ERR, phba->shost, "beiscsi_dev_probe -" + "Failed in beiscsi_alloc_memory \n"); + return ret; + } + + ret = hwi_init_controller(phba); + if (ret) + goto free_init; + SE_DEBUG(DBG_LVL_8, "Return success from beiscsi_init_controller"); + return 0; + +free_init: + beiscsi_free_mem(phba); + return -ENOMEM; +} + +static int beiscsi_init_sgl_handle(struct beiscsi_hba *phba) +{ + struct be_mem_descriptor *mem_descr_sglh, *mem_descr_sg; + struct sgl_handle *psgl_handle; + struct iscsi_sge *pfrag; + unsigned int arr_index, i, idx; + + phba->io_sgl_hndl_avbl = 0; + phba->eh_sgl_hndl_avbl = 0; + mem_descr_sglh = phba->init_mem; + mem_descr_sglh += HWI_MEM_SGLH; + if (1 == mem_descr_sglh->num_elements) { + phba->io_sgl_hndl_base = kzalloc(sizeof(struct sgl_handle *) * + phba->params.ios_per_ctrl, + GFP_KERNEL); + if (!phba->io_sgl_hndl_base) { + shost_printk(KERN_ERR, phba->shost, + "Mem Alloc Failed. Failing to load\n"); + return -ENOMEM; + } + phba->eh_sgl_hndl_base = kzalloc(sizeof(struct sgl_handle *) * + (phba->params.icds_per_ctrl - + phba->params.ios_per_ctrl), + GFP_KERNEL); + if (!phba->eh_sgl_hndl_base) { + kfree(phba->io_sgl_hndl_base); + shost_printk(KERN_ERR, phba->shost, + "Mem Alloc Failed. Failing to load\n"); + return -ENOMEM; + } + } else { + shost_printk(KERN_ERR, phba->shost, + "HWI_MEM_SGLH is more than one element." + "Failing to load\n"); + return -ENOMEM; + } + + arr_index = 0; + idx = 0; + while (idx < mem_descr_sglh->num_elements) { + psgl_handle = mem_descr_sglh->mem_array[idx].virtual_address; + + for (i = 0; i < (mem_descr_sglh->mem_array[idx].size / + sizeof(struct sgl_handle)); i++) { + if (arr_index < phba->params.ios_per_ctrl) { + phba->io_sgl_hndl_base[arr_index] = psgl_handle; + phba->io_sgl_hndl_avbl++; + arr_index++; + } else { + phba->eh_sgl_hndl_base[arr_index - + phba->params.ios_per_ctrl] = + psgl_handle; + arr_index++; + phba->eh_sgl_hndl_avbl++; + } + psgl_handle++; + } + idx++; + } + SE_DEBUG(DBG_LVL_8, + "phba->io_sgl_hndl_avbl=%d" + "phba->eh_sgl_hndl_avbl=%d \n", + phba->io_sgl_hndl_avbl, + phba->eh_sgl_hndl_avbl); + mem_descr_sg = phba->init_mem; + mem_descr_sg += HWI_MEM_SGE; + SE_DEBUG(DBG_LVL_8, "\n mem_descr_sg->num_elements=%d \n", + mem_descr_sg->num_elements); + arr_index = 0; + idx = 0; + while (idx < mem_descr_sg->num_elements) { + pfrag = mem_descr_sg->mem_array[idx].virtual_address; + + for (i = 0; + i < (mem_descr_sg->mem_array[idx].size) / + (sizeof(struct iscsi_sge) * phba->params.num_sge_per_io); + i++) { + if (arr_index < phba->params.ios_per_ctrl) + psgl_handle = phba->io_sgl_hndl_base[arr_index]; + else + psgl_handle = phba->eh_sgl_hndl_base[arr_index - + phba->params.ios_per_ctrl]; + psgl_handle->pfrag = pfrag; + AMAP_SET_BITS(struct amap_iscsi_sge, addr_hi, pfrag, 0); + AMAP_SET_BITS(struct amap_iscsi_sge, addr_lo, pfrag, 0); + pfrag += phba->params.num_sge_per_io; + psgl_handle->sgl_index = + phba->fw_config.iscsi_cid_start + arr_index++; + } + idx++; + } + phba->io_sgl_free_index = 0; + phba->io_sgl_alloc_index = 0; + phba->eh_sgl_free_index = 0; + phba->eh_sgl_alloc_index = 0; + return 0; +} + +static int hba_setup_cid_tbls(struct beiscsi_hba *phba) +{ + int i, new_cid; + + phba->cid_array = kmalloc(sizeof(void *) * phba->params.cxns_per_ctrl, + GFP_KERNEL); + if (!phba->cid_array) { + shost_printk(KERN_ERR, phba->shost, + "Failed to allocate memory in " + "hba_setup_cid_tbls\n"); + return -ENOMEM; + } + phba->ep_array = kmalloc(sizeof(struct iscsi_endpoint *) * + phba->params.cxns_per_ctrl * 2, GFP_KERNEL); + if (!phba->ep_array) { + shost_printk(KERN_ERR, phba->shost, + "Failed to allocate memory in " + "hba_setup_cid_tbls \n"); + kfree(phba->cid_array); + return -ENOMEM; + } + new_cid = phba->fw_config.iscsi_icd_start; + for (i = 0; i < phba->params.cxns_per_ctrl; i++) { + phba->cid_array[i] = new_cid; + new_cid += 2; + } + phba->avlbl_cids = phba->params.cxns_per_ctrl; + return 0; +} + +static unsigned char hwi_enable_intr(struct beiscsi_hba *phba) +{ + struct be_ctrl_info *ctrl = &phba->ctrl; + struct hwi_controller *phwi_ctrlr; + struct hwi_context_memory *phwi_context; + struct be_queue_info *eq; + u8 __iomem *addr; + u32 reg; + u32 enabled; + + phwi_ctrlr = phba->phwi_ctrlr; + phwi_context = phwi_ctrlr->phwi_ctxt; + + eq = &phwi_context->be_eq.q; + addr = (u8 __iomem *) ((u8 __iomem *) ctrl->pcicfg + + PCICFG_MEMBAR_CTRL_INT_CTRL_OFFSET); + reg = ioread32(addr); + SE_DEBUG(DBG_LVL_8, "reg =x%08x \n", reg); + + enabled = reg & MEMBAR_CTRL_INT_CTRL_HOSTINTR_MASK; + if (!enabled) { + reg |= MEMBAR_CTRL_INT_CTRL_HOSTINTR_MASK; + SE_DEBUG(DBG_LVL_8, "reg =x%08x addr=%p \n", reg, addr); + iowrite32(reg, addr); + SE_DEBUG(DBG_LVL_8, "eq->id=%d \n", eq->id); + + hwi_ring_eq_db(phba, eq->id, 0, 0, 1, 1); + } else + shost_printk(KERN_WARNING, phba->shost, + "In hwi_enable_intr, Not Enabled \n"); + return true; +} + +static void hwi_disable_intr(struct beiscsi_hba *phba) +{ + struct be_ctrl_info *ctrl = &phba->ctrl; + + u8 __iomem *addr = ctrl->pcicfg + PCICFG_MEMBAR_CTRL_INT_CTRL_OFFSET; + u32 reg = ioread32(addr); + + u32 enabled = reg & MEMBAR_CTRL_INT_CTRL_HOSTINTR_MASK; + if (enabled) { + reg &= ~MEMBAR_CTRL_INT_CTRL_HOSTINTR_MASK; + iowrite32(reg, addr); + } else + shost_printk(KERN_WARNING, phba->shost, + "In hwi_disable_intr, Already Disabled \n"); +} + +static int beiscsi_init_port(struct beiscsi_hba *phba) +{ + int ret; + + ret = beiscsi_init_controller(phba); + if (ret < 0) { + shost_printk(KERN_ERR, phba->shost, + "beiscsi_dev_probe - Failed in" + "beiscsi_init_controller \n"); + return ret; + } + ret = beiscsi_init_sgl_handle(phba); + if (ret < 0) { + shost_printk(KERN_ERR, phba->shost, + "beiscsi_dev_probe - Failed in" + "beiscsi_init_sgl_handle \n"); + goto do_cleanup_ctrlr; + } + + if (hba_setup_cid_tbls(phba)) { + shost_printk(KERN_ERR, phba->shost, + "Failed in hba_setup_cid_tbls\n"); + kfree(phba->io_sgl_hndl_base); + kfree(phba->eh_sgl_hndl_base); + goto do_cleanup_ctrlr; + } + + return ret; + +do_cleanup_ctrlr: + hwi_cleanup(phba); + return ret; +} + +static void hwi_purge_eq(struct beiscsi_hba *phba) +{ + struct hwi_controller *phwi_ctrlr; + struct hwi_context_memory *phwi_context; + struct be_queue_info *eq; + struct be_eq_entry *eqe = NULL; + + phwi_ctrlr = phba->phwi_ctrlr; + phwi_context = phwi_ctrlr->phwi_ctxt; + eq = &phwi_context->be_eq.q; + eqe = queue_tail_node(eq); + + while (eqe->dw[offsetof(struct amap_eq_entry, valid) / 32] + & EQE_VALID_MASK) { + AMAP_SET_BITS(struct amap_eq_entry, valid, eqe, 0); + queue_tail_inc(eq); + eqe = queue_tail_node(eq); + } +} + +static void beiscsi_clean_port(struct beiscsi_hba *phba) +{ + unsigned char mgmt_status; + + mgmt_status = mgmt_epfw_cleanup(phba, CMD_CONNECTION_CHUTE_0); + if (mgmt_status) + shost_printk(KERN_WARNING, phba->shost, + "mgmt_epfw_cleanup FAILED \n"); + hwi_cleanup(phba); + hwi_purge_eq(phba); + kfree(phba->io_sgl_hndl_base); + kfree(phba->eh_sgl_hndl_base); + kfree(phba->cid_array); + kfree(phba->ep_array); +} + +void +beiscsi_offload_connection(struct beiscsi_conn *beiscsi_conn, + struct beiscsi_offload_params *params) +{ + struct wrb_handle *pwrb_handle; + struct iscsi_target_context_update_wrb *pwrb = NULL; + struct be_mem_descriptor *mem_descr; + struct beiscsi_hba *phba = beiscsi_conn->phba; + u32 doorbell = 0; + + /* + * We can always use 0 here because it is reserved by libiscsi for + * login/startup related tasks. + */ + pwrb_handle = alloc_wrb_handle(phba, beiscsi_conn->beiscsi_conn_cid, 0); + pwrb = (struct iscsi_target_context_update_wrb *)pwrb_handle->pwrb; + memset(pwrb, 0, sizeof(*pwrb)); + AMAP_SET_BITS(struct amap_iscsi_target_context_update_wrb, + max_burst_length, pwrb, params->dw[offsetof + (struct amap_beiscsi_offload_params, + max_burst_length) / 32]); + AMAP_SET_BITS(struct amap_iscsi_target_context_update_wrb, + max_send_data_segment_length, pwrb, + params->dw[offsetof(struct amap_beiscsi_offload_params, + max_send_data_segment_length) / 32]); + AMAP_SET_BITS(struct amap_iscsi_target_context_update_wrb, + first_burst_length, + pwrb, + params->dw[offsetof(struct amap_beiscsi_offload_params, + first_burst_length) / 32]); + + AMAP_SET_BITS(struct amap_iscsi_target_context_update_wrb, erl, pwrb, + (params->dw[offsetof(struct amap_beiscsi_offload_params, + erl) / 32] & OFFLD_PARAMS_ERL)); + AMAP_SET_BITS(struct amap_iscsi_target_context_update_wrb, dde, pwrb, + (params->dw[offsetof(struct amap_beiscsi_offload_params, + dde) / 32] & OFFLD_PARAMS_DDE) >> 2); + AMAP_SET_BITS(struct amap_iscsi_target_context_update_wrb, hde, pwrb, + (params->dw[offsetof(struct amap_beiscsi_offload_params, + hde) / 32] & OFFLD_PARAMS_HDE) >> 3); + AMAP_SET_BITS(struct amap_iscsi_target_context_update_wrb, ir2t, pwrb, + (params->dw[offsetof(struct amap_beiscsi_offload_params, + ir2t) / 32] & OFFLD_PARAMS_IR2T) >> 4); + AMAP_SET_BITS(struct amap_iscsi_target_context_update_wrb, imd, pwrb, + (params->dw[offsetof(struct amap_beiscsi_offload_params, + imd) / 32] & OFFLD_PARAMS_IMD) >> 5); + AMAP_SET_BITS(struct amap_iscsi_target_context_update_wrb, stat_sn, + pwrb, + (params->dw[offsetof(struct amap_beiscsi_offload_params, + exp_statsn) / 32] + 1)); + AMAP_SET_BITS(struct amap_iscsi_target_context_update_wrb, type, pwrb, + 0x7); + AMAP_SET_BITS(struct amap_iscsi_target_context_update_wrb, wrb_idx, + pwrb, pwrb_handle->wrb_index); + AMAP_SET_BITS(struct amap_iscsi_target_context_update_wrb, ptr2nextwrb, + pwrb, pwrb_handle->nxt_wrb_index); + AMAP_SET_BITS(struct amap_iscsi_target_context_update_wrb, + session_state, pwrb, 0); + AMAP_SET_BITS(struct amap_iscsi_target_context_update_wrb, compltonack, + pwrb, 1); + AMAP_SET_BITS(struct amap_iscsi_target_context_update_wrb, notpredblq, + pwrb, 0); + AMAP_SET_BITS(struct amap_iscsi_target_context_update_wrb, mode, pwrb, + 0); + + mem_descr = phba->init_mem; + mem_descr += ISCSI_MEM_GLOBAL_HEADER; + + AMAP_SET_BITS(struct amap_iscsi_target_context_update_wrb, + pad_buffer_addr_hi, pwrb, + mem_descr->mem_array[0].bus_address.u.a32.address_hi); + AMAP_SET_BITS(struct amap_iscsi_target_context_update_wrb, + pad_buffer_addr_lo, pwrb, + mem_descr->mem_array[0].bus_address.u.a32.address_lo); + + be_dws_le_to_cpu(pwrb, sizeof(struct iscsi_target_context_update_wrb)); + + doorbell |= beiscsi_conn->beiscsi_conn_cid & DB_WRB_POST_CID_MASK; + doorbell |= (pwrb_handle->wrb_index & DB_DEF_PDU_WRB_INDEX_MASK) << + DB_DEF_PDU_WRB_INDEX_SHIFT; + doorbell |= 1 << DB_DEF_PDU_NUM_POSTED_SHIFT; + + iowrite32(doorbell, phba->db_va + DB_TXULP0_OFFSET); +} + +static void beiscsi_parse_pdu(struct iscsi_conn *conn, itt_t itt, + int *index, int *age) +{ + *index = be32_to_cpu(itt) >> 16; + if (age) + *age = conn->session->age; +} + +/** + * beiscsi_alloc_pdu - allocates pdu and related resources + * @task: libiscsi task + * @opcode: opcode of pdu for task + * + * This is called with the session lock held. It will allocate + * the wrb and sgl if needed for the command. And it will prep + * the pdu's itt. beiscsi_parse_pdu will later translate + * the pdu itt to the libiscsi task itt. + */ +static int beiscsi_alloc_pdu(struct iscsi_task *task, uint8_t opcode) +{ + struct beiscsi_io_task *io_task = task->dd_data; + struct iscsi_conn *conn = task->conn; + struct beiscsi_conn *beiscsi_conn = conn->dd_data; + struct beiscsi_hba *phba = beiscsi_conn->phba; + struct hwi_wrb_context *pwrb_context; + struct hwi_controller *phwi_ctrlr; + itt_t itt; + struct beiscsi_session *beiscsi_sess = beiscsi_conn->beiscsi_sess; + dma_addr_t paddr; + + io_task->cmd_bhs = pci_pool_alloc(beiscsi_sess->bhs_pool, + GFP_KERNEL, &paddr); + + if (!io_task->cmd_bhs) + return -ENOMEM; + + io_task->bhs_pa.u.a64.address = paddr; + io_task->pwrb_handle = alloc_wrb_handle(phba, + beiscsi_conn->beiscsi_conn_cid, + task->itt); + io_task->pwrb_handle->pio_handle = task; + io_task->conn = beiscsi_conn; + + task->hdr = (struct iscsi_hdr *)&io_task->cmd_bhs->iscsi_hdr; + task->hdr_max = sizeof(struct be_cmd_bhs); + + if (task->sc) { + spin_lock(&phba->io_sgl_lock); + io_task->psgl_handle = alloc_io_sgl_handle(phba); + spin_unlock(&phba->io_sgl_lock); + if (!io_task->psgl_handle) + goto free_hndls; + + } else { + io_task->scsi_cmnd = NULL; + if ((task->hdr->opcode & ISCSI_OPCODE_MASK) == ISCSI_OP_LOGIN) { + if (!beiscsi_conn->login_in_progress) { + spin_lock(&phba->mgmt_sgl_lock); + io_task->psgl_handle = (struct sgl_handle *) + alloc_mgmt_sgl_handle(phba); + spin_unlock(&phba->mgmt_sgl_lock); + if (!io_task->psgl_handle) + goto free_hndls; + + beiscsi_conn->login_in_progress = 1; + beiscsi_conn->plogin_sgl_handle = + io_task->psgl_handle; + } else { + io_task->psgl_handle = + beiscsi_conn->plogin_sgl_handle; + } + } else { + spin_lock(&phba->mgmt_sgl_lock); + io_task->psgl_handle = alloc_mgmt_sgl_handle(phba); + spin_unlock(&phba->mgmt_sgl_lock); + if (!io_task->psgl_handle) + goto free_hndls; + } + } + itt = (itt_t) cpu_to_be32(((unsigned int)task->itt << 16) | + (unsigned int)(io_task->psgl_handle->sgl_index)); + io_task->cmd_bhs->iscsi_hdr.itt = itt; + return 0; + +free_hndls: + phwi_ctrlr = phba->phwi_ctrlr; + pwrb_context = &phwi_ctrlr->wrb_context[beiscsi_conn->beiscsi_conn_cid]; + free_wrb_handle(phba, pwrb_context, io_task->pwrb_handle); + io_task->pwrb_handle = NULL; + pci_pool_free(beiscsi_sess->bhs_pool, io_task->cmd_bhs, + io_task->bhs_pa.u.a64.address); + SE_DEBUG(DBG_LVL_1, "Alloc of SGL_ICD Failed \n"); + return -ENOMEM; +} + +static void beiscsi_cleanup_task(struct iscsi_task *task) +{ + struct beiscsi_io_task *io_task = task->dd_data; + struct iscsi_conn *conn = task->conn; + struct beiscsi_conn *beiscsi_conn = conn->dd_data; + struct beiscsi_hba *phba = beiscsi_conn->phba; + struct beiscsi_session *beiscsi_sess = beiscsi_conn->beiscsi_sess; + struct hwi_wrb_context *pwrb_context; + struct hwi_controller *phwi_ctrlr; + + phwi_ctrlr = phba->phwi_ctrlr; + pwrb_context = &phwi_ctrlr->wrb_context[beiscsi_conn->beiscsi_conn_cid]; + if (io_task->pwrb_handle) { + free_wrb_handle(phba, pwrb_context, io_task->pwrb_handle); + io_task->pwrb_handle = NULL; + } + + if (io_task->cmd_bhs) { + pci_pool_free(beiscsi_sess->bhs_pool, io_task->cmd_bhs, + io_task->bhs_pa.u.a64.address); + } + + if (task->sc) { + if (io_task->psgl_handle) { + spin_lock(&phba->io_sgl_lock); + free_io_sgl_handle(phba, io_task->psgl_handle); + spin_unlock(&phba->io_sgl_lock); + io_task->psgl_handle = NULL; + } + } else { + if ((task->hdr->opcode & ISCSI_OPCODE_MASK) == ISCSI_OP_LOGIN) + return; + if (io_task->psgl_handle) { + spin_lock(&phba->mgmt_sgl_lock); + free_mgmt_sgl_handle(phba, io_task->psgl_handle); + spin_unlock(&phba->mgmt_sgl_lock); + io_task->psgl_handle = NULL; + } + } +} + +static int beiscsi_iotask(struct iscsi_task *task, struct scatterlist *sg, + unsigned int num_sg, unsigned int xferlen, + unsigned int writedir) +{ + + struct beiscsi_io_task *io_task = task->dd_data; + struct iscsi_conn *conn = task->conn; + struct beiscsi_conn *beiscsi_conn = conn->dd_data; + struct beiscsi_hba *phba = beiscsi_conn->phba; + struct iscsi_wrb *pwrb = NULL; + unsigned int doorbell = 0; + + pwrb = io_task->pwrb_handle->pwrb; + io_task->cmd_bhs->iscsi_hdr.exp_statsn = 0; + io_task->bhs_len = sizeof(struct be_cmd_bhs); + + if (writedir) { + SE_DEBUG(DBG_LVL_4, " WRITE Command \t"); + memset(&io_task->cmd_bhs->iscsi_data_pdu, 0, 48); + AMAP_SET_BITS(struct amap_pdu_data_out, itt, + &io_task->cmd_bhs->iscsi_data_pdu, + (unsigned int)io_task->cmd_bhs->iscsi_hdr.itt); + AMAP_SET_BITS(struct amap_pdu_data_out, opcode, + &io_task->cmd_bhs->iscsi_data_pdu, + ISCSI_OPCODE_SCSI_DATA_OUT); + AMAP_SET_BITS(struct amap_pdu_data_out, final_bit, + &io_task->cmd_bhs->iscsi_data_pdu, 1); + AMAP_SET_BITS(struct amap_iscsi_wrb, type, pwrb, INI_WR_CMD); + AMAP_SET_BITS(struct amap_iscsi_wrb, dsp, pwrb, 1); + } else { + SE_DEBUG(DBG_LVL_4, "READ Command \t"); + AMAP_SET_BITS(struct amap_iscsi_wrb, type, pwrb, INI_RD_CMD); + AMAP_SET_BITS(struct amap_iscsi_wrb, dsp, pwrb, 0); + } + memcpy(&io_task->cmd_bhs->iscsi_data_pdu. + dw[offsetof(struct amap_pdu_data_out, lun) / 32], + io_task->cmd_bhs->iscsi_hdr.lun, sizeof(struct scsi_lun)); + + AMAP_SET_BITS(struct amap_iscsi_wrb, lun, pwrb, + cpu_to_be16((unsigned short)io_task->cmd_bhs->iscsi_hdr. + lun[0])); + AMAP_SET_BITS(struct amap_iscsi_wrb, r2t_exp_dtl, pwrb, xferlen); + AMAP_SET_BITS(struct amap_iscsi_wrb, wrb_idx, pwrb, + io_task->pwrb_handle->wrb_index); + AMAP_SET_BITS(struct amap_iscsi_wrb, cmdsn_itt, pwrb, + be32_to_cpu(task->cmdsn)); + AMAP_SET_BITS(struct amap_iscsi_wrb, sgl_icd_idx, pwrb, + io_task->psgl_handle->sgl_index); + + hwi_write_sgl(pwrb, sg, num_sg, io_task); + + AMAP_SET_BITS(struct amap_iscsi_wrb, ptr2nextwrb, pwrb, + io_task->pwrb_handle->nxt_wrb_index); + be_dws_le_to_cpu(pwrb, sizeof(struct iscsi_wrb)); + + doorbell |= beiscsi_conn->beiscsi_conn_cid & DB_WRB_POST_CID_MASK; + doorbell |= (io_task->pwrb_handle->wrb_index & + DB_DEF_PDU_WRB_INDEX_MASK) << DB_DEF_PDU_WRB_INDEX_SHIFT; + doorbell |= 1 << DB_DEF_PDU_NUM_POSTED_SHIFT; + + iowrite32(doorbell, phba->db_va + DB_TXULP0_OFFSET); + return 0; +} + +static int beiscsi_mtask(struct iscsi_task *task) +{ + struct beiscsi_io_task *aborted_io_task, *io_task = task->dd_data; + struct iscsi_conn *conn = task->conn; + struct beiscsi_conn *beiscsi_conn = conn->dd_data; + struct beiscsi_hba *phba = beiscsi_conn->phba; + struct iscsi_wrb *pwrb = NULL; + unsigned int doorbell = 0; + struct iscsi_task *aborted_task; + + pwrb = io_task->pwrb_handle->pwrb; + AMAP_SET_BITS(struct amap_iscsi_wrb, cmdsn_itt, pwrb, + be32_to_cpu(task->cmdsn)); + AMAP_SET_BITS(struct amap_iscsi_wrb, wrb_idx, pwrb, + io_task->pwrb_handle->wrb_index); + AMAP_SET_BITS(struct amap_iscsi_wrb, sgl_icd_idx, pwrb, + io_task->psgl_handle->sgl_index); + + switch (task->hdr->opcode & ISCSI_OPCODE_MASK) { + case ISCSI_OP_LOGIN: + AMAP_SET_BITS(struct amap_iscsi_wrb, type, pwrb, TGT_DM_CMD); + AMAP_SET_BITS(struct amap_iscsi_wrb, dmsg, pwrb, 0); + AMAP_SET_BITS(struct amap_iscsi_wrb, cmdsn_itt, pwrb, 1); + hwi_write_buffer(pwrb, task); + break; + case ISCSI_OP_NOOP_OUT: + AMAP_SET_BITS(struct amap_iscsi_wrb, type, pwrb, INI_RD_CMD); + hwi_write_buffer(pwrb, task); + break; + case ISCSI_OP_TEXT: + AMAP_SET_BITS(struct amap_iscsi_wrb, type, pwrb, INI_WR_CMD); + AMAP_SET_BITS(struct amap_iscsi_wrb, dsp, pwrb, 1); + hwi_write_buffer(pwrb, task); + break; + case ISCSI_OP_SCSI_TMFUNC: + aborted_task = iscsi_itt_to_task(conn, + ((struct iscsi_tm *)task->hdr)->rtt); + if (!aborted_task) + return 0; + aborted_io_task = aborted_task->dd_data; + if (!aborted_io_task->scsi_cmnd) + return 0; + + mgmt_invalidate_icds(phba, + aborted_io_task->psgl_handle->sgl_index, + beiscsi_conn->beiscsi_conn_cid); + AMAP_SET_BITS(struct amap_iscsi_wrb, type, pwrb, INI_TMF_CMD); + AMAP_SET_BITS(struct amap_iscsi_wrb, dmsg, pwrb, 0); + hwi_write_buffer(pwrb, task); + break; + case ISCSI_OP_LOGOUT: + AMAP_SET_BITS(struct amap_iscsi_wrb, dmsg, pwrb, 0); + AMAP_SET_BITS(struct amap_iscsi_wrb, type, pwrb, + HWH_TYPE_LOGOUT); + hwi_write_buffer(pwrb, task); + break; + + default: + SE_DEBUG(DBG_LVL_1, "opcode =%d Not supported \n", + task->hdr->opcode & ISCSI_OPCODE_MASK); + return -EINVAL; + } + + AMAP_SET_BITS(struct amap_iscsi_wrb, r2t_exp_dtl, pwrb, + be32_to_cpu(task->data_count)); + AMAP_SET_BITS(struct amap_iscsi_wrb, ptr2nextwrb, pwrb, + io_task->pwrb_handle->nxt_wrb_index); + be_dws_le_to_cpu(pwrb, sizeof(struct iscsi_wrb)); + + doorbell |= beiscsi_conn->beiscsi_conn_cid & DB_WRB_POST_CID_MASK; + doorbell |= (io_task->pwrb_handle->wrb_index & + DB_DEF_PDU_WRB_INDEX_MASK) << DB_DEF_PDU_WRB_INDEX_SHIFT; + doorbell |= 1 << DB_DEF_PDU_NUM_POSTED_SHIFT; + iowrite32(doorbell, phba->db_va + DB_TXULP0_OFFSET); + return 0; +} + +static int beiscsi_task_xmit(struct iscsi_task *task) +{ + struct iscsi_conn *conn = task->conn; + struct beiscsi_io_task *io_task = task->dd_data; + struct scsi_cmnd *sc = task->sc; + struct beiscsi_conn *beiscsi_conn = conn->dd_data; + struct scatterlist *sg; + int num_sg; + unsigned int writedir = 0, xferlen = 0; + + SE_DEBUG(DBG_LVL_4, "\n cid=%d In beiscsi_task_xmit task=%p conn=%p \t" + "beiscsi_conn=%p \n", beiscsi_conn->beiscsi_conn_cid, + task, conn, beiscsi_conn); + if (!sc) + return beiscsi_mtask(task); + + io_task->scsi_cmnd = sc; + num_sg = scsi_dma_map(sc); + if (num_sg < 0) { + SE_DEBUG(DBG_LVL_1, " scsi_dma_map Failed\n") + return num_sg; + } + SE_DEBUG(DBG_LVL_4, "xferlen=0x%08x scmd=%p num_sg=%d sernum=%lu\n", + (scsi_bufflen(sc)), sc, num_sg, sc->serial_number); + xferlen = scsi_bufflen(sc); + sg = scsi_sglist(sc); + if (sc->sc_data_direction == DMA_TO_DEVICE) { + writedir = 1; + SE_DEBUG(DBG_LVL_4, "task->imm_count=0x%08x \n", + task->imm_count); + } else + writedir = 0; + return beiscsi_iotask(task, sg, num_sg, xferlen, writedir); +} + +static void beiscsi_remove(struct pci_dev *pcidev) +{ + struct beiscsi_hba *phba = NULL; + + phba = (struct beiscsi_hba *)pci_get_drvdata(pcidev); + if (!phba) { + dev_err(&pcidev->dev, "beiscsi_remove called with no phba \n"); + return; + } + + hwi_disable_intr(phba); + if (phba->pcidev->irq) + free_irq(phba->pcidev->irq, phba); + destroy_workqueue(phba->wq); + if (blk_iopoll_enabled) + blk_iopoll_disable(&phba->iopoll); + + beiscsi_clean_port(phba); + beiscsi_free_mem(phba); + beiscsi_unmap_pci_function(phba); + pci_free_consistent(phba->pcidev, + phba->ctrl.mbox_mem_alloced.size, + phba->ctrl.mbox_mem_alloced.va, + phba->ctrl.mbox_mem_alloced.dma); + iscsi_host_remove(phba->shost); + pci_dev_put(phba->pcidev); + iscsi_host_free(phba->shost); +} + +static int __devinit beiscsi_dev_probe(struct pci_dev *pcidev, + const struct pci_device_id *id) +{ + struct beiscsi_hba *phba = NULL; + int ret; + + ret = beiscsi_enable_pci(pcidev); + if (ret < 0) { + shost_printk(KERN_ERR, phba->shost, "beiscsi_dev_probe-" + "Failed to enable pci device \n"); + return ret; + } + + phba = beiscsi_hba_alloc(pcidev); + if (!phba) { + dev_err(&pcidev->dev, "beiscsi_dev_probe-" + " Failed in beiscsi_hba_alloc \n"); + goto disable_pci; + } + + pci_set_drvdata(pcidev, phba); + ret = be_ctrl_init(phba, pcidev); + if (ret) { + shost_printk(KERN_ERR, phba->shost, "beiscsi_dev_probe-" + "Failed in be_ctrl_init\n"); + goto hba_free; + } + + spin_lock_init(&phba->io_sgl_lock); + spin_lock_init(&phba->mgmt_sgl_lock); + spin_lock_init(&phba->isr_lock); + beiscsi_get_params(phba); + ret = beiscsi_init_port(phba); + if (ret < 0) { + shost_printk(KERN_ERR, phba->shost, "beiscsi_dev_probe-" + "Failed in beiscsi_init_port\n"); + goto free_port; + } + + snprintf(phba->wq_name, sizeof(phba->wq_name), "beiscsi_q_irq%u", + phba->shost->host_no); + phba->wq = create_singlethread_workqueue(phba->wq_name); + if (!phba->wq) { + shost_printk(KERN_ERR, phba->shost, "beiscsi_dev_probe-" + "Failed to allocate work queue\n"); + goto free_twq; + } + + INIT_WORK(&phba->work_cqs, beiscsi_process_all_cqs); + + if (blk_iopoll_enabled) { + blk_iopoll_init(&phba->iopoll, be_iopoll_budget, be_iopoll); + blk_iopoll_enable(&phba->iopoll); + } + + ret = beiscsi_init_irqs(phba); + if (ret < 0) { + shost_printk(KERN_ERR, phba->shost, "beiscsi_dev_probe-" + "Failed to beiscsi_init_irqs\n"); + goto free_blkenbld; + } + ret = hwi_enable_intr(phba); + if (ret < 0) { + shost_printk(KERN_ERR, phba->shost, "beiscsi_dev_probe-" + "Failed to hwi_enable_intr\n"); + goto free_ctrlr; + } + + SE_DEBUG(DBG_LVL_8, "\n\n\n SUCCESS - DRIVER LOADED \n\n\n"); + return 0; + +free_ctrlr: + if (phba->pcidev->irq) + free_irq(phba->pcidev->irq, phba); +free_blkenbld: + destroy_workqueue(phba->wq); + if (blk_iopoll_enabled) + blk_iopoll_disable(&phba->iopoll); +free_twq: + beiscsi_clean_port(phba); + beiscsi_free_mem(phba); +free_port: + pci_free_consistent(phba->pcidev, + phba->ctrl.mbox_mem_alloced.size, + phba->ctrl.mbox_mem_alloced.va, + phba->ctrl.mbox_mem_alloced.dma); + beiscsi_unmap_pci_function(phba); +hba_free: + iscsi_host_remove(phba->shost); + pci_dev_put(phba->pcidev); + iscsi_host_free(phba->shost); +disable_pci: + pci_disable_device(pcidev); + return ret; +} + +struct iscsi_transport beiscsi_iscsi_transport = { + .owner = THIS_MODULE, + .name = DRV_NAME, + .caps = CAP_RECOVERY_L0 | CAP_HDRDGST | + CAP_MULTI_R2T | CAP_DATADGST | CAP_DATA_PATH_OFFLOAD, + .param_mask = ISCSI_MAX_RECV_DLENGTH | + ISCSI_MAX_XMIT_DLENGTH | + ISCSI_HDRDGST_EN | + ISCSI_DATADGST_EN | + ISCSI_INITIAL_R2T_EN | + ISCSI_MAX_R2T | + ISCSI_IMM_DATA_EN | + ISCSI_FIRST_BURST | + ISCSI_MAX_BURST | + ISCSI_PDU_INORDER_EN | + ISCSI_DATASEQ_INORDER_EN | + ISCSI_ERL | + ISCSI_CONN_PORT | + ISCSI_CONN_ADDRESS | + ISCSI_EXP_STATSN | + ISCSI_PERSISTENT_PORT | + ISCSI_PERSISTENT_ADDRESS | + ISCSI_TARGET_NAME | ISCSI_TPGT | + ISCSI_USERNAME | ISCSI_PASSWORD | + ISCSI_USERNAME_IN | ISCSI_PASSWORD_IN | + ISCSI_FAST_ABORT | ISCSI_ABORT_TMO | + ISCSI_LU_RESET_TMO | + ISCSI_PING_TMO | ISCSI_RECV_TMO | + ISCSI_IFACE_NAME | ISCSI_INITIATOR_NAME, + .host_param_mask = ISCSI_HOST_HWADDRESS | ISCSI_HOST_IPADDRESS | + ISCSI_HOST_INITIATOR_NAME, + .create_session = beiscsi_session_create, + .destroy_session = beiscsi_session_destroy, + .create_conn = beiscsi_conn_create, + .bind_conn = beiscsi_conn_bind, + .destroy_conn = iscsi_conn_teardown, + .set_param = beiscsi_set_param, + .get_conn_param = beiscsi_conn_get_param, + .get_session_param = iscsi_session_get_param, + .get_host_param = beiscsi_get_host_param, + .start_conn = beiscsi_conn_start, + .stop_conn = beiscsi_conn_stop, + .send_pdu = iscsi_conn_send_pdu, + .xmit_task = beiscsi_task_xmit, + .cleanup_task = beiscsi_cleanup_task, + .alloc_pdu = beiscsi_alloc_pdu, + .parse_pdu_itt = beiscsi_parse_pdu, + .get_stats = beiscsi_conn_get_stats, + .ep_connect = beiscsi_ep_connect, + .ep_poll = beiscsi_ep_poll, + .ep_disconnect = beiscsi_ep_disconnect, + .session_recovery_timedout = iscsi_session_recovery_timedout, +}; + +static struct pci_driver beiscsi_pci_driver = { + .name = DRV_NAME, + .probe = beiscsi_dev_probe, + .remove = beiscsi_remove, + .id_table = beiscsi_pci_id_table +}; + +static int __init beiscsi_module_init(void) +{ + int ret; + + beiscsi_scsi_transport = + iscsi_register_transport(&beiscsi_iscsi_transport); + if (!beiscsi_scsi_transport) { + SE_DEBUG(DBG_LVL_1, + "beiscsi_module_init - Unable to register beiscsi" + "transport.\n"); + ret = -ENOMEM; + } + SE_DEBUG(DBG_LVL_8, "In beiscsi_module_init, tt=%p \n", + &beiscsi_iscsi_transport); + + ret = pci_register_driver(&beiscsi_pci_driver); + if (ret) { + SE_DEBUG(DBG_LVL_1, + "beiscsi_module_init - Unable to register" + "beiscsi pci driver.\n"); + goto unregister_iscsi_transport; + } + return 0; + +unregister_iscsi_transport: + iscsi_unregister_transport(&beiscsi_iscsi_transport); + return ret; +} + +static void __exit beiscsi_module_exit(void) +{ + pci_unregister_driver(&beiscsi_pci_driver); + iscsi_unregister_transport(&beiscsi_iscsi_transport); +} + +module_init(beiscsi_module_init); +module_exit(beiscsi_module_exit); diff --git a/drivers/scsi/be2iscsi/be_main.h b/drivers/scsi/be2iscsi/be_main.h new file mode 100644 index 000000000000..53c9b70ac7ac --- /dev/null +++ b/drivers/scsi/be2iscsi/be_main.h @@ -0,0 +1,837 @@ +/** + * Copyright (C) 2005 - 2009 ServerEngines + * All rights reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. The full GNU General + * Public License is included in this distribution in the file called COPYING. + * + * Written by: Jayamohan Kallickal (jayamohank@serverengines.com) + * + * Contact Information: + * linux-drivers@serverengines.com + * + * ServerEngines + * 209 N. Fair Oaks Ave + * Sunnyvale, CA 94085 + * + */ + +#ifndef _BEISCSI_MAIN_ +#define _BEISCSI_MAIN_ + + +#include <linux/kernel.h> +#include <linux/pci.h> +#include <linux/in.h> +#include <linux/blk-iopoll.h> +#include <scsi/scsi.h> +#include <scsi/scsi_cmnd.h> +#include <scsi/scsi_device.h> +#include <scsi/scsi_host.h> +#include <scsi/iscsi_proto.h> +#include <scsi/libiscsi.h> +#include <scsi/scsi_transport_iscsi.h> + +#include "be.h" + + + +#define DRV_NAME "be2iscsi" +#define BUILD_STR "2.0.527.0" + +#define BE_NAME "ServerEngines BladeEngine2" \ + "Linux iSCSI Driver version" BUILD_STR +#define DRV_DESC BE_NAME " " "Driver" + +#define BE_VENDOR_ID 0x19A2 +#define BE_DEVICE_ID1 0x212 +#define OC_DEVICE_ID1 0x702 +#define OC_DEVICE_ID2 0x703 + +#define BE2_MAX_SESSIONS 64 +#define BE2_CMDS_PER_CXN 128 +#define BE2_LOGOUTS BE2_MAX_SESSIONS +#define BE2_TMFS 16 +#define BE2_NOPOUT_REQ 16 +#define BE2_ASYNCPDUS BE2_MAX_SESSIONS +#define BE2_MAX_ICDS 2048 +#define BE2_SGE 32 +#define BE2_DEFPDU_HDR_SZ 64 +#define BE2_DEFPDU_DATA_SZ 8192 +#define BE2_IO_DEPTH \ + (BE2_MAX_ICDS / 2 - (BE2_LOGOUTS + BE2_TMFS + BE2_NOPOUT_REQ)) + +#define BEISCSI_SGLIST_ELEMENTS BE2_SGE + +#define BEISCSI_MAX_CMNDS 1024 /* Max IO's per Ctrlr sht->can_queue */ +#define BEISCSI_CMD_PER_LUN 128 /* scsi_host->cmd_per_lun */ +#define BEISCSI_MAX_SECTORS 2048 /* scsi_host->max_sectors */ + +#define BEISCSI_MAX_CMD_LEN 16 /* scsi_host->max_cmd_len */ +#define BEISCSI_NUM_MAX_LUN 256 /* scsi_host->max_lun */ +#define BEISCSI_NUM_DEVICES_SUPPORTED 0x01 +#define BEISCSI_MAX_FRAGS_INIT 192 +#define BE_NUM_MSIX_ENTRIES 1 +#define MPU_EP_SEMAPHORE 0xac + +#define BE_SENSE_INFO_SIZE 258 +#define BE_ISCSI_PDU_HEADER_SIZE 64 +#define BE_MIN_MEM_SIZE 16384 + +#define IIOC_SCSI_DATA 0x05 /* Write Operation */ + +#define DBG_LVL 0x00000001 +#define DBG_LVL_1 0x00000001 +#define DBG_LVL_2 0x00000002 +#define DBG_LVL_3 0x00000004 +#define DBG_LVL_4 0x00000008 +#define DBG_LVL_5 0x00000010 +#define DBG_LVL_6 0x00000020 +#define DBG_LVL_7 0x00000040 +#define DBG_LVL_8 0x00000080 + +#define SE_DEBUG(debug_mask, fmt, args...) \ +do { \ + if (debug_mask & DBG_LVL) { \ + printk(KERN_ERR "(%s():%d):", __func__, __LINE__);\ + printk(fmt, ##args); \ + } \ +} while (0); + +/** + * hardware needs the async PDU buffers to be posted in multiples of 8 + * So have atleast 8 of them by default + */ + +#define HWI_GET_ASYNC_PDU_CTX(phwi) (phwi->phwi_ctxt->pasync_ctx) + +/********* Memory BAR register ************/ +#define PCICFG_MEMBAR_CTRL_INT_CTRL_OFFSET 0xfc +/** + * Host Interrupt Enable, if set interrupts are enabled although "PCI Interrupt + * Disable" may still globally block interrupts in addition to individual + * interrupt masks; a mechanism for the device driver to block all interrupts + * atomically without having to arbitrate for the PCI Interrupt Disable bit + * with the OS. + */ +#define MEMBAR_CTRL_INT_CTRL_HOSTINTR_MASK (1 << 29) /* bit 29 */ + +/********* ISR0 Register offset **********/ +#define CEV_ISR0_OFFSET 0xC18 +#define CEV_ISR_SIZE 4 + +/** + * Macros for reading/writing a protection domain or CSR registers + * in BladeEngine. + */ + +#define DB_TXULP0_OFFSET 0x40 +#define DB_RXULP0_OFFSET 0xA0 +/********* Event Q door bell *************/ +#define DB_EQ_OFFSET DB_CQ_OFFSET +#define DB_EQ_RING_ID_MASK 0x1FF /* bits 0 - 8 */ +/* Clear the interrupt for this eq */ +#define DB_EQ_CLR_SHIFT (9) /* bit 9 */ +/* Must be 1 */ +#define DB_EQ_EVNT_SHIFT (10) /* bit 10 */ +/* Number of event entries processed */ +#define DB_EQ_NUM_POPPED_SHIFT (16) /* bits 16 - 28 */ +/* Rearm bit */ +#define DB_EQ_REARM_SHIFT (29) /* bit 29 */ + +/********* Compl Q door bell *************/ +#define DB_CQ_OFFSET 0x120 +#define DB_CQ_RING_ID_MASK 0x3FF /* bits 0 - 9 */ +/* Number of event entries processed */ +#define DB_CQ_NUM_POPPED_SHIFT (16) /* bits 16 - 28 */ +/* Rearm bit */ +#define DB_CQ_REARM_SHIFT (29) /* bit 29 */ + +#define GET_HWI_CONTROLLER_WS(pc) (pc->phwi_ctrlr) +#define HWI_GET_DEF_BUFQ_ID(pc) (((struct hwi_controller *)\ + (GET_HWI_CONTROLLER_WS(pc)))->default_pdu_data.id) +#define HWI_GET_DEF_HDRQ_ID(pc) (((struct hwi_controller *)\ + (GET_HWI_CONTROLLER_WS(pc)))->default_pdu_hdr.id) + +#define PAGES_REQUIRED(x) \ + ((x < PAGE_SIZE) ? 1 : ((x + PAGE_SIZE - 1) / PAGE_SIZE)) + +enum be_mem_enum { + HWI_MEM_ADDN_CONTEXT, + HWI_MEM_CQ, + HWI_MEM_EQ, + HWI_MEM_WRB, + HWI_MEM_WRBH, + HWI_MEM_SGLH, /* 5 */ + HWI_MEM_SGE, + HWI_MEM_ASYNC_HEADER_BUF, + HWI_MEM_ASYNC_DATA_BUF, + HWI_MEM_ASYNC_HEADER_RING, + HWI_MEM_ASYNC_DATA_RING, /* 10 */ + HWI_MEM_ASYNC_HEADER_HANDLE, + HWI_MEM_ASYNC_DATA_HANDLE, + HWI_MEM_ASYNC_PDU_CONTEXT, + ISCSI_MEM_GLOBAL_HEADER, + SE_MEM_MAX /* 15 */ +}; + +struct be_bus_address32 { + unsigned int address_lo; + unsigned int address_hi; +}; + +struct be_bus_address64 { + unsigned long long address; +}; + +struct be_bus_address { + union { + struct be_bus_address32 a32; + struct be_bus_address64 a64; + } u; +}; + +struct mem_array { + struct be_bus_address bus_address; /* Bus address of location */ + void *virtual_address; /* virtual address to the location */ + unsigned int size; /* Size required by memory block */ +}; + +struct be_mem_descriptor { + unsigned int index; /* Index of this memory parameter */ + unsigned int category; /* type indicates cached/non-cached */ + unsigned int num_elements; /* number of elements in this + * descriptor + */ + unsigned int alignment_mask; /* Alignment mask for this block */ + unsigned int size_in_bytes; /* Size required by memory block */ + struct mem_array *mem_array; +}; + +struct sgl_handle { + unsigned int sgl_index; + struct iscsi_sge *pfrag; +}; + +struct hba_parameters { + unsigned int ios_per_ctrl; + unsigned int cxns_per_ctrl; + unsigned int asyncpdus_per_ctrl; + unsigned int icds_per_ctrl; + unsigned int num_sge_per_io; + unsigned int defpdu_hdr_sz; + unsigned int defpdu_data_sz; + unsigned int num_cq_entries; + unsigned int num_eq_entries; + unsigned int wrbs_per_cxn; + unsigned int crashmode; + unsigned int hba_num; + + unsigned int mgmt_ws_sz; + unsigned int hwi_ws_sz; + + unsigned int eto; + unsigned int ldto; + + unsigned int dbg_flags; + unsigned int num_cxn; + + unsigned int eq_timer; + /** + * These are calculated from other params. They're here + * for debug purposes + */ + unsigned int num_mcc_pages; + unsigned int num_mcc_cq_pages; + unsigned int num_cq_pages; + unsigned int num_eq_pages; + + unsigned int num_async_pdu_buf_pages; + unsigned int num_async_pdu_buf_sgl_pages; + unsigned int num_async_pdu_buf_cq_pages; + + unsigned int num_async_pdu_hdr_pages; + unsigned int num_async_pdu_hdr_sgl_pages; + unsigned int num_async_pdu_hdr_cq_pages; + + unsigned int num_sge; +}; + +struct beiscsi_hba { + struct hba_parameters params; + struct hwi_controller *phwi_ctrlr; + unsigned int mem_req[SE_MEM_MAX]; + /* PCI BAR mapped addresses */ + u8 __iomem *csr_va; /* CSR */ + u8 __iomem *db_va; /* Door Bell */ + u8 __iomem *pci_va; /* PCI Config */ + struct be_bus_address csr_pa; /* CSR */ + struct be_bus_address db_pa; /* CSR */ + struct be_bus_address pci_pa; /* CSR */ + /* PCI representation of our HBA */ + struct pci_dev *pcidev; + unsigned int state; + unsigned short asic_revision; + struct blk_iopoll iopoll; + struct be_mem_descriptor *init_mem; + + unsigned short io_sgl_alloc_index; + unsigned short io_sgl_free_index; + unsigned short io_sgl_hndl_avbl; + struct sgl_handle **io_sgl_hndl_base; + + unsigned short eh_sgl_alloc_index; + unsigned short eh_sgl_free_index; + unsigned short eh_sgl_hndl_avbl; + struct sgl_handle **eh_sgl_hndl_base; + spinlock_t io_sgl_lock; + spinlock_t mgmt_sgl_lock; + spinlock_t isr_lock; + unsigned int age; + unsigned short avlbl_cids; + unsigned short cid_alloc; + unsigned short cid_free; + struct beiscsi_conn *conn_table[BE2_MAX_SESSIONS * 2]; + struct list_head hba_queue; + unsigned short *cid_array; + struct iscsi_endpoint **ep_array; + struct Scsi_Host *shost; + struct { + /** + * group together since they are used most frequently + * for cid to cri conversion + */ + unsigned int iscsi_cid_start; + unsigned int phys_port; + + unsigned int isr_offset; + unsigned int iscsi_icd_start; + unsigned int iscsi_cid_count; + unsigned int iscsi_icd_count; + unsigned int pci_function; + + unsigned short cid_alloc; + unsigned short cid_free; + unsigned short avlbl_cids; + spinlock_t cid_lock; + } fw_config; + + u8 mac_address[ETH_ALEN]; + unsigned short todo_cq; + unsigned short todo_mcc_cq; + char wq_name[20]; + struct workqueue_struct *wq; /* The actuak work queue */ + struct work_struct work_cqs; /* The work being queued */ + struct be_ctrl_info ctrl; +}; + +struct beiscsi_session { + struct pci_pool *bhs_pool; +}; + +/** + * struct beiscsi_conn - iscsi connection structure + */ +struct beiscsi_conn { + struct iscsi_conn *conn; + struct beiscsi_hba *phba; + u32 exp_statsn; + u32 beiscsi_conn_cid; + struct beiscsi_endpoint *ep; + unsigned short login_in_progress; + struct sgl_handle *plogin_sgl_handle; + struct beiscsi_session *beiscsi_sess; +}; + +/* This structure is used by the chip */ +struct pdu_data_out { + u32 dw[12]; +}; +/** + * Pseudo amap definition in which each bit of the actual structure is defined + * as a byte: used to calculate offset/shift/mask of each field + */ +struct amap_pdu_data_out { + u8 opcode[6]; /* opcode */ + u8 rsvd0[2]; /* should be 0 */ + u8 rsvd1[7]; + u8 final_bit; /* F bit */ + u8 rsvd2[16]; + u8 ahs_length[8]; /* no AHS */ + u8 data_len_hi[8]; + u8 data_len_lo[16]; /* DataSegmentLength */ + u8 lun[64]; + u8 itt[32]; /* ITT; initiator task tag */ + u8 ttt[32]; /* TTT; valid for R2T or 0xffffffff */ + u8 rsvd3[32]; + u8 exp_stat_sn[32]; + u8 rsvd4[32]; + u8 data_sn[32]; + u8 buffer_offset[32]; + u8 rsvd5[32]; +}; + +struct be_cmd_bhs { + struct iscsi_cmd iscsi_hdr; + unsigned char pad1[16]; + struct pdu_data_out iscsi_data_pdu; + unsigned char pad2[BE_SENSE_INFO_SIZE - + sizeof(struct pdu_data_out)]; +}; + +struct beiscsi_io_task { + struct wrb_handle *pwrb_handle; + struct sgl_handle *psgl_handle; + struct beiscsi_conn *conn; + struct scsi_cmnd *scsi_cmnd; + unsigned int cmd_sn; + unsigned int flags; + unsigned short cid; + unsigned short header_len; + + struct be_cmd_bhs *cmd_bhs; + struct be_bus_address bhs_pa; + unsigned short bhs_len; +}; + +struct be_nonio_bhs { + struct iscsi_hdr iscsi_hdr; + unsigned char pad1[16]; + struct pdu_data_out iscsi_data_pdu; + unsigned char pad2[BE_SENSE_INFO_SIZE - + sizeof(struct pdu_data_out)]; +}; + +struct be_status_bhs { + struct iscsi_cmd iscsi_hdr; + unsigned char pad1[16]; + /** + * The plus 2 below is to hold the sense info length that gets + * DMA'ed by RxULP + */ + unsigned char sense_info[BE_SENSE_INFO_SIZE]; +}; + +struct iscsi_sge { + u32 dw[4]; +}; + +/** + * Pseudo amap definition in which each bit of the actual structure is defined + * as a byte: used to calculate offset/shift/mask of each field + */ +struct amap_iscsi_sge { + u8 addr_hi[32]; + u8 addr_lo[32]; + u8 sge_offset[22]; /* DWORD 2 */ + u8 rsvd0[9]; /* DWORD 2 */ + u8 last_sge; /* DWORD 2 */ + u8 len[17]; /* DWORD 3 */ + u8 rsvd1[15]; /* DWORD 3 */ +}; + +struct beiscsi_offload_params { + u32 dw[5]; +}; + +#define OFFLD_PARAMS_ERL 0x00000003 +#define OFFLD_PARAMS_DDE 0x00000004 +#define OFFLD_PARAMS_HDE 0x00000008 +#define OFFLD_PARAMS_IR2T 0x00000010 +#define OFFLD_PARAMS_IMD 0x00000020 + +/** + * Pseudo amap definition in which each bit of the actual structure is defined + * as a byte: used to calculate offset/shift/mask of each field + */ +struct amap_beiscsi_offload_params { + u8 max_burst_length[32]; + u8 max_send_data_segment_length[32]; + u8 first_burst_length[32]; + u8 erl[2]; + u8 dde[1]; + u8 hde[1]; + u8 ir2t[1]; + u8 imd[1]; + u8 pad[26]; + u8 exp_statsn[32]; +}; + +/* void hwi_complete_drvr_msgs(struct beiscsi_conn *beiscsi_conn, + struct beiscsi_hba *phba, struct sol_cqe *psol);*/ + +struct async_pdu_handle { + struct list_head link; + struct be_bus_address pa; + void *pbuffer; + unsigned int consumed; + unsigned char index; + unsigned char is_header; + unsigned short cri; + unsigned long buffer_len; +}; + +struct hwi_async_entry { + struct { + unsigned char hdr_received; + unsigned char hdr_len; + unsigned short bytes_received; + unsigned int bytes_needed; + struct list_head list; + } wait_queue; + + struct list_head header_busy_list; + struct list_head data_busy_list; +}; + +#define BE_MIN_ASYNC_ENTRIES 128 + +struct hwi_async_pdu_context { + struct { + struct be_bus_address pa_base; + void *va_base; + void *ring_base; + struct async_pdu_handle *handle_base; + + unsigned int host_write_ptr; + unsigned int ep_read_ptr; + unsigned int writables; + + unsigned int free_entries; + unsigned int busy_entries; + unsigned int buffer_size; + unsigned int num_entries; + + struct list_head free_list; + } async_header; + + struct { + struct be_bus_address pa_base; + void *va_base; + void *ring_base; + struct async_pdu_handle *handle_base; + + unsigned int host_write_ptr; + unsigned int ep_read_ptr; + unsigned int writables; + + unsigned int free_entries; + unsigned int busy_entries; + unsigned int buffer_size; + struct list_head free_list; + unsigned int num_entries; + } async_data; + + /** + * This is a varying size list! Do not add anything + * after this entry!! + */ + struct hwi_async_entry async_entry[BE_MIN_ASYNC_ENTRIES]; +}; + +#define PDUCQE_CODE_MASK 0x0000003F +#define PDUCQE_DPL_MASK 0xFFFF0000 +#define PDUCQE_INDEX_MASK 0x0000FFFF + +struct i_t_dpdu_cqe { + u32 dw[4]; +} __packed; + +/** + * Pseudo amap definition in which each bit of the actual structure is defined + * as a byte: used to calculate offset/shift/mask of each field + */ +struct amap_i_t_dpdu_cqe { + u8 db_addr_hi[32]; + u8 db_addr_lo[32]; + u8 code[6]; + u8 cid[10]; + u8 dpl[16]; + u8 index[16]; + u8 num_cons[10]; + u8 rsvd0[4]; + u8 final; + u8 valid; +} __packed; + +#define CQE_VALID_MASK 0x80000000 +#define CQE_CODE_MASK 0x0000003F +#define CQE_CID_MASK 0x0000FFC0 + +#define EQE_VALID_MASK 0x00000001 +#define EQE_MAJORCODE_MASK 0x0000000E +#define EQE_RESID_MASK 0xFFFF0000 + +struct be_eq_entry { + u32 dw[1]; +} __packed; + +/** + * Pseudo amap definition in which each bit of the actual structure is defined + * as a byte: used to calculate offset/shift/mask of each field + */ +struct amap_eq_entry { + u8 valid; /* DWORD 0 */ + u8 major_code[3]; /* DWORD 0 */ + u8 minor_code[12]; /* DWORD 0 */ + u8 resource_id[16]; /* DWORD 0 */ + +} __packed; + +struct cq_db { + u32 dw[1]; +} __packed; + +/** + * Pseudo amap definition in which each bit of the actual structure is defined + * as a byte: used to calculate offset/shift/mask of each field + */ +struct amap_cq_db { + u8 qid[10]; + u8 event[1]; + u8 rsvd0[5]; + u8 num_popped[13]; + u8 rearm[1]; + u8 rsvd1[2]; +} __packed; + +void beiscsi_process_eq(struct beiscsi_hba *phba); + + +struct iscsi_wrb { + u32 dw[16]; +} __packed; + +#define WRB_TYPE_MASK 0xF0000000 + +/** + * Pseudo amap definition in which each bit of the actual structure is defined + * as a byte: used to calculate offset/shift/mask of each field + */ +struct amap_iscsi_wrb { + u8 lun[14]; /* DWORD 0 */ + u8 lt; /* DWORD 0 */ + u8 invld; /* DWORD 0 */ + u8 wrb_idx[8]; /* DWORD 0 */ + u8 dsp; /* DWORD 0 */ + u8 dmsg; /* DWORD 0 */ + u8 undr_run; /* DWORD 0 */ + u8 over_run; /* DWORD 0 */ + u8 type[4]; /* DWORD 0 */ + u8 ptr2nextwrb[8]; /* DWORD 1 */ + u8 r2t_exp_dtl[24]; /* DWORD 1 */ + u8 sgl_icd_idx[12]; /* DWORD 2 */ + u8 rsvd0[20]; /* DWORD 2 */ + u8 exp_data_sn[32]; /* DWORD 3 */ + u8 iscsi_bhs_addr_hi[32]; /* DWORD 4 */ + u8 iscsi_bhs_addr_lo[32]; /* DWORD 5 */ + u8 cmdsn_itt[32]; /* DWORD 6 */ + u8 dif_ref_tag[32]; /* DWORD 7 */ + u8 sge0_addr_hi[32]; /* DWORD 8 */ + u8 sge0_addr_lo[32]; /* DWORD 9 */ + u8 sge0_offset[22]; /* DWORD 10 */ + u8 pbs; /* DWORD 10 */ + u8 dif_mode[2]; /* DWORD 10 */ + u8 rsvd1[6]; /* DWORD 10 */ + u8 sge0_last; /* DWORD 10 */ + u8 sge0_len[17]; /* DWORD 11 */ + u8 dif_meta_tag[14]; /* DWORD 11 */ + u8 sge0_in_ddr; /* DWORD 11 */ + u8 sge1_addr_hi[32]; /* DWORD 12 */ + u8 sge1_addr_lo[32]; /* DWORD 13 */ + u8 sge1_r2t_offset[22]; /* DWORD 14 */ + u8 rsvd2[9]; /* DWORD 14 */ + u8 sge1_last; /* DWORD 14 */ + u8 sge1_len[17]; /* DWORD 15 */ + u8 ref_sgl_icd_idx[12]; /* DWORD 15 */ + u8 rsvd3[2]; /* DWORD 15 */ + u8 sge1_in_ddr; /* DWORD 15 */ + +} __packed; + +struct wrb_handle *alloc_wrb_handle(struct beiscsi_hba *phba, unsigned int cid, + int index); +void +free_mgmt_sgl_handle(struct beiscsi_hba *phba, struct sgl_handle *psgl_handle); + +struct pdu_nop_out { + u32 dw[12]; +}; + +/** + * Pseudo amap definition in which each bit of the actual structure is defined + * as a byte: used to calculate offset/shift/mask of each field + */ +struct amap_pdu_nop_out { + u8 opcode[6]; /* opcode 0x00 */ + u8 i_bit; /* I Bit */ + u8 x_bit; /* reserved; should be 0 */ + u8 fp_bit_filler1[7]; + u8 f_bit; /* always 1 */ + u8 reserved1[16]; + u8 ahs_length[8]; /* no AHS */ + u8 data_len_hi[8]; + u8 data_len_lo[16]; /* DataSegmentLength */ + u8 lun[64]; + u8 itt[32]; /* initiator id for ping or 0xffffffff */ + u8 ttt[32]; /* target id for ping or 0xffffffff */ + u8 cmd_sn[32]; + u8 exp_stat_sn[32]; + u8 reserved5[128]; +}; + +#define PDUBASE_OPCODE_MASK 0x0000003F +#define PDUBASE_DATALENHI_MASK 0x0000FF00 +#define PDUBASE_DATALENLO_MASK 0xFFFF0000 + +struct pdu_base { + u32 dw[16]; +} __packed; + +/** + * Pseudo amap definition in which each bit of the actual structure is defined + * as a byte: used to calculate offset/shift/mask of each field + */ +struct amap_pdu_base { + u8 opcode[6]; + u8 i_bit; /* immediate bit */ + u8 x_bit; /* reserved, always 0 */ + u8 reserved1[24]; /* opcode-specific fields */ + u8 ahs_length[8]; /* length units is 4 byte words */ + u8 data_len_hi[8]; + u8 data_len_lo[16]; /* DatasegmentLength */ + u8 lun[64]; /* lun or opcode-specific fields */ + u8 itt[32]; /* initiator task tag */ + u8 reserved4[224]; +}; + +struct iscsi_target_context_update_wrb { + u32 dw[16]; +} __packed; + +/** + * Pseudo amap definition in which each bit of the actual structure is defined + * as a byte: used to calculate offset/shift/mask of each field + */ +struct amap_iscsi_target_context_update_wrb { + u8 lun[14]; /* DWORD 0 */ + u8 lt; /* DWORD 0 */ + u8 invld; /* DWORD 0 */ + u8 wrb_idx[8]; /* DWORD 0 */ + u8 dsp; /* DWORD 0 */ + u8 dmsg; /* DWORD 0 */ + u8 undr_run; /* DWORD 0 */ + u8 over_run; /* DWORD 0 */ + u8 type[4]; /* DWORD 0 */ + u8 ptr2nextwrb[8]; /* DWORD 1 */ + u8 max_burst_length[19]; /* DWORD 1 */ + u8 rsvd0[5]; /* DWORD 1 */ + u8 rsvd1[15]; /* DWORD 2 */ + u8 max_send_data_segment_length[17]; /* DWORD 2 */ + u8 first_burst_length[14]; /* DWORD 3 */ + u8 rsvd2[2]; /* DWORD 3 */ + u8 tx_wrbindex_drv_msg[8]; /* DWORD 3 */ + u8 rsvd3[5]; /* DWORD 3 */ + u8 session_state[3]; /* DWORD 3 */ + u8 rsvd4[16]; /* DWORD 4 */ + u8 tx_jumbo; /* DWORD 4 */ + u8 hde; /* DWORD 4 */ + u8 dde; /* DWORD 4 */ + u8 erl[2]; /* DWORD 4 */ + u8 domain_id[5]; /* DWORD 4 */ + u8 mode; /* DWORD 4 */ + u8 imd; /* DWORD 4 */ + u8 ir2t; /* DWORD 4 */ + u8 notpredblq[2]; /* DWORD 4 */ + u8 compltonack; /* DWORD 4 */ + u8 stat_sn[32]; /* DWORD 5 */ + u8 pad_buffer_addr_hi[32]; /* DWORD 6 */ + u8 pad_buffer_addr_lo[32]; /* DWORD 7 */ + u8 pad_addr_hi[32]; /* DWORD 8 */ + u8 pad_addr_lo[32]; /* DWORD 9 */ + u8 rsvd5[32]; /* DWORD 10 */ + u8 rsvd6[32]; /* DWORD 11 */ + u8 rsvd7[32]; /* DWORD 12 */ + u8 rsvd8[32]; /* DWORD 13 */ + u8 rsvd9[32]; /* DWORD 14 */ + u8 rsvd10[32]; /* DWORD 15 */ + +} __packed; + +struct be_ring { + u32 pages; /* queue size in pages */ + u32 id; /* queue id assigned by beklib */ + u32 num; /* number of elements in queue */ + u32 cidx; /* consumer index */ + u32 pidx; /* producer index -- not used by most rings */ + u32 item_size; /* size in bytes of one object */ + + void *va; /* The virtual address of the ring. This + * should be last to allow 32 & 64 bit debugger + * extensions to work. + */ +}; + +struct hwi_wrb_context { + struct list_head wrb_handle_list; + struct list_head wrb_handle_drvr_list; + struct wrb_handle **pwrb_handle_base; + struct wrb_handle **pwrb_handle_basestd; + struct iscsi_wrb *plast_wrb; + unsigned short alloc_index; + unsigned short free_index; + unsigned short wrb_handles_available; + unsigned short cid; +}; + +struct hwi_controller { + struct list_head io_sgl_list; + struct list_head eh_sgl_list; + struct sgl_handle *psgl_handle_base; + unsigned int wrb_mem_index; + + struct hwi_wrb_context wrb_context[BE2_MAX_SESSIONS * 2]; + struct mcc_wrb *pmcc_wrb_base; + struct be_ring default_pdu_hdr; + struct be_ring default_pdu_data; + struct hwi_context_memory *phwi_ctxt; + unsigned short cq_errors[CXN_KILLED_CMND_DATA_NOT_ON_SAME_CONN]; +}; + +enum hwh_type_enum { + HWH_TYPE_IO = 1, + HWH_TYPE_LOGOUT = 2, + HWH_TYPE_TMF = 3, + HWH_TYPE_NOP = 4, + HWH_TYPE_IO_RD = 5, + HWH_TYPE_LOGIN = 11, + HWH_TYPE_INVALID = 0xFFFFFFFF +}; + +struct wrb_handle { + enum hwh_type_enum type; + unsigned short wrb_index; + unsigned short nxt_wrb_index; + + struct iscsi_task *pio_handle; + struct iscsi_wrb *pwrb; +}; + +struct hwi_context_memory { + struct be_eq_obj be_eq; + struct be_queue_info be_cq; + struct be_queue_info be_mcc_cq; + struct be_queue_info be_mcc; + + struct be_queue_info be_def_hdrq; + struct be_queue_info be_def_dataq; + + struct be_queue_info be_wrbq[BE2_MAX_SESSIONS]; + struct be_mcc_wrb_context *pbe_mcc_context; + + struct hwi_async_pdu_context *pasync_ctx; +}; + +#endif diff --git a/drivers/scsi/be2iscsi/be_mgmt.c b/drivers/scsi/be2iscsi/be_mgmt.c new file mode 100644 index 000000000000..12e644fc746e --- /dev/null +++ b/drivers/scsi/be2iscsi/be_mgmt.c @@ -0,0 +1,321 @@ +/** + * Copyright (C) 2005 - 2009 ServerEngines + * All rights reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. The full GNU General + * Public License is included in this distribution in the file called COPYING. + * + * Written by: Jayamohan Kallickal (jayamohank@serverengines.com) + * + * Contact Information: + * linux-drivers@serverengines.com + * + * ServerEngines + * 209 N. Fair Oaks Ave + * Sunnyvale, CA 94085 + * + */ + +#include "be_mgmt.h" +#include "be_iscsi.h" + +unsigned char mgmt_get_fw_config(struct be_ctrl_info *ctrl, + struct beiscsi_hba *phba) +{ + struct be_mcc_wrb *wrb = wrb_from_mbox(&ctrl->mbox_mem); + struct be_fw_cfg *req = embedded_payload(wrb); + int status = 0; + + spin_lock(&ctrl->mbox_lock); + memset(wrb, 0, sizeof(*wrb)); + + be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0); + + be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON, + OPCODE_COMMON_QUERY_FIRMWARE_CONFIG, sizeof(*req)); + + status = be_mbox_notify(ctrl); + if (!status) { + struct be_fw_cfg *pfw_cfg; + pfw_cfg = req; + phba->fw_config.phys_port = pfw_cfg->phys_port; + phba->fw_config.iscsi_icd_start = + pfw_cfg->ulp[0].icd_base; + phba->fw_config.iscsi_icd_count = + pfw_cfg->ulp[0].icd_count; + phba->fw_config.iscsi_cid_start = + pfw_cfg->ulp[0].sq_base; + phba->fw_config.iscsi_cid_count = + pfw_cfg->ulp[0].sq_count; + } else { + shost_printk(KERN_WARNING, phba->shost, + "Failed in mgmt_get_fw_config \n"); + } + + spin_unlock(&ctrl->mbox_lock); + return status; +} + +unsigned char mgmt_check_supported_fw(struct be_ctrl_info *ctrl) +{ + struct be_dma_mem nonemb_cmd; + struct be_mcc_wrb *wrb = wrb_from_mbox(&ctrl->mbox_mem); + struct be_mgmt_controller_attributes *req; + struct be_sge *sge = nonembedded_sgl(wrb); + int status = 0; + + nonemb_cmd.va = pci_alloc_consistent(ctrl->pdev, + sizeof(struct be_mgmt_controller_attributes), + &nonemb_cmd.dma); + if (nonemb_cmd.va == NULL) { + SE_DEBUG(DBG_LVL_1, + "Failed to allocate memory for mgmt_check_supported_fw" + "\n"); + return -1; + } + nonemb_cmd.size = sizeof(struct be_mgmt_controller_attributes); + req = nonemb_cmd.va; + spin_lock(&ctrl->mbox_lock); + memset(wrb, 0, sizeof(*wrb)); + be_wrb_hdr_prepare(wrb, sizeof(*req), false, 1); + be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON, + OPCODE_COMMON_GET_CNTL_ATTRIBUTES, sizeof(*req)); + sge->pa_hi = cpu_to_le32(upper_32_bits(nonemb_cmd.dma)); + sge->pa_lo = cpu_to_le32(nonemb_cmd.dma & 0xFFFFFFFF); + sge->len = cpu_to_le32(nonemb_cmd.size); + + status = be_mbox_notify(ctrl); + if (!status) { + struct be_mgmt_controller_attributes_resp *resp = nonemb_cmd.va; + SE_DEBUG(DBG_LVL_8, "Firmware version of CMD: %s\n", + resp->params.hba_attribs.flashrom_version_string); + SE_DEBUG(DBG_LVL_8, "Firmware version is : %s\n", + resp->params.hba_attribs.firmware_version_string); + SE_DEBUG(DBG_LVL_8, + "Developer Build, not performing version check...\n"); + + } else + SE_DEBUG(DBG_LVL_1, " Failed in mgmt_check_supported_fw\n"); + if (nonemb_cmd.va) + pci_free_consistent(ctrl->pdev, nonemb_cmd.size, + nonemb_cmd.va, nonemb_cmd.dma); + + spin_unlock(&ctrl->mbox_lock); + return status; +} + +unsigned char mgmt_epfw_cleanup(struct beiscsi_hba *phba, unsigned short chute) +{ + struct be_ctrl_info *ctrl = &phba->ctrl; + struct be_mcc_wrb *wrb = wrb_from_mbox(&ctrl->mbox_mem); + struct iscsi_cleanup_req *req = embedded_payload(wrb); + int status = 0; + + spin_lock(&ctrl->mbox_lock); + memset(wrb, 0, sizeof(*wrb)); + + be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0); + be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_ISCSI, + OPCODE_COMMON_ISCSI_CLEANUP, sizeof(*req)); + + req->chute = chute; + req->hdr_ring_id = 0; + req->data_ring_id = 0; + + status = be_mbox_notify(ctrl); + if (status) + shost_printk(KERN_WARNING, phba->shost, + " mgmt_epfw_cleanup , FAILED\n"); + spin_unlock(&ctrl->mbox_lock); + return status; +} + +unsigned char mgmt_invalidate_icds(struct beiscsi_hba *phba, + unsigned int icd, unsigned int cid) +{ + struct be_dma_mem nonemb_cmd; + struct be_ctrl_info *ctrl = &phba->ctrl; + struct be_mcc_wrb *wrb = wrb_from_mbox(&ctrl->mbox_mem); + struct be_sge *sge = nonembedded_sgl(wrb); + struct invalidate_commands_params_in *req; + int status = 0; + + nonemb_cmd.va = pci_alloc_consistent(ctrl->pdev, + sizeof(struct invalidate_commands_params_in), + &nonemb_cmd.dma); + if (nonemb_cmd.va == NULL) { + SE_DEBUG(DBG_LVL_1, + "Failed to allocate memory for" + "mgmt_invalidate_icds \n"); + return -1; + } + nonemb_cmd.size = sizeof(struct invalidate_commands_params_in); + req = nonemb_cmd.va; + spin_lock(&ctrl->mbox_lock); + memset(wrb, 0, sizeof(*wrb)); + + be_wrb_hdr_prepare(wrb, sizeof(*req), false, 1); + be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_ISCSI, + OPCODE_COMMON_ISCSI_ERROR_RECOVERY_INVALIDATE_COMMANDS, + sizeof(*req)); + req->ref_handle = 0; + req->cleanup_type = CMD_ISCSI_COMMAND_INVALIDATE; + req->icd_count = 0; + req->table[req->icd_count].icd = icd; + req->table[req->icd_count].cid = cid; + sge->pa_hi = cpu_to_le32(upper_32_bits(nonemb_cmd.dma)); + sge->pa_lo = cpu_to_le32(nonemb_cmd.dma & 0xFFFFFFFF); + sge->len = cpu_to_le32(nonemb_cmd.size); + + status = be_mbox_notify(ctrl); + if (status) + SE_DEBUG(DBG_LVL_1, "ICDS Invalidation Failed\n"); + spin_unlock(&ctrl->mbox_lock); + if (nonemb_cmd.va) + pci_free_consistent(ctrl->pdev, nonemb_cmd.size, + nonemb_cmd.va, nonemb_cmd.dma); + return status; +} + +unsigned char mgmt_invalidate_connection(struct beiscsi_hba *phba, + struct beiscsi_endpoint *beiscsi_ep, + unsigned short cid, + unsigned short issue_reset, + unsigned short savecfg_flag) +{ + struct be_ctrl_info *ctrl = &phba->ctrl; + struct be_mcc_wrb *wrb = wrb_from_mbox(&ctrl->mbox_mem); + struct iscsi_invalidate_connection_params_in *req = + embedded_payload(wrb); + int status = 0; + + spin_lock(&ctrl->mbox_lock); + memset(wrb, 0, sizeof(*wrb)); + + be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0); + be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_ISCSI_INI, + OPCODE_ISCSI_INI_DRIVER_INVALIDATE_CONNECTION, + sizeof(*req)); + req->session_handle = beiscsi_ep->fw_handle; + req->cid = cid; + if (issue_reset) + req->cleanup_type = CMD_ISCSI_CONNECTION_ISSUE_TCP_RST; + else + req->cleanup_type = CMD_ISCSI_CONNECTION_INVALIDATE; + req->save_cfg = savecfg_flag; + status = be_mbox_notify(ctrl); + if (status) + SE_DEBUG(DBG_LVL_1, "Invalidation Failed\n"); + + spin_unlock(&ctrl->mbox_lock); + return status; +} + +unsigned char mgmt_upload_connection(struct beiscsi_hba *phba, + unsigned short cid, unsigned int upload_flag) +{ + struct be_ctrl_info *ctrl = &phba->ctrl; + struct be_mcc_wrb *wrb = wrb_from_mbox(&ctrl->mbox_mem); + struct tcp_upload_params_in *req = embedded_payload(wrb); + int status = 0; + + spin_lock(&ctrl->mbox_lock); + memset(wrb, 0, sizeof(*wrb)); + + be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0); + be_cmd_hdr_prepare(&req->hdr, CMD_COMMON_TCP_UPLOAD, + OPCODE_COMMON_TCP_UPLOAD, sizeof(*req)); + req->id = (unsigned short)cid; + req->upload_type = (unsigned char)upload_flag; + status = be_mbox_notify(ctrl); + if (status) + SE_DEBUG(DBG_LVL_1, "mgmt_upload_connection Failed\n"); + spin_unlock(&ctrl->mbox_lock); + return status; +} + +int mgmt_open_connection(struct beiscsi_hba *phba, + struct sockaddr *dst_addr, + struct beiscsi_endpoint *beiscsi_ep) +{ + struct hwi_controller *phwi_ctrlr; + struct hwi_context_memory *phwi_context; + struct sockaddr_in *daddr_in = (struct sockaddr_in *)dst_addr; + struct sockaddr_in6 *daddr_in6 = (struct sockaddr_in6 *)dst_addr; + struct be_ctrl_info *ctrl = &phba->ctrl; + struct be_mcc_wrb *wrb = wrb_from_mbox(&ctrl->mbox_mem); + struct tcp_connect_and_offload_in *req = embedded_payload(wrb); + unsigned short def_hdr_id; + unsigned short def_data_id; + struct phys_addr template_address = { 0, 0 }; + struct phys_addr *ptemplate_address; + int status = 0; + unsigned short cid = beiscsi_ep->ep_cid; + + phwi_ctrlr = phba->phwi_ctrlr; + phwi_context = phwi_ctrlr->phwi_ctxt; + def_hdr_id = (unsigned short)HWI_GET_DEF_HDRQ_ID(phba); + def_data_id = (unsigned short)HWI_GET_DEF_BUFQ_ID(phba); + + ptemplate_address = &template_address; + ISCSI_GET_PDU_TEMPLATE_ADDRESS(phba, ptemplate_address); + spin_lock(&ctrl->mbox_lock); + memset(wrb, 0, sizeof(*wrb)); + + be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0); + be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_ISCSI, + OPCODE_COMMON_ISCSI_TCP_CONNECT_AND_OFFLOAD, + sizeof(*req)); + if (dst_addr->sa_family == PF_INET) { + __be32 s_addr = daddr_in->sin_addr.s_addr; + req->ip_address.ip_type = BE2_IPV4; + req->ip_address.ip_address[0] = s_addr & 0x000000ff; + req->ip_address.ip_address[1] = (s_addr & 0x0000ff00) >> 8; + req->ip_address.ip_address[2] = (s_addr & 0x00ff0000) >> 16; + req->ip_address.ip_address[3] = (s_addr & 0xff000000) >> 24; + req->tcp_port = ntohs(daddr_in->sin_port); + beiscsi_ep->dst_addr = daddr_in->sin_addr.s_addr; + beiscsi_ep->dst_tcpport = ntohs(daddr_in->sin_port); + beiscsi_ep->ip_type = BE2_IPV4; + } else if (dst_addr->sa_family == PF_INET6) { + req->ip_address.ip_type = BE2_IPV6; + memcpy(&req->ip_address.ip_address, + &daddr_in6->sin6_addr.in6_u.u6_addr8, 16); + req->tcp_port = ntohs(daddr_in6->sin6_port); + beiscsi_ep->dst_tcpport = ntohs(daddr_in6->sin6_port); + memcpy(&beiscsi_ep->dst6_addr, + &daddr_in6->sin6_addr.in6_u.u6_addr8, 16); + beiscsi_ep->ip_type = BE2_IPV6; + } else{ + shost_printk(KERN_ERR, phba->shost, "unknown addr family %d\n", + dst_addr->sa_family); + spin_unlock(&ctrl->mbox_lock); + return -EINVAL; + + } + req->cid = cid; + req->cq_id = phwi_context->be_cq.id; + req->defq_id = def_hdr_id; + req->hdr_ring_id = def_hdr_id; + req->data_ring_id = def_data_id; + req->do_offload = 1; + req->dataout_template_pa.lo = ptemplate_address->lo; + req->dataout_template_pa.hi = ptemplate_address->hi; + status = be_mbox_notify(ctrl); + if (!status) { + struct iscsi_endpoint *ep; + struct tcp_connect_and_offload_out *ptcpcnct_out = + embedded_payload(wrb); + + ep = phba->ep_array[ptcpcnct_out->cid]; + beiscsi_ep = ep->dd_data; + beiscsi_ep->fw_handle = 0; + beiscsi_ep->cid_vld = 1; + SE_DEBUG(DBG_LVL_8, "mgmt_open_connection Success\n"); + } else + SE_DEBUG(DBG_LVL_1, "mgmt_open_connection Failed\n"); + spin_unlock(&ctrl->mbox_lock); + return status; +} diff --git a/drivers/scsi/be2iscsi/be_mgmt.h b/drivers/scsi/be2iscsi/be_mgmt.h new file mode 100644 index 000000000000..00e816ee8070 --- /dev/null +++ b/drivers/scsi/be2iscsi/be_mgmt.h @@ -0,0 +1,249 @@ +/** + * Copyright (C) 2005 - 2009 ServerEngines + * All rights reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. The full GNU General + * Public License is included in this distribution in the file called COPYING. + * + * Written by: Jayamohan Kallickal (jayamohank@serverengines.com) + * + * Contact Information: + * linux-drivers@serverengines.com + * + * ServerEngines + * 209 N. Fair Oaks Ave + * Sunnyvale, CA 94085 + * + */ + +#ifndef _BEISCSI_MGMT_ +#define _BEISCSI_MGMT_ + +#include <linux/types.h> +#include <linux/list.h> +#include "be_iscsi.h" +#include "be_main.h" + +/** + * Pseudo amap definition in which each bit of the actual structure is defined + * as a byte: used to calculate offset/shift/mask of each field + */ +struct amap_mcc_sge { + u8 pa_lo[32]; /* dword 0 */ + u8 pa_hi[32]; /* dword 1 */ + u8 length[32]; /* DWORD 2 */ +} __packed; + +/** + * Pseudo amap definition in which each bit of the actual structure is defined + * as a byte: used to calculate offset/shift/mask of each field + */ +struct amap_mcc_wrb_payload { + union { + struct amap_mcc_sge sgl[19]; + u8 embedded[59 * 32]; /* DWORDS 57 to 115 */ + } u; +} __packed; + +/** + * Pseudo amap definition in which each bit of the actual structure is defined + * as a byte: used to calculate offset/shift/mask of each field + */ +struct amap_mcc_wrb { + u8 embedded; /* DWORD 0 */ + u8 rsvd0[2]; /* DWORD 0 */ + u8 sge_count[5]; /* DWORD 0 */ + u8 rsvd1[16]; /* DWORD 0 */ + u8 special[8]; /* DWORD 0 */ + u8 payload_length[32]; + u8 tag[64]; /* DWORD 2 */ + u8 rsvd2[32]; /* DWORD 4 */ + struct amap_mcc_wrb_payload payload; +}; + +struct mcc_sge { + u32 pa_lo; /* dword 0 */ + u32 pa_hi; /* dword 1 */ + u32 length; /* DWORD 2 */ +} __packed; + +struct mcc_wrb_payload { + union { + struct mcc_sge sgl[19]; + u32 embedded[59]; /* DWORDS 57 to 115 */ + } u; +} __packed; + +#define MCC_WRB_EMBEDDED_MASK 0x00000001 + +struct mcc_wrb { + u32 dw[0]; /* DWORD 0 */ + u32 payload_length; + u32 tag[2]; /* DWORD 2 */ + u32 rsvd2[1]; /* DWORD 4 */ + struct mcc_wrb_payload payload; +}; + +unsigned char mgmt_epfw_cleanup(struct beiscsi_hba *phba, unsigned short chute); +int mgmt_open_connection(struct beiscsi_hba *phba, struct sockaddr *dst_addr, + struct beiscsi_endpoint *beiscsi_ep); + +unsigned char mgmt_upload_connection(struct beiscsi_hba *phba, + unsigned short cid, + unsigned int upload_flag); +unsigned char mgmt_invalidate_icds(struct beiscsi_hba *phba, + unsigned int icd, unsigned int cid); + +struct iscsi_invalidate_connection_params_in { + struct be_cmd_req_hdr hdr; + unsigned int session_handle; + unsigned short cid; + unsigned short unused; + unsigned short cleanup_type; + unsigned short save_cfg; +} __packed; + +struct iscsi_invalidate_connection_params_out { + unsigned int session_handle; + unsigned short cid; + unsigned short unused; +} __packed; + +union iscsi_invalidate_connection_params { + struct iscsi_invalidate_connection_params_in request; + struct iscsi_invalidate_connection_params_out response; +} __packed; + +struct invalidate_command_table { + unsigned short icd; + unsigned short cid; +} __packed; + +struct invalidate_commands_params_in { + struct be_cmd_req_hdr hdr; + unsigned int ref_handle; + unsigned int icd_count; + struct invalidate_command_table table[128]; + unsigned short cleanup_type; + unsigned short unused; +} __packed; + +struct invalidate_commands_params_out { + unsigned int ref_handle; + unsigned int icd_count; + unsigned int icd_status[128]; +} __packed; + +union invalidate_commands_params { + struct invalidate_commands_params_in request; + struct invalidate_commands_params_out response; +} __packed; + +struct mgmt_hba_attributes { + u8 flashrom_version_string[32]; + u8 manufacturer_name[32]; + u32 supported_modes; + u8 seeprom_version_lo; + u8 seeprom_version_hi; + u8 rsvd0[2]; + u32 fw_cmd_data_struct_version; + u32 ep_fw_data_struct_version; + u32 future_reserved[12]; + u32 default_extended_timeout; + u8 controller_model_number[32]; + u8 controller_description[64]; + u8 controller_serial_number[32]; + u8 ip_version_string[32]; + u8 firmware_version_string[32]; + u8 bios_version_string[32]; + u8 redboot_version_string[32]; + u8 driver_version_string[32]; + u8 fw_on_flash_version_string[32]; + u32 functionalities_supported; + u16 max_cdblength; + u8 asic_revision; + u8 generational_guid[16]; + u8 hba_port_count; + u16 default_link_down_timeout; + u8 iscsi_ver_min_max; + u8 multifunction_device; + u8 cache_valid; + u8 hba_status; + u8 max_domains_supported; + u8 phy_port; + u32 firmware_post_status; + u32 hba_mtu[8]; + u32 future_u32[4]; +} __packed; + +struct mgmt_controller_attributes { + struct mgmt_hba_attributes hba_attribs; + u16 pci_vendor_id; + u16 pci_device_id; + u16 pci_sub_vendor_id; + u16 pci_sub_system_id; + u8 pci_bus_number; + u8 pci_device_number; + u8 pci_function_number; + u8 interface_type; + u64 unique_identifier; + u8 netfilters; + u8 rsvd0[3]; + u8 future_u32[4]; +} __packed; + +struct be_mgmt_controller_attributes { + struct be_cmd_req_hdr hdr; + struct mgmt_controller_attributes params; +} __packed; + +struct be_mgmt_controller_attributes_resp { + struct be_cmd_resp_hdr hdr; + struct mgmt_controller_attributes params; +} __packed; + +/* configuration management */ + +#define GET_MGMT_CONTROLLER_WS(phba) (phba->pmgmt_ws) + +/* MGMT CMD flags */ + +#define MGMT_CMDH_FREE (1<<0) + +/* --- MGMT_ERROR_CODES --- */ +/* Error Codes returned in the status field of the CMD response header */ +#define MGMT_STATUS_SUCCESS 0 /* The CMD completed without errors */ +#define MGMT_STATUS_FAILED 1 /* Error status in the Status field of */ + /* the CMD_RESPONSE_HEADER */ + +#define ISCSI_GET_PDU_TEMPLATE_ADDRESS(pc, pa) {\ + pa->lo = phba->init_mem[ISCSI_MEM_GLOBAL_HEADER].mem_array[0].\ + bus_address.u.a32.address_lo; \ + pa->hi = phba->init_mem[ISCSI_MEM_GLOBAL_HEADER].mem_array[0].\ + bus_address.u.a32.address_hi; \ +} + +struct beiscsi_endpoint { + struct beiscsi_hba *phba; + struct beiscsi_sess *sess; + struct beiscsi_conn *conn; + unsigned short ip_type; + char dst6_addr[ISCSI_ADDRESS_BUF_LEN]; + unsigned long dst_addr; + unsigned short ep_cid; + unsigned int fw_handle; + u16 dst_tcpport; + u16 cid_vld; +}; + +unsigned char mgmt_get_fw_config(struct be_ctrl_info *ctrl, + struct beiscsi_hba *phba); + +unsigned char mgmt_invalidate_connection(struct beiscsi_hba *phba, + struct beiscsi_endpoint *beiscsi_ep, + unsigned short cid, + unsigned short issue_reset, + unsigned short savecfg_flag); +#endif diff --git a/drivers/scsi/bfa/Makefile b/drivers/scsi/bfa/Makefile new file mode 100644 index 000000000000..1d6009490d1c --- /dev/null +++ b/drivers/scsi/bfa/Makefile @@ -0,0 +1,15 @@ +obj-$(CONFIG_SCSI_BFA_FC) := bfa.o + +bfa-y := bfad.o bfad_intr.o bfad_os.o bfad_im.o bfad_attr.o bfad_fwimg.o + +bfa-y += bfa_core.o bfa_ioc.o bfa_iocfc.o bfa_fcxp.o bfa_lps.o +bfa-y += bfa_hw_cb.o bfa_hw_ct.o bfa_intr.o bfa_timer.o bfa_rport.o +bfa-y += bfa_fcport.o bfa_port.o bfa_uf.o bfa_sgpg.o bfa_module.o bfa_ioim.o +bfa-y += bfa_itnim.o bfa_fcpim.o bfa_tskim.o bfa_log.o bfa_log_module.o +bfa-y += bfa_csdebug.o bfa_sm.o plog.o + +bfa-y += fcbuild.o fabric.o fcpim.o vfapi.o fcptm.o bfa_fcs.o bfa_fcs_port.o +bfa-y += bfa_fcs_uf.o bfa_fcs_lport.o fab.o fdmi.o ms.o ns.o scn.o loop.o +bfa-y += lport_api.o n2n.o rport.o rport_api.o rport_ftrs.o vport.o + +ccflags-y := -I$(obj) -I$(obj)/include -I$(obj)/include/cna diff --git a/drivers/scsi/bfa/bfa_callback_priv.h b/drivers/scsi/bfa/bfa_callback_priv.h new file mode 100644 index 000000000000..1e3265c9f7d4 --- /dev/null +++ b/drivers/scsi/bfa/bfa_callback_priv.h @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2005-2009 Brocade Communications Systems, Inc. + * All rights reserved + * www.brocade.com + * + * Linux driver for Brocade Fibre Channel Host Bus Adapter. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (GPL) Version 2 as + * published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#ifndef __BFA_CALLBACK_PRIV_H__ +#define __BFA_CALLBACK_PRIV_H__ + +#include <cs/bfa_q.h> + +typedef void (*bfa_cb_cbfn_t) (void *cbarg, bfa_boolean_t complete); + +/** + * Generic BFA callback element. + */ +struct bfa_cb_qe_s { + struct list_head qe; + bfa_cb_cbfn_t cbfn; + bfa_boolean_t once; + u32 rsvd; + void *cbarg; +}; + +#define bfa_cb_queue(__bfa, __hcb_qe, __cbfn, __cbarg) do { \ + (__hcb_qe)->cbfn = (__cbfn); \ + (__hcb_qe)->cbarg = (__cbarg); \ + list_add_tail(&(__hcb_qe)->qe, &(__bfa)->comp_q); \ +} while (0) + +#define bfa_cb_dequeue(__hcb_qe) list_del(&(__hcb_qe)->qe) + +#define bfa_cb_queue_once(__bfa, __hcb_qe, __cbfn, __cbarg) do { \ + (__hcb_qe)->cbfn = (__cbfn); \ + (__hcb_qe)->cbarg = (__cbarg); \ + if (!(__hcb_qe)->once) { \ + list_add_tail((__hcb_qe), &(__bfa)->comp_q); \ + (__hcb_qe)->once = BFA_TRUE; \ + } \ +} while (0) + +#define bfa_cb_queue_done(__hcb_qe) do { \ + (__hcb_qe)->once = BFA_FALSE; \ +} while (0) + +#endif /* __BFA_CALLBACK_PRIV_H__ */ diff --git a/drivers/scsi/bfa/bfa_cb_ioim_macros.h b/drivers/scsi/bfa/bfa_cb_ioim_macros.h new file mode 100644 index 000000000000..0050c838c358 --- /dev/null +++ b/drivers/scsi/bfa/bfa_cb_ioim_macros.h @@ -0,0 +1,205 @@ +/* + * Copyright (c) 2005-2009 Brocade Communications Systems, Inc. + * All rights reserved + * www.brocade.com + * + * Linux driver for Brocade Fibre Channel Host Bus Adapter. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (GPL) Version 2 as + * published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +/** + * bfa_cb_ioim_macros.h BFA IOIM driver interface macros. + */ + +#ifndef __BFA_HCB_IOIM_MACROS_H__ +#define __BFA_HCB_IOIM_MACROS_H__ + +#include <bfa_os_inc.h> +/* + * #include <linux/dma-mapping.h> + * + * #include <scsi/scsi.h> #include <scsi/scsi_cmnd.h> #include + * <scsi/scsi_device.h> #include <scsi/scsi_host.h> + */ +#include "bfad_im_compat.h" + +/* + * task attribute values in FCP-2 FCP_CMND IU + */ +#define SIMPLE_Q 0 +#define HEAD_OF_Q 1 +#define ORDERED_Q 2 +#define ACA_Q 4 +#define UNTAGGED 5 + +static inline lun_t +bfad_int_to_lun(u32 luno) +{ + union { + u16 scsi_lun[4]; + lun_t bfa_lun; + } lun; + + lun.bfa_lun = 0; + lun.scsi_lun[0] = bfa_os_htons(luno); + + return (lun.bfa_lun); +} + +/** + * Get LUN for the I/O request + */ +#define bfa_cb_ioim_get_lun(__dio) \ + bfad_int_to_lun(((struct scsi_cmnd *)__dio)->device->lun) + +/** + * Get CDB for the I/O request + */ +static inline u8 * +bfa_cb_ioim_get_cdb(struct bfad_ioim_s *dio) +{ + struct scsi_cmnd *cmnd = (struct scsi_cmnd *)dio; + + return ((u8 *) cmnd->cmnd); +} + +/** + * Get I/O direction (read/write) for the I/O request + */ +static inline enum fcp_iodir +bfa_cb_ioim_get_iodir(struct bfad_ioim_s *dio) +{ + struct scsi_cmnd *cmnd = (struct scsi_cmnd *)dio; + enum dma_data_direction dmadir; + + dmadir = cmnd->sc_data_direction; + if (dmadir == DMA_TO_DEVICE) + return FCP_IODIR_WRITE; + else if (dmadir == DMA_FROM_DEVICE) + return FCP_IODIR_READ; + else + return FCP_IODIR_NONE; +} + +/** + * Get IO size in bytes for the I/O request + */ +static inline u32 +bfa_cb_ioim_get_size(struct bfad_ioim_s *dio) +{ + struct scsi_cmnd *cmnd = (struct scsi_cmnd *)dio; + + return (scsi_bufflen(cmnd)); +} + +/** + * Get timeout for the I/O request + */ +static inline u8 +bfa_cb_ioim_get_timeout(struct bfad_ioim_s *dio) +{ + struct scsi_cmnd *cmnd = (struct scsi_cmnd *)dio; + /* + * TBD: need a timeout for scsi passthru + */ + if (cmnd->device->host == NULL) + return 4; + + return 0; +} + +/** + * Get SG element for the I/O request given the SG element index + */ +static inline union bfi_addr_u +bfa_cb_ioim_get_sgaddr(struct bfad_ioim_s *dio, int sgeid) +{ + struct scsi_cmnd *cmnd = (struct scsi_cmnd *)dio; + struct scatterlist *sge; + u64 addr; + + sge = (struct scatterlist *)scsi_sglist(cmnd) + sgeid; + addr = (u64) sg_dma_address(sge); + + return (*(union bfi_addr_u *) &addr); +} + +static inline u32 +bfa_cb_ioim_get_sglen(struct bfad_ioim_s *dio, int sgeid) +{ + struct scsi_cmnd *cmnd = (struct scsi_cmnd *)dio; + struct scatterlist *sge; + u32 len; + + sge = (struct scatterlist *)scsi_sglist(cmnd) + sgeid; + len = sg_dma_len(sge); + + return len; +} + +/** + * Get Command Reference Number for the I/O request. 0 if none. + */ +static inline u8 +bfa_cb_ioim_get_crn(struct bfad_ioim_s *dio) +{ + return 0; +} + +/** + * Get SAM-3 priority for the I/O request. 0 is default. + */ +static inline u8 +bfa_cb_ioim_get_priority(struct bfad_ioim_s *dio) +{ + return 0; +} + +/** + * Get task attributes for the I/O request. Default is FCP_TASK_ATTR_SIMPLE(0). + */ +static inline u8 +bfa_cb_ioim_get_taskattr(struct bfad_ioim_s *dio) +{ + struct scsi_cmnd *cmnd = (struct scsi_cmnd *)dio; + u8 task_attr = UNTAGGED; + + if (cmnd->device->tagged_supported) { + switch (cmnd->tag) { + case HEAD_OF_QUEUE_TAG: + task_attr = HEAD_OF_Q; + break; + case ORDERED_QUEUE_TAG: + task_attr = ORDERED_Q; + break; + default: + task_attr = SIMPLE_Q; + break; + } + } + + return task_attr; +} + +/** + * Get CDB length in bytes for the I/O request. Default is FCP_CMND_CDB_LEN(16). + */ +static inline u8 +bfa_cb_ioim_get_cdblen(struct bfad_ioim_s *dio) +{ + struct scsi_cmnd *cmnd = (struct scsi_cmnd *)dio; + + return (cmnd->cmd_len); +} + + + +#endif /* __BFA_HCB_IOIM_MACROS_H__ */ diff --git a/drivers/scsi/bfa/bfa_cee.c b/drivers/scsi/bfa/bfa_cee.c new file mode 100644 index 000000000000..7a959c34e789 --- /dev/null +++ b/drivers/scsi/bfa/bfa_cee.c @@ -0,0 +1,492 @@ +/* + * Copyright (c) 2005-2009 Brocade Communications Systems, Inc. + * All rights reserved + * www.brocade.com + * + * Linux driver for Brocade Fibre Channel Host Bus Adapter. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (GPL) Version 2 as + * published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#include <defs/bfa_defs_cee.h> +#include <cs/bfa_trc.h> +#include <cs/bfa_log.h> +#include <cs/bfa_debug.h> +#include <cee/bfa_cee.h> +#include <bfi/bfi_cee.h> +#include <bfi/bfi.h> +#include <bfa_ioc.h> +#include <cna/bfa_cna_trcmod.h> + +BFA_TRC_FILE(CNA, CEE); + +#define bfa_ioc_portid(__ioc) ((__ioc)->port_id) +#define bfa_lpuid(__arg) bfa_ioc_portid(&(__arg)->ioc) + +static void bfa_cee_format_lldp_cfg(struct bfa_cee_lldp_cfg_s *lldp_cfg); +static void bfa_cee_format_dcbcx_stats(struct bfa_cee_dcbx_stats_s + *dcbcx_stats); +static void bfa_cee_format_lldp_stats(struct bfa_cee_lldp_stats_s + *lldp_stats); +static void bfa_cee_format_cfg_stats(struct bfa_cee_cfg_stats_s *cfg_stats); +static void bfa_cee_format_cee_cfg(void *buffer); +static void bfa_cee_format_cee_stats(void *buffer); + +static void +bfa_cee_format_cee_stats(void *buffer) +{ + struct bfa_cee_stats_s *cee_stats = buffer; + bfa_cee_format_dcbcx_stats(&cee_stats->dcbx_stats); + bfa_cee_format_lldp_stats(&cee_stats->lldp_stats); + bfa_cee_format_cfg_stats(&cee_stats->cfg_stats); +} + +static void +bfa_cee_format_cee_cfg(void *buffer) +{ + struct bfa_cee_attr_s *cee_cfg = buffer; + bfa_cee_format_lldp_cfg(&cee_cfg->lldp_remote); +} + +static void +bfa_cee_format_dcbcx_stats(struct bfa_cee_dcbx_stats_s *dcbcx_stats) +{ + dcbcx_stats->subtlvs_unrecognized = + bfa_os_ntohl(dcbcx_stats->subtlvs_unrecognized); + dcbcx_stats->negotiation_failed = + bfa_os_ntohl(dcbcx_stats->negotiation_failed); + dcbcx_stats->remote_cfg_changed = + bfa_os_ntohl(dcbcx_stats->remote_cfg_changed); + dcbcx_stats->tlvs_received = bfa_os_ntohl(dcbcx_stats->tlvs_received); + dcbcx_stats->tlvs_invalid = bfa_os_ntohl(dcbcx_stats->tlvs_invalid); + dcbcx_stats->seqno = bfa_os_ntohl(dcbcx_stats->seqno); + dcbcx_stats->ackno = bfa_os_ntohl(dcbcx_stats->ackno); + dcbcx_stats->recvd_seqno = bfa_os_ntohl(dcbcx_stats->recvd_seqno); + dcbcx_stats->recvd_ackno = bfa_os_ntohl(dcbcx_stats->recvd_ackno); +} + +static void +bfa_cee_format_lldp_stats(struct bfa_cee_lldp_stats_s *lldp_stats) +{ + lldp_stats->frames_transmitted = + bfa_os_ntohl(lldp_stats->frames_transmitted); + lldp_stats->frames_aged_out = bfa_os_ntohl(lldp_stats->frames_aged_out); + lldp_stats->frames_discarded = + bfa_os_ntohl(lldp_stats->frames_discarded); + lldp_stats->frames_in_error = bfa_os_ntohl(lldp_stats->frames_in_error); + lldp_stats->frames_rcvd = bfa_os_ntohl(lldp_stats->frames_rcvd); + lldp_stats->tlvs_discarded = bfa_os_ntohl(lldp_stats->tlvs_discarded); + lldp_stats->tlvs_unrecognized = + bfa_os_ntohl(lldp_stats->tlvs_unrecognized); +} + +static void +bfa_cee_format_cfg_stats(struct bfa_cee_cfg_stats_s *cfg_stats) +{ + cfg_stats->cee_status_down = bfa_os_ntohl(cfg_stats->cee_status_down); + cfg_stats->cee_status_up = bfa_os_ntohl(cfg_stats->cee_status_up); + cfg_stats->cee_hw_cfg_changed = + bfa_os_ntohl(cfg_stats->cee_hw_cfg_changed); + cfg_stats->recvd_invalid_cfg = + bfa_os_ntohl(cfg_stats->recvd_invalid_cfg); +} + +static void +bfa_cee_format_lldp_cfg(struct bfa_cee_lldp_cfg_s *lldp_cfg) +{ + lldp_cfg->time_to_interval = bfa_os_ntohs(lldp_cfg->time_to_interval); + lldp_cfg->enabled_system_cap = + bfa_os_ntohs(lldp_cfg->enabled_system_cap); +} + +/** + * bfa_cee_attr_meminfo() + * + * + * @param[in] void + * + * @return Size of DMA region + */ +static u32 +bfa_cee_attr_meminfo(void) +{ + return BFA_ROUNDUP(sizeof(struct bfa_cee_attr_s), BFA_DMA_ALIGN_SZ); +} + +/** + * bfa_cee_stats_meminfo() + * + * + * @param[in] void + * + * @return Size of DMA region + */ +static u32 +bfa_cee_stats_meminfo(void) +{ + return BFA_ROUNDUP(sizeof(struct bfa_cee_stats_s), BFA_DMA_ALIGN_SZ); +} + +/** + * bfa_cee_get_attr_isr() + * + * + * @param[in] cee - Pointer to the CEE module + * status - Return status from the f/w + * + * @return void + */ +static void +bfa_cee_get_attr_isr(struct bfa_cee_s *cee, bfa_status_t status) +{ + cee->get_attr_status = status; + bfa_trc(cee, 0); + if (status == BFA_STATUS_OK) { + bfa_trc(cee, 0); + /* + * The requested data has been copied to the DMA area, *process + * it. + */ + memcpy(cee->attr, cee->attr_dma.kva, + sizeof(struct bfa_cee_attr_s)); + bfa_cee_format_cee_cfg(cee->attr); + } + cee->get_attr_pending = BFA_FALSE; + if (cee->cbfn.get_attr_cbfn) { + bfa_trc(cee, 0); + cee->cbfn.get_attr_cbfn(cee->cbfn.get_attr_cbarg, status); + } + bfa_trc(cee, 0); +} + +/** + * bfa_cee_get_attr_isr() + * + * + * @param[in] cee - Pointer to the CEE module + * status - Return status from the f/w + * + * @return void + */ +static void +bfa_cee_get_stats_isr(struct bfa_cee_s *cee, bfa_status_t status) +{ + cee->get_stats_status = status; + bfa_trc(cee, 0); + if (status == BFA_STATUS_OK) { + bfa_trc(cee, 0); + /* + * The requested data has been copied to the DMA area, process + * it. + */ + memcpy(cee->stats, cee->stats_dma.kva, + sizeof(struct bfa_cee_stats_s)); + bfa_cee_format_cee_stats(cee->stats); + } + cee->get_stats_pending = BFA_FALSE; + bfa_trc(cee, 0); + if (cee->cbfn.get_stats_cbfn) { + bfa_trc(cee, 0); + cee->cbfn.get_stats_cbfn(cee->cbfn.get_stats_cbarg, status); + } + bfa_trc(cee, 0); +} + +/** + * bfa_cee_get_attr_isr() + * + * + * @param[in] cee - Pointer to the CEE module + * status - Return status from the f/w + * + * @return void + */ +static void +bfa_cee_reset_stats_isr(struct bfa_cee_s *cee, bfa_status_t status) +{ + cee->reset_stats_status = status; + cee->reset_stats_pending = BFA_FALSE; + if (cee->cbfn.reset_stats_cbfn) + cee->cbfn.reset_stats_cbfn(cee->cbfn.reset_stats_cbarg, status); +} + +/** + * bfa_cee_meminfo() + * + * + * @param[in] void + * + * @return Size of DMA region + */ +u32 +bfa_cee_meminfo(void) +{ + return (bfa_cee_attr_meminfo() + bfa_cee_stats_meminfo()); +} + +/** + * bfa_cee_mem_claim() + * + * + * @param[in] cee CEE module pointer + * dma_kva Kernel Virtual Address of CEE DMA Memory + * dma_pa Physical Address of CEE DMA Memory + * + * @return void + */ +void +bfa_cee_mem_claim(struct bfa_cee_s *cee, u8 *dma_kva, u64 dma_pa) +{ + cee->attr_dma.kva = dma_kva; + cee->attr_dma.pa = dma_pa; + cee->stats_dma.kva = dma_kva + bfa_cee_attr_meminfo(); + cee->stats_dma.pa = dma_pa + bfa_cee_attr_meminfo(); + cee->attr = (struct bfa_cee_attr_s *)dma_kva; + cee->stats = + (struct bfa_cee_stats_s *)(dma_kva + bfa_cee_attr_meminfo()); +} + +/** + * bfa_cee_get_attr() + * + * Send the request to the f/w to fetch CEE attributes. + * + * @param[in] Pointer to the CEE module data structure. + * + * @return Status + */ + +bfa_status_t +bfa_cee_get_attr(struct bfa_cee_s *cee, struct bfa_cee_attr_s *attr, + bfa_cee_get_attr_cbfn_t cbfn, void *cbarg) +{ + struct bfi_cee_get_req_s *cmd; + + bfa_assert((cee != NULL) && (cee->ioc != NULL)); + bfa_trc(cee, 0); + if (!bfa_ioc_is_operational(cee->ioc)) { + bfa_trc(cee, 0); + return BFA_STATUS_IOC_FAILURE; + } + if (cee->get_attr_pending == BFA_TRUE) { + bfa_trc(cee, 0); + return BFA_STATUS_DEVBUSY; + } + cee->get_attr_pending = BFA_TRUE; + cmd = (struct bfi_cee_get_req_s *)cee->get_cfg_mb.msg; + cee->attr = attr; + cee->cbfn.get_attr_cbfn = cbfn; + cee->cbfn.get_attr_cbarg = cbarg; + bfi_h2i_set(cmd->mh, BFI_MC_CEE, BFI_CEE_H2I_GET_CFG_REQ, + bfa_ioc_portid(cee->ioc)); + bfa_dma_be_addr_set(cmd->dma_addr, cee->attr_dma.pa); + bfa_ioc_mbox_queue(cee->ioc, &cee->get_cfg_mb); + bfa_trc(cee, 0); + + return BFA_STATUS_OK; +} + +/** + * bfa_cee_get_stats() + * + * Send the request to the f/w to fetch CEE statistics. + * + * @param[in] Pointer to the CEE module data structure. + * + * @return Status + */ + +bfa_status_t +bfa_cee_get_stats(struct bfa_cee_s *cee, struct bfa_cee_stats_s *stats, + bfa_cee_get_stats_cbfn_t cbfn, void *cbarg) +{ + struct bfi_cee_get_req_s *cmd; + + bfa_assert((cee != NULL) && (cee->ioc != NULL)); + + if (!bfa_ioc_is_operational(cee->ioc)) { + bfa_trc(cee, 0); + return BFA_STATUS_IOC_FAILURE; + } + if (cee->get_stats_pending == BFA_TRUE) { + bfa_trc(cee, 0); + return BFA_STATUS_DEVBUSY; + } + cee->get_stats_pending = BFA_TRUE; + cmd = (struct bfi_cee_get_req_s *)cee->get_stats_mb.msg; + cee->stats = stats; + cee->cbfn.get_stats_cbfn = cbfn; + cee->cbfn.get_stats_cbarg = cbarg; + bfi_h2i_set(cmd->mh, BFI_MC_CEE, BFI_CEE_H2I_GET_STATS_REQ, + bfa_ioc_portid(cee->ioc)); + bfa_dma_be_addr_set(cmd->dma_addr, cee->stats_dma.pa); + bfa_ioc_mbox_queue(cee->ioc, &cee->get_stats_mb); + bfa_trc(cee, 0); + + return BFA_STATUS_OK; +} + +/** + * bfa_cee_reset_stats() + * + * + * @param[in] Pointer to the CEE module data structure. + * + * @return Status + */ + +bfa_status_t +bfa_cee_reset_stats(struct bfa_cee_s *cee, bfa_cee_reset_stats_cbfn_t cbfn, + void *cbarg) +{ + struct bfi_cee_reset_stats_s *cmd; + + bfa_assert((cee != NULL) && (cee->ioc != NULL)); + if (!bfa_ioc_is_operational(cee->ioc)) { + bfa_trc(cee, 0); + return BFA_STATUS_IOC_FAILURE; + } + if (cee->reset_stats_pending == BFA_TRUE) { + bfa_trc(cee, 0); + return BFA_STATUS_DEVBUSY; + } + cee->reset_stats_pending = BFA_TRUE; + cmd = (struct bfi_cee_reset_stats_s *)cee->reset_stats_mb.msg; + cee->cbfn.reset_stats_cbfn = cbfn; + cee->cbfn.reset_stats_cbarg = cbarg; + bfi_h2i_set(cmd->mh, BFI_MC_CEE, BFI_CEE_H2I_RESET_STATS, + bfa_ioc_portid(cee->ioc)); + bfa_ioc_mbox_queue(cee->ioc, &cee->reset_stats_mb); + bfa_trc(cee, 0); + return BFA_STATUS_OK; +} + +/** + * bfa_cee_isrs() + * + * + * @param[in] Pointer to the CEE module data structure. + * + * @return void + */ + +void +bfa_cee_isr(void *cbarg, struct bfi_mbmsg_s *m) +{ + union bfi_cee_i2h_msg_u *msg; + struct bfi_cee_get_rsp_s *get_rsp; + struct bfa_cee_s *cee = (struct bfa_cee_s *)cbarg; + msg = (union bfi_cee_i2h_msg_u *)m; + get_rsp = (struct bfi_cee_get_rsp_s *)m; + bfa_trc(cee, msg->mh.msg_id); + switch (msg->mh.msg_id) { + case BFI_CEE_I2H_GET_CFG_RSP: + bfa_trc(cee, get_rsp->cmd_status); + bfa_cee_get_attr_isr(cee, get_rsp->cmd_status); + break; + case BFI_CEE_I2H_GET_STATS_RSP: + bfa_cee_get_stats_isr(cee, get_rsp->cmd_status); + break; + case BFI_CEE_I2H_RESET_STATS_RSP: + bfa_cee_reset_stats_isr(cee, get_rsp->cmd_status); + break; + default: + bfa_assert(0); + } +} + +/** + * bfa_cee_hbfail() + * + * + * @param[in] Pointer to the CEE module data structure. + * + * @return void + */ + +void +bfa_cee_hbfail(void *arg) +{ + struct bfa_cee_s *cee; + cee = (struct bfa_cee_s *)arg; + + if (cee->get_attr_pending == BFA_TRUE) { + cee->get_attr_status = BFA_STATUS_FAILED; + cee->get_attr_pending = BFA_FALSE; + if (cee->cbfn.get_attr_cbfn) { + cee->cbfn.get_attr_cbfn(cee->cbfn.get_attr_cbarg, + BFA_STATUS_FAILED); + } + } + if (cee->get_stats_pending == BFA_TRUE) { + cee->get_stats_status = BFA_STATUS_FAILED; + cee->get_stats_pending = BFA_FALSE; + if (cee->cbfn.get_stats_cbfn) { + cee->cbfn.get_stats_cbfn(cee->cbfn.get_stats_cbarg, + BFA_STATUS_FAILED); + } + } + if (cee->reset_stats_pending == BFA_TRUE) { + cee->reset_stats_status = BFA_STATUS_FAILED; + cee->reset_stats_pending = BFA_FALSE; + if (cee->cbfn.reset_stats_cbfn) { + cee->cbfn.reset_stats_cbfn(cee->cbfn.reset_stats_cbarg, + BFA_STATUS_FAILED); + } + } +} + +/** + * bfa_cee_attach() + * + * + * @param[in] cee - Pointer to the CEE module data structure + * ioc - Pointer to the ioc module data structure + * dev - Pointer to the device driver module data structure + * The device driver specific mbox ISR functions have + * this pointer as one of the parameters. + * trcmod - + * logmod - + * + * @return void + */ +void +bfa_cee_attach(struct bfa_cee_s *cee, struct bfa_ioc_s *ioc, void *dev, + struct bfa_trc_mod_s *trcmod, struct bfa_log_mod_s *logmod) +{ + bfa_assert(cee != NULL); + cee->dev = dev; + cee->trcmod = trcmod; + cee->logmod = logmod; + cee->ioc = ioc; + + bfa_ioc_mbox_regisr(cee->ioc, BFI_MC_CEE, bfa_cee_isr, cee); + bfa_ioc_hbfail_init(&cee->hbfail, bfa_cee_hbfail, cee); + bfa_ioc_hbfail_register(cee->ioc, &cee->hbfail); + bfa_trc(cee, 0); +} + +/** + * bfa_cee_detach() + * + * + * @param[in] cee - Pointer to the CEE module data structure + * + * @return void + */ +void +bfa_cee_detach(struct bfa_cee_s *cee) +{ + /* + * For now, just check if there is some ioctl pending and mark that as + * failed? + */ + /* bfa_cee_hbfail(cee); */ +} diff --git a/drivers/scsi/bfa/bfa_core.c b/drivers/scsi/bfa/bfa_core.c new file mode 100644 index 000000000000..44e2d1155c51 --- /dev/null +++ b/drivers/scsi/bfa/bfa_core.c @@ -0,0 +1,402 @@ +/* + * Copyright (c) 2005-2009 Brocade Communications Systems, Inc. + * All rights reserved + * www.brocade.com + * + * Linux driver for Brocade Fibre Channel Host Bus Adapter. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (GPL) Version 2 as + * published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#include <bfa.h> +#include <defs/bfa_defs_pci.h> +#include <cs/bfa_debug.h> +#include <bfa_iocfc.h> + +#define DEF_CFG_NUM_FABRICS 1 +#define DEF_CFG_NUM_LPORTS 256 +#define DEF_CFG_NUM_CQS 4 +#define DEF_CFG_NUM_IOIM_REQS (BFA_IOIM_MAX) +#define DEF_CFG_NUM_TSKIM_REQS 128 +#define DEF_CFG_NUM_FCXP_REQS 64 +#define DEF_CFG_NUM_UF_BUFS 64 +#define DEF_CFG_NUM_RPORTS 1024 +#define DEF_CFG_NUM_ITNIMS (DEF_CFG_NUM_RPORTS) +#define DEF_CFG_NUM_TINS 256 + +#define DEF_CFG_NUM_SGPGS 2048 +#define DEF_CFG_NUM_REQQ_ELEMS 256 +#define DEF_CFG_NUM_RSPQ_ELEMS 64 +#define DEF_CFG_NUM_SBOOT_TGTS 16 +#define DEF_CFG_NUM_SBOOT_LUNS 16 + +/** + * Use this function query the memory requirement of the BFA library. + * This function needs to be called before bfa_attach() to get the + * memory required of the BFA layer for a given driver configuration. + * + * This call will fail, if the cap is out of range compared to pre-defined + * values within the BFA library + * + * @param[in] cfg - pointer to bfa_ioc_cfg_t. Driver layer should indicate + * its configuration in this structure. + * The default values for struct bfa_iocfc_cfg_s can be + * fetched using bfa_cfg_get_default() API. + * + * If cap's boundary check fails, the library will use + * the default bfa_cap_t values (and log a warning msg). + * + * @param[out] meminfo - pointer to bfa_meminfo_t. This content + * indicates the memory type (see bfa_mem_type_t) and + * amount of memory required. + * + * Driver should allocate the memory, populate the + * starting address for each block and provide the same + * structure as input parameter to bfa_attach() call. + * + * @return void + * + * Special Considerations: @note + */ +void +bfa_cfg_get_meminfo(struct bfa_iocfc_cfg_s *cfg, struct bfa_meminfo_s *meminfo) +{ + int i; + u32 km_len = 0, dm_len = 0; + + bfa_assert((cfg != NULL) && (meminfo != NULL)); + + bfa_os_memset((void *)meminfo, 0, sizeof(struct bfa_meminfo_s)); + meminfo->meminfo[BFA_MEM_TYPE_KVA - 1].mem_type = + BFA_MEM_TYPE_KVA; + meminfo->meminfo[BFA_MEM_TYPE_DMA - 1].mem_type = + BFA_MEM_TYPE_DMA; + + bfa_iocfc_meminfo(cfg, &km_len, &dm_len); + + for (i = 0; hal_mods[i]; i++) + hal_mods[i]->meminfo(cfg, &km_len, &dm_len); + + + meminfo->meminfo[BFA_MEM_TYPE_KVA - 1].mem_len = km_len; + meminfo->meminfo[BFA_MEM_TYPE_DMA - 1].mem_len = dm_len; +} + +/** + * Use this function to do attach the driver instance with the BFA + * library. This function will not trigger any HW initialization + * process (which will be done in bfa_init() call) + * + * This call will fail, if the cap is out of range compared to + * pre-defined values within the BFA library + * + * @param[out] bfa Pointer to bfa_t. + * @param[in] bfad Opaque handle back to the driver's IOC structure + * @param[in] cfg Pointer to bfa_ioc_cfg_t. Should be same structure + * that was used in bfa_cfg_get_meminfo(). + * @param[in] meminfo Pointer to bfa_meminfo_t. The driver should + * use the bfa_cfg_get_meminfo() call to + * find the memory blocks required, allocate the + * required memory and provide the starting addresses. + * @param[in] pcidev pointer to struct bfa_pcidev_s + * + * @return + * void + * + * Special Considerations: + * + * @note + * + */ +void +bfa_attach(struct bfa_s *bfa, void *bfad, struct bfa_iocfc_cfg_s *cfg, + struct bfa_meminfo_s *meminfo, struct bfa_pcidev_s *pcidev) +{ + int i; + struct bfa_mem_elem_s *melem; + + bfa->fcs = BFA_FALSE; + + bfa_assert((cfg != NULL) && (meminfo != NULL)); + + /** + * initialize all memory pointers for iterative allocation + */ + for (i = 0; i < BFA_MEM_TYPE_MAX; i++) { + melem = meminfo->meminfo + i; + melem->kva_curp = melem->kva; + melem->dma_curp = melem->dma; + } + + bfa_iocfc_attach(bfa, bfad, cfg, meminfo, pcidev); + + for (i = 0; hal_mods[i]; i++) + hal_mods[i]->attach(bfa, bfad, cfg, meminfo, pcidev); + +} + +/** + * Use this function to delete a BFA IOC. IOC should be stopped (by + * calling bfa_stop()) before this function call. + * + * @param[in] bfa - pointer to bfa_t. + * + * @return + * void + * + * Special Considerations: + * + * @note + */ +void +bfa_detach(struct bfa_s *bfa) +{ + int i; + + for (i = 0; hal_mods[i]; i++) + hal_mods[i]->detach(bfa); + + bfa_iocfc_detach(bfa); +} + + +void +bfa_init_trc(struct bfa_s *bfa, struct bfa_trc_mod_s *trcmod) +{ + bfa->trcmod = trcmod; +} + + +void +bfa_init_log(struct bfa_s *bfa, struct bfa_log_mod_s *logmod) +{ + bfa->logm = logmod; +} + + +void +bfa_init_aen(struct bfa_s *bfa, struct bfa_aen_s *aen) +{ + bfa->aen = aen; +} + +void +bfa_init_plog(struct bfa_s *bfa, struct bfa_plog_s *plog) +{ + bfa->plog = plog; +} + +/** + * Initialize IOC. + * + * This function will return immediately, when the IOC initialization is + * completed, the bfa_cb_init() will be called. + * + * @param[in] bfa instance + * + * @return void + * + * Special Considerations: + * + * @note + * When this function returns, the driver should register the interrupt service + * routine(s) and enable the device interrupts. If this is not done, + * bfa_cb_init() will never get called + */ +void +bfa_init(struct bfa_s *bfa) +{ + bfa_iocfc_init(bfa); +} + +/** + * Use this function initiate the IOC configuration setup. This function + * will return immediately. + * + * @param[in] bfa instance + * + * @return None + */ +void +bfa_start(struct bfa_s *bfa) +{ + bfa_iocfc_start(bfa); +} + +/** + * Use this function quiese the IOC. This function will return immediately, + * when the IOC is actually stopped, the bfa_cb_stop() will be called. + * + * @param[in] bfa - pointer to bfa_t. + * + * @return None + * + * Special Considerations: + * bfa_cb_stop() could be called before or after bfa_stop() returns. + * + * @note + * In case of any failure, we could handle it automatically by doing a + * reset and then succeed the bfa_stop() call. + */ +void +bfa_stop(struct bfa_s *bfa) +{ + bfa_iocfc_stop(bfa); +} + +void +bfa_comp_deq(struct bfa_s *bfa, struct list_head *comp_q) +{ + INIT_LIST_HEAD(comp_q); + list_splice_tail_init(&bfa->comp_q, comp_q); +} + +void +bfa_comp_process(struct bfa_s *bfa, struct list_head *comp_q) +{ + struct list_head *qe; + struct list_head *qen; + struct bfa_cb_qe_s *hcb_qe; + + list_for_each_safe(qe, qen, comp_q) { + hcb_qe = (struct bfa_cb_qe_s *) qe; + hcb_qe->cbfn(hcb_qe->cbarg, BFA_TRUE); + } +} + +void +bfa_comp_free(struct bfa_s *bfa, struct list_head *comp_q) +{ + struct list_head *qe; + struct bfa_cb_qe_s *hcb_qe; + + while (!list_empty(comp_q)) { + bfa_q_deq(comp_q, &qe); + hcb_qe = (struct bfa_cb_qe_s *) qe; + hcb_qe->cbfn(hcb_qe->cbarg, BFA_FALSE); + } +} + +void +bfa_attach_fcs(struct bfa_s *bfa) +{ + bfa->fcs = BFA_TRUE; +} + +/** + * Periodic timer heart beat from driver + */ +void +bfa_timer_tick(struct bfa_s *bfa) +{ + bfa_timer_beat(&bfa->timer_mod); +} + +#ifndef BFA_BIOS_BUILD +/** + * Return the list of PCI vendor/device id lists supported by this + * BFA instance. + */ +void +bfa_get_pciids(struct bfa_pciid_s **pciids, int *npciids) +{ + static struct bfa_pciid_s __pciids[] = { + {BFA_PCI_VENDOR_ID_BROCADE, BFA_PCI_DEVICE_ID_FC_8G2P}, + {BFA_PCI_VENDOR_ID_BROCADE, BFA_PCI_DEVICE_ID_FC_8G1P}, + {BFA_PCI_VENDOR_ID_BROCADE, BFA_PCI_DEVICE_ID_CT}, + }; + + *npciids = sizeof(__pciids) / sizeof(__pciids[0]); + *pciids = __pciids; +} + +/** + * Use this function query the default struct bfa_iocfc_cfg_s value (compiled + * into BFA layer). The OS driver can then turn back and overwrite entries that + * have been configured by the user. + * + * @param[in] cfg - pointer to bfa_ioc_cfg_t + * + * @return + * void + * + * Special Considerations: + * note + */ +void +bfa_cfg_get_default(struct bfa_iocfc_cfg_s *cfg) +{ + cfg->fwcfg.num_fabrics = DEF_CFG_NUM_FABRICS; + cfg->fwcfg.num_lports = DEF_CFG_NUM_LPORTS; + cfg->fwcfg.num_rports = DEF_CFG_NUM_RPORTS; + cfg->fwcfg.num_ioim_reqs = DEF_CFG_NUM_IOIM_REQS; + cfg->fwcfg.num_tskim_reqs = DEF_CFG_NUM_TSKIM_REQS; + cfg->fwcfg.num_fcxp_reqs = DEF_CFG_NUM_FCXP_REQS; + cfg->fwcfg.num_uf_bufs = DEF_CFG_NUM_UF_BUFS; + cfg->fwcfg.num_cqs = DEF_CFG_NUM_CQS; + + cfg->drvcfg.num_reqq_elems = DEF_CFG_NUM_REQQ_ELEMS; + cfg->drvcfg.num_rspq_elems = DEF_CFG_NUM_RSPQ_ELEMS; + cfg->drvcfg.num_sgpgs = DEF_CFG_NUM_SGPGS; + cfg->drvcfg.num_sboot_tgts = DEF_CFG_NUM_SBOOT_TGTS; + cfg->drvcfg.num_sboot_luns = DEF_CFG_NUM_SBOOT_LUNS; + cfg->drvcfg.path_tov = BFA_FCPIM_PATHTOV_DEF; + cfg->drvcfg.ioc_recover = BFA_FALSE; + cfg->drvcfg.delay_comp = BFA_FALSE; + +} + +void +bfa_cfg_get_min(struct bfa_iocfc_cfg_s *cfg) +{ + bfa_cfg_get_default(cfg); + cfg->fwcfg.num_ioim_reqs = BFA_IOIM_MIN; + cfg->fwcfg.num_tskim_reqs = BFA_TSKIM_MIN; + cfg->fwcfg.num_fcxp_reqs = BFA_FCXP_MIN; + cfg->fwcfg.num_uf_bufs = BFA_UF_MIN; + cfg->fwcfg.num_rports = BFA_RPORT_MIN; + + cfg->drvcfg.num_sgpgs = BFA_SGPG_MIN; + cfg->drvcfg.num_reqq_elems = BFA_REQQ_NELEMS_MIN; + cfg->drvcfg.num_rspq_elems = BFA_RSPQ_NELEMS_MIN; + cfg->drvcfg.min_cfg = BFA_TRUE; +} + +void +bfa_get_attr(struct bfa_s *bfa, struct bfa_ioc_attr_s *ioc_attr) +{ + bfa_ioc_get_attr(&bfa->ioc, ioc_attr); +} + +/** + * Retrieve firmware trace information on IOC failure. + */ +bfa_status_t +bfa_debug_fwsave(struct bfa_s *bfa, void *trcdata, int *trclen) +{ + return bfa_ioc_debug_fwsave(&bfa->ioc, trcdata, trclen); +} + +/** + * Fetch firmware trace data. + * + * @param[in] bfa BFA instance + * @param[out] trcdata Firmware trace buffer + * @param[in,out] trclen Firmware trace buffer len + * + * @retval BFA_STATUS_OK Firmware trace is fetched. + * @retval BFA_STATUS_INPROGRESS Firmware trace fetch is in progress. + */ +bfa_status_t +bfa_debug_fwtrc(struct bfa_s *bfa, void *trcdata, int *trclen) +{ + return bfa_ioc_debug_fwtrc(&bfa->ioc, trcdata, trclen); +} +#endif diff --git a/drivers/scsi/bfa/bfa_csdebug.c b/drivers/scsi/bfa/bfa_csdebug.c new file mode 100644 index 000000000000..1b71d349451a --- /dev/null +++ b/drivers/scsi/bfa/bfa_csdebug.c @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2005-2009 Brocade Communications Systems, Inc. + * All rights reserved + * www.brocade.com + * + * Linux driver for Brocade Fibre Channel Host Bus Adapter. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (GPL) Version 2 as + * published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#include <cs/bfa_debug.h> +#include <bfa_os_inc.h> +#include <cs/bfa_q.h> +#include <log/bfa_log_hal.h> + +/** + * cs_debug_api + */ + + +void +bfa_panic(int line, char *file, char *panicstr) +{ + bfa_log(NULL, BFA_LOG_HAL_ASSERT, file, line, panicstr); + bfa_os_panic(); +} + +void +bfa_sm_panic(struct bfa_log_mod_s *logm, int line, char *file, int event) +{ + bfa_log(logm, BFA_LOG_HAL_SM_ASSERT, file, line, event); + bfa_os_panic(); +} + +int +bfa_q_is_on_q_func(struct list_head *q, struct list_head *qe) +{ + struct list_head *tqe; + + tqe = bfa_q_next(q); + while (tqe != q) { + if (tqe == qe) + return (1); + tqe = bfa_q_next(tqe); + if (tqe == NULL) + break; + } + return (0); +} + + diff --git a/drivers/scsi/bfa/bfa_fcpim.c b/drivers/scsi/bfa/bfa_fcpim.c new file mode 100644 index 000000000000..401babe3494e --- /dev/null +++ b/drivers/scsi/bfa/bfa_fcpim.c @@ -0,0 +1,175 @@ +/* + * Copyright (c) 2005-2009 Brocade Communications Systems, Inc. + * All rights reserved + * www.brocade.com + * + * Linux driver for Brocade Fibre Channel Host Bus Adapter. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (GPL) Version 2 as + * published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#include <bfa.h> +#include <log/bfa_log_hal.h> + +BFA_TRC_FILE(HAL, FCPIM); +BFA_MODULE(fcpim); + +/** + * hal_fcpim_mod BFA FCP Initiator Mode module + */ + +/** + * Compute and return memory needed by FCP(im) module. + */ +static void +bfa_fcpim_meminfo(struct bfa_iocfc_cfg_s *cfg, u32 *km_len, + u32 *dm_len) +{ + bfa_itnim_meminfo(cfg, km_len, dm_len); + + /** + * IO memory + */ + if (cfg->fwcfg.num_ioim_reqs < BFA_IOIM_MIN) + cfg->fwcfg.num_ioim_reqs = BFA_IOIM_MIN; + else if (cfg->fwcfg.num_ioim_reqs > BFA_IOIM_MAX) + cfg->fwcfg.num_ioim_reqs = BFA_IOIM_MAX; + + *km_len += cfg->fwcfg.num_ioim_reqs * + (sizeof(struct bfa_ioim_s) + sizeof(struct bfa_ioim_sp_s)); + + *dm_len += cfg->fwcfg.num_ioim_reqs * BFI_IOIM_SNSLEN; + + /** + * task management command memory + */ + if (cfg->fwcfg.num_tskim_reqs < BFA_TSKIM_MIN) + cfg->fwcfg.num_tskim_reqs = BFA_TSKIM_MIN; + *km_len += cfg->fwcfg.num_tskim_reqs * sizeof(struct bfa_tskim_s); +} + + +static void +bfa_fcpim_attach(struct bfa_s *bfa, void *bfad, struct bfa_iocfc_cfg_s *cfg, + struct bfa_meminfo_s *meminfo, struct bfa_pcidev_s *pcidev) +{ + struct bfa_fcpim_mod_s *fcpim = BFA_FCPIM_MOD(bfa); + + bfa_trc(bfa, cfg->drvcfg.path_tov); + bfa_trc(bfa, cfg->fwcfg.num_rports); + bfa_trc(bfa, cfg->fwcfg.num_ioim_reqs); + bfa_trc(bfa, cfg->fwcfg.num_tskim_reqs); + + fcpim->bfa = bfa; + fcpim->num_itnims = cfg->fwcfg.num_rports; + fcpim->num_ioim_reqs = cfg->fwcfg.num_ioim_reqs; + fcpim->num_tskim_reqs = cfg->fwcfg.num_tskim_reqs; + fcpim->path_tov = cfg->drvcfg.path_tov; + fcpim->delay_comp = cfg->drvcfg.delay_comp; + + bfa_itnim_attach(fcpim, meminfo); + bfa_tskim_attach(fcpim, meminfo); + bfa_ioim_attach(fcpim, meminfo); +} + +static void +bfa_fcpim_initdone(struct bfa_s *bfa) +{ +} + +static void +bfa_fcpim_detach(struct bfa_s *bfa) +{ + struct bfa_fcpim_mod_s *fcpim = BFA_FCPIM_MOD(bfa); + + bfa_ioim_detach(fcpim); + bfa_tskim_detach(fcpim); +} + +static void +bfa_fcpim_start(struct bfa_s *bfa) +{ +} + +static void +bfa_fcpim_stop(struct bfa_s *bfa) +{ +} + +static void +bfa_fcpim_iocdisable(struct bfa_s *bfa) +{ + struct bfa_fcpim_mod_s *fcpim = BFA_FCPIM_MOD(bfa); + struct bfa_itnim_s *itnim; + struct list_head *qe, *qen; + + list_for_each_safe(qe, qen, &fcpim->itnim_q) { + itnim = (struct bfa_itnim_s *) qe; + bfa_itnim_iocdisable(itnim); + } +} + +void +bfa_fcpim_path_tov_set(struct bfa_s *bfa, u16 path_tov) +{ + struct bfa_fcpim_mod_s *fcpim = BFA_FCPIM_MOD(bfa); + + fcpim->path_tov = path_tov * 1000; + if (fcpim->path_tov > BFA_FCPIM_PATHTOV_MAX) + fcpim->path_tov = BFA_FCPIM_PATHTOV_MAX; +} + +u16 +bfa_fcpim_path_tov_get(struct bfa_s *bfa) +{ + struct bfa_fcpim_mod_s *fcpim = BFA_FCPIM_MOD(bfa); + + return (fcpim->path_tov / 1000); +} + +bfa_status_t +bfa_fcpim_get_modstats(struct bfa_s *bfa, struct bfa_fcpim_stats_s *modstats) +{ + struct bfa_fcpim_mod_s *fcpim = BFA_FCPIM_MOD(bfa); + + *modstats = fcpim->stats; + + return BFA_STATUS_OK; +} + +bfa_status_t +bfa_fcpim_clr_modstats(struct bfa_s *bfa) +{ + struct bfa_fcpim_mod_s *fcpim = BFA_FCPIM_MOD(bfa); + + memset(&fcpim->stats, 0, sizeof(struct bfa_fcpim_stats_s)); + + return BFA_STATUS_OK; +} + +void +bfa_fcpim_qdepth_set(struct bfa_s *bfa, u16 q_depth) +{ + struct bfa_fcpim_mod_s *fcpim = BFA_FCPIM_MOD(bfa); + + bfa_assert(q_depth <= BFA_IOCFC_QDEPTH_MAX); + + fcpim->q_depth = q_depth; +} + +u16 +bfa_fcpim_qdepth_get(struct bfa_s *bfa) +{ + struct bfa_fcpim_mod_s *fcpim = BFA_FCPIM_MOD(bfa); + + return (fcpim->q_depth); +} + + diff --git a/drivers/scsi/bfa/bfa_fcpim_priv.h b/drivers/scsi/bfa/bfa_fcpim_priv.h new file mode 100644 index 000000000000..153206cfb37a --- /dev/null +++ b/drivers/scsi/bfa/bfa_fcpim_priv.h @@ -0,0 +1,188 @@ +/* + * Copyright (c) 2005-2009 Brocade Communications Systems, Inc. + * All rights reserved + * www.brocade.com + * + * Linux driver for Brocade Fibre Channel Host Bus Adapter. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (GPL) Version 2 as + * published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#ifndef __BFA_FCPIM_PRIV_H__ +#define __BFA_FCPIM_PRIV_H__ + +#include <bfa_fcpim.h> +#include <defs/bfa_defs_fcpim.h> +#include <cs/bfa_wc.h> +#include "bfa_sgpg_priv.h" + +#define BFA_ITNIM_MIN 32 +#define BFA_ITNIM_MAX 1024 + +#define BFA_IOIM_MIN 8 +#define BFA_IOIM_MAX 2000 + +#define BFA_TSKIM_MIN 4 +#define BFA_TSKIM_MAX 512 +#define BFA_FCPIM_PATHTOV_DEF (30 * 1000) /* in millisecs */ +#define BFA_FCPIM_PATHTOV_MAX (90 * 1000) /* in millisecs */ + +#define bfa_fcpim_stats(__fcpim, __stats) \ + (__fcpim)->stats.__stats ++ + +struct bfa_fcpim_mod_s { + struct bfa_s *bfa; + struct bfa_itnim_s *itnim_arr; + struct bfa_ioim_s *ioim_arr; + struct bfa_ioim_sp_s *ioim_sp_arr; + struct bfa_tskim_s *tskim_arr; + struct bfa_dma_s snsbase; + int num_itnims; + int num_ioim_reqs; + int num_tskim_reqs; + u32 path_tov; + u16 q_depth; + u16 rsvd; + struct list_head itnim_q; /* queue of active itnim */ + struct list_head ioim_free_q; /* free IO resources */ + struct list_head ioim_resfree_q; /* IOs waiting for f/w */ + struct list_head ioim_comp_q; /* IO global comp Q */ + struct list_head tskim_free_q; + u32 ios_active; /* current active IOs */ + u32 delay_comp; + struct bfa_fcpim_stats_s stats; +}; + +struct bfa_ioim_s; +struct bfa_tskim_s; + +/** + * BFA IO (initiator mode) + */ +struct bfa_ioim_s { + struct list_head qe; /* queue elememt */ + bfa_sm_t sm; /* BFA ioim state machine */ + struct bfa_s *bfa; /* BFA module */ + struct bfa_fcpim_mod_s *fcpim; /* parent fcpim module */ + struct bfa_itnim_s *itnim; /* i-t-n nexus for this IO */ + struct bfad_ioim_s *dio; /* driver IO handle */ + u16 iotag; /* FWI IO tag */ + u16 abort_tag; /* unqiue abort request tag */ + u16 nsges; /* number of SG elements */ + u16 nsgpgs; /* number of SG pages */ + struct bfa_sgpg_s *sgpg; /* first SG page */ + struct list_head sgpg_q; /* allocated SG pages */ + struct bfa_cb_qe_s hcb_qe; /* bfa callback qelem */ + bfa_cb_cbfn_t io_cbfn; /* IO completion handler */ + struct bfa_ioim_sp_s *iosp; /* slow-path IO handling */ +}; + +struct bfa_ioim_sp_s { + struct bfi_msg_s comp_rspmsg; /* IO comp f/w response */ + u8 *snsinfo; /* sense info for this IO */ + struct bfa_sgpg_wqe_s sgpg_wqe; /* waitq elem for sgpg */ + struct bfa_reqq_wait_s reqq_wait; /* to wait for room in reqq */ + bfa_boolean_t abort_explicit; /* aborted by OS */ + struct bfa_tskim_s *tskim; /* Relevant TM cmd */ +}; + +/** + * BFA Task management command (initiator mode) + */ +struct bfa_tskim_s { + struct list_head qe; + bfa_sm_t sm; + struct bfa_s *bfa; /* BFA module */ + struct bfa_fcpim_mod_s *fcpim; /* parent fcpim module */ + struct bfa_itnim_s *itnim; /* i-t-n nexus for this IO */ + struct bfad_tskim_s *dtsk; /* driver task mgmt cmnd */ + bfa_boolean_t notify; /* notify itnim on TM comp */ + lun_t lun; /* lun if applicable */ + enum fcp_tm_cmnd tm_cmnd; /* task management command */ + u16 tsk_tag; /* FWI IO tag */ + u8 tsecs; /* timeout in seconds */ + struct bfa_reqq_wait_s reqq_wait; /* to wait for room in reqq */ + struct list_head io_q; /* queue of affected IOs */ + struct bfa_wc_s wc; /* waiting counter */ + struct bfa_cb_qe_s hcb_qe; /* bfa callback qelem */ + enum bfi_tskim_status tsk_status; /* TM status */ +}; + +/** + * BFA i-t-n (initiator mode) + */ +struct bfa_itnim_s { + struct list_head qe; /* queue element */ + bfa_sm_t sm; /* i-t-n im BFA state machine */ + struct bfa_s *bfa; /* bfa instance */ + struct bfa_rport_s *rport; /* bfa rport */ + void *ditn; /* driver i-t-n structure */ + struct bfi_mhdr_s mhdr; /* pre-built mhdr */ + u8 msg_no; /* itnim/rport firmware handle */ + u8 reqq; /* CQ for requests */ + struct bfa_cb_qe_s hcb_qe; /* bfa callback qelem */ + struct list_head pending_q; /* queue of pending IO requests*/ + struct list_head io_q; /* queue of active IO requests */ + struct list_head io_cleanup_q; /* IO being cleaned up */ + struct list_head tsk_q; /* queue of active TM commands */ + struct list_head delay_comp_q;/* queue of failed inflight cmds */ + bfa_boolean_t seq_rec; /* SQER supported */ + bfa_boolean_t is_online; /* itnim is ONLINE for IO */ + bfa_boolean_t iotov_active; /* IO TOV timer is active */ + struct bfa_wc_s wc; /* waiting counter */ + struct bfa_timer_s timer; /* pending IO TOV */ + struct bfa_reqq_wait_s reqq_wait; /* to wait for room in reqq */ + struct bfa_fcpim_mod_s *fcpim; /* fcpim module */ + struct bfa_itnim_hal_stats_s stats; +}; + +#define bfa_itnim_is_online(_itnim) (_itnim)->is_online +#define BFA_FCPIM_MOD(_hal) (&(_hal)->modules.fcpim_mod) +#define BFA_IOIM_FROM_TAG(_fcpim, _iotag) \ + (&fcpim->ioim_arr[_iotag]) +#define BFA_TSKIM_FROM_TAG(_fcpim, _tmtag) \ + (&fcpim->tskim_arr[_tmtag & (fcpim->num_tskim_reqs - 1)]) + +/* + * function prototypes + */ +void bfa_ioim_attach(struct bfa_fcpim_mod_s *fcpim, + struct bfa_meminfo_s *minfo); +void bfa_ioim_detach(struct bfa_fcpim_mod_s *fcpim); +void bfa_ioim_isr(struct bfa_s *bfa, struct bfi_msg_s *msg); +void bfa_ioim_good_comp_isr(struct bfa_s *bfa, + struct bfi_msg_s *msg); +void bfa_ioim_cleanup(struct bfa_ioim_s *ioim); +void bfa_ioim_cleanup_tm(struct bfa_ioim_s *ioim, + struct bfa_tskim_s *tskim); +void bfa_ioim_iocdisable(struct bfa_ioim_s *ioim); +void bfa_ioim_tov(struct bfa_ioim_s *ioim); + +void bfa_tskim_attach(struct bfa_fcpim_mod_s *fcpim, + struct bfa_meminfo_s *minfo); +void bfa_tskim_detach(struct bfa_fcpim_mod_s *fcpim); +void bfa_tskim_isr(struct bfa_s *bfa, struct bfi_msg_s *msg); +void bfa_tskim_iodone(struct bfa_tskim_s *tskim); +void bfa_tskim_iocdisable(struct bfa_tskim_s *tskim); +void bfa_tskim_cleanup(struct bfa_tskim_s *tskim); + +void bfa_itnim_meminfo(struct bfa_iocfc_cfg_s *cfg, u32 *km_len, + u32 *dm_len); +void bfa_itnim_attach(struct bfa_fcpim_mod_s *fcpim, + struct bfa_meminfo_s *minfo); +void bfa_itnim_detach(struct bfa_fcpim_mod_s *fcpim); +void bfa_itnim_iocdisable(struct bfa_itnim_s *itnim); +void bfa_itnim_isr(struct bfa_s *bfa, struct bfi_msg_s *msg); +void bfa_itnim_iodone(struct bfa_itnim_s *itnim); +void bfa_itnim_tskdone(struct bfa_itnim_s *itnim); +bfa_boolean_t bfa_itnim_hold_io(struct bfa_itnim_s *itnim); + +#endif /* __BFA_FCPIM_PRIV_H__ */ + diff --git a/drivers/scsi/bfa/bfa_fcport.c b/drivers/scsi/bfa/bfa_fcport.c new file mode 100644 index 000000000000..992435987deb --- /dev/null +++ b/drivers/scsi/bfa/bfa_fcport.c @@ -0,0 +1,1671 @@ +/* + * Copyright (c) 2005-2009 Brocade Communications Systems, Inc. + * All rights reserved + * www.brocade.com + * + * Linux driver for Brocade Fibre Channel Host Bus Adapter. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (GPL) Version 2 as + * published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#include <bfa.h> +#include <bfa_svc.h> +#include <bfi/bfi_pport.h> +#include <cs/bfa_debug.h> +#include <aen/bfa_aen.h> +#include <cs/bfa_plog.h> +#include <aen/bfa_aen_port.h> + +BFA_TRC_FILE(HAL, PPORT); +BFA_MODULE(pport); + +#define bfa_pport_callback(__pport, __event) do { \ + if ((__pport)->bfa->fcs) { \ + (__pport)->event_cbfn((__pport)->event_cbarg, (__event)); \ + } else { \ + (__pport)->hcb_event = (__event); \ + bfa_cb_queue((__pport)->bfa, &(__pport)->hcb_qe, \ + __bfa_cb_port_event, (__pport)); \ + } \ +} while (0) + +/* + * The port is considered disabled if corresponding physical port or IOC are + * disabled explicitly + */ +#define BFA_PORT_IS_DISABLED(bfa) \ + ((bfa_pport_is_disabled(bfa) == BFA_TRUE) || \ + (bfa_ioc_is_disabled(&bfa->ioc) == BFA_TRUE)) + +/* + * forward declarations + */ +static bfa_boolean_t bfa_pport_send_enable(struct bfa_pport_s *port); +static bfa_boolean_t bfa_pport_send_disable(struct bfa_pport_s *port); +static void bfa_pport_update_linkinfo(struct bfa_pport_s *pport); +static void bfa_pport_reset_linkinfo(struct bfa_pport_s *pport); +static void bfa_pport_set_wwns(struct bfa_pport_s *port); +static void __bfa_cb_port_event(void *cbarg, bfa_boolean_t complete); +static void __bfa_cb_port_stats(void *cbarg, bfa_boolean_t complete); +static void __bfa_cb_port_stats_clr(void *cbarg, bfa_boolean_t complete); +static void bfa_port_stats_timeout(void *cbarg); +static void bfa_port_stats_clr_timeout(void *cbarg); + +/** + * bfa_pport_private + */ + +/** + * BFA port state machine events + */ +enum bfa_pport_sm_event { + BFA_PPORT_SM_START = 1, /* start port state machine */ + BFA_PPORT_SM_STOP = 2, /* stop port state machine */ + BFA_PPORT_SM_ENABLE = 3, /* enable port */ + BFA_PPORT_SM_DISABLE = 4, /* disable port state machine */ + BFA_PPORT_SM_FWRSP = 5, /* firmware enable/disable rsp */ + BFA_PPORT_SM_LINKUP = 6, /* firmware linkup event */ + BFA_PPORT_SM_LINKDOWN = 7, /* firmware linkup down */ + BFA_PPORT_SM_QRESUME = 8, /* CQ space available */ + BFA_PPORT_SM_HWFAIL = 9, /* IOC h/w failure */ +}; + +static void bfa_pport_sm_uninit(struct bfa_pport_s *pport, + enum bfa_pport_sm_event event); +static void bfa_pport_sm_enabling_qwait(struct bfa_pport_s *pport, + enum bfa_pport_sm_event event); +static void bfa_pport_sm_enabling(struct bfa_pport_s *pport, + enum bfa_pport_sm_event event); +static void bfa_pport_sm_linkdown(struct bfa_pport_s *pport, + enum bfa_pport_sm_event event); +static void bfa_pport_sm_linkup(struct bfa_pport_s *pport, + enum bfa_pport_sm_event event); +static void bfa_pport_sm_disabling(struct bfa_pport_s *pport, + enum bfa_pport_sm_event event); +static void bfa_pport_sm_disabling_qwait(struct bfa_pport_s *pport, + enum bfa_pport_sm_event event); +static void bfa_pport_sm_disabled(struct bfa_pport_s *pport, + enum bfa_pport_sm_event event); +static void bfa_pport_sm_stopped(struct bfa_pport_s *pport, + enum bfa_pport_sm_event event); +static void bfa_pport_sm_iocdown(struct bfa_pport_s *pport, + enum bfa_pport_sm_event event); +static void bfa_pport_sm_iocfail(struct bfa_pport_s *pport, + enum bfa_pport_sm_event event); + +static struct bfa_sm_table_s hal_pport_sm_table[] = { + {BFA_SM(bfa_pport_sm_uninit), BFA_PPORT_ST_UNINIT}, + {BFA_SM(bfa_pport_sm_enabling_qwait), BFA_PPORT_ST_ENABLING_QWAIT}, + {BFA_SM(bfa_pport_sm_enabling), BFA_PPORT_ST_ENABLING}, + {BFA_SM(bfa_pport_sm_linkdown), BFA_PPORT_ST_LINKDOWN}, + {BFA_SM(bfa_pport_sm_linkup), BFA_PPORT_ST_LINKUP}, + {BFA_SM(bfa_pport_sm_disabling_qwait), + BFA_PPORT_ST_DISABLING_QWAIT}, + {BFA_SM(bfa_pport_sm_disabling), BFA_PPORT_ST_DISABLING}, + {BFA_SM(bfa_pport_sm_disabled), BFA_PPORT_ST_DISABLED}, + {BFA_SM(bfa_pport_sm_stopped), BFA_PPORT_ST_STOPPED}, + {BFA_SM(bfa_pport_sm_iocdown), BFA_PPORT_ST_IOCDOWN}, + {BFA_SM(bfa_pport_sm_iocfail), BFA_PPORT_ST_IOCDOWN}, +}; + +static void +bfa_pport_aen_post(struct bfa_pport_s *pport, enum bfa_port_aen_event event) +{ + union bfa_aen_data_u aen_data; + struct bfa_log_mod_s *logmod = pport->bfa->logm; + wwn_t pwwn = pport->pwwn; + char pwwn_ptr[BFA_STRING_32]; + struct bfa_ioc_attr_s ioc_attr; + + wwn2str(pwwn_ptr, pwwn); + switch (event) { + case BFA_PORT_AEN_ONLINE: + bfa_log(logmod, BFA_AEN_PORT_ONLINE, pwwn_ptr); + break; + case BFA_PORT_AEN_OFFLINE: + bfa_log(logmod, BFA_AEN_PORT_OFFLINE, pwwn_ptr); + break; + case BFA_PORT_AEN_ENABLE: + bfa_log(logmod, BFA_AEN_PORT_ENABLE, pwwn_ptr); + break; + case BFA_PORT_AEN_DISABLE: + bfa_log(logmod, BFA_AEN_PORT_DISABLE, pwwn_ptr); + break; + case BFA_PORT_AEN_DISCONNECT: + bfa_log(logmod, BFA_AEN_PORT_DISCONNECT, pwwn_ptr); + break; + case BFA_PORT_AEN_QOS_NEG: + bfa_log(logmod, BFA_AEN_PORT_QOS_NEG, pwwn_ptr); + break; + default: + break; + } + + bfa_ioc_get_attr(&pport->bfa->ioc, &ioc_attr); + aen_data.port.ioc_type = ioc_attr.ioc_type; + aen_data.port.pwwn = pwwn; +} + +static void +bfa_pport_sm_uninit(struct bfa_pport_s *pport, enum bfa_pport_sm_event event) +{ + bfa_trc(pport->bfa, event); + + switch (event) { + case BFA_PPORT_SM_START: + /** + * Start event after IOC is configured and BFA is started. + */ + if (bfa_pport_send_enable(pport)) + bfa_sm_set_state(pport, bfa_pport_sm_enabling); + else + bfa_sm_set_state(pport, bfa_pport_sm_enabling_qwait); + break; + + case BFA_PPORT_SM_ENABLE: + /** + * Port is persistently configured to be in enabled state. Do + * not change state. Port enabling is done when START event is + * received. + */ + break; + + case BFA_PPORT_SM_DISABLE: + /** + * If a port is persistently configured to be disabled, the + * first event will a port disable request. + */ + bfa_sm_set_state(pport, bfa_pport_sm_disabled); + break; + + case BFA_PPORT_SM_HWFAIL: + bfa_sm_set_state(pport, bfa_pport_sm_iocdown); + break; + + default: + bfa_sm_fault(pport->bfa, event); + } +} + +static void +bfa_pport_sm_enabling_qwait(struct bfa_pport_s *pport, + enum bfa_pport_sm_event event) +{ + bfa_trc(pport->bfa, event); + + switch (event) { + case BFA_PPORT_SM_QRESUME: + bfa_sm_set_state(pport, bfa_pport_sm_enabling); + bfa_pport_send_enable(pport); + break; + + case BFA_PPORT_SM_STOP: + bfa_reqq_wcancel(&pport->reqq_wait); + bfa_sm_set_state(pport, bfa_pport_sm_stopped); + break; + + case BFA_PPORT_SM_ENABLE: + /** + * Already enable is in progress. + */ + break; + + case BFA_PPORT_SM_DISABLE: + /** + * Just send disable request to firmware when room becomes + * available in request queue. + */ + bfa_sm_set_state(pport, bfa_pport_sm_disabled); + bfa_reqq_wcancel(&pport->reqq_wait); + bfa_plog_str(pport->bfa->plog, BFA_PL_MID_HAL, + BFA_PL_EID_PORT_DISABLE, 0, "Port Disable"); + bfa_pport_aen_post(pport, BFA_PORT_AEN_DISABLE); + break; + + case BFA_PPORT_SM_LINKUP: + case BFA_PPORT_SM_LINKDOWN: + /** + * Possible to get link events when doing back-to-back + * enable/disables. + */ + break; + + case BFA_PPORT_SM_HWFAIL: + bfa_reqq_wcancel(&pport->reqq_wait); + bfa_sm_set_state(pport, bfa_pport_sm_iocdown); + break; + + default: + bfa_sm_fault(pport->bfa, event); + } +} + +static void +bfa_pport_sm_enabling(struct bfa_pport_s *pport, enum bfa_pport_sm_event event) +{ + bfa_trc(pport->bfa, event); + + switch (event) { + case BFA_PPORT_SM_FWRSP: + case BFA_PPORT_SM_LINKDOWN: + bfa_sm_set_state(pport, bfa_pport_sm_linkdown); + break; + + case BFA_PPORT_SM_LINKUP: + bfa_pport_update_linkinfo(pport); + bfa_sm_set_state(pport, bfa_pport_sm_linkup); + + bfa_assert(pport->event_cbfn); + bfa_pport_callback(pport, BFA_PPORT_LINKUP); + break; + + case BFA_PPORT_SM_ENABLE: + /** + * Already being enabled. + */ + break; + + case BFA_PPORT_SM_DISABLE: + if (bfa_pport_send_disable(pport)) + bfa_sm_set_state(pport, bfa_pport_sm_disabling); + else + bfa_sm_set_state(pport, bfa_pport_sm_disabling_qwait); + + bfa_plog_str(pport->bfa->plog, BFA_PL_MID_HAL, + BFA_PL_EID_PORT_DISABLE, 0, "Port Disable"); + bfa_pport_aen_post(pport, BFA_PORT_AEN_DISABLE); + break; + + case BFA_PPORT_SM_STOP: + bfa_sm_set_state(pport, bfa_pport_sm_stopped); + break; + + case BFA_PPORT_SM_HWFAIL: + bfa_sm_set_state(pport, bfa_pport_sm_iocdown); + break; + + default: + bfa_sm_fault(pport->bfa, event); + } +} + +static void +bfa_pport_sm_linkdown(struct bfa_pport_s *pport, enum bfa_pport_sm_event event) +{ + bfa_trc(pport->bfa, event); + + switch (event) { + case BFA_PPORT_SM_LINKUP: + bfa_pport_update_linkinfo(pport); + bfa_sm_set_state(pport, bfa_pport_sm_linkup); + bfa_assert(pport->event_cbfn); + bfa_plog_str(pport->bfa->plog, BFA_PL_MID_HAL, + BFA_PL_EID_PORT_ST_CHANGE, 0, "Port Linkup"); + bfa_pport_callback(pport, BFA_PPORT_LINKUP); + bfa_pport_aen_post(pport, BFA_PORT_AEN_ONLINE); + /** + * If QoS is enabled and it is not online, + * Send a separate event. + */ + if ((pport->cfg.qos_enabled) + && (bfa_os_ntohl(pport->qos_attr.state) != BFA_QOS_ONLINE)) + bfa_pport_aen_post(pport, BFA_PORT_AEN_QOS_NEG); + + break; + + case BFA_PPORT_SM_LINKDOWN: + /** + * Possible to get link down event. + */ + break; + + case BFA_PPORT_SM_ENABLE: + /** + * Already enabled. + */ + break; + + case BFA_PPORT_SM_DISABLE: + if (bfa_pport_send_disable(pport)) + bfa_sm_set_state(pport, bfa_pport_sm_disabling); + else + bfa_sm_set_state(pport, bfa_pport_sm_disabling_qwait); + + bfa_plog_str(pport->bfa->plog, BFA_PL_MID_HAL, + BFA_PL_EID_PORT_DISABLE, 0, "Port Disable"); + bfa_pport_aen_post(pport, BFA_PORT_AEN_DISABLE); + break; + + case BFA_PPORT_SM_STOP: + bfa_sm_set_state(pport, bfa_pport_sm_stopped); + break; + + case BFA_PPORT_SM_HWFAIL: + bfa_sm_set_state(pport, bfa_pport_sm_iocdown); + break; + + default: + bfa_sm_fault(pport->bfa, event); + } +} + +static void +bfa_pport_sm_linkup(struct bfa_pport_s *pport, enum bfa_pport_sm_event event) +{ + bfa_trc(pport->bfa, event); + + switch (event) { + case BFA_PPORT_SM_ENABLE: + /** + * Already enabled. + */ + break; + + case BFA_PPORT_SM_DISABLE: + if (bfa_pport_send_disable(pport)) + bfa_sm_set_state(pport, bfa_pport_sm_disabling); + else + bfa_sm_set_state(pport, bfa_pport_sm_disabling_qwait); + + bfa_pport_reset_linkinfo(pport); + bfa_pport_callback(pport, BFA_PPORT_LINKDOWN); + bfa_plog_str(pport->bfa->plog, BFA_PL_MID_HAL, + BFA_PL_EID_PORT_DISABLE, 0, "Port Disable"); + bfa_pport_aen_post(pport, BFA_PORT_AEN_OFFLINE); + bfa_pport_aen_post(pport, BFA_PORT_AEN_DISABLE); + break; + + case BFA_PPORT_SM_LINKDOWN: + bfa_sm_set_state(pport, bfa_pport_sm_linkdown); + bfa_pport_reset_linkinfo(pport); + bfa_pport_callback(pport, BFA_PPORT_LINKDOWN); + bfa_plog_str(pport->bfa->plog, BFA_PL_MID_HAL, + BFA_PL_EID_PORT_ST_CHANGE, 0, "Port Linkdown"); + if (BFA_PORT_IS_DISABLED(pport->bfa)) { + bfa_pport_aen_post(pport, BFA_PORT_AEN_OFFLINE); + } else { + bfa_pport_aen_post(pport, BFA_PORT_AEN_DISCONNECT); + } + break; + + case BFA_PPORT_SM_STOP: + bfa_sm_set_state(pport, bfa_pport_sm_stopped); + bfa_pport_reset_linkinfo(pport); + if (BFA_PORT_IS_DISABLED(pport->bfa)) { + bfa_pport_aen_post(pport, BFA_PORT_AEN_OFFLINE); + } else { + bfa_pport_aen_post(pport, BFA_PORT_AEN_DISCONNECT); + } + break; + + case BFA_PPORT_SM_HWFAIL: + bfa_sm_set_state(pport, bfa_pport_sm_iocdown); + bfa_pport_reset_linkinfo(pport); + bfa_pport_callback(pport, BFA_PPORT_LINKDOWN); + if (BFA_PORT_IS_DISABLED(pport->bfa)) { + bfa_pport_aen_post(pport, BFA_PORT_AEN_OFFLINE); + } else { + bfa_pport_aen_post(pport, BFA_PORT_AEN_DISCONNECT); + } + break; + + default: + bfa_sm_fault(pport->bfa, event); + } +} + +static void +bfa_pport_sm_disabling_qwait(struct bfa_pport_s *pport, + enum bfa_pport_sm_event event) +{ + bfa_trc(pport->bfa, event); + + switch (event) { + case BFA_PPORT_SM_QRESUME: + bfa_sm_set_state(pport, bfa_pport_sm_disabling); + bfa_pport_send_disable(pport); + break; + + case BFA_PPORT_SM_STOP: + bfa_sm_set_state(pport, bfa_pport_sm_stopped); + bfa_reqq_wcancel(&pport->reqq_wait); + break; + + case BFA_PPORT_SM_DISABLE: + /** + * Already being disabled. + */ + break; + + case BFA_PPORT_SM_LINKUP: + case BFA_PPORT_SM_LINKDOWN: + /** + * Possible to get link events when doing back-to-back + * enable/disables. + */ + break; + + case BFA_PPORT_SM_HWFAIL: + bfa_sm_set_state(pport, bfa_pport_sm_iocfail); + bfa_reqq_wcancel(&pport->reqq_wait); + break; + + default: + bfa_sm_fault(pport->bfa, event); + } +} + +static void +bfa_pport_sm_disabling(struct bfa_pport_s *pport, enum bfa_pport_sm_event event) +{ + bfa_trc(pport->bfa, event); + + switch (event) { + case BFA_PPORT_SM_FWRSP: + bfa_sm_set_state(pport, bfa_pport_sm_disabled); + break; + + case BFA_PPORT_SM_DISABLE: + /** + * Already being disabled. + */ + break; + + case BFA_PPORT_SM_ENABLE: + if (bfa_pport_send_enable(pport)) + bfa_sm_set_state(pport, bfa_pport_sm_enabling); + else + bfa_sm_set_state(pport, bfa_pport_sm_enabling_qwait); + + bfa_plog_str(pport->bfa->plog, BFA_PL_MID_HAL, + BFA_PL_EID_PORT_ENABLE, 0, "Port Enable"); + bfa_pport_aen_post(pport, BFA_PORT_AEN_ENABLE); + break; + + case BFA_PPORT_SM_STOP: + bfa_sm_set_state(pport, bfa_pport_sm_stopped); + break; + + case BFA_PPORT_SM_LINKUP: + case BFA_PPORT_SM_LINKDOWN: + /** + * Possible to get link events when doing back-to-back + * enable/disables. + */ + break; + + case BFA_PPORT_SM_HWFAIL: + bfa_sm_set_state(pport, bfa_pport_sm_iocfail); + break; + + default: + bfa_sm_fault(pport->bfa, event); + } +} + +static void +bfa_pport_sm_disabled(struct bfa_pport_s *pport, enum bfa_pport_sm_event event) +{ + bfa_trc(pport->bfa, event); + + switch (event) { + case BFA_PPORT_SM_START: + /** + * Ignore start event for a port that is disabled. + */ + break; + + case BFA_PPORT_SM_STOP: + bfa_sm_set_state(pport, bfa_pport_sm_stopped); + break; + + case BFA_PPORT_SM_ENABLE: + if (bfa_pport_send_enable(pport)) + bfa_sm_set_state(pport, bfa_pport_sm_enabling); + else + bfa_sm_set_state(pport, bfa_pport_sm_enabling_qwait); + + bfa_plog_str(pport->bfa->plog, BFA_PL_MID_HAL, + BFA_PL_EID_PORT_ENABLE, 0, "Port Enable"); + bfa_pport_aen_post(pport, BFA_PORT_AEN_ENABLE); + break; + + case BFA_PPORT_SM_DISABLE: + /** + * Already disabled. + */ + break; + + case BFA_PPORT_SM_HWFAIL: + bfa_sm_set_state(pport, bfa_pport_sm_iocfail); + break; + + default: + bfa_sm_fault(pport->bfa, event); + } +} + +static void +bfa_pport_sm_stopped(struct bfa_pport_s *pport, enum bfa_pport_sm_event event) +{ + bfa_trc(pport->bfa, event); + + switch (event) { + case BFA_PPORT_SM_START: + if (bfa_pport_send_enable(pport)) + bfa_sm_set_state(pport, bfa_pport_sm_enabling); + else + bfa_sm_set_state(pport, bfa_pport_sm_enabling_qwait); + break; + + default: + /** + * Ignore all other events. + */ + ; + } +} + +/** + * Port is enabled. IOC is down/failed. + */ +static void +bfa_pport_sm_iocdown(struct bfa_pport_s *pport, enum bfa_pport_sm_event event) +{ + bfa_trc(pport->bfa, event); + + switch (event) { + case BFA_PPORT_SM_START: + if (bfa_pport_send_enable(pport)) + bfa_sm_set_state(pport, bfa_pport_sm_enabling); + else + bfa_sm_set_state(pport, bfa_pport_sm_enabling_qwait); + break; + + default: + /** + * Ignore all events. + */ + ; + } +} + +/** + * Port is disabled. IOC is down/failed. + */ +static void +bfa_pport_sm_iocfail(struct bfa_pport_s *pport, enum bfa_pport_sm_event event) +{ + bfa_trc(pport->bfa, event); + + switch (event) { + case BFA_PPORT_SM_START: + bfa_sm_set_state(pport, bfa_pport_sm_disabled); + break; + + case BFA_PPORT_SM_ENABLE: + bfa_sm_set_state(pport, bfa_pport_sm_iocdown); + break; + + default: + /** + * Ignore all events. + */ + ; + } +} + + + +/** + * bfa_pport_private + */ + +static void +__bfa_cb_port_event(void *cbarg, bfa_boolean_t complete) +{ + struct bfa_pport_s *pport = cbarg; + + if (complete) + pport->event_cbfn(pport->event_cbarg, pport->hcb_event); +} + +#define PPORT_STATS_DMA_SZ (BFA_ROUNDUP(sizeof(union bfa_pport_stats_u), \ + BFA_CACHELINE_SZ)) + +static void +bfa_pport_meminfo(struct bfa_iocfc_cfg_s *cfg, u32 *ndm_len, + u32 *dm_len) +{ + *dm_len += PPORT_STATS_DMA_SZ; +} + +static void +bfa_pport_qresume(void *cbarg) +{ + struct bfa_pport_s *port = cbarg; + + bfa_sm_send_event(port, BFA_PPORT_SM_QRESUME); +} + +static void +bfa_pport_mem_claim(struct bfa_pport_s *pport, struct bfa_meminfo_s *meminfo) +{ + u8 *dm_kva; + u64 dm_pa; + + dm_kva = bfa_meminfo_dma_virt(meminfo); + dm_pa = bfa_meminfo_dma_phys(meminfo); + + pport->stats_kva = dm_kva; + pport->stats_pa = dm_pa; + pport->stats = (union bfa_pport_stats_u *)dm_kva; + + dm_kva += PPORT_STATS_DMA_SZ; + dm_pa += PPORT_STATS_DMA_SZ; + + bfa_meminfo_dma_virt(meminfo) = dm_kva; + bfa_meminfo_dma_phys(meminfo) = dm_pa; +} + +/** + * Memory initialization. + */ +static void +bfa_pport_attach(struct bfa_s *bfa, void *bfad, struct bfa_iocfc_cfg_s *cfg, + struct bfa_meminfo_s *meminfo, struct bfa_pcidev_s *pcidev) +{ + struct bfa_pport_s *pport = BFA_PORT_MOD(bfa); + struct bfa_pport_cfg_s *port_cfg = &pport->cfg; + + bfa_os_memset(pport, 0, sizeof(struct bfa_pport_s)); + pport->bfa = bfa; + + bfa_pport_mem_claim(pport, meminfo); + + bfa_sm_set_state(pport, bfa_pport_sm_uninit); + + /** + * initialize and set default configuration + */ + port_cfg->topology = BFA_PPORT_TOPOLOGY_P2P; + port_cfg->speed = BFA_PPORT_SPEED_AUTO; + port_cfg->trunked = BFA_FALSE; + port_cfg->maxfrsize = 0; + + port_cfg->trl_def_speed = BFA_PPORT_SPEED_1GBPS; + + bfa_reqq_winit(&pport->reqq_wait, bfa_pport_qresume, pport); +} + +static void +bfa_pport_initdone(struct bfa_s *bfa) +{ + struct bfa_pport_s *pport = BFA_PORT_MOD(bfa); + + /** + * Initialize port attributes from IOC hardware data. + */ + bfa_pport_set_wwns(pport); + if (pport->cfg.maxfrsize == 0) + pport->cfg.maxfrsize = bfa_ioc_maxfrsize(&bfa->ioc); + pport->cfg.rx_bbcredit = bfa_ioc_rx_bbcredit(&bfa->ioc); + pport->speed_sup = bfa_ioc_speed_sup(&bfa->ioc); + + bfa_assert(pport->cfg.maxfrsize); + bfa_assert(pport->cfg.rx_bbcredit); + bfa_assert(pport->speed_sup); +} + +static void +bfa_pport_detach(struct bfa_s *bfa) +{ +} + +/** + * Called when IOC is ready. + */ +static void +bfa_pport_start(struct bfa_s *bfa) +{ + bfa_sm_send_event(BFA_PORT_MOD(bfa), BFA_PPORT_SM_START); +} + +/** + * Called before IOC is stopped. + */ +static void +bfa_pport_stop(struct bfa_s *bfa) +{ + bfa_sm_send_event(BFA_PORT_MOD(bfa), BFA_PPORT_SM_STOP); +} + +/** + * Called when IOC failure is detected. + */ +static void +bfa_pport_iocdisable(struct bfa_s *bfa) +{ + bfa_sm_send_event(BFA_PORT_MOD(bfa), BFA_PPORT_SM_HWFAIL); +} + +static void +bfa_pport_update_linkinfo(struct bfa_pport_s *pport) +{ + struct bfi_pport_event_s *pevent = pport->event_arg.i2hmsg.event; + + pport->speed = pevent->link_state.speed; + pport->topology = pevent->link_state.topology; + + if (pport->topology == BFA_PPORT_TOPOLOGY_LOOP) + pport->myalpa = pevent->link_state.tl.loop_info.myalpa; + + /* + * QoS Details + */ + bfa_os_assign(pport->qos_attr, pevent->link_state.qos_attr); + bfa_os_assign(pport->qos_vc_attr, pevent->link_state.qos_vc_attr); + + bfa_trc(pport->bfa, pport->speed); + bfa_trc(pport->bfa, pport->topology); +} + +static void +bfa_pport_reset_linkinfo(struct bfa_pport_s *pport) +{ + pport->speed = BFA_PPORT_SPEED_UNKNOWN; + pport->topology = BFA_PPORT_TOPOLOGY_NONE; +} + +/** + * Send port enable message to firmware. + */ +static bfa_boolean_t +bfa_pport_send_enable(struct bfa_pport_s *port) +{ + struct bfi_pport_enable_req_s *m; + + /** + * Increment message tag before queue check, so that responses to old + * requests are discarded. + */ + port->msgtag++; + + /** + * check for room in queue to send request now + */ + m = bfa_reqq_next(port->bfa, BFA_REQQ_PORT); + if (!m) { + bfa_reqq_wait(port->bfa, BFA_REQQ_PORT, &port->reqq_wait); + return BFA_FALSE; + } + + bfi_h2i_set(m->mh, BFI_MC_FC_PORT, BFI_PPORT_H2I_ENABLE_REQ, + bfa_lpuid(port->bfa)); + m->nwwn = port->nwwn; + m->pwwn = port->pwwn; + m->port_cfg = port->cfg; + m->msgtag = port->msgtag; + m->port_cfg.maxfrsize = bfa_os_htons(port->cfg.maxfrsize); + bfa_dma_be_addr_set(m->stats_dma_addr, port->stats_pa); + bfa_trc(port->bfa, m->stats_dma_addr.a32.addr_lo); + bfa_trc(port->bfa, m->stats_dma_addr.a32.addr_hi); + + /** + * queue I/O message to firmware + */ + bfa_reqq_produce(port->bfa, BFA_REQQ_PORT); + return BFA_TRUE; +} + +/** + * Send port disable message to firmware. + */ +static bfa_boolean_t +bfa_pport_send_disable(struct bfa_pport_s *port) +{ + bfi_pport_disable_req_t *m; + + /** + * Increment message tag before queue check, so that responses to old + * requests are discarded. + */ + port->msgtag++; + + /** + * check for room in queue to send request now + */ + m = bfa_reqq_next(port->bfa, BFA_REQQ_PORT); + if (!m) { + bfa_reqq_wait(port->bfa, BFA_REQQ_PORT, &port->reqq_wait); + return BFA_FALSE; + } + + bfi_h2i_set(m->mh, BFI_MC_FC_PORT, BFI_PPORT_H2I_DISABLE_REQ, + bfa_lpuid(port->bfa)); + m->msgtag = port->msgtag; + + /** + * queue I/O message to firmware + */ + bfa_reqq_produce(port->bfa, BFA_REQQ_PORT); + + return BFA_TRUE; +} + +static void +bfa_pport_set_wwns(struct bfa_pport_s *port) +{ + port->pwwn = bfa_ioc_get_pwwn(&port->bfa->ioc); + port->nwwn = bfa_ioc_get_nwwn(&port->bfa->ioc); + + bfa_trc(port->bfa, port->pwwn); + bfa_trc(port->bfa, port->nwwn); +} + +static void +bfa_port_send_txcredit(void *port_cbarg) +{ + + struct bfa_pport_s *port = port_cbarg; + struct bfi_pport_set_svc_params_req_s *m; + + /** + * check for room in queue to send request now + */ + m = bfa_reqq_next(port->bfa, BFA_REQQ_PORT); + if (!m) { + bfa_trc(port->bfa, port->cfg.tx_bbcredit); + return; + } + + bfi_h2i_set(m->mh, BFI_MC_FC_PORT, BFI_PPORT_H2I_SET_SVC_PARAMS_REQ, + bfa_lpuid(port->bfa)); + m->tx_bbcredit = bfa_os_htons((u16) port->cfg.tx_bbcredit); + + /** + * queue I/O message to firmware + */ + bfa_reqq_produce(port->bfa, BFA_REQQ_PORT); +} + + + +/** + * bfa_pport_public + */ + +/** + * Firmware message handler. + */ +void +bfa_pport_isr(struct bfa_s *bfa, struct bfi_msg_s *msg) +{ + struct bfa_pport_s *pport = BFA_PORT_MOD(bfa); + union bfi_pport_i2h_msg_u i2hmsg; + + i2hmsg.msg = msg; + pport->event_arg.i2hmsg = i2hmsg; + + switch (msg->mhdr.msg_id) { + case BFI_PPORT_I2H_ENABLE_RSP: + if (pport->msgtag == i2hmsg.enable_rsp->msgtag) + bfa_sm_send_event(pport, BFA_PPORT_SM_FWRSP); + break; + + case BFI_PPORT_I2H_DISABLE_RSP: + if (pport->msgtag == i2hmsg.enable_rsp->msgtag) + bfa_sm_send_event(pport, BFA_PPORT_SM_FWRSP); + break; + + case BFI_PPORT_I2H_EVENT: + switch (i2hmsg.event->link_state.linkstate) { + case BFA_PPORT_LINKUP: + bfa_sm_send_event(pport, BFA_PPORT_SM_LINKUP); + break; + case BFA_PPORT_LINKDOWN: + bfa_sm_send_event(pport, BFA_PPORT_SM_LINKDOWN); + break; + case BFA_PPORT_TRUNK_LINKDOWN: + /** todo: event notification */ + break; + } + break; + + case BFI_PPORT_I2H_GET_STATS_RSP: + case BFI_PPORT_I2H_GET_QOS_STATS_RSP: + /* + * check for timer pop before processing the rsp + */ + if (pport->stats_busy == BFA_FALSE + || pport->stats_status == BFA_STATUS_ETIMER) + break; + + bfa_timer_stop(&pport->timer); + pport->stats_status = i2hmsg.getstats_rsp->status; + bfa_cb_queue(pport->bfa, &pport->hcb_qe, __bfa_cb_port_stats, + pport); + break; + case BFI_PPORT_I2H_CLEAR_STATS_RSP: + case BFI_PPORT_I2H_CLEAR_QOS_STATS_RSP: + /* + * check for timer pop before processing the rsp + */ + if (pport->stats_busy == BFA_FALSE + || pport->stats_status == BFA_STATUS_ETIMER) + break; + + bfa_timer_stop(&pport->timer); + pport->stats_status = BFA_STATUS_OK; + bfa_cb_queue(pport->bfa, &pport->hcb_qe, + __bfa_cb_port_stats_clr, pport); + break; + + default: + bfa_assert(0); + } +} + + + +/** + * bfa_pport_api + */ + +/** + * Registered callback for port events. + */ +void +bfa_pport_event_register(struct bfa_s *bfa, + void (*cbfn) (void *cbarg, bfa_pport_event_t event), + void *cbarg) +{ + struct bfa_pport_s *pport = BFA_PORT_MOD(bfa); + + pport->event_cbfn = cbfn; + pport->event_cbarg = cbarg; +} + +bfa_status_t +bfa_pport_enable(struct bfa_s *bfa) +{ + struct bfa_pport_s *pport = BFA_PORT_MOD(bfa); + + if (pport->diag_busy) + return (BFA_STATUS_DIAG_BUSY); + else if (bfa_sm_cmp_state + (BFA_PORT_MOD(bfa), bfa_pport_sm_disabling_qwait)) + return (BFA_STATUS_DEVBUSY); + + bfa_sm_send_event(BFA_PORT_MOD(bfa), BFA_PPORT_SM_ENABLE); + return BFA_STATUS_OK; +} + +bfa_status_t +bfa_pport_disable(struct bfa_s *bfa) +{ + bfa_sm_send_event(BFA_PORT_MOD(bfa), BFA_PPORT_SM_DISABLE); + return BFA_STATUS_OK; +} + +/** + * Configure port speed. + */ +bfa_status_t +bfa_pport_cfg_speed(struct bfa_s *bfa, enum bfa_pport_speed speed) +{ + struct bfa_pport_s *pport = BFA_PORT_MOD(bfa); + + bfa_trc(bfa, speed); + + if ((speed != BFA_PPORT_SPEED_AUTO) && (speed > pport->speed_sup)) { + bfa_trc(bfa, pport->speed_sup); + return BFA_STATUS_UNSUPP_SPEED; + } + + pport->cfg.speed = speed; + + return (BFA_STATUS_OK); +} + +/** + * Get current speed. + */ +enum bfa_pport_speed +bfa_pport_get_speed(struct bfa_s *bfa) +{ + struct bfa_pport_s *port = BFA_PORT_MOD(bfa); + + return port->speed; +} + +/** + * Configure port topology. + */ +bfa_status_t +bfa_pport_cfg_topology(struct bfa_s *bfa, enum bfa_pport_topology topology) +{ + struct bfa_pport_s *pport = BFA_PORT_MOD(bfa); + + bfa_trc(bfa, topology); + bfa_trc(bfa, pport->cfg.topology); + + switch (topology) { + case BFA_PPORT_TOPOLOGY_P2P: + case BFA_PPORT_TOPOLOGY_LOOP: + case BFA_PPORT_TOPOLOGY_AUTO: + break; + + default: + return BFA_STATUS_EINVAL; + } + + pport->cfg.topology = topology; + return (BFA_STATUS_OK); +} + +/** + * Get current topology. + */ +enum bfa_pport_topology +bfa_pport_get_topology(struct bfa_s *bfa) +{ + struct bfa_pport_s *port = BFA_PORT_MOD(bfa); + + return port->topology; +} + +bfa_status_t +bfa_pport_cfg_hardalpa(struct bfa_s *bfa, u8 alpa) +{ + struct bfa_pport_s *pport = BFA_PORT_MOD(bfa); + + bfa_trc(bfa, alpa); + bfa_trc(bfa, pport->cfg.cfg_hardalpa); + bfa_trc(bfa, pport->cfg.hardalpa); + + pport->cfg.cfg_hardalpa = BFA_TRUE; + pport->cfg.hardalpa = alpa; + + return (BFA_STATUS_OK); +} + +bfa_status_t +bfa_pport_clr_hardalpa(struct bfa_s *bfa) +{ + struct bfa_pport_s *pport = BFA_PORT_MOD(bfa); + + bfa_trc(bfa, pport->cfg.cfg_hardalpa); + bfa_trc(bfa, pport->cfg.hardalpa); + + pport->cfg.cfg_hardalpa = BFA_FALSE; + return (BFA_STATUS_OK); +} + +bfa_boolean_t +bfa_pport_get_hardalpa(struct bfa_s *bfa, u8 *alpa) +{ + struct bfa_pport_s *port = BFA_PORT_MOD(bfa); + + *alpa = port->cfg.hardalpa; + return port->cfg.cfg_hardalpa; +} + +u8 +bfa_pport_get_myalpa(struct bfa_s *bfa) +{ + struct bfa_pport_s *port = BFA_PORT_MOD(bfa); + + return port->myalpa; +} + +bfa_status_t +bfa_pport_cfg_maxfrsize(struct bfa_s *bfa, u16 maxfrsize) +{ + struct bfa_pport_s *pport = BFA_PORT_MOD(bfa); + + bfa_trc(bfa, maxfrsize); + bfa_trc(bfa, pport->cfg.maxfrsize); + + /* + * with in range + */ + if ((maxfrsize > FC_MAX_PDUSZ) || (maxfrsize < FC_MIN_PDUSZ)) + return (BFA_STATUS_INVLD_DFSZ); + + /* + * power of 2, if not the max frame size of 2112 + */ + if ((maxfrsize != FC_MAX_PDUSZ) && (maxfrsize & (maxfrsize - 1))) + return (BFA_STATUS_INVLD_DFSZ); + + pport->cfg.maxfrsize = maxfrsize; + return (BFA_STATUS_OK); +} + +u16 +bfa_pport_get_maxfrsize(struct bfa_s *bfa) +{ + struct bfa_pport_s *port = BFA_PORT_MOD(bfa); + + return port->cfg.maxfrsize; +} + +u32 +bfa_pport_mypid(struct bfa_s *bfa) +{ + struct bfa_pport_s *port = BFA_PORT_MOD(bfa); + + return port->mypid; +} + +u8 +bfa_pport_get_rx_bbcredit(struct bfa_s *bfa) +{ + struct bfa_pport_s *port = BFA_PORT_MOD(bfa); + + return port->cfg.rx_bbcredit; +} + +void +bfa_pport_set_tx_bbcredit(struct bfa_s *bfa, u16 tx_bbcredit) +{ + struct bfa_pport_s *port = BFA_PORT_MOD(bfa); + + port->cfg.tx_bbcredit = (u8) tx_bbcredit; + bfa_port_send_txcredit(port); +} + +/** + * Get port attributes. + */ + +wwn_t +bfa_pport_get_wwn(struct bfa_s *bfa, bfa_boolean_t node) +{ + struct bfa_pport_s *pport = BFA_PORT_MOD(bfa); + if (node) + return pport->nwwn; + else + return pport->pwwn; +} + +void +bfa_pport_get_attr(struct bfa_s *bfa, struct bfa_pport_attr_s *attr) +{ + struct bfa_pport_s *pport = BFA_PORT_MOD(bfa); + + bfa_os_memset(attr, 0, sizeof(struct bfa_pport_attr_s)); + + attr->nwwn = pport->nwwn; + attr->pwwn = pport->pwwn; + + bfa_os_memcpy(&attr->pport_cfg, &pport->cfg, + sizeof(struct bfa_pport_cfg_s)); + /* + * speed attributes + */ + attr->pport_cfg.speed = pport->cfg.speed; + attr->speed_supported = pport->speed_sup; + attr->speed = pport->speed; + attr->cos_supported = FC_CLASS_3; + + /* + * topology attributes + */ + attr->pport_cfg.topology = pport->cfg.topology; + attr->topology = pport->topology; + + /* + * beacon attributes + */ + attr->beacon = pport->beacon; + attr->link_e2e_beacon = pport->link_e2e_beacon; + attr->plog_enabled = bfa_plog_get_setting(pport->bfa->plog); + + attr->pport_cfg.path_tov = bfa_fcpim_path_tov_get(bfa); + attr->pport_cfg.q_depth = bfa_fcpim_qdepth_get(bfa); + attr->port_state = bfa_sm_to_state(hal_pport_sm_table, pport->sm); + if (bfa_ioc_is_disabled(&pport->bfa->ioc)) + attr->port_state = BFA_PPORT_ST_IOCDIS; + else if (bfa_ioc_fw_mismatch(&pport->bfa->ioc)) + attr->port_state = BFA_PPORT_ST_FWMISMATCH; +} + +static void +bfa_port_stats_query(void *cbarg) +{ + struct bfa_pport_s *port = (struct bfa_pport_s *)cbarg; + bfi_pport_get_stats_req_t *msg; + + msg = bfa_reqq_next(port->bfa, BFA_REQQ_PORT); + + if (!msg) { + port->stats_qfull = BFA_TRUE; + bfa_reqq_winit(&port->stats_reqq_wait, bfa_port_stats_query, + port); + bfa_reqq_wait(port->bfa, BFA_REQQ_PORT, &port->stats_reqq_wait); + return; + } + port->stats_qfull = BFA_FALSE; + + bfa_os_memset(msg, 0, sizeof(bfi_pport_get_stats_req_t)); + bfi_h2i_set(msg->mh, BFI_MC_FC_PORT, BFI_PPORT_H2I_GET_STATS_REQ, + bfa_lpuid(port->bfa)); + bfa_reqq_produce(port->bfa, BFA_REQQ_PORT); + + return; +} + +static void +bfa_port_stats_clear(void *cbarg) +{ + struct bfa_pport_s *port = (struct bfa_pport_s *)cbarg; + bfi_pport_clear_stats_req_t *msg; + + msg = bfa_reqq_next(port->bfa, BFA_REQQ_PORT); + + if (!msg) { + port->stats_qfull = BFA_TRUE; + bfa_reqq_winit(&port->stats_reqq_wait, bfa_port_stats_clear, + port); + bfa_reqq_wait(port->bfa, BFA_REQQ_PORT, &port->stats_reqq_wait); + return; + } + port->stats_qfull = BFA_FALSE; + + bfa_os_memset(msg, 0, sizeof(bfi_pport_clear_stats_req_t)); + bfi_h2i_set(msg->mh, BFI_MC_FC_PORT, BFI_PPORT_H2I_CLEAR_STATS_REQ, + bfa_lpuid(port->bfa)); + bfa_reqq_produce(port->bfa, BFA_REQQ_PORT); + return; +} + +static void +bfa_port_qos_stats_clear(void *cbarg) +{ + struct bfa_pport_s *port = (struct bfa_pport_s *)cbarg; + bfi_pport_clear_qos_stats_req_t *msg; + + msg = bfa_reqq_next(port->bfa, BFA_REQQ_PORT); + + if (!msg) { + port->stats_qfull = BFA_TRUE; + bfa_reqq_winit(&port->stats_reqq_wait, bfa_port_qos_stats_clear, + port); + bfa_reqq_wait(port->bfa, BFA_REQQ_PORT, &port->stats_reqq_wait); + return; + } + port->stats_qfull = BFA_FALSE; + + bfa_os_memset(msg, 0, sizeof(bfi_pport_clear_qos_stats_req_t)); + bfi_h2i_set(msg->mh, BFI_MC_FC_PORT, BFI_PPORT_H2I_CLEAR_QOS_STATS_REQ, + bfa_lpuid(port->bfa)); + bfa_reqq_produce(port->bfa, BFA_REQQ_PORT); + return; +} + +static void +bfa_pport_stats_swap(union bfa_pport_stats_u *d, union bfa_pport_stats_u *s) +{ + u32 *dip = (u32 *) d; + u32 *sip = (u32 *) s; + int i; + + /* + * Do 64 bit fields swap first + */ + for (i = 0; + i < + ((sizeof(union bfa_pport_stats_u) - + sizeof(struct bfa_qos_stats_s)) / sizeof(u32)); i = i + 2) { +#ifdef __BIGENDIAN + dip[i] = bfa_os_ntohl(sip[i]); + dip[i + 1] = bfa_os_ntohl(sip[i + 1]); +#else + dip[i] = bfa_os_ntohl(sip[i + 1]); + dip[i + 1] = bfa_os_ntohl(sip[i]); +#endif + } + + /* + * Now swap the 32 bit fields + */ + for (; i < (sizeof(union bfa_pport_stats_u) / sizeof(u32)); ++i) + dip[i] = bfa_os_ntohl(sip[i]); +} + +static void +__bfa_cb_port_stats_clr(void *cbarg, bfa_boolean_t complete) +{ + struct bfa_pport_s *port = cbarg; + + if (complete) { + port->stats_cbfn(port->stats_cbarg, port->stats_status); + } else { + port->stats_busy = BFA_FALSE; + port->stats_status = BFA_STATUS_OK; + } +} + +static void +bfa_port_stats_clr_timeout(void *cbarg) +{ + struct bfa_pport_s *port = (struct bfa_pport_s *)cbarg; + + bfa_trc(port->bfa, port->stats_qfull); + + if (port->stats_qfull) { + bfa_reqq_wcancel(&port->stats_reqq_wait); + port->stats_qfull = BFA_FALSE; + } + + port->stats_status = BFA_STATUS_ETIMER; + bfa_cb_queue(port->bfa, &port->hcb_qe, __bfa_cb_port_stats_clr, port); +} + +static void +__bfa_cb_port_stats(void *cbarg, bfa_boolean_t complete) +{ + struct bfa_pport_s *port = cbarg; + + if (complete) { + if (port->stats_status == BFA_STATUS_OK) + bfa_pport_stats_swap(port->stats_ret, port->stats); + port->stats_cbfn(port->stats_cbarg, port->stats_status); + } else { + port->stats_busy = BFA_FALSE; + port->stats_status = BFA_STATUS_OK; + } +} + +static void +bfa_port_stats_timeout(void *cbarg) +{ + struct bfa_pport_s *port = (struct bfa_pport_s *)cbarg; + + bfa_trc(port->bfa, port->stats_qfull); + + if (port->stats_qfull) { + bfa_reqq_wcancel(&port->stats_reqq_wait); + port->stats_qfull = BFA_FALSE; + } + + port->stats_status = BFA_STATUS_ETIMER; + bfa_cb_queue(port->bfa, &port->hcb_qe, __bfa_cb_port_stats, port); +} + +#define BFA_PORT_STATS_TOV 1000 + +/** + * Fetch port attributes. + */ +bfa_status_t +bfa_pport_get_stats(struct bfa_s *bfa, union bfa_pport_stats_u *stats, + bfa_cb_pport_t cbfn, void *cbarg) +{ + struct bfa_pport_s *port = BFA_PORT_MOD(bfa); + + if (port->stats_busy) { + bfa_trc(bfa, port->stats_busy); + return (BFA_STATUS_DEVBUSY); + } + + port->stats_busy = BFA_TRUE; + port->stats_ret = stats; + port->stats_cbfn = cbfn; + port->stats_cbarg = cbarg; + + bfa_port_stats_query(port); + + bfa_timer_start(bfa, &port->timer, bfa_port_stats_timeout, port, + BFA_PORT_STATS_TOV); + return (BFA_STATUS_OK); +} + +bfa_status_t +bfa_pport_clear_stats(struct bfa_s *bfa, bfa_cb_pport_t cbfn, void *cbarg) +{ + struct bfa_pport_s *port = BFA_PORT_MOD(bfa); + + if (port->stats_busy) { + bfa_trc(bfa, port->stats_busy); + return (BFA_STATUS_DEVBUSY); + } + + port->stats_busy = BFA_TRUE; + port->stats_cbfn = cbfn; + port->stats_cbarg = cbarg; + + bfa_port_stats_clear(port); + + bfa_timer_start(bfa, &port->timer, bfa_port_stats_clr_timeout, port, + BFA_PORT_STATS_TOV); + return (BFA_STATUS_OK); +} + +bfa_status_t +bfa_pport_trunk_enable(struct bfa_s *bfa, u8 bitmap) +{ + struct bfa_pport_s *pport = BFA_PORT_MOD(bfa); + + bfa_trc(bfa, bitmap); + bfa_trc(bfa, pport->cfg.trunked); + bfa_trc(bfa, pport->cfg.trunk_ports); + + if (!bitmap || (bitmap & (bitmap - 1))) + return BFA_STATUS_EINVAL; + + pport->cfg.trunked = BFA_TRUE; + pport->cfg.trunk_ports = bitmap; + + return BFA_STATUS_OK; +} + +void +bfa_pport_qos_get_attr(struct bfa_s *bfa, struct bfa_qos_attr_s *qos_attr) +{ + struct bfa_pport_s *pport = BFA_PORT_MOD(bfa); + + qos_attr->state = bfa_os_ntohl(pport->qos_attr.state); + qos_attr->total_bb_cr = bfa_os_ntohl(pport->qos_attr.total_bb_cr); +} + +void +bfa_pport_qos_get_vc_attr(struct bfa_s *bfa, + struct bfa_qos_vc_attr_s *qos_vc_attr) +{ + struct bfa_pport_s *pport = BFA_PORT_MOD(bfa); + struct bfa_qos_vc_attr_s *bfa_vc_attr = &pport->qos_vc_attr; + u32 i = 0; + + qos_vc_attr->total_vc_count = bfa_os_ntohs(bfa_vc_attr->total_vc_count); + qos_vc_attr->shared_credit = bfa_os_ntohs(bfa_vc_attr->shared_credit); + qos_vc_attr->elp_opmode_flags = + bfa_os_ntohl(bfa_vc_attr->elp_opmode_flags); + + /* + * Individual VC info + */ + while (i < qos_vc_attr->total_vc_count) { + qos_vc_attr->vc_info[i].vc_credit = + bfa_vc_attr->vc_info[i].vc_credit; + qos_vc_attr->vc_info[i].borrow_credit = + bfa_vc_attr->vc_info[i].borrow_credit; + qos_vc_attr->vc_info[i].priority = + bfa_vc_attr->vc_info[i].priority; + ++i; + } +} + +/** + * Fetch QoS Stats. + */ +bfa_status_t +bfa_pport_get_qos_stats(struct bfa_s *bfa, union bfa_pport_stats_u *stats, + bfa_cb_pport_t cbfn, void *cbarg) +{ + /* + * QoS stats is embedded in port stats + */ + return (bfa_pport_get_stats(bfa, stats, cbfn, cbarg)); +} + +bfa_status_t +bfa_pport_clear_qos_stats(struct bfa_s *bfa, bfa_cb_pport_t cbfn, void *cbarg) +{ + struct bfa_pport_s *port = BFA_PORT_MOD(bfa); + + if (port->stats_busy) { + bfa_trc(bfa, port->stats_busy); + return (BFA_STATUS_DEVBUSY); + } + + port->stats_busy = BFA_TRUE; + port->stats_cbfn = cbfn; + port->stats_cbarg = cbarg; + + bfa_port_qos_stats_clear(port); + + bfa_timer_start(bfa, &port->timer, bfa_port_stats_clr_timeout, port, + BFA_PORT_STATS_TOV); + return (BFA_STATUS_OK); +} + +/** + * Fetch port attributes. + */ +bfa_status_t +bfa_pport_trunk_disable(struct bfa_s *bfa) +{ + return (BFA_STATUS_OK); +} + +bfa_boolean_t +bfa_pport_trunk_query(struct bfa_s *bfa, u32 *bitmap) +{ + struct bfa_pport_s *port = BFA_PORT_MOD(bfa); + + *bitmap = port->cfg.trunk_ports; + return port->cfg.trunked; +} + +bfa_boolean_t +bfa_pport_is_disabled(struct bfa_s *bfa) +{ + struct bfa_pport_s *port = BFA_PORT_MOD(bfa); + + return (bfa_sm_to_state(hal_pport_sm_table, port->sm) == + BFA_PPORT_ST_DISABLED); + +} + +bfa_boolean_t +bfa_pport_is_ratelim(struct bfa_s *bfa) +{ + struct bfa_pport_s *pport = BFA_PORT_MOD(bfa); + +return (pport->cfg.ratelimit ? BFA_TRUE : BFA_FALSE); + +} + +void +bfa_pport_cfg_qos(struct bfa_s *bfa, bfa_boolean_t on_off) +{ + struct bfa_pport_s *pport = BFA_PORT_MOD(bfa); + + bfa_trc(bfa, on_off); + bfa_trc(bfa, pport->cfg.qos_enabled); + + pport->cfg.qos_enabled = on_off; +} + +void +bfa_pport_cfg_ratelim(struct bfa_s *bfa, bfa_boolean_t on_off) +{ + struct bfa_pport_s *pport = BFA_PORT_MOD(bfa); + + bfa_trc(bfa, on_off); + bfa_trc(bfa, pport->cfg.ratelimit); + + pport->cfg.ratelimit = on_off; + if (pport->cfg.trl_def_speed == BFA_PPORT_SPEED_UNKNOWN) + pport->cfg.trl_def_speed = BFA_PPORT_SPEED_1GBPS; +} + +/** + * Configure default minimum ratelim speed + */ +bfa_status_t +bfa_pport_cfg_ratelim_speed(struct bfa_s *bfa, enum bfa_pport_speed speed) +{ + struct bfa_pport_s *pport = BFA_PORT_MOD(bfa); + + bfa_trc(bfa, speed); + + /* + * Auto and speeds greater than the supported speed, are invalid + */ + if ((speed == BFA_PPORT_SPEED_AUTO) || (speed > pport->speed_sup)) { + bfa_trc(bfa, pport->speed_sup); + return BFA_STATUS_UNSUPP_SPEED; + } + + pport->cfg.trl_def_speed = speed; + + return (BFA_STATUS_OK); +} + +/** + * Get default minimum ratelim speed + */ +enum bfa_pport_speed +bfa_pport_get_ratelim_speed(struct bfa_s *bfa) +{ + struct bfa_pport_s *pport = BFA_PORT_MOD(bfa); + + bfa_trc(bfa, pport->cfg.trl_def_speed); + return (pport->cfg.trl_def_speed); + +} + +void +bfa_pport_busy(struct bfa_s *bfa, bfa_boolean_t status) +{ + struct bfa_pport_s *pport = BFA_PORT_MOD(bfa); + + bfa_trc(bfa, status); + bfa_trc(bfa, pport->diag_busy); + + pport->diag_busy = status; +} + +void +bfa_pport_beacon(struct bfa_s *bfa, bfa_boolean_t beacon, + bfa_boolean_t link_e2e_beacon) +{ + struct bfa_pport_s *pport = BFA_PORT_MOD(bfa); + + bfa_trc(bfa, beacon); + bfa_trc(bfa, link_e2e_beacon); + bfa_trc(bfa, pport->beacon); + bfa_trc(bfa, pport->link_e2e_beacon); + + pport->beacon = beacon; + pport->link_e2e_beacon = link_e2e_beacon; +} + +bfa_boolean_t +bfa_pport_is_linkup(struct bfa_s *bfa) +{ + return bfa_sm_cmp_state(BFA_PORT_MOD(bfa), bfa_pport_sm_linkup); +} + + diff --git a/drivers/scsi/bfa/bfa_fcs.c b/drivers/scsi/bfa/bfa_fcs.c new file mode 100644 index 000000000000..7cb39a306ea9 --- /dev/null +++ b/drivers/scsi/bfa/bfa_fcs.c @@ -0,0 +1,182 @@ +/* + * Copyright (c) 2005-2009 Brocade Communications Systems, Inc. + * All rights reserved + * www.brocade.com + * + * Linux driver for Brocade Fibre Channel Host Bus Adapter. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (GPL) Version 2 as + * published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +/** + * bfa_fcs.c BFA FCS main + */ + +#include <fcs/bfa_fcs.h> +#include "fcs_port.h" +#include "fcs_uf.h" +#include "fcs_vport.h" +#include "fcs_rport.h" +#include "fcs_fabric.h" +#include "fcs_fcpim.h" +#include "fcs_fcptm.h" +#include "fcbuild.h" +#include "fcs.h" +#include "bfad_drv.h" +#include <fcb/bfa_fcb.h> + +/** + * FCS sub-modules + */ +struct bfa_fcs_mod_s { + void (*modinit) (struct bfa_fcs_s *fcs); + void (*modexit) (struct bfa_fcs_s *fcs); +}; + +#define BFA_FCS_MODULE(_mod) { _mod ## _modinit, _mod ## _modexit } + +static struct bfa_fcs_mod_s fcs_modules[] = { + BFA_FCS_MODULE(bfa_fcs_pport), + BFA_FCS_MODULE(bfa_fcs_uf), + BFA_FCS_MODULE(bfa_fcs_fabric), + BFA_FCS_MODULE(bfa_fcs_vport), + BFA_FCS_MODULE(bfa_fcs_rport), + BFA_FCS_MODULE(bfa_fcs_fcpim), +}; + +/** + * fcs_api BFA FCS API + */ + +static void +bfa_fcs_exit_comp(void *fcs_cbarg) +{ + struct bfa_fcs_s *fcs = fcs_cbarg; + struct bfad_s *bfad = fcs->bfad; + + complete(&bfad->comp); +} + + + +/** + * fcs_api BFA FCS API + */ + +/** + * FCS instance initialization. + * + * param[in] fcs FCS instance + * param[in] bfa BFA instance + * param[in] bfad BFA driver instance + * + * return None + */ +void +bfa_fcs_init(struct bfa_fcs_s *fcs, struct bfa_s *bfa, struct bfad_s *bfad, + bfa_boolean_t min_cfg) +{ + int i; + struct bfa_fcs_mod_s *mod; + + fcs->bfa = bfa; + fcs->bfad = bfad; + fcs->min_cfg = min_cfg; + + bfa_attach_fcs(bfa); + fcbuild_init(); + + for (i = 0; i < sizeof(fcs_modules) / sizeof(fcs_modules[0]); i++) { + mod = &fcs_modules[i]; + mod->modinit(fcs); + } +} + +/** + * Start FCS operations. + */ +void +bfa_fcs_start(struct bfa_fcs_s *fcs) +{ + bfa_fcs_fabric_modstart(fcs); +} + +/** + * FCS driver details initialization. + * + * param[in] fcs FCS instance + * param[in] driver_info Driver Details + * + * return None + */ +void +bfa_fcs_driver_info_init(struct bfa_fcs_s *fcs, + struct bfa_fcs_driver_info_s *driver_info) +{ + + fcs->driver_info = *driver_info; + + bfa_fcs_fabric_psymb_init(&fcs->fabric); +} + +/** + * FCS instance cleanup and exit. + * + * param[in] fcs FCS instance + * return None + */ +void +bfa_fcs_exit(struct bfa_fcs_s *fcs) +{ + struct bfa_fcs_mod_s *mod; + int nmods, i; + + bfa_wc_init(&fcs->wc, bfa_fcs_exit_comp, fcs); + + nmods = sizeof(fcs_modules) / sizeof(fcs_modules[0]); + + for (i = 0; i < nmods; i++) { + bfa_wc_up(&fcs->wc); + + mod = &fcs_modules[i]; + mod->modexit(fcs); + } + + bfa_wc_wait(&fcs->wc); +} + + +void +bfa_fcs_trc_init(struct bfa_fcs_s *fcs, struct bfa_trc_mod_s *trcmod) +{ + fcs->trcmod = trcmod; +} + + +void +bfa_fcs_log_init(struct bfa_fcs_s *fcs, struct bfa_log_mod_s *logmod) +{ + fcs->logm = logmod; +} + + +void +bfa_fcs_aen_init(struct bfa_fcs_s *fcs, struct bfa_aen_s *aen) +{ + fcs->aen = aen; +} + +void +bfa_fcs_modexit_comp(struct bfa_fcs_s *fcs) +{ + bfa_wc_down(&fcs->wc); +} + + diff --git a/drivers/scsi/bfa/bfa_fcs_lport.c b/drivers/scsi/bfa/bfa_fcs_lport.c new file mode 100644 index 000000000000..8975ed041dc0 --- /dev/null +++ b/drivers/scsi/bfa/bfa_fcs_lport.c @@ -0,0 +1,940 @@ +/* + * Copyright (c) 2005-2009 Brocade Communications Systems, Inc. + * All rights reserved + * www.brocade.com + * + * Linux driver for Brocade Fibre Channel Host Bus Adapter. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (GPL) Version 2 as + * published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +/** + * bfa_fcs_port.c BFA FCS port + */ + +#include <fcs/bfa_fcs.h> +#include <fcs/bfa_fcs_lport.h> +#include <fcs/bfa_fcs_rport.h> +#include <fcb/bfa_fcb_port.h> +#include <bfa_svc.h> +#include <log/bfa_log_fcs.h> +#include "fcs.h" +#include "fcs_lport.h" +#include "fcs_vport.h" +#include "fcs_rport.h" +#include "fcs_fcxp.h" +#include "fcs_trcmod.h" +#include "lport_priv.h" +#include <aen/bfa_aen_lport.h> + +BFA_TRC_FILE(FCS, PORT); + +/** + * Forward declarations + */ + +static void bfa_fcs_port_aen_post(struct bfa_fcs_port_s *port, + enum bfa_lport_aen_event event); +static void bfa_fcs_port_send_ls_rjt(struct bfa_fcs_port_s *port, + struct fchs_s *rx_fchs, u8 reason_code, + u8 reason_code_expl); +static void bfa_fcs_port_plogi(struct bfa_fcs_port_s *port, + struct fchs_s *rx_fchs, + struct fc_logi_s *plogi); +static void bfa_fcs_port_online_actions(struct bfa_fcs_port_s *port); +static void bfa_fcs_port_offline_actions(struct bfa_fcs_port_s *port); +static void bfa_fcs_port_unknown_init(struct bfa_fcs_port_s *port); +static void bfa_fcs_port_unknown_online(struct bfa_fcs_port_s *port); +static void bfa_fcs_port_unknown_offline(struct bfa_fcs_port_s *port); +static void bfa_fcs_port_deleted(struct bfa_fcs_port_s *port); +static void bfa_fcs_port_echo(struct bfa_fcs_port_s *port, + struct fchs_s *rx_fchs, + struct fc_echo_s *echo, u16 len); +static void bfa_fcs_port_rnid(struct bfa_fcs_port_s *port, + struct fchs_s *rx_fchs, + struct fc_rnid_cmd_s *rnid, u16 len); +static void bfa_fs_port_get_gen_topo_data(struct bfa_fcs_port_s *port, + struct fc_rnid_general_topology_data_s *gen_topo_data); + +static struct { + void (*init) (struct bfa_fcs_port_s *port); + void (*online) (struct bfa_fcs_port_s *port); + void (*offline) (struct bfa_fcs_port_s *port); +} __port_action[] = { + { + bfa_fcs_port_unknown_init, bfa_fcs_port_unknown_online, + bfa_fcs_port_unknown_offline}, { + bfa_fcs_port_fab_init, bfa_fcs_port_fab_online, + bfa_fcs_port_fab_offline}, { + bfa_fcs_port_loop_init, bfa_fcs_port_loop_online, + bfa_fcs_port_loop_offline}, { +bfa_fcs_port_n2n_init, bfa_fcs_port_n2n_online, + bfa_fcs_port_n2n_offline},}; + +/** + * fcs_port_sm FCS logical port state machine + */ + +enum bfa_fcs_port_event { + BFA_FCS_PORT_SM_CREATE = 1, + BFA_FCS_PORT_SM_ONLINE = 2, + BFA_FCS_PORT_SM_OFFLINE = 3, + BFA_FCS_PORT_SM_DELETE = 4, + BFA_FCS_PORT_SM_DELRPORT = 5, +}; + +static void bfa_fcs_port_sm_uninit(struct bfa_fcs_port_s *port, + enum bfa_fcs_port_event event); +static void bfa_fcs_port_sm_init(struct bfa_fcs_port_s *port, + enum bfa_fcs_port_event event); +static void bfa_fcs_port_sm_online(struct bfa_fcs_port_s *port, + enum bfa_fcs_port_event event); +static void bfa_fcs_port_sm_offline(struct bfa_fcs_port_s *port, + enum bfa_fcs_port_event event); +static void bfa_fcs_port_sm_deleting(struct bfa_fcs_port_s *port, + enum bfa_fcs_port_event event); + +static void +bfa_fcs_port_sm_uninit(struct bfa_fcs_port_s *port, + enum bfa_fcs_port_event event) +{ + bfa_trc(port->fcs, port->port_cfg.pwwn); + bfa_trc(port->fcs, event); + + switch (event) { + case BFA_FCS_PORT_SM_CREATE: + bfa_sm_set_state(port, bfa_fcs_port_sm_init); + break; + + default: + bfa_assert(0); + } +} + +static void +bfa_fcs_port_sm_init(struct bfa_fcs_port_s *port, enum bfa_fcs_port_event event) +{ + bfa_trc(port->fcs, port->port_cfg.pwwn); + bfa_trc(port->fcs, event); + + switch (event) { + case BFA_FCS_PORT_SM_ONLINE: + bfa_sm_set_state(port, bfa_fcs_port_sm_online); + bfa_fcs_port_online_actions(port); + break; + + case BFA_FCS_PORT_SM_DELETE: + bfa_sm_set_state(port, bfa_fcs_port_sm_uninit); + bfa_fcs_port_deleted(port); + break; + + default: + bfa_assert(0); + } +} + +static void +bfa_fcs_port_sm_online(struct bfa_fcs_port_s *port, + enum bfa_fcs_port_event event) +{ + struct bfa_fcs_rport_s *rport; + struct list_head *qe, *qen; + + bfa_trc(port->fcs, port->port_cfg.pwwn); + bfa_trc(port->fcs, event); + + switch (event) { + case BFA_FCS_PORT_SM_OFFLINE: + bfa_sm_set_state(port, bfa_fcs_port_sm_offline); + bfa_fcs_port_offline_actions(port); + break; + + case BFA_FCS_PORT_SM_DELETE: + + __port_action[port->fabric->fab_type].offline(port); + + if (port->num_rports == 0) { + bfa_sm_set_state(port, bfa_fcs_port_sm_uninit); + bfa_fcs_port_deleted(port); + } else { + bfa_sm_set_state(port, bfa_fcs_port_sm_deleting); + list_for_each_safe(qe, qen, &port->rport_q) { + rport = (struct bfa_fcs_rport_s *)qe; + bfa_fcs_rport_delete(rport); + } + } + break; + + case BFA_FCS_PORT_SM_DELRPORT: + break; + + default: + bfa_assert(0); + } +} + +static void +bfa_fcs_port_sm_offline(struct bfa_fcs_port_s *port, + enum bfa_fcs_port_event event) +{ + struct bfa_fcs_rport_s *rport; + struct list_head *qe, *qen; + + bfa_trc(port->fcs, port->port_cfg.pwwn); + bfa_trc(port->fcs, event); + + switch (event) { + case BFA_FCS_PORT_SM_ONLINE: + bfa_sm_set_state(port, bfa_fcs_port_sm_online); + bfa_fcs_port_online_actions(port); + break; + + case BFA_FCS_PORT_SM_DELETE: + if (port->num_rports == 0) { + bfa_sm_set_state(port, bfa_fcs_port_sm_uninit); + bfa_fcs_port_deleted(port); + } else { + bfa_sm_set_state(port, bfa_fcs_port_sm_deleting); + list_for_each_safe(qe, qen, &port->rport_q) { + rport = (struct bfa_fcs_rport_s *)qe; + bfa_fcs_rport_delete(rport); + } + } + break; + + case BFA_FCS_PORT_SM_DELRPORT: + case BFA_FCS_PORT_SM_OFFLINE: + break; + + default: + bfa_assert(0); + } +} + +static void +bfa_fcs_port_sm_deleting(struct bfa_fcs_port_s *port, + enum bfa_fcs_port_event event) +{ + bfa_trc(port->fcs, port->port_cfg.pwwn); + bfa_trc(port->fcs, event); + + switch (event) { + case BFA_FCS_PORT_SM_DELRPORT: + if (port->num_rports == 0) { + bfa_sm_set_state(port, bfa_fcs_port_sm_uninit); + bfa_fcs_port_deleted(port); + } + break; + + default: + bfa_assert(0); + } +} + + + +/** + * fcs_port_pvt + */ + +/** + * Send AEN notification + */ +static void +bfa_fcs_port_aen_post(struct bfa_fcs_port_s *port, + enum bfa_lport_aen_event event) +{ + union bfa_aen_data_u aen_data; + struct bfa_log_mod_s *logmod = port->fcs->logm; + enum bfa_port_role role = port->port_cfg.roles; + wwn_t lpwwn = bfa_fcs_port_get_pwwn(port); + char lpwwn_ptr[BFA_STRING_32]; + char *role_str[BFA_PORT_ROLE_FCP_MAX / 2 + 1] = + { "Initiator", "Target", "IPFC" }; + + wwn2str(lpwwn_ptr, lpwwn); + + bfa_assert(role <= BFA_PORT_ROLE_FCP_MAX); + + switch (event) { + case BFA_LPORT_AEN_ONLINE: + bfa_log(logmod, BFA_AEN_LPORT_ONLINE, lpwwn_ptr, + role_str[role / 2]); + break; + case BFA_LPORT_AEN_OFFLINE: + bfa_log(logmod, BFA_AEN_LPORT_OFFLINE, lpwwn_ptr, + role_str[role / 2]); + break; + case BFA_LPORT_AEN_NEW: + bfa_log(logmod, BFA_AEN_LPORT_NEW, lpwwn_ptr, + role_str[role / 2]); + break; + case BFA_LPORT_AEN_DELETE: + bfa_log(logmod, BFA_AEN_LPORT_DELETE, lpwwn_ptr, + role_str[role / 2]); + break; + case BFA_LPORT_AEN_DISCONNECT: + bfa_log(logmod, BFA_AEN_LPORT_DISCONNECT, lpwwn_ptr, + role_str[role / 2]); + break; + default: + break; + } + + aen_data.lport.vf_id = port->fabric->vf_id; + aen_data.lport.roles = role; + aen_data.lport.ppwwn = + bfa_fcs_port_get_pwwn(bfa_fcs_get_base_port(port->fcs)); + aen_data.lport.lpwwn = lpwwn; +} + +/* + * Send a LS reject + */ +static void +bfa_fcs_port_send_ls_rjt(struct bfa_fcs_port_s *port, struct fchs_s *rx_fchs, + u8 reason_code, u8 reason_code_expl) +{ + struct fchs_s fchs; + struct bfa_fcxp_s *fcxp; + struct bfa_rport_s *bfa_rport = NULL; + int len; + + bfa_trc(port->fcs, rx_fchs->s_id); + + fcxp = bfa_fcs_fcxp_alloc(port->fcs); + if (!fcxp) + return; + + len = fc_ls_rjt_build(&fchs, bfa_fcxp_get_reqbuf(fcxp), rx_fchs->s_id, + bfa_fcs_port_get_fcid(port), rx_fchs->ox_id, + reason_code, reason_code_expl); + + bfa_fcxp_send(fcxp, bfa_rport, port->fabric->vf_id, port->lp_tag, + BFA_FALSE, FC_CLASS_3, len, &fchs, NULL, NULL, + FC_MAX_PDUSZ, 0); +} + +/** + * Process incoming plogi from a remote port. + */ +static void +bfa_fcs_port_plogi(struct bfa_fcs_port_s *port, struct fchs_s *rx_fchs, + struct fc_logi_s *plogi) +{ + struct bfa_fcs_rport_s *rport; + + bfa_trc(port->fcs, rx_fchs->d_id); + bfa_trc(port->fcs, rx_fchs->s_id); + + /* + * If min cfg mode is enabled, drop any incoming PLOGIs + */ + if (__fcs_min_cfg(port->fcs)) { + bfa_trc(port->fcs, rx_fchs->s_id); + return; + } + + if (fc_plogi_parse(rx_fchs) != FC_PARSE_OK) { + bfa_trc(port->fcs, rx_fchs->s_id); + /* + * send a LS reject + */ + bfa_fcs_port_send_ls_rjt(port, rx_fchs, + FC_LS_RJT_RSN_PROTOCOL_ERROR, + FC_LS_RJT_EXP_SPARMS_ERR_OPTIONS); + return; + } + + /** +* Direct Attach P2P mode : verify address assigned by the r-port. + */ + if ((!bfa_fcs_fabric_is_switched(port->fabric)) + && + (memcmp + ((void *)&bfa_fcs_port_get_pwwn(port), (void *)&plogi->port_name, + sizeof(wwn_t)) < 0)) { + if (BFA_FCS_PID_IS_WKA(rx_fchs->d_id)) { + /* + * Address assigned to us cannot be a WKA + */ + bfa_fcs_port_send_ls_rjt(port, rx_fchs, + FC_LS_RJT_RSN_PROTOCOL_ERROR, + FC_LS_RJT_EXP_INVALID_NPORT_ID); + return; + } + port->pid = rx_fchs->d_id; + } + + /** + * First, check if we know the device by pwwn. + */ + rport = bfa_fcs_port_get_rport_by_pwwn(port, plogi->port_name); + if (rport) { + /** + * Direct Attach P2P mode: handle address assigned by the rport. + */ + if ((!bfa_fcs_fabric_is_switched(port->fabric)) + && + (memcmp + ((void *)&bfa_fcs_port_get_pwwn(port), + (void *)&plogi->port_name, sizeof(wwn_t)) < 0)) { + port->pid = rx_fchs->d_id; + rport->pid = rx_fchs->s_id; + } + bfa_fcs_rport_plogi(rport, rx_fchs, plogi); + return; + } + + /** + * Next, lookup rport by PID. + */ + rport = bfa_fcs_port_get_rport_by_pid(port, rx_fchs->s_id); + if (!rport) { + /** + * Inbound PLOGI from a new device. + */ + bfa_fcs_rport_plogi_create(port, rx_fchs, plogi); + return; + } + + /** + * Rport is known only by PID. + */ + if (rport->pwwn) { + /** + * This is a different device with the same pid. Old device + * disappeared. Send implicit LOGO to old device. + */ + bfa_assert(rport->pwwn != plogi->port_name); + bfa_fcs_rport_logo_imp(rport); + + /** + * Inbound PLOGI from a new device (with old PID). + */ + bfa_fcs_rport_plogi_create(port, rx_fchs, plogi); + return; + } + + /** + * PLOGI crossing each other. + */ + bfa_assert(rport->pwwn == WWN_NULL); + bfa_fcs_rport_plogi(rport, rx_fchs, plogi); +} + +/* + * Process incoming ECHO. + * Since it does not require a login, it is processed here. + */ +static void +bfa_fcs_port_echo(struct bfa_fcs_port_s *port, struct fchs_s *rx_fchs, + struct fc_echo_s *echo, u16 rx_len) +{ + struct fchs_s fchs; + struct bfa_fcxp_s *fcxp; + struct bfa_rport_s *bfa_rport = NULL; + int len, pyld_len; + + bfa_trc(port->fcs, rx_fchs->s_id); + bfa_trc(port->fcs, rx_fchs->d_id); + bfa_trc(port->fcs, rx_len); + + fcxp = bfa_fcs_fcxp_alloc(port->fcs); + if (!fcxp) + return; + + len = fc_ls_acc_build(&fchs, bfa_fcxp_get_reqbuf(fcxp), rx_fchs->s_id, + bfa_fcs_port_get_fcid(port), rx_fchs->ox_id); + + /* + * Copy the payload (if any) from the echo frame + */ + pyld_len = rx_len - sizeof(struct fchs_s); + bfa_trc(port->fcs, pyld_len); + + if (pyld_len > len) + memcpy(((u8 *) bfa_fcxp_get_reqbuf(fcxp)) + + sizeof(struct fc_echo_s), (echo + 1), + (pyld_len - sizeof(struct fc_echo_s))); + + bfa_fcxp_send(fcxp, bfa_rport, port->fabric->vf_id, port->lp_tag, + BFA_FALSE, FC_CLASS_3, pyld_len, &fchs, NULL, NULL, + FC_MAX_PDUSZ, 0); +} + +/* + * Process incoming RNID. + * Since it does not require a login, it is processed here. + */ +static void +bfa_fcs_port_rnid(struct bfa_fcs_port_s *port, struct fchs_s *rx_fchs, + struct fc_rnid_cmd_s *rnid, u16 rx_len) +{ + struct fc_rnid_common_id_data_s common_id_data; + struct fc_rnid_general_topology_data_s gen_topo_data; + struct fchs_s fchs; + struct bfa_fcxp_s *fcxp; + struct bfa_rport_s *bfa_rport = NULL; + u16 len; + u32 data_format; + + bfa_trc(port->fcs, rx_fchs->s_id); + bfa_trc(port->fcs, rx_fchs->d_id); + bfa_trc(port->fcs, rx_len); + + fcxp = bfa_fcs_fcxp_alloc(port->fcs); + if (!fcxp) + return; + + /* + * Check Node Indentification Data Format + * We only support General Topology Discovery Format. + * For any other requested Data Formats, we return Common Node Id Data + * only, as per FC-LS. + */ + bfa_trc(port->fcs, rnid->node_id_data_format); + if (rnid->node_id_data_format == RNID_NODEID_DATA_FORMAT_DISCOVERY) { + data_format = RNID_NODEID_DATA_FORMAT_DISCOVERY; + /* + * Get General topology data for this port + */ + bfa_fs_port_get_gen_topo_data(port, &gen_topo_data); + } else { + data_format = RNID_NODEID_DATA_FORMAT_COMMON; + } + + /* + * Copy the Node Id Info + */ + common_id_data.port_name = bfa_fcs_port_get_pwwn(port); + common_id_data.node_name = bfa_fcs_port_get_nwwn(port); + + len = fc_rnid_acc_build(&fchs, bfa_fcxp_get_reqbuf(fcxp), rx_fchs->s_id, + bfa_fcs_port_get_fcid(port), rx_fchs->ox_id, + data_format, &common_id_data, &gen_topo_data); + + bfa_fcxp_send(fcxp, bfa_rport, port->fabric->vf_id, port->lp_tag, + BFA_FALSE, FC_CLASS_3, len, &fchs, NULL, NULL, + FC_MAX_PDUSZ, 0); + + return; +} + +/* + * Fill out General Topolpgy Discovery Data for RNID ELS. + */ +static void +bfa_fs_port_get_gen_topo_data(struct bfa_fcs_port_s *port, + struct fc_rnid_general_topology_data_s *gen_topo_data) +{ + + bfa_os_memset(gen_topo_data, 0, + sizeof(struct fc_rnid_general_topology_data_s)); + + gen_topo_data->asso_type = bfa_os_htonl(RNID_ASSOCIATED_TYPE_HOST); + gen_topo_data->phy_port_num = 0; /* @todo */ + gen_topo_data->num_attached_nodes = bfa_os_htonl(1); +} + +static void +bfa_fcs_port_online_actions(struct bfa_fcs_port_s *port) +{ + bfa_trc(port->fcs, port->fabric->oper_type); + + __port_action[port->fabric->fab_type].init(port); + __port_action[port->fabric->fab_type].online(port); + + bfa_fcs_port_aen_post(port, BFA_LPORT_AEN_ONLINE); + bfa_fcb_port_online(port->fcs->bfad, port->port_cfg.roles, + port->fabric->vf_drv, (port->vport == NULL) ? + NULL : port->vport->vport_drv); +} + +static void +bfa_fcs_port_offline_actions(struct bfa_fcs_port_s *port) +{ + struct list_head *qe, *qen; + struct bfa_fcs_rport_s *rport; + + bfa_trc(port->fcs, port->fabric->oper_type); + + __port_action[port->fabric->fab_type].offline(port); + + if (bfa_fcs_fabric_is_online(port->fabric) == BFA_TRUE) { + bfa_fcs_port_aen_post(port, BFA_LPORT_AEN_DISCONNECT); + } else { + bfa_fcs_port_aen_post(port, BFA_LPORT_AEN_OFFLINE); + } + bfa_fcb_port_offline(port->fcs->bfad, port->port_cfg.roles, + port->fabric->vf_drv, + (port->vport == NULL) ? NULL : port->vport->vport_drv); + + list_for_each_safe(qe, qen, &port->rport_q) { + rport = (struct bfa_fcs_rport_s *)qe; + bfa_fcs_rport_offline(rport); + } +} + +static void +bfa_fcs_port_unknown_init(struct bfa_fcs_port_s *port) +{ + bfa_assert(0); +} + +static void +bfa_fcs_port_unknown_online(struct bfa_fcs_port_s *port) +{ + bfa_assert(0); +} + +static void +bfa_fcs_port_unknown_offline(struct bfa_fcs_port_s *port) +{ + bfa_assert(0); +} + +static void +bfa_fcs_port_deleted(struct bfa_fcs_port_s *port) +{ + bfa_fcs_port_aen_post(port, BFA_LPORT_AEN_DELETE); + + /* + * Base port will be deleted by the OS driver + */ + if (port->vport) { + bfa_fcb_port_delete(port->fcs->bfad, port->port_cfg.roles, + port->fabric->vf_drv, + port->vport ? port->vport->vport_drv : NULL); + bfa_fcs_vport_delete_comp(port->vport); + } else { + bfa_fcs_fabric_port_delete_comp(port->fabric); + } +} + + + +/** + * fcs_lport_api BFA FCS port API + */ +/** + * Module initialization + */ +void +bfa_fcs_port_modinit(struct bfa_fcs_s *fcs) +{ + +} + +/** + * Module cleanup + */ +void +bfa_fcs_port_modexit(struct bfa_fcs_s *fcs) +{ + bfa_fcs_modexit_comp(fcs); +} + +/** + * Unsolicited frame receive handling. + */ +void +bfa_fcs_port_uf_recv(struct bfa_fcs_port_s *lport, struct fchs_s *fchs, + u16 len) +{ + u32 pid = fchs->s_id; + struct bfa_fcs_rport_s *rport = NULL; + struct fc_els_cmd_s *els_cmd = (struct fc_els_cmd_s *) (fchs + 1); + + bfa_stats(lport, uf_recvs); + + if (!bfa_fcs_port_is_online(lport)) { + bfa_stats(lport, uf_recv_drops); + return; + } + + /** + * First, handle ELSs that donot require a login. + */ + /* + * Handle PLOGI first + */ + if ((fchs->type == FC_TYPE_ELS) && + (els_cmd->els_code == FC_ELS_PLOGI)) { + bfa_fcs_port_plogi(lport, fchs, (struct fc_logi_s *) els_cmd); + return; + } + + /* + * Handle ECHO separately. + */ + if ((fchs->type == FC_TYPE_ELS) && (els_cmd->els_code == FC_ELS_ECHO)) { + bfa_fcs_port_echo(lport, fchs, + (struct fc_echo_s *) els_cmd, len); + return; + } + + /* + * Handle RNID separately. + */ + if ((fchs->type == FC_TYPE_ELS) && (els_cmd->els_code == FC_ELS_RNID)) { + bfa_fcs_port_rnid(lport, fchs, + (struct fc_rnid_cmd_s *) els_cmd, len); + return; + } + + /** + * look for a matching remote port ID + */ + rport = bfa_fcs_port_get_rport_by_pid(lport, pid); + if (rport) { + bfa_trc(rport->fcs, fchs->s_id); + bfa_trc(rport->fcs, fchs->d_id); + bfa_trc(rport->fcs, fchs->type); + + bfa_fcs_rport_uf_recv(rport, fchs, len); + return; + } + + /** + * Only handles ELS frames for now. + */ + if (fchs->type != FC_TYPE_ELS) { + bfa_trc(lport->fcs, fchs->type); + bfa_assert(0); + return; + } + + bfa_trc(lport->fcs, els_cmd->els_code); + if (els_cmd->els_code == FC_ELS_RSCN) { + bfa_fcs_port_scn_process_rscn(lport, fchs, len); + return; + } + + if (els_cmd->els_code == FC_ELS_LOGO) { + /** + * @todo Handle LOGO frames received. + */ + bfa_trc(lport->fcs, els_cmd->els_code); + return; + } + + if (els_cmd->els_code == FC_ELS_PRLI) { + /** + * @todo Handle PRLI frames received. + */ + bfa_trc(lport->fcs, els_cmd->els_code); + return; + } + + /** + * Unhandled ELS frames. Send a LS_RJT. + */ + bfa_fcs_port_send_ls_rjt(lport, fchs, FC_LS_RJT_RSN_CMD_NOT_SUPP, + FC_LS_RJT_EXP_NO_ADDL_INFO); + +} + +/** + * PID based Lookup for a R-Port in the Port R-Port Queue + */ +struct bfa_fcs_rport_s * +bfa_fcs_port_get_rport_by_pid(struct bfa_fcs_port_s *port, u32 pid) +{ + struct bfa_fcs_rport_s *rport; + struct list_head *qe; + + list_for_each(qe, &port->rport_q) { + rport = (struct bfa_fcs_rport_s *)qe; + if (rport->pid == pid) + return rport; + } + + bfa_trc(port->fcs, pid); + return NULL; +} + +/** + * PWWN based Lookup for a R-Port in the Port R-Port Queue + */ +struct bfa_fcs_rport_s * +bfa_fcs_port_get_rport_by_pwwn(struct bfa_fcs_port_s *port, wwn_t pwwn) +{ + struct bfa_fcs_rport_s *rport; + struct list_head *qe; + + list_for_each(qe, &port->rport_q) { + rport = (struct bfa_fcs_rport_s *)qe; + if (wwn_is_equal(rport->pwwn, pwwn)) + return rport; + } + + bfa_trc(port->fcs, pwwn); + return (NULL); +} + +/** + * NWWN based Lookup for a R-Port in the Port R-Port Queue + */ +struct bfa_fcs_rport_s * +bfa_fcs_port_get_rport_by_nwwn(struct bfa_fcs_port_s *port, wwn_t nwwn) +{ + struct bfa_fcs_rport_s *rport; + struct list_head *qe; + + list_for_each(qe, &port->rport_q) { + rport = (struct bfa_fcs_rport_s *)qe; + if (wwn_is_equal(rport->nwwn, nwwn)) + return rport; + } + + bfa_trc(port->fcs, nwwn); + return (NULL); +} + +/** + * Called by rport module when new rports are discovered. + */ +void +bfa_fcs_port_add_rport(struct bfa_fcs_port_s *port, + struct bfa_fcs_rport_s *rport) +{ + list_add_tail(&rport->qe, &port->rport_q); + port->num_rports++; +} + +/** + * Called by rport module to when rports are deleted. + */ +void +bfa_fcs_port_del_rport(struct bfa_fcs_port_s *port, + struct bfa_fcs_rport_s *rport) +{ + bfa_assert(bfa_q_is_on_q(&port->rport_q, rport)); + list_del(&rport->qe); + port->num_rports--; + + bfa_sm_send_event(port, BFA_FCS_PORT_SM_DELRPORT); +} + +/** + * Called by fabric for base port when fabric login is complete. + * Called by vport for virtual ports when FDISC is complete. + */ +void +bfa_fcs_port_online(struct bfa_fcs_port_s *port) +{ + bfa_sm_send_event(port, BFA_FCS_PORT_SM_ONLINE); +} + +/** + * Called by fabric for base port when fabric goes offline. + * Called by vport for virtual ports when virtual port becomes offline. + */ +void +bfa_fcs_port_offline(struct bfa_fcs_port_s *port) +{ + bfa_sm_send_event(port, BFA_FCS_PORT_SM_OFFLINE); +} + +/** + * Called by fabric to delete base lport and associated resources. + * + * Called by vport to delete lport and associated resources. Should call + * bfa_fcs_vport_delete_comp() for vports on completion. + */ +void +bfa_fcs_port_delete(struct bfa_fcs_port_s *port) +{ + bfa_sm_send_event(port, BFA_FCS_PORT_SM_DELETE); +} + +/** + * Called by fabric in private loop topology to process LIP event. + */ +void +bfa_fcs_port_lip(struct bfa_fcs_port_s *port) +{ +} + +/** + * Return TRUE if port is online, else return FALSE + */ +bfa_boolean_t +bfa_fcs_port_is_online(struct bfa_fcs_port_s *port) +{ + return (bfa_sm_cmp_state(port, bfa_fcs_port_sm_online)); +} + +/** + * Logical port initialization of base or virtual port. + * Called by fabric for base port or by vport for virtual ports. + */ +void +bfa_fcs_lport_init(struct bfa_fcs_port_s *lport, struct bfa_fcs_s *fcs, + u16 vf_id, struct bfa_port_cfg_s *port_cfg, + struct bfa_fcs_vport_s *vport) +{ + lport->fcs = fcs; + lport->fabric = bfa_fcs_vf_lookup(fcs, vf_id); + bfa_os_assign(lport->port_cfg, *port_cfg); + lport->vport = vport; + lport->lp_tag = (vport) ? bfa_lps_get_tag(vport->lps) : + bfa_lps_get_tag(lport->fabric->lps); + + INIT_LIST_HEAD(&lport->rport_q); + lport->num_rports = 0; + + lport->bfad_port = + bfa_fcb_port_new(fcs->bfad, lport, lport->port_cfg.roles, + lport->fabric->vf_drv, + vport ? vport->vport_drv : NULL); + bfa_fcs_port_aen_post(lport, BFA_LPORT_AEN_NEW); + + bfa_sm_set_state(lport, bfa_fcs_port_sm_uninit); + bfa_sm_send_event(lport, BFA_FCS_PORT_SM_CREATE); +} + + + +/** + * fcs_lport_api + */ + +void +bfa_fcs_port_get_attr(struct bfa_fcs_port_s *port, + struct bfa_port_attr_s *port_attr) +{ + if (bfa_sm_cmp_state(port, bfa_fcs_port_sm_online)) + port_attr->pid = port->pid; + else + port_attr->pid = 0; + + port_attr->port_cfg = port->port_cfg; + + if (port->fabric) { + port_attr->port_type = bfa_fcs_fabric_port_type(port->fabric); + port_attr->loopback = bfa_fcs_fabric_is_loopback(port->fabric); + port_attr->fabric_name = bfa_fcs_port_get_fabric_name(port); + memcpy(port_attr->fabric_ip_addr, + bfa_fcs_port_get_fabric_ipaddr(port), + BFA_FCS_FABRIC_IPADDR_SZ); + + if (port->vport != NULL) + port_attr->port_type = BFA_PPORT_TYPE_VPORT; + + } else { + port_attr->port_type = BFA_PPORT_TYPE_UNKNOWN; + port_attr->state = BFA_PORT_UNINIT; + } + +} + + diff --git a/drivers/scsi/bfa/bfa_fcs_port.c b/drivers/scsi/bfa/bfa_fcs_port.c new file mode 100644 index 000000000000..9c4b24e62de1 --- /dev/null +++ b/drivers/scsi/bfa/bfa_fcs_port.c @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2005-2009 Brocade Communications Systems, Inc. + * All rights reserved + * www.brocade.com + * + * Linux driver for Brocade Fibre Channel Host Bus Adapter. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (GPL) Version 2 as + * published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +/** + * bfa_fcs_pport.c BFA FCS PPORT ( physical port) + */ + +#include <fcs/bfa_fcs.h> +#include <bfa_svc.h> +#include <fcs/bfa_fcs_fabric.h> +#include "fcs_trcmod.h" +#include "fcs.h" +#include "fcs_fabric.h" +#include "fcs_port.h" + +BFA_TRC_FILE(FCS, PPORT); + +static void +bfa_fcs_pport_event_handler(void *cbarg, bfa_pport_event_t event) +{ + struct bfa_fcs_s *fcs = cbarg; + + bfa_trc(fcs, event); + + switch (event) { + case BFA_PPORT_LINKUP: + bfa_fcs_fabric_link_up(&fcs->fabric); + break; + + case BFA_PPORT_LINKDOWN: + bfa_fcs_fabric_link_down(&fcs->fabric); + break; + + case BFA_PPORT_TRUNK_LINKDOWN: + bfa_assert(0); + break; + + default: + bfa_assert(0); + } +} + +void +bfa_fcs_pport_modinit(struct bfa_fcs_s *fcs) +{ + bfa_pport_event_register(fcs->bfa, bfa_fcs_pport_event_handler, + fcs); +} + +void +bfa_fcs_pport_modexit(struct bfa_fcs_s *fcs) +{ + bfa_fcs_modexit_comp(fcs); +} diff --git a/drivers/scsi/bfa/bfa_fcs_uf.c b/drivers/scsi/bfa/bfa_fcs_uf.c new file mode 100644 index 000000000000..ad01db6444b2 --- /dev/null +++ b/drivers/scsi/bfa/bfa_fcs_uf.c @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2005-2009 Brocade Communications Systems, Inc. + * All rights reserved + * www.brocade.com + * + * Linux driver for Brocade Fibre Channel Host Bus Adapter. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (GPL) Version 2 as + * published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +/** + * bfa_fcs_uf.c BFA FCS UF ( Unsolicited Frames) + */ + +#include <fcs/bfa_fcs.h> +#include <bfa_svc.h> +#include <fcs/bfa_fcs_fabric.h> +#include "fcs.h" +#include "fcs_trcmod.h" +#include "fcs_fabric.h" +#include "fcs_uf.h" + +BFA_TRC_FILE(FCS, UF); + +/** + * BFA callback for unsolicited frame receive handler. + * + * @param[in] cbarg callback arg for receive handler + * @param[in] uf unsolicited frame descriptor + * + * @return None + */ +static void +bfa_fcs_uf_recv(void *cbarg, struct bfa_uf_s *uf) +{ + struct bfa_fcs_s *fcs = (struct bfa_fcs_s *) cbarg; + struct fchs_s *fchs = bfa_uf_get_frmbuf(uf); + u16 len = bfa_uf_get_frmlen(uf); + struct fc_vft_s *vft; + struct bfa_fcs_fabric_s *fabric; + + /** + * check for VFT header + */ + if (fchs->routing == FC_RTG_EXT_HDR && + fchs->cat_info == FC_CAT_VFT_HDR) { + bfa_stats(fcs, uf.tagged); + vft = bfa_uf_get_frmbuf(uf); + if (fcs->port_vfid == vft->vf_id) + fabric = &fcs->fabric; + else + fabric = bfa_fcs_vf_lookup(fcs, (u16) vft->vf_id); + + /** + * drop frame if vfid is unknown + */ + if (!fabric) { + bfa_assert(0); + bfa_stats(fcs, uf.vfid_unknown); + bfa_uf_free(uf); + return; + } + + /** + * skip vft header + */ + fchs = (struct fchs_s *) (vft + 1); + len -= sizeof(struct fc_vft_s); + + bfa_trc(fcs, vft->vf_id); + } else { + bfa_stats(fcs, uf.untagged); + fabric = &fcs->fabric; + } + + bfa_trc(fcs, ((u32 *) fchs)[0]); + bfa_trc(fcs, ((u32 *) fchs)[1]); + bfa_trc(fcs, ((u32 *) fchs)[2]); + bfa_trc(fcs, ((u32 *) fchs)[3]); + bfa_trc(fcs, ((u32 *) fchs)[4]); + bfa_trc(fcs, ((u32 *) fchs)[5]); + bfa_trc(fcs, len); + + bfa_fcs_fabric_uf_recv(fabric, fchs, len); + bfa_uf_free(uf); +} + +void +bfa_fcs_uf_modinit(struct bfa_fcs_s *fcs) +{ + bfa_uf_recv_register(fcs->bfa, bfa_fcs_uf_recv, fcs); +} + +void +bfa_fcs_uf_modexit(struct bfa_fcs_s *fcs) +{ + bfa_fcs_modexit_comp(fcs); +} diff --git a/drivers/scsi/bfa/bfa_fcxp.c b/drivers/scsi/bfa/bfa_fcxp.c new file mode 100644 index 000000000000..4754a0e9006a --- /dev/null +++ b/drivers/scsi/bfa/bfa_fcxp.c @@ -0,0 +1,782 @@ +/* + * Copyright (c) 2005-2009 Brocade Communications Systems, Inc. + * All rights reserved + * www.brocade.com + * + * Linux driver for Brocade Fibre Channel Host Bus Adapter. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (GPL) Version 2 as + * published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#include <bfa.h> +#include <bfi/bfi_uf.h> +#include <cs/bfa_debug.h> + +BFA_TRC_FILE(HAL, FCXP); +BFA_MODULE(fcxp); + +/** + * forward declarations + */ +static void __bfa_fcxp_send_cbfn(void *cbarg, bfa_boolean_t complete); +static void hal_fcxp_rx_plog(struct bfa_s *bfa, struct bfa_fcxp_s *fcxp, + struct bfi_fcxp_send_rsp_s *fcxp_rsp); +static void hal_fcxp_tx_plog(struct bfa_s *bfa, u32 reqlen, + struct bfa_fcxp_s *fcxp, struct fchs_s *fchs); +static void bfa_fcxp_qresume(void *cbarg); +static void bfa_fcxp_queue(struct bfa_fcxp_s *fcxp, + struct bfi_fcxp_send_req_s *send_req); + +/** + * fcxp_pvt BFA FCXP private functions + */ + +static void +claim_fcxp_req_rsp_mem(struct bfa_fcxp_mod_s *mod, struct bfa_meminfo_s *mi) +{ + u8 *dm_kva = NULL; + u64 dm_pa; + u32 buf_pool_sz; + + dm_kva = bfa_meminfo_dma_virt(mi); + dm_pa = bfa_meminfo_dma_phys(mi); + + buf_pool_sz = mod->req_pld_sz * mod->num_fcxps; + + /* + * Initialize the fcxp req payload list + */ + mod->req_pld_list_kva = dm_kva; + mod->req_pld_list_pa = dm_pa; + dm_kva += buf_pool_sz; + dm_pa += buf_pool_sz; + bfa_os_memset(mod->req_pld_list_kva, 0, buf_pool_sz); + + /* + * Initialize the fcxp rsp payload list + */ + buf_pool_sz = mod->rsp_pld_sz * mod->num_fcxps; + mod->rsp_pld_list_kva = dm_kva; + mod->rsp_pld_list_pa = dm_pa; + dm_kva += buf_pool_sz; + dm_pa += buf_pool_sz; + bfa_os_memset(mod->rsp_pld_list_kva, 0, buf_pool_sz); + + bfa_meminfo_dma_virt(mi) = dm_kva; + bfa_meminfo_dma_phys(mi) = dm_pa; +} + +static void +claim_fcxps_mem(struct bfa_fcxp_mod_s *mod, struct bfa_meminfo_s *mi) +{ + u16 i; + struct bfa_fcxp_s *fcxp; + + fcxp = (struct bfa_fcxp_s *) bfa_meminfo_kva(mi); + bfa_os_memset(fcxp, 0, sizeof(struct bfa_fcxp_s) * mod->num_fcxps); + + INIT_LIST_HEAD(&mod->fcxp_free_q); + INIT_LIST_HEAD(&mod->fcxp_active_q); + + mod->fcxp_list = fcxp; + + for (i = 0; i < mod->num_fcxps; i++) { + fcxp->fcxp_mod = mod; + fcxp->fcxp_tag = i; + + list_add_tail(&fcxp->qe, &mod->fcxp_free_q); + bfa_reqq_winit(&fcxp->reqq_wqe, bfa_fcxp_qresume, fcxp); + fcxp->reqq_waiting = BFA_FALSE; + + fcxp = fcxp + 1; + } + + bfa_meminfo_kva(mi) = (void *)fcxp; +} + +static void +bfa_fcxp_meminfo(struct bfa_iocfc_cfg_s *cfg, u32 *ndm_len, + u32 *dm_len) +{ + u16 num_fcxp_reqs = cfg->fwcfg.num_fcxp_reqs; + + if (num_fcxp_reqs == 0) + return; + + /* + * Account for req/rsp payload + */ + *dm_len += BFA_FCXP_MAX_IBUF_SZ * num_fcxp_reqs; + if (cfg->drvcfg.min_cfg) + *dm_len += BFA_FCXP_MAX_IBUF_SZ * num_fcxp_reqs; + else + *dm_len += BFA_FCXP_MAX_LBUF_SZ * num_fcxp_reqs; + + /* + * Account for fcxp structs + */ + *ndm_len += sizeof(struct bfa_fcxp_s) * num_fcxp_reqs; +} + +static void +bfa_fcxp_attach(struct bfa_s *bfa, void *bfad, struct bfa_iocfc_cfg_s *cfg, + struct bfa_meminfo_s *meminfo, struct bfa_pcidev_s *pcidev) +{ + struct bfa_fcxp_mod_s *mod = BFA_FCXP_MOD(bfa); + + bfa_os_memset(mod, 0, sizeof(struct bfa_fcxp_mod_s)); + mod->bfa = bfa; + mod->num_fcxps = cfg->fwcfg.num_fcxp_reqs; + + /** + * Initialize FCXP request and response payload sizes. + */ + mod->req_pld_sz = mod->rsp_pld_sz = BFA_FCXP_MAX_IBUF_SZ; + if (!cfg->drvcfg.min_cfg) + mod->rsp_pld_sz = BFA_FCXP_MAX_LBUF_SZ; + + INIT_LIST_HEAD(&mod->wait_q); + + claim_fcxp_req_rsp_mem(mod, meminfo); + claim_fcxps_mem(mod, meminfo); +} + +static void +bfa_fcxp_initdone(struct bfa_s *bfa) +{ +} + +static void +bfa_fcxp_detach(struct bfa_s *bfa) +{ +} + +static void +bfa_fcxp_start(struct bfa_s *bfa) +{ +} + +static void +bfa_fcxp_stop(struct bfa_s *bfa) +{ +} + +static void +bfa_fcxp_iocdisable(struct bfa_s *bfa) +{ + struct bfa_fcxp_mod_s *mod = BFA_FCXP_MOD(bfa); + struct bfa_fcxp_s *fcxp; + struct list_head *qe, *qen; + + list_for_each_safe(qe, qen, &mod->fcxp_active_q) { + fcxp = (struct bfa_fcxp_s *) qe; + if (fcxp->caller == NULL) { + fcxp->send_cbfn(fcxp->caller, fcxp, fcxp->send_cbarg, + BFA_STATUS_IOC_FAILURE, 0, 0, NULL); + bfa_fcxp_free(fcxp); + } else { + fcxp->rsp_status = BFA_STATUS_IOC_FAILURE; + bfa_cb_queue(bfa, &fcxp->hcb_qe, + __bfa_fcxp_send_cbfn, fcxp); + } + } +} + +static struct bfa_fcxp_s * +bfa_fcxp_get(struct bfa_fcxp_mod_s *fm) +{ + struct bfa_fcxp_s *fcxp; + + bfa_q_deq(&fm->fcxp_free_q, &fcxp); + + if (fcxp) + list_add_tail(&fcxp->qe, &fm->fcxp_active_q); + + return (fcxp); +} + +static void +bfa_fcxp_put(struct bfa_fcxp_s *fcxp) +{ + struct bfa_fcxp_mod_s *mod = fcxp->fcxp_mod; + struct bfa_fcxp_wqe_s *wqe; + + bfa_q_deq(&mod->wait_q, &wqe); + if (wqe) { + bfa_trc(mod->bfa, fcxp->fcxp_tag); + wqe->alloc_cbfn(wqe->alloc_cbarg, fcxp); + return; + } + + bfa_assert(bfa_q_is_on_q(&mod->fcxp_active_q, fcxp)); + list_del(&fcxp->qe); + list_add_tail(&fcxp->qe, &mod->fcxp_free_q); +} + +static void +bfa_fcxp_null_comp(void *bfad_fcxp, struct bfa_fcxp_s *fcxp, void *cbarg, + bfa_status_t req_status, u32 rsp_len, + u32 resid_len, struct fchs_s *rsp_fchs) +{ + /**discarded fcxp completion */ +} + +static void +__bfa_fcxp_send_cbfn(void *cbarg, bfa_boolean_t complete) +{ + struct bfa_fcxp_s *fcxp = cbarg; + + if (complete) { + fcxp->send_cbfn(fcxp->caller, fcxp, fcxp->send_cbarg, + fcxp->rsp_status, fcxp->rsp_len, + fcxp->residue_len, &fcxp->rsp_fchs); + } else { + bfa_fcxp_free(fcxp); + } +} + +static void +hal_fcxp_send_comp(struct bfa_s *bfa, struct bfi_fcxp_send_rsp_s *fcxp_rsp) +{ + struct bfa_fcxp_mod_s *mod = BFA_FCXP_MOD(bfa); + struct bfa_fcxp_s *fcxp; + u16 fcxp_tag = bfa_os_ntohs(fcxp_rsp->fcxp_tag); + + bfa_trc(bfa, fcxp_tag); + + fcxp_rsp->rsp_len = bfa_os_ntohl(fcxp_rsp->rsp_len); + + /** + * @todo f/w should not set residue to non-0 when everything + * is received. + */ + if (fcxp_rsp->req_status == BFA_STATUS_OK) + fcxp_rsp->residue_len = 0; + else + fcxp_rsp->residue_len = bfa_os_ntohl(fcxp_rsp->residue_len); + + fcxp = BFA_FCXP_FROM_TAG(mod, fcxp_tag); + + bfa_assert(fcxp->send_cbfn != NULL); + + hal_fcxp_rx_plog(mod->bfa, fcxp, fcxp_rsp); + + if (fcxp->send_cbfn != NULL) { + if (fcxp->caller == NULL) { + bfa_trc(mod->bfa, fcxp->fcxp_tag); + + fcxp->send_cbfn(fcxp->caller, fcxp, fcxp->send_cbarg, + fcxp_rsp->req_status, fcxp_rsp->rsp_len, + fcxp_rsp->residue_len, &fcxp_rsp->fchs); + /* + * fcxp automatically freed on return from the callback + */ + bfa_fcxp_free(fcxp); + } else { + bfa_trc(mod->bfa, fcxp->fcxp_tag); + fcxp->rsp_status = fcxp_rsp->req_status; + fcxp->rsp_len = fcxp_rsp->rsp_len; + fcxp->residue_len = fcxp_rsp->residue_len; + fcxp->rsp_fchs = fcxp_rsp->fchs; + + bfa_cb_queue(bfa, &fcxp->hcb_qe, + __bfa_fcxp_send_cbfn, fcxp); + } + } else { + bfa_trc(bfa, fcxp_tag); + } +} + +static void +hal_fcxp_set_local_sges(struct bfi_sge_s *sge, u32 reqlen, u64 req_pa) +{ + union bfi_addr_u sga_zero = { {0} }; + + sge->sg_len = reqlen; + sge->flags = BFI_SGE_DATA_LAST; + bfa_dma_addr_set(sge[0].sga, req_pa); + bfa_sge_to_be(sge); + sge++; + + sge->sga = sga_zero; + sge->sg_len = reqlen; + sge->flags = BFI_SGE_PGDLEN; + bfa_sge_to_be(sge); +} + +static void +hal_fcxp_tx_plog(struct bfa_s *bfa, u32 reqlen, struct bfa_fcxp_s *fcxp, + struct fchs_s *fchs) +{ + /* + * TODO: TX ox_id + */ + if (reqlen > 0) { + if (fcxp->use_ireqbuf) { + u32 pld_w0 = + *((u32 *) BFA_FCXP_REQ_PLD(fcxp)); + + bfa_plog_fchdr_and_pl(bfa->plog, BFA_PL_MID_HAL_FCXP, + BFA_PL_EID_TX, + reqlen + sizeof(struct fchs_s), fchs, pld_w0); + } else { + bfa_plog_fchdr(bfa->plog, BFA_PL_MID_HAL_FCXP, + BFA_PL_EID_TX, reqlen + sizeof(struct fchs_s), + fchs); + } + } else { + bfa_plog_fchdr(bfa->plog, BFA_PL_MID_HAL_FCXP, BFA_PL_EID_TX, + reqlen + sizeof(struct fchs_s), fchs); + } +} + +static void +hal_fcxp_rx_plog(struct bfa_s *bfa, struct bfa_fcxp_s *fcxp, + struct bfi_fcxp_send_rsp_s *fcxp_rsp) +{ + if (fcxp_rsp->rsp_len > 0) { + if (fcxp->use_irspbuf) { + u32 pld_w0 = + *((u32 *) BFA_FCXP_RSP_PLD(fcxp)); + + bfa_plog_fchdr_and_pl(bfa->plog, BFA_PL_MID_HAL_FCXP, + BFA_PL_EID_RX, + (u16) fcxp_rsp->rsp_len, + &fcxp_rsp->fchs, pld_w0); + } else { + bfa_plog_fchdr(bfa->plog, BFA_PL_MID_HAL_FCXP, + BFA_PL_EID_RX, + (u16) fcxp_rsp->rsp_len, + &fcxp_rsp->fchs); + } + } else { + bfa_plog_fchdr(bfa->plog, BFA_PL_MID_HAL_FCXP, BFA_PL_EID_RX, + (u16) fcxp_rsp->rsp_len, &fcxp_rsp->fchs); + } +} + +/** + * Handler to resume sending fcxp when space in available in cpe queue. + */ +static void +bfa_fcxp_qresume(void *cbarg) +{ + struct bfa_fcxp_s *fcxp = cbarg; + struct bfa_s *bfa = fcxp->fcxp_mod->bfa; + struct bfi_fcxp_send_req_s *send_req; + + fcxp->reqq_waiting = BFA_FALSE; + send_req = bfa_reqq_next(bfa, BFA_REQQ_FCXP); + bfa_fcxp_queue(fcxp, send_req); +} + +/** + * Queue fcxp send request to foimrware. + */ +static void +bfa_fcxp_queue(struct bfa_fcxp_s *fcxp, struct bfi_fcxp_send_req_s *send_req) +{ + struct bfa_s *bfa = fcxp->fcxp_mod->bfa; + struct bfa_fcxp_req_info_s *reqi = &fcxp->req_info; + struct bfa_fcxp_rsp_info_s *rspi = &fcxp->rsp_info; + struct bfa_rport_s *rport = reqi->bfa_rport; + + bfi_h2i_set(send_req->mh, BFI_MC_FCXP, BFI_FCXP_H2I_SEND_REQ, + bfa_lpuid(bfa)); + + send_req->fcxp_tag = bfa_os_htons(fcxp->fcxp_tag); + if (rport) { + send_req->rport_fw_hndl = rport->fw_handle; + send_req->max_frmsz = bfa_os_htons(rport->rport_info.max_frmsz); + if (send_req->max_frmsz == 0) + send_req->max_frmsz = bfa_os_htons(FC_MAX_PDUSZ); + } else { + send_req->rport_fw_hndl = 0; + send_req->max_frmsz = bfa_os_htons(FC_MAX_PDUSZ); + } + + send_req->vf_id = bfa_os_htons(reqi->vf_id); + send_req->lp_tag = reqi->lp_tag; + send_req->class = reqi->class; + send_req->rsp_timeout = rspi->rsp_timeout; + send_req->cts = reqi->cts; + send_req->fchs = reqi->fchs; + + send_req->req_len = bfa_os_htonl(reqi->req_tot_len); + send_req->rsp_maxlen = bfa_os_htonl(rspi->rsp_maxlen); + + /* + * setup req sgles + */ + if (fcxp->use_ireqbuf == 1) { + hal_fcxp_set_local_sges(send_req->req_sge, reqi->req_tot_len, + BFA_FCXP_REQ_PLD_PA(fcxp)); + } else { + if (fcxp->nreq_sgles > 0) { + bfa_assert(fcxp->nreq_sgles == 1); + hal_fcxp_set_local_sges(send_req->req_sge, + reqi->req_tot_len, + fcxp->req_sga_cbfn(fcxp->caller, + 0)); + } else { + bfa_assert(reqi->req_tot_len == 0); + hal_fcxp_set_local_sges(send_req->rsp_sge, 0, 0); + } + } + + /* + * setup rsp sgles + */ + if (fcxp->use_irspbuf == 1) { + bfa_assert(rspi->rsp_maxlen <= BFA_FCXP_MAX_LBUF_SZ); + + hal_fcxp_set_local_sges(send_req->rsp_sge, rspi->rsp_maxlen, + BFA_FCXP_RSP_PLD_PA(fcxp)); + + } else { + if (fcxp->nrsp_sgles > 0) { + bfa_assert(fcxp->nrsp_sgles == 1); + hal_fcxp_set_local_sges(send_req->rsp_sge, + rspi->rsp_maxlen, + fcxp->rsp_sga_cbfn(fcxp->caller, + 0)); + } else { + bfa_assert(rspi->rsp_maxlen == 0); + hal_fcxp_set_local_sges(send_req->rsp_sge, 0, 0); + } + } + + hal_fcxp_tx_plog(bfa, reqi->req_tot_len, fcxp, &reqi->fchs); + + bfa_reqq_produce(bfa, BFA_REQQ_FCXP); + + bfa_trc(bfa, bfa_reqq_pi(bfa, BFA_REQQ_FCXP)); + bfa_trc(bfa, bfa_reqq_ci(bfa, BFA_REQQ_FCXP)); +} + + +/** + * hal_fcxp_api BFA FCXP API + */ + +/** + * Allocate an FCXP instance to send a response or to send a request + * that has a response. Request/response buffers are allocated by caller. + * + * @param[in] bfa BFA bfa instance + * @param[in] nreq_sgles Number of SG elements required for request + * buffer. 0, if fcxp internal buffers are used. + * Use bfa_fcxp_get_reqbuf() to get the + * internal req buffer. + * @param[in] req_sgles SG elements describing request buffer. Will be + * copied in by BFA and hence can be freed on + * return from this function. + * @param[in] get_req_sga function ptr to be called to get a request SG + * Address (given the sge index). + * @param[in] get_req_sglen function ptr to be called to get a request SG + * len (given the sge index). + * @param[in] get_rsp_sga function ptr to be called to get a response SG + * Address (given the sge index). + * @param[in] get_rsp_sglen function ptr to be called to get a response SG + * len (given the sge index). + * + * @return FCXP instance. NULL on failure. + */ +struct bfa_fcxp_s * +bfa_fcxp_alloc(void *caller, struct bfa_s *bfa, int nreq_sgles, + int nrsp_sgles, bfa_fcxp_get_sgaddr_t req_sga_cbfn, + bfa_fcxp_get_sglen_t req_sglen_cbfn, + bfa_fcxp_get_sgaddr_t rsp_sga_cbfn, + bfa_fcxp_get_sglen_t rsp_sglen_cbfn) +{ + struct bfa_fcxp_s *fcxp = NULL; + u32 nreq_sgpg, nrsp_sgpg; + + bfa_assert(bfa != NULL); + + fcxp = bfa_fcxp_get(BFA_FCXP_MOD(bfa)); + if (fcxp == NULL) + return (NULL); + + bfa_trc(bfa, fcxp->fcxp_tag); + + fcxp->caller = caller; + + if (nreq_sgles == 0) { + fcxp->use_ireqbuf = 1; + } else { + bfa_assert(req_sga_cbfn != NULL); + bfa_assert(req_sglen_cbfn != NULL); + + fcxp->use_ireqbuf = 0; + fcxp->req_sga_cbfn = req_sga_cbfn; + fcxp->req_sglen_cbfn = req_sglen_cbfn; + + fcxp->nreq_sgles = nreq_sgles; + + /* + * alloc required sgpgs + */ + if (nreq_sgles > BFI_SGE_INLINE) { + nreq_sgpg = BFA_SGPG_NPAGE(nreq_sgles); + + if (bfa_sgpg_malloc + (bfa, &fcxp->req_sgpg_q, nreq_sgpg) + != BFA_STATUS_OK) { + /* bfa_sgpg_wait(bfa, &fcxp->req_sgpg_wqe, + nreq_sgpg); */ + /* + * TODO + */ + } + } + } + + if (nrsp_sgles == 0) { + fcxp->use_irspbuf = 1; + } else { + bfa_assert(rsp_sga_cbfn != NULL); + bfa_assert(rsp_sglen_cbfn != NULL); + + fcxp->use_irspbuf = 0; + fcxp->rsp_sga_cbfn = rsp_sga_cbfn; + fcxp->rsp_sglen_cbfn = rsp_sglen_cbfn; + + fcxp->nrsp_sgles = nrsp_sgles; + /* + * alloc required sgpgs + */ + if (nrsp_sgles > BFI_SGE_INLINE) { + nrsp_sgpg = BFA_SGPG_NPAGE(nreq_sgles); + + if (bfa_sgpg_malloc + (bfa, &fcxp->rsp_sgpg_q, nrsp_sgpg) + != BFA_STATUS_OK) { + /* bfa_sgpg_wait(bfa, &fcxp->rsp_sgpg_wqe, + nrsp_sgpg); */ + /* + * TODO + */ + } + } + } + + return (fcxp); +} + +/** + * Get the internal request buffer pointer + * + * @param[in] fcxp BFA fcxp pointer + * + * @return pointer to the internal request buffer + */ +void * +bfa_fcxp_get_reqbuf(struct bfa_fcxp_s *fcxp) +{ + struct bfa_fcxp_mod_s *mod = fcxp->fcxp_mod; + void *reqbuf; + + bfa_assert(fcxp->use_ireqbuf == 1); + reqbuf = ((u8 *)mod->req_pld_list_kva) + + fcxp->fcxp_tag * mod->req_pld_sz; + return reqbuf; +} + +u32 +bfa_fcxp_get_reqbufsz(struct bfa_fcxp_s *fcxp) +{ + struct bfa_fcxp_mod_s *mod = fcxp->fcxp_mod; + + return mod->req_pld_sz; +} + +/** + * Get the internal response buffer pointer + * + * @param[in] fcxp BFA fcxp pointer + * + * @return pointer to the internal request buffer + */ +void * +bfa_fcxp_get_rspbuf(struct bfa_fcxp_s *fcxp) +{ + struct bfa_fcxp_mod_s *mod = fcxp->fcxp_mod; + void *rspbuf; + + bfa_assert(fcxp->use_irspbuf == 1); + + rspbuf = ((u8 *)mod->rsp_pld_list_kva) + + fcxp->fcxp_tag * mod->rsp_pld_sz; + return rspbuf; +} + +/** + * Free the BFA FCXP + * + * @param[in] fcxp BFA fcxp pointer + * + * @return void + */ +void +bfa_fcxp_free(struct bfa_fcxp_s *fcxp) +{ + struct bfa_fcxp_mod_s *mod = fcxp->fcxp_mod; + + bfa_assert(fcxp != NULL); + bfa_trc(mod->bfa, fcxp->fcxp_tag); + bfa_fcxp_put(fcxp); +} + +/** + * Send a FCXP request + * + * @param[in] fcxp BFA fcxp pointer + * @param[in] rport BFA rport pointer. Could be left NULL for WKA rports + * @param[in] vf_id virtual Fabric ID + * @param[in] lp_tag lport tag + * @param[in] cts use Continous sequence + * @param[in] cos fc Class of Service + * @param[in] reqlen request length, does not include FCHS length + * @param[in] fchs fc Header Pointer. The header content will be copied + * in by BFA. + * + * @param[in] cbfn call back function to be called on receiving + * the response + * @param[in] cbarg arg for cbfn + * @param[in] rsp_timeout + * response timeout + * + * @return bfa_status_t + */ +void +bfa_fcxp_send(struct bfa_fcxp_s *fcxp, struct bfa_rport_s *rport, + u16 vf_id, u8 lp_tag, bfa_boolean_t cts, enum fc_cos cos, + u32 reqlen, struct fchs_s *fchs, bfa_cb_fcxp_send_t cbfn, + void *cbarg, u32 rsp_maxlen, u8 rsp_timeout) +{ + struct bfa_s *bfa = fcxp->fcxp_mod->bfa; + struct bfa_fcxp_req_info_s *reqi = &fcxp->req_info; + struct bfa_fcxp_rsp_info_s *rspi = &fcxp->rsp_info; + struct bfi_fcxp_send_req_s *send_req; + + bfa_trc(bfa, fcxp->fcxp_tag); + + /** + * setup request/response info + */ + reqi->bfa_rport = rport; + reqi->vf_id = vf_id; + reqi->lp_tag = lp_tag; + reqi->class = cos; + rspi->rsp_timeout = rsp_timeout; + reqi->cts = cts; + reqi->fchs = *fchs; + reqi->req_tot_len = reqlen; + rspi->rsp_maxlen = rsp_maxlen; + fcxp->send_cbfn = cbfn ? cbfn : bfa_fcxp_null_comp; + fcxp->send_cbarg = cbarg; + + /** + * If no room in CPE queue, wait for + */ + send_req = bfa_reqq_next(bfa, BFA_REQQ_FCXP); + if (!send_req) { + bfa_trc(bfa, fcxp->fcxp_tag); + fcxp->reqq_waiting = BFA_TRUE; + bfa_reqq_wait(bfa, BFA_REQQ_FCXP, &fcxp->reqq_wqe); + return; + } + + bfa_fcxp_queue(fcxp, send_req); +} + +/** + * Abort a BFA FCXP + * + * @param[in] fcxp BFA fcxp pointer + * + * @return void + */ +bfa_status_t +bfa_fcxp_abort(struct bfa_fcxp_s *fcxp) +{ + bfa_assert(0); + return (BFA_STATUS_OK); +} + +void +bfa_fcxp_alloc_wait(struct bfa_s *bfa, struct bfa_fcxp_wqe_s *wqe, + bfa_fcxp_alloc_cbfn_t alloc_cbfn, void *alloc_cbarg) +{ + struct bfa_fcxp_mod_s *mod = BFA_FCXP_MOD(bfa); + + bfa_assert(list_empty(&mod->fcxp_free_q)); + + wqe->alloc_cbfn = alloc_cbfn; + wqe->alloc_cbarg = alloc_cbarg; + list_add_tail(&wqe->qe, &mod->wait_q); +} + +void +bfa_fcxp_walloc_cancel(struct bfa_s *bfa, struct bfa_fcxp_wqe_s *wqe) +{ + struct bfa_fcxp_mod_s *mod = BFA_FCXP_MOD(bfa); + + bfa_assert(bfa_q_is_on_q(&mod->wait_q, wqe)); + list_del(&wqe->qe); +} + +void +bfa_fcxp_discard(struct bfa_fcxp_s *fcxp) +{ + /** + * If waiting for room in request queue, cancel reqq wait + * and free fcxp. + */ + if (fcxp->reqq_waiting) { + fcxp->reqq_waiting = BFA_FALSE; + bfa_reqq_wcancel(&fcxp->reqq_wqe); + bfa_fcxp_free(fcxp); + return; + } + + fcxp->send_cbfn = bfa_fcxp_null_comp; +} + + + +/** + * hal_fcxp_public BFA FCXP public functions + */ + +void +bfa_fcxp_isr(struct bfa_s *bfa, struct bfi_msg_s *msg) +{ + switch (msg->mhdr.msg_id) { + case BFI_FCXP_I2H_SEND_RSP: + hal_fcxp_send_comp(bfa, (struct bfi_fcxp_send_rsp_s *) msg); + break; + + default: + bfa_trc(bfa, msg->mhdr.msg_id); + bfa_assert(0); + } +} + +u32 +bfa_fcxp_get_maxrsp(struct bfa_s *bfa) +{ + struct bfa_fcxp_mod_s *mod = BFA_FCXP_MOD(bfa); + + return mod->rsp_pld_sz; +} + + diff --git a/drivers/scsi/bfa/bfa_fcxp_priv.h b/drivers/scsi/bfa/bfa_fcxp_priv.h new file mode 100644 index 000000000000..4cda49397da0 --- /dev/null +++ b/drivers/scsi/bfa/bfa_fcxp_priv.h @@ -0,0 +1,138 @@ +/* + * Copyright (c) 2005-2009 Brocade Communications Systems, Inc. + * All rights reserved + * www.brocade.com + * + * Linux driver for Brocade Fibre Channel Host Bus Adapter. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (GPL) Version 2 as + * published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#ifndef __BFA_FCXP_PRIV_H__ +#define __BFA_FCXP_PRIV_H__ + +#include <cs/bfa_sm.h> +#include <protocol/fc.h> +#include <bfa_svc.h> +#include <bfi/bfi_fcxp.h> + +#define BFA_FCXP_MIN (1) +#define BFA_FCXP_MAX_IBUF_SZ (2 * 1024 + 256) +#define BFA_FCXP_MAX_LBUF_SZ (4 * 1024 + 256) + +struct bfa_fcxp_mod_s { + struct bfa_s *bfa; /* backpointer to BFA */ + struct bfa_fcxp_s *fcxp_list; /* array of FCXPs */ + u16 num_fcxps; /* max num FCXP requests */ + struct list_head fcxp_free_q; /* free FCXPs */ + struct list_head fcxp_active_q; /* active FCXPs */ + void *req_pld_list_kva; /* list of FCXP req pld */ + u64 req_pld_list_pa; /* list of FCXP req pld */ + void *rsp_pld_list_kva; /* list of FCXP resp pld */ + u64 rsp_pld_list_pa; /* list of FCXP resp pld */ + struct list_head wait_q; /* wait queue for free fcxp */ + u32 req_pld_sz; + u32 rsp_pld_sz; +}; + +#define BFA_FCXP_MOD(__bfa) (&(__bfa)->modules.fcxp_mod) +#define BFA_FCXP_FROM_TAG(__mod, __tag) (&(__mod)->fcxp_list[__tag]) + +typedef void (*fcxp_send_cb_t) (struct bfa_s *ioc, struct bfa_fcxp_s *fcxp, + void *cb_arg, bfa_status_t req_status, + u32 rsp_len, u32 resid_len, + struct fchs_s *rsp_fchs); + +/** + * Information needed for a FCXP request + */ +struct bfa_fcxp_req_info_s { + struct bfa_rport_s *bfa_rport; /* Pointer to the bfa rport that was + *returned from bfa_rport_create(). + *This could be left NULL for WKA or for + *FCXP interactions before the rport + *nexus is established + */ + struct fchs_s fchs; /* request FC header structure */ + u8 cts; /* continous sequence */ + u8 class; /* FC class for the request/response */ + u16 max_frmsz; /* max send frame size */ + u16 vf_id; /* vsan tag if applicable */ + u8 lp_tag; /* lport tag */ + u32 req_tot_len; /* request payload total length */ +}; + +struct bfa_fcxp_rsp_info_s { + struct fchs_s rsp_fchs; /* Response frame's FC header will + * be *sent back in this field */ + u8 rsp_timeout; /* timeout in seconds, 0-no response + */ + u8 rsvd2[3]; + u32 rsp_maxlen; /* max response length expected */ +}; + +struct bfa_fcxp_s { + struct list_head qe; /* fcxp queue element */ + bfa_sm_t sm; /* state machine */ + void *caller; /* driver or fcs */ + struct bfa_fcxp_mod_s *fcxp_mod; + /* back pointer to fcxp mod */ + u16 fcxp_tag; /* internal tag */ + struct bfa_fcxp_req_info_s req_info; + /* request info */ + struct bfa_fcxp_rsp_info_s rsp_info; + /* response info */ + u8 use_ireqbuf; /* use internal req buf */ + u8 use_irspbuf; /* use internal rsp buf */ + u32 nreq_sgles; /* num request SGLEs */ + u32 nrsp_sgles; /* num response SGLEs */ + struct list_head req_sgpg_q; /* SG pages for request buf */ + struct list_head req_sgpg_wqe; /* wait queue for req SG page */ + struct list_head rsp_sgpg_q; /* SG pages for response buf */ + struct list_head rsp_sgpg_wqe; /* wait queue for rsp SG page */ + + bfa_fcxp_get_sgaddr_t req_sga_cbfn; + /* SG elem addr user function */ + bfa_fcxp_get_sglen_t req_sglen_cbfn; + /* SG elem len user function */ + bfa_fcxp_get_sgaddr_t rsp_sga_cbfn; + /* SG elem addr user function */ + bfa_fcxp_get_sglen_t rsp_sglen_cbfn; + /* SG elem len user function */ + bfa_cb_fcxp_send_t send_cbfn; /* send completion callback */ + void *send_cbarg; /* callback arg */ + struct bfa_sge_s req_sge[BFA_FCXP_MAX_SGES]; + /* req SG elems */ + struct bfa_sge_s rsp_sge[BFA_FCXP_MAX_SGES]; + /* rsp SG elems */ + u8 rsp_status; /* comp: rsp status */ + u32 rsp_len; /* comp: actual response len */ + u32 residue_len; /* comp: residual rsp length */ + struct fchs_s rsp_fchs; /* comp: response fchs */ + struct bfa_cb_qe_s hcb_qe; /* comp: callback qelem */ + struct bfa_reqq_wait_s reqq_wqe; + bfa_boolean_t reqq_waiting; +}; + +#define BFA_FCXP_REQ_PLD(_fcxp) (bfa_fcxp_get_reqbuf(_fcxp)) + +#define BFA_FCXP_RSP_FCHS(_fcxp) (&((_fcxp)->rsp_info.fchs)) +#define BFA_FCXP_RSP_PLD(_fcxp) (bfa_fcxp_get_rspbuf(_fcxp)) + +#define BFA_FCXP_REQ_PLD_PA(_fcxp) \ + ((_fcxp)->fcxp_mod->req_pld_list_pa + \ + ((_fcxp)->fcxp_mod->req_pld_sz * (_fcxp)->fcxp_tag)) + +#define BFA_FCXP_RSP_PLD_PA(_fcxp) \ + ((_fcxp)->fcxp_mod->rsp_pld_list_pa + \ + ((_fcxp)->fcxp_mod->rsp_pld_sz * (_fcxp)->fcxp_tag)) + +void bfa_fcxp_isr(struct bfa_s *bfa, struct bfi_msg_s *msg); +#endif /* __BFA_FCXP_PRIV_H__ */ diff --git a/drivers/scsi/bfa/bfa_fwimg_priv.h b/drivers/scsi/bfa/bfa_fwimg_priv.h new file mode 100644 index 000000000000..1ec1355924d9 --- /dev/null +++ b/drivers/scsi/bfa/bfa_fwimg_priv.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2005-2009 Brocade Communications Systems, Inc. + * All rights reserved + * www.brocade.com + * + * Linux driver for Brocade Fibre Channel Host Bus Adapter. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (GPL) Version 2 as + * published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#ifndef __BFA_FWIMG_PRIV_H__ +#define __BFA_FWIMG_PRIV_H__ + +#define BFI_FLASH_CHUNK_SZ 256 /* Flash chunk size */ +#define BFI_FLASH_CHUNK_SZ_WORDS (BFI_FLASH_CHUNK_SZ/sizeof(u32)) + +extern u32 *bfi_image_ct_get_chunk(u32 off); +extern u32 bfi_image_ct_size; +extern u32 *bfi_image_cb_get_chunk(u32 off); +extern u32 bfi_image_cb_size; +extern u32 *bfi_image_cb; +extern u32 *bfi_image_ct; + +#endif /* __BFA_FWIMG_PRIV_H__ */ diff --git a/drivers/scsi/bfa/bfa_hw_cb.c b/drivers/scsi/bfa/bfa_hw_cb.c new file mode 100644 index 000000000000..ede1438619e2 --- /dev/null +++ b/drivers/scsi/bfa/bfa_hw_cb.c @@ -0,0 +1,142 @@ +/* + * Copyright (c) 2005-2009 Brocade Communications Systems, Inc. + * All rights reserved + * www.brocade.com + * + * Linux driver for Brocade Fibre Channel Host Bus Adapter. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (GPL) Version 2 as + * published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#include <bfa_priv.h> +#include <bfi/bfi_cbreg.h> + +void +bfa_hwcb_reginit(struct bfa_s *bfa) +{ + struct bfa_iocfc_regs_s *bfa_regs = &bfa->iocfc.bfa_regs; + bfa_os_addr_t kva = bfa_ioc_bar0(&bfa->ioc); + int i, q, fn = bfa_ioc_pcifn(&bfa->ioc); + + if (fn == 0) { + bfa_regs->intr_status = (kva + HOSTFN0_INT_STATUS); + bfa_regs->intr_mask = (kva + HOSTFN0_INT_MSK); + } else { + bfa_regs->intr_status = (kva + HOSTFN1_INT_STATUS); + bfa_regs->intr_mask = (kva + HOSTFN1_INT_MSK); + } + + for (i = 0; i < BFI_IOC_MAX_CQS; i++) { + /* + * CPE registers + */ + q = CPE_Q_NUM(fn, i); + bfa_regs->cpe_q_pi[i] = (kva + CPE_Q_PI(q)); + bfa_regs->cpe_q_ci[i] = (kva + CPE_Q_CI(q)); + bfa_regs->cpe_q_depth[i] = (kva + CPE_Q_DEPTH(q)); + + /* + * RME registers + */ + q = CPE_Q_NUM(fn, i); + bfa_regs->rme_q_pi[i] = (kva + RME_Q_PI(q)); + bfa_regs->rme_q_ci[i] = (kva + RME_Q_CI(q)); + bfa_regs->rme_q_depth[i] = (kva + RME_Q_DEPTH(q)); + } +} + +void +bfa_hwcb_rspq_ack(struct bfa_s *bfa, int rspq) +{ +} + +static void +bfa_hwcb_rspq_ack_msix(struct bfa_s *bfa, int rspq) +{ + bfa_reg_write(bfa->iocfc.bfa_regs.intr_status, + __HFN_INT_RME_Q0 << RME_Q_NUM(bfa_ioc_pcifn(&bfa->ioc), rspq)); +} + +void +bfa_hwcb_msix_getvecs(struct bfa_s *bfa, u32 *msix_vecs_bmap, + u32 *num_vecs, u32 *max_vec_bit) +{ +#define __HFN_NUMINTS 13 + if (bfa_ioc_pcifn(&bfa->ioc) == 0) { + *msix_vecs_bmap = (__HFN_INT_CPE_Q0 | __HFN_INT_CPE_Q1 | + __HFN_INT_CPE_Q2 | __HFN_INT_CPE_Q3 | + __HFN_INT_RME_Q0 | __HFN_INT_RME_Q1 | + __HFN_INT_RME_Q2 | __HFN_INT_RME_Q3 | + __HFN_INT_MBOX_LPU0); + *max_vec_bit = __HFN_INT_MBOX_LPU0; + } else { + *msix_vecs_bmap = (__HFN_INT_CPE_Q4 | __HFN_INT_CPE_Q5 | + __HFN_INT_CPE_Q6 | __HFN_INT_CPE_Q7 | + __HFN_INT_RME_Q4 | __HFN_INT_RME_Q5 | + __HFN_INT_RME_Q6 | __HFN_INT_RME_Q7 | + __HFN_INT_MBOX_LPU1); + *max_vec_bit = __HFN_INT_MBOX_LPU1; + } + + *msix_vecs_bmap |= (__HFN_INT_ERR_EMC | __HFN_INT_ERR_LPU0 | + __HFN_INT_ERR_LPU1 | __HFN_INT_ERR_PSS); + *num_vecs = __HFN_NUMINTS; +} + +/** + * No special setup required for crossbow -- vector assignments are implicit. + */ +void +bfa_hwcb_msix_init(struct bfa_s *bfa, int nvecs) +{ + int i; + + bfa_assert((nvecs == 1) || (nvecs == __HFN_NUMINTS)); + + bfa->msix.nvecs = nvecs; + if (nvecs == 1) { + for (i = 0; i < BFA_MSIX_CB_MAX; i++) + bfa->msix.handler[i] = bfa_msix_all; + return; + } + + for (i = BFA_MSIX_CPE_Q0; i <= BFA_MSIX_CPE_Q7; i++) + bfa->msix.handler[i] = bfa_msix_reqq; + + for (i = BFA_MSIX_RME_Q0; i <= BFA_MSIX_RME_Q7; i++) + bfa->msix.handler[i] = bfa_msix_rspq; + + for (; i < BFA_MSIX_CB_MAX; i++) + bfa->msix.handler[i] = bfa_msix_lpu_err; +} + +/** + * Crossbow -- dummy, interrupts are masked + */ +void +bfa_hwcb_msix_install(struct bfa_s *bfa) +{ +} + +void +bfa_hwcb_msix_uninstall(struct bfa_s *bfa) +{ +} + +/** + * No special enable/disable -- vector assignments are implicit. + */ +void +bfa_hwcb_isr_mode_set(struct bfa_s *bfa, bfa_boolean_t msix) +{ + bfa->iocfc.hwif.hw_rspq_ack = bfa_hwcb_rspq_ack_msix; +} + + diff --git a/drivers/scsi/bfa/bfa_hw_ct.c b/drivers/scsi/bfa/bfa_hw_ct.c new file mode 100644 index 000000000000..51ae5740e6e9 --- /dev/null +++ b/drivers/scsi/bfa/bfa_hw_ct.c @@ -0,0 +1,162 @@ +/* + * Copyright (c) 2005-2009 Brocade Communications Systems, Inc. + * All rights reserved + * www.brocade.com + * + * Linux driver for Brocade Fibre Channel Host Bus Adapter. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (GPL) Version 2 as + * published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#include <bfa_priv.h> +#include <bfi/bfi_ctreg.h> +#include <bfa_ioc.h> + +BFA_TRC_FILE(HAL, IOCFC_CT); + +static u32 __ct_msix_err_vec_reg[] = { + HOST_MSIX_ERR_INDEX_FN0, + HOST_MSIX_ERR_INDEX_FN1, + HOST_MSIX_ERR_INDEX_FN2, + HOST_MSIX_ERR_INDEX_FN3, +}; + +static void +bfa_hwct_msix_lpu_err_set(struct bfa_s *bfa, bfa_boolean_t msix, int vec) +{ + int fn = bfa_ioc_pcifn(&bfa->ioc); + bfa_os_addr_t kva = bfa_ioc_bar0(&bfa->ioc); + + if (msix) + bfa_reg_write(kva + __ct_msix_err_vec_reg[fn], vec); + else + bfa_reg_write(kva + __ct_msix_err_vec_reg[fn], 0); +} + +/** + * Dummy interrupt handler for handling spurious interrupt during chip-reinit. + */ +static void +bfa_hwct_msix_dummy(struct bfa_s *bfa, int vec) +{ +} + +void +bfa_hwct_reginit(struct bfa_s *bfa) +{ + struct bfa_iocfc_regs_s *bfa_regs = &bfa->iocfc.bfa_regs; + bfa_os_addr_t kva = bfa_ioc_bar0(&bfa->ioc); + int i, q, fn = bfa_ioc_pcifn(&bfa->ioc); + + if (fn == 0) { + bfa_regs->intr_status = (kva + HOSTFN0_INT_STATUS); + bfa_regs->intr_mask = (kva + HOSTFN0_INT_MSK); + } else { + bfa_regs->intr_status = (kva + HOSTFN1_INT_STATUS); + bfa_regs->intr_mask = (kva + HOSTFN1_INT_MSK); + } + + for (i = 0; i < BFI_IOC_MAX_CQS; i++) { + /* + * CPE registers + */ + q = CPE_Q_NUM(fn, i); + bfa_regs->cpe_q_pi[i] = (kva + CPE_PI_PTR_Q(q << 5)); + bfa_regs->cpe_q_ci[i] = (kva + CPE_CI_PTR_Q(q << 5)); + bfa_regs->cpe_q_depth[i] = (kva + CPE_DEPTH_Q(q << 5)); + bfa_regs->cpe_q_ctrl[i] = (kva + CPE_QCTRL_Q(q << 5)); + + /* + * RME registers + */ + q = CPE_Q_NUM(fn, i); + bfa_regs->rme_q_pi[i] = (kva + RME_PI_PTR_Q(q << 5)); + bfa_regs->rme_q_ci[i] = (kva + RME_CI_PTR_Q(q << 5)); + bfa_regs->rme_q_depth[i] = (kva + RME_DEPTH_Q(q << 5)); + bfa_regs->rme_q_ctrl[i] = (kva + RME_QCTRL_Q(q << 5)); + } +} + +void +bfa_hwct_rspq_ack(struct bfa_s *bfa, int rspq) +{ + u32 r32; + + r32 = bfa_reg_read(bfa->iocfc.bfa_regs.rme_q_ctrl[rspq]); + bfa_reg_write(bfa->iocfc.bfa_regs.rme_q_ctrl[rspq], r32); +} + +void +bfa_hwct_msix_getvecs(struct bfa_s *bfa, u32 *msix_vecs_bmap, + u32 *num_vecs, u32 *max_vec_bit) +{ + *msix_vecs_bmap = (1 << BFA_MSIX_CT_MAX) - 1; + *max_vec_bit = (1 << (BFA_MSIX_CT_MAX - 1)); + *num_vecs = BFA_MSIX_CT_MAX; +} + +/** + * Setup MSI-X vector for catapult + */ +void +bfa_hwct_msix_init(struct bfa_s *bfa, int nvecs) +{ + bfa_assert((nvecs == 1) || (nvecs == BFA_MSIX_CT_MAX)); + bfa_trc(bfa, nvecs); + + bfa->msix.nvecs = nvecs; + bfa_hwct_msix_uninstall(bfa); +} + +void +bfa_hwct_msix_install(struct bfa_s *bfa) +{ + int i; + + if (bfa->msix.nvecs == 0) + return; + + if (bfa->msix.nvecs == 1) { + for (i = 0; i < BFA_MSIX_CT_MAX; i++) + bfa->msix.handler[i] = bfa_msix_all; + return; + } + + for (i = BFA_MSIX_CPE_Q0; i <= BFA_MSIX_CPE_Q3; i++) + bfa->msix.handler[i] = bfa_msix_reqq; + + for (; i <= BFA_MSIX_RME_Q3; i++) + bfa->msix.handler[i] = bfa_msix_rspq; + + bfa_assert(i == BFA_MSIX_LPU_ERR); + bfa->msix.handler[BFA_MSIX_LPU_ERR] = bfa_msix_lpu_err; +} + +void +bfa_hwct_msix_uninstall(struct bfa_s *bfa) +{ + int i; + + for (i = 0; i < BFA_MSIX_CT_MAX; i++) + bfa->msix.handler[i] = bfa_hwct_msix_dummy; +} + +/** + * Enable MSI-X vectors + */ +void +bfa_hwct_isr_mode_set(struct bfa_s *bfa, bfa_boolean_t msix) +{ + bfa_trc(bfa, 0); + bfa_hwct_msix_lpu_err_set(bfa, msix, BFA_MSIX_LPU_ERR); + bfa_ioc_isr_mode_set(&bfa->ioc, msix); +} + + diff --git a/drivers/scsi/bfa/bfa_intr.c b/drivers/scsi/bfa/bfa_intr.c new file mode 100644 index 000000000000..0ca125712a04 --- /dev/null +++ b/drivers/scsi/bfa/bfa_intr.c @@ -0,0 +1,218 @@ +/* + * Copyright (c) 2005-2009 Brocade Communications Systems, Inc. + * All rights reserved + * www.brocade.com + * + * Linux driver for Brocade Fibre Channel Host Bus Adapter. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (GPL) Version 2 as + * published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ +#include <bfa.h> +#include <bfi/bfi_cbreg.h> +#include <bfa_port_priv.h> +#include <bfa_intr_priv.h> +#include <cs/bfa_debug.h> + +BFA_TRC_FILE(HAL, INTR); + +static void +bfa_msix_errint(struct bfa_s *bfa, u32 intr) +{ + bfa_ioc_error_isr(&bfa->ioc); +} + +static void +bfa_msix_lpu(struct bfa_s *bfa) +{ + bfa_ioc_mbox_isr(&bfa->ioc); +} + +void +bfa_msix_all(struct bfa_s *bfa, int vec) +{ + bfa_intx(bfa); +} + +/** + * hal_intr_api + */ +bfa_boolean_t +bfa_intx(struct bfa_s *bfa) +{ + u32 intr, qintr; + int queue; + + intr = bfa_reg_read(bfa->iocfc.bfa_regs.intr_status); + if (!intr) + return BFA_FALSE; + + /** + * RME completion queue interrupt + */ + qintr = intr & __HFN_INT_RME_MASK; + bfa_reg_write(bfa->iocfc.bfa_regs.intr_status, qintr); + + for (queue = 0; queue < BFI_IOC_MAX_CQS_ASIC; queue ++) { + if (intr & (__HFN_INT_RME_Q0 << queue)) + bfa_msix_rspq(bfa, queue & (BFI_IOC_MAX_CQS - 1)); + } + intr &= ~qintr; + if (!intr) + return BFA_TRUE; + + /** + * CPE completion queue interrupt + */ + qintr = intr & __HFN_INT_CPE_MASK; + bfa_reg_write(bfa->iocfc.bfa_regs.intr_status, qintr); + + for (queue = 0; queue < BFI_IOC_MAX_CQS_ASIC; queue++) { + if (intr & (__HFN_INT_CPE_Q0 << queue)) + bfa_msix_reqq(bfa, queue & (BFI_IOC_MAX_CQS - 1)); + } + intr &= ~qintr; + if (!intr) + return BFA_TRUE; + + bfa_msix_lpu_err(bfa, intr); + + return BFA_TRUE; +} + +void +bfa_isr_enable(struct bfa_s *bfa) +{ + u32 intr_unmask; + int pci_func = bfa_ioc_pcifn(&bfa->ioc); + + bfa_trc(bfa, pci_func); + + bfa_msix_install(bfa); + intr_unmask = (__HFN_INT_ERR_EMC | __HFN_INT_ERR_LPU0 | + __HFN_INT_ERR_LPU1 | __HFN_INT_ERR_PSS); + + if (pci_func == 0) + intr_unmask |= (__HFN_INT_CPE_Q0 | __HFN_INT_CPE_Q1 | + __HFN_INT_CPE_Q2 | __HFN_INT_CPE_Q3 | + __HFN_INT_RME_Q0 | __HFN_INT_RME_Q1 | + __HFN_INT_RME_Q2 | __HFN_INT_RME_Q3 | + __HFN_INT_MBOX_LPU0); + else + intr_unmask |= (__HFN_INT_CPE_Q4 | __HFN_INT_CPE_Q5 | + __HFN_INT_CPE_Q6 | __HFN_INT_CPE_Q7 | + __HFN_INT_RME_Q4 | __HFN_INT_RME_Q5 | + __HFN_INT_RME_Q6 | __HFN_INT_RME_Q7 | + __HFN_INT_MBOX_LPU1); + + bfa_reg_write(bfa->iocfc.bfa_regs.intr_status, intr_unmask); + bfa_reg_write(bfa->iocfc.bfa_regs.intr_mask, ~intr_unmask); + bfa_isr_mode_set(bfa, bfa->msix.nvecs != 0); +} + +void +bfa_isr_disable(struct bfa_s *bfa) +{ + bfa_isr_mode_set(bfa, BFA_FALSE); + bfa_reg_write(bfa->iocfc.bfa_regs.intr_mask, -1L); + bfa_msix_uninstall(bfa); +} + +void +bfa_msix_reqq(struct bfa_s *bfa, int qid) +{ + struct list_head *waitq, *qe, *qen; + struct bfa_reqq_wait_s *wqe; + + qid &= (BFI_IOC_MAX_CQS - 1); + + waitq = bfa_reqq(bfa, qid); + list_for_each_safe(qe, qen, waitq) { + /** + * Callback only as long as there is room in request queue + */ + if (bfa_reqq_full(bfa, qid)) + break; + + list_del(qe); + wqe = (struct bfa_reqq_wait_s *) qe; + wqe->qresume(wqe->cbarg); + } +} + +void +bfa_isr_unhandled(struct bfa_s *bfa, struct bfi_msg_s *m) +{ + bfa_trc(bfa, m->mhdr.msg_class); + bfa_trc(bfa, m->mhdr.msg_id); + bfa_trc(bfa, m->mhdr.mtag.i2htok); + bfa_assert(0); + bfa_trc_stop(bfa->trcmod); +} + +void +bfa_msix_rspq(struct bfa_s *bfa, int rsp_qid) +{ + struct bfi_msg_s *m; + u32 pi, ci; + + bfa_trc_fp(bfa, rsp_qid); + + rsp_qid &= (BFI_IOC_MAX_CQS - 1); + + bfa->iocfc.hwif.hw_rspq_ack(bfa, rsp_qid); + + ci = bfa_rspq_ci(bfa, rsp_qid); + pi = bfa_rspq_pi(bfa, rsp_qid); + + bfa_trc_fp(bfa, ci); + bfa_trc_fp(bfa, pi); + + if (bfa->rme_process) { + while (ci != pi) { + m = bfa_rspq_elem(bfa, rsp_qid, ci); + bfa_assert_fp(m->mhdr.msg_class < BFI_MC_MAX); + + bfa_isrs[m->mhdr.msg_class] (bfa, m); + + CQ_INCR(ci, bfa->iocfc.cfg.drvcfg.num_rspq_elems); + } + } + + /** + * update CI + */ + bfa_rspq_ci(bfa, rsp_qid) = pi; + bfa_reg_write(bfa->iocfc.bfa_regs.rme_q_ci[rsp_qid], pi); + bfa_os_mmiowb(); +} + +void +bfa_msix_lpu_err(struct bfa_s *bfa, int vec) +{ + u32 intr; + + intr = bfa_reg_read(bfa->iocfc.bfa_regs.intr_status); + + if (intr & (__HFN_INT_MBOX_LPU0 | __HFN_INT_MBOX_LPU1)) + bfa_msix_lpu(bfa); + + if (intr & (__HFN_INT_ERR_EMC | + __HFN_INT_ERR_LPU0 | __HFN_INT_ERR_LPU1 | + __HFN_INT_ERR_PSS)) + bfa_msix_errint(bfa, intr); +} + +void +bfa_isr_bind(enum bfi_mclass mc, bfa_isr_func_t isr_func) +{ + bfa_isrs[mc] = isr_func; +} + + diff --git a/drivers/scsi/bfa/bfa_intr_priv.h b/drivers/scsi/bfa/bfa_intr_priv.h new file mode 100644 index 000000000000..8ce6e6b105c8 --- /dev/null +++ b/drivers/scsi/bfa/bfa_intr_priv.h @@ -0,0 +1,115 @@ +/* + * Copyright (c) 2005-2009 Brocade Communications Systems, Inc. + * All rights reserved + * www.brocade.com + * + * Linux driver for Brocade Fibre Channel Host Bus Adapter. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (GPL) Version 2 as + * published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#ifndef __BFA_INTR_PRIV_H__ +#define __BFA_INTR_PRIV_H__ + +/** + * Message handler + */ +typedef void (*bfa_isr_func_t) (struct bfa_s *bfa, struct bfi_msg_s *m); +void bfa_isr_unhandled(struct bfa_s *bfa, struct bfi_msg_s *m); +void bfa_isr_bind(enum bfi_mclass mc, bfa_isr_func_t isr_func); + + +#define bfa_reqq_pi(__bfa, __reqq) (__bfa)->iocfc.req_cq_pi[__reqq] +#define bfa_reqq_ci(__bfa, __reqq) \ + *(u32 *)((__bfa)->iocfc.req_cq_shadow_ci[__reqq].kva) + +#define bfa_reqq_full(__bfa, __reqq) \ + (((bfa_reqq_pi(__bfa, __reqq) + 1) & \ + ((__bfa)->iocfc.cfg.drvcfg.num_reqq_elems - 1)) == \ + bfa_reqq_ci(__bfa, __reqq)) + +#define bfa_reqq_next(__bfa, __reqq) \ + (bfa_reqq_full(__bfa, __reqq) ? NULL : \ + ((void *)((struct bfi_msg_s *)((__bfa)->iocfc.req_cq_ba[__reqq].kva) \ + + bfa_reqq_pi((__bfa), (__reqq))))) + +#define bfa_reqq_produce(__bfa, __reqq) do { \ + (__bfa)->iocfc.req_cq_pi[__reqq]++; \ + (__bfa)->iocfc.req_cq_pi[__reqq] &= \ + ((__bfa)->iocfc.cfg.drvcfg.num_reqq_elems - 1); \ + bfa_reg_write((__bfa)->iocfc.bfa_regs.cpe_q_pi[__reqq], \ + (__bfa)->iocfc.req_cq_pi[__reqq]); \ + bfa_os_mmiowb(); \ +} while (0) + +#define bfa_rspq_pi(__bfa, __rspq) \ + *(u32 *)((__bfa)->iocfc.rsp_cq_shadow_pi[__rspq].kva) + +#define bfa_rspq_ci(__bfa, __rspq) (__bfa)->iocfc.rsp_cq_ci[__rspq] +#define bfa_rspq_elem(__bfa, __rspq, __ci) \ + &((struct bfi_msg_s *)((__bfa)->iocfc.rsp_cq_ba[__rspq].kva))[__ci] + +#define CQ_INCR(__index, __size) \ + (__index)++; (__index) &= ((__size) - 1) + +/** + * Queue element to wait for room in request queue. FIFO order is + * maintained when fullfilling requests. + */ +struct bfa_reqq_wait_s { + struct list_head qe; + void (*qresume) (void *cbarg); + void *cbarg; +}; + +/** + * Circular queue usage assignments + */ +enum { + BFA_REQQ_IOC = 0, /* all low-priority IOC msgs */ + BFA_REQQ_FCXP = 0, /* all FCXP messages */ + BFA_REQQ_LPS = 0, /* all lport service msgs */ + BFA_REQQ_PORT = 0, /* all port messages */ + BFA_REQQ_FLASH = 0, /* for flash module */ + BFA_REQQ_DIAG = 0, /* for diag module */ + BFA_REQQ_RPORT = 0, /* all port messages */ + BFA_REQQ_SBOOT = 0, /* all san boot messages */ + BFA_REQQ_QOS_LO = 1, /* all low priority IO */ + BFA_REQQ_QOS_MD = 2, /* all medium priority IO */ + BFA_REQQ_QOS_HI = 3, /* all high priority IO */ +}; + +static inline void +bfa_reqq_winit(struct bfa_reqq_wait_s *wqe, void (*qresume) (void *cbarg), + void *cbarg) +{ + wqe->qresume = qresume; + wqe->cbarg = cbarg; +} + +#define bfa_reqq(__bfa, __reqq) &(__bfa)->reqq_waitq[__reqq] + +/** + * static inline void + * bfa_reqq_wait(struct bfa_s *bfa, int reqq, struct bfa_reqq_wait_s *wqe) + */ +#define bfa_reqq_wait(__bfa, __reqq, __wqe) do { \ + \ + struct list_head *waitq = bfa_reqq(__bfa, __reqq); \ + \ + bfa_assert(((__reqq) < BFI_IOC_MAX_CQS)); \ + bfa_assert((__wqe)->qresume && (__wqe)->cbarg); \ + \ + list_add_tail(&(__wqe)->qe, waitq); \ +} while (0) + +#define bfa_reqq_wcancel(__wqe) list_del(&(__wqe)->qe) + +#endif /* __BFA_INTR_PRIV_H__ */ diff --git a/drivers/scsi/bfa/bfa_ioc.c b/drivers/scsi/bfa/bfa_ioc.c new file mode 100644 index 000000000000..149348934ce3 --- /dev/null +++ b/drivers/scsi/bfa/bfa_ioc.c @@ -0,0 +1,2382 @@ +/* + * Copyright (c) 2005-2009 Brocade Communications Systems, Inc. + * All rights reserved + * www.brocade.com + * + * Linux driver for Brocade Fibre Channel Host Bus Adapter. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (GPL) Version 2 as + * published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#include <bfa.h> +#include <bfa_ioc.h> +#include <bfa_fwimg_priv.h> +#include <bfa_trcmod_priv.h> +#include <cs/bfa_debug.h> +#include <bfi/bfi_ioc.h> +#include <bfi/bfi_ctreg.h> +#include <aen/bfa_aen_ioc.h> +#include <aen/bfa_aen.h> +#include <log/bfa_log_hal.h> +#include <defs/bfa_defs_pci.h> + +BFA_TRC_FILE(HAL, IOC); + +/** + * IOC local definitions + */ +#define BFA_IOC_TOV 2000 /* msecs */ +#define BFA_IOC_HB_TOV 1000 /* msecs */ +#define BFA_IOC_HB_FAIL_MAX 4 +#define BFA_IOC_HWINIT_MAX 2 +#define BFA_IOC_FWIMG_MINSZ (16 * 1024) +#define BFA_IOC_TOV_RECOVER (BFA_IOC_HB_FAIL_MAX * BFA_IOC_HB_TOV \ + + BFA_IOC_TOV) + +#define bfa_ioc_timer_start(__ioc) \ + bfa_timer_begin((__ioc)->timer_mod, &(__ioc)->ioc_timer, \ + bfa_ioc_timeout, (__ioc), BFA_IOC_TOV) +#define bfa_ioc_timer_stop(__ioc) bfa_timer_stop(&(__ioc)->ioc_timer) + +#define BFA_DBG_FWTRC_ENTS (BFI_IOC_TRC_ENTS) +#define BFA_DBG_FWTRC_LEN \ + (BFA_DBG_FWTRC_ENTS * sizeof(struct bfa_trc_s) + \ + (sizeof(struct bfa_trc_mod_s) - \ + BFA_TRC_MAX * sizeof(struct bfa_trc_s))) +#define BFA_DBG_FWTRC_OFF(_fn) (BFI_IOC_TRC_OFF + BFA_DBG_FWTRC_LEN * (_fn)) +#define bfa_ioc_stats(_ioc, _stats) (_ioc)->stats._stats ++ + +#define BFA_FLASH_CHUNK_NO(off) (off / BFI_FLASH_CHUNK_SZ_WORDS) +#define BFA_FLASH_OFFSET_IN_CHUNK(off) (off % BFI_FLASH_CHUNK_SZ_WORDS) +#define BFA_FLASH_CHUNK_ADDR(chunkno) (chunkno * BFI_FLASH_CHUNK_SZ_WORDS) +bfa_boolean_t bfa_auto_recover = BFA_FALSE; + +/* + * forward declarations + */ +static void bfa_ioc_aen_post(struct bfa_ioc_s *bfa, + enum bfa_ioc_aen_event event); +static void bfa_ioc_hw_sem_get(struct bfa_ioc_s *ioc); +static void bfa_ioc_hw_sem_release(struct bfa_ioc_s *ioc); +static void bfa_ioc_hw_sem_get_cancel(struct bfa_ioc_s *ioc); +static void bfa_ioc_hwinit(struct bfa_ioc_s *ioc, bfa_boolean_t force); +static void bfa_ioc_timeout(void *ioc); +static void bfa_ioc_send_enable(struct bfa_ioc_s *ioc); +static void bfa_ioc_send_disable(struct bfa_ioc_s *ioc); +static void bfa_ioc_send_getattr(struct bfa_ioc_s *ioc); +static void bfa_ioc_hb_monitor(struct bfa_ioc_s *ioc); +static void bfa_ioc_hb_stop(struct bfa_ioc_s *ioc); +static void bfa_ioc_reset(struct bfa_ioc_s *ioc, bfa_boolean_t force); +static void bfa_ioc_mbox_poll(struct bfa_ioc_s *ioc); +static void bfa_ioc_mbox_hbfail(struct bfa_ioc_s *ioc); +static void bfa_ioc_recover(struct bfa_ioc_s *ioc); +static bfa_boolean_t bfa_ioc_firmware_lock(struct bfa_ioc_s *ioc); +static void bfa_ioc_firmware_unlock(struct bfa_ioc_s *ioc); +static void bfa_ioc_disable_comp(struct bfa_ioc_s *ioc); +static void bfa_ioc_lpu_stop(struct bfa_ioc_s *ioc); + +/** + * bfa_ioc_sm + */ + +/** + * IOC state machine events + */ +enum ioc_event { + IOC_E_ENABLE = 1, /* IOC enable request */ + IOC_E_DISABLE = 2, /* IOC disable request */ + IOC_E_TIMEOUT = 3, /* f/w response timeout */ + IOC_E_FWREADY = 4, /* f/w initialization done */ + IOC_E_FWRSP_GETATTR = 5, /* IOC get attribute response */ + IOC_E_FWRSP_ENABLE = 6, /* enable f/w response */ + IOC_E_FWRSP_DISABLE = 7, /* disable f/w response */ + IOC_E_HBFAIL = 8, /* heartbeat failure */ + IOC_E_HWERROR = 9, /* hardware error interrupt */ + IOC_E_SEMLOCKED = 10, /* h/w semaphore is locked */ + IOC_E_DETACH = 11, /* driver detach cleanup */ +}; + +bfa_fsm_state_decl(bfa_ioc, reset, struct bfa_ioc_s, enum ioc_event); +bfa_fsm_state_decl(bfa_ioc, fwcheck, struct bfa_ioc_s, enum ioc_event); +bfa_fsm_state_decl(bfa_ioc, mismatch, struct bfa_ioc_s, enum ioc_event); +bfa_fsm_state_decl(bfa_ioc, semwait, struct bfa_ioc_s, enum ioc_event); +bfa_fsm_state_decl(bfa_ioc, hwinit, struct bfa_ioc_s, enum ioc_event); +bfa_fsm_state_decl(bfa_ioc, enabling, struct bfa_ioc_s, enum ioc_event); +bfa_fsm_state_decl(bfa_ioc, getattr, struct bfa_ioc_s, enum ioc_event); +bfa_fsm_state_decl(bfa_ioc, op, struct bfa_ioc_s, enum ioc_event); +bfa_fsm_state_decl(bfa_ioc, initfail, struct bfa_ioc_s, enum ioc_event); +bfa_fsm_state_decl(bfa_ioc, hbfail, struct bfa_ioc_s, enum ioc_event); +bfa_fsm_state_decl(bfa_ioc, disabling, struct bfa_ioc_s, enum ioc_event); +bfa_fsm_state_decl(bfa_ioc, disabled, struct bfa_ioc_s, enum ioc_event); + +static struct bfa_sm_table_s ioc_sm_table[] = { + {BFA_SM(bfa_ioc_sm_reset), BFA_IOC_RESET}, + {BFA_SM(bfa_ioc_sm_fwcheck), BFA_IOC_FWMISMATCH}, + {BFA_SM(bfa_ioc_sm_mismatch), BFA_IOC_FWMISMATCH}, + {BFA_SM(bfa_ioc_sm_semwait), BFA_IOC_SEMWAIT}, + {BFA_SM(bfa_ioc_sm_hwinit), BFA_IOC_HWINIT}, + {BFA_SM(bfa_ioc_sm_enabling), BFA_IOC_HWINIT}, + {BFA_SM(bfa_ioc_sm_getattr), BFA_IOC_GETATTR}, + {BFA_SM(bfa_ioc_sm_op), BFA_IOC_OPERATIONAL}, + {BFA_SM(bfa_ioc_sm_initfail), BFA_IOC_INITFAIL}, + {BFA_SM(bfa_ioc_sm_hbfail), BFA_IOC_HBFAIL}, + {BFA_SM(bfa_ioc_sm_disabling), BFA_IOC_DISABLING}, + {BFA_SM(bfa_ioc_sm_disabled), BFA_IOC_DISABLED}, +}; + +/** + * Reset entry actions -- initialize state machine + */ +static void +bfa_ioc_sm_reset_entry(struct bfa_ioc_s *ioc) +{ + ioc->retry_count = 0; + ioc->auto_recover = bfa_auto_recover; +} + +/** + * Beginning state. IOC is in reset state. + */ +static void +bfa_ioc_sm_reset(struct bfa_ioc_s *ioc, enum ioc_event event) +{ + bfa_trc(ioc, event); + + switch (event) { + case IOC_E_ENABLE: + bfa_fsm_set_state(ioc, bfa_ioc_sm_fwcheck); + break; + + case IOC_E_DISABLE: + bfa_ioc_disable_comp(ioc); + break; + + case IOC_E_DETACH: + break; + + default: + bfa_sm_fault(ioc, event); + } +} + +/** + * Semaphore should be acquired for version check. + */ +static void +bfa_ioc_sm_fwcheck_entry(struct bfa_ioc_s *ioc) +{ + bfa_ioc_hw_sem_get(ioc); +} + +/** + * Awaiting h/w semaphore to continue with version check. + */ +static void +bfa_ioc_sm_fwcheck(struct bfa_ioc_s *ioc, enum ioc_event event) +{ + bfa_trc(ioc, event); + + switch (event) { + case IOC_E_SEMLOCKED: + if (bfa_ioc_firmware_lock(ioc)) { + ioc->retry_count = 0; + bfa_fsm_set_state(ioc, bfa_ioc_sm_hwinit); + } else { + bfa_ioc_hw_sem_release(ioc); + bfa_fsm_set_state(ioc, bfa_ioc_sm_mismatch); + } + break; + + case IOC_E_DISABLE: + bfa_ioc_disable_comp(ioc); + /* + * fall through + */ + + case IOC_E_DETACH: + bfa_ioc_hw_sem_get_cancel(ioc); + bfa_fsm_set_state(ioc, bfa_ioc_sm_reset); + break; + + case IOC_E_FWREADY: + break; + + default: + bfa_sm_fault(ioc, event); + } +} + +/** + * Notify enable completion callback and generate mismatch AEN. + */ +static void +bfa_ioc_sm_mismatch_entry(struct bfa_ioc_s *ioc) +{ + /** + * Provide enable completion callback and AEN notification only once. + */ + if (ioc->retry_count == 0) { + ioc->cbfn->enable_cbfn(ioc->bfa, BFA_STATUS_IOC_FAILURE); + bfa_ioc_aen_post(ioc, BFA_IOC_AEN_FWMISMATCH); + } + ioc->retry_count++; + bfa_ioc_timer_start(ioc); +} + +/** + * Awaiting firmware version match. + */ +static void +bfa_ioc_sm_mismatch(struct bfa_ioc_s *ioc, enum ioc_event event) +{ + bfa_trc(ioc, event); + + switch (event) { + case IOC_E_TIMEOUT: + bfa_fsm_set_state(ioc, bfa_ioc_sm_fwcheck); + break; + + case IOC_E_DISABLE: + bfa_ioc_disable_comp(ioc); + /* + * fall through + */ + + case IOC_E_DETACH: + bfa_ioc_timer_stop(ioc); + bfa_fsm_set_state(ioc, bfa_ioc_sm_reset); + break; + + case IOC_E_FWREADY: + break; + + default: + bfa_sm_fault(ioc, event); + } +} + +/** + * Request for semaphore. + */ +static void +bfa_ioc_sm_semwait_entry(struct bfa_ioc_s *ioc) +{ + bfa_ioc_hw_sem_get(ioc); +} + +/** + * Awaiting semaphore for h/w initialzation. + */ +static void +bfa_ioc_sm_semwait(struct bfa_ioc_s *ioc, enum ioc_event event) +{ + bfa_trc(ioc, event); + + switch (event) { + case IOC_E_SEMLOCKED: + ioc->retry_count = 0; + bfa_fsm_set_state(ioc, bfa_ioc_sm_hwinit); + break; + + case IOC_E_DISABLE: + bfa_ioc_hw_sem_get_cancel(ioc); + bfa_fsm_set_state(ioc, bfa_ioc_sm_disabled); + break; + + default: + bfa_sm_fault(ioc, event); + } +} + + +static void +bfa_ioc_sm_hwinit_entry(struct bfa_ioc_s *ioc) +{ + bfa_ioc_timer_start(ioc); + bfa_ioc_reset(ioc, BFA_FALSE); +} + +/** + * Hardware is being initialized. Interrupts are enabled. + * Holding hardware semaphore lock. + */ +static void +bfa_ioc_sm_hwinit(struct bfa_ioc_s *ioc, enum ioc_event event) +{ + bfa_trc(ioc, event); + + switch (event) { + case IOC_E_FWREADY: + bfa_ioc_timer_stop(ioc); + bfa_fsm_set_state(ioc, bfa_ioc_sm_enabling); + break; + + case IOC_E_HWERROR: + bfa_ioc_timer_stop(ioc); + /* + * fall through + */ + + case IOC_E_TIMEOUT: + ioc->retry_count++; + if (ioc->retry_count < BFA_IOC_HWINIT_MAX) { + bfa_ioc_timer_start(ioc); + bfa_ioc_reset(ioc, BFA_TRUE); + break; + } + + bfa_ioc_hw_sem_release(ioc); + bfa_fsm_set_state(ioc, bfa_ioc_sm_initfail); + break; + + case IOC_E_DISABLE: + bfa_ioc_hw_sem_release(ioc); + bfa_ioc_timer_stop(ioc); + bfa_fsm_set_state(ioc, bfa_ioc_sm_disabled); + break; + + default: + bfa_sm_fault(ioc, event); + } +} + + +static void +bfa_ioc_sm_enabling_entry(struct bfa_ioc_s *ioc) +{ + bfa_ioc_timer_start(ioc); + bfa_ioc_send_enable(ioc); +} + +/** + * Host IOC function is being enabled, awaiting response from firmware. + * Semaphore is acquired. + */ +static void +bfa_ioc_sm_enabling(struct bfa_ioc_s *ioc, enum ioc_event event) +{ + bfa_trc(ioc, event); + + switch (event) { + case IOC_E_FWRSP_ENABLE: + bfa_ioc_timer_stop(ioc); + bfa_ioc_hw_sem_release(ioc); + bfa_fsm_set_state(ioc, bfa_ioc_sm_getattr); + break; + + case IOC_E_HWERROR: + bfa_ioc_timer_stop(ioc); + /* + * fall through + */ + + case IOC_E_TIMEOUT: + ioc->retry_count++; + if (ioc->retry_count < BFA_IOC_HWINIT_MAX) { + bfa_reg_write(ioc->ioc_regs.ioc_fwstate, + BFI_IOC_UNINIT); + bfa_fsm_set_state(ioc, bfa_ioc_sm_hwinit); + break; + } + + bfa_ioc_hw_sem_release(ioc); + bfa_fsm_set_state(ioc, bfa_ioc_sm_initfail); + break; + + case IOC_E_DISABLE: + bfa_ioc_timer_stop(ioc); + bfa_ioc_hw_sem_release(ioc); + bfa_fsm_set_state(ioc, bfa_ioc_sm_disabled); + break; + + case IOC_E_FWREADY: + bfa_ioc_send_enable(ioc); + break; + + default: + bfa_sm_fault(ioc, event); + } +} + + +static void +bfa_ioc_sm_getattr_entry(struct bfa_ioc_s *ioc) +{ + bfa_ioc_timer_start(ioc); + bfa_ioc_send_getattr(ioc); +} + +/** + * IOC configuration in progress. Timer is active. + */ +static void +bfa_ioc_sm_getattr(struct bfa_ioc_s *ioc, enum ioc_event event) +{ + bfa_trc(ioc, event); + + switch (event) { + case IOC_E_FWRSP_GETATTR: + bfa_ioc_timer_stop(ioc); + bfa_fsm_set_state(ioc, bfa_ioc_sm_op); + break; + + case IOC_E_HWERROR: + bfa_ioc_timer_stop(ioc); + /* + * fall through + */ + + case IOC_E_TIMEOUT: + bfa_fsm_set_state(ioc, bfa_ioc_sm_initfail); + break; + + case IOC_E_DISABLE: + bfa_ioc_timer_stop(ioc); + bfa_fsm_set_state(ioc, bfa_ioc_sm_disabled); + break; + + default: + bfa_sm_fault(ioc, event); + } +} + + +static void +bfa_ioc_sm_op_entry(struct bfa_ioc_s *ioc) +{ + ioc->cbfn->enable_cbfn(ioc->bfa, BFA_STATUS_OK); + bfa_ioc_hb_monitor(ioc); + bfa_ioc_aen_post(ioc, BFA_IOC_AEN_ENABLE); +} + +static void +bfa_ioc_sm_op(struct bfa_ioc_s *ioc, enum ioc_event event) +{ + bfa_trc(ioc, event); + + switch (event) { + case IOC_E_ENABLE: + break; + + case IOC_E_DISABLE: + bfa_ioc_hb_stop(ioc); + bfa_fsm_set_state(ioc, bfa_ioc_sm_disabling); + break; + + case IOC_E_HWERROR: + case IOC_E_FWREADY: + /** + * Hard error or IOC recovery by other function. + * Treat it same as heartbeat failure. + */ + bfa_ioc_hb_stop(ioc); + /* + * !!! fall through !!! + */ + + case IOC_E_HBFAIL: + bfa_fsm_set_state(ioc, bfa_ioc_sm_hbfail); + break; + + default: + bfa_sm_fault(ioc, event); + } +} + + +static void +bfa_ioc_sm_disabling_entry(struct bfa_ioc_s *ioc) +{ + bfa_ioc_aen_post(ioc, BFA_IOC_AEN_DISABLE); + bfa_ioc_timer_start(ioc); + bfa_ioc_send_disable(ioc); +} + +/** + * IOC is being disabled + */ +static void +bfa_ioc_sm_disabling(struct bfa_ioc_s *ioc, enum ioc_event event) +{ + bfa_trc(ioc, event); + + switch (event) { + case IOC_E_HWERROR: + case IOC_E_FWRSP_DISABLE: + bfa_ioc_timer_stop(ioc); + /* + * !!! fall through !!! + */ + + case IOC_E_TIMEOUT: + bfa_fsm_set_state(ioc, bfa_ioc_sm_disabled); + break; + + default: + bfa_sm_fault(ioc, event); + } +} + +/** + * IOC disable completion entry. + */ +static void +bfa_ioc_sm_disabled_entry(struct bfa_ioc_s *ioc) +{ + bfa_ioc_disable_comp(ioc); +} + +static void +bfa_ioc_sm_disabled(struct bfa_ioc_s *ioc, enum ioc_event event) +{ + bfa_trc(ioc, event); + + switch (event) { + case IOC_E_ENABLE: + bfa_fsm_set_state(ioc, bfa_ioc_sm_semwait); + break; + + case IOC_E_DISABLE: + ioc->cbfn->disable_cbfn(ioc->bfa); + break; + + case IOC_E_FWREADY: + break; + + case IOC_E_DETACH: + bfa_ioc_firmware_unlock(ioc); + bfa_fsm_set_state(ioc, bfa_ioc_sm_reset); + break; + + default: + bfa_sm_fault(ioc, event); + } +} + + +static void +bfa_ioc_sm_initfail_entry(struct bfa_ioc_s *ioc) +{ + ioc->cbfn->enable_cbfn(ioc->bfa, BFA_STATUS_IOC_FAILURE); + bfa_ioc_timer_start(ioc); +} + +/** + * Hardware initialization failed. + */ +static void +bfa_ioc_sm_initfail(struct bfa_ioc_s *ioc, enum ioc_event event) +{ + bfa_trc(ioc, event); + + switch (event) { + case IOC_E_DISABLE: + bfa_ioc_timer_stop(ioc); + bfa_fsm_set_state(ioc, bfa_ioc_sm_disabled); + break; + + case IOC_E_DETACH: + bfa_ioc_timer_stop(ioc); + bfa_ioc_firmware_unlock(ioc); + bfa_fsm_set_state(ioc, bfa_ioc_sm_reset); + break; + + case IOC_E_TIMEOUT: + bfa_fsm_set_state(ioc, bfa_ioc_sm_semwait); + break; + + default: + bfa_sm_fault(ioc, event); + } +} + + +static void +bfa_ioc_sm_hbfail_entry(struct bfa_ioc_s *ioc) +{ + struct list_head *qe; + struct bfa_ioc_hbfail_notify_s *notify; + + /** + * Mark IOC as failed in hardware and stop firmware. + */ + bfa_ioc_lpu_stop(ioc); + bfa_reg_write(ioc->ioc_regs.ioc_fwstate, BFI_IOC_HBFAIL); + + if (ioc->pcidev.device_id == BFA_PCI_DEVICE_ID_CT) { + bfa_reg_write(ioc->ioc_regs.ll_halt, __FW_INIT_HALT_P); + /* + * Wait for halt to take effect + */ + bfa_reg_read(ioc->ioc_regs.ll_halt); + } + + /** + * Notify driver and common modules registered for notification. + */ + ioc->cbfn->hbfail_cbfn(ioc->bfa); + list_for_each(qe, &ioc->hb_notify_q) { + notify = (struct bfa_ioc_hbfail_notify_s *)qe; + notify->cbfn(notify->cbarg); + } + + /** + * Flush any queued up mailbox requests. + */ + bfa_ioc_mbox_hbfail(ioc); + bfa_ioc_aen_post(ioc, BFA_IOC_AEN_HBFAIL); + + /** + * Trigger auto-recovery after a delay. + */ + if (ioc->auto_recover) { + bfa_timer_begin(ioc->timer_mod, &ioc->ioc_timer, + bfa_ioc_timeout, ioc, BFA_IOC_TOV_RECOVER); + } +} + +/** + * IOC heartbeat failure. + */ +static void +bfa_ioc_sm_hbfail(struct bfa_ioc_s *ioc, enum ioc_event event) +{ + bfa_trc(ioc, event); + + switch (event) { + + case IOC_E_ENABLE: + ioc->cbfn->enable_cbfn(ioc->bfa, BFA_STATUS_IOC_FAILURE); + break; + + case IOC_E_DISABLE: + if (ioc->auto_recover) + bfa_ioc_timer_stop(ioc); + bfa_fsm_set_state(ioc, bfa_ioc_sm_disabled); + break; + + case IOC_E_TIMEOUT: + bfa_fsm_set_state(ioc, bfa_ioc_sm_semwait); + break; + + case IOC_E_FWREADY: + /** + * Recovery is already initiated by other function. + */ + break; + + default: + bfa_sm_fault(ioc, event); + } +} + + + +/** + * bfa_ioc_pvt BFA IOC private functions + */ + +static void +bfa_ioc_disable_comp(struct bfa_ioc_s *ioc) +{ + struct list_head *qe; + struct bfa_ioc_hbfail_notify_s *notify; + + ioc->cbfn->disable_cbfn(ioc->bfa); + + /** + * Notify common modules registered for notification. + */ + list_for_each(qe, &ioc->hb_notify_q) { + notify = (struct bfa_ioc_hbfail_notify_s *)qe; + notify->cbfn(notify->cbarg); + } +} + +static void +bfa_ioc_sem_timeout(void *ioc_arg) +{ + struct bfa_ioc_s *ioc = (struct bfa_ioc_s *)ioc_arg; + + bfa_ioc_hw_sem_get(ioc); +} + +static void +bfa_ioc_usage_sem_get(struct bfa_ioc_s *ioc) +{ + u32 r32; + int cnt = 0; +#define BFA_SEM_SPINCNT 1000 + + do { + r32 = bfa_reg_read(ioc->ioc_regs.ioc_usage_sem_reg); + cnt++; + if (cnt > BFA_SEM_SPINCNT) + break; + } while (r32 != 0); + bfa_assert(cnt < BFA_SEM_SPINCNT); +} + +static void +bfa_ioc_usage_sem_release(struct bfa_ioc_s *ioc) +{ + bfa_reg_write(ioc->ioc_regs.ioc_usage_sem_reg, 1); +} + +static void +bfa_ioc_hw_sem_get(struct bfa_ioc_s *ioc) +{ + u32 r32; + + /** + * First read to the semaphore register will return 0, subsequent reads + * will return 1. Semaphore is released by writing 0 to the register + */ + r32 = bfa_reg_read(ioc->ioc_regs.ioc_sem_reg); + if (r32 == 0) { + bfa_fsm_send_event(ioc, IOC_E_SEMLOCKED); + return; + } + + bfa_timer_begin(ioc->timer_mod, &ioc->sem_timer, bfa_ioc_sem_timeout, + ioc, BFA_IOC_TOV); +} + +static void +bfa_ioc_hw_sem_release(struct bfa_ioc_s *ioc) +{ + bfa_reg_write(ioc->ioc_regs.ioc_sem_reg, 1); +} + +static void +bfa_ioc_hw_sem_get_cancel(struct bfa_ioc_s *ioc) +{ + bfa_timer_stop(&ioc->sem_timer); +} + +/** + * Initialize LPU local memory (aka secondary memory / SRAM) + */ +static void +bfa_ioc_lmem_init(struct bfa_ioc_s *ioc) +{ + u32 pss_ctl; + int i; +#define PSS_LMEM_INIT_TIME 10000 + + pss_ctl = bfa_reg_read(ioc->ioc_regs.pss_ctl_reg); + pss_ctl &= ~__PSS_LMEM_RESET; + pss_ctl |= __PSS_LMEM_INIT_EN; + pss_ctl |= __PSS_I2C_CLK_DIV(3UL); /* i2c workaround 12.5khz clock */ + bfa_reg_write(ioc->ioc_regs.pss_ctl_reg, pss_ctl); + + /** + * wait for memory initialization to be complete + */ + i = 0; + do { + pss_ctl = bfa_reg_read(ioc->ioc_regs.pss_ctl_reg); + i++; + } while (!(pss_ctl & __PSS_LMEM_INIT_DONE) && (i < PSS_LMEM_INIT_TIME)); + + /** + * If memory initialization is not successful, IOC timeout will catch + * such failures. + */ + bfa_assert(pss_ctl & __PSS_LMEM_INIT_DONE); + bfa_trc(ioc, pss_ctl); + + pss_ctl &= ~(__PSS_LMEM_INIT_DONE | __PSS_LMEM_INIT_EN); + bfa_reg_write(ioc->ioc_regs.pss_ctl_reg, pss_ctl); +} + +static void +bfa_ioc_lpu_start(struct bfa_ioc_s *ioc) +{ + u32 pss_ctl; + + /** + * Take processor out of reset. + */ + pss_ctl = bfa_reg_read(ioc->ioc_regs.pss_ctl_reg); + pss_ctl &= ~__PSS_LPU0_RESET; + + bfa_reg_write(ioc->ioc_regs.pss_ctl_reg, pss_ctl); +} + +static void +bfa_ioc_lpu_stop(struct bfa_ioc_s *ioc) +{ + u32 pss_ctl; + + /** + * Put processors in reset. + */ + pss_ctl = bfa_reg_read(ioc->ioc_regs.pss_ctl_reg); + pss_ctl |= (__PSS_LPU0_RESET | __PSS_LPU1_RESET); + + bfa_reg_write(ioc->ioc_regs.pss_ctl_reg, pss_ctl); +} + +/** + * Get driver and firmware versions. + */ +static void +bfa_ioc_fwver_get(struct bfa_ioc_s *ioc, struct bfi_ioc_image_hdr_s *fwhdr) +{ + u32 pgnum, pgoff; + u32 loff = 0; + int i; + u32 *fwsig = (u32 *) fwhdr; + + pgnum = bfa_ioc_smem_pgnum(ioc, loff); + pgoff = bfa_ioc_smem_pgoff(ioc, loff); + bfa_reg_write(ioc->ioc_regs.host_page_num_fn, pgnum); + + for (i = 0; i < (sizeof(struct bfi_ioc_image_hdr_s) / sizeof(u32)); + i++) { + fwsig[i] = bfa_mem_read(ioc->ioc_regs.smem_page_start, loff); + loff += sizeof(u32); + } +} + +static u32 * +bfa_ioc_fwimg_get_chunk(struct bfa_ioc_s *ioc, u32 off) +{ + if (ioc->ctdev) + return bfi_image_ct_get_chunk(off); + return bfi_image_cb_get_chunk(off); +} + +static u32 +bfa_ioc_fwimg_get_size(struct bfa_ioc_s *ioc) +{ +return (ioc->ctdev) ? bfi_image_ct_size : bfi_image_cb_size; +} + +/** + * Returns TRUE if same. + */ +static bfa_boolean_t +bfa_ioc_fwver_cmp(struct bfa_ioc_s *ioc, struct bfi_ioc_image_hdr_s *fwhdr) +{ + struct bfi_ioc_image_hdr_s *drv_fwhdr; + int i; + + drv_fwhdr = + (struct bfi_ioc_image_hdr_s *)bfa_ioc_fwimg_get_chunk(ioc, 0); + + for (i = 0; i < BFI_IOC_MD5SUM_SZ; i++) { + if (fwhdr->md5sum[i] != drv_fwhdr->md5sum[i]) { + bfa_trc(ioc, i); + bfa_trc(ioc, fwhdr->md5sum[i]); + bfa_trc(ioc, drv_fwhdr->md5sum[i]); + return BFA_FALSE; + } + } + + bfa_trc(ioc, fwhdr->md5sum[0]); + return BFA_TRUE; +} + +/** + * Return true if current running version is valid. Firmware signature and + * execution context (driver/bios) must match. + */ +static bfa_boolean_t +bfa_ioc_fwver_valid(struct bfa_ioc_s *ioc) +{ + struct bfi_ioc_image_hdr_s fwhdr, *drv_fwhdr; + + /** + * If bios/efi boot (flash based) -- return true + */ + if (bfa_ioc_fwimg_get_size(ioc) < BFA_IOC_FWIMG_MINSZ) + return BFA_TRUE; + + bfa_ioc_fwver_get(ioc, &fwhdr); + drv_fwhdr = + (struct bfi_ioc_image_hdr_s *)bfa_ioc_fwimg_get_chunk(ioc, 0); + + if (fwhdr.signature != drv_fwhdr->signature) { + bfa_trc(ioc, fwhdr.signature); + bfa_trc(ioc, drv_fwhdr->signature); + return BFA_FALSE; + } + + if (fwhdr.exec != drv_fwhdr->exec) { + bfa_trc(ioc, fwhdr.exec); + bfa_trc(ioc, drv_fwhdr->exec); + return BFA_FALSE; + } + + return bfa_ioc_fwver_cmp(ioc, &fwhdr); +} + +/** + * Return true if firmware of current driver matches the running firmware. + */ +static bfa_boolean_t +bfa_ioc_firmware_lock(struct bfa_ioc_s *ioc) +{ + enum bfi_ioc_state ioc_fwstate; + u32 usecnt; + struct bfi_ioc_image_hdr_s fwhdr; + + /** + * Firmware match check is relevant only for CNA. + */ + if (!ioc->cna) + return BFA_TRUE; + + /** + * If bios boot (flash based) -- do not increment usage count + */ + if (bfa_ioc_fwimg_get_size(ioc) < BFA_IOC_FWIMG_MINSZ) + return BFA_TRUE; + + bfa_ioc_usage_sem_get(ioc); + usecnt = bfa_reg_read(ioc->ioc_regs.ioc_usage_reg); + + /** + * If usage count is 0, always return TRUE. + */ + if (usecnt == 0) { + bfa_reg_write(ioc->ioc_regs.ioc_usage_reg, 1); + bfa_ioc_usage_sem_release(ioc); + bfa_trc(ioc, usecnt); + return BFA_TRUE; + } + + ioc_fwstate = bfa_reg_read(ioc->ioc_regs.ioc_fwstate); + bfa_trc(ioc, ioc_fwstate); + + /** + * Use count cannot be non-zero and chip in uninitialized state. + */ + bfa_assert(ioc_fwstate != BFI_IOC_UNINIT); + + /** + * Check if another driver with a different firmware is active + */ + bfa_ioc_fwver_get(ioc, &fwhdr); + if (!bfa_ioc_fwver_cmp(ioc, &fwhdr)) { + bfa_ioc_usage_sem_release(ioc); + bfa_trc(ioc, usecnt); + return BFA_FALSE; + } + + /** + * Same firmware version. Increment the reference count. + */ + usecnt++; + bfa_reg_write(ioc->ioc_regs.ioc_usage_reg, usecnt); + bfa_ioc_usage_sem_release(ioc); + bfa_trc(ioc, usecnt); + return BFA_TRUE; +} + +static void +bfa_ioc_firmware_unlock(struct bfa_ioc_s *ioc) +{ + u32 usecnt; + + /** + * Firmware lock is relevant only for CNA. + * If bios boot (flash based) -- do not decrement usage count + */ + if (!ioc->cna || (bfa_ioc_fwimg_get_size(ioc) < BFA_IOC_FWIMG_MINSZ)) + return; + + /** + * decrement usage count + */ + bfa_ioc_usage_sem_get(ioc); + usecnt = bfa_reg_read(ioc->ioc_regs.ioc_usage_reg); + bfa_assert(usecnt > 0); + + usecnt--; + bfa_reg_write(ioc->ioc_regs.ioc_usage_reg, usecnt); + bfa_trc(ioc, usecnt); + + bfa_ioc_usage_sem_release(ioc); +} + +/** + * Conditionally flush any pending message from firmware at start. + */ +static void +bfa_ioc_msgflush(struct bfa_ioc_s *ioc) +{ + u32 r32; + + r32 = bfa_reg_read(ioc->ioc_regs.lpu_mbox_cmd); + if (r32) + bfa_reg_write(ioc->ioc_regs.lpu_mbox_cmd, 1); +} + + +static void +bfa_ioc_hwinit(struct bfa_ioc_s *ioc, bfa_boolean_t force) +{ + enum bfi_ioc_state ioc_fwstate; + bfa_boolean_t fwvalid; + + ioc_fwstate = bfa_reg_read(ioc->ioc_regs.ioc_fwstate); + + if (force) + ioc_fwstate = BFI_IOC_UNINIT; + + bfa_trc(ioc, ioc_fwstate); + + /** + * check if firmware is valid + */ + fwvalid = (ioc_fwstate == BFI_IOC_UNINIT) ? + BFA_FALSE : bfa_ioc_fwver_valid(ioc); + + if (!fwvalid) { + bfa_ioc_boot(ioc, BFI_BOOT_TYPE_NORMAL, ioc->pcidev.device_id); + return; + } + + /** + * If hardware initialization is in progress (initialized by other IOC), + * just wait for an initialization completion interrupt. + */ + if (ioc_fwstate == BFI_IOC_INITING) { + bfa_trc(ioc, ioc_fwstate); + ioc->cbfn->reset_cbfn(ioc->bfa); + return; + } + + /** + * If IOC function is disabled and firmware version is same, + * just re-enable IOC. + */ + if (ioc_fwstate == BFI_IOC_DISABLED || ioc_fwstate == BFI_IOC_OP) { + bfa_trc(ioc, ioc_fwstate); + + /** + * When using MSI-X any pending firmware ready event should + * be flushed. Otherwise MSI-X interrupts are not delivered. + */ + bfa_ioc_msgflush(ioc); + ioc->cbfn->reset_cbfn(ioc->bfa); + bfa_fsm_send_event(ioc, IOC_E_FWREADY); + return; + } + + /** + * Initialize the h/w for any other states. + */ + bfa_ioc_boot(ioc, BFI_BOOT_TYPE_NORMAL, ioc->pcidev.device_id); +} + +static void +bfa_ioc_timeout(void *ioc_arg) +{ + struct bfa_ioc_s *ioc = (struct bfa_ioc_s *)ioc_arg; + + bfa_trc(ioc, 0); + bfa_fsm_send_event(ioc, IOC_E_TIMEOUT); +} + +void +bfa_ioc_mbox_send(struct bfa_ioc_s *ioc, void *ioc_msg, int len) +{ + u32 *msgp = (u32 *) ioc_msg; + u32 i; + + bfa_trc(ioc, msgp[0]); + bfa_trc(ioc, len); + + bfa_assert(len <= BFI_IOC_MSGLEN_MAX); + + /* + * first write msg to mailbox registers + */ + for (i = 0; i < len / sizeof(u32); i++) + bfa_reg_write(ioc->ioc_regs.hfn_mbox + i * sizeof(u32), + bfa_os_wtole(msgp[i])); + + for (; i < BFI_IOC_MSGLEN_MAX / sizeof(u32); i++) + bfa_reg_write(ioc->ioc_regs.hfn_mbox + i * sizeof(u32), 0); + + /* + * write 1 to mailbox CMD to trigger LPU event + */ + bfa_reg_write(ioc->ioc_regs.hfn_mbox_cmd, 1); + (void)bfa_reg_read(ioc->ioc_regs.hfn_mbox_cmd); +} + +static void +bfa_ioc_send_enable(struct bfa_ioc_s *ioc) +{ + struct bfi_ioc_ctrl_req_s enable_req; + + bfi_h2i_set(enable_req.mh, BFI_MC_IOC, BFI_IOC_H2I_ENABLE_REQ, + bfa_ioc_portid(ioc)); + enable_req.ioc_class = ioc->ioc_mc; + bfa_ioc_mbox_send(ioc, &enable_req, sizeof(struct bfi_ioc_ctrl_req_s)); +} + +static void +bfa_ioc_send_disable(struct bfa_ioc_s *ioc) +{ + struct bfi_ioc_ctrl_req_s disable_req; + + bfi_h2i_set(disable_req.mh, BFI_MC_IOC, BFI_IOC_H2I_DISABLE_REQ, + bfa_ioc_portid(ioc)); + bfa_ioc_mbox_send(ioc, &disable_req, sizeof(struct bfi_ioc_ctrl_req_s)); +} + +static void +bfa_ioc_send_getattr(struct bfa_ioc_s *ioc) +{ + struct bfi_ioc_getattr_req_s attr_req; + + bfi_h2i_set(attr_req.mh, BFI_MC_IOC, BFI_IOC_H2I_GETATTR_REQ, + bfa_ioc_portid(ioc)); + bfa_dma_be_addr_set(attr_req.attr_addr, ioc->attr_dma.pa); + bfa_ioc_mbox_send(ioc, &attr_req, sizeof(attr_req)); +} + +static void +bfa_ioc_hb_check(void *cbarg) +{ + struct bfa_ioc_s *ioc = cbarg; + u32 hb_count; + + hb_count = bfa_reg_read(ioc->ioc_regs.heartbeat); + if (ioc->hb_count == hb_count) { + ioc->hb_fail++; + } else { + ioc->hb_count = hb_count; + ioc->hb_fail = 0; + } + + if (ioc->hb_fail >= BFA_IOC_HB_FAIL_MAX) { + bfa_log(ioc->logm, BFA_LOG_HAL_HEARTBEAT_FAILURE, hb_count); + ioc->hb_fail = 0; + bfa_ioc_recover(ioc); + return; + } + + bfa_ioc_mbox_poll(ioc); + bfa_timer_begin(ioc->timer_mod, &ioc->ioc_timer, bfa_ioc_hb_check, ioc, + BFA_IOC_HB_TOV); +} + +static void +bfa_ioc_hb_monitor(struct bfa_ioc_s *ioc) +{ + ioc->hb_fail = 0; + ioc->hb_count = bfa_reg_read(ioc->ioc_regs.heartbeat); + bfa_timer_begin(ioc->timer_mod, &ioc->ioc_timer, bfa_ioc_hb_check, ioc, + BFA_IOC_HB_TOV); +} + +static void +bfa_ioc_hb_stop(struct bfa_ioc_s *ioc) +{ + bfa_timer_stop(&ioc->ioc_timer); +} + +/** + * Host to LPU mailbox message addresses + */ +static struct { + u32 hfn_mbox, lpu_mbox, hfn_pgn; +} iocreg_fnreg[] = { + { + HOSTFN0_LPU_MBOX0_0, LPU_HOSTFN0_MBOX0_0, HOST_PAGE_NUM_FN0}, { + HOSTFN1_LPU_MBOX0_8, LPU_HOSTFN1_MBOX0_8, HOST_PAGE_NUM_FN1}, { + HOSTFN2_LPU_MBOX0_0, LPU_HOSTFN2_MBOX0_0, HOST_PAGE_NUM_FN2}, { + HOSTFN3_LPU_MBOX0_8, LPU_HOSTFN3_MBOX0_8, HOST_PAGE_NUM_FN3} +}; + +/** + * Host <-> LPU mailbox command/status registers - port 0 + */ +static struct { + u32 hfn, lpu; +} iocreg_mbcmd_p0[] = { + { + HOSTFN0_LPU0_MBOX0_CMD_STAT, LPU0_HOSTFN0_MBOX0_CMD_STAT}, { + HOSTFN1_LPU0_MBOX0_CMD_STAT, LPU0_HOSTFN1_MBOX0_CMD_STAT}, { + HOSTFN2_LPU0_MBOX0_CMD_STAT, LPU0_HOSTFN2_MBOX0_CMD_STAT}, { + HOSTFN3_LPU0_MBOX0_CMD_STAT, LPU0_HOSTFN3_MBOX0_CMD_STAT} +}; + +/** + * Host <-> LPU mailbox command/status registers - port 1 + */ +static struct { + u32 hfn, lpu; +} iocreg_mbcmd_p1[] = { + { + HOSTFN0_LPU1_MBOX0_CMD_STAT, LPU1_HOSTFN0_MBOX0_CMD_STAT}, { + HOSTFN1_LPU1_MBOX0_CMD_STAT, LPU1_HOSTFN1_MBOX0_CMD_STAT}, { + HOSTFN2_LPU1_MBOX0_CMD_STAT, LPU1_HOSTFN2_MBOX0_CMD_STAT}, { + HOSTFN3_LPU1_MBOX0_CMD_STAT, LPU1_HOSTFN3_MBOX0_CMD_STAT} +}; + +/** + * Shared IRQ handling in INTX mode + */ +static struct { + u32 isr, msk; +} iocreg_shirq_next[] = { + { + HOSTFN1_INT_STATUS, HOSTFN1_INT_MSK}, { + HOSTFN2_INT_STATUS, HOSTFN2_INT_MSK}, { + HOSTFN3_INT_STATUS, HOSTFN3_INT_MSK}, { +HOSTFN0_INT_STATUS, HOSTFN0_INT_MSK},}; + +static void +bfa_ioc_reg_init(struct bfa_ioc_s *ioc) +{ + bfa_os_addr_t rb; + int pcifn = bfa_ioc_pcifn(ioc); + + rb = bfa_ioc_bar0(ioc); + + ioc->ioc_regs.hfn_mbox = rb + iocreg_fnreg[pcifn].hfn_mbox; + ioc->ioc_regs.lpu_mbox = rb + iocreg_fnreg[pcifn].lpu_mbox; + ioc->ioc_regs.host_page_num_fn = rb + iocreg_fnreg[pcifn].hfn_pgn; + + if (ioc->port_id == 0) { + ioc->ioc_regs.heartbeat = rb + BFA_IOC0_HBEAT_REG; + ioc->ioc_regs.ioc_fwstate = rb + BFA_IOC0_STATE_REG; + ioc->ioc_regs.hfn_mbox_cmd = rb + iocreg_mbcmd_p0[pcifn].hfn; + ioc->ioc_regs.lpu_mbox_cmd = rb + iocreg_mbcmd_p0[pcifn].lpu; + ioc->ioc_regs.ll_halt = rb + FW_INIT_HALT_P0; + } else { + ioc->ioc_regs.heartbeat = (rb + BFA_IOC1_HBEAT_REG); + ioc->ioc_regs.ioc_fwstate = (rb + BFA_IOC1_STATE_REG); + ioc->ioc_regs.hfn_mbox_cmd = rb + iocreg_mbcmd_p1[pcifn].hfn; + ioc->ioc_regs.lpu_mbox_cmd = rb + iocreg_mbcmd_p1[pcifn].lpu; + ioc->ioc_regs.ll_halt = rb + FW_INIT_HALT_P1; + } + + /** + * Shared IRQ handling in INTX mode + */ + ioc->ioc_regs.shirq_isr_next = rb + iocreg_shirq_next[pcifn].isr; + ioc->ioc_regs.shirq_msk_next = rb + iocreg_shirq_next[pcifn].msk; + + /* + * PSS control registers + */ + ioc->ioc_regs.pss_ctl_reg = (rb + PSS_CTL_REG); + ioc->ioc_regs.app_pll_fast_ctl_reg = (rb + APP_PLL_425_CTL_REG); + ioc->ioc_regs.app_pll_slow_ctl_reg = (rb + APP_PLL_312_CTL_REG); + + /* + * IOC semaphore registers and serialization + */ + ioc->ioc_regs.ioc_sem_reg = (rb + HOST_SEM0_REG); + ioc->ioc_regs.ioc_usage_sem_reg = (rb + HOST_SEM1_REG); + ioc->ioc_regs.ioc_usage_reg = (rb + BFA_FW_USE_COUNT); + + /** + * sram memory access + */ + ioc->ioc_regs.smem_page_start = (rb + PSS_SMEM_PAGE_START); + ioc->ioc_regs.smem_pg0 = BFI_IOC_SMEM_PG0_CB; + if (ioc->pcidev.device_id == BFA_PCI_DEVICE_ID_CT) + ioc->ioc_regs.smem_pg0 = BFI_IOC_SMEM_PG0_CT; +} + +/** + * Initiate a full firmware download. + */ +static void +bfa_ioc_download_fw(struct bfa_ioc_s *ioc, u32 boot_type, + u32 boot_param) +{ + u32 *fwimg; + u32 pgnum, pgoff; + u32 loff = 0; + u32 chunkno = 0; + u32 i; + + /** + * Initialize LMEM first before code download + */ + bfa_ioc_lmem_init(ioc); + + /** + * Flash based firmware boot + */ + bfa_trc(ioc, bfa_ioc_fwimg_get_size(ioc)); + if (bfa_ioc_fwimg_get_size(ioc) < BFA_IOC_FWIMG_MINSZ) + boot_type = BFI_BOOT_TYPE_FLASH; + fwimg = bfa_ioc_fwimg_get_chunk(ioc, chunkno); + fwimg[BFI_BOOT_TYPE_OFF / sizeof(u32)] = bfa_os_swap32(boot_type); + fwimg[BFI_BOOT_PARAM_OFF / sizeof(u32)] = + bfa_os_swap32(boot_param); + + pgnum = bfa_ioc_smem_pgnum(ioc, loff); + pgoff = bfa_ioc_smem_pgoff(ioc, loff); + + bfa_reg_write(ioc->ioc_regs.host_page_num_fn, pgnum); + + for (i = 0; i < bfa_ioc_fwimg_get_size(ioc); i++) { + + if (BFA_FLASH_CHUNK_NO(i) != chunkno) { + chunkno = BFA_FLASH_CHUNK_NO(i); + fwimg = bfa_ioc_fwimg_get_chunk(ioc, + BFA_FLASH_CHUNK_ADDR(chunkno)); + } + + /** + * write smem + */ + bfa_mem_write(ioc->ioc_regs.smem_page_start, loff, + fwimg[BFA_FLASH_OFFSET_IN_CHUNK(i)]); + + loff += sizeof(u32); + + /** + * handle page offset wrap around + */ + loff = PSS_SMEM_PGOFF(loff); + if (loff == 0) { + pgnum++; + bfa_reg_write(ioc->ioc_regs.host_page_num_fn, pgnum); + } + } + + bfa_reg_write(ioc->ioc_regs.host_page_num_fn, + bfa_ioc_smem_pgnum(ioc, 0)); +} + +static void +bfa_ioc_reset(struct bfa_ioc_s *ioc, bfa_boolean_t force) +{ + bfa_ioc_hwinit(ioc, force); +} + +/** + * Update BFA configuration from firmware configuration. + */ +static void +bfa_ioc_getattr_reply(struct bfa_ioc_s *ioc) +{ + struct bfi_ioc_attr_s *attr = ioc->attr; + + attr->adapter_prop = bfa_os_ntohl(attr->adapter_prop); + attr->maxfrsize = bfa_os_ntohs(attr->maxfrsize); + + bfa_fsm_send_event(ioc, IOC_E_FWRSP_GETATTR); +} + +/** + * Attach time initialization of mbox logic. + */ +static void +bfa_ioc_mbox_attach(struct bfa_ioc_s *ioc) +{ + struct bfa_ioc_mbox_mod_s *mod = &ioc->mbox_mod; + int mc; + + INIT_LIST_HEAD(&mod->cmd_q); + for (mc = 0; mc < BFI_MC_MAX; mc++) { + mod->mbhdlr[mc].cbfn = NULL; + mod->mbhdlr[mc].cbarg = ioc->bfa; + } +} + +/** + * Mbox poll timer -- restarts any pending mailbox requests. + */ +static void +bfa_ioc_mbox_poll(struct bfa_ioc_s *ioc) +{ + struct bfa_ioc_mbox_mod_s *mod = &ioc->mbox_mod; + struct bfa_mbox_cmd_s *cmd; + u32 stat; + + /** + * If no command pending, do nothing + */ + if (list_empty(&mod->cmd_q)) + return; + + /** + * If previous command is not yet fetched by firmware, do nothing + */ + stat = bfa_reg_read(ioc->ioc_regs.hfn_mbox_cmd); + if (stat) + return; + + /** + * Enqueue command to firmware. + */ + bfa_q_deq(&mod->cmd_q, &cmd); + bfa_ioc_mbox_send(ioc, cmd->msg, sizeof(cmd->msg)); +} + +/** + * Cleanup any pending requests. + */ +static void +bfa_ioc_mbox_hbfail(struct bfa_ioc_s *ioc) +{ + struct bfa_ioc_mbox_mod_s *mod = &ioc->mbox_mod; + struct bfa_mbox_cmd_s *cmd; + + while (!list_empty(&mod->cmd_q)) + bfa_q_deq(&mod->cmd_q, &cmd); +} + +/** + * Initialize IOC to port mapping. + */ + +#define FNC_PERS_FN_SHIFT(__fn) ((__fn) * 8) +static void +bfa_ioc_map_port(struct bfa_ioc_s *ioc) +{ + bfa_os_addr_t rb = ioc->pcidev.pci_bar_kva; + u32 r32; + + /** + * For crossbow, port id is same as pci function. + */ + if (ioc->pcidev.device_id != BFA_PCI_DEVICE_ID_CT) { + ioc->port_id = bfa_ioc_pcifn(ioc); + return; + } + + /** + * For catapult, base port id on personality register and IOC type + */ + r32 = bfa_reg_read(rb + FNC_PERS_REG); + r32 >>= FNC_PERS_FN_SHIFT(bfa_ioc_pcifn(ioc)); + ioc->port_id = (r32 & __F0_PORT_MAP_MK) >> __F0_PORT_MAP_SH; + + bfa_trc(ioc, bfa_ioc_pcifn(ioc)); + bfa_trc(ioc, ioc->port_id); +} + + + +/** + * bfa_ioc_public + */ + +/** +* Set interrupt mode for a function: INTX or MSIX + */ +void +bfa_ioc_isr_mode_set(struct bfa_ioc_s *ioc, bfa_boolean_t msix) +{ + bfa_os_addr_t rb = ioc->pcidev.pci_bar_kva; + u32 r32, mode; + + r32 = bfa_reg_read(rb + FNC_PERS_REG); + bfa_trc(ioc, r32); + + mode = (r32 >> FNC_PERS_FN_SHIFT(bfa_ioc_pcifn(ioc))) & + __F0_INTX_STATUS; + + /** + * If already in desired mode, do not change anything + */ + if (!msix && mode) + return; + + if (msix) + mode = __F0_INTX_STATUS_MSIX; + else + mode = __F0_INTX_STATUS_INTA; + + r32 &= ~(__F0_INTX_STATUS << FNC_PERS_FN_SHIFT(bfa_ioc_pcifn(ioc))); + r32 |= (mode << FNC_PERS_FN_SHIFT(bfa_ioc_pcifn(ioc))); + bfa_trc(ioc, r32); + + bfa_reg_write(rb + FNC_PERS_REG, r32); +} + +bfa_status_t +bfa_ioc_pll_init(struct bfa_ioc_s *ioc) +{ + bfa_os_addr_t rb = ioc->pcidev.pci_bar_kva; + u32 pll_sclk, pll_fclk, r32; + + if (ioc->pcidev.device_id == BFA_PCI_DEVICE_ID_CT) { + pll_sclk = + __APP_PLL_312_ENABLE | __APP_PLL_312_LRESETN | + __APP_PLL_312_RSEL200500 | __APP_PLL_312_P0_1(0U) | + __APP_PLL_312_JITLMT0_1(3U) | + __APP_PLL_312_CNTLMT0_1(1U); + pll_fclk = + __APP_PLL_425_ENABLE | __APP_PLL_425_LRESETN | + __APP_PLL_425_RSEL200500 | __APP_PLL_425_P0_1(0U) | + __APP_PLL_425_JITLMT0_1(3U) | + __APP_PLL_425_CNTLMT0_1(1U); + + /** + * For catapult, choose operational mode FC/FCoE + */ + if (ioc->fcmode) { + bfa_reg_write((rb + OP_MODE), 0); + bfa_reg_write((rb + ETH_MAC_SER_REG), + __APP_EMS_CMLCKSEL | __APP_EMS_REFCKBUFEN2 + | __APP_EMS_CHANNEL_SEL); + } else { + ioc->pllinit = BFA_TRUE; + bfa_reg_write((rb + OP_MODE), __GLOBAL_FCOE_MODE); + bfa_reg_write((rb + ETH_MAC_SER_REG), + __APP_EMS_REFCKBUFEN1); + } + } else { + pll_sclk = + __APP_PLL_312_ENABLE | __APP_PLL_312_LRESETN | + __APP_PLL_312_P0_1(3U) | __APP_PLL_312_JITLMT0_1(3U) | + __APP_PLL_312_CNTLMT0_1(3U); + pll_fclk = + __APP_PLL_425_ENABLE | __APP_PLL_425_LRESETN | + __APP_PLL_425_RSEL200500 | __APP_PLL_425_P0_1(3U) | + __APP_PLL_425_JITLMT0_1(3U) | + __APP_PLL_425_CNTLMT0_1(3U); + } + + bfa_reg_write((rb + BFA_IOC0_STATE_REG), BFI_IOC_UNINIT); + bfa_reg_write((rb + BFA_IOC1_STATE_REG), BFI_IOC_UNINIT); + + bfa_reg_write((rb + HOSTFN0_INT_MSK), 0xffffffffU); + bfa_reg_write((rb + HOSTFN1_INT_MSK), 0xffffffffU); + bfa_reg_write((rb + HOSTFN0_INT_STATUS), 0xffffffffU); + bfa_reg_write((rb + HOSTFN1_INT_STATUS), 0xffffffffU); + bfa_reg_write((rb + HOSTFN0_INT_MSK), 0xffffffffU); + bfa_reg_write((rb + HOSTFN1_INT_MSK), 0xffffffffU); + + bfa_reg_write(ioc->ioc_regs.app_pll_slow_ctl_reg, + __APP_PLL_312_LOGIC_SOFT_RESET); + bfa_reg_write(ioc->ioc_regs.app_pll_slow_ctl_reg, + __APP_PLL_312_BYPASS | __APP_PLL_312_LOGIC_SOFT_RESET); + bfa_reg_write(ioc->ioc_regs.app_pll_fast_ctl_reg, + __APP_PLL_425_LOGIC_SOFT_RESET); + bfa_reg_write(ioc->ioc_regs.app_pll_fast_ctl_reg, + __APP_PLL_425_BYPASS | __APP_PLL_425_LOGIC_SOFT_RESET); + bfa_os_udelay(2); + bfa_reg_write(ioc->ioc_regs.app_pll_slow_ctl_reg, + __APP_PLL_312_LOGIC_SOFT_RESET); + bfa_reg_write(ioc->ioc_regs.app_pll_fast_ctl_reg, + __APP_PLL_425_LOGIC_SOFT_RESET); + + bfa_reg_write(ioc->ioc_regs.app_pll_slow_ctl_reg, + pll_sclk | __APP_PLL_312_LOGIC_SOFT_RESET); + bfa_reg_write(ioc->ioc_regs.app_pll_fast_ctl_reg, + pll_fclk | __APP_PLL_425_LOGIC_SOFT_RESET); + + /** + * Wait for PLLs to lock. + */ + bfa_os_udelay(2000); + bfa_reg_write((rb + HOSTFN0_INT_STATUS), 0xffffffffU); + bfa_reg_write((rb + HOSTFN1_INT_STATUS), 0xffffffffU); + + bfa_reg_write(ioc->ioc_regs.app_pll_slow_ctl_reg, pll_sclk); + bfa_reg_write(ioc->ioc_regs.app_pll_fast_ctl_reg, pll_fclk); + + if (ioc->pcidev.device_id == BFA_PCI_DEVICE_ID_CT) { + bfa_reg_write((rb + MBIST_CTL_REG), __EDRAM_BISTR_START); + bfa_os_udelay(1000); + r32 = bfa_reg_read((rb + MBIST_STAT_REG)); + bfa_trc(ioc, r32); + } + + return BFA_STATUS_OK; +} + +/** + * Interface used by diag module to do firmware boot with memory test + * as the entry vector. + */ +void +bfa_ioc_boot(struct bfa_ioc_s *ioc, u32 boot_type, u32 boot_param) +{ + bfa_os_addr_t rb; + + bfa_ioc_stats(ioc, ioc_boots); + + if (bfa_ioc_pll_init(ioc) != BFA_STATUS_OK) + return; + + /** + * Initialize IOC state of all functions on a chip reset. + */ + rb = ioc->pcidev.pci_bar_kva; + if (boot_param == BFI_BOOT_TYPE_MEMTEST) { + bfa_reg_write((rb + BFA_IOC0_STATE_REG), BFI_IOC_MEMTEST); + bfa_reg_write((rb + BFA_IOC1_STATE_REG), BFI_IOC_MEMTEST); + } else { + bfa_reg_write((rb + BFA_IOC0_STATE_REG), BFI_IOC_INITING); + bfa_reg_write((rb + BFA_IOC1_STATE_REG), BFI_IOC_INITING); + } + + bfa_ioc_download_fw(ioc, boot_type, boot_param); + + /** + * Enable interrupts just before starting LPU + */ + ioc->cbfn->reset_cbfn(ioc->bfa); + bfa_ioc_lpu_start(ioc); +} + +/** + * Enable/disable IOC failure auto recovery. + */ +void +bfa_ioc_auto_recover(bfa_boolean_t auto_recover) +{ + bfa_auto_recover = BFA_FALSE; +} + + +bfa_boolean_t +bfa_ioc_is_operational(struct bfa_ioc_s *ioc) +{ + return bfa_fsm_cmp_state(ioc, bfa_ioc_sm_op); +} + +void +bfa_ioc_msgget(struct bfa_ioc_s *ioc, void *mbmsg) +{ + u32 *msgp = mbmsg; + u32 r32; + int i; + + /** + * read the MBOX msg + */ + for (i = 0; i < (sizeof(union bfi_ioc_i2h_msg_u) / sizeof(u32)); + i++) { + r32 = bfa_reg_read(ioc->ioc_regs.lpu_mbox + + i * sizeof(u32)); + msgp[i] = bfa_os_htonl(r32); + } + + /** + * turn off mailbox interrupt by clearing mailbox status + */ + bfa_reg_write(ioc->ioc_regs.lpu_mbox_cmd, 1); + bfa_reg_read(ioc->ioc_regs.lpu_mbox_cmd); +} + +void +bfa_ioc_isr(struct bfa_ioc_s *ioc, struct bfi_mbmsg_s *m) +{ + union bfi_ioc_i2h_msg_u *msg; + + msg = (union bfi_ioc_i2h_msg_u *)m; + + bfa_ioc_stats(ioc, ioc_isrs); + + switch (msg->mh.msg_id) { + case BFI_IOC_I2H_HBEAT: + break; + + case BFI_IOC_I2H_READY_EVENT: + bfa_fsm_send_event(ioc, IOC_E_FWREADY); + break; + + case BFI_IOC_I2H_ENABLE_REPLY: + bfa_fsm_send_event(ioc, IOC_E_FWRSP_ENABLE); + break; + + case BFI_IOC_I2H_DISABLE_REPLY: + bfa_fsm_send_event(ioc, IOC_E_FWRSP_DISABLE); + break; + + case BFI_IOC_I2H_GETATTR_REPLY: + bfa_ioc_getattr_reply(ioc); + break; + + default: + bfa_trc(ioc, msg->mh.msg_id); + bfa_assert(0); + } +} + +/** + * IOC attach time initialization and setup. + * + * @param[in] ioc memory for IOC + * @param[in] bfa driver instance structure + * @param[in] trcmod kernel trace module + * @param[in] aen kernel aen event module + * @param[in] logm kernel logging module + */ +void +bfa_ioc_attach(struct bfa_ioc_s *ioc, void *bfa, struct bfa_ioc_cbfn_s *cbfn, + struct bfa_timer_mod_s *timer_mod, struct bfa_trc_mod_s *trcmod, + struct bfa_aen_s *aen, struct bfa_log_mod_s *logm) +{ + ioc->bfa = bfa; + ioc->cbfn = cbfn; + ioc->timer_mod = timer_mod; + ioc->trcmod = trcmod; + ioc->aen = aen; + ioc->logm = logm; + ioc->fcmode = BFA_FALSE; + ioc->pllinit = BFA_FALSE; + ioc->dbg_fwsave_once = BFA_TRUE; + + bfa_ioc_mbox_attach(ioc); + INIT_LIST_HEAD(&ioc->hb_notify_q); + + bfa_fsm_set_state(ioc, bfa_ioc_sm_reset); +} + +/** + * Driver detach time IOC cleanup. + */ +void +bfa_ioc_detach(struct bfa_ioc_s *ioc) +{ + bfa_fsm_send_event(ioc, IOC_E_DETACH); +} + +/** + * Setup IOC PCI properties. + * + * @param[in] pcidev PCI device information for this IOC + */ +void +bfa_ioc_pci_init(struct bfa_ioc_s *ioc, struct bfa_pcidev_s *pcidev, + enum bfi_mclass mc) +{ + ioc->ioc_mc = mc; + ioc->pcidev = *pcidev; + ioc->ctdev = (ioc->pcidev.device_id == BFA_PCI_DEVICE_ID_CT); + ioc->cna = ioc->ctdev && !ioc->fcmode; + + bfa_ioc_map_port(ioc); + bfa_ioc_reg_init(ioc); +} + +/** + * Initialize IOC dma memory + * + * @param[in] dm_kva kernel virtual address of IOC dma memory + * @param[in] dm_pa physical address of IOC dma memory + */ +void +bfa_ioc_mem_claim(struct bfa_ioc_s *ioc, u8 *dm_kva, u64 dm_pa) +{ + /** + * dma memory for firmware attribute + */ + ioc->attr_dma.kva = dm_kva; + ioc->attr_dma.pa = dm_pa; + ioc->attr = (struct bfi_ioc_attr_s *)dm_kva; +} + +/** + * Return size of dma memory required. + */ +u32 +bfa_ioc_meminfo(void) +{ + return BFA_ROUNDUP(sizeof(struct bfi_ioc_attr_s), BFA_DMA_ALIGN_SZ); +} + +void +bfa_ioc_enable(struct bfa_ioc_s *ioc) +{ + bfa_ioc_stats(ioc, ioc_enables); + ioc->dbg_fwsave_once = BFA_TRUE; + + bfa_fsm_send_event(ioc, IOC_E_ENABLE); +} + +void +bfa_ioc_disable(struct bfa_ioc_s *ioc) +{ + bfa_ioc_stats(ioc, ioc_disables); + bfa_fsm_send_event(ioc, IOC_E_DISABLE); +} + +/** + * Returns memory required for saving firmware trace in case of crash. + * Driver must call this interface to allocate memory required for + * automatic saving of firmware trace. Driver should call + * bfa_ioc_debug_memclaim() right after bfa_ioc_attach() to setup this + * trace memory. + */ +int +bfa_ioc_debug_trcsz(bfa_boolean_t auto_recover) +{ +return (auto_recover) ? BFA_DBG_FWTRC_LEN : 0; +} + +/** + * Initialize memory for saving firmware trace. Driver must initialize + * trace memory before call bfa_ioc_enable(). + */ +void +bfa_ioc_debug_memclaim(struct bfa_ioc_s *ioc, void *dbg_fwsave) +{ + bfa_assert(ioc->auto_recover); + ioc->dbg_fwsave = dbg_fwsave; + ioc->dbg_fwsave_len = bfa_ioc_debug_trcsz(ioc->auto_recover); +} + +u32 +bfa_ioc_smem_pgnum(struct bfa_ioc_s *ioc, u32 fmaddr) +{ + return PSS_SMEM_PGNUM(ioc->ioc_regs.smem_pg0, fmaddr); +} + +u32 +bfa_ioc_smem_pgoff(struct bfa_ioc_s *ioc, u32 fmaddr) +{ + return PSS_SMEM_PGOFF(fmaddr); +} + +/** + * Register mailbox message handler functions + * + * @param[in] ioc IOC instance + * @param[in] mcfuncs message class handler functions + */ +void +bfa_ioc_mbox_register(struct bfa_ioc_s *ioc, bfa_ioc_mbox_mcfunc_t *mcfuncs) +{ + struct bfa_ioc_mbox_mod_s *mod = &ioc->mbox_mod; + int mc; + + for (mc = 0; mc < BFI_MC_MAX; mc++) + mod->mbhdlr[mc].cbfn = mcfuncs[mc]; +} + +/** + * Register mailbox message handler function, to be called by common modules + */ +void +bfa_ioc_mbox_regisr(struct bfa_ioc_s *ioc, enum bfi_mclass mc, + bfa_ioc_mbox_mcfunc_t cbfn, void *cbarg) +{ + struct bfa_ioc_mbox_mod_s *mod = &ioc->mbox_mod; + + mod->mbhdlr[mc].cbfn = cbfn; + mod->mbhdlr[mc].cbarg = cbarg; +} + +/** + * Queue a mailbox command request to firmware. Waits if mailbox is busy. + * Responsibility of caller to serialize + * + * @param[in] ioc IOC instance + * @param[i] cmd Mailbox command + */ +void +bfa_ioc_mbox_queue(struct bfa_ioc_s *ioc, struct bfa_mbox_cmd_s *cmd) +{ + struct bfa_ioc_mbox_mod_s *mod = &ioc->mbox_mod; + u32 stat; + + /** + * If a previous command is pending, queue new command + */ + if (!list_empty(&mod->cmd_q)) { + list_add_tail(&cmd->qe, &mod->cmd_q); + return; + } + + /** + * If mailbox is busy, queue command for poll timer + */ + stat = bfa_reg_read(ioc->ioc_regs.hfn_mbox_cmd); + if (stat) { + list_add_tail(&cmd->qe, &mod->cmd_q); + return; + } + + /** + * mailbox is free -- queue command to firmware + */ + bfa_ioc_mbox_send(ioc, cmd->msg, sizeof(cmd->msg)); +} + +/** + * Handle mailbox interrupts + */ +void +bfa_ioc_mbox_isr(struct bfa_ioc_s *ioc) +{ + struct bfa_ioc_mbox_mod_s *mod = &ioc->mbox_mod; + struct bfi_mbmsg_s m; + int mc; + + bfa_ioc_msgget(ioc, &m); + + /** + * Treat IOC message class as special. + */ + mc = m.mh.msg_class; + if (mc == BFI_MC_IOC) { + bfa_ioc_isr(ioc, &m); + return; + } + + if ((mc > BFI_MC_MAX) || (mod->mbhdlr[mc].cbfn == NULL)) + return; + + mod->mbhdlr[mc].cbfn(mod->mbhdlr[mc].cbarg, &m); +} + +void +bfa_ioc_error_isr(struct bfa_ioc_s *ioc) +{ + bfa_fsm_send_event(ioc, IOC_E_HWERROR); +} + +#ifndef BFA_BIOS_BUILD + +/** + * return true if IOC is disabled + */ +bfa_boolean_t +bfa_ioc_is_disabled(struct bfa_ioc_s *ioc) +{ + return (bfa_fsm_cmp_state(ioc, bfa_ioc_sm_disabling) + || bfa_fsm_cmp_state(ioc, bfa_ioc_sm_disabled)); +} + +/** + * return true if IOC firmware is different. + */ +bfa_boolean_t +bfa_ioc_fw_mismatch(struct bfa_ioc_s *ioc) +{ + return (bfa_fsm_cmp_state(ioc, bfa_ioc_sm_reset) + || bfa_fsm_cmp_state(ioc, bfa_ioc_sm_fwcheck) + || bfa_fsm_cmp_state(ioc, bfa_ioc_sm_mismatch)); +} + +#define bfa_ioc_state_disabled(__sm) \ + (((__sm) == BFI_IOC_UNINIT) || \ + ((__sm) == BFI_IOC_INITING) || \ + ((__sm) == BFI_IOC_HWINIT) || \ + ((__sm) == BFI_IOC_DISABLED) || \ + ((__sm) == BFI_IOC_HBFAIL) || \ + ((__sm) == BFI_IOC_CFG_DISABLED)) + +/** + * Check if adapter is disabled -- both IOCs should be in a disabled + * state. + */ +bfa_boolean_t +bfa_ioc_adapter_is_disabled(struct bfa_ioc_s *ioc) +{ + u32 ioc_state; + bfa_os_addr_t rb = ioc->pcidev.pci_bar_kva; + + if (!bfa_fsm_cmp_state(ioc, bfa_ioc_sm_disabled)) + return BFA_FALSE; + + ioc_state = bfa_reg_read(rb + BFA_IOC0_STATE_REG); + if (!bfa_ioc_state_disabled(ioc_state)) + return BFA_FALSE; + + ioc_state = bfa_reg_read(rb + BFA_IOC1_STATE_REG); + if (!bfa_ioc_state_disabled(ioc_state)) + return BFA_FALSE; + + return BFA_TRUE; +} + +/** + * Add to IOC heartbeat failure notification queue. To be used by common + * modules such as + */ +void +bfa_ioc_hbfail_register(struct bfa_ioc_s *ioc, + struct bfa_ioc_hbfail_notify_s *notify) +{ + list_add_tail(¬ify->qe, &ioc->hb_notify_q); +} + +#define BFA_MFG_NAME "Brocade" +void +bfa_ioc_get_adapter_attr(struct bfa_ioc_s *ioc, + struct bfa_adapter_attr_s *ad_attr) +{ + struct bfi_ioc_attr_s *ioc_attr; + char model[BFA_ADAPTER_MODEL_NAME_LEN]; + + ioc_attr = ioc->attr; + bfa_os_memcpy((void *)&ad_attr->serial_num, + (void *)ioc_attr->brcd_serialnum, + BFA_ADAPTER_SERIAL_NUM_LEN); + + bfa_os_memcpy(&ad_attr->fw_ver, ioc_attr->fw_version, BFA_VERSION_LEN); + bfa_os_memcpy(&ad_attr->optrom_ver, ioc_attr->optrom_version, + BFA_VERSION_LEN); + bfa_os_memcpy(&ad_attr->manufacturer, BFA_MFG_NAME, + BFA_ADAPTER_MFG_NAME_LEN); + bfa_os_memcpy(&ad_attr->vpd, &ioc_attr->vpd, + sizeof(struct bfa_mfg_vpd_s)); + + ad_attr->nports = BFI_ADAPTER_GETP(NPORTS, ioc_attr->adapter_prop); + ad_attr->max_speed = BFI_ADAPTER_GETP(SPEED, ioc_attr->adapter_prop); + + /** + * model name + */ + if (BFI_ADAPTER_GETP(SPEED, ioc_attr->adapter_prop) == 10) { + strcpy(model, "BR-10?0"); + model[5] = '0' + ad_attr->nports; + } else { + strcpy(model, "Brocade-??5"); + model[8] = + '0' + BFI_ADAPTER_GETP(SPEED, ioc_attr->adapter_prop); + model[9] = '0' + ad_attr->nports; + } + + if (BFI_ADAPTER_IS_SPECIAL(ioc_attr->adapter_prop)) + ad_attr->prototype = 1; + else + ad_attr->prototype = 0; + + bfa_os_memcpy(&ad_attr->model, model, BFA_ADAPTER_MODEL_NAME_LEN); + bfa_os_memcpy(&ad_attr->model_descr, &ad_attr->model, + BFA_ADAPTER_MODEL_NAME_LEN); + + ad_attr->pwwn = bfa_ioc_get_pwwn(ioc); + ad_attr->mac = bfa_ioc_get_mac(ioc); + + ad_attr->pcie_gen = ioc_attr->pcie_gen; + ad_attr->pcie_lanes = ioc_attr->pcie_lanes; + ad_attr->pcie_lanes_orig = ioc_attr->pcie_lanes_orig; + ad_attr->asic_rev = ioc_attr->asic_rev; + ad_attr->hw_ver[0] = 'R'; + ad_attr->hw_ver[1] = 'e'; + ad_attr->hw_ver[2] = 'v'; + ad_attr->hw_ver[3] = '-'; + ad_attr->hw_ver[4] = ioc_attr->asic_rev; + ad_attr->hw_ver[5] = '\0'; + + ad_attr->cna_capable = ioc->cna; +} + +void +bfa_ioc_get_attr(struct bfa_ioc_s *ioc, struct bfa_ioc_attr_s *ioc_attr) +{ + bfa_os_memset((void *)ioc_attr, 0, sizeof(struct bfa_ioc_attr_s)); + + ioc_attr->state = bfa_sm_to_state(ioc_sm_table, ioc->fsm); + ioc_attr->port_id = ioc->port_id; + + if (!ioc->ctdev) + ioc_attr->ioc_type = BFA_IOC_TYPE_FC; + else if (ioc->ioc_mc == BFI_MC_IOCFC) + ioc_attr->ioc_type = BFA_IOC_TYPE_FCoE; + else if (ioc->ioc_mc == BFI_MC_LL) + ioc_attr->ioc_type = BFA_IOC_TYPE_LL; + + bfa_ioc_get_adapter_attr(ioc, &ioc_attr->adapter_attr); + + ioc_attr->pci_attr.device_id = ioc->pcidev.device_id; + ioc_attr->pci_attr.pcifn = ioc->pcidev.pci_func; + ioc_attr->pci_attr.chip_rev[0] = 'R'; + ioc_attr->pci_attr.chip_rev[1] = 'e'; + ioc_attr->pci_attr.chip_rev[2] = 'v'; + ioc_attr->pci_attr.chip_rev[3] = '-'; + ioc_attr->pci_attr.chip_rev[4] = ioc_attr->adapter_attr.asic_rev; + ioc_attr->pci_attr.chip_rev[5] = '\0'; +} + +/** + * hal_wwn_public + */ +wwn_t +bfa_ioc_get_pwwn(struct bfa_ioc_s *ioc) +{ + union { + wwn_t wwn; + u8 byte[sizeof(wwn_t)]; + } + w; + + w.wwn = ioc->attr->mfg_wwn; + + if (bfa_ioc_portid(ioc) == 1) + w.byte[7]++; + + return w.wwn; +} + +wwn_t +bfa_ioc_get_nwwn(struct bfa_ioc_s *ioc) +{ + union { + wwn_t wwn; + u8 byte[sizeof(wwn_t)]; + } + w; + + w.wwn = ioc->attr->mfg_wwn; + + if (bfa_ioc_portid(ioc) == 1) + w.byte[7]++; + + w.byte[0] = 0x20; + + return w.wwn; +} + +wwn_t +bfa_ioc_get_wwn_naa5(struct bfa_ioc_s *ioc, u16 inst) +{ + union { + wwn_t wwn; + u8 byte[sizeof(wwn_t)]; + } + w , w5; + + bfa_trc(ioc, inst); + + w.wwn = ioc->attr->mfg_wwn; + w5.byte[0] = 0x50 | w.byte[2] >> 4; + w5.byte[1] = w.byte[2] << 4 | w.byte[3] >> 4; + w5.byte[2] = w.byte[3] << 4 | w.byte[4] >> 4; + w5.byte[3] = w.byte[4] << 4 | w.byte[5] >> 4; + w5.byte[4] = w.byte[5] << 4 | w.byte[6] >> 4; + w5.byte[5] = w.byte[6] << 4 | w.byte[7] >> 4; + w5.byte[6] = w.byte[7] << 4 | (inst & 0x0f00) >> 8; + w5.byte[7] = (inst & 0xff); + + return w5.wwn; +} + +u64 +bfa_ioc_get_adid(struct bfa_ioc_s *ioc) +{ + return ioc->attr->mfg_wwn; +} + +mac_t +bfa_ioc_get_mac(struct bfa_ioc_s *ioc) +{ + mac_t mac; + + mac = ioc->attr->mfg_mac; + mac.mac[MAC_ADDRLEN - 1] += bfa_ioc_pcifn(ioc); + + return mac; +} + +void +bfa_ioc_set_fcmode(struct bfa_ioc_s *ioc) +{ + ioc->fcmode = BFA_TRUE; + ioc->port_id = bfa_ioc_pcifn(ioc); +} + +bfa_boolean_t +bfa_ioc_get_fcmode(struct bfa_ioc_s *ioc) +{ + return ioc->fcmode || (ioc->pcidev.device_id != BFA_PCI_DEVICE_ID_CT); +} + +/** + * Return true if interrupt should be claimed. + */ +bfa_boolean_t +bfa_ioc_intx_claim(struct bfa_ioc_s *ioc) +{ + u32 isr, msk; + + /** + * Always claim if not catapult. + */ + if (!ioc->ctdev) + return BFA_TRUE; + + /** + * FALSE if next device is claiming interrupt. + * TRUE if next device is not interrupting or not present. + */ + msk = bfa_reg_read(ioc->ioc_regs.shirq_msk_next); + isr = bfa_reg_read(ioc->ioc_regs.shirq_isr_next); + return !(isr & ~msk); +} + +/** + * Send AEN notification + */ +static void +bfa_ioc_aen_post(struct bfa_ioc_s *ioc, enum bfa_ioc_aen_event event) +{ + union bfa_aen_data_u aen_data; + struct bfa_log_mod_s *logmod = ioc->logm; + s32 inst_num = 0; + struct bfa_ioc_attr_s ioc_attr; + + switch (event) { + case BFA_IOC_AEN_HBGOOD: + bfa_log(logmod, BFA_AEN_IOC_HBGOOD, inst_num); + break; + case BFA_IOC_AEN_HBFAIL: + bfa_log(logmod, BFA_AEN_IOC_HBFAIL, inst_num); + break; + case BFA_IOC_AEN_ENABLE: + bfa_log(logmod, BFA_AEN_IOC_ENABLE, inst_num); + break; + case BFA_IOC_AEN_DISABLE: + bfa_log(logmod, BFA_AEN_IOC_DISABLE, inst_num); + break; + case BFA_IOC_AEN_FWMISMATCH: + bfa_log(logmod, BFA_AEN_IOC_FWMISMATCH, inst_num); + break; + default: + break; + } + + memset(&aen_data.ioc.pwwn, 0, sizeof(aen_data.ioc.pwwn)); + memset(&aen_data.ioc.mac, 0, sizeof(aen_data.ioc.mac)); + bfa_ioc_get_attr(ioc, &ioc_attr); + switch (ioc_attr.ioc_type) { + case BFA_IOC_TYPE_FC: + aen_data.ioc.pwwn = bfa_ioc_get_pwwn(ioc); + break; + case BFA_IOC_TYPE_FCoE: + aen_data.ioc.pwwn = bfa_ioc_get_pwwn(ioc); + aen_data.ioc.mac = bfa_ioc_get_mac(ioc); + break; + case BFA_IOC_TYPE_LL: + aen_data.ioc.mac = bfa_ioc_get_mac(ioc); + break; + default: + bfa_assert(ioc_attr.ioc_type == BFA_IOC_TYPE_FC); + break; + } + aen_data.ioc.ioc_type = ioc_attr.ioc_type; +} + +/** + * Retrieve saved firmware trace from a prior IOC failure. + */ +bfa_status_t +bfa_ioc_debug_fwsave(struct bfa_ioc_s *ioc, void *trcdata, int *trclen) +{ + int tlen; + + if (ioc->dbg_fwsave_len == 0) + return BFA_STATUS_ENOFSAVE; + + tlen = *trclen; + if (tlen > ioc->dbg_fwsave_len) + tlen = ioc->dbg_fwsave_len; + + bfa_os_memcpy(trcdata, ioc->dbg_fwsave, tlen); + *trclen = tlen; + return BFA_STATUS_OK; +} + +/** + * Retrieve saved firmware trace from a prior IOC failure. + */ +bfa_status_t +bfa_ioc_debug_fwtrc(struct bfa_ioc_s *ioc, void *trcdata, int *trclen) +{ + u32 pgnum; + u32 loff = BFA_DBG_FWTRC_OFF(bfa_ioc_portid(ioc)); + int i, tlen; + u32 *tbuf = trcdata, r32; + + bfa_trc(ioc, *trclen); + + pgnum = bfa_ioc_smem_pgnum(ioc, loff); + loff = bfa_ioc_smem_pgoff(ioc, loff); + bfa_reg_write(ioc->ioc_regs.host_page_num_fn, pgnum); + + tlen = *trclen; + if (tlen > BFA_DBG_FWTRC_LEN) + tlen = BFA_DBG_FWTRC_LEN; + tlen /= sizeof(u32); + + bfa_trc(ioc, tlen); + + for (i = 0; i < tlen; i++) { + r32 = bfa_mem_read(ioc->ioc_regs.smem_page_start, loff); + tbuf[i] = bfa_os_ntohl(r32); + loff += sizeof(u32); + + /** + * handle page offset wrap around + */ + loff = PSS_SMEM_PGOFF(loff); + if (loff == 0) { + pgnum++; + bfa_reg_write(ioc->ioc_regs.host_page_num_fn, pgnum); + } + } + bfa_reg_write(ioc->ioc_regs.host_page_num_fn, + bfa_ioc_smem_pgnum(ioc, 0)); + bfa_trc(ioc, pgnum); + + *trclen = tlen * sizeof(u32); + return BFA_STATUS_OK; +} + +/** + * Save firmware trace if configured. + */ +static void +bfa_ioc_debug_save(struct bfa_ioc_s *ioc) +{ + int tlen; + + if (ioc->dbg_fwsave_len) { + tlen = ioc->dbg_fwsave_len; + bfa_ioc_debug_fwtrc(ioc, ioc->dbg_fwsave, &tlen); + } +} + +/** + * Firmware failure detected. Start recovery actions. + */ +static void +bfa_ioc_recover(struct bfa_ioc_s *ioc) +{ + if (ioc->dbg_fwsave_once) { + ioc->dbg_fwsave_once = BFA_FALSE; + bfa_ioc_debug_save(ioc); + } + + bfa_ioc_stats(ioc, ioc_hbfails); + bfa_fsm_send_event(ioc, IOC_E_HBFAIL); +} + +#else + +static void +bfa_ioc_aen_post(struct bfa_ioc_s *ioc, enum bfa_ioc_aen_event event) +{ +} + +static void +bfa_ioc_recover(struct bfa_ioc_s *ioc) +{ + bfa_assert(0); +} + +#endif + + diff --git a/drivers/scsi/bfa/bfa_ioc.h b/drivers/scsi/bfa/bfa_ioc.h new file mode 100644 index 000000000000..58efd4b13143 --- /dev/null +++ b/drivers/scsi/bfa/bfa_ioc.h @@ -0,0 +1,259 @@ +/* + * Copyright (c) 2005-2009 Brocade Communications Systems, Inc. + * All rights reserved + * www.brocade.com + * + * Linux driver for Brocade Fibre Channel Host Bus Adapter. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (GPL) Version 2 as + * published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#ifndef __BFA_IOC_H__ +#define __BFA_IOC_H__ + +#include <cs/bfa_sm.h> +#include <bfi/bfi.h> +#include <bfi/bfi_ioc.h> +#include <bfi/bfi_boot.h> +#include <bfa_timer.h> + +/** + * PCI device information required by IOC + */ +struct bfa_pcidev_s { + int pci_slot; + u8 pci_func; + u16 device_id; + bfa_os_addr_t pci_bar_kva; +}; + +/** + * Structure used to remember the DMA-able memory block's KVA and Physical + * Address + */ +struct bfa_dma_s { + void *kva; /*! Kernel virtual address */ + u64 pa; /*! Physical address */ +}; + +#define BFA_DMA_ALIGN_SZ 256 +#define BFA_ROUNDUP(_l, _s) (((_l) + ((_s) - 1)) & ~((_s) - 1)) + + + +#define bfa_dma_addr_set(dma_addr, pa) \ + __bfa_dma_addr_set(&dma_addr, (u64)pa) + +static inline void +__bfa_dma_addr_set(union bfi_addr_u *dma_addr, u64 pa) +{ + dma_addr->a32.addr_lo = (u32) pa; + dma_addr->a32.addr_hi = (u32) (bfa_os_u32(pa)); +} + + +#define bfa_dma_be_addr_set(dma_addr, pa) \ + __bfa_dma_be_addr_set(&dma_addr, (u64)pa) +static inline void +__bfa_dma_be_addr_set(union bfi_addr_u *dma_addr, u64 pa) +{ + dma_addr->a32.addr_lo = (u32) bfa_os_htonl(pa); + dma_addr->a32.addr_hi = (u32) bfa_os_htonl(bfa_os_u32(pa)); +} + +struct bfa_ioc_regs_s { + bfa_os_addr_t hfn_mbox_cmd; + bfa_os_addr_t hfn_mbox; + bfa_os_addr_t lpu_mbox_cmd; + bfa_os_addr_t lpu_mbox; + bfa_os_addr_t pss_ctl_reg; + bfa_os_addr_t app_pll_fast_ctl_reg; + bfa_os_addr_t app_pll_slow_ctl_reg; + bfa_os_addr_t ioc_sem_reg; + bfa_os_addr_t ioc_usage_sem_reg; + bfa_os_addr_t ioc_usage_reg; + bfa_os_addr_t host_page_num_fn; + bfa_os_addr_t heartbeat; + bfa_os_addr_t ioc_fwstate; + bfa_os_addr_t ll_halt; + bfa_os_addr_t shirq_isr_next; + bfa_os_addr_t shirq_msk_next; + bfa_os_addr_t smem_page_start; + u32 smem_pg0; +}; + +#define bfa_reg_read(_raddr) bfa_os_reg_read(_raddr) +#define bfa_reg_write(_raddr, _val) bfa_os_reg_write(_raddr, _val) +#define bfa_mem_read(_raddr, _off) bfa_os_mem_read(_raddr, _off) +#define bfa_mem_write(_raddr, _off, _val) \ + bfa_os_mem_write(_raddr, _off, _val) +/** + * IOC Mailbox structures + */ +struct bfa_mbox_cmd_s { + struct list_head qe; + u32 msg[BFI_IOC_MSGSZ]; +}; + +/** + * IOC mailbox module + */ +typedef void (*bfa_ioc_mbox_mcfunc_t)(void *cbarg, struct bfi_mbmsg_s *m); +struct bfa_ioc_mbox_mod_s { + struct list_head cmd_q; /* pending mbox queue */ + int nmclass; /* number of handlers */ + struct { + bfa_ioc_mbox_mcfunc_t cbfn; /* message handlers */ + void *cbarg; + } mbhdlr[BFI_MC_MAX]; +}; + +/** + * IOC callback function interfaces + */ +typedef void (*bfa_ioc_enable_cbfn_t)(void *bfa, enum bfa_status status); +typedef void (*bfa_ioc_disable_cbfn_t)(void *bfa); +typedef void (*bfa_ioc_hbfail_cbfn_t)(void *bfa); +typedef void (*bfa_ioc_reset_cbfn_t)(void *bfa); +struct bfa_ioc_cbfn_s { + bfa_ioc_enable_cbfn_t enable_cbfn; + bfa_ioc_disable_cbfn_t disable_cbfn; + bfa_ioc_hbfail_cbfn_t hbfail_cbfn; + bfa_ioc_reset_cbfn_t reset_cbfn; +}; + +/** + * Heartbeat failure notification queue element. + */ +struct bfa_ioc_hbfail_notify_s { + struct list_head qe; + bfa_ioc_hbfail_cbfn_t cbfn; + void *cbarg; +}; + +/** + * Initialize a heartbeat failure notification structure + */ +#define bfa_ioc_hbfail_init(__notify, __cbfn, __cbarg) do { \ + (__notify)->cbfn = (__cbfn); \ + (__notify)->cbarg = (__cbarg); \ +} while (0) + +struct bfa_ioc_s { + bfa_fsm_t fsm; + struct bfa_s *bfa; + struct bfa_pcidev_s pcidev; + struct bfa_timer_mod_s *timer_mod; + struct bfa_timer_s ioc_timer; + struct bfa_timer_s sem_timer; + u32 hb_count; + u32 hb_fail; + u32 retry_count; + struct list_head hb_notify_q; + void *dbg_fwsave; + int dbg_fwsave_len; + bfa_boolean_t dbg_fwsave_once; + enum bfi_mclass ioc_mc; + struct bfa_ioc_regs_s ioc_regs; + struct bfa_trc_mod_s *trcmod; + struct bfa_aen_s *aen; + struct bfa_log_mod_s *logm; + struct bfa_ioc_drv_stats_s stats; + bfa_boolean_t auto_recover; + bfa_boolean_t fcmode; + bfa_boolean_t ctdev; + bfa_boolean_t cna; + bfa_boolean_t pllinit; + u8 port_id; + + struct bfa_dma_s attr_dma; + struct bfi_ioc_attr_s *attr; + struct bfa_ioc_cbfn_s *cbfn; + struct bfa_ioc_mbox_mod_s mbox_mod; +}; + +#define bfa_ioc_pcifn(__ioc) (__ioc)->pcidev.pci_func +#define bfa_ioc_devid(__ioc) (__ioc)->pcidev.device_id +#define bfa_ioc_bar0(__ioc) (__ioc)->pcidev.pci_bar_kva +#define bfa_ioc_portid(__ioc) ((__ioc)->port_id) +#define bfa_ioc_fetch_stats(__ioc, __stats) \ + ((__stats)->drv_stats) = (__ioc)->stats +#define bfa_ioc_clr_stats(__ioc) \ + bfa_os_memset(&(__ioc)->stats, 0, sizeof((__ioc)->stats)) +#define bfa_ioc_maxfrsize(__ioc) (__ioc)->attr->maxfrsize +#define bfa_ioc_rx_bbcredit(__ioc) (__ioc)->attr->rx_bbcredit +#define bfa_ioc_speed_sup(__ioc) \ + BFI_ADAPTER_GETP(SPEED, (__ioc)->attr->adapter_prop) + +/** + * IOC mailbox interface + */ +void bfa_ioc_mbox_queue(struct bfa_ioc_s *ioc, struct bfa_mbox_cmd_s *cmd); +void bfa_ioc_mbox_register(struct bfa_ioc_s *ioc, + bfa_ioc_mbox_mcfunc_t *mcfuncs); +void bfa_ioc_mbox_isr(struct bfa_ioc_s *ioc); +void bfa_ioc_mbox_send(struct bfa_ioc_s *ioc, void *ioc_msg, int len); +void bfa_ioc_msgget(struct bfa_ioc_s *ioc, void *mbmsg); +void bfa_ioc_mbox_regisr(struct bfa_ioc_s *ioc, enum bfi_mclass mc, + bfa_ioc_mbox_mcfunc_t cbfn, void *cbarg); + +/** + * IOC interfaces + */ +void bfa_ioc_attach(struct bfa_ioc_s *ioc, void *bfa, + struct bfa_ioc_cbfn_s *cbfn, struct bfa_timer_mod_s *timer_mod, + struct bfa_trc_mod_s *trcmod, + struct bfa_aen_s *aen, struct bfa_log_mod_s *logm); +void bfa_ioc_detach(struct bfa_ioc_s *ioc); +void bfa_ioc_pci_init(struct bfa_ioc_s *ioc, struct bfa_pcidev_s *pcidev, + enum bfi_mclass mc); +u32 bfa_ioc_meminfo(void); +void bfa_ioc_mem_claim(struct bfa_ioc_s *ioc, u8 *dm_kva, u64 dm_pa); +void bfa_ioc_enable(struct bfa_ioc_s *ioc); +void bfa_ioc_disable(struct bfa_ioc_s *ioc); +bfa_boolean_t bfa_ioc_intx_claim(struct bfa_ioc_s *ioc); + +void bfa_ioc_boot(struct bfa_ioc_s *ioc, u32 boot_type, u32 boot_param); +void bfa_ioc_isr(struct bfa_ioc_s *ioc, struct bfi_mbmsg_s *msg); +void bfa_ioc_error_isr(struct bfa_ioc_s *ioc); +void bfa_ioc_isr_mode_set(struct bfa_ioc_s *ioc, bfa_boolean_t intx); +bfa_status_t bfa_ioc_pll_init(struct bfa_ioc_s *ioc); +bfa_boolean_t bfa_ioc_is_operational(struct bfa_ioc_s *ioc); +bfa_boolean_t bfa_ioc_is_disabled(struct bfa_ioc_s *ioc); +bfa_boolean_t bfa_ioc_fw_mismatch(struct bfa_ioc_s *ioc); +bfa_boolean_t bfa_ioc_adapter_is_disabled(struct bfa_ioc_s *ioc); +void bfa_ioc_cfg_complete(struct bfa_ioc_s *ioc); +void bfa_ioc_get_attr(struct bfa_ioc_s *ioc, struct bfa_ioc_attr_s *ioc_attr); +void bfa_ioc_get_adapter_attr(struct bfa_ioc_s *ioc, + struct bfa_adapter_attr_s *ad_attr); +int bfa_ioc_debug_trcsz(bfa_boolean_t auto_recover); +void bfa_ioc_debug_memclaim(struct bfa_ioc_s *ioc, void *dbg_fwsave); +bfa_status_t bfa_ioc_debug_fwsave(struct bfa_ioc_s *ioc, void *trcdata, + int *trclen); +bfa_status_t bfa_ioc_debug_fwtrc(struct bfa_ioc_s *ioc, void *trcdata, + int *trclen); +u32 bfa_ioc_smem_pgnum(struct bfa_ioc_s *ioc, u32 fmaddr); +u32 bfa_ioc_smem_pgoff(struct bfa_ioc_s *ioc, u32 fmaddr); +void bfa_ioc_set_fcmode(struct bfa_ioc_s *ioc); +bfa_boolean_t bfa_ioc_get_fcmode(struct bfa_ioc_s *ioc); +void bfa_ioc_hbfail_register(struct bfa_ioc_s *ioc, + struct bfa_ioc_hbfail_notify_s *notify); + +/* + * bfa mfg wwn API functions + */ +wwn_t bfa_ioc_get_pwwn(struct bfa_ioc_s *ioc); +wwn_t bfa_ioc_get_nwwn(struct bfa_ioc_s *ioc); +wwn_t bfa_ioc_get_wwn_naa5(struct bfa_ioc_s *ioc, u16 inst); +mac_t bfa_ioc_get_mac(struct bfa_ioc_s *ioc); +u64 bfa_ioc_get_adid(struct bfa_ioc_s *ioc); + +#endif /* __BFA_IOC_H__ */ + diff --git a/drivers/scsi/bfa/bfa_iocfc.c b/drivers/scsi/bfa/bfa_iocfc.c new file mode 100644 index 000000000000..12350b022d63 --- /dev/null +++ b/drivers/scsi/bfa/bfa_iocfc.c @@ -0,0 +1,872 @@ +/* + * Copyright (c) 2005-2009 Brocade Communications Systems, Inc. + * All rights reserved + * www.brocade.com + * + * Linux driver for Brocade Fibre Channel Host Bus Adapter. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (GPL) Version 2 as + * published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#include <cs/bfa_debug.h> +#include <bfa_priv.h> +#include <log/bfa_log_hal.h> +#include <bfi/bfi_boot.h> +#include <bfi/bfi_cbreg.h> +#include <aen/bfa_aen_ioc.h> +#include <defs/bfa_defs_iocfc.h> +#include <defs/bfa_defs_pci.h> +#include "bfa_callback_priv.h" +#include "bfad_drv.h" + +BFA_TRC_FILE(HAL, IOCFC); + +/** + * IOC local definitions + */ +#define BFA_IOCFC_TOV 5000 /* msecs */ + +enum { + BFA_IOCFC_ACT_NONE = 0, + BFA_IOCFC_ACT_INIT = 1, + BFA_IOCFC_ACT_STOP = 2, + BFA_IOCFC_ACT_DISABLE = 3, +}; + +/* + * forward declarations + */ +static void bfa_iocfc_enable_cbfn(void *bfa_arg, enum bfa_status status); +static void bfa_iocfc_disable_cbfn(void *bfa_arg); +static void bfa_iocfc_hbfail_cbfn(void *bfa_arg); +static void bfa_iocfc_reset_cbfn(void *bfa_arg); +static void bfa_iocfc_stats_clear(void *bfa_arg); +static void bfa_iocfc_stats_swap(struct bfa_fw_stats_s *d, + struct bfa_fw_stats_s *s); +static void bfa_iocfc_stats_clr_cb(void *bfa_arg, bfa_boolean_t complete); +static void bfa_iocfc_stats_clr_timeout(void *bfa_arg); +static void bfa_iocfc_stats_cb(void *bfa_arg, bfa_boolean_t complete); +static void bfa_iocfc_stats_timeout(void *bfa_arg); + +static struct bfa_ioc_cbfn_s bfa_iocfc_cbfn; + +/** + * bfa_ioc_pvt BFA IOC private functions + */ + +static void +bfa_iocfc_cqs_sz(struct bfa_iocfc_cfg_s *cfg, u32 *dm_len) +{ + int i, per_reqq_sz, per_rspq_sz; + + per_reqq_sz = BFA_ROUNDUP((cfg->drvcfg.num_reqq_elems * BFI_LMSG_SZ), + BFA_DMA_ALIGN_SZ); + per_rspq_sz = BFA_ROUNDUP((cfg->drvcfg.num_rspq_elems * BFI_LMSG_SZ), + BFA_DMA_ALIGN_SZ); + + /* + * Calculate CQ size + */ + for (i = 0; i < cfg->fwcfg.num_cqs; i++) { + *dm_len = *dm_len + per_reqq_sz; + *dm_len = *dm_len + per_rspq_sz; + } + + /* + * Calculate Shadow CI/PI size + */ + for (i = 0; i < cfg->fwcfg.num_cqs; i++) + *dm_len += (2 * BFA_CACHELINE_SZ); +} + +static void +bfa_iocfc_fw_cfg_sz(struct bfa_iocfc_cfg_s *cfg, u32 *dm_len) +{ + *dm_len += + BFA_ROUNDUP(sizeof(struct bfi_iocfc_cfg_s), BFA_CACHELINE_SZ); + *dm_len += + BFA_ROUNDUP(sizeof(struct bfi_iocfc_cfgrsp_s), + BFA_CACHELINE_SZ); + *dm_len += BFA_ROUNDUP(sizeof(struct bfa_fw_stats_s), BFA_CACHELINE_SZ); +} + +/** + * Use the Mailbox interface to send BFI_IOCFC_H2I_CFG_REQ + */ +static void +bfa_iocfc_send_cfg(void *bfa_arg) +{ + struct bfa_s *bfa = bfa_arg; + struct bfa_iocfc_s *iocfc = &bfa->iocfc; + struct bfi_iocfc_cfg_req_s cfg_req; + struct bfi_iocfc_cfg_s *cfg_info = iocfc->cfginfo; + struct bfa_iocfc_cfg_s *cfg = &iocfc->cfg; + int i; + + bfa_assert(cfg->fwcfg.num_cqs <= BFI_IOC_MAX_CQS); + bfa_trc(bfa, cfg->fwcfg.num_cqs); + + iocfc->cfgdone = BFA_FALSE; + bfa_iocfc_reset_queues(bfa); + + /** + * initialize IOC configuration info + */ + cfg_info->endian_sig = BFI_IOC_ENDIAN_SIG; + cfg_info->num_cqs = cfg->fwcfg.num_cqs; + + bfa_dma_be_addr_set(cfg_info->cfgrsp_addr, iocfc->cfgrsp_dma.pa); + bfa_dma_be_addr_set(cfg_info->stats_addr, iocfc->stats_pa); + + /** + * dma map REQ and RSP circular queues and shadow pointers + */ + for (i = 0; i < cfg->fwcfg.num_cqs; i++) { + bfa_dma_be_addr_set(cfg_info->req_cq_ba[i], + iocfc->req_cq_ba[i].pa); + bfa_dma_be_addr_set(cfg_info->req_shadow_ci[i], + iocfc->req_cq_shadow_ci[i].pa); + cfg_info->req_cq_elems[i] = + bfa_os_htons(cfg->drvcfg.num_reqq_elems); + + bfa_dma_be_addr_set(cfg_info->rsp_cq_ba[i], + iocfc->rsp_cq_ba[i].pa); + bfa_dma_be_addr_set(cfg_info->rsp_shadow_pi[i], + iocfc->rsp_cq_shadow_pi[i].pa); + cfg_info->rsp_cq_elems[i] = + bfa_os_htons(cfg->drvcfg.num_rspq_elems); + } + + /** + * dma map IOC configuration itself + */ + bfi_h2i_set(cfg_req.mh, BFI_MC_IOCFC, BFI_IOCFC_H2I_CFG_REQ, + bfa_lpuid(bfa)); + bfa_dma_be_addr_set(cfg_req.ioc_cfg_dma_addr, iocfc->cfg_info.pa); + + bfa_ioc_mbox_send(&bfa->ioc, &cfg_req, + sizeof(struct bfi_iocfc_cfg_req_s)); +} + +static void +bfa_iocfc_init_mem(struct bfa_s *bfa, void *bfad, struct bfa_iocfc_cfg_s *cfg, + struct bfa_pcidev_s *pcidev) +{ + struct bfa_iocfc_s *iocfc = &bfa->iocfc; + + bfa->bfad = bfad; + iocfc->bfa = bfa; + iocfc->action = BFA_IOCFC_ACT_NONE; + + bfa_os_assign(iocfc->cfg, *cfg); + + /** + * Initialize chip specific handlers. + */ + if (bfa_ioc_devid(&bfa->ioc) == BFA_PCI_DEVICE_ID_CT) { + iocfc->hwif.hw_reginit = bfa_hwct_reginit; + iocfc->hwif.hw_rspq_ack = bfa_hwct_rspq_ack; + iocfc->hwif.hw_msix_init = bfa_hwct_msix_init; + iocfc->hwif.hw_msix_install = bfa_hwct_msix_install; + iocfc->hwif.hw_msix_uninstall = bfa_hwct_msix_uninstall; + iocfc->hwif.hw_isr_mode_set = bfa_hwct_isr_mode_set; + iocfc->hwif.hw_msix_getvecs = bfa_hwct_msix_getvecs; + } else { + iocfc->hwif.hw_reginit = bfa_hwcb_reginit; + iocfc->hwif.hw_rspq_ack = bfa_hwcb_rspq_ack; + iocfc->hwif.hw_msix_init = bfa_hwcb_msix_init; + iocfc->hwif.hw_msix_install = bfa_hwcb_msix_install; + iocfc->hwif.hw_msix_uninstall = bfa_hwcb_msix_uninstall; + iocfc->hwif.hw_isr_mode_set = bfa_hwcb_isr_mode_set; + iocfc->hwif.hw_msix_getvecs = bfa_hwcb_msix_getvecs; + } + + iocfc->hwif.hw_reginit(bfa); + bfa->msix.nvecs = 0; +} + +static void +bfa_iocfc_mem_claim(struct bfa_s *bfa, struct bfa_iocfc_cfg_s *cfg, + struct bfa_meminfo_s *meminfo) +{ + u8 *dm_kva; + u64 dm_pa; + int i, per_reqq_sz, per_rspq_sz; + struct bfa_iocfc_s *iocfc = &bfa->iocfc; + int dbgsz; + + dm_kva = bfa_meminfo_dma_virt(meminfo); + dm_pa = bfa_meminfo_dma_phys(meminfo); + + /* + * First allocate dma memory for IOC. + */ + bfa_ioc_mem_claim(&bfa->ioc, dm_kva, dm_pa); + dm_kva += bfa_ioc_meminfo(); + dm_pa += bfa_ioc_meminfo(); + + /* + * Claim DMA-able memory for the request/response queues and for shadow + * ci/pi registers + */ + per_reqq_sz = BFA_ROUNDUP((cfg->drvcfg.num_reqq_elems * BFI_LMSG_SZ), + BFA_DMA_ALIGN_SZ); + per_rspq_sz = BFA_ROUNDUP((cfg->drvcfg.num_rspq_elems * BFI_LMSG_SZ), + BFA_DMA_ALIGN_SZ); + + for (i = 0; i < cfg->fwcfg.num_cqs; i++) { + iocfc->req_cq_ba[i].kva = dm_kva; + iocfc->req_cq_ba[i].pa = dm_pa; + bfa_os_memset(dm_kva, 0, per_reqq_sz); + dm_kva += per_reqq_sz; + dm_pa += per_reqq_sz; + + iocfc->rsp_cq_ba[i].kva = dm_kva; + iocfc->rsp_cq_ba[i].pa = dm_pa; + bfa_os_memset(dm_kva, 0, per_rspq_sz); + dm_kva += per_rspq_sz; + dm_pa += per_rspq_sz; + } + + for (i = 0; i < cfg->fwcfg.num_cqs; i++) { + iocfc->req_cq_shadow_ci[i].kva = dm_kva; + iocfc->req_cq_shadow_ci[i].pa = dm_pa; + dm_kva += BFA_CACHELINE_SZ; + dm_pa += BFA_CACHELINE_SZ; + + iocfc->rsp_cq_shadow_pi[i].kva = dm_kva; + iocfc->rsp_cq_shadow_pi[i].pa = dm_pa; + dm_kva += BFA_CACHELINE_SZ; + dm_pa += BFA_CACHELINE_SZ; + } + + /* + * Claim DMA-able memory for the config info page + */ + bfa->iocfc.cfg_info.kva = dm_kva; + bfa->iocfc.cfg_info.pa = dm_pa; + bfa->iocfc.cfginfo = (struct bfi_iocfc_cfg_s *) dm_kva; + dm_kva += BFA_ROUNDUP(sizeof(struct bfi_iocfc_cfg_s), BFA_CACHELINE_SZ); + dm_pa += BFA_ROUNDUP(sizeof(struct bfi_iocfc_cfg_s), BFA_CACHELINE_SZ); + + /* + * Claim DMA-able memory for the config response + */ + bfa->iocfc.cfgrsp_dma.kva = dm_kva; + bfa->iocfc.cfgrsp_dma.pa = dm_pa; + bfa->iocfc.cfgrsp = (struct bfi_iocfc_cfgrsp_s *) dm_kva; + + dm_kva += + BFA_ROUNDUP(sizeof(struct bfi_iocfc_cfgrsp_s), + BFA_CACHELINE_SZ); + dm_pa += BFA_ROUNDUP(sizeof(struct bfi_iocfc_cfgrsp_s), + BFA_CACHELINE_SZ); + + /* + * Claim DMA-able memory for iocfc stats + */ + bfa->iocfc.stats_kva = dm_kva; + bfa->iocfc.stats_pa = dm_pa; + bfa->iocfc.fw_stats = (struct bfa_fw_stats_s *) dm_kva; + dm_kva += BFA_ROUNDUP(sizeof(struct bfa_fw_stats_s), BFA_CACHELINE_SZ); + dm_pa += BFA_ROUNDUP(sizeof(struct bfa_fw_stats_s), BFA_CACHELINE_SZ); + + bfa_meminfo_dma_virt(meminfo) = dm_kva; + bfa_meminfo_dma_phys(meminfo) = dm_pa; + + dbgsz = bfa_ioc_debug_trcsz(bfa_auto_recover); + if (dbgsz > 0) { + bfa_ioc_debug_memclaim(&bfa->ioc, bfa_meminfo_kva(meminfo)); + bfa_meminfo_kva(meminfo) += dbgsz; + } +} + +/** + * BFA submodules initialization completion notification. + */ +static void +bfa_iocfc_initdone_submod(struct bfa_s *bfa) +{ + int i; + + for (i = 0; hal_mods[i]; i++) + hal_mods[i]->initdone(bfa); +} + +/** + * Start BFA submodules. + */ +static void +bfa_iocfc_start_submod(struct bfa_s *bfa) +{ + int i; + + bfa->rme_process = BFA_TRUE; + + for (i = 0; hal_mods[i]; i++) + hal_mods[i]->start(bfa); +} + +/** + * Disable BFA submodules. + */ +static void +bfa_iocfc_disable_submod(struct bfa_s *bfa) +{ + int i; + + for (i = 0; hal_mods[i]; i++) + hal_mods[i]->iocdisable(bfa); +} + +static void +bfa_iocfc_init_cb(void *bfa_arg, bfa_boolean_t complete) +{ + struct bfa_s *bfa = bfa_arg; + + if (complete) { + if (bfa->iocfc.cfgdone) + bfa_cb_init(bfa->bfad, BFA_STATUS_OK); + else + bfa_cb_init(bfa->bfad, BFA_STATUS_FAILED); + } else + bfa->iocfc.action = BFA_IOCFC_ACT_NONE; +} + +static void +bfa_iocfc_stop_cb(void *bfa_arg, bfa_boolean_t compl) +{ + struct bfa_s *bfa = bfa_arg; + struct bfad_s *bfad = bfa->bfad; + + if (compl) + complete(&bfad->comp); + + else + bfa->iocfc.action = BFA_IOCFC_ACT_NONE; +} + +static void +bfa_iocfc_disable_cb(void *bfa_arg, bfa_boolean_t compl) +{ + struct bfa_s *bfa = bfa_arg; + struct bfad_s *bfad = bfa->bfad; + + if (compl) + complete(&bfad->disable_comp); +} + +/** + * Update BFA configuration from firmware configuration. + */ +static void +bfa_iocfc_cfgrsp(struct bfa_s *bfa) +{ + struct bfa_iocfc_s *iocfc = &bfa->iocfc; + struct bfi_iocfc_cfgrsp_s *cfgrsp = iocfc->cfgrsp; + struct bfa_iocfc_fwcfg_s *fwcfg = &cfgrsp->fwcfg; + struct bfi_iocfc_cfg_s *cfginfo = iocfc->cfginfo; + + fwcfg->num_cqs = fwcfg->num_cqs; + fwcfg->num_ioim_reqs = bfa_os_ntohs(fwcfg->num_ioim_reqs); + fwcfg->num_tskim_reqs = bfa_os_ntohs(fwcfg->num_tskim_reqs); + fwcfg->num_fcxp_reqs = bfa_os_ntohs(fwcfg->num_fcxp_reqs); + fwcfg->num_uf_bufs = bfa_os_ntohs(fwcfg->num_uf_bufs); + fwcfg->num_rports = bfa_os_ntohs(fwcfg->num_rports); + + cfginfo->intr_attr.coalesce = cfgrsp->intr_attr.coalesce; + cfginfo->intr_attr.delay = bfa_os_ntohs(cfgrsp->intr_attr.delay); + cfginfo->intr_attr.latency = bfa_os_ntohs(cfgrsp->intr_attr.latency); + + iocfc->cfgdone = BFA_TRUE; + + /** + * Configuration is complete - initialize/start submodules + */ + if (iocfc->action == BFA_IOCFC_ACT_INIT) + bfa_cb_queue(bfa, &iocfc->init_hcb_qe, bfa_iocfc_init_cb, bfa); + else + bfa_iocfc_start_submod(bfa); +} + +static void +bfa_iocfc_stats_clear(void *bfa_arg) +{ + struct bfa_s *bfa = bfa_arg; + struct bfa_iocfc_s *iocfc = &bfa->iocfc; + struct bfi_iocfc_stats_req_s stats_req; + + bfa_timer_start(bfa, &iocfc->stats_timer, + bfa_iocfc_stats_clr_timeout, bfa, + BFA_IOCFC_TOV); + + bfi_h2i_set(stats_req.mh, BFI_MC_IOCFC, BFI_IOCFC_H2I_CLEAR_STATS_REQ, + bfa_lpuid(bfa)); + bfa_ioc_mbox_send(&bfa->ioc, &stats_req, + sizeof(struct bfi_iocfc_stats_req_s)); +} + +static void +bfa_iocfc_stats_swap(struct bfa_fw_stats_s *d, struct bfa_fw_stats_s *s) +{ + u32 *dip = (u32 *) d; + u32 *sip = (u32 *) s; + int i; + + for (i = 0; i < (sizeof(struct bfa_fw_stats_s) / sizeof(u32)); i++) + dip[i] = bfa_os_ntohl(sip[i]); +} + +static void +bfa_iocfc_stats_clr_cb(void *bfa_arg, bfa_boolean_t complete) +{ + struct bfa_s *bfa = bfa_arg; + struct bfa_iocfc_s *iocfc = &bfa->iocfc; + + if (complete) { + bfa_ioc_clr_stats(&bfa->ioc); + iocfc->stats_cbfn(iocfc->stats_cbarg, iocfc->stats_status); + } else { + iocfc->stats_busy = BFA_FALSE; + iocfc->stats_status = BFA_STATUS_OK; + } +} + +static void +bfa_iocfc_stats_clr_timeout(void *bfa_arg) +{ + struct bfa_s *bfa = bfa_arg; + struct bfa_iocfc_s *iocfc = &bfa->iocfc; + + bfa_trc(bfa, 0); + + iocfc->stats_status = BFA_STATUS_ETIMER; + bfa_cb_queue(bfa, &iocfc->stats_hcb_qe, bfa_iocfc_stats_clr_cb, bfa); +} + +static void +bfa_iocfc_stats_cb(void *bfa_arg, bfa_boolean_t complete) +{ + struct bfa_s *bfa = bfa_arg; + struct bfa_iocfc_s *iocfc = &bfa->iocfc; + + if (complete) { + if (iocfc->stats_status == BFA_STATUS_OK) { + bfa_os_memset(iocfc->stats_ret, 0, + sizeof(*iocfc->stats_ret)); + bfa_iocfc_stats_swap(&iocfc->stats_ret->fw_stats, + iocfc->fw_stats); + } + iocfc->stats_cbfn(iocfc->stats_cbarg, iocfc->stats_status); + } else { + iocfc->stats_busy = BFA_FALSE; + iocfc->stats_status = BFA_STATUS_OK; + } +} + +static void +bfa_iocfc_stats_timeout(void *bfa_arg) +{ + struct bfa_s *bfa = bfa_arg; + struct bfa_iocfc_s *iocfc = &bfa->iocfc; + + bfa_trc(bfa, 0); + + iocfc->stats_status = BFA_STATUS_ETIMER; + bfa_cb_queue(bfa, &iocfc->stats_hcb_qe, bfa_iocfc_stats_cb, bfa); +} + +static void +bfa_iocfc_stats_query(struct bfa_s *bfa) +{ + struct bfa_iocfc_s *iocfc = &bfa->iocfc; + struct bfi_iocfc_stats_req_s stats_req; + + bfa_timer_start(bfa, &iocfc->stats_timer, + bfa_iocfc_stats_timeout, bfa, BFA_IOCFC_TOV); + + bfi_h2i_set(stats_req.mh, BFI_MC_IOCFC, BFI_IOCFC_H2I_GET_STATS_REQ, + bfa_lpuid(bfa)); + bfa_ioc_mbox_send(&bfa->ioc, &stats_req, + sizeof(struct bfi_iocfc_stats_req_s)); +} + +void +bfa_iocfc_reset_queues(struct bfa_s *bfa) +{ + int q; + + for (q = 0; q < BFI_IOC_MAX_CQS; q++) { + bfa_reqq_ci(bfa, q) = 0; + bfa_reqq_pi(bfa, q) = 0; + bfa_rspq_ci(bfa, q) = 0; + bfa_rspq_pi(bfa, q) = 0; + } +} + +/** + * IOC enable request is complete + */ +static void +bfa_iocfc_enable_cbfn(void *bfa_arg, enum bfa_status status) +{ + struct bfa_s *bfa = bfa_arg; + + if (status != BFA_STATUS_OK) { + bfa_isr_disable(bfa); + if (bfa->iocfc.action == BFA_IOCFC_ACT_INIT) + bfa_cb_queue(bfa, &bfa->iocfc.init_hcb_qe, + bfa_iocfc_init_cb, bfa); + return; + } + + bfa_iocfc_initdone_submod(bfa); + bfa_iocfc_send_cfg(bfa); +} + +/** + * IOC disable request is complete + */ +static void +bfa_iocfc_disable_cbfn(void *bfa_arg) +{ + struct bfa_s *bfa = bfa_arg; + + bfa_isr_disable(bfa); + bfa_iocfc_disable_submod(bfa); + + if (bfa->iocfc.action == BFA_IOCFC_ACT_STOP) + bfa_cb_queue(bfa, &bfa->iocfc.stop_hcb_qe, bfa_iocfc_stop_cb, + bfa); + else { + bfa_assert(bfa->iocfc.action == BFA_IOCFC_ACT_DISABLE); + bfa_cb_queue(bfa, &bfa->iocfc.dis_hcb_qe, bfa_iocfc_disable_cb, + bfa); + } +} + +/** + * Notify sub-modules of hardware failure. + */ +static void +bfa_iocfc_hbfail_cbfn(void *bfa_arg) +{ + struct bfa_s *bfa = bfa_arg; + + bfa->rme_process = BFA_FALSE; + + bfa_isr_disable(bfa); + bfa_iocfc_disable_submod(bfa); + + if (bfa->iocfc.action == BFA_IOCFC_ACT_INIT) + bfa_cb_queue(bfa, &bfa->iocfc.init_hcb_qe, bfa_iocfc_init_cb, + bfa); +} + +/** + * Actions on chip-reset completion. + */ +static void +bfa_iocfc_reset_cbfn(void *bfa_arg) +{ + struct bfa_s *bfa = bfa_arg; + + bfa_iocfc_reset_queues(bfa); + bfa_isr_enable(bfa); +} + + + +/** + * bfa_ioc_public + */ + +/** + * Query IOC memory requirement information. + */ +void +bfa_iocfc_meminfo(struct bfa_iocfc_cfg_s *cfg, u32 *km_len, + u32 *dm_len) +{ + /* dma memory for IOC */ + *dm_len += bfa_ioc_meminfo(); + + bfa_iocfc_fw_cfg_sz(cfg, dm_len); + bfa_iocfc_cqs_sz(cfg, dm_len); + *km_len += bfa_ioc_debug_trcsz(bfa_auto_recover); +} + +/** + * Query IOC memory requirement information. + */ +void +bfa_iocfc_attach(struct bfa_s *bfa, void *bfad, struct bfa_iocfc_cfg_s *cfg, + struct bfa_meminfo_s *meminfo, struct bfa_pcidev_s *pcidev) +{ + int i; + + bfa_iocfc_cbfn.enable_cbfn = bfa_iocfc_enable_cbfn; + bfa_iocfc_cbfn.disable_cbfn = bfa_iocfc_disable_cbfn; + bfa_iocfc_cbfn.hbfail_cbfn = bfa_iocfc_hbfail_cbfn; + bfa_iocfc_cbfn.reset_cbfn = bfa_iocfc_reset_cbfn; + + bfa_ioc_attach(&bfa->ioc, bfa, &bfa_iocfc_cbfn, &bfa->timer_mod, + bfa->trcmod, bfa->aen, bfa->logm); + bfa_ioc_pci_init(&bfa->ioc, pcidev, BFI_MC_IOCFC); + bfa_ioc_mbox_register(&bfa->ioc, bfa_mbox_isrs); + + /** + * Choose FC (ssid: 0x1C) v/s FCoE (ssid: 0x14) mode. + */ + if (0) + bfa_ioc_set_fcmode(&bfa->ioc); + + bfa_iocfc_init_mem(bfa, bfad, cfg, pcidev); + bfa_iocfc_mem_claim(bfa, cfg, meminfo); + bfa_timer_init(&bfa->timer_mod); + + INIT_LIST_HEAD(&bfa->comp_q); + for (i = 0; i < BFI_IOC_MAX_CQS; i++) + INIT_LIST_HEAD(&bfa->reqq_waitq[i]); +} + +/** + * Query IOC memory requirement information. + */ +void +bfa_iocfc_detach(struct bfa_s *bfa) +{ + bfa_ioc_detach(&bfa->ioc); +} + +/** + * Query IOC memory requirement information. + */ +void +bfa_iocfc_init(struct bfa_s *bfa) +{ + bfa->iocfc.action = BFA_IOCFC_ACT_INIT; + bfa_ioc_enable(&bfa->ioc); + bfa_msix_install(bfa); +} + +/** + * IOC start called from bfa_start(). Called to start IOC operations + * at driver instantiation for this instance. + */ +void +bfa_iocfc_start(struct bfa_s *bfa) +{ + if (bfa->iocfc.cfgdone) + bfa_iocfc_start_submod(bfa); +} + +/** + * IOC stop called from bfa_stop(). Called only when driver is unloaded + * for this instance. + */ +void +bfa_iocfc_stop(struct bfa_s *bfa) +{ + bfa->iocfc.action = BFA_IOCFC_ACT_STOP; + + bfa->rme_process = BFA_FALSE; + bfa_ioc_disable(&bfa->ioc); +} + +void +bfa_iocfc_isr(void *bfaarg, struct bfi_mbmsg_s *m) +{ + struct bfa_s *bfa = bfaarg; + struct bfa_iocfc_s *iocfc = &bfa->iocfc; + union bfi_iocfc_i2h_msg_u *msg; + + msg = (union bfi_iocfc_i2h_msg_u *) m; + bfa_trc(bfa, msg->mh.msg_id); + + switch (msg->mh.msg_id) { + case BFI_IOCFC_I2H_CFG_REPLY: + iocfc->cfg_reply = &msg->cfg_reply; + bfa_iocfc_cfgrsp(bfa); + break; + + case BFI_IOCFC_I2H_GET_STATS_RSP: + if (iocfc->stats_busy == BFA_FALSE + || iocfc->stats_status == BFA_STATUS_ETIMER) + break; + + bfa_timer_stop(&iocfc->stats_timer); + iocfc->stats_status = BFA_STATUS_OK; + bfa_cb_queue(bfa, &iocfc->stats_hcb_qe, bfa_iocfc_stats_cb, + bfa); + break; + case BFI_IOCFC_I2H_CLEAR_STATS_RSP: + /* + * check for timer pop before processing the rsp + */ + if (iocfc->stats_busy == BFA_FALSE + || iocfc->stats_status == BFA_STATUS_ETIMER) + break; + + bfa_timer_stop(&iocfc->stats_timer); + iocfc->stats_status = BFA_STATUS_OK; + bfa_cb_queue(bfa, &iocfc->stats_hcb_qe, + bfa_iocfc_stats_clr_cb, bfa); + break; + case BFI_IOCFC_I2H_UPDATEQ_RSP: + iocfc->updateq_cbfn(iocfc->updateq_cbarg, BFA_STATUS_OK); + break; + default: + bfa_assert(0); + } +} + +#ifndef BFA_BIOS_BUILD +void +bfa_adapter_get_attr(struct bfa_s *bfa, struct bfa_adapter_attr_s *ad_attr) +{ + bfa_ioc_get_adapter_attr(&bfa->ioc, ad_attr); +} + +u64 +bfa_adapter_get_id(struct bfa_s *bfa) +{ + return bfa_ioc_get_adid(&bfa->ioc); +} + +void +bfa_iocfc_get_attr(struct bfa_s *bfa, struct bfa_iocfc_attr_s *attr) +{ + struct bfa_iocfc_s *iocfc = &bfa->iocfc; + + attr->intr_attr = iocfc->cfginfo->intr_attr; + attr->config = iocfc->cfg; +} + +bfa_status_t +bfa_iocfc_israttr_set(struct bfa_s *bfa, struct bfa_iocfc_intr_attr_s *attr) +{ + struct bfa_iocfc_s *iocfc = &bfa->iocfc; + struct bfi_iocfc_set_intr_req_s *m; + + iocfc->cfginfo->intr_attr = *attr; + if (!bfa_iocfc_is_operational(bfa)) + return BFA_STATUS_OK; + + m = bfa_reqq_next(bfa, BFA_REQQ_IOC); + if (!m) + return BFA_STATUS_DEVBUSY; + + bfi_h2i_set(m->mh, BFI_MC_IOCFC, BFI_IOCFC_H2I_SET_INTR_REQ, + bfa_lpuid(bfa)); + m->coalesce = attr->coalesce; + m->delay = bfa_os_htons(attr->delay); + m->latency = bfa_os_htons(attr->latency); + + bfa_trc(bfa, attr->delay); + bfa_trc(bfa, attr->latency); + + bfa_reqq_produce(bfa, BFA_REQQ_IOC); + return BFA_STATUS_OK; +} + +void +bfa_iocfc_set_snsbase(struct bfa_s *bfa, u64 snsbase_pa) +{ + struct bfa_iocfc_s *iocfc = &bfa->iocfc; + + iocfc->cfginfo->sense_buf_len = (BFI_IOIM_SNSLEN - 1); + bfa_dma_be_addr_set(iocfc->cfginfo->ioim_snsbase, snsbase_pa); +} + +bfa_status_t +bfa_iocfc_get_stats(struct bfa_s *bfa, struct bfa_iocfc_stats_s *stats, + bfa_cb_ioc_t cbfn, void *cbarg) +{ + struct bfa_iocfc_s *iocfc = &bfa->iocfc; + + if (iocfc->stats_busy) { + bfa_trc(bfa, iocfc->stats_busy); + return (BFA_STATUS_DEVBUSY); + } + + iocfc->stats_busy = BFA_TRUE; + iocfc->stats_ret = stats; + iocfc->stats_cbfn = cbfn; + iocfc->stats_cbarg = cbarg; + + bfa_iocfc_stats_query(bfa); + + return (BFA_STATUS_OK); +} + +bfa_status_t +bfa_iocfc_clear_stats(struct bfa_s *bfa, bfa_cb_ioc_t cbfn, void *cbarg) +{ + struct bfa_iocfc_s *iocfc = &bfa->iocfc; + + if (iocfc->stats_busy) { + bfa_trc(bfa, iocfc->stats_busy); + return (BFA_STATUS_DEVBUSY); + } + + iocfc->stats_busy = BFA_TRUE; + iocfc->stats_cbfn = cbfn; + iocfc->stats_cbarg = cbarg; + + bfa_iocfc_stats_clear(bfa); + return (BFA_STATUS_OK); +} + +/** + * Enable IOC after it is disabled. + */ +void +bfa_iocfc_enable(struct bfa_s *bfa) +{ + bfa_plog_str(bfa->plog, BFA_PL_MID_HAL, BFA_PL_EID_MISC, 0, + "IOC Enable"); + bfa_ioc_enable(&bfa->ioc); +} + +void +bfa_iocfc_disable(struct bfa_s *bfa) +{ + bfa_plog_str(bfa->plog, BFA_PL_MID_HAL, BFA_PL_EID_MISC, 0, + "IOC Disable"); + bfa->iocfc.action = BFA_IOCFC_ACT_DISABLE; + + bfa->rme_process = BFA_FALSE; + bfa_ioc_disable(&bfa->ioc); +} + + +bfa_boolean_t +bfa_iocfc_is_operational(struct bfa_s *bfa) +{ + return bfa_ioc_is_operational(&bfa->ioc) && bfa->iocfc.cfgdone; +} + +/** + * Return boot target port wwns -- read from boot information in flash. + */ +void +bfa_iocfc_get_bootwwns(struct bfa_s *bfa, u8 *nwwns, wwn_t **wwns) +{ + struct bfa_iocfc_s *iocfc = &bfa->iocfc; + struct bfi_iocfc_cfgrsp_s *cfgrsp = iocfc->cfgrsp; + + *nwwns = cfgrsp->bootwwns.nwwns; + *wwns = cfgrsp->bootwwns.wwn; +} + +#endif + + diff --git a/drivers/scsi/bfa/bfa_iocfc.h b/drivers/scsi/bfa/bfa_iocfc.h new file mode 100644 index 000000000000..7ad177ed4cfc --- /dev/null +++ b/drivers/scsi/bfa/bfa_iocfc.h @@ -0,0 +1,168 @@ +/* + * Copyright (c) 2005-2009 Brocade Communications Systems, Inc. + * All rights reserved + * www.brocade.com + * + * Linux driver for Brocade Fibre Channel Host Bus Adapter. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (GPL) Version 2 as + * published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#ifndef __BFA_IOCFC_H__ +#define __BFA_IOCFC_H__ + +#include <bfa_ioc.h> +#include <bfa.h> +#include <bfi/bfi_iocfc.h> +#include <bfa_callback_priv.h> + +#define BFA_REQQ_NELEMS_MIN (4) +#define BFA_RSPQ_NELEMS_MIN (4) + +struct bfa_iocfc_regs_s { + bfa_os_addr_t intr_status; + bfa_os_addr_t intr_mask; + bfa_os_addr_t cpe_q_pi[BFI_IOC_MAX_CQS]; + bfa_os_addr_t cpe_q_ci[BFI_IOC_MAX_CQS]; + bfa_os_addr_t cpe_q_depth[BFI_IOC_MAX_CQS]; + bfa_os_addr_t cpe_q_ctrl[BFI_IOC_MAX_CQS]; + bfa_os_addr_t rme_q_ci[BFI_IOC_MAX_CQS]; + bfa_os_addr_t rme_q_pi[BFI_IOC_MAX_CQS]; + bfa_os_addr_t rme_q_depth[BFI_IOC_MAX_CQS]; + bfa_os_addr_t rme_q_ctrl[BFI_IOC_MAX_CQS]; +}; + +/** + * MSIX vector handlers + */ +#define BFA_MSIX_MAX_VECTORS 22 +typedef void (*bfa_msix_handler_t)(struct bfa_s *bfa, int vec); +struct bfa_msix_s { + int nvecs; + bfa_msix_handler_t handler[BFA_MSIX_MAX_VECTORS]; +}; + +/** + * Chip specific interfaces + */ +struct bfa_hwif_s { + void (*hw_reginit)(struct bfa_s *bfa); + void (*hw_rspq_ack)(struct bfa_s *bfa, int rspq); + void (*hw_msix_init)(struct bfa_s *bfa, int nvecs); + void (*hw_msix_install)(struct bfa_s *bfa); + void (*hw_msix_uninstall)(struct bfa_s *bfa); + void (*hw_isr_mode_set)(struct bfa_s *bfa, bfa_boolean_t msix); + void (*hw_msix_getvecs)(struct bfa_s *bfa, u32 *vecmap, + u32 *nvecs, u32 *maxvec); +}; +typedef void (*bfa_cb_iocfc_t) (void *cbarg, enum bfa_status status); + +struct bfa_iocfc_s { + struct bfa_s *bfa; + struct bfa_iocfc_cfg_s cfg; + int action; + + u32 req_cq_pi[BFI_IOC_MAX_CQS]; + u32 rsp_cq_ci[BFI_IOC_MAX_CQS]; + + struct bfa_cb_qe_s init_hcb_qe; + struct bfa_cb_qe_s stop_hcb_qe; + struct bfa_cb_qe_s dis_hcb_qe; + struct bfa_cb_qe_s stats_hcb_qe; + bfa_boolean_t cfgdone; + + struct bfa_dma_s cfg_info; + struct bfi_iocfc_cfg_s *cfginfo; + struct bfa_dma_s cfgrsp_dma; + struct bfi_iocfc_cfgrsp_s *cfgrsp; + struct bfi_iocfc_cfg_reply_s *cfg_reply; + + u8 *stats_kva; + u64 stats_pa; + struct bfa_fw_stats_s *fw_stats; + struct bfa_timer_s stats_timer; /* timer */ + struct bfa_iocfc_stats_s *stats_ret; /* driver stats location */ + bfa_status_t stats_status; /* stats/statsclr status */ + bfa_boolean_t stats_busy; /* outstanding stats */ + bfa_cb_ioc_t stats_cbfn; /* driver callback function */ + void *stats_cbarg; /* user callback arg */ + + struct bfa_dma_s req_cq_ba[BFI_IOC_MAX_CQS]; + struct bfa_dma_s req_cq_shadow_ci[BFI_IOC_MAX_CQS]; + struct bfa_dma_s rsp_cq_ba[BFI_IOC_MAX_CQS]; + struct bfa_dma_s rsp_cq_shadow_pi[BFI_IOC_MAX_CQS]; + struct bfa_iocfc_regs_s bfa_regs; /* BFA device registers */ + struct bfa_hwif_s hwif; + + bfa_cb_iocfc_t updateq_cbfn; /* bios callback function */ + void *updateq_cbarg; /* bios callback arg */ +}; + +#define bfa_lpuid(__bfa) bfa_ioc_portid(&(__bfa)->ioc) +#define bfa_msix_init(__bfa, __nvecs) \ + (__bfa)->iocfc.hwif.hw_msix_init(__bfa, __nvecs) +#define bfa_msix_install(__bfa) \ + (__bfa)->iocfc.hwif.hw_msix_install(__bfa) +#define bfa_msix_uninstall(__bfa) \ + (__bfa)->iocfc.hwif.hw_msix_uninstall(__bfa) +#define bfa_isr_mode_set(__bfa, __msix) \ + (__bfa)->iocfc.hwif.hw_isr_mode_set(__bfa, __msix) +#define bfa_msix_getvecs(__bfa, __vecmap, __nvecs, __maxvec) \ + (__bfa)->iocfc.hwif.hw_msix_getvecs(__bfa, __vecmap, __nvecs, __maxvec) + +/* + * FC specific IOC functions. + */ +void bfa_iocfc_meminfo(struct bfa_iocfc_cfg_s *cfg, u32 *km_len, + u32 *dm_len); +void bfa_iocfc_attach(struct bfa_s *bfa, void *bfad, + struct bfa_iocfc_cfg_s *cfg, struct bfa_meminfo_s *meminfo, + struct bfa_pcidev_s *pcidev); +void bfa_iocfc_detach(struct bfa_s *bfa); +void bfa_iocfc_init(struct bfa_s *bfa); +void bfa_iocfc_start(struct bfa_s *bfa); +void bfa_iocfc_stop(struct bfa_s *bfa); +void bfa_iocfc_isr(void *bfa, struct bfi_mbmsg_s *msg); +void bfa_iocfc_set_snsbase(struct bfa_s *bfa, u64 snsbase_pa); +bfa_boolean_t bfa_iocfc_is_operational(struct bfa_s *bfa); +void bfa_iocfc_reset_queues(struct bfa_s *bfa); +void bfa_iocfc_updateq(struct bfa_s *bfa, u32 reqq_ba, u32 rspq_ba, + u32 reqq_sci, u32 rspq_spi, + bfa_cb_iocfc_t cbfn, void *cbarg); + +void bfa_msix_all(struct bfa_s *bfa, int vec); +void bfa_msix_reqq(struct bfa_s *bfa, int vec); +void bfa_msix_rspq(struct bfa_s *bfa, int vec); +void bfa_msix_lpu_err(struct bfa_s *bfa, int vec); + +void bfa_hwcb_reginit(struct bfa_s *bfa); +void bfa_hwcb_rspq_ack(struct bfa_s *bfa, int rspq); +void bfa_hwcb_msix_init(struct bfa_s *bfa, int nvecs); +void bfa_hwcb_msix_install(struct bfa_s *bfa); +void bfa_hwcb_msix_uninstall(struct bfa_s *bfa); +void bfa_hwcb_isr_mode_set(struct bfa_s *bfa, bfa_boolean_t msix); +void bfa_hwcb_msix_getvecs(struct bfa_s *bfa, u32 *vecmap, + u32 *nvecs, u32 *maxvec); +void bfa_hwct_reginit(struct bfa_s *bfa); +void bfa_hwct_rspq_ack(struct bfa_s *bfa, int rspq); +void bfa_hwct_msix_init(struct bfa_s *bfa, int nvecs); +void bfa_hwct_msix_install(struct bfa_s *bfa); +void bfa_hwct_msix_uninstall(struct bfa_s *bfa); +void bfa_hwct_isr_mode_set(struct bfa_s *bfa, bfa_boolean_t msix); +void bfa_hwct_msix_getvecs(struct bfa_s *bfa, u32 *vecmap, + u32 *nvecs, u32 *maxvec); + +void bfa_com_meminfo(bfa_boolean_t mincfg, u32 *dm_len); +void bfa_com_attach(struct bfa_s *bfa, struct bfa_meminfo_s *mi, + bfa_boolean_t mincfg); +void bfa_iocfc_get_bootwwns(struct bfa_s *bfa, u8 *nwwns, wwn_t **wwns); + +#endif /* __BFA_IOCFC_H__ */ + diff --git a/drivers/scsi/bfa/bfa_iocfc_q.c b/drivers/scsi/bfa/bfa_iocfc_q.c new file mode 100644 index 000000000000..500a17df40b2 --- /dev/null +++ b/drivers/scsi/bfa/bfa_iocfc_q.c @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2005-2009 Brocade Communications Systems, Inc. + * All rights reserved + * www.brocade.com + * + * Linux driver for Brocade Fibre Channel Host Bus Adapter. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (GPL) Version 2 as + * published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#include <bfa.h> +#include "bfa_intr_priv.h" + +BFA_TRC_FILE(HAL, IOCFC_Q); + +void +bfa_iocfc_updateq(struct bfa_s *bfa, u32 reqq_ba, u32 rspq_ba, + u32 reqq_sci, u32 rspq_spi, bfa_cb_iocfc_t cbfn, + void *cbarg) +{ + struct bfa_iocfc_s *iocfc = &bfa->iocfc; + struct bfi_iocfc_updateq_req_s updateq_req; + + iocfc->updateq_cbfn = cbfn; + iocfc->updateq_cbarg = cbarg; + + bfi_h2i_set(updateq_req.mh, BFI_MC_IOCFC, BFI_IOCFC_H2I_UPDATEQ_REQ, + bfa_lpuid(bfa)); + + updateq_req.reqq_ba = bfa_os_htonl(reqq_ba); + updateq_req.rspq_ba = bfa_os_htonl(rspq_ba); + updateq_req.reqq_sci = bfa_os_htonl(reqq_sci); + updateq_req.rspq_spi = bfa_os_htonl(rspq_spi); + + bfa_ioc_mbox_send(&bfa->ioc, &updateq_req, + sizeof(struct bfi_iocfc_updateq_req_s)); +} diff --git a/drivers/scsi/bfa/bfa_ioim.c b/drivers/scsi/bfa/bfa_ioim.c new file mode 100644 index 000000000000..7ae2552e1e14 --- /dev/null +++ b/drivers/scsi/bfa/bfa_ioim.c @@ -0,0 +1,1311 @@ +/* + * Copyright (c) 2005-2009 Brocade Communications Systems, Inc. + * All rights reserved + * www.brocade.com + * + * Linux driver for Brocade Fibre Channel Host Bus Adapter. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (GPL) Version 2 as + * published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#include <bfa.h> +#include <cs/bfa_debug.h> +#include <bfa_cb_ioim_macros.h> + +BFA_TRC_FILE(HAL, IOIM); + +/* + * forward declarations. + */ +static bfa_boolean_t bfa_ioim_send_ioreq(struct bfa_ioim_s *ioim); +static bfa_boolean_t bfa_ioim_sge_setup(struct bfa_ioim_s *ioim); +static void bfa_ioim_sgpg_setup(struct bfa_ioim_s *ioim); +static bfa_boolean_t bfa_ioim_send_abort(struct bfa_ioim_s *ioim); +static void bfa_ioim_notify_cleanup(struct bfa_ioim_s *ioim); +static void __bfa_cb_ioim_good_comp(void *cbarg, bfa_boolean_t complete); +static void __bfa_cb_ioim_comp(void *cbarg, bfa_boolean_t complete); +static void __bfa_cb_ioim_abort(void *cbarg, bfa_boolean_t complete); +static void __bfa_cb_ioim_failed(void *cbarg, bfa_boolean_t complete); +static void __bfa_cb_ioim_pathtov(void *cbarg, bfa_boolean_t complete); + +/** + * bfa_ioim_sm + */ + +/** + * IO state machine events + */ +enum bfa_ioim_event { + BFA_IOIM_SM_START = 1, /* io start request from host */ + BFA_IOIM_SM_COMP_GOOD = 2, /* io good comp, resource free */ + BFA_IOIM_SM_COMP = 3, /* io comp, resource is free */ + BFA_IOIM_SM_COMP_UTAG = 4, /* io comp, resource is free */ + BFA_IOIM_SM_DONE = 5, /* io comp, resource not free */ + BFA_IOIM_SM_FREE = 6, /* io resource is freed */ + BFA_IOIM_SM_ABORT = 7, /* abort request from scsi stack */ + BFA_IOIM_SM_ABORT_COMP = 8, /* abort from f/w */ + BFA_IOIM_SM_ABORT_DONE = 9, /* abort completion from f/w */ + BFA_IOIM_SM_QRESUME = 10, /* CQ space available to queue IO */ + BFA_IOIM_SM_SGALLOCED = 11, /* SG page allocation successful */ + BFA_IOIM_SM_SQRETRY = 12, /* sequence recovery retry */ + BFA_IOIM_SM_HCB = 13, /* bfa callback complete */ + BFA_IOIM_SM_CLEANUP = 14, /* IO cleanup from itnim */ + BFA_IOIM_SM_TMSTART = 15, /* IO cleanup from tskim */ + BFA_IOIM_SM_TMDONE = 16, /* IO cleanup from tskim */ + BFA_IOIM_SM_HWFAIL = 17, /* IOC h/w failure event */ + BFA_IOIM_SM_IOTOV = 18, /* ITN offline TOV */ +}; + +/* + * forward declaration of IO state machine + */ +static void bfa_ioim_sm_uninit(struct bfa_ioim_s *ioim, + enum bfa_ioim_event event); +static void bfa_ioim_sm_sgalloc(struct bfa_ioim_s *ioim, + enum bfa_ioim_event event); +static void bfa_ioim_sm_active(struct bfa_ioim_s *ioim, + enum bfa_ioim_event event); +static void bfa_ioim_sm_abort(struct bfa_ioim_s *ioim, + enum bfa_ioim_event event); +static void bfa_ioim_sm_cleanup(struct bfa_ioim_s *ioim, + enum bfa_ioim_event event); +static void bfa_ioim_sm_qfull(struct bfa_ioim_s *ioim, + enum bfa_ioim_event event); +static void bfa_ioim_sm_abort_qfull(struct bfa_ioim_s *ioim, + enum bfa_ioim_event event); +static void bfa_ioim_sm_cleanup_qfull(struct bfa_ioim_s *ioim, + enum bfa_ioim_event event); +static void bfa_ioim_sm_hcb(struct bfa_ioim_s *ioim, + enum bfa_ioim_event event); +static void bfa_ioim_sm_hcb_free(struct bfa_ioim_s *ioim, + enum bfa_ioim_event event); +static void bfa_ioim_sm_resfree(struct bfa_ioim_s *ioim, + enum bfa_ioim_event event); + +/** + * IO is not started (unallocated). + */ +static void +bfa_ioim_sm_uninit(struct bfa_ioim_s *ioim, enum bfa_ioim_event event) +{ + bfa_trc_fp(ioim->bfa, ioim->iotag); + bfa_trc_fp(ioim->bfa, event); + + switch (event) { + case BFA_IOIM_SM_START: + if (!bfa_itnim_is_online(ioim->itnim)) { + if (!bfa_itnim_hold_io(ioim->itnim)) { + bfa_sm_set_state(ioim, bfa_ioim_sm_hcb); + list_del(&ioim->qe); + list_add_tail(&ioim->qe, + &ioim->fcpim->ioim_comp_q); + bfa_cb_queue(ioim->bfa, &ioim->hcb_qe, + __bfa_cb_ioim_pathtov, ioim); + } else { + list_del(&ioim->qe); + list_add_tail(&ioim->qe, + &ioim->itnim->pending_q); + } + break; + } + + if (ioim->nsges > BFI_SGE_INLINE) { + if (!bfa_ioim_sge_setup(ioim)) { + bfa_sm_set_state(ioim, bfa_ioim_sm_sgalloc); + return; + } + } + + if (!bfa_ioim_send_ioreq(ioim)) { + bfa_sm_set_state(ioim, bfa_ioim_sm_qfull); + break; + } + + bfa_sm_set_state(ioim, bfa_ioim_sm_active); + break; + + case BFA_IOIM_SM_IOTOV: + bfa_sm_set_state(ioim, bfa_ioim_sm_hcb); + bfa_cb_queue(ioim->bfa, &ioim->hcb_qe, + __bfa_cb_ioim_pathtov, ioim); + break; + + case BFA_IOIM_SM_ABORT: + /** + * IO in pending queue can get abort requests. Complete abort + * requests immediately. + */ + bfa_sm_set_state(ioim, bfa_ioim_sm_hcb); + bfa_assert(bfa_q_is_on_q(&ioim->itnim->pending_q, ioim)); + bfa_cb_queue(ioim->bfa, &ioim->hcb_qe, __bfa_cb_ioim_abort, + ioim); + break; + + default: + bfa_assert(0); + } +} + +/** + * IO is waiting for SG pages. + */ +static void +bfa_ioim_sm_sgalloc(struct bfa_ioim_s *ioim, enum bfa_ioim_event event) +{ + bfa_trc(ioim->bfa, ioim->iotag); + bfa_trc(ioim->bfa, event); + + switch (event) { + case BFA_IOIM_SM_SGALLOCED: + if (!bfa_ioim_send_ioreq(ioim)) { + bfa_sm_set_state(ioim, bfa_ioim_sm_qfull); + break; + } + bfa_sm_set_state(ioim, bfa_ioim_sm_active); + break; + + case BFA_IOIM_SM_CLEANUP: + bfa_sm_set_state(ioim, bfa_ioim_sm_hcb); + bfa_sgpg_wcancel(ioim->bfa, &ioim->iosp->sgpg_wqe); + bfa_cb_queue(ioim->bfa, &ioim->hcb_qe, __bfa_cb_ioim_failed, + ioim); + bfa_ioim_notify_cleanup(ioim); + break; + + case BFA_IOIM_SM_ABORT: + bfa_sm_set_state(ioim, bfa_ioim_sm_hcb); + bfa_sgpg_wcancel(ioim->bfa, &ioim->iosp->sgpg_wqe); + bfa_cb_queue(ioim->bfa, &ioim->hcb_qe, __bfa_cb_ioim_abort, + ioim); + break; + + case BFA_IOIM_SM_HWFAIL: + bfa_sm_set_state(ioim, bfa_ioim_sm_hcb); + bfa_sgpg_wcancel(ioim->bfa, &ioim->iosp->sgpg_wqe); + bfa_cb_queue(ioim->bfa, &ioim->hcb_qe, __bfa_cb_ioim_failed, + ioim); + break; + + default: + bfa_assert(0); + } +} + +/** + * IO is active. + */ +static void +bfa_ioim_sm_active(struct bfa_ioim_s *ioim, enum bfa_ioim_event event) +{ + bfa_trc_fp(ioim->bfa, ioim->iotag); + bfa_trc_fp(ioim->bfa, event); + + switch (event) { + case BFA_IOIM_SM_COMP_GOOD: + bfa_sm_set_state(ioim, bfa_ioim_sm_hcb); + bfa_cb_queue(ioim->bfa, &ioim->hcb_qe, + __bfa_cb_ioim_good_comp, ioim); + break; + + case BFA_IOIM_SM_COMP: + bfa_sm_set_state(ioim, bfa_ioim_sm_hcb); + bfa_cb_queue(ioim->bfa, &ioim->hcb_qe, __bfa_cb_ioim_comp, + ioim); + break; + + case BFA_IOIM_SM_DONE: + bfa_sm_set_state(ioim, bfa_ioim_sm_hcb_free); + bfa_cb_queue(ioim->bfa, &ioim->hcb_qe, __bfa_cb_ioim_comp, + ioim); + break; + + case BFA_IOIM_SM_ABORT: + ioim->iosp->abort_explicit = BFA_TRUE; + ioim->io_cbfn = __bfa_cb_ioim_abort; + + if (bfa_ioim_send_abort(ioim)) + bfa_sm_set_state(ioim, bfa_ioim_sm_abort); + else { + bfa_sm_set_state(ioim, bfa_ioim_sm_abort_qfull); + bfa_reqq_wait(ioim->bfa, ioim->itnim->reqq, + &ioim->iosp->reqq_wait); + } + break; + + case BFA_IOIM_SM_CLEANUP: + ioim->iosp->abort_explicit = BFA_FALSE; + ioim->io_cbfn = __bfa_cb_ioim_failed; + + if (bfa_ioim_send_abort(ioim)) + bfa_sm_set_state(ioim, bfa_ioim_sm_cleanup); + else { + bfa_sm_set_state(ioim, bfa_ioim_sm_cleanup_qfull); + bfa_reqq_wait(ioim->bfa, ioim->itnim->reqq, + &ioim->iosp->reqq_wait); + } + break; + + case BFA_IOIM_SM_HWFAIL: + bfa_sm_set_state(ioim, bfa_ioim_sm_hcb); + bfa_cb_queue(ioim->bfa, &ioim->hcb_qe, __bfa_cb_ioim_failed, + ioim); + break; + + default: + bfa_assert(0); + } +} + +/** + * IO is being aborted, waiting for completion from firmware. + */ +static void +bfa_ioim_sm_abort(struct bfa_ioim_s *ioim, enum bfa_ioim_event event) +{ + bfa_trc(ioim->bfa, ioim->iotag); + bfa_trc(ioim->bfa, event); + + switch (event) { + case BFA_IOIM_SM_COMP_GOOD: + case BFA_IOIM_SM_COMP: + case BFA_IOIM_SM_DONE: + case BFA_IOIM_SM_FREE: + break; + + case BFA_IOIM_SM_ABORT_DONE: + bfa_sm_set_state(ioim, bfa_ioim_sm_hcb_free); + bfa_cb_queue(ioim->bfa, &ioim->hcb_qe, __bfa_cb_ioim_abort, + ioim); + break; + + case BFA_IOIM_SM_ABORT_COMP: + bfa_sm_set_state(ioim, bfa_ioim_sm_hcb); + bfa_cb_queue(ioim->bfa, &ioim->hcb_qe, __bfa_cb_ioim_abort, + ioim); + break; + + case BFA_IOIM_SM_COMP_UTAG: + bfa_sm_set_state(ioim, bfa_ioim_sm_hcb); + bfa_cb_queue(ioim->bfa, &ioim->hcb_qe, __bfa_cb_ioim_abort, + ioim); + break; + + case BFA_IOIM_SM_CLEANUP: + bfa_assert(ioim->iosp->abort_explicit == BFA_TRUE); + ioim->iosp->abort_explicit = BFA_FALSE; + + if (bfa_ioim_send_abort(ioim)) + bfa_sm_set_state(ioim, bfa_ioim_sm_cleanup); + else { + bfa_sm_set_state(ioim, bfa_ioim_sm_cleanup_qfull); + bfa_reqq_wait(ioim->bfa, ioim->itnim->reqq, + &ioim->iosp->reqq_wait); + } + break; + + case BFA_IOIM_SM_HWFAIL: + bfa_sm_set_state(ioim, bfa_ioim_sm_hcb); + bfa_cb_queue(ioim->bfa, &ioim->hcb_qe, __bfa_cb_ioim_failed, + ioim); + break; + + default: + bfa_assert(0); + } +} + +/** + * IO is being cleaned up (implicit abort), waiting for completion from + * firmware. + */ +static void +bfa_ioim_sm_cleanup(struct bfa_ioim_s *ioim, enum bfa_ioim_event event) +{ + bfa_trc(ioim->bfa, ioim->iotag); + bfa_trc(ioim->bfa, event); + + switch (event) { + case BFA_IOIM_SM_COMP_GOOD: + case BFA_IOIM_SM_COMP: + case BFA_IOIM_SM_DONE: + case BFA_IOIM_SM_FREE: + break; + + case BFA_IOIM_SM_ABORT: + /** + * IO is already being aborted implicitly + */ + ioim->io_cbfn = __bfa_cb_ioim_abort; + break; + + case BFA_IOIM_SM_ABORT_DONE: + bfa_sm_set_state(ioim, bfa_ioim_sm_hcb_free); + bfa_cb_queue(ioim->bfa, &ioim->hcb_qe, ioim->io_cbfn, ioim); + bfa_ioim_notify_cleanup(ioim); + break; + + case BFA_IOIM_SM_ABORT_COMP: + bfa_sm_set_state(ioim, bfa_ioim_sm_hcb); + bfa_cb_queue(ioim->bfa, &ioim->hcb_qe, ioim->io_cbfn, ioim); + bfa_ioim_notify_cleanup(ioim); + break; + + case BFA_IOIM_SM_COMP_UTAG: + bfa_sm_set_state(ioim, bfa_ioim_sm_hcb); + bfa_cb_queue(ioim->bfa, &ioim->hcb_qe, ioim->io_cbfn, ioim); + bfa_ioim_notify_cleanup(ioim); + break; + + case BFA_IOIM_SM_HWFAIL: + bfa_sm_set_state(ioim, bfa_ioim_sm_hcb); + bfa_cb_queue(ioim->bfa, &ioim->hcb_qe, __bfa_cb_ioim_failed, + ioim); + break; + + case BFA_IOIM_SM_CLEANUP: + /** + * IO can be in cleanup state already due to TM command. 2nd cleanup + * request comes from ITN offline event. + */ + break; + + default: + bfa_assert(0); + } +} + +/** + * IO is waiting for room in request CQ + */ +static void +bfa_ioim_sm_qfull(struct bfa_ioim_s *ioim, enum bfa_ioim_event event) +{ + bfa_trc(ioim->bfa, ioim->iotag); + bfa_trc(ioim->bfa, event); + + switch (event) { + case BFA_IOIM_SM_QRESUME: + bfa_sm_set_state(ioim, bfa_ioim_sm_active); + bfa_ioim_send_ioreq(ioim); + break; + + case BFA_IOIM_SM_ABORT: + bfa_sm_set_state(ioim, bfa_ioim_sm_hcb); + bfa_reqq_wcancel(&ioim->iosp->reqq_wait); + bfa_cb_queue(ioim->bfa, &ioim->hcb_qe, __bfa_cb_ioim_abort, + ioim); + break; + + case BFA_IOIM_SM_CLEANUP: + bfa_sm_set_state(ioim, bfa_ioim_sm_hcb); + bfa_reqq_wcancel(&ioim->iosp->reqq_wait); + bfa_cb_queue(ioim->bfa, &ioim->hcb_qe, __bfa_cb_ioim_failed, + ioim); + bfa_ioim_notify_cleanup(ioim); + break; + + case BFA_IOIM_SM_HWFAIL: + bfa_sm_set_state(ioim, bfa_ioim_sm_hcb); + bfa_reqq_wcancel(&ioim->iosp->reqq_wait); + bfa_cb_queue(ioim->bfa, &ioim->hcb_qe, __bfa_cb_ioim_failed, + ioim); + break; + + default: + bfa_assert(0); + } +} + +/** + * Active IO is being aborted, waiting for room in request CQ. + */ +static void +bfa_ioim_sm_abort_qfull(struct bfa_ioim_s *ioim, enum bfa_ioim_event event) +{ + bfa_trc(ioim->bfa, ioim->iotag); + bfa_trc(ioim->bfa, event); + + switch (event) { + case BFA_IOIM_SM_QRESUME: + bfa_sm_set_state(ioim, bfa_ioim_sm_abort); + bfa_ioim_send_abort(ioim); + break; + + case BFA_IOIM_SM_CLEANUP: + bfa_assert(ioim->iosp->abort_explicit == BFA_TRUE); + ioim->iosp->abort_explicit = BFA_FALSE; + bfa_sm_set_state(ioim, bfa_ioim_sm_cleanup_qfull); + break; + + case BFA_IOIM_SM_COMP_GOOD: + case BFA_IOIM_SM_COMP: + bfa_sm_set_state(ioim, bfa_ioim_sm_hcb); + bfa_reqq_wcancel(&ioim->iosp->reqq_wait); + bfa_cb_queue(ioim->bfa, &ioim->hcb_qe, __bfa_cb_ioim_abort, + ioim); + break; + + case BFA_IOIM_SM_DONE: + bfa_sm_set_state(ioim, bfa_ioim_sm_hcb_free); + bfa_reqq_wcancel(&ioim->iosp->reqq_wait); + bfa_cb_queue(ioim->bfa, &ioim->hcb_qe, __bfa_cb_ioim_abort, + ioim); + break; + + case BFA_IOIM_SM_HWFAIL: + bfa_sm_set_state(ioim, bfa_ioim_sm_hcb); + bfa_reqq_wcancel(&ioim->iosp->reqq_wait); + bfa_cb_queue(ioim->bfa, &ioim->hcb_qe, __bfa_cb_ioim_failed, + ioim); + break; + + default: + bfa_assert(0); + } +} + +/** + * Active IO is being cleaned up, waiting for room in request CQ. + */ +static void +bfa_ioim_sm_cleanup_qfull(struct bfa_ioim_s *ioim, enum bfa_ioim_event event) +{ + bfa_trc(ioim->bfa, ioim->iotag); + bfa_trc(ioim->bfa, event); + + switch (event) { + case BFA_IOIM_SM_QRESUME: + bfa_sm_set_state(ioim, bfa_ioim_sm_cleanup); + bfa_ioim_send_abort(ioim); + break; + + case BFA_IOIM_SM_ABORT: + /** + * IO is alraedy being cleaned up implicitly + */ + ioim->io_cbfn = __bfa_cb_ioim_abort; + break; + + case BFA_IOIM_SM_COMP_GOOD: + case BFA_IOIM_SM_COMP: + bfa_sm_set_state(ioim, bfa_ioim_sm_hcb); + bfa_reqq_wcancel(&ioim->iosp->reqq_wait); + bfa_cb_queue(ioim->bfa, &ioim->hcb_qe, ioim->io_cbfn, ioim); + bfa_ioim_notify_cleanup(ioim); + break; + + case BFA_IOIM_SM_DONE: + bfa_sm_set_state(ioim, bfa_ioim_sm_hcb_free); + bfa_reqq_wcancel(&ioim->iosp->reqq_wait); + bfa_cb_queue(ioim->bfa, &ioim->hcb_qe, ioim->io_cbfn, ioim); + bfa_ioim_notify_cleanup(ioim); + break; + + case BFA_IOIM_SM_HWFAIL: + bfa_sm_set_state(ioim, bfa_ioim_sm_hcb); + bfa_reqq_wcancel(&ioim->iosp->reqq_wait); + bfa_cb_queue(ioim->bfa, &ioim->hcb_qe, __bfa_cb_ioim_failed, + ioim); + break; + + default: + bfa_assert(0); + } +} + +/** + * IO bfa callback is pending. + */ +static void +bfa_ioim_sm_hcb(struct bfa_ioim_s *ioim, enum bfa_ioim_event event) +{ + bfa_trc_fp(ioim->bfa, ioim->iotag); + bfa_trc_fp(ioim->bfa, event); + + switch (event) { + case BFA_IOIM_SM_HCB: + bfa_sm_set_state(ioim, bfa_ioim_sm_uninit); + bfa_ioim_free(ioim); + bfa_cb_ioim_resfree(ioim->bfa->bfad); + break; + + case BFA_IOIM_SM_CLEANUP: + bfa_ioim_notify_cleanup(ioim); + break; + + case BFA_IOIM_SM_HWFAIL: + break; + + default: + bfa_assert(0); + } +} + +/** + * IO bfa callback is pending. IO resource cannot be freed. + */ +static void +bfa_ioim_sm_hcb_free(struct bfa_ioim_s *ioim, enum bfa_ioim_event event) +{ + bfa_trc(ioim->bfa, ioim->iotag); + bfa_trc(ioim->bfa, event); + + switch (event) { + case BFA_IOIM_SM_HCB: + bfa_sm_set_state(ioim, bfa_ioim_sm_resfree); + list_del(&ioim->qe); + list_add_tail(&ioim->qe, &ioim->fcpim->ioim_resfree_q); + break; + + case BFA_IOIM_SM_FREE: + bfa_sm_set_state(ioim, bfa_ioim_sm_hcb); + break; + + case BFA_IOIM_SM_CLEANUP: + bfa_ioim_notify_cleanup(ioim); + break; + + case BFA_IOIM_SM_HWFAIL: + bfa_sm_set_state(ioim, bfa_ioim_sm_hcb); + break; + + default: + bfa_assert(0); + } +} + +/** + * IO is completed, waiting resource free from firmware. + */ +static void +bfa_ioim_sm_resfree(struct bfa_ioim_s *ioim, enum bfa_ioim_event event) +{ + bfa_trc(ioim->bfa, ioim->iotag); + bfa_trc(ioim->bfa, event); + + switch (event) { + case BFA_IOIM_SM_FREE: + bfa_sm_set_state(ioim, bfa_ioim_sm_uninit); + bfa_ioim_free(ioim); + bfa_cb_ioim_resfree(ioim->bfa->bfad); + break; + + case BFA_IOIM_SM_CLEANUP: + bfa_ioim_notify_cleanup(ioim); + break; + + case BFA_IOIM_SM_HWFAIL: + break; + + default: + bfa_assert(0); + } +} + + + +/** + * bfa_ioim_private + */ + +static void +__bfa_cb_ioim_good_comp(void *cbarg, bfa_boolean_t complete) +{ + struct bfa_ioim_s *ioim = cbarg; + + if (!complete) { + bfa_sm_send_event(ioim, BFA_IOIM_SM_HCB); + return; + } + + bfa_cb_ioim_good_comp(ioim->bfa->bfad, ioim->dio); +} + +static void +__bfa_cb_ioim_comp(void *cbarg, bfa_boolean_t complete) +{ + struct bfa_ioim_s *ioim = cbarg; + struct bfi_ioim_rsp_s *m; + u8 *snsinfo = NULL; + u8 sns_len = 0; + s32 residue = 0; + + if (!complete) { + bfa_sm_send_event(ioim, BFA_IOIM_SM_HCB); + return; + } + + m = (struct bfi_ioim_rsp_s *) &ioim->iosp->comp_rspmsg; + if (m->io_status == BFI_IOIM_STS_OK) { + /** + * setup sense information, if present + */ + if (m->scsi_status == SCSI_STATUS_CHECK_CONDITION + && m->sns_len) { + sns_len = m->sns_len; + snsinfo = ioim->iosp->snsinfo; + } + + /** + * setup residue value correctly for normal completions + */ + if (m->resid_flags == FCP_RESID_UNDER) + residue = bfa_os_ntohl(m->residue); + if (m->resid_flags == FCP_RESID_OVER) { + residue = bfa_os_ntohl(m->residue); + residue = -residue; + } + } + + bfa_cb_ioim_done(ioim->bfa->bfad, ioim->dio, m->io_status, + m->scsi_status, sns_len, snsinfo, residue); +} + +static void +__bfa_cb_ioim_failed(void *cbarg, bfa_boolean_t complete) +{ + struct bfa_ioim_s *ioim = cbarg; + + if (!complete) { + bfa_sm_send_event(ioim, BFA_IOIM_SM_HCB); + return; + } + + bfa_cb_ioim_done(ioim->bfa->bfad, ioim->dio, BFI_IOIM_STS_ABORTED, + 0, 0, NULL, 0); +} + +static void +__bfa_cb_ioim_pathtov(void *cbarg, bfa_boolean_t complete) +{ + struct bfa_ioim_s *ioim = cbarg; + + if (!complete) { + bfa_sm_send_event(ioim, BFA_IOIM_SM_HCB); + return; + } + + bfa_cb_ioim_done(ioim->bfa->bfad, ioim->dio, BFI_IOIM_STS_PATHTOV, + 0, 0, NULL, 0); +} + +static void +__bfa_cb_ioim_abort(void *cbarg, bfa_boolean_t complete) +{ + struct bfa_ioim_s *ioim = cbarg; + + if (!complete) { + bfa_sm_send_event(ioim, BFA_IOIM_SM_HCB); + return; + } + + bfa_cb_ioim_abort(ioim->bfa->bfad, ioim->dio); +} + +static void +bfa_ioim_sgpg_alloced(void *cbarg) +{ + struct bfa_ioim_s *ioim = cbarg; + + ioim->nsgpgs = BFA_SGPG_NPAGE(ioim->nsges); + list_splice_tail_init(&ioim->iosp->sgpg_wqe.sgpg_q, &ioim->sgpg_q); + bfa_ioim_sgpg_setup(ioim); + bfa_sm_send_event(ioim, BFA_IOIM_SM_SGALLOCED); +} + +/** + * Send I/O request to firmware. + */ +static bfa_boolean_t +bfa_ioim_send_ioreq(struct bfa_ioim_s *ioim) +{ + struct bfa_itnim_s *itnim = ioim->itnim; + struct bfi_ioim_req_s *m; + static struct fcp_cmnd_s cmnd_z0 = { 0 }; + struct bfi_sge_s *sge; + u32 pgdlen = 0; + + /** + * check for room in queue to send request now + */ + m = bfa_reqq_next(ioim->bfa, itnim->reqq); + if (!m) { + bfa_reqq_wait(ioim->bfa, ioim->itnim->reqq, + &ioim->iosp->reqq_wait); + return BFA_FALSE; + } + + /** + * build i/o request message next + */ + m->io_tag = bfa_os_htons(ioim->iotag); + m->rport_hdl = ioim->itnim->rport->fw_handle; + m->io_timeout = bfa_cb_ioim_get_timeout(ioim->dio); + + /** + * build inline IO SG element here + */ + sge = &m->sges[0]; + if (ioim->nsges) { + sge->sga = bfa_cb_ioim_get_sgaddr(ioim->dio, 0); + pgdlen = bfa_cb_ioim_get_sglen(ioim->dio, 0); + sge->sg_len = pgdlen; + sge->flags = (ioim->nsges > BFI_SGE_INLINE) ? + BFI_SGE_DATA_CPL : BFI_SGE_DATA_LAST; + bfa_sge_to_be(sge); + sge++; + } + + if (ioim->nsges > BFI_SGE_INLINE) { + sge->sga = ioim->sgpg->sgpg_pa; + } else { + sge->sga.a32.addr_lo = 0; + sge->sga.a32.addr_hi = 0; + } + sge->sg_len = pgdlen; + sge->flags = BFI_SGE_PGDLEN; + bfa_sge_to_be(sge); + + /** + * set up I/O command parameters + */ + bfa_os_assign(m->cmnd, cmnd_z0); + m->cmnd.lun = bfa_cb_ioim_get_lun(ioim->dio); + m->cmnd.iodir = bfa_cb_ioim_get_iodir(ioim->dio); + bfa_os_assign(m->cmnd.cdb, + *(struct scsi_cdb_s *)bfa_cb_ioim_get_cdb(ioim->dio)); + m->cmnd.fcp_dl = bfa_os_htonl(bfa_cb_ioim_get_size(ioim->dio)); + + /** + * set up I/O message header + */ + switch (m->cmnd.iodir) { + case FCP_IODIR_READ: + bfi_h2i_set(m->mh, BFI_MC_IOIM_READ, 0, bfa_lpuid(ioim->bfa)); + bfa_stats(itnim, input_reqs); + break; + case FCP_IODIR_WRITE: + bfi_h2i_set(m->mh, BFI_MC_IOIM_WRITE, 0, bfa_lpuid(ioim->bfa)); + bfa_stats(itnim, output_reqs); + break; + case FCP_IODIR_RW: + bfa_stats(itnim, input_reqs); + bfa_stats(itnim, output_reqs); + default: + bfi_h2i_set(m->mh, BFI_MC_IOIM_IO, 0, bfa_lpuid(ioim->bfa)); + } + if (itnim->seq_rec || + (bfa_cb_ioim_get_size(ioim->dio) & (sizeof(u32) - 1))) + bfi_h2i_set(m->mh, BFI_MC_IOIM_IO, 0, bfa_lpuid(ioim->bfa)); + +#ifdef IOIM_ADVANCED + m->cmnd.crn = bfa_cb_ioim_get_crn(ioim->dio); + m->cmnd.priority = bfa_cb_ioim_get_priority(ioim->dio); + m->cmnd.taskattr = bfa_cb_ioim_get_taskattr(ioim->dio); + + /** + * Handle large CDB (>16 bytes). + */ + m->cmnd.addl_cdb_len = (bfa_cb_ioim_get_cdblen(ioim->dio) - + FCP_CMND_CDB_LEN) / sizeof(u32); + if (m->cmnd.addl_cdb_len) { + bfa_os_memcpy(&m->cmnd.cdb + 1, (struct scsi_cdb_s *) + bfa_cb_ioim_get_cdb(ioim->dio) + 1, + m->cmnd.addl_cdb_len * sizeof(u32)); + fcp_cmnd_fcpdl(&m->cmnd) = + bfa_os_htonl(bfa_cb_ioim_get_size(ioim->dio)); + } +#endif + + /** + * queue I/O message to firmware + */ + bfa_reqq_produce(ioim->bfa, itnim->reqq); + return BFA_TRUE; +} + +/** + * Setup any additional SG pages needed.Inline SG element is setup + * at queuing time. + */ +static bfa_boolean_t +bfa_ioim_sge_setup(struct bfa_ioim_s *ioim) +{ + u16 nsgpgs; + + bfa_assert(ioim->nsges > BFI_SGE_INLINE); + + /** + * allocate SG pages needed + */ + nsgpgs = BFA_SGPG_NPAGE(ioim->nsges); + if (!nsgpgs) + return BFA_TRUE; + + if (bfa_sgpg_malloc(ioim->bfa, &ioim->sgpg_q, nsgpgs) + != BFA_STATUS_OK) { + bfa_sgpg_wait(ioim->bfa, &ioim->iosp->sgpg_wqe, nsgpgs); + return BFA_FALSE; + } + + ioim->nsgpgs = nsgpgs; + bfa_ioim_sgpg_setup(ioim); + + return BFA_TRUE; +} + +static void +bfa_ioim_sgpg_setup(struct bfa_ioim_s *ioim) +{ + int sgeid, nsges, i; + struct bfi_sge_s *sge; + struct bfa_sgpg_s *sgpg; + u32 pgcumsz; + + sgeid = BFI_SGE_INLINE; + ioim->sgpg = sgpg = bfa_q_first(&ioim->sgpg_q); + + do { + sge = sgpg->sgpg->sges; + nsges = ioim->nsges - sgeid; + if (nsges > BFI_SGPG_DATA_SGES) + nsges = BFI_SGPG_DATA_SGES; + + pgcumsz = 0; + for (i = 0; i < nsges; i++, sge++, sgeid++) { + sge->sga = bfa_cb_ioim_get_sgaddr(ioim->dio, sgeid); + sge->sg_len = bfa_cb_ioim_get_sglen(ioim->dio, sgeid); + pgcumsz += sge->sg_len; + + /** + * set flags + */ + if (i < (nsges - 1)) + sge->flags = BFI_SGE_DATA; + else if (sgeid < (ioim->nsges - 1)) + sge->flags = BFI_SGE_DATA_CPL; + else + sge->flags = BFI_SGE_DATA_LAST; + } + + sgpg = (struct bfa_sgpg_s *) bfa_q_next(sgpg); + + /** + * set the link element of each page + */ + if (sgeid == ioim->nsges) { + sge->flags = BFI_SGE_PGDLEN; + sge->sga.a32.addr_lo = 0; + sge->sga.a32.addr_hi = 0; + } else { + sge->flags = BFI_SGE_LINK; + sge->sga = sgpg->sgpg_pa; + } + sge->sg_len = pgcumsz; + } while (sgeid < ioim->nsges); +} + +/** + * Send I/O abort request to firmware. + */ +static bfa_boolean_t +bfa_ioim_send_abort(struct bfa_ioim_s *ioim) +{ + struct bfa_itnim_s *itnim = ioim->itnim; + struct bfi_ioim_abort_req_s *m; + enum bfi_ioim_h2i msgop; + + /** + * check for room in queue to send request now + */ + m = bfa_reqq_next(ioim->bfa, itnim->reqq); + if (!m) + return BFA_FALSE; + + /** + * build i/o request message next + */ + if (ioim->iosp->abort_explicit) + msgop = BFI_IOIM_H2I_IOABORT_REQ; + else + msgop = BFI_IOIM_H2I_IOCLEANUP_REQ; + + bfi_h2i_set(m->mh, BFI_MC_IOIM, msgop, bfa_lpuid(ioim->bfa)); + m->io_tag = bfa_os_htons(ioim->iotag); + m->abort_tag = ++ioim->abort_tag; + + /** + * queue I/O message to firmware + */ + bfa_reqq_produce(ioim->bfa, itnim->reqq); + return BFA_TRUE; +} + +/** + * Call to resume any I/O requests waiting for room in request queue. + */ +static void +bfa_ioim_qresume(void *cbarg) +{ + struct bfa_ioim_s *ioim = cbarg; + + bfa_fcpim_stats(ioim->fcpim, qresumes); + bfa_sm_send_event(ioim, BFA_IOIM_SM_QRESUME); +} + + +static void +bfa_ioim_notify_cleanup(struct bfa_ioim_s *ioim) +{ + /** + * Move IO from itnim queue to fcpim global queue since itnim will be + * freed. + */ + list_del(&ioim->qe); + list_add_tail(&ioim->qe, &ioim->fcpim->ioim_comp_q); + + if (!ioim->iosp->tskim) { + if (ioim->fcpim->delay_comp && ioim->itnim->iotov_active) { + bfa_cb_dequeue(&ioim->hcb_qe); + list_del(&ioim->qe); + list_add_tail(&ioim->qe, &ioim->itnim->delay_comp_q); + } + bfa_itnim_iodone(ioim->itnim); + } else + bfa_tskim_iodone(ioim->iosp->tskim); +} + +/** + * or after the link comes back. + */ +void +bfa_ioim_delayed_comp(struct bfa_ioim_s *ioim, bfa_boolean_t iotov) +{ + /** + * If path tov timer expired, failback with PATHTOV status - these + * IO requests are not normally retried by IO stack. + * + * Otherwise device cameback online and fail it with normal failed + * status so that IO stack retries these failed IO requests. + */ + if (iotov) + ioim->io_cbfn = __bfa_cb_ioim_pathtov; + else + ioim->io_cbfn = __bfa_cb_ioim_failed; + + bfa_cb_queue(ioim->bfa, &ioim->hcb_qe, ioim->io_cbfn, ioim); + + /** + * Move IO to fcpim global queue since itnim will be + * freed. + */ + list_del(&ioim->qe); + list_add_tail(&ioim->qe, &ioim->fcpim->ioim_comp_q); +} + + + +/** + * bfa_ioim_friend + */ + +/** + * Memory allocation and initialization. + */ +void +bfa_ioim_attach(struct bfa_fcpim_mod_s *fcpim, struct bfa_meminfo_s *minfo) +{ + struct bfa_ioim_s *ioim; + struct bfa_ioim_sp_s *iosp; + u16 i; + u8 *snsinfo; + u32 snsbufsz; + + /** + * claim memory first + */ + ioim = (struct bfa_ioim_s *) bfa_meminfo_kva(minfo); + fcpim->ioim_arr = ioim; + bfa_meminfo_kva(minfo) = (u8 *) (ioim + fcpim->num_ioim_reqs); + + iosp = (struct bfa_ioim_sp_s *) bfa_meminfo_kva(minfo); + fcpim->ioim_sp_arr = iosp; + bfa_meminfo_kva(minfo) = (u8 *) (iosp + fcpim->num_ioim_reqs); + + /** + * Claim DMA memory for per IO sense data. + */ + snsbufsz = fcpim->num_ioim_reqs * BFI_IOIM_SNSLEN; + fcpim->snsbase.pa = bfa_meminfo_dma_phys(minfo); + bfa_meminfo_dma_phys(minfo) += snsbufsz; + + fcpim->snsbase.kva = bfa_meminfo_dma_virt(minfo); + bfa_meminfo_dma_virt(minfo) += snsbufsz; + snsinfo = fcpim->snsbase.kva; + bfa_iocfc_set_snsbase(fcpim->bfa, fcpim->snsbase.pa); + + /** + * Initialize ioim free queues + */ + INIT_LIST_HEAD(&fcpim->ioim_free_q); + INIT_LIST_HEAD(&fcpim->ioim_resfree_q); + INIT_LIST_HEAD(&fcpim->ioim_comp_q); + + for (i = 0; i < fcpim->num_ioim_reqs; + i++, ioim++, iosp++, snsinfo += BFI_IOIM_SNSLEN) { + /* + * initialize IOIM + */ + bfa_os_memset(ioim, 0, sizeof(struct bfa_ioim_s)); + ioim->iotag = i; + ioim->bfa = fcpim->bfa; + ioim->fcpim = fcpim; + ioim->iosp = iosp; + iosp->snsinfo = snsinfo; + INIT_LIST_HEAD(&ioim->sgpg_q); + bfa_reqq_winit(&ioim->iosp->reqq_wait, + bfa_ioim_qresume, ioim); + bfa_sgpg_winit(&ioim->iosp->sgpg_wqe, + bfa_ioim_sgpg_alloced, ioim); + bfa_sm_set_state(ioim, bfa_ioim_sm_uninit); + + list_add_tail(&ioim->qe, &fcpim->ioim_free_q); + } +} + +/** + * Driver detach time call. + */ +void +bfa_ioim_detach(struct bfa_fcpim_mod_s *fcpim) +{ +} + +void +bfa_ioim_isr(struct bfa_s *bfa, struct bfi_msg_s *m) +{ + struct bfa_fcpim_mod_s *fcpim = BFA_FCPIM_MOD(bfa); + struct bfi_ioim_rsp_s *rsp = (struct bfi_ioim_rsp_s *) m; + struct bfa_ioim_s *ioim; + u16 iotag; + enum bfa_ioim_event evt = BFA_IOIM_SM_COMP; + + iotag = bfa_os_ntohs(rsp->io_tag); + + ioim = BFA_IOIM_FROM_TAG(fcpim, iotag); + bfa_assert(ioim->iotag == iotag); + + bfa_trc(ioim->bfa, ioim->iotag); + bfa_trc(ioim->bfa, rsp->io_status); + bfa_trc(ioim->bfa, rsp->reuse_io_tag); + + if (bfa_sm_cmp_state(ioim, bfa_ioim_sm_active)) + bfa_os_assign(ioim->iosp->comp_rspmsg, *m); + + switch (rsp->io_status) { + case BFI_IOIM_STS_OK: + bfa_fcpim_stats(fcpim, iocomp_ok); + if (rsp->reuse_io_tag == 0) + evt = BFA_IOIM_SM_DONE; + else + evt = BFA_IOIM_SM_COMP; + break; + + case BFI_IOIM_STS_TIMEDOUT: + case BFI_IOIM_STS_ABORTED: + rsp->io_status = BFI_IOIM_STS_ABORTED; + bfa_fcpim_stats(fcpim, iocomp_aborted); + if (rsp->reuse_io_tag == 0) + evt = BFA_IOIM_SM_DONE; + else + evt = BFA_IOIM_SM_COMP; + break; + + case BFI_IOIM_STS_PROTO_ERR: + bfa_fcpim_stats(fcpim, iocom_proto_err); + bfa_assert(rsp->reuse_io_tag); + evt = BFA_IOIM_SM_COMP; + break; + + case BFI_IOIM_STS_SQER_NEEDED: + bfa_fcpim_stats(fcpim, iocom_sqer_needed); + bfa_assert(rsp->reuse_io_tag == 0); + evt = BFA_IOIM_SM_SQRETRY; + break; + + case BFI_IOIM_STS_RES_FREE: + bfa_fcpim_stats(fcpim, iocom_res_free); + evt = BFA_IOIM_SM_FREE; + break; + + case BFI_IOIM_STS_HOST_ABORTED: + bfa_fcpim_stats(fcpim, iocom_hostabrts); + if (rsp->abort_tag != ioim->abort_tag) { + bfa_trc(ioim->bfa, rsp->abort_tag); + bfa_trc(ioim->bfa, ioim->abort_tag); + return; + } + + if (rsp->reuse_io_tag) + evt = BFA_IOIM_SM_ABORT_COMP; + else + evt = BFA_IOIM_SM_ABORT_DONE; + break; + + case BFI_IOIM_STS_UTAG: + bfa_fcpim_stats(fcpim, iocom_utags); + evt = BFA_IOIM_SM_COMP_UTAG; + break; + + default: + bfa_assert(0); + } + + bfa_sm_send_event(ioim, evt); +} + +void +bfa_ioim_good_comp_isr(struct bfa_s *bfa, struct bfi_msg_s *m) +{ + struct bfa_fcpim_mod_s *fcpim = BFA_FCPIM_MOD(bfa); + struct bfi_ioim_rsp_s *rsp = (struct bfi_ioim_rsp_s *) m; + struct bfa_ioim_s *ioim; + u16 iotag; + + iotag = bfa_os_ntohs(rsp->io_tag); + + ioim = BFA_IOIM_FROM_TAG(fcpim, iotag); + bfa_assert(ioim->iotag == iotag); + + bfa_trc_fp(ioim->bfa, ioim->iotag); + bfa_sm_send_event(ioim, BFA_IOIM_SM_COMP_GOOD); +} + +/** + * Called by itnim to clean up IO while going offline. + */ +void +bfa_ioim_cleanup(struct bfa_ioim_s *ioim) +{ + bfa_trc(ioim->bfa, ioim->iotag); + bfa_fcpim_stats(ioim->fcpim, io_cleanups); + + ioim->iosp->tskim = NULL; + bfa_sm_send_event(ioim, BFA_IOIM_SM_CLEANUP); +} + +void +bfa_ioim_cleanup_tm(struct bfa_ioim_s *ioim, struct bfa_tskim_s *tskim) +{ + bfa_trc(ioim->bfa, ioim->iotag); + bfa_fcpim_stats(ioim->fcpim, io_tmaborts); + + ioim->iosp->tskim = tskim; + bfa_sm_send_event(ioim, BFA_IOIM_SM_CLEANUP); +} + +/** + * IOC failure handling. + */ +void +bfa_ioim_iocdisable(struct bfa_ioim_s *ioim) +{ + bfa_sm_send_event(ioim, BFA_IOIM_SM_HWFAIL); +} + +/** + * IO offline TOV popped. Fail the pending IO. + */ +void +bfa_ioim_tov(struct bfa_ioim_s *ioim) +{ + bfa_sm_send_event(ioim, BFA_IOIM_SM_IOTOV); +} + + + +/** + * bfa_ioim_api + */ + +/** + * Allocate IOIM resource for initiator mode I/O request. + */ +struct bfa_ioim_s * +bfa_ioim_alloc(struct bfa_s *bfa, struct bfad_ioim_s *dio, + struct bfa_itnim_s *itnim, u16 nsges) +{ + struct bfa_fcpim_mod_s *fcpim = BFA_FCPIM_MOD(bfa); + struct bfa_ioim_s *ioim; + + /** + * alocate IOIM resource + */ + bfa_q_deq(&fcpim->ioim_free_q, &ioim); + if (!ioim) { + bfa_fcpim_stats(fcpim, no_iotags); + return NULL; + } + + ioim->dio = dio; + ioim->itnim = itnim; + ioim->nsges = nsges; + ioim->nsgpgs = 0; + + bfa_stats(fcpim, total_ios); + bfa_stats(itnim, ios); + fcpim->ios_active++; + + list_add_tail(&ioim->qe, &itnim->io_q); + bfa_trc_fp(ioim->bfa, ioim->iotag); + + return ioim; +} + +void +bfa_ioim_free(struct bfa_ioim_s *ioim) +{ + struct bfa_fcpim_mod_s *fcpim = ioim->fcpim; + + bfa_trc_fp(ioim->bfa, ioim->iotag); + bfa_assert_fp(bfa_sm_cmp_state(ioim, bfa_ioim_sm_uninit)); + + bfa_assert_fp(list_empty(&ioim->sgpg_q) + || (ioim->nsges > BFI_SGE_INLINE)); + + if (ioim->nsgpgs > 0) + bfa_sgpg_mfree(ioim->bfa, &ioim->sgpg_q, ioim->nsgpgs); + + bfa_stats(ioim->itnim, io_comps); + fcpim->ios_active--; + + list_del(&ioim->qe); + list_add_tail(&ioim->qe, &fcpim->ioim_free_q); +} + +void +bfa_ioim_start(struct bfa_ioim_s *ioim) +{ + bfa_trc_fp(ioim->bfa, ioim->iotag); + bfa_sm_send_event(ioim, BFA_IOIM_SM_START); +} + +/** + * Driver I/O abort request. + */ +void +bfa_ioim_abort(struct bfa_ioim_s *ioim) +{ + bfa_trc(ioim->bfa, ioim->iotag); + bfa_fcpim_stats(ioim->fcpim, io_aborts); + bfa_sm_send_event(ioim, BFA_IOIM_SM_ABORT); +} + + diff --git a/drivers/scsi/bfa/bfa_itnim.c b/drivers/scsi/bfa/bfa_itnim.c new file mode 100644 index 000000000000..4d5c61a4f85c --- /dev/null +++ b/drivers/scsi/bfa/bfa_itnim.c @@ -0,0 +1,1088 @@ +/* + * Copyright (c) 2005-2009 Brocade Communications Systems, Inc. + * All rights reserved + * www.brocade.com + * + * Linux driver for Brocade Fibre Channel Host Bus Adapter. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (GPL) Version 2 as + * published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#include <bfa.h> +#include <bfa_fcpim.h> +#include "bfa_fcpim_priv.h" + +BFA_TRC_FILE(HAL, ITNIM); + +#define BFA_ITNIM_FROM_TAG(_fcpim, _tag) \ + ((_fcpim)->itnim_arr + ((_tag) & ((_fcpim)->num_itnims - 1))) + +#define bfa_fcpim_additn(__itnim) \ + list_add_tail(&(__itnim)->qe, &(__itnim)->fcpim->itnim_q) +#define bfa_fcpim_delitn(__itnim) do { \ + bfa_assert(bfa_q_is_on_q(&(__itnim)->fcpim->itnim_q, __itnim)); \ + list_del(&(__itnim)->qe); \ + bfa_assert(list_empty(&(__itnim)->io_q)); \ + bfa_assert(list_empty(&(__itnim)->io_cleanup_q)); \ + bfa_assert(list_empty(&(__itnim)->pending_q)); \ +} while (0) + +#define bfa_itnim_online_cb(__itnim) do { \ + if ((__itnim)->bfa->fcs) \ + bfa_cb_itnim_online((__itnim)->ditn); \ + else { \ + bfa_cb_queue((__itnim)->bfa, &(__itnim)->hcb_qe, \ + __bfa_cb_itnim_online, (__itnim)); \ + } \ +} while (0) + +#define bfa_itnim_offline_cb(__itnim) do { \ + if ((__itnim)->bfa->fcs) \ + bfa_cb_itnim_offline((__itnim)->ditn); \ + else { \ + bfa_cb_queue((__itnim)->bfa, &(__itnim)->hcb_qe, \ + __bfa_cb_itnim_offline, (__itnim)); \ + } \ +} while (0) + +#define bfa_itnim_sler_cb(__itnim) do { \ + if ((__itnim)->bfa->fcs) \ + bfa_cb_itnim_sler((__itnim)->ditn); \ + else { \ + bfa_cb_queue((__itnim)->bfa, &(__itnim)->hcb_qe, \ + __bfa_cb_itnim_sler, (__itnim)); \ + } \ +} while (0) + +/* + * forward declarations + */ +static void bfa_itnim_iocdisable_cleanup(struct bfa_itnim_s *itnim); +static bfa_boolean_t bfa_itnim_send_fwcreate(struct bfa_itnim_s *itnim); +static bfa_boolean_t bfa_itnim_send_fwdelete(struct bfa_itnim_s *itnim); +static void bfa_itnim_cleanp_comp(void *itnim_cbarg); +static void bfa_itnim_cleanup(struct bfa_itnim_s *itnim); +static void __bfa_cb_itnim_online(void *cbarg, bfa_boolean_t complete); +static void __bfa_cb_itnim_offline(void *cbarg, bfa_boolean_t complete); +static void __bfa_cb_itnim_sler(void *cbarg, bfa_boolean_t complete); +static void bfa_itnim_iotov_online(struct bfa_itnim_s *itnim); +static void bfa_itnim_iotov_cleanup(struct bfa_itnim_s *itnim); +static void bfa_itnim_iotov(void *itnim_arg); +static void bfa_itnim_iotov_start(struct bfa_itnim_s *itnim); +static void bfa_itnim_iotov_stop(struct bfa_itnim_s *itnim); +static void bfa_itnim_iotov_delete(struct bfa_itnim_s *itnim); + +/** + * bfa_itnim_sm BFA itnim state machine + */ + + +enum bfa_itnim_event { + BFA_ITNIM_SM_CREATE = 1, /* itnim is created */ + BFA_ITNIM_SM_ONLINE = 2, /* itnim is online */ + BFA_ITNIM_SM_OFFLINE = 3, /* itnim is offline */ + BFA_ITNIM_SM_FWRSP = 4, /* firmware response */ + BFA_ITNIM_SM_DELETE = 5, /* deleting an existing itnim */ + BFA_ITNIM_SM_CLEANUP = 6, /* IO cleanup completion */ + BFA_ITNIM_SM_SLER = 7, /* second level error recovery */ + BFA_ITNIM_SM_HWFAIL = 8, /* IOC h/w failure event */ + BFA_ITNIM_SM_QRESUME = 9, /* queue space available */ +}; + +static void bfa_itnim_sm_uninit(struct bfa_itnim_s *itnim, + enum bfa_itnim_event event); +static void bfa_itnim_sm_created(struct bfa_itnim_s *itnim, + enum bfa_itnim_event event); +static void bfa_itnim_sm_fwcreate(struct bfa_itnim_s *itnim, + enum bfa_itnim_event event); +static void bfa_itnim_sm_delete_pending(struct bfa_itnim_s *itnim, + enum bfa_itnim_event event); +static void bfa_itnim_sm_online(struct bfa_itnim_s *itnim, + enum bfa_itnim_event event); +static void bfa_itnim_sm_sler(struct bfa_itnim_s *itnim, + enum bfa_itnim_event event); +static void bfa_itnim_sm_cleanup_offline(struct bfa_itnim_s *itnim, + enum bfa_itnim_event event); +static void bfa_itnim_sm_cleanup_delete(struct bfa_itnim_s *itnim, + enum bfa_itnim_event event); +static void bfa_itnim_sm_fwdelete(struct bfa_itnim_s *itnim, + enum bfa_itnim_event event); +static void bfa_itnim_sm_offline(struct bfa_itnim_s *itnim, + enum bfa_itnim_event event); +static void bfa_itnim_sm_iocdisable(struct bfa_itnim_s *itnim, + enum bfa_itnim_event event); +static void bfa_itnim_sm_deleting(struct bfa_itnim_s *itnim, + enum bfa_itnim_event event); +static void bfa_itnim_sm_fwcreate_qfull(struct bfa_itnim_s *itnim, + enum bfa_itnim_event event); +static void bfa_itnim_sm_fwdelete_qfull(struct bfa_itnim_s *itnim, + enum bfa_itnim_event event); +static void bfa_itnim_sm_deleting_qfull(struct bfa_itnim_s *itnim, + enum bfa_itnim_event event); + +/** + * Beginning/unallocated state - no events expected. + */ +static void +bfa_itnim_sm_uninit(struct bfa_itnim_s *itnim, enum bfa_itnim_event event) +{ + bfa_trc(itnim->bfa, itnim->rport->rport_tag); + bfa_trc(itnim->bfa, event); + + switch (event) { + case BFA_ITNIM_SM_CREATE: + bfa_sm_set_state(itnim, bfa_itnim_sm_created); + itnim->is_online = BFA_FALSE; + bfa_fcpim_additn(itnim); + break; + + default: + bfa_assert(0); + } +} + +/** + * Beginning state, only online event expected. + */ +static void +bfa_itnim_sm_created(struct bfa_itnim_s *itnim, enum bfa_itnim_event event) +{ + bfa_trc(itnim->bfa, itnim->rport->rport_tag); + bfa_trc(itnim->bfa, event); + + switch (event) { + case BFA_ITNIM_SM_ONLINE: + if (bfa_itnim_send_fwcreate(itnim)) + bfa_sm_set_state(itnim, bfa_itnim_sm_fwcreate); + else + bfa_sm_set_state(itnim, bfa_itnim_sm_fwcreate_qfull); + break; + + case BFA_ITNIM_SM_DELETE: + bfa_sm_set_state(itnim, bfa_itnim_sm_uninit); + bfa_fcpim_delitn(itnim); + break; + + case BFA_ITNIM_SM_HWFAIL: + bfa_sm_set_state(itnim, bfa_itnim_sm_iocdisable); + break; + + default: + bfa_assert(0); + } +} + +/** + * Waiting for itnim create response from firmware. + */ +static void +bfa_itnim_sm_fwcreate(struct bfa_itnim_s *itnim, enum bfa_itnim_event event) +{ + bfa_trc(itnim->bfa, itnim->rport->rport_tag); + bfa_trc(itnim->bfa, event); + + switch (event) { + case BFA_ITNIM_SM_FWRSP: + bfa_sm_set_state(itnim, bfa_itnim_sm_online); + itnim->is_online = BFA_TRUE; + bfa_itnim_iotov_online(itnim); + bfa_itnim_online_cb(itnim); + break; + + case BFA_ITNIM_SM_DELETE: + bfa_sm_set_state(itnim, bfa_itnim_sm_delete_pending); + break; + + case BFA_ITNIM_SM_OFFLINE: + if (bfa_itnim_send_fwdelete(itnim)) + bfa_sm_set_state(itnim, bfa_itnim_sm_fwdelete); + else + bfa_sm_set_state(itnim, bfa_itnim_sm_fwdelete_qfull); + break; + + case BFA_ITNIM_SM_HWFAIL: + bfa_sm_set_state(itnim, bfa_itnim_sm_iocdisable); + break; + + default: + bfa_assert(0); + } +} + +static void +bfa_itnim_sm_fwcreate_qfull(struct bfa_itnim_s *itnim, + enum bfa_itnim_event event) +{ + bfa_trc(itnim->bfa, itnim->rport->rport_tag); + bfa_trc(itnim->bfa, event); + + switch (event) { + case BFA_ITNIM_SM_QRESUME: + bfa_sm_set_state(itnim, bfa_itnim_sm_fwcreate); + bfa_itnim_send_fwcreate(itnim); + break; + + case BFA_ITNIM_SM_DELETE: + bfa_sm_set_state(itnim, bfa_itnim_sm_uninit); + bfa_reqq_wcancel(&itnim->reqq_wait); + bfa_fcpim_delitn(itnim); + break; + + case BFA_ITNIM_SM_OFFLINE: + bfa_sm_set_state(itnim, bfa_itnim_sm_offline); + bfa_reqq_wcancel(&itnim->reqq_wait); + bfa_itnim_offline_cb(itnim); + break; + + case BFA_ITNIM_SM_HWFAIL: + bfa_sm_set_state(itnim, bfa_itnim_sm_iocdisable); + bfa_reqq_wcancel(&itnim->reqq_wait); + break; + + default: + bfa_assert(0); + } +} + +/** + * Waiting for itnim create response from firmware, a delete is pending. + */ +static void +bfa_itnim_sm_delete_pending(struct bfa_itnim_s *itnim, + enum bfa_itnim_event event) +{ + bfa_trc(itnim->bfa, itnim->rport->rport_tag); + bfa_trc(itnim->bfa, event); + + switch (event) { + case BFA_ITNIM_SM_FWRSP: + if (bfa_itnim_send_fwdelete(itnim)) + bfa_sm_set_state(itnim, bfa_itnim_sm_deleting); + else + bfa_sm_set_state(itnim, bfa_itnim_sm_deleting_qfull); + break; + + case BFA_ITNIM_SM_HWFAIL: + bfa_sm_set_state(itnim, bfa_itnim_sm_uninit); + bfa_fcpim_delitn(itnim); + break; + + default: + bfa_assert(0); + } +} + +/** + * Online state - normal parking state. + */ +static void +bfa_itnim_sm_online(struct bfa_itnim_s *itnim, enum bfa_itnim_event event) +{ + bfa_trc(itnim->bfa, itnim->rport->rport_tag); + bfa_trc(itnim->bfa, event); + + switch (event) { + case BFA_ITNIM_SM_OFFLINE: + bfa_sm_set_state(itnim, bfa_itnim_sm_cleanup_offline); + itnim->is_online = BFA_FALSE; + bfa_itnim_iotov_start(itnim); + bfa_itnim_cleanup(itnim); + break; + + case BFA_ITNIM_SM_DELETE: + bfa_sm_set_state(itnim, bfa_itnim_sm_cleanup_delete); + itnim->is_online = BFA_FALSE; + bfa_itnim_cleanup(itnim); + break; + + case BFA_ITNIM_SM_SLER: + bfa_sm_set_state(itnim, bfa_itnim_sm_sler); + itnim->is_online = BFA_FALSE; + bfa_itnim_iotov_start(itnim); + bfa_itnim_sler_cb(itnim); + break; + + case BFA_ITNIM_SM_HWFAIL: + bfa_sm_set_state(itnim, bfa_itnim_sm_iocdisable); + itnim->is_online = BFA_FALSE; + bfa_itnim_iotov_start(itnim); + bfa_itnim_iocdisable_cleanup(itnim); + break; + + default: + bfa_assert(0); + } +} + +/** + * Second level error recovery need. + */ +static void +bfa_itnim_sm_sler(struct bfa_itnim_s *itnim, enum bfa_itnim_event event) +{ + bfa_trc(itnim->bfa, itnim->rport->rport_tag); + bfa_trc(itnim->bfa, event); + + switch (event) { + case BFA_ITNIM_SM_OFFLINE: + bfa_sm_set_state(itnim, bfa_itnim_sm_cleanup_offline); + bfa_itnim_cleanup(itnim); + break; + + case BFA_ITNIM_SM_DELETE: + bfa_sm_set_state(itnim, bfa_itnim_sm_cleanup_delete); + bfa_itnim_cleanup(itnim); + bfa_itnim_iotov_delete(itnim); + break; + + case BFA_ITNIM_SM_HWFAIL: + bfa_sm_set_state(itnim, bfa_itnim_sm_iocdisable); + bfa_itnim_iocdisable_cleanup(itnim); + break; + + default: + bfa_assert(0); + } +} + +/** + * Going offline. Waiting for active IO cleanup. + */ +static void +bfa_itnim_sm_cleanup_offline(struct bfa_itnim_s *itnim, + enum bfa_itnim_event event) +{ + bfa_trc(itnim->bfa, itnim->rport->rport_tag); + bfa_trc(itnim->bfa, event); + + switch (event) { + case BFA_ITNIM_SM_CLEANUP: + if (bfa_itnim_send_fwdelete(itnim)) + bfa_sm_set_state(itnim, bfa_itnim_sm_fwdelete); + else + bfa_sm_set_state(itnim, bfa_itnim_sm_fwdelete_qfull); + break; + + case BFA_ITNIM_SM_DELETE: + bfa_sm_set_state(itnim, bfa_itnim_sm_cleanup_delete); + bfa_itnim_iotov_delete(itnim); + break; + + case BFA_ITNIM_SM_HWFAIL: + bfa_sm_set_state(itnim, bfa_itnim_sm_iocdisable); + bfa_itnim_iocdisable_cleanup(itnim); + bfa_itnim_offline_cb(itnim); + break; + + case BFA_ITNIM_SM_SLER: + break; + + default: + bfa_assert(0); + } +} + +/** + * Deleting itnim. Waiting for active IO cleanup. + */ +static void +bfa_itnim_sm_cleanup_delete(struct bfa_itnim_s *itnim, + enum bfa_itnim_event event) +{ + bfa_trc(itnim->bfa, itnim->rport->rport_tag); + bfa_trc(itnim->bfa, event); + + switch (event) { + case BFA_ITNIM_SM_CLEANUP: + if (bfa_itnim_send_fwdelete(itnim)) + bfa_sm_set_state(itnim, bfa_itnim_sm_deleting); + else + bfa_sm_set_state(itnim, bfa_itnim_sm_deleting_qfull); + break; + + case BFA_ITNIM_SM_HWFAIL: + bfa_sm_set_state(itnim, bfa_itnim_sm_iocdisable); + bfa_itnim_iocdisable_cleanup(itnim); + break; + + default: + bfa_assert(0); + } +} + +/** + * Rport offline. Fimrware itnim is being deleted - awaiting f/w response. + */ +static void +bfa_itnim_sm_fwdelete(struct bfa_itnim_s *itnim, enum bfa_itnim_event event) +{ + bfa_trc(itnim->bfa, itnim->rport->rport_tag); + bfa_trc(itnim->bfa, event); + + switch (event) { + case BFA_ITNIM_SM_FWRSP: + bfa_sm_set_state(itnim, bfa_itnim_sm_offline); + bfa_itnim_offline_cb(itnim); + break; + + case BFA_ITNIM_SM_DELETE: + bfa_sm_set_state(itnim, bfa_itnim_sm_deleting); + break; + + case BFA_ITNIM_SM_HWFAIL: + bfa_sm_set_state(itnim, bfa_itnim_sm_iocdisable); + bfa_itnim_offline_cb(itnim); + break; + + default: + bfa_assert(0); + } +} + +static void +bfa_itnim_sm_fwdelete_qfull(struct bfa_itnim_s *itnim, + enum bfa_itnim_event event) +{ + bfa_trc(itnim->bfa, itnim->rport->rport_tag); + bfa_trc(itnim->bfa, event); + + switch (event) { + case BFA_ITNIM_SM_QRESUME: + bfa_sm_set_state(itnim, bfa_itnim_sm_fwdelete); + bfa_itnim_send_fwdelete(itnim); + break; + + case BFA_ITNIM_SM_DELETE: + bfa_sm_set_state(itnim, bfa_itnim_sm_deleting_qfull); + break; + + case BFA_ITNIM_SM_HWFAIL: + bfa_sm_set_state(itnim, bfa_itnim_sm_iocdisable); + bfa_reqq_wcancel(&itnim->reqq_wait); + bfa_itnim_offline_cb(itnim); + break; + + default: + bfa_assert(0); + } +} + +/** + * Offline state. + */ +static void +bfa_itnim_sm_offline(struct bfa_itnim_s *itnim, enum bfa_itnim_event event) +{ + bfa_trc(itnim->bfa, itnim->rport->rport_tag); + bfa_trc(itnim->bfa, event); + + switch (event) { + case BFA_ITNIM_SM_DELETE: + bfa_sm_set_state(itnim, bfa_itnim_sm_uninit); + bfa_itnim_iotov_delete(itnim); + bfa_fcpim_delitn(itnim); + break; + + case BFA_ITNIM_SM_ONLINE: + if (bfa_itnim_send_fwcreate(itnim)) + bfa_sm_set_state(itnim, bfa_itnim_sm_fwcreate); + else + bfa_sm_set_state(itnim, bfa_itnim_sm_fwcreate_qfull); + break; + + case BFA_ITNIM_SM_HWFAIL: + bfa_sm_set_state(itnim, bfa_itnim_sm_iocdisable); + break; + + default: + bfa_assert(0); + } +} + +/** + * IOC h/w failed state. + */ +static void +bfa_itnim_sm_iocdisable(struct bfa_itnim_s *itnim, + enum bfa_itnim_event event) +{ + bfa_trc(itnim->bfa, itnim->rport->rport_tag); + bfa_trc(itnim->bfa, event); + + switch (event) { + case BFA_ITNIM_SM_DELETE: + bfa_sm_set_state(itnim, bfa_itnim_sm_uninit); + bfa_itnim_iotov_delete(itnim); + bfa_fcpim_delitn(itnim); + break; + + case BFA_ITNIM_SM_OFFLINE: + bfa_itnim_offline_cb(itnim); + break; + + case BFA_ITNIM_SM_ONLINE: + if (bfa_itnim_send_fwcreate(itnim)) + bfa_sm_set_state(itnim, bfa_itnim_sm_fwcreate); + else + bfa_sm_set_state(itnim, bfa_itnim_sm_fwcreate_qfull); + break; + + case BFA_ITNIM_SM_HWFAIL: + break; + + default: + bfa_assert(0); + } +} + +/** + * Itnim is deleted, waiting for firmware response to delete. + */ +static void +bfa_itnim_sm_deleting(struct bfa_itnim_s *itnim, enum bfa_itnim_event event) +{ + bfa_trc(itnim->bfa, itnim->rport->rport_tag); + bfa_trc(itnim->bfa, event); + + switch (event) { + case BFA_ITNIM_SM_FWRSP: + case BFA_ITNIM_SM_HWFAIL: + bfa_sm_set_state(itnim, bfa_itnim_sm_uninit); + bfa_fcpim_delitn(itnim); + break; + + default: + bfa_assert(0); + } +} + +static void +bfa_itnim_sm_deleting_qfull(struct bfa_itnim_s *itnim, + enum bfa_itnim_event event) +{ + bfa_trc(itnim->bfa, itnim->rport->rport_tag); + bfa_trc(itnim->bfa, event); + + switch (event) { + case BFA_ITNIM_SM_QRESUME: + bfa_sm_set_state(itnim, bfa_itnim_sm_deleting); + bfa_itnim_send_fwdelete(itnim); + break; + + case BFA_ITNIM_SM_HWFAIL: + bfa_sm_set_state(itnim, bfa_itnim_sm_uninit); + bfa_reqq_wcancel(&itnim->reqq_wait); + bfa_fcpim_delitn(itnim); + break; + + default: + bfa_assert(0); + } +} + + + +/** + * bfa_itnim_private + */ + +/** + * Initiate cleanup of all IOs on an IOC failure. + */ +static void +bfa_itnim_iocdisable_cleanup(struct bfa_itnim_s *itnim) +{ + struct bfa_tskim_s *tskim; + struct bfa_ioim_s *ioim; + struct list_head *qe, *qen; + + list_for_each_safe(qe, qen, &itnim->tsk_q) { + tskim = (struct bfa_tskim_s *) qe; + bfa_tskim_iocdisable(tskim); + } + + list_for_each_safe(qe, qen, &itnim->io_q) { + ioim = (struct bfa_ioim_s *) qe; + bfa_ioim_iocdisable(ioim); + } + + /** + * For IO request in pending queue, we pretend an early timeout. + */ + list_for_each_safe(qe, qen, &itnim->pending_q) { + ioim = (struct bfa_ioim_s *) qe; + bfa_ioim_tov(ioim); + } + + list_for_each_safe(qe, qen, &itnim->io_cleanup_q) { + ioim = (struct bfa_ioim_s *) qe; + bfa_ioim_iocdisable(ioim); + } +} + +/** + * IO cleanup completion + */ +static void +bfa_itnim_cleanp_comp(void *itnim_cbarg) +{ + struct bfa_itnim_s *itnim = itnim_cbarg; + + bfa_stats(itnim, cleanup_comps); + bfa_sm_send_event(itnim, BFA_ITNIM_SM_CLEANUP); +} + +/** + * Initiate cleanup of all IOs. + */ +static void +bfa_itnim_cleanup(struct bfa_itnim_s *itnim) +{ + struct bfa_ioim_s *ioim; + struct bfa_tskim_s *tskim; + struct list_head *qe, *qen; + + bfa_wc_init(&itnim->wc, bfa_itnim_cleanp_comp, itnim); + + list_for_each_safe(qe, qen, &itnim->io_q) { + ioim = (struct bfa_ioim_s *) qe; + + /** + * Move IO to a cleanup queue from active queue so that a later + * TM will not pickup this IO. + */ + list_del(&ioim->qe); + list_add_tail(&ioim->qe, &itnim->io_cleanup_q); + + bfa_wc_up(&itnim->wc); + bfa_ioim_cleanup(ioim); + } + + list_for_each_safe(qe, qen, &itnim->tsk_q) { + tskim = (struct bfa_tskim_s *) qe; + bfa_wc_up(&itnim->wc); + bfa_tskim_cleanup(tskim); + } + + bfa_wc_wait(&itnim->wc); +} + +static void +__bfa_cb_itnim_online(void *cbarg, bfa_boolean_t complete) +{ + struct bfa_itnim_s *itnim = cbarg; + + if (complete) + bfa_cb_itnim_online(itnim->ditn); +} + +static void +__bfa_cb_itnim_offline(void *cbarg, bfa_boolean_t complete) +{ + struct bfa_itnim_s *itnim = cbarg; + + if (complete) + bfa_cb_itnim_offline(itnim->ditn); +} + +static void +__bfa_cb_itnim_sler(void *cbarg, bfa_boolean_t complete) +{ + struct bfa_itnim_s *itnim = cbarg; + + if (complete) + bfa_cb_itnim_sler(itnim->ditn); +} + +/** + * Call to resume any I/O requests waiting for room in request queue. + */ +static void +bfa_itnim_qresume(void *cbarg) +{ + struct bfa_itnim_s *itnim = cbarg; + + bfa_sm_send_event(itnim, BFA_ITNIM_SM_QRESUME); +} + + + + +/** + * bfa_itnim_public + */ + +void +bfa_itnim_iodone(struct bfa_itnim_s *itnim) +{ + bfa_wc_down(&itnim->wc); +} + +void +bfa_itnim_tskdone(struct bfa_itnim_s *itnim) +{ + bfa_wc_down(&itnim->wc); +} + +void +bfa_itnim_meminfo(struct bfa_iocfc_cfg_s *cfg, u32 *km_len, + u32 *dm_len) +{ + /** + * ITN memory + */ + *km_len += cfg->fwcfg.num_rports * sizeof(struct bfa_itnim_s); +} + +void +bfa_itnim_attach(struct bfa_fcpim_mod_s *fcpim, struct bfa_meminfo_s *minfo) +{ + struct bfa_s *bfa = fcpim->bfa; + struct bfa_itnim_s *itnim; + int i; + + INIT_LIST_HEAD(&fcpim->itnim_q); + + itnim = (struct bfa_itnim_s *) bfa_meminfo_kva(minfo); + fcpim->itnim_arr = itnim; + + for (i = 0; i < fcpim->num_itnims; i++, itnim++) { + bfa_os_memset(itnim, 0, sizeof(struct bfa_itnim_s)); + itnim->bfa = bfa; + itnim->fcpim = fcpim; + itnim->reqq = BFA_REQQ_QOS_LO; + itnim->rport = BFA_RPORT_FROM_TAG(bfa, i); + itnim->iotov_active = BFA_FALSE; + bfa_reqq_winit(&itnim->reqq_wait, bfa_itnim_qresume, itnim); + + INIT_LIST_HEAD(&itnim->io_q); + INIT_LIST_HEAD(&itnim->io_cleanup_q); + INIT_LIST_HEAD(&itnim->pending_q); + INIT_LIST_HEAD(&itnim->tsk_q); + INIT_LIST_HEAD(&itnim->delay_comp_q); + bfa_sm_set_state(itnim, bfa_itnim_sm_uninit); + } + + bfa_meminfo_kva(minfo) = (u8 *) itnim; +} + +void +bfa_itnim_iocdisable(struct bfa_itnim_s *itnim) +{ + bfa_stats(itnim, ioc_disabled); + bfa_sm_send_event(itnim, BFA_ITNIM_SM_HWFAIL); +} + +static bfa_boolean_t +bfa_itnim_send_fwcreate(struct bfa_itnim_s *itnim) +{ + struct bfi_itnim_create_req_s *m; + + itnim->msg_no++; + + /** + * check for room in queue to send request now + */ + m = bfa_reqq_next(itnim->bfa, itnim->reqq); + if (!m) { + bfa_reqq_wait(itnim->bfa, itnim->reqq, &itnim->reqq_wait); + return BFA_FALSE; + } + + bfi_h2i_set(m->mh, BFI_MC_ITNIM, BFI_ITNIM_H2I_CREATE_REQ, + bfa_lpuid(itnim->bfa)); + m->fw_handle = itnim->rport->fw_handle; + m->class = FC_CLASS_3; + m->seq_rec = itnim->seq_rec; + m->msg_no = itnim->msg_no; + + /** + * queue I/O message to firmware + */ + bfa_reqq_produce(itnim->bfa, itnim->reqq); + return BFA_TRUE; +} + +static bfa_boolean_t +bfa_itnim_send_fwdelete(struct bfa_itnim_s *itnim) +{ + struct bfi_itnim_delete_req_s *m; + + /** + * check for room in queue to send request now + */ + m = bfa_reqq_next(itnim->bfa, itnim->reqq); + if (!m) { + bfa_reqq_wait(itnim->bfa, itnim->reqq, &itnim->reqq_wait); + return BFA_FALSE; + } + + bfi_h2i_set(m->mh, BFI_MC_ITNIM, BFI_ITNIM_H2I_DELETE_REQ, + bfa_lpuid(itnim->bfa)); + m->fw_handle = itnim->rport->fw_handle; + + /** + * queue I/O message to firmware + */ + bfa_reqq_produce(itnim->bfa, itnim->reqq); + return BFA_TRUE; +} + +/** + * Cleanup all pending failed inflight requests. + */ +static void +bfa_itnim_delayed_comp(struct bfa_itnim_s *itnim, bfa_boolean_t iotov) +{ + struct bfa_ioim_s *ioim; + struct list_head *qe, *qen; + + list_for_each_safe(qe, qen, &itnim->delay_comp_q) { + ioim = (struct bfa_ioim_s *)qe; + bfa_ioim_delayed_comp(ioim, iotov); + } +} + +/** + * Start all pending IO requests. + */ +static void +bfa_itnim_iotov_online(struct bfa_itnim_s *itnim) +{ + struct bfa_ioim_s *ioim; + + bfa_itnim_iotov_stop(itnim); + + /** + * Abort all inflight IO requests in the queue + */ + bfa_itnim_delayed_comp(itnim, BFA_FALSE); + + /** + * Start all pending IO requests. + */ + while (!list_empty(&itnim->pending_q)) { + bfa_q_deq(&itnim->pending_q, &ioim); + list_add_tail(&ioim->qe, &itnim->io_q); + bfa_ioim_start(ioim); + } +} + +/** + * Fail all pending IO requests + */ +static void +bfa_itnim_iotov_cleanup(struct bfa_itnim_s *itnim) +{ + struct bfa_ioim_s *ioim; + + /** + * Fail all inflight IO requests in the queue + */ + bfa_itnim_delayed_comp(itnim, BFA_TRUE); + + /** + * Fail any pending IO requests. + */ + while (!list_empty(&itnim->pending_q)) { + bfa_q_deq(&itnim->pending_q, &ioim); + list_add_tail(&ioim->qe, &ioim->fcpim->ioim_comp_q); + bfa_ioim_tov(ioim); + } +} + +/** + * IO TOV timer callback. Fail any pending IO requests. + */ +static void +bfa_itnim_iotov(void *itnim_arg) +{ + struct bfa_itnim_s *itnim = itnim_arg; + + itnim->iotov_active = BFA_FALSE; + + bfa_cb_itnim_tov_begin(itnim->ditn); + bfa_itnim_iotov_cleanup(itnim); + bfa_cb_itnim_tov(itnim->ditn); +} + +/** + * Start IO TOV timer for failing back pending IO requests in offline state. + */ +static void +bfa_itnim_iotov_start(struct bfa_itnim_s *itnim) +{ + if (itnim->fcpim->path_tov > 0) { + + itnim->iotov_active = BFA_TRUE; + bfa_assert(bfa_itnim_hold_io(itnim)); + bfa_timer_start(itnim->bfa, &itnim->timer, + bfa_itnim_iotov, itnim, itnim->fcpim->path_tov); + } +} + +/** + * Stop IO TOV timer. + */ +static void +bfa_itnim_iotov_stop(struct bfa_itnim_s *itnim) +{ + if (itnim->iotov_active) { + itnim->iotov_active = BFA_FALSE; + bfa_timer_stop(&itnim->timer); + } +} + +/** + * Stop IO TOV timer. + */ +static void +bfa_itnim_iotov_delete(struct bfa_itnim_s *itnim) +{ + bfa_boolean_t pathtov_active = BFA_FALSE; + + if (itnim->iotov_active) + pathtov_active = BFA_TRUE; + + bfa_itnim_iotov_stop(itnim); + if (pathtov_active) + bfa_cb_itnim_tov_begin(itnim->ditn); + bfa_itnim_iotov_cleanup(itnim); + if (pathtov_active) + bfa_cb_itnim_tov(itnim->ditn); +} + + + +/** + * bfa_itnim_public + */ + +/** + * Itnim interrupt processing. + */ +void +bfa_itnim_isr(struct bfa_s *bfa, struct bfi_msg_s *m) +{ + struct bfa_fcpim_mod_s *fcpim = BFA_FCPIM_MOD(bfa); + union bfi_itnim_i2h_msg_u msg; + struct bfa_itnim_s *itnim; + + bfa_trc(bfa, m->mhdr.msg_id); + + msg.msg = m; + + switch (m->mhdr.msg_id) { + case BFI_ITNIM_I2H_CREATE_RSP: + itnim = BFA_ITNIM_FROM_TAG(fcpim, + msg.create_rsp->bfa_handle); + bfa_assert(msg.create_rsp->status == BFA_STATUS_OK); + bfa_stats(itnim, create_comps); + bfa_sm_send_event(itnim, BFA_ITNIM_SM_FWRSP); + break; + + case BFI_ITNIM_I2H_DELETE_RSP: + itnim = BFA_ITNIM_FROM_TAG(fcpim, + msg.delete_rsp->bfa_handle); + bfa_assert(msg.delete_rsp->status == BFA_STATUS_OK); + bfa_stats(itnim, delete_comps); + bfa_sm_send_event(itnim, BFA_ITNIM_SM_FWRSP); + break; + + case BFI_ITNIM_I2H_SLER_EVENT: + itnim = BFA_ITNIM_FROM_TAG(fcpim, + msg.sler_event->bfa_handle); + bfa_stats(itnim, sler_events); + bfa_sm_send_event(itnim, BFA_ITNIM_SM_SLER); + break; + + default: + bfa_trc(bfa, m->mhdr.msg_id); + bfa_assert(0); + } +} + + + +/** + * bfa_itnim_api + */ + +struct bfa_itnim_s * +bfa_itnim_create(struct bfa_s *bfa, struct bfa_rport_s *rport, void *ditn) +{ + struct bfa_fcpim_mod_s *fcpim = BFA_FCPIM_MOD(bfa); + struct bfa_itnim_s *itnim; + + itnim = BFA_ITNIM_FROM_TAG(fcpim, rport->rport_tag); + bfa_assert(itnim->rport == rport); + + itnim->ditn = ditn; + + bfa_stats(itnim, creates); + bfa_sm_send_event(itnim, BFA_ITNIM_SM_CREATE); + + return (itnim); +} + +void +bfa_itnim_delete(struct bfa_itnim_s *itnim) +{ + bfa_stats(itnim, deletes); + bfa_sm_send_event(itnim, BFA_ITNIM_SM_DELETE); +} + +void +bfa_itnim_online(struct bfa_itnim_s *itnim, bfa_boolean_t seq_rec) +{ + itnim->seq_rec = seq_rec; + bfa_stats(itnim, onlines); + bfa_sm_send_event(itnim, BFA_ITNIM_SM_ONLINE); +} + +void +bfa_itnim_offline(struct bfa_itnim_s *itnim) +{ + bfa_stats(itnim, offlines); + bfa_sm_send_event(itnim, BFA_ITNIM_SM_OFFLINE); +} + +/** + * Return true if itnim is considered offline for holding off IO request. + * IO is not held if itnim is being deleted. + */ +bfa_boolean_t +bfa_itnim_hold_io(struct bfa_itnim_s *itnim) +{ + return ( + itnim->fcpim->path_tov && itnim->iotov_active && + (bfa_sm_cmp_state(itnim, bfa_itnim_sm_fwcreate) || + bfa_sm_cmp_state(itnim, bfa_itnim_sm_sler) || + bfa_sm_cmp_state(itnim, bfa_itnim_sm_cleanup_offline) || + bfa_sm_cmp_state(itnim, bfa_itnim_sm_fwdelete) || + bfa_sm_cmp_state(itnim, bfa_itnim_sm_offline) || + bfa_sm_cmp_state(itnim, bfa_itnim_sm_iocdisable)) +); +} + +void +bfa_itnim_get_stats(struct bfa_itnim_s *itnim, + struct bfa_itnim_hal_stats_s *stats) +{ + *stats = itnim->stats; +} + +void +bfa_itnim_clear_stats(struct bfa_itnim_s *itnim) +{ + bfa_os_memset(&itnim->stats, 0, sizeof(itnim->stats)); +} + + diff --git a/drivers/scsi/bfa/bfa_log.c b/drivers/scsi/bfa/bfa_log.c new file mode 100644 index 000000000000..c2735e55cf03 --- /dev/null +++ b/drivers/scsi/bfa/bfa_log.c @@ -0,0 +1,346 @@ +/* + * Copyright (c) 2005-2009 Brocade Communications Systems, Inc. + * All rights reserved + * www.brocade.com + * + * Linux driver for Brocade Fibre Channel Host Bus Adapter. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (GPL) Version 2 as + * published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +/** + * bfa_log.c BFA log library + */ + +#include <bfa_os_inc.h> +#include <cs/bfa_log.h> + +/* + * global log info structure + */ +struct bfa_log_info_s { + u32 start_idx; /* start index for a module */ + u32 total_count; /* total count for a module */ + enum bfa_log_severity level; /* global log level */ + bfa_log_cb_t cbfn; /* callback function */ +}; + +static struct bfa_log_info_s bfa_log_info[BFA_LOG_MODULE_ID_MAX + 1]; +static u32 bfa_log_msg_total_count; +static int bfa_log_initialized; + +static char *bfa_log_severity[] = + { "[none]", "[critical]", "[error]", "[warn]", "[info]", "" }; + +/** + * BFA log library initialization + * + * The log library initialization includes the following, + * - set log instance name and callback function + * - read the message array generated from xml files + * - calculate start index for each module + * - calculate message count for each module + * - perform error checking + * + * @param[in] log_mod - log module info + * @param[in] instance_name - instance name + * @param[in] cbfn - callback function + * + * It return 0 on success, or -1 on failure + */ +int +bfa_log_init(struct bfa_log_mod_s *log_mod, char *instance_name, + bfa_log_cb_t cbfn) +{ + struct bfa_log_msgdef_s *msg; + u32 pre_mod_id = 0; + u32 cur_mod_id = 0; + u32 i, pre_idx, idx, msg_id; + + /* + * set instance name + */ + if (log_mod) { + strncpy(log_mod->instance_info, instance_name, + sizeof(log_mod->instance_info)); + log_mod->cbfn = cbfn; + for (i = 0; i <= BFA_LOG_MODULE_ID_MAX; i++) + log_mod->log_level[i] = BFA_LOG_WARNING; + } + + if (bfa_log_initialized) + return 0; + + for (i = 0; i <= BFA_LOG_MODULE_ID_MAX; i++) { + bfa_log_info[i].start_idx = 0; + bfa_log_info[i].total_count = 0; + bfa_log_info[i].level = BFA_LOG_WARNING; + bfa_log_info[i].cbfn = cbfn; + } + + pre_idx = 0; + idx = 0; + msg = bfa_log_msg_array; + msg_id = BFA_LOG_GET_MSG_ID(msg); + pre_mod_id = BFA_LOG_GET_MOD_ID(msg_id); + while (msg_id != 0) { + cur_mod_id = BFA_LOG_GET_MOD_ID(msg_id); + + if (cur_mod_id > BFA_LOG_MODULE_ID_MAX) { + cbfn(log_mod, msg_id, + "%s%s log: module id %u out of range\n", + BFA_LOG_CAT_NAME, + bfa_log_severity[BFA_LOG_ERROR], + cur_mod_id); + return -1; + } + + if (pre_mod_id > BFA_LOG_MODULE_ID_MAX) { + cbfn(log_mod, msg_id, + "%s%s log: module id %u out of range\n", + BFA_LOG_CAT_NAME, + bfa_log_severity[BFA_LOG_ERROR], + pre_mod_id); + return -1; + } + + if (cur_mod_id != pre_mod_id) { + bfa_log_info[pre_mod_id].start_idx = pre_idx; + bfa_log_info[pre_mod_id].total_count = idx - pre_idx; + pre_mod_id = cur_mod_id; + pre_idx = idx; + } + + idx++; + msg++; + msg_id = BFA_LOG_GET_MSG_ID(msg); + } + + bfa_log_info[cur_mod_id].start_idx = pre_idx; + bfa_log_info[cur_mod_id].total_count = idx - pre_idx; + bfa_log_msg_total_count = idx; + + cbfn(log_mod, msg_id, "%s%s log: init OK, msg total count %u\n", + BFA_LOG_CAT_NAME, + bfa_log_severity[BFA_LOG_INFO], bfa_log_msg_total_count); + + bfa_log_initialized = 1; + + return 0; +} + +/** + * BFA log set log level for a module + * + * @param[in] log_mod - log module info + * @param[in] mod_id - module id + * @param[in] log_level - log severity level + * + * It return BFA_STATUS_OK on success, or > 0 on failure + */ +bfa_status_t +bfa_log_set_level(struct bfa_log_mod_s *log_mod, int mod_id, + enum bfa_log_severity log_level) +{ + if (mod_id <= BFA_LOG_UNUSED_ID || mod_id > BFA_LOG_MODULE_ID_MAX) + return BFA_STATUS_EINVAL; + + if (log_level <= BFA_LOG_INVALID || log_level > BFA_LOG_LEVEL_MAX) + return BFA_STATUS_EINVAL; + + if (log_mod) + log_mod->log_level[mod_id] = log_level; + else + bfa_log_info[mod_id].level = log_level; + + return BFA_STATUS_OK; +} + +/** + * BFA log set log level for all modules + * + * @param[in] log_mod - log module info + * @param[in] log_level - log severity level + * + * It return BFA_STATUS_OK on success, or > 0 on failure + */ +bfa_status_t +bfa_log_set_level_all(struct bfa_log_mod_s *log_mod, + enum bfa_log_severity log_level) +{ + int mod_id = BFA_LOG_UNUSED_ID + 1; + + if (log_level <= BFA_LOG_INVALID || log_level > BFA_LOG_LEVEL_MAX) + return BFA_STATUS_EINVAL; + + if (log_mod) { + for (; mod_id <= BFA_LOG_MODULE_ID_MAX; mod_id++) + log_mod->log_level[mod_id] = log_level; + } else { + for (; mod_id <= BFA_LOG_MODULE_ID_MAX; mod_id++) + bfa_log_info[mod_id].level = log_level; + } + + return BFA_STATUS_OK; +} + +/** + * BFA log set log level for all aen sub-modules + * + * @param[in] log_mod - log module info + * @param[in] log_level - log severity level + * + * It return BFA_STATUS_OK on success, or > 0 on failure + */ +bfa_status_t +bfa_log_set_level_aen(struct bfa_log_mod_s *log_mod, + enum bfa_log_severity log_level) +{ + int mod_id = BFA_LOG_AEN_MIN + 1; + + if (log_mod) { + for (; mod_id <= BFA_LOG_AEN_MAX; mod_id++) + log_mod->log_level[mod_id] = log_level; + } else { + for (; mod_id <= BFA_LOG_AEN_MAX; mod_id++) + bfa_log_info[mod_id].level = log_level; + } + + return BFA_STATUS_OK; +} + +/** + * BFA log get log level for a module + * + * @param[in] log_mod - log module info + * @param[in] mod_id - module id + * + * It returns log level or -1 on error + */ +enum bfa_log_severity +bfa_log_get_level(struct bfa_log_mod_s *log_mod, int mod_id) +{ + if (mod_id <= BFA_LOG_UNUSED_ID || mod_id > BFA_LOG_MODULE_ID_MAX) + return BFA_LOG_INVALID; + + if (log_mod) + return (log_mod->log_level[mod_id]); + else + return (bfa_log_info[mod_id].level); +} + +enum bfa_log_severity +bfa_log_get_msg_level(struct bfa_log_mod_s *log_mod, u32 msg_id) +{ + struct bfa_log_msgdef_s *msg; + u32 mod = BFA_LOG_GET_MOD_ID(msg_id); + u32 idx = BFA_LOG_GET_MSG_IDX(msg_id) - 1; + + if (!bfa_log_initialized) + return BFA_LOG_INVALID; + + if (mod > BFA_LOG_MODULE_ID_MAX) + return BFA_LOG_INVALID; + + if (idx >= bfa_log_info[mod].total_count) { + bfa_log_info[mod].cbfn(log_mod, msg_id, + "%s%s log: inconsistent idx %u vs. total count %u\n", + BFA_LOG_CAT_NAME, bfa_log_severity[BFA_LOG_ERROR], idx, + bfa_log_info[mod].total_count); + return BFA_LOG_INVALID; + } + + msg = bfa_log_msg_array + bfa_log_info[mod].start_idx + idx; + if (msg_id != BFA_LOG_GET_MSG_ID(msg)) { + bfa_log_info[mod].cbfn(log_mod, msg_id, + "%s%s log: inconsistent msg id %u array msg id %u\n", + BFA_LOG_CAT_NAME, bfa_log_severity[BFA_LOG_ERROR], + msg_id, BFA_LOG_GET_MSG_ID(msg)); + return BFA_LOG_INVALID; + } + + return BFA_LOG_GET_SEVERITY(msg); +} + +/** + * BFA log message handling + * + * BFA log message handling finds the message based on message id and prints + * out the message based on its format and arguments. It also does prefix + * the severity etc. + * + * @param[in] log_mod - log module info + * @param[in] msg_id - message id + * @param[in] ... - message arguments + * + * It return 0 on success, or -1 on errors + */ +int +bfa_log(struct bfa_log_mod_s *log_mod, u32 msg_id, ...) +{ + va_list ap; + char buf[256]; + struct bfa_log_msgdef_s *msg; + int log_level; + u32 mod = BFA_LOG_GET_MOD_ID(msg_id); + u32 idx = BFA_LOG_GET_MSG_IDX(msg_id) - 1; + + if (!bfa_log_initialized) + return -1; + + if (mod > BFA_LOG_MODULE_ID_MAX) + return -1; + + if (idx >= bfa_log_info[mod].total_count) { + bfa_log_info[mod]. + cbfn + (log_mod, msg_id, + "%s%s log: inconsistent idx %u vs. total count %u\n", + BFA_LOG_CAT_NAME, bfa_log_severity[BFA_LOG_ERROR], idx, + bfa_log_info[mod].total_count); + return -1; + } + + msg = bfa_log_msg_array + bfa_log_info[mod].start_idx + idx; + if (msg_id != BFA_LOG_GET_MSG_ID(msg)) { + bfa_log_info[mod]. + cbfn + (log_mod, msg_id, + "%s%s log: inconsistent msg id %u array msg id %u\n", + BFA_LOG_CAT_NAME, bfa_log_severity[BFA_LOG_ERROR], + msg_id, BFA_LOG_GET_MSG_ID(msg)); + return -1; + } + + log_level = log_mod ? log_mod->log_level[mod] : bfa_log_info[mod].level; + if ((BFA_LOG_GET_SEVERITY(msg) > log_level) && + (msg->attributes != BFA_LOG_ATTR_NONE)) + return 0; + + va_start(ap, msg_id); + bfa_os_vsprintf(buf, BFA_LOG_GET_MSG_FMT_STRING(msg), ap); + va_end(ap); + + if (log_mod) + log_mod->cbfn(log_mod, msg_id, "%s[%s]%s%s %s: %s\n", + BFA_LOG_CAT_NAME, log_mod->instance_info, + bfa_log_severity[BFA_LOG_GET_SEVERITY(msg)], + (msg->attributes & BFA_LOG_ATTR_AUDIT) + ? " (audit) " : "", msg->msg_value, buf); + else + bfa_log_info[mod].cbfn(log_mod, msg_id, "%s%s%s %s: %s\n", + BFA_LOG_CAT_NAME, + bfa_log_severity[BFA_LOG_GET_SEVERITY(msg)], + (msg->attributes & BFA_LOG_ATTR_AUDIT) ? + " (audit) " : "", msg->msg_value, buf); + + return 0; +} + diff --git a/drivers/scsi/bfa/bfa_log_module.c b/drivers/scsi/bfa/bfa_log_module.c new file mode 100644 index 000000000000..5c154d341d69 --- /dev/null +++ b/drivers/scsi/bfa/bfa_log_module.c @@ -0,0 +1,451 @@ +/* + * Copyright (c) 2005-2009 Brocade Communications Systems, Inc. + * All rights reserved + * www.brocade.com + * + * Linux driver for Brocade Fibre Channel Host Bus Adapter. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (GPL) Version 2 as + * published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#include <cs/bfa_log.h> +#include <aen/bfa_aen_adapter.h> +#include <aen/bfa_aen_audit.h> +#include <aen/bfa_aen_ethport.h> +#include <aen/bfa_aen_ioc.h> +#include <aen/bfa_aen_itnim.h> +#include <aen/bfa_aen_lport.h> +#include <aen/bfa_aen_port.h> +#include <aen/bfa_aen_rport.h> +#include <log/bfa_log_fcs.h> +#include <log/bfa_log_hal.h> +#include <log/bfa_log_linux.h> +#include <log/bfa_log_wdrv.h> + +struct bfa_log_msgdef_s bfa_log_msg_array[] = { + + +/* messages define for BFA_AEN_CAT_ADAPTER Module */ +{BFA_AEN_ADAPTER_ADD, BFA_LOG_ATTR_NONE | BFA_LOG_ATTR_LOG, BFA_LOG_INFO, + "BFA_AEN_ADAPTER_ADD", + "New adapter found: SN = %s, base port WWN = %s.", + ((BFA_LOG_S << BFA_LOG_ARG0) | (BFA_LOG_S << BFA_LOG_ARG1) | 0), 2}, + +{BFA_AEN_ADAPTER_REMOVE, BFA_LOG_ATTR_NONE | BFA_LOG_ATTR_LOG, + BFA_LOG_WARNING, "BFA_AEN_ADAPTER_REMOVE", + "Adapter removed: SN = %s.", + ((BFA_LOG_S << BFA_LOG_ARG0) | 0), 1}, + + + + +/* messages define for BFA_AEN_CAT_AUDIT Module */ +{BFA_AEN_AUDIT_AUTH_ENABLE, BFA_LOG_ATTR_NONE | BFA_LOG_ATTR_LOG, + BFA_LOG_INFO, "BFA_AEN_AUDIT_AUTH_ENABLE", + "Authentication enabled for base port: WWN = %s.", + ((BFA_LOG_S << BFA_LOG_ARG0) | 0), 1}, + +{BFA_AEN_AUDIT_AUTH_DISABLE, BFA_LOG_ATTR_NONE | BFA_LOG_ATTR_LOG, + BFA_LOG_INFO, "BFA_AEN_AUDIT_AUTH_DISABLE", + "Authentication disabled for base port: WWN = %s.", + ((BFA_LOG_S << BFA_LOG_ARG0) | 0), 1}, + + + + +/* messages define for BFA_AEN_CAT_ETHPORT Module */ +{BFA_AEN_ETHPORT_LINKUP, BFA_LOG_ATTR_NONE | BFA_LOG_ATTR_LOG, BFA_LOG_INFO, + "BFA_AEN_ETHPORT_LINKUP", + "Base port ethernet linkup: mac = %s.", + ((BFA_LOG_S << BFA_LOG_ARG0) | 0), 1}, + +{BFA_AEN_ETHPORT_LINKDOWN, BFA_LOG_ATTR_NONE | BFA_LOG_ATTR_LOG, BFA_LOG_INFO, + "BFA_AEN_ETHPORT_LINKDOWN", + "Base port ethernet linkdown: mac = %s.", + ((BFA_LOG_S << BFA_LOG_ARG0) | 0), 1}, + +{BFA_AEN_ETHPORT_ENABLE, BFA_LOG_ATTR_NONE | BFA_LOG_ATTR_LOG, BFA_LOG_INFO, + "BFA_AEN_ETHPORT_ENABLE", + "Base port ethernet interface enabled: mac = %s.", + ((BFA_LOG_S << BFA_LOG_ARG0) | 0), 1}, + +{BFA_AEN_ETHPORT_DISABLE, BFA_LOG_ATTR_NONE | BFA_LOG_ATTR_LOG, BFA_LOG_INFO, + "BFA_AEN_ETHPORT_DISABLE", + "Base port ethernet interface disabled: mac = %s.", + ((BFA_LOG_S << BFA_LOG_ARG0) | 0), 1}, + + + + +/* messages define for BFA_AEN_CAT_IOC Module */ +{BFA_AEN_IOC_HBGOOD, BFA_LOG_ATTR_NONE | BFA_LOG_ATTR_LOG, BFA_LOG_INFO, + "BFA_AEN_IOC_HBGOOD", + "Heart Beat of IOC %d is good.", + ((BFA_LOG_D << BFA_LOG_ARG0) | 0), 1}, + +{BFA_AEN_IOC_HBFAIL, BFA_LOG_ATTR_NONE | BFA_LOG_ATTR_LOG, BFA_LOG_CRITICAL, + "BFA_AEN_IOC_HBFAIL", + "Heart Beat of IOC %d has failed.", + ((BFA_LOG_D << BFA_LOG_ARG0) | 0), 1}, + +{BFA_AEN_IOC_ENABLE, BFA_LOG_ATTR_NONE | BFA_LOG_ATTR_LOG, BFA_LOG_INFO, + "BFA_AEN_IOC_ENABLE", + "IOC %d is enabled.", + ((BFA_LOG_D << BFA_LOG_ARG0) | 0), 1}, + +{BFA_AEN_IOC_DISABLE, BFA_LOG_ATTR_NONE | BFA_LOG_ATTR_LOG, BFA_LOG_INFO, + "BFA_AEN_IOC_DISABLE", + "IOC %d is disabled.", + ((BFA_LOG_D << BFA_LOG_ARG0) | 0), 1}, + +{BFA_AEN_IOC_FWMISMATCH, BFA_LOG_ATTR_NONE | BFA_LOG_ATTR_LOG, + BFA_LOG_CRITICAL, "BFA_AEN_IOC_FWMISMATCH", + "Running firmware version is incompatible with the driver version.", + (0), 0}, + + + + +/* messages define for BFA_AEN_CAT_ITNIM Module */ +{BFA_AEN_ITNIM_ONLINE, BFA_LOG_ATTR_NONE | BFA_LOG_ATTR_LOG, BFA_LOG_INFO, + "BFA_AEN_ITNIM_ONLINE", + "Target (WWN = %s) is online for initiator (WWN = %s).", + ((BFA_LOG_S << BFA_LOG_ARG0) | (BFA_LOG_S << BFA_LOG_ARG1) | 0), 2}, + +{BFA_AEN_ITNIM_OFFLINE, BFA_LOG_ATTR_NONE | BFA_LOG_ATTR_LOG, BFA_LOG_INFO, + "BFA_AEN_ITNIM_OFFLINE", + "Target (WWN = %s) offlined by initiator (WWN = %s).", + ((BFA_LOG_S << BFA_LOG_ARG0) | (BFA_LOG_S << BFA_LOG_ARG1) | 0), 2}, + +{BFA_AEN_ITNIM_DISCONNECT, BFA_LOG_ATTR_NONE | BFA_LOG_ATTR_LOG, + BFA_LOG_ERROR, "BFA_AEN_ITNIM_DISCONNECT", + "Target (WWN = %s) connectivity lost for initiator (WWN = %s).", + ((BFA_LOG_S << BFA_LOG_ARG0) | (BFA_LOG_S << BFA_LOG_ARG1) | 0), 2}, + + + + +/* messages define for BFA_AEN_CAT_LPORT Module */ +{BFA_AEN_LPORT_NEW, BFA_LOG_ATTR_NONE | BFA_LOG_ATTR_LOG, BFA_LOG_INFO, + "BFA_AEN_LPORT_NEW", + "New logical port created: WWN = %s, Role = %s.", + ((BFA_LOG_S << BFA_LOG_ARG0) | (BFA_LOG_S << BFA_LOG_ARG1) | 0), 2}, + +{BFA_AEN_LPORT_DELETE, BFA_LOG_ATTR_NONE | BFA_LOG_ATTR_LOG, BFA_LOG_INFO, + "BFA_AEN_LPORT_DELETE", + "Logical port deleted: WWN = %s, Role = %s.", + ((BFA_LOG_S << BFA_LOG_ARG0) | (BFA_LOG_S << BFA_LOG_ARG1) | 0), 2}, + +{BFA_AEN_LPORT_ONLINE, BFA_LOG_ATTR_NONE | BFA_LOG_ATTR_LOG, BFA_LOG_INFO, + "BFA_AEN_LPORT_ONLINE", + "Logical port online: WWN = %s, Role = %s.", + ((BFA_LOG_S << BFA_LOG_ARG0) | (BFA_LOG_S << BFA_LOG_ARG1) | 0), 2}, + +{BFA_AEN_LPORT_OFFLINE, BFA_LOG_ATTR_NONE | BFA_LOG_ATTR_LOG, BFA_LOG_INFO, + "BFA_AEN_LPORT_OFFLINE", + "Logical port taken offline: WWN = %s, Role = %s.", + ((BFA_LOG_S << BFA_LOG_ARG0) | (BFA_LOG_S << BFA_LOG_ARG1) | 0), 2}, + +{BFA_AEN_LPORT_DISCONNECT, BFA_LOG_ATTR_NONE | BFA_LOG_ATTR_LOG, + BFA_LOG_ERROR, "BFA_AEN_LPORT_DISCONNECT", + "Logical port lost fabric connectivity: WWN = %s, Role = %s.", + ((BFA_LOG_S << BFA_LOG_ARG0) | (BFA_LOG_S << BFA_LOG_ARG1) | 0), 2}, + +{BFA_AEN_LPORT_NEW_PROP, BFA_LOG_ATTR_NONE | BFA_LOG_ATTR_LOG, BFA_LOG_INFO, + "BFA_AEN_LPORT_NEW_PROP", + "New virtual port created using proprietary interface: WWN = %s, Role = %s.", + ((BFA_LOG_S << BFA_LOG_ARG0) | (BFA_LOG_S << BFA_LOG_ARG1) | 0), 2}, + +{BFA_AEN_LPORT_DELETE_PROP, BFA_LOG_ATTR_NONE | BFA_LOG_ATTR_LOG, + BFA_LOG_INFO, "BFA_AEN_LPORT_DELETE_PROP", + "Virtual port deleted using proprietary interface: WWN = %s, Role = %s.", + ((BFA_LOG_S << BFA_LOG_ARG0) | (BFA_LOG_S << BFA_LOG_ARG1) | 0), 2}, + +{BFA_AEN_LPORT_NEW_STANDARD, BFA_LOG_ATTR_NONE | BFA_LOG_ATTR_LOG, + BFA_LOG_INFO, "BFA_AEN_LPORT_NEW_STANDARD", + "New virtual port created using standard interface: WWN = %s, Role = %s.", + ((BFA_LOG_S << BFA_LOG_ARG0) | (BFA_LOG_S << BFA_LOG_ARG1) | 0), 2}, + +{BFA_AEN_LPORT_DELETE_STANDARD, BFA_LOG_ATTR_NONE | BFA_LOG_ATTR_LOG, + BFA_LOG_INFO, "BFA_AEN_LPORT_DELETE_STANDARD", + "Virtual port deleted using standard interface: WWN = %s, Role = %s.", + ((BFA_LOG_S << BFA_LOG_ARG0) | (BFA_LOG_S << BFA_LOG_ARG1) | 0), 2}, + +{BFA_AEN_LPORT_NPIV_DUP_WWN, BFA_LOG_ATTR_NONE | BFA_LOG_ATTR_LOG, + BFA_LOG_WARNING, "BFA_AEN_LPORT_NPIV_DUP_WWN", + "Virtual port login failed. Duplicate WWN = %s reported by fabric.", + ((BFA_LOG_S << BFA_LOG_ARG0) | 0), 1}, + +{BFA_AEN_LPORT_NPIV_FABRIC_MAX, BFA_LOG_ATTR_NONE | BFA_LOG_ATTR_LOG, + BFA_LOG_WARNING, "BFA_AEN_LPORT_NPIV_FABRIC_MAX", + "Virtual port (WWN = %s) login failed. Max NPIV ports already exist in" + " fabric/fport.", + ((BFA_LOG_S << BFA_LOG_ARG0) | 0), 1}, + +{BFA_AEN_LPORT_NPIV_UNKNOWN, BFA_LOG_ATTR_NONE | BFA_LOG_ATTR_LOG, + BFA_LOG_WARNING, "BFA_AEN_LPORT_NPIV_UNKNOWN", + "Virtual port (WWN = %s) login failed.", + ((BFA_LOG_S << BFA_LOG_ARG0) | 0), 1}, + + + + +/* messages define for BFA_AEN_CAT_PORT Module */ +{BFA_AEN_PORT_ONLINE, BFA_LOG_ATTR_NONE, BFA_LOG_INFO, "BFA_AEN_PORT_ONLINE", + "Base port online: WWN = %s.", + ((BFA_LOG_S << BFA_LOG_ARG0) | 0), 1}, + +{BFA_AEN_PORT_OFFLINE, BFA_LOG_ATTR_NONE | BFA_LOG_ATTR_LOG, BFA_LOG_WARNING, + "BFA_AEN_PORT_OFFLINE", + "Base port offline: WWN = %s.", + ((BFA_LOG_S << BFA_LOG_ARG0) | 0), 1}, + +{BFA_AEN_PORT_RLIR, BFA_LOG_ATTR_NONE | BFA_LOG_ATTR_LOG, BFA_LOG_INFO, + "BFA_AEN_PORT_RLIR", + "RLIR event not supported.", + (0), 0}, + +{BFA_AEN_PORT_SFP_INSERT, BFA_LOG_ATTR_NONE | BFA_LOG_ATTR_LOG, BFA_LOG_INFO, + "BFA_AEN_PORT_SFP_INSERT", + "New SFP found: WWN/MAC = %s.", + ((BFA_LOG_S << BFA_LOG_ARG0) | 0), 1}, + +{BFA_AEN_PORT_SFP_REMOVE, BFA_LOG_ATTR_NONE | BFA_LOG_ATTR_LOG, + BFA_LOG_WARNING, "BFA_AEN_PORT_SFP_REMOVE", + "SFP removed: WWN/MAC = %s.", + ((BFA_LOG_S << BFA_LOG_ARG0) | 0), 1}, + +{BFA_AEN_PORT_SFP_POM, BFA_LOG_ATTR_NONE | BFA_LOG_ATTR_LOG, BFA_LOG_WARNING, + "BFA_AEN_PORT_SFP_POM", + "SFP POM level to %s: WWN/MAC = %s.", + ((BFA_LOG_S << BFA_LOG_ARG0) | (BFA_LOG_S << BFA_LOG_ARG1) | 0), 2}, + +{BFA_AEN_PORT_ENABLE, BFA_LOG_ATTR_NONE | BFA_LOG_ATTR_LOG, BFA_LOG_INFO, + "BFA_AEN_PORT_ENABLE", + "Base port enabled: WWN = %s.", + ((BFA_LOG_S << BFA_LOG_ARG0) | 0), 1}, + +{BFA_AEN_PORT_DISABLE, BFA_LOG_ATTR_NONE | BFA_LOG_ATTR_LOG, BFA_LOG_INFO, + "BFA_AEN_PORT_DISABLE", + "Base port disabled: WWN = %s.", + ((BFA_LOG_S << BFA_LOG_ARG0) | 0), 1}, + +{BFA_AEN_PORT_AUTH_ON, BFA_LOG_ATTR_NONE | BFA_LOG_ATTR_LOG, BFA_LOG_INFO, + "BFA_AEN_PORT_AUTH_ON", + "Authentication successful for base port: WWN = %s.", + ((BFA_LOG_S << BFA_LOG_ARG0) | 0), 1}, + +{BFA_AEN_PORT_AUTH_OFF, BFA_LOG_ATTR_NONE | BFA_LOG_ATTR_LOG, BFA_LOG_ERROR, + "BFA_AEN_PORT_AUTH_OFF", + "Authentication unsuccessful for base port: WWN = %s.", + ((BFA_LOG_S << BFA_LOG_ARG0) | 0), 1}, + +{BFA_AEN_PORT_DISCONNECT, BFA_LOG_ATTR_NONE | BFA_LOG_ATTR_LOG, BFA_LOG_ERROR, + "BFA_AEN_PORT_DISCONNECT", + "Base port (WWN = %s) lost fabric connectivity.", + ((BFA_LOG_S << BFA_LOG_ARG0) | 0), 1}, + +{BFA_AEN_PORT_QOS_NEG, BFA_LOG_ATTR_NONE | BFA_LOG_ATTR_LOG, BFA_LOG_WARNING, + "BFA_AEN_PORT_QOS_NEG", + "QOS negotiation failed for base port: WWN = %s.", + ((BFA_LOG_S << BFA_LOG_ARG0) | 0), 1}, + +{BFA_AEN_PORT_FABRIC_NAME_CHANGE, BFA_LOG_ATTR_NONE | BFA_LOG_ATTR_LOG, + BFA_LOG_WARNING, "BFA_AEN_PORT_FABRIC_NAME_CHANGE", + "Base port WWN = %s, Fabric WWN = %s.", + ((BFA_LOG_S << BFA_LOG_ARG0) | (BFA_LOG_S << BFA_LOG_ARG1) | 0), 2}, + +{BFA_AEN_PORT_SFP_ACCESS_ERROR, BFA_LOG_ATTR_NONE | BFA_LOG_ATTR_LOG, + BFA_LOG_WARNING, "BFA_AEN_PORT_SFP_ACCESS_ERROR", + "SFP access error: WWN/MAC = %s.", + ((BFA_LOG_S << BFA_LOG_ARG0) | 0), 1}, + +{BFA_AEN_PORT_SFP_UNSUPPORT, BFA_LOG_ATTR_NONE | BFA_LOG_ATTR_LOG, + BFA_LOG_WARNING, "BFA_AEN_PORT_SFP_UNSUPPORT", + "Unsupported SFP found: WWN/MAC = %s.", + ((BFA_LOG_S << BFA_LOG_ARG0) | 0), 1}, + + + + +/* messages define for BFA_AEN_CAT_RPORT Module */ +{BFA_AEN_RPORT_ONLINE, BFA_LOG_ATTR_NONE | BFA_LOG_ATTR_LOG, BFA_LOG_INFO, + "BFA_AEN_RPORT_ONLINE", + "Remote port (WWN = %s) online for logical port (WWN = %s).", + ((BFA_LOG_S << BFA_LOG_ARG0) | (BFA_LOG_S << BFA_LOG_ARG1) | 0), 2}, + +{BFA_AEN_RPORT_OFFLINE, BFA_LOG_ATTR_NONE | BFA_LOG_ATTR_LOG, BFA_LOG_INFO, + "BFA_AEN_RPORT_OFFLINE", + "Remote port (WWN = %s) offlined by logical port (WWN = %s).", + ((BFA_LOG_S << BFA_LOG_ARG0) | (BFA_LOG_S << BFA_LOG_ARG1) | 0), 2}, + +{BFA_AEN_RPORT_DISCONNECT, BFA_LOG_ATTR_NONE | BFA_LOG_ATTR_LOG, + BFA_LOG_ERROR, "BFA_AEN_RPORT_DISCONNECT", + "Remote port (WWN = %s) connectivity lost for logical port (WWN = %s).", + ((BFA_LOG_S << BFA_LOG_ARG0) | (BFA_LOG_S << BFA_LOG_ARG1) | 0), 2}, + +{BFA_AEN_RPORT_QOS_PRIO, BFA_LOG_ATTR_NONE | BFA_LOG_ATTR_LOG, BFA_LOG_INFO, + "BFA_AEN_RPORT_QOS_PRIO", + "QOS priority changed to %s: RPWWN = %s and LPWWN = %s.", + ((BFA_LOG_S << BFA_LOG_ARG0) | (BFA_LOG_S << BFA_LOG_ARG1) | + (BFA_LOG_S << BFA_LOG_ARG2) | 0), 3}, + +{BFA_AEN_RPORT_QOS_FLOWID, BFA_LOG_ATTR_NONE | BFA_LOG_ATTR_LOG, BFA_LOG_INFO, + "BFA_AEN_RPORT_QOS_FLOWID", + "QOS flow ID changed to %d: RPWWN = %s and LPWWN = %s.", + ((BFA_LOG_D << BFA_LOG_ARG0) | (BFA_LOG_S << BFA_LOG_ARG1) | + (BFA_LOG_S << BFA_LOG_ARG2) | 0), 3}, + + + + +/* messages define for FCS Module */ +{BFA_LOG_FCS_FABRIC_NOSWITCH, BFA_LOG_ATTR_NONE | BFA_LOG_ATTR_LOG, + BFA_LOG_INFO, "FCS_FABRIC_NOSWITCH", + "No switched fabric presence is detected.", + (0), 0}, + +{BFA_LOG_FCS_FABRIC_ISOLATED, BFA_LOG_ATTR_NONE | BFA_LOG_ATTR_LOG, + BFA_LOG_INFO, "FCS_FABRIC_ISOLATED", + "Port is isolated due to VF_ID mismatch. PWWN: %s, Port VF_ID: %04x and" + " switch port VF_ID: %04x.", + ((BFA_LOG_S << BFA_LOG_ARG0) | (BFA_LOG_X << BFA_LOG_ARG1) | + (BFA_LOG_X << BFA_LOG_ARG2) | 0), 3}, + + + + +/* messages define for HAL Module */ +{BFA_LOG_HAL_ASSERT, BFA_LOG_ATTR_NONE | BFA_LOG_ATTR_LOG, BFA_LOG_ERROR, + "HAL_ASSERT", + "Assertion failure: %s:%d: %s", + ((BFA_LOG_S << BFA_LOG_ARG0) | (BFA_LOG_D << BFA_LOG_ARG1) | + (BFA_LOG_S << BFA_LOG_ARG2) | 0), 3}, + +{BFA_LOG_HAL_HEARTBEAT_FAILURE, BFA_LOG_ATTR_NONE | BFA_LOG_ATTR_LOG, + BFA_LOG_CRITICAL, "HAL_HEARTBEAT_FAILURE", + "Firmware heartbeat failure at %d", + ((BFA_LOG_D << BFA_LOG_ARG0) | 0), 1}, + +{BFA_LOG_HAL_FCPIM_PARM_INVALID, BFA_LOG_ATTR_NONE | BFA_LOG_ATTR_LOG, + BFA_LOG_INFO, "HAL_FCPIM_PARM_INVALID", + "Driver configuration %s value %d is invalid. Value should be within" + " %d and %d.", + ((BFA_LOG_S << BFA_LOG_ARG0) | (BFA_LOG_D << BFA_LOG_ARG1) | + (BFA_LOG_D << BFA_LOG_ARG2) | (BFA_LOG_D << BFA_LOG_ARG3) | 0), 4}, + +{BFA_LOG_HAL_SM_ASSERT, BFA_LOG_ATTR_NONE | BFA_LOG_ATTR_LOG, BFA_LOG_ERROR, + "HAL_SM_ASSERT", + "SM Assertion failure: %s:%d: event = %d", + ((BFA_LOG_S << BFA_LOG_ARG0) | (BFA_LOG_D << BFA_LOG_ARG1) | + (BFA_LOG_D << BFA_LOG_ARG2) | 0), 3}, + + + + +/* messages define for LINUX Module */ +{BFA_LOG_LINUX_DEVICE_CLAIMED, BFA_LOG_ATTR_NONE | BFA_LOG_ATTR_LOG, + BFA_LOG_INFO, "LINUX_DEVICE_CLAIMED", + "bfa device at %s claimed.", + ((BFA_LOG_S << BFA_LOG_ARG0) | 0), 1}, + +{BFA_LOG_LINUX_HASH_INIT_FAILED, BFA_LOG_ATTR_NONE | BFA_LOG_ATTR_LOG, + BFA_LOG_INFO, "LINUX_HASH_INIT_FAILED", + "Hash table initialization failure for the port %s.", + ((BFA_LOG_S << BFA_LOG_ARG0) | 0), 1}, + +{BFA_LOG_LINUX_SYSFS_FAILED, BFA_LOG_ATTR_NONE | BFA_LOG_ATTR_LOG, + BFA_LOG_INFO, "LINUX_SYSFS_FAILED", + "sysfs file creation failure for the port %s.", + ((BFA_LOG_S << BFA_LOG_ARG0) | 0), 1}, + +{BFA_LOG_LINUX_MEM_ALLOC_FAILED, BFA_LOG_ATTR_NONE | BFA_LOG_ATTR_LOG, + BFA_LOG_INFO, "LINUX_MEM_ALLOC_FAILED", + "Memory allocation failed: %s. ", + ((BFA_LOG_S << BFA_LOG_ARG0) | 0), 1}, + +{BFA_LOG_LINUX_DRIVER_REGISTRATION_FAILED, + BFA_LOG_ATTR_NONE | BFA_LOG_ATTR_LOG, BFA_LOG_INFO, + "LINUX_DRIVER_REGISTRATION_FAILED", + "%s. ", + ((BFA_LOG_S << BFA_LOG_ARG0) | 0), 1}, + +{BFA_LOG_LINUX_ITNIM_FREE, BFA_LOG_ATTR_NONE | BFA_LOG_ATTR_LOG, BFA_LOG_INFO, + "LINUX_ITNIM_FREE", + "scsi%d: FCID: %s WWPN: %s", + ((BFA_LOG_D << BFA_LOG_ARG0) | (BFA_LOG_S << BFA_LOG_ARG1) | + (BFA_LOG_S << BFA_LOG_ARG2) | 0), 3}, + +{BFA_LOG_LINUX_ITNIM_ONLINE, BFA_LOG_ATTR_NONE | BFA_LOG_ATTR_LOG, + BFA_LOG_INFO, "LINUX_ITNIM_ONLINE", + "Target: %d:0:%d FCID: %s WWPN: %s", + ((BFA_LOG_D << BFA_LOG_ARG0) | (BFA_LOG_D << BFA_LOG_ARG1) | + (BFA_LOG_S << BFA_LOG_ARG2) | (BFA_LOG_S << BFA_LOG_ARG3) | 0), 4}, + +{BFA_LOG_LINUX_ITNIM_OFFLINE, BFA_LOG_ATTR_NONE | BFA_LOG_ATTR_LOG, + BFA_LOG_INFO, "LINUX_ITNIM_OFFLINE", + "Target: %d:0:%d FCID: %s WWPN: %s", + ((BFA_LOG_D << BFA_LOG_ARG0) | (BFA_LOG_D << BFA_LOG_ARG1) | + (BFA_LOG_S << BFA_LOG_ARG2) | (BFA_LOG_S << BFA_LOG_ARG3) | 0), 4}, + +{BFA_LOG_LINUX_SCSI_HOST_FREE, BFA_LOG_ATTR_NONE | BFA_LOG_ATTR_LOG, + BFA_LOG_INFO, "LINUX_SCSI_HOST_FREE", + "Free scsi%d", + ((BFA_LOG_D << BFA_LOG_ARG0) | 0), 1}, + +{BFA_LOG_LINUX_SCSI_ABORT, BFA_LOG_ATTR_NONE | BFA_LOG_ATTR_LOG, BFA_LOG_INFO, + "LINUX_SCSI_ABORT", + "scsi%d: abort cmnd %p, iotag %x", + ((BFA_LOG_D << BFA_LOG_ARG0) | (BFA_LOG_P << BFA_LOG_ARG1) | + (BFA_LOG_X << BFA_LOG_ARG2) | 0), 3}, + +{BFA_LOG_LINUX_SCSI_ABORT_COMP, BFA_LOG_ATTR_NONE | BFA_LOG_ATTR_LOG, + BFA_LOG_INFO, "LINUX_SCSI_ABORT_COMP", + "scsi%d: complete abort 0x%p, iotag 0x%x", + ((BFA_LOG_D << BFA_LOG_ARG0) | (BFA_LOG_P << BFA_LOG_ARG1) | + (BFA_LOG_X << BFA_LOG_ARG2) | 0), 3}, + + + + +/* messages define for WDRV Module */ +{BFA_LOG_WDRV_IOC_INIT_ERROR, BFA_LOG_ATTR_NONE | BFA_LOG_ATTR_LOG, + BFA_LOG_INFO, "WDRV_IOC_INIT_ERROR", + "IOC initialization has failed.", + (0), 0}, + +{BFA_LOG_WDRV_IOC_INTERNAL_ERROR, BFA_LOG_ATTR_NONE | BFA_LOG_ATTR_LOG, + BFA_LOG_INFO, "WDRV_IOC_INTERNAL_ERROR", + "IOC internal error. ", + (0), 0}, + +{BFA_LOG_WDRV_IOC_START_ERROR, BFA_LOG_ATTR_NONE | BFA_LOG_ATTR_LOG, + BFA_LOG_INFO, "WDRV_IOC_START_ERROR", + "IOC could not be started. ", + (0), 0}, + +{BFA_LOG_WDRV_IOC_STOP_ERROR, BFA_LOG_ATTR_NONE | BFA_LOG_ATTR_LOG, + BFA_LOG_INFO, "WDRV_IOC_STOP_ERROR", + "IOC could not be stopped. ", + (0), 0}, + +{BFA_LOG_WDRV_INSUFFICIENT_RESOURCES, BFA_LOG_ATTR_NONE | BFA_LOG_ATTR_LOG, + BFA_LOG_INFO, "WDRV_INSUFFICIENT_RESOURCES", + "Insufficient memory. ", + (0), 0}, + +{BFA_LOG_WDRV_BASE_ADDRESS_MAP_ERROR, BFA_LOG_ATTR_NONE | BFA_LOG_ATTR_LOG, + BFA_LOG_INFO, "WDRV_BASE_ADDRESS_MAP_ERROR", + "Unable to map the IOC onto the system address space. ", + (0), 0}, + + +{0, 0, 0, "", "", 0, 0}, +}; diff --git a/drivers/scsi/bfa/bfa_lps.c b/drivers/scsi/bfa/bfa_lps.c new file mode 100644 index 000000000000..9844b45412b6 --- /dev/null +++ b/drivers/scsi/bfa/bfa_lps.c @@ -0,0 +1,782 @@ +/* + * Copyright (c) 2005-2009 Brocade Communications Systems, Inc. + * All rights reserved + * www.brocade.com + * + * Linux driver for Brocade Fibre Channel Host Bus Adapter. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (GPL) Version 2 as + * published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#include <bfa.h> +#include <bfi/bfi_lps.h> +#include <cs/bfa_debug.h> + +BFA_TRC_FILE(HAL, LPS); +BFA_MODULE(lps); + +#define BFA_LPS_MIN_LPORTS (1) +#define BFA_LPS_MAX_LPORTS (256) + +/** + * forward declarations + */ +static void bfa_lps_meminfo(struct bfa_iocfc_cfg_s *cfg, u32 *ndm_len, + u32 *dm_len); +static void bfa_lps_attach(struct bfa_s *bfa, void *bfad, + struct bfa_iocfc_cfg_s *cfg, + struct bfa_meminfo_s *meminfo, + struct bfa_pcidev_s *pcidev); +static void bfa_lps_initdone(struct bfa_s *bfa); +static void bfa_lps_detach(struct bfa_s *bfa); +static void bfa_lps_start(struct bfa_s *bfa); +static void bfa_lps_stop(struct bfa_s *bfa); +static void bfa_lps_iocdisable(struct bfa_s *bfa); +static void bfa_lps_login_rsp(struct bfa_s *bfa, + struct bfi_lps_login_rsp_s *rsp); +static void bfa_lps_logout_rsp(struct bfa_s *bfa, + struct bfi_lps_logout_rsp_s *rsp); +static void bfa_lps_reqq_resume(void *lps_arg); +static void bfa_lps_free(struct bfa_lps_s *lps); +static void bfa_lps_send_login(struct bfa_lps_s *lps); +static void bfa_lps_send_logout(struct bfa_lps_s *lps); +static void bfa_lps_login_comp(struct bfa_lps_s *lps); +static void bfa_lps_logout_comp(struct bfa_lps_s *lps); + + +/** + * lps_pvt BFA LPS private functions + */ + +enum bfa_lps_event { + BFA_LPS_SM_LOGIN = 1, /* login request from user */ + BFA_LPS_SM_LOGOUT = 2, /* logout request from user */ + BFA_LPS_SM_FWRSP = 3, /* f/w response to login/logout */ + BFA_LPS_SM_RESUME = 4, /* space present in reqq queue */ + BFA_LPS_SM_DELETE = 5, /* lps delete from user */ + BFA_LPS_SM_OFFLINE = 6, /* Link is offline */ +}; + +static void bfa_lps_sm_init(struct bfa_lps_s *lps, enum bfa_lps_event event); +static void bfa_lps_sm_login(struct bfa_lps_s *lps, enum bfa_lps_event event); +static void bfa_lps_sm_loginwait(struct bfa_lps_s *lps, + enum bfa_lps_event event); +static void bfa_lps_sm_online(struct bfa_lps_s *lps, enum bfa_lps_event event); +static void bfa_lps_sm_logout(struct bfa_lps_s *lps, enum bfa_lps_event event); +static void bfa_lps_sm_logowait(struct bfa_lps_s *lps, + enum bfa_lps_event event); + +/** + * Init state -- no login + */ +static void +bfa_lps_sm_init(struct bfa_lps_s *lps, enum bfa_lps_event event) +{ + bfa_trc(lps->bfa, lps->lp_tag); + bfa_trc(lps->bfa, event); + + switch (event) { + case BFA_LPS_SM_LOGIN: + if (bfa_reqq_full(lps->bfa, lps->reqq)) { + bfa_sm_set_state(lps, bfa_lps_sm_loginwait); + bfa_reqq_wait(lps->bfa, lps->reqq, &lps->wqe); + } else { + bfa_sm_set_state(lps, bfa_lps_sm_login); + bfa_lps_send_login(lps); + } + break; + + case BFA_LPS_SM_LOGOUT: + bfa_lps_logout_comp(lps); + break; + + case BFA_LPS_SM_DELETE: + bfa_lps_free(lps); + break; + + case BFA_LPS_SM_OFFLINE: + break; + + case BFA_LPS_SM_FWRSP: + /* Could happen when fabric detects loopback and discards + * the lps request. Fw will eventually sent out the timeout + * Just ignore + */ + break; + + default: + bfa_assert(0); + } +} + +/** + * login is in progress -- awaiting response from firmware + */ +static void +bfa_lps_sm_login(struct bfa_lps_s *lps, enum bfa_lps_event event) +{ + bfa_trc(lps->bfa, lps->lp_tag); + bfa_trc(lps->bfa, event); + + switch (event) { + case BFA_LPS_SM_FWRSP: + if (lps->status == BFA_STATUS_OK) + bfa_sm_set_state(lps, bfa_lps_sm_online); + else + bfa_sm_set_state(lps, bfa_lps_sm_init); + bfa_lps_login_comp(lps); + break; + + case BFA_LPS_SM_OFFLINE: + bfa_sm_set_state(lps, bfa_lps_sm_init); + break; + + default: + bfa_assert(0); + } +} + +/** + * login pending - awaiting space in request queue + */ +static void +bfa_lps_sm_loginwait(struct bfa_lps_s *lps, enum bfa_lps_event event) +{ + bfa_trc(lps->bfa, lps->lp_tag); + bfa_trc(lps->bfa, event); + + switch (event) { + case BFA_LPS_SM_RESUME: + bfa_sm_set_state(lps, bfa_lps_sm_login); + break; + + case BFA_LPS_SM_OFFLINE: + bfa_sm_set_state(lps, bfa_lps_sm_init); + bfa_reqq_wcancel(&lps->wqe); + break; + + default: + bfa_assert(0); + } +} + +/** + * login complete + */ +static void +bfa_lps_sm_online(struct bfa_lps_s *lps, enum bfa_lps_event event) +{ + bfa_trc(lps->bfa, lps->lp_tag); + bfa_trc(lps->bfa, event); + + switch (event) { + case BFA_LPS_SM_LOGOUT: + if (bfa_reqq_full(lps->bfa, lps->reqq)) { + bfa_sm_set_state(lps, bfa_lps_sm_logowait); + bfa_reqq_wait(lps->bfa, lps->reqq, &lps->wqe); + } else { + bfa_sm_set_state(lps, bfa_lps_sm_logout); + bfa_lps_send_logout(lps); + } + break; + + case BFA_LPS_SM_OFFLINE: + case BFA_LPS_SM_DELETE: + bfa_sm_set_state(lps, bfa_lps_sm_init); + break; + + default: + bfa_assert(0); + } +} + +/** + * logout in progress - awaiting firmware response + */ +static void +bfa_lps_sm_logout(struct bfa_lps_s *lps, enum bfa_lps_event event) +{ + bfa_trc(lps->bfa, lps->lp_tag); + bfa_trc(lps->bfa, event); + + switch (event) { + case BFA_LPS_SM_FWRSP: + bfa_sm_set_state(lps, bfa_lps_sm_init); + bfa_lps_logout_comp(lps); + break; + + case BFA_LPS_SM_OFFLINE: + bfa_sm_set_state(lps, bfa_lps_sm_init); + break; + + default: + bfa_assert(0); + } +} + +/** + * logout pending -- awaiting space in request queue + */ +static void +bfa_lps_sm_logowait(struct bfa_lps_s *lps, enum bfa_lps_event event) +{ + bfa_trc(lps->bfa, lps->lp_tag); + bfa_trc(lps->bfa, event); + + switch (event) { + case BFA_LPS_SM_RESUME: + bfa_sm_set_state(lps, bfa_lps_sm_logout); + bfa_lps_send_logout(lps); + break; + + case BFA_LPS_SM_OFFLINE: + bfa_sm_set_state(lps, bfa_lps_sm_init); + bfa_reqq_wcancel(&lps->wqe); + break; + + default: + bfa_assert(0); + } +} + + + +/** + * lps_pvt BFA LPS private functions + */ + +/** + * return memory requirement + */ +static void +bfa_lps_meminfo(struct bfa_iocfc_cfg_s *cfg, u32 *ndm_len, u32 *dm_len) +{ + if (cfg->drvcfg.min_cfg) + *ndm_len += sizeof(struct bfa_lps_s) * BFA_LPS_MIN_LPORTS; + else + *ndm_len += sizeof(struct bfa_lps_s) * BFA_LPS_MAX_LPORTS; +} + +/** + * bfa module attach at initialization time + */ +static void +bfa_lps_attach(struct bfa_s *bfa, void *bfad, struct bfa_iocfc_cfg_s *cfg, + struct bfa_meminfo_s *meminfo, struct bfa_pcidev_s *pcidev) +{ + struct bfa_lps_mod_s *mod = BFA_LPS_MOD(bfa); + struct bfa_lps_s *lps; + int i; + + bfa_os_memset(mod, 0, sizeof(struct bfa_lps_mod_s)); + mod->num_lps = BFA_LPS_MAX_LPORTS; + if (cfg->drvcfg.min_cfg) + mod->num_lps = BFA_LPS_MIN_LPORTS; + else + mod->num_lps = BFA_LPS_MAX_LPORTS; + mod->lps_arr = lps = (struct bfa_lps_s *) bfa_meminfo_kva(meminfo); + + bfa_meminfo_kva(meminfo) += mod->num_lps * sizeof(struct bfa_lps_s); + + INIT_LIST_HEAD(&mod->lps_free_q); + INIT_LIST_HEAD(&mod->lps_active_q); + + for (i = 0; i < mod->num_lps; i++, lps++) { + lps->bfa = bfa; + lps->lp_tag = (u8) i; + lps->reqq = BFA_REQQ_LPS; + bfa_reqq_winit(&lps->wqe, bfa_lps_reqq_resume, lps); + list_add_tail(&lps->qe, &mod->lps_free_q); + } +} + +static void +bfa_lps_initdone(struct bfa_s *bfa) +{ +} + +static void +bfa_lps_detach(struct bfa_s *bfa) +{ +} + +static void +bfa_lps_start(struct bfa_s *bfa) +{ +} + +static void +bfa_lps_stop(struct bfa_s *bfa) +{ +} + +/** + * IOC in disabled state -- consider all lps offline + */ +static void +bfa_lps_iocdisable(struct bfa_s *bfa) +{ + struct bfa_lps_mod_s *mod = BFA_LPS_MOD(bfa); + struct bfa_lps_s *lps; + struct list_head *qe, *qen; + + list_for_each_safe(qe, qen, &mod->lps_active_q) { + lps = (struct bfa_lps_s *) qe; + bfa_sm_send_event(lps, BFA_LPS_SM_OFFLINE); + } +} + +/** + * Firmware login response + */ +static void +bfa_lps_login_rsp(struct bfa_s *bfa, struct bfi_lps_login_rsp_s *rsp) +{ + struct bfa_lps_mod_s *mod = BFA_LPS_MOD(bfa); + struct bfa_lps_s *lps; + + bfa_assert(rsp->lp_tag < mod->num_lps); + lps = BFA_LPS_FROM_TAG(mod, rsp->lp_tag); + + lps->status = rsp->status; + switch (rsp->status) { + case BFA_STATUS_OK: + lps->fport = rsp->f_port; + lps->npiv_en = rsp->npiv_en; + lps->lp_pid = rsp->lp_pid; + lps->pr_bbcred = bfa_os_ntohs(rsp->bb_credit); + lps->pr_pwwn = rsp->port_name; + lps->pr_nwwn = rsp->node_name; + lps->auth_req = rsp->auth_req; + lps->lp_mac = rsp->lp_mac; + lps->brcd_switch = rsp->brcd_switch; + lps->fcf_mac = rsp->fcf_mac; + + break; + + case BFA_STATUS_FABRIC_RJT: + lps->lsrjt_rsn = rsp->lsrjt_rsn; + lps->lsrjt_expl = rsp->lsrjt_expl; + + break; + + case BFA_STATUS_EPROTOCOL: + lps->ext_status = rsp->ext_status; + + break; + + default: + /* Nothing to do with other status */ + break; + } + + bfa_sm_send_event(lps, BFA_LPS_SM_FWRSP); +} + +/** + * Firmware logout response + */ +static void +bfa_lps_logout_rsp(struct bfa_s *bfa, struct bfi_lps_logout_rsp_s *rsp) +{ + struct bfa_lps_mod_s *mod = BFA_LPS_MOD(bfa); + struct bfa_lps_s *lps; + + bfa_assert(rsp->lp_tag < mod->num_lps); + lps = BFA_LPS_FROM_TAG(mod, rsp->lp_tag); + + bfa_sm_send_event(lps, BFA_LPS_SM_FWRSP); +} + +/** + * Space is available in request queue, resume queueing request to firmware. + */ +static void +bfa_lps_reqq_resume(void *lps_arg) +{ + struct bfa_lps_s *lps = lps_arg; + + bfa_sm_send_event(lps, BFA_LPS_SM_RESUME); +} + +/** + * lps is freed -- triggered by vport delete + */ +static void +bfa_lps_free(struct bfa_lps_s *lps) +{ + struct bfa_lps_mod_s *mod = BFA_LPS_MOD(lps->bfa); + + list_del(&lps->qe); + list_add_tail(&lps->qe, &mod->lps_free_q); +} + +/** + * send login request to firmware + */ +static void +bfa_lps_send_login(struct bfa_lps_s *lps) +{ + struct bfi_lps_login_req_s *m; + + m = bfa_reqq_next(lps->bfa, lps->reqq); + bfa_assert(m); + + bfi_h2i_set(m->mh, BFI_MC_LPS, BFI_LPS_H2I_LOGIN_REQ, + bfa_lpuid(lps->bfa)); + + m->lp_tag = lps->lp_tag; + m->alpa = lps->alpa; + m->pdu_size = bfa_os_htons(lps->pdusz); + m->pwwn = lps->pwwn; + m->nwwn = lps->nwwn; + m->fdisc = lps->fdisc; + m->auth_en = lps->auth_en; + + bfa_reqq_produce(lps->bfa, lps->reqq); +} + +/** + * send logout request to firmware + */ +static void +bfa_lps_send_logout(struct bfa_lps_s *lps) +{ + struct bfi_lps_logout_req_s *m; + + m = bfa_reqq_next(lps->bfa, lps->reqq); + bfa_assert(m); + + bfi_h2i_set(m->mh, BFI_MC_LPS, BFI_LPS_H2I_LOGOUT_REQ, + bfa_lpuid(lps->bfa)); + + m->lp_tag = lps->lp_tag; + m->port_name = lps->pwwn; + bfa_reqq_produce(lps->bfa, lps->reqq); +} + +/** + * Indirect login completion handler for non-fcs + */ +static void +bfa_lps_login_comp_cb(void *arg, bfa_boolean_t complete) +{ + struct bfa_lps_s *lps = arg; + + if (!complete) + return; + + if (lps->fdisc) + bfa_cb_lps_fdisc_comp(lps->bfa->bfad, lps->uarg, lps->status); + else + bfa_cb_lps_flogi_comp(lps->bfa->bfad, lps->uarg, lps->status); +} + +/** + * Login completion handler -- direct call for fcs, queue for others + */ +static void +bfa_lps_login_comp(struct bfa_lps_s *lps) +{ + if (!lps->bfa->fcs) { + bfa_cb_queue(lps->bfa, &lps->hcb_qe, + bfa_lps_login_comp_cb, lps); + return; + } + + if (lps->fdisc) + bfa_cb_lps_fdisc_comp(lps->bfa->bfad, lps->uarg, lps->status); + else + bfa_cb_lps_flogi_comp(lps->bfa->bfad, lps->uarg, lps->status); +} + +/** + * Indirect logout completion handler for non-fcs + */ +static void +bfa_lps_logout_comp_cb(void *arg, bfa_boolean_t complete) +{ + struct bfa_lps_s *lps = arg; + + if (!complete) + return; + + if (lps->fdisc) + bfa_cb_lps_fdisclogo_comp(lps->bfa->bfad, lps->uarg); + else + bfa_cb_lps_flogo_comp(lps->bfa->bfad, lps->uarg); +} + +/** + * Logout completion handler -- direct call for fcs, queue for others + */ +static void +bfa_lps_logout_comp(struct bfa_lps_s *lps) +{ + if (!lps->bfa->fcs) { + bfa_cb_queue(lps->bfa, &lps->hcb_qe, + bfa_lps_logout_comp_cb, lps); + return; + } + if (lps->fdisc) + bfa_cb_lps_fdisclogo_comp(lps->bfa->bfad, lps->uarg); + else + bfa_cb_lps_flogo_comp(lps->bfa->bfad, lps->uarg); +} + + + +/** + * lps_public BFA LPS public functions + */ + +/** + * Allocate a lport srvice tag. + */ +struct bfa_lps_s * +bfa_lps_alloc(struct bfa_s *bfa) +{ + struct bfa_lps_mod_s *mod = BFA_LPS_MOD(bfa); + struct bfa_lps_s *lps = NULL; + + bfa_q_deq(&mod->lps_free_q, &lps); + + if (lps == NULL) + return NULL; + + list_add_tail(&lps->qe, &mod->lps_active_q); + + bfa_sm_set_state(lps, bfa_lps_sm_init); + return lps; +} + +/** + * Free lport service tag. This can be called anytime after an alloc. + * No need to wait for any pending login/logout completions. + */ +void +bfa_lps_delete(struct bfa_lps_s *lps) +{ + bfa_sm_send_event(lps, BFA_LPS_SM_DELETE); +} + +/** + * Initiate a lport login. + */ +void +bfa_lps_flogi(struct bfa_lps_s *lps, void *uarg, u8 alpa, u16 pdusz, + wwn_t pwwn, wwn_t nwwn, bfa_boolean_t auth_en) +{ + lps->uarg = uarg; + lps->alpa = alpa; + lps->pdusz = pdusz; + lps->pwwn = pwwn; + lps->nwwn = nwwn; + lps->fdisc = BFA_FALSE; + lps->auth_en = auth_en; + bfa_sm_send_event(lps, BFA_LPS_SM_LOGIN); +} + +/** + * Initiate a lport fdisc login. + */ +void +bfa_lps_fdisc(struct bfa_lps_s *lps, void *uarg, u16 pdusz, wwn_t pwwn, + wwn_t nwwn) +{ + lps->uarg = uarg; + lps->alpa = 0; + lps->pdusz = pdusz; + lps->pwwn = pwwn; + lps->nwwn = nwwn; + lps->fdisc = BFA_TRUE; + lps->auth_en = BFA_FALSE; + bfa_sm_send_event(lps, BFA_LPS_SM_LOGIN); +} + +/** + * Initiate a lport logout (flogi). + */ +void +bfa_lps_flogo(struct bfa_lps_s *lps) +{ + bfa_sm_send_event(lps, BFA_LPS_SM_LOGOUT); +} + +/** + * Initiate a lport FDSIC logout. + */ +void +bfa_lps_fdisclogo(struct bfa_lps_s *lps) +{ + bfa_sm_send_event(lps, BFA_LPS_SM_LOGOUT); +} + +/** + * Discard a pending login request -- should be called only for + * link down handling. + */ +void +bfa_lps_discard(struct bfa_lps_s *lps) +{ + bfa_sm_send_event(lps, BFA_LPS_SM_OFFLINE); +} + +/** + * Return lport services tag + */ +u8 +bfa_lps_get_tag(struct bfa_lps_s *lps) +{ + return lps->lp_tag; +} + +/** + * Return lport services tag given the pid + */ +u8 +bfa_lps_get_tag_from_pid(struct bfa_s *bfa, u32 pid) +{ + struct bfa_lps_mod_s *mod = BFA_LPS_MOD(bfa); + struct bfa_lps_s *lps; + int i; + + for (i = 0, lps = mod->lps_arr; i < mod->num_lps; i++, lps++) { + if (lps->lp_pid == pid) + return lps->lp_tag; + } + + /* Return base port tag anyway */ + return 0; +} + +/** + * return if fabric login indicates support for NPIV + */ +bfa_boolean_t +bfa_lps_is_npiv_en(struct bfa_lps_s *lps) +{ + return lps->npiv_en; +} + +/** + * Return TRUE if attached to F-Port, else return FALSE + */ +bfa_boolean_t +bfa_lps_is_fport(struct bfa_lps_s *lps) +{ + return lps->fport; +} + +/** + * Return TRUE if attached to a Brocade Fabric + */ +bfa_boolean_t +bfa_lps_is_brcd_fabric(struct bfa_lps_s *lps) +{ + return lps->brcd_switch; +} +/** + * return TRUE if authentication is required + */ +bfa_boolean_t +bfa_lps_is_authreq(struct bfa_lps_s *lps) +{ + return lps->auth_req; +} + +bfa_eproto_status_t +bfa_lps_get_extstatus(struct bfa_lps_s *lps) +{ + return lps->ext_status; +} + +/** + * return port id assigned to the lport + */ +u32 +bfa_lps_get_pid(struct bfa_lps_s *lps) +{ + return lps->lp_pid; +} + +/** + * Return bb_credit assigned in FLOGI response + */ +u16 +bfa_lps_get_peer_bbcredit(struct bfa_lps_s *lps) +{ + return lps->pr_bbcred; +} + +/** + * Return peer port name + */ +wwn_t +bfa_lps_get_peer_pwwn(struct bfa_lps_s *lps) +{ + return lps->pr_pwwn; +} + +/** + * Return peer node name + */ +wwn_t +bfa_lps_get_peer_nwwn(struct bfa_lps_s *lps) +{ + return lps->pr_nwwn; +} + +/** + * return reason code if login request is rejected + */ +u8 +bfa_lps_get_lsrjt_rsn(struct bfa_lps_s *lps) +{ + return lps->lsrjt_rsn; +} + +/** + * return explanation code if login request is rejected + */ +u8 +bfa_lps_get_lsrjt_expl(struct bfa_lps_s *lps) +{ + return lps->lsrjt_expl; +} + + +/** + * LPS firmware message class handler. + */ +void +bfa_lps_isr(struct bfa_s *bfa, struct bfi_msg_s *m) +{ + union bfi_lps_i2h_msg_u msg; + + bfa_trc(bfa, m->mhdr.msg_id); + msg.msg = m; + + switch (m->mhdr.msg_id) { + case BFI_LPS_H2I_LOGIN_RSP: + bfa_lps_login_rsp(bfa, msg.login_rsp); + break; + + case BFI_LPS_H2I_LOGOUT_RSP: + bfa_lps_logout_rsp(bfa, msg.logout_rsp); + break; + + default: + bfa_trc(bfa, m->mhdr.msg_id); + bfa_assert(0); + } +} + + diff --git a/drivers/scsi/bfa/bfa_lps_priv.h b/drivers/scsi/bfa/bfa_lps_priv.h new file mode 100644 index 000000000000..d16c6ce995df --- /dev/null +++ b/drivers/scsi/bfa/bfa_lps_priv.h @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2005-2009 Brocade Communications Systems, Inc. + * All rights reserved + * www.brocade.com + * + * Linux driver for Brocade Fibre Channel Host Bus Adapter. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (GPL) Version 2 as + * published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#ifndef __BFA_LPS_PRIV_H__ +#define __BFA_LPS_PRIV_H__ + +#include <bfa_svc.h> + +struct bfa_lps_mod_s { + struct list_head lps_free_q; + struct list_head lps_active_q; + struct bfa_lps_s *lps_arr; + int num_lps; +}; + +#define BFA_LPS_MOD(__bfa) (&(__bfa)->modules.lps_mod) +#define BFA_LPS_FROM_TAG(__mod, __tag) (&(__mod)->lps_arr[__tag]) + +/* + * external functions + */ +void bfa_lps_isr(struct bfa_s *bfa, struct bfi_msg_s *msg); + +#endif /* __BFA_LPS_PRIV_H__ */ diff --git a/drivers/scsi/bfa/bfa_module.c b/drivers/scsi/bfa/bfa_module.c new file mode 100644 index 000000000000..32eda8e1ec65 --- /dev/null +++ b/drivers/scsi/bfa/bfa_module.c @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2005-2009 Brocade Communications Systems, Inc. + * All rights reserved + * www.brocade.com + * + * Linux driver for Brocade Fibre Channel Host Bus Adapter. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (GPL) Version 2 as + * published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ +#include <bfa.h> +#include <defs/bfa_defs_pci.h> +#include <cs/bfa_debug.h> +#include <bfa_iocfc.h> + +/** + * BFA module list terminated by NULL + */ +struct bfa_module_s *hal_mods[] = { + &hal_mod_sgpg, + &hal_mod_pport, + &hal_mod_fcxp, + &hal_mod_lps, + &hal_mod_uf, + &hal_mod_rport, + &hal_mod_fcpim, +#ifdef BFA_CFG_PBIND + &hal_mod_pbind, +#endif + NULL +}; + +/** + * Message handlers for various modules. + */ +bfa_isr_func_t bfa_isrs[BFI_MC_MAX] = { + bfa_isr_unhandled, /* NONE */ + bfa_isr_unhandled, /* BFI_MC_IOC */ + bfa_isr_unhandled, /* BFI_MC_DIAG */ + bfa_isr_unhandled, /* BFI_MC_FLASH */ + bfa_isr_unhandled, /* BFI_MC_CEE */ + bfa_pport_isr, /* BFI_MC_PORT */ + bfa_isr_unhandled, /* BFI_MC_IOCFC */ + bfa_isr_unhandled, /* BFI_MC_LL */ + bfa_uf_isr, /* BFI_MC_UF */ + bfa_fcxp_isr, /* BFI_MC_FCXP */ + bfa_lps_isr, /* BFI_MC_LPS */ + bfa_rport_isr, /* BFI_MC_RPORT */ + bfa_itnim_isr, /* BFI_MC_ITNIM */ + bfa_isr_unhandled, /* BFI_MC_IOIM_READ */ + bfa_isr_unhandled, /* BFI_MC_IOIM_WRITE */ + bfa_isr_unhandled, /* BFI_MC_IOIM_IO */ + bfa_ioim_isr, /* BFI_MC_IOIM */ + bfa_ioim_good_comp_isr, /* BFI_MC_IOIM_IOCOM */ + bfa_tskim_isr, /* BFI_MC_TSKIM */ + bfa_isr_unhandled, /* BFI_MC_SBOOT */ + bfa_isr_unhandled, /* BFI_MC_IPFC */ + bfa_isr_unhandled, /* BFI_MC_PORT */ + bfa_isr_unhandled, /* --------- */ + bfa_isr_unhandled, /* --------- */ + bfa_isr_unhandled, /* --------- */ + bfa_isr_unhandled, /* --------- */ + bfa_isr_unhandled, /* --------- */ + bfa_isr_unhandled, /* --------- */ + bfa_isr_unhandled, /* --------- */ + bfa_isr_unhandled, /* --------- */ + bfa_isr_unhandled, /* --------- */ + bfa_isr_unhandled, /* --------- */ +}; + +/** + * Message handlers for mailbox command classes + */ +bfa_ioc_mbox_mcfunc_t bfa_mbox_isrs[BFI_MC_MAX] = { + NULL, + NULL, /* BFI_MC_IOC */ + NULL, /* BFI_MC_DIAG */ + NULL, /* BFI_MC_FLASH */ + NULL, /* BFI_MC_CEE */ + NULL, /* BFI_MC_PORT */ + bfa_iocfc_isr, /* BFI_MC_IOCFC */ + NULL, +}; + diff --git a/drivers/scsi/bfa/bfa_modules_priv.h b/drivers/scsi/bfa/bfa_modules_priv.h new file mode 100644 index 000000000000..96f70534593c --- /dev/null +++ b/drivers/scsi/bfa/bfa_modules_priv.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2005-2009 Brocade Communications Systems, Inc. + * All rights reserved + * www.brocade.com + * + * Linux driver for Brocade Fibre Channel Host Bus Adapter. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (GPL) Version 2 as + * published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#ifndef __BFA_MODULES_PRIV_H__ +#define __BFA_MODULES_PRIV_H__ + +#include "bfa_uf_priv.h" +#include "bfa_port_priv.h" +#include "bfa_rport_priv.h" +#include "bfa_fcxp_priv.h" +#include "bfa_lps_priv.h" +#include "bfa_fcpim_priv.h" +#include <cee/bfa_cee.h> +#include <port/bfa_port.h> + + +struct bfa_modules_s { + struct bfa_pport_s pport; /* physical port module */ + struct bfa_fcxp_mod_s fcxp_mod; /* fcxp module */ + struct bfa_lps_mod_s lps_mod; /* fcxp module */ + struct bfa_uf_mod_s uf_mod; /* unsolicited frame module */ + struct bfa_rport_mod_s rport_mod; /* remote port module */ + struct bfa_fcpim_mod_s fcpim_mod; /* FCP initiator module */ + struct bfa_sgpg_mod_s sgpg_mod; /* SG page module */ + struct bfa_cee_s cee; /* CEE Module */ + struct bfa_port_s port; /* Physical port module */ +}; + +#endif /* __BFA_MODULES_PRIV_H__ */ diff --git a/drivers/scsi/bfa/bfa_os_inc.h b/drivers/scsi/bfa/bfa_os_inc.h new file mode 100644 index 000000000000..10a89f75fa94 --- /dev/null +++ b/drivers/scsi/bfa/bfa_os_inc.h @@ -0,0 +1,222 @@ +/* + * Copyright (c) 2005-2009 Brocade Communications Systems, Inc. + * All rights reserved + * www.brocade.com + * + * Linux driver for Brocade Fibre Channel Host Bus Adapter. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (GPL) Version 2 as + * published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +/** + * Contains declarations all OS Specific files needed for BFA layer + */ + +#ifndef __BFA_OS_INC_H__ +#define __BFA_OS_INC_H__ + +#ifndef __KERNEL__ +#include <stdint.h> +#else +#include <linux/types.h> + +#include <linux/version.h> +#include <linux/pci.h> + +#include <linux/dma-mapping.h> +#define SET_MODULE_VERSION(VER) + +#include <linux/idr.h> + +#include <linux/interrupt.h> +#include <linux/cdev.h> +#include <linux/fs.h> +#include <linux/delay.h> +#include <linux/vmalloc.h> + +#include <linux/workqueue.h> + +#include <scsi/scsi.h> +#include <scsi/scsi_host.h> + +#include <scsi/scsi_tcq.h> +#include <scsi/scsi_transport_fc.h> +#include <scsi/scsi_transport.h> + +#define BFA_ERR KERN_ERR +#define BFA_WARNING KERN_WARNING +#define BFA_NOTICE KERN_NOTICE +#define BFA_INFO KERN_INFO +#define BFA_DEBUG KERN_DEBUG + +#define LOG_BFAD_INIT 0x00000001 +#define LOG_FCP_IO 0x00000002 + +#ifdef DEBUG +#define BFA_LOG_TRACE(bfad, level, mask, fmt, arg...) \ + BFA_LOG(bfad, level, mask, fmt, ## arg) +#define BFA_DEV_TRACE(bfad, level, fmt, arg...) \ + BFA_DEV_PRINTF(bfad, level, fmt, ## arg) +#define BFA_TRACE(level, fmt, arg...) \ + BFA_PRINTF(level, fmt, ## arg) +#else +#define BFA_LOG_TRACE(bfad, level, mask, fmt, arg...) +#define BFA_DEV_TRACE(bfad, level, fmt, arg...) +#define BFA_TRACE(level, fmt, arg...) +#endif + +#define BFA_ASSERT(p) do { \ + if (!(p)) { \ + printk(KERN_ERR "assert(%s) failed at %s:%d\n", \ + #p, __FILE__, __LINE__); \ + BUG(); \ + } \ +} while (0) + + +#define BFA_LOG(bfad, level, mask, fmt, arg...) \ +do { \ + if (((mask) & (((struct bfad_s *)(bfad))-> \ + cfg_data[cfg_log_mask])) || (level[1] <= '3')) \ + dev_printk(level, &(((struct bfad_s *) \ + (bfad))->pcidev->dev), fmt, ##arg); \ +} while (0) + +#ifndef BFA_DEV_PRINTF +#define BFA_DEV_PRINTF(bfad, level, fmt, arg...) \ + dev_printk(level, &(((struct bfad_s *) \ + (bfad))->pcidev->dev), fmt, ##arg); +#endif + +#define BFA_PRINTF(level, fmt, arg...) \ + printk(level fmt, ##arg); + +int bfa_os_MWB(void *); + +#define bfa_os_mmiowb() mmiowb() + +#define bfa_swap_3b(_x) \ + ((((_x) & 0xff) << 16) | \ + ((_x) & 0x00ff00) | \ + (((_x) & 0xff0000) >> 16)) + +#define bfa_swap_8b(_x) \ + ((((_x) & 0xff00000000000000ull) >> 56) \ + | (((_x) & 0x00ff000000000000ull) >> 40) \ + | (((_x) & 0x0000ff0000000000ull) >> 24) \ + | (((_x) & 0x000000ff00000000ull) >> 8) \ + | (((_x) & 0x00000000ff000000ull) << 8) \ + | (((_x) & 0x0000000000ff0000ull) << 24) \ + | (((_x) & 0x000000000000ff00ull) << 40) \ + | (((_x) & 0x00000000000000ffull) << 56)) + +#define bfa_os_swap32(_x) \ + ((((_x) & 0xff) << 24) | \ + (((_x) & 0x0000ff00) << 8) | \ + (((_x) & 0x00ff0000) >> 8) | \ + (((_x) & 0xff000000) >> 24)) + + +#ifndef __BIGENDIAN +#define bfa_os_htons(_x) ((u16)((((_x) & 0xff00) >> 8) | \ + (((_x) & 0x00ff) << 8))) + +#define bfa_os_htonl(_x) bfa_os_swap32(_x) +#define bfa_os_htonll(_x) bfa_swap_8b(_x) +#define bfa_os_hton3b(_x) bfa_swap_3b(_x) + +#define bfa_os_wtole(_x) (_x) + +#else + +#define bfa_os_htons(_x) (_x) +#define bfa_os_htonl(_x) (_x) +#define bfa_os_hton3b(_x) (_x) +#define bfa_os_htonll(_x) (_x) +#define bfa_os_wtole(_x) bfa_os_swap32(_x) + +#endif + +#define bfa_os_ntohs(_x) bfa_os_htons(_x) +#define bfa_os_ntohl(_x) bfa_os_htonl(_x) +#define bfa_os_ntohll(_x) bfa_os_htonll(_x) +#define bfa_os_ntoh3b(_x) bfa_os_hton3b(_x) + +#define bfa_os_u32(__pa64) ((__pa64) >> 32) + +#define bfa_os_memset memset +#define bfa_os_memcpy memcpy +#define bfa_os_udelay udelay +#define bfa_os_vsprintf vsprintf + +#define bfa_os_assign(__t, __s) __t = __s + +#define bfa_os_addr_t char __iomem * +#define bfa_os_panic() + +#define bfa_os_reg_read(_raddr) bfa_os_wtole(readl(_raddr)) +#define bfa_os_reg_write(_raddr, _val) writel(bfa_os_wtole((_val)), (_raddr)) +#define bfa_os_mem_read(_raddr, _off) \ + bfa_os_ntohl(readl(((_raddr) + (_off)))) +#define bfa_os_mem_write(_raddr, _off, _val) \ + writel(bfa_os_htonl((_val)), ((_raddr) + (_off))) + +#define BFA_TRC_TS(_trcm) \ + ({ \ + struct timeval tv; \ + \ + do_gettimeofday(&tv); \ + (tv.tv_sec*1000000+tv.tv_usec); \ + }) + +struct bfa_log_mod_s; +void bfa_os_printf(struct bfa_log_mod_s *log_mod, u32 msg_id, + const char *fmt, ...); +#endif + +#define boolean_t int + +/** + * For current time stamp, OS API will fill-in + */ +struct bfa_timeval_s { + u32 tv_sec; /* seconds */ + u32 tv_usec; /* microseconds */ +}; + +void bfa_os_gettimeofday(struct bfa_timeval_s *tv); + +static inline void +wwn2str(char *wwn_str, u64 wwn) +{ + union { + u64 wwn; + u8 byte[8]; + } w; + + w.wwn = wwn; + sprintf(wwn_str, "%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x", w.byte[0], + w.byte[1], w.byte[2], w.byte[3], w.byte[4], w.byte[5], + w.byte[6], w.byte[7]); +} + +static inline void +fcid2str(char *fcid_str, u32 fcid) +{ + union { + u32 fcid; + u8 byte[4]; + } f; + + f.fcid = fcid; + sprintf(fcid_str, "%02x:%02x:%02x", f.byte[1], f.byte[2], f.byte[3]); +} + +#endif /* __BFA_OS_INC_H__ */ diff --git a/drivers/scsi/bfa/bfa_port.c b/drivers/scsi/bfa/bfa_port.c new file mode 100644 index 000000000000..cab19028361a --- /dev/null +++ b/drivers/scsi/bfa/bfa_port.c @@ -0,0 +1,460 @@ +/* + * Copyright (c) 2005-2009 Brocade Communications Systems, Inc. + * All rights reserved + * www.brocade.com + * + * Linux driver for Brocade Fibre Channel Host Bus Adapter. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (GPL) Version 2 as + * published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#include <defs/bfa_defs_port.h> +#include <cs/bfa_trc.h> +#include <cs/bfa_log.h> +#include <cs/bfa_debug.h> +#include <port/bfa_port.h> +#include <bfi/bfi.h> +#include <bfi/bfi_port.h> +#include <bfa_ioc.h> +#include <cna/bfa_cna_trcmod.h> + +BFA_TRC_FILE(CNA, PORT); + +#define bfa_ioc_portid(__ioc) ((__ioc)->port_id) +#define bfa_lpuid(__arg) bfa_ioc_portid(&(__arg)->ioc) + +static void +bfa_port_stats_swap(struct bfa_port_s *port, union bfa_pport_stats_u *stats) +{ + u32 *dip = (u32 *) stats; + u32 t0, t1; + int i; + + for (i = 0; i < sizeof(union bfa_pport_stats_u) / sizeof(u32); + i += 2) { + t0 = dip[i]; + t1 = dip[i + 1]; +#ifdef __BIGENDIAN + dip[i] = bfa_os_ntohl(t0); + dip[i + 1] = bfa_os_ntohl(t1); +#else + dip[i] = bfa_os_ntohl(t1); + dip[i + 1] = bfa_os_ntohl(t0); +#endif + } + + /** todo + * QoS stats r also swapped as 64bit; that structure also + * has to use 64 bit counters + */ +} + +/** + * bfa_port_enable_isr() + * + * + * @param[in] port - Pointer to the port module + * status - Return status from the f/w + * + * @return void + */ +static void +bfa_port_enable_isr(struct bfa_port_s *port, bfa_status_t status) +{ + bfa_assert(0); +} + +/** + * bfa_port_disable_isr() + * + * + * @param[in] port - Pointer to the port module + * status - Return status from the f/w + * + * @return void + */ +static void +bfa_port_disable_isr(struct bfa_port_s *port, bfa_status_t status) +{ + bfa_assert(0); +} + +/** + * bfa_port_get_stats_isr() + * + * + * @param[in] port - Pointer to the Port module + * status - Return status from the f/w + * + * @return void + */ +static void +bfa_port_get_stats_isr(struct bfa_port_s *port, bfa_status_t status) +{ + port->stats_status = status; + port->stats_busy = BFA_FALSE; + + if (status == BFA_STATUS_OK) { + memcpy(port->stats, port->stats_dma.kva, + sizeof(union bfa_pport_stats_u)); + bfa_port_stats_swap(port, port->stats); + } + + if (port->stats_cbfn) { + port->stats_cbfn(port->stats_cbarg, status); + port->stats_cbfn = NULL; + } +} + +/** + * bfa_port_clear_stats_isr() + * + * + * @param[in] port - Pointer to the Port module + * status - Return status from the f/w + * + * @return void + */ +static void +bfa_port_clear_stats_isr(struct bfa_port_s *port, bfa_status_t status) +{ + port->stats_status = status; + port->stats_busy = BFA_FALSE; + + if (port->stats_cbfn) { + port->stats_cbfn(port->stats_cbarg, status); + port->stats_cbfn = NULL; + } +} + +/** + * bfa_port_isr() + * + * + * @param[in] Pointer to the Port module data structure. + * + * @return void + */ +static void +bfa_port_isr(void *cbarg, struct bfi_mbmsg_s *m) +{ + struct bfa_port_s *port = (struct bfa_port_s *)cbarg; + union bfi_port_i2h_msg_u *i2hmsg; + + i2hmsg = (union bfi_port_i2h_msg_u *)m; + bfa_trc(port, m->mh.msg_id); + + switch (m->mh.msg_id) { + case BFI_PORT_I2H_ENABLE_RSP: + if (port->endis_pending == BFA_FALSE) + break; + bfa_port_enable_isr(port, i2hmsg->enable_rsp.status); + break; + + case BFI_PORT_I2H_DISABLE_RSP: + if (port->endis_pending == BFA_FALSE) + break; + bfa_port_disable_isr(port, i2hmsg->disable_rsp.status); + break; + + case BFI_PORT_I2H_GET_STATS_RSP: + /* + * Stats busy flag is still set? (may be cmd timed out) + */ + if (port->stats_busy == BFA_FALSE) + break; + bfa_port_get_stats_isr(port, i2hmsg->getstats_rsp.status); + break; + + case BFI_PORT_I2H_CLEAR_STATS_RSP: + if (port->stats_busy == BFA_FALSE) + break; + bfa_port_clear_stats_isr(port, i2hmsg->clearstats_rsp.status); + break; + + default: + bfa_assert(0); + } +} + +/** + * bfa_port_meminfo() + * + * + * @param[in] void + * + * @return Size of DMA region + */ +u32 +bfa_port_meminfo(void) +{ + return BFA_ROUNDUP(sizeof(union bfa_pport_stats_u), BFA_DMA_ALIGN_SZ); +} + +/** + * bfa_port_mem_claim() + * + * + * @param[in] port Port module pointer + * dma_kva Kernel Virtual Address of Port DMA Memory + * dma_pa Physical Address of Port DMA Memory + * + * @return void + */ +void +bfa_port_mem_claim(struct bfa_port_s *port, u8 *dma_kva, u64 dma_pa) +{ + port->stats_dma.kva = dma_kva; + port->stats_dma.pa = dma_pa; +} + +/** + * bfa_port_enable() + * + * Send the Port enable request to the f/w + * + * @param[in] Pointer to the Port module data structure. + * + * @return Status + */ +bfa_status_t +bfa_port_enable(struct bfa_port_s *port, bfa_port_endis_cbfn_t cbfn, + void *cbarg) +{ + struct bfi_port_generic_req_s *m; + + /** todo Not implemented */ + bfa_assert(0); + + if (!bfa_ioc_is_operational(port->ioc)) { + bfa_trc(port, BFA_STATUS_IOC_FAILURE); + return BFA_STATUS_IOC_FAILURE; + } + + if (port->endis_pending) { + bfa_trc(port, BFA_STATUS_DEVBUSY); + return BFA_STATUS_DEVBUSY; + } + + m = (struct bfi_port_generic_req_s *)port->endis_mb.msg; + + port->msgtag++; + port->endis_cbfn = cbfn; + port->endis_cbarg = cbarg; + port->endis_pending = BFA_TRUE; + + bfi_h2i_set(m->mh, BFI_MC_PORT, BFI_PORT_H2I_ENABLE_REQ, + bfa_ioc_portid(port->ioc)); + bfa_ioc_mbox_queue(port->ioc, &port->endis_mb); + + return BFA_STATUS_OK; +} + +/** + * bfa_port_disable() + * + * Send the Port disable request to the f/w + * + * @param[in] Pointer to the Port module data structure. + * + * @return Status + */ +bfa_status_t +bfa_port_disable(struct bfa_port_s *port, bfa_port_endis_cbfn_t cbfn, + void *cbarg) +{ + struct bfi_port_generic_req_s *m; + + /** todo Not implemented */ + bfa_assert(0); + + if (!bfa_ioc_is_operational(port->ioc)) { + bfa_trc(port, BFA_STATUS_IOC_FAILURE); + return BFA_STATUS_IOC_FAILURE; + } + + if (port->endis_pending) { + bfa_trc(port, BFA_STATUS_DEVBUSY); + return BFA_STATUS_DEVBUSY; + } + + m = (struct bfi_port_generic_req_s *)port->endis_mb.msg; + + port->msgtag++; + port->endis_cbfn = cbfn; + port->endis_cbarg = cbarg; + port->endis_pending = BFA_TRUE; + + bfi_h2i_set(m->mh, BFI_MC_PORT, BFI_PORT_H2I_DISABLE_REQ, + bfa_ioc_portid(port->ioc)); + bfa_ioc_mbox_queue(port->ioc, &port->endis_mb); + + return BFA_STATUS_OK; +} + +/** + * bfa_port_get_stats() + * + * Send the request to the f/w to fetch Port statistics. + * + * @param[in] Pointer to the Port module data structure. + * + * @return Status + */ +bfa_status_t +bfa_port_get_stats(struct bfa_port_s *port, union bfa_pport_stats_u *stats, + bfa_port_stats_cbfn_t cbfn, void *cbarg) +{ + struct bfi_port_get_stats_req_s *m; + + if (!bfa_ioc_is_operational(port->ioc)) { + bfa_trc(port, BFA_STATUS_IOC_FAILURE); + return BFA_STATUS_IOC_FAILURE; + } + + if (port->stats_busy) { + bfa_trc(port, BFA_STATUS_DEVBUSY); + return BFA_STATUS_DEVBUSY; + } + + m = (struct bfi_port_get_stats_req_s *)port->stats_mb.msg; + + port->stats = stats; + port->stats_cbfn = cbfn; + port->stats_cbarg = cbarg; + port->stats_busy = BFA_TRUE; + bfa_dma_be_addr_set(m->dma_addr, port->stats_dma.pa); + + bfi_h2i_set(m->mh, BFI_MC_PORT, BFI_PORT_H2I_GET_STATS_REQ, + bfa_ioc_portid(port->ioc)); + bfa_ioc_mbox_queue(port->ioc, &port->stats_mb); + + return BFA_STATUS_OK; +} + +/** + * bfa_port_clear_stats() + * + * + * @param[in] Pointer to the Port module data structure. + * + * @return Status + */ +bfa_status_t +bfa_port_clear_stats(struct bfa_port_s *port, bfa_port_stats_cbfn_t cbfn, + void *cbarg) +{ + struct bfi_port_generic_req_s *m; + + if (!bfa_ioc_is_operational(port->ioc)) { + bfa_trc(port, BFA_STATUS_IOC_FAILURE); + return BFA_STATUS_IOC_FAILURE; + } + + if (port->stats_busy) { + bfa_trc(port, BFA_STATUS_DEVBUSY); + return BFA_STATUS_DEVBUSY; + } + + m = (struct bfi_port_generic_req_s *)port->stats_mb.msg; + + port->stats_cbfn = cbfn; + port->stats_cbarg = cbarg; + port->stats_busy = BFA_TRUE; + + bfi_h2i_set(m->mh, BFI_MC_PORT, BFI_PORT_H2I_CLEAR_STATS_REQ, + bfa_ioc_portid(port->ioc)); + bfa_ioc_mbox_queue(port->ioc, &port->stats_mb); + + return BFA_STATUS_OK; +} + +/** + * bfa_port_hbfail() + * + * + * @param[in] Pointer to the Port module data structure. + * + * @return void + */ +void +bfa_port_hbfail(void *arg) +{ + struct bfa_port_s *port = (struct bfa_port_s *)arg; + + /* + * Fail any pending get_stats/clear_stats requests + */ + if (port->stats_busy) { + if (port->stats_cbfn) + port->stats_cbfn(port->dev, BFA_STATUS_FAILED); + port->stats_cbfn = NULL; + port->stats_busy = BFA_FALSE; + } + + /* + * Clear any enable/disable is pending + */ + if (port->endis_pending) { + if (port->endis_cbfn) + port->endis_cbfn(port->dev, BFA_STATUS_FAILED); + port->endis_cbfn = NULL; + port->endis_pending = BFA_FALSE; + } +} + +/** + * bfa_port_attach() + * + * + * @param[in] port - Pointer to the Port module data structure + * ioc - Pointer to the ioc module data structure + * dev - Pointer to the device driver module data structure + * The device driver specific mbox ISR functions have + * this pointer as one of the parameters. + * trcmod - + * logmod - + * + * @return void + */ +void +bfa_port_attach(struct bfa_port_s *port, struct bfa_ioc_s *ioc, void *dev, + struct bfa_trc_mod_s *trcmod, struct bfa_log_mod_s *logmod) +{ + bfa_assert(port); + + port->dev = dev; + port->ioc = ioc; + port->trcmod = trcmod; + port->logmod = logmod; + + port->stats_busy = port->endis_pending = BFA_FALSE; + port->stats_cbfn = port->endis_cbfn = NULL; + + bfa_ioc_mbox_regisr(port->ioc, BFI_MC_PORT, bfa_port_isr, port); + bfa_ioc_hbfail_init(&port->hbfail, bfa_port_hbfail, port); + bfa_ioc_hbfail_register(port->ioc, &port->hbfail); + + bfa_trc(port, 0); +} + +/** + * bfa_port_detach() + * + * + * @param[in] port - Pointer to the Port module data structure + * + * @return void + */ +void +bfa_port_detach(struct bfa_port_s *port) +{ + bfa_trc(port, 0); +} diff --git a/drivers/scsi/bfa/bfa_port_priv.h b/drivers/scsi/bfa/bfa_port_priv.h new file mode 100644 index 000000000000..4b97e2759908 --- /dev/null +++ b/drivers/scsi/bfa/bfa_port_priv.h @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2005-2009 Brocade Communications Systems, Inc. + * All rights reserved + * www.brocade.com + * + * Linux driver for Brocade Fibre Channel Host Bus Adapter. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (GPL) Version 2 as + * published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#ifndef __BFA_PORT_PRIV_H__ +#define __BFA_PORT_PRIV_H__ + +#include <defs/bfa_defs_pport.h> +#include <bfi/bfi_pport.h> +#include "bfa_intr_priv.h" + +/** + * BFA physical port data structure + */ +struct bfa_pport_s { + struct bfa_s *bfa; /* parent BFA instance */ + bfa_sm_t sm; /* port state machine */ + wwn_t nwwn; /* node wwn of physical port */ + wwn_t pwwn; /* port wwn of physical oprt */ + enum bfa_pport_speed speed_sup; + /* supported speeds */ + enum bfa_pport_speed speed; /* current speed */ + enum bfa_pport_topology topology; /* current topology */ + u8 myalpa; /* my ALPA in LOOP topology */ + u8 rsvd[3]; + struct bfa_pport_cfg_s cfg; /* current port configuration */ + struct bfa_qos_attr_s qos_attr; /* QoS Attributes */ + struct bfa_qos_vc_attr_s qos_vc_attr; /* VC info from ELP */ + struct bfa_reqq_wait_s reqq_wait; + /* to wait for room in reqq */ + struct bfa_reqq_wait_s svcreq_wait; + /* to wait for room in reqq */ + struct bfa_reqq_wait_s stats_reqq_wait; + /* to wait for room in reqq (stats) */ + void *event_cbarg; + void (*event_cbfn) (void *cbarg, + bfa_pport_event_t event); + union { + union bfi_pport_i2h_msg_u i2hmsg; + } event_arg; + void *bfad; /* BFA driver handle */ + struct bfa_cb_qe_s hcb_qe; /* BFA callback queue elem */ + enum bfa_pport_linkstate hcb_event; + /* link event for callback */ + u32 msgtag; /* fimrware msg tag for reply */ + u8 *stats_kva; + u64 stats_pa; + union bfa_pport_stats_u *stats; /* pport stats */ + u32 mypid : 24; + u32 rsvd_b : 8; + struct bfa_timer_s timer; /* timer */ + union bfa_pport_stats_u *stats_ret; + /* driver stats location */ + bfa_status_t stats_status; + /* stats/statsclr status */ + bfa_boolean_t stats_busy; + /* outstanding stats/statsclr */ + bfa_boolean_t stats_qfull; + bfa_boolean_t diag_busy; + /* diag busy status */ + bfa_boolean_t beacon; + /* port beacon status */ + bfa_boolean_t link_e2e_beacon; + /* link beacon status */ + bfa_cb_pport_t stats_cbfn; + /* driver callback function */ + void *stats_cbarg; + /* *!< user callback arg */ +}; + +#define BFA_PORT_MOD(__bfa) (&(__bfa)->modules.pport) + +/* + * public functions + */ +void bfa_pport_isr(struct bfa_s *bfa, struct bfi_msg_s *msg); +#endif /* __BFA_PORT_PRIV_H__ */ diff --git a/drivers/scsi/bfa/bfa_priv.h b/drivers/scsi/bfa/bfa_priv.h new file mode 100644 index 000000000000..0747a6b26f7b --- /dev/null +++ b/drivers/scsi/bfa/bfa_priv.h @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2005-2009 Brocade Communications Systems, Inc. + * All rights reserved + * www.brocade.com + * + * Linux driver for Brocade Fibre Channel Host Bus Adapter. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (GPL) Version 2 as + * published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#ifndef __BFA_PRIV_H__ +#define __BFA_PRIV_H__ + +#include "bfa_iocfc.h" +#include "bfa_intr_priv.h" +#include "bfa_trcmod_priv.h" +#include "bfa_modules_priv.h" +#include "bfa_fwimg_priv.h" +#include <cs/bfa_log.h> +#include <bfa_timer.h> + +/** + * Macro to define a new BFA module + */ +#define BFA_MODULE(__mod) \ + static void bfa_ ## __mod ## _meminfo( \ + struct bfa_iocfc_cfg_s *cfg, u32 *ndm_len, \ + u32 *dm_len); \ + static void bfa_ ## __mod ## _attach(struct bfa_s *bfa, \ + void *bfad, struct bfa_iocfc_cfg_s *cfg, \ + struct bfa_meminfo_s *meminfo, \ + struct bfa_pcidev_s *pcidev); \ + static void bfa_ ## __mod ## _initdone(struct bfa_s *bfa); \ + static void bfa_ ## __mod ## _detach(struct bfa_s *bfa); \ + static void bfa_ ## __mod ## _start(struct bfa_s *bfa); \ + static void bfa_ ## __mod ## _stop(struct bfa_s *bfa); \ + static void bfa_ ## __mod ## _iocdisable(struct bfa_s *bfa); \ + \ + extern struct bfa_module_s hal_mod_ ## __mod; \ + struct bfa_module_s hal_mod_ ## __mod = { \ + bfa_ ## __mod ## _meminfo, \ + bfa_ ## __mod ## _attach, \ + bfa_ ## __mod ## _initdone, \ + bfa_ ## __mod ## _detach, \ + bfa_ ## __mod ## _start, \ + bfa_ ## __mod ## _stop, \ + bfa_ ## __mod ## _iocdisable, \ + } + +#define BFA_CACHELINE_SZ (256) + +/** + * Structure used to interact between different BFA sub modules + * + * Each sub module needs to implement only the entry points relevant to it (and + * can leave entry points as NULL) + */ +struct bfa_module_s { + void (*meminfo) (struct bfa_iocfc_cfg_s *cfg, u32 *km_len, + u32 *dm_len); + void (*attach) (struct bfa_s *bfa, void *bfad, + struct bfa_iocfc_cfg_s *cfg, + struct bfa_meminfo_s *meminfo, + struct bfa_pcidev_s *pcidev); + void (*initdone) (struct bfa_s *bfa); + void (*detach) (struct bfa_s *bfa); + void (*start) (struct bfa_s *bfa); + void (*stop) (struct bfa_s *bfa); + void (*iocdisable) (struct bfa_s *bfa); +}; + +extern struct bfa_module_s *hal_mods[]; + +struct bfa_s { + void *bfad; /* BFA driver instance */ + struct bfa_aen_s *aen; /* AEN module */ + struct bfa_plog_s *plog; /* portlog buffer */ + struct bfa_log_mod_s *logm; /* driver logging modulen */ + struct bfa_trc_mod_s *trcmod; /* driver tracing */ + struct bfa_ioc_s ioc; /* IOC module */ + struct bfa_iocfc_s iocfc; /* IOCFC module */ + struct bfa_timer_mod_s timer_mod; /* timer module */ + struct bfa_modules_s modules; /* BFA modules */ + struct list_head comp_q; /* pending completions */ + bfa_boolean_t rme_process; /* RME processing enabled */ + struct list_head reqq_waitq[BFI_IOC_MAX_CQS]; + bfa_boolean_t fcs; /* FCS is attached to BFA */ + struct bfa_msix_s msix; +}; + +extern bfa_isr_func_t bfa_isrs[BFI_MC_MAX]; +extern bfa_ioc_mbox_mcfunc_t bfa_mbox_isrs[]; +extern bfa_boolean_t bfa_auto_recover; +extern struct bfa_module_s hal_mod_flash; +extern struct bfa_module_s hal_mod_fcdiag; +extern struct bfa_module_s hal_mod_sgpg; +extern struct bfa_module_s hal_mod_pport; +extern struct bfa_module_s hal_mod_fcxp; +extern struct bfa_module_s hal_mod_lps; +extern struct bfa_module_s hal_mod_uf; +extern struct bfa_module_s hal_mod_rport; +extern struct bfa_module_s hal_mod_fcpim; +extern struct bfa_module_s hal_mod_pbind; + +#endif /* __BFA_PRIV_H__ */ + diff --git a/drivers/scsi/bfa/bfa_rport.c b/drivers/scsi/bfa/bfa_rport.c new file mode 100644 index 000000000000..16da77a8db28 --- /dev/null +++ b/drivers/scsi/bfa/bfa_rport.c @@ -0,0 +1,911 @@ +/* + * Copyright (c) 2005-2009 Brocade Communications Systems, Inc. + * All rights reserved + * www.brocade.com + * + * Linux driver for Brocade Fibre Channel Host Bus Adapter. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (GPL) Version 2 as + * published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#include <bfa.h> +#include <bfa_svc.h> +#include <cs/bfa_debug.h> +#include <bfi/bfi_rport.h> +#include "bfa_intr_priv.h" + +BFA_TRC_FILE(HAL, RPORT); +BFA_MODULE(rport); + +#define bfa_rport_offline_cb(__rp) do { \ + if ((__rp)->bfa->fcs) \ + bfa_cb_rport_offline((__rp)->rport_drv); \ + else { \ + bfa_cb_queue((__rp)->bfa, &(__rp)->hcb_qe, \ + __bfa_cb_rport_offline, (__rp)); \ + } \ +} while (0) + +#define bfa_rport_online_cb(__rp) do { \ + if ((__rp)->bfa->fcs) \ + bfa_cb_rport_online((__rp)->rport_drv); \ + else { \ + bfa_cb_queue((__rp)->bfa, &(__rp)->hcb_qe, \ + __bfa_cb_rport_online, (__rp)); \ + } \ +} while (0) + +/* + * forward declarations + */ +static struct bfa_rport_s *bfa_rport_alloc(struct bfa_rport_mod_s *rp_mod); +static void bfa_rport_free(struct bfa_rport_s *rport); +static bfa_boolean_t bfa_rport_send_fwcreate(struct bfa_rport_s *rp); +static bfa_boolean_t bfa_rport_send_fwdelete(struct bfa_rport_s *rp); +static bfa_boolean_t bfa_rport_send_fwspeed(struct bfa_rport_s *rp); +static void __bfa_cb_rport_online(void *cbarg, bfa_boolean_t complete); +static void __bfa_cb_rport_offline(void *cbarg, bfa_boolean_t complete); + +/** + * bfa_rport_sm BFA rport state machine + */ + + +enum bfa_rport_event { + BFA_RPORT_SM_CREATE = 1, /* rport create event */ + BFA_RPORT_SM_DELETE = 2, /* deleting an existing rport */ + BFA_RPORT_SM_ONLINE = 3, /* rport is online */ + BFA_RPORT_SM_OFFLINE = 4, /* rport is offline */ + BFA_RPORT_SM_FWRSP = 5, /* firmware response */ + BFA_RPORT_SM_HWFAIL = 6, /* IOC h/w failure */ + BFA_RPORT_SM_QOS_SCN = 7, /* QoS SCN from firmware */ + BFA_RPORT_SM_SET_SPEED = 8, /* Set Rport Speed */ + BFA_RPORT_SM_QRESUME = 9, /* space in requeue queue */ +}; + +static void bfa_rport_sm_uninit(struct bfa_rport_s *rp, + enum bfa_rport_event event); +static void bfa_rport_sm_created(struct bfa_rport_s *rp, + enum bfa_rport_event event); +static void bfa_rport_sm_fwcreate(struct bfa_rport_s *rp, + enum bfa_rport_event event); +static void bfa_rport_sm_online(struct bfa_rport_s *rp, + enum bfa_rport_event event); +static void bfa_rport_sm_fwdelete(struct bfa_rport_s *rp, + enum bfa_rport_event event); +static void bfa_rport_sm_offline(struct bfa_rport_s *rp, + enum bfa_rport_event event); +static void bfa_rport_sm_deleting(struct bfa_rport_s *rp, + enum bfa_rport_event event); +static void bfa_rport_sm_offline_pending(struct bfa_rport_s *rp, + enum bfa_rport_event event); +static void bfa_rport_sm_delete_pending(struct bfa_rport_s *rp, + enum bfa_rport_event event); +static void bfa_rport_sm_iocdisable(struct bfa_rport_s *rp, + enum bfa_rport_event event); +static void bfa_rport_sm_fwcreate_qfull(struct bfa_rport_s *rp, + enum bfa_rport_event event); +static void bfa_rport_sm_fwdelete_qfull(struct bfa_rport_s *rp, + enum bfa_rport_event event); +static void bfa_rport_sm_deleting_qfull(struct bfa_rport_s *rp, + enum bfa_rport_event event); + +/** + * Beginning state, only online event expected. + */ +static void +bfa_rport_sm_uninit(struct bfa_rport_s *rp, enum bfa_rport_event event) +{ + bfa_trc(rp->bfa, rp->rport_tag); + bfa_trc(rp->bfa, event); + + switch (event) { + case BFA_RPORT_SM_CREATE: + bfa_stats(rp, sm_un_cr); + bfa_sm_set_state(rp, bfa_rport_sm_created); + break; + + default: + bfa_stats(rp, sm_un_unexp); + bfa_assert(0); + } +} + +static void +bfa_rport_sm_created(struct bfa_rport_s *rp, enum bfa_rport_event event) +{ + bfa_trc(rp->bfa, rp->rport_tag); + bfa_trc(rp->bfa, event); + + switch (event) { + case BFA_RPORT_SM_ONLINE: + bfa_stats(rp, sm_cr_on); + if (bfa_rport_send_fwcreate(rp)) + bfa_sm_set_state(rp, bfa_rport_sm_fwcreate); + else + bfa_sm_set_state(rp, bfa_rport_sm_fwcreate_qfull); + break; + + case BFA_RPORT_SM_DELETE: + bfa_stats(rp, sm_cr_del); + bfa_sm_set_state(rp, bfa_rport_sm_uninit); + bfa_rport_free(rp); + break; + + case BFA_RPORT_SM_HWFAIL: + bfa_stats(rp, sm_cr_hwf); + bfa_sm_set_state(rp, bfa_rport_sm_iocdisable); + break; + + default: + bfa_stats(rp, sm_cr_unexp); + bfa_assert(0); + } +} + +/** + * Waiting for rport create response from firmware. + */ +static void +bfa_rport_sm_fwcreate(struct bfa_rport_s *rp, enum bfa_rport_event event) +{ + bfa_trc(rp->bfa, rp->rport_tag); + bfa_trc(rp->bfa, event); + + switch (event) { + case BFA_RPORT_SM_FWRSP: + bfa_stats(rp, sm_fwc_rsp); + bfa_sm_set_state(rp, bfa_rport_sm_online); + bfa_rport_online_cb(rp); + break; + + case BFA_RPORT_SM_DELETE: + bfa_stats(rp, sm_fwc_del); + bfa_sm_set_state(rp, bfa_rport_sm_delete_pending); + break; + + case BFA_RPORT_SM_OFFLINE: + bfa_stats(rp, sm_fwc_off); + bfa_sm_set_state(rp, bfa_rport_sm_offline_pending); + break; + + case BFA_RPORT_SM_HWFAIL: + bfa_stats(rp, sm_fwc_hwf); + bfa_sm_set_state(rp, bfa_rport_sm_iocdisable); + break; + + default: + bfa_stats(rp, sm_fwc_unexp); + bfa_assert(0); + } +} + +/** + * Request queue is full, awaiting queue resume to send create request. + */ +static void +bfa_rport_sm_fwcreate_qfull(struct bfa_rport_s *rp, enum bfa_rport_event event) +{ + bfa_trc(rp->bfa, rp->rport_tag); + bfa_trc(rp->bfa, event); + + switch (event) { + case BFA_RPORT_SM_QRESUME: + bfa_sm_set_state(rp, bfa_rport_sm_fwcreate); + bfa_rport_send_fwcreate(rp); + break; + + case BFA_RPORT_SM_DELETE: + bfa_stats(rp, sm_fwc_del); + bfa_sm_set_state(rp, bfa_rport_sm_uninit); + bfa_reqq_wcancel(&rp->reqq_wait); + bfa_rport_free(rp); + break; + + case BFA_RPORT_SM_OFFLINE: + bfa_stats(rp, sm_fwc_off); + bfa_sm_set_state(rp, bfa_rport_sm_offline); + bfa_reqq_wcancel(&rp->reqq_wait); + bfa_rport_offline_cb(rp); + break; + + case BFA_RPORT_SM_HWFAIL: + bfa_stats(rp, sm_fwc_hwf); + bfa_sm_set_state(rp, bfa_rport_sm_iocdisable); + bfa_reqq_wcancel(&rp->reqq_wait); + break; + + default: + bfa_stats(rp, sm_fwc_unexp); + bfa_assert(0); + } +} + +/** + * Online state - normal parking state. + */ +static void +bfa_rport_sm_online(struct bfa_rport_s *rp, enum bfa_rport_event event) +{ + struct bfi_rport_qos_scn_s *qos_scn; + + bfa_trc(rp->bfa, rp->rport_tag); + bfa_trc(rp->bfa, event); + + switch (event) { + case BFA_RPORT_SM_OFFLINE: + bfa_stats(rp, sm_on_off); + if (bfa_rport_send_fwdelete(rp)) + bfa_sm_set_state(rp, bfa_rport_sm_fwdelete); + else + bfa_sm_set_state(rp, bfa_rport_sm_fwdelete_qfull); + break; + + case BFA_RPORT_SM_DELETE: + bfa_stats(rp, sm_on_del); + if (bfa_rport_send_fwdelete(rp)) + bfa_sm_set_state(rp, bfa_rport_sm_deleting); + else + bfa_sm_set_state(rp, bfa_rport_sm_deleting_qfull); + break; + + case BFA_RPORT_SM_HWFAIL: + bfa_stats(rp, sm_on_hwf); + bfa_sm_set_state(rp, bfa_rport_sm_iocdisable); + break; + + case BFA_RPORT_SM_SET_SPEED: + bfa_rport_send_fwspeed(rp); + break; + + case BFA_RPORT_SM_QOS_SCN: + qos_scn = (struct bfi_rport_qos_scn_s *) rp->event_arg.fw_msg; + rp->qos_attr = qos_scn->new_qos_attr; + bfa_trc(rp->bfa, qos_scn->old_qos_attr.qos_flow_id); + bfa_trc(rp->bfa, qos_scn->new_qos_attr.qos_flow_id); + bfa_trc(rp->bfa, qos_scn->old_qos_attr.qos_priority); + bfa_trc(rp->bfa, qos_scn->new_qos_attr.qos_priority); + + qos_scn->old_qos_attr.qos_flow_id = + bfa_os_ntohl(qos_scn->old_qos_attr.qos_flow_id); + qos_scn->new_qos_attr.qos_flow_id = + bfa_os_ntohl(qos_scn->new_qos_attr.qos_flow_id); + qos_scn->old_qos_attr.qos_priority = + bfa_os_ntohl(qos_scn->old_qos_attr.qos_priority); + qos_scn->new_qos_attr.qos_priority = + bfa_os_ntohl(qos_scn->new_qos_attr.qos_priority); + + if (qos_scn->old_qos_attr.qos_flow_id != + qos_scn->new_qos_attr.qos_flow_id) + bfa_cb_rport_qos_scn_flowid(rp->rport_drv, + qos_scn->old_qos_attr, + qos_scn->new_qos_attr); + if (qos_scn->old_qos_attr.qos_priority != + qos_scn->new_qos_attr.qos_priority) + bfa_cb_rport_qos_scn_prio(rp->rport_drv, + qos_scn->old_qos_attr, + qos_scn->new_qos_attr); + break; + + default: + bfa_stats(rp, sm_on_unexp); + bfa_assert(0); + } +} + +/** + * Firmware rport is being deleted - awaiting f/w response. + */ +static void +bfa_rport_sm_fwdelete(struct bfa_rport_s *rp, enum bfa_rport_event event) +{ + bfa_trc(rp->bfa, rp->rport_tag); + bfa_trc(rp->bfa, event); + + switch (event) { + case BFA_RPORT_SM_FWRSP: + bfa_stats(rp, sm_fwd_rsp); + bfa_sm_set_state(rp, bfa_rport_sm_offline); + bfa_rport_offline_cb(rp); + break; + + case BFA_RPORT_SM_DELETE: + bfa_stats(rp, sm_fwd_del); + bfa_sm_set_state(rp, bfa_rport_sm_deleting); + break; + + case BFA_RPORT_SM_HWFAIL: + bfa_stats(rp, sm_fwd_hwf); + bfa_sm_set_state(rp, bfa_rport_sm_iocdisable); + bfa_rport_offline_cb(rp); + break; + + default: + bfa_stats(rp, sm_fwd_unexp); + bfa_assert(0); + } +} + +static void +bfa_rport_sm_fwdelete_qfull(struct bfa_rport_s *rp, enum bfa_rport_event event) +{ + bfa_trc(rp->bfa, rp->rport_tag); + bfa_trc(rp->bfa, event); + + switch (event) { + case BFA_RPORT_SM_QRESUME: + bfa_sm_set_state(rp, bfa_rport_sm_fwdelete); + bfa_rport_send_fwdelete(rp); + break; + + case BFA_RPORT_SM_DELETE: + bfa_stats(rp, sm_fwd_del); + bfa_sm_set_state(rp, bfa_rport_sm_deleting_qfull); + break; + + case BFA_RPORT_SM_HWFAIL: + bfa_stats(rp, sm_fwd_hwf); + bfa_sm_set_state(rp, bfa_rport_sm_iocdisable); + bfa_reqq_wcancel(&rp->reqq_wait); + bfa_rport_offline_cb(rp); + break; + + default: + bfa_stats(rp, sm_fwd_unexp); + bfa_assert(0); + } +} + +/** + * Offline state. + */ +static void +bfa_rport_sm_offline(struct bfa_rport_s *rp, enum bfa_rport_event event) +{ + bfa_trc(rp->bfa, rp->rport_tag); + bfa_trc(rp->bfa, event); + + switch (event) { + case BFA_RPORT_SM_DELETE: + bfa_stats(rp, sm_off_del); + bfa_sm_set_state(rp, bfa_rport_sm_uninit); + bfa_rport_free(rp); + break; + + case BFA_RPORT_SM_ONLINE: + bfa_stats(rp, sm_off_on); + if (bfa_rport_send_fwcreate(rp)) + bfa_sm_set_state(rp, bfa_rport_sm_fwcreate); + else + bfa_sm_set_state(rp, bfa_rport_sm_fwcreate_qfull); + break; + + case BFA_RPORT_SM_HWFAIL: + bfa_stats(rp, sm_off_hwf); + bfa_sm_set_state(rp, bfa_rport_sm_iocdisable); + break; + + default: + bfa_stats(rp, sm_off_unexp); + bfa_assert(0); + } +} + +/** + * Rport is deleted, waiting for firmware response to delete. + */ +static void +bfa_rport_sm_deleting(struct bfa_rport_s *rp, enum bfa_rport_event event) +{ + bfa_trc(rp->bfa, rp->rport_tag); + bfa_trc(rp->bfa, event); + + switch (event) { + case BFA_RPORT_SM_FWRSP: + bfa_stats(rp, sm_del_fwrsp); + bfa_sm_set_state(rp, bfa_rport_sm_uninit); + bfa_rport_free(rp); + break; + + case BFA_RPORT_SM_HWFAIL: + bfa_stats(rp, sm_del_hwf); + bfa_sm_set_state(rp, bfa_rport_sm_uninit); + bfa_rport_free(rp); + break; + + default: + bfa_assert(0); + } +} + +static void +bfa_rport_sm_deleting_qfull(struct bfa_rport_s *rp, enum bfa_rport_event event) +{ + bfa_trc(rp->bfa, rp->rport_tag); + bfa_trc(rp->bfa, event); + + switch (event) { + case BFA_RPORT_SM_QRESUME: + bfa_stats(rp, sm_del_fwrsp); + bfa_sm_set_state(rp, bfa_rport_sm_deleting); + bfa_rport_send_fwdelete(rp); + break; + + case BFA_RPORT_SM_HWFAIL: + bfa_stats(rp, sm_del_hwf); + bfa_sm_set_state(rp, bfa_rport_sm_uninit); + bfa_reqq_wcancel(&rp->reqq_wait); + bfa_rport_free(rp); + break; + + default: + bfa_assert(0); + } +} + +/** + * Waiting for rport create response from firmware. A delete is pending. + */ +static void +bfa_rport_sm_delete_pending(struct bfa_rport_s *rp, + enum bfa_rport_event event) +{ + bfa_trc(rp->bfa, rp->rport_tag); + bfa_trc(rp->bfa, event); + + switch (event) { + case BFA_RPORT_SM_FWRSP: + bfa_stats(rp, sm_delp_fwrsp); + if (bfa_rport_send_fwdelete(rp)) + bfa_sm_set_state(rp, bfa_rport_sm_deleting); + else + bfa_sm_set_state(rp, bfa_rport_sm_deleting_qfull); + break; + + case BFA_RPORT_SM_HWFAIL: + bfa_stats(rp, sm_delp_hwf); + bfa_sm_set_state(rp, bfa_rport_sm_uninit); + bfa_rport_free(rp); + break; + + default: + bfa_stats(rp, sm_delp_unexp); + bfa_assert(0); + } +} + +/** + * Waiting for rport create response from firmware. Rport offline is pending. + */ +static void +bfa_rport_sm_offline_pending(struct bfa_rport_s *rp, + enum bfa_rport_event event) +{ + bfa_trc(rp->bfa, rp->rport_tag); + bfa_trc(rp->bfa, event); + + switch (event) { + case BFA_RPORT_SM_FWRSP: + bfa_stats(rp, sm_offp_fwrsp); + if (bfa_rport_send_fwdelete(rp)) + bfa_sm_set_state(rp, bfa_rport_sm_fwdelete); + else + bfa_sm_set_state(rp, bfa_rport_sm_fwdelete_qfull); + break; + + case BFA_RPORT_SM_DELETE: + bfa_stats(rp, sm_offp_del); + bfa_sm_set_state(rp, bfa_rport_sm_delete_pending); + break; + + case BFA_RPORT_SM_HWFAIL: + bfa_stats(rp, sm_offp_hwf); + bfa_sm_set_state(rp, bfa_rport_sm_iocdisable); + break; + + default: + bfa_stats(rp, sm_offp_unexp); + bfa_assert(0); + } +} + +/** + * IOC h/w failed. + */ +static void +bfa_rport_sm_iocdisable(struct bfa_rport_s *rp, enum bfa_rport_event event) +{ + bfa_trc(rp->bfa, rp->rport_tag); + bfa_trc(rp->bfa, event); + + switch (event) { + case BFA_RPORT_SM_OFFLINE: + bfa_stats(rp, sm_iocd_off); + bfa_rport_offline_cb(rp); + break; + + case BFA_RPORT_SM_DELETE: + bfa_stats(rp, sm_iocd_del); + bfa_sm_set_state(rp, bfa_rport_sm_uninit); + bfa_rport_free(rp); + break; + + case BFA_RPORT_SM_ONLINE: + bfa_stats(rp, sm_iocd_on); + if (bfa_rport_send_fwcreate(rp)) + bfa_sm_set_state(rp, bfa_rport_sm_fwcreate); + else + bfa_sm_set_state(rp, bfa_rport_sm_fwcreate_qfull); + break; + + case BFA_RPORT_SM_HWFAIL: + break; + + default: + bfa_stats(rp, sm_iocd_unexp); + bfa_assert(0); + } +} + + + +/** + * bfa_rport_private BFA rport private functions + */ + +static void +__bfa_cb_rport_online(void *cbarg, bfa_boolean_t complete) +{ + struct bfa_rport_s *rp = cbarg; + + if (complete) + bfa_cb_rport_online(rp->rport_drv); +} + +static void +__bfa_cb_rport_offline(void *cbarg, bfa_boolean_t complete) +{ + struct bfa_rport_s *rp = cbarg; + + if (complete) + bfa_cb_rport_offline(rp->rport_drv); +} + +static void +bfa_rport_qresume(void *cbarg) +{ + struct bfa_rport_s *rp = cbarg; + + bfa_sm_send_event(rp, BFA_RPORT_SM_QRESUME); +} + +static void +bfa_rport_meminfo(struct bfa_iocfc_cfg_s *cfg, u32 *km_len, + u32 *dm_len) +{ + if (cfg->fwcfg.num_rports < BFA_RPORT_MIN) + cfg->fwcfg.num_rports = BFA_RPORT_MIN; + + *km_len += cfg->fwcfg.num_rports * sizeof(struct bfa_rport_s); +} + +static void +bfa_rport_attach(struct bfa_s *bfa, void *bfad, struct bfa_iocfc_cfg_s *cfg, + struct bfa_meminfo_s *meminfo, struct bfa_pcidev_s *pcidev) +{ + struct bfa_rport_mod_s *mod = BFA_RPORT_MOD(bfa); + struct bfa_rport_s *rp; + u16 i; + + INIT_LIST_HEAD(&mod->rp_free_q); + INIT_LIST_HEAD(&mod->rp_active_q); + + rp = (struct bfa_rport_s *) bfa_meminfo_kva(meminfo); + mod->rps_list = rp; + mod->num_rports = cfg->fwcfg.num_rports; + + bfa_assert(mod->num_rports + && !(mod->num_rports & (mod->num_rports - 1))); + + for (i = 0; i < mod->num_rports; i++, rp++) { + bfa_os_memset(rp, 0, sizeof(struct bfa_rport_s)); + rp->bfa = bfa; + rp->rport_tag = i; + bfa_sm_set_state(rp, bfa_rport_sm_uninit); + + /** + * - is unused + */ + if (i) + list_add_tail(&rp->qe, &mod->rp_free_q); + + bfa_reqq_winit(&rp->reqq_wait, bfa_rport_qresume, rp); + } + + /** + * consume memory + */ + bfa_meminfo_kva(meminfo) = (u8 *) rp; +} + +static void +bfa_rport_initdone(struct bfa_s *bfa) +{ +} + +static void +bfa_rport_detach(struct bfa_s *bfa) +{ +} + +static void +bfa_rport_start(struct bfa_s *bfa) +{ +} + +static void +bfa_rport_stop(struct bfa_s *bfa) +{ +} + +static void +bfa_rport_iocdisable(struct bfa_s *bfa) +{ + struct bfa_rport_mod_s *mod = BFA_RPORT_MOD(bfa); + struct bfa_rport_s *rport; + struct list_head *qe, *qen; + + list_for_each_safe(qe, qen, &mod->rp_active_q) { + rport = (struct bfa_rport_s *) qe; + bfa_sm_send_event(rport, BFA_RPORT_SM_HWFAIL); + } +} + +static struct bfa_rport_s * +bfa_rport_alloc(struct bfa_rport_mod_s *mod) +{ + struct bfa_rport_s *rport; + + bfa_q_deq(&mod->rp_free_q, &rport); + if (rport) + list_add_tail(&rport->qe, &mod->rp_active_q); + + return (rport); +} + +static void +bfa_rport_free(struct bfa_rport_s *rport) +{ + struct bfa_rport_mod_s *mod = BFA_RPORT_MOD(rport->bfa); + + bfa_assert(bfa_q_is_on_q(&mod->rp_active_q, rport)); + list_del(&rport->qe); + list_add_tail(&rport->qe, &mod->rp_free_q); +} + +static bfa_boolean_t +bfa_rport_send_fwcreate(struct bfa_rport_s *rp) +{ + struct bfi_rport_create_req_s *m; + + /** + * check for room in queue to send request now + */ + m = bfa_reqq_next(rp->bfa, BFA_REQQ_RPORT); + if (!m) { + bfa_reqq_wait(rp->bfa, BFA_REQQ_RPORT, &rp->reqq_wait); + return BFA_FALSE; + } + + bfi_h2i_set(m->mh, BFI_MC_RPORT, BFI_RPORT_H2I_CREATE_REQ, + bfa_lpuid(rp->bfa)); + m->bfa_handle = rp->rport_tag; + m->max_frmsz = bfa_os_htons(rp->rport_info.max_frmsz); + m->pid = rp->rport_info.pid; + m->lp_tag = rp->rport_info.lp_tag; + m->local_pid = rp->rport_info.local_pid; + m->fc_class = rp->rport_info.fc_class; + m->vf_en = rp->rport_info.vf_en; + m->vf_id = rp->rport_info.vf_id; + m->cisc = rp->rport_info.cisc; + + /** + * queue I/O message to firmware + */ + bfa_reqq_produce(rp->bfa, BFA_REQQ_RPORT); + return BFA_TRUE; +} + +static bfa_boolean_t +bfa_rport_send_fwdelete(struct bfa_rport_s *rp) +{ + struct bfi_rport_delete_req_s *m; + + /** + * check for room in queue to send request now + */ + m = bfa_reqq_next(rp->bfa, BFA_REQQ_RPORT); + if (!m) { + bfa_reqq_wait(rp->bfa, BFA_REQQ_RPORT, &rp->reqq_wait); + return BFA_FALSE; + } + + bfi_h2i_set(m->mh, BFI_MC_RPORT, BFI_RPORT_H2I_DELETE_REQ, + bfa_lpuid(rp->bfa)); + m->fw_handle = rp->fw_handle; + + /** + * queue I/O message to firmware + */ + bfa_reqq_produce(rp->bfa, BFA_REQQ_RPORT); + return BFA_TRUE; +} + +static bfa_boolean_t +bfa_rport_send_fwspeed(struct bfa_rport_s *rp) +{ + struct bfa_rport_speed_req_s *m; + + /** + * check for room in queue to send request now + */ + m = bfa_reqq_next(rp->bfa, BFA_REQQ_RPORT); + if (!m) { + bfa_trc(rp->bfa, rp->rport_info.speed); + return BFA_FALSE; + } + + bfi_h2i_set(m->mh, BFI_MC_RPORT, BFI_RPORT_H2I_SET_SPEED_REQ, + bfa_lpuid(rp->bfa)); + m->fw_handle = rp->fw_handle; + m->speed = (u8)rp->rport_info.speed; + + /** + * queue I/O message to firmware + */ + bfa_reqq_produce(rp->bfa, BFA_REQQ_RPORT); + return BFA_TRUE; +} + + + +/** + * bfa_rport_public + */ + +/** + * Rport interrupt processing. + */ +void +bfa_rport_isr(struct bfa_s *bfa, struct bfi_msg_s *m) +{ + union bfi_rport_i2h_msg_u msg; + struct bfa_rport_s *rp; + + bfa_trc(bfa, m->mhdr.msg_id); + + msg.msg = m; + + switch (m->mhdr.msg_id) { + case BFI_RPORT_I2H_CREATE_RSP: + rp = BFA_RPORT_FROM_TAG(bfa, msg.create_rsp->bfa_handle); + rp->fw_handle = msg.create_rsp->fw_handle; + rp->qos_attr = msg.create_rsp->qos_attr; + bfa_assert(msg.create_rsp->status == BFA_STATUS_OK); + bfa_sm_send_event(rp, BFA_RPORT_SM_FWRSP); + break; + + case BFI_RPORT_I2H_DELETE_RSP: + rp = BFA_RPORT_FROM_TAG(bfa, msg.delete_rsp->bfa_handle); + bfa_assert(msg.delete_rsp->status == BFA_STATUS_OK); + bfa_sm_send_event(rp, BFA_RPORT_SM_FWRSP); + break; + + case BFI_RPORT_I2H_QOS_SCN: + rp = BFA_RPORT_FROM_TAG(bfa, msg.qos_scn_evt->bfa_handle); + rp->event_arg.fw_msg = msg.qos_scn_evt; + bfa_sm_send_event(rp, BFA_RPORT_SM_QOS_SCN); + break; + + default: + bfa_trc(bfa, m->mhdr.msg_id); + bfa_assert(0); + } +} + + + +/** + * bfa_rport_api + */ + +struct bfa_rport_s * +bfa_rport_create(struct bfa_s *bfa, void *rport_drv) +{ + struct bfa_rport_s *rp; + + rp = bfa_rport_alloc(BFA_RPORT_MOD(bfa)); + + if (rp == NULL) + return (NULL); + + rp->bfa = bfa; + rp->rport_drv = rport_drv; + bfa_rport_clear_stats(rp); + + bfa_assert(bfa_sm_cmp_state(rp, bfa_rport_sm_uninit)); + bfa_sm_send_event(rp, BFA_RPORT_SM_CREATE); + + return (rp); +} + +void +bfa_rport_delete(struct bfa_rport_s *rport) +{ + bfa_sm_send_event(rport, BFA_RPORT_SM_DELETE); +} + +void +bfa_rport_online(struct bfa_rport_s *rport, struct bfa_rport_info_s *rport_info) +{ + bfa_assert(rport_info->max_frmsz != 0); + + /** + * Some JBODs are seen to be not setting PDU size correctly in PLOGI + * responses. Default to minimum size. + */ + if (rport_info->max_frmsz == 0) { + bfa_trc(rport->bfa, rport->rport_tag); + rport_info->max_frmsz = FC_MIN_PDUSZ; + } + + bfa_os_assign(rport->rport_info, *rport_info); + bfa_sm_send_event(rport, BFA_RPORT_SM_ONLINE); +} + +void +bfa_rport_offline(struct bfa_rport_s *rport) +{ + bfa_sm_send_event(rport, BFA_RPORT_SM_OFFLINE); +} + +void +bfa_rport_speed(struct bfa_rport_s *rport, enum bfa_pport_speed speed) +{ + bfa_assert(speed != 0); + bfa_assert(speed != BFA_PPORT_SPEED_AUTO); + + rport->rport_info.speed = speed; + bfa_sm_send_event(rport, BFA_RPORT_SM_SET_SPEED); +} + +void +bfa_rport_get_stats(struct bfa_rport_s *rport, + struct bfa_rport_hal_stats_s *stats) +{ + *stats = rport->stats; +} + +void +bfa_rport_get_qos_attr(struct bfa_rport_s *rport, + struct bfa_rport_qos_attr_s *qos_attr) +{ + qos_attr->qos_priority = bfa_os_ntohl(rport->qos_attr.qos_priority); + qos_attr->qos_flow_id = bfa_os_ntohl(rport->qos_attr.qos_flow_id); + +} + +void +bfa_rport_clear_stats(struct bfa_rport_s *rport) +{ + bfa_os_memset(&rport->stats, 0, sizeof(rport->stats)); +} + + diff --git a/drivers/scsi/bfa/bfa_rport_priv.h b/drivers/scsi/bfa/bfa_rport_priv.h new file mode 100644 index 000000000000..6490ce2e990d --- /dev/null +++ b/drivers/scsi/bfa/bfa_rport_priv.h @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2005-2009 Brocade Communications Systems, Inc. + * All rights reserved + * www.brocade.com + * + * Linux driver for Brocade Fibre Channel Host Bus Adapter. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (GPL) Version 2 as + * published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#ifndef __BFA_RPORT_PRIV_H__ +#define __BFA_RPORT_PRIV_H__ + +#include <bfa_svc.h> + +#define BFA_RPORT_MIN 4 + +struct bfa_rport_mod_s { + struct bfa_rport_s *rps_list; /* list of rports */ + struct list_head rp_free_q; /* free bfa_rports */ + struct list_head rp_active_q; /* free bfa_rports */ + u16 num_rports; /* number of rports */ +}; + +#define BFA_RPORT_MOD(__bfa) (&(__bfa)->modules.rport_mod) + +/** + * Convert rport tag to RPORT + */ +#define BFA_RPORT_FROM_TAG(__bfa, _tag) \ + (BFA_RPORT_MOD(__bfa)->rps_list + \ + ((_tag) & (BFA_RPORT_MOD(__bfa)->num_rports - 1))) + +/* + * external functions + */ +void bfa_rport_isr(struct bfa_s *bfa, struct bfi_msg_s *msg); +#endif /* __BFA_RPORT_PRIV_H__ */ diff --git a/drivers/scsi/bfa/bfa_sgpg.c b/drivers/scsi/bfa/bfa_sgpg.c new file mode 100644 index 000000000000..279d8f9b8907 --- /dev/null +++ b/drivers/scsi/bfa/bfa_sgpg.c @@ -0,0 +1,231 @@ +/* + * Copyright (c) 2005-2009 Brocade Communications Systems, Inc. + * All rights reserved + * www.brocade.com + * + * Linux driver for Brocade Fibre Channel Host Bus Adapter. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (GPL) Version 2 as + * published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#include <bfa.h> + +BFA_TRC_FILE(HAL, SGPG); +BFA_MODULE(sgpg); + +/** + * bfa_sgpg_mod BFA SGPG Mode module + */ + +/** + * Compute and return memory needed by FCP(im) module. + */ +static void +bfa_sgpg_meminfo(struct bfa_iocfc_cfg_s *cfg, u32 *km_len, + u32 *dm_len) +{ + if (cfg->drvcfg.num_sgpgs < BFA_SGPG_MIN) + cfg->drvcfg.num_sgpgs = BFA_SGPG_MIN; + + *km_len += (cfg->drvcfg.num_sgpgs + 1) * sizeof(struct bfa_sgpg_s); + *dm_len += (cfg->drvcfg.num_sgpgs + 1) * sizeof(struct bfi_sgpg_s); +} + + +static void +bfa_sgpg_attach(struct bfa_s *bfa, void *bfad, struct bfa_iocfc_cfg_s *cfg, + struct bfa_meminfo_s *minfo, struct bfa_pcidev_s *pcidev) +{ + struct bfa_sgpg_mod_s *mod = BFA_SGPG_MOD(bfa); + int i; + struct bfa_sgpg_s *hsgpg; + struct bfi_sgpg_s *sgpg; + u64 align_len; + + union { + u64 pa; + union bfi_addr_u addr; + } sgpg_pa; + + INIT_LIST_HEAD(&mod->sgpg_q); + INIT_LIST_HEAD(&mod->sgpg_wait_q); + + bfa_trc(bfa, cfg->drvcfg.num_sgpgs); + + mod->num_sgpgs = cfg->drvcfg.num_sgpgs; + mod->sgpg_arr_pa = bfa_meminfo_dma_phys(minfo); + align_len = (BFA_SGPG_ROUNDUP(mod->sgpg_arr_pa) - mod->sgpg_arr_pa); + mod->sgpg_arr_pa += align_len; + mod->hsgpg_arr = (struct bfa_sgpg_s *) (bfa_meminfo_kva(minfo) + + align_len); + mod->sgpg_arr = (struct bfi_sgpg_s *) (bfa_meminfo_dma_virt(minfo) + + align_len); + + hsgpg = mod->hsgpg_arr; + sgpg = mod->sgpg_arr; + sgpg_pa.pa = mod->sgpg_arr_pa; + mod->free_sgpgs = mod->num_sgpgs; + + bfa_assert(!(sgpg_pa.pa & (sizeof(struct bfi_sgpg_s) - 1))); + + for (i = 0; i < mod->num_sgpgs; i++) { + bfa_os_memset(hsgpg, 0, sizeof(*hsgpg)); + bfa_os_memset(sgpg, 0, sizeof(*sgpg)); + + hsgpg->sgpg = sgpg; + hsgpg->sgpg_pa = sgpg_pa.addr; + list_add_tail(&hsgpg->qe, &mod->sgpg_q); + + hsgpg++; + sgpg++; + sgpg_pa.pa += sizeof(struct bfi_sgpg_s); + } + + bfa_meminfo_kva(minfo) = (u8 *) hsgpg; + bfa_meminfo_dma_virt(minfo) = (u8 *) sgpg; + bfa_meminfo_dma_phys(minfo) = sgpg_pa.pa; +} + +static void +bfa_sgpg_initdone(struct bfa_s *bfa) +{ +} + +static void +bfa_sgpg_detach(struct bfa_s *bfa) +{ +} + +static void +bfa_sgpg_start(struct bfa_s *bfa) +{ +} + +static void +bfa_sgpg_stop(struct bfa_s *bfa) +{ +} + +static void +bfa_sgpg_iocdisable(struct bfa_s *bfa) +{ +} + + + +/** + * bfa_sgpg_public BFA SGPG public functions + */ + +bfa_status_t +bfa_sgpg_malloc(struct bfa_s *bfa, struct list_head *sgpg_q, int nsgpgs) +{ + struct bfa_sgpg_mod_s *mod = BFA_SGPG_MOD(bfa); + struct bfa_sgpg_s *hsgpg; + int i; + + bfa_trc_fp(bfa, nsgpgs); + + if (mod->free_sgpgs < nsgpgs) + return BFA_STATUS_ENOMEM; + + for (i = 0; i < nsgpgs; i++) { + bfa_q_deq(&mod->sgpg_q, &hsgpg); + bfa_assert(hsgpg); + list_add_tail(&hsgpg->qe, sgpg_q); + } + + mod->free_sgpgs -= nsgpgs; + return BFA_STATUS_OK; +} + +void +bfa_sgpg_mfree(struct bfa_s *bfa, struct list_head *sgpg_q, int nsgpg) +{ + struct bfa_sgpg_mod_s *mod = BFA_SGPG_MOD(bfa); + struct bfa_sgpg_wqe_s *wqe; + + bfa_trc_fp(bfa, nsgpg); + + mod->free_sgpgs += nsgpg; + bfa_assert(mod->free_sgpgs <= mod->num_sgpgs); + + list_splice_tail_init(sgpg_q, &mod->sgpg_q); + + if (list_empty(&mod->sgpg_wait_q)) + return; + + /** + * satisfy as many waiting requests as possible + */ + do { + wqe = bfa_q_first(&mod->sgpg_wait_q); + if (mod->free_sgpgs < wqe->nsgpg) + nsgpg = mod->free_sgpgs; + else + nsgpg = wqe->nsgpg; + bfa_sgpg_malloc(bfa, &wqe->sgpg_q, nsgpg); + wqe->nsgpg -= nsgpg; + if (wqe->nsgpg == 0) { + list_del(&wqe->qe); + wqe->cbfn(wqe->cbarg); + } + } while (mod->free_sgpgs && !list_empty(&mod->sgpg_wait_q)); +} + +void +bfa_sgpg_wait(struct bfa_s *bfa, struct bfa_sgpg_wqe_s *wqe, int nsgpg) +{ + struct bfa_sgpg_mod_s *mod = BFA_SGPG_MOD(bfa); + + bfa_assert(nsgpg > 0); + bfa_assert(nsgpg > mod->free_sgpgs); + + wqe->nsgpg_total = wqe->nsgpg = nsgpg; + + /** + * allocate any left to this one first + */ + if (mod->free_sgpgs) { + /** + * no one else is waiting for SGPG + */ + bfa_assert(list_empty(&mod->sgpg_wait_q)); + list_splice_tail_init(&mod->sgpg_q, &wqe->sgpg_q); + wqe->nsgpg -= mod->free_sgpgs; + mod->free_sgpgs = 0; + } + + list_add_tail(&wqe->qe, &mod->sgpg_wait_q); +} + +void +bfa_sgpg_wcancel(struct bfa_s *bfa, struct bfa_sgpg_wqe_s *wqe) +{ + struct bfa_sgpg_mod_s *mod = BFA_SGPG_MOD(bfa); + + bfa_assert(bfa_q_is_on_q(&mod->sgpg_wait_q, wqe)); + list_del(&wqe->qe); + + if (wqe->nsgpg_total != wqe->nsgpg) + bfa_sgpg_mfree(bfa, &wqe->sgpg_q, + wqe->nsgpg_total - wqe->nsgpg); +} + +void +bfa_sgpg_winit(struct bfa_sgpg_wqe_s *wqe, void (*cbfn) (void *cbarg), + void *cbarg) +{ + INIT_LIST_HEAD(&wqe->sgpg_q); + wqe->cbfn = cbfn; + wqe->cbarg = cbarg; +} + + diff --git a/drivers/scsi/bfa/bfa_sgpg_priv.h b/drivers/scsi/bfa/bfa_sgpg_priv.h new file mode 100644 index 000000000000..9c2a8cbe7522 --- /dev/null +++ b/drivers/scsi/bfa/bfa_sgpg_priv.h @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2005-2009 Brocade Communications Systems, Inc. + * All rights reserved + * www.brocade.com + * + * Linux driver for Brocade Fibre Channel Host Bus Adapter. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (GPL) Version 2 as + * published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +/** + * hal_sgpg.h BFA SG page module + */ + +#ifndef __BFA_SGPG_PRIV_H__ +#define __BFA_SGPG_PRIV_H__ + +#include <cs/bfa_q.h> + +#define BFA_SGPG_MIN (16) + +/** + * Alignment macro for SG page allocation + */ +#define BFA_SGPG_ROUNDUP(_l) (((_l) + (sizeof(struct bfi_sgpg_s) - 1)) \ + & ~(sizeof(struct bfi_sgpg_s) - 1)) + +struct bfa_sgpg_wqe_s { + struct list_head qe; /* queue sg page element */ + int nsgpg; /* pages to be allocated */ + int nsgpg_total; /* total pages required */ + void (*cbfn) (void *cbarg); + /* callback function */ + void *cbarg; /* callback arg */ + struct list_head sgpg_q; /* queue of alloced sgpgs */ +}; + +struct bfa_sgpg_s { + struct list_head qe; /* queue sg page element */ + struct bfi_sgpg_s *sgpg; /* va of SG page */ + union bfi_addr_u sgpg_pa;/* pa of SG page */ +}; + +/** + * Given number of SG elements, BFA_SGPG_NPAGE() returns the number of + * SG pages required. + */ +#define BFA_SGPG_NPAGE(_nsges) (((_nsges) / BFI_SGPG_DATA_SGES) + 1) + +struct bfa_sgpg_mod_s { + struct bfa_s *bfa; + int num_sgpgs; /* number of SG pages */ + int free_sgpgs; /* number of free SG pages */ + struct bfa_sgpg_s *hsgpg_arr; /* BFA SG page array */ + struct bfi_sgpg_s *sgpg_arr; /* actual SG page array */ + u64 sgpg_arr_pa; /* SG page array DMA addr */ + struct list_head sgpg_q; /* queue of free SG pages */ + struct list_head sgpg_wait_q; /* wait queue for SG pages */ +}; +#define BFA_SGPG_MOD(__bfa) (&(__bfa)->modules.sgpg_mod) + +bfa_status_t bfa_sgpg_malloc(struct bfa_s *bfa, struct list_head *sgpg_q, + int nsgpgs); +void bfa_sgpg_mfree(struct bfa_s *bfa, struct list_head *sgpg_q, + int nsgpgs); +void bfa_sgpg_winit(struct bfa_sgpg_wqe_s *wqe, + void (*cbfn) (void *cbarg), void *cbarg); +void bfa_sgpg_wait(struct bfa_s *bfa, struct bfa_sgpg_wqe_s *wqe, + int nsgpgs); +void bfa_sgpg_wcancel(struct bfa_s *bfa, struct bfa_sgpg_wqe_s *wqe); + +#endif /* __BFA_SGPG_PRIV_H__ */ diff --git a/drivers/scsi/bfa/bfa_sm.c b/drivers/scsi/bfa/bfa_sm.c new file mode 100644 index 000000000000..5420f4f45e58 --- /dev/null +++ b/drivers/scsi/bfa/bfa_sm.c @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2005-2009 Brocade Communications Systems, Inc. + * All rights reserved + * www.brocade.com + * + * Linux driver for Brocade Fibre Channel Host Bus Adapter. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (GPL) Version 2 as + * published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +/** + * bfasm.c BFA State machine utility functions + */ + +#include <cs/bfa_sm.h> + +/** + * cs_sm_api + */ + +int +bfa_sm_to_state(struct bfa_sm_table_s *smt, bfa_sm_t sm) +{ + int i = 0; + + while (smt[i].sm && smt[i].sm != sm) + i++; + return smt[i].state; +} + + diff --git a/drivers/scsi/bfa/bfa_timer.c b/drivers/scsi/bfa/bfa_timer.c new file mode 100644 index 000000000000..cb76481f5cb1 --- /dev/null +++ b/drivers/scsi/bfa/bfa_timer.c @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2005-2009 Brocade Communications Systems, Inc. + * All rights reserved + * www.brocade.com + * + * Linux driver for Brocade Fibre Channel Host Bus Adapter. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (GPL) Version 2 as + * published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#include <bfa_timer.h> +#include <cs/bfa_debug.h> + +void +bfa_timer_init(struct bfa_timer_mod_s *mod) +{ + INIT_LIST_HEAD(&mod->timer_q); +} + +void +bfa_timer_beat(struct bfa_timer_mod_s *mod) +{ + struct list_head *qh = &mod->timer_q; + struct list_head *qe, *qe_next; + struct bfa_timer_s *elem; + struct list_head timedout_q; + + INIT_LIST_HEAD(&timedout_q); + + qe = bfa_q_next(qh); + + while (qe != qh) { + qe_next = bfa_q_next(qe); + + elem = (struct bfa_timer_s *) qe; + if (elem->timeout <= BFA_TIMER_FREQ) { + elem->timeout = 0; + list_del(&elem->qe); + list_add_tail(&elem->qe, &timedout_q); + } else { + elem->timeout -= BFA_TIMER_FREQ; + } + + qe = qe_next; /* go to next elem */ + } + + /* + * Pop all the timeout entries + */ + while (!list_empty(&timedout_q)) { + bfa_q_deq(&timedout_q, &elem); + elem->timercb(elem->arg); + } +} + +/** + * Should be called with lock protection + */ +void +bfa_timer_begin(struct bfa_timer_mod_s *mod, struct bfa_timer_s *timer, + void (*timercb) (void *), void *arg, unsigned int timeout) +{ + + bfa_assert(timercb != NULL); + bfa_assert(!bfa_q_is_on_q(&mod->timer_q, timer)); + + timer->timeout = timeout; + timer->timercb = timercb; + timer->arg = arg; + + list_add_tail(&timer->qe, &mod->timer_q); +} + +/** + * Should be called with lock protection + */ +void +bfa_timer_stop(struct bfa_timer_s *timer) +{ + bfa_assert(!list_empty(&timer->qe)); + + list_del(&timer->qe); +} diff --git a/drivers/scsi/bfa/bfa_trcmod_priv.h b/drivers/scsi/bfa/bfa_trcmod_priv.h new file mode 100644 index 000000000000..b3562dce7e9f --- /dev/null +++ b/drivers/scsi/bfa/bfa_trcmod_priv.h @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2005-2009 Brocade Communications Systems, Inc. + * All rights reserved + * www.brocade.com + * + * Linux driver for Brocade Fibre Channel Host Bus Adapter. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (GPL) Version 2 as + * published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +/** + * hal_trcmod.h BFA trace modules + */ + +#ifndef __BFA_TRCMOD_PRIV_H__ +#define __BFA_TRCMOD_PRIV_H__ + +#include <cs/bfa_trc.h> + +/* + * !!! Only append to the enums defined here to avoid any versioning + * !!! needed between trace utility and driver version + */ +enum { + BFA_TRC_HAL_IOC = 1, + BFA_TRC_HAL_INTR = 2, + BFA_TRC_HAL_FCXP = 3, + BFA_TRC_HAL_UF = 4, + BFA_TRC_HAL_DIAG = 5, + BFA_TRC_HAL_RPORT = 6, + BFA_TRC_HAL_FCPIM = 7, + BFA_TRC_HAL_IOIM = 8, + BFA_TRC_HAL_TSKIM = 9, + BFA_TRC_HAL_ITNIM = 10, + BFA_TRC_HAL_PPORT = 11, + BFA_TRC_HAL_SGPG = 12, + BFA_TRC_HAL_FLASH = 13, + BFA_TRC_HAL_DEBUG = 14, + BFA_TRC_HAL_WWN = 15, + BFA_TRC_HAL_FLASH_RAW = 16, + BFA_TRC_HAL_SBOOT = 17, + BFA_TRC_HAL_SBOOT_IO = 18, + BFA_TRC_HAL_SBOOT_INTR = 19, + BFA_TRC_HAL_SBTEST = 20, + BFA_TRC_HAL_IPFC = 21, + BFA_TRC_HAL_IOCFC = 22, + BFA_TRC_HAL_FCPTM = 23, + BFA_TRC_HAL_IOTM = 24, + BFA_TRC_HAL_TSKTM = 25, + BFA_TRC_HAL_TIN = 26, + BFA_TRC_HAL_LPS = 27, + BFA_TRC_HAL_FCDIAG = 28, + BFA_TRC_HAL_PBIND = 29, + BFA_TRC_HAL_IOCFC_CT = 30, + BFA_TRC_HAL_IOCFC_CB = 31, + BFA_TRC_HAL_IOCFC_Q = 32, +}; + +#endif /* __BFA_TRCMOD_PRIV_H__ */ diff --git a/drivers/scsi/bfa/bfa_tskim.c b/drivers/scsi/bfa/bfa_tskim.c new file mode 100644 index 000000000000..010d40d1e5d3 --- /dev/null +++ b/drivers/scsi/bfa/bfa_tskim.c @@ -0,0 +1,689 @@ +/* + * Copyright (c) 2005-2009 Brocade Communications Systems, Inc. + * All rights reserved + * www.brocade.com + * + * Linux driver for Brocade Fibre Channel Host Bus Adapter. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (GPL) Version 2 as + * published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#include <bfa.h> +#include <bfa_cb_ioim_macros.h> + +BFA_TRC_FILE(HAL, TSKIM); + +/** + * task management completion handling + */ +#define bfa_tskim_qcomp(__tskim, __cbfn) do { \ + bfa_cb_queue((__tskim)->bfa, &(__tskim)->hcb_qe, __cbfn, (__tskim)); \ + bfa_tskim_notify_comp(__tskim); \ +} while (0) + +#define bfa_tskim_notify_comp(__tskim) do { \ + if ((__tskim)->notify) \ + bfa_itnim_tskdone((__tskim)->itnim); \ +} while (0) + +/* + * forward declarations + */ +static void __bfa_cb_tskim_done(void *cbarg, bfa_boolean_t complete); +static void __bfa_cb_tskim_failed(void *cbarg, bfa_boolean_t complete); +static bfa_boolean_t bfa_tskim_match_scope(struct bfa_tskim_s *tskim, + lun_t lun); +static void bfa_tskim_gather_ios(struct bfa_tskim_s *tskim); +static void bfa_tskim_cleanp_comp(void *tskim_cbarg); +static void bfa_tskim_cleanup_ios(struct bfa_tskim_s *tskim); +static bfa_boolean_t bfa_tskim_send(struct bfa_tskim_s *tskim); +static bfa_boolean_t bfa_tskim_send_abort(struct bfa_tskim_s *tskim); +static void bfa_tskim_iocdisable_ios(struct bfa_tskim_s *tskim); + +/** + * bfa_tskim_sm + */ + +enum bfa_tskim_event { + BFA_TSKIM_SM_START = 1, /* TM command start */ + BFA_TSKIM_SM_DONE = 2, /* TM completion */ + BFA_TSKIM_SM_QRESUME = 3, /* resume after qfull */ + BFA_TSKIM_SM_HWFAIL = 5, /* IOC h/w failure event */ + BFA_TSKIM_SM_HCB = 6, /* BFA callback completion */ + BFA_TSKIM_SM_IOS_DONE = 7, /* IO and sub TM completions */ + BFA_TSKIM_SM_CLEANUP = 8, /* TM cleanup on ITN offline */ + BFA_TSKIM_SM_CLEANUP_DONE = 9, /* TM abort completion */ +}; + +static void bfa_tskim_sm_uninit(struct bfa_tskim_s *tskim, + enum bfa_tskim_event event); +static void bfa_tskim_sm_active(struct bfa_tskim_s *tskim, + enum bfa_tskim_event event); +static void bfa_tskim_sm_cleanup(struct bfa_tskim_s *tskim, + enum bfa_tskim_event event); +static void bfa_tskim_sm_iocleanup(struct bfa_tskim_s *tskim, + enum bfa_tskim_event event); +static void bfa_tskim_sm_qfull(struct bfa_tskim_s *tskim, + enum bfa_tskim_event event); +static void bfa_tskim_sm_cleanup_qfull(struct bfa_tskim_s *tskim, + enum bfa_tskim_event event); +static void bfa_tskim_sm_hcb(struct bfa_tskim_s *tskim, + enum bfa_tskim_event event); + +/** + * Task management command beginning state. + */ +static void +bfa_tskim_sm_uninit(struct bfa_tskim_s *tskim, enum bfa_tskim_event event) +{ + bfa_trc(tskim->bfa, event); + + switch (event) { + case BFA_TSKIM_SM_START: + bfa_sm_set_state(tskim, bfa_tskim_sm_active); + bfa_tskim_gather_ios(tskim); + + /** + * If device is offline, do not send TM on wire. Just cleanup + * any pending IO requests and complete TM request. + */ + if (!bfa_itnim_is_online(tskim->itnim)) { + bfa_sm_set_state(tskim, bfa_tskim_sm_iocleanup); + tskim->tsk_status = BFI_TSKIM_STS_OK; + bfa_tskim_cleanup_ios(tskim); + return; + } + + if (!bfa_tskim_send(tskim)) { + bfa_sm_set_state(tskim, bfa_tskim_sm_qfull); + bfa_reqq_wait(tskim->bfa, tskim->itnim->reqq, + &tskim->reqq_wait); + } + break; + + default: + bfa_assert(0); + } +} + +/** + * brief + * TM command is active, awaiting completion from firmware to + * cleanup IO requests in TM scope. + */ +static void +bfa_tskim_sm_active(struct bfa_tskim_s *tskim, enum bfa_tskim_event event) +{ + bfa_trc(tskim->bfa, event); + + switch (event) { + case BFA_TSKIM_SM_DONE: + bfa_sm_set_state(tskim, bfa_tskim_sm_iocleanup); + bfa_tskim_cleanup_ios(tskim); + break; + + case BFA_TSKIM_SM_CLEANUP: + bfa_sm_set_state(tskim, bfa_tskim_sm_cleanup); + if (!bfa_tskim_send_abort(tskim)) { + bfa_sm_set_state(tskim, bfa_tskim_sm_cleanup_qfull); + bfa_reqq_wait(tskim->bfa, tskim->itnim->reqq, + &tskim->reqq_wait); + } + break; + + case BFA_TSKIM_SM_HWFAIL: + bfa_sm_set_state(tskim, bfa_tskim_sm_hcb); + bfa_tskim_iocdisable_ios(tskim); + bfa_tskim_qcomp(tskim, __bfa_cb_tskim_failed); + break; + + default: + bfa_assert(0); + } +} + +/** + * An active TM is being cleaned up since ITN is offline. Awaiting cleanup + * completion event from firmware. + */ +static void +bfa_tskim_sm_cleanup(struct bfa_tskim_s *tskim, enum bfa_tskim_event event) +{ + bfa_trc(tskim->bfa, event); + + switch (event) { + case BFA_TSKIM_SM_DONE: + /** + * Ignore and wait for ABORT completion from firmware. + */ + break; + + case BFA_TSKIM_SM_CLEANUP_DONE: + bfa_sm_set_state(tskim, bfa_tskim_sm_iocleanup); + bfa_tskim_cleanup_ios(tskim); + break; + + case BFA_TSKIM_SM_HWFAIL: + bfa_sm_set_state(tskim, bfa_tskim_sm_hcb); + bfa_tskim_iocdisable_ios(tskim); + bfa_tskim_qcomp(tskim, __bfa_cb_tskim_failed); + break; + + default: + bfa_assert(0); + } +} + +static void +bfa_tskim_sm_iocleanup(struct bfa_tskim_s *tskim, enum bfa_tskim_event event) +{ + bfa_trc(tskim->bfa, event); + + switch (event) { + case BFA_TSKIM_SM_IOS_DONE: + bfa_sm_set_state(tskim, bfa_tskim_sm_hcb); + bfa_tskim_qcomp(tskim, __bfa_cb_tskim_done); + break; + + case BFA_TSKIM_SM_CLEANUP: + /** + * Ignore, TM command completed on wire. + * Notify TM conmpletion on IO cleanup completion. + */ + break; + + case BFA_TSKIM_SM_HWFAIL: + bfa_sm_set_state(tskim, bfa_tskim_sm_hcb); + bfa_tskim_iocdisable_ios(tskim); + bfa_tskim_qcomp(tskim, __bfa_cb_tskim_failed); + break; + + default: + bfa_assert(0); + } +} + +/** + * Task management command is waiting for room in request CQ + */ +static void +bfa_tskim_sm_qfull(struct bfa_tskim_s *tskim, enum bfa_tskim_event event) +{ + bfa_trc(tskim->bfa, event); + + switch (event) { + case BFA_TSKIM_SM_QRESUME: + bfa_sm_set_state(tskim, bfa_tskim_sm_active); + bfa_tskim_send(tskim); + break; + + case BFA_TSKIM_SM_CLEANUP: + /** + * No need to send TM on wire since ITN is offline. + */ + bfa_sm_set_state(tskim, bfa_tskim_sm_iocleanup); + bfa_reqq_wcancel(&tskim->reqq_wait); + bfa_tskim_cleanup_ios(tskim); + break; + + case BFA_TSKIM_SM_HWFAIL: + bfa_sm_set_state(tskim, bfa_tskim_sm_hcb); + bfa_reqq_wcancel(&tskim->reqq_wait); + bfa_tskim_iocdisable_ios(tskim); + bfa_tskim_qcomp(tskim, __bfa_cb_tskim_failed); + break; + + default: + bfa_assert(0); + } +} + +/** + * Task management command is active, awaiting for room in request CQ + * to send clean up request. + */ +static void +bfa_tskim_sm_cleanup_qfull(struct bfa_tskim_s *tskim, + enum bfa_tskim_event event) +{ + bfa_trc(tskim->bfa, event); + + switch (event) { + case BFA_TSKIM_SM_DONE: + bfa_reqq_wcancel(&tskim->reqq_wait); + /** + * + * Fall through !!! + */ + + case BFA_TSKIM_SM_QRESUME: + bfa_sm_set_state(tskim, bfa_tskim_sm_cleanup); + bfa_tskim_send_abort(tskim); + break; + + case BFA_TSKIM_SM_HWFAIL: + bfa_sm_set_state(tskim, bfa_tskim_sm_hcb); + bfa_reqq_wcancel(&tskim->reqq_wait); + bfa_tskim_iocdisable_ios(tskim); + bfa_tskim_qcomp(tskim, __bfa_cb_tskim_failed); + break; + + default: + bfa_assert(0); + } +} + +/** + * BFA callback is pending + */ +static void +bfa_tskim_sm_hcb(struct bfa_tskim_s *tskim, enum bfa_tskim_event event) +{ + bfa_trc(tskim->bfa, event); + + switch (event) { + case BFA_TSKIM_SM_HCB: + bfa_sm_set_state(tskim, bfa_tskim_sm_uninit); + bfa_tskim_free(tskim); + break; + + case BFA_TSKIM_SM_CLEANUP: + bfa_tskim_notify_comp(tskim); + break; + + case BFA_TSKIM_SM_HWFAIL: + break; + + default: + bfa_assert(0); + } +} + + + +/** + * bfa_tskim_private + */ + +static void +__bfa_cb_tskim_done(void *cbarg, bfa_boolean_t complete) +{ + struct bfa_tskim_s *tskim = cbarg; + + if (!complete) { + bfa_sm_send_event(tskim, BFA_TSKIM_SM_HCB); + return; + } + + bfa_stats(tskim->itnim, tm_success); + bfa_cb_tskim_done(tskim->bfa->bfad, tskim->dtsk, tskim->tsk_status); +} + +static void +__bfa_cb_tskim_failed(void *cbarg, bfa_boolean_t complete) +{ + struct bfa_tskim_s *tskim = cbarg; + + if (!complete) { + bfa_sm_send_event(tskim, BFA_TSKIM_SM_HCB); + return; + } + + bfa_stats(tskim->itnim, tm_failures); + bfa_cb_tskim_done(tskim->bfa->bfad, tskim->dtsk, + BFI_TSKIM_STS_FAILED); +} + +static bfa_boolean_t +bfa_tskim_match_scope(struct bfa_tskim_s *tskim, lun_t lun) +{ + switch (tskim->tm_cmnd) { + case FCP_TM_TARGET_RESET: + return BFA_TRUE; + + case FCP_TM_ABORT_TASK_SET: + case FCP_TM_CLEAR_TASK_SET: + case FCP_TM_LUN_RESET: + case FCP_TM_CLEAR_ACA: + return (tskim->lun == lun); + + default: + bfa_assert(0); + } + + return BFA_FALSE; +} + +/** + * Gather affected IO requests and task management commands. + */ +static void +bfa_tskim_gather_ios(struct bfa_tskim_s *tskim) +{ + struct bfa_itnim_s *itnim = tskim->itnim; + struct bfa_ioim_s *ioim; + struct list_head *qe, *qen; + + INIT_LIST_HEAD(&tskim->io_q); + + /** + * Gather any active IO requests first. + */ + list_for_each_safe(qe, qen, &itnim->io_q) { + ioim = (struct bfa_ioim_s *) qe; + if (bfa_tskim_match_scope + (tskim, bfa_cb_ioim_get_lun(ioim->dio))) { + list_del(&ioim->qe); + list_add_tail(&ioim->qe, &tskim->io_q); + } + } + + /** + * Failback any pending IO requests immediately. + */ + list_for_each_safe(qe, qen, &itnim->pending_q) { + ioim = (struct bfa_ioim_s *) qe; + if (bfa_tskim_match_scope + (tskim, bfa_cb_ioim_get_lun(ioim->dio))) { + list_del(&ioim->qe); + list_add_tail(&ioim->qe, &ioim->fcpim->ioim_comp_q); + bfa_ioim_tov(ioim); + } + } +} + +/** + * IO cleanup completion + */ +static void +bfa_tskim_cleanp_comp(void *tskim_cbarg) +{ + struct bfa_tskim_s *tskim = tskim_cbarg; + + bfa_stats(tskim->itnim, tm_io_comps); + bfa_sm_send_event(tskim, BFA_TSKIM_SM_IOS_DONE); +} + +/** + * Gather affected IO requests and task management commands. + */ +static void +bfa_tskim_cleanup_ios(struct bfa_tskim_s *tskim) +{ + struct bfa_ioim_s *ioim; + struct list_head *qe, *qen; + + bfa_wc_init(&tskim->wc, bfa_tskim_cleanp_comp, tskim); + + list_for_each_safe(qe, qen, &tskim->io_q) { + ioim = (struct bfa_ioim_s *) qe; + bfa_wc_up(&tskim->wc); + bfa_ioim_cleanup_tm(ioim, tskim); + } + + bfa_wc_wait(&tskim->wc); +} + +/** + * Send task management request to firmware. + */ +static bfa_boolean_t +bfa_tskim_send(struct bfa_tskim_s *tskim) +{ + struct bfa_itnim_s *itnim = tskim->itnim; + struct bfi_tskim_req_s *m; + + /** + * check for room in queue to send request now + */ + m = bfa_reqq_next(tskim->bfa, itnim->reqq); + if (!m) + return BFA_FALSE; + + /** + * build i/o request message next + */ + bfi_h2i_set(m->mh, BFI_MC_TSKIM, BFI_TSKIM_H2I_TM_REQ, + bfa_lpuid(tskim->bfa)); + + m->tsk_tag = bfa_os_htons(tskim->tsk_tag); + m->itn_fhdl = tskim->itnim->rport->fw_handle; + m->t_secs = tskim->tsecs; + m->lun = tskim->lun; + m->tm_flags = tskim->tm_cmnd; + + /** + * queue I/O message to firmware + */ + bfa_reqq_produce(tskim->bfa, itnim->reqq); + return BFA_TRUE; +} + +/** + * Send abort request to cleanup an active TM to firmware. + */ +static bfa_boolean_t +bfa_tskim_send_abort(struct bfa_tskim_s *tskim) +{ + struct bfa_itnim_s *itnim = tskim->itnim; + struct bfi_tskim_abortreq_s *m; + + /** + * check for room in queue to send request now + */ + m = bfa_reqq_next(tskim->bfa, itnim->reqq); + if (!m) + return BFA_FALSE; + + /** + * build i/o request message next + */ + bfi_h2i_set(m->mh, BFI_MC_TSKIM, BFI_TSKIM_H2I_ABORT_REQ, + bfa_lpuid(tskim->bfa)); + + m->tsk_tag = bfa_os_htons(tskim->tsk_tag); + + /** + * queue I/O message to firmware + */ + bfa_reqq_produce(tskim->bfa, itnim->reqq); + return BFA_TRUE; +} + +/** + * Call to resume task management cmnd waiting for room in request queue. + */ +static void +bfa_tskim_qresume(void *cbarg) +{ + struct bfa_tskim_s *tskim = cbarg; + + bfa_fcpim_stats(tskim->fcpim, qresumes); + bfa_stats(tskim->itnim, tm_qresumes); + bfa_sm_send_event(tskim, BFA_TSKIM_SM_QRESUME); +} + +/** + * Cleanup IOs associated with a task mangement command on IOC failures. + */ +static void +bfa_tskim_iocdisable_ios(struct bfa_tskim_s *tskim) +{ + struct bfa_ioim_s *ioim; + struct list_head *qe, *qen; + + list_for_each_safe(qe, qen, &tskim->io_q) { + ioim = (struct bfa_ioim_s *) qe; + bfa_ioim_iocdisable(ioim); + } +} + + + +/** + * bfa_tskim_friend + */ + +/** + * Notification on completions from related ioim. + */ +void +bfa_tskim_iodone(struct bfa_tskim_s *tskim) +{ + bfa_wc_down(&tskim->wc); +} + +/** + * Handle IOC h/w failure notification from itnim. + */ +void +bfa_tskim_iocdisable(struct bfa_tskim_s *tskim) +{ + tskim->notify = BFA_FALSE; + bfa_stats(tskim->itnim, tm_iocdowns); + bfa_sm_send_event(tskim, BFA_TSKIM_SM_HWFAIL); +} + +/** + * Cleanup TM command and associated IOs as part of ITNIM offline. + */ +void +bfa_tskim_cleanup(struct bfa_tskim_s *tskim) +{ + tskim->notify = BFA_TRUE; + bfa_stats(tskim->itnim, tm_cleanups); + bfa_sm_send_event(tskim, BFA_TSKIM_SM_CLEANUP); +} + +/** + * Memory allocation and initialization. + */ +void +bfa_tskim_attach(struct bfa_fcpim_mod_s *fcpim, struct bfa_meminfo_s *minfo) +{ + struct bfa_tskim_s *tskim; + u16 i; + + INIT_LIST_HEAD(&fcpim->tskim_free_q); + + tskim = (struct bfa_tskim_s *) bfa_meminfo_kva(minfo); + fcpim->tskim_arr = tskim; + + for (i = 0; i < fcpim->num_tskim_reqs; i++, tskim++) { + /* + * initialize TSKIM + */ + bfa_os_memset(tskim, 0, sizeof(struct bfa_tskim_s)); + tskim->tsk_tag = i; + tskim->bfa = fcpim->bfa; + tskim->fcpim = fcpim; + tskim->notify = BFA_FALSE; + bfa_reqq_winit(&tskim->reqq_wait, bfa_tskim_qresume, + tskim); + bfa_sm_set_state(tskim, bfa_tskim_sm_uninit); + + list_add_tail(&tskim->qe, &fcpim->tskim_free_q); + } + + bfa_meminfo_kva(minfo) = (u8 *) tskim; +} + +void +bfa_tskim_detach(struct bfa_fcpim_mod_s *fcpim) +{ + /** + * @todo + */ +} + +void +bfa_tskim_isr(struct bfa_s *bfa, struct bfi_msg_s *m) +{ + struct bfa_fcpim_mod_s *fcpim = BFA_FCPIM_MOD(bfa); + struct bfi_tskim_rsp_s *rsp = (struct bfi_tskim_rsp_s *) m; + struct bfa_tskim_s *tskim; + u16 tsk_tag = bfa_os_ntohs(rsp->tsk_tag); + + tskim = BFA_TSKIM_FROM_TAG(fcpim, tsk_tag); + bfa_assert(tskim->tsk_tag == tsk_tag); + + tskim->tsk_status = rsp->tsk_status; + + /** + * Firmware sends BFI_TSKIM_STS_ABORTED status for abort + * requests. All other statuses are for normal completions. + */ + if (rsp->tsk_status == BFI_TSKIM_STS_ABORTED) { + bfa_stats(tskim->itnim, tm_cleanup_comps); + bfa_sm_send_event(tskim, BFA_TSKIM_SM_CLEANUP_DONE); + } else { + bfa_stats(tskim->itnim, tm_fw_rsps); + bfa_sm_send_event(tskim, BFA_TSKIM_SM_DONE); + } +} + + + +/** + * bfa_tskim_api + */ + + +struct bfa_tskim_s * +bfa_tskim_alloc(struct bfa_s *bfa, struct bfad_tskim_s *dtsk) +{ + struct bfa_fcpim_mod_s *fcpim = BFA_FCPIM_MOD(bfa); + struct bfa_tskim_s *tskim; + + bfa_q_deq(&fcpim->tskim_free_q, &tskim); + + if (!tskim) + bfa_fcpim_stats(fcpim, no_tskims); + else + tskim->dtsk = dtsk; + + return tskim; +} + +void +bfa_tskim_free(struct bfa_tskim_s *tskim) +{ + bfa_assert(bfa_q_is_on_q_func(&tskim->itnim->tsk_q, &tskim->qe)); + list_del(&tskim->qe); + list_add_tail(&tskim->qe, &tskim->fcpim->tskim_free_q); +} + +/** + * Start a task management command. + * + * @param[in] tskim BFA task management command instance + * @param[in] itnim i-t nexus for the task management command + * @param[in] lun lun, if applicable + * @param[in] tm_cmnd Task management command code. + * @param[in] t_secs Timeout in seconds + * + * @return None. + */ +void +bfa_tskim_start(struct bfa_tskim_s *tskim, struct bfa_itnim_s *itnim, lun_t lun, + enum fcp_tm_cmnd tm_cmnd, u8 tsecs) +{ + tskim->itnim = itnim; + tskim->lun = lun; + tskim->tm_cmnd = tm_cmnd; + tskim->tsecs = tsecs; + tskim->notify = BFA_FALSE; + bfa_stats(itnim, tm_cmnds); + + list_add_tail(&tskim->qe, &itnim->tsk_q); + bfa_sm_send_event(tskim, BFA_TSKIM_SM_START); +} + + diff --git a/drivers/scsi/bfa/bfa_uf.c b/drivers/scsi/bfa/bfa_uf.c new file mode 100644 index 000000000000..ff5f9deb1b22 --- /dev/null +++ b/drivers/scsi/bfa/bfa_uf.c @@ -0,0 +1,345 @@ +/* + * Copyright (c) 2005-2009 Brocade Communications Systems, Inc. + * All rights reserved + * www.brocade.com + * + * Linux driver for Brocade Fibre Channel Host Bus Adapter. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (GPL) Version 2 as + * published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +/** + * bfa_uf.c BFA unsolicited frame receive implementation + */ + +#include <bfa.h> +#include <bfa_svc.h> +#include <bfi/bfi_uf.h> +#include <cs/bfa_debug.h> + +BFA_TRC_FILE(HAL, UF); +BFA_MODULE(uf); + +/* + ***************************************************************************** + * Internal functions + ***************************************************************************** + */ +static void +__bfa_cb_uf_recv(void *cbarg, bfa_boolean_t complete) +{ + struct bfa_uf_s *uf = cbarg; + struct bfa_uf_mod_s *ufm = BFA_UF_MOD(uf->bfa); + + if (complete) + ufm->ufrecv(ufm->cbarg, uf); +} + +static void +claim_uf_pbs(struct bfa_uf_mod_s *ufm, struct bfa_meminfo_s *mi) +{ + u32 uf_pb_tot_sz; + + ufm->uf_pbs_kva = (struct bfa_uf_buf_s *) bfa_meminfo_dma_virt(mi); + ufm->uf_pbs_pa = bfa_meminfo_dma_phys(mi); + uf_pb_tot_sz = BFA_ROUNDUP((sizeof(struct bfa_uf_buf_s) * ufm->num_ufs), + BFA_DMA_ALIGN_SZ); + + bfa_meminfo_dma_virt(mi) += uf_pb_tot_sz; + bfa_meminfo_dma_phys(mi) += uf_pb_tot_sz; + + bfa_os_memset((void *)ufm->uf_pbs_kva, 0, uf_pb_tot_sz); +} + +static void +claim_uf_post_msgs(struct bfa_uf_mod_s *ufm, struct bfa_meminfo_s *mi) +{ + struct bfi_uf_buf_post_s *uf_bp_msg; + struct bfi_sge_s *sge; + union bfi_addr_u sga_zero = { {0} }; + u16 i; + u16 buf_len; + + ufm->uf_buf_posts = (struct bfi_uf_buf_post_s *) bfa_meminfo_kva(mi); + uf_bp_msg = ufm->uf_buf_posts; + + for (i = 0, uf_bp_msg = ufm->uf_buf_posts; i < ufm->num_ufs; + i++, uf_bp_msg++) { + bfa_os_memset(uf_bp_msg, 0, sizeof(struct bfi_uf_buf_post_s)); + + uf_bp_msg->buf_tag = i; + buf_len = sizeof(struct bfa_uf_buf_s); + uf_bp_msg->buf_len = bfa_os_htons(buf_len); + bfi_h2i_set(uf_bp_msg->mh, BFI_MC_UF, BFI_UF_H2I_BUF_POST, + bfa_lpuid(ufm->bfa)); + + sge = uf_bp_msg->sge; + sge[0].sg_len = buf_len; + sge[0].flags = BFI_SGE_DATA_LAST; + bfa_dma_addr_set(sge[0].sga, ufm_pbs_pa(ufm, i)); + bfa_sge_to_be(sge); + + sge[1].sg_len = buf_len; + sge[1].flags = BFI_SGE_PGDLEN; + sge[1].sga = sga_zero; + bfa_sge_to_be(&sge[1]); + } + + /** + * advance pointer beyond consumed memory + */ + bfa_meminfo_kva(mi) = (u8 *) uf_bp_msg; +} + +static void +claim_ufs(struct bfa_uf_mod_s *ufm, struct bfa_meminfo_s *mi) +{ + u16 i; + struct bfa_uf_s *uf; + + /* + * Claim block of memory for UF list + */ + ufm->uf_list = (struct bfa_uf_s *) bfa_meminfo_kva(mi); + + /* + * Initialize UFs and queue it in UF free queue + */ + for (i = 0, uf = ufm->uf_list; i < ufm->num_ufs; i++, uf++) { + bfa_os_memset(uf, 0, sizeof(struct bfa_uf_s)); + uf->bfa = ufm->bfa; + uf->uf_tag = i; + uf->pb_len = sizeof(struct bfa_uf_buf_s); + uf->buf_kva = (void *)&ufm->uf_pbs_kva[i]; + uf->buf_pa = ufm_pbs_pa(ufm, i); + list_add_tail(&uf->qe, &ufm->uf_free_q); + } + + /** + * advance memory pointer + */ + bfa_meminfo_kva(mi) = (u8 *) uf; +} + +static void +uf_mem_claim(struct bfa_uf_mod_s *ufm, struct bfa_meminfo_s *mi) +{ + claim_uf_pbs(ufm, mi); + claim_ufs(ufm, mi); + claim_uf_post_msgs(ufm, mi); +} + +static void +bfa_uf_meminfo(struct bfa_iocfc_cfg_s *cfg, u32 *ndm_len, u32 *dm_len) +{ + u32 num_ufs = cfg->fwcfg.num_uf_bufs; + + /* + * dma-able memory for UF posted bufs + */ + *dm_len += BFA_ROUNDUP((sizeof(struct bfa_uf_buf_s) * num_ufs), + BFA_DMA_ALIGN_SZ); + + /* + * kernel Virtual memory for UFs and UF buf post msg copies + */ + *ndm_len += sizeof(struct bfa_uf_s) * num_ufs; + *ndm_len += sizeof(struct bfi_uf_buf_post_s) * num_ufs; +} + +static void +bfa_uf_attach(struct bfa_s *bfa, void *bfad, struct bfa_iocfc_cfg_s *cfg, + struct bfa_meminfo_s *meminfo, struct bfa_pcidev_s *pcidev) +{ + struct bfa_uf_mod_s *ufm = BFA_UF_MOD(bfa); + + bfa_os_memset(ufm, 0, sizeof(struct bfa_uf_mod_s)); + ufm->bfa = bfa; + ufm->num_ufs = cfg->fwcfg.num_uf_bufs; + INIT_LIST_HEAD(&ufm->uf_free_q); + INIT_LIST_HEAD(&ufm->uf_posted_q); + + uf_mem_claim(ufm, meminfo); +} + +static void +bfa_uf_initdone(struct bfa_s *bfa) +{ +} + +static void +bfa_uf_detach(struct bfa_s *bfa) +{ +} + +static struct bfa_uf_s * +bfa_uf_get(struct bfa_uf_mod_s *uf_mod) +{ + struct bfa_uf_s *uf; + + bfa_q_deq(&uf_mod->uf_free_q, &uf); + return (uf); +} + +static void +bfa_uf_put(struct bfa_uf_mod_s *uf_mod, struct bfa_uf_s *uf) +{ + list_add_tail(&uf->qe, &uf_mod->uf_free_q); +} + +static bfa_status_t +bfa_uf_post(struct bfa_uf_mod_s *ufm, struct bfa_uf_s *uf) +{ + struct bfi_uf_buf_post_s *uf_post_msg; + + uf_post_msg = bfa_reqq_next(ufm->bfa, BFA_REQQ_FCXP); + if (!uf_post_msg) + return BFA_STATUS_FAILED; + + bfa_os_memcpy(uf_post_msg, &ufm->uf_buf_posts[uf->uf_tag], + sizeof(struct bfi_uf_buf_post_s)); + bfa_reqq_produce(ufm->bfa, BFA_REQQ_FCXP); + + bfa_trc(ufm->bfa, uf->uf_tag); + + list_add_tail(&uf->qe, &ufm->uf_posted_q); + return BFA_STATUS_OK; +} + +static void +bfa_uf_post_all(struct bfa_uf_mod_s *uf_mod) +{ + struct bfa_uf_s *uf; + + while ((uf = bfa_uf_get(uf_mod)) != NULL) { + if (bfa_uf_post(uf_mod, uf) != BFA_STATUS_OK) + break; + } +} + +static void +uf_recv(struct bfa_s *bfa, struct bfi_uf_frm_rcvd_s *m) +{ + struct bfa_uf_mod_s *ufm = BFA_UF_MOD(bfa); + u16 uf_tag = m->buf_tag; + struct bfa_uf_buf_s *uf_buf = &ufm->uf_pbs_kva[uf_tag]; + struct bfa_uf_s *uf = &ufm->uf_list[uf_tag]; + u8 *buf = &uf_buf->d[0]; + struct fchs_s *fchs; + + m->frm_len = bfa_os_ntohs(m->frm_len); + m->xfr_len = bfa_os_ntohs(m->xfr_len); + + fchs = (struct fchs_s *) uf_buf; + + list_del(&uf->qe); /* dequeue from posted queue */ + + uf->data_ptr = buf; + uf->data_len = m->xfr_len; + + bfa_assert(uf->data_len >= sizeof(struct fchs_s)); + + if (uf->data_len == sizeof(struct fchs_s)) { + bfa_plog_fchdr(bfa->plog, BFA_PL_MID_HAL_UF, BFA_PL_EID_RX, + uf->data_len, (struct fchs_s *) buf); + } else { + u32 pld_w0 = *((u32 *) (buf + sizeof(struct fchs_s))); + bfa_plog_fchdr_and_pl(bfa->plog, BFA_PL_MID_HAL_UF, + BFA_PL_EID_RX, uf->data_len, + (struct fchs_s *) buf, pld_w0); + } + + bfa_cb_queue(bfa, &uf->hcb_qe, __bfa_cb_uf_recv, uf); +} + +static void +bfa_uf_stop(struct bfa_s *bfa) +{ +} + +static void +bfa_uf_iocdisable(struct bfa_s *bfa) +{ + struct bfa_uf_mod_s *ufm = BFA_UF_MOD(bfa); + struct bfa_uf_s *uf; + struct list_head *qe, *qen; + + list_for_each_safe(qe, qen, &ufm->uf_posted_q) { + uf = (struct bfa_uf_s *) qe; + list_del(&uf->qe); + bfa_uf_put(ufm, uf); + } +} + +static void +bfa_uf_start(struct bfa_s *bfa) +{ + bfa_uf_post_all(BFA_UF_MOD(bfa)); +} + + + +/** + * bfa_uf_api + */ + +/** + * Register handler for all unsolicted recieve frames. + * + * @param[in] bfa BFA instance + * @param[in] ufrecv receive handler function + * @param[in] cbarg receive handler arg + */ +void +bfa_uf_recv_register(struct bfa_s *bfa, bfa_cb_uf_recv_t ufrecv, void *cbarg) +{ + struct bfa_uf_mod_s *ufm = BFA_UF_MOD(bfa); + + ufm->ufrecv = ufrecv; + ufm->cbarg = cbarg; +} + +/** + * Free an unsolicited frame back to BFA. + * + * @param[in] uf unsolicited frame to be freed + * + * @return None + */ +void +bfa_uf_free(struct bfa_uf_s *uf) +{ + bfa_uf_put(BFA_UF_MOD(uf->bfa), uf); + bfa_uf_post_all(BFA_UF_MOD(uf->bfa)); +} + + + +/** + * uf_pub BFA uf module public functions + */ + +void +bfa_uf_isr(struct bfa_s *bfa, struct bfi_msg_s *msg) +{ + bfa_trc(bfa, msg->mhdr.msg_id); + + switch (msg->mhdr.msg_id) { + case BFI_UF_I2H_FRM_RCVD: + uf_recv(bfa, (struct bfi_uf_frm_rcvd_s *) msg); + break; + + default: + bfa_trc(bfa, msg->mhdr.msg_id); + bfa_assert(0); + } +} + + diff --git a/drivers/scsi/bfa/bfa_uf_priv.h b/drivers/scsi/bfa/bfa_uf_priv.h new file mode 100644 index 000000000000..bcb490f834f3 --- /dev/null +++ b/drivers/scsi/bfa/bfa_uf_priv.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2005-2009 Brocade Communications Systems, Inc. + * All rights reserved + * www.brocade.com + * + * Linux driver for Brocade Fibre Channel Host Bus Adapter. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (GPL) Version 2 as + * published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ +#ifndef __BFA_UF_PRIV_H__ +#define __BFA_UF_PRIV_H__ + +#include <cs/bfa_sm.h> +#include <bfa_svc.h> +#include <bfi/bfi_uf.h> + +#define BFA_UF_MIN (4) + +struct bfa_uf_mod_s { + struct bfa_s *bfa; /* back pointer to BFA */ + struct bfa_uf_s *uf_list; /* array of UFs */ + u16 num_ufs; /* num unsolicited rx frames */ + struct list_head uf_free_q; /* free UFs */ + struct list_head uf_posted_q; /* UFs posted to IOC */ + struct bfa_uf_buf_s *uf_pbs_kva; /* list UF bufs request pld */ + u64 uf_pbs_pa; /* phy addr for UF bufs */ + struct bfi_uf_buf_post_s *uf_buf_posts; + /* pre-built UF post msgs */ + bfa_cb_uf_recv_t ufrecv; /* uf recv handler function */ + void *cbarg; /* uf receive handler arg */ +}; + +#define BFA_UF_MOD(__bfa) (&(__bfa)->modules.uf_mod) + +#define ufm_pbs_pa(_ufmod, _uftag) \ + ((_ufmod)->uf_pbs_pa + sizeof(struct bfa_uf_buf_s) * (_uftag)) + +void bfa_uf_isr(struct bfa_s *bfa, struct bfi_msg_s *msg); + +#endif /* __BFA_UF_PRIV_H__ */ diff --git a/drivers/scsi/bfa/bfad.c b/drivers/scsi/bfa/bfad.c new file mode 100644 index 000000000000..6f2be5abf561 --- /dev/null +++ b/drivers/scsi/bfa/bfad.c @@ -0,0 +1,1182 @@ +/* + * Copyright (c) 2005-2009 Brocade Communications Systems, Inc. + * All rights reserved + * www.brocade.com + * + * Linux driver for Brocade Fibre Channel Host Bus Adapter. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (GPL) Version 2 as + * published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +/** + * bfad.c Linux driver PCI interface module. + */ + +#include <linux/module.h> +#include "bfad_drv.h" +#include "bfad_im.h" +#include "bfad_tm.h" +#include "bfad_ipfc.h" +#include "bfad_trcmod.h" +#include <fcb/bfa_fcb_vf.h> +#include <fcb/bfa_fcb_rport.h> +#include <fcb/bfa_fcb_port.h> +#include <fcb/bfa_fcb.h> + +BFA_TRC_FILE(LDRV, BFAD); +static DEFINE_MUTEX(bfad_mutex); +LIST_HEAD(bfad_list); +static int bfad_inst; +int bfad_supported_fc4s; + +static char *host_name; +static char *os_name; +static char *os_patch; +static int num_rports; +static int num_ios; +static int num_tms; +static int num_fcxps; +static int num_ufbufs; +static int reqq_size; +static int rspq_size; +static int num_sgpgs; +static int rport_del_timeout = BFA_FCS_RPORT_DEF_DEL_TIMEOUT; +static int bfa_io_max_sge = BFAD_IO_MAX_SGE; +static int log_level = BFA_LOG_WARNING; +static int ioc_auto_recover = BFA_TRUE; +static int ipfc_enable = BFA_FALSE; +static int ipfc_mtu = -1; +int bfa_lun_queue_depth = BFAD_LUN_QUEUE_DEPTH; +int bfa_linkup_delay = -1; + +module_param(os_name, charp, S_IRUGO | S_IWUSR); +module_param(os_patch, charp, S_IRUGO | S_IWUSR); +module_param(host_name, charp, S_IRUGO | S_IWUSR); +module_param(num_rports, int, S_IRUGO | S_IWUSR); +module_param(num_ios, int, S_IRUGO | S_IWUSR); +module_param(num_tms, int, S_IRUGO | S_IWUSR); +module_param(num_fcxps, int, S_IRUGO | S_IWUSR); +module_param(num_ufbufs, int, S_IRUGO | S_IWUSR); +module_param(reqq_size, int, S_IRUGO | S_IWUSR); +module_param(rspq_size, int, S_IRUGO | S_IWUSR); +module_param(num_sgpgs, int, S_IRUGO | S_IWUSR); +module_param(rport_del_timeout, int, S_IRUGO | S_IWUSR); +module_param(bfa_lun_queue_depth, int, S_IRUGO | S_IWUSR); +module_param(bfa_io_max_sge, int, S_IRUGO | S_IWUSR); +module_param(log_level, int, S_IRUGO | S_IWUSR); +module_param(ioc_auto_recover, int, S_IRUGO | S_IWUSR); +module_param(ipfc_enable, int, S_IRUGO | S_IWUSR); +module_param(ipfc_mtu, int, S_IRUGO | S_IWUSR); +module_param(bfa_linkup_delay, int, S_IRUGO | S_IWUSR); + +/* + * Stores the module parm num_sgpgs value; + * used to reset for bfad next instance. + */ +static int num_sgpgs_parm; + +static bfa_status_t +bfad_fc4_probe(struct bfad_s *bfad) +{ + int rc; + + rc = bfad_im_probe(bfad); + if (rc != BFA_STATUS_OK) + goto ext; + + bfad_tm_probe(bfad); + + if (ipfc_enable) + bfad_ipfc_probe(bfad); +ext: + return rc; +} + +static void +bfad_fc4_probe_undo(struct bfad_s *bfad) +{ + bfad_im_probe_undo(bfad); + bfad_tm_probe_undo(bfad); + if (ipfc_enable) + bfad_ipfc_probe_undo(bfad); +} + +static void +bfad_fc4_probe_post(struct bfad_s *bfad) +{ + if (bfad->im) + bfad_im_probe_post(bfad->im); + + bfad_tm_probe_post(bfad); + if (ipfc_enable) + bfad_ipfc_probe_post(bfad); +} + +static bfa_status_t +bfad_fc4_port_new(struct bfad_s *bfad, struct bfad_port_s *port, int roles) +{ + int rc = BFA_STATUS_FAILED; + + if (roles & BFA_PORT_ROLE_FCP_IM) + rc = bfad_im_port_new(bfad, port); + if (rc != BFA_STATUS_OK) + goto ext; + + if (roles & BFA_PORT_ROLE_FCP_TM) + rc = bfad_tm_port_new(bfad, port); + if (rc != BFA_STATUS_OK) + goto ext; + + if ((roles & BFA_PORT_ROLE_FCP_IPFC) && ipfc_enable) + rc = bfad_ipfc_port_new(bfad, port, port->pvb_type); +ext: + return rc; +} + +static void +bfad_fc4_port_delete(struct bfad_s *bfad, struct bfad_port_s *port, int roles) +{ + if (roles & BFA_PORT_ROLE_FCP_IM) + bfad_im_port_delete(bfad, port); + + if (roles & BFA_PORT_ROLE_FCP_TM) + bfad_tm_port_delete(bfad, port); + + if ((roles & BFA_PORT_ROLE_FCP_IPFC) && ipfc_enable) + bfad_ipfc_port_delete(bfad, port); +} + +/** + * BFA callbacks + */ +void +bfad_hcb_comp(void *arg, bfa_status_t status) +{ + struct bfad_hal_comp *fcomp = (struct bfad_hal_comp *)arg; + + fcomp->status = status; + complete(&fcomp->comp); +} + +/** + * bfa_init callback + */ +void +bfa_cb_init(void *drv, bfa_status_t init_status) +{ + struct bfad_s *bfad = drv; + + if (init_status == BFA_STATUS_OK) + bfad->bfad_flags |= BFAD_HAL_INIT_DONE; + + complete(&bfad->comp); +} + + + +/** + * BFA_FCS callbacks + */ +static struct bfad_port_s * +bfad_get_drv_port(struct bfad_s *bfad, struct bfad_vf_s *vf_drv, + struct bfad_vport_s *vp_drv) +{ + return ((vp_drv) ? (&(vp_drv)->drv_port) + : ((vf_drv) ? (&(vf_drv)->base_port) : (&(bfad)->pport))); +} + +struct bfad_port_s * +bfa_fcb_port_new(struct bfad_s *bfad, struct bfa_fcs_port_s *port, + enum bfa_port_role roles, struct bfad_vf_s *vf_drv, + struct bfad_vport_s *vp_drv) +{ + bfa_status_t rc; + struct bfad_port_s *port_drv; + + if (!vp_drv && !vf_drv) { + port_drv = &bfad->pport; + port_drv->pvb_type = BFAD_PORT_PHYS_BASE; + } else if (!vp_drv && vf_drv) { + port_drv = &vf_drv->base_port; + port_drv->pvb_type = BFAD_PORT_VF_BASE; + } else if (vp_drv && !vf_drv) { + port_drv = &vp_drv->drv_port; + port_drv->pvb_type = BFAD_PORT_PHYS_VPORT; + } else { + port_drv = &vp_drv->drv_port; + port_drv->pvb_type = BFAD_PORT_VF_VPORT; + } + + port_drv->fcs_port = port; + port_drv->roles = roles; + rc = bfad_fc4_port_new(bfad, port_drv, roles); + if (rc != BFA_STATUS_OK) { + bfad_fc4_port_delete(bfad, port_drv, roles); + port_drv = NULL; + } + + return port_drv; +} + +void +bfa_fcb_port_delete(struct bfad_s *bfad, enum bfa_port_role roles, + struct bfad_vf_s *vf_drv, struct bfad_vport_s *vp_drv) +{ + struct bfad_port_s *port_drv; + + /* + * this will be only called from rmmod context + */ + if (vp_drv && !vp_drv->comp_del) { + port_drv = bfad_get_drv_port(bfad, vf_drv, vp_drv); + bfa_trc(bfad, roles); + bfad_fc4_port_delete(bfad, port_drv, roles); + } +} + +void +bfa_fcb_port_online(struct bfad_s *bfad, enum bfa_port_role roles, + struct bfad_vf_s *vf_drv, struct bfad_vport_s *vp_drv) +{ + struct bfad_port_s *port_drv = bfad_get_drv_port(bfad, vf_drv, vp_drv); + + if (roles & BFA_PORT_ROLE_FCP_IM) + bfad_im_port_online(bfad, port_drv); + + if (roles & BFA_PORT_ROLE_FCP_TM) + bfad_tm_port_online(bfad, port_drv); + + if ((roles & BFA_PORT_ROLE_FCP_IPFC) && ipfc_enable) + bfad_ipfc_port_online(bfad, port_drv); + + bfad->bfad_flags |= BFAD_PORT_ONLINE; +} + +void +bfa_fcb_port_offline(struct bfad_s *bfad, enum bfa_port_role roles, + struct bfad_vf_s *vf_drv, struct bfad_vport_s *vp_drv) +{ + struct bfad_port_s *port_drv = bfad_get_drv_port(bfad, vf_drv, vp_drv); + + if (roles & BFA_PORT_ROLE_FCP_IM) + bfad_im_port_offline(bfad, port_drv); + + if (roles & BFA_PORT_ROLE_FCP_TM) + bfad_tm_port_offline(bfad, port_drv); + + if ((roles & BFA_PORT_ROLE_FCP_IPFC) && ipfc_enable) + bfad_ipfc_port_offline(bfad, port_drv); +} + +void +bfa_fcb_vport_delete(struct bfad_vport_s *vport_drv) +{ + if (vport_drv->comp_del) { + complete(vport_drv->comp_del); + return; + } + + kfree(vport_drv); +} + +/** + * FCS RPORT alloc callback, after successful PLOGI by FCS + */ +bfa_status_t +bfa_fcb_rport_alloc(struct bfad_s *bfad, struct bfa_fcs_rport_s **rport, + struct bfad_rport_s **rport_drv) +{ + bfa_status_t rc = BFA_STATUS_OK; + + *rport_drv = kzalloc(sizeof(struct bfad_rport_s), GFP_ATOMIC); + if (*rport_drv == NULL) { + rc = BFA_STATUS_ENOMEM; + goto ext; + } + + *rport = &(*rport_drv)->fcs_rport; + +ext: + return rc; +} + + + +void +bfad_hal_mem_release(struct bfad_s *bfad) +{ + int i; + struct bfa_meminfo_s *hal_meminfo = &bfad->meminfo; + struct bfa_mem_elem_s *meminfo_elem; + + for (i = 0; i < BFA_MEM_TYPE_MAX; i++) { + meminfo_elem = &hal_meminfo->meminfo[i]; + if (meminfo_elem->kva != NULL) { + switch (meminfo_elem->mem_type) { + case BFA_MEM_TYPE_KVA: + vfree(meminfo_elem->kva); + break; + case BFA_MEM_TYPE_DMA: + dma_free_coherent(&bfad->pcidev->dev, + meminfo_elem->mem_len, + meminfo_elem->kva, + (dma_addr_t) meminfo_elem->dma); + break; + default: + bfa_assert(0); + break; + } + } + } + + memset(hal_meminfo, 0, sizeof(struct bfa_meminfo_s)); +} + +void +bfad_update_hal_cfg(struct bfa_iocfc_cfg_s *bfa_cfg) +{ + if (num_rports > 0) + bfa_cfg->fwcfg.num_rports = num_rports; + if (num_ios > 0) + bfa_cfg->fwcfg.num_ioim_reqs = num_ios; + if (num_tms > 0) + bfa_cfg->fwcfg.num_tskim_reqs = num_tms; + if (num_fcxps > 0) + bfa_cfg->fwcfg.num_fcxp_reqs = num_fcxps; + if (num_ufbufs > 0) + bfa_cfg->fwcfg.num_uf_bufs = num_ufbufs; + if (reqq_size > 0) + bfa_cfg->drvcfg.num_reqq_elems = reqq_size; + if (rspq_size > 0) + bfa_cfg->drvcfg.num_rspq_elems = rspq_size; + if (num_sgpgs > 0) + bfa_cfg->drvcfg.num_sgpgs = num_sgpgs; + + /* + * populate the hal values back to the driver for sysfs use. + * otherwise, the default values will be shown as 0 in sysfs + */ + num_rports = bfa_cfg->fwcfg.num_rports; + num_ios = bfa_cfg->fwcfg.num_ioim_reqs; + num_tms = bfa_cfg->fwcfg.num_tskim_reqs; + num_fcxps = bfa_cfg->fwcfg.num_fcxp_reqs; + num_ufbufs = bfa_cfg->fwcfg.num_uf_bufs; + reqq_size = bfa_cfg->drvcfg.num_reqq_elems; + rspq_size = bfa_cfg->drvcfg.num_rspq_elems; + num_sgpgs = bfa_cfg->drvcfg.num_sgpgs; +} + +bfa_status_t +bfad_hal_mem_alloc(struct bfad_s *bfad) +{ + struct bfa_meminfo_s *hal_meminfo = &bfad->meminfo; + struct bfa_mem_elem_s *meminfo_elem; + bfa_status_t rc = BFA_STATUS_OK; + dma_addr_t phys_addr; + int retry_count = 0; + int reset_value = 1; + int min_num_sgpgs = 512; + void *kva; + int i; + + bfa_cfg_get_default(&bfad->ioc_cfg); + +retry: + bfad_update_hal_cfg(&bfad->ioc_cfg); + bfad->cfg_data.ioc_queue_depth = bfad->ioc_cfg.fwcfg.num_ioim_reqs; + bfa_cfg_get_meminfo(&bfad->ioc_cfg, hal_meminfo); + + for (i = 0; i < BFA_MEM_TYPE_MAX; i++) { + meminfo_elem = &hal_meminfo->meminfo[i]; + switch (meminfo_elem->mem_type) { + case BFA_MEM_TYPE_KVA: + kva = vmalloc(meminfo_elem->mem_len); + if (kva == NULL) { + bfad_hal_mem_release(bfad); + rc = BFA_STATUS_ENOMEM; + goto ext; + } + memset(kva, 0, meminfo_elem->mem_len); + meminfo_elem->kva = kva; + break; + case BFA_MEM_TYPE_DMA: + kva = dma_alloc_coherent(&bfad->pcidev->dev, + meminfo_elem->mem_len, + &phys_addr, GFP_KERNEL); + if (kva == NULL) { + bfad_hal_mem_release(bfad); + /* + * If we cannot allocate with default + * num_sgpages try with half the value. + */ + if (num_sgpgs > min_num_sgpgs) { + printk(KERN_INFO "bfad[%d]: memory" + " allocation failed with" + " num_sgpgs: %d\n", + bfad->inst_no, num_sgpgs); + nextLowerInt(&num_sgpgs); + printk(KERN_INFO "bfad[%d]: trying to" + " allocate memory with" + " num_sgpgs: %d\n", + bfad->inst_no, num_sgpgs); + retry_count++; + goto retry; + } else { + if (num_sgpgs_parm > 0) + num_sgpgs = num_sgpgs_parm; + else { + reset_value = + (1 << retry_count); + num_sgpgs *= reset_value; + } + rc = BFA_STATUS_ENOMEM; + goto ext; + } + } + + if (num_sgpgs_parm > 0) + num_sgpgs = num_sgpgs_parm; + else { + reset_value = (1 << retry_count); + num_sgpgs *= reset_value; + } + + memset(kva, 0, meminfo_elem->mem_len); + meminfo_elem->kva = kva; + meminfo_elem->dma = phys_addr; + break; + default: + break; + + } + } +ext: + return rc; +} + +/** + * Create a vport under a vf. + */ +bfa_status_t +bfad_vport_create(struct bfad_s *bfad, u16 vf_id, + struct bfa_port_cfg_s *port_cfg) +{ + struct bfad_vport_s *vport; + int rc = BFA_STATUS_OK; + unsigned long flags; + struct completion fcomp; + + vport = kzalloc(sizeof(struct bfad_vport_s), GFP_KERNEL); + if (!vport) { + rc = BFA_STATUS_ENOMEM; + goto ext; + } + + vport->drv_port.bfad = bfad; + spin_lock_irqsave(&bfad->bfad_lock, flags); + rc = bfa_fcs_vport_create(&vport->fcs_vport, &bfad->bfa_fcs, vf_id, + port_cfg, vport); + spin_unlock_irqrestore(&bfad->bfad_lock, flags); + + if (rc != BFA_STATUS_OK) + goto ext_free_vport; + + if (port_cfg->roles & BFA_PORT_ROLE_FCP_IM) { + rc = bfad_im_scsi_host_alloc(bfad, vport->drv_port.im_port); + if (rc != BFA_STATUS_OK) + goto ext_free_fcs_vport; + } + + spin_lock_irqsave(&bfad->bfad_lock, flags); + bfa_fcs_vport_start(&vport->fcs_vport); + spin_unlock_irqrestore(&bfad->bfad_lock, flags); + + return BFA_STATUS_OK; + +ext_free_fcs_vport: + spin_lock_irqsave(&bfad->bfad_lock, flags); + vport->comp_del = &fcomp; + init_completion(vport->comp_del); + bfa_fcs_vport_delete(&vport->fcs_vport); + spin_unlock_irqrestore(&bfad->bfad_lock, flags); + wait_for_completion(vport->comp_del); +ext_free_vport: + kfree(vport); +ext: + return rc; +} + +/** + * Create a vf and its base vport implicitely. + */ +bfa_status_t +bfad_vf_create(struct bfad_s *bfad, u16 vf_id, + struct bfa_port_cfg_s *port_cfg) +{ + struct bfad_vf_s *vf; + int rc = BFA_STATUS_OK; + + vf = kzalloc(sizeof(struct bfad_vf_s), GFP_KERNEL); + if (!vf) { + rc = BFA_STATUS_FAILED; + goto ext; + } + + rc = bfa_fcs_vf_create(&vf->fcs_vf, &bfad->bfa_fcs, vf_id, port_cfg, + vf); + if (rc != BFA_STATUS_OK) + kfree(vf); +ext: + return rc; +} + +void +bfad_bfa_tmo(unsigned long data) +{ + struct bfad_s *bfad = (struct bfad_s *)data; + unsigned long flags; + struct list_head doneq; + + spin_lock_irqsave(&bfad->bfad_lock, flags); + + bfa_timer_tick(&bfad->bfa); + + bfa_comp_deq(&bfad->bfa, &doneq); + spin_unlock_irqrestore(&bfad->bfad_lock, flags); + + if (!list_empty(&doneq)) { + bfa_comp_process(&bfad->bfa, &doneq); + spin_lock_irqsave(&bfad->bfad_lock, flags); + bfa_comp_free(&bfad->bfa, &doneq); + spin_unlock_irqrestore(&bfad->bfad_lock, flags); + } + + mod_timer(&bfad->hal_tmo, jiffies + msecs_to_jiffies(BFA_TIMER_FREQ)); +} + +void +bfad_init_timer(struct bfad_s *bfad) +{ + init_timer(&bfad->hal_tmo); + bfad->hal_tmo.function = bfad_bfa_tmo; + bfad->hal_tmo.data = (unsigned long)bfad; + + mod_timer(&bfad->hal_tmo, jiffies + msecs_to_jiffies(BFA_TIMER_FREQ)); +} + +int +bfad_pci_init(struct pci_dev *pdev, struct bfad_s *bfad) +{ + unsigned long bar0_len; + int rc = -ENODEV; + + if (pci_enable_device(pdev)) { + BFA_PRINTF(BFA_ERR, "pci_enable_device fail %p\n", pdev); + goto out; + } + + if (pci_request_regions(pdev, BFAD_DRIVER_NAME)) + goto out_disable_device; + + pci_set_master(pdev); + + + if (pci_set_dma_mask(pdev, DMA_BIT_MASK(64)) != 0) + if (pci_set_dma_mask(pdev, DMA_BIT_MASK(32)) != 0) { + BFA_PRINTF(BFA_ERR, "pci_set_dma_mask fail %p\n", pdev); + goto out_release_region; + } + + bfad->pci_bar0_map = pci_resource_start(pdev, 0); + bar0_len = pci_resource_len(pdev, 0); + bfad->pci_bar0_kva = ioremap(bfad->pci_bar0_map, bar0_len); + + if (bfad->pci_bar0_kva == NULL) { + BFA_PRINTF(BFA_ERR, "Fail to map bar0\n"); + goto out_release_region; + } + + bfad->hal_pcidev.pci_slot = PCI_SLOT(pdev->devfn); + bfad->hal_pcidev.pci_func = PCI_FUNC(pdev->devfn); + bfad->hal_pcidev.pci_bar_kva = bfad->pci_bar0_kva; + bfad->hal_pcidev.device_id = pdev->device; + bfad->pci_name = pci_name(pdev); + + bfad->pci_attr.vendor_id = pdev->vendor; + bfad->pci_attr.device_id = pdev->device; + bfad->pci_attr.ssid = pdev->subsystem_device; + bfad->pci_attr.ssvid = pdev->subsystem_vendor; + bfad->pci_attr.pcifn = PCI_FUNC(pdev->devfn); + + bfad->pcidev = pdev; + return 0; + +out_release_region: + pci_release_regions(pdev); +out_disable_device: + pci_disable_device(pdev); +out: + return rc; +} + +void +bfad_pci_uninit(struct pci_dev *pdev, struct bfad_s *bfad) +{ +#if defined(__ia64__) + pci_iounmap(pdev, bfad->pci_bar0_kva); +#else + iounmap(bfad->pci_bar0_kva); +#endif + pci_release_regions(pdev); + pci_disable_device(pdev); + pci_set_drvdata(pdev, NULL); +} + +void +bfad_fcs_port_cfg(struct bfad_s *bfad) +{ + struct bfa_port_cfg_s port_cfg; + struct bfa_pport_attr_s attr; + char symname[BFA_SYMNAME_MAXLEN]; + + sprintf(symname, "%s-%d", BFAD_DRIVER_NAME, bfad->inst_no); + memcpy(port_cfg.sym_name.symname, symname, strlen(symname)); + bfa_pport_get_attr(&bfad->bfa, &attr); + port_cfg.nwwn = attr.nwwn; + port_cfg.pwwn = attr.pwwn; + + bfa_fcs_cfg_base_port(&bfad->bfa_fcs, &port_cfg); +} + +bfa_status_t +bfad_drv_init(struct bfad_s *bfad) +{ + bfa_status_t rc; + unsigned long flags; + struct bfa_fcs_driver_info_s driver_info; + int i; + + bfad->cfg_data.rport_del_timeout = rport_del_timeout; + bfad->cfg_data.lun_queue_depth = bfa_lun_queue_depth; + bfad->cfg_data.io_max_sge = bfa_io_max_sge; + bfad->cfg_data.binding_method = FCP_PWWN_BINDING; + + rc = bfad_hal_mem_alloc(bfad); + if (rc != BFA_STATUS_OK) { + printk(KERN_WARNING "bfad%d bfad_hal_mem_alloc failure\n", + bfad->inst_no); + printk(KERN_WARNING + "Not enough memory to attach all Brocade HBA ports," + " System may need more memory.\n"); + goto out_hal_mem_alloc_failure; + } + + bfa_init_log(&bfad->bfa, bfad->logmod); + bfa_init_trc(&bfad->bfa, bfad->trcmod); + bfa_init_aen(&bfad->bfa, bfad->aen); + INIT_LIST_HEAD(&bfad->file_q); + INIT_LIST_HEAD(&bfad->file_free_q); + for (i = 0; i < BFAD_AEN_MAX_APPS; i++) { + bfa_q_qe_init(&bfad->file_buf[i].qe); + list_add_tail(&bfad->file_buf[i].qe, &bfad->file_free_q); + } + bfa_init_plog(&bfad->bfa, &bfad->plog_buf); + bfa_plog_init(&bfad->plog_buf); + bfa_plog_str(&bfad->plog_buf, BFA_PL_MID_DRVR, BFA_PL_EID_DRIVER_START, + 0, "Driver Attach"); + + bfa_attach(&bfad->bfa, bfad, &bfad->ioc_cfg, &bfad->meminfo, + &bfad->hal_pcidev); + + init_completion(&bfad->comp); + + /* + * Enable Interrupt and wait bfa_init completion + */ + if (bfad_setup_intr(bfad)) { + printk(KERN_WARNING "bfad%d: bfad_setup_intr failed\n", + bfad->inst_no); + goto out_setup_intr_failure; + } + + spin_lock_irqsave(&bfad->bfad_lock, flags); + bfa_init(&bfad->bfa); + spin_unlock_irqrestore(&bfad->bfad_lock, flags); + + /* + * Set up interrupt handler for each vectors + */ + if ((bfad->bfad_flags & BFAD_MSIX_ON) + && bfad_install_msix_handler(bfad)) { + printk(KERN_WARNING "%s: install_msix failed, bfad%d\n", + __FUNCTION__, bfad->inst_no); + } + + bfad_init_timer(bfad); + + wait_for_completion(&bfad->comp); + + memset(&driver_info, 0, sizeof(driver_info)); + strncpy(driver_info.version, BFAD_DRIVER_VERSION, + sizeof(driver_info.version) - 1); + if (host_name) + strncpy(driver_info.host_machine_name, host_name, + sizeof(driver_info.host_machine_name) - 1); + if (os_name) + strncpy(driver_info.host_os_name, os_name, + sizeof(driver_info.host_os_name) - 1); + if (os_patch) + strncpy(driver_info.host_os_patch, os_patch, + sizeof(driver_info.host_os_patch) - 1); + + strncpy(driver_info.os_device_name, bfad->pci_name, + sizeof(driver_info.os_device_name - 1)); + + /* + * FCS INIT + */ + spin_lock_irqsave(&bfad->bfad_lock, flags); + bfa_fcs_log_init(&bfad->bfa_fcs, bfad->logmod); + bfa_fcs_trc_init(&bfad->bfa_fcs, bfad->trcmod); + bfa_fcs_aen_init(&bfad->bfa_fcs, bfad->aen); + bfa_fcs_init(&bfad->bfa_fcs, &bfad->bfa, bfad, BFA_FALSE); + bfa_fcs_driver_info_init(&bfad->bfa_fcs, &driver_info); + spin_unlock_irqrestore(&bfad->bfad_lock, flags); + + bfad->bfad_flags |= BFAD_DRV_INIT_DONE; + return BFA_STATUS_OK; + +out_setup_intr_failure: + bfa_detach(&bfad->bfa); + bfad_hal_mem_release(bfad); +out_hal_mem_alloc_failure: + return BFA_STATUS_FAILED; +} + +void +bfad_drv_uninit(struct bfad_s *bfad) +{ + del_timer_sync(&bfad->hal_tmo); + bfa_isr_disable(&bfad->bfa); + bfa_detach(&bfad->bfa); + bfad_remove_intr(bfad); + bfa_assert(list_empty(&bfad->file_q)); + bfad_hal_mem_release(bfad); +} + +void +bfad_drv_start(struct bfad_s *bfad) +{ + unsigned long flags; + + spin_lock_irqsave(&bfad->bfad_lock, flags); + bfa_start(&bfad->bfa); + bfa_fcs_start(&bfad->bfa_fcs); + bfad->bfad_flags |= BFAD_HAL_START_DONE; + spin_unlock_irqrestore(&bfad->bfad_lock, flags); + + bfad_fc4_probe_post(bfad); +} + +void +bfad_drv_stop(struct bfad_s *bfad) +{ + unsigned long flags; + + spin_lock_irqsave(&bfad->bfad_lock, flags); + init_completion(&bfad->comp); + bfad->pport.flags |= BFAD_PORT_DELETE; + bfa_fcs_exit(&bfad->bfa_fcs); + spin_unlock_irqrestore(&bfad->bfad_lock, flags); + wait_for_completion(&bfad->comp); + + spin_lock_irqsave(&bfad->bfad_lock, flags); + init_completion(&bfad->comp); + bfa_stop(&bfad->bfa); + bfad->bfad_flags &= ~BFAD_HAL_START_DONE; + spin_unlock_irqrestore(&bfad->bfad_lock, flags); + wait_for_completion(&bfad->comp); +} + +bfa_status_t +bfad_cfg_pport(struct bfad_s *bfad, enum bfa_port_role role) +{ + int rc = BFA_STATUS_OK; + + /* + * Allocate scsi_host for the physical port + */ + if ((bfad_supported_fc4s & BFA_PORT_ROLE_FCP_IM) + && (role & BFA_PORT_ROLE_FCP_IM)) { + if (bfad->pport.im_port == NULL) { + rc = BFA_STATUS_FAILED; + goto out; + } + + rc = bfad_im_scsi_host_alloc(bfad, bfad->pport.im_port); + if (rc != BFA_STATUS_OK) + goto out; + + bfad->pport.roles |= BFA_PORT_ROLE_FCP_IM; + } + + bfad->bfad_flags |= BFAD_CFG_PPORT_DONE; + +out: + return rc; +} + +void +bfad_uncfg_pport(struct bfad_s *bfad) +{ + if ((bfad->pport.roles & BFA_PORT_ROLE_FCP_IPFC) && ipfc_enable) { + bfad_ipfc_port_delete(bfad, &bfad->pport); + bfad->pport.roles &= ~BFA_PORT_ROLE_FCP_IPFC; + } + + if ((bfad_supported_fc4s & BFA_PORT_ROLE_FCP_IM) + && (bfad->pport.roles & BFA_PORT_ROLE_FCP_IM)) { + bfad_im_scsi_host_free(bfad, bfad->pport.im_port); + bfad_im_port_clean(bfad->pport.im_port); + kfree(bfad->pport.im_port); + bfad->pport.roles &= ~BFA_PORT_ROLE_FCP_IM; + } + + bfad->bfad_flags &= ~BFAD_CFG_PPORT_DONE; +} + +void +bfad_drv_log_level_set(struct bfad_s *bfad) +{ + if (log_level > BFA_LOG_INVALID && log_level <= BFA_LOG_LEVEL_MAX) + bfa_log_set_level_all(&bfad->log_data, log_level); +} + + /* + * PCI_entry PCI driver entries * { + */ + +/** + * PCI probe entry. + */ +int +bfad_pci_probe(struct pci_dev *pdev, const struct pci_device_id *pid) +{ + struct bfad_s *bfad; + int error = -ENODEV, retval; + char buf[16]; + + /* + * For single port cards - only claim function 0 + */ + if ((pdev->device == BFA_PCI_DEVICE_ID_FC_8G1P) + && (PCI_FUNC(pdev->devfn) != 0)) + return -ENODEV; + + BFA_TRACE(BFA_INFO, "bfad_pci_probe entry"); + + bfad = kzalloc(sizeof(struct bfad_s), GFP_KERNEL); + if (!bfad) { + error = -ENOMEM; + goto out; + } + + bfad->trcmod = kzalloc(sizeof(struct bfa_trc_mod_s), GFP_KERNEL); + if (!bfad->trcmod) { + printk(KERN_WARNING "Error alloc trace buffer!\n"); + error = -ENOMEM; + goto out_alloc_trace_failure; + } + + /* + * LOG/TRACE INIT + */ + bfa_trc_init(bfad->trcmod); + bfa_trc(bfad, bfad_inst); + + bfad->logmod = &bfad->log_data; + sprintf(buf, "%d", bfad_inst); + bfa_log_init(bfad->logmod, buf, bfa_os_printf); + + bfad_drv_log_level_set(bfad); + + bfad->aen = &bfad->aen_buf; + + if (!(bfad_load_fwimg(pdev))) { + printk(KERN_WARNING "bfad_load_fwimg failure!\n"); + kfree(bfad->trcmod); + goto out_alloc_trace_failure; + } + + retval = bfad_pci_init(pdev, bfad); + if (retval) { + printk(KERN_WARNING "bfad_pci_init failure!\n"); + error = retval; + goto out_pci_init_failure; + } + + mutex_lock(&bfad_mutex); + bfad->inst_no = bfad_inst++; + list_add_tail(&bfad->list_entry, &bfad_list); + mutex_unlock(&bfad_mutex); + + spin_lock_init(&bfad->bfad_lock); + pci_set_drvdata(pdev, bfad); + + bfad->ref_count = 0; + bfad->pport.bfad = bfad; + + retval = bfad_drv_init(bfad); + if (retval != BFA_STATUS_OK) + goto out_drv_init_failure; + if (!(bfad->bfad_flags & BFAD_HAL_INIT_DONE)) { + printk(KERN_WARNING "bfad%d: hal init failed\n", bfad->inst_no); + goto ok; + } + + /* + * PPORT FCS config + */ + bfad_fcs_port_cfg(bfad); + + retval = bfad_cfg_pport(bfad, BFA_PORT_ROLE_FCP_IM); + if (retval != BFA_STATUS_OK) + goto out_cfg_pport_failure; + + /* + * BFAD level FC4 (IM/TM/IPFC) specific resource allocation + */ + retval = bfad_fc4_probe(bfad); + if (retval != BFA_STATUS_OK) { + printk(KERN_WARNING "bfad_fc4_probe failed\n"); + goto out_fc4_probe_failure; + } + + bfad_drv_start(bfad); + + /* + * If bfa_linkup_delay is set to -1 default; try to retrive the + * value using the bfad_os_get_linkup_delay(); else use the + * passed in module param value as the bfa_linkup_delay. + */ + if (bfa_linkup_delay < 0) { + bfa_linkup_delay = bfad_os_get_linkup_delay(bfad); + bfad_os_rport_online_wait(bfad); + bfa_linkup_delay = -1; + } else { + bfad_os_rport_online_wait(bfad); + } + + bfa_log(bfad->logmod, BFA_LOG_LINUX_DEVICE_CLAIMED, bfad->pci_name); +ok: + return 0; + +out_fc4_probe_failure: + bfad_fc4_probe_undo(bfad); + bfad_uncfg_pport(bfad); +out_cfg_pport_failure: + bfad_drv_uninit(bfad); +out_drv_init_failure: + mutex_lock(&bfad_mutex); + bfad_inst--; + list_del(&bfad->list_entry); + mutex_unlock(&bfad_mutex); + bfad_pci_uninit(pdev, bfad); +out_pci_init_failure: + kfree(bfad->trcmod); +out_alloc_trace_failure: + kfree(bfad); +out: + return error; +} + +/** + * PCI remove entry. + */ +void +bfad_pci_remove(struct pci_dev *pdev) +{ + struct bfad_s *bfad = pci_get_drvdata(pdev); + unsigned long flags; + + bfa_trc(bfad, bfad->inst_no); + + if ((bfad->bfad_flags & BFAD_DRV_INIT_DONE) + && !(bfad->bfad_flags & BFAD_HAL_INIT_DONE)) { + + spin_lock_irqsave(&bfad->bfad_lock, flags); + init_completion(&bfad->comp); + bfa_stop(&bfad->bfa); + spin_unlock_irqrestore(&bfad->bfad_lock, flags); + wait_for_completion(&bfad->comp); + + bfad_remove_intr(bfad); + del_timer_sync(&bfad->hal_tmo); + goto hal_detach; + } else if (!(bfad->bfad_flags & BFAD_DRV_INIT_DONE)) { + goto remove_sysfs; + } + + if (bfad->bfad_flags & BFAD_HAL_START_DONE) + bfad_drv_stop(bfad); + + bfad_remove_intr(bfad); + + del_timer_sync(&bfad->hal_tmo); + bfad_fc4_probe_undo(bfad); + + if (bfad->bfad_flags & BFAD_CFG_PPORT_DONE) + bfad_uncfg_pport(bfad); + +hal_detach: + spin_lock_irqsave(&bfad->bfad_lock, flags); + bfa_detach(&bfad->bfa); + spin_unlock_irqrestore(&bfad->bfad_lock, flags); + bfad_hal_mem_release(bfad); +remove_sysfs: + + mutex_lock(&bfad_mutex); + bfad_inst--; + list_del(&bfad->list_entry); + mutex_unlock(&bfad_mutex); + bfad_pci_uninit(pdev, bfad); + + kfree(bfad->trcmod); + kfree(bfad); +} + + +static struct pci_device_id bfad_id_table[] = { + { + .vendor = BFA_PCI_VENDOR_ID_BROCADE, + .device = BFA_PCI_DEVICE_ID_FC_8G2P, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + }, + { + .vendor = BFA_PCI_VENDOR_ID_BROCADE, + .device = BFA_PCI_DEVICE_ID_FC_8G1P, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + }, + { + .vendor = BFA_PCI_VENDOR_ID_BROCADE, + .device = BFA_PCI_DEVICE_ID_CT, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .class = (PCI_CLASS_SERIAL_FIBER << 8), + .class_mask = ~0, + }, + + {0, 0}, +}; + +MODULE_DEVICE_TABLE(pci, bfad_id_table); + +static struct pci_driver bfad_pci_driver = { + .name = BFAD_DRIVER_NAME, + .id_table = bfad_id_table, + .probe = bfad_pci_probe, + .remove = __devexit_p(bfad_pci_remove), +}; + +/** + * Linux driver module functions + */ +bfa_status_t +bfad_fc4_module_init(void) +{ + int rc; + + rc = bfad_im_module_init(); + if (rc != BFA_STATUS_OK) + goto ext; + + bfad_tm_module_init(); + if (ipfc_enable) + bfad_ipfc_module_init(); +ext: + return rc; +} + +void +bfad_fc4_module_exit(void) +{ + if (ipfc_enable) + bfad_ipfc_module_exit(); + bfad_tm_module_exit(); + bfad_im_module_exit(); +} + +/** + * Driver module init. + */ +static int __init +bfad_init(void) +{ + int error = 0; + + printk(KERN_INFO "Brocade BFA FC/FCOE SCSI driver - version: %s\n", + BFAD_DRIVER_VERSION); + + if (num_sgpgs > 0) + num_sgpgs_parm = num_sgpgs; + + error = bfad_fc4_module_init(); + if (error) { + error = -ENOMEM; + printk(KERN_WARNING "bfad_fc4_module_init failure\n"); + goto ext; + } + + if (!strcmp(FCPI_NAME, " fcpim")) + bfad_supported_fc4s |= BFA_PORT_ROLE_FCP_IM; + if (!strcmp(FCPT_NAME, " fcptm")) + bfad_supported_fc4s |= BFA_PORT_ROLE_FCP_TM; + if (!strcmp(IPFC_NAME, " ipfc")) + bfad_supported_fc4s |= BFA_PORT_ROLE_FCP_IPFC; + + bfa_ioc_auto_recover(ioc_auto_recover); + bfa_fcs_rport_set_del_timeout(rport_del_timeout); + error = pci_register_driver(&bfad_pci_driver); + + if (error) { + printk(KERN_WARNING "bfad pci_register_driver failure\n"); + goto ext; + } + + return 0; + +ext: + bfad_fc4_module_exit(); + return error; +} + +/** + * Driver module exit. + */ +static void __exit +bfad_exit(void) +{ + pci_unregister_driver(&bfad_pci_driver); + bfad_fc4_module_exit(); + bfad_free_fwimg(); +} + +#define BFAD_PROTO_NAME FCPI_NAME FCPT_NAME IPFC_NAME + +module_init(bfad_init); +module_exit(bfad_exit); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Brocade Fibre Channel HBA Driver" BFAD_PROTO_NAME); +MODULE_AUTHOR("Brocade Communications Systems, Inc."); +MODULE_VERSION(BFAD_DRIVER_VERSION); + + diff --git a/drivers/scsi/bfa/bfad_attr.c b/drivers/scsi/bfa/bfad_attr.c new file mode 100644 index 000000000000..9129ae3040ff --- /dev/null +++ b/drivers/scsi/bfa/bfad_attr.c @@ -0,0 +1,649 @@ +/* + * Copyright (c) 2005-2009 Brocade Communications Systems, Inc. + * All rights reserved + * www.brocade.com + * + * Linux driver for Brocade Fibre Channel Host Bus Adapter. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (GPL) Version 2 as + * published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +/** + * bfa_attr.c Linux driver configuration interface module. + */ + +#include "bfad_drv.h" +#include "bfad_im.h" +#include "bfad_trcmod.h" +#include "bfad_attr.h" + +/** + * FC_transport_template FC transport template + */ + +/** + * FC transport template entry, get SCSI target port ID. + */ +void +bfad_im_get_starget_port_id(struct scsi_target *starget) +{ + struct Scsi_Host *shost; + struct bfad_im_port_s *im_port; + struct bfad_s *bfad; + struct bfad_itnim_s *itnim = NULL; + u32 fc_id = -1; + unsigned long flags; + + shost = bfad_os_starget_to_shost(starget); + im_port = (struct bfad_im_port_s *) shost->hostdata[0]; + bfad = im_port->bfad; + spin_lock_irqsave(&bfad->bfad_lock, flags); + + itnim = bfad_os_get_itnim(im_port, starget->id); + if (itnim) + fc_id = bfa_fcs_itnim_get_fcid(&itnim->fcs_itnim); + + fc_starget_port_id(starget) = fc_id; + spin_unlock_irqrestore(&bfad->bfad_lock, flags); +} + +/** + * FC transport template entry, get SCSI target nwwn. + */ +void +bfad_im_get_starget_node_name(struct scsi_target *starget) +{ + struct Scsi_Host *shost; + struct bfad_im_port_s *im_port; + struct bfad_s *bfad; + struct bfad_itnim_s *itnim = NULL; + u64 node_name = 0; + unsigned long flags; + + shost = bfad_os_starget_to_shost(starget); + im_port = (struct bfad_im_port_s *) shost->hostdata[0]; + bfad = im_port->bfad; + spin_lock_irqsave(&bfad->bfad_lock, flags); + + itnim = bfad_os_get_itnim(im_port, starget->id); + if (itnim) + node_name = bfa_fcs_itnim_get_nwwn(&itnim->fcs_itnim); + + fc_starget_node_name(starget) = bfa_os_htonll(node_name); + spin_unlock_irqrestore(&bfad->bfad_lock, flags); +} + +/** + * FC transport template entry, get SCSI target pwwn. + */ +void +bfad_im_get_starget_port_name(struct scsi_target *starget) +{ + struct Scsi_Host *shost; + struct bfad_im_port_s *im_port; + struct bfad_s *bfad; + struct bfad_itnim_s *itnim = NULL; + u64 port_name = 0; + unsigned long flags; + + shost = bfad_os_starget_to_shost(starget); + im_port = (struct bfad_im_port_s *) shost->hostdata[0]; + bfad = im_port->bfad; + spin_lock_irqsave(&bfad->bfad_lock, flags); + + itnim = bfad_os_get_itnim(im_port, starget->id); + if (itnim) + port_name = bfa_fcs_itnim_get_pwwn(&itnim->fcs_itnim); + + fc_starget_port_name(starget) = bfa_os_htonll(port_name); + spin_unlock_irqrestore(&bfad->bfad_lock, flags); +} + +/** + * FC transport template entry, get SCSI host port ID. + */ +void +bfad_im_get_host_port_id(struct Scsi_Host *shost) +{ + struct bfad_im_port_s *im_port = + (struct bfad_im_port_s *) shost->hostdata[0]; + struct bfad_port_s *port = im_port->port; + + fc_host_port_id(shost) = + bfa_os_hton3b(bfa_fcs_port_get_fcid(port->fcs_port)); +} + + + + + +struct Scsi_Host * +bfad_os_starget_to_shost(struct scsi_target *starget) +{ + return dev_to_shost(starget->dev.parent); +} + +/** + * FC transport template entry, get SCSI host port type. + */ +static void +bfad_im_get_host_port_type(struct Scsi_Host *shost) +{ + struct bfad_im_port_s *im_port = + (struct bfad_im_port_s *) shost->hostdata[0]; + struct bfad_s *bfad = im_port->bfad; + struct bfa_pport_attr_s attr; + + bfa_pport_get_attr(&bfad->bfa, &attr); + + switch (attr.port_type) { + case BFA_PPORT_TYPE_NPORT: + fc_host_port_type(shost) = FC_PORTTYPE_NPORT; + break; + case BFA_PPORT_TYPE_NLPORT: + fc_host_port_type(shost) = FC_PORTTYPE_NLPORT; + break; + case BFA_PPORT_TYPE_P2P: + fc_host_port_type(shost) = FC_PORTTYPE_PTP; + break; + case BFA_PPORT_TYPE_LPORT: + fc_host_port_type(shost) = FC_PORTTYPE_LPORT; + break; + default: + fc_host_port_type(shost) = FC_PORTTYPE_UNKNOWN; + break; + } +} + +/** + * FC transport template entry, get SCSI host port state. + */ +static void +bfad_im_get_host_port_state(struct Scsi_Host *shost) +{ + struct bfad_im_port_s *im_port = + (struct bfad_im_port_s *) shost->hostdata[0]; + struct bfad_s *bfad = im_port->bfad; + struct bfa_pport_attr_s attr; + + bfa_pport_get_attr(&bfad->bfa, &attr); + + switch (attr.port_state) { + case BFA_PPORT_ST_LINKDOWN: + fc_host_port_state(shost) = FC_PORTSTATE_LINKDOWN; + break; + case BFA_PPORT_ST_LINKUP: + fc_host_port_state(shost) = FC_PORTSTATE_ONLINE; + break; + case BFA_PPORT_ST_UNINIT: + case BFA_PPORT_ST_ENABLING_QWAIT: + case BFA_PPORT_ST_ENABLING: + case BFA_PPORT_ST_DISABLING_QWAIT: + case BFA_PPORT_ST_DISABLING: + case BFA_PPORT_ST_DISABLED: + case BFA_PPORT_ST_STOPPED: + case BFA_PPORT_ST_IOCDOWN: + default: + fc_host_port_state(shost) = FC_PORTSTATE_UNKNOWN; + break; + } +} + +/** + * FC transport template entry, get SCSI host active fc4s. + */ +static void +bfad_im_get_host_active_fc4s(struct Scsi_Host *shost) +{ + struct bfad_im_port_s *im_port = + (struct bfad_im_port_s *) shost->hostdata[0]; + struct bfad_port_s *port = im_port->port; + + memset(fc_host_active_fc4s(shost), 0, + sizeof(fc_host_active_fc4s(shost))); + + if (port->supported_fc4s & + (BFA_PORT_ROLE_FCP_IM | BFA_PORT_ROLE_FCP_TM)) + fc_host_active_fc4s(shost)[2] = 1; + + if (port->supported_fc4s & BFA_PORT_ROLE_FCP_IPFC) + fc_host_active_fc4s(shost)[3] = 0x20; + + fc_host_active_fc4s(shost)[7] = 1; +} + +/** + * FC transport template entry, get SCSI host link speed. + */ +static void +bfad_im_get_host_speed(struct Scsi_Host *shost) +{ + struct bfad_im_port_s *im_port = + (struct bfad_im_port_s *) shost->hostdata[0]; + struct bfad_s *bfad = im_port->bfad; + struct bfa_pport_attr_s attr; + + bfa_pport_get_attr(&bfad->bfa, &attr); + switch (attr.speed) { + case BFA_PPORT_SPEED_8GBPS: + fc_host_speed(shost) = FC_PORTSPEED_8GBIT; + break; + case BFA_PPORT_SPEED_4GBPS: + fc_host_speed(shost) = FC_PORTSPEED_4GBIT; + break; + case BFA_PPORT_SPEED_2GBPS: + fc_host_speed(shost) = FC_PORTSPEED_2GBIT; + break; + case BFA_PPORT_SPEED_1GBPS: + fc_host_speed(shost) = FC_PORTSPEED_1GBIT; + break; + default: + fc_host_speed(shost) = FC_PORTSPEED_UNKNOWN; + break; + } +} + +/** + * FC transport template entry, get SCSI host port type. + */ +static void +bfad_im_get_host_fabric_name(struct Scsi_Host *shost) +{ + struct bfad_im_port_s *im_port = + (struct bfad_im_port_s *) shost->hostdata[0]; + struct bfad_port_s *port = im_port->port; + wwn_t fabric_nwwn = 0; + + fabric_nwwn = bfa_fcs_port_get_fabric_name(port->fcs_port); + + fc_host_fabric_name(shost) = bfa_os_htonll(fabric_nwwn); + +} + +/** + * FC transport template entry, get BFAD statistics. + */ +static struct fc_host_statistics * +bfad_im_get_stats(struct Scsi_Host *shost) +{ + struct bfad_im_port_s *im_port = + (struct bfad_im_port_s *) shost->hostdata[0]; + struct bfad_s *bfad = im_port->bfad; + struct bfad_hal_comp fcomp; + struct fc_host_statistics *hstats; + bfa_status_t rc; + unsigned long flags; + + hstats = &bfad->link_stats; + init_completion(&fcomp.comp); + spin_lock_irqsave(&bfad->bfad_lock, flags); + memset(hstats, 0, sizeof(struct fc_host_statistics)); + rc = bfa_pport_get_stats(&bfad->bfa, + (union bfa_pport_stats_u *) hstats, + bfad_hcb_comp, &fcomp); + spin_unlock_irqrestore(&bfad->bfad_lock, flags); + if (rc != BFA_STATUS_OK) + return NULL; + + wait_for_completion(&fcomp.comp); + + return hstats; +} + +/** + * FC transport template entry, reset BFAD statistics. + */ +static void +bfad_im_reset_stats(struct Scsi_Host *shost) +{ + struct bfad_im_port_s *im_port = + (struct bfad_im_port_s *) shost->hostdata[0]; + struct bfad_s *bfad = im_port->bfad; + struct bfad_hal_comp fcomp; + unsigned long flags; + bfa_status_t rc; + + init_completion(&fcomp.comp); + spin_lock_irqsave(&bfad->bfad_lock, flags); + rc = bfa_pport_clear_stats(&bfad->bfa, bfad_hcb_comp, &fcomp); + spin_unlock_irqrestore(&bfad->bfad_lock, flags); + + if (rc != BFA_STATUS_OK) + return; + + wait_for_completion(&fcomp.comp); + + return; +} + +/** + * FC transport template entry, get rport loss timeout. + */ +static void +bfad_im_get_rport_loss_tmo(struct fc_rport *rport) +{ + struct bfad_itnim_data_s *itnim_data = rport->dd_data; + struct bfad_itnim_s *itnim = itnim_data->itnim; + struct bfad_s *bfad = itnim->im->bfad; + unsigned long flags; + + spin_lock_irqsave(&bfad->bfad_lock, flags); + rport->dev_loss_tmo = bfa_fcpim_path_tov_get(&bfad->bfa); + spin_unlock_irqrestore(&bfad->bfad_lock, flags); +} + +/** + * FC transport template entry, set rport loss timeout. + */ +static void +bfad_im_set_rport_loss_tmo(struct fc_rport *rport, u32 timeout) +{ + struct bfad_itnim_data_s *itnim_data = rport->dd_data; + struct bfad_itnim_s *itnim = itnim_data->itnim; + struct bfad_s *bfad = itnim->im->bfad; + unsigned long flags; + + if (timeout > 0) { + spin_lock_irqsave(&bfad->bfad_lock, flags); + bfa_fcpim_path_tov_set(&bfad->bfa, timeout); + rport->dev_loss_tmo = bfa_fcpim_path_tov_get(&bfad->bfa); + spin_unlock_irqrestore(&bfad->bfad_lock, flags); + } + +} + +struct fc_function_template bfad_im_fc_function_template = { + + /* Target dynamic attributes */ + .get_starget_port_id = bfad_im_get_starget_port_id, + .show_starget_port_id = 1, + .get_starget_node_name = bfad_im_get_starget_node_name, + .show_starget_node_name = 1, + .get_starget_port_name = bfad_im_get_starget_port_name, + .show_starget_port_name = 1, + + /* Host dynamic attribute */ + .get_host_port_id = bfad_im_get_host_port_id, + .show_host_port_id = 1, + + /* Host fixed attributes */ + .show_host_node_name = 1, + .show_host_port_name = 1, + .show_host_supported_classes = 1, + .show_host_supported_fc4s = 1, + .show_host_supported_speeds = 1, + .show_host_maxframe_size = 1, + + /* More host dynamic attributes */ + .show_host_port_type = 1, + .get_host_port_type = bfad_im_get_host_port_type, + .show_host_port_state = 1, + .get_host_port_state = bfad_im_get_host_port_state, + .show_host_active_fc4s = 1, + .get_host_active_fc4s = bfad_im_get_host_active_fc4s, + .show_host_speed = 1, + .get_host_speed = bfad_im_get_host_speed, + .show_host_fabric_name = 1, + .get_host_fabric_name = bfad_im_get_host_fabric_name, + + .show_host_symbolic_name = 1, + + /* Statistics */ + .get_fc_host_stats = bfad_im_get_stats, + .reset_fc_host_stats = bfad_im_reset_stats, + + /* Allocation length for host specific data */ + .dd_fcrport_size = sizeof(struct bfad_itnim_data_s *), + + /* Remote port fixed attributes */ + .show_rport_maxframe_size = 1, + .show_rport_supported_classes = 1, + .show_rport_dev_loss_tmo = 1, + .get_rport_dev_loss_tmo = bfad_im_get_rport_loss_tmo, + .set_rport_dev_loss_tmo = bfad_im_set_rport_loss_tmo, +}; + +/** + * Scsi_Host_attrs SCSI host attributes + */ +static ssize_t +bfad_im_serial_num_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct Scsi_Host *shost = class_to_shost(dev); + struct bfad_im_port_s *im_port = + (struct bfad_im_port_s *) shost->hostdata[0]; + struct bfad_s *bfad = im_port->bfad; + struct bfa_ioc_attr_s ioc_attr; + + memset(&ioc_attr, 0, sizeof(ioc_attr)); + bfa_get_attr(&bfad->bfa, &ioc_attr); + return snprintf(buf, PAGE_SIZE, "%s\n", + ioc_attr.adapter_attr.serial_num); +} + +static ssize_t +bfad_im_model_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct Scsi_Host *shost = class_to_shost(dev); + struct bfad_im_port_s *im_port = + (struct bfad_im_port_s *) shost->hostdata[0]; + struct bfad_s *bfad = im_port->bfad; + struct bfa_ioc_attr_s ioc_attr; + + memset(&ioc_attr, 0, sizeof(ioc_attr)); + bfa_get_attr(&bfad->bfa, &ioc_attr); + return snprintf(buf, PAGE_SIZE, "%s\n", ioc_attr.adapter_attr.model); +} + +static ssize_t +bfad_im_model_desc_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct Scsi_Host *shost = class_to_shost(dev); + struct bfad_im_port_s *im_port = + (struct bfad_im_port_s *) shost->hostdata[0]; + struct bfad_s *bfad = im_port->bfad; + struct bfa_ioc_attr_s ioc_attr; + + memset(&ioc_attr, 0, sizeof(ioc_attr)); + bfa_get_attr(&bfad->bfa, &ioc_attr); + return snprintf(buf, PAGE_SIZE, "%s\n", + ioc_attr.adapter_attr.model_descr); +} + +static ssize_t +bfad_im_node_name_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct Scsi_Host *shost = class_to_shost(dev); + struct bfad_im_port_s *im_port = + (struct bfad_im_port_s *) shost->hostdata[0]; + struct bfad_port_s *port = im_port->port; + u64 nwwn; + + nwwn = bfa_fcs_port_get_nwwn(port->fcs_port); + return snprintf(buf, PAGE_SIZE, "0x%llx\n", bfa_os_htonll(nwwn)); +} + +static ssize_t +bfad_im_symbolic_name_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct Scsi_Host *shost = class_to_shost(dev); + struct bfad_im_port_s *im_port = + (struct bfad_im_port_s *) shost->hostdata[0]; + struct bfad_s *bfad = im_port->bfad; + struct bfa_ioc_attr_s ioc_attr; + + memset(&ioc_attr, 0, sizeof(ioc_attr)); + bfa_get_attr(&bfad->bfa, &ioc_attr); + + return snprintf(buf, PAGE_SIZE, "Brocade %s FV%s DV%s\n", + ioc_attr.adapter_attr.model, + ioc_attr.adapter_attr.fw_ver, BFAD_DRIVER_VERSION); +} + +static ssize_t +bfad_im_hw_version_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct Scsi_Host *shost = class_to_shost(dev); + struct bfad_im_port_s *im_port = + (struct bfad_im_port_s *) shost->hostdata[0]; + struct bfad_s *bfad = im_port->bfad; + struct bfa_ioc_attr_s ioc_attr; + + memset(&ioc_attr, 0, sizeof(ioc_attr)); + bfa_get_attr(&bfad->bfa, &ioc_attr); + return snprintf(buf, PAGE_SIZE, "%s\n", ioc_attr.adapter_attr.hw_ver); +} + +static ssize_t +bfad_im_drv_version_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%s\n", BFAD_DRIVER_VERSION); +} + +static ssize_t +bfad_im_optionrom_version_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct Scsi_Host *shost = class_to_shost(dev); + struct bfad_im_port_s *im_port = + (struct bfad_im_port_s *) shost->hostdata[0]; + struct bfad_s *bfad = im_port->bfad; + struct bfa_ioc_attr_s ioc_attr; + + memset(&ioc_attr, 0, sizeof(ioc_attr)); + bfa_get_attr(&bfad->bfa, &ioc_attr); + return snprintf(buf, PAGE_SIZE, "%s\n", + ioc_attr.adapter_attr.optrom_ver); +} + +static ssize_t +bfad_im_fw_version_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct Scsi_Host *shost = class_to_shost(dev); + struct bfad_im_port_s *im_port = + (struct bfad_im_port_s *) shost->hostdata[0]; + struct bfad_s *bfad = im_port->bfad; + struct bfa_ioc_attr_s ioc_attr; + + memset(&ioc_attr, 0, sizeof(ioc_attr)); + bfa_get_attr(&bfad->bfa, &ioc_attr); + return snprintf(buf, PAGE_SIZE, "%s\n", ioc_attr.adapter_attr.fw_ver); +} + +static ssize_t +bfad_im_num_of_ports_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct Scsi_Host *shost = class_to_shost(dev); + struct bfad_im_port_s *im_port = + (struct bfad_im_port_s *) shost->hostdata[0]; + struct bfad_s *bfad = im_port->bfad; + struct bfa_ioc_attr_s ioc_attr; + + memset(&ioc_attr, 0, sizeof(ioc_attr)); + bfa_get_attr(&bfad->bfa, &ioc_attr); + return snprintf(buf, PAGE_SIZE, "%d\n", ioc_attr.adapter_attr.nports); +} + +static ssize_t +bfad_im_drv_name_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%s\n", BFAD_DRIVER_NAME); +} + +static ssize_t +bfad_im_num_of_discovered_ports_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct Scsi_Host *shost = class_to_shost(dev); + struct bfad_im_port_s *im_port = + (struct bfad_im_port_s *) shost->hostdata[0]; + struct bfad_port_s *port = im_port->port; + struct bfad_s *bfad = im_port->bfad; + int nrports = 2048; + wwn_t *rports = NULL; + unsigned long flags; + + rports = kzalloc(sizeof(wwn_t) * nrports , GFP_ATOMIC); + if (rports == NULL) + return -ENOMEM; + + spin_lock_irqsave(&bfad->bfad_lock, flags); + bfa_fcs_port_get_rports(port->fcs_port, rports, &nrports); + spin_unlock_irqrestore(&bfad->bfad_lock, flags); + kfree(rports); + + return snprintf(buf, PAGE_SIZE, "%d\n", nrports); +} + +static DEVICE_ATTR(serial_number, S_IRUGO, + bfad_im_serial_num_show, NULL); +static DEVICE_ATTR(model, S_IRUGO, bfad_im_model_show, NULL); +static DEVICE_ATTR(model_description, S_IRUGO, + bfad_im_model_desc_show, NULL); +static DEVICE_ATTR(node_name, S_IRUGO, bfad_im_node_name_show, NULL); +static DEVICE_ATTR(symbolic_name, S_IRUGO, + bfad_im_symbolic_name_show, NULL); +static DEVICE_ATTR(hardware_version, S_IRUGO, + bfad_im_hw_version_show, NULL); +static DEVICE_ATTR(driver_version, S_IRUGO, + bfad_im_drv_version_show, NULL); +static DEVICE_ATTR(option_rom_version, S_IRUGO, + bfad_im_optionrom_version_show, NULL); +static DEVICE_ATTR(firmware_version, S_IRUGO, + bfad_im_fw_version_show, NULL); +static DEVICE_ATTR(number_of_ports, S_IRUGO, + bfad_im_num_of_ports_show, NULL); +static DEVICE_ATTR(driver_name, S_IRUGO, bfad_im_drv_name_show, NULL); +static DEVICE_ATTR(number_of_discovered_ports, S_IRUGO, + bfad_im_num_of_discovered_ports_show, NULL); + +struct device_attribute *bfad_im_host_attrs[] = { + &dev_attr_serial_number, + &dev_attr_model, + &dev_attr_model_description, + &dev_attr_node_name, + &dev_attr_symbolic_name, + &dev_attr_hardware_version, + &dev_attr_driver_version, + &dev_attr_option_rom_version, + &dev_attr_firmware_version, + &dev_attr_number_of_ports, + &dev_attr_driver_name, + &dev_attr_number_of_discovered_ports, + NULL, +}; + +struct device_attribute *bfad_im_vport_attrs[] = { + &dev_attr_serial_number, + &dev_attr_model, + &dev_attr_model_description, + &dev_attr_node_name, + &dev_attr_symbolic_name, + &dev_attr_hardware_version, + &dev_attr_driver_version, + &dev_attr_option_rom_version, + &dev_attr_firmware_version, + &dev_attr_number_of_ports, + &dev_attr_driver_name, + &dev_attr_number_of_discovered_ports, + NULL, +}; + + diff --git a/drivers/scsi/bfa/bfad_attr.h b/drivers/scsi/bfa/bfad_attr.h new file mode 100644 index 000000000000..4d3312da6a81 --- /dev/null +++ b/drivers/scsi/bfa/bfad_attr.h @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2005-2009 Brocade Communications Systems, Inc. + * All rights reserved + * www.brocade.com + * + * Linux driver for Brocade Fibre Channel Host Bus Adapter. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (GPL) Version 2 as + * published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#ifndef __BFAD_ATTR_H__ +#define __BFAD_ATTR_H__ +/** + * bfad_attr.h VMware driver configuration interface module. + */ + +/** + * FC_transport_template FC transport template + */ + +struct Scsi_Host* +bfad_os_dev_to_shost(struct scsi_target *starget); + +/** + * FC transport template entry, get SCSI target port ID. + */ +void +bfad_im_get_starget_port_id(struct scsi_target *starget); + +/** + * FC transport template entry, get SCSI target nwwn. + */ +void +bfad_im_get_starget_node_name(struct scsi_target *starget); + +/** + * FC transport template entry, get SCSI target pwwn. + */ +void +bfad_im_get_starget_port_name(struct scsi_target *starget); + +/** + * FC transport template entry, get SCSI host port ID. + */ +void +bfad_im_get_host_port_id(struct Scsi_Host *shost); + +/** + * FC transport template entry, issue a LIP. + */ +int +bfad_im_issue_fc_host_lip(struct Scsi_Host *shost); + +struct Scsi_Host* +bfad_os_starget_to_shost(struct scsi_target *starget); + + +#endif /* __BFAD_ATTR_H__ */ diff --git a/drivers/scsi/bfa/bfad_drv.h b/drivers/scsi/bfa/bfad_drv.h new file mode 100644 index 000000000000..172c81e25c1c --- /dev/null +++ b/drivers/scsi/bfa/bfad_drv.h @@ -0,0 +1,295 @@ +/* + * Copyright (c) 2005-2009 Brocade Communications Systems, Inc. + * All rights reserved + * www.brocade.com + * + * Linux driver for Brocade Fibre Channel Host Bus Adapter. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (GPL) Version 2 as + * published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +/** + * Contains base driver definitions. + */ + +/** + * bfa_drv.h Linux driver data structures. + */ + +#ifndef __BFAD_DRV_H__ +#define __BFAD_DRV_H__ + +#include "bfa_os_inc.h" + +#include <bfa.h> +#include <bfa_svc.h> +#include <fcs/bfa_fcs.h> +#include <defs/bfa_defs_pci.h> +#include <defs/bfa_defs_port.h> +#include <defs/bfa_defs_rport.h> +#include <fcs/bfa_fcs_rport.h> +#include <defs/bfa_defs_vport.h> +#include <fcs/bfa_fcs_vport.h> + +#include <cs/bfa_plog.h> +#include "aen/bfa_aen.h" +#include <log/bfa_log_linux.h> + +#define BFAD_DRIVER_NAME "bfa" +#ifdef BFA_DRIVER_VERSION +#define BFAD_DRIVER_VERSION BFA_DRIVER_VERSION +#else +#define BFAD_DRIVER_VERSION "2.0.0.0" +#endif + + +#define BFAD_IRQ_FLAGS IRQF_SHARED + +/* + * BFAD flags + */ +#define BFAD_MSIX_ON 0x00000001 +#define BFAD_HAL_INIT_DONE 0x00000002 +#define BFAD_DRV_INIT_DONE 0x00000004 +#define BFAD_CFG_PPORT_DONE 0x00000008 +#define BFAD_HAL_START_DONE 0x00000010 +#define BFAD_PORT_ONLINE 0x00000020 +#define BFAD_RPORT_ONLINE 0x00000040 + +#define BFAD_PORT_DELETE 0x00000001 + +/* + * BFAD related definition + */ +#define SCSI_SCAN_DELAY HZ +#define BFAD_STOP_TIMEOUT 30 +#define BFAD_SUSPEND_TIMEOUT BFAD_STOP_TIMEOUT + +/* + * BFAD configuration parameter default values + */ +#define BFAD_LUN_QUEUE_DEPTH 32 +#define BFAD_IO_MAX_SGE SG_ALL + +#define bfad_isr_t irq_handler_t + +#define MAX_MSIX_ENTRY 22 + +struct bfad_msix_s { + struct bfad_s *bfad; + struct msix_entry msix; +}; + +enum bfad_port_pvb_type { + BFAD_PORT_PHYS_BASE = 0, + BFAD_PORT_PHYS_VPORT = 1, + BFAD_PORT_VF_BASE = 2, + BFAD_PORT_VF_VPORT = 3, +}; + +/* + * PORT data structure + */ +struct bfad_port_s { + struct list_head list_entry; + struct bfad_s *bfad; + struct bfa_fcs_port_s *fcs_port; + u32 roles; + s32 flags; + u32 supported_fc4s; + u8 ipfc_flags; + enum bfad_port_pvb_type pvb_type; + struct bfad_im_port_s *im_port; /* IM specific data */ + struct bfad_tm_port_s *tm_port; /* TM specific data */ + struct bfad_ipfc_port_s *ipfc_port; /* IPFC specific data */ +}; + +/* + * VPORT data structure + */ +struct bfad_vport_s { + struct bfad_port_s drv_port; + struct bfa_fcs_vport_s fcs_vport; + struct completion *comp_del; +}; + +/* + * VF data structure + */ +struct bfad_vf_s { + bfa_fcs_vf_t fcs_vf; + struct bfad_port_s base_port; /* base port for vf */ + struct bfad_s *bfad; +}; + +struct bfad_cfg_param_s { + u32 rport_del_timeout; + u32 ioc_queue_depth; + u32 lun_queue_depth; + u32 io_max_sge; + u32 binding_method; +}; + +#define BFAD_AEN_MAX_APPS 8 +struct bfad_aen_file_s { + struct list_head qe; + struct bfad_s *bfad; + s32 ri; + s32 app_id; +}; + +/* + * BFAD (PCI function) data structure + */ +struct bfad_s { + struct list_head list_entry; + struct bfa_s bfa; + struct bfa_fcs_s bfa_fcs; + struct pci_dev *pcidev; + const char *pci_name; + struct bfa_pcidev_s hal_pcidev; + struct bfa_ioc_pci_attr_s pci_attr; + unsigned long pci_bar0_map; + void __iomem *pci_bar0_kva; + struct completion comp; + struct completion suspend; + struct completion disable_comp; + bfa_boolean_t disable_active; + struct bfad_port_s pport; /* physical port of the BFAD */ + struct bfa_meminfo_s meminfo; + struct bfa_iocfc_cfg_s ioc_cfg; + u32 inst_no; /* BFAD instance number */ + u32 bfad_flags; + spinlock_t bfad_lock; + struct bfad_cfg_param_s cfg_data; + struct bfad_msix_s msix_tab[MAX_MSIX_ENTRY]; + int nvec; + char adapter_name[BFA_ADAPTER_SYM_NAME_LEN]; + char port_name[BFA_ADAPTER_SYM_NAME_LEN]; + struct timer_list hal_tmo; + unsigned long hs_start; + struct bfad_im_s *im; /* IM specific data */ + struct bfad_tm_s *tm; /* TM specific data */ + struct bfad_ipfc_s *ipfc; /* IPFC specific data */ + struct bfa_log_mod_s log_data; + struct bfa_trc_mod_s *trcmod; + struct bfa_log_mod_s *logmod; + struct bfa_aen_s *aen; + struct bfa_aen_s aen_buf; + struct bfad_aen_file_s file_buf[BFAD_AEN_MAX_APPS]; + struct list_head file_q; + struct list_head file_free_q; + struct bfa_plog_s plog_buf; + int ref_count; + bfa_boolean_t ipfc_enabled; + struct fc_host_statistics link_stats; + + struct kobject *bfa_kobj; + struct kobject *ioc_kobj; + struct kobject *pport_kobj; + struct kobject *lport_kobj; +}; + +/* + * RPORT data structure + */ +struct bfad_rport_s { + struct bfa_fcs_rport_s fcs_rport; +}; + +struct bfad_buf_info { + void *virt; + dma_addr_t phys; + u32 size; +}; + +struct bfad_fcxp { + struct bfad_port_s *port; + struct bfa_rport_s *bfa_rport; + bfa_status_t req_status; + u16 tag; + u16 rsp_len; + u16 rsp_maxlen; + u8 use_ireqbuf; + u8 use_irspbuf; + u32 num_req_sgles; + u32 num_rsp_sgles; + struct fchs_s fchs; + void *reqbuf_info; + void *rspbuf_info; + struct bfa_sge_s *req_sge; + struct bfa_sge_s *rsp_sge; + fcxp_send_cb_t send_cbfn; + void *send_cbarg; + void *bfa_fcxp; + struct completion comp; +}; + +struct bfad_hal_comp { + bfa_status_t status; + struct completion comp; +}; + +/* + * Macro to obtain the immediate lower power + * of two for the integer. + */ +#define nextLowerInt(x) \ +do { \ + int j; \ + (*x)--; \ + for (j = 1; j < (sizeof(int) * 8); j <<= 1) \ + (*x) = (*x) | (*x) >> j; \ + (*x)++; \ + (*x) = (*x) >> 1; \ +} while (0) + + +bfa_status_t bfad_vport_create(struct bfad_s *bfad, u16 vf_id, + struct bfa_port_cfg_s *port_cfg); +bfa_status_t bfad_vf_create(struct bfad_s *bfad, u16 vf_id, + struct bfa_port_cfg_s *port_cfg); +bfa_status_t bfad_cfg_pport(struct bfad_s *bfad, enum bfa_port_role role); +bfa_status_t bfad_drv_init(struct bfad_s *bfad); +void bfad_drv_start(struct bfad_s *bfad); +void bfad_uncfg_pport(struct bfad_s *bfad); +void bfad_drv_stop(struct bfad_s *bfad); +void bfad_remove_intr(struct bfad_s *bfad); +void bfad_hal_mem_release(struct bfad_s *bfad); +void bfad_hcb_comp(void *arg, bfa_status_t status); + +int bfad_setup_intr(struct bfad_s *bfad); +void bfad_remove_intr(struct bfad_s *bfad); + +void bfad_update_hal_cfg(struct bfa_iocfc_cfg_s *bfa_cfg); +bfa_status_t bfad_hal_mem_alloc(struct bfad_s *bfad); +void bfad_bfa_tmo(unsigned long data); +void bfad_init_timer(struct bfad_s *bfad); +int bfad_pci_init(struct pci_dev *pdev, struct bfad_s *bfad); +void bfad_pci_uninit(struct pci_dev *pdev, struct bfad_s *bfad); +void bfad_fcs_port_cfg(struct bfad_s *bfad); +void bfad_drv_uninit(struct bfad_s *bfad); +void bfad_drv_log_level_set(struct bfad_s *bfad); +bfa_status_t bfad_fc4_module_init(void); +void bfad_fc4_module_exit(void); + +void bfad_pci_remove(struct pci_dev *pdev); +int bfad_pci_probe(struct pci_dev *pdev, const struct pci_device_id *pid); +void bfad_os_rport_online_wait(struct bfad_s *bfad); +int bfad_os_get_linkup_delay(struct bfad_s *bfad); +int bfad_install_msix_handler(struct bfad_s *bfad); + +extern struct idr bfad_im_port_index; +extern struct list_head bfad_list; +extern int bfa_lun_queue_depth; +extern int bfad_supported_fc4s; +extern int bfa_linkup_delay; + +#endif /* __BFAD_DRV_H__ */ diff --git a/drivers/scsi/bfa/bfad_fwimg.c b/drivers/scsi/bfa/bfad_fwimg.c new file mode 100644 index 000000000000..bd34b0db2d6b --- /dev/null +++ b/drivers/scsi/bfa/bfad_fwimg.c @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2005-2009 Brocade Communications Systems, Inc. + * All rights reserved + * www.brocade.com + * + * Linux driver for Brocade Fibre Channel Host Bus Adapter. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (GPL) Version 2 as + * published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +/** + * bfad_fwimg.c Linux driver PCI interface module. + */ +#include <bfa_os_inc.h> +#include <bfad_drv.h> +#include <bfad_im_compat.h> +#include <defs/bfa_defs_version.h> +#include <linux/errno.h> +#include <linux/sched.h> +#include <linux/init.h> +#include <linux/fs.h> +#include <asm/uaccess.h> +#include <asm/fcntl.h> +#include <linux/pci.h> +#include <linux/firmware.h> +#include <bfa_fwimg_priv.h> +#include <bfa.h> + +u32 bfi_image_ct_size; +u32 bfi_image_cb_size; +u32 *bfi_image_ct; +u32 *bfi_image_cb; + + +#define BFAD_FW_FILE_CT "ctfw.bin" +#define BFAD_FW_FILE_CB "cbfw.bin" +MODULE_FIRMWARE(BFAD_FW_FILE_CT); +MODULE_FIRMWARE(BFAD_FW_FILE_CB); + +u32 * +bfad_read_firmware(struct pci_dev *pdev, u32 **bfi_image, + u32 *bfi_image_size, char *fw_name) +{ + const struct firmware *fw; + + if (request_firmware(&fw, fw_name, &pdev->dev)) { + printk(KERN_ALERT "Can't locate firmware %s\n", fw_name); + goto error; + } + + *bfi_image = vmalloc(fw->size); + if (NULL == *bfi_image) { + printk(KERN_ALERT "Fail to allocate buffer for fw image " + "size=%x!\n", (u32) fw->size); + goto error; + } + + memcpy(*bfi_image, fw->data, fw->size); + *bfi_image_size = fw->size/sizeof(u32); + + return(*bfi_image); + +error: + return(NULL); +} + +u32 * +bfad_get_firmware_buf(struct pci_dev *pdev) +{ + if (pdev->device == BFA_PCI_DEVICE_ID_CT) { + if (bfi_image_ct_size == 0) + bfad_read_firmware(pdev, &bfi_image_ct, + &bfi_image_ct_size, BFAD_FW_FILE_CT); + return(bfi_image_ct); + } else { + if (bfi_image_cb_size == 0) + bfad_read_firmware(pdev, &bfi_image_cb, + &bfi_image_cb_size, BFAD_FW_FILE_CB); + return(bfi_image_cb); + } +} + +u32 * +bfi_image_ct_get_chunk(u32 off) +{ return (u32 *)(bfi_image_ct + off); } + +u32 * +bfi_image_cb_get_chunk(u32 off) +{ return (u32 *)(bfi_image_cb + off); } + diff --git a/drivers/scsi/bfa/bfad_im.c b/drivers/scsi/bfa/bfad_im.c new file mode 100644 index 000000000000..55d012a9a668 --- /dev/null +++ b/drivers/scsi/bfa/bfad_im.c @@ -0,0 +1,1230 @@ +/* + * Copyright (c) 2005-2009 Brocade Communications Systems, Inc. + * All rights reserved + * www.brocade.com + * + * Linux driver for Brocade Fibre Channel Host Bus Adapter. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (GPL) Version 2 as + * published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +/** + * bfad_im.c Linux driver IM module. + */ + +#include "bfad_drv.h" +#include "bfad_im.h" +#include "bfad_trcmod.h" +#include "bfa_cb_ioim_macros.h" +#include <fcb/bfa_fcb_fcpim.h> + +BFA_TRC_FILE(LDRV, IM); + +DEFINE_IDR(bfad_im_port_index); +struct scsi_transport_template *bfad_im_scsi_transport_template; +static void bfad_im_itnim_work_handler(struct work_struct *work); +static int bfad_im_queuecommand(struct scsi_cmnd *cmnd, + void (*done)(struct scsi_cmnd *)); +static int bfad_im_slave_alloc(struct scsi_device *sdev); + +void +bfa_cb_ioim_done(void *drv, struct bfad_ioim_s *dio, + enum bfi_ioim_status io_status, u8 scsi_status, + int sns_len, u8 *sns_info, s32 residue) +{ + struct scsi_cmnd *cmnd = (struct scsi_cmnd *)dio; + struct bfad_s *bfad = drv; + struct bfad_itnim_data_s *itnim_data; + struct bfad_itnim_s *itnim; + + switch (io_status) { + case BFI_IOIM_STS_OK: + bfa_trc(bfad, scsi_status); + cmnd->result = ScsiResult(DID_OK, scsi_status); + scsi_set_resid(cmnd, 0); + + if (sns_len > 0) { + bfa_trc(bfad, sns_len); + if (sns_len > SCSI_SENSE_BUFFERSIZE) + sns_len = SCSI_SENSE_BUFFERSIZE; + memcpy(cmnd->sense_buffer, sns_info, sns_len); + } + if (residue > 0) + scsi_set_resid(cmnd, residue); + break; + + case BFI_IOIM_STS_ABORTED: + case BFI_IOIM_STS_TIMEDOUT: + case BFI_IOIM_STS_PATHTOV: + default: + cmnd->result = ScsiResult(DID_ERROR, 0); + } + + /* Unmap DMA, if host is NULL, it means a scsi passthru cmd */ + if (cmnd->device->host != NULL) + scsi_dma_unmap(cmnd); + + cmnd->host_scribble = NULL; + bfa_trc(bfad, cmnd->result); + + itnim_data = cmnd->device->hostdata; + if (itnim_data) { + itnim = itnim_data->itnim; + if (!cmnd->result && itnim && + (bfa_lun_queue_depth > cmnd->device->queue_depth)) { + /* Queue depth adjustment for good status completion */ + bfad_os_ramp_up_qdepth(itnim, cmnd->device); + } else if (cmnd->result == SAM_STAT_TASK_SET_FULL && itnim) { + /* qfull handling */ + bfad_os_handle_qfull(itnim, cmnd->device); + } + } + + cmnd->scsi_done(cmnd); +} + +void +bfa_cb_ioim_good_comp(void *drv, struct bfad_ioim_s *dio) +{ + struct scsi_cmnd *cmnd = (struct scsi_cmnd *)dio; + struct bfad_itnim_data_s *itnim_data; + struct bfad_itnim_s *itnim; + + cmnd->result = ScsiResult(DID_OK, SCSI_STATUS_GOOD); + + /* Unmap DMA, if host is NULL, it means a scsi passthru cmd */ + if (cmnd->device->host != NULL) + scsi_dma_unmap(cmnd); + + cmnd->host_scribble = NULL; + + /* Queue depth adjustment */ + if (bfa_lun_queue_depth > cmnd->device->queue_depth) { + itnim_data = cmnd->device->hostdata; + if (itnim_data) { + itnim = itnim_data->itnim; + if (itnim) + bfad_os_ramp_up_qdepth(itnim, cmnd->device); + } + } + + cmnd->scsi_done(cmnd); +} + +void +bfa_cb_ioim_abort(void *drv, struct bfad_ioim_s *dio) +{ + struct scsi_cmnd *cmnd = (struct scsi_cmnd *)dio; + struct bfad_s *bfad = drv; + + cmnd->result = ScsiResult(DID_ERROR, 0); + + /* Unmap DMA, if host is NULL, it means a scsi passthru cmd */ + if (cmnd->device->host != NULL) + scsi_dma_unmap(cmnd); + + bfa_trc(bfad, cmnd->result); + cmnd->host_scribble = NULL; +} + +void +bfa_cb_tskim_done(void *bfad, struct bfad_tskim_s *dtsk, + enum bfi_tskim_status tsk_status) +{ + struct scsi_cmnd *cmnd = (struct scsi_cmnd *)dtsk; + wait_queue_head_t *wq; + + cmnd->SCp.Status |= tsk_status << 1; + set_bit(IO_DONE_BIT, (unsigned long *)&cmnd->SCp.Status); + wq = (wait_queue_head_t *) cmnd->SCp.ptr; + cmnd->SCp.ptr = NULL; + + if (wq) + wake_up(wq); +} + +void +bfa_cb_ioim_resfree(void *drv) +{ +} + +/** + * Scsi_Host_template SCSI host template + */ +/** + * Scsi_Host template entry, returns BFAD PCI info. + */ +static const char * +bfad_im_info(struct Scsi_Host *shost) +{ + static char bfa_buf[256]; + struct bfad_im_port_s *im_port = + (struct bfad_im_port_s *) shost->hostdata[0]; + struct bfa_ioc_attr_s ioc_attr; + struct bfad_s *bfad = im_port->bfad; + + memset(&ioc_attr, 0, sizeof(ioc_attr)); + bfa_get_attr(&bfad->bfa, &ioc_attr); + + memset(bfa_buf, 0, sizeof(bfa_buf)); + snprintf(bfa_buf, sizeof(bfa_buf), + "Brocade FC/FCOE Adapter, " "model: %s hwpath: %s driver: %s", + ioc_attr.adapter_attr.model, bfad->pci_name, + BFAD_DRIVER_VERSION); + return bfa_buf; +} + +/** + * Scsi_Host template entry, aborts the specified SCSI command. + * + * Returns: SUCCESS or FAILED. + */ +static int +bfad_im_abort_handler(struct scsi_cmnd *cmnd) +{ + struct Scsi_Host *shost = cmnd->device->host; + struct bfad_im_port_s *im_port = + (struct bfad_im_port_s *) shost->hostdata[0]; + struct bfad_s *bfad = im_port->bfad; + struct bfa_ioim_s *hal_io; + unsigned long flags; + u32 timeout; + int rc = FAILED; + + spin_lock_irqsave(&bfad->bfad_lock, flags); + hal_io = (struct bfa_ioim_s *) cmnd->host_scribble; + if (!hal_io) { + /* IO has been completed, retrun success */ + rc = SUCCESS; + goto out; + } + if (hal_io->dio != (struct bfad_ioim_s *) cmnd) { + rc = FAILED; + goto out; + } + + bfa_trc(bfad, hal_io->iotag); + bfa_log(bfad->logmod, BFA_LOG_LINUX_SCSI_ABORT, + im_port->shost->host_no, cmnd, hal_io->iotag); + bfa_ioim_abort(hal_io); + spin_unlock_irqrestore(&bfad->bfad_lock, flags); + + /* Need to wait until the command get aborted */ + timeout = 10; + while ((struct bfa_ioim_s *) cmnd->host_scribble == hal_io) { + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(timeout); + if (timeout < 4 * HZ) + timeout *= 2; + } + + cmnd->scsi_done(cmnd); + bfa_trc(bfad, hal_io->iotag); + bfa_log(bfad->logmod, BFA_LOG_LINUX_SCSI_ABORT_COMP, + im_port->shost->host_no, cmnd, hal_io->iotag); + return SUCCESS; +out: + spin_unlock_irqrestore(&bfad->bfad_lock, flags); + return rc; +} + +static bfa_status_t +bfad_im_target_reset_send(struct bfad_s *bfad, struct scsi_cmnd *cmnd, + struct bfad_itnim_s *itnim) +{ + struct bfa_tskim_s *tskim; + struct bfa_itnim_s *bfa_itnim; + bfa_status_t rc = BFA_STATUS_OK; + + bfa_itnim = bfa_fcs_itnim_get_halitn(&itnim->fcs_itnim); + tskim = bfa_tskim_alloc(&bfad->bfa, (struct bfad_tskim_s *) cmnd); + if (!tskim) { + BFA_DEV_PRINTF(bfad, BFA_ERR, + "target reset, fail to allocate tskim\n"); + rc = BFA_STATUS_FAILED; + goto out; + } + + /* + * Set host_scribble to NULL to avoid aborting a task command if + * happens. + */ + cmnd->host_scribble = NULL; + cmnd->SCp.Status = 0; + bfa_itnim = bfa_fcs_itnim_get_halitn(&itnim->fcs_itnim); + bfa_tskim_start(tskim, bfa_itnim, (lun_t)0, + FCP_TM_TARGET_RESET, BFAD_TARGET_RESET_TMO); +out: + return rc; +} + +/** + * Scsi_Host template entry, resets a LUN and abort its all commands. + * + * Returns: SUCCESS or FAILED. + * + */ +static int +bfad_im_reset_lun_handler(struct scsi_cmnd *cmnd) +{ + struct Scsi_Host *shost = cmnd->device->host; + struct bfad_im_port_s *im_port = + (struct bfad_im_port_s *) shost->hostdata[0]; + struct bfad_itnim_data_s *itnim_data = cmnd->device->hostdata; + struct bfad_s *bfad = im_port->bfad; + struct bfa_tskim_s *tskim; + struct bfad_itnim_s *itnim; + struct bfa_itnim_s *bfa_itnim; + DECLARE_WAIT_QUEUE_HEAD(wq); + int rc = SUCCESS; + unsigned long flags; + enum bfi_tskim_status task_status; + + spin_lock_irqsave(&bfad->bfad_lock, flags); + itnim = itnim_data->itnim; + if (!itnim) { + spin_unlock_irqrestore(&bfad->bfad_lock, flags); + rc = FAILED; + goto out; + } + + tskim = bfa_tskim_alloc(&bfad->bfa, (struct bfad_tskim_s *) cmnd); + if (!tskim) { + BFA_DEV_PRINTF(bfad, BFA_ERR, + "LUN reset, fail to allocate tskim"); + spin_unlock_irqrestore(&bfad->bfad_lock, flags); + rc = FAILED; + goto out; + } + + /** + * Set host_scribble to NULL to avoid aborting a task command + * if happens. + */ + cmnd->host_scribble = NULL; + cmnd->SCp.ptr = (char *)&wq; + cmnd->SCp.Status = 0; + bfa_itnim = bfa_fcs_itnim_get_halitn(&itnim->fcs_itnim); + bfa_tskim_start(tskim, bfa_itnim, + bfad_int_to_lun(cmnd->device->lun), + FCP_TM_LUN_RESET, BFAD_LUN_RESET_TMO); + spin_unlock_irqrestore(&bfad->bfad_lock, flags); + + wait_event(wq, test_bit(IO_DONE_BIT, + (unsigned long *)&cmnd->SCp.Status)); + + task_status = cmnd->SCp.Status >> 1; + if (task_status != BFI_TSKIM_STS_OK) { + BFA_DEV_PRINTF(bfad, BFA_ERR, "LUN reset failure, status: %d\n", + task_status); + rc = FAILED; + } + +out: + return rc; +} + +/** + * Scsi_Host template entry, resets the bus and abort all commands. + */ +static int +bfad_im_reset_bus_handler(struct scsi_cmnd *cmnd) +{ + struct Scsi_Host *shost = cmnd->device->host; + struct bfad_im_port_s *im_port = + (struct bfad_im_port_s *) shost->hostdata[0]; + struct bfad_s *bfad = im_port->bfad; + struct bfad_itnim_s *itnim; + unsigned long flags; + u32 i, rc, err_cnt = 0; + DECLARE_WAIT_QUEUE_HEAD(wq); + enum bfi_tskim_status task_status; + + spin_lock_irqsave(&bfad->bfad_lock, flags); + for (i = 0; i < MAX_FCP_TARGET; i++) { + itnim = bfad_os_get_itnim(im_port, i); + if (itnim) { + cmnd->SCp.ptr = (char *)&wq; + rc = bfad_im_target_reset_send(bfad, cmnd, itnim); + if (rc != BFA_STATUS_OK) { + err_cnt++; + continue; + } + + /* wait target reset to complete */ + spin_unlock_irqrestore(&bfad->bfad_lock, flags); + wait_event(wq, test_bit(IO_DONE_BIT, + (unsigned long *)&cmnd->SCp.Status)); + spin_lock_irqsave(&bfad->bfad_lock, flags); + + task_status = cmnd->SCp.Status >> 1; + if (task_status != BFI_TSKIM_STS_OK) { + BFA_DEV_PRINTF(bfad, BFA_ERR, + "target reset failure," + " status: %d\n", task_status); + err_cnt++; + } + } + } + spin_unlock_irqrestore(&bfad->bfad_lock, flags); + + if (err_cnt) + return FAILED; + + return SUCCESS; +} + +/** + * Scsi_Host template entry slave_destroy. + */ +static void +bfad_im_slave_destroy(struct scsi_device *sdev) +{ + sdev->hostdata = NULL; + return; +} + +/** + * BFA FCS itnim callbacks + */ + +/** + * BFA FCS itnim alloc callback, after successful PRLI + * Context: Interrupt + */ +void +bfa_fcb_itnim_alloc(struct bfad_s *bfad, struct bfa_fcs_itnim_s **itnim, + struct bfad_itnim_s **itnim_drv) +{ + *itnim_drv = kzalloc(sizeof(struct bfad_itnim_s), GFP_ATOMIC); + if (*itnim_drv == NULL) + return; + + (*itnim_drv)->im = bfad->im; + *itnim = &(*itnim_drv)->fcs_itnim; + (*itnim_drv)->state = ITNIM_STATE_NONE; + + /* + * Initiaze the itnim_work + */ + INIT_WORK(&(*itnim_drv)->itnim_work, bfad_im_itnim_work_handler); + bfad->bfad_flags |= BFAD_RPORT_ONLINE; +} + +/** + * BFA FCS itnim free callback. + * Context: Interrupt. bfad_lock is held + */ +void +bfa_fcb_itnim_free(struct bfad_s *bfad, struct bfad_itnim_s *itnim_drv) +{ + struct bfad_port_s *port; + wwn_t wwpn; + u32 fcid; + char wwpn_str[32], fcid_str[16]; + + /* online to free state transtion should not happen */ + bfa_assert(itnim_drv->state != ITNIM_STATE_ONLINE); + + itnim_drv->queue_work = 1; + /* offline request is not yet done, use the same request to free */ + if (itnim_drv->state == ITNIM_STATE_OFFLINE_PENDING) + itnim_drv->queue_work = 0; + + itnim_drv->state = ITNIM_STATE_FREE; + port = bfa_fcs_itnim_get_drvport(&itnim_drv->fcs_itnim); + itnim_drv->im_port = port->im_port; + wwpn = bfa_fcs_itnim_get_pwwn(&itnim_drv->fcs_itnim); + fcid = bfa_fcs_itnim_get_fcid(&itnim_drv->fcs_itnim); + wwn2str(wwpn_str, wwpn); + fcid2str(fcid_str, fcid); + bfa_log(bfad->logmod, BFA_LOG_LINUX_ITNIM_FREE, + port->im_port->shost->host_no, + fcid_str, wwpn_str); + bfad_os_itnim_process(itnim_drv); +} + +/** + * BFA FCS itnim online callback. + * Context: Interrupt. bfad_lock is held + */ +void +bfa_fcb_itnim_online(struct bfad_itnim_s *itnim_drv) +{ + struct bfad_port_s *port; + + itnim_drv->bfa_itnim = bfa_fcs_itnim_get_halitn(&itnim_drv->fcs_itnim); + port = bfa_fcs_itnim_get_drvport(&itnim_drv->fcs_itnim); + itnim_drv->state = ITNIM_STATE_ONLINE; + itnim_drv->queue_work = 1; + itnim_drv->im_port = port->im_port; + bfad_os_itnim_process(itnim_drv); +} + +/** + * BFA FCS itnim offline callback. + * Context: Interrupt. bfad_lock is held + */ +void +bfa_fcb_itnim_offline(struct bfad_itnim_s *itnim_drv) +{ + struct bfad_port_s *port; + struct bfad_s *bfad; + + port = bfa_fcs_itnim_get_drvport(&itnim_drv->fcs_itnim); + bfad = port->bfad; + if ((bfad->pport.flags & BFAD_PORT_DELETE) || + (port->flags & BFAD_PORT_DELETE)) { + itnim_drv->state = ITNIM_STATE_OFFLINE; + return; + } + itnim_drv->im_port = port->im_port; + itnim_drv->state = ITNIM_STATE_OFFLINE_PENDING; + itnim_drv->queue_work = 1; + bfad_os_itnim_process(itnim_drv); +} + +/** + * BFA FCS itnim timeout callback. + * Context: Interrupt. bfad_lock is held + */ +void bfa_fcb_itnim_tov(struct bfad_itnim_s *itnim) +{ + itnim->state = ITNIM_STATE_TIMEOUT; +} + +/** + * Path TOV processing begin notification -- dummy for linux + */ +void +bfa_fcb_itnim_tov_begin(struct bfad_itnim_s *itnim) +{ +} + + + +/** + * Allocate a Scsi_Host for a port. + */ +int +bfad_im_scsi_host_alloc(struct bfad_s *bfad, struct bfad_im_port_s *im_port) +{ + int error = 1; + + if (!idr_pre_get(&bfad_im_port_index, GFP_KERNEL)) { + printk(KERN_WARNING "idr_pre_get failure\n"); + goto out; + } + + error = idr_get_new(&bfad_im_port_index, im_port, + &im_port->idr_id); + if (error) { + printk(KERN_WARNING "idr_get_new failure\n"); + goto out; + } + + im_port->shost = bfad_os_scsi_host_alloc(im_port, bfad); + if (!im_port->shost) { + error = 1; + goto out_free_idr; + } + + im_port->shost->hostdata[0] = (unsigned long)im_port; + im_port->shost->unique_id = im_port->idr_id; + im_port->shost->this_id = -1; + im_port->shost->max_id = MAX_FCP_TARGET; + im_port->shost->max_lun = MAX_FCP_LUN; + im_port->shost->max_cmd_len = 16; + im_port->shost->can_queue = bfad->cfg_data.ioc_queue_depth; + im_port->shost->transportt = bfad_im_scsi_transport_template; + + error = bfad_os_scsi_add_host(im_port->shost, im_port, bfad); + if (error) { + printk(KERN_WARNING "bfad_os_scsi_add_host failure %d\n", + error); + goto out_fc_rel; + } + + /* setup host fixed attribute if the lk supports */ + bfad_os_fc_host_init(im_port); + + return 0; + +out_fc_rel: + scsi_host_put(im_port->shost); +out_free_idr: + idr_remove(&bfad_im_port_index, im_port->idr_id); +out: + return error; +} + +void +bfad_im_scsi_host_free(struct bfad_s *bfad, struct bfad_im_port_s *im_port) +{ + unsigned long flags; + + bfa_trc(bfad, bfad->inst_no); + bfa_log(bfad->logmod, BFA_LOG_LINUX_SCSI_HOST_FREE, + im_port->shost->host_no); + + fc_remove_host(im_port->shost); + + scsi_remove_host(im_port->shost); + scsi_host_put(im_port->shost); + + spin_lock_irqsave(&bfad->bfad_lock, flags); + idr_remove(&bfad_im_port_index, im_port->idr_id); + spin_unlock_irqrestore(&bfad->bfad_lock, flags); +} + +static void +bfad_im_port_delete_handler(struct work_struct *work) +{ + struct bfad_im_port_s *im_port = + container_of(work, struct bfad_im_port_s, port_delete_work); + + bfad_im_scsi_host_free(im_port->bfad, im_port); + bfad_im_port_clean(im_port); + kfree(im_port); +} + +bfa_status_t +bfad_im_port_new(struct bfad_s *bfad, struct bfad_port_s *port) +{ + int rc = BFA_STATUS_OK; + struct bfad_im_port_s *im_port; + + im_port = kzalloc(sizeof(struct bfad_im_port_s), GFP_ATOMIC); + if (im_port == NULL) { + rc = BFA_STATUS_ENOMEM; + goto ext; + } + port->im_port = im_port; + im_port->port = port; + im_port->bfad = bfad; + + INIT_WORK(&im_port->port_delete_work, bfad_im_port_delete_handler); + INIT_LIST_HEAD(&im_port->itnim_mapped_list); + INIT_LIST_HEAD(&im_port->binding_list); + +ext: + return rc; +} + +void +bfad_im_port_delete(struct bfad_s *bfad, struct bfad_port_s *port) +{ + struct bfad_im_port_s *im_port = port->im_port; + + queue_work(bfad->im->drv_workq, + &im_port->port_delete_work); +} + +void +bfad_im_port_clean(struct bfad_im_port_s *im_port) +{ + struct bfad_fcp_binding *bp, *bp_new; + unsigned long flags; + struct bfad_s *bfad = im_port->bfad; + + spin_lock_irqsave(&bfad->bfad_lock, flags); + list_for_each_entry_safe(bp, bp_new, &im_port->binding_list, + list_entry) { + list_del(&bp->list_entry); + kfree(bp); + } + + /* the itnim_mapped_list must be empty at this time */ + bfa_assert(list_empty(&im_port->itnim_mapped_list)); + + spin_unlock_irqrestore(&bfad->bfad_lock, flags); +} + +void +bfad_im_port_online(struct bfad_s *bfad, struct bfad_port_s *port) +{ +} + +void +bfad_im_port_offline(struct bfad_s *bfad, struct bfad_port_s *port) +{ +} + +bfa_status_t +bfad_im_probe(struct bfad_s *bfad) +{ + struct bfad_im_s *im; + bfa_status_t rc = BFA_STATUS_OK; + + im = kzalloc(sizeof(struct bfad_im_s), GFP_KERNEL); + if (im == NULL) { + rc = BFA_STATUS_ENOMEM; + goto ext; + } + + bfad->im = im; + im->bfad = bfad; + + if (bfad_os_thread_workq(bfad) != BFA_STATUS_OK) { + kfree(im); + rc = BFA_STATUS_FAILED; + } + +ext: + return rc; +} + +void +bfad_im_probe_undo(struct bfad_s *bfad) +{ + if (bfad->im) { + bfad_os_destroy_workq(bfad->im); + kfree(bfad->im); + bfad->im = NULL; + } +} + + + + +int +bfad_os_scsi_add_host(struct Scsi_Host *shost, struct bfad_im_port_s *im_port, + struct bfad_s *bfad) +{ + struct device *dev; + + if (im_port->port->pvb_type == BFAD_PORT_PHYS_BASE) + dev = &bfad->pcidev->dev; + else + dev = &bfad->pport.im_port->shost->shost_gendev; + + return scsi_add_host(shost, dev); +} + +struct Scsi_Host * +bfad_os_scsi_host_alloc(struct bfad_im_port_s *im_port, struct bfad_s *bfad) +{ + struct scsi_host_template *sht; + + if (im_port->port->pvb_type == BFAD_PORT_PHYS_BASE) + sht = &bfad_im_scsi_host_template; + else + sht = &bfad_im_vport_template; + + sht->sg_tablesize = bfad->cfg_data.io_max_sge; + + return scsi_host_alloc(sht, sizeof(unsigned long)); +} + +void +bfad_os_scsi_host_free(struct bfad_s *bfad, struct bfad_im_port_s *im_port) +{ + flush_workqueue(bfad->im->drv_workq); + bfad_im_scsi_host_free(im_port->bfad, im_port); + bfad_im_port_clean(im_port); + kfree(im_port); +} + +void +bfad_os_destroy_workq(struct bfad_im_s *im) +{ + if (im && im->drv_workq) { + destroy_workqueue(im->drv_workq); + im->drv_workq = NULL; + } +} + +bfa_status_t +bfad_os_thread_workq(struct bfad_s *bfad) +{ + struct bfad_im_s *im = bfad->im; + + bfa_trc(bfad, 0); + snprintf(im->drv_workq_name, BFAD_KOBJ_NAME_LEN, "bfad_wq_%d", + bfad->inst_no); + im->drv_workq = create_singlethread_workqueue(im->drv_workq_name); + if (!im->drv_workq) + return BFA_STATUS_FAILED; + + return BFA_STATUS_OK; +} + +/** + * Scsi_Host template entry. + * + * Description: + * OS entry point to adjust the queue_depths on a per-device basis. + * Called once per device during the bus scan. + * Return non-zero if fails. + */ +static int +bfad_im_slave_configure(struct scsi_device *sdev) +{ + if (sdev->tagged_supported) + scsi_activate_tcq(sdev, bfa_lun_queue_depth); + else + scsi_deactivate_tcq(sdev, bfa_lun_queue_depth); + + return 0; +} + +struct scsi_host_template bfad_im_scsi_host_template = { + .module = THIS_MODULE, + .name = BFAD_DRIVER_NAME, + .info = bfad_im_info, + .queuecommand = bfad_im_queuecommand, + .eh_abort_handler = bfad_im_abort_handler, + .eh_device_reset_handler = bfad_im_reset_lun_handler, + .eh_bus_reset_handler = bfad_im_reset_bus_handler, + + .slave_alloc = bfad_im_slave_alloc, + .slave_configure = bfad_im_slave_configure, + .slave_destroy = bfad_im_slave_destroy, + + .this_id = -1, + .sg_tablesize = BFAD_IO_MAX_SGE, + .cmd_per_lun = 3, + .use_clustering = ENABLE_CLUSTERING, + .shost_attrs = bfad_im_host_attrs, + .max_sectors = 0xFFFF, +}; + +struct scsi_host_template bfad_im_vport_template = { + .module = THIS_MODULE, + .name = BFAD_DRIVER_NAME, + .info = bfad_im_info, + .queuecommand = bfad_im_queuecommand, + .eh_abort_handler = bfad_im_abort_handler, + .eh_device_reset_handler = bfad_im_reset_lun_handler, + .eh_bus_reset_handler = bfad_im_reset_bus_handler, + + .slave_alloc = bfad_im_slave_alloc, + .slave_configure = bfad_im_slave_configure, + .slave_destroy = bfad_im_slave_destroy, + + .this_id = -1, + .sg_tablesize = BFAD_IO_MAX_SGE, + .cmd_per_lun = 3, + .use_clustering = ENABLE_CLUSTERING, + .shost_attrs = bfad_im_vport_attrs, + .max_sectors = 0xFFFF, +}; + +void +bfad_im_probe_post(struct bfad_im_s *im) +{ + flush_workqueue(im->drv_workq); +} + +bfa_status_t +bfad_im_module_init(void) +{ + bfad_im_scsi_transport_template = + fc_attach_transport(&bfad_im_fc_function_template); + if (!bfad_im_scsi_transport_template) + return BFA_STATUS_ENOMEM; + + return BFA_STATUS_OK; +} + +void +bfad_im_module_exit(void) +{ + if (bfad_im_scsi_transport_template) + fc_release_transport(bfad_im_scsi_transport_template); +} + +void +bfad_os_itnim_process(struct bfad_itnim_s *itnim_drv) +{ + struct bfad_im_s *im = itnim_drv->im; + + if (itnim_drv->queue_work) + queue_work(im->drv_workq, &itnim_drv->itnim_work); +} + +void +bfad_os_ramp_up_qdepth(struct bfad_itnim_s *itnim, struct scsi_device *sdev) +{ + struct scsi_device *tmp_sdev; + + if (((jiffies - itnim->last_ramp_up_time) > + BFA_QUEUE_FULL_RAMP_UP_TIME * HZ) && + ((jiffies - itnim->last_queue_full_time) > + BFA_QUEUE_FULL_RAMP_UP_TIME * HZ)) { + shost_for_each_device(tmp_sdev, sdev->host) { + if (bfa_lun_queue_depth > tmp_sdev->queue_depth) { + if (tmp_sdev->id != sdev->id) + continue; + if (tmp_sdev->ordered_tags) + scsi_adjust_queue_depth(tmp_sdev, + MSG_ORDERED_TAG, + tmp_sdev->queue_depth + 1); + else + scsi_adjust_queue_depth(tmp_sdev, + MSG_SIMPLE_TAG, + tmp_sdev->queue_depth + 1); + + itnim->last_ramp_up_time = jiffies; + } + } + } +} + +void +bfad_os_handle_qfull(struct bfad_itnim_s *itnim, struct scsi_device *sdev) +{ + struct scsi_device *tmp_sdev; + + itnim->last_queue_full_time = jiffies; + + shost_for_each_device(tmp_sdev, sdev->host) { + if (tmp_sdev->id != sdev->id) + continue; + scsi_track_queue_full(tmp_sdev, tmp_sdev->queue_depth - 1); + } +} + + + + +struct bfad_itnim_s * +bfad_os_get_itnim(struct bfad_im_port_s *im_port, int id) +{ + struct bfad_itnim_s *itnim = NULL; + + /* Search the mapped list for this target ID */ + list_for_each_entry(itnim, &im_port->itnim_mapped_list, list_entry) { + if (id == itnim->scsi_tgt_id) + return itnim; + } + + return NULL; +} + +/** + * Scsi_Host template entry slave_alloc + */ +static int +bfad_im_slave_alloc(struct scsi_device *sdev) +{ + struct fc_rport *rport = starget_to_rport(scsi_target(sdev)); + + if (!rport || fc_remote_port_chkready(rport)) + return -ENXIO; + + sdev->hostdata = rport->dd_data; + + return 0; +} + +void +bfad_os_fc_host_init(struct bfad_im_port_s *im_port) +{ + struct Scsi_Host *host = im_port->shost; + struct bfad_s *bfad = im_port->bfad; + struct bfad_port_s *port = im_port->port; + union attr { + struct bfa_pport_attr_s pattr; + struct bfa_ioc_attr_s ioc_attr; + } attr; + + fc_host_node_name(host) = + bfa_os_htonll((bfa_fcs_port_get_nwwn(port->fcs_port))); + fc_host_port_name(host) = + bfa_os_htonll((bfa_fcs_port_get_pwwn(port->fcs_port))); + + fc_host_supported_classes(host) = FC_COS_CLASS3; + + memset(fc_host_supported_fc4s(host), 0, + sizeof(fc_host_supported_fc4s(host))); + if (bfad_supported_fc4s & (BFA_PORT_ROLE_FCP_IM | BFA_PORT_ROLE_FCP_TM)) + /* For FCP type 0x08 */ + fc_host_supported_fc4s(host)[2] = 1; + if (bfad_supported_fc4s & BFA_PORT_ROLE_FCP_IPFC) + /* For LLC/SNAP type 0x05 */ + fc_host_supported_fc4s(host)[3] = 0x20; + /* For fibre channel services type 0x20 */ + fc_host_supported_fc4s(host)[7] = 1; + + memset(&attr.ioc_attr, 0, sizeof(attr.ioc_attr)); + bfa_get_attr(&bfad->bfa, &attr.ioc_attr); + sprintf(fc_host_symbolic_name(host), "Brocade %s FV%s DV%s", + attr.ioc_attr.adapter_attr.model, + attr.ioc_attr.adapter_attr.fw_ver, BFAD_DRIVER_VERSION); + + fc_host_supported_speeds(host) = 0; + fc_host_supported_speeds(host) |= + FC_PORTSPEED_8GBIT | FC_PORTSPEED_4GBIT | FC_PORTSPEED_2GBIT | + FC_PORTSPEED_1GBIT; + + memset(&attr.pattr, 0, sizeof(attr.pattr)); + bfa_pport_get_attr(&bfad->bfa, &attr.pattr); + fc_host_maxframe_size(host) = attr.pattr.pport_cfg.maxfrsize; +} + +static void +bfad_im_fc_rport_add(struct bfad_im_port_s *im_port, struct bfad_itnim_s *itnim) +{ + struct fc_rport_identifiers rport_ids; + struct fc_rport *fc_rport; + struct bfad_itnim_data_s *itnim_data; + + rport_ids.node_name = + bfa_os_htonll(bfa_fcs_itnim_get_nwwn(&itnim->fcs_itnim)); + rport_ids.port_name = + bfa_os_htonll(bfa_fcs_itnim_get_pwwn(&itnim->fcs_itnim)); + rport_ids.port_id = + bfa_os_hton3b(bfa_fcs_itnim_get_fcid(&itnim->fcs_itnim)); + rport_ids.roles = FC_RPORT_ROLE_UNKNOWN; + + itnim->fc_rport = fc_rport = + fc_remote_port_add(im_port->shost, 0, &rport_ids); + + if (!fc_rport) + return; + + fc_rport->maxframe_size = + bfa_fcs_itnim_get_maxfrsize(&itnim->fcs_itnim); + fc_rport->supported_classes = bfa_fcs_itnim_get_cos(&itnim->fcs_itnim); + + itnim_data = fc_rport->dd_data; + itnim_data->itnim = itnim; + + rport_ids.roles |= FC_RPORT_ROLE_FCP_TARGET; + + if (rport_ids.roles != FC_RPORT_ROLE_UNKNOWN) + fc_remote_port_rolechg(fc_rport, rport_ids.roles); + + if ((fc_rport->scsi_target_id != -1) + && (fc_rport->scsi_target_id < MAX_FCP_TARGET)) + itnim->scsi_tgt_id = fc_rport->scsi_target_id; + + return; +} + +/** + * Work queue handler using FC transport service +* Context: kernel + */ +static void +bfad_im_itnim_work_handler(struct work_struct *work) +{ + struct bfad_itnim_s *itnim = container_of(work, struct bfad_itnim_s, + itnim_work); + struct bfad_im_s *im = itnim->im; + struct bfad_s *bfad = im->bfad; + struct bfad_im_port_s *im_port; + unsigned long flags; + struct fc_rport *fc_rport; + wwn_t wwpn; + u32 fcid; + char wwpn_str[32], fcid_str[16]; + + spin_lock_irqsave(&bfad->bfad_lock, flags); + im_port = itnim->im_port; + bfa_trc(bfad, itnim->state); + switch (itnim->state) { + case ITNIM_STATE_ONLINE: + if (!itnim->fc_rport) { + spin_unlock_irqrestore(&bfad->bfad_lock, flags); + bfad_im_fc_rport_add(im_port, itnim); + spin_lock_irqsave(&bfad->bfad_lock, flags); + wwpn = bfa_fcs_itnim_get_pwwn(&itnim->fcs_itnim); + fcid = bfa_fcs_itnim_get_fcid(&itnim->fcs_itnim); + wwn2str(wwpn_str, wwpn); + fcid2str(fcid_str, fcid); + list_add_tail(&itnim->list_entry, + &im_port->itnim_mapped_list); + bfa_log(bfad->logmod, BFA_LOG_LINUX_ITNIM_ONLINE, + im_port->shost->host_no, + itnim->scsi_tgt_id, + fcid_str, wwpn_str); + } else { + printk(KERN_WARNING + "%s: itnim %llx is already in online state\n", + __FUNCTION__, + bfa_fcs_itnim_get_pwwn(&itnim->fcs_itnim)); + } + + break; + case ITNIM_STATE_OFFLINE_PENDING: + itnim->state = ITNIM_STATE_OFFLINE; + if (itnim->fc_rport) { + fc_rport = itnim->fc_rport; + ((struct bfad_itnim_data_s *) + fc_rport->dd_data)->itnim = NULL; + itnim->fc_rport = NULL; + if (!(im_port->port->flags & BFAD_PORT_DELETE)) { + spin_unlock_irqrestore(&bfad->bfad_lock, flags); + fc_rport->dev_loss_tmo = + bfa_fcpim_path_tov_get(&bfad->bfa) + 1; + fc_remote_port_delete(fc_rport); + spin_lock_irqsave(&bfad->bfad_lock, flags); + } + wwpn = bfa_fcs_itnim_get_pwwn(&itnim->fcs_itnim); + fcid = bfa_fcs_itnim_get_fcid(&itnim->fcs_itnim); + wwn2str(wwpn_str, wwpn); + fcid2str(fcid_str, fcid); + list_del(&itnim->list_entry); + bfa_log(bfad->logmod, BFA_LOG_LINUX_ITNIM_OFFLINE, + im_port->shost->host_no, + itnim->scsi_tgt_id, + fcid_str, wwpn_str); + } + break; + case ITNIM_STATE_FREE: + if (itnim->fc_rport) { + fc_rport = itnim->fc_rport; + ((struct bfad_itnim_data_s *) + fc_rport->dd_data)->itnim = NULL; + itnim->fc_rport = NULL; + if (!(im_port->port->flags & BFAD_PORT_DELETE)) { + spin_unlock_irqrestore(&bfad->bfad_lock, flags); + fc_rport->dev_loss_tmo = + bfa_fcpim_path_tov_get(&bfad->bfa) + 1; + fc_remote_port_delete(fc_rport); + spin_lock_irqsave(&bfad->bfad_lock, flags); + } + list_del(&itnim->list_entry); + } + + kfree(itnim); + break; + default: + bfa_assert(0); + break; + } + + spin_unlock_irqrestore(&bfad->bfad_lock, flags); +} + +/** + * Scsi_Host template entry, queue a SCSI command to the BFAD. + */ +static int +bfad_im_queuecommand(struct scsi_cmnd *cmnd, void (*done) (struct scsi_cmnd *)) +{ + struct bfad_im_port_s *im_port = + (struct bfad_im_port_s *) cmnd->device->host->hostdata[0]; + struct bfad_s *bfad = im_port->bfad; + struct bfad_itnim_data_s *itnim_data = cmnd->device->hostdata; + struct bfad_itnim_s *itnim; + struct bfa_ioim_s *hal_io; + unsigned long flags; + int rc; + s16 sg_cnt = 0; + struct fc_rport *rport = starget_to_rport(scsi_target(cmnd->device)); + + rc = fc_remote_port_chkready(rport); + if (rc) { + cmnd->result = rc; + done(cmnd); + return 0; + } + + sg_cnt = scsi_dma_map(cmnd); + + if (sg_cnt < 0) + return SCSI_MLQUEUE_HOST_BUSY; + + cmnd->scsi_done = done; + + spin_lock_irqsave(&bfad->bfad_lock, flags); + if (!(bfad->bfad_flags & BFAD_HAL_START_DONE)) { + printk(KERN_WARNING + "bfad%d, queuecommand %p %x failed, BFA stopped\n", + bfad->inst_no, cmnd, cmnd->cmnd[0]); + cmnd->result = ScsiResult(DID_NO_CONNECT, 0); + goto out_fail_cmd; + } + + itnim = itnim_data->itnim; + if (!itnim) { + cmnd->result = ScsiResult(DID_IMM_RETRY, 0); + goto out_fail_cmd; + } + + hal_io = bfa_ioim_alloc(&bfad->bfa, (struct bfad_ioim_s *) cmnd, + itnim->bfa_itnim, sg_cnt); + if (!hal_io) { + printk(KERN_WARNING "hal_io failure\n"); + spin_unlock_irqrestore(&bfad->bfad_lock, flags); + scsi_dma_unmap(cmnd); + return SCSI_MLQUEUE_HOST_BUSY; + } + + cmnd->host_scribble = (char *)hal_io; + bfa_trc_fp(bfad, hal_io->iotag); + bfa_ioim_start(hal_io); + spin_unlock_irqrestore(&bfad->bfad_lock, flags); + + return 0; + +out_fail_cmd: + spin_unlock_irqrestore(&bfad->bfad_lock, flags); + scsi_dma_unmap(cmnd); + if (done) + done(cmnd); + + return 0; +} + +void +bfad_os_rport_online_wait(struct bfad_s *bfad) +{ + int i; + int rport_delay = 10; + + for (i = 0; !(bfad->bfad_flags & BFAD_PORT_ONLINE) + && i < bfa_linkup_delay; i++) + schedule_timeout_uninterruptible(HZ); + + if (bfad->bfad_flags & BFAD_PORT_ONLINE) { + rport_delay = rport_delay < bfa_linkup_delay ? + rport_delay : bfa_linkup_delay; + for (i = 0; !(bfad->bfad_flags & BFAD_RPORT_ONLINE) + && i < rport_delay; i++) + schedule_timeout_uninterruptible(HZ); + + if (rport_delay > 0 && (bfad->bfad_flags & BFAD_RPORT_ONLINE)) + schedule_timeout_uninterruptible(rport_delay * HZ); + } +} + +int +bfad_os_get_linkup_delay(struct bfad_s *bfad) +{ + + u8 nwwns = 0; + wwn_t *wwns; + int ldelay; + + /* + * Querying for the boot target port wwns + * -- read from boot information in flash. + * If nwwns > 0 => boot over SAN and set bfa_linkup_delay = 30 + * else => local boot machine set bfa_linkup_delay = 10 + */ + + bfa_iocfc_get_bootwwns(&bfad->bfa, &nwwns, &wwns); + + if (nwwns > 0) { + /* If boot over SAN; linkup_delay = 30sec */ + ldelay = 30; + } else { + /* If local boot; linkup_delay = 10sec */ + ldelay = 0; + } + + return ldelay; +} + + diff --git a/drivers/scsi/bfa/bfad_im.h b/drivers/scsi/bfa/bfad_im.h new file mode 100644 index 000000000000..189a5b29e21a --- /dev/null +++ b/drivers/scsi/bfa/bfad_im.h @@ -0,0 +1,150 @@ +/* + * Copyright (c) 2005-2009 Brocade Communications Systems, Inc. + * All rights reserved + * www.brocade.com + * + * Linux driver for Brocade Fibre Channel Host Bus Adapter. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (GPL) Version 2 as + * published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#ifndef __BFAD_IM_H__ +#define __BFAD_IM_H__ + +#include "fcs/bfa_fcs_fcpim.h" +#include "bfad_im_compat.h" + +#define FCPI_NAME " fcpim" + +void bfad_flags_set(struct bfad_s *bfad, u32 flags); +bfa_status_t bfad_im_module_init(void); +void bfad_im_module_exit(void); +bfa_status_t bfad_im_probe(struct bfad_s *bfad); +void bfad_im_probe_undo(struct bfad_s *bfad); +void bfad_im_probe_post(struct bfad_im_s *im); +bfa_status_t bfad_im_port_new(struct bfad_s *bfad, struct bfad_port_s *port); +void bfad_im_port_delete(struct bfad_s *bfad, struct bfad_port_s *port); +void bfad_im_port_online(struct bfad_s *bfad, struct bfad_port_s *port); +void bfad_im_port_offline(struct bfad_s *bfad, struct bfad_port_s *port); +void bfad_im_port_clean(struct bfad_im_port_s *im_port); +int bfad_im_scsi_host_alloc(struct bfad_s *bfad, + struct bfad_im_port_s *im_port); +void bfad_im_scsi_host_free(struct bfad_s *bfad, + struct bfad_im_port_s *im_port); + +#define MAX_FCP_TARGET 1024 +#define MAX_FCP_LUN 16384 +#define BFAD_TARGET_RESET_TMO 60 +#define BFAD_LUN_RESET_TMO 60 +#define ScsiResult(host_code, scsi_code) (((host_code) << 16) | scsi_code) +#define BFA_QUEUE_FULL_RAMP_UP_TIME 120 +#define BFAD_KOBJ_NAME_LEN 20 + +/* + * itnim flags + */ +#define ITNIM_MAPPED 0x00000001 + +#define SCSI_TASK_MGMT 0x00000001 +#define IO_DONE_BIT 0 + +struct bfad_itnim_data_s { + struct bfad_itnim_s *itnim; +}; + +struct bfad_im_port_s { + struct bfad_s *bfad; + struct bfad_port_s *port; + struct work_struct port_delete_work; + int idr_id; + u16 cur_scsi_id; + struct list_head binding_list; + struct Scsi_Host *shost; + struct list_head itnim_mapped_list; +}; + +enum bfad_itnim_state { + ITNIM_STATE_NONE, + ITNIM_STATE_ONLINE, + ITNIM_STATE_OFFLINE_PENDING, + ITNIM_STATE_OFFLINE, + ITNIM_STATE_TIMEOUT, + ITNIM_STATE_FREE, +}; + +/* + * Per itnim data structure + */ +struct bfad_itnim_s { + struct list_head list_entry; + struct bfa_fcs_itnim_s fcs_itnim; + struct work_struct itnim_work; + u32 flags; + enum bfad_itnim_state state; + struct bfad_im_s *im; + struct bfad_im_port_s *im_port; + struct bfad_rport_s *drv_rport; + struct fc_rport *fc_rport; + struct bfa_itnim_s *bfa_itnim; + u16 scsi_tgt_id; + u16 queue_work; + unsigned long last_ramp_up_time; + unsigned long last_queue_full_time; +}; + +enum bfad_binding_type { + FCP_PWWN_BINDING = 0x1, + FCP_NWWN_BINDING = 0x2, + FCP_FCID_BINDING = 0x3, +}; + +struct bfad_fcp_binding { + struct list_head list_entry; + enum bfad_binding_type binding_type; + u16 scsi_target_id; + u32 fc_id; + wwn_t nwwn; + wwn_t pwwn; +}; + +struct bfad_im_s { + struct bfad_s *bfad; + struct workqueue_struct *drv_workq; + char drv_workq_name[BFAD_KOBJ_NAME_LEN]; +}; + +struct Scsi_Host *bfad_os_scsi_host_alloc(struct bfad_im_port_s *im_port, + struct bfad_s *); +bfa_status_t bfad_os_thread_workq(struct bfad_s *bfad); +void bfad_os_destroy_workq(struct bfad_im_s *im); +void bfad_os_itnim_process(struct bfad_itnim_s *itnim_drv); +void bfad_os_fc_host_init(struct bfad_im_port_s *im_port); +void bfad_os_init_work(struct bfad_im_port_s *im_port); +void bfad_os_scsi_host_free(struct bfad_s *bfad, + struct bfad_im_port_s *im_port); +void bfad_os_ramp_up_qdepth(struct bfad_itnim_s *itnim, + struct scsi_device *sdev); +void bfad_os_handle_qfull(struct bfad_itnim_s *itnim, struct scsi_device *sdev); +struct bfad_itnim_s *bfad_os_get_itnim(struct bfad_im_port_s *im_port, int id); +int bfad_os_scsi_add_host(struct Scsi_Host *shost, + struct bfad_im_port_s *im_port, struct bfad_s *bfad); + +/* + * scsi_host_template entries + */ +void bfad_im_itnim_unmap(struct bfad_im_port_s *im_port, + struct bfad_itnim_s *itnim); + +extern struct scsi_host_template bfad_im_scsi_host_template; +extern struct scsi_host_template bfad_im_vport_template; +extern struct fc_function_template bfad_im_fc_function_template; +extern struct scsi_transport_template *bfad_im_scsi_transport_template; + +#endif diff --git a/drivers/scsi/bfa/bfad_im_compat.h b/drivers/scsi/bfa/bfad_im_compat.h new file mode 100644 index 000000000000..1d3e74ec338c --- /dev/null +++ b/drivers/scsi/bfa/bfad_im_compat.h @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2005-2009 Brocade Communications Systems, Inc. + * All rights reserved + * www.brocade.com + * + * Linux driver for Brocade Fibre Channel Host Bus Adapter. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (GPL) Version 2 as + * published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#ifndef __BFAD_IM_COMPAT_H__ +#define __BFAD_IM_COMPAT_H__ + +extern u32 *bfi_image_buf; +extern u32 bfi_image_size; + +extern struct device_attribute *bfad_im_host_attrs[]; +extern struct device_attribute *bfad_im_vport_attrs[]; + +u32 *bfad_get_firmware_buf(struct pci_dev *pdev); +u32 *bfad_read_firmware(struct pci_dev *pdev, u32 **bfi_image, + u32 *bfi_image_size, char *fw_name); + +static inline u32 * +bfad_load_fwimg(struct pci_dev *pdev) +{ + return(bfad_get_firmware_buf(pdev)); +} + +static inline void +bfad_free_fwimg(void) +{ + if (bfi_image_ct_size && bfi_image_ct) + vfree(bfi_image_ct); + if (bfi_image_cb_size && bfi_image_cb) + vfree(bfi_image_cb); +} + +#endif diff --git a/drivers/scsi/bfa/bfad_intr.c b/drivers/scsi/bfa/bfad_intr.c new file mode 100644 index 000000000000..f104e029cac9 --- /dev/null +++ b/drivers/scsi/bfa/bfad_intr.c @@ -0,0 +1,214 @@ +/* + * Copyright (c) 2005-2009 Brocade Communications Systems, Inc. + * All rights reserved + * www.brocade.com + * + * Linux driver for Brocade Fibre Channel Host Bus Adapter. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (GPL) Version 2 as + * published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#include "bfad_drv.h" +#include "bfad_trcmod.h" + +BFA_TRC_FILE(LDRV, INTR); + +/** + * bfa_isr BFA driver interrupt functions + */ +irqreturn_t bfad_intx(int irq, void *dev_id); +static int msix_disable; +module_param(msix_disable, int, S_IRUGO | S_IWUSR); +/** + * Line based interrupt handler. + */ +irqreturn_t +bfad_intx(int irq, void *dev_id) +{ + struct bfad_s *bfad = dev_id; + struct list_head doneq; + unsigned long flags; + bfa_boolean_t rc; + + spin_lock_irqsave(&bfad->bfad_lock, flags); + rc = bfa_intx(&bfad->bfa); + if (!rc) { + spin_unlock_irqrestore(&bfad->bfad_lock, flags); + return IRQ_NONE; + } + + bfa_comp_deq(&bfad->bfa, &doneq); + spin_unlock_irqrestore(&bfad->bfad_lock, flags); + + if (!list_empty(&doneq)) { + bfa_comp_process(&bfad->bfa, &doneq); + + spin_lock_irqsave(&bfad->bfad_lock, flags); + bfa_comp_free(&bfad->bfa, &doneq); + spin_unlock_irqrestore(&bfad->bfad_lock, flags); + bfa_trc_fp(bfad, irq); + } + + return IRQ_HANDLED; + +} + +static irqreturn_t +bfad_msix(int irq, void *dev_id) +{ + struct bfad_msix_s *vec = dev_id; + struct bfad_s *bfad = vec->bfad; + struct list_head doneq; + unsigned long flags; + + spin_lock_irqsave(&bfad->bfad_lock, flags); + + bfa_msix(&bfad->bfa, vec->msix.entry); + bfa_comp_deq(&bfad->bfa, &doneq); + spin_unlock_irqrestore(&bfad->bfad_lock, flags); + + if (!list_empty(&doneq)) { + bfa_comp_process(&bfad->bfa, &doneq); + + spin_lock_irqsave(&bfad->bfad_lock, flags); + bfa_comp_free(&bfad->bfa, &doneq); + spin_unlock_irqrestore(&bfad->bfad_lock, flags); + } + + return IRQ_HANDLED; +} + +/** + * Initialize the MSIX entry table. + */ +static void +bfad_init_msix_entry(struct bfad_s *bfad, struct msix_entry *msix_entries, + int mask, int max_bit) +{ + int i; + int match = 0x00000001; + + for (i = 0, bfad->nvec = 0; i < MAX_MSIX_ENTRY; i++) { + if (mask & match) { + bfad->msix_tab[bfad->nvec].msix.entry = i; + bfad->msix_tab[bfad->nvec].bfad = bfad; + msix_entries[bfad->nvec].entry = i; + bfad->nvec++; + } + + match <<= 1; + } + +} + +int +bfad_install_msix_handler(struct bfad_s *bfad) +{ + int i, error = 0; + + for (i = 0; i < bfad->nvec; i++) { + error = request_irq(bfad->msix_tab[i].msix.vector, + (irq_handler_t) bfad_msix, 0, + BFAD_DRIVER_NAME, &bfad->msix_tab[i]); + bfa_trc(bfad, i); + bfa_trc(bfad, bfad->msix_tab[i].msix.vector); + if (error) { + int j; + + for (j = 0; j < i; j++) + free_irq(bfad->msix_tab[j].msix.vector, + &bfad->msix_tab[j]); + + return 1; + } + } + + return 0; +} + +/** + * Setup MSIX based interrupt. + */ +int +bfad_setup_intr(struct bfad_s *bfad) +{ + int error = 0; + u32 mask = 0, i, num_bit = 0, max_bit = 0; + struct msix_entry msix_entries[MAX_MSIX_ENTRY]; + + /* Call BFA to get the msix map for this PCI function. */ + bfa_msix_getvecs(&bfad->bfa, &mask, &num_bit, &max_bit); + + /* Set up the msix entry table */ + bfad_init_msix_entry(bfad, msix_entries, mask, max_bit); + + if (!msix_disable) { + error = pci_enable_msix(bfad->pcidev, msix_entries, bfad->nvec); + if (error) { + /* + * Only error number of vector is available. + * We don't have a mechanism to map multiple + * interrupts into one vector, so even if we + * can try to request less vectors, we don't + * know how to associate interrupt events to + * vectors. Linux doesn't dupicate vectors + * in the MSIX table for this case. + */ + + printk(KERN_WARNING "bfad%d: " + "pci_enable_msix failed (%d)," + " use line based.\n", bfad->inst_no, error); + + goto line_based; + } + + /* Save the vectors */ + for (i = 0; i < bfad->nvec; i++) { + bfa_trc(bfad, msix_entries[i].vector); + bfad->msix_tab[i].msix.vector = msix_entries[i].vector; + } + + bfa_msix_init(&bfad->bfa, bfad->nvec); + + bfad->bfad_flags |= BFAD_MSIX_ON; + + return error; + } + +line_based: + error = 0; + if (request_irq + (bfad->pcidev->irq, (irq_handler_t) bfad_intx, BFAD_IRQ_FLAGS, + BFAD_DRIVER_NAME, bfad) != 0) { + /* Enable interrupt handler failed */ + return 1; + } + + return error; +} + +void +bfad_remove_intr(struct bfad_s *bfad) +{ + int i; + + if (bfad->bfad_flags & BFAD_MSIX_ON) { + for (i = 0; i < bfad->nvec; i++) + free_irq(bfad->msix_tab[i].msix.vector, + &bfad->msix_tab[i]); + + pci_disable_msix(bfad->pcidev); + bfad->bfad_flags &= ~BFAD_MSIX_ON; + } else { + free_irq(bfad->pcidev->irq, bfad); + } +} + + diff --git a/drivers/scsi/bfa/bfad_ipfc.h b/drivers/scsi/bfa/bfad_ipfc.h new file mode 100644 index 000000000000..718bc5227671 --- /dev/null +++ b/drivers/scsi/bfa/bfad_ipfc.h @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2005-2009 Brocade Communications Systems, Inc. + * All rights reserved + * www.brocade.com + * + * Linux driver for Brocade Fibre Channel Host Bus Adapter. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (GPL) Version 2 as + * published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ +#ifndef __BFA_DRV_IPFC_H__ +#define __BFA_DRV_IPFC_H__ + + +#define IPFC_NAME "" + +#define bfad_ipfc_module_init(x) do {} while (0) +#define bfad_ipfc_module_exit(x) do {} while (0) +#define bfad_ipfc_probe(x) do {} while (0) +#define bfad_ipfc_probe_undo(x) do {} while (0) +#define bfad_ipfc_port_config(x, y) BFA_STATUS_OK +#define bfad_ipfc_port_unconfig(x, y) do {} while (0) + +#define bfad_ipfc_probe_post(x) do {} while (0) +#define bfad_ipfc_port_new(x, y, z) BFA_STATUS_OK +#define bfad_ipfc_port_delete(x, y) do {} while (0) +#define bfad_ipfc_port_online(x, y) do {} while (0) +#define bfad_ipfc_port_offline(x, y) do {} while (0) + +#define bfad_ip_get_attr(x) BFA_STATUS_FAILED +#define bfad_ip_reset_drv_stats(x) BFA_STATUS_FAILED +#define bfad_ip_get_drv_stats(x, y) BFA_STATUS_FAILED +#define bfad_ip_enable_ipfc(x, y, z) BFA_STATUS_FAILED + + +#endif diff --git a/drivers/scsi/bfa/bfad_os.c b/drivers/scsi/bfa/bfad_os.c new file mode 100644 index 000000000000..faf47b4f1a38 --- /dev/null +++ b/drivers/scsi/bfa/bfad_os.c @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2005-2009 Brocade Communications Systems, Inc. + * All rights reserved + * www.brocade.com + * + * Linux driver for Brocade Fibre Channel Host Bus Adapter. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (GPL) Version 2 as + * published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +/** + * bfad_os.c Linux driver OS specific calls. + */ + +#include "bfa_os_inc.h" +#include "bfad_drv.h" + +void +bfa_os_gettimeofday(struct bfa_timeval_s *tv) +{ + struct timeval tmp_tv; + + do_gettimeofday(&tmp_tv); + tv->tv_sec = (u32) tmp_tv.tv_sec; + tv->tv_usec = (u32) tmp_tv.tv_usec; +} + +void +bfa_os_printf(struct bfa_log_mod_s *log_mod, u32 msg_id, + const char *fmt, ...) +{ + va_list ap; + #define BFA_STRING_256 256 + char tmp[BFA_STRING_256]; + + va_start(ap, fmt); + vsprintf(tmp, fmt, ap); + va_end(ap); + + printk(tmp); +} + + diff --git a/drivers/scsi/bfa/bfad_tm.h b/drivers/scsi/bfa/bfad_tm.h new file mode 100644 index 000000000000..4901b1b7df02 --- /dev/null +++ b/drivers/scsi/bfa/bfad_tm.h @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2005-2009 Brocade Communications Systems, Inc. + * All rights reserved + * www.brocade.com + * + * Linux driver for Brocade Fibre Channel Host Bus Adapter. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (GPL) Version 2 as + * published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +/* + * Brocade Fibre Channel HBA Linux Target Mode Driver + */ + +/** + * tm/dummy/bfad_tm.h BFA callback dummy header file for BFA Linux target mode PCI interface module. + */ + +#ifndef __BFAD_TM_H__ +#define __BFAD_TM_H__ + +#include <defs/bfa_defs_status.h> + +#define FCPT_NAME "" + +/* + * Called from base Linux driver on (De)Init events + */ + +/* attach tgt template with scst */ +#define bfad_tm_module_init() do {} while (0) + +/* detach/release tgt template */ +#define bfad_tm_module_exit() do {} while (0) + +#define bfad_tm_probe(x) do {} while (0) +#define bfad_tm_probe_undo(x) do {} while (0) +#define bfad_tm_probe_post(x) do {} while (0) + +/* + * Called by base Linux driver but triggered by BFA FCS on config events + */ +#define bfad_tm_port_new(x, y) BFA_STATUS_OK +#define bfad_tm_port_delete(x, y) do {} while (0) + +/* + * Called by base Linux driver but triggered by BFA FCS on PLOGI/O events + */ +#define bfad_tm_port_online(x, y) do {} while (0) +#define bfad_tm_port_offline(x, y) do {} while (0) + +#endif diff --git a/drivers/scsi/bfa/bfad_trcmod.h b/drivers/scsi/bfa/bfad_trcmod.h new file mode 100644 index 000000000000..2827b2acd041 --- /dev/null +++ b/drivers/scsi/bfa/bfad_trcmod.h @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2005-2009 Brocade Communications Systems, Inc. + * All rights reserved + * www.brocade.com + * + * Linux driver for Brocade Fibre Channel Host Bus Adapter. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (GPL) Version 2 as + * published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +/** + * bfad_trcmod.h Linux driver trace modules + */ + + +#ifndef __BFAD_TRCMOD_H__ +#define __BFAD_TRCMOD_H__ + +#include <cs/bfa_trc.h> + +/* + * !!! Only append to the enums defined here to avoid any versioning + * !!! needed between trace utility and driver version + */ +enum { + /* 2.6 Driver */ + BFA_TRC_LDRV_BFAD = 1, + BFA_TRC_LDRV_BFAD_2_6 = 2, + BFA_TRC_LDRV_BFAD_2_6_9 = 3, + BFA_TRC_LDRV_BFAD_2_6_10 = 4, + BFA_TRC_LDRV_INTR = 5, + BFA_TRC_LDRV_IOCTL = 6, + BFA_TRC_LDRV_OS = 7, + BFA_TRC_LDRV_IM = 8, + BFA_TRC_LDRV_IM_2_6 = 9, + BFA_TRC_LDRV_IM_2_6_9 = 10, + BFA_TRC_LDRV_IM_2_6_10 = 11, + BFA_TRC_LDRV_TM = 12, + BFA_TRC_LDRV_IPFC = 13, + BFA_TRC_LDRV_IM_2_4 = 14, + BFA_TRC_LDRV_IM_VMW = 15, + BFA_TRC_LDRV_IM_LT_2_6_10 = 16, +}; + +#endif /* __BFAD_TRCMOD_H__ */ diff --git a/drivers/scsi/bfa/fab.c b/drivers/scsi/bfa/fab.c new file mode 100644 index 000000000000..7e3a4d5d7bb4 --- /dev/null +++ b/drivers/scsi/bfa/fab.c @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2005-2009 Brocade Communications Systems, Inc. + * All rights reserved + * www.brocade.com + * + * Linux driver for Brocade Fibre Channel Host Bus Adapter. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (GPL) Version 2 as + * published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#include <bfa.h> +#include <bfa_svc.h> +#include "fcs_lport.h" +#include "fcs_rport.h" +#include "lport_priv.h" + +/** + * fab.c port fab implementation. + */ + +/** + * bfa_fcs_port_fab_public port fab public functions + */ + +/** + * Called by port to initialize fabric services of the base port. + */ +void +bfa_fcs_port_fab_init(struct bfa_fcs_port_s *port) +{ + bfa_fcs_port_ns_init(port); + bfa_fcs_port_scn_init(port); + bfa_fcs_port_ms_init(port); +} + +/** + * Called by port to notify transition to online state. + */ +void +bfa_fcs_port_fab_online(struct bfa_fcs_port_s *port) +{ + bfa_fcs_port_ns_online(port); + bfa_fcs_port_scn_online(port); +} + +/** + * Called by port to notify transition to offline state. + */ +void +bfa_fcs_port_fab_offline(struct bfa_fcs_port_s *port) +{ + bfa_fcs_port_ns_offline(port); + bfa_fcs_port_scn_offline(port); + bfa_fcs_port_ms_offline(port); +} diff --git a/drivers/scsi/bfa/fabric.c b/drivers/scsi/bfa/fabric.c new file mode 100644 index 000000000000..a8b14c47b009 --- /dev/null +++ b/drivers/scsi/bfa/fabric.c @@ -0,0 +1,1278 @@ +/* + * Copyright (c) 2005-2009 Brocade Communications Systems, Inc. + * All rights reserved + * www.brocade.com + * + * Linux driver for Brocade Fibre Channel Host Bus Adapter. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (GPL) Version 2 as + * published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +/** + * fabric.c Fabric module implementation. + */ + +#include "fcs_fabric.h" +#include "fcs_lport.h" +#include "fcs_vport.h" +#include "fcs_trcmod.h" +#include "fcs_fcxp.h" +#include "fcs_auth.h" +#include "fcs.h" +#include "fcbuild.h" +#include <log/bfa_log_fcs.h> +#include <aen/bfa_aen_port.h> +#include <bfa_svc.h> + +BFA_TRC_FILE(FCS, FABRIC); + +#define BFA_FCS_FABRIC_RETRY_DELAY (2000) /* Milliseconds */ +#define BFA_FCS_FABRIC_CLEANUP_DELAY (10000) /* Milliseconds */ + +#define bfa_fcs_fabric_set_opertype(__fabric) do { \ + if (bfa_pport_get_topology((__fabric)->fcs->bfa) \ + == BFA_PPORT_TOPOLOGY_P2P) \ + (__fabric)->oper_type = BFA_PPORT_TYPE_NPORT; \ + else \ + (__fabric)->oper_type = BFA_PPORT_TYPE_NLPORT; \ +} while (0) + +/* + * forward declarations + */ +static void bfa_fcs_fabric_init(struct bfa_fcs_fabric_s *fabric); +static void bfa_fcs_fabric_login(struct bfa_fcs_fabric_s *fabric); +static void bfa_fcs_fabric_notify_online(struct bfa_fcs_fabric_s *fabric); +static void bfa_fcs_fabric_notify_offline(struct bfa_fcs_fabric_s *fabric); +static void bfa_fcs_fabric_delay(void *cbarg); +static void bfa_fcs_fabric_delete(struct bfa_fcs_fabric_s *fabric); +static void bfa_fcs_fabric_delete_comp(void *cbarg); +static void bfa_fcs_fabric_process_uf(struct bfa_fcs_fabric_s *fabric, + struct fchs_s *fchs, u16 len); +static void bfa_fcs_fabric_process_flogi(struct bfa_fcs_fabric_s *fabric, + struct fchs_s *fchs, u16 len); +static void bfa_fcs_fabric_send_flogi_acc(struct bfa_fcs_fabric_s *fabric); +static void bfa_fcs_fabric_flogiacc_comp(void *fcsarg, + struct bfa_fcxp_s *fcxp, + void *cbarg, bfa_status_t status, + u32 rsp_len, + u32 resid_len, + struct fchs_s *rspfchs); +/** + * fcs_fabric_sm fabric state machine functions + */ + +/** + * Fabric state machine events + */ +enum bfa_fcs_fabric_event { + BFA_FCS_FABRIC_SM_CREATE = 1, /* fabric create from driver */ + BFA_FCS_FABRIC_SM_DELETE = 2, /* fabric delete from driver */ + BFA_FCS_FABRIC_SM_LINK_DOWN = 3, /* link down from port */ + BFA_FCS_FABRIC_SM_LINK_UP = 4, /* link up from port */ + BFA_FCS_FABRIC_SM_CONT_OP = 5, /* continue op from flogi/auth */ + BFA_FCS_FABRIC_SM_RETRY_OP = 6, /* continue op from flogi/auth */ + BFA_FCS_FABRIC_SM_NO_FABRIC = 7, /* no fabric from flogi/auth + */ + BFA_FCS_FABRIC_SM_PERF_EVFP = 8, /* perform EVFP from + *flogi/auth */ + BFA_FCS_FABRIC_SM_ISOLATE = 9, /* isolate from EVFP processing */ + BFA_FCS_FABRIC_SM_NO_TAGGING = 10,/* no VFT tagging from EVFP */ + BFA_FCS_FABRIC_SM_DELAYED = 11, /* timeout delay event */ + BFA_FCS_FABRIC_SM_AUTH_FAILED = 12, /* authentication failed */ + BFA_FCS_FABRIC_SM_AUTH_SUCCESS = 13, /* authentication successful + */ + BFA_FCS_FABRIC_SM_DELCOMP = 14, /* all vports deleted event */ + BFA_FCS_FABRIC_SM_LOOPBACK = 15, /* Received our own FLOGI */ + BFA_FCS_FABRIC_SM_START = 16, /* fabric delete from driver */ +}; + +static void bfa_fcs_fabric_sm_uninit(struct bfa_fcs_fabric_s *fabric, + enum bfa_fcs_fabric_event event); +static void bfa_fcs_fabric_sm_created(struct bfa_fcs_fabric_s *fabric, + enum bfa_fcs_fabric_event event); +static void bfa_fcs_fabric_sm_linkdown(struct bfa_fcs_fabric_s *fabric, + enum bfa_fcs_fabric_event event); +static void bfa_fcs_fabric_sm_flogi(struct bfa_fcs_fabric_s *fabric, + enum bfa_fcs_fabric_event event); +static void bfa_fcs_fabric_sm_flogi_retry(struct bfa_fcs_fabric_s *fabric, + enum bfa_fcs_fabric_event event); +static void bfa_fcs_fabric_sm_auth(struct bfa_fcs_fabric_s *fabric, + enum bfa_fcs_fabric_event event); +static void bfa_fcs_fabric_sm_auth_failed(struct bfa_fcs_fabric_s *fabric, + enum bfa_fcs_fabric_event event); +static void bfa_fcs_fabric_sm_loopback(struct bfa_fcs_fabric_s *fabric, + enum bfa_fcs_fabric_event event); +static void bfa_fcs_fabric_sm_nofabric(struct bfa_fcs_fabric_s *fabric, + enum bfa_fcs_fabric_event event); +static void bfa_fcs_fabric_sm_online(struct bfa_fcs_fabric_s *fabric, + enum bfa_fcs_fabric_event event); +static void bfa_fcs_fabric_sm_evfp(struct bfa_fcs_fabric_s *fabric, + enum bfa_fcs_fabric_event event); +static void bfa_fcs_fabric_sm_evfp_done(struct bfa_fcs_fabric_s *fabric, + enum bfa_fcs_fabric_event event); +static void bfa_fcs_fabric_sm_isolated(struct bfa_fcs_fabric_s *fabric, + enum bfa_fcs_fabric_event event); +static void bfa_fcs_fabric_sm_deleting(struct bfa_fcs_fabric_s *fabric, + enum bfa_fcs_fabric_event event); +/** + * Beginning state before fabric creation. + */ +static void +bfa_fcs_fabric_sm_uninit(struct bfa_fcs_fabric_s *fabric, + enum bfa_fcs_fabric_event event) +{ + bfa_trc(fabric->fcs, fabric->bport.port_cfg.pwwn); + bfa_trc(fabric->fcs, event); + + switch (event) { + case BFA_FCS_FABRIC_SM_CREATE: + bfa_sm_set_state(fabric, bfa_fcs_fabric_sm_created); + bfa_fcs_fabric_init(fabric); + bfa_fcs_lport_init(&fabric->bport, fabric->fcs, FC_VF_ID_NULL, + &fabric->bport.port_cfg, NULL); + break; + + case BFA_FCS_FABRIC_SM_LINK_UP: + case BFA_FCS_FABRIC_SM_LINK_DOWN: + break; + + default: + bfa_sm_fault(fabric->fcs, event); + } +} + +/** + * Beginning state before fabric creation. + */ +static void +bfa_fcs_fabric_sm_created(struct bfa_fcs_fabric_s *fabric, + enum bfa_fcs_fabric_event event) +{ + bfa_trc(fabric->fcs, fabric->bport.port_cfg.pwwn); + bfa_trc(fabric->fcs, event); + + switch (event) { + case BFA_FCS_FABRIC_SM_START: + if (bfa_pport_is_linkup(fabric->fcs->bfa)) { + bfa_sm_set_state(fabric, bfa_fcs_fabric_sm_flogi); + bfa_fcs_fabric_login(fabric); + } else + bfa_sm_set_state(fabric, bfa_fcs_fabric_sm_linkdown); + break; + + case BFA_FCS_FABRIC_SM_LINK_UP: + case BFA_FCS_FABRIC_SM_LINK_DOWN: + break; + + case BFA_FCS_FABRIC_SM_DELETE: + bfa_sm_set_state(fabric, bfa_fcs_fabric_sm_uninit); + bfa_fcs_modexit_comp(fabric->fcs); + break; + + default: + bfa_sm_fault(fabric->fcs, event); + } +} + +/** + * Link is down, awaiting LINK UP event from port. This is also the + * first state at fabric creation. + */ +static void +bfa_fcs_fabric_sm_linkdown(struct bfa_fcs_fabric_s *fabric, + enum bfa_fcs_fabric_event event) +{ + bfa_trc(fabric->fcs, fabric->bport.port_cfg.pwwn); + bfa_trc(fabric->fcs, event); + + switch (event) { + case BFA_FCS_FABRIC_SM_LINK_UP: + bfa_sm_set_state(fabric, bfa_fcs_fabric_sm_flogi); + bfa_fcs_fabric_login(fabric); + break; + + case BFA_FCS_FABRIC_SM_RETRY_OP: + break; + + case BFA_FCS_FABRIC_SM_DELETE: + bfa_sm_set_state(fabric, bfa_fcs_fabric_sm_deleting); + bfa_fcs_fabric_delete(fabric); + break; + + default: + bfa_sm_fault(fabric->fcs, event); + } +} + +/** + * FLOGI is in progress, awaiting FLOGI reply. + */ +static void +bfa_fcs_fabric_sm_flogi(struct bfa_fcs_fabric_s *fabric, + enum bfa_fcs_fabric_event event) +{ + bfa_trc(fabric->fcs, fabric->bport.port_cfg.pwwn); + bfa_trc(fabric->fcs, event); + + switch (event) { + case BFA_FCS_FABRIC_SM_CONT_OP: + + bfa_pport_set_tx_bbcredit(fabric->fcs->bfa, fabric->bb_credit); + fabric->fab_type = BFA_FCS_FABRIC_SWITCHED; + + if (fabric->auth_reqd && fabric->is_auth) { + bfa_sm_set_state(fabric, bfa_fcs_fabric_sm_auth); + bfa_trc(fabric->fcs, event); + } else { + bfa_sm_set_state(fabric, bfa_fcs_fabric_sm_online); + bfa_fcs_fabric_notify_online(fabric); + } + break; + + case BFA_FCS_FABRIC_SM_RETRY_OP: + bfa_sm_set_state(fabric, bfa_fcs_fabric_sm_flogi_retry); + bfa_timer_start(fabric->fcs->bfa, &fabric->delay_timer, + bfa_fcs_fabric_delay, fabric, + BFA_FCS_FABRIC_RETRY_DELAY); + break; + + case BFA_FCS_FABRIC_SM_LOOPBACK: + bfa_sm_set_state(fabric, bfa_fcs_fabric_sm_loopback); + bfa_lps_discard(fabric->lps); + bfa_fcs_fabric_set_opertype(fabric); + break; + + case BFA_FCS_FABRIC_SM_NO_FABRIC: + fabric->fab_type = BFA_FCS_FABRIC_N2N; + bfa_pport_set_tx_bbcredit(fabric->fcs->bfa, fabric->bb_credit); + bfa_fcs_fabric_notify_online(fabric); + bfa_sm_set_state(fabric, bfa_fcs_fabric_sm_nofabric); + break; + + case BFA_FCS_FABRIC_SM_LINK_DOWN: + bfa_sm_set_state(fabric, bfa_fcs_fabric_sm_linkdown); + bfa_lps_discard(fabric->lps); + break; + + case BFA_FCS_FABRIC_SM_DELETE: + bfa_sm_set_state(fabric, bfa_fcs_fabric_sm_deleting); + bfa_lps_discard(fabric->lps); + bfa_fcs_fabric_delete(fabric); + break; + + default: + bfa_sm_fault(fabric->fcs, event); + } +} + + +static void +bfa_fcs_fabric_sm_flogi_retry(struct bfa_fcs_fabric_s *fabric, + enum bfa_fcs_fabric_event event) +{ + bfa_trc(fabric->fcs, fabric->bport.port_cfg.pwwn); + bfa_trc(fabric->fcs, event); + + switch (event) { + case BFA_FCS_FABRIC_SM_DELAYED: + bfa_sm_set_state(fabric, bfa_fcs_fabric_sm_flogi); + bfa_fcs_fabric_login(fabric); + break; + + case BFA_FCS_FABRIC_SM_LINK_DOWN: + bfa_sm_set_state(fabric, bfa_fcs_fabric_sm_linkdown); + bfa_timer_stop(&fabric->delay_timer); + break; + + case BFA_FCS_FABRIC_SM_DELETE: + bfa_sm_set_state(fabric, bfa_fcs_fabric_sm_deleting); + bfa_timer_stop(&fabric->delay_timer); + bfa_fcs_fabric_delete(fabric); + break; + + default: + bfa_sm_fault(fabric->fcs, event); + } +} + +/** + * Authentication is in progress, awaiting authentication results. + */ +static void +bfa_fcs_fabric_sm_auth(struct bfa_fcs_fabric_s *fabric, + enum bfa_fcs_fabric_event event) +{ + bfa_trc(fabric->fcs, fabric->bport.port_cfg.pwwn); + bfa_trc(fabric->fcs, event); + + switch (event) { + case BFA_FCS_FABRIC_SM_AUTH_FAILED: + bfa_sm_set_state(fabric, bfa_fcs_fabric_sm_auth_failed); + bfa_lps_discard(fabric->lps); + break; + + case BFA_FCS_FABRIC_SM_AUTH_SUCCESS: + bfa_sm_set_state(fabric, bfa_fcs_fabric_sm_online); + bfa_fcs_fabric_notify_online(fabric); + break; + + case BFA_FCS_FABRIC_SM_PERF_EVFP: + bfa_sm_set_state(fabric, bfa_fcs_fabric_sm_evfp); + break; + + case BFA_FCS_FABRIC_SM_LINK_DOWN: + bfa_sm_set_state(fabric, bfa_fcs_fabric_sm_linkdown); + bfa_lps_discard(fabric->lps); + break; + + case BFA_FCS_FABRIC_SM_DELETE: + bfa_sm_set_state(fabric, bfa_fcs_fabric_sm_deleting); + bfa_fcs_fabric_delete(fabric); + break; + + default: + bfa_sm_fault(fabric->fcs, event); + } +} + +/** + * Authentication failed + */ +static void +bfa_fcs_fabric_sm_auth_failed(struct bfa_fcs_fabric_s *fabric, + enum bfa_fcs_fabric_event event) +{ + bfa_trc(fabric->fcs, fabric->bport.port_cfg.pwwn); + bfa_trc(fabric->fcs, event); + + switch (event) { + case BFA_FCS_FABRIC_SM_LINK_DOWN: + bfa_sm_set_state(fabric, bfa_fcs_fabric_sm_linkdown); + bfa_fcs_fabric_notify_offline(fabric); + break; + + case BFA_FCS_FABRIC_SM_DELETE: + bfa_sm_set_state(fabric, bfa_fcs_fabric_sm_deleting); + bfa_fcs_fabric_delete(fabric); + break; + + default: + bfa_sm_fault(fabric->fcs, event); + } +} + +/** + * Port is in loopback mode. + */ +static void +bfa_fcs_fabric_sm_loopback(struct bfa_fcs_fabric_s *fabric, + enum bfa_fcs_fabric_event event) +{ + bfa_trc(fabric->fcs, fabric->bport.port_cfg.pwwn); + bfa_trc(fabric->fcs, event); + + switch (event) { + case BFA_FCS_FABRIC_SM_LINK_DOWN: + bfa_sm_set_state(fabric, bfa_fcs_fabric_sm_linkdown); + bfa_fcs_fabric_notify_offline(fabric); + break; + + case BFA_FCS_FABRIC_SM_DELETE: + bfa_sm_set_state(fabric, bfa_fcs_fabric_sm_deleting); + bfa_fcs_fabric_delete(fabric); + break; + + default: + bfa_sm_fault(fabric->fcs, event); + } +} + +/** + * There is no attached fabric - private loop or NPort-to-NPort topology. + */ +static void +bfa_fcs_fabric_sm_nofabric(struct bfa_fcs_fabric_s *fabric, + enum bfa_fcs_fabric_event event) +{ + bfa_trc(fabric->fcs, fabric->bport.port_cfg.pwwn); + bfa_trc(fabric->fcs, event); + + switch (event) { + case BFA_FCS_FABRIC_SM_LINK_DOWN: + bfa_sm_set_state(fabric, bfa_fcs_fabric_sm_linkdown); + bfa_lps_discard(fabric->lps); + bfa_fcs_fabric_notify_offline(fabric); + break; + + case BFA_FCS_FABRIC_SM_DELETE: + bfa_sm_set_state(fabric, bfa_fcs_fabric_sm_deleting); + bfa_fcs_fabric_delete(fabric); + break; + + case BFA_FCS_FABRIC_SM_NO_FABRIC: + bfa_trc(fabric->fcs, fabric->bb_credit); + bfa_pport_set_tx_bbcredit(fabric->fcs->bfa, fabric->bb_credit); + break; + + default: + bfa_sm_fault(fabric->fcs, event); + } +} + +/** + * Fabric is online - normal operating state. + */ +static void +bfa_fcs_fabric_sm_online(struct bfa_fcs_fabric_s *fabric, + enum bfa_fcs_fabric_event event) +{ + bfa_trc(fabric->fcs, fabric->bport.port_cfg.pwwn); + bfa_trc(fabric->fcs, event); + + switch (event) { + case BFA_FCS_FABRIC_SM_LINK_DOWN: + bfa_sm_set_state(fabric, bfa_fcs_fabric_sm_linkdown); + bfa_lps_discard(fabric->lps); + bfa_fcs_fabric_notify_offline(fabric); + break; + + case BFA_FCS_FABRIC_SM_DELETE: + bfa_sm_set_state(fabric, bfa_fcs_fabric_sm_deleting); + bfa_fcs_fabric_delete(fabric); + break; + + case BFA_FCS_FABRIC_SM_AUTH_FAILED: + bfa_sm_set_state(fabric, bfa_fcs_fabric_sm_auth_failed); + bfa_lps_discard(fabric->lps); + break; + + case BFA_FCS_FABRIC_SM_AUTH_SUCCESS: + break; + + default: + bfa_sm_fault(fabric->fcs, event); + } +} + +/** + * Exchanging virtual fabric parameters. + */ +static void +bfa_fcs_fabric_sm_evfp(struct bfa_fcs_fabric_s *fabric, + enum bfa_fcs_fabric_event event) +{ + bfa_trc(fabric->fcs, fabric->bport.port_cfg.pwwn); + bfa_trc(fabric->fcs, event); + + switch (event) { + case BFA_FCS_FABRIC_SM_CONT_OP: + bfa_sm_set_state(fabric, bfa_fcs_fabric_sm_evfp_done); + break; + + case BFA_FCS_FABRIC_SM_ISOLATE: + bfa_sm_set_state(fabric, bfa_fcs_fabric_sm_isolated); + break; + + default: + bfa_sm_fault(fabric->fcs, event); + } +} + +/** + * EVFP exchange complete and VFT tagging is enabled. + */ +static void +bfa_fcs_fabric_sm_evfp_done(struct bfa_fcs_fabric_s *fabric, + enum bfa_fcs_fabric_event event) +{ + bfa_trc(fabric->fcs, fabric->bport.port_cfg.pwwn); + bfa_trc(fabric->fcs, event); +} + +/** + * Port is isolated after EVFP exchange due to VF_ID mismatch (N and F). + */ +static void +bfa_fcs_fabric_sm_isolated(struct bfa_fcs_fabric_s *fabric, + enum bfa_fcs_fabric_event event) +{ + bfa_trc(fabric->fcs, fabric->bport.port_cfg.pwwn); + bfa_trc(fabric->fcs, event); + + bfa_log(fabric->fcs->logm, BFA_LOG_FCS_FABRIC_ISOLATED, + fabric->bport.port_cfg.pwwn, fabric->fcs->port_vfid, + fabric->event_arg.swp_vfid); +} + +/** + * Fabric is being deleted, awaiting vport delete completions. + */ +static void +bfa_fcs_fabric_sm_deleting(struct bfa_fcs_fabric_s *fabric, + enum bfa_fcs_fabric_event event) +{ + bfa_trc(fabric->fcs, fabric->bport.port_cfg.pwwn); + bfa_trc(fabric->fcs, event); + + switch (event) { + case BFA_FCS_FABRIC_SM_DELCOMP: + bfa_sm_set_state(fabric, bfa_fcs_fabric_sm_uninit); + bfa_fcs_modexit_comp(fabric->fcs); + break; + + case BFA_FCS_FABRIC_SM_LINK_UP: + break; + + case BFA_FCS_FABRIC_SM_LINK_DOWN: + bfa_fcs_fabric_notify_offline(fabric); + break; + + default: + bfa_sm_fault(fabric->fcs, event); + } +} + + + +/** + * fcs_fabric_private fabric private functions + */ + +static void +bfa_fcs_fabric_init(struct bfa_fcs_fabric_s *fabric) +{ + struct bfa_port_cfg_s *port_cfg = &fabric->bport.port_cfg; + + port_cfg->roles = BFA_PORT_ROLE_FCP_IM; + port_cfg->nwwn = bfa_ioc_get_nwwn(&fabric->fcs->bfa->ioc); + port_cfg->pwwn = bfa_ioc_get_pwwn(&fabric->fcs->bfa->ioc); +} + +/** + * Port Symbolic Name Creation for base port. + */ +void +bfa_fcs_fabric_psymb_init(struct bfa_fcs_fabric_s *fabric) +{ + struct bfa_port_cfg_s *port_cfg = &fabric->bport.port_cfg; + struct bfa_adapter_attr_s adapter_attr; + struct bfa_fcs_driver_info_s *driver_info = &fabric->fcs->driver_info; + + bfa_os_memset((void *)&adapter_attr, 0, + sizeof(struct bfa_adapter_attr_s)); + bfa_ioc_get_adapter_attr(&fabric->fcs->bfa->ioc, &adapter_attr); + + /* + * Model name/number + */ + strncpy((char *)&port_cfg->sym_name, adapter_attr.model, + BFA_FCS_PORT_SYMBNAME_MODEL_SZ); + strncat((char *)&port_cfg->sym_name, BFA_FCS_PORT_SYMBNAME_SEPARATOR, + sizeof(BFA_FCS_PORT_SYMBNAME_SEPARATOR)); + + /* + * Driver Version + */ + strncat((char *)&port_cfg->sym_name, (char *)driver_info->version, + BFA_FCS_PORT_SYMBNAME_VERSION_SZ); + strncat((char *)&port_cfg->sym_name, BFA_FCS_PORT_SYMBNAME_SEPARATOR, + sizeof(BFA_FCS_PORT_SYMBNAME_SEPARATOR)); + + /* + * Host machine name + */ + strncat((char *)&port_cfg->sym_name, + (char *)driver_info->host_machine_name, + BFA_FCS_PORT_SYMBNAME_MACHINENAME_SZ); + strncat((char *)&port_cfg->sym_name, BFA_FCS_PORT_SYMBNAME_SEPARATOR, + sizeof(BFA_FCS_PORT_SYMBNAME_SEPARATOR)); + + /* + * Host OS Info : + * If OS Patch Info is not there, do not truncate any bytes from the + * OS name string and instead copy the entire OS info string (64 bytes). + */ + if (driver_info->host_os_patch[0] == '\0') { + strncat((char *)&port_cfg->sym_name, + (char *)driver_info->host_os_name, BFA_FCS_OS_STR_LEN); + strncat((char *)&port_cfg->sym_name, + BFA_FCS_PORT_SYMBNAME_SEPARATOR, + sizeof(BFA_FCS_PORT_SYMBNAME_SEPARATOR)); + } else { + strncat((char *)&port_cfg->sym_name, + (char *)driver_info->host_os_name, + BFA_FCS_PORT_SYMBNAME_OSINFO_SZ); + strncat((char *)&port_cfg->sym_name, + BFA_FCS_PORT_SYMBNAME_SEPARATOR, + sizeof(BFA_FCS_PORT_SYMBNAME_SEPARATOR)); + + /* + * Append host OS Patch Info + */ + strncat((char *)&port_cfg->sym_name, + (char *)driver_info->host_os_patch, + BFA_FCS_PORT_SYMBNAME_OSPATCH_SZ); + } + + /* + * null terminate + */ + port_cfg->sym_name.symname[BFA_SYMNAME_MAXLEN - 1] = 0; +} + +/** + * bfa lps login completion callback + */ +void +bfa_cb_lps_flogi_comp(void *bfad, void *uarg, bfa_status_t status) +{ + struct bfa_fcs_fabric_s *fabric = uarg; + + bfa_trc(fabric->fcs, fabric->bport.port_cfg.pwwn); + bfa_trc(fabric->fcs, status); + + switch (status) { + case BFA_STATUS_OK: + fabric->stats.flogi_accepts++; + break; + + case BFA_STATUS_INVALID_MAC: + /* + * Only for CNA + */ + fabric->stats.flogi_acc_err++; + bfa_sm_send_event(fabric, BFA_FCS_FABRIC_SM_RETRY_OP); + + return; + + case BFA_STATUS_EPROTOCOL: + switch (bfa_lps_get_extstatus(fabric->lps)) { + case BFA_EPROTO_BAD_ACCEPT: + fabric->stats.flogi_acc_err++; + break; + + case BFA_EPROTO_UNKNOWN_RSP: + fabric->stats.flogi_unknown_rsp++; + break; + + default: + break; + } + bfa_sm_send_event(fabric, BFA_FCS_FABRIC_SM_RETRY_OP); + + return; + + case BFA_STATUS_FABRIC_RJT: + fabric->stats.flogi_rejects++; + bfa_sm_send_event(fabric, BFA_FCS_FABRIC_SM_RETRY_OP); + return; + + default: + fabric->stats.flogi_rsp_err++; + bfa_sm_send_event(fabric, BFA_FCS_FABRIC_SM_RETRY_OP); + return; + } + + fabric->bb_credit = bfa_lps_get_peer_bbcredit(fabric->lps); + bfa_trc(fabric->fcs, fabric->bb_credit); + + if (!bfa_lps_is_brcd_fabric(fabric->lps)) + fabric->fabric_name = bfa_lps_get_peer_nwwn(fabric->lps); + + /* + * Check port type. It should be 1 = F-port. + */ + if (bfa_lps_is_fport(fabric->lps)) { + fabric->bport.pid = bfa_lps_get_pid(fabric->lps); + fabric->is_npiv = bfa_lps_is_npiv_en(fabric->lps); + fabric->is_auth = bfa_lps_is_authreq(fabric->lps); + bfa_sm_send_event(fabric, BFA_FCS_FABRIC_SM_CONT_OP); + } else { + /* + * Nport-2-Nport direct attached + */ + fabric->bport.port_topo.pn2n.rem_port_wwn = + bfa_lps_get_peer_pwwn(fabric->lps); + bfa_sm_send_event(fabric, BFA_FCS_FABRIC_SM_NO_FABRIC); + } + + bfa_trc(fabric->fcs, fabric->bport.pid); + bfa_trc(fabric->fcs, fabric->is_npiv); + bfa_trc(fabric->fcs, fabric->is_auth); +} + +/** + * Allocate and send FLOGI. + */ +static void +bfa_fcs_fabric_login(struct bfa_fcs_fabric_s *fabric) +{ + struct bfa_s *bfa = fabric->fcs->bfa; + struct bfa_port_cfg_s *pcfg = &fabric->bport.port_cfg; + u8 alpa = 0; + + if (bfa_pport_get_topology(bfa) == BFA_PPORT_TOPOLOGY_LOOP) + alpa = bfa_pport_get_myalpa(bfa); + + bfa_lps_flogi(fabric->lps, fabric, alpa, bfa_pport_get_maxfrsize(bfa), + pcfg->pwwn, pcfg->nwwn, fabric->auth_reqd); + + fabric->stats.flogi_sent++; +} + +static void +bfa_fcs_fabric_notify_online(struct bfa_fcs_fabric_s *fabric) +{ + struct bfa_fcs_vport_s *vport; + struct list_head *qe, *qen; + + bfa_trc(fabric->fcs, fabric->fabric_name); + + bfa_fcs_fabric_set_opertype(fabric); + fabric->stats.fabric_onlines++; + + /** + * notify online event to base and then virtual ports + */ + bfa_fcs_port_online(&fabric->bport); + + list_for_each_safe(qe, qen, &fabric->vport_q) { + vport = (struct bfa_fcs_vport_s *)qe; + bfa_fcs_vport_online(vport); + } +} + +static void +bfa_fcs_fabric_notify_offline(struct bfa_fcs_fabric_s *fabric) +{ + struct bfa_fcs_vport_s *vport; + struct list_head *qe, *qen; + + bfa_trc(fabric->fcs, fabric->fabric_name); + fabric->stats.fabric_offlines++; + + /** + * notify offline event first to vports and then base port. + */ + list_for_each_safe(qe, qen, &fabric->vport_q) { + vport = (struct bfa_fcs_vport_s *)qe; + bfa_fcs_vport_offline(vport); + } + + bfa_fcs_port_offline(&fabric->bport); + + fabric->fabric_name = 0; + fabric->fabric_ip_addr[0] = 0; +} + +static void +bfa_fcs_fabric_delay(void *cbarg) +{ + struct bfa_fcs_fabric_s *fabric = cbarg; + + bfa_sm_send_event(fabric, BFA_FCS_FABRIC_SM_DELAYED); +} + +/** + * Delete all vports and wait for vport delete completions. + */ +static void +bfa_fcs_fabric_delete(struct bfa_fcs_fabric_s *fabric) +{ + struct bfa_fcs_vport_s *vport; + struct list_head *qe, *qen; + + list_for_each_safe(qe, qen, &fabric->vport_q) { + vport = (struct bfa_fcs_vport_s *)qe; + bfa_fcs_vport_delete(vport); + } + + bfa_fcs_port_delete(&fabric->bport); + bfa_wc_wait(&fabric->wc); +} + +static void +bfa_fcs_fabric_delete_comp(void *cbarg) +{ + struct bfa_fcs_fabric_s *fabric = cbarg; + + bfa_sm_send_event(fabric, BFA_FCS_FABRIC_SM_DELCOMP); +} + + + +/** + * fcs_fabric_public fabric public functions + */ + +/** + * Module initialization + */ +void +bfa_fcs_fabric_modinit(struct bfa_fcs_s *fcs) +{ + struct bfa_fcs_fabric_s *fabric; + + fabric = &fcs->fabric; + bfa_os_memset(fabric, 0, sizeof(struct bfa_fcs_fabric_s)); + + /** + * Initialize base fabric. + */ + fabric->fcs = fcs; + INIT_LIST_HEAD(&fabric->vport_q); + INIT_LIST_HEAD(&fabric->vf_q); + fabric->lps = bfa_lps_alloc(fcs->bfa); + bfa_assert(fabric->lps); + + /** + * Initialize fabric delete completion handler. Fabric deletion is complete + * when the last vport delete is complete. + */ + bfa_wc_init(&fabric->wc, bfa_fcs_fabric_delete_comp, fabric); + bfa_wc_up(&fabric->wc); /* For the base port */ + + bfa_sm_set_state(fabric, bfa_fcs_fabric_sm_uninit); + bfa_sm_send_event(fabric, BFA_FCS_FABRIC_SM_CREATE); + bfa_trc(fcs, 0); +} + +/** + * Module cleanup + */ +void +bfa_fcs_fabric_modexit(struct bfa_fcs_s *fcs) +{ + struct bfa_fcs_fabric_s *fabric; + + bfa_trc(fcs, 0); + + /** + * Cleanup base fabric. + */ + fabric = &fcs->fabric; + bfa_lps_delete(fabric->lps); + bfa_sm_send_event(fabric, BFA_FCS_FABRIC_SM_DELETE); +} + +/** + * Fabric module start -- kick starts FCS actions + */ +void +bfa_fcs_fabric_modstart(struct bfa_fcs_s *fcs) +{ + struct bfa_fcs_fabric_s *fabric; + + bfa_trc(fcs, 0); + fabric = &fcs->fabric; + bfa_sm_send_event(fabric, BFA_FCS_FABRIC_SM_START); +} + +/** + * Suspend fabric activity as part of driver suspend. + */ +void +bfa_fcs_fabric_modsusp(struct bfa_fcs_s *fcs) +{ +} + +bfa_boolean_t +bfa_fcs_fabric_is_loopback(struct bfa_fcs_fabric_s *fabric) +{ + return (bfa_sm_cmp_state(fabric, bfa_fcs_fabric_sm_loopback)); +} + +enum bfa_pport_type +bfa_fcs_fabric_port_type(struct bfa_fcs_fabric_s *fabric) +{ + return fabric->oper_type; +} + +/** + * Link up notification from BFA physical port module. + */ +void +bfa_fcs_fabric_link_up(struct bfa_fcs_fabric_s *fabric) +{ + bfa_trc(fabric->fcs, fabric->bport.port_cfg.pwwn); + bfa_sm_send_event(fabric, BFA_FCS_FABRIC_SM_LINK_UP); +} + +/** + * Link down notification from BFA physical port module. + */ +void +bfa_fcs_fabric_link_down(struct bfa_fcs_fabric_s *fabric) +{ + bfa_trc(fabric->fcs, fabric->bport.port_cfg.pwwn); + bfa_sm_send_event(fabric, BFA_FCS_FABRIC_SM_LINK_DOWN); +} + +/** + * A child vport is being created in the fabric. + * + * Call from vport module at vport creation. A list of base port and vports + * belonging to a fabric is maintained to propagate link events. + * + * param[in] fabric - Fabric instance. This can be a base fabric or vf. + * param[in] vport - Vport being created. + * + * @return None (always succeeds) + */ +void +bfa_fcs_fabric_addvport(struct bfa_fcs_fabric_s *fabric, + struct bfa_fcs_vport_s *vport) +{ + /** + * - add vport to fabric's vport_q + */ + bfa_trc(fabric->fcs, fabric->vf_id); + + list_add_tail(&vport->qe, &fabric->vport_q); + fabric->num_vports++; + bfa_wc_up(&fabric->wc); +} + +/** + * A child vport is being deleted from fabric. + * + * Vport is being deleted. + */ +void +bfa_fcs_fabric_delvport(struct bfa_fcs_fabric_s *fabric, + struct bfa_fcs_vport_s *vport) +{ + list_del(&vport->qe); + fabric->num_vports--; + bfa_wc_down(&fabric->wc); +} + +/** + * Base port is deleted. + */ +void +bfa_fcs_fabric_port_delete_comp(struct bfa_fcs_fabric_s *fabric) +{ + bfa_wc_down(&fabric->wc); +} + +/** + * Check if fabric is online. + * + * param[in] fabric - Fabric instance. This can be a base fabric or vf. + * + * @return TRUE/FALSE + */ +int +bfa_fcs_fabric_is_online(struct bfa_fcs_fabric_s *fabric) +{ + return (bfa_sm_cmp_state(fabric, bfa_fcs_fabric_sm_online)); +} + + +bfa_status_t +bfa_fcs_fabric_addvf(struct bfa_fcs_fabric_s *vf, struct bfa_fcs_s *fcs, + struct bfa_port_cfg_s *port_cfg, + struct bfad_vf_s *vf_drv) +{ + bfa_sm_set_state(vf, bfa_fcs_fabric_sm_uninit); + return BFA_STATUS_OK; +} + +/** + * Lookup for a vport withing a fabric given its pwwn + */ +struct bfa_fcs_vport_s * +bfa_fcs_fabric_vport_lookup(struct bfa_fcs_fabric_s *fabric, wwn_t pwwn) +{ + struct bfa_fcs_vport_s *vport; + struct list_head *qe; + + list_for_each(qe, &fabric->vport_q) { + vport = (struct bfa_fcs_vport_s *)qe; + if (bfa_fcs_port_get_pwwn(&vport->lport) == pwwn) + return vport; + } + + return NULL; +} + +/** + * In a given fabric, return the number of lports. + * + * param[in] fabric - Fabric instance. This can be a base fabric or vf. + * +* @return : 1 or more. + */ +u16 +bfa_fcs_fabric_vport_count(struct bfa_fcs_fabric_s *fabric) +{ + return (fabric->num_vports); +} + +/** + * Unsolicited frame receive handling. + */ +void +bfa_fcs_fabric_uf_recv(struct bfa_fcs_fabric_s *fabric, struct fchs_s *fchs, + u16 len) +{ + u32 pid = fchs->d_id; + struct bfa_fcs_vport_s *vport; + struct list_head *qe; + struct fc_els_cmd_s *els_cmd = (struct fc_els_cmd_s *) (fchs + 1); + struct fc_logi_s *flogi = (struct fc_logi_s *) els_cmd; + + bfa_trc(fabric->fcs, len); + bfa_trc(fabric->fcs, pid); + + /** + * Look for our own FLOGI frames being looped back. This means an + * external loopback cable is in place. Our own FLOGI frames are + * sometimes looped back when switch port gets temporarily bypassed. + */ + if ((pid == bfa_os_ntoh3b(FC_FABRIC_PORT)) + && (els_cmd->els_code == FC_ELS_FLOGI) + && (flogi->port_name == bfa_fcs_port_get_pwwn(&fabric->bport))) { + bfa_sm_send_event(fabric, BFA_FCS_FABRIC_SM_LOOPBACK); + return; + } + + /** + * FLOGI/EVFP exchanges should be consumed by base fabric. + */ + if (fchs->d_id == bfa_os_hton3b(FC_FABRIC_PORT)) { + bfa_trc(fabric->fcs, pid); + bfa_fcs_fabric_process_uf(fabric, fchs, len); + return; + } + + if (fabric->bport.pid == pid) { + /** + * All authentication frames should be routed to auth + */ + bfa_trc(fabric->fcs, els_cmd->els_code); + if (els_cmd->els_code == FC_ELS_AUTH) { + bfa_trc(fabric->fcs, els_cmd->els_code); + fabric->auth.response = (u8 *) els_cmd; + return; + } + + bfa_trc(fabric->fcs, *(u8 *) ((u8 *) fchs)); + bfa_fcs_port_uf_recv(&fabric->bport, fchs, len); + return; + } + + /** + * look for a matching local port ID + */ + list_for_each(qe, &fabric->vport_q) { + vport = (struct bfa_fcs_vport_s *)qe; + if (vport->lport.pid == pid) { + bfa_fcs_port_uf_recv(&vport->lport, fchs, len); + return; + } + } + bfa_trc(fabric->fcs, els_cmd->els_code); + bfa_fcs_port_uf_recv(&fabric->bport, fchs, len); +} + +/** + * Unsolicited frames to be processed by fabric. + */ +static void +bfa_fcs_fabric_process_uf(struct bfa_fcs_fabric_s *fabric, struct fchs_s *fchs, + u16 len) +{ + struct fc_els_cmd_s *els_cmd = (struct fc_els_cmd_s *) (fchs + 1); + + bfa_trc(fabric->fcs, els_cmd->els_code); + + switch (els_cmd->els_code) { + case FC_ELS_FLOGI: + bfa_fcs_fabric_process_flogi(fabric, fchs, len); + break; + + default: + /* + * need to generate a LS_RJT + */ + break; + } +} + +/** + * Process incoming FLOGI + */ +static void +bfa_fcs_fabric_process_flogi(struct bfa_fcs_fabric_s *fabric, + struct fchs_s *fchs, u16 len) +{ + struct fc_logi_s *flogi = (struct fc_logi_s *) (fchs + 1); + struct bfa_fcs_port_s *bport = &fabric->bport; + + bfa_trc(fabric->fcs, fchs->s_id); + + fabric->stats.flogi_rcvd++; + /* + * Check port type. It should be 0 = n-port. + */ + if (flogi->csp.port_type) { + /* + * @todo: may need to send a LS_RJT + */ + bfa_trc(fabric->fcs, flogi->port_name); + fabric->stats.flogi_rejected++; + return; + } + + fabric->bb_credit = bfa_os_ntohs(flogi->csp.bbcred); + bport->port_topo.pn2n.rem_port_wwn = flogi->port_name; + bport->port_topo.pn2n.reply_oxid = fchs->ox_id; + + /* + * Send a Flogi Acc + */ + bfa_fcs_fabric_send_flogi_acc(fabric); + bfa_sm_send_event(fabric, BFA_FCS_FABRIC_SM_NO_FABRIC); +} + +static void +bfa_fcs_fabric_send_flogi_acc(struct bfa_fcs_fabric_s *fabric) +{ + struct bfa_port_cfg_s *pcfg = &fabric->bport.port_cfg; + struct bfa_fcs_port_n2n_s *n2n_port = &fabric->bport.port_topo.pn2n; + struct bfa_s *bfa = fabric->fcs->bfa; + struct bfa_fcxp_s *fcxp; + u16 reqlen; + struct fchs_s fchs; + + fcxp = bfa_fcs_fcxp_alloc(fabric->fcs); + /** + * Do not expect this failure -- expect remote node to retry + */ + if (!fcxp) + return; + + reqlen = fc_flogi_acc_build(&fchs, bfa_fcxp_get_reqbuf(fcxp), + bfa_os_hton3b(FC_FABRIC_PORT), + n2n_port->reply_oxid, pcfg->pwwn, + pcfg->nwwn, bfa_pport_get_maxfrsize(bfa), + bfa_pport_get_rx_bbcredit(bfa)); + + bfa_fcxp_send(fcxp, NULL, fabric->vf_id, bfa_lps_get_tag(fabric->lps), + BFA_FALSE, FC_CLASS_3, reqlen, &fchs, + bfa_fcs_fabric_flogiacc_comp, fabric, + FC_MAX_PDUSZ, 0); /* Timeout 0 indicates no + * response expected + */ +} + +/** + * Flogi Acc completion callback. + */ +static void +bfa_fcs_fabric_flogiacc_comp(void *fcsarg, struct bfa_fcxp_s *fcxp, void *cbarg, + bfa_status_t status, u32 rsp_len, + u32 resid_len, struct fchs_s *rspfchs) +{ + struct bfa_fcs_fabric_s *fabric = cbarg; + + bfa_trc(fabric->fcs, status); +} + +/* + * + * @param[in] fabric - fabric + * @param[in] result - 1 + * + * @return - none + */ +void +bfa_fcs_auth_finished(struct bfa_fcs_fabric_s *fabric, enum auth_status status) +{ + bfa_trc(fabric->fcs, status); + + if (status == FC_AUTH_STATE_SUCCESS) + bfa_sm_send_event(fabric, BFA_FCS_FABRIC_SM_AUTH_SUCCESS); + else + bfa_sm_send_event(fabric, BFA_FCS_FABRIC_SM_AUTH_FAILED); +} + +/** + * Send AEN notification + */ +static void +bfa_fcs_fabric_aen_post(struct bfa_fcs_port_s *port, + enum bfa_port_aen_event event) +{ + union bfa_aen_data_u aen_data; + struct bfa_log_mod_s *logmod = port->fcs->logm; + wwn_t pwwn = bfa_fcs_port_get_pwwn(port); + wwn_t fwwn = bfa_fcs_port_get_fabric_name(port); + char pwwn_ptr[BFA_STRING_32]; + char fwwn_ptr[BFA_STRING_32]; + + wwn2str(pwwn_ptr, pwwn); + wwn2str(fwwn_ptr, fwwn); + + switch (event) { + case BFA_PORT_AEN_FABRIC_NAME_CHANGE: + bfa_log(logmod, BFA_AEN_PORT_FABRIC_NAME_CHANGE, pwwn_ptr, + fwwn_ptr); + break; + default: + break; + } + + aen_data.port.pwwn = pwwn; + aen_data.port.fwwn = fwwn; +} + +/* + * + * @param[in] fabric - fabric + * @param[in] wwn_t - new fabric name + * + * @return - none + */ +void +bfa_fcs_fabric_set_fabric_name(struct bfa_fcs_fabric_s *fabric, + wwn_t fabric_name) +{ + bfa_trc(fabric->fcs, fabric_name); + + if (fabric->fabric_name == 0) { + /* + * With BRCD switches, we don't get Fabric Name in FLOGI. + * Don't generate a fabric name change event in this case. + */ + fabric->fabric_name = fabric_name; + } else { + fabric->fabric_name = fabric_name; + /* + * Generate a Event + */ + bfa_fcs_fabric_aen_post(&fabric->bport, + BFA_PORT_AEN_FABRIC_NAME_CHANGE); + } + +} + +/** + * Not used by FCS. + */ +void +bfa_cb_lps_flogo_comp(void *bfad, void *uarg) +{ +} + + diff --git a/drivers/scsi/bfa/fcbuild.c b/drivers/scsi/bfa/fcbuild.c new file mode 100644 index 000000000000..d174706b9caa --- /dev/null +++ b/drivers/scsi/bfa/fcbuild.c @@ -0,0 +1,1449 @@ +/* + * Copyright (c) 2005-2009 Brocade Communications Systems, Inc. + * All rights reserved + * www.brocade.com + * + * Linux driver for Brocade Fibre Channel Host Bus Adapter. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (GPL) Version 2 as + * published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ +/* + * fcbuild.c - FC link service frame building and parsing routines + */ + +#include <bfa_os_inc.h> +#include "fcbuild.h" + +/* + * static build functions + */ +static void fc_els_rsp_build(struct fchs_s *fchs, u32 d_id, u32 s_id, + u16 ox_id); +static void fc_bls_rsp_build(struct fchs_s *fchs, u32 d_id, u32 s_id, + u16 ox_id); +static struct fchs_s fc_els_req_tmpl; +static struct fchs_s fc_els_rsp_tmpl; +static struct fchs_s fc_bls_req_tmpl; +static struct fchs_s fc_bls_rsp_tmpl; +static struct fc_ba_acc_s ba_acc_tmpl; +static struct fc_logi_s plogi_tmpl; +static struct fc_prli_s prli_tmpl; +static struct fc_rrq_s rrq_tmpl; +static struct fchs_s fcp_fchs_tmpl; + +void +fcbuild_init(void) +{ + /* + * fc_els_req_tmpl + */ + fc_els_req_tmpl.routing = FC_RTG_EXT_LINK; + fc_els_req_tmpl.cat_info = FC_CAT_LD_REQUEST; + fc_els_req_tmpl.type = FC_TYPE_ELS; + fc_els_req_tmpl.f_ctl = + bfa_os_hton3b(FCTL_SEQ_INI | FCTL_FS_EXCH | FCTL_END_SEQ | + FCTL_SI_XFER); + fc_els_req_tmpl.rx_id = FC_RXID_ANY; + + /* + * fc_els_rsp_tmpl + */ + fc_els_rsp_tmpl.routing = FC_RTG_EXT_LINK; + fc_els_rsp_tmpl.cat_info = FC_CAT_LD_REPLY; + fc_els_rsp_tmpl.type = FC_TYPE_ELS; + fc_els_rsp_tmpl.f_ctl = + bfa_os_hton3b(FCTL_EC_RESP | FCTL_SEQ_INI | FCTL_LS_EXCH | + FCTL_END_SEQ | FCTL_SI_XFER); + fc_els_rsp_tmpl.rx_id = FC_RXID_ANY; + + /* + * fc_bls_req_tmpl + */ + fc_bls_req_tmpl.routing = FC_RTG_BASIC_LINK; + fc_bls_req_tmpl.type = FC_TYPE_BLS; + fc_bls_req_tmpl.f_ctl = bfa_os_hton3b(FCTL_END_SEQ | FCTL_SI_XFER); + fc_bls_req_tmpl.rx_id = FC_RXID_ANY; + + /* + * fc_bls_rsp_tmpl + */ + fc_bls_rsp_tmpl.routing = FC_RTG_BASIC_LINK; + fc_bls_rsp_tmpl.cat_info = FC_CAT_BA_ACC; + fc_bls_rsp_tmpl.type = FC_TYPE_BLS; + fc_bls_rsp_tmpl.f_ctl = + bfa_os_hton3b(FCTL_EC_RESP | FCTL_SEQ_INI | FCTL_LS_EXCH | + FCTL_END_SEQ | FCTL_SI_XFER); + fc_bls_rsp_tmpl.rx_id = FC_RXID_ANY; + + /* + * ba_acc_tmpl + */ + ba_acc_tmpl.seq_id_valid = 0; + ba_acc_tmpl.low_seq_cnt = 0; + ba_acc_tmpl.high_seq_cnt = 0xFFFF; + + /* + * plogi_tmpl + */ + plogi_tmpl.csp.verhi = FC_PH_VER_PH_3; + plogi_tmpl.csp.verlo = FC_PH_VER_4_3; + plogi_tmpl.csp.bbcred = bfa_os_htons(0x0004); + plogi_tmpl.csp.ciro = 0x1; + plogi_tmpl.csp.cisc = 0x0; + plogi_tmpl.csp.altbbcred = 0x0; + plogi_tmpl.csp.conseq = bfa_os_htons(0x00FF); + plogi_tmpl.csp.ro_bitmap = bfa_os_htons(0x0002); + plogi_tmpl.csp.e_d_tov = bfa_os_htonl(2000); + + plogi_tmpl.class3.class_valid = 1; + plogi_tmpl.class3.sequential = 1; + plogi_tmpl.class3.conseq = 0xFF; + plogi_tmpl.class3.ospx = 1; + + /* + * prli_tmpl + */ + prli_tmpl.command = FC_ELS_PRLI; + prli_tmpl.pglen = 0x10; + prli_tmpl.pagebytes = bfa_os_htons(0x0014); + prli_tmpl.parampage.type = FC_TYPE_FCP; + prli_tmpl.parampage.imagepair = 1; + prli_tmpl.parampage.servparams.rxrdisab = 1; + + /* + * rrq_tmpl + */ + rrq_tmpl.els_cmd.els_code = FC_ELS_RRQ; + + /* + * fcp_fchs_tmpl + */ + fcp_fchs_tmpl.routing = FC_RTG_FC4_DEV_DATA; + fcp_fchs_tmpl.cat_info = FC_CAT_UNSOLICIT_CMD; + fcp_fchs_tmpl.type = FC_TYPE_FCP; + fcp_fchs_tmpl.f_ctl = + bfa_os_hton3b(FCTL_FS_EXCH | FCTL_END_SEQ | FCTL_SI_XFER); + fcp_fchs_tmpl.seq_id = 1; + fcp_fchs_tmpl.rx_id = FC_RXID_ANY; +} + +static void +fc_gs_fchdr_build(struct fchs_s *fchs, u32 d_id, u32 s_id, + u32 ox_id) +{ + bfa_os_memset(fchs, 0, sizeof(struct fchs_s)); + + fchs->routing = FC_RTG_FC4_DEV_DATA; + fchs->cat_info = FC_CAT_UNSOLICIT_CTRL; + fchs->type = FC_TYPE_SERVICES; + fchs->f_ctl = + bfa_os_hton3b(FCTL_SEQ_INI | FCTL_FS_EXCH | FCTL_END_SEQ | + FCTL_SI_XFER); + fchs->rx_id = FC_RXID_ANY; + fchs->d_id = (d_id); + fchs->s_id = (s_id); + fchs->ox_id = bfa_os_htons(ox_id); + + /** + * @todo no need to set ox_id for request + * no need to set rx_id for response + */ +} + +void +fc_els_req_build(struct fchs_s *fchs, u32 d_id, u32 s_id, + u16 ox_id) +{ + bfa_os_memcpy(fchs, &fc_els_req_tmpl, sizeof(struct fchs_s)); + fchs->d_id = (d_id); + fchs->s_id = (s_id); + fchs->ox_id = bfa_os_htons(ox_id); +} + +static void +fc_els_rsp_build(struct fchs_s *fchs, u32 d_id, u32 s_id, + u16 ox_id) +{ + bfa_os_memcpy(fchs, &fc_els_rsp_tmpl, sizeof(struct fchs_s)); + fchs->d_id = d_id; + fchs->s_id = s_id; + fchs->ox_id = ox_id; +} + +enum fc_parse_status +fc_els_rsp_parse(struct fchs_s *fchs, int len) +{ + struct fc_els_cmd_s *els_cmd = (struct fc_els_cmd_s *) (fchs + 1); + struct fc_ls_rjt_s *ls_rjt = (struct fc_ls_rjt_s *) els_cmd; + + len = len; + + switch (els_cmd->els_code) { + case FC_ELS_LS_RJT: + if (ls_rjt->reason_code == FC_LS_RJT_RSN_LOGICAL_BUSY) + return (FC_PARSE_BUSY); + else + return (FC_PARSE_FAILURE); + + case FC_ELS_ACC: + return (FC_PARSE_OK); + } + return (FC_PARSE_OK); +} + +static void +fc_bls_rsp_build(struct fchs_s *fchs, u32 d_id, u32 s_id, + u16 ox_id) +{ + bfa_os_memcpy(fchs, &fc_bls_rsp_tmpl, sizeof(struct fchs_s)); + fchs->d_id = d_id; + fchs->s_id = s_id; + fchs->ox_id = ox_id; +} + +static u16 +fc_plogi_x_build(struct fchs_s *fchs, void *pld, u32 d_id, u32 s_id, + u16 ox_id, wwn_t port_name, wwn_t node_name, + u16 pdu_size, u8 els_code) +{ + struct fc_logi_s *plogi = (struct fc_logi_s *) (pld); + + bfa_os_memcpy(plogi, &plogi_tmpl, sizeof(struct fc_logi_s)); + + plogi->els_cmd.els_code = els_code; + if (els_code == FC_ELS_PLOGI) + fc_els_req_build(fchs, d_id, s_id, ox_id); + else + fc_els_rsp_build(fchs, d_id, s_id, ox_id); + + plogi->csp.rxsz = plogi->class3.rxsz = bfa_os_htons(pdu_size); + + bfa_os_memcpy(&plogi->port_name, &port_name, sizeof(wwn_t)); + bfa_os_memcpy(&plogi->node_name, &node_name, sizeof(wwn_t)); + + return (sizeof(struct fc_logi_s)); +} + +u16 +fc_flogi_build(struct fchs_s *fchs, struct fc_logi_s *flogi, u32 s_id, + u16 ox_id, wwn_t port_name, wwn_t node_name, + u16 pdu_size, u8 set_npiv, u8 set_auth, + u16 local_bb_credits) +{ + u32 d_id = bfa_os_hton3b(FC_FABRIC_PORT); + u32 *vvl_info; + + bfa_os_memcpy(flogi, &plogi_tmpl, sizeof(struct fc_logi_s)); + + flogi->els_cmd.els_code = FC_ELS_FLOGI; + fc_els_req_build(fchs, d_id, s_id, ox_id); + + flogi->csp.rxsz = flogi->class3.rxsz = bfa_os_htons(pdu_size); + flogi->port_name = port_name; + flogi->node_name = node_name; + + /* + * Set the NPIV Capability Bit ( word 1, bit 31) of Common + * Service Parameters. + */ + flogi->csp.ciro = set_npiv; + + /* set AUTH capability */ + flogi->csp.security = set_auth; + + flogi->csp.bbcred = bfa_os_htons(local_bb_credits); + + /* Set brcd token in VVL */ + vvl_info = (u32 *)&flogi->vvl[0]; + + /* set the flag to indicate the presence of VVL */ + flogi->csp.npiv_supp = 1; /* @todo. field name is not correct */ + vvl_info[0] = bfa_os_htonl(FLOGI_VVL_BRCD); + + return (sizeof(struct fc_logi_s)); +} + +u16 +fc_flogi_acc_build(struct fchs_s *fchs, struct fc_logi_s *flogi, u32 s_id, + u16 ox_id, wwn_t port_name, wwn_t node_name, + u16 pdu_size, u16 local_bb_credits) +{ + u32 d_id = 0; + + bfa_os_memcpy(flogi, &plogi_tmpl, sizeof(struct fc_logi_s)); + fc_els_rsp_build(fchs, d_id, s_id, ox_id); + + flogi->els_cmd.els_code = FC_ELS_ACC; + flogi->csp.rxsz = flogi->class3.rxsz = bfa_os_htons(pdu_size); + flogi->port_name = port_name; + flogi->node_name = node_name; + + flogi->csp.bbcred = bfa_os_htons(local_bb_credits); + + return (sizeof(struct fc_logi_s)); +} + +u16 +fc_fdisc_build(struct fchs_s *fchs, struct fc_logi_s *flogi, u32 s_id, + u16 ox_id, wwn_t port_name, wwn_t node_name, + u16 pdu_size) +{ + u32 d_id = bfa_os_hton3b(FC_FABRIC_PORT); + + bfa_os_memcpy(flogi, &plogi_tmpl, sizeof(struct fc_logi_s)); + + flogi->els_cmd.els_code = FC_ELS_FDISC; + fc_els_req_build(fchs, d_id, s_id, ox_id); + + flogi->csp.rxsz = flogi->class3.rxsz = bfa_os_htons(pdu_size); + flogi->port_name = port_name; + flogi->node_name = node_name; + + return (sizeof(struct fc_logi_s)); +} + +u16 +fc_plogi_build(struct fchs_s *fchs, void *pld, u32 d_id, u32 s_id, + u16 ox_id, wwn_t port_name, wwn_t node_name, + u16 pdu_size) +{ + return fc_plogi_x_build(fchs, pld, d_id, s_id, ox_id, port_name, + node_name, pdu_size, FC_ELS_PLOGI); +} + +u16 +fc_plogi_acc_build(struct fchs_s *fchs, void *pld, u32 d_id, u32 s_id, + u16 ox_id, wwn_t port_name, wwn_t node_name, + u16 pdu_size) +{ + return fc_plogi_x_build(fchs, pld, d_id, s_id, ox_id, port_name, + node_name, pdu_size, FC_ELS_ACC); +} + +enum fc_parse_status +fc_plogi_rsp_parse(struct fchs_s *fchs, int len, wwn_t port_name) +{ + struct fc_els_cmd_s *els_cmd = (struct fc_els_cmd_s *) (fchs + 1); + struct fc_logi_s *plogi; + struct fc_ls_rjt_s *ls_rjt; + + switch (els_cmd->els_code) { + case FC_ELS_LS_RJT: + ls_rjt = (struct fc_ls_rjt_s *) (fchs + 1); + if (ls_rjt->reason_code == FC_LS_RJT_RSN_LOGICAL_BUSY) + return (FC_PARSE_BUSY); + else + return (FC_PARSE_FAILURE); + case FC_ELS_ACC: + plogi = (struct fc_logi_s *) (fchs + 1); + if (len < sizeof(struct fc_logi_s)) + return (FC_PARSE_FAILURE); + + if (!wwn_is_equal(plogi->port_name, port_name)) + return (FC_PARSE_FAILURE); + + if (!plogi->class3.class_valid) + return (FC_PARSE_FAILURE); + + if (bfa_os_ntohs(plogi->class3.rxsz) < (FC_MIN_PDUSZ)) + return (FC_PARSE_FAILURE); + + return (FC_PARSE_OK); + default: + return (FC_PARSE_FAILURE); + } +} + +enum fc_parse_status +fc_plogi_parse(struct fchs_s *fchs) +{ + struct fc_logi_s *plogi = (struct fc_logi_s *) (fchs + 1); + + if (plogi->class3.class_valid != 1) + return FC_PARSE_FAILURE; + + if ((bfa_os_ntohs(plogi->class3.rxsz) < FC_MIN_PDUSZ) + || (bfa_os_ntohs(plogi->class3.rxsz) > FC_MAX_PDUSZ) + || (plogi->class3.rxsz == 0)) + return (FC_PARSE_FAILURE); + + return FC_PARSE_OK; +} + +u16 +fc_prli_build(struct fchs_s *fchs, void *pld, u32 d_id, u32 s_id, + u16 ox_id) +{ + struct fc_prli_s *prli = (struct fc_prli_s *) (pld); + + fc_els_req_build(fchs, d_id, s_id, ox_id); + bfa_os_memcpy(prli, &prli_tmpl, sizeof(struct fc_prli_s)); + + prli->command = FC_ELS_PRLI; + prli->parampage.servparams.initiator = 1; + prli->parampage.servparams.retry = 1; + prli->parampage.servparams.rec_support = 1; + prli->parampage.servparams.task_retry_id = 0; + prli->parampage.servparams.confirm = 1; + + return (sizeof(struct fc_prli_s)); +} + +u16 +fc_prli_acc_build(struct fchs_s *fchs, void *pld, u32 d_id, u32 s_id, + u16 ox_id, enum bfa_port_role role) +{ + struct fc_prli_s *prli = (struct fc_prli_s *) (pld); + + fc_els_rsp_build(fchs, d_id, s_id, ox_id); + bfa_os_memcpy(prli, &prli_tmpl, sizeof(struct fc_prli_s)); + + prli->command = FC_ELS_ACC; + + if ((role & BFA_PORT_ROLE_FCP_TM) == BFA_PORT_ROLE_FCP_TM) + prli->parampage.servparams.target = 1; + else + prli->parampage.servparams.initiator = 1; + + prli->parampage.rspcode = FC_PRLI_ACC_XQTD; + + return (sizeof(struct fc_prli_s)); +} + +enum fc_parse_status +fc_prli_rsp_parse(struct fc_prli_s *prli, int len) +{ + if (len < sizeof(struct fc_prli_s)) + return (FC_PARSE_FAILURE); + + if (prli->command != FC_ELS_ACC) + return (FC_PARSE_FAILURE); + + if ((prli->parampage.rspcode != FC_PRLI_ACC_XQTD) + && (prli->parampage.rspcode != FC_PRLI_ACC_PREDEF_IMG)) + return (FC_PARSE_FAILURE); + + if (prli->parampage.servparams.target != 1) + return (FC_PARSE_FAILURE); + + return (FC_PARSE_OK); +} + +enum fc_parse_status +fc_prli_parse(struct fc_prli_s *prli) +{ + if (prli->parampage.type != FC_TYPE_FCP) + return (FC_PARSE_FAILURE); + + if (!prli->parampage.imagepair) + return (FC_PARSE_FAILURE); + + if (!prli->parampage.servparams.initiator) + return (FC_PARSE_FAILURE); + + return (FC_PARSE_OK); +} + +u16 +fc_logo_build(struct fchs_s *fchs, struct fc_logo_s *logo, u32 d_id, + u32 s_id, u16 ox_id, wwn_t port_name) +{ + fc_els_req_build(fchs, d_id, s_id, ox_id); + + memset(logo, '\0', sizeof(struct fc_logo_s)); + logo->els_cmd.els_code = FC_ELS_LOGO; + logo->nport_id = (s_id); + logo->orig_port_name = port_name; + + return (sizeof(struct fc_logo_s)); +} + +static u16 +fc_adisc_x_build(struct fchs_s *fchs, struct fc_adisc_s *adisc, u32 d_id, + u32 s_id, u16 ox_id, wwn_t port_name, + wwn_t node_name, u8 els_code) +{ + memset(adisc, '\0', sizeof(struct fc_adisc_s)); + + adisc->els_cmd.els_code = els_code; + + if (els_code == FC_ELS_ADISC) + fc_els_req_build(fchs, d_id, s_id, ox_id); + else + fc_els_rsp_build(fchs, d_id, s_id, ox_id); + + adisc->orig_HA = 0; + adisc->orig_port_name = port_name; + adisc->orig_node_name = node_name; + adisc->nport_id = (s_id); + + return (sizeof(struct fc_adisc_s)); +} + +u16 +fc_adisc_build(struct fchs_s *fchs, struct fc_adisc_s *adisc, u32 d_id, + u32 s_id, u16 ox_id, wwn_t port_name, + wwn_t node_name) +{ + return fc_adisc_x_build(fchs, adisc, d_id, s_id, ox_id, port_name, + node_name, FC_ELS_ADISC); +} + +u16 +fc_adisc_acc_build(struct fchs_s *fchs, struct fc_adisc_s *adisc, u32 d_id, + u32 s_id, u16 ox_id, wwn_t port_name, + wwn_t node_name) +{ + return fc_adisc_x_build(fchs, adisc, d_id, s_id, ox_id, port_name, + node_name, FC_ELS_ACC); +} + +enum fc_parse_status +fc_adisc_rsp_parse(struct fc_adisc_s *adisc, int len, wwn_t port_name, + wwn_t node_name) +{ + + if (len < sizeof(struct fc_adisc_s)) + return (FC_PARSE_FAILURE); + + if (adisc->els_cmd.els_code != FC_ELS_ACC) + return (FC_PARSE_FAILURE); + + if (!wwn_is_equal(adisc->orig_port_name, port_name)) + return (FC_PARSE_FAILURE); + + return (FC_PARSE_OK); +} + +enum fc_parse_status +fc_adisc_parse(struct fchs_s *fchs, void *pld, u32 host_dap, + wwn_t node_name, wwn_t port_name) +{ + struct fc_adisc_s *adisc = (struct fc_adisc_s *) pld; + + if (adisc->els_cmd.els_code != FC_ELS_ACC) + return (FC_PARSE_FAILURE); + + if ((adisc->nport_id == (host_dap)) + && wwn_is_equal(adisc->orig_port_name, port_name) + && wwn_is_equal(adisc->orig_node_name, node_name)) + return (FC_PARSE_OK); + + return (FC_PARSE_FAILURE); +} + +enum fc_parse_status +fc_pdisc_parse(struct fchs_s *fchs, wwn_t node_name, wwn_t port_name) +{ + struct fc_logi_s *pdisc = (struct fc_logi_s *) (fchs + 1); + + if (pdisc->class3.class_valid != 1) + return FC_PARSE_FAILURE; + + if ((bfa_os_ntohs(pdisc->class3.rxsz) < + (FC_MIN_PDUSZ - sizeof(struct fchs_s))) + || (pdisc->class3.rxsz == 0)) + return (FC_PARSE_FAILURE); + + if (!wwn_is_equal(pdisc->port_name, port_name)) + return (FC_PARSE_FAILURE); + + if (!wwn_is_equal(pdisc->node_name, node_name)) + return (FC_PARSE_FAILURE); + + return FC_PARSE_OK; +} + +u16 +fc_abts_build(struct fchs_s *fchs, u32 d_id, u32 s_id, u16 ox_id) +{ + bfa_os_memcpy(fchs, &fc_bls_req_tmpl, sizeof(struct fchs_s)); + fchs->cat_info = FC_CAT_ABTS; + fchs->d_id = (d_id); + fchs->s_id = (s_id); + fchs->ox_id = bfa_os_htons(ox_id); + + return (sizeof(struct fchs_s)); +} + +enum fc_parse_status +fc_abts_rsp_parse(struct fchs_s *fchs, int len) +{ + if ((fchs->cat_info == FC_CAT_BA_ACC) + || (fchs->cat_info == FC_CAT_BA_RJT)) + return (FC_PARSE_OK); + + return (FC_PARSE_FAILURE); +} + +u16 +fc_rrq_build(struct fchs_s *fchs, struct fc_rrq_s *rrq, u32 d_id, + u32 s_id, u16 ox_id, u16 rrq_oxid) +{ + fc_els_req_build(fchs, d_id, s_id, ox_id); + + /* + * build rrq payload + */ + bfa_os_memcpy(rrq, &rrq_tmpl, sizeof(struct fc_rrq_s)); + rrq->s_id = (s_id); + rrq->ox_id = bfa_os_htons(rrq_oxid); + rrq->rx_id = FC_RXID_ANY; + + return (sizeof(struct fc_rrq_s)); +} + +u16 +fc_logo_acc_build(struct fchs_s *fchs, void *pld, u32 d_id, u32 s_id, + u16 ox_id) +{ + struct fc_els_cmd_s *acc = pld; + + fc_els_rsp_build(fchs, d_id, s_id, ox_id); + + memset(acc, 0, sizeof(struct fc_els_cmd_s)); + acc->els_code = FC_ELS_ACC; + + return (sizeof(struct fc_els_cmd_s)); +} + +u16 +fc_ls_rjt_build(struct fchs_s *fchs, struct fc_ls_rjt_s *ls_rjt, u32 d_id, + u32 s_id, u16 ox_id, u8 reason_code, + u8 reason_code_expl) +{ + fc_els_rsp_build(fchs, d_id, s_id, ox_id); + memset(ls_rjt, 0, sizeof(struct fc_ls_rjt_s)); + + ls_rjt->els_cmd.els_code = FC_ELS_LS_RJT; + ls_rjt->reason_code = reason_code; + ls_rjt->reason_code_expl = reason_code_expl; + ls_rjt->vendor_unique = 0x00; + + return (sizeof(struct fc_ls_rjt_s)); +} + +u16 +fc_ba_acc_build(struct fchs_s *fchs, struct fc_ba_acc_s *ba_acc, u32 d_id, + u32 s_id, u16 ox_id, u16 rx_id) +{ + fc_bls_rsp_build(fchs, d_id, s_id, ox_id); + + bfa_os_memcpy(ba_acc, &ba_acc_tmpl, sizeof(struct fc_ba_acc_s)); + + fchs->rx_id = rx_id; + + ba_acc->ox_id = fchs->ox_id; + ba_acc->rx_id = fchs->rx_id; + + return (sizeof(struct fc_ba_acc_s)); +} + +u16 +fc_ls_acc_build(struct fchs_s *fchs, struct fc_els_cmd_s *els_cmd, + u32 d_id, u32 s_id, u16 ox_id) +{ + fc_els_rsp_build(fchs, d_id, s_id, ox_id); + memset(els_cmd, 0, sizeof(struct fc_els_cmd_s)); + els_cmd->els_code = FC_ELS_ACC; + + return (sizeof(struct fc_els_cmd_s)); +} + +int +fc_logout_params_pages(struct fchs_s *fc_frame, u8 els_code) +{ + int num_pages = 0; + struct fc_prlo_s *prlo; + struct fc_tprlo_s *tprlo; + + if (els_code == FC_ELS_PRLO) { + prlo = (struct fc_prlo_s *) (fc_frame + 1); + num_pages = (bfa_os_ntohs(prlo->payload_len) - 4) / 16; + } else { + tprlo = (struct fc_tprlo_s *) (fc_frame + 1); + num_pages = (bfa_os_ntohs(tprlo->payload_len) - 4) / 16; + } + return num_pages; +} + +u16 +fc_tprlo_acc_build(struct fchs_s *fchs, struct fc_tprlo_acc_s *tprlo_acc, + u32 d_id, u32 s_id, u16 ox_id, + int num_pages) +{ + int page; + + fc_els_rsp_build(fchs, d_id, s_id, ox_id); + + memset(tprlo_acc, 0, (num_pages * 16) + 4); + tprlo_acc->command = FC_ELS_ACC; + + tprlo_acc->page_len = 0x10; + tprlo_acc->payload_len = bfa_os_htons((num_pages * 16) + 4); + + for (page = 0; page < num_pages; page++) { + tprlo_acc->tprlo_acc_params[page].opa_valid = 0; + tprlo_acc->tprlo_acc_params[page].rpa_valid = 0; + tprlo_acc->tprlo_acc_params[page].fc4type_csp = FC_TYPE_FCP; + tprlo_acc->tprlo_acc_params[page].orig_process_assc = 0; + tprlo_acc->tprlo_acc_params[page].resp_process_assc = 0; + } + return (bfa_os_ntohs(tprlo_acc->payload_len)); +} + +u16 +fc_prlo_acc_build(struct fchs_s *fchs, struct fc_prlo_acc_s *prlo_acc, + u32 d_id, u32 s_id, u16 ox_id, + int num_pages) +{ + int page; + + fc_els_rsp_build(fchs, d_id, s_id, ox_id); + + memset(prlo_acc, 0, (num_pages * 16) + 4); + prlo_acc->command = FC_ELS_ACC; + prlo_acc->page_len = 0x10; + prlo_acc->payload_len = bfa_os_htons((num_pages * 16) + 4); + + for (page = 0; page < num_pages; page++) { + prlo_acc->prlo_acc_params[page].opa_valid = 0; + prlo_acc->prlo_acc_params[page].rpa_valid = 0; + prlo_acc->prlo_acc_params[page].fc4type_csp = FC_TYPE_FCP; + prlo_acc->prlo_acc_params[page].orig_process_assc = 0; + prlo_acc->prlo_acc_params[page].resp_process_assc = 0; + } + + return (bfa_os_ntohs(prlo_acc->payload_len)); +} + +u16 +fc_rnid_build(struct fchs_s *fchs, struct fc_rnid_cmd_s *rnid, u32 d_id, + u32 s_id, u16 ox_id, u32 data_format) +{ + fc_els_req_build(fchs, d_id, s_id, ox_id); + + memset(rnid, 0, sizeof(struct fc_rnid_cmd_s)); + + rnid->els_cmd.els_code = FC_ELS_RNID; + rnid->node_id_data_format = data_format; + + return (sizeof(struct fc_rnid_cmd_s)); +} + +u16 +fc_rnid_acc_build(struct fchs_s *fchs, struct fc_rnid_acc_s *rnid_acc, + u32 d_id, u32 s_id, u16 ox_id, + u32 data_format, + struct fc_rnid_common_id_data_s *common_id_data, + struct fc_rnid_general_topology_data_s *gen_topo_data) +{ + memset(rnid_acc, 0, sizeof(struct fc_rnid_acc_s)); + + fc_els_rsp_build(fchs, d_id, s_id, ox_id); + + rnid_acc->els_cmd.els_code = FC_ELS_ACC; + rnid_acc->node_id_data_format = data_format; + rnid_acc->common_id_data_length = + sizeof(struct fc_rnid_common_id_data_s); + rnid_acc->common_id_data = *common_id_data; + + if (data_format == RNID_NODEID_DATA_FORMAT_DISCOVERY) { + rnid_acc->specific_id_data_length = + sizeof(struct fc_rnid_general_topology_data_s); + bfa_os_assign(rnid_acc->gen_topology_data, *gen_topo_data); + return (sizeof(struct fc_rnid_acc_s)); + } else { + return (sizeof(struct fc_rnid_acc_s) - + sizeof(struct fc_rnid_general_topology_data_s)); + } + +} + +u16 +fc_rpsc_build(struct fchs_s *fchs, struct fc_rpsc_cmd_s *rpsc, u32 d_id, + u32 s_id, u16 ox_id) +{ + fc_els_req_build(fchs, d_id, s_id, ox_id); + + memset(rpsc, 0, sizeof(struct fc_rpsc_cmd_s)); + + rpsc->els_cmd.els_code = FC_ELS_RPSC; + return (sizeof(struct fc_rpsc_cmd_s)); +} + +u16 +fc_rpsc2_build(struct fchs_s *fchs, struct fc_rpsc2_cmd_s *rpsc2, + u32 d_id, u32 s_id, u32 *pid_list, + u16 npids) +{ + u32 dctlr_id = FC_DOMAIN_CTRLR(bfa_os_hton3b(d_id)); + int i = 0; + + fc_els_req_build(fchs, bfa_os_hton3b(dctlr_id), s_id, 0); + + memset(rpsc2, 0, sizeof(struct fc_rpsc2_cmd_s)); + + rpsc2->els_cmd.els_code = FC_ELS_RPSC; + rpsc2->token = bfa_os_htonl(FC_BRCD_TOKEN); + rpsc2->num_pids = bfa_os_htons(npids); + for (i = 0; i < npids; i++) + rpsc2->pid_list[i].pid = pid_list[i]; + + return (sizeof(struct fc_rpsc2_cmd_s) + ((npids - 1) * + (sizeof(u32)))); +} + +u16 +fc_rpsc_acc_build(struct fchs_s *fchs, struct fc_rpsc_acc_s *rpsc_acc, + u32 d_id, u32 s_id, u16 ox_id, + struct fc_rpsc_speed_info_s *oper_speed) +{ + memset(rpsc_acc, 0, sizeof(struct fc_rpsc_acc_s)); + + fc_els_rsp_build(fchs, d_id, s_id, ox_id); + + rpsc_acc->command = FC_ELS_ACC; + rpsc_acc->num_entries = bfa_os_htons(1); + + rpsc_acc->speed_info[0].port_speed_cap = + bfa_os_htons(oper_speed->port_speed_cap); + + rpsc_acc->speed_info[0].port_op_speed = + bfa_os_htons(oper_speed->port_op_speed); + + return (sizeof(struct fc_rpsc_acc_s)); + +} + +/* + * TBD - + * . get rid of unnecessary memsets + */ + +u16 +fc_logo_rsp_parse(struct fchs_s *fchs, int len) +{ + struct fc_els_cmd_s *els_cmd = (struct fc_els_cmd_s *) (fchs + 1); + + len = len; + if (els_cmd->els_code != FC_ELS_ACC) + return FC_PARSE_FAILURE; + + return FC_PARSE_OK; +} + +u16 +fc_pdisc_build(struct fchs_s *fchs, u32 d_id, u32 s_id, + u16 ox_id, wwn_t port_name, wwn_t node_name, + u16 pdu_size) +{ + struct fc_logi_s *pdisc = (struct fc_logi_s *) (fchs + 1); + + bfa_os_memcpy(pdisc, &plogi_tmpl, sizeof(struct fc_logi_s)); + + pdisc->els_cmd.els_code = FC_ELS_PDISC; + fc_els_req_build(fchs, d_id, s_id, ox_id); + + pdisc->csp.rxsz = pdisc->class3.rxsz = bfa_os_htons(pdu_size); + pdisc->port_name = port_name; + pdisc->node_name = node_name; + + return (sizeof(struct fc_logi_s)); +} + +u16 +fc_pdisc_rsp_parse(struct fchs_s *fchs, int len, wwn_t port_name) +{ + struct fc_logi_s *pdisc = (struct fc_logi_s *) (fchs + 1); + + if (len < sizeof(struct fc_logi_s)) + return (FC_PARSE_LEN_INVAL); + + if (pdisc->els_cmd.els_code != FC_ELS_ACC) + return (FC_PARSE_ACC_INVAL); + + if (!wwn_is_equal(pdisc->port_name, port_name)) + return (FC_PARSE_PWWN_NOT_EQUAL); + + if (!pdisc->class3.class_valid) + return (FC_PARSE_NWWN_NOT_EQUAL); + + if (bfa_os_ntohs(pdisc->class3.rxsz) < (FC_MIN_PDUSZ)) + return (FC_PARSE_RXSZ_INVAL); + + return (FC_PARSE_OK); +} + +u16 +fc_prlo_build(struct fchs_s *fchs, u32 d_id, u32 s_id, u16 ox_id, + int num_pages) +{ + struct fc_prlo_s *prlo = (struct fc_prlo_s *) (fchs + 1); + int page; + + fc_els_req_build(fchs, d_id, s_id, ox_id); + memset(prlo, 0, (num_pages * 16) + 4); + prlo->command = FC_ELS_PRLO; + prlo->page_len = 0x10; + prlo->payload_len = bfa_os_htons((num_pages * 16) + 4); + + for (page = 0; page < num_pages; page++) { + prlo->prlo_params[page].type = FC_TYPE_FCP; + prlo->prlo_params[page].opa_valid = 0; + prlo->prlo_params[page].rpa_valid = 0; + prlo->prlo_params[page].orig_process_assc = 0; + prlo->prlo_params[page].resp_process_assc = 0; + } + + return (bfa_os_ntohs(prlo->payload_len)); +} + +u16 +fc_prlo_rsp_parse(struct fchs_s *fchs, int len) +{ + struct fc_prlo_acc_s *prlo = (struct fc_prlo_acc_s *) (fchs + 1); + int num_pages = 0; + int page = 0; + + len = len; + + if (prlo->command != FC_ELS_ACC) + return (FC_PARSE_FAILURE); + + num_pages = ((bfa_os_ntohs(prlo->payload_len)) - 4) / 16; + + for (page = 0; page < num_pages; page++) { + if (prlo->prlo_acc_params[page].type != FC_TYPE_FCP) + return FC_PARSE_FAILURE; + + if (prlo->prlo_acc_params[page].opa_valid != 0) + return FC_PARSE_FAILURE; + + if (prlo->prlo_acc_params[page].rpa_valid != 0) + return FC_PARSE_FAILURE; + + if (prlo->prlo_acc_params[page].orig_process_assc != 0) + return FC_PARSE_FAILURE; + + if (prlo->prlo_acc_params[page].resp_process_assc != 0) + return FC_PARSE_FAILURE; + } + return (FC_PARSE_OK); + +} + +u16 +fc_tprlo_build(struct fchs_s *fchs, u32 d_id, u32 s_id, + u16 ox_id, int num_pages, + enum fc_tprlo_type tprlo_type, u32 tpr_id) +{ + struct fc_tprlo_s *tprlo = (struct fc_tprlo_s *) (fchs + 1); + int page; + + fc_els_req_build(fchs, d_id, s_id, ox_id); + memset(tprlo, 0, (num_pages * 16) + 4); + tprlo->command = FC_ELS_TPRLO; + tprlo->page_len = 0x10; + tprlo->payload_len = bfa_os_htons((num_pages * 16) + 4); + + for (page = 0; page < num_pages; page++) { + tprlo->tprlo_params[page].type = FC_TYPE_FCP; + tprlo->tprlo_params[page].opa_valid = 0; + tprlo->tprlo_params[page].rpa_valid = 0; + tprlo->tprlo_params[page].orig_process_assc = 0; + tprlo->tprlo_params[page].resp_process_assc = 0; + if (tprlo_type == FC_GLOBAL_LOGO) { + tprlo->tprlo_params[page].global_process_logout = 1; + } else if (tprlo_type == FC_TPR_LOGO) { + tprlo->tprlo_params[page].tpo_nport_valid = 1; + tprlo->tprlo_params[page].tpo_nport_id = (tpr_id); + } + } + + return (bfa_os_ntohs(tprlo->payload_len)); +} + +u16 +fc_tprlo_rsp_parse(struct fchs_s *fchs, int len) +{ + struct fc_tprlo_acc_s *tprlo = (struct fc_tprlo_acc_s *) (fchs + 1); + int num_pages = 0; + int page = 0; + + len = len; + + if (tprlo->command != FC_ELS_ACC) + return (FC_PARSE_ACC_INVAL); + + num_pages = (bfa_os_ntohs(tprlo->payload_len) - 4) / 16; + + for (page = 0; page < num_pages; page++) { + if (tprlo->tprlo_acc_params[page].type != FC_TYPE_FCP) + return (FC_PARSE_NOT_FCP); + if (tprlo->tprlo_acc_params[page].opa_valid != 0) + return (FC_PARSE_OPAFLAG_INVAL); + if (tprlo->tprlo_acc_params[page].rpa_valid != 0) + return (FC_PARSE_RPAFLAG_INVAL); + if (tprlo->tprlo_acc_params[page].orig_process_assc != 0) + return (FC_PARSE_OPA_INVAL); + if (tprlo->tprlo_acc_params[page].resp_process_assc != 0) + return (FC_PARSE_RPA_INVAL); + } + return (FC_PARSE_OK); +} + +enum fc_parse_status +fc_rrq_rsp_parse(struct fchs_s *fchs, int len) +{ + struct fc_els_cmd_s *els_cmd = (struct fc_els_cmd_s *) (fchs + 1); + + len = len; + if (els_cmd->els_code != FC_ELS_ACC) + return FC_PARSE_FAILURE; + + return FC_PARSE_OK; +} + +u16 +fc_ba_rjt_build(struct fchs_s *fchs, u32 d_id, u32 s_id, + u16 ox_id, u32 reason_code, + u32 reason_expl) +{ + struct fc_ba_rjt_s *ba_rjt = (struct fc_ba_rjt_s *) (fchs + 1); + + fc_bls_rsp_build(fchs, d_id, s_id, ox_id); + + fchs->cat_info = FC_CAT_BA_RJT; + ba_rjt->reason_code = reason_code; + ba_rjt->reason_expl = reason_expl; + return (sizeof(struct fc_ba_rjt_s)); +} + +static void +fc_gs_cthdr_build(struct ct_hdr_s *cthdr, u32 s_id, u16 cmd_code) +{ + bfa_os_memset(cthdr, 0, sizeof(struct ct_hdr_s)); + cthdr->rev_id = CT_GS3_REVISION; + cthdr->gs_type = CT_GSTYPE_DIRSERVICE; + cthdr->gs_sub_type = CT_GSSUBTYPE_NAMESERVER; + cthdr->cmd_rsp_code = bfa_os_htons(cmd_code); +} + +static void +fc_gs_fdmi_cthdr_build(struct ct_hdr_s *cthdr, u32 s_id, u16 cmd_code) +{ + bfa_os_memset(cthdr, 0, sizeof(struct ct_hdr_s)); + cthdr->rev_id = CT_GS3_REVISION; + cthdr->gs_type = CT_GSTYPE_MGMTSERVICE; + cthdr->gs_sub_type = CT_GSSUBTYPE_HBA_MGMTSERVER; + cthdr->cmd_rsp_code = bfa_os_htons(cmd_code); +} + +static void +fc_gs_ms_cthdr_build(struct ct_hdr_s *cthdr, u32 s_id, u16 cmd_code, + u8 sub_type) +{ + bfa_os_memset(cthdr, 0, sizeof(struct ct_hdr_s)); + cthdr->rev_id = CT_GS3_REVISION; + cthdr->gs_type = CT_GSTYPE_MGMTSERVICE; + cthdr->gs_sub_type = sub_type; + cthdr->cmd_rsp_code = bfa_os_htons(cmd_code); +} + +u16 +fc_gidpn_build(struct fchs_s *fchs, void *pyld, u32 s_id, u16 ox_id, + wwn_t port_name) +{ + + struct ct_hdr_s *cthdr = (struct ct_hdr_s *) pyld; + struct fcgs_gidpn_req_s *gidpn = + (struct fcgs_gidpn_req_s *) (cthdr + 1); + u32 d_id = bfa_os_hton3b(FC_NAME_SERVER); + + fc_gs_fchdr_build(fchs, d_id, s_id, ox_id); + fc_gs_cthdr_build(cthdr, s_id, GS_GID_PN); + + bfa_os_memset(gidpn, 0, sizeof(struct fcgs_gidpn_req_s)); + gidpn->port_name = port_name; + return (sizeof(struct fcgs_gidpn_req_s) + sizeof(struct ct_hdr_s)); +} + +u16 +fc_gpnid_build(struct fchs_s *fchs, void *pyld, u32 s_id, u16 ox_id, + u32 port_id) +{ + + struct ct_hdr_s *cthdr = (struct ct_hdr_s *) pyld; + fcgs_gpnid_req_t *gpnid = (fcgs_gpnid_req_t *) (cthdr + 1); + u32 d_id = bfa_os_hton3b(FC_NAME_SERVER); + + fc_gs_fchdr_build(fchs, d_id, s_id, ox_id); + fc_gs_cthdr_build(cthdr, s_id, GS_GPN_ID); + + bfa_os_memset(gpnid, 0, sizeof(fcgs_gpnid_req_t)); + gpnid->dap = port_id; + return (sizeof(fcgs_gpnid_req_t) + sizeof(struct ct_hdr_s)); +} + +u16 +fc_gnnid_build(struct fchs_s *fchs, void *pyld, u32 s_id, u16 ox_id, + u32 port_id) +{ + + struct ct_hdr_s *cthdr = (struct ct_hdr_s *) pyld; + fcgs_gnnid_req_t *gnnid = (fcgs_gnnid_req_t *) (cthdr + 1); + u32 d_id = bfa_os_hton3b(FC_NAME_SERVER); + + fc_gs_fchdr_build(fchs, d_id, s_id, ox_id); + fc_gs_cthdr_build(cthdr, s_id, GS_GNN_ID); + + bfa_os_memset(gnnid, 0, sizeof(fcgs_gnnid_req_t)); + gnnid->dap = port_id; + return (sizeof(fcgs_gnnid_req_t) + sizeof(struct ct_hdr_s)); +} + +u16 +fc_ct_rsp_parse(struct ct_hdr_s *cthdr) +{ + if (bfa_os_ntohs(cthdr->cmd_rsp_code) != CT_RSP_ACCEPT) { + if (cthdr->reason_code == CT_RSN_LOGICAL_BUSY) + return FC_PARSE_BUSY; + else + return FC_PARSE_FAILURE; + } + + return FC_PARSE_OK; +} + +u16 +fc_scr_build(struct fchs_s *fchs, struct fc_scr_s *scr, u8 set_br_reg, + u32 s_id, u16 ox_id) +{ + u32 d_id = bfa_os_hton3b(FC_FABRIC_CONTROLLER); + + fc_els_req_build(fchs, d_id, s_id, ox_id); + + bfa_os_memset(scr, 0, sizeof(struct fc_scr_s)); + scr->command = FC_ELS_SCR; + scr->reg_func = FC_SCR_REG_FUNC_FULL; + if (set_br_reg) + scr->vu_reg_func = FC_VU_SCR_REG_FUNC_FABRIC_NAME_CHANGE; + + return (sizeof(struct fc_scr_s)); +} + +u16 +fc_rscn_build(struct fchs_s *fchs, struct fc_rscn_pl_s *rscn, u32 s_id, + u16 ox_id) +{ + u32 d_id = bfa_os_hton3b(FC_FABRIC_CONTROLLER); + u16 payldlen; + + fc_els_req_build(fchs, d_id, s_id, ox_id); + rscn->command = FC_ELS_RSCN; + rscn->pagelen = sizeof(rscn->event[0]); + + payldlen = sizeof(u32) + rscn->pagelen; + rscn->payldlen = bfa_os_htons(payldlen); + + rscn->event[0].format = FC_RSCN_FORMAT_PORTID; + rscn->event[0].portid = s_id; + + return (sizeof(struct fc_rscn_pl_s)); +} + +u16 +fc_rftid_build(struct fchs_s *fchs, void *pyld, u32 s_id, u16 ox_id, + enum bfa_port_role roles) +{ + struct ct_hdr_s *cthdr = (struct ct_hdr_s *) pyld; + struct fcgs_rftid_req_s *rftid = + (struct fcgs_rftid_req_s *) (cthdr + 1); + u32 type_value, d_id = bfa_os_hton3b(FC_NAME_SERVER); + u8 index; + + fc_gs_fchdr_build(fchs, d_id, s_id, ox_id); + fc_gs_cthdr_build(cthdr, s_id, GS_RFT_ID); + + bfa_os_memset(rftid, 0, sizeof(struct fcgs_rftid_req_s)); + + rftid->dap = s_id; + + /* By default, FCP FC4 Type is registered */ + index = FC_TYPE_FCP >> 5; + type_value = 1 << (FC_TYPE_FCP % 32); + rftid->fc4_type[index] = bfa_os_htonl(type_value); + + if (roles & BFA_PORT_ROLE_FCP_IPFC) { + index = FC_TYPE_IP >> 5; + type_value = 1 << (FC_TYPE_IP % 32); + rftid->fc4_type[index] |= bfa_os_htonl(type_value); + } + + return (sizeof(struct fcgs_rftid_req_s) + sizeof(struct ct_hdr_s)); +} + +u16 +fc_rftid_build_sol(struct fchs_s *fchs, void *pyld, u32 s_id, + u16 ox_id, u8 *fc4_bitmap, + u32 bitmap_size) +{ + struct ct_hdr_s *cthdr = (struct ct_hdr_s *) pyld; + struct fcgs_rftid_req_s *rftid = + (struct fcgs_rftid_req_s *) (cthdr + 1); + u32 d_id = bfa_os_hton3b(FC_NAME_SERVER); + + fc_gs_fchdr_build(fchs, d_id, s_id, ox_id); + fc_gs_cthdr_build(cthdr, s_id, GS_RFT_ID); + + bfa_os_memset(rftid, 0, sizeof(struct fcgs_rftid_req_s)); + + rftid->dap = s_id; + bfa_os_memcpy((void *)rftid->fc4_type, (void *)fc4_bitmap, + (bitmap_size < 32 ? bitmap_size : 32)); + + return (sizeof(struct fcgs_rftid_req_s) + sizeof(struct ct_hdr_s)); +} + +u16 +fc_rffid_build(struct fchs_s *fchs, void *pyld, u32 s_id, u16 ox_id, + u8 fc4_type, u8 fc4_ftrs) +{ + struct ct_hdr_s *cthdr = (struct ct_hdr_s *) pyld; + struct fcgs_rffid_req_s *rffid = + (struct fcgs_rffid_req_s *) (cthdr + 1); + u32 d_id = bfa_os_hton3b(FC_NAME_SERVER); + + fc_gs_fchdr_build(fchs, d_id, s_id, ox_id); + fc_gs_cthdr_build(cthdr, s_id, GS_RFF_ID); + + bfa_os_memset(rffid, 0, sizeof(struct fcgs_rffid_req_s)); + + rffid->dap = s_id; + rffid->fc4ftr_bits = fc4_ftrs; + rffid->fc4_type = fc4_type; + + return (sizeof(struct fcgs_rffid_req_s) + sizeof(struct ct_hdr_s)); +} + +u16 +fc_rspnid_build(struct fchs_s *fchs, void *pyld, u32 s_id, u16 ox_id, + u8 *name) +{ + + struct ct_hdr_s *cthdr = (struct ct_hdr_s *) pyld; + struct fcgs_rspnid_req_s *rspnid = + (struct fcgs_rspnid_req_s *) (cthdr + 1); + u32 d_id = bfa_os_hton3b(FC_NAME_SERVER); + + fc_gs_fchdr_build(fchs, d_id, s_id, ox_id); + fc_gs_cthdr_build(cthdr, s_id, GS_RSPN_ID); + + bfa_os_memset(rspnid, 0, sizeof(struct fcgs_rspnid_req_s)); + + rspnid->dap = s_id; + rspnid->spn_len = (u8) strlen((char *)name); + strncpy((char *)rspnid->spn, (char *)name, rspnid->spn_len); + + return (sizeof(struct fcgs_rspnid_req_s) + sizeof(struct ct_hdr_s)); +} + +u16 +fc_gid_ft_build(struct fchs_s *fchs, void *pyld, u32 s_id, + u8 fc4_type) +{ + + struct ct_hdr_s *cthdr = (struct ct_hdr_s *) pyld; + struct fcgs_gidft_req_s *gidft = + (struct fcgs_gidft_req_s *) (cthdr + 1); + u32 d_id = bfa_os_hton3b(FC_NAME_SERVER); + + fc_gs_fchdr_build(fchs, d_id, s_id, 0); + + fc_gs_cthdr_build(cthdr, s_id, GS_GID_FT); + + bfa_os_memset(gidft, 0, sizeof(struct fcgs_gidft_req_s)); + gidft->fc4_type = fc4_type; + gidft->domain_id = 0; + gidft->area_id = 0; + + return (sizeof(struct fcgs_gidft_req_s) + sizeof(struct ct_hdr_s)); +} + +u16 +fc_rpnid_build(struct fchs_s *fchs, void *pyld, u32 s_id, u32 port_id, + wwn_t port_name) +{ + struct ct_hdr_s *cthdr = (struct ct_hdr_s *) pyld; + struct fcgs_rpnid_req_s *rpnid = + (struct fcgs_rpnid_req_s *) (cthdr + 1); + u32 d_id = bfa_os_hton3b(FC_NAME_SERVER); + + fc_gs_fchdr_build(fchs, d_id, s_id, 0); + fc_gs_cthdr_build(cthdr, s_id, GS_RPN_ID); + + bfa_os_memset(rpnid, 0, sizeof(struct fcgs_rpnid_req_s)); + rpnid->port_id = port_id; + rpnid->port_name = port_name; + + return (sizeof(struct fcgs_rpnid_req_s) + sizeof(struct ct_hdr_s)); +} + +u16 +fc_rnnid_build(struct fchs_s *fchs, void *pyld, u32 s_id, u32 port_id, + wwn_t node_name) +{ + struct ct_hdr_s *cthdr = (struct ct_hdr_s *) pyld; + struct fcgs_rnnid_req_s *rnnid = + (struct fcgs_rnnid_req_s *) (cthdr + 1); + u32 d_id = bfa_os_hton3b(FC_NAME_SERVER); + + fc_gs_fchdr_build(fchs, d_id, s_id, 0); + fc_gs_cthdr_build(cthdr, s_id, GS_RNN_ID); + + bfa_os_memset(rnnid, 0, sizeof(struct fcgs_rnnid_req_s)); + rnnid->port_id = port_id; + rnnid->node_name = node_name; + + return (sizeof(struct fcgs_rnnid_req_s) + sizeof(struct ct_hdr_s)); +} + +u16 +fc_rcsid_build(struct fchs_s *fchs, void *pyld, u32 s_id, u32 port_id, + u32 cos) +{ + struct ct_hdr_s *cthdr = (struct ct_hdr_s *) pyld; + struct fcgs_rcsid_req_s *rcsid = + (struct fcgs_rcsid_req_s *) (cthdr + 1); + u32 d_id = bfa_os_hton3b(FC_NAME_SERVER); + + fc_gs_fchdr_build(fchs, d_id, s_id, 0); + fc_gs_cthdr_build(cthdr, s_id, GS_RCS_ID); + + bfa_os_memset(rcsid, 0, sizeof(struct fcgs_rcsid_req_s)); + rcsid->port_id = port_id; + rcsid->cos = cos; + + return (sizeof(struct fcgs_rcsid_req_s) + sizeof(struct ct_hdr_s)); +} + +u16 +fc_rptid_build(struct fchs_s *fchs, void *pyld, u32 s_id, u32 port_id, + u8 port_type) +{ + struct ct_hdr_s *cthdr = (struct ct_hdr_s *) pyld; + struct fcgs_rptid_req_s *rptid = + (struct fcgs_rptid_req_s *) (cthdr + 1); + u32 d_id = bfa_os_hton3b(FC_NAME_SERVER); + + fc_gs_fchdr_build(fchs, d_id, s_id, 0); + fc_gs_cthdr_build(cthdr, s_id, GS_RPT_ID); + + bfa_os_memset(rptid, 0, sizeof(struct fcgs_rptid_req_s)); + rptid->port_id = port_id; + rptid->port_type = port_type; + + return (sizeof(struct fcgs_rptid_req_s) + sizeof(struct ct_hdr_s)); +} + +u16 +fc_ganxt_build(struct fchs_s *fchs, void *pyld, u32 s_id, u32 port_id) +{ + struct ct_hdr_s *cthdr = (struct ct_hdr_s *) pyld; + struct fcgs_ganxt_req_s *ganxt = + (struct fcgs_ganxt_req_s *) (cthdr + 1); + u32 d_id = bfa_os_hton3b(FC_NAME_SERVER); + + fc_gs_fchdr_build(fchs, d_id, s_id, 0); + fc_gs_cthdr_build(cthdr, s_id, GS_GA_NXT); + + bfa_os_memset(ganxt, 0, sizeof(struct fcgs_ganxt_req_s)); + ganxt->port_id = port_id; + + return (sizeof(struct ct_hdr_s) + sizeof(struct fcgs_ganxt_req_s)); +} + +/* + * Builds fc hdr and ct hdr for FDMI requests. + */ +u16 +fc_fdmi_reqhdr_build(struct fchs_s *fchs, void *pyld, u32 s_id, + u16 cmd_code) +{ + + struct ct_hdr_s *cthdr = (struct ct_hdr_s *) pyld; + u32 d_id = bfa_os_hton3b(FC_MGMT_SERVER); + + fc_gs_fchdr_build(fchs, d_id, s_id, 0); + fc_gs_fdmi_cthdr_build(cthdr, s_id, cmd_code); + + return (sizeof(struct ct_hdr_s)); +} + +/* + * Given a FC4 Type, this function returns a fc4 type bitmask + */ +void +fc_get_fc4type_bitmask(u8 fc4_type, u8 *bit_mask) +{ + u8 index; + u32 *ptr = (u32 *) bit_mask; + u32 type_value; + + /* + * @todo : Check for bitmask size + */ + + index = fc4_type >> 5; + type_value = 1 << (fc4_type % 32); + ptr[index] = bfa_os_htonl(type_value); + +} + +/* + * GMAL Request + */ +u16 +fc_gmal_req_build(struct fchs_s *fchs, void *pyld, u32 s_id, wwn_t wwn) +{ + struct ct_hdr_s *cthdr = (struct ct_hdr_s *) pyld; + fcgs_gmal_req_t *gmal = (fcgs_gmal_req_t *) (cthdr + 1); + u32 d_id = bfa_os_hton3b(FC_MGMT_SERVER); + + fc_gs_fchdr_build(fchs, d_id, s_id, 0); + fc_gs_ms_cthdr_build(cthdr, s_id, GS_FC_GMAL_CMD, + CT_GSSUBTYPE_CFGSERVER); + + bfa_os_memset(gmal, 0, sizeof(fcgs_gmal_req_t)); + gmal->wwn = wwn; + + return (sizeof(struct ct_hdr_s) + sizeof(fcgs_gmal_req_t)); +} + +/* + * GFN (Get Fabric Name) Request + */ +u16 +fc_gfn_req_build(struct fchs_s *fchs, void *pyld, u32 s_id, wwn_t wwn) +{ + struct ct_hdr_s *cthdr = (struct ct_hdr_s *) pyld; + fcgs_gfn_req_t *gfn = (fcgs_gfn_req_t *) (cthdr + 1); + u32 d_id = bfa_os_hton3b(FC_MGMT_SERVER); + + fc_gs_fchdr_build(fchs, d_id, s_id, 0); + fc_gs_ms_cthdr_build(cthdr, s_id, GS_FC_GFN_CMD, + CT_GSSUBTYPE_CFGSERVER); + + bfa_os_memset(gfn, 0, sizeof(fcgs_gfn_req_t)); + gfn->wwn = wwn; + + return (sizeof(struct ct_hdr_s) + sizeof(fcgs_gfn_req_t)); +} diff --git a/drivers/scsi/bfa/fcbuild.h b/drivers/scsi/bfa/fcbuild.h new file mode 100644 index 000000000000..4d248424f7b3 --- /dev/null +++ b/drivers/scsi/bfa/fcbuild.h @@ -0,0 +1,273 @@ +/* + * Copyright (c) 2005-2009 Brocade Communications Systems, Inc. + * All rights reserved + * www.brocade.com + * + * Linux driver for Brocade Fibre Channel Host Bus Adapter. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (GPL) Version 2 as + * published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ +/* + * fcbuild.h - FC link service frame building and parsing routines + */ + +#ifndef __FCBUILD_H__ +#define __FCBUILD_H__ + +#include <bfa_os_inc.h> +#include <protocol/fc.h> +#include <protocol/fcp.h> +#include <protocol/ct.h> +#include <defs/bfa_defs_port.h> +#include <defs/bfa_defs_pport.h> + +/* + * Utility Macros/functions + */ + +#define fcif_sof_set(_ifhdr, _sof) (_ifhdr)->sof = FC_ ## _sof +#define fcif_eof_set(_ifhdr, _eof) (_ifhdr)->eof = FC_ ## _eof + +#define wwn_is_equal(_wwn1, _wwn2) \ + (memcmp(&(_wwn1), &(_wwn2), sizeof(wwn_t)) == 0) + +#define fc_roundup(_l, _s) (((_l) + ((_s) - 1)) & ~((_s) - 1)) + +/* + * Given the fc response length, this routine will return + * the length of the actual payload bytes following the CT header. + * + * Assumes the input response length does not include the crc, eof, etc. + */ +static inline u32 +fc_get_ctresp_pyld_len(u32 resp_len) +{ + return (resp_len - sizeof(struct ct_hdr_s)); +} + +/* + * Convert bfa speed to rpsc speed value. + */ +static inline enum bfa_pport_speed +fc_rpsc_operspeed_to_bfa_speed(enum fc_rpsc_op_speed_s speed) +{ + switch (speed) { + + case RPSC_OP_SPEED_1G: + return BFA_PPORT_SPEED_1GBPS; + + case RPSC_OP_SPEED_2G: + return BFA_PPORT_SPEED_2GBPS; + + case RPSC_OP_SPEED_4G: + return BFA_PPORT_SPEED_4GBPS; + + case RPSC_OP_SPEED_8G: + return BFA_PPORT_SPEED_8GBPS; + + default: + return BFA_PPORT_SPEED_UNKNOWN; + } +} + +/* + * Convert RPSC speed to bfa speed value. + */ +static inline enum fc_rpsc_op_speed_s +fc_bfa_speed_to_rpsc_operspeed(enum bfa_pport_speed op_speed) +{ + switch (op_speed) { + + case BFA_PPORT_SPEED_1GBPS: + return RPSC_OP_SPEED_1G; + + case BFA_PPORT_SPEED_2GBPS: + return RPSC_OP_SPEED_2G; + + case BFA_PPORT_SPEED_4GBPS: + return RPSC_OP_SPEED_4G; + + case BFA_PPORT_SPEED_8GBPS: + return RPSC_OP_SPEED_8G; + + default: + return RPSC_OP_SPEED_NOT_EST; + } +} +enum fc_parse_status { + FC_PARSE_OK = 0, + FC_PARSE_FAILURE = 1, + FC_PARSE_BUSY = 2, + FC_PARSE_LEN_INVAL, + FC_PARSE_ACC_INVAL, + FC_PARSE_PWWN_NOT_EQUAL, + FC_PARSE_NWWN_NOT_EQUAL, + FC_PARSE_RXSZ_INVAL, + FC_PARSE_NOT_FCP, + FC_PARSE_OPAFLAG_INVAL, + FC_PARSE_RPAFLAG_INVAL, + FC_PARSE_OPA_INVAL, + FC_PARSE_RPA_INVAL, + +}; + +struct fc_templates_s { + struct fchs_s fc_els_req; + struct fchs_s fc_bls_req; + struct fc_logi_s plogi; + struct fc_rrq_s rrq; +}; + +void fcbuild_init(void); + +u16 fc_flogi_build(struct fchs_s *fchs, struct fc_logi_s *flogi, + u32 s_id, u16 ox_id, wwn_t port_name, + wwn_t node_name, u16 pdu_size, u8 set_npiv, + u8 set_auth, u16 local_bb_credits); +u16 fc_fdisc_build(struct fchs_s *buf, struct fc_logi_s *flogi, + u32 s_id, u16 ox_id, wwn_t port_name, + wwn_t node_name, u16 pdu_size); +u16 fc_flogi_acc_build(struct fchs_s *fchs, struct fc_logi_s *flogi, + u32 s_id, u16 ox_id, wwn_t port_name, + wwn_t node_name, u16 pdu_size, + u16 local_bb_credits); +u16 fc_plogi_build(struct fchs_s *fchs, void *pld, u32 d_id, + u32 s_id, u16 ox_id, wwn_t port_name, + wwn_t node_name, u16 pdu_size); +enum fc_parse_status fc_plogi_parse(struct fchs_s *fchs); +u16 fc_abts_build(struct fchs_s *buf, u32 d_id, u32 s_id, + u16 ox_id); +enum fc_parse_status fc_abts_rsp_parse(struct fchs_s *buf, int len); +u16 fc_rrq_build(struct fchs_s *buf, struct fc_rrq_s *rrq, u32 d_id, + u32 s_id, u16 ox_id, u16 rrq_oxid); +enum fc_parse_status fc_rrq_rsp_parse(struct fchs_s *buf, int len); +u16 fc_rspnid_build(struct fchs_s *fchs, void *pld, u32 s_id, + u16 ox_id, u8 *name); +u16 fc_rftid_build(struct fchs_s *fchs, void *pld, u32 s_id, + u16 ox_id, enum bfa_port_role role); +u16 fc_rftid_build_sol(struct fchs_s *fchs, void *pyld, u32 s_id, + u16 ox_id, u8 *fc4_bitmap, + u32 bitmap_size); +u16 fc_rffid_build(struct fchs_s *fchs, void *pyld, u32 s_id, + u16 ox_id, u8 fc4_type, u8 fc4_ftrs); +u16 fc_gidpn_build(struct fchs_s *fchs, void *pyld, u32 s_id, + u16 ox_id, wwn_t port_name); +u16 fc_gpnid_build(struct fchs_s *fchs, void *pld, u32 s_id, + u16 ox_id, u32 port_id); +u16 fc_scr_build(struct fchs_s *fchs, struct fc_scr_s *scr, + u8 set_br_reg, u32 s_id, u16 ox_id); +u16 fc_plogi_acc_build(struct fchs_s *fchs, void *pld, u32 d_id, + u32 s_id, u16 ox_id, + wwn_t port_name, wwn_t node_name, u16 pdu_size); + +u16 fc_adisc_build(struct fchs_s *fchs, struct fc_adisc_s *adisc, + u32 d_id, u32 s_id, u16 ox_id, + wwn_t port_name, wwn_t node_name); +enum fc_parse_status fc_adisc_parse(struct fchs_s *fchs, void *pld, + u32 host_dap, + wwn_t node_name, wwn_t port_name); +enum fc_parse_status fc_adisc_rsp_parse(struct fc_adisc_s *adisc, int len, + wwn_t port_name, wwn_t node_name); +u16 fc_adisc_acc_build(struct fchs_s *fchs, struct fc_adisc_s *adisc, + u32 d_id, u32 s_id, u16 ox_id, + wwn_t port_name, wwn_t node_name); +u16 fc_ls_rjt_build(struct fchs_s *fchs, struct fc_ls_rjt_s *ls_rjt, + u32 d_id, u32 s_id, u16 ox_id, + u8 reason_code, u8 reason_code_expl); +u16 fc_ls_acc_build(struct fchs_s *fchs, struct fc_els_cmd_s *els_cmd, + u32 d_id, u32 s_id, u16 ox_id); +u16 fc_prli_build(struct fchs_s *fchs, void *pld, u32 d_id, + u32 s_id, u16 ox_id); +enum fc_parse_status fc_prli_rsp_parse(struct fc_prli_s *prli, int len); + +u16 fc_prli_acc_build(struct fchs_s *fchs, void *pld, u32 d_id, + u32 s_id, u16 ox_id, + enum bfa_port_role role); +u16 fc_rnid_build(struct fchs_s *fchs, struct fc_rnid_cmd_s *rnid, + u32 d_id, u32 s_id, u16 ox_id, + u32 data_format); +u16 fc_rnid_acc_build(struct fchs_s *fchs, struct fc_rnid_acc_s *rnid_acc, + u32 d_id, u32 s_id, u16 ox_id, + u32 data_format, + struct fc_rnid_common_id_data_s *common_id_data, + struct fc_rnid_general_topology_data_s * + gen_topo_data); +u16 fc_rpsc2_build(struct fchs_s *fchs, struct fc_rpsc2_cmd_s *rps2c, + u32 d_id, u32 s_id, + u32 *pid_list, u16 npids); +u16 fc_rpsc_build(struct fchs_s *fchs, struct fc_rpsc_cmd_s *rpsc, + u32 d_id, u32 s_id, u16 ox_id); +u16 fc_rpsc_acc_build(struct fchs_s *fchs, struct fc_rpsc_acc_s *rpsc_acc, + u32 d_id, u32 s_id, u16 ox_id, + struct fc_rpsc_speed_info_s *oper_speed); +u16 fc_gid_ft_build(struct fchs_s *fchs, void *pld, u32 s_id, + u8 fc4_type); +u16 fc_rpnid_build(struct fchs_s *fchs, void *pyld, u32 s_id, + u32 port_id, wwn_t port_name); +u16 fc_rnnid_build(struct fchs_s *fchs, void *pyld, u32 s_id, + u32 port_id, wwn_t node_name); +u16 fc_rcsid_build(struct fchs_s *fchs, void *pyld, u32 s_id, + u32 port_id, u32 cos); +u16 fc_rptid_build(struct fchs_s *fchs, void *pyld, u32 s_id, + u32 port_id, u8 port_type); +u16 fc_ganxt_build(struct fchs_s *fchs, void *pyld, u32 s_id, + u32 port_id); +u16 fc_logo_build(struct fchs_s *fchs, struct fc_logo_s *logo, + u32 d_id, u32 s_id, u16 ox_id, + wwn_t port_name); +u16 fc_logo_acc_build(struct fchs_s *fchs, void *pld, u32 d_id, + u32 s_id, u16 ox_id); +u16 fc_fdmi_reqhdr_build(struct fchs_s *fchs, void *pyld, u32 s_id, + u16 cmd_code); +u16 fc_gmal_req_build(struct fchs_s *fchs, void *pyld, u32 s_id, + wwn_t wwn); +u16 fc_gfn_req_build(struct fchs_s *fchs, void *pyld, u32 s_id, + wwn_t wwn); +void fc_get_fc4type_bitmask(u8 fc4_type, u8 *bit_mask); +void fc_els_req_build(struct fchs_s *fchs, u32 d_id, u32 s_id, + u16 ox_id); +enum fc_parse_status fc_els_rsp_parse(struct fchs_s *fchs, int len); +enum fc_parse_status fc_plogi_rsp_parse(struct fchs_s *fchs, int len, + wwn_t port_name); +enum fc_parse_status fc_prli_parse(struct fc_prli_s *prli); +enum fc_parse_status fc_pdisc_parse(struct fchs_s *fchs, wwn_t node_name, + wwn_t port_name); +u16 fc_ba_acc_build(struct fchs_s *fchs, struct fc_ba_acc_s *ba_acc, + u32 d_id, u32 s_id, u16 ox_id, + u16 rx_id); +int fc_logout_params_pages(struct fchs_s *fc_frame, u8 els_code); +u16 fc_tprlo_acc_build(struct fchs_s *fchs, + struct fc_tprlo_acc_s *tprlo_acc, + u32 d_id, u32 s_id, u16 ox_id, + int num_pages); +u16 fc_prlo_acc_build(struct fchs_s *fchs, struct fc_prlo_acc_s *prlo_acc, + u32 d_id, u32 s_id, u16 ox_id, + int num_pages); +u16 fc_logo_rsp_parse(struct fchs_s *fchs, int len); +u16 fc_pdisc_build(struct fchs_s *fchs, u32 d_id, u32 s_id, + u16 ox_id, wwn_t port_name, wwn_t node_name, + u16 pdu_size); +u16 fc_pdisc_rsp_parse(struct fchs_s *fchs, int len, wwn_t port_name); +u16 fc_prlo_build(struct fchs_s *fchs, u32 d_id, u32 s_id, + u16 ox_id, int num_pages); +u16 fc_prlo_rsp_parse(struct fchs_s *fchs, int len); +u16 fc_tprlo_build(struct fchs_s *fchs, u32 d_id, u32 s_id, + u16 ox_id, int num_pages, + enum fc_tprlo_type tprlo_type, u32 tpr_id); +u16 fc_tprlo_rsp_parse(struct fchs_s *fchs, int len); +u16 fc_ba_rjt_build(struct fchs_s *fchs, u32 d_id, u32 s_id, + u16 ox_id, u32 reason_code, + u32 reason_expl); +u16 fc_gnnid_build(struct fchs_s *fchs, void *pyld, u32 s_id, + u16 ox_id, u32 port_id); +u16 fc_ct_rsp_parse(struct ct_hdr_s *cthdr); +u16 fc_rscn_build(struct fchs_s *fchs, struct fc_rscn_pl_s *rscn, + u32 s_id, u16 ox_id); +#endif diff --git a/drivers/scsi/bfa/fcpim.c b/drivers/scsi/bfa/fcpim.c new file mode 100644 index 000000000000..8ce5d8934677 --- /dev/null +++ b/drivers/scsi/bfa/fcpim.c @@ -0,0 +1,844 @@ +/* + * Copyright (c) 2005-2009 Brocade Communications Systems, Inc. + * All rights reserved + * www.brocade.com + * + * Linux driver for Brocade Fibre Channel Host Bus Adapter. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (GPL) Version 2 as + * published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +/** + * fcpim.c - FCP initiator mode i-t nexus state machine + */ + +#include <bfa.h> +#include <bfa_svc.h> +#include "fcs_fcpim.h" +#include "fcs_rport.h" +#include "fcs_lport.h" +#include "fcs_trcmod.h" +#include "fcs_fcxp.h" +#include "fcs.h" +#include <fcs/bfa_fcs_fcpim.h> +#include <fcb/bfa_fcb_fcpim.h> +#include <aen/bfa_aen_itnim.h> + +BFA_TRC_FILE(FCS, FCPIM); + +/* + * forward declarations + */ +static void bfa_fcs_itnim_timeout(void *arg); +static void bfa_fcs_itnim_free(struct bfa_fcs_itnim_s *itnim); +static void bfa_fcs_itnim_send_prli(void *itnim_cbarg, + struct bfa_fcxp_s *fcxp_alloced); +static void bfa_fcs_itnim_prli_response(void *fcsarg, + struct bfa_fcxp_s *fcxp, + void *cbarg, + bfa_status_t req_status, + u32 rsp_len, + u32 resid_len, + struct fchs_s *rsp_fchs); +static void bfa_fcs_itnim_aen_post(struct bfa_fcs_itnim_s *itnim, + enum bfa_itnim_aen_event event); + +/** + * fcs_itnim_sm FCS itnim state machine events + */ + +enum bfa_fcs_itnim_event { + BFA_FCS_ITNIM_SM_ONLINE = 1, /* rport online event */ + BFA_FCS_ITNIM_SM_OFFLINE = 2, /* rport offline */ + BFA_FCS_ITNIM_SM_FRMSENT = 3, /* prli frame is sent */ + BFA_FCS_ITNIM_SM_RSP_OK = 4, /* good response */ + BFA_FCS_ITNIM_SM_RSP_ERROR = 5, /* error response */ + BFA_FCS_ITNIM_SM_TIMEOUT = 6, /* delay timeout */ + BFA_FCS_ITNIM_SM_HCB_OFFLINE = 7, /* BFA online callback */ + BFA_FCS_ITNIM_SM_HCB_ONLINE = 8, /* BFA offline callback */ + BFA_FCS_ITNIM_SM_INITIATOR = 9, /* rport is initiator */ + BFA_FCS_ITNIM_SM_DELETE = 10, /* delete event from rport */ + BFA_FCS_ITNIM_SM_PRLO = 11, /* delete event from rport */ +}; + +static void bfa_fcs_itnim_sm_offline(struct bfa_fcs_itnim_s *itnim, + enum bfa_fcs_itnim_event event); +static void bfa_fcs_itnim_sm_prli_send(struct bfa_fcs_itnim_s *itnim, + enum bfa_fcs_itnim_event event); +static void bfa_fcs_itnim_sm_prli(struct bfa_fcs_itnim_s *itnim, + enum bfa_fcs_itnim_event event); +static void bfa_fcs_itnim_sm_prli_retry(struct bfa_fcs_itnim_s *itnim, + enum bfa_fcs_itnim_event event); +static void bfa_fcs_itnim_sm_hcb_online(struct bfa_fcs_itnim_s *itnim, + enum bfa_fcs_itnim_event event); +static void bfa_fcs_itnim_sm_online(struct bfa_fcs_itnim_s *itnim, + enum bfa_fcs_itnim_event event); +static void bfa_fcs_itnim_sm_hcb_offline(struct bfa_fcs_itnim_s *itnim, + enum bfa_fcs_itnim_event event); +static void bfa_fcs_itnim_sm_initiator(struct bfa_fcs_itnim_s *itnim, + enum bfa_fcs_itnim_event event); + +static struct bfa_sm_table_s itnim_sm_table[] = { + {BFA_SM(bfa_fcs_itnim_sm_offline), BFA_ITNIM_OFFLINE}, + {BFA_SM(bfa_fcs_itnim_sm_prli_send), BFA_ITNIM_PRLI_SEND}, + {BFA_SM(bfa_fcs_itnim_sm_prli), BFA_ITNIM_PRLI_SENT}, + {BFA_SM(bfa_fcs_itnim_sm_prli_retry), BFA_ITNIM_PRLI_RETRY}, + {BFA_SM(bfa_fcs_itnim_sm_hcb_online), BFA_ITNIM_HCB_ONLINE}, + {BFA_SM(bfa_fcs_itnim_sm_online), BFA_ITNIM_ONLINE}, + {BFA_SM(bfa_fcs_itnim_sm_hcb_offline), BFA_ITNIM_HCB_OFFLINE}, + {BFA_SM(bfa_fcs_itnim_sm_initiator), BFA_ITNIM_INITIATIOR}, +}; + +/** + * fcs_itnim_sm FCS itnim state machine + */ + +static void +bfa_fcs_itnim_sm_offline(struct bfa_fcs_itnim_s *itnim, + enum bfa_fcs_itnim_event event) +{ + bfa_trc(itnim->fcs, itnim->rport->pwwn); + bfa_trc(itnim->fcs, event); + + switch (event) { + case BFA_FCS_ITNIM_SM_ONLINE: + bfa_sm_set_state(itnim, bfa_fcs_itnim_sm_prli_send); + bfa_fcs_itnim_send_prli(itnim, NULL); + break; + + case BFA_FCS_ITNIM_SM_OFFLINE: + bfa_fcs_rport_itnim_ack(itnim->rport); + break; + + case BFA_FCS_ITNIM_SM_INITIATOR: + bfa_sm_set_state(itnim, bfa_fcs_itnim_sm_initiator); + break; + + case BFA_FCS_ITNIM_SM_DELETE: + bfa_fcs_itnim_free(itnim); + break; + + default: + bfa_assert(0); + } + +} + +static void +bfa_fcs_itnim_sm_prli_send(struct bfa_fcs_itnim_s *itnim, + enum bfa_fcs_itnim_event event) +{ + bfa_trc(itnim->fcs, itnim->rport->pwwn); + bfa_trc(itnim->fcs, event); + + switch (event) { + case BFA_FCS_ITNIM_SM_FRMSENT: + bfa_sm_set_state(itnim, bfa_fcs_itnim_sm_prli); + break; + + case BFA_FCS_ITNIM_SM_INITIATOR: + bfa_sm_set_state(itnim, bfa_fcs_itnim_sm_initiator); + bfa_fcxp_walloc_cancel(itnim->fcs->bfa, &itnim->fcxp_wqe); + break; + + case BFA_FCS_ITNIM_SM_OFFLINE: + bfa_sm_set_state(itnim, bfa_fcs_itnim_sm_offline); + bfa_fcxp_walloc_cancel(itnim->fcs->bfa, &itnim->fcxp_wqe); + bfa_fcs_rport_itnim_ack(itnim->rport); + break; + + case BFA_FCS_ITNIM_SM_DELETE: + bfa_sm_set_state(itnim, bfa_fcs_itnim_sm_offline); + bfa_fcxp_walloc_cancel(itnim->fcs->bfa, &itnim->fcxp_wqe); + bfa_fcs_itnim_free(itnim); + break; + + default: + bfa_assert(0); + } +} + +static void +bfa_fcs_itnim_sm_prli(struct bfa_fcs_itnim_s *itnim, + enum bfa_fcs_itnim_event event) +{ + bfa_trc(itnim->fcs, itnim->rport->pwwn); + bfa_trc(itnim->fcs, event); + + switch (event) { + case BFA_FCS_ITNIM_SM_RSP_OK: + bfa_sm_set_state(itnim, bfa_fcs_itnim_sm_hcb_online); + bfa_itnim_online(itnim->bfa_itnim, itnim->seq_rec); + break; + + case BFA_FCS_ITNIM_SM_RSP_ERROR: + bfa_sm_set_state(itnim, bfa_fcs_itnim_sm_prli_retry); + bfa_timer_start(itnim->fcs->bfa, &itnim->timer, + bfa_fcs_itnim_timeout, itnim, + BFA_FCS_RETRY_TIMEOUT); + break; + + case BFA_FCS_ITNIM_SM_OFFLINE: + bfa_sm_set_state(itnim, bfa_fcs_itnim_sm_offline); + bfa_fcxp_discard(itnim->fcxp); + bfa_fcs_rport_itnim_ack(itnim->rport); + break; + + case BFA_FCS_ITNIM_SM_INITIATOR: + bfa_sm_set_state(itnim, bfa_fcs_itnim_sm_initiator); + /* + * dont discard fcxp. accept will reach same state + */ + break; + + case BFA_FCS_ITNIM_SM_DELETE: + bfa_sm_set_state(itnim, bfa_fcs_itnim_sm_offline); + bfa_fcxp_discard(itnim->fcxp); + bfa_fcs_itnim_free(itnim); + break; + + default: + bfa_assert(0); + } +} + +static void +bfa_fcs_itnim_sm_prli_retry(struct bfa_fcs_itnim_s *itnim, + enum bfa_fcs_itnim_event event) +{ + bfa_trc(itnim->fcs, itnim->rport->pwwn); + bfa_trc(itnim->fcs, event); + + switch (event) { + case BFA_FCS_ITNIM_SM_TIMEOUT: + bfa_sm_set_state(itnim, bfa_fcs_itnim_sm_prli_send); + bfa_fcs_itnim_send_prli(itnim, NULL); + break; + + case BFA_FCS_ITNIM_SM_OFFLINE: + bfa_sm_set_state(itnim, bfa_fcs_itnim_sm_offline); + bfa_timer_stop(&itnim->timer); + bfa_fcs_rport_itnim_ack(itnim->rport); + break; + + case BFA_FCS_ITNIM_SM_INITIATOR: + bfa_sm_set_state(itnim, bfa_fcs_itnim_sm_initiator); + bfa_timer_stop(&itnim->timer); + break; + + case BFA_FCS_ITNIM_SM_DELETE: + bfa_sm_set_state(itnim, bfa_fcs_itnim_sm_offline); + bfa_timer_stop(&itnim->timer); + bfa_fcs_itnim_free(itnim); + break; + + default: + bfa_assert(0); + } +} + +static void +bfa_fcs_itnim_sm_hcb_online(struct bfa_fcs_itnim_s *itnim, + enum bfa_fcs_itnim_event event) +{ + bfa_trc(itnim->fcs, itnim->rport->pwwn); + bfa_trc(itnim->fcs, event); + + switch (event) { + case BFA_FCS_ITNIM_SM_HCB_ONLINE: + bfa_sm_set_state(itnim, bfa_fcs_itnim_sm_online); + bfa_fcb_itnim_online(itnim->itnim_drv); + bfa_fcs_itnim_aen_post(itnim, BFA_ITNIM_AEN_ONLINE); + break; + + case BFA_FCS_ITNIM_SM_OFFLINE: + bfa_sm_set_state(itnim, bfa_fcs_itnim_sm_offline); + bfa_itnim_offline(itnim->bfa_itnim); + bfa_fcs_rport_itnim_ack(itnim->rport); + break; + + case BFA_FCS_ITNIM_SM_DELETE: + bfa_sm_set_state(itnim, bfa_fcs_itnim_sm_offline); + bfa_fcs_itnim_free(itnim); + break; + + default: + bfa_assert(0); + } +} + +static void +bfa_fcs_itnim_sm_online(struct bfa_fcs_itnim_s *itnim, + enum bfa_fcs_itnim_event event) +{ + bfa_trc(itnim->fcs, itnim->rport->pwwn); + bfa_trc(itnim->fcs, event); + + switch (event) { + case BFA_FCS_ITNIM_SM_OFFLINE: + bfa_sm_set_state(itnim, bfa_fcs_itnim_sm_hcb_offline); + bfa_fcb_itnim_offline(itnim->itnim_drv); + bfa_itnim_offline(itnim->bfa_itnim); + if (bfa_fcs_port_is_online(itnim->rport->port) == BFA_TRUE) { + bfa_fcs_itnim_aen_post(itnim, BFA_ITNIM_AEN_DISCONNECT); + } else { + bfa_fcs_itnim_aen_post(itnim, BFA_ITNIM_AEN_OFFLINE); + } + break; + + case BFA_FCS_ITNIM_SM_DELETE: + bfa_sm_set_state(itnim, bfa_fcs_itnim_sm_offline); + bfa_fcs_itnim_free(itnim); + break; + + default: + bfa_assert(0); + } +} + +static void +bfa_fcs_itnim_sm_hcb_offline(struct bfa_fcs_itnim_s *itnim, + enum bfa_fcs_itnim_event event) +{ + bfa_trc(itnim->fcs, itnim->rport->pwwn); + bfa_trc(itnim->fcs, event); + + switch (event) { + case BFA_FCS_ITNIM_SM_HCB_OFFLINE: + bfa_sm_set_state(itnim, bfa_fcs_itnim_sm_offline); + bfa_fcs_rport_itnim_ack(itnim->rport); + break; + + case BFA_FCS_ITNIM_SM_DELETE: + bfa_sm_set_state(itnim, bfa_fcs_itnim_sm_offline); + bfa_fcs_itnim_free(itnim); + break; + + default: + bfa_assert(0); + } +} + +/* + * This state is set when a discovered rport is also in intiator mode. + * This ITN is marked as no_op and is not active and will not be truned into + * online state. + */ +static void +bfa_fcs_itnim_sm_initiator(struct bfa_fcs_itnim_s *itnim, + enum bfa_fcs_itnim_event event) +{ + bfa_trc(itnim->fcs, itnim->rport->pwwn); + bfa_trc(itnim->fcs, event); + + switch (event) { + case BFA_FCS_ITNIM_SM_OFFLINE: + bfa_sm_set_state(itnim, bfa_fcs_itnim_sm_offline); + bfa_fcs_rport_itnim_ack(itnim->rport); + break; + + case BFA_FCS_ITNIM_SM_RSP_ERROR: + case BFA_FCS_ITNIM_SM_ONLINE: + case BFA_FCS_ITNIM_SM_INITIATOR: + break; + + case BFA_FCS_ITNIM_SM_DELETE: + bfa_sm_set_state(itnim, bfa_fcs_itnim_sm_offline); + bfa_fcs_itnim_free(itnim); + break; + + default: + bfa_assert(0); + } +} + + + +/** + * itnim_private FCS ITNIM private interfaces + */ + +static void +bfa_fcs_itnim_aen_post(struct bfa_fcs_itnim_s *itnim, + enum bfa_itnim_aen_event event) +{ + struct bfa_fcs_rport_s *rport = itnim->rport; + union bfa_aen_data_u aen_data; + struct bfa_log_mod_s *logmod = rport->fcs->logm; + wwn_t lpwwn = bfa_fcs_port_get_pwwn(rport->port); + wwn_t rpwwn = rport->pwwn; + char lpwwn_ptr[BFA_STRING_32]; + char rpwwn_ptr[BFA_STRING_32]; + + /* + * Don't post events for well known addresses + */ + if (BFA_FCS_PID_IS_WKA(rport->pid)) + return; + + wwn2str(lpwwn_ptr, lpwwn); + wwn2str(rpwwn_ptr, rpwwn); + + switch (event) { + case BFA_ITNIM_AEN_ONLINE: + bfa_log(logmod, BFA_AEN_ITNIM_ONLINE, rpwwn_ptr, lpwwn_ptr); + break; + case BFA_ITNIM_AEN_OFFLINE: + bfa_log(logmod, BFA_AEN_ITNIM_OFFLINE, rpwwn_ptr, lpwwn_ptr); + break; + case BFA_ITNIM_AEN_DISCONNECT: + bfa_log(logmod, BFA_AEN_ITNIM_DISCONNECT, rpwwn_ptr, lpwwn_ptr); + break; + default: + break; + } + + aen_data.itnim.vf_id = rport->port->fabric->vf_id; + aen_data.itnim.ppwwn = + bfa_fcs_port_get_pwwn(bfa_fcs_get_base_port(itnim->fcs)); + aen_data.itnim.lpwwn = lpwwn; + aen_data.itnim.rpwwn = rpwwn; +} + +static void +bfa_fcs_itnim_send_prli(void *itnim_cbarg, struct bfa_fcxp_s *fcxp_alloced) +{ + struct bfa_fcs_itnim_s *itnim = itnim_cbarg; + struct bfa_fcs_rport_s *rport = itnim->rport; + struct bfa_fcs_port_s *port = rport->port; + struct fchs_s fchs; + struct bfa_fcxp_s *fcxp; + int len; + + bfa_trc(itnim->fcs, itnim->rport->pwwn); + + fcxp = fcxp_alloced ? fcxp_alloced : bfa_fcs_fcxp_alloc(port->fcs); + if (!fcxp) { + itnim->stats.fcxp_alloc_wait++; + bfa_fcxp_alloc_wait(port->fcs->bfa, &itnim->fcxp_wqe, + bfa_fcs_itnim_send_prli, itnim); + return; + } + itnim->fcxp = fcxp; + + len = fc_prli_build(&fchs, bfa_fcxp_get_reqbuf(fcxp), itnim->rport->pid, + bfa_fcs_port_get_fcid(port), 0); + + bfa_fcxp_send(fcxp, rport->bfa_rport, port->fabric->vf_id, port->lp_tag, + BFA_FALSE, FC_CLASS_3, len, &fchs, + bfa_fcs_itnim_prli_response, (void *)itnim, FC_MAX_PDUSZ, + FC_RA_TOV); + + itnim->stats.prli_sent++; + bfa_sm_send_event(itnim, BFA_FCS_ITNIM_SM_FRMSENT); +} + +static void +bfa_fcs_itnim_prli_response(void *fcsarg, struct bfa_fcxp_s *fcxp, void *cbarg, + bfa_status_t req_status, u32 rsp_len, + u32 resid_len, struct fchs_s *rsp_fchs) +{ + struct bfa_fcs_itnim_s *itnim = (struct bfa_fcs_itnim_s *)cbarg; + struct fc_els_cmd_s *els_cmd; + struct fc_prli_s *prli_resp; + struct fc_ls_rjt_s *ls_rjt; + struct fc_prli_params_s *sparams; + + bfa_trc(itnim->fcs, req_status); + + /* + * Sanity Checks + */ + if (req_status != BFA_STATUS_OK) { + itnim->stats.prli_rsp_err++; + bfa_sm_send_event(itnim, BFA_FCS_ITNIM_SM_RSP_ERROR); + return; + } + + els_cmd = (struct fc_els_cmd_s *) BFA_FCXP_RSP_PLD(fcxp); + + if (els_cmd->els_code == FC_ELS_ACC) { + prli_resp = (struct fc_prli_s *) els_cmd; + + if (fc_prli_rsp_parse(prli_resp, rsp_len) != FC_PARSE_OK) { + bfa_trc(itnim->fcs, rsp_len); + /* + * Check if this r-port is also in Initiator mode. + * If so, we need to set this ITN as a no-op. + */ + if (prli_resp->parampage.servparams.initiator) { + bfa_trc(itnim->fcs, prli_resp->parampage.type); + itnim->rport->scsi_function = + BFA_RPORT_INITIATOR; + itnim->stats.prli_rsp_acc++; + bfa_sm_send_event(itnim, + BFA_FCS_ITNIM_SM_INITIATOR); + return; + } + + itnim->stats.prli_rsp_parse_err++; + return; + } + itnim->rport->scsi_function = BFA_RPORT_TARGET; + + sparams = &prli_resp->parampage.servparams; + itnim->seq_rec = sparams->retry; + itnim->rec_support = sparams->rec_support; + itnim->task_retry_id = sparams->task_retry_id; + itnim->conf_comp = sparams->confirm; + + itnim->stats.prli_rsp_acc++; + bfa_sm_send_event(itnim, BFA_FCS_ITNIM_SM_RSP_OK); + } else { + ls_rjt = (struct fc_ls_rjt_s *) BFA_FCXP_RSP_PLD(fcxp); + + bfa_trc(itnim->fcs, ls_rjt->reason_code); + bfa_trc(itnim->fcs, ls_rjt->reason_code_expl); + + itnim->stats.prli_rsp_rjt++; + bfa_sm_send_event(itnim, BFA_FCS_ITNIM_SM_RSP_ERROR); + } +} + +static void +bfa_fcs_itnim_timeout(void *arg) +{ + struct bfa_fcs_itnim_s *itnim = (struct bfa_fcs_itnim_s *)arg; + + itnim->stats.timeout++; + bfa_sm_send_event(itnim, BFA_FCS_ITNIM_SM_TIMEOUT); +} + +static void +bfa_fcs_itnim_free(struct bfa_fcs_itnim_s *itnim) +{ + bfa_itnim_delete(itnim->bfa_itnim); + bfa_fcb_itnim_free(itnim->fcs->bfad, itnim->itnim_drv); +} + + + +/** + * itnim_public FCS ITNIM public interfaces + */ + +/** + * Called by rport when a new rport is created. + * + * @param[in] rport - remote port. + */ +struct bfa_fcs_itnim_s * +bfa_fcs_itnim_create(struct bfa_fcs_rport_s *rport) +{ + struct bfa_fcs_port_s *port = rport->port; + struct bfa_fcs_itnim_s *itnim; + struct bfad_itnim_s *itnim_drv; + struct bfa_itnim_s *bfa_itnim; + + /* + * call bfad to allocate the itnim + */ + bfa_fcb_itnim_alloc(port->fcs->bfad, &itnim, &itnim_drv); + if (itnim == NULL) { + bfa_trc(port->fcs, rport->pwwn); + return NULL; + } + + /* + * Initialize itnim + */ + itnim->rport = rport; + itnim->fcs = rport->fcs; + itnim->itnim_drv = itnim_drv; + + /* + * call BFA to create the itnim + */ + bfa_itnim = bfa_itnim_create(port->fcs->bfa, rport->bfa_rport, itnim); + + if (bfa_itnim == NULL) { + bfa_trc(port->fcs, rport->pwwn); + bfa_fcb_itnim_free(port->fcs->bfad, itnim_drv); + bfa_assert(0); + return NULL; + } + + itnim->bfa_itnim = bfa_itnim; + itnim->seq_rec = BFA_FALSE; + itnim->rec_support = BFA_FALSE; + itnim->conf_comp = BFA_FALSE; + itnim->task_retry_id = BFA_FALSE; + + /* + * Set State machine + */ + bfa_sm_set_state(itnim, bfa_fcs_itnim_sm_offline); + + return itnim; +} + +/** + * Called by rport to delete the instance of FCPIM. + * + * @param[in] rport - remote port. + */ +void +bfa_fcs_itnim_delete(struct bfa_fcs_itnim_s *itnim) +{ + bfa_trc(itnim->fcs, itnim->rport->pid); + bfa_sm_send_event(itnim, BFA_FCS_ITNIM_SM_DELETE); +} + +/** + * Notification from rport that PLOGI is complete to initiate FC-4 session. + */ +void +bfa_fcs_itnim_rport_online(struct bfa_fcs_itnim_s *itnim) +{ + itnim->stats.onlines++; + + if (!BFA_FCS_PID_IS_WKA(itnim->rport->pid)) { + bfa_sm_send_event(itnim, BFA_FCS_ITNIM_SM_ONLINE); + } else { + /* + * For well known addresses, we set the itnim to initiator + * state + */ + itnim->stats.initiator++; + bfa_sm_send_event(itnim, BFA_FCS_ITNIM_SM_INITIATOR); + } +} + +/** + * Called by rport to handle a remote device offline. + */ +void +bfa_fcs_itnim_rport_offline(struct bfa_fcs_itnim_s *itnim) +{ + itnim->stats.offlines++; + bfa_sm_send_event(itnim, BFA_FCS_ITNIM_SM_OFFLINE); +} + +/** + * Called by rport when remote port is known to be an initiator from + * PRLI received. + */ +void +bfa_fcs_itnim_is_initiator(struct bfa_fcs_itnim_s *itnim) +{ + bfa_trc(itnim->fcs, itnim->rport->pid); + itnim->stats.initiator++; + bfa_sm_send_event(itnim, BFA_FCS_ITNIM_SM_INITIATOR); +} + +/** + * Called by rport to check if the itnim is online. + */ +bfa_status_t +bfa_fcs_itnim_get_online_state(struct bfa_fcs_itnim_s *itnim) +{ + bfa_trc(itnim->fcs, itnim->rport->pid); + switch (bfa_sm_to_state(itnim_sm_table, itnim->sm)) { + case BFA_ITNIM_ONLINE: + case BFA_ITNIM_INITIATIOR: + return BFA_STATUS_OK; + + default: + return BFA_STATUS_NO_FCPIM_NEXUS; + + } +} + +/** + * BFA completion callback for bfa_itnim_online(). + */ +void +bfa_cb_itnim_online(void *cbarg) +{ + struct bfa_fcs_itnim_s *itnim = (struct bfa_fcs_itnim_s *)cbarg; + + bfa_trc(itnim->fcs, itnim->rport->pwwn); + bfa_sm_send_event(itnim, BFA_FCS_ITNIM_SM_HCB_ONLINE); +} + +/** + * BFA completion callback for bfa_itnim_offline(). + */ +void +bfa_cb_itnim_offline(void *cb_arg) +{ + struct bfa_fcs_itnim_s *itnim = (struct bfa_fcs_itnim_s *)cb_arg; + + bfa_trc(itnim->fcs, itnim->rport->pwwn); + bfa_sm_send_event(itnim, BFA_FCS_ITNIM_SM_HCB_OFFLINE); +} + +/** + * Mark the beginning of PATH TOV handling. IO completion callbacks + * are still pending. + */ +void +bfa_cb_itnim_tov_begin(void *cb_arg) +{ + struct bfa_fcs_itnim_s *itnim = (struct bfa_fcs_itnim_s *)cb_arg; + + bfa_trc(itnim->fcs, itnim->rport->pwwn); + bfa_fcb_itnim_tov_begin(itnim->itnim_drv); +} + +/** + * Mark the end of PATH TOV handling. All pending IOs are already cleaned up. + */ +void +bfa_cb_itnim_tov(void *cb_arg) +{ + struct bfa_fcs_itnim_s *itnim = (struct bfa_fcs_itnim_s *)cb_arg; + + bfa_trc(itnim->fcs, itnim->rport->pwwn); + bfa_fcb_itnim_tov(itnim->itnim_drv); +} + +/** + * BFA notification to FCS/driver for second level error recovery. + * + * Atleast one I/O request has timedout and target is unresponsive to + * repeated abort requests. Second level error recovery should be initiated + * by starting implicit logout and recovery procedures. + */ +void +bfa_cb_itnim_sler(void *cb_arg) +{ + struct bfa_fcs_itnim_s *itnim = (struct bfa_fcs_itnim_s *)cb_arg; + + itnim->stats.sler++; + bfa_trc(itnim->fcs, itnim->rport->pwwn); + bfa_fcs_rport_logo_imp(itnim->rport); +} + +struct bfa_fcs_itnim_s * +bfa_fcs_itnim_lookup(struct bfa_fcs_port_s *port, wwn_t rpwwn) +{ + struct bfa_fcs_rport_s *rport; + rport = bfa_fcs_rport_lookup(port, rpwwn); + + if (!rport) + return NULL; + + bfa_assert(rport->itnim != NULL); + return (rport->itnim); +} + +bfa_status_t +bfa_fcs_itnim_attr_get(struct bfa_fcs_port_s *port, wwn_t rpwwn, + struct bfa_itnim_attr_s *attr) +{ + struct bfa_fcs_itnim_s *itnim = NULL; + + itnim = bfa_fcs_itnim_lookup(port, rpwwn); + + if (itnim == NULL) + return BFA_STATUS_NO_FCPIM_NEXUS; + + attr->state = bfa_sm_to_state(itnim_sm_table, itnim->sm); + attr->retry = itnim->seq_rec; + attr->rec_support = itnim->rec_support; + attr->conf_comp = itnim->conf_comp; + attr->task_retry_id = itnim->task_retry_id; + + return BFA_STATUS_OK; +} + +bfa_status_t +bfa_fcs_itnim_stats_get(struct bfa_fcs_port_s *port, wwn_t rpwwn, + struct bfa_itnim_stats_s *stats) +{ + struct bfa_fcs_itnim_s *itnim = NULL; + + bfa_assert(port != NULL); + + itnim = bfa_fcs_itnim_lookup(port, rpwwn); + + if (itnim == NULL) + return BFA_STATUS_NO_FCPIM_NEXUS; + + bfa_os_memcpy(stats, &itnim->stats, sizeof(struct bfa_itnim_stats_s)); + + return BFA_STATUS_OK; +} + +bfa_status_t +bfa_fcs_itnim_stats_clear(struct bfa_fcs_port_s *port, wwn_t rpwwn) +{ + struct bfa_fcs_itnim_s *itnim = NULL; + + bfa_assert(port != NULL); + + itnim = bfa_fcs_itnim_lookup(port, rpwwn); + + if (itnim == NULL) + return BFA_STATUS_NO_FCPIM_NEXUS; + + bfa_os_memset(&itnim->stats, 0, sizeof(struct bfa_itnim_stats_s)); + return BFA_STATUS_OK; +} + +void +bfa_fcs_fcpim_uf_recv(struct bfa_fcs_itnim_s *itnim, struct fchs_s *fchs, + u16 len) +{ + struct fc_els_cmd_s *els_cmd; + + bfa_trc(itnim->fcs, fchs->type); + + if (fchs->type != FC_TYPE_ELS) + return; + + els_cmd = (struct fc_els_cmd_s *) (fchs + 1); + + bfa_trc(itnim->fcs, els_cmd->els_code); + + switch (els_cmd->els_code) { + case FC_ELS_PRLO: + /* bfa_sm_send_event(itnim, BFA_FCS_ITNIM_SM_PRLO); */ + break; + + default: + bfa_assert(0); + } +} + +void +bfa_fcs_itnim_pause(struct bfa_fcs_itnim_s *itnim) +{ +} + +void +bfa_fcs_itnim_resume(struct bfa_fcs_itnim_s *itnim) +{ +} + +/** + * Module initialization + */ +void +bfa_fcs_fcpim_modinit(struct bfa_fcs_s *fcs) +{ +} + +/** + * Module cleanup + */ +void +bfa_fcs_fcpim_modexit(struct bfa_fcs_s *fcs) +{ + bfa_fcs_modexit_comp(fcs); +} + + diff --git a/drivers/scsi/bfa/fcptm.c b/drivers/scsi/bfa/fcptm.c new file mode 100644 index 000000000000..8c8b08c72e7a --- /dev/null +++ b/drivers/scsi/bfa/fcptm.c @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2005-2009 Brocade Communications Systems, Inc. + * All rights reserved + * www.brocade.com + * + * Linux driver for Brocade Fibre Channel Host Bus Adapter. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (GPL) Version 2 as + * published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +/** + * This file contains dummy FCPTM routines to aid in Initiator Mode only + * compilation of OS driver. + * + */ + +#include "bfa_os_inc.h" +#include "fcs_rport.h" +#include "fcs_fcptm.h" +#include "fcs/bfa_fcs_rport.h" + +struct bfa_fcs_tin_s * +bfa_fcs_tin_create(struct bfa_fcs_rport_s *rport) +{ + return NULL; +} + +void +bfa_fcs_tin_delete(struct bfa_fcs_tin_s *tin) +{ +} + +void +bfa_fcs_tin_rport_offline(struct bfa_fcs_tin_s *tin) +{ +} + +void +bfa_fcs_tin_rport_online(struct bfa_fcs_tin_s *tin) +{ +} + +void +bfa_fcs_tin_rx_prli(struct bfa_fcs_tin_s *tin, struct fchs_s *fchs, u16 len) +{ +} + +void +bfa_fcs_fcptm_uf_recv(struct bfa_fcs_tin_s *tin, struct fchs_s *fchs, u16 len) +{ +} + +void +bfa_fcs_tin_pause(struct bfa_fcs_tin_s *tin) +{ +} + +void +bfa_fcs_tin_resume(struct bfa_fcs_tin_s *tin) +{ +} diff --git a/drivers/scsi/bfa/fcs.h b/drivers/scsi/bfa/fcs.h new file mode 100644 index 000000000000..deee685e8478 --- /dev/null +++ b/drivers/scsi/bfa/fcs.h @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2005-2009 Brocade Communications Systems, Inc. + * All rights reserved + * www.brocade.com + * + * Linux driver for Brocade Fibre Channel Host Bus Adapter. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (GPL) Version 2 as + * published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +/** + * fcs.h FCS module functions + */ + + +#ifndef __FCS_H__ +#define __FCS_H__ + +#define __fcs_min_cfg(__fcs) (__fcs)->min_cfg + +void bfa_fcs_modexit_comp(struct bfa_fcs_s *fcs); + +#endif /* __FCS_H__ */ diff --git a/drivers/scsi/bfa/fcs_auth.h b/drivers/scsi/bfa/fcs_auth.h new file mode 100644 index 000000000000..65d155fea3d7 --- /dev/null +++ b/drivers/scsi/bfa/fcs_auth.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2005-2009 Brocade Communications Systems, Inc. + * All rights reserved + * www.brocade.com + * + * Linux driver for Brocade Fibre Channel Host Bus Adapter. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (GPL) Version 2 as + * published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +/** + * fcs_uf.h FCS unsolicited frame receive + */ + + +#ifndef __FCS_AUTH_H__ +#define __FCS_AUTH_H__ + +#include <fcs/bfa_fcs.h> +#include <fcs/bfa_fcs_vport.h> +#include <fcs/bfa_fcs_lport.h> + +/* + * fcs friend functions: only between fcs modules + */ +void bfa_fcs_auth_uf_recv(struct bfa_fcs_fabric_s *fabric, int len); +void bfa_fcs_auth_start(struct bfa_fcs_fabric_s *fabric); +void bfa_fcs_auth_stop(struct bfa_fcs_fabric_s *fabric); + +#endif /* __FCS_UF_H__ */ diff --git a/drivers/scsi/bfa/fcs_fabric.h b/drivers/scsi/bfa/fcs_fabric.h new file mode 100644 index 000000000000..eee960820f86 --- /dev/null +++ b/drivers/scsi/bfa/fcs_fabric.h @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2005-2009 Brocade Communications Systems, Inc. + * All rights reserved + * www.brocade.com + * + * Linux driver for Brocade Fibre Channel Host Bus Adapter. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (GPL) Version 2 as + * published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +/** + * fcs_lport.h FCS logical port interfaces + */ + +#ifndef __FCS_FABRIC_H__ +#define __FCS_FABRIC_H__ + +#include <fcs/bfa_fcs.h> +#include <fcs/bfa_fcs_vport.h> +#include <fcs/bfa_fcs_lport.h> + +/* +* fcs friend functions: only between fcs modules + */ +void bfa_fcs_fabric_modinit(struct bfa_fcs_s *fcs); +void bfa_fcs_fabric_modexit(struct bfa_fcs_s *fcs); +void bfa_fcs_fabric_modsusp(struct bfa_fcs_s *fcs); +void bfa_fcs_fabric_link_up(struct bfa_fcs_fabric_s *fabric); +void bfa_fcs_fabric_link_down(struct bfa_fcs_fabric_s *fabric); +void bfa_fcs_fabric_addvport(struct bfa_fcs_fabric_s *fabric, + struct bfa_fcs_vport_s *vport); +void bfa_fcs_fabric_delvport(struct bfa_fcs_fabric_s *fabric, + struct bfa_fcs_vport_s *vport); +int bfa_fcs_fabric_is_online(struct bfa_fcs_fabric_s *fabric); +struct bfa_fcs_vport_s *bfa_fcs_fabric_vport_lookup( + struct bfa_fcs_fabric_s *fabric, wwn_t pwwn); +void bfa_fcs_fabric_modstart(struct bfa_fcs_s *fcs); +void bfa_fcs_fabric_uf_recv(struct bfa_fcs_fabric_s *fabric, + struct fchs_s *fchs, u16 len); +u16 bfa_fcs_fabric_vport_count(struct bfa_fcs_fabric_s *fabric); +bfa_boolean_t bfa_fcs_fabric_is_loopback(struct bfa_fcs_fabric_s *fabric); +enum bfa_pport_type bfa_fcs_fabric_port_type(struct bfa_fcs_fabric_s *fabric); +void bfa_fcs_fabric_psymb_init(struct bfa_fcs_fabric_s *fabric); +void bfa_fcs_fabric_port_delete_comp(struct bfa_fcs_fabric_s *fabric); + +bfa_status_t bfa_fcs_fabric_addvf(struct bfa_fcs_fabric_s *vf, + struct bfa_fcs_s *fcs, struct bfa_port_cfg_s *port_cfg, + struct bfad_vf_s *vf_drv); +void bfa_fcs_auth_finished(struct bfa_fcs_fabric_s *fabric, + enum auth_status status); + +void bfa_fcs_fabric_set_fabric_name(struct bfa_fcs_fabric_s *fabric, + wwn_t fabric_name); +#endif /* __FCS_FABRIC_H__ */ diff --git a/drivers/scsi/bfa/fcs_fcpim.h b/drivers/scsi/bfa/fcs_fcpim.h new file mode 100644 index 000000000000..61e9e2687de3 --- /dev/null +++ b/drivers/scsi/bfa/fcs_fcpim.h @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2005-2009 Brocade Communications Systems, Inc. + * All rights reserved + * www.brocade.com + * + * Linux driver for Brocade Fibre Channel Host Bus Adapter. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (GPL) Version 2 as + * published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ +#ifndef __FCS_FCPIM_H__ +#define __FCS_FCPIM_H__ + +#include <defs/bfa_defs_port.h> +#include <fcs/bfa_fcs_lport.h> +#include <fcs/bfa_fcs_rport.h> + +/* + * Following routines are from FCPIM and will be called by rport. + */ +struct bfa_fcs_itnim_s *bfa_fcs_itnim_create(struct bfa_fcs_rport_s *rport); +void bfa_fcs_itnim_delete(struct bfa_fcs_itnim_s *itnim); +void bfa_fcs_itnim_rport_offline(struct bfa_fcs_itnim_s *itnim); +void bfa_fcs_itnim_rport_online(struct bfa_fcs_itnim_s *itnim); +bfa_status_t bfa_fcs_itnim_get_online_state(struct bfa_fcs_itnim_s *itnim); + +void bfa_fcs_itnim_is_initiator(struct bfa_fcs_itnim_s *itnim); +void bfa_fcs_itnim_pause(struct bfa_fcs_itnim_s *itnim); +void bfa_fcs_itnim_resume(struct bfa_fcs_itnim_s *itnim); + +/* + * Modudle init/cleanup routines. + */ +void bfa_fcs_fcpim_modinit(struct bfa_fcs_s *fcs); +void bfa_fcs_fcpim_modexit(struct bfa_fcs_s *fcs); +void bfa_fcs_fcpim_uf_recv(struct bfa_fcs_itnim_s *itnim, struct fchs_s *fchs, + u16 len); +#endif /* __FCS_FCPIM_H__ */ diff --git a/drivers/scsi/bfa/fcs_fcptm.h b/drivers/scsi/bfa/fcs_fcptm.h new file mode 100644 index 000000000000..ffff0829fd31 --- /dev/null +++ b/drivers/scsi/bfa/fcs_fcptm.h @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2005-2009 Brocade Communications Systems, Inc. + * All rights reserved + * www.brocade.com + * + * Linux driver for Brocade Fibre Channel Host Bus Adapter. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (GPL) Version 2 as + * published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#ifndef __FCS_FCPTM_H__ +#define __FCS_FCPTM_H__ + +#include <defs/bfa_defs_port.h> +#include <fcs/bfa_fcs_lport.h> +#include <fcs/bfa_fcs_rport.h> + +/* + * Following routines are from FCPTM and will be called by rport. + */ +struct bfa_fcs_tin_s *bfa_fcs_tin_create(struct bfa_fcs_rport_s *rport); +void bfa_fcs_tin_rport_offline(struct bfa_fcs_tin_s *tin); +void bfa_fcs_tin_rport_online(struct bfa_fcs_tin_s *tin); +void bfa_fcs_tin_delete(struct bfa_fcs_tin_s *tin); +void bfa_fcs_tin_rx_prli(struct bfa_fcs_tin_s *tin, struct fchs_s *fchs, + u16 len); +void bfa_fcs_tin_pause(struct bfa_fcs_tin_s *tin); +void bfa_fcs_tin_resume(struct bfa_fcs_tin_s *tin); + +/* + * Modudle init/cleanup routines. + */ +void bfa_fcs_fcptm_modinit(struct bfa_fcs_s *fcs); +void bfa_fcs_fcptm_modexit(struct bfa_fcs_s *fcs); +void bfa_fcs_fcptm_uf_recv(struct bfa_fcs_tin_s *tin, struct fchs_s *fchs, + u16 len); + +#endif /* __FCS_FCPTM_H__ */ diff --git a/drivers/scsi/bfa/fcs_fcxp.h b/drivers/scsi/bfa/fcs_fcxp.h new file mode 100644 index 000000000000..8277fe9c2b70 --- /dev/null +++ b/drivers/scsi/bfa/fcs_fcxp.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2005-2009 Brocade Communications Systems, Inc. + * All rights reserved + * www.brocade.com + * + * Linux driver for Brocade Fibre Channel Host Bus Adapter. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (GPL) Version 2 as + * published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +/** + * fcs_fcxp.h FCXP helper macros for FCS + */ + + +#ifndef __FCS_FCXP_H__ +#define __FCS_FCXP_H__ + +#define bfa_fcs_fcxp_alloc(__fcs) \ + bfa_fcxp_alloc(NULL, (__fcs)->bfa, 0, 0, NULL, NULL, NULL, NULL) + +#endif /* __FCS_FCXP_H__ */ diff --git a/drivers/scsi/bfa/fcs_lport.h b/drivers/scsi/bfa/fcs_lport.h new file mode 100644 index 000000000000..ae744ba35671 --- /dev/null +++ b/drivers/scsi/bfa/fcs_lport.h @@ -0,0 +1,117 @@ +/* + * Copyright (c) 2005-2009 Brocade Communications Systems, Inc. + * All rights reserved + * www.brocade.com + * + * Linux driver for Brocade Fibre Channel Host Bus Adapter. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (GPL) Version 2 as + * published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +/** + * fcs_lport.h FCS logical port interfaces + */ + +#ifndef __FCS_LPORT_H__ +#define __FCS_LPORT_H__ + +#define __VPORT_H__ +#include <defs/bfa_defs_port.h> +#include <bfa_svc.h> +#include <fcs/bfa_fcs_lport.h> +#include <fcs/bfa_fcs_rport.h> +#include <fcs/bfa_fcs_vport.h> +#include <fcs_fabric.h> +#include <fcs_ms.h> +#include <cs/bfa_q.h> +#include <fcbuild.h> + +/* + * PID used in P2P/N2N ( In Big Endian) + */ +#define N2N_LOCAL_PID 0x010000 +#define N2N_REMOTE_PID 0x020000 + +/* + * Misc Timeouts + */ +/* + * To be used when spawning a timer before retrying a failed command. Milli + * Secs. + */ +#define BFA_FCS_RETRY_TIMEOUT 2000 + +/* + * Check for Port/Vport Mode/Role + */ +#define BFA_FCS_VPORT_IS_INITIATOR_MODE(port) \ + (port->port_cfg.roles & BFA_PORT_ROLE_FCP_IM) + +#define BFA_FCS_VPORT_IS_TARGET_MODE(port) \ + (port->port_cfg.roles & BFA_PORT_ROLE_FCP_TM) + +#define BFA_FCS_VPORT_IS_IPFC_MODE(port) \ + (port->port_cfg.roles & BFA_PORT_ROLE_FCP_IPFC) + +/* + * Is this a Well Known Address + */ +#define BFA_FCS_PID_IS_WKA(pid) ((bfa_os_ntoh3b(pid) > 0xFFF000) ? 1 : 0) + +/* + * Pointer to elements within Port + */ +#define BFA_FCS_GET_HAL_FROM_PORT(port) (port->fcs->bfa) +#define BFA_FCS_GET_NS_FROM_PORT(port) (&port->port_topo.pfab.ns) +#define BFA_FCS_GET_SCN_FROM_PORT(port) (&port->port_topo.pfab.scn) +#define BFA_FCS_GET_MS_FROM_PORT(port) (&port->port_topo.pfab.ms) +#define BFA_FCS_GET_FDMI_FROM_PORT(port) (&port->port_topo.pfab.ms.fdmi) + +/* + * handler for unsolicied frames + */ +void bfa_fcs_port_uf_recv(struct bfa_fcs_port_s *lport, struct fchs_s *fchs, + u16 len); + +/* + * Following routines will be called by Fabric to indicate port + * online/offline to vport. + */ +void bfa_fcs_lport_init(struct bfa_fcs_port_s *lport, struct bfa_fcs_s *fcs, + u16 vf_id, struct bfa_port_cfg_s *port_cfg, + struct bfa_fcs_vport_s *vport); +void bfa_fcs_port_online(struct bfa_fcs_port_s *port); +void bfa_fcs_port_offline(struct bfa_fcs_port_s *port); +void bfa_fcs_port_delete(struct bfa_fcs_port_s *port); +bfa_boolean_t bfa_fcs_port_is_online(struct bfa_fcs_port_s *port); + +/* + * Lookup rport based on PID + */ +struct bfa_fcs_rport_s *bfa_fcs_port_get_rport_by_pid( + struct bfa_fcs_port_s *port, u32 pid); + +/* + * Lookup rport based on PWWN + */ +struct bfa_fcs_rport_s *bfa_fcs_port_get_rport_by_pwwn( + struct bfa_fcs_port_s *port, wwn_t pwwn); +struct bfa_fcs_rport_s *bfa_fcs_port_get_rport_by_nwwn( + struct bfa_fcs_port_s *port, wwn_t nwwn); +void bfa_fcs_port_add_rport(struct bfa_fcs_port_s *port, + struct bfa_fcs_rport_s *rport); +void bfa_fcs_port_del_rport(struct bfa_fcs_port_s *port, + struct bfa_fcs_rport_s *rport); + +void bfa_fcs_port_modinit(struct bfa_fcs_s *fcs); +void bfa_fcs_port_modexit(struct bfa_fcs_s *fcs); +void bfa_fcs_port_lip(struct bfa_fcs_port_s *port); + +#endif /* __FCS_LPORT_H__ */ diff --git a/drivers/scsi/bfa/fcs_ms.h b/drivers/scsi/bfa/fcs_ms.h new file mode 100644 index 000000000000..b6a8c12876f4 --- /dev/null +++ b/drivers/scsi/bfa/fcs_ms.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2005-2009 Brocade Communications Systems, Inc. + * All rights reserved + * www.brocade.com + * + * Linux driver for Brocade Fibre Channel Host Bus Adapter. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (GPL) Version 2 as + * published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +/** + * fcs_ms.h FCS ms interfaces + */ +#ifndef __FCS_MS_H__ +#define __FCS_MS_H__ + +/* MS FCS routines */ +void bfa_fcs_port_ms_init(struct bfa_fcs_port_s *port); +void bfa_fcs_port_ms_offline(struct bfa_fcs_port_s *port); +void bfa_fcs_port_ms_online(struct bfa_fcs_port_s *port); +void bfa_fcs_port_ms_fabric_rscn(struct bfa_fcs_port_s *port); + +/* FDMI FCS routines */ +void bfa_fcs_port_fdmi_init(struct bfa_fcs_port_ms_s *ms); +void bfa_fcs_port_fdmi_offline(struct bfa_fcs_port_ms_s *ms); +void bfa_fcs_port_fdmi_online(struct bfa_fcs_port_ms_s *ms); + +#endif diff --git a/drivers/scsi/bfa/fcs_port.h b/drivers/scsi/bfa/fcs_port.h new file mode 100644 index 000000000000..abb65191dd27 --- /dev/null +++ b/drivers/scsi/bfa/fcs_port.h @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2005-2009 Brocade Communications Systems, Inc. + * All rights reserved + * www.brocade.com + * + * Linux driver for Brocade Fibre Channel Host Bus Adapter. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (GPL) Version 2 as + * published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +/** + * fcs_pport.h FCS physical port interfaces + */ + + +#ifndef __FCS_PPORT_H__ +#define __FCS_PPORT_H__ + +/* + * fcs friend functions: only between fcs modules + */ +void bfa_fcs_pport_modinit(struct bfa_fcs_s *fcs); +void bfa_fcs_pport_modexit(struct bfa_fcs_s *fcs); + +#endif /* __FCS_PPORT_H__ */ diff --git a/drivers/scsi/bfa/fcs_rport.h b/drivers/scsi/bfa/fcs_rport.h new file mode 100644 index 000000000000..f601e9d74236 --- /dev/null +++ b/drivers/scsi/bfa/fcs_rport.h @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2005-2009 Brocade Communications Systems, Inc. + * All rights reserved + * www.brocade.com + * + * Linux driver for Brocade Fibre Channel Host Bus Adapter. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (GPL) Version 2 as + * published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +/** + * fcs_rport.h FCS rport interfaces and defines + */ + +#ifndef __FCS_RPORT_H__ +#define __FCS_RPORT_H__ + +#include <fcs/bfa_fcs_rport.h> + +void bfa_fcs_rport_modinit(struct bfa_fcs_s *fcs); +void bfa_fcs_rport_modexit(struct bfa_fcs_s *fcs); + +void bfa_fcs_rport_uf_recv(struct bfa_fcs_rport_s *rport, struct fchs_s *fchs, + u16 len); +void bfa_fcs_rport_scn(struct bfa_fcs_rport_s *rport); + +struct bfa_fcs_rport_s *bfa_fcs_rport_create(struct bfa_fcs_port_s *port, + u32 pid); +void bfa_fcs_rport_delete(struct bfa_fcs_rport_s *rport); +void bfa_fcs_rport_online(struct bfa_fcs_rport_s *rport); +void bfa_fcs_rport_offline(struct bfa_fcs_rport_s *rport); +void bfa_fcs_rport_start(struct bfa_fcs_port_s *port, struct fchs_s *rx_fchs, + struct fc_logi_s *plogi_rsp); +void bfa_fcs_rport_plogi_create(struct bfa_fcs_port_s *port, + struct fchs_s *rx_fchs, + struct fc_logi_s *plogi); +void bfa_fcs_rport_plogi(struct bfa_fcs_rport_s *rport, struct fchs_s *fchs, + struct fc_logi_s *plogi); +void bfa_fcs_rport_logo_imp(struct bfa_fcs_rport_s *rport); +void bfa_fcs_rport_itnim_ack(struct bfa_fcs_rport_s *rport); +void bfa_fcs_rport_itntm_ack(struct bfa_fcs_rport_s *rport); +void bfa_fcs_rport_tin_ack(struct bfa_fcs_rport_s *rport); +void bfa_fcs_rport_fcptm_offline_done(struct bfa_fcs_rport_s *rport); +int bfa_fcs_rport_get_state(struct bfa_fcs_rport_s *rport); +struct bfa_fcs_rport_s *bfa_fcs_rport_create_by_wwn(struct bfa_fcs_port_s *port, + wwn_t wwn); + + +/* Rport Features */ +void bfa_fcs_rpf_init(struct bfa_fcs_rport_s *rport); +void bfa_fcs_rpf_rport_online(struct bfa_fcs_rport_s *rport); +void bfa_fcs_rpf_rport_offline(struct bfa_fcs_rport_s *rport); + +#endif /* __FCS_RPORT_H__ */ diff --git a/drivers/scsi/bfa/fcs_trcmod.h b/drivers/scsi/bfa/fcs_trcmod.h new file mode 100644 index 000000000000..41b5ae8d7644 --- /dev/null +++ b/drivers/scsi/bfa/fcs_trcmod.h @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2005-2009 Brocade Communications Systems, Inc. + * All rights reserved + * www.brocade.com + * + * Linux driver for Brocade Fibre Channel Host Bus Adapter. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (GPL) Version 2 as + * published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +/** + * fcs_trcmod.h BFA FCS trace modules + */ + +#ifndef __FCS_TRCMOD_H__ +#define __FCS_TRCMOD_H__ + +#include <cs/bfa_trc.h> + +/* + * !!! Only append to the enums defined here to avoid any versioning + * !!! needed between trace utility and driver version + */ +enum { + BFA_TRC_FCS_FABRIC = 1, + BFA_TRC_FCS_VFAPI = 2, + BFA_TRC_FCS_PORT = 3, + BFA_TRC_FCS_VPORT = 4, + BFA_TRC_FCS_VP_API = 5, + BFA_TRC_FCS_VPS = 6, + BFA_TRC_FCS_RPORT = 7, + BFA_TRC_FCS_FCPIM = 8, + BFA_TRC_FCS_FCPTM = 9, + BFA_TRC_FCS_NS = 10, + BFA_TRC_FCS_SCN = 11, + BFA_TRC_FCS_LOOP = 12, + BFA_TRC_FCS_UF = 13, + BFA_TRC_FCS_PPORT = 14, + BFA_TRC_FCS_FCPIP = 15, + BFA_TRC_FCS_PORT_API = 16, + BFA_TRC_FCS_RPORT_API = 17, + BFA_TRC_FCS_AUTH = 18, + BFA_TRC_FCS_N2N = 19, + BFA_TRC_FCS_MS = 20, + BFA_TRC_FCS_FDMI = 21, + BFA_TRC_FCS_RPORT_FTRS = 22, +}; + +#endif /* __FCS_TRCMOD_H__ */ diff --git a/drivers/scsi/bfa/fcs_uf.h b/drivers/scsi/bfa/fcs_uf.h new file mode 100644 index 000000000000..96f1bdcb31ed --- /dev/null +++ b/drivers/scsi/bfa/fcs_uf.h @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2005-2009 Brocade Communications Systems, Inc. + * All rights reserved + * www.brocade.com + * + * Linux driver for Brocade Fibre Channel Host Bus Adapter. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (GPL) Version 2 as + * published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +/** + * fcs_uf.h FCS unsolicited frame receive + */ + + +#ifndef __FCS_UF_H__ +#define __FCS_UF_H__ + +/* + * fcs friend functions: only between fcs modules + */ +void bfa_fcs_uf_modinit(struct bfa_fcs_s *fcs); +void bfa_fcs_uf_modexit(struct bfa_fcs_s *fcs); + +#endif /* __FCS_UF_H__ */ diff --git a/drivers/scsi/bfa/fcs_vport.h b/drivers/scsi/bfa/fcs_vport.h new file mode 100644 index 000000000000..9e80b6a97b7f --- /dev/null +++ b/drivers/scsi/bfa/fcs_vport.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2005-2009 Brocade Communications Systems, Inc. + * All rights reserved + * www.brocade.com + * + * Linux driver for Brocade Fibre Channel Host Bus Adapter. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (GPL) Version 2 as + * published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#ifndef __FCS_VPORT_H__ +#define __FCS_VPORT_H__ + +#include <fcs/bfa_fcs_lport.h> +#include <fcs/bfa_fcs_vport.h> +#include <defs/bfa_defs_pci.h> + +/* + * Modudle init/cleanup routines. + */ + +void bfa_fcs_vport_modinit(struct bfa_fcs_s *fcs); +void bfa_fcs_vport_modexit(struct bfa_fcs_s *fcs); + +void bfa_fcs_vport_cleanup(struct bfa_fcs_vport_s *vport); +void bfa_fcs_vport_online(struct bfa_fcs_vport_s *vport); +void bfa_fcs_vport_offline(struct bfa_fcs_vport_s *vport); +void bfa_fcs_vport_delete_comp(struct bfa_fcs_vport_s *vport); +u32 bfa_fcs_vport_get_max(struct bfa_fcs_s *fcs); + +#endif /* __FCS_VPORT_H__ */ + diff --git a/drivers/scsi/bfa/fdmi.c b/drivers/scsi/bfa/fdmi.c new file mode 100644 index 000000000000..b845eb272c78 --- /dev/null +++ b/drivers/scsi/bfa/fdmi.c @@ -0,0 +1,1223 @@ +/* + * Copyright (c) 2005-2009 Brocade Communications Systems, Inc. + * All rights reserved + * www.brocade.com + * + * Linux driver for Brocade Fibre Channel Host Bus Adapter. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (GPL) Version 2 as + * published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +/** + * port_api.c BFA FCS port + */ + + +#include <bfa.h> +#include <bfa_svc.h> +#include "fcs_lport.h" +#include "fcs_rport.h" +#include "lport_priv.h" +#include "fcs_trcmod.h" +#include "fcs_fcxp.h" +#include <fcs/bfa_fcs_fdmi.h> + +BFA_TRC_FILE(FCS, FDMI); + +#define BFA_FCS_FDMI_CMD_MAX_RETRIES 2 + +/* + * forward declarations + */ +static void bfa_fcs_port_fdmi_send_rhba(void *fdmi_cbarg, + struct bfa_fcxp_s *fcxp_alloced); +static void bfa_fcs_port_fdmi_send_rprt(void *fdmi_cbarg, + struct bfa_fcxp_s *fcxp_alloced); +static void bfa_fcs_port_fdmi_send_rpa(void *fdmi_cbarg, + struct bfa_fcxp_s *fcxp_alloced); +static void bfa_fcs_port_fdmi_rhba_response(void *fcsarg, + struct bfa_fcxp_s *fcxp, + void *cbarg, + bfa_status_t req_status, + u32 rsp_len, + u32 resid_len, + struct fchs_s *rsp_fchs); +static void bfa_fcs_port_fdmi_rprt_response(void *fcsarg, + struct bfa_fcxp_s *fcxp, + void *cbarg, + bfa_status_t req_status, + u32 rsp_len, + u32 resid_len, + struct fchs_s *rsp_fchs); +static void bfa_fcs_port_fdmi_rpa_response(void *fcsarg, + struct bfa_fcxp_s *fcxp, + void *cbarg, + bfa_status_t req_status, + u32 rsp_len, + u32 resid_len, + struct fchs_s *rsp_fchs); +static void bfa_fcs_port_fdmi_timeout(void *arg); +static u16 bfa_fcs_port_fdmi_build_rhba_pyld( + struct bfa_fcs_port_fdmi_s *fdmi, u8 *pyld); +static u16 bfa_fcs_port_fdmi_build_rprt_pyld( + struct bfa_fcs_port_fdmi_s *fdmi, u8 *pyld); +static u16 bfa_fcs_port_fdmi_build_rpa_pyld( + struct bfa_fcs_port_fdmi_s *fdmi, u8 *pyld); +static u16 bfa_fcs_port_fdmi_build_portattr_block( + struct bfa_fcs_port_fdmi_s *fdmi, u8 *pyld); +void bfa_fcs_fdmi_get_hbaattr(struct bfa_fcs_port_fdmi_s *fdmi, + struct bfa_fcs_fdmi_hba_attr_s *hba_attr); +void bfa_fcs_fdmi_get_portattr(struct bfa_fcs_port_fdmi_s *fdmi, + struct bfa_fcs_fdmi_port_attr_s *port_attr); +/** + * fcs_fdmi_sm FCS FDMI state machine + */ + +/** + * FDMI State Machine events + */ +enum port_fdmi_event { + FDMISM_EVENT_PORT_ONLINE = 1, + FDMISM_EVENT_PORT_OFFLINE = 2, + FDMISM_EVENT_RSP_OK = 4, + FDMISM_EVENT_RSP_ERROR = 5, + FDMISM_EVENT_TIMEOUT = 6, + FDMISM_EVENT_RHBA_SENT = 7, + FDMISM_EVENT_RPRT_SENT = 8, + FDMISM_EVENT_RPA_SENT = 9, +}; + +static void bfa_fcs_port_fdmi_sm_offline(struct bfa_fcs_port_fdmi_s *fdmi, + enum port_fdmi_event event); +static void bfa_fcs_port_fdmi_sm_sending_rhba(struct bfa_fcs_port_fdmi_s *fdmi, + enum port_fdmi_event event); +static void bfa_fcs_port_fdmi_sm_rhba(struct bfa_fcs_port_fdmi_s *fdmi, + enum port_fdmi_event event); +static void bfa_fcs_port_fdmi_sm_rhba_retry(struct bfa_fcs_port_fdmi_s *fdmi, + enum port_fdmi_event event); +static void bfa_fcs_port_fdmi_sm_sending_rprt(struct bfa_fcs_port_fdmi_s *fdmi, + enum port_fdmi_event event); +static void bfa_fcs_port_fdmi_sm_rprt(struct bfa_fcs_port_fdmi_s *fdmi, + enum port_fdmi_event event); +static void bfa_fcs_port_fdmi_sm_rprt_retry(struct bfa_fcs_port_fdmi_s *fdmi, + enum port_fdmi_event event); +static void bfa_fcs_port_fdmi_sm_sending_rpa(struct bfa_fcs_port_fdmi_s *fdmi, + enum port_fdmi_event event); +static void bfa_fcs_port_fdmi_sm_rpa(struct bfa_fcs_port_fdmi_s *fdmi, + enum port_fdmi_event event); +static void bfa_fcs_port_fdmi_sm_rpa_retry(struct bfa_fcs_port_fdmi_s *fdmi, + enum port_fdmi_event event); +static void bfa_fcs_port_fdmi_sm_online(struct bfa_fcs_port_fdmi_s *fdmi, + enum port_fdmi_event event); +/** + * Start in offline state - awaiting MS to send start. + */ +static void +bfa_fcs_port_fdmi_sm_offline(struct bfa_fcs_port_fdmi_s *fdmi, + enum port_fdmi_event event) +{ + struct bfa_fcs_port_s *port = fdmi->ms->port; + + bfa_trc(port->fcs, port->port_cfg.pwwn); + bfa_trc(port->fcs, event); + + fdmi->retry_cnt = 0; + + switch (event) { + case FDMISM_EVENT_PORT_ONLINE: + if (port->vport) { + /* + * For Vports, register a new port. + */ + bfa_sm_set_state(fdmi, + bfa_fcs_port_fdmi_sm_sending_rprt); + bfa_fcs_port_fdmi_send_rprt(fdmi, NULL); + } else { + /* + * For a base port, we should first register the HBA + * atribute. The HBA attribute also contains the base + * port registration. + */ + bfa_sm_set_state(fdmi, + bfa_fcs_port_fdmi_sm_sending_rhba); + bfa_fcs_port_fdmi_send_rhba(fdmi, NULL); + } + break; + + case FDMISM_EVENT_PORT_OFFLINE: + break; + + default: + bfa_assert(0); + } +} + +static void +bfa_fcs_port_fdmi_sm_sending_rhba(struct bfa_fcs_port_fdmi_s *fdmi, + enum port_fdmi_event event) +{ + struct bfa_fcs_port_s *port = fdmi->ms->port; + + bfa_trc(port->fcs, port->port_cfg.pwwn); + bfa_trc(port->fcs, event); + + switch (event) { + case FDMISM_EVENT_RHBA_SENT: + bfa_sm_set_state(fdmi, bfa_fcs_port_fdmi_sm_rhba); + break; + + case FDMISM_EVENT_PORT_OFFLINE: + bfa_sm_set_state(fdmi, bfa_fcs_port_fdmi_sm_offline); + bfa_fcxp_walloc_cancel(BFA_FCS_GET_HAL_FROM_PORT(port), + &fdmi->fcxp_wqe); + break; + + default: + bfa_assert(0); + } +} + +static void +bfa_fcs_port_fdmi_sm_rhba(struct bfa_fcs_port_fdmi_s *fdmi, + enum port_fdmi_event event) +{ + struct bfa_fcs_port_s *port = fdmi->ms->port; + + bfa_trc(port->fcs, port->port_cfg.pwwn); + bfa_trc(port->fcs, event); + + switch (event) { + case FDMISM_EVENT_RSP_ERROR: + /* + * if max retries have not been reached, start timer for a + * delayed retry + */ + if (fdmi->retry_cnt++ < BFA_FCS_FDMI_CMD_MAX_RETRIES) { + bfa_sm_set_state(fdmi, bfa_fcs_port_fdmi_sm_rhba_retry); + bfa_timer_start(BFA_FCS_GET_HAL_FROM_PORT(port), + &fdmi->timer, bfa_fcs_port_fdmi_timeout, + fdmi, BFA_FCS_RETRY_TIMEOUT); + } else { + /* + * set state to offline + */ + bfa_sm_set_state(fdmi, bfa_fcs_port_fdmi_sm_offline); + } + break; + + case FDMISM_EVENT_RSP_OK: + /* + * Initiate Register Port Attributes + */ + bfa_sm_set_state(fdmi, bfa_fcs_port_fdmi_sm_sending_rpa); + fdmi->retry_cnt = 0; + bfa_fcs_port_fdmi_send_rpa(fdmi, NULL); + break; + + case FDMISM_EVENT_PORT_OFFLINE: + bfa_fcxp_discard(fdmi->fcxp); + bfa_sm_set_state(fdmi, bfa_fcs_port_fdmi_sm_offline); + break; + + default: + bfa_assert(0); + } +} + +static void +bfa_fcs_port_fdmi_sm_rhba_retry(struct bfa_fcs_port_fdmi_s *fdmi, + enum port_fdmi_event event) +{ + struct bfa_fcs_port_s *port = fdmi->ms->port; + + bfa_trc(port->fcs, port->port_cfg.pwwn); + bfa_trc(port->fcs, event); + + switch (event) { + case FDMISM_EVENT_TIMEOUT: + /* + * Retry Timer Expired. Re-send + */ + bfa_sm_set_state(fdmi, bfa_fcs_port_fdmi_sm_sending_rhba); + bfa_fcs_port_fdmi_send_rhba(fdmi, NULL); + break; + + case FDMISM_EVENT_PORT_OFFLINE: + bfa_sm_set_state(fdmi, bfa_fcs_port_fdmi_sm_offline); + bfa_timer_stop(&fdmi->timer); + break; + + default: + bfa_assert(0); + } +} + +/* +* RPRT : Register Port + */ +static void +bfa_fcs_port_fdmi_sm_sending_rprt(struct bfa_fcs_port_fdmi_s *fdmi, + enum port_fdmi_event event) +{ + struct bfa_fcs_port_s *port = fdmi->ms->port; + + bfa_trc(port->fcs, port->port_cfg.pwwn); + bfa_trc(port->fcs, event); + + switch (event) { + case FDMISM_EVENT_RPRT_SENT: + bfa_sm_set_state(fdmi, bfa_fcs_port_fdmi_sm_rprt); + break; + + case FDMISM_EVENT_PORT_OFFLINE: + bfa_sm_set_state(fdmi, bfa_fcs_port_fdmi_sm_offline); + bfa_fcxp_walloc_cancel(BFA_FCS_GET_HAL_FROM_PORT(port), + &fdmi->fcxp_wqe); + break; + + default: + bfa_assert(0); + } +} + +static void +bfa_fcs_port_fdmi_sm_rprt(struct bfa_fcs_port_fdmi_s *fdmi, + enum port_fdmi_event event) +{ + struct bfa_fcs_port_s *port = fdmi->ms->port; + + bfa_trc(port->fcs, port->port_cfg.pwwn); + bfa_trc(port->fcs, event); + + switch (event) { + case FDMISM_EVENT_RSP_ERROR: + /* + * if max retries have not been reached, start timer for a + * delayed retry + */ + if (fdmi->retry_cnt++ < BFA_FCS_FDMI_CMD_MAX_RETRIES) { + bfa_sm_set_state(fdmi, bfa_fcs_port_fdmi_sm_rprt_retry); + bfa_timer_start(BFA_FCS_GET_HAL_FROM_PORT(port), + &fdmi->timer, bfa_fcs_port_fdmi_timeout, + fdmi, BFA_FCS_RETRY_TIMEOUT); + + } else { + /* + * set state to offline + */ + bfa_sm_set_state(fdmi, bfa_fcs_port_fdmi_sm_offline); + fdmi->retry_cnt = 0; + } + break; + + case FDMISM_EVENT_RSP_OK: + fdmi->retry_cnt = 0; + bfa_sm_set_state(fdmi, bfa_fcs_port_fdmi_sm_online); + break; + + case FDMISM_EVENT_PORT_OFFLINE: + bfa_fcxp_discard(fdmi->fcxp); + bfa_sm_set_state(fdmi, bfa_fcs_port_fdmi_sm_offline); + break; + + default: + bfa_assert(0); + } +} + +static void +bfa_fcs_port_fdmi_sm_rprt_retry(struct bfa_fcs_port_fdmi_s *fdmi, + enum port_fdmi_event event) +{ + struct bfa_fcs_port_s *port = fdmi->ms->port; + + bfa_trc(port->fcs, port->port_cfg.pwwn); + bfa_trc(port->fcs, event); + + switch (event) { + case FDMISM_EVENT_TIMEOUT: + /* + * Retry Timer Expired. Re-send + */ + bfa_sm_set_state(fdmi, bfa_fcs_port_fdmi_sm_sending_rprt); + bfa_fcs_port_fdmi_send_rprt(fdmi, NULL); + break; + + case FDMISM_EVENT_PORT_OFFLINE: + bfa_sm_set_state(fdmi, bfa_fcs_port_fdmi_sm_offline); + bfa_timer_stop(&fdmi->timer); + break; + + default: + bfa_assert(0); + } +} + +/* + * Register Port Attributes + */ +static void +bfa_fcs_port_fdmi_sm_sending_rpa(struct bfa_fcs_port_fdmi_s *fdmi, + enum port_fdmi_event event) +{ + struct bfa_fcs_port_s *port = fdmi->ms->port; + + bfa_trc(port->fcs, port->port_cfg.pwwn); + bfa_trc(port->fcs, event); + + switch (event) { + case FDMISM_EVENT_RPA_SENT: + bfa_sm_set_state(fdmi, bfa_fcs_port_fdmi_sm_rpa); + break; + + case FDMISM_EVENT_PORT_OFFLINE: + bfa_sm_set_state(fdmi, bfa_fcs_port_fdmi_sm_offline); + bfa_fcxp_walloc_cancel(BFA_FCS_GET_HAL_FROM_PORT(port), + &fdmi->fcxp_wqe); + break; + + default: + bfa_assert(0); + } +} + +static void +bfa_fcs_port_fdmi_sm_rpa(struct bfa_fcs_port_fdmi_s *fdmi, + enum port_fdmi_event event) +{ + struct bfa_fcs_port_s *port = fdmi->ms->port; + + bfa_trc(port->fcs, port->port_cfg.pwwn); + bfa_trc(port->fcs, event); + + switch (event) { + case FDMISM_EVENT_RSP_ERROR: + /* + * if max retries have not been reached, start timer for a + * delayed retry + */ + if (fdmi->retry_cnt++ < BFA_FCS_FDMI_CMD_MAX_RETRIES) { + bfa_sm_set_state(fdmi, bfa_fcs_port_fdmi_sm_rpa_retry); + bfa_timer_start(BFA_FCS_GET_HAL_FROM_PORT(port), + &fdmi->timer, bfa_fcs_port_fdmi_timeout, + fdmi, BFA_FCS_RETRY_TIMEOUT); + } else { + /* + * set state to offline + */ + bfa_sm_set_state(fdmi, bfa_fcs_port_fdmi_sm_offline); + fdmi->retry_cnt = 0; + } + break; + + case FDMISM_EVENT_RSP_OK: + bfa_sm_set_state(fdmi, bfa_fcs_port_fdmi_sm_online); + fdmi->retry_cnt = 0; + break; + + case FDMISM_EVENT_PORT_OFFLINE: + bfa_fcxp_discard(fdmi->fcxp); + bfa_sm_set_state(fdmi, bfa_fcs_port_fdmi_sm_offline); + break; + + default: + bfa_assert(0); + } +} + +static void +bfa_fcs_port_fdmi_sm_rpa_retry(struct bfa_fcs_port_fdmi_s *fdmi, + enum port_fdmi_event event) +{ + struct bfa_fcs_port_s *port = fdmi->ms->port; + + bfa_trc(port->fcs, port->port_cfg.pwwn); + bfa_trc(port->fcs, event); + + switch (event) { + case FDMISM_EVENT_TIMEOUT: + /* + * Retry Timer Expired. Re-send + */ + bfa_sm_set_state(fdmi, bfa_fcs_port_fdmi_sm_sending_rpa); + bfa_fcs_port_fdmi_send_rpa(fdmi, NULL); + break; + + case FDMISM_EVENT_PORT_OFFLINE: + bfa_sm_set_state(fdmi, bfa_fcs_port_fdmi_sm_offline); + bfa_timer_stop(&fdmi->timer); + break; + + default: + bfa_assert(0); + } +} + +static void +bfa_fcs_port_fdmi_sm_online(struct bfa_fcs_port_fdmi_s *fdmi, + enum port_fdmi_event event) +{ + struct bfa_fcs_port_s *port = fdmi->ms->port; + + bfa_trc(port->fcs, port->port_cfg.pwwn); + bfa_trc(port->fcs, event); + + switch (event) { + case FDMISM_EVENT_PORT_OFFLINE: + bfa_sm_set_state(fdmi, bfa_fcs_port_fdmi_sm_offline); + break; + + default: + bfa_assert(0); + } +} + + +/** +* RHBA : Register HBA Attributes. + */ +static void +bfa_fcs_port_fdmi_send_rhba(void *fdmi_cbarg, struct bfa_fcxp_s *fcxp_alloced) +{ + struct bfa_fcs_port_fdmi_s *fdmi = fdmi_cbarg; + struct bfa_fcs_port_s *port = fdmi->ms->port; + struct fchs_s fchs; + int len, attr_len; + struct bfa_fcxp_s *fcxp; + u8 *pyld; + + bfa_trc(port->fcs, port->port_cfg.pwwn); + + fcxp = fcxp_alloced ? fcxp_alloced : bfa_fcs_fcxp_alloc(port->fcs); + if (!fcxp) { + bfa_fcxp_alloc_wait(port->fcs->bfa, &fdmi->fcxp_wqe, + bfa_fcs_port_fdmi_send_rhba, fdmi); + return; + } + fdmi->fcxp = fcxp; + + pyld = bfa_fcxp_get_reqbuf(fcxp); + bfa_os_memset(pyld, 0, FC_MAX_PDUSZ); + + len = fc_fdmi_reqhdr_build(&fchs, pyld, bfa_fcs_port_get_fcid(port), + FDMI_RHBA); + + attr_len = bfa_fcs_port_fdmi_build_rhba_pyld(fdmi, + (u8 *) ((struct ct_hdr_s *) pyld + 1)); + + bfa_fcxp_send(fcxp, NULL, port->fabric->vf_id, port->lp_tag, BFA_FALSE, + FC_CLASS_3, (len + attr_len), &fchs, + bfa_fcs_port_fdmi_rhba_response, (void *)fdmi, + FC_MAX_PDUSZ, FC_RA_TOV); + + bfa_sm_send_event(fdmi, FDMISM_EVENT_RHBA_SENT); +} + +static u16 +bfa_fcs_port_fdmi_build_rhba_pyld(struct bfa_fcs_port_fdmi_s *fdmi, + u8 *pyld) +{ + struct bfa_fcs_port_s *port = fdmi->ms->port; + struct bfa_fcs_fdmi_hba_attr_s hba_attr; /* @todo */ + struct bfa_fcs_fdmi_hba_attr_s *fcs_hba_attr = &hba_attr; /* @todo */ + struct fdmi_rhba_s *rhba = (struct fdmi_rhba_s *) pyld; + struct fdmi_attr_s *attr; + u8 *curr_ptr; + u16 len, count; + + /* + * get hba attributes + */ + bfa_fcs_fdmi_get_hbaattr(fdmi, fcs_hba_attr); + + rhba->hba_id = bfa_fcs_port_get_pwwn(port); + rhba->port_list.num_ports = bfa_os_htonl(1); + rhba->port_list.port_entry = bfa_fcs_port_get_pwwn(port); + + len = sizeof(rhba->hba_id) + sizeof(rhba->port_list); + + count = 0; + len += sizeof(rhba->hba_attr_blk.attr_count); + + /* + * fill out the invididual entries of the HBA attrib Block + */ + curr_ptr = (u8 *) &rhba->hba_attr_blk.hba_attr; + + /* + * Node Name + */ + attr = (struct fdmi_attr_s *) curr_ptr; + attr->type = bfa_os_htons(FDMI_HBA_ATTRIB_NODENAME); + attr->len = sizeof(wwn_t); + memcpy(attr->value, &bfa_fcs_port_get_nwwn(port), attr->len); + curr_ptr += sizeof(attr->type) + sizeof(attr->len) + attr->len; + len += attr->len; + count++; + attr->len = + bfa_os_htons(attr->len + sizeof(attr->type) + + sizeof(attr->len)); + + /* + * Manufacturer + */ + attr = (struct fdmi_attr_s *) curr_ptr; + attr->type = bfa_os_htons(FDMI_HBA_ATTRIB_MANUFACTURER); + attr->len = (u16) strlen(fcs_hba_attr->manufacturer); + memcpy(attr->value, fcs_hba_attr->manufacturer, attr->len); + /* variable fields need to be 4 byte aligned */ + attr->len = fc_roundup(attr->len, sizeof(u32)); + curr_ptr += sizeof(attr->type) + sizeof(attr->len) + attr->len; + len += attr->len; + count++; + attr->len = + bfa_os_htons(attr->len + sizeof(attr->type) + + sizeof(attr->len)); + + /* + * Serial Number + */ + attr = (struct fdmi_attr_s *) curr_ptr; + attr->type = bfa_os_htons(FDMI_HBA_ATTRIB_SERIALNUM); + attr->len = (u16) strlen(fcs_hba_attr->serial_num); + memcpy(attr->value, fcs_hba_attr->serial_num, attr->len); + /* variable fields need to be 4 byte aligned */ + attr->len = fc_roundup(attr->len, sizeof(u32)); + curr_ptr += sizeof(attr->type) + sizeof(attr->len) + attr->len; + len += attr->len; + count++; + attr->len = + bfa_os_htons(attr->len + sizeof(attr->type) + + sizeof(attr->len)); + + /* + * Model + */ + attr = (struct fdmi_attr_s *) curr_ptr; + attr->type = bfa_os_htons(FDMI_HBA_ATTRIB_MODEL); + attr->len = (u16) strlen(fcs_hba_attr->model); + memcpy(attr->value, fcs_hba_attr->model, attr->len); + /* variable fields need to be 4 byte aligned */ + attr->len = fc_roundup(attr->len, sizeof(u32)); + curr_ptr += sizeof(attr->type) + sizeof(attr->len) + attr->len; + len += attr->len; + count++; + attr->len = + bfa_os_htons(attr->len + sizeof(attr->type) + + sizeof(attr->len)); + + /* + * Model Desc + */ + attr = (struct fdmi_attr_s *) curr_ptr; + attr->type = bfa_os_htons(FDMI_HBA_ATTRIB_MODEL_DESC); + attr->len = (u16) strlen(fcs_hba_attr->model_desc); + memcpy(attr->value, fcs_hba_attr->model_desc, attr->len); + /* variable fields need to be 4 byte aligned */ + attr->len = fc_roundup(attr->len, sizeof(u32)); + curr_ptr += sizeof(attr->type) + sizeof(attr->len) + attr->len; + len += attr->len; + count++; + attr->len = + bfa_os_htons(attr->len + sizeof(attr->type) + + sizeof(attr->len)); + + /* + * H/W Version + */ + if (fcs_hba_attr->hw_version[0] != '\0') { + attr = (struct fdmi_attr_s *) curr_ptr; + attr->type = bfa_os_htons(FDMI_HBA_ATTRIB_HW_VERSION); + attr->len = (u16) strlen(fcs_hba_attr->hw_version); + memcpy(attr->value, fcs_hba_attr->hw_version, attr->len); + /* variable fields need to be 4 byte aligned */ + attr->len = fc_roundup(attr->len, sizeof(u32)); + curr_ptr += sizeof(attr->type) + sizeof(attr->len) + attr->len; + len += attr->len; + count++; + attr->len = + bfa_os_htons(attr->len + sizeof(attr->type) + + sizeof(attr->len)); + } + + /* + * Driver Version + */ + attr = (struct fdmi_attr_s *) curr_ptr; + attr->type = bfa_os_htons(FDMI_HBA_ATTRIB_DRIVER_VERSION); + attr->len = (u16) strlen(fcs_hba_attr->driver_version); + memcpy(attr->value, fcs_hba_attr->driver_version, attr->len); + /* variable fields need to be 4 byte aligned */ + attr->len = fc_roundup(attr->len, sizeof(u32)); + curr_ptr += sizeof(attr->type) + sizeof(attr->len) + attr->len; + len += attr->len;; + count++; + attr->len = + bfa_os_htons(attr->len + sizeof(attr->type) + + sizeof(attr->len)); + + /* + * Option Rom Version + */ + if (fcs_hba_attr->option_rom_ver[0] != '\0') { + attr = (struct fdmi_attr_s *) curr_ptr; + attr->type = bfa_os_htons(FDMI_HBA_ATTRIB_ROM_VERSION); + attr->len = (u16) strlen(fcs_hba_attr->option_rom_ver); + memcpy(attr->value, fcs_hba_attr->option_rom_ver, attr->len); + /* variable fields need to be 4 byte aligned */ + attr->len = fc_roundup(attr->len, sizeof(u32)); + curr_ptr += sizeof(attr->type) + sizeof(attr->len) + attr->len; + len += attr->len; + count++; + attr->len = + bfa_os_htons(attr->len + sizeof(attr->type) + + sizeof(attr->len)); + } + + /* + * f/w Version = driver version + */ + attr = (struct fdmi_attr_s *) curr_ptr; + attr->type = bfa_os_htons(FDMI_HBA_ATTRIB_FW_VERSION); + attr->len = (u16) strlen(fcs_hba_attr->driver_version); + memcpy(attr->value, fcs_hba_attr->driver_version, attr->len); + /* variable fields need to be 4 byte aligned */ + attr->len = fc_roundup(attr->len, sizeof(u32)); + curr_ptr += sizeof(attr->type) + sizeof(attr->len) + attr->len; + len += attr->len; + count++; + attr->len = + bfa_os_htons(attr->len + sizeof(attr->type) + + sizeof(attr->len)); + + /* + * OS Name + */ + if (fcs_hba_attr->os_name[0] != '\0') { + attr = (struct fdmi_attr_s *) curr_ptr; + attr->type = bfa_os_htons(FDMI_HBA_ATTRIB_OS_NAME); + attr->len = (u16) strlen(fcs_hba_attr->os_name); + memcpy(attr->value, fcs_hba_attr->os_name, attr->len); + /* variable fields need to be 4 byte aligned */ + attr->len = fc_roundup(attr->len, sizeof(u32)); + curr_ptr += sizeof(attr->type) + sizeof(attr->len) + attr->len; + len += attr->len; + count++; + attr->len = + bfa_os_htons(attr->len + sizeof(attr->type) + + sizeof(attr->len)); + } + + /* + * MAX_CT_PAYLOAD + */ + attr = (struct fdmi_attr_s *) curr_ptr; + attr->type = bfa_os_htons(FDMI_HBA_ATTRIB_MAX_CT); + attr->len = sizeof(fcs_hba_attr->max_ct_pyld); + memcpy(attr->value, &fcs_hba_attr->max_ct_pyld, attr->len); + len += attr->len; + count++; + attr->len = + bfa_os_htons(attr->len + sizeof(attr->type) + + sizeof(attr->len)); + + /* + * Update size of payload + */ + len += ((sizeof(attr->type) + sizeof(attr->len)) * count); + + rhba->hba_attr_blk.attr_count = bfa_os_htonl(count); + return len; +} + +static void +bfa_fcs_port_fdmi_rhba_response(void *fcsarg, struct bfa_fcxp_s *fcxp, + void *cbarg, bfa_status_t req_status, + u32 rsp_len, u32 resid_len, + struct fchs_s *rsp_fchs) +{ + struct bfa_fcs_port_fdmi_s *fdmi = (struct bfa_fcs_port_fdmi_s *)cbarg; + struct bfa_fcs_port_s *port = fdmi->ms->port; + struct ct_hdr_s *cthdr = NULL; + + bfa_trc(port->fcs, port->port_cfg.pwwn); + + /* + * Sanity Checks + */ + if (req_status != BFA_STATUS_OK) { + bfa_trc(port->fcs, req_status); + bfa_sm_send_event(fdmi, FDMISM_EVENT_RSP_ERROR); + return; + } + + cthdr = (struct ct_hdr_s *) BFA_FCXP_RSP_PLD(fcxp); + cthdr->cmd_rsp_code = bfa_os_ntohs(cthdr->cmd_rsp_code); + + if (cthdr->cmd_rsp_code == CT_RSP_ACCEPT) { + bfa_sm_send_event(fdmi, FDMISM_EVENT_RSP_OK); + return; + } + + bfa_trc(port->fcs, cthdr->reason_code); + bfa_trc(port->fcs, cthdr->exp_code); + bfa_sm_send_event(fdmi, FDMISM_EVENT_RSP_ERROR); +} + +/** +* RPRT : Register Port + */ +static void +bfa_fcs_port_fdmi_send_rprt(void *fdmi_cbarg, struct bfa_fcxp_s *fcxp_alloced) +{ + struct bfa_fcs_port_fdmi_s *fdmi = fdmi_cbarg; + struct bfa_fcs_port_s *port = fdmi->ms->port; + struct fchs_s fchs; + u16 len, attr_len; + struct bfa_fcxp_s *fcxp; + u8 *pyld; + + bfa_trc(port->fcs, port->port_cfg.pwwn); + + fcxp = fcxp_alloced ? fcxp_alloced : bfa_fcs_fcxp_alloc(port->fcs); + if (!fcxp) { + bfa_fcxp_alloc_wait(port->fcs->bfa, &fdmi->fcxp_wqe, + bfa_fcs_port_fdmi_send_rprt, fdmi); + return; + } + fdmi->fcxp = fcxp; + + pyld = bfa_fcxp_get_reqbuf(fcxp); + bfa_os_memset(pyld, 0, FC_MAX_PDUSZ); + + len = fc_fdmi_reqhdr_build(&fchs, pyld, bfa_fcs_port_get_fcid(port), + FDMI_RPRT); + + attr_len = bfa_fcs_port_fdmi_build_rprt_pyld(fdmi, + (u8 *) ((struct ct_hdr_s *) pyld + 1)); + + bfa_fcxp_send(fcxp, NULL, port->fabric->vf_id, port->lp_tag, BFA_FALSE, + FC_CLASS_3, len + attr_len, &fchs, + bfa_fcs_port_fdmi_rprt_response, (void *)fdmi, + FC_MAX_PDUSZ, FC_RA_TOV); + + bfa_sm_send_event(fdmi, FDMISM_EVENT_RPRT_SENT); +} + +/** + * This routine builds Port Attribute Block that used in RPA, RPRT commands. + */ +static u16 +bfa_fcs_port_fdmi_build_portattr_block(struct bfa_fcs_port_fdmi_s *fdmi, + u8 *pyld) +{ + struct bfa_fcs_fdmi_port_attr_s fcs_port_attr; + struct fdmi_port_attr_s *port_attrib = (struct fdmi_port_attr_s *) pyld; + struct fdmi_attr_s *attr; + u8 *curr_ptr; + u16 len; + u8 count = 0; + + /* + * get port attributes + */ + bfa_fcs_fdmi_get_portattr(fdmi, &fcs_port_attr); + + len = sizeof(port_attrib->attr_count); + + /* + * fill out the invididual entries + */ + curr_ptr = (u8 *) &port_attrib->port_attr; + + /* + * FC4 Types + */ + attr = (struct fdmi_attr_s *) curr_ptr; + attr->type = bfa_os_htons(FDMI_PORT_ATTRIB_FC4_TYPES); + attr->len = sizeof(fcs_port_attr.supp_fc4_types); + memcpy(attr->value, fcs_port_attr.supp_fc4_types, attr->len); + curr_ptr += sizeof(attr->type) + sizeof(attr->len) + attr->len; + len += attr->len; + ++count; + attr->len = + bfa_os_htons(attr->len + sizeof(attr->type) + + sizeof(attr->len)); + + /* + * Supported Speed + */ + attr = (struct fdmi_attr_s *) curr_ptr; + attr->type = bfa_os_htons(FDMI_PORT_ATTRIB_SUPP_SPEED); + attr->len = sizeof(fcs_port_attr.supp_speed); + memcpy(attr->value, &fcs_port_attr.supp_speed, attr->len); + curr_ptr += sizeof(attr->type) + sizeof(attr->len) + attr->len; + len += attr->len; + ++count; + attr->len = + bfa_os_htons(attr->len + sizeof(attr->type) + + sizeof(attr->len)); + + /* + * current Port Speed + */ + attr = (struct fdmi_attr_s *) curr_ptr; + attr->type = bfa_os_htons(FDMI_PORT_ATTRIB_PORT_SPEED); + attr->len = sizeof(fcs_port_attr.curr_speed); + memcpy(attr->value, &fcs_port_attr.curr_speed, attr->len); + curr_ptr += sizeof(attr->type) + sizeof(attr->len) + attr->len; + len += attr->len; + ++count; + attr->len = + bfa_os_htons(attr->len + sizeof(attr->type) + + sizeof(attr->len)); + + /* + * max frame size + */ + attr = (struct fdmi_attr_s *) curr_ptr; + attr->type = bfa_os_htons(FDMI_PORT_ATTRIB_FRAME_SIZE); + attr->len = sizeof(fcs_port_attr.max_frm_size); + memcpy(attr->value, &fcs_port_attr.max_frm_size, attr->len); + curr_ptr += sizeof(attr->type) + sizeof(attr->len) + attr->len; + len += attr->len; + ++count; + attr->len = + bfa_os_htons(attr->len + sizeof(attr->type) + + sizeof(attr->len)); + + /* + * OS Device Name + */ + if (fcs_port_attr.os_device_name[0] != '\0') { + attr = (struct fdmi_attr_s *) curr_ptr; + attr->type = bfa_os_htons(FDMI_PORT_ATTRIB_DEV_NAME); + attr->len = (u16) strlen(fcs_port_attr.os_device_name); + memcpy(attr->value, fcs_port_attr.os_device_name, attr->len); + /* variable fields need to be 4 byte aligned */ + attr->len = fc_roundup(attr->len, sizeof(u32)); + curr_ptr += sizeof(attr->type) + sizeof(attr->len) + attr->len; + len += attr->len; + ++count; + attr->len = + bfa_os_htons(attr->len + sizeof(attr->type) + + sizeof(attr->len)); + + } + /* + * Host Name + */ + if (fcs_port_attr.host_name[0] != '\0') { + attr = (struct fdmi_attr_s *) curr_ptr; + attr->type = bfa_os_htons(FDMI_PORT_ATTRIB_HOST_NAME); + attr->len = (u16) strlen(fcs_port_attr.host_name); + memcpy(attr->value, fcs_port_attr.host_name, attr->len); + /* variable fields need to be 4 byte aligned */ + attr->len = fc_roundup(attr->len, sizeof(u32)); + curr_ptr += sizeof(attr->type) + sizeof(attr->len) + attr->len; + len += attr->len; + ++count; + attr->len = + bfa_os_htons(attr->len + sizeof(attr->type) + + sizeof(attr->len)); + + } + + /* + * Update size of payload + */ + port_attrib->attr_count = bfa_os_htonl(count); + len += ((sizeof(attr->type) + sizeof(attr->len)) * count); + return len; +} + +static u16 +bfa_fcs_port_fdmi_build_rprt_pyld(struct bfa_fcs_port_fdmi_s *fdmi, + u8 *pyld) +{ + struct bfa_fcs_port_s *port = fdmi->ms->port; + struct fdmi_rprt_s *rprt = (struct fdmi_rprt_s *) pyld; + u16 len; + + rprt->hba_id = bfa_fcs_port_get_pwwn(bfa_fcs_get_base_port(port->fcs)); + rprt->port_name = bfa_fcs_port_get_pwwn(port); + + len = bfa_fcs_port_fdmi_build_portattr_block(fdmi, + (u8 *) &rprt->port_attr_blk); + + len += sizeof(rprt->hba_id) + sizeof(rprt->port_name); + + return len; +} + +static void +bfa_fcs_port_fdmi_rprt_response(void *fcsarg, struct bfa_fcxp_s *fcxp, + void *cbarg, bfa_status_t req_status, + u32 rsp_len, u32 resid_len, + struct fchs_s *rsp_fchs) +{ + struct bfa_fcs_port_fdmi_s *fdmi = (struct bfa_fcs_port_fdmi_s *)cbarg; + struct bfa_fcs_port_s *port = fdmi->ms->port; + struct ct_hdr_s *cthdr = NULL; + + bfa_trc(port->fcs, port->port_cfg.pwwn); + + /* + * Sanity Checks + */ + if (req_status != BFA_STATUS_OK) { + bfa_trc(port->fcs, req_status); + bfa_sm_send_event(fdmi, FDMISM_EVENT_RSP_ERROR); + return; + } + + cthdr = (struct ct_hdr_s *) BFA_FCXP_RSP_PLD(fcxp); + cthdr->cmd_rsp_code = bfa_os_ntohs(cthdr->cmd_rsp_code); + + if (cthdr->cmd_rsp_code == CT_RSP_ACCEPT) { + bfa_sm_send_event(fdmi, FDMISM_EVENT_RSP_OK); + return; + } + + bfa_trc(port->fcs, cthdr->reason_code); + bfa_trc(port->fcs, cthdr->exp_code); + bfa_sm_send_event(fdmi, FDMISM_EVENT_RSP_ERROR); +} + +/** +* RPA : Register Port Attributes. + */ +static void +bfa_fcs_port_fdmi_send_rpa(void *fdmi_cbarg, struct bfa_fcxp_s *fcxp_alloced) +{ + struct bfa_fcs_port_fdmi_s *fdmi = fdmi_cbarg; + struct bfa_fcs_port_s *port = fdmi->ms->port; + struct fchs_s fchs; + u16 len, attr_len; + struct bfa_fcxp_s *fcxp; + u8 *pyld; + + bfa_trc(port->fcs, port->port_cfg.pwwn); + + fcxp = fcxp_alloced ? fcxp_alloced : bfa_fcs_fcxp_alloc(port->fcs); + if (!fcxp) { + bfa_fcxp_alloc_wait(port->fcs->bfa, &fdmi->fcxp_wqe, + bfa_fcs_port_fdmi_send_rpa, fdmi); + return; + } + fdmi->fcxp = fcxp; + + pyld = bfa_fcxp_get_reqbuf(fcxp); + bfa_os_memset(pyld, 0, FC_MAX_PDUSZ); + + len = fc_fdmi_reqhdr_build(&fchs, pyld, bfa_fcs_port_get_fcid(port), + FDMI_RPA); + + attr_len = bfa_fcs_port_fdmi_build_rpa_pyld(fdmi, + (u8 *) ((struct ct_hdr_s *) pyld + 1)); + + bfa_fcxp_send(fcxp, NULL, port->fabric->vf_id, port->lp_tag, BFA_FALSE, + FC_CLASS_3, len + attr_len, &fchs, + bfa_fcs_port_fdmi_rpa_response, (void *)fdmi, + FC_MAX_PDUSZ, FC_RA_TOV); + + bfa_sm_send_event(fdmi, FDMISM_EVENT_RPA_SENT); +} + +static u16 +bfa_fcs_port_fdmi_build_rpa_pyld(struct bfa_fcs_port_fdmi_s *fdmi, + u8 *pyld) +{ + struct bfa_fcs_port_s *port = fdmi->ms->port; + struct fdmi_rpa_s *rpa = (struct fdmi_rpa_s *) pyld; + u16 len; + + rpa->port_name = bfa_fcs_port_get_pwwn(port); + + len = bfa_fcs_port_fdmi_build_portattr_block(fdmi, + (u8 *) &rpa->port_attr_blk); + + len += sizeof(rpa->port_name); + + return len; +} + +static void +bfa_fcs_port_fdmi_rpa_response(void *fcsarg, struct bfa_fcxp_s *fcxp, + void *cbarg, bfa_status_t req_status, + u32 rsp_len, u32 resid_len, + struct fchs_s *rsp_fchs) +{ + struct bfa_fcs_port_fdmi_s *fdmi = (struct bfa_fcs_port_fdmi_s *)cbarg; + struct bfa_fcs_port_s *port = fdmi->ms->port; + struct ct_hdr_s *cthdr = NULL; + + bfa_trc(port->fcs, port->port_cfg.pwwn); + + /* + * Sanity Checks + */ + if (req_status != BFA_STATUS_OK) { + bfa_trc(port->fcs, req_status); + bfa_sm_send_event(fdmi, FDMISM_EVENT_RSP_ERROR); + return; + } + + cthdr = (struct ct_hdr_s *) BFA_FCXP_RSP_PLD(fcxp); + cthdr->cmd_rsp_code = bfa_os_ntohs(cthdr->cmd_rsp_code); + + if (cthdr->cmd_rsp_code == CT_RSP_ACCEPT) { + bfa_sm_send_event(fdmi, FDMISM_EVENT_RSP_OK); + return; + } + + bfa_trc(port->fcs, cthdr->reason_code); + bfa_trc(port->fcs, cthdr->exp_code); + bfa_sm_send_event(fdmi, FDMISM_EVENT_RSP_ERROR); +} + +static void +bfa_fcs_port_fdmi_timeout(void *arg) +{ + struct bfa_fcs_port_fdmi_s *fdmi = (struct bfa_fcs_port_fdmi_s *)arg; + + bfa_sm_send_event(fdmi, FDMISM_EVENT_TIMEOUT); +} + +void +bfa_fcs_fdmi_get_hbaattr(struct bfa_fcs_port_fdmi_s *fdmi, + struct bfa_fcs_fdmi_hba_attr_s *hba_attr) +{ + struct bfa_fcs_port_s *port = fdmi->ms->port; + struct bfa_fcs_driver_info_s *driver_info = &port->fcs->driver_info; + struct bfa_adapter_attr_s adapter_attr; + + bfa_os_memset(hba_attr, 0, sizeof(struct bfa_fcs_fdmi_hba_attr_s)); + bfa_os_memset(&adapter_attr, 0, sizeof(struct bfa_adapter_attr_s)); + + bfa_ioc_get_adapter_attr(&port->fcs->bfa->ioc, &adapter_attr); + + strncpy(hba_attr->manufacturer, adapter_attr.manufacturer, + sizeof(adapter_attr.manufacturer)); + + strncpy(hba_attr->serial_num, adapter_attr.serial_num, + sizeof(adapter_attr.serial_num)); + + strncpy(hba_attr->model, adapter_attr.model, sizeof(hba_attr->model)); + + strncpy(hba_attr->model_desc, adapter_attr.model_descr, + sizeof(hba_attr->model_desc)); + + strncpy(hba_attr->hw_version, adapter_attr.hw_ver, + sizeof(hba_attr->hw_version)); + + strncpy(hba_attr->driver_version, (char *)driver_info->version, + sizeof(hba_attr->driver_version)); + + strncpy(hba_attr->option_rom_ver, adapter_attr.optrom_ver, + sizeof(hba_attr->option_rom_ver)); + + strncpy(hba_attr->fw_version, adapter_attr.fw_ver, + sizeof(hba_attr->fw_version)); + + strncpy(hba_attr->os_name, driver_info->host_os_name, + sizeof(hba_attr->os_name)); + + /* + * If there is a patch level, append it to the os name along with a + * separator + */ + if (driver_info->host_os_patch[0] != '\0') { + strncat(hba_attr->os_name, BFA_FCS_PORT_SYMBNAME_SEPARATOR, + sizeof(BFA_FCS_PORT_SYMBNAME_SEPARATOR)); + strncat(hba_attr->os_name, driver_info->host_os_patch, + sizeof(driver_info->host_os_patch)); + } + + hba_attr->max_ct_pyld = bfa_os_htonl(FC_MAX_PDUSZ); + +} + +void +bfa_fcs_fdmi_get_portattr(struct bfa_fcs_port_fdmi_s *fdmi, + struct bfa_fcs_fdmi_port_attr_s *port_attr) +{ + struct bfa_fcs_port_s *port = fdmi->ms->port; + struct bfa_fcs_driver_info_s *driver_info = &port->fcs->driver_info; + struct bfa_pport_attr_s pport_attr; + + bfa_os_memset(port_attr, 0, sizeof(struct bfa_fcs_fdmi_port_attr_s)); + + /* + * get pport attributes from hal + */ + bfa_pport_get_attr(port->fcs->bfa, &pport_attr); + + /* + * get FC4 type Bitmask + */ + fc_get_fc4type_bitmask(FC_TYPE_FCP, port_attr->supp_fc4_types); + + /* + * Supported Speeds + */ + port_attr->supp_speed = bfa_os_htonl(BFA_FCS_FDMI_SUPORTED_SPEEDS); + + /* + * Current Speed + */ + port_attr->curr_speed = bfa_os_htonl(pport_attr.speed); + + /* + * Max PDU Size. + */ + port_attr->max_frm_size = bfa_os_htonl(FC_MAX_PDUSZ); + + /* + * OS device Name + */ + strncpy(port_attr->os_device_name, (char *)driver_info->os_device_name, + sizeof(port_attr->os_device_name)); + + /* + * Host name + */ + strncpy(port_attr->host_name, (char *)driver_info->host_machine_name, + sizeof(port_attr->host_name)); + +} + + +void +bfa_fcs_port_fdmi_init(struct bfa_fcs_port_ms_s *ms) +{ + struct bfa_fcs_port_fdmi_s *fdmi = &ms->fdmi; + + fdmi->ms = ms; + bfa_sm_set_state(fdmi, bfa_fcs_port_fdmi_sm_offline); +} + +void +bfa_fcs_port_fdmi_offline(struct bfa_fcs_port_ms_s *ms) +{ + struct bfa_fcs_port_fdmi_s *fdmi = &ms->fdmi; + + fdmi->ms = ms; + bfa_sm_send_event(fdmi, FDMISM_EVENT_PORT_OFFLINE); +} + +void +bfa_fcs_port_fdmi_online(struct bfa_fcs_port_ms_s *ms) +{ + struct bfa_fcs_port_fdmi_s *fdmi = &ms->fdmi; + + fdmi->ms = ms; + bfa_sm_send_event(fdmi, FDMISM_EVENT_PORT_ONLINE); +} diff --git a/drivers/scsi/bfa/include/aen/bfa_aen.h b/drivers/scsi/bfa/include/aen/bfa_aen.h new file mode 100644 index 000000000000..da8cac093d3d --- /dev/null +++ b/drivers/scsi/bfa/include/aen/bfa_aen.h @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2005-2009 Brocade Communications Systems, Inc. + * All rights reserved + * www.brocade.com + * + * Linux driver for Brocade Fibre Channel Host Bus Adapter. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (GPL) Version 2 as + * published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ +#ifndef __BFA_AEN_H__ +#define __BFA_AEN_H__ + +#include "defs/bfa_defs_aen.h" + +#define BFA_AEN_MAX_ENTRY 512 + +extern s32 bfa_aen_max_cfg_entry; +struct bfa_aen_s { + void *bfad; + s32 max_entry; + s32 write_index; + s32 read_index; + u32 bfad_num; + u32 seq_num; + void (*aen_cb_notify)(void *bfad); + void (*gettimeofday)(struct bfa_timeval_s *tv); + struct bfa_trc_mod_s *trcmod; + struct bfa_aen_entry_s list[BFA_AEN_MAX_ENTRY]; /* Must be the last */ +}; + + +/** + * Public APIs + */ +static inline void +bfa_aen_set_max_cfg_entry(int max_entry) +{ + bfa_aen_max_cfg_entry = max_entry; +} + +static inline s32 +bfa_aen_get_max_cfg_entry(void) +{ + return bfa_aen_max_cfg_entry; +} + +static inline s32 +bfa_aen_get_meminfo(void) +{ + return (sizeof(struct bfa_aen_entry_s) * bfa_aen_get_max_cfg_entry()); +} + +static inline s32 +bfa_aen_get_wi(struct bfa_aen_s *aen) +{ + return aen->write_index; +} + +static inline s32 +bfa_aen_get_ri(struct bfa_aen_s *aen) +{ + return aen->read_index; +} + +static inline s32 +bfa_aen_fetch_count(struct bfa_aen_s *aen, s32 read_index) +{ + return ((aen->write_index + aen->max_entry) - read_index) + % aen->max_entry; +} + +s32 bfa_aen_init(struct bfa_aen_s *aen, struct bfa_trc_mod_s *trcmod, + void *bfad, u32 inst_id, void (*aen_cb_notify)(void *), + void (*gettimeofday)(struct bfa_timeval_s *)); + +s32 bfa_aen_post(struct bfa_aen_s *aen, enum bfa_aen_category aen_category, + int aen_type, union bfa_aen_data_u *aen_data); + +s32 bfa_aen_fetch(struct bfa_aen_s *aen, struct bfa_aen_entry_s *aen_entry, + s32 entry_space, s32 rii, s32 *ri_arr, + s32 ri_arr_cnt); + +s32 bfa_aen_get_inst(struct bfa_aen_s *aen); + +#endif /* __BFA_AEN_H__ */ diff --git a/drivers/scsi/bfa/include/aen/bfa_aen_adapter.h b/drivers/scsi/bfa/include/aen/bfa_aen_adapter.h new file mode 100644 index 000000000000..260d3ea1cab3 --- /dev/null +++ b/drivers/scsi/bfa/include/aen/bfa_aen_adapter.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2005-2009 Brocade Communications Systems, Inc. + * All rights reserved + * www.brocade.com + * + * Linux driver for Brocade Fibre Channel Host Bus Adapter. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (GPL) Version 2 as + * published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +/* messages define for BFA_AEN_CAT_ADAPTER Module */ +#ifndef __bfa_aen_adapter_h__ +#define __bfa_aen_adapter_h__ + +#include <cs/bfa_log.h> +#include <defs/bfa_defs_aen.h> + +#define BFA_AEN_ADAPTER_ADD \ + BFA_LOG_CREATE_ID(BFA_AEN_CAT_ADAPTER, BFA_ADAPTER_AEN_ADD) +#define BFA_AEN_ADAPTER_REMOVE \ + BFA_LOG_CREATE_ID(BFA_AEN_CAT_ADAPTER, BFA_ADAPTER_AEN_REMOVE) + +#endif + diff --git a/drivers/scsi/bfa/include/aen/bfa_aen_audit.h b/drivers/scsi/bfa/include/aen/bfa_aen_audit.h new file mode 100644 index 000000000000..12cd7aab5d53 --- /dev/null +++ b/drivers/scsi/bfa/include/aen/bfa_aen_audit.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2005-2009 Brocade Communications Systems, Inc. + * All rights reserved + * www.brocade.com + * + * Linux driver for Brocade Fibre Channel Host Bus Adapter. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (GPL) Version 2 as + * published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +/* messages define for BFA_AEN_CAT_AUDIT Module */ +#ifndef __bfa_aen_audit_h__ +#define __bfa_aen_audit_h__ + +#include <cs/bfa_log.h> +#include <defs/bfa_defs_aen.h> + +#define BFA_AEN_AUDIT_AUTH_ENABLE \ + BFA_LOG_CREATE_ID(BFA_AEN_CAT_AUDIT, BFA_AUDIT_AEN_AUTH_ENABLE) +#define BFA_AEN_AUDIT_AUTH_DISABLE \ + BFA_LOG_CREATE_ID(BFA_AEN_CAT_AUDIT, BFA_AUDIT_AEN_AUTH_DISABLE) + +#endif + diff --git a/drivers/scsi/bfa/include/aen/bfa_aen_ethport.h b/drivers/scsi/bfa/include/aen/bfa_aen_ethport.h new file mode 100644 index 000000000000..507d0b58d149 --- /dev/null +++ b/drivers/scsi/bfa/include/aen/bfa_aen_ethport.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2005-2009 Brocade Communications Systems, Inc. + * All rights reserved + * www.brocade.com + * + * Linux driver for Brocade Fibre Channel Host Bus Adapter. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (GPL) Version 2 as + * published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +/* messages define for BFA_AEN_CAT_ETHPORT Module */ +#ifndef __bfa_aen_ethport_h__ +#define __bfa_aen_ethport_h__ + +#include <cs/bfa_log.h> +#include <defs/bfa_defs_aen.h> + +#define BFA_AEN_ETHPORT_LINKUP \ + BFA_LOG_CREATE_ID(BFA_AEN_CAT_ETHPORT, BFA_ETHPORT_AEN_LINKUP) +#define BFA_AEN_ETHPORT_LINKDOWN \ + BFA_LOG_CREATE_ID(BFA_AEN_CAT_ETHPORT, BFA_ETHPORT_AEN_LINKDOWN) +#define BFA_AEN_ETHPORT_ENABLE \ + BFA_LOG_CREATE_ID(BFA_AEN_CAT_ETHPORT, BFA_ETHPORT_AEN_ENABLE) +#define BFA_AEN_ETHPORT_DISABLE \ + BFA_LOG_CREATE_ID(BFA_AEN_CAT_ETHPORT, BFA_ETHPORT_AEN_DISABLE) + +#endif + diff --git a/drivers/scsi/bfa/include/aen/bfa_aen_ioc.h b/drivers/scsi/bfa/include/aen/bfa_aen_ioc.h new file mode 100644 index 000000000000..71378b446b69 --- /dev/null +++ b/drivers/scsi/bfa/include/aen/bfa_aen_ioc.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2005-2009 Brocade Communications Systems, Inc. + * All rights reserved + * www.brocade.com + * + * Linux driver for Brocade Fibre Channel Host Bus Adapter. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (GPL) Version 2 as + * published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +/* messages define for BFA_AEN_CAT_IOC Module */ +#ifndef __bfa_aen_ioc_h__ +#define __bfa_aen_ioc_h__ + +#include <cs/bfa_log.h> +#include <defs/bfa_defs_aen.h> + +#define BFA_AEN_IOC_HBGOOD \ + BFA_LOG_CREATE_ID(BFA_AEN_CAT_IOC, BFA_IOC_AEN_HBGOOD) +#define BFA_AEN_IOC_HBFAIL \ + BFA_LOG_CREATE_ID(BFA_AEN_CAT_IOC, BFA_IOC_AEN_HBFAIL) +#define BFA_AEN_IOC_ENABLE \ + BFA_LOG_CREATE_ID(BFA_AEN_CAT_IOC, BFA_IOC_AEN_ENABLE) +#define BFA_AEN_IOC_DISABLE \ + BFA_LOG_CREATE_ID(BFA_AEN_CAT_IOC, BFA_IOC_AEN_DISABLE) +#define BFA_AEN_IOC_FWMISMATCH \ + BFA_LOG_CREATE_ID(BFA_AEN_CAT_IOC, BFA_IOC_AEN_FWMISMATCH) + +#endif + diff --git a/drivers/scsi/bfa/include/aen/bfa_aen_itnim.h b/drivers/scsi/bfa/include/aen/bfa_aen_itnim.h new file mode 100644 index 000000000000..a7d8ddcfef99 --- /dev/null +++ b/drivers/scsi/bfa/include/aen/bfa_aen_itnim.h @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2005-2009 Brocade Communications Systems, Inc. + * All rights reserved + * www.brocade.com + * + * Linux driver for Brocade Fibre Channel Host Bus Adapter. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (GPL) Version 2 as + * published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +/* messages define for BFA_AEN_CAT_ITNIM Module */ +#ifndef __bfa_aen_itnim_h__ +#define __bfa_aen_itnim_h__ + +#include <cs/bfa_log.h> +#include <defs/bfa_defs_aen.h> + +#define BFA_AEN_ITNIM_ONLINE \ + BFA_LOG_CREATE_ID(BFA_AEN_CAT_ITNIM, BFA_ITNIM_AEN_ONLINE) +#define BFA_AEN_ITNIM_OFFLINE \ + BFA_LOG_CREATE_ID(BFA_AEN_CAT_ITNIM, BFA_ITNIM_AEN_OFFLINE) +#define BFA_AEN_ITNIM_DISCONNECT \ + BFA_LOG_CREATE_ID(BFA_AEN_CAT_ITNIM, BFA_ITNIM_AEN_DISCONNECT) + +#endif + diff --git a/drivers/scsi/bfa/include/aen/bfa_aen_lport.h b/drivers/scsi/bfa/include/aen/bfa_aen_lport.h new file mode 100644 index 000000000000..5a8ebb65193f --- /dev/null +++ b/drivers/scsi/bfa/include/aen/bfa_aen_lport.h @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2005-2009 Brocade Communications Systems, Inc. + * All rights reserved + * www.brocade.com + * + * Linux driver for Brocade Fibre Channel Host Bus Adapter. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (GPL) Version 2 as + * published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +/* messages define for BFA_AEN_CAT_LPORT Module */ +#ifndef __bfa_aen_lport_h__ +#define __bfa_aen_lport_h__ + +#include <cs/bfa_log.h> +#include <defs/bfa_defs_aen.h> + +#define BFA_AEN_LPORT_NEW \ + BFA_LOG_CREATE_ID(BFA_AEN_CAT_LPORT, BFA_LPORT_AEN_NEW) +#define BFA_AEN_LPORT_DELETE \ + BFA_LOG_CREATE_ID(BFA_AEN_CAT_LPORT, BFA_LPORT_AEN_DELETE) +#define BFA_AEN_LPORT_ONLINE \ + BFA_LOG_CREATE_ID(BFA_AEN_CAT_LPORT, BFA_LPORT_AEN_ONLINE) +#define BFA_AEN_LPORT_OFFLINE \ + BFA_LOG_CREATE_ID(BFA_AEN_CAT_LPORT, BFA_LPORT_AEN_OFFLINE) +#define BFA_AEN_LPORT_DISCONNECT \ + BFA_LOG_CREATE_ID(BFA_AEN_CAT_LPORT, BFA_LPORT_AEN_DISCONNECT) +#define BFA_AEN_LPORT_NEW_PROP \ + BFA_LOG_CREATE_ID(BFA_AEN_CAT_LPORT, BFA_LPORT_AEN_NEW_PROP) +#define BFA_AEN_LPORT_DELETE_PROP \ + BFA_LOG_CREATE_ID(BFA_AEN_CAT_LPORT, BFA_LPORT_AEN_DELETE_PROP) +#define BFA_AEN_LPORT_NEW_STANDARD \ + BFA_LOG_CREATE_ID(BFA_AEN_CAT_LPORT, BFA_LPORT_AEN_NEW_STANDARD) +#define BFA_AEN_LPORT_DELETE_STANDARD \ + BFA_LOG_CREATE_ID(BFA_AEN_CAT_LPORT, BFA_LPORT_AEN_DELETE_STANDARD) +#define BFA_AEN_LPORT_NPIV_DUP_WWN \ + BFA_LOG_CREATE_ID(BFA_AEN_CAT_LPORT, BFA_LPORT_AEN_NPIV_DUP_WWN) +#define BFA_AEN_LPORT_NPIV_FABRIC_MAX \ + BFA_LOG_CREATE_ID(BFA_AEN_CAT_LPORT, BFA_LPORT_AEN_NPIV_FABRIC_MAX) +#define BFA_AEN_LPORT_NPIV_UNKNOWN \ + BFA_LOG_CREATE_ID(BFA_AEN_CAT_LPORT, BFA_LPORT_AEN_NPIV_UNKNOWN) + +#endif + diff --git a/drivers/scsi/bfa/include/aen/bfa_aen_port.h b/drivers/scsi/bfa/include/aen/bfa_aen_port.h new file mode 100644 index 000000000000..9add905a622d --- /dev/null +++ b/drivers/scsi/bfa/include/aen/bfa_aen_port.h @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2005-2009 Brocade Communications Systems, Inc. + * All rights reserved + * www.brocade.com + * + * Linux driver for Brocade Fibre Channel Host Bus Adapter. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (GPL) Version 2 as + * published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +/* messages define for BFA_AEN_CAT_PORT Module */ +#ifndef __bfa_aen_port_h__ +#define __bfa_aen_port_h__ + +#include <cs/bfa_log.h> +#include <defs/bfa_defs_aen.h> + +#define BFA_AEN_PORT_ONLINE \ + BFA_LOG_CREATE_ID(BFA_AEN_CAT_PORT, BFA_PORT_AEN_ONLINE) +#define BFA_AEN_PORT_OFFLINE \ + BFA_LOG_CREATE_ID(BFA_AEN_CAT_PORT, BFA_PORT_AEN_OFFLINE) +#define BFA_AEN_PORT_RLIR \ + BFA_LOG_CREATE_ID(BFA_AEN_CAT_PORT, BFA_PORT_AEN_RLIR) +#define BFA_AEN_PORT_SFP_INSERT \ + BFA_LOG_CREATE_ID(BFA_AEN_CAT_PORT, BFA_PORT_AEN_SFP_INSERT) +#define BFA_AEN_PORT_SFP_REMOVE \ + BFA_LOG_CREATE_ID(BFA_AEN_CAT_PORT, BFA_PORT_AEN_SFP_REMOVE) +#define BFA_AEN_PORT_SFP_POM \ + BFA_LOG_CREATE_ID(BFA_AEN_CAT_PORT, BFA_PORT_AEN_SFP_POM) +#define BFA_AEN_PORT_ENABLE \ + BFA_LOG_CREATE_ID(BFA_AEN_CAT_PORT, BFA_PORT_AEN_ENABLE) +#define BFA_AEN_PORT_DISABLE \ + BFA_LOG_CREATE_ID(BFA_AEN_CAT_PORT, BFA_PORT_AEN_DISABLE) +#define BFA_AEN_PORT_AUTH_ON \ + BFA_LOG_CREATE_ID(BFA_AEN_CAT_PORT, BFA_PORT_AEN_AUTH_ON) +#define BFA_AEN_PORT_AUTH_OFF \ + BFA_LOG_CREATE_ID(BFA_AEN_CAT_PORT, BFA_PORT_AEN_AUTH_OFF) +#define BFA_AEN_PORT_DISCONNECT \ + BFA_LOG_CREATE_ID(BFA_AEN_CAT_PORT, BFA_PORT_AEN_DISCONNECT) +#define BFA_AEN_PORT_QOS_NEG \ + BFA_LOG_CREATE_ID(BFA_AEN_CAT_PORT, BFA_PORT_AEN_QOS_NEG) +#define BFA_AEN_PORT_FABRIC_NAME_CHANGE \ + BFA_LOG_CREATE_ID(BFA_AEN_CAT_PORT, BFA_PORT_AEN_FABRIC_NAME_CHANGE) +#define BFA_AEN_PORT_SFP_ACCESS_ERROR \ + BFA_LOG_CREATE_ID(BFA_AEN_CAT_PORT, BFA_PORT_AEN_SFP_ACCESS_ERROR) +#define BFA_AEN_PORT_SFP_UNSUPPORT \ + BFA_LOG_CREATE_ID(BFA_AEN_CAT_PORT, BFA_PORT_AEN_SFP_UNSUPPORT) + +#endif + diff --git a/drivers/scsi/bfa/include/aen/bfa_aen_rport.h b/drivers/scsi/bfa/include/aen/bfa_aen_rport.h new file mode 100644 index 000000000000..7e4be1fd5e15 --- /dev/null +++ b/drivers/scsi/bfa/include/aen/bfa_aen_rport.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2005-2009 Brocade Communications Systems, Inc. + * All rights reserved + * www.brocade.com + * + * Linux driver for Brocade Fibre Channel Host Bus Adapter. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (GPL) Version 2 as + * published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +/* messages define for BFA_AEN_CAT_RPORT Module */ +#ifndef __bfa_aen_rport_h__ +#define __bfa_aen_rport_h__ + +#include <cs/bfa_log.h> +#include <defs/bfa_defs_aen.h> + +#define BFA_AEN_RPORT_ONLINE \ + BFA_LOG_CREATE_ID(BFA_AEN_CAT_RPORT, BFA_RPORT_AEN_ONLINE) +#define BFA_AEN_RPORT_OFFLINE \ + BFA_LOG_CREATE_ID(BFA_AEN_CAT_RPORT, BFA_RPORT_AEN_OFFLINE) +#define BFA_AEN_RPORT_DISCONNECT \ + BFA_LOG_CREATE_ID(BFA_AEN_CAT_RPORT, BFA_RPORT_AEN_DISCONNECT) +#define BFA_AEN_RPORT_QOS_PRIO \ + BFA_LOG_CREATE_ID(BFA_AEN_CAT_RPORT, BFA_RPORT_AEN_QOS_PRIO) +#define BFA_AEN_RPORT_QOS_FLOWID \ + BFA_LOG_CREATE_ID(BFA_AEN_CAT_RPORT, BFA_RPORT_AEN_QOS_FLOWID) + +#endif + diff --git a/drivers/scsi/bfa/include/bfa.h b/drivers/scsi/bfa/include/bfa.h new file mode 100644 index 000000000000..64c1412c5703 --- /dev/null +++ b/drivers/scsi/bfa/include/bfa.h @@ -0,0 +1,177 @@ +/* + * Copyright (c) 2005-2009 Brocade Communications Systems, Inc. + * All rights reserved + * www.brocade.com + * + * Linux driver for Brocade Fibre Channel Host Bus Adapter. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (GPL) Version 2 as + * published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ +#ifndef __BFA_H__ +#define __BFA_H__ + +#include <bfa_os_inc.h> +#include <cs/bfa_debug.h> +#include <cs/bfa_q.h> +#include <cs/bfa_trc.h> +#include <cs/bfa_log.h> +#include <cs/bfa_plog.h> +#include <defs/bfa_defs_status.h> +#include <defs/bfa_defs_ioc.h> +#include <defs/bfa_defs_iocfc.h> +#include <aen/bfa_aen.h> +#include <bfi/bfi.h> + +struct bfa_s; +#include <bfa_intr_priv.h> + +struct bfa_pcidev_s; + +/** + * PCI devices supported by the current BFA + */ +struct bfa_pciid_s { + u16 device_id; + u16 vendor_id; +}; + +extern char bfa_version[]; + +/** + * BFA Power Mgmt Commands + */ +enum bfa_pm_cmd { + BFA_PM_CTL_D0 = 0, + BFA_PM_CTL_D1 = 1, + BFA_PM_CTL_D2 = 2, + BFA_PM_CTL_D3 = 3, +}; + +/** + * BFA memory resources + */ +enum bfa_mem_type { + BFA_MEM_TYPE_KVA = 1, /*! Kernel Virtual Memory *(non-dma-able) */ + BFA_MEM_TYPE_DMA = 2, /*! DMA-able memory */ + BFA_MEM_TYPE_MAX = BFA_MEM_TYPE_DMA, +}; + +struct bfa_mem_elem_s { + enum bfa_mem_type mem_type; /* see enum bfa_mem_type */ + u32 mem_len; /* Total Length in Bytes */ + u8 *kva; /* kernel virtual address */ + u64 dma; /* dma address if DMA memory */ + u8 *kva_curp; /* kva allocation cursor */ + u64 dma_curp; /* dma allocation cursor */ +}; + +struct bfa_meminfo_s { + struct bfa_mem_elem_s meminfo[BFA_MEM_TYPE_MAX]; +}; +#define bfa_meminfo_kva(_m) \ + (_m)->meminfo[BFA_MEM_TYPE_KVA - 1].kva_curp +#define bfa_meminfo_dma_virt(_m) \ + (_m)->meminfo[BFA_MEM_TYPE_DMA - 1].kva_curp +#define bfa_meminfo_dma_phys(_m) \ + (_m)->meminfo[BFA_MEM_TYPE_DMA - 1].dma_curp + +/** + * Generic Scatter Gather Element used by driver + */ +struct bfa_sge_s { + u32 sg_len; + void *sg_addr; +}; + +#define bfa_sge_to_be(__sge) do { \ + ((u32 *)(__sge))[0] = bfa_os_htonl(((u32 *)(__sge))[0]); \ + ((u32 *)(__sge))[1] = bfa_os_htonl(((u32 *)(__sge))[1]); \ + ((u32 *)(__sge))[2] = bfa_os_htonl(((u32 *)(__sge))[2]); \ +} while (0) + + +/* + * bfa stats interfaces + */ +#define bfa_stats(_mod, _stats) (_mod)->stats._stats ++ + +#define bfa_ioc_get_stats(__bfa, __ioc_stats) \ + bfa_ioc_fetch_stats(&(__bfa)->ioc, __ioc_stats) +#define bfa_ioc_clear_stats(__bfa) \ + bfa_ioc_clr_stats(&(__bfa)->ioc) + +/* + * bfa API functions + */ +void bfa_get_pciids(struct bfa_pciid_s **pciids, int *npciids); +void bfa_cfg_get_default(struct bfa_iocfc_cfg_s *cfg); +void bfa_cfg_get_min(struct bfa_iocfc_cfg_s *cfg); +void bfa_cfg_get_meminfo(struct bfa_iocfc_cfg_s *cfg, + struct bfa_meminfo_s *meminfo); +void bfa_attach(struct bfa_s *bfa, void *bfad, struct bfa_iocfc_cfg_s *cfg, + struct bfa_meminfo_s *meminfo, + struct bfa_pcidev_s *pcidev); +void bfa_init_trc(struct bfa_s *bfa, struct bfa_trc_mod_s *trcmod); +void bfa_init_log(struct bfa_s *bfa, struct bfa_log_mod_s *logmod); +void bfa_init_aen(struct bfa_s *bfa, struct bfa_aen_s *aen); +void bfa_init_plog(struct bfa_s *bfa, struct bfa_plog_s *plog); +void bfa_detach(struct bfa_s *bfa); +void bfa_init(struct bfa_s *bfa); +void bfa_start(struct bfa_s *bfa); +void bfa_stop(struct bfa_s *bfa); +void bfa_attach_fcs(struct bfa_s *bfa); +void bfa_cb_init(void *bfad, bfa_status_t status); +void bfa_cb_stop(void *bfad, bfa_status_t status); +void bfa_cb_updateq(void *bfad, bfa_status_t status); + +bfa_boolean_t bfa_intx(struct bfa_s *bfa); +void bfa_isr_enable(struct bfa_s *bfa); +void bfa_isr_disable(struct bfa_s *bfa); +void bfa_msix_getvecs(struct bfa_s *bfa, u32 *msix_vecs_bmap, + u32 *num_vecs, u32 *max_vec_bit); +#define bfa_msix(__bfa, __vec) (__bfa)->msix.handler[__vec](__bfa, __vec) + +void bfa_comp_deq(struct bfa_s *bfa, struct list_head *comp_q); +void bfa_comp_process(struct bfa_s *bfa, struct list_head *comp_q); +void bfa_comp_free(struct bfa_s *bfa, struct list_head *comp_q); + +typedef void (*bfa_cb_ioc_t) (void *cbarg, enum bfa_status status); +void bfa_iocfc_get_attr(struct bfa_s *bfa, struct bfa_iocfc_attr_s *attr); +bfa_status_t bfa_iocfc_get_stats(struct bfa_s *bfa, + struct bfa_iocfc_stats_s *stats, + bfa_cb_ioc_t cbfn, void *cbarg); +bfa_status_t bfa_iocfc_clear_stats(struct bfa_s *bfa, + bfa_cb_ioc_t cbfn, void *cbarg); +void bfa_get_attr(struct bfa_s *bfa, struct bfa_ioc_attr_s *ioc_attr); + +void bfa_adapter_get_attr(struct bfa_s *bfa, + struct bfa_adapter_attr_s *ad_attr); +u64 bfa_adapter_get_id(struct bfa_s *bfa); + +bfa_status_t bfa_iocfc_israttr_set(struct bfa_s *bfa, + struct bfa_iocfc_intr_attr_s *attr); + +void bfa_iocfc_enable(struct bfa_s *bfa); +void bfa_iocfc_disable(struct bfa_s *bfa); +void bfa_ioc_auto_recover(bfa_boolean_t auto_recover); +void bfa_cb_ioc_disable(void *bfad); +void bfa_timer_tick(struct bfa_s *bfa); +#define bfa_timer_start(_bfa, _timer, _timercb, _arg, _timeout) \ + bfa_timer_begin(&(_bfa)->timer_mod, _timer, _timercb, _arg, _timeout) + +/* + * BFA debug API functions + */ +bfa_status_t bfa_debug_fwtrc(struct bfa_s *bfa, void *trcdata, int *trclen); +bfa_status_t bfa_debug_fwsave(struct bfa_s *bfa, void *trcdata, int *trclen); + +#include "bfa_priv.h" + +#endif /* __BFA_H__ */ diff --git a/drivers/scsi/bfa/include/bfa_fcpim.h b/drivers/scsi/bfa/include/bfa_fcpim.h new file mode 100644 index 000000000000..04789795fa53 --- /dev/null +++ b/drivers/scsi/bfa/include/bfa_fcpim.h @@ -0,0 +1,159 @@ +/* + * Copyright (c) 2005-2009 Brocade Communications Systems, Inc. + * All rights reserved + * www.brocade.com + * + * Linux driver for Brocade Fibre Channel Host Bus Adapter. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (GPL) Version 2 as + * published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#ifndef __BFA_FCPIM_H__ +#define __BFA_FCPIM_H__ + +#include <bfa.h> +#include <bfa_svc.h> +#include <bfi/bfi_fcpim.h> +#include <defs/bfa_defs_fcpim.h> + +/* + * forward declarations + */ +struct bfa_itnim_s; +struct bfa_ioim_s; +struct bfa_tskim_s; +struct bfad_ioim_s; +struct bfad_tskim_s; + +/* + * bfa fcpim module API functions + */ +void bfa_fcpim_path_tov_set(struct bfa_s *bfa, u16 path_tov); +u16 bfa_fcpim_path_tov_get(struct bfa_s *bfa); +void bfa_fcpim_qdepth_set(struct bfa_s *bfa, u16 q_depth); +u16 bfa_fcpim_qdepth_get(struct bfa_s *bfa); +bfa_status_t bfa_fcpim_get_modstats(struct bfa_s *bfa, + struct bfa_fcpim_stats_s *modstats); +bfa_status_t bfa_fcpim_clr_modstats(struct bfa_s *bfa); + +/* + * bfa itnim API functions + */ +struct bfa_itnim_s *bfa_itnim_create(struct bfa_s *bfa, + struct bfa_rport_s *rport, void *itnim); +void bfa_itnim_delete(struct bfa_itnim_s *itnim); +void bfa_itnim_online(struct bfa_itnim_s *itnim, + bfa_boolean_t seq_rec); +void bfa_itnim_offline(struct bfa_itnim_s *itnim); +void bfa_itnim_get_stats(struct bfa_itnim_s *itnim, + struct bfa_itnim_hal_stats_s *stats); +void bfa_itnim_clear_stats(struct bfa_itnim_s *itnim); + + +/** + * BFA completion callback for bfa_itnim_online(). + * + * @param[in] itnim FCS or driver itnim instance + * + * return None + */ +void bfa_cb_itnim_online(void *itnim); + +/** + * BFA completion callback for bfa_itnim_offline(). + * + * @param[in] itnim FCS or driver itnim instance + * + * return None + */ +void bfa_cb_itnim_offline(void *itnim); +void bfa_cb_itnim_tov_begin(void *itnim); +void bfa_cb_itnim_tov(void *itnim); + +/** + * BFA notification to FCS/driver for second level error recovery. + * + * Atleast one I/O request has timedout and target is unresponsive to + * repeated abort requests. Second level error recovery should be initiated + * by starting implicit logout and recovery procedures. + * + * @param[in] itnim FCS or driver itnim instance + * + * return None + */ +void bfa_cb_itnim_sler(void *itnim); + +/* + * bfa ioim API functions + */ +struct bfa_ioim_s *bfa_ioim_alloc(struct bfa_s *bfa, + struct bfad_ioim_s *dio, + struct bfa_itnim_s *itnim, + u16 nsgles); + +void bfa_ioim_free(struct bfa_ioim_s *ioim); +void bfa_ioim_start(struct bfa_ioim_s *ioim); +void bfa_ioim_abort(struct bfa_ioim_s *ioim); +void bfa_ioim_delayed_comp(struct bfa_ioim_s *ioim, + bfa_boolean_t iotov); + + +/** + * I/O completion notification. + * + * @param[in] dio driver IO structure + * @param[in] io_status IO completion status + * @param[in] scsi_status SCSI status returned by target + * @param[in] sns_len SCSI sense length, 0 if none + * @param[in] sns_info SCSI sense data, if any + * @param[in] residue Residual length + * + * @return None + */ +void bfa_cb_ioim_done(void *bfad, struct bfad_ioim_s *dio, + enum bfi_ioim_status io_status, + u8 scsi_status, int sns_len, + u8 *sns_info, s32 residue); + +/** + * I/O good completion notification. + * + * @param[in] dio driver IO structure + * + * @return None + */ +void bfa_cb_ioim_good_comp(void *bfad, struct bfad_ioim_s *dio); + +/** + * I/O abort completion notification + * + * @param[in] dio driver IO that was aborted + * + * @return None + */ +void bfa_cb_ioim_abort(void *bfad, struct bfad_ioim_s *dio); +void bfa_cb_ioim_resfree(void *hcb_bfad); + +void bfa_cb_ioim_resfree(void *hcb_bfad); + +/* + * bfa tskim API functions + */ +struct bfa_tskim_s *bfa_tskim_alloc(struct bfa_s *bfa, + struct bfad_tskim_s *dtsk); +void bfa_tskim_free(struct bfa_tskim_s *tskim); +void bfa_tskim_start(struct bfa_tskim_s *tskim, + struct bfa_itnim_s *itnim, lun_t lun, + enum fcp_tm_cmnd tm, u8 t_secs); +void bfa_cb_tskim_done(void *bfad, struct bfad_tskim_s *dtsk, + enum bfi_tskim_status tsk_status); + +#endif /* __BFA_FCPIM_H__ */ + diff --git a/drivers/scsi/bfa/include/bfa_fcptm.h b/drivers/scsi/bfa/include/bfa_fcptm.h new file mode 100644 index 000000000000..5f5ffe0bb1bb --- /dev/null +++ b/drivers/scsi/bfa/include/bfa_fcptm.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2005-2009 Brocade Communications Systems, Inc. + * All rights reserved + * www.brocade.com + * + * Linux driver for Brocade Fibre Channel Host Bus Adapter. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (GPL) Version 2 as + * published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#ifndef __BFA_FCPTM_H__ +#define __BFA_FCPTM_H__ + +#include <bfa.h> +#include <bfa_svc.h> +#include <bfi/bfi_fcptm.h> + +/* + * forward declarations + */ +struct bfa_tin_s; +struct bfa_iotm_s; +struct bfa_tsktm_s; + +/* + * bfa fcptm module API functions + */ +void bfa_fcptm_path_tov_set(struct bfa_s *bfa, u16 path_tov); +u16 bfa_fcptm_path_tov_get(struct bfa_s *bfa); +void bfa_fcptm_qdepth_set(struct bfa_s *bfa, u16 q_depth); +u16 bfa_fcptm_qdepth_get(struct bfa_s *bfa); + +/* + * bfa tin API functions + */ +void bfa_tin_get_stats(struct bfa_tin_s *tin, struct bfa_tin_stats_s *stats); +void bfa_tin_clear_stats(struct bfa_tin_s *tin); + +#endif /* __BFA_FCPTM_H__ */ + diff --git a/drivers/scsi/bfa/include/bfa_svc.h b/drivers/scsi/bfa/include/bfa_svc.h new file mode 100644 index 000000000000..0c80b74f72ef --- /dev/null +++ b/drivers/scsi/bfa/include/bfa_svc.h @@ -0,0 +1,324 @@ +/* + * Copyright (c) 2005-2009 Brocade Communications Systems, Inc. + * All rights reserved + * www.brocade.com + * + * Linux driver for Brocade Fibre Channel Host Bus Adapter. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (GPL) Version 2 as + * published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ +#ifndef __BFA_SVC_H__ +#define __BFA_SVC_H__ + +/* + * forward declarations + */ +struct bfa_fcxp_s; + +#include <defs/bfa_defs_status.h> +#include <defs/bfa_defs_pport.h> +#include <defs/bfa_defs_rport.h> +#include <defs/bfa_defs_qos.h> +#include <cs/bfa_sm.h> +#include <bfa.h> + +/** + * BFA rport information. + */ +struct bfa_rport_info_s { + u16 max_frmsz; /* max rcv pdu size */ + u32 pid : 24, /* remote port ID */ + lp_tag : 8; + u32 local_pid : 24, /* local port ID */ + cisc : 8; /* CIRO supported */ + u8 fc_class; /* supported FC classes. enum fc_cos */ + u8 vf_en; /* virtual fabric enable */ + u16 vf_id; /* virtual fabric ID */ + enum bfa_pport_speed speed; /* Rport's current speed */ +}; + +/** + * BFA rport data structure + */ +struct bfa_rport_s { + struct list_head qe; /* queue element */ + bfa_sm_t sm; /* state machine */ + struct bfa_s *bfa; /* backpointer to BFA */ + void *rport_drv; /* fcs/driver rport object */ + u16 fw_handle; /* firmware rport handle */ + u16 rport_tag; /* BFA rport tag */ + struct bfa_rport_info_s rport_info; /* rport info from *fcs/driver */ + struct bfa_reqq_wait_s reqq_wait; /* to wait for room in reqq */ + struct bfa_cb_qe_s hcb_qe; /* BFA callback qelem */ + struct bfa_rport_hal_stats_s stats; /* BFA rport statistics */ + struct bfa_rport_qos_attr_s qos_attr; + union a { + bfa_status_t status; /* f/w status */ + void *fw_msg; /* QoS scn event */ + } event_arg; +}; +#define BFA_RPORT_FC_COS(_rport) ((_rport)->rport_info.fc_class) + +/** + * Send completion callback. + */ +typedef void (*bfa_cb_fcxp_send_t) (void *bfad_fcxp, struct bfa_fcxp_s *fcxp, + void *cbarg, enum bfa_status req_status, + u32 rsp_len, u32 resid_len, + struct fchs_s *rsp_fchs); + +/** + * BFA fcxp allocation (asynchronous) + */ +typedef void (*bfa_fcxp_alloc_cbfn_t) (void *cbarg, struct bfa_fcxp_s *fcxp); + +struct bfa_fcxp_wqe_s { + struct list_head qe; + bfa_fcxp_alloc_cbfn_t alloc_cbfn; + void *alloc_cbarg; +}; + +typedef u64 (*bfa_fcxp_get_sgaddr_t) (void *bfad_fcxp, int sgeid); +typedef u32 (*bfa_fcxp_get_sglen_t) (void *bfad_fcxp, int sgeid); + +#define BFA_UF_BUFSZ (2 * 1024 + 256) + +/** + * @todo private + */ +struct bfa_uf_buf_s { + u8 d[BFA_UF_BUFSZ]; +}; + + +struct bfa_uf_s { + struct list_head qe; /* queue element */ + struct bfa_s *bfa; /* bfa instance */ + u16 uf_tag; /* identifying tag f/w messages */ + u16 vf_id; + u16 src_rport_handle; + u16 rsvd; + u8 *data_ptr; + u16 data_len; /* actual receive length */ + u16 pb_len; /* posted buffer length */ + void *buf_kva; /* buffer virtual address */ + u64 buf_pa; /* buffer physical address */ + struct bfa_cb_qe_s hcb_qe; /* comp: BFA comp qelem */ + struct bfa_sge_s sges[BFI_SGE_INLINE_MAX]; +}; + +typedef void (*bfa_cb_pport_t) (void *cbarg, enum bfa_status status); + +/** + * bfa lport login/logout service interface + */ +struct bfa_lps_s { + struct list_head qe; /* queue element */ + struct bfa_s *bfa; /* parent bfa instance */ + bfa_sm_t sm; /* finite state machine */ + u8 lp_tag; /* lport tag */ + u8 reqq; /* lport request queue */ + u8 alpa; /* ALPA for loop topologies */ + u32 lp_pid; /* lport port ID */ + bfa_boolean_t fdisc; /* send FDISC instead of FLOGI*/ + bfa_boolean_t auth_en; /* enable authentication */ + bfa_boolean_t auth_req; /* authentication required */ + bfa_boolean_t npiv_en; /* NPIV is allowed by peer */ + bfa_boolean_t fport; /* attached peer is F_PORT */ + bfa_boolean_t brcd_switch;/* attached peer is brcd switch */ + bfa_status_t status; /* login status */ + u16 pdusz; /* max receive PDU size */ + u16 pr_bbcred; /* BB_CREDIT from peer */ + u8 lsrjt_rsn; /* LSRJT reason */ + u8 lsrjt_expl; /* LSRJT explanation */ + wwn_t pwwn; /* port wwn of lport */ + wwn_t nwwn; /* node wwn of lport */ + wwn_t pr_pwwn; /* port wwn of lport peer */ + wwn_t pr_nwwn; /* node wwn of lport peer */ + mac_t lp_mac; /* fpma/spma MAC for lport */ + mac_t fcf_mac; /* FCF MAC of lport */ + struct bfa_reqq_wait_s wqe; /* request wait queue element */ + void *uarg; /* user callback arg */ + struct bfa_cb_qe_s hcb_qe; /* comp: callback qelem */ + struct bfi_lps_login_rsp_s *loginrsp; + bfa_eproto_status_t ext_status; +}; + +/* + * bfa pport API functions + */ +bfa_status_t bfa_pport_enable(struct bfa_s *bfa); +bfa_status_t bfa_pport_disable(struct bfa_s *bfa); +bfa_status_t bfa_pport_cfg_speed(struct bfa_s *bfa, + enum bfa_pport_speed speed); +enum bfa_pport_speed bfa_pport_get_speed(struct bfa_s *bfa); +bfa_status_t bfa_pport_cfg_topology(struct bfa_s *bfa, + enum bfa_pport_topology topo); +enum bfa_pport_topology bfa_pport_get_topology(struct bfa_s *bfa); +bfa_status_t bfa_pport_cfg_hardalpa(struct bfa_s *bfa, u8 alpa); +bfa_boolean_t bfa_pport_get_hardalpa(struct bfa_s *bfa, u8 *alpa); +u8 bfa_pport_get_myalpa(struct bfa_s *bfa); +bfa_status_t bfa_pport_clr_hardalpa(struct bfa_s *bfa); +bfa_status_t bfa_pport_cfg_maxfrsize(struct bfa_s *bfa, u16 maxsize); +u16 bfa_pport_get_maxfrsize(struct bfa_s *bfa); +u32 bfa_pport_mypid(struct bfa_s *bfa); +u8 bfa_pport_get_rx_bbcredit(struct bfa_s *bfa); +bfa_status_t bfa_pport_trunk_enable(struct bfa_s *bfa, u8 bitmap); +bfa_status_t bfa_pport_trunk_disable(struct bfa_s *bfa); +bfa_boolean_t bfa_pport_trunk_query(struct bfa_s *bfa, u32 *bitmap); +void bfa_pport_get_attr(struct bfa_s *bfa, struct bfa_pport_attr_s *attr); +wwn_t bfa_pport_get_wwn(struct bfa_s *bfa, bfa_boolean_t node); +bfa_status_t bfa_pport_get_stats(struct bfa_s *bfa, + union bfa_pport_stats_u *stats, + bfa_cb_pport_t cbfn, void *cbarg); +bfa_status_t bfa_pport_clear_stats(struct bfa_s *bfa, bfa_cb_pport_t cbfn, + void *cbarg); +void bfa_pport_event_register(struct bfa_s *bfa, + void (*event_cbfn) (void *cbarg, + bfa_pport_event_t event), void *event_cbarg); +bfa_boolean_t bfa_pport_is_disabled(struct bfa_s *bfa); +void bfa_pport_cfg_qos(struct bfa_s *bfa, bfa_boolean_t on_off); +void bfa_pport_cfg_ratelim(struct bfa_s *bfa, bfa_boolean_t on_off); +bfa_status_t bfa_pport_cfg_ratelim_speed(struct bfa_s *bfa, + enum bfa_pport_speed speed); +enum bfa_pport_speed bfa_pport_get_ratelim_speed(struct bfa_s *bfa); + +void bfa_pport_set_tx_bbcredit(struct bfa_s *bfa, u16 tx_bbcredit); +void bfa_pport_busy(struct bfa_s *bfa, bfa_boolean_t status); +void bfa_pport_beacon(struct bfa_s *bfa, bfa_boolean_t beacon, + bfa_boolean_t link_e2e_beacon); +void bfa_cb_pport_event(void *cbarg, bfa_pport_event_t event); +void bfa_pport_qos_get_attr(struct bfa_s *bfa, struct bfa_qos_attr_s *qos_attr); +void bfa_pport_qos_get_vc_attr(struct bfa_s *bfa, + struct bfa_qos_vc_attr_s *qos_vc_attr); +bfa_status_t bfa_pport_get_qos_stats(struct bfa_s *bfa, + union bfa_pport_stats_u *stats, + bfa_cb_pport_t cbfn, void *cbarg); +bfa_status_t bfa_pport_clear_qos_stats(struct bfa_s *bfa, bfa_cb_pport_t cbfn, + void *cbarg); +bfa_boolean_t bfa_pport_is_ratelim(struct bfa_s *bfa); +bfa_boolean_t bfa_pport_is_linkup(struct bfa_s *bfa); + +/* + * bfa rport API functions + */ +struct bfa_rport_s *bfa_rport_create(struct bfa_s *bfa, void *rport_drv); +void bfa_rport_delete(struct bfa_rport_s *rport); +void bfa_rport_online(struct bfa_rport_s *rport, + struct bfa_rport_info_s *rport_info); +void bfa_rport_offline(struct bfa_rport_s *rport); +void bfa_rport_speed(struct bfa_rport_s *rport, enum bfa_pport_speed speed); +void bfa_rport_get_stats(struct bfa_rport_s *rport, + struct bfa_rport_hal_stats_s *stats); +void bfa_rport_clear_stats(struct bfa_rport_s *rport); +void bfa_cb_rport_online(void *rport); +void bfa_cb_rport_offline(void *rport); +void bfa_cb_rport_qos_scn_flowid(void *rport, + struct bfa_rport_qos_attr_s old_qos_attr, + struct bfa_rport_qos_attr_s new_qos_attr); +void bfa_cb_rport_qos_scn_prio(void *rport, + struct bfa_rport_qos_attr_s old_qos_attr, + struct bfa_rport_qos_attr_s new_qos_attr); +void bfa_rport_get_qos_attr(struct bfa_rport_s *rport, + struct bfa_rport_qos_attr_s *qos_attr); + +/* + * bfa fcxp API functions + */ +struct bfa_fcxp_s *bfa_fcxp_alloc(void *bfad_fcxp, struct bfa_s *bfa, + int nreq_sgles, int nrsp_sgles, + bfa_fcxp_get_sgaddr_t get_req_sga, + bfa_fcxp_get_sglen_t get_req_sglen, + bfa_fcxp_get_sgaddr_t get_rsp_sga, + bfa_fcxp_get_sglen_t get_rsp_sglen); +void bfa_fcxp_alloc_wait(struct bfa_s *bfa, struct bfa_fcxp_wqe_s *wqe, + bfa_fcxp_alloc_cbfn_t alloc_cbfn, void *cbarg); +void bfa_fcxp_walloc_cancel(struct bfa_s *bfa, + struct bfa_fcxp_wqe_s *wqe); +void bfa_fcxp_discard(struct bfa_fcxp_s *fcxp); + +void *bfa_fcxp_get_reqbuf(struct bfa_fcxp_s *fcxp); +void *bfa_fcxp_get_rspbuf(struct bfa_fcxp_s *fcxp); + +void bfa_fcxp_free(struct bfa_fcxp_s *fcxp); + +void bfa_fcxp_send(struct bfa_fcxp_s *fcxp, + struct bfa_rport_s *rport, u16 vf_id, u8 lp_tag, + bfa_boolean_t cts, enum fc_cos cos, + u32 reqlen, struct fchs_s *fchs, + bfa_cb_fcxp_send_t cbfn, + void *cbarg, + u32 rsp_maxlen, u8 rsp_timeout); +bfa_status_t bfa_fcxp_abort(struct bfa_fcxp_s *fcxp); +u32 bfa_fcxp_get_reqbufsz(struct bfa_fcxp_s *fcxp); +u32 bfa_fcxp_get_maxrsp(struct bfa_s *bfa); + +static inline void * +bfa_uf_get_frmbuf(struct bfa_uf_s *uf) +{ + return uf->data_ptr; +} + +static inline u16 +bfa_uf_get_frmlen(struct bfa_uf_s *uf) +{ + return uf->data_len; +} + +/** + * Callback prototype for unsolicited frame receive handler. + * + * @param[in] cbarg callback arg for receive handler + * @param[in] uf unsolicited frame descriptor + * + * @return None + */ +typedef void (*bfa_cb_uf_recv_t) (void *cbarg, struct bfa_uf_s *uf); + +/* + * bfa uf API functions + */ +void bfa_uf_recv_register(struct bfa_s *bfa, bfa_cb_uf_recv_t ufrecv, + void *cbarg); +void bfa_uf_free(struct bfa_uf_s *uf); + +/** + * bfa lport service api + */ + +struct bfa_lps_s *bfa_lps_alloc(struct bfa_s *bfa); +void bfa_lps_delete(struct bfa_lps_s *lps); +void bfa_lps_discard(struct bfa_lps_s *lps); +void bfa_lps_flogi(struct bfa_lps_s *lps, void *uarg, u8 alpa, u16 pdusz, + wwn_t pwwn, wwn_t nwwn, bfa_boolean_t auth_en); +void bfa_lps_fdisc(struct bfa_lps_s *lps, void *uarg, u16 pdusz, wwn_t pwwn, + wwn_t nwwn); +void bfa_lps_flogo(struct bfa_lps_s *lps); +void bfa_lps_fdisclogo(struct bfa_lps_s *lps); +u8 bfa_lps_get_tag(struct bfa_lps_s *lps); +bfa_boolean_t bfa_lps_is_npiv_en(struct bfa_lps_s *lps); +bfa_boolean_t bfa_lps_is_fport(struct bfa_lps_s *lps); +bfa_boolean_t bfa_lps_is_brcd_fabric(struct bfa_lps_s *lps); +bfa_boolean_t bfa_lps_is_authreq(struct bfa_lps_s *lps); +bfa_eproto_status_t bfa_lps_get_extstatus(struct bfa_lps_s *lps); +u32 bfa_lps_get_pid(struct bfa_lps_s *lps); +u8 bfa_lps_get_tag_from_pid(struct bfa_s *bfa, u32 pid); +u16 bfa_lps_get_peer_bbcredit(struct bfa_lps_s *lps); +wwn_t bfa_lps_get_peer_pwwn(struct bfa_lps_s *lps); +wwn_t bfa_lps_get_peer_nwwn(struct bfa_lps_s *lps); +u8 bfa_lps_get_lsrjt_rsn(struct bfa_lps_s *lps); +u8 bfa_lps_get_lsrjt_expl(struct bfa_lps_s *lps); +void bfa_cb_lps_flogi_comp(void *bfad, void *uarg, bfa_status_t status); +void bfa_cb_lps_flogo_comp(void *bfad, void *uarg); +void bfa_cb_lps_fdisc_comp(void *bfad, void *uarg, bfa_status_t status); +void bfa_cb_lps_fdisclogo_comp(void *bfad, void *uarg); + +#endif /* __BFA_SVC_H__ */ + diff --git a/drivers/scsi/bfa/include/bfa_timer.h b/drivers/scsi/bfa/include/bfa_timer.h new file mode 100644 index 000000000000..e407103fa565 --- /dev/null +++ b/drivers/scsi/bfa/include/bfa_timer.h @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2005-2009 Brocade Communications Systems, Inc. + * All rights reserved + * www.brocade.com + * + * Linux driver for Brocade Fibre Channel Host Bus Adapter. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (GPL) Version 2 as + * published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ +#ifndef __BFA_TIMER_H__ +#define __BFA_TIMER_H__ + +#include <bfa_os_inc.h> +#include <cs/bfa_q.h> + +struct bfa_s; + +typedef void (*bfa_timer_cbfn_t)(void *); + +/** + * BFA timer data structure + */ +struct bfa_timer_s { + struct list_head qe; + bfa_timer_cbfn_t timercb; + void *arg; + int timeout; /**< in millisecs. */ +}; + +/** + * Timer module structure + */ +struct bfa_timer_mod_s { + struct list_head timer_q; +}; + +#define BFA_TIMER_FREQ 500 /**< specified in millisecs */ + +void bfa_timer_beat(struct bfa_timer_mod_s *mod); +void bfa_timer_init(struct bfa_timer_mod_s *mod); +void bfa_timer_begin(struct bfa_timer_mod_s *mod, struct bfa_timer_s *timer, + bfa_timer_cbfn_t timercb, void *arg, + unsigned int timeout); +void bfa_timer_stop(struct bfa_timer_s *timer); + +#endif /* __BFA_TIMER_H__ */ diff --git a/drivers/scsi/bfa/include/bfi/bfi.h b/drivers/scsi/bfa/include/bfi/bfi.h new file mode 100644 index 000000000000..6cadfe0d4ba1 --- /dev/null +++ b/drivers/scsi/bfa/include/bfi/bfi.h @@ -0,0 +1,174 @@ +/* + * Copyright (c) 2005-2009 Brocade Communications Systems, Inc. + * All rights reserved + * www.brocade.com + * + * Linux driver for Brocade Fibre Channel Host Bus Adapter. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (GPL) Version 2 as + * published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#ifndef __BFI_H__ +#define __BFI_H__ + +#include <bfa_os_inc.h> +#include <defs/bfa_defs_status.h> + +#pragma pack(1) + +/** + * Msg header common to all msgs + */ +struct bfi_mhdr_s { + u8 msg_class; /* @ref bfi_mclass_t */ + u8 msg_id; /* msg opcode with in the class */ + union { + struct { + u8 rsvd; + u8 lpu_id; /* msg destination */ + } h2i; + u16 i2htok; /* token in msgs to host */ + } mtag; +}; + +#define bfi_h2i_set(_mh, _mc, _op, _lpuid) do { \ + (_mh).msg_class = (_mc); \ + (_mh).msg_id = (_op); \ + (_mh).mtag.h2i.lpu_id = (_lpuid); \ +} while (0) + +#define bfi_i2h_set(_mh, _mc, _op, _i2htok) do { \ + (_mh).msg_class = (_mc); \ + (_mh).msg_id = (_op); \ + (_mh).mtag.i2htok = (_i2htok); \ +} while (0) + +/* + * Message opcodes: 0-127 to firmware, 128-255 to host + */ +#define BFI_I2H_OPCODE_BASE 128 +#define BFA_I2HM(_x) ((_x) + BFI_I2H_OPCODE_BASE) + +/** + **************************************************************************** + * + * Scatter Gather Element and Page definition + * + **************************************************************************** + */ + +#define BFI_SGE_INLINE 1 +#define BFI_SGE_INLINE_MAX (BFI_SGE_INLINE + 1) + +/** + * SG Flags + */ +enum { + BFI_SGE_DATA = 0, /* data address, not last */ + BFI_SGE_DATA_CPL = 1, /* data addr, last in current page */ + BFI_SGE_DATA_LAST = 3, /* data address, last */ + BFI_SGE_LINK = 2, /* link address */ + BFI_SGE_PGDLEN = 2, /* cumulative data length for page */ +}; + +/** + * DMA addresses + */ +union bfi_addr_u { + struct { + u32 addr_lo; + u32 addr_hi; + } a32; +}; + +/** + * Scatter Gather Element + */ +struct bfi_sge_s { +#ifdef __BIGENDIAN + u32 flags : 2, + rsvd : 2, + sg_len : 28; +#else + u32 sg_len : 28, + rsvd : 2, + flags : 2; +#endif + union bfi_addr_u sga; +}; + +/** + * Scatter Gather Page + */ +#define BFI_SGPG_DATA_SGES 7 +#define BFI_SGPG_SGES_MAX (BFI_SGPG_DATA_SGES + 1) +#define BFI_SGPG_RSVD_WD_LEN 8 +struct bfi_sgpg_s { + struct bfi_sge_s sges[BFI_SGPG_SGES_MAX]; + u32 rsvd[BFI_SGPG_RSVD_WD_LEN]; +}; + +/* + * Large Message structure - 128 Bytes size Msgs + */ +#define BFI_LMSG_SZ 128 +#define BFI_LMSG_PL_WSZ \ + ((BFI_LMSG_SZ - sizeof(struct bfi_mhdr_s)) / 4) + +struct bfi_msg_s { + struct bfi_mhdr_s mhdr; + u32 pl[BFI_LMSG_PL_WSZ]; +}; + +/** + * Mailbox message structure + */ +#define BFI_MBMSG_SZ 7 +struct bfi_mbmsg_s { + struct bfi_mhdr_s mh; + u32 pl[BFI_MBMSG_SZ]; +}; + +/** + * Message Classes + */ +enum bfi_mclass { + BFI_MC_IOC = 1, /* IO Controller (IOC) */ + BFI_MC_DIAG = 2, /* Diagnostic Msgs */ + BFI_MC_FLASH = 3, /* Flash message class */ + BFI_MC_CEE = 4, + BFI_MC_FC_PORT = 5, /* FC port */ + BFI_MC_IOCFC = 6, /* FC - IO Controller (IOC) */ + BFI_MC_LL = 7, /* Link Layer */ + BFI_MC_UF = 8, /* Unsolicited frame receive */ + BFI_MC_FCXP = 9, /* FC Transport */ + BFI_MC_LPS = 10, /* lport fc login services */ + BFI_MC_RPORT = 11, /* Remote port */ + BFI_MC_ITNIM = 12, /* I-T nexus (Initiator mode) */ + BFI_MC_IOIM_READ = 13, /* read IO (Initiator mode) */ + BFI_MC_IOIM_WRITE = 14, /* write IO (Initiator mode) */ + BFI_MC_IOIM_IO = 15, /* IO (Initiator mode) */ + BFI_MC_IOIM = 16, /* IO (Initiator mode) */ + BFI_MC_IOIM_IOCOM = 17, /* good IO completion */ + BFI_MC_TSKIM = 18, /* Initiator Task management */ + BFI_MC_SBOOT = 19, /* SAN boot services */ + BFI_MC_IPFC = 20, /* IP over FC Msgs */ + BFI_MC_PORT = 21, /* Physical port */ + BFI_MC_MAX = 32 +}; + +#define BFI_IOC_MAX_CQS 4 +#define BFI_IOC_MAX_CQS_ASIC 8 +#define BFI_IOC_MSGLEN_MAX 32 /* 32 bytes */ + +#pragma pack() + +#endif /* __BFI_H__ */ + diff --git a/drivers/scsi/bfa/include/bfi/bfi_boot.h b/drivers/scsi/bfa/include/bfi/bfi_boot.h new file mode 100644 index 000000000000..5955afe7d108 --- /dev/null +++ b/drivers/scsi/bfa/include/bfi/bfi_boot.h @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2005-2009 Brocade Communications Systems, Inc. + * All rights reserved + * www.brocade.com + * + * Linux driver for Brocade Fibre Channel Host Bus Adapter. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (GPL) Version 2 as + * published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ +/* + * bfi_boot.h + */ + +#ifndef __BFI_BOOT_H__ +#define __BFI_BOOT_H__ + +#define BFI_BOOT_TYPE_OFF 8 +#define BFI_BOOT_PARAM_OFF 12 + +#define BFI_BOOT_TYPE_NORMAL 0 /* param is device id */ +#define BFI_BOOT_TYPE_FLASH 1 +#define BFI_BOOT_TYPE_MEMTEST 2 + +#define BFI_BOOT_MEMTEST_RES_ADDR 0x900 +#define BFI_BOOT_MEMTEST_RES_SIG 0xA0A1A2A3 + +#endif diff --git a/drivers/scsi/bfa/include/bfi/bfi_cbreg.h b/drivers/scsi/bfa/include/bfi/bfi_cbreg.h new file mode 100644 index 000000000000..b3bb52b565b1 --- /dev/null +++ b/drivers/scsi/bfa/include/bfi/bfi_cbreg.h @@ -0,0 +1,305 @@ +/* + * Copyright (c) 2005-2009 Brocade Communications Systems, Inc. + * All rights reserved + * www.brocade.com + * + * Linux driver for Brocade Fibre Channel Host Bus Adapter. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (GPL) Version 2 as + * published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +/* + * bfi_cbreg.h crossbow host block register definitions + * + * !!! Do not edit. Auto generated. !!! + */ + +#ifndef __BFI_CBREG_H__ +#define __BFI_CBREG_H__ + + +#define HOSTFN0_INT_STATUS 0x00014000 +#define __HOSTFN0_INT_STATUS_LVL_MK 0x00f00000 +#define __HOSTFN0_INT_STATUS_LVL_SH 20 +#define __HOSTFN0_INT_STATUS_LVL(_v) ((_v) << __HOSTFN0_INT_STATUS_LVL_SH) +#define __HOSTFN0_INT_STATUS_P 0x000fffff +#define HOSTFN0_INT_MSK 0x00014004 +#define HOST_PAGE_NUM_FN0 0x00014008 +#define __HOST_PAGE_NUM_FN 0x000001ff +#define HOSTFN1_INT_STATUS 0x00014100 +#define __HOSTFN1_INT_STAT_LVL_MK 0x00f00000 +#define __HOSTFN1_INT_STAT_LVL_SH 20 +#define __HOSTFN1_INT_STAT_LVL(_v) ((_v) << __HOSTFN1_INT_STAT_LVL_SH) +#define __HOSTFN1_INT_STAT_P 0x000fffff +#define HOSTFN1_INT_MSK 0x00014104 +#define HOST_PAGE_NUM_FN1 0x00014108 +#define APP_PLL_400_CTL_REG 0x00014204 +#define __P_400_PLL_LOCK 0x80000000 +#define __APP_PLL_400_SRAM_USE_100MHZ 0x00100000 +#define __APP_PLL_400_RESET_TIMER_MK 0x000e0000 +#define __APP_PLL_400_RESET_TIMER_SH 17 +#define __APP_PLL_400_RESET_TIMER(_v) ((_v) << __APP_PLL_400_RESET_TIMER_SH) +#define __APP_PLL_400_LOGIC_SOFT_RESET 0x00010000 +#define __APP_PLL_400_CNTLMT0_1_MK 0x0000c000 +#define __APP_PLL_400_CNTLMT0_1_SH 14 +#define __APP_PLL_400_CNTLMT0_1(_v) ((_v) << __APP_PLL_400_CNTLMT0_1_SH) +#define __APP_PLL_400_JITLMT0_1_MK 0x00003000 +#define __APP_PLL_400_JITLMT0_1_SH 12 +#define __APP_PLL_400_JITLMT0_1(_v) ((_v) << __APP_PLL_400_JITLMT0_1_SH) +#define __APP_PLL_400_HREF 0x00000800 +#define __APP_PLL_400_HDIV 0x00000400 +#define __APP_PLL_400_P0_1_MK 0x00000300 +#define __APP_PLL_400_P0_1_SH 8 +#define __APP_PLL_400_P0_1(_v) ((_v) << __APP_PLL_400_P0_1_SH) +#define __APP_PLL_400_Z0_2_MK 0x000000e0 +#define __APP_PLL_400_Z0_2_SH 5 +#define __APP_PLL_400_Z0_2(_v) ((_v) << __APP_PLL_400_Z0_2_SH) +#define __APP_PLL_400_RSEL200500 0x00000010 +#define __APP_PLL_400_ENARST 0x00000008 +#define __APP_PLL_400_BYPASS 0x00000004 +#define __APP_PLL_400_LRESETN 0x00000002 +#define __APP_PLL_400_ENABLE 0x00000001 +#define APP_PLL_212_CTL_REG 0x00014208 +#define __P_212_PLL_LOCK 0x80000000 +#define __APP_PLL_212_RESET_TIMER_MK 0x000e0000 +#define __APP_PLL_212_RESET_TIMER_SH 17 +#define __APP_PLL_212_RESET_TIMER(_v) ((_v) << __APP_PLL_212_RESET_TIMER_SH) +#define __APP_PLL_212_LOGIC_SOFT_RESET 0x00010000 +#define __APP_PLL_212_CNTLMT0_1_MK 0x0000c000 +#define __APP_PLL_212_CNTLMT0_1_SH 14 +#define __APP_PLL_212_CNTLMT0_1(_v) ((_v) << __APP_PLL_212_CNTLMT0_1_SH) +#define __APP_PLL_212_JITLMT0_1_MK 0x00003000 +#define __APP_PLL_212_JITLMT0_1_SH 12 +#define __APP_PLL_212_JITLMT0_1(_v) ((_v) << __APP_PLL_212_JITLMT0_1_SH) +#define __APP_PLL_212_HREF 0x00000800 +#define __APP_PLL_212_HDIV 0x00000400 +#define __APP_PLL_212_P0_1_MK 0x00000300 +#define __APP_PLL_212_P0_1_SH 8 +#define __APP_PLL_212_P0_1(_v) ((_v) << __APP_PLL_212_P0_1_SH) +#define __APP_PLL_212_Z0_2_MK 0x000000e0 +#define __APP_PLL_212_Z0_2_SH 5 +#define __APP_PLL_212_Z0_2(_v) ((_v) << __APP_PLL_212_Z0_2_SH) +#define __APP_PLL_212_RSEL200500 0x00000010 +#define __APP_PLL_212_ENARST 0x00000008 +#define __APP_PLL_212_BYPASS 0x00000004 +#define __APP_PLL_212_LRESETN 0x00000002 +#define __APP_PLL_212_ENABLE 0x00000001 +#define HOST_SEM0_REG 0x00014230 +#define __HOST_SEMAPHORE 0x00000001 +#define HOST_SEM1_REG 0x00014234 +#define HOST_SEM2_REG 0x00014238 +#define HOST_SEM3_REG 0x0001423c +#define HOST_SEM0_INFO_REG 0x00014240 +#define HOST_SEM1_INFO_REG 0x00014244 +#define HOST_SEM2_INFO_REG 0x00014248 +#define HOST_SEM3_INFO_REG 0x0001424c +#define HOSTFN0_LPU0_CMD_STAT 0x00019000 +#define __HOSTFN0_LPU0_MBOX_INFO_MK 0xfffffffe +#define __HOSTFN0_LPU0_MBOX_INFO_SH 1 +#define __HOSTFN0_LPU0_MBOX_INFO(_v) ((_v) << __HOSTFN0_LPU0_MBOX_INFO_SH) +#define __HOSTFN0_LPU0_MBOX_CMD_STATUS 0x00000001 +#define LPU0_HOSTFN0_CMD_STAT 0x00019008 +#define __LPU0_HOSTFN0_MBOX_INFO_MK 0xfffffffe +#define __LPU0_HOSTFN0_MBOX_INFO_SH 1 +#define __LPU0_HOSTFN0_MBOX_INFO(_v) ((_v) << __LPU0_HOSTFN0_MBOX_INFO_SH) +#define __LPU0_HOSTFN0_MBOX_CMD_STATUS 0x00000001 +#define HOSTFN1_LPU1_CMD_STAT 0x00019014 +#define __HOSTFN1_LPU1_MBOX_INFO_MK 0xfffffffe +#define __HOSTFN1_LPU1_MBOX_INFO_SH 1 +#define __HOSTFN1_LPU1_MBOX_INFO(_v) ((_v) << __HOSTFN1_LPU1_MBOX_INFO_SH) +#define __HOSTFN1_LPU1_MBOX_CMD_STATUS 0x00000001 +#define LPU1_HOSTFN1_CMD_STAT 0x0001901c +#define __LPU1_HOSTFN1_MBOX_INFO_MK 0xfffffffe +#define __LPU1_HOSTFN1_MBOX_INFO_SH 1 +#define __LPU1_HOSTFN1_MBOX_INFO(_v) ((_v) << __LPU1_HOSTFN1_MBOX_INFO_SH) +#define __LPU1_HOSTFN1_MBOX_CMD_STATUS 0x00000001 +#define CPE_Q0_DEPTH 0x00010014 +#define CPE_Q0_PI 0x0001001c +#define CPE_Q0_CI 0x00010020 +#define CPE_Q1_DEPTH 0x00010034 +#define CPE_Q1_PI 0x0001003c +#define CPE_Q1_CI 0x00010040 +#define CPE_Q2_DEPTH 0x00010054 +#define CPE_Q2_PI 0x0001005c +#define CPE_Q2_CI 0x00010060 +#define CPE_Q3_DEPTH 0x00010074 +#define CPE_Q3_PI 0x0001007c +#define CPE_Q3_CI 0x00010080 +#define CPE_Q4_DEPTH 0x00010094 +#define CPE_Q4_PI 0x0001009c +#define CPE_Q4_CI 0x000100a0 +#define CPE_Q5_DEPTH 0x000100b4 +#define CPE_Q5_PI 0x000100bc +#define CPE_Q5_CI 0x000100c0 +#define CPE_Q6_DEPTH 0x000100d4 +#define CPE_Q6_PI 0x000100dc +#define CPE_Q6_CI 0x000100e0 +#define CPE_Q7_DEPTH 0x000100f4 +#define CPE_Q7_PI 0x000100fc +#define CPE_Q7_CI 0x00010100 +#define RME_Q0_DEPTH 0x00011014 +#define RME_Q0_PI 0x0001101c +#define RME_Q0_CI 0x00011020 +#define RME_Q1_DEPTH 0x00011034 +#define RME_Q1_PI 0x0001103c +#define RME_Q1_CI 0x00011040 +#define RME_Q2_DEPTH 0x00011054 +#define RME_Q2_PI 0x0001105c +#define RME_Q2_CI 0x00011060 +#define RME_Q3_DEPTH 0x00011074 +#define RME_Q3_PI 0x0001107c +#define RME_Q3_CI 0x00011080 +#define RME_Q4_DEPTH 0x00011094 +#define RME_Q4_PI 0x0001109c +#define RME_Q4_CI 0x000110a0 +#define RME_Q5_DEPTH 0x000110b4 +#define RME_Q5_PI 0x000110bc +#define RME_Q5_CI 0x000110c0 +#define RME_Q6_DEPTH 0x000110d4 +#define RME_Q6_PI 0x000110dc +#define RME_Q6_CI 0x000110e0 +#define RME_Q7_DEPTH 0x000110f4 +#define RME_Q7_PI 0x000110fc +#define RME_Q7_CI 0x00011100 +#define PSS_CTL_REG 0x00018800 +#define __PSS_I2C_CLK_DIV_MK 0x00030000 +#define __PSS_I2C_CLK_DIV_SH 16 +#define __PSS_I2C_CLK_DIV(_v) ((_v) << __PSS_I2C_CLK_DIV_SH) +#define __PSS_LMEM_INIT_DONE 0x00001000 +#define __PSS_LMEM_RESET 0x00000200 +#define __PSS_LMEM_INIT_EN 0x00000100 +#define __PSS_LPU1_RESET 0x00000002 +#define __PSS_LPU0_RESET 0x00000001 + + +/* + * These definitions are either in error/missing in spec. Its auto-generated + * from hard coded values in regparse.pl. + */ +#define __EMPHPOST_AT_4G_MK_FIX 0x0000001c +#define __EMPHPOST_AT_4G_SH_FIX 0x00000002 +#define __EMPHPRE_AT_4G_FIX 0x00000003 +#define __SFP_TXRATE_EN_FIX 0x00000100 +#define __SFP_RXRATE_EN_FIX 0x00000080 + + +/* + * These register definitions are auto-generated from hard coded values + * in regparse.pl. + */ +#define HOSTFN0_LPU_MBOX0_0 0x00019200 +#define HOSTFN1_LPU_MBOX0_8 0x00019260 +#define LPU_HOSTFN0_MBOX0_0 0x00019280 +#define LPU_HOSTFN1_MBOX0_8 0x000192e0 + + +/* + * These register mapping definitions are auto-generated from mapping tables + * in regparse.pl. + */ +#define BFA_IOC0_HBEAT_REG HOST_SEM0_INFO_REG +#define BFA_IOC0_STATE_REG HOST_SEM1_INFO_REG +#define BFA_IOC1_HBEAT_REG HOST_SEM2_INFO_REG +#define BFA_IOC1_STATE_REG HOST_SEM3_INFO_REG +#define BFA_FW_USE_COUNT HOST_SEM4_INFO_REG + +#define CPE_Q_DEPTH(__n) \ + (CPE_Q0_DEPTH + (__n) * (CPE_Q1_DEPTH - CPE_Q0_DEPTH)) +#define CPE_Q_PI(__n) \ + (CPE_Q0_PI + (__n) * (CPE_Q1_PI - CPE_Q0_PI)) +#define CPE_Q_CI(__n) \ + (CPE_Q0_CI + (__n) * (CPE_Q1_CI - CPE_Q0_CI)) +#define RME_Q_DEPTH(__n) \ + (RME_Q0_DEPTH + (__n) * (RME_Q1_DEPTH - RME_Q0_DEPTH)) +#define RME_Q_PI(__n) \ + (RME_Q0_PI + (__n) * (RME_Q1_PI - RME_Q0_PI)) +#define RME_Q_CI(__n) \ + (RME_Q0_CI + (__n) * (RME_Q1_CI - RME_Q0_CI)) + +#define CPE_Q_NUM(__fn, __q) (((__fn) << 2) + (__q)) +#define RME_Q_NUM(__fn, __q) (((__fn) << 2) + (__q)) +#define CPE_Q_MASK(__q) ((__q) & 0x3) +#define RME_Q_MASK(__q) ((__q) & 0x3) + + +/* + * PCI MSI-X vector defines + */ +enum { + BFA_MSIX_CPE_Q0 = 0, + BFA_MSIX_CPE_Q1 = 1, + BFA_MSIX_CPE_Q2 = 2, + BFA_MSIX_CPE_Q3 = 3, + BFA_MSIX_CPE_Q4 = 4, + BFA_MSIX_CPE_Q5 = 5, + BFA_MSIX_CPE_Q6 = 6, + BFA_MSIX_CPE_Q7 = 7, + BFA_MSIX_RME_Q0 = 8, + BFA_MSIX_RME_Q1 = 9, + BFA_MSIX_RME_Q2 = 10, + BFA_MSIX_RME_Q3 = 11, + BFA_MSIX_RME_Q4 = 12, + BFA_MSIX_RME_Q5 = 13, + BFA_MSIX_RME_Q6 = 14, + BFA_MSIX_RME_Q7 = 15, + BFA_MSIX_ERR_EMC = 16, + BFA_MSIX_ERR_LPU0 = 17, + BFA_MSIX_ERR_LPU1 = 18, + BFA_MSIX_ERR_PSS = 19, + BFA_MSIX_MBOX_LPU0 = 20, + BFA_MSIX_MBOX_LPU1 = 21, + BFA_MSIX_CB_MAX = 22, +}; + +/* + * And corresponding host interrupt status bit field defines + */ +#define __HFN_INT_CPE_Q0 0x00000001U +#define __HFN_INT_CPE_Q1 0x00000002U +#define __HFN_INT_CPE_Q2 0x00000004U +#define __HFN_INT_CPE_Q3 0x00000008U +#define __HFN_INT_CPE_Q4 0x00000010U +#define __HFN_INT_CPE_Q5 0x00000020U +#define __HFN_INT_CPE_Q6 0x00000040U +#define __HFN_INT_CPE_Q7 0x00000080U +#define __HFN_INT_RME_Q0 0x00000100U +#define __HFN_INT_RME_Q1 0x00000200U +#define __HFN_INT_RME_Q2 0x00000400U +#define __HFN_INT_RME_Q3 0x00000800U +#define __HFN_INT_RME_Q4 0x00001000U +#define __HFN_INT_RME_Q5 0x00002000U +#define __HFN_INT_RME_Q6 0x00004000U +#define __HFN_INT_RME_Q7 0x00008000U +#define __HFN_INT_ERR_EMC 0x00010000U +#define __HFN_INT_ERR_LPU0 0x00020000U +#define __HFN_INT_ERR_LPU1 0x00040000U +#define __HFN_INT_ERR_PSS 0x00080000U +#define __HFN_INT_MBOX_LPU0 0x00100000U +#define __HFN_INT_MBOX_LPU1 0x00200000U +#define __HFN_INT_MBOX1_LPU0 0x00400000U +#define __HFN_INT_MBOX1_LPU1 0x00800000U +#define __HFN_INT_CPE_MASK 0x000000ffU +#define __HFN_INT_RME_MASK 0x0000ff00U + + +/* + * crossbow memory map. + */ +#define PSS_SMEM_PAGE_START 0x8000 +#define PSS_SMEM_PGNUM(_pg0, _ma) ((_pg0) + ((_ma) >> 15)) +#define PSS_SMEM_PGOFF(_ma) ((_ma) & 0x7fff) + +/* + * End of crossbow memory map + */ + + +#endif /* __BFI_CBREG_H__ */ + diff --git a/drivers/scsi/bfa/include/bfi/bfi_cee.h b/drivers/scsi/bfa/include/bfi/bfi_cee.h new file mode 100644 index 000000000000..0970596583ea --- /dev/null +++ b/drivers/scsi/bfa/include/bfi/bfi_cee.h @@ -0,0 +1,119 @@ +/* + * Copyright (c) 2005-2009 Brocade Communications Systems, Inc. + * All rights reserved + * www.brocade.com + * + * Linux driver for Brocade Fibre Channel Host Bus Adapter. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (GPL) Version 2 as + * published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ +/** + * Copyright (c) 2006-2009 Brocade Communications Systems, Inc. + * All rights reserved. + * + * bfi_dcbx.h BFI Interface (Mailbox commands and related structures) + * between host driver and DCBX/LLDP firmware module. + * +**/ + +#ifndef __BFI_CEE_H__ +#define __BFI_CEE_H__ + +#include <bfi/bfi.h> + +#pragma pack(1) + + +enum bfi_cee_h2i_msgs_e { + BFI_CEE_H2I_GET_CFG_REQ = 1, + BFI_CEE_H2I_RESET_STATS = 2, + BFI_CEE_H2I_GET_STATS_REQ = 3, +}; + + +enum bfi_cee_i2h_msgs_e { + BFI_CEE_I2H_GET_CFG_RSP = BFA_I2HM(1), + BFI_CEE_I2H_RESET_STATS_RSP = BFA_I2HM(2), + BFI_CEE_I2H_GET_STATS_RSP = BFA_I2HM(3), +}; + + +/* Data structures */ + +/* + * BFI_CEE_H2I_RESET_STATS + */ +struct bfi_lldp_reset_stats_s { + struct bfi_mhdr_s mh; +}; + +/* + * BFI_CEE_H2I_RESET_STATS + */ +struct bfi_cee_reset_stats_s { + struct bfi_mhdr_s mh; +}; + +/* + * BFI_CEE_H2I_GET_CFG_REQ + */ +struct bfi_cee_get_req_s { + struct bfi_mhdr_s mh; + union bfi_addr_u dma_addr; +}; + + +/* + * BFI_CEE_I2H_GET_CFG_RSP + */ +struct bfi_cee_get_rsp_s { + struct bfi_mhdr_s mh; + u8 cmd_status; + u8 rsvd[3]; +}; + +/* + * BFI_CEE_H2I_GET_STATS_REQ + */ +struct bfi_cee_stats_req_s { + struct bfi_mhdr_s mh; + union bfi_addr_u dma_addr; +}; + + +/* + * BFI_CEE_I2H_GET_STATS_RSP + */ +struct bfi_cee_stats_rsp_s { + struct bfi_mhdr_s mh; + u8 cmd_status; + u8 rsvd[3]; +}; + + + +union bfi_cee_h2i_msg_u { + struct bfi_mhdr_s mh; + struct bfi_cee_get_req_s get_req; + struct bfi_cee_stats_req_s stats_req; +}; + + +union bfi_cee_i2h_msg_u { + struct bfi_mhdr_s mh; + struct bfi_cee_get_rsp_s get_rsp; + struct bfi_cee_stats_rsp_s stats_rsp; +}; + +#pragma pack() + + +#endif /* __BFI_CEE_H__ */ + diff --git a/drivers/scsi/bfa/include/bfi/bfi_ctreg.h b/drivers/scsi/bfa/include/bfi/bfi_ctreg.h new file mode 100644 index 000000000000..d3caa58c0a0a --- /dev/null +++ b/drivers/scsi/bfa/include/bfi/bfi_ctreg.h @@ -0,0 +1,611 @@ +/* + * Copyright (c) 2005-2009 Brocade Communications Systems, Inc. + * All rights reserved + * www.brocade.com + * + * Linux driver for Brocade Fibre Channel Host Bus Adapter. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (GPL) Version 2 as + * published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +/* + * bfi_ctreg.h catapult host block register definitions + * + * !!! Do not edit. Auto generated. !!! + */ + +#ifndef __BFI_CTREG_H__ +#define __BFI_CTREG_H__ + + +#define HOSTFN0_LPU_MBOX0_0 0x00019200 +#define HOSTFN1_LPU_MBOX0_8 0x00019260 +#define LPU_HOSTFN0_MBOX0_0 0x00019280 +#define LPU_HOSTFN1_MBOX0_8 0x000192e0 +#define HOSTFN2_LPU_MBOX0_0 0x00019400 +#define HOSTFN3_LPU_MBOX0_8 0x00019460 +#define LPU_HOSTFN2_MBOX0_0 0x00019480 +#define LPU_HOSTFN3_MBOX0_8 0x000194e0 +#define HOSTFN0_INT_STATUS 0x00014000 +#define __HOSTFN0_HALT_OCCURRED 0x01000000 +#define __HOSTFN0_INT_STATUS_LVL_MK 0x00f00000 +#define __HOSTFN0_INT_STATUS_LVL_SH 20 +#define __HOSTFN0_INT_STATUS_LVL(_v) ((_v) << __HOSTFN0_INT_STATUS_LVL_SH) +#define __HOSTFN0_INT_STATUS_P_MK 0x000f0000 +#define __HOSTFN0_INT_STATUS_P_SH 16 +#define __HOSTFN0_INT_STATUS_P(_v) ((_v) << __HOSTFN0_INT_STATUS_P_SH) +#define __HOSTFN0_INT_STATUS_F 0x0000ffff +#define HOSTFN0_INT_MSK 0x00014004 +#define HOST_PAGE_NUM_FN0 0x00014008 +#define __HOST_PAGE_NUM_FN 0x000001ff +#define HOST_MSIX_ERR_INDEX_FN0 0x0001400c +#define __MSIX_ERR_INDEX_FN 0x000001ff +#define HOSTFN1_INT_STATUS 0x00014100 +#define __HOSTFN1_HALT_OCCURRED 0x01000000 +#define __HOSTFN1_INT_STATUS_LVL_MK 0x00f00000 +#define __HOSTFN1_INT_STATUS_LVL_SH 20 +#define __HOSTFN1_INT_STATUS_LVL(_v) ((_v) << __HOSTFN1_INT_STATUS_LVL_SH) +#define __HOSTFN1_INT_STATUS_P_MK 0x000f0000 +#define __HOSTFN1_INT_STATUS_P_SH 16 +#define __HOSTFN1_INT_STATUS_P(_v) ((_v) << __HOSTFN1_INT_STATUS_P_SH) +#define __HOSTFN1_INT_STATUS_F 0x0000ffff +#define HOSTFN1_INT_MSK 0x00014104 +#define HOST_PAGE_NUM_FN1 0x00014108 +#define HOST_MSIX_ERR_INDEX_FN1 0x0001410c +#define APP_PLL_425_CTL_REG 0x00014204 +#define __P_425_PLL_LOCK 0x80000000 +#define __APP_PLL_425_SRAM_USE_100MHZ 0x00100000 +#define __APP_PLL_425_RESET_TIMER_MK 0x000e0000 +#define __APP_PLL_425_RESET_TIMER_SH 17 +#define __APP_PLL_425_RESET_TIMER(_v) ((_v) << __APP_PLL_425_RESET_TIMER_SH) +#define __APP_PLL_425_LOGIC_SOFT_RESET 0x00010000 +#define __APP_PLL_425_CNTLMT0_1_MK 0x0000c000 +#define __APP_PLL_425_CNTLMT0_1_SH 14 +#define __APP_PLL_425_CNTLMT0_1(_v) ((_v) << __APP_PLL_425_CNTLMT0_1_SH) +#define __APP_PLL_425_JITLMT0_1_MK 0x00003000 +#define __APP_PLL_425_JITLMT0_1_SH 12 +#define __APP_PLL_425_JITLMT0_1(_v) ((_v) << __APP_PLL_425_JITLMT0_1_SH) +#define __APP_PLL_425_HREF 0x00000800 +#define __APP_PLL_425_HDIV 0x00000400 +#define __APP_PLL_425_P0_1_MK 0x00000300 +#define __APP_PLL_425_P0_1_SH 8 +#define __APP_PLL_425_P0_1(_v) ((_v) << __APP_PLL_425_P0_1_SH) +#define __APP_PLL_425_Z0_2_MK 0x000000e0 +#define __APP_PLL_425_Z0_2_SH 5 +#define __APP_PLL_425_Z0_2(_v) ((_v) << __APP_PLL_425_Z0_2_SH) +#define __APP_PLL_425_RSEL200500 0x00000010 +#define __APP_PLL_425_ENARST 0x00000008 +#define __APP_PLL_425_BYPASS 0x00000004 +#define __APP_PLL_425_LRESETN 0x00000002 +#define __APP_PLL_425_ENABLE 0x00000001 +#define APP_PLL_312_CTL_REG 0x00014208 +#define __P_312_PLL_LOCK 0x80000000 +#define __ENABLE_MAC_AHB_1 0x00800000 +#define __ENABLE_MAC_AHB_0 0x00400000 +#define __ENABLE_MAC_1 0x00200000 +#define __ENABLE_MAC_0 0x00100000 +#define __APP_PLL_312_RESET_TIMER_MK 0x000e0000 +#define __APP_PLL_312_RESET_TIMER_SH 17 +#define __APP_PLL_312_RESET_TIMER(_v) ((_v) << __APP_PLL_312_RESET_TIMER_SH) +#define __APP_PLL_312_LOGIC_SOFT_RESET 0x00010000 +#define __APP_PLL_312_CNTLMT0_1_MK 0x0000c000 +#define __APP_PLL_312_CNTLMT0_1_SH 14 +#define __APP_PLL_312_CNTLMT0_1(_v) ((_v) << __APP_PLL_312_CNTLMT0_1_SH) +#define __APP_PLL_312_JITLMT0_1_MK 0x00003000 +#define __APP_PLL_312_JITLMT0_1_SH 12 +#define __APP_PLL_312_JITLMT0_1(_v) ((_v) << __APP_PLL_312_JITLMT0_1_SH) +#define __APP_PLL_312_HREF 0x00000800 +#define __APP_PLL_312_HDIV 0x00000400 +#define __APP_PLL_312_P0_1_MK 0x00000300 +#define __APP_PLL_312_P0_1_SH 8 +#define __APP_PLL_312_P0_1(_v) ((_v) << __APP_PLL_312_P0_1_SH) +#define __APP_PLL_312_Z0_2_MK 0x000000e0 +#define __APP_PLL_312_Z0_2_SH 5 +#define __APP_PLL_312_Z0_2(_v) ((_v) << __APP_PLL_312_Z0_2_SH) +#define __APP_PLL_312_RSEL200500 0x00000010 +#define __APP_PLL_312_ENARST 0x00000008 +#define __APP_PLL_312_BYPASS 0x00000004 +#define __APP_PLL_312_LRESETN 0x00000002 +#define __APP_PLL_312_ENABLE 0x00000001 +#define MBIST_CTL_REG 0x00014220 +#define __EDRAM_BISTR_START 0x00000004 +#define __MBIST_RESET 0x00000002 +#define __MBIST_START 0x00000001 +#define MBIST_STAT_REG 0x00014224 +#define __EDRAM_BISTR_STATUS 0x00000008 +#define __EDRAM_BISTR_DONE 0x00000004 +#define __MEM_BIT_STATUS 0x00000002 +#define __MBIST_DONE 0x00000001 +#define HOST_SEM0_REG 0x00014230 +#define __HOST_SEMAPHORE 0x00000001 +#define HOST_SEM1_REG 0x00014234 +#define HOST_SEM2_REG 0x00014238 +#define HOST_SEM3_REG 0x0001423c +#define HOST_SEM0_INFO_REG 0x00014240 +#define HOST_SEM1_INFO_REG 0x00014244 +#define HOST_SEM2_INFO_REG 0x00014248 +#define HOST_SEM3_INFO_REG 0x0001424c +#define ETH_MAC_SER_REG 0x00014288 +#define __APP_EMS_CKBUFAMPIN 0x00000020 +#define __APP_EMS_REFCLKSEL 0x00000010 +#define __APP_EMS_CMLCKSEL 0x00000008 +#define __APP_EMS_REFCKBUFEN2 0x00000004 +#define __APP_EMS_REFCKBUFEN1 0x00000002 +#define __APP_EMS_CHANNEL_SEL 0x00000001 +#define HOSTFN2_INT_STATUS 0x00014300 +#define __HOSTFN2_HALT_OCCURRED 0x01000000 +#define __HOSTFN2_INT_STATUS_LVL_MK 0x00f00000 +#define __HOSTFN2_INT_STATUS_LVL_SH 20 +#define __HOSTFN2_INT_STATUS_LVL(_v) ((_v) << __HOSTFN2_INT_STATUS_LVL_SH) +#define __HOSTFN2_INT_STATUS_P_MK 0x000f0000 +#define __HOSTFN2_INT_STATUS_P_SH 16 +#define __HOSTFN2_INT_STATUS_P(_v) ((_v) << __HOSTFN2_INT_STATUS_P_SH) +#define __HOSTFN2_INT_STATUS_F 0x0000ffff +#define HOSTFN2_INT_MSK 0x00014304 +#define HOST_PAGE_NUM_FN2 0x00014308 +#define HOST_MSIX_ERR_INDEX_FN2 0x0001430c +#define HOSTFN3_INT_STATUS 0x00014400 +#define __HALT_OCCURRED 0x01000000 +#define __HOSTFN3_INT_STATUS_LVL_MK 0x00f00000 +#define __HOSTFN3_INT_STATUS_LVL_SH 20 +#define __HOSTFN3_INT_STATUS_LVL(_v) ((_v) << __HOSTFN3_INT_STATUS_LVL_SH) +#define __HOSTFN3_INT_STATUS_P_MK 0x000f0000 +#define __HOSTFN3_INT_STATUS_P_SH 16 +#define __HOSTFN3_INT_STATUS_P(_v) ((_v) << __HOSTFN3_INT_STATUS_P_SH) +#define __HOSTFN3_INT_STATUS_F 0x0000ffff +#define HOSTFN3_INT_MSK 0x00014404 +#define HOST_PAGE_NUM_FN3 0x00014408 +#define HOST_MSIX_ERR_INDEX_FN3 0x0001440c +#define FNC_ID_REG 0x00014600 +#define __FUNCTION_NUMBER 0x00000007 +#define FNC_PERS_REG 0x00014604 +#define __F3_FUNCTION_ACTIVE 0x80000000 +#define __F3_FUNCTION_MODE 0x40000000 +#define __F3_PORT_MAP_MK 0x30000000 +#define __F3_PORT_MAP_SH 28 +#define __F3_PORT_MAP(_v) ((_v) << __F3_PORT_MAP_SH) +#define __F3_VM_MODE 0x08000000 +#define __F3_INTX_STATUS_MK 0x07000000 +#define __F3_INTX_STATUS_SH 24 +#define __F3_INTX_STATUS(_v) ((_v) << __F3_INTX_STATUS_SH) +#define __F2_FUNCTION_ACTIVE 0x00800000 +#define __F2_FUNCTION_MODE 0x00400000 +#define __F2_PORT_MAP_MK 0x00300000 +#define __F2_PORT_MAP_SH 20 +#define __F2_PORT_MAP(_v) ((_v) << __F2_PORT_MAP_SH) +#define __F2_VM_MODE 0x00080000 +#define __F2_INTX_STATUS_MK 0x00070000 +#define __F2_INTX_STATUS_SH 16 +#define __F2_INTX_STATUS(_v) ((_v) << __F2_INTX_STATUS_SH) +#define __F1_FUNCTION_ACTIVE 0x00008000 +#define __F1_FUNCTION_MODE 0x00004000 +#define __F1_PORT_MAP_MK 0x00003000 +#define __F1_PORT_MAP_SH 12 +#define __F1_PORT_MAP(_v) ((_v) << __F1_PORT_MAP_SH) +#define __F1_VM_MODE 0x00000800 +#define __F1_INTX_STATUS_MK 0x00000700 +#define __F1_INTX_STATUS_SH 8 +#define __F1_INTX_STATUS(_v) ((_v) << __F1_INTX_STATUS_SH) +#define __F0_FUNCTION_ACTIVE 0x00000080 +#define __F0_FUNCTION_MODE 0x00000040 +#define __F0_PORT_MAP_MK 0x00000030 +#define __F0_PORT_MAP_SH 4 +#define __F0_PORT_MAP(_v) ((_v) << __F0_PORT_MAP_SH) +#define __F0_VM_MODE 0x00000008 +#define __F0_INTX_STATUS 0x00000007 +enum { + __F0_INTX_STATUS_MSIX = 0x0, + __F0_INTX_STATUS_INTA = 0x1, + __F0_INTX_STATUS_INTB = 0x2, + __F0_INTX_STATUS_INTC = 0x3, + __F0_INTX_STATUS_INTD = 0x4, +}; +#define OP_MODE 0x0001460c +#define __APP_ETH_CLK_LOWSPEED 0x00000004 +#define __GLOBAL_CORECLK_HALFSPEED 0x00000002 +#define __GLOBAL_FCOE_MODE 0x00000001 +#define HOST_SEM4_REG 0x00014610 +#define HOST_SEM5_REG 0x00014614 +#define HOST_SEM6_REG 0x00014618 +#define HOST_SEM7_REG 0x0001461c +#define HOST_SEM4_INFO_REG 0x00014620 +#define HOST_SEM5_INFO_REG 0x00014624 +#define HOST_SEM6_INFO_REG 0x00014628 +#define HOST_SEM7_INFO_REG 0x0001462c +#define HOSTFN0_LPU0_MBOX0_CMD_STAT 0x00019000 +#define __HOSTFN0_LPU0_MBOX0_INFO_MK 0xfffffffe +#define __HOSTFN0_LPU0_MBOX0_INFO_SH 1 +#define __HOSTFN0_LPU0_MBOX0_INFO(_v) ((_v) << __HOSTFN0_LPU0_MBOX0_INFO_SH) +#define __HOSTFN0_LPU0_MBOX0_CMD_STATUS 0x00000001 +#define HOSTFN0_LPU1_MBOX0_CMD_STAT 0x00019004 +#define __HOSTFN0_LPU1_MBOX0_INFO_MK 0xfffffffe +#define __HOSTFN0_LPU1_MBOX0_INFO_SH 1 +#define __HOSTFN0_LPU1_MBOX0_INFO(_v) ((_v) << __HOSTFN0_LPU1_MBOX0_INFO_SH) +#define __HOSTFN0_LPU1_MBOX0_CMD_STATUS 0x00000001 +#define LPU0_HOSTFN0_MBOX0_CMD_STAT 0x00019008 +#define __LPU0_HOSTFN0_MBOX0_INFO_MK 0xfffffffe +#define __LPU0_HOSTFN0_MBOX0_INFO_SH 1 +#define __LPU0_HOSTFN0_MBOX0_INFO(_v) ((_v) << __LPU0_HOSTFN0_MBOX0_INFO_SH) +#define __LPU0_HOSTFN0_MBOX0_CMD_STATUS 0x00000001 +#define LPU1_HOSTFN0_MBOX0_CMD_STAT 0x0001900c +#define __LPU1_HOSTFN0_MBOX0_INFO_MK 0xfffffffe +#define __LPU1_HOSTFN0_MBOX0_INFO_SH 1 +#define __LPU1_HOSTFN0_MBOX0_INFO(_v) ((_v) << __LPU1_HOSTFN0_MBOX0_INFO_SH) +#define __LPU1_HOSTFN0_MBOX0_CMD_STATUS 0x00000001 +#define HOSTFN1_LPU0_MBOX0_CMD_STAT 0x00019010 +#define __HOSTFN1_LPU0_MBOX0_INFO_MK 0xfffffffe +#define __HOSTFN1_LPU0_MBOX0_INFO_SH 1 +#define __HOSTFN1_LPU0_MBOX0_INFO(_v) ((_v) << __HOSTFN1_LPU0_MBOX0_INFO_SH) +#define __HOSTFN1_LPU0_MBOX0_CMD_STATUS 0x00000001 +#define HOSTFN1_LPU1_MBOX0_CMD_STAT 0x00019014 +#define __HOSTFN1_LPU1_MBOX0_INFO_MK 0xfffffffe +#define __HOSTFN1_LPU1_MBOX0_INFO_SH 1 +#define __HOSTFN1_LPU1_MBOX0_INFO(_v) ((_v) << __HOSTFN1_LPU1_MBOX0_INFO_SH) +#define __HOSTFN1_LPU1_MBOX0_CMD_STATUS 0x00000001 +#define LPU0_HOSTFN1_MBOX0_CMD_STAT 0x00019018 +#define __LPU0_HOSTFN1_MBOX0_INFO_MK 0xfffffffe +#define __LPU0_HOSTFN1_MBOX0_INFO_SH 1 +#define __LPU0_HOSTFN1_MBOX0_INFO(_v) ((_v) << __LPU0_HOSTFN1_MBOX0_INFO_SH) +#define __LPU0_HOSTFN1_MBOX0_CMD_STATUS 0x00000001 +#define LPU1_HOSTFN1_MBOX0_CMD_STAT 0x0001901c +#define __LPU1_HOSTFN1_MBOX0_INFO_MK 0xfffffffe +#define __LPU1_HOSTFN1_MBOX0_INFO_SH 1 +#define __LPU1_HOSTFN1_MBOX0_INFO(_v) ((_v) << __LPU1_HOSTFN1_MBOX0_INFO_SH) +#define __LPU1_HOSTFN1_MBOX0_CMD_STATUS 0x00000001 +#define HOSTFN2_LPU0_MBOX0_CMD_STAT 0x00019150 +#define __HOSTFN2_LPU0_MBOX0_INFO_MK 0xfffffffe +#define __HOSTFN2_LPU0_MBOX0_INFO_SH 1 +#define __HOSTFN2_LPU0_MBOX0_INFO(_v) ((_v) << __HOSTFN2_LPU0_MBOX0_INFO_SH) +#define __HOSTFN2_LPU0_MBOX0_CMD_STATUS 0x00000001 +#define HOSTFN2_LPU1_MBOX0_CMD_STAT 0x00019154 +#define __HOSTFN2_LPU1_MBOX0_INFO_MK 0xfffffffe +#define __HOSTFN2_LPU1_MBOX0_INFO_SH 1 +#define __HOSTFN2_LPU1_MBOX0_INFO(_v) ((_v) << __HOSTFN2_LPU1_MBOX0_INFO_SH) +#define __HOSTFN2_LPU1_MBOX0BOX0_CMD_STATUS 0x00000001 +#define LPU0_HOSTFN2_MBOX0_CMD_STAT 0x00019158 +#define __LPU0_HOSTFN2_MBOX0_INFO_MK 0xfffffffe +#define __LPU0_HOSTFN2_MBOX0_INFO_SH 1 +#define __LPU0_HOSTFN2_MBOX0_INFO(_v) ((_v) << __LPU0_HOSTFN2_MBOX0_INFO_SH) +#define __LPU0_HOSTFN2_MBOX0_CMD_STATUS 0x00000001 +#define LPU1_HOSTFN2_MBOX0_CMD_STAT 0x0001915c +#define __LPU1_HOSTFN2_MBOX0_INFO_MK 0xfffffffe +#define __LPU1_HOSTFN2_MBOX0_INFO_SH 1 +#define __LPU1_HOSTFN2_MBOX0_INFO(_v) ((_v) << __LPU1_HOSTFN2_MBOX0_INFO_SH) +#define __LPU1_HOSTFN2_MBOX0_CMD_STATUS 0x00000001 +#define HOSTFN3_LPU0_MBOX0_CMD_STAT 0x00019160 +#define __HOSTFN3_LPU0_MBOX0_INFO_MK 0xfffffffe +#define __HOSTFN3_LPU0_MBOX0_INFO_SH 1 +#define __HOSTFN3_LPU0_MBOX0_INFO(_v) ((_v) << __HOSTFN3_LPU0_MBOX0_INFO_SH) +#define __HOSTFN3_LPU0_MBOX0_CMD_STATUS 0x00000001 +#define HOSTFN3_LPU1_MBOX0_CMD_STAT 0x00019164 +#define __HOSTFN3_LPU1_MBOX0_INFO_MK 0xfffffffe +#define __HOSTFN3_LPU1_MBOX0_INFO_SH 1 +#define __HOSTFN3_LPU1_MBOX0_INFO(_v) ((_v) << __HOSTFN3_LPU1_MBOX0_INFO_SH) +#define __HOSTFN3_LPU1_MBOX0_CMD_STATUS 0x00000001 +#define LPU0_HOSTFN3_MBOX0_CMD_STAT 0x00019168 +#define __LPU0_HOSTFN3_MBOX0_INFO_MK 0xfffffffe +#define __LPU0_HOSTFN3_MBOX0_INFO_SH 1 +#define __LPU0_HOSTFN3_MBOX0_INFO(_v) ((_v) << __LPU0_HOSTFN3_MBOX0_INFO_SH) +#define __LPU0_HOSTFN3_MBOX0_CMD_STATUS 0x00000001 +#define LPU1_HOSTFN3_MBOX0_CMD_STAT 0x0001916c +#define __LPU1_HOSTFN3_MBOX0_INFO_MK 0xfffffffe +#define __LPU1_HOSTFN3_MBOX0_INFO_SH 1 +#define __LPU1_HOSTFN3_MBOX0_INFO(_v) ((_v) << __LPU1_HOSTFN3_MBOX0_INFO_SH) +#define __LPU1_HOSTFN3_MBOX0_CMD_STATUS 0x00000001 +#define FW_INIT_HALT_P0 0x000191ac +#define __FW_INIT_HALT_P 0x00000001 +#define FW_INIT_HALT_P1 0x000191bc +#define CPE_PI_PTR_Q0 0x00038000 +#define __CPE_PI_UNUSED_MK 0xffff0000 +#define __CPE_PI_UNUSED_SH 16 +#define __CPE_PI_UNUSED(_v) ((_v) << __CPE_PI_UNUSED_SH) +#define __CPE_PI_PTR 0x0000ffff +#define CPE_PI_PTR_Q1 0x00038040 +#define CPE_CI_PTR_Q0 0x00038004 +#define __CPE_CI_UNUSED_MK 0xffff0000 +#define __CPE_CI_UNUSED_SH 16 +#define __CPE_CI_UNUSED(_v) ((_v) << __CPE_CI_UNUSED_SH) +#define __CPE_CI_PTR 0x0000ffff +#define CPE_CI_PTR_Q1 0x00038044 +#define CPE_DEPTH_Q0 0x00038008 +#define __CPE_DEPTH_UNUSED_MK 0xf8000000 +#define __CPE_DEPTH_UNUSED_SH 27 +#define __CPE_DEPTH_UNUSED(_v) ((_v) << __CPE_DEPTH_UNUSED_SH) +#define __CPE_MSIX_VEC_INDEX_MK 0x07ff0000 +#define __CPE_MSIX_VEC_INDEX_SH 16 +#define __CPE_MSIX_VEC_INDEX(_v) ((_v) << __CPE_MSIX_VEC_INDEX_SH) +#define __CPE_DEPTH 0x0000ffff +#define CPE_DEPTH_Q1 0x00038048 +#define CPE_QCTRL_Q0 0x0003800c +#define __CPE_CTRL_UNUSED30_MK 0xfc000000 +#define __CPE_CTRL_UNUSED30_SH 26 +#define __CPE_CTRL_UNUSED30(_v) ((_v) << __CPE_CTRL_UNUSED30_SH) +#define __CPE_FUNC_INT_CTRL_MK 0x03000000 +#define __CPE_FUNC_INT_CTRL_SH 24 +#define __CPE_FUNC_INT_CTRL(_v) ((_v) << __CPE_FUNC_INT_CTRL_SH) +enum { + __CPE_FUNC_INT_CTRL_DISABLE = 0x0, + __CPE_FUNC_INT_CTRL_F2NF = 0x1, + __CPE_FUNC_INT_CTRL_3QUART = 0x2, + __CPE_FUNC_INT_CTRL_HALF = 0x3, +}; +#define __CPE_CTRL_UNUSED20_MK 0x00f00000 +#define __CPE_CTRL_UNUSED20_SH 20 +#define __CPE_CTRL_UNUSED20(_v) ((_v) << __CPE_CTRL_UNUSED20_SH) +#define __CPE_SCI_TH_MK 0x000f0000 +#define __CPE_SCI_TH_SH 16 +#define __CPE_SCI_TH(_v) ((_v) << __CPE_SCI_TH_SH) +#define __CPE_CTRL_UNUSED10_MK 0x0000c000 +#define __CPE_CTRL_UNUSED10_SH 14 +#define __CPE_CTRL_UNUSED10(_v) ((_v) << __CPE_CTRL_UNUSED10_SH) +#define __CPE_ACK_PENDING 0x00002000 +#define __CPE_CTRL_UNUSED40_MK 0x00001c00 +#define __CPE_CTRL_UNUSED40_SH 10 +#define __CPE_CTRL_UNUSED40(_v) ((_v) << __CPE_CTRL_UNUSED40_SH) +#define __CPE_PCIEID_MK 0x00000300 +#define __CPE_PCIEID_SH 8 +#define __CPE_PCIEID(_v) ((_v) << __CPE_PCIEID_SH) +#define __CPE_CTRL_UNUSED00_MK 0x000000fe +#define __CPE_CTRL_UNUSED00_SH 1 +#define __CPE_CTRL_UNUSED00(_v) ((_v) << __CPE_CTRL_UNUSED00_SH) +#define __CPE_ESIZE 0x00000001 +#define CPE_QCTRL_Q1 0x0003804c +#define __CPE_CTRL_UNUSED31_MK 0xfc000000 +#define __CPE_CTRL_UNUSED31_SH 26 +#define __CPE_CTRL_UNUSED31(_v) ((_v) << __CPE_CTRL_UNUSED31_SH) +#define __CPE_CTRL_UNUSED21_MK 0x00f00000 +#define __CPE_CTRL_UNUSED21_SH 20 +#define __CPE_CTRL_UNUSED21(_v) ((_v) << __CPE_CTRL_UNUSED21_SH) +#define __CPE_CTRL_UNUSED11_MK 0x0000c000 +#define __CPE_CTRL_UNUSED11_SH 14 +#define __CPE_CTRL_UNUSED11(_v) ((_v) << __CPE_CTRL_UNUSED11_SH) +#define __CPE_CTRL_UNUSED41_MK 0x00001c00 +#define __CPE_CTRL_UNUSED41_SH 10 +#define __CPE_CTRL_UNUSED41(_v) ((_v) << __CPE_CTRL_UNUSED41_SH) +#define __CPE_CTRL_UNUSED01_MK 0x000000fe +#define __CPE_CTRL_UNUSED01_SH 1 +#define __CPE_CTRL_UNUSED01(_v) ((_v) << __CPE_CTRL_UNUSED01_SH) +#define RME_PI_PTR_Q0 0x00038020 +#define __LATENCY_TIME_STAMP_MK 0xffff0000 +#define __LATENCY_TIME_STAMP_SH 16 +#define __LATENCY_TIME_STAMP(_v) ((_v) << __LATENCY_TIME_STAMP_SH) +#define __RME_PI_PTR 0x0000ffff +#define RME_PI_PTR_Q1 0x00038060 +#define RME_CI_PTR_Q0 0x00038024 +#define __DELAY_TIME_STAMP_MK 0xffff0000 +#define __DELAY_TIME_STAMP_SH 16 +#define __DELAY_TIME_STAMP(_v) ((_v) << __DELAY_TIME_STAMP_SH) +#define __RME_CI_PTR 0x0000ffff +#define RME_CI_PTR_Q1 0x00038064 +#define RME_DEPTH_Q0 0x00038028 +#define __RME_DEPTH_UNUSED_MK 0xf8000000 +#define __RME_DEPTH_UNUSED_SH 27 +#define __RME_DEPTH_UNUSED(_v) ((_v) << __RME_DEPTH_UNUSED_SH) +#define __RME_MSIX_VEC_INDEX_MK 0x07ff0000 +#define __RME_MSIX_VEC_INDEX_SH 16 +#define __RME_MSIX_VEC_INDEX(_v) ((_v) << __RME_MSIX_VEC_INDEX_SH) +#define __RME_DEPTH 0x0000ffff +#define RME_DEPTH_Q1 0x00038068 +#define RME_QCTRL_Q0 0x0003802c +#define __RME_INT_LATENCY_TIMER_MK 0xff000000 +#define __RME_INT_LATENCY_TIMER_SH 24 +#define __RME_INT_LATENCY_TIMER(_v) ((_v) << __RME_INT_LATENCY_TIMER_SH) +#define __RME_INT_DELAY_TIMER_MK 0x00ff0000 +#define __RME_INT_DELAY_TIMER_SH 16 +#define __RME_INT_DELAY_TIMER(_v) ((_v) << __RME_INT_DELAY_TIMER_SH) +#define __RME_INT_DELAY_DISABLE 0x00008000 +#define __RME_DLY_DELAY_DISABLE 0x00004000 +#define __RME_ACK_PENDING 0x00002000 +#define __RME_FULL_INTERRUPT_DISABLE 0x00001000 +#define __RME_CTRL_UNUSED10_MK 0x00000c00 +#define __RME_CTRL_UNUSED10_SH 10 +#define __RME_CTRL_UNUSED10(_v) ((_v) << __RME_CTRL_UNUSED10_SH) +#define __RME_PCIEID_MK 0x00000300 +#define __RME_PCIEID_SH 8 +#define __RME_PCIEID(_v) ((_v) << __RME_PCIEID_SH) +#define __RME_CTRL_UNUSED00_MK 0x000000fe +#define __RME_CTRL_UNUSED00_SH 1 +#define __RME_CTRL_UNUSED00(_v) ((_v) << __RME_CTRL_UNUSED00_SH) +#define __RME_ESIZE 0x00000001 +#define RME_QCTRL_Q1 0x0003806c +#define __RME_CTRL_UNUSED11_MK 0x00000c00 +#define __RME_CTRL_UNUSED11_SH 10 +#define __RME_CTRL_UNUSED11(_v) ((_v) << __RME_CTRL_UNUSED11_SH) +#define __RME_CTRL_UNUSED01_MK 0x000000fe +#define __RME_CTRL_UNUSED01_SH 1 +#define __RME_CTRL_UNUSED01(_v) ((_v) << __RME_CTRL_UNUSED01_SH) +#define PSS_CTL_REG 0x00018800 +#define __PSS_I2C_CLK_DIV_MK 0x007f0000 +#define __PSS_I2C_CLK_DIV_SH 16 +#define __PSS_I2C_CLK_DIV(_v) ((_v) << __PSS_I2C_CLK_DIV_SH) +#define __PSS_LMEM_INIT_DONE 0x00001000 +#define __PSS_LMEM_RESET 0x00000200 +#define __PSS_LMEM_INIT_EN 0x00000100 +#define __PSS_LPU1_RESET 0x00000002 +#define __PSS_LPU0_RESET 0x00000001 +#define HQM_QSET0_RXQ_DRBL_P0 0x00038000 +#define __RXQ0_ADD_VECTORS_P 0x80000000 +#define __RXQ0_STOP_P 0x40000000 +#define __RXQ0_PRD_PTR_P 0x0000ffff +#define HQM_QSET1_RXQ_DRBL_P0 0x00038080 +#define __RXQ1_ADD_VECTORS_P 0x80000000 +#define __RXQ1_STOP_P 0x40000000 +#define __RXQ1_PRD_PTR_P 0x0000ffff +#define HQM_QSET0_RXQ_DRBL_P1 0x0003c000 +#define HQM_QSET1_RXQ_DRBL_P1 0x0003c080 +#define HQM_QSET0_TXQ_DRBL_P0 0x00038020 +#define __TXQ0_ADD_VECTORS_P 0x80000000 +#define __TXQ0_STOP_P 0x40000000 +#define __TXQ0_PRD_PTR_P 0x0000ffff +#define HQM_QSET1_TXQ_DRBL_P0 0x000380a0 +#define __TXQ1_ADD_VECTORS_P 0x80000000 +#define __TXQ1_STOP_P 0x40000000 +#define __TXQ1_PRD_PTR_P 0x0000ffff +#define HQM_QSET0_TXQ_DRBL_P1 0x0003c020 +#define HQM_QSET1_TXQ_DRBL_P1 0x0003c0a0 +#define HQM_QSET0_IB_DRBL_1_P0 0x00038040 +#define __IB1_0_ACK_P 0x80000000 +#define __IB1_0_DISABLE_P 0x40000000 +#define __IB1_0_NUM_OF_ACKED_EVENTS_P 0x0000ffff +#define HQM_QSET1_IB_DRBL_1_P0 0x000380c0 +#define __IB1_1_ACK_P 0x80000000 +#define __IB1_1_DISABLE_P 0x40000000 +#define __IB1_1_NUM_OF_ACKED_EVENTS_P 0x0000ffff +#define HQM_QSET0_IB_DRBL_1_P1 0x0003c040 +#define HQM_QSET1_IB_DRBL_1_P1 0x0003c0c0 +#define HQM_QSET0_IB_DRBL_2_P0 0x00038060 +#define __IB2_0_ACK_P 0x80000000 +#define __IB2_0_DISABLE_P 0x40000000 +#define __IB2_0_NUM_OF_ACKED_EVENTS_P 0x0000ffff +#define HQM_QSET1_IB_DRBL_2_P0 0x000380e0 +#define __IB2_1_ACK_P 0x80000000 +#define __IB2_1_DISABLE_P 0x40000000 +#define __IB2_1_NUM_OF_ACKED_EVENTS_P 0x0000ffff +#define HQM_QSET0_IB_DRBL_2_P1 0x0003c060 +#define HQM_QSET1_IB_DRBL_2_P1 0x0003c0e0 + + +/* + * These definitions are either in error/missing in spec. Its auto-generated + * from hard coded values in regparse.pl. + */ +#define __EMPHPOST_AT_4G_MK_FIX 0x0000001c +#define __EMPHPOST_AT_4G_SH_FIX 0x00000002 +#define __EMPHPRE_AT_4G_FIX 0x00000003 +#define __SFP_TXRATE_EN_FIX 0x00000100 +#define __SFP_RXRATE_EN_FIX 0x00000080 + + +/* + * These register definitions are auto-generated from hard coded values + * in regparse.pl. + */ + + +/* + * These register mapping definitions are auto-generated from mapping tables + * in regparse.pl. + */ +#define BFA_IOC0_HBEAT_REG HOST_SEM0_INFO_REG +#define BFA_IOC0_STATE_REG HOST_SEM1_INFO_REG +#define BFA_IOC1_HBEAT_REG HOST_SEM2_INFO_REG +#define BFA_IOC1_STATE_REG HOST_SEM3_INFO_REG +#define BFA_FW_USE_COUNT HOST_SEM4_INFO_REG + +#define CPE_DEPTH_Q(__n) \ + (CPE_DEPTH_Q0 + (__n) * (CPE_DEPTH_Q1 - CPE_DEPTH_Q0)) +#define CPE_QCTRL_Q(__n) \ + (CPE_QCTRL_Q0 + (__n) * (CPE_QCTRL_Q1 - CPE_QCTRL_Q0)) +#define CPE_PI_PTR_Q(__n) \ + (CPE_PI_PTR_Q0 + (__n) * (CPE_PI_PTR_Q1 - CPE_PI_PTR_Q0)) +#define CPE_CI_PTR_Q(__n) \ + (CPE_CI_PTR_Q0 + (__n) * (CPE_CI_PTR_Q1 - CPE_CI_PTR_Q0)) +#define RME_DEPTH_Q(__n) \ + (RME_DEPTH_Q0 + (__n) * (RME_DEPTH_Q1 - RME_DEPTH_Q0)) +#define RME_QCTRL_Q(__n) \ + (RME_QCTRL_Q0 + (__n) * (RME_QCTRL_Q1 - RME_QCTRL_Q0)) +#define RME_PI_PTR_Q(__n) \ + (RME_PI_PTR_Q0 + (__n) * (RME_PI_PTR_Q1 - RME_PI_PTR_Q0)) +#define RME_CI_PTR_Q(__n) \ + (RME_CI_PTR_Q0 + (__n) * (RME_CI_PTR_Q1 - RME_CI_PTR_Q0)) +#define HQM_QSET_RXQ_DRBL_P0(__n) \ + (HQM_QSET0_RXQ_DRBL_P0 + (__n) * (HQM_QSET1_RXQ_DRBL_P0 - \ + HQM_QSET0_RXQ_DRBL_P0)) +#define HQM_QSET_TXQ_DRBL_P0(__n) \ + (HQM_QSET0_TXQ_DRBL_P0 + (__n) * (HQM_QSET1_TXQ_DRBL_P0 - \ + HQM_QSET0_TXQ_DRBL_P0)) +#define HQM_QSET_IB_DRBL_1_P0(__n) \ + (HQM_QSET0_IB_DRBL_1_P0 + (__n) * (HQM_QSET1_IB_DRBL_1_P0 - \ + HQM_QSET0_IB_DRBL_1_P0)) +#define HQM_QSET_IB_DRBL_2_P0(__n) \ + (HQM_QSET0_IB_DRBL_2_P0 + (__n) * (HQM_QSET1_IB_DRBL_2_P0 - \ + HQM_QSET0_IB_DRBL_2_P0)) +#define HQM_QSET_RXQ_DRBL_P1(__n) \ + (HQM_QSET0_RXQ_DRBL_P1 + (__n) * (HQM_QSET1_RXQ_DRBL_P1 - \ + HQM_QSET0_RXQ_DRBL_P1)) +#define HQM_QSET_TXQ_DRBL_P1(__n) \ + (HQM_QSET0_TXQ_DRBL_P1 + (__n) * (HQM_QSET1_TXQ_DRBL_P1 - \ + HQM_QSET0_TXQ_DRBL_P1)) +#define HQM_QSET_IB_DRBL_1_P1(__n) \ + (HQM_QSET0_IB_DRBL_1_P1 + (__n) * (HQM_QSET1_IB_DRBL_1_P1 - \ + HQM_QSET0_IB_DRBL_1_P1)) +#define HQM_QSET_IB_DRBL_2_P1(__n) \ + (HQM_QSET0_IB_DRBL_2_P1 + (__n) * (HQM_QSET1_IB_DRBL_2_P1 - \ + HQM_QSET0_IB_DRBL_2_P1)) + +#define CPE_Q_NUM(__fn, __q) (((__fn) << 2) + (__q)) +#define RME_Q_NUM(__fn, __q) (((__fn) << 2) + (__q)) +#define CPE_Q_MASK(__q) ((__q) & 0x3) +#define RME_Q_MASK(__q) ((__q) & 0x3) + + +/* + * PCI MSI-X vector defines + */ +enum { + BFA_MSIX_CPE_Q0 = 0, + BFA_MSIX_CPE_Q1 = 1, + BFA_MSIX_CPE_Q2 = 2, + BFA_MSIX_CPE_Q3 = 3, + BFA_MSIX_RME_Q0 = 4, + BFA_MSIX_RME_Q1 = 5, + BFA_MSIX_RME_Q2 = 6, + BFA_MSIX_RME_Q3 = 7, + BFA_MSIX_LPU_ERR = 8, + BFA_MSIX_CT_MAX = 9, +}; + +/* + * And corresponding host interrupt status bit field defines + */ +#define __HFN_INT_CPE_Q0 0x00000001U +#define __HFN_INT_CPE_Q1 0x00000002U +#define __HFN_INT_CPE_Q2 0x00000004U +#define __HFN_INT_CPE_Q3 0x00000008U +#define __HFN_INT_CPE_Q4 0x00000010U +#define __HFN_INT_CPE_Q5 0x00000020U +#define __HFN_INT_CPE_Q6 0x00000040U +#define __HFN_INT_CPE_Q7 0x00000080U +#define __HFN_INT_RME_Q0 0x00000100U +#define __HFN_INT_RME_Q1 0x00000200U +#define __HFN_INT_RME_Q2 0x00000400U +#define __HFN_INT_RME_Q3 0x00000800U +#define __HFN_INT_RME_Q4 0x00001000U +#define __HFN_INT_RME_Q5 0x00002000U +#define __HFN_INT_RME_Q6 0x00004000U +#define __HFN_INT_RME_Q7 0x00008000U +#define __HFN_INT_ERR_EMC 0x00010000U +#define __HFN_INT_ERR_LPU0 0x00020000U +#define __HFN_INT_ERR_LPU1 0x00040000U +#define __HFN_INT_ERR_PSS 0x00080000U +#define __HFN_INT_MBOX_LPU0 0x00100000U +#define __HFN_INT_MBOX_LPU1 0x00200000U +#define __HFN_INT_MBOX1_LPU0 0x00400000U +#define __HFN_INT_MBOX1_LPU1 0x00800000U +#define __HFN_INT_CPE_MASK 0x000000ffU +#define __HFN_INT_RME_MASK 0x0000ff00U + + +/* + * catapult memory map. + */ +#define LL_PGN_HQM0 0x0096 +#define LL_PGN_HQM1 0x0097 +#define PSS_SMEM_PAGE_START 0x8000 +#define PSS_SMEM_PGNUM(_pg0, _ma) ((_pg0) + ((_ma) >> 15)) +#define PSS_SMEM_PGOFF(_ma) ((_ma) & 0x7fff) + +/* + * End of catapult memory map + */ + + +#endif /* __BFI_CTREG_H__ */ + diff --git a/drivers/scsi/bfa/include/bfi/bfi_fabric.h b/drivers/scsi/bfa/include/bfi/bfi_fabric.h new file mode 100644 index 000000000000..c0669ed41078 --- /dev/null +++ b/drivers/scsi/bfa/include/bfi/bfi_fabric.h @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2005-2009 Brocade Communications Systems, Inc. + * All rights reserved + * www.brocade.com + * + * Linux driver for Brocade Fibre Channel Host Bus Adapter. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (GPL) Version 2 as + * published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#ifndef __BFI_FABRIC_H__ +#define __BFI_FABRIC_H__ + +#include <bfi/bfi.h> + +#pragma pack(1) + +enum bfi_fabric_h2i_msgs { + BFI_FABRIC_H2I_CREATE_REQ = 1, + BFI_FABRIC_H2I_DELETE_REQ = 2, + BFI_FABRIC_H2I_SETAUTH = 3, +}; + +enum bfi_fabric_i2h_msgs { + BFI_FABRIC_I2H_CREATE_RSP = BFA_I2HM(1), + BFI_FABRIC_I2H_DELETE_RSP = BFA_I2HM(2), + BFI_FABRIC_I2H_SETAUTH_RSP = BFA_I2HM(3), + BFI_FABRIC_I2H_ONLINE = BFA_I2HM(4), + BFI_FABRIC_I2H_OFFLINE = BFA_I2HM(5), +}; + +struct bfi_fabric_create_req_s { + bfi_mhdr_t mh; /* common msg header */ + u8 vf_en; /* virtual fabric enable */ + u8 rsvd; + u16 vf_id; /* virtual fabric ID */ + wwn_t pwwn; /* port name */ + wwn_t nwwn; /* node name */ +}; + +struct bfi_fabric_create_rsp_s { + bfi_mhdr_t mh; /* common msg header */ + u16 bfa_handle; /* host fabric handle */ + u8 status; /* fabric create status */ + u8 rsvd; +}; + +struct bfi_fabric_delete_req_s { + bfi_mhdr_t mh; /* common msg header */ + u16 fw_handle; /* firmware fabric handle */ + u16 rsvd; +}; + +struct bfi_fabric_delete_rsp_s { + bfi_mhdr_t mh; /* common msg header */ + u16 bfa_handle; /* host fabric handle */ + u8 status; /* fabric deletion status */ + u8 rsvd; +}; + +#define BFI_FABRIC_AUTHSECRET_LEN 64 +struct bfi_fabric_setauth_req_s { + bfi_mhdr_t mh; /* common msg header */ + u16 fw_handle; /* f/w handle of fabric */ + u8 algorithm; + u8 group; + u8 secret[BFI_FABRIC_AUTHSECRET_LEN]; +}; + +union bfi_fabric_h2i_msg_u { + bfi_msg_t *msg; + struct bfi_fabric_create_req_s *create_req; + struct bfi_fabric_delete_req_s *delete_req; +}; + +union bfi_fabric_i2h_msg_u { + bfi_msg_t *msg; + struct bfi_fabric_create_rsp_s *create_rsp; + struct bfi_fabric_delete_rsp_s *delete_rsp; +}; + +#pragma pack() + +#endif /* __BFI_FABRIC_H__ */ + diff --git a/drivers/scsi/bfa/include/bfi/bfi_fcpim.h b/drivers/scsi/bfa/include/bfi/bfi_fcpim.h new file mode 100644 index 000000000000..52c059fb4c3a --- /dev/null +++ b/drivers/scsi/bfa/include/bfi/bfi_fcpim.h @@ -0,0 +1,301 @@ +/* + * Copyright (c) 2005-2009 Brocade Communications Systems, Inc. + * All rights reserved + * www.brocade.com + * + * Linux driver for Brocade Fibre Channel Host Bus Adapter. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (GPL) Version 2 as + * published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#ifndef __BFI_FCPIM_H__ +#define __BFI_FCPIM_H__ + +#include "bfi.h" +#include <protocol/fcp.h> + +#pragma pack(1) + +/* + * Initiator mode I-T nexus interface defines. + */ + +enum bfi_itnim_h2i { + BFI_ITNIM_H2I_CREATE_REQ = 1, /* i-t nexus creation */ + BFI_ITNIM_H2I_DELETE_REQ = 2, /* i-t nexus deletion */ +}; + +enum bfi_itnim_i2h { + BFI_ITNIM_I2H_CREATE_RSP = BFA_I2HM(1), + BFI_ITNIM_I2H_DELETE_RSP = BFA_I2HM(2), + BFI_ITNIM_I2H_SLER_EVENT = BFA_I2HM(3), +}; + +struct bfi_itnim_create_req_s { + struct bfi_mhdr_s mh; /* common msg header */ + u16 fw_handle; /* f/w handle for itnim */ + u8 class; /* FC class for IO */ + u8 seq_rec; /* sequence recovery support */ + u8 msg_no; /* seq id of the msg */ +}; + +struct bfi_itnim_create_rsp_s { + struct bfi_mhdr_s mh; /* common msg header */ + u16 bfa_handle; /* bfa handle for itnim */ + u8 status; /* fcp request status */ + u8 seq_id; /* seq id of the msg */ +}; + +struct bfi_itnim_delete_req_s { + struct bfi_mhdr_s mh; /* common msg header */ + u16 fw_handle; /* f/w itnim handle */ + u8 seq_id; /* seq id of the msg */ + u8 rsvd; +}; + +struct bfi_itnim_delete_rsp_s { + struct bfi_mhdr_s mh; /* common msg header */ + u16 bfa_handle; /* bfa handle for itnim */ + u8 status; /* fcp request status */ + u8 seq_id; /* seq id of the msg */ +}; + +struct bfi_itnim_sler_event_s { + struct bfi_mhdr_s mh; /* common msg header */ + u16 bfa_handle; /* bfa handle for itnim */ + u16 rsvd; +}; + +union bfi_itnim_h2i_msg_u { + struct bfi_itnim_create_req_s *create_req; + struct bfi_itnim_delete_req_s *delete_req; + struct bfi_msg_s *msg; +}; + +union bfi_itnim_i2h_msg_u { + struct bfi_itnim_create_rsp_s *create_rsp; + struct bfi_itnim_delete_rsp_s *delete_rsp; + struct bfi_itnim_sler_event_s *sler_event; + struct bfi_msg_s *msg; +}; + +/* + * Initiator mode IO interface defines. + */ + +enum bfi_ioim_h2i { + BFI_IOIM_H2I_IOABORT_REQ = 1, /* IO abort request */ + BFI_IOIM_H2I_IOCLEANUP_REQ = 2, /* IO cleanup request */ +}; + +enum bfi_ioim_i2h { + BFI_IOIM_I2H_IO_RSP = BFA_I2HM(1), /* non-fp IO response */ + BFI_IOIM_I2H_IOABORT_RSP = BFA_I2HM(2),/* ABORT rsp */ +}; + +/** + * IO command DIF info + */ +struct bfi_ioim_dif_s { + u32 dif_info[4]; +}; + +/** + * FCP IO messages overview + * + * @note + * - Max CDB length supported is 64 bytes. + * - SCSI Linked commands and SCSI bi-directional Commands not + * supported. + * + */ +struct bfi_ioim_req_s { + struct bfi_mhdr_s mh; /* Common msg header */ + u16 io_tag; /* I/O tag */ + u16 rport_hdl; /* itnim/rport firmware handle */ + struct fcp_cmnd_s cmnd; /* IO request info */ + + /** + * SG elements array within the IO request must be double word + * aligned. This aligment is required to optimize SGM setup for the IO. + */ + struct bfi_sge_s sges[BFI_SGE_INLINE_MAX]; + u8 io_timeout; + u8 dif_en; + u8 rsvd_a[2]; + struct bfi_ioim_dif_s dif; +}; + +/** + * This table shows various IO status codes from firmware and their + * meaning. Host driver can use these status codes to further process + * IO completions. + * + * BFI_IOIM_STS_OK : IO completed with error free SCSI & + * transport status. + * - io-tag can be reused. + * + * BFA_IOIM_STS_SCSI_ERR : IO completed with scsi error. + * - io-tag can be reused. + * + * BFI_IOIM_STS_HOST_ABORTED : IO was aborted successfully due to + * host request. + * - io-tag cannot be reused yet. + * + * BFI_IOIM_STS_ABORTED : IO was aborted successfully + * internally by f/w. + * - io-tag cannot be reused yet. + * + * BFI_IOIM_STS_TIMEDOUT : IO timedout and ABTS/RRQ is happening + * in the firmware and + * - io-tag cannot be reused yet. + * + * BFI_IOIM_STS_SQER_NEEDED : Firmware could not recover the IO + * with sequence level error + * logic and hence host needs to retry + * this IO with a different IO tag + * - io-tag cannot be used yet. + * + * BFI_IOIM_STS_NEXUS_ABORT : Second Level Error Recovery from host + * is required because 2 consecutive ABTS + * timedout and host needs logout and + * re-login with the target + * - io-tag cannot be used yet. + * + * BFI_IOIM_STS_UNDERRUN : IO completed with SCSI status good, + * but the data tranferred is less than + * the fcp data length in the command. + * ex. SCSI INQUIRY where transferred + * data length and residue count in FCP + * response accounts for total fcp-dl + * - io-tag can be reused. + * + * BFI_IOIM_STS_OVERRUN : IO completed with SCSI status good, + * but the data transerred is more than + * fcp data length in the command. ex. + * TAPE IOs where blocks can of unequal + * lengths. + * - io-tag can be reused. + * + * BFI_IOIM_STS_RES_FREE : Firmware has completed using io-tag + * during abort process + * - io-tag can be reused. + * + * BFI_IOIM_STS_PROTO_ERR : Firmware detected a protocol error. + * ex target sent more data than + * requested, or there was data frame + * loss and other reasons + * - io-tag cannot be used yet. + * + * BFI_IOIM_STS_DIF_ERR : Firwmare detected DIF error. ex: DIF + * CRC err or Ref Tag err or App tag err. + * - io-tag can be reused. + * + * BFA_IOIM_STS_TSK_MGT_ABORT : IO was aborted because of Task + * Management command from the host + * - io-tag can be reused. + * + * BFI_IOIM_STS_UTAG : Firmware does not know about this + * io_tag. + * - io-tag can be reused. + */ +enum bfi_ioim_status { + BFI_IOIM_STS_OK = 0, + BFI_IOIM_STS_HOST_ABORTED = 1, + BFI_IOIM_STS_ABORTED = 2, + BFI_IOIM_STS_TIMEDOUT = 3, + BFI_IOIM_STS_RES_FREE = 4, + BFI_IOIM_STS_SQER_NEEDED = 5, + BFI_IOIM_STS_PROTO_ERR = 6, + BFI_IOIM_STS_UTAG = 7, + BFI_IOIM_STS_PATHTOV = 8, +}; + +#define BFI_IOIM_SNSLEN (256) +/** + * I/O response message + */ +struct bfi_ioim_rsp_s { + struct bfi_mhdr_s mh; /* common msg header */ + u16 io_tag; /* completed IO tag */ + u16 bfa_rport_hndl; /* releated rport handle */ + u8 io_status; /* IO completion status */ + u8 reuse_io_tag; /* IO tag can be reused */ + u16 abort_tag; /* host abort request tag */ + u8 scsi_status; /* scsi status from target */ + u8 sns_len; /* scsi sense length */ + u8 resid_flags; /* IO residue flags */ + u8 rsvd_a; + u32 residue; /* IO residual length in bytes */ + u32 rsvd_b[3]; +}; + +struct bfi_ioim_abort_req_s { + struct bfi_mhdr_s mh; /* Common msg header */ + u16 io_tag; /* I/O tag */ + u16 abort_tag; /* unique request tag */ +}; + +/* + * Initiator mode task management command interface defines. + */ + +enum bfi_tskim_h2i { + BFI_TSKIM_H2I_TM_REQ = 1, /* task-mgmt command */ + BFI_TSKIM_H2I_ABORT_REQ = 2, /* task-mgmt command */ +}; + +enum bfi_tskim_i2h { + BFI_TSKIM_I2H_TM_RSP = BFA_I2HM(1), +}; + +struct bfi_tskim_req_s { + struct bfi_mhdr_s mh; /* Common msg header */ + u16 tsk_tag; /* task management tag */ + u16 itn_fhdl; /* itn firmware handle */ + lun_t lun; /* LU number */ + u8 tm_flags; /* see fcp_tm_cmnd_t */ + u8 t_secs; /* Timeout value in seconds */ + u8 rsvd[2]; +}; + +struct bfi_tskim_abortreq_s { + struct bfi_mhdr_s mh; /* Common msg header */ + u16 tsk_tag; /* task management tag */ + u16 rsvd; +}; + +enum bfi_tskim_status { + /* + * Following are FCP-4 spec defined status codes, + * **DO NOT CHANGE THEM ** + */ + BFI_TSKIM_STS_OK = 0, + BFI_TSKIM_STS_NOT_SUPP = 4, + BFI_TSKIM_STS_FAILED = 5, + + /** + * Defined by BFA + */ + BFI_TSKIM_STS_TIMEOUT = 10, /* TM request timedout */ + BFI_TSKIM_STS_ABORTED = 11, /* Aborted on host request */ +}; + +struct bfi_tskim_rsp_s { + struct bfi_mhdr_s mh; /* Common msg header */ + u16 tsk_tag; /* task mgmt cmnd tag */ + u8 tsk_status; /* @ref bfi_tskim_status */ + u8 rsvd; +}; + +#pragma pack() + +#endif /* __BFI_FCPIM_H__ */ + diff --git a/drivers/scsi/bfa/include/bfi/bfi_fcxp.h b/drivers/scsi/bfa/include/bfi/bfi_fcxp.h new file mode 100644 index 000000000000..e0e995a32828 --- /dev/null +++ b/drivers/scsi/bfa/include/bfi/bfi_fcxp.h @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2005-2009 Brocade Communications Systems, Inc. + * All rights reserved + * www.brocade.com + * + * Linux driver for Brocade Fibre Channel Host Bus Adapter. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (GPL) Version 2 as + * published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#ifndef __BFI_FCXP_H__ +#define __BFI_FCXP_H__ + +#include "bfi.h" + +#pragma pack(1) + +enum bfi_fcxp_h2i { + BFI_FCXP_H2I_SEND_REQ = 1, +}; + +enum bfi_fcxp_i2h { + BFI_FCXP_I2H_SEND_RSP = BFA_I2HM(1), +}; + +#define BFA_FCXP_MAX_SGES 2 + +/** + * FCXP send request structure + */ +struct bfi_fcxp_send_req_s { + struct bfi_mhdr_s mh; /* Common msg header */ + u16 fcxp_tag; /* driver request tag */ + u16 max_frmsz; /* max send frame size */ + u16 vf_id; /* vsan tag if applicable */ + u16 rport_fw_hndl; /* FW Handle for the remote port */ + u8 class; /* FC class used for req/rsp */ + u8 rsp_timeout; /* timeout in secs, 0-no response */ + u8 cts; /* continue sequence */ + u8 lp_tag; /* lport tag */ + struct fchs_s fchs; /* request FC header structure */ + u32 req_len; /* request payload length */ + u32 rsp_maxlen; /* max response length expected */ + struct bfi_sge_s req_sge[BFA_FCXP_MAX_SGES]; /* request buf */ + struct bfi_sge_s rsp_sge[BFA_FCXP_MAX_SGES]; /* response buf */ +}; + +/** + * FCXP send response structure + */ +struct bfi_fcxp_send_rsp_s { + struct bfi_mhdr_s mh; /* Common msg header */ + u16 fcxp_tag; /* send request tag */ + u8 req_status; /* request status */ + u8 rsvd; + u32 rsp_len; /* actual response length */ + u32 residue_len; /* residual response length */ + struct fchs_s fchs; /* response FC header structure */ +}; + +#pragma pack() + +#endif /* __BFI_FCXP_H__ */ + diff --git a/drivers/scsi/bfa/include/bfi/bfi_ioc.h b/drivers/scsi/bfa/include/bfi/bfi_ioc.h new file mode 100644 index 000000000000..026e9c06ae97 --- /dev/null +++ b/drivers/scsi/bfa/include/bfi/bfi_ioc.h @@ -0,0 +1,202 @@ +/* + * Copyright (c) 2005-2009 Brocade Communications Systems, Inc. + * All rights reserved + * www.brocade.com + * + * Linux driver for Brocade Fibre Channel Host Bus Adapter. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (GPL) Version 2 as + * published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#ifndef __BFI_IOC_H__ +#define __BFI_IOC_H__ + +#include "bfi.h" +#include <defs/bfa_defs_ioc.h> + +#pragma pack(1) + +enum bfi_ioc_h2i_msgs { + BFI_IOC_H2I_ENABLE_REQ = 1, + BFI_IOC_H2I_DISABLE_REQ = 2, + BFI_IOC_H2I_GETATTR_REQ = 3, + BFI_IOC_H2I_DBG_SYNC = 4, + BFI_IOC_H2I_DBG_DUMP = 5, +}; + +enum bfi_ioc_i2h_msgs { + BFI_IOC_I2H_ENABLE_REPLY = BFA_I2HM(1), + BFI_IOC_I2H_DISABLE_REPLY = BFA_I2HM(2), + BFI_IOC_I2H_GETATTR_REPLY = BFA_I2HM(3), + BFI_IOC_I2H_READY_EVENT = BFA_I2HM(4), + BFI_IOC_I2H_HBEAT = BFA_I2HM(5), +}; + +/** + * BFI_IOC_H2I_GETATTR_REQ message + */ +struct bfi_ioc_getattr_req_s { + struct bfi_mhdr_s mh; + union bfi_addr_u attr_addr; +}; + +struct bfi_ioc_attr_s { + wwn_t mfg_wwn; + mac_t mfg_mac; + u16 rsvd_a; + char brcd_serialnum[STRSZ(BFA_MFG_SERIALNUM_SIZE)]; + u8 pcie_gen; + u8 pcie_lanes_orig; + u8 pcie_lanes; + u8 rx_bbcredit; /* receive buffer credits */ + u32 adapter_prop; /* adapter properties */ + u16 maxfrsize; /* max receive frame size */ + char asic_rev; + u8 rsvd_b; + char fw_version[BFA_VERSION_LEN]; + char optrom_version[BFA_VERSION_LEN]; + struct bfa_mfg_vpd_s vpd; +}; + +/** + * BFI_IOC_I2H_GETATTR_REPLY message + */ +struct bfi_ioc_getattr_reply_s { + struct bfi_mhdr_s mh; /* Common msg header */ + u8 status; /* cfg reply status */ + u8 rsvd[3]; +}; + +/** + * Firmware memory page offsets + */ +#define BFI_IOC_SMEM_PG0_CB (0x40) +#define BFI_IOC_SMEM_PG0_CT (0x180) + +/** + * Firmware trace offset + */ +#define BFI_IOC_TRC_OFF (0x4b00) +#define BFI_IOC_TRC_ENTS 256 + +#define BFI_IOC_FW_SIGNATURE (0xbfadbfad) +#define BFI_IOC_MD5SUM_SZ 4 +struct bfi_ioc_image_hdr_s { + u32 signature; /* constant signature */ + u32 rsvd_a; + u32 exec; /* exec vector */ + u32 param; /* parameters */ + u32 rsvd_b[4]; + u32 md5sum[BFI_IOC_MD5SUM_SZ]; +}; + +/** + * BFI_IOC_I2H_READY_EVENT message + */ +struct bfi_ioc_rdy_event_s { + struct bfi_mhdr_s mh; /* common msg header */ + u8 init_status; /* init event status */ + u8 rsvd[3]; +}; + +struct bfi_ioc_hbeat_s { + struct bfi_mhdr_s mh; /* common msg header */ + u32 hb_count; /* current heart beat count */ +}; + +/** + * IOC hardware/firmware state + */ +enum bfi_ioc_state { + BFI_IOC_UNINIT = 0, /* not initialized */ + BFI_IOC_INITING = 1, /* h/w is being initialized */ + BFI_IOC_HWINIT = 2, /* h/w is initialized */ + BFI_IOC_CFG = 3, /* IOC configuration in progress */ + BFI_IOC_OP = 4, /* IOC is operational */ + BFI_IOC_DISABLING = 5, /* IOC is being disabled */ + BFI_IOC_DISABLED = 6, /* IOC is disabled */ + BFI_IOC_CFG_DISABLED = 7, /* IOC is being disabled;transient */ + BFI_IOC_HBFAIL = 8, /* IOC heart-beat failure */ + BFI_IOC_MEMTEST = 9, /* IOC is doing memtest */ +}; + +#define BFI_IOC_ENDIAN_SIG 0x12345678 + +enum { + BFI_ADAPTER_TYPE_FC = 0x01, /* FC adapters */ + BFI_ADAPTER_TYPE_MK = 0x0f0000, /* adapter type mask */ + BFI_ADAPTER_TYPE_SH = 16, /* adapter type shift */ + BFI_ADAPTER_NPORTS_MK = 0xff00, /* number of ports mask */ + BFI_ADAPTER_NPORTS_SH = 8, /* number of ports shift */ + BFI_ADAPTER_SPEED_MK = 0xff, /* adapter speed mask */ + BFI_ADAPTER_SPEED_SH = 0, /* adapter speed shift */ + BFI_ADAPTER_PROTO = 0x100000, /* prototype adapaters */ + BFI_ADAPTER_TTV = 0x200000, /* TTV debug capable */ + BFI_ADAPTER_UNSUPP = 0x400000, /* unknown adapter type */ +}; + +#define BFI_ADAPTER_GETP(__prop,__adap_prop) \ + (((__adap_prop) & BFI_ADAPTER_ ## __prop ## _MK) >> \ + BFI_ADAPTER_ ## __prop ## _SH) +#define BFI_ADAPTER_SETP(__prop, __val) \ + ((__val) << BFI_ADAPTER_ ## __prop ## _SH) +#define BFI_ADAPTER_IS_PROTO(__adap_type) \ + ((__adap_type) & BFI_ADAPTER_PROTO) +#define BFI_ADAPTER_IS_TTV(__adap_type) \ + ((__adap_type) & BFI_ADAPTER_TTV) +#define BFI_ADAPTER_IS_UNSUPP(__adap_type) \ + ((__adap_type) & BFI_ADAPTER_UNSUPP) +#define BFI_ADAPTER_IS_SPECIAL(__adap_type) \ + ((__adap_type) & (BFI_ADAPTER_TTV | BFI_ADAPTER_PROTO | \ + BFI_ADAPTER_UNSUPP)) + +/** + * BFI_IOC_H2I_ENABLE_REQ & BFI_IOC_H2I_DISABLE_REQ messages + */ +struct bfi_ioc_ctrl_req_s { + struct bfi_mhdr_s mh; + u8 ioc_class; + u8 rsvd[3]; +}; + +/** + * BFI_IOC_I2H_ENABLE_REPLY & BFI_IOC_I2H_DISABLE_REPLY messages + */ +struct bfi_ioc_ctrl_reply_s { + struct bfi_mhdr_s mh; /* Common msg header */ + u8 status; /* enable/disable status */ + u8 rsvd[3]; +}; + +#define BFI_IOC_MSGSZ 8 +/** + * H2I Messages + */ +union bfi_ioc_h2i_msg_u { + struct bfi_mhdr_s mh; + struct bfi_ioc_ctrl_req_s enable_req; + struct bfi_ioc_ctrl_req_s disable_req; + struct bfi_ioc_getattr_req_s getattr_req; + u32 mboxmsg[BFI_IOC_MSGSZ]; +}; + +/** + * I2H Messages + */ +union bfi_ioc_i2h_msg_u { + struct bfi_mhdr_s mh; + struct bfi_ioc_rdy_event_s rdy_event; + u32 mboxmsg[BFI_IOC_MSGSZ]; +}; + +#pragma pack() + +#endif /* __BFI_IOC_H__ */ + diff --git a/drivers/scsi/bfa/include/bfi/bfi_iocfc.h b/drivers/scsi/bfa/include/bfi/bfi_iocfc.h new file mode 100644 index 000000000000..c3760df72575 --- /dev/null +++ b/drivers/scsi/bfa/include/bfi/bfi_iocfc.h @@ -0,0 +1,177 @@ +/* + * Copyright (c) 2005-2009 Brocade Communications Systems, Inc. + * All rights reserved + * www.brocade.com + * + * Linux driver for Brocade Fibre Channel Host Bus Adapter. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (GPL) Version 2 as + * published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#ifndef __BFI_IOCFC_H__ +#define __BFI_IOCFC_H__ + +#include "bfi.h" +#include <defs/bfa_defs_ioc.h> +#include <defs/bfa_defs_iocfc.h> +#include <defs/bfa_defs_boot.h> + +#pragma pack(1) + +enum bfi_iocfc_h2i_msgs { + BFI_IOCFC_H2I_CFG_REQ = 1, + BFI_IOCFC_H2I_GET_STATS_REQ = 2, + BFI_IOCFC_H2I_CLEAR_STATS_REQ = 3, + BFI_IOCFC_H2I_SET_INTR_REQ = 4, + BFI_IOCFC_H2I_UPDATEQ_REQ = 5, +}; + +enum bfi_iocfc_i2h_msgs { + BFI_IOCFC_I2H_CFG_REPLY = BFA_I2HM(1), + BFI_IOCFC_I2H_GET_STATS_RSP = BFA_I2HM(2), + BFI_IOCFC_I2H_CLEAR_STATS_RSP = BFA_I2HM(3), + BFI_IOCFC_I2H_UPDATEQ_RSP = BFA_I2HM(5), +}; + +struct bfi_iocfc_cfg_s { + u8 num_cqs; /* Number of CQs to be used */ + u8 sense_buf_len; /* SCSI sense length */ + u8 trunk_enabled; /* port trunking enabled */ + u8 trunk_ports; /* trunk ports bit map */ + u32 endian_sig; /* endian signature of host */ + + /** + * Request and response circular queue base addresses, size and + * shadow index pointers. + */ + union bfi_addr_u req_cq_ba[BFI_IOC_MAX_CQS]; + union bfi_addr_u req_shadow_ci[BFI_IOC_MAX_CQS]; + u16 req_cq_elems[BFI_IOC_MAX_CQS]; + union bfi_addr_u rsp_cq_ba[BFI_IOC_MAX_CQS]; + union bfi_addr_u rsp_shadow_pi[BFI_IOC_MAX_CQS]; + u16 rsp_cq_elems[BFI_IOC_MAX_CQS]; + + union bfi_addr_u stats_addr; /* DMA-able address for stats */ + union bfi_addr_u cfgrsp_addr; /* config response dma address */ + union bfi_addr_u ioim_snsbase; /* IO sense buffer base address */ + struct bfa_iocfc_intr_attr_s intr_attr; /* IOC interrupt attributes */ +}; + +/** + * Boot target wwn information for this port. This contains either the stored + * or discovered boot target port wwns for the port. + */ +struct bfi_iocfc_bootwwns { + wwn_t wwn[BFA_BOOT_BOOTLUN_MAX]; + u8 nwwns; + u8 rsvd[7]; +}; + +struct bfi_iocfc_cfgrsp_s { + struct bfa_iocfc_fwcfg_s fwcfg; + struct bfa_iocfc_intr_attr_s intr_attr; + struct bfi_iocfc_bootwwns bootwwns; +}; + +/** + * BFI_IOCFC_H2I_CFG_REQ message + */ +struct bfi_iocfc_cfg_req_s { + struct bfi_mhdr_s mh; + union bfi_addr_u ioc_cfg_dma_addr; +}; + +/** + * BFI_IOCFC_I2H_CFG_REPLY message + */ +struct bfi_iocfc_cfg_reply_s { + struct bfi_mhdr_s mh; /* Common msg header */ + u8 cfg_success; /* cfg reply status */ + u8 lpu_bm; /* LPUs assigned for this IOC */ + u8 rsvd[2]; +}; + +/** + * BFI_IOCFC_H2I_GET_STATS_REQ & BFI_IOCFC_H2I_CLEAR_STATS_REQ messages + */ +struct bfi_iocfc_stats_req_s { + struct bfi_mhdr_s mh; /* msg header */ + u32 msgtag; /* msgtag for reply */ +}; + +/** + * BFI_IOCFC_I2H_GET_STATS_RSP & BFI_IOCFC_I2H_CLEAR_STATS_RSP messages + */ +struct bfi_iocfc_stats_rsp_s { + struct bfi_mhdr_s mh; /* common msg header */ + u8 status; /* reply status */ + u8 rsvd[3]; + u32 msgtag; /* msgtag for reply */ +}; + +/** + * BFI_IOCFC_H2I_SET_INTR_REQ message + */ +struct bfi_iocfc_set_intr_req_s { + struct bfi_mhdr_s mh; /* common msg header */ + u8 coalesce; /* enable intr coalescing*/ + u8 rsvd[3]; + u16 delay; /* delay timer 0..1125us */ + u16 latency; /* latency timer 0..225us */ +}; + +/** + * BFI_IOCFC_H2I_UPDATEQ_REQ message + */ +struct bfi_iocfc_updateq_req_s { + struct bfi_mhdr_s mh; /* common msg header */ + u32 reqq_ba; /* reqq base addr */ + u32 rspq_ba; /* rspq base addr */ + u32 reqq_sci; /* reqq shadow ci */ + u32 rspq_spi; /* rspq shadow pi */ +}; + +/** + * BFI_IOCFC_I2H_UPDATEQ_RSP message + */ +struct bfi_iocfc_updateq_rsp_s { + struct bfi_mhdr_s mh; /* common msg header */ + u8 status; /* updateq status */ + u8 rsvd[3]; +}; + +/** + * H2I Messages + */ +union bfi_iocfc_h2i_msg_u { + struct bfi_mhdr_s mh; + struct bfi_iocfc_cfg_req_s cfg_req; + struct bfi_iocfc_stats_req_s stats_get; + struct bfi_iocfc_stats_req_s stats_clr; + struct bfi_iocfc_updateq_req_s updateq_req; + u32 mboxmsg[BFI_IOC_MSGSZ]; +}; + +/** + * I2H Messages + */ +union bfi_iocfc_i2h_msg_u { + struct bfi_mhdr_s mh; + struct bfi_iocfc_cfg_reply_s cfg_reply; + struct bfi_iocfc_stats_rsp_s stats_get_rsp; + struct bfi_iocfc_stats_rsp_s stats_clr_rsp; + struct bfi_iocfc_updateq_rsp_s updateq_rsp; + u32 mboxmsg[BFI_IOC_MSGSZ]; +}; + +#pragma pack() + +#endif /* __BFI_IOCFC_H__ */ + diff --git a/drivers/scsi/bfa/include/bfi/bfi_lport.h b/drivers/scsi/bfa/include/bfi/bfi_lport.h new file mode 100644 index 000000000000..29010614bac9 --- /dev/null +++ b/drivers/scsi/bfa/include/bfi/bfi_lport.h @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2005-2009 Brocade Communications Systems, Inc. + * All rights reserved + * www.brocade.com + * + * Linux driver for Brocade Fibre Channel Host Bus Adapter. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (GPL) Version 2 as + * published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#ifndef __BFI_LPORT_H__ +#define __BFI_LPORT_H__ + +#include <bfi/bfi.h> + +#pragma pack(1) + +enum bfi_lport_h2i_msgs { + BFI_LPORT_H2I_CREATE_REQ = 1, + BFI_LPORT_H2I_DELETE_REQ = 2, +}; + +enum bfi_lport_i2h_msgs { + BFI_LPORT_I2H_CREATE_RSP = BFA_I2HM(1), + BFI_LPORT_I2H_DELETE_RSP = BFA_I2HM(2), + BFI_LPORT_I2H_ONLINE = BFA_I2HM(3), + BFI_LPORT_I2H_OFFLINE = BFA_I2HM(4), +}; + +#define BFI_LPORT_MAX_SYNNAME 64 + +enum bfi_lport_role_e { + BFI_LPORT_ROLE_FCPIM = 1, + BFI_LPORT_ROLE_FCPTM = 2, + BFI_LPORT_ROLE_IPFC = 4, +}; + +struct bfi_lport_create_req_s { + bfi_mhdr_t mh; /* common msg header */ + u16 fabric_fwhdl; /* parent fabric instance */ + u8 roles; /* lport FC-4 roles */ + u8 rsvd; + wwn_t pwwn; /* port name */ + wwn_t nwwn; /* node name */ + u8 symname[BFI_LPORT_MAX_SYNNAME]; +}; + +struct bfi_lport_create_rsp_s { + bfi_mhdr_t mh; /* common msg header */ + u8 status; /* lport creation status */ + u8 rsvd[3]; +}; + +struct bfi_lport_delete_req_s { + bfi_mhdr_t mh; /* common msg header */ + u16 fw_handle; /* firmware lport handle */ + u16 rsvd; +}; + +struct bfi_lport_delete_rsp_s { + bfi_mhdr_t mh; /* common msg header */ + u16 bfa_handle; /* host lport handle */ + u8 status; /* lport deletion status */ + u8 rsvd; +}; + +union bfi_lport_h2i_msg_u { + bfi_msg_t *msg; + struct bfi_lport_create_req_s *create_req; + struct bfi_lport_delete_req_s *delete_req; +}; + +union bfi_lport_i2h_msg_u { + bfi_msg_t *msg; + struct bfi_lport_create_rsp_s *create_rsp; + struct bfi_lport_delete_rsp_s *delete_rsp; +}; + +#pragma pack() + +#endif /* __BFI_LPORT_H__ */ + diff --git a/drivers/scsi/bfa/include/bfi/bfi_lps.h b/drivers/scsi/bfa/include/bfi/bfi_lps.h new file mode 100644 index 000000000000..414b0e30f6ef --- /dev/null +++ b/drivers/scsi/bfa/include/bfi/bfi_lps.h @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2005-2009 Brocade Communications Systems, Inc. + * All rights reserved + * www.brocade.com + * + * Linux driver for Brocade Fibre Channel Host Bus Adapter. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (GPL) Version 2 as + * published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#ifndef __BFI_LPS_H__ +#define __BFI_LPS_H__ + +#include <bfi/bfi.h> + +#pragma pack(1) + +enum bfi_lps_h2i_msgs { + BFI_LPS_H2I_LOGIN_REQ = 1, + BFI_LPS_H2I_LOGOUT_REQ = 2, +}; + +enum bfi_lps_i2h_msgs { + BFI_LPS_H2I_LOGIN_RSP = BFA_I2HM(1), + BFI_LPS_H2I_LOGOUT_RSP = BFA_I2HM(2), +}; + +struct bfi_lps_login_req_s { + struct bfi_mhdr_s mh; /* common msg header */ + u8 lp_tag; + u8 alpa; + u16 pdu_size; + wwn_t pwwn; + wwn_t nwwn; + u8 fdisc; + u8 auth_en; + u8 rsvd[2]; +}; + +struct bfi_lps_login_rsp_s { + struct bfi_mhdr_s mh; /* common msg header */ + u8 lp_tag; + u8 status; + u8 lsrjt_rsn; + u8 lsrjt_expl; + wwn_t port_name; + wwn_t node_name; + u16 bb_credit; + u8 f_port; + u8 npiv_en; + u32 lp_pid : 24; + u32 auth_req : 8; + mac_t lp_mac; + mac_t fcf_mac; + u8 ext_status; + u8 brcd_switch;/* attached peer is brcd switch */ +}; + +struct bfi_lps_logout_req_s { + struct bfi_mhdr_s mh; /* common msg header */ + u8 lp_tag; + u8 rsvd[3]; + wwn_t port_name; +}; + +struct bfi_lps_logout_rsp_s { + struct bfi_mhdr_s mh; /* common msg header */ + u8 lp_tag; + u8 status; + u8 rsvd[2]; +}; + +union bfi_lps_h2i_msg_u { + struct bfi_mhdr_s *msg; + struct bfi_lps_login_req_s *login_req; + struct bfi_lps_logout_req_s *logout_req; +}; + +union bfi_lps_i2h_msg_u { + struct bfi_msg_s *msg; + struct bfi_lps_login_rsp_s *login_rsp; + struct bfi_lps_logout_rsp_s *logout_rsp; +}; + +#pragma pack() + +#endif /* __BFI_LPS_H__ */ + + diff --git a/drivers/scsi/bfa/include/bfi/bfi_port.h b/drivers/scsi/bfa/include/bfi/bfi_port.h new file mode 100644 index 000000000000..3ec3bea110ba --- /dev/null +++ b/drivers/scsi/bfa/include/bfi/bfi_port.h @@ -0,0 +1,115 @@ +/* + * Copyright (c) 2005-2009 Brocade Communications Systems, Inc. + * All rights reserved + * www.brocade.com + * + * Linux driver for Brocade Fibre Channel Host Bus Adapter. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (GPL) Version 2 as + * published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ +#ifndef __BFI_PORT_H__ +#define __BFI_PORT_H__ + +#include <bfi/bfi.h> +#include <defs/bfa_defs_pport.h> + +#pragma pack(1) + +enum bfi_port_h2i { + BFI_PORT_H2I_ENABLE_REQ = (1), + BFI_PORT_H2I_DISABLE_REQ = (2), + BFI_PORT_H2I_GET_STATS_REQ = (3), + BFI_PORT_H2I_CLEAR_STATS_REQ = (4), +}; + +enum bfi_port_i2h { + BFI_PORT_I2H_ENABLE_RSP = BFA_I2HM(1), + BFI_PORT_I2H_DISABLE_RSP = BFA_I2HM(2), + BFI_PORT_I2H_GET_STATS_RSP = BFA_I2HM(3), + BFI_PORT_I2H_CLEAR_STATS_RSP = BFA_I2HM(4), +}; + +/** + * Generic REQ type + */ +struct bfi_port_generic_req_s { + struct bfi_mhdr_s mh; /* msg header */ + u32 msgtag; /* msgtag for reply */ + u32 rsvd; +}; + +/** + * Generic RSP type + */ +struct bfi_port_generic_rsp_s { + struct bfi_mhdr_s mh; /* common msg header */ + u8 status; /* port enable status */ + u8 rsvd[3]; + u32 msgtag; /* msgtag for reply */ +}; + +/** + * @todo + * BFI_PORT_H2I_ENABLE_REQ + */ + +/** + * @todo + * BFI_PORT_I2H_ENABLE_RSP + */ + +/** + * BFI_PORT_H2I_DISABLE_REQ + */ + +/** + * BFI_PORT_I2H_DISABLE_RSP + */ + +/** + * BFI_PORT_H2I_GET_STATS_REQ + */ +struct bfi_port_get_stats_req_s { + struct bfi_mhdr_s mh; /* common msg header */ + union bfi_addr_u dma_addr; +}; + +/** + * BFI_PORT_I2H_GET_STATS_RSP + */ + +/** + * BFI_PORT_H2I_CLEAR_STATS_REQ + */ + +/** + * BFI_PORT_I2H_CLEAR_STATS_RSP + */ + +union bfi_port_h2i_msg_u { + struct bfi_mhdr_s mh; + struct bfi_port_generic_req_s enable_req; + struct bfi_port_generic_req_s disable_req; + struct bfi_port_get_stats_req_s getstats_req; + struct bfi_port_generic_req_s clearstats_req; +}; + +union bfi_port_i2h_msg_u { + struct bfi_mhdr_s mh; + struct bfi_port_generic_rsp_s enable_rsp; + struct bfi_port_generic_rsp_s disable_rsp; + struct bfi_port_generic_rsp_s getstats_rsp; + struct bfi_port_generic_rsp_s clearstats_rsp; +}; + +#pragma pack() + +#endif /* __BFI_PORT_H__ */ + diff --git a/drivers/scsi/bfa/include/bfi/bfi_pport.h b/drivers/scsi/bfa/include/bfi/bfi_pport.h new file mode 100644 index 000000000000..c96d246851af --- /dev/null +++ b/drivers/scsi/bfa/include/bfi/bfi_pport.h @@ -0,0 +1,184 @@ +/* + * Copyright (c) 2005-2009 Brocade Communications Systems, Inc. + * All rights reserved + * www.brocade.com + * + * Linux driver for Brocade Fibre Channel Host Bus Adapter. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (GPL) Version 2 as + * published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ +#ifndef __BFI_PPORT_H__ +#define __BFI_PPORT_H__ + +#include <bfi/bfi.h> +#include <defs/bfa_defs_pport.h> + +#pragma pack(1) + +enum bfi_pport_h2i { + BFI_PPORT_H2I_ENABLE_REQ = (1), + BFI_PPORT_H2I_DISABLE_REQ = (2), + BFI_PPORT_H2I_GET_STATS_REQ = (3), + BFI_PPORT_H2I_CLEAR_STATS_REQ = (4), + BFI_PPORT_H2I_SET_SVC_PARAMS_REQ = (5), + BFI_PPORT_H2I_ENABLE_RX_VF_TAG_REQ = (6), + BFI_PPORT_H2I_ENABLE_TX_VF_TAG_REQ = (7), + BFI_PPORT_H2I_GET_QOS_STATS_REQ = (8), + BFI_PPORT_H2I_CLEAR_QOS_STATS_REQ = (9), +}; + +enum bfi_pport_i2h { + BFI_PPORT_I2H_ENABLE_RSP = BFA_I2HM(1), + BFI_PPORT_I2H_DISABLE_RSP = BFA_I2HM(2), + BFI_PPORT_I2H_GET_STATS_RSP = BFA_I2HM(3), + BFI_PPORT_I2H_CLEAR_STATS_RSP = BFA_I2HM(4), + BFI_PPORT_I2H_SET_SVC_PARAMS_RSP = BFA_I2HM(5), + BFI_PPORT_I2H_ENABLE_RX_VF_TAG_RSP = BFA_I2HM(6), + BFI_PPORT_I2H_ENABLE_TX_VF_TAG_RSP = BFA_I2HM(7), + BFI_PPORT_I2H_EVENT = BFA_I2HM(8), + BFI_PPORT_I2H_GET_QOS_STATS_RSP = BFA_I2HM(9), + BFI_PPORT_I2H_CLEAR_QOS_STATS_RSP = BFA_I2HM(10), +}; + +/** + * Generic REQ type + */ +struct bfi_pport_generic_req_s { + struct bfi_mhdr_s mh; /* msg header */ + u32 msgtag; /* msgtag for reply */ +}; + +/** + * Generic RSP type + */ +struct bfi_pport_generic_rsp_s { + struct bfi_mhdr_s mh; /* common msg header */ + u8 status; /* port enable status */ + u8 rsvd[3]; + u32 msgtag; /* msgtag for reply */ +}; + +/** + * BFI_PPORT_H2I_ENABLE_REQ + */ +struct bfi_pport_enable_req_s { + struct bfi_mhdr_s mh; /* msg header */ + u32 rsvd1; + wwn_t nwwn; /* node wwn of physical port */ + wwn_t pwwn; /* port wwn of physical port */ + struct bfa_pport_cfg_s port_cfg; /* port configuration */ + union bfi_addr_u stats_dma_addr; /* DMA address for stats */ + u32 msgtag; /* msgtag for reply */ + u32 rsvd2; +}; + +/** + * BFI_PPORT_I2H_ENABLE_RSP + */ +#define bfi_pport_enable_rsp_t struct bfi_pport_generic_rsp_s + +/** + * BFI_PPORT_H2I_DISABLE_REQ + */ +#define bfi_pport_disable_req_t struct bfi_pport_generic_req_s + +/** + * BFI_PPORT_I2H_DISABLE_RSP + */ +#define bfi_pport_disable_rsp_t struct bfi_pport_generic_rsp_s + +/** + * BFI_PPORT_H2I_GET_STATS_REQ + */ +#define bfi_pport_get_stats_req_t struct bfi_pport_generic_req_s + +/** + * BFI_PPORT_I2H_GET_STATS_RSP + */ +#define bfi_pport_get_stats_rsp_t struct bfi_pport_generic_rsp_s + +/** + * BFI_PPORT_H2I_CLEAR_STATS_REQ + */ +#define bfi_pport_clear_stats_req_t struct bfi_pport_generic_req_s + +/** + * BFI_PPORT_I2H_CLEAR_STATS_RSP + */ +#define bfi_pport_clear_stats_rsp_t struct bfi_pport_generic_rsp_s + +/** + * BFI_PPORT_H2I_GET_QOS_STATS_REQ + */ +#define bfi_pport_get_qos_stats_req_t struct bfi_pport_generic_req_s + +/** + * BFI_PPORT_H2I_GET_QOS_STATS_RSP + */ +#define bfi_pport_get_qos_stats_rsp_t struct bfi_pport_generic_rsp_s + +/** + * BFI_PPORT_H2I_CLEAR_QOS_STATS_REQ + */ +#define bfi_pport_clear_qos_stats_req_t struct bfi_pport_generic_req_s + +/** + * BFI_PPORT_H2I_CLEAR_QOS_STATS_RSP + */ +#define bfi_pport_clear_qos_stats_rsp_t struct bfi_pport_generic_rsp_s + +/** + * BFI_PPORT_H2I_SET_SVC_PARAMS_REQ + */ +struct bfi_pport_set_svc_params_req_s { + struct bfi_mhdr_s mh; /* msg header */ + u16 tx_bbcredit; /* Tx credits */ + u16 rsvd; +}; + +/** + * BFI_PPORT_I2H_SET_SVC_PARAMS_RSP + */ + +/** + * BFI_PPORT_I2H_EVENT + */ +struct bfi_pport_event_s { + struct bfi_mhdr_s mh; /* common msg header */ + struct bfa_pport_link_s link_state; +}; + +union bfi_pport_h2i_msg_u { + struct bfi_mhdr_s *mhdr; + struct bfi_pport_enable_req_s *penable; + struct bfi_pport_generic_req_s *pdisable; + struct bfi_pport_generic_req_s *pgetstats; + struct bfi_pport_generic_req_s *pclearstats; + struct bfi_pport_set_svc_params_req_s *psetsvcparams; + struct bfi_pport_get_qos_stats_req_s *pgetqosstats; + struct bfi_pport_generic_req_s *pclearqosstats; +}; + +union bfi_pport_i2h_msg_u { + struct bfi_msg_s *msg; + struct bfi_pport_generic_rsp_s *enable_rsp; + struct bfi_pport_disable_rsp_s *disable_rsp; + struct bfi_pport_generic_rsp_s *getstats_rsp; + struct bfi_pport_clear_stats_rsp_s *clearstats_rsp; + struct bfi_pport_set_svc_params_rsp_s *setsvcparasm_rsp; + struct bfi_pport_get_qos_stats_rsp_s *getqosstats_rsp; + struct bfi_pport_clear_qos_stats_rsp_s *clearqosstats_rsp; + struct bfi_pport_event_s *event; +}; + +#pragma pack() + +#endif /* __BFI_PPORT_H__ */ + diff --git a/drivers/scsi/bfa/include/bfi/bfi_rport.h b/drivers/scsi/bfa/include/bfi/bfi_rport.h new file mode 100644 index 000000000000..3520f55f09d7 --- /dev/null +++ b/drivers/scsi/bfa/include/bfi/bfi_rport.h @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2005-2009 Brocade Communications Systems, Inc. + * All rights reserved + * www.brocade.com + * + * Linux driver for Brocade Fibre Channel Host Bus Adapter. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (GPL) Version 2 as + * published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#ifndef __BFI_RPORT_H__ +#define __BFI_RPORT_H__ + +#include <bfi/bfi.h> + +#pragma pack(1) + +enum bfi_rport_h2i_msgs { + BFI_RPORT_H2I_CREATE_REQ = 1, + BFI_RPORT_H2I_DELETE_REQ = 2, + BFI_RPORT_H2I_SET_SPEED_REQ = 3, +}; + +enum bfi_rport_i2h_msgs { + BFI_RPORT_I2H_CREATE_RSP = BFA_I2HM(1), + BFI_RPORT_I2H_DELETE_RSP = BFA_I2HM(2), + BFI_RPORT_I2H_QOS_SCN = BFA_I2HM(3), +}; + +struct bfi_rport_create_req_s { + struct bfi_mhdr_s mh; /* common msg header */ + u16 bfa_handle; /* host rport handle */ + u16 max_frmsz; /* max rcv pdu size */ + u32 pid : 24, /* remote port ID */ + lp_tag : 8; /* local port tag */ + u32 local_pid : 24, /* local port ID */ + cisc : 8; + u8 fc_class; /* supported FC classes */ + u8 vf_en; /* virtual fabric enable */ + u16 vf_id; /* virtual fabric ID */ +}; + +struct bfi_rport_create_rsp_s { + struct bfi_mhdr_s mh; /* common msg header */ + u8 status; /* rport creation status */ + u8 rsvd[3]; + u16 bfa_handle; /* host rport handle */ + u16 fw_handle; /* firmware rport handle */ + struct bfa_rport_qos_attr_s qos_attr; /* QoS Attributes */ +}; + +struct bfa_rport_speed_req_s { + struct bfi_mhdr_s mh; /* common msg header */ + u16 fw_handle; /* firmware rport handle */ + u8 speed; /*! rport's speed via RPSC */ + u8 rsvd; +}; + +struct bfi_rport_delete_req_s { + struct bfi_mhdr_s mh; /* common msg header */ + u16 fw_handle; /* firmware rport handle */ + u16 rsvd; +}; + +struct bfi_rport_delete_rsp_s { + struct bfi_mhdr_s mh; /* common msg header */ + u16 bfa_handle; /* host rport handle */ + u8 status; /* rport deletion status */ + u8 rsvd; +}; + +struct bfi_rport_qos_scn_s { + struct bfi_mhdr_s mh; /* common msg header */ + u16 bfa_handle; /* host rport handle */ + u16 rsvd; + struct bfa_rport_qos_attr_s old_qos_attr; /* Old QoS Attributes */ + struct bfa_rport_qos_attr_s new_qos_attr; /* New QoS Attributes */ +}; + +union bfi_rport_h2i_msg_u { + struct bfi_msg_s *msg; + struct bfi_rport_create_req_s *create_req; + struct bfi_rport_delete_req_s *delete_req; + struct bfi_rport_speed_req_s *speed_req; +}; + +union bfi_rport_i2h_msg_u { + struct bfi_msg_s *msg; + struct bfi_rport_create_rsp_s *create_rsp; + struct bfi_rport_delete_rsp_s *delete_rsp; + struct bfi_rport_qos_scn_s *qos_scn_evt; +}; + +#pragma pack() + +#endif /* __BFI_RPORT_H__ */ + diff --git a/drivers/scsi/bfa/include/bfi/bfi_uf.h b/drivers/scsi/bfa/include/bfi/bfi_uf.h new file mode 100644 index 000000000000..f328a9e7e622 --- /dev/null +++ b/drivers/scsi/bfa/include/bfi/bfi_uf.h @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2005-2009 Brocade Communications Systems, Inc. + * All rights reserved + * www.brocade.com + * + * Linux driver for Brocade Fibre Channel Host Bus Adapter. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (GPL) Version 2 as + * published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#ifndef __BFI_UF_H__ +#define __BFI_UF_H__ + +#include "bfi.h" + +#pragma pack(1) + +enum bfi_uf_h2i { + BFI_UF_H2I_BUF_POST = 1, +}; + +enum bfi_uf_i2h { + BFI_UF_I2H_FRM_RCVD = BFA_I2HM(1), +}; + +#define BFA_UF_MAX_SGES 2 + +struct bfi_uf_buf_post_s { + struct bfi_mhdr_s mh; /* Common msg header */ + u16 buf_tag; /* buffer tag */ + u16 buf_len; /* total buffer length */ + struct bfi_sge_s sge[BFA_UF_MAX_SGES]; /* buffer DMA SGEs */ +}; + +struct bfi_uf_frm_rcvd_s { + struct bfi_mhdr_s mh; /* Common msg header */ + u16 buf_tag; /* buffer tag */ + u16 rsvd; + u16 frm_len; /* received frame length */ + u16 xfr_len; /* tranferred length */ +}; + +#pragma pack() + +#endif /* __BFI_UF_H__ */ diff --git a/drivers/scsi/bfa/include/cna/bfa_cna_trcmod.h b/drivers/scsi/bfa/include/cna/bfa_cna_trcmod.h new file mode 100644 index 000000000000..43ba7064e81a --- /dev/null +++ b/drivers/scsi/bfa/include/cna/bfa_cna_trcmod.h @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2005-2009 Brocade Communications Systems, Inc. + * All rights reserved + * www.brocade.com + * + * Linux driver for Brocade Fibre Channel Host Bus Adapter. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (GPL) Version 2 as + * published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +/** + * bfa_cna_trcmod.h CNA trace modules + */ + +#ifndef __BFA_CNA_TRCMOD_H__ +#define __BFA_CNA_TRCMOD_H__ + +#include <cs/bfa_trc.h> + +/* + * !!! Only append to the enums defined here to avoid any versioning + * !!! needed between trace utility and driver version + */ +enum { + BFA_TRC_CNA_CEE = 1, + BFA_TRC_CNA_PORT = 2, +}; + +#endif /* __BFA_CNA_TRCMOD_H__ */ diff --git a/drivers/scsi/bfa/include/cna/cee/bfa_cee.h b/drivers/scsi/bfa/include/cna/cee/bfa_cee.h new file mode 100644 index 000000000000..77f297f68046 --- /dev/null +++ b/drivers/scsi/bfa/include/cna/cee/bfa_cee.h @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2005-2009 Brocade Communications Systems, Inc. + * All rights reserved + * www.brocade.com + * + * Linux driver for Brocade Fibre Channel Host Bus Adapter. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (GPL) Version 2 as + * published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#ifndef __BFA_CEE_H__ +#define __BFA_CEE_H__ + +#include <defs/bfa_defs_cee.h> +#include <bfa_ioc.h> +#include <cs/bfa_trc.h> +#include <cs/bfa_log.h> + +typedef void (*bfa_cee_get_attr_cbfn_t) (void *dev, bfa_status_t status); +typedef void (*bfa_cee_get_stats_cbfn_t) (void *dev, bfa_status_t status); +typedef void (*bfa_cee_reset_stats_cbfn_t) (void *dev, bfa_status_t status); +typedef void (*bfa_cee_hbfail_cbfn_t) (void *dev, bfa_status_t status); + +struct bfa_cee_cbfn_s { + bfa_cee_get_attr_cbfn_t get_attr_cbfn; + void *get_attr_cbarg; + bfa_cee_get_stats_cbfn_t get_stats_cbfn; + void *get_stats_cbarg; + bfa_cee_reset_stats_cbfn_t reset_stats_cbfn; + void *reset_stats_cbarg; +}; + +struct bfa_cee_s { + void *dev; + bfa_boolean_t get_attr_pending; + bfa_boolean_t get_stats_pending; + bfa_boolean_t reset_stats_pending; + bfa_status_t get_attr_status; + bfa_status_t get_stats_status; + bfa_status_t reset_stats_status; + struct bfa_cee_cbfn_s cbfn; + struct bfa_ioc_hbfail_notify_s hbfail; + struct bfa_trc_mod_s *trcmod; + struct bfa_log_mod_s *logmod; + struct bfa_cee_attr_s *attr; + struct bfa_cee_stats_s *stats; + struct bfa_dma_s attr_dma; + struct bfa_dma_s stats_dma; + struct bfa_ioc_s *ioc; + struct bfa_mbox_cmd_s get_cfg_mb; + struct bfa_mbox_cmd_s get_stats_mb; + struct bfa_mbox_cmd_s reset_stats_mb; +}; + +u32 bfa_cee_meminfo(void); +void bfa_cee_mem_claim(struct bfa_cee_s *cee, u8 *dma_kva, + u64 dma_pa); +void bfa_cee_attach(struct bfa_cee_s *cee, struct bfa_ioc_s *ioc, void *dev, + struct bfa_trc_mod_s *trcmod, + struct bfa_log_mod_s *logmod); +void bfa_cee_detach(struct bfa_cee_s *cee); +bfa_status_t bfa_cee_get_attr(struct bfa_cee_s *cee, + struct bfa_cee_attr_s *attr, + bfa_cee_get_attr_cbfn_t cbfn, void *cbarg); +bfa_status_t bfa_cee_get_stats(struct bfa_cee_s *cee, + struct bfa_cee_stats_s *stats, + bfa_cee_get_stats_cbfn_t cbfn, void *cbarg); +bfa_status_t bfa_cee_reset_stats(struct bfa_cee_s *cee, + bfa_cee_reset_stats_cbfn_t cbfn, void *cbarg); +#endif /* __BFA_CEE_H__ */ diff --git a/drivers/scsi/bfa/include/cna/port/bfa_port.h b/drivers/scsi/bfa/include/cna/port/bfa_port.h new file mode 100644 index 000000000000..7cbf17d3141b --- /dev/null +++ b/drivers/scsi/bfa/include/cna/port/bfa_port.h @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2005-2009 Brocade Communications Systems, Inc. + * All rights reserved + * www.brocade.com + * + * Linux driver for Brocade Fibre Channel Host Bus Adapter. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (GPL) Version 2 as + * published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#ifndef __BFA_PORT_H__ +#define __BFA_PORT_H__ + +#include <defs/bfa_defs_port.h> +#include <bfa_ioc.h> +#include <cs/bfa_trc.h> +#include <cs/bfa_log.h> + +typedef void (*bfa_port_stats_cbfn_t) (void *dev, bfa_status_t status); +typedef void (*bfa_port_endis_cbfn_t) (void *dev, bfa_status_t status); + +struct bfa_port_s { + void *dev; + struct bfa_ioc_s *ioc; + struct bfa_trc_mod_s *trcmod; + struct bfa_log_mod_s *logmod; + u32 msgtag; + bfa_boolean_t stats_busy; + struct bfa_mbox_cmd_s stats_mb; + bfa_port_stats_cbfn_t stats_cbfn; + void *stats_cbarg; + bfa_status_t stats_status; + union bfa_pport_stats_u *stats; + struct bfa_dma_s stats_dma; + bfa_boolean_t endis_pending; + struct bfa_mbox_cmd_s endis_mb; + bfa_port_endis_cbfn_t endis_cbfn; + void *endis_cbarg; + bfa_status_t endis_status; + struct bfa_ioc_hbfail_notify_s hbfail; +}; + +void bfa_port_attach(struct bfa_port_s *port, struct bfa_ioc_s *ioc, + void *dev, struct bfa_trc_mod_s *trcmod, + struct bfa_log_mod_s *logmod); +void bfa_port_detach(struct bfa_port_s *port); +void bfa_port_hbfail(void *arg); + +bfa_status_t bfa_port_get_stats(struct bfa_port_s *port, + union bfa_pport_stats_u *stats, + bfa_port_stats_cbfn_t cbfn, void *cbarg); +bfa_status_t bfa_port_clear_stats(struct bfa_port_s *port, + bfa_port_stats_cbfn_t cbfn, void *cbarg); +bfa_status_t bfa_port_enable(struct bfa_port_s *port, + bfa_port_endis_cbfn_t cbfn, void *cbarg); +bfa_status_t bfa_port_disable(struct bfa_port_s *port, + bfa_port_endis_cbfn_t cbfn, void *cbarg); +u32 bfa_port_meminfo(void); +void bfa_port_mem_claim(struct bfa_port_s *port, u8 *dma_kva, + u64 dma_pa); + +#endif /* __BFA_PORT_H__ */ diff --git a/drivers/scsi/bfa/include/cna/pstats/ethport_defs.h b/drivers/scsi/bfa/include/cna/pstats/ethport_defs.h new file mode 100644 index 000000000000..1563ee512218 --- /dev/null +++ b/drivers/scsi/bfa/include/cna/pstats/ethport_defs.h @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2005-2009 Brocade Communications Systems, Inc. + * All rights reserved. + * + * Linux driver for Brocade Fibre Channel Host Bus Adapter. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (GPL) Version 2 as + * published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#ifndef __ETHPORT_DEFS_H__ +#define __ETHPORT_DEFS_H__ + +struct bnad_drv_stats { + u64 netif_queue_stop; + u64 netif_queue_wakeup; + u64 tso4; + u64 tso6; + u64 tso_err; + u64 tcpcsum_offload; + u64 udpcsum_offload; + u64 csum_help; + u64 csum_help_err; + + u64 hw_stats_updates; + u64 netif_rx_schedule; + u64 netif_rx_complete; + u64 netif_rx_dropped; +}; +#endif diff --git a/drivers/scsi/bfa/include/cna/pstats/phyport_defs.h b/drivers/scsi/bfa/include/cna/pstats/phyport_defs.h new file mode 100644 index 000000000000..eb7548030d0f --- /dev/null +++ b/drivers/scsi/bfa/include/cna/pstats/phyport_defs.h @@ -0,0 +1,218 @@ +/* + * Copyright (c) 2005-2009 Brocade Communications Systems, Inc. + * All rights reserved. + * + * Linux driver for Brocade Fibre Channel Host Bus Adapter. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (GPL) Version 2 as + * published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#ifndef __PHYPORT_DEFS_H__ +#define __PHYPORT_DEFS_H__ + +#define BNA_TXF_ID_MAX 64 +#define BNA_RXF_ID_MAX 64 + +/* + * Statistics + */ + +/* + * TxF Frame Statistics + */ +struct bna_stats_txf { + u64 ucast_octets; + u64 ucast; + u64 ucast_vlan; + + u64 mcast_octets; + u64 mcast; + u64 mcast_vlan; + + u64 bcast_octets; + u64 bcast; + u64 bcast_vlan; + + u64 errors; + u64 filter_vlan; /* frames filtered due to VLAN */ + u64 filter_mac_sa; /* frames filtered due to SA check */ +}; + +/* + * RxF Frame Statistics + */ +struct bna_stats_rxf { + u64 ucast_octets; + u64 ucast; + u64 ucast_vlan; + + u64 mcast_octets; + u64 mcast; + u64 mcast_vlan; + + u64 bcast_octets; + u64 bcast; + u64 bcast_vlan; + u64 frame_drops; +}; + +/* + * FC Tx Frame Statistics + */ +struct bna_stats_fc_tx { + u64 txf_ucast_octets; + u64 txf_ucast; + u64 txf_ucast_vlan; + + u64 txf_mcast_octets; + u64 txf_mcast; + u64 txf_mcast_vlan; + + u64 txf_bcast_octets; + u64 txf_bcast; + u64 txf_bcast_vlan; + + u64 txf_parity_errors; + u64 txf_timeout; + u64 txf_fid_parity_errors; +}; + +/* + * FC Rx Frame Statistics + */ +struct bna_stats_fc_rx { + u64 rxf_ucast_octets; + u64 rxf_ucast; + u64 rxf_ucast_vlan; + + u64 rxf_mcast_octets; + u64 rxf_mcast; + u64 rxf_mcast_vlan; + + u64 rxf_bcast_octets; + u64 rxf_bcast; + u64 rxf_bcast_vlan; +}; + +/* + * RAD Frame Statistics + */ +struct cna_stats_rad { + u64 rx_frames; + u64 rx_octets; + u64 rx_vlan_frames; + + u64 rx_ucast; + u64 rx_ucast_octets; + u64 rx_ucast_vlan; + + u64 rx_mcast; + u64 rx_mcast_octets; + u64 rx_mcast_vlan; + + u64 rx_bcast; + u64 rx_bcast_octets; + u64 rx_bcast_vlan; + + u64 rx_drops; +}; + +/* + * BPC Tx Registers + */ +struct cna_stats_bpc_tx { + u64 tx_pause[8]; + u64 tx_zero_pause[8]; /* Pause cancellation */ + u64 tx_first_pause[8]; /* Pause initiation rather + *than retention */ +}; + +/* + * BPC Rx Registers + */ +struct cna_stats_bpc_rx { + u64 rx_pause[8]; + u64 rx_zero_pause[8]; /* Pause cancellation */ + u64 rx_first_pause[8]; /* Pause initiation rather + *than retention */ +}; + +/* + * MAC Rx Statistics + */ +struct cna_stats_mac_rx { + u64 frame_64; /* both rx and tx counter */ + u64 frame_65_127; /* both rx and tx counter */ + u64 frame_128_255; /* both rx and tx counter */ + u64 frame_256_511; /* both rx and tx counter */ + u64 frame_512_1023; /* both rx and tx counter */ + u64 frame_1024_1518; /* both rx and tx counter */ + u64 frame_1518_1522; /* both rx and tx counter */ + u64 rx_bytes; + u64 rx_packets; + u64 rx_fcs_error; + u64 rx_multicast; + u64 rx_broadcast; + u64 rx_control_frames; + u64 rx_pause; + u64 rx_unknown_opcode; + u64 rx_alignment_error; + u64 rx_frame_length_error; + u64 rx_code_error; + u64 rx_carrier_sense_error; + u64 rx_undersize; + u64 rx_oversize; + u64 rx_fragments; + u64 rx_jabber; + u64 rx_drop; +}; + +/* + * MAC Tx Statistics + */ +struct cna_stats_mac_tx { + u64 tx_bytes; + u64 tx_packets; + u64 tx_multicast; + u64 tx_broadcast; + u64 tx_pause; + u64 tx_deferral; + u64 tx_excessive_deferral; + u64 tx_single_collision; + u64 tx_muliple_collision; + u64 tx_late_collision; + u64 tx_excessive_collision; + u64 tx_total_collision; + u64 tx_pause_honored; + u64 tx_drop; + u64 tx_jabber; + u64 tx_fcs_error; + u64 tx_control_frame; + u64 tx_oversize; + u64 tx_undersize; + u64 tx_fragments; +}; + +/* + * Complete statistics + */ +struct bna_stats { + struct cna_stats_mac_rx mac_rx_stats; + struct cna_stats_bpc_rx bpc_rx_stats; + struct cna_stats_rad rad_stats; + struct bna_stats_fc_rx fc_rx_stats; + struct cna_stats_mac_tx mac_tx_stats; + struct cna_stats_bpc_tx bpc_tx_stats; + struct bna_stats_fc_tx fc_tx_stats; + struct bna_stats_rxf rxf_stats[BNA_TXF_ID_MAX]; + struct bna_stats_txf txf_stats[BNA_RXF_ID_MAX]; +}; + +#endif diff --git a/drivers/scsi/bfa/include/cs/bfa_checksum.h b/drivers/scsi/bfa/include/cs/bfa_checksum.h new file mode 100644 index 000000000000..af8c1d533ba8 --- /dev/null +++ b/drivers/scsi/bfa/include/cs/bfa_checksum.h @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2005-2009 Brocade Communications Systems, Inc. + * All rights reserved + * www.brocade.com + * + * Linux driver for Brocade Fibre Channel Host Bus Adapter. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (GPL) Version 2 as + * published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +/** + * bfa_checksum.h BFA checksum utilities + */ + +#ifndef __BFA_CHECKSUM_H__ +#define __BFA_CHECKSUM_H__ + +static inline u32 +bfa_checksum_u32(u32 *buf, int sz) +{ + int i, m = sz >> 2; + u32 sum = 0; + + for (i = 0; i < m; i++) + sum ^= buf[i]; + + return (sum); +} + +static inline u16 +bfa_checksum_u16(u16 *buf, int sz) +{ + int i, m = sz >> 1; + u16 sum = 0; + + for (i = 0; i < m; i++) + sum ^= buf[i]; + + return (sum); +} + +static inline u8 +bfa_checksum_u8(u8 *buf, int sz) +{ + int i; + u8 sum = 0; + + for (i = 0; i < sz; i++) + sum ^= buf[i]; + + return (sum); +} +#endif diff --git a/drivers/scsi/bfa/include/cs/bfa_debug.h b/drivers/scsi/bfa/include/cs/bfa_debug.h new file mode 100644 index 000000000000..441be86b1b0f --- /dev/null +++ b/drivers/scsi/bfa/include/cs/bfa_debug.h @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2005-2009 Brocade Communications Systems, Inc. + * All rights reserved + * www.brocade.com + * + * Linux driver for Brocade Fibre Channel Host Bus Adapter. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (GPL) Version 2 as + * published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +/** + * bfa_debug.h BFA debug interfaces + */ + +#ifndef __BFA_DEBUG_H__ +#define __BFA_DEBUG_H__ + +#define bfa_assert(__cond) do { \ + if (!(__cond)) \ + bfa_panic(__LINE__, __FILE__, #__cond); \ +} while (0) + +#define bfa_sm_fault(__mod, __event) do { \ + bfa_sm_panic((__mod)->logm, __LINE__, __FILE__, __event); \ +} while (0) + +#ifndef BFA_PERF_BUILD +#define bfa_assert_fp(__cond) bfa_assert(__cond) +#else +#define bfa_assert_fp(__cond) +#endif + +struct bfa_log_mod_s; +void bfa_panic(int line, char *file, char *panicstr); +void bfa_sm_panic(struct bfa_log_mod_s *logm, int line, char *file, int event); + +#endif /* __BFA_DEBUG_H__ */ diff --git a/drivers/scsi/bfa/include/cs/bfa_log.h b/drivers/scsi/bfa/include/cs/bfa_log.h new file mode 100644 index 000000000000..761cbe22130a --- /dev/null +++ b/drivers/scsi/bfa/include/cs/bfa_log.h @@ -0,0 +1,184 @@ +/* + * Copyright (c) 2005-2009 Brocade Communications Systems, Inc. + * All rights reserved + * www.brocade.com + * + * Linux driver for Brocade Fibre Channel Host Bus Adapter. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (GPL) Version 2 as + * published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +/** + * bfa_log.h BFA log library data structure and function definition + */ + +#ifndef __BFA_LOG_H__ +#define __BFA_LOG_H__ + +#include <bfa_os_inc.h> +#include <defs/bfa_defs_status.h> +#include <defs/bfa_defs_aen.h> + +/* + * BFA log module definition + * + * To create a new module id: + * Add a #define at the end of the list below. Select a value for your + * definition so that it is one (1) greater than the previous + * definition. Modify the definition of BFA_LOG_MODULE_ID_MAX to become + * your new definition. + * Should have no gaps in between the values because this is used in arrays. + * IMPORTANT: AEN_IDs must be at the begining, otherwise update bfa_defs_aen.h + */ + +enum bfa_log_module_id { + BFA_LOG_UNUSED_ID = 0, + + /* AEN defs begin */ + BFA_LOG_AEN_MIN = BFA_LOG_UNUSED_ID, + + BFA_LOG_AEN_ID_ADAPTER = BFA_LOG_AEN_MIN + BFA_AEN_CAT_ADAPTER,/* 1 */ + BFA_LOG_AEN_ID_PORT = BFA_LOG_AEN_MIN + BFA_AEN_CAT_PORT, /* 2 */ + BFA_LOG_AEN_ID_LPORT = BFA_LOG_AEN_MIN + BFA_AEN_CAT_LPORT, /* 3 */ + BFA_LOG_AEN_ID_RPORT = BFA_LOG_AEN_MIN + BFA_AEN_CAT_RPORT, /* 4 */ + BFA_LOG_AEN_ID_ITNIM = BFA_LOG_AEN_MIN + BFA_AEN_CAT_ITNIM, /* 5 */ + BFA_LOG_AEN_ID_TIN = BFA_LOG_AEN_MIN + BFA_AEN_CAT_TIN, /* 6 */ + BFA_LOG_AEN_ID_IPFC = BFA_LOG_AEN_MIN + BFA_AEN_CAT_IPFC, /* 7 */ + BFA_LOG_AEN_ID_AUDIT = BFA_LOG_AEN_MIN + BFA_AEN_CAT_AUDIT, /* 8 */ + BFA_LOG_AEN_ID_IOC = BFA_LOG_AEN_MIN + BFA_AEN_CAT_IOC, /* 9 */ + BFA_LOG_AEN_ID_ETHPORT = BFA_LOG_AEN_MIN + BFA_AEN_CAT_ETHPORT,/* 10 */ + + BFA_LOG_AEN_MAX = BFA_LOG_AEN_ID_ETHPORT, + /* AEN defs end */ + + BFA_LOG_MODULE_ID_MIN = BFA_LOG_AEN_MAX, + + BFA_LOG_FW_ID = BFA_LOG_MODULE_ID_MIN + 1, + BFA_LOG_HAL_ID = BFA_LOG_MODULE_ID_MIN + 2, + BFA_LOG_FCS_ID = BFA_LOG_MODULE_ID_MIN + 3, + BFA_LOG_WDRV_ID = BFA_LOG_MODULE_ID_MIN + 4, + BFA_LOG_LINUX_ID = BFA_LOG_MODULE_ID_MIN + 5, + BFA_LOG_SOLARIS_ID = BFA_LOG_MODULE_ID_MIN + 6, + + BFA_LOG_MODULE_ID_MAX = BFA_LOG_SOLARIS_ID, + + /* Not part of any arrays */ + BFA_LOG_MODULE_ID_ALL = BFA_LOG_MODULE_ID_MAX + 1, + BFA_LOG_AEN_ALL = BFA_LOG_MODULE_ID_MAX + 2, + BFA_LOG_DRV_ALL = BFA_LOG_MODULE_ID_MAX + 3, +}; + +/* + * BFA log catalog name + */ +#define BFA_LOG_CAT_NAME "BFA" + +/* + * bfa log severity values + */ +enum bfa_log_severity { + BFA_LOG_INVALID = 0, + BFA_LOG_CRITICAL = 1, + BFA_LOG_ERROR = 2, + BFA_LOG_WARNING = 3, + BFA_LOG_INFO = 4, + BFA_LOG_NONE = 5, + BFA_LOG_LEVEL_MAX = BFA_LOG_NONE +}; + +#define BFA_LOG_MODID_OFFSET 16 + + +struct bfa_log_msgdef_s { + u32 msg_id; /* message id */ + int attributes; /* attributes */ + int severity; /* severity level */ + char *msg_value; + /* msg string */ + char *message; + /* msg format string */ + int arg_type; /* argument type */ + int arg_num; /* number of argument */ +}; + +/* + * supported argument type + */ +enum bfa_log_arg_type { + BFA_LOG_S = 0, /* string */ + BFA_LOG_D, /* decimal */ + BFA_LOG_I, /* integer */ + BFA_LOG_O, /* oct number */ + BFA_LOG_U, /* unsigned integer */ + BFA_LOG_X, /* hex number */ + BFA_LOG_F, /* floating */ + BFA_LOG_C, /* character */ + BFA_LOG_L, /* double */ + BFA_LOG_P /* pointer */ +}; + +#define BFA_LOG_ARG_TYPE 2 +#define BFA_LOG_ARG0 (0 * BFA_LOG_ARG_TYPE) +#define BFA_LOG_ARG1 (1 * BFA_LOG_ARG_TYPE) +#define BFA_LOG_ARG2 (2 * BFA_LOG_ARG_TYPE) +#define BFA_LOG_ARG3 (3 * BFA_LOG_ARG_TYPE) + +#define BFA_LOG_GET_MOD_ID(msgid) ((msgid >> BFA_LOG_MODID_OFFSET) & 0xff) +#define BFA_LOG_GET_MSG_IDX(msgid) (msgid & 0xffff) +#define BFA_LOG_GET_MSG_ID(msgdef) ((msgdef)->msg_id) +#define BFA_LOG_GET_MSG_FMT_STRING(msgdef) ((msgdef)->message) +#define BFA_LOG_GET_SEVERITY(msgdef) ((msgdef)->severity) + +/* + * Event attributes + */ +#define BFA_LOG_ATTR_NONE 0 +#define BFA_LOG_ATTR_AUDIT 1 +#define BFA_LOG_ATTR_LOG 2 +#define BFA_LOG_ATTR_FFDC 4 + +#define BFA_LOG_CREATE_ID(msw, lsw) \ + (((u32)msw << BFA_LOG_MODID_OFFSET) | lsw) + +struct bfa_log_mod_s; + +/** + * callback function + */ +typedef void (*bfa_log_cb_t)(struct bfa_log_mod_s *log_mod, u32 msg_id, + const char *format, ...); + + +struct bfa_log_mod_s { + char instance_info[16]; /* instance info */ + int log_level[BFA_LOG_MODULE_ID_MAX + 1]; + /* log level for modules */ + bfa_log_cb_t cbfn; /* callback function */ +}; + +extern int bfa_log_init(struct bfa_log_mod_s *log_mod, + char *instance_name, bfa_log_cb_t cbfn); +extern int bfa_log(struct bfa_log_mod_s *log_mod, u32 msg_id, ...); +extern bfa_status_t bfa_log_set_level(struct bfa_log_mod_s *log_mod, + int mod_id, enum bfa_log_severity log_level); +extern bfa_status_t bfa_log_set_level_all(struct bfa_log_mod_s *log_mod, + enum bfa_log_severity log_level); +extern bfa_status_t bfa_log_set_level_aen(struct bfa_log_mod_s *log_mod, + enum bfa_log_severity log_level); +extern enum bfa_log_severity bfa_log_get_level(struct bfa_log_mod_s *log_mod, + int mod_id); +extern enum bfa_log_severity bfa_log_get_msg_level( + struct bfa_log_mod_s *log_mod, u32 msg_id); +/* + * array of messages generated from xml files + */ +extern struct bfa_log_msgdef_s bfa_log_msg_array[]; + +#endif diff --git a/drivers/scsi/bfa/include/cs/bfa_perf.h b/drivers/scsi/bfa/include/cs/bfa_perf.h new file mode 100644 index 000000000000..45aa5f978ff5 --- /dev/null +++ b/drivers/scsi/bfa/include/cs/bfa_perf.h @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2005-2009 Brocade Communications Systems, Inc. + * All rights reserved + * www.brocade.com + * + * Linux driver for Brocade Fibre Channel Host Bus Adapter. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (GPL) Version 2 as + * published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ +#ifndef __BFAD_PERF_H__ +#define __BFAD_PERF_H__ + +#ifdef BFAD_PERF_BUILD + +#undef bfa_trc +#undef bfa_trc32 +#undef bfa_assert +#undef BFA_TRC_FILE + +#define bfa_trc(_trcp, _data) +#define bfa_trc32(_trcp, _data) +#define bfa_assert(__cond) +#define BFA_TRC_FILE(__mod, __submod) + +#endif + +#endif /* __BFAD_PERF_H__ */ diff --git a/drivers/scsi/bfa/include/cs/bfa_plog.h b/drivers/scsi/bfa/include/cs/bfa_plog.h new file mode 100644 index 000000000000..670f86e5fc6e --- /dev/null +++ b/drivers/scsi/bfa/include/cs/bfa_plog.h @@ -0,0 +1,162 @@ +/* + * Copyright (c) 2005-2009 Brocade Communications Systems, Inc. + * All rights reserved + * www.brocade.com + * + * Linux driver for Brocade Fibre Channel Host Bus Adapter. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (GPL) Version 2 as + * published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ +#ifndef __BFA_PORTLOG_H__ +#define __BFA_PORTLOG_H__ + +#include "protocol/fc.h" +#include <defs/bfa_defs_types.h> + +#define BFA_PL_NLOG_ENTS 256 +#define BFA_PL_LOG_REC_INCR(_x) ((_x)++, (_x) %= BFA_PL_NLOG_ENTS) + +#define BFA_PL_STRING_LOG_SZ 32 /* number of chars in string log */ +#define BFA_PL_INT_LOG_SZ 8 /* number of integers in the integer log */ + +enum bfa_plog_log_type { + BFA_PL_LOG_TYPE_INVALID = 0, + BFA_PL_LOG_TYPE_INT = 1, + BFA_PL_LOG_TYPE_STRING = 2, +}; + +/* + * the (fixed size) record format for each entry in the portlog + */ +struct bfa_plog_rec_s { + u32 tv; /* Filled by the portlog driver when the * + * entry is added to the circular log. */ + u8 port; /* Source port that logged this entry. CM + * entities will use 0xFF */ + u8 mid; /* Integer value to be used by all entities * + * while logging. The module id to string * + * conversion will be done by BFAL. See + * enum bfa_plog_mid */ + u8 eid; /* indicates Rx, Tx, IOCTL, etc. See + * enum bfa_plog_eid */ + u8 log_type; /* indicates string log or integer log. + * see bfa_plog_log_type_t */ + u8 log_num_ints; + /* + * interpreted only if log_type is INT_LOG. indicates number of + * integers in the int_log[] (0-PL_INT_LOG_SZ). + */ + u8 rsvd; + u16 misc; /* can be used to indicate fc frame length, + *etc.. */ + union { + char string_log[BFA_PL_STRING_LOG_SZ]; + u32 int_log[BFA_PL_INT_LOG_SZ]; + } log_entry; + +}; + +/* + * the following #defines will be used by the logging entities to indicate + * their module id. BFAL will convert the integer value to string format + * +* process to be used while changing the following #defines: + * - Always add new entries at the end + * - define corresponding string in BFAL + * - Do not remove any entry or rearrange the order. + */ +enum bfa_plog_mid { + BFA_PL_MID_INVALID = 0, + BFA_PL_MID_DEBUG = 1, + BFA_PL_MID_DRVR = 2, + BFA_PL_MID_HAL = 3, + BFA_PL_MID_HAL_FCXP = 4, + BFA_PL_MID_HAL_UF = 5, + BFA_PL_MID_FCS = 6, + BFA_PL_MID_MAX = 7 +}; + +#define BFA_PL_MID_STRLEN 8 +struct bfa_plog_mid_strings_s { + char m_str[BFA_PL_MID_STRLEN]; +}; + +/* + * the following #defines will be used by the logging entities to indicate + * their event type. BFAL will convert the integer value to string format + * +* process to be used while changing the following #defines: + * - Always add new entries at the end + * - define corresponding string in BFAL + * - Do not remove any entry or rearrange the order. + */ +enum bfa_plog_eid { + BFA_PL_EID_INVALID = 0, + BFA_PL_EID_IOC_DISABLE = 1, + BFA_PL_EID_IOC_ENABLE = 2, + BFA_PL_EID_PORT_DISABLE = 3, + BFA_PL_EID_PORT_ENABLE = 4, + BFA_PL_EID_PORT_ST_CHANGE = 5, + BFA_PL_EID_TX = 6, + BFA_PL_EID_TX_ACK1 = 7, + BFA_PL_EID_TX_RJT = 8, + BFA_PL_EID_TX_BSY = 9, + BFA_PL_EID_RX = 10, + BFA_PL_EID_RX_ACK1 = 11, + BFA_PL_EID_RX_RJT = 12, + BFA_PL_EID_RX_BSY = 13, + BFA_PL_EID_CT_IN = 14, + BFA_PL_EID_CT_OUT = 15, + BFA_PL_EID_DRIVER_START = 16, + BFA_PL_EID_RSCN = 17, + BFA_PL_EID_DEBUG = 18, + BFA_PL_EID_MISC = 19, + BFA_PL_EID_MAX = 20 +}; + +#define BFA_PL_ENAME_STRLEN 8 +struct bfa_plog_eid_strings_s { + char e_str[BFA_PL_ENAME_STRLEN]; +}; + +#define BFA_PL_SIG_LEN 8 +#define BFA_PL_SIG_STR "12pl123" + +/* + * per port circular log buffer + */ +struct bfa_plog_s { + char plog_sig[BFA_PL_SIG_LEN]; /* Start signature */ + u8 plog_enabled; + u8 rsvd[7]; + u32 ticks; + u16 head; + u16 tail; + struct bfa_plog_rec_s plog_recs[BFA_PL_NLOG_ENTS]; +}; + +void bfa_plog_init(struct bfa_plog_s *plog); +void bfa_plog_str(struct bfa_plog_s *plog, enum bfa_plog_mid mid, + enum bfa_plog_eid event, u16 misc, char *log_str); +void bfa_plog_intarr(struct bfa_plog_s *plog, enum bfa_plog_mid mid, + enum bfa_plog_eid event, u16 misc, + u32 *intarr, u32 num_ints); +void bfa_plog_fchdr(struct bfa_plog_s *plog, enum bfa_plog_mid mid, + enum bfa_plog_eid event, u16 misc, + struct fchs_s *fchdr); +void bfa_plog_fchdr_and_pl(struct bfa_plog_s *plog, enum bfa_plog_mid mid, + enum bfa_plog_eid event, u16 misc, + struct fchs_s *fchdr, u32 pld_w0); +void bfa_plog_clear(struct bfa_plog_s *plog); +void bfa_plog_enable(struct bfa_plog_s *plog); +void bfa_plog_disable(struct bfa_plog_s *plog); +bfa_boolean_t bfa_plog_get_setting(struct bfa_plog_s *plog); + +#endif /* __BFA_PORTLOG_H__ */ diff --git a/drivers/scsi/bfa/include/cs/bfa_q.h b/drivers/scsi/bfa/include/cs/bfa_q.h new file mode 100644 index 000000000000..ea895facedbc --- /dev/null +++ b/drivers/scsi/bfa/include/cs/bfa_q.h @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2005-2009 Brocade Communications Systems, Inc. + * All rights reserved + * www.brocade.com + * + * Linux driver for Brocade Fibre Channel Host Bus Adapter. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (GPL) Version 2 as + * published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +/** + * bfa_q.h Circular queue definitions. + */ + +#ifndef __BFA_Q_H__ +#define __BFA_Q_H__ + +#define bfa_q_first(_q) ((void *)(((struct list_head *) (_q))->next)) +#define bfa_q_next(_qe) (((struct list_head *) (_qe))->next) +#define bfa_q_prev(_qe) (((struct list_head *) (_qe))->prev) + +/* + * bfa_q_qe_init - to initialize a queue element + */ +#define bfa_q_qe_init(_qe) { \ + bfa_q_next(_qe) = (struct list_head *) NULL; \ + bfa_q_prev(_qe) = (struct list_head *) NULL; \ +} + +/* + * bfa_q_deq - dequeue an element from head of the queue + */ +#define bfa_q_deq(_q, _qe) { \ + if (!list_empty(_q)) { \ + (*((struct list_head **) (_qe))) = bfa_q_next(_q); \ + bfa_q_prev(bfa_q_next(*((struct list_head **) _qe))) = \ + (struct list_head *) (_q); \ + bfa_q_next(_q) = bfa_q_next(*((struct list_head **) _qe)); \ + BFA_Q_DBG_INIT(*((struct list_head **) _qe)); \ + } else { \ + *((struct list_head **) (_qe)) = (struct list_head *) NULL; \ + } \ +} + +/* + * bfa_q_deq_tail - dequeue an element from tail of the queue + */ +#define bfa_q_deq_tail(_q, _qe) { \ + if (!list_empty(_q)) { \ + *((struct list_head **) (_qe)) = bfa_q_prev(_q); \ + bfa_q_next(bfa_q_prev(*((struct list_head **) _qe))) = \ + (struct list_head *) (_q); \ + bfa_q_prev(_q) = bfa_q_prev(*(struct list_head **) _qe); \ + BFA_Q_DBG_INIT(*((struct list_head **) _qe)); \ + } else { \ + *((struct list_head **) (_qe)) = (struct list_head *) NULL; \ + } \ +} + +/* + * #ifdef BFA_DEBUG (Using bfa_assert to check for debug_build is not + * consistent across modules) + */ +#ifndef BFA_PERF_BUILD +#define BFA_Q_DBG_INIT(_qe) bfa_q_qe_init(_qe) +#else +#define BFA_Q_DBG_INIT(_qe) +#endif + +#define bfa_q_is_on_q(_q, _qe) \ + bfa_q_is_on_q_func(_q, (struct list_head *)(_qe)) +extern int bfa_q_is_on_q_func(struct list_head *q, struct list_head *qe); + +#endif diff --git a/drivers/scsi/bfa/include/cs/bfa_sm.h b/drivers/scsi/bfa/include/cs/bfa_sm.h new file mode 100644 index 000000000000..9877066680a6 --- /dev/null +++ b/drivers/scsi/bfa/include/cs/bfa_sm.h @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2005-2009 Brocade Communications Systems, Inc. + * All rights reserved + * www.brocade.com + * + * Linux driver for Brocade Fibre Channel Host Bus Adapter. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (GPL) Version 2 as + * published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +/** + * bfasm.h State machine defines + */ + +#ifndef __BFA_SM_H__ +#define __BFA_SM_H__ + +typedef void (*bfa_sm_t)(void *sm, int event); + +#define bfa_sm_set_state(_sm, _state) (_sm)->sm = (bfa_sm_t)(_state) +#define bfa_sm_send_event(_sm, _event) (_sm)->sm((_sm), (_event)) +#define bfa_sm_get_state(_sm) ((_sm)->sm) +#define bfa_sm_cmp_state(_sm, _state) ((_sm)->sm == (bfa_sm_t)(_state)) + +/** + * For converting from state machine function to state encoding. + */ +struct bfa_sm_table_s { + bfa_sm_t sm; /* state machine function */ + int state; /* state machine encoding */ + char *name; /* state name for display */ +}; +#define BFA_SM(_sm) ((bfa_sm_t)(_sm)) + +int bfa_sm_to_state(struct bfa_sm_table_s *smt, bfa_sm_t sm); + +/** + * State machine with entry actions. + */ +typedef void (*bfa_fsm_t)(void *fsm, int event); + +/** + * oc - object class eg. bfa_ioc + * st - state, eg. reset + * otype - object type, eg. struct bfa_ioc_s + * etype - object type, eg. enum ioc_event + */ +#define bfa_fsm_state_decl(oc, st, otype, etype) \ + static void oc ## _sm_ ## st(otype * fsm, etype event); \ + static void oc ## _sm_ ## st ## _entry(otype * fsm) + +#define bfa_fsm_set_state(_fsm, _state) do { \ + (_fsm)->fsm = (bfa_fsm_t)(_state); \ + _state ## _entry(_fsm); \ +} while (0) + +#define bfa_fsm_send_event(_fsm, _event) \ + (_fsm)->fsm((_fsm), (_event)) +#define bfa_fsm_cmp_state(_fsm, _state) \ + ((_fsm)->fsm == (bfa_fsm_t)(_state)) + +#endif diff --git a/drivers/scsi/bfa/include/cs/bfa_trc.h b/drivers/scsi/bfa/include/cs/bfa_trc.h new file mode 100644 index 000000000000..3e743928c74c --- /dev/null +++ b/drivers/scsi/bfa/include/cs/bfa_trc.h @@ -0,0 +1,176 @@ +/* + * Copyright (c) 2005-2009 Brocade Communications Systems, Inc. + * All rights reserved + * www.brocade.com + * + * Linux driver for Brocade Fibre Channel Host Bus Adapter. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (GPL) Version 2 as + * published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ +#ifndef __BFA_TRC_H__ +#define __BFA_TRC_H__ + +#include <bfa_os_inc.h> + +#ifndef BFA_TRC_MAX +#define BFA_TRC_MAX (4 * 1024) +#endif + +#ifndef BFA_TRC_TS +#define BFA_TRC_TS(_trcm) ((_trcm)->ticks ++) +#endif + +struct bfa_trc_s { +#ifdef __BIGENDIAN + u16 fileno; + u16 line; +#else + u16 line; + u16 fileno; +#endif + u32 timestamp; + union { + struct { + u32 rsvd; + u32 u32; + } u32; + u64 u64; + } data; +}; + + +struct bfa_trc_mod_s { + u32 head; + u32 tail; + u32 ntrc; + u32 stopped; + u32 ticks; + u32 rsvd[3]; + struct bfa_trc_s trc[BFA_TRC_MAX]; +}; + + +enum { + BFA_TRC_FW = 1, /* firmware modules */ + BFA_TRC_HAL = 2, /* BFA modules */ + BFA_TRC_FCS = 3, /* BFA FCS modules */ + BFA_TRC_LDRV = 4, /* Linux driver modules */ + BFA_TRC_SDRV = 5, /* Solaris driver modules */ + BFA_TRC_VDRV = 6, /* vmware driver modules */ + BFA_TRC_WDRV = 7, /* windows driver modules */ + BFA_TRC_AEN = 8, /* AEN module */ + BFA_TRC_BIOS = 9, /* bios driver modules */ + BFA_TRC_EFI = 10, /* EFI driver modules */ + BNA_TRC_WDRV = 11, /* BNA windows driver modules */ + BNA_TRC_VDRV = 12, /* BNA vmware driver modules */ + BNA_TRC_SDRV = 13, /* BNA Solaris driver modules */ + BNA_TRC_LDRV = 14, /* BNA Linux driver modules */ + BNA_TRC_HAL = 15, /* BNA modules */ + BFA_TRC_CNA = 16, /* Common modules */ + BNA_TRC_IMDRV = 17 /* BNA windows intermediate driver modules */ +}; +#define BFA_TRC_MOD_SH 10 +#define BFA_TRC_MOD(__mod) ((BFA_TRC_ ## __mod) << BFA_TRC_MOD_SH) + +/** + * Define a new tracing file (module). Module should match one defined above. + */ +#define BFA_TRC_FILE(__mod, __submod) \ + static int __trc_fileno = ((BFA_TRC_ ## __mod ## _ ## __submod) | \ + BFA_TRC_MOD(__mod)) + + +#define bfa_trc32(_trcp, _data) \ + __bfa_trc((_trcp)->trcmod, __trc_fileno, __LINE__, (u32)_data) + + +#ifndef BFA_BOOT_BUILD +#define bfa_trc(_trcp, _data) \ + __bfa_trc((_trcp)->trcmod, __trc_fileno, __LINE__, (u64)_data) +#else +void bfa_boot_trc(struct bfa_trc_mod_s *trcmod, u16 fileno, + u16 line, u32 data); +#define bfa_trc(_trcp, _data) \ + bfa_boot_trc((_trcp)->trcmod, __trc_fileno, __LINE__, (u32)_data) +#endif + + +static inline void +bfa_trc_init(struct bfa_trc_mod_s *trcm) +{ + trcm->head = trcm->tail = trcm->stopped = 0; + trcm->ntrc = BFA_TRC_MAX; +} + + +static inline void +bfa_trc_stop(struct bfa_trc_mod_s *trcm) +{ + trcm->stopped = 1; +} + +#ifdef FWTRC +extern void dc_flush(void *data); +#else +#define dc_flush(data) +#endif + + +static inline void +__bfa_trc(struct bfa_trc_mod_s *trcm, int fileno, int line, u64 data) +{ + int tail = trcm->tail; + struct bfa_trc_s *trc = &trcm->trc[tail]; + + if (trcm->stopped) + return; + + trc->fileno = (u16) fileno; + trc->line = (u16) line; + trc->data.u64 = data; + trc->timestamp = BFA_TRC_TS(trcm); + dc_flush(trc); + + trcm->tail = (trcm->tail + 1) & (BFA_TRC_MAX - 1); + if (trcm->tail == trcm->head) + trcm->head = (trcm->head + 1) & (BFA_TRC_MAX - 1); + dc_flush(trcm); +} + + +static inline void +__bfa_trc32(struct bfa_trc_mod_s *trcm, int fileno, int line, u32 data) +{ + int tail = trcm->tail; + struct bfa_trc_s *trc = &trcm->trc[tail]; + + if (trcm->stopped) + return; + + trc->fileno = (u16) fileno; + trc->line = (u16) line; + trc->data.u32.u32 = data; + trc->timestamp = BFA_TRC_TS(trcm); + dc_flush(trc); + + trcm->tail = (trcm->tail + 1) & (BFA_TRC_MAX - 1); + if (trcm->tail == trcm->head) + trcm->head = (trcm->head + 1) & (BFA_TRC_MAX - 1); + dc_flush(trcm); +} + +#ifndef BFA_PERF_BUILD +#define bfa_trc_fp(_trcp, _data) bfa_trc(_trcp, _data) +#else +#define bfa_trc_fp(_trcp, _data) +#endif + +#endif /* __BFA_TRC_H__ */ + diff --git a/drivers/scsi/bfa/include/cs/bfa_wc.h b/drivers/scsi/bfa/include/cs/bfa_wc.h new file mode 100644 index 000000000000..0460bd4fc7c4 --- /dev/null +++ b/drivers/scsi/bfa/include/cs/bfa_wc.h @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2005-2009 Brocade Communications Systems, Inc. + * All rights reserved + * www.brocade.com + * + * Linux driver for Brocade Fibre Channel Host Bus Adapter. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (GPL) Version 2 as + * published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +/** + * bfa_wc.h Generic wait counter. + */ + +#ifndef __BFA_WC_H__ +#define __BFA_WC_H__ + +typedef void (*bfa_wc_resume_t) (void *cbarg); + +struct bfa_wc_s { + bfa_wc_resume_t wc_resume; + void *wc_cbarg; + int wc_count; +}; + +static inline void +bfa_wc_up(struct bfa_wc_s *wc) +{ + wc->wc_count++; +} + +static inline void +bfa_wc_down(struct bfa_wc_s *wc) +{ + wc->wc_count--; + if (wc->wc_count == 0) + wc->wc_resume(wc->wc_cbarg); +} + +/** + * Initialize a waiting counter. + */ +static inline void +bfa_wc_init(struct bfa_wc_s *wc, bfa_wc_resume_t wc_resume, void *wc_cbarg) +{ + wc->wc_resume = wc_resume; + wc->wc_cbarg = wc_cbarg; + wc->wc_count = 0; + bfa_wc_up(wc); +} + +/** + * Wait for counter to reach zero + */ +static inline void +bfa_wc_wait(struct bfa_wc_s *wc) +{ + bfa_wc_down(wc); +} + +#endif diff --git a/drivers/scsi/bfa/include/defs/bfa_defs_adapter.h b/drivers/scsi/bfa/include/defs/bfa_defs_adapter.h new file mode 100644 index 000000000000..8c208fc8e329 --- /dev/null +++ b/drivers/scsi/bfa/include/defs/bfa_defs_adapter.h @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2005-2009 Brocade Communications Systems, Inc. + * All rights reserved + * www.brocade.com + * + * Linux driver for Brocade Fibre Channel Host Bus Adapter. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (GPL) Version 2 as + * published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ +#ifndef __BFA_DEFS_ADAPTER_H__ +#define __BFA_DEFS_ADAPTER_H__ + +#include <protocol/types.h> +#include <defs/bfa_defs_version.h> +#include <defs/bfa_defs_mfg.h> + +/** + * BFA adapter level attributes. + */ +enum { + BFA_ADAPTER_SERIAL_NUM_LEN = STRSZ(BFA_MFG_SERIALNUM_SIZE), + /* + *!< adapter serial num length + */ + BFA_ADAPTER_MODEL_NAME_LEN = 16, /* model name length */ + BFA_ADAPTER_MODEL_DESCR_LEN = 128, /* model description length */ + BFA_ADAPTER_MFG_NAME_LEN = 8, /* manufacturer name length */ + BFA_ADAPTER_SYM_NAME_LEN = 64, /* adapter symbolic name length */ + BFA_ADAPTER_OS_TYPE_LEN = 64, /* adapter os type length */ +}; + +struct bfa_adapter_attr_s { + char manufacturer[BFA_ADAPTER_MFG_NAME_LEN]; + char serial_num[BFA_ADAPTER_SERIAL_NUM_LEN]; + u32 rsvd1; + char model[BFA_ADAPTER_MODEL_NAME_LEN]; + char model_descr[BFA_ADAPTER_MODEL_DESCR_LEN]; + wwn_t pwwn; + char node_symname[FC_SYMNAME_MAX]; + char hw_ver[BFA_VERSION_LEN]; + char fw_ver[BFA_VERSION_LEN]; + char optrom_ver[BFA_VERSION_LEN]; + char os_type[BFA_ADAPTER_OS_TYPE_LEN]; + struct bfa_mfg_vpd_s vpd; + struct mac_s mac; + + u8 nports; + u8 max_speed; + u8 prototype; + char asic_rev; + + u8 pcie_gen; + u8 pcie_lanes_orig; + u8 pcie_lanes; + u8 cna_capable; +}; + +/** + * BFA adapter level events + * Arguments below are in BFAL context from Mgmt + * BFA_PORT_AEN_ADD: [in]: None [out]: serial_num, pwwn, nports + * BFA_PORT_AEN_REMOVE: [in]: pwwn [out]: serial_num, pwwn, nports + */ +enum bfa_adapter_aen_event { + BFA_ADAPTER_AEN_ADD = 1, /* New Adapter found event */ + BFA_ADAPTER_AEN_REMOVE = 2, /* Adapter removed event */ +}; + +struct bfa_adapter_aen_data_s { + char serial_num[BFA_ADAPTER_SERIAL_NUM_LEN]; + u32 nports; /* Number of NPorts */ + wwn_t pwwn; /* WWN of one of its physical port */ +}; + +#endif /* __BFA_DEFS_ADAPTER_H__ */ diff --git a/drivers/scsi/bfa/include/defs/bfa_defs_aen.h b/drivers/scsi/bfa/include/defs/bfa_defs_aen.h new file mode 100644 index 000000000000..4c81a613db3d --- /dev/null +++ b/drivers/scsi/bfa/include/defs/bfa_defs_aen.h @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2005-2009 Brocade Communications Systems, Inc. + * All rights reserved + * www.brocade.com + * + * Linux driver for Brocade Fibre Channel Host Bus Adapter. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (GPL) Version 2 as + * published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#ifndef __BFA_DEFS_AEN_H__ +#define __BFA_DEFS_AEN_H__ + +#include <defs/bfa_defs_types.h> +#include <defs/bfa_defs_ioc.h> +#include <defs/bfa_defs_adapter.h> +#include <defs/bfa_defs_port.h> +#include <defs/bfa_defs_lport.h> +#include <defs/bfa_defs_rport.h> +#include <defs/bfa_defs_itnim.h> +#include <defs/bfa_defs_tin.h> +#include <defs/bfa_defs_ipfc.h> +#include <defs/bfa_defs_audit.h> +#include <defs/bfa_defs_ethport.h> + +enum bfa_aen_category { + BFA_AEN_CAT_ADAPTER = 1, + BFA_AEN_CAT_PORT = 2, + BFA_AEN_CAT_LPORT = 3, + BFA_AEN_CAT_RPORT = 4, + BFA_AEN_CAT_ITNIM = 5, + BFA_AEN_CAT_TIN = 6, + BFA_AEN_CAT_IPFC = 7, + BFA_AEN_CAT_AUDIT = 8, + BFA_AEN_CAT_IOC = 9, + BFA_AEN_CAT_ETHPORT = 10, + BFA_AEN_MAX_CAT = 10 +}; + +#pragma pack(1) +union bfa_aen_data_u { + struct bfa_adapter_aen_data_s adapter; + struct bfa_port_aen_data_s port; + struct bfa_lport_aen_data_s lport; + struct bfa_rport_aen_data_s rport; + struct bfa_itnim_aen_data_s itnim; + struct bfa_audit_aen_data_s audit; + struct bfa_ioc_aen_data_s ioc; + struct bfa_ethport_aen_data_s ethport; +}; + +struct bfa_aen_entry_s { + enum bfa_aen_category aen_category; + int aen_type; + union bfa_aen_data_u aen_data; + struct bfa_timeval_s aen_tv; + s32 seq_num; + s32 bfad_num; + s32 rsvd[1]; +}; + +#pragma pack() + +#define bfa_aen_event_t int + +#endif /* __BFA_DEFS_AEN_H__ */ diff --git a/drivers/scsi/bfa/include/defs/bfa_defs_audit.h b/drivers/scsi/bfa/include/defs/bfa_defs_audit.h new file mode 100644 index 000000000000..8e3a962bf20c --- /dev/null +++ b/drivers/scsi/bfa/include/defs/bfa_defs_audit.h @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2005-2009 Brocade Communications Systems, Inc. + * All rights reserved + * www.brocade.com + * + * Linux driver for Brocade Fibre Channel Host Bus Adapter. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (GPL) Version 2 as + * published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#ifndef __BFA_DEFS_AUDIT_H__ +#define __BFA_DEFS_AUDIT_H__ + +#include <bfa_os_inc.h> + +/** + * BFA audit events + */ +enum bfa_audit_aen_event { + BFA_AUDIT_AEN_AUTH_ENABLE = 1, + BFA_AUDIT_AEN_AUTH_DISABLE = 2, +}; + +/** + * audit event data + */ +struct bfa_audit_aen_data_s { + wwn_t pwwn; +}; + +#endif /* __BFA_DEFS_AUDIT_H__ */ diff --git a/drivers/scsi/bfa/include/defs/bfa_defs_auth.h b/drivers/scsi/bfa/include/defs/bfa_defs_auth.h new file mode 100644 index 000000000000..dd19c83aba58 --- /dev/null +++ b/drivers/scsi/bfa/include/defs/bfa_defs_auth.h @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2005-2009 Brocade Communications Systems, Inc. + * All rights reserved + * www.brocade.com + * + * Linux driver for Brocade Fibre Channel Host Bus Adapter. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (GPL) Version 2 as + * published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ +#ifndef __BFA_DEFS_AUTH_H__ +#define __BFA_DEFS_AUTH_H__ + +#include <defs/bfa_defs_types.h> + +#define PUBLIC_KEY 15409 +#define PRIVATE_KEY 19009 +#define KEY_LEN 32399 +#define BFA_AUTH_SECRET_STRING_LEN 256 +#define BFA_AUTH_FAIL_TIMEOUT 0xFF + +/** + * Authentication status + */ +enum bfa_auth_status { + BFA_AUTH_STATUS_NONE = 0, /* no authentication */ + BFA_AUTH_UNINIT = 1, /* state - uninit */ + BFA_AUTH_NEG_SEND = 2, /* state - negotiate send */ + BFA_AUTH_CHAL_WAIT = 3, /* state - challenge wait */ + BFA_AUTH_NEG_RETRY = 4, /* state - negotiate retry */ + BFA_AUTH_REPLY_SEND = 5, /* state - reply send */ + BFA_AUTH_STATUS_WAIT = 6, /* state - status wait */ + BFA_AUTH_SUCCESS = 7, /* state - success */ + BFA_AUTH_FAILED = 8, /* state - failed */ + BFA_AUTH_STATUS_UNKNOWN = 9, /* authentication status unknown */ +}; + +struct auth_proto_stats_s { + u32 auth_rjts; + u32 auth_negs; + u32 auth_dones; + + u32 dhchap_challenges; + u32 dhchap_replies; + u32 dhchap_successes; +}; + +/** + * Authentication related statistics + */ +struct bfa_auth_stats_s { + u32 auth_failures; /* authentication failures */ + u32 auth_successes; /* authentication successes*/ + struct auth_proto_stats_s auth_rx_stats; /* Rx protocol stats */ + struct auth_proto_stats_s auth_tx_stats; /* Tx protocol stats */ +}; + +/** + * Authentication hash function algorithms + */ +enum bfa_auth_algo { + BFA_AUTH_ALGO_MD5 = 1, /* Message-Digest algorithm 5 */ + BFA_AUTH_ALGO_SHA1 = 2, /* Secure Hash Algorithm 1 */ + BFA_AUTH_ALGO_MS = 3, /* MD5, then SHA-1 */ + BFA_AUTH_ALGO_SM = 4, /* SHA-1, then MD5 */ +}; + +/** + * DH Groups + * + * Current value could be combination of one or more of the following values + */ +enum bfa_auth_group { + BFA_AUTH_GROUP_DHNULL = 0, /* DH NULL (value == 0) */ + BFA_AUTH_GROUP_DH768 = 1, /* DH group 768 (value == 1) */ + BFA_AUTH_GROUP_DH1024 = 2, /* DH group 1024 (value == 2) */ + BFA_AUTH_GROUP_DH1280 = 4, /* DH group 1280 (value == 3) */ + BFA_AUTH_GROUP_DH1536 = 8, /* DH group 1536 (value == 4) */ + + BFA_AUTH_GROUP_ALL = 256 /* Use default DH group order + * 0, 1, 2, 3, 4 */ +}; + +/** + * Authentication secret sources + */ +enum bfa_auth_secretsource { + BFA_AUTH_SECSRC_LOCAL = 1, /* locally configured */ + BFA_AUTH_SECSRC_RADIUS = 2, /* use radius server */ + BFA_AUTH_SECSRC_TACACS = 3, /* TACACS server */ +}; + +/** + * Authentication attributes + */ +struct bfa_auth_attr_s { + enum bfa_auth_status status; + enum bfa_auth_algo algo; + enum bfa_auth_group dh_grp; + u16 rjt_code; + u16 rjt_code_exp; + u8 secret_set; + u8 resv[7]; +}; + +#endif /* __BFA_DEFS_AUTH_H__ */ diff --git a/drivers/scsi/bfa/include/defs/bfa_defs_boot.h b/drivers/scsi/bfa/include/defs/bfa_defs_boot.h new file mode 100644 index 000000000000..6f4aa5283545 --- /dev/null +++ b/drivers/scsi/bfa/include/defs/bfa_defs_boot.h @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2005-2009 Brocade Communications Systems, Inc. + * All rights reserved + * www.brocade.com + * + * Linux driver for Brocade Fibre Channel Host Bus Adapter. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (GPL) Version 2 as + * published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#ifndef __BFA_DEFS_BOOT_H__ +#define __BFA_DEFS_BOOT_H__ + +#include <protocol/types.h> +#include <defs/bfa_defs_types.h> +#include <defs/bfa_defs_pport.h> + +enum { + BFA_BOOT_BOOTLUN_MAX = 4, /* maximum boot lun per IOC */ +}; + +#define BOOT_CFG_REV1 1 + +/** + * Boot options setting. Boot options setting determines from where + * to get the boot lun information + */ +enum bfa_boot_bootopt { + BFA_BOOT_AUTO_DISCOVER = 0, /* Boot from blun provided by fabric */ + BFA_BOOT_STORED_BLUN = 1, /* Boot from bluns stored in flash */ + BFA_BOOT_FIRST_LUN = 2, /* Boot from first discovered blun */ +}; + +/** + * Boot lun information. + */ +struct bfa_boot_bootlun_s { + wwn_t pwwn; /* port wwn of target */ + lun_t lun; /* 64-bit lun */ +}; + +/** + * BOOT boot configuraton + */ +struct bfa_boot_cfg_s { + u8 version; + u8 rsvd1; + u16 chksum; + + u8 enable; /* enable/disable SAN boot */ + u8 speed; /* boot speed settings */ + u8 topology; /* boot topology setting */ + u8 bootopt; /* bfa_boot_bootopt_t */ + + u32 nbluns; /* number of boot luns */ + + u32 rsvd2; + + struct bfa_boot_bootlun_s blun[BFA_BOOT_BOOTLUN_MAX]; + struct bfa_boot_bootlun_s blun_disc[BFA_BOOT_BOOTLUN_MAX]; +}; + + +#endif /* __BFA_DEFS_BOOT_H__ */ diff --git a/drivers/scsi/bfa/include/defs/bfa_defs_cee.h b/drivers/scsi/bfa/include/defs/bfa_defs_cee.h new file mode 100644 index 000000000000..520a22f52dd1 --- /dev/null +++ b/drivers/scsi/bfa/include/defs/bfa_defs_cee.h @@ -0,0 +1,159 @@ +/* + * Copyright (c) 2005-2009 Brocade Communications Systems, Inc. + * All rights reserved + * www.brocade.com + * + * bfa_defs_cee.h Interface declarations between host based + * BFAL and DCBX/LLDP module in Firmware + * + * Linux driver for Brocade Fibre Channel Host Bus Adapter. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (GPL) Version 2 as + * published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ +#ifndef __BFA_DEFS_CEE_H__ +#define __BFA_DEFS_CEE_H__ + +#include <defs/bfa_defs_types.h> +#include <defs/bfa_defs_pport.h> +#include <protocol/types.h> + +#pragma pack(1) + +#define BFA_CEE_LLDP_MAX_STRING_LEN (128) + + +/* FIXME: this is coming from the protocol spec. Can the host & apps share the + protocol .h files ? + */ +#define BFA_CEE_LLDP_SYS_CAP_OTHER 0x0001 +#define BFA_CEE_LLDP_SYS_CAP_REPEATER 0x0002 +#define BFA_CEE_LLDP_SYS_CAP_MAC_BRIDGE 0x0004 +#define BFA_CEE_LLDP_SYS_CAP_WLAN_AP 0x0008 +#define BFA_CEE_LLDP_SYS_CAP_ROUTER 0x0010 +#define BFA_CEE_LLDP_SYS_CAP_TELEPHONE 0x0020 +#define BFA_CEE_LLDP_SYS_CAP_DOCSIS_CD 0x0040 +#define BFA_CEE_LLDP_SYS_CAP_STATION 0x0080 +#define BFA_CEE_LLDP_SYS_CAP_CVLAN 0x0100 +#define BFA_CEE_LLDP_SYS_CAP_SVLAN 0x0200 +#define BFA_CEE_LLDP_SYS_CAP_TPMR 0x0400 + + +/* LLDP string type */ +struct bfa_cee_lldp_str_s { + u8 sub_type; + u8 len; + u8 rsvd[2]; + u8 value[BFA_CEE_LLDP_MAX_STRING_LEN]; +}; + + +/* LLDP paramters */ +struct bfa_cee_lldp_cfg_s { + struct bfa_cee_lldp_str_s chassis_id; + struct bfa_cee_lldp_str_s port_id; + struct bfa_cee_lldp_str_s port_desc; + struct bfa_cee_lldp_str_s sys_name; + struct bfa_cee_lldp_str_s sys_desc; + struct bfa_cee_lldp_str_s mgmt_addr; + u16 time_to_interval; + u16 enabled_system_cap; +}; + +enum bfa_cee_dcbx_version_e { + DCBX_PROTOCOL_PRECEE = 1, + DCBX_PROTOCOL_CEE = 2, +}; + +enum bfa_cee_lls_e { + CEE_LLS_DOWN_NO_TLV = 0, /* LLS is down because the TLV not sent by + * the peer */ + CEE_LLS_DOWN = 1, /* LLS is down as advertised by the peer */ + CEE_LLS_UP = 2, +}; + +/* CEE/DCBX parameters */ +struct bfa_cee_dcbx_cfg_s { + u8 pgid[8]; + u8 pg_percentage[8]; + u8 pfc_enabled; /* bitmap of priorties with PFC enabled */ + u8 fcoe_user_priority; /* bitmap of priorities used for FcoE + * traffic */ + u8 dcbx_version; /* operating version:CEE or preCEE */ + u8 lls_fcoe; /* FCoE Logical Link Status */ + u8 lls_lan; /* LAN Logical Link Status */ + u8 rsvd[3]; +}; + +/* CEE status */ +/* Making this to tri-state for the benefit of port list command */ +enum bfa_cee_status_e { + CEE_PHY_DOWN = 0, + CEE_PHY_UP = 1, + CEE_UP = 2, +}; + +/* CEE Query */ +struct bfa_cee_attr_s { + u8 cee_status; + u8 error_reason; + struct bfa_cee_lldp_cfg_s lldp_remote; + struct bfa_cee_dcbx_cfg_s dcbx_remote; + mac_t src_mac; + u8 link_speed; + u8 filler[3]; +}; + + + + +/* LLDP/DCBX/CEE Statistics */ + +struct bfa_cee_lldp_stats_s { + u32 frames_transmitted; + u32 frames_aged_out; + u32 frames_discarded; + u32 frames_in_error; + u32 frames_rcvd; + u32 tlvs_discarded; + u32 tlvs_unrecognized; +}; + +struct bfa_cee_dcbx_stats_s { + u32 subtlvs_unrecognized; + u32 negotiation_failed; + u32 remote_cfg_changed; + u32 tlvs_received; + u32 tlvs_invalid; + u32 seqno; + u32 ackno; + u32 recvd_seqno; + u32 recvd_ackno; +}; + +struct bfa_cee_cfg_stats_s { + u32 cee_status_down; + u32 cee_status_up; + u32 cee_hw_cfg_changed; + u32 recvd_invalid_cfg; +}; + + +struct bfa_cee_stats_s { + struct bfa_cee_lldp_stats_s lldp_stats; + struct bfa_cee_dcbx_stats_s dcbx_stats; + struct bfa_cee_cfg_stats_s cfg_stats; +}; + +#pragma pack() + + +#endif /* __BFA_DEFS_CEE_H__ */ + + diff --git a/drivers/scsi/bfa/include/defs/bfa_defs_driver.h b/drivers/scsi/bfa/include/defs/bfa_defs_driver.h new file mode 100644 index 000000000000..57049805762b --- /dev/null +++ b/drivers/scsi/bfa/include/defs/bfa_defs_driver.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2005-2009 Brocade Communications Systems, Inc. + * All rights reserved + * www.brocade.com + * + * Linux driver for Brocade Fibre Channel Host Bus Adapter. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (GPL) Version 2 as + * published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#ifndef __BFA_DEFS_DRIVER_H__ +#define __BFA_DEFS_DRIVER_H__ + +/** + * Driver statistics + */ + u16 tm_io_abort; + u16 tm_io_abort_comp; + u16 tm_lun_reset; + u16 tm_lun_reset_comp; + u16 tm_target_reset; + u16 tm_bus_reset; + u16 ioc_restart; /* IOC restart count */ + u16 io_pending; /* outstanding io count per-IOC */ + u64 control_req; + u64 input_req; + u64 output_req; + u64 input_words; + u64 output_words; +} bfa_driver_stats_t; + + +#endif /* __BFA_DEFS_DRIVER_H__ */ diff --git a/drivers/scsi/bfa/include/defs/bfa_defs_ethport.h b/drivers/scsi/bfa/include/defs/bfa_defs_ethport.h new file mode 100644 index 000000000000..79f9b3e146f7 --- /dev/null +++ b/drivers/scsi/bfa/include/defs/bfa_defs_ethport.h @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2005-2009 Brocade Communications Systems, Inc. + * All rights reserved + * www.brocade.com + * + * Linux driver for Brocade Fibre Channel Host Bus Adapter. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (GPL) Version 2 as + * published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#ifndef __BFA_DEFS_ETHPORT_H__ +#define __BFA_DEFS_ETHPORT_H__ + +#include <defs/bfa_defs_status.h> +#include <protocol/types.h> +#include <cna/pstats/phyport_defs.h> +#include <cna/pstats/ethport_defs.h> + +struct bna_tx_info_s { + u32 miniport_state; + u32 adapter_state; + u64 tx_count; + u64 tx_wi; + u64 tx_sg; + u64 tx_tcp_chksum; + u64 tx_udp_chksum; + u64 tx_ip_chksum; + u64 tx_lsov1; + u64 tx_lsov2; + u64 tx_max_sg_len ; +}; + +struct bna_rx_queue_info_s { + u16 q_id ; + u16 buf_size ; + u16 buf_count ; + u16 rsvd ; + u64 rx_count ; + u64 rx_dropped ; + u64 rx_unsupported ; + u64 rx_internal_err ; + u64 rss_count ; + u64 vlan_count ; + u64 rx_tcp_chksum ; + u64 rx_udp_chksum ; + u64 rx_ip_chksum ; + u64 rx_hds ; +}; + +struct bna_rx_q_set_s { + u16 q_set_type; + u32 miniport_state; + u32 adapter_state; + struct bna_rx_queue_info_s rx_queue[2]; +}; + +struct bna_port_stats_s { + struct bna_tx_info_s tx_stats; + u16 qset_count ; + struct bna_rx_q_set_s rx_qset[8]; +}; + +struct bfa_ethport_stats_s { + struct bna_stats_txf txf_stats[1]; + struct bna_stats_rxf rxf_stats[1]; + struct bnad_drv_stats drv_stats; +}; + +/** + * Ethernet port events + * Arguments below are in BFAL context from Mgmt + * BFA_PORT_AEN_ETH_LINKUP: [in]: mac [out]: mac + * BFA_PORT_AEN_ETH_LINKDOWN: [in]: mac [out]: mac + * BFA_PORT_AEN_ETH_ENABLE: [in]: mac [out]: mac + * BFA_PORT_AEN_ETH_DISABLE: [in]: mac [out]: mac + * + */ +enum bfa_ethport_aen_event { + BFA_ETHPORT_AEN_LINKUP = 1, /* Base Port Ethernet link up event */ + BFA_ETHPORT_AEN_LINKDOWN = 2, /* Base Port Ethernet link down event */ + BFA_ETHPORT_AEN_ENABLE = 3, /* Base Port Ethernet link enable event */ + BFA_ETHPORT_AEN_DISABLE = 4, /* Base Port Ethernet link disable + * event */ +}; + +struct bfa_ethport_aen_data_s { + mac_t mac; /* MAC address of the physical port */ +}; + + +#endif /* __BFA_DEFS_ETHPORT_H__ */ diff --git a/drivers/scsi/bfa/include/defs/bfa_defs_fcpim.h b/drivers/scsi/bfa/include/defs/bfa_defs_fcpim.h new file mode 100644 index 000000000000..c08f4f5026ac --- /dev/null +++ b/drivers/scsi/bfa/include/defs/bfa_defs_fcpim.h @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2005-2009 Brocade Communications Systems, Inc. + * All rights reserved + * www.brocade.com + * + * Linux driver for Brocade Fibre Channel Host Bus Adapter. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (GPL) Version 2 as + * published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ +#ifndef __BFA_DEFS_FCPIM_H__ +#define __BFA_DEFS_FCPIM_H__ + +struct bfa_fcpim_stats_s { + u32 total_ios; /* Total IO count */ + u32 qresumes; /* IO waiting for CQ space */ + u32 no_iotags; /* NO IO contexts */ + u32 io_aborts; /* IO abort requests */ + u32 no_tskims; /* NO task management contexts */ + u32 iocomp_ok; /* IO completions with OK status */ + u32 iocomp_underrun; /* IO underrun (good) */ + u32 iocomp_overrun; /* IO overrun (good) */ + u32 iocomp_aborted; /* Aborted IO requests */ + u32 iocomp_timedout; /* IO timeouts */ + u32 iocom_nexus_abort; /* IO selection timeouts */ + u32 iocom_proto_err; /* IO protocol errors */ + u32 iocom_dif_err; /* IO SBC-3 protection errors */ + u32 iocom_tm_abort; /* IO aborted by TM requests */ + u32 iocom_sqer_needed; /* IO retry for SQ error + *recovery */ + u32 iocom_res_free; /* Delayed freeing of IO resources */ + u32 iocomp_scsierr; /* IO with non-good SCSI status */ + u32 iocom_hostabrts; /* Host IO abort requests */ + u32 iocom_utags; /* IO comp with unknown tags */ + u32 io_cleanups; /* IO implicitly aborted */ + u32 io_tmaborts; /* IO aborted due to TM commands */ + u32 rsvd; +}; +#endif /*__BFA_DEFS_FCPIM_H__*/ diff --git a/drivers/scsi/bfa/include/defs/bfa_defs_im_common.h b/drivers/scsi/bfa/include/defs/bfa_defs_im_common.h new file mode 100644 index 000000000000..9ccf53bef65a --- /dev/null +++ b/drivers/scsi/bfa/include/defs/bfa_defs_im_common.h @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2005-2009 Brocade Communications Systems, Inc. + * All rights reserved + * www.brocade.com + * + * Linux driver for Brocade Fibre Channel Host Bus Adapter. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (GPL) Version 2 as + * published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#ifndef __BFA_DEFS_IM_COMMON_H__ +#define __BFA_DEFS_IM_COMMON_H__ + +#define BFA_ADAPTER_NAME_LEN 256 +#define BFA_ADAPTER_GUID_LEN 256 +#define RESERVED_VLAN_NAME L"PORT VLAN" +#define PASSTHRU_VLAN_NAME L"PASSTHRU VLAN" + + u64 tx_pkt_cnt; + u64 rx_pkt_cnt; + u32 duration; + u8 status; +} bfa_im_stats_t, *pbfa_im_stats_t; + +#endif /* __BFA_DEFS_IM_COMMON_H__ */ diff --git a/drivers/scsi/bfa/include/defs/bfa_defs_im_team.h b/drivers/scsi/bfa/include/defs/bfa_defs_im_team.h new file mode 100644 index 000000000000..a486a7eb81d6 --- /dev/null +++ b/drivers/scsi/bfa/include/defs/bfa_defs_im_team.h @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2005-2009 Brocade Communications Systems, Inc. + * All rights reserved + * www.brocade.com + * + * Linux driver for Brocade Fibre Channel Host Bus Adapter. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (GPL) Version 2 as + * published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#ifndef __BFA_DEFS_IM_TEAM_H__ +#define __BFA_DEFS_IM_TEAM_H__ + +#include <protocol/types.h> + +#define BFA_TEAM_MAX_PORTS 8 +#define BFA_TEAM_NAME_LEN 256 +#define BFA_MAX_NUM_TEAMS 16 +#define BFA_TEAM_INVALID_DELAY -1 + + BFA_LACP_RATE_SLOW = 1, + BFA_LACP_RATE_FAST +} bfa_im_lacp_rate_t; + + BFA_TEAM_MODE_FAIL_OVER = 1, + BFA_TEAM_MODE_FAIL_BACK, + BFA_TEAM_MODE_LACP, + BFA_TEAM_MODE_NONE +} bfa_im_team_mode_t; + + BFA_XMIT_POLICY_L2 = 1, + BFA_XMIT_POLICY_L3_L4 +} bfa_im_xmit_policy_t; + + bfa_im_team_mode_t team_mode; + bfa_im_lacp_rate_t lacp_rate; + bfa_im_xmit_policy_t xmit_policy; + int delay; + wchar_t primary[BFA_ADAPTER_NAME_LEN]; + wchar_t preferred_primary[BFA_ADAPTER_NAME_LEN]; + mac_t mac; + u16 num_ports; + u16 num_vlans; + u16 vlan_list[BFA_MAX_VLANS_PER_PORT]; + wchar_t team_guid_list[BFA_TEAM_MAX_PORTS][BFA_ADAPTER_GUID_LEN]; + wchar_t ioc_name_list[BFA_TEAM_MAX_PORTS][BFA_ADAPTER_NAME_LEN]; +} bfa_im_team_attr_t; + + wchar_t team_name[BFA_TEAM_NAME_LEN]; + bfa_im_xmit_policy_t xmit_policy; + int delay; + wchar_t primary[BFA_ADAPTER_NAME_LEN]; + wchar_t preferred_primary[BFA_ADAPTER_NAME_LEN]; +} bfa_im_team_edit_t, *pbfa_im_team_edit_t; + + wchar_t team_name[BFA_TEAM_NAME_LEN]; + bfa_im_team_mode_t team_mode; + mac_t mac; +} bfa_im_team_info_t; + + bfa_im_team_info_t team_info[BFA_MAX_NUM_TEAMS]; + u16 num_teams; +} bfa_im_team_list_t, *pbfa_im_team_list_t; + +#endif /* __BFA_DEFS_IM_TEAM_H__ */ diff --git a/drivers/scsi/bfa/include/defs/bfa_defs_ioc.h b/drivers/scsi/bfa/include/defs/bfa_defs_ioc.h new file mode 100644 index 000000000000..b1d532da3a9d --- /dev/null +++ b/drivers/scsi/bfa/include/defs/bfa_defs_ioc.h @@ -0,0 +1,152 @@ +/* + * Copyright (c) 2005-2009 Brocade Communications Systems, Inc. + * All rights reserved + * www.brocade.com + * + * Linux driver for Brocade Fibre Channel Host Bus Adapter. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (GPL) Version 2 as + * published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#ifndef __BFA_DEFS_IOC_H__ +#define __BFA_DEFS_IOC_H__ + +#include <protocol/types.h> +#include <defs/bfa_defs_types.h> +#include <defs/bfa_defs_version.h> +#include <defs/bfa_defs_adapter.h> +#include <defs/bfa_defs_pm.h> + +enum { + BFA_IOC_DRIVER_LEN = 16, + BFA_IOC_CHIP_REV_LEN = 8, +}; + +/** + * Driver and firmware versions. + */ +struct bfa_ioc_driver_attr_s { + char driver[BFA_IOC_DRIVER_LEN]; /* driver name */ + char driver_ver[BFA_VERSION_LEN]; /* driver version */ + char fw_ver[BFA_VERSION_LEN]; /* firmware version*/ + char bios_ver[BFA_VERSION_LEN]; /* bios version */ + char efi_ver[BFA_VERSION_LEN]; /* EFI version */ + char ob_ver[BFA_VERSION_LEN]; /* openboot version*/ +}; + +/** + * IOC PCI device attributes + */ +struct bfa_ioc_pci_attr_s { + u16 vendor_id; /* PCI vendor ID */ + u16 device_id; /* PCI device ID */ + u16 ssid; /* subsystem ID */ + u16 ssvid; /* subsystem vendor ID */ + u32 pcifn; /* PCI device function */ + u32 rsvd; /* padding */ + u8 chip_rev[BFA_IOC_CHIP_REV_LEN]; /* chip revision */ +}; + +/** + * IOC states + */ +enum bfa_ioc_state { + BFA_IOC_RESET = 1, /* IOC is in reset state */ + BFA_IOC_SEMWAIT = 2, /* Waiting for IOC hardware semaphore */ + BFA_IOC_HWINIT = 3, /* IOC hardware is being initialized */ + BFA_IOC_GETATTR = 4, /* IOC is being configured */ + BFA_IOC_OPERATIONAL = 5, /* IOC is operational */ + BFA_IOC_INITFAIL = 6, /* IOC hardware failure */ + BFA_IOC_HBFAIL = 7, /* IOC heart-beat failure */ + BFA_IOC_DISABLING = 8, /* IOC is being disabled */ + BFA_IOC_DISABLED = 9, /* IOC is disabled */ + BFA_IOC_FWMISMATCH = 10, /* IOC firmware different from drivers */ +}; + +/** + * IOC firmware stats + */ +struct bfa_fw_ioc_stats_s { + u32 hb_count; + u32 cfg_reqs; + u32 enable_reqs; + u32 disable_reqs; + u32 stats_reqs; + u32 clrstats_reqs; + u32 unknown_reqs; + u32 ic_reqs; /* interrupt coalesce reqs */ +}; + +/** + * IOC driver stats + */ +struct bfa_ioc_drv_stats_s { + u32 ioc_isrs; + u32 ioc_enables; + u32 ioc_disables; + u32 ioc_hbfails; + u32 ioc_boots; + u32 stats_tmos; + u32 hb_count; + u32 disable_reqs; + u32 enable_reqs; + u32 disable_replies; + u32 enable_replies; +}; + +/** + * IOC statistics + */ +struct bfa_ioc_stats_s { + struct bfa_ioc_drv_stats_s drv_stats; /* driver IOC stats */ + struct bfa_fw_ioc_stats_s fw_stats; /* firmware IOC stats */ +}; + + +enum bfa_ioc_type_e { + BFA_IOC_TYPE_FC = 1, + BFA_IOC_TYPE_FCoE = 2, + BFA_IOC_TYPE_LL = 3, +}; + +/** + * IOC attributes returned in queries + */ +struct bfa_ioc_attr_s { + enum bfa_ioc_type_e ioc_type; + enum bfa_ioc_state state; /* IOC state */ + struct bfa_adapter_attr_s adapter_attr; /* HBA attributes */ + struct bfa_ioc_driver_attr_s driver_attr; /* driver attr */ + struct bfa_ioc_pci_attr_s pci_attr; + u8 port_id; /* port number */ +}; + +/** + * BFA IOC level events + */ +enum bfa_ioc_aen_event { + BFA_IOC_AEN_HBGOOD = 1, /* Heart Beat restore event */ + BFA_IOC_AEN_HBFAIL = 2, /* Heart Beat failure event */ + BFA_IOC_AEN_ENABLE = 3, /* IOC enabled event */ + BFA_IOC_AEN_DISABLE = 4, /* IOC disabled event */ + BFA_IOC_AEN_FWMISMATCH = 5, /* IOC firmware mismatch */ +}; + +/** + * BFA IOC level event data, now just a place holder + */ +struct bfa_ioc_aen_data_s { + enum bfa_ioc_type_e ioc_type; + wwn_t pwwn; + mac_t mac; +}; + +#endif /* __BFA_DEFS_IOC_H__ */ + diff --git a/drivers/scsi/bfa/include/defs/bfa_defs_iocfc.h b/drivers/scsi/bfa/include/defs/bfa_defs_iocfc.h new file mode 100644 index 000000000000..d76bcbd9820f --- /dev/null +++ b/drivers/scsi/bfa/include/defs/bfa_defs_iocfc.h @@ -0,0 +1,310 @@ +/* + * Copyright (c) 2005-2009 Brocade Communications Systems, Inc. + * All rights reserved + * www.brocade.com + * + * Linux driver for Brocade Fibre Channel Host Bus Adapter. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (GPL) Version 2 as + * published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#ifndef __BFA_DEFS_IOCFC_H__ +#define __BFA_DEFS_IOCFC_H__ + +#include <protocol/types.h> +#include <defs/bfa_defs_types.h> +#include <defs/bfa_defs_version.h> +#include <defs/bfa_defs_adapter.h> +#include <defs/bfa_defs_pm.h> + +#define BFA_IOCFC_INTR_DELAY 1125 +#define BFA_IOCFC_INTR_LATENCY 225 + +/** + * Interrupt coalescing configuration. + */ +struct bfa_iocfc_intr_attr_s { + bfa_boolean_t coalesce; /* enable/disable coalescing */ + u16 latency; /* latency in microseconds */ + u16 delay; /* delay in microseconds */ +}; + +/** + * IOC firmware configuraton + */ +struct bfa_iocfc_fwcfg_s { + u16 num_fabrics; /* number of fabrics */ + u16 num_lports; /* number of local lports */ + u16 num_rports; /* number of remote ports */ + u16 num_ioim_reqs; /* number of IO reqs */ + u16 num_tskim_reqs; /* task management requests */ + u16 num_iotm_reqs; /* number of TM IO reqs */ + u16 num_tsktm_reqs; /* TM task management requests*/ + u16 num_fcxp_reqs; /* unassisted FC exchanges */ + u16 num_uf_bufs; /* unsolicited recv buffers */ + u8 num_cqs; + u8 rsvd; +}; + +struct bfa_iocfc_drvcfg_s { + u16 num_reqq_elems; /* number of req queue elements */ + u16 num_rspq_elems; /* number of rsp queue elements */ + u16 num_sgpgs; /* number of total SG pages */ + u16 num_sboot_tgts; /* number of SAN boot targets */ + u16 num_sboot_luns; /* number of SAN boot luns */ + u16 ioc_recover; /* IOC recovery mode */ + u16 min_cfg; /* minimum configuration */ + u16 path_tov; /* device path timeout */ + bfa_boolean_t delay_comp; /* delay completion of + failed inflight IOs */ + u32 rsvd; +}; +/** + * IOC configuration + */ +struct bfa_iocfc_cfg_s { + struct bfa_iocfc_fwcfg_s fwcfg; /* firmware side config */ + struct bfa_iocfc_drvcfg_s drvcfg; /* driver side config */ +}; + +/** + * IOC firmware IO stats + */ +struct bfa_fw_io_stats_s { + u32 host_abort; /* IO aborted by host driver*/ + u32 host_cleanup; /* IO clean up by host driver */ + + u32 fw_io_timeout; /* IOs timedout */ + u32 fw_frm_parse; /* frame parsed by f/w */ + u32 fw_frm_data; /* fcp_data frame parsed by f/w */ + u32 fw_frm_rsp; /* fcp_rsp frame parsed by f/w */ + u32 fw_frm_xfer_rdy; /* xfer_rdy frame parsed by f/w */ + u32 fw_frm_bls_acc; /* BLS ACC frame parsed by f/w */ + u32 fw_frm_tgt_abort; /* target ABTS parsed by f/w */ + u32 fw_frm_unknown; /* unknown parsed by f/w */ + u32 fw_data_dma; /* f/w DMA'ed the data frame */ + u32 fw_frm_drop; /* f/w drop the frame */ + + u32 rec_timeout; /* FW rec timed out */ + u32 error_rec; /* FW sending rec on + * an error condition*/ + u32 wait_for_si; /* FW wait for SI */ + u32 rec_rsp_inval; /* REC rsp invalid */ + u32 seqr_io_abort; /* target does not know cmd so abort */ + u32 seqr_io_retry; /* SEQR failed so retry IO */ + + u32 itn_cisc_upd_rsp; /* ITN cisc updated on fcp_rsp */ + u32 itn_cisc_upd_data; /* ITN cisc updated on fcp_data */ + u32 itn_cisc_upd_xfer_rdy; /* ITN cisc updated on fcp_data */ + + u32 fcp_data_lost; /* fcp data lost */ + + u32 ro_set_in_xfer_rdy; /* Target set RO in Xfer_rdy frame */ + u32 xfer_rdy_ooo_err; /* Out of order Xfer_rdy received */ + u32 xfer_rdy_unknown_err; /* unknown error in xfer_rdy frame */ + + u32 io_abort_timeout; /* ABTS timedout */ + u32 sler_initiated; /* SLER initiated */ + + u32 unexp_fcp_rsp; /* fcp response in wrong state */ + + u32 fcp_rsp_under_run; /* fcp rsp IO underrun */ + u32 fcp_rsp_under_run_wr; /* fcp rsp IO underrun for write */ + u32 fcp_rsp_under_run_err; /* fcp rsp IO underrun error */ + u32 fcp_rsp_resid_inval; /* invalid residue */ + u32 fcp_rsp_over_run; /* fcp rsp IO overrun */ + u32 fcp_rsp_over_run_err; /* fcp rsp IO overrun error */ + u32 fcp_rsp_proto_err; /* protocol error in fcp rsp */ + u32 fcp_rsp_sense_err; /* error in sense info in fcp rsp */ + u32 fcp_conf_req; /* FCP conf requested */ + + u32 tgt_aborted_io; /* target initiated abort */ + + u32 ioh_edtov_timeout_event;/* IOH edtov timer popped */ + u32 ioh_fcp_rsp_excp_event; /* IOH FCP_RSP exception */ + u32 ioh_fcp_conf_event; /* IOH FCP_CONF */ + u32 ioh_mult_frm_rsp_event; /* IOH multi_frame FCP_RSP */ + u32 ioh_hit_class2_event; /* IOH hit class2 */ + u32 ioh_miss_other_event; /* IOH miss other */ + u32 ioh_seq_cnt_err_event; /* IOH seq cnt error */ + u32 ioh_len_err_event; /* IOH len error - fcp_dl != + * bytes xfered */ + u32 ioh_seq_len_err_event; /* IOH seq len error */ + u32 ioh_data_oor_event; /* Data out of range */ + u32 ioh_ro_ooo_event; /* Relative offset out of range */ + u32 ioh_cpu_owned_event; /* IOH hit -iost owned by f/w */ + u32 ioh_unexp_frame_event; /* unexpected frame recieved + * count */ + u32 ioh_err_int; /* IOH error int during data-phase + * for scsi write + */ +}; + +/** + * IOC port firmware stats + */ + +struct bfa_fw_port_fpg_stats_s { + u32 intr_evt; + u32 intr; + u32 intr_excess; + u32 intr_cause0; + u32 intr_other; + u32 intr_other_ign; + u32 sig_lost; + u32 sig_regained; + u32 sync_lost; + u32 sync_to; + u32 sync_regained; + u32 div2_overflow; + u32 div2_underflow; + u32 efifo_overflow; + u32 efifo_underflow; + u32 idle_rx; + u32 lrr_rx; + u32 lr_rx; + u32 ols_rx; + u32 nos_rx; + u32 lip_rx; + u32 arbf0_rx; + u32 mrk_rx; + u32 const_mrk_rx; + u32 prim_unknown; + u32 rsvd; +}; + + +struct bfa_fw_port_lksm_stats_s { + u32 hwsm_success; /* hwsm state machine success */ + u32 hwsm_fails; /* hwsm fails */ + u32 hwsm_wdtov; /* hwsm timed out */ + u32 swsm_success; /* swsm success */ + u32 swsm_fails; /* swsm fails */ + u32 swsm_wdtov; /* swsm timed out */ + u32 busybufs; /* link init failed due to busybuf */ + u32 buf_waits; /* bufwait state entries */ + u32 link_fails; /* link failures */ + u32 psp_errors; /* primitive sequence protocol errors */ + u32 lr_unexp; /* No. of times LR rx-ed unexpectedly */ + u32 lrr_unexp; /* No. of times LRR rx-ed unexpectedly */ + u32 lr_tx; /* No. of times LR tx started */ + u32 lrr_tx; /* No. of times LRR tx started */ + u32 ols_tx; /* No. of times OLS tx started */ + u32 nos_tx; /* No. of times NOS tx started */ +}; + + +struct bfa_fw_port_snsm_stats_s { + u32 hwsm_success; /* Successful hwsm terminations */ + u32 hwsm_fails; /* hwsm fail count */ + u32 hwsm_wdtov; /* hwsm timed out */ + u32 swsm_success; /* swsm success */ + u32 swsm_wdtov; /* swsm timed out */ + u32 error_resets; /* error resets initiated by upsm */ + u32 sync_lost; /* Sync loss count */ + u32 sig_lost; /* Signal loss count */ +}; + + +struct bfa_fw_port_physm_stats_s { + u32 module_inserts; /* Module insert count */ + u32 module_xtracts; /* Module extracts count */ + u32 module_invalids; /* Invalid module inserted count */ + u32 module_read_ign; /* Module validation status ignored */ + u32 laser_faults; /* Laser fault count */ + u32 rsvd; +}; + + +struct bfa_fw_fip_stats_s { + u32 disc_req; /* Discovery solicit requests */ + u32 disc_rsp; /* Discovery solicit response */ + u32 disc_err; /* Discovery advt. parse errors */ + u32 disc_unsol; /* Discovery unsolicited */ + u32 disc_timeouts; /* Discovery timeouts */ + u32 linksvc_unsupp; /* Unsupported link service req */ + u32 linksvc_err; /* Parse error in link service req */ + u32 logo_req; /* Number of FIP logos received */ + u32 clrvlink_req; /* Clear virtual link req */ + u32 op_unsupp; /* Unsupported FIP operation */ + u32 untagged; /* Untagged frames (ignored) */ + u32 rsvd; +}; + + +struct bfa_fw_lps_stats_s { + u32 mac_invalids; /* Invalid mac assigned */ + u32 rsvd; +}; + + +struct bfa_fw_fcoe_stats_s { + u32 cee_linkups; /* CEE link up count */ + u32 cee_linkdns; /* CEE link down count */ + u32 fip_linkups; /* FIP link up count */ + u32 fip_linkdns; /* FIP link up count */ + u32 fip_fails; /* FIP fail count */ + u32 mac_invalids; /* Invalid mac assigned */ +}; + +/** + * IOC firmware FCoE port stats + */ +struct bfa_fw_fcoe_port_stats_s { + struct bfa_fw_fcoe_stats_s fcoe_stats; + struct bfa_fw_fip_stats_s fip_stats; +}; + +/** + * IOC firmware FC port stats + */ +struct bfa_fw_fc_port_stats_s { + struct bfa_fw_port_fpg_stats_s fpg_stats; + struct bfa_fw_port_physm_stats_s physm_stats; + struct bfa_fw_port_snsm_stats_s snsm_stats; + struct bfa_fw_port_lksm_stats_s lksm_stats; +}; + +/** + * IOC firmware FC port stats + */ +union bfa_fw_port_stats_s { + struct bfa_fw_fc_port_stats_s fc_stats; + struct bfa_fw_fcoe_port_stats_s fcoe_stats; +}; + +/** + * IOC firmware stats + */ +struct bfa_fw_stats_s { + struct bfa_fw_ioc_stats_s ioc_stats; + struct bfa_fw_io_stats_s io_stats; + union bfa_fw_port_stats_s port_stats; +}; + +/** + * IOC statistics + */ +struct bfa_iocfc_stats_s { + struct bfa_fw_stats_s fw_stats; /* firmware IOC stats */ +}; + +/** + * IOC attributes returned in queries + */ +struct bfa_iocfc_attr_s { + struct bfa_iocfc_cfg_s config; /* IOCFC config */ + struct bfa_iocfc_intr_attr_s intr_attr; /* interrupt attr */ +}; + +#define BFA_IOCFC_PATHTOV_MAX 60 +#define BFA_IOCFC_QDEPTH_MAX 2000 + +#endif /* __BFA_DEFS_IOC_H__ */ diff --git a/drivers/scsi/bfa/include/defs/bfa_defs_ipfc.h b/drivers/scsi/bfa/include/defs/bfa_defs_ipfc.h new file mode 100644 index 000000000000..7cb63ea98f38 --- /dev/null +++ b/drivers/scsi/bfa/include/defs/bfa_defs_ipfc.h @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2005-2009 Brocade Communications Systems, Inc. + * All rights reserved + * www.brocade.com + * + * Linux driver for Brocade Fibre Channel Host Bus Adapter. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (GPL) Version 2 as + * published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ +#ifndef __BFA_DEFS_IPFC_H__ +#define __BFA_DEFS_IPFC_H__ + +#include <bfa_os_inc.h> +#include <protocol/types.h> +#include <defs/bfa_defs_types.h> + +/** + * FCS ip remote port states + */ +enum bfa_iprp_state { + BFA_IPRP_UNINIT = 0, /* PORT is not yet initialized */ + BFA_IPRP_ONLINE = 1, /* process login is complete */ + BFA_IPRP_OFFLINE = 2, /* iprp is offline */ +}; + +/** + * FCS remote port statistics + */ +struct bfa_iprp_stats_s { + u32 offlines; + u32 onlines; + u32 rscns; + u32 plogis; + u32 logos; + u32 plogi_timeouts; + u32 plogi_rejects; +}; + +/** + * FCS iprp attribute returned in queries + */ +struct bfa_iprp_attr_s { + enum bfa_iprp_state state; +}; + +struct bfa_ipfc_stats_s { + u32 arp_sent; + u32 arp_recv; + u32 arp_reply_sent; + u32 arp_reply_recv; + u32 farp_sent; + u32 farp_recv; + u32 farp_reply_sent; + u32 farp_reply_recv; + u32 farp_reject_sent; + u32 farp_reject_recv; +}; + +struct bfa_ipfc_attr_s { + bfa_boolean_t enabled; +}; + +#endif /* __BFA_DEFS_IPFC_H__ */ diff --git a/drivers/scsi/bfa/include/defs/bfa_defs_itnim.h b/drivers/scsi/bfa/include/defs/bfa_defs_itnim.h new file mode 100644 index 000000000000..2ec769903d24 --- /dev/null +++ b/drivers/scsi/bfa/include/defs/bfa_defs_itnim.h @@ -0,0 +1,126 @@ +/* + * Copyright (c) 2005-2009 Brocade Communications Systems, Inc. + * All rights reserved + * www.brocade.com + * + * Linux driver for Brocade Fibre Channel Host Bus Adapter. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (GPL) Version 2 as + * published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ +#ifndef __BFA_DEFS_ITNIM_H__ +#define __BFA_DEFS_ITNIM_H__ + +#include <bfa_os_inc.h> +#include <protocol/types.h> + +/** + * FCS itnim states + */ +enum bfa_itnim_state { + BFA_ITNIM_OFFLINE = 0, /* offline */ + BFA_ITNIM_PRLI_SEND = 1, /* prli send */ + BFA_ITNIM_PRLI_SENT = 2, /* prli sent */ + BFA_ITNIM_PRLI_RETRY = 3, /* prli retry */ + BFA_ITNIM_HCB_ONLINE = 4, /* online callback */ + BFA_ITNIM_ONLINE = 5, /* online */ + BFA_ITNIM_HCB_OFFLINE = 6, /* offline callback */ + BFA_ITNIM_INITIATIOR = 7, /* initiator */ +}; + +struct bfa_itnim_hal_stats_s { + u32 onlines; /* ITN nexus onlines (PRLI done) */ + u32 offlines; /* ITN Nexus offlines */ + u32 creates; /* ITN create requests */ + u32 deletes; /* ITN delete requests */ + u32 create_comps; /* ITN create completions */ + u32 delete_comps; /* ITN delete completions */ + u32 sler_events; /* SLER (sequence level error + * recovery) events */ + u32 ioc_disabled; /* Num IOC disables */ + u32 cleanup_comps; /* ITN cleanup completions */ + u32 tm_cmnds; /* task management(TM) cmnds sent */ + u32 tm_fw_rsps; /* TM cmds firmware responses */ + u32 tm_success; /* TM successes */ + u32 tm_failures; /* TM failures */ + u32 tm_io_comps; /* TM IO completions */ + u32 tm_qresumes; /* TM queue resumes (after waiting + * for resources) + */ + u32 tm_iocdowns; /* TM cmnds affected by IOC down */ + u32 tm_cleanups; /* TM cleanups */ + u32 tm_cleanup_comps; + /* TM cleanup completions */ + u32 ios; /* IO requests */ + u32 io_comps; /* IO completions */ + u64 input_reqs; /* INPUT requests */ + u64 output_reqs; /* OUTPUT requests */ +}; + +/** + * FCS remote port statistics + */ +struct bfa_itnim_stats_s { + u32 onlines; /* num rport online */ + u32 offlines; /* num rport offline */ + u32 prli_sent; /* num prli sent out */ + u32 fcxp_alloc_wait;/* num fcxp alloc waits */ + u32 prli_rsp_err; /* num prli rsp errors */ + u32 prli_rsp_acc; /* num prli rsp accepts */ + u32 initiator; /* rport is an initiator */ + u32 prli_rsp_parse_err; /* prli rsp parsing errors */ + u32 prli_rsp_rjt; /* num prli rsp rejects */ + u32 timeout; /* num timeouts detected */ + u32 sler; /* num sler notification from BFA */ + u32 rsvd; + struct bfa_itnim_hal_stats_s hal_stats; +}; + +/** + * FCS itnim attributes returned in queries + */ +struct bfa_itnim_attr_s { + enum bfa_itnim_state state; /* FCS itnim state */ + u8 retry; /* data retransmision support */ + u8 task_retry_id; /* task retry ident support */ + u8 rec_support; /* REC supported */ + u8 conf_comp; /* confirmed completion supp */ +}; + +/** + * BFA ITNIM events. + * Arguments below are in BFAL context from Mgmt + * BFA_ITNIM_AEN_NEW: [in]: None [out]: vf_id, lpwwn + * BFA_ITNIM_AEN_DELETE: [in]: vf_id, lpwwn, rpwwn (0 = all fcp4 targets), + * [out]: vf_id, ppwwn, lpwwn, rpwwn + * BFA_ITNIM_AEN_ONLINE: [in]: vf_id, lpwwn, rpwwn (0 = all fcp4 targets), + * [out]: vf_id, ppwwn, lpwwn, rpwwn + * BFA_ITNIM_AEN_OFFLINE: [in]: vf_id, lpwwn, rpwwn (0 = all fcp4 targets), + * [out]: vf_id, ppwwn, lpwwn, rpwwn + * BFA_ITNIM_AEN_DISCONNECT:[in]: vf_id, lpwwn, rpwwn (0 = all fcp4 targets), + * [out]: vf_id, ppwwn, lpwwn, rpwwn + */ +enum bfa_itnim_aen_event { + BFA_ITNIM_AEN_ONLINE = 1, /* Target online */ + BFA_ITNIM_AEN_OFFLINE = 2, /* Target offline */ + BFA_ITNIM_AEN_DISCONNECT = 3, /* Target disconnected */ +}; + +/** + * BFA ITNIM event data structure. + */ +struct bfa_itnim_aen_data_s { + u16 vf_id; /* vf_id of the IT nexus */ + u16 rsvd[3]; + wwn_t ppwwn; /* WWN of its physical port */ + wwn_t lpwwn; /* WWN of logical port */ + wwn_t rpwwn; /* WWN of remote(target) port */ +}; + +#endif /* __BFA_DEFS_ITNIM_H__ */ diff --git a/drivers/scsi/bfa/include/defs/bfa_defs_led.h b/drivers/scsi/bfa/include/defs/bfa_defs_led.h new file mode 100644 index 000000000000..62039273264e --- /dev/null +++ b/drivers/scsi/bfa/include/defs/bfa_defs_led.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2005-2009 Brocade Communications Systems, Inc. + * All rights reserved + * www.brocade.com + * + * Linux driver for Brocade Fibre Channel Host Bus Adapter. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (GPL) Version 2 as + * published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#ifndef __BFA_DEFS_LED_H__ +#define __BFA_DEFS_LED_H__ + +#define BFA_LED_MAX_NUM 3 + +enum bfa_led_op { + BFA_LED_OFF = 0, + BFA_LED_ON = 1, + BFA_LED_FLICK = 2, + BFA_LED_BLINK = 3, +}; + +enum bfa_led_color { + BFA_LED_GREEN = 0, + BFA_LED_AMBER = 1, +}; + +#endif /* __BFA_DEFS_LED_H__ */ diff --git a/drivers/scsi/bfa/include/defs/bfa_defs_lport.h b/drivers/scsi/bfa/include/defs/bfa_defs_lport.h new file mode 100644 index 000000000000..7359f82aacfc --- /dev/null +++ b/drivers/scsi/bfa/include/defs/bfa_defs_lport.h @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2005-2009 Brocade Communications Systems, Inc. + * All rights reserved + * www.brocade.com + * + * Linux driver for Brocade Fibre Channel Host Bus Adapter. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (GPL) Version 2 as + * published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#ifndef __BFA_DEFS_LPORT_H__ +#define __BFA_DEFS_LPORT_H__ + +#include <defs/bfa_defs_types.h> +#include <defs/bfa_defs_port.h> + +/** + * BFA AEN logical port events. + * Arguments below are in BFAL context from Mgmt + * BFA_LPORT_AEN_NEW: [in]: None [out]: vf_id, ppwwn, lpwwn, roles + * BFA_LPORT_AEN_DELETE: [in]: lpwwn [out]: vf_id, ppwwn. lpwwn, roles + * BFA_LPORT_AEN_ONLINE: [in]: lpwwn [out]: vf_id, ppwwn. lpwwn, roles + * BFA_LPORT_AEN_OFFLINE: [in]: lpwwn [out]: vf_id, ppwwn. lpwwn, roles + * BFA_LPORT_AEN_DISCONNECT:[in]: lpwwn [out]: vf_id, ppwwn. lpwwn, roles + * BFA_LPORT_AEN_NEW_PROP: [in]: None [out]: vf_id, ppwwn. lpwwn, roles + * BFA_LPORT_AEN_DELETE_PROP: [in]: lpwwn [out]: vf_id, ppwwn. lpwwn, roles + * BFA_LPORT_AEN_NEW_STANDARD: [in]: None [out]: vf_id, ppwwn. lpwwn, roles + * BFA_LPORT_AEN_DELETE_STANDARD: [in]: lpwwn [out]: vf_id, ppwwn. lpwwn, roles + * BFA_LPORT_AEN_NPIV_DUP_WWN: [in]: lpwwn [out]: vf_id, ppwwn. lpwwn, roles + * BFA_LPORT_AEN_NPIV_FABRIC_MAX: [in]: lpwwn [out]: vf_id, ppwwn. lpwwn, roles + * BFA_LPORT_AEN_NPIV_UNKNOWN: [in]: lpwwn [out]: vf_id, ppwwn. lpwwn, roles + */ +enum bfa_lport_aen_event { + BFA_LPORT_AEN_NEW = 1, /* LPort created event */ + BFA_LPORT_AEN_DELETE = 2, /* LPort deleted event */ + BFA_LPORT_AEN_ONLINE = 3, /* LPort online event */ + BFA_LPORT_AEN_OFFLINE = 4, /* LPort offline event */ + BFA_LPORT_AEN_DISCONNECT = 5, /* LPort disconnect event */ + BFA_LPORT_AEN_NEW_PROP = 6, /* VPort created event */ + BFA_LPORT_AEN_DELETE_PROP = 7, /* VPort deleted event */ + BFA_LPORT_AEN_NEW_STANDARD = 8, /* VPort created event */ + BFA_LPORT_AEN_DELETE_STANDARD = 9, /* VPort deleted event */ + BFA_LPORT_AEN_NPIV_DUP_WWN = 10, /* VPort configured with + * duplicate WWN event + */ + BFA_LPORT_AEN_NPIV_FABRIC_MAX = 11, /* Max NPIV in fabric/fport */ + BFA_LPORT_AEN_NPIV_UNKNOWN = 12, /* Unknown NPIV Error code event */ +}; + +/** + * BFA AEN event data structure + */ +struct bfa_lport_aen_data_s { + u16 vf_id; /* vf_id of this logical port */ + u16 rsvd; + enum bfa_port_role roles; /* Logical port mode,IM/TM/IP etc */ + wwn_t ppwwn; /* WWN of its physical port */ + wwn_t lpwwn; /* WWN of this logical port */ +}; + +#endif /* __BFA_DEFS_LPORT_H__ */ diff --git a/drivers/scsi/bfa/include/defs/bfa_defs_mfg.h b/drivers/scsi/bfa/include/defs/bfa_defs_mfg.h new file mode 100644 index 000000000000..13fd4ab6aae2 --- /dev/null +++ b/drivers/scsi/bfa/include/defs/bfa_defs_mfg.h @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2005-2009 Brocade Communications Systems, Inc. + * All rights reserved + * www.brocade.com + * + * Linux driver for Brocade Fibre Channel Host Bus Adapter. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (GPL) Version 2 as + * published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ +#ifndef __BFA_DEFS_MFG_H__ +#define __BFA_DEFS_MFG_H__ + +#include <bfa_os_inc.h> + +/** + * Manufacturing block version + */ +#define BFA_MFG_VERSION 1 + +/** + * Manufacturing block format + */ +#define BFA_MFG_SERIALNUM_SIZE 11 +#define BFA_MFG_PARTNUM_SIZE 14 +#define BFA_MFG_SUPPLIER_ID_SIZE 10 +#define BFA_MFG_SUPPLIER_PARTNUM_SIZE 20 +#define BFA_MFG_SUPPLIER_SERIALNUM_SIZE 20 +#define BFA_MFG_SUPPLIER_REVISION_SIZE 4 +#define STRSZ(_n) (((_n) + 4) & ~3) + +/** + * VPD data length + */ +#define BFA_MFG_VPD_LEN 256 + +/** + * All numerical fields are in big-endian format. + */ +struct bfa_mfg_vpd_s { + u8 version; /* vpd data version */ + u8 vpd_sig[3]; /* characters 'V', 'P', 'D' */ + u8 chksum; /* u8 checksum */ + u8 vendor; /* vendor */ + u8 len; /* vpd data length excluding header */ + u8 rsv; + u8 data[BFA_MFG_VPD_LEN]; /* vpd data */ +}; + +#pragma pack(1) + +#endif /* __BFA_DEFS_MFG_H__ */ diff --git a/drivers/scsi/bfa/include/defs/bfa_defs_pci.h b/drivers/scsi/bfa/include/defs/bfa_defs_pci.h new file mode 100644 index 000000000000..c9b83321694b --- /dev/null +++ b/drivers/scsi/bfa/include/defs/bfa_defs_pci.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2005-2009 Brocade Communications Systems, Inc. + * All rights reserved + * www.brocade.com + * + * Linux driver for Brocade Fibre Channel Host Bus Adapter. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (GPL) Version 2 as + * published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#ifndef __BFA_DEFS_PCI_H__ +#define __BFA_DEFS_PCI_H__ + +/** + * PCI device and vendor ID information + */ +enum { + BFA_PCI_VENDOR_ID_BROCADE = 0x1657, + BFA_PCI_DEVICE_ID_FC_8G2P = 0x13, + BFA_PCI_DEVICE_ID_FC_8G1P = 0x17, + BFA_PCI_DEVICE_ID_CT = 0x14, +}; + +/** + * PCI sub-system device and vendor ID information + */ +enum { + BFA_PCI_FCOE_SSDEVICE_ID = 0x14, +}; + +#define BFA_PCI_ACCESS_RANGES 1 /* Maximum number of device address ranges + * mapped through different BAR(s). */ + +#endif /* __BFA_DEFS_PCI_H__ */ diff --git a/drivers/scsi/bfa/include/defs/bfa_defs_pm.h b/drivers/scsi/bfa/include/defs/bfa_defs_pm.h new file mode 100644 index 000000000000..e8d6d959006e --- /dev/null +++ b/drivers/scsi/bfa/include/defs/bfa_defs_pm.h @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2005-2009 Brocade Communications Systems, Inc. + * All rights reserved + * www.brocade.com + * + * Linux driver for Brocade Fibre Channel Host Bus Adapter. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (GPL) Version 2 as + * published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#ifndef __BFA_DEFS_PM_H__ +#define __BFA_DEFS_PM_H__ + +#include <bfa_os_inc.h> + +/** + * BFA power management device states + */ +enum bfa_pm_ds { + BFA_PM_DS_D0 = 0, /* full power mode */ + BFA_PM_DS_D1 = 1, /* power save state 1 */ + BFA_PM_DS_D2 = 2, /* power save state 2 */ + BFA_PM_DS_D3 = 3, /* power off state */ +}; + +#endif /* __BFA_DEFS_PM_H__ */ diff --git a/drivers/scsi/bfa/include/defs/bfa_defs_pom.h b/drivers/scsi/bfa/include/defs/bfa_defs_pom.h new file mode 100644 index 000000000000..d9fa278472b7 --- /dev/null +++ b/drivers/scsi/bfa/include/defs/bfa_defs_pom.h @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2005-2009 Brocade Communications Systems, Inc. + * All rights reserved + * www.brocade.com + * + * Linux driver for Brocade Fibre Channel Host Bus Adapter. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (GPL) Version 2 as + * published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ +#ifndef __BFA_DEFS_POM_H__ +#define __BFA_DEFS_POM_H__ + +#include <bfa_os_inc.h> +#include <defs/bfa_defs_types.h> + +/** + * POM health status levels for each attributes. + */ +enum bfa_pom_entry_health { + BFA_POM_HEALTH_NOINFO = 1, /* no information */ + BFA_POM_HEALTH_NORMAL = 2, /* health is normal */ + BFA_POM_HEALTH_WARNING = 3, /* warning level */ + BFA_POM_HEALTH_ALARM = 4, /* alarming level */ +}; + +/** + * Reading of temperature/voltage/current/power + */ +struct bfa_pom_entry_s { + enum bfa_pom_entry_health health; /* POM entry health */ + u32 curr_value; /* current value */ + u32 thr_warn_high; /* threshold warning high */ + u32 thr_warn_low; /* threshold warning low */ + u32 thr_alarm_low; /* threshold alaram low */ + u32 thr_alarm_high; /* threshold alarm high */ +}; + +/** + * POM attributes + */ +struct bfa_pom_attr_s { + struct bfa_pom_entry_s temperature; /* centigrade */ + struct bfa_pom_entry_s voltage; /* volts */ + struct bfa_pom_entry_s curr; /* milli amps */ + struct bfa_pom_entry_s txpower; /* micro watts */ + struct bfa_pom_entry_s rxpower; /* micro watts */ +}; + +#endif /* __BFA_DEFS_POM_H__ */ diff --git a/drivers/scsi/bfa/include/defs/bfa_defs_port.h b/drivers/scsi/bfa/include/defs/bfa_defs_port.h new file mode 100644 index 000000000000..de0696c81bc4 --- /dev/null +++ b/drivers/scsi/bfa/include/defs/bfa_defs_port.h @@ -0,0 +1,245 @@ +/* + * Copyright (c) 2005-2009 Brocade Communications Systems, Inc. + * All rights reserved + * www.brocade.com + * + * Linux driver for Brocade Fibre Channel Host Bus Adapter. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (GPL) Version 2 as + * published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#ifndef __BFA_DEFS_PORT_H__ +#define __BFA_DEFS_PORT_H__ + +#include <bfa_os_inc.h> +#include <protocol/types.h> +#include <defs/bfa_defs_pport.h> +#include <defs/bfa_defs_ioc.h> + +#define BFA_FCS_FABRIC_IPADDR_SZ 16 + +/** + * symbolic names for base port/virtual port + */ +#define BFA_SYMNAME_MAXLEN 128 /* vmware/windows uses 128 bytes */ +struct bfa_port_symname_s { + char symname[BFA_SYMNAME_MAXLEN]; +}; + +/** +* Roles of FCS port: + * - FCP IM and FCP TM roles cannot be enabled together for a FCS port + * - Create multiple ports if both IM and TM functions required. + * - Atleast one role must be specified. + */ +enum bfa_port_role { + BFA_PORT_ROLE_FCP_IM = 0x01, /* FCP initiator role */ + BFA_PORT_ROLE_FCP_TM = 0x02, /* FCP target role */ + BFA_PORT_ROLE_FCP_IPFC = 0x04, /* IP over FC role */ + BFA_PORT_ROLE_FCP_MAX = BFA_PORT_ROLE_FCP_IPFC | BFA_PORT_ROLE_FCP_IM +}; + +/** + * FCS port configuration. + */ +struct bfa_port_cfg_s { + wwn_t pwwn; /* port wwn */ + wwn_t nwwn; /* node wwn */ + struct bfa_port_symname_s sym_name; /* vm port symbolic name */ + enum bfa_port_role roles; /* FCS port roles */ + u32 rsvd; + u8 tag[16]; /* opaque tag from application */ +}; + +/** + * FCS port states + */ +enum bfa_port_state { + BFA_PORT_UNINIT = 0, /* PORT is not yet initialized */ + BFA_PORT_FDISC = 1, /* FDISC is in progress */ + BFA_PORT_ONLINE = 2, /* login to fabric is complete */ + BFA_PORT_OFFLINE = 3, /* No login to fabric */ +}; + +/** + * FCS port type. Required for VmWare. + */ +enum bfa_port_type { + BFA_PORT_TYPE_PHYSICAL = 0, + BFA_PORT_TYPE_VIRTUAL, +}; + +/** + * FCS port offline reason. Required for VmWare. + */ +enum bfa_port_offline_reason { + BFA_PORT_OFFLINE_UNKNOWN = 0, + BFA_PORT_OFFLINE_LINKDOWN, + BFA_PORT_OFFLINE_FAB_UNSUPPORTED, /* NPIV not supported by the + * fabric */ + BFA_PORT_OFFLINE_FAB_NORESOURCES, + BFA_PORT_OFFLINE_FAB_LOGOUT, +}; + +/** + * FCS lport info. Required for VmWare. + */ +struct bfa_port_info_s { + u8 port_type; /* bfa_port_type_t : physical or + * virtual */ + u8 port_state; /* one of bfa_port_state values */ + u8 offline_reason; /* one of bfa_port_offline_reason_t + * values */ + wwn_t port_wwn; + wwn_t node_wwn; + + /* + * following 4 feilds are valid for Physical Ports only + */ + u32 max_vports_supp; /* Max supported vports */ + u32 num_vports_inuse; /* Num of in use vports */ + u32 max_rports_supp; /* Max supported rports */ + u32 num_rports_inuse; /* Num of doscovered rports */ + +}; + +/** + * FCS port statistics + */ +struct bfa_port_stats_s { + u32 ns_plogi_sent; + u32 ns_plogi_rsp_err; + u32 ns_plogi_acc_err; + u32 ns_plogi_accepts; + u32 ns_rejects; /* NS command rejects */ + u32 ns_plogi_unknown_rsp; + u32 ns_plogi_alloc_wait; + + u32 ns_retries; /* NS command retries */ + u32 ns_timeouts; /* NS command timeouts */ + + u32 ns_rspnid_sent; + u32 ns_rspnid_accepts; + u32 ns_rspnid_rsp_err; + u32 ns_rspnid_rejects; + u32 ns_rspnid_alloc_wait; + + u32 ns_rftid_sent; + u32 ns_rftid_accepts; + u32 ns_rftid_rsp_err; + u32 ns_rftid_rejects; + u32 ns_rftid_alloc_wait; + + u32 ns_rffid_sent; + u32 ns_rffid_accepts; + u32 ns_rffid_rsp_err; + u32 ns_rffid_rejects; + u32 ns_rffid_alloc_wait; + + u32 ns_gidft_sent; + u32 ns_gidft_accepts; + u32 ns_gidft_rsp_err; + u32 ns_gidft_rejects; + u32 ns_gidft_unknown_rsp; + u32 ns_gidft_alloc_wait; + + /* + * Mgmt Server stats + */ + u32 ms_retries; /* MS command retries */ + u32 ms_timeouts; /* MS command timeouts */ + u32 ms_plogi_sent; + u32 ms_plogi_rsp_err; + u32 ms_plogi_acc_err; + u32 ms_plogi_accepts; + u32 ms_rejects; /* NS command rejects */ + u32 ms_plogi_unknown_rsp; + u32 ms_plogi_alloc_wait; + + u32 num_rscn; /* Num of RSCN received */ + u32 num_portid_rscn;/* Num portid format RSCN + * received */ + + u32 uf_recvs; /* unsolicited recv frames */ + u32 uf_recv_drops; /* dropped received frames */ + + u32 rsvd; /* padding for 64 bit alignment */ +}; + +/** + * BFA port attribute returned in queries + */ +struct bfa_port_attr_s { + enum bfa_port_state state; /* port state */ + u32 pid; /* port ID */ + struct bfa_port_cfg_s port_cfg; /* port configuration */ + enum bfa_pport_type port_type; /* current topology */ + u32 loopback; /* cable is externally looped back */ + wwn_t fabric_name; /* attached switch's nwwn */ + u8 fabric_ip_addr[BFA_FCS_FABRIC_IPADDR_SZ]; /* attached + * fabric's ip addr */ +}; + +/** + * BFA physical port Level events + * Arguments below are in BFAL context from Mgmt + * BFA_PORT_AEN_ONLINE: [in]: pwwn [out]: pwwn + * BFA_PORT_AEN_OFFLINE: [in]: pwwn [out]: pwwn + * BFA_PORT_AEN_RLIR: [in]: None [out]: pwwn, rlir_data, rlir_len + * BFA_PORT_AEN_SFP_INSERT: [in]: pwwn [out]: port_id, pwwn + * BFA_PORT_AEN_SFP_REMOVE: [in]: pwwn [out]: port_id, pwwn + * BFA_PORT_AEN_SFP_POM: [in]: pwwn [out]: level, port_id, pwwn + * BFA_PORT_AEN_ENABLE: [in]: pwwn [out]: pwwn + * BFA_PORT_AEN_DISABLE: [in]: pwwn [out]: pwwn + * BFA_PORT_AEN_AUTH_ON: [in]: pwwn [out]: pwwn + * BFA_PORT_AEN_AUTH_OFF: [in]: pwwn [out]: pwwn + * BFA_PORT_AEN_DISCONNECT: [in]: pwwn [out]: pwwn + * BFA_PORT_AEN_QOS_NEG: [in]: pwwn [out]: pwwn + * BFA_PORT_AEN_FABRIC_NAME_CHANGE: [in]: pwwn, [out]: pwwn, fwwn + * + */ +enum bfa_port_aen_event { + BFA_PORT_AEN_ONLINE = 1, /* Physical Port online event */ + BFA_PORT_AEN_OFFLINE = 2, /* Physical Port offline event */ + BFA_PORT_AEN_RLIR = 3, /* RLIR event, not supported */ + BFA_PORT_AEN_SFP_INSERT = 4, /* SFP inserted event */ + BFA_PORT_AEN_SFP_REMOVE = 5, /* SFP removed event */ + BFA_PORT_AEN_SFP_POM = 6, /* SFP POM event */ + BFA_PORT_AEN_ENABLE = 7, /* Physical Port enable event */ + BFA_PORT_AEN_DISABLE = 8, /* Physical Port disable event */ + BFA_PORT_AEN_AUTH_ON = 9, /* Physical Port auth success event */ + BFA_PORT_AEN_AUTH_OFF = 10, /* Physical Port auth fail event */ + BFA_PORT_AEN_DISCONNECT = 11, /* Physical Port disconnect event */ + BFA_PORT_AEN_QOS_NEG = 12, /* Base Port QOS negotiation event */ + BFA_PORT_AEN_FABRIC_NAME_CHANGE = 13, /* Fabric Name/WWN change + * event */ + BFA_PORT_AEN_SFP_ACCESS_ERROR = 14, /* SFP read error event */ + BFA_PORT_AEN_SFP_UNSUPPORT = 15, /* Unsupported SFP event */ +}; + +enum bfa_port_aen_sfp_pom { + BFA_PORT_AEN_SFP_POM_GREEN = 1, /* Normal */ + BFA_PORT_AEN_SFP_POM_AMBER = 2, /* Warning */ + BFA_PORT_AEN_SFP_POM_RED = 3, /* Critical */ + BFA_PORT_AEN_SFP_POM_MAX = BFA_PORT_AEN_SFP_POM_RED +}; + +struct bfa_port_aen_data_s { + enum bfa_ioc_type_e ioc_type; + wwn_t pwwn; /* WWN of the physical port */ + wwn_t fwwn; /* WWN of the fabric port */ + mac_t mac; /* MAC addres of the ethernet port, + * applicable to CNA port only */ + int phy_port_num; /*! For SFP related events */ + enum bfa_port_aen_sfp_pom level; /* Only transitions will + * be informed */ +}; + +#endif /* __BFA_DEFS_PORT_H__ */ diff --git a/drivers/scsi/bfa/include/defs/bfa_defs_pport.h b/drivers/scsi/bfa/include/defs/bfa_defs_pport.h new file mode 100644 index 000000000000..a000bc4e2d4a --- /dev/null +++ b/drivers/scsi/bfa/include/defs/bfa_defs_pport.h @@ -0,0 +1,383 @@ +/* + * Copyright (c) 2005-2009 Brocade Communications Systems, Inc. + * All rights reserved + * www.brocade.com + * + * Linux driver for Brocade Fibre Channel Host Bus Adapter. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (GPL) Version 2 as + * published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#ifndef __BFA_DEFS_PPORT_H__ +#define __BFA_DEFS_PPORT_H__ + +#include <bfa_os_inc.h> +#include <protocol/fc.h> +#include <defs/bfa_defs_types.h> +#include <defs/bfa_defs_qos.h> +#include <cna/pstats/phyport_defs.h> + +/* Modify char* port_stt[] in bfal_port.c if a new state was added */ +enum bfa_pport_states { + BFA_PPORT_ST_UNINIT = 1, + BFA_PPORT_ST_ENABLING_QWAIT = 2, + BFA_PPORT_ST_ENABLING = 3, + BFA_PPORT_ST_LINKDOWN = 4, + BFA_PPORT_ST_LINKUP = 5, + BFA_PPORT_ST_DISABLING_QWAIT = 6, + BFA_PPORT_ST_DISABLING = 7, + BFA_PPORT_ST_DISABLED = 8, + BFA_PPORT_ST_STOPPED = 9, + BFA_PPORT_ST_IOCDOWN = 10, + BFA_PPORT_ST_IOCDIS = 11, + BFA_PPORT_ST_FWMISMATCH = 12, + BFA_PPORT_ST_MAX_STATE, +}; + +/** + * Port speed settings. Each specific speed is a bit field. Use multiple + * bits to specify speeds to be selected for auto-negotiation. + */ +enum bfa_pport_speed { + BFA_PPORT_SPEED_UNKNOWN = 0, + BFA_PPORT_SPEED_1GBPS = 1, + BFA_PPORT_SPEED_2GBPS = 2, + BFA_PPORT_SPEED_4GBPS = 4, + BFA_PPORT_SPEED_8GBPS = 8, + BFA_PPORT_SPEED_10GBPS = 10, + BFA_PPORT_SPEED_AUTO = + (BFA_PPORT_SPEED_1GBPS | BFA_PPORT_SPEED_2GBPS | + BFA_PPORT_SPEED_4GBPS | BFA_PPORT_SPEED_8GBPS), +}; + +/** + * Port operational type (in sync with SNIA port type). + */ +enum bfa_pport_type { + BFA_PPORT_TYPE_UNKNOWN = 1, /* port type is unkown */ + BFA_PPORT_TYPE_TRUNKED = 2, /* Trunked mode */ + BFA_PPORT_TYPE_NPORT = 5, /* P2P with switched fabric */ + BFA_PPORT_TYPE_NLPORT = 6, /* public loop */ + BFA_PPORT_TYPE_LPORT = 20, /* private loop */ + BFA_PPORT_TYPE_P2P = 21, /* P2P with no switched fabric */ + BFA_PPORT_TYPE_VPORT = 22, /* NPIV - virtual port */ +}; + +/** + * Port topology setting. A port's topology and fabric login status + * determine its operational type. + */ +enum bfa_pport_topology { + BFA_PPORT_TOPOLOGY_NONE = 0, /* No valid topology */ + BFA_PPORT_TOPOLOGY_P2P = 1, /* P2P only */ + BFA_PPORT_TOPOLOGY_LOOP = 2, /* LOOP topology */ + BFA_PPORT_TOPOLOGY_AUTO = 3, /* auto topology selection */ +}; + +/** + * Physical port loopback types. + */ +enum bfa_pport_opmode { + BFA_PPORT_OPMODE_NORMAL = 0x00, /* normal non-loopback mode */ + BFA_PPORT_OPMODE_LB_INT = 0x01, /* internal loop back */ + BFA_PPORT_OPMODE_LB_SLW = 0x02, /* serial link wrapback (serdes) */ + BFA_PPORT_OPMODE_LB_EXT = 0x04, /* external loop back (serdes) */ + BFA_PPORT_OPMODE_LB_CBL = 0x08, /* cabled loop back */ + BFA_PPORT_OPMODE_LB_NLINT = 0x20, /* NL_Port internal loopback */ +}; + +#define BFA_PPORT_OPMODE_LB_HARD(_mode) \ + ((_mode == BFA_PPORT_OPMODE_LB_INT) || \ + (_mode == BFA_PPORT_OPMODE_LB_SLW) || \ + (_mode == BFA_PPORT_OPMODE_LB_EXT)) + +/** + Port State (in sync with SNIA port state). + */ +enum bfa_pport_snia_state { + BFA_PPORT_STATE_UNKNOWN = 1, /* port is not initialized */ + BFA_PPORT_STATE_ONLINE = 2, /* port is ONLINE */ + BFA_PPORT_STATE_DISABLED = 3, /* port is disabled by user */ + BFA_PPORT_STATE_BYPASSED = 4, /* port is bypassed (in LOOP) */ + BFA_PPORT_STATE_DIAG = 5, /* port diagnostics is active */ + BFA_PPORT_STATE_LINKDOWN = 6, /* link is down */ + BFA_PPORT_STATE_LOOPBACK = 8, /* port is looped back */ +}; + +/** + * Port link state + */ +enum bfa_pport_linkstate { + BFA_PPORT_LINKUP = 1, /* Physical port/Trunk link up */ + BFA_PPORT_LINKDOWN = 2, /* Physical port/Trunk link down */ + BFA_PPORT_TRUNK_LINKDOWN = 3, /* Trunk link down (new tmaster) */ +}; + +/** + * Port link state event + */ +#define bfa_pport_event_t enum bfa_pport_linkstate + +/** + * Port link state reason code + */ +enum bfa_pport_linkstate_rsn { + BFA_PPORT_LINKSTATE_RSN_NONE = 0, + BFA_PPORT_LINKSTATE_RSN_DISABLED = 1, + BFA_PPORT_LINKSTATE_RSN_RX_NOS = 2, + BFA_PPORT_LINKSTATE_RSN_RX_OLS = 3, + BFA_PPORT_LINKSTATE_RSN_RX_LIP = 4, + BFA_PPORT_LINKSTATE_RSN_RX_LIPF7 = 5, + BFA_PPORT_LINKSTATE_RSN_SFP_REMOVED = 6, + BFA_PPORT_LINKSTATE_RSN_PORT_FAULT = 7, + BFA_PPORT_LINKSTATE_RSN_RX_LOS = 8, + BFA_PPORT_LINKSTATE_RSN_LOCAL_FAULT = 9, + BFA_PPORT_LINKSTATE_RSN_REMOTE_FAULT = 10, + BFA_PPORT_LINKSTATE_RSN_TIMEOUT = 11, + + + + /* CEE related reason codes/errors */ + CEE_LLDP_INFO_AGED_OUT = 20, + CEE_LLDP_SHUTDOWN_TLV_RCVD = 21, + CEE_PEER_NOT_ADVERTISE_DCBX = 22, + CEE_PEER_NOT_ADVERTISE_PG = 23, + CEE_PEER_NOT_ADVERTISE_PFC = 24, + CEE_PEER_NOT_ADVERTISE_FCOE = 25, + CEE_PG_NOT_COMPATIBLE = 26, + CEE_PFC_NOT_COMPATIBLE = 27, + CEE_FCOE_NOT_COMPATIBLE = 28, + CEE_BAD_PG_RCVD = 29, + CEE_BAD_BW_RCVD = 30, + CEE_BAD_PFC_RCVD = 31, + CEE_BAD_FCOE_PRI_RCVD = 32, + CEE_FCOE_PRI_PFC_OFF = 33, + CEE_DUP_CONTROL_TLV_RCVD = 34, + CEE_DUP_FEAT_TLV_RCVD = 35, + CEE_APPLY_NEW_CFG = 36, /* reason, not an error */ + CEE_PROTOCOL_INIT = 37, /* reason, not an error */ + CEE_PHY_LINK_DOWN = 38, + CEE_LLS_FCOE_ABSENT = 39, + CEE_LLS_FCOE_DOWN = 40 +}; + +/** + * Default Target Rate Limiting Speed. + */ +#define BFA_PPORT_DEF_TRL_SPEED BFA_PPORT_SPEED_1GBPS + +/** + * Physical port configuration + */ +struct bfa_pport_cfg_s { + u8 topology; /* bfa_pport_topology */ + u8 speed; /* enum bfa_pport_speed */ + u8 trunked; /* trunked or not */ + u8 qos_enabled; /* qos enabled or not */ + u8 trunk_ports; /* bitmap of trunked ports */ + u8 cfg_hardalpa; /* is hard alpa configured */ + u16 maxfrsize; /* maximum frame size */ + u8 hardalpa; /* configured hard alpa */ + u8 rx_bbcredit; /* receive buffer credits */ + u8 tx_bbcredit; /* transmit buffer credits */ + u8 ratelimit; /* ratelimit enabled or not */ + u8 trl_def_speed; /* ratelimit default speed */ + u8 rsvd[3]; + u16 path_tov; /* device path timeout */ + u16 q_depth; /* SCSI Queue depth */ +}; + +/** + * Port attribute values. + */ +struct bfa_pport_attr_s { + /* + * Static fields + */ + wwn_t nwwn; /* node wwn */ + wwn_t pwwn; /* port wwn */ + enum fc_cos cos_supported; /* supported class of services */ + u32 rsvd; + struct fc_symname_s port_symname; /* port symbolic name */ + enum bfa_pport_speed speed_supported; /* supported speeds */ + bfa_boolean_t pbind_enabled; /* Will be set if Persistent binding + * enabled. Relevant only in Windows + */ + + /* + * Configured values + */ + struct bfa_pport_cfg_s pport_cfg; /* pport cfg */ + + /* + * Dynamic field - info from BFA + */ + enum bfa_pport_states port_state; /* current port state */ + enum bfa_pport_speed speed; /* current speed */ + enum bfa_pport_topology topology; /* current topology */ + bfa_boolean_t beacon; /* current beacon status */ + bfa_boolean_t link_e2e_beacon;/* set if link beacon on */ + bfa_boolean_t plog_enabled; /* set if portlog is enabled*/ + + /* + * Dynamic field - info from FCS + */ + u32 pid; /* port ID */ + enum bfa_pport_type port_type; /* current topology */ + u32 loopback; /* external loopback */ + u32 rsvd1; + u32 rsvd2; /* padding for 64 bit */ +}; + +/** + * FC Port statistics. + */ +struct bfa_pport_fc_stats_s { + u64 secs_reset; /* seconds since stats is reset */ + u64 tx_frames; /* transmitted frames */ + u64 tx_words; /* transmitted words */ + u64 rx_frames; /* received frames */ + u64 rx_words; /* received words */ + u64 lip_count; /* LIPs seen */ + u64 nos_count; /* NOS count */ + u64 error_frames; /* errored frames (sent?) */ + u64 dropped_frames; /* dropped frames */ + u64 link_failures; /* link failure count */ + u64 loss_of_syncs; /* loss of sync count */ + u64 loss_of_signals;/* loss of signal count */ + u64 primseq_errs; /* primitive sequence protocol */ + u64 bad_os_count; /* invalid ordered set */ + u64 err_enc_out; /* Encoding error outside frame */ + u64 invalid_crcs; /* frames received with invalid CRC*/ + u64 undersized_frm; /* undersized frames */ + u64 oversized_frm; /* oversized frames */ + u64 bad_eof_frm; /* frames with bad EOF */ + struct bfa_qos_stats_s qos_stats; /* QoS statistics */ +}; + +/** + * Eth Port statistics. + */ +struct bfa_pport_eth_stats_s { + u64 secs_reset; /* seconds since stats is reset */ + u64 frame_64; /* both rx and tx counter */ + u64 frame_65_127; /* both rx and tx counter */ + u64 frame_128_255; /* both rx and tx counter */ + u64 frame_256_511; /* both rx and tx counter */ + u64 frame_512_1023; /* both rx and tx counter */ + u64 frame_1024_1518; /* both rx and tx counter */ + u64 frame_1519_1522; /* both rx and tx counter */ + + u64 tx_bytes; + u64 tx_packets; + u64 tx_mcast_packets; + u64 tx_bcast_packets; + u64 tx_control_frame; + u64 tx_drop; + u64 tx_jabber; + u64 tx_fcs_error; + u64 tx_fragments; + + u64 rx_bytes; + u64 rx_packets; + u64 rx_mcast_packets; + u64 rx_bcast_packets; + u64 rx_control_frames; + u64 rx_unknown_opcode; + u64 rx_drop; + u64 rx_jabber; + u64 rx_fcs_error; + u64 rx_alignment_error; + u64 rx_frame_length_error; + u64 rx_code_error; + u64 rx_fragments; + + u64 rx_pause; /* BPC */ + u64 rx_zero_pause; /* BPC Pause cancellation */ + u64 tx_pause; /* BPC */ + u64 tx_zero_pause; /* BPC Pause cancellation */ + u64 rx_fcoe_pause; /* BPC */ + u64 rx_fcoe_zero_pause; /* BPC Pause cancellation */ + u64 tx_fcoe_pause; /* BPC */ + u64 tx_fcoe_zero_pause; /* BPC Pause cancellation */ +}; + +/** + * Port statistics. + */ +union bfa_pport_stats_u { + struct bfa_pport_fc_stats_s fc; + struct bfa_pport_eth_stats_s eth; +}; + +/** + * Port FCP mappings. + */ +struct bfa_pport_fcpmap_s { + char osdevname[256]; + u32 bus; + u32 target; + u32 oslun; + u32 fcid; + wwn_t nwwn; + wwn_t pwwn; + u64 fcplun; + char luid[256]; +}; + +/** + * Port RNID info. + */ +struct bfa_pport_rnid_s { + wwn_t wwn; + u32 unittype; + u32 portid; + u32 attached_nodes_num; + u16 ip_version; + u16 udp_port; + u8 ipaddr[16]; + u16 rsvd; + u16 topologydiscoveryflags; +}; + +/** + * Link state information + */ +struct bfa_pport_link_s { + u8 linkstate; /* Link state bfa_pport_linkstate */ + u8 linkstate_rsn; /* bfa_pport_linkstate_rsn_t */ + u8 topology; /* P2P/LOOP bfa_pport_topology */ + u8 speed; /* Link speed (1/2/4/8 G) */ + u32 linkstate_opt; /* Linkstate optional data (debug) */ + u8 trunked; /* Trunked or not (1 or 0) */ + u8 resvd[3]; + struct bfa_qos_attr_s qos_attr; /* QoS Attributes */ + struct bfa_qos_vc_attr_s qos_vc_attr; /* VC info from ELP */ + union { + struct { + u8 tmaster;/* Trunk Master or + * not (1 or 0) */ + u8 tlinks; /* Trunk links bitmap + * (linkup) */ + u8 resv1; /* Reserved */ + } trunk_info; + + struct { + u8 myalpa; /* alpa claimed */ + u8 login_req; /* Login required or + * not (1 or 0) */ + u8 alpabm_val;/* alpa bitmap valid + * or not (1 or 0) */ + struct fc_alpabm_s alpabm; /* alpa bitmap */ + } loop_info; + } tl; +}; + +#endif /* __BFA_DEFS_PPORT_H__ */ diff --git a/drivers/scsi/bfa/include/defs/bfa_defs_qos.h b/drivers/scsi/bfa/include/defs/bfa_defs_qos.h new file mode 100644 index 000000000000..aadbacd1d2d7 --- /dev/null +++ b/drivers/scsi/bfa/include/defs/bfa_defs_qos.h @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2005-2009 Brocade Communications Systems, Inc. + * All rights reserved + * www.brocade.com + * + * Linux driver for Brocade Fibre Channel Host Bus Adapter. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (GPL) Version 2 as + * published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#ifndef __BFA_DEFS_QOS_H__ +#define __BFA_DEFS_QOS_H__ + +/** + * QoS states + */ +enum bfa_qos_state { + BFA_QOS_ONLINE = 1, /* QoS is online */ + BFA_QOS_OFFLINE = 2, /* QoS is offline */ +}; + + +/** + * QoS Priority levels. + */ +enum bfa_qos_priority { + BFA_QOS_UNKNOWN = 0, + BFA_QOS_HIGH = 1, /* QoS Priority Level High */ + BFA_QOS_MED = 2, /* QoS Priority Level Medium */ + BFA_QOS_LOW = 3, /* QoS Priority Level Low */ +}; + + +/** + * QoS bandwidth allocation for each priority level + */ +enum bfa_qos_bw_alloc { + BFA_QOS_BW_HIGH = 60, /* bandwidth allocation for High */ + BFA_QOS_BW_MED = 30, /* bandwidth allocation for Medium */ + BFA_QOS_BW_LOW = 10, /* bandwidth allocation for Low */ +}; + +/** + * QoS attribute returned in QoS Query + */ +struct bfa_qos_attr_s { + enum bfa_qos_state state; /* QoS current state */ + u32 total_bb_cr; /* Total BB Credits */ +}; + +/** + * These fields should be displayed only from the CLI. + * There will be a separate BFAL API (get_qos_vc_attr ?) + * to retrieve this. + * + */ +#define BFA_QOS_MAX_VC 16 + +struct bfa_qos_vc_info_s { + u8 vc_credit; + u8 borrow_credit; + u8 priority; + u8 resvd; +}; + +struct bfa_qos_vc_attr_s { + u16 total_vc_count; /* Total VC Count */ + u16 shared_credit; + u32 elp_opmode_flags; + struct bfa_qos_vc_info_s vc_info[BFA_QOS_MAX_VC]; /* as many as + * total_vc_count */ +}; + +/** + * QoS statistics + */ +struct bfa_qos_stats_s { + u32 flogi_sent; /* QoS Flogi sent */ + u32 flogi_acc_recvd; /* QoS Flogi Acc received */ + u32 flogi_rjt_recvd; /* QoS Flogi rejects received */ + u32 flogi_retries; /* QoS Flogi retries */ + + u32 elp_recvd; /* QoS ELP received */ + u32 elp_accepted; /* QoS ELP Accepted */ + u32 elp_rejected; /* QoS ELP rejected */ + u32 elp_dropped; /* QoS ELP dropped */ + + u32 qos_rscn_recvd; /* QoS RSCN received */ + u32 rsvd; /* padding for 64 bit alignment */ +}; + +#endif /* __BFA_DEFS_QOS_H__ */ diff --git a/drivers/scsi/bfa/include/defs/bfa_defs_rport.h b/drivers/scsi/bfa/include/defs/bfa_defs_rport.h new file mode 100644 index 000000000000..e0af59d6d2f6 --- /dev/null +++ b/drivers/scsi/bfa/include/defs/bfa_defs_rport.h @@ -0,0 +1,199 @@ +/* + * Copyright (c) 2005-2009 Brocade Communications Systems, Inc. + * All rights reserved + * www.brocade.com + * + * Linux driver for Brocade Fibre Channel Host Bus Adapter. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (GPL) Version 2 as + * published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#ifndef __BFA_DEFS_RPORT_H__ +#define __BFA_DEFS_RPORT_H__ + +#include <bfa_os_inc.h> +#include <protocol/types.h> +#include <defs/bfa_defs_pport.h> +#include <defs/bfa_defs_port.h> +#include <defs/bfa_defs_qos.h> + +/** + * FCS remote port states + */ +enum bfa_rport_state { + BFA_RPORT_UNINIT = 0, /* PORT is not yet initialized */ + BFA_RPORT_OFFLINE = 1, /* rport is offline */ + BFA_RPORT_PLOGI = 2, /* PLOGI to rport is in progress */ + BFA_RPORT_ONLINE = 3, /* login to rport is complete */ + BFA_RPORT_PLOGI_RETRY = 4, /* retrying login to rport */ + BFA_RPORT_NSQUERY = 5, /* nameserver query */ + BFA_RPORT_ADISC = 6, /* ADISC authentication */ + BFA_RPORT_LOGO = 7, /* logging out with rport */ + BFA_RPORT_LOGORCV = 8, /* handling LOGO from rport */ + BFA_RPORT_NSDISC = 9, /* re-discover rport */ +}; + +/** + * Rport Scsi Function : Initiator/Target. + */ +enum bfa_rport_function { + BFA_RPORT_INITIATOR = 0x01, /* SCSI Initiator */ + BFA_RPORT_TARGET = 0x02, /* SCSI Target */ +}; + +/** + * port/node symbolic names for rport + */ +#define BFA_RPORT_SYMNAME_MAXLEN 255 +struct bfa_rport_symname_s { + char symname[BFA_RPORT_SYMNAME_MAXLEN]; +}; + +struct bfa_rport_hal_stats_s { + u32 sm_un_cr; /* uninit: create events */ + u32 sm_un_unexp; /* uninit: exception events */ + u32 sm_cr_on; /* created: online events */ + u32 sm_cr_del; /* created: delete events */ + u32 sm_cr_hwf; /* created: IOC down */ + u32 sm_cr_unexp; /* created: exception events */ + u32 sm_fwc_rsp; /* fw create: f/w responses */ + u32 sm_fwc_del; /* fw create: delete events */ + u32 sm_fwc_off; /* fw create: offline events */ + u32 sm_fwc_hwf; /* fw create: IOC down */ + u32 sm_fwc_unexp; /* fw create: exception events*/ + u32 sm_on_off; /* online: offline events */ + u32 sm_on_del; /* online: delete events */ + u32 sm_on_hwf; /* online: IOC down events */ + u32 sm_on_unexp; /* online: exception events */ + u32 sm_fwd_rsp; /* fw delete: fw responses */ + u32 sm_fwd_del; /* fw delete: delete events */ + u32 sm_fwd_hwf; /* fw delete: IOC down events */ + u32 sm_fwd_unexp; /* fw delete: exception events*/ + u32 sm_off_del; /* offline: delete events */ + u32 sm_off_on; /* offline: online events */ + u32 sm_off_hwf; /* offline: IOC down events */ + u32 sm_off_unexp; /* offline: exception events */ + u32 sm_del_fwrsp; /* delete: fw responses */ + u32 sm_del_hwf; /* delete: IOC down events */ + u32 sm_del_unexp; /* delete: exception events */ + u32 sm_delp_fwrsp; /* delete pend: fw responses */ + u32 sm_delp_hwf; /* delete pend: IOC downs */ + u32 sm_delp_unexp; /* delete pend: exceptions */ + u32 sm_offp_fwrsp; /* off-pending: fw responses */ + u32 sm_offp_del; /* off-pending: deletes */ + u32 sm_offp_hwf; /* off-pending: IOC downs */ + u32 sm_offp_unexp; /* off-pending: exceptions */ + u32 sm_iocd_off; /* IOC down: offline events */ + u32 sm_iocd_del; /* IOC down: delete events */ + u32 sm_iocd_on; /* IOC down: online events */ + u32 sm_iocd_unexp; /* IOC down: exceptions */ + u32 rsvd; +}; + +/** + * FCS remote port statistics + */ +struct bfa_rport_stats_s { + u32 offlines; /* remote port offline count */ + u32 onlines; /* remote port online count */ + u32 rscns; /* RSCN affecting rport */ + u32 plogis; /* plogis sent */ + u32 plogi_accs; /* plogi accepts */ + u32 plogi_timeouts; /* plogi timeouts */ + u32 plogi_rejects; /* rcvd plogi rejects */ + u32 plogi_failed; /* local failure */ + u32 plogi_rcvd; /* plogis rcvd */ + u32 prli_rcvd; /* inbound PRLIs */ + u32 adisc_rcvd; /* ADISCs received */ + u32 adisc_rejects; /* recvd ADISC rejects */ + u32 adisc_sent; /* ADISC requests sent */ + u32 adisc_accs; /* ADISC accepted by rport */ + u32 adisc_failed; /* ADISC failed (no response) */ + u32 adisc_rejected; /* ADISC rejected by us */ + u32 logos; /* logos sent */ + u32 logo_accs; /* LOGO accepts from rport */ + u32 logo_failed; /* LOGO failures */ + u32 logo_rejected; /* LOGO rejects from rport */ + u32 logo_rcvd; /* LOGO from remote port */ + + u32 rpsc_rcvd; /* RPSC received */ + u32 rpsc_rejects; /* recvd RPSC rejects */ + u32 rpsc_sent; /* RPSC requests sent */ + u32 rpsc_accs; /* RPSC accepted by rport */ + u32 rpsc_failed; /* RPSC failed (no response) */ + u32 rpsc_rejected; /* RPSC rejected by us */ + + u32 rsvd; + struct bfa_rport_hal_stats_s hal_stats; /* BFA rport stats */ +}; + +/** + * Rport's QoS attributes + */ +struct bfa_rport_qos_attr_s { + enum bfa_qos_priority qos_priority; /* rport's QoS priority */ + u32 qos_flow_id; /* QoS flow Id */ +}; + +/** + * FCS remote port attributes returned in queries + */ +struct bfa_rport_attr_s { + wwn_t nwwn; /* node wwn */ + wwn_t pwwn; /* port wwn */ + enum fc_cos cos_supported; /* supported class of services */ + u32 pid; /* port ID */ + u32 df_sz; /* Max payload size */ + enum bfa_rport_state state; /* Rport State machine state */ + enum fc_cos fc_cos; /* FC classes of services */ + bfa_boolean_t cisc; /* CISC capable device */ + struct bfa_rport_symname_s symname; /* Symbolic Name */ + enum bfa_rport_function scsi_function; /* Initiator/Target */ + struct bfa_rport_qos_attr_s qos_attr; /* qos attributes */ + enum bfa_pport_speed curr_speed; /* operating speed got from + * RPSC ELS. UNKNOWN, if RPSC + * is not supported */ + bfa_boolean_t trl_enforced; /* TRL enforced ? TRUE/FALSE */ + enum bfa_pport_speed assigned_speed; /* Speed assigned by the user. + * will be used if RPSC is not + * supported by the rport */ +}; + +#define bfa_rport_aen_qos_data_t struct bfa_rport_qos_attr_s + +/** + * BFA remote port events + * Arguments below are in BFAL context from Mgmt + * BFA_RPORT_AEN_ONLINE: [in]: lpwwn [out]: vf_id, lpwwn, rpwwn + * BFA_RPORT_AEN_OFFLINE: [in]: lpwwn [out]: vf_id, lpwwn, rpwwn + * BFA_RPORT_AEN_DISCONNECT:[in]: lpwwn [out]: vf_id, lpwwn, rpwwn + * BFA_RPORT_AEN_QOS_PRIO: [in]: lpwwn [out]: vf_id, lpwwn, rpwwn, prio + * BFA_RPORT_AEN_QOS_FLOWID:[in]: lpwwn [out]: vf_id, lpwwn, rpwwn, flow_id + */ +enum bfa_rport_aen_event { + BFA_RPORT_AEN_ONLINE = 1, /* RPort online event */ + BFA_RPORT_AEN_OFFLINE = 2, /* RPort offline event */ + BFA_RPORT_AEN_DISCONNECT = 3, /* RPort disconnect event */ + BFA_RPORT_AEN_QOS_PRIO = 4, /* QOS priority change event */ + BFA_RPORT_AEN_QOS_FLOWID = 5, /* QOS flow Id change event */ +}; + +struct bfa_rport_aen_data_s { + u16 vf_id; /* vf_id of this logical port */ + u16 rsvd[3]; + wwn_t ppwwn; /* WWN of its physical port */ + wwn_t lpwwn; /* WWN of this logical port */ + wwn_t rpwwn; /* WWN of this remote port */ + union { + bfa_rport_aen_qos_data_t qos; + } priv; +}; + +#endif /* __BFA_DEFS_RPORT_H__ */ diff --git a/drivers/scsi/bfa/include/defs/bfa_defs_status.h b/drivers/scsi/bfa/include/defs/bfa_defs_status.h new file mode 100644 index 000000000000..cdceaeb9f4b8 --- /dev/null +++ b/drivers/scsi/bfa/include/defs/bfa_defs_status.h @@ -0,0 +1,255 @@ +/* + * Copyright (c) 2005-2009 Brocade Communications Systems, Inc. + * All rights reserved + * www.brocade.com + * + * Linux driver for Brocade Fibre Channel Host Bus Adapter. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (GPL) Version 2 as + * published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ +#ifndef __BFA_DEFS_STATUS_H__ +#define __BFA_DEFS_STATUS_H__ + +/** + * API status return values + * + * NOTE: The error msgs are auto generated from the comments. Only singe line + * comments are supported + */ +enum bfa_status { + BFA_STATUS_OK = 0, /* Success */ + BFA_STATUS_FAILED = 1, /* Operation failed */ + BFA_STATUS_EINVAL = 2, /* Invalid params Check input + * parameters */ + BFA_STATUS_ENOMEM = 3, /* Out of resources */ + BFA_STATUS_ENOSYS = 4, /* Function not implemented */ + BFA_STATUS_ETIMER = 5, /* Timer expired - Retry, if + * persists, contact support */ + BFA_STATUS_EPROTOCOL = 6, /* Protocol error */ + BFA_STATUS_ENOFCPORTS = 7, /* No FC ports resources */ + BFA_STATUS_NOFLASH = 8, /* Flash not present */ + BFA_STATUS_BADFLASH = 9, /* Flash is corrupted or bad */ + BFA_STATUS_SFP_UNSUPP = 10, /* Unsupported SFP - Replace SFP */ + BFA_STATUS_UNKNOWN_VFID = 11, /* VF_ID not found */ + BFA_STATUS_DATACORRUPTED = 12, /* Diag returned data corrupted + * contact support */ + BFA_STATUS_DEVBUSY = 13, /* Device busy - Retry operation */ + BFA_STATUS_ABORTED = 14, /* Operation aborted */ + BFA_STATUS_NODEV = 15, /* Dev is not present */ + BFA_STATUS_HDMA_FAILED = 16, /* Host dma failed contact support */ + BFA_STATUS_FLASH_BAD_LEN = 17, /* Flash bad length */ + BFA_STATUS_UNKNOWN_LWWN = 18, /* LPORT PWWN not found */ + BFA_STATUS_UNKNOWN_RWWN = 19, /* RPORT PWWN not found */ + BFA_STATUS_FCPT_LS_RJT = 20, /* Got LS_RJT for FC Pass + * through Req */ + BFA_STATUS_VPORT_EXISTS = 21, /* VPORT already exists */ + BFA_STATUS_VPORT_MAX = 22, /* Reached max VPORT supported + * limit */ + BFA_STATUS_UNSUPP_SPEED = 23, /* Invalid Speed Check speed + * setting */ + BFA_STATUS_INVLD_DFSZ = 24, /* Invalid Max data field size */ + BFA_STATUS_CNFG_FAILED = 25, /* Setting can not be persisted */ + BFA_STATUS_CMD_NOTSUPP = 26, /* Command/API not supported */ + BFA_STATUS_NO_ADAPTER = 27, /* No Brocade Adapter Found */ + BFA_STATUS_LINKDOWN = 28, /* Link is down - Check or replace + * SFP/cable */ + BFA_STATUS_FABRIC_RJT = 29, /* Reject from attached fabric */ + BFA_STATUS_UNKNOWN_VWWN = 30, /* VPORT PWWN not found */ + BFA_STATUS_NSLOGIN_FAILED = 31, /* Nameserver login failed */ + BFA_STATUS_NO_RPORTS = 32, /* No remote ports found */ + BFA_STATUS_NSQUERY_FAILED = 33, /* Nameserver query failed */ + BFA_STATUS_PORT_OFFLINE = 34, /* Port is not online */ + BFA_STATUS_RPORT_OFFLINE = 35, /* RPORT is not online */ + BFA_STATUS_TGTOPEN_FAILED = 36, /* Remote SCSI target open failed */ + BFA_STATUS_BAD_LUNS = 37, /* No valid LUNs found */ + BFA_STATUS_IO_FAILURE = 38, /* SCSI target IO failure */ + BFA_STATUS_NO_FABRIC = 39, /* No switched fabric present */ + BFA_STATUS_EBADF = 40, /* Bad file descriptor */ + BFA_STATUS_EINTR = 41, /* A signal was caught during ioctl */ + BFA_STATUS_EIO = 42, /* I/O error */ + BFA_STATUS_ENOTTY = 43, /* Inappropriate I/O control + * operation */ + BFA_STATUS_ENXIO = 44, /* No such device or address */ + BFA_STATUS_EFOPEN = 45, /* Failed to open file */ + BFA_STATUS_VPORT_WWN_BP = 46, /* WWN is same as base port's WWN */ + BFA_STATUS_PORT_NOT_DISABLED = 47, /* Port not disabled disable port + * first */ + BFA_STATUS_BADFRMHDR = 48, /* Bad frame header */ + BFA_STATUS_BADFRMSZ = 49, /* Bad frame size check and replace + * SFP/cable */ + BFA_STATUS_MISSINGFRM = 50, /* Missing frame check and replace + * SFP/cable */ + BFA_STATUS_LINKTIMEOUT = 51, /* Link timeout check and replace + * SFP/cable */ + BFA_STATUS_NO_FCPIM_NEXUS = 52, /* No FCP Nexus exists with the + * rport */ + BFA_STATUS_CHECKSUM_FAIL = 53, /* checksum failure */ + BFA_STATUS_GZME_FAILED = 54, /* Get zone member query failed */ + BFA_STATUS_SCSISTART_REQD = 55, /* SCSI disk require START command */ + BFA_STATUS_IOC_FAILURE = 56, /* IOC failure - Retry, if persists + * contact support */ + BFA_STATUS_INVALID_WWN = 57, /* Invalid WWN */ + BFA_STATUS_MISMATCH = 58, /* Version mismatch */ + BFA_STATUS_IOC_ENABLED = 59, /* IOC is already enabled */ + BFA_STATUS_ADAPTER_ENABLED = 60, /* Adapter is not disabled disable + * adapter first */ + BFA_STATUS_IOC_NON_OP = 61, /* IOC is not operational. Enable IOC + * and if it still fails, + * contact support */ + BFA_STATUS_ADDR_MAP_FAILURE = 62, /* PCI base address not mapped + * in OS */ + BFA_STATUS_SAME_NAME = 63, /* Name exists! use a different + * name */ + BFA_STATUS_PENDING = 64, /* API completes asynchronously */ + BFA_STATUS_8G_SPD = 65, /* Speed setting not valid for + * 8G HBA */ + BFA_STATUS_4G_SPD = 66, /* Speed setting not valid for + * 4G HBA */ + BFA_STATUS_AD_IS_ENABLE = 67, /* Adapter is already enabled */ + BFA_STATUS_EINVAL_TOV = 68, /* Invalid path failover TOV */ + BFA_STATUS_EINVAL_QDEPTH = 69, /* Invalid queue depth value */ + BFA_STATUS_VERSION_FAIL = 70, /* Application/Driver version + * mismatch */ + BFA_STATUS_DIAG_BUSY = 71, /* diag busy */ + BFA_STATUS_BEACON_ON = 72, /* Port Beacon already on */ + BFA_STATUS_BEACON_OFF = 73, /* Port Beacon already off */ + BFA_STATUS_LBEACON_ON = 74, /* Link End-to-End Beacon already + * on */ + BFA_STATUS_LBEACON_OFF = 75, /* Link End-to-End Beacon already + * off */ + BFA_STATUS_PORT_NOT_INITED = 76, /* Port not initialized */ + BFA_STATUS_RPSC_ENABLED = 77, /* Target has a valid speed */ + BFA_STATUS_ENOFSAVE = 78, /* No saved firmware trace */ + BFA_STATUS_BAD_FILE = 79, /* Not a valid Brocade Boot Code + * file */ + BFA_STATUS_RLIM_EN = 80, /* Target rate limiting is already + * enabled */ + BFA_STATUS_RLIM_DIS = 81, /* Target rate limiting is already + * disabled */ + BFA_STATUS_IOC_DISABLED = 82, /* IOC is already disabled */ + BFA_STATUS_ADAPTER_DISABLED = 83, /* Adapter is already disabled */ + BFA_STATUS_BIOS_DISABLED = 84, /* Bios is already disabled */ + BFA_STATUS_AUTH_ENABLED = 85, /* Authentication is already + * enabled */ + BFA_STATUS_AUTH_DISABLED = 86, /* Authentication is already + * disabled */ + BFA_STATUS_ERROR_TRL_ENABLED = 87, /* Target rate limiting is + * enabled */ + BFA_STATUS_ERROR_QOS_ENABLED = 88, /* QoS is enabled */ + BFA_STATUS_NO_SFP_DEV = 89, /* No SFP device check or replace SFP */ + BFA_STATUS_MEMTEST_FAILED = 90, /* Memory test failed contact + * support */ + BFA_STATUS_INVALID_DEVID = 91, /* Invalid device id provided */ + BFA_STATUS_QOS_ENABLED = 92, /* QOS is already enabled */ + BFA_STATUS_QOS_DISABLED = 93, /* QOS is already disabled */ + BFA_STATUS_INCORRECT_DRV_CONFIG = 94, /* Check configuration + * key/value pair */ + BFA_STATUS_REG_FAIL = 95, /* Can't read windows registry */ + BFA_STATUS_IM_INV_CODE = 96, /* Invalid IOCTL code */ + BFA_STATUS_IM_INV_VLAN = 97, /* Invalid VLAN ID */ + BFA_STATUS_IM_INV_ADAPT_NAME = 98, /* Invalid adapter name */ + BFA_STATUS_IM_LOW_RESOURCES = 99, /* Memory allocation failure in + * driver */ + BFA_STATUS_IM_VLANID_IS_PVID = 100, /* Given VLAN id same as PVID */ + BFA_STATUS_IM_VLANID_EXISTS = 101, /* Given VLAN id already exists */ + BFA_STATUS_IM_FW_UPDATE_FAIL = 102, /* Updating firmware with new + * VLAN ID failed */ + BFA_STATUS_PORTLOG_ENABLED = 103, /* Port Log is already enabled */ + BFA_STATUS_PORTLOG_DISABLED = 104, /* Port Log is already disabled */ + BFA_STATUS_FILE_NOT_FOUND = 105, /* Specified file could not be + * found */ + BFA_STATUS_QOS_FC_ONLY = 106, /* QOS can be enabled for FC mode + * only */ + BFA_STATUS_RLIM_FC_ONLY = 107, /* RATELIM can be enabled for FC mode + * only */ + BFA_STATUS_CT_SPD = 108, /* Invalid speed selection for Catapult. */ + BFA_STATUS_LEDTEST_OP = 109, /* LED test is operating */ + BFA_STATUS_CEE_NOT_DN = 110, /* eth port is not at down state, please + * bring down first */ + BFA_STATUS_10G_SPD = 111, /* Speed setting not valid for 10G HBA */ + BFA_STATUS_IM_INV_TEAM_NAME = 112, /* Invalid team name */ + BFA_STATUS_IM_DUP_TEAM_NAME = 113, /* Given team name already + * exists */ + BFA_STATUS_IM_ADAPT_ALREADY_IN_TEAM = 114, /* Given adapter is part + * of another team */ + BFA_STATUS_IM_ADAPT_HAS_VLANS = 115, /* Adapter has VLANs configured. + * Delete all VLANs before + * creating team */ + BFA_STATUS_IM_PVID_MISMATCH = 116, /* Mismatching PVIDs configured + * for adapters */ + BFA_STATUS_IM_LINK_SPEED_MISMATCH = 117, /* Mismatching link speeds + * configured for adapters */ + BFA_STATUS_IM_MTU_MISMATCH = 118, /* Mismatching MTUs configured for + * adapters */ + BFA_STATUS_IM_RSS_MISMATCH = 119, /* Mismatching RSS parameters + * configured for adapters */ + BFA_STATUS_IM_HDS_MISMATCH = 120, /* Mismatching HDS parameters + * configured for adapters */ + BFA_STATUS_IM_OFFLOAD_MISMATCH = 121, /* Mismatching offload + * parameters configured for + * adapters */ + BFA_STATUS_IM_PORT_PARAMS = 122, /* Error setting port parameters */ + BFA_STATUS_IM_PORT_NOT_IN_TEAM = 123, /* Port is not part of team */ + BFA_STATUS_IM_CANNOT_REM_PRI = 124, /* Primary adapter cannot be + * removed. Change primary before + * removing */ + BFA_STATUS_IM_MAX_PORTS_REACHED = 125, /* Exceeding maximum ports + * per team */ + BFA_STATUS_IM_LAST_PORT_DELETE = 126, /* Last port in team being + * deleted */ + BFA_STATUS_IM_NO_DRIVER = 127, /* IM driver is not installed */ + BFA_STATUS_IM_MAX_VLANS_REACHED = 128, /* Exceeding maximum VLANs + * per port */ + BFA_STATUS_TOMCAT_SPD_NOT_ALLOWED = 129, /* Bios speed config not + * allowed for CNA */ + BFA_STATUS_NO_MINPORT_DRIVER = 130, /* Miniport driver is not + * loaded */ + BFA_STATUS_CARD_TYPE_MISMATCH = 131, /* Card type mismatch */ + BFA_STATUS_BAD_ASICBLK = 132, /* Bad ASIC block */ + BFA_STATUS_NO_DRIVER = 133, /* Storage/Ethernet driver not loaded */ + BFA_STATUS_INVALID_MAC = 134, /* Invalid mac address */ + BFA_STATUS_IM_NO_VLAN = 135, /* No VLANs configured on the adapter */ + BFA_STATUS_IM_ETH_LB_FAILED = 136, /* Ethernet loopback test failed */ + BFA_STATUS_IM_PVID_REMOVE = 137, /* Cannot remove port vlan (PVID) */ + BFA_STATUS_IM_PVID_EDIT = 138, /* Cannot edit port vlan (PVID) */ + BFA_STATUS_CNA_NO_BOOT = 139, /* Boot upload not allowed for CNA */ + BFA_STATUS_IM_PVID_NON_ZERO = 140, /* Port VLAN ID (PVID) is Set to + * Non-Zero Value */ + BFA_STATUS_IM_INETCFG_LOCK_FAILED = 141, /* Acquiring Network + * Subsytem Lock Failed.Please + * try after some time */ + BFA_STATUS_IM_GET_INETCFG_FAILED = 142, /* Acquiring Network Subsytem + * handle Failed. Please try + * after some time */ + BFA_STATUS_IM_NOT_BOUND = 143, /* Brocade 10G Ethernet Service is not + * Enabled on this port */ + BFA_STATUS_INSUFFICIENT_PERMS = 144, /* User doesn't have sufficient + * permissions to execute the BCU + * application */ + BFA_STATUS_IM_INV_VLAN_NAME = 145, /* Invalid/Reserved Vlan name + * string. The name is not allowed + * for the normal Vlans */ + BFA_STATUS_CMD_NOTSUPP_CNA = 146, /* Command not supported for CNA */ + BFA_STATUS_IM_PASSTHRU_EDIT = 147, /* Can not edit passthru vlan id */ + BFA_STATUS_IM_BIND_FAILED = 148, /*! < IM Driver bind operation + * failed */ + BFA_STATUS_IM_UNBIND_FAILED = 149, /* ! < IM Driver unbind operation + * failed */ + BFA_STATUS_MAX_VAL /* Unknown error code */ +}; +#define bfa_status_t enum bfa_status + +enum bfa_eproto_status { + BFA_EPROTO_BAD_ACCEPT = 0, + BFA_EPROTO_UNKNOWN_RSP = 1 +}; +#define bfa_eproto_status_t enum bfa_eproto_status + +#endif /* __BFA_DEFS_STATUS_H__ */ diff --git a/drivers/scsi/bfa/include/defs/bfa_defs_tin.h b/drivers/scsi/bfa/include/defs/bfa_defs_tin.h new file mode 100644 index 000000000000..e05a2db7abed --- /dev/null +++ b/drivers/scsi/bfa/include/defs/bfa_defs_tin.h @@ -0,0 +1,118 @@ +/* + * Copyright (c) 2005-2009 Brocade Communications Systems, Inc. + * All rights reserved + * www.brocade.com + * + * Linux driver for Brocade Fibre Channel Host Bus Adapter. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (GPL) Version 2 as + * published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#ifndef __BFA_DEFS_TIN_H__ +#define __BFA_DEFS_TIN_H__ + +#include <protocol/types.h> +#include <protocol/fc.h> + +/** + * FCS tin states + */ +enum bfa_tin_state_e { + BFA_TIN_SM_OFFLINE = 0, /* tin is offline */ + BFA_TIN_SM_WOS_LOGIN = 1, /* Waiting PRLI ACC/RJT from ULP */ + BFA_TIN_SM_WFW_ONLINE = 2, /* Waiting ACK to PRLI ACC from FW */ + BFA_TIN_SM_ONLINE = 3, /* tin login is complete */ + BFA_TIN_SM_WIO_RELOGIN = 4, /* tin relogin is in progress */ + BFA_TIN_SM_WIO_LOGOUT = 5, /* Processing of PRLO req from + * Initiator is in progress + */ + BFA_TIN_SM_WOS_LOGOUT = 6, /* Processing of PRLO req from + * Initiator is in progress + */ + BFA_TIN_SM_WIO_CLEAN = 7, /* Waiting for IO cleanup before tin + * is offline. This can be triggered + * by RPORT LOGO (rcvd/sent) or by + * PRLO (rcvd/sent) + */ +}; + +struct bfa_prli_req_s { + struct fchs_s fchs; + struct fc_prli_s prli_payload; +}; + +struct bfa_prlo_req_s { + struct fchs_s fchs; + struct fc_prlo_s prlo_payload; +}; + +void bfa_tin_send_login_rsp(void *bfa_tin, u32 login_rsp, + struct fc_ls_rjt_s rjt_payload); +void bfa_tin_send_logout_rsp(void *bfa_tin, u32 logout_rsp, + struct fc_ls_rjt_s rjt_payload); +/** + * FCS target port statistics + */ +struct bfa_tin_stats_s { + u32 onlines; /* ITN nexus onlines (PRLI done) */ + u32 offlines; /* ITN Nexus offlines */ + u32 prli_req_parse_err; /* prli req parsing errors */ + u32 prli_rsp_rjt; /* num prli rsp rejects sent */ + u32 prli_rsp_acc; /* num prli rsp accepts sent */ + u32 cleanup_comps; /* ITN cleanup completions */ +}; + +/** + * FCS tin attributes returned in queries + */ +struct bfa_tin_attr_s { + enum bfa_tin_state_e state; + u8 seq_retry; /* Sequence retry supported */ + u8 rsvd[3]; +}; + +/** + * BFA TIN async event data structure for BFAL + */ +enum bfa_tin_aen_event { + BFA_TIN_AEN_ONLINE = 1, /* Target online */ + BFA_TIN_AEN_OFFLINE = 2, /* Target offline */ + BFA_TIN_AEN_DISCONNECT = 3, /* Target disconnected */ +}; + +/** + * BFA TIN event data structure. + */ +struct bfa_tin_aen_data_s { + u16 vf_id; /* vf_id of the IT nexus */ + u16 rsvd[3]; + wwn_t lpwwn; /* WWN of logical port */ + wwn_t rpwwn; /* WWN of remote(target) port */ +}; + +/** + * Below APIs are needed from BFA driver + * Move these to BFA driver public header file? + */ +/* TIN rcvd new PRLI & gets bfad_tin_t ptr from driver this callback */ +void *bfad_tin_rcvd_login_req(void *bfad_tm_port, void *bfa_tin, + wwn_t rp_wwn, u32 rp_fcid, + struct bfa_prli_req_s prli_req); +/* TIN rcvd new PRLO */ +void bfad_tin_rcvd_logout_req(void *bfad_tin, wwn_t rp_wwn, u32 rp_fcid, + struct bfa_prlo_req_s prlo_req); +/* TIN is online and ready for IO */ +void bfad_tin_online(void *bfad_tin); +/* TIN is offline and BFA driver can shutdown its upper stack */ +void bfad_tin_offline(void *bfad_tin); +/* TIN does not need this BFA driver tin tag anymore, so can be freed */ +void bfad_tin_res_free(void *bfad_tin); + +#endif /* __BFA_DEFS_TIN_H__ */ diff --git a/drivers/scsi/bfa/include/defs/bfa_defs_tsensor.h b/drivers/scsi/bfa/include/defs/bfa_defs_tsensor.h new file mode 100644 index 000000000000..31881d218515 --- /dev/null +++ b/drivers/scsi/bfa/include/defs/bfa_defs_tsensor.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2005-2009 Brocade Communications Systems, Inc. + * All rights reserved + * www.brocade.com + * + * Linux driver for Brocade Fibre Channel Host Bus Adapter. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (GPL) Version 2 as + * published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#ifndef __BFA_DEFS_TSENSOR_H__ +#define __BFA_DEFS_TSENSOR_H__ + +#include <bfa_os_inc.h> +#include <defs/bfa_defs_types.h> + +/** + * Temperature sensor status values + */ +enum bfa_tsensor_status { + BFA_TSENSOR_STATUS_UNKNOWN = 1, /* unkown status */ + BFA_TSENSOR_STATUS_FAULTY = 2, /* sensor is faulty */ + BFA_TSENSOR_STATUS_BELOW_MIN = 3, /* temperature below mininum */ + BFA_TSENSOR_STATUS_NOMINAL = 4, /* normal temperature */ + BFA_TSENSOR_STATUS_ABOVE_MAX = 5, /* temperature above maximum */ +}; + +/** + * Temperature sensor attribute + */ +struct bfa_tsensor_attr_s { + enum bfa_tsensor_status status; /* temperature sensor status */ + u32 value; /* current temperature in celsius */ +}; + +#endif /* __BFA_DEFS_TSENSOR_H__ */ diff --git a/drivers/scsi/bfa/include/defs/bfa_defs_types.h b/drivers/scsi/bfa/include/defs/bfa_defs_types.h new file mode 100644 index 000000000000..4348332b107a --- /dev/null +++ b/drivers/scsi/bfa/include/defs/bfa_defs_types.h @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2005-2009 Brocade Communications Systems, Inc. + * All rights reserved + * www.brocade.com + * + * Linux driver for Brocade Fibre Channel Host Bus Adapter. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (GPL) Version 2 as + * published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ +#ifndef __BFA_DEFS_TYPES_H__ +#define __BFA_DEFS_TYPES_H__ + +#include <bfa_os_inc.h> + +enum bfa_boolean { + BFA_FALSE = 0, + BFA_TRUE = 1 +}; +#define bfa_boolean_t enum bfa_boolean + +#define BFA_STRING_32 32 + +#endif /* __BFA_DEFS_TYPES_H__ */ diff --git a/drivers/scsi/bfa/include/defs/bfa_defs_version.h b/drivers/scsi/bfa/include/defs/bfa_defs_version.h new file mode 100644 index 000000000000..f8902a2c9aad --- /dev/null +++ b/drivers/scsi/bfa/include/defs/bfa_defs_version.h @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2005-2009 Brocade Communications Systems, Inc. + * All rights reserved + * www.brocade.com + * + * Linux driver for Brocade Fibre Channel Host Bus Adapter. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (GPL) Version 2 as + * published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ +#ifndef __BFA_DEFS_VERSION_H__ +#define __BFA_DEFS_VERSION_H__ + +#define BFA_VERSION_LEN 64 + +#endif diff --git a/drivers/scsi/bfa/include/defs/bfa_defs_vf.h b/drivers/scsi/bfa/include/defs/bfa_defs_vf.h new file mode 100644 index 000000000000..3235be5e9423 --- /dev/null +++ b/drivers/scsi/bfa/include/defs/bfa_defs_vf.h @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2005-2009 Brocade Communications Systems, Inc. + * All rights reserved + * www.brocade.com + * + * Linux driver for Brocade Fibre Channel Host Bus Adapter. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (GPL) Version 2 as + * published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#ifndef __BFA_DEFS_VF_H__ +#define __BFA_DEFS_VF_H__ + +#include <bfa_os_inc.h> +#include <defs/bfa_defs_port.h> +#include <protocol/types.h> + +/** + * VF states + */ +enum bfa_vf_state { + BFA_VF_UNINIT = 0, /* fabric is not yet initialized */ + BFA_VF_LINK_DOWN = 1, /* link is down */ + BFA_VF_FLOGI = 2, /* flogi is in progress */ + BFA_VF_AUTH = 3, /* authentication in progress */ + BFA_VF_NOFABRIC = 4, /* fabric is not present */ + BFA_VF_ONLINE = 5, /* login to fabric is complete */ + BFA_VF_EVFP = 6, /* EVFP is in progress */ + BFA_VF_ISOLATED = 7, /* port isolated due to vf_id mismatch */ +}; + +/** + * VF statistics + */ +struct bfa_vf_stats_s { + u32 flogi_sent; /* Num FLOGIs sent */ + u32 flogi_rsp_err; /* FLOGI response errors */ + u32 flogi_acc_err; /* FLOGI accept errors */ + u32 flogi_accepts; /* FLOGI accepts received */ + u32 flogi_rejects; /* FLOGI rejects received */ + u32 flogi_unknown_rsp; /* Unknown responses for FLOGI */ + u32 flogi_alloc_wait; /* Allocation waits prior to + * sending FLOGI + */ + u32 flogi_rcvd; /* FLOGIs received */ + u32 flogi_rejected; /* Incoming FLOGIs rejected */ + u32 fabric_onlines; /* Internal fabric online + * notification sent to other + * modules + */ + u32 fabric_offlines; /* Internal fabric offline + * notification sent to other + * modules + */ + u32 resvd; +}; + +/** + * VF attributes returned in queries + */ +struct bfa_vf_attr_s { + enum bfa_vf_state state; /* VF state */ + u32 rsvd; + wwn_t fabric_name; /* fabric name */ +}; + +#endif /* __BFA_DEFS_VF_H__ */ diff --git a/drivers/scsi/bfa/include/defs/bfa_defs_vport.h b/drivers/scsi/bfa/include/defs/bfa_defs_vport.h new file mode 100644 index 000000000000..9f021f43b3b4 --- /dev/null +++ b/drivers/scsi/bfa/include/defs/bfa_defs_vport.h @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2005-2009 Brocade Communications Systems, Inc. + * All rights reserved + * www.brocade.com + * + * Linux driver for Brocade Fibre Channel Host Bus Adapter. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (GPL) Version 2 as + * published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#ifndef __BFA_DEFS_VPORT_H__ +#define __BFA_DEFS_VPORT_H__ + +#include <bfa_os_inc.h> +#include <defs/bfa_defs_port.h> +#include <protocol/types.h> + +/** + * VPORT states + */ +enum bfa_vport_state { + BFA_FCS_VPORT_UNINIT = 0, + BFA_FCS_VPORT_CREATED = 1, + BFA_FCS_VPORT_OFFLINE = 1, + BFA_FCS_VPORT_FDISC_SEND = 2, + BFA_FCS_VPORT_FDISC = 3, + BFA_FCS_VPORT_FDISC_RETRY = 4, + BFA_FCS_VPORT_ONLINE = 5, + BFA_FCS_VPORT_DELETING = 6, + BFA_FCS_VPORT_CLEANUP = 6, + BFA_FCS_VPORT_LOGO_SEND = 7, + BFA_FCS_VPORT_LOGO = 8, + BFA_FCS_VPORT_ERROR = 9, + BFA_FCS_VPORT_MAX_STATE, +}; + +/** + * vport statistics + */ +struct bfa_vport_stats_s { + struct bfa_port_stats_s port_stats; /* base class (port) stats */ + /* + * TODO - remove + */ + + u32 fdisc_sent; /* num fdisc sent */ + u32 fdisc_accepts; /* fdisc accepts */ + u32 fdisc_retries; /* fdisc retries */ + u32 fdisc_timeouts; /* fdisc timeouts */ + u32 fdisc_rsp_err; /* fdisc response error */ + u32 fdisc_acc_bad; /* bad fdisc accepts */ + u32 fdisc_rejects; /* fdisc rejects */ + u32 fdisc_unknown_rsp; + /* + *!< fdisc rsp unknown error + */ + u32 fdisc_alloc_wait;/* fdisc req (fcxp)alloc wait */ + + u32 logo_alloc_wait;/* logo req (fcxp) alloc wait */ + u32 logo_sent; /* logo sent */ + u32 logo_accepts; /* logo accepts */ + u32 logo_rejects; /* logo rejects */ + u32 logo_rsp_err; /* logo rsp errors */ + u32 logo_unknown_rsp; + /* logo rsp unknown errors */ + + u32 fab_no_npiv; /* fabric does not support npiv */ + + u32 fab_offline; /* offline events from fab SM */ + u32 fab_online; /* online events from fab SM */ + u32 fab_cleanup; /* cleanup request from fab SM */ + u32 rsvd; +}; + +/** + * BFA vport attribute returned in queries + */ +struct bfa_vport_attr_s { + struct bfa_port_attr_s port_attr; /* base class (port) attributes */ + enum bfa_vport_state vport_state; /* vport state */ + u32 rsvd; +}; + +#endif /* __BFA_DEFS_VPORT_H__ */ diff --git a/drivers/scsi/bfa/include/fcb/bfa_fcb.h b/drivers/scsi/bfa/include/fcb/bfa_fcb.h new file mode 100644 index 000000000000..2963b0bc30e7 --- /dev/null +++ b/drivers/scsi/bfa/include/fcb/bfa_fcb.h @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2005-2009 Brocade Communications Systems, Inc. + * All rights reserved + * www.brocade.com + * + * Linux driver for Brocade Fibre Channel Host Bus Adapter. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (GPL) Version 2 as + * published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +/** + * bfa_fcb.h BFA FCS callback interfaces + */ + +#ifndef __BFA_FCB_H__ +#define __BFA_FCB_H__ + +/** + * fcb Main fcs callbacks + */ + +void bfa_fcb_exit(struct bfad_s *bfad); + + + +#endif /* __BFA_FCB_H__ */ diff --git a/drivers/scsi/bfa/include/fcb/bfa_fcb_fcpim.h b/drivers/scsi/bfa/include/fcb/bfa_fcb_fcpim.h new file mode 100644 index 000000000000..a6c70aee0aa3 --- /dev/null +++ b/drivers/scsi/bfa/include/fcb/bfa_fcb_fcpim.h @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2005-2009 Brocade Communications Systems, Inc. + * All rights reserved + * www.brocade.com + * + * Linux driver for Brocade Fibre Channel Host Bus Adapter. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (GPL) Version 2 as + * published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +/** +* : bfad_fcpim.h - BFA FCS initiator mode remote port callbacks + */ + +#ifndef __BFAD_FCB_FCPIM_H__ +#define __BFAD_FCB_FCPIM_H__ + +struct bfad_itnim_s; + +/* + * RPIM callbacks + */ + +/** + * Memory allocation for remote port instance. Called before PRLI is + * initiated to the remote target port. + * + * @param[in] bfad - driver instance + * @param[out] itnim - FCS remote port (IM) instance + * @param[out] itnim_drv - driver remote port (IM) instance + * + * @return None + */ +void bfa_fcb_itnim_alloc(struct bfad_s *bfad, struct bfa_fcs_itnim_s **itnim, + struct bfad_itnim_s **itnim_drv); + +/** + * Free remote port (IM) instance. + * + * @param[in] bfad - driver instance + * @param[in] itnim_drv - driver remote port instance + * + * @return None + */ +void bfa_fcb_itnim_free(struct bfad_s *bfad, + struct bfad_itnim_s *itnim_drv); + +/** + * Notification of when login with a remote target device is complete. + * + * @param[in] itnim_drv - driver remote port instance + * + * @return None + */ +void bfa_fcb_itnim_online(struct bfad_itnim_s *itnim_drv); + +/** + * Notification when login with the remote device is severed. + * + * @param[in] itnim_drv - driver remote port instance + * + * @return None + */ +void bfa_fcb_itnim_offline(struct bfad_itnim_s *itnim_drv); + +void bfa_fcb_itnim_tov_begin(struct bfad_itnim_s *itnim_drv); +void bfa_fcb_itnim_tov(struct bfad_itnim_s *itnim_drv); + +#endif /* __BFAD_FCB_FCPIM_H__ */ diff --git a/drivers/scsi/bfa/include/fcb/bfa_fcb_port.h b/drivers/scsi/bfa/include/fcb/bfa_fcb_port.h new file mode 100644 index 000000000000..5fd7f986fa32 --- /dev/null +++ b/drivers/scsi/bfa/include/fcb/bfa_fcb_port.h @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2005-2009 Brocade Communications Systems, Inc. + * All rights reserved + * www.brocade.com + * + * Linux driver for Brocade Fibre Channel Host Bus Adapter. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (GPL) Version 2 as + * published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +/** + * bfa_fcb_port.h BFA FCS virtual port driver interfaces + */ + +#ifndef __BFA_FCB_PORT_H__ +#define __BFA_FCB_PORT_H__ + +#include <fcb/bfa_fcb_vport.h> +/** + * fcs_port_fcb FCS port driver interfaces + */ + +/* + * Forward declarations + */ +struct bfad_port_s; + +/* + * Callback functions from BFA FCS to driver + */ + +/** + * Call from FCS to driver module when a port is instantiated. The port + * can be a base port or a virtual port with in the base fabric or + * a virtual fabric. + * + * On this callback, driver is supposed to create scsi_host, scsi_tgt or + * network interfaces bases on ports personality/roles. + * + * base port of base fabric: vf_drv == NULL && vp_drv == NULL + * vport of base fabric: vf_drv == NULL && vp_drv != NULL + * base port of VF: vf_drv != NULL && vp_drv == NULL + * vport of VF: vf_drv != NULL && vp_drv != NULL + * + * @param[in] bfad - driver instance + * @param[in] port - FCS port instance + * @param[in] roles - port roles: IM, TM, IP + * @param[in] vf_drv - VF driver instance, NULL if base fabric (no VF) + * @param[in] vp_drv - vport driver instance, NULL if base port + * + * @return None + */ +struct bfad_port_s *bfa_fcb_port_new(struct bfad_s *bfad, + struct bfa_fcs_port_s *port, + enum bfa_port_role roles, struct bfad_vf_s *vf_drv, + struct bfad_vport_s *vp_drv); + +/** + * Call from FCS to driver module when a port is deleted. The port + * can be a base port or a virtual port with in the base fabric or + * a virtual fabric. + * + * @param[in] bfad - driver instance + * @param[in] roles - port roles: IM, TM, IP + * @param[in] vf_drv - VF driver instance, NULL if base fabric (no VF) + * @param[in] vp_drv - vport driver instance, NULL if base port + * + * @return None + */ +void bfa_fcb_port_delete(struct bfad_s *bfad, enum bfa_port_role roles, + struct bfad_vf_s *vf_drv, struct bfad_vport_s *vp_drv); + +/** + * Notification when port transitions to ONLINE state. + * + * Online notification is a logical link up for the local port. This + * notification is sent after a successfull FLOGI, or a successful + * link initialization in proviate-loop or N2N topologies. + * + * @param[in] bfad - driver instance + * @param[in] roles - port roles: IM, TM, IP + * @param[in] vf_drv - VF driver instance, NULL if base fabric (no VF) + * @param[in] vp_drv - vport driver instance, NULL if base port + * + * @return None + */ +void bfa_fcb_port_online(struct bfad_s *bfad, enum bfa_port_role roles, + struct bfad_vf_s *vf_drv, struct bfad_vport_s *vp_drv); + +/** + * Notification when port transitions to OFFLINE state. + * + * Offline notification is a logical link down for the local port. + * + * @param[in] bfad - driver instance + * @param[in] roles - port roles: IM, TM, IP + * @param[in] vf_drv - VF driver instance, NULL if base fabric (no VF) + * @param[in] vp_drv - vport driver instance, NULL if base port + * + * @return None + */ +void bfa_fcb_port_offline(struct bfad_s *bfad, enum bfa_port_role roles, + struct bfad_vf_s *vf_drv, struct bfad_vport_s *vp_drv); + + +#endif /* __BFA_FCB_PORT_H__ */ diff --git a/drivers/scsi/bfa/include/fcb/bfa_fcb_rport.h b/drivers/scsi/bfa/include/fcb/bfa_fcb_rport.h new file mode 100644 index 000000000000..e0261bb6d1c1 --- /dev/null +++ b/drivers/scsi/bfa/include/fcb/bfa_fcb_rport.h @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2005-2009 Brocade Communications Systems, Inc. + * All rights reserved + * www.brocade.com + * + * Linux driver for Brocade Fibre Channel Host Bus Adapter. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (GPL) Version 2 as + * published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +/** + * bfa_fcb_rport.h BFA FCS rport driver interfaces + */ + +#ifndef __BFA_FCB_RPORT_H__ +#define __BFA_FCB_RPORT_H__ + +/** + * fcs_rport_fcb Remote port driver interfaces + */ + + +struct bfad_rport_s; + +/* + * Callback functions from BFA FCS to driver + */ + +/** + * Completion callback for bfa_fcs_rport_add(). + * + * @param[in] rport_drv - driver instance of rport + * + * @return None + */ +void bfa_fcb_rport_add(struct bfad_rport_s *rport_drv); + +/** + * Completion callback for bfa_fcs_rport_remove(). + * + * @param[in] rport_drv - driver instance of rport + * + * @return None + */ +void bfa_fcb_rport_remove(struct bfad_rport_s *rport_drv); + +/** + * Call to allocate a rport instance. + * + * @param[in] bfad - driver instance + * @param[out] rport - BFA FCS instance of rport + * @param[out] rport_drv - driver instance of rport + * + * @retval BFA_STATUS_OK - successfully allocated + * @retval BFA_STATUS_ENOMEM - cannot allocate + */ +bfa_status_t bfa_fcb_rport_alloc(struct bfad_s *bfad, + struct bfa_fcs_rport_s **rport, + struct bfad_rport_s **rport_drv); + +/** + * Call to free rport memory resources. + * + * @param[in] bfad - driver instance + * @param[in] rport_drv - driver instance of rport + * + * @return None + */ +void bfa_fcb_rport_free(struct bfad_s *bfad, struct bfad_rport_s **rport_drv); + + + +#endif /* __BFA_FCB_RPORT_H__ */ diff --git a/drivers/scsi/bfa/include/fcb/bfa_fcb_vf.h b/drivers/scsi/bfa/include/fcb/bfa_fcb_vf.h new file mode 100644 index 000000000000..cfd3fac0a4e2 --- /dev/null +++ b/drivers/scsi/bfa/include/fcb/bfa_fcb_vf.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2005-2009 Brocade Communications Systems, Inc. + * All rights reserved + * www.brocade.com + * + * Linux driver for Brocade Fibre Channel Host Bus Adapter. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (GPL) Version 2 as + * published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +/** + * bfa_fcb_vf.h BFA FCS virtual fabric driver interfaces + */ + +#ifndef __BFA_FCB_VF_H__ +#define __BFA_FCB_VF_H__ + +/** + * fcs_vf_fcb Virtual fabric driver intrefaces + */ + + +struct bfad_vf_s; + +/* + * Callback functions from BFA FCS to driver + */ + +/** + * Completion callback for bfa_fcs_vf_stop(). + * + * @param[in] vf_drv - driver instance of vf + * + * @return None + */ +void bfa_fcb_vf_stop(struct bfad_vf_s *vf_drv); + + + +#endif /* __BFA_FCB_VF_H__ */ diff --git a/drivers/scsi/bfa/include/fcb/bfa_fcb_vport.h b/drivers/scsi/bfa/include/fcb/bfa_fcb_vport.h new file mode 100644 index 000000000000..a39f474c2fcf --- /dev/null +++ b/drivers/scsi/bfa/include/fcb/bfa_fcb_vport.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2005-2009 Brocade Communications Systems, Inc. + * All rights reserved + * www.brocade.com + * + * Linux driver for Brocade Fibre Channel Host Bus Adapter. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (GPL) Version 2 as + * published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +/** + * bfa_fcb_vport.h BFA FCS virtual port driver interfaces + */ + +#ifndef __BFA_FCB_VPORT_H__ +#define __BFA_FCB_VPORT_H__ + +/** + * fcs_vport_fcb Virtual port driver interfaces + */ + + +struct bfad_vport_s; + +/* + * Callback functions from BFA FCS to driver + */ + +/** + * Completion callback for bfa_fcs_vport_delete(). + * + * @param[in] vport_drv - driver instance of vport + * + * @return None + */ +void bfa_fcb_vport_delete(struct bfad_vport_s *vport_drv); + + + +#endif /* __BFA_FCB_VPORT_H__ */ diff --git a/drivers/scsi/bfa/include/fcs/bfa_fcs.h b/drivers/scsi/bfa/include/fcs/bfa_fcs.h new file mode 100644 index 000000000000..627669c65546 --- /dev/null +++ b/drivers/scsi/bfa/include/fcs/bfa_fcs.h @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2005-2009 Brocade Communications Systems, Inc. + * All rights reserved + * www.brocade.com + * + * Linux driver for Brocade Fibre Channel Host Bus Adapter. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (GPL) Version 2 as + * published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#ifndef __BFA_FCS_H__ +#define __BFA_FCS_H__ + +#include <cs/bfa_debug.h> +#include <defs/bfa_defs_status.h> +#include <defs/bfa_defs_version.h> +#include <bfa.h> +#include <fcs/bfa_fcs_fabric.h> + +#define BFA_FCS_OS_STR_LEN 64 + +struct bfa_fcs_stats_s { + struct { + u32 untagged; /* untagged receive frames */ + u32 tagged; /* tagged receive frames */ + u32 vfid_unknown; /* VF id is unknown */ + } uf; +}; + +struct bfa_fcs_driver_info_s { + u8 version[BFA_VERSION_LEN]; /* Driver Version */ + u8 host_machine_name[BFA_FCS_OS_STR_LEN]; + u8 host_os_name[BFA_FCS_OS_STR_LEN]; /* OS name and version */ + u8 host_os_patch[BFA_FCS_OS_STR_LEN];/* patch or service pack */ + u8 os_device_name[BFA_FCS_OS_STR_LEN]; /* Driver Device Name */ +}; + +struct bfa_fcs_s { + struct bfa_s *bfa; /* corresponding BFA bfa instance */ + struct bfad_s *bfad; /* corresponding BDA driver instance */ + struct bfa_log_mod_s *logm; /* driver logging module instance */ + struct bfa_trc_mod_s *trcmod; /* tracing module */ + struct bfa_aen_s *aen; /* aen component */ + bfa_boolean_t vf_enabled; /* VF mode is enabled */ + bfa_boolean_t min_cfg; /* min cfg enabled/disabled */ + u16 port_vfid; /* port default VF ID */ + struct bfa_fcs_driver_info_s driver_info; + struct bfa_fcs_fabric_s fabric; /* base fabric state machine */ + struct bfa_fcs_stats_s stats; /* FCS statistics */ + struct bfa_wc_s wc; /* waiting counter */ +}; + +/* + * bfa fcs API functions + */ +void bfa_fcs_init(struct bfa_fcs_s *fcs, struct bfa_s *bfa, struct bfad_s *bfad, + bfa_boolean_t min_cfg); +void bfa_fcs_driver_info_init(struct bfa_fcs_s *fcs, + struct bfa_fcs_driver_info_s *driver_info); +void bfa_fcs_exit(struct bfa_fcs_s *fcs); +void bfa_fcs_trc_init(struct bfa_fcs_s *fcs, struct bfa_trc_mod_s *trcmod); +void bfa_fcs_log_init(struct bfa_fcs_s *fcs, struct bfa_log_mod_s *logmod); +void bfa_fcs_aen_init(struct bfa_fcs_s *fcs, struct bfa_aen_s *aen); +void bfa_fcs_start(struct bfa_fcs_s *fcs); + +#endif /* __BFA_FCS_H__ */ diff --git a/drivers/scsi/bfa/include/fcs/bfa_fcs_auth.h b/drivers/scsi/bfa/include/fcs/bfa_fcs_auth.h new file mode 100644 index 000000000000..28c4c9ff08b3 --- /dev/null +++ b/drivers/scsi/bfa/include/fcs/bfa_fcs_auth.h @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2005-2009 Brocade Communications Systems, Inc. + * All rights reserved + * www.brocade.com + * + * Linux driver for Brocade Fibre Channel Host Bus Adapter. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (GPL) Version 2 as + * published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#ifndef __BFA_FCS_AUTH_H__ +#define __BFA_FCS_AUTH_H__ + +struct bfa_fcs_s; + +#include <defs/bfa_defs_status.h> +#include <defs/bfa_defs_auth.h> +#include <defs/bfa_defs_vf.h> +#include <cs/bfa_q.h> +#include <cs/bfa_sm.h> +#include <defs/bfa_defs_pport.h> +#include <fcs/bfa_fcs_lport.h> +#include <protocol/fc_sp.h> + +struct bfa_fcs_fabric_s; + + + +struct bfa_fcs_auth_s { + bfa_sm_t sm; /* state machine */ + bfa_boolean_t policy; /* authentication enabled/disabled */ + enum bfa_auth_status status; /* authentication status */ + enum auth_rjt_codes rjt_code; /* auth reject status */ + enum auth_rjt_code_exps rjt_code_exp; /* auth reject reason */ + enum bfa_auth_algo algo; /* Authentication algorithm */ + struct bfa_auth_stats_s stats; /* Statistics */ + enum auth_dh_gid group; /* DH(diffie-hellman) Group */ + enum bfa_auth_secretsource source; /* Secret source */ + char secret[BFA_AUTH_SECRET_STRING_LEN]; + /* secret string */ + u8 secret_len; + /* secret string length */ + u8 nretries; + /* number of retries */ + struct bfa_fcs_fabric_s *fabric;/* pointer to fabric */ + u8 sentcode; /* pointer to response data */ + u8 *response; /* pointer to response data */ + struct bfa_timer_s delay_timer; /* delay timer */ + struct bfa_fcxp_s *fcxp; /* pointer to fcxp */ + struct bfa_fcxp_wqe_s fcxp_wqe; +}; + +/** + * bfa fcs authentication public functions + */ +bfa_status_t bfa_fcs_auth_get_attr(struct bfa_fcs_s *port, + struct bfa_auth_attr_s *attr); +bfa_status_t bfa_fcs_auth_set_policy(struct bfa_fcs_s *port, + bfa_boolean_t policy); +enum bfa_auth_status bfa_fcs_auth_get_status(struct bfa_fcs_s *port); +bfa_status_t bfa_fcs_auth_set_algo(struct bfa_fcs_s *port, + enum bfa_auth_algo algo); +bfa_status_t bfa_fcs_auth_get_stats(struct bfa_fcs_s *port, + struct bfa_auth_stats_s *stats); +bfa_status_t bfa_fcs_auth_set_dh_group(struct bfa_fcs_s *port, int group); +bfa_status_t bfa_fcs_auth_set_secretstring(struct bfa_fcs_s *port, + char *secret); +bfa_status_t bfa_fcs_auth_set_secretstring_encrypt(struct bfa_fcs_s *port, + u32 secret[], u32 len); +bfa_status_t bfa_fcs_auth_set_secretsource(struct bfa_fcs_s *port, + enum bfa_auth_secretsource src); +bfa_status_t bfa_fcs_auth_reset_stats(struct bfa_fcs_s *port); +bfa_status_t bfa_fcs_auth_reinit(struct bfa_fcs_s *port); + +#endif /* __BFA_FCS_AUTH_H__ */ diff --git a/drivers/scsi/bfa/include/fcs/bfa_fcs_fabric.h b/drivers/scsi/bfa/include/fcs/bfa_fcs_fabric.h new file mode 100644 index 000000000000..4ffd2242d3de --- /dev/null +++ b/drivers/scsi/bfa/include/fcs/bfa_fcs_fabric.h @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2005-2009 Brocade Communications Systems, Inc. + * All rights reserved + * www.brocade.com + * + * Linux driver for Brocade Fibre Channel Host Bus Adapter. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (GPL) Version 2 as + * published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#ifndef __BFA_FCS_FABRIC_H__ +#define __BFA_FCS_FABRIC_H__ + +struct bfa_fcs_s; + +#include <defs/bfa_defs_status.h> +#include <defs/bfa_defs_vf.h> +#include <cs/bfa_q.h> +#include <cs/bfa_sm.h> +#include <defs/bfa_defs_pport.h> +#include <fcs/bfa_fcs_lport.h> +#include <protocol/fc_sp.h> +#include <fcs/bfa_fcs_auth.h> + +/* + * forward declaration + */ +struct bfad_vf_s; + +enum bfa_fcs_fabric_type { + BFA_FCS_FABRIC_UNKNOWN = 0, + BFA_FCS_FABRIC_SWITCHED = 1, + BFA_FCS_FABRIC_PLOOP = 2, + BFA_FCS_FABRIC_N2N = 3, +}; + + +struct bfa_fcs_fabric_s { + struct list_head qe; /* queue element */ + bfa_sm_t sm; /* state machine */ + struct bfa_fcs_s *fcs; /* FCS instance */ + struct bfa_fcs_port_s bport; /* base logical port */ + enum bfa_fcs_fabric_type fab_type; /* fabric type */ + enum bfa_pport_type oper_type; /* current link topology */ + u8 is_vf; /* is virtual fabric? */ + u8 is_npiv; /* is NPIV supported ? */ + u8 is_auth; /* is Security/Auth supported ? */ + u16 bb_credit; /* BB credit from fabric */ + u16 vf_id; /* virtual fabric ID */ + u16 num_vports; /* num vports */ + u16 rsvd; + struct list_head vport_q; /* queue of virtual ports */ + struct list_head vf_q; /* queue of virtual fabrics */ + struct bfad_vf_s *vf_drv; /* driver vf structure */ + struct bfa_timer_s link_timer; /* Link Failure timer. Vport */ + wwn_t fabric_name; /* attached fabric name */ + bfa_boolean_t auth_reqd; /* authentication required */ + struct bfa_timer_s delay_timer; /* delay timer */ + union { + u16 swp_vfid;/* switch port VF id */ + } event_arg; + struct bfa_fcs_auth_s auth; /* authentication config */ + struct bfa_wc_s wc; /* wait counter for delete */ + struct bfa_vf_stats_s stats; /* fabric/vf stats */ + struct bfa_lps_s *lps; /* lport login services */ + u8 fabric_ip_addr[BFA_FCS_FABRIC_IPADDR_SZ]; /* attached + * fabric's ip addr + */ +}; + +#define bfa_fcs_fabric_npiv_capable(__f) (__f)->is_npiv +#define bfa_fcs_fabric_is_switched(__f) \ + ((__f)->fab_type == BFA_FCS_FABRIC_SWITCHED) + +/** + * The design calls for a single implementation of base fabric and vf. + */ +#define bfa_fcs_vf_t struct bfa_fcs_fabric_s + +struct bfa_vf_event_s { + u32 undefined; +}; + +/** + * bfa fcs vf public functions + */ +bfa_status_t bfa_fcs_vf_mode_enable(struct bfa_fcs_s *fcs, u16 vf_id); +bfa_status_t bfa_fcs_vf_mode_disable(struct bfa_fcs_s *fcs); +bfa_status_t bfa_fcs_vf_create(bfa_fcs_vf_t *vf, struct bfa_fcs_s *fcs, + u16 vf_id, struct bfa_port_cfg_s *port_cfg, + struct bfad_vf_s *vf_drv); +bfa_status_t bfa_fcs_vf_delete(bfa_fcs_vf_t *vf); +void bfa_fcs_vf_start(bfa_fcs_vf_t *vf); +bfa_status_t bfa_fcs_vf_stop(bfa_fcs_vf_t *vf); +void bfa_fcs_vf_list(struct bfa_fcs_s *fcs, u16 *vf_ids, int *nvfs); +void bfa_fcs_vf_list_all(struct bfa_fcs_s *fcs, u16 *vf_ids, int *nvfs); +void bfa_fcs_vf_get_attr(bfa_fcs_vf_t *vf, struct bfa_vf_attr_s *vf_attr); +void bfa_fcs_vf_get_stats(bfa_fcs_vf_t *vf, + struct bfa_vf_stats_s *vf_stats); +void bfa_fcs_vf_clear_stats(bfa_fcs_vf_t *vf); +void bfa_fcs_vf_get_ports(bfa_fcs_vf_t *vf, wwn_t vpwwn[], int *nports); +bfa_fcs_vf_t *bfa_fcs_vf_lookup(struct bfa_fcs_s *fcs, u16 vf_id); +struct bfad_vf_s *bfa_fcs_vf_get_drv_vf(bfa_fcs_vf_t *vf); + +#endif /* __BFA_FCS_FABRIC_H__ */ diff --git a/drivers/scsi/bfa/include/fcs/bfa_fcs_fcpim.h b/drivers/scsi/bfa/include/fcs/bfa_fcs_fcpim.h new file mode 100644 index 000000000000..e719f2c3eb35 --- /dev/null +++ b/drivers/scsi/bfa/include/fcs/bfa_fcs_fcpim.h @@ -0,0 +1,131 @@ +/* + * Copyright (c) 2005-2009 Brocade Communications Systems, Inc. + * All rights reserved + * www.brocade.com + * + * Linux driver for Brocade Fibre Channel Host Bus Adapter. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (GPL) Version 2 as + * published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +/** + * bfa_fcs_fcpim.h BFA FCS FCP Initiator Mode interfaces/defines. + */ + +#ifndef __BFA_FCS_FCPIM_H__ +#define __BFA_FCS_FCPIM_H__ + +#include <defs/bfa_defs_status.h> +#include <defs/bfa_defs_itnim.h> +#include <fcs/bfa_fcs.h> +#include <fcs/bfa_fcs_rport.h> +#include <fcs/bfa_fcs_lport.h> +#include <bfa_fcpim.h> + +/* + * forward declarations + */ +struct bfad_itnim_s; + +struct bfa_fcs_itnim_s { + bfa_sm_t sm; /* state machine */ + struct bfa_fcs_rport_s *rport; /* parent remote rport */ + struct bfad_itnim_s *itnim_drv; /* driver peer instance */ + struct bfa_fcs_s *fcs; /* fcs instance */ + struct bfa_timer_s timer; /* timer functions */ + struct bfa_itnim_s *bfa_itnim; /* BFA itnim struct */ + bfa_boolean_t seq_rec; /* seq recovery support */ + bfa_boolean_t rec_support; /* REC supported */ + bfa_boolean_t conf_comp; /* FCP_CONF support */ + bfa_boolean_t task_retry_id; /* task retry id supp */ + struct bfa_fcxp_wqe_s fcxp_wqe; /* wait qelem for fcxp */ + struct bfa_fcxp_s *fcxp; /* FCXP in use */ + struct bfa_itnim_stats_s stats; /* itn statistics */ +}; + + +static inline struct bfad_port_s * +bfa_fcs_itnim_get_drvport(struct bfa_fcs_itnim_s *itnim) +{ + return itnim->rport->port->bfad_port; +} + + +static inline struct bfa_fcs_port_s * +bfa_fcs_itnim_get_port(struct bfa_fcs_itnim_s *itnim) +{ + return itnim->rport->port; +} + + +static inline wwn_t +bfa_fcs_itnim_get_nwwn(struct bfa_fcs_itnim_s *itnim) +{ + return itnim->rport->nwwn; +} + + +static inline wwn_t +bfa_fcs_itnim_get_pwwn(struct bfa_fcs_itnim_s *itnim) +{ + return itnim->rport->pwwn; +} + + +static inline u32 +bfa_fcs_itnim_get_fcid(struct bfa_fcs_itnim_s *itnim) +{ + return itnim->rport->pid; +} + + +static inline u32 +bfa_fcs_itnim_get_maxfrsize(struct bfa_fcs_itnim_s *itnim) +{ + return itnim->rport->maxfrsize; +} + + +static inline enum fc_cos +bfa_fcs_itnim_get_cos(struct bfa_fcs_itnim_s *itnim) +{ + return itnim->rport->fc_cos; +} + + +static inline struct bfad_itnim_s * +bfa_fcs_itnim_get_drvitn(struct bfa_fcs_itnim_s *itnim) +{ + return itnim->itnim_drv; +} + + +static inline struct bfa_itnim_s * +bfa_fcs_itnim_get_halitn(struct bfa_fcs_itnim_s *itnim) +{ + return itnim->bfa_itnim; +} + +/** + * bfa fcs FCP Initiator mode API functions + */ +void bfa_fcs_itnim_get_attr(struct bfa_fcs_itnim_s *itnim, + struct bfa_itnim_attr_s *attr); +void bfa_fcs_itnim_get_stats(struct bfa_fcs_itnim_s *itnim, + struct bfa_itnim_stats_s *stats); +struct bfa_fcs_itnim_s *bfa_fcs_itnim_lookup(struct bfa_fcs_port_s *port, + wwn_t rpwwn); +bfa_status_t bfa_fcs_itnim_attr_get(struct bfa_fcs_port_s *port, wwn_t rpwwn, + struct bfa_itnim_attr_s *attr); +bfa_status_t bfa_fcs_itnim_stats_get(struct bfa_fcs_port_s *port, wwn_t rpwwn, + struct bfa_itnim_stats_s *stats); +bfa_status_t bfa_fcs_itnim_stats_clear(struct bfa_fcs_port_s *port, + wwn_t rpwwn); +#endif /* __BFA_FCS_FCPIM_H__ */ diff --git a/drivers/scsi/bfa/include/fcs/bfa_fcs_fdmi.h b/drivers/scsi/bfa/include/fcs/bfa_fcs_fdmi.h new file mode 100644 index 000000000000..4441fffc9c82 --- /dev/null +++ b/drivers/scsi/bfa/include/fcs/bfa_fcs_fdmi.h @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2005-2009 Brocade Communications Systems, Inc. + * All rights reserved + * www.brocade.com + * + * Linux driver for Brocade Fibre Channel Host Bus Adapter. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (GPL) Version 2 as + * published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +/** + * bfa_fcs_fdmi.h BFA fcs fdmi module public interface + */ + +#ifndef __BFA_FCS_FDMI_H__ +#define __BFA_FCS_FDMI_H__ +#include <bfa_os_inc.h> +#include <protocol/fdmi.h> + +#define BFA_FCS_FDMI_SUPORTED_SPEEDS (FDMI_TRANS_SPEED_1G | \ + FDMI_TRANS_SPEED_2G | \ + FDMI_TRANS_SPEED_4G | \ + FDMI_TRANS_SPEED_8G) + +/* +* HBA Attribute Block : BFA internal representation. Note : Some variable +* sizes have been trimmed to suit BFA For Ex : Model will be "Brocade". Based + * on this the size has been reduced to 16 bytes from the standard's 64 bytes. + */ +struct bfa_fcs_fdmi_hba_attr_s { + wwn_t node_name; + u8 manufacturer[64]; + u8 serial_num[64]; + u8 model[16]; + u8 model_desc[256]; + u8 hw_version[8]; + u8 driver_version[8]; + u8 option_rom_ver[BFA_VERSION_LEN]; + u8 fw_version[8]; + u8 os_name[256]; + u32 max_ct_pyld; +}; + +/* + * Port Attribute Block + */ +struct bfa_fcs_fdmi_port_attr_s { + u8 supp_fc4_types[32]; /* supported FC4 types */ + u32 supp_speed; /* supported speed */ + u32 curr_speed; /* current Speed */ + u32 max_frm_size; /* max frame size */ + u8 os_device_name[256]; /* OS device Name */ + u8 host_name[256]; /* host name */ +}; + +#endif /* __BFA_FCS_FDMI_H__ */ diff --git a/drivers/scsi/bfa/include/fcs/bfa_fcs_lport.h b/drivers/scsi/bfa/include/fcs/bfa_fcs_lport.h new file mode 100644 index 000000000000..b85cba884b96 --- /dev/null +++ b/drivers/scsi/bfa/include/fcs/bfa_fcs_lport.h @@ -0,0 +1,226 @@ +/* + * Copyright (c) 2005-2009 Brocade Communications Systems, Inc. + * All rights reserved + * www.brocade.com + * + * Linux driver for Brocade Fibre Channel Host Bus Adapter. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (GPL) Version 2 as + * published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +/** + * bfa_fcs_port.h BFA fcs port module public interface + */ + +#ifndef __BFA_FCS_PORT_H__ +#define __BFA_FCS_PORT_H__ + +#include <defs/bfa_defs_status.h> +#include <defs/bfa_defs_port.h> +#include <defs/bfa_defs_pport.h> +#include <defs/bfa_defs_rport.h> +#include <cs/bfa_q.h> +#include <bfa_svc.h> +#include <cs/bfa_wc.h> + +struct bfa_fcs_s; +struct bfa_fcs_fabric_s; + +/* +* @todo : need to move to a global config file. + * Maximum Vports supported per physical port or vf. + */ +#define BFA_FCS_MAX_VPORTS_SUPP_CB 255 +#define BFA_FCS_MAX_VPORTS_SUPP_CT 191 + +/* +* @todo : need to move to a global config file. + * Maximum Rports supported per port (physical/logical). + */ +#define BFA_FCS_MAX_RPORTS_SUPP 256 /* @todo : tentative value */ + + +struct bfa_fcs_port_ns_s { + bfa_sm_t sm; /* state machine */ + struct bfa_timer_s timer; + struct bfa_fcs_port_s *port; /* parent port */ + struct bfa_fcxp_s *fcxp; + struct bfa_fcxp_wqe_s fcxp_wqe; +}; + + +struct bfa_fcs_port_scn_s { + bfa_sm_t sm; /* state machine */ + struct bfa_timer_s timer; + struct bfa_fcs_port_s *port; /* parent port */ + struct bfa_fcxp_s *fcxp; + struct bfa_fcxp_wqe_s fcxp_wqe; +}; + + +struct bfa_fcs_port_fdmi_s { + bfa_sm_t sm; /* state machine */ + struct bfa_timer_s timer; + struct bfa_fcs_port_ms_s *ms; /* parent ms */ + struct bfa_fcxp_s *fcxp; + struct bfa_fcxp_wqe_s fcxp_wqe; + u8 retry_cnt; /* retry count */ + u8 rsvd[3]; +}; + + +struct bfa_fcs_port_ms_s { + bfa_sm_t sm; /* state machine */ + struct bfa_timer_s timer; + struct bfa_fcs_port_s *port; /* parent port */ + struct bfa_fcxp_s *fcxp; + struct bfa_fcxp_wqe_s fcxp_wqe; + struct bfa_fcs_port_fdmi_s fdmi; /* FDMI component of MS */ + u8 retry_cnt; /* retry count */ + u8 rsvd[3]; +}; + + +struct bfa_fcs_port_fab_s { + struct bfa_fcs_port_ns_s ns; /* NS component of port */ + struct bfa_fcs_port_scn_s scn; /* scn component of port */ + struct bfa_fcs_port_ms_s ms; /* MS component of port */ +}; + + + +#define MAX_ALPA_COUNT 127 + +struct bfa_fcs_port_loop_s { + u8 num_alpa; /* Num of ALPA entries in the map */ + u8 alpa_pos_map[MAX_ALPA_COUNT]; /* ALPA Positional + *Map */ + struct bfa_fcs_port_s *port; /* parent port */ +}; + + + +struct bfa_fcs_port_n2n_s { + u32 rsvd; + u16 reply_oxid; /* ox_id from the req flogi to be + *used in flogi acc */ + wwn_t rem_port_wwn; /* Attached port's wwn */ +}; + + +union bfa_fcs_port_topo_u { + struct bfa_fcs_port_fab_s pfab; + struct bfa_fcs_port_loop_s ploop; + struct bfa_fcs_port_n2n_s pn2n; +}; + + +struct bfa_fcs_port_s { + struct list_head qe; /* used by port/vport */ + bfa_sm_t sm; /* state machine */ + struct bfa_fcs_fabric_s *fabric; /* parent fabric */ + struct bfa_port_cfg_s port_cfg; /* port configuration */ + struct bfa_timer_s link_timer; /* timer for link offline */ + u32 pid : 24; /* FC address */ + u8 lp_tag; /* lport tag */ + u16 num_rports; /* Num of r-ports */ + struct list_head rport_q; /* queue of discovered r-ports */ + struct bfa_fcs_s *fcs; /* FCS instance */ + union bfa_fcs_port_topo_u port_topo; /* fabric/loop/n2n details */ + struct bfad_port_s *bfad_port; /* driver peer instance */ + struct bfa_fcs_vport_s *vport; /* NULL for base ports */ + struct bfa_fcxp_s *fcxp; + struct bfa_fcxp_wqe_s fcxp_wqe; + struct bfa_port_stats_s stats; + struct bfa_wc_s wc; /* waiting counter for events */ +}; + +#define bfa_fcs_lport_t struct bfa_fcs_port_s + +/** + * Symbolic Name related defines + * Total bytes 255. + * Physical Port's symbolic name 128 bytes. + * For Vports, Vport's symbolic name is appended to the Physical port's + * Symbolic Name. + * + * Physical Port's symbolic name Format : (Total 128 bytes) + * Adapter Model number/name : 12 bytes + * Driver Version : 10 bytes + * Host Machine Name : 30 bytes + * Host OS Info : 48 bytes + * Host OS PATCH Info : 16 bytes + * ( remaining 12 bytes reserved to be used for separator) + */ +#define BFA_FCS_PORT_SYMBNAME_SEPARATOR " | " + +#define BFA_FCS_PORT_SYMBNAME_MODEL_SZ 12 +#define BFA_FCS_PORT_SYMBNAME_VERSION_SZ 10 +#define BFA_FCS_PORT_SYMBNAME_MACHINENAME_SZ 30 +#define BFA_FCS_PORT_SYMBNAME_OSINFO_SZ 48 +#define BFA_FCS_PORT_SYMBNAME_OSPATCH_SZ 16 + +/** + * Get FC port ID for a logical port. + */ +#define bfa_fcs_port_get_fcid(_lport) ((_lport)->pid) +#define bfa_fcs_port_get_pwwn(_lport) ((_lport)->port_cfg.pwwn) +#define bfa_fcs_port_get_nwwn(_lport) ((_lport)->port_cfg.nwwn) +#define bfa_fcs_port_get_psym_name(_lport) ((_lport)->port_cfg.sym_name) +#define bfa_fcs_port_is_initiator(_lport) \ + ((_lport)->port_cfg.roles & BFA_PORT_ROLE_FCP_IM) +#define bfa_fcs_port_is_target(_lport) \ + ((_lport)->port_cfg.roles & BFA_PORT_ROLE_FCP_TM) +#define bfa_fcs_port_get_nrports(_lport) \ + ((_lport) ? (_lport)->num_rports : 0) + +static inline struct bfad_port_s * +bfa_fcs_port_get_drvport(struct bfa_fcs_port_s *port) +{ + return port->bfad_port; +} + + +#define bfa_fcs_port_get_opertype(_lport) (_lport)->fabric->oper_type + + +#define bfa_fcs_port_get_fabric_name(_lport) (_lport)->fabric->fabric_name + + +#define bfa_fcs_port_get_fabric_ipaddr(_lport) (_lport)->fabric->fabric_ip_addr + +/** + * bfa fcs port public functions + */ +void bfa_fcs_cfg_base_port(struct bfa_fcs_s *fcs, + struct bfa_port_cfg_s *port_cfg); +struct bfa_fcs_port_s *bfa_fcs_get_base_port(struct bfa_fcs_s *fcs); +void bfa_fcs_port_get_rports(struct bfa_fcs_port_s *port, + wwn_t rport_wwns[], int *nrports); + +wwn_t bfa_fcs_port_get_rport(struct bfa_fcs_port_s *port, wwn_t wwn, + int index, int nrports, bfa_boolean_t bwwn); + +struct bfa_fcs_port_s *bfa_fcs_lookup_port(struct bfa_fcs_s *fcs, + u16 vf_id, wwn_t lpwwn); + +void bfa_fcs_port_get_info(struct bfa_fcs_port_s *port, + struct bfa_port_info_s *port_info); +void bfa_fcs_port_get_attr(struct bfa_fcs_port_s *port, + struct bfa_port_attr_s *port_attr); +void bfa_fcs_port_get_stats(struct bfa_fcs_port_s *fcs_port, + struct bfa_port_stats_s *port_stats); +void bfa_fcs_port_clear_stats(struct bfa_fcs_port_s *fcs_port); +enum bfa_pport_speed bfa_fcs_port_get_rport_max_speed( + struct bfa_fcs_port_s *port); +void bfa_fcs_port_enable_ipfc_roles(struct bfa_fcs_port_s *fcs_port); +void bfa_fcs_port_disable_ipfc_roles(struct bfa_fcs_port_s *fcs_port); + +#endif /* __BFA_FCS_PORT_H__ */ diff --git a/drivers/scsi/bfa/include/fcs/bfa_fcs_rport.h b/drivers/scsi/bfa/include/fcs/bfa_fcs_rport.h new file mode 100644 index 000000000000..702b95b76c2d --- /dev/null +++ b/drivers/scsi/bfa/include/fcs/bfa_fcs_rport.h @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2005-2009 Brocade Communications Systems, Inc. + * All rights reserved + * www.brocade.com + * + * Linux driver for Brocade Fibre Channel Host Bus Adapter. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (GPL) Version 2 as + * published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#ifndef __BFA_FCS_RPORT_H__ +#define __BFA_FCS_RPORT_H__ + +#include <defs/bfa_defs_status.h> +#include <cs/bfa_q.h> +#include <fcs/bfa_fcs.h> +#include <defs/bfa_defs_rport.h> + +#define BFA_FCS_RPORT_DEF_DEL_TIMEOUT 90 /* in secs */ +/* + * forward declarations + */ +struct bfad_rport_s; + +struct bfa_fcs_itnim_s; +struct bfa_fcs_tin_s; +struct bfa_fcs_iprp_s; + +/* Rport Features (RPF) */ +struct bfa_fcs_rpf_s { + bfa_sm_t sm; /* state machine */ + struct bfa_fcs_rport_s *rport; /* parent rport */ + struct bfa_timer_s timer; /* general purpose timer */ + struct bfa_fcxp_s *fcxp; /* FCXP needed for discarding */ + struct bfa_fcxp_wqe_s fcxp_wqe; /* fcxp wait queue element */ + int rpsc_retries; /* max RPSC retry attempts */ + enum bfa_pport_speed rpsc_speed; /* Current Speed from RPSC. + * O if RPSC fails */ + enum bfa_pport_speed assigned_speed; /* Speed assigned by the user. + * will be used if RPSC is not + * supported by the rport */ +}; + +struct bfa_fcs_rport_s { + struct list_head qe; /* used by port/vport */ + struct bfa_fcs_port_s *port; /* parent FCS port */ + struct bfa_fcs_s *fcs; /* fcs instance */ + struct bfad_rport_s *rp_drv; /* driver peer instance */ + u32 pid; /* port ID of rport */ + u16 maxfrsize; /* maximum frame size */ + u16 reply_oxid; /* OX_ID of inbound requests */ + enum fc_cos fc_cos; /* FC classes of service supp */ + bfa_boolean_t cisc; /* CISC capable device */ + wwn_t pwwn; /* port wwn of rport */ + wwn_t nwwn; /* node wwn of rport */ + struct bfa_rport_symname_s psym_name; /* port symbolic name */ + bfa_sm_t sm; /* state machine */ + struct bfa_timer_s timer; /* general purpose timer */ + struct bfa_fcs_itnim_s *itnim; /* ITN initiator mode role */ + struct bfa_fcs_tin_s *tin; /* ITN initiator mode role */ + struct bfa_fcs_iprp_s *iprp; /* IP/FC role */ + struct bfa_rport_s *bfa_rport; /* BFA Rport */ + struct bfa_fcxp_s *fcxp; /* FCXP needed for discarding */ + int plogi_retries; /* max plogi retry attempts */ + int ns_retries; /* max NS query retry attempts */ + struct bfa_fcxp_wqe_s fcxp_wqe; /* fcxp wait queue element */ + struct bfa_rport_stats_s stats; /* rport stats */ + enum bfa_rport_function scsi_function; /* Initiator/Target */ + struct bfa_fcs_rpf_s rpf; /* Rport features module */ +}; + +static inline struct bfa_rport_s * +bfa_fcs_rport_get_halrport(struct bfa_fcs_rport_s *rport) +{ + return rport->bfa_rport; +} + +/** + * bfa fcs rport API functions + */ +bfa_status_t bfa_fcs_rport_add(struct bfa_fcs_port_s *port, wwn_t *pwwn, + struct bfa_fcs_rport_s *rport, + struct bfad_rport_s *rport_drv); +bfa_status_t bfa_fcs_rport_remove(struct bfa_fcs_rport_s *rport); +void bfa_fcs_rport_get_attr(struct bfa_fcs_rport_s *rport, + struct bfa_rport_attr_s *attr); +void bfa_fcs_rport_get_stats(struct bfa_fcs_rport_s *rport, + struct bfa_rport_stats_s *stats); +void bfa_fcs_rport_clear_stats(struct bfa_fcs_rport_s *rport); +struct bfa_fcs_rport_s *bfa_fcs_rport_lookup(struct bfa_fcs_port_s *port, + wwn_t rpwwn); +struct bfa_fcs_rport_s *bfa_fcs_rport_lookup_by_nwwn( + struct bfa_fcs_port_s *port, wwn_t rnwwn); +void bfa_fcs_rport_set_del_timeout(u8 rport_tmo); +void bfa_fcs_rport_set_speed(struct bfa_fcs_rport_s *rport, + enum bfa_pport_speed speed); +#endif /* __BFA_FCS_RPORT_H__ */ diff --git a/drivers/scsi/bfa/include/fcs/bfa_fcs_vport.h b/drivers/scsi/bfa/include/fcs/bfa_fcs_vport.h new file mode 100644 index 000000000000..cd33f2cd5c34 --- /dev/null +++ b/drivers/scsi/bfa/include/fcs/bfa_fcs_vport.h @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2005-2009 Brocade Communications Systems, Inc. + * All rights reserved + * www.brocade.com + * + * Linux driver for Brocade Fibre Channel Host Bus Adapter. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (GPL) Version 2 as + * published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +/** + * bfa_fcs_vport.h BFA fcs vport module public interface + */ + +#ifndef __BFA_FCS_VPORT_H__ +#define __BFA_FCS_VPORT_H__ + +#include <defs/bfa_defs_status.h> +#include <defs/bfa_defs_port.h> +#include <defs/bfa_defs_vport.h> +#include <fcs/bfa_fcs.h> +#include <fcb/bfa_fcb_vport.h> + +struct bfa_fcs_vport_s { + struct list_head qe; /* queue elem */ + bfa_sm_t sm; /* state machine */ + bfa_fcs_lport_t lport; /* logical port */ + struct bfa_timer_s timer; /* general purpose timer */ + struct bfad_vport_s *vport_drv; /* Driver private */ + struct bfa_vport_stats_s vport_stats; /* vport statistics */ + struct bfa_lps_s *lps; /* Lport login service */ + int fdisc_retries; +}; + +#define bfa_fcs_vport_get_port(vport) \ + ((struct bfa_fcs_port_s *)(&vport->port)) + +/** + * bfa fcs vport public functions + */ +bfa_status_t bfa_fcs_vport_create(struct bfa_fcs_vport_s *vport, + struct bfa_fcs_s *fcs, u16 vf_id, + struct bfa_port_cfg_s *port_cfg, + struct bfad_vport_s *vport_drv); +bfa_status_t bfa_fcs_vport_delete(struct bfa_fcs_vport_s *vport); +bfa_status_t bfa_fcs_vport_start(struct bfa_fcs_vport_s *vport); +bfa_status_t bfa_fcs_vport_stop(struct bfa_fcs_vport_s *vport); +void bfa_fcs_vport_get_attr(struct bfa_fcs_vport_s *vport, + struct bfa_vport_attr_s *vport_attr); +void bfa_fcs_vport_get_stats(struct bfa_fcs_vport_s *vport, + struct bfa_vport_stats_s *vport_stats); +void bfa_fcs_vport_clr_stats(struct bfa_fcs_vport_s *vport); +struct bfa_fcs_vport_s *bfa_fcs_vport_lookup(struct bfa_fcs_s *fcs, + u16 vf_id, wwn_t vpwwn); + +#endif /* __BFA_FCS_VPORT_H__ */ diff --git a/drivers/scsi/bfa/include/log/bfa_log_fcs.h b/drivers/scsi/bfa/include/log/bfa_log_fcs.h new file mode 100644 index 000000000000..b6f5df8827f8 --- /dev/null +++ b/drivers/scsi/bfa/include/log/bfa_log_fcs.h @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2005-2009 Brocade Communications Systems, Inc. + * All rights reserved + * www.brocade.com + * + * Linux driver for Brocade Fibre Channel Host Bus Adapter. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (GPL) Version 2 as + * published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +/* + * messages define for FCS Module + */ +#ifndef __BFA_LOG_FCS_H__ +#define __BFA_LOG_FCS_H__ +#include <cs/bfa_log.h> +#define BFA_LOG_FCS_FABRIC_NOSWITCH \ + (((u32) BFA_LOG_FCS_ID << BFA_LOG_MODID_OFFSET) | 1) +#define BFA_LOG_FCS_FABRIC_ISOLATED \ + (((u32) BFA_LOG_FCS_ID << BFA_LOG_MODID_OFFSET) | 2) +#endif diff --git a/drivers/scsi/bfa/include/log/bfa_log_hal.h b/drivers/scsi/bfa/include/log/bfa_log_hal.h new file mode 100644 index 000000000000..0412aea2ec30 --- /dev/null +++ b/drivers/scsi/bfa/include/log/bfa_log_hal.h @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2005-2009 Brocade Communications Systems, Inc. + * All rights reserved + * www.brocade.com + * + * Linux driver for Brocade Fibre Channel Host Bus Adapter. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (GPL) Version 2 as + * published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +/* messages define for HAL Module */ +#ifndef __BFA_LOG_HAL_H__ +#define __BFA_LOG_HAL_H__ +#include <cs/bfa_log.h> +#define BFA_LOG_HAL_ASSERT \ + (((u32) BFA_LOG_HAL_ID << BFA_LOG_MODID_OFFSET) | 1) +#define BFA_LOG_HAL_HEARTBEAT_FAILURE \ + (((u32) BFA_LOG_HAL_ID << BFA_LOG_MODID_OFFSET) | 2) +#define BFA_LOG_HAL_FCPIM_PARM_INVALID \ + (((u32) BFA_LOG_HAL_ID << BFA_LOG_MODID_OFFSET) | 3) +#define BFA_LOG_HAL_SM_ASSERT \ + (((u32) BFA_LOG_HAL_ID << BFA_LOG_MODID_OFFSET) | 4) +#endif diff --git a/drivers/scsi/bfa/include/log/bfa_log_linux.h b/drivers/scsi/bfa/include/log/bfa_log_linux.h new file mode 100644 index 000000000000..317c0547ee16 --- /dev/null +++ b/drivers/scsi/bfa/include/log/bfa_log_linux.h @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2005-2009 Brocade Communications Systems, Inc. + * All rights reserved + * www.brocade.com + * + * Linux driver for Brocade Fibre Channel Host Bus Adapter. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (GPL) Version 2 as + * published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +/* messages define for LINUX Module */ +#ifndef __BFA_LOG_LINUX_H__ +#define __BFA_LOG_LINUX_H__ +#include <cs/bfa_log.h> +#define BFA_LOG_LINUX_DEVICE_CLAIMED \ + (((u32) BFA_LOG_LINUX_ID << BFA_LOG_MODID_OFFSET) | 1) +#define BFA_LOG_LINUX_HASH_INIT_FAILED \ + (((u32) BFA_LOG_LINUX_ID << BFA_LOG_MODID_OFFSET) | 2) +#define BFA_LOG_LINUX_SYSFS_FAILED \ + (((u32) BFA_LOG_LINUX_ID << BFA_LOG_MODID_OFFSET) | 3) +#define BFA_LOG_LINUX_MEM_ALLOC_FAILED \ + (((u32) BFA_LOG_LINUX_ID << BFA_LOG_MODID_OFFSET) | 4) +#define BFA_LOG_LINUX_DRIVER_REGISTRATION_FAILED \ + (((u32) BFA_LOG_LINUX_ID << BFA_LOG_MODID_OFFSET) | 5) +#define BFA_LOG_LINUX_ITNIM_FREE \ + (((u32) BFA_LOG_LINUX_ID << BFA_LOG_MODID_OFFSET) | 6) +#define BFA_LOG_LINUX_ITNIM_ONLINE \ + (((u32) BFA_LOG_LINUX_ID << BFA_LOG_MODID_OFFSET) | 7) +#define BFA_LOG_LINUX_ITNIM_OFFLINE \ + (((u32) BFA_LOG_LINUX_ID << BFA_LOG_MODID_OFFSET) | 8) +#define BFA_LOG_LINUX_SCSI_HOST_FREE \ + (((u32) BFA_LOG_LINUX_ID << BFA_LOG_MODID_OFFSET) | 9) +#define BFA_LOG_LINUX_SCSI_ABORT \ + (((u32) BFA_LOG_LINUX_ID << BFA_LOG_MODID_OFFSET) | 10) +#define BFA_LOG_LINUX_SCSI_ABORT_COMP \ + (((u32) BFA_LOG_LINUX_ID << BFA_LOG_MODID_OFFSET) | 11) +#endif diff --git a/drivers/scsi/bfa/include/log/bfa_log_wdrv.h b/drivers/scsi/bfa/include/log/bfa_log_wdrv.h new file mode 100644 index 000000000000..809a95f7afe2 --- /dev/null +++ b/drivers/scsi/bfa/include/log/bfa_log_wdrv.h @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2005-2009 Brocade Communications Systems, Inc. + * All rights reserved + * www.brocade.com + * + * Linux driver for Brocade Fibre Channel Host Bus Adapter. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (GPL) Version 2 as + * published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +/* + * messages define for WDRV Module + */ +#ifndef __BFA_LOG_WDRV_H__ +#define __BFA_LOG_WDRV_H__ +#include <cs/bfa_log.h> +#define BFA_LOG_WDRV_IOC_INIT_ERROR \ + (((u32) BFA_LOG_WDRV_ID << BFA_LOG_MODID_OFFSET) | 1) +#define BFA_LOG_WDRV_IOC_INTERNAL_ERROR \ + (((u32) BFA_LOG_WDRV_ID << BFA_LOG_MODID_OFFSET) | 2) +#define BFA_LOG_WDRV_IOC_START_ERROR \ + (((u32) BFA_LOG_WDRV_ID << BFA_LOG_MODID_OFFSET) | 3) +#define BFA_LOG_WDRV_IOC_STOP_ERROR \ + (((u32) BFA_LOG_WDRV_ID << BFA_LOG_MODID_OFFSET) | 4) +#define BFA_LOG_WDRV_INSUFFICIENT_RESOURCES \ + (((u32) BFA_LOG_WDRV_ID << BFA_LOG_MODID_OFFSET) | 5) +#define BFA_LOG_WDRV_BASE_ADDRESS_MAP_ERROR \ + (((u32) BFA_LOG_WDRV_ID << BFA_LOG_MODID_OFFSET) | 6) +#endif diff --git a/drivers/scsi/bfa/include/protocol/ct.h b/drivers/scsi/bfa/include/protocol/ct.h new file mode 100644 index 000000000000..c59d6630b070 --- /dev/null +++ b/drivers/scsi/bfa/include/protocol/ct.h @@ -0,0 +1,492 @@ +/* + * Copyright (c) 2005-2009 Brocade Communications Systems, Inc. + * All rights reserved + * www.brocade.com + * + * Linux driver for Brocade Fibre Channel Host Bus Adapter. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (GPL) Version 2 as + * published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#ifndef __CT_H__ +#define __CT_H__ + +#include <protocol/types.h> + +#pragma pack(1) + +struct ct_hdr_s{ + u32 rev_id:8; /* Revision of the CT */ + u32 in_id:24; /* Initiator Id */ + u32 gs_type:8; /* Generic service Type */ + u32 gs_sub_type:8; /* Generic service sub type */ + u32 options:8; /* options */ + u32 rsvrd:8; /* reserved */ + u32 cmd_rsp_code:16;/* ct command/response code */ + u32 max_res_size:16;/* maximum/residual size */ + u32 frag_id:8; /* fragment ID */ + u32 reason_code:8; /* reason code */ + u32 exp_code:8; /* explanation code */ + u32 vendor_unq:8; /* vendor unique */ +}; + +/* + * defines for the Revision + */ +enum { + CT_GS3_REVISION = 0x01, +}; + +/* + * defines for gs_type + */ +enum { + CT_GSTYPE_KEYSERVICE = 0xF7, + CT_GSTYPE_ALIASSERVICE = 0xF8, + CT_GSTYPE_MGMTSERVICE = 0xFA, + CT_GSTYPE_TIMESERVICE = 0xFB, + CT_GSTYPE_DIRSERVICE = 0xFC, +}; + +/* + * defines for gs_sub_type for gs type directory service + */ +enum { + CT_GSSUBTYPE_NAMESERVER = 0x02, +}; + +/* + * defines for gs_sub_type for gs type management service + */ +enum { + CT_GSSUBTYPE_CFGSERVER = 0x01, + CT_GSSUBTYPE_UNZONED_NS = 0x02, + CT_GSSUBTYPE_ZONESERVER = 0x03, + CT_GSSUBTYPE_LOCKSERVER = 0x04, + CT_GSSUBTYPE_HBA_MGMTSERVER = 0x10, /* for FDMI */ +}; + +/* + * defines for CT response code field + */ +enum { + CT_RSP_REJECT = 0x8001, + CT_RSP_ACCEPT = 0x8002, +}; + +/* + * defintions for CT reason code + */ +enum { + CT_RSN_INV_CMD = 0x01, + CT_RSN_INV_VER = 0x02, + CT_RSN_LOGIC_ERR = 0x03, + CT_RSN_INV_SIZE = 0x04, + CT_RSN_LOGICAL_BUSY = 0x05, + CT_RSN_PROTO_ERR = 0x07, + CT_RSN_UNABLE_TO_PERF = 0x09, + CT_RSN_NOT_SUPP = 0x0B, + CT_RSN_SERVER_NOT_AVBL = 0x0D, + CT_RSN_SESSION_COULD_NOT_BE_ESTBD = 0x0E, + CT_RSN_VENDOR_SPECIFIC = 0xFF, + +}; + +/* + * definitions for explanations code for Name server + */ +enum { + CT_NS_EXP_NOADDITIONAL = 0x00, + CT_NS_EXP_ID_NOT_REG = 0x01, + CT_NS_EXP_PN_NOT_REG = 0x02, + CT_NS_EXP_NN_NOT_REG = 0x03, + CT_NS_EXP_CS_NOT_REG = 0x04, + CT_NS_EXP_IPN_NOT_REG = 0x05, + CT_NS_EXP_IPA_NOT_REG = 0x06, + CT_NS_EXP_FT_NOT_REG = 0x07, + CT_NS_EXP_SPN_NOT_REG = 0x08, + CT_NS_EXP_SNN_NOT_REG = 0x09, + CT_NS_EXP_PT_NOT_REG = 0x0A, + CT_NS_EXP_IPP_NOT_REG = 0x0B, + CT_NS_EXP_FPN_NOT_REG = 0x0C, + CT_NS_EXP_HA_NOT_REG = 0x0D, + CT_NS_EXP_FD_NOT_REG = 0x0E, + CT_NS_EXP_FF_NOT_REG = 0x0F, + CT_NS_EXP_ACCESSDENIED = 0x10, + CT_NS_EXP_UNACCEPTABLE_ID = 0x11, + CT_NS_EXP_DATABASEEMPTY = 0x12, + CT_NS_EXP_NOT_REG_IN_SCOPE = 0x13, + CT_NS_EXP_DOM_ID_NOT_PRESENT = 0x14, + CT_NS_EXP_PORT_NUM_NOT_PRESENT = 0x15, + CT_NS_EXP_NO_DEVICE_ATTACHED = 0x16 +}; + +/* + * defintions for the explanation code for all servers + */ +enum { + CT_EXP_AUTH_EXCEPTION = 0xF1, + CT_EXP_DB_FULL = 0xF2, + CT_EXP_DB_EMPTY = 0xF3, + CT_EXP_PROCESSING_REQ = 0xF4, + CT_EXP_UNABLE_TO_VERIFY_CONN = 0xF5, + CT_EXP_DEVICES_NOT_IN_CMN_ZONE = 0xF6 +}; + +/* + * Command codes for Name server + */ +enum { + GS_GID_PN = 0x0121, /* Get Id on port name */ + GS_GPN_ID = 0x0112, /* Get port name on ID */ + GS_GNN_ID = 0x0113, /* Get node name on ID */ + GS_GID_FT = 0x0171, /* Get Id on FC4 type */ + GS_GSPN_ID = 0x0118, /* Get symbolic PN on ID */ + GS_RFT_ID = 0x0217, /* Register fc4type on ID */ + GS_RSPN_ID = 0x0218, /* Register symbolic PN on ID */ + GS_RPN_ID = 0x0212, /* Register port name */ + GS_RNN_ID = 0x0213, /* Register node name */ + GS_RCS_ID = 0x0214, /* Register class of service */ + GS_RPT_ID = 0x021A, /* Register port type */ + GS_GA_NXT = 0x0100, /* Get all next */ + GS_RFF_ID = 0x021F, /* Register FC4 Feature */ +}; + +struct fcgs_id_req_s{ + u32 rsvd:8; + u32 dap:24; /* port identifier */ +}; +#define fcgs_gpnid_req_t struct fcgs_id_req_s +#define fcgs_gnnid_req_t struct fcgs_id_req_s +#define fcgs_gspnid_req_t struct fcgs_id_req_s + +struct fcgs_gidpn_req_s{ + wwn_t port_name; /* port wwn */ +}; + +struct fcgs_gidpn_resp_s{ + u32 rsvd:8; + u32 dap:24; /* port identifier */ +}; + +/** + * RFT_ID + */ +struct fcgs_rftid_req_s { + u32 rsvd:8; + u32 dap:24; /* port identifier */ + u32 fc4_type[8]; /* fc4 types */ +}; + +/** + * RFF_ID : Register FC4 features. + */ + +#define FC_GS_FCP_FC4_FEATURE_INITIATOR 0x02 +#define FC_GS_FCP_FC4_FEATURE_TARGET 0x01 + +struct fcgs_rffid_req_s{ + u32 rsvd :8; + u32 dap :24; /* port identifier */ + u32 rsvd1 :16; + u32 fc4ftr_bits :8; /* fc4 feature bits */ + u32 fc4_type :8; /* corresponding FC4 Type */ +}; + +/** + * GID_FT Request + */ +struct fcgs_gidft_req_s{ + u8 reserved; + u8 domain_id; /* domain, 0 - all fabric */ + u8 area_id; /* area, 0 - whole domain */ + u8 fc4_type; /* FC_TYPE_FCP for SCSI devices */ +}; /* GID_FT Request */ + +/** + * GID_FT Response + */ +struct fcgs_gidft_resp_s { + u8 last:1; /* last port identifier flag */ + u8 reserved:7; + u32 pid:24; /* port identifier */ +}; /* GID_FT Response */ + +/** + * RSPN_ID + */ +struct fcgs_rspnid_req_s{ + u32 rsvd:8; + u32 dap:24; /* port identifier */ + u8 spn_len; /* symbolic port name length */ + u8 spn[256]; /* symbolic port name */ +}; + +/** + * RPN_ID + */ +struct fcgs_rpnid_req_s{ + u32 rsvd:8; + u32 port_id:24; + wwn_t port_name; +}; + +/** + * RNN_ID + */ +struct fcgs_rnnid_req_s{ + u32 rsvd:8; + u32 port_id:24; + wwn_t node_name; +}; + +/** + * RCS_ID + */ +struct fcgs_rcsid_req_s{ + u32 rsvd:8; + u32 port_id:24; + u32 cos; +}; + +/** + * RPT_ID + */ +struct fcgs_rptid_req_s{ + u32 rsvd:8; + u32 port_id:24; + u32 port_type:8; + u32 rsvd1:24; +}; + +/** + * GA_NXT Request + */ +struct fcgs_ganxt_req_s{ + u32 rsvd:8; + u32 port_id:24; +}; + +/** + * GA_NXT Response + */ +struct fcgs_ganxt_rsp_s{ + u32 port_type:8; /* Port Type */ + u32 port_id:24; /* Port Identifier */ + wwn_t port_name; /* Port Name */ + u8 spn_len; /* Length of Symbolic Port Name */ + char spn[255]; /* Symbolic Port Name */ + wwn_t node_name; /* Node Name */ + u8 snn_len; /* Length of Symbolic Node Name */ + char snn[255]; /* Symbolic Node Name */ + u8 ipa[8]; /* Initial Process Associator */ + u8 ip[16]; /* IP Address */ + u32 cos; /* Class of Service */ + u32 fc4types[8]; /* FC-4 TYPEs */ + wwn_t fabric_port_name; + /* Fabric Port Name */ + u32 rsvd:8; /* Reserved */ + u32 hard_addr:24; /* Hard Address */ +}; + +/* + * Fabric Config Server + */ + +/* + * Command codes for Fabric Configuration Server + */ +enum { + GS_FC_GFN_CMD = 0x0114, /* GS FC Get Fabric Name */ + GS_FC_GMAL_CMD = 0x0116, /* GS FC GMAL */ + GS_FC_TRACE_CMD = 0x0400, /* GS FC Trace Route */ + GS_FC_PING_CMD = 0x0401, /* GS FC Ping */ +}; + +/* + * Source or Destination Port Tags. + */ +enum { + GS_FTRACE_TAG_NPORT_ID = 1, + GS_FTRACE_TAG_NPORT_NAME = 2, +}; + +/* +* Port Value : Could be a Port id or wwn + */ +union fcgs_port_val_u{ + u32 nport_id; + wwn_t nport_wwn; +}; + +#define GS_FTRACE_MAX_HOP_COUNT 20 +#define GS_FTRACE_REVISION 1 + +/* + * Ftrace Related Structures. + */ + +/* + * STR (Switch Trace) Reject Reason Codes. From FC-SW. + */ +enum { + GS_FTRACE_STR_CMD_COMPLETED_SUCC = 0, + GS_FTRACE_STR_CMD_NOT_SUPP_IN_NEXT_SWITCH, + GS_FTRACE_STR_NO_RESP_FROM_NEXT_SWITCH, + GS_FTRACE_STR_MAX_HOP_CNT_REACHED, + GS_FTRACE_STR_SRC_PORT_NOT_FOUND, + GS_FTRACE_STR_DST_PORT_NOT_FOUND, + GS_FTRACE_STR_DEVICES_NOT_IN_COMMON_ZONE, + GS_FTRACE_STR_NO_ROUTE_BW_PORTS, + GS_FTRACE_STR_NO_ADDL_EXPLN, + GS_FTRACE_STR_FABRIC_BUSY, + GS_FTRACE_STR_FABRIC_BUILD_IN_PROGRESS, + GS_FTRACE_STR_VENDOR_SPECIFIC_ERR_START = 0xf0, + GS_FTRACE_STR_VENDOR_SPECIFIC_ERR_END = 0xff, +}; + +/* + * Ftrace Request + */ +struct fcgs_ftrace_req_s{ + u32 revision; + u16 src_port_tag; /* Source Port tag */ + u16 src_port_len; /* Source Port len */ + union fcgs_port_val_u src_port_val; /* Source Port value */ + u16 dst_port_tag; /* Destination Port tag */ + u16 dst_port_len; /* Destination Port len */ + union fcgs_port_val_u dst_port_val; /* Destination Port value */ + u32 token; + u8 vendor_id[8]; /* T10 Vendor Identifier */ + u8 vendor_info[8]; /* Vendor specific Info */ + u32 max_hop_cnt; /* Max Hop Count */ +}; + +/* + * Path info structure + */ +struct fcgs_ftrace_path_info_s{ + wwn_t switch_name; /* Switch WWN */ + u32 domain_id; + wwn_t ingress_port_name; /* Ingress ports wwn */ + u32 ingress_phys_port_num; /* Ingress ports physical port + * number + */ + wwn_t egress_port_name; /* Ingress ports wwn */ + u32 egress_phys_port_num; /* Ingress ports physical port + * number + */ +}; + +/* + * Ftrace Acc Response + */ +struct fcgs_ftrace_resp_s{ + u32 revision; + u32 token; + u8 vendor_id[8]; /* T10 Vendor Identifier */ + u8 vendor_info[8]; /* Vendor specific Info */ + u32 str_rej_reason_code; /* STR Reject Reason Code */ + u32 num_path_info_entries; /* No. of path info entries */ + /* + * path info entry/entries. + */ + struct fcgs_ftrace_path_info_s path_info[1]; + +}; + +/* +* Fabric Config Server : FCPing + */ + +/* + * FC Ping Request + */ +struct fcgs_fcping_req_s{ + u32 revision; + u16 port_tag; + u16 port_len; /* Port len */ + union fcgs_port_val_u port_val; /* Port value */ + u32 token; +}; + +/* + * FC Ping Response + */ +struct fcgs_fcping_resp_s{ + u32 token; +}; + +/* + * Command codes for zone server query. + */ +enum { + ZS_GZME = 0x0124, /* Get zone member extended */ +}; + +/* + * ZS GZME request + */ +#define ZS_GZME_ZNAMELEN 32 +struct zs_gzme_req_s{ + u8 znamelen; + u8 rsvd[3]; + u8 zname[ZS_GZME_ZNAMELEN]; +}; + +enum zs_mbr_type{ + ZS_MBR_TYPE_PWWN = 1, + ZS_MBR_TYPE_DOMPORT = 2, + ZS_MBR_TYPE_PORTID = 3, + ZS_MBR_TYPE_NWWN = 4, +}; + +struct zs_mbr_wwn_s{ + u8 mbr_type; + u8 rsvd[3]; + wwn_t wwn; +}; + +struct zs_query_resp_s{ + u32 nmbrs; /* number of zone members */ + struct zs_mbr_wwn_s mbr[1]; +}; + +/* + * GMAL Command ( Get ( interconnect Element) Management Address List) + * To retrieve the IP Address of a Switch. + */ + +#define CT_GMAL_RESP_PREFIX_TELNET "telnet://" +#define CT_GMAL_RESP_PREFIX_HTTP "http://" + +/* GMAL/GFN request */ +struct fcgs_req_s { + wwn_t wwn; /* PWWN/NWWN */ +}; + +#define fcgs_gmal_req_t struct fcgs_req_s +#define fcgs_gfn_req_t struct fcgs_req_s + +/* Accept Response to GMAL */ +struct fcgs_gmal_resp_s { + u32 ms_len; /* Num of entries */ + u8 ms_ma[256]; +}; + +struct fc_gmal_entry_s { + u8 len; + u8 prefix[7]; /* like "http://" */ + u8 ip_addr[248]; +}; + +#pragma pack() + +#endif diff --git a/drivers/scsi/bfa/include/protocol/fc.h b/drivers/scsi/bfa/include/protocol/fc.h new file mode 100644 index 000000000000..3e39ba58cfb5 --- /dev/null +++ b/drivers/scsi/bfa/include/protocol/fc.h @@ -0,0 +1,1105 @@ +/* + * Copyright (c) 2005-2009 Brocade Communications Systems, Inc. + * All rights reserved + * www.brocade.com + * + * Linux driver for Brocade Fibre Channel Host Bus Adapter. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (GPL) Version 2 as + * published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#ifndef __FC_H__ +#define __FC_H__ + +#include <protocol/types.h> + +#pragma pack(1) + +/* + * Fibre Channel Header Structure (FCHS) definition + */ +struct fchs_s { +#ifdef __BIGENDIAN + u32 routing:4; /* routing bits */ + u32 cat_info:4; /* category info */ +#else + u32 cat_info:4; /* category info */ + u32 routing:4; /* routing bits */ +#endif + u32 d_id:24; /* destination identifier */ + + u32 cs_ctl:8; /* class specific control */ + u32 s_id:24; /* source identifier */ + + u32 type:8; /* data structure type */ + u32 f_ctl:24; /* initial frame control */ + + u8 seq_id; /* sequence identifier */ + u8 df_ctl; /* data field control */ + u16 seq_cnt; /* sequence count */ + + u16 ox_id; /* originator exchange ID */ + u16 rx_id; /* responder exchange ID */ + + u32 ro; /* relative offset */ +}; +/* + * Fibre Channel BB_E Header Structure + */ +struct fcbbehs_s { + u16 ver_rsvd; + u32 rsvd[2]; + u32 rsvd__sof; +}; + +#define FC_SEQ_ID_MAX 256 + +/* + * routing bit definitions + */ +enum { + FC_RTG_FC4_DEV_DATA = 0x0, /* FC-4 Device Data */ + FC_RTG_EXT_LINK = 0x2, /* Extended Link Data */ + FC_RTG_FC4_LINK_DATA = 0x3, /* FC-4 Link Data */ + FC_RTG_VIDEO_DATA = 0x4, /* Video Data */ + FC_RTG_EXT_HDR = 0x5, /* VFT, IFR or Encapsuled */ + FC_RTG_BASIC_LINK = 0x8, /* Basic Link data */ + FC_RTG_LINK_CTRL = 0xC, /* Link Control */ +}; + +/* + * information category for extended link data and FC-4 Link Data + */ +enum { + FC_CAT_LD_REQUEST = 0x2, /* Request */ + FC_CAT_LD_REPLY = 0x3, /* Reply */ + FC_CAT_LD_DIAG = 0xF, /* for DIAG use only */ +}; + +/* + * information category for extended headers (VFT, IFR or encapsulation) + */ +enum { + FC_CAT_VFT_HDR = 0x0, /* Virtual fabric tagging header */ + FC_CAT_IFR_HDR = 0x1, /* Inter-Fabric routing header */ + FC_CAT_ENC_HDR = 0x2, /* Encapsulation header */ +}; + +/* + * information category for FC-4 device data + */ +enum { + FC_CAT_UNCATEG_INFO = 0x0, /* Uncategorized information */ + FC_CAT_SOLICIT_DATA = 0x1, /* Solicited Data */ + FC_CAT_UNSOLICIT_CTRL = 0x2, /* Unsolicited Control */ + FC_CAT_SOLICIT_CTRL = 0x3, /* Solicited Control */ + FC_CAT_UNSOLICIT_DATA = 0x4, /* Unsolicited Data */ + FC_CAT_DATA_DESC = 0x5, /* Data Descriptor */ + FC_CAT_UNSOLICIT_CMD = 0x6, /* Unsolicited Command */ + FC_CAT_CMD_STATUS = 0x7, /* Command Status */ +}; + +/* + * information category for Link Control + */ +enum { + FC_CAT_ACK_1 = 0x00, + FC_CAT_ACK_0_N = 0x01, + FC_CAT_P_RJT = 0x02, + FC_CAT_F_RJT = 0x03, + FC_CAT_P_BSY = 0x04, + FC_CAT_F_BSY_DATA = 0x05, + FC_CAT_F_BSY_LINK_CTL = 0x06, + FC_CAT_F_LCR = 0x07, + FC_CAT_NTY = 0x08, + FC_CAT_END = 0x09, +}; + +/* + * Type Field Definitions. FC-PH Section 18.5 pg. 165 + */ +enum { + FC_TYPE_BLS = 0x0, /* Basic Link Service */ + FC_TYPE_ELS = 0x1, /* Extended Link Service */ + FC_TYPE_IP = 0x5, /* IP */ + FC_TYPE_FCP = 0x8, /* SCSI-FCP */ + FC_TYPE_GPP = 0x9, /* SCSI_GPP */ + FC_TYPE_SERVICES = 0x20, /* Fibre Channel Services */ + FC_TYPE_FC_FSS = 0x22, /* Fabric Switch Services */ + FC_TYPE_FC_AL = 0x23, /* FC-AL */ + FC_TYPE_FC_SNMP = 0x24, /* FC-SNMP */ + FC_TYPE_MAX = 256, /* 256 FC-4 types */ +}; + +struct fc_fc4types_s{ + u8 bits[FC_TYPE_MAX / 8]; +}; + +/* + * Frame Control Definitions. FC-PH Table-45. pg. 168 + */ +enum { + FCTL_EC_ORIG = 0x000000, /* exchange originator */ + FCTL_EC_RESP = 0x800000, /* exchange responder */ + FCTL_SEQ_INI = 0x000000, /* sequence initiator */ + FCTL_SEQ_REC = 0x400000, /* sequence recipient */ + FCTL_FS_EXCH = 0x200000, /* first sequence of xchg */ + FCTL_LS_EXCH = 0x100000, /* last sequence of xchg */ + FCTL_END_SEQ = 0x080000, /* last frame of sequence */ + FCTL_SI_XFER = 0x010000, /* seq initiative transfer */ + FCTL_RO_PRESENT = 0x000008, /* relative offset present */ + FCTL_FILLBYTE_MASK = 0x000003 /* , fill byte mask */ +}; + +/* + * Fabric Well Known Addresses + */ +enum { + FC_MIN_WELL_KNOWN_ADDR = 0xFFFFF0, + FC_DOMAIN_CONTROLLER_MASK = 0xFFFC00, + FC_ALIAS_SERVER = 0xFFFFF8, + FC_MGMT_SERVER = 0xFFFFFA, + FC_TIME_SERVER = 0xFFFFFB, + FC_NAME_SERVER = 0xFFFFFC, + FC_FABRIC_CONTROLLER = 0xFFFFFD, + FC_FABRIC_PORT = 0xFFFFFE, + FC_BROADCAST_SERVER = 0xFFFFFF +}; + +/* + * domain/area/port defines + */ +#define FC_DOMAIN_MASK 0xFF0000 +#define FC_DOMAIN_SHIFT 16 +#define FC_AREA_MASK 0x00FF00 +#define FC_AREA_SHIFT 8 +#define FC_PORT_MASK 0x0000FF +#define FC_PORT_SHIFT 0 + +#define FC_GET_DOMAIN(p) (((p) & FC_DOMAIN_MASK) >> FC_DOMAIN_SHIFT) +#define FC_GET_AREA(p) (((p) & FC_AREA_MASK) >> FC_AREA_SHIFT) +#define FC_GET_PORT(p) (((p) & FC_PORT_MASK) >> FC_PORT_SHIFT) + +#define FC_DOMAIN_CTRLR(p) (FC_DOMAIN_CONTROLLER_MASK | (FC_GET_DOMAIN(p))) + +enum { + FC_RXID_ANY = 0xFFFFU, +}; + +/* + * generic ELS command + */ +struct fc_els_cmd_s{ + u32 els_code:8; /* ELS Command Code */ + u32 reserved:24; +}; + +/* + * ELS Command Codes. FC-PH Table-75. pg. 223 + */ +enum { + FC_ELS_LS_RJT = 0x1, /* Link Service Reject. */ + FC_ELS_ACC = 0x02, /* Accept */ + FC_ELS_PLOGI = 0x03, /* N_Port Login. */ + FC_ELS_FLOGI = 0x04, /* F_Port Login. */ + FC_ELS_LOGO = 0x05, /* Logout. */ + FC_ELS_ABTX = 0x06, /* Abort Exchange */ + FC_ELS_RES = 0x08, /* Read Exchange status */ + FC_ELS_RSS = 0x09, /* Read sequence status block */ + FC_ELS_RSI = 0x0A, /* Request Sequence Initiative */ + FC_ELS_ESTC = 0x0C, /* Estimate Credit. */ + FC_ELS_RTV = 0x0E, /* Read Timeout Value. */ + FC_ELS_RLS = 0x0F, /* Read Link Status. */ + FC_ELS_ECHO = 0x10, /* Echo */ + FC_ELS_TEST = 0x11, /* Test */ + FC_ELS_RRQ = 0x12, /* Reinstate Recovery Qualifier. */ + FC_ELS_REC = 0x13, /* Add this for TAPE support in FCR */ + FC_ELS_PRLI = 0x20, /* Process Login */ + FC_ELS_PRLO = 0x21, /* Process Logout. */ + FC_ELS_SCN = 0x22, /* State Change Notification. */ + FC_ELS_TPRLO = 0x24, /* Third Party Process Logout. */ + FC_ELS_PDISC = 0x50, /* Discover N_Port Parameters. */ + FC_ELS_FDISC = 0x51, /* Discover F_Port Parameters. */ + FC_ELS_ADISC = 0x52, /* Discover Address. */ + FC_ELS_FAN = 0x60, /* Fabric Address Notification */ + FC_ELS_RSCN = 0x61, /* Reg State Change Notification */ + FC_ELS_SCR = 0x62, /* State Change Registration. */ + FC_ELS_RTIN = 0x77, /* Mangement server request */ + FC_ELS_RNID = 0x78, /* Mangement server request */ + FC_ELS_RLIR = 0x79, /* Registered Link Incident Record */ + + FC_ELS_RPSC = 0x7D, /* Report Port Speed Capabilities */ + FC_ELS_QSA = 0x7E, /* Query Security Attributes. Ref FC-SP */ + FC_ELS_E2E_LBEACON = 0x81, + /* End-to-End Link Beacon */ + FC_ELS_AUTH = 0x90, /* Authentication. Ref FC-SP */ + FC_ELS_RFCN = 0x97, /* Request Fabric Change Notification. Ref + *FC-SP */ + +}; + +/* + * Version numbers for FC-PH standards, + * used in login to indicate what port + * supports. See FC-PH-X table 158. + */ +enum { + FC_PH_VER_4_3 = 0x09, + FC_PH_VER_PH_3 = 0x20, +}; + +/* + * PDU size defines + */ +enum { + FC_MIN_PDUSZ = 512, + FC_MAX_PDUSZ = 2112, +}; + +/* + * N_Port PLOGI Common Service Parameters. + * FC-PH-x. Figure-76. pg. 308. + */ +struct fc_plogi_csp_s{ + u8 verhi; /* FC-PH high version */ + u8 verlo; /* FC-PH low version */ + u16 bbcred; /* BB_Credit */ + +#ifdef __BIGENDIAN + u8 ciro:1, /* continuously increasing RO */ + rro:1, /* random relative offset */ + npiv_supp:1, /* NPIV supported */ + port_type:1, /* N_Port/F_port */ + altbbcred:1, /* alternate BB_Credit */ + resolution:1, /* ms/ns ED_TOV resolution */ + vvl_info:1, /* VVL Info included */ + reserved1:1; + + u8 hg_supp:1, + query_dbc:1, + security:1, + sync_cap:1, + r_t_tov:1, + dh_dup_supp:1, + cisc:1, /* continuously increasing seq count */ + payload:1; +#else + u8 reserved2:2, + resolution:1, /* ms/ns ED_TOV resolution */ + altbbcred:1, /* alternate BB_Credit */ + port_type:1, /* N_Port/F_port */ + npiv_supp:1, /* NPIV supported */ + rro:1, /* random relative offset */ + ciro:1; /* continuously increasing RO */ + + u8 payload:1, + cisc:1, /* continuously increasing seq count */ + dh_dup_supp:1, + r_t_tov:1, + sync_cap:1, + security:1, + query_dbc:1, + hg_supp:1; +#endif + + u16 rxsz; /* recieve data_field size */ + + u16 conseq; + u16 ro_bitmap; + + u32 e_d_tov; +}; + +/* + * N_Port PLOGI Class Specific Parameters. + * FC-PH-x. Figure 78. pg. 318. + */ +struct fc_plogi_clp_s{ +#ifdef __BIGENDIAN + u32 class_valid:1; + u32 intermix:1; /* class intermix supported if set =1. + * valid only for class1. Reserved for + * class2 & class3 + */ + u32 reserved1:2; + u32 sequential:1; + u32 reserved2:3; +#else + u32 reserved2:3; + u32 sequential:1; + u32 reserved1:2; + u32 intermix:1; /* class intermix supported if set =1. + * valid only for class1. Reserved for + * class2 & class3 + */ + u32 class_valid:1; +#endif + + u32 reserved3:24; + + u32 reserved4:16; + u32 rxsz:16; /* Receive data_field size */ + + u32 reserved5:8; + u32 conseq:8; + u32 e2e_credit:16; /* end to end credit */ + + u32 reserved7:8; + u32 ospx:8; + u32 reserved8:16; +}; + +#define FLOGI_VVL_BRCD 0x42524344 /* ASCII value for each character in + * string "BRCD" */ + +/* + * PLOGI els command and reply payload + */ +struct fc_logi_s{ + struct fc_els_cmd_s els_cmd; /* ELS command code */ + struct fc_plogi_csp_s csp; /* common service params */ + wwn_t port_name; + wwn_t node_name; + struct fc_plogi_clp_s class1; /* class 1 service parameters */ + struct fc_plogi_clp_s class2; /* class 2 service parameters */ + struct fc_plogi_clp_s class3; /* class 3 service parameters */ + struct fc_plogi_clp_s class4; /* class 4 service parameters */ + u8 vvl[16]; /* vendor version level */ +}; + +/* + * LOGO els command payload + */ +struct fc_logo_s{ + struct fc_els_cmd_s els_cmd; /* ELS command code */ + u32 res1:8; + u32 nport_id:24; /* N_Port identifier of source */ + wwn_t orig_port_name; /* Port name of the LOGO originator */ +}; + +/* + * ADISC els command payload + */ +struct fc_adisc_s { + struct fc_els_cmd_s els_cmd; /* ELS command code */ + u32 res1:8; + u32 orig_HA:24; /* originator hard address */ + wwn_t orig_port_name; /* originator port name */ + wwn_t orig_node_name; /* originator node name */ + u32 res2:8; + u32 nport_id:24; /* originator NPortID */ +}; + +/* + * Exchange status block + */ +struct fc_exch_status_blk_s{ + u32 oxid:16; + u32 rxid:16; + u32 res1:8; + u32 orig_np:24; /* originator NPortID */ + u32 res2:8; + u32 resp_np:24; /* responder NPortID */ + u32 es_bits; + u32 res3; + /* + * un modified section of the fields + */ +}; + +/* + * RES els command payload + */ +struct fc_res_s { + struct fc_els_cmd_s els_cmd; /* ELS command code */ + u32 res1:8; + u32 nport_id:24; /* N_Port identifier of source */ + u32 oxid:16; + u32 rxid:16; + u8 assoc_hdr[32]; +}; + +/* + * RES els accept payload + */ +struct fc_res_acc_s{ + struct fc_els_cmd_s els_cmd; /* ELS command code */ + struct fc_exch_status_blk_s fc_exch_blk; /* Exchange status block */ +}; + +/* + * REC els command payload + */ +struct fc_rec_s { + struct fc_els_cmd_s els_cmd; /* ELS command code */ + u32 res1:8; + u32 nport_id:24; /* N_Port identifier of source */ + u32 oxid:16; + u32 rxid:16; +}; + +#define FC_REC_ESB_OWN_RSP 0x80000000 /* responder owns */ +#define FC_REC_ESB_SI 0x40000000 /* SI is owned */ +#define FC_REC_ESB_COMP 0x20000000 /* exchange is complete */ +#define FC_REC_ESB_ENDCOND_ABN 0x10000000 /* abnormal ending */ +#define FC_REC_ESB_RQACT 0x04000000 /* recovery qual active */ +#define FC_REC_ESB_ERRP_MSK 0x03000000 +#define FC_REC_ESB_OXID_INV 0x00800000 /* invalid OXID */ +#define FC_REC_ESB_RXID_INV 0x00400000 /* invalid RXID */ +#define FC_REC_ESB_PRIO_INUSE 0x00200000 + +/* + * REC els accept payload + */ +struct fc_rec_acc_s { + struct fc_els_cmd_s els_cmd; /* ELS command code */ + u32 oxid:16; + u32 rxid:16; + u32 res1:8; + u32 orig_id:24; /* N_Port id of exchange originator */ + u32 res2:8; + u32 resp_id:24; /* N_Port id of exchange responder */ + u32 count; /* data transfer count */ + u32 e_stat; /* exchange status */ +}; + +/* + * RSI els payload + */ +struct fc_rsi_s { + struct fc_els_cmd_s els_cmd; + u32 res1:8; + u32 orig_sid:24; + u32 oxid:16; + u32 rxid:16; +}; + +/* + * structure for PRLI paramater pages, both request & response + * see FC-PH-X table 113 & 115 for explanation also FCP table 8 + */ +struct fc_prli_params_s{ + u32 reserved: 16; +#ifdef __BIGENDIAN + u32 reserved1: 5; + u32 rec_support : 1; + u32 task_retry_id : 1; + u32 retry : 1; + + u32 confirm : 1; + u32 doverlay:1; + u32 initiator:1; + u32 target:1; + u32 cdmix:1; + u32 drmix:1; + u32 rxrdisab:1; + u32 wxrdisab:1; +#else + u32 retry : 1; + u32 task_retry_id : 1; + u32 rec_support : 1; + u32 reserved1: 5; + + u32 wxrdisab:1; + u32 rxrdisab:1; + u32 drmix:1; + u32 cdmix:1; + u32 target:1; + u32 initiator:1; + u32 doverlay:1; + u32 confirm : 1; +#endif +}; + +/* + * valid values for rspcode in PRLI ACC payload + */ +enum { + FC_PRLI_ACC_XQTD = 0x1, /* request executed */ + FC_PRLI_ACC_PREDEF_IMG = 0x5, /* predefined image - no prli needed */ +}; + +struct fc_prli_params_page_s{ + u32 type:8; + u32 codext:8; +#ifdef __BIGENDIAN + u32 origprocasv:1; + u32 rsppav:1; + u32 imagepair:1; + u32 reserved1:1; + u32 rspcode:4; +#else + u32 rspcode:4; + u32 reserved1:1; + u32 imagepair:1; + u32 rsppav:1; + u32 origprocasv:1; +#endif + u32 reserved2:8; + + u32 origprocas; + u32 rspprocas; + struct fc_prli_params_s servparams; +}; + +/* + * PRLI request and accept payload, FC-PH-X tables 112 & 114 + */ +struct fc_prli_s{ + u32 command:8; + u32 pglen:8; + u32 pagebytes:16; + struct fc_prli_params_page_s parampage; +}; + +/* + * PRLO logout params page + */ +struct fc_prlo_params_page_s{ + u32 type:8; + u32 type_ext:8; +#ifdef __BIGENDIAN + u32 opa_valid:1; /* originator process associator + * valid + */ + u32 rpa_valid:1; /* responder process associator valid */ + u32 res1:14; +#else + u32 res1:14; + u32 rpa_valid:1; /* responder process associator valid */ + u32 opa_valid:1; /* originator process associator + * valid + */ +#endif + u32 orig_process_assc; + u32 resp_process_assc; + + u32 res2; +}; + +/* + * PRLO els command payload + */ +struct fc_prlo_s{ + u32 command:8; + u32 page_len:8; + u32 payload_len:16; + struct fc_prlo_params_page_s prlo_params[1]; +}; + +/* + * PRLO Logout response parameter page + */ +struct fc_prlo_acc_params_page_s{ + u32 type:8; + u32 type_ext:8; + +#ifdef __BIGENDIAN + u32 opa_valid:1; /* originator process associator + * valid + */ + u32 rpa_valid:1; /* responder process associator valid */ + u32 res1:14; +#else + u32 res1:14; + u32 rpa_valid:1; /* responder process associator valid */ + u32 opa_valid:1; /* originator process associator + * valid + */ +#endif + u32 orig_process_assc; + u32 resp_process_assc; + + u32 fc4type_csp; +}; + +/* + * PRLO els command ACC payload + */ +struct fc_prlo_acc_s{ + u32 command:8; + u32 page_len:8; + u32 payload_len:16; + struct fc_prlo_acc_params_page_s prlo_acc_params[1]; +}; + +/* + * SCR els command payload + */ +enum { + FC_SCR_REG_FUNC_FABRIC_DETECTED = 0x01, + FC_SCR_REG_FUNC_N_PORT_DETECTED = 0x02, + FC_SCR_REG_FUNC_FULL = 0x03, + FC_SCR_REG_FUNC_CLEAR_REG = 0xFF, +}; + +/* SCR VU registrations */ +enum { + FC_VU_SCR_REG_FUNC_FABRIC_NAME_CHANGE = 0x01 +}; + +struct fc_scr_s{ + u32 command:8; + u32 res:24; + u32 vu_reg_func:8; /* Vendor Unique Registrations */ + u32 res1:16; + u32 reg_func:8; +}; + +/* + * Information category for Basic link data + */ +enum { + FC_CAT_NOP = 0x0, + FC_CAT_ABTS = 0x1, + FC_CAT_RMC = 0x2, + FC_CAT_BA_ACC = 0x4, + FC_CAT_BA_RJT = 0x5, + FC_CAT_PRMT = 0x6, +}; + +/* + * LS_RJT els reply payload + */ +struct fc_ls_rjt_s { + struct fc_els_cmd_s els_cmd; /* ELS command code */ + u32 res1:8; + u32 reason_code:8; /* Reason code for reject */ + u32 reason_code_expl:8; /* Reason code explanation */ + u32 vendor_unique:8; /* Vendor specific */ +}; + +/* + * LS_RJT reason codes + */ +enum { + FC_LS_RJT_RSN_INV_CMD_CODE = 0x01, + FC_LS_RJT_RSN_LOGICAL_ERROR = 0x03, + FC_LS_RJT_RSN_LOGICAL_BUSY = 0x05, + FC_LS_RJT_RSN_PROTOCOL_ERROR = 0x07, + FC_LS_RJT_RSN_UNABLE_TO_PERF_CMD = 0x09, + FC_LS_RJT_RSN_CMD_NOT_SUPP = 0x0B, +}; + +/* + * LS_RJT reason code explanation + */ +enum { + FC_LS_RJT_EXP_NO_ADDL_INFO = 0x00, + FC_LS_RJT_EXP_SPARMS_ERR_OPTIONS = 0x01, + FC_LS_RJT_EXP_SPARMS_ERR_INI_CTL = 0x03, + FC_LS_RJT_EXP_SPARMS_ERR_REC_CTL = 0x05, + FC_LS_RJT_EXP_SPARMS_ERR_RXSZ = 0x07, + FC_LS_RJT_EXP_SPARMS_ERR_CONSEQ = 0x09, + FC_LS_RJT_EXP_SPARMS_ERR_CREDIT = 0x0B, + FC_LS_RJT_EXP_INV_PORT_NAME = 0x0D, + FC_LS_RJT_EXP_INV_NODE_FABRIC_NAME = 0x0E, + FC_LS_RJT_EXP_INV_CSP = 0x0F, + FC_LS_RJT_EXP_INV_ASSOC_HDR = 0x11, + FC_LS_RJT_EXP_ASSOC_HDR_REQD = 0x13, + FC_LS_RJT_EXP_INV_ORIG_S_ID = 0x15, + FC_LS_RJT_EXP_INV_OXID_RXID_COMB = 0x17, + FC_LS_RJT_EXP_CMD_ALREADY_IN_PROG = 0x19, + FC_LS_RJT_EXP_LOGIN_REQUIRED = 0x1E, + FC_LS_RJT_EXP_INVALID_NPORT_ID = 0x1F, + FC_LS_RJT_EXP_INSUFF_RES = 0x29, + FC_LS_RJT_EXP_CMD_NOT_SUPP = 0x2C, + FC_LS_RJT_EXP_INV_PAYLOAD_LEN = 0x2D, +}; + +/* + * RRQ els command payload + */ +struct fc_rrq_s{ + struct fc_els_cmd_s els_cmd; /* ELS command code */ + u32 res1:8; + u32 s_id:24; /* exchange originator S_ID */ + + u32 ox_id:16; /* originator exchange ID */ + u32 rx_id:16; /* responder exchange ID */ + + u32 res2[8]; /* optional association header */ +}; + +/* + * ABTS BA_ACC reply payload + */ +struct fc_ba_acc_s{ + u32 seq_id_valid:8; /* set to 0x00 for Abort Exchange */ + u32 seq_id:8; /* invalid for Abort Exchange */ + u32 res2:16; + u32 ox_id:16; /* OX_ID from ABTS frame */ + u32 rx_id:16; /* RX_ID from ABTS frame */ + u32 low_seq_cnt:16; /* set to 0x0000 for Abort Exchange */ + u32 high_seq_cnt:16;/* set to 0xFFFF for Abort Exchange */ +}; + +/* + * ABTS BA_RJT reject payload + */ +struct fc_ba_rjt_s{ + u32 res1:8; /* Reserved */ + u32 reason_code:8; /* reason code for reject */ + u32 reason_expl:8; /* reason code explanation */ + u32 vendor_unique:8;/* vendor unique reason code,set to 0 */ +}; + +/* + * TPRLO logout parameter page + */ +struct fc_tprlo_params_page_s{ + u32 type:8; + u32 type_ext:8; + +#ifdef __BIGENDIAN + u32 opa_valid:1; + u32 rpa_valid:1; + u32 tpo_nport_valid:1; + u32 global_process_logout:1; + u32 res1:12; +#else + u32 res1:12; + u32 global_process_logout:1; + u32 tpo_nport_valid:1; + u32 rpa_valid:1; + u32 opa_valid:1; +#endif + + u32 orig_process_assc; + u32 resp_process_assc; + + u32 res2:8; + u32 tpo_nport_id; +}; + +/* + * TPRLO ELS command payload + */ +struct fc_tprlo_s{ + u32 command:8; + u32 page_len:8; + u32 payload_len:16; + + struct fc_tprlo_params_page_s tprlo_params[1]; +}; + +enum fc_tprlo_type{ + FC_GLOBAL_LOGO = 1, + FC_TPR_LOGO +}; + +/* + * TPRLO els command ACC payload + */ +struct fc_tprlo_acc_s{ + u32 command:8; + u32 page_len:8; + u32 payload_len:16; + struct fc_prlo_acc_params_page_s tprlo_acc_params[1]; +}; + +/* + * RSCN els command req payload + */ +#define FC_RSCN_PGLEN 0x4 + +enum fc_rscn_format{ + FC_RSCN_FORMAT_PORTID = 0x0, + FC_RSCN_FORMAT_AREA = 0x1, + FC_RSCN_FORMAT_DOMAIN = 0x2, + FC_RSCN_FORMAT_FABRIC = 0x3, +}; + +struct fc_rscn_event_s{ + u32 format:2; + u32 qualifier:4; + u32 resvd:2; + u32 portid:24; +}; + +struct fc_rscn_pl_s{ + u8 command; + u8 pagelen; + u16 payldlen; + struct fc_rscn_event_s event[1]; +}; + +/* + * ECHO els command req payload + */ +struct fc_echo_s { + struct fc_els_cmd_s els_cmd; +}; + +/* + * RNID els command + */ + +#define RNID_NODEID_DATA_FORMAT_COMMON 0x00 +#define RNID_NODEID_DATA_FORMAT_FCP3 0x08 +#define RNID_NODEID_DATA_FORMAT_DISCOVERY 0xDF + +#define RNID_ASSOCIATED_TYPE_UNKNOWN 0x00000001 +#define RNID_ASSOCIATED_TYPE_OTHER 0x00000002 +#define RNID_ASSOCIATED_TYPE_HUB 0x00000003 +#define RNID_ASSOCIATED_TYPE_SWITCH 0x00000004 +#define RNID_ASSOCIATED_TYPE_GATEWAY 0x00000005 +#define RNID_ASSOCIATED_TYPE_STORAGE_DEVICE 0x00000009 +#define RNID_ASSOCIATED_TYPE_HOST 0x0000000A +#define RNID_ASSOCIATED_TYPE_STORAGE_SUBSYSTEM 0x0000000B +#define RNID_ASSOCIATED_TYPE_STORAGE_ACCESS_DEVICE 0x0000000E +#define RNID_ASSOCIATED_TYPE_NAS_SERVER 0x00000011 +#define RNID_ASSOCIATED_TYPE_BRIDGE 0x00000002 +#define RNID_ASSOCIATED_TYPE_VIRTUALIZATION_DEVICE 0x00000003 +#define RNID_ASSOCIATED_TYPE_MULTI_FUNCTION_DEVICE 0x000000FF + +/* + * RNID els command payload + */ +struct fc_rnid_cmd_s{ + struct fc_els_cmd_s els_cmd; + u32 node_id_data_format:8; + u32 reserved:24; +}; + +/* + * RNID els response payload + */ + +struct fc_rnid_common_id_data_s{ + wwn_t port_name; + wwn_t node_name; +}; + +struct fc_rnid_general_topology_data_s{ + u32 vendor_unique[4]; + u32 asso_type; + u32 phy_port_num; + u32 num_attached_nodes; + u32 node_mgmt:8; + u32 ip_version:8; + u32 udp_tcp_port_num:16; + u32 ip_address[4]; + u32 reserved:16; + u32 vendor_specific:16; +}; + +struct fc_rnid_acc_s{ + struct fc_els_cmd_s els_cmd; + u32 node_id_data_format:8; + u32 common_id_data_length:8; + u32 reserved:8; + u32 specific_id_data_length:8; + struct fc_rnid_common_id_data_s common_id_data; + struct fc_rnid_general_topology_data_s gen_topology_data; +}; + +#define RNID_ASSOCIATED_TYPE_UNKNOWN 0x00000001 +#define RNID_ASSOCIATED_TYPE_OTHER 0x00000002 +#define RNID_ASSOCIATED_TYPE_HUB 0x00000003 +#define RNID_ASSOCIATED_TYPE_SWITCH 0x00000004 +#define RNID_ASSOCIATED_TYPE_GATEWAY 0x00000005 +#define RNID_ASSOCIATED_TYPE_STORAGE_DEVICE 0x00000009 +#define RNID_ASSOCIATED_TYPE_HOST 0x0000000A +#define RNID_ASSOCIATED_TYPE_STORAGE_SUBSYSTEM 0x0000000B +#define RNID_ASSOCIATED_TYPE_STORAGE_ACCESS_DEVICE 0x0000000E +#define RNID_ASSOCIATED_TYPE_NAS_SERVER 0x00000011 +#define RNID_ASSOCIATED_TYPE_BRIDGE 0x00000002 +#define RNID_ASSOCIATED_TYPE_VIRTUALIZATION_DEVICE 0x00000003 +#define RNID_ASSOCIATED_TYPE_MULTI_FUNCTION_DEVICE 0x000000FF + +enum fc_rpsc_speed_cap{ + RPSC_SPEED_CAP_1G = 0x8000, + RPSC_SPEED_CAP_2G = 0x4000, + RPSC_SPEED_CAP_4G = 0x2000, + RPSC_SPEED_CAP_10G = 0x1000, + RPSC_SPEED_CAP_8G = 0x0800, + RPSC_SPEED_CAP_16G = 0x0400, + + RPSC_SPEED_CAP_UNKNOWN = 0x0001, +}; + +enum fc_rpsc_op_speed_s{ + RPSC_OP_SPEED_1G = 0x8000, + RPSC_OP_SPEED_2G = 0x4000, + RPSC_OP_SPEED_4G = 0x2000, + RPSC_OP_SPEED_10G = 0x1000, + RPSC_OP_SPEED_8G = 0x0800, + RPSC_OP_SPEED_16G = 0x0400, + + RPSC_OP_SPEED_NOT_EST = 0x0001, /*! speed not established */ +}; + +struct fc_rpsc_speed_info_s{ + u16 port_speed_cap; /*! see fc_rpsc_speed_cap_t */ + u16 port_op_speed; /*! see fc_rpsc_op_speed_t */ +}; + +enum link_e2e_beacon_subcmd{ + LINK_E2E_BEACON_ON = 1, + LINK_E2E_BEACON_OFF = 2 +}; + +enum beacon_type{ + BEACON_TYPE_NORMAL = 1, /*! Normal Beaconing. Green */ + BEACON_TYPE_WARN = 2, /*! Warning Beaconing. Yellow/Amber */ + BEACON_TYPE_CRITICAL = 3 /*! Critical Beaconing. Red */ +}; + +struct link_e2e_beacon_param_s { + u8 beacon_type; /* Beacon Type. See beacon_type_t */ + u8 beacon_frequency; + /* Beacon frequency. Number of blinks + * per 10 seconds + */ + u16 beacon_duration;/* Beacon duration (in Seconds). The + * command operation should be + * terminated at the end of this + * timeout value. + * + * Ignored if diag_sub_cmd is + * LINK_E2E_BEACON_OFF. + * + * If 0, beaconing will continue till a + * BEACON OFF request is received + */ +}; + +/* + * Link E2E beacon request/good response format. For LS_RJTs use fc_ls_rjt_t + */ +struct link_e2e_beacon_req_s{ + u32 ls_code; /*! FC_ELS_E2E_LBEACON in requests * + *or FC_ELS_ACC in good replies */ + u32 ls_sub_cmd; /*! See link_e2e_beacon_subcmd_t */ + struct link_e2e_beacon_param_s beacon_parm; +}; + +/** + * If RPSC request is sent to the Domain Controller, the request is for + * all the ports within that domain (TODO - I don't think FOS implements + * this...). + */ +struct fc_rpsc_cmd_s{ + struct fc_els_cmd_s els_cmd; +}; + +/* + * RPSC Acc + */ +struct fc_rpsc_acc_s{ + u32 command:8; + u32 rsvd:8; + u32 num_entries:16; + + struct fc_rpsc_speed_info_s speed_info[1]; +}; + +/** + * If RPSC2 request is sent to the Domain Controller, + */ +#define FC_BRCD_TOKEN 0x42524344 + +struct fc_rpsc2_cmd_s{ + struct fc_els_cmd_s els_cmd; + u32 token; + u16 resvd; + u16 num_pids; /* Number of pids in the request */ + struct { + u32 rsvd1:8; + u32 pid:24; /* port identifier */ + } pid_list[1]; +}; + +enum fc_rpsc2_port_type{ + RPSC2_PORT_TYPE_UNKNOWN = 0, + RPSC2_PORT_TYPE_NPORT = 1, + RPSC2_PORT_TYPE_NLPORT = 2, + RPSC2_PORT_TYPE_NPIV_PORT = 0x5f, + RPSC2_PORT_TYPE_NPORT_TRUNK = 0x6f, +}; + +/* + * RPSC2 portInfo entry structure + */ +struct fc_rpsc2_port_info_s{ + u32 pid; /* PID */ + u16 resvd1; + u16 index; /* port number / index */ + u8 resvd2; + u8 type; /* port type N/NL/... */ + u16 speed; /* port Operating Speed */ +}; + +/* + * RPSC2 Accept payload + */ +struct fc_rpsc2_acc_s{ + u8 els_cmd; + u8 resvd; + u16 num_pids; /* Number of pids in the request */ + struct fc_rpsc2_port_info_s port_info[1]; /* port information */ +}; + +/** + * bit fields so that multiple classes can be specified + */ +enum fc_cos{ + FC_CLASS_2 = 0x04, + FC_CLASS_3 = 0x08, + FC_CLASS_2_3 = 0x0C, +}; + +/* + * symbolic name + */ +struct fc_symname_s{ + u8 symname[FC_SYMNAME_MAX]; +}; + +struct fc_alpabm_s{ + u8 alpa_bm[FC_ALPA_MAX / 8]; +}; + +/* + * protocol default timeout values + */ +#define FC_ED_TOV 2 +#define FC_REC_TOV (FC_ED_TOV + 1) +#define FC_RA_TOV 10 +#define FC_ELS_TOV (2 * FC_RA_TOV) + +/* + * virtual fabric related defines + */ +#define FC_VF_ID_NULL 0 /* must not be used as VF_ID */ +#define FC_VF_ID_MIN 1 +#define FC_VF_ID_MAX 0xEFF +#define FC_VF_ID_CTL 0xFEF /* control VF_ID */ + +/** + * Virtual Fabric Tagging header format + * @caution This is defined only in BIG ENDIAN format. + */ +struct fc_vft_s{ + u32 r_ctl:8; + u32 ver:2; + u32 type:4; + u32 res_a:2; + u32 priority:3; + u32 vf_id:12; + u32 res_b:1; + u32 hopct:8; + u32 res_c:24; +}; + +#pragma pack() + +#endif diff --git a/drivers/scsi/bfa/include/protocol/fc_sp.h b/drivers/scsi/bfa/include/protocol/fc_sp.h new file mode 100644 index 000000000000..55bb0b31d04b --- /dev/null +++ b/drivers/scsi/bfa/include/protocol/fc_sp.h @@ -0,0 +1,224 @@ +/* + * Copyright (c) 2005-2009 Brocade Communications Systems, Inc. + * All rights reserved + * www.brocade.com + * + * Linux driver for Brocade Fibre Channel Host Bus Adapter. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (GPL) Version 2 as + * published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#ifndef __FC_SP_H__ +#define __FC_SP_H__ + +#include <protocol/types.h> + +#pragma pack(1) + +enum auth_els_flags{ + FC_AUTH_ELS_MORE_FRAGS_FLAG = 0x80, /*! bit-7. More Fragments + * Follow + */ + FC_AUTH_ELS_CONCAT_FLAG = 0x40, /*! bit-6. Concatenation Flag */ + FC_AUTH_ELS_SEQ_NUM_FLAG = 0x01 /*! bit-0. Sequence Number */ +}; + +enum auth_msg_codes{ + FC_AUTH_MC_AUTH_RJT = 0x0A, /*! Auth Reject */ + FC_AUTH_MC_AUTH_NEG = 0x0B, /*! Auth Negotiate */ + FC_AUTH_MC_AUTH_DONE = 0x0C, /*! Auth Done */ + + FC_AUTH_MC_DHCHAP_CHAL = 0x10, /*! DHCHAP Challenge */ + FC_AUTH_MC_DHCHAP_REPLY = 0x11, /*! DHCHAP Reply */ + FC_AUTH_MC_DHCHAP_SUCC = 0x12, /*! DHCHAP Success */ + + FC_AUTH_MC_FCAP_REQ = 0x13, /*! FCAP Request */ + FC_AUTH_MC_FCAP_ACK = 0x14, /*! FCAP Acknowledge */ + FC_AUTH_MC_FCAP_CONF = 0x15, /*! FCAP Confirm */ + + FC_AUTH_MC_FCPAP_INIT = 0x16, /*! FCPAP Init */ + FC_AUTH_MC_FCPAP_ACC = 0x17, /*! FCPAP Accept */ + FC_AUTH_MC_FCPAP_COMP = 0x18, /*! FCPAP Complete */ + + FC_AUTH_MC_IKE_SA_INIT = 0x22, /*! IKE SA INIT */ + FC_AUTH_MC_IKE_SA_AUTH = 0x23, /*! IKE SA Auth */ + FC_AUTH_MC_IKE_CREATE_CHILD_SA = 0x24, /*! IKE Create Child SA */ + FC_AUTH_MC_IKE_INFO = 0x25, /*! IKE informational */ +}; + +enum auth_proto_version{ + FC_AUTH_PROTO_VER_1 = 1, /*! Protocol Version 1 */ +}; + +enum { + FC_AUTH_ELS_COMMAND_CODE = 0x90,/*! Authentication ELS Command code */ + FC_AUTH_PROTO_PARAM_LEN_SZ = 4, /*! Size of Proto Parameter Len Field */ + FC_AUTH_PROTO_PARAM_VAL_SZ = 4, /*! Size of Proto Parameter Val Field */ + FC_MAX_AUTH_SECRET_LEN = 256, + /*! Maximum secret string length */ + FC_AUTH_NUM_USABLE_PROTO_LEN_SZ = 4, + /*! Size of usable protocols field */ + FC_AUTH_RESP_VALUE_LEN_SZ = 4, + /*! Size of response value length */ + FC_MAX_CHAP_KEY_LEN = 256, /*! Maximum md5 digest length */ + FC_MAX_AUTH_RETRIES = 3, /*! Maximum number of retries */ + FC_MD5_DIGEST_LEN = 16, /*! MD5 digest length */ + FC_SHA1_DIGEST_LEN = 20, /*! SHA1 digest length */ + FC_MAX_DHG_SUPPORTED = 1, /*! Maximum DH Groups supported */ + FC_MAX_ALG_SUPPORTED = 1, /*! Maximum algorithms supported */ + FC_MAX_PROTO_SUPPORTED = 1, /*! Maximum protocols supported */ + FC_START_TXN_ID = 2, /*! Starting transaction ID */ +}; + +enum auth_proto_id{ + FC_AUTH_PROTO_DHCHAP = 0x00000001, + FC_AUTH_PROTO_FCAP = 0x00000002, + FC_AUTH_PROTO_FCPAP = 0x00000003, + FC_AUTH_PROTO_IKEv2 = 0x00000004, + FC_AUTH_PROTO_IKEv2_AUTH = 0x00000005, +}; + +struct auth_name_s{ + u16 name_tag; /*! Name Tag = 1 for Authentication */ + u16 name_len; /*! Name Length = 8 for Authentication + */ + wwn_t name; /*! Name. TODO - is this PWWN */ +}; + + +enum auth_hash_func{ + FC_AUTH_HASH_FUNC_MD5 = 0x00000005, + FC_AUTH_HASH_FUNC_SHA_1 = 0x00000006, +}; + +enum auth_dh_gid{ + FC_AUTH_DH_GID_0_DHG_NULL = 0x00000000, + FC_AUTH_DH_GID_1_DHG_1024 = 0x00000001, + FC_AUTH_DH_GID_2_DHG_1280 = 0x00000002, + FC_AUTH_DH_GID_3_DHG_1536 = 0x00000003, + FC_AUTH_DH_GID_4_DHG_2048 = 0x00000004, + FC_AUTH_DH_GID_6_DHG_3072 = 0x00000006, + FC_AUTH_DH_GID_7_DHG_4096 = 0x00000007, + FC_AUTH_DH_GID_8_DHG_6144 = 0x00000008, + FC_AUTH_DH_GID_9_DHG_8192 = 0x00000009, +}; + +struct auth_els_msg_s { + u8 auth_els_code; /* Authentication ELS Code (0x90) */ + u8 auth_els_flag; /* Authentication ELS Flags */ + u8 auth_msg_code; /* Authentication Message Code */ + u8 proto_version; /* Protocol Version */ + u32 msg_len; /* Message Length */ + u32 trans_id; /* Transaction Identifier (T_ID) */ + + /* Msg payload follows... */ +}; + + +enum auth_neg_param_tags { + FC_AUTH_NEG_DHCHAP_HASHLIST = 0x0001, + FC_AUTH_NEG_DHCHAP_DHG_ID_LIST = 0x0002, +}; + + +struct dhchap_param_format_s { + u16 tag; /*! Parameter Tag. See + * auth_neg_param_tags_t + */ + u16 word_cnt; + + /* followed by variable length parameter value... */ +}; + +struct auth_proto_params_s { + u32 proto_param_len; + u32 proto_id; + + /* + * Followed by variable length Protocol specific parameters. DH-CHAP + * uses dhchap_param_format_t + */ +}; + +struct auth_neg_msg_s { + struct auth_name_s auth_ini_name; + u32 usable_auth_protos; + struct auth_proto_params_s proto_params[1]; /*! (1..usable_auth_proto) + * protocol params + */ +}; + +struct auth_dh_val_s { + u32 dh_val_len; + u32 dh_val[1]; +}; + +struct auth_dhchap_chal_msg_s { + struct auth_els_msg_s hdr; + struct auth_name_s auth_responder_name; /* TODO VRK - is auth_name_t + * type OK? + */ + u32 hash_id; + u32 dh_grp_id; + u32 chal_val_len; + char chal_val[1]; + + /* ...followed by variable Challenge length/value and DH length/value */ +}; + + +enum auth_rjt_codes { + FC_AUTH_RJT_CODE_AUTH_FAILURE = 0x01, + FC_AUTH_RJT_CODE_LOGICAL_ERR = 0x02, +}; + +enum auth_rjt_code_exps { + FC_AUTH_CEXP_AUTH_MECH_NOT_USABLE = 0x01, + FC_AUTH_CEXP_DH_GROUP_NOT_USABLE = 0x02, + FC_AUTH_CEXP_HASH_FUNC_NOT_USABLE = 0x03, + FC_AUTH_CEXP_AUTH_XACT_STARTED = 0x04, + FC_AUTH_CEXP_AUTH_FAILED = 0x05, + FC_AUTH_CEXP_INCORRECT_PLD = 0x06, + FC_AUTH_CEXP_INCORRECT_PROTO_MSG = 0x07, + FC_AUTH_CEXP_RESTART_AUTH_PROTO = 0x08, + FC_AUTH_CEXP_AUTH_CONCAT_NOT_SUPP = 0x09, + FC_AUTH_CEXP_PROTO_VER_NOT_SUPP = 0x0A, +}; + +enum auth_status { + FC_AUTH_STATE_INPROGRESS = 0, /*! authentication in progress */ + FC_AUTH_STATE_FAILED = 1, /*! authentication failed */ + FC_AUTH_STATE_SUCCESS = 2 /*! authentication successful */ +}; + +struct auth_rjt_msg_s { + struct auth_els_msg_s hdr; + u8 reason_code; + u8 reason_code_exp; + u8 rsvd[2]; +}; + + +struct auth_dhchap_neg_msg_s { + struct auth_els_msg_s hdr; + struct auth_neg_msg_s nego; +}; + +struct auth_dhchap_reply_msg_s { + struct auth_els_msg_s hdr; + + /* + * followed by response value length & Value + DH Value Length & Value + */ +}; + +#pragma pack() + +#endif /* __FC_SP_H__ */ diff --git a/drivers/scsi/bfa/include/protocol/fcp.h b/drivers/scsi/bfa/include/protocol/fcp.h new file mode 100644 index 000000000000..9ade68ad2853 --- /dev/null +++ b/drivers/scsi/bfa/include/protocol/fcp.h @@ -0,0 +1,186 @@ +/* + * Copyright (c) 2005-2009 Brocade Communications Systems, Inc. + * All rights reserved + * www.brocade.com + * + * Linux driver for Brocade Fibre Channel Host Bus Adapter. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (GPL) Version 2 as + * published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#ifndef __FCPPROTO_H__ +#define __FCPPROTO_H__ + +#include <protocol/scsi.h> + +#pragma pack(1) + +enum { + FCP_RJT = 0x01000000, /* SRR reject */ + FCP_SRR_ACCEPT = 0x02000000, /* SRR accept */ + FCP_SRR = 0x14000000, /* Sequence Retransmission Request */ +}; + +/* + * SRR FC-4 LS payload + */ +struct fc_srr_s{ + u32 ls_cmd; + u32 ox_id:16; /* ox-id */ + u32 rx_id:16; /* rx-id */ + u32 ro; /* relative offset */ + u32 r_ctl:8; /* R_CTL for I.U. */ + u32 res:24; +}; + + +/* + * FCP_CMND definitions + */ +#define FCP_CMND_CDB_LEN 16 +#define FCP_CMND_LUN_LEN 8 + +struct fcp_cmnd_s{ + lun_t lun; /* 64-bit LU number */ + u8 crn; /* command reference number */ +#ifdef __BIGENDIAN + u8 resvd:1, + priority:4, /* FCP-3: SAM-3 priority */ + taskattr:3; /* scsi task attribute */ +#else + u8 taskattr:3, /* scsi task attribute */ + priority:4, /* FCP-3: SAM-3 priority */ + resvd:1; +#endif + u8 tm_flags; /* task management flags */ +#ifdef __BIGENDIAN + u8 addl_cdb_len:6, /* additional CDB length words */ + iodir:2; /* read/write FCP_DATA IUs */ +#else + u8 iodir:2, /* read/write FCP_DATA IUs */ + addl_cdb_len:6; /* additional CDB length */ +#endif + struct scsi_cdb_s cdb; + + /* + * !!! additional cdb bytes follows here!!! + */ + u32 fcp_dl; /* bytes to be transferred */ +}; + +#define fcp_cmnd_cdb_len(_cmnd) ((_cmnd)->addl_cdb_len * 4 + FCP_CMND_CDB_LEN) +#define fcp_cmnd_fcpdl(_cmnd) ((&(_cmnd)->fcp_dl)[(_cmnd)->addl_cdb_len]) + +/* + * fcp_cmnd_t.iodir field values + */ +enum fcp_iodir{ + FCP_IODIR_NONE = 0, + FCP_IODIR_WRITE = 1, + FCP_IODIR_READ = 2, + FCP_IODIR_RW = 3, +}; + +/* + * Task attribute field + */ +enum { + FCP_TASK_ATTR_SIMPLE = 0, + FCP_TASK_ATTR_HOQ = 1, + FCP_TASK_ATTR_ORDERED = 2, + FCP_TASK_ATTR_ACA = 4, + FCP_TASK_ATTR_UNTAGGED = 5, /* obsolete in FCP-3 */ +}; + +/* + * Task management flags field - only one bit shall be set + */ +#ifndef BIT +#define BIT(_x) (1 << (_x)) +#endif +enum fcp_tm_cmnd{ + FCP_TM_ABORT_TASK_SET = BIT(1), + FCP_TM_CLEAR_TASK_SET = BIT(2), + FCP_TM_LUN_RESET = BIT(4), + FCP_TM_TARGET_RESET = BIT(5), /* obsolete in FCP-3 */ + FCP_TM_CLEAR_ACA = BIT(6), +}; + +/* + * FCP_XFER_RDY IU defines + */ +struct fcp_xfer_rdy_s{ + u32 data_ro; + u32 burst_len; + u32 reserved; +}; + +/* + * FCP_RSP residue flags + */ +enum fcp_residue{ + FCP_NO_RESIDUE = 0, /* no residue */ + FCP_RESID_OVER = 1, /* more data left that was not sent */ + FCP_RESID_UNDER = 2, /* less data than requested */ +}; + +enum { + FCP_RSPINFO_GOOD = 0, + FCP_RSPINFO_DATALEN_MISMATCH = 1, + FCP_RSPINFO_CMND_INVALID = 2, + FCP_RSPINFO_ROLEN_MISMATCH = 3, + FCP_RSPINFO_TM_NOT_SUPP = 4, + FCP_RSPINFO_TM_FAILED = 5, +}; + +struct fcp_rspinfo_s{ + u32 res0:24; + u32 rsp_code:8; /* response code (as above) */ + u32 res1; +}; + +struct fcp_resp_s{ + u32 reserved[2]; /* 2 words reserved */ + u16 reserved2; +#ifdef __BIGENDIAN + u8 reserved3:3; + u8 fcp_conf_req:1; /* FCP_CONF is requested */ + u8 resid_flags:2; /* underflow/overflow */ + u8 sns_len_valid:1;/* sense len is valid */ + u8 rsp_len_valid:1;/* response len is valid */ +#else + u8 rsp_len_valid:1;/* response len is valid */ + u8 sns_len_valid:1;/* sense len is valid */ + u8 resid_flags:2; /* underflow/overflow */ + u8 fcp_conf_req:1; /* FCP_CONF is requested */ + u8 reserved3:3; +#endif + u8 scsi_status; /* one byte SCSI status */ + u32 residue; /* residual data bytes */ + u32 sns_len; /* length od sense info */ + u32 rsp_len; /* length of response info */ +}; + +#define fcp_snslen(__fcprsp) ((__fcprsp)->sns_len_valid ? \ + (__fcprsp)->sns_len : 0) +#define fcp_rsplen(__fcprsp) ((__fcprsp)->rsp_len_valid ? \ + (__fcprsp)->rsp_len : 0) +#define fcp_rspinfo(__fcprsp) ((struct fcp_rspinfo_s *)((__fcprsp) + 1)) +#define fcp_snsinfo(__fcprsp) (((u8 *)fcp_rspinfo(__fcprsp)) + \ + fcp_rsplen(__fcprsp)) + +struct fcp_cmnd_fr_s{ + struct fchs_s fchs; + struct fcp_cmnd_s fcp; +}; + +#pragma pack() + +#endif diff --git a/drivers/scsi/bfa/include/protocol/fdmi.h b/drivers/scsi/bfa/include/protocol/fdmi.h new file mode 100644 index 000000000000..6c05c268c71b --- /dev/null +++ b/drivers/scsi/bfa/include/protocol/fdmi.h @@ -0,0 +1,163 @@ +/* + * Copyright (c) 2005-2009 Brocade Communications Systems, Inc. + * All rights reserved + * www.brocade.com + * + * Linux driver for Brocade Fibre Channel Host Bus Adapter. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (GPL) Version 2 as + * published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#ifndef __FDMI_H__ +#define __FDMI_H__ + +#include <protocol/types.h> +#include <protocol/fc.h> +#include <protocol/ct.h> + +#pragma pack(1) + +/* + * FDMI Command Codes + */ +#define FDMI_GRHL 0x0100 +#define FDMI_GHAT 0x0101 +#define FDMI_GRPL 0x0102 +#define FDMI_GPAT 0x0110 +#define FDMI_RHBA 0x0200 +#define FDMI_RHAT 0x0201 +#define FDMI_RPRT 0x0210 +#define FDMI_RPA 0x0211 +#define FDMI_DHBA 0x0300 +#define FDMI_DPRT 0x0310 + +/* + * FDMI reason codes + */ +#define FDMI_NO_ADDITIONAL_EXP 0x00 +#define FDMI_HBA_ALREADY_REG 0x10 +#define FDMI_HBA_ATTRIB_NOT_REG 0x11 +#define FDMI_HBA_ATTRIB_MULTIPLE 0x12 +#define FDMI_HBA_ATTRIB_LENGTH_INVALID 0x13 +#define FDMI_HBA_ATTRIB_NOT_PRESENT 0x14 +#define FDMI_PORT_ORIG_NOT_IN_LIST 0x15 +#define FDMI_PORT_HBA_NOT_IN_LIST 0x16 +#define FDMI_PORT_ATTRIB_NOT_REG 0x20 +#define FDMI_PORT_NOT_REG 0x21 +#define FDMI_PORT_ATTRIB_MULTIPLE 0x22 +#define FDMI_PORT_ATTRIB_LENGTH_INVALID 0x23 +#define FDMI_PORT_ALREADY_REGISTEREED 0x24 + +/* + * FDMI Transmission Speed Mask values + */ +#define FDMI_TRANS_SPEED_1G 0x00000001 +#define FDMI_TRANS_SPEED_2G 0x00000002 +#define FDMI_TRANS_SPEED_10G 0x00000004 +#define FDMI_TRANS_SPEED_4G 0x00000008 +#define FDMI_TRANS_SPEED_8G 0x00000010 +#define FDMI_TRANS_SPEED_16G 0x00000020 +#define FDMI_TRANS_SPEED_UNKNOWN 0x00008000 + +/* + * FDMI HBA attribute types + */ +enum fdmi_hba_attribute_type { + FDMI_HBA_ATTRIB_NODENAME = 1, /* 0x0001 */ + FDMI_HBA_ATTRIB_MANUFACTURER, /* 0x0002 */ + FDMI_HBA_ATTRIB_SERIALNUM, /* 0x0003 */ + FDMI_HBA_ATTRIB_MODEL, /* 0x0004 */ + FDMI_HBA_ATTRIB_MODEL_DESC, /* 0x0005 */ + FDMI_HBA_ATTRIB_HW_VERSION, /* 0x0006 */ + FDMI_HBA_ATTRIB_DRIVER_VERSION, /* 0x0007 */ + FDMI_HBA_ATTRIB_ROM_VERSION, /* 0x0008 */ + FDMI_HBA_ATTRIB_FW_VERSION, /* 0x0009 */ + FDMI_HBA_ATTRIB_OS_NAME, /* 0x000A */ + FDMI_HBA_ATTRIB_MAX_CT, /* 0x000B */ + + FDMI_HBA_ATTRIB_MAX_TYPE +}; + +/* + * FDMI Port attribute types + */ +enum fdmi_port_attribute_type { + FDMI_PORT_ATTRIB_FC4_TYPES = 1, /* 0x0001 */ + FDMI_PORT_ATTRIB_SUPP_SPEED, /* 0x0002 */ + FDMI_PORT_ATTRIB_PORT_SPEED, /* 0x0003 */ + FDMI_PORT_ATTRIB_FRAME_SIZE, /* 0x0004 */ + FDMI_PORT_ATTRIB_DEV_NAME, /* 0x0005 */ + FDMI_PORT_ATTRIB_HOST_NAME, /* 0x0006 */ + + FDMI_PORT_ATTR_MAX_TYPE +}; + +/* + * FDMI attribute + */ +struct fdmi_attr_s { + u16 type; + u16 len; + u8 value[1]; +}; + +/* + * HBA Attribute Block + */ +struct fdmi_hba_attr_s { + u32 attr_count; /* # of attributes */ + struct fdmi_attr_s hba_attr; /* n attributes */ +}; + +/* + * Registered Port List + */ +struct fdmi_port_list_s { + u32 num_ports; /* number Of Port Entries */ + wwn_t port_entry; /* one or more */ +}; + +/* + * Port Attribute Block + */ +struct fdmi_port_attr_s { + u32 attr_count; /* # of attributes */ + struct fdmi_attr_s port_attr; /* n attributes */ +}; + +/* + * FDMI Register HBA Attributes + */ +struct fdmi_rhba_s { + wwn_t hba_id; /* HBA Identifier */ + struct fdmi_port_list_s port_list; /* Registered Port List */ + struct fdmi_hba_attr_s hba_attr_blk; /* HBA attribute block */ +}; + +/* + * FDMI Register Port + */ +struct fdmi_rprt_s { + wwn_t hba_id; /* HBA Identifier */ + wwn_t port_name; /* Port wwn */ + struct fdmi_port_attr_s port_attr_blk; /* Port Attr Block */ +}; + +/* + * FDMI Register Port Attributes + */ +struct fdmi_rpa_s { + wwn_t port_name; /* port wwn */ + struct fdmi_port_attr_s port_attr_blk; /* Port Attr Block */ +}; + +#pragma pack() + +#endif diff --git a/drivers/scsi/bfa/include/protocol/pcifw.h b/drivers/scsi/bfa/include/protocol/pcifw.h new file mode 100644 index 000000000000..6830dc3ee58a --- /dev/null +++ b/drivers/scsi/bfa/include/protocol/pcifw.h @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2005-2009 Brocade Communications Systems, Inc. + * All rights reserved + * www.brocade.com + * + * Linux driver for Brocade Fibre Channel Host Bus Adapter. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (GPL) Version 2 as + * published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +/** + * pcifw.h PCI FW related headers + */ + +#ifndef __PCIFW_H__ +#define __PCIFW_H__ + +#pragma pack(1) + +struct pnp_hdr_s{ + u32 signature; /* "$PnP" */ + u8 rev; /* Struct revision */ + u8 len; /* Header structure len in multiples + * of 16 bytes */ + u16 off; /* Offset to next header 00 if none */ + u8 rsvd; /* Reserved byte */ + u8 cksum; /* 8-bit checksum for this header */ + u32 pnp_dev_id; /* PnP Device Id */ + u16 mfstr; /* Pointer to manufacturer string */ + u16 prstr; /* Pointer to product string */ + u8 devtype[3]; /* Device Type Code */ + u8 devind; /* Device Indicator */ + u16 bcventr; /* Bootstrap entry vector */ + u16 rsvd2; /* Reserved */ + u16 sriv; /* Static resource information vector */ +}; + +struct pci_3_0_ds_s{ + u32 sig; /* Signature "PCIR" */ + u16 vendid; /* Vendor ID */ + u16 devid; /* Device ID */ + u16 devlistoff; /* Device List Offset */ + u16 len; /* PCI Data Structure Length */ + u8 rev; /* PCI Data Structure Revision */ + u8 clcode[3]; /* Class Code */ + u16 imglen; /* Code image length in multiples of + * 512 bytes */ + u16 coderev; /* Revision level of code/data */ + u8 codetype; /* Code type 0x00 - BIOS */ + u8 indr; /* Last image indicator */ + u16 mrtimglen; /* Max Run Time Image Length */ + u16 cuoff; /* Config Utility Code Header Offset */ + u16 dmtfclp; /* DMTF CLP entry point offset */ +}; + +struct pci_optrom_hdr_s{ + u16 sig; /* Signature 0x55AA */ + u8 len; /* Option ROM length in units of 512 bytes */ + u8 inivec[3]; /* Initialization vector */ + u8 rsvd[16]; /* Reserved field */ + u16 verptr; /* Pointer to version string - private */ + u16 pcids; /* Pointer to PCI data structure */ + u16 pnphdr; /* Pointer to PnP expansion header */ +}; + +#pragma pack() + +#endif diff --git a/drivers/scsi/bfa/include/protocol/scsi.h b/drivers/scsi/bfa/include/protocol/scsi.h new file mode 100644 index 000000000000..b220e6b4f6e1 --- /dev/null +++ b/drivers/scsi/bfa/include/protocol/scsi.h @@ -0,0 +1,1648 @@ +/* + * Copyright (c) 2005-2009 Brocade Communications Systems, Inc. + * All rights reserved + * www.brocade.com + * + * Linux driver for Brocade Fibre Channel Host Bus Adapter. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (GPL) Version 2 as + * published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#ifndef __SCSI_H__ +#define __SCSI_H__ + +#include <protocol/types.h> + +#pragma pack(1) + +/* + * generic SCSI cdb definition + */ +#define SCSI_MAX_CDBLEN 16 +struct scsi_cdb_s{ + u8 scsi_cdb[SCSI_MAX_CDBLEN]; +}; + +/* + * scsi lun serial number definition + */ +#define SCSI_LUN_SN_LEN 32 +struct scsi_lun_sn_s{ + u8 lun_sn[SCSI_LUN_SN_LEN]; +}; + +/* + * SCSI Direct Access Commands + */ +enum { + SCSI_OP_TEST_UNIT_READY = 0x00, + SCSI_OP_REQUEST_SENSE = 0x03, + SCSI_OP_FORMAT_UNIT = 0x04, + SCSI_OP_READ6 = 0x08, + SCSI_OP_WRITE6 = 0x0A, + SCSI_OP_WRITE_FILEMARKS = 0x10, + SCSI_OP_INQUIRY = 0x12, + SCSI_OP_MODE_SELECT6 = 0x15, + SCSI_OP_RESERVE6 = 0x16, + SCSI_OP_RELEASE6 = 0x17, + SCSI_OP_MODE_SENSE6 = 0x1A, + SCSI_OP_START_STOP_UNIT = 0x1B, + SCSI_OP_SEND_DIAGNOSTIC = 0x1D, + SCSI_OP_READ_CAPACITY = 0x25, + SCSI_OP_READ10 = 0x28, + SCSI_OP_WRITE10 = 0x2A, + SCSI_OP_VERIFY10 = 0x2F, + SCSI_OP_READ_DEFECT_DATA = 0x37, + SCSI_OP_LOG_SELECT = 0x4C, + SCSI_OP_LOG_SENSE = 0x4D, + SCSI_OP_MODE_SELECT10 = 0x55, + SCSI_OP_RESERVE10 = 0x56, + SCSI_OP_RELEASE10 = 0x57, + SCSI_OP_MODE_SENSE10 = 0x5A, + SCSI_OP_PER_RESERVE_IN = 0x5E, + SCSI_OP_PER_RESERVE_OUR = 0x5E, + SCSI_OP_READ16 = 0x88, + SCSI_OP_WRITE16 = 0x8A, + SCSI_OP_VERIFY16 = 0x8F, + SCSI_OP_READ_CAPACITY16 = 0x9E, + SCSI_OP_REPORT_LUNS = 0xA0, + SCSI_OP_READ12 = 0xA8, + SCSI_OP_WRITE12 = 0xAA, + SCSI_OP_UNDEF = 0xFF, +}; + +/* + * SCSI START_STOP_UNIT command + */ +struct scsi_start_stop_unit_s{ + u8 opcode; +#ifdef __BIGENDIAN + u8 lun:3; + u8 reserved1:4; + u8 immed:1; +#else + u8 immed:1; + u8 reserved1:4; + u8 lun:3; +#endif + u8 reserved2; + u8 reserved3; +#ifdef __BIGENDIAN + u8 power_conditions:4; + u8 reserved4:2; + u8 loEj:1; + u8 start:1; +#else + u8 start:1; + u8 loEj:1; + u8 reserved4:2; + u8 power_conditions:4; +#endif + u8 control; +}; + +/* + * SCSI SEND_DIAGNOSTIC command + */ +struct scsi_send_diagnostic_s{ + u8 opcode; +#ifdef __BIGENDIAN + u8 self_test_code:3; + u8 pf:1; + u8 reserved1:1; + u8 self_test:1; + u8 dev_offl:1; + u8 unit_offl:1; +#else + u8 unit_offl:1; + u8 dev_offl:1; + u8 self_test:1; + u8 reserved1:1; + u8 pf:1; + u8 self_test_code:3; +#endif + u8 reserved2; + + u8 param_list_length[2]; /* MSB first */ + u8 control; + +}; + +/* + * SCSI READ10/WRITE10 commands + */ +struct scsi_rw10_s{ + u8 opcode; +#ifdef __BIGENDIAN + u8 lun:3; + u8 dpo:1; /* Disable Page Out */ + u8 fua:1; /* Force Unit Access */ + u8 reserved1:2; + u8 rel_adr:1; /* relative address */ +#else + u8 rel_adr:1; + u8 reserved1:2; + u8 fua:1; + u8 dpo:1; + u8 lun:3; +#endif + u8 lba0; /* logical block address - MSB */ + u8 lba1; + u8 lba2; + u8 lba3; /* LSB */ + u8 reserved3; + u8 xfer_length0; /* transfer length in blocks - MSB */ + u8 xfer_length1; /* LSB */ + u8 control; +}; + +#define SCSI_CDB10_GET_LBA(cdb) \ + (((cdb)->lba0 << 24) | ((cdb)->lba1 << 16) | \ + ((cdb)->lba2 << 8) | (cdb)->lba3) + +#define SCSI_CDB10_SET_LBA(cdb, lba) { \ + (cdb)->lba0 = lba >> 24; \ + (cdb)->lba1 = (lba >> 16) & 0xFF; \ + (cdb)->lba2 = (lba >> 8) & 0xFF; \ + (cdb)->lba3 = lba & 0xFF; \ +} + +#define SCSI_CDB10_GET_TL(cdb) \ + ((cdb)->xfer_length0 << 8 | (cdb)->xfer_length1) +#define SCSI_CDB10_SET_TL(cdb, tl) { \ + (cdb)->xfer_length0 = tl >> 8; \ + (cdb)->xfer_length1 = tl & 0xFF; \ +} + +/* + * SCSI READ6/WRITE6 commands + */ +struct scsi_rw6_s{ + u8 opcode; +#ifdef __BIGENDIAN + u8 lun:3; + u8 lba0:5; /* MSb */ +#else + u8 lba0:5; /* MSb */ + u8 lun:3; +#endif + u8 lba1; + u8 lba2; /* LSB */ + u8 xfer_length; + u8 control; +}; + +#define SCSI_TAPE_CDB6_GET_TL(cdb) \ + (((cdb)->tl0 << 16) | ((cdb)->tl1 << 8) | (cdb)->tl2) + +#define SCSI_TAPE_CDB6_SET_TL(cdb, tl) { \ + (cdb)->tl0 = tl >> 16; \ + (cdb)->tl1 = (tl >> 8) & 0xFF; \ + (cdb)->tl2 = tl & 0xFF; \ +} + +/* + * SCSI sequential (TAPE) wrtie command + */ +struct scsi_tape_wr_s{ + u8 opcode; +#ifdef __BIGENDIAN + u8 rsvd:7; + u8 fixed:1; /* MSb */ +#else + u8 fixed:1; /* MSb */ + u8 rsvd:7; +#endif + u8 tl0; /* Msb */ + u8 tl1; + u8 tl2; /* Lsb */ + + u8 control; +}; + +#define SCSI_CDB6_GET_LBA(cdb) \ + (((cdb)->lba0 << 16) | ((cdb)->lba1 << 8) | (cdb)->lba2) + +#define SCSI_CDB6_SET_LBA(cdb, lba) { \ + (cdb)->lba0 = lba >> 16; \ + (cdb)->lba1 = (lba >> 8) & 0xFF; \ + (cdb)->lba2 = lba & 0xFF; \ +} + +#define SCSI_CDB6_GET_TL(cdb) ((cdb)->xfer_length) +#define SCSI_CDB6_SET_TL(cdb, tl) { \ + (cdb)->xfer_length = tl; \ +} + +/* + * SCSI sense data format + */ +struct scsi_sense_s{ +#ifdef __BIGENDIAN + u8 valid:1; + u8 rsp_code:7; +#else + u8 rsp_code:7; + u8 valid:1; +#endif + u8 seg_num; +#ifdef __BIGENDIAN + u8 file_mark:1; + u8 eom:1; /* end of media */ + u8 ili:1; /* incorrect length indicator */ + u8 reserved:1; + u8 sense_key:4; +#else + u8 sense_key:4; + u8 reserved:1; + u8 ili:1; /* incorrect length indicator */ + u8 eom:1; /* end of media */ + u8 file_mark:1; +#endif + u8 information[4]; /* device-type or command specific info + */ + u8 add_sense_length; + /* additional sense length */ + u8 command_info[4];/* command specific information + */ + u8 asc; /* additional sense code */ + u8 ascq; /* additional sense code qualifier */ + u8 fru_code; /* field replaceable unit code */ +#ifdef __BIGENDIAN + u8 sksv:1; /* sense key specific valid */ + u8 c_d:1; /* command/data bit */ + u8 res1:2; + u8 bpv:1; /* bit pointer valid */ + u8 bpointer:3; /* bit pointer */ +#else + u8 bpointer:3; /* bit pointer */ + u8 bpv:1; /* bit pointer valid */ + u8 res1:2; + u8 c_d:1; /* command/data bit */ + u8 sksv:1; /* sense key specific valid */ +#endif + u8 fpointer[2]; /* field pointer */ +}; + +#define SCSI_SENSE_CUR_ERR 0x70 +#define SCSI_SENSE_DEF_ERR 0x71 + +/* + * SCSI sense key values + */ +#define SCSI_SK_NO_SENSE 0x0 +#define SCSI_SK_REC_ERR 0x1 /* recovered error */ +#define SCSI_SK_NOT_READY 0x2 +#define SCSI_SK_MED_ERR 0x3 /* medium error */ +#define SCSI_SK_HW_ERR 0x4 /* hardware error */ +#define SCSI_SK_ILLEGAL_REQ 0x5 +#define SCSI_SK_UNIT_ATT 0x6 /* unit attention */ +#define SCSI_SK_DATA_PROTECT 0x7 +#define SCSI_SK_BLANK_CHECK 0x8 +#define SCSI_SK_VENDOR_SPEC 0x9 +#define SCSI_SK_COPY_ABORTED 0xA +#define SCSI_SK_ABORTED_CMND 0xB +#define SCSI_SK_VOL_OVERFLOW 0xD +#define SCSI_SK_MISCOMPARE 0xE + +/* + * SCSI additional sense codes + */ +#define SCSI_ASC_NO_ADD_SENSE 0x00 +#define SCSI_ASC_LUN_NOT_READY 0x04 +#define SCSI_ASC_LUN_COMMUNICATION 0x08 +#define SCSI_ASC_WRITE_ERROR 0x0C +#define SCSI_ASC_INVALID_CMND_CODE 0x20 +#define SCSI_ASC_BAD_LBA 0x21 +#define SCSI_ASC_INVALID_FIELD_IN_CDB 0x24 +#define SCSI_ASC_LUN_NOT_SUPPORTED 0x25 +#define SCSI_ASC_LUN_WRITE_PROTECT 0x27 +#define SCSI_ASC_POWERON_BDR 0x29 /* power on reset, bus reset, + * bus device reset + */ +#define SCSI_ASC_PARAMS_CHANGED 0x2A +#define SCSI_ASC_CMND_CLEARED_BY_A_I 0x2F +#define SCSI_ASC_SAVING_PARAM_NOTSUPP 0x39 +#define SCSI_ASC_TOCC 0x3F /* target operating condtions + * changed + */ +#define SCSI_ASC_PARITY_ERROR 0x47 +#define SCSI_ASC_CMND_PHASE_ERROR 0x4A +#define SCSI_ASC_DATA_PHASE_ERROR 0x4B +#define SCSI_ASC_VENDOR_SPEC 0x7F + +/* + * SCSI additional sense code qualifiers + */ +#define SCSI_ASCQ_CAUSE_NOT_REPORT 0x00 +#define SCSI_ASCQ_BECOMING_READY 0x01 +#define SCSI_ASCQ_INIT_CMD_REQ 0x02 +#define SCSI_ASCQ_FORMAT_IN_PROGRESS 0x04 +#define SCSI_ASCQ_OPERATION_IN_PROGRESS 0x07 +#define SCSI_ASCQ_SELF_TEST_IN_PROGRESS 0x09 +#define SCSI_ASCQ_WR_UNEXP_UNSOL_DATA 0x0C +#define SCSI_ASCQ_WR_NOTENG_UNSOL_DATA 0x0D + +#define SCSI_ASCQ_LBA_OUT_OF_RANGE 0x00 +#define SCSI_ASCQ_INVALID_ELEMENT_ADDR 0x01 + +#define SCSI_ASCQ_LUN_WRITE_PROTECTED 0x00 +#define SCSI_ASCQ_LUN_HW_WRITE_PROTECTED 0x01 +#define SCSI_ASCQ_LUN_SW_WRITE_PROTECTED 0x02 + +#define SCSI_ASCQ_POR 0x01 /* power on reset */ +#define SCSI_ASCQ_SBR 0x02 /* scsi bus reset */ +#define SCSI_ASCQ_BDR 0x03 /* bus device reset */ +#define SCSI_ASCQ_DIR 0x04 /* device internal reset */ + +#define SCSI_ASCQ_MODE_PARAMS_CHANGED 0x01 +#define SCSI_ASCQ_LOG_PARAMS_CHANGED 0x02 +#define SCSI_ASCQ_RESERVATIONS_PREEMPTED 0x03 +#define SCSI_ASCQ_RESERVATIONS_RELEASED 0x04 +#define SCSI_ASCQ_REGISTRATIONS_PREEMPTED 0x05 + +#define SCSI_ASCQ_MICROCODE_CHANGED 0x01 +#define SCSI_ASCQ_CHANGED_OPER_COND 0x02 +#define SCSI_ASCQ_INQ_CHANGED 0x03 /* inquiry data changed */ +#define SCSI_ASCQ_DI_CHANGED 0x05 /* device id changed */ +#define SCSI_ASCQ_RL_DATA_CHANGED 0x0E /* report luns data changed */ + +#define SCSI_ASCQ_DP_CRC_ERR 0x01 /* data phase crc error */ +#define SCSI_ASCQ_DP_SCSI_PARITY_ERR 0x02 /* data phase scsi parity error + */ +#define SCSI_ASCQ_IU_CRC_ERR 0x03 /* information unit crc error */ +#define SCSI_ASCQ_PROTO_SERV_CRC_ERR 0x05 + +#define SCSI_ASCQ_LUN_TIME_OUT 0x01 + +/* ------------------------------------------------------------ + * SCSI INQUIRY + * ------------------------------------------------------------*/ + +struct scsi_inquiry_s{ + u8 opcode; +#ifdef __BIGENDIAN + u8 lun:3; + u8 reserved1:3; + u8 cmd_dt:1; + u8 evpd:1; +#else + u8 evpd:1; + u8 cmd_dt:1; + u8 reserved1:3; + u8 lun:3; +#endif + u8 page_code; + u8 reserved2; + u8 alloc_length; + u8 control; +}; + +struct scsi_inquiry_vendor_s{ + u8 vendor_id[8]; +}; + +struct scsi_inquiry_prodid_s{ + u8 product_id[16]; +}; + +struct scsi_inquiry_prodrev_s{ + u8 product_rev[4]; +}; + +struct scsi_inquiry_data_s{ +#ifdef __BIGENDIAN + u8 peripheral_qual:3; /* peripheral qualifier */ + u8 device_type:5; /* peripheral device type */ + + u8 rmb:1; /* removable medium bit */ + u8 device_type_mod:7; /* device type modifier */ + + u8 version; + + u8 aenc:1; /* async event notification capability + */ + u8 trm_iop:1; /* terminate I/O process */ + u8 norm_aca:1; /* normal ACA supported */ + u8 hi_support:1; /* SCSI-3: supports REPORT LUNS */ + u8 rsp_data_format:4; + + u8 additional_len; + u8 sccs:1; + u8 reserved1:7; + + u8 reserved2:1; + u8 enc_serv:1; /* enclosure service component */ + u8 reserved3:1; + u8 multi_port:1; /* multi-port device */ + u8 m_chngr:1; /* device in medium transport element */ + u8 ack_req_q:1; /* SIP specific bit */ + u8 addr32:1; /* SIP specific bit */ + u8 addr16:1; /* SIP specific bit */ + + u8 rel_adr:1; /* relative address */ + u8 w_bus32:1; + u8 w_bus16:1; + u8 synchronous:1; + u8 linked_commands:1; + u8 trans_dis:1; + u8 cmd_queue:1; /* command queueing supported */ + u8 soft_reset:1; /* soft reset alternative (VS) */ +#else + u8 device_type:5; /* peripheral device type */ + u8 peripheral_qual:3; + /* peripheral qualifier */ + + u8 device_type_mod:7; + /* device type modifier */ + u8 rmb:1; /* removable medium bit */ + + u8 version; + + u8 rsp_data_format:4; + u8 hi_support:1; /* SCSI-3: supports REPORT LUNS */ + u8 norm_aca:1; /* normal ACA supported */ + u8 terminate_iop:1;/* terminate I/O process */ + u8 aenc:1; /* async event notification capability + */ + + u8 additional_len; + u8 reserved1:7; + u8 sccs:1; + + u8 addr16:1; /* SIP specific bit */ + u8 addr32:1; /* SIP specific bit */ + u8 ack_req_q:1; /* SIP specific bit */ + u8 m_chngr:1; /* device in medium transport element */ + u8 multi_port:1; /* multi-port device */ + u8 reserved3:1; /* TBD - Vendor Specific */ + u8 enc_serv:1; /* enclosure service component */ + u8 reserved2:1; + + u8 soft_seset:1; /* soft reset alternative (VS) */ + u8 cmd_queue:1; /* command queueing supported */ + u8 trans_dis:1; + u8 linked_commands:1; + u8 synchronous:1; + u8 w_bus16:1; + u8 w_bus32:1; + u8 rel_adr:1; /* relative address */ +#endif + struct scsi_inquiry_vendor_s vendor_id; + struct scsi_inquiry_prodid_s product_id; + struct scsi_inquiry_prodrev_s product_rev; + u8 vendor_specific[20]; + u8 reserved4[40]; +}; + +/* + * inquiry.peripheral_qual field values + */ +#define SCSI_DEVQUAL_DEFAULT 0 +#define SCSI_DEVQUAL_NOT_CONNECTED 1 +#define SCSI_DEVQUAL_NOT_SUPPORTED 3 + +/* + * inquiry.device_type field values + */ +#define SCSI_DEVICE_DIRECT_ACCESS 0x00 +#define SCSI_DEVICE_SEQ_ACCESS 0x01 +#define SCSI_DEVICE_ARRAY_CONTROLLER 0x0C +#define SCSI_DEVICE_UNKNOWN 0x1F + +/* + * inquiry.version + */ +#define SCSI_VERSION_ANSI_X3131 2 /* ANSI X3.131 SCSI-2 */ +#define SCSI_VERSION_SPC 3 /* SPC (SCSI-3), ANSI X3.301:1997 */ +#define SCSI_VERSION_SPC_2 4 /* SPC-2 */ + +/* + * response data format + */ +#define SCSI_RSP_DATA_FORMAT 2 /* SCSI-2 & SPC */ + +/* + * SCSI inquiry page codes + */ +#define SCSI_INQ_PAGE_VPD_PAGES 0x00 /* supported vpd pages */ +#define SCSI_INQ_PAGE_USN_PAGE 0x80 /* unit serial number page */ +#define SCSI_INQ_PAGE_DEV_IDENT 0x83 /* device indentification page + */ +#define SCSI_INQ_PAGES_MAX 3 + +/* + * supported vital product data pages + */ +struct scsi_inq_page_vpd_pages_s{ +#ifdef __BIGENDIAN + u8 peripheral_qual:3; + u8 device_type:5; +#else + u8 device_type:5; + u8 peripheral_qual:3; +#endif + u8 page_code; + u8 reserved; + u8 page_length; + u8 pages[SCSI_INQ_PAGES_MAX]; +}; + +/* + * Unit serial number page + */ +#define SCSI_INQ_USN_LEN 32 + +struct scsi_inq_usn_s{ + char usn[SCSI_INQ_USN_LEN]; +}; + +struct scsi_inq_page_usn_s{ +#ifdef __BIGENDIAN + u8 peripheral_qual:3; + u8 device_type:5; +#else + u8 device_type:5; + u8 peripheral_qual:3; +#endif + u8 page_code; + u8 reserved1; + u8 page_length; + struct scsi_inq_usn_s usn; +}; + +enum { + SCSI_INQ_DIP_CODE_BINARY = 1, /* identifier has binary value */ + SCSI_INQ_DIP_CODE_ASCII = 2, /* identifier has ascii value */ +}; + +enum { + SCSI_INQ_DIP_ASSOC_LUN = 0, /* id is associated with device */ + SCSI_INQ_DIP_ASSOC_PORT = 1, /* id is associated with port that + * received the request + */ +}; + +enum { + SCSI_INQ_ID_TYPE_VENDOR = 1, + SCSI_INQ_ID_TYPE_IEEE = 2, + SCSI_INQ_ID_TYPE_FC_FS = 3, + SCSI_INQ_ID_TYPE_OTHER = 4, +}; + +struct scsi_inq_dip_desc_s{ +#ifdef __BIGENDIAN + u8 res0:4; + u8 code_set:4; + u8 res1:2; + u8 association:2; + u8 id_type:4; +#else + u8 code_set:4; + u8 res0:4; + u8 id_type:4; + u8 association:2; + u8 res1:2; +#endif + u8 res2; + u8 id_len; + struct scsi_lun_sn_s id; +}; + +/* + * Device indentification page + */ +struct scsi_inq_page_dev_ident_s{ +#ifdef __BIGENDIAN + u8 peripheral_qual:3; + u8 device_type:5; +#else + u8 device_type:5; + u8 peripheral_qual:3; +#endif + u8 page_code; + u8 reserved1; + u8 page_length; + struct scsi_inq_dip_desc_s desc; +}; + +/* ------------------------------------------------------------ + * READ CAPACITY + * ------------------------------------------------------------ + */ + +struct scsi_read_capacity_s{ + u8 opcode; +#ifdef __BIGENDIAN + u8 lun:3; + u8 reserved1:4; + u8 rel_adr:1; +#else + u8 rel_adr:1; + u8 reserved1:4; + u8 lun:3; +#endif + u8 lba0; /* MSB */ + u8 lba1; + u8 lba2; + u8 lba3; /* LSB */ + u8 reserved2; + u8 reserved3; +#ifdef __BIGENDIAN + u8 reserved4:7; + u8 pmi:1; /* partial medium indicator */ +#else + u8 pmi:1; /* partial medium indicator */ + u8 reserved4:7; +#endif + u8 control; +}; + +struct scsi_read_capacity_data_s{ + u32 max_lba; /* maximum LBA available */ + u32 block_length; /* in bytes */ +}; + +struct scsi_read_capacity16_data_s{ + u64 lba; /* maximum LBA available */ + u32 block_length; /* in bytes */ +#ifdef __BIGENDIAN + u8 reserved1:4, + p_type:3, + prot_en:1; + u8 reserved2:4, + lb_pbe:4; /* logical blocks per physical block + * exponent */ + u16 reserved3:2, + lba_align:14; /* lowest aligned logical block + * address */ +#else + u16 lba_align:14, /* lowest aligned logical block + * address */ + reserved3:2; + u8 lb_pbe:4, /* logical blocks per physical block + * exponent */ + reserved2:4; + u8 prot_en:1, + p_type:3, + reserved1:4; +#endif + u64 reserved4; + u64 reserved5; +}; + +/* ------------------------------------------------------------ + * REPORT LUNS command + * ------------------------------------------------------------ + */ + +struct scsi_report_luns_s{ + u8 opcode; /* A0h - REPORT LUNS opCode */ + u8 reserved1[5]; + u8 alloc_length[4];/* allocation length MSB first */ + u8 reserved2; + u8 control; +}; + +#define SCSI_REPORT_LUN_ALLOC_LENGTH(rl) \ + ((rl->alloc_length[0] << 24) | (rl->alloc_length[1] << 16) | \ + (rl->alloc_length[2] << 8) | (rl->alloc_length[3])) + +#define SCSI_REPORT_LUNS_SET_ALLOCLEN(rl, alloc_len) { \ + (rl)->alloc_length[0] = (alloc_len) >> 24; \ + (rl)->alloc_length[1] = ((alloc_len) >> 16) & 0xFF; \ + (rl)->alloc_length[2] = ((alloc_len) >> 8) & 0xFF; \ + (rl)->alloc_length[3] = (alloc_len) & 0xFF; \ +} + +struct scsi_report_luns_data_s{ + u32 lun_list_length; /* length of LUN list length */ + u32 reserved; + lun_t lun[1]; /* first LUN in lun list */ +}; + +/* ------------------------------------------------------------- + * SCSI mode parameters + * ----------------------------------------------------------- + */ +enum { + SCSI_DA_MEDIUM_DEF = 0, /* direct access default medium type */ + SCSI_DA_MEDIUM_SS = 1, /* direct access single sided */ + SCSI_DA_MEDIUM_DS = 2, /* direct access double sided */ +}; + +/* + * SCSI Mode Select(6) cdb + */ +struct scsi_mode_select6_s{ + u8 opcode; +#ifdef __BIGENDIAN + u8 reserved1:3; + u8 pf:1; /* page format */ + u8 reserved2:3; + u8 sp:1; /* save pages if set to 1 */ +#else + u8 sp:1; /* save pages if set to 1 */ + u8 reserved2:3; + u8 pf:1; /* page format */ + u8 reserved1:3; +#endif + u8 reserved3[2]; + u8 alloc_len; + u8 control; +}; + +/* + * SCSI Mode Select(10) cdb + */ +struct scsi_mode_select10_s{ + u8 opcode; +#ifdef __BIGENDIAN + u8 reserved1:3; + u8 pf:1; /* page format */ + u8 reserved2:3; + u8 sp:1; /* save pages if set to 1 */ +#else + u8 sp:1; /* save pages if set to 1 */ + u8 reserved2:3; + u8 pf:1; /* page format */ + u8 reserved1:3; +#endif + u8 reserved3[5]; + u8 alloc_len_msb; + u8 alloc_len_lsb; + u8 control; +}; + +/* + * SCSI Mode Sense(6) cdb + */ +struct scsi_mode_sense6_s{ + u8 opcode; +#ifdef __BIGENDIAN + u8 reserved1:4; + u8 dbd:1; /* disable block discriptors if set to 1 */ + u8 reserved2:3; + + u8 pc:2; /* page control */ + u8 page_code:6; +#else + u8 reserved2:3; + u8 dbd:1; /* disable block descriptors if set to 1 */ + u8 reserved1:4; + + u8 page_code:6; + u8 pc:2; /* page control */ +#endif + u8 reserved3; + u8 alloc_len; + u8 control; +}; + +/* + * SCSI Mode Sense(10) cdb + */ +struct scsi_mode_sense10_s{ + u8 opcode; +#ifdef __BIGENDIAN + u8 reserved1:3; + u8 LLBAA:1; /* long LBA accepted if set to 1 */ + u8 dbd:1; /* disable block descriptors if set + * to 1 + */ + u8 reserved2:3; + + u8 pc:2; /* page control */ + u8 page_code:6; +#else + u8 reserved2:3; + u8 dbd:1; /* disable block descriptors if set to + * 1 + */ + u8 LLBAA:1; /* long LBA accepted if set to 1 */ + u8 reserved1:3; + + u8 page_code:6; + u8 pc:2; /* page control */ +#endif + u8 reserved3[4]; + u8 alloc_len_msb; + u8 alloc_len_lsb; + u8 control; +}; + +#define SCSI_CDB10_GET_AL(cdb) \ + ((cdb)->alloc_len_msb << 8 | (cdb)->alloc_len_lsb) + +#define SCSI_CDB10_SET_AL(cdb, al) { \ + (cdb)->alloc_len_msb = al >> 8; \ + (cdb)->alloc_len_lsb = al & 0xFF; \ +} + +#define SCSI_CDB6_GET_AL(cdb) ((cdb)->alloc_len) + +#define SCSI_CDB6_SET_AL(cdb, al) { \ + (cdb)->alloc_len = al; \ +} + +/* + * page control field values + */ +#define SCSI_PC_CURRENT_VALUES 0x0 +#define SCSI_PC_CHANGEABLE_VALUES 0x1 +#define SCSI_PC_DEFAULT_VALUES 0x2 +#define SCSI_PC_SAVED_VALUES 0x3 + +/* + * SCSI mode page codes + */ +#define SCSI_MP_VENDOR_SPEC 0x00 +#define SCSI_MP_DISC_RECN 0x02 /* disconnect-reconnect page */ +#define SCSI_MP_FORMAT_DEVICE 0x03 +#define SCSI_MP_RDG 0x04 /* rigid disk geometry page */ +#define SCSI_MP_FDP 0x05 /* flexible disk page */ +#define SCSI_MP_CACHING 0x08 /* caching page */ +#define SCSI_MP_CONTROL 0x0A /* control mode page */ +#define SCSI_MP_MED_TYPES_SUP 0x0B /* medium types supported page */ +#define SCSI_MP_INFO_EXCP_CNTL 0x1C /* informational exception control */ +#define SCSI_MP_ALL 0x3F /* return all pages - mode sense only */ + +/* + * mode parameter header + */ +struct scsi_mode_param_header6_s{ + u8 mode_datalen; + u8 medium_type; + + /* + * device specific parameters expanded for direct access devices + */ +#ifdef __BIGENDIAN + u32 wp:1; /* write protected */ + u32 reserved1:2; + u32 dpofua:1; /* disable page out + force unit access + */ + u32 reserved2:4; +#else + u32 reserved2:4; + u32 dpofua:1; /* disable page out + force unit access + */ + u32 reserved1:2; + u32 wp:1; /* write protected */ +#endif + + u8 block_desclen; +}; + +struct scsi_mode_param_header10_s{ + u32 mode_datalen:16; + u32 medium_type:8; + + /* + * device specific parameters expanded for direct access devices + */ +#ifdef __BIGENDIAN + u32 wp:1; /* write protected */ + u32 reserved1:2; + u32 dpofua:1; /* disable page out + force unit access + */ + u32 reserved2:4; +#else + u32 reserved2:4; + u32 dpofua:1; /* disable page out + force unit access + */ + u32 reserved1:2; + u32 wp:1; /* write protected */ +#endif + +#ifdef __BIGENDIAN + u32 reserved3:7; + u32 longlba:1; +#else + u32 longlba:1; + u32 reserved3:7; +#endif + u32 reserved4:8; + u32 block_desclen:16; +}; + +/* + * mode parameter block descriptor + */ +struct scsi_mode_param_desc_s{ + u32 nblks; + u32 density_code:8; + u32 block_length:24; +}; + +/* + * Disconnect-reconnect mode page format + */ +struct scsi_mp_disc_recn_s{ +#ifdef __BIGENDIAN + u8 ps:1; + u8 reserved1:1; + u8 page_code:6; +#else + u8 page_code:6; + u8 reserved1:1; + u8 ps:1; +#endif + u8 page_len; + u8 buf_full_ratio; + u8 buf_empty_ratio; + + u8 bil_msb; /* bus inactivity limit -MSB */ + u8 bil_lsb; /* bus inactivity limit -LSB */ + + u8 dtl_msb; /* disconnect time limit - MSB */ + u8 dtl_lsb; /* disconnect time limit - LSB */ + + u8 ctl_msb; /* connect time limit - MSB */ + u8 ctl_lsb; /* connect time limit - LSB */ + + u8 max_burst_len_msb; + u8 max_burst_len_lsb; +#ifdef __BIGENDIAN + u8 emdp:1; /* enable modify data pointers */ + u8 fa:3; /* fair arbitration */ + u8 dimm:1; /* disconnect immediate */ + u8 dtdc:3; /* data transfer disconnect control */ +#else + u8 dtdc:3; /* data transfer disconnect control */ + u8 dimm:1; /* disconnect immediate */ + u8 fa:3; /* fair arbitration */ + u8 emdp:1; /* enable modify data pointers */ +#endif + + u8 reserved3; + + u8 first_burst_len_msb; + u8 first_burst_len_lsb; +}; + +/* + * SCSI format device mode page + */ +struct scsi_mp_format_device_s{ +#ifdef __BIGENDIAN + u32 ps:1; + u32 reserved1:1; + u32 page_code:6; +#else + u32 page_code:6; + u32 reserved1:1; + u32 ps:1; +#endif + u32 page_len:8; + u32 tracks_per_zone:16; + + u32 a_sec_per_zone:16; + u32 a_tracks_per_zone:16; + + u32 a_tracks_per_lun:16; /* alternate tracks/lun-MSB */ + u32 sec_per_track:16; /* sectors/track-MSB */ + + u32 bytes_per_sector:16; + u32 interleave:16; + + u32 tsf:16; /* track skew factor-MSB */ + u32 csf:16; /* cylinder skew factor-MSB */ + +#ifdef __BIGENDIAN + u32 ssec:1; /* soft sector formatting */ + u32 hsec:1; /* hard sector formatting */ + u32 rmb:1; /* removable media */ + u32 surf:1; /* surface */ + u32 reserved2:4; +#else + u32 reserved2:4; + u32 surf:1; /* surface */ + u32 rmb:1; /* removable media */ + u32 hsec:1; /* hard sector formatting */ + u32 ssec:1; /* soft sector formatting */ +#endif + u32 reserved3:24; +}; + +/* + * SCSI rigid disk device geometry page + */ +struct scsi_mp_rigid_device_geometry_s{ +#ifdef __BIGENDIAN + u32 ps:1; + u32 reserved1:1; + u32 page_code:6; +#else + u32 page_code:6; + u32 reserved1:1; + u32 ps:1; +#endif + u32 page_len:8; + u32 num_cylinders0:8; + u32 num_cylinders1:8; + + u32 num_cylinders2:8; + u32 num_heads:8; + u32 scwp0:8; + u32 scwp1:8; + + u32 scwp2:8; + u32 scrwc0:8; + u32 scrwc1:8; + u32 scrwc2:8; + + u32 dsr:16; + u32 lscyl0:8; + u32 lscyl1:8; + + u32 lscyl2:8; +#ifdef __BIGENDIAN + u32 reserved2:6; + u32 rpl:2; /* rotational position locking */ +#else + u32 rpl:2; /* rotational position locking */ + u32 reserved2:6; +#endif + u32 rot_off:8; + u32 reserved3:8; + + u32 med_rot_rate:16; + u32 reserved4:16; +}; + +/* + * SCSI caching mode page + */ +struct scsi_mp_caching_s{ +#ifdef __BIGENDIAN + u8 ps:1; + u8 res1:1; + u8 page_code:6; +#else + u8 page_code:6; + u8 res1:1; + u8 ps:1; +#endif + u8 page_len; +#ifdef __BIGENDIAN + u8 ic:1; /* initiator control */ + u8 abpf:1; /* abort pre-fetch */ + u8 cap:1; /* caching analysis permitted */ + u8 disc:1; /* discontinuity */ + u8 size:1; /* size enable */ + u8 wce:1; /* write cache enable */ + u8 mf:1; /* multiplication factor */ + u8 rcd:1; /* read cache disable */ + + u8 drrp:4; /* demand read retention priority */ + u8 wrp:4; /* write retention priority */ +#else + u8 rcd:1; /* read cache disable */ + u8 mf:1; /* multiplication factor */ + u8 wce:1; /* write cache enable */ + u8 size:1; /* size enable */ + u8 disc:1; /* discontinuity */ + u8 cap:1; /* caching analysis permitted */ + u8 abpf:1; /* abort pre-fetch */ + u8 ic:1; /* initiator control */ + + u8 wrp:4; /* write retention priority */ + u8 drrp:4; /* demand read retention priority */ +#endif + u8 dptl[2];/* disable pre-fetch transfer length */ + u8 min_prefetch[2]; + u8 max_prefetch[2]; + u8 max_prefetch_limit[2]; +#ifdef __BIGENDIAN + u8 fsw:1; /* force sequential write */ + u8 lbcss:1;/* logical block cache segment size */ + u8 dra:1; /* disable read ahead */ + u8 vs:2; /* vendor specific */ + u8 res2:3; +#else + u8 res2:3; + u8 vs:2; /* vendor specific */ + u8 dra:1; /* disable read ahead */ + u8 lbcss:1;/* logical block cache segment size */ + u8 fsw:1; /* force sequential write */ +#endif + u8 num_cache_segs; + + u8 cache_seg_size[2]; + u8 res3; + u8 non_cache_seg_size[3]; +}; + +/* + * SCSI control mode page + */ +struct scsi_mp_control_page_s{ +#ifdef __BIGENDIAN +u8 ps:1; +u8 reserved1:1; +u8 page_code:6; +#else +u8 page_code:6; +u8 reserved1:1; +u8 ps:1; +#endif + u8 page_len; +#ifdef __BIGENDIAN + u8 tst:3; /* task set type */ + u8 reserved3:3; + u8 gltsd:1; /* global logging target save disable */ + u8 rlec:1; /* report log exception condition */ + + u8 qalgo_mod:4; /* queue alogorithm modifier */ + u8 reserved4:1; + u8 qerr:2; /* queue error management */ + u8 dque:1; /* disable queuing */ + + u8 reserved5:1; + u8 rac:1; /* report a check */ + u8 reserved6:2; + u8 swp:1; /* software write protect */ + u8 raerp:1; /* ready AER permission */ + u8 uaaerp:1; /* unit attenstion AER permission */ + u8 eaerp:1; /* error AER permission */ + + u8 reserved7:5; + u8 autoload_mod:3; +#else + u8 rlec:1; /* report log exception condition */ + u8 gltsd:1; /* global logging target save disable */ + u8 reserved3:3; + u8 tst:3; /* task set type */ + + u8 dque:1; /* disable queuing */ + u8 qerr:2; /* queue error management */ + u8 reserved4:1; + u8 qalgo_mod:4; /* queue alogorithm modifier */ + + u8 eaerp:1; /* error AER permission */ + u8 uaaerp:1; /* unit attenstion AER permission */ + u8 raerp:1; /* ready AER permission */ + u8 swp:1; /* software write protect */ + u8 reserved6:2; + u8 rac:1; /* report a check */ + u8 reserved5:1; + + u8 autoload_mod:3; + u8 reserved7:5; +#endif + u8 rahp_msb; /* ready AER holdoff period - MSB */ + u8 rahp_lsb; /* ready AER holdoff period - LSB */ + + u8 busy_timeout_period_msb; + u8 busy_timeout_period_lsb; + + u8 ext_selftest_compl_time_msb; + u8 ext_selftest_compl_time_lsb; +}; + +/* + * SCSI medium types supported mode page + */ +struct scsi_mp_medium_types_sup_s{ +#ifdef __BIGENDIAN + u8 ps:1; + u8 reserved1:1; + u8 page_code:6; +#else + u8 page_code:6; + u8 reserved1:1; + u8 ps:1; +#endif + u8 page_len; + + u8 reserved3[2]; + u8 med_type1_sup; /* medium type one supported */ + u8 med_type2_sup; /* medium type two supported */ + u8 med_type3_sup; /* medium type three supported */ + u8 med_type4_sup; /* medium type four supported */ +}; + +/* + * SCSI informational exception control mode page + */ +struct scsi_mp_info_excpt_cntl_s{ +#ifdef __BIGENDIAN + u8 ps:1; + u8 reserved1:1; + u8 page_code:6; +#else + u8 page_code:6; + u8 reserved1:1; + u8 ps:1; +#endif + u8 page_len; +#ifdef __BIGENDIAN + u8 perf:1; /* performance */ + u8 reserved3:1; + u8 ebf:1; /* enable background fucntion */ + u8 ewasc:1; /* enable warning */ + u8 dexcpt:1; /* disable exception control */ + u8 test:1; /* enable test device failure + * notification + */ + u8 reserved4:1; + u8 log_error:1; + + u8 reserved5:4; + u8 mrie:4; /* method of reporting info + * exceptions + */ +#else + u8 log_error:1; + u8 reserved4:1; + u8 test:1; /* enable test device failure + * notification + */ + u8 dexcpt:1; /* disable exception control */ + u8 ewasc:1; /* enable warning */ + u8 ebf:1; /* enable background fucntion */ + u8 reserved3:1; + u8 perf:1; /* performance */ + + u8 mrie:4; /* method of reporting info + * exceptions + */ + u8 reserved5:4; +#endif + u8 interval_timer_msb; + u8 interval_timer_lsb; + + u8 report_count_msb; + u8 report_count_lsb; +}; + +/* + * Methods of reporting informational exceptions + */ +#define SCSI_MP_IEC_NO_REPORT 0x0 /* no reporting of exceptions */ +#define SCSI_MP_IEC_AER 0x1 /* async event reporting */ +#define SCSI_MP_IEC_UNIT_ATTN 0x2 /* generate unit attenstion */ +#define SCSI_MO_IEC_COND_REC_ERR 0x3 /* conditionally generate recovered + * error + */ +#define SCSI_MP_IEC_UNCOND_REC_ERR 0x4 /* unconditionally generate recovered + * error + */ +#define SCSI_MP_IEC_NO_SENSE 0x5 /* generate no sense */ +#define SCSI_MP_IEC_ON_REQUEST 0x6 /* only report exceptions on request */ + +/* + * SCSI flexible disk page + */ +struct scsi_mp_flexible_disk_s{ +#ifdef __BIGENDIAN + u8 ps:1; + u8 reserved1:1; + u8 page_code:6; +#else + u8 page_code:6; + u8 reserved1:1; + u8 ps:1; +#endif + u8 page_len; + + u8 transfer_rate_msb; + u8 transfer_rate_lsb; + + u8 num_heads; + u8 num_sectors; + + u8 bytes_per_sector_msb; + u8 bytes_per_sector_lsb; + + u8 num_cylinders_msb; + u8 num_cylinders_lsb; + + u8 sc_wpc_msb; /* starting cylinder-write + * precompensation msb + */ + u8 sc_wpc_lsb; /* starting cylinder-write + * precompensation lsb + */ + u8 sc_rwc_msb; /* starting cylinder-reduced write + * current msb + */ + u8 sc_rwc_lsb; /* starting cylinder-reduced write + * current lsb + */ + + u8 dev_step_rate_msb; + u8 dev_step_rate_lsb; + + u8 dev_step_pulse_width; + + u8 head_sd_msb; /* head settle delay msb */ + u8 head_sd_lsb; /* head settle delay lsb */ + + u8 motor_on_delay; + u8 motor_off_delay; +#ifdef __BIGENDIAN + u8 trdy:1; /* true ready bit */ + u8 ssn:1; /* start sector number bit */ + u8 mo:1; /* motor on bit */ + u8 reserved3:5; + + u8 reserved4:4; + u8 spc:4; /* step pulse per cylinder */ +#else + u8 reserved3:5; + u8 mo:1; /* motor on bit */ + u8 ssn:1; /* start sector number bit */ + u8 trdy:1; /* true ready bit */ + + u8 spc:4; /* step pulse per cylinder */ + u8 reserved4:4; +#endif + u8 write_comp; + u8 head_load_delay; + u8 head_unload_delay; +#ifdef __BIGENDIAN + u8 pin34:4; /* pin34 usage */ + u8 pin2:4; /* pin2 usage */ + + u8 pin4:4; /* pin4 usage */ + u8 pin1:4; /* pin1 usage */ +#else + u8 pin2:4; /* pin2 usage */ + u8 pin34:4; /* pin34 usage */ + + u8 pin1:4; /* pin1 usage */ + u8 pin4:4; /* pin4 usage */ +#endif + u8 med_rot_rate_msb; + u8 med_rot_rate_lsb; + + u8 reserved5[2]; +}; + +struct scsi_mode_page_format_data6_s{ + struct scsi_mode_param_header6_s mph; /* mode page header */ + struct scsi_mode_param_desc_s desc; /* block descriptor */ + struct scsi_mp_format_device_s format; /* format device data */ +}; + +struct scsi_mode_page_format_data10_s{ + struct scsi_mode_param_header10_s mph; /* mode page header */ + struct scsi_mode_param_desc_s desc; /* block descriptor */ + struct scsi_mp_format_device_s format; /* format device data */ +}; + +struct scsi_mode_page_rdg_data6_s{ + struct scsi_mode_param_header6_s mph; /* mode page header */ + struct scsi_mode_param_desc_s desc; /* block descriptor */ + struct scsi_mp_rigid_device_geometry_s rdg; + /* rigid geometry data */ +}; + +struct scsi_mode_page_rdg_data10_s{ + struct scsi_mode_param_header10_s mph; /* mode page header */ + struct scsi_mode_param_desc_s desc; /* block descriptor */ + struct scsi_mp_rigid_device_geometry_s rdg; + /* rigid geometry data */ +}; + +struct scsi_mode_page_cache6_s{ + struct scsi_mode_param_header6_s mph; /* mode page header */ + struct scsi_mode_param_desc_s desc; /* block descriptor */ + struct scsi_mp_caching_s cache; /* cache page data */ +}; + +struct scsi_mode_page_cache10_s{ + struct scsi_mode_param_header10_s mph; /* mode page header */ + struct scsi_mode_param_desc_s desc; /* block descriptor */ + struct scsi_mp_caching_s cache; /* cache page data */ +}; + +/* -------------------------------------------------------------- + * Format Unit command + * ------------------------------------------------------------ + */ + +/* + * Format Unit CDB + */ +struct scsi_format_unit_s{ + u8 opcode; +#ifdef __BIGENDIAN + u8 res1:3; + u8 fmtdata:1; /* if set, data out phase has format + * data + */ + u8 cmplst:1; /* if set, defect list is complete */ + u8 def_list:3; /* format of defect descriptor is + * fmtdata =1 + */ +#else + u8 def_list:3; /* format of defect descriptor is + * fmtdata = 1 + */ + u8 cmplst:1; /* if set, defect list is complete */ + u8 fmtdata:1; /* if set, data out phase has format + * data + */ + u8 res1:3; +#endif + u8 interleave_msb; + u8 interleave_lsb; + u8 vendor_spec; + u8 control; +}; + +/* + * h + */ +struct scsi_reserve6_s{ + u8 opcode; +#ifdef __BIGENDIAN + u8 reserved:3; + u8 obsolete:4; + u8 extent:1; +#else + u8 extent:1; + u8 obsolete:4; + u8 reserved:3; +#endif + u8 reservation_id; + u16 param_list_len; + u8 control; +}; + +/* + * h + */ +struct scsi_release6_s{ + u8 opcode; +#ifdef __BIGENDIAN + u8 reserved1:3; + u8 obsolete:4; + u8 extent:1; +#else + u8 extent:1; + u8 obsolete:4; + u8 reserved1:3; +#endif + u8 reservation_id; + u16 reserved2; + u8 control; +}; + +/* + * h + */ +struct scsi_reserve10_s{ + u8 opcode; +#ifdef __BIGENDIAN + u8 reserved1:3; + u8 third_party:1; + u8 reserved2:2; + u8 long_id:1; + u8 extent:1; +#else + u8 extent:1; + u8 long_id:1; + u8 reserved2:2; + u8 third_party:1; + u8 reserved1:3; +#endif + u8 reservation_id; + u8 third_pty_dev_id; + u8 reserved3; + u8 reserved4; + u8 reserved5; + u16 param_list_len; + u8 control; +}; + +struct scsi_release10_s{ + u8 opcode; +#ifdef __BIGENDIAN + u8 reserved1:3; + u8 third_party:1; + u8 reserved2:2; + u8 long_id:1; + u8 extent:1; +#else + u8 extent:1; + u8 long_id:1; + u8 reserved2:2; + u8 third_party:1; + u8 reserved1:3; +#endif + u8 reservation_id; + u8 third_pty_dev_id; + u8 reserved3; + u8 reserved4; + u8 reserved5; + u16 param_list_len; + u8 control; +}; + +struct scsi_verify10_s{ + u8 opcode; +#ifdef __BIGENDIAN + u8 lun:3; + u8 dpo:1; + u8 reserved:2; + u8 bytchk:1; + u8 reladdr:1; +#else + u8 reladdr:1; + u8 bytchk:1; + u8 reserved:2; + u8 dpo:1; + u8 lun:3; +#endif + u8 lba0; + u8 lba1; + u8 lba2; + u8 lba3; + u8 reserved1; + u8 verification_len0; + u8 verification_len1; + u8 control_byte; +}; + +struct scsi_request_sense_s{ + u8 opcode; +#ifdef __BIGENDIAN + u8 lun:3; + u8 reserved:5; +#else + u8 reserved:5; + u8 lun:3; +#endif + u8 reserved0; + u8 reserved1; + u8 alloc_len; + u8 control_byte; +}; + +/* ------------------------------------------------------------ + * SCSI status byte values + * ------------------------------------------------------------ + */ +#define SCSI_STATUS_GOOD 0x00 +#define SCSI_STATUS_CHECK_CONDITION 0x02 +#define SCSI_STATUS_CONDITION_MET 0x04 +#define SCSI_STATUS_BUSY 0x08 +#define SCSI_STATUS_INTERMEDIATE 0x10 +#define SCSI_STATUS_ICM 0x14 /* intermediate condition met */ +#define SCSI_STATUS_RESERVATION_CONFLICT 0x18 +#define SCSI_STATUS_COMMAND_TERMINATED 0x22 +#define SCSI_STATUS_QUEUE_FULL 0x28 +#define SCSI_STATUS_ACA_ACTIVE 0x30 + +#define SCSI_MAX_ALLOC_LEN 0xFF /* maximum allocarion length + * in CDBs + */ + +#define SCSI_OP_WRITE_VERIFY10 0x2E +#define SCSI_OP_WRITE_VERIFY12 0xAE +#define SCSI_OP_UNDEF 0xFF + +/* + * SCSI WRITE-VERIFY(10) command + */ +struct scsi_write_verify10_s{ + u8 opcode; +#ifdef __BIGENDIAN + u8 reserved1:3; + u8 dpo:1; /* Disable Page Out */ + u8 reserved2:1; + u8 ebp:1; /* erse by-pass */ + u8 bytchk:1; /* byte check */ + u8 rel_adr:1; /* relative address */ +#else + u8 rel_adr:1; /* relative address */ + u8 bytchk:1; /* byte check */ + u8 ebp:1; /* erse by-pass */ + u8 reserved2:1; + u8 dpo:1; /* Disable Page Out */ + u8 reserved1:3; +#endif + u8 lba0; /* logical block address - MSB */ + u8 lba1; + u8 lba2; + u8 lba3; /* LSB */ + u8 reserved3; + u8 xfer_length0; /* transfer length in blocks - MSB */ + u8 xfer_length1; /* LSB */ + u8 control; +}; + +#pragma pack() + +#endif /* __SCSI_H__ */ diff --git a/drivers/scsi/bfa/include/protocol/types.h b/drivers/scsi/bfa/include/protocol/types.h new file mode 100644 index 000000000000..2875a6cced3b --- /dev/null +++ b/drivers/scsi/bfa/include/protocol/types.h @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2005-2009 Brocade Communications Systems, Inc. + * All rights reserved + * www.brocade.com + * + * Linux driver for Brocade Fibre Channel Host Bus Adapter. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (GPL) Version 2 as + * published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +/** + * types.h Protocol defined base types + */ + +#ifndef __TYPES_H__ +#define __TYPES_H__ + +#include <bfa_os_inc.h> + +#define wwn_t u64 +#define lun_t u64 + +#define WWN_NULL (0) +#define FC_SYMNAME_MAX 256 /* max name server symbolic name size */ +#define FC_ALPA_MAX 128 + +#pragma pack(1) + +#define MAC_ADDRLEN (6) +struct mac_s { u8 mac[MAC_ADDRLEN]; }; +#define mac_t struct mac_s + +#pragma pack() + +#endif diff --git a/drivers/scsi/bfa/loop.c b/drivers/scsi/bfa/loop.c new file mode 100644 index 000000000000..a418dedebe9e --- /dev/null +++ b/drivers/scsi/bfa/loop.c @@ -0,0 +1,422 @@ +/* + * Copyright (c) 2005-2009 Brocade Communications Systems, Inc. + * All rights reserved + * www.brocade.com + * + * Linux driver for Brocade Fibre Channel Host Bus Adapter. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (GPL) Version 2 as + * published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +/** + * port_loop.c vport private loop implementation. + */ +#include <bfa.h> +#include <bfa_svc.h> +#include "fcs_lport.h" +#include "fcs_rport.h" +#include "fcs_trcmod.h" +#include "lport_priv.h" + +BFA_TRC_FILE(FCS, LOOP); + +/** + * ALPA to LIXA bitmap mapping + * + * ALPA 0x00 (Word 0, Bit 30) is invalid for N_Ports. Also Word 0 Bit 31 + * is for L_bit (login required) and is filled as ALPA 0x00 here. + */ +static const u8 port_loop_alpa_map[] = { + 0xEF, 0xE8, 0xE4, 0xE2, 0xE1, 0xE0, 0xDC, 0xDA, /* Word 3 Bits 0..7 */ + 0xD9, 0xD6, 0xD5, 0xD4, 0xD3, 0xD2, 0xD1, 0xCE, /* Word 3 Bits 8..15 */ + 0xCD, 0xCC, 0xCB, 0xCA, 0xC9, 0xC7, 0xC6, 0xC5, /* Word 3 Bits 16..23 */ + 0xC3, 0xBC, 0xBA, 0xB9, 0xB6, 0xB5, 0xB4, 0xB3, /* Word 3 Bits 24..31 */ + + 0xB2, 0xB1, 0xAE, 0xAD, 0xAC, 0xAB, 0xAA, 0xA9, /* Word 2 Bits 0..7 */ + 0xA7, 0xA6, 0xA5, 0xA3, 0x9F, 0x9E, 0x9D, 0x9B, /* Word 2 Bits 8..15 */ + 0x98, 0x97, 0x90, 0x8F, 0x88, 0x84, 0x82, 0x81, /* Word 2 Bits 16..23 */ + 0x80, 0x7C, 0x7A, 0x79, 0x76, 0x75, 0x74, 0x73, /* Word 2 Bits 24..31 */ + + 0x72, 0x71, 0x6E, 0x6D, 0x6C, 0x6B, 0x6A, 0x69, /* Word 1 Bits 0..7 */ + 0x67, 0x66, 0x65, 0x63, 0x5C, 0x5A, 0x59, 0x56, /* Word 1 Bits 8..15 */ + 0x55, 0x54, 0x53, 0x52, 0x51, 0x4E, 0x4D, 0x4C, /* Word 1 Bits 16..23 */ + 0x4B, 0x4A, 0x49, 0x47, 0x46, 0x45, 0x43, 0x3C, /* Word 1 Bits 24..31 */ + + 0x3A, 0x39, 0x36, 0x35, 0x34, 0x33, 0x32, 0x31, /* Word 0 Bits 0..7 */ + 0x2E, 0x2D, 0x2C, 0x2B, 0x2A, 0x29, 0x27, 0x26, /* Word 0 Bits 8..15 */ + 0x25, 0x23, 0x1F, 0x1E, 0x1D, 0x1B, 0x18, 0x17, /* Word 0 Bits 16..23 */ + 0x10, 0x0F, 0x08, 0x04, 0x02, 0x01, 0x00, 0x00, /* Word 0 Bits 24..31 */ +}; + +/* + * Local Functions + */ +bfa_status_t bfa_fcs_port_loop_send_plogi(struct bfa_fcs_port_s *port, + u8 alpa); + +void bfa_fcs_port_loop_plogi_response(void *fcsarg, + struct bfa_fcxp_s *fcxp, + void *cbarg, + bfa_status_t req_status, + u32 rsp_len, + u32 resid_len, + struct fchs_s *rsp_fchs); + +bfa_status_t bfa_fcs_port_loop_send_adisc(struct bfa_fcs_port_s *port, + u8 alpa); + +void bfa_fcs_port_loop_adisc_response(void *fcsarg, + struct bfa_fcxp_s *fcxp, + void *cbarg, + bfa_status_t req_status, + u32 rsp_len, + u32 resid_len, + struct fchs_s *rsp_fchs); + +bfa_status_t bfa_fcs_port_loop_send_plogi_acc(struct bfa_fcs_port_s *port, + u8 alpa); + +void bfa_fcs_port_loop_plogi_acc_response(void *fcsarg, + struct bfa_fcxp_s *fcxp, + void *cbarg, + bfa_status_t req_status, + u32 rsp_len, + u32 resid_len, + struct fchs_s *rsp_fchs); + +bfa_status_t bfa_fcs_port_loop_send_adisc_acc(struct bfa_fcs_port_s *port, + u8 alpa); + +void bfa_fcs_port_loop_adisc_acc_response(void *fcsarg, + struct bfa_fcxp_s *fcxp, + void *cbarg, + bfa_status_t req_status, + u32 rsp_len, + u32 resid_len, + struct fchs_s *rsp_fchs); +/** + * Called by port to initializar in provate LOOP topology. + */ +void +bfa_fcs_port_loop_init(struct bfa_fcs_port_s *port) +{ +} + +/** + * Called by port to notify transition to online state. + */ +void +bfa_fcs_port_loop_online(struct bfa_fcs_port_s *port) +{ + + u8 num_alpa = port->port_topo.ploop.num_alpa; + u8 *alpa_pos_map = port->port_topo.ploop.alpa_pos_map; + struct bfa_fcs_rport_s *r_port; + int ii = 0; + + /* + * If the port role is Initiator Mode, create Rports. + */ + if (port->port_cfg.roles == BFA_PORT_ROLE_FCP_IM) { + /* + * Check if the ALPA positional bitmap is available. + * if not, we send PLOGI to all possible ALPAs. + */ + if (num_alpa > 0) { + for (ii = 0; ii < num_alpa; ii++) { + /* + * ignore ALPA of bfa port + */ + if (alpa_pos_map[ii] != port->pid) { + r_port = bfa_fcs_rport_create(port, + alpa_pos_map[ii]); + } + } + } else { + for (ii = 0; ii < MAX_ALPA_COUNT; ii++) { + /* + * ignore ALPA of bfa port + */ + if ((port_loop_alpa_map[ii] > 0) + && (port_loop_alpa_map[ii] != port->pid)) + bfa_fcs_port_loop_send_plogi(port, + port_loop_alpa_map[ii]); + /**TBD */ + } + } + } else { + /* + * TBD Target Mode ?? + */ + } + +} + +/** + * Called by port to notify transition to offline state. + */ +void +bfa_fcs_port_loop_offline(struct bfa_fcs_port_s *port) +{ + +} + +/** + * Called by port to notify a LIP on the loop. + */ +void +bfa_fcs_port_loop_lip(struct bfa_fcs_port_s *port) +{ +} + +/** + * Local Functions. + */ +bfa_status_t +bfa_fcs_port_loop_send_plogi(struct bfa_fcs_port_s *port, u8 alpa) +{ + struct fchs_s fchs; + struct bfa_fcxp_s *fcxp = NULL; + int len; + + bfa_trc(port->fcs, alpa); + + fcxp = bfa_fcxp_alloc(NULL, port->fcs->bfa, 0, 0, NULL, NULL, NULL, + NULL); + bfa_assert(fcxp); + + len = fc_plogi_build(&fchs, bfa_fcxp_get_reqbuf(fcxp), alpa, + bfa_fcs_port_get_fcid(port), 0, + port->port_cfg.pwwn, port->port_cfg.nwwn, + bfa_pport_get_maxfrsize(port->fcs->bfa)); + + bfa_fcxp_send(fcxp, NULL, port->fabric->vf_id, port->lp_tag, BFA_FALSE, + FC_CLASS_3, len, &fchs, + bfa_fcs_port_loop_plogi_response, (void *)port, + FC_MAX_PDUSZ, FC_RA_TOV); + + return BFA_STATUS_OK; +} + +/** + * Called by fcxp to notify the Plogi response + */ +void +bfa_fcs_port_loop_plogi_response(void *fcsarg, struct bfa_fcxp_s *fcxp, + void *cbarg, bfa_status_t req_status, + u32 rsp_len, u32 resid_len, + struct fchs_s *rsp_fchs) +{ + struct bfa_fcs_port_s *port = (struct bfa_fcs_port_s *) cbarg; + struct fc_logi_s *plogi_resp; + struct fc_els_cmd_s *els_cmd; + + bfa_trc(port->fcs, req_status); + + /* + * Sanity Checks + */ + if (req_status != BFA_STATUS_OK) { + bfa_trc(port->fcs, req_status); + /* + * @todo + * This could mean that the device with this APLA does not + * exist on the loop. + */ + + return; + } + + els_cmd = (struct fc_els_cmd_s *) BFA_FCXP_RSP_PLD(fcxp); + plogi_resp = (struct fc_logi_s *) els_cmd; + + if (els_cmd->els_code == FC_ELS_ACC) { + bfa_fcs_rport_start(port, rsp_fchs, plogi_resp); + } else { + bfa_trc(port->fcs, plogi_resp->els_cmd.els_code); + bfa_assert(0); + } +} + +bfa_status_t +bfa_fcs_port_loop_send_plogi_acc(struct bfa_fcs_port_s *port, u8 alpa) +{ + struct fchs_s fchs; + struct bfa_fcxp_s *fcxp; + int len; + + bfa_trc(port->fcs, alpa); + + fcxp = bfa_fcxp_alloc(NULL, port->fcs->bfa, 0, 0, NULL, NULL, NULL, + NULL); + bfa_assert(fcxp); + + len = fc_plogi_acc_build(&fchs, bfa_fcxp_get_reqbuf(fcxp), alpa, + bfa_fcs_port_get_fcid(port), 0, + port->port_cfg.pwwn, port->port_cfg.nwwn, + bfa_pport_get_maxfrsize(port->fcs->bfa)); + + bfa_fcxp_send(fcxp, NULL, port->fabric->vf_id, port->lp_tag, BFA_FALSE, + FC_CLASS_3, len, &fchs, + bfa_fcs_port_loop_plogi_acc_response, + (void *)port, FC_MAX_PDUSZ, 0); /* No response + * expected + */ + + return BFA_STATUS_OK; +} + +/* + * Plogi Acc Response + * We donot do any processing here. + */ +void +bfa_fcs_port_loop_plogi_acc_response(void *fcsarg, struct bfa_fcxp_s *fcxp, + void *cbarg, bfa_status_t req_status, + u32 rsp_len, u32 resid_len, + struct fchs_s *rsp_fchs) +{ + + struct bfa_fcs_port_s *port = (struct bfa_fcs_port_s *) cbarg; + + bfa_trc(port->fcs, port->pid); + + /* + * Sanity Checks + */ + if (req_status != BFA_STATUS_OK) { + bfa_trc(port->fcs, req_status); + return; + } +} + +bfa_status_t +bfa_fcs_port_loop_send_adisc(struct bfa_fcs_port_s *port, u8 alpa) +{ + struct fchs_s fchs; + struct bfa_fcxp_s *fcxp; + int len; + + bfa_trc(port->fcs, alpa); + + fcxp = bfa_fcxp_alloc(NULL, port->fcs->bfa, 0, 0, NULL, NULL, NULL, + NULL); + bfa_assert(fcxp); + + len = fc_adisc_build(&fchs, bfa_fcxp_get_reqbuf(fcxp), alpa, + bfa_fcs_port_get_fcid(port), 0, + port->port_cfg.pwwn, port->port_cfg.nwwn); + + bfa_fcxp_send(fcxp, NULL, port->fabric->vf_id, port->lp_tag, BFA_FALSE, + FC_CLASS_3, len, &fchs, + bfa_fcs_port_loop_adisc_response, (void *)port, + FC_MAX_PDUSZ, FC_RA_TOV); + + return BFA_STATUS_OK; +} + +/** + * Called by fcxp to notify the ADISC response + */ +void +bfa_fcs_port_loop_adisc_response(void *fcsarg, struct bfa_fcxp_s *fcxp, + void *cbarg, bfa_status_t req_status, + u32 rsp_len, u32 resid_len, + struct fchs_s *rsp_fchs) +{ + struct bfa_fcs_port_s *port = (struct bfa_fcs_port_s *) cbarg; + struct bfa_fcs_rport_s *rport; + struct fc_adisc_s *adisc_resp; + struct fc_els_cmd_s *els_cmd; + u32 pid = rsp_fchs->s_id; + + bfa_trc(port->fcs, req_status); + + /* + * Sanity Checks + */ + if (req_status != BFA_STATUS_OK) { + /* + * TBD : we may need to retry certain requests + */ + bfa_fcxp_free(fcxp); + return; + } + + els_cmd = (struct fc_els_cmd_s *) BFA_FCXP_RSP_PLD(fcxp); + adisc_resp = (struct fc_adisc_s *) els_cmd; + + if (els_cmd->els_code == FC_ELS_ACC) { + } else { + bfa_trc(port->fcs, adisc_resp->els_cmd.els_code); + + /* + * TBD: we may need to check for reject codes and retry + */ + rport = bfa_fcs_port_get_rport_by_pid(port, pid); + if (rport) { + list_del(&rport->qe); + bfa_fcs_rport_delete(rport); + } + + } + return; +} + +bfa_status_t +bfa_fcs_port_loop_send_adisc_acc(struct bfa_fcs_port_s *port, u8 alpa) +{ + struct fchs_s fchs; + struct bfa_fcxp_s *fcxp; + int len; + + bfa_trc(port->fcs, alpa); + + fcxp = bfa_fcxp_alloc(NULL, port->fcs->bfa, 0, 0, NULL, NULL, NULL, + NULL); + bfa_assert(fcxp); + + len = fc_adisc_acc_build(&fchs, bfa_fcxp_get_reqbuf(fcxp), alpa, + bfa_fcs_port_get_fcid(port), 0, + port->port_cfg.pwwn, port->port_cfg.nwwn); + + bfa_fcxp_send(fcxp, NULL, port->fabric->vf_id, port->lp_tag, BFA_FALSE, + FC_CLASS_3, len, &fchs, + bfa_fcs_port_loop_adisc_acc_response, + (void *)port, FC_MAX_PDUSZ, 0); /* no reponse + * expected + */ + + return BFA_STATUS_OK; +} + +/* + * Adisc Acc Response + * We donot do any processing here. + */ +void +bfa_fcs_port_loop_adisc_acc_response(void *fcsarg, struct bfa_fcxp_s *fcxp, + void *cbarg, bfa_status_t req_status, + u32 rsp_len, u32 resid_len, + struct fchs_s *rsp_fchs) +{ + + struct bfa_fcs_port_s *port = (struct bfa_fcs_port_s *) cbarg; + + bfa_trc(port->fcs, port->pid); + + /* + * Sanity Checks + */ + if (req_status != BFA_STATUS_OK) { + bfa_trc(port->fcs, req_status); + return; + } +} diff --git a/drivers/scsi/bfa/lport_api.c b/drivers/scsi/bfa/lport_api.c new file mode 100644 index 000000000000..8f51a83f1834 --- /dev/null +++ b/drivers/scsi/bfa/lport_api.c @@ -0,0 +1,291 @@ +/* + * Copyright (c) 2005-2009 Brocade Communications Systems, Inc. + * All rights reserved + * www.brocade.com + * + * Linux driver for Brocade Fibre Channel Host Bus Adapter. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (GPL) Version 2 as + * published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +/** + * port_api.c BFA FCS port + */ + +#include <fcs/bfa_fcs.h> +#include <fcs/bfa_fcs_lport.h> +#include <fcs/bfa_fcs_rport.h> +#include "fcs_rport.h" +#include "fcs_fabric.h" +#include "fcs_trcmod.h" +#include "fcs_vport.h" + +BFA_TRC_FILE(FCS, PORT_API); + + + +/** + * fcs_port_api BFA FCS port API + */ + +void +bfa_fcs_cfg_base_port(struct bfa_fcs_s *fcs, struct bfa_port_cfg_s *port_cfg) +{ +} + +struct bfa_fcs_port_s * +bfa_fcs_get_base_port(struct bfa_fcs_s *fcs) +{ + return (&fcs->fabric.bport); +} + +wwn_t +bfa_fcs_port_get_rport(struct bfa_fcs_port_s *port, wwn_t wwn, int index, + int nrports, bfa_boolean_t bwwn) +{ + struct list_head *qh, *qe; + struct bfa_fcs_rport_s *rport = NULL; + int i; + struct bfa_fcs_s *fcs; + + if (port == NULL || nrports == 0) + return (wwn_t) 0; + + fcs = port->fcs; + bfa_trc(fcs, (u32) nrports); + + i = 0; + qh = &port->rport_q; + qe = bfa_q_first(qh); + + while ((qe != qh) && (i < nrports)) { + rport = (struct bfa_fcs_rport_s *)qe; + if (bfa_os_ntoh3b(rport->pid) > 0xFFF000) { + qe = bfa_q_next(qe); + bfa_trc(fcs, (u32) rport->pwwn); + bfa_trc(fcs, rport->pid); + bfa_trc(fcs, i); + continue; + } + + if (bwwn) { + if (!memcmp(&wwn, &rport->pwwn, 8)) + break; + } else { + if (i == index) + break; + } + + i++; + qe = bfa_q_next(qe); + } + + bfa_trc(fcs, i); + if (rport) { + return rport->pwwn; + } else { + return (wwn_t) 0; + } +} + +void +bfa_fcs_port_get_rports(struct bfa_fcs_port_s *port, wwn_t rport_wwns[], + int *nrports) +{ + struct list_head *qh, *qe; + struct bfa_fcs_rport_s *rport = NULL; + int i; + struct bfa_fcs_s *fcs; + + if (port == NULL || rport_wwns == NULL || *nrports == 0) + return; + + fcs = port->fcs; + bfa_trc(fcs, (u32) *nrports); + + i = 0; + qh = &port->rport_q; + qe = bfa_q_first(qh); + + while ((qe != qh) && (i < *nrports)) { + rport = (struct bfa_fcs_rport_s *)qe; + if (bfa_os_ntoh3b(rport->pid) > 0xFFF000) { + qe = bfa_q_next(qe); + bfa_trc(fcs, (u32) rport->pwwn); + bfa_trc(fcs, rport->pid); + bfa_trc(fcs, i); + continue; + } + + rport_wwns[i] = rport->pwwn; + + i++; + qe = bfa_q_next(qe); + } + + bfa_trc(fcs, i); + *nrports = i; + return; +} + +/* + * Iterate's through all the rport's in the given port to + * determine the maximum operating speed. + */ +enum bfa_pport_speed +bfa_fcs_port_get_rport_max_speed(struct bfa_fcs_port_s *port) +{ + struct list_head *qh, *qe; + struct bfa_fcs_rport_s *rport = NULL; + struct bfa_fcs_s *fcs; + enum bfa_pport_speed max_speed = 0; + struct bfa_pport_attr_s pport_attr; + enum bfa_pport_speed pport_speed; + + if (port == NULL) + return 0; + + fcs = port->fcs; + + /* + * Get Physical port's current speed + */ + bfa_pport_get_attr(port->fcs->bfa, &pport_attr); + pport_speed = pport_attr.speed; + bfa_trc(fcs, pport_speed); + + qh = &port->rport_q; + qe = bfa_q_first(qh); + + while (qe != qh) { + rport = (struct bfa_fcs_rport_s *)qe; + if ((bfa_os_ntoh3b(rport->pid) > 0xFFF000) + || (bfa_fcs_rport_get_state(rport) == BFA_RPORT_OFFLINE)) { + qe = bfa_q_next(qe); + continue; + } + + if ((rport->rpf.rpsc_speed == BFA_PPORT_SPEED_8GBPS) + || (rport->rpf.rpsc_speed > pport_speed)) { + max_speed = rport->rpf.rpsc_speed; + break; + } else if (rport->rpf.rpsc_speed > max_speed) { + max_speed = rport->rpf.rpsc_speed; + } + + qe = bfa_q_next(qe); + } + + bfa_trc(fcs, max_speed); + return max_speed; +} + +struct bfa_fcs_port_s * +bfa_fcs_lookup_port(struct bfa_fcs_s *fcs, u16 vf_id, wwn_t lpwwn) +{ + struct bfa_fcs_vport_s *vport; + bfa_fcs_vf_t *vf; + + bfa_assert(fcs != NULL); + + vf = bfa_fcs_vf_lookup(fcs, vf_id); + if (vf == NULL) { + bfa_trc(fcs, vf_id); + return (NULL); + } + + if (!lpwwn || (vf->bport.port_cfg.pwwn == lpwwn)) + return (&vf->bport); + + vport = bfa_fcs_fabric_vport_lookup(vf, lpwwn); + if (vport) + return (&vport->lport); + + return (NULL); +} + +/* + * API corresponding to VmWare's NPIV_VPORT_GETINFO. + */ +void +bfa_fcs_port_get_info(struct bfa_fcs_port_s *port, + struct bfa_port_info_s *port_info) +{ + + bfa_trc(port->fcs, port->fabric->fabric_name); + + if (port->vport == NULL) { + /* + * This is a Physical port + */ + port_info->port_type = BFA_PORT_TYPE_PHYSICAL; + + /* + * @todo : need to fix the state & reason + */ + port_info->port_state = 0; + port_info->offline_reason = 0; + + port_info->port_wwn = bfa_fcs_port_get_pwwn(port); + port_info->node_wwn = bfa_fcs_port_get_nwwn(port); + + port_info->max_vports_supp = bfa_fcs_vport_get_max(port->fcs); + port_info->num_vports_inuse = + bfa_fcs_fabric_vport_count(port->fabric); + port_info->max_rports_supp = BFA_FCS_MAX_RPORTS_SUPP; + port_info->num_rports_inuse = port->num_rports; + } else { + /* + * This is a virtual port + */ + port_info->port_type = BFA_PORT_TYPE_VIRTUAL; + + /* + * @todo : need to fix the state & reason + */ + port_info->port_state = 0; + port_info->offline_reason = 0; + + port_info->port_wwn = bfa_fcs_port_get_pwwn(port); + port_info->node_wwn = bfa_fcs_port_get_nwwn(port); + } +} + +void +bfa_fcs_port_get_stats(struct bfa_fcs_port_s *fcs_port, + struct bfa_port_stats_s *port_stats) +{ + bfa_os_memcpy(port_stats, &fcs_port->stats, + sizeof(struct bfa_port_stats_s)); + return; +} + +void +bfa_fcs_port_clear_stats(struct bfa_fcs_port_s *fcs_port) +{ + bfa_os_memset(&fcs_port->stats, 0, sizeof(struct bfa_port_stats_s)); + return; +} + +void +bfa_fcs_port_enable_ipfc_roles(struct bfa_fcs_port_s *fcs_port) +{ + fcs_port->port_cfg.roles |= BFA_PORT_ROLE_FCP_IPFC; + return; +} + +void +bfa_fcs_port_disable_ipfc_roles(struct bfa_fcs_port_s *fcs_port) +{ + fcs_port->port_cfg.roles &= ~BFA_PORT_ROLE_FCP_IPFC; + return; +} + + diff --git a/drivers/scsi/bfa/lport_priv.h b/drivers/scsi/bfa/lport_priv.h new file mode 100644 index 000000000000..dbae370a599a --- /dev/null +++ b/drivers/scsi/bfa/lport_priv.h @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2005-2009 Brocade Communications Systems, Inc. + * All rights reserved + * www.brocade.com + * + * Linux driver for Brocade Fibre Channel Host Bus Adapter. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (GPL) Version 2 as + * published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#ifndef __VP_PRIV_H__ +#define __VP_PRIV_H__ + +#include <fcs/bfa_fcs_lport.h> +#include <fcs/bfa_fcs_vport.h> + +/* + * Functions exported by vps + */ +void bfa_fcs_vport_init(struct bfa_fcs_vport_s *vport); + +/* + * Functions exported by vps + */ +void bfa_fcs_vps_online(struct bfa_fcs_port_s *port); +void bfa_fcs_vps_offline(struct bfa_fcs_port_s *port); +void bfa_fcs_vps_lip(struct bfa_fcs_port_s *port); + +/* + * Functions exported by port_fab + */ +void bfa_fcs_port_fab_init(struct bfa_fcs_port_s *vport); +void bfa_fcs_port_fab_online(struct bfa_fcs_port_s *vport); +void bfa_fcs_port_fab_offline(struct bfa_fcs_port_s *vport); +void bfa_fcs_port_fab_rx_frame(struct bfa_fcs_port_s *port, + u8 *rx_frame, u32 len); + +/* + * Functions exported by VP-NS. + */ +void bfa_fcs_port_ns_init(struct bfa_fcs_port_s *vport); +void bfa_fcs_port_ns_offline(struct bfa_fcs_port_s *vport); +void bfa_fcs_port_ns_online(struct bfa_fcs_port_s *vport); +void bfa_fcs_port_ns_query(struct bfa_fcs_port_s *port); + +/* + * Functions exported by VP-SCN + */ +void bfa_fcs_port_scn_init(struct bfa_fcs_port_s *vport); +void bfa_fcs_port_scn_offline(struct bfa_fcs_port_s *vport); +void bfa_fcs_port_scn_online(struct bfa_fcs_port_s *vport); +void bfa_fcs_port_scn_process_rscn(struct bfa_fcs_port_s *port, + struct fchs_s *rx_frame, u32 len); + +/* + * Functions exported by VP-N2N + */ + +void bfa_fcs_port_n2n_init(struct bfa_fcs_port_s *port); +void bfa_fcs_port_n2n_online(struct bfa_fcs_port_s *port); +void bfa_fcs_port_n2n_offline(struct bfa_fcs_port_s *port); +void bfa_fcs_port_n2n_rx_frame(struct bfa_fcs_port_s *port, + u8 *rx_frame, u32 len); + +/* + * Functions exported by VP-LOOP + */ +void bfa_fcs_port_loop_init(struct bfa_fcs_port_s *port); +void bfa_fcs_port_loop_online(struct bfa_fcs_port_s *port); +void bfa_fcs_port_loop_offline(struct bfa_fcs_port_s *port); +void bfa_fcs_port_loop_lip(struct bfa_fcs_port_s *port); +void bfa_fcs_port_loop_rx_frame(struct bfa_fcs_port_s *port, + u8 *rx_frame, u32 len); + +#endif /* __VP_PRIV_H__ */ diff --git a/drivers/scsi/bfa/ms.c b/drivers/scsi/bfa/ms.c new file mode 100644 index 000000000000..c96b3ca007ae --- /dev/null +++ b/drivers/scsi/bfa/ms.c @@ -0,0 +1,759 @@ +/* + * Copyright (c) 2005-2009 Brocade Communications Systems, Inc. + * All rights reserved + * www.brocade.com + * + * Linux driver for Brocade Fibre Channel Host Bus Adapter. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (GPL) Version 2 as + * published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + + +#include <bfa.h> +#include <bfa_svc.h> +#include "fcs_lport.h" +#include "fcs_rport.h" +#include "fcs_trcmod.h" +#include "fcs_fcxp.h" +#include "lport_priv.h" + +BFA_TRC_FILE(FCS, MS); + +#define BFA_FCS_MS_CMD_MAX_RETRIES 2 +/* + * forward declarations + */ +static void bfa_fcs_port_ms_send_plogi(void *ms_cbarg, + struct bfa_fcxp_s *fcxp_alloced); +static void bfa_fcs_port_ms_timeout(void *arg); +static void bfa_fcs_port_ms_plogi_response(void *fcsarg, + struct bfa_fcxp_s *fcxp, + void *cbarg, + bfa_status_t req_status, + u32 rsp_len, + u32 resid_len, + struct fchs_s *rsp_fchs); + +static void bfa_fcs_port_ms_send_gmal(void *ms_cbarg, + struct bfa_fcxp_s *fcxp_alloced); +static void bfa_fcs_port_ms_gmal_response(void *fcsarg, + struct bfa_fcxp_s *fcxp, + void *cbarg, + bfa_status_t req_status, + u32 rsp_len, + u32 resid_len, + struct fchs_s *rsp_fchs); +static void bfa_fcs_port_ms_send_gfn(void *ms_cbarg, + struct bfa_fcxp_s *fcxp_alloced); +static void bfa_fcs_port_ms_gfn_response(void *fcsarg, + struct bfa_fcxp_s *fcxp, + void *cbarg, + bfa_status_t req_status, + u32 rsp_len, + u32 resid_len, + struct fchs_s *rsp_fchs); +/** + * fcs_ms_sm FCS MS state machine + */ + +/** + * MS State Machine events + */ +enum port_ms_event { + MSSM_EVENT_PORT_ONLINE = 1, + MSSM_EVENT_PORT_OFFLINE = 2, + MSSM_EVENT_RSP_OK = 3, + MSSM_EVENT_RSP_ERROR = 4, + MSSM_EVENT_TIMEOUT = 5, + MSSM_EVENT_FCXP_SENT = 6, + MSSM_EVENT_PORT_FABRIC_RSCN = 7 +}; + +static void bfa_fcs_port_ms_sm_offline(struct bfa_fcs_port_ms_s *ms, + enum port_ms_event event); +static void bfa_fcs_port_ms_sm_plogi_sending(struct bfa_fcs_port_ms_s *ms, + enum port_ms_event event); +static void bfa_fcs_port_ms_sm_plogi(struct bfa_fcs_port_ms_s *ms, + enum port_ms_event event); +static void bfa_fcs_port_ms_sm_plogi_retry(struct bfa_fcs_port_ms_s *ms, + enum port_ms_event event); +static void bfa_fcs_port_ms_sm_gmal_sending(struct bfa_fcs_port_ms_s *ms, + enum port_ms_event event); +static void bfa_fcs_port_ms_sm_gmal(struct bfa_fcs_port_ms_s *ms, + enum port_ms_event event); +static void bfa_fcs_port_ms_sm_gmal_retry(struct bfa_fcs_port_ms_s *ms, + enum port_ms_event event); +static void bfa_fcs_port_ms_sm_gfn_sending(struct bfa_fcs_port_ms_s *ms, + enum port_ms_event event); +static void bfa_fcs_port_ms_sm_gfn(struct bfa_fcs_port_ms_s *ms, + enum port_ms_event event); +static void bfa_fcs_port_ms_sm_gfn_retry(struct bfa_fcs_port_ms_s *ms, + enum port_ms_event event); +static void bfa_fcs_port_ms_sm_online(struct bfa_fcs_port_ms_s *ms, + enum port_ms_event event); +/** + * Start in offline state - awaiting NS to send start. + */ +static void +bfa_fcs_port_ms_sm_offline(struct bfa_fcs_port_ms_s *ms, + enum port_ms_event event) +{ + bfa_trc(ms->port->fcs, ms->port->port_cfg.pwwn); + bfa_trc(ms->port->fcs, event); + + switch (event) { + case MSSM_EVENT_PORT_ONLINE: + bfa_sm_set_state(ms, bfa_fcs_port_ms_sm_plogi_sending); + bfa_fcs_port_ms_send_plogi(ms, NULL); + break; + + case MSSM_EVENT_PORT_OFFLINE: + break; + + default: + bfa_assert(0); + } +} + +static void +bfa_fcs_port_ms_sm_plogi_sending(struct bfa_fcs_port_ms_s *ms, + enum port_ms_event event) +{ + bfa_trc(ms->port->fcs, ms->port->port_cfg.pwwn); + bfa_trc(ms->port->fcs, event); + + switch (event) { + case MSSM_EVENT_FCXP_SENT: + bfa_sm_set_state(ms, bfa_fcs_port_ms_sm_plogi); + break; + + case MSSM_EVENT_PORT_OFFLINE: + bfa_sm_set_state(ms, bfa_fcs_port_ms_sm_offline); + bfa_fcxp_walloc_cancel(BFA_FCS_GET_HAL_FROM_PORT(ms->port), + &ms->fcxp_wqe); + break; + + default: + bfa_assert(0); + } +} + +static void +bfa_fcs_port_ms_sm_plogi(struct bfa_fcs_port_ms_s *ms, enum port_ms_event event) +{ + bfa_trc(ms->port->fcs, ms->port->port_cfg.pwwn); + bfa_trc(ms->port->fcs, event); + + switch (event) { + case MSSM_EVENT_RSP_ERROR: + /* + * Start timer for a delayed retry + */ + bfa_sm_set_state(ms, bfa_fcs_port_ms_sm_plogi_retry); + bfa_timer_start(BFA_FCS_GET_HAL_FROM_PORT(ms->port), &ms->timer, + bfa_fcs_port_ms_timeout, ms, + BFA_FCS_RETRY_TIMEOUT); + break; + + case MSSM_EVENT_RSP_OK: + /* + * since plogi is done, now invoke MS related sub-modules + */ + bfa_fcs_port_fdmi_online(ms); + + /** + * if this is a Vport, go to online state. + */ + if (ms->port->vport) { + bfa_sm_set_state(ms, bfa_fcs_port_ms_sm_online); + break; + } + + /* + * For a base port we need to get the + * switch's IP address. + */ + bfa_sm_set_state(ms, bfa_fcs_port_ms_sm_gmal_sending); + bfa_fcs_port_ms_send_gmal(ms, NULL); + break; + + case MSSM_EVENT_PORT_OFFLINE: + bfa_sm_set_state(ms, bfa_fcs_port_ms_sm_offline); + bfa_fcxp_discard(ms->fcxp); + break; + + default: + bfa_assert(0); + } +} + +static void +bfa_fcs_port_ms_sm_plogi_retry(struct bfa_fcs_port_ms_s *ms, + enum port_ms_event event) +{ + bfa_trc(ms->port->fcs, ms->port->port_cfg.pwwn); + bfa_trc(ms->port->fcs, event); + + switch (event) { + case MSSM_EVENT_TIMEOUT: + /* + * Retry Timer Expired. Re-send + */ + bfa_sm_set_state(ms, bfa_fcs_port_ms_sm_plogi_sending); + bfa_fcs_port_ms_send_plogi(ms, NULL); + break; + + case MSSM_EVENT_PORT_OFFLINE: + bfa_sm_set_state(ms, bfa_fcs_port_ms_sm_offline); + bfa_timer_stop(&ms->timer); + break; + + default: + bfa_assert(0); + } +} + +static void +bfa_fcs_port_ms_sm_online(struct bfa_fcs_port_ms_s *ms, + enum port_ms_event event) +{ + bfa_trc(ms->port->fcs, ms->port->port_cfg.pwwn); + bfa_trc(ms->port->fcs, event); + + switch (event) { + case MSSM_EVENT_PORT_OFFLINE: + bfa_sm_set_state(ms, bfa_fcs_port_ms_sm_offline); + /* + * now invoke MS related sub-modules + */ + bfa_fcs_port_fdmi_offline(ms); + break; + + case MSSM_EVENT_PORT_FABRIC_RSCN: + bfa_sm_set_state(ms, bfa_fcs_port_ms_sm_gfn_sending); + ms->retry_cnt = 0; + bfa_fcs_port_ms_send_gfn(ms, NULL); + break; + + default: + bfa_assert(0); + } +} + +static void +bfa_fcs_port_ms_sm_gmal_sending(struct bfa_fcs_port_ms_s *ms, + enum port_ms_event event) +{ + bfa_trc(ms->port->fcs, ms->port->port_cfg.pwwn); + bfa_trc(ms->port->fcs, event); + + switch (event) { + case MSSM_EVENT_FCXP_SENT: + bfa_sm_set_state(ms, bfa_fcs_port_ms_sm_gmal); + break; + + case MSSM_EVENT_PORT_OFFLINE: + bfa_sm_set_state(ms, bfa_fcs_port_ms_sm_offline); + bfa_fcxp_walloc_cancel(BFA_FCS_GET_HAL_FROM_PORT(ms->port), + &ms->fcxp_wqe); + break; + + default: + bfa_assert(0); + } +} + +static void +bfa_fcs_port_ms_sm_gmal(struct bfa_fcs_port_ms_s *ms, enum port_ms_event event) +{ + bfa_trc(ms->port->fcs, ms->port->port_cfg.pwwn); + bfa_trc(ms->port->fcs, event); + + switch (event) { + case MSSM_EVENT_RSP_ERROR: + /* + * Start timer for a delayed retry + */ + if (ms->retry_cnt++ < BFA_FCS_MS_CMD_MAX_RETRIES) { + bfa_sm_set_state(ms, bfa_fcs_port_ms_sm_gmal_retry); + bfa_timer_start(BFA_FCS_GET_HAL_FROM_PORT(ms->port), + &ms->timer, bfa_fcs_port_ms_timeout, ms, + BFA_FCS_RETRY_TIMEOUT); + } else { + bfa_sm_set_state(ms, bfa_fcs_port_ms_sm_gfn_sending); + bfa_fcs_port_ms_send_gfn(ms, NULL); + ms->retry_cnt = 0; + } + break; + + case MSSM_EVENT_RSP_OK: + bfa_sm_set_state(ms, bfa_fcs_port_ms_sm_gfn_sending); + bfa_fcs_port_ms_send_gfn(ms, NULL); + break; + + case MSSM_EVENT_PORT_OFFLINE: + bfa_sm_set_state(ms, bfa_fcs_port_ms_sm_offline); + bfa_fcxp_discard(ms->fcxp); + break; + + default: + bfa_assert(0); + } +} + +static void +bfa_fcs_port_ms_sm_gmal_retry(struct bfa_fcs_port_ms_s *ms, + enum port_ms_event event) +{ + bfa_trc(ms->port->fcs, ms->port->port_cfg.pwwn); + bfa_trc(ms->port->fcs, event); + + switch (event) { + case MSSM_EVENT_TIMEOUT: + /* + * Retry Timer Expired. Re-send + */ + bfa_sm_set_state(ms, bfa_fcs_port_ms_sm_gmal_sending); + bfa_fcs_port_ms_send_gmal(ms, NULL); + break; + + case MSSM_EVENT_PORT_OFFLINE: + bfa_sm_set_state(ms, bfa_fcs_port_ms_sm_offline); + bfa_timer_stop(&ms->timer); + break; + + default: + bfa_assert(0); + } +} + +/** + * ms_pvt MS local functions + */ + +static void +bfa_fcs_port_ms_send_gmal(void *ms_cbarg, struct bfa_fcxp_s *fcxp_alloced) +{ + struct bfa_fcs_port_ms_s *ms = ms_cbarg; + struct bfa_fcs_port_s *port = ms->port; + struct fchs_s fchs; + int len; + struct bfa_fcxp_s *fcxp; + + bfa_trc(port->fcs, port->pid); + + fcxp = fcxp_alloced ? fcxp_alloced : bfa_fcs_fcxp_alloc(port->fcs); + if (!fcxp) { + bfa_fcxp_alloc_wait(port->fcs->bfa, &ms->fcxp_wqe, + bfa_fcs_port_ms_send_gmal, ms); + return; + } + ms->fcxp = fcxp; + + len = fc_gmal_req_build(&fchs, bfa_fcxp_get_reqbuf(fcxp), + bfa_fcs_port_get_fcid(port), + bfa_lps_get_peer_nwwn(port->fabric->lps)); + + bfa_fcxp_send(fcxp, NULL, port->fabric->vf_id, port->lp_tag, BFA_FALSE, + FC_CLASS_3, len, &fchs, bfa_fcs_port_ms_gmal_response, + (void *)ms, FC_MAX_PDUSZ, FC_RA_TOV); + + bfa_sm_send_event(ms, MSSM_EVENT_FCXP_SENT); +} + +static void +bfa_fcs_port_ms_gmal_response(void *fcsarg, struct bfa_fcxp_s *fcxp, + void *cbarg, bfa_status_t req_status, + u32 rsp_len, u32 resid_len, + struct fchs_s *rsp_fchs) +{ + struct bfa_fcs_port_ms_s *ms = (struct bfa_fcs_port_ms_s *)cbarg; + struct bfa_fcs_port_s *port = ms->port; + struct ct_hdr_s *cthdr = NULL; + struct fcgs_gmal_resp_s *gmal_resp; + struct fc_gmal_entry_s *gmal_entry; + u32 num_entries; + u8 *rsp_str; + + bfa_trc(port->fcs, req_status); + bfa_trc(port->fcs, port->port_cfg.pwwn); + + /* + * Sanity Checks + */ + if (req_status != BFA_STATUS_OK) { + bfa_trc(port->fcs, req_status); + bfa_sm_send_event(ms, MSSM_EVENT_RSP_ERROR); + return; + } + + cthdr = (struct ct_hdr_s *) BFA_FCXP_RSP_PLD(fcxp); + cthdr->cmd_rsp_code = bfa_os_ntohs(cthdr->cmd_rsp_code); + + if (cthdr->cmd_rsp_code == CT_RSP_ACCEPT) { + gmal_resp = (struct fcgs_gmal_resp_s *)(cthdr + 1); + num_entries = bfa_os_ntohl(gmal_resp->ms_len); + if (num_entries == 0) { + bfa_sm_send_event(ms, MSSM_EVENT_RSP_ERROR); + return; + } + /* + * The response could contain multiple Entries. + * Entries for SNMP interface, etc. + * We look for the entry with a telnet prefix. + * First "http://" entry refers to IP addr + */ + + gmal_entry = (struct fc_gmal_entry_s *)gmal_resp->ms_ma; + while (num_entries > 0) { + if (strncmp + (gmal_entry->prefix, CT_GMAL_RESP_PREFIX_HTTP, + sizeof(gmal_entry->prefix)) == 0) { + + /* + * if the IP address is terminating with a '/', + * remove it. *Byte 0 consists of the length + * of the string. + */ + rsp_str = &(gmal_entry->prefix[0]); + if (rsp_str[gmal_entry->len - 1] == '/') + rsp_str[gmal_entry->len - 1] = 0; + /* + * copy IP Address to fabric + */ + strncpy(bfa_fcs_port_get_fabric_ipaddr(port), + gmal_entry->ip_addr, + BFA_FCS_FABRIC_IPADDR_SZ); + break; + } else { + --num_entries; + ++gmal_entry; + } + } + + bfa_sm_send_event(ms, MSSM_EVENT_RSP_OK); + return; + } + + bfa_trc(port->fcs, cthdr->reason_code); + bfa_trc(port->fcs, cthdr->exp_code); + bfa_sm_send_event(ms, MSSM_EVENT_RSP_ERROR); +} + +static void +bfa_fcs_port_ms_sm_gfn_sending(struct bfa_fcs_port_ms_s *ms, + enum port_ms_event event) +{ + bfa_trc(ms->port->fcs, ms->port->port_cfg.pwwn); + bfa_trc(ms->port->fcs, event); + + switch (event) { + case MSSM_EVENT_FCXP_SENT: + bfa_sm_set_state(ms, bfa_fcs_port_ms_sm_gfn); + break; + + case MSSM_EVENT_PORT_OFFLINE: + bfa_sm_set_state(ms, bfa_fcs_port_ms_sm_offline); + bfa_fcxp_walloc_cancel(BFA_FCS_GET_HAL_FROM_PORT(ms->port), + &ms->fcxp_wqe); + break; + + default: + bfa_assert(0); + } +} + +static void +bfa_fcs_port_ms_sm_gfn(struct bfa_fcs_port_ms_s *ms, enum port_ms_event event) +{ + bfa_trc(ms->port->fcs, ms->port->port_cfg.pwwn); + bfa_trc(ms->port->fcs, event); + + switch (event) { + case MSSM_EVENT_RSP_ERROR: + /* + * Start timer for a delayed retry + */ + if (ms->retry_cnt++ < BFA_FCS_MS_CMD_MAX_RETRIES) { + bfa_sm_set_state(ms, bfa_fcs_port_ms_sm_gfn_retry); + bfa_timer_start(BFA_FCS_GET_HAL_FROM_PORT(ms->port), + &ms->timer, bfa_fcs_port_ms_timeout, ms, + BFA_FCS_RETRY_TIMEOUT); + } else { + bfa_sm_set_state(ms, bfa_fcs_port_ms_sm_online); + ms->retry_cnt = 0; + } + break; + + case MSSM_EVENT_RSP_OK: + bfa_sm_set_state(ms, bfa_fcs_port_ms_sm_online); + break; + + case MSSM_EVENT_PORT_OFFLINE: + bfa_sm_set_state(ms, bfa_fcs_port_ms_sm_offline); + bfa_fcxp_discard(ms->fcxp); + break; + + default: + bfa_assert(0); + } +} + +static void +bfa_fcs_port_ms_sm_gfn_retry(struct bfa_fcs_port_ms_s *ms, + enum port_ms_event event) +{ + bfa_trc(ms->port->fcs, ms->port->port_cfg.pwwn); + bfa_trc(ms->port->fcs, event); + + switch (event) { + case MSSM_EVENT_TIMEOUT: + /* + * Retry Timer Expired. Re-send + */ + bfa_sm_set_state(ms, bfa_fcs_port_ms_sm_gfn_sending); + bfa_fcs_port_ms_send_gfn(ms, NULL); + break; + + case MSSM_EVENT_PORT_OFFLINE: + bfa_sm_set_state(ms, bfa_fcs_port_ms_sm_offline); + bfa_timer_stop(&ms->timer); + break; + + default: + bfa_assert(0); + } +} + +/** + * ms_pvt MS local functions + */ + +static void +bfa_fcs_port_ms_send_gfn(void *ms_cbarg, struct bfa_fcxp_s *fcxp_alloced) +{ + struct bfa_fcs_port_ms_s *ms = ms_cbarg; + struct bfa_fcs_port_s *port = ms->port; + struct fchs_s fchs; + int len; + struct bfa_fcxp_s *fcxp; + + bfa_trc(port->fcs, port->pid); + + fcxp = fcxp_alloced ? fcxp_alloced : bfa_fcs_fcxp_alloc(port->fcs); + if (!fcxp) { + bfa_fcxp_alloc_wait(port->fcs->bfa, &ms->fcxp_wqe, + bfa_fcs_port_ms_send_gfn, ms); + return; + } + ms->fcxp = fcxp; + + len = fc_gfn_req_build(&fchs, bfa_fcxp_get_reqbuf(fcxp), + bfa_fcs_port_get_fcid(port), + bfa_lps_get_peer_nwwn(port->fabric->lps)); + + bfa_fcxp_send(fcxp, NULL, port->fabric->vf_id, port->lp_tag, BFA_FALSE, + FC_CLASS_3, len, &fchs, bfa_fcs_port_ms_gfn_response, + (void *)ms, FC_MAX_PDUSZ, FC_RA_TOV); + + bfa_sm_send_event(ms, MSSM_EVENT_FCXP_SENT); +} + +static void +bfa_fcs_port_ms_gfn_response(void *fcsarg, struct bfa_fcxp_s *fcxp, void *cbarg, + bfa_status_t req_status, u32 rsp_len, + u32 resid_len, struct fchs_s *rsp_fchs) +{ + struct bfa_fcs_port_ms_s *ms = (struct bfa_fcs_port_ms_s *)cbarg; + struct bfa_fcs_port_s *port = ms->port; + struct ct_hdr_s *cthdr = NULL; + wwn_t *gfn_resp; + + bfa_trc(port->fcs, req_status); + bfa_trc(port->fcs, port->port_cfg.pwwn); + + /* + * Sanity Checks + */ + if (req_status != BFA_STATUS_OK) { + bfa_trc(port->fcs, req_status); + bfa_sm_send_event(ms, MSSM_EVENT_RSP_ERROR); + return; + } + + cthdr = (struct ct_hdr_s *) BFA_FCXP_RSP_PLD(fcxp); + cthdr->cmd_rsp_code = bfa_os_ntohs(cthdr->cmd_rsp_code); + + if (cthdr->cmd_rsp_code == CT_RSP_ACCEPT) { + gfn_resp = (wwn_t *) (cthdr + 1); + /* + * check if it has actually changed + */ + if ((memcmp + ((void *)&bfa_fcs_port_get_fabric_name(port), gfn_resp, + sizeof(wwn_t)) != 0)) + bfa_fcs_fabric_set_fabric_name(port->fabric, *gfn_resp); + bfa_sm_send_event(ms, MSSM_EVENT_RSP_OK); + return; + } + + bfa_trc(port->fcs, cthdr->reason_code); + bfa_trc(port->fcs, cthdr->exp_code); + bfa_sm_send_event(ms, MSSM_EVENT_RSP_ERROR); +} + +/** + * ms_pvt MS local functions + */ + +static void +bfa_fcs_port_ms_send_plogi(void *ms_cbarg, struct bfa_fcxp_s *fcxp_alloced) +{ + struct bfa_fcs_port_ms_s *ms = ms_cbarg; + struct bfa_fcs_port_s *port = ms->port; + struct fchs_s fchs; + int len; + struct bfa_fcxp_s *fcxp; + + bfa_trc(port->fcs, port->pid); + + fcxp = fcxp_alloced ? fcxp_alloced : bfa_fcs_fcxp_alloc(port->fcs); + if (!fcxp) { + port->stats.ms_plogi_alloc_wait++; + bfa_fcxp_alloc_wait(port->fcs->bfa, &ms->fcxp_wqe, + bfa_fcs_port_ms_send_plogi, ms); + return; + } + ms->fcxp = fcxp; + + len = fc_plogi_build(&fchs, bfa_fcxp_get_reqbuf(fcxp), + bfa_os_hton3b(FC_MGMT_SERVER), + bfa_fcs_port_get_fcid(port), 0, + port->port_cfg.pwwn, port->port_cfg.nwwn, + bfa_pport_get_maxfrsize(port->fcs->bfa)); + + bfa_fcxp_send(fcxp, NULL, port->fabric->vf_id, port->lp_tag, BFA_FALSE, + FC_CLASS_3, len, &fchs, bfa_fcs_port_ms_plogi_response, + (void *)ms, FC_MAX_PDUSZ, FC_RA_TOV); + + port->stats.ms_plogi_sent++; + bfa_sm_send_event(ms, MSSM_EVENT_FCXP_SENT); +} + +static void +bfa_fcs_port_ms_plogi_response(void *fcsarg, struct bfa_fcxp_s *fcxp, + void *cbarg, bfa_status_t req_status, + u32 rsp_len, u32 resid_len, + struct fchs_s *rsp_fchs) +{ + struct bfa_fcs_port_ms_s *ms = (struct bfa_fcs_port_ms_s *)cbarg; + + struct bfa_fcs_port_s *port = ms->port; + struct fc_els_cmd_s *els_cmd; + struct fc_ls_rjt_s *ls_rjt; + + bfa_trc(port->fcs, req_status); + bfa_trc(port->fcs, port->port_cfg.pwwn); + + /* + * Sanity Checks + */ + if (req_status != BFA_STATUS_OK) { + port->stats.ms_plogi_rsp_err++; + bfa_trc(port->fcs, req_status); + bfa_sm_send_event(ms, MSSM_EVENT_RSP_ERROR); + return; + } + + els_cmd = (struct fc_els_cmd_s *) BFA_FCXP_RSP_PLD(fcxp); + + switch (els_cmd->els_code) { + + case FC_ELS_ACC: + if (rsp_len < sizeof(struct fc_logi_s)) { + bfa_trc(port->fcs, rsp_len); + port->stats.ms_plogi_acc_err++; + bfa_sm_send_event(ms, MSSM_EVENT_RSP_ERROR); + break; + } + port->stats.ms_plogi_accepts++; + bfa_sm_send_event(ms, MSSM_EVENT_RSP_OK); + break; + + case FC_ELS_LS_RJT: + ls_rjt = (struct fc_ls_rjt_s *) BFA_FCXP_RSP_PLD(fcxp); + + bfa_trc(port->fcs, ls_rjt->reason_code); + bfa_trc(port->fcs, ls_rjt->reason_code_expl); + + port->stats.ms_rejects++; + bfa_sm_send_event(ms, MSSM_EVENT_RSP_ERROR); + break; + + default: + port->stats.ms_plogi_unknown_rsp++; + bfa_trc(port->fcs, els_cmd->els_code); + bfa_sm_send_event(ms, MSSM_EVENT_RSP_ERROR); + } +} + +static void +bfa_fcs_port_ms_timeout(void *arg) +{ + struct bfa_fcs_port_ms_s *ms = (struct bfa_fcs_port_ms_s *)arg; + + ms->port->stats.ms_timeouts++; + bfa_sm_send_event(ms, MSSM_EVENT_TIMEOUT); +} + + +void +bfa_fcs_port_ms_init(struct bfa_fcs_port_s *port) +{ + struct bfa_fcs_port_ms_s *ms = BFA_FCS_GET_MS_FROM_PORT(port); + + ms->port = port; + bfa_sm_set_state(ms, bfa_fcs_port_ms_sm_offline); + + /* + * Invoke init routines of sub modules. + */ + bfa_fcs_port_fdmi_init(ms); +} + +void +bfa_fcs_port_ms_offline(struct bfa_fcs_port_s *port) +{ + struct bfa_fcs_port_ms_s *ms = BFA_FCS_GET_MS_FROM_PORT(port); + + ms->port = port; + bfa_sm_send_event(ms, MSSM_EVENT_PORT_OFFLINE); +} + +void +bfa_fcs_port_ms_online(struct bfa_fcs_port_s *port) +{ + struct bfa_fcs_port_ms_s *ms = BFA_FCS_GET_MS_FROM_PORT(port); + + ms->port = port; + bfa_sm_send_event(ms, MSSM_EVENT_PORT_ONLINE); +} + +void +bfa_fcs_port_ms_fabric_rscn(struct bfa_fcs_port_s *port) +{ + struct bfa_fcs_port_ms_s *ms = BFA_FCS_GET_MS_FROM_PORT(port); + + /* + * @todo. Handle this only when in Online state + */ + if (bfa_sm_cmp_state(ms, bfa_fcs_port_ms_sm_online)) + bfa_sm_send_event(ms, MSSM_EVENT_PORT_FABRIC_RSCN); +} diff --git a/drivers/scsi/bfa/n2n.c b/drivers/scsi/bfa/n2n.c new file mode 100644 index 000000000000..735456824346 --- /dev/null +++ b/drivers/scsi/bfa/n2n.c @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2005-2009 Brocade Communications Systems, Inc. + * All rights reserved + * www.brocade.com + * + * Linux driver for Brocade Fibre Channel Host Bus Adapter. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (GPL) Version 2 as + * published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +/** + * n2n.c n2n implementation. + */ +#include <bfa.h> +#include <bfa_svc.h> +#include "fcs_lport.h" +#include "fcs_rport.h" +#include "fcs_trcmod.h" +#include "lport_priv.h" + +BFA_TRC_FILE(FCS, N2N); + +/** + * Called by fcs/port to initialize N2N topology. + */ +void +bfa_fcs_port_n2n_init(struct bfa_fcs_port_s *port) +{ +} + +/** + * Called by fcs/port to notify transition to online state. + */ +void +bfa_fcs_port_n2n_online(struct bfa_fcs_port_s *port) +{ + struct bfa_fcs_port_n2n_s *n2n_port = &port->port_topo.pn2n; + struct bfa_port_cfg_s *pcfg = &port->port_cfg; + struct bfa_fcs_rport_s *rport; + + bfa_trc(port->fcs, pcfg->pwwn); + + /* + * If our PWWN is > than that of the r-port, we have to initiate PLOGI + * and assign an Address. if not, we need to wait for its PLOGI. + * + * If our PWWN is < than that of the remote port, it will send a PLOGI + * with the PIDs assigned. The rport state machine take care of this + * incoming PLOGI. + */ + if (memcmp + ((void *)&pcfg->pwwn, (void *)&n2n_port->rem_port_wwn, + sizeof(wwn_t)) > 0) { + port->pid = N2N_LOCAL_PID; + /** + * First, check if we know the device by pwwn. + */ + rport = bfa_fcs_port_get_rport_by_pwwn(port, + n2n_port->rem_port_wwn); + if (rport) { + bfa_trc(port->fcs, rport->pid); + bfa_trc(port->fcs, rport->pwwn); + rport->pid = N2N_REMOTE_PID; + bfa_fcs_rport_online(rport); + return; + } + + /* + * In n2n there can be only one rport. Delete the old one whose + * pid should be zero, because it is offline. + */ + if (port->num_rports > 0) { + rport = bfa_fcs_port_get_rport_by_pid(port, 0); + bfa_assert(rport != NULL); + if (rport) { + bfa_trc(port->fcs, rport->pwwn); + bfa_fcs_rport_delete(rport); + } + } + bfa_fcs_rport_create(port, N2N_REMOTE_PID); + } +} + +/** + * Called by fcs/port to notify transition to offline state. + */ +void +bfa_fcs_port_n2n_offline(struct bfa_fcs_port_s *port) +{ + struct bfa_fcs_port_n2n_s *n2n_port = &port->port_topo.pn2n; + + bfa_trc(port->fcs, port->pid); + port->pid = 0; + n2n_port->rem_port_wwn = 0; + n2n_port->reply_oxid = 0; +} + + diff --git a/drivers/scsi/bfa/ns.c b/drivers/scsi/bfa/ns.c new file mode 100644 index 000000000000..59fea99d67a4 --- /dev/null +++ b/drivers/scsi/bfa/ns.c @@ -0,0 +1,1243 @@ +/* + * Copyright (c) 2005-2009 Brocade Communications Systems, Inc. + * All rights reserved + * www.brocade.com + * + * Linux driver for Brocade Fibre Channel Host Bus Adapter. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (GPL) Version 2 as + * published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +/** + * @page ns_sm_info VPORT NS State Machine + * + * @section ns_sm_interactions VPORT NS State Machine Interactions + * + * @section ns_sm VPORT NS State Machine + * img ns_sm.jpg + */ +#include <bfa.h> +#include <bfa_svc.h> +#include <bfa_iocfc.h> +#include "fcs_lport.h" +#include "fcs_rport.h" +#include "fcs_trcmod.h" +#include "fcs_fcxp.h" +#include "fcs.h" +#include "lport_priv.h" + +BFA_TRC_FILE(FCS, NS); + +/* + * forward declarations + */ +static void bfa_fcs_port_ns_send_plogi(void *ns_cbarg, + struct bfa_fcxp_s *fcxp_alloced); +static void bfa_fcs_port_ns_send_rspn_id(void *ns_cbarg, + struct bfa_fcxp_s *fcxp_alloced); +static void bfa_fcs_port_ns_send_rft_id(void *ns_cbarg, + struct bfa_fcxp_s *fcxp_alloced); +static void bfa_fcs_port_ns_send_rff_id(void *ns_cbarg, + struct bfa_fcxp_s *fcxp_alloced); +static void bfa_fcs_port_ns_send_gid_ft(void *ns_cbarg, + struct bfa_fcxp_s *fcxp_alloced); +static void bfa_fcs_port_ns_timeout(void *arg); +static void bfa_fcs_port_ns_plogi_response(void *fcsarg, + struct bfa_fcxp_s *fcxp, + void *cbarg, + bfa_status_t req_status, + u32 rsp_len, + u32 resid_len, + struct fchs_s *rsp_fchs); +static void bfa_fcs_port_ns_rspn_id_response(void *fcsarg, + struct bfa_fcxp_s *fcxp, + void *cbarg, + bfa_status_t req_status, + u32 rsp_len, + u32 resid_len, + struct fchs_s *rsp_fchs); +static void bfa_fcs_port_ns_rft_id_response(void *fcsarg, + struct bfa_fcxp_s *fcxp, + void *cbarg, + bfa_status_t req_status, + u32 rsp_len, + u32 resid_len, + struct fchs_s *rsp_fchs); +static void bfa_fcs_port_ns_rff_id_response(void *fcsarg, + struct bfa_fcxp_s *fcxp, + void *cbarg, + bfa_status_t req_status, + u32 rsp_len, + u32 resid_len, + struct fchs_s *rsp_fchs); +static void bfa_fcs_port_ns_gid_ft_response(void *fcsarg, + struct bfa_fcxp_s *fcxp, + void *cbarg, + bfa_status_t req_status, + u32 rsp_len, + u32 resid_len, + struct fchs_s *rsp_fchs); +static void bfa_fcs_port_ns_process_gidft_pids(struct bfa_fcs_port_s *port, + u32 *pid_buf, + u32 n_pids); + +static void bfa_fcs_port_ns_boot_target_disc(struct bfa_fcs_port_s *port); +/** + * fcs_ns_sm FCS nameserver interface state machine + */ + +/** + * VPort NS State Machine events + */ +enum vport_ns_event { + NSSM_EVENT_PORT_ONLINE = 1, + NSSM_EVENT_PORT_OFFLINE = 2, + NSSM_EVENT_PLOGI_SENT = 3, + NSSM_EVENT_RSP_OK = 4, + NSSM_EVENT_RSP_ERROR = 5, + NSSM_EVENT_TIMEOUT = 6, + NSSM_EVENT_NS_QUERY = 7, + NSSM_EVENT_RSPNID_SENT = 8, + NSSM_EVENT_RFTID_SENT = 9, + NSSM_EVENT_RFFID_SENT = 10, + NSSM_EVENT_GIDFT_SENT = 11, +}; + +static void bfa_fcs_port_ns_sm_offline(struct bfa_fcs_port_ns_s *ns, + enum vport_ns_event event); +static void bfa_fcs_port_ns_sm_plogi_sending(struct bfa_fcs_port_ns_s *ns, + enum vport_ns_event event); +static void bfa_fcs_port_ns_sm_plogi(struct bfa_fcs_port_ns_s *ns, + enum vport_ns_event event); +static void bfa_fcs_port_ns_sm_plogi_retry(struct bfa_fcs_port_ns_s *ns, + enum vport_ns_event event); +static void bfa_fcs_port_ns_sm_sending_rspn_id(struct bfa_fcs_port_ns_s *ns, + enum vport_ns_event event); +static void bfa_fcs_port_ns_sm_rspn_id(struct bfa_fcs_port_ns_s *ns, + enum vport_ns_event event); +static void bfa_fcs_port_ns_sm_rspn_id_retry(struct bfa_fcs_port_ns_s *ns, + enum vport_ns_event event); +static void bfa_fcs_port_ns_sm_sending_rft_id(struct bfa_fcs_port_ns_s *ns, + enum vport_ns_event event); +static void bfa_fcs_port_ns_sm_rft_id_retry(struct bfa_fcs_port_ns_s *ns, + enum vport_ns_event event); +static void bfa_fcs_port_ns_sm_rft_id(struct bfa_fcs_port_ns_s *ns, + enum vport_ns_event event); +static void bfa_fcs_port_ns_sm_sending_rff_id(struct bfa_fcs_port_ns_s *ns, + enum vport_ns_event event); +static void bfa_fcs_port_ns_sm_rff_id_retry(struct bfa_fcs_port_ns_s *ns, + enum vport_ns_event event); +static void bfa_fcs_port_ns_sm_rff_id(struct bfa_fcs_port_ns_s *ns, + enum vport_ns_event event); +static void bfa_fcs_port_ns_sm_sending_gid_ft(struct bfa_fcs_port_ns_s *ns, + enum vport_ns_event event); +static void bfa_fcs_port_ns_sm_gid_ft(struct bfa_fcs_port_ns_s *ns, + enum vport_ns_event event); +static void bfa_fcs_port_ns_sm_gid_ft_retry(struct bfa_fcs_port_ns_s *ns, + enum vport_ns_event event); +static void bfa_fcs_port_ns_sm_online(struct bfa_fcs_port_ns_s *ns, + enum vport_ns_event event); +/** + * Start in offline state - awaiting linkup + */ +static void +bfa_fcs_port_ns_sm_offline(struct bfa_fcs_port_ns_s *ns, + enum vport_ns_event event) +{ + bfa_trc(ns->port->fcs, ns->port->port_cfg.pwwn); + bfa_trc(ns->port->fcs, event); + + switch (event) { + case NSSM_EVENT_PORT_ONLINE: + bfa_sm_set_state(ns, bfa_fcs_port_ns_sm_plogi_sending); + bfa_fcs_port_ns_send_plogi(ns, NULL); + break; + + case NSSM_EVENT_PORT_OFFLINE: + break; + + default: + bfa_assert(0); + } +} + +static void +bfa_fcs_port_ns_sm_plogi_sending(struct bfa_fcs_port_ns_s *ns, + enum vport_ns_event event) +{ + bfa_trc(ns->port->fcs, ns->port->port_cfg.pwwn); + bfa_trc(ns->port->fcs, event); + + switch (event) { + case NSSM_EVENT_PLOGI_SENT: + bfa_sm_set_state(ns, bfa_fcs_port_ns_sm_plogi); + break; + + case NSSM_EVENT_PORT_OFFLINE: + bfa_sm_set_state(ns, bfa_fcs_port_ns_sm_offline); + bfa_fcxp_walloc_cancel(BFA_FCS_GET_HAL_FROM_PORT(ns->port), + &ns->fcxp_wqe); + break; + + default: + bfa_assert(0); + } +} + +static void +bfa_fcs_port_ns_sm_plogi(struct bfa_fcs_port_ns_s *ns, + enum vport_ns_event event) +{ + bfa_trc(ns->port->fcs, ns->port->port_cfg.pwwn); + bfa_trc(ns->port->fcs, event); + + switch (event) { + case NSSM_EVENT_RSP_ERROR: + /* + * Start timer for a delayed retry + */ + bfa_sm_set_state(ns, bfa_fcs_port_ns_sm_plogi_retry); + ns->port->stats.ns_retries++; + bfa_timer_start(BFA_FCS_GET_HAL_FROM_PORT(ns->port), &ns->timer, + bfa_fcs_port_ns_timeout, ns, + BFA_FCS_RETRY_TIMEOUT); + break; + + case NSSM_EVENT_RSP_OK: + bfa_sm_set_state(ns, bfa_fcs_port_ns_sm_sending_rspn_id); + bfa_fcs_port_ns_send_rspn_id(ns, NULL); + break; + + case NSSM_EVENT_PORT_OFFLINE: + bfa_sm_set_state(ns, bfa_fcs_port_ns_sm_offline); + bfa_fcxp_discard(ns->fcxp); + break; + + default: + bfa_assert(0); + } +} + +static void +bfa_fcs_port_ns_sm_plogi_retry(struct bfa_fcs_port_ns_s *ns, + enum vport_ns_event event) +{ + bfa_trc(ns->port->fcs, ns->port->port_cfg.pwwn); + bfa_trc(ns->port->fcs, event); + + switch (event) { + case NSSM_EVENT_TIMEOUT: + /* + * Retry Timer Expired. Re-send + */ + bfa_sm_set_state(ns, bfa_fcs_port_ns_sm_plogi_sending); + bfa_fcs_port_ns_send_plogi(ns, NULL); + break; + + case NSSM_EVENT_PORT_OFFLINE: + bfa_sm_set_state(ns, bfa_fcs_port_ns_sm_offline); + bfa_timer_stop(&ns->timer); + break; + + default: + bfa_assert(0); + } +} + +static void +bfa_fcs_port_ns_sm_sending_rspn_id(struct bfa_fcs_port_ns_s *ns, + enum vport_ns_event event) +{ + bfa_trc(ns->port->fcs, ns->port->port_cfg.pwwn); + bfa_trc(ns->port->fcs, event); + + switch (event) { + case NSSM_EVENT_RSPNID_SENT: + bfa_sm_set_state(ns, bfa_fcs_port_ns_sm_rspn_id); + break; + + case NSSM_EVENT_PORT_OFFLINE: + bfa_sm_set_state(ns, bfa_fcs_port_ns_sm_offline); + bfa_fcxp_walloc_cancel(BFA_FCS_GET_HAL_FROM_PORT(ns->port), + &ns->fcxp_wqe); + break; + + default: + bfa_assert(0); + } +} + +static void +bfa_fcs_port_ns_sm_rspn_id(struct bfa_fcs_port_ns_s *ns, + enum vport_ns_event event) +{ + bfa_trc(ns->port->fcs, ns->port->port_cfg.pwwn); + bfa_trc(ns->port->fcs, event); + + switch (event) { + case NSSM_EVENT_RSP_ERROR: + /* + * Start timer for a delayed retry + */ + bfa_sm_set_state(ns, bfa_fcs_port_ns_sm_rspn_id_retry); + ns->port->stats.ns_retries++; + bfa_timer_start(BFA_FCS_GET_HAL_FROM_PORT(ns->port), &ns->timer, + bfa_fcs_port_ns_timeout, ns, + BFA_FCS_RETRY_TIMEOUT); + break; + + case NSSM_EVENT_RSP_OK: + bfa_sm_set_state(ns, bfa_fcs_port_ns_sm_sending_rft_id); + bfa_fcs_port_ns_send_rft_id(ns, NULL); + break; + + case NSSM_EVENT_PORT_OFFLINE: + bfa_fcxp_discard(ns->fcxp); + bfa_sm_set_state(ns, bfa_fcs_port_ns_sm_offline); + break; + + default: + bfa_assert(0); + } +} + +static void +bfa_fcs_port_ns_sm_rspn_id_retry(struct bfa_fcs_port_ns_s *ns, + enum vport_ns_event event) +{ + bfa_trc(ns->port->fcs, ns->port->port_cfg.pwwn); + bfa_trc(ns->port->fcs, event); + + switch (event) { + case NSSM_EVENT_TIMEOUT: + /* + * Retry Timer Expired. Re-send + */ + bfa_sm_set_state(ns, bfa_fcs_port_ns_sm_sending_rspn_id); + bfa_fcs_port_ns_send_rspn_id(ns, NULL); + break; + + case NSSM_EVENT_PORT_OFFLINE: + bfa_sm_set_state(ns, bfa_fcs_port_ns_sm_offline); + bfa_timer_stop(&ns->timer); + break; + + default: + bfa_assert(0); + } +} + +static void +bfa_fcs_port_ns_sm_sending_rft_id(struct bfa_fcs_port_ns_s *ns, + enum vport_ns_event event) +{ + bfa_trc(ns->port->fcs, ns->port->port_cfg.pwwn); + bfa_trc(ns->port->fcs, event); + + switch (event) { + case NSSM_EVENT_RFTID_SENT: + bfa_sm_set_state(ns, bfa_fcs_port_ns_sm_rft_id); + break; + + case NSSM_EVENT_PORT_OFFLINE: + bfa_sm_set_state(ns, bfa_fcs_port_ns_sm_offline); + bfa_fcxp_walloc_cancel(BFA_FCS_GET_HAL_FROM_PORT(ns->port), + &ns->fcxp_wqe); + break; + + default: + bfa_assert(0); + } +} + +static void +bfa_fcs_port_ns_sm_rft_id(struct bfa_fcs_port_ns_s *ns, + enum vport_ns_event event) +{ + bfa_trc(ns->port->fcs, ns->port->port_cfg.pwwn); + bfa_trc(ns->port->fcs, event); + + switch (event) { + case NSSM_EVENT_RSP_OK: + /* + * Now move to register FC4 Features + */ + bfa_sm_set_state(ns, bfa_fcs_port_ns_sm_sending_rff_id); + bfa_fcs_port_ns_send_rff_id(ns, NULL); + break; + + case NSSM_EVENT_RSP_ERROR: + /* + * Start timer for a delayed retry + */ + bfa_sm_set_state(ns, bfa_fcs_port_ns_sm_rft_id_retry); + ns->port->stats.ns_retries++; + bfa_timer_start(BFA_FCS_GET_HAL_FROM_PORT(ns->port), &ns->timer, + bfa_fcs_port_ns_timeout, ns, + BFA_FCS_RETRY_TIMEOUT); + break; + + case NSSM_EVENT_PORT_OFFLINE: + bfa_sm_set_state(ns, bfa_fcs_port_ns_sm_offline); + bfa_fcxp_discard(ns->fcxp); + break; + + default: + bfa_assert(0); + } +} + +static void +bfa_fcs_port_ns_sm_rft_id_retry(struct bfa_fcs_port_ns_s *ns, + enum vport_ns_event event) +{ + bfa_trc(ns->port->fcs, ns->port->port_cfg.pwwn); + bfa_trc(ns->port->fcs, event); + + switch (event) { + case NSSM_EVENT_TIMEOUT: + bfa_sm_set_state(ns, bfa_fcs_port_ns_sm_sending_rft_id); + bfa_fcs_port_ns_send_rft_id(ns, NULL); + break; + + case NSSM_EVENT_PORT_OFFLINE: + bfa_sm_set_state(ns, bfa_fcs_port_ns_sm_offline); + bfa_timer_stop(&ns->timer); + break; + + default: + bfa_assert(0); + } +} + +static void +bfa_fcs_port_ns_sm_sending_rff_id(struct bfa_fcs_port_ns_s *ns, + enum vport_ns_event event) +{ + bfa_trc(ns->port->fcs, ns->port->port_cfg.pwwn); + bfa_trc(ns->port->fcs, event); + + switch (event) { + case NSSM_EVENT_RFFID_SENT: + bfa_sm_set_state(ns, bfa_fcs_port_ns_sm_rff_id); + break; + + case NSSM_EVENT_PORT_OFFLINE: + bfa_sm_set_state(ns, bfa_fcs_port_ns_sm_offline); + bfa_fcxp_walloc_cancel(BFA_FCS_GET_HAL_FROM_PORT(ns->port), + &ns->fcxp_wqe); + break; + + default: + bfa_assert(0); + } +} + +static void +bfa_fcs_port_ns_sm_rff_id(struct bfa_fcs_port_ns_s *ns, + enum vport_ns_event event) +{ + bfa_trc(ns->port->fcs, ns->port->port_cfg.pwwn); + bfa_trc(ns->port->fcs, event); + + switch (event) { + case NSSM_EVENT_RSP_OK: + + /* + * If min cfg mode is enabled, we donot initiate rport + * discovery with the fabric. Instead, we will retrieve the + * boot targets from HAL/FW. + */ + if (__fcs_min_cfg(ns->port->fcs)) { + bfa_fcs_port_ns_boot_target_disc(ns->port); + bfa_sm_set_state(ns, bfa_fcs_port_ns_sm_online); + return; + } + + /* + * If the port role is Initiator Mode issue NS query. + * If it is Target Mode, skip this and go to online. + */ + if (BFA_FCS_VPORT_IS_INITIATOR_MODE(ns->port)) { + bfa_sm_set_state(ns, bfa_fcs_port_ns_sm_sending_gid_ft); + bfa_fcs_port_ns_send_gid_ft(ns, NULL); + } else if (BFA_FCS_VPORT_IS_TARGET_MODE(ns->port)) { + bfa_sm_set_state(ns, bfa_fcs_port_ns_sm_online); + } + /* + * kick off mgmt srvr state machine + */ + bfa_fcs_port_ms_online(ns->port); + break; + + case NSSM_EVENT_RSP_ERROR: + /* + * Start timer for a delayed retry + */ + bfa_sm_set_state(ns, bfa_fcs_port_ns_sm_rff_id_retry); + ns->port->stats.ns_retries++; + bfa_timer_start(BFA_FCS_GET_HAL_FROM_PORT(ns->port), &ns->timer, + bfa_fcs_port_ns_timeout, ns, + BFA_FCS_RETRY_TIMEOUT); + break; + + case NSSM_EVENT_PORT_OFFLINE: + bfa_sm_set_state(ns, bfa_fcs_port_ns_sm_offline); + bfa_fcxp_discard(ns->fcxp); + break; + + default: + bfa_assert(0); + } +} + +static void +bfa_fcs_port_ns_sm_rff_id_retry(struct bfa_fcs_port_ns_s *ns, + enum vport_ns_event event) +{ + bfa_trc(ns->port->fcs, ns->port->port_cfg.pwwn); + bfa_trc(ns->port->fcs, event); + + switch (event) { + case NSSM_EVENT_TIMEOUT: + bfa_sm_set_state(ns, bfa_fcs_port_ns_sm_sending_rff_id); + bfa_fcs_port_ns_send_rff_id(ns, NULL); + break; + + case NSSM_EVENT_PORT_OFFLINE: + bfa_sm_set_state(ns, bfa_fcs_port_ns_sm_offline); + bfa_timer_stop(&ns->timer); + break; + + default: + bfa_assert(0); + } +} +static void +bfa_fcs_port_ns_sm_sending_gid_ft(struct bfa_fcs_port_ns_s *ns, + enum vport_ns_event event) +{ + bfa_trc(ns->port->fcs, ns->port->port_cfg.pwwn); + bfa_trc(ns->port->fcs, event); + + switch (event) { + case NSSM_EVENT_GIDFT_SENT: + bfa_sm_set_state(ns, bfa_fcs_port_ns_sm_gid_ft); + break; + + case NSSM_EVENT_PORT_OFFLINE: + bfa_sm_set_state(ns, bfa_fcs_port_ns_sm_offline); + bfa_fcxp_walloc_cancel(BFA_FCS_GET_HAL_FROM_PORT(ns->port), + &ns->fcxp_wqe); + break; + + default: + bfa_assert(0); + } +} + +static void +bfa_fcs_port_ns_sm_gid_ft(struct bfa_fcs_port_ns_s *ns, + enum vport_ns_event event) +{ + bfa_trc(ns->port->fcs, ns->port->port_cfg.pwwn); + bfa_trc(ns->port->fcs, event); + + switch (event) { + case NSSM_EVENT_RSP_OK: + bfa_sm_set_state(ns, bfa_fcs_port_ns_sm_online); + break; + + case NSSM_EVENT_RSP_ERROR: + /* + * TBD: for certain reject codes, we don't need to retry + */ + /* + * Start timer for a delayed retry + */ + bfa_sm_set_state(ns, bfa_fcs_port_ns_sm_gid_ft_retry); + ns->port->stats.ns_retries++; + bfa_timer_start(BFA_FCS_GET_HAL_FROM_PORT(ns->port), &ns->timer, + bfa_fcs_port_ns_timeout, ns, + BFA_FCS_RETRY_TIMEOUT); + break; + + case NSSM_EVENT_PORT_OFFLINE: + bfa_sm_set_state(ns, bfa_fcs_port_ns_sm_offline); + bfa_fcxp_discard(ns->fcxp); + break; + + default: + bfa_assert(0); + } +} + +static void +bfa_fcs_port_ns_sm_gid_ft_retry(struct bfa_fcs_port_ns_s *ns, + enum vport_ns_event event) +{ + bfa_trc(ns->port->fcs, ns->port->port_cfg.pwwn); + bfa_trc(ns->port->fcs, event); + + switch (event) { + case NSSM_EVENT_TIMEOUT: + bfa_sm_set_state(ns, bfa_fcs_port_ns_sm_sending_gid_ft); + bfa_fcs_port_ns_send_gid_ft(ns, NULL); + break; + + case NSSM_EVENT_PORT_OFFLINE: + bfa_sm_set_state(ns, bfa_fcs_port_ns_sm_offline); + bfa_timer_stop(&ns->timer); + break; + + default: + bfa_assert(0); + } +} + +static void +bfa_fcs_port_ns_sm_online(struct bfa_fcs_port_ns_s *ns, + enum vport_ns_event event) +{ + bfa_trc(ns->port->fcs, ns->port->port_cfg.pwwn); + bfa_trc(ns->port->fcs, event); + + switch (event) { + case NSSM_EVENT_PORT_OFFLINE: + bfa_sm_set_state(ns, bfa_fcs_port_ns_sm_offline); + break; + + case NSSM_EVENT_NS_QUERY: + /* + * If the port role is Initiator Mode issue NS query. + * If it is Target Mode, skip this and go to online. + */ + if (BFA_FCS_VPORT_IS_INITIATOR_MODE(ns->port)) { + bfa_sm_set_state(ns, bfa_fcs_port_ns_sm_sending_gid_ft); + bfa_fcs_port_ns_send_gid_ft(ns, NULL); + }; + break; + + default: + bfa_assert(0); + } +} + + + +/** + * ns_pvt Nameserver local functions + */ + +static void +bfa_fcs_port_ns_send_plogi(void *ns_cbarg, struct bfa_fcxp_s *fcxp_alloced) +{ + struct bfa_fcs_port_ns_s *ns = ns_cbarg; + struct bfa_fcs_port_s *port = ns->port; + struct fchs_s fchs; + int len; + struct bfa_fcxp_s *fcxp; + + bfa_trc(port->fcs, port->pid); + + fcxp = fcxp_alloced ? fcxp_alloced : bfa_fcs_fcxp_alloc(port->fcs); + if (!fcxp) { + port->stats.ns_plogi_alloc_wait++; + bfa_fcxp_alloc_wait(port->fcs->bfa, &ns->fcxp_wqe, + bfa_fcs_port_ns_send_plogi, ns); + return; + } + ns->fcxp = fcxp; + + len = fc_plogi_build(&fchs, bfa_fcxp_get_reqbuf(fcxp), + bfa_os_hton3b(FC_NAME_SERVER), + bfa_fcs_port_get_fcid(port), 0, + port->port_cfg.pwwn, port->port_cfg.nwwn, + bfa_pport_get_maxfrsize(port->fcs->bfa)); + + bfa_fcxp_send(fcxp, NULL, port->fabric->vf_id, port->lp_tag, BFA_FALSE, + FC_CLASS_3, len, &fchs, bfa_fcs_port_ns_plogi_response, + (void *)ns, FC_MAX_PDUSZ, FC_RA_TOV); + port->stats.ns_plogi_sent++; + + bfa_sm_send_event(ns, NSSM_EVENT_PLOGI_SENT); +} + +static void +bfa_fcs_port_ns_plogi_response(void *fcsarg, struct bfa_fcxp_s *fcxp, + void *cbarg, bfa_status_t req_status, + u32 rsp_len, u32 resid_len, + struct fchs_s *rsp_fchs) +{ + struct bfa_fcs_port_ns_s *ns = (struct bfa_fcs_port_ns_s *)cbarg; + struct bfa_fcs_port_s *port = ns->port; + /* struct fc_logi_s *plogi_resp; */ + struct fc_els_cmd_s *els_cmd; + struct fc_ls_rjt_s *ls_rjt; + + bfa_trc(port->fcs, req_status); + bfa_trc(port->fcs, port->port_cfg.pwwn); + + /* + * Sanity Checks + */ + if (req_status != BFA_STATUS_OK) { + bfa_trc(port->fcs, req_status); + port->stats.ns_plogi_rsp_err++; + bfa_sm_send_event(ns, NSSM_EVENT_RSP_ERROR); + return; + } + + els_cmd = (struct fc_els_cmd_s *) BFA_FCXP_RSP_PLD(fcxp); + + switch (els_cmd->els_code) { + + case FC_ELS_ACC: + if (rsp_len < sizeof(struct fc_logi_s)) { + bfa_trc(port->fcs, rsp_len); + port->stats.ns_plogi_acc_err++; + bfa_sm_send_event(ns, NSSM_EVENT_RSP_ERROR); + break; + } + port->stats.ns_plogi_accepts++; + bfa_sm_send_event(ns, NSSM_EVENT_RSP_OK); + break; + + case FC_ELS_LS_RJT: + ls_rjt = (struct fc_ls_rjt_s *) BFA_FCXP_RSP_PLD(fcxp); + + bfa_trc(port->fcs, ls_rjt->reason_code); + bfa_trc(port->fcs, ls_rjt->reason_code_expl); + + port->stats.ns_rejects++; + + bfa_sm_send_event(ns, NSSM_EVENT_RSP_ERROR); + break; + + default: + port->stats.ns_plogi_unknown_rsp++; + bfa_trc(port->fcs, els_cmd->els_code); + bfa_sm_send_event(ns, NSSM_EVENT_RSP_ERROR); + } +} + +/** + * Register the symbolic port name. + */ +static void +bfa_fcs_port_ns_send_rspn_id(void *ns_cbarg, struct bfa_fcxp_s *fcxp_alloced) +{ + struct bfa_fcs_port_ns_s *ns = ns_cbarg; + struct bfa_fcs_port_s *port = ns->port; + struct fchs_s fchs; + int len; + struct bfa_fcxp_s *fcxp; + u8 symbl[256]; + u8 *psymbl = &symbl[0]; + + bfa_os_memset(symbl, 0, sizeof(symbl)); + + bfa_trc(port->fcs, port->port_cfg.pwwn); + + fcxp = fcxp_alloced ? fcxp_alloced : bfa_fcs_fcxp_alloc(port->fcs); + if (!fcxp) { + port->stats.ns_rspnid_alloc_wait++; + bfa_fcxp_alloc_wait(port->fcs->bfa, &ns->fcxp_wqe, + bfa_fcs_port_ns_send_rspn_id, ns); + return; + } + ns->fcxp = fcxp; + + /* + * for V-Port, form a Port Symbolic Name + */ + if (port->vport) { + /**For Vports, + * we append the vport's port symbolic name to that of the base port. + */ + + strncpy((char *)psymbl, + (char *) + &(bfa_fcs_port_get_psym_name + (bfa_fcs_get_base_port(port->fcs))), + strlen((char *) + &bfa_fcs_port_get_psym_name(bfa_fcs_get_base_port + (port->fcs)))); + + /* + * Ensure we have a null terminating string. + */ + ((char *) + psymbl)[strlen((char *) + &bfa_fcs_port_get_psym_name + (bfa_fcs_get_base_port(port->fcs)))] = 0; + + strncat((char *)psymbl, + (char *)&(bfa_fcs_port_get_psym_name(port)), + strlen((char *)&bfa_fcs_port_get_psym_name(port))); + } else { + psymbl = (u8 *) &(bfa_fcs_port_get_psym_name(port)); + } + + len = fc_rspnid_build(&fchs, bfa_fcxp_get_reqbuf(fcxp), + bfa_fcs_port_get_fcid(port), 0, psymbl); + + bfa_fcxp_send(fcxp, NULL, port->fabric->vf_id, port->lp_tag, BFA_FALSE, + FC_CLASS_3, len, &fchs, bfa_fcs_port_ns_rspn_id_response, + (void *)ns, FC_MAX_PDUSZ, FC_RA_TOV); + + port->stats.ns_rspnid_sent++; + + bfa_sm_send_event(ns, NSSM_EVENT_RSPNID_SENT); +} + +static void +bfa_fcs_port_ns_rspn_id_response(void *fcsarg, struct bfa_fcxp_s *fcxp, + void *cbarg, bfa_status_t req_status, + u32 rsp_len, u32 resid_len, + struct fchs_s *rsp_fchs) +{ + struct bfa_fcs_port_ns_s *ns = (struct bfa_fcs_port_ns_s *)cbarg; + struct bfa_fcs_port_s *port = ns->port; + struct ct_hdr_s *cthdr = NULL; + + bfa_trc(port->fcs, port->port_cfg.pwwn); + + /* + * Sanity Checks + */ + if (req_status != BFA_STATUS_OK) { + bfa_trc(port->fcs, req_status); + port->stats.ns_rspnid_rsp_err++; + bfa_sm_send_event(ns, NSSM_EVENT_RSP_ERROR); + return; + } + + cthdr = (struct ct_hdr_s *) BFA_FCXP_RSP_PLD(fcxp); + cthdr->cmd_rsp_code = bfa_os_ntohs(cthdr->cmd_rsp_code); + + if (cthdr->cmd_rsp_code == CT_RSP_ACCEPT) { + port->stats.ns_rspnid_accepts++; + bfa_sm_send_event(ns, NSSM_EVENT_RSP_OK); + return; + } + + port->stats.ns_rspnid_rejects++; + bfa_trc(port->fcs, cthdr->reason_code); + bfa_trc(port->fcs, cthdr->exp_code); + bfa_sm_send_event(ns, NSSM_EVENT_RSP_ERROR); +} + +/** + * Register FC4-Types + * TBD, Need to retrieve this from the OS driver, in case IPFC is enabled ? + */ +static void +bfa_fcs_port_ns_send_rft_id(void *ns_cbarg, struct bfa_fcxp_s *fcxp_alloced) +{ + struct bfa_fcs_port_ns_s *ns = ns_cbarg; + struct bfa_fcs_port_s *port = ns->port; + struct fchs_s fchs; + int len; + struct bfa_fcxp_s *fcxp; + + bfa_trc(port->fcs, port->port_cfg.pwwn); + + fcxp = fcxp_alloced ? fcxp_alloced : bfa_fcs_fcxp_alloc(port->fcs); + if (!fcxp) { + port->stats.ns_rftid_alloc_wait++; + bfa_fcxp_alloc_wait(port->fcs->bfa, &ns->fcxp_wqe, + bfa_fcs_port_ns_send_rft_id, ns); + return; + } + ns->fcxp = fcxp; + + len = fc_rftid_build(&fchs, bfa_fcxp_get_reqbuf(fcxp), + bfa_fcs_port_get_fcid(port), 0, + port->port_cfg.roles); + + bfa_fcxp_send(fcxp, NULL, port->fabric->vf_id, port->lp_tag, BFA_FALSE, + FC_CLASS_3, len, &fchs, bfa_fcs_port_ns_rft_id_response, + (void *)ns, FC_MAX_PDUSZ, FC_RA_TOV); + + port->stats.ns_rftid_sent++; + bfa_sm_send_event(ns, NSSM_EVENT_RFTID_SENT); +} + +static void +bfa_fcs_port_ns_rft_id_response(void *fcsarg, struct bfa_fcxp_s *fcxp, + void *cbarg, bfa_status_t req_status, + u32 rsp_len, u32 resid_len, + struct fchs_s *rsp_fchs) +{ + struct bfa_fcs_port_ns_s *ns = (struct bfa_fcs_port_ns_s *)cbarg; + struct bfa_fcs_port_s *port = ns->port; + struct ct_hdr_s *cthdr = NULL; + + bfa_trc(port->fcs, port->port_cfg.pwwn); + + /* + * Sanity Checks + */ + if (req_status != BFA_STATUS_OK) { + bfa_trc(port->fcs, req_status); + port->stats.ns_rftid_rsp_err++; + bfa_sm_send_event(ns, NSSM_EVENT_RSP_ERROR); + return; + } + + cthdr = (struct ct_hdr_s *) BFA_FCXP_RSP_PLD(fcxp); + cthdr->cmd_rsp_code = bfa_os_ntohs(cthdr->cmd_rsp_code); + + if (cthdr->cmd_rsp_code == CT_RSP_ACCEPT) { + port->stats.ns_rftid_accepts++; + bfa_sm_send_event(ns, NSSM_EVENT_RSP_OK); + return; + } + + port->stats.ns_rftid_rejects++; + bfa_trc(port->fcs, cthdr->reason_code); + bfa_trc(port->fcs, cthdr->exp_code); + bfa_sm_send_event(ns, NSSM_EVENT_RSP_ERROR); +} + +/** +* Register FC4-Features : Should be done after RFT_ID + */ +static void +bfa_fcs_port_ns_send_rff_id(void *ns_cbarg, struct bfa_fcxp_s *fcxp_alloced) +{ + struct bfa_fcs_port_ns_s *ns = ns_cbarg; + struct bfa_fcs_port_s *port = ns->port; + struct fchs_s fchs; + int len; + struct bfa_fcxp_s *fcxp; + u8 fc4_ftrs = 0; + + bfa_trc(port->fcs, port->port_cfg.pwwn); + + fcxp = fcxp_alloced ? fcxp_alloced : bfa_fcs_fcxp_alloc(port->fcs); + if (!fcxp) { + port->stats.ns_rffid_alloc_wait++; + bfa_fcxp_alloc_wait(port->fcs->bfa, &ns->fcxp_wqe, + bfa_fcs_port_ns_send_rff_id, ns); + return; + } + ns->fcxp = fcxp; + + if (BFA_FCS_VPORT_IS_INITIATOR_MODE(ns->port)) { + fc4_ftrs = FC_GS_FCP_FC4_FEATURE_INITIATOR; + } else if (BFA_FCS_VPORT_IS_TARGET_MODE(ns->port)) { + fc4_ftrs = FC_GS_FCP_FC4_FEATURE_TARGET; + } + + len = fc_rffid_build(&fchs, bfa_fcxp_get_reqbuf(fcxp), + bfa_fcs_port_get_fcid(port), 0, FC_TYPE_FCP, + fc4_ftrs); + + bfa_fcxp_send(fcxp, NULL, port->fabric->vf_id, port->lp_tag, BFA_FALSE, + FC_CLASS_3, len, &fchs, bfa_fcs_port_ns_rff_id_response, + (void *)ns, FC_MAX_PDUSZ, FC_RA_TOV); + + port->stats.ns_rffid_sent++; + bfa_sm_send_event(ns, NSSM_EVENT_RFFID_SENT); +} + +static void +bfa_fcs_port_ns_rff_id_response(void *fcsarg, struct bfa_fcxp_s *fcxp, + void *cbarg, bfa_status_t req_status, + u32 rsp_len, u32 resid_len, + struct fchs_s *rsp_fchs) +{ + struct bfa_fcs_port_ns_s *ns = (struct bfa_fcs_port_ns_s *)cbarg; + struct bfa_fcs_port_s *port = ns->port; + struct ct_hdr_s *cthdr = NULL; + + bfa_trc(port->fcs, port->port_cfg.pwwn); + + /* + * Sanity Checks + */ + if (req_status != BFA_STATUS_OK) { + bfa_trc(port->fcs, req_status); + port->stats.ns_rffid_rsp_err++; + bfa_sm_send_event(ns, NSSM_EVENT_RSP_ERROR); + return; + } + + cthdr = (struct ct_hdr_s *) BFA_FCXP_RSP_PLD(fcxp); + cthdr->cmd_rsp_code = bfa_os_ntohs(cthdr->cmd_rsp_code); + + if (cthdr->cmd_rsp_code == CT_RSP_ACCEPT) { + port->stats.ns_rffid_accepts++; + bfa_sm_send_event(ns, NSSM_EVENT_RSP_OK); + return; + } + + port->stats.ns_rffid_rejects++; + bfa_trc(port->fcs, cthdr->reason_code); + bfa_trc(port->fcs, cthdr->exp_code); + + if (cthdr->reason_code == CT_RSN_NOT_SUPP) { + /* + * if this command is not supported, we don't retry + */ + bfa_sm_send_event(ns, NSSM_EVENT_RSP_OK); + } else { + bfa_sm_send_event(ns, NSSM_EVENT_RSP_ERROR); + } +} + +/** + * Query Fabric for FC4-Types Devices. + * +* TBD : Need to use a local (FCS private) response buffer, since the response + * can be larger than 2K. + */ +static void +bfa_fcs_port_ns_send_gid_ft(void *ns_cbarg, struct bfa_fcxp_s *fcxp_alloced) +{ + struct bfa_fcs_port_ns_s *ns = ns_cbarg; + struct bfa_fcs_port_s *port = ns->port; + struct fchs_s fchs; + int len; + struct bfa_fcxp_s *fcxp; + + bfa_trc(port->fcs, port->pid); + + fcxp = fcxp_alloced ? fcxp_alloced : bfa_fcs_fcxp_alloc(port->fcs); + if (!fcxp) { + port->stats.ns_gidft_alloc_wait++; + bfa_fcxp_alloc_wait(port->fcs->bfa, &ns->fcxp_wqe, + bfa_fcs_port_ns_send_gid_ft, ns); + return; + } + ns->fcxp = fcxp; + + /* + * This query is only initiated for FCP initiator mode. + */ + len = fc_gid_ft_build(&fchs, bfa_fcxp_get_reqbuf(fcxp), ns->port->pid, + FC_TYPE_FCP); + + bfa_fcxp_send(fcxp, NULL, port->fabric->vf_id, port->lp_tag, BFA_FALSE, + FC_CLASS_3, len, &fchs, bfa_fcs_port_ns_gid_ft_response, + (void *)ns, bfa_fcxp_get_maxrsp(port->fcs->bfa), + FC_RA_TOV); + + port->stats.ns_gidft_sent++; + + bfa_sm_send_event(ns, NSSM_EVENT_GIDFT_SENT); +} + +static void +bfa_fcs_port_ns_gid_ft_response(void *fcsarg, struct bfa_fcxp_s *fcxp, + void *cbarg, bfa_status_t req_status, + u32 rsp_len, u32 resid_len, + struct fchs_s *rsp_fchs) +{ + struct bfa_fcs_port_ns_s *ns = (struct bfa_fcs_port_ns_s *)cbarg; + struct bfa_fcs_port_s *port = ns->port; + struct ct_hdr_s *cthdr = NULL; + u32 n_pids; + + bfa_trc(port->fcs, port->port_cfg.pwwn); + + /* + * Sanity Checks + */ + if (req_status != BFA_STATUS_OK) { + bfa_trc(port->fcs, req_status); + port->stats.ns_gidft_rsp_err++; + bfa_sm_send_event(ns, NSSM_EVENT_RSP_ERROR); + return; + } + + if (resid_len != 0) { + /* + * TBD : we will need to allocate a larger buffer & retry the + * command + */ + bfa_trc(port->fcs, rsp_len); + bfa_trc(port->fcs, resid_len); + return; + } + + cthdr = (struct ct_hdr_s *) BFA_FCXP_RSP_PLD(fcxp); + cthdr->cmd_rsp_code = bfa_os_ntohs(cthdr->cmd_rsp_code); + + switch (cthdr->cmd_rsp_code) { + + case CT_RSP_ACCEPT: + + port->stats.ns_gidft_accepts++; + n_pids = (fc_get_ctresp_pyld_len(rsp_len) / sizeof(u32)); + bfa_trc(port->fcs, n_pids); + bfa_fcs_port_ns_process_gidft_pids(port, + (u32 *) (cthdr + 1), + n_pids); + bfa_sm_send_event(ns, NSSM_EVENT_RSP_OK); + break; + + case CT_RSP_REJECT: + + /* + * Check the reason code & explanation. + * There may not have been any FC4 devices in the fabric + */ + port->stats.ns_gidft_rejects++; + bfa_trc(port->fcs, cthdr->reason_code); + bfa_trc(port->fcs, cthdr->exp_code); + + if ((cthdr->reason_code == CT_RSN_UNABLE_TO_PERF) + && (cthdr->exp_code == CT_NS_EXP_FT_NOT_REG)) { + + bfa_sm_send_event(ns, NSSM_EVENT_RSP_OK); + } else { + /* + * for all other errors, retry + */ + bfa_sm_send_event(ns, NSSM_EVENT_RSP_ERROR); + } + break; + + default: + port->stats.ns_gidft_unknown_rsp++; + bfa_trc(port->fcs, cthdr->cmd_rsp_code); + bfa_sm_send_event(ns, NSSM_EVENT_RSP_ERROR); + } +} + +/** + * This routine will be called by bfa_timer on timer timeouts. + * + * param[in] port - pointer to bfa_fcs_port_t. + * + * return + * void + * +* Special Considerations: + * + * note + */ +static void +bfa_fcs_port_ns_timeout(void *arg) +{ + struct bfa_fcs_port_ns_s *ns = (struct bfa_fcs_port_ns_s *)arg; + + ns->port->stats.ns_timeouts++; + bfa_sm_send_event(ns, NSSM_EVENT_TIMEOUT); +} + +/* + * Process the PID list in GID_FT response + */ +static void +bfa_fcs_port_ns_process_gidft_pids(struct bfa_fcs_port_s *port, + u32 *pid_buf, u32 n_pids) +{ + struct fcgs_gidft_resp_s *gidft_entry; + struct bfa_fcs_rport_s *rport; + u32 ii; + + for (ii = 0; ii < n_pids; ii++) { + gidft_entry = (struct fcgs_gidft_resp_s *) &pid_buf[ii]; + + if (gidft_entry->pid == port->pid) + continue; + + /* + * Check if this rport already exists + */ + rport = bfa_fcs_port_get_rport_by_pid(port, gidft_entry->pid); + if (rport == NULL) { + /* + * this is a new device. create rport + */ + rport = bfa_fcs_rport_create(port, gidft_entry->pid); + } else { + /* + * this rport already exists + */ + bfa_fcs_rport_scn(rport); + } + + bfa_trc(port->fcs, gidft_entry->pid); + + /* + * if the last entry bit is set, bail out. + */ + if (gidft_entry->last) + return; + } +} + +/** + * fcs_ns_public FCS nameserver public interfaces + */ + +/* + * Functions called by port/fab. + * These will send relevant Events to the ns state machine. + */ +void +bfa_fcs_port_ns_init(struct bfa_fcs_port_s *port) +{ + struct bfa_fcs_port_ns_s *ns = BFA_FCS_GET_NS_FROM_PORT(port); + + ns->port = port; + bfa_sm_set_state(ns, bfa_fcs_port_ns_sm_offline); +} + +void +bfa_fcs_port_ns_offline(struct bfa_fcs_port_s *port) +{ + struct bfa_fcs_port_ns_s *ns = BFA_FCS_GET_NS_FROM_PORT(port); + + ns->port = port; + bfa_sm_send_event(ns, NSSM_EVENT_PORT_OFFLINE); +} + +void +bfa_fcs_port_ns_online(struct bfa_fcs_port_s *port) +{ + struct bfa_fcs_port_ns_s *ns = BFA_FCS_GET_NS_FROM_PORT(port); + + ns->port = port; + bfa_sm_send_event(ns, NSSM_EVENT_PORT_ONLINE); +} + +void +bfa_fcs_port_ns_query(struct bfa_fcs_port_s *port) +{ + struct bfa_fcs_port_ns_s *ns = BFA_FCS_GET_NS_FROM_PORT(port); + + bfa_trc(port->fcs, port->pid); + bfa_sm_send_event(ns, NSSM_EVENT_NS_QUERY); +} + +static void +bfa_fcs_port_ns_boot_target_disc(struct bfa_fcs_port_s *port) +{ + + struct bfa_fcs_rport_s *rport; + u8 nwwns; + wwn_t *wwns; + int ii; + + bfa_iocfc_get_bootwwns(port->fcs->bfa, &nwwns, &wwns); + + for (ii = 0; ii < nwwns; ++ii) { + rport = bfa_fcs_rport_create_by_wwn(port, wwns[ii]); + bfa_assert(rport); + } +} + + diff --git a/drivers/scsi/bfa/plog.c b/drivers/scsi/bfa/plog.c new file mode 100644 index 000000000000..86af818d17bb --- /dev/null +++ b/drivers/scsi/bfa/plog.c @@ -0,0 +1,184 @@ +/* + * Copyright (c) 2005-2009 Brocade Communications Systems, Inc. + * All rights reserved + * www.brocade.com + * + * Linux driver for Brocade Fibre Channel Host Bus Adapter. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (GPL) Version 2 as + * published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#include <bfa_os_inc.h> +#include <cs/bfa_plog.h> +#include <cs/bfa_debug.h> + +static int +plkd_validate_logrec(struct bfa_plog_rec_s *pl_rec) +{ + if ((pl_rec->log_type != BFA_PL_LOG_TYPE_INT) + && (pl_rec->log_type != BFA_PL_LOG_TYPE_STRING)) + return 1; + + if ((pl_rec->log_type != BFA_PL_LOG_TYPE_INT) + && (pl_rec->log_num_ints > BFA_PL_INT_LOG_SZ)) + return 1; + + return 0; +} + +static void +bfa_plog_add(struct bfa_plog_s *plog, struct bfa_plog_rec_s *pl_rec) +{ + u16 tail; + struct bfa_plog_rec_s *pl_recp; + + if (plog->plog_enabled == 0) + return; + + if (plkd_validate_logrec(pl_rec)) { + bfa_assert(0); + return; + } + + tail = plog->tail; + + pl_recp = &(plog->plog_recs[tail]); + + bfa_os_memcpy(pl_recp, pl_rec, sizeof(struct bfa_plog_rec_s)); + + pl_recp->tv = BFA_TRC_TS(plog); + BFA_PL_LOG_REC_INCR(plog->tail); + + if (plog->head == plog->tail) + BFA_PL_LOG_REC_INCR(plog->head); +} + +void +bfa_plog_init(struct bfa_plog_s *plog) +{ + bfa_os_memset((char *)plog, 0, sizeof(struct bfa_plog_s)); + + bfa_os_memcpy(plog->plog_sig, BFA_PL_SIG_STR, BFA_PL_SIG_LEN); + plog->head = plog->tail = 0; + plog->plog_enabled = 1; +} + +void +bfa_plog_str(struct bfa_plog_s *plog, enum bfa_plog_mid mid, + enum bfa_plog_eid event, + u16 misc, char *log_str) +{ + struct bfa_plog_rec_s lp; + + if (plog->plog_enabled) { + bfa_os_memset(&lp, 0, sizeof(struct bfa_plog_rec_s)); + lp.mid = mid; + lp.eid = event; + lp.log_type = BFA_PL_LOG_TYPE_STRING; + lp.misc = misc; + strncpy(lp.log_entry.string_log, log_str, + BFA_PL_STRING_LOG_SZ - 1); + lp.log_entry.string_log[BFA_PL_STRING_LOG_SZ - 1] = '\0'; + bfa_plog_add(plog, &lp); + } +} + +void +bfa_plog_intarr(struct bfa_plog_s *plog, enum bfa_plog_mid mid, + enum bfa_plog_eid event, + u16 misc, u32 *intarr, u32 num_ints) +{ + struct bfa_plog_rec_s lp; + u32 i; + + if (num_ints > BFA_PL_INT_LOG_SZ) + num_ints = BFA_PL_INT_LOG_SZ; + + if (plog->plog_enabled) { + bfa_os_memset(&lp, 0, sizeof(struct bfa_plog_rec_s)); + lp.mid = mid; + lp.eid = event; + lp.log_type = BFA_PL_LOG_TYPE_INT; + lp.misc = misc; + + for (i = 0; i < num_ints; i++) + bfa_os_assign(lp.log_entry.int_log[i], + intarr[i]); + + lp.log_num_ints = (u8) num_ints; + + bfa_plog_add(plog, &lp); + } +} + +void +bfa_plog_fchdr(struct bfa_plog_s *plog, enum bfa_plog_mid mid, + enum bfa_plog_eid event, + u16 misc, struct fchs_s *fchdr) +{ + struct bfa_plog_rec_s lp; + u32 *tmp_int = (u32 *) fchdr; + u32 ints[BFA_PL_INT_LOG_SZ]; + + if (plog->plog_enabled) { + bfa_os_memset(&lp, 0, sizeof(struct bfa_plog_rec_s)); + + ints[0] = tmp_int[0]; + ints[1] = tmp_int[1]; + ints[2] = tmp_int[4]; + + bfa_plog_intarr(plog, mid, event, misc, ints, 3); + } +} + +void +bfa_plog_fchdr_and_pl(struct bfa_plog_s *plog, enum bfa_plog_mid mid, + enum bfa_plog_eid event, u16 misc, struct fchs_s *fchdr, + u32 pld_w0) +{ + struct bfa_plog_rec_s lp; + u32 *tmp_int = (u32 *) fchdr; + u32 ints[BFA_PL_INT_LOG_SZ]; + + if (plog->plog_enabled) { + bfa_os_memset(&lp, 0, sizeof(struct bfa_plog_rec_s)); + + ints[0] = tmp_int[0]; + ints[1] = tmp_int[1]; + ints[2] = tmp_int[4]; + ints[3] = pld_w0; + + bfa_plog_intarr(plog, mid, event, misc, ints, 4); + } +} + +void +bfa_plog_clear(struct bfa_plog_s *plog) +{ + plog->head = plog->tail = 0; +} + +void +bfa_plog_enable(struct bfa_plog_s *plog) +{ + plog->plog_enabled = 1; +} + +void +bfa_plog_disable(struct bfa_plog_s *plog) +{ + plog->plog_enabled = 0; +} + +bfa_boolean_t +bfa_plog_get_setting(struct bfa_plog_s *plog) +{ + return((bfa_boolean_t)plog->plog_enabled); +} diff --git a/drivers/scsi/bfa/rport.c b/drivers/scsi/bfa/rport.c new file mode 100644 index 000000000000..9cf58bb138dc --- /dev/null +++ b/drivers/scsi/bfa/rport.c @@ -0,0 +1,2618 @@ +/* + * Copyright (c) 2005-2009 Brocade Communications Systems, Inc. + * All rights reserved + * www.brocade.com + * + * Linux driver for Brocade Fibre Channel Host Bus Adapter. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (GPL) Version 2 as + * published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +/** + * rport.c Remote port implementation. + */ + +#include <bfa.h> +#include <bfa_svc.h> +#include "fcbuild.h" +#include "fcs_vport.h" +#include "fcs_lport.h" +#include "fcs_rport.h" +#include "fcs_fcpim.h" +#include "fcs_fcptm.h" +#include "fcs_trcmod.h" +#include "fcs_fcxp.h" +#include "fcs.h" +#include <fcb/bfa_fcb_rport.h> +#include <aen/bfa_aen_rport.h> + +BFA_TRC_FILE(FCS, RPORT); + +#define BFA_FCS_RPORT_MAX_RETRIES (5) + +/* In millisecs */ +static u32 bfa_fcs_rport_del_timeout = + BFA_FCS_RPORT_DEF_DEL_TIMEOUT * 1000; + +/* + * forward declarations + */ +static struct bfa_fcs_rport_s *bfa_fcs_rport_alloc(struct bfa_fcs_port_s *port, + wwn_t pwwn, u32 rpid); +static void bfa_fcs_rport_free(struct bfa_fcs_rport_s *rport); +static void bfa_fcs_rport_hal_online(struct bfa_fcs_rport_s *rport); +static void bfa_fcs_rport_online_action(struct bfa_fcs_rport_s *rport); +static void bfa_fcs_rport_offline_action(struct bfa_fcs_rport_s *rport); +static void bfa_fcs_rport_update(struct bfa_fcs_rport_s *rport, + struct fc_logi_s *plogi); +static void bfa_fcs_rport_fc4_pause(struct bfa_fcs_rport_s *rport); +static void bfa_fcs_rport_fc4_resume(struct bfa_fcs_rport_s *rport); +static void bfa_fcs_rport_timeout(void *arg); +static void bfa_fcs_rport_send_plogi(void *rport_cbarg, + struct bfa_fcxp_s *fcxp_alloced); +static void bfa_fcs_rport_send_plogiacc(void *rport_cbarg, + struct bfa_fcxp_s *fcxp_alloced); +static void bfa_fcs_rport_plogi_response(void *fcsarg, + struct bfa_fcxp_s *fcxp, + void *cbarg, + bfa_status_t req_status, + u32 rsp_len, + u32 resid_len, + struct fchs_s *rsp_fchs); +static void bfa_fcs_rport_send_adisc(void *rport_cbarg, + struct bfa_fcxp_s *fcxp_alloced); +static void bfa_fcs_rport_adisc_response(void *fcsarg, + struct bfa_fcxp_s *fcxp, + void *cbarg, + bfa_status_t req_status, + u32 rsp_len, + u32 resid_len, + struct fchs_s *rsp_fchs); +static void bfa_fcs_rport_send_gidpn(void *rport_cbarg, + struct bfa_fcxp_s *fcxp_alloced); +static void bfa_fcs_rport_gidpn_response(void *fcsarg, + struct bfa_fcxp_s *fcxp, + void *cbarg, + bfa_status_t req_status, + u32 rsp_len, + u32 resid_len, + struct fchs_s *rsp_fchs); +static void bfa_fcs_rport_send_logo(void *rport_cbarg, + struct bfa_fcxp_s *fcxp_alloced); +static void bfa_fcs_rport_send_logo_acc(void *rport_cbarg); +static void bfa_fcs_rport_process_prli(struct bfa_fcs_rport_s *rport, + struct fchs_s *rx_fchs, u16 len); +static void bfa_fcs_rport_send_ls_rjt(struct bfa_fcs_rport_s *rport, + struct fchs_s *rx_fchs, u8 reason_code, + u8 reason_code_expl); +static void bfa_fcs_rport_process_adisc(struct bfa_fcs_rport_s *rport, + struct fchs_s *rx_fchs, u16 len); +/** + * fcs_rport_sm FCS rport state machine events + */ + +enum rport_event { + RPSM_EVENT_PLOGI_SEND = 1, /* new rport; start with PLOGI */ + RPSM_EVENT_PLOGI_RCVD = 2, /* Inbound PLOGI from remote port */ + RPSM_EVENT_PLOGI_COMP = 3, /* PLOGI completed to rport */ + RPSM_EVENT_LOGO_RCVD = 4, /* LOGO from remote device */ + RPSM_EVENT_LOGO_IMP = 5, /* implicit logo for SLER */ + RPSM_EVENT_FCXP_SENT = 6, /* Frame from has been sent */ + RPSM_EVENT_DELETE = 7, /* RPORT delete request */ + RPSM_EVENT_SCN = 8, /* state change notification */ + RPSM_EVENT_ACCEPTED = 9,/* Good response from remote device */ + RPSM_EVENT_FAILED = 10, /* Request to rport failed. */ + RPSM_EVENT_TIMEOUT = 11, /* Rport SM timeout event */ + RPSM_EVENT_HCB_ONLINE = 12, /* BFA rport online callback */ + RPSM_EVENT_HCB_OFFLINE = 13, /* BFA rport offline callback */ + RPSM_EVENT_FC4_OFFLINE = 14, /* FC-4 offline complete */ + RPSM_EVENT_ADDRESS_CHANGE = 15, /* Rport's PID has changed */ + RPSM_EVENT_ADDRESS_DISC = 16 /* Need to Discover rport's PID */ +}; + +static void bfa_fcs_rport_sm_uninit(struct bfa_fcs_rport_s *rport, + enum rport_event event); +static void bfa_fcs_rport_sm_plogi_sending(struct bfa_fcs_rport_s *rport, + enum rport_event event); +static void bfa_fcs_rport_sm_plogiacc_sending(struct bfa_fcs_rport_s *rport, + enum rport_event event); +static void bfa_fcs_rport_sm_plogi_retry(struct bfa_fcs_rport_s *rport, + enum rport_event event); +static void bfa_fcs_rport_sm_plogi(struct bfa_fcs_rport_s *rport, + enum rport_event event); +static void bfa_fcs_rport_sm_hal_online(struct bfa_fcs_rport_s *rport, + enum rport_event event); +static void bfa_fcs_rport_sm_online(struct bfa_fcs_rport_s *rport, + enum rport_event event); +static void bfa_fcs_rport_sm_nsquery_sending(struct bfa_fcs_rport_s *rport, + enum rport_event event); +static void bfa_fcs_rport_sm_nsquery(struct bfa_fcs_rport_s *rport, + enum rport_event event); +static void bfa_fcs_rport_sm_adisc_sending(struct bfa_fcs_rport_s *rport, + enum rport_event event); +static void bfa_fcs_rport_sm_adisc(struct bfa_fcs_rport_s *rport, + enum rport_event event); +static void bfa_fcs_rport_sm_fc4_logorcv(struct bfa_fcs_rport_s *rport, + enum rport_event event); +static void bfa_fcs_rport_sm_fc4_logosend(struct bfa_fcs_rport_s *rport, + enum rport_event event); +static void bfa_fcs_rport_sm_fc4_offline(struct bfa_fcs_rport_s *rport, + enum rport_event event); +static void bfa_fcs_rport_sm_hcb_offline(struct bfa_fcs_rport_s *rport, + enum rport_event event); +static void bfa_fcs_rport_sm_hcb_logorcv(struct bfa_fcs_rport_s *rport, + enum rport_event event); +static void bfa_fcs_rport_sm_hcb_logosend(struct bfa_fcs_rport_s *rport, + enum rport_event event); +static void bfa_fcs_rport_sm_logo_sending(struct bfa_fcs_rport_s *rport, + enum rport_event event); +static void bfa_fcs_rport_sm_offline(struct bfa_fcs_rport_s *rport, + enum rport_event event); +static void bfa_fcs_rport_sm_nsdisc_sending(struct bfa_fcs_rport_s *rport, + enum rport_event event); +static void bfa_fcs_rport_sm_nsdisc_retry(struct bfa_fcs_rport_s *rport, + enum rport_event event); +static void bfa_fcs_rport_sm_nsdisc_sent(struct bfa_fcs_rport_s *rport, + enum rport_event event); +static void bfa_fcs_rport_sm_nsdisc_sent(struct bfa_fcs_rport_s *rport, + enum rport_event event); + +static struct bfa_sm_table_s rport_sm_table[] = { + {BFA_SM(bfa_fcs_rport_sm_uninit), BFA_RPORT_UNINIT}, + {BFA_SM(bfa_fcs_rport_sm_plogi_sending), BFA_RPORT_PLOGI}, + {BFA_SM(bfa_fcs_rport_sm_plogiacc_sending), BFA_RPORT_ONLINE}, + {BFA_SM(bfa_fcs_rport_sm_plogi_retry), BFA_RPORT_PLOGI_RETRY}, + {BFA_SM(bfa_fcs_rport_sm_plogi), BFA_RPORT_PLOGI}, + {BFA_SM(bfa_fcs_rport_sm_hal_online), BFA_RPORT_ONLINE}, + {BFA_SM(bfa_fcs_rport_sm_online), BFA_RPORT_ONLINE}, + {BFA_SM(bfa_fcs_rport_sm_nsquery_sending), BFA_RPORT_NSQUERY}, + {BFA_SM(bfa_fcs_rport_sm_nsquery), BFA_RPORT_NSQUERY}, + {BFA_SM(bfa_fcs_rport_sm_adisc_sending), BFA_RPORT_ADISC}, + {BFA_SM(bfa_fcs_rport_sm_adisc), BFA_RPORT_ADISC}, + {BFA_SM(bfa_fcs_rport_sm_fc4_logorcv), BFA_RPORT_LOGORCV}, + {BFA_SM(bfa_fcs_rport_sm_fc4_logosend), BFA_RPORT_LOGO}, + {BFA_SM(bfa_fcs_rport_sm_fc4_offline), BFA_RPORT_OFFLINE}, + {BFA_SM(bfa_fcs_rport_sm_hcb_offline), BFA_RPORT_OFFLINE}, + {BFA_SM(bfa_fcs_rport_sm_hcb_logorcv), BFA_RPORT_LOGORCV}, + {BFA_SM(bfa_fcs_rport_sm_hcb_logosend), BFA_RPORT_LOGO}, + {BFA_SM(bfa_fcs_rport_sm_logo_sending), BFA_RPORT_LOGO}, + {BFA_SM(bfa_fcs_rport_sm_offline), BFA_RPORT_OFFLINE}, + {BFA_SM(bfa_fcs_rport_sm_nsdisc_sending), BFA_RPORT_NSDISC}, + {BFA_SM(bfa_fcs_rport_sm_nsdisc_retry), BFA_RPORT_NSDISC}, + {BFA_SM(bfa_fcs_rport_sm_nsdisc_sent), BFA_RPORT_NSDISC}, +}; + +/** + * Beginning state. + */ +static void +bfa_fcs_rport_sm_uninit(struct bfa_fcs_rport_s *rport, enum rport_event event) +{ + bfa_trc(rport->fcs, rport->pwwn); + bfa_trc(rport->fcs, rport->pid); + bfa_trc(rport->fcs, event); + + switch (event) { + case RPSM_EVENT_PLOGI_SEND: + bfa_sm_set_state(rport, bfa_fcs_rport_sm_plogi_sending); + rport->plogi_retries = 0; + bfa_fcs_rport_send_plogi(rport, NULL); + break; + + case RPSM_EVENT_PLOGI_RCVD: + bfa_sm_set_state(rport, bfa_fcs_rport_sm_plogiacc_sending); + bfa_fcs_rport_send_plogiacc(rport, NULL); + break; + + case RPSM_EVENT_PLOGI_COMP: + bfa_sm_set_state(rport, bfa_fcs_rport_sm_hal_online); + bfa_fcs_rport_hal_online(rport); + break; + + case RPSM_EVENT_ADDRESS_CHANGE: + case RPSM_EVENT_ADDRESS_DISC: + bfa_sm_set_state(rport, bfa_fcs_rport_sm_nsdisc_sending); + rport->ns_retries = 0; + bfa_fcs_rport_send_gidpn(rport, NULL); + break; + + default: + bfa_assert(0); + } +} + +/** + * PLOGI is being sent. + */ +static void +bfa_fcs_rport_sm_plogi_sending(struct bfa_fcs_rport_s *rport, + enum rport_event event) +{ + bfa_trc(rport->fcs, rport->pwwn); + bfa_trc(rport->fcs, rport->pid); + bfa_trc(rport->fcs, event); + + switch (event) { + case RPSM_EVENT_FCXP_SENT: + bfa_sm_set_state(rport, bfa_fcs_rport_sm_plogi); + break; + + case RPSM_EVENT_DELETE: + bfa_sm_set_state(rport, bfa_fcs_rport_sm_uninit); + bfa_fcxp_walloc_cancel(rport->fcs->bfa, &rport->fcxp_wqe); + bfa_fcs_rport_free(rport); + break; + + case RPSM_EVENT_PLOGI_RCVD: + bfa_sm_set_state(rport, bfa_fcs_rport_sm_plogiacc_sending); + bfa_fcxp_walloc_cancel(rport->fcs->bfa, &rport->fcxp_wqe); + bfa_fcs_rport_send_plogiacc(rport, NULL); + break; + + case RPSM_EVENT_ADDRESS_CHANGE: + bfa_fcxp_walloc_cancel(rport->fcs->bfa, &rport->fcxp_wqe); + bfa_sm_set_state(rport, bfa_fcs_rport_sm_nsdisc_sending); + rport->ns_retries = 0; + bfa_fcs_rport_send_gidpn(rport, NULL); + break; + + case RPSM_EVENT_LOGO_IMP: + rport->pid = 0; + bfa_sm_set_state(rport, bfa_fcs_rport_sm_offline); + bfa_fcxp_walloc_cancel(rport->fcs->bfa, &rport->fcxp_wqe); + bfa_timer_start(rport->fcs->bfa, &rport->timer, + bfa_fcs_rport_timeout, rport, + bfa_fcs_rport_del_timeout); + break; + + case RPSM_EVENT_SCN: + break; + + default: + bfa_assert(0); + } +} + +/** + * PLOGI is being sent. + */ +static void +bfa_fcs_rport_sm_plogiacc_sending(struct bfa_fcs_rport_s *rport, + enum rport_event event) +{ + bfa_trc(rport->fcs, rport->pwwn); + bfa_trc(rport->fcs, rport->pid); + bfa_trc(rport->fcs, event); + + switch (event) { + case RPSM_EVENT_FCXP_SENT: + bfa_sm_set_state(rport, bfa_fcs_rport_sm_hal_online); + bfa_fcs_rport_hal_online(rport); + break; + + case RPSM_EVENT_DELETE: + bfa_sm_set_state(rport, bfa_fcs_rport_sm_uninit); + bfa_fcxp_walloc_cancel(rport->fcs->bfa, &rport->fcxp_wqe); + bfa_fcs_rport_free(rport); + break; + + case RPSM_EVENT_SCN: + /** + * Ignore, SCN is possibly online notification. + */ + break; + + case RPSM_EVENT_ADDRESS_CHANGE: + bfa_fcxp_walloc_cancel(rport->fcs->bfa, &rport->fcxp_wqe); + bfa_sm_set_state(rport, bfa_fcs_rport_sm_nsdisc_sending); + rport->ns_retries = 0; + bfa_fcs_rport_send_gidpn(rport, NULL); + break; + + case RPSM_EVENT_LOGO_IMP: + rport->pid = 0; + bfa_sm_set_state(rport, bfa_fcs_rport_sm_offline); + bfa_fcxp_walloc_cancel(rport->fcs->bfa, &rport->fcxp_wqe); + bfa_timer_start(rport->fcs->bfa, &rport->timer, + bfa_fcs_rport_timeout, rport, + bfa_fcs_rport_del_timeout); + break; + + case RPSM_EVENT_HCB_OFFLINE: + /** + * Ignore BFA callback, on a PLOGI receive we call bfa offline. + */ + break; + + default: + bfa_assert(0); + } +} + +/** + * PLOGI is sent. + */ +static void +bfa_fcs_rport_sm_plogi_retry(struct bfa_fcs_rport_s *rport, + enum rport_event event) +{ + bfa_trc(rport->fcs, rport->pwwn); + bfa_trc(rport->fcs, rport->pid); + bfa_trc(rport->fcs, event); + + switch (event) { + case RPSM_EVENT_SCN: + bfa_timer_stop(&rport->timer); + /* + * !! fall through !! + */ + + case RPSM_EVENT_TIMEOUT: + rport->plogi_retries++; + if (rport->plogi_retries < BFA_FCS_RPORT_MAX_RETRIES) { + bfa_sm_set_state(rport, bfa_fcs_rport_sm_plogi_sending); + bfa_fcs_rport_send_plogi(rport, NULL); + } else { + rport->pid = 0; + bfa_sm_set_state(rport, bfa_fcs_rport_sm_offline); + bfa_timer_start(rport->fcs->bfa, &rport->timer, + bfa_fcs_rport_timeout, rport, + bfa_fcs_rport_del_timeout); + } + break; + + case RPSM_EVENT_DELETE: + bfa_sm_set_state(rport, bfa_fcs_rport_sm_uninit); + bfa_timer_stop(&rport->timer); + bfa_fcs_rport_free(rport); + break; + + case RPSM_EVENT_LOGO_RCVD: + break; + + case RPSM_EVENT_PLOGI_RCVD: + bfa_sm_set_state(rport, bfa_fcs_rport_sm_plogiacc_sending); + bfa_timer_stop(&rport->timer); + bfa_fcs_rport_send_plogiacc(rport, NULL); + break; + + case RPSM_EVENT_ADDRESS_CHANGE: + bfa_timer_stop(&rport->timer); + bfa_sm_set_state(rport, bfa_fcs_rport_sm_nsdisc_sending); + rport->ns_retries = 0; + bfa_fcs_rport_send_gidpn(rport, NULL); + break; + + case RPSM_EVENT_LOGO_IMP: + rport->pid = 0; + bfa_sm_set_state(rport, bfa_fcs_rport_sm_offline); + bfa_timer_stop(&rport->timer); + bfa_timer_start(rport->fcs->bfa, &rport->timer, + bfa_fcs_rport_timeout, rport, + bfa_fcs_rport_del_timeout); + break; + + case RPSM_EVENT_PLOGI_COMP: + bfa_sm_set_state(rport, bfa_fcs_rport_sm_hal_online); + bfa_timer_stop(&rport->timer); + bfa_fcs_rport_hal_online(rport); + break; + + default: + bfa_assert(0); + } +} + +/** + * PLOGI is sent. + */ +static void +bfa_fcs_rport_sm_plogi(struct bfa_fcs_rport_s *rport, enum rport_event event) +{ + bfa_trc(rport->fcs, rport->pwwn); + bfa_trc(rport->fcs, rport->pid); + bfa_trc(rport->fcs, event); + + switch (event) { + case RPSM_EVENT_ACCEPTED: + bfa_sm_set_state(rport, bfa_fcs_rport_sm_hal_online); + rport->plogi_retries = 0; + bfa_fcs_rport_hal_online(rport); + break; + + case RPSM_EVENT_LOGO_RCVD: + bfa_fcs_rport_send_logo_acc(rport); + bfa_fcxp_discard(rport->fcxp); + /* + * !! fall through !! + */ + case RPSM_EVENT_FAILED: + bfa_sm_set_state(rport, bfa_fcs_rport_sm_plogi_retry); + bfa_timer_start(rport->fcs->bfa, &rport->timer, + bfa_fcs_rport_timeout, rport, + BFA_FCS_RETRY_TIMEOUT); + break; + + case RPSM_EVENT_LOGO_IMP: + rport->pid = 0; + bfa_sm_set_state(rport, bfa_fcs_rport_sm_offline); + bfa_fcxp_discard(rport->fcxp); + bfa_timer_start(rport->fcs->bfa, &rport->timer, + bfa_fcs_rport_timeout, rport, + bfa_fcs_rport_del_timeout); + break; + + case RPSM_EVENT_ADDRESS_CHANGE: + bfa_fcxp_discard(rport->fcxp); + bfa_sm_set_state(rport, bfa_fcs_rport_sm_nsdisc_sending); + rport->ns_retries = 0; + bfa_fcs_rport_send_gidpn(rport, NULL); + break; + + case RPSM_EVENT_PLOGI_RCVD: + bfa_sm_set_state(rport, bfa_fcs_rport_sm_plogiacc_sending); + bfa_fcxp_discard(rport->fcxp); + bfa_fcs_rport_send_plogiacc(rport, NULL); + break; + + case RPSM_EVENT_SCN: + /** + * Ignore SCN - wait for PLOGI response. + */ + break; + + case RPSM_EVENT_DELETE: + bfa_sm_set_state(rport, bfa_fcs_rport_sm_uninit); + bfa_fcxp_discard(rport->fcxp); + bfa_fcs_rport_free(rport); + break; + + case RPSM_EVENT_PLOGI_COMP: + bfa_sm_set_state(rport, bfa_fcs_rport_sm_hal_online); + bfa_fcxp_discard(rport->fcxp); + bfa_fcs_rport_hal_online(rport); + break; + + default: + bfa_assert(0); + } +} + +/** + * PLOGI is complete. Awaiting BFA rport online callback. FC-4s + * are offline. + */ +static void +bfa_fcs_rport_sm_hal_online(struct bfa_fcs_rport_s *rport, + enum rport_event event) +{ + bfa_trc(rport->fcs, rport->pwwn); + bfa_trc(rport->fcs, rport->pid); + bfa_trc(rport->fcs, event); + + switch (event) { + case RPSM_EVENT_HCB_ONLINE: + bfa_sm_set_state(rport, bfa_fcs_rport_sm_online); + bfa_fcs_rport_online_action(rport); + break; + + case RPSM_EVENT_LOGO_RCVD: + bfa_sm_set_state(rport, bfa_fcs_rport_sm_hcb_logorcv); + bfa_rport_offline(rport->bfa_rport); + break; + + case RPSM_EVENT_LOGO_IMP: + case RPSM_EVENT_ADDRESS_CHANGE: + bfa_sm_set_state(rport, bfa_fcs_rport_sm_hcb_offline); + bfa_rport_offline(rport->bfa_rport); + break; + + case RPSM_EVENT_PLOGI_RCVD: + bfa_sm_set_state(rport, bfa_fcs_rport_sm_plogiacc_sending); + bfa_rport_offline(rport->bfa_rport); + bfa_fcs_rport_send_plogiacc(rport, NULL); + break; + + case RPSM_EVENT_DELETE: + bfa_sm_set_state(rport, bfa_fcs_rport_sm_hcb_logosend); + bfa_rport_offline(rport->bfa_rport); + break; + + case RPSM_EVENT_SCN: + /** + * @todo + * Ignore SCN - PLOGI just completed, FC-4 login should detect + * device failures. + */ + break; + + default: + bfa_assert(0); + } +} + +/** + * Rport is ONLINE. FC-4s active. + */ +static void +bfa_fcs_rport_sm_online(struct bfa_fcs_rport_s *rport, enum rport_event event) +{ + bfa_trc(rport->fcs, rport->pwwn); + bfa_trc(rport->fcs, rport->pid); + bfa_trc(rport->fcs, event); + + switch (event) { + case RPSM_EVENT_SCN: + /** + * Pause FC-4 activity till rport is authenticated. + * In switched fabrics, check presence of device in nameserver + * first. + */ + bfa_fcs_rport_fc4_pause(rport); + + if (bfa_fcs_fabric_is_switched(rport->port->fabric)) { + bfa_sm_set_state(rport, + bfa_fcs_rport_sm_nsquery_sending); + rport->ns_retries = 0; + bfa_fcs_rport_send_gidpn(rport, NULL); + } else { + bfa_sm_set_state(rport, bfa_fcs_rport_sm_adisc_sending); + bfa_fcs_rport_send_adisc(rport, NULL); + } + break; + + case RPSM_EVENT_PLOGI_RCVD: + case RPSM_EVENT_LOGO_IMP: + case RPSM_EVENT_ADDRESS_CHANGE: + bfa_sm_set_state(rport, bfa_fcs_rport_sm_fc4_offline); + bfa_fcs_rport_offline_action(rport); + break; + + case RPSM_EVENT_DELETE: + bfa_sm_set_state(rport, bfa_fcs_rport_sm_fc4_logosend); + bfa_fcs_rport_offline_action(rport); + break; + + case RPSM_EVENT_LOGO_RCVD: + bfa_sm_set_state(rport, bfa_fcs_rport_sm_fc4_logorcv); + bfa_fcs_rport_offline_action(rport); + break; + + case RPSM_EVENT_PLOGI_COMP: + break; + + default: + bfa_assert(0); + } +} + +/** + * An SCN event is received in ONLINE state. NS query is being sent + * prior to ADISC authentication with rport. FC-4s are paused. + */ +static void +bfa_fcs_rport_sm_nsquery_sending(struct bfa_fcs_rport_s *rport, + enum rport_event event) +{ + bfa_trc(rport->fcs, rport->pwwn); + bfa_trc(rport->fcs, rport->pid); + bfa_trc(rport->fcs, event); + + switch (event) { + case RPSM_EVENT_FCXP_SENT: + bfa_sm_set_state(rport, bfa_fcs_rport_sm_nsquery); + break; + + case RPSM_EVENT_DELETE: + bfa_sm_set_state(rport, bfa_fcs_rport_sm_fc4_logosend); + bfa_fcxp_walloc_cancel(rport->fcs->bfa, &rport->fcxp_wqe); + bfa_fcs_rport_offline_action(rport); + break; + + case RPSM_EVENT_SCN: + /** + * ignore SCN, wait for response to query itself + */ + break; + + case RPSM_EVENT_LOGO_RCVD: + bfa_sm_set_state(rport, bfa_fcs_rport_sm_fc4_logorcv); + bfa_fcxp_walloc_cancel(rport->fcs->bfa, &rport->fcxp_wqe); + bfa_fcs_rport_offline_action(rport); + break; + + case RPSM_EVENT_LOGO_IMP: + rport->pid = 0; + bfa_sm_set_state(rport, bfa_fcs_rport_sm_offline); + bfa_fcxp_walloc_cancel(rport->fcs->bfa, &rport->fcxp_wqe); + bfa_timer_start(rport->fcs->bfa, &rport->timer, + bfa_fcs_rport_timeout, rport, + bfa_fcs_rport_del_timeout); + break; + + case RPSM_EVENT_PLOGI_RCVD: + case RPSM_EVENT_ADDRESS_CHANGE: + case RPSM_EVENT_PLOGI_COMP: + bfa_sm_set_state(rport, bfa_fcs_rport_sm_fc4_offline); + bfa_fcxp_walloc_cancel(rport->fcs->bfa, &rport->fcxp_wqe); + bfa_fcs_rport_offline_action(rport); + break; + + default: + bfa_assert(0); + } +} + +/** + * An SCN event is received in ONLINE state. NS query is sent to rport. + * FC-4s are paused. + */ +static void +bfa_fcs_rport_sm_nsquery(struct bfa_fcs_rport_s *rport, enum rport_event event) +{ + bfa_trc(rport->fcs, rport->pwwn); + bfa_trc(rport->fcs, rport->pid); + bfa_trc(rport->fcs, event); + + switch (event) { + case RPSM_EVENT_ACCEPTED: + bfa_sm_set_state(rport, bfa_fcs_rport_sm_adisc_sending); + bfa_fcs_rport_send_adisc(rport, NULL); + break; + + case RPSM_EVENT_FAILED: + rport->ns_retries++; + if (rport->ns_retries < BFA_FCS_RPORT_MAX_RETRIES) { + bfa_sm_set_state(rport, + bfa_fcs_rport_sm_nsquery_sending); + bfa_fcs_rport_send_gidpn(rport, NULL); + } else { + bfa_sm_set_state(rport, bfa_fcs_rport_sm_fc4_offline); + bfa_fcs_rport_offline_action(rport); + } + break; + + case RPSM_EVENT_DELETE: + bfa_sm_set_state(rport, bfa_fcs_rport_sm_fc4_logosend); + bfa_fcxp_discard(rport->fcxp); + bfa_fcs_rport_offline_action(rport); + break; + + case RPSM_EVENT_SCN: + break; + + case RPSM_EVENT_LOGO_RCVD: + bfa_sm_set_state(rport, bfa_fcs_rport_sm_fc4_logorcv); + bfa_fcxp_discard(rport->fcxp); + bfa_fcs_rport_offline_action(rport); + break; + + case RPSM_EVENT_PLOGI_COMP: + case RPSM_EVENT_ADDRESS_CHANGE: + case RPSM_EVENT_PLOGI_RCVD: + case RPSM_EVENT_LOGO_IMP: + bfa_sm_set_state(rport, bfa_fcs_rport_sm_fc4_offline); + bfa_fcxp_discard(rport->fcxp); + bfa_fcs_rport_offline_action(rport); + break; + + default: + bfa_assert(0); + } +} + +/** + * An SCN event is received in ONLINE state. ADISC is being sent for + * authenticating with rport. FC-4s are paused. + */ +static void +bfa_fcs_rport_sm_adisc_sending(struct bfa_fcs_rport_s *rport, + enum rport_event event) +{ + bfa_trc(rport->fcs, rport->pwwn); + bfa_trc(rport->fcs, rport->pid); + bfa_trc(rport->fcs, event); + + switch (event) { + case RPSM_EVENT_FCXP_SENT: + bfa_sm_set_state(rport, bfa_fcs_rport_sm_adisc); + break; + + case RPSM_EVENT_DELETE: + bfa_sm_set_state(rport, bfa_fcs_rport_sm_fc4_logosend); + bfa_fcxp_walloc_cancel(rport->fcs->bfa, &rport->fcxp_wqe); + bfa_fcs_rport_offline_action(rport); + break; + + case RPSM_EVENT_LOGO_IMP: + case RPSM_EVENT_ADDRESS_CHANGE: + bfa_sm_set_state(rport, bfa_fcs_rport_sm_fc4_offline); + bfa_fcxp_walloc_cancel(rport->fcs->bfa, &rport->fcxp_wqe); + bfa_fcs_rport_offline_action(rport); + break; + + case RPSM_EVENT_LOGO_RCVD: + bfa_sm_set_state(rport, bfa_fcs_rport_sm_fc4_logorcv); + bfa_fcxp_walloc_cancel(rport->fcs->bfa, &rport->fcxp_wqe); + bfa_fcs_rport_offline_action(rport); + break; + + case RPSM_EVENT_SCN: + break; + + case RPSM_EVENT_PLOGI_RCVD: + bfa_sm_set_state(rport, bfa_fcs_rport_sm_fc4_offline); + bfa_fcxp_walloc_cancel(rport->fcs->bfa, &rport->fcxp_wqe); + bfa_fcs_rport_offline_action(rport); + break; + + default: + bfa_assert(0); + } +} + +/** + * An SCN event is received in ONLINE state. ADISC is to rport. + * FC-4s are paused. + */ +static void +bfa_fcs_rport_sm_adisc(struct bfa_fcs_rport_s *rport, enum rport_event event) +{ + bfa_trc(rport->fcs, rport->pwwn); + bfa_trc(rport->fcs, rport->pid); + bfa_trc(rport->fcs, event); + + switch (event) { + case RPSM_EVENT_ACCEPTED: + bfa_sm_set_state(rport, bfa_fcs_rport_sm_online); + bfa_fcs_rport_fc4_resume(rport); + break; + + case RPSM_EVENT_PLOGI_RCVD: + /** + * Too complex to cleanup FC-4 & rport and then acc to PLOGI. + * At least go offline when a PLOGI is received. + */ + bfa_fcxp_discard(rport->fcxp); + /* + * !!! fall through !!! + */ + + case RPSM_EVENT_FAILED: + case RPSM_EVENT_ADDRESS_CHANGE: + bfa_sm_set_state(rport, bfa_fcs_rport_sm_fc4_offline); + bfa_fcs_rport_offline_action(rport); + break; + + case RPSM_EVENT_DELETE: + bfa_sm_set_state(rport, bfa_fcs_rport_sm_fc4_logosend); + bfa_fcxp_discard(rport->fcxp); + bfa_fcs_rport_offline_action(rport); + break; + + case RPSM_EVENT_SCN: + /** + * already processing RSCN + */ + break; + + case RPSM_EVENT_LOGO_IMP: + bfa_sm_set_state(rport, bfa_fcs_rport_sm_fc4_offline); + bfa_fcxp_discard(rport->fcxp); + bfa_fcs_rport_offline_action(rport); + break; + + case RPSM_EVENT_LOGO_RCVD: + bfa_sm_set_state(rport, bfa_fcs_rport_sm_fc4_logorcv); + bfa_fcxp_discard(rport->fcxp); + bfa_fcs_rport_offline_action(rport); + break; + + default: + bfa_assert(0); + } +} + +/** + * Rport has sent LOGO. Awaiting FC-4 offline completion callback. + */ +static void +bfa_fcs_rport_sm_fc4_logorcv(struct bfa_fcs_rport_s *rport, + enum rport_event event) +{ + bfa_trc(rport->fcs, rport->pwwn); + bfa_trc(rport->fcs, rport->pid); + bfa_trc(rport->fcs, event); + + switch (event) { + case RPSM_EVENT_FC4_OFFLINE: + bfa_sm_set_state(rport, bfa_fcs_rport_sm_hcb_logorcv); + bfa_rport_offline(rport->bfa_rport); + break; + + case RPSM_EVENT_DELETE: + bfa_sm_set_state(rport, bfa_fcs_rport_sm_fc4_logosend); + break; + + case RPSM_EVENT_LOGO_RCVD: + case RPSM_EVENT_ADDRESS_CHANGE: + break; + + default: + bfa_assert(0); + } +} + +/** + * LOGO needs to be sent to rport. Awaiting FC-4 offline completion + * callback. + */ +static void +bfa_fcs_rport_sm_fc4_logosend(struct bfa_fcs_rport_s *rport, + enum rport_event event) +{ + bfa_trc(rport->fcs, rport->pwwn); + bfa_trc(rport->fcs, rport->pid); + bfa_trc(rport->fcs, event); + + switch (event) { + case RPSM_EVENT_FC4_OFFLINE: + bfa_sm_set_state(rport, bfa_fcs_rport_sm_hcb_logosend); + bfa_rport_offline(rport->bfa_rport); + break; + + default: + bfa_assert(0); + } +} + +/** + * Rport is going offline. Awaiting FC-4 offline completion callback. + */ +static void +bfa_fcs_rport_sm_fc4_offline(struct bfa_fcs_rport_s *rport, + enum rport_event event) +{ + bfa_trc(rport->fcs, rport->pwwn); + bfa_trc(rport->fcs, rport->pid); + bfa_trc(rport->fcs, event); + + switch (event) { + case RPSM_EVENT_FC4_OFFLINE: + bfa_sm_set_state(rport, bfa_fcs_rport_sm_hcb_offline); + bfa_rport_offline(rport->bfa_rport); + break; + + case RPSM_EVENT_SCN: + case RPSM_EVENT_LOGO_IMP: + case RPSM_EVENT_LOGO_RCVD: + case RPSM_EVENT_ADDRESS_CHANGE: + /** + * rport is already going offline. + * SCN - ignore and wait till transitioning to offline state + */ + break; + + case RPSM_EVENT_DELETE: + bfa_sm_set_state(rport, bfa_fcs_rport_sm_fc4_logosend); + break; + + default: + bfa_assert(0); + } +} + +/** + * Rport is offline. FC-4s are offline. Awaiting BFA rport offline + * callback. + */ +static void +bfa_fcs_rport_sm_hcb_offline(struct bfa_fcs_rport_s *rport, + enum rport_event event) +{ + bfa_trc(rport->fcs, rport->pwwn); + bfa_trc(rport->fcs, rport->pid); + bfa_trc(rport->fcs, event); + + switch (event) { + case RPSM_EVENT_HCB_OFFLINE: + case RPSM_EVENT_ADDRESS_CHANGE: + if (bfa_fcs_port_is_online(rport->port)) { + bfa_sm_set_state(rport, + bfa_fcs_rport_sm_nsdisc_sending); + rport->ns_retries = 0; + bfa_fcs_rport_send_gidpn(rport, NULL); + } else { + rport->pid = 0; + bfa_sm_set_state(rport, bfa_fcs_rport_sm_offline); + bfa_timer_start(rport->fcs->bfa, &rport->timer, + bfa_fcs_rport_timeout, rport, + bfa_fcs_rport_del_timeout); + } + break; + + case RPSM_EVENT_DELETE: + bfa_sm_set_state(rport, bfa_fcs_rport_sm_uninit); + bfa_fcs_rport_free(rport); + break; + + case RPSM_EVENT_SCN: + case RPSM_EVENT_LOGO_RCVD: + /** + * Ignore, already offline. + */ + break; + + default: + bfa_assert(0); + } +} + +/** + * Rport is offline. FC-4s are offline. Awaiting BFA rport offline + * callback to send LOGO accept. + */ +static void +bfa_fcs_rport_sm_hcb_logorcv(struct bfa_fcs_rport_s *rport, + enum rport_event event) +{ + bfa_trc(rport->fcs, rport->pwwn); + bfa_trc(rport->fcs, rport->pid); + bfa_trc(rport->fcs, event); + + switch (event) { + case RPSM_EVENT_HCB_OFFLINE: + case RPSM_EVENT_ADDRESS_CHANGE: + if (rport->pid) + bfa_fcs_rport_send_logo_acc(rport); + /* + * If the lport is online and if the rport is not a well known + * address port, we try to re-discover the r-port. + */ + if (bfa_fcs_port_is_online(rport->port) + && (!BFA_FCS_PID_IS_WKA(rport->pid))) { + bfa_sm_set_state(rport, + bfa_fcs_rport_sm_nsdisc_sending); + rport->ns_retries = 0; + bfa_fcs_rport_send_gidpn(rport, NULL); + } else { + /* + * if it is not a well known address, reset the pid to + * + */ + if (!BFA_FCS_PID_IS_WKA(rport->pid)) + rport->pid = 0; + bfa_sm_set_state(rport, bfa_fcs_rport_sm_offline); + bfa_timer_start(rport->fcs->bfa, &rport->timer, + bfa_fcs_rport_timeout, rport, + bfa_fcs_rport_del_timeout); + } + break; + + case RPSM_EVENT_DELETE: + bfa_sm_set_state(rport, bfa_fcs_rport_sm_hcb_logosend); + break; + + case RPSM_EVENT_LOGO_IMP: + bfa_sm_set_state(rport, bfa_fcs_rport_sm_hcb_offline); + break; + + case RPSM_EVENT_LOGO_RCVD: + /** + * Ignore - already processing a LOGO. + */ + break; + + default: + bfa_assert(0); + } +} + +/** + * Rport is being deleted. FC-4s are offline. Awaiting BFA rport offline + * callback to send LOGO. + */ +static void +bfa_fcs_rport_sm_hcb_logosend(struct bfa_fcs_rport_s *rport, + enum rport_event event) +{ + bfa_trc(rport->fcs, rport->pwwn); + bfa_trc(rport->fcs, rport->pid); + bfa_trc(rport->fcs, event); + + switch (event) { + case RPSM_EVENT_HCB_OFFLINE: + bfa_sm_set_state(rport, bfa_fcs_rport_sm_logo_sending); + bfa_fcs_rport_send_logo(rport, NULL); + break; + + case RPSM_EVENT_LOGO_RCVD: + case RPSM_EVENT_ADDRESS_CHANGE: + break; + + default: + bfa_assert(0); + } +} + +/** + * Rport is being deleted. FC-4s are offline. LOGO is being sent. + */ +static void +bfa_fcs_rport_sm_logo_sending(struct bfa_fcs_rport_s *rport, + enum rport_event event) +{ + bfa_trc(rport->fcs, rport->pwwn); + bfa_trc(rport->fcs, rport->pid); + bfa_trc(rport->fcs, event); + + switch (event) { + case RPSM_EVENT_FCXP_SENT: + /* + * Once LOGO is sent, we donot wait for the response + */ + bfa_sm_set_state(rport, bfa_fcs_rport_sm_uninit); + bfa_fcs_rport_free(rport); + break; + + case RPSM_EVENT_SCN: + case RPSM_EVENT_ADDRESS_CHANGE: + break; + + case RPSM_EVENT_LOGO_RCVD: + bfa_sm_set_state(rport, bfa_fcs_rport_sm_uninit); + bfa_fcxp_walloc_cancel(rport->fcs->bfa, &rport->fcxp_wqe); + bfa_fcs_rport_free(rport); + break; + + default: + bfa_assert(0); + } +} + +/** + * Rport is offline. FC-4s are offline. BFA rport is offline. + * Timer active to delete stale rport. + */ +static void +bfa_fcs_rport_sm_offline(struct bfa_fcs_rport_s *rport, enum rport_event event) +{ + bfa_trc(rport->fcs, rport->pwwn); + bfa_trc(rport->fcs, rport->pid); + bfa_trc(rport->fcs, event); + + switch (event) { + case RPSM_EVENT_TIMEOUT: + bfa_sm_set_state(rport, bfa_fcs_rport_sm_uninit); + bfa_fcs_rport_free(rport); + break; + + case RPSM_EVENT_SCN: + case RPSM_EVENT_ADDRESS_CHANGE: + bfa_sm_set_state(rport, bfa_fcs_rport_sm_nsdisc_sending); + bfa_timer_stop(&rport->timer); + rport->ns_retries = 0; + bfa_fcs_rport_send_gidpn(rport, NULL); + break; + + case RPSM_EVENT_DELETE: + bfa_sm_set_state(rport, bfa_fcs_rport_sm_uninit); + bfa_timer_stop(&rport->timer); + bfa_fcs_rport_free(rport); + break; + + case RPSM_EVENT_PLOGI_RCVD: + bfa_sm_set_state(rport, bfa_fcs_rport_sm_plogiacc_sending); + bfa_timer_stop(&rport->timer); + bfa_fcs_rport_send_plogiacc(rport, NULL); + break; + + case RPSM_EVENT_LOGO_RCVD: + case RPSM_EVENT_LOGO_IMP: + break; + + case RPSM_EVENT_PLOGI_COMP: + bfa_sm_set_state(rport, bfa_fcs_rport_sm_hal_online); + bfa_timer_stop(&rport->timer); + bfa_fcs_rport_hal_online(rport); + break; + + case RPSM_EVENT_PLOGI_SEND: + bfa_timer_stop(&rport->timer); + bfa_sm_set_state(rport, bfa_fcs_rport_sm_plogi_sending); + rport->plogi_retries = 0; + bfa_fcs_rport_send_plogi(rport, NULL); + break; + + default: + bfa_assert(0); + } +} + +/** + * Rport address has changed. Nameserver discovery request is being sent. + */ +static void +bfa_fcs_rport_sm_nsdisc_sending(struct bfa_fcs_rport_s *rport, + enum rport_event event) +{ + bfa_trc(rport->fcs, rport->pwwn); + bfa_trc(rport->fcs, rport->pid); + bfa_trc(rport->fcs, event); + + switch (event) { + case RPSM_EVENT_FCXP_SENT: + bfa_sm_set_state(rport, bfa_fcs_rport_sm_nsdisc_sent); + break; + + case RPSM_EVENT_DELETE: + bfa_sm_set_state(rport, bfa_fcs_rport_sm_uninit); + bfa_fcxp_walloc_cancel(rport->fcs->bfa, &rport->fcxp_wqe); + bfa_fcs_rport_free(rport); + break; + + case RPSM_EVENT_PLOGI_RCVD: + bfa_sm_set_state(rport, bfa_fcs_rport_sm_plogiacc_sending); + bfa_fcxp_walloc_cancel(rport->fcs->bfa, &rport->fcxp_wqe); + bfa_fcs_rport_send_plogiacc(rport, NULL); + break; + + case RPSM_EVENT_SCN: + case RPSM_EVENT_LOGO_RCVD: + case RPSM_EVENT_PLOGI_SEND: + break; + + case RPSM_EVENT_ADDRESS_CHANGE: + rport->ns_retries = 0; /* reset the retry count */ + break; + + case RPSM_EVENT_LOGO_IMP: + bfa_sm_set_state(rport, bfa_fcs_rport_sm_offline); + bfa_fcxp_walloc_cancel(rport->fcs->bfa, &rport->fcxp_wqe); + bfa_timer_start(rport->fcs->bfa, &rport->timer, + bfa_fcs_rport_timeout, rport, + bfa_fcs_rport_del_timeout); + break; + + case RPSM_EVENT_PLOGI_COMP: + bfa_sm_set_state(rport, bfa_fcs_rport_sm_hal_online); + bfa_fcxp_walloc_cancel(rport->fcs->bfa, &rport->fcxp_wqe); + bfa_fcs_rport_hal_online(rport); + break; + + default: + bfa_assert(0); + } +} + +/** + * Nameserver discovery failed. Waiting for timeout to retry. + */ +static void +bfa_fcs_rport_sm_nsdisc_retry(struct bfa_fcs_rport_s *rport, + enum rport_event event) +{ + bfa_trc(rport->fcs, rport->pwwn); + bfa_trc(rport->fcs, rport->pid); + bfa_trc(rport->fcs, event); + + switch (event) { + case RPSM_EVENT_TIMEOUT: + bfa_sm_set_state(rport, bfa_fcs_rport_sm_nsdisc_sending); + bfa_fcs_rport_send_gidpn(rport, NULL); + break; + + case RPSM_EVENT_SCN: + case RPSM_EVENT_ADDRESS_CHANGE: + bfa_sm_set_state(rport, bfa_fcs_rport_sm_nsdisc_sending); + bfa_timer_stop(&rport->timer); + rport->ns_retries = 0; + bfa_fcs_rport_send_gidpn(rport, NULL); + break; + + case RPSM_EVENT_DELETE: + bfa_sm_set_state(rport, bfa_fcs_rport_sm_uninit); + bfa_timer_stop(&rport->timer); + bfa_fcs_rport_free(rport); + break; + + case RPSM_EVENT_PLOGI_RCVD: + bfa_sm_set_state(rport, bfa_fcs_rport_sm_plogiacc_sending); + bfa_timer_stop(&rport->timer); + bfa_fcs_rport_send_plogiacc(rport, NULL); + break; + + case RPSM_EVENT_LOGO_IMP: + rport->pid = 0; + bfa_sm_set_state(rport, bfa_fcs_rport_sm_offline); + bfa_timer_stop(&rport->timer); + bfa_timer_start(rport->fcs->bfa, &rport->timer, + bfa_fcs_rport_timeout, rport, + bfa_fcs_rport_del_timeout); + break; + + case RPSM_EVENT_LOGO_RCVD: + bfa_fcs_rport_send_logo_acc(rport); + break; + + case RPSM_EVENT_PLOGI_COMP: + bfa_sm_set_state(rport, bfa_fcs_rport_sm_hal_online); + bfa_timer_stop(&rport->timer); + bfa_fcs_rport_hal_online(rport); + break; + + default: + bfa_assert(0); + } +} + +/** + * Rport address has changed. Nameserver discovery request is sent. + */ +static void +bfa_fcs_rport_sm_nsdisc_sent(struct bfa_fcs_rport_s *rport, + enum rport_event event) +{ + bfa_trc(rport->fcs, rport->pwwn); + bfa_trc(rport->fcs, rport->pid); + bfa_trc(rport->fcs, event); + + switch (event) { + case RPSM_EVENT_ACCEPTED: + case RPSM_EVENT_ADDRESS_CHANGE: + if (rport->pid) { + bfa_sm_set_state(rport, bfa_fcs_rport_sm_plogi_sending); + bfa_fcs_rport_send_plogi(rport, NULL); + } else { + bfa_sm_set_state(rport, + bfa_fcs_rport_sm_nsdisc_sending); + rport->ns_retries = 0; + bfa_fcs_rport_send_gidpn(rport, NULL); + } + break; + + case RPSM_EVENT_FAILED: + rport->ns_retries++; + if (rport->ns_retries < BFA_FCS_RPORT_MAX_RETRIES) { + bfa_sm_set_state(rport, + bfa_fcs_rport_sm_nsdisc_sending); + bfa_fcs_rport_send_gidpn(rport, NULL); + } else { + rport->pid = 0; + bfa_sm_set_state(rport, bfa_fcs_rport_sm_offline); + bfa_timer_start(rport->fcs->bfa, &rport->timer, + bfa_fcs_rport_timeout, rport, + bfa_fcs_rport_del_timeout); + }; + break; + + case RPSM_EVENT_DELETE: + bfa_sm_set_state(rport, bfa_fcs_rport_sm_uninit); + bfa_fcxp_discard(rport->fcxp); + bfa_fcs_rport_free(rport); + break; + + case RPSM_EVENT_PLOGI_RCVD: + bfa_sm_set_state(rport, bfa_fcs_rport_sm_plogiacc_sending); + bfa_fcxp_discard(rport->fcxp); + bfa_fcs_rport_send_plogiacc(rport, NULL); + break; + + case RPSM_EVENT_LOGO_IMP: + rport->pid = 0; + bfa_sm_set_state(rport, bfa_fcs_rport_sm_offline); + bfa_fcxp_discard(rport->fcxp); + bfa_timer_start(rport->fcs->bfa, &rport->timer, + bfa_fcs_rport_timeout, rport, + bfa_fcs_rport_del_timeout); + break; + + case RPSM_EVENT_SCN: + /** + * ignore, wait for NS query response + */ + break; + + case RPSM_EVENT_LOGO_RCVD: + /** + * Not logged-in yet. Accept LOGO. + */ + bfa_fcs_rport_send_logo_acc(rport); + break; + + case RPSM_EVENT_PLOGI_COMP: + bfa_sm_set_state(rport, bfa_fcs_rport_sm_hal_online); + bfa_fcxp_discard(rport->fcxp); + bfa_fcs_rport_hal_online(rport); + break; + + default: + bfa_assert(0); + } +} + + + +/** + * fcs_rport_private FCS RPORT provate functions + */ + +static void +bfa_fcs_rport_send_plogi(void *rport_cbarg, struct bfa_fcxp_s *fcxp_alloced) +{ + struct bfa_fcs_rport_s *rport = rport_cbarg; + struct bfa_fcs_port_s *port = rport->port; + struct fchs_s fchs; + int len; + struct bfa_fcxp_s *fcxp; + + bfa_trc(rport->fcs, rport->pwwn); + + fcxp = fcxp_alloced ? fcxp_alloced : bfa_fcs_fcxp_alloc(port->fcs); + if (!fcxp) { + bfa_fcxp_alloc_wait(port->fcs->bfa, &rport->fcxp_wqe, + bfa_fcs_rport_send_plogi, rport); + return; + } + rport->fcxp = fcxp; + + len = fc_plogi_build(&fchs, bfa_fcxp_get_reqbuf(fcxp), rport->pid, + bfa_fcs_port_get_fcid(port), 0, + port->port_cfg.pwwn, port->port_cfg.nwwn, + bfa_pport_get_maxfrsize(port->fcs->bfa)); + + bfa_fcxp_send(fcxp, NULL, port->fabric->vf_id, port->lp_tag, BFA_FALSE, + FC_CLASS_3, len, &fchs, bfa_fcs_rport_plogi_response, + (void *)rport, FC_MAX_PDUSZ, FC_RA_TOV); + + rport->stats.plogis++; + bfa_sm_send_event(rport, RPSM_EVENT_FCXP_SENT); +} + +static void +bfa_fcs_rport_plogi_response(void *fcsarg, struct bfa_fcxp_s *fcxp, void *cbarg, + bfa_status_t req_status, u32 rsp_len, + u32 resid_len, struct fchs_s *rsp_fchs) +{ + struct bfa_fcs_rport_s *rport = (struct bfa_fcs_rport_s *)cbarg; + struct fc_logi_s *plogi_rsp; + struct fc_ls_rjt_s *ls_rjt; + struct bfa_fcs_rport_s *twin; + struct list_head *qe; + + bfa_trc(rport->fcs, rport->pwwn); + + /* + * Sanity Checks + */ + if (req_status != BFA_STATUS_OK) { + bfa_trc(rport->fcs, req_status); + rport->stats.plogi_failed++; + bfa_sm_send_event(rport, RPSM_EVENT_FAILED); + return; + } + + plogi_rsp = (struct fc_logi_s *) BFA_FCXP_RSP_PLD(fcxp); + + /** + * Check for failure first. + */ + if (plogi_rsp->els_cmd.els_code != FC_ELS_ACC) { + ls_rjt = (struct fc_ls_rjt_s *) BFA_FCXP_RSP_PLD(fcxp); + + bfa_trc(rport->fcs, ls_rjt->reason_code); + bfa_trc(rport->fcs, ls_rjt->reason_code_expl); + + rport->stats.plogi_rejects++; + bfa_sm_send_event(rport, RPSM_EVENT_FAILED); + return; + } + + /** + * PLOGI is complete. Make sure this device is not one of the known + * device with a new FC port address. + */ + list_for_each(qe, &rport->port->rport_q) { + twin = (struct bfa_fcs_rport_s *)qe; + if (twin == rport) + continue; + if (!rport->pwwn && (plogi_rsp->port_name == twin->pwwn)) { + bfa_trc(rport->fcs, twin->pid); + bfa_trc(rport->fcs, rport->pid); + + /* + * Update plogi stats in twin + */ + twin->stats.plogis += rport->stats.plogis; + twin->stats.plogi_rejects += rport->stats.plogi_rejects; + twin->stats.plogi_timeouts += + rport->stats.plogi_timeouts; + twin->stats.plogi_failed += rport->stats.plogi_failed; + twin->stats.plogi_rcvd += rport->stats.plogi_rcvd; + twin->stats.plogi_accs++; + + bfa_fcs_rport_delete(rport); + + bfa_fcs_rport_update(twin, plogi_rsp); + twin->pid = rsp_fchs->s_id; + bfa_sm_send_event(twin, RPSM_EVENT_PLOGI_COMP); + return; + } + } + + /** + * Normal login path -- no evil twins. + */ + rport->stats.plogi_accs++; + bfa_fcs_rport_update(rport, plogi_rsp); + bfa_sm_send_event(rport, RPSM_EVENT_ACCEPTED); +} + +static void +bfa_fcs_rport_send_plogiacc(void *rport_cbarg, struct bfa_fcxp_s *fcxp_alloced) +{ + struct bfa_fcs_rport_s *rport = rport_cbarg; + struct bfa_fcs_port_s *port = rport->port; + struct fchs_s fchs; + int len; + struct bfa_fcxp_s *fcxp; + + bfa_trc(rport->fcs, rport->pwwn); + bfa_trc(rport->fcs, rport->reply_oxid); + + fcxp = fcxp_alloced ? fcxp_alloced : bfa_fcs_fcxp_alloc(port->fcs); + if (!fcxp) { + bfa_fcxp_alloc_wait(port->fcs->bfa, &rport->fcxp_wqe, + bfa_fcs_rport_send_plogiacc, rport); + return; + } + rport->fcxp = fcxp; + + len = fc_plogi_acc_build(&fchs, bfa_fcxp_get_reqbuf(fcxp), rport->pid, + bfa_fcs_port_get_fcid(port), rport->reply_oxid, + port->port_cfg.pwwn, port->port_cfg.nwwn, + bfa_pport_get_maxfrsize(port->fcs->bfa)); + + bfa_fcxp_send(fcxp, NULL, port->fabric->vf_id, port->lp_tag, BFA_FALSE, + FC_CLASS_3, len, &fchs, NULL, NULL, FC_MAX_PDUSZ, 0); + + bfa_sm_send_event(rport, RPSM_EVENT_FCXP_SENT); +} + +static void +bfa_fcs_rport_send_adisc(void *rport_cbarg, struct bfa_fcxp_s *fcxp_alloced) +{ + struct bfa_fcs_rport_s *rport = rport_cbarg; + struct bfa_fcs_port_s *port = rport->port; + struct fchs_s fchs; + int len; + struct bfa_fcxp_s *fcxp; + + bfa_trc(rport->fcs, rport->pwwn); + + fcxp = fcxp_alloced ? fcxp_alloced : bfa_fcs_fcxp_alloc(port->fcs); + if (!fcxp) { + bfa_fcxp_alloc_wait(port->fcs->bfa, &rport->fcxp_wqe, + bfa_fcs_rport_send_adisc, rport); + return; + } + rport->fcxp = fcxp; + + len = fc_adisc_build(&fchs, bfa_fcxp_get_reqbuf(fcxp), rport->pid, + bfa_fcs_port_get_fcid(port), 0, + port->port_cfg.pwwn, port->port_cfg.nwwn); + + bfa_fcxp_send(fcxp, NULL, port->fabric->vf_id, port->lp_tag, BFA_FALSE, + FC_CLASS_3, len, &fchs, bfa_fcs_rport_adisc_response, + rport, FC_MAX_PDUSZ, FC_RA_TOV); + + rport->stats.adisc_sent++; + bfa_sm_send_event(rport, RPSM_EVENT_FCXP_SENT); +} + +static void +bfa_fcs_rport_adisc_response(void *fcsarg, struct bfa_fcxp_s *fcxp, void *cbarg, + bfa_status_t req_status, u32 rsp_len, + u32 resid_len, struct fchs_s *rsp_fchs) +{ + struct bfa_fcs_rport_s *rport = (struct bfa_fcs_rport_s *)cbarg; + void *pld = bfa_fcxp_get_rspbuf(fcxp); + struct fc_ls_rjt_s *ls_rjt; + + if (req_status != BFA_STATUS_OK) { + bfa_trc(rport->fcs, req_status); + rport->stats.adisc_failed++; + bfa_sm_send_event(rport, RPSM_EVENT_FAILED); + return; + } + + if (fc_adisc_rsp_parse((struct fc_adisc_s *)pld, rsp_len, rport->pwwn, + rport->nwwn) == FC_PARSE_OK) { + rport->stats.adisc_accs++; + bfa_sm_send_event(rport, RPSM_EVENT_ACCEPTED); + return; + } + + rport->stats.adisc_rejects++; + ls_rjt = pld; + bfa_trc(rport->fcs, ls_rjt->els_cmd.els_code); + bfa_trc(rport->fcs, ls_rjt->reason_code); + bfa_trc(rport->fcs, ls_rjt->reason_code_expl); + bfa_sm_send_event(rport, RPSM_EVENT_FAILED); +} + +static void +bfa_fcs_rport_send_gidpn(void *rport_cbarg, struct bfa_fcxp_s *fcxp_alloced) +{ + struct bfa_fcs_rport_s *rport = rport_cbarg; + struct bfa_fcs_port_s *port = rport->port; + struct fchs_s fchs; + struct bfa_fcxp_s *fcxp; + int len; + + bfa_trc(rport->fcs, rport->pid); + + fcxp = fcxp_alloced ? fcxp_alloced : bfa_fcs_fcxp_alloc(port->fcs); + if (!fcxp) { + bfa_fcxp_alloc_wait(port->fcs->bfa, &rport->fcxp_wqe, + bfa_fcs_rport_send_gidpn, rport); + return; + } + rport->fcxp = fcxp; + + len = fc_gidpn_build(&fchs, bfa_fcxp_get_reqbuf(fcxp), + bfa_fcs_port_get_fcid(port), 0, rport->pwwn); + + bfa_fcxp_send(fcxp, NULL, port->fabric->vf_id, port->lp_tag, BFA_FALSE, + FC_CLASS_3, len, &fchs, bfa_fcs_rport_gidpn_response, + (void *)rport, FC_MAX_PDUSZ, FC_RA_TOV); + + bfa_sm_send_event(rport, RPSM_EVENT_FCXP_SENT); +} + +static void +bfa_fcs_rport_gidpn_response(void *fcsarg, struct bfa_fcxp_s *fcxp, void *cbarg, + bfa_status_t req_status, u32 rsp_len, + u32 resid_len, struct fchs_s *rsp_fchs) +{ + struct bfa_fcs_rport_s *rport = (struct bfa_fcs_rport_s *)cbarg; + struct bfa_fcs_rport_s *twin; + struct list_head *qe; + struct ct_hdr_s *cthdr; + struct fcgs_gidpn_resp_s *gidpn_rsp; + + bfa_trc(rport->fcs, rport->pwwn); + + cthdr = (struct ct_hdr_s *) BFA_FCXP_RSP_PLD(fcxp); + cthdr->cmd_rsp_code = bfa_os_ntohs(cthdr->cmd_rsp_code); + + if (cthdr->cmd_rsp_code == CT_RSP_ACCEPT) { + /* + * Check if the pid is the same as before. + */ + gidpn_rsp = (struct fcgs_gidpn_resp_s *) (cthdr + 1); + + if (gidpn_rsp->dap == rport->pid) { + /* + * Device is online + */ + bfa_sm_send_event(rport, RPSM_EVENT_ACCEPTED); + } else { + /* + * Device's PID has changed. We need to cleanup and + * re-login. If there is another device with the the + * newly discovered pid, send an scn notice so that its + * new pid can be discovered. + */ + list_for_each(qe, &rport->port->rport_q) { + twin = (struct bfa_fcs_rport_s *)qe; + if (twin == rport) + continue; + if (gidpn_rsp->dap == twin->pid) { + bfa_trc(rport->fcs, twin->pid); + bfa_trc(rport->fcs, rport->pid); + + twin->pid = 0; + bfa_sm_send_event(twin, + RPSM_EVENT_ADDRESS_CHANGE); + } + } + rport->pid = gidpn_rsp->dap; + bfa_sm_send_event(rport, RPSM_EVENT_ADDRESS_CHANGE); + } + return; + } + + /* + * Reject Response + */ + switch (cthdr->reason_code) { + case CT_RSN_LOGICAL_BUSY: + /* + * Need to retry + */ + bfa_sm_send_event(rport, RPSM_EVENT_TIMEOUT); + break; + + case CT_RSN_UNABLE_TO_PERF: + /* + * device doesn't exist : Start timer to cleanup this later. + */ + bfa_sm_send_event(rport, RPSM_EVENT_FAILED); + break; + + default: + bfa_sm_send_event(rport, RPSM_EVENT_FAILED); + break; + } +} + +/** + * Called to send a logout to the rport. + */ +static void +bfa_fcs_rport_send_logo(void *rport_cbarg, struct bfa_fcxp_s *fcxp_alloced) +{ + struct bfa_fcs_rport_s *rport = rport_cbarg; + struct bfa_fcs_port_s *port; + struct fchs_s fchs; + struct bfa_fcxp_s *fcxp; + u16 len; + + bfa_trc(rport->fcs, rport->pid); + + port = rport->port; + + fcxp = fcxp_alloced ? fcxp_alloced : bfa_fcs_fcxp_alloc(port->fcs); + if (!fcxp) { + bfa_fcxp_alloc_wait(port->fcs->bfa, &rport->fcxp_wqe, + bfa_fcs_rport_send_logo, rport); + return; + } + rport->fcxp = fcxp; + + len = fc_logo_build(&fchs, bfa_fcxp_get_reqbuf(fcxp), rport->pid, + bfa_fcs_port_get_fcid(port), 0, + bfa_fcs_port_get_pwwn(port)); + + bfa_fcxp_send(fcxp, NULL, port->fabric->vf_id, port->lp_tag, BFA_FALSE, + FC_CLASS_3, len, &fchs, NULL, rport, FC_MAX_PDUSZ, + FC_ED_TOV); + + rport->stats.logos++; + bfa_fcxp_discard(rport->fcxp); + bfa_sm_send_event(rport, RPSM_EVENT_FCXP_SENT); +} + +/** + * Send ACC for a LOGO received. + */ +static void +bfa_fcs_rport_send_logo_acc(void *rport_cbarg) +{ + struct bfa_fcs_rport_s *rport = rport_cbarg; + struct bfa_fcs_port_s *port; + struct fchs_s fchs; + struct bfa_fcxp_s *fcxp; + u16 len; + + bfa_trc(rport->fcs, rport->pid); + + port = rport->port; + + fcxp = bfa_fcs_fcxp_alloc(port->fcs); + if (!fcxp) + return; + + rport->stats.logo_rcvd++; + len = fc_logo_acc_build(&fchs, bfa_fcxp_get_reqbuf(fcxp), rport->pid, + bfa_fcs_port_get_fcid(port), rport->reply_oxid); + + bfa_fcxp_send(fcxp, NULL, port->fabric->vf_id, port->lp_tag, BFA_FALSE, + FC_CLASS_3, len, &fchs, NULL, NULL, FC_MAX_PDUSZ, 0); +} + +/** + * This routine will be called by bfa_timer on timer timeouts. + * + * param[in] rport - pointer to bfa_fcs_port_ns_t. + * param[out] rport_status - pointer to return vport status in + * + * return + * void + * +* Special Considerations: + * + * note + */ +static void +bfa_fcs_rport_timeout(void *arg) +{ + struct bfa_fcs_rport_s *rport = (struct bfa_fcs_rport_s *)arg; + + rport->stats.plogi_timeouts++; + bfa_sm_send_event(rport, RPSM_EVENT_TIMEOUT); +} + +static void +bfa_fcs_rport_process_prli(struct bfa_fcs_rport_s *rport, + struct fchs_s *rx_fchs, u16 len) +{ + struct bfa_fcxp_s *fcxp; + struct fchs_s fchs; + struct bfa_fcs_port_s *port = rport->port; + struct fc_prli_s *prli; + + bfa_trc(port->fcs, rx_fchs->s_id); + bfa_trc(port->fcs, rx_fchs->d_id); + + rport->stats.prli_rcvd++; + + if (BFA_FCS_VPORT_IS_TARGET_MODE(port)) { + /* + * Target Mode : Let the fcptm handle it + */ + bfa_fcs_tin_rx_prli(rport->tin, rx_fchs, len); + return; + } + + /* + * We are either in Initiator or ipfc Mode + */ + prli = (struct fc_prli_s *) (rx_fchs + 1); + + if (prli->parampage.servparams.initiator) { + bfa_trc(rport->fcs, prli->parampage.type); + rport->scsi_function = BFA_RPORT_INITIATOR; + bfa_fcs_itnim_is_initiator(rport->itnim); + } else { + /* + * @todo: PRLI from a target ? + */ + bfa_trc(port->fcs, rx_fchs->s_id); + rport->scsi_function = BFA_RPORT_TARGET; + } + + fcxp = bfa_fcs_fcxp_alloc(port->fcs); + if (!fcxp) + return; + + len = fc_prli_acc_build(&fchs, bfa_fcxp_get_reqbuf(fcxp), rx_fchs->s_id, + bfa_fcs_port_get_fcid(port), rx_fchs->ox_id, + port->port_cfg.roles); + + bfa_fcxp_send(fcxp, NULL, port->fabric->vf_id, port->lp_tag, BFA_FALSE, + FC_CLASS_3, len, &fchs, NULL, NULL, FC_MAX_PDUSZ, 0); +} + +static void +bfa_fcs_rport_process_rpsc(struct bfa_fcs_rport_s *rport, + struct fchs_s *rx_fchs, u16 len) +{ + struct bfa_fcxp_s *fcxp; + struct fchs_s fchs; + struct bfa_fcs_port_s *port = rport->port; + struct fc_rpsc_speed_info_s speeds; + struct bfa_pport_attr_s pport_attr; + + bfa_trc(port->fcs, rx_fchs->s_id); + bfa_trc(port->fcs, rx_fchs->d_id); + + rport->stats.rpsc_rcvd++; + speeds.port_speed_cap = + RPSC_SPEED_CAP_1G | RPSC_SPEED_CAP_2G | RPSC_SPEED_CAP_4G | + RPSC_SPEED_CAP_8G; + + /* + * get curent speed from pport attributes from BFA + */ + bfa_pport_get_attr(port->fcs->bfa, &pport_attr); + + speeds.port_op_speed = fc_bfa_speed_to_rpsc_operspeed(pport_attr.speed); + + fcxp = bfa_fcs_fcxp_alloc(port->fcs); + if (!fcxp) + return; + + len = fc_rpsc_acc_build(&fchs, bfa_fcxp_get_reqbuf(fcxp), rx_fchs->s_id, + bfa_fcs_port_get_fcid(port), rx_fchs->ox_id, + &speeds); + + bfa_fcxp_send(fcxp, NULL, port->fabric->vf_id, port->lp_tag, BFA_FALSE, + FC_CLASS_3, len, &fchs, NULL, NULL, FC_MAX_PDUSZ, 0); +} + +static void +bfa_fcs_rport_process_adisc(struct bfa_fcs_rport_s *rport, + struct fchs_s *rx_fchs, u16 len) +{ + struct bfa_fcxp_s *fcxp; + struct fchs_s fchs; + struct bfa_fcs_port_s *port = rport->port; + struct fc_adisc_s *adisc; + + bfa_trc(port->fcs, rx_fchs->s_id); + bfa_trc(port->fcs, rx_fchs->d_id); + + rport->stats.adisc_rcvd++; + + if (BFA_FCS_VPORT_IS_TARGET_MODE(port)) { + /* + * @todo : Target Mode handling + */ + bfa_trc(port->fcs, rx_fchs->d_id); + bfa_assert(0); + return; + } + + adisc = (struct fc_adisc_s *) (rx_fchs + 1); + + /* + * Accept if the itnim for this rport is online. Else reject the ADISC + */ + if (bfa_fcs_itnim_get_online_state(rport->itnim) == BFA_STATUS_OK) { + + fcxp = bfa_fcs_fcxp_alloc(port->fcs); + if (!fcxp) + return; + + len = fc_adisc_acc_build(&fchs, bfa_fcxp_get_reqbuf(fcxp), + rx_fchs->s_id, + bfa_fcs_port_get_fcid(port), + rx_fchs->ox_id, port->port_cfg.pwwn, + port->port_cfg.nwwn); + + bfa_fcxp_send(fcxp, NULL, port->fabric->vf_id, port->lp_tag, + BFA_FALSE, FC_CLASS_3, len, &fchs, NULL, NULL, + FC_MAX_PDUSZ, 0); + } else { + rport->stats.adisc_rejected++; + bfa_fcs_rport_send_ls_rjt(rport, rx_fchs, + FC_LS_RJT_RSN_UNABLE_TO_PERF_CMD, + FC_LS_RJT_EXP_LOGIN_REQUIRED); + } + +} + +static void +bfa_fcs_rport_hal_online(struct bfa_fcs_rport_s *rport) +{ + struct bfa_fcs_port_s *port = rport->port; + struct bfa_rport_info_s rport_info; + + rport_info.pid = rport->pid; + rport_info.local_pid = port->pid; + rport_info.lp_tag = port->lp_tag; + rport_info.vf_id = port->fabric->vf_id; + rport_info.vf_en = port->fabric->is_vf; + rport_info.fc_class = rport->fc_cos; + rport_info.cisc = rport->cisc; + rport_info.max_frmsz = rport->maxfrsize; + bfa_rport_online(rport->bfa_rport, &rport_info); +} + +static void +bfa_fcs_rport_fc4_pause(struct bfa_fcs_rport_s *rport) +{ + if (bfa_fcs_port_is_initiator(rport->port)) + bfa_fcs_itnim_pause(rport->itnim); + + if (bfa_fcs_port_is_target(rport->port)) + bfa_fcs_tin_pause(rport->tin); +} + +static void +bfa_fcs_rport_fc4_resume(struct bfa_fcs_rport_s *rport) +{ + if (bfa_fcs_port_is_initiator(rport->port)) + bfa_fcs_itnim_resume(rport->itnim); + + if (bfa_fcs_port_is_target(rport->port)) + bfa_fcs_tin_resume(rport->tin); +} + +static struct bfa_fcs_rport_s * +bfa_fcs_rport_alloc(struct bfa_fcs_port_s *port, wwn_t pwwn, u32 rpid) +{ + struct bfa_fcs_s *fcs = port->fcs; + struct bfa_fcs_rport_s *rport; + struct bfad_rport_s *rport_drv; + + /** + * allocate rport + */ + if (bfa_fcb_rport_alloc(fcs->bfad, &rport, &rport_drv) + != BFA_STATUS_OK) { + bfa_trc(fcs, rpid); + return NULL; + } + + /* + * Initialize r-port + */ + rport->port = port; + rport->fcs = fcs; + rport->rp_drv = rport_drv; + rport->pid = rpid; + rport->pwwn = pwwn; + + /** + * allocate BFA rport + */ + rport->bfa_rport = bfa_rport_create(port->fcs->bfa, rport); + if (!rport->bfa_rport) { + bfa_trc(fcs, rpid); + kfree(rport_drv); + return NULL; + } + + /** + * allocate FC-4s + */ + bfa_assert(bfa_fcs_port_is_initiator(port) ^ + bfa_fcs_port_is_target(port)); + + if (bfa_fcs_port_is_initiator(port)) { + rport->itnim = bfa_fcs_itnim_create(rport); + if (!rport->itnim) { + bfa_trc(fcs, rpid); + bfa_rport_delete(rport->bfa_rport); + kfree(rport_drv); + return NULL; + } + } + + if (bfa_fcs_port_is_target(port)) { + rport->tin = bfa_fcs_tin_create(rport); + if (!rport->tin) { + bfa_trc(fcs, rpid); + bfa_rport_delete(rport->bfa_rport); + kfree(rport_drv); + return NULL; + } + } + + bfa_fcs_port_add_rport(port, rport); + + bfa_sm_set_state(rport, bfa_fcs_rport_sm_uninit); + + /* + * Initialize the Rport Features(RPF) Sub Module + */ + if (!BFA_FCS_PID_IS_WKA(rport->pid)) + bfa_fcs_rpf_init(rport); + + return rport; +} + + +static void +bfa_fcs_rport_free(struct bfa_fcs_rport_s *rport) +{ + struct bfa_fcs_port_s *port = rport->port; + + /** + * - delete FC-4s + * - delete BFA rport + * - remove from queue of rports + */ + if (bfa_fcs_port_is_initiator(port)) + bfa_fcs_itnim_delete(rport->itnim); + + if (bfa_fcs_port_is_target(port)) + bfa_fcs_tin_delete(rport->tin); + + bfa_rport_delete(rport->bfa_rport); + bfa_fcs_port_del_rport(port, rport); + kfree(rport->rp_drv); +} + +static void +bfa_fcs_rport_aen_post(struct bfa_fcs_rport_s *rport, + enum bfa_rport_aen_event event, + struct bfa_rport_aen_data_s *data) +{ + union bfa_aen_data_u aen_data; + struct bfa_log_mod_s *logmod = rport->fcs->logm; + wwn_t lpwwn = bfa_fcs_port_get_pwwn(rport->port); + wwn_t rpwwn = rport->pwwn; + char lpwwn_ptr[BFA_STRING_32]; + char rpwwn_ptr[BFA_STRING_32]; + char *prio_str[] = { "unknown", "high", "medium", "low" }; + + wwn2str(lpwwn_ptr, lpwwn); + wwn2str(rpwwn_ptr, rpwwn); + + switch (event) { + case BFA_RPORT_AEN_ONLINE: + bfa_log(logmod, BFA_AEN_RPORT_ONLINE, rpwwn_ptr, lpwwn_ptr); + break; + case BFA_RPORT_AEN_OFFLINE: + bfa_log(logmod, BFA_AEN_RPORT_OFFLINE, rpwwn_ptr, lpwwn_ptr); + break; + case BFA_RPORT_AEN_DISCONNECT: + bfa_log(logmod, BFA_AEN_RPORT_DISCONNECT, rpwwn_ptr, lpwwn_ptr); + break; + case BFA_RPORT_AEN_QOS_PRIO: + aen_data.rport.priv.qos = data->priv.qos; + bfa_log(logmod, BFA_AEN_RPORT_QOS_PRIO, + prio_str[aen_data.rport.priv.qos.qos_priority], + rpwwn_ptr, lpwwn_ptr); + break; + case BFA_RPORT_AEN_QOS_FLOWID: + aen_data.rport.priv.qos = data->priv.qos; + bfa_log(logmod, BFA_AEN_RPORT_QOS_FLOWID, + aen_data.rport.priv.qos.qos_flow_id, rpwwn_ptr, + lpwwn_ptr); + break; + default: + break; + } + + aen_data.rport.vf_id = rport->port->fabric->vf_id; + aen_data.rport.ppwwn = + bfa_fcs_port_get_pwwn(bfa_fcs_get_base_port(rport->fcs)); + aen_data.rport.lpwwn = lpwwn; + aen_data.rport.rpwwn = rpwwn; +} + +static void +bfa_fcs_rport_online_action(struct bfa_fcs_rport_s *rport) +{ + struct bfa_fcs_port_s *port = rport->port; + + rport->stats.onlines++; + + if (bfa_fcs_port_is_initiator(port)) { + bfa_fcs_itnim_rport_online(rport->itnim); + if (!BFA_FCS_PID_IS_WKA(rport->pid)) + bfa_fcs_rpf_rport_online(rport); + }; + + if (bfa_fcs_port_is_target(port)) + bfa_fcs_tin_rport_online(rport->tin); + + /* + * Don't post events for well known addresses + */ + if (!BFA_FCS_PID_IS_WKA(rport->pid)) + bfa_fcs_rport_aen_post(rport, BFA_RPORT_AEN_ONLINE, NULL); +} + +static void +bfa_fcs_rport_offline_action(struct bfa_fcs_rport_s *rport) +{ + struct bfa_fcs_port_s *port = rport->port; + + rport->stats.offlines++; + + /* + * Don't post events for well known addresses + */ + if (!BFA_FCS_PID_IS_WKA(rport->pid)) { + if (bfa_fcs_port_is_online(rport->port) == BFA_TRUE) { + bfa_fcs_rport_aen_post(rport, BFA_RPORT_AEN_DISCONNECT, + NULL); + } else { + bfa_fcs_rport_aen_post(rport, BFA_RPORT_AEN_OFFLINE, + NULL); + } + } + + if (bfa_fcs_port_is_initiator(port)) { + bfa_fcs_itnim_rport_offline(rport->itnim); + if (!BFA_FCS_PID_IS_WKA(rport->pid)) + bfa_fcs_rpf_rport_offline(rport); + } + + if (bfa_fcs_port_is_target(port)) + bfa_fcs_tin_rport_offline(rport->tin); +} + +/** + * Update rport parameters from PLOGI or PLOGI accept. + */ +static void +bfa_fcs_rport_update(struct bfa_fcs_rport_s *rport, struct fc_logi_s *plogi) +{ + struct bfa_fcs_port_s *port = rport->port; + + /** + * - port name + * - node name + */ + rport->pwwn = plogi->port_name; + rport->nwwn = plogi->node_name; + + /** + * - class of service + */ + rport->fc_cos = 0; + if (plogi->class3.class_valid) + rport->fc_cos = FC_CLASS_3; + + if (plogi->class2.class_valid) + rport->fc_cos |= FC_CLASS_2; + + /** + * - CISC + * - MAX receive frame size + */ + rport->cisc = plogi->csp.cisc; + rport->maxfrsize = bfa_os_ntohs(plogi->class3.rxsz); + + bfa_trc(port->fcs, bfa_os_ntohs(plogi->csp.bbcred)); + bfa_trc(port->fcs, port->fabric->bb_credit); + /** + * Direct Attach P2P mode : + * This is to handle a bug (233476) in IBM targets in Direct Attach + * Mode. Basically, in FLOGI Accept the target would have erroneously + * set the BB Credit to the value used in the FLOGI sent by the HBA. + * It uses the correct value (its own BB credit) in PLOGI. + */ + if ((!bfa_fcs_fabric_is_switched(port->fabric)) + && (bfa_os_ntohs(plogi->csp.bbcred) < port->fabric->bb_credit)) { + + bfa_trc(port->fcs, bfa_os_ntohs(plogi->csp.bbcred)); + bfa_trc(port->fcs, port->fabric->bb_credit); + + port->fabric->bb_credit = bfa_os_ntohs(plogi->csp.bbcred); + bfa_pport_set_tx_bbcredit(port->fcs->bfa, + port->fabric->bb_credit); + } + +} + +/** + * Called to handle LOGO received from an existing remote port. + */ +static void +bfa_fcs_rport_process_logo(struct bfa_fcs_rport_s *rport, struct fchs_s *fchs) +{ + rport->reply_oxid = fchs->ox_id; + bfa_trc(rport->fcs, rport->reply_oxid); + + rport->stats.logo_rcvd++; + bfa_sm_send_event(rport, RPSM_EVENT_LOGO_RCVD); +} + + + +/** + * fcs_rport_public FCS rport public interfaces + */ + +/** + * Called by bport/vport to create a remote port instance for a discovered + * remote device. + * + * @param[in] port - base port or vport + * @param[in] rpid - remote port ID + * + * @return None + */ +struct bfa_fcs_rport_s * +bfa_fcs_rport_create(struct bfa_fcs_port_s *port, u32 rpid) +{ + struct bfa_fcs_rport_s *rport; + + bfa_trc(port->fcs, rpid); + rport = bfa_fcs_rport_alloc(port, WWN_NULL, rpid); + if (!rport) + return NULL; + + bfa_sm_send_event(rport, RPSM_EVENT_PLOGI_SEND); + return rport; +} + +/** + * Called to create a rport for which only the wwn is known. + * + * @param[in] port - base port + * @param[in] rpwwn - remote port wwn + * + * @return None + */ +struct bfa_fcs_rport_s * +bfa_fcs_rport_create_by_wwn(struct bfa_fcs_port_s *port, wwn_t rpwwn) +{ + struct bfa_fcs_rport_s *rport; + + bfa_trc(port->fcs, rpwwn); + rport = bfa_fcs_rport_alloc(port, rpwwn, 0); + if (!rport) + return NULL; + + bfa_sm_send_event(rport, RPSM_EVENT_ADDRESS_DISC); + return rport; +} + +/** + * Called by bport in private loop topology to indicate that a + * rport has been discovered and plogi has been completed. + * + * @param[in] port - base port or vport + * @param[in] rpid - remote port ID + */ +void +bfa_fcs_rport_start(struct bfa_fcs_port_s *port, struct fchs_s *fchs, + struct fc_logi_s *plogi) +{ + struct bfa_fcs_rport_s *rport; + + rport = bfa_fcs_rport_alloc(port, WWN_NULL, fchs->s_id); + if (!rport) + return; + + bfa_fcs_rport_update(rport, plogi); + + bfa_sm_send_event(rport, RPSM_EVENT_PLOGI_COMP); +} + +/** + * Called by bport/vport to handle PLOGI received from a new remote port. + * If an existing rport does a plogi, it will be handled separately. + */ +void +bfa_fcs_rport_plogi_create(struct bfa_fcs_port_s *port, struct fchs_s *fchs, + struct fc_logi_s *plogi) +{ + struct bfa_fcs_rport_s *rport; + + rport = bfa_fcs_rport_alloc(port, plogi->port_name, fchs->s_id); + if (!rport) + return; + + bfa_fcs_rport_update(rport, plogi); + + rport->reply_oxid = fchs->ox_id; + bfa_trc(rport->fcs, rport->reply_oxid); + + rport->stats.plogi_rcvd++; + bfa_sm_send_event(rport, RPSM_EVENT_PLOGI_RCVD); +} + +static int +wwn_compare(wwn_t wwn1, wwn_t wwn2) +{ + u8 *b1 = (u8 *) &wwn1; + u8 *b2 = (u8 *) &wwn2; + int i; + + for (i = 0; i < sizeof(wwn_t); i++) { + if (b1[i] < b2[i]) + return -1; + if (b1[i] > b2[i]) + return 1; + } + return 0; +} + +/** + * Called by bport/vport to handle PLOGI received from an existing + * remote port. + */ +void +bfa_fcs_rport_plogi(struct bfa_fcs_rport_s *rport, struct fchs_s *rx_fchs, + struct fc_logi_s *plogi) +{ + /** + * @todo Handle P2P and initiator-initiator. + */ + + bfa_fcs_rport_update(rport, plogi); + + rport->reply_oxid = rx_fchs->ox_id; + bfa_trc(rport->fcs, rport->reply_oxid); + + /** + * In Switched fabric topology, + * PLOGI to each other. If our pwwn is smaller, ignore it, + * if it is not a well known address. + * If the link topology is N2N, + * this Plogi should be accepted. + */ + if ((wwn_compare(rport->port->port_cfg.pwwn, rport->pwwn) == -1) + && (bfa_fcs_fabric_is_switched(rport->port->fabric)) + && (!BFA_FCS_PID_IS_WKA(rport->pid))) { + bfa_trc(rport->fcs, rport->pid); + return; + } + + rport->stats.plogi_rcvd++; + bfa_sm_send_event(rport, RPSM_EVENT_PLOGI_RCVD); +} + +/** + * Called by bport/vport to delete a remote port instance. + * +* Rport delete is called under the following conditions: + * - vport is deleted + * - vf is deleted + * - explicit request from OS to delete rport (vmware) + */ +void +bfa_fcs_rport_delete(struct bfa_fcs_rport_s *rport) +{ + bfa_sm_send_event(rport, RPSM_EVENT_DELETE); +} + +/** + * Called by bport/vport to when a target goes offline. + * + */ +void +bfa_fcs_rport_offline(struct bfa_fcs_rport_s *rport) +{ + bfa_sm_send_event(rport, RPSM_EVENT_LOGO_IMP); +} + +/** + * Called by bport in n2n when a target (attached port) becomes online. + * + */ +void +bfa_fcs_rport_online(struct bfa_fcs_rport_s *rport) +{ + bfa_sm_send_event(rport, RPSM_EVENT_PLOGI_SEND); +} + +/** + * Called by bport/vport to notify SCN for the remote port + */ +void +bfa_fcs_rport_scn(struct bfa_fcs_rport_s *rport) +{ + + rport->stats.rscns++; + bfa_sm_send_event(rport, RPSM_EVENT_SCN); +} + +/** + * Called by fcpim to notify that the ITN cleanup is done. + */ +void +bfa_fcs_rport_itnim_ack(struct bfa_fcs_rport_s *rport) +{ + bfa_sm_send_event(rport, RPSM_EVENT_FC4_OFFLINE); +} + +/** + * Called by fcptm to notify that the ITN cleanup is done. + */ +void +bfa_fcs_rport_tin_ack(struct bfa_fcs_rport_s *rport) +{ + bfa_sm_send_event(rport, RPSM_EVENT_FC4_OFFLINE); +} + +/** + * This routine BFA callback for bfa_rport_online() call. + * + * param[in] cb_arg - rport struct. + * + * return + * void + * +* Special Considerations: + * + * note + */ +void +bfa_cb_rport_online(void *cbarg) +{ + + struct bfa_fcs_rport_s *rport = (struct bfa_fcs_rport_s *)cbarg; + + bfa_trc(rport->fcs, rport->pwwn); + bfa_sm_send_event(rport, RPSM_EVENT_HCB_ONLINE); +} + +/** + * This routine BFA callback for bfa_rport_offline() call. + * + * param[in] rport - + * + * return + * void + * + * Special Considerations: + * + * note + */ +void +bfa_cb_rport_offline(void *cbarg) +{ + struct bfa_fcs_rport_s *rport = (struct bfa_fcs_rport_s *)cbarg; + + bfa_trc(rport->fcs, rport->pwwn); + bfa_sm_send_event(rport, RPSM_EVENT_HCB_OFFLINE); +} + +/** + * This routine is a static BFA callback when there is a QoS flow_id + * change notification + * + * @param[in] rport - + * + * @return void + * + * Special Considerations: + * + * @note + */ +void +bfa_cb_rport_qos_scn_flowid(void *cbarg, + struct bfa_rport_qos_attr_s old_qos_attr, + struct bfa_rport_qos_attr_s new_qos_attr) +{ + struct bfa_fcs_rport_s *rport = (struct bfa_fcs_rport_s *)cbarg; + struct bfa_rport_aen_data_s aen_data; + + bfa_trc(rport->fcs, rport->pwwn); + aen_data.priv.qos = new_qos_attr; + bfa_fcs_rport_aen_post(rport, BFA_RPORT_AEN_QOS_FLOWID, &aen_data); +} + +/** + * This routine is a static BFA callback when there is a QoS priority + * change notification + * + * @param[in] rport - + * + * @return void + * + * Special Considerations: + * + * @note + */ +void +bfa_cb_rport_qos_scn_prio(void *cbarg, struct bfa_rport_qos_attr_s old_qos_attr, + struct bfa_rport_qos_attr_s new_qos_attr) +{ + struct bfa_fcs_rport_s *rport = (struct bfa_fcs_rport_s *)cbarg; + struct bfa_rport_aen_data_s aen_data; + + bfa_trc(rport->fcs, rport->pwwn); + aen_data.priv.qos = new_qos_attr; + bfa_fcs_rport_aen_post(rport, BFA_RPORT_AEN_QOS_PRIO, &aen_data); +} + +/** + * Called to process any unsolicted frames from this remote port + */ +void +bfa_fcs_rport_logo_imp(struct bfa_fcs_rport_s *rport) +{ + bfa_sm_send_event(rport, RPSM_EVENT_LOGO_IMP); +} + +/** + * Called to process any unsolicted frames from this remote port + */ +void +bfa_fcs_rport_uf_recv(struct bfa_fcs_rport_s *rport, struct fchs_s *fchs, + u16 len) +{ + struct bfa_fcs_port_s *port = rport->port; + struct fc_els_cmd_s *els_cmd; + + bfa_trc(rport->fcs, fchs->s_id); + bfa_trc(rport->fcs, fchs->d_id); + bfa_trc(rport->fcs, fchs->type); + + if (fchs->type != FC_TYPE_ELS) + return; + + els_cmd = (struct fc_els_cmd_s *) (fchs + 1); + + bfa_trc(rport->fcs, els_cmd->els_code); + + switch (els_cmd->els_code) { + case FC_ELS_LOGO: + bfa_fcs_rport_process_logo(rport, fchs); + break; + + case FC_ELS_ADISC: + bfa_fcs_rport_process_adisc(rport, fchs, len); + break; + + case FC_ELS_PRLO: + if (bfa_fcs_port_is_initiator(port)) + bfa_fcs_fcpim_uf_recv(rport->itnim, fchs, len); + + if (bfa_fcs_port_is_target(port)) + bfa_fcs_fcptm_uf_recv(rport->tin, fchs, len); + break; + + case FC_ELS_PRLI: + bfa_fcs_rport_process_prli(rport, fchs, len); + break; + + case FC_ELS_RPSC: + bfa_fcs_rport_process_rpsc(rport, fchs, len); + break; + + default: + bfa_fcs_rport_send_ls_rjt(rport, fchs, + FC_LS_RJT_RSN_CMD_NOT_SUPP, + FC_LS_RJT_EXP_NO_ADDL_INFO); + break; + } +} + +/* + * Send a LS reject + */ +static void +bfa_fcs_rport_send_ls_rjt(struct bfa_fcs_rport_s *rport, struct fchs_s *rx_fchs, + u8 reason_code, u8 reason_code_expl) +{ + struct bfa_fcs_port_s *port = rport->port; + struct fchs_s fchs; + struct bfa_fcxp_s *fcxp; + int len; + + bfa_trc(rport->fcs, rx_fchs->s_id); + + fcxp = bfa_fcs_fcxp_alloc(rport->fcs); + if (!fcxp) + return; + + len = fc_ls_rjt_build(&fchs, bfa_fcxp_get_reqbuf(fcxp), rx_fchs->s_id, + bfa_fcs_port_get_fcid(port), rx_fchs->ox_id, + reason_code, reason_code_expl); + + bfa_fcxp_send(fcxp, NULL, port->fabric->vf_id, port->lp_tag, BFA_FALSE, + FC_CLASS_3, len, &fchs, NULL, NULL, FC_MAX_PDUSZ, 0); +} + +/** + * Module initialization + */ +void +bfa_fcs_rport_modinit(struct bfa_fcs_s *fcs) +{ +} + +/** + * Module cleanup + */ +void +bfa_fcs_rport_modexit(struct bfa_fcs_s *fcs) +{ + bfa_fcs_modexit_comp(fcs); +} + +/** + * Return state of rport. + */ +int +bfa_fcs_rport_get_state(struct bfa_fcs_rport_s *rport) +{ + return bfa_sm_to_state(rport_sm_table, rport->sm); +} + +/** + * Called by the Driver to set rport delete/ageout timeout + * + * param[in] rport timeout value in seconds. + * + * return None + */ +void +bfa_fcs_rport_set_del_timeout(u8 rport_tmo) +{ + /* + * convert to Millisecs + */ + if (rport_tmo > 0) + bfa_fcs_rport_del_timeout = rport_tmo * 1000; +} diff --git a/drivers/scsi/bfa/rport_api.c b/drivers/scsi/bfa/rport_api.c new file mode 100644 index 000000000000..3dae1774181e --- /dev/null +++ b/drivers/scsi/bfa/rport_api.c @@ -0,0 +1,180 @@ +/* + * Copyright (c) 2005-2009 Brocade Communications Systems, Inc. + * All rights reserved + * www.brocade.com + * + * Linux driver for Brocade Fibre Channel Host Bus Adapter. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (GPL) Version 2 as + * published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ +#include <bfa.h> +#include <bfa_svc.h> +#include "fcs_vport.h" +#include "fcs_lport.h" +#include "fcs_rport.h" +#include "fcs_trcmod.h" + +BFA_TRC_FILE(FCS, RPORT_API); + +/** + * rport_api.c Remote port implementation. + */ + +/** + * fcs_rport_api FCS rport API. + */ + +/** + * Direct API to add a target by port wwn. This interface is used, for + * example, by bios when target pwwn is known from boot lun configuration. + */ +bfa_status_t +bfa_fcs_rport_add(struct bfa_fcs_port_s *port, wwn_t *pwwn, + struct bfa_fcs_rport_s *rport, + struct bfad_rport_s *rport_drv) +{ + bfa_trc(port->fcs, *pwwn); + + return BFA_STATUS_OK; +} + +/** + * Direct API to remove a target and its associated resources. This + * interface is used, for example, by vmware driver to remove target + * ports from the target list for a VM. + */ +bfa_status_t +bfa_fcs_rport_remove(struct bfa_fcs_rport_s *rport_in) +{ + + struct bfa_fcs_rport_s *rport; + + bfa_trc(rport_in->fcs, rport_in->pwwn); + + rport = bfa_fcs_port_get_rport_by_pwwn(rport_in->port, rport_in->pwwn); + if (rport == NULL) { + /* + * TBD Error handling + */ + bfa_trc(rport_in->fcs, rport_in->pid); + return BFA_STATUS_UNKNOWN_RWWN; + } + + /* + * TBD if this remote port is online, send a logo + */ + return BFA_STATUS_OK; + +} + +/** + * Remote device status for display/debug. + */ +void +bfa_fcs_rport_get_attr(struct bfa_fcs_rport_s *rport, + struct bfa_rport_attr_s *rport_attr) +{ + struct bfa_rport_qos_attr_s qos_attr; + struct bfa_fcs_port_s *port = rport->port; + + bfa_os_memset(rport_attr, 0, sizeof(struct bfa_rport_attr_s)); + + rport_attr->pid = rport->pid; + rport_attr->pwwn = rport->pwwn; + rport_attr->nwwn = rport->nwwn; + rport_attr->cos_supported = rport->fc_cos; + rport_attr->df_sz = rport->maxfrsize; + rport_attr->state = bfa_fcs_rport_get_state(rport); + rport_attr->fc_cos = rport->fc_cos; + rport_attr->cisc = rport->cisc; + rport_attr->scsi_function = rport->scsi_function; + rport_attr->curr_speed = rport->rpf.rpsc_speed; + rport_attr->assigned_speed = rport->rpf.assigned_speed; + + bfa_rport_get_qos_attr(rport->bfa_rport, &qos_attr); + rport_attr->qos_attr = qos_attr; + + rport_attr->trl_enforced = BFA_FALSE; + if (bfa_pport_is_ratelim(port->fcs->bfa)) { + if ((rport->rpf.rpsc_speed == BFA_PPORT_SPEED_UNKNOWN) || + (rport->rpf.rpsc_speed < + bfa_fcs_port_get_rport_max_speed(port))) + rport_attr->trl_enforced = BFA_TRUE; + } + + /* + * TODO + * rport->symname + */ +} + +/** + * Per remote device statistics. + */ +void +bfa_fcs_rport_get_stats(struct bfa_fcs_rport_s *rport, + struct bfa_rport_stats_s *stats) +{ + *stats = rport->stats; +} + +void +bfa_fcs_rport_clear_stats(struct bfa_fcs_rport_s *rport) +{ + bfa_os_memset((char *)&rport->stats, 0, + sizeof(struct bfa_rport_stats_s)); +} + +struct bfa_fcs_rport_s * +bfa_fcs_rport_lookup(struct bfa_fcs_port_s *port, wwn_t rpwwn) +{ + struct bfa_fcs_rport_s *rport; + + rport = bfa_fcs_port_get_rport_by_pwwn(port, rpwwn); + if (rport == NULL) { + /* + * TBD Error handling + */ + } + + return rport; +} + +struct bfa_fcs_rport_s * +bfa_fcs_rport_lookup_by_nwwn(struct bfa_fcs_port_s *port, wwn_t rnwwn) +{ + struct bfa_fcs_rport_s *rport; + + rport = bfa_fcs_port_get_rport_by_nwwn(port, rnwwn); + if (rport == NULL) { + /* + * TBD Error handling + */ + } + + return rport; +} + +/* + * This API is to set the Rport's speed. Should be used when RPSC is not + * supported by the rport. + */ +void +bfa_fcs_rport_set_speed(struct bfa_fcs_rport_s *rport, + enum bfa_pport_speed speed) +{ + rport->rpf.assigned_speed = speed; + + /* Set this speed in f/w only if the RPSC speed is not available */ + if (rport->rpf.rpsc_speed == BFA_PPORT_SPEED_UNKNOWN) + bfa_rport_speed(rport->bfa_rport, speed); +} + + diff --git a/drivers/scsi/bfa/rport_ftrs.c b/drivers/scsi/bfa/rport_ftrs.c new file mode 100644 index 000000000000..8a1f59d596c1 --- /dev/null +++ b/drivers/scsi/bfa/rport_ftrs.c @@ -0,0 +1,375 @@ +/* + * Copyright (c) 2005-2009 Brocade Communications Systems, Inc. + * All rights reserved + * www.brocade.com + * + * Linux driver for Brocade Fibre Channel Host Bus Adapter. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (GPL) Version 2 as + * published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +/** + * rport_ftrs.c Remote port features (RPF) implementation. + */ + +#include <bfa.h> +#include <bfa_svc.h> +#include "fcbuild.h" +#include "fcs_rport.h" +#include "fcs_lport.h" +#include "fcs_trcmod.h" +#include "fcs_fcxp.h" +#include "fcs.h" + +BFA_TRC_FILE(FCS, RPORT_FTRS); + +#define BFA_FCS_RPF_RETRIES (3) +#define BFA_FCS_RPF_RETRY_TIMEOUT (1000) /* 1 sec (In millisecs) */ + +static void bfa_fcs_rpf_send_rpsc2(void *rport_cbarg, + struct bfa_fcxp_s *fcxp_alloced); +static void bfa_fcs_rpf_rpsc2_response(void *fcsarg, + struct bfa_fcxp_s *fcxp, void *cbarg, + bfa_status_t req_status, u32 rsp_len, + u32 resid_len, + struct fchs_s *rsp_fchs); +static void bfa_fcs_rpf_timeout(void *arg); + +/** + * fcs_rport_ftrs_sm FCS rport state machine events + */ + +enum rpf_event { + RPFSM_EVENT_RPORT_OFFLINE = 1, /* Rport offline */ + RPFSM_EVENT_RPORT_ONLINE = 2, /* Rport online */ + RPFSM_EVENT_FCXP_SENT = 3, /* Frame from has been sent */ + RPFSM_EVENT_TIMEOUT = 4, /* Rport SM timeout event */ + RPFSM_EVENT_RPSC_COMP = 5, + RPFSM_EVENT_RPSC_FAIL = 6, + RPFSM_EVENT_RPSC_ERROR = 7, +}; + +static void bfa_fcs_rpf_sm_uninit(struct bfa_fcs_rpf_s *rpf, + enum rpf_event event); +static void bfa_fcs_rpf_sm_rpsc_sending(struct bfa_fcs_rpf_s *rpf, + enum rpf_event event); +static void bfa_fcs_rpf_sm_rpsc(struct bfa_fcs_rpf_s *rpf, + enum rpf_event event); +static void bfa_fcs_rpf_sm_rpsc_retry(struct bfa_fcs_rpf_s *rpf, + enum rpf_event event); +static void bfa_fcs_rpf_sm_offline(struct bfa_fcs_rpf_s *rpf, + enum rpf_event event); +static void bfa_fcs_rpf_sm_online(struct bfa_fcs_rpf_s *rpf, + enum rpf_event event); + +static void +bfa_fcs_rpf_sm_uninit(struct bfa_fcs_rpf_s *rpf, enum rpf_event event) +{ + struct bfa_fcs_rport_s *rport = rpf->rport; + + bfa_trc(rport->fcs, rport->pwwn); + bfa_trc(rport->fcs, rport->pid); + bfa_trc(rport->fcs, event); + + switch (event) { + case RPFSM_EVENT_RPORT_ONLINE : + if (!BFA_FCS_PID_IS_WKA(rport->pid)) { + bfa_sm_set_state(rpf, bfa_fcs_rpf_sm_rpsc_sending); + rpf->rpsc_retries = 0; + bfa_fcs_rpf_send_rpsc2(rpf, NULL); + break; + }; + + case RPFSM_EVENT_RPORT_OFFLINE : + break; + + default: + bfa_assert(0); + } +} + +static void +bfa_fcs_rpf_sm_rpsc_sending(struct bfa_fcs_rpf_s *rpf, enum rpf_event event) +{ + struct bfa_fcs_rport_s *rport = rpf->rport; + + bfa_trc(rport->fcs, event); + + switch (event) { + case RPFSM_EVENT_FCXP_SENT: + bfa_sm_set_state(rpf, bfa_fcs_rpf_sm_rpsc); + break; + + case RPFSM_EVENT_RPORT_OFFLINE : + bfa_sm_set_state(rpf, bfa_fcs_rpf_sm_offline); + bfa_fcxp_walloc_cancel(rport->fcs->bfa, &rpf->fcxp_wqe); + rpf->rpsc_retries = 0; + break; + + default: + bfa_assert(0); + } +} + +static void +bfa_fcs_rpf_sm_rpsc(struct bfa_fcs_rpf_s *rpf, enum rpf_event event) +{ + struct bfa_fcs_rport_s *rport = rpf->rport; + + bfa_trc(rport->fcs, rport->pid); + bfa_trc(rport->fcs, event); + + switch (event) { + case RPFSM_EVENT_RPSC_COMP: + bfa_sm_set_state(rpf, bfa_fcs_rpf_sm_online); + /* Update speed info in f/w via BFA */ + if (rpf->rpsc_speed != BFA_PPORT_SPEED_UNKNOWN) { + bfa_rport_speed(rport->bfa_rport, rpf->rpsc_speed); + } else if (rpf->assigned_speed != BFA_PPORT_SPEED_UNKNOWN) { + bfa_rport_speed(rport->bfa_rport, rpf->assigned_speed); + } + break; + + case RPFSM_EVENT_RPSC_FAIL: + /* RPSC not supported by rport */ + bfa_sm_set_state(rpf, bfa_fcs_rpf_sm_online); + break; + + case RPFSM_EVENT_RPSC_ERROR: + /* need to retry...delayed a bit. */ + if (rpf->rpsc_retries++ < BFA_FCS_RPF_RETRIES) { + bfa_timer_start(rport->fcs->bfa, &rpf->timer, + bfa_fcs_rpf_timeout, rpf, + BFA_FCS_RPF_RETRY_TIMEOUT); + bfa_sm_set_state(rpf, bfa_fcs_rpf_sm_rpsc_retry); + } else { + bfa_sm_set_state(rpf, bfa_fcs_rpf_sm_online); + } + break; + + case RPFSM_EVENT_RPORT_OFFLINE : + bfa_sm_set_state(rpf, bfa_fcs_rpf_sm_offline); + bfa_fcxp_discard(rpf->fcxp); + rpf->rpsc_retries = 0; + break; + + default: + bfa_assert(0); + } +} + +static void +bfa_fcs_rpf_sm_rpsc_retry(struct bfa_fcs_rpf_s *rpf, enum rpf_event event) +{ + struct bfa_fcs_rport_s *rport = rpf->rport; + + bfa_trc(rport->fcs, rport->pid); + bfa_trc(rport->fcs, event); + + switch (event) { + case RPFSM_EVENT_TIMEOUT : + /* re-send the RPSC */ + bfa_sm_set_state(rpf, bfa_fcs_rpf_sm_rpsc_sending); + bfa_fcs_rpf_send_rpsc2(rpf, NULL); + break; + + case RPFSM_EVENT_RPORT_OFFLINE : + bfa_timer_stop(&rpf->timer); + bfa_sm_set_state(rpf, bfa_fcs_rpf_sm_offline); + rpf->rpsc_retries = 0; + break; + + default: + bfa_assert(0); + } +} + +static void +bfa_fcs_rpf_sm_online(struct bfa_fcs_rpf_s *rpf, enum rpf_event event) +{ + struct bfa_fcs_rport_s *rport = rpf->rport; + + bfa_trc(rport->fcs, rport->pwwn); + bfa_trc(rport->fcs, rport->pid); + bfa_trc(rport->fcs, event); + + switch (event) { + case RPFSM_EVENT_RPORT_OFFLINE : + bfa_sm_set_state(rpf, bfa_fcs_rpf_sm_offline); + rpf->rpsc_retries = 0; + break; + + default: + bfa_assert(0); + } +} + +static void +bfa_fcs_rpf_sm_offline(struct bfa_fcs_rpf_s *rpf, enum rpf_event event) +{ + struct bfa_fcs_rport_s *rport = rpf->rport; + + bfa_trc(rport->fcs, rport->pwwn); + bfa_trc(rport->fcs, rport->pid); + bfa_trc(rport->fcs, event); + + switch (event) { + case RPFSM_EVENT_RPORT_ONLINE : + bfa_sm_set_state(rpf, bfa_fcs_rpf_sm_rpsc_sending); + bfa_fcs_rpf_send_rpsc2(rpf, NULL); + break; + + case RPFSM_EVENT_RPORT_OFFLINE : + break; + + default: + bfa_assert(0); + } +} +/** + * Called when Rport is created. + */ +void bfa_fcs_rpf_init(struct bfa_fcs_rport_s *rport) +{ + struct bfa_fcs_rpf_s *rpf = &rport->rpf; + + bfa_trc(rport->fcs, rport->pid); + rpf->rport = rport; + + bfa_sm_set_state(rpf, bfa_fcs_rpf_sm_uninit); +} + +/** + * Called when Rport becomes online + */ +void bfa_fcs_rpf_rport_online(struct bfa_fcs_rport_s *rport) +{ + bfa_trc(rport->fcs, rport->pid); + + if (__fcs_min_cfg(rport->port->fcs)) + return; + + if (bfa_fcs_fabric_is_switched(rport->port->fabric)) + bfa_sm_send_event(&rport->rpf, RPFSM_EVENT_RPORT_ONLINE); +} + +/** + * Called when Rport becomes offline + */ +void bfa_fcs_rpf_rport_offline(struct bfa_fcs_rport_s *rport) +{ + bfa_trc(rport->fcs, rport->pid); + + if (__fcs_min_cfg(rport->port->fcs)) + return; + + bfa_sm_send_event(&rport->rpf, RPFSM_EVENT_RPORT_OFFLINE); +} + +static void +bfa_fcs_rpf_timeout(void *arg) +{ + struct bfa_fcs_rpf_s *rpf = (struct bfa_fcs_rpf_s *) arg; + struct bfa_fcs_rport_s *rport = rpf->rport; + + bfa_trc(rport->fcs, rport->pid); + bfa_sm_send_event(rpf, RPFSM_EVENT_TIMEOUT); +} + +static void +bfa_fcs_rpf_send_rpsc2(void *rpf_cbarg, struct bfa_fcxp_s *fcxp_alloced) +{ + struct bfa_fcs_rpf_s *rpf = (struct bfa_fcs_rpf_s *)rpf_cbarg; + struct bfa_fcs_rport_s *rport = rpf->rport; + struct bfa_fcs_port_s *port = rport->port; + struct fchs_s fchs; + int len; + struct bfa_fcxp_s *fcxp; + + bfa_trc(rport->fcs, rport->pwwn); + + fcxp = fcxp_alloced ? fcxp_alloced : bfa_fcs_fcxp_alloc(port->fcs); + if (!fcxp) { + bfa_fcxp_alloc_wait(port->fcs->bfa, &rpf->fcxp_wqe, + bfa_fcs_rpf_send_rpsc2, rpf); + return; + } + rpf->fcxp = fcxp; + + len = fc_rpsc2_build(&fchs, bfa_fcxp_get_reqbuf(fcxp), rport->pid, + bfa_fcs_port_get_fcid(port), &rport->pid, 1); + + bfa_fcxp_send(fcxp, NULL, port->fabric->vf_id, port->lp_tag, BFA_FALSE, + FC_CLASS_3, len, &fchs, bfa_fcs_rpf_rpsc2_response, + rpf, FC_MAX_PDUSZ, FC_RA_TOV); + rport->stats.rpsc_sent++; + bfa_sm_send_event(rpf, RPFSM_EVENT_FCXP_SENT); + +} + +static void +bfa_fcs_rpf_rpsc2_response(void *fcsarg, struct bfa_fcxp_s *fcxp, void *cbarg, + bfa_status_t req_status, u32 rsp_len, + u32 resid_len, struct fchs_s *rsp_fchs) +{ + struct bfa_fcs_rpf_s *rpf = (struct bfa_fcs_rpf_s *) cbarg; + struct bfa_fcs_rport_s *rport = rpf->rport; + struct fc_ls_rjt_s *ls_rjt; + struct fc_rpsc2_acc_s *rpsc2_acc; + u16 num_ents; + + bfa_trc(rport->fcs, req_status); + + if (req_status != BFA_STATUS_OK) { + bfa_trc(rport->fcs, req_status); + if (req_status == BFA_STATUS_ETIMER) + rport->stats.rpsc_failed++; + bfa_sm_send_event(rpf, RPFSM_EVENT_RPSC_ERROR); + return; + } + + rpsc2_acc = (struct fc_rpsc2_acc_s *) BFA_FCXP_RSP_PLD(fcxp); + if (rpsc2_acc->els_cmd == FC_ELS_ACC) { + rport->stats.rpsc_accs++; + num_ents = bfa_os_ntohs(rpsc2_acc->num_pids); + bfa_trc(rport->fcs, num_ents); + if (num_ents > 0) { + bfa_assert(rpsc2_acc->port_info[0].pid != rport->pid); + bfa_trc(rport->fcs, + bfa_os_ntohs(rpsc2_acc->port_info[0].pid)); + bfa_trc(rport->fcs, + bfa_os_ntohs(rpsc2_acc->port_info[0].speed)); + bfa_trc(rport->fcs, + bfa_os_ntohs(rpsc2_acc->port_info[0].index)); + bfa_trc(rport->fcs, + rpsc2_acc->port_info[0].type); + + if (rpsc2_acc->port_info[0].speed == 0) { + bfa_sm_send_event(rpf, RPFSM_EVENT_RPSC_ERROR); + return; + } + + rpf->rpsc_speed = fc_rpsc_operspeed_to_bfa_speed( + bfa_os_ntohs(rpsc2_acc->port_info[0].speed)); + + bfa_sm_send_event(rpf, RPFSM_EVENT_RPSC_COMP); + } + } else { + ls_rjt = (struct fc_ls_rjt_s *) BFA_FCXP_RSP_PLD(fcxp); + bfa_trc(rport->fcs, ls_rjt->reason_code); + bfa_trc(rport->fcs, ls_rjt->reason_code_expl); + rport->stats.rpsc_rejects++; + if (ls_rjt->reason_code == FC_LS_RJT_RSN_CMD_NOT_SUPP) { + bfa_sm_send_event(rpf, RPFSM_EVENT_RPSC_FAIL); + } else { + bfa_sm_send_event(rpf, RPFSM_EVENT_RPSC_ERROR); + } + } +} diff --git a/drivers/scsi/bfa/scn.c b/drivers/scsi/bfa/scn.c new file mode 100644 index 000000000000..bd4771ff62c8 --- /dev/null +++ b/drivers/scsi/bfa/scn.c @@ -0,0 +1,482 @@ +/* + * Copyright (c) 2005-2009 Brocade Communications Systems, Inc. + * All rights reserved + * www.brocade.com + * + * Linux driver for Brocade Fibre Channel Host Bus Adapter. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (GPL) Version 2 as + * published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#include <bfa.h> +#include <bfa_svc.h> +#include "fcs_lport.h" +#include "fcs_rport.h" +#include "fcs_ms.h" +#include "fcs_trcmod.h" +#include "fcs_fcxp.h" +#include "fcs.h" +#include "lport_priv.h" + +BFA_TRC_FILE(FCS, SCN); + +#define FC_QOS_RSCN_EVENT 0x0c +#define FC_FABRIC_NAME_RSCN_EVENT 0x0d + +/* + * forward declarations + */ +static void bfa_fcs_port_scn_send_scr(void *scn_cbarg, + struct bfa_fcxp_s *fcxp_alloced); +static void bfa_fcs_port_scn_scr_response(void *fcsarg, + struct bfa_fcxp_s *fcxp, + void *cbarg, + bfa_status_t req_status, + u32 rsp_len, + u32 resid_len, + struct fchs_s *rsp_fchs); +static void bfa_fcs_port_scn_send_ls_acc(struct bfa_fcs_port_s *port, + struct fchs_s *rx_fchs); +static void bfa_fcs_port_scn_timeout(void *arg); + +/** + * fcs_scm_sm FCS SCN state machine + */ + +/** + * VPort SCN State Machine events + */ +enum port_scn_event { + SCNSM_EVENT_PORT_ONLINE = 1, + SCNSM_EVENT_PORT_OFFLINE = 2, + SCNSM_EVENT_RSP_OK = 3, + SCNSM_EVENT_RSP_ERROR = 4, + SCNSM_EVENT_TIMEOUT = 5, + SCNSM_EVENT_SCR_SENT = 6, +}; + +static void bfa_fcs_port_scn_sm_offline(struct bfa_fcs_port_scn_s *scn, + enum port_scn_event event); +static void bfa_fcs_port_scn_sm_sending_scr(struct bfa_fcs_port_scn_s *scn, + enum port_scn_event event); +static void bfa_fcs_port_scn_sm_scr(struct bfa_fcs_port_scn_s *scn, + enum port_scn_event event); +static void bfa_fcs_port_scn_sm_scr_retry(struct bfa_fcs_port_scn_s *scn, + enum port_scn_event event); +static void bfa_fcs_port_scn_sm_online(struct bfa_fcs_port_scn_s *scn, + enum port_scn_event event); + +/** + * Starting state - awaiting link up. + */ +static void +bfa_fcs_port_scn_sm_offline(struct bfa_fcs_port_scn_s *scn, + enum port_scn_event event) +{ + switch (event) { + case SCNSM_EVENT_PORT_ONLINE: + bfa_sm_set_state(scn, bfa_fcs_port_scn_sm_sending_scr); + bfa_fcs_port_scn_send_scr(scn, NULL); + break; + + case SCNSM_EVENT_PORT_OFFLINE: + break; + + default: + bfa_assert(0); + } +} + +static void +bfa_fcs_port_scn_sm_sending_scr(struct bfa_fcs_port_scn_s *scn, + enum port_scn_event event) +{ + switch (event) { + case SCNSM_EVENT_SCR_SENT: + bfa_sm_set_state(scn, bfa_fcs_port_scn_sm_scr); + break; + + case SCNSM_EVENT_PORT_OFFLINE: + bfa_sm_set_state(scn, bfa_fcs_port_scn_sm_offline); + bfa_fcxp_walloc_cancel(scn->port->fcs->bfa, &scn->fcxp_wqe); + break; + + default: + bfa_assert(0); + } +} + +static void +bfa_fcs_port_scn_sm_scr(struct bfa_fcs_port_scn_s *scn, + enum port_scn_event event) +{ + struct bfa_fcs_port_s *port = scn->port; + + switch (event) { + case SCNSM_EVENT_RSP_OK: + bfa_sm_set_state(scn, bfa_fcs_port_scn_sm_online); + break; + + case SCNSM_EVENT_RSP_ERROR: + bfa_sm_set_state(scn, bfa_fcs_port_scn_sm_scr_retry); + bfa_timer_start(port->fcs->bfa, &scn->timer, + bfa_fcs_port_scn_timeout, scn, + BFA_FCS_RETRY_TIMEOUT); + break; + + case SCNSM_EVENT_PORT_OFFLINE: + bfa_sm_set_state(scn, bfa_fcs_port_scn_sm_offline); + bfa_fcxp_discard(scn->fcxp); + break; + + default: + bfa_assert(0); + } +} + +static void +bfa_fcs_port_scn_sm_scr_retry(struct bfa_fcs_port_scn_s *scn, + enum port_scn_event event) +{ + switch (event) { + case SCNSM_EVENT_TIMEOUT: + bfa_sm_set_state(scn, bfa_fcs_port_scn_sm_sending_scr); + bfa_fcs_port_scn_send_scr(scn, NULL); + break; + + case SCNSM_EVENT_PORT_OFFLINE: + bfa_sm_set_state(scn, bfa_fcs_port_scn_sm_offline); + bfa_timer_stop(&scn->timer); + break; + + default: + bfa_assert(0); + } +} + +static void +bfa_fcs_port_scn_sm_online(struct bfa_fcs_port_scn_s *scn, + enum port_scn_event event) +{ + switch (event) { + case SCNSM_EVENT_PORT_OFFLINE: + bfa_sm_set_state(scn, bfa_fcs_port_scn_sm_offline); + break; + + default: + bfa_assert(0); + } +} + + + +/** + * fcs_scn_private FCS SCN private functions + */ + +/** + * This routine will be called to send a SCR command. + */ +static void +bfa_fcs_port_scn_send_scr(void *scn_cbarg, struct bfa_fcxp_s *fcxp_alloced) +{ + struct bfa_fcs_port_scn_s *scn = scn_cbarg; + struct bfa_fcs_port_s *port = scn->port; + struct fchs_s fchs; + int len; + struct bfa_fcxp_s *fcxp; + + bfa_trc(port->fcs, port->pid); + bfa_trc(port->fcs, port->port_cfg.pwwn); + + fcxp = fcxp_alloced ? fcxp_alloced : bfa_fcs_fcxp_alloc(port->fcs); + if (!fcxp) { + bfa_fcxp_alloc_wait(port->fcs->bfa, &scn->fcxp_wqe, + bfa_fcs_port_scn_send_scr, scn); + return; + } + scn->fcxp = fcxp; + + /* + * Handle VU registrations for Base port only + */ + if ((!port->vport) && bfa_ioc_get_fcmode(&port->fcs->bfa->ioc)) { + len = fc_scr_build(&fchs, bfa_fcxp_get_reqbuf(fcxp), + bfa_lps_is_brcd_fabric(port->fabric->lps), + port->pid, 0); + } else { + len = fc_scr_build(&fchs, bfa_fcxp_get_reqbuf(fcxp), BFA_FALSE, + port->pid, 0); + } + + bfa_fcxp_send(fcxp, NULL, port->fabric->vf_id, port->lp_tag, BFA_FALSE, + FC_CLASS_3, len, &fchs, bfa_fcs_port_scn_scr_response, + (void *)scn, FC_MAX_PDUSZ, FC_RA_TOV); + + bfa_sm_send_event(scn, SCNSM_EVENT_SCR_SENT); +} + +static void +bfa_fcs_port_scn_scr_response(void *fcsarg, struct bfa_fcxp_s *fcxp, + void *cbarg, bfa_status_t req_status, + u32 rsp_len, u32 resid_len, + struct fchs_s *rsp_fchs) +{ + struct bfa_fcs_port_scn_s *scn = (struct bfa_fcs_port_scn_s *)cbarg; + struct bfa_fcs_port_s *port = scn->port; + struct fc_els_cmd_s *els_cmd; + struct fc_ls_rjt_s *ls_rjt; + + bfa_trc(port->fcs, port->port_cfg.pwwn); + + /* + * Sanity Checks + */ + if (req_status != BFA_STATUS_OK) { + bfa_trc(port->fcs, req_status); + bfa_sm_send_event(scn, SCNSM_EVENT_RSP_ERROR); + return; + } + + els_cmd = (struct fc_els_cmd_s *) BFA_FCXP_RSP_PLD(fcxp); + + switch (els_cmd->els_code) { + + case FC_ELS_ACC: + bfa_sm_send_event(scn, SCNSM_EVENT_RSP_OK); + break; + + case FC_ELS_LS_RJT: + + ls_rjt = (struct fc_ls_rjt_s *) BFA_FCXP_RSP_PLD(fcxp); + + bfa_trc(port->fcs, ls_rjt->reason_code); + bfa_trc(port->fcs, ls_rjt->reason_code_expl); + + bfa_sm_send_event(scn, SCNSM_EVENT_RSP_ERROR); + break; + + default: + bfa_sm_send_event(scn, SCNSM_EVENT_RSP_ERROR); + } +} + +/* + * Send a LS Accept + */ +static void +bfa_fcs_port_scn_send_ls_acc(struct bfa_fcs_port_s *port, + struct fchs_s *rx_fchs) +{ + struct fchs_s fchs; + struct bfa_fcxp_s *fcxp; + struct bfa_rport_s *bfa_rport = NULL; + int len; + + bfa_trc(port->fcs, rx_fchs->s_id); + + fcxp = bfa_fcs_fcxp_alloc(port->fcs); + if (!fcxp) + return; + + len = fc_ls_acc_build(&fchs, bfa_fcxp_get_reqbuf(fcxp), rx_fchs->s_id, + bfa_fcs_port_get_fcid(port), rx_fchs->ox_id); + + bfa_fcxp_send(fcxp, bfa_rport, port->fabric->vf_id, port->lp_tag, + BFA_FALSE, FC_CLASS_3, len, &fchs, NULL, NULL, + FC_MAX_PDUSZ, 0); +} + +/** + * This routine will be called by bfa_timer on timer timeouts. + * + * param[in] vport - pointer to bfa_fcs_port_t. + * param[out] vport_status - pointer to return vport status in + * + * return + * void + * +* Special Considerations: + * + * note + */ +static void +bfa_fcs_port_scn_timeout(void *arg) +{ + struct bfa_fcs_port_scn_s *scn = (struct bfa_fcs_port_scn_s *)arg; + + bfa_sm_send_event(scn, SCNSM_EVENT_TIMEOUT); +} + + + +/** + * fcs_scn_public FCS state change notification public interfaces + */ + +/* + * Functions called by port/fab + */ +void +bfa_fcs_port_scn_init(struct bfa_fcs_port_s *port) +{ + struct bfa_fcs_port_scn_s *scn = BFA_FCS_GET_SCN_FROM_PORT(port); + + scn->port = port; + bfa_sm_set_state(scn, bfa_fcs_port_scn_sm_offline); +} + +void +bfa_fcs_port_scn_offline(struct bfa_fcs_port_s *port) +{ + struct bfa_fcs_port_scn_s *scn = BFA_FCS_GET_SCN_FROM_PORT(port); + + scn->port = port; + bfa_sm_send_event(scn, SCNSM_EVENT_PORT_OFFLINE); +} + +void +bfa_fcs_port_scn_online(struct bfa_fcs_port_s *port) +{ + struct bfa_fcs_port_scn_s *scn = BFA_FCS_GET_SCN_FROM_PORT(port); + + scn->port = port; + bfa_sm_send_event(scn, SCNSM_EVENT_PORT_ONLINE); +} + +static void +bfa_fcs_port_scn_portid_rscn(struct bfa_fcs_port_s *port, u32 rpid) +{ + struct bfa_fcs_rport_s *rport; + + bfa_trc(port->fcs, rpid); + + /** + * If this is an unknown device, then it just came online. + * Otherwise let rport handle the RSCN event. + */ + rport = bfa_fcs_port_get_rport_by_pid(port, rpid); + if (rport == NULL) { + /* + * If min cfg mode is enabled, we donot need to + * discover any new rports. + */ + if (!__fcs_min_cfg(port->fcs)) + rport = bfa_fcs_rport_create(port, rpid); + } else { + bfa_fcs_rport_scn(rport); + } +} + +/** + * rscn format based PID comparison + */ +#define __fc_pid_match(__c0, __c1, __fmt) \ + (((__fmt) == FC_RSCN_FORMAT_FABRIC) || \ + (((__fmt) == FC_RSCN_FORMAT_DOMAIN) && \ + ((__c0)[0] == (__c1)[0])) || \ + (((__fmt) == FC_RSCN_FORMAT_AREA) && \ + ((__c0)[0] == (__c1)[0]) && \ + ((__c0)[1] == (__c1)[1]))) + +static void +bfa_fcs_port_scn_multiport_rscn(struct bfa_fcs_port_s *port, + enum fc_rscn_format format, u32 rscn_pid) +{ + struct bfa_fcs_rport_s *rport; + struct list_head *qe, *qe_next; + u8 *c0, *c1; + + bfa_trc(port->fcs, format); + bfa_trc(port->fcs, rscn_pid); + + c0 = (u8 *) &rscn_pid; + + list_for_each_safe(qe, qe_next, &port->rport_q) { + rport = (struct bfa_fcs_rport_s *)qe; + c1 = (u8 *) &rport->pid; + if (__fc_pid_match(c0, c1, format)) + bfa_fcs_rport_scn(rport); + } +} + +void +bfa_fcs_port_scn_process_rscn(struct bfa_fcs_port_s *port, struct fchs_s *fchs, + u32 len) +{ + struct fc_rscn_pl_s *rscn = (struct fc_rscn_pl_s *) (fchs + 1); + int num_entries; + u32 rscn_pid; + bfa_boolean_t nsquery = BFA_FALSE; + int i = 0; + + num_entries = + (bfa_os_ntohs(rscn->payldlen) - + sizeof(u32)) / sizeof(rscn->event[0]); + + bfa_trc(port->fcs, num_entries); + + port->stats.num_rscn++; + + bfa_fcs_port_scn_send_ls_acc(port, fchs); + + for (i = 0; i < num_entries; i++) { + rscn_pid = rscn->event[i].portid; + + bfa_trc(port->fcs, rscn->event[i].format); + bfa_trc(port->fcs, rscn_pid); + + switch (rscn->event[i].format) { + case FC_RSCN_FORMAT_PORTID: + if (rscn->event[i].qualifier == FC_QOS_RSCN_EVENT) { + /* + * Ignore this event. f/w would have processed + * it + */ + bfa_trc(port->fcs, rscn_pid); + } else { + port->stats.num_portid_rscn++; + bfa_fcs_port_scn_portid_rscn(port, rscn_pid); + } + break; + + case FC_RSCN_FORMAT_FABRIC: + if (rscn->event[i].qualifier == + FC_FABRIC_NAME_RSCN_EVENT) { + bfa_fcs_port_ms_fabric_rscn(port); + break; + } + /* + * !!!!!!!!! Fall Through !!!!!!!!!!!!! + */ + + case FC_RSCN_FORMAT_AREA: + case FC_RSCN_FORMAT_DOMAIN: + nsquery = BFA_TRUE; + bfa_fcs_port_scn_multiport_rscn(port, + rscn->event[i].format, + rscn_pid); + break; + + default: + bfa_assert(0); + nsquery = BFA_TRUE; + } + } + + /** + * If any of area, domain or fabric RSCN is received, do a fresh discovery + * to find new devices. + */ + if (nsquery) + bfa_fcs_port_ns_query(port); +} + + diff --git a/drivers/scsi/bfa/vfapi.c b/drivers/scsi/bfa/vfapi.c new file mode 100644 index 000000000000..31d81fe2fc48 --- /dev/null +++ b/drivers/scsi/bfa/vfapi.c @@ -0,0 +1,292 @@ +/* + * Copyright (c) 2005-2009 Brocade Communications Systems, Inc. + * All rights reserved + * www.brocade.com + * + * Linux driver for Brocade Fibre Channel Host Bus Adapter. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (GPL) Version 2 as + * published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +/** + * vfapi.c Fabric module implementation. + */ + +#include "fcs_fabric.h" +#include "fcs_trcmod.h" + +BFA_TRC_FILE(FCS, VFAPI); + +/** + * fcs_vf_api virtual fabrics API + */ + +/** + * Enable VF mode. + * + * @param[in] fcs fcs module instance + * @param[in] vf_id default vf_id of port, FC_VF_ID_NULL + * to use standard default vf_id of 1. + * + * @retval BFA_STATUS_OK vf mode is enabled + * @retval BFA_STATUS_BUSY Port is active. Port must be disabled + * before VF mode can be enabled. + */ +bfa_status_t +bfa_fcs_vf_mode_enable(struct bfa_fcs_s *fcs, u16 vf_id) +{ + return BFA_STATUS_OK; +} + +/** + * Disable VF mode. + * + * @param[in] fcs fcs module instance + * + * @retval BFA_STATUS_OK vf mode is disabled + * @retval BFA_STATUS_BUSY VFs are present and being used. All + * VFs must be deleted before disabling + * VF mode. + */ +bfa_status_t +bfa_fcs_vf_mode_disable(struct bfa_fcs_s *fcs) +{ + return BFA_STATUS_OK; +} + +/** + * Create a new VF instance. + * + * A new VF is created using the given VF configuration. A VF is identified + * by VF id. No duplicate VF creation is allowed with the same VF id. Once + * a VF is created, VF is automatically started after link initialization + * and EVFP exchange is completed. + * + * param[in] vf - FCS vf data structure. Memory is + * allocated by caller (driver) + * param[in] fcs - FCS module + * param[in] vf_cfg - VF configuration + * param[in] vf_drv - Opaque handle back to the driver's + * virtual vf structure + * + * retval BFA_STATUS_OK VF creation is successful + * retval BFA_STATUS_FAILED VF creation failed + * retval BFA_STATUS_EEXIST A VF exists with the given vf_id + */ +bfa_status_t +bfa_fcs_vf_create(bfa_fcs_vf_t *vf, struct bfa_fcs_s *fcs, u16 vf_id, + struct bfa_port_cfg_s *port_cfg, struct bfad_vf_s *vf_drv) +{ + bfa_trc(fcs, vf_id); + return BFA_STATUS_OK; +} + +/** + * Use this function to delete a BFA VF object. VF object should + * be stopped before this function call. + * + * param[in] vf - pointer to bfa_vf_t. + * + * retval BFA_STATUS_OK On vf deletion success + * retval BFA_STATUS_BUSY VF is not in a stopped state + * retval BFA_STATUS_INPROGRESS VF deletion in in progress + */ +bfa_status_t +bfa_fcs_vf_delete(bfa_fcs_vf_t *vf) +{ + bfa_trc(vf->fcs, vf->vf_id); + return BFA_STATUS_OK; +} + +/** + * Start participation in VF. This triggers login to the virtual fabric. + * + * param[in] vf - pointer to bfa_vf_t. + * + * return None + */ +void +bfa_fcs_vf_start(bfa_fcs_vf_t *vf) +{ + bfa_trc(vf->fcs, vf->vf_id); +} + +/** + * Logout with the virtual fabric. + * + * param[in] vf - pointer to bfa_vf_t. + * + * retval BFA_STATUS_OK On success. + * retval BFA_STATUS_INPROGRESS VF is being stopped. + */ +bfa_status_t +bfa_fcs_vf_stop(bfa_fcs_vf_t *vf) +{ + bfa_trc(vf->fcs, vf->vf_id); + return BFA_STATUS_OK; +} + +/** + * Returns attributes of the given VF. + * + * param[in] vf pointer to bfa_vf_t. + * param[out] vf_attr vf attributes returned + * + * return None + */ +void +bfa_fcs_vf_get_attr(bfa_fcs_vf_t *vf, struct bfa_vf_attr_s *vf_attr) +{ + bfa_trc(vf->fcs, vf->vf_id); +} + +/** + * Return statistics associated with the given vf. + * + * param[in] vf pointer to bfa_vf_t. + * param[out] vf_stats vf statistics returned + * + * @return None + */ +void +bfa_fcs_vf_get_stats(bfa_fcs_vf_t *vf, struct bfa_vf_stats_s *vf_stats) +{ + bfa_os_memcpy(vf_stats, &vf->stats, sizeof(struct bfa_vf_stats_s)); + return; +} + +void +/** + * clear statistics associated with the given vf. + * + * param[in] vf pointer to bfa_vf_t. + * + * @return None + */ +bfa_fcs_vf_clear_stats(bfa_fcs_vf_t *vf) +{ + bfa_os_memset(&vf->stats, 0, sizeof(struct bfa_vf_stats_s)); + return; +} + +/** + * Returns FCS vf structure for a given vf_id. + * + * param[in] vf_id - VF_ID + * + * return + * If lookup succeeds, retuns fcs vf object, otherwise returns NULL + */ +bfa_fcs_vf_t * +bfa_fcs_vf_lookup(struct bfa_fcs_s *fcs, u16 vf_id) +{ + bfa_trc(fcs, vf_id); + if (vf_id == FC_VF_ID_NULL) + return (&fcs->fabric); + + /** + * @todo vf support + */ + + return NULL; +} + +/** + * Returns driver VF structure for a given FCS vf. + * + * param[in] vf - pointer to bfa_vf_t + * + * return Driver VF structure + */ +struct bfad_vf_s * +bfa_fcs_vf_get_drv_vf(bfa_fcs_vf_t *vf) +{ + bfa_assert(vf); + bfa_trc(vf->fcs, vf->vf_id); + return vf->vf_drv; +} + +/** + * Return the list of VFs configured. + * + * param[in] fcs fcs module instance + * param[out] vf_ids returned list of vf_ids + * param[in,out] nvfs in:size of vf_ids array, + * out:total elements present, + * actual elements returned is limited by the size + * + * return Driver VF structure + */ +void +bfa_fcs_vf_list(struct bfa_fcs_s *fcs, u16 *vf_ids, int *nvfs) +{ + bfa_trc(fcs, *nvfs); +} + +/** + * Return the list of all VFs visible from fabric. + * + * param[in] fcs fcs module instance + * param[out] vf_ids returned list of vf_ids + * param[in,out] nvfs in:size of vf_ids array, + * out:total elements present, + * actual elements returned is limited by the size + * + * return Driver VF structure + */ +void +bfa_fcs_vf_list_all(struct bfa_fcs_s *fcs, u16 *vf_ids, int *nvfs) +{ + bfa_trc(fcs, *nvfs); +} + +/** + * Return the list of local logical ports present in the given VF. + * + * param[in] vf vf for which logical ports are returned + * param[out] lpwwn returned logical port wwn list + * param[in,out] nlports in:size of lpwwn list; + * out:total elements present, + * actual elements returned is limited by the size + * + */ +void +bfa_fcs_vf_get_ports(bfa_fcs_vf_t *vf, wwn_t lpwwn[], int *nlports) +{ + struct list_head *qe; + struct bfa_fcs_vport_s *vport; + int i; + struct bfa_fcs_s *fcs; + + if (vf == NULL || lpwwn == NULL || *nlports == 0) + return; + + fcs = vf->fcs; + + bfa_trc(fcs, vf->vf_id); + bfa_trc(fcs, (u32) *nlports); + + i = 0; + lpwwn[i++] = vf->bport.port_cfg.pwwn; + + list_for_each(qe, &vf->vport_q) { + if (i >= *nlports) + break; + + vport = (struct bfa_fcs_vport_s *) qe; + lpwwn[i++] = vport->lport.port_cfg.pwwn; + } + + bfa_trc(fcs, i); + *nlports = i; + return; +} + + diff --git a/drivers/scsi/bfa/vport.c b/drivers/scsi/bfa/vport.c new file mode 100644 index 000000000000..c10af06c5714 --- /dev/null +++ b/drivers/scsi/bfa/vport.c @@ -0,0 +1,891 @@ +/* + * Copyright (c) 2005-2009 Brocade Communications Systems, Inc. + * All rights reserved + * www.brocade.com + * + * Linux driver for Brocade Fibre Channel Host Bus Adapter. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (GPL) Version 2 as + * published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +/** + * bfa_fcs_vport.c FCS virtual port state machine + */ + +#include <bfa.h> +#include <bfa_svc.h> +#include <fcbuild.h> +#include "fcs_fabric.h" +#include "fcs_lport.h" +#include "fcs_vport.h" +#include "fcs_trcmod.h" +#include "fcs.h" +#include <aen/bfa_aen_lport.h> + +BFA_TRC_FILE(FCS, VPORT); + +#define __vport_fcs(__vp) (__vp)->lport.fcs +#define __vport_pwwn(__vp) (__vp)->lport.port_cfg.pwwn +#define __vport_nwwn(__vp) (__vp)->lport.port_cfg.nwwn +#define __vport_bfa(__vp) (__vp)->lport.fcs->bfa +#define __vport_fcid(__vp) (__vp)->lport.pid +#define __vport_fabric(__vp) (__vp)->lport.fabric +#define __vport_vfid(__vp) (__vp)->lport.fabric->vf_id + +#define BFA_FCS_VPORT_MAX_RETRIES 5 +/* + * Forward declarations + */ +static void bfa_fcs_vport_do_fdisc(struct bfa_fcs_vport_s *vport); +static void bfa_fcs_vport_timeout(void *vport_arg); +static void bfa_fcs_vport_do_logo(struct bfa_fcs_vport_s *vport); +static void bfa_fcs_vport_free(struct bfa_fcs_vport_s *vport); + +/** + * fcs_vport_sm FCS virtual port state machine + */ + +/** + * VPort State Machine events + */ +enum bfa_fcs_vport_event { + BFA_FCS_VPORT_SM_CREATE = 1, /* vport create event */ + BFA_FCS_VPORT_SM_DELETE = 2, /* vport delete event */ + BFA_FCS_VPORT_SM_START = 3, /* vport start request */ + BFA_FCS_VPORT_SM_STOP = 4, /* stop: unsupported */ + BFA_FCS_VPORT_SM_ONLINE = 5, /* fabric online */ + BFA_FCS_VPORT_SM_OFFLINE = 6, /* fabric offline event */ + BFA_FCS_VPORT_SM_FRMSENT = 7, /* fdisc/logo sent events */ + BFA_FCS_VPORT_SM_RSP_OK = 8, /* good response */ + BFA_FCS_VPORT_SM_RSP_ERROR = 9, /* error/bad response */ + BFA_FCS_VPORT_SM_TIMEOUT = 10, /* delay timer event */ + BFA_FCS_VPORT_SM_DELCOMP = 11, /* lport delete completion */ + BFA_FCS_VPORT_SM_RSP_DUP_WWN = 12, /* Dup wnn error */ + BFA_FCS_VPORT_SM_RSP_FAILED = 13, /* non-retryable failure */ +}; + +static void bfa_fcs_vport_sm_uninit(struct bfa_fcs_vport_s *vport, + enum bfa_fcs_vport_event event); +static void bfa_fcs_vport_sm_created(struct bfa_fcs_vport_s *vport, + enum bfa_fcs_vport_event event); +static void bfa_fcs_vport_sm_offline(struct bfa_fcs_vport_s *vport, + enum bfa_fcs_vport_event event); +static void bfa_fcs_vport_sm_fdisc(struct bfa_fcs_vport_s *vport, + enum bfa_fcs_vport_event event); +static void bfa_fcs_vport_sm_fdisc_retry(struct bfa_fcs_vport_s *vport, + enum bfa_fcs_vport_event event); +static void bfa_fcs_vport_sm_online(struct bfa_fcs_vport_s *vport, + enum bfa_fcs_vport_event event); +static void bfa_fcs_vport_sm_deleting(struct bfa_fcs_vport_s *vport, + enum bfa_fcs_vport_event event); +static void bfa_fcs_vport_sm_cleanup(struct bfa_fcs_vport_s *vport, + enum bfa_fcs_vport_event event); +static void bfa_fcs_vport_sm_logo(struct bfa_fcs_vport_s *vport, + enum bfa_fcs_vport_event event); +static void bfa_fcs_vport_sm_error(struct bfa_fcs_vport_s *vport, + enum bfa_fcs_vport_event event); + +static struct bfa_sm_table_s vport_sm_table[] = { + {BFA_SM(bfa_fcs_vport_sm_uninit), BFA_FCS_VPORT_UNINIT}, + {BFA_SM(bfa_fcs_vport_sm_created), BFA_FCS_VPORT_CREATED}, + {BFA_SM(bfa_fcs_vport_sm_offline), BFA_FCS_VPORT_OFFLINE}, + {BFA_SM(bfa_fcs_vport_sm_fdisc), BFA_FCS_VPORT_FDISC}, + {BFA_SM(bfa_fcs_vport_sm_fdisc_retry), BFA_FCS_VPORT_FDISC_RETRY}, + {BFA_SM(bfa_fcs_vport_sm_online), BFA_FCS_VPORT_ONLINE}, + {BFA_SM(bfa_fcs_vport_sm_deleting), BFA_FCS_VPORT_DELETING}, + {BFA_SM(bfa_fcs_vport_sm_cleanup), BFA_FCS_VPORT_CLEANUP}, + {BFA_SM(bfa_fcs_vport_sm_logo), BFA_FCS_VPORT_LOGO}, + {BFA_SM(bfa_fcs_vport_sm_error), BFA_FCS_VPORT_ERROR} +}; + +/** + * Beginning state. + */ +static void +bfa_fcs_vport_sm_uninit(struct bfa_fcs_vport_s *vport, + enum bfa_fcs_vport_event event) +{ + bfa_trc(__vport_fcs(vport), __vport_pwwn(vport)); + bfa_trc(__vport_fcs(vport), event); + + switch (event) { + case BFA_FCS_VPORT_SM_CREATE: + bfa_sm_set_state(vport, bfa_fcs_vport_sm_created); + bfa_fcs_fabric_addvport(__vport_fabric(vport), vport); + break; + + default: + bfa_assert(0); + } +} + +/** + * Created state - a start event is required to start up the state machine. + */ +static void +bfa_fcs_vport_sm_created(struct bfa_fcs_vport_s *vport, + enum bfa_fcs_vport_event event) +{ + bfa_trc(__vport_fcs(vport), __vport_pwwn(vport)); + bfa_trc(__vport_fcs(vport), event); + + switch (event) { + case BFA_FCS_VPORT_SM_START: + if (bfa_fcs_fabric_is_online(__vport_fabric(vport)) + && bfa_fcs_fabric_npiv_capable(__vport_fabric(vport))) { + bfa_sm_set_state(vport, bfa_fcs_vport_sm_fdisc); + bfa_fcs_vport_do_fdisc(vport); + } else { + /** + * Fabric is offline or not NPIV capable, stay in + * offline state. + */ + vport->vport_stats.fab_no_npiv++; + bfa_sm_set_state(vport, bfa_fcs_vport_sm_offline); + } + break; + + case BFA_FCS_VPORT_SM_DELETE: + bfa_sm_set_state(vport, bfa_fcs_vport_sm_cleanup); + bfa_fcs_port_delete(&vport->lport); + break; + + case BFA_FCS_VPORT_SM_ONLINE: + case BFA_FCS_VPORT_SM_OFFLINE: + /** + * Ignore ONLINE/OFFLINE events from fabric till vport is started. + */ + break; + + default: + bfa_assert(0); + } +} + +/** + * Offline state - awaiting ONLINE event from fabric SM. + */ +static void +bfa_fcs_vport_sm_offline(struct bfa_fcs_vport_s *vport, + enum bfa_fcs_vport_event event) +{ + bfa_trc(__vport_fcs(vport), __vport_pwwn(vport)); + bfa_trc(__vport_fcs(vport), event); + + switch (event) { + case BFA_FCS_VPORT_SM_DELETE: + bfa_sm_set_state(vport, bfa_fcs_vport_sm_cleanup); + bfa_fcs_port_delete(&vport->lport); + break; + + case BFA_FCS_VPORT_SM_ONLINE: + bfa_sm_set_state(vport, bfa_fcs_vport_sm_fdisc); + vport->fdisc_retries = 0; + bfa_fcs_vport_do_fdisc(vport); + break; + + case BFA_FCS_VPORT_SM_OFFLINE: + /* + * This can happen if the vport couldn't be initialzied due + * the fact that the npiv was not enabled on the switch. In + * that case we will put the vport in offline state. However, + * the link can go down and cause the this event to be sent when + * we are already offline. Ignore it. + */ + break; + + default: + bfa_assert(0); + } +} + +/** + * FDISC is sent and awaiting reply from fabric. + */ +static void +bfa_fcs_vport_sm_fdisc(struct bfa_fcs_vport_s *vport, + enum bfa_fcs_vport_event event) +{ + bfa_trc(__vport_fcs(vport), __vport_pwwn(vport)); + bfa_trc(__vport_fcs(vport), event); + + switch (event) { + case BFA_FCS_VPORT_SM_DELETE: + bfa_sm_set_state(vport, bfa_fcs_vport_sm_logo); + bfa_lps_discard(vport->lps); + bfa_fcs_vport_do_logo(vport); + break; + + case BFA_FCS_VPORT_SM_OFFLINE: + bfa_sm_set_state(vport, bfa_fcs_vport_sm_offline); + bfa_lps_discard(vport->lps); + break; + + case BFA_FCS_VPORT_SM_RSP_OK: + bfa_sm_set_state(vport, bfa_fcs_vport_sm_online); + bfa_fcs_port_online(&vport->lport); + break; + + case BFA_FCS_VPORT_SM_RSP_ERROR: + bfa_sm_set_state(vport, bfa_fcs_vport_sm_fdisc_retry); + bfa_timer_start(__vport_bfa(vport), &vport->timer, + bfa_fcs_vport_timeout, vport, + BFA_FCS_RETRY_TIMEOUT); + break; + + case BFA_FCS_VPORT_SM_RSP_FAILED: + bfa_sm_set_state(vport, bfa_fcs_vport_sm_offline); + break; + + case BFA_FCS_VPORT_SM_RSP_DUP_WWN: + bfa_sm_set_state(vport, bfa_fcs_vport_sm_error); + break; + + default: + bfa_assert(0); + } +} + +/** + * FDISC attempt failed - a timer is active to retry FDISC. + */ +static void +bfa_fcs_vport_sm_fdisc_retry(struct bfa_fcs_vport_s *vport, + enum bfa_fcs_vport_event event) +{ + bfa_trc(__vport_fcs(vport), __vport_pwwn(vport)); + bfa_trc(__vport_fcs(vport), event); + + switch (event) { + case BFA_FCS_VPORT_SM_DELETE: + bfa_sm_set_state(vport, bfa_fcs_vport_sm_cleanup); + bfa_timer_stop(&vport->timer); + bfa_fcs_port_delete(&vport->lport); + break; + + case BFA_FCS_VPORT_SM_OFFLINE: + bfa_sm_set_state(vport, bfa_fcs_vport_sm_offline); + bfa_timer_stop(&vport->timer); + break; + + case BFA_FCS_VPORT_SM_TIMEOUT: + bfa_sm_set_state(vport, bfa_fcs_vport_sm_fdisc); + vport->vport_stats.fdisc_retries++; + vport->fdisc_retries++; + bfa_fcs_vport_do_fdisc(vport); + break; + + default: + bfa_assert(0); + } +} + +/** + * Vport is online (FDISC is complete). + */ +static void +bfa_fcs_vport_sm_online(struct bfa_fcs_vport_s *vport, + enum bfa_fcs_vport_event event) +{ + bfa_trc(__vport_fcs(vport), __vport_pwwn(vport)); + bfa_trc(__vport_fcs(vport), event); + + switch (event) { + case BFA_FCS_VPORT_SM_DELETE: + bfa_sm_set_state(vport, bfa_fcs_vport_sm_deleting); + bfa_fcs_port_delete(&vport->lport); + break; + + case BFA_FCS_VPORT_SM_OFFLINE: + bfa_sm_set_state(vport, bfa_fcs_vport_sm_offline); + bfa_lps_discard(vport->lps); + bfa_fcs_port_offline(&vport->lport); + break; + + default: + bfa_assert(0); + } +} + +/** + * Vport is being deleted - awaiting lport delete completion to send + * LOGO to fabric. + */ +static void +bfa_fcs_vport_sm_deleting(struct bfa_fcs_vport_s *vport, + enum bfa_fcs_vport_event event) +{ + bfa_trc(__vport_fcs(vport), __vport_pwwn(vport)); + bfa_trc(__vport_fcs(vport), event); + + switch (event) { + case BFA_FCS_VPORT_SM_DELETE: + break; + + case BFA_FCS_VPORT_SM_DELCOMP: + bfa_sm_set_state(vport, bfa_fcs_vport_sm_logo); + bfa_fcs_vport_do_logo(vport); + break; + + case BFA_FCS_VPORT_SM_OFFLINE: + bfa_sm_set_state(vport, bfa_fcs_vport_sm_cleanup); + break; + + default: + bfa_assert(0); + } +} + +/** + * Error State. + * This state will be set when the Vport Creation fails due to errors like + * Dup WWN. In this state only operation allowed is a Vport Delete. + */ +static void +bfa_fcs_vport_sm_error(struct bfa_fcs_vport_s *vport, + enum bfa_fcs_vport_event event) +{ + bfa_trc(__vport_fcs(vport), __vport_pwwn(vport)); + bfa_trc(__vport_fcs(vport), event); + + switch (event) { + case BFA_FCS_VPORT_SM_DELETE: + bfa_sm_set_state(vport, bfa_fcs_vport_sm_uninit); + bfa_fcs_vport_free(vport); + break; + + default: + bfa_trc(__vport_fcs(vport), event); + } +} + +/** + * Lport cleanup is in progress since vport is being deleted. Fabric is + * offline, so no LOGO is needed to complete vport deletion. + */ +static void +bfa_fcs_vport_sm_cleanup(struct bfa_fcs_vport_s *vport, + enum bfa_fcs_vport_event event) +{ + bfa_trc(__vport_fcs(vport), __vport_pwwn(vport)); + bfa_trc(__vport_fcs(vport), event); + + switch (event) { + case BFA_FCS_VPORT_SM_DELCOMP: + bfa_sm_set_state(vport, bfa_fcs_vport_sm_uninit); + bfa_fcs_vport_free(vport); + break; + + case BFA_FCS_VPORT_SM_DELETE: + break; + + default: + bfa_assert(0); + } +} + +/** + * LOGO is sent to fabric. Vport delete is in progress. Lport delete cleanup + * is done. + */ +static void +bfa_fcs_vport_sm_logo(struct bfa_fcs_vport_s *vport, + enum bfa_fcs_vport_event event) +{ + bfa_trc(__vport_fcs(vport), __vport_pwwn(vport)); + bfa_trc(__vport_fcs(vport), event); + + switch (event) { + case BFA_FCS_VPORT_SM_OFFLINE: + bfa_lps_discard(vport->lps); + /* + * !!! fall through !!! + */ + + case BFA_FCS_VPORT_SM_RSP_OK: + case BFA_FCS_VPORT_SM_RSP_ERROR: + bfa_sm_set_state(vport, bfa_fcs_vport_sm_uninit); + bfa_fcs_vport_free(vport); + break; + + case BFA_FCS_VPORT_SM_DELETE: + break; + + default: + bfa_assert(0); + } +} + + + +/** + * fcs_vport_private FCS virtual port private functions + */ + +/** + * Send AEN notification + */ +static void +bfa_fcs_vport_aen_post(bfa_fcs_lport_t *port, enum bfa_lport_aen_event event) +{ + union bfa_aen_data_u aen_data; + struct bfa_log_mod_s *logmod = port->fcs->logm; + enum bfa_port_role role = port->port_cfg.roles; + wwn_t lpwwn = bfa_fcs_port_get_pwwn(port); + char lpwwn_ptr[BFA_STRING_32]; + char *role_str[BFA_PORT_ROLE_FCP_MAX / 2 + 1] = + { "Initiator", "Target", "IPFC" }; + + wwn2str(lpwwn_ptr, lpwwn); + + bfa_assert(role <= BFA_PORT_ROLE_FCP_MAX); + + switch (event) { + case BFA_LPORT_AEN_NPIV_DUP_WWN: + bfa_log(logmod, BFA_AEN_LPORT_NPIV_DUP_WWN, lpwwn_ptr, + role_str[role / 2]); + break; + case BFA_LPORT_AEN_NPIV_FABRIC_MAX: + bfa_log(logmod, BFA_AEN_LPORT_NPIV_FABRIC_MAX, lpwwn_ptr, + role_str[role / 2]); + break; + case BFA_LPORT_AEN_NPIV_UNKNOWN: + bfa_log(logmod, BFA_AEN_LPORT_NPIV_UNKNOWN, lpwwn_ptr, + role_str[role / 2]); + break; + default: + break; + } + + aen_data.lport.vf_id = port->fabric->vf_id; + aen_data.lport.roles = role; + aen_data.lport.ppwwn = + bfa_fcs_port_get_pwwn(bfa_fcs_get_base_port(port->fcs)); + aen_data.lport.lpwwn = lpwwn; +} + +/** + * This routine will be called to send a FDISC command. + */ +static void +bfa_fcs_vport_do_fdisc(struct bfa_fcs_vport_s *vport) +{ + bfa_lps_fdisc(vport->lps, vport, + bfa_pport_get_maxfrsize(__vport_bfa(vport)), + __vport_pwwn(vport), __vport_nwwn(vport)); + vport->vport_stats.fdisc_sent++; +} + +static void +bfa_fcs_vport_fdisc_rejected(struct bfa_fcs_vport_s *vport) +{ + u8 lsrjt_rsn = bfa_lps_get_lsrjt_rsn(vport->lps); + u8 lsrjt_expl = bfa_lps_get_lsrjt_expl(vport->lps); + + bfa_trc(__vport_fcs(vport), lsrjt_rsn); + bfa_trc(__vport_fcs(vport), lsrjt_expl); + + /* + * For certain reason codes, we don't want to retry. + */ + switch (bfa_lps_get_lsrjt_expl(vport->lps)) { + case FC_LS_RJT_EXP_INV_PORT_NAME: /* by brocade */ + case FC_LS_RJT_EXP_INVALID_NPORT_ID: /* by Cisco */ + if (vport->fdisc_retries < BFA_FCS_VPORT_MAX_RETRIES) + bfa_sm_send_event(vport, BFA_FCS_VPORT_SM_RSP_ERROR); + else { + bfa_fcs_vport_aen_post(&vport->lport, + BFA_LPORT_AEN_NPIV_DUP_WWN); + bfa_sm_send_event(vport, BFA_FCS_VPORT_SM_RSP_DUP_WWN); + } + break; + + case FC_LS_RJT_EXP_INSUFF_RES: + /* + * This means max logins per port/switch setting on the + * switch was exceeded. + */ + if (vport->fdisc_retries < BFA_FCS_VPORT_MAX_RETRIES) + bfa_sm_send_event(vport, BFA_FCS_VPORT_SM_RSP_ERROR); + else { + bfa_fcs_vport_aen_post(&vport->lport, + BFA_LPORT_AEN_NPIV_FABRIC_MAX); + bfa_sm_send_event(vport, BFA_FCS_VPORT_SM_RSP_FAILED); + } + break; + + default: + if (vport->fdisc_retries == 0) /* Print only once */ + bfa_fcs_vport_aen_post(&vport->lport, + BFA_LPORT_AEN_NPIV_UNKNOWN); + bfa_sm_send_event(vport, BFA_FCS_VPORT_SM_RSP_ERROR); + } +} + +/** + * Called to send a logout to the fabric. Used when a V-Port is + * deleted/stopped. + */ +static void +bfa_fcs_vport_do_logo(struct bfa_fcs_vport_s *vport) +{ + bfa_trc(__vport_fcs(vport), __vport_pwwn(vport)); + + vport->vport_stats.logo_sent++; + bfa_lps_fdisclogo(vport->lps); +} + +/** + * This routine will be called by bfa_timer on timer timeouts. + * + * param[in] vport - pointer to bfa_fcs_vport_t. + * param[out] vport_status - pointer to return vport status in + * + * return + * void + * +* Special Considerations: + * + * note + */ +static void +bfa_fcs_vport_timeout(void *vport_arg) +{ + struct bfa_fcs_vport_s *vport = (struct bfa_fcs_vport_s *)vport_arg; + + vport->vport_stats.fdisc_timeouts++; + bfa_sm_send_event(vport, BFA_FCS_VPORT_SM_TIMEOUT); +} + +static void +bfa_fcs_vport_free(struct bfa_fcs_vport_s *vport) +{ + bfa_fcs_fabric_delvport(__vport_fabric(vport), vport); + bfa_fcb_vport_delete(vport->vport_drv); + bfa_lps_delete(vport->lps); +} + + + +/** + * fcs_vport_public FCS virtual port public interfaces + */ + +/** + * Online notification from fabric SM. + */ +void +bfa_fcs_vport_online(struct bfa_fcs_vport_s *vport) +{ + vport->vport_stats.fab_online++; + bfa_sm_send_event(vport, BFA_FCS_VPORT_SM_ONLINE); +} + +/** + * Offline notification from fabric SM. + */ +void +bfa_fcs_vport_offline(struct bfa_fcs_vport_s *vport) +{ + vport->vport_stats.fab_offline++; + bfa_sm_send_event(vport, BFA_FCS_VPORT_SM_OFFLINE); +} + +/** + * Cleanup notification from fabric SM on link timer expiry. + */ +void +bfa_fcs_vport_cleanup(struct bfa_fcs_vport_s *vport) +{ + vport->vport_stats.fab_cleanup++; +} + +/** + * Delete completion callback from associated lport + */ +void +bfa_fcs_vport_delete_comp(struct bfa_fcs_vport_s *vport) +{ + bfa_sm_send_event(vport, BFA_FCS_VPORT_SM_DELCOMP); +} + +/** + * Module initialization + */ +void +bfa_fcs_vport_modinit(struct bfa_fcs_s *fcs) +{ +} + +/** + * Module cleanup + */ +void +bfa_fcs_vport_modexit(struct bfa_fcs_s *fcs) +{ + bfa_fcs_modexit_comp(fcs); +} + +u32 +bfa_fcs_vport_get_max(struct bfa_fcs_s *fcs) +{ + struct bfa_ioc_attr_s ioc_attr; + + bfa_get_attr(fcs->bfa, &ioc_attr); + + if (ioc_attr.pci_attr.device_id == BFA_PCI_DEVICE_ID_CT) + return (BFA_FCS_MAX_VPORTS_SUPP_CT); + else + return (BFA_FCS_MAX_VPORTS_SUPP_CB); +} + + + +/** + * fcs_vport_api Virtual port API + */ + +/** + * Use this function to instantiate a new FCS vport object. This + * function will not trigger any HW initialization process (which will be + * done in vport_start() call) + * + * param[in] vport - pointer to bfa_fcs_vport_t. This space + * needs to be allocated by the driver. + * param[in] fcs - FCS instance + * param[in] vport_cfg - vport configuration + * param[in] vf_id - VF_ID if vport is created within a VF. + * FC_VF_ID_NULL to specify base fabric. + * param[in] vport_drv - Opaque handle back to the driver's vport + * structure + * + * retval BFA_STATUS_OK - on success. + * retval BFA_STATUS_FAILED - on failure. + */ +bfa_status_t +bfa_fcs_vport_create(struct bfa_fcs_vport_s *vport, struct bfa_fcs_s *fcs, + u16 vf_id, struct bfa_port_cfg_s *vport_cfg, + struct bfad_vport_s *vport_drv) +{ + if (vport_cfg->pwwn == 0) + return (BFA_STATUS_INVALID_WWN); + + if (bfa_fcs_port_get_pwwn(&fcs->fabric.bport) == vport_cfg->pwwn) + return BFA_STATUS_VPORT_WWN_BP; + + if (bfa_fcs_vport_lookup(fcs, vf_id, vport_cfg->pwwn) != NULL) + return BFA_STATUS_VPORT_EXISTS; + + if (bfa_fcs_fabric_vport_count(&fcs->fabric) == + bfa_fcs_vport_get_max(fcs)) + return BFA_STATUS_VPORT_MAX; + + vport->lps = bfa_lps_alloc(fcs->bfa); + if (!vport->lps) + return BFA_STATUS_VPORT_MAX; + + vport->vport_drv = vport_drv; + bfa_sm_set_state(vport, bfa_fcs_vport_sm_uninit); + + bfa_fcs_lport_init(&vport->lport, fcs, vf_id, vport_cfg, vport); + + bfa_sm_send_event(vport, BFA_FCS_VPORT_SM_CREATE); + + return BFA_STATUS_OK; +} + +/** + * Use this function initialize the vport. + * + * @param[in] vport - pointer to bfa_fcs_vport_t. + * + * @returns None + */ +bfa_status_t +bfa_fcs_vport_start(struct bfa_fcs_vport_s *vport) +{ + bfa_sm_send_event(vport, BFA_FCS_VPORT_SM_START); + + return BFA_STATUS_OK; +} + +/** + * Use this function quiese the vport object. This function will return + * immediately, when the vport is actually stopped, the + * bfa_drv_vport_stop_cb() will be called. + * + * param[in] vport - pointer to bfa_fcs_vport_t. + * + * return None + */ +bfa_status_t +bfa_fcs_vport_stop(struct bfa_fcs_vport_s *vport) +{ + bfa_sm_send_event(vport, BFA_FCS_VPORT_SM_STOP); + + return BFA_STATUS_OK; +} + +/** + * Use this function to delete a vport object. Fabric object should + * be stopped before this function call. + * + * param[in] vport - pointer to bfa_fcs_vport_t. + * + * return None + */ +bfa_status_t +bfa_fcs_vport_delete(struct bfa_fcs_vport_s *vport) +{ + bfa_sm_send_event(vport, BFA_FCS_VPORT_SM_DELETE); + + return BFA_STATUS_OK; +} + +/** + * Use this function to get vport's current status info. + * + * param[in] vport pointer to bfa_fcs_vport_t. + * param[out] attr pointer to return vport attributes + * + * return None + */ +void +bfa_fcs_vport_get_attr(struct bfa_fcs_vport_s *vport, + struct bfa_vport_attr_s *attr) +{ + if (vport == NULL || attr == NULL) + return; + + bfa_os_memset(attr, 0, sizeof(struct bfa_vport_attr_s)); + + bfa_fcs_port_get_attr(&vport->lport, &attr->port_attr); + attr->vport_state = bfa_sm_to_state(vport_sm_table, vport->sm); +} + +/** + * Use this function to get vport's statistics. + * + * param[in] vport pointer to bfa_fcs_vport_t. + * param[out] stats pointer to return vport statistics in + * + * return None + */ +void +bfa_fcs_vport_get_stats(struct bfa_fcs_vport_s *vport, + struct bfa_vport_stats_s *stats) +{ + *stats = vport->vport_stats; +} + +/** + * Use this function to clear vport's statistics. + * + * param[in] vport pointer to bfa_fcs_vport_t. + * + * return None + */ +void +bfa_fcs_vport_clr_stats(struct bfa_fcs_vport_s *vport) +{ + bfa_os_memset(&vport->vport_stats, 0, sizeof(struct bfa_vport_stats_s)); +} + +/** + * Lookup a virtual port. Excludes base port from lookup. + */ +struct bfa_fcs_vport_s * +bfa_fcs_vport_lookup(struct bfa_fcs_s *fcs, u16 vf_id, wwn_t vpwwn) +{ + struct bfa_fcs_vport_s *vport; + struct bfa_fcs_fabric_s *fabric; + + bfa_trc(fcs, vf_id); + bfa_trc(fcs, vpwwn); + + fabric = bfa_fcs_vf_lookup(fcs, vf_id); + if (!fabric) { + bfa_trc(fcs, vf_id); + return NULL; + } + + vport = bfa_fcs_fabric_vport_lookup(fabric, vpwwn); + return vport; +} + +/** + * FDISC Response + */ +void +bfa_cb_lps_fdisc_comp(void *bfad, void *uarg, bfa_status_t status) +{ + struct bfa_fcs_vport_s *vport = uarg; + + bfa_trc(__vport_fcs(vport), __vport_pwwn(vport)); + bfa_trc(__vport_fcs(vport), status); + + switch (status) { + case BFA_STATUS_OK: + /* + * Initialiaze the V-Port fields + */ + __vport_fcid(vport) = bfa_lps_get_pid(vport->lps); + vport->vport_stats.fdisc_accepts++; + bfa_sm_send_event(vport, BFA_FCS_VPORT_SM_RSP_OK); + break; + + case BFA_STATUS_INVALID_MAC: + /* + * Only for CNA + */ + vport->vport_stats.fdisc_acc_bad++; + bfa_sm_send_event(vport, BFA_FCS_VPORT_SM_RSP_ERROR); + + break; + + case BFA_STATUS_EPROTOCOL: + switch (bfa_lps_get_extstatus(vport->lps)) { + case BFA_EPROTO_BAD_ACCEPT: + vport->vport_stats.fdisc_acc_bad++; + break; + + case BFA_EPROTO_UNKNOWN_RSP: + vport->vport_stats.fdisc_unknown_rsp++; + break; + + default: + break; + } + + bfa_sm_send_event(vport, BFA_FCS_VPORT_SM_RSP_ERROR); + break; + + case BFA_STATUS_FABRIC_RJT: + vport->vport_stats.fdisc_rejects++; + bfa_fcs_vport_fdisc_rejected(vport); + break; + + default: + vport->vport_stats.fdisc_rsp_err++; + bfa_sm_send_event(vport, BFA_FCS_VPORT_SM_RSP_ERROR); + } +} + +/** + * LOGO response + */ +void +bfa_cb_lps_fdisclogo_comp(void *bfad, void *uarg) +{ + struct bfa_fcs_vport_s *vport = uarg; + bfa_sm_send_event(vport, BFA_FCS_VPORT_SM_RSP_OK); +} + + diff --git a/drivers/scsi/bnx2i/bnx2i.h b/drivers/scsi/bnx2i/bnx2i.h index d7576f28c6e9..5edde1a8c04d 100644 --- a/drivers/scsi/bnx2i/bnx2i.h +++ b/drivers/scsi/bnx2i/bnx2i.h @@ -100,6 +100,8 @@ #define CTX_OFFSET 0x10000 #define MAX_CID_CNT 0x4000 +#define BNX2I_570X_PAGE_SIZE_DEFAULT 4096 + /* 5709 context registers */ #define BNX2_MQ_CONFIG2 0x00003d00 #define BNX2_MQ_CONFIG2_CONT_SZ (0x7L<<4) diff --git a/drivers/scsi/bnx2i/bnx2i_hwi.c b/drivers/scsi/bnx2i/bnx2i_hwi.c index 41e1b0e7e2ef..5c8d7630c13e 100644 --- a/drivers/scsi/bnx2i/bnx2i_hwi.c +++ b/drivers/scsi/bnx2i/bnx2i_hwi.c @@ -2386,7 +2386,7 @@ int bnx2i_map_ep_dbell_regs(struct bnx2i_endpoint *ep) ctx_sz = (config2 & BNX2_MQ_CONFIG2_CONT_SZ) >> 3; if (ctx_sz) reg_off = CTX_OFFSET + MAX_CID_CNT * MB_KERNEL_CTX_SIZE - + PAGE_SIZE * + + BNX2I_570X_PAGE_SIZE_DEFAULT * (((cid_num - first_l4l5) / ctx_sz) + 256); else reg_off = CTX_OFFSET + (MB_KERNEL_CTX_SIZE * cid_num); diff --git a/drivers/scsi/bnx2i/bnx2i_iscsi.c b/drivers/scsi/bnx2i/bnx2i_iscsi.c index 9a7ba71f1af4..cafb888c2376 100644 --- a/drivers/scsi/bnx2i/bnx2i_iscsi.c +++ b/drivers/scsi/bnx2i/bnx2i_iscsi.c @@ -1243,7 +1243,7 @@ bnx2i_session_create(struct iscsi_endpoint *ep, cmds_max = BNX2I_SQ_WQES_MIN; cls_session = iscsi_session_setup(&bnx2i_iscsi_transport, shost, - cmds_max, sizeof(struct bnx2i_cmd), + cmds_max, 0, sizeof(struct bnx2i_cmd), initial_cmdsn, ISCSI_MAX_TARGET); if (!cls_session) return NULL; diff --git a/drivers/scsi/cxgb3i/cxgb3i_iscsi.c b/drivers/scsi/cxgb3i/cxgb3i_iscsi.c index c399f485aa7d..2631bddd255e 100644 --- a/drivers/scsi/cxgb3i/cxgb3i_iscsi.c +++ b/drivers/scsi/cxgb3i/cxgb3i_iscsi.c @@ -422,7 +422,7 @@ cxgb3i_session_create(struct iscsi_endpoint *ep, u16 cmds_max, u16 qdepth, BUG_ON(hba != iscsi_host_priv(shost)); cls_session = iscsi_session_setup(&cxgb3i_iscsi_transport, shost, - cmds_max, + cmds_max, 0, sizeof(struct iscsi_tcp_task) + sizeof(struct cxgb3i_task_data), initial_cmdsn, ISCSI_MAX_TARGET); diff --git a/drivers/scsi/device_handler/scsi_dh_rdac.c b/drivers/scsi/device_handler/scsi_dh_rdac.c index 11c89311427e..268189d31d9c 100644 --- a/drivers/scsi/device_handler/scsi_dh_rdac.c +++ b/drivers/scsi/device_handler/scsi_dh_rdac.c @@ -500,8 +500,6 @@ static int mode_select_handle_sense(struct scsi_device *sdev, if (!ret) goto done; - err = SCSI_DH_OK; - switch (sense_hdr.sense_key) { case NO_SENSE: case ABORTED_COMMAND: diff --git a/drivers/scsi/dpt_i2o.c b/drivers/scsi/dpt_i2o.c index b6af63ca980b..496764349c41 100644 --- a/drivers/scsi/dpt_i2o.c +++ b/drivers/scsi/dpt_i2o.c @@ -1918,6 +1918,10 @@ static int adpt_i2o_passthru(adpt_hba* pHba, u32 __user *arg) } size = size>>16; size *= 4; + if (size > MAX_MESSAGE_SIZE) { + rcode = -EINVAL; + goto cleanup; + } /* Copy in the user's I2O command */ if (copy_from_user (msg, user_msg, size)) { rcode = -EFAULT; diff --git a/drivers/scsi/gdth.c b/drivers/scsi/gdth.c index 185e6bc4dd40..9e8fce0f0c1b 100644 --- a/drivers/scsi/gdth.c +++ b/drivers/scsi/gdth.c @@ -2900,7 +2900,7 @@ static int gdth_read_event(gdth_ha_str *ha, int handle, gdth_evt_str *estr) eindex = handle; estr->event_source = 0; - if (eindex >= MAX_EVENTS) { + if (eindex < 0 || eindex >= MAX_EVENTS) { spin_unlock_irqrestore(&ha->smp_lock, flags); return eindex; } diff --git a/drivers/scsi/hptiop.c b/drivers/scsi/hptiop.c index c596ab5f05c3..a0e7e711ff9d 100644 --- a/drivers/scsi/hptiop.c +++ b/drivers/scsi/hptiop.c @@ -1,6 +1,6 @@ /* * HighPoint RR3xxx/4xxx controller driver for Linux - * Copyright (C) 2006-2007 HighPoint Technologies, Inc. All Rights Reserved. + * Copyright (C) 2006-2009 HighPoint Technologies, Inc. All Rights Reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -41,7 +41,7 @@ MODULE_DESCRIPTION("HighPoint RocketRAID 3xxx/4xxx Controller Driver"); static char driver_name[] = "hptiop"; static const char driver_name_long[] = "RocketRAID 3xxx/4xxx Controller driver"; -static const char driver_ver[] = "v1.3 (071203)"; +static const char driver_ver[] = "v1.6 (090910)"; static int iop_send_sync_msg(struct hptiop_hba *hba, u32 msg, u32 millisec); static void hptiop_finish_scsi_req(struct hptiop_hba *hba, u32 tag, @@ -115,9 +115,13 @@ static void hptiop_drain_outbound_queue_itl(struct hptiop_hba *hba) static int iop_intr_itl(struct hptiop_hba *hba) { struct hpt_iopmu_itl __iomem *iop = hba->u.itl.iop; + void __iomem *plx = hba->u.itl.plx; u32 status; int ret = 0; + if (plx && readl(plx + 0x11C5C) & 0xf) + writel(1, plx + 0x11C60); + status = readl(&iop->outbound_intstatus); if (status & IOPMU_OUTBOUND_INT_MSG0) { @@ -460,15 +464,25 @@ static void __iomem *hptiop_map_pci_bar(struct hptiop_hba *hba, int index) static int hptiop_map_pci_bar_itl(struct hptiop_hba *hba) { + struct pci_dev *pcidev = hba->pcidev; hba->u.itl.iop = hptiop_map_pci_bar(hba, 0); - if (hba->u.itl.iop) - return 0; - else + if (hba->u.itl.iop == NULL) return -1; + if ((pcidev->device & 0xff00) == 0x4400) { + hba->u.itl.plx = hba->u.itl.iop; + hba->u.itl.iop = hptiop_map_pci_bar(hba, 2); + if (hba->u.itl.iop == NULL) { + iounmap(hba->u.itl.plx); + return -1; + } + } + return 0; } static void hptiop_unmap_pci_bar_itl(struct hptiop_hba *hba) { + if (hba->u.itl.plx) + iounmap(hba->u.itl.plx); iounmap(hba->u.itl.iop); } @@ -1239,22 +1253,23 @@ static struct hptiop_adapter_ops hptiop_mv_ops = { static struct pci_device_id hptiop_id_table[] = { { PCI_VDEVICE(TTI, 0x3220), (kernel_ulong_t)&hptiop_itl_ops }, { PCI_VDEVICE(TTI, 0x3320), (kernel_ulong_t)&hptiop_itl_ops }, - { PCI_VDEVICE(TTI, 0x3520), (kernel_ulong_t)&hptiop_itl_ops }, - { PCI_VDEVICE(TTI, 0x4320), (kernel_ulong_t)&hptiop_itl_ops }, + { PCI_VDEVICE(TTI, 0x3410), (kernel_ulong_t)&hptiop_itl_ops }, { PCI_VDEVICE(TTI, 0x3510), (kernel_ulong_t)&hptiop_itl_ops }, { PCI_VDEVICE(TTI, 0x3511), (kernel_ulong_t)&hptiop_itl_ops }, + { PCI_VDEVICE(TTI, 0x3520), (kernel_ulong_t)&hptiop_itl_ops }, { PCI_VDEVICE(TTI, 0x3521), (kernel_ulong_t)&hptiop_itl_ops }, { PCI_VDEVICE(TTI, 0x3522), (kernel_ulong_t)&hptiop_itl_ops }, - { PCI_VDEVICE(TTI, 0x3410), (kernel_ulong_t)&hptiop_itl_ops }, - { PCI_VDEVICE(TTI, 0x3540), (kernel_ulong_t)&hptiop_itl_ops }, { PCI_VDEVICE(TTI, 0x3530), (kernel_ulong_t)&hptiop_itl_ops }, + { PCI_VDEVICE(TTI, 0x3540), (kernel_ulong_t)&hptiop_itl_ops }, { PCI_VDEVICE(TTI, 0x3560), (kernel_ulong_t)&hptiop_itl_ops }, - { PCI_VDEVICE(TTI, 0x4322), (kernel_ulong_t)&hptiop_itl_ops }, - { PCI_VDEVICE(TTI, 0x4321), (kernel_ulong_t)&hptiop_itl_ops }, { PCI_VDEVICE(TTI, 0x4210), (kernel_ulong_t)&hptiop_itl_ops }, { PCI_VDEVICE(TTI, 0x4211), (kernel_ulong_t)&hptiop_itl_ops }, { PCI_VDEVICE(TTI, 0x4310), (kernel_ulong_t)&hptiop_itl_ops }, { PCI_VDEVICE(TTI, 0x4311), (kernel_ulong_t)&hptiop_itl_ops }, + { PCI_VDEVICE(TTI, 0x4320), (kernel_ulong_t)&hptiop_itl_ops }, + { PCI_VDEVICE(TTI, 0x4321), (kernel_ulong_t)&hptiop_itl_ops }, + { PCI_VDEVICE(TTI, 0x4322), (kernel_ulong_t)&hptiop_itl_ops }, + { PCI_VDEVICE(TTI, 0x4400), (kernel_ulong_t)&hptiop_itl_ops }, { PCI_VDEVICE(TTI, 0x3120), (kernel_ulong_t)&hptiop_mv_ops }, { PCI_VDEVICE(TTI, 0x3122), (kernel_ulong_t)&hptiop_mv_ops }, { PCI_VDEVICE(TTI, 0x3020), (kernel_ulong_t)&hptiop_mv_ops }, diff --git a/drivers/scsi/hptiop.h b/drivers/scsi/hptiop.h index a0289f219752..0b871c0ae568 100644 --- a/drivers/scsi/hptiop.h +++ b/drivers/scsi/hptiop.h @@ -1,6 +1,6 @@ /* * HighPoint RR3xxx/4xxx controller driver for Linux - * Copyright (C) 2006-2007 HighPoint Technologies, Inc. All Rights Reserved. + * Copyright (C) 2006-2009 HighPoint Technologies, Inc. All Rights Reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -228,6 +228,7 @@ struct hptiop_hba { union { struct { struct hpt_iopmu_itl __iomem *iop; + void __iomem *plx; } itl; struct { struct hpt_iopmv_regs *regs; diff --git a/drivers/scsi/ipr.c b/drivers/scsi/ipr.c index 5f045505a1f4..76d294fc7846 100644 --- a/drivers/scsi/ipr.c +++ b/drivers/scsi/ipr.c @@ -4189,6 +4189,25 @@ static irqreturn_t ipr_handle_other_interrupt(struct ipr_ioa_cfg *ioa_cfg, } /** + * ipr_isr_eh - Interrupt service routine error handler + * @ioa_cfg: ioa config struct + * @msg: message to log + * + * Return value: + * none + **/ +static void ipr_isr_eh(struct ipr_ioa_cfg *ioa_cfg, char *msg) +{ + ioa_cfg->errors_logged++; + dev_err(&ioa_cfg->pdev->dev, "%s\n", msg); + + if (WAIT_FOR_DUMP == ioa_cfg->sdt_state) + ioa_cfg->sdt_state = GET_DUMP; + + ipr_initiate_ioa_reset(ioa_cfg, IPR_SHUTDOWN_NONE); +} + +/** * ipr_isr - Interrupt service routine * @irq: irq number * @devp: pointer to ioa config struct @@ -4203,6 +4222,7 @@ static irqreturn_t ipr_isr(int irq, void *devp) volatile u32 int_reg, int_mask_reg; u32 ioasc; u16 cmd_index; + int num_hrrq = 0; struct ipr_cmnd *ipr_cmd; irqreturn_t rc = IRQ_NONE; @@ -4233,13 +4253,7 @@ static irqreturn_t ipr_isr(int irq, void *devp) IPR_HRRQ_REQ_RESP_HANDLE_MASK) >> IPR_HRRQ_REQ_RESP_HANDLE_SHIFT; if (unlikely(cmd_index >= IPR_NUM_CMD_BLKS)) { - ioa_cfg->errors_logged++; - dev_err(&ioa_cfg->pdev->dev, "Invalid response handle from IOA\n"); - - if (WAIT_FOR_DUMP == ioa_cfg->sdt_state) - ioa_cfg->sdt_state = GET_DUMP; - - ipr_initiate_ioa_reset(ioa_cfg, IPR_SHUTDOWN_NONE); + ipr_isr_eh(ioa_cfg, "Invalid response handle from IOA"); spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags); return IRQ_HANDLED; } @@ -4266,8 +4280,18 @@ static irqreturn_t ipr_isr(int irq, void *devp) if (ipr_cmd != NULL) { /* Clear the PCI interrupt */ - writel(IPR_PCII_HRRQ_UPDATED, ioa_cfg->regs.clr_interrupt_reg); - int_reg = readl(ioa_cfg->regs.sense_interrupt_reg) & ~int_mask_reg; + do { + writel(IPR_PCII_HRRQ_UPDATED, ioa_cfg->regs.clr_interrupt_reg); + int_reg = readl(ioa_cfg->regs.sense_interrupt_reg) & ~int_mask_reg; + } while (int_reg & IPR_PCII_HRRQ_UPDATED && + num_hrrq++ < IPR_MAX_HRRQ_RETRIES); + + if (int_reg & IPR_PCII_HRRQ_UPDATED) { + ipr_isr_eh(ioa_cfg, "Error clearing HRRQ"); + spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags); + return IRQ_HANDLED; + } + } else break; } diff --git a/drivers/scsi/ipr.h b/drivers/scsi/ipr.h index 163245a1c3e5..19bbcf39f0c9 100644 --- a/drivers/scsi/ipr.h +++ b/drivers/scsi/ipr.h @@ -144,6 +144,7 @@ #define IPR_IOA_MAX_SECTORS 32767 #define IPR_VSET_MAX_SECTORS 512 #define IPR_MAX_CDB_LEN 16 +#define IPR_MAX_HRRQ_RETRIES 3 #define IPR_DEFAULT_BUS_WIDTH 16 #define IPR_80MBs_SCSI_RATE ((80 * 10) / (IPR_DEFAULT_BUS_WIDTH / 8)) diff --git a/drivers/scsi/iscsi_tcp.c b/drivers/scsi/iscsi_tcp.c index 2b1b834a098b..edc49ca49cea 100644 --- a/drivers/scsi/iscsi_tcp.c +++ b/drivers/scsi/iscsi_tcp.c @@ -811,7 +811,7 @@ iscsi_sw_tcp_session_create(struct iscsi_endpoint *ep, uint16_t cmds_max, goto free_host; cls_session = iscsi_session_setup(&iscsi_sw_tcp_transport, shost, - cmds_max, + cmds_max, 0, sizeof(struct iscsi_tcp_task) + sizeof(struct iscsi_sw_tcp_hdrbuf), initial_cmdsn, 0); diff --git a/drivers/scsi/libiscsi.c b/drivers/scsi/libiscsi.c index 8dc73c489a17..f1a4246f890c 100644 --- a/drivers/scsi/libiscsi.c +++ b/drivers/scsi/libiscsi.c @@ -2436,7 +2436,7 @@ static void iscsi_host_dec_session_cnt(struct Scsi_Host *shost) */ struct iscsi_cls_session * iscsi_session_setup(struct iscsi_transport *iscsit, struct Scsi_Host *shost, - uint16_t cmds_max, int cmd_task_size, + uint16_t cmds_max, int dd_size, int cmd_task_size, uint32_t initial_cmdsn, unsigned int id) { struct iscsi_host *ihost = shost_priv(shost); @@ -2486,7 +2486,8 @@ iscsi_session_setup(struct iscsi_transport *iscsit, struct Scsi_Host *shost, scsi_cmds = total_cmds - ISCSI_MGMT_CMDS_MAX; cls_session = iscsi_alloc_session(shost, iscsit, - sizeof(struct iscsi_session)); + sizeof(struct iscsi_session) + + dd_size); if (!cls_session) goto dec_session_count; session = cls_session->dd_data; @@ -2503,6 +2504,7 @@ iscsi_session_setup(struct iscsi_transport *iscsit, struct Scsi_Host *shost, session->max_cmdsn = initial_cmdsn + 1; session->max_r2t = 1; session->tt = iscsit; + session->dd_data = cls_session->dd_data + sizeof(*session); mutex_init(&session->eh_mutex); spin_lock_init(&session->lock); diff --git a/drivers/scsi/libsas/sas_expander.c b/drivers/scsi/libsas/sas_expander.c index b3381959acce..33cf988c8c8a 100644 --- a/drivers/scsi/libsas/sas_expander.c +++ b/drivers/scsi/libsas/sas_expander.c @@ -960,7 +960,6 @@ static int sas_ex_discover_dev(struct domain_device *dev, int phy_id) } } - res = 0; } return res; diff --git a/drivers/scsi/lpfc/lpfc_scsi.c b/drivers/scsi/lpfc/lpfc_scsi.c index 61d089703806..c88f59f0ce30 100644 --- a/drivers/scsi/lpfc/lpfc_scsi.c +++ b/drivers/scsi/lpfc/lpfc_scsi.c @@ -56,8 +56,6 @@ static char *dif_op_str[] = { "SCSI_PROT_WRITE_INSERT", "SCSI_PROT_READ_PASS", "SCSI_PROT_WRITE_PASS", - "SCSI_PROT_READ_CONVERT", - "SCSI_PROT_WRITE_CONVERT" }; static void lpfc_release_scsi_buf_s4(struct lpfc_hba *phba, struct lpfc_scsi_buf *psb); @@ -1131,13 +1129,11 @@ lpfc_sc_to_sli_prof(struct scsi_cmnd *sc) ret_prof = LPFC_PROF_A1; break; - case SCSI_PROT_READ_CONVERT: - case SCSI_PROT_WRITE_CONVERT: + case SCSI_PROT_READ_PASS: + case SCSI_PROT_WRITE_PASS: ret_prof = LPFC_PROF_AST1; break; - case SCSI_PROT_READ_PASS: - case SCSI_PROT_WRITE_PASS: case SCSI_PROT_NORMAL: default: printk(KERN_ERR "Bad op/guard:%d/%d combination\n", @@ -1157,8 +1153,6 @@ lpfc_sc_to_sli_prof(struct scsi_cmnd *sc) ret_prof = LPFC_PROF_C1; break; - case SCSI_PROT_READ_CONVERT: - case SCSI_PROT_WRITE_CONVERT: case SCSI_PROT_READ_INSERT: case SCSI_PROT_WRITE_STRIP: case SCSI_PROT_NORMAL: @@ -1209,8 +1203,7 @@ lpfc_get_cmd_dif_parms(struct scsi_cmnd *sc, uint16_t *apptagmask, static int cnt; if (protcnt && (op == SCSI_PROT_WRITE_STRIP || - op == SCSI_PROT_WRITE_PASS || - op == SCSI_PROT_WRITE_CONVERT)) { + op == SCSI_PROT_WRITE_PASS)) { cnt++; spt = page_address(sg_page(scsi_prot_sglist(sc))) + @@ -1501,8 +1494,6 @@ lpfc_prot_group_type(struct lpfc_hba *phba, struct scsi_cmnd *sc) case SCSI_PROT_WRITE_STRIP: case SCSI_PROT_READ_PASS: case SCSI_PROT_WRITE_PASS: - case SCSI_PROT_WRITE_CONVERT: - case SCSI_PROT_READ_CONVERT: ret = LPFC_PG_TYPE_DIF_BUF; break; default: diff --git a/drivers/scsi/mpt2sas/Kconfig b/drivers/scsi/mpt2sas/Kconfig index 4a86855c23b3..70c4c2467dd8 100644 --- a/drivers/scsi/mpt2sas/Kconfig +++ b/drivers/scsi/mpt2sas/Kconfig @@ -2,7 +2,7 @@ # Kernel configuration file for the MPT2SAS # # This code is based on drivers/scsi/mpt2sas/Kconfig -# Copyright (C) 2007-2008 LSI Corporation +# Copyright (C) 2007-2009 LSI Corporation # (mailto:DL-MPTFusionLinux@lsi.com) # This program is free software; you can redistribute it and/or diff --git a/drivers/scsi/mpt2sas/mpi/mpi2.h b/drivers/scsi/mpt2sas/mpi/mpi2.h index 7bb2ece8b2e4..f9f6c0839276 100644 --- a/drivers/scsi/mpt2sas/mpi/mpi2.h +++ b/drivers/scsi/mpt2sas/mpi/mpi2.h @@ -8,7 +8,7 @@ * scatter/gather formats. * Creation Date: June 21, 2006 * - * mpi2.h Version: 02.00.11 + * mpi2.h Version: 02.00.12 * * Version History * --------------- @@ -45,6 +45,13 @@ * 10-02-08 02.00.10 Bumped MPI2_HEADER_VERSION_UNIT. * Moved LUN field defines from mpi2_init.h. * 01-19-09 02.00.11 Bumped MPI2_HEADER_VERSION_UNIT. + * 05-06-09 02.00.12 Bumped MPI2_HEADER_VERSION_UNIT. + * In all request and reply descriptors, replaced VF_ID + * field with MSIxIndex field. + * Removed DevHandle field from + * MPI2_SCSI_IO_SUCCESS_REPLY_DESCRIPTOR and made those + * bytes reserved. + * Added RAID Accelerator functionality. * -------------------------------------------------------------------------- */ @@ -70,7 +77,7 @@ #define MPI2_VERSION_02_00 (0x0200) /* versioning for this MPI header set */ -#define MPI2_HEADER_VERSION_UNIT (0x0B) +#define MPI2_HEADER_VERSION_UNIT (0x0C) #define MPI2_HEADER_VERSION_DEV (0x00) #define MPI2_HEADER_VERSION_UNIT_MASK (0xFF00) #define MPI2_HEADER_VERSION_UNIT_SHIFT (8) @@ -257,7 +264,7 @@ typedef volatile struct _MPI2_SYSTEM_INTERFACE_REGS typedef struct _MPI2_DEFAULT_REQUEST_DESCRIPTOR { U8 RequestFlags; /* 0x00 */ - U8 VF_ID; /* 0x01 */ + U8 MSIxIndex; /* 0x01 */ U16 SMID; /* 0x02 */ U16 LMID; /* 0x04 */ U16 DescriptorTypeDependent; /* 0x06 */ @@ -271,6 +278,7 @@ typedef struct _MPI2_DEFAULT_REQUEST_DESCRIPTOR #define MPI2_REQ_DESCRIPT_FLAGS_SCSI_TARGET (0x02) #define MPI2_REQ_DESCRIPT_FLAGS_HIGH_PRIORITY (0x06) #define MPI2_REQ_DESCRIPT_FLAGS_DEFAULT_TYPE (0x08) +#define MPI2_REQ_DESCRIPT_FLAGS_RAID_ACCELERATOR (0x0A) #define MPI2_REQ_DESCRIPT_FLAGS_IOC_FIFO_MARKER (0x01) @@ -279,7 +287,7 @@ typedef struct _MPI2_DEFAULT_REQUEST_DESCRIPTOR typedef struct _MPI2_HIGH_PRIORITY_REQUEST_DESCRIPTOR { U8 RequestFlags; /* 0x00 */ - U8 VF_ID; /* 0x01 */ + U8 MSIxIndex; /* 0x01 */ U16 SMID; /* 0x02 */ U16 LMID; /* 0x04 */ U16 Reserved1; /* 0x06 */ @@ -293,7 +301,7 @@ typedef struct _MPI2_HIGH_PRIORITY_REQUEST_DESCRIPTOR typedef struct _MPI2_SCSI_IO_REQUEST_DESCRIPTOR { U8 RequestFlags; /* 0x00 */ - U8 VF_ID; /* 0x01 */ + U8 MSIxIndex; /* 0x01 */ U16 SMID; /* 0x02 */ U16 LMID; /* 0x04 */ U16 DevHandle; /* 0x06 */ @@ -306,7 +314,7 @@ typedef struct _MPI2_SCSI_IO_REQUEST_DESCRIPTOR typedef struct _MPI2_SCSI_TARGET_REQUEST_DESCRIPTOR { U8 RequestFlags; /* 0x00 */ - U8 VF_ID; /* 0x01 */ + U8 MSIxIndex; /* 0x01 */ U16 SMID; /* 0x02 */ U16 LMID; /* 0x04 */ U16 IoIndex; /* 0x06 */ @@ -315,14 +323,29 @@ typedef struct _MPI2_SCSI_TARGET_REQUEST_DESCRIPTOR Mpi2SCSITargetRequestDescriptor_t, MPI2_POINTER pMpi2SCSITargetRequestDescriptor_t; + +/* RAID Accelerator Request Descriptor */ +typedef struct _MPI2_RAID_ACCEL_REQUEST_DESCRIPTOR { + U8 RequestFlags; /* 0x00 */ + U8 MSIxIndex; /* 0x01 */ + U16 SMID; /* 0x02 */ + U16 LMID; /* 0x04 */ + U16 Reserved; /* 0x06 */ +} MPI2_RAID_ACCEL_REQUEST_DESCRIPTOR, + MPI2_POINTER PTR_MPI2_RAID_ACCEL_REQUEST_DESCRIPTOR, + Mpi2RAIDAcceleratorRequestDescriptor_t, + MPI2_POINTER pMpi2RAIDAcceleratorRequestDescriptor_t; + + /* union of Request Descriptors */ typedef union _MPI2_REQUEST_DESCRIPTOR_UNION { - MPI2_DEFAULT_REQUEST_DESCRIPTOR Default; - MPI2_HIGH_PRIORITY_REQUEST_DESCRIPTOR HighPriority; - MPI2_SCSI_IO_REQUEST_DESCRIPTOR SCSIIO; - MPI2_SCSI_TARGET_REQUEST_DESCRIPTOR SCSITarget; - U64 Words; + MPI2_DEFAULT_REQUEST_DESCRIPTOR Default; + MPI2_HIGH_PRIORITY_REQUEST_DESCRIPTOR HighPriority; + MPI2_SCSI_IO_REQUEST_DESCRIPTOR SCSIIO; + MPI2_SCSI_TARGET_REQUEST_DESCRIPTOR SCSITarget; + MPI2_RAID_ACCEL_REQUEST_DESCRIPTOR RAIDAccelerator; + U64 Words; } MPI2_REQUEST_DESCRIPTOR_UNION, MPI2_POINTER PTR_MPI2_REQUEST_DESCRIPTOR_UNION, Mpi2RequestDescriptorUnion_t, MPI2_POINTER pMpi2RequestDescriptorUnion_t; @@ -333,19 +356,20 @@ typedef union _MPI2_REQUEST_DESCRIPTOR_UNION typedef struct _MPI2_DEFAULT_REPLY_DESCRIPTOR { U8 ReplyFlags; /* 0x00 */ - U8 VF_ID; /* 0x01 */ + U8 MSIxIndex; /* 0x01 */ U16 DescriptorTypeDependent1; /* 0x02 */ U32 DescriptorTypeDependent2; /* 0x04 */ } MPI2_DEFAULT_REPLY_DESCRIPTOR, MPI2_POINTER PTR_MPI2_DEFAULT_REPLY_DESCRIPTOR, Mpi2DefaultReplyDescriptor_t, MPI2_POINTER pMpi2DefaultReplyDescriptor_t; /* defines for the ReplyFlags field */ -#define MPI2_RPY_DESCRIPT_FLAGS_TYPE_MASK (0x0F) -#define MPI2_RPY_DESCRIPT_FLAGS_SCSI_IO_SUCCESS (0x00) -#define MPI2_RPY_DESCRIPT_FLAGS_ADDRESS_REPLY (0x01) -#define MPI2_RPY_DESCRIPT_FLAGS_TARGETASSIST_SUCCESS (0x02) -#define MPI2_RPY_DESCRIPT_FLAGS_TARGET_COMMAND_BUFFER (0x03) -#define MPI2_RPY_DESCRIPT_FLAGS_UNUSED (0x0F) +#define MPI2_RPY_DESCRIPT_FLAGS_TYPE_MASK (0x0F) +#define MPI2_RPY_DESCRIPT_FLAGS_SCSI_IO_SUCCESS (0x00) +#define MPI2_RPY_DESCRIPT_FLAGS_ADDRESS_REPLY (0x01) +#define MPI2_RPY_DESCRIPT_FLAGS_TARGETASSIST_SUCCESS (0x02) +#define MPI2_RPY_DESCRIPT_FLAGS_TARGET_COMMAND_BUFFER (0x03) +#define MPI2_RPY_DESCRIPT_FLAGS_RAID_ACCELERATOR_SUCCESS (0x05) +#define MPI2_RPY_DESCRIPT_FLAGS_UNUSED (0x0F) /* values for marking a reply descriptor as unused */ #define MPI2_RPY_DESCRIPT_UNUSED_WORD0_MARK (0xFFFFFFFF) @@ -355,7 +379,7 @@ typedef struct _MPI2_DEFAULT_REPLY_DESCRIPTOR typedef struct _MPI2_ADDRESS_REPLY_DESCRIPTOR { U8 ReplyFlags; /* 0x00 */ - U8 VF_ID; /* 0x01 */ + U8 MSIxIndex; /* 0x01 */ U16 SMID; /* 0x02 */ U32 ReplyFrameAddress; /* 0x04 */ } MPI2_ADDRESS_REPLY_DESCRIPTOR, MPI2_POINTER PTR_MPI2_ADDRESS_REPLY_DESCRIPTOR, @@ -368,10 +392,10 @@ typedef struct _MPI2_ADDRESS_REPLY_DESCRIPTOR typedef struct _MPI2_SCSI_IO_SUCCESS_REPLY_DESCRIPTOR { U8 ReplyFlags; /* 0x00 */ - U8 VF_ID; /* 0x01 */ + U8 MSIxIndex; /* 0x01 */ U16 SMID; /* 0x02 */ U16 TaskTag; /* 0x04 */ - U16 DevHandle; /* 0x06 */ + U16 Reserved1; /* 0x06 */ } MPI2_SCSI_IO_SUCCESS_REPLY_DESCRIPTOR, MPI2_POINTER PTR_MPI2_SCSI_IO_SUCCESS_REPLY_DESCRIPTOR, Mpi2SCSIIOSuccessReplyDescriptor_t, @@ -382,7 +406,7 @@ typedef struct _MPI2_SCSI_IO_SUCCESS_REPLY_DESCRIPTOR typedef struct _MPI2_TARGETASSIST_SUCCESS_REPLY_DESCRIPTOR { U8 ReplyFlags; /* 0x00 */ - U8 VF_ID; /* 0x01 */ + U8 MSIxIndex; /* 0x01 */ U16 SMID; /* 0x02 */ U8 SequenceNumber; /* 0x04 */ U8 Reserved1; /* 0x05 */ @@ -397,7 +421,7 @@ typedef struct _MPI2_TARGETASSIST_SUCCESS_REPLY_DESCRIPTOR typedef struct _MPI2_TARGET_COMMAND_BUFFER_REPLY_DESCRIPTOR { U8 ReplyFlags; /* 0x00 */ - U8 VF_ID; /* 0x01 */ + U8 MSIxIndex; /* 0x01 */ U8 VP_ID; /* 0x02 */ U8 Flags; /* 0x03 */ U16 InitiatorDevHandle; /* 0x04 */ @@ -411,15 +435,28 @@ typedef struct _MPI2_TARGET_COMMAND_BUFFER_REPLY_DESCRIPTOR #define MPI2_RPY_DESCRIPT_TCB_FLAGS_PHYNUM_MASK (0x3F) +/* RAID Accelerator Success Reply Descriptor */ +typedef struct _MPI2_RAID_ACCELERATOR_SUCCESS_REPLY_DESCRIPTOR { + U8 ReplyFlags; /* 0x00 */ + U8 MSIxIndex; /* 0x01 */ + U16 SMID; /* 0x02 */ + U32 Reserved; /* 0x04 */ +} MPI2_RAID_ACCELERATOR_SUCCESS_REPLY_DESCRIPTOR, + MPI2_POINTER PTR_MPI2_RAID_ACCELERATOR_SUCCESS_REPLY_DESCRIPTOR, + Mpi2RAIDAcceleratorSuccessReplyDescriptor_t, + MPI2_POINTER pMpi2RAIDAcceleratorSuccessReplyDescriptor_t; + + /* union of Reply Descriptors */ typedef union _MPI2_REPLY_DESCRIPTORS_UNION { - MPI2_DEFAULT_REPLY_DESCRIPTOR Default; - MPI2_ADDRESS_REPLY_DESCRIPTOR AddressReply; - MPI2_SCSI_IO_SUCCESS_REPLY_DESCRIPTOR SCSIIOSuccess; - MPI2_TARGETASSIST_SUCCESS_REPLY_DESCRIPTOR TargetAssistSuccess; - MPI2_TARGET_COMMAND_BUFFER_REPLY_DESCRIPTOR TargetCommandBuffer; - U64 Words; + MPI2_DEFAULT_REPLY_DESCRIPTOR Default; + MPI2_ADDRESS_REPLY_DESCRIPTOR AddressReply; + MPI2_SCSI_IO_SUCCESS_REPLY_DESCRIPTOR SCSIIOSuccess; + MPI2_TARGETASSIST_SUCCESS_REPLY_DESCRIPTOR TargetAssistSuccess; + MPI2_TARGET_COMMAND_BUFFER_REPLY_DESCRIPTOR TargetCommandBuffer; + MPI2_RAID_ACCELERATOR_SUCCESS_REPLY_DESCRIPTOR RAIDAcceleratorSuccess; + U64 Words; } MPI2_REPLY_DESCRIPTORS_UNION, MPI2_POINTER PTR_MPI2_REPLY_DESCRIPTORS_UNION, Mpi2ReplyDescriptorsUnion_t, MPI2_POINTER pMpi2ReplyDescriptorsUnion_t; @@ -458,6 +495,7 @@ typedef union _MPI2_REPLY_DESCRIPTORS_UNION #define MPI2_FUNCTION_DIAG_RELEASE (0x1E) /* Diagnostic Release */ #define MPI2_FUNCTION_TARGET_CMD_BUF_BASE_POST (0x24) /* Target Command Buffer Post Base */ #define MPI2_FUNCTION_TARGET_CMD_BUF_LIST_POST (0x25) /* Target Command Buffer Post List */ +#define MPI2_FUNCTION_RAID_ACCELERATOR (0x2C) /* RAID Accelerator*/ @@ -555,12 +593,17 @@ typedef union _MPI2_REPLY_DESCRIPTORS_UNION #define MPI2_IOCSTATUS_DIAGNOSTIC_RELEASED (0x00A0) +/**************************************************************************** +* RAID Accelerator values +****************************************************************************/ + +#define MPI2_IOCSTATUS_RAID_ACCEL_ERROR (0x00B0) /**************************************************************************** * IOCStatus flag to indicate that log info is available ****************************************************************************/ -#define MPI2_IOCSTATUS_FLAG_LOG_INFO_AVAILABLE (0x8000) +#define MPI2_IOCSTATUS_FLAG_LOG_INFO_AVAILABLE (0x8000) /**************************************************************************** * IOCLogInfo Types diff --git a/drivers/scsi/mpt2sas/mpi/mpi2_cnfg.h b/drivers/scsi/mpt2sas/mpi/mpi2_cnfg.h index 2f27cf6d6c65..ab47c4679640 100644 --- a/drivers/scsi/mpt2sas/mpi/mpi2_cnfg.h +++ b/drivers/scsi/mpt2sas/mpi/mpi2_cnfg.h @@ -6,7 +6,7 @@ * Title: MPI Configuration messages and pages * Creation Date: November 10, 2006 * - * mpi2_cnfg.h Version: 02.00.10 + * mpi2_cnfg.h Version: 02.00.11 * * Version History * --------------- @@ -95,6 +95,11 @@ * Added MPI2_SAS_DEVICE0_ASTATUS_DEVICE_BLOCKED define. * Added PortGroups, DmaGroup, and ControlGroup fields to * SAS Device Page 0. + * 05-06-09 02.00.11 Added structures and defines for IO Unit Page 5 and IO + * Unit Page 6. + * Added expander reduced functionality data to SAS + * Expander Page 0. + * Added SAS PHY Page 2 and SAS PHY Page 3. * -------------------------------------------------------------------------- */ @@ -723,6 +728,65 @@ typedef struct _MPI2_CONFIG_PAGE_IO_UNIT_3 #define MPI2_IOUNITPAGE3_GPIO_SETTING_ON (0x0001) +/* IO Unit Page 5 */ + +/* + * Upper layer code (drivers, utilities, etc.) should leave this define set to + * one and check Header.PageLength or NumDmaEngines at runtime. + */ +#ifndef MPI2_IOUNITPAGE5_DMAENGINE_ENTRIES +#define MPI2_IOUNITPAGE5_DMAENGINE_ENTRIES (1) +#endif + +typedef struct _MPI2_CONFIG_PAGE_IO_UNIT_5 { + MPI2_CONFIG_PAGE_HEADER Header; /* 0x00 */ + U64 RaidAcceleratorBufferBaseAddress; /* 0x04 */ + U64 RaidAcceleratorBufferSize; /* 0x0C */ + U64 RaidAcceleratorControlBaseAddress; /* 0x14 */ + U8 RAControlSize; /* 0x1C */ + U8 NumDmaEngines; /* 0x1D */ + U8 RAMinControlSize; /* 0x1E */ + U8 RAMaxControlSize; /* 0x1F */ + U32 Reserved1; /* 0x20 */ + U32 Reserved2; /* 0x24 */ + U32 Reserved3; /* 0x28 */ + U32 DmaEngineCapabilities + [MPI2_IOUNITPAGE5_DMAENGINE_ENTRIES]; /* 0x2C */ +} MPI2_CONFIG_PAGE_IO_UNIT_5, MPI2_POINTER PTR_MPI2_CONFIG_PAGE_IO_UNIT_5, + Mpi2IOUnitPage5_t, MPI2_POINTER pMpi2IOUnitPage5_t; + +#define MPI2_IOUNITPAGE5_PAGEVERSION (0x00) + +/* defines for IO Unit Page 5 DmaEngineCapabilities field */ +#define MPI2_IOUNITPAGE5_DMA_CAP_MASK_MAX_REQUESTS (0xFF00) +#define MPI2_IOUNITPAGE5_DMA_CAP_SHIFT_MAX_REQUESTS (16) + +#define MPI2_IOUNITPAGE5_DMA_CAP_EEDP (0x0008) +#define MPI2_IOUNITPAGE5_DMA_CAP_PARITY_GENERATION (0x0004) +#define MPI2_IOUNITPAGE5_DMA_CAP_HASHING (0x0002) +#define MPI2_IOUNITPAGE5_DMA_CAP_ENCRYPTION (0x0001) + + +/* IO Unit Page 6 */ + +typedef struct _MPI2_CONFIG_PAGE_IO_UNIT_6 { + MPI2_CONFIG_PAGE_HEADER Header; /* 0x00 */ + U16 Flags; /* 0x04 */ + U8 RAHostControlSize; /* 0x06 */ + U8 Reserved0; /* 0x07 */ + U64 RaidAcceleratorHostControlBaseAddress; /* 0x08 */ + U32 Reserved1; /* 0x10 */ + U32 Reserved2; /* 0x14 */ + U32 Reserved3; /* 0x18 */ +} MPI2_CONFIG_PAGE_IO_UNIT_6, MPI2_POINTER PTR_MPI2_CONFIG_PAGE_IO_UNIT_6, + Mpi2IOUnitPage6_t, MPI2_POINTER pMpi2IOUnitPage6_t; + +#define MPI2_IOUNITPAGE6_PAGEVERSION (0x00) + +/* defines for IO Unit Page 6 Flags field */ +#define MPI2_IOUNITPAGE6_FLAGS_ENABLE_RAID_ACCELERATOR (0x0001) + + /**************************************************************************** * IOC Config Pages ****************************************************************************/ @@ -1709,10 +1773,14 @@ typedef struct _MPI2_CONFIG_PAGE_EXPANDER_0 U64 ActiveZoneManagerSASAddress;/* 0x2C */ U16 ZoneLockInactivityLimit; /* 0x34 */ U16 Reserved1; /* 0x36 */ + U8 TimeToReducedFunc; /* 0x38 */ + U8 InitialTimeToReducedFunc; /* 0x39 */ + U8 MaxReducedFuncTime; /* 0x3A */ + U8 Reserved2; /* 0x3B */ } MPI2_CONFIG_PAGE_EXPANDER_0, MPI2_POINTER PTR_MPI2_CONFIG_PAGE_EXPANDER_0, Mpi2ExpanderPage0_t, MPI2_POINTER pMpi2ExpanderPage0_t; -#define MPI2_SASEXPANDER0_PAGEVERSION (0x05) +#define MPI2_SASEXPANDER0_PAGEVERSION (0x06) /* values for SAS Expander Page 0 DiscoveryStatus field */ #define MPI2_SAS_EXPANDER0_DS_MAX_ENCLOSURES_EXCEED (0x80000000) @@ -1737,6 +1805,7 @@ typedef struct _MPI2_CONFIG_PAGE_EXPANDER_0 #define MPI2_SAS_EXPANDER0_DS_LOOP_DETECTED (0x00000001) /* values for SAS Expander Page 0 Flags field */ +#define MPI2_SAS_EXPANDER0_FLAGS_REDUCED_FUNCTIONALITY (0x2000) #define MPI2_SAS_EXPANDER0_FLAGS_ZONE_LOCKED (0x1000) #define MPI2_SAS_EXPANDER0_FLAGS_SUPPORTED_PHYSICAL_PRES (0x0800) #define MPI2_SAS_EXPANDER0_FLAGS_ASSERTED_PHYSICAL_PRES (0x0400) @@ -1944,6 +2013,133 @@ typedef struct _MPI2_CONFIG_PAGE_SAS_PHY_1 #define MPI2_SASPHY1_PAGEVERSION (0x01) +/* SAS PHY Page 2 */ + +typedef struct _MPI2_SASPHY2_PHY_EVENT { + U8 PhyEventCode; /* 0x00 */ + U8 Reserved1; /* 0x01 */ + U16 Reserved2; /* 0x02 */ + U32 PhyEventInfo; /* 0x04 */ +} MPI2_SASPHY2_PHY_EVENT, MPI2_POINTER PTR_MPI2_SASPHY2_PHY_EVENT, + Mpi2SasPhy2PhyEvent_t, MPI2_POINTER pMpi2SasPhy2PhyEvent_t; + +/* use MPI2_SASPHY3_EVENT_CODE_ for the PhyEventCode field */ + + +/* + * Host code (drivers, BIOS, utilities, etc.) should leave this define set to + * one and check Header.ExtPageLength or NumPhyEvents at runtime. + */ +#ifndef MPI2_SASPHY2_PHY_EVENT_MAX +#define MPI2_SASPHY2_PHY_EVENT_MAX (1) +#endif + +typedef struct _MPI2_CONFIG_PAGE_SAS_PHY_2 { + MPI2_CONFIG_EXTENDED_PAGE_HEADER Header; /* 0x00 */ + U32 Reserved1; /* 0x08 */ + U8 NumPhyEvents; /* 0x0C */ + U8 Reserved2; /* 0x0D */ + U16 Reserved3; /* 0x0E */ + MPI2_SASPHY2_PHY_EVENT PhyEvent[MPI2_SASPHY2_PHY_EVENT_MAX]; + /* 0x10 */ +} MPI2_CONFIG_PAGE_SAS_PHY_2, MPI2_POINTER PTR_MPI2_CONFIG_PAGE_SAS_PHY_2, + Mpi2SasPhyPage2_t, MPI2_POINTER pMpi2SasPhyPage2_t; + +#define MPI2_SASPHY2_PAGEVERSION (0x00) + + +/* SAS PHY Page 3 */ + +typedef struct _MPI2_SASPHY3_PHY_EVENT_CONFIG { + U8 PhyEventCode; /* 0x00 */ + U8 Reserved1; /* 0x01 */ + U16 Reserved2; /* 0x02 */ + U8 CounterType; /* 0x04 */ + U8 ThresholdWindow; /* 0x05 */ + U8 TimeUnits; /* 0x06 */ + U8 Reserved3; /* 0x07 */ + U32 EventThreshold; /* 0x08 */ + U16 ThresholdFlags; /* 0x0C */ + U16 Reserved4; /* 0x0E */ +} MPI2_SASPHY3_PHY_EVENT_CONFIG, MPI2_POINTER PTR_MPI2_SASPHY3_PHY_EVENT_CONFIG, + Mpi2SasPhy3PhyEventConfig_t, MPI2_POINTER pMpi2SasPhy3PhyEventConfig_t; + +/* values for PhyEventCode field */ +#define MPI2_SASPHY3_EVENT_CODE_NO_EVENT (0x00) +#define MPI2_SASPHY3_EVENT_CODE_INVALID_DWORD (0x01) +#define MPI2_SASPHY3_EVENT_CODE_RUNNING_DISPARITY_ERROR (0x02) +#define MPI2_SASPHY3_EVENT_CODE_LOSS_DWORD_SYNC (0x03) +#define MPI2_SASPHY3_EVENT_CODE_PHY_RESET_PROBLEM (0x04) +#define MPI2_SASPHY3_EVENT_CODE_ELASTICITY_BUF_OVERFLOW (0x05) +#define MPI2_SASPHY3_EVENT_CODE_RX_ERROR (0x06) +#define MPI2_SASPHY3_EVENT_CODE_RX_ADDR_FRAME_ERROR (0x20) +#define MPI2_SASPHY3_EVENT_CODE_TX_AC_OPEN_REJECT (0x21) +#define MPI2_SASPHY3_EVENT_CODE_RX_AC_OPEN_REJECT (0x22) +#define MPI2_SASPHY3_EVENT_CODE_TX_RC_OPEN_REJECT (0x23) +#define MPI2_SASPHY3_EVENT_CODE_RX_RC_OPEN_REJECT (0x24) +#define MPI2_SASPHY3_EVENT_CODE_RX_AIP_PARTIAL_WAITING_ON (0x25) +#define MPI2_SASPHY3_EVENT_CODE_RX_AIP_CONNECT_WAITING_ON (0x26) +#define MPI2_SASPHY3_EVENT_CODE_TX_BREAK (0x27) +#define MPI2_SASPHY3_EVENT_CODE_RX_BREAK (0x28) +#define MPI2_SASPHY3_EVENT_CODE_BREAK_TIMEOUT (0x29) +#define MPI2_SASPHY3_EVENT_CODE_CONNECTION (0x2A) +#define MPI2_SASPHY3_EVENT_CODE_PEAKTX_PATHWAY_BLOCKED (0x2B) +#define MPI2_SASPHY3_EVENT_CODE_PEAKTX_ARB_WAIT_TIME (0x2C) +#define MPI2_SASPHY3_EVENT_CODE_PEAK_ARB_WAIT_TIME (0x2D) +#define MPI2_SASPHY3_EVENT_CODE_PEAK_CONNECT_TIME (0x2E) +#define MPI2_SASPHY3_EVENT_CODE_TX_SSP_FRAMES (0x40) +#define MPI2_SASPHY3_EVENT_CODE_RX_SSP_FRAMES (0x41) +#define MPI2_SASPHY3_EVENT_CODE_TX_SSP_ERROR_FRAMES (0x42) +#define MPI2_SASPHY3_EVENT_CODE_RX_SSP_ERROR_FRAMES (0x43) +#define MPI2_SASPHY3_EVENT_CODE_TX_CREDIT_BLOCKED (0x44) +#define MPI2_SASPHY3_EVENT_CODE_RX_CREDIT_BLOCKED (0x45) +#define MPI2_SASPHY3_EVENT_CODE_TX_SATA_FRAMES (0x50) +#define MPI2_SASPHY3_EVENT_CODE_RX_SATA_FRAMES (0x51) +#define MPI2_SASPHY3_EVENT_CODE_SATA_OVERFLOW (0x52) +#define MPI2_SASPHY3_EVENT_CODE_TX_SMP_FRAMES (0x60) +#define MPI2_SASPHY3_EVENT_CODE_RX_SMP_FRAMES (0x61) +#define MPI2_SASPHY3_EVENT_CODE_RX_SMP_ERROR_FRAMES (0x63) +#define MPI2_SASPHY3_EVENT_CODE_HOTPLUG_TIMEOUT (0xD0) +#define MPI2_SASPHY3_EVENT_CODE_MISALIGNED_MUX_PRIMITIVE (0xD1) +#define MPI2_SASPHY3_EVENT_CODE_RX_AIP (0xD2) + +/* values for the CounterType field */ +#define MPI2_SASPHY3_COUNTER_TYPE_WRAPPING (0x00) +#define MPI2_SASPHY3_COUNTER_TYPE_SATURATING (0x01) +#define MPI2_SASPHY3_COUNTER_TYPE_PEAK_VALUE (0x02) + +/* values for the TimeUnits field */ +#define MPI2_SASPHY3_TIME_UNITS_10_MICROSECONDS (0x00) +#define MPI2_SASPHY3_TIME_UNITS_100_MICROSECONDS (0x01) +#define MPI2_SASPHY3_TIME_UNITS_1_MILLISECOND (0x02) +#define MPI2_SASPHY3_TIME_UNITS_10_MILLISECONDS (0x03) + +/* values for the ThresholdFlags field */ +#define MPI2_SASPHY3_TFLAGS_PHY_RESET (0x0002) +#define MPI2_SASPHY3_TFLAGS_EVENT_NOTIFY (0x0001) + +/* + * Host code (drivers, BIOS, utilities, etc.) should leave this define set to + * one and check Header.ExtPageLength or NumPhyEvents at runtime. + */ +#ifndef MPI2_SASPHY3_PHY_EVENT_MAX +#define MPI2_SASPHY3_PHY_EVENT_MAX (1) +#endif + +typedef struct _MPI2_CONFIG_PAGE_SAS_PHY_3 { + MPI2_CONFIG_EXTENDED_PAGE_HEADER Header; /* 0x00 */ + U32 Reserved1; /* 0x08 */ + U8 NumPhyEvents; /* 0x0C */ + U8 Reserved2; /* 0x0D */ + U16 Reserved3; /* 0x0E */ + MPI2_SASPHY3_PHY_EVENT_CONFIG PhyEventConfig + [MPI2_SASPHY3_PHY_EVENT_MAX]; /* 0x10 */ +} MPI2_CONFIG_PAGE_SAS_PHY_3, MPI2_POINTER PTR_MPI2_CONFIG_PAGE_SAS_PHY_3, + Mpi2SasPhyPage3_t, MPI2_POINTER pMpi2SasPhyPage3_t; + +#define MPI2_SASPHY3_PAGEVERSION (0x00) + + /**************************************************************************** * SAS Port Config Pages ****************************************************************************/ diff --git a/drivers/scsi/mpt2sas/mpi/mpi2_history.txt b/drivers/scsi/mpt2sas/mpi/mpi2_history.txt new file mode 100644 index 000000000000..65fcaa31cb30 --- /dev/null +++ b/drivers/scsi/mpt2sas/mpi/mpi2_history.txt @@ -0,0 +1,334 @@ + ============================== + Fusion-MPT MPI 2.0 Header File Change History + ============================== + + Copyright (c) 2000-2009 LSI Corporation. + + --------------------------------------- + Header Set Release Version: 02.00.12 + Header Set Release Date: 05-06-09 + --------------------------------------- + + Filename Current version Prior version + ---------- --------------- ------------- + mpi2.h 02.00.12 02.00.11 + mpi2_cnfg.h 02.00.11 02.00.10 + mpi2_init.h 02.00.07 02.00.06 + mpi2_ioc.h 02.00.11 02.00.10 + mpi2_raid.h 02.00.03 02.00.03 + mpi2_sas.h 02.00.02 02.00.02 + mpi2_targ.h 02.00.03 02.00.03 + mpi2_tool.h 02.00.03 02.00.02 + mpi2_type.h 02.00.00 02.00.00 + mpi2_ra.h 02.00.00 + mpi2_history.txt 02.00.11 02.00.12 + + + * Date Version Description + * -------- -------- ------------------------------------------------------ + +mpi2.h + * 04-30-07 02.00.00 Corresponds to Fusion-MPT MPI Specification Rev A. + * 06-04-07 02.00.01 Bumped MPI2_HEADER_VERSION_UNIT. + * 06-26-07 02.00.02 Bumped MPI2_HEADER_VERSION_UNIT. + * 08-31-07 02.00.03 Bumped MPI2_HEADER_VERSION_UNIT. + * Moved ReplyPostHostIndex register to offset 0x6C of the + * MPI2_SYSTEM_INTERFACE_REGS and modified the define for + * MPI2_REPLY_POST_HOST_INDEX_OFFSET. + * Added union of request descriptors. + * Added union of reply descriptors. + * 10-31-07 02.00.04 Bumped MPI2_HEADER_VERSION_UNIT. + * Added define for MPI2_VERSION_02_00. + * Fixed the size of the FunctionDependent5 field in the + * MPI2_DEFAULT_REPLY structure. + * 12-18-07 02.00.05 Bumped MPI2_HEADER_VERSION_UNIT. + * Removed the MPI-defined Fault Codes and extended the + * product specific codes up to 0xEFFF. + * Added a sixth key value for the WriteSequence register + * and changed the flush value to 0x0. + * Added message function codes for Diagnostic Buffer Post + * and Diagnsotic Release. + * New IOCStatus define: MPI2_IOCSTATUS_DIAGNOSTIC_RELEASED + * Moved MPI2_VERSION_UNION from mpi2_ioc.h. + * 02-29-08 02.00.06 Bumped MPI2_HEADER_VERSION_UNIT. + * 03-03-08 02.00.07 Bumped MPI2_HEADER_VERSION_UNIT. + * 05-21-08 02.00.08 Bumped MPI2_HEADER_VERSION_UNIT. + * Added #defines for marking a reply descriptor as unused. + * 06-27-08 02.00.09 Bumped MPI2_HEADER_VERSION_UNIT. + * 10-02-08 02.00.10 Bumped MPI2_HEADER_VERSION_UNIT. + * Moved LUN field defines from mpi2_init.h. + * 01-19-09 02.00.11 Bumped MPI2_HEADER_VERSION_UNIT. + * 05-06-09 02.00.12 Bumped MPI2_HEADER_VERSION_UNIT. + * In all request and reply descriptors, replaced VF_ID + * field with MSIxIndex field. + * Removed DevHandle field from + * MPI2_SCSI_IO_SUCCESS_REPLY_DESCRIPTOR and made those + * bytes reserved. + * Added RAID Accelerator functionality. + * -------------------------------------------------------------------------- + +mpi2_cnfg.h + * 04-30-07 02.00.00 Corresponds to Fusion-MPT MPI Specification Rev A. + * 06-04-07 02.00.01 Added defines for SAS IO Unit Page 2 PhyFlags. + * Added Manufacturing Page 11. + * Added MPI2_SAS_EXPANDER0_FLAGS_CONNECTOR_END_DEVICE + * define. + * 06-26-07 02.00.02 Adding generic structure for product-specific + * Manufacturing pages: MPI2_CONFIG_PAGE_MANUFACTURING_PS. + * Rework of BIOS Page 2 configuration page. + * Fixed MPI2_BIOSPAGE2_BOOT_DEVICE to be a union of the + * forms. + * Added configuration pages IOC Page 8 and Driver + * Persistent Mapping Page 0. + * 08-31-07 02.00.03 Modified configuration pages dealing with Integrated + * RAID (Manufacturing Page 4, RAID Volume Pages 0 and 1, + * RAID Physical Disk Pages 0 and 1, RAID Configuration + * Page 0). + * Added new value for AccessStatus field of SAS Device + * Page 0 (_SATA_NEEDS_INITIALIZATION). + * 10-31-07 02.00.04 Added missing SEPDevHandle field to + * MPI2_CONFIG_PAGE_SAS_ENCLOSURE_0. + * 12-18-07 02.00.05 Modified IO Unit Page 0 to use 32-bit version fields for + * NVDATA. + * Modified IOC Page 7 to use masks and added field for + * SASBroadcastPrimitiveMasks. + * Added MPI2_CONFIG_PAGE_BIOS_4. + * Added MPI2_CONFIG_PAGE_LOG_0. + * 02-29-08 02.00.06 Modified various names to make them 32-character unique. + * Added SAS Device IDs. + * Updated Integrated RAID configuration pages including + * Manufacturing Page 4, IOC Page 6, and RAID Configuration + * Page 0. + * 05-21-08 02.00.07 Added define MPI2_MANPAGE4_MIX_SSD_SAS_SATA. + * Added define MPI2_MANPAGE4_PHYSDISK_128MB_COERCION. + * Fixed define MPI2_IOCPAGE8_FLAGS_ENCLOSURE_SLOT_MAPPING. + * Added missing MaxNumRoutedSasAddresses field to + * MPI2_CONFIG_PAGE_EXPANDER_0. + * Added SAS Port Page 0. + * Modified structure layout for + * MPI2_CONFIG_PAGE_DRIVER_MAPPING_0. + * 06-27-08 02.00.08 Changed MPI2_CONFIG_PAGE_RD_PDISK_1 to use + * MPI2_RAID_PHYS_DISK1_PATH_MAX to size the array. + * 10-02-08 02.00.09 Changed MPI2_RAID_PGAD_CONFIGNUM_MASK from 0x0000FFFF + * to 0x000000FF. + * Added two new values for the Physical Disk Coercion Size + * bits in the Flags field of Manufacturing Page 4. + * Added product-specific Manufacturing pages 16 to 31. + * Modified Flags bits for controlling write cache on SATA + * drives in IO Unit Page 1. + * Added new bit to AdditionalControlFlags of SAS IO Unit + * Page 1 to control Invalid Topology Correction. + * Added SupportedPhysDisks field to RAID Volume Page 1 and + * added related defines. + * Added additional defines for RAID Volume Page 0 + * VolumeStatusFlags field. + * Modified meaning of RAID Volume Page 0 VolumeSettings + * define for auto-configure of hot-swap drives. + * Added PhysDiskAttributes field (and related defines) to + * RAID Physical Disk Page 0. + * Added MPI2_SAS_PHYINFO_PHY_VACANT define. + * Added three new DiscoveryStatus bits for SAS IO Unit + * Page 0 and SAS Expander Page 0. + * Removed multiplexing information from SAS IO Unit pages. + * Added BootDeviceWaitTime field to SAS IO Unit Page 4. + * Removed Zone Address Resolved bit from PhyInfo and from + * Expander Page 0 Flags field. + * Added two new AccessStatus values to SAS Device Page 0 + * for indicating routing problems. Added 3 reserved words + * to this page. + * 01-19-09 02.00.10 Fixed defines for GPIOVal field of IO Unit Page 3. + * Inserted missing reserved field into structure for IOC + * Page 6. + * Added more pending task bits to RAID Volume Page 0 + * VolumeStatusFlags defines. + * Added MPI2_PHYSDISK0_STATUS_FLAG_NOT_CERTIFIED define. + * Added a new DiscoveryStatus bit for SAS IO Unit Page 0 + * and SAS Expander Page 0 to flag a downstream initiator + * when in simplified routing mode. + * Removed SATA Init Failure defines for DiscoveryStatus + * fields of SAS IO Unit Page 0 and SAS Expander Page 0. + * Added MPI2_SAS_DEVICE0_ASTATUS_DEVICE_BLOCKED define. + * Added PortGroups, DmaGroup, and ControlGroup fields to + * SAS Device Page 0. + * 05-06-09 02.00.11 Added structures and defines for IO Unit Page 5 and IO + * Unit Page 6. + * Added expander reduced functionality data to SAS + * Expander Page 0. + * Added SAS PHY Page 2 and SAS PHY Page 3. + * -------------------------------------------------------------------------- + +mpi2_init.h + * 04-30-07 02.00.00 Corresponds to Fusion-MPT MPI Specification Rev A. + * 10-31-07 02.00.01 Fixed name for pMpi2SCSITaskManagementRequest_t. + * 12-18-07 02.00.02 Modified Task Management Target Reset Method defines. + * 02-29-08 02.00.03 Added Query Task Set and Query Unit Attention. + * 03-03-08 02.00.04 Fixed name of struct _MPI2_SCSI_TASK_MANAGE_REPLY. + * 05-21-08 02.00.05 Fixed typo in name of Mpi2SepRequest_t. + * 10-02-08 02.00.06 Removed Untagged and No Disconnect values from SCSI IO + * Control field Task Attribute flags. + * Moved LUN field defines to mpi2.h becasue they are + * common to many structures. + * 05-06-09 02.00.07 Changed task management type of Query Unit Attention to + * Query Asynchronous Event. + * Defined two new bits in the SlotStatus field of the SCSI + * Enclosure Processor Request and Reply. + * -------------------------------------------------------------------------- + +mpi2_ioc.h + * 04-30-07 02.00.00 Corresponds to Fusion-MPT MPI Specification Rev A. + * 06-04-07 02.00.01 In IOCFacts Reply structure, renamed MaxDevices to + * MaxTargets. + * Added TotalImageSize field to FWDownload Request. + * Added reserved words to FWUpload Request. + * 06-26-07 02.00.02 Added IR Configuration Change List Event. + * 08-31-07 02.00.03 Removed SystemReplyQueueDepth field from the IOCInit + * request and replaced it with + * ReplyDescriptorPostQueueDepth and ReplyFreeQueueDepth. + * Replaced the MinReplyQueueDepth field of the IOCFacts + * reply with MaxReplyDescriptorPostQueueDepth. + * Added MPI2_RDPQ_DEPTH_MIN define to specify the minimum + * depth for the Reply Descriptor Post Queue. + * Added SASAddress field to Initiator Device Table + * Overflow Event data. + * 10-31-07 02.00.04 Added ReasonCode MPI2_EVENT_SAS_INIT_RC_NOT_RESPONDING + * for SAS Initiator Device Status Change Event data. + * Modified Reason Code defines for SAS Topology Change + * List Event data, including adding a bit for PHY Vacant + * status, and adding a mask for the Reason Code. + * Added define for + * MPI2_EVENT_SAS_TOPO_ES_DELAY_NOT_RESPONDING. + * Added define for MPI2_EXT_IMAGE_TYPE_MEGARAID. + * 12-18-07 02.00.05 Added Boot Status defines for the IOCExceptions field of + * the IOCFacts Reply. + * Removed MPI2_IOCFACTS_CAPABILITY_EXTENDED_BUFFER define. + * Moved MPI2_VERSION_UNION to mpi2.h. + * Changed MPI2_EVENT_NOTIFICATION_REQUEST to use masks + * instead of enables, and added SASBroadcastPrimitiveMasks + * field. + * Added Log Entry Added Event and related structure. + * 02-29-08 02.00.06 Added define MPI2_IOCFACTS_CAPABILITY_INTEGRATED_RAID. + * Removed define MPI2_IOCFACTS_PROTOCOL_SMP_TARGET. + * Added MaxVolumes and MaxPersistentEntries fields to + * IOCFacts reply. + * Added ProtocalFlags and IOCCapabilities fields to + * MPI2_FW_IMAGE_HEADER. + * Removed MPI2_PORTENABLE_FLAGS_ENABLE_SINGLE_PORT. + * 03-03-08 02.00.07 Fixed MPI2_FW_IMAGE_HEADER by changing Reserved26 to + * a U16 (from a U32). + * Removed extra 's' from EventMasks name. + * 06-27-08 02.00.08 Fixed an offset in a comment. + * 10-02-08 02.00.09 Removed SystemReplyFrameSize from MPI2_IOC_INIT_REQUEST. + * Removed CurReplyFrameSize from MPI2_IOC_FACTS_REPLY and + * renamed MinReplyFrameSize to ReplyFrameSize. + * Added MPI2_IOCFACTS_EXCEPT_IR_FOREIGN_CONFIG_MAX. + * Added two new RAIDOperation values for Integrated RAID + * Operations Status Event data. + * Added four new IR Configuration Change List Event data + * ReasonCode values. + * Added two new ReasonCode defines for SAS Device Status + * Change Event data. + * Added three new DiscoveryStatus bits for the SAS + * Discovery event data. + * Added Multiplexing Status Change bit to the PhyStatus + * field of the SAS Topology Change List event data. + * Removed define for MPI2_INIT_IMAGE_BOOTFLAGS_XMEMCOPY. + * BootFlags are now product-specific. + * Added defines for the indivdual signature bytes + * for MPI2_INIT_IMAGE_FOOTER. + * 01-19-09 02.00.10 Added MPI2_IOCFACTS_CAPABILITY_EVENT_REPLAY define. + * Added MPI2_EVENT_SAS_DISC_DS_DOWNSTREAM_INITIATOR + * define. + * Added MPI2_EVENT_SAS_DEV_STAT_RC_SATA_INIT_FAILURE + * define. + * Removed MPI2_EVENT_SAS_DISC_DS_SATA_INIT_FAILURE define. + * 05-06-09 02.00.11 Added MPI2_IOCFACTS_CAPABILITY_RAID_ACCELERATOR define. + * Added MPI2_IOCFACTS_CAPABILITY_MSI_X_INDEX define. + * Added two new reason codes for SAS Device Status Change + * Event. + * Added new event: SAS PHY Counter. + * -------------------------------------------------------------------------- + +mpi2_raid.h + * 04-30-07 02.00.00 Corresponds to Fusion-MPT MPI Specification Rev A. + * 08-31-07 02.00.01 Modifications to RAID Action request and reply, + * including the Actions and ActionData. + * 02-29-08 02.00.02 Added MPI2_RAID_ACTION_ADATA_DISABL_FULL_REBUILD. + * 05-21-08 02.00.03 Added MPI2_RAID_VOL_CREATION_NUM_PHYSDISKS so that + * the PhysDisk array in MPI2_RAID_VOLUME_CREATION_STRUCT + * can be sized by the build environment. + * -------------------------------------------------------------------------- + +mpi2_sas.h + * 04-30-07 02.00.00 Corresponds to Fusion-MPT MPI Specification Rev A. + * 06-26-07 02.00.01 Added Clear All Persistent Operation to SAS IO Unit + * Control Request. + * 10-02-08 02.00.02 Added Set IOC Parameter Operation to SAS IO Unit Control + * Request. + * -------------------------------------------------------------------------- + +mpi2_targ.h + * 04-30-07 02.00.00 Corresponds to Fusion-MPT MPI Specification Rev A. + * 08-31-07 02.00.01 Added Command Buffer Data Location Address Space bits to + * BufferPostFlags field of CommandBufferPostBase Request. + * 02-29-08 02.00.02 Modified various names to make them 32-character unique. + * 10-02-08 02.00.03 Removed NextCmdBufferOffset from + * MPI2_TARGET_CMD_BUF_POST_BASE_REQUEST. + * Target Status Send Request only takes a single SGE for + * response data. + * -------------------------------------------------------------------------- + +mpi2_tool.h + * 04-30-07 02.00.00 Corresponds to Fusion-MPT MPI Specification Rev A. + * 12-18-07 02.00.01 Added Diagnostic Buffer Post and Diagnostic Release + * structures and defines. + * 02-29-08 02.00.02 Modified various names to make them 32-character unique. + * 05-06-09 02.00.03 Added ISTWI Read Write Tool and Diagnostic CLI Tool. + * -------------------------------------------------------------------------- + +mpi2_type.h + * 04-30-07 02.00.00 Corresponds to Fusion-MPT MPI Specification Rev A. + * -------------------------------------------------------------------------- + +mpi2_ra.h + * 05-06-09 02.00.00 Initial version. + * -------------------------------------------------------------------------- + +mpi2_history.txt Parts list history + +Filename 02.00.12 +---------- -------- +mpi2.h 02.00.12 +mpi2_cnfg.h 02.00.11 +mpi2_init.h 02.00.07 +mpi2_ioc.h 02.00.11 +mpi2_raid.h 02.00.03 +mpi2_sas.h 02.00.02 +mpi2_targ.h 02.00.03 +mpi2_tool.h 02.00.03 +mpi2_type.h 02.00.00 +mpi2_ra.h 02.00.00 + +Filename 02.00.11 02.00.10 02.00.09 02.00.08 02.00.07 02.00.06 +---------- -------- -------- -------- -------- -------- -------- +mpi2.h 02.00.11 02.00.10 02.00.09 02.00.08 02.00.07 02.00.06 +mpi2_cnfg.h 02.00.10 02.00.09 02.00.08 02.00.07 02.00.06 02.00.06 +mpi2_init.h 02.00.06 02.00.06 02.00.05 02.00.05 02.00.04 02.00.03 +mpi2_ioc.h 02.00.10 02.00.09 02.00.08 02.00.07 02.00.07 02.00.06 +mpi2_raid.h 02.00.03 02.00.03 02.00.03 02.00.03 02.00.02 02.00.02 +mpi2_sas.h 02.00.02 02.00.02 02.00.01 02.00.01 02.00.01 02.00.01 +mpi2_targ.h 02.00.03 02.00.03 02.00.02 02.00.02 02.00.02 02.00.02 +mpi2_tool.h 02.00.02 02.00.02 02.00.02 02.00.02 02.00.02 02.00.02 +mpi2_type.h 02.00.00 02.00.00 02.00.00 02.00.00 02.00.00 02.00.00 + +Filename 02.00.05 02.00.04 02.00.03 02.00.02 02.00.01 02.00.00 +---------- -------- -------- -------- -------- -------- -------- +mpi2.h 02.00.05 02.00.04 02.00.03 02.00.02 02.00.01 02.00.00 +mpi2_cnfg.h 02.00.05 02.00.04 02.00.03 02.00.02 02.00.01 02.00.00 +mpi2_init.h 02.00.02 02.00.01 02.00.00 02.00.00 02.00.00 02.00.00 +mpi2_ioc.h 02.00.05 02.00.04 02.00.03 02.00.02 02.00.01 02.00.00 +mpi2_raid.h 02.00.01 02.00.01 02.00.01 02.00.00 02.00.00 02.00.00 +mpi2_sas.h 02.00.01 02.00.01 02.00.01 02.00.01 02.00.00 02.00.00 +mpi2_targ.h 02.00.01 02.00.01 02.00.01 02.00.00 02.00.00 02.00.00 +mpi2_tool.h 02.00.01 02.00.00 02.00.00 02.00.00 02.00.00 02.00.00 +mpi2_type.h 02.00.00 02.00.00 02.00.00 02.00.00 02.00.00 02.00.00 + diff --git a/drivers/scsi/mpt2sas/mpi/mpi2_init.h b/drivers/scsi/mpt2sas/mpi/mpi2_init.h index f1115f0f0eb2..563e56d2e945 100644 --- a/drivers/scsi/mpt2sas/mpi/mpi2_init.h +++ b/drivers/scsi/mpt2sas/mpi/mpi2_init.h @@ -1,12 +1,12 @@ /* - * Copyright (c) 2000-2008 LSI Corporation. + * Copyright (c) 2000-2009 LSI Corporation. * * * Name: mpi2_init.h * Title: MPI SCSI initiator mode messages and structures * Creation Date: June 23, 2006 * - * mpi2_init.h Version: 02.00.06 + * mpi2_init.h Version: 02.00.07 * * Version History * --------------- @@ -23,6 +23,10 @@ * Control field Task Attribute flags. * Moved LUN field defines to mpi2.h becasue they are * common to many structures. + * 05-06-09 02.00.07 Changed task management type of Query Unit Attention to + * Query Asynchronous Event. + * Defined two new bits in the SlotStatus field of the SCSI + * Enclosure Processor Request and Reply. * -------------------------------------------------------------------------- */ @@ -289,7 +293,11 @@ typedef struct _MPI2_SCSI_TASK_MANAGE_REQUEST #define MPI2_SCSITASKMGMT_TASKTYPE_QUERY_TASK (0x07) #define MPI2_SCSITASKMGMT_TASKTYPE_CLR_ACA (0x08) #define MPI2_SCSITASKMGMT_TASKTYPE_QRY_TASK_SET (0x09) -#define MPI2_SCSITASKMGMT_TASKTYPE_QRY_UNIT_ATTENTION (0x0A) +#define MPI2_SCSITASKMGMT_TASKTYPE_QRY_ASYNC_EVENT (0x0A) + +/* obsolete TaskType name */ +#define MPI2_SCSITASKMGMT_TASKTYPE_QRY_UNIT_ATTENTION \ + (MPI2_SCSITASKMGMT_TASKTYPE_QRY_ASYNC_EVENT) /* MsgFlags bits */ @@ -375,6 +383,8 @@ typedef struct _MPI2_SEP_REQUEST #define MPI2_SEP_REQ_SLOTSTATUS_HOT_SPARE (0x00000100) #define MPI2_SEP_REQ_SLOTSTATUS_UNCONFIGURED (0x00000080) #define MPI2_SEP_REQ_SLOTSTATUS_PREDICTED_FAULT (0x00000040) +#define MPI2_SEP_REQ_SLOTSTATUS_IN_CRITICAL_ARRAY (0x00000010) +#define MPI2_SEP_REQ_SLOTSTATUS_IN_FAILED_ARRAY (0x00000008) #define MPI2_SEP_REQ_SLOTSTATUS_DEV_REBUILDING (0x00000004) #define MPI2_SEP_REQ_SLOTSTATUS_DEV_FAULTY (0x00000002) #define MPI2_SEP_REQ_SLOTSTATUS_NO_ERROR (0x00000001) @@ -410,6 +420,8 @@ typedef struct _MPI2_SEP_REPLY #define MPI2_SEP_REPLY_SLOTSTATUS_HOT_SPARE (0x00000100) #define MPI2_SEP_REPLY_SLOTSTATUS_UNCONFIGURED (0x00000080) #define MPI2_SEP_REPLY_SLOTSTATUS_PREDICTED_FAULT (0x00000040) +#define MPI2_SEP_REPLY_SLOTSTATUS_IN_CRITICAL_ARRAY (0x00000010) +#define MPI2_SEP_REPLY_SLOTSTATUS_IN_FAILED_ARRAY (0x00000008) #define MPI2_SEP_REPLY_SLOTSTATUS_DEV_REBUILDING (0x00000004) #define MPI2_SEP_REPLY_SLOTSTATUS_DEV_FAULTY (0x00000002) #define MPI2_SEP_REPLY_SLOTSTATUS_NO_ERROR (0x00000001) diff --git a/drivers/scsi/mpt2sas/mpi/mpi2_ioc.h b/drivers/scsi/mpt2sas/mpi/mpi2_ioc.h index 8c5d81870c03..c294128bdeb4 100644 --- a/drivers/scsi/mpt2sas/mpi/mpi2_ioc.h +++ b/drivers/scsi/mpt2sas/mpi/mpi2_ioc.h @@ -6,7 +6,7 @@ * Title: MPI IOC, Port, Event, FW Download, and FW Upload messages * Creation Date: October 11, 2006 * - * mpi2_ioc.h Version: 02.00.10 + * mpi2_ioc.h Version: 02.00.11 * * Version History * --------------- @@ -79,6 +79,11 @@ * Added MPI2_EVENT_SAS_DEV_STAT_RC_SATA_INIT_FAILURE * define. * Removed MPI2_EVENT_SAS_DISC_DS_SATA_INIT_FAILURE define. + * 05-06-09 02.00.11 Added MPI2_IOCFACTS_CAPABILITY_RAID_ACCELERATOR define. + * Added MPI2_IOCFACTS_CAPABILITY_MSI_X_INDEX define. + * Added two new reason codes for SAS Device Status Change + * Event. + * Added new event: SAS PHY Counter. * -------------------------------------------------------------------------- */ @@ -261,6 +266,8 @@ typedef struct _MPI2_IOC_FACTS_REPLY /* ProductID field uses MPI2_FW_HEADER_PID_ */ /* IOCCapabilities */ +#define MPI2_IOCFACTS_CAPABILITY_MSI_X_INDEX (0x00008000) +#define MPI2_IOCFACTS_CAPABILITY_RAID_ACCELERATOR (0x00004000) #define MPI2_IOCFACTS_CAPABILITY_EVENT_REPLAY (0x00002000) #define MPI2_IOCFACTS_CAPABILITY_INTEGRATED_RAID (0x00001000) #define MPI2_IOCFACTS_CAPABILITY_TLR (0x00000800) @@ -440,6 +447,7 @@ typedef struct _MPI2_EVENT_NOTIFICATION_REPLY #define MPI2_EVENT_IR_PHYSICAL_DISK (0x001F) #define MPI2_EVENT_IR_CONFIGURATION_CHANGE_LIST (0x0020) #define MPI2_EVENT_LOG_ENTRY_ADDED (0x0021) +#define MPI2_EVENT_SAS_PHY_COUNTER (0x0022) /* Log Entry Added Event data */ @@ -502,17 +510,19 @@ typedef struct _MPI2_EVENT_DATA_SAS_DEVICE_STATUS_CHANGE MPI2_POINTER pMpi2EventDataSasDeviceStatusChange_t; /* SAS Device Status Change Event data ReasonCode values */ -#define MPI2_EVENT_SAS_DEV_STAT_RC_SMART_DATA (0x05) -#define MPI2_EVENT_SAS_DEV_STAT_RC_UNSUPPORTED (0x07) -#define MPI2_EVENT_SAS_DEV_STAT_RC_INTERNAL_DEVICE_RESET (0x08) -#define MPI2_EVENT_SAS_DEV_STAT_RC_TASK_ABORT_INTERNAL (0x09) -#define MPI2_EVENT_SAS_DEV_STAT_RC_ABORT_TASK_SET_INTERNAL (0x0A) -#define MPI2_EVENT_SAS_DEV_STAT_RC_CLEAR_TASK_SET_INTERNAL (0x0B) -#define MPI2_EVENT_SAS_DEV_STAT_RC_QUERY_TASK_INTERNAL (0x0C) -#define MPI2_EVENT_SAS_DEV_STAT_RC_ASYNC_NOTIFICATION (0x0D) -#define MPI2_EVENT_SAS_DEV_STAT_RC_CMP_INTERNAL_DEV_RESET (0x0E) -#define MPI2_EVENT_SAS_DEV_STAT_RC_CMP_TASK_ABORT_INTERNAL (0x0F) -#define MPI2_EVENT_SAS_DEV_STAT_RC_SATA_INIT_FAILURE (0x10) +#define MPI2_EVENT_SAS_DEV_STAT_RC_SMART_DATA (0x05) +#define MPI2_EVENT_SAS_DEV_STAT_RC_UNSUPPORTED (0x07) +#define MPI2_EVENT_SAS_DEV_STAT_RC_INTERNAL_DEVICE_RESET (0x08) +#define MPI2_EVENT_SAS_DEV_STAT_RC_TASK_ABORT_INTERNAL (0x09) +#define MPI2_EVENT_SAS_DEV_STAT_RC_ABORT_TASK_SET_INTERNAL (0x0A) +#define MPI2_EVENT_SAS_DEV_STAT_RC_CLEAR_TASK_SET_INTERNAL (0x0B) +#define MPI2_EVENT_SAS_DEV_STAT_RC_QUERY_TASK_INTERNAL (0x0C) +#define MPI2_EVENT_SAS_DEV_STAT_RC_ASYNC_NOTIFICATION (0x0D) +#define MPI2_EVENT_SAS_DEV_STAT_RC_CMP_INTERNAL_DEV_RESET (0x0E) +#define MPI2_EVENT_SAS_DEV_STAT_RC_CMP_TASK_ABORT_INTERNAL (0x0F) +#define MPI2_EVENT_SAS_DEV_STAT_RC_SATA_INIT_FAILURE (0x10) +#define MPI2_EVENT_SAS_DEV_STAT_RC_EXPANDER_REDUCED_FUNCTIONALITY (0x11) +#define MPI2_EVENT_SAS_DEV_STAT_RC_CMP_EXPANDER_REDUCED_FUNCTIONALITY (0x12) /* Integrated RAID Operation Status Event data */ @@ -822,6 +832,37 @@ typedef struct _MPI2_EVENT_DATA_SAS_ENCL_DEV_STATUS_CHANGE #define MPI2_EVENT_SAS_ENCL_RC_NOT_RESPONDING (0x02) +/* SAS PHY Counter Event data */ + +typedef struct _MPI2_EVENT_DATA_SAS_PHY_COUNTER { + U64 TimeStamp; /* 0x00 */ + U32 Reserved1; /* 0x08 */ + U8 PhyEventCode; /* 0x0C */ + U8 PhyNum; /* 0x0D */ + U16 Reserved2; /* 0x0E */ + U32 PhyEventInfo; /* 0x10 */ + U8 CounterType; /* 0x14 */ + U8 ThresholdWindow; /* 0x15 */ + U8 TimeUnits; /* 0x16 */ + U8 Reserved3; /* 0x17 */ + U32 EventThreshold; /* 0x18 */ + U16 ThresholdFlags; /* 0x1C */ + U16 Reserved4; /* 0x1E */ +} MPI2_EVENT_DATA_SAS_PHY_COUNTER, + MPI2_POINTER PTR_MPI2_EVENT_DATA_SAS_PHY_COUNTER, + Mpi2EventDataSasPhyCounter_t, MPI2_POINTER pMpi2EventDataSasPhyCounter_t; + +/* use MPI2_SASPHY3_EVENT_CODE_ values from mpi2_cnfg.h for the + * PhyEventCode field + * use MPI2_SASPHY3_COUNTER_TYPE_ values from mpi2_cnfg.h for the + * CounterType field + * use MPI2_SASPHY3_TIME_UNITS_ values from mpi2_cnfg.h for the + * TimeUnits field + * use MPI2_SASPHY3_TFLAGS_ values from mpi2_cnfg.h for the + * ThresholdFlags field + * */ + + /**************************************************************************** * EventAck message ****************************************************************************/ diff --git a/drivers/scsi/mpt2sas/mpi/mpi2_tool.h b/drivers/scsi/mpt2sas/mpi/mpi2_tool.h index 2ff4e936bd39..007e950f7bfa 100644 --- a/drivers/scsi/mpt2sas/mpi/mpi2_tool.h +++ b/drivers/scsi/mpt2sas/mpi/mpi2_tool.h @@ -1,12 +1,12 @@ /* - * Copyright (c) 2000-2008 LSI Corporation. + * Copyright (c) 2000-2009 LSI Corporation. * * * Name: mpi2_tool.h * Title: MPI diagnostic tool structures and definitions * Creation Date: March 26, 2007 * - * mpi2_tool.h Version: 02.00.02 + * mpi2_tool.h Version: 02.00.03 * * Version History * --------------- @@ -17,6 +17,7 @@ * 12-18-07 02.00.01 Added Diagnostic Buffer Post and Diagnostic Release * structures and defines. * 02-29-08 02.00.02 Modified various names to make them 32-character unique. + * 05-06-09 02.00.03 Added ISTWI Read Write Tool and Diagnostic CLI Tool. * -------------------------------------------------------------------------- */ @@ -32,7 +33,10 @@ /* defines for the Tools */ #define MPI2_TOOLBOX_CLEAN_TOOL (0x00) #define MPI2_TOOLBOX_MEMORY_MOVE_TOOL (0x01) +#define MPI2_TOOLBOX_ISTWI_READ_WRITE_TOOL (0x03) #define MPI2_TOOLBOX_BEACON_TOOL (0x05) +#define MPI2_TOOLBOX_DIAGNOSTIC_CLI_TOOL (0x06) + /**************************************************************************** * Toolbox reply @@ -112,6 +116,77 @@ typedef struct _MPI2_TOOLBOX_MEM_MOVE_REQUEST /**************************************************************************** +* Toolbox ISTWI Read Write Tool +****************************************************************************/ + +/* Toolbox ISTWI Read Write Tool request message */ +typedef struct _MPI2_TOOLBOX_ISTWI_READ_WRITE_REQUEST { + U8 Tool; /* 0x00 */ + U8 Reserved1; /* 0x01 */ + U8 ChainOffset; /* 0x02 */ + U8 Function; /* 0x03 */ + U16 Reserved2; /* 0x04 */ + U8 Reserved3; /* 0x06 */ + U8 MsgFlags; /* 0x07 */ + U8 VP_ID; /* 0x08 */ + U8 VF_ID; /* 0x09 */ + U16 Reserved4; /* 0x0A */ + U32 Reserved5; /* 0x0C */ + U32 Reserved6; /* 0x10 */ + U8 DevIndex; /* 0x14 */ + U8 Action; /* 0x15 */ + U8 SGLFlags; /* 0x16 */ + U8 Reserved7; /* 0x17 */ + U16 TxDataLength; /* 0x18 */ + U16 RxDataLength; /* 0x1A */ + U32 Reserved8; /* 0x1C */ + U32 Reserved9; /* 0x20 */ + U32 Reserved10; /* 0x24 */ + U32 Reserved11; /* 0x28 */ + U32 Reserved12; /* 0x2C */ + MPI2_SGE_SIMPLE_UNION SGL; /* 0x30 */ +} MPI2_TOOLBOX_ISTWI_READ_WRITE_REQUEST, + MPI2_POINTER PTR_MPI2_TOOLBOX_ISTWI_READ_WRITE_REQUEST, + Mpi2ToolboxIstwiReadWriteRequest_t, + MPI2_POINTER pMpi2ToolboxIstwiReadWriteRequest_t; + +/* values for the Action field */ +#define MPI2_TOOL_ISTWI_ACTION_READ_DATA (0x01) +#define MPI2_TOOL_ISTWI_ACTION_WRITE_DATA (0x02) +#define MPI2_TOOL_ISTWI_ACTION_SEQUENCE (0x03) +#define MPI2_TOOL_ISTWI_ACTION_RESERVE_BUS (0x10) +#define MPI2_TOOL_ISTWI_ACTION_RELEASE_BUS (0x11) +#define MPI2_TOOL_ISTWI_ACTION_RESET (0x12) + +/* values for SGLFlags field are in the SGL section of mpi2.h */ + + +/* Toolbox ISTWI Read Write Tool reply message */ +typedef struct _MPI2_TOOLBOX_ISTWI_REPLY { + U8 Tool; /* 0x00 */ + U8 Reserved1; /* 0x01 */ + U8 MsgLength; /* 0x02 */ + U8 Function; /* 0x03 */ + U16 Reserved2; /* 0x04 */ + U8 Reserved3; /* 0x06 */ + U8 MsgFlags; /* 0x07 */ + U8 VP_ID; /* 0x08 */ + U8 VF_ID; /* 0x09 */ + U16 Reserved4; /* 0x0A */ + U16 Reserved5; /* 0x0C */ + U16 IOCStatus; /* 0x0E */ + U32 IOCLogInfo; /* 0x10 */ + U8 DevIndex; /* 0x14 */ + U8 Action; /* 0x15 */ + U8 IstwiStatus; /* 0x16 */ + U8 Reserved6; /* 0x17 */ + U16 TxDataCount; /* 0x18 */ + U16 RxDataCount; /* 0x1A */ +} MPI2_TOOLBOX_ISTWI_REPLY, MPI2_POINTER PTR_MPI2_TOOLBOX_ISTWI_REPLY, + Mpi2ToolboxIstwiReply_t, MPI2_POINTER pMpi2ToolboxIstwiReply_t; + + +/**************************************************************************** * Toolbox Beacon Tool request ****************************************************************************/ @@ -139,6 +214,61 @@ typedef struct _MPI2_TOOLBOX_BEACON_REQUEST #define MPI2_TOOLBOX_FLAGS_BEACONMODE_ON (0x01) +/**************************************************************************** +* Toolbox Diagnostic CLI Tool +****************************************************************************/ + +#define MPI2_TOOLBOX_DIAG_CLI_CMD_LENGTH (0x5C) + +/* Toolbox Diagnostic CLI Tool request message */ +typedef struct _MPI2_TOOLBOX_DIAGNOSTIC_CLI_REQUEST { + U8 Tool; /* 0x00 */ + U8 Reserved1; /* 0x01 */ + U8 ChainOffset; /* 0x02 */ + U8 Function; /* 0x03 */ + U16 Reserved2; /* 0x04 */ + U8 Reserved3; /* 0x06 */ + U8 MsgFlags; /* 0x07 */ + U8 VP_ID; /* 0x08 */ + U8 VF_ID; /* 0x09 */ + U16 Reserved4; /* 0x0A */ + U8 SGLFlags; /* 0x0C */ + U8 Reserved5; /* 0x0D */ + U16 Reserved6; /* 0x0E */ + U32 DataLength; /* 0x10 */ + U8 DiagnosticCliCommand + [MPI2_TOOLBOX_DIAG_CLI_CMD_LENGTH]; /* 0x14 */ + MPI2_SGE_SIMPLE_UNION SGL; /* 0x70 */ +} MPI2_TOOLBOX_DIAGNOSTIC_CLI_REQUEST, + MPI2_POINTER PTR_MPI2_TOOLBOX_DIAGNOSTIC_CLI_REQUEST, + Mpi2ToolboxDiagnosticCliRequest_t, + MPI2_POINTER pMpi2ToolboxDiagnosticCliRequest_t; + +/* values for SGLFlags field are in the SGL section of mpi2.h */ + + +/* Toolbox Diagnostic CLI Tool reply message */ +typedef struct _MPI2_TOOLBOX_DIAGNOSTIC_CLI_REPLY { + U8 Tool; /* 0x00 */ + U8 Reserved1; /* 0x01 */ + U8 MsgLength; /* 0x02 */ + U8 Function; /* 0x03 */ + U16 Reserved2; /* 0x04 */ + U8 Reserved3; /* 0x06 */ + U8 MsgFlags; /* 0x07 */ + U8 VP_ID; /* 0x08 */ + U8 VF_ID; /* 0x09 */ + U16 Reserved4; /* 0x0A */ + U16 Reserved5; /* 0x0C */ + U16 IOCStatus; /* 0x0E */ + U32 IOCLogInfo; /* 0x10 */ + U32 ReturnedDataLength; /* 0x14 */ +} MPI2_TOOLBOX_DIAGNOSTIC_CLI_REPLY, + MPI2_POINTER PTR_MPI2_TOOLBOX_DIAG_CLI_REPLY, + Mpi2ToolboxDiagnosticCliReply_t, + MPI2_POINTER pMpi2ToolboxDiagnosticCliReply_t; + + /***************************************************************************** * * Diagnostic Buffer Messages diff --git a/drivers/scsi/mpt2sas/mpt2sas_base.c b/drivers/scsi/mpt2sas/mpt2sas_base.c index d95d2f274cb3..670241efa4b5 100644 --- a/drivers/scsi/mpt2sas/mpt2sas_base.c +++ b/drivers/scsi/mpt2sas/mpt2sas_base.c @@ -3,7 +3,7 @@ * for access to MPT (Message Passing Technology) firmware. * * This code is based on drivers/scsi/mpt2sas/mpt2_base.c - * Copyright (C) 2007-2008 LSI Corporation + * Copyright (C) 2007-2009 LSI Corporation * (mailto:DL-MPTFusionLinux@lsi.com) * * This program is free software; you can redistribute it and/or @@ -63,7 +63,7 @@ static MPT_CALLBACK mpt_callbacks[MPT_MAX_CALLBACKS]; #define FAULT_POLLING_INTERVAL 1000 /* in milliseconds */ -#define MPT2SAS_MAX_REQUEST_QUEUE 500 /* maximum controller queue depth */ +#define MPT2SAS_MAX_REQUEST_QUEUE 600 /* maximum controller queue depth */ static int max_queue_depth = -1; module_param(max_queue_depth, int, 0); @@ -543,13 +543,13 @@ mpt2sas_base_fault_info(struct MPT2SAS_ADAPTER *ioc , u16 fault_code) * _base_display_reply_info - * @ioc: pointer to scsi command object * @smid: system request message index - * @VF_ID: virtual function id + * @msix_index: MSIX table index supplied by the OS * @reply: reply message frame(lower 32bit addr) * * Return nothing. */ static void -_base_display_reply_info(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 VF_ID, +_base_display_reply_info(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 msix_index, u32 reply) { MPI2DefaultReply_t *mpi_reply; @@ -572,22 +572,24 @@ _base_display_reply_info(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 VF_ID, * mpt2sas_base_done - base internal command completion routine * @ioc: pointer to scsi command object * @smid: system request message index - * @VF_ID: virtual function id + * @msix_index: MSIX table index supplied by the OS * @reply: reply message frame(lower 32bit addr) * - * Return nothing. + * Return 1 meaning mf should be freed from _base_interrupt + * 0 means the mf is freed from this function. */ -void -mpt2sas_base_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 VF_ID, u32 reply) +u8 +mpt2sas_base_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 msix_index, + u32 reply) { MPI2DefaultReply_t *mpi_reply; mpi_reply = mpt2sas_base_get_reply_virt_addr(ioc, reply); if (mpi_reply && mpi_reply->Function == MPI2_FUNCTION_EVENT_ACK) - return; + return 1; if (ioc->base_cmds.status == MPT2_CMD_NOT_USED) - return; + return 1; ioc->base_cmds.status |= MPT2_CMD_COMPLETE; if (mpi_reply) { @@ -596,18 +598,20 @@ mpt2sas_base_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 VF_ID, u32 reply) } ioc->base_cmds.status &= ~MPT2_CMD_PENDING; complete(&ioc->base_cmds.done); + return 1; } /** * _base_async_event - main callback handler for firmware asyn events * @ioc: pointer to scsi command object - * @VF_ID: virtual function id + * @msix_index: MSIX table index supplied by the OS * @reply: reply message frame(lower 32bit addr) * - * Return nothing. + * Return 1 meaning mf should be freed from _base_interrupt + * 0 means the mf is freed from this function. */ -static void -_base_async_event(struct MPT2SAS_ADAPTER *ioc, u8 VF_ID, u32 reply) +static u8 +_base_async_event(struct MPT2SAS_ADAPTER *ioc, u8 msix_index, u32 reply) { Mpi2EventNotificationReply_t *mpi_reply; Mpi2EventAckRequest_t *ack_request; @@ -615,9 +619,9 @@ _base_async_event(struct MPT2SAS_ADAPTER *ioc, u8 VF_ID, u32 reply) mpi_reply = mpt2sas_base_get_reply_virt_addr(ioc, reply); if (!mpi_reply) - return; + return 1; if (mpi_reply->Function != MPI2_FUNCTION_EVENT_NOTIFICATION) - return; + return 1; #ifdef CONFIG_SCSI_MPT2SAS_LOGGING _base_display_event_data(ioc, mpi_reply); #endif @@ -635,16 +639,47 @@ _base_async_event(struct MPT2SAS_ADAPTER *ioc, u8 VF_ID, u32 reply) ack_request->Function = MPI2_FUNCTION_EVENT_ACK; ack_request->Event = mpi_reply->Event; ack_request->EventContext = mpi_reply->EventContext; - ack_request->VF_ID = VF_ID; - mpt2sas_base_put_smid_default(ioc, smid, VF_ID); + ack_request->VF_ID = 0; /* TODO */ + ack_request->VP_ID = 0; + mpt2sas_base_put_smid_default(ioc, smid); out: /* scsih callback handler */ - mpt2sas_scsih_event_callback(ioc, VF_ID, reply); + mpt2sas_scsih_event_callback(ioc, msix_index, reply); /* ctl callback handler */ - mpt2sas_ctl_event_callback(ioc, VF_ID, reply); + mpt2sas_ctl_event_callback(ioc, msix_index, reply); + + return 1; +} + +/** + * _base_get_cb_idx - obtain the callback index + * @ioc: per adapter object + * @smid: system request message index + * + * Return callback index. + */ +static u8 +_base_get_cb_idx(struct MPT2SAS_ADAPTER *ioc, u16 smid) +{ + int i; + u8 cb_idx = 0xFF; + + if (smid >= ioc->hi_priority_smid) { + if (smid < ioc->internal_smid) { + i = smid - ioc->hi_priority_smid; + cb_idx = ioc->hpr_lookup[i].cb_idx; + } else { + i = smid - ioc->internal_smid; + cb_idx = ioc->internal_lookup[i].cb_idx; + } + } else { + i = smid - 1; + cb_idx = ioc->scsi_lookup[i].cb_idx; + } + return cb_idx; } /** @@ -680,7 +715,6 @@ _base_unmask_interrupts(struct MPT2SAS_ADAPTER *ioc) { u32 him_register; - writel(0, &ioc->chip->HostInterruptStatus); him_register = readl(&ioc->chip->HostInterruptMask); him_register &= ~MPI2_HIM_RIM; writel(him_register, &ioc->chip->HostInterruptMask); @@ -712,9 +746,10 @@ _base_interrupt(int irq, void *bus_id) u16 smid; u8 cb_idx; u32 reply; - u8 VF_ID; + u8 msix_index; struct MPT2SAS_ADAPTER *ioc = bus_id; Mpi2ReplyDescriptorsUnion_t *rpf; + u8 rc; if (ioc->mask_interrupts) return IRQ_NONE; @@ -733,7 +768,7 @@ _base_interrupt(int irq, void *bus_id) reply = 0; cb_idx = 0xFF; smid = le16_to_cpu(rpf->Default.DescriptorTypeDependent1); - VF_ID = rpf->Default.VF_ID; + msix_index = rpf->Default.MSIxIndex; if (request_desript_type == MPI2_RPY_DESCRIPT_FLAGS_ADDRESS_REPLY) { reply = le32_to_cpu @@ -745,16 +780,18 @@ _base_interrupt(int irq, void *bus_id) MPI2_RPY_DESCRIPT_FLAGS_TARGETASSIST_SUCCESS) goto next; if (smid) - cb_idx = ioc->scsi_lookup[smid - 1].cb_idx; + cb_idx = _base_get_cb_idx(ioc, smid); if (smid && cb_idx != 0xFF) { - mpt_callbacks[cb_idx](ioc, smid, VF_ID, reply); + rc = mpt_callbacks[cb_idx](ioc, smid, msix_index, + reply); if (reply) - _base_display_reply_info(ioc, smid, VF_ID, + _base_display_reply_info(ioc, smid, msix_index, reply); - mpt2sas_base_free_smid(ioc, smid); + if (rc) + mpt2sas_base_free_smid(ioc, smid); } if (!smid) - _base_async_event(ioc, VF_ID, reply); + _base_async_event(ioc, msix_index, reply); /* reply free queue handling */ if (reply) { @@ -1191,19 +1228,6 @@ mpt2sas_base_map_resources(struct MPT2SAS_ADAPTER *ioc) } /** - * mpt2sas_base_get_msg_frame_dma - obtain request mf pointer phys addr - * @ioc: per adapter object - * @smid: system request message index(smid zero is invalid) - * - * Returns phys pointer to message frame. - */ -dma_addr_t -mpt2sas_base_get_msg_frame_dma(struct MPT2SAS_ADAPTER *ioc, u16 smid) -{ - return ioc->request_dma + (smid * ioc->request_sz); -} - -/** * mpt2sas_base_get_msg_frame - obtain request mf pointer * @ioc: per adapter object * @smid: system request message index(smid zero is invalid) @@ -1258,7 +1282,7 @@ mpt2sas_base_get_reply_virt_addr(struct MPT2SAS_ADAPTER *ioc, u32 phys_addr) } /** - * mpt2sas_base_get_smid - obtain a free smid + * mpt2sas_base_get_smid - obtain a free smid from internal queue * @ioc: per adapter object * @cb_idx: callback index * @@ -1272,6 +1296,39 @@ mpt2sas_base_get_smid(struct MPT2SAS_ADAPTER *ioc, u8 cb_idx) u16 smid; spin_lock_irqsave(&ioc->scsi_lookup_lock, flags); + if (list_empty(&ioc->internal_free_list)) { + spin_unlock_irqrestore(&ioc->scsi_lookup_lock, flags); + printk(MPT2SAS_ERR_FMT "%s: smid not available\n", + ioc->name, __func__); + return 0; + } + + request = list_entry(ioc->internal_free_list.next, + struct request_tracker, tracker_list); + request->cb_idx = cb_idx; + smid = request->smid; + list_del(&request->tracker_list); + spin_unlock_irqrestore(&ioc->scsi_lookup_lock, flags); + return smid; +} + +/** + * mpt2sas_base_get_smid_scsiio - obtain a free smid from scsiio queue + * @ioc: per adapter object + * @cb_idx: callback index + * @scmd: pointer to scsi command object + * + * Returns smid (zero is invalid) + */ +u16 +mpt2sas_base_get_smid_scsiio(struct MPT2SAS_ADAPTER *ioc, u8 cb_idx, + struct scsi_cmnd *scmd) +{ + unsigned long flags; + struct request_tracker *request; + u16 smid; + + spin_lock_irqsave(&ioc->scsi_lookup_lock, flags); if (list_empty(&ioc->free_list)) { spin_unlock_irqrestore(&ioc->scsi_lookup_lock, flags); printk(MPT2SAS_ERR_FMT "%s: smid not available\n", @@ -1281,6 +1338,36 @@ mpt2sas_base_get_smid(struct MPT2SAS_ADAPTER *ioc, u8 cb_idx) request = list_entry(ioc->free_list.next, struct request_tracker, tracker_list); + request->scmd = scmd; + request->cb_idx = cb_idx; + smid = request->smid; + list_del(&request->tracker_list); + spin_unlock_irqrestore(&ioc->scsi_lookup_lock, flags); + return smid; +} + +/** + * mpt2sas_base_get_smid_hpr - obtain a free smid from hi-priority queue + * @ioc: per adapter object + * @cb_idx: callback index + * + * Returns smid (zero is invalid) + */ +u16 +mpt2sas_base_get_smid_hpr(struct MPT2SAS_ADAPTER *ioc, u8 cb_idx) +{ + unsigned long flags; + struct request_tracker *request; + u16 smid; + + spin_lock_irqsave(&ioc->scsi_lookup_lock, flags); + if (list_empty(&ioc->hpr_free_list)) { + spin_unlock_irqrestore(&ioc->scsi_lookup_lock, flags); + return 0; + } + + request = list_entry(ioc->hpr_free_list.next, + struct request_tracker, tracker_list); request->cb_idx = cb_idx; smid = request->smid; list_del(&request->tracker_list); @@ -1300,10 +1387,32 @@ void mpt2sas_base_free_smid(struct MPT2SAS_ADAPTER *ioc, u16 smid) { unsigned long flags; + int i; spin_lock_irqsave(&ioc->scsi_lookup_lock, flags); - ioc->scsi_lookup[smid - 1].cb_idx = 0xFF; - list_add_tail(&ioc->scsi_lookup[smid - 1].tracker_list, + if (smid >= ioc->hi_priority_smid) { + if (smid < ioc->internal_smid) { + /* hi-priority */ + i = smid - ioc->hi_priority_smid; + ioc->hpr_lookup[i].cb_idx = 0xFF; + list_add_tail(&ioc->hpr_lookup[i].tracker_list, + &ioc->hpr_free_list); + } else { + /* internal queue */ + i = smid - ioc->internal_smid; + ioc->internal_lookup[i].cb_idx = 0xFF; + list_add_tail(&ioc->internal_lookup[i].tracker_list, + &ioc->internal_free_list); + } + spin_unlock_irqrestore(&ioc->scsi_lookup_lock, flags); + return; + } + + /* scsiio queue */ + i = smid - 1; + ioc->scsi_lookup[i].cb_idx = 0xFF; + ioc->scsi_lookup[i].scmd = NULL; + list_add_tail(&ioc->scsi_lookup[i].tracker_list, &ioc->free_list); spin_unlock_irqrestore(&ioc->scsi_lookup_lock, flags); @@ -1352,21 +1461,19 @@ static inline void _base_writeq(__u64 b, volatile void __iomem *addr, * mpt2sas_base_put_smid_scsi_io - send SCSI_IO request to firmware * @ioc: per adapter object * @smid: system request message index - * @vf_id: virtual function id * @handle: device handle * * Return nothing. */ void -mpt2sas_base_put_smid_scsi_io(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 vf_id, - u16 handle) +mpt2sas_base_put_smid_scsi_io(struct MPT2SAS_ADAPTER *ioc, u16 smid, u16 handle) { Mpi2RequestDescriptorUnion_t descriptor; u64 *request = (u64 *)&descriptor; descriptor.SCSIIO.RequestFlags = MPI2_REQ_DESCRIPT_FLAGS_SCSI_IO; - descriptor.SCSIIO.VF_ID = vf_id; + descriptor.SCSIIO.MSIxIndex = 0; /* TODO */ descriptor.SCSIIO.SMID = cpu_to_le16(smid); descriptor.SCSIIO.DevHandle = cpu_to_le16(handle); descriptor.SCSIIO.LMID = 0; @@ -1379,20 +1486,18 @@ mpt2sas_base_put_smid_scsi_io(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 vf_id, * mpt2sas_base_put_smid_hi_priority - send Task Managment request to firmware * @ioc: per adapter object * @smid: system request message index - * @vf_id: virtual function id * * Return nothing. */ void -mpt2sas_base_put_smid_hi_priority(struct MPT2SAS_ADAPTER *ioc, u16 smid, - u8 vf_id) +mpt2sas_base_put_smid_hi_priority(struct MPT2SAS_ADAPTER *ioc, u16 smid) { Mpi2RequestDescriptorUnion_t descriptor; u64 *request = (u64 *)&descriptor; descriptor.HighPriority.RequestFlags = MPI2_REQ_DESCRIPT_FLAGS_HIGH_PRIORITY; - descriptor.HighPriority.VF_ID = vf_id; + descriptor.HighPriority.MSIxIndex = 0; /* TODO */ descriptor.HighPriority.SMID = cpu_to_le16(smid); descriptor.HighPriority.LMID = 0; descriptor.HighPriority.Reserved1 = 0; @@ -1404,18 +1509,17 @@ mpt2sas_base_put_smid_hi_priority(struct MPT2SAS_ADAPTER *ioc, u16 smid, * mpt2sas_base_put_smid_default - Default, primarily used for config pages * @ioc: per adapter object * @smid: system request message index - * @vf_id: virtual function id * * Return nothing. */ void -mpt2sas_base_put_smid_default(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 vf_id) +mpt2sas_base_put_smid_default(struct MPT2SAS_ADAPTER *ioc, u16 smid) { Mpi2RequestDescriptorUnion_t descriptor; u64 *request = (u64 *)&descriptor; descriptor.Default.RequestFlags = MPI2_REQ_DESCRIPT_FLAGS_DEFAULT_TYPE; - descriptor.Default.VF_ID = vf_id; + descriptor.Default.MSIxIndex = 0; /* TODO */ descriptor.Default.SMID = cpu_to_le16(smid); descriptor.Default.LMID = 0; descriptor.Default.DescriptorTypeDependent = 0; @@ -1427,21 +1531,20 @@ mpt2sas_base_put_smid_default(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 vf_id) * mpt2sas_base_put_smid_target_assist - send Target Assist/Status to firmware * @ioc: per adapter object * @smid: system request message index - * @vf_id: virtual function id * @io_index: value used to track the IO * * Return nothing. */ void mpt2sas_base_put_smid_target_assist(struct MPT2SAS_ADAPTER *ioc, u16 smid, - u8 vf_id, u16 io_index) + u16 io_index) { Mpi2RequestDescriptorUnion_t descriptor; u64 *request = (u64 *)&descriptor; descriptor.SCSITarget.RequestFlags = MPI2_REQ_DESCRIPT_FLAGS_SCSI_TARGET; - descriptor.SCSITarget.VF_ID = vf_id; + descriptor.SCSITarget.MSIxIndex = 0; /* TODO */ descriptor.SCSITarget.SMID = cpu_to_le16(smid); descriptor.SCSITarget.LMID = 0; descriptor.SCSITarget.IoIndex = cpu_to_le16(io_index); @@ -1717,6 +1820,8 @@ _base_release_memory_pools(struct MPT2SAS_ADAPTER *ioc) } kfree(ioc->scsi_lookup); + kfree(ioc->hpr_lookup); + kfree(ioc->internal_lookup); } @@ -1736,7 +1841,6 @@ _base_allocate_memory_pools(struct MPT2SAS_ADAPTER *ioc, int sleep_flag) u16 num_of_reply_frames; u16 chains_needed_per_io; u32 sz, total_sz; - u16 i; u32 retry_sz; u16 max_request_credit; @@ -1764,7 +1868,10 @@ _base_allocate_memory_pools(struct MPT2SAS_ADAPTER *ioc, int sleep_flag) MPT2SAS_MAX_REQUEST_QUEUE) ? MPT2SAS_MAX_REQUEST_QUEUE : facts->RequestCredit; } - ioc->request_depth = max_request_credit; + + ioc->hba_queue_depth = max_request_credit; + ioc->hi_priority_depth = facts->HighPriorityCredit; + ioc->internal_depth = ioc->hi_priority_depth + 5; /* request frame size */ ioc->request_sz = facts->IOCRequestFrameSize * 4; @@ -1802,7 +1909,7 @@ _base_allocate_memory_pools(struct MPT2SAS_ADAPTER *ioc, int sleep_flag) ioc->chains_needed_per_io = chains_needed_per_io; /* reply free queue sizing - taking into account for events */ - num_of_reply_frames = ioc->request_depth + 32; + num_of_reply_frames = ioc->hba_queue_depth + 32; /* number of replies frames can't be a multiple of 16 */ /* decrease number of reply frames by 1 */ @@ -1823,7 +1930,7 @@ _base_allocate_memory_pools(struct MPT2SAS_ADAPTER *ioc, int sleep_flag) * frames */ - queue_size = ioc->request_depth + num_of_reply_frames + 1; + queue_size = ioc->hba_queue_depth + num_of_reply_frames + 1; /* round up to 16 byte boundary */ if (queue_size % 16) queue_size += 16 - (queue_size % 16); @@ -1837,60 +1944,85 @@ _base_allocate_memory_pools(struct MPT2SAS_ADAPTER *ioc, int sleep_flag) if (queue_diff % 16) queue_diff += 16 - (queue_diff % 16); - /* adjust request_depth, reply_free_queue_depth, + /* adjust hba_queue_depth, reply_free_queue_depth, * and queue_size */ - ioc->request_depth -= queue_diff; + ioc->hba_queue_depth -= queue_diff; ioc->reply_free_queue_depth -= queue_diff; queue_size -= queue_diff; } ioc->reply_post_queue_depth = queue_size; - /* max scsi host queue depth */ - ioc->shost->can_queue = ioc->request_depth - INTERNAL_CMDS_COUNT; - dinitprintk(ioc, printk(MPT2SAS_INFO_FMT "scsi host queue: depth" - "(%d)\n", ioc->name, ioc->shost->can_queue)); - dinitprintk(ioc, printk(MPT2SAS_INFO_FMT "scatter gather: " "sge_in_main_msg(%d), sge_per_chain(%d), sge_per_io(%d), " "chains_per_io(%d)\n", ioc->name, ioc->max_sges_in_main_message, ioc->max_sges_in_chain_message, ioc->shost->sg_tablesize, ioc->chains_needed_per_io)); + ioc->scsiio_depth = ioc->hba_queue_depth - + ioc->hi_priority_depth - ioc->internal_depth; + + /* set the scsi host can_queue depth + * with some internal commands that could be outstanding + */ + ioc->shost->can_queue = ioc->scsiio_depth - (2); + dinitprintk(ioc, printk(MPT2SAS_INFO_FMT "scsi host: " + "can_queue depth (%d)\n", ioc->name, ioc->shost->can_queue)); + /* contiguous pool for request and chains, 16 byte align, one extra " * "frame for smid=0 */ - ioc->chain_depth = ioc->chains_needed_per_io * ioc->request_depth; - sz = ((ioc->request_depth + 1 + ioc->chain_depth) * ioc->request_sz); + ioc->chain_depth = ioc->chains_needed_per_io * ioc->scsiio_depth; + sz = ((ioc->scsiio_depth + 1 + ioc->chain_depth) * ioc->request_sz); + + /* hi-priority queue */ + sz += (ioc->hi_priority_depth * ioc->request_sz); + + /* internal queue */ + sz += (ioc->internal_depth * ioc->request_sz); ioc->request_dma_sz = sz; ioc->request = pci_alloc_consistent(ioc->pdev, sz, &ioc->request_dma); if (!ioc->request) { printk(MPT2SAS_ERR_FMT "request pool: pci_alloc_consistent " - "failed: req_depth(%d), chains_per_io(%d), frame_sz(%d), " - "total(%d kB)\n", ioc->name, ioc->request_depth, + "failed: hba_depth(%d), chains_per_io(%d), frame_sz(%d), " + "total(%d kB)\n", ioc->name, ioc->hba_queue_depth, ioc->chains_needed_per_io, ioc->request_sz, sz/1024); - if (ioc->request_depth < MPT2SAS_SAS_QUEUE_DEPTH) + if (ioc->scsiio_depth < MPT2SAS_SAS_QUEUE_DEPTH) goto out; retry_sz += 64; - ioc->request_depth = max_request_credit - retry_sz; + ioc->hba_queue_depth = max_request_credit - retry_sz; goto retry_allocation; } if (retry_sz) printk(MPT2SAS_ERR_FMT "request pool: pci_alloc_consistent " - "succeed: req_depth(%d), chains_per_io(%d), frame_sz(%d), " - "total(%d kb)\n", ioc->name, ioc->request_depth, + "succeed: hba_depth(%d), chains_per_io(%d), frame_sz(%d), " + "total(%d kb)\n", ioc->name, ioc->hba_queue_depth, ioc->chains_needed_per_io, ioc->request_sz, sz/1024); - ioc->chain = ioc->request + ((ioc->request_depth + 1) * + + /* hi-priority queue */ + ioc->hi_priority = ioc->request + ((ioc->scsiio_depth + 1) * + ioc->request_sz); + ioc->hi_priority_dma = ioc->request_dma + ((ioc->scsiio_depth + 1) * + ioc->request_sz); + + /* internal queue */ + ioc->internal = ioc->hi_priority + (ioc->hi_priority_depth * + ioc->request_sz); + ioc->internal_dma = ioc->hi_priority_dma + (ioc->hi_priority_depth * + ioc->request_sz); + + ioc->chain = ioc->internal + (ioc->internal_depth * ioc->request_sz); - ioc->chain_dma = ioc->request_dma + ((ioc->request_depth + 1) * + ioc->chain_dma = ioc->internal_dma + (ioc->internal_depth * ioc->request_sz); + dinitprintk(ioc, printk(MPT2SAS_INFO_FMT "request pool(0x%p): " "depth(%d), frame_size(%d), pool_size(%d kB)\n", ioc->name, - ioc->request, ioc->request_depth, ioc->request_sz, - ((ioc->request_depth + 1) * ioc->request_sz)/1024)); + ioc->request, ioc->hba_queue_depth, ioc->request_sz, + (ioc->hba_queue_depth * ioc->request_sz)/1024)); dinitprintk(ioc, printk(MPT2SAS_INFO_FMT "chain pool(0x%p): depth" "(%d), frame_size(%d), pool_size(%d kB)\n", ioc->name, ioc->chain, ioc->chain_depth, ioc->request_sz, ((ioc->chain_depth * @@ -1899,7 +2031,7 @@ _base_allocate_memory_pools(struct MPT2SAS_ADAPTER *ioc, int sleep_flag) ioc->name, (unsigned long long) ioc->request_dma)); total_sz += sz; - ioc->scsi_lookup = kcalloc(ioc->request_depth, + ioc->scsi_lookup = kcalloc(ioc->scsiio_depth, sizeof(struct request_tracker), GFP_KERNEL); if (!ioc->scsi_lookup) { printk(MPT2SAS_ERR_FMT "scsi_lookup: kcalloc failed\n", @@ -1907,12 +2039,38 @@ _base_allocate_memory_pools(struct MPT2SAS_ADAPTER *ioc, int sleep_flag) goto out; } - /* initialize some bits */ - for (i = 0; i < ioc->request_depth; i++) - ioc->scsi_lookup[i].smid = i + 1; + dinitprintk(ioc, printk(MPT2SAS_INFO_FMT "scsiio(0x%p): " + "depth(%d)\n", ioc->name, ioc->request, + ioc->scsiio_depth)); + + /* initialize hi-priority queue smid's */ + ioc->hpr_lookup = kcalloc(ioc->hi_priority_depth, + sizeof(struct request_tracker), GFP_KERNEL); + if (!ioc->hpr_lookup) { + printk(MPT2SAS_ERR_FMT "hpr_lookup: kcalloc failed\n", + ioc->name); + goto out; + } + ioc->hi_priority_smid = ioc->scsiio_depth + 1; + dinitprintk(ioc, printk(MPT2SAS_INFO_FMT "hi_priority(0x%p): " + "depth(%d), start smid(%d)\n", ioc->name, ioc->hi_priority, + ioc->hi_priority_depth, ioc->hi_priority_smid)); + + /* initialize internal queue smid's */ + ioc->internal_lookup = kcalloc(ioc->internal_depth, + sizeof(struct request_tracker), GFP_KERNEL); + if (!ioc->internal_lookup) { + printk(MPT2SAS_ERR_FMT "internal_lookup: kcalloc failed\n", + ioc->name); + goto out; + } + ioc->internal_smid = ioc->hi_priority_smid + ioc->hi_priority_depth; + dinitprintk(ioc, printk(MPT2SAS_INFO_FMT "internal(0x%p): " + "depth(%d), start smid(%d)\n", ioc->name, ioc->internal, + ioc->internal_depth, ioc->internal_smid)); /* sense buffers, 4 byte align */ - sz = ioc->request_depth * SCSI_SENSE_BUFFERSIZE; + sz = ioc->scsiio_depth * SCSI_SENSE_BUFFERSIZE; ioc->sense_dma_pool = pci_pool_create("sense pool", ioc->pdev, sz, 4, 0); if (!ioc->sense_dma_pool) { @@ -1929,7 +2087,7 @@ _base_allocate_memory_pools(struct MPT2SAS_ADAPTER *ioc, int sleep_flag) } dinitprintk(ioc, printk(MPT2SAS_INFO_FMT "sense pool(0x%p): depth(%d), element_size(%d), pool_size" - "(%d kB)\n", ioc->name, ioc->sense, ioc->request_depth, + "(%d kB)\n", ioc->name, ioc->sense, ioc->scsiio_depth, SCSI_SENSE_BUFFERSIZE, sz/1024)); dinitprintk(ioc, printk(MPT2SAS_INFO_FMT "sense_dma(0x%llx)\n", ioc->name, (unsigned long long)ioc->sense_dma)); @@ -2304,7 +2462,7 @@ _base_handshake_req_reply_wait(struct MPT2SAS_ADAPTER *ioc, int request_bytes, ((request_bytes/4)<<MPI2_DOORBELL_ADD_DWORDS_SHIFT)), &ioc->chip->Doorbell); - if ((_base_wait_for_doorbell_int(ioc, 5, sleep_flag))) { + if ((_base_wait_for_doorbell_int(ioc, 5, NO_SLEEP))) { printk(MPT2SAS_ERR_FMT "doorbell handshake " "int failed (line=%d)\n", ioc->name, __LINE__); return -EFAULT; @@ -2454,7 +2612,8 @@ mpt2sas_base_sas_iounit_control(struct MPT2SAS_ADAPTER *ioc, if (mpi_request->Operation == MPI2_SAS_OP_PHY_HARD_RESET || mpi_request->Operation == MPI2_SAS_OP_PHY_LINK_RESET) ioc->ioc_link_reset_in_progress = 1; - mpt2sas_base_put_smid_default(ioc, smid, mpi_request->VF_ID); + mpt2sas_base_put_smid_default(ioc, smid); + init_completion(&ioc->base_cmds.done); timeleft = wait_for_completion_timeout(&ioc->base_cmds.done, msecs_to_jiffies(10000)); if ((mpi_request->Operation == MPI2_SAS_OP_PHY_HARD_RESET || @@ -2555,7 +2714,8 @@ mpt2sas_base_scsi_enclosure_processor(struct MPT2SAS_ADAPTER *ioc, request = mpt2sas_base_get_msg_frame(ioc, smid); ioc->base_cmds.smid = smid; memcpy(request, mpi_request, sizeof(Mpi2SepReply_t)); - mpt2sas_base_put_smid_default(ioc, smid, mpi_request->VF_ID); + mpt2sas_base_put_smid_default(ioc, smid); + init_completion(&ioc->base_cmds.done); timeleft = wait_for_completion_timeout(&ioc->base_cmds.done, msecs_to_jiffies(10000)); if (!(ioc->base_cmds.status & MPT2_CMD_COMPLETE)) { @@ -2701,13 +2861,12 @@ _base_get_ioc_facts(struct MPT2SAS_ADAPTER *ioc, int sleep_flag) /** * _base_send_ioc_init - send ioc_init to firmware * @ioc: per adapter object - * @VF_ID: virtual function id * @sleep_flag: CAN_SLEEP or NO_SLEEP * * Returns 0 for success, non-zero for failure. */ static int -_base_send_ioc_init(struct MPT2SAS_ADAPTER *ioc, u8 VF_ID, int sleep_flag) +_base_send_ioc_init(struct MPT2SAS_ADAPTER *ioc, int sleep_flag) { Mpi2IOCInitRequest_t mpi_request; Mpi2IOCInitReply_t mpi_reply; @@ -2719,7 +2878,8 @@ _base_send_ioc_init(struct MPT2SAS_ADAPTER *ioc, u8 VF_ID, int sleep_flag) memset(&mpi_request, 0, sizeof(Mpi2IOCInitRequest_t)); mpi_request.Function = MPI2_FUNCTION_IOC_INIT; mpi_request.WhoInit = MPI2_WHOINIT_HOST_DRIVER; - mpi_request.VF_ID = VF_ID; + mpi_request.VF_ID = 0; /* TODO */ + mpi_request.VP_ID = 0; mpi_request.MsgVersion = cpu_to_le16(MPI2_VERSION); mpi_request.HeaderVersion = cpu_to_le16(MPI2_HEADER_VERSION); @@ -2795,13 +2955,12 @@ _base_send_ioc_init(struct MPT2SAS_ADAPTER *ioc, u8 VF_ID, int sleep_flag) /** * _base_send_port_enable - send port_enable(discovery stuff) to firmware * @ioc: per adapter object - * @VF_ID: virtual function id * @sleep_flag: CAN_SLEEP or NO_SLEEP * * Returns 0 for success, non-zero for failure. */ static int -_base_send_port_enable(struct MPT2SAS_ADAPTER *ioc, u8 VF_ID, int sleep_flag) +_base_send_port_enable(struct MPT2SAS_ADAPTER *ioc, int sleep_flag) { Mpi2PortEnableRequest_t *mpi_request; u32 ioc_state; @@ -2829,9 +2988,11 @@ _base_send_port_enable(struct MPT2SAS_ADAPTER *ioc, u8 VF_ID, int sleep_flag) ioc->base_cmds.smid = smid; memset(mpi_request, 0, sizeof(Mpi2PortEnableRequest_t)); mpi_request->Function = MPI2_FUNCTION_PORT_ENABLE; - mpi_request->VF_ID = VF_ID; + mpi_request->VF_ID = 0; /* TODO */ + mpi_request->VP_ID = 0; - mpt2sas_base_put_smid_default(ioc, smid, VF_ID); + mpt2sas_base_put_smid_default(ioc, smid); + init_completion(&ioc->base_cmds.done); timeleft = wait_for_completion_timeout(&ioc->base_cmds.done, 300*HZ); if (!(ioc->base_cmds.status & MPT2_CMD_COMPLETE)) { @@ -2892,13 +3053,12 @@ _base_unmask_events(struct MPT2SAS_ADAPTER *ioc, u16 event) /** * _base_event_notification - send event notification * @ioc: per adapter object - * @VF_ID: virtual function id * @sleep_flag: CAN_SLEEP or NO_SLEEP * * Returns 0 for success, non-zero for failure. */ static int -_base_event_notification(struct MPT2SAS_ADAPTER *ioc, u8 VF_ID, int sleep_flag) +_base_event_notification(struct MPT2SAS_ADAPTER *ioc, int sleep_flag) { Mpi2EventNotificationRequest_t *mpi_request; unsigned long timeleft; @@ -2926,11 +3086,13 @@ _base_event_notification(struct MPT2SAS_ADAPTER *ioc, u8 VF_ID, int sleep_flag) ioc->base_cmds.smid = smid; memset(mpi_request, 0, sizeof(Mpi2EventNotificationRequest_t)); mpi_request->Function = MPI2_FUNCTION_EVENT_NOTIFICATION; - mpi_request->VF_ID = VF_ID; + mpi_request->VF_ID = 0; /* TODO */ + mpi_request->VP_ID = 0; for (i = 0; i < MPI2_EVENT_NOTIFY_EVENTMASK_WORDS; i++) mpi_request->EventMasks[i] = le32_to_cpu(ioc->event_masks[i]); - mpt2sas_base_put_smid_default(ioc, smid, VF_ID); + mpt2sas_base_put_smid_default(ioc, smid); + init_completion(&ioc->base_cmds.done); timeleft = wait_for_completion_timeout(&ioc->base_cmds.done, 30*HZ); if (!(ioc->base_cmds.status & MPT2_CMD_COMPLETE)) { printk(MPT2SAS_ERR_FMT "%s: timeout\n", @@ -2981,7 +3143,7 @@ mpt2sas_base_validate_event_type(struct MPT2SAS_ADAPTER *ioc, u32 *event_type) return; mutex_lock(&ioc->base_cmds.mutex); - _base_event_notification(ioc, 0, CAN_SLEEP); + _base_event_notification(ioc, CAN_SLEEP); mutex_unlock(&ioc->base_cmds.mutex); } @@ -3006,7 +3168,6 @@ _base_diag_reset(struct MPT2SAS_ADAPTER *ioc, int sleep_flag) drsprintk(ioc, printk(MPT2SAS_DEBUG_FMT "clear interrupts\n", ioc->name)); - writel(0, &ioc->chip->HostInterruptStatus); count = 0; do { @@ -3160,30 +3321,60 @@ _base_make_ioc_ready(struct MPT2SAS_ADAPTER *ioc, int sleep_flag, /** * _base_make_ioc_operational - put controller in OPERATIONAL state * @ioc: per adapter object - * @VF_ID: virtual function id * @sleep_flag: CAN_SLEEP or NO_SLEEP * * Returns 0 for success, non-zero for failure. */ static int -_base_make_ioc_operational(struct MPT2SAS_ADAPTER *ioc, u8 VF_ID, - int sleep_flag) +_base_make_ioc_operational(struct MPT2SAS_ADAPTER *ioc, int sleep_flag) { int r, i; unsigned long flags; u32 reply_address; + u16 smid; + struct _tr_list *delayed_tr, *delayed_tr_next; dinitprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s\n", ioc->name, __func__)); + /* clean the delayed target reset list */ + list_for_each_entry_safe(delayed_tr, delayed_tr_next, + &ioc->delayed_tr_list, list) { + list_del(&delayed_tr->list); + kfree(delayed_tr); + } + /* initialize the scsi lookup free list */ spin_lock_irqsave(&ioc->scsi_lookup_lock, flags); INIT_LIST_HEAD(&ioc->free_list); - for (i = 0; i < ioc->request_depth; i++) { + smid = 1; + for (i = 0; i < ioc->scsiio_depth; i++, smid++) { ioc->scsi_lookup[i].cb_idx = 0xFF; + ioc->scsi_lookup[i].smid = smid; + ioc->scsi_lookup[i].scmd = NULL; list_add_tail(&ioc->scsi_lookup[i].tracker_list, &ioc->free_list); } + + /* hi-priority queue */ + INIT_LIST_HEAD(&ioc->hpr_free_list); + smid = ioc->hi_priority_smid; + for (i = 0; i < ioc->hi_priority_depth; i++, smid++) { + ioc->hpr_lookup[i].cb_idx = 0xFF; + ioc->hpr_lookup[i].smid = smid; + list_add_tail(&ioc->hpr_lookup[i].tracker_list, + &ioc->hpr_free_list); + } + + /* internal queue */ + INIT_LIST_HEAD(&ioc->internal_free_list); + smid = ioc->internal_smid; + for (i = 0; i < ioc->internal_depth; i++, smid++) { + ioc->internal_lookup[i].cb_idx = 0xFF; + ioc->internal_lookup[i].smid = smid; + list_add_tail(&ioc->internal_lookup[i].tracker_list, + &ioc->internal_free_list); + } spin_unlock_irqrestore(&ioc->scsi_lookup_lock, flags); /* initialize Reply Free Queue */ @@ -3196,7 +3387,7 @@ _base_make_ioc_operational(struct MPT2SAS_ADAPTER *ioc, u8 VF_ID, for (i = 0; i < ioc->reply_post_queue_depth; i++) ioc->reply_post_free[i].Words = ULLONG_MAX; - r = _base_send_ioc_init(ioc, VF_ID, sleep_flag); + r = _base_send_ioc_init(ioc, sleep_flag); if (r) return r; @@ -3207,14 +3398,14 @@ _base_make_ioc_operational(struct MPT2SAS_ADAPTER *ioc, u8 VF_ID, writel(0, &ioc->chip->ReplyPostHostIndex); _base_unmask_interrupts(ioc); - r = _base_event_notification(ioc, VF_ID, sleep_flag); + r = _base_event_notification(ioc, sleep_flag); if (r) return r; if (sleep_flag == CAN_SLEEP) _base_static_config_pages(ioc); - r = _base_send_port_enable(ioc, VF_ID, sleep_flag); + r = _base_send_port_enable(ioc, sleep_flag); if (r) return r; @@ -3278,6 +3469,17 @@ mpt2sas_base_attach(struct MPT2SAS_ADAPTER *ioc) if (r) goto out_free_resources; + ioc->pfacts = kcalloc(ioc->facts.NumberOfPorts, + sizeof(Mpi2PortFactsReply_t), GFP_KERNEL); + if (!ioc->pfacts) + goto out_free_resources; + + for (i = 0 ; i < ioc->facts.NumberOfPorts; i++) { + r = _base_get_port_facts(ioc, i, CAN_SLEEP); + if (r) + goto out_free_resources; + } + r = _base_allocate_memory_pools(ioc, CAN_SLEEP); if (r) goto out_free_resources; @@ -3286,7 +3488,6 @@ mpt2sas_base_attach(struct MPT2SAS_ADAPTER *ioc) /* base internal command bits */ mutex_init(&ioc->base_cmds.mutex); - init_completion(&ioc->base_cmds.done); ioc->base_cmds.reply = kzalloc(ioc->reply_sz, GFP_KERNEL); ioc->base_cmds.status = MPT2_CMD_NOT_USED; @@ -3294,7 +3495,6 @@ mpt2sas_base_attach(struct MPT2SAS_ADAPTER *ioc) ioc->transport_cmds.reply = kzalloc(ioc->reply_sz, GFP_KERNEL); ioc->transport_cmds.status = MPT2_CMD_NOT_USED; mutex_init(&ioc->transport_cmds.mutex); - init_completion(&ioc->transport_cmds.done); /* task management internal command bits */ ioc->tm_cmds.reply = kzalloc(ioc->reply_sz, GFP_KERNEL); @@ -3310,7 +3510,6 @@ mpt2sas_base_attach(struct MPT2SAS_ADAPTER *ioc) ioc->ctl_cmds.reply = kzalloc(ioc->reply_sz, GFP_KERNEL); ioc->ctl_cmds.status = MPT2_CMD_NOT_USED; mutex_init(&ioc->ctl_cmds.mutex); - init_completion(&ioc->ctl_cmds.done); for (i = 0; i < MPI2_EVENT_NOTIFY_EVENTMASK_WORDS; i++) ioc->event_masks[i] = -1; @@ -3327,18 +3526,7 @@ mpt2sas_base_attach(struct MPT2SAS_ADAPTER *ioc) _base_unmask_events(ioc, MPI2_EVENT_IR_OPERATION_STATUS); _base_unmask_events(ioc, MPI2_EVENT_TASK_SET_FULL); _base_unmask_events(ioc, MPI2_EVENT_LOG_ENTRY_ADDED); - - ioc->pfacts = kcalloc(ioc->facts.NumberOfPorts, - sizeof(Mpi2PortFactsReply_t), GFP_KERNEL); - if (!ioc->pfacts) - goto out_free_resources; - - for (i = 0 ; i < ioc->facts.NumberOfPorts; i++) { - r = _base_get_port_facts(ioc, i, CAN_SLEEP); - if (r) - goto out_free_resources; - } - r = _base_make_ioc_operational(ioc, 0, CAN_SLEEP); + r = _base_make_ioc_operational(ioc, CAN_SLEEP); if (r) goto out_free_resources; @@ -3466,7 +3654,7 @@ _wait_for_commands_to_complete(struct MPT2SAS_ADAPTER *ioc, int sleep_flag) /* pending command count */ spin_lock_irqsave(&ioc->scsi_lookup_lock, flags); - for (i = 0; i < ioc->request_depth; i++) + for (i = 0; i < ioc->scsiio_depth; i++) if (ioc->scsi_lookup[i].cb_idx != 0xFF) ioc->pending_io_count++; spin_unlock_irqrestore(&ioc->scsi_lookup_lock, flags); @@ -3490,7 +3678,7 @@ int mpt2sas_base_hard_reset_handler(struct MPT2SAS_ADAPTER *ioc, int sleep_flag, enum reset_type type) { - int r, i; + int r; unsigned long flags; dtmprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s: enter\n", ioc->name, @@ -3513,9 +3701,7 @@ mpt2sas_base_hard_reset_handler(struct MPT2SAS_ADAPTER *ioc, int sleep_flag, if (r) goto out; _base_reset_handler(ioc, MPT2_IOC_AFTER_RESET); - for (i = 0 ; i < ioc->facts.NumberOfPorts; i++) - r = _base_make_ioc_operational(ioc, ioc->pfacts[i].VF_ID, - sleep_flag); + r = _base_make_ioc_operational(ioc, sleep_flag); if (!r) _base_reset_handler(ioc, MPT2_IOC_DONE_RESET); out: diff --git a/drivers/scsi/mpt2sas/mpt2sas_base.h b/drivers/scsi/mpt2sas/mpt2sas_base.h index 2faab1e690e9..0cf6bc236e4d 100644 --- a/drivers/scsi/mpt2sas/mpt2sas_base.h +++ b/drivers/scsi/mpt2sas/mpt2sas_base.h @@ -3,7 +3,7 @@ * for access to MPT (Message Passing Technology) firmware. * * This code is based on drivers/scsi/mpt2sas/mpt2_base.h - * Copyright (C) 2007-2008 LSI Corporation + * Copyright (C) 2007-2009 LSI Corporation * (mailto:DL-MPTFusionLinux@lsi.com) * * This program is free software; you can redistribute it and/or @@ -69,10 +69,10 @@ #define MPT2SAS_DRIVER_NAME "mpt2sas" #define MPT2SAS_AUTHOR "LSI Corporation <DL-MPTFusionLinux@lsi.com>" #define MPT2SAS_DESCRIPTION "LSI MPT Fusion SAS 2.0 Device Driver" -#define MPT2SAS_DRIVER_VERSION "01.100.06.00" -#define MPT2SAS_MAJOR_VERSION 01 +#define MPT2SAS_DRIVER_VERSION "02.100.03.00" +#define MPT2SAS_MAJOR_VERSION 02 #define MPT2SAS_MINOR_VERSION 100 -#define MPT2SAS_BUILD_VERSION 06 +#define MPT2SAS_BUILD_VERSION 03 #define MPT2SAS_RELEASE_VERSION 00 /* @@ -264,6 +264,13 @@ struct _internal_cmd { * SAS Topology Structures */ +#define MPTSAS_STATE_TR_SEND 0x0001 +#define MPTSAS_STATE_TR_COMPLETE 0x0002 +#define MPTSAS_STATE_CNTRL_SEND 0x0004 +#define MPTSAS_STATE_CNTRL_COMPLETE 0x0008 + +#define MPT2SAS_REQ_SAS_CNTRL 0x0010 + /** * struct _sas_device - attached device information * @list: sas device list @@ -300,6 +307,7 @@ struct _sas_device { u16 slot; u8 hidden_raid_component; u8 responding; + u16 state; }; /** @@ -436,6 +444,17 @@ struct request_tracker { struct list_head tracker_list; }; +/** + * struct _tr_list - target reset list + * @handle: device handle + * @state: state machine + */ +struct _tr_list { + struct list_head list; + u16 handle; + u16 state; +}; + typedef void (*MPT_ADD_SGE)(void *paddr, u32 flags_length, dma_addr_t dma_addr); /** @@ -510,8 +529,9 @@ typedef void (*MPT_ADD_SGE)(void *paddr, u32 flags_length, dma_addr_t dma_addr); * @config_page_sz: config page size * @config_page: reserve memory for config page payload * @config_page_dma: + * @hba_queue_depth: hba request queue depth * @sge_size: sg element size for either 32/64 bit - * @request_depth: hba request queue depth + * @scsiio_depth: SCSI_IO queue depth * @request_sz: per request frame size * @request: pool of request frames * @request_dma: @@ -528,6 +548,18 @@ typedef void (*MPT_ADD_SGE)(void *paddr, u32 flags_length, dma_addr_t dma_addr); * @chains_needed_per_io: max chains per io * @chain_offset_value_for_main_message: location 1st sg in main * @chain_depth: total chains allocated + * @hi_priority_smid: + * @hi_priority: + * @hi_priority_dma: + * @hi_priority_depth: + * @hpr_lookup: + * @hpr_free_list: + * @internal_smid: + * @internal: + * @internal_dma: + * @internal_depth: + * @internal_lookup: + * @internal_free_list: * @sense: pool of sense * @sense_dma: * @sense_dma_pool: @@ -597,6 +629,8 @@ struct MPT2SAS_ADAPTER { u8 ctl_cb_idx; u8 base_cb_idx; u8 config_cb_idx; + u8 tm_tr_cb_idx; + u8 tm_sas_control_cb_idx; struct _internal_cmd base_cmds; struct _internal_cmd transport_cmds; struct _internal_cmd tm_cmds; @@ -643,9 +677,10 @@ struct MPT2SAS_ADAPTER { void *config_page; dma_addr_t config_page_dma; - /* request */ + /* scsiio request */ + u16 hba_queue_depth; u16 sge_size; - u16 request_depth; + u16 scsiio_depth; u16 request_sz; u8 *request; dma_addr_t request_dma; @@ -665,6 +700,22 @@ struct MPT2SAS_ADAPTER { u16 chain_offset_value_for_main_message; u16 chain_depth; + /* hi-priority queue */ + u16 hi_priority_smid; + u8 *hi_priority; + dma_addr_t hi_priority_dma; + u16 hi_priority_depth; + struct request_tracker *hpr_lookup; + struct list_head hpr_free_list; + + /* internal queue */ + u16 internal_smid; + u8 *internal; + dma_addr_t internal_dma; + u16 internal_depth; + struct request_tracker *internal_lookup; + struct list_head internal_free_list; + /* sense */ u8 *sense; dma_addr_t sense_dma; @@ -690,6 +741,8 @@ struct MPT2SAS_ADAPTER { struct dma_pool *reply_post_free_dma_pool; u32 reply_post_host_index; + struct list_head delayed_tr_list; + /* diag buffer support */ u8 *diag_buffer[MPI2_DIAG_BUF_TYPE_COUNT]; u32 diag_buffer_sz[MPI2_DIAG_BUF_TYPE_COUNT]; @@ -701,7 +754,7 @@ struct MPT2SAS_ADAPTER { u32 diagnostic_flags[MPI2_DIAG_BUF_TYPE_COUNT]; }; -typedef void (*MPT_CALLBACK)(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 VF_ID, +typedef u8 (*MPT_CALLBACK)(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 msix_index, u32 reply); @@ -720,22 +773,28 @@ int mpt2sas_base_hard_reset_handler(struct MPT2SAS_ADAPTER *ioc, int sleep_flag, void *mpt2sas_base_get_msg_frame(struct MPT2SAS_ADAPTER *ioc, u16 smid); void *mpt2sas_base_get_sense_buffer(struct MPT2SAS_ADAPTER *ioc, u16 smid); void mpt2sas_base_build_zero_len_sge(struct MPT2SAS_ADAPTER *ioc, void *paddr); -dma_addr_t mpt2sas_base_get_msg_frame_dma(struct MPT2SAS_ADAPTER *ioc, u16 smid); -dma_addr_t mpt2sas_base_get_sense_buffer_dma(struct MPT2SAS_ADAPTER *ioc, u16 smid); +dma_addr_t mpt2sas_base_get_sense_buffer_dma(struct MPT2SAS_ADAPTER *ioc, + u16 smid); + +/* hi-priority queue */ +u16 mpt2sas_base_get_smid_hpr(struct MPT2SAS_ADAPTER *ioc, u8 cb_idx); +u16 mpt2sas_base_get_smid_scsiio(struct MPT2SAS_ADAPTER *ioc, u8 cb_idx, + struct scsi_cmnd *scmd); u16 mpt2sas_base_get_smid(struct MPT2SAS_ADAPTER *ioc, u8 cb_idx); void mpt2sas_base_free_smid(struct MPT2SAS_ADAPTER *ioc, u16 smid); -void mpt2sas_base_put_smid_scsi_io(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 vf_id, +void mpt2sas_base_put_smid_scsi_io(struct MPT2SAS_ADAPTER *ioc, u16 smid, u16 handle); -void mpt2sas_base_put_smid_hi_priority(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 vf_id); +void mpt2sas_base_put_smid_hi_priority(struct MPT2SAS_ADAPTER *ioc, u16 smid); void mpt2sas_base_put_smid_target_assist(struct MPT2SAS_ADAPTER *ioc, u16 smid, - u8 vf_id, u16 io_index); -void mpt2sas_base_put_smid_default(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 vf_id); + u16 io_index); +void mpt2sas_base_put_smid_default(struct MPT2SAS_ADAPTER *ioc, u16 smid); void mpt2sas_base_initialize_callback_handler(void); u8 mpt2sas_base_register_callback_handler(MPT_CALLBACK cb_func); void mpt2sas_base_release_callback_handler(u8 cb_idx); -void mpt2sas_base_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 VF_ID, u32 reply); +u8 mpt2sas_base_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 msix_index, + u32 reply); void *mpt2sas_base_get_reply_virt_addr(struct MPT2SAS_ADAPTER *ioc, u32 phys_addr); u32 mpt2sas_base_get_iocstate(struct MPT2SAS_ADAPTER *ioc, int cooked); @@ -749,6 +808,8 @@ int mpt2sas_base_scsi_enclosure_processor(struct MPT2SAS_ADAPTER *ioc, void mpt2sas_base_validate_event_type(struct MPT2SAS_ADAPTER *ioc, u32 *event_type); /* scsih shared API */ +u8 mpt2sas_scsih_event_callback(struct MPT2SAS_ADAPTER *ioc, u8 msix_index, + u32 reply); void mpt2sas_scsih_issue_tm(struct MPT2SAS_ADAPTER *ioc, u16 handle, uint lun, u8 type, u16 smid_task, ulong timeout); void mpt2sas_scsih_set_tm_flag(struct MPT2SAS_ADAPTER *ioc, u16 handle); @@ -760,11 +821,11 @@ struct _sas_node *mpt2sas_scsih_expander_find_by_sas_address(struct MPT2SAS_ADAP struct _sas_device *mpt2sas_scsih_sas_device_find_by_sas_address( struct MPT2SAS_ADAPTER *ioc, u64 sas_address); -void mpt2sas_scsih_event_callback(struct MPT2SAS_ADAPTER *ioc, u8 VF_ID, u32 reply); void mpt2sas_scsih_reset_handler(struct MPT2SAS_ADAPTER *ioc, int reset_phase); /* config shared API */ -void mpt2sas_config_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 VF_ID, u32 reply); +u8 mpt2sas_config_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 msix_index, + u32 reply); int mpt2sas_config_get_number_hba_phys(struct MPT2SAS_ADAPTER *ioc, u8 *num_phys); int mpt2sas_config_get_manufacturing_pg0(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t *mpi_reply, Mpi2ManufacturingPage0_t *config_page); @@ -817,14 +878,17 @@ extern struct device_attribute *mpt2sas_host_attrs[]; extern struct device_attribute *mpt2sas_dev_attrs[]; void mpt2sas_ctl_init(void); void mpt2sas_ctl_exit(void); -void mpt2sas_ctl_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 VF_ID, u32 reply); +u8 mpt2sas_ctl_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 msix_index, + u32 reply); void mpt2sas_ctl_reset_handler(struct MPT2SAS_ADAPTER *ioc, int reset_phase); -void mpt2sas_ctl_event_callback(struct MPT2SAS_ADAPTER *ioc, u8 VF_ID, u32 reply); +u8 mpt2sas_ctl_event_callback(struct MPT2SAS_ADAPTER *ioc, u8 msix_index, + u32 reply); void mpt2sas_ctl_add_to_event_log(struct MPT2SAS_ADAPTER *ioc, Mpi2EventNotificationReply_t *mpi_reply); /* transport shared API */ -void mpt2sas_transport_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 VF_ID, u32 reply); +u8 mpt2sas_transport_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 msix_index, + u32 reply); struct _sas_port *mpt2sas_transport_port_add(struct MPT2SAS_ADAPTER *ioc, u16 handle, u16 parent_handle); void mpt2sas_transport_port_remove(struct MPT2SAS_ADAPTER *ioc, u64 sas_address, @@ -838,6 +902,8 @@ void mpt2sas_transport_update_links(struct MPT2SAS_ADAPTER *ioc, u16 handle, extern struct sas_function_template mpt2sas_transport_functions; extern struct scsi_transport_template *mpt2sas_transport_template; extern int scsi_internal_device_block(struct scsi_device *sdev); +extern u8 mpt2sas_stm_zero_smid_handler(struct MPT2SAS_ADAPTER *ioc, + u8 msix_index, u32 reply); extern int scsi_internal_device_unblock(struct scsi_device *sdev); #endif /* MPT2SAS_BASE_H_INCLUDED */ diff --git a/drivers/scsi/mpt2sas/mpt2sas_config.c b/drivers/scsi/mpt2sas/mpt2sas_config.c index ab8c560865d8..594a389c6526 100644 --- a/drivers/scsi/mpt2sas/mpt2sas_config.c +++ b/drivers/scsi/mpt2sas/mpt2sas_config.c @@ -2,7 +2,7 @@ * This module provides common API for accessing firmware configuration pages * * This code is based on drivers/scsi/mpt2sas/mpt2_base.c - * Copyright (C) 2007-2008 LSI Corporation + * Copyright (C) 2007-2009 LSI Corporation * (mailto:DL-MPTFusionLinux@lsi.com) * * This program is free software; you can redistribute it and/or @@ -227,23 +227,25 @@ _config_free_config_dma_memory(struct MPT2SAS_ADAPTER *ioc, * mpt2sas_config_done - config page completion routine * @ioc: per adapter object * @smid: system request message index - * @VF_ID: virtual function id + * @msix_index: MSIX table index supplied by the OS * @reply: reply message frame(lower 32bit addr) * Context: none. * * The callback handler when using _config_request. * - * Return nothing. + * Return 1 meaning mf should be freed from _base_interrupt + * 0 means the mf is freed from this function. */ -void -mpt2sas_config_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 VF_ID, u32 reply) +u8 +mpt2sas_config_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 msix_index, + u32 reply) { MPI2DefaultReply_t *mpi_reply; if (ioc->config_cmds.status == MPT2_CMD_NOT_USED) - return; + return 1; if (ioc->config_cmds.smid != smid) - return; + return 1; ioc->config_cmds.status |= MPT2_CMD_COMPLETE; mpi_reply = mpt2sas_base_get_reply_virt_addr(ioc, reply); if (mpi_reply) { @@ -257,6 +259,7 @@ mpt2sas_config_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 VF_ID, u32 reply) #endif ioc->config_cmds.smid = USHORT_MAX; complete(&ioc->config_cmds.done); + return 1; } /** @@ -303,6 +306,9 @@ _config_request(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigRequest_t retry_count = 0; memset(&mem, 0, sizeof(struct config_request)); + mpi_request->VF_ID = 0; /* TODO */ + mpi_request->VP_ID = 0; + if (config_page) { mpi_request->Header.PageVersion = mpi_reply->Header.PageVersion; mpi_request->Header.PageNumber = mpi_reply->Header.PageNumber; @@ -380,7 +386,7 @@ _config_request(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigRequest_t _config_display_some_debug(ioc, smid, "config_request", NULL); #endif init_completion(&ioc->config_cmds.done); - mpt2sas_base_put_smid_default(ioc, smid, config_request->VF_ID); + mpt2sas_base_put_smid_default(ioc, smid); timeleft = wait_for_completion_timeout(&ioc->config_cmds.done, timeout*HZ); if (!(ioc->config_cmds.status & MPT2_CMD_COMPLETE)) { diff --git a/drivers/scsi/mpt2sas/mpt2sas_ctl.c b/drivers/scsi/mpt2sas/mpt2sas_ctl.c index c2a51018910f..57d724633906 100644 --- a/drivers/scsi/mpt2sas/mpt2sas_ctl.c +++ b/drivers/scsi/mpt2sas/mpt2sas_ctl.c @@ -3,7 +3,7 @@ * controllers * * This code is based on drivers/scsi/mpt2sas/mpt2_ctl.c - * Copyright (C) 2007-2008 LSI Corporation + * Copyright (C) 2007-2009 LSI Corporation * (mailto:DL-MPTFusionLinux@lsi.com) * * This program is free software; you can redistribute it and/or @@ -219,23 +219,25 @@ _ctl_display_some_debug(struct MPT2SAS_ADAPTER *ioc, u16 smid, * mpt2sas_ctl_done - ctl module completion routine * @ioc: per adapter object * @smid: system request message index - * @VF_ID: virtual function id + * @msix_index: MSIX table index supplied by the OS * @reply: reply message frame(lower 32bit addr) * Context: none. * * The callback handler when using ioc->ctl_cb_idx. * - * Return nothing. + * Return 1 meaning mf should be freed from _base_interrupt + * 0 means the mf is freed from this function. */ -void -mpt2sas_ctl_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 VF_ID, u32 reply) +u8 +mpt2sas_ctl_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 msix_index, + u32 reply) { MPI2DefaultReply_t *mpi_reply; if (ioc->ctl_cmds.status == MPT2_CMD_NOT_USED) - return; + return 1; if (ioc->ctl_cmds.smid != smid) - return; + return 1; ioc->ctl_cmds.status |= MPT2_CMD_COMPLETE; mpi_reply = mpt2sas_base_get_reply_virt_addr(ioc, reply); if (mpi_reply) { @@ -247,6 +249,7 @@ mpt2sas_ctl_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 VF_ID, u32 reply) #endif ioc->ctl_cmds.status &= ~MPT2_CMD_PENDING; complete(&ioc->ctl_cmds.done); + return 1; } /** @@ -328,22 +331,25 @@ mpt2sas_ctl_add_to_event_log(struct MPT2SAS_ADAPTER *ioc, /** * mpt2sas_ctl_event_callback - firmware event handler (called at ISR time) * @ioc: per adapter object - * @VF_ID: virtual function id + * @msix_index: MSIX table index supplied by the OS * @reply: reply message frame(lower 32bit addr) * Context: interrupt. * * This function merely adds a new work task into ioc->firmware_event_thread. * The tasks are worked from _firmware_event_work in user context. * - * Return nothing. + * Return 1 meaning mf should be freed from _base_interrupt + * 0 means the mf is freed from this function. */ -void -mpt2sas_ctl_event_callback(struct MPT2SAS_ADAPTER *ioc, u8 VF_ID, u32 reply) +u8 +mpt2sas_ctl_event_callback(struct MPT2SAS_ADAPTER *ioc, u8 msix_index, + u32 reply) { Mpi2EventNotificationReply_t *mpi_reply; mpi_reply = mpt2sas_base_get_reply_virt_addr(ioc, reply); mpt2sas_ctl_add_to_event_log(ioc, mpi_reply); + return 1; } /** @@ -507,7 +513,7 @@ _ctl_set_task_mid(struct MPT2SAS_ADAPTER *ioc, struct mpt2_ioctl_command *karg, handle = le16_to_cpu(tm_request->DevHandle); spin_lock_irqsave(&ioc->scsi_lookup_lock, flags); - for (i = ioc->request_depth; i && !found; i--) { + for (i = ioc->scsiio_depth; i && !found; i--) { scmd = ioc->scsi_lookup[i - 1].scmd; if (scmd == NULL || scmd->device == NULL || scmd->device->hostdata == NULL) @@ -614,7 +620,7 @@ _ctl_do_mpt_command(struct MPT2SAS_ADAPTER *ioc, printk(MPT2SAS_INFO_FMT "%s: ioc is operational\n", ioc->name, __func__); - smid = mpt2sas_base_get_smid(ioc, ioc->ctl_cb_idx); + smid = mpt2sas_base_get_smid_scsiio(ioc, ioc->ctl_cb_idx, NULL); if (!smid) { printk(MPT2SAS_ERR_FMT "%s: failed obtaining a smid\n", ioc->name, __func__); @@ -737,7 +743,7 @@ _ctl_do_mpt_command(struct MPT2SAS_ADAPTER *ioc, (u32)mpt2sas_base_get_sense_buffer_dma(ioc, smid); priv_sense = mpt2sas_base_get_sense_buffer(ioc, smid); memset(priv_sense, 0, SCSI_SENSE_BUFFERSIZE); - mpt2sas_base_put_smid_scsi_io(ioc, smid, 0, + mpt2sas_base_put_smid_scsi_io(ioc, smid, le16_to_cpu(mpi_request->FunctionDependent1)); break; } @@ -759,8 +765,7 @@ _ctl_do_mpt_command(struct MPT2SAS_ADAPTER *ioc, mutex_lock(&ioc->tm_cmds.mutex); mpt2sas_scsih_set_tm_flag(ioc, le16_to_cpu( tm_request->DevHandle)); - mpt2sas_base_put_smid_hi_priority(ioc, smid, - mpi_request->VF_ID); + mpt2sas_base_put_smid_hi_priority(ioc, smid); break; } case MPI2_FUNCTION_SMP_PASSTHROUGH: @@ -781,7 +786,7 @@ _ctl_do_mpt_command(struct MPT2SAS_ADAPTER *ioc, ioc->ioc_link_reset_in_progress = 1; ioc->ignore_loginfos = 1; } - mpt2sas_base_put_smid_default(ioc, smid, mpi_request->VF_ID); + mpt2sas_base_put_smid_default(ioc, smid); break; } case MPI2_FUNCTION_SAS_IO_UNIT_CONTROL: @@ -795,11 +800,11 @@ _ctl_do_mpt_command(struct MPT2SAS_ADAPTER *ioc, ioc->ioc_link_reset_in_progress = 1; ioc->ignore_loginfos = 1; } - mpt2sas_base_put_smid_default(ioc, smid, mpi_request->VF_ID); + mpt2sas_base_put_smid_default(ioc, smid); break; } default: - mpt2sas_base_put_smid_default(ioc, smid, mpi_request->VF_ID); + mpt2sas_base_put_smid_default(ioc, smid); break; } @@ -807,6 +812,7 @@ _ctl_do_mpt_command(struct MPT2SAS_ADAPTER *ioc, timeout = MPT2_IOCTL_DEFAULT_TIMEOUT; else timeout = karg.timeout; + init_completion(&ioc->ctl_cmds.done); timeleft = wait_for_completion_timeout(&ioc->ctl_cmds.done, timeout*HZ); if (mpi_request->Function == MPI2_FUNCTION_SCSI_TASK_MGMT) { @@ -1371,6 +1377,8 @@ _ctl_diag_register(void __user *arg, enum block_state state) mpi_request->Flags = cpu_to_le32(karg.diagnostic_flags); mpi_request->BufferAddress = cpu_to_le64(request_data_dma); mpi_request->BufferLength = cpu_to_le32(request_data_sz); + mpi_request->VF_ID = 0; /* TODO */ + mpi_request->VP_ID = 0; dctlprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s: diag_buffer(0x%p), " "dma(0x%llx), sz(%d)\n", ioc->name, __func__, request_data, @@ -1380,7 +1388,8 @@ _ctl_diag_register(void __user *arg, enum block_state state) mpi_request->ProductSpecific[i] = cpu_to_le32(ioc->product_specific[buffer_type][i]); - mpt2sas_base_put_smid_default(ioc, smid, mpi_request->VF_ID); + mpt2sas_base_put_smid_default(ioc, smid); + init_completion(&ioc->ctl_cmds.done); timeleft = wait_for_completion_timeout(&ioc->ctl_cmds.done, MPT2_IOCTL_DEFAULT_TIMEOUT*HZ); @@ -1643,8 +1652,11 @@ _ctl_send_release(struct MPT2SAS_ADAPTER *ioc, u8 buffer_type, u8 *issue_reset) mpi_request->Function = MPI2_FUNCTION_DIAG_RELEASE; mpi_request->BufferType = buffer_type; + mpi_request->VF_ID = 0; /* TODO */ + mpi_request->VP_ID = 0; - mpt2sas_base_put_smid_default(ioc, smid, mpi_request->VF_ID); + mpt2sas_base_put_smid_default(ioc, smid); + init_completion(&ioc->ctl_cmds.done); timeleft = wait_for_completion_timeout(&ioc->ctl_cmds.done, MPT2_IOCTL_DEFAULT_TIMEOUT*HZ); @@ -1902,8 +1914,11 @@ _ctl_diag_read_buffer(void __user *arg, enum block_state state) for (i = 0; i < MPT2_PRODUCT_SPECIFIC_DWORDS; i++) mpi_request->ProductSpecific[i] = cpu_to_le32(ioc->product_specific[buffer_type][i]); + mpi_request->VF_ID = 0; /* TODO */ + mpi_request->VP_ID = 0; - mpt2sas_base_put_smid_default(ioc, smid, mpi_request->VF_ID); + mpt2sas_base_put_smid_default(ioc, smid); + init_completion(&ioc->ctl_cmds.done); timeleft = wait_for_completion_timeout(&ioc->ctl_cmds.done, MPT2_IOCTL_DEFAULT_TIMEOUT*HZ); @@ -2069,6 +2084,7 @@ static long _ctl_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { long ret; + lock_kernel(); ret = _ctl_ioctl_main(file, cmd, (void __user *)arg); unlock_kernel(); @@ -2143,6 +2159,7 @@ static long _ctl_ioctl_compat(struct file *file, unsigned cmd, unsigned long arg) { long ret; + lock_kernel(); if (cmd == MPT2COMMAND32) ret = _ctl_compat_mpt_command(file, cmd, arg); diff --git a/drivers/scsi/mpt2sas/mpt2sas_ctl.h b/drivers/scsi/mpt2sas/mpt2sas_ctl.h index 4da11435533f..211f296dd191 100644 --- a/drivers/scsi/mpt2sas/mpt2sas_ctl.h +++ b/drivers/scsi/mpt2sas/mpt2sas_ctl.h @@ -3,7 +3,7 @@ * controllers * * This code is based on drivers/scsi/mpt2sas/mpt2_ctl.h - * Copyright (C) 2007-2008 LSI Corporation + * Copyright (C) 2007-2009 LSI Corporation * (mailto:DL-MPTFusionLinux@lsi.com) * * This program is free software; you can redistribute it and/or diff --git a/drivers/scsi/mpt2sas/mpt2sas_debug.h b/drivers/scsi/mpt2sas/mpt2sas_debug.h index ad325096e842..5308a25cb307 100644 --- a/drivers/scsi/mpt2sas/mpt2sas_debug.h +++ b/drivers/scsi/mpt2sas/mpt2sas_debug.h @@ -2,7 +2,7 @@ * Logging Support for MPT (Message Passing Technology) based controllers * * This code is based on drivers/scsi/mpt2sas/mpt2_debug.c - * Copyright (C) 2007-2008 LSI Corporation + * Copyright (C) 2007-2009 LSI Corporation * (mailto:DL-MPTFusionLinux@lsi.com) * * This program is free software; you can redistribute it and/or diff --git a/drivers/scsi/mpt2sas/mpt2sas_scsih.c b/drivers/scsi/mpt2sas/mpt2sas_scsih.c index 774b34525bba..86ab32d7ab15 100644 --- a/drivers/scsi/mpt2sas/mpt2sas_scsih.c +++ b/drivers/scsi/mpt2sas/mpt2sas_scsih.c @@ -2,7 +2,7 @@ * Scsi Host Layer for MPT (Message Passing Technology) based controllers * * This code is based on drivers/scsi/mpt2sas/mpt2_scsih.c - * Copyright (C) 2007-2008 LSI Corporation + * Copyright (C) 2007-2009 LSI Corporation * (mailto:DL-MPTFusionLinux@lsi.com) * * This program is free software; you can redistribute it and/or @@ -79,6 +79,9 @@ static u8 transport_cb_idx = -1; static u8 config_cb_idx = -1; static int mpt_ids; +static u8 tm_tr_cb_idx = -1 ; +static u8 tm_sas_control_cb_idx = -1; + /* command line options */ static u32 logging_level; MODULE_PARM_DESC(logging_level, " bits for enabling additional logging info " @@ -109,6 +112,7 @@ struct sense_info { * @work: work object (ioc->fault_reset_work_q) * @ioc: per adapter object * @VF_ID: virtual function id + * @VP_ID: virtual port id * @host_reset_handling: handling events during host reset * @ignore: flag meaning this event has been marked to ignore * @event: firmware event MPI2_EVENT_XXX defined in mpt2_ioc.h @@ -121,6 +125,7 @@ struct fw_event_work { struct work_struct work; struct MPT2SAS_ADAPTER *ioc; u8 VF_ID; + u8 VP_ID; u8 host_reset_handling; u8 ignore; u16 event; @@ -138,8 +143,10 @@ struct fw_event_work { * @lun: lun number * @cdb_length: cdb length * @cdb: cdb contents - * @valid_reply: flag set for reply message * @timeout: timeout for this command + * @VF_ID: virtual function id + * @VP_ID: virtual port id + * @valid_reply: flag set for reply message * @sense_length: sense length * @ioc_status: ioc status * @scsi_state: scsi state @@ -161,6 +168,8 @@ struct _scsi_io_transfer { u8 cdb_length; u8 cdb[32]; u8 timeout; + u8 VF_ID; + u8 VP_ID; u8 valid_reply; /* the following bits are only valid when 'valid_reply = 1' */ u32 sense_length; @@ -756,66 +765,16 @@ _scsih_is_end_device(u32 device_info) } /** - * _scsih_scsi_lookup_get - returns scmd entry + * mptscsih_get_scsi_lookup - returns scmd entry * @ioc: per adapter object * @smid: system request message index - * Context: This function will acquire ioc->scsi_lookup_lock. * * Returns the smid stored scmd pointer. */ static struct scsi_cmnd * _scsih_scsi_lookup_get(struct MPT2SAS_ADAPTER *ioc, u16 smid) { - unsigned long flags; - struct scsi_cmnd *scmd; - - spin_lock_irqsave(&ioc->scsi_lookup_lock, flags); - scmd = ioc->scsi_lookup[smid - 1].scmd; - spin_unlock_irqrestore(&ioc->scsi_lookup_lock, flags); - return scmd; -} - -/** - * mptscsih_getclear_scsi_lookup - returns scmd entry - * @ioc: per adapter object - * @smid: system request message index - * Context: This function will acquire ioc->scsi_lookup_lock. - * - * Returns the smid stored scmd pointer, as well as clearing the scmd pointer. - */ -static struct scsi_cmnd * -_scsih_scsi_lookup_getclear(struct MPT2SAS_ADAPTER *ioc, u16 smid) -{ - unsigned long flags; - struct scsi_cmnd *scmd; - - spin_lock_irqsave(&ioc->scsi_lookup_lock, flags); - scmd = ioc->scsi_lookup[smid - 1].scmd; - ioc->scsi_lookup[smid - 1].scmd = NULL; - spin_unlock_irqrestore(&ioc->scsi_lookup_lock, flags); - return scmd; -} - -/** - * _scsih_scsi_lookup_set - updates scmd entry in lookup - * @ioc: per adapter object - * @smid: system request message index - * @scmd: pointer to scsi command object - * Context: This function will acquire ioc->scsi_lookup_lock. - * - * This will save scmd pointer in the scsi_lookup array. - * - * Return nothing. - */ -static void -_scsih_scsi_lookup_set(struct MPT2SAS_ADAPTER *ioc, u16 smid, - struct scsi_cmnd *scmd) -{ - unsigned long flags; - - spin_lock_irqsave(&ioc->scsi_lookup_lock, flags); - ioc->scsi_lookup[smid - 1].scmd = scmd; - spin_unlock_irqrestore(&ioc->scsi_lookup_lock, flags); + return ioc->scsi_lookup[smid - 1].scmd; } /** @@ -838,9 +797,9 @@ _scsih_scsi_lookup_find_by_scmd(struct MPT2SAS_ADAPTER *ioc, struct scsi_cmnd spin_lock_irqsave(&ioc->scsi_lookup_lock, flags); smid = 0; - for (i = 0; i < ioc->request_depth; i++) { + for (i = 0; i < ioc->scsiio_depth; i++) { if (ioc->scsi_lookup[i].scmd == scmd) { - smid = i + 1; + smid = ioc->scsi_lookup[i].smid; goto out; } } @@ -869,7 +828,7 @@ _scsih_scsi_lookup_find_by_target(struct MPT2SAS_ADAPTER *ioc, int id, spin_lock_irqsave(&ioc->scsi_lookup_lock, flags); found = 0; - for (i = 0 ; i < ioc->request_depth; i++) { + for (i = 0 ; i < ioc->scsiio_depth; i++) { if (ioc->scsi_lookup[i].scmd && (ioc->scsi_lookup[i].scmd->device->id == id && ioc->scsi_lookup[i].scmd->device->channel == channel)) { @@ -903,7 +862,7 @@ _scsih_scsi_lookup_find_by_lun(struct MPT2SAS_ADAPTER *ioc, int id, spin_lock_irqsave(&ioc->scsi_lookup_lock, flags); found = 0; - for (i = 0 ; i < ioc->request_depth; i++) { + for (i = 0 ; i < ioc->scsiio_depth; i++) { if (ioc->scsi_lookup[i].scmd && (ioc->scsi_lookup[i].scmd->device->id == id && ioc->scsi_lookup[i].scmd->device->channel == channel && @@ -1113,7 +1072,7 @@ _scsih_change_queue_depth(struct scsi_device *sdev, int qdepth) } /** - * _scsih_change_queue_depth - changing device queue tag type + * _scsih_change_queue_type - changing device queue tag type * @sdev: scsi device struct * @tag_type: requested tag type * @@ -1679,23 +1638,24 @@ _scsih_response_code(struct MPT2SAS_ADAPTER *ioc, u8 response_code) * _scsih_tm_done - tm completion routine * @ioc: per adapter object * @smid: system request message index - * @VF_ID: virtual function id + * @msix_index: MSIX table index supplied by the OS * @reply: reply message frame(lower 32bit addr) * Context: none. * * The callback handler when using scsih_issue_tm. * - * Return nothing. + * Return 1 meaning mf should be freed from _base_interrupt + * 0 means the mf is freed from this function. */ -static void -_scsih_tm_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 VF_ID, u32 reply) +static u8 +_scsih_tm_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 msix_index, u32 reply) { MPI2DefaultReply_t *mpi_reply; if (ioc->tm_cmds.status == MPT2_CMD_NOT_USED) - return; + return 1; if (ioc->tm_cmds.smid != smid) - return; + return 1; ioc->tm_cmds.status |= MPT2_CMD_COMPLETE; mpi_reply = mpt2sas_base_get_reply_virt_addr(ioc, reply); if (mpi_reply) { @@ -1704,6 +1664,7 @@ _scsih_tm_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 VF_ID, u32 reply) } ioc->tm_cmds.status &= ~MPT2_CMD_PENDING; complete(&ioc->tm_cmds.done); + return 1; } /** @@ -1790,7 +1751,6 @@ mpt2sas_scsih_issue_tm(struct MPT2SAS_ADAPTER *ioc, u16 handle, uint lun, u16 smid = 0; u32 ioc_state; unsigned long timeleft; - u8 VF_ID = 0; if (ioc->tm_cmds.status != MPT2_CMD_NOT_USED) { printk(MPT2SAS_INFO_FMT "%s: tm_cmd busy!!!\n", @@ -1817,7 +1777,7 @@ mpt2sas_scsih_issue_tm(struct MPT2SAS_ADAPTER *ioc, u16 handle, uint lun, goto issue_host_reset; } - smid = mpt2sas_base_get_smid(ioc, ioc->tm_cb_idx); + smid = mpt2sas_base_get_smid_hpr(ioc, ioc->tm_cb_idx); if (!smid) { printk(MPT2SAS_ERR_FMT "%s: failed obtaining a smid\n", ioc->name, __func__); @@ -1825,7 +1785,8 @@ mpt2sas_scsih_issue_tm(struct MPT2SAS_ADAPTER *ioc, u16 handle, uint lun, } dtmprintk(ioc, printk(MPT2SAS_INFO_FMT "sending tm: handle(0x%04x)," - " task_type(0x%02x), smid(%d)\n", ioc->name, handle, type, smid)); + " task_type(0x%02x), smid(%d)\n", ioc->name, handle, type, + smid_task)); ioc->tm_cmds.status = MPT2_CMD_PENDING; mpi_request = mpt2sas_base_get_msg_frame(ioc, smid); ioc->tm_cmds.smid = smid; @@ -1834,10 +1795,12 @@ mpt2sas_scsih_issue_tm(struct MPT2SAS_ADAPTER *ioc, u16 handle, uint lun, mpi_request->DevHandle = cpu_to_le16(handle); mpi_request->TaskType = type; mpi_request->TaskMID = cpu_to_le16(smid_task); + mpi_request->VP_ID = 0; /* TODO */ + mpi_request->VF_ID = 0; int_to_scsilun(lun, (struct scsi_lun *)mpi_request->LUN); mpt2sas_scsih_set_tm_flag(ioc, handle); init_completion(&ioc->tm_cmds.done); - mpt2sas_base_put_smid_hi_priority(ioc, smid, VF_ID); + mpt2sas_base_put_smid_hi_priority(ioc, smid); timeleft = wait_for_completion_timeout(&ioc->tm_cmds.done, timeout*HZ); mpt2sas_scsih_clear_tm_flag(ioc, handle); if (!(ioc->tm_cmds.status & MPT2_CMD_COMPLETE)) { @@ -2075,7 +2038,7 @@ _scsih_target_reset(struct scsi_cmnd *scmd) } /** - * _scsih_abort - eh threads main host reset routine + * _scsih_host_reset - eh threads main host reset routine * @sdev: scsi device struct * * Returns SUCCESS if command aborted else FAILED @@ -2354,6 +2317,231 @@ _scsih_block_io_to_children_attached_directly(struct MPT2SAS_ADAPTER *ioc, } /** + * _scsih_tm_tr_send - send task management request + * @ioc: per adapter object + * @handle: device handle + * Context: interrupt time. + * + * This code is to initiate the device removal handshake protocal + * with controller firmware. This function will issue target reset + * using high priority request queue. It will send a sas iounit + * controll request (MPI2_SAS_OP_REMOVE_DEVICE) from this completion. + * + * This is designed to send muliple task management request at the same + * time to the fifo. If the fifo is full, we will append the request, + * and process it in a future completion. + */ +static void +_scsih_tm_tr_send(struct MPT2SAS_ADAPTER *ioc, u16 handle) +{ + Mpi2SCSITaskManagementRequest_t *mpi_request; + struct MPT2SAS_TARGET *sas_target_priv_data; + u16 smid; + struct _sas_device *sas_device; + unsigned long flags; + struct _tr_list *delayed_tr; + + if (ioc->shost_recovery) { + printk(MPT2SAS_INFO_FMT "%s: host reset in progress!\n", + __func__, ioc->name); + return; + } + + spin_lock_irqsave(&ioc->sas_device_lock, flags); + sas_device = _scsih_sas_device_find_by_handle(ioc, handle); + if (!sas_device) { + spin_unlock_irqrestore(&ioc->sas_device_lock, flags); + printk(MPT2SAS_ERR_FMT "%s: failed finding sas_device\n", + ioc->name, __func__); + return; + } + spin_unlock_irqrestore(&ioc->sas_device_lock, flags); + + /* skip is hidden raid component */ + if (sas_device->hidden_raid_component) + return; + + smid = mpt2sas_base_get_smid_hpr(ioc, ioc->tm_tr_cb_idx); + if (!smid) { + delayed_tr = kzalloc(sizeof(*delayed_tr), GFP_ATOMIC); + if (!delayed_tr) + return; + INIT_LIST_HEAD(&delayed_tr->list); + delayed_tr->handle = handle; + delayed_tr->state = MPT2SAS_REQ_SAS_CNTRL; + list_add_tail(&delayed_tr->list, + &ioc->delayed_tr_list); + if (sas_device->starget) + dewtprintk(ioc, starget_printk(KERN_INFO, + sas_device->starget, "DELAYED:tr:handle(0x%04x), " + "(open)\n", sas_device->handle)); + return; + } + + if (sas_device->starget && sas_device->starget->hostdata) { + sas_target_priv_data = sas_device->starget->hostdata; + sas_target_priv_data->tm_busy = 1; + dewtprintk(ioc, starget_printk(KERN_INFO, sas_device->starget, + "tr:handle(0x%04x), (open)\n", sas_device->handle)); + } + + mpi_request = mpt2sas_base_get_msg_frame(ioc, smid); + memset(mpi_request, 0, sizeof(Mpi2SCSITaskManagementRequest_t)); + mpi_request->Function = MPI2_FUNCTION_SCSI_TASK_MGMT; + mpi_request->DevHandle = cpu_to_le16(handle); + mpi_request->TaskType = MPI2_SCSITASKMGMT_TASKTYPE_TARGET_RESET; + sas_device->state |= MPTSAS_STATE_TR_SEND; + sas_device->state |= MPT2SAS_REQ_SAS_CNTRL; + mpt2sas_base_put_smid_hi_priority(ioc, smid); +} + + + +/** + * _scsih_sas_control_complete - completion routine + * @ioc: per adapter object + * @smid: system request message index + * @msix_index: MSIX table index supplied by the OS + * @reply: reply message frame(lower 32bit addr) + * Context: interrupt time. + * + * This is the sas iounit controll completion routine. + * This code is part of the code to initiate the device removal + * handshake protocal with controller firmware. + * + * Return 1 meaning mf should be freed from _base_interrupt + * 0 means the mf is freed from this function. + */ +static u8 +_scsih_sas_control_complete(struct MPT2SAS_ADAPTER *ioc, u16 smid, + u8 msix_index, u32 reply) +{ + unsigned long flags; + u16 handle; + struct _sas_device *sas_device; + Mpi2SasIoUnitControlReply_t *mpi_reply = + mpt2sas_base_get_reply_virt_addr(ioc, reply); + + handle = le16_to_cpu(mpi_reply->DevHandle); + + spin_lock_irqsave(&ioc->sas_device_lock, flags); + sas_device = _scsih_sas_device_find_by_handle(ioc, handle); + if (!sas_device) { + spin_unlock_irqrestore(&ioc->sas_device_lock, flags); + printk(MPT2SAS_ERR_FMT "%s: failed finding sas_device\n", + ioc->name, __func__); + return 1; + } + sas_device->state |= MPTSAS_STATE_CNTRL_COMPLETE; + spin_unlock_irqrestore(&ioc->sas_device_lock, flags); + + if (sas_device->starget) + dewtprintk(ioc, starget_printk(KERN_INFO, sas_device->starget, + "sc_complete:handle(0x%04x), " + "ioc_status(0x%04x), loginfo(0x%08x)\n", + handle, le16_to_cpu(mpi_reply->IOCStatus), + le32_to_cpu(mpi_reply->IOCLogInfo))); + return 1; +} + +/** + * _scsih_tm_tr_complete - + * @ioc: per adapter object + * @smid: system request message index + * @msix_index: MSIX table index supplied by the OS + * @reply: reply message frame(lower 32bit addr) + * Context: interrupt time. + * + * This is the target reset completion routine. + * This code is part of the code to initiate the device removal + * handshake protocal with controller firmware. + * It will send a sas iounit controll request (MPI2_SAS_OP_REMOVE_DEVICE) + * + * Return 1 meaning mf should be freed from _base_interrupt + * 0 means the mf is freed from this function. + */ +static u8 +_scsih_tm_tr_complete(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 msix_index, + u32 reply) +{ + unsigned long flags; + u16 handle; + struct _sas_device *sas_device; + Mpi2SCSITaskManagementReply_t *mpi_reply = + mpt2sas_base_get_reply_virt_addr(ioc, reply); + Mpi2SasIoUnitControlRequest_t *mpi_request; + u16 smid_sas_ctrl; + struct MPT2SAS_TARGET *sas_target_priv_data; + struct _tr_list *delayed_tr; + u8 rc; + + handle = le16_to_cpu(mpi_reply->DevHandle); + spin_lock_irqsave(&ioc->sas_device_lock, flags); + sas_device = _scsih_sas_device_find_by_handle(ioc, handle); + if (!sas_device) { + spin_unlock_irqrestore(&ioc->sas_device_lock, flags); + printk(MPT2SAS_ERR_FMT "%s: failed finding sas_device\n", + ioc->name, __func__); + return 1; + } + sas_device->state |= MPTSAS_STATE_TR_COMPLETE; + spin_unlock_irqrestore(&ioc->sas_device_lock, flags); + + if (sas_device->starget) + dewtprintk(ioc, starget_printk(KERN_INFO, sas_device->starget, + "tr_complete:handle(0x%04x), (%s) ioc_status(0x%04x), " + "loginfo(0x%08x), completed(%d)\n", + sas_device->handle, (sas_device->state & + MPT2SAS_REQ_SAS_CNTRL) ? "open" : "active", + le16_to_cpu(mpi_reply->IOCStatus), + le32_to_cpu(mpi_reply->IOCLogInfo), + le32_to_cpu(mpi_reply->TerminationCount))); + + if (sas_device->starget && sas_device->starget->hostdata) { + sas_target_priv_data = sas_device->starget->hostdata; + sas_target_priv_data->tm_busy = 0; + } + + if (!list_empty(&ioc->delayed_tr_list)) { + delayed_tr = list_entry(ioc->delayed_tr_list.next, + struct _tr_list, list); + mpt2sas_base_free_smid(ioc, smid); + if (delayed_tr->state & MPT2SAS_REQ_SAS_CNTRL) + _scsih_tm_tr_send(ioc, delayed_tr->handle); + list_del(&delayed_tr->list); + kfree(delayed_tr); + rc = 0; /* tells base_interrupt not to free mf */ + } else + rc = 1; + + + if (!(sas_device->state & MPT2SAS_REQ_SAS_CNTRL)) + return rc; + + if (ioc->shost_recovery) { + printk(MPT2SAS_INFO_FMT "%s: host reset in progress!\n", + __func__, ioc->name); + return rc; + } + + smid_sas_ctrl = mpt2sas_base_get_smid(ioc, ioc->tm_sas_control_cb_idx); + if (!smid_sas_ctrl) { + printk(MPT2SAS_ERR_FMT "%s: failed obtaining a smid\n", + ioc->name, __func__); + return rc; + } + + mpi_request = mpt2sas_base_get_msg_frame(ioc, smid_sas_ctrl); + memset(mpi_request, 0, sizeof(Mpi2SasIoUnitControlRequest_t)); + mpi_request->Function = MPI2_FUNCTION_SAS_IO_UNIT_CONTROL; + mpi_request->Operation = MPI2_SAS_OP_REMOVE_DEVICE; + mpi_request->DevHandle = mpi_reply->DevHandle; + sas_device->state |= MPTSAS_STATE_CNTRL_SEND; + mpt2sas_base_put_smid_default(ioc, smid_sas_ctrl); + return rc; +} + +/** * _scsih_check_topo_delete_events - sanity check on topo events * @ioc: per adapter object * @event_data: the event data payload @@ -2375,6 +2563,21 @@ _scsih_check_topo_delete_events(struct MPT2SAS_ADAPTER *ioc, u16 expander_handle; struct _sas_node *sas_expander; unsigned long flags; + int i, reason_code; + u16 handle; + + for (i = 0 ; i < event_data->NumEntries; i++) { + if (event_data->PHY[i].PhyStatus & + MPI2_EVENT_SAS_TOPO_PHYSTATUS_VACANT) + continue; + handle = le16_to_cpu(event_data->PHY[i].AttachedDevHandle); + if (!handle) + continue; + reason_code = event_data->PHY[i].PhyStatus & + MPI2_EVENT_SAS_TOPO_RC_MASK; + if (reason_code == MPI2_EVENT_SAS_TOPO_RC_TARG_NOT_RESPONDING) + _scsih_tm_tr_send(ioc, handle); + } expander_handle = le16_to_cpu(event_data->ExpanderDevHandle); if (expander_handle < ioc->sas_hba.num_phys) { @@ -2433,8 +2636,8 @@ _scsih_flush_running_cmds(struct MPT2SAS_ADAPTER *ioc) u16 smid; u16 count = 0; - for (smid = 1; smid <= ioc->request_depth; smid++) { - scmd = _scsih_scsi_lookup_getclear(ioc, smid); + for (smid = 1; smid <= ioc->scsiio_depth; smid++) { + scmd = _scsih_scsi_lookup_get(ioc, smid); if (!scmd) continue; count++; @@ -2616,7 +2819,7 @@ _scsih_qcmd(struct scsi_cmnd *scmd, void (*done)(struct scsi_cmnd *)) if ((sas_device_priv_data->flags & MPT_DEVICE_TLR_ON)) mpi_control |= MPI2_SCSIIO_CONTROL_TLR_ON; - smid = mpt2sas_base_get_smid(ioc, ioc->scsi_io_cb_idx); + smid = mpt2sas_base_get_smid_scsiio(ioc, ioc->scsi_io_cb_idx, scmd); if (!smid) { printk(MPT2SAS_ERR_FMT "%s: failed obtaining a smid\n", ioc->name, __func__); @@ -2643,7 +2846,8 @@ _scsih_qcmd(struct scsi_cmnd *scmd, void (*done)(struct scsi_cmnd *)) mpi_request->SGLOffset0 = offsetof(Mpi2SCSIIORequest_t, SGL) / 4; mpi_request->SGLFlags = cpu_to_le16(MPI2_SCSIIO_SGLFLAGS_TYPE_MPI + MPI2_SCSIIO_SGLFLAGS_SYSTEM_ADDR); - + mpi_request->VF_ID = 0; /* TODO */ + mpi_request->VP_ID = 0; int_to_scsilun(sas_device_priv_data->lun, (struct scsi_lun *) mpi_request->LUN); memcpy(mpi_request->CDB.CDB32, scmd->cmnd, scmd->cmd_len); @@ -2657,8 +2861,7 @@ _scsih_qcmd(struct scsi_cmnd *scmd, void (*done)(struct scsi_cmnd *)) } } - _scsih_scsi_lookup_set(ioc, smid, scmd); - mpt2sas_base_put_smid_scsi_io(ioc, smid, 0, + mpt2sas_base_put_smid_scsi_io(ioc, smid, sas_device_priv_data->sas_target->handle); return 0; @@ -2954,15 +3157,16 @@ _scsih_smart_predicted_fault(struct MPT2SAS_ADAPTER *ioc, u16 handle) * _scsih_io_done - scsi request callback * @ioc: per adapter object * @smid: system request message index - * @VF_ID: virtual function id + * @msix_index: MSIX table index supplied by the OS * @reply: reply message frame(lower 32bit addr) * - * Callback handler when using scsih_qcmd. + * Callback handler when using _scsih_qcmd. * - * Return nothing. + * Return 1 meaning mf should be freed from _base_interrupt + * 0 means the mf is freed from this function. */ -static void -_scsih_io_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 VF_ID, u32 reply) +static u8 +_scsih_io_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 msix_index, u32 reply) { Mpi2SCSIIORequest_t *mpi_request; Mpi2SCSIIOReply_t *mpi_reply; @@ -2976,9 +3180,9 @@ _scsih_io_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 VF_ID, u32 reply) u32 response_code; mpi_reply = mpt2sas_base_get_reply_virt_addr(ioc, reply); - scmd = _scsih_scsi_lookup_getclear(ioc, smid); + scmd = _scsih_scsi_lookup_get(ioc, smid); if (scmd == NULL) - return; + return 1; mpi_request = mpt2sas_base_get_msg_frame(ioc, smid); @@ -3134,6 +3338,7 @@ _scsih_io_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 VF_ID, u32 reply) out: scsi_dma_unmap(scmd); scmd->scsi_done(scmd); + return 1; } /** @@ -3398,9 +3603,8 @@ _scsih_expander_add(struct MPT2SAS_ADAPTER *ioc, u16 handle) } } - sas_address = le64_to_cpu(expander_pg0.SASAddress); - spin_lock_irqsave(&ioc->sas_node_lock, flags); + sas_address = le64_to_cpu(expander_pg0.SASAddress); sas_expander = mpt2sas_scsih_expander_find_by_sas_address(ioc, sas_address); spin_unlock_irqrestore(&ioc->sas_node_lock, flags); @@ -3666,6 +3870,12 @@ _scsih_remove_device(struct MPT2SAS_ADAPTER *ioc, u16 handle) if (ioc->remove_host) goto out; + if ((sas_device->state & MPTSAS_STATE_TR_COMPLETE)) { + dewtprintk(ioc, printk(MPT2SAS_INFO_FMT "\tskip " + "target_reset handle(0x%04x)\n", ioc->name, handle)); + goto skip_tr; + } + /* Target Reset to flush out all the outstanding IO */ device_handle = (sas_device->hidden_raid_component) ? sas_device->volume_handle : handle; @@ -3682,6 +3892,13 @@ _scsih_remove_device(struct MPT2SAS_ADAPTER *ioc, u16 handle) if (ioc->shost_recovery) goto out; } + skip_tr: + + if ((sas_device->state & MPTSAS_STATE_CNTRL_COMPLETE)) { + dewtprintk(ioc, printk(MPT2SAS_INFO_FMT "\tskip " + "sas_cntrl handle(0x%04x)\n", ioc->name, handle)); + goto out; + } /* SAS_IO_UNIT_CNTR - send REMOVE_DEVICE */ dewtprintk(ioc, printk(MPT2SAS_INFO_FMT "sas_iounit: handle" @@ -3690,7 +3907,8 @@ _scsih_remove_device(struct MPT2SAS_ADAPTER *ioc, u16 handle) mpi_request.Function = MPI2_FUNCTION_SAS_IO_UNIT_CONTROL; mpi_request.Operation = MPI2_SAS_OP_REMOVE_DEVICE; mpi_request.DevHandle = handle; - mpi_request.VF_ID = 0; + mpi_request.VF_ID = 0; /* TODO */ + mpi_request.VP_ID = 0; if ((mpt2sas_base_sas_iounit_control(ioc, &mpi_reply, &mpi_request)) != 0) { printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n", @@ -3800,15 +4018,12 @@ _scsih_sas_topology_change_event_debug(struct MPT2SAS_ADAPTER *ioc, /** * _scsih_sas_topology_change_event - handle topology changes * @ioc: per adapter object - * @VF_ID: - * @event_data: event data payload - * fw_event: + * @fw_event: The fw_event_work object * Context: user. * */ static void -_scsih_sas_topology_change_event(struct MPT2SAS_ADAPTER *ioc, u8 VF_ID, - Mpi2EventDataSasTopologyChangeList_t *event_data, +_scsih_sas_topology_change_event(struct MPT2SAS_ADAPTER *ioc, struct fw_event_work *fw_event) { int i; @@ -3818,6 +4033,7 @@ _scsih_sas_topology_change_event(struct MPT2SAS_ADAPTER *ioc, u8 VF_ID, struct _sas_node *sas_expander; unsigned long flags; u8 link_rate_; + Mpi2EventDataSasTopologyChangeList_t *event_data = fw_event->event_data; #ifdef CONFIG_SCSI_MPT2SAS_LOGGING if (ioc->logging_level & MPT_DEBUG_EVENT_WORK_TASK) @@ -3851,15 +4067,16 @@ _scsih_sas_topology_change_event(struct MPT2SAS_ADAPTER *ioc, u8 VF_ID, } if (ioc->shost_recovery) return; - if (event_data->PHY[i].PhyStatus & - MPI2_EVENT_SAS_TOPO_PHYSTATUS_VACANT) + phy_number = event_data->StartPhyNum + i; + reason_code = event_data->PHY[i].PhyStatus & + MPI2_EVENT_SAS_TOPO_RC_MASK; + if ((event_data->PHY[i].PhyStatus & + MPI2_EVENT_SAS_TOPO_PHYSTATUS_VACANT) && (reason_code != + MPI2_EVENT_SAS_TOPO_RC_TARG_NOT_RESPONDING)) continue; handle = le16_to_cpu(event_data->PHY[i].AttachedDevHandle); if (!handle) continue; - phy_number = event_data->StartPhyNum + i; - reason_code = event_data->PHY[i].PhyStatus & - MPI2_EVENT_SAS_TOPO_RC_MASK; link_rate_ = event_data->PHY[i].LinkRate >> 4; switch (reason_code) { case MPI2_EVENT_SAS_TOPO_RC_PHY_CHANGED: @@ -3971,19 +4188,19 @@ _scsih_sas_device_status_change_event_debug(struct MPT2SAS_ADAPTER *ioc, /** * _scsih_sas_device_status_change_event - handle device status change * @ioc: per adapter object - * @VF_ID: - * @event_data: event data payload + * @fw_event: The fw_event_work object * Context: user. * * Return nothing. */ static void -_scsih_sas_device_status_change_event(struct MPT2SAS_ADAPTER *ioc, u8 VF_ID, - Mpi2EventDataSasDeviceStatusChange_t *event_data) +_scsih_sas_device_status_change_event(struct MPT2SAS_ADAPTER *ioc, + struct fw_event_work *fw_event) { #ifdef CONFIG_SCSI_MPT2SAS_LOGGING if (ioc->logging_level & MPT_DEBUG_EVENT_WORK_TASK) - _scsih_sas_device_status_change_event_debug(ioc, event_data); + _scsih_sas_device_status_change_event_debug(ioc, + fw_event->event_data); #endif } @@ -4026,34 +4243,33 @@ _scsih_sas_enclosure_dev_status_change_event_debug(struct MPT2SAS_ADAPTER *ioc, /** * _scsih_sas_enclosure_dev_status_change_event - handle enclosure events * @ioc: per adapter object - * @VF_ID: - * @event_data: event data payload + * @fw_event: The fw_event_work object * Context: user. * * Return nothing. */ static void _scsih_sas_enclosure_dev_status_change_event(struct MPT2SAS_ADAPTER *ioc, - u8 VF_ID, Mpi2EventDataSasEnclDevStatusChange_t *event_data) + struct fw_event_work *fw_event) { #ifdef CONFIG_SCSI_MPT2SAS_LOGGING if (ioc->logging_level & MPT_DEBUG_EVENT_WORK_TASK) _scsih_sas_enclosure_dev_status_change_event_debug(ioc, - event_data); + fw_event->event_data); #endif } /** * _scsih_sas_broadcast_primative_event - handle broadcast events * @ioc: per adapter object - * @event_data: event data payload + * @fw_event: The fw_event_work object * Context: user. * * Return nothing. */ static void -_scsih_sas_broadcast_primative_event(struct MPT2SAS_ADAPTER *ioc, u8 VF_ID, - Mpi2EventDataSasBroadcastPrimitive_t *event_data) +_scsih_sas_broadcast_primative_event(struct MPT2SAS_ADAPTER *ioc, + struct fw_event_work *fw_event) { struct scsi_cmnd *scmd; u16 smid, handle; @@ -4062,11 +4278,12 @@ _scsih_sas_broadcast_primative_event(struct MPT2SAS_ADAPTER *ioc, u8 VF_ID, u32 termination_count; u32 query_count; Mpi2SCSITaskManagementReply_t *mpi_reply; - +#ifdef CONFIG_SCSI_MPT2SAS_LOGGING + Mpi2EventDataSasBroadcastPrimitive_t *event_data = fw_event->event_data; +#endif dewtprintk(ioc, printk(MPT2SAS_DEBUG_FMT "broadcast primative: " "phy number(%d), width(%d)\n", ioc->name, event_data->PhyNum, event_data->PortWidth)); - dtmprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s: enter\n", ioc->name, __func__)); @@ -4074,7 +4291,7 @@ _scsih_sas_broadcast_primative_event(struct MPT2SAS_ADAPTER *ioc, u8 VF_ID, termination_count = 0; query_count = 0; mpi_reply = ioc->tm_cmds.reply; - for (smid = 1; smid <= ioc->request_depth; smid++) { + for (smid = 1; smid <= ioc->scsiio_depth; smid++) { scmd = _scsih_scsi_lookup_get(ioc, smid); if (!scmd) continue; @@ -4121,23 +4338,25 @@ _scsih_sas_broadcast_primative_event(struct MPT2SAS_ADAPTER *ioc, u8 VF_ID, /** * _scsih_sas_discovery_event - handle discovery events * @ioc: per adapter object - * @event_data: event data payload + * @fw_event: The fw_event_work object * Context: user. * * Return nothing. */ static void -_scsih_sas_discovery_event(struct MPT2SAS_ADAPTER *ioc, u8 VF_ID, - Mpi2EventDataSasDiscovery_t *event_data) +_scsih_sas_discovery_event(struct MPT2SAS_ADAPTER *ioc, + struct fw_event_work *fw_event) { + Mpi2EventDataSasDiscovery_t *event_data = fw_event->event_data; + #ifdef CONFIG_SCSI_MPT2SAS_LOGGING if (ioc->logging_level & MPT_DEBUG_EVENT_WORK_TASK) { printk(MPT2SAS_DEBUG_FMT "discovery event: (%s)", ioc->name, (event_data->ReasonCode == MPI2_EVENT_SAS_DISC_RC_STARTED) ? "start" : "stop"); if (event_data->DiscoveryStatus) - printk(MPT2SAS_DEBUG_FMT ", discovery_status(0x%08x)", - ioc->name, le32_to_cpu(event_data->DiscoveryStatus)); + printk("discovery_status(0x%08x)", + le32_to_cpu(event_data->DiscoveryStatus)); printk("\n"); } #endif @@ -4488,19 +4707,19 @@ _scsih_sas_ir_config_change_event_debug(struct MPT2SAS_ADAPTER *ioc, /** * _scsih_sas_ir_config_change_event - handle ir configuration change events * @ioc: per adapter object - * @VF_ID: - * @event_data: event data payload + * @fw_event: The fw_event_work object * Context: user. * * Return nothing. */ static void -_scsih_sas_ir_config_change_event(struct MPT2SAS_ADAPTER *ioc, u8 VF_ID, - Mpi2EventDataIrConfigChangeList_t *event_data) +_scsih_sas_ir_config_change_event(struct MPT2SAS_ADAPTER *ioc, + struct fw_event_work *fw_event) { Mpi2EventIrConfigElement_t *element; int i; u8 foreign_config; + Mpi2EventDataIrConfigChangeList_t *event_data = fw_event->event_data; #ifdef CONFIG_SCSI_MPT2SAS_LOGGING if (ioc->logging_level & MPT_DEBUG_EVENT_WORK_TASK) @@ -4543,14 +4762,14 @@ _scsih_sas_ir_config_change_event(struct MPT2SAS_ADAPTER *ioc, u8 VF_ID, /** * _scsih_sas_ir_volume_event - IR volume event * @ioc: per adapter object - * @event_data: event data payload + * @fw_event: The fw_event_work object * Context: user. * * Return nothing. */ static void -_scsih_sas_ir_volume_event(struct MPT2SAS_ADAPTER *ioc, u8 VF_ID, - Mpi2EventDataIrVolume_t *event_data) +_scsih_sas_ir_volume_event(struct MPT2SAS_ADAPTER *ioc, + struct fw_event_work *fw_event) { u64 wwid; unsigned long flags; @@ -4559,6 +4778,7 @@ _scsih_sas_ir_volume_event(struct MPT2SAS_ADAPTER *ioc, u8 VF_ID, u32 state; int rc; struct MPT2SAS_TARGET *sas_target_priv_data; + Mpi2EventDataIrVolume_t *event_data = fw_event->event_data; if (event_data->ReasonCode != MPI2_EVENT_IR_VOLUME_RC_STATE_CHANGED) return; @@ -4628,14 +4848,14 @@ _scsih_sas_ir_volume_event(struct MPT2SAS_ADAPTER *ioc, u8 VF_ID, /** * _scsih_sas_ir_physical_disk_event - PD event * @ioc: per adapter object - * @event_data: event data payload + * @fw_event: The fw_event_work object * Context: user. * * Return nothing. */ static void -_scsih_sas_ir_physical_disk_event(struct MPT2SAS_ADAPTER *ioc, u8 VF_ID, - Mpi2EventDataIrPhysicalDisk_t *event_data) +_scsih_sas_ir_physical_disk_event(struct MPT2SAS_ADAPTER *ioc, + struct fw_event_work *fw_event) { u16 handle; u32 state; @@ -4644,6 +4864,7 @@ _scsih_sas_ir_physical_disk_event(struct MPT2SAS_ADAPTER *ioc, u8 VF_ID, Mpi2ConfigReply_t mpi_reply; Mpi2SasDevicePage0_t sas_device_pg0; u32 ioc_status; + Mpi2EventDataIrPhysicalDisk_t *event_data = fw_event->event_data; if (event_data->ReasonCode != MPI2_EVENT_IR_PHYSDISK_RC_STATE_CHANGED) return; @@ -4743,33 +4964,33 @@ _scsih_sas_ir_operation_status_event_debug(struct MPT2SAS_ADAPTER *ioc, /** * _scsih_sas_ir_operation_status_event - handle RAID operation events * @ioc: per adapter object - * @VF_ID: - * @event_data: event data payload + * @fw_event: The fw_event_work object * Context: user. * * Return nothing. */ static void -_scsih_sas_ir_operation_status_event(struct MPT2SAS_ADAPTER *ioc, u8 VF_ID, - Mpi2EventDataIrOperationStatus_t *event_data) +_scsih_sas_ir_operation_status_event(struct MPT2SAS_ADAPTER *ioc, + struct fw_event_work *fw_event) { #ifdef CONFIG_SCSI_MPT2SAS_LOGGING if (ioc->logging_level & MPT_DEBUG_EVENT_WORK_TASK) - _scsih_sas_ir_operation_status_event_debug(ioc, event_data); + _scsih_sas_ir_operation_status_event_debug(ioc, + fw_event->event_data); #endif } /** * _scsih_task_set_full - handle task set full * @ioc: per adapter object - * @event_data: event data payload + * @fw_event: The fw_event_work object * Context: user. * * Throttle back qdepth. */ static void -_scsih_task_set_full(struct MPT2SAS_ADAPTER *ioc, u8 VF_ID, - Mpi2EventDataTaskSetFull_t *event_data) +_scsih_task_set_full(struct MPT2SAS_ADAPTER *ioc, struct fw_event_work + *fw_event) { unsigned long flags; struct _sas_device *sas_device; @@ -4780,6 +5001,7 @@ _scsih_task_set_full(struct MPT2SAS_ADAPTER *ioc, u8 VF_ID, u16 handle; int id, channel; u64 sas_address; + Mpi2EventDataTaskSetFull_t *event_data = fw_event->event_data; current_depth = le16_to_cpu(event_data->CurrentDepth); handle = le16_to_cpu(event_data->DevHandle); @@ -4868,6 +5090,10 @@ _scsih_mark_responding_sas_device(struct MPT2SAS_ADAPTER *ioc, u64 sas_address, if (sas_device->sas_address == sas_address && sas_device->slot == slot && sas_device->starget) { sas_device->responding = 1; + sas_device->state = 0; + starget = sas_device->starget; + sas_target_priv_data = starget->hostdata; + sas_target_priv_data->tm_busy = 0; starget_printk(KERN_INFO, sas_device->starget, "handle(0x%04x), sas_addr(0x%016llx), enclosure " "logical id(0x%016llx), slot(%d)\n", handle, @@ -4880,8 +5106,6 @@ _scsih_mark_responding_sas_device(struct MPT2SAS_ADAPTER *ioc, u64 sas_address, printk(KERN_INFO "\thandle changed from(0x%04x)!!!\n", sas_device->handle); sas_device->handle = handle; - starget = sas_device->starget; - sas_target_priv_data = starget->hostdata; sas_target_priv_data->handle = handle; goto out; } @@ -5227,44 +5451,38 @@ _firmware_event_work(struct work_struct *work) switch (fw_event->event) { case MPI2_EVENT_SAS_TOPOLOGY_CHANGE_LIST: - _scsih_sas_topology_change_event(ioc, fw_event->VF_ID, - fw_event->event_data, fw_event); + _scsih_sas_topology_change_event(ioc, fw_event); break; case MPI2_EVENT_SAS_DEVICE_STATUS_CHANGE: - _scsih_sas_device_status_change_event(ioc, fw_event->VF_ID, - fw_event->event_data); + _scsih_sas_device_status_change_event(ioc, + fw_event); break; case MPI2_EVENT_SAS_DISCOVERY: - _scsih_sas_discovery_event(ioc, fw_event->VF_ID, - fw_event->event_data); + _scsih_sas_discovery_event(ioc, + fw_event); break; case MPI2_EVENT_SAS_BROADCAST_PRIMITIVE: - _scsih_sas_broadcast_primative_event(ioc, fw_event->VF_ID, - fw_event->event_data); + _scsih_sas_broadcast_primative_event(ioc, + fw_event); break; case MPI2_EVENT_SAS_ENCL_DEVICE_STATUS_CHANGE: _scsih_sas_enclosure_dev_status_change_event(ioc, - fw_event->VF_ID, fw_event->event_data); + fw_event); break; case MPI2_EVENT_IR_CONFIGURATION_CHANGE_LIST: - _scsih_sas_ir_config_change_event(ioc, fw_event->VF_ID, - fw_event->event_data); + _scsih_sas_ir_config_change_event(ioc, fw_event); break; case MPI2_EVENT_IR_VOLUME: - _scsih_sas_ir_volume_event(ioc, fw_event->VF_ID, - fw_event->event_data); + _scsih_sas_ir_volume_event(ioc, fw_event); break; case MPI2_EVENT_IR_PHYSICAL_DISK: - _scsih_sas_ir_physical_disk_event(ioc, fw_event->VF_ID, - fw_event->event_data); + _scsih_sas_ir_physical_disk_event(ioc, fw_event); break; case MPI2_EVENT_IR_OPERATION_STATUS: - _scsih_sas_ir_operation_status_event(ioc, fw_event->VF_ID, - fw_event->event_data); + _scsih_sas_ir_operation_status_event(ioc, fw_event); break; case MPI2_EVENT_TASK_SET_FULL: - _scsih_task_set_full(ioc, fw_event->VF_ID, - fw_event->event_data); + _scsih_task_set_full(ioc, fw_event); break; } _scsih_fw_event_free(ioc, fw_event); @@ -5273,17 +5491,19 @@ _firmware_event_work(struct work_struct *work) /** * mpt2sas_scsih_event_callback - firmware event handler (called at ISR time) * @ioc: per adapter object - * @VF_ID: virtual function id + * @msix_index: MSIX table index supplied by the OS * @reply: reply message frame(lower 32bit addr) * Context: interrupt. * * This function merely adds a new work task into ioc->firmware_event_thread. * The tasks are worked from _firmware_event_work in user context. * - * Return nothing. + * Return 1 meaning mf should be freed from _base_interrupt + * 0 means the mf is freed from this function. */ -void -mpt2sas_scsih_event_callback(struct MPT2SAS_ADAPTER *ioc, u8 VF_ID, u32 reply) +u8 +mpt2sas_scsih_event_callback(struct MPT2SAS_ADAPTER *ioc, u8 msix_index, + u32 reply) { struct fw_event_work *fw_event; Mpi2EventNotificationReply_t *mpi_reply; @@ -5294,11 +5514,11 @@ mpt2sas_scsih_event_callback(struct MPT2SAS_ADAPTER *ioc, u8 VF_ID, u32 reply) spin_lock_irqsave(&ioc->fw_event_lock, flags); if (ioc->fw_events_off || ioc->remove_host) { spin_unlock_irqrestore(&ioc->fw_event_lock, flags); - return; + return 1; } spin_unlock_irqrestore(&ioc->fw_event_lock, flags); - mpi_reply = mpt2sas_base_get_reply_virt_addr(ioc, reply); + mpi_reply = mpt2sas_base_get_reply_virt_addr(ioc, reply); event = le16_to_cpu(mpi_reply->Event); switch (event) { @@ -5312,7 +5532,7 @@ mpt2sas_scsih_event_callback(struct MPT2SAS_ADAPTER *ioc, u8 VF_ID, u32 reply) if (baen_data->Primitive != MPI2_EVENT_PRIMITIVE_ASYNCHRONOUS_EVENT || ioc->broadcast_aen_busy) - return; + return 1; ioc->broadcast_aen_busy = 1; break; } @@ -5334,14 +5554,14 @@ mpt2sas_scsih_event_callback(struct MPT2SAS_ADAPTER *ioc, u8 VF_ID, u32 reply) break; default: /* ignore the rest */ - return; + return 1; } fw_event = kzalloc(sizeof(struct fw_event_work), GFP_ATOMIC); if (!fw_event) { printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n", ioc->name, __FILE__, __LINE__, __func__); - return; + return 1; } fw_event->event_data = kzalloc(mpi_reply->EventDataLength*4, GFP_ATOMIC); @@ -5349,15 +5569,17 @@ mpt2sas_scsih_event_callback(struct MPT2SAS_ADAPTER *ioc, u8 VF_ID, u32 reply) printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n", ioc->name, __FILE__, __LINE__, __func__); kfree(fw_event); - return; + return 1; } memcpy(fw_event->event_data, mpi_reply->EventData, mpi_reply->EventDataLength*4); fw_event->ioc = ioc; - fw_event->VF_ID = VF_ID; + fw_event->VF_ID = mpi_reply->VF_ID; + fw_event->VP_ID = mpi_reply->VP_ID; fw_event->event = event; _scsih_fw_event_add(ioc, fw_event); + return 1; } /* shost template */ @@ -5617,7 +5839,7 @@ _scsih_probe_raid(struct MPT2SAS_ADAPTER *ioc) } /** - * _scsih_probe_sas - reporting raid volumes to sas transport + * _scsih_probe_sas - reporting sas devices to sas transport * @ioc: per adapter object * * Called during initial loading of the driver. @@ -5714,6 +5936,8 @@ _scsih_probe(struct pci_dev *pdev, const struct pci_device_id *id) ioc->base_cb_idx = base_cb_idx; ioc->transport_cb_idx = transport_cb_idx; ioc->config_cb_idx = config_cb_idx; + ioc->tm_tr_cb_idx = tm_tr_cb_idx; + ioc->tm_sas_control_cb_idx = tm_sas_control_cb_idx; ioc->logging_level = logging_level; /* misc semaphores and spin locks */ spin_lock_init(&ioc->ioc_reset_in_progress_lock); @@ -5729,6 +5953,7 @@ _scsih_probe(struct pci_dev *pdev, const struct pci_device_id *id) INIT_LIST_HEAD(&ioc->fw_event_list); INIT_LIST_HEAD(&ioc->raid_device_list); INIT_LIST_HEAD(&ioc->sas_hba.sas_port_list); + INIT_LIST_HEAD(&ioc->delayed_tr_list); /* init shost parameters */ shost->max_cmd_len = 16; @@ -5745,6 +5970,7 @@ _scsih_probe(struct pci_dev *pdev, const struct pci_device_id *id) scsi_host_set_prot(shost, SHOST_DIF_TYPE1_PROTECTION | SHOST_DIF_TYPE3_PROTECTION); + scsi_host_set_guard(shost, SHOST_DIX_GUARD_CRC); /* event thread */ snprintf(ioc->firmware_event_name, sizeof(ioc->firmware_event_name), @@ -5894,6 +6120,11 @@ _scsih_init(void) /* ctl module callback handler */ ctl_cb_idx = mpt2sas_base_register_callback_handler(mpt2sas_ctl_done); + tm_tr_cb_idx = mpt2sas_base_register_callback_handler( + _scsih_tm_tr_complete); + tm_sas_control_cb_idx = mpt2sas_base_register_callback_handler( + _scsih_sas_control_complete); + mpt2sas_ctl_init(); error = pci_register_driver(&scsih_driver); @@ -5924,6 +6155,9 @@ _scsih_exit(void) mpt2sas_base_release_callback_handler(config_cb_idx); mpt2sas_base_release_callback_handler(ctl_cb_idx); + mpt2sas_base_release_callback_handler(tm_tr_cb_idx); + mpt2sas_base_release_callback_handler(tm_sas_control_cb_idx); + mpt2sas_ctl_exit(); } diff --git a/drivers/scsi/mpt2sas/mpt2sas_transport.c b/drivers/scsi/mpt2sas/mpt2sas_transport.c index 742324a0a11e..eb98188c7f3f 100644 --- a/drivers/scsi/mpt2sas/mpt2sas_transport.c +++ b/drivers/scsi/mpt2sas/mpt2sas_transport.c @@ -2,7 +2,7 @@ * SAS Transport Layer for MPT (Message Passing Technology) based controllers * * This code is based on drivers/scsi/mpt2sas/mpt2_transport.c - * Copyright (C) 2007-2008 LSI Corporation + * Copyright (C) 2007-2009 LSI Corporation * (mailto:DL-MPTFusionLinux@lsi.com) * * This program is free software; you can redistribute it and/or @@ -212,25 +212,26 @@ _transport_set_identify(struct MPT2SAS_ADAPTER *ioc, u16 handle, * mpt2sas_transport_done - internal transport layer callback handler. * @ioc: per adapter object * @smid: system request message index - * @VF_ID: virtual function id + * @msix_index: MSIX table index supplied by the OS * @reply: reply message frame(lower 32bit addr) * * Callback handler when sending internal generated transport cmds. * The callback index passed is `ioc->transport_cb_idx` * - * Return nothing. + * Return 1 meaning mf should be freed from _base_interrupt + * 0 means the mf is freed from this function. */ -void -mpt2sas_transport_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 VF_ID, +u8 +mpt2sas_transport_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 msix_index, u32 reply) { MPI2DefaultReply_t *mpi_reply; mpi_reply = mpt2sas_base_get_reply_virt_addr(ioc, reply); if (ioc->transport_cmds.status == MPT2_CMD_NOT_USED) - return; + return 1; if (ioc->transport_cmds.smid != smid) - return; + return 1; ioc->transport_cmds.status |= MPT2_CMD_COMPLETE; if (mpi_reply) { memcpy(ioc->transport_cmds.reply, mpi_reply, @@ -239,6 +240,7 @@ mpt2sas_transport_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 VF_ID, } ioc->transport_cmds.status &= ~MPT2_CMD_PENDING; complete(&ioc->transport_cmds.done); + return 1; } /* report manufacture request structure */ @@ -369,6 +371,8 @@ _transport_expander_report_manufacture(struct MPT2SAS_ADAPTER *ioc, memset(mpi_request, 0, sizeof(Mpi2SmpPassthroughRequest_t)); mpi_request->Function = MPI2_FUNCTION_SMP_PASSTHROUGH; mpi_request->PhysicalPort = 0xFF; + mpi_request->VF_ID = 0; /* TODO */ + mpi_request->VP_ID = 0; sas_address_le = (u64 *)&mpi_request->SASAddress; *sas_address_le = cpu_to_le64(sas_address); mpi_request->RequestDataLength = sizeof(struct rep_manu_request); @@ -396,7 +400,8 @@ _transport_expander_report_manufacture(struct MPT2SAS_ADAPTER *ioc, dtransportprintk(ioc, printk(MPT2SAS_DEBUG_FMT "report_manufacture - " "send to sas_addr(0x%016llx)\n", ioc->name, (unsigned long long)sas_address)); - mpt2sas_base_put_smid_default(ioc, smid, 0 /* VF_ID */); + mpt2sas_base_put_smid_default(ioc, smid); + init_completion(&ioc->transport_cmds.done); timeleft = wait_for_completion_timeout(&ioc->transport_cmds.done, 10*HZ); @@ -1106,6 +1111,8 @@ _transport_smp_handler(struct Scsi_Host *shost, struct sas_rphy *rphy, memset(mpi_request, 0, sizeof(Mpi2SmpPassthroughRequest_t)); mpi_request->Function = MPI2_FUNCTION_SMP_PASSTHROUGH; mpi_request->PhysicalPort = 0xFF; + mpi_request->VF_ID = 0; /* TODO */ + mpi_request->VP_ID = 0; *((u64 *)&mpi_request->SASAddress) = (rphy) ? cpu_to_le64(rphy->identify.sas_address) : cpu_to_le64(ioc->sas_hba.sas_address); @@ -1147,7 +1154,8 @@ _transport_smp_handler(struct Scsi_Host *shost, struct sas_rphy *rphy, dtransportprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s - " "sending smp request\n", ioc->name, __func__)); - mpt2sas_base_put_smid_default(ioc, smid, 0 /* VF_ID */); + mpt2sas_base_put_smid_default(ioc, smid); + init_completion(&ioc->transport_cmds.done); timeleft = wait_for_completion_timeout(&ioc->transport_cmds.done, 10*HZ); diff --git a/drivers/scsi/mvsas/mv_defs.h b/drivers/scsi/mvsas/mv_defs.h index f8cb9defb961..1849da1f030d 100644 --- a/drivers/scsi/mvsas/mv_defs.h +++ b/drivers/scsi/mvsas/mv_defs.h @@ -25,6 +25,8 @@ #ifndef _MV_DEFS_H_ #define _MV_DEFS_H_ +#define PCI_DEVICE_ID_ARECA_1300 0x1300 +#define PCI_DEVICE_ID_ARECA_1320 0x1320 enum chip_flavors { chip_6320, @@ -32,6 +34,8 @@ enum chip_flavors { chip_6485, chip_9480, chip_9180, + chip_1300, + chip_1320 }; /* driver compile-time configuration */ diff --git a/drivers/scsi/mvsas/mv_init.c b/drivers/scsi/mvsas/mv_init.c index 8646a19f999d..c790d45876c4 100644 --- a/drivers/scsi/mvsas/mv_init.c +++ b/drivers/scsi/mvsas/mv_init.c @@ -32,6 +32,8 @@ static const struct mvs_chip_info mvs_chips[] = { [chip_6485] = { 1, 8, 0x800, 33, 32, 10, &mvs_64xx_dispatch, }, [chip_9180] = { 2, 4, 0x800, 17, 64, 9, &mvs_94xx_dispatch, }, [chip_9480] = { 2, 4, 0x800, 17, 64, 9, &mvs_94xx_dispatch, }, + [chip_1300] = { 1, 4, 0x400, 17, 16, 9, &mvs_64xx_dispatch, }, + [chip_1320] = { 2, 4, 0x800, 17, 64, 9, &mvs_94xx_dispatch, }, }; #define SOC_SAS_NUM 2 @@ -653,6 +655,8 @@ static struct pci_device_id __devinitdata mvs_pci_table[] = { { PCI_VDEVICE(MARVELL, 0x6485), chip_6485 }, { PCI_VDEVICE(MARVELL, 0x9480), chip_9480 }, { PCI_VDEVICE(MARVELL, 0x9180), chip_9180 }, + { PCI_VDEVICE(ARECA, PCI_DEVICE_ID_ARECA_1300), chip_1300 }, + { PCI_VDEVICE(ARECA, PCI_DEVICE_ID_ARECA_1320), chip_1320 }, { } /* terminate list */ }; diff --git a/drivers/scsi/pmcraid.c b/drivers/scsi/pmcraid.c index 4302f06e4ec9..0a97bc9074bb 100644 --- a/drivers/scsi/pmcraid.c +++ b/drivers/scsi/pmcraid.c @@ -46,6 +46,7 @@ #include <linux/mutex.h> #include <scsi/scsi.h> #include <scsi/scsi_host.h> +#include <scsi/scsi_device.h> #include <scsi/scsi_tcq.h> #include <scsi/scsi_eh.h> #include <scsi/scsi_cmnd.h> @@ -684,7 +685,7 @@ static void pmcraid_timeout_handler(struct pmcraid_cmd *cmd) struct pmcraid_instance *pinstance = cmd->drv_inst; unsigned long lock_flags; - dev_err(&pinstance->pdev->dev, + dev_info(&pinstance->pdev->dev, "Adapter being reset due to command timeout.\n"); /* Command timeouts result in hard reset sequence. The command that got @@ -815,8 +816,9 @@ static void pmcraid_erp_done(struct pmcraid_cmd *cmd) if (PMCRAID_IOASC_SENSE_KEY(ioasc) > 0) { scsi_cmd->result |= (DID_ERROR << 16); - pmcraid_err("command CDB[0] = %x failed with IOASC: 0x%08X\n", - cmd->ioa_cb->ioarcb.cdb[0], ioasc); + scmd_printk(KERN_INFO, scsi_cmd, + "command CDB[0] = %x failed with IOASC: 0x%08X\n", + cmd->ioa_cb->ioarcb.cdb[0], ioasc); } /* if we had allocated sense buffers for request sense, copy the sense @@ -1069,7 +1071,7 @@ static struct pmcraid_cmd *pmcraid_init_hcam ioarcb->data_transfer_length = cpu_to_le32(rcb_size); - ioadl[0].flags |= cpu_to_le32(IOADL_FLAGS_READ_LAST); + ioadl[0].flags |= IOADL_FLAGS_READ_LAST; ioadl[0].data_len = cpu_to_le32(rcb_size); ioadl[0].address = cpu_to_le32(dma); @@ -1541,13 +1543,13 @@ static void pmcraid_handle_error_log(struct pmcraid_instance *pinstance) if (pinstance->ldn.hcam->notification_lost == HOSTRCB_NOTIFICATIONS_LOST) - dev_err(&pinstance->pdev->dev, "Error notifications lost\n"); + dev_info(&pinstance->pdev->dev, "Error notifications lost\n"); ioasc = le32_to_cpu(hcam_ldn->error_log.fd_ioasc); if (ioasc == PMCRAID_IOASC_UA_BUS_WAS_RESET || ioasc == PMCRAID_IOASC_UA_BUS_WAS_RESET_BY_OTHER) { - dev_err(&pinstance->pdev->dev, + dev_info(&pinstance->pdev->dev, "UnitAttention due to IOA Bus Reset\n"); scsi_report_bus_reset( pinstance->host, @@ -1584,7 +1586,7 @@ static void pmcraid_process_ccn(struct pmcraid_cmd *cmd) atomic_read(&pinstance->ccn.ignore) == 1) { return; } else if (ioasc) { - dev_err(&pinstance->pdev->dev, + dev_info(&pinstance->pdev->dev, "Host RCB (CCN) failed with IOASC: 0x%08X\n", ioasc); spin_lock_irqsave(pinstance->host->host_lock, lock_flags); pmcraid_send_hcam(pinstance, PMCRAID_HCAM_CODE_CONFIG_CHANGE); @@ -1634,7 +1636,7 @@ static void pmcraid_process_ldn(struct pmcraid_cmd *cmd) return; } } else { - dev_err(&pinstance->pdev->dev, + dev_info(&pinstance->pdev->dev, "Host RCB(LDN) failed with IOASC: 0x%08X\n", ioasc); } /* send netlink message for HCAM notification if enabled */ @@ -1822,7 +1824,6 @@ static void pmcraid_fail_outstanding_cmds(struct pmcraid_instance *pinstance) scsi_dma_unmap(scsi_cmd); pmcraid_return_cmd(cmd); - pmcraid_info("failing(%d) CDB[0] = %x result: %x\n", le32_to_cpu(resp) >> 2, cmd->ioa_cb->ioarcb.cdb[0], @@ -2250,7 +2251,7 @@ static void pmcraid_request_sense(struct pmcraid_cmd *cmd) ioadl->address = cpu_to_le64(cmd->sense_buffer_dma); ioadl->data_len = cpu_to_le32(SCSI_SENSE_BUFFERSIZE); - ioadl->flags = cpu_to_le32(IOADL_FLAGS_LAST_DESC); + ioadl->flags = IOADL_FLAGS_LAST_DESC; /* request sense might be called as part of error response processing * which runs in tasklets context. It is possible that mid-layer might @@ -2514,7 +2515,8 @@ static int pmcraid_reset_device( res = scsi_cmd->device->hostdata; if (!res) { - pmcraid_err("reset_device: NULL resource pointer\n"); + sdev_printk(KERN_ERR, scsi_cmd->device, + "reset_device: NULL resource pointer\n"); return FAILED; } @@ -2752,8 +2754,8 @@ static int pmcraid_eh_abort_handler(struct scsi_cmnd *scsi_cmd) pinstance = (struct pmcraid_instance *)scsi_cmd->device->host->hostdata; - dev_err(&pinstance->pdev->dev, - "I/O command timed out, aborting it.\n"); + scmd_printk(KERN_INFO, scsi_cmd, + "I/O command timed out, aborting it.\n"); res = scsi_cmd->device->hostdata; @@ -2824,7 +2826,8 @@ static int pmcraid_eh_abort_handler(struct scsi_cmnd *scsi_cmd) */ static int pmcraid_eh_device_reset_handler(struct scsi_cmnd *scmd) { - pmcraid_err("Doing device reset due to an I/O command timeout.\n"); + scmd_printk(KERN_INFO, scmd, + "resetting device due to an I/O command timeout.\n"); return pmcraid_reset_device(scmd, PMCRAID_INTERNAL_TIMEOUT, RESET_DEVICE_LUN); @@ -2832,7 +2835,8 @@ static int pmcraid_eh_device_reset_handler(struct scsi_cmnd *scmd) static int pmcraid_eh_bus_reset_handler(struct scsi_cmnd *scmd) { - pmcraid_err("Doing bus reset due to an I/O command timeout.\n"); + scmd_printk(KERN_INFO, scmd, + "Doing bus reset due to an I/O command timeout.\n"); return pmcraid_reset_device(scmd, PMCRAID_RESET_BUS_TIMEOUT, RESET_DEVICE_BUS); @@ -2840,7 +2844,8 @@ static int pmcraid_eh_bus_reset_handler(struct scsi_cmnd *scmd) static int pmcraid_eh_target_reset_handler(struct scsi_cmnd *scmd) { - pmcraid_err("Doing target reset due to an I/O command timeout.\n"); + scmd_printk(KERN_INFO, scmd, + "Doing target reset due to an I/O command timeout.\n"); return pmcraid_reset_device(scmd, PMCRAID_INTERNAL_TIMEOUT, RESET_DEVICE_TARGET); @@ -2988,11 +2993,11 @@ static int pmcraid_build_ioadl( nseg = scsi_dma_map(scsi_cmd); if (nseg < 0) { - dev_err(&pinstance->pdev->dev, "scsi_map_dma failed!\n"); + scmd_printk(KERN_ERR, scsi_cmd, "scsi_map_dma failed!\n"); return -1; } else if (nseg > PMCRAID_MAX_IOADLS) { scsi_dma_unmap(scsi_cmd); - dev_err(&pinstance->pdev->dev, + scmd_printk(KERN_ERR, scsi_cmd, "sg count is (%d) more than allowed!\n", nseg); return -1; } @@ -3012,7 +3017,7 @@ static int pmcraid_build_ioadl( ioadl[i].flags = 0; } /* setup last descriptor */ - ioadl[i - 1].flags = cpu_to_le32(IOADL_FLAGS_LAST_DESC); + ioadl[i - 1].flags = IOADL_FLAGS_LAST_DESC; return 0; } @@ -3382,7 +3387,7 @@ static int pmcraid_build_passthrough_ioadls( } /* setup the last descriptor */ - ioadl[i - 1].flags = cpu_to_le32(IOADL_FLAGS_LAST_DESC); + ioadl[i - 1].flags = IOADL_FLAGS_LAST_DESC; return 0; } @@ -5040,7 +5045,7 @@ static int pmcraid_resume(struct pci_dev *pdev) rc = pci_enable_device(pdev); if (rc) { - pmcraid_err("pmcraid: Enable device failed\n"); + dev_err(&pdev->dev, "resume: Enable device failed\n"); return rc; } @@ -5054,7 +5059,7 @@ static int pmcraid_resume(struct pci_dev *pdev) rc = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32)); if (rc != 0) { - dev_err(&pdev->dev, "Failed to set PCI DMA mask\n"); + dev_err(&pdev->dev, "resume: Failed to set PCI DMA mask\n"); goto disable_device; } @@ -5063,7 +5068,8 @@ static int pmcraid_resume(struct pci_dev *pdev) rc = pmcraid_register_interrupt_handler(pinstance); if (rc) { - pmcraid_err("resume: couldn't register interrupt handlers\n"); + dev_err(&pdev->dev, + "resume: couldn't register interrupt handlers\n"); rc = -ENODEV; goto release_host; } @@ -5080,7 +5086,7 @@ static int pmcraid_resume(struct pci_dev *pdev) * state. */ if (pmcraid_reset_bringup(pinstance)) { - pmcraid_err("couldn't initialize IOA \n"); + dev_err(&pdev->dev, "couldn't initialize IOA \n"); rc = -ENODEV; goto release_tasklets; } @@ -5187,7 +5193,7 @@ static void pmcraid_init_res_table(struct pmcraid_cmd *cmd) LIST_HEAD(old_res); if (pinstance->cfg_table->flags & MICROCODE_UPDATE_REQUIRED) - dev_err(&pinstance->pdev->dev, "Require microcode download\n"); + pmcraid_err("IOA requires microcode download\n"); /* resource list is protected by pinstance->resource_lock. * init_res_table can be called from probe (user-thread) or runtime @@ -5224,8 +5230,7 @@ static void pmcraid_init_res_table(struct pmcraid_cmd *cmd) if (!found) { if (list_empty(&pinstance->free_res_q)) { - dev_err(&pinstance->pdev->dev, - "Too many devices attached\n"); + pmcraid_err("Too many devices attached\n"); break; } @@ -5309,7 +5314,7 @@ static void pmcraid_querycfg(struct pmcraid_cmd *cmd) cpu_to_le32(sizeof(struct pmcraid_config_table)); ioadl = &(ioarcb->add_data.u.ioadl[0]); - ioadl->flags = cpu_to_le32(IOADL_FLAGS_LAST_DESC); + ioadl->flags = IOADL_FLAGS_LAST_DESC; ioadl->address = cpu_to_le64(pinstance->cfg_table_bus_addr); ioadl->data_len = cpu_to_le32(sizeof(struct pmcraid_config_table)); @@ -5442,7 +5447,7 @@ static int __devinit pmcraid_probe( rc = pmcraid_register_interrupt_handler(pinstance); if (rc) { - pmcraid_err("couldn't register interrupt handler\n"); + dev_err(&pdev->dev, "couldn't register interrupt handler\n"); goto out_scsi_host_put; } @@ -5466,7 +5471,7 @@ static int __devinit pmcraid_probe( */ pmcraid_info("starting IOA initialization sequence\n"); if (pmcraid_reset_bringup(pinstance)) { - pmcraid_err("couldn't initialize IOA \n"); + dev_err(&pdev->dev, "couldn't initialize IOA \n"); rc = 1; goto out_release_bufs; } @@ -5534,7 +5539,6 @@ static struct pci_driver pmcraid_driver = { .shutdown = pmcraid_shutdown }; - /** * pmcraid_init - module load entry point */ @@ -5566,7 +5570,6 @@ static int __init pmcraid_init(void) goto out_unreg_chrdev; } - error = pmcraid_netlink_init(); if (error) @@ -5584,6 +5587,7 @@ static int __init pmcraid_init(void) out_unreg_chrdev: unregister_chrdev_region(MKDEV(pmcraid_major, 0), PMCRAID_MAX_ADAPTERS); + out_init: return error; } diff --git a/drivers/scsi/pmcraid.h b/drivers/scsi/pmcraid.h index 614b3a764fed..3441b3f90827 100644 --- a/drivers/scsi/pmcraid.h +++ b/drivers/scsi/pmcraid.h @@ -26,7 +26,6 @@ #include <linux/completion.h> #include <linux/list.h> #include <scsi/scsi.h> -#include <linux/kref.h> #include <scsi/scsi_cmnd.h> #include <linux/cdev.h> #include <net/netlink.h> diff --git a/drivers/scsi/qla2xxx/qla_mid.c b/drivers/scsi/qla2xxx/qla_mid.c index 42b799abba57..e07b3617f019 100644 --- a/drivers/scsi/qla2xxx/qla_mid.c +++ b/drivers/scsi/qla2xxx/qla_mid.c @@ -568,7 +568,7 @@ qla25xx_create_req_que(struct qla_hw_data *ha, uint16_t options, if (req == NULL) { qla_printk(KERN_WARNING, ha, "could not allocate memory" "for request que\n"); - goto que_failed; + goto failed; } req->length = REQUEST_ENTRY_CNT_24XX; @@ -632,6 +632,7 @@ qla25xx_create_req_que(struct qla_hw_data *ha, uint16_t options, que_failed: qla25xx_free_req_que(base_vha, req); +failed: return 0; } @@ -659,7 +660,7 @@ qla25xx_create_rsp_que(struct qla_hw_data *ha, uint16_t options, if (rsp == NULL) { qla_printk(KERN_WARNING, ha, "could not allocate memory for" " response que\n"); - goto que_failed; + goto failed; } rsp->length = RESPONSE_ENTRY_CNT_MQ; @@ -728,6 +729,7 @@ qla25xx_create_rsp_que(struct qla_hw_data *ha, uint16_t options, que_failed: qla25xx_free_rsp_que(base_vha, rsp); +failed: return 0; } diff --git a/drivers/scsi/scsi.c b/drivers/scsi/scsi.c index b6e03074cb8f..dd098cad337b 100644 --- a/drivers/scsi/scsi.c +++ b/drivers/scsi/scsi.c @@ -241,10 +241,7 @@ scsi_host_alloc_command(struct Scsi_Host *shost, gfp_t gfp_mask) */ struct scsi_cmnd *__scsi_get_command(struct Scsi_Host *shost, gfp_t gfp_mask) { - struct scsi_cmnd *cmd; - unsigned char *buf; - - cmd = scsi_host_alloc_command(shost, gfp_mask); + struct scsi_cmnd *cmd = scsi_host_alloc_command(shost, gfp_mask); if (unlikely(!cmd)) { unsigned long flags; @@ -258,9 +255,15 @@ struct scsi_cmnd *__scsi_get_command(struct Scsi_Host *shost, gfp_t gfp_mask) spin_unlock_irqrestore(&shost->free_list_lock, flags); if (cmd) { + void *buf, *prot; + buf = cmd->sense_buffer; + prot = cmd->prot_sdb; + memset(cmd, 0, sizeof(*cmd)); + cmd->sense_buffer = buf; + cmd->prot_sdb = prot; } } diff --git a/drivers/scsi/scsi_debug.c b/drivers/scsi/scsi_debug.c index fb9af207d61d..c4103bef41b5 100644 --- a/drivers/scsi/scsi_debug.c +++ b/drivers/scsi/scsi_debug.c @@ -50,6 +50,7 @@ #include <scsi/scsi_host.h> #include <scsi/scsicam.h> #include <scsi/scsi_eh.h> +#include <scsi/scsi_dbg.h> #include "sd.h" #include "scsi_logging.h" @@ -64,6 +65,7 @@ static const char * scsi_debug_version_date = "20070104"; #define PARAMETER_LIST_LENGTH_ERR 0x1a #define INVALID_OPCODE 0x20 #define ADDR_OUT_OF_RANGE 0x21 +#define INVALID_COMMAND_OPCODE 0x20 #define INVALID_FIELD_IN_CDB 0x24 #define INVALID_FIELD_IN_PARAM_LIST 0x26 #define POWERON_RESET 0x29 @@ -180,7 +182,7 @@ static int sdebug_sectors_per; /* sectors per cylinder */ #define SDEBUG_SENSE_LEN 32 #define SCSI_DEBUG_CANQUEUE 255 -#define SCSI_DEBUG_MAX_CMD_LEN 16 +#define SCSI_DEBUG_MAX_CMD_LEN 32 struct sdebug_dev_info { struct list_head dev_list; @@ -296,9 +298,25 @@ static void mk_sense_buffer(struct sdebug_dev_info *devip, int key, } static void get_data_transfer_info(unsigned char *cmd, - unsigned long long *lba, unsigned int *num) + unsigned long long *lba, unsigned int *num, + u32 *ei_lba) { + *ei_lba = 0; + switch (*cmd) { + case VARIABLE_LENGTH_CMD: + *lba = (u64)cmd[19] | (u64)cmd[18] << 8 | + (u64)cmd[17] << 16 | (u64)cmd[16] << 24 | + (u64)cmd[15] << 32 | (u64)cmd[14] << 40 | + (u64)cmd[13] << 48 | (u64)cmd[12] << 56; + + *ei_lba = (u32)cmd[23] | (u32)cmd[22] << 8 | + (u32)cmd[21] << 16 | (u32)cmd[20] << 24; + + *num = (u32)cmd[31] | (u32)cmd[30] << 8 | (u32)cmd[29] << 16 | + (u32)cmd[28] << 24; + break; + case WRITE_16: case READ_16: *lba = (u64)cmd[9] | (u64)cmd[8] << 8 | @@ -1589,7 +1607,7 @@ static int do_device_access(struct scsi_cmnd *scmd, } static int prot_verify_read(struct scsi_cmnd *SCpnt, sector_t start_sec, - unsigned int sectors) + unsigned int sectors, u32 ei_lba) { unsigned int i, resid; struct scatterlist *psgl; @@ -1636,13 +1654,23 @@ static int prot_verify_read(struct scsi_cmnd *SCpnt, sector_t start_sec, return 0x01; } - if (scsi_debug_dif != SD_DIF_TYPE3_PROTECTION && + if (scsi_debug_dif == SD_DIF_TYPE1_PROTECTION && be32_to_cpu(sdt[i].ref_tag) != (sector & 0xffffffff)) { printk(KERN_ERR "%s: REF check failed on sector %lu\n", __func__, (unsigned long)sector); dif_errors++; return 0x03; } + + if (scsi_debug_dif == SD_DIF_TYPE2_PROTECTION && + be32_to_cpu(sdt[i].ref_tag) != ei_lba) { + printk(KERN_ERR "%s: REF check failed on sector %lu\n", + __func__, (unsigned long)sector); + dif_errors++; + return 0x03; + } + + ei_lba++; } resid = sectors * 8; /* Bytes of protection data to copy into sgl */ @@ -1670,7 +1698,8 @@ static int prot_verify_read(struct scsi_cmnd *SCpnt, sector_t start_sec, } static int resp_read(struct scsi_cmnd *SCpnt, unsigned long long lba, - unsigned int num, struct sdebug_dev_info *devip) + unsigned int num, struct sdebug_dev_info *devip, + u32 ei_lba) { unsigned long iflags; int ret; @@ -1699,7 +1728,7 @@ static int resp_read(struct scsi_cmnd *SCpnt, unsigned long long lba, /* DIX + T10 DIF */ if (scsi_debug_dix && scsi_prot_sg_count(SCpnt)) { - int prot_ret = prot_verify_read(SCpnt, lba, num); + int prot_ret = prot_verify_read(SCpnt, lba, num, ei_lba); if (prot_ret) { mk_sense_buffer(devip, ABORTED_COMMAND, 0x10, prot_ret); @@ -1735,7 +1764,7 @@ void dump_sector(unsigned char *buf, int len) } static int prot_verify_write(struct scsi_cmnd *SCpnt, sector_t start_sec, - unsigned int sectors) + unsigned int sectors, u32 ei_lba) { int i, j, ret; struct sd_dif_tuple *sdt; @@ -1749,11 +1778,6 @@ static int prot_verify_write(struct scsi_cmnd *SCpnt, sector_t start_sec, sector = do_div(tmp_sec, sdebug_store_sectors); - if (((SCpnt->cmnd[1] >> 5) & 7) != 1) { - printk(KERN_WARNING "scsi_debug: WRPROTECT != 1\n"); - return 0; - } - BUG_ON(scsi_sg_count(SCpnt) == 0); BUG_ON(scsi_prot_sg_count(SCpnt) == 0); @@ -1808,7 +1832,7 @@ static int prot_verify_write(struct scsi_cmnd *SCpnt, sector_t start_sec, goto out; } - if (scsi_debug_dif != SD_DIF_TYPE3_PROTECTION && + if (scsi_debug_dif == SD_DIF_TYPE1_PROTECTION && be32_to_cpu(sdt->ref_tag) != (start_sec & 0xffffffff)) { printk(KERN_ERR @@ -1819,6 +1843,16 @@ static int prot_verify_write(struct scsi_cmnd *SCpnt, sector_t start_sec, goto out; } + if (scsi_debug_dif == SD_DIF_TYPE2_PROTECTION && + be32_to_cpu(sdt->ref_tag) != ei_lba) { + printk(KERN_ERR + "%s: REF check failed on sector %lu\n", + __func__, (unsigned long)sector); + ret = 0x03; + dump_sector(daddr, scsi_debug_sector_size); + goto out; + } + /* Would be great to copy this in bigger * chunks. However, for the sake of * correctness we need to verify each sector @@ -1832,6 +1866,7 @@ static int prot_verify_write(struct scsi_cmnd *SCpnt, sector_t start_sec, sector = 0; /* Force wrap */ start_sec++; + ei_lba++; daddr += scsi_debug_sector_size; ppage_offset += sizeof(struct sd_dif_tuple); } @@ -1853,7 +1888,8 @@ out: } static int resp_write(struct scsi_cmnd *SCpnt, unsigned long long lba, - unsigned int num, struct sdebug_dev_info *devip) + unsigned int num, struct sdebug_dev_info *devip, + u32 ei_lba) { unsigned long iflags; int ret; @@ -1864,7 +1900,7 @@ static int resp_write(struct scsi_cmnd *SCpnt, unsigned long long lba, /* DIX + T10 DIF */ if (scsi_debug_dix && scsi_prot_sg_count(SCpnt)) { - int prot_ret = prot_verify_write(SCpnt, lba, num); + int prot_ret = prot_verify_write(SCpnt, lba, num, ei_lba); if (prot_ret) { mk_sense_buffer(devip, ILLEGAL_REQUEST, 0x10, prot_ret); @@ -2872,11 +2908,12 @@ static int __init scsi_debug_init(void) case SD_DIF_TYPE0_PROTECTION: case SD_DIF_TYPE1_PROTECTION: + case SD_DIF_TYPE2_PROTECTION: case SD_DIF_TYPE3_PROTECTION: break; default: - printk(KERN_ERR "scsi_debug_init: dif must be 0, 1 or 3\n"); + printk(KERN_ERR "scsi_debug_init: dif must be 0, 1, 2 or 3\n"); return -EINVAL; } @@ -3121,6 +3158,7 @@ int scsi_debug_queuecommand(struct scsi_cmnd *SCpnt, done_funct_t done) int len, k; unsigned int num; unsigned long long lba; + u32 ei_lba; int errsts = 0; int target = SCpnt->device->id; struct sdebug_dev_info *devip = NULL; @@ -3254,14 +3292,30 @@ int scsi_debug_queuecommand(struct scsi_cmnd *SCpnt, done_funct_t done) case READ_16: case READ_12: case READ_10: + /* READ{10,12,16} and DIF Type 2 are natural enemies */ + if (scsi_debug_dif == SD_DIF_TYPE2_PROTECTION && + cmd[1] & 0xe0) { + mk_sense_buffer(devip, ILLEGAL_REQUEST, + INVALID_COMMAND_OPCODE, 0); + errsts = check_condition_result; + break; + } + + if ((scsi_debug_dif == SD_DIF_TYPE1_PROTECTION || + scsi_debug_dif == SD_DIF_TYPE3_PROTECTION) && + (cmd[1] & 0xe0) == 0) + printk(KERN_ERR "Unprotected RD/WR to DIF device\n"); + + /* fall through */ case READ_6: +read: errsts = check_readiness(SCpnt, 0, devip); if (errsts) break; if (scsi_debug_fake_rw) break; - get_data_transfer_info(cmd, &lba, &num); - errsts = resp_read(SCpnt, lba, num, devip); + get_data_transfer_info(cmd, &lba, &num, &ei_lba); + errsts = resp_read(SCpnt, lba, num, devip, ei_lba); if (inj_recovered && (0 == errsts)) { mk_sense_buffer(devip, RECOVERED_ERROR, THRESHOLD_EXCEEDED, 0); @@ -3288,14 +3342,30 @@ int scsi_debug_queuecommand(struct scsi_cmnd *SCpnt, done_funct_t done) case WRITE_16: case WRITE_12: case WRITE_10: + /* WRITE{10,12,16} and DIF Type 2 are natural enemies */ + if (scsi_debug_dif == SD_DIF_TYPE2_PROTECTION && + cmd[1] & 0xe0) { + mk_sense_buffer(devip, ILLEGAL_REQUEST, + INVALID_COMMAND_OPCODE, 0); + errsts = check_condition_result; + break; + } + + if ((scsi_debug_dif == SD_DIF_TYPE1_PROTECTION || + scsi_debug_dif == SD_DIF_TYPE3_PROTECTION) && + (cmd[1] & 0xe0) == 0) + printk(KERN_ERR "Unprotected RD/WR to DIF device\n"); + + /* fall through */ case WRITE_6: +write: errsts = check_readiness(SCpnt, 0, devip); if (errsts) break; if (scsi_debug_fake_rw) break; - get_data_transfer_info(cmd, &lba, &num); - errsts = resp_write(SCpnt, lba, num, devip); + get_data_transfer_info(cmd, &lba, &num, &ei_lba); + errsts = resp_write(SCpnt, lba, num, devip, ei_lba); if (inj_recovered && (0 == errsts)) { mk_sense_buffer(devip, RECOVERED_ERROR, THRESHOLD_EXCEEDED, 0); @@ -3341,15 +3411,38 @@ int scsi_debug_queuecommand(struct scsi_cmnd *SCpnt, done_funct_t done) break; if (scsi_debug_fake_rw) break; - get_data_transfer_info(cmd, &lba, &num); - errsts = resp_read(SCpnt, lba, num, devip); + get_data_transfer_info(cmd, &lba, &num, &ei_lba); + errsts = resp_read(SCpnt, lba, num, devip, ei_lba); if (errsts) break; - errsts = resp_write(SCpnt, lba, num, devip); + errsts = resp_write(SCpnt, lba, num, devip, ei_lba); if (errsts) break; errsts = resp_xdwriteread(SCpnt, lba, num, devip); break; + case VARIABLE_LENGTH_CMD: + if (scsi_debug_dif == SD_DIF_TYPE2_PROTECTION) { + + if ((cmd[10] & 0xe0) == 0) + printk(KERN_ERR + "Unprotected RD/WR to DIF device\n"); + + if (cmd[9] == READ_32) { + BUG_ON(SCpnt->cmd_len < 32); + goto read; + } + + if (cmd[9] == WRITE_32) { + BUG_ON(SCpnt->cmd_len < 32); + goto write; + } + } + + mk_sense_buffer(devip, ILLEGAL_REQUEST, + INVALID_FIELD_IN_CDB, 0); + errsts = check_condition_result; + break; + default: if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts) printk(KERN_INFO "scsi_debug: Opcode: 0x%x not " diff --git a/drivers/scsi/scsi_error.c b/drivers/scsi/scsi_error.c index 877204daf549..1b0060b791e8 100644 --- a/drivers/scsi/scsi_error.c +++ b/drivers/scsi/scsi_error.c @@ -725,6 +725,9 @@ static int scsi_send_eh_cmnd(struct scsi_cmnd *scmd, unsigned char *cmnd, case NEEDS_RETRY: case FAILED: break; + case ADD_TO_MLQUEUE: + rtn = NEEDS_RETRY; + break; default: rtn = FAILED; break; diff --git a/drivers/scsi/scsi_scan.c b/drivers/scsi/scsi_scan.c index c44783801402..0547a7f44d42 100644 --- a/drivers/scsi/scsi_scan.c +++ b/drivers/scsi/scsi_scan.c @@ -317,6 +317,7 @@ static struct scsi_device *scsi_alloc_sdev(struct scsi_target *starget, out_device_destroy: scsi_device_set_state(sdev, SDEV_DEL); transport_destroy_device(&sdev->sdev_gendev); + put_device(&sdev->sdev_dev); put_device(&sdev->sdev_gendev); out: if (display_failure_msg) @@ -957,6 +958,7 @@ static inline void scsi_destroy_sdev(struct scsi_device *sdev) if (sdev->host->hostt->slave_destroy) sdev->host->hostt->slave_destroy(sdev); transport_destroy_device(&sdev->sdev_gendev); + put_device(&sdev->sdev_dev); put_device(&sdev->sdev_gendev); } diff --git a/drivers/scsi/scsi_sysfs.c b/drivers/scsi/scsi_sysfs.c index fde54537d715..5c7eb63a19d1 100644 --- a/drivers/scsi/scsi_sysfs.c +++ b/drivers/scsi/scsi_sysfs.c @@ -864,10 +864,6 @@ int scsi_sysfs_add_sdev(struct scsi_device *sdev) goto clean_device; } - /* take a reference for the sdev_dev; this is - * released by the sdev_class .release */ - get_device(&sdev->sdev_gendev); - /* create queue files, which may be writable, depending on the host */ if (sdev->host->hostt->change_queue_depth) error = device_create_file(&sdev->sdev_gendev, &sdev_attr_queue_depth_rw); @@ -917,6 +913,7 @@ int scsi_sysfs_add_sdev(struct scsi_device *sdev) device_del(&sdev->sdev_gendev); transport_destroy_device(&sdev->sdev_gendev); + put_device(&sdev->sdev_dev); put_device(&sdev->sdev_gendev); return error; @@ -1065,7 +1062,7 @@ void scsi_sysfs_device_initialize(struct scsi_device *sdev) sdev->host->host_no, sdev->channel, sdev->id, sdev->lun); device_initialize(&sdev->sdev_dev); - sdev->sdev_dev.parent = &sdev->sdev_gendev; + sdev->sdev_dev.parent = get_device(&sdev->sdev_gendev); sdev->sdev_dev.class = &sdev_class; dev_set_name(&sdev->sdev_dev, "%d:%d:%d:%d", sdev->host->host_no, sdev->channel, sdev->id, sdev->lun); diff --git a/drivers/scsi/scsi_transport_fc.c b/drivers/scsi/scsi_transport_fc.c index b98885de6876..c6f70dae9b2e 100644 --- a/drivers/scsi/scsi_transport_fc.c +++ b/drivers/scsi/scsi_transport_fc.c @@ -3586,6 +3586,7 @@ enum fc_dispatch_result { /** * fc_bsg_host_dispatch - process fc host bsg requests and dispatch to LLDD + * @q: fc host request queue * @shost: scsi host rport attached to * @job: bsg job to be processed */ @@ -3655,6 +3656,7 @@ fc_bsg_host_dispatch(struct request_queue *q, struct Scsi_Host *shost, fail_host_msg: /* return the errno failure code as the only status */ BUG_ON(job->reply_len < sizeof(uint32_t)); + job->reply->reply_payload_rcv_len = 0; job->reply->result = ret; job->reply_len = sizeof(uint32_t); fc_bsg_jobdone(job); @@ -3693,6 +3695,7 @@ fc_bsg_goose_queue(struct fc_rport *rport) /** * fc_bsg_rport_dispatch - process rport bsg requests and dispatch to LLDD + * @q: rport request queue * @shost: scsi host rport attached to * @rport: rport request destined to * @job: bsg job to be processed @@ -3739,6 +3742,7 @@ check_bidi: fail_rport_msg: /* return the errno failure code as the only status */ BUG_ON(job->reply_len < sizeof(uint32_t)); + job->reply->reply_payload_rcv_len = 0; job->reply->result = ret; job->reply_len = sizeof(uint32_t); fc_bsg_jobdone(job); @@ -3795,6 +3799,7 @@ fc_bsg_request_handler(struct request_queue *q, struct Scsi_Host *shost, /* check if we have the msgcode value at least */ if (job->request_len < sizeof(uint32_t)) { BUG_ON(job->reply_len < sizeof(uint32_t)); + job->reply->reply_payload_rcv_len = 0; job->reply->result = -ENOMSG; job->reply_len = sizeof(uint32_t); fc_bsg_jobdone(job); diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c index 8dd96dcd716c..9093c7261f33 100644 --- a/drivers/scsi/sd.c +++ b/drivers/scsi/sd.c @@ -116,6 +116,9 @@ static DEFINE_IDA(sd_index_ida); * object after last put) */ static DEFINE_MUTEX(sd_ref_mutex); +struct kmem_cache *sd_cdb_cache; +mempool_t *sd_cdb_pool; + static const char *sd_cache_types[] = { "write through", "none", "write back", "write back, no read (daft)" @@ -370,6 +373,31 @@ static void scsi_disk_put(struct scsi_disk *sdkp) mutex_unlock(&sd_ref_mutex); } +static void sd_prot_op(struct scsi_cmnd *scmd, unsigned int dif) +{ + unsigned int prot_op = SCSI_PROT_NORMAL; + unsigned int dix = scsi_prot_sg_count(scmd); + + if (scmd->sc_data_direction == DMA_FROM_DEVICE) { + if (dif && dix) + prot_op = SCSI_PROT_READ_PASS; + else if (dif && !dix) + prot_op = SCSI_PROT_READ_STRIP; + else if (!dif && dix) + prot_op = SCSI_PROT_READ_INSERT; + } else { + if (dif && dix) + prot_op = SCSI_PROT_WRITE_PASS; + else if (dif && !dix) + prot_op = SCSI_PROT_WRITE_INSERT; + else if (!dif && dix) + prot_op = SCSI_PROT_WRITE_STRIP; + } + + scsi_set_prot_op(scmd, prot_op); + scsi_set_prot_type(scmd, dif); +} + /** * sd_init_command - build a scsi (read or write) command from * information in the request structure. @@ -388,6 +416,7 @@ static int sd_prep_fn(struct request_queue *q, struct request *rq) sector_t threshold; unsigned int this_count = blk_rq_sectors(rq); int ret, host_dif; + unsigned char protect; if (rq->cmd_type == REQ_TYPE_BLOCK_PC) { ret = scsi_setup_blk_pc_cmnd(sdp, rq); @@ -520,13 +549,49 @@ static int sd_prep_fn(struct request_queue *q, struct request *rq) /* Set RDPROTECT/WRPROTECT if disk is formatted with DIF */ host_dif = scsi_host_dif_capable(sdp->host, sdkp->protection_type); if (host_dif) - SCpnt->cmnd[1] = 1 << 5; + protect = 1 << 5; else - SCpnt->cmnd[1] = 0; + protect = 0; - if (block > 0xffffffff) { + if (host_dif == SD_DIF_TYPE2_PROTECTION) { + SCpnt->cmnd = mempool_alloc(sd_cdb_pool, GFP_ATOMIC); + + if (unlikely(SCpnt->cmnd == NULL)) { + ret = BLKPREP_DEFER; + goto out; + } + + SCpnt->cmd_len = SD_EXT_CDB_SIZE; + memset(SCpnt->cmnd, 0, SCpnt->cmd_len); + SCpnt->cmnd[0] = VARIABLE_LENGTH_CMD; + SCpnt->cmnd[7] = 0x18; + SCpnt->cmnd[9] = (rq_data_dir(rq) == READ) ? READ_32 : WRITE_32; + SCpnt->cmnd[10] = protect | (blk_fua_rq(rq) ? 0x8 : 0); + + /* LBA */ + SCpnt->cmnd[12] = sizeof(block) > 4 ? (unsigned char) (block >> 56) & 0xff : 0; + SCpnt->cmnd[13] = sizeof(block) > 4 ? (unsigned char) (block >> 48) & 0xff : 0; + SCpnt->cmnd[14] = sizeof(block) > 4 ? (unsigned char) (block >> 40) & 0xff : 0; + SCpnt->cmnd[15] = sizeof(block) > 4 ? (unsigned char) (block >> 32) & 0xff : 0; + SCpnt->cmnd[16] = (unsigned char) (block >> 24) & 0xff; + SCpnt->cmnd[17] = (unsigned char) (block >> 16) & 0xff; + SCpnt->cmnd[18] = (unsigned char) (block >> 8) & 0xff; + SCpnt->cmnd[19] = (unsigned char) block & 0xff; + + /* Expected Indirect LBA */ + SCpnt->cmnd[20] = (unsigned char) (block >> 24) & 0xff; + SCpnt->cmnd[21] = (unsigned char) (block >> 16) & 0xff; + SCpnt->cmnd[22] = (unsigned char) (block >> 8) & 0xff; + SCpnt->cmnd[23] = (unsigned char) block & 0xff; + + /* Transfer length */ + SCpnt->cmnd[28] = (unsigned char) (this_count >> 24) & 0xff; + SCpnt->cmnd[29] = (unsigned char) (this_count >> 16) & 0xff; + SCpnt->cmnd[30] = (unsigned char) (this_count >> 8) & 0xff; + SCpnt->cmnd[31] = (unsigned char) this_count & 0xff; + } else if (block > 0xffffffff) { SCpnt->cmnd[0] += READ_16 - READ_6; - SCpnt->cmnd[1] |= blk_fua_rq(rq) ? 0x8 : 0; + SCpnt->cmnd[1] = protect | (blk_fua_rq(rq) ? 0x8 : 0); SCpnt->cmnd[2] = sizeof(block) > 4 ? (unsigned char) (block >> 56) & 0xff : 0; SCpnt->cmnd[3] = sizeof(block) > 4 ? (unsigned char) (block >> 48) & 0xff : 0; SCpnt->cmnd[4] = sizeof(block) > 4 ? (unsigned char) (block >> 40) & 0xff : 0; @@ -547,7 +612,7 @@ static int sd_prep_fn(struct request_queue *q, struct request *rq) this_count = 0xffff; SCpnt->cmnd[0] += READ_10 - READ_6; - SCpnt->cmnd[1] |= blk_fua_rq(rq) ? 0x8 : 0; + SCpnt->cmnd[1] = protect | (blk_fua_rq(rq) ? 0x8 : 0); SCpnt->cmnd[2] = (unsigned char) (block >> 24) & 0xff; SCpnt->cmnd[3] = (unsigned char) (block >> 16) & 0xff; SCpnt->cmnd[4] = (unsigned char) (block >> 8) & 0xff; @@ -578,8 +643,7 @@ static int sd_prep_fn(struct request_queue *q, struct request *rq) /* If DIF or DIX is enabled, tell HBA how to handle request */ if (host_dif || scsi_prot_sg_count(SCpnt)) - sd_dif_op(SCpnt, host_dif, scsi_prot_sg_count(SCpnt), - sdkp->protection_type); + sd_prot_op(SCpnt, host_dif); /* * We shouldn't disconnect in the middle of a sector, so with a dumb @@ -1023,6 +1087,7 @@ static int sd_done(struct scsi_cmnd *SCpnt) int result = SCpnt->result; unsigned int good_bytes = result ? 0 : scsi_bufflen(SCpnt); struct scsi_sense_hdr sshdr; + struct scsi_disk *sdkp = scsi_disk(SCpnt->request->rq_disk); int sense_valid = 0; int sense_deferred = 0; @@ -1084,6 +1149,10 @@ static int sd_done(struct scsi_cmnd *SCpnt) if (rq_data_dir(SCpnt->request) == READ && scsi_prot_sg_count(SCpnt)) sd_dif_complete(SCpnt, good_bytes); + if (scsi_host_dif_capable(sdkp->device->host, sdkp->protection_type) + == SD_DIF_TYPE2_PROTECTION && SCpnt->cmnd != SCpnt->request->cmd) + mempool_free(SCpnt->cmnd, sd_cdb_pool); + return good_bytes; } @@ -1238,34 +1307,28 @@ void sd_read_protection_type(struct scsi_disk *sdkp, unsigned char *buffer) u8 type; if (scsi_device_protection(sdp) == 0 || (buffer[12] & 1) == 0) - type = 0; - else - type = ((buffer[12] >> 1) & 7) + 1; /* P_TYPE 0 = Type 1 */ + return; - sdkp->protection_type = type; + type = ((buffer[12] >> 1) & 7) + 1; /* P_TYPE 0 = Type 1 */ - switch (type) { - case SD_DIF_TYPE0_PROTECTION: - case SD_DIF_TYPE1_PROTECTION: - case SD_DIF_TYPE3_PROTECTION: - break; + if (type == sdkp->protection_type || !sdkp->first_scan) + return; - case SD_DIF_TYPE2_PROTECTION: - sd_printk(KERN_ERR, sdkp, "formatted with DIF Type 2 " \ - "protection which is currently unsupported. " \ - "Disabling disk!\n"); - goto disable; + sdkp->protection_type = type; - default: - sd_printk(KERN_ERR, sdkp, "formatted with unknown " \ - "protection type %d. Disabling disk!\n", type); - goto disable; + if (type > SD_DIF_TYPE3_PROTECTION) { + sd_printk(KERN_ERR, sdkp, "formatted with unsupported " \ + "protection type %u. Disabling disk!\n", type); + sdkp->capacity = 0; + return; } - return; - -disable: - sdkp->capacity = 0; + if (scsi_host_dif_capable(sdp->host, type)) + sd_printk(KERN_NOTICE, sdkp, + "Enabling DIF Type %u protection\n", type); + else + sd_printk(KERN_NOTICE, sdkp, + "Disabling DIF Type %u protection\n", type); } static void read_capacity_error(struct scsi_disk *sdkp, struct scsi_device *sdp, @@ -2300,8 +2363,24 @@ static int __init init_sd(void) if (err) goto err_out_class; + sd_cdb_cache = kmem_cache_create("sd_ext_cdb", SD_EXT_CDB_SIZE, + 0, 0, NULL); + if (!sd_cdb_cache) { + printk(KERN_ERR "sd: can't init extended cdb cache\n"); + goto err_out_class; + } + + sd_cdb_pool = mempool_create_slab_pool(SD_MEMPOOL_SIZE, sd_cdb_cache); + if (!sd_cdb_pool) { + printk(KERN_ERR "sd: can't init extended cdb pool\n"); + goto err_out_cache; + } + return 0; +err_out_cache: + kmem_cache_destroy(sd_cdb_cache); + err_out_class: class_unregister(&sd_disk_class); err_out: @@ -2321,6 +2400,9 @@ static void __exit exit_sd(void) SCSI_LOG_HLQUEUE(3, printk("exit_sd: exiting sd driver\n")); + mempool_destroy(sd_cdb_pool); + kmem_cache_destroy(sd_cdb_cache); + scsi_unregister_driver(&sd_template.gendrv); class_unregister(&sd_disk_class); diff --git a/drivers/scsi/sd.h b/drivers/scsi/sd.h index 8474b5bad3fe..e374804d26fb 100644 --- a/drivers/scsi/sd.h +++ b/drivers/scsi/sd.h @@ -37,6 +37,11 @@ */ #define SD_LAST_BUGGY_SECTORS 8 +enum { + SD_EXT_CDB_SIZE = 32, /* Extended CDB size */ + SD_MEMPOOL_SIZE = 2, /* CDB pool size */ +}; + struct scsi_disk { struct scsi_driver *driver; /* always &sd_template */ struct scsi_device *device; @@ -101,16 +106,12 @@ struct sd_dif_tuple { #ifdef CONFIG_BLK_DEV_INTEGRITY -extern void sd_dif_op(struct scsi_cmnd *, unsigned int, unsigned int, unsigned int); extern void sd_dif_config_host(struct scsi_disk *); extern int sd_dif_prepare(struct request *rq, sector_t, unsigned int); extern void sd_dif_complete(struct scsi_cmnd *, unsigned int); #else /* CONFIG_BLK_DEV_INTEGRITY */ -static inline void sd_dif_op(struct scsi_cmnd *cmd, unsigned int a, unsigned int b, unsigned int c) -{ -} static inline void sd_dif_config_host(struct scsi_disk *disk) { } diff --git a/drivers/scsi/sd_dif.c b/drivers/scsi/sd_dif.c index 82f14a9482d0..88da97745710 100644 --- a/drivers/scsi/sd_dif.c +++ b/drivers/scsi/sd_dif.c @@ -320,15 +320,6 @@ void sd_dif_config_host(struct scsi_disk *sdkp) dif = 0; dix = 1; } - if (type) { - if (dif) - sd_printk(KERN_NOTICE, sdkp, - "Enabling DIF Type %d protection\n", type); - else - sd_printk(KERN_NOTICE, sdkp, - "Disabling DIF Type %d protection\n", type); - } - if (!dix) return; @@ -360,62 +351,6 @@ void sd_dif_config_host(struct scsi_disk *sdkp) } /* - * DIF DMA operation magic decoder ring. - */ -void sd_dif_op(struct scsi_cmnd *scmd, unsigned int dif, unsigned int dix, unsigned int type) -{ - int csum_convert, prot_op; - - prot_op = 0; - - /* Convert checksum? */ - if (scsi_host_get_guard(scmd->device->host) != SHOST_DIX_GUARD_CRC) - csum_convert = 1; - else - csum_convert = 0; - - BUG_ON(dif && (scmd->cmnd[0] == READ_6 || scmd->cmnd[0] == WRITE_6)); - - switch (scmd->cmnd[0]) { - case READ_6: - case READ_10: - case READ_12: - case READ_16: - if (dif && dix) - if (csum_convert) - prot_op = SCSI_PROT_READ_CONVERT; - else - prot_op = SCSI_PROT_READ_PASS; - else if (dif && !dix) - prot_op = SCSI_PROT_READ_STRIP; - else if (!dif && dix) - prot_op = SCSI_PROT_READ_INSERT; - - break; - - case WRITE_6: - case WRITE_10: - case WRITE_12: - case WRITE_16: - if (dif && dix) - if (csum_convert) - prot_op = SCSI_PROT_WRITE_CONVERT; - else - prot_op = SCSI_PROT_WRITE_PASS; - else if (dif && !dix) - prot_op = SCSI_PROT_WRITE_INSERT; - else if (!dif && dix) - prot_op = SCSI_PROT_WRITE_STRIP; - - break; - } - - scsi_set_prot_op(scmd, prot_op); - if (dif) - scsi_set_prot_type(scmd, type); -} - -/* * The virtual start sector is the one that was originally submitted * by the block layer. Due to partitioning, MD/DM cloning, etc. the * actual physical start sector is likely to be different. Remap diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c index 4968c4ced385..040f751809ea 100644 --- a/drivers/scsi/sg.c +++ b/drivers/scsi/sg.c @@ -1185,7 +1185,7 @@ sg_vma_fault(struct vm_area_struct *vma, struct vm_fault *vmf) return VM_FAULT_SIGBUS; } -static struct vm_operations_struct sg_mmap_vm_ops = { +static const struct vm_operations_struct sg_mmap_vm_ops = { .fault = sg_vma_fault, }; @@ -1317,7 +1317,7 @@ static void sg_rq_end_io(struct request *rq, int uptodate) } } -static struct file_operations sg_fops = { +static const struct file_operations sg_fops = { .owner = THIS_MODULE, .read = sg_read, .write = sg_write, @@ -1708,11 +1708,6 @@ static int sg_finish_rem_req(Sg_request * srp) Sg_scatter_hold *req_schp = &srp->data; SCSI_LOG_TIMEOUT(4, printk("sg_finish_rem_req: res_used=%d\n", (int) srp->res_used)); - if (srp->res_used) - sg_unlink_reserve(sfp, srp); - else - sg_remove_scat(req_schp); - if (srp->rq) { if (srp->bio) ret = blk_rq_unmap_user(srp->bio); @@ -1720,6 +1715,11 @@ static int sg_finish_rem_req(Sg_request * srp) blk_put_request(srp->rq); } + if (srp->res_used) + sg_unlink_reserve(sfp, srp); + else + sg_remove_scat(req_schp); + sg_remove_request(sfp, srp); return ret; @@ -2194,9 +2194,11 @@ static int sg_proc_seq_show_int(struct seq_file *s, void *v); static int sg_proc_single_open_adio(struct inode *inode, struct file *file); static ssize_t sg_proc_write_adio(struct file *filp, const char __user *buffer, size_t count, loff_t *off); -static struct file_operations adio_fops = { - /* .owner, .read and .llseek added in sg_proc_init() */ +static const struct file_operations adio_fops = { + .owner = THIS_MODULE, .open = sg_proc_single_open_adio, + .read = seq_read, + .llseek = seq_lseek, .write = sg_proc_write_adio, .release = single_release, }; @@ -2204,23 +2206,32 @@ static struct file_operations adio_fops = { static int sg_proc_single_open_dressz(struct inode *inode, struct file *file); static ssize_t sg_proc_write_dressz(struct file *filp, const char __user *buffer, size_t count, loff_t *off); -static struct file_operations dressz_fops = { +static const struct file_operations dressz_fops = { + .owner = THIS_MODULE, .open = sg_proc_single_open_dressz, + .read = seq_read, + .llseek = seq_lseek, .write = sg_proc_write_dressz, .release = single_release, }; static int sg_proc_seq_show_version(struct seq_file *s, void *v); static int sg_proc_single_open_version(struct inode *inode, struct file *file); -static struct file_operations version_fops = { +static const struct file_operations version_fops = { + .owner = THIS_MODULE, .open = sg_proc_single_open_version, + .read = seq_read, + .llseek = seq_lseek, .release = single_release, }; static int sg_proc_seq_show_devhdr(struct seq_file *s, void *v); static int sg_proc_single_open_devhdr(struct inode *inode, struct file *file); -static struct file_operations devhdr_fops = { +static const struct file_operations devhdr_fops = { + .owner = THIS_MODULE, .open = sg_proc_single_open_devhdr, + .read = seq_read, + .llseek = seq_lseek, .release = single_release, }; @@ -2229,11 +2240,14 @@ static int sg_proc_open_dev(struct inode *inode, struct file *file); static void * dev_seq_start(struct seq_file *s, loff_t *pos); static void * dev_seq_next(struct seq_file *s, void *v, loff_t *pos); static void dev_seq_stop(struct seq_file *s, void *v); -static struct file_operations dev_fops = { +static const struct file_operations dev_fops = { + .owner = THIS_MODULE, .open = sg_proc_open_dev, + .read = seq_read, + .llseek = seq_lseek, .release = seq_release, }; -static struct seq_operations dev_seq_ops = { +static const struct seq_operations dev_seq_ops = { .start = dev_seq_start, .next = dev_seq_next, .stop = dev_seq_stop, @@ -2242,11 +2256,14 @@ static struct seq_operations dev_seq_ops = { static int sg_proc_seq_show_devstrs(struct seq_file *s, void *v); static int sg_proc_open_devstrs(struct inode *inode, struct file *file); -static struct file_operations devstrs_fops = { +static const struct file_operations devstrs_fops = { + .owner = THIS_MODULE, .open = sg_proc_open_devstrs, + .read = seq_read, + .llseek = seq_lseek, .release = seq_release, }; -static struct seq_operations devstrs_seq_ops = { +static const struct seq_operations devstrs_seq_ops = { .start = dev_seq_start, .next = dev_seq_next, .stop = dev_seq_stop, @@ -2255,11 +2272,14 @@ static struct seq_operations devstrs_seq_ops = { static int sg_proc_seq_show_debug(struct seq_file *s, void *v); static int sg_proc_open_debug(struct inode *inode, struct file *file); -static struct file_operations debug_fops = { +static const struct file_operations debug_fops = { + .owner = THIS_MODULE, .open = sg_proc_open_debug, + .read = seq_read, + .llseek = seq_lseek, .release = seq_release, }; -static struct seq_operations debug_seq_ops = { +static const struct seq_operations debug_seq_ops = { .start = dev_seq_start, .next = dev_seq_next, .stop = dev_seq_stop, @@ -2269,7 +2289,7 @@ static struct seq_operations debug_seq_ops = { struct sg_proc_leaf { const char * name; - struct file_operations * fops; + const struct file_operations * fops; }; static struct sg_proc_leaf sg_proc_leaf_arr[] = { @@ -2295,9 +2315,6 @@ sg_proc_init(void) for (k = 0; k < num_leaves; ++k) { leaf = &sg_proc_leaf_arr[k]; mask = leaf->fops->write ? S_IRUGO | S_IWUSR : S_IRUGO; - leaf->fops->owner = THIS_MODULE; - leaf->fops->read = seq_read; - leaf->fops->llseek = seq_lseek; proc_create(leaf->name, mask, sg_proc_sgp, leaf->fops); } return 0; diff --git a/drivers/scsi/sr.c b/drivers/scsi/sr.c index eb61f7a70e1d..d6f340f48a3b 100644 --- a/drivers/scsi/sr.c +++ b/drivers/scsi/sr.c @@ -684,14 +684,20 @@ static void get_sectorsize(struct scsi_cd *cd) cd->capacity = 0x1fffff; sector_size = 2048; /* A guess, just in case */ } else { -#if 0 - if (cdrom_get_last_written(&cd->cdi, - &cd->capacity)) -#endif - cd->capacity = 1 + ((buffer[0] << 24) | - (buffer[1] << 16) | - (buffer[2] << 8) | - buffer[3]); + long last_written; + + cd->capacity = 1 + ((buffer[0] << 24) | (buffer[1] << 16) | + (buffer[2] << 8) | buffer[3]); + /* + * READ_CAPACITY doesn't return the correct size on + * certain UDF media. If last_written is larger, use + * it instead. + * + * http://bugzilla.kernel.org/show_bug.cgi?id=9668 + */ + if (!cdrom_get_last_written(&cd->cdi, &last_written)) + cd->capacity = max_t(long, cd->capacity, last_written); + sector_size = (buffer[4] << 24) | (buffer[5] << 16) | (buffer[6] << 8) | buffer[7]; switch (sector_size) { diff --git a/drivers/scsi/st.c b/drivers/scsi/st.c index b33d04250bbc..12d58a7ed6bc 100644 --- a/drivers/scsi/st.c +++ b/drivers/scsi/st.c @@ -2859,11 +2859,8 @@ static int st_int_ioctl(struct scsi_tape *STp, unsigned int cmd_in, unsigned lon ioctl_result = st_int_ioctl(STp, MTBSF, 1); if (cmd_in == MTSETBLK || cmd_in == SET_DENS_AND_BLK) { - int old_block_size = STp->block_size; STp->block_size = arg & MT_ST_BLKSIZE_MASK; if (STp->block_size != 0) { - if (old_block_size == 0) - normalize_buffer(STp->buffer); (STp->buffer)->buffer_blocks = (STp->buffer)->buffer_size / STp->block_size; } diff --git a/drivers/serial/8250.c b/drivers/serial/8250.c index 2209620d2349..737b4c960971 100644 --- a/drivers/serial/8250.c +++ b/drivers/serial/8250.c @@ -64,6 +64,8 @@ static int serial_index(struct uart_port *port) return (serial8250_reg.minor - 64) + port->line; } +static unsigned int skip_txen_test; /* force skip of txen test at init time */ + /* * Debugging. */ @@ -1087,7 +1089,7 @@ static void autoconfig(struct uart_8250_port *up, unsigned int probeflags) if (!up->port.iobase && !up->port.mapbase && !up->port.membase) return; - DEBUG_AUTOCONF("ttyS%d: autoconf (0x%04x, 0x%p): ", + DEBUG_AUTOCONF("ttyS%d: autoconf (0x%04lx, 0x%p): ", serial_index(&up->port), up->port.iobase, up->port.membase); /* @@ -2108,7 +2110,7 @@ static int serial8250_startup(struct uart_port *port) is variable. So, let's just don't test if we receive TX irq. This way, we'll never enable UART_BUG_TXEN. */ - if (up->port.flags & UPF_NO_TXEN_TEST) + if (skip_txen_test || up->port.flags & UPF_NO_TXEN_TEST) goto dont_test_tx_en; /* @@ -3248,6 +3250,9 @@ MODULE_PARM_DESC(share_irqs, "Share IRQs with other non-8250/16x50 devices" module_param(nr_uarts, uint, 0644); MODULE_PARM_DESC(nr_uarts, "Maximum number of UARTs supported. (1-" __MODULE_STRING(CONFIG_SERIAL_8250_NR_UARTS) ")"); +module_param(skip_txen_test, uint, 0644); +MODULE_PARM_DESC(skip_txen_test, "Skip checking for the TXEN bug at init time"); + #ifdef CONFIG_SERIAL_8250_RSA module_param_array(probe_rsa, ulong, &probe_rsa_count, 0444); MODULE_PARM_DESC(probe_rsa, "Probe I/O ports for RSA"); diff --git a/drivers/serial/8250_pci.c b/drivers/serial/8250_pci.c index e7108e75653d..b28af13c45a1 100644 --- a/drivers/serial/8250_pci.c +++ b/drivers/serial/8250_pci.c @@ -1561,11 +1561,16 @@ enum pci_board_num_t { pbn_exar_XR17C152, pbn_exar_XR17C154, pbn_exar_XR17C158, + pbn_exar_ibm_saturn, pbn_pasemi_1682M, pbn_ni8430_2, pbn_ni8430_4, pbn_ni8430_8, pbn_ni8430_16, + pbn_ADDIDATA_PCIe_1_3906250, + pbn_ADDIDATA_PCIe_2_3906250, + pbn_ADDIDATA_PCIe_4_3906250, + pbn_ADDIDATA_PCIe_8_3906250, }; /* @@ -2146,6 +2151,13 @@ static struct pciserial_board pci_boards[] __devinitdata = { .base_baud = 921600, .uart_offset = 0x200, }, + [pbn_exar_ibm_saturn] = { + .flags = FL_BASE0, + .num_ports = 1, + .base_baud = 921600, + .uart_offset = 0x200, + }, + /* * PA Semi PWRficient PA6T-1682M on-chip UART */ @@ -2185,6 +2197,37 @@ static struct pciserial_board pci_boards[] __devinitdata = { .uart_offset = 0x10, .first_offset = 0x800, }, + /* + * ADDI-DATA GmbH PCI-Express communication cards <info@addi-data.com> + */ + [pbn_ADDIDATA_PCIe_1_3906250] = { + .flags = FL_BASE0, + .num_ports = 1, + .base_baud = 3906250, + .uart_offset = 0x200, + .first_offset = 0x1000, + }, + [pbn_ADDIDATA_PCIe_2_3906250] = { + .flags = FL_BASE0, + .num_ports = 2, + .base_baud = 3906250, + .uart_offset = 0x200, + .first_offset = 0x1000, + }, + [pbn_ADDIDATA_PCIe_4_3906250] = { + .flags = FL_BASE0, + .num_ports = 4, + .base_baud = 3906250, + .uart_offset = 0x200, + .first_offset = 0x1000, + }, + [pbn_ADDIDATA_PCIe_8_3906250] = { + .flags = FL_BASE0, + .num_ports = 8, + .base_baud = 3906250, + .uart_offset = 0x200, + .first_offset = 0x1000, + }, }; static const struct pci_device_id softmodem_blacklist[] = { @@ -2340,7 +2383,7 @@ pciserial_init_ports(struct pci_dev *dev, const struct pciserial_board *board) break; #ifdef SERIAL_DEBUG_PCI - printk(KERN_DEBUG "Setup PCI port: port %x, irq %d, type %d\n", + printk(KERN_DEBUG "Setup PCI port: port %lx, irq %d, type %d\n", serial_port.iobase, serial_port.irq, serial_port.iotype); #endif @@ -2649,6 +2692,9 @@ static struct pci_device_id serial_pci_tbl[] = { PCI_SUBVENDOR_ID_CONNECT_TECH, PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_8_485, 0, 0, pbn_b0_8_1843200_200 }, + { PCI_VENDOR_ID_EXAR, PCI_DEVICE_ID_EXAR_XR17C152, + PCI_VENDOR_ID_IBM, PCI_SUBDEVICE_ID_IBM_SATURN_SERIAL_ONE_PORT, + 0, 0, pbn_exar_ibm_saturn }, { PCI_VENDOR_ID_SEALEVEL, PCI_DEVICE_ID_SEALEVEL_U530, PCI_ANY_ID, PCI_ANY_ID, 0, 0, @@ -3093,6 +3139,12 @@ static struct pci_device_id serial_pci_tbl[] = { { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_QUATRO_B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, pbn_b0_bt_2_115200 }, + { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_QUATTRO_A, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_bt_2_115200 }, + { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_QUATTRO_B, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_bt_2_115200 }, { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_OCTO_A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, pbn_b0_bt_4_460800 }, @@ -3556,6 +3608,38 @@ static struct pci_device_id serial_pci_tbl[] = { 0, pbn_b0_8_115200 }, + { PCI_VENDOR_ID_ADDIDATA, + PCI_DEVICE_ID_ADDIDATA_APCIe7500, + PCI_ANY_ID, + PCI_ANY_ID, + 0, + 0, + pbn_ADDIDATA_PCIe_4_3906250 }, + + { PCI_VENDOR_ID_ADDIDATA, + PCI_DEVICE_ID_ADDIDATA_APCIe7420, + PCI_ANY_ID, + PCI_ANY_ID, + 0, + 0, + pbn_ADDIDATA_PCIe_2_3906250 }, + + { PCI_VENDOR_ID_ADDIDATA, + PCI_DEVICE_ID_ADDIDATA_APCIe7300, + PCI_ANY_ID, + PCI_ANY_ID, + 0, + 0, + pbn_ADDIDATA_PCIe_1_3906250 }, + + { PCI_VENDOR_ID_ADDIDATA, + PCI_DEVICE_ID_ADDIDATA_APCIe7800, + PCI_ANY_ID, + PCI_ANY_ID, + 0, + 0, + pbn_ADDIDATA_PCIe_8_3906250 }, + { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9835, PCI_VENDOR_ID_IBM, 0x0299, 0, 0, pbn_b0_bt_2_115200 }, diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig index 03422ce878cf..e52257257279 100644 --- a/drivers/serial/Kconfig +++ b/drivers/serial/Kconfig @@ -862,7 +862,7 @@ config SERIAL_IMX_CONSOLE config SERIAL_UARTLITE tristate "Xilinx uartlite serial port support" - depends on PPC32 || MICROBLAZE + depends on PPC32 || MICROBLAZE || MFD_TIMBERDALE select SERIAL_CORE help Say Y here if you want to use the Xilinx uartlite serial controller. @@ -1458,4 +1458,23 @@ config SERIAL_TIMBERDALE ---help--- Add support for UART controller on timberdale. +config SERIAL_BCM63XX + tristate "bcm63xx serial port support" + select SERIAL_CORE + depends on BCM63XX + help + If you have a bcm63xx CPU, you can enable its onboard + serial port by enabling this options. + + To compile this driver as a module, choose M here: the + module will be called bcm963xx_uart. + +config SERIAL_BCM63XX_CONSOLE + bool "Console on bcm63xx serial port" + depends on SERIAL_BCM63XX=y + select SERIAL_CORE_CONSOLE + help + If you have enabled the serial port on the bcm63xx CPU + you can make it the console by answering Y to this option. + endmenu diff --git a/drivers/serial/Makefile b/drivers/serial/Makefile index 97f6fcc8b432..d21d5dd5d048 100644 --- a/drivers/serial/Makefile +++ b/drivers/serial/Makefile @@ -34,6 +34,7 @@ obj-$(CONFIG_SERIAL_CLPS711X) += clps711x.o obj-$(CONFIG_SERIAL_PXA) += pxa.o obj-$(CONFIG_SERIAL_PNX8XXX) += pnx8xxx_uart.o obj-$(CONFIG_SERIAL_SA1100) += sa1100.o +obj-$(CONFIG_SERIAL_BCM63XX) += bcm63xx_uart.o obj-$(CONFIG_SERIAL_BFIN) += bfin_5xx.o obj-$(CONFIG_SERIAL_BFIN_SPORT) += bfin_sport_uart.o obj-$(CONFIG_SERIAL_SAMSUNG) += samsung.o diff --git a/drivers/serial/atmel_serial.c b/drivers/serial/atmel_serial.c index 3551c5cb7094..9d948bccafaf 100644 --- a/drivers/serial/atmel_serial.c +++ b/drivers/serial/atmel_serial.c @@ -1531,7 +1531,7 @@ static int __devinit atmel_serial_probe(struct platform_device *pdev) void *data; int ret; - BUILD_BUG_ON(!is_power_of_2(ATMEL_SERIAL_RINGSIZE)); + BUILD_BUG_ON(ATMEL_SERIAL_RINGSIZE & (ATMEL_SERIAL_RINGSIZE - 1)); port = &atmel_ports[pdev->id]; port->backup_imr = 0; diff --git a/drivers/serial/bcm63xx_uart.c b/drivers/serial/bcm63xx_uart.c new file mode 100644 index 000000000000..beddaa6e9069 --- /dev/null +++ b/drivers/serial/bcm63xx_uart.c @@ -0,0 +1,890 @@ +/* + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Derived from many drivers using generic_serial interface. + * + * Copyright (C) 2008 Maxime Bizon <mbizon@freebox.fr> + * + * Serial driver for BCM63xx integrated UART. + * + * Hardware flow control was _not_ tested since I only have RX/TX on + * my board. + */ + +#if defined(CONFIG_SERIAL_BCM63XX_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) +#define SUPPORT_SYSRQ +#endif + +#include <linux/kernel.h> +#include <linux/platform_device.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/module.h> +#include <linux/console.h> +#include <linux/clk.h> +#include <linux/tty.h> +#include <linux/tty_flip.h> +#include <linux/sysrq.h> +#include <linux/serial.h> +#include <linux/serial_core.h> + +#include <bcm63xx_clk.h> +#include <bcm63xx_irq.h> +#include <bcm63xx_regs.h> +#include <bcm63xx_io.h> + +#define BCM63XX_NR_UARTS 1 + +static struct uart_port ports[BCM63XX_NR_UARTS]; + +/* + * rx interrupt mask / stat + * + * mask: + * - rx fifo full + * - rx fifo above threshold + * - rx fifo not empty for too long + */ +#define UART_RX_INT_MASK (UART_IR_MASK(UART_IR_RXOVER) | \ + UART_IR_MASK(UART_IR_RXTHRESH) | \ + UART_IR_MASK(UART_IR_RXTIMEOUT)) + +#define UART_RX_INT_STAT (UART_IR_STAT(UART_IR_RXOVER) | \ + UART_IR_STAT(UART_IR_RXTHRESH) | \ + UART_IR_STAT(UART_IR_RXTIMEOUT)) + +/* + * tx interrupt mask / stat + * + * mask: + * - tx fifo empty + * - tx fifo below threshold + */ +#define UART_TX_INT_MASK (UART_IR_MASK(UART_IR_TXEMPTY) | \ + UART_IR_MASK(UART_IR_TXTRESH)) + +#define UART_TX_INT_STAT (UART_IR_STAT(UART_IR_TXEMPTY) | \ + UART_IR_STAT(UART_IR_TXTRESH)) + +/* + * external input interrupt + * + * mask: any edge on CTS, DCD + */ +#define UART_EXTINP_INT_MASK (UART_EXTINP_IRMASK(UART_EXTINP_IR_CTS) | \ + UART_EXTINP_IRMASK(UART_EXTINP_IR_DCD)) + +/* + * handy uart register accessor + */ +static inline unsigned int bcm_uart_readl(struct uart_port *port, + unsigned int offset) +{ + return bcm_readl(port->membase + offset); +} + +static inline void bcm_uart_writel(struct uart_port *port, + unsigned int value, unsigned int offset) +{ + bcm_writel(value, port->membase + offset); +} + +/* + * serial core request to check if uart tx fifo is empty + */ +static unsigned int bcm_uart_tx_empty(struct uart_port *port) +{ + unsigned int val; + + val = bcm_uart_readl(port, UART_IR_REG); + return (val & UART_IR_STAT(UART_IR_TXEMPTY)) ? 1 : 0; +} + +/* + * serial core request to set RTS and DTR pin state and loopback mode + */ +static void bcm_uart_set_mctrl(struct uart_port *port, unsigned int mctrl) +{ + unsigned int val; + + val = bcm_uart_readl(port, UART_MCTL_REG); + val &= ~(UART_MCTL_DTR_MASK | UART_MCTL_RTS_MASK); + /* invert of written value is reflected on the pin */ + if (!(mctrl & TIOCM_DTR)) + val |= UART_MCTL_DTR_MASK; + if (!(mctrl & TIOCM_RTS)) + val |= UART_MCTL_RTS_MASK; + bcm_uart_writel(port, val, UART_MCTL_REG); + + val = bcm_uart_readl(port, UART_CTL_REG); + if (mctrl & TIOCM_LOOP) + val |= UART_CTL_LOOPBACK_MASK; + else + val &= ~UART_CTL_LOOPBACK_MASK; + bcm_uart_writel(port, val, UART_CTL_REG); +} + +/* + * serial core request to return RI, CTS, DCD and DSR pin state + */ +static unsigned int bcm_uart_get_mctrl(struct uart_port *port) +{ + unsigned int val, mctrl; + + mctrl = 0; + val = bcm_uart_readl(port, UART_EXTINP_REG); + if (val & UART_EXTINP_RI_MASK) + mctrl |= TIOCM_RI; + if (val & UART_EXTINP_CTS_MASK) + mctrl |= TIOCM_CTS; + if (val & UART_EXTINP_DCD_MASK) + mctrl |= TIOCM_CD; + if (val & UART_EXTINP_DSR_MASK) + mctrl |= TIOCM_DSR; + return mctrl; +} + +/* + * serial core request to disable tx ASAP (used for flow control) + */ +static void bcm_uart_stop_tx(struct uart_port *port) +{ + unsigned int val; + + val = bcm_uart_readl(port, UART_CTL_REG); + val &= ~(UART_CTL_TXEN_MASK); + bcm_uart_writel(port, val, UART_CTL_REG); + + val = bcm_uart_readl(port, UART_IR_REG); + val &= ~UART_TX_INT_MASK; + bcm_uart_writel(port, val, UART_IR_REG); +} + +/* + * serial core request to (re)enable tx + */ +static void bcm_uart_start_tx(struct uart_port *port) +{ + unsigned int val; + + val = bcm_uart_readl(port, UART_IR_REG); + val |= UART_TX_INT_MASK; + bcm_uart_writel(port, val, UART_IR_REG); + + val = bcm_uart_readl(port, UART_CTL_REG); + val |= UART_CTL_TXEN_MASK; + bcm_uart_writel(port, val, UART_CTL_REG); +} + +/* + * serial core request to stop rx, called before port shutdown + */ +static void bcm_uart_stop_rx(struct uart_port *port) +{ + unsigned int val; + + val = bcm_uart_readl(port, UART_IR_REG); + val &= ~UART_RX_INT_MASK; + bcm_uart_writel(port, val, UART_IR_REG); +} + +/* + * serial core request to enable modem status interrupt reporting + */ +static void bcm_uart_enable_ms(struct uart_port *port) +{ + unsigned int val; + + val = bcm_uart_readl(port, UART_IR_REG); + val |= UART_IR_MASK(UART_IR_EXTIP); + bcm_uart_writel(port, val, UART_IR_REG); +} + +/* + * serial core request to start/stop emitting break char + */ +static void bcm_uart_break_ctl(struct uart_port *port, int ctl) +{ + unsigned long flags; + unsigned int val; + + spin_lock_irqsave(&port->lock, flags); + + val = bcm_uart_readl(port, UART_CTL_REG); + if (ctl) + val |= UART_CTL_XMITBRK_MASK; + else + val &= ~UART_CTL_XMITBRK_MASK; + bcm_uart_writel(port, val, UART_CTL_REG); + + spin_unlock_irqrestore(&port->lock, flags); +} + +/* + * return port type in string format + */ +static const char *bcm_uart_type(struct uart_port *port) +{ + return (port->type == PORT_BCM63XX) ? "bcm63xx_uart" : NULL; +} + +/* + * read all chars in rx fifo and send them to core + */ +static void bcm_uart_do_rx(struct uart_port *port) +{ + struct tty_struct *tty; + unsigned int max_count; + + /* limit number of char read in interrupt, should not be + * higher than fifo size anyway since we're much faster than + * serial port */ + max_count = 32; + tty = port->info->port.tty; + do { + unsigned int iestat, c, cstat; + char flag; + + /* get overrun/fifo empty information from ier + * register */ + iestat = bcm_uart_readl(port, UART_IR_REG); + if (!(iestat & UART_IR_STAT(UART_IR_RXNOTEMPTY))) + break; + + cstat = c = bcm_uart_readl(port, UART_FIFO_REG); + port->icount.rx++; + flag = TTY_NORMAL; + c &= 0xff; + + if (unlikely((cstat & UART_FIFO_ANYERR_MASK))) { + /* do stats first */ + if (cstat & UART_FIFO_BRKDET_MASK) { + port->icount.brk++; + if (uart_handle_break(port)) + continue; + } + + if (cstat & UART_FIFO_PARERR_MASK) + port->icount.parity++; + if (cstat & UART_FIFO_FRAMEERR_MASK) + port->icount.frame++; + + /* update flag wrt read_status_mask */ + cstat &= port->read_status_mask; + if (cstat & UART_FIFO_BRKDET_MASK) + flag = TTY_BREAK; + if (cstat & UART_FIFO_FRAMEERR_MASK) + flag = TTY_FRAME; + if (cstat & UART_FIFO_PARERR_MASK) + flag = TTY_PARITY; + } + + if (uart_handle_sysrq_char(port, c)) + continue; + + if (unlikely(iestat & UART_IR_STAT(UART_IR_RXOVER))) { + port->icount.overrun++; + tty_insert_flip_char(tty, 0, TTY_OVERRUN); + } + + if ((cstat & port->ignore_status_mask) == 0) + tty_insert_flip_char(tty, c, flag); + + } while (--max_count); + + tty_flip_buffer_push(tty); +} + +/* + * fill tx fifo with chars to send, stop when fifo is about to be full + * or when all chars have been sent. + */ +static void bcm_uart_do_tx(struct uart_port *port) +{ + struct circ_buf *xmit; + unsigned int val, max_count; + + if (port->x_char) { + bcm_uart_writel(port, port->x_char, UART_FIFO_REG); + port->icount.tx++; + port->x_char = 0; + return; + } + + if (uart_tx_stopped(port)) { + bcm_uart_stop_tx(port); + return; + } + + xmit = &port->info->xmit; + if (uart_circ_empty(xmit)) + goto txq_empty; + + val = bcm_uart_readl(port, UART_MCTL_REG); + val = (val & UART_MCTL_TXFIFOFILL_MASK) >> UART_MCTL_TXFIFOFILL_SHIFT; + max_count = port->fifosize - val; + + while (max_count--) { + unsigned int c; + + c = xmit->buf[xmit->tail]; + bcm_uart_writel(port, c, UART_FIFO_REG); + xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); + port->icount.tx++; + if (uart_circ_empty(xmit)) + break; + } + + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(port); + + if (uart_circ_empty(xmit)) + goto txq_empty; + return; + +txq_empty: + /* nothing to send, disable transmit interrupt */ + val = bcm_uart_readl(port, UART_IR_REG); + val &= ~UART_TX_INT_MASK; + bcm_uart_writel(port, val, UART_IR_REG); + return; +} + +/* + * process uart interrupt + */ +static irqreturn_t bcm_uart_interrupt(int irq, void *dev_id) +{ + struct uart_port *port; + unsigned int irqstat; + + port = dev_id; + spin_lock(&port->lock); + + irqstat = bcm_uart_readl(port, UART_IR_REG); + if (irqstat & UART_RX_INT_STAT) + bcm_uart_do_rx(port); + + if (irqstat & UART_TX_INT_STAT) + bcm_uart_do_tx(port); + + if (irqstat & UART_IR_MASK(UART_IR_EXTIP)) { + unsigned int estat; + + estat = bcm_uart_readl(port, UART_EXTINP_REG); + if (estat & UART_EXTINP_IRSTAT(UART_EXTINP_IR_CTS)) + uart_handle_cts_change(port, + estat & UART_EXTINP_CTS_MASK); + if (estat & UART_EXTINP_IRSTAT(UART_EXTINP_IR_DCD)) + uart_handle_dcd_change(port, + estat & UART_EXTINP_DCD_MASK); + } + + spin_unlock(&port->lock); + return IRQ_HANDLED; +} + +/* + * enable rx & tx operation on uart + */ +static void bcm_uart_enable(struct uart_port *port) +{ + unsigned int val; + + val = bcm_uart_readl(port, UART_CTL_REG); + val |= (UART_CTL_BRGEN_MASK | UART_CTL_TXEN_MASK | UART_CTL_RXEN_MASK); + bcm_uart_writel(port, val, UART_CTL_REG); +} + +/* + * disable rx & tx operation on uart + */ +static void bcm_uart_disable(struct uart_port *port) +{ + unsigned int val; + + val = bcm_uart_readl(port, UART_CTL_REG); + val &= ~(UART_CTL_BRGEN_MASK | UART_CTL_TXEN_MASK | + UART_CTL_RXEN_MASK); + bcm_uart_writel(port, val, UART_CTL_REG); +} + +/* + * clear all unread data in rx fifo and unsent data in tx fifo + */ +static void bcm_uart_flush(struct uart_port *port) +{ + unsigned int val; + + /* empty rx and tx fifo */ + val = bcm_uart_readl(port, UART_CTL_REG); + val |= UART_CTL_RSTRXFIFO_MASK | UART_CTL_RSTTXFIFO_MASK; + bcm_uart_writel(port, val, UART_CTL_REG); + + /* read any pending char to make sure all irq status are + * cleared */ + (void)bcm_uart_readl(port, UART_FIFO_REG); +} + +/* + * serial core request to initialize uart and start rx operation + */ +static int bcm_uart_startup(struct uart_port *port) +{ + unsigned int val; + int ret; + + /* mask all irq and flush port */ + bcm_uart_disable(port); + bcm_uart_writel(port, 0, UART_IR_REG); + bcm_uart_flush(port); + + /* clear any pending external input interrupt */ + (void)bcm_uart_readl(port, UART_EXTINP_REG); + + /* set rx/tx fifo thresh to fifo half size */ + val = bcm_uart_readl(port, UART_MCTL_REG); + val &= ~(UART_MCTL_RXFIFOTHRESH_MASK | UART_MCTL_TXFIFOTHRESH_MASK); + val |= (port->fifosize / 2) << UART_MCTL_RXFIFOTHRESH_SHIFT; + val |= (port->fifosize / 2) << UART_MCTL_TXFIFOTHRESH_SHIFT; + bcm_uart_writel(port, val, UART_MCTL_REG); + + /* set rx fifo timeout to 1 char time */ + val = bcm_uart_readl(port, UART_CTL_REG); + val &= ~UART_CTL_RXTMOUTCNT_MASK; + val |= 1 << UART_CTL_RXTMOUTCNT_SHIFT; + bcm_uart_writel(port, val, UART_CTL_REG); + + /* report any edge on dcd and cts */ + val = UART_EXTINP_INT_MASK; + val |= UART_EXTINP_DCD_NOSENSE_MASK; + val |= UART_EXTINP_CTS_NOSENSE_MASK; + bcm_uart_writel(port, val, UART_EXTINP_REG); + + /* register irq and enable rx interrupts */ + ret = request_irq(port->irq, bcm_uart_interrupt, 0, + bcm_uart_type(port), port); + if (ret) + return ret; + bcm_uart_writel(port, UART_RX_INT_MASK, UART_IR_REG); + bcm_uart_enable(port); + return 0; +} + +/* + * serial core request to flush & disable uart + */ +static void bcm_uart_shutdown(struct uart_port *port) +{ + unsigned long flags; + + spin_lock_irqsave(&port->lock, flags); + bcm_uart_writel(port, 0, UART_IR_REG); + spin_unlock_irqrestore(&port->lock, flags); + + bcm_uart_disable(port); + bcm_uart_flush(port); + free_irq(port->irq, port); +} + +/* + * serial core request to change current uart setting + */ +static void bcm_uart_set_termios(struct uart_port *port, + struct ktermios *new, + struct ktermios *old) +{ + unsigned int ctl, baud, quot, ier; + unsigned long flags; + + spin_lock_irqsave(&port->lock, flags); + + /* disable uart while changing speed */ + bcm_uart_disable(port); + bcm_uart_flush(port); + + /* update Control register */ + ctl = bcm_uart_readl(port, UART_CTL_REG); + ctl &= ~UART_CTL_BITSPERSYM_MASK; + + switch (new->c_cflag & CSIZE) { + case CS5: + ctl |= (0 << UART_CTL_BITSPERSYM_SHIFT); + break; + case CS6: + ctl |= (1 << UART_CTL_BITSPERSYM_SHIFT); + break; + case CS7: + ctl |= (2 << UART_CTL_BITSPERSYM_SHIFT); + break; + default: + ctl |= (3 << UART_CTL_BITSPERSYM_SHIFT); + break; + } + + ctl &= ~UART_CTL_STOPBITS_MASK; + if (new->c_cflag & CSTOPB) + ctl |= UART_CTL_STOPBITS_2; + else + ctl |= UART_CTL_STOPBITS_1; + + ctl &= ~(UART_CTL_RXPAREN_MASK | UART_CTL_TXPAREN_MASK); + if (new->c_cflag & PARENB) + ctl |= (UART_CTL_RXPAREN_MASK | UART_CTL_TXPAREN_MASK); + ctl &= ~(UART_CTL_RXPAREVEN_MASK | UART_CTL_TXPAREVEN_MASK); + if (new->c_cflag & PARODD) + ctl |= (UART_CTL_RXPAREVEN_MASK | UART_CTL_TXPAREVEN_MASK); + bcm_uart_writel(port, ctl, UART_CTL_REG); + + /* update Baudword register */ + baud = uart_get_baud_rate(port, new, old, 0, port->uartclk / 16); + quot = uart_get_divisor(port, baud) - 1; + bcm_uart_writel(port, quot, UART_BAUD_REG); + + /* update Interrupt register */ + ier = bcm_uart_readl(port, UART_IR_REG); + + ier &= ~UART_IR_MASK(UART_IR_EXTIP); + if (UART_ENABLE_MS(port, new->c_cflag)) + ier |= UART_IR_MASK(UART_IR_EXTIP); + + bcm_uart_writel(port, ier, UART_IR_REG); + + /* update read/ignore mask */ + port->read_status_mask = UART_FIFO_VALID_MASK; + if (new->c_iflag & INPCK) { + port->read_status_mask |= UART_FIFO_FRAMEERR_MASK; + port->read_status_mask |= UART_FIFO_PARERR_MASK; + } + if (new->c_iflag & (BRKINT)) + port->read_status_mask |= UART_FIFO_BRKDET_MASK; + + port->ignore_status_mask = 0; + if (new->c_iflag & IGNPAR) + port->ignore_status_mask |= UART_FIFO_PARERR_MASK; + if (new->c_iflag & IGNBRK) + port->ignore_status_mask |= UART_FIFO_BRKDET_MASK; + if (!(new->c_cflag & CREAD)) + port->ignore_status_mask |= UART_FIFO_VALID_MASK; + + uart_update_timeout(port, new->c_cflag, baud); + bcm_uart_enable(port); + spin_unlock_irqrestore(&port->lock, flags); +} + +/* + * serial core request to claim uart iomem + */ +static int bcm_uart_request_port(struct uart_port *port) +{ + unsigned int size; + + size = RSET_UART_SIZE; + if (!request_mem_region(port->mapbase, size, "bcm63xx")) { + dev_err(port->dev, "Memory region busy\n"); + return -EBUSY; + } + + port->membase = ioremap(port->mapbase, size); + if (!port->membase) { + dev_err(port->dev, "Unable to map registers\n"); + release_mem_region(port->mapbase, size); + return -EBUSY; + } + return 0; +} + +/* + * serial core request to release uart iomem + */ +static void bcm_uart_release_port(struct uart_port *port) +{ + release_mem_region(port->mapbase, RSET_UART_SIZE); + iounmap(port->membase); +} + +/* + * serial core request to do any port required autoconfiguration + */ +static void bcm_uart_config_port(struct uart_port *port, int flags) +{ + if (flags & UART_CONFIG_TYPE) { + if (bcm_uart_request_port(port)) + return; + port->type = PORT_BCM63XX; + } +} + +/* + * serial core request to check that port information in serinfo are + * suitable + */ +static int bcm_uart_verify_port(struct uart_port *port, + struct serial_struct *serinfo) +{ + if (port->type != PORT_BCM63XX) + return -EINVAL; + if (port->irq != serinfo->irq) + return -EINVAL; + if (port->iotype != serinfo->io_type) + return -EINVAL; + if (port->mapbase != (unsigned long)serinfo->iomem_base) + return -EINVAL; + return 0; +} + +/* serial core callbacks */ +static struct uart_ops bcm_uart_ops = { + .tx_empty = bcm_uart_tx_empty, + .get_mctrl = bcm_uart_get_mctrl, + .set_mctrl = bcm_uart_set_mctrl, + .start_tx = bcm_uart_start_tx, + .stop_tx = bcm_uart_stop_tx, + .stop_rx = bcm_uart_stop_rx, + .enable_ms = bcm_uart_enable_ms, + .break_ctl = bcm_uart_break_ctl, + .startup = bcm_uart_startup, + .shutdown = bcm_uart_shutdown, + .set_termios = bcm_uart_set_termios, + .type = bcm_uart_type, + .release_port = bcm_uart_release_port, + .request_port = bcm_uart_request_port, + .config_port = bcm_uart_config_port, + .verify_port = bcm_uart_verify_port, +}; + + + +#ifdef CONFIG_SERIAL_BCM63XX_CONSOLE +static inline void wait_for_xmitr(struct uart_port *port) +{ + unsigned int tmout; + + /* Wait up to 10ms for the character(s) to be sent. */ + tmout = 10000; + while (--tmout) { + unsigned int val; + + val = bcm_uart_readl(port, UART_IR_REG); + if (val & UART_IR_STAT(UART_IR_TXEMPTY)) + break; + udelay(1); + } + + /* Wait up to 1s for flow control if necessary */ + if (port->flags & UPF_CONS_FLOW) { + tmout = 1000000; + while (--tmout) { + unsigned int val; + + val = bcm_uart_readl(port, UART_EXTINP_REG); + if (val & UART_EXTINP_CTS_MASK) + break; + udelay(1); + } + } +} + +/* + * output given char + */ +static void bcm_console_putchar(struct uart_port *port, int ch) +{ + wait_for_xmitr(port); + bcm_uart_writel(port, ch, UART_FIFO_REG); +} + +/* + * console core request to output given string + */ +static void bcm_console_write(struct console *co, const char *s, + unsigned int count) +{ + struct uart_port *port; + unsigned long flags; + int locked; + + port = &ports[co->index]; + + local_irq_save(flags); + if (port->sysrq) { + /* bcm_uart_interrupt() already took the lock */ + locked = 0; + } else if (oops_in_progress) { + locked = spin_trylock(&port->lock); + } else { + spin_lock(&port->lock); + locked = 1; + } + + /* call helper to deal with \r\n */ + uart_console_write(port, s, count, bcm_console_putchar); + + /* and wait for char to be transmitted */ + wait_for_xmitr(port); + + if (locked) + spin_unlock(&port->lock); + local_irq_restore(flags); +} + +/* + * console core request to setup given console, find matching uart + * port and setup it. + */ +static int bcm_console_setup(struct console *co, char *options) +{ + struct uart_port *port; + int baud = 9600; + int bits = 8; + int parity = 'n'; + int flow = 'n'; + + if (co->index < 0 || co->index >= BCM63XX_NR_UARTS) + return -EINVAL; + port = &ports[co->index]; + if (!port->membase) + return -ENODEV; + if (options) + uart_parse_options(options, &baud, &parity, &bits, &flow); + + return uart_set_options(port, co, baud, parity, bits, flow); +} + +static struct uart_driver bcm_uart_driver; + +static struct console bcm63xx_console = { + .name = "ttyS", + .write = bcm_console_write, + .device = uart_console_device, + .setup = bcm_console_setup, + .flags = CON_PRINTBUFFER, + .index = -1, + .data = &bcm_uart_driver, +}; + +static int __init bcm63xx_console_init(void) +{ + register_console(&bcm63xx_console); + return 0; +} + +console_initcall(bcm63xx_console_init); + +#define BCM63XX_CONSOLE (&bcm63xx_console) +#else +#define BCM63XX_CONSOLE NULL +#endif /* CONFIG_SERIAL_BCM63XX_CONSOLE */ + +static struct uart_driver bcm_uart_driver = { + .owner = THIS_MODULE, + .driver_name = "bcm63xx_uart", + .dev_name = "ttyS", + .major = TTY_MAJOR, + .minor = 64, + .nr = 1, + .cons = BCM63XX_CONSOLE, +}; + +/* + * platform driver probe/remove callback + */ +static int __devinit bcm_uart_probe(struct platform_device *pdev) +{ + struct resource *res_mem, *res_irq; + struct uart_port *port; + struct clk *clk; + int ret; + + if (pdev->id < 0 || pdev->id >= BCM63XX_NR_UARTS) + return -EINVAL; + + if (ports[pdev->id].membase) + return -EBUSY; + + res_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res_mem) + return -ENODEV; + + res_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (!res_irq) + return -ENODEV; + + clk = clk_get(&pdev->dev, "periph"); + if (IS_ERR(clk)) + return -ENODEV; + + port = &ports[pdev->id]; + memset(port, 0, sizeof(*port)); + port->iotype = UPIO_MEM; + port->mapbase = res_mem->start; + port->irq = res_irq->start; + port->ops = &bcm_uart_ops; + port->flags = UPF_BOOT_AUTOCONF; + port->dev = &pdev->dev; + port->fifosize = 16; + port->uartclk = clk_get_rate(clk) / 2; + clk_put(clk); + + ret = uart_add_one_port(&bcm_uart_driver, port); + if (ret) { + kfree(port); + return ret; + } + platform_set_drvdata(pdev, port); + return 0; +} + +static int __devexit bcm_uart_remove(struct platform_device *pdev) +{ + struct uart_port *port; + + port = platform_get_drvdata(pdev); + uart_remove_one_port(&bcm_uart_driver, port); + platform_set_drvdata(pdev, NULL); + /* mark port as free */ + ports[pdev->id].membase = 0; + return 0; +} + +/* + * platform driver stuff + */ +static struct platform_driver bcm_uart_platform_driver = { + .probe = bcm_uart_probe, + .remove = __devexit_p(bcm_uart_remove), + .driver = { + .owner = THIS_MODULE, + .name = "bcm63xx_uart", + }, +}; + +static int __init bcm_uart_init(void) +{ + int ret; + + ret = uart_register_driver(&bcm_uart_driver); + if (ret) + return ret; + + ret = platform_driver_register(&bcm_uart_platform_driver); + if (ret) + uart_unregister_driver(&bcm_uart_driver); + + return ret; +} + +static void __exit bcm_uart_exit(void) +{ + platform_driver_unregister(&bcm_uart_platform_driver); + uart_unregister_driver(&bcm_uart_driver); +} + +module_init(bcm_uart_init); +module_exit(bcm_uart_exit); + +MODULE_AUTHOR("Maxime Bizon <mbizon@freebox.fr>"); +MODULE_DESCRIPTION("Broadcom 63<xx integrated uart driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/serial/cpm_uart/cpm_uart_core.c b/drivers/serial/cpm_uart/cpm_uart_core.c index 8d349b23249a..300cea768d74 100644 --- a/drivers/serial/cpm_uart/cpm_uart_core.c +++ b/drivers/serial/cpm_uart/cpm_uart_core.c @@ -649,7 +649,7 @@ static int cpm_uart_tx_pump(struct uart_port *port) u8 *p; int count; struct uart_cpm_port *pinfo = (struct uart_cpm_port *)port; - struct circ_buf *xmit = &port->info->xmit; + struct circ_buf *xmit = &port->state->xmit; /* Handle xon/xoff */ if (port->x_char) { diff --git a/drivers/serial/crisv10.c b/drivers/serial/crisv10.c index 7be52fe288eb..31f172397af3 100644 --- a/drivers/serial/crisv10.c +++ b/drivers/serial/crisv10.c @@ -18,6 +18,7 @@ static char *serial_version = "$Revision: 1.25 $"; #include <linux/tty.h> #include <linux/tty_flip.h> #include <linux/major.h> +#include <linux/smp_lock.h> #include <linux/string.h> #include <linux/fcntl.h> #include <linux/mm.h> diff --git a/drivers/serial/icom.c b/drivers/serial/icom.c index 2d7feecaf492..0028b6f89ce6 100644 --- a/drivers/serial/icom.c +++ b/drivers/serial/icom.c @@ -307,7 +307,7 @@ static void stop_processor(struct icom_port *icom_port) if (port < 4) { temp = readl(stop_proc[port].global_control_reg); temp = - (temp & ~start_proc[port].processor_id) | stop_proc[port].processor_id; + (temp & ~start_proc[port].processor_id) | stop_proc[port].processor_id; writel(temp, stop_proc[port].global_control_reg); /* write flush */ @@ -336,7 +336,7 @@ static void start_processor(struct icom_port *icom_port) if (port < 4) { temp = readl(start_proc[port].global_control_reg); temp = - (temp & ~stop_proc[port].processor_id) | start_proc[port].processor_id; + (temp & ~stop_proc[port].processor_id) | start_proc[port].processor_id; writel(temp, start_proc[port].global_control_reg); /* write flush */ @@ -509,8 +509,8 @@ static void load_code(struct icom_port *icom_port) dev_err(&icom_port->adapter->pci_dev->dev,"Port not opertional\n"); } - if (new_page != NULL) - pci_free_consistent(dev, 4096, new_page, temp_pci); + if (new_page != NULL) + pci_free_consistent(dev, 4096, new_page, temp_pci); } static int startup(struct icom_port *icom_port) @@ -1493,15 +1493,15 @@ static int __devinit icom_probe(struct pci_dev *dev, const struct pci_device_id *ent) { int index; - unsigned int command_reg; - int retval; - struct icom_adapter *icom_adapter; - struct icom_port *icom_port; + unsigned int command_reg; + int retval; + struct icom_adapter *icom_adapter; + struct icom_port *icom_port; - retval = pci_enable_device(dev); - if (retval) { + retval = pci_enable_device(dev); + if (retval) { dev_err(&dev->dev, "Device enable FAILED\n"); - return retval; + return retval; } if ( (retval = pci_request_regions(dev, "icom"))) { @@ -1510,23 +1510,23 @@ static int __devinit icom_probe(struct pci_dev *dev, return retval; } - pci_set_master(dev); + pci_set_master(dev); - if ( (retval = pci_read_config_dword(dev, PCI_COMMAND, &command_reg))) { + if ( (retval = pci_read_config_dword(dev, PCI_COMMAND, &command_reg))) { dev_err(&dev->dev, "PCI Config read FAILED\n"); - return retval; - } + return retval; + } pci_write_config_dword(dev, PCI_COMMAND, command_reg | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER | PCI_COMMAND_PARITY | PCI_COMMAND_SERR); - if (ent->driver_data == ADAPTER_V1) { + if (ent->driver_data == ADAPTER_V1) { pci_write_config_dword(dev, 0x44, 0x8300830A); - } else { + } else { pci_write_config_dword(dev, 0x44, 0x42004200); pci_write_config_dword(dev, 0x48, 0x42004200); - } + } retval = icom_alloc_adapter(&icom_adapter); @@ -1536,10 +1536,10 @@ static int __devinit icom_probe(struct pci_dev *dev, goto probe_exit0; } - icom_adapter->base_addr_pci = pci_resource_start(dev, 0); - icom_adapter->pci_dev = dev; - icom_adapter->version = ent->driver_data; - icom_adapter->subsystem_id = ent->subdevice; + icom_adapter->base_addr_pci = pci_resource_start(dev, 0); + icom_adapter->pci_dev = dev; + icom_adapter->version = ent->driver_data; + icom_adapter->subsystem_id = ent->subdevice; retval = icom_init_ports(icom_adapter); @@ -1548,7 +1548,7 @@ static int __devinit icom_probe(struct pci_dev *dev, goto probe_exit1; } - icom_adapter->base_addr = pci_ioremap_bar(dev, 0); + icom_adapter->base_addr = pci_ioremap_bar(dev, 0); if (!icom_adapter->base_addr) goto probe_exit1; @@ -1562,7 +1562,7 @@ static int __devinit icom_probe(struct pci_dev *dev, retval = icom_load_ports(icom_adapter); - for (index = 0; index < icom_adapter->numb_ports; index++) { + for (index = 0; index < icom_adapter->numb_ports; index++) { icom_port = &icom_adapter->port_info[index]; if (icom_port->status == ICOM_PORT_ACTIVE) { @@ -1579,7 +1579,7 @@ static int __devinit icom_probe(struct pci_dev *dev, icom_port->status = ICOM_PORT_OFF; dev_err(&dev->dev, "Device add failed\n"); } else - dev_info(&dev->dev, "Device added\n"); + dev_info(&dev->dev, "Device added\n"); } } @@ -1595,9 +1595,7 @@ probe_exit0: pci_release_regions(dev); pci_disable_device(dev); - return retval; - - + return retval; } static void __devexit icom_remove(struct pci_dev *dev) diff --git a/drivers/serial/max3100.c b/drivers/serial/max3100.c index 75ab00631c41..3c30c56aa2e1 100644 --- a/drivers/serial/max3100.c +++ b/drivers/serial/max3100.c @@ -925,3 +925,4 @@ module_exit(max3100_exit); MODULE_DESCRIPTION("MAX3100 driver"); MODULE_AUTHOR("Christian Pellegrin <chripell@evolware.org>"); MODULE_LICENSE("GPL"); +MODULE_ALIAS("spi:max3100"); diff --git a/drivers/serial/mpc52xx_uart.c b/drivers/serial/mpc52xx_uart.c index d7bcd074d383..7ce9e9f567a3 100644 --- a/drivers/serial/mpc52xx_uart.c +++ b/drivers/serial/mpc52xx_uart.c @@ -705,7 +705,7 @@ mpc52xx_uart_verify_port(struct uart_port *port, struct serial_struct *ser) return -EINVAL; if ((ser->irq != port->irq) || - (ser->io_type != SERIAL_IO_MEM) || + (ser->io_type != UPIO_MEM) || (ser->baud_base != port->uartclk) || (ser->iomem_base != (void *)port->mapbase) || (ser->hub6 != 0)) diff --git a/drivers/serial/pxa.c b/drivers/serial/pxa.c index 6443b7ff274a..b8629d74f6a2 100644 --- a/drivers/serial/pxa.c +++ b/drivers/serial/pxa.c @@ -726,9 +726,10 @@ static struct uart_driver serial_pxa_reg = { .cons = PXA_CONSOLE, }; -static int serial_pxa_suspend(struct platform_device *dev, pm_message_t state) +#ifdef CONFIG_PM +static int serial_pxa_suspend(struct device *dev) { - struct uart_pxa_port *sport = platform_get_drvdata(dev); + struct uart_pxa_port *sport = dev_get_drvdata(dev); if (sport) uart_suspend_port(&serial_pxa_reg, &sport->port); @@ -736,9 +737,9 @@ static int serial_pxa_suspend(struct platform_device *dev, pm_message_t state) return 0; } -static int serial_pxa_resume(struct platform_device *dev) +static int serial_pxa_resume(struct device *dev) { - struct uart_pxa_port *sport = platform_get_drvdata(dev); + struct uart_pxa_port *sport = dev_get_drvdata(dev); if (sport) uart_resume_port(&serial_pxa_reg, &sport->port); @@ -746,6 +747,12 @@ static int serial_pxa_resume(struct platform_device *dev) return 0; } +static struct dev_pm_ops serial_pxa_pm_ops = { + .suspend = serial_pxa_suspend, + .resume = serial_pxa_resume, +}; +#endif + static int serial_pxa_probe(struct platform_device *dev) { struct uart_pxa_port *sport; @@ -825,11 +832,12 @@ static struct platform_driver serial_pxa_driver = { .probe = serial_pxa_probe, .remove = serial_pxa_remove, - .suspend = serial_pxa_suspend, - .resume = serial_pxa_resume, .driver = { .name = "pxa2xx-uart", .owner = THIS_MODULE, +#ifdef CONFIG_PM + .pm = &serial_pxa_pm_ops, +#endif }, }; diff --git a/drivers/serial/sa1100.c b/drivers/serial/sa1100.c index 7f5e26873220..2199d819a987 100644 --- a/drivers/serial/sa1100.c +++ b/drivers/serial/sa1100.c @@ -638,7 +638,7 @@ static void __init sa1100_init_ports(void) PPSR |= PPC_TXD1 | PPC_TXD3; } -void __init sa1100_register_uart_fns(struct sa1100_port_fns *fns) +void __devinit sa1100_register_uart_fns(struct sa1100_port_fns *fns) { if (fns->get_mctrl) sa1100_pops.get_mctrl = fns->get_mctrl; diff --git a/drivers/serial/serial_core.c b/drivers/serial/serial_core.c index 2514d00c0f6f..dcc72444e8e7 100644 --- a/drivers/serial/serial_core.c +++ b/drivers/serial/serial_core.c @@ -1270,6 +1270,9 @@ static void uart_close(struct tty_struct *tty, struct file *filp) BUG_ON(!kernel_locked()); + if (!state) + return; + uport = state->uart_port; port = &state->port; @@ -1316,9 +1319,9 @@ static void uart_close(struct tty_struct *tty, struct file *filp) */ if (port->flags & ASYNC_INITIALIZED) { unsigned long flags; - spin_lock_irqsave(&port->lock, flags); + spin_lock_irqsave(&uport->lock, flags); uport->ops->stop_rx(uport); - spin_unlock_irqrestore(&port->lock, flags); + spin_unlock_irqrestore(&uport->lock, flags); /* * Before we drop DTR, make sure the UART transmitter * has completely drained; this is especially @@ -2426,7 +2429,7 @@ struct tty_driver *uart_console_device(struct console *co, int *index) /** * uart_add_one_port - attach a driver-defined port structure * @drv: pointer to the uart low level driver structure for this port - * @port: uart port structure to use for this port. + * @uport: uart port structure to use for this port. * * This allows the driver to register its own uart_port structure * with the core driver. The main purpose is to allow the low @@ -2499,7 +2502,7 @@ int uart_add_one_port(struct uart_driver *drv, struct uart_port *uport) /** * uart_remove_one_port - detach a driver defined port structure * @drv: pointer to the uart low level driver structure for this port - * @port: uart port structure for this port + * @uport: uart port structure for this port * * This unhooks (and hangs up) the specified port structure from the * core driver. No further calls will be made to the low-level code diff --git a/drivers/serial/serial_cs.c b/drivers/serial/serial_cs.c index a3bb49031a7f..7c7914f5fa02 100644 --- a/drivers/serial/serial_cs.c +++ b/drivers/serial/serial_cs.c @@ -873,20 +873,20 @@ static struct pcmcia_device_id serial_ids[] = { PCMCIA_PFC_DEVICE_CIS_PROD_ID12(1, "Psion Dacom", "Gold Card V34 Ethernet", 0xf5f025c2, 0x338e8155, "cis/PCMLM28.cis"), PCMCIA_PFC_DEVICE_CIS_PROD_ID12(1, "Psion Dacom", "Gold Card V34 Ethernet GSM", 0xf5f025c2, 0x4ae85d35, "cis/PCMLM28.cis"), PCMCIA_PFC_DEVICE_CIS_PROD_ID12(1, "LINKSYS", "PCMLM28", 0xf7cb0b07, 0x66881874, "cis/PCMLM28.cis"), - PCMCIA_MFC_DEVICE_CIS_PROD_ID12(1, "DAYNA COMMUNICATIONS", "LAN AND MODEM MULTIFUNCTION", 0x8fdf8f89, 0xdd5ed9e8, "DP83903.cis"), - PCMCIA_MFC_DEVICE_CIS_PROD_ID4(1, "NSC MF LAN/Modem", 0x58fc6056, "DP83903.cis"), + PCMCIA_MFC_DEVICE_CIS_PROD_ID12(1, "DAYNA COMMUNICATIONS", "LAN AND MODEM MULTIFUNCTION", 0x8fdf8f89, 0xdd5ed9e8, "cis/DP83903.cis"), + PCMCIA_MFC_DEVICE_CIS_PROD_ID4(1, "NSC MF LAN/Modem", 0x58fc6056, "cis/DP83903.cis"), PCMCIA_MFC_DEVICE_CIS_MANF_CARD(1, 0x0101, 0x0556, "cis/3CCFEM556.cis"), - PCMCIA_MFC_DEVICE_CIS_MANF_CARD(1, 0x0175, 0x0000, "DP83903.cis"), + PCMCIA_MFC_DEVICE_CIS_MANF_CARD(1, 0x0175, 0x0000, "cis/DP83903.cis"), PCMCIA_MFC_DEVICE_CIS_MANF_CARD(1, 0x0101, 0x0035, "cis/3CXEM556.cis"), PCMCIA_MFC_DEVICE_CIS_MANF_CARD(1, 0x0101, 0x003d, "cis/3CXEM556.cis"), - PCMCIA_DEVICE_CIS_PROD_ID12("Sierra Wireless", "AC850", 0xd85f6206, 0x42a2c018, "SW_8xx_SER.cis"), /* Sierra Wireless AC850 3G Network Adapter R1 */ - PCMCIA_DEVICE_CIS_MANF_CARD(0x0192, 0x0710, "SW_7xx_SER.cis"), /* Sierra Wireless AC710/AC750 GPRS Network Adapter R1 */ - PCMCIA_DEVICE_CIS_MANF_CARD(0x0192, 0xa555, "SW_555_SER.cis"), /* Sierra Aircard 555 CDMA 1xrtt Modem -- pre update */ - PCMCIA_DEVICE_CIS_MANF_CARD(0x013f, 0xa555, "SW_555_SER.cis"), /* Sierra Aircard 555 CDMA 1xrtt Modem -- post update */ + PCMCIA_DEVICE_CIS_PROD_ID12("Sierra Wireless", "AC850", 0xd85f6206, 0x42a2c018, "cis/SW_8xx_SER.cis"), /* Sierra Wireless AC850 3G Network Adapter R1 */ + PCMCIA_DEVICE_CIS_PROD_ID12("Sierra Wireless", "AC710/AC750", 0xd85f6206, 0x761b11e0, "cis/SW_7xx_SER.cis"), /* Sierra Wireless AC710/AC750 GPRS Network Adapter R1 */ + PCMCIA_DEVICE_CIS_MANF_CARD(0x0192, 0xa555, "cis/SW_555_SER.cis"), /* Sierra Aircard 555 CDMA 1xrtt Modem -- pre update */ + PCMCIA_DEVICE_CIS_MANF_CARD(0x013f, 0xa555, "cis/SW_555_SER.cis"), /* Sierra Aircard 555 CDMA 1xrtt Modem -- post update */ PCMCIA_DEVICE_CIS_PROD_ID12("MultiTech", "PCMCIA 56K DataFax", 0x842047ee, 0xc2efcf03, "cis/MT5634ZLX.cis"), - PCMCIA_DEVICE_CIS_PROD_ID12("ADVANTECH", "COMpad-32/85B-2", 0x96913a85, 0x27ab5437, "COMpad2.cis"), - PCMCIA_DEVICE_CIS_PROD_ID12("ADVANTECH", "COMpad-32/85B-4", 0x96913a85, 0xcec8f102, "COMpad4.cis"), - PCMCIA_DEVICE_CIS_PROD_ID123("ADVANTECH", "COMpad-32/85", "1.0", 0x96913a85, 0x8fbe92ae, 0x0877b627, "COMpad2.cis"), + PCMCIA_DEVICE_CIS_PROD_ID12("ADVANTECH", "COMpad-32/85B-2", 0x96913a85, 0x27ab5437, "cis/COMpad2.cis"), + PCMCIA_DEVICE_CIS_PROD_ID12("ADVANTECH", "COMpad-32/85B-4", 0x96913a85, 0xcec8f102, "cis/COMpad4.cis"), + PCMCIA_DEVICE_CIS_PROD_ID123("ADVANTECH", "COMpad-32/85", "1.0", 0x96913a85, 0x8fbe92ae, 0x0877b627, "cis/COMpad2.cis"), PCMCIA_DEVICE_CIS_PROD_ID2("RS-COM 2P", 0xad20b156, "cis/RS-COM-2P.cis"), PCMCIA_DEVICE_CIS_MANF_CARD(0x0013, 0x0000, "GLOBETROTTER.cis"), PCMCIA_DEVICE_PROD_ID12("ELAN DIGITAL SYSTEMS LTD, c1997.","SERIAL CARD: SL100 1.00.",0x19ca78af,0xf964f42b), diff --git a/drivers/serial/serial_txx9.c b/drivers/serial/serial_txx9.c index 0f7cf4c453e6..c50e9fbbf743 100644 --- a/drivers/serial/serial_txx9.c +++ b/drivers/serial/serial_txx9.c @@ -221,21 +221,26 @@ sio_quot_set(struct uart_txx9_port *up, int quot) sio_out(up, TXX9_SIBGR, 0xff | TXX9_SIBGR_BCLK_T6); } +static struct uart_txx9_port *to_uart_txx9_port(struct uart_port *port) +{ + return container_of(port, struct uart_txx9_port, port); +} + static void serial_txx9_stop_tx(struct uart_port *port) { - struct uart_txx9_port *up = (struct uart_txx9_port *)port; + struct uart_txx9_port *up = to_uart_txx9_port(port); sio_mask(up, TXX9_SIDICR, TXX9_SIDICR_TIE); } static void serial_txx9_start_tx(struct uart_port *port) { - struct uart_txx9_port *up = (struct uart_txx9_port *)port; + struct uart_txx9_port *up = to_uart_txx9_port(port); sio_set(up, TXX9_SIDICR, TXX9_SIDICR_TIE); } static void serial_txx9_stop_rx(struct uart_port *port) { - struct uart_txx9_port *up = (struct uart_txx9_port *)port; + struct uart_txx9_port *up = to_uart_txx9_port(port); up->port.read_status_mask &= ~TXX9_SIDISR_RDIS; } @@ -246,7 +251,7 @@ static void serial_txx9_enable_ms(struct uart_port *port) static void serial_txx9_initialize(struct uart_port *port) { - struct uart_txx9_port *up = (struct uart_txx9_port *)port; + struct uart_txx9_port *up = to_uart_txx9_port(port); unsigned int tmout = 10000; sio_out(up, TXX9_SIFCR, TXX9_SIFCR_SWRST); @@ -414,7 +419,7 @@ static irqreturn_t serial_txx9_interrupt(int irq, void *dev_id) static unsigned int serial_txx9_tx_empty(struct uart_port *port) { - struct uart_txx9_port *up = (struct uart_txx9_port *)port; + struct uart_txx9_port *up = to_uart_txx9_port(port); unsigned long flags; unsigned int ret; @@ -427,7 +432,7 @@ static unsigned int serial_txx9_tx_empty(struct uart_port *port) static unsigned int serial_txx9_get_mctrl(struct uart_port *port) { - struct uart_txx9_port *up = (struct uart_txx9_port *)port; + struct uart_txx9_port *up = to_uart_txx9_port(port); unsigned int ret; /* no modem control lines */ @@ -440,7 +445,7 @@ static unsigned int serial_txx9_get_mctrl(struct uart_port *port) static void serial_txx9_set_mctrl(struct uart_port *port, unsigned int mctrl) { - struct uart_txx9_port *up = (struct uart_txx9_port *)port; + struct uart_txx9_port *up = to_uart_txx9_port(port); if (mctrl & TIOCM_RTS) sio_mask(up, TXX9_SIFLCR, TXX9_SIFLCR_RTSSC); @@ -450,7 +455,7 @@ static void serial_txx9_set_mctrl(struct uart_port *port, unsigned int mctrl) static void serial_txx9_break_ctl(struct uart_port *port, int break_state) { - struct uart_txx9_port *up = (struct uart_txx9_port *)port; + struct uart_txx9_port *up = to_uart_txx9_port(port); unsigned long flags; spin_lock_irqsave(&up->port.lock, flags); @@ -494,7 +499,7 @@ static int serial_txx9_get_poll_char(struct uart_port *port) { unsigned int ier; unsigned char c; - struct uart_txx9_port *up = (struct uart_txx9_port *)port; + struct uart_txx9_port *up = to_uart_txx9_port(port); /* * First save the IER then disable the interrupts @@ -520,7 +525,7 @@ static int serial_txx9_get_poll_char(struct uart_port *port) static void serial_txx9_put_poll_char(struct uart_port *port, unsigned char c) { unsigned int ier; - struct uart_txx9_port *up = (struct uart_txx9_port *)port; + struct uart_txx9_port *up = to_uart_txx9_port(port); /* * First save the IER then disable the interrupts @@ -551,7 +556,7 @@ static void serial_txx9_put_poll_char(struct uart_port *port, unsigned char c) static int serial_txx9_startup(struct uart_port *port) { - struct uart_txx9_port *up = (struct uart_txx9_port *)port; + struct uart_txx9_port *up = to_uart_txx9_port(port); unsigned long flags; int retval; @@ -596,7 +601,7 @@ static int serial_txx9_startup(struct uart_port *port) static void serial_txx9_shutdown(struct uart_port *port) { - struct uart_txx9_port *up = (struct uart_txx9_port *)port; + struct uart_txx9_port *up = to_uart_txx9_port(port); unsigned long flags; /* @@ -636,7 +641,7 @@ static void serial_txx9_set_termios(struct uart_port *port, struct ktermios *termios, struct ktermios *old) { - struct uart_txx9_port *up = (struct uart_txx9_port *)port; + struct uart_txx9_port *up = to_uart_txx9_port(port); unsigned int cval, fcr = 0; unsigned long flags; unsigned int baud, quot; @@ -814,19 +819,19 @@ static void serial_txx9_release_resource(struct uart_txx9_port *up) static void serial_txx9_release_port(struct uart_port *port) { - struct uart_txx9_port *up = (struct uart_txx9_port *)port; + struct uart_txx9_port *up = to_uart_txx9_port(port); serial_txx9_release_resource(up); } static int serial_txx9_request_port(struct uart_port *port) { - struct uart_txx9_port *up = (struct uart_txx9_port *)port; + struct uart_txx9_port *up = to_uart_txx9_port(port); return serial_txx9_request_resource(up); } static void serial_txx9_config_port(struct uart_port *port, int uflags) { - struct uart_txx9_port *up = (struct uart_txx9_port *)port; + struct uart_txx9_port *up = to_uart_txx9_port(port); int ret; /* @@ -897,7 +902,7 @@ static void __init serial_txx9_register_ports(struct uart_driver *drv, static void serial_txx9_console_putchar(struct uart_port *port, int ch) { - struct uart_txx9_port *up = (struct uart_txx9_port *)port; + struct uart_txx9_port *up = to_uart_txx9_port(port); wait_for_xmitr(up); sio_out(up, TXX9_SITFIFO, ch); diff --git a/drivers/serial/sh-sci.c b/drivers/serial/sh-sci.c index 85119fb7cb50..6498bd1fb6dd 100644 --- a/drivers/serial/sh-sci.c +++ b/drivers/serial/sh-sci.c @@ -1143,7 +1143,7 @@ static void serial_console_write(struct console *co, const char *s, while ((sci_in(port, SCxSR) & bits) != bits) cpu_relax(); - if (sci_port->disable); + if (sci_port->disable) sci_port->disable(port); } diff --git a/drivers/serial/suncore.c b/drivers/serial/suncore.c index a2d4a19550ab..ed7d958b0a01 100644 --- a/drivers/serial/suncore.c +++ b/drivers/serial/suncore.c @@ -53,20 +53,21 @@ void sunserial_unregister_minors(struct uart_driver *drv, int count) EXPORT_SYMBOL(sunserial_unregister_minors); int sunserial_console_match(struct console *con, struct device_node *dp, - struct uart_driver *drv, int line) + struct uart_driver *drv, int line, bool ignore_line) { - int off; - if (!con || of_console_device != dp) return 0; - off = 0; - if (of_console_options && - *of_console_options == 'b') - off = 1; + if (!ignore_line) { + int off = 0; - if ((line & 1) != off) - return 0; + if (of_console_options && + *of_console_options == 'b') + off = 1; + + if ((line & 1) != off) + return 0; + } con->index = line; drv->cons = con; @@ -76,23 +77,24 @@ int sunserial_console_match(struct console *con, struct device_node *dp, } EXPORT_SYMBOL(sunserial_console_match); -void -sunserial_console_termios(struct console *con) +void sunserial_console_termios(struct console *con, struct device_node *uart_dp) { - struct device_node *dp; - const char *od, *mode, *s; + const char *mode, *s; char mode_prop[] = "ttyX-mode"; int baud, bits, stop, cflag; char parity; - dp = of_find_node_by_path("/options"); - od = of_get_property(dp, "output-device", NULL); - if (!strcmp(od, "rsc")) { - mode = of_get_property(of_console_device, + if (!strcmp(uart_dp->name, "rsc") || + !strcmp(uart_dp->name, "rsc-console") || + !strcmp(uart_dp->name, "rsc-control")) { + mode = of_get_property(uart_dp, "ssp-console-modes", NULL); if (!mode) mode = "115200,8,n,1,-"; + } else if (!strcmp(uart_dp->name, "lom-console")) { + mode = "9600,8,n,1,-"; } else { + struct device_node *dp; char c; c = 'a'; @@ -101,6 +103,7 @@ sunserial_console_termios(struct console *con) mode_prop[3] = c; + dp = of_find_node_by_path("/options"); mode = of_get_property(dp, mode_prop, NULL); if (!mode) mode = "9600,8,n,1,-"; diff --git a/drivers/serial/suncore.h b/drivers/serial/suncore.h index 042668aa602e..db2057936c31 100644 --- a/drivers/serial/suncore.h +++ b/drivers/serial/suncore.h @@ -26,7 +26,8 @@ extern int sunserial_register_minors(struct uart_driver *, int); extern void sunserial_unregister_minors(struct uart_driver *, int); extern int sunserial_console_match(struct console *, struct device_node *, - struct uart_driver *, int); -extern void sunserial_console_termios(struct console *); + struct uart_driver *, int, bool); +extern void sunserial_console_termios(struct console *, + struct device_node *); #endif /* !(_SERIAL_SUN_H) */ diff --git a/drivers/serial/sunhv.c b/drivers/serial/sunhv.c index d548652dee50..d14cca7fb88d 100644 --- a/drivers/serial/sunhv.c +++ b/drivers/serial/sunhv.c @@ -566,7 +566,7 @@ static int __devinit hv_probe(struct of_device *op, const struct of_device_id *m goto out_free_con_read_page; sunserial_console_match(&sunhv_console, op->node, - &sunhv_reg, port->line); + &sunhv_reg, port->line, false); err = uart_add_one_port(&sunhv_reg, port); if (err) diff --git a/drivers/serial/sunsab.c b/drivers/serial/sunsab.c index d1ad34128635..d514e28d0755 100644 --- a/drivers/serial/sunsab.c +++ b/drivers/serial/sunsab.c @@ -883,7 +883,7 @@ static int sunsab_console_setup(struct console *con, char *options) printk("Console: ttyS%d (SAB82532)\n", (sunsab_reg.minor - 64) + con->index); - sunserial_console_termios(con); + sunserial_console_termios(con, to_of_device(up->port.dev)->node); switch (con->cflag & CBAUD) { case B150: baud = 150; break; @@ -1027,10 +1027,12 @@ static int __devinit sab_probe(struct of_device *op, const struct of_device_id * goto out1; sunserial_console_match(SUNSAB_CONSOLE(), op->node, - &sunsab_reg, up[0].port.line); + &sunsab_reg, up[0].port.line, + false); sunserial_console_match(SUNSAB_CONSOLE(), op->node, - &sunsab_reg, up[1].port.line); + &sunsab_reg, up[1].port.line, + false); err = uart_add_one_port(&sunsab_reg, &up[0].port); if (err) @@ -1116,7 +1118,6 @@ static int __init sunsab_init(void) if (!sunsab_ports) return -ENOMEM; - sunsab_reg.cons = SUNSAB_CONSOLE(); err = sunserial_register_minors(&sunsab_reg, num_channels); if (err) { kfree(sunsab_ports); diff --git a/drivers/serial/sunsu.c b/drivers/serial/sunsu.c index 68d262b15749..170d3d68c8f0 100644 --- a/drivers/serial/sunsu.c +++ b/drivers/serial/sunsu.c @@ -1329,11 +1329,9 @@ static void sunsu_console_write(struct console *co, const char *s, */ static int __init sunsu_console_setup(struct console *co, char *options) { + static struct ktermios dummy; + struct ktermios termios; struct uart_port *port; - int baud = 9600; - int bits = 8; - int parity = 'n'; - int flow = 'n'; printk("Console: ttyS%d (SU)\n", (sunsu_reg.minor - 64) + co->index); @@ -1352,10 +1350,15 @@ static int __init sunsu_console_setup(struct console *co, char *options) */ spin_lock_init(&port->lock); - if (options) - uart_parse_options(options, &baud, &parity, &bits, &flow); + /* Get firmware console settings. */ + sunserial_console_termios(co, to_of_device(port->dev)->node); - return uart_set_options(port, co, baud, parity, bits, flow); + memset(&termios, 0, sizeof(struct ktermios)); + termios.c_cflag = co->cflag; + port->mctrl |= TIOCM_DTR; + port->ops->set_termios(port, &termios, &dummy); + + return 0; } static struct console sunsu_console = { @@ -1409,6 +1412,7 @@ static int __devinit su_probe(struct of_device *op, const struct of_device_id *m struct uart_sunsu_port *up; struct resource *rp; enum su_type type; + bool ignore_line; int err; type = su_get_type(dp); @@ -1467,8 +1471,14 @@ static int __devinit su_probe(struct of_device *op, const struct of_device_id *m up->port.ops = &sunsu_pops; + ignore_line = false; + if (!strcmp(dp->name, "rsc-console") || + !strcmp(dp->name, "lom-console")) + ignore_line = true; + sunserial_console_match(SUNSU_CONSOLE(), dp, - &sunsu_reg, up->port.line); + &sunsu_reg, up->port.line, + ignore_line); err = uart_add_one_port(&sunsu_reg, &up->port); if (err) goto out_unmap; @@ -1517,6 +1527,10 @@ static const struct of_device_id su_match[] = { .name = "serial", .compatible = "su", }, + { + .type = "serial", + .compatible = "su", + }, {}, }; MODULE_DEVICE_TABLE(of, su_match); @@ -1548,6 +1562,12 @@ static int __init sunsu_init(void) num_uart++; } } + for_each_node_by_type(dp, "serial") { + if (of_device_is_compatible(dp, "su")) { + if (su_get_type(dp) == SU_PORT_PORT) + num_uart++; + } + } if (num_uart) { err = sunserial_register_minors(&sunsu_reg, num_uart); diff --git a/drivers/serial/sunzilog.c b/drivers/serial/sunzilog.c index ef693ae22e7f..2c7a66af4f52 100644 --- a/drivers/serial/sunzilog.c +++ b/drivers/serial/sunzilog.c @@ -1180,7 +1180,7 @@ static int __init sunzilog_console_setup(struct console *con, char *options) (sunzilog_reg.minor - 64) + con->index, con->index); /* Get firmware console settings. */ - sunserial_console_termios(con); + sunserial_console_termios(con, to_of_device(up->port.dev)->node); /* Firmware console speed is limited to 150-->38400 baud so * this hackish cflag thing is OK. @@ -1416,7 +1416,8 @@ static int __devinit zs_probe(struct of_device *op, const struct of_device_id *m if (!keyboard_mouse) { if (sunserial_console_match(SUNZILOG_CONSOLE(), op->node, - &sunzilog_reg, up[0].port.line)) + &sunzilog_reg, up[0].port.line, + false)) up->flags |= SUNZILOG_FLAG_IS_CONS; err = uart_add_one_port(&sunzilog_reg, &up[0].port); if (err) { @@ -1425,7 +1426,8 @@ static int __devinit zs_probe(struct of_device *op, const struct of_device_id *m return err; } if (sunserial_console_match(SUNZILOG_CONSOLE(), op->node, - &sunzilog_reg, up[1].port.line)) + &sunzilog_reg, up[1].port.line, + false)) up->flags |= SUNZILOG_FLAG_IS_CONS; err = uart_add_one_port(&sunzilog_reg, &up[1].port); if (err) { diff --git a/drivers/sfi/Kconfig b/drivers/sfi/Kconfig new file mode 100644 index 000000000000..dd115121e0b6 --- /dev/null +++ b/drivers/sfi/Kconfig @@ -0,0 +1,17 @@ +# +# SFI Configuration +# + +menuconfig SFI + bool "SFI (Simple Firmware Interface) Support" + ---help--- + The Simple Firmware Interface (SFI) provides a lightweight method + for platform firmware to pass information to the operating system + via static tables in memory. Kernel SFI support is required to + boot on SFI-only platforms. Currently, all SFI-only platforms are + based on the 2nd generation Intel Atom processor platform, + code-named Moorestown. + + For more information, see http://simplefirmware.org + + Say 'Y' here to enable the kernel to boot on SFI-only platforms. diff --git a/drivers/sfi/Makefile b/drivers/sfi/Makefile new file mode 100644 index 000000000000..2343732aefeb --- /dev/null +++ b/drivers/sfi/Makefile @@ -0,0 +1,3 @@ +obj-y += sfi_acpi.o +obj-y += sfi_core.o + diff --git a/drivers/sfi/sfi_acpi.c b/drivers/sfi/sfi_acpi.c new file mode 100644 index 000000000000..34aba30eb84b --- /dev/null +++ b/drivers/sfi/sfi_acpi.c @@ -0,0 +1,175 @@ +/* sfi_acpi.c Simple Firmware Interface - ACPI extensions */ + +/* + + This file is provided under a dual BSD/GPLv2 license. When using or + redistributing this file, you may do so under either license. + + GPL LICENSE SUMMARY + + Copyright(c) 2009 Intel Corporation. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of version 2 of the GNU General Public License as + published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + The full GNU General Public License is included in this distribution + in the file called LICENSE.GPL. + + BSD LICENSE + + Copyright(c) 2009 Intel Corporation. All rights reserved. + + 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. + * Neither the name of Intel Corporation nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +*/ + +#define KMSG_COMPONENT "SFI" +#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt + +#include <linux/kernel.h> +#include <acpi/acpi.h> + +#include <linux/sfi.h> +#include "sfi_core.h" + +/* + * SFI can access ACPI-defined tables via an optional ACPI XSDT. + * + * This allows re-use, and avoids re-definition, of standard tables. + * For example, the "MCFG" table is defined by PCI, reserved by ACPI, + * and is expected to be present many SFI-only systems. + */ + +static struct acpi_table_xsdt *xsdt_va __read_mostly; + +#define XSDT_GET_NUM_ENTRIES(ptable, entry_type) \ + ((ptable->header.length - sizeof(struct acpi_table_header)) / \ + (sizeof(entry_type))) + +static inline struct sfi_table_header *acpi_to_sfi_th( + struct acpi_table_header *th) +{ + return (struct sfi_table_header *)th; +} + +static inline struct acpi_table_header *sfi_to_acpi_th( + struct sfi_table_header *th) +{ + return (struct acpi_table_header *)th; +} + +/* + * sfi_acpi_parse_xsdt() + * + * Parse the ACPI XSDT for later access by sfi_acpi_table_parse(). + */ +static int __init sfi_acpi_parse_xsdt(struct sfi_table_header *th) +{ + struct sfi_table_key key = SFI_ANY_KEY; + int tbl_cnt, i; + void *ret; + + xsdt_va = (struct acpi_table_xsdt *)th; + tbl_cnt = XSDT_GET_NUM_ENTRIES(xsdt_va, u64); + for (i = 0; i < tbl_cnt; i++) { + ret = sfi_check_table(xsdt_va->table_offset_entry[i], &key); + if (IS_ERR(ret)) { + disable_sfi(); + return -1; + } + } + + return 0; +} + +int __init sfi_acpi_init(void) +{ + struct sfi_table_key xsdt_key = { .sig = SFI_SIG_XSDT }; + + sfi_table_parse(SFI_SIG_XSDT, NULL, NULL, sfi_acpi_parse_xsdt); + + /* Only call the get_table to keep the table mapped */ + xsdt_va = (struct acpi_table_xsdt *)sfi_get_table(&xsdt_key); + return 0; +} + +static struct acpi_table_header *sfi_acpi_get_table(struct sfi_table_key *key) +{ + u32 tbl_cnt, i; + void *ret; + + tbl_cnt = XSDT_GET_NUM_ENTRIES(xsdt_va, u64); + for (i = 0; i < tbl_cnt; i++) { + ret = sfi_check_table(xsdt_va->table_offset_entry[i], key); + if (!IS_ERR(ret) && ret) + return sfi_to_acpi_th(ret); + } + + return NULL; +} + +static void sfi_acpi_put_table(struct acpi_table_header *table) +{ + sfi_put_table(acpi_to_sfi_th(table)); +} + +/* + * sfi_acpi_table_parse() + * + * Find specified table in XSDT, run handler on it and return its return value + */ +int sfi_acpi_table_parse(char *signature, char *oem_id, char *oem_table_id, + int(*handler)(struct acpi_table_header *)) +{ + struct acpi_table_header *table = NULL; + struct sfi_table_key key; + int ret = 0; + + if (sfi_disabled) + return -1; + + key.sig = signature; + key.oem_id = oem_id; + key.oem_table_id = oem_table_id; + + table = sfi_acpi_get_table(&key); + if (!table) + return -EINVAL; + + ret = handler(table); + sfi_acpi_put_table(table); + return ret; +} diff --git a/drivers/sfi/sfi_core.c b/drivers/sfi/sfi_core.c new file mode 100644 index 000000000000..b204a0929139 --- /dev/null +++ b/drivers/sfi/sfi_core.c @@ -0,0 +1,416 @@ +/* sfi_core.c Simple Firmware Interface - core internals */ + +/* + + This file is provided under a dual BSD/GPLv2 license. When using or + redistributing this file, you may do so under either license. + + GPL LICENSE SUMMARY + + Copyright(c) 2009 Intel Corporation. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of version 2 of the GNU General Public License as + published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + The full GNU General Public License is included in this distribution + in the file called LICENSE.GPL. + + BSD LICENSE + + Copyright(c) 2009 Intel Corporation. All rights reserved. + + 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. + * Neither the name of Intel Corporation nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +*/ + +#define KMSG_COMPONENT "SFI" +#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt + +#include <linux/bootmem.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/errno.h> +#include <linux/types.h> +#include <linux/acpi.h> +#include <linux/init.h> +#include <linux/sfi.h> + +#include "sfi_core.h" + +#define ON_SAME_PAGE(addr1, addr2) \ + (((unsigned long)(addr1) & PAGE_MASK) == \ + ((unsigned long)(addr2) & PAGE_MASK)) +#define TABLE_ON_PAGE(page, table, size) (ON_SAME_PAGE(page, table) && \ + ON_SAME_PAGE(page, table + size)) + +int sfi_disabled __read_mostly; +EXPORT_SYMBOL(sfi_disabled); + +static u64 syst_pa __read_mostly; +static struct sfi_table_simple *syst_va __read_mostly; + +/* + * FW creates and saves the SFI tables in memory. When these tables get + * used, they may need to be mapped to virtual address space, and the mapping + * can happen before or after the ioremap() is ready, so a flag is needed + * to indicating this + */ +static u32 sfi_use_ioremap __read_mostly; + +/* + * sfi_un/map_memory calls early_ioremap/iounmap which is a __init function + * and introduces section mismatch. So use __ref to make it calm. + */ +static void __iomem * __ref sfi_map_memory(u64 phys, u32 size) +{ + if (!phys || !size) + return NULL; + + if (sfi_use_ioremap) + return ioremap(phys, size); + else + return early_ioremap(phys, size); +} + +static void __ref sfi_unmap_memory(void __iomem *virt, u32 size) +{ + if (!virt || !size) + return; + + if (sfi_use_ioremap) + iounmap(virt); + else + early_iounmap(virt, size); +} + +static void sfi_print_table_header(unsigned long long pa, + struct sfi_table_header *header) +{ + pr_info("%4.4s %llX, %04X (v%d %6.6s %8.8s)\n", + header->sig, pa, + header->len, header->rev, header->oem_id, + header->oem_table_id); +} + +/* + * sfi_verify_table() + * Sanity check table lengh, calculate checksum + */ +static int sfi_verify_table(struct sfi_table_header *table) +{ + + u8 checksum = 0; + u8 *puchar = (u8 *)table; + u32 length = table->len; + + /* Sanity check table length against arbitrary 1MB limit */ + if (length > 0x100000) { + pr_err("Invalid table length 0x%x\n", length); + return -1; + } + + while (length--) + checksum += *puchar++; + + if (checksum) { + pr_err("Checksum %2.2X should be %2.2X\n", + table->csum, table->csum - checksum); + return -1; + } + return 0; +} + +/* + * sfi_map_table() + * + * Return address of mapped table + * Check for common case that we can re-use mapping to SYST, + * which requires syst_pa, syst_va to be initialized. + */ +struct sfi_table_header *sfi_map_table(u64 pa) +{ + struct sfi_table_header *th; + u32 length; + + if (!TABLE_ON_PAGE(syst_pa, pa, sizeof(struct sfi_table_header))) + th = sfi_map_memory(pa, sizeof(struct sfi_table_header)); + else + th = (void *)syst_va + (pa - syst_pa); + + /* If table fits on same page as its header, we are done */ + if (TABLE_ON_PAGE(th, th, th->len)) + return th; + + /* Entire table does not fit on same page as SYST */ + length = th->len; + if (!TABLE_ON_PAGE(syst_pa, pa, sizeof(struct sfi_table_header))) + sfi_unmap_memory(th, sizeof(struct sfi_table_header)); + + return sfi_map_memory(pa, length); +} + +/* + * sfi_unmap_table() + * + * Undoes effect of sfi_map_table() by unmapping table + * if it did not completely fit on same page as SYST. + */ +void sfi_unmap_table(struct sfi_table_header *th) +{ + if (!TABLE_ON_PAGE(syst_va, th, th->len)) + sfi_unmap_memory(th, TABLE_ON_PAGE(th, th, th->len) ? + sizeof(*th) : th->len); +} + +static int sfi_table_check_key(struct sfi_table_header *th, + struct sfi_table_key *key) +{ + + if (strncmp(th->sig, key->sig, SFI_SIGNATURE_SIZE) + || (key->oem_id && strncmp(th->oem_id, + key->oem_id, SFI_OEM_ID_SIZE)) + || (key->oem_table_id && strncmp(th->oem_table_id, + key->oem_table_id, SFI_OEM_TABLE_ID_SIZE))) + return -1; + + return 0; +} + +/* + * This function will be used in 2 cases: + * 1. used to enumerate and verify the tables addressed by SYST/XSDT, + * thus no signature will be given (in kernel boot phase) + * 2. used to parse one specific table, signature must exist, and + * the mapped virt address will be returned, and the virt space + * will be released by call sfi_put_table() later + * + * This two cases are from two different functions with two different + * sections and causes section mismatch warning. So use __ref to tell + * modpost not to make any noise. + * + * Return value: + * NULL: when can't find a table matching the key + * ERR_PTR(error): error value + * virt table address: when a matched table is found + */ +struct sfi_table_header * + __ref sfi_check_table(u64 pa, struct sfi_table_key *key) +{ + struct sfi_table_header *th; + void *ret = NULL; + + th = sfi_map_table(pa); + if (!th) + return ERR_PTR(-ENOMEM); + + if (!key->sig) { + sfi_print_table_header(pa, th); + if (sfi_verify_table(th)) + ret = ERR_PTR(-EINVAL); + } else { + if (!sfi_table_check_key(th, key)) + return th; /* Success */ + } + + sfi_unmap_table(th); + return ret; +} + +/* + * sfi_get_table() + * + * Search SYST for the specified table with the signature in + * the key, and return the mapped table + */ +struct sfi_table_header *sfi_get_table(struct sfi_table_key *key) +{ + struct sfi_table_header *th; + u32 tbl_cnt, i; + + tbl_cnt = SFI_GET_NUM_ENTRIES(syst_va, u64); + for (i = 0; i < tbl_cnt; i++) { + th = sfi_check_table(syst_va->pentry[i], key); + if (!IS_ERR(th) && th) + return th; + } + + return NULL; +} + +void sfi_put_table(struct sfi_table_header *th) +{ + sfi_unmap_table(th); +} + +/* Find table with signature, run handler on it */ +int sfi_table_parse(char *signature, char *oem_id, char *oem_table_id, + sfi_table_handler handler) +{ + struct sfi_table_header *table = NULL; + struct sfi_table_key key; + int ret = -EINVAL; + + if (sfi_disabled || !handler || !signature) + goto exit; + + key.sig = signature; + key.oem_id = oem_id; + key.oem_table_id = oem_table_id; + + table = sfi_get_table(&key); + if (!table) + goto exit; + + ret = handler(table); + sfi_put_table(table); +exit: + return ret; +} +EXPORT_SYMBOL_GPL(sfi_table_parse); + +/* + * sfi_parse_syst() + * Checksum all the tables in SYST and print their headers + * + * success: set syst_va, return 0 + */ +static int __init sfi_parse_syst(void) +{ + struct sfi_table_key key = SFI_ANY_KEY; + int tbl_cnt, i; + void *ret; + + syst_va = sfi_map_memory(syst_pa, sizeof(struct sfi_table_simple)); + if (!syst_va) + return -ENOMEM; + + tbl_cnt = SFI_GET_NUM_ENTRIES(syst_va, u64); + for (i = 0; i < tbl_cnt; i++) { + ret = sfi_check_table(syst_va->pentry[i], &key); + if (IS_ERR(ret)) + return PTR_ERR(ret); + } + + return 0; +} + +/* + * The OS finds the System Table by searching 16-byte boundaries between + * physical address 0x000E0000 and 0x000FFFFF. The OS shall search this region + * starting at the low address and shall stop searching when the 1st valid SFI + * System Table is found. + * + * success: set syst_pa, return 0 + * fail: return -1 + */ +static __init int sfi_find_syst(void) +{ + unsigned long offset, len; + void *start; + + len = SFI_SYST_SEARCH_END - SFI_SYST_SEARCH_BEGIN; + start = sfi_map_memory(SFI_SYST_SEARCH_BEGIN, len); + if (!start) + return -1; + + for (offset = 0; offset < len; offset += 16) { + struct sfi_table_header *syst_hdr; + + syst_hdr = start + offset; + if (strncmp(syst_hdr->sig, SFI_SIG_SYST, + SFI_SIGNATURE_SIZE)) + continue; + + if (syst_hdr->len > PAGE_SIZE) + continue; + + sfi_print_table_header(SFI_SYST_SEARCH_BEGIN + offset, + syst_hdr); + + if (sfi_verify_table(syst_hdr)) + continue; + + /* + * Enforce SFI spec mandate that SYST reside within a page. + */ + if (!ON_SAME_PAGE(syst_pa, syst_pa + syst_hdr->len)) { + pr_info("SYST 0x%llx + 0x%x crosses page\n", + syst_pa, syst_hdr->len); + continue; + } + + /* Success */ + syst_pa = SFI_SYST_SEARCH_BEGIN + offset; + sfi_unmap_memory(start, len); + return 0; + } + + sfi_unmap_memory(start, len); + return -1; +} + +void __init sfi_init(void) +{ + if (!acpi_disabled) + disable_sfi(); + + if (sfi_disabled) + return; + + pr_info("Simple Firmware Interface v0.7 http://simplefirmware.org\n"); + + if (sfi_find_syst() || sfi_parse_syst() || sfi_platform_init()) + disable_sfi(); + + return; +} + +void __init sfi_init_late(void) +{ + int length; + + if (sfi_disabled) + return; + + length = syst_va->header.len; + sfi_unmap_memory(syst_va, sizeof(struct sfi_table_simple)); + + /* Use ioremap now after it is ready */ + sfi_use_ioremap = 1; + syst_va = sfi_map_memory(syst_pa, length); + + sfi_acpi_init(); +} diff --git a/drivers/sfi/sfi_core.h b/drivers/sfi/sfi_core.h new file mode 100644 index 000000000000..da82d39e104d --- /dev/null +++ b/drivers/sfi/sfi_core.h @@ -0,0 +1,70 @@ +/* sfi_core.h Simple Firmware Interface, internal header */ + +/* + + This file is provided under a dual BSD/GPLv2 license. When using or + redistributing this file, you may do so under either license. + + GPL LICENSE SUMMARY + + Copyright(c) 2009 Intel Corporation. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of version 2 of the GNU General Public License as + published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + The full GNU General Public License is included in this distribution + in the file called LICENSE.GPL. + + BSD LICENSE + + Copyright(c) 2009 Intel Corporation. All rights reserved. + + 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. + * Neither the name of Intel Corporation nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +*/ +struct sfi_table_key{ + char *sig; + char *oem_id; + char *oem_table_id; +}; + +#define SFI_ANY_KEY { .sig = NULL, .oem_id = NULL, .oem_table_id = NULL } + +extern int __init sfi_acpi_init(void); +extern struct sfi_table_header *sfi_check_table(u64 paddr, + struct sfi_table_key *key); +struct sfi_table_header *sfi_get_table(struct sfi_table_key *key); +extern void sfi_put_table(struct sfi_table_header *table); diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 2c733c27db2f..4b6f7cba3b3d 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -117,10 +117,11 @@ config SPI_GPIO speed with a custom version of this driver; see the source code. config SPI_IMX - tristate "Freescale iMX SPI controller" - depends on ARCH_MX1 && EXPERIMENTAL + tristate "Freescale i.MX SPI controllers" + depends on ARCH_MXC + select SPI_BITBANG help - This enables using the Freescale iMX SPI controller in master + This enables using the Freescale i.MX SPI controllers in master mode. config SPI_LM70_LLP @@ -173,11 +174,21 @@ config SPI_PL022 tristate "ARM AMBA PL022 SSP controller (EXPERIMENTAL)" depends on ARM_AMBA && EXPERIMENTAL default y if MACH_U300 + default y if ARCH_REALVIEW + default y if INTEGRATOR_IMPD1 + default y if ARCH_VERSATILE help This selects the ARM(R) AMBA(R) PrimeCell PL022 SSP controller. If you have an embedded system with an AMBA(R) bus and a PL022 controller, say Y or M here. +config SPI_PPC4xx + tristate "PPC4xx SPI Controller" + depends on PPC32 && 4xx && SPI_MASTER + select SPI_BITBANG + help + This selects a driver for the PPC4xx SPI Controller. + config SPI_PXA2XX tristate "PXA2xx SSP SPI master" depends on ARCH_PXA && EXPERIMENTAL @@ -211,6 +222,12 @@ config SPI_SH_SCI help SPI driver for SuperH SCI blocks. +config SPI_STMP3XXX + tristate "Freescale STMP37xx/378x SPI/SSP controller" + depends on ARCH_STMP3XXX && SPI_MASTER + help + SPI driver for Freescale STMP37xx/378x SoC SSP interface + config SPI_TXX9 tristate "Toshiba TXx9 SPI controller" depends on GENERIC_GPIO && CPU_TX49XX diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index 3de408d294ba..21a118269cac 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -26,11 +26,13 @@ obj-$(CONFIG_SPI_ORION) += orion_spi.o obj-$(CONFIG_SPI_PL022) += amba-pl022.o obj-$(CONFIG_SPI_MPC52xx_PSC) += mpc52xx_psc_spi.o obj-$(CONFIG_SPI_MPC8xxx) += spi_mpc8xxx.o +obj-$(CONFIG_SPI_PPC4xx) += spi_ppc4xx.o obj-$(CONFIG_SPI_S3C24XX_GPIO) += spi_s3c24xx_gpio.o obj-$(CONFIG_SPI_S3C24XX) += spi_s3c24xx.o obj-$(CONFIG_SPI_TXX9) += spi_txx9.o obj-$(CONFIG_SPI_XILINX) += xilinx_spi.o obj-$(CONFIG_SPI_SH_SCI) += spi_sh_sci.o +obj-$(CONFIG_SPI_STMP3XXX) += spi_stmp.o # ... add above this line ... # SPI protocol drivers (device/link on bus) diff --git a/drivers/spi/amba-pl022.c b/drivers/spi/amba-pl022.c index c0f950a7cbec..ff5bbb9c43c9 100644 --- a/drivers/spi/amba-pl022.c +++ b/drivers/spi/amba-pl022.c @@ -532,7 +532,7 @@ static void restore_state(struct pl022 *pl022) GEN_MASK_BITS(SSP_DATA_BITS_12, SSP_CR0_MASK_DSS, 0) | \ GEN_MASK_BITS(SSP_MICROWIRE_CHANNEL_FULL_DUPLEX, SSP_CR0_MASK_HALFDUP, 5) | \ GEN_MASK_BITS(SSP_CLK_POL_IDLE_LOW, SSP_CR0_MASK_SPO, 6) | \ - GEN_MASK_BITS(SSP_CLK_FALLING_EDGE, SSP_CR0_MASK_SPH, 7) | \ + GEN_MASK_BITS(SSP_CLK_SECOND_EDGE, SSP_CR0_MASK_SPH, 7) | \ GEN_MASK_BITS(NMDK_SSP_DEFAULT_CLKRATE, SSP_CR0_MASK_SCR, 8) | \ GEN_MASK_BITS(SSP_BITS_8, SSP_CR0_MASK_CSS, 16) | \ GEN_MASK_BITS(SSP_INTERFACE_MOTOROLA_SPI, SSP_CR0_MASK_FRF, 21) \ @@ -1247,8 +1247,8 @@ static int verify_controller_parameters(struct pl022 *pl022, return -EINVAL; } if (chip_info->iface == SSP_INTERFACE_MOTOROLA_SPI) { - if ((chip_info->clk_phase != SSP_CLK_RISING_EDGE) - && (chip_info->clk_phase != SSP_CLK_FALLING_EDGE)) { + if ((chip_info->clk_phase != SSP_CLK_FIRST_EDGE) + && (chip_info->clk_phase != SSP_CLK_SECOND_EDGE)) { dev_err(chip_info->dev, "Clock Phase is configured incorrectly\n"); return -EINVAL; @@ -1485,7 +1485,7 @@ static int pl022_setup(struct spi_device *spi) chip_info->data_size = SSP_DATA_BITS_12; chip_info->rx_lev_trig = SSP_RX_1_OR_MORE_ELEM; chip_info->tx_lev_trig = SSP_TX_1_OR_MORE_EMPTY_LOC; - chip_info->clk_phase = SSP_CLK_FALLING_EDGE; + chip_info->clk_phase = SSP_CLK_SECOND_EDGE; chip_info->clk_pol = SSP_CLK_POL_IDLE_LOW; chip_info->ctrl_len = SSP_BITS_8; chip_info->wait_state = SSP_MWIRE_WAIT_ZERO; @@ -1826,7 +1826,7 @@ static struct amba_id pl022_ids[] = { * ST Micro derivative, this has 32bit wide * and 32 locations deep TX/RX FIFO */ - .id = 0x00108022, + .id = 0x01080022, .mask = 0xffffffff, .data = &vendor_st, }, diff --git a/drivers/spi/omap2_mcspi.c b/drivers/spi/omap2_mcspi.c index 9b80ad36dbba..ba1a872b221e 100644 --- a/drivers/spi/omap2_mcspi.c +++ b/drivers/spi/omap2_mcspi.c @@ -41,6 +41,9 @@ #define OMAP2_MCSPI_MAX_FREQ 48000000 +/* OMAP2 has 3 SPI controllers, while OMAP3 has 4 */ +#define OMAP2_MCSPI_MAX_CTRL 4 + #define OMAP2_MCSPI_REVISION 0x00 #define OMAP2_MCSPI_SYSCONFIG 0x10 #define OMAP2_MCSPI_SYSSTATUS 0x14 @@ -59,40 +62,40 @@ /* per-register bitmasks: */ -#define OMAP2_MCSPI_SYSCONFIG_SMARTIDLE (2 << 3) -#define OMAP2_MCSPI_SYSCONFIG_ENAWAKEUP (1 << 2) -#define OMAP2_MCSPI_SYSCONFIG_AUTOIDLE (1 << 0) -#define OMAP2_MCSPI_SYSCONFIG_SOFTRESET (1 << 1) +#define OMAP2_MCSPI_SYSCONFIG_SMARTIDLE BIT(4) +#define OMAP2_MCSPI_SYSCONFIG_ENAWAKEUP BIT(2) +#define OMAP2_MCSPI_SYSCONFIG_AUTOIDLE BIT(0) +#define OMAP2_MCSPI_SYSCONFIG_SOFTRESET BIT(1) -#define OMAP2_MCSPI_SYSSTATUS_RESETDONE (1 << 0) +#define OMAP2_MCSPI_SYSSTATUS_RESETDONE BIT(0) -#define OMAP2_MCSPI_MODULCTRL_SINGLE (1 << 0) -#define OMAP2_MCSPI_MODULCTRL_MS (1 << 2) -#define OMAP2_MCSPI_MODULCTRL_STEST (1 << 3) +#define OMAP2_MCSPI_MODULCTRL_SINGLE BIT(0) +#define OMAP2_MCSPI_MODULCTRL_MS BIT(2) +#define OMAP2_MCSPI_MODULCTRL_STEST BIT(3) -#define OMAP2_MCSPI_CHCONF_PHA (1 << 0) -#define OMAP2_MCSPI_CHCONF_POL (1 << 1) +#define OMAP2_MCSPI_CHCONF_PHA BIT(0) +#define OMAP2_MCSPI_CHCONF_POL BIT(1) #define OMAP2_MCSPI_CHCONF_CLKD_MASK (0x0f << 2) -#define OMAP2_MCSPI_CHCONF_EPOL (1 << 6) +#define OMAP2_MCSPI_CHCONF_EPOL BIT(6) #define OMAP2_MCSPI_CHCONF_WL_MASK (0x1f << 7) -#define OMAP2_MCSPI_CHCONF_TRM_RX_ONLY (0x01 << 12) -#define OMAP2_MCSPI_CHCONF_TRM_TX_ONLY (0x02 << 12) +#define OMAP2_MCSPI_CHCONF_TRM_RX_ONLY BIT(12) +#define OMAP2_MCSPI_CHCONF_TRM_TX_ONLY BIT(13) #define OMAP2_MCSPI_CHCONF_TRM_MASK (0x03 << 12) -#define OMAP2_MCSPI_CHCONF_DMAW (1 << 14) -#define OMAP2_MCSPI_CHCONF_DMAR (1 << 15) -#define OMAP2_MCSPI_CHCONF_DPE0 (1 << 16) -#define OMAP2_MCSPI_CHCONF_DPE1 (1 << 17) -#define OMAP2_MCSPI_CHCONF_IS (1 << 18) -#define OMAP2_MCSPI_CHCONF_TURBO (1 << 19) -#define OMAP2_MCSPI_CHCONF_FORCE (1 << 20) +#define OMAP2_MCSPI_CHCONF_DMAW BIT(14) +#define OMAP2_MCSPI_CHCONF_DMAR BIT(15) +#define OMAP2_MCSPI_CHCONF_DPE0 BIT(16) +#define OMAP2_MCSPI_CHCONF_DPE1 BIT(17) +#define OMAP2_MCSPI_CHCONF_IS BIT(18) +#define OMAP2_MCSPI_CHCONF_TURBO BIT(19) +#define OMAP2_MCSPI_CHCONF_FORCE BIT(20) -#define OMAP2_MCSPI_CHSTAT_RXS (1 << 0) -#define OMAP2_MCSPI_CHSTAT_TXS (1 << 1) -#define OMAP2_MCSPI_CHSTAT_EOT (1 << 2) +#define OMAP2_MCSPI_CHSTAT_RXS BIT(0) +#define OMAP2_MCSPI_CHSTAT_TXS BIT(1) +#define OMAP2_MCSPI_CHSTAT_EOT BIT(2) -#define OMAP2_MCSPI_CHCTRL_EN (1 << 0) +#define OMAP2_MCSPI_CHCTRL_EN BIT(0) -#define OMAP2_MCSPI_WAKEUPENABLE_WKEN (1 << 0) +#define OMAP2_MCSPI_WAKEUPENABLE_WKEN BIT(0) /* We have 2 DMA channels per CS, one for RX and one for TX */ struct omap2_mcspi_dma { @@ -131,8 +134,23 @@ struct omap2_mcspi_cs { void __iomem *base; unsigned long phys; int word_len; + struct list_head node; + /* Context save and restore shadow register */ + u32 chconf0; +}; + +/* used for context save and restore, structure members to be updated whenever + * corresponding registers are modified. + */ +struct omap2_mcspi_regs { + u32 sysconfig; + u32 modulctrl; + u32 wakeupenable; + struct list_head cs; }; +static struct omap2_mcspi_regs omap2_mcspi_ctx[OMAP2_MCSPI_MAX_CTRL]; + static struct workqueue_struct *omap2_mcspi_wq; #define MOD_REG_BIT(val, mask, set) do { \ @@ -172,12 +190,27 @@ static inline u32 mcspi_read_cs_reg(const struct spi_device *spi, int idx) return __raw_readl(cs->base + idx); } +static inline u32 mcspi_cached_chconf0(const struct spi_device *spi) +{ + struct omap2_mcspi_cs *cs = spi->controller_state; + + return cs->chconf0; +} + +static inline void mcspi_write_chconf0(const struct spi_device *spi, u32 val) +{ + struct omap2_mcspi_cs *cs = spi->controller_state; + + cs->chconf0 = val; + mcspi_write_cs_reg(spi, OMAP2_MCSPI_CHCONF0, val); +} + static void omap2_mcspi_set_dma_req(const struct spi_device *spi, int is_read, int enable) { u32 l, rw; - l = mcspi_read_cs_reg(spi, OMAP2_MCSPI_CHCONF0); + l = mcspi_cached_chconf0(spi); if (is_read) /* 1 is read, 0 write */ rw = OMAP2_MCSPI_CHCONF_DMAR; @@ -185,7 +218,7 @@ static void omap2_mcspi_set_dma_req(const struct spi_device *spi, rw = OMAP2_MCSPI_CHCONF_DMAW; MOD_REG_BIT(l, rw, enable); - mcspi_write_cs_reg(spi, OMAP2_MCSPI_CHCONF0, l); + mcspi_write_chconf0(spi, l); } static void omap2_mcspi_set_enable(const struct spi_device *spi, int enable) @@ -200,9 +233,9 @@ static void omap2_mcspi_force_cs(struct spi_device *spi, int cs_active) { u32 l; - l = mcspi_read_cs_reg(spi, OMAP2_MCSPI_CHCONF0); + l = mcspi_cached_chconf0(spi); MOD_REG_BIT(l, OMAP2_MCSPI_CHCONF_FORCE, cs_active); - mcspi_write_cs_reg(spi, OMAP2_MCSPI_CHCONF0, l); + mcspi_write_chconf0(spi, l); } static void omap2_mcspi_set_master_mode(struct spi_master *master) @@ -217,6 +250,46 @@ static void omap2_mcspi_set_master_mode(struct spi_master *master) MOD_REG_BIT(l, OMAP2_MCSPI_MODULCTRL_MS, 0); MOD_REG_BIT(l, OMAP2_MCSPI_MODULCTRL_SINGLE, 1); mcspi_write_reg(master, OMAP2_MCSPI_MODULCTRL, l); + + omap2_mcspi_ctx[master->bus_num - 1].modulctrl = l; +} + +static void omap2_mcspi_restore_ctx(struct omap2_mcspi *mcspi) +{ + struct spi_master *spi_cntrl; + struct omap2_mcspi_cs *cs; + spi_cntrl = mcspi->master; + + /* McSPI: context restore */ + mcspi_write_reg(spi_cntrl, OMAP2_MCSPI_MODULCTRL, + omap2_mcspi_ctx[spi_cntrl->bus_num - 1].modulctrl); + + mcspi_write_reg(spi_cntrl, OMAP2_MCSPI_SYSCONFIG, + omap2_mcspi_ctx[spi_cntrl->bus_num - 1].sysconfig); + + mcspi_write_reg(spi_cntrl, OMAP2_MCSPI_WAKEUPENABLE, + omap2_mcspi_ctx[spi_cntrl->bus_num - 1].wakeupenable); + + list_for_each_entry(cs, &omap2_mcspi_ctx[spi_cntrl->bus_num - 1].cs, + node) + __raw_writel(cs->chconf0, cs->base + OMAP2_MCSPI_CHCONF0); +} +static void omap2_mcspi_disable_clocks(struct omap2_mcspi *mcspi) +{ + clk_disable(mcspi->ick); + clk_disable(mcspi->fck); +} + +static int omap2_mcspi_enable_clocks(struct omap2_mcspi *mcspi) +{ + if (clk_enable(mcspi->ick)) + return -ENODEV; + if (clk_enable(mcspi->fck)) + return -ENODEV; + + omap2_mcspi_restore_ctx(mcspi); + + return 0; } static unsigned @@ -357,7 +430,7 @@ omap2_mcspi_txrx_pio(struct spi_device *spi, struct spi_transfer *xfer) c = count; word_len = cs->word_len; - l = mcspi_read_cs_reg(spi, OMAP2_MCSPI_CHCONF0); + l = mcspi_cached_chconf0(spi); l &= ~OMAP2_MCSPI_CHCONF_TRM_MASK; /* We store the pre-calculated register addresses on stack to speed @@ -397,8 +470,7 @@ omap2_mcspi_txrx_pio(struct spi_device *spi, struct spi_transfer *xfer) * more word i/o: switch to rx+tx */ if (c == 0 && tx == NULL) - mcspi_write_cs_reg(spi, - OMAP2_MCSPI_CHCONF0, l); + mcspi_write_chconf0(spi, l); *rx++ = __raw_readl(rx_reg); #ifdef VERBOSE dev_dbg(&spi->dev, "read-%d %02x\n", @@ -436,8 +508,7 @@ omap2_mcspi_txrx_pio(struct spi_device *spi, struct spi_transfer *xfer) * more word i/o: switch to rx+tx */ if (c == 0 && tx == NULL) - mcspi_write_cs_reg(spi, - OMAP2_MCSPI_CHCONF0, l); + mcspi_write_chconf0(spi, l); *rx++ = __raw_readl(rx_reg); #ifdef VERBOSE dev_dbg(&spi->dev, "read-%d %04x\n", @@ -475,8 +546,7 @@ omap2_mcspi_txrx_pio(struct spi_device *spi, struct spi_transfer *xfer) * more word i/o: switch to rx+tx */ if (c == 0 && tx == NULL) - mcspi_write_cs_reg(spi, - OMAP2_MCSPI_CHCONF0, l); + mcspi_write_chconf0(spi, l); *rx++ = __raw_readl(rx_reg); #ifdef VERBOSE dev_dbg(&spi->dev, "read-%d %04x\n", @@ -505,10 +575,12 @@ static int omap2_mcspi_setup_transfer(struct spi_device *spi, { struct omap2_mcspi_cs *cs = spi->controller_state; struct omap2_mcspi *mcspi; + struct spi_master *spi_cntrl; u32 l = 0, div = 0; u8 word_len = spi->bits_per_word; mcspi = spi_master_get_devdata(spi->master); + spi_cntrl = mcspi->master; if (t != NULL && t->bits_per_word) word_len = t->bits_per_word; @@ -522,7 +594,7 @@ static int omap2_mcspi_setup_transfer(struct spi_device *spi, } else div = 15; - l = mcspi_read_cs_reg(spi, OMAP2_MCSPI_CHCONF0); + l = mcspi_cached_chconf0(spi); /* standard 4-wire master mode: SCK, MOSI/out, MISO/in, nCS * REVISIT: this controller could support SPI_3WIRE mode. @@ -554,7 +626,7 @@ static int omap2_mcspi_setup_transfer(struct spi_device *spi, else l &= ~OMAP2_MCSPI_CHCONF_PHA; - mcspi_write_cs_reg(spi, OMAP2_MCSPI_CHCONF0, l); + mcspi_write_chconf0(spi, l); dev_dbg(&spi->dev, "setup: speed %d, sample %s edge, clk %s\n", OMAP2_MCSPI_MAX_FREQ / (1 << div), @@ -647,7 +719,11 @@ static int omap2_mcspi_setup(struct spi_device *spi) return -ENOMEM; cs->base = mcspi->base + spi->chip_select * 0x14; cs->phys = mcspi->phys + spi->chip_select * 0x14; + cs->chconf0 = 0; spi->controller_state = cs; + /* Link this to context save list */ + list_add_tail(&cs->node, + &omap2_mcspi_ctx[mcspi->master->bus_num - 1].cs); } if (mcspi_dma->dma_rx_channel == -1 @@ -657,11 +733,11 @@ static int omap2_mcspi_setup(struct spi_device *spi) return ret; } - clk_enable(mcspi->ick); - clk_enable(mcspi->fck); + if (omap2_mcspi_enable_clocks(mcspi)) + return -ENODEV; + ret = omap2_mcspi_setup_transfer(spi, NULL); - clk_disable(mcspi->fck); - clk_disable(mcspi->ick); + omap2_mcspi_disable_clocks(mcspi); return ret; } @@ -670,10 +746,15 @@ static void omap2_mcspi_cleanup(struct spi_device *spi) { struct omap2_mcspi *mcspi; struct omap2_mcspi_dma *mcspi_dma; + struct omap2_mcspi_cs *cs; mcspi = spi_master_get_devdata(spi->master); mcspi_dma = &mcspi->dma_channels[spi->chip_select]; + /* Unlink controller state from context save list */ + cs = spi->controller_state; + list_del(&cs->node); + kfree(spi->controller_state); if (mcspi_dma->dma_rx_channel != -1) { @@ -693,8 +774,8 @@ static void omap2_mcspi_work(struct work_struct *work) mcspi = container_of(work, struct omap2_mcspi, work); spin_lock_irq(&mcspi->lock); - clk_enable(mcspi->ick); - clk_enable(mcspi->fck); + if (omap2_mcspi_enable_clocks(mcspi)) + goto out; /* We only enable one channel at a time -- the one whose message is * at the head of the queue -- although this controller would gladly @@ -741,13 +822,13 @@ static void omap2_mcspi_work(struct work_struct *work) cs_active = 1; } - chconf = mcspi_read_cs_reg(spi, OMAP2_MCSPI_CHCONF0); + chconf = mcspi_cached_chconf0(spi); chconf &= ~OMAP2_MCSPI_CHCONF_TRM_MASK; if (t->tx_buf == NULL) chconf |= OMAP2_MCSPI_CHCONF_TRM_RX_ONLY; else if (t->rx_buf == NULL) chconf |= OMAP2_MCSPI_CHCONF_TRM_TX_ONLY; - mcspi_write_cs_reg(spi, OMAP2_MCSPI_CHCONF0, chconf); + mcspi_write_chconf0(spi, chconf); if (t->len) { unsigned count; @@ -796,9 +877,9 @@ static void omap2_mcspi_work(struct work_struct *work) spin_lock_irq(&mcspi->lock); } - clk_disable(mcspi->fck); - clk_disable(mcspi->ick); + omap2_mcspi_disable_clocks(mcspi); +out: spin_unlock_irq(&mcspi->lock); } @@ -885,8 +966,8 @@ static int __init omap2_mcspi_reset(struct omap2_mcspi *mcspi) struct spi_master *master = mcspi->master; u32 tmp; - clk_enable(mcspi->ick); - clk_enable(mcspi->fck); + if (omap2_mcspi_enable_clocks(mcspi)) + return -1; mcspi_write_reg(master, OMAP2_MCSPI_SYSCONFIG, OMAP2_MCSPI_SYSCONFIG_SOFTRESET); @@ -894,18 +975,18 @@ static int __init omap2_mcspi_reset(struct omap2_mcspi *mcspi) tmp = mcspi_read_reg(master, OMAP2_MCSPI_SYSSTATUS); } while (!(tmp & OMAP2_MCSPI_SYSSTATUS_RESETDONE)); - mcspi_write_reg(master, OMAP2_MCSPI_SYSCONFIG, - OMAP2_MCSPI_SYSCONFIG_AUTOIDLE | - OMAP2_MCSPI_SYSCONFIG_ENAWAKEUP | - OMAP2_MCSPI_SYSCONFIG_SMARTIDLE); + tmp = OMAP2_MCSPI_SYSCONFIG_AUTOIDLE | + OMAP2_MCSPI_SYSCONFIG_ENAWAKEUP | + OMAP2_MCSPI_SYSCONFIG_SMARTIDLE; + mcspi_write_reg(master, OMAP2_MCSPI_SYSCONFIG, tmp); + omap2_mcspi_ctx[master->bus_num - 1].sysconfig = tmp; - mcspi_write_reg(master, OMAP2_MCSPI_WAKEUPENABLE, - OMAP2_MCSPI_WAKEUPENABLE_WKEN); + tmp = OMAP2_MCSPI_WAKEUPENABLE_WKEN; + mcspi_write_reg(master, OMAP2_MCSPI_WAKEUPENABLE, tmp); + omap2_mcspi_ctx[master->bus_num - 1].wakeupenable = tmp; omap2_mcspi_set_master_mode(master); - - clk_disable(mcspi->fck); - clk_disable(mcspi->ick); + omap2_mcspi_disable_clocks(mcspi); return 0; } @@ -933,7 +1014,8 @@ static u8 __initdata spi2_txdma_id[] = { OMAP24XX_DMA_SPI2_TX1, }; -#if defined(CONFIG_ARCH_OMAP2430) || defined(CONFIG_ARCH_OMAP34XX) +#if defined(CONFIG_ARCH_OMAP2430) || defined(CONFIG_ARCH_OMAP34XX) \ + || defined(CONFIG_ARCH_OMAP4) static u8 __initdata spi3_rxdma_id[] = { OMAP24XX_DMA_SPI3_RX0, OMAP24XX_DMA_SPI3_RX1, @@ -945,7 +1027,7 @@ static u8 __initdata spi3_txdma_id[] = { }; #endif -#ifdef CONFIG_ARCH_OMAP3 +#if defined(CONFIG_ARCH_OMAP3) || defined(CONFIG_ARCH_OMAP4) static u8 __initdata spi4_rxdma_id[] = { OMAP34XX_DMA_SPI4_RX0, }; @@ -975,14 +1057,15 @@ static int __init omap2_mcspi_probe(struct platform_device *pdev) txdma_id = spi2_txdma_id; num_chipselect = 2; break; -#if defined(CONFIG_ARCH_OMAP2430) || defined(CONFIG_ARCH_OMAP3) +#if defined(CONFIG_ARCH_OMAP2430) || defined(CONFIG_ARCH_OMAP3) \ + || defined(CONFIG_ARCH_OMAP4) case 3: rxdma_id = spi3_rxdma_id; txdma_id = spi3_txdma_id; num_chipselect = 2; break; #endif -#ifdef CONFIG_ARCH_OMAP3 +#if defined(CONFIG_ARCH_OMAP3) || defined(CONFIG_ARCH_OMAP4) case 4: rxdma_id = spi4_rxdma_id; txdma_id = spi4_txdma_id; @@ -1038,6 +1121,7 @@ static int __init omap2_mcspi_probe(struct platform_device *pdev) spin_lock_init(&mcspi->lock); INIT_LIST_HEAD(&mcspi->msg_queue); + INIT_LIST_HEAD(&omap2_mcspi_ctx[master->bus_num - 1].cs); mcspi->ick = clk_get(&pdev->dev, "ick"); if (IS_ERR(mcspi->ick)) { diff --git a/drivers/spi/pxa2xx_spi.c b/drivers/spi/pxa2xx_spi.c index d949dbf1141f..c8c2b693ffac 100644 --- a/drivers/spi/pxa2xx_spi.c +++ b/drivers/spi/pxa2xx_spi.c @@ -1668,10 +1668,9 @@ static void pxa2xx_spi_shutdown(struct platform_device *pdev) } #ifdef CONFIG_PM - -static int pxa2xx_spi_suspend(struct platform_device *pdev, pm_message_t state) +static int pxa2xx_spi_suspend(struct device *dev) { - struct driver_data *drv_data = platform_get_drvdata(pdev); + struct driver_data *drv_data = dev_get_drvdata(dev); struct ssp_device *ssp = drv_data->ssp; int status = 0; @@ -1684,9 +1683,9 @@ static int pxa2xx_spi_suspend(struct platform_device *pdev, pm_message_t state) return 0; } -static int pxa2xx_spi_resume(struct platform_device *pdev) +static int pxa2xx_spi_resume(struct device *dev) { - struct driver_data *drv_data = platform_get_drvdata(pdev); + struct driver_data *drv_data = dev_get_drvdata(dev); struct ssp_device *ssp = drv_data->ssp; int status = 0; @@ -1703,33 +1702,36 @@ static int pxa2xx_spi_resume(struct platform_device *pdev) /* Start the queue running */ status = start_queue(drv_data); if (status != 0) { - dev_err(&pdev->dev, "problem starting queue (%d)\n", status); + dev_err(dev, "problem starting queue (%d)\n", status); return status; } return 0; } -#else -#define pxa2xx_spi_suspend NULL -#define pxa2xx_spi_resume NULL -#endif /* CONFIG_PM */ + +static struct dev_pm_ops pxa2xx_spi_pm_ops = { + .suspend = pxa2xx_spi_suspend, + .resume = pxa2xx_spi_resume, +}; +#endif static struct platform_driver driver = { .driver = { - .name = "pxa2xx-spi", - .owner = THIS_MODULE, + .name = "pxa2xx-spi", + .owner = THIS_MODULE, +#ifdef CONFIG_PM + .pm = &pxa2xx_spi_pm_ops, +#endif }, .remove = pxa2xx_spi_remove, .shutdown = pxa2xx_spi_shutdown, - .suspend = pxa2xx_spi_suspend, - .resume = pxa2xx_spi_resume, }; static int __init pxa2xx_spi_init(void) { return platform_driver_probe(&driver, pxa2xx_spi_probe); } -module_init(pxa2xx_spi_init); +subsys_initcall(pxa2xx_spi_init); static void __exit pxa2xx_spi_exit(void) { diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index 70845ccd85c3..b76f2468a84a 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -23,6 +23,7 @@ #include <linux/init.h> #include <linux/cache.h> #include <linux/mutex.h> +#include <linux/mod_devicetable.h> #include <linux/spi/spi.h> @@ -59,9 +60,32 @@ static struct device_attribute spi_dev_attrs[] = { * and the sysfs version makes coldplug work too. */ +static const struct spi_device_id *spi_match_id(const struct spi_device_id *id, + const struct spi_device *sdev) +{ + while (id->name[0]) { + if (!strcmp(sdev->modalias, id->name)) + return id; + id++; + } + return NULL; +} + +const struct spi_device_id *spi_get_device_id(const struct spi_device *sdev) +{ + const struct spi_driver *sdrv = to_spi_driver(sdev->dev.driver); + + return spi_match_id(sdrv->id_table, sdev); +} +EXPORT_SYMBOL_GPL(spi_get_device_id); + static int spi_match_device(struct device *dev, struct device_driver *drv) { const struct spi_device *spi = to_spi_device(dev); + const struct spi_driver *sdrv = to_spi_driver(drv); + + if (sdrv->id_table) + return !!spi_match_id(sdrv->id_table, spi); return strcmp(spi->modalias, drv->name) == 0; } @@ -70,7 +94,7 @@ static int spi_uevent(struct device *dev, struct kobj_uevent_env *env) { const struct spi_device *spi = to_spi_device(dev); - add_uevent_var(env, "MODALIAS=%s", spi->modalias); + add_uevent_var(env, "MODALIAS=%s%s", SPI_MODULE_PREFIX, spi->modalias); return 0; } @@ -639,6 +663,65 @@ int spi_setup(struct spi_device *spi) } EXPORT_SYMBOL_GPL(spi_setup); +/** + * spi_async - asynchronous SPI transfer + * @spi: device with which data will be exchanged + * @message: describes the data transfers, including completion callback + * Context: any (irqs may be blocked, etc) + * + * This call may be used in_irq and other contexts which can't sleep, + * as well as from task contexts which can sleep. + * + * The completion callback is invoked in a context which can't sleep. + * Before that invocation, the value of message->status is undefined. + * When the callback is issued, message->status holds either zero (to + * indicate complete success) or a negative error code. After that + * callback returns, the driver which issued the transfer request may + * deallocate the associated memory; it's no longer in use by any SPI + * core or controller driver code. + * + * Note that although all messages to a spi_device are handled in + * FIFO order, messages may go to different devices in other orders. + * Some device might be higher priority, or have various "hard" access + * time requirements, for example. + * + * On detection of any fault during the transfer, processing of + * the entire message is aborted, and the device is deselected. + * Until returning from the associated message completion callback, + * no other spi_message queued to that device will be processed. + * (This rule applies equally to all the synchronous transfer calls, + * which are wrappers around this core asynchronous primitive.) + */ +int spi_async(struct spi_device *spi, struct spi_message *message) +{ + struct spi_master *master = spi->master; + + /* Half-duplex links include original MicroWire, and ones with + * only one data pin like SPI_3WIRE (switches direction) or where + * either MOSI or MISO is missing. They can also be caused by + * software limitations. + */ + if ((master->flags & SPI_MASTER_HALF_DUPLEX) + || (spi->mode & SPI_3WIRE)) { + struct spi_transfer *xfer; + unsigned flags = master->flags; + + list_for_each_entry(xfer, &message->transfers, transfer_list) { + if (xfer->rx_buf && xfer->tx_buf) + return -EINVAL; + if ((flags & SPI_MASTER_NO_TX) && xfer->tx_buf) + return -EINVAL; + if ((flags & SPI_MASTER_NO_RX) && xfer->rx_buf) + return -EINVAL; + } + } + + message->spi = spi; + message->status = -EINPROGRESS; + return master->transfer(spi, message); +} +EXPORT_SYMBOL_GPL(spi_async); + /*-------------------------------------------------------------------------*/ diff --git a/drivers/spi/spi_imx.c b/drivers/spi/spi_imx.c index c195e45f7f35..89c22efedfb0 100644 --- a/drivers/spi/spi_imx.c +++ b/drivers/spi/spi_imx.c @@ -1,1770 +1,680 @@ /* - * drivers/spi/spi_imx.c - * - * Copyright (C) 2006 SWAPP - * Andrea Paterniani <a.paterniani@swapp-eng.it> - * - * Initial version inspired by: - * linux-2.6.17-rc3-mm1/drivers/spi/pxa2xx_spi.c - * - * 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. + * Copyright 2004-2007 Freescale Semiconductor, Inc. All Rights Reserved. + * Copyright (C) 2008 Juergen Beisert * + * 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 + * 51 Franklin Street, Fifth Floor + * Boston, MA 02110-1301, USA. */ +#include <linux/clk.h> +#include <linux/completion.h> +#include <linux/delay.h> +#include <linux/err.h> +#include <linux/gpio.h> #include <linux/init.h> -#include <linux/module.h> -#include <linux/device.h> -#include <linux/ioport.h> -#include <linux/errno.h> #include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/irq.h> +#include <linux/kernel.h> +#include <linux/module.h> #include <linux/platform_device.h> -#include <linux/dma-mapping.h> #include <linux/spi/spi.h> -#include <linux/workqueue.h> -#include <linux/delay.h> -#include <linux/clk.h> +#include <linux/spi/spi_bitbang.h> +#include <linux/types.h> -#include <asm/io.h> -#include <asm/irq.h> -#include <asm/delay.h> - -#include <mach/hardware.h> -#include <mach/imx-dma.h> -#include <mach/spi_imx.h> - -/*-------------------------------------------------------------------------*/ -/* SPI Registers offsets from peripheral base address */ -#define SPI_RXDATA (0x00) -#define SPI_TXDATA (0x04) -#define SPI_CONTROL (0x08) -#define SPI_INT_STATUS (0x0C) -#define SPI_TEST (0x10) -#define SPI_PERIOD (0x14) -#define SPI_DMA (0x18) -#define SPI_RESET (0x1C) - -/* SPI Control Register Bit Fields & Masks */ -#define SPI_CONTROL_BITCOUNT_MASK (0xF) /* Bit Count Mask */ -#define SPI_CONTROL_BITCOUNT(n) (((n) - 1) & SPI_CONTROL_BITCOUNT_MASK) -#define SPI_CONTROL_POL (0x1 << 4) /* Clock Polarity Mask */ -#define SPI_CONTROL_POL_ACT_HIGH (0x0 << 4) /* Active high pol. (0=idle) */ -#define SPI_CONTROL_POL_ACT_LOW (0x1 << 4) /* Active low pol. (1=idle) */ -#define SPI_CONTROL_PHA (0x1 << 5) /* Clock Phase Mask */ -#define SPI_CONTROL_PHA_0 (0x0 << 5) /* Clock Phase 0 */ -#define SPI_CONTROL_PHA_1 (0x1 << 5) /* Clock Phase 1 */ -#define SPI_CONTROL_SSCTL (0x1 << 6) /* /SS Waveform Select Mask */ -#define SPI_CONTROL_SSCTL_0 (0x0 << 6) /* Master: /SS stays low between SPI burst - Slave: RXFIFO advanced by BIT_COUNT */ -#define SPI_CONTROL_SSCTL_1 (0x1 << 6) /* Master: /SS insert pulse between SPI burst - Slave: RXFIFO advanced by /SS rising edge */ -#define SPI_CONTROL_SSPOL (0x1 << 7) /* /SS Polarity Select Mask */ -#define SPI_CONTROL_SSPOL_ACT_LOW (0x0 << 7) /* /SS Active low */ -#define SPI_CONTROL_SSPOL_ACT_HIGH (0x1 << 7) /* /SS Active high */ -#define SPI_CONTROL_XCH (0x1 << 8) /* Exchange */ -#define SPI_CONTROL_SPIEN (0x1 << 9) /* SPI Module Enable */ -#define SPI_CONTROL_MODE (0x1 << 10) /* SPI Mode Select Mask */ -#define SPI_CONTROL_MODE_SLAVE (0x0 << 10) /* SPI Mode Slave */ -#define SPI_CONTROL_MODE_MASTER (0x1 << 10) /* SPI Mode Master */ -#define SPI_CONTROL_DRCTL (0x3 << 11) /* /SPI_RDY Control Mask */ -#define SPI_CONTROL_DRCTL_0 (0x0 << 11) /* Ignore /SPI_RDY */ -#define SPI_CONTROL_DRCTL_1 (0x1 << 11) /* /SPI_RDY falling edge triggers input */ -#define SPI_CONTROL_DRCTL_2 (0x2 << 11) /* /SPI_RDY active low level triggers input */ -#define SPI_CONTROL_DATARATE (0x7 << 13) /* Data Rate Mask */ -#define SPI_PERCLK2_DIV_MIN (0) /* PERCLK2:4 */ -#define SPI_PERCLK2_DIV_MAX (7) /* PERCLK2:512 */ -#define SPI_CONTROL_DATARATE_MIN (SPI_PERCLK2_DIV_MAX << 13) -#define SPI_CONTROL_DATARATE_MAX (SPI_PERCLK2_DIV_MIN << 13) -#define SPI_CONTROL_DATARATE_BAD (SPI_CONTROL_DATARATE_MIN + 1) - -/* SPI Interrupt/Status Register Bit Fields & Masks */ -#define SPI_STATUS_TE (0x1 << 0) /* TXFIFO Empty Status */ -#define SPI_STATUS_TH (0x1 << 1) /* TXFIFO Half Status */ -#define SPI_STATUS_TF (0x1 << 2) /* TXFIFO Full Status */ -#define SPI_STATUS_RR (0x1 << 3) /* RXFIFO Data Ready Status */ -#define SPI_STATUS_RH (0x1 << 4) /* RXFIFO Half Status */ -#define SPI_STATUS_RF (0x1 << 5) /* RXFIFO Full Status */ -#define SPI_STATUS_RO (0x1 << 6) /* RXFIFO Overflow */ -#define SPI_STATUS_BO (0x1 << 7) /* Bit Count Overflow */ -#define SPI_STATUS (0xFF) /* SPI Status Mask */ -#define SPI_INTEN_TE (0x1 << 8) /* TXFIFO Empty Interrupt Enable */ -#define SPI_INTEN_TH (0x1 << 9) /* TXFIFO Half Interrupt Enable */ -#define SPI_INTEN_TF (0x1 << 10) /* TXFIFO Full Interrupt Enable */ -#define SPI_INTEN_RE (0x1 << 11) /* RXFIFO Data Ready Interrupt Enable */ -#define SPI_INTEN_RH (0x1 << 12) /* RXFIFO Half Interrupt Enable */ -#define SPI_INTEN_RF (0x1 << 13) /* RXFIFO Full Interrupt Enable */ -#define SPI_INTEN_RO (0x1 << 14) /* RXFIFO Overflow Interrupt Enable */ -#define SPI_INTEN_BO (0x1 << 15) /* Bit Count Overflow Interrupt Enable */ -#define SPI_INTEN (0xFF << 8) /* SPI Interrupt Enable Mask */ - -/* SPI Test Register Bit Fields & Masks */ -#define SPI_TEST_TXCNT (0xF << 0) /* TXFIFO Counter */ -#define SPI_TEST_RXCNT_LSB (4) /* RXFIFO Counter LSB */ -#define SPI_TEST_RXCNT (0xF << 4) /* RXFIFO Counter */ -#define SPI_TEST_SSTATUS (0xF << 8) /* State Machine Status */ -#define SPI_TEST_LBC (0x1 << 14) /* Loop Back Control */ - -/* SPI Period Register Bit Fields & Masks */ -#define SPI_PERIOD_WAIT (0x7FFF << 0) /* Wait Between Transactions */ -#define SPI_PERIOD_MAX_WAIT (0x7FFF) /* Max Wait Between - Transactions */ -#define SPI_PERIOD_CSRC (0x1 << 15) /* Period Clock Source Mask */ -#define SPI_PERIOD_CSRC_BCLK (0x0 << 15) /* Period Clock Source is - Bit Clock */ -#define SPI_PERIOD_CSRC_32768 (0x1 << 15) /* Period Clock Source is - 32.768 KHz Clock */ - -/* SPI DMA Register Bit Fields & Masks */ -#define SPI_DMA_RHDMA (0x1 << 4) /* RXFIFO Half Status */ -#define SPI_DMA_RFDMA (0x1 << 5) /* RXFIFO Full Status */ -#define SPI_DMA_TEDMA (0x1 << 6) /* TXFIFO Empty Status */ -#define SPI_DMA_THDMA (0x1 << 7) /* TXFIFO Half Status */ -#define SPI_DMA_RHDEN (0x1 << 12) /* RXFIFO Half DMA Request Enable */ -#define SPI_DMA_RFDEN (0x1 << 13) /* RXFIFO Full DMA Request Enable */ -#define SPI_DMA_TEDEN (0x1 << 14) /* TXFIFO Empty DMA Request Enable */ -#define SPI_DMA_THDEN (0x1 << 15) /* TXFIFO Half DMA Request Enable */ - -/* SPI Soft Reset Register Bit Fields & Masks */ -#define SPI_RESET_START (0x1) /* Start */ - -/* Default SPI configuration values */ -#define SPI_DEFAULT_CONTROL \ -( \ - SPI_CONTROL_BITCOUNT(16) | \ - SPI_CONTROL_POL_ACT_HIGH | \ - SPI_CONTROL_PHA_0 | \ - SPI_CONTROL_SPIEN | \ - SPI_CONTROL_SSCTL_1 | \ - SPI_CONTROL_MODE_MASTER | \ - SPI_CONTROL_DRCTL_0 | \ - SPI_CONTROL_DATARATE_MIN \ -) -#define SPI_DEFAULT_ENABLE_LOOPBACK (0) -#define SPI_DEFAULT_ENABLE_DMA (0) -#define SPI_DEFAULT_PERIOD_WAIT (8) -/*-------------------------------------------------------------------------*/ - - -/*-------------------------------------------------------------------------*/ -/* TX/RX SPI FIFO size */ -#define SPI_FIFO_DEPTH (8) -#define SPI_FIFO_BYTE_WIDTH (2) -#define SPI_FIFO_OVERFLOW_MARGIN (2) - -/* DMA burst length for half full/empty request trigger */ -#define SPI_DMA_BLR (SPI_FIFO_DEPTH * SPI_FIFO_BYTE_WIDTH / 2) - -/* Dummy char output to achieve reads. - Choosing something different from all zeroes may help pattern recogition - for oscilloscope analysis, but may break some drivers. */ -#define SPI_DUMMY_u8 0 -#define SPI_DUMMY_u16 ((SPI_DUMMY_u8 << 8) | SPI_DUMMY_u8) -#define SPI_DUMMY_u32 ((SPI_DUMMY_u16 << 16) | SPI_DUMMY_u16) - -/** - * Macro to change a u32 field: - * @r : register to edit - * @m : bit mask - * @v : new value for the field correctly bit-alligned -*/ -#define u32_EDIT(r, m, v) r = (r & ~(m)) | (v) - -/* Message state */ -#define START_STATE ((void*)0) -#define RUNNING_STATE ((void*)1) -#define DONE_STATE ((void*)2) -#define ERROR_STATE ((void*)-1) - -/* Queue state */ -#define QUEUE_RUNNING (0) -#define QUEUE_STOPPED (1) - -#define IS_DMA_ALIGNED(x) (((u32)(x) & 0x03) == 0) -#define DMA_ALIGNMENT 4 -/*-------------------------------------------------------------------------*/ - - -/*-------------------------------------------------------------------------*/ -/* Driver data structs */ - -/* Context */ -struct driver_data { - /* Driver model hookup */ - struct platform_device *pdev; - - /* SPI framework hookup */ - struct spi_master *master; +#include <mach/spi.h> - /* IMX hookup */ - struct spi_imx_master *master_info; - - /* Memory resources and SPI regs virtual address */ - struct resource *ioarea; - void __iomem *regs; - - /* SPI RX_DATA physical address */ - dma_addr_t rd_data_phys; - - /* Driver message queue */ - struct workqueue_struct *workqueue; - struct work_struct work; - spinlock_t lock; - struct list_head queue; - int busy; - int run; - - /* Message Transfer pump */ - struct tasklet_struct pump_transfers; - - /* Current message, transfer and state */ - struct spi_message *cur_msg; - struct spi_transfer *cur_transfer; - struct chip_data *cur_chip; - - /* Rd / Wr buffers pointers */ - size_t len; - void *tx; - void *tx_end; - void *rx; - void *rx_end; - - u8 rd_only; - u8 n_bytes; - int cs_change; - - /* Function pointers */ - irqreturn_t (*transfer_handler)(struct driver_data *drv_data); - void (*cs_control)(u32 command); - - /* DMA setup */ - int rx_channel; - int tx_channel; - dma_addr_t rx_dma; - dma_addr_t tx_dma; - int rx_dma_needs_unmap; - int tx_dma_needs_unmap; - size_t tx_map_len; - u32 dummy_dma_buf ____cacheline_aligned; +#define DRIVER_NAME "spi_imx" - struct clk *clk; -}; +#define MXC_CSPIRXDATA 0x00 +#define MXC_CSPITXDATA 0x04 +#define MXC_CSPICTRL 0x08 +#define MXC_CSPIINT 0x0c +#define MXC_RESET 0x1c -/* Runtime state */ -struct chip_data { - u32 control; - u32 period; - u32 test; +/* generic defines to abstract from the different register layouts */ +#define MXC_INT_RR (1 << 0) /* Receive data ready interrupt */ +#define MXC_INT_TE (1 << 1) /* Transmit FIFO empty interrupt */ - u8 enable_dma:1; - u8 bits_per_word; - u8 n_bytes; - u32 max_speed_hz; - - void (*cs_control)(u32 command); +struct spi_imx_config { + unsigned int speed_hz; + unsigned int bpw; + unsigned int mode; + int cs; }; -/*-------------------------------------------------------------------------*/ - -static void pump_messages(struct work_struct *work); +struct spi_imx_data { + struct spi_bitbang bitbang; -static void flush(struct driver_data *drv_data) -{ - void __iomem *regs = drv_data->regs; - u32 control; + struct completion xfer_done; + void *base; + int irq; + struct clk *clk; + unsigned long spi_clk; + int *chipselect; + + unsigned int count; + void (*tx)(struct spi_imx_data *); + void (*rx)(struct spi_imx_data *); + void *rx_buf; + const void *tx_buf; + unsigned int txfifo; /* number of words pushed in tx FIFO */ + + /* SoC specific functions */ + void (*intctrl)(struct spi_imx_data *, int); + int (*config)(struct spi_imx_data *, struct spi_imx_config *); + void (*trigger)(struct spi_imx_data *); + int (*rx_available)(struct spi_imx_data *); +}; - dev_dbg(&drv_data->pdev->dev, "flush\n"); +#define MXC_SPI_BUF_RX(type) \ +static void spi_imx_buf_rx_##type(struct spi_imx_data *spi_imx) \ +{ \ + unsigned int val = readl(spi_imx->base + MXC_CSPIRXDATA); \ + \ + if (spi_imx->rx_buf) { \ + *(type *)spi_imx->rx_buf = val; \ + spi_imx->rx_buf += sizeof(type); \ + } \ +} + +#define MXC_SPI_BUF_TX(type) \ +static void spi_imx_buf_tx_##type(struct spi_imx_data *spi_imx) \ +{ \ + type val = 0; \ + \ + if (spi_imx->tx_buf) { \ + val = *(type *)spi_imx->tx_buf; \ + spi_imx->tx_buf += sizeof(type); \ + } \ + \ + spi_imx->count -= sizeof(type); \ + \ + writel(val, spi_imx->base + MXC_CSPITXDATA); \ +} + +MXC_SPI_BUF_RX(u8) +MXC_SPI_BUF_TX(u8) +MXC_SPI_BUF_RX(u16) +MXC_SPI_BUF_TX(u16) +MXC_SPI_BUF_RX(u32) +MXC_SPI_BUF_TX(u32) + +/* First entry is reserved, second entry is valid only if SDHC_SPIEN is set + * (which is currently not the case in this driver) + */ +static int mxc_clkdivs[] = {0, 3, 4, 6, 8, 12, 16, 24, 32, 48, 64, 96, 128, 192, + 256, 384, 512, 768, 1024}; - /* Wait for end of transaction */ - do { - control = readl(regs + SPI_CONTROL); - } while (control & SPI_CONTROL_XCH); +/* MX21, MX27 */ +static unsigned int spi_imx_clkdiv_1(unsigned int fin, + unsigned int fspi) +{ + int i, max; - /* Release chip select if requested, transfer delays are - handled in pump_transfers */ - if (drv_data->cs_change) - drv_data->cs_control(SPI_CS_DEASSERT); + if (cpu_is_mx21()) + max = 18; + else + max = 16; - /* Disable SPI to flush FIFOs */ - writel(control & ~SPI_CONTROL_SPIEN, regs + SPI_CONTROL); - writel(control, regs + SPI_CONTROL); -} + for (i = 2; i < max; i++) + if (fspi * mxc_clkdivs[i] >= fin) + return i; -static void restore_state(struct driver_data *drv_data) -{ - void __iomem *regs = drv_data->regs; - struct chip_data *chip = drv_data->cur_chip; - - /* Load chip registers */ - dev_dbg(&drv_data->pdev->dev, - "restore_state\n" - " test = 0x%08X\n" - " control = 0x%08X\n", - chip->test, - chip->control); - writel(chip->test, regs + SPI_TEST); - writel(chip->period, regs + SPI_PERIOD); - writel(0, regs + SPI_INT_STATUS); - writel(chip->control, regs + SPI_CONTROL); + return max; } -static void null_cs_control(u32 command) +/* MX1, MX31, MX35 */ +static unsigned int spi_imx_clkdiv_2(unsigned int fin, + unsigned int fspi) { -} + int i, div = 4; -static inline u32 data_to_write(struct driver_data *drv_data) -{ - return ((u32)(drv_data->tx_end - drv_data->tx)) / drv_data->n_bytes; -} + for (i = 0; i < 7; i++) { + if (fspi * div >= fin) + return i; + div <<= 1; + } -static inline u32 data_to_read(struct driver_data *drv_data) -{ - return ((u32)(drv_data->rx_end - drv_data->rx)) / drv_data->n_bytes; + return 7; } -static int write(struct driver_data *drv_data) -{ - void __iomem *regs = drv_data->regs; - void *tx = drv_data->tx; - void *tx_end = drv_data->tx_end; - u8 n_bytes = drv_data->n_bytes; - u32 remaining_writes; - u32 fifo_avail_space; - u32 n; - u16 d; - - /* Compute how many fifo writes to do */ - remaining_writes = (u32)(tx_end - tx) / n_bytes; - fifo_avail_space = SPI_FIFO_DEPTH - - (readl(regs + SPI_TEST) & SPI_TEST_TXCNT); - if (drv_data->rx && (fifo_avail_space > SPI_FIFO_OVERFLOW_MARGIN)) - /* Fix misunderstood receive overflow */ - fifo_avail_space -= SPI_FIFO_OVERFLOW_MARGIN; - n = min(remaining_writes, fifo_avail_space); - - dev_dbg(&drv_data->pdev->dev, - "write type %s\n" - " remaining writes = %d\n" - " fifo avail space = %d\n" - " fifo writes = %d\n", - (n_bytes == 1) ? "u8" : "u16", - remaining_writes, - fifo_avail_space, - n); - - if (n > 0) { - /* Fill SPI TXFIFO */ - if (drv_data->rd_only) { - tx += n * n_bytes; - while (n--) - writel(SPI_DUMMY_u16, regs + SPI_TXDATA); - } else { - if (n_bytes == 1) { - while (n--) { - d = *(u8*)tx; - writel(d, regs + SPI_TXDATA); - tx += 1; - } - } else { - while (n--) { - d = *(u16*)tx; - writel(d, regs + SPI_TXDATA); - tx += 2; - } - } - } - - /* Trigger transfer */ - writel(readl(regs + SPI_CONTROL) | SPI_CONTROL_XCH, - regs + SPI_CONTROL); +#define MX31_INTREG_TEEN (1 << 0) +#define MX31_INTREG_RREN (1 << 3) - /* Update tx pointer */ - drv_data->tx = tx; - } +#define MX31_CSPICTRL_ENABLE (1 << 0) +#define MX31_CSPICTRL_MASTER (1 << 1) +#define MX31_CSPICTRL_XCH (1 << 2) +#define MX31_CSPICTRL_POL (1 << 4) +#define MX31_CSPICTRL_PHA (1 << 5) +#define MX31_CSPICTRL_SSCTL (1 << 6) +#define MX31_CSPICTRL_SSPOL (1 << 7) +#define MX31_CSPICTRL_BC_SHIFT 8 +#define MX35_CSPICTRL_BL_SHIFT 20 +#define MX31_CSPICTRL_CS_SHIFT 24 +#define MX35_CSPICTRL_CS_SHIFT 12 +#define MX31_CSPICTRL_DR_SHIFT 16 - return (tx >= tx_end); -} +#define MX31_CSPISTATUS 0x14 +#define MX31_STATUS_RR (1 << 3) -static int read(struct driver_data *drv_data) +/* These functions also work for the i.MX35, but be aware that + * the i.MX35 has a slightly different register layout for bits + * we do not use here. + */ +static void mx31_intctrl(struct spi_imx_data *spi_imx, int enable) { - void __iomem *regs = drv_data->regs; - void *rx = drv_data->rx; - void *rx_end = drv_data->rx_end; - u8 n_bytes = drv_data->n_bytes; - u32 remaining_reads; - u32 fifo_rxcnt; - u32 n; - u16 d; - - /* Compute how many fifo reads to do */ - remaining_reads = (u32)(rx_end - rx) / n_bytes; - fifo_rxcnt = (readl(regs + SPI_TEST) & SPI_TEST_RXCNT) >> - SPI_TEST_RXCNT_LSB; - n = min(remaining_reads, fifo_rxcnt); - - dev_dbg(&drv_data->pdev->dev, - "read type %s\n" - " remaining reads = %d\n" - " fifo rx count = %d\n" - " fifo reads = %d\n", - (n_bytes == 1) ? "u8" : "u16", - remaining_reads, - fifo_rxcnt, - n); - - if (n > 0) { - /* Read SPI RXFIFO */ - if (n_bytes == 1) { - while (n--) { - d = readl(regs + SPI_RXDATA); - *((u8*)rx) = d; - rx += 1; - } - } else { - while (n--) { - d = readl(regs + SPI_RXDATA); - *((u16*)rx) = d; - rx += 2; - } - } + unsigned int val = 0; - /* Update rx pointer */ - drv_data->rx = rx; - } + if (enable & MXC_INT_TE) + val |= MX31_INTREG_TEEN; + if (enable & MXC_INT_RR) + val |= MX31_INTREG_RREN; - return (rx >= rx_end); + writel(val, spi_imx->base + MXC_CSPIINT); } -static void *next_transfer(struct driver_data *drv_data) +static void mx31_trigger(struct spi_imx_data *spi_imx) { - struct spi_message *msg = drv_data->cur_msg; - struct spi_transfer *trans = drv_data->cur_transfer; - - /* Move to next transfer */ - if (trans->transfer_list.next != &msg->transfers) { - drv_data->cur_transfer = - list_entry(trans->transfer_list.next, - struct spi_transfer, - transfer_list); - return RUNNING_STATE; - } + unsigned int reg; - return DONE_STATE; + reg = readl(spi_imx->base + MXC_CSPICTRL); + reg |= MX31_CSPICTRL_XCH; + writel(reg, spi_imx->base + MXC_CSPICTRL); } -static int map_dma_buffers(struct driver_data *drv_data) +static int mx31_config(struct spi_imx_data *spi_imx, + struct spi_imx_config *config) { - struct spi_message *msg; - struct device *dev; - void *buf; - - drv_data->rx_dma_needs_unmap = 0; - drv_data->tx_dma_needs_unmap = 0; - - if (!drv_data->master_info->enable_dma || - !drv_data->cur_chip->enable_dma) - return -1; - - msg = drv_data->cur_msg; - dev = &msg->spi->dev; - if (msg->is_dma_mapped) { - if (drv_data->tx_dma) - /* The caller provided at least dma and cpu virtual - address for write; pump_transfers() will consider the - transfer as write only if cpu rx virtual address is - NULL */ - return 0; - - if (drv_data->rx_dma) { - /* The caller provided dma and cpu virtual address to - performe read only transfer --> - use drv_data->dummy_dma_buf for dummy writes to - achive reads */ - buf = &drv_data->dummy_dma_buf; - drv_data->tx_map_len = sizeof(drv_data->dummy_dma_buf); - drv_data->tx_dma = dma_map_single(dev, - buf, - drv_data->tx_map_len, - DMA_TO_DEVICE); - if (dma_mapping_error(dev, drv_data->tx_dma)) - return -1; - - drv_data->tx_dma_needs_unmap = 1; - - /* Flags transfer as rd_only for pump_transfers() DMA - regs programming (should be redundant) */ - drv_data->tx = NULL; - - return 0; - } - } + unsigned int reg = MX31_CSPICTRL_ENABLE | MX31_CSPICTRL_MASTER; - if (!IS_DMA_ALIGNED(drv_data->rx) || !IS_DMA_ALIGNED(drv_data->tx)) - return -1; - - if (drv_data->tx == NULL) { - /* Read only message --> use drv_data->dummy_dma_buf for dummy - writes to achive reads */ - buf = &drv_data->dummy_dma_buf; - drv_data->tx_map_len = sizeof(drv_data->dummy_dma_buf); - } else { - buf = drv_data->tx; - drv_data->tx_map_len = drv_data->len; + reg |= spi_imx_clkdiv_2(spi_imx->spi_clk, config->speed_hz) << + MX31_CSPICTRL_DR_SHIFT; + + if (cpu_is_mx31()) + reg |= (config->bpw - 1) << MX31_CSPICTRL_BC_SHIFT; + else if (cpu_is_mx35()) { + reg |= (config->bpw - 1) << MX35_CSPICTRL_BL_SHIFT; + reg |= MX31_CSPICTRL_SSCTL; } - drv_data->tx_dma = dma_map_single(dev, - buf, - drv_data->tx_map_len, - DMA_TO_DEVICE); - if (dma_mapping_error(dev, drv_data->tx_dma)) - return -1; - drv_data->tx_dma_needs_unmap = 1; - - /* NULL rx means write-only transfer and no map needed - * since rx DMA will not be used */ - if (drv_data->rx) { - buf = drv_data->rx; - drv_data->rx_dma = dma_map_single(dev, - buf, - drv_data->len, - DMA_FROM_DEVICE); - if (dma_mapping_error(dev, drv_data->rx_dma)) { - if (drv_data->tx_dma) { - dma_unmap_single(dev, - drv_data->tx_dma, - drv_data->tx_map_len, - DMA_TO_DEVICE); - drv_data->tx_dma_needs_unmap = 0; - } - return -1; - } - drv_data->rx_dma_needs_unmap = 1; + + if (config->mode & SPI_CPHA) + reg |= MX31_CSPICTRL_PHA; + if (config->mode & SPI_CPOL) + reg |= MX31_CSPICTRL_POL; + if (config->mode & SPI_CS_HIGH) + reg |= MX31_CSPICTRL_SSPOL; + if (config->cs < 0) { + if (cpu_is_mx31()) + reg |= (config->cs + 32) << MX31_CSPICTRL_CS_SHIFT; + else if (cpu_is_mx35()) + reg |= (config->cs + 32) << MX35_CSPICTRL_CS_SHIFT; } + writel(reg, spi_imx->base + MXC_CSPICTRL); + return 0; } -static void unmap_dma_buffers(struct driver_data *drv_data) +static int mx31_rx_available(struct spi_imx_data *spi_imx) { - struct spi_message *msg = drv_data->cur_msg; - struct device *dev = &msg->spi->dev; - - if (drv_data->rx_dma_needs_unmap) { - dma_unmap_single(dev, - drv_data->rx_dma, - drv_data->len, - DMA_FROM_DEVICE); - drv_data->rx_dma_needs_unmap = 0; - } - if (drv_data->tx_dma_needs_unmap) { - dma_unmap_single(dev, - drv_data->tx_dma, - drv_data->tx_map_len, - DMA_TO_DEVICE); - drv_data->tx_dma_needs_unmap = 0; - } + return readl(spi_imx->base + MX31_CSPISTATUS) & MX31_STATUS_RR; } -/* Caller already set message->status (dma is already blocked) */ -static void giveback(struct spi_message *message, struct driver_data *drv_data) -{ - void __iomem *regs = drv_data->regs; - - /* Bring SPI to sleep; restore_state() and pump_transfer() - will do new setup */ - writel(0, regs + SPI_INT_STATUS); - writel(0, regs + SPI_DMA); - - /* Unconditioned deselct */ - drv_data->cs_control(SPI_CS_DEASSERT); +#define MX27_INTREG_RR (1 << 4) +#define MX27_INTREG_TEEN (1 << 9) +#define MX27_INTREG_RREN (1 << 13) - message->state = NULL; - if (message->complete) - message->complete(message->context); +#define MX27_CSPICTRL_POL (1 << 5) +#define MX27_CSPICTRL_PHA (1 << 6) +#define MX27_CSPICTRL_SSPOL (1 << 8) +#define MX27_CSPICTRL_XCH (1 << 9) +#define MX27_CSPICTRL_ENABLE (1 << 10) +#define MX27_CSPICTRL_MASTER (1 << 11) +#define MX27_CSPICTRL_DR_SHIFT 14 +#define MX27_CSPICTRL_CS_SHIFT 19 - drv_data->cur_msg = NULL; - drv_data->cur_transfer = NULL; - drv_data->cur_chip = NULL; - queue_work(drv_data->workqueue, &drv_data->work); -} - -static void dma_err_handler(int channel, void *data, int errcode) +static void mx27_intctrl(struct spi_imx_data *spi_imx, int enable) { - struct driver_data *drv_data = data; - struct spi_message *msg = drv_data->cur_msg; - - dev_dbg(&drv_data->pdev->dev, "dma_err_handler\n"); - - /* Disable both rx and tx dma channels */ - imx_dma_disable(drv_data->rx_channel); - imx_dma_disable(drv_data->tx_channel); - unmap_dma_buffers(drv_data); + unsigned int val = 0; - flush(drv_data); + if (enable & MXC_INT_TE) + val |= MX27_INTREG_TEEN; + if (enable & MXC_INT_RR) + val |= MX27_INTREG_RREN; - msg->state = ERROR_STATE; - tasklet_schedule(&drv_data->pump_transfers); + writel(val, spi_imx->base + MXC_CSPIINT); } -static void dma_tx_handler(int channel, void *data) +static void mx27_trigger(struct spi_imx_data *spi_imx) { - struct driver_data *drv_data = data; + unsigned int reg; - dev_dbg(&drv_data->pdev->dev, "dma_tx_handler\n"); - - imx_dma_disable(channel); - - /* Now waits for TX FIFO empty */ - writel(SPI_INTEN_TE, drv_data->regs + SPI_INT_STATUS); + reg = readl(spi_imx->base + MXC_CSPICTRL); + reg |= MX27_CSPICTRL_XCH; + writel(reg, spi_imx->base + MXC_CSPICTRL); } -static irqreturn_t dma_transfer(struct driver_data *drv_data) +static int mx27_config(struct spi_imx_data *spi_imx, + struct spi_imx_config *config) { - u32 status; - struct spi_message *msg = drv_data->cur_msg; - void __iomem *regs = drv_data->regs; - - status = readl(regs + SPI_INT_STATUS); + unsigned int reg = MX27_CSPICTRL_ENABLE | MX27_CSPICTRL_MASTER; - if ((status & (SPI_INTEN_RO | SPI_STATUS_RO)) - == (SPI_INTEN_RO | SPI_STATUS_RO)) { - writel(status & ~SPI_INTEN, regs + SPI_INT_STATUS); + reg |= spi_imx_clkdiv_1(spi_imx->spi_clk, config->speed_hz) << + MX27_CSPICTRL_DR_SHIFT; + reg |= config->bpw - 1; - imx_dma_disable(drv_data->tx_channel); - imx_dma_disable(drv_data->rx_channel); - unmap_dma_buffers(drv_data); + if (config->mode & SPI_CPHA) + reg |= MX27_CSPICTRL_PHA; + if (config->mode & SPI_CPOL) + reg |= MX27_CSPICTRL_POL; + if (config->mode & SPI_CS_HIGH) + reg |= MX27_CSPICTRL_SSPOL; + if (config->cs < 0) + reg |= (config->cs + 32) << MX27_CSPICTRL_CS_SHIFT; - flush(drv_data); + writel(reg, spi_imx->base + MXC_CSPICTRL); - dev_warn(&drv_data->pdev->dev, - "dma_transfer - fifo overun\n"); - - msg->state = ERROR_STATE; - tasklet_schedule(&drv_data->pump_transfers); - - return IRQ_HANDLED; - } - - if (status & SPI_STATUS_TE) { - writel(status & ~SPI_INTEN_TE, regs + SPI_INT_STATUS); - - if (drv_data->rx) { - /* Wait end of transfer before read trailing data */ - while (readl(regs + SPI_CONTROL) & SPI_CONTROL_XCH) - cpu_relax(); - - imx_dma_disable(drv_data->rx_channel); - unmap_dma_buffers(drv_data); - - /* Release chip select if requested, transfer delays are - handled in pump_transfers() */ - if (drv_data->cs_change) - drv_data->cs_control(SPI_CS_DEASSERT); - - /* Calculate number of trailing data and read them */ - dev_dbg(&drv_data->pdev->dev, - "dma_transfer - test = 0x%08X\n", - readl(regs + SPI_TEST)); - drv_data->rx = drv_data->rx_end - - ((readl(regs + SPI_TEST) & - SPI_TEST_RXCNT) >> - SPI_TEST_RXCNT_LSB)*drv_data->n_bytes; - read(drv_data); - } else { - /* Write only transfer */ - unmap_dma_buffers(drv_data); - - flush(drv_data); - } - - /* End of transfer, update total byte transfered */ - msg->actual_length += drv_data->len; - - /* Move to next transfer */ - msg->state = next_transfer(drv_data); - - /* Schedule transfer tasklet */ - tasklet_schedule(&drv_data->pump_transfers); - - return IRQ_HANDLED; - } - - /* Opps problem detected */ - return IRQ_NONE; + return 0; } -static irqreturn_t interrupt_wronly_transfer(struct driver_data *drv_data) +static int mx27_rx_available(struct spi_imx_data *spi_imx) { - struct spi_message *msg = drv_data->cur_msg; - void __iomem *regs = drv_data->regs; - u32 status; - irqreturn_t handled = IRQ_NONE; - - status = readl(regs + SPI_INT_STATUS); - - if (status & SPI_INTEN_TE) { - /* TXFIFO Empty Interrupt on the last transfered word */ - writel(status & ~SPI_INTEN, regs + SPI_INT_STATUS); - dev_dbg(&drv_data->pdev->dev, - "interrupt_wronly_transfer - end of tx\n"); - - flush(drv_data); + return readl(spi_imx->base + MXC_CSPIINT) & MX27_INTREG_RR; +} - /* Update total byte transfered */ - msg->actual_length += drv_data->len; +#define MX1_INTREG_RR (1 << 3) +#define MX1_INTREG_TEEN (1 << 8) +#define MX1_INTREG_RREN (1 << 11) - /* Move to next transfer */ - msg->state = next_transfer(drv_data); +#define MX1_CSPICTRL_POL (1 << 4) +#define MX1_CSPICTRL_PHA (1 << 5) +#define MX1_CSPICTRL_XCH (1 << 8) +#define MX1_CSPICTRL_ENABLE (1 << 9) +#define MX1_CSPICTRL_MASTER (1 << 10) +#define MX1_CSPICTRL_DR_SHIFT 13 - /* Schedule transfer tasklet */ - tasklet_schedule(&drv_data->pump_transfers); +static void mx1_intctrl(struct spi_imx_data *spi_imx, int enable) +{ + unsigned int val = 0; - return IRQ_HANDLED; - } else { - while (status & SPI_STATUS_TH) { - dev_dbg(&drv_data->pdev->dev, - "interrupt_wronly_transfer - status = 0x%08X\n", - status); - - /* Pump data */ - if (write(drv_data)) { - /* End of TXFIFO writes, - now wait until TXFIFO is empty */ - writel(SPI_INTEN_TE, regs + SPI_INT_STATUS); - return IRQ_HANDLED; - } - - status = readl(regs + SPI_INT_STATUS); - - /* We did something */ - handled = IRQ_HANDLED; - } - } + if (enable & MXC_INT_TE) + val |= MX1_INTREG_TEEN; + if (enable & MXC_INT_RR) + val |= MX1_INTREG_RREN; - return handled; + writel(val, spi_imx->base + MXC_CSPIINT); } -static irqreturn_t interrupt_transfer(struct driver_data *drv_data) +static void mx1_trigger(struct spi_imx_data *spi_imx) { - struct spi_message *msg = drv_data->cur_msg; - void __iomem *regs = drv_data->regs; - u32 status, control; - irqreturn_t handled = IRQ_NONE; - unsigned long limit; - - status = readl(regs + SPI_INT_STATUS); - - if (status & SPI_INTEN_TE) { - /* TXFIFO Empty Interrupt on the last transfered word */ - writel(status & ~SPI_INTEN, regs + SPI_INT_STATUS); - dev_dbg(&drv_data->pdev->dev, - "interrupt_transfer - end of tx\n"); - - if (msg->state == ERROR_STATE) { - /* RXFIFO overrun was detected and message aborted */ - flush(drv_data); - } else { - /* Wait for end of transaction */ - do { - control = readl(regs + SPI_CONTROL); - } while (control & SPI_CONTROL_XCH); - - /* Release chip select if requested, transfer delays are - handled in pump_transfers */ - if (drv_data->cs_change) - drv_data->cs_control(SPI_CS_DEASSERT); - - /* Read trailing bytes */ - limit = loops_per_jiffy << 1; - while ((read(drv_data) == 0) && --limit) - cpu_relax(); - - if (limit == 0) - dev_err(&drv_data->pdev->dev, - "interrupt_transfer - " - "trailing byte read failed\n"); - else - dev_dbg(&drv_data->pdev->dev, - "interrupt_transfer - end of rx\n"); - - /* Update total byte transfered */ - msg->actual_length += drv_data->len; - - /* Move to next transfer */ - msg->state = next_transfer(drv_data); - } - - /* Schedule transfer tasklet */ - tasklet_schedule(&drv_data->pump_transfers); + unsigned int reg; - return IRQ_HANDLED; - } else { - while (status & (SPI_STATUS_TH | SPI_STATUS_RO)) { - dev_dbg(&drv_data->pdev->dev, - "interrupt_transfer - status = 0x%08X\n", - status); - - if (status & SPI_STATUS_RO) { - /* RXFIFO overrun, abort message end wait - until TXFIFO is empty */ - writel(SPI_INTEN_TE, regs + SPI_INT_STATUS); - - dev_warn(&drv_data->pdev->dev, - "interrupt_transfer - fifo overun\n" - " data not yet written = %d\n" - " data not yet read = %d\n", - data_to_write(drv_data), - data_to_read(drv_data)); - - msg->state = ERROR_STATE; - - return IRQ_HANDLED; - } - - /* Pump data */ - read(drv_data); - if (write(drv_data)) { - /* End of TXFIFO writes, - now wait until TXFIFO is empty */ - writel(SPI_INTEN_TE, regs + SPI_INT_STATUS); - return IRQ_HANDLED; - } - - status = readl(regs + SPI_INT_STATUS); - - /* We did something */ - handled = IRQ_HANDLED; - } - } - - return handled; + reg = readl(spi_imx->base + MXC_CSPICTRL); + reg |= MX1_CSPICTRL_XCH; + writel(reg, spi_imx->base + MXC_CSPICTRL); } -static irqreturn_t spi_int(int irq, void *dev_id) +static int mx1_config(struct spi_imx_data *spi_imx, + struct spi_imx_config *config) { - struct driver_data *drv_data = (struct driver_data *)dev_id; + unsigned int reg = MX1_CSPICTRL_ENABLE | MX1_CSPICTRL_MASTER; - if (!drv_data->cur_msg) { - dev_err(&drv_data->pdev->dev, - "spi_int - bad message state\n"); - /* Never fail */ - return IRQ_HANDLED; - } + reg |= spi_imx_clkdiv_2(spi_imx->spi_clk, config->speed_hz) << + MX1_CSPICTRL_DR_SHIFT; + reg |= config->bpw - 1; - return drv_data->transfer_handler(drv_data); -} + if (config->mode & SPI_CPHA) + reg |= MX1_CSPICTRL_PHA; + if (config->mode & SPI_CPOL) + reg |= MX1_CSPICTRL_POL; -static inline u32 spi_speed_hz(struct driver_data *drv_data, u32 data_rate) -{ - return clk_get_rate(drv_data->clk) / (4 << ((data_rate) >> 13)); + writel(reg, spi_imx->base + MXC_CSPICTRL); + + return 0; } -static u32 spi_data_rate(struct driver_data *drv_data, u32 speed_hz) +static int mx1_rx_available(struct spi_imx_data *spi_imx) { - u32 div; - u32 quantized_hz = clk_get_rate(drv_data->clk) >> 2; - - for (div = SPI_PERCLK2_DIV_MIN; - div <= SPI_PERCLK2_DIV_MAX; - div++, quantized_hz >>= 1) { - if (quantized_hz <= speed_hz) - /* Max available speed LEQ required speed */ - return div << 13; - } - return SPI_CONTROL_DATARATE_BAD; + return readl(spi_imx->base + MXC_CSPIINT) & MX1_INTREG_RR; } -static void pump_transfers(unsigned long data) +static void spi_imx_chipselect(struct spi_device *spi, int is_active) { - struct driver_data *drv_data = (struct driver_data *)data; - struct spi_message *message; - struct spi_transfer *transfer, *previous; - struct chip_data *chip; - void __iomem *regs; - u32 tmp, control; - - dev_dbg(&drv_data->pdev->dev, "pump_transfer\n"); + struct spi_imx_data *spi_imx = spi_master_get_devdata(spi->master); + int gpio = spi_imx->chipselect[spi->chip_select]; + int active = is_active != BITBANG_CS_INACTIVE; + int dev_is_lowactive = !(spi->mode & SPI_CS_HIGH); - message = drv_data->cur_msg; - - /* Handle for abort */ - if (message->state == ERROR_STATE) { - message->status = -EIO; - giveback(message, drv_data); - return; - } - - /* Handle end of message */ - if (message->state == DONE_STATE) { - message->status = 0; - giveback(message, drv_data); + if (gpio < 0) return; - } - - chip = drv_data->cur_chip; - - /* Delay if requested at end of transfer*/ - transfer = drv_data->cur_transfer; - if (message->state == RUNNING_STATE) { - previous = list_entry(transfer->transfer_list.prev, - struct spi_transfer, - transfer_list); - if (previous->delay_usecs) - udelay(previous->delay_usecs); - } else { - /* START_STATE */ - message->state = RUNNING_STATE; - drv_data->cs_control = chip->cs_control; - } - transfer = drv_data->cur_transfer; - drv_data->tx = (void *)transfer->tx_buf; - drv_data->tx_end = drv_data->tx + transfer->len; - drv_data->rx = transfer->rx_buf; - drv_data->rx_end = drv_data->rx + transfer->len; - drv_data->rx_dma = transfer->rx_dma; - drv_data->tx_dma = transfer->tx_dma; - drv_data->len = transfer->len; - drv_data->cs_change = transfer->cs_change; - drv_data->rd_only = (drv_data->tx == NULL); - - regs = drv_data->regs; - control = readl(regs + SPI_CONTROL); - - /* Bits per word setup */ - tmp = transfer->bits_per_word; - if (tmp == 0) { - /* Use device setup */ - tmp = chip->bits_per_word; - drv_data->n_bytes = chip->n_bytes; - } else - /* Use per-transfer setup */ - drv_data->n_bytes = (tmp <= 8) ? 1 : 2; - u32_EDIT(control, SPI_CONTROL_BITCOUNT_MASK, tmp - 1); - - /* Speed setup (surely valid because already checked) */ - tmp = transfer->speed_hz; - if (tmp == 0) - tmp = chip->max_speed_hz; - tmp = spi_data_rate(drv_data, tmp); - u32_EDIT(control, SPI_CONTROL_DATARATE, tmp); - - writel(control, regs + SPI_CONTROL); - - /* Assert device chip-select */ - drv_data->cs_control(SPI_CS_ASSERT); - - /* DMA cannot read/write SPI FIFOs other than 16 bits at a time; hence - if bits_per_word is less or equal 8 PIO transfers are performed. - Moreover DMA is convinient for transfer length bigger than FIFOs - byte size. */ - if ((drv_data->n_bytes == 2) && - (drv_data->len > SPI_FIFO_DEPTH*SPI_FIFO_BYTE_WIDTH) && - (map_dma_buffers(drv_data) == 0)) { - dev_dbg(&drv_data->pdev->dev, - "pump dma transfer\n" - " tx = %p\n" - " tx_dma = %08X\n" - " rx = %p\n" - " rx_dma = %08X\n" - " len = %d\n", - drv_data->tx, - (unsigned int)drv_data->tx_dma, - drv_data->rx, - (unsigned int)drv_data->rx_dma, - drv_data->len); - - /* Ensure we have the correct interrupt handler */ - drv_data->transfer_handler = dma_transfer; - - /* Trigger transfer */ - writel(readl(regs + SPI_CONTROL) | SPI_CONTROL_XCH, - regs + SPI_CONTROL); - - /* Setup tx DMA */ - if (drv_data->tx) - /* Linear source address */ - CCR(drv_data->tx_channel) = - CCR_DMOD_FIFO | - CCR_SMOD_LINEAR | - CCR_SSIZ_32 | CCR_DSIZ_16 | - CCR_REN; - else - /* Read only transfer -> fixed source address for - dummy write to achive read */ - CCR(drv_data->tx_channel) = - CCR_DMOD_FIFO | - CCR_SMOD_FIFO | - CCR_SSIZ_32 | CCR_DSIZ_16 | - CCR_REN; - - imx_dma_setup_single( - drv_data->tx_channel, - drv_data->tx_dma, - drv_data->len, - drv_data->rd_data_phys + 4, - DMA_MODE_WRITE); - - if (drv_data->rx) { - /* Setup rx DMA for linear destination address */ - CCR(drv_data->rx_channel) = - CCR_DMOD_LINEAR | - CCR_SMOD_FIFO | - CCR_DSIZ_32 | CCR_SSIZ_16 | - CCR_REN; - imx_dma_setup_single( - drv_data->rx_channel, - drv_data->rx_dma, - drv_data->len, - drv_data->rd_data_phys, - DMA_MODE_READ); - imx_dma_enable(drv_data->rx_channel); - - /* Enable SPI interrupt */ - writel(SPI_INTEN_RO, regs + SPI_INT_STATUS); - - /* Set SPI to request DMA service on both - Rx and Tx half fifo watermark */ - writel(SPI_DMA_RHDEN | SPI_DMA_THDEN, regs + SPI_DMA); - } else - /* Write only access -> set SPI to request DMA - service on Tx half fifo watermark */ - writel(SPI_DMA_THDEN, regs + SPI_DMA); - - imx_dma_enable(drv_data->tx_channel); - } else { - dev_dbg(&drv_data->pdev->dev, - "pump pio transfer\n" - " tx = %p\n" - " rx = %p\n" - " len = %d\n", - drv_data->tx, - drv_data->rx, - drv_data->len); - - /* Ensure we have the correct interrupt handler */ - if (drv_data->rx) - drv_data->transfer_handler = interrupt_transfer; - else - drv_data->transfer_handler = interrupt_wronly_transfer; - - /* Enable SPI interrupt */ - if (drv_data->rx) - writel(SPI_INTEN_TH | SPI_INTEN_RO, - regs + SPI_INT_STATUS); - else - writel(SPI_INTEN_TH, regs + SPI_INT_STATUS); - } + gpio_set_value(gpio, dev_is_lowactive ^ active); } -static void pump_messages(struct work_struct *work) +static void spi_imx_push(struct spi_imx_data *spi_imx) { - struct driver_data *drv_data = - container_of(work, struct driver_data, work); - unsigned long flags; - - /* Lock queue and check for queue work */ - spin_lock_irqsave(&drv_data->lock, flags); - if (list_empty(&drv_data->queue) || drv_data->run == QUEUE_STOPPED) { - drv_data->busy = 0; - spin_unlock_irqrestore(&drv_data->lock, flags); - return; - } - - /* Make sure we are not already running a message */ - if (drv_data->cur_msg) { - spin_unlock_irqrestore(&drv_data->lock, flags); - return; + while (spi_imx->txfifo < 8) { + if (!spi_imx->count) + break; + spi_imx->tx(spi_imx); + spi_imx->txfifo++; } - /* Extract head of queue */ - drv_data->cur_msg = list_entry(drv_data->queue.next, - struct spi_message, queue); - list_del_init(&drv_data->cur_msg->queue); - drv_data->busy = 1; - spin_unlock_irqrestore(&drv_data->lock, flags); - - /* Initial message state */ - drv_data->cur_msg->state = START_STATE; - drv_data->cur_transfer = list_entry(drv_data->cur_msg->transfers.next, - struct spi_transfer, - transfer_list); - - /* Setup the SPI using the per chip configuration */ - drv_data->cur_chip = spi_get_ctldata(drv_data->cur_msg->spi); - restore_state(drv_data); - - /* Mark as busy and launch transfers */ - tasklet_schedule(&drv_data->pump_transfers); + spi_imx->trigger(spi_imx); } -static int transfer(struct spi_device *spi, struct spi_message *msg) +static irqreturn_t spi_imx_isr(int irq, void *dev_id) { - struct driver_data *drv_data = spi_master_get_devdata(spi->master); - u32 min_speed_hz, max_speed_hz, tmp; - struct spi_transfer *trans; - unsigned long flags; - - msg->actual_length = 0; - - /* Per transfer setup check */ - min_speed_hz = spi_speed_hz(drv_data, SPI_CONTROL_DATARATE_MIN); - max_speed_hz = spi->max_speed_hz; - list_for_each_entry(trans, &msg->transfers, transfer_list) { - tmp = trans->bits_per_word; - if (tmp > 16) { - dev_err(&drv_data->pdev->dev, - "message rejected : " - "invalid transfer bits_per_word (%d bits)\n", - tmp); - goto msg_rejected; - } - tmp = trans->speed_hz; - if (tmp) { - if (tmp < min_speed_hz) { - dev_err(&drv_data->pdev->dev, - "message rejected : " - "device min speed (%d Hz) exceeds " - "required transfer speed (%d Hz)\n", - min_speed_hz, - tmp); - goto msg_rejected; - } else if (tmp > max_speed_hz) { - dev_err(&drv_data->pdev->dev, - "message rejected : " - "transfer speed (%d Hz) exceeds " - "device max speed (%d Hz)\n", - tmp, - max_speed_hz); - goto msg_rejected; - } - } - } - - /* Message accepted */ - msg->status = -EINPROGRESS; - msg->state = START_STATE; + struct spi_imx_data *spi_imx = dev_id; - spin_lock_irqsave(&drv_data->lock, flags); - if (drv_data->run == QUEUE_STOPPED) { - spin_unlock_irqrestore(&drv_data->lock, flags); - return -ESHUTDOWN; + while (spi_imx->rx_available(spi_imx)) { + spi_imx->rx(spi_imx); + spi_imx->txfifo--; } - list_add_tail(&msg->queue, &drv_data->queue); - if (drv_data->run == QUEUE_RUNNING && !drv_data->busy) - queue_work(drv_data->workqueue, &drv_data->work); - - spin_unlock_irqrestore(&drv_data->lock, flags); - return 0; - -msg_rejected: - /* Message rejected and not queued */ - msg->status = -EINVAL; - msg->state = ERROR_STATE; - if (msg->complete) - msg->complete(msg->context); - return -EINVAL; -} - -/* On first setup bad values must free chip_data memory since will cause - spi_new_device to fail. Bad value setup from protocol driver are simply not - applied and notified to the calling driver. */ -static int setup(struct spi_device *spi) -{ - struct driver_data *drv_data = spi_master_get_devdata(spi->master); - struct spi_imx_chip *chip_info; - struct chip_data *chip; - int first_setup = 0; - u32 tmp; - int status = 0; - - /* Get controller data */ - chip_info = spi->controller_data; - - /* Get controller_state */ - chip = spi_get_ctldata(spi); - if (chip == NULL) { - first_setup = 1; - - chip = kzalloc(sizeof(struct chip_data), GFP_KERNEL); - if (!chip) { - dev_err(&spi->dev, - "setup - cannot allocate controller state\n"); - return -ENOMEM; - } - chip->control = SPI_DEFAULT_CONTROL; - - if (chip_info == NULL) { - /* spi_board_info.controller_data not is supplied */ - chip_info = kzalloc(sizeof(struct spi_imx_chip), - GFP_KERNEL); - if (!chip_info) { - dev_err(&spi->dev, - "setup - " - "cannot allocate controller data\n"); - status = -ENOMEM; - goto err_first_setup; - } - /* Set controller data default value */ - chip_info->enable_loopback = - SPI_DEFAULT_ENABLE_LOOPBACK; - chip_info->enable_dma = SPI_DEFAULT_ENABLE_DMA; - chip_info->ins_ss_pulse = 1; - chip_info->bclk_wait = SPI_DEFAULT_PERIOD_WAIT; - chip_info->cs_control = null_cs_control; - } + if (spi_imx->count) { + spi_imx_push(spi_imx); + return IRQ_HANDLED; } - /* Now set controller state based on controller data */ - - if (first_setup) { - /* SPI loopback */ - if (chip_info->enable_loopback) - chip->test = SPI_TEST_LBC; - else - chip->test = 0; - - /* SPI dma driven */ - chip->enable_dma = chip_info->enable_dma; - - /* SPI /SS pulse between spi burst */ - if (chip_info->ins_ss_pulse) - u32_EDIT(chip->control, - SPI_CONTROL_SSCTL, SPI_CONTROL_SSCTL_1); - else - u32_EDIT(chip->control, - SPI_CONTROL_SSCTL, SPI_CONTROL_SSCTL_0); - - /* SPI bclk waits between each bits_per_word spi burst */ - if (chip_info->bclk_wait > SPI_PERIOD_MAX_WAIT) { - dev_err(&spi->dev, - "setup - " - "bclk_wait exceeds max allowed (%d)\n", - SPI_PERIOD_MAX_WAIT); - goto err_first_setup; - } - chip->period = SPI_PERIOD_CSRC_BCLK | - (chip_info->bclk_wait & SPI_PERIOD_WAIT); + if (spi_imx->txfifo) { + /* No data left to push, but still waiting for rx data, + * enable receive data available interrupt. + */ + spi_imx->intctrl(spi_imx, MXC_INT_RR); + return IRQ_HANDLED; } - /* SPI mode */ - tmp = spi->mode; - if (tmp & SPI_CS_HIGH) { - u32_EDIT(chip->control, - SPI_CONTROL_SSPOL, SPI_CONTROL_SSPOL_ACT_HIGH); - } - switch (tmp & SPI_MODE_3) { - case SPI_MODE_0: - tmp = 0; - break; - case SPI_MODE_1: - tmp = SPI_CONTROL_PHA_1; - break; - case SPI_MODE_2: - tmp = SPI_CONTROL_POL_ACT_LOW; - break; - default: - /* SPI_MODE_3 */ - tmp = SPI_CONTROL_PHA_1 | SPI_CONTROL_POL_ACT_LOW; - break; - } - u32_EDIT(chip->control, SPI_CONTROL_POL | SPI_CONTROL_PHA, tmp); - - /* SPI word width */ - tmp = spi->bits_per_word; - if (tmp > 16) { - status = -EINVAL; - dev_err(&spi->dev, - "setup - " - "invalid bits_per_word (%d)\n", - tmp); - if (first_setup) - goto err_first_setup; - else { - /* Undo setup using chip as backup copy */ - tmp = chip->bits_per_word; - spi->bits_per_word = tmp; - } - } - chip->bits_per_word = tmp; - u32_EDIT(chip->control, SPI_CONTROL_BITCOUNT_MASK, tmp - 1); - chip->n_bytes = (tmp <= 8) ? 1 : 2; - - /* SPI datarate */ - tmp = spi_data_rate(drv_data, spi->max_speed_hz); - if (tmp == SPI_CONTROL_DATARATE_BAD) { - status = -EINVAL; - dev_err(&spi->dev, - "setup - " - "HW min speed (%d Hz) exceeds required " - "max speed (%d Hz)\n", - spi_speed_hz(drv_data, SPI_CONTROL_DATARATE_MIN), - spi->max_speed_hz); - if (first_setup) - goto err_first_setup; - else - /* Undo setup using chip as backup copy */ - spi->max_speed_hz = chip->max_speed_hz; - } else { - u32_EDIT(chip->control, SPI_CONTROL_DATARATE, tmp); - /* Actual rounded max_speed_hz */ - tmp = spi_speed_hz(drv_data, tmp); - spi->max_speed_hz = tmp; - chip->max_speed_hz = tmp; - } + spi_imx->intctrl(spi_imx, 0); + complete(&spi_imx->xfer_done); - /* SPI chip-select management */ - if (chip_info->cs_control) - chip->cs_control = chip_info->cs_control; - else - chip->cs_control = null_cs_control; - - /* Save controller_state */ - spi_set_ctldata(spi, chip); - - /* Summary */ - dev_dbg(&spi->dev, - "setup succeded\n" - " loopback enable = %s\n" - " dma enable = %s\n" - " insert /ss pulse = %s\n" - " period wait = %d\n" - " mode = %d\n" - " bits per word = %d\n" - " min speed = %d Hz\n" - " rounded max speed = %d Hz\n", - chip->test & SPI_TEST_LBC ? "Yes" : "No", - chip->enable_dma ? "Yes" : "No", - chip->control & SPI_CONTROL_SSCTL ? "Yes" : "No", - chip->period & SPI_PERIOD_WAIT, - spi->mode, - spi->bits_per_word, - spi_speed_hz(drv_data, SPI_CONTROL_DATARATE_MIN), - spi->max_speed_hz); - return status; - -err_first_setup: - kfree(chip); - return status; + return IRQ_HANDLED; } -static void cleanup(struct spi_device *spi) +static int spi_imx_setupxfer(struct spi_device *spi, + struct spi_transfer *t) { - kfree(spi_get_ctldata(spi)); -} + struct spi_imx_data *spi_imx = spi_master_get_devdata(spi->master); + struct spi_imx_config config; -static int __init init_queue(struct driver_data *drv_data) -{ - INIT_LIST_HEAD(&drv_data->queue); - spin_lock_init(&drv_data->lock); + config.bpw = t ? t->bits_per_word : spi->bits_per_word; + config.speed_hz = t ? t->speed_hz : spi->max_speed_hz; + config.mode = spi->mode; + config.cs = spi_imx->chipselect[spi->chip_select]; - drv_data->run = QUEUE_STOPPED; - drv_data->busy = 0; + if (!config.speed_hz) + config.speed_hz = spi->max_speed_hz; + if (!config.bpw) + config.bpw = spi->bits_per_word; + if (!config.speed_hz) + config.speed_hz = spi->max_speed_hz; - tasklet_init(&drv_data->pump_transfers, - pump_transfers, (unsigned long)drv_data); + /* Initialize the functions for transfer */ + if (config.bpw <= 8) { + spi_imx->rx = spi_imx_buf_rx_u8; + spi_imx->tx = spi_imx_buf_tx_u8; + } else if (config.bpw <= 16) { + spi_imx->rx = spi_imx_buf_rx_u16; + spi_imx->tx = spi_imx_buf_tx_u16; + } else if (config.bpw <= 32) { + spi_imx->rx = spi_imx_buf_rx_u32; + spi_imx->tx = spi_imx_buf_tx_u32; + } else + BUG(); - INIT_WORK(&drv_data->work, pump_messages); - drv_data->workqueue = create_singlethread_workqueue( - dev_name(drv_data->master->dev.parent)); - if (drv_data->workqueue == NULL) - return -EBUSY; + spi_imx->config(spi_imx, &config); return 0; } -static int start_queue(struct driver_data *drv_data) +static int spi_imx_transfer(struct spi_device *spi, + struct spi_transfer *transfer) { - unsigned long flags; + struct spi_imx_data *spi_imx = spi_master_get_devdata(spi->master); - spin_lock_irqsave(&drv_data->lock, flags); + spi_imx->tx_buf = transfer->tx_buf; + spi_imx->rx_buf = transfer->rx_buf; + spi_imx->count = transfer->len; + spi_imx->txfifo = 0; - if (drv_data->run == QUEUE_RUNNING || drv_data->busy) { - spin_unlock_irqrestore(&drv_data->lock, flags); - return -EBUSY; - } + init_completion(&spi_imx->xfer_done); - drv_data->run = QUEUE_RUNNING; - drv_data->cur_msg = NULL; - drv_data->cur_transfer = NULL; - drv_data->cur_chip = NULL; - spin_unlock_irqrestore(&drv_data->lock, flags); + spi_imx_push(spi_imx); - queue_work(drv_data->workqueue, &drv_data->work); + spi_imx->intctrl(spi_imx, MXC_INT_TE); - return 0; + wait_for_completion(&spi_imx->xfer_done); + + return transfer->len; } -static int stop_queue(struct driver_data *drv_data) +static int spi_imx_setup(struct spi_device *spi) { - unsigned long flags; - unsigned limit = 500; - int status = 0; - - spin_lock_irqsave(&drv_data->lock, flags); - - /* This is a bit lame, but is optimized for the common execution path. - * A wait_queue on the drv_data->busy could be used, but then the common - * execution path (pump_messages) would be required to call wake_up or - * friends on every SPI message. Do this instead */ - drv_data->run = QUEUE_STOPPED; - while (!list_empty(&drv_data->queue) && drv_data->busy && limit--) { - spin_unlock_irqrestore(&drv_data->lock, flags); - msleep(10); - spin_lock_irqsave(&drv_data->lock, flags); - } + struct spi_imx_data *spi_imx = spi_master_get_devdata(spi->master); + int gpio = spi_imx->chipselect[spi->chip_select]; + + pr_debug("%s: mode %d, %u bpw, %d hz\n", __func__, + spi->mode, spi->bits_per_word, spi->max_speed_hz); - if (!list_empty(&drv_data->queue) || drv_data->busy) - status = -EBUSY; + if (gpio >= 0) + gpio_direction_output(gpio, spi->mode & SPI_CS_HIGH ? 0 : 1); - spin_unlock_irqrestore(&drv_data->lock, flags); + spi_imx_chipselect(spi, BITBANG_CS_INACTIVE); - return status; + return 0; } -static int destroy_queue(struct driver_data *drv_data) +static void spi_imx_cleanup(struct spi_device *spi) { - int status; - - status = stop_queue(drv_data); - if (status != 0) - return status; - - if (drv_data->workqueue) - destroy_workqueue(drv_data->workqueue); - - return 0; } static int __init spi_imx_probe(struct platform_device *pdev) { - struct device *dev = &pdev->dev; - struct spi_imx_master *platform_info; + struct spi_imx_master *mxc_platform_info; struct spi_master *master; - struct driver_data *drv_data; + struct spi_imx_data *spi_imx; struct resource *res; - int irq, status = 0; + int i, ret; - platform_info = dev->platform_data; - if (platform_info == NULL) { - dev_err(&pdev->dev, "probe - no platform data supplied\n"); - status = -ENODEV; - goto err_no_pdata; + mxc_platform_info = (struct spi_imx_master *)pdev->dev.platform_data; + if (!mxc_platform_info) { + dev_err(&pdev->dev, "can't get the platform data\n"); + return -EINVAL; } - /* Allocate master with space for drv_data */ - master = spi_alloc_master(dev, sizeof(struct driver_data)); - if (!master) { - dev_err(&pdev->dev, "probe - cannot alloc spi_master\n"); - status = -ENOMEM; - goto err_no_mem; - } - drv_data = spi_master_get_devdata(master); - drv_data->master = master; - drv_data->master_info = platform_info; - drv_data->pdev = pdev; + master = spi_alloc_master(&pdev->dev, sizeof(struct spi_imx_data)); + if (!master) + return -ENOMEM; - /* the spi->mode bits understood by this driver: */ - master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH; + platform_set_drvdata(pdev, master); master->bus_num = pdev->id; - master->num_chipselect = platform_info->num_chipselect; - master->dma_alignment = DMA_ALIGNMENT; - master->cleanup = cleanup; - master->setup = setup; - master->transfer = transfer; - - drv_data->dummy_dma_buf = SPI_DUMMY_u32; - - drv_data->clk = clk_get(&pdev->dev, "perclk2"); - if (IS_ERR(drv_data->clk)) { - dev_err(&pdev->dev, "probe - cannot get clock\n"); - status = PTR_ERR(drv_data->clk); - goto err_no_clk; - } - clk_enable(drv_data->clk); - - /* Find and map resources */ - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) { - dev_err(&pdev->dev, "probe - MEM resources not defined\n"); - status = -ENODEV; - goto err_no_iores; - } - drv_data->ioarea = request_mem_region(res->start, - res->end - res->start + 1, - pdev->name); - if (drv_data->ioarea == NULL) { - dev_err(&pdev->dev, "probe - cannot reserve region\n"); - status = -ENXIO; - goto err_no_iores; - } - drv_data->regs = ioremap(res->start, res->end - res->start + 1); - if (drv_data->regs == NULL) { - dev_err(&pdev->dev, "probe - cannot map IO\n"); - status = -ENXIO; - goto err_no_iomap; - } - drv_data->rd_data_phys = (dma_addr_t)res->start; - - /* Attach to IRQ */ - irq = platform_get_irq(pdev, 0); - if (irq < 0) { - dev_err(&pdev->dev, "probe - IRQ resource not defined\n"); - status = -ENODEV; - goto err_no_irqres; - } - status = request_irq(irq, spi_int, IRQF_DISABLED, - dev_name(dev), drv_data); - if (status < 0) { - dev_err(&pdev->dev, "probe - cannot get IRQ (%d)\n", status); - goto err_no_irqres; - } - - /* Setup DMA if requested */ - drv_data->tx_channel = -1; - drv_data->rx_channel = -1; - if (platform_info->enable_dma) { - /* Get rx DMA channel */ - drv_data->rx_channel = imx_dma_request_by_prio("spi_imx_rx", - DMA_PRIO_HIGH); - if (drv_data->rx_channel < 0) { - dev_err(dev, - "probe - problem (%d) requesting rx channel\n", - drv_data->rx_channel); - goto err_no_rxdma; - } else - imx_dma_setup_handlers(drv_data->rx_channel, NULL, - dma_err_handler, drv_data); - - /* Get tx DMA channel */ - drv_data->tx_channel = imx_dma_request_by_prio("spi_imx_tx", - DMA_PRIO_MEDIUM); - if (drv_data->tx_channel < 0) { - dev_err(dev, - "probe - problem (%d) requesting tx channel\n", - drv_data->tx_channel); - imx_dma_free(drv_data->rx_channel); - goto err_no_txdma; - } else - imx_dma_setup_handlers(drv_data->tx_channel, - dma_tx_handler, dma_err_handler, - drv_data); - - /* Set request source and burst length for allocated channels */ - switch (drv_data->pdev->id) { - case 1: - /* Using SPI1 */ - RSSR(drv_data->rx_channel) = DMA_REQ_SPI1_R; - RSSR(drv_data->tx_channel) = DMA_REQ_SPI1_T; - break; - case 2: - /* Using SPI2 */ - RSSR(drv_data->rx_channel) = DMA_REQ_SPI2_R; - RSSR(drv_data->tx_channel) = DMA_REQ_SPI2_T; - break; - default: - dev_err(dev, "probe - bad SPI Id\n"); - imx_dma_free(drv_data->rx_channel); - imx_dma_free(drv_data->tx_channel); - status = -ENODEV; - goto err_no_devid; + master->num_chipselect = mxc_platform_info->num_chipselect; + + spi_imx = spi_master_get_devdata(master); + spi_imx->bitbang.master = spi_master_get(master); + spi_imx->chipselect = mxc_platform_info->chipselect; + + for (i = 0; i < master->num_chipselect; i++) { + if (spi_imx->chipselect[i] < 0) + continue; + ret = gpio_request(spi_imx->chipselect[i], DRIVER_NAME); + if (ret) { + i--; + while (i > 0) + if (spi_imx->chipselect[i] >= 0) + gpio_free(spi_imx->chipselect[i--]); + dev_err(&pdev->dev, "can't get cs gpios"); + goto out_master_put; } - BLR(drv_data->rx_channel) = SPI_DMA_BLR; - BLR(drv_data->tx_channel) = SPI_DMA_BLR; } - /* Load default SPI configuration */ - writel(SPI_RESET_START, drv_data->regs + SPI_RESET); - writel(0, drv_data->regs + SPI_RESET); - writel(SPI_DEFAULT_CONTROL, drv_data->regs + SPI_CONTROL); + spi_imx->bitbang.chipselect = spi_imx_chipselect; + spi_imx->bitbang.setup_transfer = spi_imx_setupxfer; + spi_imx->bitbang.txrx_bufs = spi_imx_transfer; + spi_imx->bitbang.master->setup = spi_imx_setup; + spi_imx->bitbang.master->cleanup = spi_imx_cleanup; + spi_imx->bitbang.master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH; - /* Initial and start queue */ - status = init_queue(drv_data); - if (status != 0) { - dev_err(&pdev->dev, "probe - problem initializing queue\n"); - goto err_init_queue; - } - status = start_queue(drv_data); - if (status != 0) { - dev_err(&pdev->dev, "probe - problem starting queue\n"); - goto err_start_queue; - } + init_completion(&spi_imx->xfer_done); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "can't get platform resource\n"); + ret = -ENOMEM; + goto out_gpio_free; + } + + if (!request_mem_region(res->start, resource_size(res), pdev->name)) { + dev_err(&pdev->dev, "request_mem_region failed\n"); + ret = -EBUSY; + goto out_gpio_free; + } + + spi_imx->base = ioremap(res->start, resource_size(res)); + if (!spi_imx->base) { + ret = -EINVAL; + goto out_release_mem; + } + + spi_imx->irq = platform_get_irq(pdev, 0); + if (!spi_imx->irq) { + ret = -EINVAL; + goto out_iounmap; + } + + ret = request_irq(spi_imx->irq, spi_imx_isr, 0, DRIVER_NAME, spi_imx); + if (ret) { + dev_err(&pdev->dev, "can't get irq%d: %d\n", spi_imx->irq, ret); + goto out_iounmap; + } + + if (cpu_is_mx31() || cpu_is_mx35()) { + spi_imx->intctrl = mx31_intctrl; + spi_imx->config = mx31_config; + spi_imx->trigger = mx31_trigger; + spi_imx->rx_available = mx31_rx_available; + } else if (cpu_is_mx27() || cpu_is_mx21()) { + spi_imx->intctrl = mx27_intctrl; + spi_imx->config = mx27_config; + spi_imx->trigger = mx27_trigger; + spi_imx->rx_available = mx27_rx_available; + } else if (cpu_is_mx1()) { + spi_imx->intctrl = mx1_intctrl; + spi_imx->config = mx1_config; + spi_imx->trigger = mx1_trigger; + spi_imx->rx_available = mx1_rx_available; + } else + BUG(); - /* Register with the SPI framework */ - platform_set_drvdata(pdev, drv_data); - status = spi_register_master(master); - if (status != 0) { - dev_err(&pdev->dev, "probe - problem registering spi master\n"); - goto err_spi_register; + spi_imx->clk = clk_get(&pdev->dev, NULL); + if (IS_ERR(spi_imx->clk)) { + dev_err(&pdev->dev, "unable to get clock\n"); + ret = PTR_ERR(spi_imx->clk); + goto out_free_irq; } - dev_dbg(dev, "probe succeded\n"); - return 0; + clk_enable(spi_imx->clk); + spi_imx->spi_clk = clk_get_rate(spi_imx->clk); -err_init_queue: -err_start_queue: -err_spi_register: - destroy_queue(drv_data); + if (!cpu_is_mx31() || !cpu_is_mx35()) + writel(1, spi_imx->base + MXC_RESET); -err_no_rxdma: -err_no_txdma: -err_no_devid: - free_irq(irq, drv_data); + spi_imx->intctrl(spi_imx, 0); -err_no_irqres: - iounmap(drv_data->regs); + ret = spi_bitbang_start(&spi_imx->bitbang); + if (ret) { + dev_err(&pdev->dev, "bitbang start failed with %d\n", ret); + goto out_clk_put; + } -err_no_iomap: - release_resource(drv_data->ioarea); - kfree(drv_data->ioarea); + dev_info(&pdev->dev, "probed\n"); -err_no_iores: - clk_disable(drv_data->clk); - clk_put(drv_data->clk); + return ret; -err_no_clk: +out_clk_put: + clk_disable(spi_imx->clk); + clk_put(spi_imx->clk); +out_free_irq: + free_irq(spi_imx->irq, spi_imx); +out_iounmap: + iounmap(spi_imx->base); +out_release_mem: + release_mem_region(res->start, resource_size(res)); +out_gpio_free: + for (i = 0; i < master->num_chipselect; i++) + if (spi_imx->chipselect[i] >= 0) + gpio_free(spi_imx->chipselect[i]); +out_master_put: spi_master_put(master); - -err_no_pdata: -err_no_mem: - return status; + kfree(master); + platform_set_drvdata(pdev, NULL); + return ret; } static int __exit spi_imx_remove(struct platform_device *pdev) { - struct driver_data *drv_data = platform_get_drvdata(pdev); - int irq; - int status = 0; - - if (!drv_data) - return 0; - - tasklet_kill(&drv_data->pump_transfers); + struct spi_master *master = platform_get_drvdata(pdev); + struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + struct spi_imx_data *spi_imx = spi_master_get_devdata(master); + int i; - /* Remove the queue */ - status = destroy_queue(drv_data); - if (status != 0) { - dev_err(&pdev->dev, "queue remove failed (%d)\n", status); - return status; - } - - /* Reset SPI */ - writel(SPI_RESET_START, drv_data->regs + SPI_RESET); - writel(0, drv_data->regs + SPI_RESET); - - /* Release DMA */ - if (drv_data->master_info->enable_dma) { - RSSR(drv_data->rx_channel) = 0; - RSSR(drv_data->tx_channel) = 0; - imx_dma_free(drv_data->tx_channel); - imx_dma_free(drv_data->rx_channel); - } + spi_bitbang_stop(&spi_imx->bitbang); - /* Release IRQ */ - irq = platform_get_irq(pdev, 0); - if (irq >= 0) - free_irq(irq, drv_data); + writel(0, spi_imx->base + MXC_CSPICTRL); + clk_disable(spi_imx->clk); + clk_put(spi_imx->clk); + free_irq(spi_imx->irq, spi_imx); + iounmap(spi_imx->base); - clk_disable(drv_data->clk); - clk_put(drv_data->clk); + for (i = 0; i < master->num_chipselect; i++) + if (spi_imx->chipselect[i] >= 0) + gpio_free(spi_imx->chipselect[i]); - /* Release map resources */ - iounmap(drv_data->regs); - release_resource(drv_data->ioarea); - kfree(drv_data->ioarea); + spi_master_put(master); - /* Disconnect from the SPI framework */ - spi_unregister_master(drv_data->master); - spi_master_put(drv_data->master); + release_mem_region(res->start, resource_size(res)); - /* Prevent double remove */ platform_set_drvdata(pdev, NULL); - dev_dbg(&pdev->dev, "remove succeded\n"); - return 0; } -static void spi_imx_shutdown(struct platform_device *pdev) -{ - struct driver_data *drv_data = platform_get_drvdata(pdev); - - /* Reset SPI */ - writel(SPI_RESET_START, drv_data->regs + SPI_RESET); - writel(0, drv_data->regs + SPI_RESET); - - dev_dbg(&pdev->dev, "shutdown succeded\n"); -} - -#ifdef CONFIG_PM - -static int spi_imx_suspend(struct platform_device *pdev, pm_message_t state) -{ - struct driver_data *drv_data = platform_get_drvdata(pdev); - int status = 0; - - status = stop_queue(drv_data); - if (status != 0) { - dev_warn(&pdev->dev, "suspend cannot stop queue\n"); - return status; - } - - dev_dbg(&pdev->dev, "suspended\n"); - - return 0; -} - -static int spi_imx_resume(struct platform_device *pdev) -{ - struct driver_data *drv_data = platform_get_drvdata(pdev); - int status = 0; - - /* Start the queue running */ - status = start_queue(drv_data); - if (status != 0) - dev_err(&pdev->dev, "problem starting queue (%d)\n", status); - else - dev_dbg(&pdev->dev, "resumed\n"); - - return status; -} -#else -#define spi_imx_suspend NULL -#define spi_imx_resume NULL -#endif /* CONFIG_PM */ - -/* work with hotplug and coldplug */ -MODULE_ALIAS("platform:spi_imx"); - -static struct platform_driver driver = { +static struct platform_driver spi_imx_driver = { .driver = { - .name = "spi_imx", - .owner = THIS_MODULE, - }, + .name = DRIVER_NAME, + .owner = THIS_MODULE, + }, + .probe = spi_imx_probe, .remove = __exit_p(spi_imx_remove), - .shutdown = spi_imx_shutdown, - .suspend = spi_imx_suspend, - .resume = spi_imx_resume, }; static int __init spi_imx_init(void) { - return platform_driver_probe(&driver, spi_imx_probe); + return platform_driver_register(&spi_imx_driver); } -module_init(spi_imx_init); static void __exit spi_imx_exit(void) { - platform_driver_unregister(&driver); + platform_driver_unregister(&spi_imx_driver); } + +module_init(spi_imx_init); module_exit(spi_imx_exit); -MODULE_AUTHOR("Andrea Paterniani, <a.paterniani@swapp-eng.it>"); -MODULE_DESCRIPTION("iMX SPI Controller Driver"); +MODULE_DESCRIPTION("SPI Master Controller driver"); +MODULE_AUTHOR("Sascha Hauer, Pengutronix"); MODULE_LICENSE("GPL"); diff --git a/drivers/spi/spi_ppc4xx.c b/drivers/spi/spi_ppc4xx.c new file mode 100644 index 000000000000..140a18d6cf3e --- /dev/null +++ b/drivers/spi/spi_ppc4xx.c @@ -0,0 +1,612 @@ +/* + * SPI_PPC4XX SPI controller driver. + * + * Copyright (C) 2007 Gary Jennejohn <garyj@denx.de> + * Copyright 2008 Stefan Roese <sr@denx.de>, DENX Software Engineering + * Copyright 2009 Harris Corporation, Steven A. Falco <sfalco@harris.com> + * + * Based in part on drivers/spi/spi_s3c24xx.c + * + * Copyright (c) 2006 Ben Dooks + * Copyright (c) 2006 Simtec Electronics + * Ben Dooks <ben@simtec.co.uk> + * + * 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. + */ + +/* + * The PPC4xx SPI controller has no FIFO so each sent/received byte will + * generate an interrupt to the CPU. This can cause high CPU utilization. + * This driver allows platforms to reduce the interrupt load on the CPU + * during SPI transfers by setting max_speed_hz via the device tree. + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/sched.h> +#include <linux/errno.h> +#include <linux/wait.h> +#include <linux/of_platform.h> +#include <linux/of_spi.h> +#include <linux/of_gpio.h> +#include <linux/interrupt.h> +#include <linux/delay.h> + +#include <linux/gpio.h> +#include <linux/spi/spi.h> +#include <linux/spi/spi_bitbang.h> + +#include <asm/io.h> +#include <asm/dcr.h> +#include <asm/dcr-regs.h> + +/* bits in mode register - bit 0 is MSb */ + +/* + * SPI_PPC4XX_MODE_SCP = 0 means "data latched on trailing edge of clock" + * SPI_PPC4XX_MODE_SCP = 1 means "data latched on leading edge of clock" + * Note: This is the inverse of CPHA. + */ +#define SPI_PPC4XX_MODE_SCP (0x80 >> 3) + +/* SPI_PPC4XX_MODE_SPE = 1 means "port enabled" */ +#define SPI_PPC4XX_MODE_SPE (0x80 >> 4) + +/* + * SPI_PPC4XX_MODE_RD = 0 means "MSB first" - this is the normal mode + * SPI_PPC4XX_MODE_RD = 1 means "LSB first" - this is bit-reversed mode + * Note: This is identical to SPI_LSB_FIRST. + */ +#define SPI_PPC4XX_MODE_RD (0x80 >> 5) + +/* + * SPI_PPC4XX_MODE_CI = 0 means "clock idles low" + * SPI_PPC4XX_MODE_CI = 1 means "clock idles high" + * Note: This is identical to CPOL. + */ +#define SPI_PPC4XX_MODE_CI (0x80 >> 6) + +/* + * SPI_PPC4XX_MODE_IL = 0 means "loopback disable" + * SPI_PPC4XX_MODE_IL = 1 means "loopback enable" + */ +#define SPI_PPC4XX_MODE_IL (0x80 >> 7) + +/* bits in control register */ +/* starts a transfer when set */ +#define SPI_PPC4XX_CR_STR (0x80 >> 7) + +/* bits in status register */ +/* port is busy with a transfer */ +#define SPI_PPC4XX_SR_BSY (0x80 >> 6) +/* RxD ready */ +#define SPI_PPC4XX_SR_RBR (0x80 >> 7) + +/* clock settings (SCP and CI) for various SPI modes */ +#define SPI_CLK_MODE0 (SPI_PPC4XX_MODE_SCP | 0) +#define SPI_CLK_MODE1 (0 | 0) +#define SPI_CLK_MODE2 (SPI_PPC4XX_MODE_SCP | SPI_PPC4XX_MODE_CI) +#define SPI_CLK_MODE3 (0 | SPI_PPC4XX_MODE_CI) + +#define DRIVER_NAME "spi_ppc4xx_of" + +struct spi_ppc4xx_regs { + u8 mode; + u8 rxd; + u8 txd; + u8 cr; + u8 sr; + u8 dummy; + /* + * Clock divisor modulus register + * This uses the follwing formula: + * SCPClkOut = OPBCLK/(4(CDM + 1)) + * or + * CDM = (OPBCLK/4*SCPClkOut) - 1 + * bit 0 is the MSb! + */ + u8 cdm; +}; + +/* SPI Controller driver's private data. */ +struct ppc4xx_spi { + /* bitbang has to be first */ + struct spi_bitbang bitbang; + struct completion done; + + u64 mapbase; + u64 mapsize; + int irqnum; + /* need this to set the SPI clock */ + unsigned int opb_freq; + + /* for transfers */ + int len; + int count; + /* data buffers */ + const unsigned char *tx; + unsigned char *rx; + + int *gpios; + + struct spi_ppc4xx_regs __iomem *regs; /* pointer to the registers */ + struct spi_master *master; + struct device *dev; +}; + +/* need this so we can set the clock in the chipselect routine */ +struct spi_ppc4xx_cs { + u8 mode; +}; + +static int spi_ppc4xx_txrx(struct spi_device *spi, struct spi_transfer *t) +{ + struct ppc4xx_spi *hw; + u8 data; + + dev_dbg(&spi->dev, "txrx: tx %p, rx %p, len %d\n", + t->tx_buf, t->rx_buf, t->len); + + hw = spi_master_get_devdata(spi->master); + + hw->tx = t->tx_buf; + hw->rx = t->rx_buf; + hw->len = t->len; + hw->count = 0; + + /* send the first byte */ + data = hw->tx ? hw->tx[0] : 0; + out_8(&hw->regs->txd, data); + out_8(&hw->regs->cr, SPI_PPC4XX_CR_STR); + wait_for_completion(&hw->done); + + return hw->count; +} + +static int spi_ppc4xx_setupxfer(struct spi_device *spi, struct spi_transfer *t) +{ + struct ppc4xx_spi *hw = spi_master_get_devdata(spi->master); + struct spi_ppc4xx_cs *cs = spi->controller_state; + int scr; + u8 cdm = 0; + u32 speed; + u8 bits_per_word; + + /* Start with the generic configuration for this device. */ + bits_per_word = spi->bits_per_word; + speed = spi->max_speed_hz; + + /* + * Modify the configuration if the transfer overrides it. Do not allow + * the transfer to overwrite the generic configuration with zeros. + */ + if (t) { + if (t->bits_per_word) + bits_per_word = t->bits_per_word; + + if (t->speed_hz) + speed = min(t->speed_hz, spi->max_speed_hz); + } + + if (bits_per_word != 8) { + dev_err(&spi->dev, "invalid bits-per-word (%d)\n", + bits_per_word); + return -EINVAL; + } + + if (!speed || (speed > spi->max_speed_hz)) { + dev_err(&spi->dev, "invalid speed_hz (%d)\n", speed); + return -EINVAL; + } + + /* Write new configration */ + out_8(&hw->regs->mode, cs->mode); + + /* Set the clock */ + /* opb_freq was already divided by 4 */ + scr = (hw->opb_freq / speed) - 1; + if (scr > 0) + cdm = min(scr, 0xff); + + dev_dbg(&spi->dev, "setting pre-scaler to %d (hz %d)\n", cdm, speed); + + if (in_8(&hw->regs->cdm) != cdm) + out_8(&hw->regs->cdm, cdm); + + spin_lock(&hw->bitbang.lock); + if (!hw->bitbang.busy) { + hw->bitbang.chipselect(spi, BITBANG_CS_INACTIVE); + /* Need to ndelay here? */ + } + spin_unlock(&hw->bitbang.lock); + + return 0; +} + +static int spi_ppc4xx_setup(struct spi_device *spi) +{ + struct spi_ppc4xx_cs *cs = spi->controller_state; + + if (spi->bits_per_word != 8) { + dev_err(&spi->dev, "invalid bits-per-word (%d)\n", + spi->bits_per_word); + return -EINVAL; + } + + if (!spi->max_speed_hz) { + dev_err(&spi->dev, "invalid max_speed_hz (must be non-zero)\n"); + return -EINVAL; + } + + if (cs == NULL) { + cs = kzalloc(sizeof *cs, GFP_KERNEL); + if (!cs) + return -ENOMEM; + spi->controller_state = cs; + } + + /* + * We set all bits of the SPI0_MODE register, so, + * no need to read-modify-write + */ + cs->mode = SPI_PPC4XX_MODE_SPE; + + switch (spi->mode & (SPI_CPHA | SPI_CPOL)) { + case SPI_MODE_0: + cs->mode |= SPI_CLK_MODE0; + break; + case SPI_MODE_1: + cs->mode |= SPI_CLK_MODE1; + break; + case SPI_MODE_2: + cs->mode |= SPI_CLK_MODE2; + break; + case SPI_MODE_3: + cs->mode |= SPI_CLK_MODE3; + break; + } + + if (spi->mode & SPI_LSB_FIRST) + cs->mode |= SPI_PPC4XX_MODE_RD; + + return 0; +} + +static void spi_ppc4xx_chipsel(struct spi_device *spi, int value) +{ + struct ppc4xx_spi *hw = spi_master_get_devdata(spi->master); + unsigned int cs = spi->chip_select; + unsigned int cspol; + + /* + * If there are no chip selects at all, or if this is the special + * case of a non-existent (dummy) chip select, do nothing. + */ + + if (!hw->master->num_chipselect || hw->gpios[cs] == -EEXIST) + return; + + cspol = spi->mode & SPI_CS_HIGH ? 1 : 0; + if (value == BITBANG_CS_INACTIVE) + cspol = !cspol; + + gpio_set_value(hw->gpios[cs], cspol); +} + +static irqreturn_t spi_ppc4xx_int(int irq, void *dev_id) +{ + struct ppc4xx_spi *hw; + u8 status; + u8 data; + unsigned int count; + + hw = (struct ppc4xx_spi *)dev_id; + + status = in_8(&hw->regs->sr); + if (!status) + return IRQ_NONE; + + /* + * BSY de-asserts one cycle after the transfer is complete. The + * interrupt is asserted after the transfer is complete. The exact + * relationship is not documented, hence this code. + */ + + if (unlikely(status & SPI_PPC4XX_SR_BSY)) { + u8 lstatus; + int cnt = 0; + + dev_dbg(hw->dev, "got interrupt but spi still busy?\n"); + do { + ndelay(10); + lstatus = in_8(&hw->regs->sr); + } while (++cnt < 100 && lstatus & SPI_PPC4XX_SR_BSY); + + if (cnt >= 100) { + dev_err(hw->dev, "busywait: too many loops!\n"); + complete(&hw->done); + return IRQ_HANDLED; + } else { + /* status is always 1 (RBR) here */ + status = in_8(&hw->regs->sr); + dev_dbg(hw->dev, "loops %d status %x\n", cnt, status); + } + } + + count = hw->count; + hw->count++; + + /* RBR triggered this interrupt. Therefore, data must be ready. */ + data = in_8(&hw->regs->rxd); + if (hw->rx) + hw->rx[count] = data; + + count++; + + if (count < hw->len) { + data = hw->tx ? hw->tx[count] : 0; + out_8(&hw->regs->txd, data); + out_8(&hw->regs->cr, SPI_PPC4XX_CR_STR); + } else { + complete(&hw->done); + } + + return IRQ_HANDLED; +} + +static void spi_ppc4xx_cleanup(struct spi_device *spi) +{ + kfree(spi->controller_state); +} + +static void spi_ppc4xx_enable(struct ppc4xx_spi *hw) +{ + /* + * On all 4xx PPC's the SPI bus is shared/multiplexed with + * the 2nd I2C bus. We need to enable the the SPI bus before + * using it. + */ + + /* need to clear bit 14 to enable SPC */ + dcri_clrset(SDR0, SDR0_PFC1, 0x80000000 >> 14, 0); +} + +static void free_gpios(struct ppc4xx_spi *hw) +{ + if (hw->master->num_chipselect) { + int i; + for (i = 0; i < hw->master->num_chipselect; i++) + if (gpio_is_valid(hw->gpios[i])) + gpio_free(hw->gpios[i]); + + kfree(hw->gpios); + hw->gpios = NULL; + } +} + +/* + * of_device layer stuff... + */ +static int __init spi_ppc4xx_of_probe(struct of_device *op, + const struct of_device_id *match) +{ + struct ppc4xx_spi *hw; + struct spi_master *master; + struct spi_bitbang *bbp; + struct resource resource; + struct device_node *np = op->node; + struct device *dev = &op->dev; + struct device_node *opbnp; + int ret; + int num_gpios; + const unsigned int *clk; + + master = spi_alloc_master(dev, sizeof *hw); + if (master == NULL) + return -ENOMEM; + dev_set_drvdata(dev, master); + hw = spi_master_get_devdata(master); + hw->master = spi_master_get(master); + hw->dev = dev; + + init_completion(&hw->done); + + /* + * A count of zero implies a single SPI device without any chip-select. + * Note that of_gpio_count counts all gpios assigned to this spi master. + * This includes both "null" gpio's and real ones. + */ + num_gpios = of_gpio_count(np); + if (num_gpios) { + int i; + + hw->gpios = kzalloc(sizeof(int) * num_gpios, GFP_KERNEL); + if (!hw->gpios) { + ret = -ENOMEM; + goto free_master; + } + + for (i = 0; i < num_gpios; i++) { + int gpio; + enum of_gpio_flags flags; + + gpio = of_get_gpio_flags(np, i, &flags); + hw->gpios[i] = gpio; + + if (gpio_is_valid(gpio)) { + /* Real CS - set the initial state. */ + ret = gpio_request(gpio, np->name); + if (ret < 0) { + dev_err(dev, "can't request gpio " + "#%d: %d\n", i, ret); + goto free_gpios; + } + + gpio_direction_output(gpio, + !!(flags & OF_GPIO_ACTIVE_LOW)); + } else if (gpio == -EEXIST) { + ; /* No CS, but that's OK. */ + } else { + dev_err(dev, "invalid gpio #%d: %d\n", i, gpio); + ret = -EINVAL; + goto free_gpios; + } + } + } + + /* Setup the state for the bitbang driver */ + bbp = &hw->bitbang; + bbp->master = hw->master; + bbp->setup_transfer = spi_ppc4xx_setupxfer; + bbp->chipselect = spi_ppc4xx_chipsel; + bbp->txrx_bufs = spi_ppc4xx_txrx; + bbp->use_dma = 0; + bbp->master->setup = spi_ppc4xx_setup; + bbp->master->cleanup = spi_ppc4xx_cleanup; + + /* Allocate bus num dynamically. */ + bbp->master->bus_num = -1; + + /* the spi->mode bits understood by this driver: */ + bbp->master->mode_bits = + SPI_CPHA | SPI_CPOL | SPI_CS_HIGH | SPI_LSB_FIRST; + + /* this many pins in all GPIO controllers */ + bbp->master->num_chipselect = num_gpios; + + /* Get the clock for the OPB */ + opbnp = of_find_compatible_node(NULL, NULL, "ibm,opb"); + if (opbnp == NULL) { + dev_err(dev, "OPB: cannot find node\n"); + ret = -ENODEV; + goto free_gpios; + } + /* Get the clock (Hz) for the OPB */ + clk = of_get_property(opbnp, "clock-frequency", NULL); + if (clk == NULL) { + dev_err(dev, "OPB: no clock-frequency property set\n"); + of_node_put(opbnp); + ret = -ENODEV; + goto free_gpios; + } + hw->opb_freq = *clk; + hw->opb_freq >>= 2; + of_node_put(opbnp); + + ret = of_address_to_resource(np, 0, &resource); + if (ret) { + dev_err(dev, "error while parsing device node resource\n"); + goto free_gpios; + } + hw->mapbase = resource.start; + hw->mapsize = resource.end - resource.start + 1; + + /* Sanity check */ + if (hw->mapsize < sizeof(struct spi_ppc4xx_regs)) { + dev_err(dev, "too small to map registers\n"); + ret = -EINVAL; + goto free_gpios; + } + + /* Request IRQ */ + hw->irqnum = irq_of_parse_and_map(np, 0); + ret = request_irq(hw->irqnum, spi_ppc4xx_int, + IRQF_DISABLED, "spi_ppc4xx_of", (void *)hw); + if (ret) { + dev_err(dev, "unable to allocate interrupt\n"); + goto free_gpios; + } + + if (!request_mem_region(hw->mapbase, hw->mapsize, DRIVER_NAME)) { + dev_err(dev, "resource unavailable\n"); + ret = -EBUSY; + goto request_mem_error; + } + + hw->regs = ioremap(hw->mapbase, sizeof(struct spi_ppc4xx_regs)); + + if (!hw->regs) { + dev_err(dev, "unable to memory map registers\n"); + ret = -ENXIO; + goto map_io_error; + } + + spi_ppc4xx_enable(hw); + + /* Finally register our spi controller */ + dev->dma_mask = 0; + ret = spi_bitbang_start(bbp); + if (ret) { + dev_err(dev, "failed to register SPI master\n"); + goto unmap_regs; + } + + dev_info(dev, "driver initialized\n"); + of_register_spi_devices(master, np); + + return 0; + +unmap_regs: + iounmap(hw->regs); +map_io_error: + release_mem_region(hw->mapbase, hw->mapsize); +request_mem_error: + free_irq(hw->irqnum, hw); +free_gpios: + free_gpios(hw); +free_master: + dev_set_drvdata(dev, NULL); + spi_master_put(master); + + dev_err(dev, "initialization failed\n"); + return ret; +} + +static int __exit spi_ppc4xx_of_remove(struct of_device *op) +{ + struct spi_master *master = dev_get_drvdata(&op->dev); + struct ppc4xx_spi *hw = spi_master_get_devdata(master); + + spi_bitbang_stop(&hw->bitbang); + dev_set_drvdata(&op->dev, NULL); + release_mem_region(hw->mapbase, hw->mapsize); + free_irq(hw->irqnum, hw); + iounmap(hw->regs); + free_gpios(hw); + return 0; +} + +static struct of_device_id spi_ppc4xx_of_match[] = { + { .compatible = "ibm,ppc4xx-spi", }, + {}, +}; + +MODULE_DEVICE_TABLE(of, spi_ppc4xx_of_match); + +static struct of_platform_driver spi_ppc4xx_of_driver = { + .match_table = spi_ppc4xx_of_match, + .probe = spi_ppc4xx_of_probe, + .remove = __exit_p(spi_ppc4xx_of_remove), + .driver = { + .name = DRIVER_NAME, + .owner = THIS_MODULE, + }, +}; + +static int __init spi_ppc4xx_init(void) +{ + return of_register_platform_driver(&spi_ppc4xx_of_driver); +} +module_init(spi_ppc4xx_init); + +static void __exit spi_ppc4xx_exit(void) +{ + of_unregister_platform_driver(&spi_ppc4xx_of_driver); +} +module_exit(spi_ppc4xx_exit); + +MODULE_AUTHOR("Gary Jennejohn & Stefan Roese"); +MODULE_DESCRIPTION("Simple PPC4xx SPI Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/spi/spi_s3c24xx.c b/drivers/spi/spi_s3c24xx.c index 6ba8aece90b5..33d94f76b9ef 100644 --- a/drivers/spi/spi_s3c24xx.c +++ b/drivers/spi/spi_s3c24xx.c @@ -20,17 +20,28 @@ #include <linux/clk.h> #include <linux/platform_device.h> #include <linux/gpio.h> +#include <linux/io.h> #include <linux/spi/spi.h> #include <linux/spi/spi_bitbang.h> -#include <asm/io.h> -#include <asm/dma.h> -#include <mach/hardware.h> - #include <plat/regs-spi.h> #include <mach/spi.h> +/** + * s3c24xx_spi_devstate - per device data + * @hz: Last frequency calculated for @sppre field. + * @mode: Last mode setting for the @spcon field. + * @spcon: Value to write to the SPCON register. + * @sppre: Value to write to the SPPRE register. + */ +struct s3c24xx_spi_devstate { + unsigned int hz; + unsigned int mode; + u8 spcon; + u8 sppre; +}; + struct s3c24xx_spi { /* bitbang has to be first */ struct spi_bitbang bitbang; @@ -71,43 +82,31 @@ static void s3c24xx_spi_gpiocs(struct s3c2410_spi_info *spi, int cs, int pol) static void s3c24xx_spi_chipsel(struct spi_device *spi, int value) { + struct s3c24xx_spi_devstate *cs = spi->controller_state; struct s3c24xx_spi *hw = to_hw(spi); unsigned int cspol = spi->mode & SPI_CS_HIGH ? 1 : 0; - unsigned int spcon; + + /* change the chipselect state and the state of the spi engine clock */ switch (value) { case BITBANG_CS_INACTIVE: hw->set_cs(hw->pdata, spi->chip_select, cspol^1); + writeb(cs->spcon, hw->regs + S3C2410_SPCON); break; case BITBANG_CS_ACTIVE: - spcon = readb(hw->regs + S3C2410_SPCON); - - if (spi->mode & SPI_CPHA) - spcon |= S3C2410_SPCON_CPHA_FMTB; - else - spcon &= ~S3C2410_SPCON_CPHA_FMTB; - - if (spi->mode & SPI_CPOL) - spcon |= S3C2410_SPCON_CPOL_HIGH; - else - spcon &= ~S3C2410_SPCON_CPOL_HIGH; - - spcon |= S3C2410_SPCON_ENSCK; - - /* write new configration */ - - writeb(spcon, hw->regs + S3C2410_SPCON); + writeb(cs->spcon | S3C2410_SPCON_ENSCK, + hw->regs + S3C2410_SPCON); hw->set_cs(hw->pdata, spi->chip_select, cspol); - break; } } -static int s3c24xx_spi_setupxfer(struct spi_device *spi, - struct spi_transfer *t) +static int s3c24xx_spi_update_state(struct spi_device *spi, + struct spi_transfer *t) { struct s3c24xx_spi *hw = to_hw(spi); + struct s3c24xx_spi_devstate *cs = spi->controller_state; unsigned int bpw; unsigned int hz; unsigned int div; @@ -127,41 +126,89 @@ static int s3c24xx_spi_setupxfer(struct spi_device *spi, return -EINVAL; } - clk = clk_get_rate(hw->clk); - div = DIV_ROUND_UP(clk, hz * 2) - 1; + if (spi->mode != cs->mode) { + u8 spcon = SPCON_DEFAULT; - if (div > 255) - div = 255; + if (spi->mode & SPI_CPHA) + spcon |= S3C2410_SPCON_CPHA_FMTB; - dev_dbg(&spi->dev, "setting pre-scaler to %d (wanted %d, got %ld)\n", - div, hz, clk / (2 * (div + 1))); + if (spi->mode & SPI_CPOL) + spcon |= S3C2410_SPCON_CPOL_HIGH; + cs->mode = spi->mode; + cs->spcon = spcon; + } - writeb(div, hw->regs + S3C2410_SPPRE); + if (cs->hz != hz) { + clk = clk_get_rate(hw->clk); + div = DIV_ROUND_UP(clk, hz * 2) - 1; - spin_lock(&hw->bitbang.lock); - if (!hw->bitbang.busy) { - hw->bitbang.chipselect(spi, BITBANG_CS_INACTIVE); - /* need to ndelay for 0.5 clocktick ? */ + if (div > 255) + div = 255; + + dev_dbg(&spi->dev, "pre-scaler=%d (wanted %d, got %ld)\n", + div, hz, clk / (2 * (div + 1))); + + cs->hz = hz; + cs->sppre = div; } - spin_unlock(&hw->bitbang.lock); return 0; } +static int s3c24xx_spi_setupxfer(struct spi_device *spi, + struct spi_transfer *t) +{ + struct s3c24xx_spi_devstate *cs = spi->controller_state; + struct s3c24xx_spi *hw = to_hw(spi); + int ret; + + ret = s3c24xx_spi_update_state(spi, t); + if (!ret) + writeb(cs->sppre, hw->regs + S3C2410_SPPRE); + + return ret; +} + static int s3c24xx_spi_setup(struct spi_device *spi) { + struct s3c24xx_spi_devstate *cs = spi->controller_state; + struct s3c24xx_spi *hw = to_hw(spi); int ret; - ret = s3c24xx_spi_setupxfer(spi, NULL); - if (ret < 0) { - dev_err(&spi->dev, "setupxfer returned %d\n", ret); + /* allocate settings on the first call */ + if (!cs) { + cs = kzalloc(sizeof(struct s3c24xx_spi_devstate), GFP_KERNEL); + if (!cs) { + dev_err(&spi->dev, "no memory for controller state\n"); + return -ENOMEM; + } + + cs->spcon = SPCON_DEFAULT; + cs->hz = -1; + spi->controller_state = cs; + } + + /* initialise the state from the device */ + ret = s3c24xx_spi_update_state(spi, NULL); + if (ret) return ret; + + spin_lock(&hw->bitbang.lock); + if (!hw->bitbang.busy) { + hw->bitbang.chipselect(spi, BITBANG_CS_INACTIVE); + /* need to ndelay for 0.5 clocktick ? */ } + spin_unlock(&hw->bitbang.lock); return 0; } +static void s3c24xx_spi_cleanup(struct spi_device *spi) +{ + kfree(spi->controller_state); +} + static inline unsigned int hw_txbyte(struct s3c24xx_spi *hw, int count) { return hw->tx ? hw->tx[count] : 0; @@ -289,7 +336,9 @@ static int __init s3c24xx_spi_probe(struct platform_device *pdev) hw->bitbang.setup_transfer = s3c24xx_spi_setupxfer; hw->bitbang.chipselect = s3c24xx_spi_chipsel; hw->bitbang.txrx_bufs = s3c24xx_spi_txrx; - hw->bitbang.master->setup = s3c24xx_spi_setup; + + hw->master->setup = s3c24xx_spi_setup; + hw->master->cleanup = s3c24xx_spi_cleanup; dev_dbg(hw->dev, "bitbang at %p\n", &hw->bitbang); @@ -302,7 +351,7 @@ static int __init s3c24xx_spi_probe(struct platform_device *pdev) goto err_no_iores; } - hw->ioarea = request_mem_region(res->start, (res->end - res->start)+1, + hw->ioarea = request_mem_region(res->start, resource_size(res), pdev->name); if (hw->ioarea == NULL) { @@ -311,7 +360,7 @@ static int __init s3c24xx_spi_probe(struct platform_device *pdev) goto err_no_iores; } - hw->regs = ioremap(res->start, (res->end - res->start)+1); + hw->regs = ioremap(res->start, resource_size(res)); if (hw->regs == NULL) { dev_err(&pdev->dev, "Cannot map IO\n"); err = -ENXIO; @@ -421,9 +470,9 @@ static int __exit s3c24xx_spi_remove(struct platform_device *dev) #ifdef CONFIG_PM -static int s3c24xx_spi_suspend(struct platform_device *pdev, pm_message_t msg) +static int s3c24xx_spi_suspend(struct device *dev) { - struct s3c24xx_spi *hw = platform_get_drvdata(pdev); + struct s3c24xx_spi *hw = platform_get_drvdata(to_platform_device(dev)); if (hw->pdata && hw->pdata->gpio_setup) hw->pdata->gpio_setup(hw->pdata, 0); @@ -432,27 +481,31 @@ static int s3c24xx_spi_suspend(struct platform_device *pdev, pm_message_t msg) return 0; } -static int s3c24xx_spi_resume(struct platform_device *pdev) +static int s3c24xx_spi_resume(struct device *dev) { - struct s3c24xx_spi *hw = platform_get_drvdata(pdev); + struct s3c24xx_spi *hw = platform_get_drvdata(to_platform_device(dev)); s3c24xx_spi_initialsetup(hw); return 0; } +static struct dev_pm_ops s3c24xx_spi_pmops = { + .suspend = s3c24xx_spi_suspend, + .resume = s3c24xx_spi_resume, +}; + +#define S3C24XX_SPI_PMOPS &s3c24xx_spi_pmops #else -#define s3c24xx_spi_suspend NULL -#define s3c24xx_spi_resume NULL -#endif +#define S3C24XX_SPI_PMOPS NULL +#endif /* CONFIG_PM */ MODULE_ALIAS("platform:s3c2410-spi"); static struct platform_driver s3c24xx_spi_driver = { .remove = __exit_p(s3c24xx_spi_remove), - .suspend = s3c24xx_spi_suspend, - .resume = s3c24xx_spi_resume, .driver = { .name = "s3c2410-spi", .owner = THIS_MODULE, + .pm = S3C24XX_SPI_PMOPS, }, }; diff --git a/drivers/spi/spi_stmp.c b/drivers/spi/spi_stmp.c new file mode 100644 index 000000000000..2552bb364005 --- /dev/null +++ b/drivers/spi/spi_stmp.c @@ -0,0 +1,679 @@ +/* + * Freescale STMP378X SPI master driver + * + * Author: dmitry pervushin <dimka@embeddedalley.com> + * + * Copyright 2008 Freescale Semiconductor, Inc. All Rights Reserved. + * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved. + */ + +/* + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ +#include <linux/module.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/platform_device.h> +#include <linux/spi/spi.h> +#include <linux/err.h> +#include <linux/clk.h> +#include <linux/io.h> +#include <linux/dma-mapping.h> +#include <linux/delay.h> + +#include <mach/platform.h> +#include <mach/stmp3xxx.h> +#include <mach/dma.h> +#include <mach/regs-ssp.h> +#include <mach/regs-apbh.h> + + +/* 0 means DMA mode(recommended, default), !0 - PIO mode */ +static int pio; +static int clock; + +/* default timeout for busy waits is 2 seconds */ +#define STMP_SPI_TIMEOUT (2 * HZ) + +struct stmp_spi { + int id; + + void * __iomem regs; /* vaddr of the control registers */ + + int irq, err_irq; + u32 dma; + struct stmp3xxx_dma_descriptor d; + + u32 speed_khz; + u32 saved_timings; + u32 divider; + + struct clk *clk; + struct device *master_dev; + + struct work_struct work; + struct workqueue_struct *workqueue; + + /* lock protects queue access */ + spinlock_t lock; + struct list_head queue; + + struct completion done; +}; + +#define busy_wait(cond) \ + ({ \ + unsigned long end_jiffies = jiffies + STMP_SPI_TIMEOUT; \ + bool succeeded = false; \ + do { \ + if (cond) { \ + succeeded = true; \ + break; \ + } \ + cpu_relax(); \ + } while (time_before(end_jiffies, jiffies)); \ + succeeded; \ + }) + +/** + * stmp_spi_init_hw + * Initialize the SSP port + */ +static int stmp_spi_init_hw(struct stmp_spi *ss) +{ + int err = 0; + void *pins = ss->master_dev->platform_data; + + err = stmp3xxx_request_pin_group(pins, dev_name(ss->master_dev)); + if (err) + goto out; + + ss->clk = clk_get(NULL, "ssp"); + if (IS_ERR(ss->clk)) { + err = PTR_ERR(ss->clk); + goto out_free_pins; + } + clk_enable(ss->clk); + + stmp3xxx_reset_block(ss->regs, false); + stmp3xxx_dma_reset_channel(ss->dma); + + return 0; + +out_free_pins: + stmp3xxx_release_pin_group(pins, dev_name(ss->master_dev)); +out: + return err; +} + +static void stmp_spi_release_hw(struct stmp_spi *ss) +{ + void *pins = ss->master_dev->platform_data; + + if (ss->clk && !IS_ERR(ss->clk)) { + clk_disable(ss->clk); + clk_put(ss->clk); + } + stmp3xxx_release_pin_group(pins, dev_name(ss->master_dev)); +} + +static int stmp_spi_setup_transfer(struct spi_device *spi, + struct spi_transfer *t) +{ + u8 bits_per_word; + u32 hz; + struct stmp_spi *ss = spi_master_get_devdata(spi->master); + u16 rate; + + bits_per_word = spi->bits_per_word; + if (t && t->bits_per_word) + bits_per_word = t->bits_per_word; + + /* + * Calculate speed: + * - by default, use maximum speed from ssp clk + * - if device overrides it, use it + * - if transfer specifies other speed, use transfer's one + */ + hz = 1000 * ss->speed_khz / ss->divider; + if (spi->max_speed_hz) + hz = min(hz, spi->max_speed_hz); + if (t && t->speed_hz) + hz = min(hz, t->speed_hz); + + if (hz == 0) { + dev_err(&spi->dev, "Cannot continue with zero clock\n"); + return -EINVAL; + } + + if (bits_per_word != 8) { + dev_err(&spi->dev, "%s, unsupported bits_per_word=%d\n", + __func__, bits_per_word); + return -EINVAL; + } + + dev_dbg(&spi->dev, "Requested clk rate = %uHz, max = %uHz/%d = %uHz\n", + hz, ss->speed_khz, ss->divider, + ss->speed_khz * 1000 / ss->divider); + + if (ss->speed_khz * 1000 / ss->divider < hz) { + dev_err(&spi->dev, "%s, unsupported clock rate %uHz\n", + __func__, hz); + return -EINVAL; + } + + rate = 1000 * ss->speed_khz/ss->divider/hz; + + writel(BF(ss->divider, SSP_TIMING_CLOCK_DIVIDE) | + BF(rate - 1, SSP_TIMING_CLOCK_RATE), + HW_SSP_TIMING + ss->regs); + + writel(BF(1 /* mode SPI */, SSP_CTRL1_SSP_MODE) | + BF(4 /* 8 bits */, SSP_CTRL1_WORD_LENGTH) | + ((spi->mode & SPI_CPOL) ? BM_SSP_CTRL1_POLARITY : 0) | + ((spi->mode & SPI_CPHA) ? BM_SSP_CTRL1_PHASE : 0) | + (pio ? 0 : BM_SSP_CTRL1_DMA_ENABLE), + ss->regs + HW_SSP_CTRL1); + + return 0; +} + +static int stmp_spi_setup(struct spi_device *spi) +{ + /* spi_setup() does basic checks, + * stmp_spi_setup_transfer() does more later + */ + if (spi->bits_per_word != 8) { + dev_err(&spi->dev, "%s, unsupported bits_per_word=%d\n", + __func__, spi->bits_per_word); + return -EINVAL; + } + return 0; +} + +static inline u32 stmp_spi_cs(unsigned cs) +{ + return ((cs & 1) ? BM_SSP_CTRL0_WAIT_FOR_CMD : 0) | + ((cs & 2) ? BM_SSP_CTRL0_WAIT_FOR_IRQ : 0); +} + +static int stmp_spi_txrx_dma(struct stmp_spi *ss, int cs, + unsigned char *buf, dma_addr_t dma_buf, int len, + int first, int last, bool write) +{ + u32 c0 = 0; + dma_addr_t spi_buf_dma = dma_buf; + int status = 0; + enum dma_data_direction dir = write ? DMA_TO_DEVICE : DMA_FROM_DEVICE; + + c0 |= (first ? BM_SSP_CTRL0_LOCK_CS : 0); + c0 |= (last ? BM_SSP_CTRL0_IGNORE_CRC : 0); + c0 |= (write ? 0 : BM_SSP_CTRL0_READ); + c0 |= BM_SSP_CTRL0_DATA_XFER; + + c0 |= stmp_spi_cs(cs); + + c0 |= BF(len, SSP_CTRL0_XFER_COUNT); + + if (!dma_buf) + spi_buf_dma = dma_map_single(ss->master_dev, buf, len, dir); + + ss->d.command->cmd = + BF(len, APBH_CHn_CMD_XFER_COUNT) | + BF(1, APBH_CHn_CMD_CMDWORDS) | + BM_APBH_CHn_CMD_WAIT4ENDCMD | + BM_APBH_CHn_CMD_IRQONCMPLT | + BF(write ? BV_APBH_CHn_CMD_COMMAND__DMA_READ : + BV_APBH_CHn_CMD_COMMAND__DMA_WRITE, + APBH_CHn_CMD_COMMAND); + ss->d.command->pio_words[0] = c0; + ss->d.command->buf_ptr = spi_buf_dma; + + stmp3xxx_dma_reset_channel(ss->dma); + stmp3xxx_dma_clear_interrupt(ss->dma); + stmp3xxx_dma_enable_interrupt(ss->dma); + init_completion(&ss->done); + stmp3xxx_dma_go(ss->dma, &ss->d, 1); + wait_for_completion(&ss->done); + + if (!busy_wait(readl(ss->regs + HW_SSP_CTRL0) & BM_SSP_CTRL0_RUN)) + status = -ETIMEDOUT; + + if (!dma_buf) + dma_unmap_single(ss->master_dev, spi_buf_dma, len, dir); + + return status; +} + +static inline void stmp_spi_enable(struct stmp_spi *ss) +{ + stmp3xxx_setl(BM_SSP_CTRL0_LOCK_CS, ss->regs + HW_SSP_CTRL0); + stmp3xxx_clearl(BM_SSP_CTRL0_IGNORE_CRC, ss->regs + HW_SSP_CTRL0); +} + +static inline void stmp_spi_disable(struct stmp_spi *ss) +{ + stmp3xxx_clearl(BM_SSP_CTRL0_LOCK_CS, ss->regs + HW_SSP_CTRL0); + stmp3xxx_setl(BM_SSP_CTRL0_IGNORE_CRC, ss->regs + HW_SSP_CTRL0); +} + +static int stmp_spi_txrx_pio(struct stmp_spi *ss, int cs, + unsigned char *buf, int len, + bool first, bool last, bool write) +{ + if (first) + stmp_spi_enable(ss); + + stmp3xxx_setl(stmp_spi_cs(cs), ss->regs + HW_SSP_CTRL0); + + while (len--) { + if (last && len <= 0) + stmp_spi_disable(ss); + + stmp3xxx_clearl(BM_SSP_CTRL0_XFER_COUNT, + ss->regs + HW_SSP_CTRL0); + stmp3xxx_setl(1, ss->regs + HW_SSP_CTRL0); + + if (write) + stmp3xxx_clearl(BM_SSP_CTRL0_READ, + ss->regs + HW_SSP_CTRL0); + else + stmp3xxx_setl(BM_SSP_CTRL0_READ, + ss->regs + HW_SSP_CTRL0); + + /* Run! */ + stmp3xxx_setl(BM_SSP_CTRL0_RUN, ss->regs + HW_SSP_CTRL0); + + if (!busy_wait(readl(ss->regs + HW_SSP_CTRL0) & + BM_SSP_CTRL0_RUN)) + break; + + if (write) + writel(*buf, ss->regs + HW_SSP_DATA); + + /* Set TRANSFER */ + stmp3xxx_setl(BM_SSP_CTRL0_DATA_XFER, ss->regs + HW_SSP_CTRL0); + + if (!write) { + if (busy_wait((readl(ss->regs + HW_SSP_STATUS) & + BM_SSP_STATUS_FIFO_EMPTY))) + break; + *buf = readl(ss->regs + HW_SSP_DATA) & 0xFF; + } + + if (!busy_wait(readl(ss->regs + HW_SSP_CTRL0) & + BM_SSP_CTRL0_RUN)) + break; + + /* advance to the next byte */ + buf++; + } + + return len < 0 ? 0 : -ETIMEDOUT; +} + +static int stmp_spi_handle_message(struct stmp_spi *ss, struct spi_message *m) +{ + bool first, last; + struct spi_transfer *t, *tmp_t; + int status = 0; + int cs; + + cs = m->spi->chip_select; + + list_for_each_entry_safe(t, tmp_t, &m->transfers, transfer_list) { + + first = (&t->transfer_list == m->transfers.next); + last = (&t->transfer_list == m->transfers.prev); + + if (first || t->speed_hz || t->bits_per_word) + stmp_spi_setup_transfer(m->spi, t); + + /* reject "not last" transfers which request to change cs */ + if (t->cs_change && !last) { + dev_err(&m->spi->dev, + "Message with t->cs_change has been skipped\n"); + continue; + } + + if (t->tx_buf) { + status = pio ? + stmp_spi_txrx_pio(ss, cs, (void *)t->tx_buf, + t->len, first, last, true) : + stmp_spi_txrx_dma(ss, cs, (void *)t->tx_buf, + t->tx_dma, t->len, first, last, true); +#ifdef DEBUG + if (t->len < 0x10) + print_hex_dump_bytes("Tx ", + DUMP_PREFIX_OFFSET, + t->tx_buf, t->len); + else + pr_debug("Tx: %d bytes\n", t->len); +#endif + } + if (t->rx_buf) { + status = pio ? + stmp_spi_txrx_pio(ss, cs, t->rx_buf, + t->len, first, last, false) : + stmp_spi_txrx_dma(ss, cs, t->rx_buf, + t->rx_dma, t->len, first, last, false); +#ifdef DEBUG + if (t->len < 0x10) + print_hex_dump_bytes("Rx ", + DUMP_PREFIX_OFFSET, + t->rx_buf, t->len); + else + pr_debug("Rx: %d bytes\n", t->len); +#endif + } + + if (t->delay_usecs) + udelay(t->delay_usecs); + + if (status) + break; + + } + return status; +} + +/** + * stmp_spi_handle - handle messages from the queue + */ +static void stmp_spi_handle(struct work_struct *w) +{ + struct stmp_spi *ss = container_of(w, struct stmp_spi, work); + unsigned long flags; + struct spi_message *m; + + spin_lock_irqsave(&ss->lock, flags); + while (!list_empty(&ss->queue)) { + m = list_entry(ss->queue.next, struct spi_message, queue); + list_del_init(&m->queue); + spin_unlock_irqrestore(&ss->lock, flags); + + m->status = stmp_spi_handle_message(ss, m); + m->complete(m->context); + + spin_lock_irqsave(&ss->lock, flags); + } + spin_unlock_irqrestore(&ss->lock, flags); + + return; +} + +/** + * stmp_spi_transfer - perform message transfer. + * Called indirectly from spi_async, queues all the messages to + * spi_handle_message. + * @spi: spi device + * @m: message to be queued + */ +static int stmp_spi_transfer(struct spi_device *spi, struct spi_message *m) +{ + struct stmp_spi *ss = spi_master_get_devdata(spi->master); + unsigned long flags; + + m->status = -EINPROGRESS; + spin_lock_irqsave(&ss->lock, flags); + list_add_tail(&m->queue, &ss->queue); + queue_work(ss->workqueue, &ss->work); + spin_unlock_irqrestore(&ss->lock, flags); + return 0; +} + +static irqreturn_t stmp_spi_irq(int irq, void *dev_id) +{ + struct stmp_spi *ss = dev_id; + + stmp3xxx_dma_clear_interrupt(ss->dma); + complete(&ss->done); + return IRQ_HANDLED; +} + +static irqreturn_t stmp_spi_irq_err(int irq, void *dev_id) +{ + struct stmp_spi *ss = dev_id; + u32 c1, st; + + c1 = readl(ss->regs + HW_SSP_CTRL1); + st = readl(ss->regs + HW_SSP_STATUS); + dev_err(ss->master_dev, "%s: status = 0x%08X, c1 = 0x%08X\n", + __func__, st, c1); + stmp3xxx_clearl(c1 & 0xCCCC0000, ss->regs + HW_SSP_CTRL1); + + return IRQ_HANDLED; +} + +static int __devinit stmp_spi_probe(struct platform_device *dev) +{ + int err = 0; + struct spi_master *master; + struct stmp_spi *ss; + struct resource *r; + + master = spi_alloc_master(&dev->dev, sizeof(struct stmp_spi)); + if (master == NULL) { + err = -ENOMEM; + goto out0; + } + master->flags = SPI_MASTER_HALF_DUPLEX; + + ss = spi_master_get_devdata(master); + platform_set_drvdata(dev, master); + + /* Get resources(memory, IRQ) associated with the device */ + r = platform_get_resource(dev, IORESOURCE_MEM, 0); + if (r == NULL) { + err = -ENODEV; + goto out_put_master; + } + ss->regs = ioremap(r->start, resource_size(r)); + if (!ss->regs) { + err = -EINVAL; + goto out_put_master; + } + + ss->master_dev = &dev->dev; + ss->id = dev->id; + + INIT_WORK(&ss->work, stmp_spi_handle); + INIT_LIST_HEAD(&ss->queue); + spin_lock_init(&ss->lock); + + ss->workqueue = create_singlethread_workqueue(dev_name(&dev->dev)); + if (!ss->workqueue) { + err = -ENXIO; + goto out_put_master; + } + master->transfer = stmp_spi_transfer; + master->setup = stmp_spi_setup; + + /* the spi->mode bits understood by this driver: */ + master->mode_bits = SPI_CPOL | SPI_CPHA; + + ss->irq = platform_get_irq(dev, 0); + if (ss->irq < 0) { + err = ss->irq; + goto out_put_master; + } + ss->err_irq = platform_get_irq(dev, 1); + if (ss->err_irq < 0) { + err = ss->err_irq; + goto out_put_master; + } + + r = platform_get_resource(dev, IORESOURCE_DMA, 0); + if (r == NULL) { + err = -ENODEV; + goto out_put_master; + } + + ss->dma = r->start; + err = stmp3xxx_dma_request(ss->dma, &dev->dev, dev_name(&dev->dev)); + if (err) + goto out_put_master; + + err = stmp3xxx_dma_allocate_command(ss->dma, &ss->d); + if (err) + goto out_free_dma; + + master->bus_num = dev->id; + master->num_chipselect = 1; + + /* SPI controller initializations */ + err = stmp_spi_init_hw(ss); + if (err) { + dev_dbg(&dev->dev, "cannot initialize hardware\n"); + goto out_free_dma_desc; + } + + if (clock) { + dev_info(&dev->dev, "clock rate forced to %d\n", clock); + clk_set_rate(ss->clk, clock); + } + ss->speed_khz = clk_get_rate(ss->clk); + ss->divider = 2; + dev_info(&dev->dev, "max possible speed %d = %ld/%d kHz\n", + ss->speed_khz, clk_get_rate(ss->clk), ss->divider); + + /* Register for SPI interrupt */ + err = request_irq(ss->irq, stmp_spi_irq, 0, + dev_name(&dev->dev), ss); + if (err) { + dev_dbg(&dev->dev, "request_irq failed, %d\n", err); + goto out_release_hw; + } + + /* ..and shared interrupt for all SSP controllers */ + err = request_irq(ss->err_irq, stmp_spi_irq_err, IRQF_SHARED, + dev_name(&dev->dev), ss); + if (err) { + dev_dbg(&dev->dev, "request_irq(error) failed, %d\n", err); + goto out_free_irq; + } + + err = spi_register_master(master); + if (err) { + dev_dbg(&dev->dev, "cannot register spi master, %d\n", err); + goto out_free_irq_2; + } + dev_info(&dev->dev, "at (mapped) 0x%08X, irq=%d, bus %d, %s mode\n", + (u32)ss->regs, ss->irq, master->bus_num, + pio ? "PIO" : "DMA"); + return 0; + +out_free_irq_2: + free_irq(ss->err_irq, ss); +out_free_irq: + free_irq(ss->irq, ss); +out_free_dma_desc: + stmp3xxx_dma_free_command(ss->dma, &ss->d); +out_free_dma: + stmp3xxx_dma_release(ss->dma); +out_release_hw: + stmp_spi_release_hw(ss); +out_put_master: + if (ss->workqueue) + destroy_workqueue(ss->workqueue); + if (ss->regs) + iounmap(ss->regs); + platform_set_drvdata(dev, NULL); + spi_master_put(master); +out0: + return err; +} + +static int __devexit stmp_spi_remove(struct platform_device *dev) +{ + struct stmp_spi *ss; + struct spi_master *master; + + master = platform_get_drvdata(dev); + if (master == NULL) + goto out0; + ss = spi_master_get_devdata(master); + + spi_unregister_master(master); + + free_irq(ss->err_irq, ss); + free_irq(ss->irq, ss); + stmp3xxx_dma_free_command(ss->dma, &ss->d); + stmp3xxx_dma_release(ss->dma); + stmp_spi_release_hw(ss); + destroy_workqueue(ss->workqueue); + iounmap(ss->regs); + spi_master_put(master); + platform_set_drvdata(dev, NULL); +out0: + return 0; +} + +#ifdef CONFIG_PM +static int stmp_spi_suspend(struct platform_device *pdev, pm_message_t pmsg) +{ + struct stmp_spi *ss; + struct spi_master *master; + + master = platform_get_drvdata(pdev); + ss = spi_master_get_devdata(master); + + ss->saved_timings = readl(HW_SSP_TIMING + ss->regs); + clk_disable(ss->clk); + + return 0; +} + +static int stmp_spi_resume(struct platform_device *pdev) +{ + struct stmp_spi *ss; + struct spi_master *master; + + master = platform_get_drvdata(pdev); + ss = spi_master_get_devdata(master); + + clk_enable(ss->clk); + stmp3xxx_reset_block(ss->regs, false); + writel(ss->saved_timings, ss->regs + HW_SSP_TIMING); + + return 0; +} + +#else +#define stmp_spi_suspend NULL +#define stmp_spi_resume NULL +#endif + +static struct platform_driver stmp_spi_driver = { + .probe = stmp_spi_probe, + .remove = __devexit_p(stmp_spi_remove), + .driver = { + .name = "stmp3xxx_ssp", + .owner = THIS_MODULE, + }, + .suspend = stmp_spi_suspend, + .resume = stmp_spi_resume, +}; + +static int __init stmp_spi_init(void) +{ + return platform_driver_register(&stmp_spi_driver); +} + +static void __exit stmp_spi_exit(void) +{ + platform_driver_unregister(&stmp_spi_driver); +} + +module_init(stmp_spi_init); +module_exit(stmp_spi_exit); +module_param(pio, int, S_IRUGO); +module_param(clock, int, S_IRUGO); +MODULE_AUTHOR("dmitry pervushin <dpervushin@embeddedalley.com>"); +MODULE_DESCRIPTION("STMP3xxx SPI/SSP driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/spi/spidev.c b/drivers/spi/spidev.c index 606e7a40a8da..5d23983f02fc 100644 --- a/drivers/spi/spidev.c +++ b/drivers/spi/spidev.c @@ -537,7 +537,7 @@ static int spidev_release(struct inode *inode, struct file *filp) return status; } -static struct file_operations spidev_fops = { +static const struct file_operations spidev_fops = { .owner = THIS_MODULE, /* REVISIT switch to aio primitives, so that userspace * gets more complete API coverage. It'll simplify things @@ -688,3 +688,4 @@ module_exit(spidev_exit); MODULE_AUTHOR("Andrea Paterniani, <a.paterniani@swapp-eng.it>"); MODULE_DESCRIPTION("User mode SPI device interface"); MODULE_LICENSE("GPL"); +MODULE_ALIAS("spi:spidev"); diff --git a/drivers/spi/tle62x0.c b/drivers/spi/tle62x0.c index 455991fbe28f..bf9540f5fb98 100644 --- a/drivers/spi/tle62x0.c +++ b/drivers/spi/tle62x0.c @@ -329,3 +329,4 @@ module_exit(tle62x0_exit); MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>"); MODULE_DESCRIPTION("TLE62x0 SPI driver"); MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("spi:tle62x0"); diff --git a/drivers/ssb/scan.c b/drivers/ssb/scan.c index b74212d698c7..e8b89e8ac9bd 100644 --- a/drivers/ssb/scan.c +++ b/drivers/ssb/scan.c @@ -162,6 +162,8 @@ static u8 chipid_to_nrcores(u16 chipid) static u32 scan_read32(struct ssb_bus *bus, u8 current_coreidx, u16 offset) { + u32 lo, hi; + switch (bus->bustype) { case SSB_BUSTYPE_SSB: offset += current_coreidx * SSB_CORE_SIZE; @@ -174,7 +176,9 @@ static u32 scan_read32(struct ssb_bus *bus, u8 current_coreidx, offset -= 0x800; } else ssb_pcmcia_switch_segment(bus, 0); - break; + lo = readw(bus->mmio + offset); + hi = readw(bus->mmio + offset + 2); + return lo | (hi << 16); case SSB_BUSTYPE_SDIO: offset += current_coreidx * SSB_CORE_SIZE; return ssb_sdio_scan_read32(bus, offset); diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig index 82b34893e5b5..d21b3469f6d7 100644 --- a/drivers/staging/Kconfig +++ b/drivers/staging/Kconfig @@ -59,8 +59,6 @@ source "drivers/staging/echo/Kconfig" source "drivers/staging/poch/Kconfig" -source "drivers/staging/agnx/Kconfig" - source "drivers/staging/otus/Kconfig" source "drivers/staging/rt2860/Kconfig" @@ -95,8 +93,6 @@ source "drivers/staging/dst/Kconfig" source "drivers/staging/pohmelfs/Kconfig" -source "drivers/staging/stlc45xx/Kconfig" - source "drivers/staging/b3dfg/Kconfig" source "drivers/staging/phison/Kconfig" @@ -117,8 +113,6 @@ source "drivers/staging/vt6655/Kconfig" source "drivers/staging/vt6656/Kconfig" -source "drivers/staging/cpc-usb/Kconfig" - source "drivers/staging/udlfb/Kconfig" source "drivers/staging/hv/Kconfig" @@ -131,7 +125,5 @@ source "drivers/staging/sep/Kconfig" source "drivers/staging/iio/Kconfig" -source "drivers/staging/cowloop/Kconfig" - endif # !STAGING_EXCLUDE_BUILD endif # STAGING diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile index b1cad0d9ba72..8cbf1aebea2e 100644 --- a/drivers/staging/Makefile +++ b/drivers/staging/Makefile @@ -12,7 +12,6 @@ obj-$(CONFIG_W35UND) += winbond/ obj-$(CONFIG_PRISM2_USB) += wlan-ng/ obj-$(CONFIG_ECHO) += echo/ obj-$(CONFIG_POCH) += poch/ -obj-$(CONFIG_AGNX) += agnx/ obj-$(CONFIG_OTUS) += otus/ obj-$(CONFIG_RT2860) += rt2860/ obj-$(CONFIG_RT2870) += rt2870/ @@ -30,7 +29,6 @@ obj-$(CONFIG_ANDROID) += android/ obj-$(CONFIG_ANDROID) += dream/ obj-$(CONFIG_DST) += dst/ obj-$(CONFIG_POHMELFS) += pohmelfs/ -obj-$(CONFIG_STLC45XX) += stlc45xx/ obj-$(CONFIG_B3DFG) += b3dfg/ obj-$(CONFIG_IDE_PHISON) += phison/ obj-$(CONFIG_PLAN9AUTH) += p9auth/ @@ -40,11 +38,9 @@ obj-$(CONFIG_USB_SERIAL_QUATECH_USB2) += quatech_usb2/ obj-$(CONFIG_OCTEON_ETHERNET) += octeon/ obj-$(CONFIG_VT6655) += vt6655/ obj-$(CONFIG_VT6656) += vt6656/ -obj-$(CONFIG_USB_CPC) += cpc-usb/ obj-$(CONFIG_FB_UDL) += udlfb/ obj-$(CONFIG_HYPERV) += hv/ obj-$(CONFIG_VME_BUS) += vme/ obj-$(CONFIG_RAR_REGISTER) += rar/ obj-$(CONFIG_DX_SEP) += sep/ obj-$(CONFIG_IIO) += iio/ -obj-$(CONFIG_COWLOOP) += cowloop/ diff --git a/drivers/staging/agnx/Kconfig b/drivers/staging/agnx/Kconfig deleted file mode 100644 index 7f43549e36dd..000000000000 --- a/drivers/staging/agnx/Kconfig +++ /dev/null @@ -1,5 +0,0 @@ -config AGNX - tristate "Wireless Airgo AGNX support" - depends on WLAN_80211 && MAC80211 - ---help--- - This is an experimental driver for Airgo AGNX00 wireless chip. diff --git a/drivers/staging/agnx/Makefile b/drivers/staging/agnx/Makefile deleted file mode 100644 index 1216564a312d..000000000000 --- a/drivers/staging/agnx/Makefile +++ /dev/null @@ -1,8 +0,0 @@ -obj-$(CONFIG_AGNX) += agnx.o - -agnx-objs := rf.o \ - pci.o \ - xmit.o \ - table.o \ - sta.o \ - phy.o diff --git a/drivers/staging/agnx/TODO b/drivers/staging/agnx/TODO deleted file mode 100644 index 89bec74318aa..000000000000 --- a/drivers/staging/agnx/TODO +++ /dev/null @@ -1,22 +0,0 @@ -2008 7/18 - -The RX has can't receive OFDM packet correctly, -Guess it need be do RX calibrate. - - -before 2008 3/1 - -1: The RX get too much "CRC failed" pakets, it make the card work very unstable, -2: After running a while, the card will get infinity "RX Frame" and "Error" -interrupt, not know the root reason so far, try to fix it -3: Using two tx queue txd and txm but not only txm. -4: Set the hdr correctly. -5: Try to do recalibrate correvtly -6: To support G mode in future -7: Fix the mac address can't be readed and set correctly in BE machine. -8: Fix include and exclude FCS in promisous mode and manage mode -9: Using sta_notify to notice sta change -10: Turn on frame reception at the end of start -11: Guess the card support HW_MULTICAST_FILTER -12: The tx process should be implment atomic? -13: Using mac80211 function to control the TX&RX LED. diff --git a/drivers/staging/agnx/agnx.h b/drivers/staging/agnx/agnx.h deleted file mode 100644 index 3963d2597a11..000000000000 --- a/drivers/staging/agnx/agnx.h +++ /dev/null @@ -1,156 +0,0 @@ -#ifndef AGNX_H_ -#define AGNX_H_ - -#include <linux/io.h> - -#include "xmit.h" - -#define PFX KBUILD_MODNAME ": " - -static inline u32 agnx_read32(void __iomem *mem_region, u32 offset) -{ - return ioread32(mem_region + offset); -} - -static inline void agnx_write32(void __iomem *mem_region, u32 offset, u32 val) -{ - iowrite32(val, mem_region + offset); -} - -/* static const struct ieee80211_rate agnx_rates_80211b[] = { */ -/* { .rate = 10, */ -/* .val = 0xa, */ -/* .flags = IEEE80211_RATE_CCK }, */ -/* { .rate = 20, */ -/* .val = 0x14, */ -/* .hw_value = -0x14, */ -/* .flags = IEEE80211_RATE_CCK_2 }, */ -/* { .rate = 55, */ -/* .val = 0x37, */ -/* .val2 = -0x37, */ -/* .flags = IEEE80211_RATE_CCK_2 }, */ -/* { .rate = 110, */ -/* .val = 0x6e, */ -/* .val2 = -0x6e, */ -/* .flags = IEEE80211_RATE_CCK_2 } */ -/* }; */ - - -static const struct ieee80211_rate agnx_rates_80211g[] = { -/* { .bitrate = 10, .hw_value = 1, .flags = IEEE80211_RATE_SHORT_PREAMBLE }, */ -/* { .bitrate = 20, .hw_value = 2, .flags = IEEE80211_RATE_SHORT_PREAMBLE }, */ -/* { .bitrate = 55, .hw_value = 3, .flags = IEEE80211_RATE_SHORT_PREAMBLE }, */ -/* { .bitrate = 110, .hw_value = 4, .flags = IEEE80211_RATE_SHORT_PREAMBLE }, */ - { .bitrate = 10, .hw_value = 1, }, - { .bitrate = 20, .hw_value = 2, }, - { .bitrate = 55, .hw_value = 3, }, - { .bitrate = 110, .hw_value = 4,}, - - { .bitrate = 60, .hw_value = 0xB, }, - { .bitrate = 90, .hw_value = 0xF, }, - { .bitrate = 120, .hw_value = 0xA }, - { .bitrate = 180, .hw_value = 0xE, }, -/* { .bitrate = 240, .hw_value = 0xd, }, */ - { .bitrate = 360, .hw_value = 0xD, }, - { .bitrate = 480, .hw_value = 0x8, }, - { .bitrate = 540, .hw_value = 0xC, }, -}; - -static const struct ieee80211_channel agnx_channels[] = { - { .center_freq = 2412, .hw_value = 1, }, - { .center_freq = 2417, .hw_value = 2, }, - { .center_freq = 2422, .hw_value = 3, }, - { .center_freq = 2427, .hw_value = 4, }, - { .center_freq = 2432, .hw_value = 5, }, - { .center_freq = 2437, .hw_value = 6, }, - { .center_freq = 2442, .hw_value = 7, }, - { .center_freq = 2447, .hw_value = 8, }, - { .center_freq = 2452, .hw_value = 9, }, - { .center_freq = 2457, .hw_value = 10, }, - { .center_freq = 2462, .hw_value = 11, }, - { .center_freq = 2467, .hw_value = 12, }, - { .center_freq = 2472, .hw_value = 13, }, - { .center_freq = 2484, .hw_value = 14, }, -}; - -#define NUM_DRIVE_MODES 2 -/* Agnx operate mode */ -enum { - AGNX_MODE_80211A, - AGNX_MODE_80211A_OOB, - AGNX_MODE_80211A_MIMO, - AGNX_MODE_80211B_SHORT, - AGNX_MODE_80211B_LONG, - AGNX_MODE_80211G, - AGNX_MODE_80211G_OOB, - AGNX_MODE_80211G_MIMO, -}; - -enum { - AGNX_UNINIT, - AGNX_START, - AGNX_STOP, -}; - -struct agnx_priv { - struct pci_dev *pdev; - struct ieee80211_hw *hw; - - spinlock_t lock; - struct mutex mutex; - unsigned int init_status; - - void __iomem *ctl; /* pointer to base ram address */ - void __iomem *data; /* pointer to mem region #2 */ - - struct agnx_ring rx; - struct agnx_ring txm; - struct agnx_ring txd; - - /* Need volatile? */ - u32 irq_status; - - struct delayed_work periodic_work; /* Periodic tasks like recalibrate */ - struct ieee80211_low_level_stats stats; - - /* unsigned int phymode; */ - int mode; - int channel; - u8 bssid[ETH_ALEN]; - - u8 mac_addr[ETH_ALEN]; - u8 revid; - - struct ieee80211_supported_band band; -}; - - -#define AGNX_CHAINS_MAX 6 -#define AGNX_PERIODIC_DELAY 60000 /* unit: ms */ -#define LOCAL_STAID 0 /* the station entry for the card itself */ -#define BSSID_STAID 1 /* the station entry for the bsssid AP */ -#define spi_delay() udelay(40) -#define eeprom_delay() udelay(40) -#define routing_table_delay() udelay(50) - -/* PDU pool MEM region #2 */ -#define AGNX_PDUPOOL 0x40000 /* PDU pool */ -#define AGNX_PDUPOOL_SIZE 0x8000 /* PDU pool size*/ -#define AGNX_PDU_TX_WQ 0x41000 /* PDU list TX workqueue */ -#define AGNX_PDU_FREE 0x41800 /* Free Pool */ -#define PDU_SIZE 0x80 /* Free Pool node size */ -#define PDU_FREE_CNT 0xd0 /* Free pool node count */ - - -/* RF stuffs */ -extern void rf_chips_init(struct agnx_priv *priv); -extern void spi_rc_write(void __iomem *mem_region, u32 chip_ids, u32 sw); -extern void calibrate_oscillator(struct agnx_priv *priv); -extern void do_calibration(struct agnx_priv *priv); -extern void antenna_calibrate(struct agnx_priv *priv); -extern void __antenna_calibrate(struct agnx_priv *priv); -extern void print_offsets(struct agnx_priv *priv); -extern int agnx_set_channel(struct agnx_priv *priv, unsigned int channel); - - -#endif /* AGNX_H_ */ diff --git a/drivers/staging/agnx/debug.h b/drivers/staging/agnx/debug.h deleted file mode 100644 index 7947f327a214..000000000000 --- a/drivers/staging/agnx/debug.h +++ /dev/null @@ -1,416 +0,0 @@ -#ifndef AGNX_DEBUG_H_ -#define AGNX_DEBUG_H_ - -#include "agnx.h" -#include "phy.h" -#include "sta.h" -#include "xmit.h" - -#define AGNX_TRACE printk(KERN_ERR PFX "function:%s line:%d\n", __func__, __LINE__) - -#define PRINTK_LE16(prefix, var) printk(KERN_DEBUG PFX #prefix ": " #var " 0x%.4x\n", le16_to_cpu(var)) -#define PRINTK_LE32(prefix, var) printk(KERN_DEBUG PFX #prefix ": " #var " 0x%.8x\n", le32_to_cpu(var)) -#define PRINTK_U8(prefix, var) printk(KERN_DEBUG PFX #prefix ": " #var " 0x%.2x\n", var) -#define PRINTK_BE16(prefix, var) printk(KERN_DEBUG PFX #prefix ": " #var " 0x%.4x\n", be16_to_cpu(var)) -#define PRINTK_BE32(prefix, var) printk(KERN_DEBUG PFX #prefix ": " #var " 0x%.8x\n", be32_to_cpu(var)) -#define PRINTK_BITS(prefix, field) printk(KERN_DEBUG PFX #prefix ": " #field ": 0x%x\n", (reg & field) >> field##_SHIFT) - -static inline void agnx_bug(char *reason) -{ - printk(KERN_ERR PFX "%s\n", reason); - BUG(); -} - -static inline void agnx_print_desc(struct agnx_desc *desc) -{ - u32 reg = be32_to_cpu(desc->frag); - - PRINTK_BITS(DESC, PACKET_LEN); - - if (reg & FIRST_FRAG) { - PRINTK_BITS(DESC, FIRST_PACKET_MASK); - PRINTK_BITS(DESC, FIRST_RESERV2); - PRINTK_BITS(DESC, FIRST_TKIP_ERROR); - PRINTK_BITS(DESC, FIRST_TKIP_PACKET); - PRINTK_BITS(DESC, FIRST_RESERV1); - PRINTK_BITS(DESC, FIRST_FRAG_LEN); - } else { - PRINTK_BITS(DESC, SUB_RESERV2); - PRINTK_BITS(DESC, SUB_TKIP_ERROR); - PRINTK_BITS(DESC, SUB_TKIP_PACKET); - PRINTK_BITS(DESC, SUB_RESERV1); - PRINTK_BITS(DESC, SUB_FRAG_LEN); - } - - PRINTK_BITS(DESC, FIRST_FRAG); - PRINTK_BITS(DESC, LAST_FRAG); - PRINTK_BITS(DESC, OWNER); -} - - -static inline void dump_ieee80211b_phy_hdr(__be32 _11b0, __be32 _11b1) -{ - -} - -static inline void agnx_print_hdr(struct agnx_hdr *hdr) -{ - u32 reg; - int i; - - reg = be32_to_cpu(hdr->reg0); - PRINTK_BITS(HDR, RTS); - PRINTK_BITS(HDR, MULTICAST); - PRINTK_BITS(HDR, ACK); - PRINTK_BITS(HDR, TM); - PRINTK_BITS(HDR, RELAY); - PRINTK_BITS(HDR, REVISED_FCS); - PRINTK_BITS(HDR, NEXT_BUFFER_ADDR); - - reg = be32_to_cpu(hdr->reg1); - PRINTK_BITS(HDR, MAC_HDR_LEN); - PRINTK_BITS(HDR, DURATION_OVERIDE); - PRINTK_BITS(HDR, PHY_HDR_OVERIDE); - PRINTK_BITS(HDR, CRC_FAIL); - PRINTK_BITS(HDR, SEQUENCE_NUMBER); - PRINTK_BITS(HDR, BUFF_HEAD_ADDR); - - reg = be32_to_cpu(hdr->reg2); - PRINTK_BITS(HDR, PDU_COUNT); - PRINTK_BITS(HDR, WEP_KEY); - PRINTK_BITS(HDR, USES_WEP_KEY); - PRINTK_BITS(HDR, KEEP_ALIVE); - PRINTK_BITS(HDR, BUFF_TAIL_ADDR); - - reg = be32_to_cpu(hdr->reg3); - PRINTK_BITS(HDR, CTS_11G); - PRINTK_BITS(HDR, RTS_11G); - PRINTK_BITS(HDR, FRAG_SIZE); - PRINTK_BITS(HDR, PAYLOAD_LEN); - PRINTK_BITS(HDR, FRAG_NUM); - - reg = be32_to_cpu(hdr->reg4); - PRINTK_BITS(HDR, RELAY_STAID); - PRINTK_BITS(HDR, STATION_ID); - PRINTK_BITS(HDR, WORKQUEUE_ID); - - reg = be32_to_cpu(hdr->reg5); - /* printf the route flag */ - PRINTK_BITS(HDR, ROUTE_HOST); - PRINTK_BITS(HDR, ROUTE_CARD_CPU); - PRINTK_BITS(HDR, ROUTE_ENCRYPTION); - PRINTK_BITS(HDR, ROUTE_TX); - PRINTK_BITS(HDR, ROUTE_RX1); - PRINTK_BITS(HDR, ROUTE_RX2); - PRINTK_BITS(HDR, ROUTE_COMPRESSION); - - PRINTK_BE32(HDR, hdr->_11g0); - PRINTK_BE32(HDR, hdr->_11g1); - PRINTK_BE32(HDR, hdr->_11b0); - PRINTK_BE32(HDR, hdr->_11b1); - - dump_ieee80211b_phy_hdr(hdr->_11b0, hdr->_11b1); - - /* Fixme */ - for (i = 0; i < ARRAY_SIZE(hdr->mac_hdr); i++) { - if (i == 0) - printk(KERN_DEBUG PFX "IEEE80211 HDR: "); - printk("%.2x ", hdr->mac_hdr[i]); - if (i + 1 == ARRAY_SIZE(hdr->mac_hdr)) - printk("\n"); - } - - PRINTK_BE16(HDR, hdr->rts_duration); - PRINTK_BE16(HDR, hdr->last_duration); - PRINTK_BE16(HDR, hdr->sec_last_duration); - PRINTK_BE16(HDR, hdr->other_duration); - PRINTK_BE16(HDR, hdr->tx_other_duration); - PRINTK_BE16(HDR, hdr->last_11g_len); - PRINTK_BE16(HDR, hdr->other_11g_len); - PRINTK_BE16(HDR, hdr->last_11b_len); - PRINTK_BE16(HDR, hdr->other_11b_len); - - /* FIXME */ - reg = be16_to_cpu(hdr->reg6); - PRINTK_BITS(HDR, MBF); - PRINTK_BITS(HDR, RSVD4); - - PRINTK_BE16(HDR, hdr->rx_frag_stat); - - PRINTK_BE32(HDR, hdr->time_stamp); - PRINTK_BE32(HDR, hdr->phy_stats_hi); - PRINTK_BE32(HDR, hdr->phy_stats_lo); - PRINTK_BE32(HDR, hdr->mic_key0); - PRINTK_BE32(HDR, hdr->mic_key1); -} /* agnx_print_hdr */ - - -static inline void agnx_print_rx_hdr(struct agnx_hdr *hdr) -{ - agnx_print_hdr(hdr); - - PRINTK_BE16(HDR, hdr->rx.rx_packet_duration); - PRINTK_BE16(HDR, hdr->rx.replay_cnt); - - PRINTK_U8(HDR, hdr->rx_channel); -} - -static inline void agnx_print_tx_hdr(struct agnx_hdr *hdr) -{ - agnx_print_hdr(hdr); - - PRINTK_U8(HDR, hdr->tx.long_retry_limit); - PRINTK_U8(HDR, hdr->tx.short_retry_limit); - PRINTK_U8(HDR, hdr->tx.long_retry_cnt); - PRINTK_U8(HDR, hdr->tx.short_retry_cnt); - - PRINTK_U8(HDR, hdr->rx_channel); -} - -static inline void -agnx_print_sta_power(struct agnx_priv *priv, unsigned int sta_idx) -{ - struct agnx_sta_power power; - u32 reg; - - get_sta_power(priv, &power, sta_idx); - - reg = le32_to_cpu(power.reg); - PRINTK_BITS(STA_POWER, SIGNAL); - PRINTK_BITS(STA_POWER, RATE); - PRINTK_BITS(STA_POWER, TIFS); - PRINTK_BITS(STA_POWER, EDCF); - PRINTK_BITS(STA_POWER, CHANNEL_BOND); - PRINTK_BITS(STA_POWER, PHY_MODE); - PRINTK_BITS(STA_POWER, POWER_LEVEL); - PRINTK_BITS(STA_POWER, NUM_TRANSMITTERS); -} - -static inline void -agnx_print_sta_tx_wq(struct agnx_priv *priv, unsigned int sta_idx, unsigned int wq_idx) -{ - struct agnx_sta_tx_wq tx_wq; - u32 reg; - - get_sta_tx_wq(priv, &tx_wq, sta_idx, wq_idx); - - reg = le32_to_cpu(tx_wq.reg0); - PRINTK_BITS(STA_TX_WQ, TAIL_POINTER); - PRINTK_BITS(STA_TX_WQ, HEAD_POINTER_LOW); - - reg = le32_to_cpu(tx_wq.reg3); - PRINTK_BITS(STA_TX_WQ, HEAD_POINTER_HIGH); - PRINTK_BITS(STA_TX_WQ, ACK_POINTER_LOW); - - reg = le32_to_cpu(tx_wq.reg1); - PRINTK_BITS(STA_TX_WQ, ACK_POINTER_HIGH); - PRINTK_BITS(STA_TX_WQ, HEAD_TIMOUT_TAIL_PACK_CNT); - PRINTK_BITS(STA_TX_WQ, ACK_TIMOUT_TAIL_PACK_CNT); - - reg = le32_to_cpu(tx_wq.reg2); - PRINTK_BITS(STA_TX_WQ, HEAD_TIMOUT_WIN_LIM_BYTE_CNT); - PRINTK_BITS(STA_TX_WQ, HEAD_TIMOUT_WIN_LIM_FRAG_CNT); - PRINTK_BITS(STA_TX_WQ, WORK_QUEUE_ACK_TYPE); - PRINTK_BITS(STA_TX_WQ, WORK_QUEUE_VALID); -} - -static inline void agnx_print_sta_traffic(struct agnx_sta_traffic *traffic) -{ - u32 reg; - - reg = le32_to_cpu(traffic->reg0); - PRINTK_BITS(STA_TRAFFIC, ACK_TIMOUT_CNT); - PRINTK_BITS(STA_TRAFFIC, TRAFFIC_ACK_TYPE); - PRINTK_BITS(STA_TRAFFIC, NEW_PACKET); - PRINTK_BITS(STA_TRAFFIC, TRAFFIC_VALID); - PRINTK_BITS(STA_TRAFFIC, RX_HDR_DESC_POINTER); - - reg = le32_to_cpu(traffic->reg1); - PRINTK_BITS(STA_TRAFFIC, RX_PACKET_TIMESTAMP); - PRINTK_BITS(STA_TRAFFIC, TRAFFIC_RESERVED); - PRINTK_BITS(STA_TRAFFIC, SV); - PRINTK_BITS(STA_TRAFFIC, RX_SEQUENCE_NUM); - - PRINTK_LE32(STA_TRAFFIC, traffic->tx_replay_cnt_low); - - PRINTK_LE16(STA_TRAFFIC, traffic->tx_replay_cnt_high); - PRINTK_LE16(STA_TRAFFIC, traffic->rx_replay_cnt_high); - - PRINTK_LE32(STA_TRAFFIC, traffic->rx_replay_cnt_low); -} - -static inline void agnx_print_sta(struct agnx_priv *priv, unsigned int sta_idx) -{ - struct agnx_sta station; - struct agnx_sta *sta = &station; - u32 reg; - unsigned int i; - - get_sta(priv, sta, sta_idx); - - for (i = 0; i < 4; i++) - PRINTK_LE32(STA, sta->tx_session_keys[i]); - for (i = 0; i < 4; i++) - PRINTK_LE32(STA, sta->rx_session_keys[i]); - - reg = le32_to_cpu(sta->reg); - PRINTK_BITS(STA, ID_1); - PRINTK_BITS(STA, ID_0); - PRINTK_BITS(STA, ENABLE_CONCATENATION); - PRINTK_BITS(STA, ENABLE_DECOMPRESSION); - PRINTK_BITS(STA, STA_RESERVED); - PRINTK_BITS(STA, EAP); - PRINTK_BITS(STA, ED_NULL); - PRINTK_BITS(STA, ENCRYPTION_POLICY); - PRINTK_BITS(STA, DEFINED_KEY_ID); - PRINTK_BITS(STA, FIXED_KEY); - PRINTK_BITS(STA, KEY_VALID); - PRINTK_BITS(STA, STATION_VALID); - - PRINTK_LE32(STA, sta->tx_aes_blks_unicast); - PRINTK_LE32(STA, sta->rx_aes_blks_unicast); - - PRINTK_LE16(STA, sta->aes_format_err_unicast_cnt); - PRINTK_LE16(STA, sta->aes_replay_unicast); - - PRINTK_LE16(STA, sta->aes_decrypt_err_unicast); - PRINTK_LE16(STA, sta->aes_decrypt_err_default); - - PRINTK_LE16(STA, sta->single_retry_packets); - PRINTK_LE16(STA, sta->failed_tx_packets); - - PRINTK_LE16(STA, sta->muti_retry_packets); - PRINTK_LE16(STA, sta->ack_timeouts); - - PRINTK_LE16(STA, sta->frag_tx_cnt); - PRINTK_LE16(STA, sta->rts_brq_sent); - - PRINTK_LE16(STA, sta->tx_packets); - PRINTK_LE16(STA, sta->cts_back_timeout); - - PRINTK_LE32(STA, sta->phy_stats_high); - PRINTK_LE32(STA, sta->phy_stats_low); - - /* for (i = 0; i < 8; i++) */ - agnx_print_sta_traffic(sta->traffic + 0); - - PRINTK_LE16(STA, sta->traffic_class0_frag_success); - PRINTK_LE16(STA, sta->traffic_class1_frag_success); - PRINTK_LE16(STA, sta->traffic_class2_frag_success); - PRINTK_LE16(STA, sta->traffic_class3_frag_success); - PRINTK_LE16(STA, sta->traffic_class4_frag_success); - PRINTK_LE16(STA, sta->traffic_class5_frag_success); - PRINTK_LE16(STA, sta->traffic_class6_frag_success); - PRINTK_LE16(STA, sta->traffic_class7_frag_success); - - PRINTK_LE16(STA, sta->num_frag_non_prime_rates); - PRINTK_LE16(STA, sta->ack_timeout_non_prime_rates); -} - - -static inline void dump_ieee80211_hdr(struct ieee80211_hdr *hdr, char *tag) -{ - u16 fctl; - int hdrlen; - - fctl = le16_to_cpu(hdr->frame_control); - switch (fctl & IEEE80211_FCTL_FTYPE) { - case IEEE80211_FTYPE_DATA: - printk(PFX "%s DATA ", tag); - break; - case IEEE80211_FTYPE_CTL: - printk(PFX "%s CTL ", tag); - break; - case IEEE80211_FTYPE_MGMT: - printk(PFX "%s MGMT ", tag); - switch (fctl & IEEE80211_FCTL_STYPE) { - case IEEE80211_STYPE_ASSOC_REQ: - printk("SubType: ASSOC_REQ "); - break; - case IEEE80211_STYPE_ASSOC_RESP: - printk("SubType: ASSOC_RESP "); - break; - case IEEE80211_STYPE_REASSOC_REQ: - printk("SubType: REASSOC_REQ "); - break; - case IEEE80211_STYPE_REASSOC_RESP: - printk("SubType: REASSOC_RESP "); - break; - case IEEE80211_STYPE_PROBE_REQ: - printk("SubType: PROBE_REQ "); - break; - case IEEE80211_STYPE_PROBE_RESP: - printk("SubType: PROBE_RESP "); - break; - case IEEE80211_STYPE_BEACON: - printk("SubType: BEACON "); - break; - case IEEE80211_STYPE_ATIM: - printk("SubType: ATIM "); - break; - case IEEE80211_STYPE_DISASSOC: - printk("SubType: DISASSOC "); - break; - case IEEE80211_STYPE_AUTH: - printk("SubType: AUTH "); - break; - case IEEE80211_STYPE_DEAUTH: - printk("SubType: DEAUTH "); - break; - case IEEE80211_STYPE_ACTION: - printk("SubType: ACTION "); - break; - default: - printk("SubType: Unknow\n"); - } - break; - default: - printk(PFX "%s Packet type: Unknow\n", tag); - } - - hdrlen = ieee80211_hdrlen(fctl); - - if (hdrlen >= 4) - printk("FC=0x%04x DUR=0x%04x", - fctl, le16_to_cpu(hdr->duration_id)); - if (hdrlen >= 10) - printk(" A1=%pM", hdr->addr1); - if (hdrlen >= 16) - printk(" A2=%pM", hdr->addr2); - if (hdrlen >= 24) - printk(" A3=%pM", hdr->addr3); - if (hdrlen >= 30) - printk(" A4=%pM", hdr->addr4); - printk("\n"); -} - -static inline void dump_txm_registers(struct agnx_priv *priv) -{ - void __iomem *ctl = priv->ctl; - int i; - for (i = 0; i <= 0x1e8; i += 4) - printk(KERN_DEBUG PFX "TXM: %x---> 0x%.8x\n", i, ioread32(ctl + i)); -} -static inline void dump_rxm_registers(struct agnx_priv *priv) -{ - void __iomem *ctl = priv->ctl; - int i; - for (i = 0; i <= 0x108; i += 4) - printk(KERN_DEBUG PFX "RXM: %x---> 0x%.8x\n", i, ioread32(ctl + 0x2000 + i)); -} -static inline void dump_bm_registers(struct agnx_priv *priv) -{ - void __iomem *ctl = priv->ctl; - int i; - for (i = 0; i <= 0x90; i += 4) - printk(KERN_DEBUG PFX "BM: %x---> 0x%.8x\n", i, ioread32(ctl + 0x2c00 + i)); -} -static inline void dump_cir_registers(struct agnx_priv *priv) -{ - void __iomem *ctl = priv->ctl; - int i; - for (i = 0; i <= 0xb8; i += 4) - printk(KERN_DEBUG PFX "CIR: %x---> 0x%.8x\n", i, ioread32(ctl + 0x3000 + i)); -} - -#endif /* AGNX_DEBUG_H_ */ diff --git a/drivers/staging/agnx/pci.c b/drivers/staging/agnx/pci.c deleted file mode 100644 index 32b5489456a8..000000000000 --- a/drivers/staging/agnx/pci.c +++ /dev/null @@ -1,635 +0,0 @@ -/** - * Airgo MIMO wireless driver - * - * Copyright (c) 2007 Li YanBo <dreamfly281@gmail.com> - - * Thanks for Jeff Williams <angelbane@gmail.com> do reverse engineer - * works and published the SPECS at http://airgo.wdwconsulting.net/mymoin - - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#include <linux/init.h> -#include <linux/etherdevice.h> -#include <linux/pci.h> -#include <linux/delay.h> - -#include "agnx.h" -#include "debug.h" -#include "xmit.h" -#include "phy.h" - -MODULE_AUTHOR("Li YanBo <dreamfly281@gmail.com>"); -MODULE_DESCRIPTION("Airgo MIMO PCI wireless driver"); -MODULE_LICENSE("GPL"); - -static struct pci_device_id agnx_pci_id_tbl[] __devinitdata = { - { PCI_DEVICE(0x17cb, 0x0001) }, /* Beklin F5d8010, Netgear WGM511 etc */ - { PCI_DEVICE(0x17cb, 0x0002) }, /* Netgear Wpnt511 */ - { 0 } -}; - -MODULE_DEVICE_TABLE(pci, agnx_pci_id_tbl); - - -static inline void agnx_interrupt_ack(struct agnx_priv *priv, u32 *reason) -{ - void __iomem *ctl = priv->ctl; - u32 reg; - - if (*reason & AGNX_STAT_RX) { - /* Mark complete RX */ - reg = ioread32(ctl + AGNX_CIR_RXCTL); - reg |= 0x4; - iowrite32(reg, ctl + AGNX_CIR_RXCTL); - /* disable Rx interrupt */ - } - if (*reason & AGNX_STAT_TX) { - reg = ioread32(ctl + AGNX_CIR_TXDCTL); - if (reg & 0x4) { - iowrite32(reg, ctl + AGNX_CIR_TXDCTL); - *reason |= AGNX_STAT_TXD; - } - reg = ioread32(ctl + AGNX_CIR_TXMCTL); - if (reg & 0x4) { - iowrite32(reg, ctl + AGNX_CIR_TXMCTL); - *reason |= AGNX_STAT_TXM; - } - } -#if 0 - if (*reason & AGNX_STAT_X) { - reg = ioread32(ctl + AGNX_INT_STAT); - iowrite32(reg, ctl + AGNX_INT_STAT); - /* FIXME reinit interrupt mask */ - reg = 0xc390bf9 & ~IRQ_TX_BEACON; - reg &= ~IRQ_TX_DISABLE; - iowrite32(reg, ctl + AGNX_INT_MASK); - iowrite32(0x800, ctl + AGNX_CIR_BLKCTL); - } -#endif -} /* agnx_interrupt_ack */ - -static irqreturn_t agnx_interrupt_handler(int irq, void *dev_id) -{ - struct ieee80211_hw *dev = dev_id; - struct agnx_priv *priv = dev->priv; - void __iomem *ctl = priv->ctl; - irqreturn_t ret = IRQ_NONE; - u32 irq_reason; - - spin_lock(&priv->lock); - -/* printk(KERN_ERR PFX "Get a interrupt %s\n", __func__); */ - - if (priv->init_status != AGNX_START) - goto out; - - /* FiXME Here has no lock, Is this will lead to race? */ - irq_reason = ioread32(ctl + AGNX_CIR_BLKCTL); - if (!(irq_reason & 0x7)) - goto out; - - ret = IRQ_HANDLED; - priv->irq_status = ioread32(ctl + AGNX_INT_STAT); - -/* printk(PFX "Interrupt reason is 0x%x\n", irq_reason); */ - /* Make sure the txm and txd flags don't conflict with other unknown - interrupt flag, maybe is not necessary */ - irq_reason &= 0xF; - - disable_rx_interrupt(priv); - /* TODO Make sure the card finished initialized */ - agnx_interrupt_ack(priv, &irq_reason); - - if (irq_reason & AGNX_STAT_RX) - handle_rx_irq(priv); - if (irq_reason & AGNX_STAT_TXD) - handle_txd_irq(priv); - if (irq_reason & AGNX_STAT_TXM) - handle_txm_irq(priv); - if (irq_reason & AGNX_STAT_X) - handle_other_irq(priv); - - enable_rx_interrupt(priv); -out: - spin_unlock(&priv->lock); - return ret; -} /* agnx_interrupt_handler */ - - -/* FIXME */ -static int agnx_tx(struct ieee80211_hw *dev, struct sk_buff *skb) -{ - AGNX_TRACE; - return _agnx_tx(dev->priv, skb); -} /* agnx_tx */ - - -static int agnx_get_mac_address(struct agnx_priv *priv) -{ - void __iomem *ctl = priv->ctl; - u32 reg; - AGNX_TRACE; - - /* Attention! directly read the MAC or other date from EEPROM will - lead to cardbus(WGM511) lock up when write to PM PLL register */ - reg = agnx_read32(ctl, 0x3544); - udelay(40); - reg = agnx_read32(ctl, 0x354c); - udelay(50); - /* Get the mac address */ - reg = agnx_read32(ctl, 0x3544); - udelay(40); - - /* HACK */ - reg = cpu_to_le32(reg); - priv->mac_addr[0] = ((u8 *)®)[2]; - priv->mac_addr[1] = ((u8 *)®)[3]; - reg = agnx_read32(ctl, 0x3548); - udelay(50); - *((u32 *)(priv->mac_addr + 2)) = cpu_to_le32(reg); - - if (!is_valid_ether_addr(priv->mac_addr)) { - printk(KERN_WARNING PFX "read mac %pM\n", priv->mac_addr); - printk(KERN_WARNING PFX "Invalid hwaddr! Using random hwaddr\n"); - random_ether_addr(priv->mac_addr); - } - - return 0; -} /* agnx_get_mac_address */ - -static int agnx_alloc_rings(struct agnx_priv *priv) -{ - unsigned int len; - AGNX_TRACE; - - /* Allocate RX/TXM/TXD rings info */ - priv->rx.size = AGNX_RX_RING_SIZE; - priv->txm.size = AGNX_TXM_RING_SIZE; - priv->txd.size = AGNX_TXD_RING_SIZE; - - len = priv->rx.size + priv->txm.size + priv->txd.size; - -/* priv->rx.info = kzalloc(sizeof(struct agnx_info) * len, GFP_KERNEL); */ - priv->rx.info = kzalloc(sizeof(struct agnx_info) * len, GFP_ATOMIC); - if (!priv->rx.info) - return -ENOMEM; - priv->txm.info = priv->rx.info + priv->rx.size; - priv->txd.info = priv->txm.info + priv->txm.size; - - /* Allocate RX/TXM/TXD descriptors */ - priv->rx.desc = pci_alloc_consistent(priv->pdev, sizeof(struct agnx_desc) * len, - &priv->rx.dma); - if (!priv->rx.desc) { - kfree(priv->rx.info); - return -ENOMEM; - } - - priv->txm.desc = priv->rx.desc + priv->rx.size; - priv->txm.dma = priv->rx.dma + sizeof(struct agnx_desc) * priv->rx.size; - priv->txd.desc = priv->txm.desc + priv->txm.size; - priv->txd.dma = priv->txm.dma + sizeof(struct agnx_desc) * priv->txm.size; - - return 0; -} /* agnx_alloc_rings */ - -static void rings_free(struct agnx_priv *priv) -{ - unsigned int len = priv->rx.size + priv->txm.size + priv->txd.size; - unsigned long flags; - AGNX_TRACE; - - spin_lock_irqsave(&priv->lock, flags); - kfree(priv->rx.info); - pci_free_consistent(priv->pdev, sizeof(struct agnx_desc) * len, - priv->rx.desc, priv->rx.dma); - spin_unlock_irqrestore(&priv->lock, flags); -} - -#if 0 -static void agnx_periodic_work_handler(struct work_struct *work) -{ - struct agnx_priv *priv = container_of(work, struct agnx_priv, periodic_work.work); -/* unsigned long flags; */ - unsigned long delay; - - /* fixme: using mutex?? */ -/* spin_lock_irqsave(&priv->lock, flags); */ - - /* TODO Recalibrate*/ -/* calibrate_oscillator(priv); */ -/* antenna_calibrate(priv); */ -/* agnx_send_packet(priv, 997); */ - /* FIXME */ -/* if (debug == 3) */ -/* delay = msecs_to_jiffies(AGNX_PERIODIC_DELAY); */ -/* else */ - delay = msecs_to_jiffies(AGNX_PERIODIC_DELAY); -/* delay = round_jiffies(HZ * 15); */ - - queue_delayed_work(priv->hw->workqueue, &priv->periodic_work, delay); - -/* spin_unlock_irqrestore(&priv->lock, flags); */ -} -#endif - -static int agnx_start(struct ieee80211_hw *dev) -{ - struct agnx_priv *priv = dev->priv; - /* unsigned long delay; */ - int err = 0; - AGNX_TRACE; - - err = agnx_alloc_rings(priv); - if (err) { - printk(KERN_ERR PFX "Can't alloc RX/TXM/TXD rings\n"); - goto out; - } - err = request_irq(priv->pdev->irq, &agnx_interrupt_handler, - IRQF_SHARED, "agnx_pci", dev); - if (err) { - printk(KERN_ERR PFX "Failed to register IRQ handler\n"); - rings_free(priv); - goto out; - } - -/* mdelay(500); */ - - might_sleep(); - agnx_hw_init(priv); - -/* mdelay(500); */ - might_sleep(); - - priv->init_status = AGNX_START; -/* INIT_DELAYED_WORK(&priv->periodic_work, agnx_periodic_work_handler); */ -/* delay = msecs_to_jiffies(AGNX_PERIODIC_DELAY); */ -/* queue_delayed_work(priv->hw->workqueue, &priv->periodic_work, delay); */ -out: - return err; -} /* agnx_start */ - -static void agnx_stop(struct ieee80211_hw *dev) -{ - struct agnx_priv *priv = dev->priv; - AGNX_TRACE; - - priv->init_status = AGNX_STOP; - /* make sure hardware will not generate irq */ - agnx_hw_reset(priv); - free_irq(priv->pdev->irq, dev); -/* flush_workqueue(priv->hw->workqueue); */ -/* cancel_delayed_work_sync(&priv->periodic_work); */ - unfill_rings(priv); - rings_free(priv); -} - -static int agnx_config(struct ieee80211_hw *dev, u32 changed) -{ - struct agnx_priv *priv = dev->priv; - struct ieee80211_conf *conf = &dev->conf; - int channel = ieee80211_frequency_to_channel(conf->channel->center_freq); - AGNX_TRACE; - - spin_lock(&priv->lock); - /* FIXME need priv lock? */ - if (channel != priv->channel) { - priv->channel = channel; - agnx_set_channel(priv, priv->channel); - } - - spin_unlock(&priv->lock); - return 0; -} - -static void agnx_bss_info_changed(struct ieee80211_hw *dev, - struct ieee80211_vif *vif, - struct ieee80211_bss_conf *conf, - u32 changed) -{ - struct agnx_priv *priv = dev->priv; - void __iomem *ctl = priv->ctl; - AGNX_TRACE; - - if (!(changed & BSS_CHANGED_BSSID)) - return; - - spin_lock(&priv->lock); - - if (memcmp(conf->bssid, priv->bssid, ETH_ALEN)) { - agnx_set_bssid(priv, conf->bssid); - memcpy(priv->bssid, conf->bssid, ETH_ALEN); - hash_write(priv, conf->bssid, BSSID_STAID); - sta_init(priv, BSSID_STAID); - /* FIXME needed? */ - sta_power_init(priv, BSSID_STAID); - agnx_write32(ctl, AGNX_BM_MTSM, 0xff & ~0x1); - } - spin_unlock(&priv->lock); -} /* agnx_bss_info_changed */ - - -static void agnx_configure_filter(struct ieee80211_hw *dev, - unsigned int changed_flags, - unsigned int *total_flags, - int mc_count, struct dev_mc_list *mclist) -{ - unsigned int new_flags = 0; - - *total_flags = new_flags; - /* TODO */ -} - -static int agnx_add_interface(struct ieee80211_hw *dev, - struct ieee80211_if_init_conf *conf) -{ - struct agnx_priv *priv = dev->priv; - AGNX_TRACE; - - spin_lock(&priv->lock); - /* FIXME */ - if (priv->mode != NL80211_IFTYPE_MONITOR) - return -EOPNOTSUPP; - - switch (conf->type) { - case NL80211_IFTYPE_STATION: - priv->mode = conf->type; - break; - default: - return -EOPNOTSUPP; - } - - spin_unlock(&priv->lock); - - return 0; -} - -static void agnx_remove_interface(struct ieee80211_hw *dev, - struct ieee80211_if_init_conf *conf) -{ - struct agnx_priv *priv = dev->priv; - AGNX_TRACE; - - /* TODO */ - priv->mode = NL80211_IFTYPE_MONITOR; -} - -static int agnx_get_stats(struct ieee80211_hw *dev, - struct ieee80211_low_level_stats *stats) -{ - struct agnx_priv *priv = dev->priv; - AGNX_TRACE; - spin_lock(&priv->lock); - /* TODO !! */ - memcpy(stats, &priv->stats, sizeof(*stats)); - spin_unlock(&priv->lock); - - return 0; -} - -static u64 agnx_get_tsft(struct ieee80211_hw *dev) -{ - void __iomem *ctl = ((struct agnx_priv *)dev->priv)->ctl; - u32 tsftl; - u64 tsft; - AGNX_TRACE; - - /* FIXME */ - tsftl = ioread32(ctl + AGNX_TXM_TIMESTAMPLO); - tsft = ioread32(ctl + AGNX_TXM_TIMESTAMPHI); - tsft <<= 32; - tsft |= tsftl; - - return tsft; -} - -static int agnx_get_tx_stats(struct ieee80211_hw *dev, - struct ieee80211_tx_queue_stats *stats) -{ - struct agnx_priv *priv = dev->priv; - AGNX_TRACE; - - /* FIXME now we just using txd queue, but should using txm queue too */ - stats[0].len = (priv->txd.idx - priv->txd.idx_sent) / 2; - stats[0].limit = priv->txd.size - 2; - stats[0].count = priv->txd.idx / 2; - - return 0; -} - -static struct ieee80211_ops agnx_ops = { - .tx = agnx_tx, - .start = agnx_start, - .stop = agnx_stop, - .add_interface = agnx_add_interface, - .remove_interface = agnx_remove_interface, - .config = agnx_config, - .bss_info_changed = agnx_bss_info_changed, - .configure_filter = agnx_configure_filter, - .get_stats = agnx_get_stats, - .get_tx_stats = agnx_get_tx_stats, - .get_tsf = agnx_get_tsft -}; - -static void __devexit agnx_pci_remove(struct pci_dev *pdev) -{ - struct ieee80211_hw *dev = pci_get_drvdata(pdev); - struct agnx_priv *priv; - AGNX_TRACE; - - if (!dev) - return; - priv = dev->priv; - ieee80211_unregister_hw(dev); - pci_iounmap(pdev, priv->ctl); - pci_iounmap(pdev, priv->data); - pci_release_regions(pdev); - pci_disable_device(pdev); - - ieee80211_free_hw(dev); -} - -static int __devinit agnx_pci_probe(struct pci_dev *pdev, - const struct pci_device_id *id) -{ - struct ieee80211_hw *dev; - struct agnx_priv *priv; - int err; - - err = pci_enable_device(pdev); - if (err) { - dev_err(&pdev->dev, "can't enable pci device\n"); - return err; - } - - err = pci_request_regions(pdev, "agnx-pci"); - if (err) { - dev_err(&pdev->dev, "can't reserve PCI resources\n"); - return err; - } - - if (pci_set_dma_mask(pdev, DMA_BIT_MASK(32)) || - pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32))) { - dev_err(&pdev->dev, "no suitable DMA available\n"); - err = -EIO; - goto err_free_reg; - } - - pci_set_master(pdev); - - dev = ieee80211_alloc_hw(sizeof(*priv), &agnx_ops); - if (!dev) { - dev_err(&pdev->dev, "ieee80211 alloc failed\n"); - err = -ENOMEM; - goto err_free_reg; - } - priv = dev->priv; - memset(priv, 0, sizeof(*priv)); - priv->mode = NL80211_IFTYPE_MONITOR; - priv->pdev = pdev; - priv->hw = dev; - spin_lock_init(&priv->lock); - priv->init_status = AGNX_UNINIT; - - priv->ctl = pci_iomap(pdev, 0, 0); -/* dev_dbg(&pdev->dev, "MEM1 mapped address is 0x%p\n", priv->ctl); */ - if (!priv->ctl) { - dev_err(&pdev->dev, "can't map device memory\n"); - err = -ENOMEM; - goto err_free_dev; - } - priv->data = pci_iomap(pdev, 1, 0); - if (!priv->data) { - dev_err(&pdev->dev, "can't map device memory\n"); - err = -ENOMEM; - goto err_iounmap2; - } - - pci_read_config_byte(pdev, PCI_REVISION_ID, &priv->revid); - - priv->band.channels = (struct ieee80211_channel *)agnx_channels; - priv->band.n_channels = ARRAY_SIZE(agnx_channels); - priv->band.bitrates = (struct ieee80211_rate *)agnx_rates_80211g; - priv->band.n_bitrates = ARRAY_SIZE(agnx_rates_80211g); - - /* Init ieee802.11 dev */ - SET_IEEE80211_DEV(dev, &pdev->dev); - pci_set_drvdata(pdev, dev); - dev->extra_tx_headroom = sizeof(struct agnx_hdr); - - /* FIXME It only include FCS in promious mode but not manage mode */ -/* dev->flags = IEEE80211_HW_RX_INCLUDES_FCS; */ - dev->channel_change_time = 5000; - dev->max_signal = 100; - /* FIXME */ - dev->queues = 1; - - agnx_get_mac_address(priv); - - SET_IEEE80211_PERM_ADDR(dev, priv->mac_addr); - -/* /\* FIXME *\/ */ -/* for (i = 1; i < NUM_DRIVE_MODES; i++) { */ -/* err = ieee80211_register_hwmode(dev, &priv->modes[i]); */ -/* if (err) { */ -/* printk(KERN_ERR PFX "Can't register hwmode\n"); */ -/* goto err_iounmap; */ -/* } */ -/* } */ - - priv->channel = 1; - dev->wiphy->bands[IEEE80211_BAND_2GHZ] = &priv->band; - - err = ieee80211_register_hw(dev); - if (err) { - dev_err(&pdev->dev, "can't register hardware\n"); - goto err_iounmap; - } - - agnx_hw_reset(priv); - - dev_info(&pdev->dev, "%s: hwaddr %pM, Rev 0x%02x\n", - wiphy_name(dev->wiphy), - dev->wiphy->perm_addr, priv->revid); - return 0; - - err_iounmap: - pci_iounmap(pdev, priv->data); - - err_iounmap2: - pci_iounmap(pdev, priv->ctl); - - err_free_dev: - pci_set_drvdata(pdev, NULL); - ieee80211_free_hw(dev); - - err_free_reg: - pci_release_regions(pdev); - - pci_disable_device(pdev); - return err; -} /* agnx_pci_probe*/ - -#ifdef CONFIG_PM - -static int agnx_pci_suspend(struct pci_dev *pdev, pm_message_t state) -{ - struct ieee80211_hw *dev = pci_get_drvdata(pdev); - AGNX_TRACE; - - ieee80211_stop_queues(dev); - agnx_stop(dev); - - pci_save_state(pdev); - pci_set_power_state(pdev, pci_choose_state(pdev, state)); - return 0; -} - -static int agnx_pci_resume(struct pci_dev *pdev) -{ - struct ieee80211_hw *dev = pci_get_drvdata(pdev); - AGNX_TRACE; - - pci_set_power_state(pdev, PCI_D0); - pci_restore_state(pdev); - - agnx_start(dev); - ieee80211_wake_queues(dev); - - return 0; -} - -#else - -#define agnx_pci_suspend NULL -#define agnx_pci_resume NULL - -#endif /* CONFIG_PM */ - - -static struct pci_driver agnx_pci_driver = { - .name = "agnx-pci", - .id_table = agnx_pci_id_tbl, - .probe = agnx_pci_probe, - .remove = __devexit_p(agnx_pci_remove), - .suspend = agnx_pci_suspend, - .resume = agnx_pci_resume, -}; - -static int __init agnx_pci_init(void) -{ - AGNX_TRACE; - return pci_register_driver(&agnx_pci_driver); -} - -static void __exit agnx_pci_exit(void) -{ - AGNX_TRACE; - pci_unregister_driver(&agnx_pci_driver); -} - - -module_init(agnx_pci_init); -module_exit(agnx_pci_exit); diff --git a/drivers/staging/agnx/phy.c b/drivers/staging/agnx/phy.c deleted file mode 100644 index ec1ca86fa0c4..000000000000 --- a/drivers/staging/agnx/phy.c +++ /dev/null @@ -1,960 +0,0 @@ -/** - * Airgo MIMO wireless driver - * - * Copyright (c) 2007 Li YanBo <dreamfly281@gmail.com> - - * Thanks for Jeff Williams <angelbane@gmail.com> do reverse engineer - * works and published the SPECS at http://airgo.wdwconsulting.net/mymoin - - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#include <linux/init.h> -#include <linux/etherdevice.h> -#include <linux/pci.h> -#include <linux/delay.h> -#include "agnx.h" -#include "debug.h" -#include "phy.h" -#include "table.h" -#include "sta.h" -#include "xmit.h" - -u8 read_from_eeprom(struct agnx_priv *priv, u16 address) -{ - void __iomem *ctl = priv->ctl; - struct agnx_eeprom cmd; - u32 reg; - - memset(&cmd, 0, sizeof(cmd)); - cmd.cmd = EEPROM_CMD_READ << AGNX_EEPROM_COMMAND_SHIFT; - cmd.address = address; - /* Verify that the Status bit is clear */ - /* Read Command and Address are written to the Serial Interface */ - iowrite32(*(__le32 *)&cmd, ctl + AGNX_CIR_SERIALITF); - /* Wait for the Status bit to clear again */ - eeprom_delay(); - /* Read from Data */ - reg = ioread32(ctl + AGNX_CIR_SERIALITF); - - cmd = *(struct agnx_eeprom *)® - - return cmd.data; -} - -static int card_full_reset(struct agnx_priv *priv) -{ - void __iomem *ctl = priv->ctl; - u32 reg; - AGNX_TRACE; - - reg = agnx_read32(ctl, AGNX_CIR_BLKCTL); - agnx_write32(ctl, AGNX_CIR_BLKCTL, 0x80); - reg = agnx_read32(ctl, AGNX_CIR_BLKCTL); - return 0; -} - -inline void enable_power_saving(struct agnx_priv *priv) -{ - void __iomem *ctl = priv->ctl; - u32 reg; - - reg = agnx_read32(ctl, AGNX_PM_PMCTL); - reg &= ~0x8; - agnx_write32(ctl, AGNX_PM_PMCTL, reg); -} - -inline void disable_power_saving(struct agnx_priv *priv) -{ - void __iomem *ctl = priv->ctl; - u32 reg; - - reg = agnx_read32(ctl, AGNX_PM_PMCTL); - reg |= 0x8; - agnx_write32(ctl, AGNX_PM_PMCTL, reg); -} - - -void disable_receiver(struct agnx_priv *priv) -{ - void __iomem *ctl = priv->ctl; - AGNX_TRACE; - - /* FIXME Disable the receiver */ - agnx_write32(ctl, AGNX_GCR_DISCOVMOD, 0x0); - /* Set gain control reset */ - agnx_write32(ctl, AGNX_GCR_RSTGCTL, 0x1); - /* Reset gain control reset */ - agnx_write32(ctl, AGNX_GCR_RSTGCTL, 0x0); -} - - -/* Fixme this shoule be disable RX, above is enable RX */ -void enable_receiver(struct agnx_priv *priv) -{ - void __iomem *ctl = priv->ctl; - AGNX_TRACE; - - /* Set adaptive gain control discovery mode */ - agnx_write32(ctl, AGNX_GCR_DISCOVMOD, 0x3); - /* Set gain control reset */ - agnx_write32(ctl, AGNX_GCR_RSTGCTL, 0x1); - /* Clear gain control reset */ - agnx_write32(ctl, AGNX_GCR_RSTGCTL, 0x0); -} - -static void mac_address_set(struct agnx_priv *priv) -{ - void __iomem *ctl = priv->ctl; - u8 *mac_addr = priv->mac_addr; - u32 reg; - - /* FIXME */ - reg = (mac_addr[0] << 24) | (mac_addr[1] << 16) | mac_addr[2] << 8 | mac_addr[3]; - iowrite32(reg, ctl + AGNX_RXM_MACHI); - reg = (mac_addr[4] << 8) | mac_addr[5]; - iowrite32(reg, ctl + AGNX_RXM_MACLO); -} - -static void receiver_bssid_set(struct agnx_priv *priv, const u8 *bssid) -{ - void __iomem *ctl = priv->ctl; - u32 reg; - - disable_receiver(priv); - /* FIXME */ - reg = bssid[0] << 24 | (bssid[1] << 16) | (bssid[2] << 8) | bssid[3]; - iowrite32(reg, ctl + AGNX_RXM_BSSIDHI); - reg = (bssid[4] << 8) | bssid[5]; - iowrite32(reg, ctl + AGNX_RXM_BSSIDLO); - - /* Enable the receiver */ - enable_receiver(priv); - - /* Clear the TSF */ -/* agnx_write32(ctl, AGNX_TXM_TSFLO, 0x0); */ -/* agnx_write32(ctl, AGNX_TXM_TSFHI, 0x0); */ - /* Clear the TBTT */ - agnx_write32(ctl, AGNX_TXM_TBTTLO, 0x0); - agnx_write32(ctl, AGNX_TXM_TBTTHI, 0x0); - disable_receiver(priv); -} /* receiver_bssid_set */ - -static void band_management_init(struct agnx_priv *priv) -{ - void __iomem *ctl = priv->ctl; - void __iomem *data = priv->data; - u32 reg; - int i; - AGNX_TRACE; - - agnx_write32(ctl, AGNX_BM_TXWADDR, AGNX_PDU_TX_WQ); - agnx_write32(ctl, AGNX_CIR_ADDRWIN, 0x0); - memset_io(data + AGNX_PDUPOOL, 0x0, AGNX_PDUPOOL_SIZE); - agnx_write32(ctl, AGNX_BM_BMCTL, 0x200); - - agnx_write32(ctl, AGNX_BM_CIPDUWCNT, 0x40); - agnx_write32(ctl, AGNX_BM_SPPDUWCNT, 0x2); - agnx_write32(ctl, AGNX_BM_RFPPDUWCNT, 0x0); - agnx_write32(ctl, AGNX_BM_RHPPDUWCNT, 0x22); - - /* FIXME Initialize the Free Pool Linked List */ - /* 1. Write the Address of the Next Node ((0x41800 + node*size)/size) - to the first word of each node. */ - for (i = 0; i < PDU_FREE_CNT; i++) { - iowrite32((AGNX_PDU_FREE + (i+1)*PDU_SIZE)/PDU_SIZE, - data + AGNX_PDU_FREE + (PDU_SIZE * i)); - /* The last node should be set to 0x0 */ - if ((i + 1) == PDU_FREE_CNT) - memset_io(data + AGNX_PDU_FREE + (PDU_SIZE * i), - 0x0, PDU_SIZE); - } - - /* Head is First Pool address (0x41800) / size (0x80) */ - agnx_write32(ctl, AGNX_BM_FPLHP, AGNX_PDU_FREE/PDU_SIZE); - /* Tail is Last Pool Address (0x47f80) / size (0x80) */ - agnx_write32(ctl, AGNX_BM_FPLTP, 0x47f80/PDU_SIZE); - /* Count is Number of Nodes in the Pool (0xd0) */ - agnx_write32(ctl, AGNX_BM_FPCNT, PDU_FREE_CNT); - - /* Start all workqueue */ - agnx_write32(ctl, AGNX_BM_CIWQCTL, 0x80000); - agnx_write32(ctl, AGNX_BM_CPULWCTL, 0x80000); - agnx_write32(ctl, AGNX_BM_CPUHWCTL, 0x80000); - agnx_write32(ctl, AGNX_BM_CPUTXWCTL, 0x80000); - agnx_write32(ctl, AGNX_BM_CPURXWCTL, 0x80000); - agnx_write32(ctl, AGNX_BM_SPRXWCTL, 0x80000); - agnx_write32(ctl, AGNX_BM_SPTXWCTL, 0x80000); - agnx_write32(ctl, AGNX_BM_RFPWCTL, 0x80000); - - /* Enable the Band Management */ - reg = agnx_read32(ctl, AGNX_BM_BMCTL); - reg |= 0x1; - agnx_write32(ctl, AGNX_BM_BMCTL, reg); -} /* band_managment_init */ - - -static void system_itf_init(struct agnx_priv *priv) -{ - void __iomem *ctl = priv->ctl; - u32 reg; - AGNX_TRACE; - - agnx_write32(ctl, AGNX_SYSITF_GPIOUT, 0x0); - agnx_write32(ctl, AGNX_PM_TESTPHY, 0x11e143a); - - if (priv->revid == 0) { - reg = agnx_read32(ctl, AGNX_SYSITF_SYSMODE); - reg |= 0x11; - agnx_write32(ctl, AGNX_SYSITF_SYSMODE, reg); - } - /* ??? What is that means? it should difference for differice type - of cards */ - agnx_write32(ctl, AGNX_CIR_SERIALITF, 0xfff81006); - - agnx_write32(ctl, AGNX_SYSITF_GPIOIN, 0x1f0000); - agnx_write32(ctl, AGNX_SYSITF_GPIOUT, 0x5); - reg = agnx_read32(ctl, AGNX_SYSITF_GPIOIN); -} - -static void encryption_init(struct agnx_priv *priv) -{ - void __iomem *ctl = priv->ctl; - AGNX_TRACE; - - agnx_write32(ctl, AGNX_ENCRY_WEPKEY0, 0x0); - agnx_write32(ctl, AGNX_ENCRY_WEPKEY1, 0x0); - agnx_write32(ctl, AGNX_ENCRY_WEPKEY2, 0x0); - agnx_write32(ctl, AGNX_ENCRY_WEPKEY3, 0x0); - agnx_write32(ctl, AGNX_ENCRY_CCMRECTL, 0x8); -} - -static void tx_management_init(struct agnx_priv *priv) -{ - void __iomem *ctl = priv->ctl; - void __iomem *data = priv->data; - u32 reg; - AGNX_TRACE; - - /* Fill out the ComputationalEngineLookupTable - * starting at memory #2 offset 0x800 - */ - tx_engine_lookup_tbl_init(priv); - memset_io(data + 0x1000, 0, 0xfe0); - /* Enable Transmission Management Functions */ - agnx_write32(ctl, AGNX_TXM_ETMF, 0x3ff); - /* Write 0x3f to Transmission Template */ - agnx_write32(ctl, AGNX_TXM_TXTEMP, 0x3f); - - if (priv->revid >= 2) - agnx_write32(ctl, AGNX_TXM_SIFSPIFS, 0x1e140a0b); - else - agnx_write32(ctl, AGNX_TXM_SIFSPIFS, 0x1e190a0b); - - reg = agnx_read32(ctl, AGNX_TXM_TIFSEIFS); - reg &= 0xff00; - reg |= 0xb; - agnx_write32(ctl, AGNX_TXM_TIFSEIFS, reg); - reg = agnx_read32(ctl, AGNX_TXM_TIFSEIFS); - reg &= 0xffff00ff; - reg |= 0xa00; - agnx_write32(ctl, AGNX_TXM_TIFSEIFS, reg); - /* Enable TIFS */ - agnx_write32(ctl, AGNX_TXM_CTL, 0x40000); - - reg = agnx_read32(ctl, AGNX_TXM_TIFSEIFS); - reg &= 0xff00ffff; - reg |= 0x510000; - agnx_write32(ctl, AGNX_TXM_TIFSEIFS, reg); - reg = agnx_read32(ctl, AGNX_TXM_PROBDELAY); - reg &= 0xff00ffff; - agnx_write32(ctl, AGNX_TXM_PROBDELAY, reg); - reg = agnx_read32(ctl, AGNX_TXM_TIFSEIFS); - reg &= 0x00ffffff; - reg |= 0x1c000000; - agnx_write32(ctl, AGNX_TXM_TIFSEIFS, reg); - reg = agnx_read32(ctl, AGNX_TXM_PROBDELAY); - reg &= 0x00ffffff; - reg |= 0x01000000; - agnx_write32(ctl, AGNX_TXM_PROBDELAY, reg); - - /* # Set DIF 0-1,2-3,4-5,6-7 to defaults */ - agnx_write32(ctl, AGNX_TXM_DIF01, 0x321d321d); - agnx_write32(ctl, AGNX_TXM_DIF23, 0x321d321d); - agnx_write32(ctl, AGNX_TXM_DIF45, 0x321d321d); - agnx_write32(ctl, AGNX_TXM_DIF67, 0x321d321d); - - /* Max Ack timeout limit */ - agnx_write32(ctl, AGNX_TXM_MAXACKTIM, 0x1e19); - /* Max RX Data Timeout count, */ - reg = agnx_read32(ctl, AGNX_TXM_MAXRXTIME); - reg &= 0xffff0000; - reg |= 0xff; - agnx_write32(ctl, AGNX_TXM_MAXRXTIME, reg); - - /* CF poll RX Timeout count */ - reg = agnx_read32(ctl, AGNX_TXM_CFPOLLRXTIM); - reg &= 0xffff; - reg |= 0xff0000; - agnx_write32(ctl, AGNX_TXM_CFPOLLRXTIM, reg); - - /* Max Timeout Exceeded count, */ - reg = agnx_read32(ctl, AGNX_TXM_MAXTIMOUT); - reg &= 0xff00ffff; - reg |= 0x190000; - agnx_write32(ctl, AGNX_TXM_MAXTIMOUT, reg); - - /* CF ack timeout limit for 11b */ - reg = agnx_read32(ctl, AGNX_TXM_CFACKT11B); - reg &= 0xff00; - reg |= 0x1e; - agnx_write32(ctl, AGNX_TXM_CFACKT11B, reg); - - /* Max CF Poll Timeout Count */ - reg = agnx_read32(ctl, AGNX_TXM_CFPOLLRXTIM); - reg &= 0xffff0000; - reg |= 0x19; - agnx_write32(ctl, AGNX_TXM_CFPOLLRXTIM, reg); - /* CF Poll RX Timeout Count */ - reg = agnx_read32(ctl, AGNX_TXM_CFPOLLRXTIM); - reg &= 0xffff0000; - reg |= 0x1e; - agnx_write32(ctl, AGNX_TXM_CFPOLLRXTIM, reg); - - /* # write default to */ - /* 1. Schedule Empty Count */ - agnx_write32(ctl, AGNX_TXM_SCHEMPCNT, 0x5); - /* 2. CFP Period Count */ - agnx_write32(ctl, AGNX_TXM_CFPERCNT, 0x1); - /* 3. CFP MDV */ - agnx_write32(ctl, AGNX_TXM_CFPMDV, 0x10000); - - /* Probe Delay */ - reg = agnx_read32(ctl, AGNX_TXM_PROBDELAY); - reg &= 0xffff0000; - reg |= 0x400; - agnx_write32(ctl, AGNX_TXM_PROBDELAY, reg); - - /* Max CCA count Slot */ - reg = agnx_read32(ctl, AGNX_TXM_MAXCCACNTSLOT); - reg &= 0xffff00ff; - reg |= 0x900; - agnx_write32(ctl, AGNX_TXM_MAXCCACNTSLOT, reg); - - /* Slot limit/1 msec Limit */ - reg = agnx_read32(ctl, AGNX_TXM_SLOTLIMIT); - reg &= 0xff00ffff; - reg |= 0x140077; - agnx_write32(ctl, AGNX_TXM_SLOTLIMIT, reg); - - /* # Set CW #(0-7) to default */ - agnx_write32(ctl, AGNX_TXM_CW0, 0xff0007); - agnx_write32(ctl, AGNX_TXM_CW1, 0xff0007); - agnx_write32(ctl, AGNX_TXM_CW2, 0xff0007); - agnx_write32(ctl, AGNX_TXM_CW3, 0xff0007); - agnx_write32(ctl, AGNX_TXM_CW4, 0xff0007); - agnx_write32(ctl, AGNX_TXM_CW5, 0xff0007); - agnx_write32(ctl, AGNX_TXM_CW6, 0xff0007); - agnx_write32(ctl, AGNX_TXM_CW7, 0xff0007); - - /* # Set Short/Long limit #(0-7) to default */ - agnx_write32(ctl, AGNX_TXM_SLBEALIM0, 0xa000a); - agnx_write32(ctl, AGNX_TXM_SLBEALIM1, 0xa000a); - agnx_write32(ctl, AGNX_TXM_SLBEALIM2, 0xa000a); - agnx_write32(ctl, AGNX_TXM_SLBEALIM3, 0xa000a); - agnx_write32(ctl, AGNX_TXM_SLBEALIM4, 0xa000a); - agnx_write32(ctl, AGNX_TXM_SLBEALIM5, 0xa000a); - agnx_write32(ctl, AGNX_TXM_SLBEALIM6, 0xa000a); - agnx_write32(ctl, AGNX_TXM_SLBEALIM7, 0xa000a); - - reg = agnx_read32(ctl, AGNX_TXM_CTL); - reg |= 0x1400; - agnx_write32(ctl, AGNX_TXM_CTL, reg); - /* Wait for bit 0 in Control Reg to clear */ - udelay(80); - reg = agnx_read32(ctl, AGNX_TXM_CTL); - /* Or 0x18000 to Control reg */ - reg = agnx_read32(ctl, AGNX_TXM_CTL); - reg |= 0x18000; - agnx_write32(ctl, AGNX_TXM_CTL, reg); - /* Wait for bit 0 in Control Reg to clear */ - udelay(80); - reg = agnx_read32(ctl, AGNX_TXM_CTL); - - /* Set Listen Interval Count to default */ - agnx_write32(ctl, AGNX_TXM_LISINTERCNT, 0x1); - /* Set DTIM period count to default */ - agnx_write32(ctl, AGNX_TXM_DTIMPERICNT, 0x2000); -} /* tx_management_init */ - -static void rx_management_init(struct agnx_priv *priv) -{ - void __iomem *ctl = priv->ctl; - AGNX_TRACE; - - /* Initialize the Routing Table */ - routing_table_init(priv); - - if (priv->revid >= 3) { - agnx_write32(ctl, 0x2074, 0x1f171710); - agnx_write32(ctl, 0x2078, 0x10100d0d); - agnx_write32(ctl, 0x207c, 0x11111010); - } else { - agnx_write32(ctl, AGNX_RXM_DELAY11, 0x0); - } - agnx_write32(ctl, AGNX_RXM_REQRATE, 0x8195e00); -} - - -static void agnx_timer_init(struct agnx_priv *priv) -{ - void __iomem *ctl = priv->ctl; - AGNX_TRACE; - -/* /\* Write 0x249f00 (tick duration?) to Timer 1 *\/ */ -/* agnx_write32(ctl, AGNX_TIMCTL_TIMER1, 0x249f00); */ -/* /\* Write 0xe2 to Timer 1 Control *\/ */ -/* agnx_write32(ctl, AGNX_TIMCTL_TIM1CTL, 0xe2); */ - - /* Write 0x249f00 (tick duration?) to Timer 1 */ - agnx_write32(ctl, AGNX_TIMCTL_TIMER1, 0x0); - /* Write 0xe2 to Timer 1 Control */ - agnx_write32(ctl, AGNX_TIMCTL_TIM1CTL, 0x0); - - iowrite32(0xFFFFFFFF, priv->ctl + AGNX_TXM_BEACON_CTL); -} - -static void power_manage_init(struct agnx_priv *priv) -{ - void __iomem *ctl = priv->ctl; - u32 reg; - AGNX_TRACE; - - agnx_write32(ctl, AGNX_PM_MACMSW, 0x1f); - agnx_write32(ctl, AGNX_PM_RFCTL, 0x1f); - - reg = agnx_read32(ctl, AGNX_PM_PMCTL); - reg &= 0xf00f; - reg |= 0xa0; - agnx_write32(ctl, AGNX_PM_PMCTL, reg); - - if (priv->revid >= 3) { - reg = agnx_read32(ctl, AGNX_PM_SOFTRST); - reg |= 0x18; - agnx_write32(ctl, AGNX_PM_SOFTRST, reg); - } -} /* power_manage_init */ - - -static void gain_ctlcnt_init(struct agnx_priv *priv) -{ - void __iomem *ctl = priv->ctl; - u32 reg; - AGNX_TRACE; - - agnx_write32(ctl, AGNX_GCR_TRACNT5, 0x119); - agnx_write32(ctl, AGNX_GCR_TRACNT6, 0x118); - agnx_write32(ctl, AGNX_GCR_TRACNT7, 0x117); - - reg = agnx_read32(ctl, AGNX_PM_PMCTL); - reg |= 0x8; - agnx_write32(ctl, AGNX_PM_PMCTL, reg); - - reg = agnx_read32(ctl, AGNX_PM_PMCTL); - reg &= ~0x8; - agnx_write32(ctl, AGNX_PM_PMCTL, reg); - - agnx_write32(ctl, AGNX_CIR_ADDRWIN, 0x0); - - /* FIXME Write the initial Station Descriptor for the card */ - sta_init(priv, LOCAL_STAID); - sta_init(priv, BSSID_STAID); - - /* Enable staion 0 and 1 can do TX */ - /* It seemed if we set other bit to 1 the bit 0 will - be auto change to 0 */ - agnx_write32(ctl, AGNX_BM_TXTOPEER, 0x2 | 0x1); -/* agnx_write32(ctl, AGNX_BM_TXTOPEER, 0x1); */ -} /* gain_ctlcnt_init */ - - -static void phy_init(struct agnx_priv *priv) -{ - void __iomem *ctl = priv->ctl; - void __iomem *data = priv->data; - u32 reg; - AGNX_TRACE; - - /* Load InitialGainTable */ - gain_table_init(priv); - - agnx_write32(ctl, AGNX_CIR_ADDRWIN, 0x2000000); - - /* Clear the following offsets in Memory Range #2: */ - memset_io(data + 0x5040, 0, 0xa * 4); - memset_io(data + 0x5080, 0, 0xa * 4); - memset_io(data + 0x50c0, 0, 0xa * 4); - memset_io(data + 0x5400, 0, 0x80 * 4); - memset_io(data + 0x6000, 0, 0x280 * 4); - memset_io(data + 0x7000, 0, 0x280 * 4); - memset_io(data + 0x8000, 0, 0x280 * 4); - - /* Initialize the Following Registers According to PCI Revision ID */ - if (priv->revid == 0) { - /* fixme the part hasn't been update but below has been update - based on WGM511 */ - agnx_write32(ctl, AGNX_ACI_LEN, 0xf); - agnx_write32(ctl, AGNX_ACI_TIMER1, 0x1d); - agnx_write32(ctl, AGNX_ACI_TIMER2, 0x3); - agnx_write32(ctl, AGNX_ACI_AICCHA0OVE, 0x11); - agnx_write32(ctl, AGNX_ACI_AICCHA1OVE, 0x0); - agnx_write32(ctl, AGNX_GCR_THD0A, 0x64); - agnx_write32(ctl, AGNX_GCR_THD0AL, 0x4b); - agnx_write32(ctl, AGNX_GCR_THD0B, 0x4b); - agnx_write32(ctl, AGNX_GCR_DUNSAT, 0x14); - agnx_write32(ctl, AGNX_GCR_DSAT, 0x24); - agnx_write32(ctl, AGNX_GCR_DFIRCAL, 0x8); - agnx_write32(ctl, AGNX_GCR_DGCTL11A, 0x1a); - agnx_write32(ctl, AGNX_GCR_DGCTL11B, 0x3); - agnx_write32(ctl, AGNX_GCR_GAININIT, 0xd); - agnx_write32(ctl, AGNX_GCR_THNOSIG, 0x1); - agnx_write32(ctl, AGNX_GCR_COARSTEP, 0x7); - agnx_write32(ctl, AGNX_GCR_SIFST11A, 0x28); - agnx_write32(ctl, AGNX_GCR_SIFST11B, 0x28); - reg = agnx_read32(ctl, AGNX_GCR_CWDETEC); - reg |= 0x1; - agnx_write32(ctl, AGNX_GCR_CWDETEC, reg); - agnx_write32(ctl, AGNX_GCR_0X38, 0x1e); - agnx_write32(ctl, AGNX_GCR_BOACT, 0x26); - agnx_write32(ctl, AGNX_GCR_DISCOVMOD, 0x3); - agnx_write32(ctl, AGNX_GCR_NLISTANT, 0x3); - agnx_write32(ctl, AGNX_GCR_NACTIANT, 0x3); - agnx_write32(ctl, AGNX_GCR_NMEASANT, 0x3); - agnx_write32(ctl, AGNX_GCR_NCAPTANT, 0x3); - agnx_write32(ctl, AGNX_GCR_THCAP11A, 0x0); - agnx_write32(ctl, AGNX_GCR_THCAP11B, 0x0); - agnx_write32(ctl, AGNX_GCR_THCAPRX11A, 0x0); - agnx_write32(ctl, AGNX_GCR_THCAPRX11B, 0x0); - agnx_write32(ctl, AGNX_GCR_THLEVDRO, 0x10); - agnx_write32(ctl, AGNX_GCR_MAXRXTIME11A, 0x1); - agnx_write32(ctl, AGNX_GCR_MAXRXTIME11B, 0x1); - agnx_write32(ctl, AGNX_GCR_CORRTIME, 0x190); - agnx_write32(ctl, AGNX_GCR_SIGHTH, 0x78); - agnx_write32(ctl, AGNX_GCR_SIGLTH, 0x1c); - agnx_write32(ctl, AGNX_GCR_CORRDROP, 0x0); - agnx_write32(ctl, AGNX_GCR_THCD, 0x0); - agnx_write32(ctl, AGNX_GCR_MAXPOWDIFF, 0x1); - agnx_write32(ctl, AGNX_GCR_TESTBUS, 0x0); - agnx_write32(ctl, AGNX_GCR_ANTCFG, 0x1f); - agnx_write32(ctl, AGNX_GCR_THJUMP, 0x14); - agnx_write32(ctl, AGNX_GCR_THPOWER, 0x0); - agnx_write32(ctl, AGNX_GCR_THPOWCLIP, 0x30); - agnx_write32(ctl, AGNX_GCR_THD0BTFEST, 0x32); - agnx_write32(ctl, AGNX_GCR_THRX11BPOWMIN, 0x19); - agnx_write32(ctl, AGNX_GCR_0X14c, 0x0); - agnx_write32(ctl, AGNX_GCR_0X150, 0x0); - agnx_write32(ctl, 0x9400, 0x0); - agnx_write32(ctl, 0x940c, 0x6ff); - agnx_write32(ctl, 0x9428, 0xa0); - agnx_write32(ctl, 0x9434, 0x0); - agnx_write32(ctl, 0x9c04, 0x15); - agnx_write32(ctl, 0x9c0c, 0x7f); - agnx_write32(ctl, 0x9c34, 0x0); - agnx_write32(ctl, 0xc000, 0x38d); - agnx_write32(ctl, 0x14018, 0x0); - agnx_write32(ctl, 0x16000, 0x1); - agnx_write32(ctl, 0x11004, 0x0); - agnx_write32(ctl, 0xec54, 0xa); - agnx_write32(ctl, 0xec1c, 0x5); - } else if (priv->revid > 0) { - agnx_write32(ctl, AGNX_ACI_LEN, 0xf); - agnx_write32(ctl, AGNX_ACI_TIMER1, 0x21); - agnx_write32(ctl, AGNX_ACI_TIMER2, 0x27); - agnx_write32(ctl, AGNX_ACI_AICCHA0OVE, 0x11); - agnx_write32(ctl, AGNX_ACI_AICCHA1OVE, 0x0); - agnx_write32(ctl, AGNX_GCR_DUNSAT, 0x14); - agnx_write32(ctl, AGNX_GCR_DSAT, 0x24); - agnx_write32(ctl, AGNX_GCR_DFIRCAL, 0x8); - agnx_write32(ctl, AGNX_GCR_DGCTL11A, 0x1a); - agnx_write32(ctl, AGNX_GCR_DGCTL11B, 0x3); - agnx_write32(ctl, AGNX_GCR_GAININIT, 0xd); - agnx_write32(ctl, AGNX_GCR_THNOSIG, 0x1); - agnx_write32(ctl, AGNX_GCR_COARSTEP, 0x7); - agnx_write32(ctl, AGNX_GCR_SIFST11A, 0x28); - agnx_write32(ctl, AGNX_GCR_SIFST11B, 0x28); - agnx_write32(ctl, AGNX_GCR_CWDETEC, 0x0); - agnx_write32(ctl, AGNX_GCR_0X38, 0x1e); -/* agnx_write32(ctl, AGNX_GCR_BOACT, 0x26);*/ - agnx_write32(ctl, AGNX_GCR_DISCOVMOD, 0x3); - - agnx_write32(ctl, AGNX_GCR_THCAP11A, 0x32); - agnx_write32(ctl, AGNX_GCR_THCAP11B, 0x32); - agnx_write32(ctl, AGNX_GCR_THCAPRX11A, 0x32); - agnx_write32(ctl, AGNX_GCR_THCAPRX11B, 0x32); - agnx_write32(ctl, AGNX_GCR_THLEVDRO, 0x10); - agnx_write32(ctl, AGNX_GCR_MAXRXTIME11A, 0x1ad); - agnx_write32(ctl, AGNX_GCR_MAXRXTIME11B, 0xa10); - agnx_write32(ctl, AGNX_GCR_CORRTIME, 0x190); - agnx_write32(ctl, AGNX_GCR_CORRDROP, 0x0); - agnx_write32(ctl, AGNX_GCR_THCD, 0x0); - agnx_write32(ctl, AGNX_GCR_THCS, 0x0); - agnx_write32(ctl, AGNX_GCR_MAXPOWDIFF, 0x4); - agnx_write32(ctl, AGNX_GCR_TESTBUS, 0x0); - agnx_write32(ctl, AGNX_GCR_THJUMP, 0x1e); - agnx_write32(ctl, AGNX_GCR_THPOWER, 0x0); - agnx_write32(ctl, AGNX_GCR_THPOWCLIP, 0x2a); - agnx_write32(ctl, AGNX_GCR_THD0BTFEST, 0x3c); - agnx_write32(ctl, AGNX_GCR_THRX11BPOWMIN, 0x19); - agnx_write32(ctl, AGNX_GCR_0X14c, 0x0); - agnx_write32(ctl, AGNX_GCR_0X150, 0x0); - agnx_write32(ctl, AGNX_GCR_RXOVERIDE, 0x0); - agnx_write32(ctl, AGNX_GCR_WATCHDOG, 0x37); - agnx_write32(ctl, 0x9400, 0x0); - agnx_write32(ctl, 0x940c, 0x6ff); - agnx_write32(ctl, 0x9428, 0xa0); - agnx_write32(ctl, 0x9434, 0x0); - agnx_write32(ctl, 0x9c04, 0x15); - agnx_write32(ctl, 0x9c0c, 0x7f); - agnx_write32(ctl, 0x9c34, 0x0); - agnx_write32(ctl, 0xc000, 0x38d); - agnx_write32(ctl, 0x14014, 0x1000); - agnx_write32(ctl, 0x14018, 0x0); - agnx_write32(ctl, 0x16000, 0x1); - agnx_write32(ctl, 0x11004, 0x0); - agnx_write32(ctl, 0xec54, 0xa); - agnx_write32(ctl, 0xec1c, 0x50); - } else if (priv->revid > 1) { - reg = agnx_read32(ctl, 0xec18); - reg |= 0x8; - agnx_write32(ctl, 0xec18, reg); - } - - /* Write the TX Fir Coefficient Table */ - tx_fir_table_init(priv); - - reg = agnx_read32(ctl, AGNX_PM_PMCTL); - reg &= ~0x8; - agnx_write32(ctl, AGNX_PM_PMCTL, reg); - reg = agnx_read32(ctl, AGNX_PM_PLLCTL); - reg |= 0x1; - agnx_write32(ctl, AGNX_PM_PLLCTL, reg); - -/* reg = agnx_read32(ctl, 0x1a030); */ -/* reg &= ~0x4; */ -/* agnx_write32(ctl, 0x1a030, reg); */ - - agnx_write32(ctl, AGNX_GCR_TRACNT4, 0x113); -} /* phy_init */ - -static void chip_init(struct agnx_priv *priv) -{ - void __iomem *ctl = priv->ctl; - u32 reg; - AGNX_TRACE; - - band_management_init(priv); - - rf_chips_init(priv); - - reg = agnx_read32(ctl, AGNX_PM_PMCTL); - reg |= 0x8; - agnx_write32(ctl, AGNX_PM_PMCTL, reg); - - /* Initialize the PHY */ - phy_init(priv); - - encryption_init(priv); - - tx_management_init(priv); - - rx_management_init(priv); - - power_manage_init(priv); - - /* Initialize the Timers */ - agnx_timer_init(priv); - - /* Write 0xc390bf9 to Interrupt Mask (Disable TX) */ - reg = 0xc390bf9 & ~IRQ_TX_BEACON; - reg &= ~IRQ_TX_DISABLE; - agnx_write32(ctl, AGNX_INT_MASK, reg); - - reg = agnx_read32(ctl, AGNX_CIR_BLKCTL); - reg |= 0x800; - agnx_write32(ctl, AGNX_CIR_BLKCTL, reg); - - /* set it when need get multicast enable? */ - agnx_write32(ctl, AGNX_BM_MTSM, 0xff); -} /* chip_init */ - - -static inline void set_promis_and_managed(struct agnx_priv *priv) -{ - void __iomem *ctl = priv->ctl; - agnx_write32(ctl, AGNX_SYSITF_SYSMODE, 0x10 | 0x2); - agnx_write32(ctl, AGNX_SYSITF_SYSMODE, 0x10 | 0x2); -} -static inline void set_learn_mode(struct agnx_priv *priv) -{ - void __iomem *ctl = priv->ctl; - agnx_write32(ctl, AGNX_SYSITF_SYSMODE, 0x8); -} -static inline void set_scan_mode(struct agnx_priv *priv) -{ - void __iomem *ctl = priv->ctl; - agnx_write32(ctl, AGNX_SYSITF_SYSMODE, 0x20); -} -static inline void set_promiscuous_mode(struct agnx_priv *priv) -{ - void __iomem *ctl = priv->ctl; - /* agnx_write32(ctl, AGNX_SYSITF_SYSMODE, 0x210);*/ - agnx_write32(ctl, AGNX_SYSITF_SYSMODE, 0x10); -} -static inline void set_managed_mode(struct agnx_priv *priv) -{ - void __iomem *ctl = priv->ctl; - agnx_write32(ctl, AGNX_SYSITF_SYSMODE, 0x2); -} -static inline void set_adhoc_mode(struct agnx_priv *priv) -{ - void __iomem *ctl = priv->ctl; - agnx_write32(ctl, AGNX_SYSITF_SYSMODE, 0x0); -} - -#if 0 -static void unknow_register_write(struct agnx_priv *priv) -{ - void __iomem *ctl = priv->ctl; - - agnx_write32(ctl, AGNX_UNKNOWN_BASE + 0x0, 0x3e); - agnx_write32(ctl, AGNX_UNKNOWN_BASE + 0x4, 0xb2); - agnx_write32(ctl, AGNX_UNKNOWN_BASE + 0x8, 0x140); - agnx_write32(ctl, AGNX_UNKNOWN_BASE + 0xc, 0x1C0); - agnx_write32(ctl, AGNX_UNKNOWN_BASE + 0x10, 0x1FF); - agnx_write32(ctl, AGNX_UNKNOWN_BASE + 0x14, 0x1DD); - agnx_write32(ctl, AGNX_UNKNOWN_BASE + 0x18, 0x15F); - agnx_write32(ctl, AGNX_UNKNOWN_BASE + 0x1c, 0xA1); - agnx_write32(ctl, AGNX_UNKNOWN_BASE + 0x20, 0x3E7); - agnx_write32(ctl, AGNX_UNKNOWN_BASE + 0x24, 0x36B); - agnx_write32(ctl, AGNX_UNKNOWN_BASE + 0x28, 0x348); - agnx_write32(ctl, AGNX_UNKNOWN_BASE + 0x2c, 0x37D); - agnx_write32(ctl, AGNX_UNKNOWN_BASE + 0x30, 0x3DE); - agnx_write32(ctl, AGNX_UNKNOWN_BASE + 0x34, 0x36); - agnx_write32(ctl, AGNX_UNKNOWN_BASE + 0x38, 0x64); - agnx_write32(ctl, AGNX_UNKNOWN_BASE + 0x3c, 0x57); - agnx_write32(ctl, AGNX_UNKNOWN_BASE + 0x40, 0x23); - agnx_write32(ctl, AGNX_UNKNOWN_BASE + 0x44, 0x3ED); - agnx_write32(ctl, AGNX_UNKNOWN_BASE + 0x48, 0x3C9); - agnx_write32(ctl, AGNX_UNKNOWN_BASE + 0x4c, 0x3CA); - agnx_write32(ctl, AGNX_UNKNOWN_BASE + 0x50, 0x3E7); - agnx_write32(ctl, AGNX_UNKNOWN_BASE + 0x54, 0x8); - agnx_write32(ctl, AGNX_UNKNOWN_BASE + 0x58, 0x1F); - agnx_write32(ctl, AGNX_UNKNOWN_BASE + 0x5c, 0x1a); -} -#endif - -static void card_interface_init(struct agnx_priv *priv) -{ - void __iomem *ctl = priv->ctl; - u8 bssid[ETH_ALEN] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; - u32 reg; - unsigned int i; - AGNX_TRACE; - - might_sleep(); - /* Clear RX Control and Enable RX queues */ - agnx_write32(ctl, AGNX_CIR_RXCTL, 0x8); - - might_sleep(); - /* Do a full reset of the card */ - card_full_reset(priv); - might_sleep(); - - /* Check and set Card Endianness */ - reg = ioread32(priv->ctl + AGNX_CIR_ENDIAN); - /* TODO If not 0xB3B2B1B0 set to 0xB3B2B1B0 */ - printk(KERN_INFO PFX "CIR_ENDIAN is %x\n", reg); - - - /* Config the eeprom */ - agnx_write32(ctl, AGNX_CIR_SERIALITF, 0x7000086); - udelay(10); - reg = agnx_read32(ctl, AGNX_CIR_SERIALITF); - - - agnx_write32(ctl, AGNX_PM_SOFTRST, 0x80000033); - reg = agnx_read32(ctl, 0xec50); - reg |= 0xf; - agnx_write32(ctl, 0xec50, reg); - agnx_write32(ctl, AGNX_PM_SOFTRST, 0x0); - - - reg = agnx_read32(ctl, AGNX_SYSITF_GPIOIN); - udelay(10); - reg = agnx_read32(ctl, AGNX_CIR_SERIALITF); - - /* Dump the eeprom */ - do { - char eeprom[0x100000/0x100]; - - for (i = 0; i < 0x100000; i += 0x100) { - agnx_write32(ctl, AGNX_CIR_SERIALITF, 0x3000000 + i); - udelay(13); - reg = agnx_read32(ctl, AGNX_CIR_SERIALITF); - udelay(70); - reg = agnx_read32(ctl, AGNX_CIR_SERIALITF); - eeprom[i/0x100] = reg & 0xFF; - udelay(10); - } - print_hex_dump_bytes(PFX "EEPROM: ", DUMP_PREFIX_NONE, eeprom, - ARRAY_SIZE(eeprom)); - } while (0); - - spi_rc_write(ctl, RF_CHIP0, 0x26); - reg = agnx_read32(ctl, AGNX_SPI_RLSW); - - /* Initialize the system interface */ - system_itf_init(priv); - - might_sleep(); - /* Chip Initialization (Polaris) */ - chip_init(priv); - might_sleep(); - - /* Calibrate the antennae */ - antenna_calibrate(priv); - - reg = agnx_read32(ctl, 0xec50); - reg &= ~0x40; - agnx_write32(ctl, 0xec50, reg); - agnx_write32(ctl, AGNX_PM_SOFTRST, 0x0); - agnx_write32(ctl, AGNX_PM_PLLCTL, 0x1); - - reg = agnx_read32(ctl, AGNX_BM_BMCTL); - reg |= 0x8000; - agnx_write32(ctl, AGNX_BM_BMCTL, reg); - enable_receiver(priv); - reg = agnx_read32(ctl, AGNX_SYSITF_SYSMODE); - reg |= 0x200; - agnx_write32(ctl, AGNX_SYSITF_SYSMODE, reg); - enable_receiver(priv); - - might_sleep(); - /* Initialize Gain Control Counts */ - gain_ctlcnt_init(priv); - - /* Write Initial Station Power Template for this station(#0) */ - sta_power_init(priv, LOCAL_STAID); - - might_sleep(); - /* Initialize the rx,td,tm rings, for each node in the ring */ - fill_rings(priv); - - might_sleep(); - - - agnx_write32(ctl, AGNX_PM_SOFTRST, 0x80000033); - agnx_write32(ctl, 0xec50, 0xc); - agnx_write32(ctl, AGNX_PM_SOFTRST, 0x0); - - /* FIXME Initialize the transmit control register */ - agnx_write32(ctl, AGNX_TXM_CTL, 0x194c1); - - enable_receiver(priv); - - might_sleep(); - /* FIXME Set the Receive Control Mac Address to card address */ - mac_address_set(priv); - enable_receiver(priv); - might_sleep(); - - /* Set the recieve request rate */ - /* FIXME Enable the request */ - /* Check packet length */ - /* Set maximum packet length */ -/* agnx_write32(ctl, AGNX_RXM_REQRATE, 0x88195e00); */ -/* enable_receiver(priv); */ - - /* Set the Receiver BSSID */ - receiver_bssid_set(priv, bssid); - - /* FIXME Set to managed mode */ - set_managed_mode(priv); -/* set_promiscuous_mode(priv); */ -/* set_scan_mode(priv); */ -/* set_learn_mode(priv); */ -/* set_promis_and_managed(priv); */ -/* set_adhoc_mode(priv); */ - - /* Set the recieve request rate */ - /* Check packet length */ - agnx_write32(ctl, AGNX_RXM_REQRATE, 0x08000000); - reg = agnx_read32(ctl, AGNX_RXM_REQRATE); - /* Set maximum packet length */ - reg |= 0x00195e00; - agnx_write32(ctl, AGNX_RXM_REQRATE, reg); - - /* Configure the RX and TX interrupt */ - reg = ENABLE_RX_INTERRUPT | RX_CACHE_LINE | FRAG_LEN_2048 | FRAG_BE; - agnx_write32(ctl, AGNX_CIR_RXCFG, reg); - /* FIXME */ - reg = ENABLE_TX_INTERRUPT | TX_CACHE_LINE | FRAG_LEN_2048 | FRAG_BE; - agnx_write32(ctl, AGNX_CIR_TXCFG, reg); - - /* Enable RX TX Interrupts */ - agnx_write32(ctl, AGNX_CIR_RXCTL, 0x80); - agnx_write32(ctl, AGNX_CIR_TXMCTL, 0x80); - agnx_write32(ctl, AGNX_CIR_TXDCTL, 0x80); - - /* FIXME Set the master control interrupt in block control */ - agnx_write32(ctl, AGNX_CIR_BLKCTL, 0x800); - - /* Enable RX and TX queues */ - reg = agnx_read32(ctl, AGNX_CIR_RXCTL); - reg |= 0x8; - agnx_write32(ctl, AGNX_CIR_RXCTL, reg); - reg = agnx_read32(ctl, AGNX_CIR_TXMCTL); - reg |= 0x8; - agnx_write32(ctl, AGNX_CIR_TXMCTL, reg); - reg = agnx_read32(ctl, AGNX_CIR_TXDCTL); - reg |= 0x8; - agnx_write32(ctl, AGNX_CIR_TXDCTL, reg); - - agnx_write32(ctl, AGNX_SYSITF_GPIOUT, 0x5); - /* FIXME */ - /* unknow_register_write(priv); */ - /* Update local card hash entry */ - hash_write(priv, priv->mac_addr, LOCAL_STAID); - - might_sleep(); - - /* FIXME */ - agnx_set_channel(priv, 1); - might_sleep(); -} /* agnx_card_interface_init */ - - -void agnx_hw_init(struct agnx_priv *priv) -{ - AGNX_TRACE; - might_sleep(); - card_interface_init(priv); -} - -int agnx_hw_reset(struct agnx_priv *priv) -{ - return card_full_reset(priv); -} - -int agnx_set_ssid(struct agnx_priv *priv, u8 *ssid, size_t ssid_len) -{ - AGNX_TRACE; - return 0; -} - -void agnx_set_bssid(struct agnx_priv *priv, const u8 *bssid) -{ - receiver_bssid_set(priv, bssid); -} diff --git a/drivers/staging/agnx/phy.h b/drivers/staging/agnx/phy.h deleted file mode 100644 index a955f05361e7..000000000000 --- a/drivers/staging/agnx/phy.h +++ /dev/null @@ -1,409 +0,0 @@ -#ifndef AGNX_PHY_H_ -#define AGNX_PHY_H_ - -#include "agnx.h" - -/* Transmission Managment Registers */ -#define AGNX_TXM_BASE 0x0000 -#define AGNX_TXM_CTL 0x0000 /* control register */ -#define AGNX_TXM_ETMF 0x0004 /* enable transmission management functions */ -#define AGNX_TXM_TXTEMP 0x0008 /* transmission template */ -#define AGNX_TXM_RETRYSTAID 0x000c /* Retry Station ID */ -#define AGNX_TXM_TIMESTAMPLO 0x0010 /* Timestamp Lo */ -#define AGNX_TXM_TIMESTAMPHI 0x0014 /* Timestamp Hi */ -#define AGNX_TXM_TXDELAY 0x0018 /* tx delay */ -#define AGNX_TXM_TBTTLO 0x0020 /* tbtt Lo */ -#define AGNX_TXM_TBTTHI 0x0024 /* tbtt Hi */ -#define AGNX_TXM_BEAINTER 0x0028 /* Beacon Interval */ -#define AGNX_TXM_NAV 0x0030 /* NAV */ -#define AGNX_TXM_CFPMDV 0x0034 /* CFP MDV */ -#define AGNX_TXM_CFPERCNT 0x0038 /* CFP period count */ -#define AGNX_TXM_PROBDELAY 0x003c /* probe delay */ -#define AGNX_TXM_LISINTERCNT 0x0040 /* listen interval count */ -#define AGNX_TXM_DTIMPERICNT 0x004c /* DTIM period count */ - -#define AGNX_TXM_BEACON_CTL 0x005c /* beacon control */ - -#define AGNX_TXM_SCHEMPCNT 0x007c /* schedule empty count */ -#define AGNX_TXM_MAXTIMOUT 0x0084 /* max timeout exceed count */ -#define AGNX_TXM_MAXCFPTIM 0x0088 /* max CF poll timeout count */ -#define AGNX_TXM_MAXRXTIME 0x008c /* max RX timeout count */ -#define AGNX_TXM_MAXACKTIM 0x0090 /* max ACK timeout count */ -#define AGNX_TXM_DIF01 0x00a0 /* DIF 0-1 */ -#define AGNX_TXM_DIF23 0x00a4 /* DIF 2-3 */ -#define AGNX_TXM_DIF45 0x00a8 /* DIF 4-5 */ -#define AGNX_TXM_DIF67 0x00ac /* DIF 6-7 */ -#define AGNX_TXM_SIFSPIFS 0x00b0 /* SIFS/PIFS */ -#define AGNX_TXM_TIFSEIFS 0x00b4 /* TIFS/EIFS */ -#define AGNX_TXM_MAXCCACNTSLOT 0x00b8 /* max CCA count slot */ -#define AGNX_TXM_SLOTLIMIT 0x00bc /* slot limit/1 msec limit */ -#define AGNX_TXM_CFPOLLRXTIM 0x00f0 /* CF poll RX timeout count */ -#define AGNX_TXM_CFACKT11B 0x00f4 /* CF ack timeout limit for 11b */ -#define AGNX_TXM_CW0 0x0100 /* CW 0 */ -#define AGNX_TXM_SLBEALIM0 0x0108 /* short/long beacon limit 0 */ -#define AGNX_TXM_CW1 0x0120 /* CW 1 */ -#define AGNX_TXM_SLBEALIM1 0x0128 /* short/long beacon limit 1 */ -#define AGNX_TXM_CW2 0x0140 /* CW 2 */ -#define AGNX_TXM_SLBEALIM2 0x0148 /* short/long beacon limit 2 */ -#define AGNX_TXM_CW3 0x0160 /* CW 3 */ -#define AGNX_TXM_SLBEALIM3 0x0168 /* short/long beacon limit 3 */ -#define AGNX_TXM_CW4 0x0180 /* CW 4 */ -#define AGNX_TXM_SLBEALIM4 0x0188 /* short/long beacon limit 4 */ -#define AGNX_TXM_CW5 0x01a0 /* CW 5 */ -#define AGNX_TXM_SLBEALIM5 0x01a8 /* short/long beacon limit 5 */ -#define AGNX_TXM_CW6 0x01c0 /* CW 6 */ -#define AGNX_TXM_SLBEALIM6 0x01c8 /* short/long beacon limit 6 */ -#define AGNX_TXM_CW7 0x01e0 /* CW 7 */ -#define AGNX_TXM_SLBEALIM7 0x01e8 /* short/long beacon limit 7 */ -#define AGNX_TXM_BEACONTEMP 0x1000 /* beacon template */ -#define AGNX_TXM_STAPOWTEMP 0x1a00 /* Station Power Template */ - -/* Receive Management Control Registers */ -#define AGNX_RXM_BASE 0x2000 -#define AGNX_RXM_REQRATE 0x2000 /* requested rate */ -#define AGNX_RXM_MACHI 0x2004 /* first 4 bytes of mac address */ -#define AGNX_RXM_MACLO 0x2008 /* last 2 bytes of mac address */ -#define AGNX_RXM_BSSIDHI 0x200c /* bssid hi */ -#define AGNX_RXM_BSSIDLO 0x2010 /* bssid lo */ -#define AGNX_RXM_HASH_CMD_FLAG 0x2014 /* Flags for the RX Hash Command Default:0 */ -#define AGNX_RXM_HASH_CMD_HIGH 0x2018 /* The High half of the Hash Command */ -#define AGNX_RXM_HASH_CMD_LOW 0x201c /* The Low half of the Hash Command */ -#define AGNX_RXM_ROUTAB 0x2020 /* routing table */ -#define ROUTAB_SUBTYPE_SHIFT 24 -#define ROUTAB_TYPE_SHIFT 28 -#define ROUTAB_STATUS_SHIFT 30 -#define ROUTAB_RW_SHIFT 31 -#define ROUTAB_ROUTE_DROP 0xf00000 /* Drop */ -#define ROUTAB_ROUTE_CPU 0x400000 /* CPU */ -#define ROUTAB_ROUTE_ENCRY 0x500800 /* Encryption */ -#define ROUTAB_ROUTE_RFP 0x800000 /* RFP */ - -#define ROUTAB_TYPE_MANAG 0x0 /* Management */ -#define ROUTAB_TYPE_CTL 0x1 /* Control */ -#define ROUTAB_TYPE_DATA 0x2 /* Data */ - -#define ROUTAB_SUBTYPE_DATA 0x0 -#define ROUTAB_SUBTYPE_DATAACK 0x1 -#define ROUTAB_SUBTYPE_DATAPOLL 0x2 -#define ROUTAB_SUBTYPE_DATAPOLLACK 0x3 -#define ROUTAB_SUBTYPE_NULL 0x4 /* NULL */ -#define ROUTAB_SUBTYPE_NULLACK 0x5 -#define ROUTAB_SUBTYPE_NULLPOLL 0x6 -#define ROUTAB_SUBTYPE_NULLPOLLACK 0x7 -#define ROUTAB_SUBTYPE_QOSDATA 0x8 /* QOS DATA */ -#define ROUTAB_SUBTYPE_QOSDATAACK 0x9 -#define ROUTAB_SUBTYPE_QOSDATAPOLL 0xa -#define ROUTAB_SUBTYPE_QOSDATAACKPOLL 0xb -#define ROUTAB_SUBTYPE_QOSNULL 0xc -#define ROUTAB_SUBTYPE_QOSNULLACK 0xd -#define ROUTAB_SUBTYPE_QOSNULLPOLL 0xe -#define ROUTAB_SUBTYPE_QOSNULLPOLLACK 0xf -#define AGNX_RXM_DELAY11 0x2024 /* delay 11(AB) */ -#define AGNX_RXM_SOF_CNT 0x2028 /* SOF Count */ -#define AGNX_RXM_FRAG_CNT 0x202c /* Fragment Count*/ -#define AGNX_RXM_FCS_CNT 0x2030 /* FCS Count */ -#define AGNX_RXM_BSSID_MISS_CNT 0x2034 /* BSSID Miss Count */ -#define AGNX_RXM_PDU_ERR_CNT 0x2038 /* PDU Error Count */ -#define AGNX_RXM_DEST_MISS_CNT 0x203C /* Destination Miss Count */ -#define AGNX_RXM_DROP_CNT 0x2040 /* Drop Count */ -#define AGNX_RXM_ABORT_CNT 0x2044 /* Abort Count */ -#define AGNX_RXM_RELAY_CNT 0x2048 /* Relay Count */ -#define AGNX_RXM_HASH_MISS_CNT 0x204c /* Hash Miss Count */ -#define AGNX_RXM_SA_HI 0x2050 /* Address of received packet Hi */ -#define AGNX_RXM_SA_LO 0x2054 /* Address of received packet Lo */ -#define AGNX_RXM_HASH_DUMP_LST 0x2100 /* Contains Hash Data */ -#define AGNX_RXM_HASH_DUMP_MST 0x2104 /* Contains Hash Data */ -#define AGNX_RXM_HASH_DUMP_DATA 0x2108 /* The Station ID to dump */ - - -/* Encryption Managment */ -#define AGNX_ENCRY_BASE 0x2400 -#define AGNX_ENCRY_WEPKEY0 0x2440 /* wep key #0 */ -#define AGNX_ENCRY_WEPKEY1 0x2444 /* wep key #1 */ -#define AGNX_ENCRY_WEPKEY2 0x2448 /* wep key #2 */ -#define AGNX_ENCRY_WEPKEY3 0x244c /* wep key #3 */ -#define AGNX_ENCRY_CCMRECTL 0x2460 /* ccm replay control */ - - -/* Band Management Registers */ -#define AGNX_BM_BASE 0x2c00 -#define AGNX_BM_BMCTL 0x2c00 /* band management control */ -#define AGNX_BM_TXWADDR 0x2c18 /* tx workqueue address start */ -#define AGNX_BM_TXTOPEER 0x2c24 /* transmit to peers */ -#define AGNX_BM_FPLHP 0x2c2c /* free pool list head pointer */ -#define AGNX_BM_FPLTP 0x2c30 /* free pool list tail pointer */ -#define AGNX_BM_FPCNT 0x2c34 /* free pool count */ -#define AGNX_BM_CIPDUWCNT 0x2c38 /* card interface pdu workqueue count */ -#define AGNX_BM_SPPDUWCNT 0x2c3c /* sp pdu workqueue count */ -#define AGNX_BM_RFPPDUWCNT 0x2c40 /* rfp pdu workqueue count */ -#define AGNX_BM_RHPPDUWCNT 0x2c44 /* rhp pdu workqueue count */ -#define AGNX_BM_CIWQCTL 0x2c48 /* Card Interface WorkQueue Control */ -#define AGNX_BM_CPUTXWCTL 0x2c50 /* cpu tx workqueue control */ -#define AGNX_BM_CPURXWCTL 0x2c58 /* cpu rx workqueue control */ -#define AGNX_BM_CPULWCTL 0x2c60 /* cpu low workqueue control */ -#define AGNX_BM_CPUHWCTL 0x2c68 /* cpu high workqueue control */ -#define AGNX_BM_SPTXWCTL 0x2c70 /* sp tx workqueue control */ -#define AGNX_BM_SPRXWCTL 0x2c78 /* sp rx workqueue control */ -#define AGNX_BM_RFPWCTL 0x2c80 /* RFP workqueue control */ -#define AGNX_BM_MTSM 0x2c90 /* Multicast Transmit Station Mask */ - -/* Card Interface Registers (32bits) */ -#define AGNX_CIR_BASE 0x3000 -#define AGNX_CIR_BLKCTL 0x3000 /* block control*/ -#define AGNX_STAT_TX 0x1 -#define AGNX_STAT_RX 0x2 -#define AGNX_STAT_X 0x4 -/* Below two interrupt flags will be set by our but not CPU or the card */ -#define AGNX_STAT_TXD 0x10 -#define AGNX_STAT_TXM 0x20 - -#define AGNX_CIR_ADDRWIN 0x3004 /* Addressable Windows*/ -#define AGNX_CIR_ENDIAN 0x3008 /* card endianness */ -#define AGNX_CIR_SERIALITF 0x3020 /* serial interface */ -#define AGNX_CIR_RXCFG 0x3040 /* receive config */ -#define ENABLE_RX_INTERRUPT 0x20 -#define RX_CACHE_LINE 0x8 -/* the RX fragment length */ -#define FRAG_LEN_256 0x0 /* 256B */ -#define FRAG_LEN_512 0x1 -#define FRAG_LEN_1024 0x2 -#define FRAG_LEN_2048 0x3 -#define FRAG_BE 0x10 -#define AGNX_CIR_RXCTL 0x3050 /* receive control */ -/* memory address, chipside */ -#define AGNX_CIR_RXCMSTART 0x3054 /* receive client memory start */ -#define AGNX_CIR_RXCMEND 0x3058 /* receive client memory end */ -/* memory address, pci */ -#define AGNX_CIR_RXHOSTADDR 0x3060 /* receive hostside address */ -/* memory address, chipside */ -#define AGNX_CIR_RXCLIADDR 0x3064 /* receive clientside address */ -#define AGNX_CIR_RXDMACTL 0x3068 /* receive dma control */ -#define AGNX_CIR_TXCFG 0x3080 /* transmit config */ -#define AGNX_CIR_TXMCTL 0x3090 /* Transmit Management Control */ -#define ENABLE_TX_INTERRUPT 0x20 -#define TX_CACHE_LINE 0x8 -#define AGNX_CIR_TXMSTART 0x3094 /* Transmit Management Start */ -#define AGNX_CIR_TXMEND 0x3098 /* Transmit Management End */ -#define AGNX_CIR_TXDCTL 0x30a0 /* transmit data control */ -/* memeory address, chipset */ -#define AGNX_CIR_TXDSTART 0x30a4 /* transmit data start */ -#define AGNX_CIR_TXDEND 0x30a8 /* transmit data end */ -#define AGNX_CIR_TXMHADDR 0x30b0 /* Transmit Management Hostside Address */ -#define AGNX_CIR_TXMCADDR 0x30b4 /* Transmit Management Clientside Address */ -#define AGNX_CIR_TXDMACTL 0x30b8 /* transmit dma control */ - - -/* Power Managment Unit */ -#define AGNX_PM_BASE 0x3c00 -#define AGNX_PM_PMCTL 0x3c00 /* PM Control*/ -#define AGNX_PM_MACMSW 0x3c08 /* MAC Manual Slow Work Enable */ -#define AGNX_PM_RFCTL 0x3c0c /* RF Control */ -#define AGNX_PM_PHYMW 0x3c14 /* Phy Mannal Work */ -#define AGNX_PM_SOFTRST 0x3c18 /* PMU Soft Reset */ -#define AGNX_PM_PLLCTL 0x3c1c /* PMU PLL control*/ -#define AGNX_PM_TESTPHY 0x3c24 /* PMU Test Phy */ - - -/* Interrupt Control interface */ -#define AGNX_INT_BASE 0x4000 -#define AGNX_INT_STAT 0x4000 /* interrupt status */ -#define AGNX_INT_MASK 0x400c /* interrupt mask */ -/* FIXME */ -#define IRQ_TX_BEACON 0x1 /* TX Beacon */ -#define IRQ_TX_RETRY 0x8 /* TX Retry Interrupt */ -#define IRQ_TX_ACTIVITY 0x10 /* TX Activity */ -#define IRQ_RX_ACTIVITY 0x20 /* RX Activity */ -/* FIXME I guess that instead RX a none exist staion's packet or - the station hasn't been init */ -#define IRQ_RX_X 0x40 -#define IRQ_RX_Y 0x80 /* RX ? */ -#define IRQ_RX_HASHHIT 0x100 /* RX Hash Hit */ -#define IRQ_RX_FRAME 0x200 /* RX Frame */ -#define IRQ_ERR_INT 0x400 /* Error Interrupt */ -#define IRQ_TX_QUE_FULL 0x800 /* TX Workqueue Full */ -#define IRQ_BANDMAN_ERR 0x10000 /* Bandwidth Management Error */ -#define IRQ_TX_DISABLE 0x20000 /* TX Disable */ -#define IRQ_RX_IVASESKEY 0x80000 /* RX Invalid Session Key */ -#define IRQ_RX_KEYIDMIS 0x100000 /* RX key ID Mismatch */ -#define IRQ_REP_THHIT 0x200000 /* Replay Threshold Hit */ -#define IRQ_TIMER1 0x4000000 /* Timer1 */ -#define IRQ_TIMER_CNT 0x10000000 /* Timer Count */ -#define IRQ_PHY_FASTINT 0x20000000 /* Phy Fast Interrupt */ -#define IRQ_PHY_SLOWINT 0x40000000 /* Phy Slow Interrupt */ -#define IRQ_OTHER 0x80000000 /* Unknow interrupt */ -#define AGNX_IRQ_ALL 0xffffffff - -/* System Interface */ -#define AGNX_SYSITF_BASE 0x4400 -#define AGNX_SYSITF_SYSMODE 0x4400 /* system mode */ -#define AGNX_SYSITF_GPIOIN 0x4410 /* GPIO In */ -/* PIN lines for leds? */ -#define AGNX_SYSITF_GPIOUT 0x4414 /* GPIO Out */ - -/* Timer Control */ -#define AGNX_TIMCTL_TIMER1 0x4800 /* Timer 1 */ -#define AGNX_TIMCTL_TIM1CTL 0x4808 /* Timer 1 Control */ - - -/* Antenna Calibration Interface */ -#define AGNX_ACI_BASE 0x5000 -#define AGNX_ACI_MODE 0x5000 /* Mode */ -#define AGNX_ACI_MEASURE 0x5004 /* Measure */ -#define AGNX_ACI_SELCHAIN 0x5008 /* Select Chain */ -#define AGNX_ACI_LEN 0x500c /* Length */ -#define AGNX_ACI_TIMER1 0x5018 /* Timer 1 */ -#define AGNX_ACI_TIMER2 0x501c /* Timer 2 */ -#define AGNX_ACI_OFFSET 0x5020 /* Offset */ -#define AGNX_ACI_STATUS 0x5030 /* Status */ -#define CALI_IDLE 0x0 -#define CALI_DONE 0x1 -#define CALI_BUSY 0x2 -#define CALI_ERR 0x3 -#define AGNX_ACI_AICCHA0OVE 0x5034 /* AIC Channel 0 Override */ -#define AGNX_ACI_AICCHA1OVE 0x5038 /* AIC Channel 1 Override */ - -/* Gain Control Registers */ -#define AGNX_GCR_BASE 0x9000 -/* threshold of primary antenna */ -#define AGNX_GCR_THD0A 0x9000 /* threshold? D0 A */ -/* low threshold of primary antenna */ -#define AGNX_GCR_THD0AL 0x9004 /* threshold? D0 A low */ -/* threshold of secondary antenna */ -#define AGNX_GCR_THD0B 0x9008 /* threshold? D0_B */ -#define AGNX_GCR_DUNSAT 0x900c /* d unsaturated */ -#define AGNX_GCR_DSAT 0x9010 /* d saturated */ -#define AGNX_GCR_DFIRCAL 0x9014 /* D Fir/Cal */ -#define AGNX_GCR_DGCTL11A 0x9018 /* d gain control 11a */ -#define AGNX_GCR_DGCTL11B 0x901c /* d gain control 11b */ -/* strength of gain */ -#define AGNX_GCR_GAININIT 0x9020 /* gain initialization */ -#define AGNX_GCR_THNOSIG 0x9024 /* threhold no signal */ -#define AGNX_GCR_COARSTEP 0x9028 /* coarse stepping */ -#define AGNX_GCR_SIFST11A 0x902c /* sifx time 11a */ -#define AGNX_GCR_SIFST11B 0x9030 /* sifx time 11b */ -#define AGNX_GCR_CWDETEC 0x9034 /* cw detection */ -#define AGNX_GCR_0X38 0x9038 /* ???? */ -#define AGNX_GCR_BOACT 0x903c /* BO Active */ -#define AGNX_GCR_BOINACT 0x9040 /* BO Inactive */ -#define AGNX_GCR_BODYNA 0x9044 /* BO dynamic */ -/* 802.11 mode(a,b,g) */ -#define AGNX_GCR_DISCOVMOD 0x9048 /* discovery mode */ -#define AGNX_GCR_NLISTANT 0x904c /* number of listening antenna */ -#define AGNX_GCR_NACTIANT 0x9050 /* number of active antenna */ -#define AGNX_GCR_NMEASANT 0x9054 /* number of measuring antenna */ -#define AGNX_GCR_NCAPTANT 0x9058 /* number of capture antenna */ -#define AGNX_GCR_THCAP11A 0x905c /* threshold capture 11a */ -#define AGNX_GCR_THCAP11B 0x9060 /* threshold capture 11b */ -#define AGNX_GCR_THCAPRX11A 0x9064 /* threshold capture rx 11a */ -#define AGNX_GCR_THCAPRX11B 0x9068 /* threshold capture rx 11b */ -#define AGNX_GCR_THLEVDRO 0x906c /* threshold level drop */ -#define AGNX_GCR_GAINSET0 0x9070 /* Gainset 0 */ -#define AGNX_GCR_GAINSET1 0x9074 /* Gainset 1 */ -#define AGNX_GCR_GAINSET2 0x9078 /* Gainset 2 */ -#define AGNX_GCR_MAXRXTIME11A 0x907c /* maximum rx time 11a */ -#define AGNX_GCR_MAXRXTIME11B 0x9080 /* maximum rx time 11b */ -#define AGNX_GCR_CORRTIME 0x9084 /* correction time */ -/* reset the subsystem, 0 = disable, 1 = enable */ -#define AGNX_GCR_RSTGCTL 0x9088 /* reset gain control */ -/* channel receiving */ -#define AGNX_GCR_RXCHANEL 0x908c /* receive channel */ -#define AGNX_GCR_NOISE0 0x9090 /* Noise 0 */ -#define AGNX_GCR_NOISE1 0x9094 /* Noise 1 */ -#define AGNX_GCR_NOISE2 0x9098 /* Noise 2 */ -#define AGNX_GCR_SIGHTH 0x909c /* Signal High Threshold */ -#define AGNX_GCR_SIGLTH 0x90a0 /* Signal Low Threshold */ -#define AGNX_GCR_CORRDROP 0x90a4 /* correction drop */ -/* threshold of tertiay antenna */ -#define AGNX_GCR_THCD 0x90a8 /* threshold? CD */ -#define AGNX_GCR_THCS 0x90ac /* threshold? CS */ -#define AGNX_GCR_MAXPOWDIFF 0x90b8 /* maximum power difference */ -#define AGNX_GCR_TRACNT4 0x90ec /* Transition Count 4 */ -#define AGNX_GCR_TRACNT5 0x90f0 /* transition count 5 */ -#define AGNX_GCR_TRACNT6 0x90f4 /* transition count 6 */ -#define AGNX_GCR_TRACNT7 0x90f8 /* transition coutn 7 */ -#define AGNX_GCR_TESTBUS 0x911c /* test bus */ -#define AGNX_GCR_CHAINNUM 0x9120 /* Number of Chains */ -#define AGNX_GCR_ANTCFG 0x9124 /* Antenna Config */ -#define AGNX_GCR_THJUMP 0x912c /* threhold jump */ -#define AGNX_GCR_THPOWER 0x9130 /* threshold power */ -#define AGNX_GCR_THPOWCLIP 0x9134 /* threshold power clip*/ -#define AGNX_GCR_FORCECTLCLK 0x9138 /* Force Gain Control Clock */ -#define AGNX_GCR_GAINSETWRITE 0x913c /* Gainset Write */ -#define AGNX_GCR_THD0BTFEST 0x9140 /* threshold d0 b tf estimate */ -#define AGNX_GCR_THRX11BPOWMIN 0x9144 /* threshold rx 11b power minimum */ -#define AGNX_GCR_0X14c 0x914c /* ?? */ -#define AGNX_GCR_0X150 0x9150 /* ?? */ -#define AGNX_GCR_RXOVERIDE 0x9194 /* recieve override */ -#define AGNX_GCR_WATCHDOG 0x91b0 /* watchdog timeout */ - - -/* Spi Interface */ -#define AGNX_SPI_BASE 0xdc00 -#define AGNX_SPI_CFG 0xdc00 /* spi configuration */ -/* Only accept 16 bits */ -#define AGNX_SPI_WMSW 0xdc04 /* write most significant word */ -/* Only accept 16 bits */ -#define AGNX_SPI_WLSW 0xdc08 /* write least significant word */ -#define AGNX_SPI_CTL 0xdc0c /* spi control */ -#define AGNX_SPI_RMSW 0xdc10 /* read most significant word */ -#define AGNX_SPI_RLSW 0xdc14 /* read least significant word */ -/* SPI Control Mask */ -#define SPI_READ_CTL 0x4000 /* read control */ -#define SPI_BUSY_CTL 0x8000 /* busy control */ -/* RF and synth chips in spi */ -#define RF_CHIP0 0x400 -#define RF_CHIP1 0x800 -#define RF_CHIP2 0x1000 -#define SYNTH_CHIP 0x2000 - -/* Unknown register */ -#define AGNX_UNKNOWN_BASE 0x7800 - -/* FIXME MonitorGain */ -#define AGNX_MONGCR_BASE 0x12000 - -/* Gain Table */ -#define AGNX_GAIN_TABLE 0x12400 - -/* The initial FIR coefficient table */ -#define AGNX_FIR_BASE 0x19804 - -#define AGNX_ENGINE_LOOKUP_TBL 0x800 - -/* eeprom commands */ -#define EEPROM_CMD_NULL 0x0 /* NULL */ -#define EEPROM_CMD_WRITE 0x2 /* write */ -#define EEPROM_CMD_READ 0x3 /* read */ -#define EEPROM_CMD_STATUSREAD 0x5 /* status register read */ -#define EEPROM_CMD_WRITEENABLE 0x6 /* write enable */ -#define EEPROM_CMD_CONFIGURE 0x7 /* configure */ - -#define EEPROM_DATAFORCOFIGURE 0x6 /* ??? */ - -/* eeprom address */ -#define EEPROM_ADDR_SUBVID 0x0 /* Sub Vendor ID */ -#define EEPROM_ADDR_SUBSID 0x2 /* Sub System ID */ -#define EEPROM_ADDR_MACADDR 0x146 /* MAC Address */ -#define EEPROM_ADDR_LOTYPE 0x14f /* LO type */ - -struct agnx_eeprom { - u8 data; /* date */ - u16 address; /* address in EEPROM */ - u8 cmd; /* command, unknown, status */ -} __attribute__((__packed__)); - -#define AGNX_EEPROM_COMMAND_SHIFT 5 -#define AGNX_EEPROM_COMMAND_STAT 0x01 - -void disable_receiver(struct agnx_priv *priv); -void enable_receiver(struct agnx_priv *priv); -u8 read_from_eeprom(struct agnx_priv *priv, u16 address); -void agnx_hw_init(struct agnx_priv *priv); -int agnx_hw_reset(struct agnx_priv *priv); -int agnx_set_ssid(struct agnx_priv *priv, u8 *ssid, size_t ssid_len); -void agnx_set_bssid(struct agnx_priv *priv, const u8 *bssid); -void enable_power_saving(struct agnx_priv *priv); -void disable_power_saving(struct agnx_priv *priv); -void calibrate_antenna_period(unsigned long data); - -#endif /* AGNX_PHY_H_ */ diff --git a/drivers/staging/agnx/rf.c b/drivers/staging/agnx/rf.c deleted file mode 100644 index 42e457a1844f..000000000000 --- a/drivers/staging/agnx/rf.c +++ /dev/null @@ -1,893 +0,0 @@ -/** - * Airgo MIMO wireless driver - * - * Copyright (c) 2007 Li YanBo <dreamfly281@gmail.com> - - * Thanks for Jeff Williams <angelbane@gmail.com> do reverse engineer - * works and published the SPECS at http://airgo.wdwconsulting.net/mymoin - - * 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/pci.h> -#include <linux/delay.h> -#include "agnx.h" -#include "debug.h" -#include "phy.h" -#include "table.h" - -/* FIXME! */ -static inline void spi_write(void __iomem *region, u32 chip_ids, u32 sw, - u16 size, u32 control) -{ - u32 reg; - u32 lsw = sw & 0xffff; /* lower 16 bits of sw*/ - u32 msw = sw >> 16; /* high 16 bits of sw */ - - /* FIXME Write Most Significant Word of the 32bit data to MSW */ - /* FIXME And Least Significant Word to LSW */ - iowrite32((lsw), region + AGNX_SPI_WLSW); - iowrite32((msw), region + AGNX_SPI_WMSW); - reg = chip_ids | size | control; - /* Write chip id(s), write size and busy control to Control Register */ - iowrite32((reg), region + AGNX_SPI_CTL); - /* Wait for Busy control to clear */ - spi_delay(); -} - -/* - * Write to SPI Synth register - */ -static inline void spi_sy_write(void __iomem *region, u32 chip_ids, u32 sw) -{ - /* FIXME the size 0x15 is a magic value*/ - spi_write(region, chip_ids, sw, 0x15, SPI_BUSY_CTL); -} - -/* - * Write to SPI RF register - */ -static inline void spi_rf_write(void __iomem *region, u32 chip_ids, u32 sw) -{ - /* FIXME the size 0xd is a magic value*/ - spi_write(region, chip_ids, sw, 0xd, SPI_BUSY_CTL); -} /* spi_rf_write */ - -/* - * Write to SPI with Read Control bit set - */ -inline void spi_rc_write(void __iomem *region, u32 chip_ids, u32 sw) -{ - /* FIXME the size 0xe5 is a magic value */ - spi_write(region, chip_ids, sw, 0xe5, SPI_BUSY_CTL|SPI_READ_CTL); -} - -/* Get the active chains's count */ -static int get_active_chains(struct agnx_priv *priv) -{ - void __iomem *ctl = priv->ctl; - int num = 0; - u32 reg; - AGNX_TRACE; - - spi_rc_write(ctl, RF_CHIP0, 0x21); - reg = agnx_read32(ctl, AGNX_SPI_RLSW); - if (reg == 1) - num++; - - spi_rc_write(ctl, RF_CHIP1, 0x21); - reg = agnx_read32(ctl, AGNX_SPI_RLSW); - if (reg == 1) - num++; - - spi_rc_write(ctl, RF_CHIP2, 0x21); - reg = agnx_read32(ctl, AGNX_SPI_RLSW); - if (reg == 1) - num++; - - spi_rc_write(ctl, RF_CHIP0, 0x26); - reg = agnx_read32(ctl, AGNX_SPI_RLSW); - if (0x33 != reg) - printk(KERN_WARNING PFX "Unmatched rf chips result\n"); - - return num; -} /* get_active_chains */ - -void rf_chips_init(struct agnx_priv *priv) -{ - void __iomem *ctl = priv->ctl; - u32 reg; - int num; - AGNX_TRACE; - - if (priv->revid == 1) { - reg = agnx_read32(ctl, AGNX_SYSITF_GPIOUT); - reg |= 0x8; - agnx_write32(ctl, AGNX_SYSITF_GPIOUT, reg); - } - - /* Set SPI clock speed to 200NS */ - reg = agnx_read32(ctl, AGNX_SPI_CFG); - reg &= ~0xF; - reg |= 0x3; - agnx_write32(ctl, AGNX_SPI_CFG, reg); - - /* Set SPI clock speed to 50NS */ - reg = agnx_read32(ctl, AGNX_SPI_CFG); - reg &= ~0xF; - reg |= 0x1; - agnx_write32(ctl, AGNX_SPI_CFG, reg); - - spi_rf_write(ctl, RF_CHIP0|RF_CHIP1|RF_CHIP2, 0x1101); - - num = get_active_chains(priv); - printk(KERN_INFO PFX "Active chains are %d\n", num); - - reg = agnx_read32(ctl, AGNX_SPI_CFG); - reg &= ~0xF; - agnx_write32(ctl, AGNX_SPI_CFG, reg); - - spi_rf_write(ctl, RF_CHIP0|RF_CHIP1|RF_CHIP2, 0x1908); -} /* rf_chips_init */ - - -static u32 channel_tbl[15][9] = { - {0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - {1, 0x00, 0x00, 0x624, 0x00, 0x1a4, 0x28, 0x00, 0x1e}, - {2, 0x00, 0x00, 0x615, 0x00, 0x1ae, 0x28, 0x00, 0x1e}, - {3, 0x00, 0x00, 0x61a, 0x00, 0x1ae, 0x28, 0x00, 0x1e}, - {4, 0x00, 0x00, 0x61f, 0x00, 0x1ae, 0x28, 0x00, 0x1e}, - {5, 0x00, 0x00, 0x624, 0x00, 0x1ae, 0x28, 0x00, 0x1e}, - {6, 0x00, 0x00, 0x61f, 0x00, 0x1b3, 0x28, 0x00, 0x1e}, - {7, 0x00, 0x00, 0x624, 0x00, 0x1b3, 0x28, 0x00, 0x1e}, - {8, 0x00, 0x00, 0x629, 0x00, 0x1b3, 0x28, 0x00, 0x1e}, - {9, 0x00, 0x00, 0x624, 0x00, 0x1b8, 0x28, 0x00, 0x1e}, - {10, 0x00, 0x00, 0x629, 0x00, 0x1b8, 0x28, 0x00, 0x1e}, - {11, 0x00, 0x00, 0x62e, 0x00, 0x1b8, 0x28, 0x00, 0x1e}, - {12, 0x00, 0x00, 0x633, 0x00, 0x1b8, 0x28, 0x00, 0x1e}, - {13, 0x00, 0x00, 0x628, 0x00, 0x1b8, 0x28, 0x00, 0x1e}, - {14, 0x00, 0x00, 0x644, 0x00, 0x1b8, 0x28, 0x00, 0x1e}, -}; - - -static inline void -channel_tbl_write(struct agnx_priv *priv, unsigned int channel, unsigned int reg_num) -{ - void __iomem *ctl = priv->ctl; - u32 reg; - - reg = channel_tbl[channel][reg_num]; - reg <<= 4; - reg |= reg_num; - spi_sy_write(ctl, SYNTH_CHIP, reg); -} - -static void synth_freq_set(struct agnx_priv *priv, unsigned int channel) -{ - void __iomem *ctl = priv->ctl; - u32 reg; - AGNX_TRACE; - - spi_rf_write(ctl, RF_CHIP0|RF_CHIP1, 0x1201); - - /* Set the Clock bits to 50NS */ - reg = agnx_read32(ctl, AGNX_SPI_CFG); - reg &= ~0xF; - reg |= 0x1; - agnx_write32(ctl, AGNX_SPI_CFG, reg); - - /* Write 0x00c0 to LSW and 0x3 to MSW of Synth Chip */ - spi_sy_write(ctl, SYNTH_CHIP, 0x300c0); - - spi_sy_write(ctl, SYNTH_CHIP, 0x32); - - /* # Write to Register 1 on the Synth Chip */ - channel_tbl_write(priv, channel, 1); - /* # Write to Register 3 on the Synth Chip */ - channel_tbl_write(priv, channel, 3); - /* # Write to Register 6 on the Synth Chip */ - channel_tbl_write(priv, channel, 6); - /* # Write to Register 5 on the Synth Chip */ - channel_tbl_write(priv, channel, 5); - /* # Write to register 8 on the Synth Chip */ - channel_tbl_write(priv, channel, 8); - - /* FIXME Clear the clock bits */ - reg = agnx_read32(ctl, AGNX_SPI_CFG); - reg &= ~0xf; - agnx_write32(ctl, AGNX_SPI_CFG, reg); -} /* synth_chip_init */ - - -static void antenna_init(struct agnx_priv *priv, int num_antenna) -{ - void __iomem *ctl = priv->ctl; - - switch (num_antenna) { - case 1: - agnx_write32(ctl, AGNX_GCR_NLISTANT, 1); - agnx_write32(ctl, AGNX_GCR_NMEASANT, 1); - agnx_write32(ctl, AGNX_GCR_NACTIANT, 1); - agnx_write32(ctl, AGNX_GCR_NCAPTANT, 1); - - agnx_write32(ctl, AGNX_GCR_ANTCFG, 7); - agnx_write32(ctl, AGNX_GCR_BOACT, 34); - agnx_write32(ctl, AGNX_GCR_BOINACT, 34); - agnx_write32(ctl, AGNX_GCR_BODYNA, 30); - - agnx_write32(ctl, AGNX_GCR_THD0A, 125); - agnx_write32(ctl, AGNX_GCR_THD0AL, 100); - agnx_write32(ctl, AGNX_GCR_THD0B, 90); - - agnx_write32(ctl, AGNX_GCR_THD0BTFEST, 80); - agnx_write32(ctl, AGNX_GCR_SIGHTH, 100); - agnx_write32(ctl, AGNX_GCR_SIGLTH, 16); - break; - case 2: - agnx_write32(ctl, AGNX_GCR_NLISTANT, 2); - agnx_write32(ctl, AGNX_GCR_NMEASANT, 2); - agnx_write32(ctl, AGNX_GCR_NACTIANT, 2); - agnx_write32(ctl, AGNX_GCR_NCAPTANT, 2); - agnx_write32(ctl, AGNX_GCR_ANTCFG, 15); - agnx_write32(ctl, AGNX_GCR_BOACT, 36); - agnx_write32(ctl, AGNX_GCR_BOINACT, 36); - agnx_write32(ctl, AGNX_GCR_BODYNA, 32); - agnx_write32(ctl, AGNX_GCR_THD0A, 120); - agnx_write32(ctl, AGNX_GCR_THD0AL, 100); - agnx_write32(ctl, AGNX_GCR_THD0B, 80); - agnx_write32(ctl, AGNX_GCR_THD0BTFEST, 70); - agnx_write32(ctl, AGNX_GCR_SIGHTH, 100); - agnx_write32(ctl, AGNX_GCR_SIGLTH, 32); - break; - case 3: - agnx_write32(ctl, AGNX_GCR_NLISTANT, 3); - agnx_write32(ctl, AGNX_GCR_NMEASANT, 3); - agnx_write32(ctl, AGNX_GCR_NACTIANT, 3); - agnx_write32(ctl, AGNX_GCR_NCAPTANT, 3); - agnx_write32(ctl, AGNX_GCR_ANTCFG, 31); - agnx_write32(ctl, AGNX_GCR_BOACT, 36); - agnx_write32(ctl, AGNX_GCR_BOINACT, 36); - agnx_write32(ctl, AGNX_GCR_BODYNA, 32); - agnx_write32(ctl, AGNX_GCR_THD0A, 100); - agnx_write32(ctl, AGNX_GCR_THD0AL, 100); - agnx_write32(ctl, AGNX_GCR_THD0B, 70); - agnx_write32(ctl, AGNX_GCR_THD0BTFEST, 70); - agnx_write32(ctl, AGNX_GCR_SIGHTH, 100); - agnx_write32(ctl, AGNX_GCR_SIGLTH, 48); -/* agnx_write32(ctl, AGNX_GCR_SIGLTH, 16); */ - break; - default: - printk(KERN_WARNING PFX "Unknow antenna number\n"); - } -} /* antenna_init */ - -static void chain_update(struct agnx_priv *priv, u32 chain) -{ - void __iomem *ctl = priv->ctl; - u32 reg; - AGNX_TRACE; - - spi_rc_write(ctl, RF_CHIP0, 0x20); - reg = agnx_read32(ctl, AGNX_SPI_RLSW); - - if (reg == 0x4) - spi_rf_write(ctl, RF_CHIP0|RF_CHIP1, reg|0x1000); - else if (reg != 0x0) - spi_rf_write(ctl, RF_CHIP0|RF_CHIP1|RF_CHIP2, reg|0x1000); - else { - if (chain == 3 || chain == 6) { - spi_rf_write(ctl, RF_CHIP0|RF_CHIP1|RF_CHIP2, reg|0x1000); - agnx_write32(ctl, AGNX_GCR_RXOVERIDE, 0x0); - } else if (chain == 2 || chain == 4) { - spi_rf_write(ctl, RF_CHIP0|RF_CHIP1, reg|0x1000); - spi_rf_write(ctl, RF_CHIP2, 0x1005); - agnx_write32(ctl, AGNX_GCR_RXOVERIDE, 0x824); - } else if (chain == 1) { - spi_rf_write(ctl, RF_CHIP0, reg|0x1000); - spi_rf_write(ctl, RF_CHIP1|RF_CHIP2, 0x1004); - agnx_write32(ctl, AGNX_GCR_RXOVERIDE, 0xc36); - } - } - - spi_rc_write(ctl, RF_CHIP0, 0x22); - reg = agnx_read32(ctl, AGNX_SPI_RLSW); - - switch (reg) { - case 0: - spi_rf_write(ctl, RF_CHIP0|RF_CHIP1|RF_CHIP2, 0x1005); - break; - case 1: - spi_rf_write(ctl, RF_CHIP0|RF_CHIP1, 0x1201); - break; - case 2: - if (chain == 6 || chain == 4) { - spi_rf_write(ctl, RF_CHIP0|RF_CHIP1, 0x1202); - spi_rf_write(ctl, RF_CHIP2, 0x1005); - } else if (chain < 3) { - spi_rf_write(ctl, RF_CHIP0, 0x1202); - spi_rf_write(ctl, RF_CHIP1|RF_CHIP2, 0x1005); - } - break; - default: - if (chain == 3) { - spi_rf_write(ctl, RF_CHIP0|RF_CHIP1, 0x1203); - spi_rf_write(ctl, RF_CHIP2, 0x1201); - } else if (chain == 2) { - spi_rf_write(ctl, RF_CHIP0, 0x1203); - spi_rf_write(ctl, RF_CHIP2, 0x1200); - spi_rf_write(ctl, RF_CHIP1, 0x1201); - } else if (chain == 1) { - spi_rf_write(ctl, RF_CHIP0, 0x1203); - spi_rf_write(ctl, RF_CHIP1|RF_CHIP2, 0x1200); - } else if (chain == 4) { - spi_rf_write(ctl, RF_CHIP0|RF_CHIP1, 0x1203); - spi_rf_write(ctl, RF_CHIP2, 0x1201); - } else { - spi_rf_write(ctl, RF_CHIP0, 0x1203); - spi_rf_write(ctl, RF_CHIP1|RF_CHIP2, 0x1201); - } - } -} /* chain_update */ - -static void antenna_config(struct agnx_priv *priv) -{ - void __iomem *ctl = priv->ctl; - u32 reg; - AGNX_TRACE; - - /* Write 0x0 to the TX Management Control Register Enable bit */ - reg = agnx_read32(ctl, AGNX_TXM_CTL); - reg &= ~0x1; - agnx_write32(ctl, AGNX_TXM_CTL, reg); - - /* FIXME */ - /* Set initial value based on number of Antennae */ - antenna_init(priv, 3); - - /* FIXME Update Power Templates for current valid Stations */ - /* sta_power_init(priv, 0);*/ - - /* FIXME the number of chains should get from eeprom*/ - chain_update(priv, AGNX_CHAINS_MAX); -} /* antenna_config */ - -void calibrate_oscillator(struct agnx_priv *priv) -{ - void __iomem *ctl = priv->ctl; - u32 reg; - AGNX_TRACE; - - spi_rc_write(ctl, RF_CHIP0|RF_CHIP1, 0x1201); - reg = agnx_read32(ctl, AGNX_GCR_GAINSET1); - reg |= 0x10; - agnx_write32(ctl, AGNX_GCR_GAINSET1, reg); - - agnx_write32(ctl, AGNX_GCR_GAINSETWRITE, 1); - agnx_write32(ctl, AGNX_GCR_RSTGCTL, 1); - - agnx_write32(ctl, AGNX_ACI_LEN, 0x3ff); - - agnx_write32(ctl, AGNX_ACI_TIMER1, 0x27); - agnx_write32(ctl, AGNX_ACI_TIMER2, 0x27); - /* (Residual DC Calibration) to Calibration Mode */ - agnx_write32(ctl, AGNX_ACI_MODE, 0x2); - - spi_rc_write(ctl, RF_CHIP0|RF_CHIP1, 0x1004); - agnx_write32(ctl, AGNX_ACI_LEN, 0x3ff); - /* (TX LO Calibration) to Calibration Mode */ - agnx_write32(ctl, AGNX_ACI_MODE, 0x4); - - do { - u32 reg1, reg2, reg3; - /* Enable Power Saving Control */ - enable_power_saving(priv); - /* Save the following registers to restore */ - reg1 = ioread32(ctl + 0x11000); - reg2 = ioread32(ctl + 0xec50); - reg3 = ioread32(ctl + 0xec54); - wmb(); - - agnx_write32(ctl, 0x11000, 0xcfdf); - agnx_write32(ctl, 0xec50, 0x70); - /* Restore the registers */ - agnx_write32(ctl, 0x11000, reg1); - agnx_write32(ctl, 0xec50, reg2); - agnx_write32(ctl, 0xec54, reg3); - /* Disable Power Saving Control */ - disable_power_saving(priv); - } while (0); - - agnx_write32(ctl, AGNX_GCR_RSTGCTL, 0); -} /* calibrate_oscillator */ - - -static void radio_channel_set(struct agnx_priv *priv, unsigned int channel) -{ - void __iomem *ctl = priv->ctl; - unsigned int freq = priv->band.channels[channel - 1].center_freq; - u32 reg; - AGNX_TRACE; - - spi_rf_write(ctl, RF_CHIP0|RF_CHIP1, 0x1201); - /* Set SPI Clock to 50 Ns */ - reg = agnx_read32(ctl, AGNX_SPI_CFG); - reg &= ~0xF; - reg |= 0x1; - agnx_write32(ctl, AGNX_SPI_CFG, reg); - - /* Clear the Disable Tx interrupt bit in Interrupt Mask */ -/* reg = agnx_read32(ctl, AGNX_INT_MASK); */ -/* reg &= ~IRQ_TX_DISABLE; */ -/* agnx_write32(ctl, AGNX_INT_MASK, reg); */ - - /* Band Selection */ - reg = agnx_read32(ctl, AGNX_SYSITF_GPIOUT); - reg |= 0x8; - agnx_write32(ctl, AGNX_SYSITF_GPIOUT, reg); - - /* FIXME Set the SiLabs Chip Frequency */ - synth_freq_set(priv, channel); - - reg = agnx_read32(ctl, AGNX_PM_SOFTRST); - reg |= 0x80100030; - agnx_write32(ctl, AGNX_PM_SOFTRST, reg); - reg = agnx_read32(ctl, AGNX_PM_PLLCTL); - reg |= 0x20009; - agnx_write32(ctl, AGNX_PM_PLLCTL, reg); - - agnx_write32(ctl, AGNX_SYSITF_GPIOUT, 0x5); - - spi_rf_write(ctl, RF_CHIP0|RF_CHIP1|RF_CHIP2, 0x1100); - - /* Load the MonitorGain Table */ - monitor_gain_table_init(priv); - - /* Load the TX Fir table */ - tx_fir_table_init(priv); - - reg = agnx_read32(ctl, AGNX_PM_PMCTL); - reg |= 0x8; - agnx_write32(ctl, AGNX_PM_PMCTL, reg); - - spi_rc_write(ctl, RF_CHIP0|RF_CHIP1, 0x22); - udelay(80); - reg = agnx_read32(ctl, AGNX_SPI_RLSW); - - - agnx_write32(ctl, AGNX_GCR_RXOVERIDE, 0xff); - agnx_write32(ctl, AGNX_GCR_DISCOVMOD, 0x3); - - reg = agnx_read32(ctl, 0xec50); - reg |= 0x4f; - agnx_write32(ctl, 0xec50, reg); - - spi_rf_write(ctl, RF_CHIP0|RF_CHIP1, 0x1201); - agnx_write32(ctl, 0x11008, 0x1); - agnx_write32(ctl, 0x1100c, 0x0); - agnx_write32(ctl, 0x11008, 0x0); - agnx_write32(ctl, 0xec50, 0xc); - - agnx_write32(ctl, AGNX_GCR_DISCOVMOD, 0x3); - agnx_write32(ctl, AGNX_GCR_RXOVERIDE, 0x0); - agnx_write32(ctl, 0x11010, 0x6e); - agnx_write32(ctl, 0x11014, 0x6c); - - spi_rf_write(ctl, RF_CHIP0|RF_CHIP1, 0x1201); - - /* Calibrate the Antenna */ - /* antenna_calibrate(priv); */ - /* Calibrate the TxLocalOscillator */ - calibrate_oscillator(priv); - - reg = agnx_read32(ctl, AGNX_PM_PMCTL); - reg &= ~0x8; - agnx_write32(ctl, AGNX_PM_PMCTL, reg); - agnx_write32(ctl, AGNX_GCR_GAININIT, 0xa); - agnx_write32(ctl, AGNX_GCR_THCD, 0x0); - - agnx_write32(ctl, 0x11018, 0xb); - agnx_write32(ctl, AGNX_GCR_DISCOVMOD, 0x0); - - /* Write Frequency to Gain Control Channel */ - agnx_write32(ctl, AGNX_GCR_RXCHANEL, freq); - /* Write 0x140000/Freq to 0x9c08 */ - reg = 0x140000/freq; - agnx_write32(ctl, 0x9c08, reg); - - reg = agnx_read32(ctl, AGNX_PM_SOFTRST); - reg &= ~0x80100030; - agnx_write32(ctl, AGNX_PM_SOFTRST, reg); - - reg = agnx_read32(ctl, AGNX_PM_PLLCTL); - reg &= ~0x20009; - reg |= 0x1; - agnx_write32(ctl, AGNX_PM_PLLCTL, reg); - - agnx_write32(ctl, AGNX_ACI_MODE, 0x0); - -/* FIXME According to Number of Chains: */ - -/* 1. 1: */ -/* 1. Write 0x1203 to RF Chip 0 */ -/* 2. Write 0x1200 to RF Chips 1 +2 */ -/* 2. 2: */ -/* 1. Write 0x1203 to RF Chip 0 */ -/* 2. Write 0x1200 to RF Chip 2 */ -/* 3. Write 0x1201 to RF Chip 1 */ -/* 3. 3: */ -/* 1. Write 0x1203 to RF Chip 0 */ -/* 2. Write 0x1201 to RF Chip 1 + 2 */ -/* 4. 4: */ -/* 1. Write 0x1203 to RF Chip 0 + 1 */ -/* 2. Write 0x1200 to RF Chip 2 */ - -/* 5. 6: */ - spi_rf_write(ctl, RF_CHIP0|RF_CHIP1, 0x1203); - spi_rf_write(ctl, RF_CHIP2, 0x1201); - - spi_rf_write(ctl, RF_CHIP0|RF_CHIP1|RF_CHIP2, 0x1000); - agnx_write32(ctl, AGNX_GCR_RXOVERIDE, 0x0); - - /* FIXME Set the Disable Tx interrupt bit in Interrupt Mask - (Or 0x20000 to Interrupt Mask) */ -/* reg = agnx_read32(ctl, AGNX_INT_MASK); */ -/* reg |= IRQ_TX_DISABLE; */ -/* agnx_write32(ctl, AGNX_INT_MASK, reg); */ - - agnx_write32(ctl, AGNX_GCR_RSTGCTL, 0x1); - agnx_write32(ctl, AGNX_GCR_RSTGCTL, 0x0); - - /* Configure the Antenna */ - antenna_config(priv); - - /* Write 0x0 to Discovery Mode Enable detect G, B, A packet? */ - agnx_write32(ctl, AGNX_GCR_DISCOVMOD, 0); - - reg = agnx_read32(ctl, AGNX_RXM_REQRATE); - reg |= 0x80000000; - agnx_write32(ctl, AGNX_RXM_REQRATE, reg); - agnx_write32(ctl, AGNX_GCR_RSTGCTL, 0x1); - agnx_write32(ctl, AGNX_GCR_RSTGCTL, 0x0); - - /* enable radio on and the power LED */ - reg = agnx_read32(ctl, AGNX_SYSITF_GPIOUT); - reg &= ~0x1; - reg |= 0x2; - agnx_write32(ctl, AGNX_SYSITF_GPIOUT, reg); - - reg = agnx_read32(ctl, AGNX_TXM_CTL); - reg |= 0x1; - agnx_write32(ctl, AGNX_TXM_CTL, reg); -} /* radio_channel_set */ - -static void base_band_filter_calibrate(struct agnx_priv *priv) -{ - void __iomem *ctl = priv->ctl; - - spi_rf_write(ctl, RF_CHIP0|RF_CHIP1|RF_CHIP2, 0x1700); - spi_rf_write(ctl, RF_CHIP0|RF_CHIP1|RF_CHIP2, 0x1001); - agnx_write32(ctl, AGNX_GCR_FORCECTLCLK, 0x0); - spi_rc_write(ctl, RF_CHIP0, 0x27); - spi_rc_write(ctl, RF_CHIP1, 0x27); - spi_rc_write(ctl, RF_CHIP2, 0x27); - agnx_write32(ctl, AGNX_GCR_FORCECTLCLK, 0x1); -} - -static void print_offset(struct agnx_priv *priv, u32 chain) -{ - void __iomem *ctl = priv->ctl; - u32 offset; - - iowrite32((chain), ctl + AGNX_ACI_SELCHAIN); - udelay(10); - offset = (ioread32(ctl + AGNX_ACI_OFFSET)); - printk(PFX "Chain is 0x%x, Offset is 0x%x\n", chain, offset); -} - -void print_offsets(struct agnx_priv *priv) -{ - print_offset(priv, 0); - print_offset(priv, 4); - print_offset(priv, 1); - print_offset(priv, 5); - print_offset(priv, 2); - print_offset(priv, 6); -} - - -struct chains { - u32 cali; /* calibrate value*/ - -#define NEED_CALIBRATE 0 -#define SUCCESS_CALIBRATE 1 - int status; -}; - -static void chain_calibrate(struct agnx_priv *priv, struct chains *chains, - unsigned int num) -{ - void __iomem *ctl = priv->ctl; - u32 calibra = chains[num].cali; - - if (num < 3) - calibra |= 0x1400; - else - calibra |= 0x1500; - - switch (num) { - case 0: - case 4: - spi_rf_write(ctl, RF_CHIP0, calibra); - break; - case 1: - case 5: - spi_rf_write(ctl, RF_CHIP1, calibra); - break; - case 2: - case 6: - spi_rf_write(ctl, RF_CHIP2, calibra); - break; - default: - BUG(); - } -} /* chain_calibrate */ - -static inline void get_calibrete_value(struct agnx_priv *priv, struct chains *chains, - unsigned int num) -{ - void __iomem *ctl = priv->ctl; - u32 offset; - - iowrite32((num), ctl + AGNX_ACI_SELCHAIN); - /* FIXME */ - udelay(10); - offset = (ioread32(ctl + AGNX_ACI_OFFSET)); - - if (offset < 0xf) { - chains[num].status = SUCCESS_CALIBRATE; - return; - } - - if (num == 0 || num == 1 || num == 2) { - if (0 == chains[num].cali) - chains[num].cali = 0xff; - else - chains[num].cali--; - } else - chains[num].cali++; - - chains[num].status = NEED_CALIBRATE; -} - -static inline void calibra_delay(struct agnx_priv *priv) -{ - void __iomem *ctl = priv->ctl; - u32 reg; - unsigned int i = 100; - - wmb(); - while (--i) { - reg = (ioread32(ctl + AGNX_ACI_STATUS)); - if (reg == 0x4000) - break; - udelay(10); - } - if (!i) - printk(PFX "calibration failed\n"); -} - -void do_calibration(struct agnx_priv *priv) -{ - void __iomem *ctl = priv->ctl; - struct chains chains[7]; - unsigned int i, j; - AGNX_TRACE; - - for (i = 0; i < 7; i++) { - if (i == 3) - continue; - - chains[i].cali = 0x7f; - chains[i].status = NEED_CALIBRATE; - } - - /* FIXME 0x300 is a magic number */ - for (j = 0; j < 0x300; j++) { - if (chains[0].status == SUCCESS_CALIBRATE && - chains[1].status == SUCCESS_CALIBRATE && - chains[2].status == SUCCESS_CALIBRATE && - chains[4].status == SUCCESS_CALIBRATE && - chains[5].status == SUCCESS_CALIBRATE && - chains[6].status == SUCCESS_CALIBRATE) - break; - - /* Attention, there is no chain 3 */ - for (i = 0; i < 7; i++) { - if (i == 3) - continue; - if (chains[i].status == NEED_CALIBRATE) - chain_calibrate(priv, chains, i); - } - /* Write 0x1 to Calibration Measure */ - iowrite32((0x1), ctl + AGNX_ACI_MEASURE); - calibra_delay(priv); - - for (i = 0; i < 7; i++) { - if (i == 3) - continue; - - get_calibrete_value(priv, chains, i); - } - } - printk(PFX "Clibrate times is %d\n", j); - print_offsets(priv); -} /* do_calibration */ - -void antenna_calibrate(struct agnx_priv *priv) -{ - void __iomem *ctl = priv->ctl; - u32 reg; - AGNX_TRACE; - - agnx_write32(ctl, AGNX_GCR_NLISTANT, 0x3); - agnx_write32(ctl, AGNX_GCR_NMEASANT, 0x3); - agnx_write32(ctl, AGNX_GCR_NACTIANT, 0x3); - agnx_write32(ctl, AGNX_GCR_NCAPTANT, 0x3); - - agnx_write32(ctl, AGNX_GCR_ANTCFG, 0x1f); - agnx_write32(ctl, AGNX_GCR_BOACT, 0x24); - agnx_write32(ctl, AGNX_GCR_BOINACT, 0x24); - agnx_write32(ctl, AGNX_GCR_BODYNA, 0x20); - agnx_write32(ctl, AGNX_GCR_THD0A, 0x64); - agnx_write32(ctl, AGNX_GCR_THD0AL, 0x64); - agnx_write32(ctl, AGNX_GCR_THD0B, 0x46); - agnx_write32(ctl, AGNX_GCR_THD0BTFEST, 0x3c); - agnx_write32(ctl, AGNX_GCR_SIGHTH, 0x64); - agnx_write32(ctl, AGNX_GCR_SIGLTH, 0x30); - - spi_rc_write(ctl, RF_CHIP0, 0x20); - /* Fixme */ - udelay(80); - /* 1. Should read 0x0 */ - reg = agnx_read32(ctl, AGNX_SPI_RLSW); - if (0x0 != reg) - printk(KERN_WARNING PFX "Unmatched rf chips result\n"); - spi_rf_write(ctl, RF_CHIP0|RF_CHIP1|RF_CHIP2, 0x1000); - - agnx_write32(ctl, AGNX_GCR_RXOVERIDE, 0x0); - - spi_rc_write(ctl, RF_CHIP0, 0x22); - udelay(80); - reg = agnx_read32(ctl, AGNX_SPI_RLSW); - if (0x0 != reg) - printk(KERN_WARNING PFX "Unmatched rf chips result\n"); - spi_rf_write(ctl, RF_CHIP0|RF_CHIP1|RF_CHIP2, 0x1005); - - agnx_write32(ctl, AGNX_GCR_RSTGCTL, 0x1); - agnx_write32(ctl, AGNX_GCR_RSTGCTL, 0x0); - - reg = agnx_read32(ctl, AGNX_PM_SOFTRST); - reg |= 0x1c000032; - agnx_write32(ctl, AGNX_PM_SOFTRST, reg); - reg = agnx_read32(ctl, AGNX_PM_PLLCTL); - reg |= 0x0003f07; - agnx_write32(ctl, AGNX_PM_PLLCTL, reg); - - reg = agnx_read32(ctl, 0xec50); - reg |= 0x40; - agnx_write32(ctl, 0xec50, reg); - - agnx_write32(ctl, AGNX_GCR_RXOVERIDE, 0xff8); - agnx_write32(ctl, AGNX_GCR_DISCOVMOD, 0x3); - - agnx_write32(ctl, AGNX_GCR_CHAINNUM, 0x6); - agnx_write32(ctl, 0x19874, 0x0); - spi_rf_write(ctl, RF_CHIP0|RF_CHIP1|RF_CHIP2, 0x1700); - - /* Calibrate the BaseBandFilter */ - base_band_filter_calibrate(priv); - - spi_rf_write(ctl, RF_CHIP0|RF_CHIP1|RF_CHIP2, 0x1002); - - agnx_write32(ctl, AGNX_GCR_GAINSET0, 0x1d); - agnx_write32(ctl, AGNX_GCR_GAINSET1, 0x1d); - agnx_write32(ctl, AGNX_GCR_GAINSET2, 0x1d); - agnx_write32(ctl, AGNX_GCR_GAINSETWRITE, 0x1); - - agnx_write32(ctl, AGNX_ACI_MODE, 0x1); - agnx_write32(ctl, AGNX_ACI_LEN, 0x3ff); - - agnx_write32(ctl, AGNX_ACI_TIMER1, 0x27); - agnx_write32(ctl, AGNX_ACI_TIMER2, 0x27); - - spi_rf_write(ctl, RF_CHIP0|RF_CHIP1|RF_CHIP2, 0x1400); - spi_rf_write(ctl, RF_CHIP0|RF_CHIP1|RF_CHIP2, 0x1500); - - /* Measure Calibration */ - agnx_write32(ctl, AGNX_ACI_MEASURE, 0x1); - calibra_delay(priv); - - /* do calibration */ - do_calibration(priv); - - agnx_write32(ctl, AGNX_GCR_RXOVERIDE, 0x0); - agnx_write32(ctl, AGNX_ACI_TIMER1, 0x21); - agnx_write32(ctl, AGNX_ACI_TIMER2, 0x27); - agnx_write32(ctl, AGNX_ACI_LEN, 0xf); - - reg = agnx_read32(ctl, AGNX_GCR_GAINSET0); - reg &= 0xf; - agnx_write32(ctl, AGNX_GCR_GAINSET0, reg); - reg = agnx_read32(ctl, AGNX_GCR_GAINSET1); - reg &= 0xf; - agnx_write32(ctl, AGNX_GCR_GAINSET1, reg); - reg = agnx_read32(ctl, AGNX_GCR_GAINSET2); - reg &= 0xf; - agnx_write32(ctl, AGNX_GCR_GAINSET2, reg); - - agnx_write32(ctl, AGNX_GCR_GAINSETWRITE, 0x0); - disable_receiver(priv); -} /* antenna_calibrate */ - -void __antenna_calibrate(struct agnx_priv *priv) -{ - void __iomem *ctl = priv->ctl; - u32 reg; - - /* Calibrate the BaseBandFilter */ - /* base_band_filter_calibrate(priv); */ - spi_rf_write(ctl, RF_CHIP0|RF_CHIP1|RF_CHIP2, 0x1002); - - - agnx_write32(ctl, AGNX_GCR_GAINSET0, 0x1d); - agnx_write32(ctl, AGNX_GCR_GAINSET1, 0x1d); - agnx_write32(ctl, AGNX_GCR_GAINSET2, 0x1d); - - agnx_write32(ctl, AGNX_GCR_GAINSETWRITE, 0x1); - - agnx_write32(ctl, AGNX_ACI_MODE, 0x1); - agnx_write32(ctl, AGNX_ACI_LEN, 0x3ff); - - - agnx_write32(ctl, AGNX_ACI_TIMER1, 0x27); - agnx_write32(ctl, AGNX_ACI_TIMER2, 0x27); - spi_rf_write(ctl, RF_CHIP0|RF_CHIP1|RF_CHIP2, 0x1400); - spi_rf_write(ctl, RF_CHIP0|RF_CHIP1|RF_CHIP2, 0x1500); - /* Measure Calibration */ - agnx_write32(ctl, AGNX_ACI_MEASURE, 0x1); - calibra_delay(priv); - do_calibration(priv); - agnx_write32(ctl, AGNX_GCR_RXOVERIDE, 0x0); - - agnx_write32(ctl, AGNX_ACI_TIMER1, 0x21); - agnx_write32(ctl, AGNX_ACI_TIMER2, 0x27); - - agnx_write32(ctl, AGNX_ACI_LEN, 0xf); - - reg = agnx_read32(ctl, AGNX_GCR_GAINSET0); - reg &= 0xf; - agnx_write32(ctl, AGNX_GCR_GAINSET0, reg); - reg = agnx_read32(ctl, AGNX_GCR_GAINSET1); - reg &= 0xf; - agnx_write32(ctl, AGNX_GCR_GAINSET1, reg); - reg = agnx_read32(ctl, AGNX_GCR_GAINSET2); - reg &= 0xf; - agnx_write32(ctl, AGNX_GCR_GAINSET2, reg); - - - agnx_write32(ctl, AGNX_GCR_GAINSETWRITE, 0x0); - - /* Write 0x3 Gain Control Discovery Mode */ - enable_receiver(priv); -} - -int agnx_set_channel(struct agnx_priv *priv, unsigned int channel) -{ - AGNX_TRACE; - - printk(KERN_ERR PFX "Channel is %d %s\n", channel, __func__); - radio_channel_set(priv, channel); - return 0; -} diff --git a/drivers/staging/agnx/sta.c b/drivers/staging/agnx/sta.c deleted file mode 100644 index 3e7db5e2811a..000000000000 --- a/drivers/staging/agnx/sta.c +++ /dev/null @@ -1,218 +0,0 @@ -#include <linux/delay.h> -#include <linux/etherdevice.h> -#include "phy.h" -#include "sta.h" -#include "debug.h" - -void hash_read(struct agnx_priv *priv, u32 reghi, u32 reglo, u8 sta_id) -{ - void __iomem *ctl = priv->ctl; - - reglo &= 0xFFFF; - reglo |= 0x30000000; - reglo |= 0x40000000; /* Set status busy */ - reglo |= sta_id << 16; - - iowrite32(0, ctl + AGNX_RXM_HASH_CMD_FLAG); - iowrite32(reghi, ctl + AGNX_RXM_HASH_CMD_HIGH); - iowrite32(reglo, ctl + AGNX_RXM_HASH_CMD_LOW); - - reghi = ioread32(ctl + AGNX_RXM_HASH_CMD_HIGH); - reglo = ioread32(ctl + AGNX_RXM_HASH_CMD_LOW); - printk(PFX "RX hash cmd are : %.8x%.8x\n", reghi, reglo); -} - -void hash_write(struct agnx_priv *priv, const u8 *mac_addr, u8 sta_id) -{ - void __iomem *ctl = priv->ctl; - u32 reghi, reglo; - - if (!is_valid_ether_addr(mac_addr)) - printk(KERN_WARNING PFX "Update hash table: Invalid hwaddr!\n"); - - reghi = mac_addr[0] << 24 | mac_addr[1] << 16 | mac_addr[2] << 8 | mac_addr[3]; - reglo = mac_addr[4] << 8 | mac_addr[5]; - reglo |= 0x10000000; /* Set hash commmand */ - reglo |= 0x40000000; /* Set status busy */ - reglo |= sta_id << 16; - - iowrite32(0, ctl + AGNX_RXM_HASH_CMD_FLAG); - iowrite32(reghi, ctl + AGNX_RXM_HASH_CMD_HIGH); - iowrite32(reglo, ctl + AGNX_RXM_HASH_CMD_LOW); - - reglo = ioread32(ctl + AGNX_RXM_HASH_CMD_LOW); - if (!(reglo & 0x80000000)) - printk(KERN_WARNING PFX "Update hash table failed\n"); -} - -void hash_delete(struct agnx_priv *priv, u32 reghi, u32 reglo, u8 sta_id) -{ - void __iomem *ctl = priv->ctl; - - reglo &= 0xFFFF; - reglo |= 0x20000000; - reglo |= 0x40000000; /* Set status busy */ - reglo |= sta_id << 16; - - iowrite32(0, ctl + AGNX_RXM_HASH_CMD_FLAG); - iowrite32(reghi, ctl + AGNX_RXM_HASH_CMD_HIGH); - iowrite32(reglo, ctl + AGNX_RXM_HASH_CMD_LOW); - reghi = ioread32(ctl + AGNX_RXM_HASH_CMD_HIGH); - - reglo = ioread32(ctl + AGNX_RXM_HASH_CMD_LOW); - printk(PFX "RX hash cmd are : %.8x%.8x\n", reghi, reglo); - -} - -void hash_dump(struct agnx_priv *priv, u8 sta_id) -{ - void __iomem *ctl = priv->ctl; - u32 reghi, reglo; - - reglo = 0x40000000; /* status bit */ - iowrite32(reglo, ctl + AGNX_RXM_HASH_CMD_LOW); - iowrite32(sta_id << 16, ctl + AGNX_RXM_HASH_DUMP_DATA); - - udelay(80); - - reghi = ioread32(ctl + AGNX_RXM_HASH_CMD_HIGH); - reglo = ioread32(ctl + AGNX_RXM_HASH_CMD_LOW); - printk(PFX "hash cmd are : %.8x%.8x\n", reghi, reglo); - reghi = ioread32(ctl + AGNX_RXM_HASH_CMD_FLAG); - printk(PFX "hash flag is : %.8x\n", reghi); - reghi = ioread32(ctl + AGNX_RXM_HASH_DUMP_MST); - reglo = ioread32(ctl + AGNX_RXM_HASH_DUMP_LST); - printk(PFX "hash dump mst lst: %.8x%.8x\n", reghi, reglo); - reghi = ioread32(ctl + AGNX_RXM_HASH_DUMP_DATA); - printk(PFX "hash dump data: %.8x\n", reghi); -} - -void get_sta_power(struct agnx_priv *priv, struct agnx_sta_power *power, unsigned int sta_idx) -{ - void __iomem *ctl = priv->ctl; - memcpy_fromio(power, ctl + AGNX_TXM_STAPOWTEMP + sizeof(*power) * sta_idx, - sizeof(*power)); -} - -inline void -set_sta_power(struct agnx_priv *priv, struct agnx_sta_power *power, unsigned int sta_idx) -{ - void __iomem *ctl = priv->ctl; - /* FIXME 2. Write Template to offset + station number */ - memcpy_toio(ctl + AGNX_TXM_STAPOWTEMP + sizeof(*power) * sta_idx, - power, sizeof(*power)); -} - - -void get_sta_tx_wq(struct agnx_priv *priv, struct agnx_sta_tx_wq *tx_wq, - unsigned int sta_idx, unsigned int wq_idx) -{ - void __iomem *data = priv->data; - memcpy_fromio(tx_wq, data + AGNX_PDU_TX_WQ + sizeof(*tx_wq) * STA_TX_WQ_NUM * sta_idx + - sizeof(*tx_wq) * wq_idx, sizeof(*tx_wq)); - -} - -inline void set_sta_tx_wq(struct agnx_priv *priv, struct agnx_sta_tx_wq *tx_wq, - unsigned int sta_idx, unsigned int wq_idx) -{ - void __iomem *data = priv->data; - memcpy_toio(data + AGNX_PDU_TX_WQ + sizeof(*tx_wq) * STA_TX_WQ_NUM * sta_idx + - sizeof(*tx_wq) * wq_idx, tx_wq, sizeof(*tx_wq)); -} - - -void get_sta(struct agnx_priv *priv, struct agnx_sta *sta, unsigned int sta_idx) -{ - void __iomem *data = priv->data; - - memcpy_fromio(sta, data + AGNX_PDUPOOL + sizeof(*sta) * sta_idx, - sizeof(*sta)); -} - -inline void set_sta(struct agnx_priv *priv, struct agnx_sta *sta, unsigned int sta_idx) -{ - void __iomem *data = priv->data; - - memcpy_toio(data + AGNX_PDUPOOL + sizeof(*sta) * sta_idx, - sta, sizeof(*sta)); -} - -/* FIXME */ -void sta_power_init(struct agnx_priv *priv, unsigned int sta_idx) -{ - struct agnx_sta_power power; - u32 reg; - AGNX_TRACE; - - memset(&power, 0, sizeof(power)); - reg = agnx_set_bits(EDCF, EDCF_SHIFT, 0x1); - power.reg = cpu_to_le32(reg); - set_sta_power(priv, &power, sta_idx); - udelay(40); -} /* add_power_template */ - - -/* @num: The #number of station that is visible to the card */ -static void sta_tx_workqueue_init(struct agnx_priv *priv, unsigned int sta_idx) -{ - struct agnx_sta_tx_wq tx_wq; - u32 reg; - unsigned int i; - - memset(&tx_wq, 0, sizeof(tx_wq)); - - reg = agnx_set_bits(WORK_QUEUE_VALID, WORK_QUEUE_VALID_SHIFT, 1); - reg |= agnx_set_bits(WORK_QUEUE_ACK_TYPE, WORK_QUEUE_ACK_TYPE_SHIFT, 1); -/* reg |= agnx_set_bits(WORK_QUEUE_ACK_TYPE, WORK_QUEUE_ACK_TYPE_SHIFT, 0); */ - tx_wq.reg2 |= cpu_to_le32(reg); - - /* Suppose all 8 traffic class are used */ - for (i = 0; i < STA_TX_WQ_NUM; i++) - set_sta_tx_wq(priv, &tx_wq, sta_idx, i); -} /* sta_tx_workqueue_init */ - - -static void sta_traffic_init(struct agnx_sta_traffic *traffic) -{ - u32 reg; - memset(traffic, 0, sizeof(*traffic)); - - reg = agnx_set_bits(NEW_PACKET, NEW_PACKET_SHIFT, 1); - reg |= agnx_set_bits(TRAFFIC_VALID, TRAFFIC_VALID_SHIFT, 1); -/* reg |= agnx_set_bits(TRAFFIC_ACK_TYPE, TRAFFIC_ACK_TYPE_SHIFT, 1); */ - traffic->reg0 = cpu_to_le32(reg); - - /* 3. setting RX Sequence Number to 4095 */ - reg = agnx_set_bits(RX_SEQUENCE_NUM, RX_SEQUENCE_NUM_SHIFT, 4095); - traffic->reg1 = cpu_to_le32(reg); -} - - -/* @num: The #number of station that is visible to the card */ -void sta_init(struct agnx_priv *priv, unsigned int sta_idx) -{ - /* FIXME the length of sta is 256 bytes Is that - * dangerous to stack overflow? */ - struct agnx_sta sta; - u32 reg; - int i; - - memset(&sta, 0, sizeof(sta)); - /* Set valid to 1 */ - reg = agnx_set_bits(STATION_VALID, STATION_VALID_SHIFT, 1); - /* Set Enable Concatenation to 0 (?) */ - reg |= agnx_set_bits(ENABLE_CONCATENATION, ENABLE_CONCATENATION_SHIFT, 0); - /* Set Enable Decompression to 0 (?) */ - reg |= agnx_set_bits(ENABLE_DECOMPRESSION, ENABLE_DECOMPRESSION_SHIFT, 0); - sta.reg = cpu_to_le32(reg); - - /* Initialize each of the Traffic Class Structures by: */ - for (i = 0; i < 8; i++) - sta_traffic_init(sta.traffic + i); - - set_sta(priv, &sta, sta_idx); - sta_tx_workqueue_init(priv, sta_idx); -} /* sta_descriptor_init */ - - diff --git a/drivers/staging/agnx/sta.h b/drivers/staging/agnx/sta.h deleted file mode 100644 index fd504e3f3870..000000000000 --- a/drivers/staging/agnx/sta.h +++ /dev/null @@ -1,222 +0,0 @@ -#ifndef AGNX_STA_H_ -#define AGNX_STA_H_ - -#define STA_TX_WQ_NUM 8 /* The number of TX workqueue one STA has */ - -struct agnx_hash_cmd { - __be32 cmdhi; -#define MACLO 0xFFFF0000 -#define MACLO_SHIFT 16 -#define STA_ID 0x0000FFF0 -#define STA_ID_SHIFT 4 -#define CMD 0x0000000C -#define CMD_SHIFT 2 -#define STATUS 0x00000002 -#define STATUS_SHIFT 1 -#define PASS 0x00000001 -#define PASS_SHIFT 1 - __be32 cmdlo; -} __attribute__((__packed__)); - - -/* - * Station Power Template - * FIXME Just for agn100 yet - */ -struct agnx_sta_power { - __le32 reg; -#define SIGNAL 0x000000FF /* signal */ -#define SIGNAL_SHIFT 0 -#define RATE 0x00000F00 -#define RATE_SHIFT 8 -#define TIFS 0x00001000 -#define TIFS_SHIFT 12 -#define EDCF 0x00002000 -#define EDCF_SHIFT 13 -#define CHANNEL_BOND 0x00004000 -#define CHANNEL_BOND_SHIFT 14 -#define PHY_MODE 0x00038000 -#define PHY_MODE_SHIFT 15 -#define POWER_LEVEL 0x007C0000 -#define POWER_LEVEL_SHIFT 18 -#define NUM_TRANSMITTERS 0x00800000 -#define NUM_TRANSMITTERS_SHIFT 23 -} __attribute__((__packed__)); - -/* - * TX Workqueue Descriptor - */ -struct agnx_sta_tx_wq { - __le32 reg0; -#define HEAD_POINTER_LOW 0xFF000000 /* Head pointer low */ -#define HEAD_POINTER_LOW_SHIFT 24 -#define TAIL_POINTER 0x00FFFFFF /* Tail pointer */ -#define TAIL_POINTER_SHIFT 0 - - __le32 reg3; -#define ACK_POINTER_LOW 0xFFFF0000 /* ACK pointer low */ -#define ACK_POINTER_LOW_SHIFT 16 -#define HEAD_POINTER_HIGH 0x0000FFFF /* Head pointer high */ -#define HEAD_POINTER_HIGH_SHIFT 0 - - __le32 reg1; -/* ACK timeout tail packet count */ -#define ACK_TIMOUT_TAIL_PACK_CNT 0xFFF00000 -#define ACK_TIMOUT_TAIL_PACK_CNT_SHIFT 20 -/* Head timeout tail packet count */ -#define HEAD_TIMOUT_TAIL_PACK_CNT 0x000FFF00 -#define HEAD_TIMOUT_TAIL_PACK_CNT_SHIFT 8 -#define ACK_POINTER_HIGH 0x000000FF /* ACK pointer high */ -#define ACK_POINTER_HIGH_SHIFT 0 - - __le32 reg2; -#define WORK_QUEUE_VALID 0x80000000 /* valid */ -#define WORK_QUEUE_VALID_SHIFT 31 -#define WORK_QUEUE_ACK_TYPE 0x40000000 /* ACK type */ -#define WORK_QUEUE_ACK_TYPE_SHIFT 30 -/* Head timeout window limit fragmentation count */ -#define HEAD_TIMOUT_WIN_LIM_FRAG_CNT 0x3FFF0000 -#define HEAD_TIMOUT_WIN_LIM_FRAG_CNT_SHIFT 16 -/* Head timeout window limit byte count */ -#define HEAD_TIMOUT_WIN_LIM_BYTE_CNT 0x0000FFFF -#define HEAD_TIMOUT_WIN_LIM_BYTE_CNT_SHIFT 0 -} __attribute__((__packed__)); - - -/* - * Traffic Class Structure - */ -struct agnx_sta_traffic { - __le32 reg0; -#define ACK_TIMOUT_CNT 0xFF800000 /* ACK Timeout Counts */ -#define ACK_TIMOUT_CNT_SHIFT 23 -#define TRAFFIC_ACK_TYPE 0x00600000 /* ACK Type */ -#define TRAFFIC_ACK_TYPE_SHIFT 21 -#define NEW_PACKET 0x00100000 /* New Packet */ -#define NEW_PACKET_SHIFT 20 -#define TRAFFIC_VALID 0x00080000 /* Valid */ -#define TRAFFIC_VALID_SHIFT 19 -#define RX_HDR_DESC_POINTER 0x0007FFFF /* RX Header Descripter pointer */ -#define RX_HDR_DESC_POINTER_SHIFT 0 - - __le32 reg1; -#define RX_PACKET_TIMESTAMP 0xFFFF0000 /* RX Packet Timestamp */ -#define RX_PACKET_TIMESTAMP_SHIFT 16 -#define TRAFFIC_RESERVED 0x0000E000 /* Reserved */ -#define TRAFFIC_RESERVED_SHIFT 13 -#define SV 0x00001000 /* sv */ -#define SV_SHIFT 12 -#define RX_SEQUENCE_NUM 0x00000FFF /* RX Sequence Number */ -#define RX_SEQUENCE_NUM_SHIFT 0 - - __le32 tx_replay_cnt_low; /* TX Replay Counter Low */ - - __le16 tx_replay_cnt_high; /* TX Replay Counter High */ - __le16 rx_replay_cnt_high; /* RX Replay Counter High */ - - __be32 rx_replay_cnt_low; /* RX Replay Counter Low */ -} __attribute__((__packed__)); - -/* - * Station Descriptors - */ -struct agnx_sta { - __le32 tx_session_keys[4]; /* Transmit Session Key (0-3) */ - __le32 rx_session_keys[4]; /* Receive Session Key (0-3) */ - - __le32 reg; -#define ID_1 0xC0000000 /* id 1 */ -#define ID_1_SHIFT 30 -#define ID_0 0x30000000 /* id 0 */ -#define ID_0_SHIFT 28 -#define ENABLE_CONCATENATION 0x0FF00000 /* Enable concatenation */ -#define ENABLE_CONCATENATION_SHIFT 20 -#define ENABLE_DECOMPRESSION 0x000FF000 /* Enable decompression */ -#define ENABLE_DECOMPRESSION_SHIFT 12 -#define STA_RESERVED 0x00000C00 /* Reserved */ -#define STA_RESERVED_SHIFT 10 -#define EAP 0x00000200 /* EAP */ -#define EAP_SHIFT 9 -#define ED_NULL 0x00000100 /* ED NULL */ -#define ED_NULL_SHIFT 8 -#define ENCRYPTION_POLICY 0x000000E0 /* Encryption Policy */ -#define ENCRYPTION_POLICY_SHIFT 5 -#define DEFINED_KEY_ID 0x00000018 /* Defined Key ID */ -#define DEFINED_KEY_ID_SHIFT 3 -#define FIXED_KEY 0x00000004 /* Fixed Key */ -#define FIXED_KEY_SHIFT 2 -#define KEY_VALID 0x00000002 /* Key Valid */ -#define KEY_VALID_SHIFT 1 -#define STATION_VALID 0x00000001 /* Station Valid */ -#define STATION_VALID_SHIFT 0 - - __le32 tx_aes_blks_unicast; /* TX AES Blks Unicast */ - __le32 rx_aes_blks_unicast; /* RX AES Blks Unicast */ - - __le16 aes_format_err_unicast_cnt; /* AES Format Error Unicast Counts */ - __le16 aes_replay_unicast; /* AES Replay Unicast */ - - __le16 aes_decrypt_err_unicast; /* AES Decrypt Error Unicast */ - __le16 aes_decrypt_err_default; /* AES Decrypt Error default */ - - __le16 single_retry_packets; /* Single Retry Packets */ - __le16 failed_tx_packets; /* Failed Tx Packets */ - - __le16 muti_retry_packets; /* Multiple Retry Packets */ - __le16 ack_timeouts; /* ACK Timeouts */ - - __le16 frag_tx_cnt; /* Fragment TX Counts */ - __le16 rts_brq_sent; /* RTS Brq Sent */ - - __le16 tx_packets; /* TX Packets */ - __le16 cts_back_timeout; /* CTS Back Timeout */ - - __le32 phy_stats_high; /* PHY Stats High */ - __le32 phy_stats_low; /* PHY Stats Low */ - - struct agnx_sta_traffic traffic[8]; /* Traffic Class Structure (8) */ - - __le16 traffic_class0_frag_success; /* Traffic Class 0 Fragment Success */ - __le16 traffic_class1_frag_success; /* Traffic Class 1 Fragment Success */ - __le16 traffic_class2_frag_success; /* Traffic Class 2 Fragment Success */ - __le16 traffic_class3_frag_success; /* Traffic Class 3 Fragment Success */ - __le16 traffic_class4_frag_success; /* Traffic Class 4 Fragment Success */ - __le16 traffic_class5_frag_success; /* Traffic Class 5 Fragment Success */ - __le16 traffic_class6_frag_success; /* Traffic Class 6 Fragment Success */ - __le16 traffic_class7_frag_success; /* Traffic Class 7 Fragment Success */ - - __le16 num_frag_non_prime_rates; /* number of Fragments for non-prime rates */ - __le16 ack_timeout_non_prime_rates; /* ACK Timeout for non-prime rates */ - -} __attribute__((__packed__)); - - -struct agnx_beacon_hdr { - struct agnx_sta_power power; /* Tx Station Power Template */ - u8 phy_hdr[6]; /* PHY Hdr */ - u8 frame_len_lo; /* Frame Length Lo */ - u8 frame_len_hi; /* Frame Length Hi */ - u8 mac_hdr[24]; /* MAC Header */ - /* FIXME */ - /* 802.11(abg) beacon */ -} __attribute__((__packed__)); - -void hash_write(struct agnx_priv *priv, const u8 *mac_addr, u8 sta_id); -void hash_dump(struct agnx_priv *priv, u8 sta_id); -void hash_read(struct agnx_priv *priv, u32 reghi, u32 reglo, u8 sta_id); -void hash_delete(struct agnx_priv *priv, u32 reghi, u32 reglo, u8 sta_id); - -void get_sta_power(struct agnx_priv *priv, struct agnx_sta_power *power, unsigned int sta_idx); -void set_sta_power(struct agnx_priv *priv, struct agnx_sta_power *power, - unsigned int sta_idx); -void get_sta_tx_wq(struct agnx_priv *priv, struct agnx_sta_tx_wq *tx_wq, - unsigned int sta_idx, unsigned int wq_idx); -void set_sta_tx_wq(struct agnx_priv *priv, struct agnx_sta_tx_wq *tx_wq, - unsigned int sta_idx, unsigned int wq_idx); -void get_sta(struct agnx_priv *priv, struct agnx_sta *sta, unsigned int sta_idx); -void set_sta(struct agnx_priv *priv, struct agnx_sta *sta, unsigned int sta_idx); - -void sta_power_init(struct agnx_priv *priv, unsigned int num); -void sta_init(struct agnx_priv *priv, unsigned int num); - -#endif /* AGNX_STA_H_ */ diff --git a/drivers/staging/agnx/table.c b/drivers/staging/agnx/table.c deleted file mode 100644 index b52fef9db0e3..000000000000 --- a/drivers/staging/agnx/table.c +++ /dev/null @@ -1,168 +0,0 @@ -#include <linux/pci.h> -#include <linux/delay.h> -#include "agnx.h" -#include "debug.h" -#include "phy.h" - -static const u32 -tx_fir_table[] = { 0x19, 0x5d, 0xce, 0x151, 0x1c3, 0x1ff, 0x1ea, 0x17c, 0xcf, - 0x19, 0x38e, 0x350, 0x362, 0x3ad, 0x5, 0x44, 0x59, 0x49, - 0x21, 0x3f7, 0x3e0, 0x3e3, 0x3f3, 0x0 }; - -void tx_fir_table_init(struct agnx_priv *priv) -{ - void __iomem *ctl = priv->ctl; - int i; - - for (i = 0; i < ARRAY_SIZE(tx_fir_table); i++) - iowrite32(tx_fir_table[i], ctl + AGNX_FIR_BASE + i*4); -} /* fir_table_setup */ - - -static const u32 -gain_table[] = { 0x8, 0x8, 0xf, 0x13, 0x17, 0x1b, 0x1f, 0x23, 0x27, 0x2b, - 0x2f, 0x33, 0x37, 0x3b, 0x3f, 0x43, 0x47, 0x4b, 0x4f, - 0x53, 0x57, 0x5b, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, - 0x5f, 0x5f, 0x5f, 0x5f }; - -void gain_table_init(struct agnx_priv *priv) -{ - void __iomem *ctl = priv->ctl; - int i; - - for (i = 0; i < ARRAY_SIZE(gain_table); i++) { - iowrite32(gain_table[i], ctl + AGNX_GAIN_TABLE + i*4); - iowrite32(gain_table[i], ctl + AGNX_GAIN_TABLE + i*4 + 0x80); - } -} /* gain_table_init */ - -void monitor_gain_table_init(struct agnx_priv *priv) -{ - void __iomem *ctl = priv->ctl; - unsigned int i; - - for (i = 0; i < 0x44; i += 4) { - iowrite32(0x61, ctl + AGNX_MONGCR_BASE + i); - iowrite32(0x61, ctl + AGNX_MONGCR_BASE + 0x200 + i); - } - for (i = 0x44; i < 0x64; i += 4) { - iowrite32(0x6e, ctl + AGNX_MONGCR_BASE + i); - iowrite32(0x6e, ctl + AGNX_MONGCR_BASE + 0x200 + i); - } - for (i = 0x64; i < 0x94; i += 4) { - iowrite32(0x7a, ctl + AGNX_MONGCR_BASE + i); - iowrite32(0x7a, ctl + AGNX_MONGCR_BASE + 0x200 + i); - } - for (i = 0x94; i < 0xdc; i += 4) { - iowrite32(0x87, ctl + AGNX_MONGCR_BASE + i); - iowrite32(0x87, ctl + AGNX_MONGCR_BASE + 0x200 + i); - } - for (i = 0xdc; i < 0x148; i += 4) { - iowrite32(0x95, ctl + AGNX_MONGCR_BASE + i); - iowrite32(0x95, ctl + AGNX_MONGCR_BASE + 0x200 + i); - } - for (i = 0x148; i < 0x1e8; i += 4) { - iowrite32(0xa2, ctl + AGNX_MONGCR_BASE + i); - iowrite32(0xa2, ctl + AGNX_MONGCR_BASE + 0x200 + i); - } - for (i = 0x1e8; i <= 0x1fc; i += 4) { - iowrite32(0xb0, ctl + AGNX_MONGCR_BASE + i); - iowrite32(0xb0, ctl + AGNX_MONGCR_BASE + 0x200 + i); - } -} /* monitor_gain_table_init */ - - -void routing_table_init(struct agnx_priv *priv) -{ - void __iomem *ctl = priv->ctl; - unsigned int type, subtype; - u32 reg; - - disable_receiver(priv); - - for (type = 0; type < 0x3; type++) { - for (subtype = 0; subtype < 0x10; subtype++) { - /* 1. Set Routing table to R/W and to Return status on Read */ - reg = (type << ROUTAB_TYPE_SHIFT) | - (subtype << ROUTAB_SUBTYPE_SHIFT); - reg |= (1 << ROUTAB_RW_SHIFT) | (1 << ROUTAB_STATUS_SHIFT); - if (type == ROUTAB_TYPE_DATA) { - /* NULL goes to RFP */ - if (subtype == ROUTAB_SUBTYPE_NULL) -/* reg |= ROUTAB_ROUTE_RFP; */ - reg |= ROUTAB_ROUTE_CPU; - /* QOS NULL goes to CPU */ - else if (subtype == ROUTAB_SUBTYPE_QOSNULL) - reg |= ROUTAB_ROUTE_CPU; - /* All Data and QOS data subtypes go to Encryption */ - else if ((subtype == ROUTAB_SUBTYPE_DATA) || - (subtype == ROUTAB_SUBTYPE_DATAACK) || - (subtype == ROUTAB_SUBTYPE_DATAPOLL) || - (subtype == ROUTAB_SUBTYPE_DATAPOLLACK) || - (subtype == ROUTAB_SUBTYPE_QOSDATA) || - (subtype == ROUTAB_SUBTYPE_QOSDATAACK) || - (subtype == ROUTAB_SUBTYPE_QOSDATAPOLL) || - (subtype == ROUTAB_SUBTYPE_QOSDATAACKPOLL)) - reg |= ROUTAB_ROUTE_ENCRY; -/* reg |= ROUTAB_ROUTE_CPU; */ - /*Drop NULL and QOS NULL ack, poll and poll ack*/ - else if ((subtype == ROUTAB_SUBTYPE_NULLACK) || - (subtype == ROUTAB_SUBTYPE_QOSNULLACK) || - (subtype == ROUTAB_SUBTYPE_NULLPOLL) || - (subtype == ROUTAB_SUBTYPE_QOSNULLPOLL) || - (subtype == ROUTAB_SUBTYPE_NULLPOLLACK) || - (subtype == ROUTAB_SUBTYPE_QOSNULLPOLLACK)) -/* reg |= ROUTAB_ROUTE_DROP; */ - reg |= ROUTAB_ROUTE_CPU; - } else { - reg |= (ROUTAB_ROUTE_CPU); - } - iowrite32(reg, ctl + AGNX_RXM_ROUTAB); - /* Check to verify that the status bit cleared */ - routing_table_delay(); - } - } - enable_receiver(priv); -} /* routing_table_init */ - -void tx_engine_lookup_tbl_init(struct agnx_priv *priv) -{ - void __iomem *data = priv->data; - unsigned int i; - - for (i = 0; i <= 28; i += 4) - iowrite32(0xb00c, data + AGNX_ENGINE_LOOKUP_TBL + i); - for (i = 32; i <= 120; i += 8) { - iowrite32(0x1e58, data + AGNX_ENGINE_LOOKUP_TBL + i); - iowrite32(0xb00c, data + AGNX_ENGINE_LOOKUP_TBL + i + 4); - } - - for (i = 128; i <= 156; i += 4) - iowrite32(0x980c, data + AGNX_ENGINE_LOOKUP_TBL + i); - for (i = 160; i <= 248; i += 8) { - iowrite32(0x1858, data + AGNX_ENGINE_LOOKUP_TBL + i); - iowrite32(0x980c, data + AGNX_ENGINE_LOOKUP_TBL + i + 4); - } - - for (i = 256; i <= 284; i += 4) - iowrite32(0x980c, data + AGNX_ENGINE_LOOKUP_TBL + i); - for (i = 288; i <= 376; i += 8) { - iowrite32(0x1a58, data + AGNX_ENGINE_LOOKUP_TBL + i); - iowrite32(0x1858, data + AGNX_ENGINE_LOOKUP_TBL + i + 4); - } - - for (i = 512; i <= 540; i += 4) - iowrite32(0xc00c, data + AGNX_ENGINE_LOOKUP_TBL + i); - for (i = 544; i <= 632; i += 8) { - iowrite32(0x2058, data + AGNX_ENGINE_LOOKUP_TBL + i); - iowrite32(0xc00c, data + AGNX_ENGINE_LOOKUP_TBL + i + 4); - } - - for (i = 640; i <= 668; i += 4) - iowrite32(0xc80c, data + AGNX_ENGINE_LOOKUP_TBL + i); - for (i = 672; i <= 764; i += 8) { - iowrite32(0x2258, data + AGNX_ENGINE_LOOKUP_TBL + i); - iowrite32(0xc80c, data + AGNX_ENGINE_LOOKUP_TBL + i + 4); - } -} - diff --git a/drivers/staging/agnx/table.h b/drivers/staging/agnx/table.h deleted file mode 100644 index f0626b5ee86b..000000000000 --- a/drivers/staging/agnx/table.h +++ /dev/null @@ -1,10 +0,0 @@ -#ifndef AGNX_TABLE_H_ -#define AGNX_TABLE_H_ - -void tx_fir_table_init(struct agnx_priv *priv); -void gain_table_init(struct agnx_priv *priv); -void monitor_gain_table_init(struct agnx_priv *priv); -void routing_table_init(struct agnx_priv *priv); -void tx_engine_lookup_tbl_init(struct agnx_priv *priv); - -#endif /* AGNX_TABLE_H_ */ diff --git a/drivers/staging/agnx/xmit.c b/drivers/staging/agnx/xmit.c deleted file mode 100644 index 42db41070cf0..000000000000 --- a/drivers/staging/agnx/xmit.c +++ /dev/null @@ -1,836 +0,0 @@ -/** - * Airgo MIMO wireless driver - * - * Copyright (c) 2007 Li YanBo <dreamfly281@gmail.com> - - * Thanks for Jeff Williams <angelbane@gmail.com> do reverse engineer - * works and published the SPECS at http://airgo.wdwconsulting.net/mymoin - - * 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/pci.h> -#include <linux/delay.h> -#include "agnx.h" -#include "debug.h" -#include "phy.h" - -unsigned int rx_frame_cnt; -/* unsigned int local_tx_sent_cnt = 0; */ - -static inline void disable_rx_engine(struct agnx_priv *priv) -{ - void __iomem *ctl = priv->ctl; - iowrite32(0x100, ctl + AGNX_CIR_RXCTL); - /* Wait for RX Control to have the Disable Rx Interrupt (0x100) set */ - ioread32(ctl + AGNX_CIR_RXCTL); -} - -static inline void enable_rx_engine(struct agnx_priv *priv) -{ - void __iomem *ctl = priv->ctl; - iowrite32(0x80, ctl + AGNX_CIR_RXCTL); - ioread32(ctl + AGNX_CIR_RXCTL); -} - -inline void disable_rx_interrupt(struct agnx_priv *priv) -{ - void __iomem *ctl = priv->ctl; - u32 reg; - - disable_rx_engine(priv); - reg = ioread32(ctl + AGNX_CIR_RXCFG); - reg &= ~0x20; - iowrite32(reg, ctl + AGNX_CIR_RXCFG); - ioread32(ctl + AGNX_CIR_RXCFG); -} - -inline void enable_rx_interrupt(struct agnx_priv *priv) -{ - void __iomem *ctl = priv->ctl; - u32 reg; - - reg = ioread32(ctl + AGNX_CIR_RXCFG); - reg |= 0x20; - iowrite32(reg, ctl + AGNX_CIR_RXCFG); - ioread32(ctl + AGNX_CIR_RXCFG); - enable_rx_engine(priv); -} - -static inline void rx_desc_init(struct agnx_priv *priv, unsigned int idx) -{ - struct agnx_desc *desc = priv->rx.desc + idx; - struct agnx_info *info = priv->rx.info + idx; - - memset(info, 0, sizeof(*info)); - - info->dma_len = IEEE80211_MAX_RTS_THRESHOLD + sizeof(struct agnx_hdr); - info->skb = dev_alloc_skb(info->dma_len); - if (info->skb == NULL) - agnx_bug("refill err"); - - info->mapping = pci_map_single(priv->pdev, skb_tail_pointer(info->skb), - info->dma_len, PCI_DMA_FROMDEVICE); - memset(desc, 0, sizeof(*desc)); - desc->dma_addr = cpu_to_be32(info->mapping); - /* Set the owner to the card */ - desc->frag = cpu_to_be32(be32_to_cpu(desc->frag) | OWNER); -} - -static inline void rx_desc_reinit(struct agnx_priv *priv, unsigned int idx) -{ - struct agnx_info *info = priv->rx.info + idx; - - /* Cause ieee80211 will free the skb buffer, so we needn't to free it again?! */ - pci_unmap_single(priv->pdev, info->mapping, info->dma_len, PCI_DMA_FROMDEVICE); - rx_desc_init(priv, idx); -} - -static inline void rx_desc_reusing(struct agnx_priv *priv, unsigned int idx) -{ - struct agnx_desc *desc = priv->rx.desc + idx; - struct agnx_info *info = priv->rx.info + idx; - - memset(desc, 0, sizeof(*desc)); - desc->dma_addr = cpu_to_be32(info->mapping); - /* Set the owner to the card */ - desc->frag = cpu_to_be32(be32_to_cpu(desc->frag) | OWNER); -} - -static void rx_desc_free(struct agnx_priv *priv, unsigned int idx) -{ - struct agnx_desc *desc = priv->rx.desc + idx; - struct agnx_info *info = priv->rx.info + idx; - - BUG_ON(!desc || !info); - if (info->mapping) - pci_unmap_single(priv->pdev, info->mapping, info->dma_len, PCI_DMA_FROMDEVICE); - if (info->skb) - dev_kfree_skb(info->skb); - memset(info, 0, sizeof(*info)); - memset(desc, 0, sizeof(*desc)); -} - -static inline void __tx_desc_free(struct agnx_priv *priv, - struct agnx_desc *desc, struct agnx_info *info) -{ - BUG_ON(!desc || !info); - /* TODO make sure mapping, skb and len are consistency */ - if (info->mapping) - pci_unmap_single(priv->pdev, info->mapping, - info->dma_len, PCI_DMA_TODEVICE); - if (info->type == PACKET) - dev_kfree_skb(info->skb); - - memset(info, 0, sizeof(*info)); - memset(desc, 0, sizeof(*desc)); -} - -static void txm_desc_free(struct agnx_priv *priv, unsigned int idx) -{ - struct agnx_desc *desc = priv->txm.desc + idx; - struct agnx_info *info = priv->txm.info + idx; - - __tx_desc_free(priv, desc, info); -} - -static void txd_desc_free(struct agnx_priv *priv, unsigned int idx) -{ - struct agnx_desc *desc = priv->txd.desc + idx; - struct agnx_info *info = priv->txd.info + idx; - - __tx_desc_free(priv, desc, info); -} - -int fill_rings(struct agnx_priv *priv) -{ - void __iomem *ctl = priv->ctl; - unsigned int i; - u32 reg; - AGNX_TRACE; - - priv->txd.idx_sent = priv->txm.idx_sent = 0; - priv->rx.idx = priv->txm.idx = priv->txd.idx = 0; - - for (i = 0; i < priv->rx.size; i++) - rx_desc_init(priv, i); - for (i = 0; i < priv->txm.size; i++) { - memset(priv->txm.desc + i, 0, sizeof(struct agnx_desc)); - memset(priv->txm.info + i, 0, sizeof(struct agnx_info)); - } - for (i = 0; i < priv->txd.size; i++) { - memset(priv->txd.desc + i, 0, sizeof(struct agnx_desc)); - memset(priv->txd.info + i, 0, sizeof(struct agnx_info)); - } - - /* FIXME Set the card RX TXM and TXD address */ - agnx_write32(ctl, AGNX_CIR_RXCMSTART, priv->rx.dma); - agnx_write32(ctl, AGNX_CIR_RXCMEND, priv->txm.dma); - - agnx_write32(ctl, AGNX_CIR_TXMSTART, priv->txm.dma); - agnx_write32(ctl, AGNX_CIR_TXMEND, priv->txd.dma); - - agnx_write32(ctl, AGNX_CIR_TXDSTART, priv->txd.dma); - agnx_write32(ctl, AGNX_CIR_TXDEND, priv->txd.dma + - sizeof(struct agnx_desc) * priv->txd.size); - - /* FIXME Relinquish control of rings to card */ - reg = agnx_read32(ctl, AGNX_CIR_BLKCTL); - reg &= ~0x800; - agnx_write32(ctl, AGNX_CIR_BLKCTL, reg); - return 0; -} /* fill_rings */ - -void unfill_rings(struct agnx_priv *priv) -{ - unsigned long flags; - unsigned int i; - AGNX_TRACE; - - spin_lock_irqsave(&priv->lock, flags); - - for (i = 0; i < priv->rx.size; i++) - rx_desc_free(priv, i); - for (i = 0; i < priv->txm.size; i++) - txm_desc_free(priv, i); - for (i = 0; i < priv->txd.size; i++) - txd_desc_free(priv, i); - - spin_unlock_irqrestore(&priv->lock, flags); -} - -/* Extract the bitrate out of a CCK PLCP header. - copy from bcm43xx driver */ -static inline u8 agnx_plcp_get_bitrate_cck(__be32 *phyhdr_11b) -{ - /* FIXME */ - switch (*(u8 *)phyhdr_11b) { - case 0x0A: - return 0; - case 0x14: - return 1; - case 0x37: - return 2; - case 0x6E: - return 3; - } - agnx_bug("Wrong plcp rate"); - return 0; -} - -/* FIXME */ -static inline u8 agnx_plcp_get_bitrate_ofdm(__be32 *phyhdr_11g) -{ - u8 rate = *(u8 *)phyhdr_11g & 0xF; - - printk(PFX "G mode rate is 0x%x\n", rate); - return rate; -} - -/* FIXME */ -static void get_rx_stats(struct agnx_priv *priv, struct agnx_hdr *hdr, - struct ieee80211_rx_status *stat) -{ - void __iomem *ctl = priv->ctl; - u8 *rssi; - u32 noise; - /* FIXME just for test */ - int snr = 40; /* signal-to-noise ratio */ - - memset(stat, 0, sizeof(*stat)); - /* RSSI */ - rssi = (u8 *)&hdr->phy_stats_lo; -/* stat->ssi = (rssi[0] + rssi[1] + rssi[2]) / 3; */ - /* Noise */ - noise = ioread32(ctl + AGNX_GCR_NOISE0); - noise += ioread32(ctl + AGNX_GCR_NOISE1); - noise += ioread32(ctl + AGNX_GCR_NOISE2); - stat->noise = noise / 3; - /* Signal quality */ -/* snr = stat->ssi - stat->noise; */ - if (snr >= 0 && snr < 40) - stat->signal = 5 * snr / 2; - else if (snr >= 40) - stat->signal = 100; - else - stat->signal = 0; - - - if (hdr->_11b0 && !hdr->_11g0) { - stat->rate_idx = agnx_plcp_get_bitrate_cck(&hdr->_11b0); - } else if (!hdr->_11b0 && hdr->_11g0) { - printk(PFX "RX: Found G mode packet\n"); - stat->rate_idx = agnx_plcp_get_bitrate_ofdm(&hdr->_11g0); - } else - agnx_bug("Unknown packets type"); - - - stat->band = IEEE80211_BAND_2GHZ; - stat->freq = agnx_channels[priv->channel - 1].center_freq; -/* stat->antenna = 3; - stat->mactime = be32_to_cpu(hdr->time_stamp); - stat->channel = priv->channel; */ -} - -static inline void combine_hdr_frag(struct ieee80211_hdr *ieeehdr, - struct sk_buff *skb) -{ - u16 fctl; - unsigned int hdrlen; - - fctl = le16_to_cpu(ieeehdr->frame_control); - hdrlen = ieee80211_hdrlen(fctl); - /* FIXME */ - if (hdrlen < (2+2+6)/*minimum hdr*/ || - hdrlen > sizeof(struct ieee80211_mgmt)) { - printk(KERN_ERR PFX "hdr len is %d\n", hdrlen); - agnx_bug("Wrong ieee80211 hdr detected"); - } - skb_push(skb, hdrlen); - memcpy(skb->data, ieeehdr, hdrlen); -} /* combine_hdr_frag */ - -static inline int agnx_packet_check(struct agnx_priv *priv, struct agnx_hdr *agnxhdr, - unsigned packet_len) -{ - if (agnx_get_bits(CRC_FAIL, CRC_FAIL_SHIFT, be32_to_cpu(agnxhdr->reg1)) == 1) { - printk(PFX "RX: CRC check fail\n"); - goto drop; - } - if (packet_len > 2048) { - printk(PFX "RX: Too long packet detected\n"); - goto drop; - } - - /* FIXME Just usable for Promious Mode, for Manage mode exclude FCS */ -/* if (packet_len - sizeof(*agnxhdr) < FCS_LEN) { */ -/* printk(PFX "RX: Too short packet detected\n"); */ -/* goto drop; */ -/* } */ - return 0; -drop: - priv->stats.dot11FCSErrorCount++; - return -1; -} - -void handle_rx_irq(struct agnx_priv *priv) -{ - struct ieee80211_rx_status status; - unsigned int len; -/* AGNX_TRACE; */ - - do { - struct agnx_desc *desc; - u32 frag; - struct agnx_info *info; - struct agnx_hdr *hdr; - struct sk_buff *skb; - unsigned int i = priv->rx.idx % priv->rx.size; - - desc = priv->rx.desc + i; - frag = be32_to_cpu(desc->frag); - if (frag & OWNER) - break; - - info = priv->rx.info + i; - skb = info->skb; - hdr = (struct agnx_hdr *)(skb->data); - - len = (frag & PACKET_LEN) >> PACKET_LEN_SHIFT; - if (agnx_packet_check(priv, hdr, len) == -1) { - rx_desc_reusing(priv, i); - continue; - } - skb_put(skb, len); - - do { - u16 fctl; - fctl = le16_to_cpu(((struct ieee80211_hdr *)hdr->mac_hdr)->frame_control); - if ((fctl & IEEE80211_FCTL_STYPE) != IEEE80211_STYPE_BEACON)/* && !(fctl & IEEE80211_STYPE_BEACON)) */ - dump_ieee80211_hdr((struct ieee80211_hdr *)hdr->mac_hdr, "RX"); - } while (0); - - if (hdr->_11b0 && !hdr->_11g0) { -/* int j; - u16 fctl = le16_to_cpu(((struct ieee80211_hdr *)hdr->mac_hdr) - ->frame_control); - if ( (fctl & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_DATA) { - agnx_print_rx_hdr(hdr); - agnx_print_sta(priv, BSSID_STAID); - for (j = 0; j < 8; j++) - agnx_print_sta_tx_wq(priv, BSSID_STAID, j); - } */ - - get_rx_stats(priv, hdr, &status); - skb_pull(skb, sizeof(*hdr)); - combine_hdr_frag((struct ieee80211_hdr *)hdr->mac_hdr, skb); - } else if (!hdr->_11b0 && hdr->_11g0) { -/* int j; */ - agnx_print_rx_hdr(hdr); - agnx_print_sta(priv, BSSID_STAID); -/* for (j = 0; j < 8; j++) */ - agnx_print_sta_tx_wq(priv, BSSID_STAID, 0); - - print_hex_dump_bytes("agnx: RX_PACKET: ", DUMP_PREFIX_NONE, - skb->data, skb->len + 8); - -/* if (agnx_plcp_get_bitrate_ofdm(&hdr->_11g0) == 0) */ - get_rx_stats(priv, hdr, &status); - skb_pull(skb, sizeof(*hdr)); - combine_hdr_frag((struct ieee80211_hdr *) - ((void *)&hdr->mac_hdr), skb); -/* dump_ieee80211_hdr((struct ieee80211_hdr *)skb->data, "RX G"); */ - } else - agnx_bug("Unknown packets type"); - memcpy(IEEE80211_SKB_RXCB(skb), &status, sizeof(status)); - ieee80211_rx_irqsafe(priv->hw, skb); - rx_desc_reinit(priv, i); - - } while (priv->rx.idx++); -} /* handle_rx_irq */ - -static inline void handle_tx_irq(struct agnx_priv *priv, struct agnx_ring *ring) -{ - struct agnx_desc *desc; - struct agnx_info *info; - unsigned int idx; - - for (idx = ring->idx_sent; idx < ring->idx; idx++) { - unsigned int i = idx % ring->size; - u32 frag; - - desc = ring->desc + i; - info = ring->info + i; - - frag = be32_to_cpu(desc->frag); - if (frag & OWNER) { - if (info->type == HEADER) - break; - else - agnx_bug("TX error"); - } - - pci_unmap_single(priv->pdev, info->mapping, info->dma_len, PCI_DMA_TODEVICE); - - do { -/* int j; */ - size_t len; - len = info->skb->len - sizeof(struct agnx_hdr) + info->hdr_len; -/* if (len == 614) { */ -/* agnx_print_desc(desc); */ - if (info->type == PACKET) { -/* agnx_print_tx_hdr((struct agnx_hdr *)info->skb->data); */ -/* agnx_print_sta_power(priv, LOCAL_STAID); */ -/* agnx_print_sta(priv, LOCAL_STAID); */ -/* for (j = 0; j < 8; j++) */ -/* agnx_print_sta_tx_wq(priv, LOCAL_STAID, 0); */ -/* agnx_print_sta_power(priv, BSSID_STAID); */ -/* agnx_print_sta(priv, BSSID_STAID); */ -/* for (j = 0; j < 8; j++) */ -/* agnx_print_sta_tx_wq(priv, BSSID_STAID, 0); */ - } -/* } */ - } while (0); - - if (info->type == PACKET) { -/* dump_txm_registers(priv); - dump_rxm_registers(priv); - dump_bm_registers(priv); - dump_cir_registers(priv); */ - } - - if (info->type == PACKET) { -/* struct ieee80211_hdr *hdr; */ - struct ieee80211_tx_info *txi = IEEE80211_SKB_CB(info->skb); - - skb_pull(info->skb, sizeof(struct agnx_hdr)); - memcpy(skb_push(info->skb, info->hdr_len), &info->hdr, info->hdr_len); - -/* dump_ieee80211_hdr((struct ieee80211_hdr *)info->skb->data, "TX_HANDLE"); */ -/* print_hex_dump_bytes("agnx: TX_HANDLE: ", DUMP_PREFIX_NONE, */ -/* info->skb->data, info->skb->len); */ - - if (!(txi->flags & IEEE80211_TX_CTL_NO_ACK)) - txi->flags |= IEEE80211_TX_STAT_ACK; - - ieee80211_tx_status_irqsafe(priv->hw, info->skb); - - -/* info->tx_status.queue_number = (ring->size - i) / 2; */ -/* ieee80211_tx_status_irqsafe(priv->hw, info->skb, &(info->tx_status)); */ -/* } else */ -/* dev_kfree_skb_irq(info->skb); */ - } - memset(desc, 0, sizeof(*desc)); - memset(info, 0, sizeof(*info)); - } - - ring->idx_sent = idx; - /* TODO fill the priv->low_level_stats */ - - /* ieee80211_wake_queue(priv->hw, 0); */ -} - -void handle_txm_irq(struct agnx_priv *priv) -{ - handle_tx_irq(priv, &priv->txm); -} - -void handle_txd_irq(struct agnx_priv *priv) -{ - handle_tx_irq(priv, &priv->txd); -} - -void handle_other_irq(struct agnx_priv *priv) -{ -/* void __iomem *ctl = priv->ctl; */ - u32 status = priv->irq_status; - void __iomem *ctl = priv->ctl; - u32 reg; - - if (status & IRQ_TX_BEACON) { - iowrite32(IRQ_TX_BEACON, ctl + AGNX_INT_STAT); - printk(PFX "IRQ: TX Beacon control is 0X%.8X\n", ioread32(ctl + AGNX_TXM_BEACON_CTL)); - printk(PFX "IRQ: TX Beacon rx frame num: %d\n", rx_frame_cnt); - } - if (status & IRQ_TX_RETRY) { - reg = ioread32(ctl + AGNX_TXM_RETRYSTAID); - printk(PFX "IRQ: TX Retry, RETRY STA ID is %x\n", reg); - } - if (status & IRQ_TX_ACTIVITY) - printk(PFX "IRQ: TX Activity\n"); - if (status & IRQ_RX_ACTIVITY) - printk(PFX "IRQ: RX Activity\n"); - if (status & IRQ_RX_X) - printk(PFX "IRQ: RX X\n"); - if (status & IRQ_RX_Y) { - reg = ioread32(ctl + AGNX_INT_MASK); - reg &= ~IRQ_RX_Y; - iowrite32(reg, ctl + AGNX_INT_MASK); - iowrite32(IRQ_RX_Y, ctl + AGNX_INT_STAT); - printk(PFX "IRQ: RX Y\n"); - } - if (status & IRQ_RX_HASHHIT) { - reg = ioread32(ctl + AGNX_INT_MASK); - reg &= ~IRQ_RX_HASHHIT; - iowrite32(reg, ctl + AGNX_INT_MASK); - iowrite32(IRQ_RX_HASHHIT, ctl + AGNX_INT_STAT); - printk(PFX "IRQ: RX Hash Hit\n"); - - } - if (status & IRQ_RX_FRAME) { - reg = ioread32(ctl + AGNX_INT_MASK); - reg &= ~IRQ_RX_FRAME; - iowrite32(reg, ctl + AGNX_INT_MASK); - iowrite32(IRQ_RX_FRAME, ctl + AGNX_INT_STAT); - printk(PFX "IRQ: RX Frame\n"); - rx_frame_cnt++; - } - if (status & IRQ_ERR_INT) { - iowrite32(IRQ_ERR_INT, ctl + AGNX_INT_STAT); -/* agnx_hw_reset(priv); */ - printk(PFX "IRQ: Error Interrupt\n"); - } - if (status & IRQ_TX_QUE_FULL) - printk(PFX "IRQ: TX Workqueue Full\n"); - if (status & IRQ_BANDMAN_ERR) - printk(PFX "IRQ: Bandwidth Management Error\n"); - if (status & IRQ_TX_DISABLE) - printk(PFX "IRQ: TX Disable\n"); - if (status & IRQ_RX_IVASESKEY) - printk(PFX "IRQ: RX Invalid Session Key\n"); - if (status & IRQ_REP_THHIT) - printk(PFX "IRQ: Replay Threshold Hit\n"); - if (status & IRQ_TIMER1) - printk(PFX "IRQ: Timer1\n"); - if (status & IRQ_TIMER_CNT) - printk(PFX "IRQ: Timer Count\n"); - if (status & IRQ_PHY_FASTINT) - printk(PFX "IRQ: Phy Fast Interrupt\n"); - if (status & IRQ_PHY_SLOWINT) - printk(PFX "IRQ: Phy Slow Interrupt\n"); - if (status & IRQ_OTHER) - printk(PFX "IRQ: 0x80000000\n"); -} /* handle_other_irq */ - - -static inline void route_flag_set(struct agnx_hdr *txhdr) -{ -/* u32 reg = 0; */ - - /* FIXME */ -/* reg = (0x7 << ROUTE_COMPRESSION_SHIFT) & ROUTE_COMPRESSION; */ -/* txhdr->reg5 = cpu_to_be32(reg); */ - txhdr->reg5 = (0xa << 0x0) | (0x7 << 0x18); -/* txhdr->reg5 = cpu_to_be32((0xa << 0x0) | (0x7 << 0x18)); */ -/* txhdr->reg5 = cpu_to_be32(0x7 << 0x0); */ -} - -/* Return 0 if no match */ -static inline unsigned int get_power_level(unsigned int rate, unsigned int antennas_num) -{ - unsigned int power_level; - - switch (rate) { - case 10: - case 20: - case 55: - case 60: - case 90: - case 120: - power_level = 22; - break; - - case 180: - power_level = 19; - break; - - case 240: - power_level = 18; - break; - - case 360: - power_level = 16; - break; - - case 480: - power_level = 15; - break; - - case 540: - power_level = 14; - break; - default: - agnx_bug("Error rate setting\n"); - } - - if (power_level && (antennas_num == 2)) - power_level -= 3; - - return power_level; -} - -static inline void fill_agnx_hdr(struct agnx_priv *priv, struct agnx_info *tx_info) -{ - struct agnx_hdr *txhdr = (struct agnx_hdr *)tx_info->skb->data; - size_t len; - u16 fc = le16_to_cpu(*(__le16 *)&tx_info->hdr); - u32 reg; - - memset(txhdr, 0, sizeof(*txhdr)); - -/* reg = agnx_set_bits(STATION_ID, STATION_ID_SHIFT, LOCAL_STAID); */ - reg = agnx_set_bits(STATION_ID, STATION_ID_SHIFT, BSSID_STAID); - reg |= agnx_set_bits(WORKQUEUE_ID, WORKQUEUE_ID_SHIFT, 0); - txhdr->reg4 = cpu_to_be32(reg); - - /* Set the Hardware Sequence Number to 1? */ - reg = agnx_set_bits(SEQUENCE_NUMBER, SEQUENCE_NUMBER_SHIFT, 0); -/* reg = agnx_set_bits(SEQUENCE_NUMBER, SEQUENCE_NUMBER_SHIFT, 1); */ - reg |= agnx_set_bits(MAC_HDR_LEN, MAC_HDR_LEN_SHIFT, tx_info->hdr_len); - txhdr->reg1 = cpu_to_be32(reg); - /* Set the agnx_hdr's MAC header */ - memcpy(txhdr->mac_hdr, &tx_info->hdr, tx_info->hdr_len); - - reg = agnx_set_bits(ACK, ACK_SHIFT, 1); -/* reg = agnx_set_bits(ACK, ACK_SHIFT, 0); */ - reg |= agnx_set_bits(MULTICAST, MULTICAST_SHIFT, 0); -/* reg |= agnx_set_bits(MULTICAST, MULTICAST_SHIFT, 1); */ - reg |= agnx_set_bits(RELAY, RELAY_SHIFT, 0); - reg |= agnx_set_bits(TM, TM_SHIFT, 0); - txhdr->reg0 = cpu_to_be32(reg); - - /* Set the long and short retry limits */ - txhdr->tx.short_retry_limit = tx_info->txi->control.rates[0].count; - txhdr->tx.long_retry_limit = tx_info->txi->control.rates[0].count; - - /* FIXME */ - len = tx_info->skb->len - sizeof(*txhdr) + tx_info->hdr_len + FCS_LEN; - if (fc & IEEE80211_FCTL_PROTECTED) - len += 8; - len = 2398; - reg = agnx_set_bits(FRAG_SIZE, FRAG_SIZE_SHIFT, len); - len = tx_info->skb->len - sizeof(*txhdr); - reg |= agnx_set_bits(PAYLOAD_LEN, PAYLOAD_LEN_SHIFT, len); - txhdr->reg3 = cpu_to_be32(reg); - - route_flag_set(txhdr); -} /* fill_hdr */ - -static void txm_power_set(struct agnx_priv *priv, - struct ieee80211_tx_info *txi) -{ - struct agnx_sta_power power; - u32 reg; - - /* FIXME */ - if (txi->control.rates[0].idx < 0) { - /* For B mode Short Preamble */ - reg = agnx_set_bits(PHY_MODE, PHY_MODE_SHIFT, AGNX_MODE_80211B_SHORT); -/* control->tx_rate = -control->tx_rate; */ - } else - reg = agnx_set_bits(PHY_MODE, PHY_MODE_SHIFT, AGNX_MODE_80211G); -/* reg = agnx_set_bits(PHY_MODE, PHY_MODE_SHIFT, AGNX_MODE_80211B_LONG); */ - reg |= agnx_set_bits(SIGNAL, SIGNAL_SHIFT, 0xB); - reg |= agnx_set_bits(RATE, RATE_SHIFT, 0xB); -/* reg |= agnx_set_bits(POWER_LEVEL, POWER_LEVEL_SHIFT, 15); */ - reg |= agnx_set_bits(POWER_LEVEL, POWER_LEVEL_SHIFT, 20); - /* if rate < 11M set it to 0 */ - reg |= agnx_set_bits(NUM_TRANSMITTERS, NUM_TRANSMITTERS_SHIFT, 1); -/* reg |= agnx_set_bits(EDCF, EDCF_SHIFT, 1); */ -/* reg |= agnx_set_bits(TIFS, TIFS_SHIFT, 1); */ - - power.reg = reg; -/* power.reg = cpu_to_le32(reg); */ - -/* set_sta_power(priv, &power, LOCAL_STAID); */ - set_sta_power(priv, &power, BSSID_STAID); -} - -static inline int tx_packet_check(struct sk_buff *skb) -{ - unsigned int ieee_len = ieee80211_get_hdrlen_from_skb(skb); - if (skb->len > 2048) { - printk(KERN_ERR PFX "length is %d\n", skb->len); - agnx_bug("Too long TX skb"); - return -1; - } - /* FIXME */ - if (skb->len == ieee_len) { - printk(PFX "A strange TX packet\n"); - return -1; - /* tx_faile_irqsafe(); */ - } - return 0; -} - -static int __agnx_tx(struct agnx_priv *priv, struct sk_buff *skb, - struct agnx_ring *ring) -{ - struct agnx_desc *hdr_desc, *frag_desc; - struct agnx_info *hdr_info, *frag_info; - struct ieee80211_tx_info *txi = IEEE80211_SKB_CB(skb); - unsigned long flags; - unsigned int i; - - spin_lock_irqsave(&priv->lock, flags); - - /* The RX interrupt need be Disable until this TX packet - is handled in the next tx interrupt */ - disable_rx_interrupt(priv); - - i = ring->idx; - ring->idx += 2; -/* if (priv->txm_idx - priv->txm_idx_sent == AGNX_TXM_RING_SIZE - 2) */ -/* ieee80211_stop_queue(priv->hw, 0); */ - - /* Set agnx header's info and desc */ - i %= ring->size; - hdr_desc = ring->desc + i; - hdr_info = ring->info + i; - hdr_info->hdr_len = ieee80211_get_hdrlen_from_skb(skb); - memcpy(&hdr_info->hdr, skb->data, hdr_info->hdr_len); - - /* Add the agnx header to the front of the SKB */ - skb_push(skb, sizeof(struct agnx_hdr) - hdr_info->hdr_len); - - hdr_info->txi = txi; - hdr_info->dma_len = sizeof(struct agnx_hdr); - hdr_info->skb = skb; - hdr_info->type = HEADER; - fill_agnx_hdr(priv, hdr_info); - hdr_info->mapping = pci_map_single(priv->pdev, skb->data, - hdr_info->dma_len, PCI_DMA_TODEVICE); - do { - u32 frag = 0; - frag |= agnx_set_bits(FIRST_FRAG, FIRST_FRAG_SHIFT, 1); - frag |= agnx_set_bits(LAST_FRAG, LAST_FRAG_SHIFT, 0); - frag |= agnx_set_bits(PACKET_LEN, PACKET_LEN_SHIFT, skb->len); - frag |= agnx_set_bits(FIRST_FRAG_LEN, FIRST_FRAG_LEN_SHIFT, 1); - frag |= agnx_set_bits(OWNER, OWNER_SHIFT, 1); - hdr_desc->frag = cpu_to_be32(frag); - } while (0); - hdr_desc->dma_addr = cpu_to_be32(hdr_info->mapping); - - - /* Set Frag's info and desc */ - i = (i + 1) % ring->size; - frag_desc = ring->desc + i; - frag_info = ring->info + i; - memcpy(frag_info, hdr_info, sizeof(struct agnx_info)); - frag_info->type = PACKET; - frag_info->dma_len = skb->len - hdr_info->dma_len; - frag_info->mapping = pci_map_single(priv->pdev, skb->data + hdr_info->dma_len, - frag_info->dma_len, PCI_DMA_TODEVICE); - do { - u32 frag = 0; - frag |= agnx_set_bits(FIRST_FRAG, FIRST_FRAG_SHIFT, 0); - frag |= agnx_set_bits(LAST_FRAG, LAST_FRAG_SHIFT, 1); - frag |= agnx_set_bits(PACKET_LEN, PACKET_LEN_SHIFT, skb->len); - frag |= agnx_set_bits(SUB_FRAG_LEN, SUB_FRAG_LEN_SHIFT, frag_info->dma_len); - frag_desc->frag = cpu_to_be32(frag); - } while (0); - frag_desc->dma_addr = cpu_to_be32(frag_info->mapping); - - txm_power_set(priv, txi); - -/* do { */ -/* int j; */ -/* size_t len; */ -/* len = skb->len - hdr_info->dma_len + hdr_info->hdr_len; */ -/* if (len == 614) { */ -/* agnx_print_desc(hdr_desc); */ -/* agnx_print_desc(frag_desc); */ -/* agnx_print_tx_hdr((struct agnx_hdr *)skb->data); */ -/* agnx_print_sta_power(priv, LOCAL_STAID); */ -/* agnx_print_sta(priv, LOCAL_STAID); */ -/* for (j = 0; j < 8; j++) */ -/* agnx_print_sta_tx_wq(priv, LOCAL_STAID, j); */ -/* agnx_print_sta_power(priv, BSSID_STAID); */ -/* agnx_print_sta(priv, BSSID_STAID); */ -/* for (j = 0; j < 8; j++) */ -/* agnx_print_sta_tx_wq(priv, BSSID_STAID, j); */ -/* } */ -/* } while (0); */ - - spin_unlock_irqrestore(&priv->lock, flags); - - /* FIXME ugly code */ - /* Trigger TXM */ - do { - u32 reg; - reg = (ioread32(priv->ctl + AGNX_CIR_TXMCTL)); - reg |= 0x8; - iowrite32((reg), priv->ctl + AGNX_CIR_TXMCTL); - } while (0); - - /* Trigger TXD */ - do { - u32 reg; - reg = (ioread32(priv->ctl + AGNX_CIR_TXDCTL)); - reg |= 0x8; - iowrite32((reg), priv->ctl + AGNX_CIR_TXDCTL); - } while (0); - - return 0; -} - -int _agnx_tx(struct agnx_priv *priv, struct sk_buff *skb) -{ - u16 fctl; - - if (tx_packet_check(skb)) - return 0; - -/* print_hex_dump_bytes("agnx: TX_PACKET: ", DUMP_PREFIX_NONE, */ -/* skb->data, skb->len); */ - - fctl = le16_to_cpu(*((__le16 *)skb->data)); - - if ((fctl & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_DATA) - return __agnx_tx(priv, skb, &priv->txd); - else - return __agnx_tx(priv, skb, &priv->txm); -} diff --git a/drivers/staging/agnx/xmit.h b/drivers/staging/agnx/xmit.h deleted file mode 100644 index 93ac4157e697..000000000000 --- a/drivers/staging/agnx/xmit.h +++ /dev/null @@ -1,250 +0,0 @@ -#ifndef AGNX_XMIT_H_ -#define AGNX_XMIT_H_ - -#include <net/mac80211.h> - -struct agnx_priv; - -static inline u32 agnx_set_bits(u32 mask, u8 shift, u32 value) -{ - return (value << shift) & mask; -} - -static inline u32 agnx_get_bits(u32 mask, u8 shift, u32 value) -{ - return (value & mask) >> shift; -} - - -struct agnx_rx { - __be16 rx_packet_duration; /* RX Packet Duration */ - __be16 replay_cnt; /* Replay Count */ -} __attribute__((__packed__)); - - -struct agnx_tx { - u8 long_retry_limit; /* Long Retry Limit */ - u8 short_retry_limit; /* Short Retry Limit */ - u8 long_retry_cnt; /* Long Retry Count */ - u8 short_retry_cnt; /* Short Retry Count */ -} __attribute__((__packed__)); - - -/* Copy from bcm43xx */ -#define P4D_BYT3S(magic, nr_bytes) u8 __p4dding##magic[nr_bytes] -#define P4D_BYTES(line, nr_bytes) P4D_BYT3S(line, nr_bytes) -#define PAD_BYTES(nr_bytes) P4D_BYTES(__LINE__, nr_bytes) - -#define P4D_BIT3S(magic, nr_bits) __be32 __padding##magic:nr_bits -#define P4D_BITS(line, nr_bits) P4D_BIT3S(line, nr_bits) -#define PAD_BITS(nr_bits) P4D_BITS(__LINE__, nr_bits) - - -struct agnx_hdr { - __be32 reg0; -#define RTS 0x80000000 /* RTS */ -#define RTS_SHIFT 31 -#define MULTICAST 0x40000000 /* multicast */ -#define MULTICAST_SHIFT 30 -#define ACK 0x30000000 /* ACK */ -#define ACK_SHIFT 28 -#define TM 0x08000000 /* TM */ -#define TM_SHIFT 27 -#define RELAY 0x04000000 /* Relay */ -#define RELAY_SHIFT 26 -/* PAD_BITS(4); */ -#define REVISED_FCS 0x00380000 /* revised FCS */ -#define REVISED_FCS_SHIFT 19 -#define NEXT_BUFFER_ADDR 0x0007FFFF /* Next Buffer Address */ -#define NEXT_BUFFER_ADDR_SHIFT 0 - - __be32 reg1; -#define MAC_HDR_LEN 0xFC000000 /* MAC Header Length */ -#define MAC_HDR_LEN_SHIFT 26 -#define DURATION_OVERIDE 0x02000000 /* Duration Override */ -#define DURATION_OVERIDE_SHIFT 25 -#define PHY_HDR_OVERIDE 0x01000000 /* PHY Header Override */ -#define PHY_HDR_OVERIDE_SHIFT 24 -#define CRC_FAIL 0x00800000 /* CRC fail */ -#define CRC_FAIL_SHIFT 23 -/* PAD_BITS(1); */ -#define SEQUENCE_NUMBER 0x00200000 /* Sequence Number */ -#define SEQUENCE_NUMBER_SHIFT 21 -/* PAD_BITS(2); */ -#define BUFF_HEAD_ADDR 0x0007FFFF /* Buffer Head Address */ -#define BUFF_HEAD_ADDR_SHIFT 0 - - __be32 reg2; -#define PDU_COUNT 0xFC000000 /* PDU Count */ -#define PDU_COUNT_SHIFT 26 -/* PAD_BITS(3); */ -#define WEP_KEY 0x00600000 /* WEP Key # */ -#define WEP_KEY_SHIFT 21 -#define USES_WEP_KEY 0x00100000 /* Uses WEP Key */ -#define USES_WEP_KEY_SHIFT 20 -#define KEEP_ALIVE 0x00080000 /* Keep alive */ -#define KEEP_ALIVE_SHIFT 19 -#define BUFF_TAIL_ADDR 0x0007FFFF /* Buffer Tail Address */ -#define BUFF_TAIL_ADDR_SHIFT 0 - - __be32 reg3; -#define CTS_11G 0x80000000 /* CTS in 11g */ -#define CTS_11G_SHIFT 31 -#define RTS_11G 0x40000000 /* RTS in 11g */ -#define RTS_11G_SHIFT 30 -/* PAD_BITS(2); */ -#define FRAG_SIZE 0x0FFF0000 /* fragment size */ -#define FRAG_SIZE_SHIFT 16 -#define PAYLOAD_LEN 0x0000FFF0 /* payload length */ -#define PAYLOAD_LEN_SHIFT 4 -#define FRAG_NUM 0x0000000F /* number of frags */ -#define FRAG_NUM_SHIFT 0 - - __be32 reg4; -/* PAD_BITS(4); */ -#define RELAY_STAID 0x0FFF0000 /* relayStald */ -#define RELAY_STAID_SHIFT 16 -#define STATION_ID 0x0000FFF0 /* Station ID */ -#define STATION_ID_SHIFT 4 -#define WORKQUEUE_ID 0x0000000F /* Workqueue ID */ -#define WORKQUEUE_ID_SHIFT 0 - - /* FIXME this register maybe is LE? */ - __be32 reg5; -/* PAD_BITS(4); */ -#define ROUTE_HOST 0x0F000000 -#define ROUTE_HOST_SHIFT 24 -#define ROUTE_CARD_CPU 0x00F00000 -#define ROUTE_CARD_CPU_SHIFT 20 -#define ROUTE_ENCRYPTION 0x000F0000 -#define ROUTE_ENCRYPTION_SHIFT 16 -#define ROUTE_TX 0x0000F000 -#define ROUTE_TX_SHIFT 12 -#define ROUTE_RX1 0x00000F00 -#define ROUTE_RX1_SHIFT 8 -#define ROUTE_RX2 0x000000F0 -#define ROUTE_RX2_SHIFT 4 -#define ROUTE_COMPRESSION 0x0000000F -#define ROUTE_COMPRESSION_SHIFT 0 - - __be32 _11g0; /* 11g */ - __be32 _11g1; /* 11g */ - __be32 _11b0; /* 11b */ - __be32 _11b1; /* 11b */ - u8 mac_hdr[32]; /* MAC header */ - - __be16 rts_duration; /* RTS duration */ - __be16 last_duration; /* Last duration */ - __be16 sec_last_duration; /* Second to Last duration */ - __be16 other_duration; /* Other duration */ - __be16 tx_last_duration; /* TX Last duration */ - __be16 tx_other_duration; /* TX Other Duration */ - __be16 last_11g_len; /* Length of last 11g */ - __be16 other_11g_len; /* Lenght of other 11g */ - - __be16 last_11b_len; /* Length of last 11b */ - __be16 other_11b_len; /* Lenght of other 11b */ - - - __be16 reg6; -#define MBF 0xF000 /* mbf */ -#define MBF_SHIFT 12 -#define RSVD4 0x0FFF /* rsvd4 */ -#define RSVD4_SHIFT 0 - - __be16 rx_frag_stat; /* RX fragmentation status */ - - __be32 time_stamp; /* TimeStamp */ - __be32 phy_stats_hi; /* PHY stats hi */ - __be32 phy_stats_lo; /* PHY stats lo */ - __be32 mic_key0; /* MIC key 0 */ - __be32 mic_key1; /* MIC key 1 */ - - union { /* RX/TX Union */ - struct agnx_rx rx; - struct agnx_tx tx; - }; - - u8 rx_channel; /* Recieve Channel */ - PAD_BYTES(3); - - u8 reserved[4]; -} __attribute__((__packed__)); - - -struct agnx_desc { -#define PACKET_LEN 0xFFF00000 -#define PACKET_LEN_SHIFT 20 -/* ------------------------------------------------ */ -#define FIRST_PACKET_MASK 0x00080000 -#define FIRST_PACKET_MASK_SHIFT 19 -#define FIRST_RESERV2 0x00040000 -#define FIRST_RESERV2_SHIFT 18 -#define FIRST_TKIP_ERROR 0x00020000 -#define FIRST_TKIP_ERROR_SHIFT 17 -#define FIRST_TKIP_PACKET 0x00010000 -#define FIRST_TKIP_PACKET_SHIFT 16 -#define FIRST_RESERV1 0x0000F000 -#define FIRST_RESERV1_SHIFT 12 -#define FIRST_FRAG_LEN 0x00000FF8 -#define FIRST_FRAG_LEN_SHIFT 3 -/* ------------------------------------------------ */ -#define SUB_RESERV2 0x000c0000 -#define SUB_RESERV2_SHIFT 18 -#define SUB_TKIP_ERROR 0x00020000 -#define SUB_TKIP_ERROR_SHIFT 17 -#define SUB_TKIP_PACKET 0x00010000 -#define SUB_TKIP_PACKET_SHIFT 16 -#define SUB_RESERV1 0x00008000 -#define SUB_RESERV1_SHIFT 15 -#define SUB_FRAG_LEN 0x00007FF8 -#define SUB_FRAG_LEN_SHIFT 3 -/* ------------------------------------------------ */ -#define FIRST_FRAG 0x00000004 -#define FIRST_FRAG_SHIFT 2 -#define LAST_FRAG 0x00000002 -#define LAST_FRAG_SHIFT 1 -#define OWNER 0x00000001 -#define OWNER_SHIFT 0 - __be32 frag; - __be32 dma_addr; -} __attribute__((__packed__)); - -enum {HEADER, PACKET}; - -struct agnx_info { - struct sk_buff *skb; - dma_addr_t mapping; - u32 dma_len; /* dma buffer len */ - /* Below fields only usful for tx */ - u32 hdr_len; /* ieee80211 header length */ - unsigned int type; - struct ieee80211_tx_info *txi; - struct ieee80211_hdr hdr; -}; - - -struct agnx_ring { - struct agnx_desc *desc; - dma_addr_t dma; - struct agnx_info *info; - /* Will lead to overflow when sent packet number enough? */ - unsigned int idx; - unsigned int idx_sent; /* only usful for txd and txm */ - unsigned int size; -}; - -#define AGNX_RX_RING_SIZE 128 -#define AGNX_TXD_RING_SIZE 256 -#define AGNX_TXM_RING_SIZE 128 - -void disable_rx_interrupt(struct agnx_priv *priv); -void enable_rx_interrupt(struct agnx_priv *priv); -int fill_rings(struct agnx_priv *priv); -void unfill_rings(struct agnx_priv *priv); -void handle_rx_irq(struct agnx_priv *priv); -void handle_txd_irq(struct agnx_priv *priv); -void handle_txm_irq(struct agnx_priv *priv); -void handle_other_irq(struct agnx_priv *priv); -int _agnx_tx(struct agnx_priv *priv, struct sk_buff *skb); -#endif /* AGNX_XMIT_H_ */ diff --git a/drivers/staging/android/Kconfig b/drivers/staging/android/Kconfig index 247194992374..eb675635ae60 100644 --- a/drivers/staging/android/Kconfig +++ b/drivers/staging/android/Kconfig @@ -2,6 +2,7 @@ menu "Android" config ANDROID bool "Android Drivers" + depends on BROKEN default N ---help--- Enable support for various drivers needed on the Android platform diff --git a/drivers/staging/b3dfg/b3dfg.c b/drivers/staging/b3dfg/b3dfg.c index 94c5d27d24d7..cda26bb493b3 100644 --- a/drivers/staging/b3dfg/b3dfg.c +++ b/drivers/staging/b3dfg/b3dfg.c @@ -36,6 +36,7 @@ #include <linux/wait.h> #include <linux/mm.h> #include <linux/uaccess.h> +#include <linux/sched.h> static unsigned int b3dfg_nbuf = 2; diff --git a/drivers/staging/comedi/Kconfig b/drivers/staging/comedi/Kconfig index af723cb9d08f..d63c889ce557 100644 --- a/drivers/staging/comedi/Kconfig +++ b/drivers/staging/comedi/Kconfig @@ -1,7 +1,7 @@ config COMEDI tristate "Data acquisition support (comedi)" default N - depends on m + depends on m && (PCI || PCMCIA || PCCARD || USB) ---help--- Enable support a wide range of data acquisition devices for Linux. diff --git a/drivers/staging/comedi/comedi_fops.c b/drivers/staging/comedi/comedi_fops.c index f54bb9b3ee37..aaad76e0a76a 100644 --- a/drivers/staging/comedi/comedi_fops.c +++ b/drivers/staging/comedi/comedi_fops.c @@ -2337,7 +2337,7 @@ static int resize_async_buffer(struct comedi_device *dev, } DPRINTK("comedi%i subd %d buffer resized to %i bytes\n", - dev->minor, s - dev->subdevices, async->prealloc_bufsz); + dev->minor, (int)(s - dev->subdevices), async->prealloc_bufsz); return 0; } diff --git a/drivers/staging/comedi/drivers/cb_das16_cs.c b/drivers/staging/comedi/drivers/cb_das16_cs.c index 12d12b43a6f1..80c0df8656f3 100644 --- a/drivers/staging/comedi/drivers/cb_das16_cs.c +++ b/drivers/staging/comedi/drivers/cb_das16_cs.c @@ -744,7 +744,7 @@ static int das16cs_pcmcia_attach(struct pcmcia_device *link) /* Initialize the pcmcia_device structure */ /* Interrupt setup */ - link->irq.Attributes = IRQ_TYPE_EXCLUSIVE; + link->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING; link->irq.IRQInfo1 = IRQ_LEVEL_ID; link->irq.Handler = NULL; diff --git a/drivers/staging/comedi/drivers/cb_pcidio.c b/drivers/staging/comedi/drivers/cb_pcidio.c index 4d10bc31d461..09e6e3bdfb3e 100644 --- a/drivers/staging/comedi/drivers/cb_pcidio.c +++ b/drivers/staging/comedi/drivers/cb_pcidio.c @@ -53,7 +53,8 @@ Passing a zero for an option is the same as leaving it unspecified. * Some drivers use arrays such as this, other do not. */ struct pcidio_board { - const char *name; /* anme of the board */ + const char *name; /* name of the board */ + int dev_id; int n_8255; /* number of 8255 chips on board */ /* indices of base address regions */ @@ -64,18 +65,21 @@ struct pcidio_board { static const struct pcidio_board pcidio_boards[] = { { .name = "pci-dio24", + .dev_id = 0x0028, .n_8255 = 1, .pcicontroler_badrindex = 1, .dioregs_badrindex = 2, }, { .name = "pci-dio24h", + .dev_id = 0x0014, .n_8255 = 1, .pcicontroler_badrindex = 1, .dioregs_badrindex = 2, }, { .name = "pci-dio48h", + .dev_id = 0x000b, .n_8255 = 2, .pcicontroler_badrindex = 0, .dioregs_badrindex = 1, @@ -206,7 +210,7 @@ static int pcidio_attach(struct comedi_device *dev, struct comedi_devconfig *it) continue; /* loop through cards supported by this driver */ for (index = 0; index < ARRAY_SIZE(pcidio_boards); index++) { - if (pcidio_pci_table[index].device != pcidev->device) + if (pcidio_boards[index].dev_id != pcidev->device) continue; /* was a particular bus/slot requested? */ diff --git a/drivers/staging/comedi/drivers/jr3_pci.c b/drivers/staging/comedi/drivers/jr3_pci.c index 14bf29bf5781..0d2c2eb23b23 100644 --- a/drivers/staging/comedi/drivers/jr3_pci.c +++ b/drivers/staging/comedi/drivers/jr3_pci.c @@ -515,6 +515,7 @@ static struct poll_delay_t jr3_pci_poll_subdevice(struct comedi_subdevice *s) { struct poll_delay_t result = poll_delay_min_max(1000, 2000); struct jr3_pci_subdev_private *p = s->private; + int i; if (p) { volatile struct jr3_channel *channel = p->channel; @@ -570,18 +571,11 @@ static struct poll_delay_t jr3_pci_poll_subdevice(struct comedi_subdevice *s) p->serial_no); /* Transformation all zeros */ - transf.link[0].link_type = - (enum link_types)0; - transf.link[0].link_amount = 0; - transf.link[1].link_type = - (enum link_types)0; - transf.link[1].link_amount = 0; - transf.link[2].link_type = - (enum link_types)0; - transf.link[2].link_amount = 0; - transf.link[3].link_type = - (enum link_types)0; - transf.link[3].link_amount = 0; + for (i = 0; i < ARRAY_SIZE(transf.link); i++) { + transf.link[i].link_type = + (enum link_types)0; + transf.link[i].link_amount = 0; + } set_transforms(channel, transf, 0); use_transform(channel, 0); diff --git a/drivers/staging/comedi/drivers/me_daq.c b/drivers/staging/comedi/drivers/me_daq.c index 2cda7ad1d32f..80e192d2e77e 100644 --- a/drivers/staging/comedi/drivers/me_daq.c +++ b/drivers/staging/comedi/drivers/me_daq.c @@ -51,6 +51,7 @@ from http://www.comedi.org */ #include <linux/interrupt.h> +#include <linux/sched.h> #include "../comedidev.h" #include "comedi_pci.h" diff --git a/drivers/staging/comedi/drivers/ni_65xx.c b/drivers/staging/comedi/drivers/ni_65xx.c index 6b118c15b49e..bbf75eb6d7f2 100644 --- a/drivers/staging/comedi/drivers/ni_65xx.c +++ b/drivers/staging/comedi/drivers/ni_65xx.c @@ -418,15 +418,15 @@ static int ni_65xx_dio_insn_bits(struct comedi_device *dev, return -EINVAL; base_bitfield_channel = CR_CHAN(insn->chanspec); for (j = 0; j < max_ports_per_bitfield; ++j) { + const unsigned port_offset = ni_65xx_port_by_channel(base_bitfield_channel) + j; const unsigned port = - sprivate(s)->base_port + - ni_65xx_port_by_channel(base_bitfield_channel) + j; + sprivate(s)->base_port + port_offset; unsigned base_port_channel; unsigned port_mask, port_data, port_read_bits; int bitshift; if (port >= ni_65xx_total_num_ports(board(dev))) break; - base_port_channel = port * ni_65xx_channels_per_port; + base_port_channel = port_offset * ni_65xx_channels_per_port; port_mask = data[0]; port_data = data[1]; bitshift = base_port_channel - base_bitfield_channel; @@ -457,6 +457,12 @@ static int ni_65xx_dio_insn_bits(struct comedi_device *dev, port_read_bits = readb(private(dev)->mite->daq_io_addr + Port_Data(port)); /* printk("read 0x%x from port %i\n", port_read_bits, port); */ + if (s->type == COMEDI_SUBD_DO && board(dev)->invert_outputs) { + /* Outputs inverted, so invert value read back from + * DO subdevice. (Does not apply to boards with DIO + * subdevice.) */ + port_read_bits ^= 0xFF; + } if (bitshift > 0) { port_read_bits <<= bitshift; } else { diff --git a/drivers/staging/comedi/drivers/ni_daq_700.c b/drivers/staging/comedi/drivers/ni_daq_700.c index 6a7797604c97..ec31a3970664 100644 --- a/drivers/staging/comedi/drivers/ni_daq_700.c +++ b/drivers/staging/comedi/drivers/ni_daq_700.c @@ -520,7 +520,7 @@ static int dio700_cs_attach(struct pcmcia_device *link) link->priv = local; /* Interrupt setup */ - link->irq.Attributes = IRQ_TYPE_EXCLUSIVE; + link->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING; link->irq.IRQInfo1 = IRQ_LEVEL_ID; link->irq.Handler = NULL; diff --git a/drivers/staging/comedi/drivers/ni_daq_dio24.c b/drivers/staging/comedi/drivers/ni_daq_dio24.c index b06e81c526e8..0700a8bddd1e 100644 --- a/drivers/staging/comedi/drivers/ni_daq_dio24.c +++ b/drivers/staging/comedi/drivers/ni_daq_dio24.c @@ -271,7 +271,7 @@ static int dio24_cs_attach(struct pcmcia_device *link) link->priv = local; /* Interrupt setup */ - link->irq.Attributes = IRQ_TYPE_EXCLUSIVE; + link->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING; link->irq.IRQInfo1 = IRQ_LEVEL_ID; link->irq.Handler = NULL; diff --git a/drivers/staging/comedi/drivers/ni_labpc_cs.c b/drivers/staging/comedi/drivers/ni_labpc_cs.c index 57aecfa883c7..a3053b8da1c6 100644 --- a/drivers/staging/comedi/drivers/ni_labpc_cs.c +++ b/drivers/staging/comedi/drivers/ni_labpc_cs.c @@ -246,7 +246,7 @@ static int labpc_cs_attach(struct pcmcia_device *link) link->priv = local; /* Interrupt setup */ - link->irq.Attributes = IRQ_TYPE_EXCLUSIVE | IRQ_FORCED_PULSE; + link->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING | IRQ_FORCED_PULSE; link->irq.IRQInfo1 = IRQ_INFO2_VALID | IRQ_PULSE_ID; link->irq.Handler = NULL; diff --git a/drivers/staging/comedi/drivers/ni_mio_common.c b/drivers/staging/comedi/drivers/ni_mio_common.c index e3ffb067ead1..753ee0512342 100644 --- a/drivers/staging/comedi/drivers/ni_mio_common.c +++ b/drivers/staging/comedi/drivers/ni_mio_common.c @@ -62,6 +62,7 @@ /* #define DEBUG_STATUS_B */ #include <linux/interrupt.h> +#include <linux/sched.h> #include "8255.h" #include "mite.h" #include "comedi_fc.h" diff --git a/drivers/staging/comedi/drivers/ni_mio_cs.c b/drivers/staging/comedi/drivers/ni_mio_cs.c index b7322963cf78..9aef87fc81dc 100644 --- a/drivers/staging/comedi/drivers/ni_mio_cs.c +++ b/drivers/staging/comedi/drivers/ni_mio_cs.c @@ -273,7 +273,7 @@ static int cs_attach(struct pcmcia_device *link) { link->io.Attributes1 = IO_DATA_PATH_WIDTH_16; link->io.NumPorts1 = 16; - link->irq.Attributes = IRQ_TYPE_EXCLUSIVE; + link->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING; link->irq.IRQInfo1 = IRQ_LEVEL_ID; link->conf.Attributes = CONF_ENABLE_IRQ; link->conf.IntType = INT_MEMORY_AND_IO; diff --git a/drivers/staging/comedi/drivers/ni_pcidio.c b/drivers/staging/comedi/drivers/ni_pcidio.c index 52b2eca9e73d..d544698f2414 100644 --- a/drivers/staging/comedi/drivers/ni_pcidio.c +++ b/drivers/staging/comedi/drivers/ni_pcidio.c @@ -70,6 +70,7 @@ comedi_nonfree_firmware tarball available from http://www.comedi.org /* #define DEBUG_FLAGS */ #include <linux/interrupt.h> +#include <linux/sched.h> #include "../comedidev.h" #include "mite.h" diff --git a/drivers/staging/comedi/drivers/ni_pcimio.c b/drivers/staging/comedi/drivers/ni_pcimio.c index 19d87553d906..24c8b8ed5b4c 100644 --- a/drivers/staging/comedi/drivers/ni_pcimio.c +++ b/drivers/staging/comedi/drivers/ni_pcimio.c @@ -29,7 +29,7 @@ Devices: [National Instruments] PCI-MIO-16XE-50 (ni_pcimio), PCI-MIO-16XE-10, PXI-6030E, PCI-MIO-16E-1, PCI-MIO-16E-4, PCI-6014, PCI-6040E, PXI-6040E, PCI-6030E, PCI-6031E, PCI-6032E, PCI-6033E, PCI-6071E, PCI-6023E, PCI-6024E, PCI-6025E, PXI-6025E, PCI-6034E, PCI-6035E, PCI-6052E, - PCI-6110, PCI-6111, PCI-6220, PCI-6221, PCI-6224, PXI-6224, PCI-6225, + PCI-6110, PCI-6111, PCI-6220, PCI-6221, PCI-6224, PXI-6224, PCI-6225, PXI-6225, PCI-6229, PCI-6250, PCI-6251, PCIe-6251, PCI-6254, PCI-6259, PCIe-6259, PCI-6280, PCI-6281, PXI-6281, PCI-6284, PCI-6289, PCI-6711, PXI-6711, PCI-6713, PXI-6713, @@ -179,6 +179,7 @@ static DEFINE_PCI_DEVICE_TABLE(ni_pci_table) = { PCI_VENDOR_ID_NATINST, 0x70f2, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, { PCI_VENDOR_ID_NATINST, 0x710d, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, { PCI_VENDOR_ID_NATINST, 0x716c, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, { + PCI_VENDOR_ID_NATINST, 0x716d, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, { PCI_VENDOR_ID_NATINST, 0x717f, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, { PCI_VENDOR_ID_NATINST, 0x71bc, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, { PCI_VENDOR_ID_NATINST, 0x717d, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, { @@ -953,6 +954,25 @@ static const struct ni_board_struct ni_boards[] = { .has_8255 = 0, }, { + .device_id = 0x716d, + .name = "pxi-6225", + .n_adchan = 80, + .adbits = 16, + .ai_fifo_depth = 4095, + .gainlkup = ai_gain_622x, + .ai_speed = 4000, + .n_aochan = 2, + .aobits = 16, + .ao_fifo_depth = 8191, + .ao_range_table = &range_ni_M_622x_ao, + .reg_type = ni_reg_622x, + .ao_unipolar = 0, + .ao_speed = 1200, + .num_p0_dio_channels = 32, + .caldac = {caldac_none}, + .has_8255 = 0, + }, + { .device_id = 0x70aa, .name = "pci-6229", .n_adchan = 32, diff --git a/drivers/staging/comedi/drivers/quatech_daqp_cs.c b/drivers/staging/comedi/drivers/quatech_daqp_cs.c index f63bdc35cffd..344b82353e08 100644 --- a/drivers/staging/comedi/drivers/quatech_daqp_cs.c +++ b/drivers/staging/comedi/drivers/quatech_daqp_cs.c @@ -1079,7 +1079,7 @@ static int daqp_cs_attach(struct pcmcia_device *link) link->priv = local; /* Interrupt setup */ - link->irq.Attributes = IRQ_TYPE_EXCLUSIVE | IRQ_HANDLE_PRESENT; + link->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING | IRQ_HANDLE_PRESENT; link->irq.IRQInfo1 = IRQ_LEVEL_ID; link->irq.Handler = daqp_interrupt; link->irq.Instance = local; diff --git a/drivers/staging/comedi/drivers/s526.c b/drivers/staging/comedi/drivers/s526.c index b89e1ec267c5..07c21e686f27 100644 --- a/drivers/staging/comedi/drivers/s526.c +++ b/drivers/staging/comedi/drivers/s526.c @@ -43,6 +43,7 @@ comedi_config /dev/comedi0 s526 0x2C0,0x3 #include "../comedidev.h" #include <linux/ioport.h> +#include <asm/byteorder.h> #define S526_SIZE 64 @@ -113,6 +114,7 @@ static const int s526_ports[] = { }; struct counter_mode_register_t { +#if defined (__LITTLE_ENDIAN_BITFIELD) unsigned short coutSource:1; unsigned short coutPolarity:1; unsigned short autoLoadResetRcap:3; @@ -124,12 +126,27 @@ struct counter_mode_register_t { unsigned short outputRegLatchCtrl:1; unsigned short preloadRegSel:1; unsigned short reserved:1; + #elif defined(__BIG_ENDIAN_BITFIELD) + unsigned short reserved:1; + unsigned short preloadRegSel:1; + unsigned short outputRegLatchCtrl:1; + unsigned short countDirCtrl:1; + unsigned short countDir:1; + unsigned short clockSource:2; + unsigned short ctEnableCtrl:2; + unsigned short hwCtEnableSource:2; + unsigned short autoLoadResetRcap:3; + unsigned short coutPolarity:1; + unsigned short coutSource:1; +#else +#error Unknown bit field order +#endif }; -union { +union cmReg { struct counter_mode_register_t reg; unsigned short value; -} cmReg; +}; #define MAX_GPCT_CONFIG_DATA 6 @@ -285,6 +302,7 @@ static int s526_attach(struct comedi_device *dev, struct comedi_devconfig *it) int i, n; /* short value; */ /* int subdev_channel = 0; */ + union cmReg cmReg; printk("comedi%d: s526: ", dev->minor); @@ -375,7 +393,7 @@ static int s526_attach(struct comedi_device *dev, struct comedi_devconfig *it) if (thisboard->have_dio) { s->type = COMEDI_SUBD_DIO; s->subdev_flags = SDF_READABLE | SDF_WRITABLE; - s->n_chan = 2; + s->n_chan = 8; s->maxdata = 1; s->range_table = &range_digital; s->insn_bits = s526_dio_insn_bits; @@ -435,11 +453,11 @@ static int s526_attach(struct comedi_device *dev, struct comedi_devconfig *it) udelay(1000); printk("Read back mode reg=0x%04x\n", inw(ADDR_CHAN_REG(REG_C0M, n))); - /* Load the pre-laod register high word */ + /* Load the pre-load register high word */ /* value = (short) (0x55); */ /* outw(value, ADDR_CHAN_REG(REG_C0H, n)); */ - /* Load the pre-laod register low word */ + /* Load the pre-load register low word */ /* value = (short)(0xaa55); */ /* outw(value, ADDR_CHAN_REG(REG_C0L, n)); */ @@ -516,6 +534,7 @@ static int s526_gpct_insn_config(struct comedi_device *dev, int subdev_channel = CR_CHAN(insn->chanspec); /* Unpack chanspec */ int i; short value; + union cmReg cmReg; /* printk("s526: GPCT_INSN_CONFIG: Configuring Channel %d\n", subdev_channel); */ @@ -568,19 +587,8 @@ static int s526_gpct_insn_config(struct comedi_device *dev, #if 1 /* Set Counter Mode Register */ - cmReg.reg.coutSource = 0; /* out RCAP */ - cmReg.reg.coutPolarity = 0; /* Polarity inverted */ - cmReg.reg.autoLoadResetRcap = 0; /* Auto load disabled */ - cmReg.reg.hwCtEnableSource = 2; /* NOT RCAP */ - cmReg.reg.ctEnableCtrl = 1; /* 1: Software, >1 : Hardware */ - cmReg.reg.clockSource = 3; /* x4 */ - cmReg.reg.countDir = 0; /* up */ - cmReg.reg.countDirCtrl = 0; /* quadrature */ - cmReg.reg.outputRegLatchCtrl = 0; /* latch on read */ - cmReg.reg.preloadRegSel = 0; /* PR0 */ - cmReg.reg.reserved = 0; + cmReg.value = insn->data[1] & 0xFFFF; - /* Set Counter Mode Register */ /* printk("s526: Counter Mode register=%x\n", cmReg.value); */ outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, subdev_channel)); @@ -615,11 +623,11 @@ static int s526_gpct_insn_config(struct comedi_device *dev, cmReg.value = (short)(insn->data[1] & 0xFFFF); outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, subdev_channel)); - /* Load the pre-laod register high word */ + /* Load the pre-load register high word */ value = (short)((insn->data[2] >> 16) & 0xFFFF); outw(value, ADDR_CHAN_REG(REG_C0H, subdev_channel)); - /* Load the pre-laod register low word */ + /* Load the pre-load register low word */ value = (short)(insn->data[2] & 0xFFFF); outw(value, ADDR_CHAN_REG(REG_C0L, subdev_channel)); @@ -653,11 +661,11 @@ static int s526_gpct_insn_config(struct comedi_device *dev, cmReg.reg.preloadRegSel = 0; /* PR0 */ outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, subdev_channel)); - /* Load the pre-laod register 0 high word */ + /* Load the pre-load register 0 high word */ value = (short)((insn->data[2] >> 16) & 0xFFFF); outw(value, ADDR_CHAN_REG(REG_C0H, subdev_channel)); - /* Load the pre-laod register 0 low word */ + /* Load the pre-load register 0 low word */ value = (short)(insn->data[2] & 0xFFFF); outw(value, ADDR_CHAN_REG(REG_C0L, subdev_channel)); @@ -666,17 +674,17 @@ static int s526_gpct_insn_config(struct comedi_device *dev, cmReg.reg.preloadRegSel = 1; /* PR1 */ outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, subdev_channel)); - /* Load the pre-laod register 1 high word */ + /* Load the pre-load register 1 high word */ value = (short)((insn->data[3] >> 16) & 0xFFFF); outw(value, ADDR_CHAN_REG(REG_C0H, subdev_channel)); - /* Load the pre-laod register 1 low word */ + /* Load the pre-load register 1 low word */ value = (short)(insn->data[3] & 0xFFFF); outw(value, ADDR_CHAN_REG(REG_C0L, subdev_channel)); /* Write the Counter Control Register */ - if (insn->data[3] != 0) { - value = (short)(insn->data[3] & 0xFFFF); + if (insn->data[4] != 0) { + value = (short)(insn->data[4] & 0xFFFF); outw(value, ADDR_CHAN_REG(REG_C0C, subdev_channel)); } break; @@ -698,11 +706,11 @@ static int s526_gpct_insn_config(struct comedi_device *dev, cmReg.reg.preloadRegSel = 0; /* PR0 */ outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, subdev_channel)); - /* Load the pre-laod register 0 high word */ + /* Load the pre-load register 0 high word */ value = (short)((insn->data[2] >> 16) & 0xFFFF); outw(value, ADDR_CHAN_REG(REG_C0H, subdev_channel)); - /* Load the pre-laod register 0 low word */ + /* Load the pre-load register 0 low word */ value = (short)(insn->data[2] & 0xFFFF); outw(value, ADDR_CHAN_REG(REG_C0L, subdev_channel)); @@ -711,17 +719,17 @@ static int s526_gpct_insn_config(struct comedi_device *dev, cmReg.reg.preloadRegSel = 1; /* PR1 */ outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, subdev_channel)); - /* Load the pre-laod register 1 high word */ + /* Load the pre-load register 1 high word */ value = (short)((insn->data[3] >> 16) & 0xFFFF); outw(value, ADDR_CHAN_REG(REG_C0H, subdev_channel)); - /* Load the pre-laod register 1 low word */ + /* Load the pre-load register 1 low word */ value = (short)(insn->data[3] & 0xFFFF); outw(value, ADDR_CHAN_REG(REG_C0L, subdev_channel)); /* Write the Counter Control Register */ - if (insn->data[3] != 0) { - value = (short)(insn->data[3] & 0xFFFF); + if (insn->data[4] != 0) { + value = (short)(insn->data[4] & 0xFFFF); outw(value, ADDR_CHAN_REG(REG_C0C, subdev_channel)); } break; @@ -741,6 +749,7 @@ static int s526_gpct_winsn(struct comedi_device *dev, { int subdev_channel = CR_CHAN(insn->chanspec); /* Unpack chanspec */ short value; + union cmReg cmReg; printk("s526: GPCT_INSN_WRITE on channel %d\n", subdev_channel); cmReg.value = inw(ADDR_CHAN_REG(REG_C0M, subdev_channel)); @@ -775,9 +784,8 @@ static int s526_gpct_winsn(struct comedi_device *dev, (devpriv->s526_gpct_config[subdev_channel]).data[1] = insn->data[1]; } else { - printk("%d \t %d\n", insn->data[1], insn->data[2]); - printk - ("s526: INSN_WRITE: PTG: Problem with Pulse params\n"); + printk("s526: INSN_WRITE: PTG: Problem with Pulse params -> %d %d\n", + insn->data[0], insn->data[1]); return -EINVAL; } @@ -949,7 +957,7 @@ static int s526_dio_insn_bits(struct comedi_device *dev, data[1] = inw(ADDR_REG(REG_DIO)) & 0xFF; /* low 8 bits are the data */ /* or we could just return the software copy of the output values if * it was a purely digital output subdevice */ - /* data[1]=s->state; */ + /* data[1]=s->state & 0xFF; */ return 2; } @@ -959,28 +967,33 @@ static int s526_dio_insn_config(struct comedi_device *dev, struct comedi_insn *insn, unsigned int *data) { int chan = CR_CHAN(insn->chanspec); - short value; + int group, mask; printk("S526 DIO insn_config\n"); - if (insn->n != 1) - return -EINVAL; - - value = inw(ADDR_REG(REG_DIO)); - /* The input or output configuration of each digital line is * configured by a special insn_config instruction. chanspec * contains the channel to be changed, and data[0] contains the * value COMEDI_INPUT or COMEDI_OUTPUT. */ - if (data[0] == COMEDI_OUTPUT) { - value |= 1 << (chan + 10); /* bit 10/11 set the group 1/2's mode */ - s->io_bits |= (0xF << chan); - } else { - value &= ~(1 << (chan + 10)); /* 1 is output, 0 is input. */ - s->io_bits &= ~(0xF << chan); + group = chan >> 2; + mask = 0xF << (group << 2); + switch (data[0]) { + case INSN_CONFIG_DIO_OUTPUT: + s->state |= 1 << (group + 10); // bit 10/11 set the group 1/2's mode + s->io_bits |= mask; + break; + case INSN_CONFIG_DIO_INPUT: + s->state &= ~(1 << (group + 10));// 1 is output, 0 is input. + s->io_bits &= ~mask; + break; + case INSN_CONFIG_DIO_QUERY: + data[1] = (s->io_bits & mask) ? COMEDI_OUTPUT : COMEDI_INPUT; + return insn->n; + default: + return -EINVAL; } - outw(value, ADDR_REG(REG_DIO)); + outw(s->state, ADDR_REG(REG_DIO)); return 1; } diff --git a/drivers/staging/comedi/drivers/serial2002.c b/drivers/staging/comedi/drivers/serial2002.c index a21967983942..82aa86e718b2 100644 --- a/drivers/staging/comedi/drivers/serial2002.c +++ b/drivers/staging/comedi/drivers/serial2002.c @@ -35,6 +35,7 @@ Status: in development #include <linux/delay.h> #include <linux/ioport.h> +#include <linux/sched.h> #include <asm/termios.h> #include <asm/ioctls.h> diff --git a/drivers/staging/cowloop/Kconfig b/drivers/staging/cowloop/Kconfig deleted file mode 100644 index 58d2a23bd2c1..000000000000 --- a/drivers/staging/cowloop/Kconfig +++ /dev/null @@ -1,16 +0,0 @@ -config COWLOOP - tristate "copy-on-write pseudo Block Driver" - depends on BLOCK - default n - ---help--- - Cowloop is a "copy-on-write" pseudo block driver. It can be - stacked on top of a "real" block driver, and catches all write - operations on their way from the file systems layer above to - the real driver below, effectively shielding the lower driver - from those write accesses. The requests are then diverted to - an ordinary file, located somewhere else (configurable). Later - read requests are checked to see whether they can be serviced - by the "real" block driver below, or must be pulled in from - the diverted location. More information and userspace tools to - use the driver are on the project's website - http://www.ATComputing.nl/cowloop/ diff --git a/drivers/staging/cowloop/Makefile b/drivers/staging/cowloop/Makefile deleted file mode 100644 index 2b6b81a63d21..000000000000 --- a/drivers/staging/cowloop/Makefile +++ /dev/null @@ -1 +0,0 @@ -obj-$(CONFIG_COWLOOP) += cowloop.o diff --git a/drivers/staging/cowloop/TODO b/drivers/staging/cowloop/TODO deleted file mode 100644 index 9399d1c16e15..000000000000 --- a/drivers/staging/cowloop/TODO +++ /dev/null @@ -1,11 +0,0 @@ -TODO: - - checkpatch.pl cleanups - - run sparse to ensure clean - - fix up 32/64bit ioctl issues - - move proc file usage to debugfs - - audit ioctls - - add documentation - - get linux-fsdevel to review it - -Please send patches to "H.J. Thomassen" <hjt@ATComputing.nl> and -Greg Kroah-Hartman <gregkh@suse.de> diff --git a/drivers/staging/cowloop/cowloop.c b/drivers/staging/cowloop/cowloop.c deleted file mode 100644 index a71c743a1196..000000000000 --- a/drivers/staging/cowloop/cowloop.c +++ /dev/null @@ -1,2842 +0,0 @@ -/* -** COWLOOP block device driver (2.6 kernel compliant) -** ======================================================================= -** Read-write loop-driver with copy-on-write functionality. -** -** Synopsis: -** -** modprobe cowloop [maxcows=..] [rdofile=..... cowfile=.... [option=r]] -** -** Definition of number of configured cowdevices: -** maxcows= number of configured cowdevices (default: 16) -** (do not confuse this with MAXCOWS: absolute maximum as compiled) -** -** One pair of filenames can be supplied during insmod/modprobe to open -** the first cowdevice: -** rdofile= read-only file (or filesystem) -** cowfile= storage-space for modified blocks of read-only file(system) -** option=r repair cowfile automatically if it appears to be dirty -** -** Other cowdevices can be activated via the command "cowdev" -** whenever the cowloop-driver is loaded. -** -** The read-only file may be of type 'regular' or 'block-device'. -** -** The cowfile must be of type 'regular'. -** If an existing regular file is used as cowfile, its contents will be -** used again for the current read-only file. When the cowfile has not been -** closed properly during a previous session (i.e. rmmod cowloop), the -** cowloop-driver refuses to open it unless the parameter "option=r" is -** specified. -** -** Layout of cowfile: -** -** +-----------------------------+ -** | cow head block | MAPUNIT bytes -** |-----------------------------| -** | | MAPUNIT bytes -** |--- ---| -** | | MAPUNIT bytes -** |--- ---| -** | used-block bitmap | MAPUNIT bytes -** |-----------------------------| -** | gap to align start-offset | -** | to 4K multiple | -** |-----------------------------| <---- start-offset cow blocks -** | | -** | written cow blocks | MAPUNIT bytes -** | ..... | -** -** cowhead block: -** - contains general info about the rdofile which is related -** to this cowfile -** -** used-block bitmap: -** - contains one bit per block with a size of MAPUNIT bytes -** - bit-value '1' = block has been written on cow -** '0' = block unused on cow -** - total bitmap rounded to multiples of MAPUNIT -** -** ============================================================================ -** Author: Gerlof Langeveld - AT Computing (March 2003) -** Current maintainer: Hendrik-Jan Thomassen - AT Computing (Summer 2006) -** Email: hjt@ATComputing.nl -** ---------------------------------------------------------------------------- -** Copyright (C) 2003-2009 AT Consultancy -** -** This program is free software; you can redistribute it and/or modify it -** under the terms of the GNU General Public License as published by the -** Free Software Foundation; either version 2, or (at your option) any -** later version. -** -** This program is distributed in the hope that it will be useful, but -** WITHOUT ANY WARRANTY; without even the implied warranty of -** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -** See the GNU General Public License for more details. -** -** You should have received a copy of the GNU General Public License -** along with this program; if not, write to the Free Software -** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -** ---------------------------------------------------------------------------- -** -** Major modifications: -** -** 200405 Ported to kernel-version 2.6 Hendrik-Jan Thomassen -** 200405 Added cowhead to cowfile to garantee -** consistency with read-only file Gerlof Langeveld -** 200405 Postponed flushing of bitmaps to improve -** performance. Gerlof Langeveld -** 200405 Inline recovery for dirty cowfiles. Gerlof Langeveld -** 200502 Redesign to support more cowdevices. Gerlof Langeveld -** 200502 Support devices/file > 2 Gbytes. Gerlof Langeveld -** 200507 Check for free space to expand cowfile. Gerlof Langeveld -** 200902 Upgrade for kernel 2.6.28 Hendrik-Jan Thomassen -** -** Inspired by -** loop.c by Theodore Ts'o and -** cloop.c by Paul `Rusty' Russell & Klaus Knopper. -** -** Design-considerations: -** -** For the first experiments with the cowloop-driver, the request-queue -** made use of the do_generic_file_read() which worked fine except -** in combination with the cloop-driver; that combination -** resulted in a non-interruptible hangup of the system during -** heavy load. Other experiments using the `make_request' interface also -** resulted in unpredictable system hangups (with proper use of spinlocks). -** -** To overcome these problems, the cowloop-driver starts a kernel-thread -** for every active cowdevice. -** All read- and write-request on the read-only file and copy-on-write file -** are handled in the context of that thread. -** A scheme has been designed to wakeup the kernel-thread as -** soon as I/O-requests are available in the request-queue; this thread -** handles the requests one-by-one by calling the proper read- or -** write-function related to the open read-only file or copy-on-write file. -** When all pending requests have been handled, the kernel-thread goes -** back to sleep-state. -** This approach requires some additional context-switches; however the -** performance loss during heavy I/O is less than 3%. -** -** -------------------------------------------------------------------------*/ -/* The following is the cowloop package version number. It must be - identical to the content of the include-file "version.h" that is - used in all supporting utilities: */ -char revision[] = "$Revision: 3.1 $"; /* cowlo_init_module() has - assumptions about this string's format */ - -/* Note that the following numbers are *not* the cowloop package version - numbers, but separate revision history numbers to track the - modifications of this particular source file: */ -/* $Log: cowloop.c,v $ -** -** Revision 1.30 2009/02/08 hjt -** Integrated earlier fixes -** Upgraded to kernel 2.6.28 (thanks Jerome Poulin) -** -** Revision 1.29 2006/12/03 22:12:00 hjt -** changed 'cowdevlock' from spinlock to semaphore, to avoid -** "scheduling while atomic". Contributed by Juergen Christ. -** Added version.h again -** -** Revision 1.28 2006/08/16 16:00:00 hjt -** malloc each individual cowloopdevice struct separately -** -** Revision 1.27 2006/03/14 14:57:03 root -** Removed include version.h -** -** Revision 1.26 2005/08/08 11:22:48 root -** Implement possibility to close a cow file or reopen a cowfile read-only. -** -** Revision 1.25 2005/08/03 14:00:39 root -** Added modinfo info to driver. -** -** Revision 1.24 2005/07/21 06:14:53 root -** Cosmetic changes source code. -** -** Revision 1.23 2005/07/20 13:07:32 root -** Supply ioctl to write watchdog program to react on lack of cowfile space. -** -** Revision 1.22 2005/07/20 07:53:34 root -** Regular verification of free space in filesystem holding the cowfile -** (give warnings whenever space is almost exhausted). -** Terminology change: checksum renamed to fingerprint. -** -** Revision 1.21 2005/07/19 09:21:52 root -** Removing maximum limit of 16 Gb per cowdevice. -** -** Revision 1.20 2005/07/19 07:50:33 root -** Minor bugfixes and cosmetic changes. -** -** Revision 1.19 2005/06/10 12:29:55 root -** Removed lock/unlock operation from cowlo_open(). -** -** Revision 1.18 2005/05/09 12:56:26 root -** Allow a cowdevice to be open more than once -** (needed for support of ReiserFS and XFS). -** -** Revision 1.17 2005/03/17 14:36:16 root -** Fixed some license issues. -** -** Revision 1.16 2005/03/07 14:42:05 root -** Only allow one parallel open per cowdevice. -** -** Revision 1.15 2005/02/18 11:52:04 gerlof -** Redesign to support more than one cowdevice > 2 Gb space. -** -** Revision 1.14 2004/08/17 14:19:16 gerlof -** Modified output of /proc/cowloop. -** -** Revision 1.13 2004/08/16 07:21:10 gerlof -** Separate statistical counter for read on rdofile and cowfile. -** -** Revision 1.12 2004/08/11 06:52:11 gerlof -** Modified messages. -** -** Revision 1.11 2004/08/11 06:44:11 gerlof -** Modified log messages. -** -** Revision 1.10 2004/08/10 12:27:27 gerlof -** Cosmetic changes. -** -** Revision 1.9 2004/08/09 11:43:37 gerlof -** Removed double definition of major number (COWMAJOR). -** -** Revision 1.8 2004/08/09 08:03:39 gerlof -** Cleanup of messages. -** -** Revision 1.7 2004/05/27 06:37:33 gerlof -** Modified /proc message. -** -** Revision 1.6 2004/05/26 21:23:28 gerlof -** Modified /proc output. -** -** Revision 1.5 2004/05/26 13:23:34 gerlof -** Support cowsync to force flushing the bitmaps and cowhead. -** -** Revision 1.4 2004/05/26 11:11:10 gerlof -** Updated the comment to the actual situation. -** -** Revision 1.3 2004/05/26 10:50:00 gerlof -** Implemented recovery-option. -** -** Revision 1.2 2004/05/25 15:14:41 gerlof -** Modified bitmap flushing strategy. -** -*/ - -#define COWMAJOR 241 - -// #define COWDEBUG - -#ifdef COWDEBUG -#define DEBUGP printk -#define DCOW KERN_ALERT -#else -#define DEBUGP(format, x...) -#endif - -#include <linux/types.h> -#include <linux/autoconf.h> -#ifndef AUTOCONF_INCLUDED -#include <linux/config.h> -#endif -#include <linux/module.h> -#include <linux/version.h> -#include <linux/moduleparam.h> -#include <linux/init.h> -#include <linux/errno.h> -#include <linux/kernel.h> -#include <linux/major.h> -#include <linux/sched.h> -#include <linux/fs.h> -#include <linux/file.h> -#include <linux/stat.h> -#include <linux/vmalloc.h> -#include <linux/slab.h> -#include <linux/semaphore.h> -#include <asm/uaccess.h> -#include <linux/proc_fs.h> -#include <linux/blkdev.h> -#include <linux/buffer_head.h> -#include <linux/hdreg.h> -#include <linux/genhd.h> -#include <linux/statfs.h> - -#include "cowloop.h" - -MODULE_LICENSE("GPL"); -/* MODULE_AUTHOR("Gerlof Langeveld <gerlof@ATComputing.nl>"); obsolete address */ -MODULE_AUTHOR("Hendrik-Jan Thomassen <hjt@ATComputing.nl>"); /* current maintainer */ -MODULE_DESCRIPTION("Copy-on-write loop driver"); -MODULE_PARM_DESC(maxcows, " Number of configured cowdevices (default 16)"); -MODULE_PARM_DESC(rdofile, " Read-only file for /dev/cow/0"); -MODULE_PARM_DESC(cowfile, " Cowfile for /dev/cow/0"); -MODULE_PARM_DESC(option, " Repair cowfile if inconsistent: option=r"); - -#define DEVICE_NAME "cow" - -#define DFLCOWS 16 /* default cowloop devices */ - -static int maxcows = DFLCOWS; -module_param(maxcows, int, 0); -static char *rdofile = ""; -module_param(rdofile, charp, 0); -static char *cowfile = ""; -module_param(cowfile, charp, 0); -static char *option = ""; -module_param(option, charp, 0); - -/* -** per cowdevice several bitmap chunks are allowed of MAPCHUNKSZ each -** -** each bitmap chunk can describe MAPCHUNKSZ * 8 * MAPUNIT bytes of data -** suppose: -** MAPCHUNKSZ 4096 and MAPUNIT 1024 --> 4096 * 8 * 1024 = 32 Mb per chunk -*/ -#define MAPCHUNKSZ 4096 /* #bytes per bitmap chunk (do not change) */ - -#define SPCMINBLK 100 /* space threshold to give warning messages */ -#define SPCDFLINTVL 16 /* once every SPCDFLINTVL writes to cowfile, */ - /* available space in filesystem is checked */ - -#define CALCMAP(x) ((x)/(MAPCHUNKSZ*8)) -#define CALCBYTE(x) (((x)%(MAPCHUNKSZ*8))>>3) -#define CALCBIT(x) ((x)&7) - -#define ALLCOW 1 -#define ALLRDO 2 -#define MIXEDUP 3 - -static char allzeroes[MAPUNIT]; - -/* -** administration per cowdevice (pair of cowfile/rdofile) -*/ - -/* bit-values for state */ -#define COWDEVOPEN 0x01 /* cowdevice opened */ -#define COWRWCOWOPEN 0x02 /* cowfile opened read-write */ -#define COWRDCOWOPEN 0x04 /* cowfile opened read-only */ -#define COWWATCHDOG 0x08 /* ioctl for watchdog cowfile space active */ - -#define COWCOWOPEN (COWRWCOWOPEN|COWRDCOWOPEN) - -struct cowloop_device -{ - /* - ** current status - */ - int state; /* bit-values (see above) */ - int opencnt; /* # opens for cowdevice */ - - /* - ** open file pointers - */ - struct file *rdofp, *cowfp; /* open file pointers */ - char *rdoname, *cowname; /* file names */ - - /* - ** request queue administration - */ - struct request_queue *rqueue; - spinlock_t rqlock; - struct gendisk *gd; - - /* - ** administration about read-only file - */ - unsigned int numblocks; /* # blocks input file in MAPUNIT */ - unsigned int blocksz; /* minimum unit to access this dev */ - unsigned long fingerprint; /* fingerprint of current rdofile */ - struct block_device *belowdev; /* block device below us */ - struct gendisk *belowgd; /* gendisk for blk dev below us */ - struct request_queue *belowq; /* req. queue of blk dev below us */ - - /* - ** bitmap administration to register which blocks are modified - */ - long int mapsize; /* total size of bitmap (bytes) */ - long int mapremain; /* remaining bytes in last bitmap */ - int mapcount; /* number of bitmaps in use */ - char **mapcache; /* area with pointers to bitmaps */ - - char *iobuf; /* databuffer of MAPUNIT bytes */ - struct cowhead *cowhead; /* buffer containing cowhead */ - - /* - ** administration for interface with the kernel-thread - */ - int pid; /* pid==0: no thread available */ - struct request *req; /* request to be handled now */ - wait_queue_head_t waitq; /* wait-Q: thread waits for work */ - char closedown; /* boolean: thread exit required */ - char qfilled; /* boolean: I/O request pending */ - char iobusy; /* boolean: req under treatment */ - - /* - ** administration to keep track of free space in cowfile filesystem - */ - unsigned long blksize; /* block size of fs (bytes) */ - unsigned long blktotal; /* recent total space in fs (blocks) */ - unsigned long blkavail; /* recent free space in fs (blocks) */ - - wait_queue_head_t watchq; /* wait-Q: watcher awaits threshold */ - unsigned long watchthresh; /* threshold of watcher (blocks) */ - - /* - ** statistical counters - */ - unsigned long rdoreads; /* number of read-actions rdo */ - unsigned long cowreads; /* number of read-actions cow */ - unsigned long cowwrites; /* number of write-actions */ - unsigned long nrcowblocks; /* number of blocks in use on cow */ -}; - -static struct cowloop_device **cowdevall; /* ptr to ptrs to all cowdevices */ -static struct semaphore cowdevlock; /* generic lock for cowdevs */ - -static struct gendisk *cowctlgd; /* gendisk control channel */ -static spinlock_t cowctlrqlock; /* for req.q. of ctrl. channel */ - -/* -** private directory /proc/cow -*/ -struct proc_dir_entry *cowlo_procdir; - -/* -** function prototypes -*/ -static long int cowlo_do_request (struct request *req); -static void cowlo_sync (void); -static int cowlo_checkio (struct cowloop_device *, int, loff_t); -static int cowlo_readmix (struct cowloop_device *, void *, int, loff_t); -static int cowlo_writemix (struct cowloop_device *, void *, int, loff_t); -static long int cowlo_readrdo (struct cowloop_device *, void *, int, loff_t); -static long int cowlo_readcow (struct cowloop_device *, void *, int, loff_t); -static long int cowlo_readcowraw (struct cowloop_device *, void *, int, loff_t); -static long int cowlo_writecow (struct cowloop_device *, void *, int, loff_t); -static long int cowlo_writecowraw(struct cowloop_device *, void *, int, loff_t); -static int cowlo_ioctl (struct block_device *, fmode_t, - unsigned int, unsigned long); -static int cowlo_makepair (struct cowpair __user *); -static int cowlo_removepair (unsigned long __user *); -static int cowlo_watch (struct cowpair __user *); -static int cowlo_cowctl (unsigned long __user *, int); -static int cowlo_openpair (char *, char *, int, int); -static int cowlo_closepair (struct cowloop_device *); -static int cowlo_openrdo (struct cowloop_device *, char *); -static int cowlo_opencow (struct cowloop_device *, char *, int); -static void cowlo_undo_openrdo(struct cowloop_device *); -static void cowlo_undo_opencow(struct cowloop_device *); - -/*****************************************************************************/ -/* System call handling */ -/*****************************************************************************/ - -/* -** handle system call open()/mount() -** -** returns: -** 0 - okay -** < 0 - error value -*/ -static int cowlo_open(struct block_device *bdev, fmode_t mode) -{ - struct inode *inode = bdev->bd_inode; - - if (!inode) - return -EINVAL; - - if (imajor(inode) != COWMAJOR) { - printk(KERN_WARNING - "cowloop - unexpected major %d\n", imajor(inode)); - return -ENODEV; - } - - switch (iminor(inode)) { - case COWCTL: - DEBUGP(DCOW"cowloop - open %d control\n", COWCTL); - break; - - default: - DEBUGP(DCOW"cowloop - open minor %d\n", iminor(inode)); - - if ( iminor(inode) >= maxcows ) - return -ENODEV; - - if ( !((cowdevall[iminor(inode)])->state & COWDEVOPEN) ) - return -ENODEV; - - (cowdevall[iminor(inode)])->opencnt++; - } - - return 0; -} - -/* -** handle system call close()/umount() -** -** returns: -** 0 - okay -*/ -static int cowlo_release(struct gendisk *gd, fmode_t mode) -{ - struct block_device *bdev; - struct inode *inode; - - bdev = bdget_disk(gd, 0); - inode = bdev->bd_inode; - if (!inode) - return 0; - - DEBUGP(DCOW"cowloop - release (close) minor %d\n", iminor(inode)); - - if ( iminor(inode) != COWCTL) - (cowdevall[iminor(inode)])->opencnt--; - - return 0; -} - -/* -** handle system call ioctl() -** -** returns: -** 0 - okay -** < 0 - error value -*/ -static int cowlo_ioctl(struct block_device *bdev, fmode_t mode, - unsigned int cmd, unsigned long arg) -{ - struct hd_geometry geo; - struct inode *inode = bdev->bd_inode; - - DEBUGP(DCOW "cowloop - ioctl cmd %x\n", cmd); - - switch ( iminor(inode) ) { - - /* - ** allowed via control device only - */ - case COWCTL: - switch (cmd) { - /* - ** write all bitmap chunks and cowheaders to cowfiles - */ - case COWSYNC: - down(&cowdevlock); - cowlo_sync(); - up(&cowdevlock); - return 0; - - /* - ** open a new cowdevice (pair of rdofile/cowfile) - */ - case COWMKPAIR: - return cowlo_makepair((void __user *)arg); - - /* - ** close a cowdevice (pair of rdofile/cowfile) - */ - case COWRMPAIR: - return cowlo_removepair((void __user *)arg); - - /* - ** watch free space of filesystem containing cowfile - */ - case COWWATCH: - return cowlo_watch((void __user *)arg); - - /* - ** close cowfile for active device - */ - case COWCLOSE: - return cowlo_cowctl((void __user *)arg, COWCLOSE); - - /* - ** reopen cowfile read-only for active device - */ - case COWRDOPEN: - return cowlo_cowctl((void __user *)arg, COWRDOPEN); - - default: - return -EINVAL; - } /* end of switch on command */ - - /* - ** allowed for any other cowdevice - */ - default: - switch (cmd) { - /* - ** HDIO_GETGEO must be supported for fdisk, etc - */ - case HDIO_GETGEO: - geo.cylinders = 0; - geo.heads = 0; - geo.sectors = 0; - - if (copy_to_user((void __user *)arg, &geo, sizeof geo)) - return -EFAULT; - return 0; - - default: - return -EINVAL; - } /* end of switch on ioctl-cmd code parameter */ - } /* end of switch on minor number */ -} - -static struct block_device_operations cowlo_fops = -{ - .owner = THIS_MODULE, - .open = cowlo_open, /* called upon open */ - .release = cowlo_release, /* called upon close */ - .ioctl = cowlo_ioctl, /* called upon ioctl */ -}; - -/* -** handle ioctl-command COWMKPAIR: -** open a new cowdevice (pair of rdofile/cowfile) on-the-fly -** -** returns: -** 0 - okay -** < 0 - error value -*/ -static int -cowlo_makepair(struct cowpair __user *arg) -{ - int i, rv=0; - struct cowpair cowpair; - unsigned char *cowpath; - unsigned char *rdopath; - - /* - ** retrieve info about pathnames - */ - if ( copy_from_user(&cowpair, arg, sizeof cowpair) ) - return -EFAULT; - - if ( (MAJOR(cowpair.device) != COWMAJOR) && (cowpair.device != ANYDEV) ) - return -EINVAL; - - if ( (MINOR(cowpair.device) >= maxcows) && (cowpair.device != ANYDEV) ) - return -EINVAL; - - /* - ** retrieve pathname strings - */ - if ( (cowpair.cowflen > PATH_MAX) || (cowpair.rdoflen > PATH_MAX) ) - return -ENAMETOOLONG; - - if ( !(cowpath = kmalloc(cowpair.cowflen+1, GFP_KERNEL)) ) - return -ENOMEM; - - if ( copy_from_user(cowpath, (void __user *)cowpair.cowfile, - cowpair.cowflen) ) { - kfree(cowpath); - return -EFAULT; - } - *(cowpath+cowpair.cowflen) = 0; - - if ( !(rdopath = kmalloc(cowpair.rdoflen+1, GFP_KERNEL)) ) { - kfree(cowpath); - return -ENOMEM; - } - - if ( copy_from_user(rdopath, (void __user *)cowpair.rdofile, - cowpair.rdoflen) ) { - kfree(rdopath); - kfree(cowpath); - return -EFAULT; - } - *(rdopath+cowpair.rdoflen) = 0; - - /* - ** open new cowdevice - */ - if ( cowpair.device == ANYDEV) { - /* - ** search first unused minor - */ - for (i=0, rv=-EBUSY; i < maxcows; i++) { - if ( !((cowdevall[i])->state & COWDEVOPEN) ) { - rv = cowlo_openpair(rdopath, cowpath, 0, i); - break; - } - } - - if (rv) { /* open failed? */ - kfree(rdopath); - kfree(cowpath); - return rv; - } - - /* - ** return newly allocated cowdevice to user space - */ - cowpair.device = MKDEV(COWMAJOR, i); - - if ( copy_to_user(arg, &cowpair, sizeof cowpair)) { - kfree(rdopath); - kfree(cowpath); - return -EFAULT; - } - } else { /* specific minor requested */ - if ( (rv = cowlo_openpair(rdopath, cowpath, 0, - MINOR(cowpair.device)))) { - kfree(rdopath); - kfree(cowpath); - return rv; - } - } - - return 0; -} - -/* -** handle ioctl-command COWRMPAIR: -** deactivate an existing cowdevice (pair of rdofile/cowfile) on-the-fly -** -** returns: -** 0 - okay -** < 0 - error value -*/ -static int -cowlo_removepair(unsigned long __user *arg) -{ - unsigned long cowdevice; - struct cowloop_device *cowdev; - - /* - ** retrieve info about device to be removed - */ - if ( copy_from_user(&cowdevice, arg, sizeof cowdevice)) - return -EFAULT; - - /* - ** verify major-minor number - */ - if ( MAJOR(cowdevice) != COWMAJOR) - return -EINVAL; - - if ( MINOR(cowdevice) >= maxcows) - return -EINVAL; - - cowdev = cowdevall[MINOR(cowdevice)]; - - if ( !(cowdev->state & COWDEVOPEN) ) - return -ENODEV; - - /* - ** synchronize bitmaps and close cowdevice - */ - if (cowdev->state & COWRWCOWOPEN) { - down(&cowdevlock); - cowlo_sync(); - up(&cowdevlock); - } - - return cowlo_closepair(cowdev); -} - -/* -** handle ioctl-command COWWATCH: -** watch the free space of the filesystem containing a cowfile -** of an open cowdevice -** -** returns: -** 0 - okay -** < 0 - error value -*/ -static int -cowlo_watch(struct cowpair __user *arg) -{ - struct cowloop_device *cowdev; - struct cowwatch cowwatch; - - /* - ** retrieve structure holding info - */ - if ( copy_from_user(&cowwatch, arg, sizeof cowwatch)) - return -EFAULT; - - /* - ** verify if cowdevice exists and is currently open - */ - if ( MINOR(cowwatch.device) >= maxcows) - return -EINVAL; - - cowdev = cowdevall[MINOR(cowwatch.device)]; - - if ( !(cowdev->state & COWDEVOPEN) ) - return -ENODEV; - - /* - ** if the WATCHWAIT-option is set, wait until the indicated - ** threshold is reached (only one waiter allowed) - */ - if (cowwatch.flags & WATCHWAIT) { - /* - ** check if already another waiter active - ** for this cowdevice - */ - if (cowdev->state & COWWATCHDOG) - return -EAGAIN; - - cowdev->state |= COWWATCHDOG; - - cowdev->watchthresh = (unsigned long long) - cowwatch.threshold / - (cowdev->blksize / 1024); - - if (wait_event_interruptible(cowdev->watchq, - cowdev->watchthresh >= cowdev->blkavail)) { - cowdev->state &= ~COWWATCHDOG; - return EINTR; - } - - cowdev->state &= ~COWWATCHDOG; - } - - cowwatch.totalkb = (unsigned long long)cowdev->blktotal * - cowdev->blksize / 1024; - cowwatch.availkb = (unsigned long long)cowdev->blkavail * - cowdev->blksize / 1024; - - if ( copy_to_user(arg, &cowwatch, sizeof cowwatch)) - return -EFAULT; - - return 0; -} - -/* -** handle ioctl-commands COWCLOSE and COWRDOPEN: -** COWCLOSE - close the cowfile while the cowdevice remains open; -** this allows an unmount of the filesystem on which -** the cowfile resides -** COWRDOPEN - close the cowfile and reopen it for read-only; -** this allows a remount read-ony of the filesystem -** on which the cowfile resides -** -** returns: -** 0 - okay -** < 0 - error value -*/ -static int -cowlo_cowctl(unsigned long __user *arg, int cmd) -{ - struct cowloop_device *cowdev; - unsigned long cowdevice; - - /* - ** retrieve info about device to be removed - */ - if ( copy_from_user(&cowdevice, arg, sizeof cowdevice)) - return -EFAULT; - - /* - ** verify major-minor number - */ - if ( MAJOR(cowdevice) != COWMAJOR) - return -EINVAL; - - if ( MINOR(cowdevice) >= maxcows) - return -EINVAL; - - cowdev = cowdevall[MINOR(cowdevice)]; - - if ( !(cowdev->state & COWDEVOPEN) ) - return -ENODEV; - - /* - ** synchronize bitmaps and close cowfile - */ - if (cowdev->state & COWRWCOWOPEN) { - down(&cowdevlock); - cowlo_sync(); - up(&cowdevlock); - } - - /* - ** handle specific ioctl-command - */ - switch (cmd) { - case COWRDOPEN: - /* - ** if the cowfile is still opened read-write - */ - if (cowdev->state & COWRWCOWOPEN) { - /* - ** close the cowfile - */ - if (cowdev->cowfp) - filp_close(cowdev->cowfp, 0); - - cowdev->state &= ~COWRWCOWOPEN; - - /* - ** open again for read-only - */ - cowdev->cowfp = filp_open(cowdev->cowname, - O_RDONLY|O_LARGEFILE, 0600); - - if ( (cowdev->cowfp == NULL) || IS_ERR(cowdev->cowfp) ) { - printk(KERN_ERR - "cowloop - failed to reopen cowfile %s\n", - cowdev->cowname); - return -EINVAL; - } - - /* - ** mark cowfile open for read-only - */ - cowdev->state |= COWRDCOWOPEN; - } else { - return -EINVAL; - } - break; - - case COWCLOSE: - /* - ** if the cowfile is still open - */ - if (cowdev->state & COWCOWOPEN) { - /* - ** close the cowfile - */ - if (cowdev->cowfp) - filp_close(cowdev->cowfp, 0); - - cowdev->state &= ~COWCOWOPEN; - } - } - - return 0; -} - - -/*****************************************************************************/ -/* Handling of I/O-requests for a cowdevice */ -/*****************************************************************************/ - -/* -** function to be called by core-kernel to handle the I/O-requests -** in the queue -*/ -static void cowlo_request(struct request_queue *q) -{ - struct request *req; - struct cowloop_device *cowdev; - - DEBUGP(DCOW "cowloop - request function called....\n"); - - while((req = blk_peek_request(q)) != NULL) { - DEBUGP(DCOW "cowloop - got next request\n"); - - if (! blk_fs_request(req)) { - /* this is not a normal file system request */ - __blk_end_request_cur(req, -EIO); - continue; - } - cowdev = req->rq_disk->private_data; - - if (cowdev->iobusy) - return; - else - cowdev->iobusy = 1; - - /* - ** when no kernel-thread is available, the request will - ** produce an I/O-error - */ - if (!cowdev->pid) { - printk(KERN_ERR"cowloop - no thread available\n"); - __blk_end_request_cur(req, -EIO); /* request failed */ - cowdev->iobusy = 0; - continue; - } - - /* - ** handle I/O-request in the context of the kernel-thread - */ - cowdev->req = req; - cowdev->qfilled = 1; - - wake_up_interruptible_sync(&cowdev->waitq); - - /* - ** get out of this function now while the I/O-request is - ** under treatment of the kernel-thread; this function - ** will be called again after the current I/O-request has - ** been finished by the thread - */ - return; - } -} - -/* -** daemon-process (kernel-thread) executes this function -*/ -static int -cowlo_daemon(struct cowloop_device *cowdev) -{ - int rv; - int minor; - char myname[16]; - - for (minor = 0; minor < maxcows; minor++) { - if (cowdev == cowdevall[minor]) break; - } - sprintf(myname, "cowloopd%d", minor); - - daemonize(myname); - - while (!cowdev->closedown) { - /* - ** sleep while waiting for an I/O request; - ** note that no non-interruptible wait has been used - ** because the non-interruptible version of - ** a *synchronous* wake_up does not exist (any more) - */ - if (wait_event_interruptible(cowdev->waitq, cowdev->qfilled)){ - flush_signals(current); /* ignore signal-based wakeup */ - continue; - } - - if (cowdev->closedown) /* module will be unloaded ? */{ - cowdev->pid = 0; - return 0; - } - - /* - ** woken up by the I/O-request handler: treat requested I/O - */ - cowdev->qfilled = 0; - - rv = cowlo_do_request(cowdev->req); - - /* - ** reacquire the queue-spinlock for manipulating - ** the request-queue and dequeue the request - */ - spin_lock_irq(&cowdev->rqlock); - - __blk_end_request_cur(cowdev->req, rv); - cowdev->iobusy = 0; - - /* - ** initiate the next request from the queue - */ - cowlo_request(cowdev->rqueue); - - spin_unlock_irq(&cowdev->rqlock); - } - return 0; -} - -/* -** function to be called in the context of the kernel thread -** to handle the queued I/O-requests -** -** returns: -** 0 - fail -** 1 - success -*/ -static long int -cowlo_do_request(struct request *req) -{ - unsigned long len; - long int rv; - loff_t offset; - struct cowloop_device *cowdev = req->rq_disk->private_data; - - /* - ** calculate some variables which are needed later on - */ - len = blk_rq_cur_sectors(req) << 9; - offset = (loff_t) blk_rq_pos(req) << 9; - - DEBUGP(DCOW"cowloop - req cmd=%d offset=%lld len=%lu addr=%p\n", - *(req->cmd), offset, len, req->buffer); - - /* - ** handle READ- or WRITE-request - */ - switch (rq_data_dir(req)) { - /**********************************************************/ - case READ: - switch ( cowlo_checkio(cowdev, len, offset) ) { - case ALLCOW: - rv = cowlo_readcow(cowdev, req->buffer, len, offset); - break; - - case ALLRDO: - rv = cowlo_readrdo(cowdev, req->buffer, len, offset); - break; - - case MIXEDUP: - rv = cowlo_readmix(cowdev, req->buffer, len, offset); - break; - - default: - rv = 0; /* never happens */ - } - break; - - /**********************************************************/ - case WRITE: - switch ( cowlo_checkio(cowdev, len, offset) ) { - case ALLCOW: - /* - ** straight-forward write will do... - */ - DEBUGP(DCOW"cowloop - write straight "); - - rv = cowlo_writecow(cowdev, req->buffer, len, offset); - break; /* from switch */ - - case ALLRDO: - if ( (len & MUMASK) == 0) { - DEBUGP(DCOW"cowloop - write straight "); - - rv = cowlo_writecow(cowdev, req->buffer, - len, offset); - break; - } - - case MIXEDUP: - rv = cowlo_writemix(cowdev, req->buffer, len, offset); - break; - - default: - rv = 0; /* never happens */ - } - break; - - default: - printk(KERN_ERR - "cowloop - unrecognized command %d\n", *(req->cmd)); - rv = 0; - } - - return (rv <= 0 ? 0 : 1); -} - -/* -** check for a given I/O-request if all underlying blocks -** (with size MAPUNIT) are either in the read-only file or in -** the cowfile (or a combination of the two) -** -** returns: -** ALLRDO - all underlying blocks in rdofile -** ALLCOW - all underlying blocks in cowfile -** MIXEDUP - underlying blocks partly in rdofile and partly in cowfile -*/ -static int -cowlo_checkio(struct cowloop_device *cowdev, int len, loff_t offset) -{ - unsigned long mapnum, bytenum, bitnum, blocknr, partlen; - long int totcnt, cowcnt; - char *mc; - - /* - ** notice that the requested block might cross - ** a blocksize boundary while one of the concerned - ** blocks resides in the read-only file and another - ** one in the copy-on-write file; in that case the - ** request will be broken up into pieces - */ - if ( (len <= MAPUNIT) && - (MAPUNIT - (offset & MUMASK) <= len) ) { - /* - ** easy situation: - ** requested data-block entirely fits within - ** the mapunit used for the bitmap - ** check if that block is located in rdofile or - ** cowfile - */ - blocknr = offset >> MUSHIFT; - - mapnum = CALCMAP (blocknr); - bytenum = CALCBYTE(blocknr); - bitnum = CALCBIT (blocknr); - - if (*(*(cowdev->mapcache+mapnum)+bytenum)&(1<<bitnum)) - return ALLCOW; - else - return ALLRDO; - } - - /* - ** less easy situation: - ** the requested data-block does not fit within the mapunit - ** used for the bitmap - ** check if *all* underlying blocks involved reside on the rdofile - ** or the cowfile (so still no breakup required) - */ - for (cowcnt=totcnt=0; len > 0; len-=partlen, offset+=partlen, totcnt++){ - /* - ** calculate blocknr of involved block - */ - blocknr = offset >> MUSHIFT; - - /* - ** calculate partial length for this transfer - */ - partlen = MAPUNIT - (offset & MUMASK); - if (partlen > len) - partlen = len; - - /* - ** is this block located in the cowfile - */ - mapnum = CALCMAP (blocknr); - bytenum = CALCBYTE(blocknr); - bitnum = CALCBIT (blocknr); - - mc = *(cowdev->mapcache+mapnum); - - if (*(mc+bytenum)&(1<<bitnum)) - cowcnt++;; - - DEBUGP(DCOW - "cowloop - check %lu - map %lu, byte %lu, bit %lu, " - "cowcnt %ld, totcnt %ld %02x %p\n", - blocknr, mapnum, bytenum, bitnum, cowcnt, totcnt, - *(mc+bytenum), mc); - } - - if (cowcnt == 0) /* all involved blocks on rdofile? */ - return ALLRDO; - - if (cowcnt == totcnt) /* all involved blocks on cowfile? */ - return ALLCOW; - - /* - ** situation somewhat more complicated: - ** involved underlying blocks spread over both files - */ - return MIXEDUP; -} - -/* -** read requested chunk partly from rdofile and partly from cowfile -** -** returns: -** 0 - fail -** 1 - success -*/ -static int -cowlo_readmix(struct cowloop_device *cowdev, void *buf, int len, loff_t offset) -{ - unsigned long mapnum, bytenum, bitnum, blocknr, partlen; - long int rv; - char *mc; - - /* - ** complicated approach: breakup required of read-request - */ - for (rv=1; len > 0; len-=partlen, buf+=partlen, offset+=partlen) { - /* - ** calculate blocknr of entire block - */ - blocknr = offset >> MUSHIFT; - - /* - ** calculate partial length for this transfer - */ - partlen = MAPUNIT - (offset & MUMASK); - if (partlen > len) - partlen = len; - - /* - ** is this block located in the cowfile - */ - mapnum = CALCMAP (blocknr); - bytenum = CALCBYTE(blocknr); - bitnum = CALCBIT (blocknr); - mc = *(cowdev->mapcache+mapnum); - - if (*(mc+bytenum)&(1<<bitnum)) { - /* - ** read (partial) block from cowfile - */ - DEBUGP(DCOW"cowloop - split read " - "cow partlen=%ld off=%lld\n", partlen, offset); - - if (cowlo_readcow(cowdev, buf, partlen, offset) <= 0) - rv = 0; - } else { - /* - ** read (partial) block from rdofile - */ - DEBUGP(DCOW"cowloop - split read " - "rdo partlen=%ld off=%lld\n", partlen, offset); - - if (cowlo_readrdo(cowdev, buf, partlen, offset) <= 0) - rv = 0; - } - } - - return rv; -} - -/* -** chunk to be written to the cowfile needs pieces to be -** read from the rdofile -** -** returns: -** 0 - fail -** 1 - success -*/ -static int -cowlo_writemix(struct cowloop_device *cowdev, void *buf, int len, loff_t offset) -{ - unsigned long mapnum, bytenum, bitnum, blocknr, partlen; - long int rv; - char *mc; - - /* - ** somewhat more complicated stuff is required: - ** if the request is larger than one underlying - ** block or is spread over two underlying blocks, - ** split the request into pieces; if a block does not - ** start at a block boundary, take care that - ** surrounding data is read first (if needed), - ** fit the new data in and write it as a full block - */ - for (rv=1; len > 0; len-=partlen, buf+=partlen, offset+=partlen) { - /* - ** calculate partial length for this transfer - */ - partlen = MAPUNIT - (offset & MUMASK); - if (partlen > len) - partlen = len; - - /* - ** calculate blocknr of entire block - */ - blocknr = offset >> MUSHIFT; - - /* - ** has this block been written before? - */ - mapnum = CALCMAP (blocknr); - bytenum = CALCBYTE(blocknr); - bitnum = CALCBIT (blocknr); - mc = *(cowdev->mapcache+mapnum); - - if (*(mc+bytenum)&(1<<bitnum)) { - /* - ** block has been written before; - ** write transparantly to cowfile - */ - DEBUGP(DCOW - "cowloop - splitwr transp\n"); - - if (cowlo_writecow(cowdev, buf, partlen, offset) <= 0) - rv = 0; - } else { - /* - ** block has never been written before, - ** so read entire block from - ** read-only file first, unless - ** a full block is requested to - ** be written - */ - if (partlen < MAPUNIT) { - if (cowlo_readrdo(cowdev, cowdev->iobuf, - MAPUNIT, (loff_t)blocknr << MUSHIFT) <= 0) - rv = 0; - } - - /* - ** transfer modified part into - ** the block just read - */ - memcpy(cowdev->iobuf + (offset & MUMASK), buf, partlen); - - /* - ** write entire block to cowfile - */ - DEBUGP(DCOW"cowloop - split " - "partlen=%ld off=%lld\n", - partlen, (loff_t)blocknr << MUSHIFT); - - if (cowlo_writecow(cowdev, cowdev->iobuf, MAPUNIT, - (loff_t)blocknr << MUSHIFT) <= 0) - rv = 0; - } - } - - return rv; -} - -/*****************************************************************************/ -/* I/O-support for read-only file and copy-on-write file */ -/*****************************************************************************/ - -/* -** read data from the read-only file -** -** return-value: similar to user-mode read -*/ -static long int -cowlo_readrdo(struct cowloop_device *cowdev, void *buf, int len, loff_t offset) -{ - long int rv; - mm_segment_t old_fs; - loff_t saveoffset = offset; - - DEBUGP(DCOW"cowloop - readrdo called\n"); - - old_fs = get_fs(); - set_fs( get_ds() ); - rv = cowdev->rdofp->f_op->read(cowdev->rdofp, buf, len, &offset); - set_fs(old_fs); - - if (rv < len) { - printk(KERN_WARNING "cowloop - read-failure %ld on rdofile" - "- offset=%lld len=%d\n", - rv, saveoffset, len); - } - - cowdev->rdoreads++; - return rv; -} - -/* -** read cowfile from a modified offset, i.e. skipping the bitmap and cowhead -** -** return-value: similar to user-mode read -*/ -static long int -cowlo_readcow(struct cowloop_device *cowdev, void *buf, int len, loff_t offset) -{ - DEBUGP(DCOW"cowloop - readcow called\n"); - - offset += cowdev->cowhead->doffset; - - return cowlo_readcowraw(cowdev, buf, len, offset); -} - -/* -** read cowfile from an absolute offset -** -** return-value: similar to user-mode read -*/ -static long int -cowlo_readcowraw(struct cowloop_device *cowdev, - void *buf, int len, loff_t offset) -{ - long int rv; - mm_segment_t old_fs; - loff_t saveoffset = offset; - - DEBUGP(DCOW"cowloop - readcowraw called\n"); - - /* - ** be sure that cowfile is opened for read-write - */ - if ( !(cowdev->state & COWCOWOPEN) ) { - printk(KERN_WARNING - "cowloop - read request from cowfile refused\n"); - - return -EBADF; - } - - /* - ** issue low level read - */ - old_fs = get_fs(); - set_fs( get_ds() ); - rv = cowdev->cowfp->f_op->read(cowdev->cowfp, buf, len, &offset); - set_fs(old_fs); - - if (rv < len) { - printk(KERN_WARNING - "cowloop - read-failure %ld on cowfile" - "- offset=%lld len=%d\n", rv, saveoffset, len); - } - - cowdev->cowreads++; - return rv; -} - -/* -** write cowfile from a modified offset, i.e. skipping the bitmap and cowhead -** -** if a block is written for the first time while its contents consists -** of binary zeroes only, the concerning bitmap is flushed to the cowfile -** -** return-value: similar to user-mode write -*/ -static long int -cowlo_writecow(struct cowloop_device *cowdev, void *buf, int len, loff_t offset) -{ - long int rv; - unsigned long mapnum=0, mapbyte=0, mapbit=0, cowblock=0, partlen; - char *tmpptr, *mapptr = NULL; - loff_t tmpoffset, mapoffset = 0; - - DEBUGP(DCOW"cowloop - writecow called\n"); - - /* - ** be sure that cowfile is opened for read-write - */ - if ( !(cowdev->state & COWRWCOWOPEN) ) { - printk(KERN_WARNING - "cowloop - Write request to cowfile refused\n"); - - return -EBADF; - } - - /* - ** write the entire block to the cowfile - */ - tmpoffset = offset + cowdev->cowhead->doffset; - - rv = cowlo_writecowraw(cowdev, buf, len, tmpoffset); - - /* - ** verify if enough space available on filesystem holding - ** the cowfile - ** - when the last write failed (might be caused by lack of space) - ** - when a watcher is active (to react adequatly) - ** - when the previous check indicated fs was almost full - ** - with regular intervals - */ - if ( (rv <= 0) || - (cowdev->state & COWWATCHDOG) || - (cowdev->blkavail / 2 < SPCDFLINTVL) || - (cowdev->cowwrites % SPCDFLINTVL == 0) ) { - struct kstatfs ks; - - if (vfs_statfs(cowdev->cowfp->f_dentry, &ks)==0){ - if (ks.f_bavail <= SPCMINBLK) { - switch (ks.f_bavail) { - case 0: - case 1: - case 2: - case 3: - printk(KERN_ALERT - "cowloop - " - "ALERT: cowfile full!\n"); - break; - - default: - printk(KERN_WARNING - "cowloop - cowfile almost " - "full (only %llu Kb free)\n", - (unsigned long long) - ks.f_bsize * ks.f_bavail /1024); - } - } - - cowdev->blktotal = ks.f_blocks; - cowdev->blkavail = ks.f_bavail; - - /* - ** wakeup watcher if threshold has been reached - */ - if ( (cowdev->state & COWWATCHDOG) && - (cowdev->watchthresh >= cowdev->blkavail) ) { - wake_up_interruptible(&cowdev->watchq); - } - } - } - - if (rv <= 0) - return rv; - - DEBUGP(DCOW"cowloop - block written\n"); - - /* - ** check if block(s) is/are written to the cowfile - ** for the first time; if so, adapt the bitmap - */ - for (; len > 0; len-=partlen, offset+=partlen, buf+=partlen) { - /* - ** calculate partial length for this transfer - */ - partlen = MAPUNIT - (offset & MUMASK); - if (partlen > len) - partlen = len; - - /* - ** calculate bitnr of written chunk of cowblock - */ - cowblock = offset >> MUSHIFT; - - mapnum = CALCMAP (cowblock); - mapbyte = CALCBYTE(cowblock); - mapbit = CALCBIT (cowblock); - - if (*(*(cowdev->mapcache+mapnum)+mapbyte) & (1<<mapbit)) - continue; /* already written before */ - - /* - ** if the block is written for the first time, - ** the corresponding bit should be set in the bitmap - */ - *(*(cowdev->mapcache+mapnum)+mapbyte) |= (1<<mapbit); - - cowdev->nrcowblocks++; - - DEBUGP(DCOW"cowloop - bitupdate blk=%ld map=%ld " - "byte=%ld bit=%ld\n", - cowblock, mapnum, mapbyte, mapbit); - - /* - ** check if the cowhead in the cowfile is currently - ** marked clean; if so, mark it dirty and flush it - */ - if ( !(cowdev->cowhead->flags &= COWDIRTY)) { - cowdev->cowhead->flags |= COWDIRTY; - - cowlo_writecowraw(cowdev, cowdev->cowhead, - MAPUNIT, (loff_t)0); - } - - /* - ** if the written datablock contained binary zeroes, - ** the bitmap block should be marked to be flushed to disk - ** (blocks containing all zeroes cannot be recovered by - ** the cowrepair-program later on if cowloop is not properly - ** removed via rmmod) - */ - if ( memcmp(buf, allzeroes, partlen) ) /* not all zeroes? */ - continue; /* no flush needed */ - - /* - ** calculate positions of bitmap block to be flushed - ** - pointer of bitmap block in memory - ** - offset of bitmap block in cowfile - */ - tmpptr = *(cowdev->mapcache+mapnum) + (mapbyte & (~MUMASK)); - tmpoffset = (loff_t) MAPUNIT + mapnum * MAPCHUNKSZ + - (mapbyte & (~MUMASK)); - - /* - ** flush a bitmap block at the moment that all bits have - ** been set in that block, i.e. at the moment that we - ** switch to another bitmap block - */ - if ( (mapoffset != 0) && (mapoffset != tmpoffset) ) { - if (cowlo_writecowraw(cowdev, mapptr, MAPUNIT, - mapoffset) < 0) { - printk(KERN_WARNING - "cowloop - write-failure on bitmap - " - "blk=%ld map=%ld byte=%ld bit=%ld\n", - cowblock, mapnum, mapbyte, mapbit); - } - - DEBUGP(DCOW"cowloop - bitmap blk written %lld\n", - mapoffset); - } - - /* - ** remember offset in cowfile and offset in memory - ** for bitmap to be flushed; flushing will be done - ** as soon as all updates in this bitmap block have - ** been done - */ - mapoffset = tmpoffset; - mapptr = tmpptr; - } - - /* - ** any new block written containing binary zeroes? - */ - if (mapoffset) { - if (cowlo_writecowraw(cowdev, mapptr, MAPUNIT, mapoffset) < 0) { - printk(KERN_WARNING - "cowloop - write-failure on bitmap - " - "blk=%ld map=%ld byte=%ld bit=%ld\n", - cowblock, mapnum, mapbyte, mapbit); - } - - DEBUGP(DCOW"cowloop - bitmap block written %lld\n", mapoffset); - } - - return rv; -} - -/* -** write cowfile from an absolute offset -** -** return-value: similar to user-mode write -*/ -static long int -cowlo_writecowraw(struct cowloop_device *cowdev, - void *buf, int len, loff_t offset) -{ - long int rv; - mm_segment_t old_fs; - loff_t saveoffset = offset; - - DEBUGP(DCOW"cowloop - writecowraw called\n"); - - /* - ** be sure that cowfile is opened for read-write - */ - if ( !(cowdev->state & COWRWCOWOPEN) ) { - printk(KERN_WARNING - "cowloop - write request to cowfile refused\n"); - - return -EBADF; - } - - /* - ** issue low level write - */ - old_fs = get_fs(); - set_fs( get_ds() ); - rv = cowdev->cowfp->f_op->write(cowdev->cowfp, buf, len, &offset); - set_fs(old_fs); - - if (rv < len) { - printk(KERN_WARNING - "cowloop - write-failure %ld on cowfile" - "- offset=%lld len=%d\n", rv, saveoffset, len); - } - - cowdev->cowwrites++; - return rv; -} - - -/* -** readproc-function: called when the corresponding /proc-file is read -*/ -static int -cowlo_readproc(char *buf, char **start, off_t pos, int cnt, int *eof, void *p) -{ - struct cowloop_device *cowdev = p; - - revision[sizeof revision - 3] = '\0'; - - return sprintf(buf, - " cowloop version: %9s\n\n" - " device state: %s%s%s%s\n" - " number of opens: %9d\n" - " pid of thread: %9d\n\n" - " read-only file: %9s\n" - " rdoreads: %9lu\n\n" - "copy-on-write file: %9s\n" - " state cowfile: %9s\n" - " bitmap-blocks: %9lu (of %d bytes)\n" - " cowblocks in use: %9lu (of %d bytes)\n" - " cowreads: %9lu\n" - " cowwrites: %9lu\n", - &revision[11], - - cowdev->state & COWDEVOPEN ? "devopen " : "", - cowdev->state & COWRWCOWOPEN ? "cowopenrw " : "", - cowdev->state & COWRDCOWOPEN ? "cowopenro " : "", - cowdev->state & COWWATCHDOG ? "watchdog " : "", - - cowdev->opencnt, - cowdev->pid, - cowdev->rdoname, - cowdev->rdoreads, - cowdev->cowname, - cowdev->cowhead->flags & COWDIRTY ? "dirty":"clean", - cowdev->mapsize >> MUSHIFT, MAPUNIT, - cowdev->nrcowblocks, MAPUNIT, - cowdev->cowreads, - cowdev->cowwrites); -} - -/*****************************************************************************/ -/* Setup and destroy cowdevices */ -/*****************************************************************************/ - -/* -** open and prepare a cowdevice (rdofile and cowfile) and allocate bitmaps -** -** returns: -** 0 - okay -** < 0 - error value -*/ -static int -cowlo_openpair(char *rdof, char *cowf, int autorecover, int minor) -{ - long int rv; - struct cowloop_device *cowdev = cowdevall[minor]; - struct kstatfs ks; - - down(&cowdevlock); - - /* - ** requested device exists? - */ - if (minor >= maxcows) { - up(&cowdevlock); - return -ENODEV; - } - - /* - ** requested device already assigned to cowdevice? - */ - if (cowdev->state & COWDEVOPEN) { - up(&cowdevlock); - return -EBUSY; - } - - /* - ** initialize administration - */ - memset(cowdev, 0, sizeof *cowdev); - - spin_lock_init (&cowdev->rqlock); - init_waitqueue_head(&cowdev->waitq); - init_waitqueue_head(&cowdev->watchq); - - /* - ** open the read-only file - */ - DEBUGP(DCOW"cowloop - call openrdo....\n"); - - if ( (rv = cowlo_openrdo(cowdev, rdof)) ) { - cowlo_undo_openrdo(cowdev); - up(&cowdevlock); - return rv; - } - - /* - ** open the cowfile - */ - DEBUGP(DCOW"cowloop - call opencow....\n"); - - if ( (rv = cowlo_opencow(cowdev, cowf, autorecover)) ) { - cowlo_undo_openrdo(cowdev); - cowlo_undo_opencow(cowdev); - up(&cowdevlock); - return rv; - } - - /* - ** administer total and available size of filesystem holding cowfile - */ - if (vfs_statfs(cowdev->cowfp->f_dentry, &ks)==0) { - cowdev->blksize = ks.f_bsize; - cowdev->blktotal = ks.f_blocks; - cowdev->blkavail = ks.f_bavail; - } else { - cowdev->blksize = 1024; /* avoid division by zero */ - } - - /* - ** flush the (recovered) bitmaps and cowhead to the cowfile - */ - DEBUGP(DCOW"cowloop - call cowsync....\n"); - - cowlo_sync(); - - /* - ** allocate gendisk for the cow device - */ - DEBUGP(DCOW"cowloop - alloc disk....\n"); - - if ((cowdev->gd = alloc_disk(1)) == NULL) { - printk(KERN_WARNING - "cowloop - unable to alloc_disk for cowloop\n"); - - cowlo_undo_openrdo(cowdev); - cowlo_undo_opencow(cowdev); - up(&cowdevlock); - return -ENOMEM; - } - - cowdev->gd->major = COWMAJOR; - cowdev->gd->first_minor = minor; - cowdev->gd->minors = 1; - cowdev->gd->fops = &cowlo_fops; - cowdev->gd->private_data = cowdev; - sprintf(cowdev->gd->disk_name, "%s%d", DEVICE_NAME, minor); - - /* in .5 Kb units */ - set_capacity(cowdev->gd, (cowdev->numblocks*(MAPUNIT/512))); - - DEBUGP(DCOW"cowloop - init request queue....\n"); - - if ((cowdev->rqueue = blk_init_queue(cowlo_request, &cowdev->rqlock)) - == NULL) { - printk(KERN_WARNING - "cowloop - unable to get request queue for cowloop\n"); - - del_gendisk(cowdev->gd); - cowlo_undo_openrdo(cowdev); - cowlo_undo_opencow(cowdev); - up(&cowdevlock); - return -EINVAL; - } - - blk_queue_logical_block_size(cowdev->rqueue, cowdev->blocksz); - cowdev->gd->queue = cowdev->rqueue; - - /* - ** start kernel thread to handle requests - */ - DEBUGP(DCOW"cowloop - kickoff daemon....\n"); - - cowdev->pid = kernel_thread((int (*)(void *))cowlo_daemon, cowdev, 0); - - /* - ** create a file below directory /proc/cow for this new cowdevice - */ - if (cowlo_procdir) { - char tmpname[64]; - - sprintf(tmpname, "%d", minor); - - create_proc_read_entry(tmpname, 0 , cowlo_procdir, - cowlo_readproc, cowdev); - } - - cowdev->state |= COWDEVOPEN; - - cowdev->rdoname = rdof; - cowdev->cowname = cowf; - - /* - ** enable the new disk; this triggers the first request! - */ - DEBUGP(DCOW"cowloop - call add_disk....\n"); - - add_disk(cowdev->gd); - - up(&cowdevlock); - return 0; -} - -/* -** close a cowdevice (pair of rdofile/cowfile) and release memory -** -** returns: -** 0 - okay -** < 0 - error value -*/ -static int -cowlo_closepair(struct cowloop_device *cowdev) -{ - int minor; - - down(&cowdevlock); - - /* - ** if cowdevice is not activated at all, refuse - */ - if ( !(cowdev->state & COWDEVOPEN) ) { - up(&cowdevlock); - return -ENODEV; - } - - /* - ** if this cowdevice is still open, refuse - */ - if (cowdev->opencnt > 0) { - up(&cowdevlock); - return -EBUSY; - } - - up(&cowdevlock); - - /* - ** wakeup watcher (if any) - */ - if (cowdev->state & COWWATCHDOG) { - cowdev->watchthresh = cowdev->blkavail; - wake_up_interruptible(&cowdev->watchq); - } - - /* - ** wakeup kernel-thread to be able to exit - ** and wait until it has exited - */ - cowdev->closedown = 1; - cowdev->qfilled = 1; - wake_up_interruptible(&cowdev->waitq); - - while (cowdev->pid) - schedule(); - - del_gendisk(cowdev->gd); /* revert the alloc_disk() */ - put_disk(cowdev->gd); /* revert the add_disk() */ - - if (cowlo_procdir) { - char tmpname[64]; - - for (minor = 0; minor < maxcows; minor++) { - if (cowdev == cowdevall[minor]) break; - } - sprintf(tmpname, "%d", minor); - - remove_proc_entry(tmpname, cowlo_procdir); - } - - blk_cleanup_queue(cowdev->rqueue); - - /* - ** release memory for filenames if these names have - ** been allocated dynamically - */ - if ( (cowdev->cowname) && (cowdev->cowname != cowfile)) - kfree(cowdev->cowname); - - if ( (cowdev->rdoname) && (cowdev->rdoname != rdofile)) - kfree(cowdev->rdoname); - - cowlo_undo_openrdo(cowdev); - cowlo_undo_opencow(cowdev); - - cowdev->state &= ~COWDEVOPEN; - - return 0; -} - -/* -** open the read-only file -** -** returns: -** 0 - okay -** < 0 - error value -*/ -static int -cowlo_openrdo(struct cowloop_device *cowdev, char *rdof) -{ - struct file *f; - struct inode *inode; - long int i, nrval; - - DEBUGP(DCOW"cowloop - openrdo called\n"); - - /* - ** open the read-only file - */ - if(*rdof == '\0') { - printk(KERN_ERR - "cowloop - specify name for read-only file\n\n"); - return -EINVAL; - } - - f = filp_open(rdof, O_RDONLY|O_LARGEFILE, 0); - - if ( (f == NULL) || IS_ERR(f) ) { - printk(KERN_ERR - "cowloop - open of rdofile %s failed\n", rdof); - return -EINVAL; - } - - cowdev->rdofp = f; - - inode = f->f_dentry->d_inode; - - if ( !S_ISREG(inode->i_mode) && !S_ISBLK(inode->i_mode) ) { - printk(KERN_ERR - "cowloop - %s not regular file or blockdev\n", rdof); - return -EINVAL; - } - - DEBUGP(DCOW"cowloop - determine size rdo....\n"); - - /* - ** determine block-size and total size of read-only file - */ - if (S_ISREG(inode->i_mode)) { - /* - ** read-only file is a regular file - */ - cowdev->blocksz = 512; /* other value fails */ - cowdev->numblocks = inode->i_size >> MUSHIFT; - - if (inode->i_size & MUMASK) { - printk(KERN_WARNING - "cowloop - rdofile %s truncated to multiple " - "of %d bytes\n", rdof, MAPUNIT); - } - - DEBUGP(DCOW"cowloop - RO=regular: numblocks=%d, blocksz=%d\n", - cowdev->numblocks, cowdev->blocksz); - } else { - /* - ** read-only file is a block device - */ - cowdev->belowdev = inode->i_bdev; - cowdev->belowgd = cowdev->belowdev->bd_disk; /* gendisk */ - - if (cowdev->belowdev->bd_part) { - cowdev->numblocks = cowdev->belowdev->bd_part->nr_sects - / (MAPUNIT/512); - } - - if (cowdev->belowgd) { - cowdev->belowq = cowdev->belowgd->queue; - - if (cowdev->numblocks == 0) { - cowdev->numblocks = get_capacity(cowdev->belowgd) - / (MAPUNIT/512); - } - } - - - if (cowdev->belowq) - cowdev->blocksz = queue_logical_block_size(cowdev->belowq); - - if (cowdev->blocksz == 0) - cowdev->blocksz = BLOCK_SIZE; /* default 2^10 */ - - DEBUGP(DCOW"cowloop - numblocks=%d, " - "blocksz=%d, belowgd=%p, belowq=%p\n", - cowdev->numblocks, cowdev->blocksz, - cowdev->belowgd, cowdev->belowq); - - DEBUGP(DCOW"cowloop - belowdev.bd_block_size=%d\n", - cowdev->belowdev->bd_block_size); - } - - if (cowdev->numblocks == 0) { - printk(KERN_ERR "cowloop - %s has no contents\n", rdof); - return -EINVAL; - } - - /* - ** reserve space in memory as generic I/O buffer - */ - cowdev->iobuf = kmalloc(MAPUNIT, GFP_KERNEL); - - if (!cowdev->iobuf) { - printk(KERN_ERR - "cowloop - cannot get space for buffer %d\n", MAPUNIT); - return -ENOMEM; - } - - DEBUGP(DCOW"cowloop - determine fingerprint rdo....\n"); - - /* - ** determine fingerprint for read-only file - ** calculate fingerprint from first four datablocks - ** which do not contain binary zeroes - */ - for (i=0, cowdev->fingerprint=0, nrval=0; - (nrval < 4)&&(i < cowdev->numblocks); i++) { - int j; - unsigned char cs; - - /* - ** read next block - */ - if (cowlo_readrdo(cowdev, cowdev->iobuf, MAPUNIT, - (loff_t)i << MUSHIFT) < 1) - break; - - /* - ** calculate fingerprint by adding all byte-values - */ - for (j=0, cs=0; j < MAPUNIT; j++) - cs += *(cowdev->iobuf+j); - - if (cs == 0) /* block probably contained zeroes */ - continue; - - /* - ** shift byte-value to proper place in final fingerprint - */ - cowdev->fingerprint |= cs << (nrval*8); - nrval++; - } - - return 0; -} - -/* -** undo memory allocs and file opens issued so far -** related to the read-only file -*/ -static void -cowlo_undo_openrdo(struct cowloop_device *cowdev) -{ - if(cowdev->iobuf); - kfree(cowdev->iobuf); - - if (cowdev->rdofp) - filp_close(cowdev->rdofp, 0); -} - -/* -** open the cowfile -** -** returns: -** 0 - okay -** < 0 - error value -*/ -static int -cowlo_opencow(struct cowloop_device *cowdev, char *cowf, int autorecover) -{ - long int i, rv; - int minor; - unsigned long nb; - struct file *f; - struct inode *inode; - loff_t offset; - struct cowloop_device *cowtmp; - - DEBUGP(DCOW"cowloop - opencow called\n"); - - /* - ** open copy-on-write file (read-write) - */ - if (cowf[0] == '\0') { - printk(KERN_ERR - "cowloop - specify name of copy-on-write file\n\n"); - return -EINVAL; - } - - f = filp_open(cowf, O_RDWR|O_LARGEFILE, 0600); - - if ( (f == NULL) || IS_ERR(f) ) { - /* - ** non-existing cowfile: try to create - */ - f = filp_open(cowf, O_RDWR|O_CREAT|O_LARGEFILE, 0600); - - if ( (f == NULL) || IS_ERR(f) ) { - printk(KERN_ERR - "cowloop - failed to open file %s for read-write\n\n", - cowf); - return -EINVAL; - } - } - - cowdev->cowfp = f; - - inode = f->f_dentry->d_inode; - - if (!S_ISREG(inode->i_mode)) { - printk(KERN_ERR "cowloop - %s is not regular file\n", cowf); - return -EINVAL; - } - - /* - ** check if this cowfile is already in use for another cowdevice - */ - for (minor = 0; minor < maxcows; minor++) { - - cowtmp = cowdevall[minor]; - - if ( !(cowtmp->state & COWDEVOPEN) ) - continue; - - if (cowtmp == cowdev) - continue; - - if (cowtmp->cowfp->f_dentry->d_inode == f->f_dentry->d_inode) { - printk(KERN_ERR - "cowloop - %s: already in use as cow\n", cowf); - return -EBUSY; - } - } - - /* - ** mark cowfile open for read-write - */ - cowdev->state |= COWRWCOWOPEN; - - /* - ** calculate size (in bytes) for total bitmap in cowfile; - ** when the size of the cowhead block is added, the start-offset - ** for the modified data blocks can be found - */ - nb = cowdev->numblocks; - - if (nb%8) /* transform #bits to #bytes */ - nb+=8; /* rounded if necessary */ - nb /= 8; - - if (nb & MUMASK) /* round up #bytes to MAPUNIT chunks */ - cowdev->mapsize = ( (nb>>MUSHIFT) +1) << MUSHIFT; - else - cowdev->mapsize = nb; - - /* - ** reserve space in memory for the cowhead - */ - cowdev->cowhead = kmalloc(MAPUNIT, GFP_KERNEL); - - if (!cowdev->cowhead) { - printk(KERN_ERR "cowloop - cannot get space for cowhead %d\n", - MAPUNIT); - return -ENOMEM; - } - - memset(cowdev->cowhead, 0, MAPUNIT); - - DEBUGP(DCOW"cowloop - prepare cowhead....\n"); - - /* - ** check if the cowfile exists or should be created - */ - if (inode->i_size != 0) { - /* - ** existing cowfile: read the cow head - */ - if (inode->i_size < MAPUNIT) { - printk(KERN_ERR - "cowloop - existing cowfile %s too small\n", - cowf); - return -EINVAL; - } - - cowlo_readcowraw(cowdev, cowdev->cowhead, MAPUNIT, (loff_t) 0); - - /* - ** verify if the existing file is really a cowfile - */ - if (cowdev->cowhead->magic != COWMAGIC) { - printk(KERN_ERR - "cowloop - cowfile %s has incorrect format\n", - cowf); - return -EINVAL; - } - - /* - ** verify the cowhead version of the cowfile - */ - if (cowdev->cowhead->version > COWVERSION) { - printk(KERN_ERR - "cowloop - cowfile %s newer than this driver\n", - cowf); - return -EINVAL; - } - - /* - ** make sure that this is not a packed cowfile - */ - if (cowdev->cowhead->flags & COWPACKED) { - printk(KERN_ERR - "cowloop - packed cowfile %s not accepted\n", cowf); - return -EINVAL; - } - - /* - ** verify if the cowfile has been properly closed - */ - if (cowdev->cowhead->flags & COWDIRTY) { - /* - ** cowfile was not properly closed; - ** check if automatic recovery is required - ** (actual recovery will be done later on) - */ - if (!autorecover) { - printk(KERN_ERR - "cowloop - cowfile %s is dirty " - "(not properly closed by rmmod?)\n", - cowf); - printk(KERN_ERR - "cowloop - run cowrepair or specify " - "'option=r' to recover\n"); - return -EINVAL; - } - } - - /* - ** verify if the cowfile is really related to this rdofile - */ - if (cowdev->cowhead->rdoblocks != cowdev->numblocks) { - printk(KERN_ERR - "cowloop - cowfile %s (size %lld) not related " - "to rdofile (size %lld)\n", - cowf, - (long long)cowdev->cowhead->rdoblocks <<MUSHIFT, - (long long)cowdev->numblocks <<MUSHIFT); - return -EINVAL; - } - - if (cowdev->cowhead->rdofingerprint != cowdev->fingerprint) { - printk(KERN_ERR - "cowloop - cowfile %s not related to rdofile " - " (fingerprint err - rdofile modified?)\n", cowf); - return -EINVAL; - } - } else { - /* - ** new cowfile: determine the minimal size (cowhead+bitmap) - */ - offset = (loff_t) MAPUNIT + cowdev->mapsize - 1; - - if ( cowlo_writecowraw(cowdev, "", 1, offset) < 1) { - printk(KERN_ERR - "cowloop - cannot set cowfile to size %lld\n", - offset+1); - return -EINVAL; - } - - /* - ** prepare new cowhead - */ - cowdev->cowhead->magic = COWMAGIC; - cowdev->cowhead->version = COWVERSION; - cowdev->cowhead->mapunit = MAPUNIT; - cowdev->cowhead->mapsize = cowdev->mapsize; - cowdev->cowhead->rdoblocks = cowdev->numblocks; - cowdev->cowhead->rdofingerprint = cowdev->fingerprint; - cowdev->cowhead->cowused = 0; - - /* - ** calculate start offset of data in cowfile, - ** rounded up to multiple of 4K to avoid - ** unnecessary disk-usage for written datablocks in - ** the sparsed cowfile on e.g. 4K filesystems - */ - cowdev->cowhead->doffset = - ((MAPUNIT+cowdev->mapsize+4095)>>12)<<12; - } - - cowdev->cowhead->flags = 0; - - DEBUGP(DCOW"cowloop - reserve space bitmap....\n"); - - /* - ** reserve space in memory for the entire bitmap and - ** fill it with the bitmap-data from disk; the entire - ** bitmap is allocated in several chunks because kmalloc - ** has restrictions regarding the allowed size per kmalloc - */ - cowdev->mapcount = (cowdev->mapsize+MAPCHUNKSZ-1)/MAPCHUNKSZ; - - /* - ** the size of every bitmap chunk will be MAPCHUNKSZ bytes, except for - ** the last bitmap chunk: calculate remaining size for this chunk - */ - if (cowdev->mapsize % MAPCHUNKSZ == 0) - cowdev->mapremain = MAPCHUNKSZ; - else - cowdev->mapremain = cowdev->mapsize % MAPCHUNKSZ; - - /* - ** allocate space to store all pointers for the bitmap-chunks - ** (initialize area with zeroes to allow proper undo) - */ - cowdev->mapcache = kmalloc(cowdev->mapcount * sizeof(char *), - GFP_KERNEL); - if (!cowdev->mapcache) { - printk(KERN_ERR - "cowloop - can not allocate space for bitmap ptrs\n"); - return -ENOMEM; - } - - memset(cowdev->mapcache, 0, cowdev->mapcount * sizeof(char *)); - - /* - ** allocate space to store the bitmap-chunks themselves - */ - for (i=0; i < cowdev->mapcount; i++) { - if (i < (cowdev->mapcount-1)) - *(cowdev->mapcache+i) = kmalloc(MAPCHUNKSZ, GFP_KERNEL); - else - *(cowdev->mapcache+i) = kmalloc(cowdev->mapremain, - GFP_KERNEL); - - if (*(cowdev->mapcache+i) == NULL) { - printk(KERN_ERR "cowloop - no space for bitmapchunk %ld" - " totmapsz=%ld, mapcnt=%d mapunit=%d\n", - i, cowdev->mapsize, cowdev->mapcount, - MAPUNIT); - return -ENOMEM; - } - } - - DEBUGP(DCOW"cowloop - read bitmap from cow....\n"); - - /* - ** read the entire bitmap from the cowfile into the in-memory cache; - ** count the number of blocks that are in use already - ** (statistical purposes) - */ - for (i=0, offset=MAPUNIT; i < cowdev->mapcount; - i++, offset+=MAPCHUNKSZ) { - unsigned long numbytes; - - if (i < (cowdev->mapcount-1)) - /* - ** full bitmap chunk - */ - numbytes = MAPCHUNKSZ; - else - /* - ** last bitmap chunk: might be partly filled - */ - numbytes = cowdev->mapremain; - - cowlo_readcowraw(cowdev, *(cowdev->mapcache+i), - numbytes, offset); - } - - /* - ** if the cowfile was dirty and automatic recovery is required, - ** reconstruct a proper bitmap in memory now - */ - if (cowdev->cowhead->flags & COWDIRTY) { - unsigned long long blocknum; - char databuf[MAPUNIT]; - unsigned long mapnum, mapbyte, mapbit; - - printk(KERN_NOTICE "cowloop - recover dirty cowfile %s....\n", - cowf); - - /* - ** read all data blocks - */ - for (blocknum=0, rv=1, offset=0; - cowlo_readcow(cowdev, databuf, MAPUNIT, offset) > 0; - blocknum++, offset += MAPUNIT) { - - /* - ** if this datablock contains real data (not binary - ** zeroes), set the corresponding bit in the bitmap - */ - if ( memcmp(databuf, allzeroes, MAPUNIT) == 0) - continue; - - mapnum = CALCMAP (blocknum); - mapbyte = CALCBYTE(blocknum); - mapbit = CALCBIT (blocknum); - - *(*(cowdev->mapcache+mapnum)+mapbyte) |= (1<<mapbit); - } - - printk(KERN_NOTICE "cowloop - cowfile recovery completed\n"); - } - - /* - ** count all bits set in the bitmaps for statistical purposes - */ - for (i=0, cowdev->nrcowblocks = 0; i < cowdev->mapcount; i++) { - long numbytes; - char *p; - - if (i < (cowdev->mapcount-1)) - numbytes = MAPCHUNKSZ; - else - numbytes = cowdev->mapremain; - - p = *(cowdev->mapcache+i); - - for (numbytes--; numbytes >= 0; numbytes--, p++) { - /* - ** for only eight checks the following construction - ** is faster than a loop-construction - */ - if ((*p) & 0x01) cowdev->nrcowblocks++; - if ((*p) & 0x02) cowdev->nrcowblocks++; - if ((*p) & 0x04) cowdev->nrcowblocks++; - if ((*p) & 0x08) cowdev->nrcowblocks++; - if ((*p) & 0x10) cowdev->nrcowblocks++; - if ((*p) & 0x20) cowdev->nrcowblocks++; - if ((*p) & 0x40) cowdev->nrcowblocks++; - if ((*p) & 0x80) cowdev->nrcowblocks++; - } - } - - /* - ** consistency-check for number of bits set in bitmap - */ - if ( !(cowdev->cowhead->flags & COWDIRTY) && - (cowdev->cowhead->cowused != cowdev->nrcowblocks) ) { - printk(KERN_ERR "cowloop - inconsistent cowfile admi\n"); - return -EINVAL; - } - - return 0; -} - -/* -** undo memory allocs and file opens issued so far -** related to the cowfile -*/ -static void -cowlo_undo_opencow(struct cowloop_device *cowdev) -{ - int i; - - if (cowdev->mapcache) { - for (i=0; i < cowdev->mapcount; i++) { - if (*(cowdev->mapcache+i) != NULL) - kfree( *(cowdev->mapcache+i) ); - } - - kfree(cowdev->mapcache); - } - - if (cowdev->cowhead) - kfree(cowdev->cowhead); - - if ( (cowdev->state & COWCOWOPEN) && (cowdev->cowfp) ) - filp_close(cowdev->cowfp, 0); - - /* - ** mark cowfile closed - */ - cowdev->state &= ~COWCOWOPEN; -} - -/* -** flush the entire bitmap and the cowhead (clean) to the cowfile -** -** must be called with the cowdevices-lock set -*/ -static void -cowlo_sync(void) -{ - int i, minor; - loff_t offset; - struct cowloop_device *cowdev; - - for (minor=0; minor < maxcows; minor++) { - cowdev = cowdevall[minor]; - if ( ! (cowdev->state & COWRWCOWOPEN) ) - continue; - - for (i=0, offset=MAPUNIT; i < cowdev->mapcount; - i++, offset += MAPCHUNKSZ) { - unsigned long numbytes; - - if (i < (cowdev->mapcount-1)) - /* - ** full bitmap chunk - */ - numbytes = MAPCHUNKSZ; - else - /* - ** last bitmap chunk: might be partly filled - */ - numbytes = cowdev->mapremain; - - DEBUGP(DCOW - "cowloop - flushing bitmap %2d (%3ld Kb)\n", - i, numbytes/1024); - - if (cowlo_writecowraw(cowdev, *(cowdev->mapcache+i), - numbytes, offset) < numbytes) { - break; - } - } - - /* - ** flush clean up-to-date cowhead to cowfile - */ - cowdev->cowhead->cowused = cowdev->nrcowblocks; - cowdev->cowhead->flags &= ~COWDIRTY; - - DEBUGP(DCOW "cowloop - flushing cowhead (%3d Kb)\n", - MAPUNIT/1024); - - cowlo_writecowraw(cowdev, cowdev->cowhead, MAPUNIT, (loff_t) 0); - } -} - -/*****************************************************************************/ -/* Module loading/unloading */ -/*****************************************************************************/ - -/* -** called during insmod/modprobe -*/ -static int __init -cowlo_init_module(void) -{ - int rv; - int minor, uptocows; - - revision[sizeof revision - 3] = '\0'; - - printk(KERN_NOTICE "cowloop - (C) 2009 ATComputing.nl - version: %s\n", &revision[11]); - printk(KERN_NOTICE "cowloop - info: www.ATComputing.nl/cowloop\n"); - - memset(allzeroes, 0, MAPUNIT); - - /* - ** Setup administration for all possible cowdevices. - ** Note that their minor numbers go from 0 to MAXCOWS-1 inclusive - ** and minor == MAXCOWS-1 is reserved for the control device. - */ - if ((maxcows < 1) || (maxcows > MAXCOWS)) { - printk(KERN_WARNING - "cowloop - maxcows exceeds maximum of %d\n", MAXCOWS); - - maxcows = DFLCOWS; - } - - /* allocate room for a table with a pointer to each cowloop_device: */ - if ( (cowdevall = kmalloc(maxcows * sizeof(struct cowloop_device *), - GFP_KERNEL)) == NULL) { - printk(KERN_WARNING - "cowloop - can not alloc table for %d devs\n", maxcows); - uptocows = 0; - rv = -ENOMEM; - goto error_out; - } - memset(cowdevall, 0, maxcows * sizeof(struct cowloop_device *)); - /* then hook an actual cowloop_device struct to each pointer: */ - for (minor=0; minor < maxcows; minor++) { - if ((cowdevall[minor] = kmalloc(sizeof(struct cowloop_device), - GFP_KERNEL)) == NULL) { - printk(KERN_WARNING - "cowloop - can not alloc admin-struct for dev no %d\n", minor); - - uptocows = minor; /* this is how far we got.... */ - rv = -ENOMEM; - goto error_out; - } - memset(cowdevall[minor], 0, sizeof(struct cowloop_device)); - } - uptocows = maxcows; /* we got all devices */ - - sema_init(&cowdevlock, 1); - - /* - ** register cowloop module - */ - if ( register_blkdev(COWMAJOR, DEVICE_NAME) < 0) { - printk(KERN_WARNING - "cowloop - unable to get major %d for cowloop\n", COWMAJOR); - rv = -EIO; - goto error_out; - } - - /* - ** create a directory below /proc to allocate a file - ** for each cowdevice that is allocated later on - */ - cowlo_procdir = proc_mkdir("cow", NULL); - - /* - ** check if a cowdevice has to be opened during insmod/modprobe; - ** two parameters should be specified then: rdofile= and cowfile= - */ - if( (rdofile[0] != '\0') && (cowfile[0] != '\0') ) { - char *po = option; - int wantrecover = 0; - - /* - ** check if automatic recovery is wanted - */ - while (*po) { - if (*po == 'r') { - wantrecover = 1; - break; - } - po++; - } - - /* - ** open new cowdevice with minor number 0 - */ - if ( (rv = cowlo_openpair(rdofile, cowfile, wantrecover, 0))) { - remove_proc_entry("cow", NULL); - unregister_blkdev(COWMAJOR, DEVICE_NAME); - goto error_out; - } - } else { - /* - ** check if only one parameter has been specified - */ - if( (rdofile[0] != '\0') || (cowfile[0] != '\0') ) { - printk(KERN_ERR - "cowloop - only one filename specified\n"); - remove_proc_entry("cow", NULL); - unregister_blkdev(COWMAJOR, DEVICE_NAME); - rv = -EINVAL; - goto error_out; - } - } - - /* - ** allocate fake disk as control channel to handle the requests - ** to activate and deactivate cowdevices dynamically - */ - if (!(cowctlgd = alloc_disk(1))) { - printk(KERN_WARNING - "cowloop - unable to alloc_disk for cowctl\n"); - - remove_proc_entry("cow", NULL); - (void) cowlo_closepair(cowdevall[0]); - unregister_blkdev(COWMAJOR, DEVICE_NAME); - rv = -ENOMEM; - goto error_out; - } - - spin_lock_init(&cowctlrqlock); - cowctlgd->major = COWMAJOR; - cowctlgd->first_minor = COWCTL; - cowctlgd->minors = 1; - cowctlgd->fops = &cowlo_fops; - cowctlgd->private_data = NULL; - /* the device has capacity 0, so there will be no q-requests */ - cowctlgd->queue = blk_init_queue(NULL, &cowctlrqlock); - sprintf(cowctlgd->disk_name, "cowctl"); - set_capacity(cowctlgd, 0); - - add_disk(cowctlgd); - - printk(KERN_NOTICE "cowloop - number of configured cowdevices: %d\n", - maxcows); - if (rdofile[0] != '\0') { - printk(KERN_NOTICE "cowloop - initialized on rdofile=%s\n", - rdofile); - } else { - printk(KERN_NOTICE "cowloop - initialized without rdofile yet\n"); - } - return 0; - -error_out: - for (minor=0; minor < uptocows ; minor++) { - kfree(cowdevall[minor]); - } - kfree(cowdevall); - return rv; -} - -/* -** called during rmmod -*/ -static void __exit -cowlo_cleanup_module(void) -{ - int minor; - - /* - ** flush bitmaps and cowheads to the cowfiles - */ - down(&cowdevlock); - cowlo_sync(); - up(&cowdevlock); - - /* - ** close all cowdevices - */ - for (minor=0; minor < maxcows; minor++) - (void) cowlo_closepair(cowdevall[minor]); - - unregister_blkdev(COWMAJOR, DEVICE_NAME); - - /* - ** get rid of /proc/cow and unregister the driver - */ - remove_proc_entry("cow", NULL); - - for (minor = 0; minor < maxcows; minor++) { - kfree(cowdevall[minor]); - } - kfree(cowdevall); - - del_gendisk(cowctlgd); /* revert the alloc_disk() */ - put_disk (cowctlgd); /* revert the add_disk() */ - blk_cleanup_queue(cowctlgd->queue); /* cleanup the empty queue */ - - printk(KERN_NOTICE "cowloop - unloaded\n"); -} - -module_init(cowlo_init_module); -module_exit(cowlo_cleanup_module); diff --git a/drivers/staging/cowloop/cowloop.h b/drivers/staging/cowloop/cowloop.h deleted file mode 100644 index bbd4a35ac667..000000000000 --- a/drivers/staging/cowloop/cowloop.h +++ /dev/null @@ -1,66 +0,0 @@ -/* -** DO NOT MODIFY THESE VALUES (would make old cowfiles unusable) -*/ -#define MAPUNIT 1024 /* blocksize for bit in bitmap */ -#define MUSHIFT 10 /* bitshift for bit in bitmap */ -#define MUMASK 0x3ff /* bitmask for bit in bitmap */ - -#define COWMAGIC 0x574f437f /* byte-swapped '7f C O W' */ -#define COWDIRTY 0x01 -#define COWPACKED 0x02 -#define COWVERSION 1 - -struct cowhead -{ - int magic; /* identifies a cowfile */ - short version; /* version of cowhead */ - short flags; /* flags indicating status */ - unsigned long mapunit; /* blocksize per bit in bitmap */ - unsigned long mapsize; /* total size of bitmap (bytes) */ - unsigned long doffset; /* start-offset datablocks in cow */ - unsigned long rdoblocks; /* size of related read-only file */ - unsigned long rdofingerprint; /* fingerprint of read-only file */ - unsigned long cowused; /* number of datablocks used in cow */ -}; - -#define COWDEVDIR "/dev/cow/" -#define COWDEVICE COWDEVDIR "%ld" -#define COWCONTROL COWDEVDIR "ctl" - -#define MAXCOWS 1024 -#define COWCTL (MAXCOWS-1) /* minor number of /dev/cow/ctl */ - -#define COWPROCDIR "/proc/cow/" -#define COWPROCFILE COWPROCDIR "%d" - -/* -** ioctl related stuff -*/ -#define ANYDEV ((unsigned long)-1) - -struct cowpair -{ - unsigned char *rdofile; /* pathname of the rdofile */ - unsigned char *cowfile; /* pathname of the cowfile */ - unsigned short rdoflen; /* length of rdofile pathname */ - unsigned short cowflen; /* length of cowfile pathname */ - unsigned long device; /* requested/returned device number */ -}; - -struct cowwatch -{ - int flags; /* request flags */ - unsigned long device; /* requested device number */ - unsigned long threshold; /* continue if free Kb < threshold */ - unsigned long totalkb; /* ret: total filesystem size (Kb) */ - unsigned long availkb; /* ret: free filesystem size (Kb) */ -}; - -#define WATCHWAIT 0x01 /* block until threshold reached */ - -#define COWSYNC _IO ('C', 1) -#define COWMKPAIR _IOW ('C', 2, struct cowpair) -#define COWRMPAIR _IOW ('C', 3, unsigned long) -#define COWWATCH _IOW ('C', 4, struct cowwatch) -#define COWCLOSE _IOW ('C', 5, unsigned long) -#define COWRDOPEN _IOW ('C', 6, unsigned long) diff --git a/drivers/staging/cpc-usb/Kconfig b/drivers/staging/cpc-usb/Kconfig deleted file mode 100644 index 2be0bc9c39d0..000000000000 --- a/drivers/staging/cpc-usb/Kconfig +++ /dev/null @@ -1,4 +0,0 @@ -config USB_CPC - tristate "CPC CAN USB driver" - depends on USB && PROC_FS - default n diff --git a/drivers/staging/cpc-usb/Makefile b/drivers/staging/cpc-usb/Makefile deleted file mode 100644 index 3f83170a8fab..000000000000 --- a/drivers/staging/cpc-usb/Makefile +++ /dev/null @@ -1,3 +0,0 @@ -obj-$(CONFIG_USB_CPC) += cpc-usb.o - -cpc-usb-y := cpc-usb_drv.o sja2m16c_2.o diff --git a/drivers/staging/cpc-usb/TODO b/drivers/staging/cpc-usb/TODO deleted file mode 100644 index 9b1752fb9cd7..000000000000 --- a/drivers/staging/cpc-usb/TODO +++ /dev/null @@ -1,10 +0,0 @@ -Things to do for this driver to get merged into the main portion of the -kernel: - - checkpatch cleanups - - sparse clean - - remove proc code - - tie into CAN socket interfaces if possible - - figure out sane userspace api - - use linux's error codes - -Send patches to Greg Kroah-Hartman <greg@kroah.com> diff --git a/drivers/staging/cpc-usb/cpc-usb_drv.c b/drivers/staging/cpc-usb/cpc-usb_drv.c deleted file mode 100644 index c5eca46996fb..000000000000 --- a/drivers/staging/cpc-usb/cpc-usb_drv.c +++ /dev/null @@ -1,1184 +0,0 @@ -/* - * CPC-USB CAN Interface Kernel Driver - * - * Copyright (C) 2004-2009 EMS Dr. Thomas Wuensche - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published - * by the Free Software Foundation; version 2 of the License. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ -#include <linux/kernel.h> -#include <linux/errno.h> -#include <linux/init.h> -#include <linux/slab.h> -#include <linux/vmalloc.h> -#include <linux/module.h> -#include <linux/poll.h> -#include <linux/smp_lock.h> -#include <linux/completion.h> -#include <asm/uaccess.h> -#include <linux/usb.h> - - -#include <linux/proc_fs.h> - -#include "cpc.h" - -#include "cpc_int.h" -#include "cpcusb.h" - -#include "sja2m16c.h" - -/* Version Information */ -#define DRIVER_AUTHOR "Sebastian Haas <haas@ems-wuensche.com>" -#define DRIVER_DESC "CPC-USB Driver for Linux Kernel 2.6" -#define DRIVER_VERSION CPC_DRIVER_VERSION - -MODULE_AUTHOR(DRIVER_AUTHOR); -MODULE_DESCRIPTION(DRIVER_DESC); -MODULE_VERSION(DRIVER_VERSION); -MODULE_LICENSE("GPL v2"); - -/* Define these values to match your devices */ -#define USB_CPCUSB_VENDOR_ID 0x12D6 - -#define USB_CPCUSB_M16C_PRODUCT_ID 0x0888 -#define USB_CPCUSB_LPC2119_PRODUCT_ID 0x0444 - -#define CPC_USB_PROC_DIR CPC_PROC_DIR "cpc-usb" - -static struct proc_dir_entry *procDir; -static struct proc_dir_entry *procEntry; - -/* Module parameters */ -static int debug; -module_param(debug, int, S_IRUGO); - -/* table of devices that work with this driver */ -static struct usb_device_id cpcusb_table[] = { - {USB_DEVICE(USB_CPCUSB_VENDOR_ID, USB_CPCUSB_M16C_PRODUCT_ID)}, - {USB_DEVICE(USB_CPCUSB_VENDOR_ID, USB_CPCUSB_LPC2119_PRODUCT_ID)}, - {} /* Terminating entry */ -}; - -MODULE_DEVICE_TABLE(usb, cpcusb_table); - -/* use to prevent kernel panic if driver is unloaded - * while a programm has still open the device - */ -DECLARE_WAIT_QUEUE_HEAD(rmmodWq); -atomic_t useCount; - -static CPC_USB_T *CPCUSB_Table[CPC_USB_CARD_CNT] = { 0 }; -static unsigned int CPCUsbCnt; - -/* prevent races between open() and disconnect() */ -static DECLARE_MUTEX(disconnect_sem); - -/* local function prototypes */ -static ssize_t cpcusb_read(struct file *file, char *buffer, size_t count, - loff_t *ppos); -static ssize_t cpcusb_write(struct file *file, const char *buffer, - size_t count, loff_t *ppos); -static unsigned int cpcusb_poll(struct file *file, poll_table * wait); -static int cpcusb_open(struct inode *inode, struct file *file); -static int cpcusb_release(struct inode *inode, struct file *file); - -static int cpcusb_probe(struct usb_interface *interface, - const struct usb_device_id *id); -static void cpcusb_disconnect(struct usb_interface *interface); - -static void cpcusb_read_bulk_callback(struct urb *urb); -static void cpcusb_write_bulk_callback(struct urb *urb); -static void cpcusb_read_interrupt_callback(struct urb *urb); - -static int cpcusb_setup_intrep(CPC_USB_T *card); - -static struct file_operations cpcusb_fops = { - /* - * The owner field is part of the module-locking - * mechanism. The idea is that the kernel knows - * which module to increment the use-counter of - * BEFORE it calls the device's open() function. - * This also means that the kernel can decrement - * the use-counter again before calling release() - * or should the open() function fail. - */ - .owner = THIS_MODULE, - - .read = cpcusb_read, - .write = cpcusb_write, - .poll = cpcusb_poll, - .open = cpcusb_open, - .release = cpcusb_release, -}; - -/* - * usb class driver info in order to get a minor number from the usb core, - * and to have the device registered with devfs and the driver core - */ -static struct usb_class_driver cpcusb_class = { - .name = "usb/cpc_usb%d", - .fops = &cpcusb_fops, - .minor_base = CPC_USB_BASE_MNR, -}; - -/* usb specific object needed to register this driver with the usb subsystem */ -static struct usb_driver cpcusb_driver = { - .name = "cpc-usb", - .probe = cpcusb_probe, - .disconnect = cpcusb_disconnect, - .id_table = cpcusb_table, -}; - -static int cpcusb_create_info_output(char *buf) -{ - int i = 0, j; - - for (j = 0; j < CPC_USB_CARD_CNT; j++) { - if (CPCUSB_Table[j]) { - CPC_USB_T *card = CPCUSB_Table[j]; - CPC_CHAN_T *chan = card->chan; - - /* MINOR CHANNELNO BUSNO SLOTNO */ - i += sprintf(&buf[i], "%d %s\n", chan->minor, - card->serialNumber); - } - } - - return i; -} - -static int cpcusb_proc_read_info(char *page, char **start, off_t off, - int count, int *eof, void *data) -{ - int len = cpcusb_create_info_output(page); - - if (len <= off + count) - *eof = 1; - *start = page + off; - len -= off; - if (len > count) - len = count; - if (len < 0) - len = 0; - - return len; -} - -/* - * Remove CPC-USB and cleanup - */ -static inline void cpcusb_delete(CPC_USB_T *card) -{ - if (card) { - if (card->chan) { - if (card->chan->buf) - vfree(card->chan->buf); - - if (card->chan->CPCWait_q) - kfree(card->chan->CPCWait_q); - - kfree(card->chan); - } - - CPCUSB_Table[card->idx] = NULL; - kfree(card); - } -} - -/* - * setup the interrupt IN endpoint of a specific CPC-USB device - */ -static int cpcusb_setup_intrep(CPC_USB_T *card) -{ - int retval = 0; - struct usb_endpoint_descriptor *ep; - - ep = &card->interface->altsetting[0].endpoint[card->num_intr_in].desc; - - card->intr_in_buffer[0] = 0; - card->free_slots = 15; /* initial size */ - - /* setup the urb */ - usb_fill_int_urb(card->intr_in_urb, card->udev, - usb_rcvintpipe(card->udev, card->num_intr_in), - card->intr_in_buffer, - sizeof(card->intr_in_buffer), - cpcusb_read_interrupt_callback, - card, - ep->bInterval); - - card->intr_in_urb->status = 0; /* needed! */ - - /* submit the urb */ - retval = usb_submit_urb(card->intr_in_urb, GFP_KERNEL); - - if (retval) - err("%s - failed submitting intr urb, error %d", __func__, - retval); - - return retval; -} - -static int cpcusb_open(struct inode *inode, struct file *file) -{ - CPC_USB_T *card = NULL; - struct usb_interface *interface; - int subminor; - int j, retval = 0; - - subminor = iminor(inode); - - /* prevent disconnects */ - down(&disconnect_sem); - - interface = usb_find_interface(&cpcusb_driver, subminor); - if (!interface) { - err("%s - error, can't find device for minor %d", - __func__, subminor); - retval = CPC_ERR_NO_INTERFACE_PRESENT; - goto exit_no_device; - } - - card = usb_get_intfdata(interface); - if (!card) { - retval = CPC_ERR_NO_INTERFACE_PRESENT; - goto exit_no_device; - } - - /* lock this device */ - down(&card->sem); - - /* increment our usage count for the driver */ - if (card->open) { - dbg("device already opened"); - retval = CPC_ERR_CHANNEL_ALREADY_OPEN; - goto exit_on_error; - } - - /* save our object in the file's private structure */ - file->private_data = card; - for (j = 0; j < CPC_USB_URB_CNT; j++) { - usb_fill_bulk_urb(card->urbs[j].urb, card->udev, - usb_rcvbulkpipe(card->udev, card->num_bulk_in), - card->urbs[j].buffer, card->urbs[j].size, - cpcusb_read_bulk_callback, card); - - retval = usb_submit_urb(card->urbs[j].urb, GFP_KERNEL); - - if (retval) { - err("%s - failed submitting read urb, error %d", - __func__, retval); - retval = CPC_ERR_TRANSMISSION_FAILED; - goto exit_on_error; - } - } - - info("%s - %d URB's submitted", __func__, j); - - ResetBuffer(card->chan); - - cpcusb_setup_intrep(card); - card->open = 1; - - atomic_inc(&useCount); - -exit_on_error: - /* unlock this device */ - up(&card->sem); - -exit_no_device: - up(&disconnect_sem); - - return retval; -} - -static unsigned int cpcusb_poll(struct file *file, poll_table * wait) -{ - CPC_USB_T *card = (CPC_USB_T *) file->private_data; - unsigned int retval = 0; - - if (!card) { - err("%s - device object lost", __func__); - return -EIO; - } - - poll_wait(file, card->chan->CPCWait_q, wait); - - if (IsBufferNotEmpty(card->chan) || !(card->present)) - retval |= (POLLIN | POLLRDNORM); - - if (card->free_slots) - retval |= (POLLOUT | POLLWRNORM); - - return retval; -} - -static int cpcusb_release(struct inode *inode, struct file *file) -{ - CPC_USB_T *card = (CPC_USB_T *) file->private_data; - int j, retval = 0; - - if (card == NULL) { - dbg("%s - object is NULL", __func__); - return CPC_ERR_NO_INTERFACE_PRESENT; - } - - /* lock our device */ - down(&card->sem); - - if (!card->open) { - dbg("%s - device not opened", __func__); - retval = CPC_ERR_NO_INTERFACE_PRESENT; - goto exit_not_opened; - } - - /* if device wasn't unplugged kill all urbs */ - if (card->present) { - /* kill read urbs */ - for (j = 0; j < CPC_USB_URB_CNT; j++) { - usb_kill_urb(card->urbs[j].urb); - } - - /* kill irq urb */ - usb_kill_urb(card->intr_in_urb); - - /* kill write urbs */ - for (j = 0; j < CPC_USB_URB_CNT; j++) { - if (atomic_read(&card->wrUrbs[j].busy)) { - usb_kill_urb(card->wrUrbs[j].urb); - wait_for_completion(&card->wrUrbs[j].finished); - } - } - } - - atomic_dec(&useCount); - - /* last process detached */ - if (atomic_read(&useCount) == 0) { - wake_up(&rmmodWq); - } - - if (!card->present && card->open) { - /* the device was unplugged before the file was released */ - up(&card->sem); - cpcusb_delete(card); - return 0; - } - - card->open = 0; - -exit_not_opened: - up(&card->sem); - - return 0; -} - -static ssize_t cpcusb_read(struct file *file, char *buffer, size_t count, - loff_t *ppos) -{ - CPC_USB_T *card = (CPC_USB_T *) file->private_data; - CPC_CHAN_T *chan; - int retval = 0; - - if (count < sizeof(CPC_MSG_T)) - return CPC_ERR_UNKNOWN; - - /* check if can read from the given address */ - if (!access_ok(VERIFY_WRITE, buffer, count)) - return CPC_ERR_UNKNOWN; - - /* lock this object */ - down(&card->sem); - - /* verify that the device wasn't unplugged */ - if (!card->present) { - up(&card->sem); - return CPC_ERR_NO_INTERFACE_PRESENT; - } - - if (IsBufferEmpty(card->chan)) { - retval = 0; - } else { - chan = card->chan; - -#if 0 - /* convert LPC2119 params back to SJA1000 params */ - if (card->deviceRevision >= 0x0200 - && chan->buf[chan->oidx].type == CPC_MSG_T_CAN_PRMS) { - LPC2119_TO_SJA1000_Params(&chan->buf[chan->oidx]); - } -#endif - - if (copy_to_user(buffer, &chan->buf[chan->oidx], count) != 0) { - retval = CPC_ERR_IO_TRANSFER; - } else { - chan->oidx = (chan->oidx + 1) % CPC_MSG_BUF_CNT; - chan->WnR = 1; - retval = sizeof(CPC_MSG_T); - } - } -/* spin_unlock_irqrestore(&card->slock, flags); */ - - /* unlock the device */ - up(&card->sem); - - return retval; -} - -#define SHIFT 1 -static inline void cpcusb_align_buffer_alignment(unsigned char *buf) -{ - /* CPC-USB uploads packed bytes. */ - CPC_MSG_T *cpc = (CPC_MSG_T *) buf; - unsigned int i; - - for (i = 0; i < cpc->length + (2 * sizeof(unsigned long)); i++) { - ((unsigned char *) &cpc->msgid)[1 + i] = - ((unsigned char *) &cpc->msgid)[1 + SHIFT + i]; - } -} - -static int cpc_get_buffer_count(CPC_CHAN_T *chan) -{ - /* check the buffer parameters */ - if (chan->iidx == chan->oidx) - return !chan->WnR ? CPC_MSG_BUF_CNT : 0; - else if (chan->iidx >= chan->oidx) - return (chan->iidx - chan->oidx) % CPC_MSG_BUF_CNT; - - return (chan->iidx + CPC_MSG_BUF_CNT - chan->oidx) % CPC_MSG_BUF_CNT; -} - -static ssize_t cpcusb_write(struct file *file, const char *buffer, - size_t count, loff_t *ppos) -{ - CPC_USB_T *card = (CPC_USB_T *) file->private_data; - CPC_USB_WRITE_URB_T *wrUrb = NULL; - - ssize_t bytes_written = 0; - int retval = 0; - int j; - - unsigned char *obuf = NULL; - unsigned char type = 0; - CPC_MSG_T *info = NULL; - - dbg("%s - entered minor %d, count = %zu, present = %d", - __func__, card->minor, count, card->present); - - if (count > sizeof(CPC_MSG_T)) - return CPC_ERR_UNKNOWN; - - /* check if can read from the given address */ - if (!access_ok(VERIFY_READ, buffer, count)) - return CPC_ERR_UNKNOWN; - - /* lock this object */ - down(&card->sem); - - /* verify that the device wasn't unplugged */ - if (!card->present) { - retval = CPC_ERR_NO_INTERFACE_PRESENT; - goto exit; - } - - /* verify that we actually have some data to write */ - if (count == 0) { - dbg("%s - write request of 0 bytes", __func__); - goto exit; - } - - if (card->free_slots <= 5) { - info = (CPC_MSG_T *) buffer; - - if (info->type != CPC_CMD_T_CLEAR_CMD_QUEUE - || card->free_slots <= 0) { - dbg("%s - send buffer full please try again %d", - __func__, card->free_slots); - retval = CPC_ERR_CAN_NO_TRANSMIT_BUF; - goto exit; - } - } - - /* Find a free write urb */ - for (j = 0; j < CPC_USB_URB_CNT; j++) { - if (!atomic_read(&card->wrUrbs[j].busy)) { - wrUrb = &card->wrUrbs[j]; /* remember found URB */ - atomic_set(&wrUrb->busy, 1); /* lock this URB */ - init_completion(&wrUrb->finished); /* init completion */ - dbg("WR URB no. %d started", j); - break; - } - } - - /* don't found write urb say error */ - if (!wrUrb) { - dbg("%s - no free send urb available", __func__); - retval = CPC_ERR_CAN_NO_TRANSMIT_BUF; - goto exit; - } - dbg("URB write req"); - - obuf = (unsigned char *) wrUrb->urb->transfer_buffer; - - /* copy the data from userspace into our transfer buffer; - * this is the only copy required. - */ - if (copy_from_user(&obuf[4], buffer, count) != 0) { - atomic_set(&wrUrb->busy, 0); /* release urb */ - retval = CPC_ERR_IO_TRANSFER; - goto exit; - } - - /* check if it is a DRIVER information message, so we can - * response to that message and not the USB - */ - info = (CPC_MSG_T *) &obuf[4]; - - bytes_written = 11 + info->length; - if (bytes_written >= wrUrb->size) { - retval = CPC_ERR_IO_TRANSFER; - goto exit; - } - - switch (info->type) { - case CPC_CMD_T_CLEAR_MSG_QUEUE: - ResetBuffer(card->chan); - break; - - case CPC_CMD_T_INQ_MSG_QUEUE_CNT: - retval = cpc_get_buffer_count(card->chan); - atomic_set(&wrUrb->busy, 0); - - goto exit; - - case CPC_CMD_T_INQ_INFO: - if (info->msg.info.source == CPC_INFOMSG_T_DRIVER) { - /* release urb cause we'll use it for driver - * information - */ - atomic_set(&wrUrb->busy, 0); - if (IsBufferFull(card->chan)) { - retval = CPC_ERR_IO_TRANSFER; - goto exit; - } - - /* it is a driver information request message and we have - * free rx slots to store the response - */ - type = info->msg.info.type; - info = &card->chan->buf[card->chan->iidx]; - - info->type = CPC_MSG_T_INFO; - info->msg.info.source = CPC_INFOMSG_T_DRIVER; - info->msg.info.type = type; - - switch (type) { - case CPC_INFOMSG_T_VERSION: - info->length = strlen(CPC_DRIVER_VERSION) + 2; - sprintf(info->msg.info.msg, "%s\n", - CPC_DRIVER_VERSION); - break; - - case CPC_INFOMSG_T_SERIAL: - info->length = strlen(CPC_DRIVER_SERIAL) + 2; - sprintf(info->msg.info.msg, "%s\n", - CPC_DRIVER_SERIAL); - break; - - default: - info->length = 2; - info->msg.info.type = - CPC_INFOMSG_T_UNKNOWN_TYPE; - } - - card->chan->WnR = 0; - card->chan->iidx = - (card->chan->iidx + 1) % CPC_MSG_BUF_CNT; - - retval = info->length; - goto exit; - } - break; - case CPC_CMD_T_CAN_PRMS: - /* Check the controller type. If it's the new CPC-USB, make sure if these are SJA1000 params */ - if (info->msg.canparams.cc_type != SJA1000 - && info->msg.canparams.cc_type != M16C_BASIC - && (card->productId == USB_CPCUSB_LPC2119_PRODUCT_ID - && info->msg.canparams.cc_type != SJA1000)) { - /* don't forget to release the urb */ - atomic_set(&wrUrb->busy, 0); - retval = CPC_ERR_WRONG_CONTROLLER_TYPE; - goto exit; - } - break; - } - - /* just convert the params if it is an old CPC-USB with M16C controller */ - if (card->productId == USB_CPCUSB_M16C_PRODUCT_ID) { - /* if it is a parameter message convert it from SJA1000 controller - * settings to M16C Basic controller settings - */ - SJA1000_TO_M16C_BASIC_Params((CPC_MSG_T *) &obuf[4]); - } - - /* don't forget the byte alignment */ - cpcusb_align_buffer_alignment(&obuf[4]); - - /* setup a the 4 byte header */ - obuf[0] = obuf[1] = obuf[2] = obuf[3] = 0; - - /* this urb was already set up, except for this write size */ - wrUrb->urb->transfer_buffer_length = bytes_written + 4; - - /* send the data out the bulk port */ - /* a character device write uses GFP_KERNEL, - unless a spinlock is held */ - retval = usb_submit_urb(wrUrb->urb, GFP_KERNEL); - if (retval) { - atomic_set(&wrUrb->busy, 0); /* release urb */ - err("%s - failed submitting write urb, error %d", - __func__, retval); - } else { - retval = bytes_written; - } - -exit: - /* unlock the device */ - up(&card->sem); - - dbg("%s - leaved", __func__); - - return retval; -} - -/* - * callback for interrupt IN urb - */ -static void cpcusb_read_interrupt_callback(struct urb *urb) -{ - CPC_USB_T *card = (CPC_USB_T *) urb->context; - int retval; - unsigned long flags; - - spin_lock_irqsave(&card->slock, flags); - - if (!card->present) { - spin_unlock_irqrestore(&card->slock, flags); - info("%s - no such device", __func__); - return; - } - - switch (urb->status) { - case 0: /* success */ - card->free_slots = card->intr_in_buffer[1]; - break; - case -ECONNRESET: - case -ENOENT: - case -ESHUTDOWN: - /* urb was killed */ - spin_unlock_irqrestore(&card->slock, flags); - dbg("%s - intr urb killed", __func__); - return; - default: - info("%s - nonzero urb status %d", __func__, urb->status); - break; - } - - retval = usb_submit_urb(urb, GFP_ATOMIC); - if (retval) { - err("%s - failed resubmitting intr urb, error %d", - __func__, retval); - } - - spin_unlock_irqrestore(&card->slock, flags); - wake_up_interruptible(card->chan->CPCWait_q); - - return; -} - -#define UN_SHIFT 1 -#define CPCMSG_HEADER_LEN_FIRMWARE 11 -static inline int cpcusb_unalign_and_copy_buffy(unsigned char *out, - unsigned char *in) -{ - unsigned int i, j; - - for (i = 0; i < 3; i++) - out[i] = in[i]; - - for (j = 0; j < (in[1] + (CPCMSG_HEADER_LEN_FIRMWARE - 3)); j++) - out[j + i + UN_SHIFT] = in[j + i]; - - return i + j; -} - -/* - * callback for bulk IN urb - */ -static void cpcusb_read_bulk_callback(struct urb *urb) -{ - CPC_USB_T *card = (CPC_USB_T *) urb->context; - CPC_CHAN_T *chan; - unsigned char *ibuf = urb->transfer_buffer; - int retval, msgCnt, start, again = 0; - unsigned long flags; - - if (!card) { - err("%s - device object lost", __func__); - return; - } - - spin_lock_irqsave(&card->slock, flags); - - if (!card->present) { - spin_unlock_irqrestore(&card->slock, flags); - info("%s - no such device", __func__); - return; - } - - switch (urb->status) { - case 0: /* success */ - break; - case -ECONNRESET: - case -ENOENT: - case -ESHUTDOWN: - /* urb was killed */ - spin_unlock_irqrestore(&card->slock, flags); - dbg("%s - read urb killed", __func__); - return; - default: - info("%s - nonzero urb status %d", __func__, urb->status); - break; - } - - if (urb->actual_length) { - msgCnt = ibuf[0] & ~0x80; - again = ibuf[0] & 0x80; - - /* we have a 4 byte header */ - start = 4; - chan = card->chan; - while (msgCnt) { - if (!(IsBufferFull(card->chan))) { - start += - cpcusb_unalign_and_copy_buffy((unsigned char *) - &chan->buf[chan->iidx], &ibuf[start]); - - if (start > urb->transfer_buffer_length) { - err("%d > %d", start, urb->transfer_buffer_length); - break; - } - - chan->WnR = 0; - chan->iidx = (chan->iidx + 1) % CPC_MSG_BUF_CNT; - msgCnt--; - } else { - break; - } - } - } - - usb_fill_bulk_urb(urb, card->udev, - usb_rcvbulkpipe(card->udev, card->num_bulk_in), - urb->transfer_buffer, - urb->transfer_buffer_length, - cpcusb_read_bulk_callback, card); - - retval = usb_submit_urb(urb, GFP_ATOMIC); - - if (retval) { - err("%s - failed resubmitting read urb, error %d", __func__, retval); - } - - spin_unlock_irqrestore(&card->slock, flags); - - wake_up_interruptible(card->chan->CPCWait_q); -} - -/* - * callback for bulk IN urb - */ -static void cpcusb_write_bulk_callback(struct urb *urb) -{ - CPC_USB_T *card = (CPC_USB_T *) urb->context; - unsigned long flags; - int j; - - spin_lock_irqsave(&card->slock, flags); - - /* find this urb */ - for (j = 0; j < CPC_USB_URB_CNT; j++) { - if (card->wrUrbs[j].urb == urb) { - dbg("URB found no. %d", j); - /* notify anyone waiting that the write has finished */ - complete(&card->wrUrbs[j].finished); - atomic_set(&card->wrUrbs[j].busy, 0); - break; - } - } - - switch (urb->status) { - case 0: /* success */ - break; - case -ECONNRESET: - case -ENOENT: - case -ESHUTDOWN: - /* urb was killed */ - spin_unlock_irqrestore(&card->slock, flags); - dbg("%s - write urb no. %d killed", __func__, j); - return; - default: - info("%s - nonzero urb status %d", __func__, urb->status); - break; - } - - spin_unlock_irqrestore(&card->slock, flags); - - wake_up_interruptible(card->chan->CPCWait_q); -} - -static inline int cpcusb_get_free_slot(void) -{ - int i; - - for (i = 0; i < CPC_USB_CARD_CNT; i++) { - if (!CPCUSB_Table[i]) - return i; - } - - return -1; -} - -/* - * probe function for new CPC-USB devices - */ -static int cpcusb_probe(struct usb_interface *interface, - const struct usb_device_id *id) -{ - CPC_USB_T *card = NULL; - CPC_CHAN_T *chan = NULL; - - struct usb_device *udev = interface_to_usbdev(interface); - struct usb_host_interface *iface_desc; - struct usb_endpoint_descriptor *endpoint; - - int i, j, retval = -ENOMEM, slot; - - slot = cpcusb_get_free_slot(); - if (slot < 0) { - info("No more devices supported"); - return -ENOMEM; - } - - /* allocate memory for our device state and initialize it */ - card = kzalloc(sizeof(CPC_USB_T), GFP_KERNEL); - if (!card) { - err("Out of memory"); - return -ENOMEM; - } - CPCUSB_Table[slot] = card; - - /* allocate and initialize the channel struct */ - card->chan = kmalloc(sizeof(CPC_CHAN_T), GFP_KERNEL); - if (!card->chan) { - kfree(card); - err("Out of memory"); - return -ENOMEM; - } - - chan = card->chan; - memset(chan, 0, sizeof(CPC_CHAN_T)); - ResetBuffer(chan); - - init_MUTEX(&card->sem); - spin_lock_init(&card->slock); - - card->udev = udev; - card->interface = interface; - if (udev->descriptor.iSerialNumber) { - usb_string(udev, udev->descriptor.iSerialNumber, card->serialNumber, - 128); - info("Serial %s", card->serialNumber); - } - - card->productId = udev->descriptor.idProduct; - info("Product %s", - card->productId == USB_CPCUSB_LPC2119_PRODUCT_ID ? - "CPC-USB/ARM7" : "CPC-USB/M16C"); - - /* set up the endpoint information */ - /* check out the endpoints */ - /* use only the first bulk-in and bulk-out endpoints */ - iface_desc = &interface->altsetting[0]; - for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) { - endpoint = &iface_desc->endpoint[i].desc; - - if (!card->num_intr_in && - (endpoint->bEndpointAddress & USB_DIR_IN) && - ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) - == USB_ENDPOINT_XFER_INT)) { - card->intr_in_urb = usb_alloc_urb(0, GFP_KERNEL); - card->num_intr_in = 1; - - if (!card->intr_in_urb) { - err("No free urbs available"); - goto error; - } - - dbg("intr_in urb %d", card->num_intr_in); - } - - if (!card->num_bulk_in && - (endpoint->bEndpointAddress & USB_DIR_IN) && - ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) - == USB_ENDPOINT_XFER_BULK)) { - card->num_bulk_in = 2; - for (j = 0; j < CPC_USB_URB_CNT; j++) { - card->urbs[j].size = endpoint->wMaxPacketSize; - card->urbs[j].urb = usb_alloc_urb(0, GFP_KERNEL); - if (!card->urbs[j].urb) { - err("No free urbs available"); - goto error; - } - card->urbs[j].buffer = - usb_buffer_alloc(udev, - card->urbs[j].size, - GFP_KERNEL, - &card->urbs[j].urb->transfer_dma); - if (!card->urbs[j].buffer) { - err("Couldn't allocate bulk_in_buffer"); - goto error; - } - } - info("%s - %d reading URB's allocated", - __func__, CPC_USB_URB_CNT); - } - - if (!card->num_bulk_out && - !(endpoint->bEndpointAddress & USB_DIR_IN) && - ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) - == USB_ENDPOINT_XFER_BULK)) { - - card->num_bulk_out = 2; - - for (j = 0; j < CPC_USB_URB_CNT; j++) { - card->wrUrbs[j].size = - endpoint->wMaxPacketSize; - card->wrUrbs[j].urb = - usb_alloc_urb(0, GFP_KERNEL); - if (!card->wrUrbs[j].urb) { - err("No free urbs available"); - goto error; - } - card->wrUrbs[j].buffer = usb_buffer_alloc(udev, - card->wrUrbs[j].size, GFP_KERNEL, - &card->wrUrbs[j].urb->transfer_dma); - - if (!card->wrUrbs[j].buffer) { - err("Couldn't allocate bulk_out_buffer"); - goto error; - } - - usb_fill_bulk_urb(card->wrUrbs[j].urb, udev, - usb_sndbulkpipe(udev, endpoint->bEndpointAddress), - card->wrUrbs[j].buffer, - card->wrUrbs[j].size, - cpcusb_write_bulk_callback, - card); - } - - info("%s - %d writing URB's allocated", __func__, CPC_USB_URB_CNT); - } - } - - if (!(card->num_bulk_in && card->num_bulk_out)) { - err("Couldn't find both bulk-in and bulk-out endpoints"); - goto error; - } - - /* allow device read, write and ioctl */ - card->present = 1; - - /* we can register the device now, as it is ready */ - usb_set_intfdata(interface, card); - retval = usb_register_dev(interface, &cpcusb_class); - - if (retval) { - /* something prevented us from registering this driver */ - err("Not able to get a minor for this device."); - usb_set_intfdata(interface, NULL); - goto error; - } - - card->chan->minor = card->minor = interface->minor; - - chan->buf = vmalloc(sizeof(CPC_MSG_T) * CPC_MSG_BUF_CNT); - if (chan->buf == NULL) { - err("Out of memory"); - retval = -ENOMEM; - goto error; - } - info("Allocated memory for %d messages (%lu kbytes)", - CPC_MSG_BUF_CNT, (long unsigned int)(sizeof(CPC_MSG_T) * CPC_MSG_BUF_CNT) / 1000); - memset(chan->buf, 0, sizeof(CPC_MSG_T) * CPC_MSG_BUF_CNT); - - ResetBuffer(chan); - - card->chan->CPCWait_q = kmalloc(sizeof(wait_queue_head_t), GFP_KERNEL); - if (!card->chan->CPCWait_q) { - err("Out of memory"); - retval = -ENOMEM; - goto error; - } - init_waitqueue_head(card->chan->CPCWait_q); - - CPCUSB_Table[slot] = card; - card->idx = slot; - CPCUsbCnt++; - - /* let the user know what node this device is now attached to */ - info("Device now attached to USB-%d", card->minor); - return 0; - -error: - for (j = 0; j < CPC_USB_URB_CNT; j++) { - if (card->urbs[j].buffer) { - usb_buffer_free(card->udev, card->urbs[j].size, - card->urbs[j].buffer, - card->urbs[j].urb->transfer_dma); - card->urbs[j].buffer = NULL; - } - if (card->urbs[j].urb) { - usb_free_urb(card->urbs[j].urb); - card->urbs[j].urb = NULL; - } - } - - cpcusb_delete(card); - return retval; -} - -/* - * called by the usb core when the device is removed from the system - */ -static void cpcusb_disconnect(struct usb_interface *interface) -{ - CPC_USB_T *card = NULL; - int minor, j; - - /* prevent races with open() */ - down(&disconnect_sem); - - card = usb_get_intfdata(interface); - usb_set_intfdata(interface, NULL); - - down(&card->sem); - - /* prevent device read, write and ioctl */ - card->present = 0; - - minor = card->minor; - - /* free all urbs and their buffers */ - for (j = 0; j < CPC_USB_URB_CNT; j++) { - /* terminate an ongoing write */ - if (atomic_read(&card->wrUrbs[j].busy)) { - usb_kill_urb(card->wrUrbs[j].urb); - wait_for_completion(&card->wrUrbs[j].finished); - } - usb_buffer_free(card->udev, card->wrUrbs[j].size, - card->wrUrbs[j].buffer, - card->wrUrbs[j].urb->transfer_dma); - usb_free_urb(card->wrUrbs[j].urb); - } - info("%d write URBs freed", CPC_USB_URB_CNT); - - /* free all urbs and their buffers */ - for (j = 0; j < CPC_USB_URB_CNT; j++) { - usb_buffer_free(card->udev, card->urbs[j].size, - card->urbs[j].buffer, - card->urbs[j].urb->transfer_dma); - usb_free_urb(card->urbs[j].urb); - } - info("%d read URBs freed", CPC_USB_URB_CNT); - usb_free_urb(card->intr_in_urb); - - /* give back our minor */ - usb_deregister_dev(interface, &cpcusb_class); - - up(&card->sem); - - /* if the device is opened, cpcusb_release will clean this up */ - if (!card->open) - cpcusb_delete(card); - else - wake_up_interruptible(card->chan->CPCWait_q); - - up(&disconnect_sem); - - CPCUsbCnt--; - info("USB-%d now disconnected", minor); -} - -static int __init CPCUsb_Init(void) -{ - int result, i; - - info(DRIVER_DESC " v" DRIVER_VERSION); - info("Build on " __DATE__ " at " __TIME__); - - for (i = 0; i < CPC_USB_CARD_CNT; i++) - CPCUSB_Table[i] = 0; - - /* register this driver with the USB subsystem */ - result = usb_register(&cpcusb_driver); - if (result) { - err("usb_register failed. Error number %d", result); - return result; - } - - procDir = proc_mkdir(CPC_USB_PROC_DIR, NULL); - if (!procDir) { - err("Could not create proc entry"); - } else { - procEntry = create_proc_read_entry("info", 0444, procDir, - cpcusb_proc_read_info, - NULL); - if (!procEntry) { - err("Could not create proc entry %s", CPC_USB_PROC_DIR "/info"); - remove_proc_entry(CPC_USB_PROC_DIR, NULL); - procDir = NULL; - } - } - - return 0; -} - -static void __exit CPCUsb_Exit(void) -{ - wait_event(rmmodWq, !atomic_read(&useCount)); - - /* deregister this driver with the USB subsystem */ - usb_deregister(&cpcusb_driver); - - if (procDir) { - if (procEntry) - remove_proc_entry("info", procDir); - remove_proc_entry(CPC_USB_PROC_DIR, NULL); - } -} - -module_init(CPCUsb_Init); -module_exit(CPCUsb_Exit); diff --git a/drivers/staging/cpc-usb/cpc.h b/drivers/staging/cpc-usb/cpc.h deleted file mode 100644 index b2fda5d14c1d..000000000000 --- a/drivers/staging/cpc-usb/cpc.h +++ /dev/null @@ -1,417 +0,0 @@ -/* - * CPC CAN Interface Definitions - * - * Copyright (C) 2000-2008 EMS Dr. Thomas Wuensche - * - * 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. - */ -#ifndef CPC_HEADER -#define CPC_HEADER - -/* - * the maximum length of the union members within a CPC_MSG - * this value can be defined by the customer, but has to be - * >= 64 bytes - * however, if not defined before, we set a length of 64 byte - */ -#if !defined(CPC_MSG_LEN) || (CPC_MSG_LEN < 64) -#undef CPC_MSG_LEN -#define CPC_MSG_LEN 64 -#endif - -/* - * Transmission of events from CPC interfaces to PC can be individually - * controlled per event type. Default state is: don't transmit - * Control values are constructed by bit-or of Subject and Action - * and passed to CPC_Control() - */ - -/* Control-Values for CPC_Control() Command Subject Selection */ -#define CONTR_CAN_Message 0x04 -#define CONTR_Busload 0x08 -#define CONTR_CAN_State 0x0C -#define CONTR_SendAck 0x10 -#define CONTR_Filter 0x14 -#define CONTR_CmdQueue 0x18 /* reserved, do not use */ -#define CONTR_BusError 0x1C - -/* Control Command Actions */ -#define CONTR_CONT_OFF 0 -#define CONTR_CONT_ON 1 -#define CONTR_SING_ON 2 -/* - * CONTR_SING_ON doesn't change CONTR_CONT_ON state, so it should be - * read as: transmit at least once - */ - -/* defines for confirmed request */ -#define DO_NOT_CONFIRM 0 -#define DO_CONFIRM 1 - -/* event flags */ -#define EVENT_READ 0x01 -#define EVENT_WRITE 0x02 - -/* - * Messages from CPC to PC contain a message object type field. - * The following message types are sent by CPC and can be used in - * handlers, others should be ignored. - */ -#define CPC_MSG_T_RESYNC 0 /* Normally to be ignored */ -#define CPC_MSG_T_CAN 1 /* CAN data frame */ -#define CPC_MSG_T_BUSLOAD 2 /* Busload message */ -#define CPC_MSG_T_STRING 3 /* Normally to be ignored */ -#define CPC_MSG_T_CONTI 4 /* Normally to be ignored */ -#define CPC_MSG_T_MEM 7 /* Normally not to be handled */ -#define CPC_MSG_T_RTR 8 /* CAN remote frame */ -#define CPC_MSG_T_TXACK 9 /* Send acknowledge */ -#define CPC_MSG_T_POWERUP 10 /* Power-up message */ -#define CPC_MSG_T_CMD_NO 11 /* Normally to be ignored */ -#define CPC_MSG_T_CAN_PRMS 12 /* Actual CAN parameters */ -#define CPC_MSG_T_ABORTED 13 /* Command aborted message */ -#define CPC_MSG_T_CANSTATE 14 /* CAN state message */ -#define CPC_MSG_T_RESET 15 /* used to reset CAN-Controller */ -#define CPC_MSG_T_XCAN 16 /* XCAN data frame */ -#define CPC_MSG_T_XRTR 17 /* XCAN remote frame */ -#define CPC_MSG_T_INFO 18 /* information strings */ -#define CPC_MSG_T_CONTROL 19 /* used for control of interface/driver behaviour */ -#define CPC_MSG_T_CONFIRM 20 /* response type for confirmed requests */ -#define CPC_MSG_T_OVERRUN 21 /* response type for overrun conditions */ -#define CPC_MSG_T_KEEPALIVE 22 /* response type for keep alive conditions */ -#define CPC_MSG_T_CANERROR 23 /* response type for bus error conditions */ -#define CPC_MSG_T_DISCONNECTED 24 /* response type for a disconnected interface */ -#define CPC_MSG_T_ERR_COUNTER 25 /* RX/TX error counter of CAN controller */ - -#define CPC_MSG_T_FIRMWARE 100 /* response type for USB firmware download */ - -/* - * Messages from the PC to the CPC interface contain a command field - * Most of the command types are wrapped by the library functions and have therefore - * normally not to be used. - * However, programmers who wish to circumvent the library and talk directly - * to the drivers (mainly Linux programmers) can use the following - * command types: - */ -#define CPC_CMD_T_CAN 1 /* CAN data frame */ -#define CPC_CMD_T_CONTROL 3 /* used for control of interface/driver behaviour */ -#define CPC_CMD_T_CAN_PRMS 6 /* set CAN parameters */ -#define CPC_CMD_T_CLEARBUF 8 /* clears input queue; this is depricated, use CPC_CMD_T_CLEAR_MSG_QUEUE instead */ -#define CPC_CMD_T_INQ_CAN_PARMS 11 /* inquire actual CAN parameters */ -#define CPC_CMD_T_FILTER_PRMS 12 /* set filter parameter */ -#define CPC_CMD_T_RTR 13 /* CAN remote frame */ -#define CPC_CMD_T_CANSTATE 14 /* CAN state message */ -#define CPC_CMD_T_XCAN 15 /* XCAN data frame */ -#define CPC_CMD_T_XRTR 16 /* XCAN remote frame */ -#define CPC_CMD_T_RESET 17 /* used to reset CAN-Controller */ -#define CPC_CMD_T_INQ_INFO 18 /* miscellanous information strings */ -#define CPC_CMD_T_OPEN_CHAN 19 /* open a channel */ -#define CPC_CMD_T_CLOSE_CHAN 20 /* close a channel */ -#define CPC_CMD_T_CNTBUF 21 /* this is depricated, use CPC_CMD_T_INQ_MSG_QUEUE_CNT instead */ -#define CPC_CMD_T_CAN_EXIT 200 /* exit the CAN (disable interrupts; reset bootrate; reset output_cntr; mode = 1) */ - -#define CPC_CMD_T_INQ_MSG_QUEUE_CNT CPC_CMD_T_CNTBUF /* inquires the count of elements in the message queue */ -#define CPC_CMD_T_INQ_ERR_COUNTER 25 /* request the CAN controllers error counter */ -#define CPC_CMD_T_CLEAR_MSG_QUEUE CPC_CMD_T_CLEARBUF /* clear CPC_MSG queue */ -#define CPC_CMD_T_CLEAR_CMD_QUEUE 28 /* clear CPC_CMD queue */ -#define CPC_CMD_T_FIRMWARE 100 /* reserved, must not be used */ -#define CPC_CMD_T_USB_RESET 101 /* reserved, must not be used */ -#define CPC_CMD_T_WAIT_NOTIFY 102 /* reserved, must not be used */ -#define CPC_CMD_T_WAIT_SETUP 103 /* reserved, must not be used */ -#define CPC_CMD_T_ABORT 255 /* Normally not to be used */ - -/* definitions for CPC_MSG_T_INFO information sources */ -#define CPC_INFOMSG_T_UNKNOWN_SOURCE 0 -#define CPC_INFOMSG_T_INTERFACE 1 -#define CPC_INFOMSG_T_DRIVER 2 -#define CPC_INFOMSG_T_LIBRARY 3 - -/* information types */ -#define CPC_INFOMSG_T_UNKNOWN_TYPE 0 -#define CPC_INFOMSG_T_VERSION 1 -#define CPC_INFOMSG_T_SERIAL 2 - -/* definitions for controller types */ -#define PCA82C200 1 /* Philips basic CAN controller, replaced by SJA1000 */ -#define SJA1000 2 /* Philips basic CAN controller */ -#define AN82527 3 /* Intel full CAN controller */ -#define M16C_BASIC 4 /* M16C controller running in basic CAN (not full CAN) mode */ - -/* channel open error codes */ -#define CPC_ERR_NO_FREE_CHANNEL -1 /* no more free space within the channel array */ -#define CPC_ERR_CHANNEL_ALREADY_OPEN -2 /* the channel is already open */ -#define CPC_ERR_CHANNEL_NOT_ACTIVE -3 /* access to a channel not active failed */ -#define CPC_ERR_NO_DRIVER_PRESENT -4 /* no driver at the location searched by the library */ -#define CPC_ERR_NO_INIFILE_PRESENT -5 /* the library could not find the inifile */ -#define CPC_ERR_WRONG_PARAMETERS -6 /* wrong parameters in the inifile */ -#define CPC_ERR_NO_INTERFACE_PRESENT -7 /* 1. The specified interface is not connected */ - /* 2. The interface (mostly CPC-USB) was disconnected upon operation */ -#define CPC_ERR_NO_MATCHING_CHANNEL -8 /* the driver couldn't find a matching channel */ -#define CPC_ERR_NO_BUFFER_AVAILABLE -9 /* the driver couldn't allocate buffer for messages */ -#define CPC_ERR_NO_INTERRUPT -10 /* the requested interrupt couldn't be claimed */ -#define CPC_ERR_NO_MATCHING_INTERFACE -11 /* no interface type related to this channel was found */ -#define CPC_ERR_NO_RESOURCES -12 /* the requested resources could not be claimed */ -#define CPC_ERR_SOCKET -13 /* error concerning TCP sockets */ - -/* init error codes */ -#define CPC_ERR_WRONG_CONTROLLER_TYPE -14 /* wrong CAN controller type within initialization */ -#define CPC_ERR_NO_RESET_MODE -15 /* the controller could not be set into reset mode */ -#define CPC_ERR_NO_CAN_ACCESS -16 /* the CAN controller could not be accessed */ - -/* transmit error codes */ -#define CPC_ERR_CAN_WRONG_ID -20 /* the provided CAN id is too big */ -#define CPC_ERR_CAN_WRONG_LENGTH -21 /* the provided CAN length is too long */ -#define CPC_ERR_CAN_NO_TRANSMIT_BUF -22 /* the transmit buffer was occupied */ -#define CPC_ERR_CAN_TRANSMIT_TIMEOUT -23 /* The message could not be sent within a */ - /* specified time */ - -/* other error codes */ -#define CPC_ERR_SERVICE_NOT_SUPPORTED -30 /* the requested service is not supported by the interface */ -#define CPC_ERR_IO_TRANSFER -31 /* a transmission error down to the driver occurred */ -#define CPC_ERR_TRANSMISSION_FAILED -32 /* a transmission error down to the interface occurred */ -#define CPC_ERR_TRANSMISSION_TIMEOUT -33 /* a timeout occurred within transmission to the interface */ -#define CPC_ERR_OP_SYS_NOT_SUPPORTED -35 /* the operating system is not supported */ -#define CPC_ERR_UNKNOWN -40 /* an unknown error ocurred (mostly IOCTL errors) */ - -#define CPC_ERR_LOADING_DLL -50 /* the library 'cpcwin.dll' could not be loaded */ -#define CPC_ERR_ASSIGNING_FUNCTION -51 /* the specified function could not be assigned */ -#define CPC_ERR_DLL_INITIALIZATION -52 /* the DLL was not initialized correctly */ -#define CPC_ERR_MISSING_LICFILE -55 /* the file containing the licenses does not exist */ -#define CPC_ERR_MISSING_LICENSE -56 /* a required license was not found */ - -/* CAN state bit values. Ignore any bits not listed */ -#define CPC_CAN_STATE_BUSOFF 0x80 -#define CPC_CAN_STATE_ERROR 0x40 - -/* Mask to help ignore undefined bits */ -#define CPC_CAN_STATE_MASK 0xc0 - -/* - * CAN-Message representation in a CPC_MS - * Message object type is CPC_MSG_T_CAN or CPC_MSG_T_RTR - * or CPC_MSG_T_XCAN or CPC_MSG_T_XRTR - */ -typedef struct CPC_CAN_MSG { - u32 id; - u8 length; - u8 msg[8]; -} CPC_CAN_MSG_T; - -/* representation of the CAN parameters for the PCA82C200 controller */ -typedef struct CPC_PCA82C200_PARAMS { - u8 acc_code; /* Acceptance-code for receive, Standard: 0 */ - u8 acc_mask; /* Acceptance-mask for receive, Standard: 0xff (everything) */ - u8 btr0; /* Bus-timing register 0 */ - u8 btr1; /* Bus-timing register 1 */ - u8 outp_contr; /* Output-control register */ -} CPC_PCA82C200_PARAMS_T; - -/* representation of the CAN parameters for the SJA1000 controller */ -typedef struct CPC_SJA1000_PARAMS { - u8 mode; /* enables single or dual acceptance filtering */ - u8 acc_code0; /* Acceptance-code for receive, Standard: 0 */ - u8 acc_code1; - u8 acc_code2; - u8 acc_code3; - u8 acc_mask0; /* Acceptance-mask for receive, Standard: 0xff (everything) */ - u8 acc_mask1; - u8 acc_mask2; - u8 acc_mask3; - u8 btr0; /* Bus-timing register 0 */ - u8 btr1; /* Bus-timing register 1 */ - u8 outp_contr; /* Output-control register */ -} CPC_SJA1000_PARAMS_T; - -/* - * representation of the CAN parameters for the M16C controller - * in basic CAN mode (means no full CAN) - */ -typedef struct CPC_M16C_BASIC_PARAMS { - u8 con0; - u8 con1; - u8 ctlr0; - u8 ctlr1; - u8 clk; - u8 acc_std_code0; - u8 acc_std_code1; - u8 acc_ext_code0; - u8 acc_ext_code1; - u8 acc_ext_code2; - u8 acc_ext_code3; - u8 acc_std_mask0; - u8 acc_std_mask1; - u8 acc_ext_mask0; - u8 acc_ext_mask1; - u8 acc_ext_mask2; - u8 acc_ext_mask3; -} CPC_M16C_BASIC_PARAMS_T; - -/* CAN params message representation */ -typedef struct CPC_CAN_PARAMS { - u8 cc_type; /* represents the controller type */ - union { - CPC_M16C_BASIC_PARAMS_T m16c_basic; - CPC_SJA1000_PARAMS_T sja1000; - CPC_PCA82C200_PARAMS_T pca82c200; - } cc_params; -} CPC_CAN_PARAMS_T; - -/* CHAN init params representation */ -typedef struct CPC_CHAN_PARAMS { - int fd; -} CPC_CHAN_PARAMS_T; - -/* CAN init params message representation */ -typedef struct CPC_INIT_PARAMS { - CPC_CHAN_PARAMS_T chanparams; - CPC_CAN_PARAMS_T canparams; -} CPC_INIT_PARAMS_T; - -/* structure for confirmed message handling */ -typedef struct CPC_CONFIRM { - u8 result; /* error code */ -} CPC_CONFIRM_T; - -/* structure for information requests */ -typedef struct CPC_INFO { - u8 source; /* interface, driver or library */ - u8 type; /* version or serial number */ - char msg[CPC_MSG_LEN - 2]; /* string holding the requested information */ -} CPC_INFO_T; - -/* - * OVERRUN - * In general two types of overrun may occur. - * A hardware overrun, where the CAN controller - * lost a message, because the interrupt was - * not handled before the next messgae comes in. - * Or a software overrun, where i.e. a received - * message could not be stored in the CPC_MSG - * buffer. - */ - -/* After a software overrun has occurred - * we wait until we have CPC_OVR_GAP slots - * free in the CPC_MSG buffer. - */ -#define CPC_OVR_GAP 10 - -/* - * Two types of software overrun may occur. - * A received CAN message or a CAN state event - * can cause an overrun. - * Note: A CPC_CMD which would normally store - * its result immediately in the CPC_MSG - * queue may fail, because the message queue is full. - * This will not generate an overrun message, but - * will halt command execution, until this command - * is able to store its message in the message queue. - */ -#define CPC_OVR_EVENT_CAN 0x01 -#define CPC_OVR_EVENT_CANSTATE 0x02 -#define CPC_OVR_EVENT_BUSERROR 0x04 - -/* - * If the CAN controller lost a message - * we indicate it with the highest bit - * set in the count field. - */ -#define CPC_OVR_HW 0x80 - -/* structure for overrun conditions */ -typedef struct { - u8 event; - u8 count; -} CPC_OVERRUN_T; - -/* - * CAN errors - * Each CAN controller type has different - * registers to record errors. - * Therefor a structure containing the specific - * errors is set up for each controller here - */ - -/* - * SJA1000 error structure - * see the SJA1000 datasheet for detailed - * explanation of the registers - */ -typedef struct CPC_SJA1000_CAN_ERROR { - u8 ecc; /* error capture code register */ - u8 rxerr; /* RX error counter register */ - u8 txerr; /* TX error counter register */ -} CPC_SJA1000_CAN_ERROR_T; - -/* - * M16C error structure - * see the M16C datasheet for detailed - * explanation of the registers - */ -typedef struct CPC_M16C_CAN_ERROR { - u8 tbd; /* to be defined */ -} CPC_M16C_CAN_ERROR_T; - -/* structure for CAN error conditions */ -#define CPC_CAN_ECODE_ERRFRAME 0x01 -typedef struct CPC_CAN_ERROR { - u8 ecode; - struct { - u8 cc_type; /* CAN controller type */ - union { - CPC_SJA1000_CAN_ERROR_T sja1000; - CPC_M16C_CAN_ERROR_T m16c; - } regs; - } cc; -} CPC_CAN_ERROR_T; - -/* - * Structure containing RX/TX error counter. - * This structure is used to request the - * values of the CAN controllers TX and RX - * error counter. - */ -typedef struct CPC_CAN_ERR_COUNTER { - u8 rx; - u8 tx; -} CPC_CAN_ERR_COUNTER_T; - -/* If this flag is set, transmissions from PC to CPC are protected against loss */ -#define CPC_SECURE_TO_CPC 0x01 - -/* If this flag is set, transmissions from CPC to PC are protected against loss */ -#define CPC_SECURE_TO_PC 0x02 - -/* If this flag is set, the CAN-transmit buffer is checked to be free before sending a message */ -#define CPC_SECURE_SEND 0x04 - -/* - * If this flag is set, the transmission complete flag is checked - * after sending a message - * THIS IS CURRENTLY ONLY IMPLEMENTED IN THE PASSIVE INTERFACE DRIVERS - */ -#define CPC_SECURE_TRANSMIT 0x08 - -/* main message type used between library and application */ -typedef struct CPC_MSG { - u8 type; /* type of message */ - u8 length; /* length of data within union 'msg' */ - u8 msgid; /* confirmation handle */ - u32 ts_sec; /* timestamp in seconds */ - u32 ts_nsec; /* timestamp in nano seconds */ - union { - u8 generic[CPC_MSG_LEN]; - CPC_CAN_MSG_T canmsg; - CPC_CAN_PARAMS_T canparams; - CPC_CONFIRM_T confirmation; - CPC_INFO_T info; - CPC_OVERRUN_T overrun; - CPC_CAN_ERROR_T error; - CPC_CAN_ERR_COUNTER_T err_counter; - u8 busload; - u8 canstate; - } msg; -} CPC_MSG_T; - -#endif /* CPC_HEADER */ diff --git a/drivers/staging/cpc-usb/cpc_int.h b/drivers/staging/cpc-usb/cpc_int.h deleted file mode 100644 index 38674e9690a0..000000000000 --- a/drivers/staging/cpc-usb/cpc_int.h +++ /dev/null @@ -1,83 +0,0 @@ -/* - * CPCLIB - * - * Copyright (C) 2000-2008 EMS Dr. Thomas Wuensche - * - * 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. - * - */ -#ifndef CPC_INT_H -#define CPC_INT_H - -#include <linux/wait.h> - -#define CPC_MSG_BUF_CNT 1500 - -#define CPC_PROC_DIR "driver/" - -#undef dbg -#undef err -#undef info - -/* Use our own dbg macro */ -#define dbg(format, arg...) do { if (debug) printk( KERN_INFO format "\n" , ## arg); } while (0) -#define err(format, arg...) do { printk( KERN_INFO "ERROR " format "\n" , ## arg); } while (0) -#define info(format, arg...) do { printk( KERN_INFO format "\n" , ## arg); } while (0) - -/* Macros help using of our buffers */ -#define IsBufferFull(x) (!(x)->WnR) && ((x)->iidx == (x)->oidx) -#define IsBufferEmpty(x) ((x)->WnR) && ((x)->iidx == (x)->oidx) -#define IsBufferNotEmpty(x) (!(x)->WnR) || ((x)->iidx != (x)->oidx) -#define ResetBuffer(x) do { (x)->oidx = (x)->iidx=0; (x)->WnR = 1; } while(0); - -#define CPC_BufWriteAllowed ((chan->oidx != chan->iidx) || chan->WnR) - -typedef void (*chan_write_byte_t) (void *chan, unsigned int reg, - unsigned char val); -typedef unsigned char (*chan_read_byte_t) (void *chan, unsigned int reg); - -typedef struct CPC_CHAN { - void __iomem * canBase; /* base address of SJA1000 */ - chan_read_byte_t read_byte; /* CAN controller read access routine */ - chan_write_byte_t write_byte; /* CAN controller write access routine */ - CPC_MSG_T *buf; /* buffer for CPC msg */ - unsigned int iidx; - unsigned int oidx; - unsigned int WnR; - unsigned int minor; - unsigned int locked; - unsigned int irqDisabled; - - unsigned char cpcCtrlCANMessage; - unsigned char cpcCtrlCANState; - unsigned char cpcCtrlBUSState; - - unsigned char controllerType; - - unsigned long ovrTimeSec; - unsigned long ovrTimeNSec; - unsigned long ovrLockedBuffer; - CPC_OVERRUN_T ovr; - - /* for debugging only */ - unsigned int handledIrqs; - unsigned int lostMessages; - - unsigned int sentStdCan; - unsigned int sentExtCan; - unsigned int sentStdRtr; - unsigned int sentExtRtr; - - unsigned int recvStdCan; - unsigned int recvExtCan; - unsigned int recvStdRtr; - unsigned int recvExtRtr; - - wait_queue_head_t *CPCWait_q; - - void *private; -} CPC_CHAN_T; - -#endif diff --git a/drivers/staging/cpc-usb/cpcusb.h b/drivers/staging/cpc-usb/cpcusb.h deleted file mode 100644 index 6bdf30be239f..000000000000 --- a/drivers/staging/cpc-usb/cpcusb.h +++ /dev/null @@ -1,86 +0,0 @@ -/* Header for CPC-USB Driver ******************** - * Copyright 1999, 2000, 2001 - * - * Company: EMS Dr. Thomas Wuensche - * Sonnenhang 3 - * 85304 Ilmmuenster - * Phone: +49-8441-490260 - * Fax: +49-8441-81860 - * email: support@ems-wuensche.com - * WWW: www.ems-wuensche.com - */ - -#ifndef CPCUSB_H -#define CPCUSB_H - -#undef err -#undef dbg -#undef info - -/* Use our own dbg macro */ -#define dbg(format, arg...) do { if (debug) printk(KERN_INFO "CPC-USB: " format "\n" , ## arg); } while (0) -#define info(format, arg...) do { printk(KERN_INFO "CPC-USB: " format "\n" , ## arg); } while (0) -#define err(format, arg...) do { printk(KERN_INFO "CPC-USB(ERROR): " format "\n" , ## arg); } while (0) - -#define CPC_USB_CARD_CNT 4 - -typedef struct CPC_USB_READ_URB { - unsigned char *buffer; /* the buffer to send data */ - size_t size; /* the size of the send buffer */ - struct urb *urb; /* the urb used to send data */ -} CPC_USB_READ_URB_T; - -typedef struct CPC_USB_WRITE_URB { - unsigned char *buffer; /* the buffer to send data */ - size_t size; /* the size of the send buffer */ - struct urb *urb; /* the urb used to send data */ - atomic_t busy; /* true if write urb is busy */ - struct completion finished; /* wait for the write to finish */ -} CPC_USB_WRITE_URB_T; - -#define CPC_USB_URB_CNT 10 - -typedef struct CPC_USB { - struct usb_device *udev; /* save off the usb device pointer */ - struct usb_interface *interface; /* the interface for this device */ - unsigned char minor; /* the starting minor number for this device */ - unsigned char num_ports; /* the number of ports this device has */ - int num_intr_in; /* number of interrupt in endpoints we have */ - int num_bulk_in; /* number of bulk in endpoints we have */ - int num_bulk_out; /* number of bulk out endpoints we have */ - - CPC_USB_READ_URB_T urbs[CPC_USB_URB_CNT]; - - unsigned char intr_in_buffer[4]; /* interrupt transfer buffer */ - struct urb *intr_in_urb; /* interrupt transfer urb */ - - CPC_USB_WRITE_URB_T wrUrbs[CPC_USB_URB_CNT]; - - int open; /* if the port is open or not */ - int present; /* if the device is not disconnected */ - struct semaphore sem; /* locks this structure */ - - int free_slots; /* free send slots of CPC-USB */ - int idx; - - spinlock_t slock; - - char serialNumber[128]; /* serial number */ - int productId; /* product id to differ between M16C and LPC2119 */ - CPC_CHAN_T *chan; -} CPC_USB_T; - -#define CPCTable CPCUSB_Table - -#define CPC_DRIVER_VERSION "0.724" -#define CPC_DRIVER_SERIAL "not applicable" - -#define OBUF_SIZE 255 /* 4096 */ - -/* read timeouts -- RD_NAK_TIMEOUT * RD_EXPIRE = Number of seconds */ -#define RD_NAK_TIMEOUT (10*HZ) /* Default number of X seconds to wait */ -#define RD_EXPIRE 12 /* Number of attempts to wait X seconds */ - -#define CPC_USB_BASE_MNR 0 /* CPC-USB start at minor 0 */ - -#endif diff --git a/drivers/staging/cpc-usb/sja2m16c.h b/drivers/staging/cpc-usb/sja2m16c.h deleted file mode 100644 index 654bd3fc91dc..000000000000 --- a/drivers/staging/cpc-usb/sja2m16c.h +++ /dev/null @@ -1,41 +0,0 @@ -#ifndef _SJA2M16C_H -#define _SJA2M16C_H - -#include "cpc.h" - -#define BAUDRATE_TOLERANCE_PERCENT 1 -#define SAMPLEPOINT_TOLERANCE_PERCENT 5 -#define SAMPLEPOINT_UPPER_LIMIT 88 - -/* M16C parameters */ -struct FIELD_C0CONR { - unsigned int brp:4; - unsigned int sam:1; - unsigned int pr:3; - unsigned int dummy:8; -}; -struct FIELD_C1CONR { - unsigned int ph1:3; - unsigned int ph2:3; - unsigned int sjw:2; - unsigned int dummy:8; -}; -typedef union C0CONR { - unsigned char c0con; - struct FIELD_C0CONR bc0con; -} C0CONR_T; -typedef union C1CONR { - unsigned char c1con; - struct FIELD_C1CONR bc1con; -} C1CONR_T; - -#define SJA_TSEG1 ((pParams->btr1 & 0x0f)+1) -#define SJA_TSEG2 (((pParams->btr1 & 0x70)>>4)+1) -#define SJA_BRP ((pParams->btr0 & 0x3f)+1) -#define SJA_SJW ((pParams->btr0 & 0xc0)>>6) -#define SJA_SAM ((pParams->btr1 & 0x80)>>7) -int baudrate_m16c(int clk, int brp, int pr, int ph1, int ph2); -int samplepoint_m16c(int brp, int pr, int ph1, int ph2); -int SJA1000_TO_M16C_BASIC_Params(CPC_MSG_T *pMsg); - -#endif diff --git a/drivers/staging/cpc-usb/sja2m16c_2.c b/drivers/staging/cpc-usb/sja2m16c_2.c deleted file mode 100644 index bf0230fb7780..000000000000 --- a/drivers/staging/cpc-usb/sja2m16c_2.c +++ /dev/null @@ -1,452 +0,0 @@ -/**************************************************************************** -* -* Copyright (c) 2003,2004 by EMS Dr. Thomas Wuensche -* -* - All rights reserved - -* -* This code is provided "as is" without warranty of any kind, either -* expressed or implied, including but not limited to the liability -* concerning the freedom from material defects, the fitness for parti- -* cular purposes or the freedom of proprietary rights of third parties. -* -***************************************************************************** -* Module name.: cpcusb -***************************************************************************** -* Include file: cpc.h -***************************************************************************** -* Project.....: Windows Driver Development Kit -* Filename....: sja2m16c.cpp -* Authors.....: (GU) Gerhard Uttenthaler -* (CS) Christian Schoett -***************************************************************************** -* Short descr.: converts baudrate between SJA1000 and M16C -***************************************************************************** -* Description.: handles the baudrate conversion from SJA1000 parameters to -* M16C parameters -***************************************************************************** -* Address : EMS Dr. Thomas Wuensche -* Sonnenhang 3 -* D-85304 Ilmmuenster -* Tel. : +49-8441-490260 -* Fax. : +49-8441-81860 -* email: support@ems-wuensche.com -***************************************************************************** -* History -***************************************************************************** -* Version Date Auth Remark -* -* 01.00 ?? GU - initial release -* 01.10 ?????????? CS - adapted to fit into the USB Windows driver -* 02.00 18.08.2004 GU - improved the baudrate calculating algorithm -* - implemented acceptance filtering -* 02.10 10.09.2004 CS - adapted to fit into the USB Windows driver -***************************************************************************** -* ToDo's -***************************************************************************** -*/ - -/****************************************************************************/ -/* I N C L U D E S -*/ -#include <linux/kernel.h> -#include <linux/errno.h> -#include <linux/init.h> -#include <linux/slab.h> -#include <linux/vmalloc.h> -#include <linux/module.h> -#include <linux/poll.h> -#include <linux/smp_lock.h> -#include <linux/completion.h> -#include <asm/uaccess.h> -#include <linux/usb.h> - -#include "cpc.h" -#include "cpc_int.h" -#include "cpcusb.h" - -#include "sja2m16c.h" - -/*********************************************************************/ -int baudrate_m16c(int clk, int brp, int pr, int ph1, int ph2) -{ - return (16000000 / (1 << clk)) / 2 / (brp + 1) / (1 + pr + 1 + - ph1 + 1 + ph2 + - 1); -} - - -/*********************************************************************/ -int samplepoint_m16c(int brp, int pr, int ph1, int ph2) -{ - return (100 * (1 + pr + 1 + ph1 + 1)) / (1 + pr + 1 + ph1 + 1 + - ph2 + 1); -} - - -/**************************************************************************** -* Function.....: SJA1000_TO_M16C_BASIC_Params -* -* Task.........: This routine converts SJA1000 CAN btr parameters into M16C -* parameters based on the sample point and the error. In -* addition it converts the acceptance filter parameters to -* suit the M16C parameters -* -* Parameters...: None -* -* Return values: None -* -* Comments.....: -***************************************************************************** -* History -***************************************************************************** -* 19.01.2005 CS - modifed the conversion of SJA1000 filter params into -* M16C params. Due to compatibility reasons with the -* older 82C200 CAN controller the SJA1000 -****************************************************************************/ -int SJA1000_TO_M16C_BASIC_Params(CPC_MSG_T * in) -{ - int sjaBaudrate; - int sjaSamplepoint; - int *baudrate_error; // BRP[0..15], PR[0..7], PH1[0..7], PH2[0..7] - int *samplepoint_error; // BRP[0..15], PR[0..7], PH1[0..7], PH2[0..7] - int baudrate_error_merk; - int clk, brp, pr, ph1, ph2; - int clk_merk, brp_merk, pr_merk, ph1_merk, ph2_merk; - int index; - unsigned char acc_code0, acc_code1, acc_code2, acc_code3; - unsigned char acc_mask0, acc_mask1, acc_mask2, acc_mask3; - CPC_MSG_T * out; - C0CONR_T c0con; - C1CONR_T c1con; - int tmpAccCode; - int tmpAccMask; - - // we have to convert the parameters into M16C parameters - CPC_SJA1000_PARAMS_T * pParams; - - // check if the type is CAN parameters and if we have to convert the given params - if (in->type != CPC_CMD_T_CAN_PRMS - || in->msg.canparams.cc_type != SJA1000) - return 0; - pParams = - (CPC_SJA1000_PARAMS_T *) & in->msg.canparams.cc_params.sja1000; - acc_code0 = pParams->acc_code0; - acc_code1 = pParams->acc_code1; - acc_code2 = pParams->acc_code2; - acc_code3 = pParams->acc_code3; - acc_mask0 = pParams->acc_mask0; - acc_mask1 = pParams->acc_mask1; - acc_mask2 = pParams->acc_mask2; - acc_mask3 = pParams->acc_mask3; - -#ifdef _DEBUG_OUTPUT_CAN_PARAMS - info("acc_code0: %2.2Xh\n", acc_code0); - info("acc_code1: %2.2Xh\n", acc_code1); - info("acc_code2: %2.2Xh\n", acc_code2); - info("acc_code3: %2.2Xh\n", acc_code3); - info("acc_mask0: %2.2Xh\n", acc_mask0); - info("acc_mask1: %2.2Xh\n", acc_mask1); - info("acc_mask2: %2.2Xh\n", acc_mask2); - info("acc_mask3: %2.2Xh\n", acc_mask3); - -#endif /* */ - if (! - (baudrate_error = - (int *) vmalloc(sizeof(int) * 16 * 8 * 8 * 8 * 5))) { - err("Could not allocate memory\n"); - return -3; - } - if (! - (samplepoint_error = - (int *) vmalloc(sizeof(int) * 16 * 8 * 8 * 8 * 5))) { - err("Could not allocate memory\n"); - vfree(baudrate_error); - return -3; - } - memset(baudrate_error, 0xff, sizeof(baudrate_error)); - memset(samplepoint_error, 0xff, sizeof(baudrate_error)); - sjaBaudrate = - 16000000 / 2 / SJA_BRP / (1 + SJA_TSEG1 + SJA_TSEG2); - sjaSamplepoint = - 100 * (1 + SJA_TSEG1) / (1 + SJA_TSEG1 + SJA_TSEG2); - if (sjaBaudrate == 0) { - vfree(baudrate_error); - vfree(samplepoint_error); - return -2; - } - -#ifdef _DEBUG_OUTPUT_CAN_PARAMS - info("\nStarting SJA CAN params\n"); - info("-------------------------\n"); - info("TS1 : %2.2Xh TS2 : %2.2Xh\n", SJA_TSEG1, SJA_TSEG2); - info("BTR0 : %2.2Xh BTR1: %2.2Xh\n", pParams->btr0, - pParams->btr1); - info("Baudrate: %d.%dkBaud\n", sjaBaudrate / 1000, - sjaBaudrate % 1000); - info("Sample P: 0.%d\n", sjaSamplepoint); - info("\n"); - -#endif /* */ - c0con.bc0con.sam = SJA_SAM; - c1con.bc1con.sjw = SJA_SJW; - - // calculate errors for all baudrates - index = 0; - for (clk = 0; clk < 5; clk++) { - for (brp = 0; brp < 16; brp++) { - for (pr = 0; pr < 8; pr++) { - for (ph1 = 0; ph1 < 8; ph1++) { - for (ph2 = 0; ph2 < 8; ph2++) { - baudrate_error[index] = - 100 * - abs(baudrate_m16c - (clk, brp, pr, ph1, - ph2) - - sjaBaudrate) / - sjaBaudrate; - samplepoint_error[index] = - abs(samplepoint_m16c - (brp, pr, ph1, - ph2) - - sjaSamplepoint); - -#if 0 - info - ("Baudrate : %d kBaud\n", - baudrate_m16c(clk, - brp, pr, - ph1, - ph2)); - info - ("Baudrate Error: %d\n", - baudrate_error - [index]); - info - ("Sample P Error: %d\n", - samplepoint_error - [index]); - info - ("clk : %d\n", - clk); - -#endif /* */ - index++; - } - } - } - } - } - - // mark all baudrate_error entries which are outer limits - index = 0; - for (clk = 0; clk < 5; clk++) { - for (brp = 0; brp < 16; brp++) { - for (pr = 0; pr < 8; pr++) { - for (ph1 = 0; ph1 < 8; ph1++) { - for (ph2 = 0; ph2 < 8; ph2++) { - if ((baudrate_error[index] - > - BAUDRATE_TOLERANCE_PERCENT) - || - (samplepoint_error - [index] > - SAMPLEPOINT_TOLERANCE_PERCENT) - || - (samplepoint_m16c - (brp, pr, ph1, - ph2) > - SAMPLEPOINT_UPPER_LIMIT)) - { - baudrate_error - [index] = -1; - } else - if (((1 + pr + 1 + - ph1 + 1 + ph2 + - 1) < 8) - || - ((1 + pr + 1 + - ph1 + 1 + ph2 + - 1) > 25)) { - baudrate_error - [index] = -1; - } - -#if 0 - else { - info - ("Baudrate : %d kBaud\n", - baudrate_m16c - (clk, brp, pr, - ph1, ph2)); - info - ("Baudrate Error: %d\n", - baudrate_error - [index]); - info - ("Sample P Error: %d\n", - samplepoint_error - [index]); - } - -#endif /* */ - index++; - } - } - } - } - } - - // find list of minimum of baudrate_error within unmarked entries - clk_merk = brp_merk = pr_merk = ph1_merk = ph2_merk = 0; - baudrate_error_merk = 100; - index = 0; - for (clk = 0; clk < 5; clk++) { - for (brp = 0; brp < 16; brp++) { - for (pr = 0; pr < 8; pr++) { - for (ph1 = 0; ph1 < 8; ph1++) { - for (ph2 = 0; ph2 < 8; ph2++) { - if (baudrate_error[index] - != -1) { - if (baudrate_error - [index] < - baudrate_error_merk) - { - baudrate_error_merk - = - baudrate_error - [index]; - brp_merk = - brp; - pr_merk = - pr; - ph1_merk = - ph1; - ph2_merk = - ph2; - clk_merk = - clk; - -#if 0 - info - ("brp: %2.2Xh pr: %2.2Xh ph1: %2.2Xh ph2: %2.2Xh\n", - brp, - pr, - ph1, - ph2); - info - ("Baudrate : %d kBaud\n", - baudrate_m16c - (clk, - brp, - pr, - ph1, - ph2)); - info - ("Baudrate Error: %d\n", - baudrate_error - [index]); - info - ("Sample P Error: %d\n", - samplepoint_error - [index]); - -#endif /* */ - } - } - index++; - } - } - } - } - } - if (baudrate_error_merk == 100) { - info("ERROR: Could not convert CAN init parameter\n"); - vfree(baudrate_error); - vfree(samplepoint_error); - return -1; - } - - // setting m16c CAN parameter - c0con.bc0con.brp = brp_merk; - c0con.bc0con.pr = pr_merk; - c1con.bc1con.ph1 = ph1_merk; - c1con.bc1con.ph2 = ph2_merk; - -#ifdef _DEBUG_OUTPUT_CAN_PARAMS - info("\nResulting M16C CAN params\n"); - info("-------------------------\n"); - info("clk : %2.2Xh\n", clk_merk); - info("ph1 : %2.2Xh ph2: %2.2Xh\n", c1con.bc1con.ph1 + 1, - c1con.bc1con.ph2 + 1); - info("pr : %2.2Xh brp: %2.2Xh\n", c0con.bc0con.pr + 1, - c0con.bc0con.brp + 1); - info("sjw : %2.2Xh sam: %2.2Xh\n", c1con.bc1con.sjw, - c0con.bc0con.sam); - info("co1 : %2.2Xh co0: %2.2Xh\n", c1con.c1con, c0con.c0con); - info("Baudrate: %d.%dBaud\n", - baudrate_m16c(clk_merk, c0con.bc0con.brp, c0con.bc0con.pr, - c1con.bc1con.ph1, c1con.bc1con.ph2) / 1000, - baudrate_m16c(clk_merk, c0con.bc0con.brp, c0con.bc0con.pr, - c1con.bc1con.ph1, c1con.bc1con.ph2) % 1000); - info("Sample P: 0.%d\n", - samplepoint_m16c(c0con.bc0con.brp, c0con.bc0con.pr, - c1con.bc1con.ph1, c1con.bc1con.ph2)); - info("\n"); - -#endif /* */ - out = in; - out->type = 6; - out->length = sizeof(CPC_M16C_BASIC_PARAMS_T) + 1; - out->msg.canparams.cc_type = M16C_BASIC; - out->msg.canparams.cc_params.m16c_basic.con0 = c0con.c0con; - out->msg.canparams.cc_params.m16c_basic.con1 = c1con.c1con; - out->msg.canparams.cc_params.m16c_basic.ctlr0 = 0x4C; - out->msg.canparams.cc_params.m16c_basic.ctlr1 = 0x00; - out->msg.canparams.cc_params.m16c_basic.clk = clk_merk; - out->msg.canparams.cc_params.m16c_basic.acc_std_code0 = - acc_code0; - out->msg.canparams.cc_params.m16c_basic.acc_std_code1 = acc_code1; - -// info("code0: 0x%2.2X, code1: 0x%2.2X\n", out->msg.canparams.cc_params.m16c_basic.acc_std_code0, out->msg.canparams.cc_params.m16c_basic.acc_std_code1); - tmpAccCode = (acc_code1 >> 5) + (acc_code0 << 3); - out->msg.canparams.cc_params.m16c_basic.acc_std_code0 = - (unsigned char) tmpAccCode; - out->msg.canparams.cc_params.m16c_basic.acc_std_code1 = - (unsigned char) (tmpAccCode >> 8); - -// info("code0: 0x%2.2X, code1: 0x%2.2X\n", out->msg.canparams.cc_params.m16c_basic.acc_std_code0, out->msg.canparams.cc_params.m16c_basic.acc_std_code1); - out->msg.canparams.cc_params.m16c_basic.acc_std_mask0 = - ~acc_mask0; - out->msg.canparams.cc_params.m16c_basic.acc_std_mask1 = - ~acc_mask1; - -// info("mask0: 0x%2.2X, mask1: 0x%2.2X\n", out->msg.canparams.cc_params.m16c_basic.acc_std_mask0, out->msg.canparams.cc_params.m16c_basic.acc_std_mask1); - tmpAccMask = ((acc_mask1) >> 5) + ((acc_mask0) << 3); - -// info("tmpAccMask: 0x%4.4X\n", tmpAccMask); - out->msg.canparams.cc_params.m16c_basic.acc_std_mask0 = - (unsigned char) ~tmpAccMask; - out->msg.canparams.cc_params.m16c_basic.acc_std_mask1 = - (unsigned char) ~(tmpAccMask >> 8); - -// info("mask0: 0x%2.2X, mask1: 0x%2.2X\n", out->msg.canparams.cc_params.m16c_basic.acc_std_mask0, out->msg.canparams.cc_params.m16c_basic.acc_std_mask1); - out->msg.canparams.cc_params.m16c_basic.acc_ext_code0 = - (unsigned char) tmpAccCode; - out->msg.canparams.cc_params.m16c_basic.acc_ext_code1 = - (unsigned char) (tmpAccCode >> 8); - out->msg.canparams.cc_params.m16c_basic.acc_ext_code2 = acc_code2; - out->msg.canparams.cc_params.m16c_basic.acc_ext_code3 = acc_code3; - out->msg.canparams.cc_params.m16c_basic.acc_ext_mask0 = - (unsigned char) ~tmpAccMask; - out->msg.canparams.cc_params.m16c_basic.acc_ext_mask1 = - (unsigned char) ~(tmpAccMask >> 8); - out->msg.canparams.cc_params.m16c_basic.acc_ext_mask2 = - ~acc_mask2; - out->msg.canparams.cc_params.m16c_basic.acc_ext_mask3 = - ~acc_mask3; - vfree(baudrate_error); - vfree(samplepoint_error); - return 0; -} - - diff --git a/drivers/staging/dst/dcore.c b/drivers/staging/dst/dcore.c index ac8577358ba0..c24e4e0367a2 100644 --- a/drivers/staging/dst/dcore.c +++ b/drivers/staging/dst/dcore.c @@ -102,7 +102,7 @@ static int dst_request(struct request_queue *q, struct bio *bio) struct dst_node *n = q->queuedata; int err = -EIO; - if (bio_empty_barrier(bio) && !q->prepare_discard_fn) { + if (bio_empty_barrier(bio) && !blk_queue_discard(q)) { /* * This is a dirty^Wnice hack, but if we complete this * operation with -EOPNOTSUPP like intended, XFS @@ -847,7 +847,7 @@ static dst_command_func dst_commands[] = { /* * Configuration parser. */ -static void cn_dst_callback(struct cn_msg *msg) +static void cn_dst_callback(struct cn_msg *msg, struct netlink_skb_parms *nsp) { struct dst_ctl *ctl; int err; @@ -855,6 +855,11 @@ static void cn_dst_callback(struct cn_msg *msg) struct dst_node *n = NULL, *tmp; unsigned int hash; + if (!cap_raised(nsp->eff_cap, CAP_SYS_ADMIN)) { + err = -EPERM; + goto out; + } + if (msg->len < sizeof(struct dst_ctl)) { err = -EBADMSG; goto out; diff --git a/drivers/staging/et131x/et1310_address_map.h b/drivers/staging/et131x/et1310_address_map.h index 6294d3814e72..2c3d65a622a7 100644 --- a/drivers/staging/et131x/et1310_address_map.h +++ b/drivers/staging/et131x/et1310_address_map.h @@ -223,7 +223,7 @@ typedef union _TXDMA_PR_NUM_DES_t { extern inline void add_10bit(u32 *v, int n) { - *v = INDEX10(*v + n); + *v = INDEX10(*v + n) | (*v & ET_DMA10_WRAP); } /* diff --git a/drivers/staging/et131x/et1310_rx.c b/drivers/staging/et131x/et1310_rx.c index 8f2e91fa0a86..10e21db57ac3 100644 --- a/drivers/staging/et131x/et1310_rx.c +++ b/drivers/staging/et131x/et1310_rx.c @@ -1177,12 +1177,20 @@ void et131x_handle_recv_interrupt(struct et131x_adapter *etdev) static inline u32 bump_fbr(u32 *fbr, u32 limit) { - u32 v = *fbr; - add_10bit(&v, 1); - if (v > limit) - v = (*fbr & ~ET_DMA10_MASK) ^ ET_DMA10_WRAP; - *fbr = v; - return v; + u32 v = *fbr; + v++; + /* This works for all cases where limit < 1024. The 1023 case + works because 1023++ is 1024 which means the if condition is not + taken but the carry of the bit into the wrap bit toggles the wrap + value correctly */ + if ((v & ET_DMA10_MASK) > limit) { + v &= ~ET_DMA10_MASK; + v ^= ET_DMA10_WRAP; + } + /* For the 1023 case */ + v &= (ET_DMA10_MASK|ET_DMA10_WRAP); + *fbr = v; + return v; } /** diff --git a/drivers/staging/go7007/Makefile b/drivers/staging/go7007/Makefile index d14ea84a01f6..1301caa7495d 100644 --- a/drivers/staging/go7007/Makefile +++ b/drivers/staging/go7007/Makefile @@ -32,8 +32,3 @@ endif EXTRA_CFLAGS += -Idrivers/media/dvb/frontends EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core - -# Ubuntu 8.04 has CONFIG_SND undefined, so include lum sound/config.h too -ifeq ($(CONFIG_SND),) -EXTRA_CFLAGS += -include sound/config.h -endif diff --git a/drivers/staging/go7007/s2250-board.c b/drivers/staging/go7007/s2250-board.c index 8c85a9c3665a..f4a6541c3e60 100644 --- a/drivers/staging/go7007/s2250-board.c +++ b/drivers/staging/go7007/s2250-board.c @@ -261,7 +261,7 @@ static int read_reg_fp(struct i2c_client *client, u16 addr, u16 *val) memset(buf, 0xcd, 6); usb = go->hpi_context; - if (down_interruptible(&usb->i2c_lock) != 0) { + if (mutex_lock_interruptible(&usb->i2c_lock) != 0) { printk(KERN_INFO "i2c lock failed\n"); kfree(buf); return -EINTR; @@ -270,7 +270,7 @@ static int read_reg_fp(struct i2c_client *client, u16 addr, u16 *val) kfree(buf); return -EFAULT; } - up(&usb->i2c_lock); + mutex_unlock(&usb->i2c_lock); *val = (buf[0] << 8) | buf[1]; kfree(buf); diff --git a/drivers/staging/go7007/s2250-loader.h b/drivers/staging/go7007/s2250-loader.h new file mode 100644 index 000000000000..b7c301af16cc --- /dev/null +++ b/drivers/staging/go7007/s2250-loader.h @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2005-2006 Micronas USA Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License (Version 2) as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. + */ + +#ifndef _S2250_LOADER_H_ +#define _S2250_LOADER_H_ + +extern int s2250loader_init(void); +extern void s2250loader_cleanup(void); + +#endif diff --git a/drivers/staging/hv/ChannelMgmt.h b/drivers/staging/hv/ChannelMgmt.h index a839d8fe6cec..fa973d86b624 100644 --- a/drivers/staging/hv/ChannelMgmt.h +++ b/drivers/staging/hv/ChannelMgmt.h @@ -26,6 +26,7 @@ #define _CHANNEL_MGMT_H_ #include <linux/list.h> +#include <linux/timer.h> #include "RingBuffer.h" #include "VmbusChannelInterface.h" #include "VmbusPacketFormat.h" @@ -54,7 +55,7 @@ enum vmbus_channel_message_type { ChannelMessageViewRangeRemove = 18, #endif ChannelMessageCount -} __attribute__((packed)); +}; struct vmbus_channel_message_header { enum vmbus_channel_message_type MessageType; diff --git a/drivers/staging/hv/NetVsc.c b/drivers/staging/hv/NetVsc.c index 1610b845198f..d384c0ddf069 100644 --- a/drivers/staging/hv/NetVsc.c +++ b/drivers/staging/hv/NetVsc.c @@ -1052,7 +1052,7 @@ static void NetVscOnReceive(struct hv_device *Device, */ spin_lock_irqsave(&netDevice->receive_packet_list_lock, flags); while (!list_empty(&netDevice->ReceivePacketList)) { - list_move_tail(&netDevice->ReceivePacketList, &listHead); + list_move_tail(netDevice->ReceivePacketList.next, &listHead); if (++count == vmxferpagePacket->RangeCount + 1) break; } @@ -1071,7 +1071,7 @@ static void NetVscOnReceive(struct hv_device *Device, /* Return it to the freelist */ spin_lock_irqsave(&netDevice->receive_packet_list_lock, flags); for (i = count; i != 0; i--) { - list_move_tail(&listHead, + list_move_tail(listHead.next, &netDevice->ReceivePacketList); } spin_unlock_irqrestore(&netDevice->receive_packet_list_lock, @@ -1085,8 +1085,7 @@ static void NetVscOnReceive(struct hv_device *Device, } /* Remove the 1st packet to represent the xfer page packet itself */ - xferpagePacket = list_entry(&listHead, struct xferpage_packet, - ListEntry); + xferpagePacket = (struct xferpage_packet*)listHead.next; list_del(&xferpagePacket->ListEntry); /* This is how much we can satisfy */ @@ -1102,8 +1101,7 @@ static void NetVscOnReceive(struct hv_device *Device, /* Each range represents 1 RNDIS pkt that contains 1 ethernet frame */ for (i = 0; i < (count - 1); i++) { - netvscPacket = list_entry(&listHead, struct hv_netvsc_packet, - ListEntry); + netvscPacket = (struct hv_netvsc_packet*)listHead.next; list_del(&netvscPacket->ListEntry); /* Initialize the netvsc packet */ diff --git a/drivers/staging/hv/TODO b/drivers/staging/hv/TODO index 4d390b237742..dbfbde937a66 100644 --- a/drivers/staging/hv/TODO +++ b/drivers/staging/hv/TODO @@ -1,11 +1,17 @@ TODO: - fix remaining checkpatch warnings and errors + - use of /** when it is not a kerneldoc header - remove RingBuffer.c to us in-kernel ringbuffer functions instead. - audit the vmbus to verify it is working properly with the driver model + - convert vmbus driver interface function pointer tables + to constant, a.k.a vmbus_ops - see if the vmbus can be merged with the other virtual busses in the kernel - audit the network driver + - use existing net_device_stats struct in network device + - checking for carrier inside open is wrong, network device API + confusion?? - audit the block driver - audit the scsi driver diff --git a/drivers/staging/hv/osd.c b/drivers/staging/hv/osd.c index 8fe543bd9910..3a4793a0fd05 100644 --- a/drivers/staging/hv/osd.c +++ b/drivers/staging/hv/osd.c @@ -30,6 +30,7 @@ #include <linux/ioport.h> #include <linux/irq.h> #include <linux/interrupt.h> +#include <linux/sched.h> #include <linux/wait.h> #include <linux/spinlock.h> #include <linux/workqueue.h> diff --git a/drivers/staging/hv/osd.h b/drivers/staging/hv/osd.h index 9504604c72bd..ce064e8ea644 100644 --- a/drivers/staging/hv/osd.h +++ b/drivers/staging/hv/osd.h @@ -25,6 +25,7 @@ #ifndef _OSD_H_ #define _OSD_H_ +#include <linux/workqueue.h> /* Defines */ #define ALIGN_UP(value, align) (((value) & (align-1)) ? \ diff --git a/drivers/staging/hv/vmbus_drv.c b/drivers/staging/hv/vmbus_drv.c index 582318f10222..894eecfc63ca 100644 --- a/drivers/staging/hv/vmbus_drv.c +++ b/drivers/staging/hv/vmbus_drv.c @@ -507,12 +507,12 @@ static struct hv_device *vmbus_child_device_create(struct hv_guid *type, child_device_obj = &child_device_ctx->device_obj; child_device_obj->context = context; - memcpy(&child_device_obj->deviceType, &type, sizeof(struct hv_guid)); - memcpy(&child_device_obj->deviceInstance, &instance, + memcpy(&child_device_obj->deviceType, type, sizeof(struct hv_guid)); + memcpy(&child_device_obj->deviceInstance, instance, sizeof(struct hv_guid)); - memcpy(&child_device_ctx->class_id, &type, sizeof(struct hv_guid)); - memcpy(&child_device_ctx->device_id, &instance, sizeof(struct hv_guid)); + memcpy(&child_device_ctx->class_id, type, sizeof(struct hv_guid)); + memcpy(&child_device_ctx->device_id, instance, sizeof(struct hv_guid)); DPRINT_EXIT(VMBUS_DRV); @@ -537,18 +537,7 @@ static int vmbus_child_device_register(struct hv_device *root_device_obj, DPRINT_DBG(VMBUS_DRV, "child device (%p) registering", child_device_ctx); - /* Make sure we are not registered already */ - if (strlen(dev_name(&child_device_ctx->device)) != 0) { - DPRINT_ERR(VMBUS_DRV, - "child device (%p) already registered - busid %s", - child_device_ctx, - dev_name(&child_device_ctx->device)); - - ret = -1; - goto Cleanup; - } - - /* Set the device bus id. Otherwise, device_register()will fail. */ + /* Set the device name. Otherwise, device_register() will fail. */ dev_set_name(&child_device_ctx->device, "vmbus_0_%d", atomic_inc_return(&device_num)); @@ -573,7 +562,6 @@ static int vmbus_child_device_register(struct hv_device *root_device_obj, DPRINT_INFO(VMBUS_DRV, "child device (%p) registered", &child_device_ctx->device); -Cleanup: DPRINT_EXIT(VMBUS_DRV); return ret; @@ -623,8 +611,6 @@ static void vmbus_child_device_destroy(struct hv_device *device_obj) static int vmbus_uevent(struct device *device, struct kobj_uevent_env *env) { struct device_context *device_ctx = device_to_device_context(device); - int i = 0; - int len = 0; int ret; DPRINT_ENTER(VMBUS_DRV); @@ -644,8 +630,6 @@ static int vmbus_uevent(struct device *device, struct kobj_uevent_env *env) device_ctx->class_id.data[14], device_ctx->class_id.data[15]); - env->envp_idx = i; - env->buflen = len; ret = add_uevent_var(env, "VMBUS_DEVICE_CLASS_GUID={" "%02x%02x%02x%02x-%02x%02x-%02x%02x-" "%02x%02x%02x%02x%02x%02x%02x%02x}", @@ -691,8 +675,6 @@ static int vmbus_uevent(struct device *device, struct kobj_uevent_env *env) if (ret) return ret; - env->envp[env->envp_idx] = NULL; - DPRINT_EXIT(VMBUS_DRV); return 0; diff --git a/drivers/staging/iio/Kconfig b/drivers/staging/iio/Kconfig index beb99a547f09..4586650d65c3 100644 --- a/drivers/staging/iio/Kconfig +++ b/drivers/staging/iio/Kconfig @@ -4,6 +4,7 @@ menuconfig IIO tristate "Industrial I/O support" + depends on !S390 ---help--- The industrial I/O subsystem provides a unified framework for drivers for many different types of embedded sensors using a diff --git a/drivers/staging/iio/industrialio-core.c b/drivers/staging/iio/industrialio-core.c index 1fa18f255814..768f44894d08 100644 --- a/drivers/staging/iio/industrialio-core.c +++ b/drivers/staging/iio/industrialio-core.c @@ -18,6 +18,8 @@ #include <linux/fs.h> #include <linux/interrupt.h> #include <linux/poll.h> +#include <linux/sched.h> +#include <linux/wait.h> #include <linux/cdev.h> #include "iio.h" #include "trigger_consumer.h" diff --git a/drivers/staging/iio/light/tsl2561.c b/drivers/staging/iio/light/tsl2561.c index ea8a5efc19bc..fc2107f4c049 100644 --- a/drivers/staging/iio/light/tsl2561.c +++ b/drivers/staging/iio/light/tsl2561.c @@ -239,10 +239,6 @@ static int __devexit tsl2561_remove(struct i2c_client *client) return tsl2561_powerdown(client); } -static unsigned short normal_i2c[] = { 0x29, 0x39, 0x49, I2C_CLIENT_END }; - -I2C_CLIENT_INSMOD; - static const struct i2c_device_id tsl2561_id[] = { { "tsl2561", 0 }, { } diff --git a/drivers/staging/octeon/ethernet-mdio.c b/drivers/staging/octeon/ethernet-mdio.c index 42230e62a222..31a58e508924 100644 --- a/drivers/staging/octeon/ethernet-mdio.c +++ b/drivers/staging/octeon/ethernet-mdio.c @@ -170,7 +170,7 @@ static u32 cvm_oct_get_link(struct net_device *dev) return ret; } -struct const ethtool_ops cvm_oct_ethtool_ops = { +const struct ethtool_ops cvm_oct_ethtool_ops = { .get_drvinfo = cvm_oct_get_drvinfo, .get_settings = cvm_oct_get_settings, .set_settings = cvm_oct_set_settings, diff --git a/drivers/staging/octeon/ethernet-spi.c b/drivers/staging/octeon/ethernet-spi.c index 66190b0cb68f..00dc0f4bad19 100644 --- a/drivers/staging/octeon/ethernet-spi.c +++ b/drivers/staging/octeon/ethernet-spi.c @@ -317,6 +317,6 @@ void cvm_oct_spi_uninit(struct net_device *dev) cvmx_write_csr(CVMX_SPXX_INT_MSK(interface), 0); cvmx_write_csr(CVMX_STXX_INT_MSK(interface), 0); } - free_irq(8 + 46, &number_spi_ports); + free_irq(OCTEON_IRQ_RML, &number_spi_ports); } } diff --git a/drivers/staging/octeon/ethernet.c b/drivers/staging/octeon/ethernet.c index b8479517dce2..492c5029992d 100644 --- a/drivers/staging/octeon/ethernet.c +++ b/drivers/staging/octeon/ethernet.c @@ -111,6 +111,16 @@ MODULE_PARM_DESC(disable_core_queueing, "\n" "\tallows packets to be sent without lock contention in the packet\n" "\tscheduler resulting in some cases in improved throughput.\n"); + +/* + * The offset from mac_addr_base that should be used for the next port + * that is configured. By convention, if any mgmt ports exist on the + * chip, they get the first mac addresses, The ports controlled by + * this driver are numbered sequencially following any mgmt addresses + * that may exist. + */ +static unsigned int cvm_oct_mac_addr_offset; + /** * Periodic timer to check auto negotiation */ @@ -474,16 +484,30 @@ static int cvm_oct_common_set_mac_address(struct net_device *dev, void *addr) */ int cvm_oct_common_init(struct net_device *dev) { - static int count; - char mac[8] = { 0x00, 0x00, - octeon_bootinfo->mac_addr_base[0], - octeon_bootinfo->mac_addr_base[1], - octeon_bootinfo->mac_addr_base[2], - octeon_bootinfo->mac_addr_base[3], - octeon_bootinfo->mac_addr_base[4], - octeon_bootinfo->mac_addr_base[5] + count - }; struct octeon_ethernet *priv = netdev_priv(dev); + struct sockaddr sa; + u64 mac = ((u64)(octeon_bootinfo->mac_addr_base[0] & 0xff) << 40) | + ((u64)(octeon_bootinfo->mac_addr_base[1] & 0xff) << 32) | + ((u64)(octeon_bootinfo->mac_addr_base[2] & 0xff) << 24) | + ((u64)(octeon_bootinfo->mac_addr_base[3] & 0xff) << 16) | + ((u64)(octeon_bootinfo->mac_addr_base[4] & 0xff) << 8) | + (u64)(octeon_bootinfo->mac_addr_base[5] & 0xff); + + mac += cvm_oct_mac_addr_offset; + sa.sa_data[0] = (mac >> 40) & 0xff; + sa.sa_data[1] = (mac >> 32) & 0xff; + sa.sa_data[2] = (mac >> 24) & 0xff; + sa.sa_data[3] = (mac >> 16) & 0xff; + sa.sa_data[4] = (mac >> 8) & 0xff; + sa.sa_data[5] = mac & 0xff; + + if (cvm_oct_mac_addr_offset >= octeon_bootinfo->mac_addr_count) + printk(KERN_DEBUG "%s: Using MAC outside of the assigned range:" + " %02x:%02x:%02x:%02x:%02x:%02x\n", dev->name, + sa.sa_data[0] & 0xff, sa.sa_data[1] & 0xff, + sa.sa_data[2] & 0xff, sa.sa_data[3] & 0xff, + sa.sa_data[4] & 0xff, sa.sa_data[5] & 0xff); + cvm_oct_mac_addr_offset++; /* * Force the interface to use the POW send if always_use_pow @@ -496,14 +520,12 @@ int cvm_oct_common_init(struct net_device *dev) if (priv->queue != -1 && USE_HW_TCPUDP_CHECKSUM) dev->features |= NETIF_F_IP_CSUM; - count++; - /* We do our own locking, Linux doesn't need to */ dev->features |= NETIF_F_LLTX; SET_ETHTOOL_OPS(dev, &cvm_oct_ethtool_ops); cvm_oct_mdio_setup_device(dev); - dev->netdev_ops->ndo_set_mac_address(dev, mac); + dev->netdev_ops->ndo_set_mac_address(dev, &sa); dev->netdev_ops->ndo_change_mtu(dev, dev->mtu); /* @@ -620,6 +642,13 @@ static int __init cvm_oct_init_module(void) pr_notice("cavium-ethernet %s\n", OCTEON_ETHERNET_VERSION); + if (OCTEON_IS_MODEL(OCTEON_CN52XX)) + cvm_oct_mac_addr_offset = 2; /* First two are the mgmt ports. */ + else if (OCTEON_IS_MODEL(OCTEON_CN56XX)) + cvm_oct_mac_addr_offset = 1; /* First one is the mgmt port. */ + else + cvm_oct_mac_addr_offset = 0; + cvm_oct_proc_initialize(); cvm_oct_rx_initialize(); cvm_oct_configure_common_hw(); diff --git a/drivers/staging/otus/Kconfig b/drivers/staging/otus/Kconfig index d549d08fd495..f6cc2625e341 100644 --- a/drivers/staging/otus/Kconfig +++ b/drivers/staging/otus/Kconfig @@ -1,6 +1,6 @@ config OTUS tristate "Atheros OTUS 802.11n USB wireless support" - depends on USB && WLAN_80211 && MAC80211 + depends on USB && WLAN && MAC80211 default N ---help--- Enable support for Atheros 802.11n USB hardware: diff --git a/drivers/staging/p9auth/p9auth.c b/drivers/staging/p9auth/p9auth.c index 9111dcba37a1..8ccfff723eec 100644 --- a/drivers/staging/p9auth/p9auth.c +++ b/drivers/staging/p9auth/p9auth.c @@ -183,7 +183,7 @@ static ssize_t cap_write(struct file *filp, const char __user *buf, user_buf_running = NULL; hash_str = NULL; node_ptr = kmalloc(sizeof(struct cap_node), GFP_KERNEL); - user_buf = kzalloc(count, GFP_KERNEL); + user_buf = kzalloc(count+1, GFP_KERNEL); if (!node_ptr || !user_buf) goto out; @@ -207,6 +207,7 @@ static ssize_t cap_write(struct file *filp, const char __user *buf, list_add(&(node_ptr->list), &(dev->head->list)); node_ptr = NULL; } else { + char *tmpu; if (!cap_devices[0].head || list_empty(&(cap_devices[0].head->list))) { retval = -EINVAL; @@ -218,10 +219,10 @@ static ssize_t cap_write(struct file *filp, const char __user *buf, * need to split it and hash 'user1@user2' using 'randomstring' * as the key. */ - user_buf_running = kstrdup(user_buf, GFP_KERNEL); - source_user = strsep(&user_buf_running, "@"); - target_user = strsep(&user_buf_running, "@"); - rand_str = strsep(&user_buf_running, "@"); + tmpu = user_buf_running = kstrdup(user_buf, GFP_KERNEL); + source_user = strsep(&tmpu, "@"); + target_user = strsep(&tmpu, "@"); + rand_str = tmpu; if (!source_user || !target_user || !rand_str) { retval = -EINVAL; goto out; @@ -229,7 +230,8 @@ static ssize_t cap_write(struct file *filp, const char __user *buf, /* hash the string user1@user2 with rand_str as the key */ len = strlen(source_user) + strlen(target_user) + 1; - hash_str = kzalloc(len, GFP_KERNEL); + /* src, @, len, \0 */ + hash_str = kzalloc(len+1, GFP_KERNEL); strcat(hash_str, source_user); strcat(hash_str, "@"); strcat(hash_str, target_user); diff --git a/drivers/staging/panel/panel.c b/drivers/staging/panel/panel.c index dd7d3fde9699..4ce399b6d237 100644 --- a/drivers/staging/panel/panel.c +++ b/drivers/staging/panel/panel.c @@ -2071,11 +2071,15 @@ static void panel_detach(struct parport *port) return; } - if (keypad_enabled && keypad_initialized) + if (keypad_enabled && keypad_initialized) { misc_deregister(&keypad_dev); + keypad_initialized = 0; + } - if (lcd_enabled && lcd_initialized) + if (lcd_enabled && lcd_initialized) { misc_deregister(&lcd_dev); + lcd_initialized = 0; + } parport_release(pprt); parport_unregister_device(pprt); @@ -2211,13 +2215,16 @@ static void __exit panel_cleanup_module(void) del_timer(&scan_timer); if (pprt != NULL) { - if (keypad_enabled) + if (keypad_enabled) { misc_deregister(&keypad_dev); + keypad_initialized = 0; + } if (lcd_enabled) { panel_lcd_print("\x0cLCD driver " PANEL_VERSION "\nunloaded.\x1b[Lc\x1b[Lb\x1b[L-"); misc_deregister(&lcd_dev); + lcd_initialized = 0; } /* TODO: free all input signals */ diff --git a/drivers/staging/poch/poch.c b/drivers/staging/poch/poch.c index 0d111ddfabb2..2eb8e3d43c4d 100644 --- a/drivers/staging/poch/poch.c +++ b/drivers/staging/poch/poch.c @@ -20,6 +20,7 @@ #include <linux/init.h> #include <linux/ioctl.h> #include <linux/io.h> +#include <linux/sched.h> #include "poch.h" diff --git a/drivers/staging/pohmelfs/config.c b/drivers/staging/pohmelfs/config.c index 90f962ee5fd8..5d04bf5b021a 100644 --- a/drivers/staging/pohmelfs/config.c +++ b/drivers/staging/pohmelfs/config.c @@ -527,10 +527,13 @@ out_unlock: return err; } -static void pohmelfs_cn_callback(struct cn_msg *msg) +static void pohmelfs_cn_callback(struct cn_msg *msg, struct netlink_skb_parms *nsp) { int err; + if (!cap_raised(nsp->eff_cap, CAP_SYS_ADMIN)) + return; + switch (msg->flags) { case POHMELFS_FLAGS_ADD: case POHMELFS_FLAGS_DEL: diff --git a/drivers/staging/rt2860/Kconfig b/drivers/staging/rt2860/Kconfig index 7f44e5e72463..efe38e25c5ed 100644 --- a/drivers/staging/rt2860/Kconfig +++ b/drivers/staging/rt2860/Kconfig @@ -1,5 +1,5 @@ config RT2860 tristate "Ralink 2860 wireless support" - depends on PCI && X86 && WLAN_80211 + depends on PCI && X86 && WLAN ---help--- This is an experimental driver for the Ralink 2860 wireless chip. diff --git a/drivers/staging/rt2860/common/cmm_data_2860.c b/drivers/staging/rt2860/common/cmm_data_2860.c index fb1735533b74..857ff450b6c9 100644 --- a/drivers/staging/rt2860/common/cmm_data_2860.c +++ b/drivers/staging/rt2860/common/cmm_data_2860.c @@ -363,6 +363,8 @@ int RtmpPCIMgmtKickOut( ULONG SwIdx = pAd->MgmtRing.TxCpuIdx; pTxD = (PTXD_STRUC) pAd->MgmtRing.Cell[SwIdx].AllocVa; + if (!pTxD) + return 0; pAd->MgmtRing.Cell[SwIdx].pNdisPacket = pPacket; pAd->MgmtRing.Cell[SwIdx].pNextNdisPacket = NULL; diff --git a/drivers/staging/rt2860/common/cmm_info.c b/drivers/staging/rt2860/common/cmm_info.c index 9d589c240ed0..019cc4474ce8 100644 --- a/drivers/staging/rt2860/common/cmm_info.c +++ b/drivers/staging/rt2860/common/cmm_info.c @@ -25,6 +25,7 @@ ************************************************************************* */ +#include <linux/sched.h> #include "../rt_config.h" INT Show_SSID_Proc( diff --git a/drivers/staging/rt2860/rt_linux.c b/drivers/staging/rt2860/rt_linux.c index b396a9b570e2..ed27b8545a1b 100644 --- a/drivers/staging/rt2860/rt_linux.c +++ b/drivers/staging/rt2860/rt_linux.c @@ -25,6 +25,7 @@ ************************************************************************* */ +#include <linux/sched.h> #include "rt_config.h" ULONG RTDebugLevel = RT_DEBUG_ERROR; diff --git a/drivers/staging/rt2870/Kconfig b/drivers/staging/rt2870/Kconfig index 76841f6dea93..aea5c8221810 100644 --- a/drivers/staging/rt2870/Kconfig +++ b/drivers/staging/rt2870/Kconfig @@ -1,5 +1,5 @@ config RT2870 tristate "Ralink 2870/3070 wireless support" - depends on USB && X86 && WLAN_80211 + depends on USB && X86 && WLAN ---help--- This is an experimental driver for the Ralink xx70 wireless chips. diff --git a/drivers/staging/rt3090/Kconfig b/drivers/staging/rt3090/Kconfig index 255e8eaa4836..2b3f745d72b7 100644 --- a/drivers/staging/rt3090/Kconfig +++ b/drivers/staging/rt3090/Kconfig @@ -1,5 +1,5 @@ config RT3090 tristate "Ralink 3090 wireless support" - depends on PCI && X86 && WLAN_80211 + depends on PCI && X86 && WLAN ---help--- This is an experimental driver for the Ralink 3090 wireless chip. diff --git a/drivers/staging/rt3090/common/cmm_info.c b/drivers/staging/rt3090/common/cmm_info.c index 5be0714666cb..3e51e98b474c 100644 --- a/drivers/staging/rt3090/common/cmm_info.c +++ b/drivers/staging/rt3090/common/cmm_info.c @@ -34,6 +34,7 @@ --------- ---------- ---------------------------------------------- */ +#include <linux/sched.h> #include "../rt_config.h" diff --git a/drivers/staging/rt3090/rt_linux.c b/drivers/staging/rt3090/rt_linux.c index d2241ecdf583..9b94aa6eb904 100644 --- a/drivers/staging/rt3090/rt_linux.c +++ b/drivers/staging/rt3090/rt_linux.c @@ -25,6 +25,7 @@ ************************************************************************* */ +#include <linux/sched.h> #include "rt_config.h" ULONG RTDebugLevel = RT_DEBUG_ERROR; diff --git a/drivers/staging/rtl8187se/Kconfig b/drivers/staging/rtl8187se/Kconfig index 236e42725447..203c79b8180f 100644 --- a/drivers/staging/rtl8187se/Kconfig +++ b/drivers/staging/rtl8187se/Kconfig @@ -1,6 +1,6 @@ config RTL8187SE tristate "RealTek RTL8187SE Wireless LAN NIC driver" - depends on PCI + depends on PCI && WLAN depends on WIRELESS_EXT default N ---help--- diff --git a/drivers/staging/rtl8187se/ieee80211/ieee80211_crypt.c b/drivers/staging/rtl8187se/ieee80211/ieee80211_crypt.c index 013c3e19ae25..4c5d63fd5833 100644 --- a/drivers/staging/rtl8187se/ieee80211/ieee80211_crypt.c +++ b/drivers/staging/rtl8187se/ieee80211/ieee80211_crypt.c @@ -53,10 +53,8 @@ void ieee80211_crypt_deinit_entries(struct ieee80211_device *ieee, list_del(ptr); - if (entry->ops) { + if (entry->ops) entry->ops->deinit(entry->priv); - module_put(entry->ops->owner); - } kfree(entry); } } diff --git a/drivers/staging/rtl8187se/ieee80211/ieee80211_module.c b/drivers/staging/rtl8187se/ieee80211/ieee80211_module.c index 6fbe4890cb66..18392fce487d 100644 --- a/drivers/staging/rtl8187se/ieee80211/ieee80211_module.c +++ b/drivers/staging/rtl8187se/ieee80211/ieee80211_module.c @@ -189,10 +189,8 @@ void free_ieee80211(struct net_device *dev) for (i = 0; i < WEP_KEYS; i++) { struct ieee80211_crypt_data *crypt = ieee->crypt[i]; if (crypt) { - if (crypt->ops) { + if (crypt->ops) crypt->ops->deinit(crypt->priv); - module_put(crypt->ops->owner); - } kfree(crypt); ieee->crypt[i] = NULL; } diff --git a/drivers/staging/rtl8187se/ieee80211/ieee80211_softmac.c b/drivers/staging/rtl8187se/ieee80211/ieee80211_softmac.c index 59b2ab48cdcf..334e4c7ec61b 100644 --- a/drivers/staging/rtl8187se/ieee80211/ieee80211_softmac.c +++ b/drivers/staging/rtl8187se/ieee80211/ieee80211_softmac.c @@ -2839,16 +2839,12 @@ static int ieee80211_wpa_set_encryption(struct ieee80211_device *ieee, goto skip_host_crypt; ops = ieee80211_get_crypto_ops(param->u.crypt.alg); - if (ops == NULL && strcmp(param->u.crypt.alg, "WEP") == 0) { - request_module("ieee80211_crypt_wep"); + if (ops == NULL && strcmp(param->u.crypt.alg, "WEP") == 0) ops = ieee80211_get_crypto_ops(param->u.crypt.alg); - } else if (ops == NULL && strcmp(param->u.crypt.alg, "TKIP") == 0) { - request_module("ieee80211_crypt_tkip"); + else if (ops == NULL && strcmp(param->u.crypt.alg, "TKIP") == 0) ops = ieee80211_get_crypto_ops(param->u.crypt.alg); - } else if (ops == NULL && strcmp(param->u.crypt.alg, "CCMP") == 0) { - request_module("ieee80211_crypt_ccmp"); + else if (ops == NULL && strcmp(param->u.crypt.alg, "CCMP") == 0) ops = ieee80211_get_crypto_ops(param->u.crypt.alg); - } if (ops == NULL) { printk("unknown crypto alg '%s'\n", param->u.crypt.alg); param->u.crypt.err = IEEE_CRYPT_ERR_UNKNOWN_ALG; @@ -2869,7 +2865,7 @@ static int ieee80211_wpa_set_encryption(struct ieee80211_device *ieee, } memset(new_crypt, 0, sizeof(struct ieee80211_crypt_data)); new_crypt->ops = ops; - if (new_crypt->ops && try_module_get(new_crypt->ops->owner)) + if (new_crypt->ops) new_crypt->priv = new_crypt->ops->init(param->u.crypt.idx); diff --git a/drivers/staging/rtl8187se/ieee80211/ieee80211_wx.c b/drivers/staging/rtl8187se/ieee80211/ieee80211_wx.c index 8d8bdd0a130e..a08b97a09512 100644 --- a/drivers/staging/rtl8187se/ieee80211/ieee80211_wx.c +++ b/drivers/staging/rtl8187se/ieee80211/ieee80211_wx.c @@ -331,12 +331,10 @@ int ieee80211_wx_set_encode(struct ieee80211_device *ieee, return -ENOMEM; memset(new_crypt, 0, sizeof(struct ieee80211_crypt_data)); new_crypt->ops = ieee80211_get_crypto_ops("WEP"); - if (!new_crypt->ops) { - request_module("ieee80211_crypt_wep"); + if (!new_crypt->ops) new_crypt->ops = ieee80211_get_crypto_ops("WEP"); - } - if (new_crypt->ops && try_module_get(new_crypt->ops->owner)) + if (new_crypt->ops) new_crypt->priv = new_crypt->ops->init(key); if (!new_crypt->ops || !new_crypt->priv) { @@ -483,7 +481,7 @@ int ieee80211_wx_set_encode_ext(struct ieee80211_device *ieee, struct iw_encode_ext *ext = (struct iw_encode_ext *)extra; int i, idx, ret = 0; int group_key = 0; - const char *alg, *module; + const char *alg; struct ieee80211_crypto_ops *ops; struct ieee80211_crypt_data **crypt; @@ -539,15 +537,12 @@ int ieee80211_wx_set_encode_ext(struct ieee80211_device *ieee, switch (ext->alg) { case IW_ENCODE_ALG_WEP: alg = "WEP"; - module = "ieee80211_crypt_wep"; break; case IW_ENCODE_ALG_TKIP: alg = "TKIP"; - module = "ieee80211_crypt_tkip"; break; case IW_ENCODE_ALG_CCMP: alg = "CCMP"; - module = "ieee80211_crypt_ccmp"; break; default: IEEE80211_DEBUG_WX("%s: unknown crypto alg %d\n", @@ -558,10 +553,8 @@ int ieee80211_wx_set_encode_ext(struct ieee80211_device *ieee, // printk("8-09-08-9=====>%s, alg name:%s\n",__func__, alg); ops = ieee80211_get_crypto_ops(alg); - if (ops == NULL) { - request_module(module); + if (ops == NULL) ops = ieee80211_get_crypto_ops(alg); - } if (ops == NULL) { IEEE80211_DEBUG_WX("%s: unknown crypto alg %d\n", dev->name, ext->alg); @@ -581,7 +574,7 @@ int ieee80211_wx_set_encode_ext(struct ieee80211_device *ieee, goto done; } new_crypt->ops = ops; - if (new_crypt->ops && try_module_get(new_crypt->ops->owner)) + if (new_crypt->ops) new_crypt->priv = new_crypt->ops->init(idx); if (new_crypt->priv == NULL) { kfree(new_crypt); diff --git a/drivers/staging/rtl8192e/Kconfig b/drivers/staging/rtl8192e/Kconfig index 3100aa58c940..37e4fde45073 100644 --- a/drivers/staging/rtl8192e/Kconfig +++ b/drivers/staging/rtl8192e/Kconfig @@ -1,6 +1,6 @@ config RTL8192E tristate "RealTek RTL8192E Wireless LAN NIC driver" - depends on PCI + depends on PCI && WLAN depends on WIRELESS_EXT default N ---help--- diff --git a/drivers/staging/rtl8192e/ieee80211/ieee80211_crypt.c b/drivers/staging/rtl8192e/ieee80211/ieee80211_crypt.c index 1a8ea8a40c3c..b1c54932da3e 100644 --- a/drivers/staging/rtl8192e/ieee80211/ieee80211_crypt.c +++ b/drivers/staging/rtl8192e/ieee80211/ieee80211_crypt.c @@ -53,14 +53,8 @@ void ieee80211_crypt_deinit_entries(struct ieee80211_device *ieee, list_del(ptr); - if (entry->ops) { + if (entry->ops) entry->ops->deinit(entry->priv); -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0) - module_put(entry->ops->owner); -#else - __MOD_DEC_USE_COUNT(entry->ops->owner); -#endif - } kfree(entry); } } diff --git a/drivers/staging/rtl8192e/ieee80211/ieee80211_module.c b/drivers/staging/rtl8192e/ieee80211/ieee80211_module.c index 16256a31f993..12c2a18e1fa2 100644 --- a/drivers/staging/rtl8192e/ieee80211/ieee80211_module.c +++ b/drivers/staging/rtl8192e/ieee80211/ieee80211_module.c @@ -242,14 +242,8 @@ void free_ieee80211(struct net_device *dev) for (i = 0; i < WEP_KEYS; i++) { struct ieee80211_crypt_data *crypt = ieee->crypt[i]; if (crypt) { - if (crypt->ops) { + if (crypt->ops) crypt->ops->deinit(crypt->priv); -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0) - module_put(crypt->ops->owner); -#else - __MOD_DEC_USE_COUNT(crypt->ops->owner); -#endif - } kfree(crypt); ieee->crypt[i] = NULL; } diff --git a/drivers/staging/rtl8192e/ieee80211/ieee80211_softmac.c b/drivers/staging/rtl8192e/ieee80211/ieee80211_softmac.c index 2fc04df872ca..eae7c4579a68 100644 --- a/drivers/staging/rtl8192e/ieee80211/ieee80211_softmac.c +++ b/drivers/staging/rtl8192e/ieee80211/ieee80211_softmac.c @@ -3284,17 +3284,14 @@ static int ieee80211_wpa_set_encryption(struct ieee80211_device *ieee, goto skip_host_crypt; ops = ieee80211_get_crypto_ops(param->u.crypt.alg); - if (ops == NULL && strcmp(param->u.crypt.alg, "WEP") == 0) { - request_module("ieee80211_crypt_wep"); + if (ops == NULL && strcmp(param->u.crypt.alg, "WEP") == 0) ops = ieee80211_get_crypto_ops(param->u.crypt.alg); - //set WEP40 first, it will be modified according to WEP104 or WEP40 at other place - } else if (ops == NULL && strcmp(param->u.crypt.alg, "TKIP") == 0) { - request_module("ieee80211_crypt_tkip"); + /* set WEP40 first, it will be modified according to WEP104 or + * WEP40 at other place */ + else if (ops == NULL && strcmp(param->u.crypt.alg, "TKIP") == 0) ops = ieee80211_get_crypto_ops(param->u.crypt.alg); - } else if (ops == NULL && strcmp(param->u.crypt.alg, "CCMP") == 0) { - request_module("ieee80211_crypt_ccmp"); + else if (ops == NULL && strcmp(param->u.crypt.alg, "CCMP") == 0) ops = ieee80211_get_crypto_ops(param->u.crypt.alg); - } if (ops == NULL) { printk("unknown crypto alg '%s'\n", param->u.crypt.alg); param->u.crypt.err = IEEE_CRYPT_ERR_UNKNOWN_ALG; @@ -3315,11 +3312,7 @@ static int ieee80211_wpa_set_encryption(struct ieee80211_device *ieee, } memset(new_crypt, 0, sizeof(struct ieee80211_crypt_data)); new_crypt->ops = ops; -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0) - if (new_crypt->ops && try_module_get(new_crypt->ops->owner)) -#else - if (new_crypt->ops && try_inc_mod_count(new_crypt->ops->owner)) -#endif + if (new_crypt->ops) new_crypt->priv = new_crypt->ops->init(param->u.crypt.idx); diff --git a/drivers/staging/rtl8192e/ieee80211/ieee80211_wx.c b/drivers/staging/rtl8192e/ieee80211/ieee80211_wx.c index 223483126b0e..4e34a1f4c66b 100644 --- a/drivers/staging/rtl8192e/ieee80211/ieee80211_wx.c +++ b/drivers/staging/rtl8192e/ieee80211/ieee80211_wx.c @@ -482,15 +482,9 @@ int ieee80211_wx_set_encode(struct ieee80211_device *ieee, return -ENOMEM; memset(new_crypt, 0, sizeof(struct ieee80211_crypt_data)); new_crypt->ops = ieee80211_get_crypto_ops("WEP"); - if (!new_crypt->ops) { - request_module("ieee80211_crypt_wep"); + if (!new_crypt->ops) new_crypt->ops = ieee80211_get_crypto_ops("WEP"); - } -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0) - if (new_crypt->ops && try_module_get(new_crypt->ops->owner)) -#else - if (new_crypt->ops && try_inc_mod_count(new_crypt->ops->owner)) -#endif + if (new_crypt->ops) new_crypt->priv = new_crypt->ops->init(key); if (!new_crypt->ops || !new_crypt->priv) { @@ -644,7 +638,7 @@ int ieee80211_wx_set_encode_ext(struct ieee80211_device *ieee, struct iw_encode_ext *ext = (struct iw_encode_ext *)extra; int i, idx; int group_key = 0; - const char *alg, *module; + const char *alg; struct ieee80211_crypto_ops *ops; struct ieee80211_crypt_data **crypt; @@ -711,15 +705,12 @@ int ieee80211_wx_set_encode_ext(struct ieee80211_device *ieee, switch (ext->alg) { case IW_ENCODE_ALG_WEP: alg = "WEP"; - module = "ieee80211_crypt_wep"; break; case IW_ENCODE_ALG_TKIP: alg = "TKIP"; - module = "ieee80211_crypt_tkip"; break; case IW_ENCODE_ALG_CCMP: alg = "CCMP"; - module = "ieee80211_crypt_ccmp"; break; default: IEEE80211_DEBUG_WX("%s: unknown crypto alg %d\n", @@ -730,10 +721,8 @@ int ieee80211_wx_set_encode_ext(struct ieee80211_device *ieee, printk("alg name:%s\n",alg); ops = ieee80211_get_crypto_ops(alg); - if (ops == NULL) { - request_module(module); + if (ops == NULL) ops = ieee80211_get_crypto_ops(alg); - } if (ops == NULL) { IEEE80211_DEBUG_WX("%s: unknown crypto alg %d\n", dev->name, ext->alg); @@ -758,7 +747,7 @@ int ieee80211_wx_set_encode_ext(struct ieee80211_device *ieee, goto done; } new_crypt->ops = ops; - if (new_crypt->ops && try_module_get(new_crypt->ops->owner)) + if (new_crypt->ops) new_crypt->priv = new_crypt->ops->init(idx); if (new_crypt->priv == NULL) { kfree(new_crypt); diff --git a/drivers/staging/rtl8192e/r8192E_core.c b/drivers/staging/rtl8192e/r8192E_core.c index d4fa65489655..b0802a7aeb5f 100644 --- a/drivers/staging/rtl8192e/r8192E_core.c +++ b/drivers/staging/rtl8192e/r8192E_core.c @@ -46,6 +46,7 @@ #undef DEBUG_TX_DESC //#define CONFIG_RTL8192_IO_MAP +#include <linux/vmalloc.h> #include <asm/uaccess.h> #include "r8192E_hw.h" #include "r8192E.h" diff --git a/drivers/staging/rtl8192su/Kconfig b/drivers/staging/rtl8192su/Kconfig index 770f41280f21..b8c95f942069 100644 --- a/drivers/staging/rtl8192su/Kconfig +++ b/drivers/staging/rtl8192su/Kconfig @@ -1,6 +1,6 @@ config RTL8192SU tristate "RealTek RTL8192SU Wireless LAN NIC driver" - depends on PCI + depends on PCI && WLAN depends on WIRELESS_EXT default N ---help--- diff --git a/drivers/staging/rtl8192su/ieee80211/ieee80211_crypt.c b/drivers/staging/rtl8192su/ieee80211/ieee80211_crypt.c index d76a54d59d2f..521e7b989934 100644 --- a/drivers/staging/rtl8192su/ieee80211/ieee80211_crypt.c +++ b/drivers/staging/rtl8192su/ieee80211/ieee80211_crypt.c @@ -53,10 +53,8 @@ void ieee80211_crypt_deinit_entries(struct ieee80211_device *ieee, list_del(ptr); - if (entry->ops) { + if (entry->ops) entry->ops->deinit(entry->priv); - module_put(entry->ops->owner); - } kfree(entry); } } diff --git a/drivers/staging/rtl8192su/ieee80211/ieee80211_module.c b/drivers/staging/rtl8192su/ieee80211/ieee80211_module.c index 68dc8fa094cc..c3383bb8b760 100644 --- a/drivers/staging/rtl8192su/ieee80211/ieee80211_module.c +++ b/drivers/staging/rtl8192su/ieee80211/ieee80211_module.c @@ -216,10 +216,8 @@ void free_ieee80211(struct net_device *dev) for (i = 0; i < WEP_KEYS; i++) { struct ieee80211_crypt_data *crypt = ieee->crypt[i]; if (crypt) { - if (crypt->ops) { + if (crypt->ops) crypt->ops->deinit(crypt->priv); - module_put(crypt->ops->owner); - } kfree(crypt); ieee->crypt[i] = NULL; } diff --git a/drivers/staging/rtl8192su/ieee80211/ieee80211_softmac.c b/drivers/staging/rtl8192su/ieee80211/ieee80211_softmac.c index c64ae03f68a0..fd8e11252f1b 100644 --- a/drivers/staging/rtl8192su/ieee80211/ieee80211_softmac.c +++ b/drivers/staging/rtl8192su/ieee80211/ieee80211_softmac.c @@ -3026,17 +3026,14 @@ static int ieee80211_wpa_set_encryption(struct ieee80211_device *ieee, goto skip_host_crypt; ops = ieee80211_get_crypto_ops(param->u.crypt.alg); - if (ops == NULL && strcmp(param->u.crypt.alg, "WEP") == 0) { - request_module("ieee80211_crypt_wep"); + if (ops == NULL && strcmp(param->u.crypt.alg, "WEP") == 0) ops = ieee80211_get_crypto_ops(param->u.crypt.alg); - //set WEP40 first, it will be modified according to WEP104 or WEP40 at other place - } else if (ops == NULL && strcmp(param->u.crypt.alg, "TKIP") == 0) { - request_module("ieee80211_crypt_tkip"); + /* set WEP40 first, it will be modified according to WEP104 or + * WEP40 at other place */ + else if (ops == NULL && strcmp(param->u.crypt.alg, "TKIP") == 0) ops = ieee80211_get_crypto_ops(param->u.crypt.alg); - } else if (ops == NULL && strcmp(param->u.crypt.alg, "CCMP") == 0) { - request_module("ieee80211_crypt_ccmp"); + else if (ops == NULL && strcmp(param->u.crypt.alg, "CCMP") == 0) ops = ieee80211_get_crypto_ops(param->u.crypt.alg); - } if (ops == NULL) { printk("unknown crypto alg '%s'\n", param->u.crypt.alg); param->u.crypt.err = IEEE_CRYPT_ERR_UNKNOWN_ALG; @@ -3058,7 +3055,7 @@ static int ieee80211_wpa_set_encryption(struct ieee80211_device *ieee, memset(new_crypt, 0, sizeof(struct ieee80211_crypt_data)); new_crypt->ops = ops; - if (new_crypt->ops && try_module_get(new_crypt->ops->owner)) + if (new_crypt->ops) new_crypt->priv = new_crypt->ops->init(param->u.crypt.idx); diff --git a/drivers/staging/rtl8192su/ieee80211/ieee80211_wx.c b/drivers/staging/rtl8192su/ieee80211/ieee80211_wx.c index 107759024335..6146c6435dde 100644 --- a/drivers/staging/rtl8192su/ieee80211/ieee80211_wx.c +++ b/drivers/staging/rtl8192su/ieee80211/ieee80211_wx.c @@ -358,11 +358,9 @@ int ieee80211_wx_set_encode(struct ieee80211_device *ieee, return -ENOMEM; memset(new_crypt, 0, sizeof(struct ieee80211_crypt_data)); new_crypt->ops = ieee80211_get_crypto_ops("WEP"); - if (!new_crypt->ops) { - request_module("ieee80211_crypt_wep"); + if (!new_crypt->ops) new_crypt->ops = ieee80211_get_crypto_ops("WEP"); - } - if (new_crypt->ops && try_module_get(new_crypt->ops->owner)) + if (new_crypt->ops) new_crypt->priv = new_crypt->ops->init(key); if (!new_crypt->ops || !new_crypt->priv) { @@ -507,7 +505,7 @@ int ieee80211_wx_set_encode_ext(struct ieee80211_device *ieee, struct iw_encode_ext *ext = (struct iw_encode_ext *)extra; int i, idx; int group_key = 0; - const char *alg, *module; + const char *alg; struct ieee80211_crypto_ops *ops; struct ieee80211_crypt_data **crypt; @@ -570,15 +568,12 @@ int ieee80211_wx_set_encode_ext(struct ieee80211_device *ieee, switch (ext->alg) { case IW_ENCODE_ALG_WEP: alg = "WEP"; - module = "ieee80211_crypt_wep"; break; case IW_ENCODE_ALG_TKIP: alg = "TKIP"; - module = "ieee80211_crypt_tkip"; break; case IW_ENCODE_ALG_CCMP: alg = "CCMP"; - module = "ieee80211_crypt_ccmp"; break; default: IEEE80211_DEBUG_WX("%s: unknown crypto alg %d\n", @@ -589,10 +584,8 @@ int ieee80211_wx_set_encode_ext(struct ieee80211_device *ieee, printk("alg name:%s\n",alg); ops = ieee80211_get_crypto_ops(alg); - if (ops == NULL) { - request_module("%s", module); + if (ops == NULL) ops = ieee80211_get_crypto_ops(alg); - } if (ops == NULL) { IEEE80211_DEBUG_WX("%s: unknown crypto alg %d\n", dev->name, ext->alg); @@ -612,7 +605,7 @@ int ieee80211_wx_set_encode_ext(struct ieee80211_device *ieee, goto done; } new_crypt->ops = ops; - if (new_crypt->ops && try_module_get(new_crypt->ops->owner)) + if (new_crypt->ops) new_crypt->priv = new_crypt->ops->init(idx); if (new_crypt->priv == NULL) { kfree(new_crypt); diff --git a/drivers/staging/sep/sep_driver.c b/drivers/staging/sep/sep_driver.c index 87f8a1192762..f890a16096c0 100644 --- a/drivers/staging/sep/sep_driver.c +++ b/drivers/staging/sep/sep_driver.c @@ -38,6 +38,7 @@ #include <linux/mm.h> #include <linux/poll.h> #include <linux/wait.h> +#include <linux/sched.h> #include <linux/pci.h> #include <linux/firmware.h> #include <asm/ioctl.h> diff --git a/drivers/staging/stlc45xx/Kconfig b/drivers/staging/stlc45xx/Kconfig deleted file mode 100644 index 947fb75a9c68..000000000000 --- a/drivers/staging/stlc45xx/Kconfig +++ /dev/null @@ -1,8 +0,0 @@ -config STLC45XX - tristate "stlc4550/4560 support" - depends on MAC80211 && WLAN_80211 && SPI_MASTER && GENERIC_HARDIRQS - ---help--- - This is a driver for stlc4550 and stlc4560 chipsets. - - To compile this driver as a module, choose M here: the module will be - called stlc45xx. If unsure, say N. diff --git a/drivers/staging/stlc45xx/Makefile b/drivers/staging/stlc45xx/Makefile deleted file mode 100644 index 7ee32903055a..000000000000 --- a/drivers/staging/stlc45xx/Makefile +++ /dev/null @@ -1 +0,0 @@ -obj-$(CONFIG_STLC45XX) += stlc45xx.o diff --git a/drivers/staging/stlc45xx/stlc45xx.c b/drivers/staging/stlc45xx/stlc45xx.c deleted file mode 100644 index 12d414deaad6..000000000000 --- a/drivers/staging/stlc45xx/stlc45xx.c +++ /dev/null @@ -1,2593 +0,0 @@ -/* - * This file is part of stlc45xx - * - * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies). - * - * Contact: Kalle Valo <kalle.valo@nokia.com> - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA - * 02110-1301 USA - * - */ - -#include "stlc45xx.h" - -#include <linux/module.h> -#include <linux/platform_device.h> -#include <linux/interrupt.h> -#include <linux/firmware.h> -#include <linux/delay.h> -#include <linux/irq.h> -#include <linux/spi/spi.h> -#include <linux/etherdevice.h> -#include <linux/gpio.h> -#include <linux/moduleparam.h> - -#include "stlc45xx_lmac.h" - -/* - * gpios should be handled in board files and provided via platform data, - * but because it's currently impossible for stlc45xx to have a header file - * in include/linux, let's use module paramaters for now - */ -static int stlc45xx_gpio_power = 97; -module_param(stlc45xx_gpio_power, int, 0444); -MODULE_PARM_DESC(stlc45xx_gpio_power, "stlc45xx gpio number for power line"); - -static int stlc45xx_gpio_irq = 87; -module_param(stlc45xx_gpio_irq, int, 0444); -MODULE_PARM_DESC(stlc45xx_gpio_irq, "stlc45xx gpio number for irq line"); - -static const u8 default_cal_channels[] = { - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6c, 0x09, - 0x00, 0x00, 0xc9, 0xff, 0xd8, 0xff, 0x00, 0x00, 0x00, 0x01, 0x10, - 0x01, 0x10, 0x01, 0x10, 0x01, 0x10, 0x01, 0xe0, 0x00, 0xe0, 0x00, - 0xe0, 0x00, 0xe0, 0x00, 0xd0, 0x00, 0xd0, 0x00, 0xd0, 0x00, 0xd0, - 0x00, 0x54, 0x01, 0xab, 0xf6, 0xc0, 0x42, 0xc0, 0x42, 0xc0, 0x42, - 0xc0, 0x42, 0x00, 0xcb, 0x00, 0xcb, 0x00, 0xcb, 0x00, 0xcb, 0x00, - 0xcb, 0x00, 0xcb, 0x00, 0xcb, 0x00, 0xcb, 0x22, 0x01, 0x37, 0xa9, - 0xc0, 0x33, 0xc0, 0x33, 0xc0, 0x33, 0xc0, 0x33, 0x00, 0xbc, 0x00, - 0xbc, 0x00, 0xbc, 0x00, 0xbc, 0x00, 0xbc, 0x00, 0xbc, 0x00, 0xbc, - 0x00, 0xbc, 0xfb, 0x00, 0xca, 0x79, 0xc0, 0x2b, 0xc0, 0x2b, 0xc0, - 0x2b, 0xc0, 0x2b, 0x00, 0xb4, 0x00, 0xb4, 0x00, 0xb4, 0x00, 0xb4, - 0x00, 0xb4, 0x00, 0xb4, 0x00, 0xb4, 0x00, 0xb4, 0xd0, 0x00, 0x5d, - 0x54, 0xc0, 0x21, 0xc0, 0x21, 0xc0, 0x21, 0xc0, 0x21, 0x00, 0xaa, - 0x00, 0xaa, 0x00, 0xaa, 0x00, 0xaa, 0x00, 0xaa, 0x00, 0xaa, 0x00, - 0xaa, 0x00, 0xaa, 0xa7, 0x00, 0xa9, 0x3d, 0xc0, 0x17, 0xc0, 0x17, - 0xc0, 0x17, 0xc0, 0x17, 0x00, 0xa0, 0x00, 0xa0, 0x00, 0xa0, 0x00, - 0xa0, 0x00, 0xa0, 0x00, 0xa0, 0x00, 0xa0, 0x00, 0xa0, 0x7a, 0x00, - 0x06, 0x2c, 0xc0, 0x0d, 0xc0, 0x0d, 0xc0, 0x0d, 0xc0, 0x0d, 0x00, - 0x96, 0x00, 0x96, 0x00, 0x96, 0x00, 0x96, 0x00, 0x96, 0x00, 0x96, - 0x00, 0x96, 0x00, 0x96, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x06, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x71, 0x09, 0x00, 0x00, 0xc9, 0xff, 0xd8, - 0xff, 0x00, 0x00, 0x00, 0x01, 0x10, 0x01, 0x10, 0x01, 0x10, 0x01, - 0x10, 0x01, 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, 0xd0, - 0x00, 0xd0, 0x00, 0xd0, 0x00, 0xd0, 0x00, 0x54, 0x01, 0xab, 0xf6, - 0xc0, 0x42, 0xc0, 0x42, 0xc0, 0x42, 0xc0, 0x42, 0x00, 0xcb, 0x00, - 0xcb, 0x00, 0xcb, 0x00, 0xcb, 0x00, 0xcb, 0x00, 0xcb, 0x00, 0xcb, - 0x00, 0xcb, 0x22, 0x01, 0x37, 0xa9, 0xc0, 0x33, 0xc0, 0x33, 0xc0, - 0x33, 0xc0, 0x33, 0x00, 0xbc, 0x00, 0xbc, 0x00, 0xbc, 0x00, 0xbc, - 0x00, 0xbc, 0x00, 0xbc, 0x00, 0xbc, 0x00, 0xbc, 0xfb, 0x00, 0xca, - 0x79, 0xc0, 0x2b, 0xc0, 0x2b, 0xc0, 0x2b, 0xc0, 0x2b, 0x00, 0xb4, - 0x00, 0xb4, 0x00, 0xb4, 0x00, 0xb4, 0x00, 0xb4, 0x00, 0xb4, 0x00, - 0xb4, 0x00, 0xb4, 0xd0, 0x00, 0x5d, 0x54, 0xc0, 0x21, 0xc0, 0x21, - 0xc0, 0x21, 0xc0, 0x21, 0x00, 0xaa, 0x00, 0xaa, 0x00, 0xaa, 0x00, - 0xaa, 0x00, 0xaa, 0x00, 0xaa, 0x00, 0xaa, 0x00, 0xaa, 0xa7, 0x00, - 0xa9, 0x3d, 0xc0, 0x17, 0xc0, 0x17, 0xc0, 0x17, 0xc0, 0x17, 0x00, - 0xa0, 0x00, 0xa0, 0x00, 0xa0, 0x00, 0xa0, 0x00, 0xa0, 0x00, 0xa0, - 0x00, 0xa0, 0x00, 0xa0, 0x7a, 0x00, 0x06, 0x2c, 0xc0, 0x0d, 0xc0, - 0x0d, 0xc0, 0x0d, 0xc0, 0x0d, 0x00, 0x96, 0x00, 0x96, 0x00, 0x96, - 0x00, 0x96, 0x00, 0x96, 0x00, 0x96, 0x00, 0x96, 0x00, 0x96, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x80, 0x80, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, - 0x09, 0x00, 0x00, 0xc9, 0xff, 0xd8, 0xff, 0x00, 0x00, 0x00, 0x01, - 0x10, 0x01, 0x10, 0x01, 0x10, 0x01, 0x10, 0x01, 0xf0, 0x00, 0xf0, - 0x00, 0xf0, 0x00, 0xf0, 0x00, 0xd0, 0x00, 0xd0, 0x00, 0xd0, 0x00, - 0xd0, 0x00, 0x54, 0x01, 0xab, 0xf6, 0xc0, 0x42, 0xc0, 0x42, 0xc0, - 0x42, 0xc0, 0x42, 0x00, 0xcb, 0x00, 0xcb, 0x00, 0xcb, 0x00, 0xcb, - 0x00, 0xcb, 0x00, 0xcb, 0x00, 0xcb, 0x00, 0xcb, 0x22, 0x01, 0x37, - 0xa9, 0xc0, 0x33, 0xc0, 0x33, 0xc0, 0x33, 0xc0, 0x33, 0x00, 0xbc, - 0x00, 0xbc, 0x00, 0xbc, 0x00, 0xbc, 0x00, 0xbc, 0x00, 0xbc, 0x00, - 0xbc, 0x00, 0xbc, 0xfb, 0x00, 0xca, 0x79, 0xc0, 0x2b, 0xc0, 0x2b, - 0xc0, 0x2b, 0xc0, 0x2b, 0x00, 0xb4, 0x00, 0xb4, 0x00, 0xb4, 0x00, - 0xb4, 0x00, 0xb4, 0x00, 0xb4, 0x00, 0xb4, 0x00, 0xb4, 0xd0, 0x00, - 0x5d, 0x54, 0xc0, 0x21, 0xc0, 0x21, 0xc0, 0x21, 0xc0, 0x21, 0x00, - 0xaa, 0x00, 0xaa, 0x00, 0xaa, 0x00, 0xaa, 0x00, 0xaa, 0x00, 0xaa, - 0x00, 0xaa, 0x00, 0xaa, 0xa7, 0x00, 0xa9, 0x3d, 0xc0, 0x17, 0xc0, - 0x17, 0xc0, 0x17, 0xc0, 0x17, 0x00, 0xa0, 0x00, 0xa0, 0x00, 0xa0, - 0x00, 0xa0, 0x00, 0xa0, 0x00, 0xa0, 0x00, 0xa0, 0x00, 0xa0, 0x7a, - 0x00, 0x06, 0x2c, 0xc0, 0x0d, 0xc0, 0x0d, 0xc0, 0x0d, 0xc0, 0x0d, - 0x00, 0x96, 0x00, 0x96, 0x00, 0x96, 0x00, 0x96, 0x00, 0x96, 0x00, - 0x96, 0x00, 0x96, 0x00, 0x96, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x06, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x7b, 0x09, 0x00, 0x00, 0xc9, 0xff, - 0xd8, 0xff, 0x00, 0x00, 0x00, 0x01, 0x10, 0x01, 0x10, 0x01, 0x10, - 0x01, 0x10, 0x01, 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, - 0xd0, 0x00, 0xd0, 0x00, 0xd0, 0x00, 0xd0, 0x00, 0x54, 0x01, 0xab, - 0xf6, 0xc0, 0x42, 0xc0, 0x42, 0xc0, 0x42, 0xc0, 0x42, 0x00, 0xcb, - 0x00, 0xcb, 0x00, 0xcb, 0x00, 0xcb, 0x00, 0xcb, 0x00, 0xcb, 0x00, - 0xcb, 0x00, 0xcb, 0x22, 0x01, 0x37, 0xa9, 0xc0, 0x33, 0xc0, 0x33, - 0xc0, 0x33, 0xc0, 0x33, 0x00, 0xbc, 0x00, 0xbc, 0x00, 0xbc, 0x00, - 0xbc, 0x00, 0xbc, 0x00, 0xbc, 0x00, 0xbc, 0x00, 0xbc, 0xfb, 0x00, - 0xca, 0x79, 0xc0, 0x2b, 0xc0, 0x2b, 0xc0, 0x2b, 0xc0, 0x2b, 0x00, - 0xb4, 0x00, 0xb4, 0x00, 0xb4, 0x00, 0xb4, 0x00, 0xb4, 0x00, 0xb4, - 0x00, 0xb4, 0x00, 0xb4, 0xd0, 0x00, 0x5d, 0x54, 0xc0, 0x21, 0xc0, - 0x21, 0xc0, 0x21, 0xc0, 0x21, 0x00, 0xaa, 0x00, 0xaa, 0x00, 0xaa, - 0x00, 0xaa, 0x00, 0xaa, 0x00, 0xaa, 0x00, 0xaa, 0x00, 0xaa, 0xa7, - 0x00, 0xa9, 0x3d, 0xc0, 0x17, 0xc0, 0x17, 0xc0, 0x17, 0xc0, 0x17, - 0x00, 0xa0, 0x00, 0xa0, 0x00, 0xa0, 0x00, 0xa0, 0x00, 0xa0, 0x00, - 0xa0, 0x00, 0xa0, 0x00, 0xa0, 0x7a, 0x00, 0x06, 0x2c, 0xc0, 0x0d, - 0xc0, 0x0d, 0xc0, 0x0d, 0xc0, 0x0d, 0x00, 0x96, 0x00, 0x96, 0x00, - 0x96, 0x00, 0x96, 0x00, 0x96, 0x00, 0x96, 0x00, 0x96, 0x00, 0x96, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x80, - 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x80, 0x09, 0x00, 0x00, 0xc9, 0xff, 0xd8, 0xff, 0x00, 0x00, 0x00, - 0x01, 0x10, 0x01, 0x10, 0x01, 0x10, 0x01, 0x10, 0x01, 0xf0, 0x00, - 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, 0xd0, 0x00, 0xd0, 0x00, 0xd0, - 0x00, 0xd0, 0x00, 0x54, 0x01, 0xab, 0xf6, 0xc0, 0x42, 0xc0, 0x42, - 0xc0, 0x42, 0xc0, 0x42, 0x00, 0xcb, 0x00, 0xcb, 0x00, 0xcb, 0x00, - 0xcb, 0x00, 0xcb, 0x00, 0xcb, 0x00, 0xcb, 0x00, 0xcb, 0x22, 0x01, - 0x37, 0xa9, 0xc0, 0x33, 0xc0, 0x33, 0xc0, 0x33, 0xc0, 0x33, 0x00, - 0xbc, 0x00, 0xbc, 0x00, 0xbc, 0x00, 0xbc, 0x00, 0xbc, 0x00, 0xbc, - 0x00, 0xbc, 0x00, 0xbc, 0xfb, 0x00, 0xca, 0x79, 0xc0, 0x2b, 0xc0, - 0x2b, 0xc0, 0x2b, 0xc0, 0x2b, 0x00, 0xb4, 0x00, 0xb4, 0x00, 0xb4, - 0x00, 0xb4, 0x00, 0xb4, 0x00, 0xb4, 0x00, 0xb4, 0x00, 0xb4, 0xd0, - 0x00, 0x5d, 0x54, 0xc0, 0x21, 0xc0, 0x21, 0xc0, 0x21, 0xc0, 0x21, - 0x00, 0xaa, 0x00, 0xaa, 0x00, 0xaa, 0x00, 0xaa, 0x00, 0xaa, 0x00, - 0xaa, 0x00, 0xaa, 0x00, 0xaa, 0xa7, 0x00, 0xa9, 0x3d, 0xc0, 0x17, - 0xc0, 0x17, 0xc0, 0x17, 0xc0, 0x17, 0x00, 0xa0, 0x00, 0xa0, 0x00, - 0xa0, 0x00, 0xa0, 0x00, 0xa0, 0x00, 0xa0, 0x00, 0xa0, 0x00, 0xa0, - 0x7a, 0x00, 0x06, 0x2c, 0xc0, 0x0d, 0xc0, 0x0d, 0xc0, 0x0d, 0xc0, - 0x0d, 0x00, 0x96, 0x00, 0x96, 0x00, 0x96, 0x00, 0x96, 0x00, 0x96, - 0x00, 0x96, 0x00, 0x96, 0x00, 0x96, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x06, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x85, 0x09, 0x00, 0x00, 0xc9, - 0xff, 0xd8, 0xff, 0x00, 0x00, 0x00, 0x01, 0x10, 0x01, 0x10, 0x01, - 0x10, 0x01, 0x10, 0x01, 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, 0xf0, - 0x00, 0xd0, 0x00, 0xd0, 0x00, 0xd0, 0x00, 0xd0, 0x00, 0x54, 0x01, - 0xab, 0xf6, 0xc0, 0x42, 0xc0, 0x42, 0xc0, 0x42, 0xc0, 0x42, 0x00, - 0xcb, 0x00, 0xcb, 0x00, 0xcb, 0x00, 0xcb, 0x00, 0xcb, 0x00, 0xcb, - 0x00, 0xcb, 0x00, 0xcb, 0x22, 0x01, 0x37, 0xa9, 0xc0, 0x33, 0xc0, - 0x33, 0xc0, 0x33, 0xc0, 0x33, 0x00, 0xbc, 0x00, 0xbc, 0x00, 0xbc, - 0x00, 0xbc, 0x00, 0xbc, 0x00, 0xbc, 0x00, 0xbc, 0x00, 0xbc, 0xfb, - 0x00, 0xca, 0x79, 0xc0, 0x2b, 0xc0, 0x2b, 0xc0, 0x2b, 0xc0, 0x2b, - 0x00, 0xb4, 0x00, 0xb4, 0x00, 0xb4, 0x00, 0xb4, 0x00, 0xb4, 0x00, - 0xb4, 0x00, 0xb4, 0x00, 0xb4, 0xd0, 0x00, 0x5d, 0x54, 0xc0, 0x21, - 0xc0, 0x21, 0xc0, 0x21, 0xc0, 0x21, 0x00, 0xaa, 0x00, 0xaa, 0x00, - 0xaa, 0x00, 0xaa, 0x00, 0xaa, 0x00, 0xaa, 0x00, 0xaa, 0x00, 0xaa, - 0xa7, 0x00, 0xa9, 0x3d, 0xc0, 0x17, 0xc0, 0x17, 0xc0, 0x17, 0xc0, - 0x17, 0x00, 0xa0, 0x00, 0xa0, 0x00, 0xa0, 0x00, 0xa0, 0x00, 0xa0, - 0x00, 0xa0, 0x00, 0xa0, 0x00, 0xa0, 0x7a, 0x00, 0x06, 0x2c, 0xc0, - 0x0d, 0xc0, 0x0d, 0xc0, 0x0d, 0xc0, 0x0d, 0x00, 0x96, 0x00, 0x96, - 0x00, 0x96, 0x00, 0x96, 0x00, 0x96, 0x00, 0x96, 0x00, 0x96, 0x00, - 0x96, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, - 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x8a, 0x09, 0x00, 0x00, 0xc9, 0xff, 0xd8, 0xff, 0x00, 0x00, - 0x00, 0x01, 0x10, 0x01, 0x10, 0x01, 0x10, 0x01, 0x10, 0x01, 0xf0, - 0x00, 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, 0xd0, 0x00, 0xd0, 0x00, - 0xd0, 0x00, 0xd0, 0x00, 0x54, 0x01, 0xab, 0xf6, 0xc0, 0x42, 0xc0, - 0x42, 0xc0, 0x42, 0xc0, 0x42, 0x00, 0xcb, 0x00, 0xcb, 0x00, 0xcb, - 0x00, 0xcb, 0x00, 0xcb, 0x00, 0xcb, 0x00, 0xcb, 0x00, 0xcb, 0x22, - 0x01, 0x37, 0xa9, 0xc0, 0x33, 0xc0, 0x33, 0xc0, 0x33, 0xc0, 0x33, - 0x00, 0xbc, 0x00, 0xbc, 0x00, 0xbc, 0x00, 0xbc, 0x00, 0xbc, 0x00, - 0xbc, 0x00, 0xbc, 0x00, 0xbc, 0xfb, 0x00, 0xca, 0x79, 0xc0, 0x2b, - 0xc0, 0x2b, 0xc0, 0x2b, 0xc0, 0x2b, 0x00, 0xb4, 0x00, 0xb4, 0x00, - 0xb4, 0x00, 0xb4, 0x00, 0xb4, 0x00, 0xb4, 0x00, 0xb4, 0x00, 0xb4, - 0xd0, 0x00, 0x5d, 0x54, 0xc0, 0x21, 0xc0, 0x21, 0xc0, 0x21, 0xc0, - 0x21, 0x00, 0xaa, 0x00, 0xaa, 0x00, 0xaa, 0x00, 0xaa, 0x00, 0xaa, - 0x00, 0xaa, 0x00, 0xaa, 0x00, 0xaa, 0xa7, 0x00, 0xa9, 0x3d, 0xc0, - 0x17, 0xc0, 0x17, 0xc0, 0x17, 0xc0, 0x17, 0x00, 0xa0, 0x00, 0xa0, - 0x00, 0xa0, 0x00, 0xa0, 0x00, 0xa0, 0x00, 0xa0, 0x00, 0xa0, 0x00, - 0xa0, 0x7a, 0x00, 0x06, 0x2c, 0xc0, 0x0d, 0xc0, 0x0d, 0xc0, 0x0d, - 0xc0, 0x0d, 0x00, 0x96, 0x00, 0x96, 0x00, 0x96, 0x00, 0x96, 0x00, - 0x96, 0x00, 0x96, 0x00, 0x96, 0x00, 0x96, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x80, 0x80, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8f, 0x09, 0x00, 0x00, - 0xc9, 0xff, 0xd8, 0xff, 0x00, 0x00, 0x00, 0x01, 0x10, 0x01, 0x10, - 0x01, 0x10, 0x01, 0x10, 0x01, 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, - 0xf0, 0x00, 0xd0, 0x00, 0xd0, 0x00, 0xd0, 0x00, 0xd0, 0x00, 0x54, - 0x01, 0xab, 0xf6, 0xc0, 0x42, 0xc0, 0x42, 0xc0, 0x42, 0xc0, 0x42, - 0x00, 0xcb, 0x00, 0xcb, 0x00, 0xcb, 0x00, 0xcb, 0x00, 0xcb, 0x00, - 0xcb, 0x00, 0xcb, 0x00, 0xcb, 0x22, 0x01, 0x37, 0xa9, 0xc0, 0x33, - 0xc0, 0x33, 0xc0, 0x33, 0xc0, 0x33, 0x00, 0xbc, 0x00, 0xbc, 0x00, - 0xbc, 0x00, 0xbc, 0x00, 0xbc, 0x00, 0xbc, 0x00, 0xbc, 0x00, 0xbc, - 0xfb, 0x00, 0xca, 0x79, 0xc0, 0x2b, 0xc0, 0x2b, 0xc0, 0x2b, 0xc0, - 0x2b, 0x00, 0xb4, 0x00, 0xb4, 0x00, 0xb4, 0x00, 0xb4, 0x00, 0xb4, - 0x00, 0xb4, 0x00, 0xb4, 0x00, 0xb4, 0xd0, 0x00, 0x5d, 0x54, 0xc0, - 0x21, 0xc0, 0x21, 0xc0, 0x21, 0xc0, 0x21, 0x00, 0xaa, 0x00, 0xaa, - 0x00, 0xaa, 0x00, 0xaa, 0x00, 0xaa, 0x00, 0xaa, 0x00, 0xaa, 0x00, - 0xaa, 0xa7, 0x00, 0xa9, 0x3d, 0xc0, 0x17, 0xc0, 0x17, 0xc0, 0x17, - 0xc0, 0x17, 0x00, 0xa0, 0x00, 0xa0, 0x00, 0xa0, 0x00, 0xa0, 0x00, - 0xa0, 0x00, 0xa0, 0x00, 0xa0, 0x00, 0xa0, 0x7a, 0x00, 0x06, 0x2c, - 0xc0, 0x0d, 0xc0, 0x0d, 0xc0, 0x0d, 0xc0, 0x0d, 0x00, 0x96, 0x00, - 0x96, 0x00, 0x96, 0x00, 0x96, 0x00, 0x96, 0x00, 0x96, 0x00, 0x96, - 0x00, 0x96, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x06, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x94, 0x09, 0x00, 0x00, 0xc9, 0xff, 0xd8, 0xff, 0x00, - 0x00, 0x00, 0x01, 0x10, 0x01, 0x10, 0x01, 0x10, 0x01, 0x10, 0x01, - 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, 0xd0, 0x00, 0xd0, - 0x00, 0xd0, 0x00, 0xd0, 0x00, 0x54, 0x01, 0xab, 0xf6, 0xc0, 0x42, - 0xc0, 0x42, 0xc0, 0x42, 0xc0, 0x42, 0x00, 0xcb, 0x00, 0xcb, 0x00, - 0xcb, 0x00, 0xcb, 0x00, 0xcb, 0x00, 0xcb, 0x00, 0xcb, 0x00, 0xcb, - 0x22, 0x01, 0x37, 0xa9, 0xc0, 0x33, 0xc0, 0x33, 0xc0, 0x33, 0xc0, - 0x33, 0x00, 0xbc, 0x00, 0xbc, 0x00, 0xbc, 0x00, 0xbc, 0x00, 0xbc, - 0x00, 0xbc, 0x00, 0xbc, 0x00, 0xbc, 0xfb, 0x00, 0xca, 0x79, 0xc0, - 0x2b, 0xc0, 0x2b, 0xc0, 0x2b, 0xc0, 0x2b, 0x00, 0xb4, 0x00, 0xb4, - 0x00, 0xb4, 0x00, 0xb4, 0x00, 0xb4, 0x00, 0xb4, 0x00, 0xb4, 0x00, - 0xb4, 0xd0, 0x00, 0x5d, 0x54, 0xc0, 0x21, 0xc0, 0x21, 0xc0, 0x21, - 0xc0, 0x21, 0x00, 0xaa, 0x00, 0xaa, 0x00, 0xaa, 0x00, 0xaa, 0x00, - 0xaa, 0x00, 0xaa, 0x00, 0xaa, 0x00, 0xaa, 0xa7, 0x00, 0xa9, 0x3d, - 0xc0, 0x17, 0xc0, 0x17, 0xc0, 0x17, 0xc0, 0x17, 0x00, 0xa0, 0x00, - 0xa0, 0x00, 0xa0, 0x00, 0xa0, 0x00, 0xa0, 0x00, 0xa0, 0x00, 0xa0, - 0x00, 0xa0, 0x7a, 0x00, 0x06, 0x2c, 0xc0, 0x0d, 0xc0, 0x0d, 0xc0, - 0x0d, 0xc0, 0x0d, 0x00, 0x96, 0x00, 0x96, 0x00, 0x96, 0x00, 0x96, - 0x00, 0x96, 0x00, 0x96, 0x00, 0x96, 0x00, 0x96, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x80, 0x80, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x99, 0x09, 0x00, - 0x00, 0xc9, 0xff, 0xd8, 0xff, 0x00, 0x00, 0x00, 0x01, 0x10, 0x01, - 0x10, 0x01, 0x10, 0x01, 0x10, 0x01, 0xf0, 0x00, 0xf0, 0x00, 0xf0, - 0x00, 0xf0, 0x00, 0xd0, 0x00, 0xd0, 0x00, 0xd0, 0x00, 0xd0, 0x00, - 0x54, 0x01, 0xab, 0xf6, 0xc0, 0x42, 0xc0, 0x42, 0xc0, 0x42, 0xc0, - 0x42, 0x00, 0xcb, 0x00, 0xcb, 0x00, 0xcb, 0x00, 0xcb, 0x00, 0xcb, - 0x00, 0xcb, 0x00, 0xcb, 0x00, 0xcb, 0x22, 0x01, 0x37, 0xa9, 0xc0, - 0x33, 0xc0, 0x33, 0xc0, 0x33, 0xc0, 0x33, 0x00, 0xbc, 0x00, 0xbc, - 0x00, 0xbc, 0x00, 0xbc, 0x00, 0xbc, 0x00, 0xbc, 0x00, 0xbc, 0x00, - 0xbc, 0xfb, 0x00, 0xca, 0x79, 0xc0, 0x2b, 0xc0, 0x2b, 0xc0, 0x2b, - 0xc0, 0x2b, 0x00, 0xb4, 0x00, 0xb4, 0x00, 0xb4, 0x00, 0xb4, 0x00, - 0xb4, 0x00, 0xb4, 0x00, 0xb4, 0x00, 0xb4, 0xd0, 0x00, 0x5d, 0x54, - 0xc0, 0x21, 0xc0, 0x21, 0xc0, 0x21, 0xc0, 0x21, 0x00, 0xaa, 0x00, - 0xaa, 0x00, 0xaa, 0x00, 0xaa, 0x00, 0xaa, 0x00, 0xaa, 0x00, 0xaa, - 0x00, 0xaa, 0xa7, 0x00, 0xa9, 0x3d, 0xc0, 0x17, 0xc0, 0x17, 0xc0, - 0x17, 0xc0, 0x17, 0x00, 0xa0, 0x00, 0xa0, 0x00, 0xa0, 0x00, 0xa0, - 0x00, 0xa0, 0x00, 0xa0, 0x00, 0xa0, 0x00, 0xa0, 0x7a, 0x00, 0x06, - 0x2c, 0xc0, 0x0d, 0xc0, 0x0d, 0xc0, 0x0d, 0xc0, 0x0d, 0x00, 0x96, - 0x00, 0x96, 0x00, 0x96, 0x00, 0x96, 0x00, 0x96, 0x00, 0x96, 0x00, - 0x96, 0x00, 0x96, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x06, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x9e, 0x09, 0x00, 0x00, 0xc9, 0xff, 0xd8, 0xff, - 0x00, 0x00, 0x00, 0x01, 0x10, 0x01, 0x10, 0x01, 0x10, 0x01, 0x10, - 0x01, 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, 0xd0, 0x00, - 0xd0, 0x00, 0xd0, 0x00, 0xd0, 0x00, 0x54, 0x01, 0xab, 0xf6, 0xc0, - 0x42, 0xc0, 0x42, 0xc0, 0x42, 0xc0, 0x42, 0x00, 0xcb, 0x00, 0xcb, - 0x00, 0xcb, 0x00, 0xcb, 0x00, 0xcb, 0x00, 0xcb, 0x00, 0xcb, 0x00, - 0xcb, 0x22, 0x01, 0x37, 0xa9, 0xc0, 0x33, 0xc0, 0x33, 0xc0, 0x33, - 0xc0, 0x33, 0x00, 0xbc, 0x00, 0xbc, 0x00, 0xbc, 0x00, 0xbc, 0x00, - 0xbc, 0x00, 0xbc, 0x00, 0xbc, 0x00, 0xbc, 0xfb, 0x00, 0xca, 0x79, - 0xc0, 0x2b, 0xc0, 0x2b, 0xc0, 0x2b, 0xc0, 0x2b, 0x00, 0xb4, 0x00, - 0xb4, 0x00, 0xb4, 0x00, 0xb4, 0x00, 0xb4, 0x00, 0xb4, 0x00, 0xb4, - 0x00, 0xb4, 0xd0, 0x00, 0x5d, 0x54, 0xc0, 0x21, 0xc0, 0x21, 0xc0, - 0x21, 0xc0, 0x21, 0x00, 0xaa, 0x00, 0xaa, 0x00, 0xaa, 0x00, 0xaa, - 0x00, 0xaa, 0x00, 0xaa, 0x00, 0xaa, 0x00, 0xaa, 0xa7, 0x00, 0xa9, - 0x3d, 0xc0, 0x17, 0xc0, 0x17, 0xc0, 0x17, 0xc0, 0x17, 0x00, 0xa0, - 0x00, 0xa0, 0x00, 0xa0, 0x00, 0xa0, 0x00, 0xa0, 0x00, 0xa0, 0x00, - 0xa0, 0x00, 0xa0, 0x7a, 0x00, 0x06, 0x2c, 0xc0, 0x0d, 0xc0, 0x0d, - 0xc0, 0x0d, 0xc0, 0x0d, 0x00, 0x96, 0x00, 0x96, 0x00, 0x96, 0x00, - 0x96, 0x00, 0x96, 0x00, 0x96, 0x00, 0x96, 0x00, 0x96, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x80, 0x80, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00 }; - -static const u8 default_cal_rssi[] = { - 0x0a, 0x01, 0x72, 0xfe, 0x1a, 0x00, 0x00, 0x00, 0x0a, 0x01, 0x72, - 0xfe, 0x1a, 0x00, 0x00, 0x00, 0x0a, 0x01, 0x72, 0xfe, 0x1a, 0x00, - 0x00, 0x00, 0x0a, 0x01, 0x72, 0xfe, 0x1a, 0x00, 0x00, 0x00, 0x0a, - 0x01, 0x72, 0xfe, 0x1a, 0x00, 0x00, 0x00, 0x0a, 0x01, 0x72, 0xfe, - 0x1a, 0x00, 0x00, 0x00, 0x0a, 0x01, 0x72, 0xfe, 0x1a, 0x00, 0x00, - 0x00, 0x0a, 0x01, 0x72, 0xfe, 0x1a, 0x00, 0x00, 0x00, 0x0a, 0x01, - 0x72, 0xfe, 0x1a, 0x00, 0x00, 0x00, 0x0a, 0x01, 0x72, 0xfe, 0x1a, - 0x00, 0x00, 0x00, 0x0a, 0x01, 0x72, 0xfe, 0x1a, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00 }; - -static void stlc45xx_tx_edcf(struct stlc45xx *stlc); -static void stlc45xx_tx_setup(struct stlc45xx *stlc); -static void stlc45xx_tx_scan(struct stlc45xx *stlc); -static void stlc45xx_tx_psm(struct stlc45xx *stlc, bool enable); -static int stlc45xx_tx_nullfunc(struct stlc45xx *stlc, bool powersave); -static int stlc45xx_tx_pspoll(struct stlc45xx *stlc, bool powersave); - -static ssize_t stlc45xx_sysfs_show_cal_rssi(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - struct stlc45xx *stlc = dev_get_drvdata(dev); - ssize_t len; - - stlc45xx_debug(DEBUG_FUNC, "%s", __func__); - - len = PAGE_SIZE; - - mutex_lock(&stlc->mutex); - - if (stlc->cal_rssi) - hex_dump_to_buffer(stlc->cal_rssi, RSSI_CAL_ARRAY_LEN, 16, - 2, buf, len, 0); - mutex_unlock(&stlc->mutex); - - len = strlen(buf); - - return len; -} - -static ssize_t stlc45xx_sysfs_store_cal_rssi(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) -{ - struct stlc45xx *stlc = dev_get_drvdata(dev); - - stlc45xx_debug(DEBUG_FUNC, "%s", __func__); - - mutex_lock(&stlc->mutex); - - if (count != RSSI_CAL_ARRAY_LEN) { - stlc45xx_error("invalid cal_rssi length: %zu", count); - count = 0; - goto out_unlock; - } - - kfree(stlc->cal_rssi); - - stlc->cal_rssi = kmemdup(buf, RSSI_CAL_ARRAY_LEN, GFP_KERNEL); - - if (!stlc->cal_rssi) { - stlc45xx_error("failed to allocate memory for cal_rssi"); - count = 0; - goto out_unlock; - } - - out_unlock: - mutex_unlock(&stlc->mutex); - - return count; -} - -static ssize_t stlc45xx_sysfs_show_cal_channels(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - struct stlc45xx *stlc = dev_get_drvdata(dev); - ssize_t len; - - stlc45xx_debug(DEBUG_FUNC, "%s", __func__); - - len = PAGE_SIZE; - - mutex_lock(&stlc->mutex); - - if (stlc->cal_channels) - hex_dump_to_buffer(stlc->cal_channels, CHANNEL_CAL_ARRAY_LEN, - 16, 2, buf, len, 0); - - mutex_unlock(&stlc->mutex); - - len = strlen(buf); - - return len; -} - -static ssize_t stlc45xx_sysfs_store_cal_channels(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) -{ - struct stlc45xx *stlc = dev_get_drvdata(dev); - - stlc45xx_debug(DEBUG_FUNC, "%s", __func__); - - mutex_lock(&stlc->mutex); - - if (count != CHANNEL_CAL_ARRAY_LEN) { - stlc45xx_error("invalid cal_channels size: %zu ", count); - count = 0; - goto out_unlock; - } - - kfree(stlc->cal_channels); - - stlc->cal_channels = kmemdup(buf, count, GFP_KERNEL); - - if (!stlc->cal_channels) { - stlc45xx_error("failed to allocate memory for cal_channels"); - count = 0; - goto out_unlock; - } - -out_unlock: - mutex_unlock(&stlc->mutex); - - return count; -} - -static ssize_t stlc45xx_sysfs_show_tx_buf(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - struct stlc45xx *stlc = dev_get_drvdata(dev); - struct txbuffer *entry; - ssize_t len = 0; - - stlc45xx_debug(DEBUG_FUNC, "%s()", __func__); - - mutex_lock(&stlc->mutex); - - list_for_each_entry(entry, &stlc->tx_sent, tx_list) { - len += sprintf(buf + len, "0x%x: 0x%x-0x%x\n", - entry->handle, entry->start, - entry->end); - } - - mutex_unlock(&stlc->mutex); - - return len; -} - -static DEVICE_ATTR(cal_rssi, S_IRUGO | S_IWUSR, - stlc45xx_sysfs_show_cal_rssi, - stlc45xx_sysfs_store_cal_rssi); -static DEVICE_ATTR(cal_channels, S_IRUGO | S_IWUSR, - stlc45xx_sysfs_show_cal_channels, - stlc45xx_sysfs_store_cal_channels); -static DEVICE_ATTR(tx_buf, S_IRUGO, stlc45xx_sysfs_show_tx_buf, NULL); - -static void stlc45xx_spi_read(struct stlc45xx *stlc, unsigned long addr, - void *buf, size_t len) -{ - struct spi_transfer t[2]; - struct spi_message m; - - /* We first push the address */ - addr = (addr << 8) | ADDR_READ_BIT_15; - - spi_message_init(&m); - memset(t, 0, sizeof(t)); - - t[0].tx_buf = &addr; - t[0].len = 2; - spi_message_add_tail(&t[0], &m); - - t[1].rx_buf = buf; - t[1].len = len; - spi_message_add_tail(&t[1], &m); - - spi_sync(stlc->spi, &m); -} - - -static void stlc45xx_spi_write(struct stlc45xx *stlc, unsigned long addr, - void *buf, size_t len) -{ - struct spi_transfer t[3]; - struct spi_message m; - u16 last_word; - - /* We first push the address */ - addr = addr << 8; - - spi_message_init(&m); - memset(t, 0, sizeof(t)); - - t[0].tx_buf = &addr; - t[0].len = 2; - spi_message_add_tail(&t[0], &m); - - t[1].tx_buf = buf; - t[1].len = len; - spi_message_add_tail(&t[1], &m); - - if (len % 2) { - last_word = ((u8 *)buf)[len - 1]; - - t[2].tx_buf = &last_word; - t[2].len = 2; - spi_message_add_tail(&t[2], &m); - } - - spi_sync(stlc->spi, &m); -} - -static u16 stlc45xx_read16(struct stlc45xx *stlc, unsigned long addr) -{ - u16 val; - - stlc45xx_spi_read(stlc, addr, &val, sizeof(val)); - - return val; -} - -static u32 stlc45xx_read32(struct stlc45xx *stlc, unsigned long addr) -{ - u32 val; - - stlc45xx_spi_read(stlc, addr, &val, sizeof(val)); - - return val; -} - -static void stlc45xx_write16(struct stlc45xx *stlc, unsigned long addr, u16 val) -{ - stlc45xx_spi_write(stlc, addr, &val, sizeof(val)); -} - -static void stlc45xx_write32(struct stlc45xx *stlc, unsigned long addr, u32 val) -{ - stlc45xx_spi_write(stlc, addr, &val, sizeof(val)); -} - -struct stlc45xx_spi_reg { - u16 address; - u16 length; - char *name; -}; - -/* caller must hold tx_lock */ -static void stlc45xx_txbuffer_dump(struct stlc45xx *stlc) -{ - struct txbuffer *txbuffer; - char *buf, *pos; - int buf_len, l, count; - - if (!(DEBUG_LEVEL & DEBUG_TXBUFFER)) - return; - - stlc45xx_debug(DEBUG_FUNC, "%s()", __func__); - - buf_len = 500; - buf = kmalloc(buf_len, GFP_ATOMIC); - if (!buf) - return; - - pos = buf; - count = 0; - - list_for_each_entry(txbuffer, &stlc->txbuffer, buffer_list) { - l = snprintf(pos, buf_len, "0x%x-0x%x,", - txbuffer->start, txbuffer->end); - /* drop the null byte */ - pos += l; - buf_len -= l; - count++; - } - - if (count == 0) - *pos = '\0'; - else - *--pos = '\0'; - - stlc45xx_debug(DEBUG_TXBUFFER, "txbuffer: in buffer %d regions: %s", - count, buf); - - kfree(buf); -} - -/* caller must hold tx_lock */ -static int stlc45xx_txbuffer_find(struct stlc45xx *stlc, size_t len) -{ - struct txbuffer *txbuffer; - int pos; - - stlc45xx_debug(DEBUG_FUNC, "%s()", __func__); - - pos = FIRMWARE_TXBUFFER_START; - - if (list_empty(&stlc->txbuffer)) - goto out; - - /* - * the entries in txbuffer must be in the same order as they are in - * the real buffer - */ - list_for_each_entry(txbuffer, &stlc->txbuffer, buffer_list) { - if (pos + len < txbuffer->start) - break; - pos = ALIGN(txbuffer->end + 1, 4); - } - - if (pos + len > FIRMWARE_TXBUFFER_END) - /* not enough room */ - pos = -1; - - stlc45xx_debug(DEBUG_TXBUFFER, "txbuffer: find %zu B: 0x%x", len, pos); - -out: - return pos; -} - -static int stlc45xx_txbuffer_add(struct stlc45xx *stlc, - struct txbuffer *txbuffer) -{ - struct txbuffer *r, *prev = NULL; - - if (list_empty(&stlc->txbuffer)) { - list_add(&txbuffer->buffer_list, &stlc->txbuffer); - return 0; - } - - r = list_first_entry(&stlc->txbuffer, struct txbuffer, buffer_list); - - if (txbuffer->start < r->start) { - /* add to the beginning of the list */ - list_add(&txbuffer->buffer_list, &stlc->txbuffer); - return 0; - } - - prev = NULL; - list_for_each_entry(r, &stlc->txbuffer, buffer_list) { - /* skip first entry, we checked for that above */ - if (!prev) { - prev = r; - continue; - } - - /* double-check overlaps */ - WARN_ON_ONCE(txbuffer->start >= r->start && - txbuffer->start <= r->end); - WARN_ON_ONCE(txbuffer->end >= r->start && - txbuffer->end <= r->end); - - if (prev->end < txbuffer->start && - txbuffer->end < r->start) { - /* insert at this spot */ - list_add_tail(&txbuffer->buffer_list, &r->buffer_list); - return 0; - } - - prev = r; - } - - /* not found */ - list_add_tail(&txbuffer->buffer_list, &stlc->txbuffer); - - return 0; - -} - -/* caller must hold tx_lock */ -static struct txbuffer *stlc45xx_txbuffer_alloc(struct stlc45xx *stlc, - size_t frame_len) -{ - struct txbuffer *entry = NULL; - size_t len; - int pos; - - stlc45xx_debug(DEBUG_FUNC, "%s()", __func__); - - len = FIRMWARE_TXBUFFER_HEADER + frame_len + FIRMWARE_TXBUFFER_TRAILER; - pos = stlc45xx_txbuffer_find(stlc, len); - - if (pos < 0) - return NULL; - - WARN_ON_ONCE(pos + len > FIRMWARE_TXBUFFER_END); - WARN_ON_ONCE(pos < FIRMWARE_TXBUFFER_START); - - entry = kmalloc(sizeof(*entry), GFP_ATOMIC); - entry->start = pos; - entry->frame_start = pos + FIRMWARE_TXBUFFER_HEADER; - entry->end = entry->start + len - 1; - - stlc45xx_debug(DEBUG_TXBUFFER, "txbuffer: allocated 0x%x-0x%x", - entry->start, entry->end); - - stlc45xx_txbuffer_add(stlc, entry); - - stlc45xx_txbuffer_dump(stlc); - - return entry; -} - -/* caller must hold tx_lock */ -static void stlc45xx_txbuffer_free(struct stlc45xx *stlc, - struct txbuffer *txbuffer) -{ - stlc45xx_debug(DEBUG_FUNC, "%s()", __func__); - - stlc45xx_debug(DEBUG_TXBUFFER, "txbuffer: freed 0x%x-0x%x", - txbuffer->start, txbuffer->end); - - list_del(&txbuffer->buffer_list); - kfree(txbuffer); -} - - -static int stlc45xx_wait_bit(struct stlc45xx *stlc, u16 reg, u32 mask, - u32 expected) -{ - int i; - char buffer[4]; - - for (i = 0; i < 2000; i++) { - stlc45xx_spi_read(stlc, reg, buffer, sizeof(buffer)); - if (((*(u32 *)buffer) & mask) == expected) - return 1; - msleep(1); - } - - return 0; -} - -static int stlc45xx_request_firmware(struct stlc45xx *stlc) -{ - const struct firmware *fw; - int ret; - - /* FIXME: should driver use it's own struct device? */ - ret = request_firmware(&fw, "3826.arm", &stlc->spi->dev); - - if (ret < 0) { - stlc45xx_error("request_firmware() failed: %d", ret); - return ret; - } - - if (fw->size % 4) { - stlc45xx_error("firmware size is not multiple of 32bit: %zu", - fw->size); - return -EILSEQ; /* Illegal byte sequence */; - } - - if (fw->size < 1000) { - stlc45xx_error("firmware is too small: %zu", fw->size); - return -EILSEQ; - } - - stlc->fw = kmemdup(fw->data, fw->size, GFP_KERNEL); - if (!stlc->fw) { - stlc45xx_error("could not allocate memory for firmware"); - return -ENOMEM; - } - - stlc->fw_len = fw->size; - - release_firmware(fw); - - return 0; -} - -static int stlc45xx_upload_firmware(struct stlc45xx *stlc) -{ - struct s_dma_regs dma_regs; - unsigned long fw_len, fw_addr; - long _fw_len; - int ret; - - stlc45xx_debug(DEBUG_FUNC, "%s", __func__); - - if (!stlc->fw) { - ret = stlc45xx_request_firmware(stlc); - if (ret < 0) - return ret; - } - - /* stop the device */ - stlc45xx_write16(stlc, SPI_ADRS_DEV_CTRL_STAT, - SPI_CTRL_STAT_HOST_OVERRIDE | SPI_CTRL_STAT_HOST_RESET - | SPI_CTRL_STAT_START_HALTED); - - msleep(TARGET_BOOT_SLEEP); - - stlc45xx_write16(stlc, SPI_ADRS_DEV_CTRL_STAT, - SPI_CTRL_STAT_HOST_OVERRIDE - | SPI_CTRL_STAT_START_HALTED); - - msleep(TARGET_BOOT_SLEEP); - - fw_addr = FIRMWARE_ADDRESS; - fw_len = stlc->fw_len; - - while (fw_len > 0) { - _fw_len = (fw_len > SPI_MAX_PACKET_SIZE) - ? SPI_MAX_PACKET_SIZE : fw_len; - dma_regs.cmd = SPI_DMA_WRITE_CTRL_ENABLE; - dma_regs.len = cpu_to_le16(_fw_len); - dma_regs.addr = cpu_to_le32(fw_addr); - - fw_len -= _fw_len; - fw_addr += _fw_len; - - stlc45xx_write16(stlc, SPI_ADRS_DMA_WRITE_CTRL, dma_regs.cmd); - - if (stlc45xx_wait_bit(stlc, SPI_ADRS_DMA_WRITE_CTRL, - HOST_ALLOWED, HOST_ALLOWED) == 0) { - stlc45xx_error("fw_upload not allowed to DMA write"); - return -EAGAIN; - } - - stlc45xx_write16(stlc, SPI_ADRS_DMA_WRITE_LEN, dma_regs.len); - stlc45xx_write32(stlc, SPI_ADRS_DMA_WRITE_BASE, dma_regs.addr); - - stlc45xx_spi_write(stlc, SPI_ADRS_DMA_DATA, stlc->fw, _fw_len); - - /* FIXME: I think this doesn't work if firmware is large, - * this loop goes to second round. fw->data is not - * increased at all! */ - } - - BUG_ON(fw_len != 0); - - /* enable host interrupts */ - stlc45xx_write32(stlc, SPI_ADRS_HOST_INT_EN, SPI_HOST_INTS_DEFAULT); - - /* boot the device */ - stlc45xx_write16(stlc, SPI_ADRS_DEV_CTRL_STAT, - SPI_CTRL_STAT_HOST_OVERRIDE | SPI_CTRL_STAT_HOST_RESET - | SPI_CTRL_STAT_RAM_BOOT); - - msleep(TARGET_BOOT_SLEEP); - - stlc45xx_write16(stlc, SPI_ADRS_DEV_CTRL_STAT, - SPI_CTRL_STAT_HOST_OVERRIDE | SPI_CTRL_STAT_RAM_BOOT); - msleep(TARGET_BOOT_SLEEP); - - return 0; -} - -/* caller must hold tx_lock */ -static void stlc45xx_check_txsent(struct stlc45xx *stlc) -{ - struct txbuffer *entry, *n; - - list_for_each_entry_safe(entry, n, &stlc->tx_sent, tx_list) { - if (time_after(jiffies, entry->lifetime)) { - if (net_ratelimit()) - stlc45xx_warning("frame 0x%x lifetime exceeded", - entry->start); - list_del(&entry->tx_list); - skb_pull(entry->skb, entry->header_len); - ieee80211_tx_status(stlc->hw, entry->skb); - stlc45xx_txbuffer_free(stlc, entry); - } - } -} - -static void stlc45xx_power_off(struct stlc45xx *stlc) -{ - disable_irq(gpio_to_irq(stlc45xx_gpio_irq)); - gpio_set_value(stlc45xx_gpio_power, 0); -} - -static void stlc45xx_power_on(struct stlc45xx *stlc) -{ - gpio_set_value(stlc45xx_gpio_power, 1); - enable_irq(gpio_to_irq(stlc45xx_gpio_irq)); - - /* - * need to wait a while before device can be accessed, the length - * is just a guess - */ - msleep(10); -} - -/* caller must hold tx_lock */ -static void stlc45xx_flush_queues(struct stlc45xx *stlc) -{ - struct txbuffer *entry; - - while (!list_empty(&stlc->tx_sent)) { - entry = list_first_entry(&stlc->tx_sent, - struct txbuffer, tx_list); - list_del(&entry->tx_list); - dev_kfree_skb(entry->skb); - stlc45xx_txbuffer_free(stlc, entry); - } - - WARN_ON(!list_empty(&stlc->tx_sent)); - - while (!list_empty(&stlc->tx_pending)) { - entry = list_first_entry(&stlc->tx_pending, - struct txbuffer, tx_list); - list_del(&entry->tx_list); - dev_kfree_skb(entry->skb); - stlc45xx_txbuffer_free(stlc, entry); - } - - WARN_ON(!list_empty(&stlc->tx_pending)); - WARN_ON(!list_empty(&stlc->txbuffer)); -} - -static void stlc45xx_work_reset(struct work_struct *work) -{ - struct stlc45xx *stlc = container_of(work, struct stlc45xx, - work_reset); - - mutex_lock(&stlc->mutex); - - if (stlc->fw_state != FW_STATE_RESET) - goto out; - - stlc45xx_power_off(stlc); - - mutex_unlock(&stlc->mutex); - - /* wait that all work_structs have finished, we can't hold - * stlc->mutex to avoid deadlock */ - cancel_work_sync(&stlc->work); - - /* FIXME: find out good value to wait for chip power down */ - msleep(100); - - mutex_lock(&stlc->mutex); - - /* FIXME: we should gracefully handle if the state has changed - * after re-acquiring mutex */ - WARN_ON(stlc->fw_state != FW_STATE_RESET); - - spin_lock_bh(&stlc->tx_lock); - stlc45xx_flush_queues(stlc); - spin_unlock_bh(&stlc->tx_lock); - - stlc->fw_state = FW_STATE_RESETTING; - - stlc45xx_power_on(stlc); - stlc45xx_upload_firmware(stlc); - -out: - mutex_unlock(&stlc->mutex); -} - -/* caller must hold mutex */ -static void stlc45xx_reset(struct stlc45xx *stlc) -{ - stlc45xx_warning("resetting firmware"); - stlc->fw_state = FW_STATE_RESET; - ieee80211_stop_queues(stlc->hw); - queue_work(stlc->hw->workqueue, &stlc->work_reset); -} - -static void stlc45xx_work_tx_timeout(struct work_struct *work) -{ - struct stlc45xx *stlc = container_of(work, struct stlc45xx, - work_tx_timeout.work); - - stlc45xx_warning("tx timeout"); - - mutex_lock(&stlc->mutex); - - if (stlc->fw_state != FW_STATE_READY) - goto out; - - stlc45xx_reset(stlc); - -out: - mutex_unlock(&stlc->mutex); -} - -static void stlc45xx_int_ack(struct stlc45xx *stlc, u32 val) -{ - stlc45xx_debug(DEBUG_FUNC, "%s", __func__); - - stlc45xx_write32(stlc, SPI_ADRS_HOST_INT_ACK, val); -} - -static void stlc45xx_wakeup(struct stlc45xx *stlc) -{ - unsigned long timeout; - u32 ints; - - stlc45xx_debug(DEBUG_FUNC, "%s", __func__); - - /* wake the chip */ - stlc45xx_write32(stlc, SPI_ADRS_ARM_INTERRUPTS, SPI_TARGET_INT_WAKEUP); - - /* And wait for the READY interrupt */ - timeout = jiffies + HZ; - - ints = stlc45xx_read32(stlc, SPI_ADRS_HOST_INTERRUPTS); - while (!(ints & SPI_HOST_INT_READY)) { - if (time_after(jiffies, timeout)) - goto out; - ints = stlc45xx_read32(stlc, SPI_ADRS_HOST_INTERRUPTS); - } - - stlc45xx_int_ack(stlc, SPI_HOST_INT_READY); - -out: - return; -} - -static void stlc45xx_sleep(struct stlc45xx *stlc) -{ - stlc45xx_debug(DEBUG_FUNC, "%s", __func__); - - stlc45xx_write32(stlc, SPI_ADRS_ARM_INTERRUPTS, SPI_TARGET_INT_SLEEP); -} - -static void stlc45xx_int_ready(struct stlc45xx *stlc) -{ - stlc45xx_debug(DEBUG_FUNC, "%s", __func__); - - stlc45xx_write32(stlc, SPI_ADRS_HOST_INT_EN, - SPI_HOST_INT_UPDATE | SPI_HOST_INT_SW_UPDATE); - - switch (stlc->fw_state) { - case FW_STATE_BOOTING: - stlc->fw_state = FW_STATE_READY; - complete(&stlc->fw_comp); - break; - case FW_STATE_RESETTING: - stlc->fw_state = FW_STATE_READY; - - stlc45xx_tx_scan(stlc); - stlc45xx_tx_setup(stlc); - stlc45xx_tx_edcf(stlc); - - ieee80211_wake_queues(stlc->hw); - break; - default: - break; - } -} - -static int stlc45xx_rx_txack(struct stlc45xx *stlc, struct sk_buff *skb) -{ - struct ieee80211_tx_info *info; - struct s_lm_control *control; - struct s_lmo_tx *tx; - struct txbuffer *entry; - int found = 0; - - stlc45xx_debug(DEBUG_FUNC, "%s", __func__); - - control = (struct s_lm_control *) skb->data; - tx = (struct s_lmo_tx *) (control + 1); - - if (list_empty(&stlc->tx_sent)) { - if (net_ratelimit()) - stlc45xx_warning("no frames waiting for " - "acknowledgement"); - return -1; - } - - list_for_each_entry(entry, &stlc->tx_sent, tx_list) { - if (control->handle == entry->handle) { - found = 1; - break; - } - } - - if (!found) { - if (net_ratelimit()) - stlc45xx_warning("couldn't find frame for tx ack 0x%x", - control->handle); - return -1; - } - - stlc45xx_debug(DEBUG_TX, "TX ACK 0x%x", entry->handle); - - if (entry->status_needed) { - info = IEEE80211_SKB_CB(entry->skb); - - if (!(tx->flags & LM_TX_FAILED)) { - /* frame was acked */ - info->flags |= IEEE80211_TX_STAT_ACK; - info->status.ack_signal = tx->rcpi / 2 - 110; - } - - skb_pull(entry->skb, entry->header_len); - - ieee80211_tx_status(stlc->hw, entry->skb); - } - - list_del(&entry->tx_list); - - stlc45xx_check_txsent(stlc); - if (list_empty(&stlc->tx_sent)) - /* there are no pending frames, we can stop the tx timeout - * timer */ - cancel_delayed_work(&stlc->work_tx_timeout); - - spin_lock_bh(&stlc->tx_lock); - - stlc45xx_txbuffer_free(stlc, entry); - - if (stlc->tx_queue_stopped && - stlc45xx_txbuffer_find(stlc, MAX_FRAME_LEN) != -1) { - stlc45xx_debug(DEBUG_QUEUE, "room in tx buffer, waking queues"); - ieee80211_wake_queues(stlc->hw); - stlc->tx_queue_stopped = 0; - } - - spin_unlock_bh(&stlc->tx_lock); - - return 0; -} - -static int stlc45xx_rx_control(struct stlc45xx *stlc, struct sk_buff *skb) -{ - struct s_lm_control *control; - int ret = 0; - - stlc45xx_debug(DEBUG_FUNC, "%s", __func__); - - control = (struct s_lm_control *) skb->data; - - switch (control->oid) { - case LM_OID_TX: - ret = stlc45xx_rx_txack(stlc, skb); - break; - case LM_OID_SETUP: - case LM_OID_SCAN: - case LM_OID_TRAP: - case LM_OID_EDCF: - case LM_OID_KEYCACHE: - case LM_OID_PSM: - case LM_OID_STATS: - case LM_OID_LED: - default: - stlc45xx_warning("unhandled rx control oid %d\n", - control->oid); - break; - } - - dev_kfree_skb(skb); - - return ret; -} - -/* copied from mac80211 */ -static void stlc45xx_parse_elems(u8 *start, size_t len, - struct stlc45xx_ie_tim **tim, - size_t *tim_len) -{ - size_t left = len; - u8 *pos = start; - - while (left >= 2) { - u8 id, elen; - - id = *pos++; - elen = *pos++; - left -= 2; - - if (elen > left) - return; - - switch (id) { - case WLAN_EID_TIM: - *tim = (struct stlc45xx_ie_tim *) pos; - *tim_len = elen; - break; - default: - break; - } - - left -= elen; - pos += elen; - } -} - -/* - * mac80211 doesn't have support for asking frames with PS-Poll, so let's - * implement in the driver for now. We have to add support to mac80211 - * later. - */ -static int stlc45xx_check_more_data(struct stlc45xx *stlc, struct sk_buff *skb) -{ - struct s_lm_data_in *data = (struct s_lm_data_in *) skb->data; - struct ieee80211_hdr *hdr; - size_t len; - u16 fc; - - hdr = (void *) skb->data + sizeof(*data); - len = skb->len - sizeof(*data); - - /* minimum frame length is the null frame length 24 bytes */ - if (len < 24) { - stlc45xx_warning("invalid frame length when checking for " - "more data"); - return -EINVAL; - } - - fc = le16_to_cpu(hdr->frame_control); - if (!(fc & IEEE80211_FCTL_FROMDS)) - /* this is not from DS */ - return 0; - - if (compare_ether_addr(hdr->addr1, stlc->mac_addr) != 0) - /* the frame was not for us */ - return 0; - - if (!(fc & IEEE80211_FCTL_MOREDATA)) { - /* AP has no more frames buffered for us */ - stlc45xx_debug(DEBUG_PSM, "all buffered frames retrieved"); - stlc->pspolling = false; - return 0; - } - - /* MOREDATA bit is set, let's ask for a new frame from the AP */ - stlc45xx_tx_pspoll(stlc, stlc->psm); - - return 0; -} - -/* - * mac80211 cannot read TIM from beacons, so let's add a hack to the - * driver. We have to add support to mac80211 later. - */ -static int stlc45xx_rx_data_beacon(struct stlc45xx *stlc, struct sk_buff *skb) -{ - struct s_lm_data_in *data = (struct s_lm_data_in *) skb->data; - size_t len = skb->len, tim_len = 0, baselen, pvbmap_len; - struct ieee80211_mgmt *mgmt; - struct stlc45xx_ie_tim *tim = NULL; - int bmap_offset, index, aid_bit; - - mgmt = (void *) skb->data + sizeof(*data); - - baselen = (u8 *) mgmt->u.beacon.variable - (u8 *) mgmt; - if (baselen > len) { - stlc45xx_warning("invalid baselen in beacon"); - return -EINVAL; - } - - stlc45xx_parse_elems(mgmt->u.beacon.variable, len - baselen, &tim, - &tim_len); - - if (!tim) { - stlc45xx_warning("didn't find tim from a beacon"); - return -EINVAL; - } - - bmap_offset = tim->bmap_control & 0xfe; - index = stlc->aid / 8 - bmap_offset; - - pvbmap_len = tim_len - 3; - if (index > pvbmap_len) - return -EINVAL; - - aid_bit = !!(tim->pvbmap[index] & (1 << stlc->aid % 8)); - - stlc45xx_debug(DEBUG_PSM, "fc 0x%x duration %d seq %d dtim %u " - "bmap_control 0x%x aid_bit %d", - mgmt->frame_control, mgmt->duration, mgmt->seq_ctrl >> 4, - tim->dtim_count, tim->bmap_control, aid_bit); - - if (!aid_bit) - return 0; - - stlc->pspolling = true; - stlc45xx_tx_pspoll(stlc, stlc->psm); - - return 0; -} - -static int stlc45xx_rx_data(struct stlc45xx *stlc, struct sk_buff *skb) -{ - struct ieee80211_rx_status status; - struct s_lm_data_in *data = (struct s_lm_data_in *) skb->data; - int align = 0; - u8 *p, align_len; - u16 len; - - stlc45xx_debug(DEBUG_FUNC, "%s", __func__); - - if (stlc->psm) { - if (data->flags & LM_IN_BEACON) - stlc45xx_rx_data_beacon(stlc, skb); - else if (stlc->pspolling && (data->flags & LM_IN_DATA)) - stlc45xx_check_more_data(stlc, skb); - } - - memset(&status, 0, sizeof(status)); - - status.freq = data->frequency; - status.signal = data->rcpi / 2 - 110; - - /* let's assume that maximum rcpi value is 140 (= 35 dBm) */ - status.qual = data->rcpi * 100 / 140; - - status.band = IEEE80211_BAND_2GHZ; - - /* - * FIXME: this gives warning from __ieee80211_rx() - * - * status.rate_idx = data->rate; - */ - - len = data->length; - - if (data->flags & LM_FLAG_ALIGN) - align = 1; - - skb_pull(skb, sizeof(*data)); - - if (align) { - p = skb->data; - align_len = *p; - skb_pull(skb, align_len); - } - - skb_trim(skb, len); - - stlc45xx_debug(DEBUG_RX, "rx data 0x%p %d B", skb->data, skb->len); - stlc45xx_dump(DEBUG_RX_CONTENT, skb->data, skb->len); - - memcpy(IEEE80211_SKB_RXCB(skb), &status, sizeof(status)); - ieee80211_rx(stlc->hw, skb); - - return 0; -} - - - -static int stlc45xx_rx(struct stlc45xx *stlc) -{ - struct s_lm_control *control; - struct sk_buff *skb; - int ret; - u16 len; - - stlc45xx_debug(DEBUG_FUNC, "%s", __func__); - - stlc45xx_wakeup(stlc); - - /* dummy read to flush SPI DMA controller bug */ - stlc45xx_read16(stlc, SPI_ADRS_GEN_PURP_1); - - len = stlc45xx_read16(stlc, SPI_ADRS_DMA_DATA); - - if (len == 0) { - stlc45xx_warning("rx request of zero bytes"); - return 0; - } - - skb = dev_alloc_skb(len); - if (!skb) { - stlc45xx_warning("could not alloc skb"); - return 0; - } - - stlc45xx_spi_read(stlc, SPI_ADRS_DMA_DATA, skb_put(skb, len), len); - - stlc45xx_sleep(stlc); - - stlc45xx_debug(DEBUG_RX, "rx frame 0x%p %d B", skb->data, skb->len); - stlc45xx_dump(DEBUG_RX_CONTENT, skb->data, skb->len); - - control = (struct s_lm_control *) skb->data; - - if (control->flags & LM_FLAG_CONTROL) - ret = stlc45xx_rx_control(stlc, skb); - else - ret = stlc45xx_rx_data(stlc, skb); - - return ret; -} - - -static irqreturn_t stlc45xx_interrupt(int irq, void *config) -{ - struct spi_device *spi = config; - struct stlc45xx *stlc = dev_get_drvdata(&spi->dev); - - stlc45xx_debug(DEBUG_IRQ, "IRQ"); - - queue_work(stlc->hw->workqueue, &stlc->work); - - return IRQ_HANDLED; -} - -static int stlc45xx_tx_frame(struct stlc45xx *stlc, u32 address, - void *buf, size_t len) -{ - struct s_dma_regs dma_regs; - unsigned long timeout; - int ret = 0; - u32 ints; - - stlc->tx_frames++; - - stlc45xx_debug(DEBUG_FUNC, "%s", __func__); - - stlc45xx_debug(DEBUG_TX, "tx frame 0x%p %zu B", buf, len); - stlc45xx_dump(DEBUG_TX_CONTENT, buf, len); - - stlc45xx_wakeup(stlc); - - dma_regs.cmd = SPI_DMA_WRITE_CTRL_ENABLE; - dma_regs.len = cpu_to_le16(len); - dma_regs.addr = cpu_to_le32(address); - - stlc45xx_spi_write(stlc, SPI_ADRS_DMA_WRITE_CTRL, &dma_regs, - sizeof(dma_regs)); - - stlc45xx_spi_write(stlc, SPI_ADRS_DMA_DATA, buf, len); - - timeout = jiffies + 2 * HZ; - ints = stlc45xx_read32(stlc, SPI_ADRS_HOST_INTERRUPTS); - while (!(ints & SPI_HOST_INT_WR_READY)) { - if (time_after(jiffies, timeout)) { - stlc45xx_warning("WR_READY timeout"); - ret = -1; - goto out; - } - ints = stlc45xx_read32(stlc, SPI_ADRS_HOST_INTERRUPTS); - } - - stlc45xx_int_ack(stlc, SPI_HOST_INT_WR_READY); - - stlc45xx_sleep(stlc); - -out: - return ret; -} - -static int stlc45xx_wq_tx(struct stlc45xx *stlc) -{ - struct txbuffer *entry; - int ret = 0; - - spin_lock_bh(&stlc->tx_lock); - - while (!list_empty(&stlc->tx_pending)) { - entry = list_entry(stlc->tx_pending.next, - struct txbuffer, tx_list); - - list_del_init(&entry->tx_list); - - spin_unlock_bh(&stlc->tx_lock); - - ret = stlc45xx_tx_frame(stlc, entry->frame_start, - entry->skb->data, entry->skb->len); - - spin_lock_bh(&stlc->tx_lock); - - if (ret < 0) { - /* frame transfer to firmware buffer failed */ - /* FIXME: report this to mac80211 */ - dev_kfree_skb(entry->skb); - stlc45xx_txbuffer_free(stlc, entry); - goto out; - } - - list_add(&entry->tx_list, &stlc->tx_sent); - queue_delayed_work(stlc->hw->workqueue, - &stlc->work_tx_timeout, - msecs_to_jiffies(TX_TIMEOUT)); - } - -out: - spin_unlock_bh(&stlc->tx_lock); - return ret; -} - -static void stlc45xx_work(struct work_struct *work) -{ - struct stlc45xx *stlc = container_of(work, struct stlc45xx, work); - u32 ints; - int ret; - - stlc45xx_debug(DEBUG_FUNC, "%s", __func__); - - mutex_lock(&stlc->mutex); - - if (stlc->fw_state == FW_STATE_OFF && - stlc->fw_state == FW_STATE_RESET) - goto out; - - ints = stlc45xx_read32(stlc, SPI_ADRS_HOST_INTERRUPTS); - stlc45xx_debug(DEBUG_BH, "begin host_ints 0x%08x", ints); - - if (ints & SPI_HOST_INT_READY) { - stlc45xx_int_ready(stlc); - stlc45xx_int_ack(stlc, SPI_HOST_INT_READY); - } - - if (stlc->fw_state != FW_STATE_READY) - goto out; - - if (ints & SPI_HOST_INT_UPDATE) { - stlc45xx_int_ack(stlc, SPI_HOST_INT_UPDATE); - ret = stlc45xx_rx(stlc); - if (ret < 0) { - stlc45xx_reset(stlc); - goto out; - } - } - if (ints & SPI_HOST_INT_SW_UPDATE) { - stlc45xx_int_ack(stlc, SPI_HOST_INT_SW_UPDATE); - ret = stlc45xx_rx(stlc); - if (ret < 0) { - stlc45xx_reset(stlc); - goto out; - } - } - - ret = stlc45xx_wq_tx(stlc); - if (ret < 0) { - stlc45xx_reset(stlc); - goto out; - } - - ints = stlc45xx_read32(stlc, SPI_ADRS_HOST_INTERRUPTS); - stlc45xx_debug(DEBUG_BH, "end host_ints 0x%08x", ints); - -out: - mutex_unlock(&stlc->mutex); -} - -static void stlc45xx_tx_edcf(struct stlc45xx *stlc) -{ - struct s_lm_control *control; - struct s_lmo_edcf *edcf; - size_t len, edcf_len; - - stlc45xx_debug(DEBUG_FUNC, "%s", __func__); - - edcf_len = sizeof(*edcf); - len = sizeof(*control) + edcf_len; - control = kzalloc(len, GFP_KERNEL); - edcf = (struct s_lmo_edcf *) (control + 1); - - control->flags = LM_FLAG_CONTROL | LM_CTRL_OPSET; - control->length = edcf_len; - control->oid = LM_OID_EDCF; - - edcf->slottime = 0x14; - edcf->sifs = 10; - edcf->eofpad = 6; - edcf->maxburst = 1500; - - edcf->queues[0].aifs = 2; - edcf->queues[0].pad0 = 1; - edcf->queues[0].cwmin = 3; - edcf->queues[0].cwmax = 7; - edcf->queues[0].txop = 47; - edcf->queues[1].aifs = 2; - edcf->queues[1].pad0 = 0; - edcf->queues[1].cwmin = 7; - edcf->queues[1].cwmax = 15; - edcf->queues[1].txop = 94; - edcf->queues[2].aifs = 3; - edcf->queues[2].pad0 = 0; - edcf->queues[2].cwmin = 15; - edcf->queues[2].cwmax = 1023; - edcf->queues[2].txop = 0; - edcf->queues[3].aifs = 7; - edcf->queues[3].pad0 = 0; - edcf->queues[3].cwmin = 15; - edcf->queues[3].cwmax = 1023; - edcf->queues[3].txop = 0; - edcf->queues[4].aifs = 13; - edcf->queues[4].pad0 = 99; - edcf->queues[4].cwmin = 3437; - edcf->queues[4].cwmax = 512; - edcf->queues[4].txop = 12; - edcf->queues[5].aifs = 142; - edcf->queues[5].pad0 = 109; - edcf->queues[5].cwmin = 8756; - edcf->queues[5].cwmax = 6; - edcf->queues[5].txop = 0; - edcf->queues[6].aifs = 4; - edcf->queues[6].pad0 = 0; - edcf->queues[6].cwmin = 0; - edcf->queues[6].cwmax = 58705; - edcf->queues[6].txop = 25716; - edcf->queues[7].aifs = 0; - edcf->queues[7].pad0 = 0; - edcf->queues[7].cwmin = 0; - edcf->queues[7].cwmax = 0; - edcf->queues[7].txop = 0; - - stlc45xx_tx_frame(stlc, FIRMWARE_CONFIG_START, control, len); - - kfree(control); -} - -static void stlc45xx_tx_setup(struct stlc45xx *stlc) -{ - struct s_lm_control *control; - struct s_lmo_setup *setup; - size_t len, setup_len; - - stlc45xx_debug(DEBUG_FUNC, "%s", __func__); - - setup_len = sizeof(*setup); - len = sizeof(*control) + setup_len; - control = kzalloc(len, GFP_KERNEL); - setup = (struct s_lmo_setup *) (control + 1); - - control->flags = LM_FLAG_CONTROL | LM_CTRL_OPSET; - control->length = setup_len; - control->oid = LM_OID_SETUP; - - setup->flags = LM_SETUP_INFRA; - setup->antenna = 2; - setup->rx_align = 0; - setup->rx_buffer = FIRMWARE_RXBUFFER_START; - setup->rx_mtu = FIRMWARE_MTU; - setup->frontend = 5; - setup->timeout = 0; - setup->truncate = 48896; - setup->bratemask = 0xffffffff; - setup->ref_clock = 644245094; - setup->lpf_bandwidth = 65535; - setup->osc_start_delay = 65535; - - memcpy(setup->macaddr, stlc->mac_addr, ETH_ALEN); - memcpy(setup->bssid, stlc->bssid, ETH_ALEN); - - stlc45xx_tx_frame(stlc, FIRMWARE_CONFIG_START, control, len); - - kfree(control); -} - -static void stlc45xx_tx_scan(struct stlc45xx *stlc) -{ - struct s_lm_control *control; - struct s_lmo_scan *scan; - size_t len, scan_len; - - stlc45xx_debug(DEBUG_FUNC, "%s", __func__); - - scan_len = sizeof(*scan); - len = sizeof(*control) + scan_len; - control = kzalloc(len, GFP_KERNEL); - scan = (struct s_lmo_scan *) (control + 1); - - control->flags = LM_FLAG_CONTROL | LM_CTRL_OPSET; - control->length = scan_len; - control->oid = LM_OID_SCAN; - - scan->flags = LM_SCAN_EXIT; - scan->bratemask = 0x15f; - scan->aloft[0] = 3; - scan->aloft[1] = 3; - scan->aloft[2] = 1; - scan->aloft[3] = 0; - scan->aloft[4] = 0; - scan->aloft[5] = 0; - scan->aloft[6] = 0; - scan->aloft[7] = 0; - - memcpy(&scan->rssical, &stlc->cal_rssi[(stlc->channel - 1) * - RSSI_CAL_LEN], - RSSI_CAL_LEN); - memcpy(&scan->channel, &stlc->cal_channels[(stlc->channel - 1) * - CHANNEL_CAL_LEN], - CHANNEL_CAL_LEN); - - stlc45xx_tx_frame(stlc, FIRMWARE_CONFIG_START, control, len); - - kfree(control); -} - -/* - * caller must hold mutex - */ -static int stlc45xx_tx_pspoll(struct stlc45xx *stlc, bool powersave) -{ - struct ieee80211_hdr *pspoll; - int payload_len, padding, i; - struct s_lm_data_out *data; - struct txbuffer *entry; - struct sk_buff *skb; - char *payload; - u16 fc; - - skb = dev_alloc_skb(stlc->hw->extra_tx_headroom + 16); - if (!skb) { - stlc45xx_warning("failed to allocate pspoll frame"); - return -ENOMEM; - } - skb_reserve(skb, stlc->hw->extra_tx_headroom); - - pspoll = (struct ieee80211_hdr *) skb_put(skb, 16); - memset(pspoll, 0, 16); - fc = IEEE80211_FTYPE_CTL | IEEE80211_STYPE_PSPOLL; - if (powersave) - fc |= IEEE80211_FCTL_PM; - pspoll->frame_control = cpu_to_le16(fc); - pspoll->duration_id = cpu_to_le16(stlc->aid); - - /* aid in PS-Poll has its two MSBs each set to 1 */ - pspoll->duration_id |= cpu_to_le16(1 << 15) | cpu_to_le16(1 << 14); - - memcpy(pspoll->addr1, stlc->bssid, ETH_ALEN); - memcpy(pspoll->addr2, stlc->mac_addr, ETH_ALEN); - - stlc45xx_debug(DEBUG_PSM, "sending PS-Poll frame to %pM (powersave %d, " - "fc 0x%x, aid %d)", pspoll->addr1, - powersave, fc, stlc->aid); - - spin_lock_bh(&stlc->tx_lock); - - entry = stlc45xx_txbuffer_alloc(stlc, skb->len); - - spin_unlock_bh(&stlc->tx_lock); - - if (!entry) { - /* - * The queue should be stopped before the firmware buffer - * is full, so firmware buffer should always have enough - * space. - * - * But I'm too lazy and omit it for now. - */ - if (net_ratelimit()) - stlc45xx_warning("firmware tx buffer full is full " - "for null frame"); - return -ENOSPC; - } - - payload = skb->data; - payload_len = skb->len; - padding = (int) (skb->data - sizeof(*data)) & 3; - entry->header_len = sizeof(*data) + padding; - - entry->skb = skb; - entry->status_needed = false; - entry->handle = (u32) skb; - entry->lifetime = jiffies + msecs_to_jiffies(TX_FRAME_LIFETIME); - - stlc45xx_debug(DEBUG_TX, "tx data 0x%x (0x%p payload %d B " - "padding %d header_len %d)", - entry->handle, payload, payload_len, padding, - entry->header_len); - stlc45xx_dump(DEBUG_TX_CONTENT, payload, payload_len); - - data = (struct s_lm_data_out *) skb_push(skb, entry->header_len); - - memset(data, 0, entry->header_len); - - if (padding) - data->flags = LM_FLAG_ALIGN; - - data->flags = LM_OUT_BURST; - data->length = payload_len; - data->handle = entry->handle; - data->aid = 1; - data->rts_retries = 7; - data->retries = 7; - data->aloft_ctrl = 0; - data->crypt_offset = 58; - data->keytype = 0; - data->keylen = 0; - data->queue = LM_QUEUE_DATA3; - data->backlog = 32; - data->antenna = 2; - data->cts = 3; - data->power = 127; - - for (i = 0; i < 8; i++) - data->aloft[i] = 0; - - /* - * check if there's enough space in tx buffer - * - * FIXME: ignored for now - */ - - stlc45xx_tx_frame(stlc, entry->start, skb->data, skb->len); - - list_add(&entry->tx_list, &stlc->tx_sent); - - return 0; -} - -/* - * caller must hold mutex - * - * shamelessly stolen from mac80211/ieee80211_send_nullfunc - */ -static int stlc45xx_tx_nullfunc(struct stlc45xx *stlc, bool powersave) -{ - struct ieee80211_hdr *nullfunc; - int payload_len, padding, i; - struct s_lm_data_out *data; - struct txbuffer *entry; - struct sk_buff *skb; - char *payload; - u16 fc; - - skb = dev_alloc_skb(stlc->hw->extra_tx_headroom + 24); - if (!skb) { - stlc45xx_warning("failed to allocate buffer for null frame\n"); - return -ENOMEM; - } - skb_reserve(skb, stlc->hw->extra_tx_headroom); - - nullfunc = (struct ieee80211_hdr *) skb_put(skb, 24); - memset(nullfunc, 0, 24); - fc = IEEE80211_FTYPE_DATA | IEEE80211_STYPE_NULLFUNC | - IEEE80211_FCTL_TODS; - - if (powersave) - fc |= IEEE80211_FCTL_PM; - - nullfunc->frame_control = cpu_to_le16(fc); - memcpy(nullfunc->addr1, stlc->bssid, ETH_ALEN); - memcpy(nullfunc->addr2, stlc->mac_addr, ETH_ALEN); - memcpy(nullfunc->addr3, stlc->bssid, ETH_ALEN); - - stlc45xx_debug(DEBUG_PSM, "sending Null frame to %pM (powersave %d, " - "fc 0x%x)", nullfunc->addr1, powersave, fc); - - spin_lock_bh(&stlc->tx_lock); - - entry = stlc45xx_txbuffer_alloc(stlc, skb->len); - - spin_unlock_bh(&stlc->tx_lock); - - if (!entry) { - /* - * The queue should be stopped before the firmware buffer - * is full, so firmware buffer should always have enough - * space. - * - * But I'm too lazy and omit it for now. - */ - if (net_ratelimit()) - stlc45xx_warning("firmware tx buffer full is full " - "for null frame"); - return -ENOSPC; - } - - payload = skb->data; - payload_len = skb->len; - padding = (int) (skb->data - sizeof(*data)) & 3; - entry->header_len = sizeof(*data) + padding; - - entry->skb = skb; - entry->status_needed = false; - entry->handle = (u32) skb; - entry->lifetime = jiffies + msecs_to_jiffies(TX_FRAME_LIFETIME); - - stlc45xx_debug(DEBUG_TX, "tx data 0x%x (0x%p payload %d B " - "padding %d header_len %d)", - entry->handle, payload, payload_len, padding, - entry->header_len); - stlc45xx_dump(DEBUG_TX_CONTENT, payload, payload_len); - - data = (struct s_lm_data_out *) skb_push(skb, entry->header_len); - - memset(data, 0, entry->header_len); - - if (padding) - data->flags = LM_FLAG_ALIGN; - - data->flags = LM_OUT_BURST; - data->length = payload_len; - data->handle = entry->handle; - data->aid = 1; - data->rts_retries = 7; - data->retries = 7; - data->aloft_ctrl = 0; - data->crypt_offset = 58; - data->keytype = 0; - data->keylen = 0; - data->queue = LM_QUEUE_DATA3; - data->backlog = 32; - data->antenna = 2; - data->cts = 3; - data->power = 127; - - for (i = 0; i < 8; i++) - data->aloft[i] = 0; - - /* - * check if there's enough space in tx buffer - * - * FIXME: ignored for now - */ - - stlc45xx_tx_frame(stlc, entry->start, skb->data, skb->len); - - list_add(&entry->tx_list, &stlc->tx_sent); - - return 0; -} - -/* caller must hold mutex */ -static void stlc45xx_tx_psm(struct stlc45xx *stlc, bool enable) -{ - struct s_lm_control *control; - struct s_lmo_psm *psm; - size_t len, psm_len; - - WARN_ON(!stlc->associated); - WARN_ON(stlc->aid < 1); - WARN_ON(stlc->aid > 2007); - - psm_len = sizeof(*psm); - len = sizeof(*control) + psm_len; - control = kzalloc(len, GFP_KERNEL); - psm = (struct s_lmo_psm *) (control + 1); - - control->flags = LM_FLAG_CONTROL | LM_CTRL_OPSET; - control->length = psm_len; - control->oid = LM_OID_PSM; - - if (enable) - psm->flags |= LM_PSM; - - psm->aid = stlc->aid; - - psm->beacon_rcpi_skip_max = 60; - - psm->intervals[0].interval = 1; - psm->intervals[0].periods = 1; - psm->intervals[1].interval = 1; - psm->intervals[1].periods = 1; - psm->intervals[2].interval = 1; - psm->intervals[2].periods = 1; - psm->intervals[3].interval = 1; - psm->intervals[3].periods = 1; - - psm->nr = 0; - psm->exclude[0] = 0; - - stlc45xx_debug(DEBUG_PSM, "sending LM_OID_PSM (aid %d, interval %d)", - psm->aid, psm->intervals[0].interval); - - stlc45xx_tx_frame(stlc, FIRMWARE_CONFIG_START, control, len); - - kfree(control); -} - -static int stlc45xx_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb) -{ - struct stlc45xx *stlc = hw->priv; - struct ieee80211_tx_info *info; - struct ieee80211_rate *rate; - int payload_len, padding, i; - struct s_lm_data_out *data; - struct txbuffer *entry; - char *payload; - - stlc45xx_debug(DEBUG_FUNC, "%s", __func__); - - spin_lock_bh(&stlc->tx_lock); - - entry = stlc45xx_txbuffer_alloc(stlc, skb->len); - if (!entry) { - /* the queue should be stopped before the firmware buffer - * is full, so firmware buffer should always have enough - * space */ - if (net_ratelimit()) - stlc45xx_warning("firmware buffer full"); - spin_unlock_bh(&stlc->tx_lock); - return NETDEV_TX_BUSY; - } - - info = IEEE80211_SKB_CB(skb); - - payload = skb->data; - payload_len = skb->len; - padding = (int) (skb->data - sizeof(*data)) & 3; - entry->header_len = sizeof(*data) + padding; - - entry->skb = skb; - entry->status_needed = true; - entry->handle = (u32) skb; - entry->lifetime = jiffies + msecs_to_jiffies(TX_FRAME_LIFETIME); - - stlc45xx_debug(DEBUG_TX, "tx data 0x%x (0x%p payload %d B " - "padding %d header_len %d)", - entry->handle, payload, payload_len, padding, - entry->header_len); - stlc45xx_dump(DEBUG_TX_CONTENT, payload, payload_len); - - data = (struct s_lm_data_out *) skb_push(skb, entry->header_len); - - memset(data, 0, entry->header_len); - - if (padding) - data->flags = LM_FLAG_ALIGN; - - data->flags = LM_OUT_BURST; - data->length = payload_len; - data->handle = entry->handle; - data->aid = 1; - data->rts_retries = 7; - data->retries = 7; - data->aloft_ctrl = 0; - data->crypt_offset = 58; - data->keytype = 0; - data->keylen = 0; - data->queue = 2; - data->backlog = 32; - data->antenna = 2; - data->cts = 3; - data->power = 127; - - for (i = 0; i < 8; i++) { - rate = ieee80211_get_tx_rate(stlc->hw, info); - data->aloft[i] = rate->hw_value; - } - - list_add_tail(&entry->tx_list, &stlc->tx_pending); - - /* check if there's enough space in tx buffer */ - if (stlc45xx_txbuffer_find(stlc, MAX_FRAME_LEN) == -1) { - stlc45xx_debug(DEBUG_QUEUE, "tx buffer full, stopping queues"); - stlc->tx_queue_stopped = 1; - ieee80211_stop_queues(stlc->hw); - } - - queue_work(stlc->hw->workqueue, &stlc->work); - - spin_unlock_bh(&stlc->tx_lock); - - return NETDEV_TX_OK; -} - -static int stlc45xx_op_start(struct ieee80211_hw *hw) -{ - struct stlc45xx *stlc = hw->priv; - unsigned long timeout; - int ret = 0; - - stlc45xx_debug(DEBUG_FUNC, "%s", __func__); - - mutex_lock(&stlc->mutex); - - stlc->fw_state = FW_STATE_BOOTING; - stlc->channel = 1; - - stlc45xx_power_on(stlc); - - ret = stlc45xx_upload_firmware(stlc); - if (ret < 0) { - stlc45xx_power_off(stlc); - goto out_unlock; - } - - stlc->tx_queue_stopped = 0; - - mutex_unlock(&stlc->mutex); - - timeout = msecs_to_jiffies(2000); - timeout = wait_for_completion_interruptible_timeout(&stlc->fw_comp, - timeout); - if (!timeout) { - stlc45xx_error("firmware boot failed"); - stlc45xx_power_off(stlc); - ret = -1; - goto out; - } - - stlc45xx_debug(DEBUG_BOOT, "firmware booted"); - - /* FIXME: should we take mutex just after wait_for_completion()? */ - mutex_lock(&stlc->mutex); - - WARN_ON(stlc->fw_state != FW_STATE_READY); - -out_unlock: - mutex_unlock(&stlc->mutex); - -out: - return ret; -} - -static void stlc45xx_op_stop(struct ieee80211_hw *hw) -{ - struct stlc45xx *stlc = hw->priv; - - stlc45xx_debug(DEBUG_FUNC, "%s", __func__); - - mutex_lock(&stlc->mutex); - - WARN_ON(stlc->fw_state != FW_STATE_READY); - - stlc45xx_power_off(stlc); - - /* FIXME: make sure that all work_structs have completed */ - - spin_lock_bh(&stlc->tx_lock); - stlc45xx_flush_queues(stlc); - spin_unlock_bh(&stlc->tx_lock); - - stlc->fw_state = FW_STATE_OFF; - - mutex_unlock(&stlc->mutex); -} - -static int stlc45xx_op_add_interface(struct ieee80211_hw *hw, - struct ieee80211_if_init_conf *conf) -{ - struct stlc45xx *stlc = hw->priv; - - stlc45xx_debug(DEBUG_FUNC, "%s", __func__); - - switch (conf->type) { - case NL80211_IFTYPE_STATION: - break; - default: - return -EOPNOTSUPP; - } - - memcpy(stlc->mac_addr, conf->mac_addr, ETH_ALEN); - - return 0; -} - -static void stlc45xx_op_remove_interface(struct ieee80211_hw *hw, - struct ieee80211_if_init_conf *conf) -{ - stlc45xx_debug(DEBUG_FUNC, "%s", __func__); -} - -static int stlc45xx_op_config(struct ieee80211_hw *hw, u32 changed) -{ - struct stlc45xx *stlc = hw->priv; - - stlc45xx_debug(DEBUG_FUNC, "%s", __func__); - - mutex_lock(&stlc->mutex); - - stlc->channel = hw->conf.channel->hw_value; - stlc45xx_tx_scan(stlc); - stlc45xx_tx_setup(stlc); - stlc45xx_tx_edcf(stlc); - - if ((hw->conf.flags & IEEE80211_CONF_PS) != stlc->psm) { - stlc->psm = hw->conf.flags & IEEE80211_CONF_PS; - if (stlc->associated) { - stlc45xx_tx_psm(stlc, stlc->psm); - stlc45xx_tx_nullfunc(stlc, stlc->psm); - } - } - - mutex_unlock(&stlc->mutex); - - return 0; -} - -static void stlc45xx_op_configure_filter(struct ieee80211_hw *hw, - unsigned int changed_flags, - unsigned int *total_flags, - int mc_count, - struct dev_addr_list *mc_list) -{ - *total_flags = 0; -} - -static void stlc45xx_op_bss_info_changed(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - struct ieee80211_bss_conf *info, - u32 changed) -{ - struct stlc45xx *stlc = hw->priv; - - stlc45xx_debug(DEBUG_FUNC, "%s", __func__); - mutex_lock(&stlc->mutex); - - memcpy(stlc->bssid, info->bssid, ETH_ALEN); - stlc45xx_tx_setup(stlc); - - mutex_unlock(&stlc->mutex); - - if (changed & BSS_CHANGED_ASSOC) { - stlc->associated = info->assoc; - if (info->assoc) - stlc->aid = info->aid; - else - stlc->aid = -1; - - if (stlc->psm) { - stlc45xx_tx_psm(stlc, stlc->psm); - stlc45xx_tx_nullfunc(stlc, stlc->psm); - } - } -} - - -/* can't be const, mac80211 writes to this */ -static struct ieee80211_rate stlc45xx_rates[] = { - { .bitrate = 10, .hw_value = 0, .hw_value_short = 0, }, - { .bitrate = 20, .hw_value = 1, .hw_value_short = 1, }, - { .bitrate = 55, .hw_value = 2, .hw_value_short = 2, }, - { .bitrate = 110, .hw_value = 3, .hw_value_short = 3, }, - { .bitrate = 60, .hw_value = 4, .hw_value_short = 4, }, - { .bitrate = 90, .hw_value = 5, .hw_value_short = 5, }, - { .bitrate = 120, .hw_value = 6, .hw_value_short = 6, }, - { .bitrate = 180, .hw_value = 7, .hw_value_short = 7, }, - { .bitrate = 240, .hw_value = 8, .hw_value_short = 8, }, - { .bitrate = 360, .hw_value = 9, .hw_value_short = 9, }, - { .bitrate = 480, .hw_value = 10, .hw_value_short = 10, }, - { .bitrate = 540, .hw_value = 11, .hw_value_short = 11, }, -}; - -/* can't be const, mac80211 writes to this */ -static struct ieee80211_channel stlc45xx_channels[] = { - { .hw_value = 1, .center_freq = 2412}, - { .hw_value = 2, .center_freq = 2417}, - { .hw_value = 3, .center_freq = 2422}, - { .hw_value = 4, .center_freq = 2427}, - { .hw_value = 5, .center_freq = 2432}, - { .hw_value = 6, .center_freq = 2437}, - { .hw_value = 7, .center_freq = 2442}, - { .hw_value = 8, .center_freq = 2447}, - { .hw_value = 9, .center_freq = 2452}, - { .hw_value = 10, .center_freq = 2457}, - { .hw_value = 11, .center_freq = 2462}, - { .hw_value = 12, .center_freq = 2467}, - { .hw_value = 13, .center_freq = 2472}, -}; - -/* can't be const, mac80211 writes to this */ -static struct ieee80211_supported_band stlc45xx_band_2ghz = { - .channels = stlc45xx_channels, - .n_channels = ARRAY_SIZE(stlc45xx_channels), - .bitrates = stlc45xx_rates, - .n_bitrates = ARRAY_SIZE(stlc45xx_rates), -}; - -static const struct ieee80211_ops stlc45xx_ops = { - .start = stlc45xx_op_start, - .stop = stlc45xx_op_stop, - .add_interface = stlc45xx_op_add_interface, - .remove_interface = stlc45xx_op_remove_interface, - .config = stlc45xx_op_config, - .configure_filter = stlc45xx_op_configure_filter, - .tx = stlc45xx_op_tx, - .bss_info_changed = stlc45xx_op_bss_info_changed, -}; - -static int stlc45xx_register_mac80211(struct stlc45xx *stlc) -{ - /* FIXME: SET_IEEE80211_PERM_ADDR() requires default_mac_addr - to be non-const for some strange reason */ - static u8 default_mac_addr[ETH_ALEN] = { - 0x00, 0x02, 0xee, 0xc0, 0xff, 0xee - }; - int ret; - - SET_IEEE80211_PERM_ADDR(stlc->hw, default_mac_addr); - - ret = ieee80211_register_hw(stlc->hw); - if (ret) { - stlc45xx_error("unable to register mac80211 hw: %d", ret); - return ret; - } - - return 0; -} - -static void stlc45xx_device_release(struct device *dev) -{ - -} - -static struct platform_device stlc45xx_device = { - .name = "stlc45xx", - .id = -1, - - /* device model insists to have a release function */ - .dev = { - .release = stlc45xx_device_release, - }, -}; - -static int __devinit stlc45xx_probe(struct spi_device *spi) -{ - struct stlc45xx *stlc; - struct ieee80211_hw *hw; - int ret; - - stlc45xx_debug(DEBUG_FUNC, "%s", __func__); - - /* mac80211 alloc */ - hw = ieee80211_alloc_hw(sizeof(*stlc), &stlc45xx_ops); - if (!hw) { - stlc45xx_error("could not alloc ieee80211_hw"); - ret = -ENOMEM; - goto out; - } - - /* mac80211 clears hw->priv */ - stlc = hw->priv; - - stlc->hw = hw; - dev_set_drvdata(&spi->dev, stlc); - stlc->spi = spi; - - spi->bits_per_word = 16; - spi->max_speed_hz = 24000000; - - ret = spi_setup(spi); - if (ret < 0) - stlc45xx_error("spi_setup failed"); - - ret = gpio_request(stlc45xx_gpio_power, "stlc45xx power"); - if (ret < 0) { - stlc45xx_error("power GPIO request failed: %d", ret); - return ret; - } - - ret = gpio_request(stlc45xx_gpio_irq, "stlc45xx irq"); - if (ret < 0) { - stlc45xx_error("irq GPIO request failed: %d", ret); - goto out; - } - - gpio_direction_output(stlc45xx_gpio_power, 0); - gpio_direction_input(stlc45xx_gpio_irq); - - ret = request_irq(gpio_to_irq(stlc45xx_gpio_irq), - stlc45xx_interrupt, IRQF_DISABLED, "stlc45xx", - stlc->spi); - if (ret < 0) - /* FIXME: handle the error */ - stlc45xx_error("request_irq() failed"); - - set_irq_type(gpio_to_irq(stlc45xx_gpio_irq), - IRQ_TYPE_EDGE_RISING); - - disable_irq(gpio_to_irq(stlc45xx_gpio_irq)); - - ret = platform_device_register(&stlc45xx_device); - if (ret) { - stlc45xx_error("Couldn't register wlan_omap device."); - return ret; - } - dev_set_drvdata(&stlc45xx_device.dev, stlc); - - INIT_WORK(&stlc->work, stlc45xx_work); - INIT_WORK(&stlc->work_reset, stlc45xx_work_reset); - INIT_DELAYED_WORK(&stlc->work_tx_timeout, stlc45xx_work_tx_timeout); - mutex_init(&stlc->mutex); - init_completion(&stlc->fw_comp); - spin_lock_init(&stlc->tx_lock); - INIT_LIST_HEAD(&stlc->txbuffer); - INIT_LIST_HEAD(&stlc->tx_pending); - INIT_LIST_HEAD(&stlc->tx_sent); - - hw->flags = IEEE80211_HW_RX_INCLUDES_FCS | - IEEE80211_HW_SIGNAL_DBM | - IEEE80211_HW_NOISE_DBM; - /* four bytes for padding */ - hw->extra_tx_headroom = sizeof(struct s_lm_data_out) + 4; - - /* unit us */ - hw->channel_change_time = 1000; - - hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION); - hw->wiphy->bands[IEEE80211_BAND_2GHZ] = &stlc45xx_band_2ghz; - - SET_IEEE80211_DEV(hw, &spi->dev); - - BUILD_BUG_ON(sizeof(default_cal_rssi) != RSSI_CAL_ARRAY_LEN); - BUILD_BUG_ON(sizeof(default_cal_channels) != CHANNEL_CAL_ARRAY_LEN); - - stlc->cal_rssi = kmemdup(default_cal_rssi, RSSI_CAL_ARRAY_LEN, - GFP_KERNEL); - stlc->cal_channels = kmemdup(default_cal_channels, - CHANNEL_CAL_ARRAY_LEN, - GFP_KERNEL); - - ret = device_create_file(&stlc45xx_device.dev, &dev_attr_cal_rssi); - if (ret < 0) { - stlc45xx_error("failed to create sysfs file cal_rssi"); - goto out; - } - - ret = device_create_file(&stlc45xx_device.dev, &dev_attr_cal_channels); - if (ret < 0) { - stlc45xx_error("failed to create sysfs file cal_channels"); - goto out; - } - - ret = device_create_file(&stlc45xx_device.dev, &dev_attr_tx_buf); - if (ret < 0) { - stlc45xx_error("failed to create sysfs file tx_buf"); - goto out; - } - - ret = stlc45xx_register_mac80211(stlc); - if (ret < 0) - goto out; - - stlc45xx_info("v" DRIVER_VERSION " loaded"); - - stlc45xx_info("config buffer 0x%x-0x%x", - FIRMWARE_CONFIG_START, FIRMWARE_CONFIG_END); - stlc45xx_info("tx 0x%x-0x%x, rx 0x%x-0x%x", - FIRMWARE_TXBUFFER_START, FIRMWARE_TXBUFFER_END, - FIRMWARE_RXBUFFER_START, FIRMWARE_RXBUFFER_END); - -out: - return ret; -} - -static int __devexit stlc45xx_remove(struct spi_device *spi) -{ - struct stlc45xx *stlc = dev_get_drvdata(&spi->dev); - - stlc45xx_debug(DEBUG_FUNC, "%s", __func__); - - platform_device_unregister(&stlc45xx_device); - - ieee80211_unregister_hw(stlc->hw); - - free_irq(gpio_to_irq(stlc45xx_gpio_irq), spi); - - gpio_free(stlc45xx_gpio_power); - gpio_free(stlc45xx_gpio_irq); - - /* FIXME: free cal_channels and cal_rssi? */ - - kfree(stlc->fw); - - mutex_destroy(&stlc->mutex); - - /* frees also stlc */ - ieee80211_free_hw(stlc->hw); - stlc = NULL; - - return 0; -} - - -static struct spi_driver stlc45xx_spi_driver = { - .driver = { - /* use cx3110x name because board-n800.c uses that for the - * SPI port */ - .name = "cx3110x", - .bus = &spi_bus_type, - .owner = THIS_MODULE, - }, - - .probe = stlc45xx_probe, - .remove = __devexit_p(stlc45xx_remove), -}; - -static int __init stlc45xx_init(void) -{ - int ret; - - stlc45xx_debug(DEBUG_FUNC, "%s", __func__); - - ret = spi_register_driver(&stlc45xx_spi_driver); - if (ret < 0) { - stlc45xx_error("failed to register SPI driver: %d", ret); - goto out; - } - -out: - return ret; -} - -static void __exit stlc45xx_exit(void) -{ - stlc45xx_debug(DEBUG_FUNC, "%s", __func__); - - spi_unregister_driver(&stlc45xx_spi_driver); - - stlc45xx_info("unloaded"); -} - -module_init(stlc45xx_init); -module_exit(stlc45xx_exit); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Kalle Valo <kalle.valo@nokia.com>"); diff --git a/drivers/staging/stlc45xx/stlc45xx.h b/drivers/staging/stlc45xx/stlc45xx.h deleted file mode 100644 index ac96bbbde79f..000000000000 --- a/drivers/staging/stlc45xx/stlc45xx.h +++ /dev/null @@ -1,283 +0,0 @@ -/* - * This file is part of stlc45xx - * - * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies). - * - * Contact: Kalle Valo <kalle.valo@nokia.com> - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA - * 02110-1301 USA - * - */ - -#include <linux/mutex.h> -#include <linux/list.h> -#include <net/mac80211.h> - -#include "stlc45xx_lmac.h" - -#define DRIVER_NAME "stlc45xx" -#define DRIVER_VERSION "0.1.3" - -#define DRIVER_PREFIX DRIVER_NAME ": " - -enum { - DEBUG_NONE = 0, - DEBUG_FUNC = 1 << 0, - DEBUG_IRQ = 1 << 1, - DEBUG_BH = 1 << 2, - DEBUG_RX = 1 << 3, - DEBUG_RX_CONTENT = 1 << 5, - DEBUG_TX = 1 << 6, - DEBUG_TX_CONTENT = 1 << 8, - DEBUG_TXBUFFER = 1 << 9, - DEBUG_QUEUE = 1 << 10, - DEBUG_BOOT = 1 << 11, - DEBUG_PSM = 1 << 12, - DEBUG_ALL = ~0, -}; - -#define DEBUG_LEVEL DEBUG_NONE -/* #define DEBUG_LEVEL DEBUG_ALL */ -/* #define DEBUG_LEVEL (DEBUG_TX | DEBUG_RX | DEBUG_IRQ) */ -/* #define DEBUG_LEVEL (DEBUG_TX | DEBUG_MEMREGION | DEBUG_QUEUE) */ -/* #define DEBUG_LEVEL (DEBUG_MEMREGION | DEBUG_QUEUE) */ - -#define stlc45xx_error(fmt, arg...) \ - printk(KERN_ERR DRIVER_PREFIX "ERROR " fmt "\n", ##arg) - -#define stlc45xx_warning(fmt, arg...) \ - printk(KERN_WARNING DRIVER_PREFIX "WARNING " fmt "\n", ##arg) - -#define stlc45xx_info(fmt, arg...) \ - printk(KERN_INFO DRIVER_PREFIX fmt "\n", ##arg) - -#define stlc45xx_debug(level, fmt, arg...) \ - do { \ - if (level & DEBUG_LEVEL) \ - printk(KERN_DEBUG DRIVER_PREFIX fmt "\n", ##arg); \ - } while (0) - -#define stlc45xx_dump(level, buf, len) \ - do { \ - if (level & DEBUG_LEVEL) \ - print_hex_dump(KERN_DEBUG, "", DUMP_PREFIX_OFFSET, \ - 16, 1, buf, len, 1); \ - } while (0) - -#define MAC2STR(a) ((a)[0], (a)[1], (a)[2], (a)[3], (a)[4], (a)[5]) -#define MACSTR "%02x:%02x:%02x:%02x:%02x:%02x" - -/* Bit 15 is read/write bit; ON = READ, OFF = WRITE */ -#define ADDR_READ_BIT_15 0x8000 - -#define SPI_ADRS_ARM_INTERRUPTS 0x00 -#define SPI_ADRS_ARM_INT_EN 0x04 - -#define SPI_ADRS_HOST_INTERRUPTS 0x08 -#define SPI_ADRS_HOST_INT_EN 0x0c -#define SPI_ADRS_HOST_INT_ACK 0x10 - -#define SPI_ADRS_GEN_PURP_1 0x14 -#define SPI_ADRS_GEN_PURP_2 0x18 - -/* high word */ -#define SPI_ADRS_DEV_CTRL_STAT 0x26 - -#define SPI_ADRS_DMA_DATA 0x28 - -#define SPI_ADRS_DMA_WRITE_CTRL 0x2c -#define SPI_ADRS_DMA_WRITE_LEN 0x2e -#define SPI_ADRS_DMA_WRITE_BASE 0x30 - -#define SPI_ADRS_DMA_READ_CTRL 0x34 -#define SPI_ADRS_DMA_READ_LEN 0x36 -#define SPI_ADRS_DMA_READ_BASE 0x38 - -#define SPI_CTRL_STAT_HOST_OVERRIDE 0x8000 -#define SPI_CTRL_STAT_START_HALTED 0x4000 -#define SPI_CTRL_STAT_RAM_BOOT 0x2000 -#define SPI_CTRL_STAT_HOST_RESET 0x1000 -#define SPI_CTRL_STAT_HOST_CPU_EN 0x0800 - -#define SPI_DMA_WRITE_CTRL_ENABLE 0x0001 -#define SPI_DMA_READ_CTRL_ENABLE 0x0001 -#define HOST_ALLOWED (1 << 7) - -#define FIRMWARE_ADDRESS 0x20000 - -#define SPI_TIMEOUT 100 /* msec */ - -#define SPI_MAX_TX_PACKETS 32 - -#define SPI_MAX_PACKET_SIZE 32767 - -#define SPI_TARGET_INT_WAKEUP 0x00000001 -#define SPI_TARGET_INT_SLEEP 0x00000002 -#define SPI_TARGET_INT_RDDONE 0x00000004 - -#define SPI_TARGET_INT_CTS 0x00004000 -#define SPI_TARGET_INT_DR 0x00008000 - -#define SPI_HOST_INT_READY 0x00000001 -#define SPI_HOST_INT_WR_READY 0x00000002 -#define SPI_HOST_INT_SW_UPDATE 0x00000004 -#define SPI_HOST_INT_UPDATE 0x10000000 - -/* clear to send */ -#define SPI_HOST_INT_CTS 0x00004000 - -/* data ready */ -#define SPI_HOST_INT_DR 0x00008000 - -#define SPI_HOST_INTS_DEFAULT \ - (SPI_HOST_INT_READY | SPI_HOST_INT_UPDATE | SPI_HOST_INT_SW_UPDATE) - -#define TARGET_BOOT_SLEEP 50 - -/* The firmware buffer is divided into three areas: - * - * o config area (for control commands) - * o tx buffer - * o rx buffer - */ -#define FIRMWARE_BUFFER_START 0x20200 -#define FIRMWARE_BUFFER_END 0x27c60 -#define FIRMWARE_BUFFER_LEN (FIRMWARE_BUFFER_END - FIRMWARE_BUFFER_START) -#define FIRMWARE_MTU 3240 -#define FIRMWARE_CONFIG_PAYLOAD_LEN 1024 -#define FIRMWARE_CONFIG_START FIRMWARE_BUFFER_START -#define FIRMWARE_CONFIG_LEN (sizeof(struct s_lm_control) + \ - FIRMWARE_CONFIG_PAYLOAD_LEN) -#define FIRMWARE_CONFIG_END (FIRMWARE_CONFIG_START + FIRMWARE_CONFIG_LEN - 1) -#define FIRMWARE_RXBUFFER_LEN (5 * FIRMWARE_MTU + 1024) -#define FIRMWARE_RXBUFFER_START (FIRMWARE_BUFFER_END - FIRMWARE_RXBUFFER_LEN) -#define FIRMWARE_RXBUFFER_END (FIRMWARE_RXBUFFER_START + \ - FIRMWARE_RXBUFFER_LEN - 1) -#define FIRMWARE_TXBUFFER_START (FIRMWARE_BUFFER_START + FIRMWARE_CONFIG_LEN) -#define FIRMWARE_TXBUFFER_LEN (FIRMWARE_BUFFER_LEN - FIRMWARE_CONFIG_LEN - \ - FIRMWARE_RXBUFFER_LEN) -#define FIRMWARE_TXBUFFER_END (FIRMWARE_TXBUFFER_START + \ - FIRMWARE_TXBUFFER_LEN - 1) - -#define FIRMWARE_TXBUFFER_HEADER 100 -#define FIRMWARE_TXBUFFER_TRAILER 4 - -/* FIXME: come up with a proper value */ -#define MAX_FRAME_LEN 2500 - -/* unit is ms */ -#define TX_FRAME_LIFETIME 2000 -#define TX_TIMEOUT 4000 - -#define SUPPORTED_CHANNELS 13 - -/* FIXME */ -/* #define CHANNEL_CAL_LEN offsetof(struct s_lmo_scan, bratemask) - \ */ -/* offsetof(struct s_lmo_scan, channel) */ -#define CHANNEL_CAL_LEN 292 -#define CHANNEL_CAL_ARRAY_LEN (SUPPORTED_CHANNELS * CHANNEL_CAL_LEN) -/* FIXME */ -/* #define RSSI_CAL_LEN sizeof(struct s_lmo_scan) - \ */ -/* offsetof(struct s_lmo_scan, rssical) */ -#define RSSI_CAL_LEN 8 -#define RSSI_CAL_ARRAY_LEN (SUPPORTED_CHANNELS * RSSI_CAL_LEN) - -struct s_dma_regs { - unsigned short cmd; - unsigned short len; - unsigned long addr; -}; - -struct stlc45xx_ie_tim { - u8 dtim_count; - u8 dtim_period; - u8 bmap_control; - u8 pvbmap[251]; -}; - -struct txbuffer { - /* can be removed when switched to skb queue */ - struct list_head tx_list; - - struct list_head buffer_list; - - int start; - int frame_start; - int end; - - struct sk_buff *skb; - u32 handle; - - bool status_needed; - - int header_len; - - /* unit jiffies */ - unsigned long lifetime; -}; - -enum fw_state { - FW_STATE_OFF, - FW_STATE_BOOTING, - FW_STATE_READY, - FW_STATE_RESET, - FW_STATE_RESETTING, -}; - -struct stlc45xx { - struct ieee80211_hw *hw; - struct spi_device *spi; - struct work_struct work; - struct work_struct work_reset; - struct delayed_work work_tx_timeout; - struct mutex mutex; - struct completion fw_comp; - - - u8 bssid[ETH_ALEN]; - u8 mac_addr[ETH_ALEN]; - int channel; - - u8 *cal_rssi; - u8 *cal_channels; - - enum fw_state fw_state; - - spinlock_t tx_lock; - - /* protected by tx_lock */ - struct list_head txbuffer; - - /* protected by tx_lock */ - struct list_head tx_pending; - - /* protected by tx_lock */ - int tx_queue_stopped; - - /* protected by mutex */ - struct list_head tx_sent; - - int tx_frames; - - u8 *fw; - int fw_len; - - bool psm; - bool associated; - int aid; - bool pspolling; -}; - - diff --git a/drivers/staging/stlc45xx/stlc45xx_lmac.h b/drivers/staging/stlc45xx/stlc45xx_lmac.h deleted file mode 100644 index af5db801347f..000000000000 --- a/drivers/staging/stlc45xx/stlc45xx_lmac.h +++ /dev/null @@ -1,434 +0,0 @@ -/************************************************************************ -* This is the LMAC API interface header file for STLC4560. * -* Copyright (C) 2007 Conexant Systems, Inc. * -* This program is free software; you can redistribute it and/or * -* modify it under the terms of the GNU General Public License * -* as published by the Free Software Foundation; either version 2 * -* of the License, or (at your option) any later version. * -* * -* This program is distributed in the hope that it will be useful, * -* but WITHOUT ANY WARRANTY; without even the implied warranty of * -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * -* GNU General Public License for more details. * -* * -* You should have received a copy of the GNU General Public License * -* along with this program. If not, see <http://www.gnu.org/licenses/>.* -*************************************************************************/ - -#ifndef __lmac_h__ -#define __lmac_h__ - -#define LM_TOP_VARIANT 0x0506 -#define LM_BOTTOM_VARIANT 0x0506 - -/* - * LMAC - UMAC interface definition: - */ - -#define LM_FLAG_CONTROL 0x8000 -#define LM_FLAG_ALIGN 0x4000 - -#define LM_CTRL_OPSET 0x0001 - -#define LM_OUT_PROMISC 0x0001 -#define LM_OUT_TIMESTAMP 0x0002 -#define LM_OUT_SEQNR 0x0004 -#define LM_OUT_BURST 0x0010 -#define LM_OUT_NOCANCEL 0x0020 -#define LM_OUT_CLEARTIM 0x0040 -#define LM_OUT_HITCHHIKE 0x0080 -#define LM_OUT_COMPRESS 0x0100 -#define LM_OUT_CONCAT 0x0200 -#define LM_OUT_PCS_ACCEPT 0x0400 -#define LM_OUT_WAITEOSP 0x0800 - - -#define LM_ALOFT_SP 0x10 -#define LM_ALOFT_CTS 0x20 -#define LM_ALOFT_RTS 0x40 -#define LM_ALOFT_MASK 0x1f -#define LM_ALOFT_RATE 0x0f - -#define LM_IN_FCS_GOOD 0x0001 -#define LM_IN_MATCH_MAC 0x0002 -#define LM_IN_MCBC 0x0004 -#define LM_IN_BEACON 0x0008 -#define LM_IN_MATCH_BSS 0x0010 -#define LM_IN_BCAST_BSS 0x0020 -#define LM_IN_DATA 0x0040 -#define LM_IN_TRUNCATED 0x0080 - -#define LM_IN_TRANSPARENT 0x0200 - -#define LM_QUEUE_BEACON 0 -#define LM_QUEUE_SCAN 1 -#define LM_QUEUE_MGT 2 -#define LM_QUEUE_MCBC 3 -#define LM_QUEUE_DATA 4 -#define LM_QUEUE_DATA0 4 -#define LM_QUEUE_DATA1 5 -#define LM_QUEUE_DATA2 6 -#define LM_QUEUE_DATA3 7 - -#define LM_SETUP_INFRA 0x0001 -#define LM_SETUP_IBSS 0x0002 -#define LM_SETUP_TRANSPARENT 0x0008 -#define LM_SETUP_PROMISCUOUS 0x0010 -#define LM_SETUP_HIBERNATE 0x0020 -#define LM_SETUP_NOACK 0x0040 -#define LM_SETUP_RX_DISABLED 0x0080 - -#define LM_ANTENNA_0 0 -#define LM_ANTENNA_1 1 -#define LM_ANTENNA_DIVERSITY 2 - -#define LM_TX_FAILED 0x0001 -#define LM_TX_PSM 0x0002 -#define LM_TX_PSM_CANCELLED 0x0004 - -#define LM_SCAN_EXIT 0x0001 -#define LM_SCAN_TRAP 0x0002 -#define LM_SCAN_ACTIVE 0x0004 -#define LM_SCAN_FILTER 0x0008 - -#define LM_PSM 0x0001 -#define LM_PSM_DTIM 0x0002 -#define LM_PSM_MCBC 0x0004 -#define LM_PSM_CHECKSUM 0x0008 -#define LM_PSM_SKIP_MORE_DATA 0x0010 -#define LM_PSM_BEACON_TIMEOUT 0x0020 -#define LM_PSM_HFOSLEEP 0x0040 -#define LM_PSM_AUTOSWITCH_SLEEP 0x0080 -#define LM_PSM_LPIT 0x0100 -#define LM_PSM_BF_UCAST_SKIP 0x0200 -#define LM_PSM_BF_MCAST_SKIP 0x0400 - -/* hfosleep */ -#define LM_PSM_SLEEP_OPTION_MASK (LM_PSM_AUTOSWITCH_SLEEP | LM_PSM_HFOSLEEP) -#define LM_PSM_SLEEP_OPTION_SHIFT 6 -/* hfosleepend */ -#define LM_PSM_BF_OPTION_MASK (LM_PSM_BF_MCAST_SKIP | LM_PSM_BF_UCAST_SKIP) -#define LM_PSM_BF_OPTION_SHIFT 9 - - -#define LM_PRIVACC_WEP 0x01 -#define LM_PRIVACC_TKIP 0x02 -#define LM_PRIVACC_MICHAEL 0x04 -#define LM_PRIVACC_CCX_KP 0x08 -#define LM_PRIVACC_CCX_MIC 0x10 -#define LM_PRIVACC_AES_CCMP 0x20 - -/* size of s_lm_descr in words */ -#define LM_DESCR_SIZE_WORDS 11 - -#ifndef __ASSEMBLER__ - -enum { - LM_MODE_CLIENT = 0, - LM_MODE_AP -}; - -struct s_lm_descr { - uint16_t modes; - uint16_t flags; - uint32_t buffer_start; - uint32_t buffer_end; - uint8_t header; - uint8_t trailer; - uint8_t tx_queues; - uint8_t tx_depth; - uint8_t privacy; - uint8_t rx_keycache; - uint8_t tim_size; - uint8_t pad1; - uint8_t rates[16]; - uint32_t link; - uint16_t mtu; -}; - - -struct s_lm_control { - uint16_t flags; - uint16_t length; - uint32_t handle; - uint16_t oid; - uint16_t pad; - /* uint8_t data[]; */ -}; - -enum { - LM_PRIV_NONE = 0, - LM_PRIV_WEP, - LM_PRIV_TKIP, - LM_PRIV_TKIPMICHAEL, - LM_PRIV_CCX_WEPMIC, - LM_PRIV_CCX_KPMIC, - LM_PRIV_CCX_KP, - LM_PRIV_AES_CCMP -}; - -enum { - LM_DECRYPT_NONE, - LM_DECRYPT_OK, - LM_DECRYPT_NOKEY, - LM_DECRYPT_NOMICHAEL, - LM_DECRYPT_NOCKIPMIC, - LM_DECRYPT_FAIL_WEP, - LM_DECRYPT_FAIL_TKIP, - LM_DECRYPT_FAIL_MICHAEL, - LM_DECRYPT_FAIL_CKIPKP, - LM_DECRYPT_FAIL_CKIPMIC, - LM_DECRYPT_FAIL_AESCCMP -}; - -struct s_lm_data_out { - uint16_t flags; - uint16_t length; - uint32_t handle; - uint16_t aid; - uint8_t rts_retries; - uint8_t retries; - uint8_t aloft[8]; - uint8_t aloft_ctrl; - uint8_t crypt_offset; - uint8_t keytype; - uint8_t keylen; - uint8_t key[16]; - uint8_t queue; - uint8_t backlog; - uint16_t durations[4]; - uint8_t antenna; - uint8_t cts; - int16_t power; - uint8_t pad[2]; - /*uint8_t data[];*/ -}; - -#define LM_RCPI_INVALID (0xff) - -struct s_lm_data_in { - uint16_t flags; - uint16_t length; - uint16_t frequency; - uint8_t antenna; - uint8_t rate; - uint8_t rcpi; - uint8_t sq; - uint8_t decrypt; - uint8_t rssi_raw; - uint32_t clock[2]; - /*uint8_t data[];*/ -}; - -union u_lm_data { - struct s_lm_data_out out; - struct s_lm_data_in in; -}; - -enum { - LM_OID_SETUP = 0, - LM_OID_SCAN = 1, - LM_OID_TRAP = 2, - LM_OID_EDCF = 3, - LM_OID_KEYCACHE = 4, - LM_OID_PSM = 6, - LM_OID_TXCANCEL = 7, - LM_OID_TX = 8, - LM_OID_BURST = 9, - LM_OID_STATS = 10, - LM_OID_LED = 13, - LM_OID_TIMER = 15, - LM_OID_NAV = 20, - LM_OID_PCS = 22, - LM_OID_BT_BALANCER = 28, - LM_OID_GROUP_ADDRESS_TABLE = 30, - LM_OID_ARPTABLE = 31, - LM_OID_BT_OPTIONS = 35 -}; - -enum { - LM_FRONTEND_UNKNOWN = 0, - LM_FRONTEND_DUETTE3, - LM_FRONTEND_DUETTE2, - LM_FRONTEND_FRISBEE, - LM_FRONTEND_CROSSBOW, - LM_FRONTEND_LONGBOW -}; - - -#define INVALID_LPF_BANDWIDTH 0xffff -#define INVALID_OSC_START_DELAY 0xffff - -struct s_lmo_setup { - uint16_t flags; - uint8_t macaddr[6]; - uint8_t bssid[6]; - uint8_t antenna; - uint8_t rx_align; - uint32_t rx_buffer; - uint16_t rx_mtu; - uint16_t frontend; - uint16_t timeout; - uint16_t truncate; - uint32_t bratemask; - uint8_t sbss_offset; - uint8_t mcast_window; - uint8_t rx_rssi_threshold; - uint8_t rx_ed_threshold; - uint32_t ref_clock; - uint16_t lpf_bandwidth; - uint16_t osc_start_delay; -}; - - -struct s_lmo_scan { - uint16_t flags; - uint16_t dwell; - uint8_t channel[292]; - uint32_t bratemask; - uint8_t aloft[8]; - uint8_t rssical[8]; -}; - - -enum { - LM_TRAP_SCAN = 0, - LM_TRAP_TIMER, - LM_TRAP_BEACON_TX, - LM_TRAP_FAA_RADIO_ON, - LM_TRAP_FAA_RADIO_OFF, - LM_TRAP_RADAR, - LM_TRAP_NO_BEACON, - LM_TRAP_TBTT, - LM_TRAP_SCO_ENTER, - LM_TRAP_SCO_EXIT -}; - -struct s_lmo_trap { - uint16_t event; - uint16_t frequency; -}; - -struct s_lmo_timer { - uint32_t interval; -}; - -struct s_lmo_nav { - uint32_t period; -}; - - -struct s_lmo_edcf_queue; - -struct s_lmo_edcf { - uint8_t flags; - uint8_t slottime; - uint8_t sifs; - uint8_t eofpad; - struct s_lmo_edcf_queue { - uint8_t aifs; - uint8_t pad0; - uint16_t cwmin; - uint16_t cwmax; - uint16_t txop; - } queues[8]; - uint8_t mapping[4]; - uint16_t maxburst; - uint16_t round_trip_delay; -}; - -struct s_lmo_keycache { - uint8_t entry; - uint8_t keyid; - uint8_t address[6]; - uint8_t pad[2]; - uint8_t keytype; - uint8_t keylen; - uint8_t key[24]; -}; - - -struct s_lm_interval; - -struct s_lmo_psm { - uint16_t flags; - uint16_t aid; - struct s_lm_interval { - uint16_t interval; - uint16_t periods; - } intervals[4]; - /* uint16_t pad; */ - uint8_t beacon_rcpi_skip_max; - uint8_t rcpi_delta_threshold; - uint8_t nr; - uint8_t exclude[1]; -}; - -#define MC_FILTER_ADDRESS_NUM 4 - -struct s_lmo_group_address_table { - uint16_t filter_enable; - uint16_t num_address; - uint8_t macaddr_list[MC_FILTER_ADDRESS_NUM][6]; -}; - -struct s_lmo_txcancel { - uint32_t address[1]; -}; - - -struct s_lmo_tx { - uint8_t flags; - uint8_t retries; - uint8_t rcpi; - uint8_t sq; - uint16_t seqctrl; - uint8_t antenna; - uint8_t pad; -}; - -struct s_lmo_burst { - uint8_t flags; - uint8_t queue; - uint8_t backlog; - uint8_t pad; - uint16_t durations[32]; -}; - -struct s_lmo_stats { - uint32_t valid; - uint32_t fcs; - uint32_t abort; - uint32_t phyabort; - uint32_t rts_success; - uint32_t rts_fail; - uint32_t timestamp; - uint32_t time_tx; - uint32_t noisefloor; - uint32_t sample_noise[8]; - uint32_t sample_cca; - uint32_t sample_tx; -}; - - -struct s_lmo_led { - uint16_t flags; - uint16_t mask[2]; - uint16_t delay/*[2]*/; -}; - - -struct s_lmo_bt_balancer { - uint16_t prio_thresh; - uint16_t acl_thresh; -}; - - -struct s_lmo_arp_table { - uint16_t filter_enable; - uint32_t ipaddr; -}; - -#endif /* __ASSEMBLER__ */ - -#endif /* __lmac_h__ */ diff --git a/drivers/staging/vme/bridges/vme_ca91cx42.c b/drivers/staging/vme/bridges/vme_ca91cx42.c index 3d2a84c45829..e139eaeaa174 100644 --- a/drivers/staging/vme/bridges/vme_ca91cx42.c +++ b/drivers/staging/vme/bridges/vme_ca91cx42.c @@ -25,6 +25,7 @@ #include <linux/poll.h> #include <linux/interrupt.h> #include <linux/spinlock.h> +#include <linux/sched.h> #include <asm/time.h> #include <asm/io.h> #include <asm/uaccess.h> diff --git a/drivers/staging/vme/bridges/vme_tsi148.c b/drivers/staging/vme/bridges/vme_tsi148.c index 8960fa9ee7aa..00fe0803c21c 100644 --- a/drivers/staging/vme/bridges/vme_tsi148.c +++ b/drivers/staging/vme/bridges/vme_tsi148.c @@ -25,6 +25,7 @@ #include <linux/dma-mapping.h> #include <linux/interrupt.h> #include <linux/spinlock.h> +#include <linux/sched.h> #include <asm/time.h> #include <asm/io.h> #include <asm/uaccess.h> diff --git a/drivers/staging/vt6656/main_usb.c b/drivers/staging/vt6656/main_usb.c index 7f96bcaf1c60..05186110c029 100644 --- a/drivers/staging/vt6656/main_usb.c +++ b/drivers/staging/vt6656/main_usb.c @@ -1332,7 +1332,6 @@ device_release_WPADEV(pDevice); free_netdev(pDevice->dev); } - kfree(pDevice); DBG_PRT(MSG_LEVEL_DEBUG, KERN_INFO "device_disconnect3.. \n"); } diff --git a/drivers/staging/winbond/Kconfig b/drivers/staging/winbond/Kconfig index 940460c39f36..132671d96d0d 100644 --- a/drivers/staging/winbond/Kconfig +++ b/drivers/staging/winbond/Kconfig @@ -1,6 +1,6 @@ config W35UND tristate "IS89C35 WLAN USB driver" - depends on MAC80211 && WLAN_80211 && USB && EXPERIMENTAL + depends on MAC80211 && WLAN && USB && EXPERIMENTAL default n ---help--- This is highly experimental driver for Winbond WIFI card. diff --git a/drivers/staging/winbond/wbusb.c b/drivers/staging/winbond/wbusb.c index 8950724f168e..067082a7d759 100644 --- a/drivers/staging/winbond/wbusb.c +++ b/drivers/staging/winbond/wbusb.c @@ -51,10 +51,26 @@ static struct ieee80211_supported_band wbsoft_band_2GHz = { .n_bitrates = ARRAY_SIZE(wbsoft_rates), }; +static void hal_set_beacon_period(struct hw_data *pHwData, u16 beacon_period) +{ + u32 tmp; + + if (pHwData->SurpriseRemove) + return; + + pHwData->BeaconPeriod = beacon_period; + tmp = pHwData->BeaconPeriod << 16; + tmp |= pHwData->ProbeDelay; + Wb35Reg_Write(pHwData, 0x0848, tmp); +} + static int wbsoft_add_interface(struct ieee80211_hw *dev, struct ieee80211_if_init_conf *conf) { - printk("wbsoft_add interface called\n"); + struct wbsoft_priv *priv = dev->priv; + + hal_set_beacon_period(&priv->sHwData, conf->vif->bss_conf.beacon_int); + return 0; } @@ -83,10 +99,16 @@ static int wbsoft_get_tx_stats(struct ieee80211_hw *hw, return 0; } +static u64 wbsoft_prepare_multicast(struct ieee80211_hw *hw, int mc_count, + struct dev_addr_list *mc_list) +{ + return mc_count; +} + static void wbsoft_configure_filter(struct ieee80211_hw *dev, unsigned int changed_flags, unsigned int *total_flags, - int mc_count, struct dev_mc_list *mclist) + u64 multicast) { unsigned int new_flags; @@ -94,7 +116,7 @@ static void wbsoft_configure_filter(struct ieee80211_hw *dev, if (*total_flags & FIF_PROMISC_IN_BSS) new_flags |= FIF_PROMISC_IN_BSS; - else if ((*total_flags & FIF_ALLMULTI) || (mc_count > 32)) + else if ((*total_flags & FIF_ALLMULTI) || (multicast > 32)) new_flags |= FIF_ALLMULTI; dev->flags &= ~IEEE80211_HW_RX_INCLUDES_FCS; @@ -138,19 +160,6 @@ static void hal_set_radio_mode(struct hw_data *pHwData, unsigned char radio_off) Wb35Reg_Write(pHwData, 0x0824, reg->M24_MacControl); } -static void hal_set_beacon_period(struct hw_data *pHwData, u16 beacon_period) -{ - u32 tmp; - - if (pHwData->SurpriseRemove) - return; - - pHwData->BeaconPeriod = beacon_period; - tmp = pHwData->BeaconPeriod << 16; - tmp |= pHwData->ProbeDelay; - Wb35Reg_Write(pHwData, 0x0848, tmp); -} - static void hal_set_current_channel_ex(struct hw_data *pHwData, ChanInfo channel) { @@ -244,7 +253,6 @@ static void hal_set_accept_beacon(struct hw_data *pHwData, u8 enable) static int wbsoft_config(struct ieee80211_hw *dev, u32 changed) { struct wbsoft_priv *priv = dev->priv; - struct ieee80211_conf *conf = &dev->conf; ChanInfo ch; printk("wbsoft_config called\n"); @@ -254,7 +262,6 @@ static int wbsoft_config(struct ieee80211_hw *dev, u32 changed) ch.ChanNo = 1; hal_set_current_channel(&priv->sHwData, ch); - hal_set_beacon_period(&priv->sHwData, conf->beacon_int); hal_set_accept_broadcast(&priv->sHwData, 1); hal_set_accept_promiscuous(&priv->sHwData, 1); hal_set_accept_multicast(&priv->sHwData, 1); @@ -277,6 +284,7 @@ static const struct ieee80211_ops wbsoft_ops = { .add_interface = wbsoft_add_interface, .remove_interface = wbsoft_remove_interface, .config = wbsoft_config, + .prepare_multicast = wbsoft_prepare_multicast, .configure_filter = wbsoft_configure_filter, .get_stats = wbsoft_get_stats, .get_tx_stats = wbsoft_get_tx_stats, diff --git a/drivers/staging/wlan-ng/Kconfig b/drivers/staging/wlan-ng/Kconfig index 9959b658c8cf..f44294b0d8dc 100644 --- a/drivers/staging/wlan-ng/Kconfig +++ b/drivers/staging/wlan-ng/Kconfig @@ -1,6 +1,6 @@ config PRISM2_USB tristate "Prism2.5/3 USB driver" - depends on WLAN_80211 && USB && WIRELESS_EXT + depends on WLAN && USB && WIRELESS_EXT default n ---help--- This is the wlan-ng prism 2.5/3 USB driver for a wide range of diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig index a86e952ed4ca..bf7c687519ef 100644 --- a/drivers/thermal/Kconfig +++ b/drivers/thermal/Kconfig @@ -15,6 +15,7 @@ menuconfig THERMAL config THERMAL_HWMON bool "Hardware monitoring support" + depends on THERMAL depends on HWMON=y || HWMON=THERMAL help The generic thermal sysfs driver's hardware monitoring support diff --git a/drivers/thermal/thermal_sys.c b/drivers/thermal/thermal_sys.c index 4e83c297ec9e..6f8d8f971212 100644 --- a/drivers/thermal/thermal_sys.c +++ b/drivers/thermal/thermal_sys.c @@ -180,15 +180,15 @@ trip_point_type_show(struct device *dev, struct device_attribute *attr, switch (type) { case THERMAL_TRIP_CRITICAL: - return sprintf(buf, "critical"); + return sprintf(buf, "critical\n"); case THERMAL_TRIP_HOT: - return sprintf(buf, "hot"); + return sprintf(buf, "hot\n"); case THERMAL_TRIP_PASSIVE: - return sprintf(buf, "passive"); + return sprintf(buf, "passive\n"); case THERMAL_TRIP_ACTIVE: - return sprintf(buf, "active"); + return sprintf(buf, "active\n"); default: - return sprintf(buf, "unknown"); + return sprintf(buf, "unknown\n"); } } diff --git a/drivers/uio/uio.c b/drivers/uio/uio.c index 03efb065455f..e941367dd28f 100644 --- a/drivers/uio/uio.c +++ b/drivers/uio/uio.c @@ -19,6 +19,7 @@ #include <linux/device.h> #include <linux/mm.h> #include <linux/idr.h> +#include <linux/sched.h> #include <linux/string.h> #include <linux/kobject.h> #include <linux/uio_driver.h> @@ -658,7 +659,7 @@ static int uio_vma_fault(struct vm_area_struct *vma, struct vm_fault *vmf) return 0; } -static struct vm_operations_struct uio_vm_ops = { +static const struct vm_operations_struct uio_vm_ops = { .open = uio_vma_open, .close = uio_vma_close, .fault = uio_vma_fault, diff --git a/drivers/uio/uio_pdrv_genirq.c b/drivers/uio/uio_pdrv_genirq.c index 02347c57357d..aa53db9f2e88 100644 --- a/drivers/uio/uio_pdrv_genirq.c +++ b/drivers/uio/uio_pdrv_genirq.c @@ -178,6 +178,7 @@ static int uio_pdrv_genirq_probe(struct platform_device *pdev) return 0; bad1: kfree(priv); + pm_runtime_disable(&pdev->dev); bad0: return ret; } diff --git a/drivers/usb/Kconfig b/drivers/usb/Kconfig index dcd49f1e96d0..240750881d28 100644 --- a/drivers/usb/Kconfig +++ b/drivers/usb/Kconfig @@ -22,7 +22,6 @@ config USB_ARCH_HAS_HCD default y if PCMCIA && !M32R # sl811_cs default y if ARM # SL-811 default y if SUPERH # r8a66597-hcd - default y if MICROBLAZE default PCI # many non-PCI SOC chips embed OHCI @@ -39,6 +38,7 @@ config USB_ARCH_HAS_OHCI default y if ARCH_AT91 default y if ARCH_PNX4008 && I2C default y if MFD_TC6393XB + default y if ARCH_W90X900 # PPC: default y if STB03xxx default y if PPC_MPC52xx @@ -58,6 +58,8 @@ config USB_ARCH_HAS_EHCI default y if PPC_83xx default y if SOC_AU1200 default y if ARCH_IXP4XX + default y if ARCH_W90X900 + default y if ARCH_AT91SAM9G45 default PCI # ARM SA1111 chips have a non-PCI based "OHCI-compatible" USB host interface. diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile index 19cb7d5480d7..be3c9b80bc9f 100644 --- a/drivers/usb/Makefile +++ b/drivers/usb/Makefile @@ -16,6 +16,7 @@ obj-$(CONFIG_USB_UHCI_HCD) += host/ obj-$(CONFIG_USB_FHCI_HCD) += host/ obj-$(CONFIG_USB_XHCI_HCD) += host/ obj-$(CONFIG_USB_SL811_HCD) += host/ +obj-$(CONFIG_USB_ISP1362_HCD) += host/ obj-$(CONFIG_USB_U132_HCD) += host/ obj-$(CONFIG_USB_R8A66597_HCD) += host/ obj-$(CONFIG_USB_HWA_HCD) += host/ @@ -39,6 +40,7 @@ obj-$(CONFIG_USB_MICROTEK) += image/ obj-$(CONFIG_USB_SERIAL) += serial/ obj-$(CONFIG_USB) += misc/ +obj-y += early/ obj-$(CONFIG_USB_ATM) += atm/ obj-$(CONFIG_USB_SPEEDTOUCH) += atm/ diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c index 85a1a55815cf..e4eca7810bcf 100644 --- a/drivers/usb/class/cdc-acm.c +++ b/drivers/usb/class/cdc-acm.c @@ -59,6 +59,7 @@ #include <linux/init.h> #include <linux/slab.h> #include <linux/tty.h> +#include <linux/serial.h> #include <linux/tty_driver.h> #include <linux/tty_flip.h> #include <linux/module.h> @@ -608,8 +609,9 @@ static int acm_tty_open(struct tty_struct *tty, struct file *filp) acm->throttle = 0; - tasklet_schedule(&acm->urb_task); + set_bit(ASYNCB_INITIALIZED, &acm->port.flags); rv = tty_port_block_til_ready(&acm->port, tty, filp); + tasklet_schedule(&acm->urb_task); done: mutex_unlock(&acm->mutex); err_out: @@ -684,15 +686,21 @@ static void acm_tty_close(struct tty_struct *tty, struct file *filp) /* Perform the closing process and see if we need to do the hardware shutdown */ - if (!acm || tty_port_close_start(&acm->port, tty, filp) == 0) + if (!acm) + return; + if (tty_port_close_start(&acm->port, tty, filp) == 0) { + mutex_lock(&open_mutex); + if (!acm->dev) { + tty_port_tty_set(&acm->port, NULL); + acm_tty_unregister(acm); + tty->driver_data = NULL; + } + mutex_unlock(&open_mutex); return; + } acm_port_down(acm, 0); tty_port_close_end(&acm->port, tty); - mutex_lock(&open_mutex); tty_port_tty_set(&acm->port, NULL); - if (!acm->dev) - acm_tty_unregister(acm); - mutex_unlock(&open_mutex); } static int acm_tty_write(struct tty_struct *tty, diff --git a/drivers/usb/class/cdc-wdm.c b/drivers/usb/class/cdc-wdm.c index 8c64c018b676..3e564bfe17d1 100644 --- a/drivers/usb/class/cdc-wdm.c +++ b/drivers/usb/class/cdc-wdm.c @@ -313,8 +313,13 @@ static ssize_t wdm_write r = usb_autopm_get_interface(desc->intf); if (r < 0) goto outnp; - r = wait_event_interruptible(desc->wait, !test_bit(WDM_IN_USE, - &desc->flags)); + + if (!file->f_flags && O_NONBLOCK) + r = wait_event_interruptible(desc->wait, !test_bit(WDM_IN_USE, + &desc->flags)); + else + if (test_bit(WDM_IN_USE, &desc->flags)) + r = -EAGAIN; if (r < 0) goto out; @@ -377,7 +382,7 @@ outnl: static ssize_t wdm_read (struct file *file, char __user *buffer, size_t count, loff_t *ppos) { - int rv, cntr; + int rv, cntr = 0; int i = 0; struct wdm_device *desc = file->private_data; @@ -389,10 +394,23 @@ static ssize_t wdm_read if (desc->length == 0) { desc->read = 0; retry: + if (test_bit(WDM_DISCONNECTING, &desc->flags)) { + rv = -ENODEV; + goto err; + } i++; - rv = wait_event_interruptible(desc->wait, - test_bit(WDM_READ, &desc->flags)); + if (file->f_flags & O_NONBLOCK) { + if (!test_bit(WDM_READ, &desc->flags)) { + rv = cntr ? cntr : -EAGAIN; + goto err; + } + rv = 0; + } else { + rv = wait_event_interruptible(desc->wait, + test_bit(WDM_READ, &desc->flags)); + } + /* may have happened while we slept */ if (test_bit(WDM_DISCONNECTING, &desc->flags)) { rv = -ENODEV; goto err; @@ -448,7 +466,7 @@ retry: err: mutex_unlock(&desc->rlock); - if (rv < 0) + if (rv < 0 && rv != -EAGAIN) dev_err(&desc->intf->dev, "wdm_read: exit error\n"); return rv; } diff --git a/drivers/usb/class/usbtmc.c b/drivers/usb/class/usbtmc.c index b09a527f7341..2473cf0c6b1d 100644 --- a/drivers/usb/class/usbtmc.c +++ b/drivers/usb/class/usbtmc.c @@ -39,7 +39,7 @@ #define USBTMC_SIZE_IOBUFFER 2048 /* Default USB timeout (in milliseconds) */ -#define USBTMC_TIMEOUT 10 +#define USBTMC_TIMEOUT 5000 /* * Maximum number of read cycles to empty bulk in endpoint during CLEAR and @@ -57,7 +57,9 @@ MODULE_DEVICE_TABLE(usb, usbtmc_devices); /* * This structure is the capabilities for the device - * See section 4.2.1.8 of the USBTMC specification for details. + * See section 4.2.1.8 of the USBTMC specification, + * and section 4.2.2 of the USBTMC usb488 subclass + * specification for details. */ struct usbtmc_dev_capabilities { __u8 interface_capabilities; @@ -86,6 +88,8 @@ struct usbtmc_device_data { bool TermCharEnabled; bool auto_abort; + bool zombie; /* fd of disconnected device */ + struct usbtmc_dev_capabilities capabilities; struct kref kref; struct mutex io_mutex; /* only one i/o function running at a time */ @@ -367,13 +371,13 @@ static ssize_t usbtmc_read(struct file *filp, char __user *buf, { struct usbtmc_device_data *data; struct device *dev; - unsigned long int n_characters; + u32 n_characters; u8 *buffer; int actual; - int done; - int remaining; + size_t done; + size_t remaining; int retval; - int this_part; + size_t this_part; /* Get pointer to private data structure */ data = filp->private_data; @@ -384,6 +388,10 @@ static ssize_t usbtmc_read(struct file *filp, char __user *buf, return -ENOMEM; mutex_lock(&data->io_mutex); + if (data->zombie) { + retval = -ENODEV; + goto exit; + } remaining = count; done = 0; @@ -401,10 +409,10 @@ static ssize_t usbtmc_read(struct file *filp, char __user *buf, buffer[1] = data->bTag; buffer[2] = ~(data->bTag); buffer[3] = 0; /* Reserved */ - buffer[4] = (this_part - 12 - 3) & 255; - buffer[5] = ((this_part - 12 - 3) >> 8) & 255; - buffer[6] = ((this_part - 12 - 3) >> 16) & 255; - buffer[7] = ((this_part - 12 - 3) >> 24) & 255; + buffer[4] = (this_part) & 255; + buffer[5] = ((this_part) >> 8) & 255; + buffer[6] = ((this_part) >> 16) & 255; + buffer[7] = ((this_part) >> 24) & 255; buffer[8] = data->TermCharEnabled * 2; /* Use term character? */ buffer[9] = data->TermChar; @@ -455,6 +463,22 @@ static ssize_t usbtmc_read(struct file *filp, char __user *buf, (buffer[6] << 16) + (buffer[7] << 24); + /* Ensure the instrument doesn't lie about it */ + if(n_characters > actual - 12) { + dev_err(dev, "Device lies about message size: %u > %d\n", n_characters, actual - 12); + n_characters = actual - 12; + } + + /* Ensure the instrument doesn't send more back than requested */ + if(n_characters > this_part) { + dev_err(dev, "Device returns more than requested: %zu > %zu\n", done + n_characters, done + this_part); + n_characters = this_part; + } + + /* Bound amount of data received by amount of data requested */ + if (n_characters > this_part) + n_characters = this_part; + /* Copy buffer to user space */ if (copy_to_user(buf + done, &buffer[12], n_characters)) { /* There must have been an addressing problem */ @@ -463,8 +487,11 @@ static ssize_t usbtmc_read(struct file *filp, char __user *buf, } done += n_characters; - if (n_characters < USBTMC_SIZE_IOBUFFER) + /* Terminate if end-of-message bit recieved from device */ + if ((buffer[8] & 0x01) && (actual >= n_characters + 12)) remaining = 0; + else + remaining -= n_characters; } /* Update file position value */ @@ -496,6 +523,10 @@ static ssize_t usbtmc_write(struct file *filp, const char __user *buf, return -ENOMEM; mutex_lock(&data->io_mutex); + if (data->zombie) { + retval = -ENODEV; + goto exit; + } remaining = count; done = 0; @@ -767,20 +798,21 @@ static int get_capabilities(struct usbtmc_device_data *data) } dev_dbg(dev, "GET_CAPABILITIES returned %x\n", buffer[0]); - dev_dbg(dev, "Interface capabilities are %x\n", buffer[4]); - dev_dbg(dev, "Device capabilities are %x\n", buffer[5]); - dev_dbg(dev, "USB488 interface capabilities are %x\n", buffer[14]); - dev_dbg(dev, "USB488 device capabilities are %x\n", buffer[15]); if (buffer[0] != USBTMC_STATUS_SUCCESS) { dev_err(dev, "GET_CAPABILITIES returned %x\n", buffer[0]); rv = -EPERM; goto err_out; } + dev_dbg(dev, "Interface capabilities are %x\n", buffer[4]); + dev_dbg(dev, "Device capabilities are %x\n", buffer[5]); + dev_dbg(dev, "USB488 interface capabilities are %x\n", buffer[14]); + dev_dbg(dev, "USB488 device capabilities are %x\n", buffer[15]); data->capabilities.interface_capabilities = buffer[4]; data->capabilities.device_capabilities = buffer[5]; data->capabilities.usb488_interface_capabilities = buffer[14]; data->capabilities.usb488_device_capabilities = buffer[15]; + rv = 0; err_out: kfree(buffer); @@ -925,6 +957,10 @@ static long usbtmc_ioctl(struct file *file, unsigned int cmd, unsigned long arg) data = file->private_data; mutex_lock(&data->io_mutex); + if (data->zombie) { + retval = -ENODEV; + goto skip_io_on_zombie; + } switch (cmd) { case USBTMC_IOCTL_CLEAR_OUT_HALT: @@ -952,11 +988,12 @@ static long usbtmc_ioctl(struct file *file, unsigned int cmd, unsigned long arg) break; } +skip_io_on_zombie: mutex_unlock(&data->io_mutex); return retval; } -static struct file_operations fops = { +static const struct file_operations fops = { .owner = THIS_MODULE, .read = usbtmc_read, .write = usbtmc_write, @@ -995,6 +1032,7 @@ static int usbtmc_probe(struct usb_interface *intf, usb_set_intfdata(intf, data); kref_init(&data->kref); mutex_init(&data->io_mutex); + data->zombie = 0; /* Initialize USBTMC bTag and other fields */ data->bTag = 1; @@ -1065,14 +1103,30 @@ static void usbtmc_disconnect(struct usb_interface *intf) usb_deregister_dev(intf, &usbtmc_class); sysfs_remove_group(&intf->dev.kobj, &capability_attr_grp); sysfs_remove_group(&intf->dev.kobj, &data_attr_grp); + mutex_lock(&data->io_mutex); + data->zombie = 1; + mutex_unlock(&data->io_mutex); kref_put(&data->kref, usbtmc_delete); } +static int usbtmc_suspend (struct usb_interface *intf, pm_message_t message) +{ + /* this driver does not have pending URBs */ + return 0; +} + +static int usbtmc_resume (struct usb_interface *intf) +{ + return 0; +} + static struct usb_driver usbtmc_driver = { .name = "usbtmc", .id_table = usbtmc_devices, .probe = usbtmc_probe, - .disconnect = usbtmc_disconnect + .disconnect = usbtmc_disconnect, + .suspend = usbtmc_suspend, + .resume = usbtmc_resume, }; static int __init usbtmc_init(void) diff --git a/drivers/usb/core/config.c b/drivers/usb/core/config.c index a16c538d0132..0d3af6a6ee49 100644 --- a/drivers/usb/core/config.c +++ b/drivers/usb/core/config.c @@ -105,7 +105,7 @@ static int usb_parse_ss_endpoint_companion(struct device *ddev, int cfgno, ep->ss_ep_comp->extralen = i; buffer += i; size -= i; - retval = buffer - buffer_start + i; + retval = buffer - buffer_start; if (num_skipped > 0) dev_dbg(ddev, "skipped %d descriptor%s after %s\n", num_skipped, plural(num_skipped), diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c index 4247eccf858c..181f78c84105 100644 --- a/drivers/usb/core/devio.c +++ b/drivers/usb/core/devio.c @@ -52,6 +52,7 @@ #include "hcd.h" /* for usbcore internals */ #include "usb.h" +#include "hub.h" #define USB_MAXBUS 64 #define USB_DEVICE_MAX USB_MAXBUS * 128 @@ -73,6 +74,7 @@ struct dev_state { void __user *disccontext; unsigned long ifclaimed; u32 secid; + u32 disabled_bulk_eps; }; struct async { @@ -87,6 +89,8 @@ struct async { struct urb *urb; int status; u32 secid; + u8 bulk_addr; + u8 bulk_status; }; static int usbfs_snoop; @@ -99,11 +103,15 @@ MODULE_PARM_DESC(usbfs_snoop, "true to log all usbfs traffic"); dev_info(dev , format , ## arg); \ } while (0) -#define USB_DEVICE_DEV MKDEV(USB_DEVICE_MAJOR, 0) +enum snoop_when { + SUBMIT, COMPLETE +}; +#define USB_DEVICE_DEV MKDEV(USB_DEVICE_MAJOR, 0) #define MAX_USBFS_BUFFER_SIZE 16384 + static int connected(struct dev_state *ps) { return (!list_empty(&ps->list) && @@ -300,24 +308,79 @@ static struct async *async_getpending(struct dev_state *ps, return NULL; } -static void snoop_urb(struct urb *urb, void __user *userurb) +static void snoop_urb(struct usb_device *udev, + void __user *userurb, int pipe, unsigned length, + int timeout_or_status, enum snoop_when when) { - unsigned j; - unsigned char *data = urb->transfer_buffer; + static const char *types[] = {"isoc", "int", "ctrl", "bulk"}; + static const char *dirs[] = {"out", "in"}; + int ep; + const char *t, *d; if (!usbfs_snoop) return; - dev_info(&urb->dev->dev, "direction=%s\n", - usb_urb_dir_in(urb) ? "IN" : "OUT"); - dev_info(&urb->dev->dev, "userurb=%p\n", userurb); - dev_info(&urb->dev->dev, "transfer_buffer_length=%u\n", - urb->transfer_buffer_length); - dev_info(&urb->dev->dev, "actual_length=%u\n", urb->actual_length); - dev_info(&urb->dev->dev, "data: "); - for (j = 0; j < urb->transfer_buffer_length; ++j) - printk("%02x ", data[j]); - printk("\n"); + ep = usb_pipeendpoint(pipe); + t = types[usb_pipetype(pipe)]; + d = dirs[!!usb_pipein(pipe)]; + + if (userurb) { /* Async */ + if (when == SUBMIT) + dev_info(&udev->dev, "userurb %p, ep%d %s-%s, " + "length %u\n", + userurb, ep, t, d, length); + else + dev_info(&udev->dev, "userurb %p, ep%d %s-%s, " + "actual_length %u status %d\n", + userurb, ep, t, d, length, + timeout_or_status); + } else { + if (when == SUBMIT) + dev_info(&udev->dev, "ep%d %s-%s, length %u, " + "timeout %d\n", + ep, t, d, length, timeout_or_status); + else + dev_info(&udev->dev, "ep%d %s-%s, actual_length %u, " + "status %d\n", + ep, t, d, length, timeout_or_status); + } +} + +#define AS_CONTINUATION 1 +#define AS_UNLINK 2 + +static void cancel_bulk_urbs(struct dev_state *ps, unsigned bulk_addr) +__releases(ps->lock) +__acquires(ps->lock) +{ + struct async *as; + + /* Mark all the pending URBs that match bulk_addr, up to but not + * including the first one without AS_CONTINUATION. If such an + * URB is encountered then a new transfer has already started so + * the endpoint doesn't need to be disabled; otherwise it does. + */ + list_for_each_entry(as, &ps->async_pending, asynclist) { + if (as->bulk_addr == bulk_addr) { + if (as->bulk_status != AS_CONTINUATION) + goto rescan; + as->bulk_status = AS_UNLINK; + as->bulk_addr = 0; + } + } + ps->disabled_bulk_eps |= (1 << bulk_addr); + + /* Now carefully unlink all the marked pending URBs */ + rescan: + list_for_each_entry(as, &ps->async_pending, asynclist) { + if (as->bulk_status == AS_UNLINK) { + as->bulk_status = 0; /* Only once */ + spin_unlock(&ps->lock); /* Allow completions */ + usb_unlink_urb(as->urb); + spin_lock(&ps->lock); + goto rescan; + } + } } static void async_completed(struct urb *urb) @@ -346,7 +409,11 @@ static void async_completed(struct urb *urb) secid = as->secid; } snoop(&urb->dev->dev, "urb complete\n"); - snoop_urb(urb, as->userurb); + snoop_urb(urb->dev, as->userurb, urb->pipe, urb->actual_length, + as->status, COMPLETE); + if (as->status < 0 && as->bulk_addr && as->status != -ECONNRESET && + as->status != -ENOENT) + cancel_bulk_urbs(ps, as->bulk_addr); spin_unlock(&ps->lock); if (signr) @@ -655,6 +722,7 @@ static int usbdev_release(struct inode *inode, struct file *file) struct async *as; usb_lock_device(dev); + usb_hub_release_all_ports(dev, ps); /* Protect against simultaneous open */ mutex_lock(&usbfs_mutex); @@ -688,7 +756,7 @@ static int proc_control(struct dev_state *ps, void __user *arg) unsigned int tmo; unsigned char *tbuf; unsigned wLength; - int i, j, ret; + int i, pipe, ret; if (copy_from_user(&ctrl, arg, sizeof(ctrl))) return -EFAULT; @@ -708,24 +776,17 @@ static int proc_control(struct dev_state *ps, void __user *arg) free_page((unsigned long)tbuf); return -EINVAL; } - snoop(&dev->dev, "control read: bRequest=%02x " - "bRrequestType=%02x wValue=%04x " - "wIndex=%04x wLength=%04x\n", - ctrl.bRequest, ctrl.bRequestType, ctrl.wValue, - ctrl.wIndex, ctrl.wLength); + pipe = usb_rcvctrlpipe(dev, 0); + snoop_urb(dev, NULL, pipe, ctrl.wLength, tmo, SUBMIT); usb_unlock_device(dev); - i = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), ctrl.bRequest, + i = usb_control_msg(dev, pipe, ctrl.bRequest, ctrl.bRequestType, ctrl.wValue, ctrl.wIndex, tbuf, ctrl.wLength, tmo); usb_lock_device(dev); + snoop_urb(dev, NULL, pipe, max(i, 0), min(i, 0), COMPLETE); + if ((i > 0) && ctrl.wLength) { - if (usbfs_snoop) { - dev_info(&dev->dev, "control read: data "); - for (j = 0; j < i; ++j) - printk("%02x ", (u8)(tbuf)[j]); - printk("\n"); - } if (copy_to_user(ctrl.data, tbuf, i)) { free_page((unsigned long)tbuf); return -EFAULT; @@ -738,22 +799,15 @@ static int proc_control(struct dev_state *ps, void __user *arg) return -EFAULT; } } - snoop(&dev->dev, "control write: bRequest=%02x " - "bRrequestType=%02x wValue=%04x " - "wIndex=%04x wLength=%04x\n", - ctrl.bRequest, ctrl.bRequestType, ctrl.wValue, - ctrl.wIndex, ctrl.wLength); - if (usbfs_snoop) { - dev_info(&dev->dev, "control write: data: "); - for (j = 0; j < ctrl.wLength; ++j) - printk("%02x ", (unsigned char)(tbuf)[j]); - printk("\n"); - } + pipe = usb_sndctrlpipe(dev, 0); + snoop_urb(dev, NULL, pipe, ctrl.wLength, tmo, SUBMIT); + usb_unlock_device(dev); i = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), ctrl.bRequest, ctrl.bRequestType, ctrl.wValue, ctrl.wIndex, tbuf, ctrl.wLength, tmo); usb_lock_device(dev); + snoop_urb(dev, NULL, pipe, max(i, 0), min(i, 0), COMPLETE); } free_page((unsigned long)tbuf); if (i < 0 && i != -EPIPE) { @@ -772,7 +826,7 @@ static int proc_bulk(struct dev_state *ps, void __user *arg) unsigned int tmo, len1, pipe; int len2; unsigned char *tbuf; - int i, j, ret; + int i, ret; if (copy_from_user(&bulk, arg, sizeof(bulk))) return -EFAULT; @@ -799,18 +853,14 @@ static int proc_bulk(struct dev_state *ps, void __user *arg) kfree(tbuf); return -EINVAL; } - snoop(&dev->dev, "bulk read: len=0x%02x timeout=%04d\n", - bulk.len, bulk.timeout); + snoop_urb(dev, NULL, pipe, len1, tmo, SUBMIT); + usb_unlock_device(dev); i = usb_bulk_msg(dev, pipe, tbuf, len1, &len2, tmo); usb_lock_device(dev); + snoop_urb(dev, NULL, pipe, len2, i, COMPLETE); + if (!i && len2) { - if (usbfs_snoop) { - dev_info(&dev->dev, "bulk read: data "); - for (j = 0; j < len2; ++j) - printk("%02x ", (u8)(tbuf)[j]); - printk("\n"); - } if (copy_to_user(bulk.data, tbuf, len2)) { kfree(tbuf); return -EFAULT; @@ -823,17 +873,12 @@ static int proc_bulk(struct dev_state *ps, void __user *arg) return -EFAULT; } } - snoop(&dev->dev, "bulk write: len=0x%02x timeout=%04d\n", - bulk.len, bulk.timeout); - if (usbfs_snoop) { - dev_info(&dev->dev, "bulk write: data: "); - for (j = 0; j < len1; ++j) - printk("%02x ", (unsigned char)(tbuf)[j]); - printk("\n"); - } + snoop_urb(dev, NULL, pipe, len1, tmo, SUBMIT); + usb_unlock_device(dev); i = usb_bulk_msg(dev, pipe, tbuf, len1, &len2, tmo); usb_lock_device(dev); + snoop_urb(dev, NULL, pipe, len2, i, COMPLETE); } kfree(tbuf); if (i < 0) @@ -991,6 +1036,7 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb, if (uurb->flags & ~(USBDEVFS_URB_ISO_ASAP | USBDEVFS_URB_SHORT_NOT_OK | + USBDEVFS_URB_BULK_CONTINUATION | USBDEVFS_URB_NO_FSBR | USBDEVFS_URB_ZERO_PACKET | USBDEVFS_URB_NO_INTERRUPT)) @@ -1051,13 +1097,6 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb, is_in = 0; uurb->endpoint &= ~USB_DIR_IN; } - snoop(&ps->dev->dev, "control urb: bRequest=%02x " - "bRrequestType=%02x wValue=%04x " - "wIndex=%04x wLength=%04x\n", - dr->bRequest, dr->bRequestType, - __le16_to_cpup(&dr->wValue), - __le16_to_cpup(&dr->wIndex), - __le16_to_cpup(&dr->wLength)); break; case USBDEVFS_URB_TYPE_BULK: @@ -1070,7 +1109,6 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb, uurb->number_of_packets = 0; if (uurb->buffer_length > MAX_USBFS_BUFFER_SIZE) return -EINVAL; - snoop(&ps->dev->dev, "bulk urb\n"); break; case USBDEVFS_URB_TYPE_ISO: @@ -1097,12 +1135,12 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb, } totlen += isopkt[u].length; } - if (totlen > 32768) { + /* 3072 * 64 microframes */ + if (totlen > 196608) { kfree(isopkt); return -EINVAL; } uurb->buffer_length = totlen; - snoop(&ps->dev->dev, "iso urb\n"); break; case USBDEVFS_URB_TYPE_INTERRUPT: @@ -1111,7 +1149,6 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb, return -EINVAL; if (uurb->buffer_length > MAX_USBFS_BUFFER_SIZE) return -EINVAL; - snoop(&ps->dev->dev, "interrupt urb\n"); break; default: @@ -1198,11 +1235,46 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb, return -EFAULT; } } - snoop_urb(as->urb, as->userurb); + snoop_urb(ps->dev, as->userurb, as->urb->pipe, + as->urb->transfer_buffer_length, 0, SUBMIT); async_newpending(as); - if ((ret = usb_submit_urb(as->urb, GFP_KERNEL))) { + + if (usb_endpoint_xfer_bulk(&ep->desc)) { + spin_lock_irq(&ps->lock); + + /* Not exactly the endpoint address; the direction bit is + * shifted to the 0x10 position so that the value will be + * between 0 and 31. + */ + as->bulk_addr = usb_endpoint_num(&ep->desc) | + ((ep->desc.bEndpointAddress & USB_ENDPOINT_DIR_MASK) + >> 3); + + /* If this bulk URB is the start of a new transfer, re-enable + * the endpoint. Otherwise mark it as a continuation URB. + */ + if (uurb->flags & USBDEVFS_URB_BULK_CONTINUATION) + as->bulk_status = AS_CONTINUATION; + else + ps->disabled_bulk_eps &= ~(1 << as->bulk_addr); + + /* Don't accept continuation URBs if the endpoint is + * disabled because of an earlier error. + */ + if (ps->disabled_bulk_eps & (1 << as->bulk_addr)) + ret = -EREMOTEIO; + else + ret = usb_submit_urb(as->urb, GFP_ATOMIC); + spin_unlock_irq(&ps->lock); + } else { + ret = usb_submit_urb(as->urb, GFP_KERNEL); + } + + if (ret) { dev_printk(KERN_DEBUG, &ps->dev->dev, "usbfs: usb_submit_urb returned %d\n", ret); + snoop_urb(ps->dev, as->userurb, as->urb->pipe, + 0, ret, COMPLETE); async_removepending(as); free_async(as); return ret; @@ -1548,6 +1620,29 @@ static int proc_ioctl_compat(struct dev_state *ps, compat_uptr_t arg) } #endif +static int proc_claim_port(struct dev_state *ps, void __user *arg) +{ + unsigned portnum; + int rc; + + if (get_user(portnum, (unsigned __user *) arg)) + return -EFAULT; + rc = usb_hub_claim_port(ps->dev, portnum, ps); + if (rc == 0) + snoop(&ps->dev->dev, "port %d claimed by process %d: %s\n", + portnum, task_pid_nr(current), current->comm); + return rc; +} + +static int proc_release_port(struct dev_state *ps, void __user *arg) +{ + unsigned portnum; + + if (get_user(portnum, (unsigned __user *) arg)) + return -EFAULT; + return usb_hub_release_port(ps->dev, portnum, ps); +} + /* * NOTE: All requests here that have interface numbers as parameters * are assuming that somehow the configuration has been prevented from @@ -1645,7 +1740,7 @@ static int usbdev_ioctl(struct inode *inode, struct file *file, break; case USBDEVFS_REAPURBNDELAY32: - snoop(&dev->dev, "%s: REAPURBDELAY32\n", __func__); + snoop(&dev->dev, "%s: REAPURBNDELAY32\n", __func__); ret = proc_reapurbnonblock_compat(ps, p); break; @@ -1666,7 +1761,7 @@ static int usbdev_ioctl(struct inode *inode, struct file *file, break; case USBDEVFS_REAPURBNDELAY: - snoop(&dev->dev, "%s: REAPURBDELAY\n", __func__); + snoop(&dev->dev, "%s: REAPURBNDELAY\n", __func__); ret = proc_reapurbnonblock(ps, p); break; @@ -1689,6 +1784,16 @@ static int usbdev_ioctl(struct inode *inode, struct file *file, snoop(&dev->dev, "%s: IOCTL\n", __func__); ret = proc_ioctl_default(ps, p); break; + + case USBDEVFS_CLAIM_PORT: + snoop(&dev->dev, "%s: CLAIM_PORT\n", __func__); + ret = proc_claim_port(ps, p); + break; + + case USBDEVFS_RELEASE_PORT: + snoop(&dev->dev, "%s: RELEASE_PORT\n", __func__); + ret = proc_release_port(ps, p); + break; } usb_unlock_device(dev); if (ret >= 0) diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c index 69e5773abfce..4f864472c5c4 100644 --- a/drivers/usb/core/driver.c +++ b/drivers/usb/core/driver.c @@ -207,6 +207,9 @@ static int usb_probe_interface(struct device *dev) intf->needs_binding = 0; + if (usb_device_is_owned(udev)) + return -ENODEV; + if (udev->authorized == 0) { dev_err(&intf->dev, "Device is not authorized for usage\n"); return -ENODEV; @@ -232,28 +235,35 @@ static int usb_probe_interface(struct device *dev) /* The interface should always appear to be in use * unless the driver suports autosuspend. */ - intf->pm_usage_cnt = !(driver->supports_autosuspend); + atomic_set(&intf->pm_usage_cnt, !driver->supports_autosuspend); /* Carry out a deferred switch to altsetting 0 */ if (intf->needs_altsetting0) { - usb_set_interface(udev, intf->altsetting[0]. + error = usb_set_interface(udev, intf->altsetting[0]. desc.bInterfaceNumber, 0); + if (error < 0) + goto err; + intf->needs_altsetting0 = 0; } error = driver->probe(intf, id); - if (error) { - mark_quiesced(intf); - intf->needs_remote_wakeup = 0; - intf->condition = USB_INTERFACE_UNBOUND; - usb_cancel_queued_reset(intf); - } else - intf->condition = USB_INTERFACE_BOUND; + if (error) + goto err; + intf->condition = USB_INTERFACE_BOUND; usb_autosuspend_device(udev); } return error; + +err: + mark_quiesced(intf); + intf->needs_remote_wakeup = 0; + intf->condition = USB_INTERFACE_UNBOUND; + usb_cancel_queued_reset(intf); + usb_autosuspend_device(udev); + return error; } /* called from driver core with dev locked */ @@ -262,7 +272,7 @@ static int usb_unbind_interface(struct device *dev) struct usb_driver *driver = to_usb_driver(dev->driver); struct usb_interface *intf = to_usb_interface(dev); struct usb_device *udev; - int error; + int error, r; intf->condition = USB_INTERFACE_UNBINDING; @@ -290,11 +300,14 @@ static int usb_unbind_interface(struct device *dev) * Just re-enable it without affecting the endpoint toggles. */ usb_enable_interface(udev, intf, false); - } else if (!error && intf->dev.power.status == DPM_ON) - usb_set_interface(udev, intf->altsetting[0]. + } else if (!error && intf->dev.power.status == DPM_ON) { + r = usb_set_interface(udev, intf->altsetting[0]. desc.bInterfaceNumber, 0); - else + if (r < 0) + intf->needs_altsetting0 = 1; + } else { intf->needs_altsetting0 = 1; + } usb_set_intfdata(intf, NULL); intf->condition = USB_INTERFACE_UNBOUND; @@ -344,7 +357,7 @@ int usb_driver_claim_interface(struct usb_driver *driver, usb_pm_lock(udev); iface->condition = USB_INTERFACE_BOUND; mark_active(iface); - iface->pm_usage_cnt = !(driver->supports_autosuspend); + atomic_set(&iface->pm_usage_cnt, !driver->supports_autosuspend); usb_pm_unlock(udev); /* if interface was already added, bind now; else let @@ -1065,7 +1078,7 @@ static int autosuspend_check(struct usb_device *udev, int reschedule) intf = udev->actconfig->interface[i]; if (!is_active(intf)) continue; - if (intf->pm_usage_cnt > 0) + if (atomic_read(&intf->pm_usage_cnt) > 0) return -EBUSY; if (intf->needs_remote_wakeup && !udev->do_remote_wakeup) { @@ -1461,17 +1474,19 @@ static int usb_autopm_do_interface(struct usb_interface *intf, status = -ENODEV; else { udev->auto_pm = 1; - intf->pm_usage_cnt += inc_usage_cnt; + atomic_add(inc_usage_cnt, &intf->pm_usage_cnt); udev->last_busy = jiffies; - if (inc_usage_cnt >= 0 && intf->pm_usage_cnt > 0) { + if (inc_usage_cnt >= 0 && + atomic_read(&intf->pm_usage_cnt) > 0) { if (udev->state == USB_STATE_SUSPENDED) status = usb_resume_both(udev, PMSG_AUTO_RESUME); if (status != 0) - intf->pm_usage_cnt -= inc_usage_cnt; + atomic_sub(inc_usage_cnt, &intf->pm_usage_cnt); else udev->last_busy = jiffies; - } else if (inc_usage_cnt <= 0 && intf->pm_usage_cnt <= 0) { + } else if (inc_usage_cnt <= 0 && + atomic_read(&intf->pm_usage_cnt) <= 0) { status = usb_suspend_both(udev, PMSG_AUTO_SUSPEND); } } @@ -1516,7 +1531,7 @@ void usb_autopm_put_interface(struct usb_interface *intf) status = usb_autopm_do_interface(intf, -1); dev_vdbg(&intf->dev, "%s: status %d cnt %d\n", - __func__, status, intf->pm_usage_cnt); + __func__, status, atomic_read(&intf->pm_usage_cnt)); } EXPORT_SYMBOL_GPL(usb_autopm_put_interface); @@ -1544,10 +1559,10 @@ void usb_autopm_put_interface_async(struct usb_interface *intf) status = -ENODEV; } else { udev->last_busy = jiffies; - --intf->pm_usage_cnt; + atomic_dec(&intf->pm_usage_cnt); if (udev->autosuspend_disabled || udev->autosuspend_delay < 0) status = -EPERM; - else if (intf->pm_usage_cnt <= 0 && + else if (atomic_read(&intf->pm_usage_cnt) <= 0 && !timer_pending(&udev->autosuspend.timer)) { queue_delayed_work(ksuspend_usb_wq, &udev->autosuspend, round_jiffies_up_relative( @@ -1555,7 +1570,7 @@ void usb_autopm_put_interface_async(struct usb_interface *intf) } } dev_vdbg(&intf->dev, "%s: status %d cnt %d\n", - __func__, status, intf->pm_usage_cnt); + __func__, status, atomic_read(&intf->pm_usage_cnt)); } EXPORT_SYMBOL_GPL(usb_autopm_put_interface_async); @@ -1599,7 +1614,7 @@ int usb_autopm_get_interface(struct usb_interface *intf) status = usb_autopm_do_interface(intf, 1); dev_vdbg(&intf->dev, "%s: status %d cnt %d\n", - __func__, status, intf->pm_usage_cnt); + __func__, status, atomic_read(&intf->pm_usage_cnt)); return status; } EXPORT_SYMBOL_GPL(usb_autopm_get_interface); @@ -1627,10 +1642,14 @@ int usb_autopm_get_interface_async(struct usb_interface *intf) status = -ENODEV; else if (udev->autoresume_disabled) status = -EPERM; - else if (++intf->pm_usage_cnt > 0 && udev->state == USB_STATE_SUSPENDED) - queue_work(ksuspend_usb_wq, &udev->autoresume); + else { + atomic_inc(&intf->pm_usage_cnt); + if (atomic_read(&intf->pm_usage_cnt) > 0 && + udev->state == USB_STATE_SUSPENDED) + queue_work(ksuspend_usb_wq, &udev->autoresume); + } dev_vdbg(&intf->dev, "%s: status %d cnt %d\n", - __func__, status, intf->pm_usage_cnt); + __func__, status, atomic_read(&intf->pm_usage_cnt)); return status; } EXPORT_SYMBOL_GPL(usb_autopm_get_interface_async); @@ -1652,7 +1671,7 @@ int usb_autopm_set_interface(struct usb_interface *intf) status = usb_autopm_do_interface(intf, 0); dev_vdbg(&intf->dev, "%s: status %d cnt %d\n", - __func__, status, intf->pm_usage_cnt); + __func__, status, atomic_read(&intf->pm_usage_cnt)); return status; } EXPORT_SYMBOL_GPL(usb_autopm_set_interface); diff --git a/drivers/usb/core/generic.c b/drivers/usb/core/generic.c index 30ecac3af15a..05e6d313961e 100644 --- a/drivers/usb/core/generic.c +++ b/drivers/usb/core/generic.c @@ -158,7 +158,9 @@ static int generic_probe(struct usb_device *udev) /* Choose and set the configuration. This registers the interfaces * with the driver core and lets interface drivers bind to them. */ - if (udev->authorized == 0) + if (usb_device_is_owned(udev)) + ; /* Don't configure if the device is owned */ + else if (udev->authorized == 0) dev_err(&udev->dev, "Device is not authorized for usage\n"); else { c = usb_choose_configuration(udev); diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index 95ccfa0b9fc5..34de475f016e 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c @@ -337,72 +337,89 @@ static const u8 ss_rh_config_descriptor[] = { /*-------------------------------------------------------------------------*/ -/* - * helper routine for returning string descriptors in UTF-16LE - * input can actually be ISO-8859-1; ASCII is its 7-bit subset +/** + * ascii2desc() - Helper routine for producing UTF-16LE string descriptors + * @s: Null-terminated ASCII (actually ISO-8859-1) string + * @buf: Buffer for USB string descriptor (header + UTF-16LE) + * @len: Length (in bytes; may be odd) of descriptor buffer. + * + * The return value is the number of bytes filled in: 2 + 2*strlen(s) or + * buflen, whichever is less. + * + * USB String descriptors can contain at most 126 characters; input + * strings longer than that are truncated. */ -static unsigned ascii2utf(char *s, u8 *utf, int utfmax) +static unsigned +ascii2desc(char const *s, u8 *buf, unsigned len) { - unsigned retval; + unsigned n, t = 2 + 2*strlen(s); - for (retval = 0; *s && utfmax > 1; utfmax -= 2, retval += 2) { - *utf++ = *s++; - *utf++ = 0; - } - if (utfmax > 0) { - *utf = *s; - ++retval; + if (t > 254) + t = 254; /* Longest possible UTF string descriptor */ + if (len > t) + len = t; + + t += USB_DT_STRING << 8; /* Now t is first 16 bits to store */ + + n = len; + while (n--) { + *buf++ = t; + if (!n--) + break; + *buf++ = t >> 8; + t = (unsigned char)*s++; } - return retval; + return len; } -/* - * rh_string - provides manufacturer, product and serial strings for root hub - * @id: the string ID number (1: serial number, 2: product, 3: vendor) +/** + * rh_string() - provides string descriptors for root hub + * @id: the string ID number (0: langids, 1: serial #, 2: product, 3: vendor) * @hcd: the host controller for this root hub - * @data: return packet in UTF-16 LE - * @len: length of the return packet + * @data: buffer for output packet + * @len: length of the provided buffer * * Produces either a manufacturer, product or serial number string for the * virtual root hub device. + * Returns the number of bytes filled in: the length of the descriptor or + * of the provided buffer, whichever is less. */ -static unsigned rh_string(int id, struct usb_hcd *hcd, u8 *data, unsigned len) +static unsigned +rh_string(int id, struct usb_hcd const *hcd, u8 *data, unsigned len) { - char buf [100]; + char buf[100]; + char const *s; + static char const langids[4] = {4, USB_DT_STRING, 0x09, 0x04}; // language ids - if (id == 0) { - buf[0] = 4; buf[1] = 3; /* 4 bytes string data */ - buf[2] = 0x09; buf[3] = 0x04; /* MSFT-speak for "en-us" */ - len = min_t(unsigned, len, 4); - memcpy (data, buf, len); + switch (id) { + case 0: + /* Array of LANGID codes (0x0409 is MSFT-speak for "en-us") */ + /* See http://www.usb.org/developers/docs/USB_LANGIDs.pdf */ + if (len > 4) + len = 4; + memcpy(data, langids, len); return len; - - // serial number - } else if (id == 1) { - strlcpy (buf, hcd->self.bus_name, sizeof buf); - - // product description - } else if (id == 2) { - strlcpy (buf, hcd->product_desc, sizeof buf); - - // id 3 == vendor description - } else if (id == 3) { + case 1: + /* Serial number */ + s = hcd->self.bus_name; + break; + case 2: + /* Product name */ + s = hcd->product_desc; + break; + case 3: + /* Manufacturer */ snprintf (buf, sizeof buf, "%s %s %s", init_utsname()->sysname, init_utsname()->release, hcd->driver->description); - } - - switch (len) { /* All cases fall through */ + s = buf; + break; default: - len = 2 + ascii2utf (buf, data + 2, len - 2); - case 2: - data [1] = 3; /* type == string */ - case 1: - data [0] = 2 * (strlen (buf) + 1); - case 0: - ; /* Compiler wants a statement here */ + /* Can't happen; caller guarantees it */ + return 0; } - return len; + + return ascii2desc(s, data, len); } diff --git a/drivers/usb/core/hcd.h b/drivers/usb/core/hcd.h index ec5c67ea07b7..79782a1c43f6 100644 --- a/drivers/usb/core/hcd.h +++ b/drivers/usb/core/hcd.h @@ -267,6 +267,11 @@ struct hc_driver { void (*reset_bandwidth)(struct usb_hcd *, struct usb_device *); /* Returns the hardware-chosen device address */ int (*address_device)(struct usb_hcd *, struct usb_device *udev); + /* Notifies the HCD after a hub descriptor is fetched. + * Will block. + */ + int (*update_hub_device)(struct usb_hcd *, struct usb_device *hdev, + struct usb_tt *tt, gfp_t mem_flags); }; extern int usb_hcd_link_urb_to_ep(struct usb_hcd *hcd, struct urb *urb); diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 71f86c60d83c..5ce839137ad6 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -78,6 +78,7 @@ struct usb_hub { u8 indicator[USB_MAXCHILDREN]; struct delayed_work leds; struct delayed_work init_work; + void **port_owners; }; @@ -162,8 +163,10 @@ static inline char *portspeed(int portstatus) } /* Note that hdev or one of its children must be locked! */ -static inline struct usb_hub *hdev_to_hub(struct usb_device *hdev) +static struct usb_hub *hdev_to_hub(struct usb_device *hdev) { + if (!hdev || !hdev->actconfig) + return NULL; return usb_get_intfdata(hdev->actconfig->interface[0]); } @@ -372,7 +375,7 @@ static void kick_khubd(struct usb_hub *hub) unsigned long flags; /* Suppress autosuspend until khubd runs */ - to_usb_interface(hub->intfdev)->pm_usage_cnt = 1; + atomic_set(&to_usb_interface(hub->intfdev)->pm_usage_cnt, 1); spin_lock_irqsave(&hub_event_lock, flags); if (!hub->disconnected && list_empty(&hub->event_list)) { @@ -384,8 +387,10 @@ static void kick_khubd(struct usb_hub *hub) void usb_kick_khubd(struct usb_device *hdev) { - /* FIXME: What if hdev isn't bound to the hub driver? */ - kick_khubd(hdev_to_hub(hdev)); + struct usb_hub *hub = hdev_to_hub(hdev); + + if (hub) + kick_khubd(hub); } @@ -677,7 +682,8 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type) msecs_to_jiffies(delay)); /* Suppress autosuspend until init is done */ - to_usb_interface(hub->intfdev)->pm_usage_cnt = 1; + atomic_set(&to_usb_interface(hub->intfdev)-> + pm_usage_cnt, 1); return; /* Continues at init2: below */ } else { hub_power_on(hub, true); @@ -854,25 +860,24 @@ static int hub_post_reset(struct usb_interface *intf) static int hub_configure(struct usb_hub *hub, struct usb_endpoint_descriptor *endpoint) { + struct usb_hcd *hcd; struct usb_device *hdev = hub->hdev; struct device *hub_dev = hub->intfdev; u16 hubstatus, hubchange; u16 wHubCharacteristics; unsigned int pipe; int maxp, ret; - char *message; + char *message = "out of memory"; hub->buffer = usb_buffer_alloc(hdev, sizeof(*hub->buffer), GFP_KERNEL, &hub->buffer_dma); if (!hub->buffer) { - message = "can't allocate hub irq buffer"; ret = -ENOMEM; goto fail; } hub->status = kmalloc(sizeof(*hub->status), GFP_KERNEL); if (!hub->status) { - message = "can't kmalloc hub status buffer"; ret = -ENOMEM; goto fail; } @@ -880,7 +885,6 @@ static int hub_configure(struct usb_hub *hub, hub->descriptor = kmalloc(sizeof(*hub->descriptor), GFP_KERNEL); if (!hub->descriptor) { - message = "can't kmalloc hub descriptor"; ret = -ENOMEM; goto fail; } @@ -904,6 +908,12 @@ static int hub_configure(struct usb_hub *hub, dev_info (hub_dev, "%d port%s detected\n", hdev->maxchild, (hdev->maxchild == 1) ? "" : "s"); + hub->port_owners = kzalloc(hdev->maxchild * sizeof(void *), GFP_KERNEL); + if (!hub->port_owners) { + ret = -ENOMEM; + goto fail; + } + wHubCharacteristics = le16_to_cpu(hub->descriptor->wHubCharacteristics); if (wHubCharacteristics & HUB_CHAR_COMPOUND) { @@ -1052,6 +1062,19 @@ static int hub_configure(struct usb_hub *hub, dev_dbg(hub_dev, "%umA bus power budget for each child\n", hub->mA_per_port); + /* Update the HCD's internal representation of this hub before khubd + * starts getting port status changes for devices under the hub. + */ + hcd = bus_to_hcd(hdev->bus); + if (hcd->driver->update_hub_device) { + ret = hcd->driver->update_hub_device(hcd, hdev, + &hub->tt, GFP_KERNEL); + if (ret < 0) { + message = "can't update HCD hub info"; + goto fail; + } + } + ret = hub_hub_status(hub, &hubstatus, &hubchange); if (ret < 0) { message = "can't get hub status"; @@ -1082,7 +1105,6 @@ static int hub_configure(struct usb_hub *hub, hub->urb = usb_alloc_urb(0, GFP_KERNEL); if (!hub->urb) { - message = "couldn't allocate interrupt urb"; ret = -ENOMEM; goto fail; } @@ -1131,11 +1153,13 @@ static void hub_disconnect(struct usb_interface *intf) hub_quiesce(hub, HUB_DISCONNECT); usb_set_intfdata (intf, NULL); + hub->hdev->maxchild = 0; if (hub->hdev->speed == USB_SPEED_HIGH) highspeed_hubs--; usb_free_urb(hub->urb); + kfree(hub->port_owners); kfree(hub->descriptor); kfree(hub->status); usb_buffer_free(hub->hdev, sizeof(*hub->buffer), hub->buffer, @@ -1250,6 +1274,79 @@ hub_ioctl(struct usb_interface *intf, unsigned int code, void *user_data) } } +/* + * Allow user programs to claim ports on a hub. When a device is attached + * to one of these "claimed" ports, the program will "own" the device. + */ +static int find_port_owner(struct usb_device *hdev, unsigned port1, + void ***ppowner) +{ + if (hdev->state == USB_STATE_NOTATTACHED) + return -ENODEV; + if (port1 == 0 || port1 > hdev->maxchild) + return -EINVAL; + + /* This assumes that devices not managed by the hub driver + * will always have maxchild equal to 0. + */ + *ppowner = &(hdev_to_hub(hdev)->port_owners[port1 - 1]); + return 0; +} + +/* In the following three functions, the caller must hold hdev's lock */ +int usb_hub_claim_port(struct usb_device *hdev, unsigned port1, void *owner) +{ + int rc; + void **powner; + + rc = find_port_owner(hdev, port1, &powner); + if (rc) + return rc; + if (*powner) + return -EBUSY; + *powner = owner; + return rc; +} + +int usb_hub_release_port(struct usb_device *hdev, unsigned port1, void *owner) +{ + int rc; + void **powner; + + rc = find_port_owner(hdev, port1, &powner); + if (rc) + return rc; + if (*powner != owner) + return -ENOENT; + *powner = NULL; + return rc; +} + +void usb_hub_release_all_ports(struct usb_device *hdev, void *owner) +{ + int n; + void **powner; + + n = find_port_owner(hdev, 1, &powner); + if (n == 0) { + for (; n < hdev->maxchild; (++n, ++powner)) { + if (*powner == owner) + *powner = NULL; + } + } +} + +/* The caller must hold udev's lock */ +bool usb_device_is_owned(struct usb_device *udev) +{ + struct usb_hub *hub; + + if (udev->state == USB_STATE_NOTATTACHED || !udev->parent) + return false; + hub = hdev_to_hub(udev->parent); + return !!hub->port_owners[udev->portnum - 1]; +} + static void recursively_mark_NOTATTACHED(struct usb_device *udev) { @@ -2849,14 +2946,7 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1, /* For a suspended device, treat this as a * remote wakeup event. */ - if (udev->do_remote_wakeup) - status = remote_wakeup(udev); - - /* Otherwise leave it be; devices can't tell the - * difference between suspended and disabled. - */ - else - status = 0; + status = remote_wakeup(udev); #endif } else { diff --git a/drivers/usb/core/message.c b/drivers/usb/core/message.c index 9720e699f472..da718e84d58d 100644 --- a/drivers/usb/core/message.c +++ b/drivers/usb/core/message.c @@ -459,35 +459,23 @@ int usb_sg_init(struct usb_sg_request *io, struct usb_device *dev, io->urbs[i]->context = io; /* - * Some systems need to revert to PIO when DMA is - * temporarily unavailable. For their sakes, both - * transfer_buffer and transfer_dma are set when - * possible. However this can only work on systems - * without: + * Some systems need to revert to PIO when DMA is temporarily + * unavailable. For their sakes, both transfer_buffer and + * transfer_dma are set when possible. * - * - HIGHMEM, since DMA buffers located in high memory - * are not directly addressable by the CPU for PIO; - * - * - IOMMU, since dma_map_sg() is allowed to use an - * IOMMU to make virtually discontiguous buffers be - * "dma-contiguous" so that PIO and DMA need diferent - * numbers of URBs. - * - * So when HIGHMEM or IOMMU are in use, transfer_buffer - * is NULL to prevent stale pointers and to help spot - * bugs. + * Note that if IOMMU coalescing occurred, we cannot + * trust sg_page anymore, so check if S/G list shrunk. */ + if (io->nents == io->entries && !PageHighMem(sg_page(sg))) + io->urbs[i]->transfer_buffer = sg_virt(sg); + else + io->urbs[i]->transfer_buffer = NULL; + if (dma) { io->urbs[i]->transfer_dma = sg_dma_address(sg); len = sg_dma_len(sg); -#if defined(CONFIG_HIGHMEM) || defined(CONFIG_GART_IOMMU) - io->urbs[i]->transfer_buffer = NULL; -#else - io->urbs[i]->transfer_buffer = sg_virt(sg); -#endif } else { /* hc may use _only_ transfer_buffer */ - io->urbs[i]->transfer_buffer = sg_virt(sg); len = sg->length; } diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c index 43ee943d757a..b1b85abb9a2d 100644 --- a/drivers/usb/core/usb.c +++ b/drivers/usb/core/usb.c @@ -413,8 +413,13 @@ struct usb_device *usb_alloc_dev(struct usb_device *parent, } else { snprintf(dev->devpath, sizeof dev->devpath, "%s.%d", parent->devpath, port1); - dev->route = parent->route + - (port1 << ((parent->level - 1)*4)); + /* Route string assumes hubs have less than 16 ports */ + if (port1 < 15) + dev->route = parent->route + + (port1 << ((parent->level - 1)*4)); + else + dev->route = parent->route + + (15 << ((parent->level - 1)*4)); } dev->dev.parent = &parent->dev; @@ -914,11 +919,11 @@ int usb_buffer_map_sg(const struct usb_device *dev, int is_in, || !(bus = dev->bus) || !(controller = bus->controller) || !controller->dma_mask) - return -1; + return -EINVAL; /* FIXME generic api broken like pci, can't report errors */ return dma_map_sg(controller, sg, nents, - is_in ? DMA_FROM_DEVICE : DMA_TO_DEVICE); + is_in ? DMA_FROM_DEVICE : DMA_TO_DEVICE) ? : -ENOMEM; } EXPORT_SYMBOL_GPL(usb_buffer_map_sg); diff --git a/drivers/usb/core/usb.h b/drivers/usb/core/usb.h index c0e0ae2bb8e7..9a8b15e6377a 100644 --- a/drivers/usb/core/usb.h +++ b/drivers/usb/core/usb.h @@ -37,6 +37,13 @@ extern int usb_match_device(struct usb_device *dev, extern void usb_forced_unbind_intf(struct usb_interface *intf); extern void usb_rebind_intf(struct usb_interface *intf); +extern int usb_hub_claim_port(struct usb_device *hdev, unsigned port, + void *owner); +extern int usb_hub_release_port(struct usb_device *hdev, unsigned port, + void *owner); +extern void usb_hub_release_all_ports(struct usb_device *hdev, void *owner); +extern bool usb_device_is_owned(struct usb_device *udev); + extern int usb_hub_init(void); extern void usb_hub_cleanup(void); extern int usb_major_init(void); diff --git a/drivers/usb/early/Makefile b/drivers/usb/early/Makefile new file mode 100644 index 000000000000..dfedee8c45b6 --- /dev/null +++ b/drivers/usb/early/Makefile @@ -0,0 +1,5 @@ +# +# Makefile for early USB devices +# + +obj-$(CONFIG_EARLY_PRINTK_DBGP) += ehci-dbgp.o diff --git a/drivers/usb/early/ehci-dbgp.c b/drivers/usb/early/ehci-dbgp.c new file mode 100644 index 000000000000..1206a26ef893 --- /dev/null +++ b/drivers/usb/early/ehci-dbgp.c @@ -0,0 +1,996 @@ +/* + * Standalone EHCI usb debug driver + * + * Originally written by: + * Eric W. Biederman" <ebiederm@xmission.com> and + * Yinghai Lu <yhlu.kernel@gmail.com> + * + * Changes for early/late printk and HW errata: + * Jason Wessel <jason.wessel@windriver.com> + * Copyright (C) 2009 Wind River Systems, Inc. + * + */ + +#include <linux/console.h> +#include <linux/errno.h> +#include <linux/module.h> +#include <linux/pci_regs.h> +#include <linux/pci_ids.h> +#include <linux/usb/ch9.h> +#include <linux/usb/ehci_def.h> +#include <linux/delay.h> +#include <asm/io.h> +#include <asm/pci-direct.h> +#include <asm/fixmap.h> + +/* The code here is intended to talk directly to the EHCI debug port + * and does not require that you have any kind of USB host controller + * drivers or USB device drivers compiled into the kernel. + * + * If you make a change to anything in here, the following test cases + * need to pass where a USB debug device works in the following + * configurations. + * + * 1. boot args: earlyprintk=dbgp + * o kernel compiled with # CONFIG_USB_EHCI_HCD is not set + * o kernel compiled with CONFIG_USB_EHCI_HCD=y + * 2. boot args: earlyprintk=dbgp,keep + * o kernel compiled with # CONFIG_USB_EHCI_HCD is not set + * o kernel compiled with CONFIG_USB_EHCI_HCD=y + * 3. boot args: earlyprintk=dbgp console=ttyUSB0 + * o kernel has CONFIG_USB_EHCI_HCD=y and + * CONFIG_USB_SERIAL_DEBUG=y + * 4. boot args: earlyprintk=vga,dbgp + * o kernel compiled with # CONFIG_USB_EHCI_HCD is not set + * o kernel compiled with CONFIG_USB_EHCI_HCD=y + * + * For the 4th configuration you can turn on or off the DBGP_DEBUG + * such that you can debug the dbgp device's driver code. + */ + +static int dbgp_phys_port = 1; + +static struct ehci_caps __iomem *ehci_caps; +static struct ehci_regs __iomem *ehci_regs; +static struct ehci_dbg_port __iomem *ehci_debug; +static int dbgp_not_safe; /* Cannot use debug device during ehci reset */ +static unsigned int dbgp_endpoint_out; + +struct ehci_dev { + u32 bus; + u32 slot; + u32 func; +}; + +static struct ehci_dev ehci_dev; + +#define USB_DEBUG_DEVNUM 127 + +#define DBGP_DATA_TOGGLE 0x8800 + +#ifdef DBGP_DEBUG +#define dbgp_printk printk +static void dbgp_ehci_status(char *str) +{ + if (!ehci_debug) + return; + dbgp_printk("dbgp: %s\n", str); + dbgp_printk(" Debug control: %08x", readl(&ehci_debug->control)); + dbgp_printk(" ehci cmd : %08x", readl(&ehci_regs->command)); + dbgp_printk(" ehci conf flg: %08x\n", + readl(&ehci_regs->configured_flag)); + dbgp_printk(" ehci status : %08x", readl(&ehci_regs->status)); + dbgp_printk(" ehci portsc : %08x\n", + readl(&ehci_regs->port_status[dbgp_phys_port - 1])); +} +#else +static inline void dbgp_ehci_status(char *str) { } +static inline void dbgp_printk(const char *fmt, ...) { } +#endif + +static inline u32 dbgp_pid_update(u32 x, u32 tok) +{ + return ((x ^ DBGP_DATA_TOGGLE) & 0xffff00) | (tok & 0xff); +} + +static inline u32 dbgp_len_update(u32 x, u32 len) +{ + return (x & ~0x0f) | (len & 0x0f); +} + +/* + * USB Packet IDs (PIDs) + */ + +/* token */ +#define USB_PID_OUT 0xe1 +#define USB_PID_IN 0x69 +#define USB_PID_SOF 0xa5 +#define USB_PID_SETUP 0x2d +/* handshake */ +#define USB_PID_ACK 0xd2 +#define USB_PID_NAK 0x5a +#define USB_PID_STALL 0x1e +#define USB_PID_NYET 0x96 +/* data */ +#define USB_PID_DATA0 0xc3 +#define USB_PID_DATA1 0x4b +#define USB_PID_DATA2 0x87 +#define USB_PID_MDATA 0x0f +/* Special */ +#define USB_PID_PREAMBLE 0x3c +#define USB_PID_ERR 0x3c +#define USB_PID_SPLIT 0x78 +#define USB_PID_PING 0xb4 +#define USB_PID_UNDEF_0 0xf0 + +#define USB_PID_DATA_TOGGLE 0x88 +#define DBGP_CLAIM (DBGP_OWNER | DBGP_ENABLED | DBGP_INUSE) + +#define PCI_CAP_ID_EHCI_DEBUG 0xa + +#define HUB_ROOT_RESET_TIME 50 /* times are in msec */ +#define HUB_SHORT_RESET_TIME 10 +#define HUB_LONG_RESET_TIME 200 +#define HUB_RESET_TIMEOUT 500 + +#define DBGP_MAX_PACKET 8 +#define DBGP_TIMEOUT (250 * 1000) + +static int dbgp_wait_until_complete(void) +{ + u32 ctrl; + int loop = DBGP_TIMEOUT; + + do { + ctrl = readl(&ehci_debug->control); + /* Stop when the transaction is finished */ + if (ctrl & DBGP_DONE) + break; + udelay(1); + } while (--loop > 0); + + if (!loop) + return -DBGP_TIMEOUT; + + /* + * Now that we have observed the completed transaction, + * clear the done bit. + */ + writel(ctrl | DBGP_DONE, &ehci_debug->control); + return (ctrl & DBGP_ERROR) ? -DBGP_ERRCODE(ctrl) : DBGP_LEN(ctrl); +} + +static inline void dbgp_mdelay(int ms) +{ + int i; + + while (ms--) { + for (i = 0; i < 1000; i++) + outb(0x1, 0x80); + } +} + +static void dbgp_breath(void) +{ + /* Sleep to give the debug port a chance to breathe */ +} + +static int dbgp_wait_until_done(unsigned ctrl) +{ + u32 pids, lpid; + int ret; + int loop = 3; + +retry: + writel(ctrl | DBGP_GO, &ehci_debug->control); + ret = dbgp_wait_until_complete(); + pids = readl(&ehci_debug->pids); + lpid = DBGP_PID_GET(pids); + + if (ret < 0) { + /* A -DBGP_TIMEOUT failure here means the device has + * failed, perhaps because it was unplugged, in which + * case we do not want to hang the system so the dbgp + * will be marked as unsafe to use. EHCI reset is the + * only way to recover if you unplug the dbgp device. + */ + if (ret == -DBGP_TIMEOUT && !dbgp_not_safe) + dbgp_not_safe = 1; + return ret; + } + + /* + * If the port is getting full or it has dropped data + * start pacing ourselves, not necessary but it's friendly. + */ + if ((lpid == USB_PID_NAK) || (lpid == USB_PID_NYET)) + dbgp_breath(); + + /* If I get a NACK reissue the transmission */ + if (lpid == USB_PID_NAK) { + if (--loop > 0) + goto retry; + } + + return ret; +} + +static inline void dbgp_set_data(const void *buf, int size) +{ + const unsigned char *bytes = buf; + u32 lo, hi; + int i; + + lo = hi = 0; + for (i = 0; i < 4 && i < size; i++) + lo |= bytes[i] << (8*i); + for (; i < 8 && i < size; i++) + hi |= bytes[i] << (8*(i - 4)); + writel(lo, &ehci_debug->data03); + writel(hi, &ehci_debug->data47); +} + +static inline void dbgp_get_data(void *buf, int size) +{ + unsigned char *bytes = buf; + u32 lo, hi; + int i; + + lo = readl(&ehci_debug->data03); + hi = readl(&ehci_debug->data47); + for (i = 0; i < 4 && i < size; i++) + bytes[i] = (lo >> (8*i)) & 0xff; + for (; i < 8 && i < size; i++) + bytes[i] = (hi >> (8*(i - 4))) & 0xff; +} + +static int dbgp_out(u32 addr, const char *bytes, int size) +{ + u32 pids, ctrl; + + pids = readl(&ehci_debug->pids); + pids = dbgp_pid_update(pids, USB_PID_OUT); + + ctrl = readl(&ehci_debug->control); + ctrl = dbgp_len_update(ctrl, size); + ctrl |= DBGP_OUT; + ctrl |= DBGP_GO; + + dbgp_set_data(bytes, size); + writel(addr, &ehci_debug->address); + writel(pids, &ehci_debug->pids); + return dbgp_wait_until_done(ctrl); +} + +static int dbgp_bulk_write(unsigned devnum, unsigned endpoint, + const char *bytes, int size) +{ + int ret; + int loops = 5; + u32 addr; + if (size > DBGP_MAX_PACKET) + return -1; + + addr = DBGP_EPADDR(devnum, endpoint); +try_again: + if (loops--) { + ret = dbgp_out(addr, bytes, size); + if (ret == -DBGP_ERR_BAD) { + int try_loops = 3; + do { + /* Emit a dummy packet to re-sync communication + * with the debug device */ + if (dbgp_out(addr, "12345678", 8) >= 0) { + udelay(2); + goto try_again; + } + } while (try_loops--); + } + } + + return ret; +} + +static int dbgp_bulk_read(unsigned devnum, unsigned endpoint, void *data, + int size) +{ + u32 pids, addr, ctrl; + int ret; + + if (size > DBGP_MAX_PACKET) + return -1; + + addr = DBGP_EPADDR(devnum, endpoint); + + pids = readl(&ehci_debug->pids); + pids = dbgp_pid_update(pids, USB_PID_IN); + + ctrl = readl(&ehci_debug->control); + ctrl = dbgp_len_update(ctrl, size); + ctrl &= ~DBGP_OUT; + ctrl |= DBGP_GO; + + writel(addr, &ehci_debug->address); + writel(pids, &ehci_debug->pids); + ret = dbgp_wait_until_done(ctrl); + if (ret < 0) + return ret; + + if (size > ret) + size = ret; + dbgp_get_data(data, size); + return ret; +} + +static int dbgp_control_msg(unsigned devnum, int requesttype, + int request, int value, int index, void *data, int size) +{ + u32 pids, addr, ctrl; + struct usb_ctrlrequest req; + int read; + int ret; + + read = (requesttype & USB_DIR_IN) != 0; + if (size > (read ? DBGP_MAX_PACKET:0)) + return -1; + + /* Compute the control message */ + req.bRequestType = requesttype; + req.bRequest = request; + req.wValue = cpu_to_le16(value); + req.wIndex = cpu_to_le16(index); + req.wLength = cpu_to_le16(size); + + pids = DBGP_PID_SET(USB_PID_DATA0, USB_PID_SETUP); + addr = DBGP_EPADDR(devnum, 0); + + ctrl = readl(&ehci_debug->control); + ctrl = dbgp_len_update(ctrl, sizeof(req)); + ctrl |= DBGP_OUT; + ctrl |= DBGP_GO; + + /* Send the setup message */ + dbgp_set_data(&req, sizeof(req)); + writel(addr, &ehci_debug->address); + writel(pids, &ehci_debug->pids); + ret = dbgp_wait_until_done(ctrl); + if (ret < 0) + return ret; + + /* Read the result */ + return dbgp_bulk_read(devnum, 0, data, size); +} + + +/* Find a PCI capability */ +static u32 __init find_cap(u32 num, u32 slot, u32 func, int cap) +{ + u8 pos; + int bytes; + + if (!(read_pci_config_16(num, slot, func, PCI_STATUS) & + PCI_STATUS_CAP_LIST)) + return 0; + + pos = read_pci_config_byte(num, slot, func, PCI_CAPABILITY_LIST); + for (bytes = 0; bytes < 48 && pos >= 0x40; bytes++) { + u8 id; + + pos &= ~3; + id = read_pci_config_byte(num, slot, func, pos+PCI_CAP_LIST_ID); + if (id == 0xff) + break; + if (id == cap) + return pos; + + pos = read_pci_config_byte(num, slot, func, + pos+PCI_CAP_LIST_NEXT); + } + return 0; +} + +static u32 __init __find_dbgp(u32 bus, u32 slot, u32 func) +{ + u32 class; + + class = read_pci_config(bus, slot, func, PCI_CLASS_REVISION); + if ((class >> 8) != PCI_CLASS_SERIAL_USB_EHCI) + return 0; + + return find_cap(bus, slot, func, PCI_CAP_ID_EHCI_DEBUG); +} + +static u32 __init find_dbgp(int ehci_num, u32 *rbus, u32 *rslot, u32 *rfunc) +{ + u32 bus, slot, func; + + for (bus = 0; bus < 256; bus++) { + for (slot = 0; slot < 32; slot++) { + for (func = 0; func < 8; func++) { + unsigned cap; + + cap = __find_dbgp(bus, slot, func); + + if (!cap) + continue; + if (ehci_num-- != 0) + continue; + *rbus = bus; + *rslot = slot; + *rfunc = func; + return cap; + } + } + } + return 0; +} + +static int dbgp_ehci_startup(void) +{ + u32 ctrl, cmd, status; + int loop; + + /* Claim ownership, but do not enable yet */ + ctrl = readl(&ehci_debug->control); + ctrl |= DBGP_OWNER; + ctrl &= ~(DBGP_ENABLED | DBGP_INUSE); + writel(ctrl, &ehci_debug->control); + udelay(1); + + dbgp_ehci_status("EHCI startup"); + /* Start the ehci running */ + cmd = readl(&ehci_regs->command); + cmd &= ~(CMD_LRESET | CMD_IAAD | CMD_PSE | CMD_ASE | CMD_RESET); + cmd |= CMD_RUN; + writel(cmd, &ehci_regs->command); + + /* Ensure everything is routed to the EHCI */ + writel(FLAG_CF, &ehci_regs->configured_flag); + + /* Wait until the controller is no longer halted */ + loop = 10; + do { + status = readl(&ehci_regs->status); + if (!(status & STS_HALT)) + break; + udelay(1); + } while (--loop > 0); + + if (!loop) { + dbgp_printk("ehci can not be started\n"); + return -ENODEV; + } + dbgp_printk("ehci started\n"); + return 0; +} + +static int dbgp_ehci_controller_reset(void) +{ + int loop = 250 * 1000; + u32 cmd; + + /* Reset the EHCI controller */ + cmd = readl(&ehci_regs->command); + cmd |= CMD_RESET; + writel(cmd, &ehci_regs->command); + do { + cmd = readl(&ehci_regs->command); + } while ((cmd & CMD_RESET) && (--loop > 0)); + + if (!loop) { + dbgp_printk("can not reset ehci\n"); + return -1; + } + dbgp_ehci_status("ehci reset done"); + return 0; +} +static int ehci_wait_for_port(int port); +/* Return 0 on success + * Return -ENODEV for any general failure + * Return -EIO if wait for port fails + */ +int dbgp_external_startup(void) +{ + int devnum; + struct usb_debug_descriptor dbgp_desc; + int ret; + u32 ctrl, portsc, cmd; + int dbg_port = dbgp_phys_port; + int tries = 3; + int reset_port_tries = 1; + int try_hard_once = 1; + +try_port_reset_again: + ret = dbgp_ehci_startup(); + if (ret) + return ret; + + /* Wait for a device to show up in the debug port */ + ret = ehci_wait_for_port(dbg_port); + if (ret < 0) { + portsc = readl(&ehci_regs->port_status[dbg_port - 1]); + if (!(portsc & PORT_CONNECT) && try_hard_once) { + /* Last ditch effort to try to force enable + * the debug device by using the packet test + * ehci command to try and wake it up. */ + try_hard_once = 0; + cmd = readl(&ehci_regs->command); + cmd &= ~CMD_RUN; + writel(cmd, &ehci_regs->command); + portsc = readl(&ehci_regs->port_status[dbg_port - 1]); + portsc |= PORT_TEST_PKT; + writel(portsc, &ehci_regs->port_status[dbg_port - 1]); + dbgp_ehci_status("Trying to force debug port online"); + mdelay(50); + dbgp_ehci_controller_reset(); + goto try_port_reset_again; + } else if (reset_port_tries--) { + goto try_port_reset_again; + } + dbgp_printk("No device found in debug port\n"); + return -EIO; + } + dbgp_ehci_status("wait for port done"); + + /* Enable the debug port */ + ctrl = readl(&ehci_debug->control); + ctrl |= DBGP_CLAIM; + writel(ctrl, &ehci_debug->control); + ctrl = readl(&ehci_debug->control); + if ((ctrl & DBGP_CLAIM) != DBGP_CLAIM) { + dbgp_printk("No device in debug port\n"); + writel(ctrl & ~DBGP_CLAIM, &ehci_debug->control); + return -ENODEV; + } + dbgp_ehci_status("debug ported enabled"); + + /* Completely transfer the debug device to the debug controller */ + portsc = readl(&ehci_regs->port_status[dbg_port - 1]); + portsc &= ~PORT_PE; + writel(portsc, &ehci_regs->port_status[dbg_port - 1]); + + dbgp_mdelay(100); + +try_again: + /* Find the debug device and make it device number 127 */ + for (devnum = 0; devnum <= 127; devnum++) { + ret = dbgp_control_msg(devnum, + USB_DIR_IN | USB_TYPE_STANDARD | USB_RECIP_DEVICE, + USB_REQ_GET_DESCRIPTOR, (USB_DT_DEBUG << 8), 0, + &dbgp_desc, sizeof(dbgp_desc)); + if (ret > 0) + break; + } + if (devnum > 127) { + dbgp_printk("Could not find attached debug device\n"); + goto err; + } + if (ret < 0) { + dbgp_printk("Attached device is not a debug device\n"); + goto err; + } + dbgp_endpoint_out = dbgp_desc.bDebugOutEndpoint; + + /* Move the device to 127 if it isn't already there */ + if (devnum != USB_DEBUG_DEVNUM) { + ret = dbgp_control_msg(devnum, + USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_DEVICE, + USB_REQ_SET_ADDRESS, USB_DEBUG_DEVNUM, 0, NULL, 0); + if (ret < 0) { + dbgp_printk("Could not move attached device to %d\n", + USB_DEBUG_DEVNUM); + goto err; + } + devnum = USB_DEBUG_DEVNUM; + dbgp_printk("debug device renamed to 127\n"); + } + + /* Enable the debug interface */ + ret = dbgp_control_msg(USB_DEBUG_DEVNUM, + USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_DEVICE, + USB_REQ_SET_FEATURE, USB_DEVICE_DEBUG_MODE, 0, NULL, 0); + if (ret < 0) { + dbgp_printk(" Could not enable the debug device\n"); + goto err; + } + dbgp_printk("debug interface enabled\n"); + /* Perform a small write to get the even/odd data state in sync + */ + ret = dbgp_bulk_write(USB_DEBUG_DEVNUM, dbgp_endpoint_out, " ", 1); + if (ret < 0) { + dbgp_printk("dbgp_bulk_write failed: %d\n", ret); + goto err; + } + dbgp_printk("small write doned\n"); + dbgp_not_safe = 0; + + return 0; +err: + if (tries--) + goto try_again; + return -ENODEV; +} +EXPORT_SYMBOL_GPL(dbgp_external_startup); + +static int __init ehci_reset_port(int port) +{ + u32 portsc; + u32 delay_time, delay; + int loop; + + dbgp_ehci_status("reset port"); + /* Reset the usb debug port */ + portsc = readl(&ehci_regs->port_status[port - 1]); + portsc &= ~PORT_PE; + portsc |= PORT_RESET; + writel(portsc, &ehci_regs->port_status[port - 1]); + + delay = HUB_ROOT_RESET_TIME; + for (delay_time = 0; delay_time < HUB_RESET_TIMEOUT; + delay_time += delay) { + dbgp_mdelay(delay); + portsc = readl(&ehci_regs->port_status[port - 1]); + if (!(portsc & PORT_RESET)) + break; + } + if (portsc & PORT_RESET) { + /* force reset to complete */ + loop = 100 * 1000; + writel(portsc & ~(PORT_RWC_BITS | PORT_RESET), + &ehci_regs->port_status[port - 1]); + do { + udelay(1); + portsc = readl(&ehci_regs->port_status[port-1]); + } while ((portsc & PORT_RESET) && (--loop > 0)); + } + + /* Device went away? */ + if (!(portsc & PORT_CONNECT)) + return -ENOTCONN; + + /* bomb out completely if something weird happend */ + if ((portsc & PORT_CSC)) + return -EINVAL; + + /* If we've finished resetting, then break out of the loop */ + if (!(portsc & PORT_RESET) && (portsc & PORT_PE)) + return 0; + return -EBUSY; +} + +static int ehci_wait_for_port(int port) +{ + u32 status; + int ret, reps; + + for (reps = 0; reps < 300; reps++) { + status = readl(&ehci_regs->status); + if (status & STS_PCD) + break; + dbgp_mdelay(1); + } + ret = ehci_reset_port(port); + if (ret == 0) + return 0; + return -ENOTCONN; +} + +typedef void (*set_debug_port_t)(int port); + +static void __init default_set_debug_port(int port) +{ +} + +static set_debug_port_t __initdata set_debug_port = default_set_debug_port; + +static void __init nvidia_set_debug_port(int port) +{ + u32 dword; + dword = read_pci_config(ehci_dev.bus, ehci_dev.slot, ehci_dev.func, + 0x74); + dword &= ~(0x0f<<12); + dword |= ((port & 0x0f)<<12); + write_pci_config(ehci_dev.bus, ehci_dev.slot, ehci_dev.func, 0x74, + dword); + dbgp_printk("set debug port to %d\n", port); +} + +static void __init detect_set_debug_port(void) +{ + u32 vendorid; + + vendorid = read_pci_config(ehci_dev.bus, ehci_dev.slot, ehci_dev.func, + 0x00); + + if ((vendorid & 0xffff) == 0x10de) { + dbgp_printk("using nvidia set_debug_port\n"); + set_debug_port = nvidia_set_debug_port; + } +} + +/* The code in early_ehci_bios_handoff() is derived from the usb pci + * quirk initialization, but altered so as to use the early PCI + * routines. */ +#define EHCI_USBLEGSUP_BIOS (1 << 16) /* BIOS semaphore */ +#define EHCI_USBLEGCTLSTS 4 /* legacy control/status */ +static void __init early_ehci_bios_handoff(void) +{ + u32 hcc_params = readl(&ehci_caps->hcc_params); + int offset = (hcc_params >> 8) & 0xff; + u32 cap; + int msec; + + if (!offset) + return; + + cap = read_pci_config(ehci_dev.bus, ehci_dev.slot, + ehci_dev.func, offset); + dbgp_printk("dbgp: ehci BIOS state %08x\n", cap); + + if ((cap & 0xff) == 1 && (cap & EHCI_USBLEGSUP_BIOS)) { + dbgp_printk("dbgp: BIOS handoff\n"); + write_pci_config_byte(ehci_dev.bus, ehci_dev.slot, + ehci_dev.func, offset + 3, 1); + } + + /* if boot firmware now owns EHCI, spin till it hands it over. */ + msec = 1000; + while ((cap & EHCI_USBLEGSUP_BIOS) && (msec > 0)) { + mdelay(10); + msec -= 10; + cap = read_pci_config(ehci_dev.bus, ehci_dev.slot, + ehci_dev.func, offset); + } + + if (cap & EHCI_USBLEGSUP_BIOS) { + /* well, possibly buggy BIOS... try to shut it down, + * and hope nothing goes too wrong */ + dbgp_printk("dbgp: BIOS handoff failed: %08x\n", cap); + write_pci_config_byte(ehci_dev.bus, ehci_dev.slot, + ehci_dev.func, offset + 2, 0); + } + + /* just in case, always disable EHCI SMIs */ + write_pci_config_byte(ehci_dev.bus, ehci_dev.slot, ehci_dev.func, + offset + EHCI_USBLEGCTLSTS, 0); +} + +static int __init ehci_setup(void) +{ + u32 ctrl, portsc, hcs_params; + u32 debug_port, new_debug_port = 0, n_ports; + int ret, i; + int port_map_tried; + int playtimes = 3; + + early_ehci_bios_handoff(); + +try_next_time: + port_map_tried = 0; + +try_next_port: + + hcs_params = readl(&ehci_caps->hcs_params); + debug_port = HCS_DEBUG_PORT(hcs_params); + dbgp_phys_port = debug_port; + n_ports = HCS_N_PORTS(hcs_params); + + dbgp_printk("debug_port: %d\n", debug_port); + dbgp_printk("n_ports: %d\n", n_ports); + dbgp_ehci_status(""); + + for (i = 1; i <= n_ports; i++) { + portsc = readl(&ehci_regs->port_status[i-1]); + dbgp_printk("portstatus%d: %08x\n", i, portsc); + } + + if (port_map_tried && (new_debug_port != debug_port)) { + if (--playtimes) { + set_debug_port(new_debug_port); + goto try_next_time; + } + return -1; + } + + /* Only reset the controller if it is not already in the + * configured state */ + if (!(readl(&ehci_regs->configured_flag) & FLAG_CF)) { + if (dbgp_ehci_controller_reset() != 0) + return -1; + } else { + dbgp_ehci_status("ehci skip - already configured"); + } + + ret = dbgp_external_startup(); + if (ret == -EIO) + goto next_debug_port; + + if (ret < 0) { + /* Things didn't work so remove my claim */ + ctrl = readl(&ehci_debug->control); + ctrl &= ~(DBGP_CLAIM | DBGP_OUT); + writel(ctrl, &ehci_debug->control); + return -1; + } + return 0; + +next_debug_port: + port_map_tried |= (1<<(debug_port - 1)); + new_debug_port = ((debug_port-1+1)%n_ports) + 1; + if (port_map_tried != ((1<<n_ports) - 1)) { + set_debug_port(new_debug_port); + goto try_next_port; + } + if (--playtimes) { + set_debug_port(new_debug_port); + goto try_next_time; + } + + return -1; +} + +int __init early_dbgp_init(char *s) +{ + u32 debug_port, bar, offset; + u32 bus, slot, func, cap; + void __iomem *ehci_bar; + u32 dbgp_num; + u32 bar_val; + char *e; + int ret; + u8 byte; + + if (!early_pci_allowed()) + return -1; + + dbgp_num = 0; + if (*s) + dbgp_num = simple_strtoul(s, &e, 10); + dbgp_printk("dbgp_num: %d\n", dbgp_num); + + cap = find_dbgp(dbgp_num, &bus, &slot, &func); + if (!cap) + return -1; + + dbgp_printk("Found EHCI debug port on %02x:%02x.%1x\n", bus, slot, + func); + + debug_port = read_pci_config(bus, slot, func, cap); + bar = (debug_port >> 29) & 0x7; + bar = (bar * 4) + 0xc; + offset = (debug_port >> 16) & 0xfff; + dbgp_printk("bar: %02x offset: %03x\n", bar, offset); + if (bar != PCI_BASE_ADDRESS_0) { + dbgp_printk("only debug ports on bar 1 handled.\n"); + + return -1; + } + + bar_val = read_pci_config(bus, slot, func, PCI_BASE_ADDRESS_0); + dbgp_printk("bar_val: %02x offset: %03x\n", bar_val, offset); + if (bar_val & ~PCI_BASE_ADDRESS_MEM_MASK) { + dbgp_printk("only simple 32bit mmio bars supported\n"); + + return -1; + } + + /* double check if the mem space is enabled */ + byte = read_pci_config_byte(bus, slot, func, 0x04); + if (!(byte & 0x2)) { + byte |= 0x02; + write_pci_config_byte(bus, slot, func, 0x04, byte); + dbgp_printk("mmio for ehci enabled\n"); + } + + /* + * FIXME I don't have the bar size so just guess PAGE_SIZE is more + * than enough. 1K is the biggest I have seen. + */ + set_fixmap_nocache(FIX_DBGP_BASE, bar_val & PAGE_MASK); + ehci_bar = (void __iomem *)__fix_to_virt(FIX_DBGP_BASE); + ehci_bar += bar_val & ~PAGE_MASK; + dbgp_printk("ehci_bar: %p\n", ehci_bar); + + ehci_caps = ehci_bar; + ehci_regs = ehci_bar + HC_LENGTH(readl(&ehci_caps->hc_capbase)); + ehci_debug = ehci_bar + offset; + ehci_dev.bus = bus; + ehci_dev.slot = slot; + ehci_dev.func = func; + + detect_set_debug_port(); + + ret = ehci_setup(); + if (ret < 0) { + dbgp_printk("ehci_setup failed\n"); + ehci_debug = NULL; + + return -1; + } + dbgp_ehci_status("early_init_complete"); + + return 0; +} + +static void early_dbgp_write(struct console *con, const char *str, u32 n) +{ + int chunk, ret; + char buf[DBGP_MAX_PACKET]; + int use_cr = 0; + u32 cmd, ctrl; + int reset_run = 0; + + if (!ehci_debug || dbgp_not_safe) + return; + + cmd = readl(&ehci_regs->command); + if (unlikely(!(cmd & CMD_RUN))) { + /* If the ehci controller is not in the run state do extended + * checks to see if the acpi or some other initialization also + * reset the ehci debug port */ + ctrl = readl(&ehci_debug->control); + if (!(ctrl & DBGP_ENABLED)) { + dbgp_not_safe = 1; + dbgp_external_startup(); + } else { + cmd |= CMD_RUN; + writel(cmd, &ehci_regs->command); + reset_run = 1; + } + } + while (n > 0) { + for (chunk = 0; chunk < DBGP_MAX_PACKET && n > 0; + str++, chunk++, n--) { + if (!use_cr && *str == '\n') { + use_cr = 1; + buf[chunk] = '\r'; + str--; + n++; + continue; + } + if (use_cr) + use_cr = 0; + buf[chunk] = *str; + } + if (chunk > 0) { + ret = dbgp_bulk_write(USB_DEBUG_DEVNUM, + dbgp_endpoint_out, buf, chunk); + } + } + if (unlikely(reset_run)) { + cmd = readl(&ehci_regs->command); + cmd &= ~CMD_RUN; + writel(cmd, &ehci_regs->command); + } +} + +struct console early_dbgp_console = { + .name = "earlydbg", + .write = early_dbgp_write, + .flags = CON_PRINTBUFFER, + .index = -1, +}; + +int dbgp_reset_prep(void) +{ + u32 ctrl; + + dbgp_not_safe = 1; + if (!ehci_debug) + return 0; + + if (early_dbgp_console.index != -1 && + !(early_dbgp_console.flags & CON_BOOT)) + return 1; + /* This means the console is not initialized, or should get + * shutdown so as to allow for reuse of the usb device, which + * means it is time to shutdown the usb debug port. */ + ctrl = readl(&ehci_debug->control); + if (ctrl & DBGP_ENABLED) { + ctrl &= ~(DBGP_CLAIM); + writel(ctrl, &ehci_debug->control); + } + return 0; +} +EXPORT_SYMBOL_GPL(dbgp_reset_prep); diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index 9f986b417c5b..a18e3c5dd82e 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -124,7 +124,7 @@ choice config USB_GADGET_AT91 boolean "Atmel AT91 USB Device Port" - depends on ARCH_AT91 && !ARCH_AT91SAM9RL && !ARCH_AT91CAP9 + depends on ARCH_AT91 && !ARCH_AT91SAM9RL && !ARCH_AT91CAP9 && !ARCH_AT91SAM9G45 select USB_GADGET_SELECTED help Many Atmel AT91 processors (such as the AT91RM2000) have a @@ -143,7 +143,7 @@ config USB_AT91 config USB_GADGET_ATMEL_USBA boolean "Atmel USBA" select USB_GADGET_DUALSPEED - depends on AVR32 || ARCH_AT91CAP9 || ARCH_AT91SAM9RL + depends on AVR32 || ARCH_AT91CAP9 || ARCH_AT91SAM9RL || ARCH_AT91SAM9G45 help USBA is the integrated high-speed USB Device controller on the AT32AP700x, some AT91SAM9 and AT91CAP9 processors from Atmel. @@ -223,6 +223,7 @@ config USB_OTG config USB_GADGET_PXA25X boolean "PXA 25x or IXP 4xx" depends on (ARCH_PXA && PXA25x) || ARCH_IXP4XX + select USB_OTG_UTILS help Intel's PXA 25x series XScale ARM-5TE processors include an integrated full speed USB 1.1 device controller. The @@ -627,9 +628,10 @@ config USB_AUDIO config USB_ETH tristate "Ethernet Gadget (with CDC Ethernet support)" depends on NET + select CRC32 help - This driver implements Ethernet style communication, in either - of two ways: + This driver implements Ethernet style communication, in one of + several ways: - The "Communication Device Class" (CDC) Ethernet Control Model. That protocol is often avoided with pure Ethernet adapters, in @@ -639,7 +641,11 @@ config USB_ETH - On hardware can't implement that protocol, a simple CDC subset is used, placing fewer demands on USB. - RNDIS support is a third option, more demanding than that subset. + - CDC Ethernet Emulation Model (EEM) is a newer standard that has + a simpler interface that can be used by more USB hardware. + + RNDIS support is an additional option, more demanding than than + subset. Within the USB device, this gadget driver exposes a network device "usbX", where X depends on what other networking devices you have. @@ -672,6 +678,22 @@ config USB_ETH_RNDIS XP, you'll need to download drivers from Microsoft's website; a URL is given in comments found in that info file. +config USB_ETH_EEM + bool "Ethernet Emulation Model (EEM) support" + depends on USB_ETH + default n + help + CDC EEM is a newer USB standard that is somewhat simpler than CDC ECM + and therefore can be supported by more hardware. Technically ECM and + EEM are designed for different applications. The ECM model extends + the network interface to the target (e.g. a USB cable modem), and the + EEM model is for mobile devices to communicate with hosts using + ethernet over USB. For Linux gadgets, however, the interface with + the host is the same (a usbX device), so the differences are minimal. + + If you say "y" here, the Ethernet gadget driver will use the EEM + protocol rather than ECM. If unsure, say "n". + config USB_GADGETFS tristate "Gadget Filesystem (EXPERIMENTAL)" depends on EXPERIMENTAL diff --git a/drivers/usb/gadget/amd5536udc.c b/drivers/usb/gadget/amd5536udc.c index 77352ccc245e..d5b65962dd36 100644 --- a/drivers/usb/gadget/amd5536udc.c +++ b/drivers/usb/gadget/amd5536udc.c @@ -2378,40 +2378,34 @@ static irqreturn_t udc_data_in_isr(struct udc *dev, int ep_ix) if (!ep->cancel_transfer && !list_empty(&ep->queue)) { req = list_entry(ep->queue.next, struct udc_request, queue); - if (req) { - /* - * length bytes transfered - * check dma done of last desc. in PPBDU mode - */ - if (use_dma_ppb_du) { - td = udc_get_last_dma_desc(req); - if (td) { - dma_done = - AMD_GETBITS(td->status, - UDC_DMA_IN_STS_BS); - /* don't care DMA done */ - req->req.actual = - req->req.length; - } - } else { - /* assume all bytes transferred */ + /* + * length bytes transfered + * check dma done of last desc. in PPBDU mode + */ + if (use_dma_ppb_du) { + td = udc_get_last_dma_desc(req); + if (td) { + dma_done = + AMD_GETBITS(td->status, + UDC_DMA_IN_STS_BS); + /* don't care DMA done */ req->req.actual = req->req.length; } + } else { + /* assume all bytes transferred */ + req->req.actual = req->req.length; + } - if (req->req.actual == req->req.length) { - /* complete req */ - complete_req(ep, req, 0); - req->dma_going = 0; - /* further request available ? */ - if (list_empty(&ep->queue)) { - /* disable interrupt */ - tmp = readl( - &dev->regs->ep_irqmsk); - tmp |= AMD_BIT(ep->num); - writel(tmp, - &dev->regs->ep_irqmsk); - } - + if (req->req.actual == req->req.length) { + /* complete req */ + complete_req(ep, req, 0); + req->dma_going = 0; + /* further request available ? */ + if (list_empty(&ep->queue)) { + /* disable interrupt */ + tmp = readl(&dev->regs->ep_irqmsk); + tmp |= AMD_BIT(ep->num); + writel(tmp, &dev->regs->ep_irqmsk); } } } diff --git a/drivers/usb/gadget/at91_udc.c b/drivers/usb/gadget/at91_udc.c index 72bae8f39d81..66450a1abc22 100644 --- a/drivers/usb/gadget/at91_udc.c +++ b/drivers/usb/gadget/at91_udc.c @@ -1754,7 +1754,6 @@ static int __init at91udc_probe(struct platform_device *pdev) IRQF_DISABLED, driver_name, udc)) { DBG("request vbus irq %d failed\n", udc->board.vbus_pin); - free_irq(udc->udp_irq, udc); retval = -EBUSY; goto fail3; } diff --git a/drivers/usb/gadget/audio.c b/drivers/usb/gadget/audio.c index 9f80f4e970bd..a3a0f4a27ef0 100644 --- a/drivers/usb/gadget/audio.c +++ b/drivers/usb/gadget/audio.c @@ -106,20 +106,20 @@ static int audio_set_endpoint_req(struct usb_configuration *c, ctrl->bRequest, w_value, len, ep); switch (ctrl->bRequest) { - case SET_CUR: + case UAC_SET_CUR: value = 0; break; - case SET_MIN: + case UAC_SET_MIN: break; - case SET_MAX: + case UAC_SET_MAX: break; - case SET_RES: + case UAC_SET_RES: break; - case SET_MEM: + case UAC_SET_MEM: break; default: @@ -142,13 +142,13 @@ static int audio_get_endpoint_req(struct usb_configuration *c, ctrl->bRequest, w_value, len, ep); switch (ctrl->bRequest) { - case GET_CUR: - case GET_MIN: - case GET_MAX: - case GET_RES: + case UAC_GET_CUR: + case UAC_GET_MIN: + case UAC_GET_MAX: + case UAC_GET_RES: value = 3; break; - case GET_MEM: + case UAC_GET_MEM: break; default: break; @@ -171,11 +171,11 @@ audio_setup(struct usb_configuration *c, const struct usb_ctrlrequest *ctrl) * Audio class messages; interface activation uses set_alt(). */ switch (ctrl->bRequestType) { - case USB_AUDIO_SET_ENDPOINT: + case USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_ENDPOINT: value = audio_set_endpoint_req(c, ctrl); break; - case USB_AUDIO_GET_ENDPOINT: + case USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_ENDPOINT: value = audio_get_endpoint_req(c, ctrl); break; diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c index 59e85234fa0a..d05397ec8a18 100644 --- a/drivers/usb/gadget/composite.c +++ b/drivers/usb/gadget/composite.c @@ -602,7 +602,7 @@ static int get_string(struct usb_composite_dev *cdev, } } - for (len = 0; s->wData[len] && len <= 126; len++) + for (len = 0; len <= 126 && s->wData[len]; len++) continue; if (!len) return -EINVAL; diff --git a/drivers/usb/gadget/dummy_hcd.c b/drivers/usb/gadget/dummy_hcd.c index a56b24d305f8..5e0966485188 100644 --- a/drivers/usb/gadget/dummy_hcd.c +++ b/drivers/usb/gadget/dummy_hcd.c @@ -1306,11 +1306,6 @@ restart: setup = *(struct usb_ctrlrequest*) urb->setup_packet; w_index = le16_to_cpu(setup.wIndex); w_value = le16_to_cpu(setup.wValue); - if (le16_to_cpu(setup.wLength) != - urb->transfer_buffer_length) { - status = -EOVERFLOW; - goto return_urb; - } /* paranoia, in case of stale queued data */ list_for_each_entry (req, &ep->queue, queue) { diff --git a/drivers/usb/gadget/ether.c b/drivers/usb/gadget/ether.c index bd102f5052ba..167cb2a8ecef 100644 --- a/drivers/usb/gadget/ether.c +++ b/drivers/usb/gadget/ether.c @@ -114,6 +114,7 @@ static inline bool has_rndis(void) #include "f_rndis.c" #include "rndis.c" #endif +#include "f_eem.c" #include "u_ether.c" /*-------------------------------------------------------------------------*/ @@ -150,6 +151,10 @@ static inline bool has_rndis(void) #define RNDIS_VENDOR_NUM 0x0525 /* NetChip */ #define RNDIS_PRODUCT_NUM 0xa4a2 /* Ethernet/RNDIS Gadget */ +/* For EEM gadgets */ +#define EEM_VENDOR_NUM 0x1d6b /* Linux Foundation */ +#define EEM_PRODUCT_NUM 0x0102 /* EEM Gadget */ + /*-------------------------------------------------------------------------*/ static struct usb_device_descriptor device_desc = { @@ -246,8 +251,16 @@ static struct usb_configuration rndis_config_driver = { /*-------------------------------------------------------------------------*/ +#ifdef CONFIG_USB_ETH_EEM +static int use_eem = 1; +#else +static int use_eem; +#endif +module_param(use_eem, bool, 0); +MODULE_PARM_DESC(use_eem, "use CDC EEM mode"); + /* - * We _always_ have an ECM or CDC Subset configuration. + * We _always_ have an ECM, CDC Subset, or EEM configuration. */ static int __init eth_do_config(struct usb_configuration *c) { @@ -258,7 +271,9 @@ static int __init eth_do_config(struct usb_configuration *c) c->bmAttributes |= USB_CONFIG_ATT_WAKEUP; } - if (can_support_ecm(c->cdev->gadget)) + if (use_eem) + return eem_bind_config(c); + else if (can_support_ecm(c->cdev->gadget)) return ecm_bind_config(c, hostaddr); else return geth_bind_config(c, hostaddr); @@ -286,7 +301,12 @@ static int __init eth_bind(struct usb_composite_dev *cdev) return status; /* set up main config label and device descriptor */ - if (can_support_ecm(cdev->gadget)) { + if (use_eem) { + /* EEM */ + eth_config_driver.label = "CDC Ethernet (EEM)"; + device_desc.idVendor = cpu_to_le16(EEM_VENDOR_NUM); + device_desc.idProduct = cpu_to_le16(EEM_PRODUCT_NUM); + } else if (can_support_ecm(cdev->gadget)) { /* ECM */ eth_config_driver.label = "CDC Ethernet (ECM)"; } else { diff --git a/drivers/usb/gadget/f_audio.c b/drivers/usb/gadget/f_audio.c index 66527ba2d2ea..98e9bb977291 100644 --- a/drivers/usb/gadget/f_audio.c +++ b/drivers/usb/gadget/f_audio.c @@ -28,6 +28,9 @@ static int audio_buf_size = 48000; module_param(audio_buf_size, int, S_IRUGO); MODULE_PARM_DESC(audio_buf_size, "Audio buffer size"); +static int generic_set_cmd(struct usb_audio_control *con, u8 cmd, int value); +static int generic_get_cmd(struct usb_audio_control *con, u8 cmd); + /* * DESCRIPTORS ... most are static, but strings and full * configuration descriptors are built on demand. @@ -50,16 +53,16 @@ static struct usb_interface_descriptor ac_interface_desc __initdata = { .bInterfaceSubClass = USB_SUBCLASS_AUDIOCONTROL, }; -DECLARE_USB_AC_HEADER_DESCRIPTOR(2); +DECLARE_UAC_AC_HEADER_DESCRIPTOR(2); -#define USB_DT_AC_HEADER_LENGH USB_DT_AC_HEADER_SIZE(F_AUDIO_NUM_INTERFACES) +#define UAC_DT_AC_HEADER_LENGTH UAC_DT_AC_HEADER_SIZE(F_AUDIO_NUM_INTERFACES) /* B.3.2 Class-Specific AC Interface Descriptor */ -static struct usb_ac_header_descriptor_2 ac_header_desc = { - .bLength = USB_DT_AC_HEADER_LENGH, +static struct uac_ac_header_descriptor_2 ac_header_desc = { + .bLength = UAC_DT_AC_HEADER_LENGTH, .bDescriptorType = USB_DT_CS_INTERFACE, - .bDescriptorSubtype = HEADER, + .bDescriptorSubtype = UAC_HEADER, .bcdADC = __constant_cpu_to_le16(0x0100), - .wTotalLength = __constant_cpu_to_le16(USB_DT_AC_HEADER_LENGH), + .wTotalLength = __constant_cpu_to_le16(UAC_DT_AC_HEADER_LENGTH), .bInCollection = F_AUDIO_NUM_INTERFACES, .baInterfaceNr = { [0] = F_AUDIO_AC_INTERFACE, @@ -68,33 +71,33 @@ static struct usb_ac_header_descriptor_2 ac_header_desc = { }; #define INPUT_TERMINAL_ID 1 -static struct usb_input_terminal_descriptor input_terminal_desc = { - .bLength = USB_DT_AC_INPUT_TERMINAL_SIZE, +static struct uac_input_terminal_descriptor input_terminal_desc = { + .bLength = UAC_DT_INPUT_TERMINAL_SIZE, .bDescriptorType = USB_DT_CS_INTERFACE, - .bDescriptorSubtype = INPUT_TERMINAL, + .bDescriptorSubtype = UAC_INPUT_TERMINAL, .bTerminalID = INPUT_TERMINAL_ID, - .wTerminalType = USB_AC_TERMINAL_STREAMING, + .wTerminalType = UAC_TERMINAL_STREAMING, .bAssocTerminal = 0, .wChannelConfig = 0x3, }; -DECLARE_USB_AC_FEATURE_UNIT_DESCRIPTOR(0); +DECLARE_UAC_FEATURE_UNIT_DESCRIPTOR(0); #define FEATURE_UNIT_ID 2 -static struct usb_ac_feature_unit_descriptor_0 feature_unit_desc = { - .bLength = USB_DT_AC_FEATURE_UNIT_SIZE(0), +static struct uac_feature_unit_descriptor_0 feature_unit_desc = { + .bLength = UAC_DT_FEATURE_UNIT_SIZE(0), .bDescriptorType = USB_DT_CS_INTERFACE, - .bDescriptorSubtype = FEATURE_UNIT, + .bDescriptorSubtype = UAC_FEATURE_UNIT, .bUnitID = FEATURE_UNIT_ID, .bSourceID = INPUT_TERMINAL_ID, .bControlSize = 2, - .bmaControls[0] = (FU_MUTE | FU_VOLUME), + .bmaControls[0] = (UAC_FU_MUTE | UAC_FU_VOLUME), }; static struct usb_audio_control mute_control = { .list = LIST_HEAD_INIT(mute_control.list), .name = "Mute Control", - .type = MUTE_CONTROL, + .type = UAC_MUTE_CONTROL, /* Todo: add real Mute control code */ .set = generic_set_cmd, .get = generic_get_cmd, @@ -103,7 +106,7 @@ static struct usb_audio_control mute_control = { static struct usb_audio_control volume_control = { .list = LIST_HEAD_INIT(volume_control.list), .name = "Volume Control", - .type = VOLUME_CONTROL, + .type = UAC_VOLUME_CONTROL, /* Todo: add real Volume control code */ .set = generic_set_cmd, .get = generic_get_cmd, @@ -113,17 +116,17 @@ static struct usb_audio_control_selector feature_unit = { .list = LIST_HEAD_INIT(feature_unit.list), .id = FEATURE_UNIT_ID, .name = "Mute & Volume Control", - .type = FEATURE_UNIT, + .type = UAC_FEATURE_UNIT, .desc = (struct usb_descriptor_header *)&feature_unit_desc, }; #define OUTPUT_TERMINAL_ID 3 -static struct usb_output_terminal_descriptor output_terminal_desc = { - .bLength = USB_DT_AC_OUTPUT_TERMINAL_SIZE, +static struct uac_output_terminal_descriptor output_terminal_desc = { + .bLength = UAC_DT_OUTPUT_TERMINAL_SIZE, .bDescriptorType = USB_DT_CS_INTERFACE, - .bDescriptorSubtype = OUTPUT_TERMINAL, + .bDescriptorSubtype = UAC_OUTPUT_TERMINAL, .bTerminalID = OUTPUT_TERMINAL_ID, - .wTerminalType = USB_AC_OUTPUT_TERMINAL_SPEAKER, + .wTerminalType = UAC_OUTPUT_TERMINAL_SPEAKER, .bAssocTerminal = FEATURE_UNIT_ID, .bSourceID = FEATURE_UNIT_ID, }; @@ -148,22 +151,22 @@ static struct usb_interface_descriptor as_interface_alt_1_desc = { }; /* B.4.2 Class-Specific AS Interface Descriptor */ -static struct usb_as_header_descriptor as_header_desc = { - .bLength = USB_DT_AS_HEADER_SIZE, +static struct uac_as_header_descriptor as_header_desc = { + .bLength = UAC_DT_AS_HEADER_SIZE, .bDescriptorType = USB_DT_CS_INTERFACE, - .bDescriptorSubtype = AS_GENERAL, + .bDescriptorSubtype = UAC_AS_GENERAL, .bTerminalLink = INPUT_TERMINAL_ID, .bDelay = 1, - .wFormatTag = USB_AS_AUDIO_FORMAT_TYPE_I_PCM, + .wFormatTag = UAC_FORMAT_TYPE_I_PCM, }; -DECLARE_USB_AS_FORMAT_TYPE_I_DISCRETE_DESC(1); +DECLARE_UAC_FORMAT_TYPE_I_DISCRETE_DESC(1); -static struct usb_as_formate_type_i_discrete_descriptor_1 as_type_i_desc = { - .bLength = USB_AS_FORMAT_TYPE_I_DISCRETE_DESC_SIZE(1), +static struct uac_format_type_i_discrete_descriptor_1 as_type_i_desc = { + .bLength = UAC_FORMAT_TYPE_I_DISCRETE_DESC_SIZE(1), .bDescriptorType = USB_DT_CS_INTERFACE, - .bDescriptorSubtype = FORMAT_TYPE, - .bFormatType = USB_AS_FORMAT_TYPE_I, + .bDescriptorSubtype = UAC_FORMAT_TYPE, + .bFormatType = UAC_FORMAT_TYPE_I, .bSubframeSize = 2, .bBitResolution = 16, .bSamFreqType = 1, @@ -174,17 +177,17 @@ static struct usb_endpoint_descriptor as_out_ep_desc __initdata = { .bLength = USB_DT_ENDPOINT_AUDIO_SIZE, .bDescriptorType = USB_DT_ENDPOINT, .bEndpointAddress = USB_DIR_OUT, - .bmAttributes = USB_AS_ENDPOINT_ADAPTIVE + .bmAttributes = USB_ENDPOINT_SYNC_ADAPTIVE | USB_ENDPOINT_XFER_ISOC, .wMaxPacketSize = __constant_cpu_to_le16(OUT_EP_MAX_PACKET_SIZE), .bInterval = 4, }; /* Class-specific AS ISO OUT Endpoint Descriptor */ -static struct usb_as_iso_endpoint_descriptor as_iso_out_desc __initdata = { - .bLength = USB_AS_ISO_ENDPOINT_DESC_SIZE, +static struct uac_iso_endpoint_descriptor as_iso_out_desc __initdata = { + .bLength = UAC_ISO_ENDPOINT_DESC_SIZE, .bDescriptorType = USB_DT_CS_ENDPOINT, - .bDescriptorSubtype = EP_GENERAL, + .bDescriptorSubtype = UAC_EP_GENERAL, .bmAttributes = 1, .bLockDelayUnits = 1, .wLockDelay = __constant_cpu_to_le16(1), @@ -456,11 +459,11 @@ f_audio_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl) * Audio class messages; interface activation uses set_alt(). */ switch (ctrl->bRequestType) { - case USB_AUDIO_SET_INTF: + case USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE: value = audio_set_intf_req(f, ctrl); break; - case USB_AUDIO_GET_INTF: + case USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE: value = audio_get_intf_req(f, ctrl); break; @@ -632,6 +635,18 @@ f_audio_unbind(struct usb_configuration *c, struct usb_function *f) /*-------------------------------------------------------------------------*/ +static int generic_set_cmd(struct usb_audio_control *con, u8 cmd, int value) +{ + con->data[cmd] = value; + + return 0; +} + +static int generic_get_cmd(struct usb_audio_control *con, u8 cmd) +{ + return con->data[cmd]; +} + /* Todo: add more control selecotor dynamically */ int __init control_selector_init(struct f_audio *audio) { @@ -642,10 +657,10 @@ int __init control_selector_init(struct f_audio *audio) list_add(&mute_control.list, &feature_unit.control); list_add(&volume_control.list, &feature_unit.control); - volume_control.data[_CUR] = 0xffc0; - volume_control.data[_MIN] = 0xe3a0; - volume_control.data[_MAX] = 0xfff0; - volume_control.data[_RES] = 0x0030; + volume_control.data[UAC__CUR] = 0xffc0; + volume_control.data[UAC__MIN] = 0xe3a0; + volume_control.data[UAC__MAX] = 0xfff0; + volume_control.data[UAC__RES] = 0x0030; return 0; } diff --git a/drivers/usb/gadget/f_eem.c b/drivers/usb/gadget/f_eem.c new file mode 100644 index 000000000000..0a577d5694fd --- /dev/null +++ b/drivers/usb/gadget/f_eem.c @@ -0,0 +1,562 @@ +/* + * f_eem.c -- USB CDC Ethernet (EEM) link function driver + * + * Copyright (C) 2003-2005,2008 David Brownell + * Copyright (C) 2008 Nokia Corporation + * Copyright (C) 2009 EF Johnson Technologies + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <linux/kernel.h> +#include <linux/device.h> +#include <linux/etherdevice.h> +#include <linux/crc32.h> + +#include "u_ether.h" + +#define EEM_HLEN 2 + +/* + * This function is a "CDC Ethernet Emulation Model" (CDC EEM) + * Ethernet link. + */ + +struct eem_ep_descs { + struct usb_endpoint_descriptor *in; + struct usb_endpoint_descriptor *out; +}; + +struct f_eem { + struct gether port; + u8 ctrl_id; + + struct eem_ep_descs fs; + struct eem_ep_descs hs; +}; + +static inline struct f_eem *func_to_eem(struct usb_function *f) +{ + return container_of(f, struct f_eem, port.func); +} + +/*-------------------------------------------------------------------------*/ + +/* interface descriptor: */ + +static struct usb_interface_descriptor eem_intf __initdata = { + .bLength = sizeof eem_intf, + .bDescriptorType = USB_DT_INTERFACE, + + /* .bInterfaceNumber = DYNAMIC */ + .bNumEndpoints = 2, + .bInterfaceClass = USB_CLASS_COMM, + .bInterfaceSubClass = USB_CDC_SUBCLASS_EEM, + .bInterfaceProtocol = USB_CDC_PROTO_EEM, + /* .iInterface = DYNAMIC */ +}; + +/* full speed support: */ + +static struct usb_endpoint_descriptor eem_fs_in_desc __initdata = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_BULK, +}; + +static struct usb_endpoint_descriptor eem_fs_out_desc __initdata = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + .bEndpointAddress = USB_DIR_OUT, + .bmAttributes = USB_ENDPOINT_XFER_BULK, +}; + +static struct usb_descriptor_header *eem_fs_function[] __initdata = { + /* CDC EEM control descriptors */ + (struct usb_descriptor_header *) &eem_intf, + (struct usb_descriptor_header *) &eem_fs_in_desc, + (struct usb_descriptor_header *) &eem_fs_out_desc, + NULL, +}; + +/* high speed support: */ + +static struct usb_endpoint_descriptor eem_hs_in_desc __initdata = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = cpu_to_le16(512), +}; + +static struct usb_endpoint_descriptor eem_hs_out_desc __initdata = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + .bEndpointAddress = USB_DIR_OUT, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = cpu_to_le16(512), +}; + +static struct usb_descriptor_header *eem_hs_function[] __initdata = { + /* CDC EEM control descriptors */ + (struct usb_descriptor_header *) &eem_intf, + (struct usb_descriptor_header *) &eem_hs_in_desc, + (struct usb_descriptor_header *) &eem_hs_out_desc, + NULL, +}; + +/* string descriptors: */ + +static struct usb_string eem_string_defs[] = { + [0].s = "CDC Ethernet Emulation Model (EEM)", + { } /* end of list */ +}; + +static struct usb_gadget_strings eem_string_table = { + .language = 0x0409, /* en-us */ + .strings = eem_string_defs, +}; + +static struct usb_gadget_strings *eem_strings[] = { + &eem_string_table, + NULL, +}; + +/*-------------------------------------------------------------------------*/ + +static int eem_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl) +{ + struct usb_composite_dev *cdev = f->config->cdev; + int value = -EOPNOTSUPP; + u16 w_index = le16_to_cpu(ctrl->wIndex); + u16 w_value = le16_to_cpu(ctrl->wValue); + u16 w_length = le16_to_cpu(ctrl->wLength); + + DBG(cdev, "invalid control req%02x.%02x v%04x i%04x l%d\n", + ctrl->bRequestType, ctrl->bRequest, + w_value, w_index, w_length); + + /* device either stalls (value < 0) or reports success */ + return value; +} + + +static int eem_set_alt(struct usb_function *f, unsigned intf, unsigned alt) +{ + struct f_eem *eem = func_to_eem(f); + struct usb_composite_dev *cdev = f->config->cdev; + struct net_device *net; + + /* we know alt == 0, so this is an activation or a reset */ + if (alt != 0) + goto fail; + + if (intf == eem->ctrl_id) { + + if (eem->port.in_ep->driver_data) { + DBG(cdev, "reset eem\n"); + gether_disconnect(&eem->port); + } + + if (!eem->port.in) { + DBG(cdev, "init eem\n"); + eem->port.in = ep_choose(cdev->gadget, + eem->hs.in, eem->fs.in); + eem->port.out = ep_choose(cdev->gadget, + eem->hs.out, eem->fs.out); + } + + /* zlps should not occur because zero-length EEM packets + * will be inserted in those cases where they would occur + */ + eem->port.is_zlp_ok = 1; + eem->port.cdc_filter = DEFAULT_FILTER; + DBG(cdev, "activate eem\n"); + net = gether_connect(&eem->port); + if (IS_ERR(net)) + return PTR_ERR(net); + } else + goto fail; + + return 0; +fail: + return -EINVAL; +} + +static void eem_disable(struct usb_function *f) +{ + struct f_eem *eem = func_to_eem(f); + struct usb_composite_dev *cdev = f->config->cdev; + + DBG(cdev, "eem deactivated\n"); + + if (eem->port.in_ep->driver_data) + gether_disconnect(&eem->port); +} + +/*-------------------------------------------------------------------------*/ + +/* EEM function driver setup/binding */ + +static int __init +eem_bind(struct usb_configuration *c, struct usb_function *f) +{ + struct usb_composite_dev *cdev = c->cdev; + struct f_eem *eem = func_to_eem(f); + int status; + struct usb_ep *ep; + + /* allocate instance-specific interface IDs */ + status = usb_interface_id(c, f); + if (status < 0) + goto fail; + eem->ctrl_id = status; + eem_intf.bInterfaceNumber = status; + + status = -ENODEV; + + /* allocate instance-specific endpoints */ + ep = usb_ep_autoconfig(cdev->gadget, &eem_fs_in_desc); + if (!ep) + goto fail; + eem->port.in_ep = ep; + ep->driver_data = cdev; /* claim */ + + ep = usb_ep_autoconfig(cdev->gadget, &eem_fs_out_desc); + if (!ep) + goto fail; + eem->port.out_ep = ep; + ep->driver_data = cdev; /* claim */ + + status = -ENOMEM; + + /* copy descriptors, and track endpoint copies */ + f->descriptors = usb_copy_descriptors(eem_fs_function); + if (!f->descriptors) + goto fail; + + eem->fs.in = usb_find_endpoint(eem_fs_function, + f->descriptors, &eem_fs_in_desc); + eem->fs.out = usb_find_endpoint(eem_fs_function, + f->descriptors, &eem_fs_out_desc); + + /* support all relevant hardware speeds... we expect that when + * hardware is dual speed, all bulk-capable endpoints work at + * both speeds + */ + if (gadget_is_dualspeed(c->cdev->gadget)) { + eem_hs_in_desc.bEndpointAddress = + eem_fs_in_desc.bEndpointAddress; + eem_hs_out_desc.bEndpointAddress = + eem_fs_out_desc.bEndpointAddress; + + /* copy descriptors, and track endpoint copies */ + f->hs_descriptors = usb_copy_descriptors(eem_hs_function); + if (!f->hs_descriptors) + goto fail; + + eem->hs.in = usb_find_endpoint(eem_hs_function, + f->hs_descriptors, &eem_hs_in_desc); + eem->hs.out = usb_find_endpoint(eem_hs_function, + f->hs_descriptors, &eem_hs_out_desc); + } + + DBG(cdev, "CDC Ethernet (EEM): %s speed IN/%s OUT/%s\n", + gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full", + eem->port.in_ep->name, eem->port.out_ep->name); + return 0; + +fail: + if (f->descriptors) + usb_free_descriptors(f->descriptors); + + /* we might as well release our claims on endpoints */ + if (eem->port.out) + eem->port.out_ep->driver_data = NULL; + if (eem->port.in) + eem->port.in_ep->driver_data = NULL; + + ERROR(cdev, "%s: can't bind, err %d\n", f->name, status); + + return status; +} + +static void +eem_unbind(struct usb_configuration *c, struct usb_function *f) +{ + struct f_eem *eem = func_to_eem(f); + + DBG(c->cdev, "eem unbind\n"); + + if (gadget_is_dualspeed(c->cdev->gadget)) + usb_free_descriptors(f->hs_descriptors); + usb_free_descriptors(f->descriptors); + kfree(eem); +} + +static void eem_cmd_complete(struct usb_ep *ep, struct usb_request *req) +{ +} + +/* + * Add the EEM header and ethernet checksum. + * We currently do not attempt to put multiple ethernet frames + * into a single USB transfer + */ +static struct sk_buff *eem_wrap(struct gether *port, struct sk_buff *skb) +{ + struct sk_buff *skb2 = NULL; + struct usb_ep *in = port->in_ep; + int padlen = 0; + u16 len = skb->len; + + if (!skb_cloned(skb)) { + int headroom = skb_headroom(skb); + int tailroom = skb_tailroom(skb); + + /* When (len + EEM_HLEN + ETH_FCS_LEN) % in->maxpacket) is 0, + * stick two bytes of zero-length EEM packet on the end. + */ + if (((len + EEM_HLEN + ETH_FCS_LEN) % in->maxpacket) == 0) + padlen += 2; + + if ((tailroom >= (ETH_FCS_LEN + padlen)) && + (headroom >= EEM_HLEN)) + goto done; + } + + skb2 = skb_copy_expand(skb, EEM_HLEN, ETH_FCS_LEN + padlen, GFP_ATOMIC); + dev_kfree_skb_any(skb); + skb = skb2; + if (!skb) + return skb; + +done: + /* use the "no CRC" option */ + put_unaligned_be32(0xdeadbeef, skb_put(skb, 4)); + + /* EEM packet header format: + * b0..13: length of ethernet frame + * b14: bmCRC (0 == sentinel CRC) + * b15: bmType (0 == data) + */ + len = skb->len; + put_unaligned_le16((len & 0x3FFF) | BIT(14), skb_push(skb, 2)); + + /* add a zero-length EEM packet, if needed */ + if (padlen) + put_unaligned_le16(0, skb_put(skb, 2)); + + return skb; +} + +/* + * Remove the EEM header. Note that there can be many EEM packets in a single + * USB transfer, so we need to break them out and handle them independently. + */ +static int eem_unwrap(struct gether *port, + struct sk_buff *skb, + struct sk_buff_head *list) +{ + struct usb_composite_dev *cdev = port->func.config->cdev; + int status = 0; + + do { + struct sk_buff *skb2; + u16 header; + u16 len = 0; + + if (skb->len < EEM_HLEN) { + status = -EINVAL; + DBG(cdev, "invalid EEM header\n"); + goto error; + } + + /* remove the EEM header */ + header = get_unaligned_le16(skb->data); + skb_pull(skb, EEM_HLEN); + + /* EEM packet header format: + * b0..14: EEM type dependent (data or command) + * b15: bmType (0 == data, 1 == command) + */ + if (header & BIT(15)) { + struct usb_request *req = cdev->req; + u16 bmEEMCmd; + + /* EEM command packet format: + * b0..10: bmEEMCmdParam + * b11..13: bmEEMCmd + * b14: reserved (must be zero) + * b15: bmType (1 == command) + */ + if (header & BIT(14)) + continue; + + bmEEMCmd = (header >> 11) & 0x7; + switch (bmEEMCmd) { + case 0: /* echo */ + len = header & 0x7FF; + if (skb->len < len) { + status = -EOVERFLOW; + goto error; + } + + skb2 = skb_clone(skb, GFP_ATOMIC); + if (unlikely(!skb2)) { + DBG(cdev, "EEM echo response error\n"); + goto next; + } + skb_trim(skb2, len); + put_unaligned_le16(BIT(15) | BIT(11) | len, + skb_push(skb2, 2)); + skb_copy_bits(skb, 0, req->buf, skb->len); + req->length = skb->len; + req->complete = eem_cmd_complete; + req->zero = 1; + if (usb_ep_queue(port->in_ep, req, GFP_ATOMIC)) + DBG(cdev, "echo response queue fail\n"); + break; + + case 1: /* echo response */ + case 2: /* suspend hint */ + case 3: /* response hint */ + case 4: /* response complete hint */ + case 5: /* tickle */ + default: /* reserved */ + continue; + } + } else { + u32 crc, crc2; + struct sk_buff *skb3; + + /* check for zero-length EEM packet */ + if (header == 0) + continue; + + /* EEM data packet format: + * b0..13: length of ethernet frame + * b14: bmCRC (0 == sentinel, 1 == calculated) + * b15: bmType (0 == data) + */ + len = header & 0x3FFF; + if ((skb->len < len) + || (len < (ETH_HLEN + ETH_FCS_LEN))) { + status = -EINVAL; + goto error; + } + + /* validate CRC */ + crc = get_unaligned_le32(skb->data + len - ETH_FCS_LEN); + if (header & BIT(14)) { + crc = get_unaligned_le32(skb->data + len + - ETH_FCS_LEN); + crc2 = ~crc32_le(~0, + skb->data, + skb->len - ETH_FCS_LEN); + } else { + crc = get_unaligned_be32(skb->data + len + - ETH_FCS_LEN); + crc2 = 0xdeadbeef; + } + if (crc != crc2) { + DBG(cdev, "invalid EEM CRC\n"); + goto next; + } + + skb2 = skb_clone(skb, GFP_ATOMIC); + if (unlikely(!skb2)) { + DBG(cdev, "unable to unframe EEM packet\n"); + continue; + } + skb_trim(skb2, len - ETH_FCS_LEN); + + skb3 = skb_copy_expand(skb2, + NET_IP_ALIGN, + 0, + GFP_ATOMIC); + if (unlikely(!skb3)) { + DBG(cdev, "unable to realign EEM packet\n"); + dev_kfree_skb_any(skb2); + continue; + } + dev_kfree_skb_any(skb2); + skb_queue_tail(list, skb3); + } +next: + skb_pull(skb, len); + } while (skb->len); + +error: + dev_kfree_skb_any(skb); + return status; +} + +/** + * eem_bind_config - add CDC Ethernet (EEM) network link to a configuration + * @c: the configuration to support the network link + * Context: single threaded during gadget setup + * + * Returns zero on success, else negative errno. + * + * Caller must have called @gether_setup(). Caller is also responsible + * for calling @gether_cleanup() before module unload. + */ +int __init eem_bind_config(struct usb_configuration *c) +{ + struct f_eem *eem; + int status; + + /* maybe allocate device-global string IDs */ + if (eem_string_defs[0].id == 0) { + + /* control interface label */ + status = usb_string_id(c->cdev); + if (status < 0) + return status; + eem_string_defs[0].id = status; + eem_intf.iInterface = status; + } + + /* allocate and initialize one new instance */ + eem = kzalloc(sizeof *eem, GFP_KERNEL); + if (!eem) + return -ENOMEM; + + eem->port.cdc_filter = DEFAULT_FILTER; + + eem->port.func.name = "cdc_eem"; + eem->port.func.strings = eem_strings; + /* descriptors are per-instance copies */ + eem->port.func.bind = eem_bind; + eem->port.func.unbind = eem_unbind; + eem->port.func.set_alt = eem_set_alt; + eem->port.func.setup = eem_setup; + eem->port.func.disable = eem_disable; + eem->port.wrap = eem_wrap; + eem->port.unwrap = eem_unwrap; + eem->port.header_len = EEM_HLEN; + + status = usb_add_function(c, &eem->port.func); + if (status) + kfree(eem); + return status; +} + diff --git a/drivers/usb/gadget/f_loopback.c b/drivers/usb/gadget/f_loopback.c index eb6ddfc20857..6cb29d3df575 100644 --- a/drivers/usb/gadget/f_loopback.c +++ b/drivers/usb/gadget/f_loopback.c @@ -22,7 +22,6 @@ /* #define VERBOSE_DEBUG */ #include <linux/kernel.h> -#include <linux/utsname.h> #include <linux/device.h> #include "g_zero.h" diff --git a/drivers/usb/gadget/f_obex.c b/drivers/usb/gadget/f_obex.c index 46d6266f30ec..b4a3ba654ea5 100644 --- a/drivers/usb/gadget/f_obex.c +++ b/drivers/usb/gadget/f_obex.c @@ -24,7 +24,6 @@ /* #define VERBOSE_DEBUG */ #include <linux/kernel.h> -#include <linux/utsname.h> #include <linux/device.h> #include "u_serial.h" diff --git a/drivers/usb/gadget/f_rndis.c b/drivers/usb/gadget/f_rndis.c index 424a37c5773f..c9966cc07d3a 100644 --- a/drivers/usb/gadget/f_rndis.c +++ b/drivers/usb/gadget/f_rndis.c @@ -286,12 +286,17 @@ static struct usb_gadget_strings *rndis_strings[] = { /*-------------------------------------------------------------------------*/ -static struct sk_buff *rndis_add_header(struct sk_buff *skb) +static struct sk_buff *rndis_add_header(struct gether *port, + struct sk_buff *skb) { - skb = skb_realloc_headroom(skb, sizeof(struct rndis_packet_msg_type)); - if (skb) - rndis_add_hdr(skb); - return skb; + struct sk_buff *skb2; + + skb2 = skb_realloc_headroom(skb, sizeof(struct rndis_packet_msg_type)); + if (skb2) + rndis_add_hdr(skb2); + + dev_kfree_skb_any(skb); + return skb2; } static void rndis_response_available(void *_rndis) diff --git a/drivers/usb/gadget/f_sourcesink.c b/drivers/usb/gadget/f_sourcesink.c index bffe91d525f9..09cba273d2db 100644 --- a/drivers/usb/gadget/f_sourcesink.c +++ b/drivers/usb/gadget/f_sourcesink.c @@ -22,7 +22,6 @@ /* #define VERBOSE_DEBUG */ #include <linux/kernel.h> -#include <linux/utsname.h> #include <linux/device.h> #include "g_zero.h" diff --git a/drivers/usb/gadget/fsl_qe_udc.c b/drivers/usb/gadget/fsl_qe_udc.c index d701bf4698d2..7881f12413c4 100644 --- a/drivers/usb/gadget/fsl_qe_udc.c +++ b/drivers/usb/gadget/fsl_qe_udc.c @@ -2751,6 +2751,10 @@ static int __devexit qe_udc_remove(struct of_device *ofdev) /*-------------------------------------------------------------------------*/ static struct of_device_id __devinitdata qe_udc_match[] = { { + .compatible = "fsl,mpc8323-qe-usb", + .data = (void *)PORT_QE, + }, + { .compatible = "fsl,mpc8360-qe-usb", .data = (void *)PORT_QE, }, diff --git a/drivers/usb/gadget/fsl_udc_core.c b/drivers/usb/gadget/fsl_udc_core.c index 42a74b8a0bb8..fa3d142ba64d 100644 --- a/drivers/usb/gadget/fsl_udc_core.c +++ b/drivers/usb/gadget/fsl_udc_core.c @@ -2139,7 +2139,7 @@ static int fsl_proc_read(char *page, char **start, off_t off, int count, static void fsl_udc_release(struct device *dev) { complete(udc_controller->done); - dma_free_coherent(dev, udc_controller->ep_qh_size, + dma_free_coherent(dev->parent, udc_controller->ep_qh_size, udc_controller->ep_qh, udc_controller->ep_qh_dma); kfree(udc_controller); } diff --git a/drivers/usb/gadget/gmidi.c b/drivers/usb/gadget/gmidi.c index b9312dc6e041..d0b1e836f0e0 100644 --- a/drivers/usb/gadget/gmidi.c +++ b/drivers/usb/gadget/gmidi.c @@ -191,7 +191,7 @@ module_param(qlen, uint, S_IRUGO); #define GMIDI_MS_INTERFACE 1 #define GMIDI_NUM_INTERFACES 2 -DECLARE_USB_AC_HEADER_DESCRIPTOR(1); +DECLARE_UAC_AC_HEADER_DESCRIPTOR(1); DECLARE_USB_MIDI_OUT_JACK_DESCRIPTOR(1); DECLARE_USB_MS_ENDPOINT_DESCRIPTOR(1); @@ -237,12 +237,12 @@ static const struct usb_interface_descriptor ac_interface_desc = { }; /* B.3.2 Class-Specific AC Interface Descriptor */ -static const struct usb_ac_header_descriptor_1 ac_header_desc = { - .bLength = USB_DT_AC_HEADER_SIZE(1), +static const struct uac_ac_header_descriptor_1 ac_header_desc = { + .bLength = UAC_DT_AC_HEADER_SIZE(1), .bDescriptorType = USB_DT_CS_INTERFACE, .bDescriptorSubtype = USB_MS_HEADER, .bcdADC = cpu_to_le16(0x0100), - .wTotalLength = cpu_to_le16(USB_DT_AC_HEADER_SIZE(1)), + .wTotalLength = cpu_to_le16(UAC_DT_AC_HEADER_SIZE(1)), .bInCollection = 1, .baInterfaceNr = { [0] = GMIDI_MS_INTERFACE, diff --git a/drivers/usb/gadget/imx_udc.c b/drivers/usb/gadget/imx_udc.c index c52a681f376c..01ee0b9bc957 100644 --- a/drivers/usb/gadget/imx_udc.c +++ b/drivers/usb/gadget/imx_udc.c @@ -1402,7 +1402,8 @@ static int __init imx_udc_probe(struct platform_device *pdev) struct clk *clk; void __iomem *base; int ret = 0; - int i, res_size; + int i; + resource_size_t res_size; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) { @@ -1416,7 +1417,7 @@ static int __init imx_udc_probe(struct platform_device *pdev) return -ENODEV; } - res_size = res->end - res->start + 1; + res_size = resource_size(res); if (!request_mem_region(res->start, res_size, res->name)) { dev_err(&pdev->dev, "can't allocate %d bytes at %d address\n", res_size, res->start); @@ -1527,8 +1528,7 @@ static int __exit imx_udc_remove(struct platform_device *pdev) clk_disable(imx_usb->clk); iounmap(imx_usb->base); - release_mem_region(imx_usb->res->start, - imx_usb->res->end - imx_usb->res->start + 1); + release_mem_region(imx_usb->res->start, resource_size(imx_usb->res)); if (pdata->exit) pdata->exit(&pdev->dev); diff --git a/drivers/usb/gadget/inode.c b/drivers/usb/gadget/inode.c index c44367fea185..bf0f6520c6df 100644 --- a/drivers/usb/gadget/inode.c +++ b/drivers/usb/gadget/inode.c @@ -30,6 +30,7 @@ #include <linux/wait.h> #include <linux/compiler.h> #include <asm/uaccess.h> +#include <linux/sched.h> #include <linux/slab.h> #include <linux/poll.h> #include <linux/smp_lock.h> diff --git a/drivers/usb/gadget/printer.c b/drivers/usb/gadget/printer.c index 29500154d00c..2d867fd22413 100644 --- a/drivers/usb/gadget/printer.c +++ b/drivers/usb/gadget/printer.c @@ -875,7 +875,7 @@ printer_ioctl(struct file *fd, unsigned int code, unsigned long arg) } /* used after endpoint configuration */ -static struct file_operations printer_io_operations = { +static const struct file_operations printer_io_operations = { .owner = THIS_MODULE, .open = printer_open, .read = printer_read, diff --git a/drivers/usb/gadget/pxa25x_udc.c b/drivers/usb/gadget/pxa25x_udc.c index ed21e263f832..e6fedbd5a654 100644 --- a/drivers/usb/gadget/pxa25x_udc.c +++ b/drivers/usb/gadget/pxa25x_udc.c @@ -56,6 +56,7 @@ #include <linux/usb/ch9.h> #include <linux/usb/gadget.h> +#include <linux/usb/otg.h> /* * This driver is PXA25x only. Grab the right register definitions. @@ -1008,15 +1009,27 @@ static int pxa25x_udc_pullup(struct usb_gadget *_gadget, int is_active) return 0; } +/* boards may consume current from VBUS, up to 100-500mA based on config. + * the 500uA suspend ceiling means that exclusively vbus-powered PXA designs + * violate USB specs. + */ +static int pxa25x_udc_vbus_draw(struct usb_gadget *_gadget, unsigned mA) +{ + struct pxa25x_udc *udc; + + udc = container_of(_gadget, struct pxa25x_udc, gadget); + + if (udc->transceiver) + return otg_set_power(udc->transceiver, mA); + return -EOPNOTSUPP; +} + static const struct usb_gadget_ops pxa25x_udc_ops = { .get_frame = pxa25x_udc_get_frame, .wakeup = pxa25x_udc_wakeup, .vbus_session = pxa25x_udc_vbus_session, .pullup = pxa25x_udc_pullup, - - // .vbus_draw ... boards may consume current from VBUS, up to - // 100-500mA based on config. the 500uA suspend ceiling means - // that exclusively vbus-powered PXA designs violate USB specs. + .vbus_draw = pxa25x_udc_vbus_draw, }; /*-------------------------------------------------------------------------*/ @@ -1303,9 +1316,23 @@ fail: * for set_configuration as well as eventual disconnect. */ DMSG("registered gadget driver '%s'\n", driver->driver.name); + + /* connect to bus through transceiver */ + if (dev->transceiver) { + retval = otg_set_peripheral(dev->transceiver, &dev->gadget); + if (retval) { + DMSG("can't bind to transceiver\n"); + if (driver->unbind) + driver->unbind(&dev->gadget); + goto bind_fail; + } + } + pullup(dev); dump_state(dev); return 0; +bind_fail: + return retval; } EXPORT_SYMBOL(usb_gadget_register_driver); @@ -1351,6 +1378,9 @@ int usb_gadget_unregister_driver(struct usb_gadget_driver *driver) stop_activity(dev, driver); local_irq_enable(); + if (dev->transceiver) + (void) otg_set_peripheral(dev->transceiver, NULL); + driver->unbind(&dev->gadget); dev->gadget.dev.driver = NULL; dev->driver = NULL; @@ -2162,6 +2192,8 @@ static int __init pxa25x_udc_probe(struct platform_device *pdev) dev->dev = &pdev->dev; dev->mach = pdev->dev.platform_data; + dev->transceiver = otg_get_transceiver(); + if (gpio_is_valid(dev->mach->gpio_vbus)) { if ((retval = gpio_request(dev->mach->gpio_vbus, "pxa25x_udc GPIO VBUS"))) { @@ -2264,6 +2296,10 @@ lubbock_fail0: if (gpio_is_valid(dev->mach->gpio_vbus)) gpio_free(dev->mach->gpio_vbus); err_gpio_vbus: + if (dev->transceiver) { + otg_put_transceiver(dev->transceiver); + dev->transceiver = NULL; + } clk_put(dev->clk); err_clk: return retval; @@ -2305,6 +2341,11 @@ static int __exit pxa25x_udc_remove(struct platform_device *pdev) clk_put(dev->clk); + if (dev->transceiver) { + otg_put_transceiver(dev->transceiver); + dev->transceiver = NULL; + } + platform_set_drvdata(pdev, NULL); the_controller = NULL; return 0; diff --git a/drivers/usb/gadget/pxa25x_udc.h b/drivers/usb/gadget/pxa25x_udc.h index 1d51aa21e6eb..f572c5617462 100644 --- a/drivers/usb/gadget/pxa25x_udc.h +++ b/drivers/usb/gadget/pxa25x_udc.h @@ -128,6 +128,7 @@ struct pxa25x_udc { struct device *dev; struct clk *clk; struct pxa2xx_udc_mach_info *mach; + struct otg_transceiver *transceiver; u64 dma_mask; struct pxa25x_ep ep [PXA_UDC_NUM_ENDPOINTS]; diff --git a/drivers/usb/gadget/r8a66597-udc.h b/drivers/usb/gadget/r8a66597-udc.h index 03087e7b9190..9a537aa07968 100644 --- a/drivers/usb/gadget/r8a66597-udc.h +++ b/drivers/usb/gadget/r8a66597-udc.h @@ -131,31 +131,48 @@ static inline u16 r8a66597_read(struct r8a66597 *r8a66597, unsigned long offset) } static inline void r8a66597_read_fifo(struct r8a66597 *r8a66597, - unsigned long offset, u16 *buf, + unsigned long offset, + unsigned char *buf, int len) { + unsigned long fifoaddr = r8a66597->reg + offset; + unsigned int data; + int i; + if (r8a66597->pdata->on_chip) { - unsigned long fifoaddr = r8a66597->reg + offset; - unsigned long count; - union { - unsigned long dword; - unsigned char byte[4]; - } data; - unsigned char *pb; - int i; - - count = len / 4; - insl(fifoaddr, buf, count); - - if (len & 0x00000003) { - data.dword = inl(fifoaddr); - pb = (unsigned char *)buf + count * 4; - for (i = 0; i < (len & 0x00000003); i++) - pb[i] = data.byte[i]; + /* 32-bit accesses for on_chip controllers */ + + /* aligned buf case */ + if (len >= 4 && !((unsigned long)buf & 0x03)) { + insl(fifoaddr, buf, len / 4); + buf += len & ~0x03; + len &= 0x03; + } + + /* unaligned buf case */ + for (i = 0; i < len; i++) { + if (!(i & 0x03)) + data = inl(fifoaddr); + + buf[i] = (data >> ((i & 0x03) * 8)) & 0xff; } } else { - len = (len + 1) / 2; - insw(r8a66597->reg + offset, buf, len); + /* 16-bit accesses for external controllers */ + + /* aligned buf case */ + if (len >= 2 && !((unsigned long)buf & 0x01)) { + insw(fifoaddr, buf, len / 2); + buf += len & ~0x01; + len &= 0x01; + } + + /* unaligned buf case */ + for (i = 0; i < len; i++) { + if (!(i & 0x01)) + data = inw(fifoaddr); + + buf[i] = (data >> ((i & 0x01) * 8)) & 0xff; + } } } @@ -166,38 +183,40 @@ static inline void r8a66597_write(struct r8a66597 *r8a66597, u16 val, } static inline void r8a66597_write_fifo(struct r8a66597 *r8a66597, - unsigned long offset, u16 *buf, + unsigned long offset, + unsigned char *buf, int len) { unsigned long fifoaddr = r8a66597->reg + offset; + int adj = 0; + int i; if (r8a66597->pdata->on_chip) { - unsigned long count; - unsigned char *pb; - int i; - - count = len / 4; - outsl(fifoaddr, buf, count); - - if (len & 0x00000003) { - pb = (unsigned char *)buf + count * 4; - for (i = 0; i < (len & 0x00000003); i++) { - if (r8a66597_read(r8a66597, CFIFOSEL) & BIGEND) - outb(pb[i], fifoaddr + i); - else - outb(pb[i], fifoaddr + 3 - i); - } + /* 32-bit access only if buf is 32-bit aligned */ + if (len >= 4 && !((unsigned long)buf & 0x03)) { + outsl(fifoaddr, buf, len / 4); + buf += len & ~0x03; + len &= 0x03; } } else { - int odd = len & 0x0001; - - len = len / 2; - outsw(fifoaddr, buf, len); - if (unlikely(odd)) { - buf = &buf[len]; - outb((unsigned char)*buf, fifoaddr); + /* 16-bit access only if buf is 16-bit aligned */ + if (len >= 2 && !((unsigned long)buf & 0x01)) { + outsw(fifoaddr, buf, len / 2); + buf += len & ~0x01; + len &= 0x01; } } + + /* adjust fifo address in the little endian case */ + if (!(r8a66597_read(r8a66597, CFIFOSEL) & BIGEND)) { + if (r8a66597->pdata->on_chip) + adj = 0x03; /* 32-bit wide */ + else + adj = 0x01; /* 16-bit wide */ + } + + for (i = 0; i < len; i++) + outb(buf[i], fifoaddr + adj - (i & adj)); } static inline void r8a66597_mdfy(struct r8a66597 *r8a66597, diff --git a/drivers/usb/gadget/rndis.c b/drivers/usb/gadget/rndis.c index ca41b0b5afb3..48267bc0b2e0 100644 --- a/drivers/usb/gadget/rndis.c +++ b/drivers/usb/gadget/rndis.c @@ -1022,22 +1022,29 @@ static rndis_resp_t *rndis_add_response (int configNr, u32 length) return r; } -int rndis_rm_hdr(struct sk_buff *skb) +int rndis_rm_hdr(struct gether *port, + struct sk_buff *skb, + struct sk_buff_head *list) { /* tmp points to a struct rndis_packet_msg_type */ __le32 *tmp = (void *) skb->data; /* MessageType, MessageLength */ if (cpu_to_le32(REMOTE_NDIS_PACKET_MSG) - != get_unaligned(tmp++)) + != get_unaligned(tmp++)) { + dev_kfree_skb_any(skb); return -EINVAL; + } tmp++; /* DataOffset, DataLength */ - if (!skb_pull(skb, get_unaligned_le32(tmp++) + 8)) + if (!skb_pull(skb, get_unaligned_le32(tmp++) + 8)) { + dev_kfree_skb_any(skb); return -EOVERFLOW; + } skb_trim(skb, get_unaligned_le32(tmp++)); + skb_queue_tail(list, skb); return 0; } diff --git a/drivers/usb/gadget/rndis.h b/drivers/usb/gadget/rndis.h index aac61dfe0f03..c236aaa9dcd1 100644 --- a/drivers/usb/gadget/rndis.h +++ b/drivers/usb/gadget/rndis.h @@ -251,7 +251,8 @@ int rndis_set_param_vendor (u8 configNr, u32 vendorID, const char *vendorDescr); int rndis_set_param_medium (u8 configNr, u32 medium, u32 speed); void rndis_add_hdr (struct sk_buff *skb); -int rndis_rm_hdr (struct sk_buff *skb); +int rndis_rm_hdr(struct gether *port, struct sk_buff *skb, + struct sk_buff_head *list); u8 *rndis_get_next_response (int configNr, u32 *length); void rndis_free_response (int configNr, u8 *buf); diff --git a/drivers/usb/gadget/s3c-hsotg.c b/drivers/usb/gadget/s3c-hsotg.c index 50c71aae2cc2..4b5dbd0127f5 100644 --- a/drivers/usb/gadget/s3c-hsotg.c +++ b/drivers/usb/gadget/s3c-hsotg.c @@ -2392,7 +2392,7 @@ static int s3c_hsotg_corereset(struct s3c_hsotg *hsotg) grstctl = readl(hsotg->regs + S3C_GRSTCTL); } while (!(grstctl & S3C_GRSTCTL_CSftRst) && timeout-- > 0); - if (!grstctl & S3C_GRSTCTL_CSftRst) { + if (!(grstctl & S3C_GRSTCTL_CSftRst)) { dev_err(hsotg->dev, "Failed to get CSftRst asserted\n"); return -EINVAL; } @@ -2514,8 +2514,8 @@ int usb_gadget_register_driver(struct usb_gadget_driver *driver) * DMA mode we may need this. */ writel(S3C_DOEPMSK_SetupMsk | S3C_DOEPMSK_AHBErrMsk | S3C_DOEPMSK_EPDisbldMsk | - using_dma(hsotg) ? (S3C_DIEPMSK_XferComplMsk | - S3C_DIEPMSK_TimeOUTMsk) : 0, + (using_dma(hsotg) ? (S3C_DIEPMSK_XferComplMsk | + S3C_DIEPMSK_TimeOUTMsk) : 0), hsotg->regs + S3C_DOEPMSK); writel(0, hsotg->regs + S3C_DAINTMSK); diff --git a/drivers/usb/gadget/s3c2410_udc.c b/drivers/usb/gadget/s3c2410_udc.c index a9b452fe6221..d5f4c1d45c97 100644 --- a/drivers/usb/gadget/s3c2410_udc.c +++ b/drivers/usb/gadget/s3c2410_udc.c @@ -1703,8 +1703,7 @@ int usb_gadget_unregister_driver(struct usb_gadget_driver *driver) dprintk(DEBUG_NORMAL,"usb_gadget_register_driver() '%s'\n", driver->driver.name); - if (driver->disconnect) - driver->disconnect(&udc->gadget); + driver->unbind(&udc->gadget); device_del(&udc->gadget.dev); udc->driver = NULL; diff --git a/drivers/usb/gadget/u_audio.c b/drivers/usb/gadget/u_audio.c index 0f3d22fc030e..8252595d619d 100644 --- a/drivers/usb/gadget/u_audio.c +++ b/drivers/usb/gadget/u_audio.c @@ -10,7 +10,6 @@ */ #include <linux/kernel.h> -#include <linux/utsname.h> #include <linux/device.h> #include <linux/delay.h> #include <linux/ctype.h> @@ -253,11 +252,13 @@ static int gaudio_open_snd_dev(struct gaudio *card) snd->filp = filp_open(fn_cap, O_RDONLY, 0); if (IS_ERR(snd->filp)) { ERROR(card, "No such PCM capture device: %s\n", fn_cap); - snd->filp = NULL; + snd->substream = NULL; + snd->card = NULL; + } else { + pcm_file = snd->filp->private_data; + snd->substream = pcm_file->substream; + snd->card = card; } - pcm_file = snd->filp->private_data; - snd->substream = pcm_file->substream; - snd->card = card; return 0; } diff --git a/drivers/usb/gadget/u_ether.c b/drivers/usb/gadget/u_ether.c index c66521953917..2fc02bd95848 100644 --- a/drivers/usb/gadget/u_ether.c +++ b/drivers/usb/gadget/u_ether.c @@ -23,7 +23,6 @@ /* #define VERBOSE_DEBUG */ #include <linux/kernel.h> -#include <linux/utsname.h> #include <linux/device.h> #include <linux/ctype.h> #include <linux/etherdevice.h> @@ -37,8 +36,9 @@ * one (!) network link through the USB gadget stack, normally "usb0". * * The control and data models are handled by the function driver which - * connects to this code; such as CDC Ethernet, "CDC Subset", or RNDIS. - * That includes all descriptor and endpoint management. + * connects to this code; such as CDC Ethernet (ECM or EEM), + * "CDC Subset", or RNDIS. That includes all descriptor and endpoint + * management. * * Link level addressing is handled by this component using module * parameters; if no such parameters are provided, random link level @@ -68,9 +68,13 @@ struct eth_dev { struct list_head tx_reqs, rx_reqs; atomic_t tx_qlen; + struct sk_buff_head rx_frames; + unsigned header_len; - struct sk_buff *(*wrap)(struct sk_buff *skb); - int (*unwrap)(struct sk_buff *skb); + struct sk_buff *(*wrap)(struct gether *, struct sk_buff *skb); + int (*unwrap)(struct gether *, + struct sk_buff *skb, + struct sk_buff_head *list); struct work_struct work; @@ -269,7 +273,7 @@ enomem: static void rx_complete(struct usb_ep *ep, struct usb_request *req) { - struct sk_buff *skb = req->context; + struct sk_buff *skb = req->context, *skb2; struct eth_dev *dev = ep->driver_data; int status = req->status; @@ -278,26 +282,47 @@ static void rx_complete(struct usb_ep *ep, struct usb_request *req) /* normal completion */ case 0: skb_put(skb, req->actual); - if (dev->unwrap) - status = dev->unwrap(skb); - if (status < 0 - || ETH_HLEN > skb->len - || skb->len > ETH_FRAME_LEN) { - dev->net->stats.rx_errors++; - dev->net->stats.rx_length_errors++; - DBG(dev, "rx length %d\n", skb->len); - break; - } - skb->protocol = eth_type_trans(skb, dev->net); - dev->net->stats.rx_packets++; - dev->net->stats.rx_bytes += skb->len; + if (dev->unwrap) { + unsigned long flags; - /* no buffer copies needed, unless hardware can't - * use skb buffers. - */ - status = netif_rx(skb); + spin_lock_irqsave(&dev->lock, flags); + if (dev->port_usb) { + status = dev->unwrap(dev->port_usb, + skb, + &dev->rx_frames); + } else { + dev_kfree_skb_any(skb); + status = -ENOTCONN; + } + spin_unlock_irqrestore(&dev->lock, flags); + } else { + skb_queue_tail(&dev->rx_frames, skb); + } skb = NULL; + + skb2 = skb_dequeue(&dev->rx_frames); + while (skb2) { + if (status < 0 + || ETH_HLEN > skb2->len + || skb2->len > ETH_FRAME_LEN) { + dev->net->stats.rx_errors++; + dev->net->stats.rx_length_errors++; + DBG(dev, "rx length %d\n", skb2->len); + dev_kfree_skb_any(skb2); + goto next_frame; + } + skb2->protocol = eth_type_trans(skb2, dev->net); + dev->net->stats.rx_packets++; + dev->net->stats.rx_bytes += skb2->len; + + /* no buffer copies needed, unless hardware can't + * use skb buffers. + */ + status = netif_rx(skb2); +next_frame: + skb2 = skb_dequeue(&dev->rx_frames); + } break; /* software-driven interface shutdown */ @@ -537,14 +562,15 @@ static netdev_tx_t eth_start_xmit(struct sk_buff *skb, * or there's not enough space for extra headers we need */ if (dev->wrap) { - struct sk_buff *skb_new; + unsigned long flags; - skb_new = dev->wrap(skb); - if (!skb_new) + spin_lock_irqsave(&dev->lock, flags); + if (dev->port_usb) + skb = dev->wrap(dev->port_usb, skb); + spin_unlock_irqrestore(&dev->lock, flags); + if (!skb) goto drop; - dev_kfree_skb_any(skb); - skb = skb_new; length = skb->len; } req->buf = skb->data; @@ -578,9 +604,9 @@ static netdev_tx_t eth_start_xmit(struct sk_buff *skb, } if (retval) { + dev_kfree_skb_any(skb); drop: dev->net->stats.tx_dropped++; - dev_kfree_skb_any(skb); spin_lock_irqsave(&dev->req_lock, flags); if (list_empty(&dev->tx_reqs)) netif_start_queue(net); @@ -753,6 +779,8 @@ int __init gether_setup(struct usb_gadget *g, u8 ethaddr[ETH_ALEN]) INIT_LIST_HEAD(&dev->tx_reqs); INIT_LIST_HEAD(&dev->rx_reqs); + skb_queue_head_init(&dev->rx_frames); + /* network device setup */ dev->net = net; strcpy(net->name, "usb%d"); diff --git a/drivers/usb/gadget/u_ether.h b/drivers/usb/gadget/u_ether.h index 0d1f7ae3b071..91b39ffdf6ea 100644 --- a/drivers/usb/gadget/u_ether.h +++ b/drivers/usb/gadget/u_ether.h @@ -60,12 +60,13 @@ struct gether { u16 cdc_filter; - /* hooks for added framing, as needed for RNDIS and EEM. - * we currently don't support multiple frames per SKB. - */ + /* hooks for added framing, as needed for RNDIS and EEM. */ u32 header_len; - struct sk_buff *(*wrap)(struct sk_buff *skb); - int (*unwrap)(struct sk_buff *skb); + struct sk_buff *(*wrap)(struct gether *port, + struct sk_buff *skb); + int (*unwrap)(struct gether *port, + struct sk_buff *skb, + struct sk_buff_head *list); /* called on network open/close */ void (*open)(struct gether *); @@ -109,6 +110,7 @@ static inline bool can_support_ecm(struct usb_gadget *gadget) /* each configuration may bind one instance of an ethernet link */ int geth_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN]); int ecm_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN]); +int eem_bind_config(struct usb_configuration *c); #ifdef CONFIG_USB_ETH_RNDIS diff --git a/drivers/usb/gadget/u_serial.c b/drivers/usb/gadget/u_serial.c index fc6e709f45b1..adf8260c3a6a 100644 --- a/drivers/usb/gadget/u_serial.c +++ b/drivers/usb/gadget/u_serial.c @@ -1114,7 +1114,6 @@ int __init gserial_setup(struct usb_gadget *g, unsigned count) /* export the driver ... */ status = tty_register_driver(gs_tty_driver); if (status) { - put_tty_driver(gs_tty_driver); pr_err("%s: cannot register, err %d\n", __func__, status); goto fail; diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig index f21ca7d27a43..9b43b226817f 100644 --- a/drivers/usb/host/Kconfig +++ b/drivers/usb/host/Kconfig @@ -113,6 +113,12 @@ config USB_EHCI_HCD_PPC_OF Enables support for the USB controller present on the PowerPC OpenFirmware platform bus. +config USB_W90X900_EHCI + bool "W90X900(W90P910) EHCI support" + depends on USB_EHCI_HCD && ARCH_W90X900 + ---help--- + Enables support for the W90X900 USB controller + config USB_OXU210HP_HCD tristate "OXU210HP HCD support" depends on USB @@ -153,6 +159,18 @@ config USB_ISP1760_HCD To compile this driver as a module, choose M here: the module will be called isp1760. +config USB_ISP1362_HCD + tristate "ISP1362 HCD support" + depends on USB + default N + ---help--- + Supports the Philips ISP1362 chip as a host controller + + This driver does not support isochronous transfers. + + To compile this driver as a module, choose M here: the + module will be called isp1362-hcd. + config USB_OHCI_HCD tristate "OHCI HCD support" depends on USB && USB_ARCH_HAS_OHCI diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile index 289d748bb414..f58b2494c44a 100644 --- a/drivers/usb/host/Makefile +++ b/drivers/usb/host/Makefile @@ -21,6 +21,7 @@ obj-$(CONFIG_PCI) += pci-quirks.o obj-$(CONFIG_USB_EHCI_HCD) += ehci-hcd.o obj-$(CONFIG_USB_OXU210HP_HCD) += oxu210hp-hcd.o obj-$(CONFIG_USB_ISP116X_HCD) += isp116x-hcd.o +obj-$(CONFIG_USB_ISP1362_HCD) += isp1362-hcd.o obj-$(CONFIG_USB_OHCI_HCD) += ohci-hcd.o obj-$(CONFIG_USB_UHCI_HCD) += uhci-hcd.o obj-$(CONFIG_USB_FHCI_HCD) += fhci.o diff --git a/drivers/usb/host/ehci-atmel.c b/drivers/usb/host/ehci-atmel.c new file mode 100644 index 000000000000..87c1b7c34c0e --- /dev/null +++ b/drivers/usb/host/ehci-atmel.c @@ -0,0 +1,230 @@ +/* + * Driver for EHCI UHP on Atmel chips + * + * Copyright (C) 2009 Atmel Corporation, + * Nicolas Ferre <nicolas.ferre@atmel.com> + * + * Based on various ehci-*.c drivers + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file COPYING in the main directory of this archive for + * more details. + */ + +#include <linux/clk.h> +#include <linux/platform_device.h> + +/* interface and function clocks */ +static struct clk *iclk, *fclk; +static int clocked; + +/*-------------------------------------------------------------------------*/ + +static void atmel_start_clock(void) +{ + clk_enable(iclk); + clk_enable(fclk); + clocked = 1; +} + +static void atmel_stop_clock(void) +{ + clk_disable(fclk); + clk_disable(iclk); + clocked = 0; +} + +static void atmel_start_ehci(struct platform_device *pdev) +{ + dev_dbg(&pdev->dev, "start\n"); + atmel_start_clock(); +} + +static void atmel_stop_ehci(struct platform_device *pdev) +{ + dev_dbg(&pdev->dev, "stop\n"); + atmel_stop_clock(); +} + +/*-------------------------------------------------------------------------*/ + +static int ehci_atmel_setup(struct usb_hcd *hcd) +{ + struct ehci_hcd *ehci = hcd_to_ehci(hcd); + int retval = 0; + + /* registers start at offset 0x0 */ + ehci->caps = hcd->regs; + ehci->regs = hcd->regs + + HC_LENGTH(ehci_readl(ehci, &ehci->caps->hc_capbase)); + dbg_hcs_params(ehci, "reset"); + dbg_hcc_params(ehci, "reset"); + + /* cache this readonly data; minimize chip reads */ + ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params); + + retval = ehci_halt(ehci); + if (retval) + return retval; + + /* data structure init */ + retval = ehci_init(hcd); + if (retval) + return retval; + + ehci->sbrn = 0x20; + + ehci_reset(ehci); + ehci_port_power(ehci, 0); + + return retval; +} + +static const struct hc_driver ehci_atmel_hc_driver = { + .description = hcd_name, + .product_desc = "Atmel EHCI UHP HS", + .hcd_priv_size = sizeof(struct ehci_hcd), + + /* generic hardware linkage */ + .irq = ehci_irq, + .flags = HCD_MEMORY | HCD_USB2, + + /* basic lifecycle operations */ + .reset = ehci_atmel_setup, + .start = ehci_run, + .stop = ehci_stop, + .shutdown = ehci_shutdown, + + /* managing i/o requests and associated device resources */ + .urb_enqueue = ehci_urb_enqueue, + .urb_dequeue = ehci_urb_dequeue, + .endpoint_disable = ehci_endpoint_disable, + + /* scheduling support */ + .get_frame_number = ehci_get_frame, + + /* root hub support */ + .hub_status_data = ehci_hub_status_data, + .hub_control = ehci_hub_control, + .bus_suspend = ehci_bus_suspend, + .bus_resume = ehci_bus_resume, + .relinquish_port = ehci_relinquish_port, + .port_handed_over = ehci_port_handed_over, +}; + +static int __init ehci_atmel_drv_probe(struct platform_device *pdev) +{ + struct usb_hcd *hcd; + const struct hc_driver *driver = &ehci_atmel_hc_driver; + struct resource *res; + int irq; + int retval; + + if (usb_disabled()) + return -ENODEV; + + pr_debug("Initializing Atmel-SoC USB Host Controller\n"); + + irq = platform_get_irq(pdev, 0); + if (irq <= 0) { + dev_err(&pdev->dev, + "Found HC with no IRQ. Check %s setup!\n", + dev_name(&pdev->dev)); + retval = -ENODEV; + goto fail_create_hcd; + } + + hcd = usb_create_hcd(driver, &pdev->dev, dev_name(&pdev->dev)); + if (!hcd) { + retval = -ENOMEM; + goto fail_create_hcd; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, + "Found HC with no register addr. Check %s setup!\n", + dev_name(&pdev->dev)); + retval = -ENODEV; + goto fail_request_resource; + } + hcd->rsrc_start = res->start; + hcd->rsrc_len = res->end - res->start + 1; + + if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, + driver->description)) { + dev_dbg(&pdev->dev, "controller already in use\n"); + retval = -EBUSY; + goto fail_request_resource; + } + + hcd->regs = ioremap_nocache(hcd->rsrc_start, hcd->rsrc_len); + if (hcd->regs == NULL) { + dev_dbg(&pdev->dev, "error mapping memory\n"); + retval = -EFAULT; + goto fail_ioremap; + } + + iclk = clk_get(&pdev->dev, "ehci_clk"); + if (IS_ERR(iclk)) { + dev_err(&pdev->dev, "Error getting interface clock\n"); + retval = -ENOENT; + goto fail_get_iclk; + } + fclk = clk_get(&pdev->dev, "uhpck"); + if (IS_ERR(fclk)) { + dev_err(&pdev->dev, "Error getting function clock\n"); + retval = -ENOENT; + goto fail_get_fclk; + } + + atmel_start_ehci(pdev); + + retval = usb_add_hcd(hcd, irq, IRQF_SHARED); + if (retval) + goto fail_add_hcd; + + return retval; + +fail_add_hcd: + atmel_stop_ehci(pdev); + clk_put(fclk); +fail_get_fclk: + clk_put(iclk); +fail_get_iclk: + iounmap(hcd->regs); +fail_ioremap: + release_mem_region(hcd->rsrc_start, hcd->rsrc_len); +fail_request_resource: + usb_put_hcd(hcd); +fail_create_hcd: + dev_err(&pdev->dev, "init %s fail, %d\n", + dev_name(&pdev->dev), retval); + + return retval; +} + +static int __exit ehci_atmel_drv_remove(struct platform_device *pdev) +{ + struct usb_hcd *hcd = platform_get_drvdata(pdev); + + ehci_shutdown(hcd); + usb_remove_hcd(hcd); + iounmap(hcd->regs); + release_mem_region(hcd->rsrc_start, hcd->rsrc_len); + usb_put_hcd(hcd); + + atmel_stop_ehci(pdev); + clk_put(fclk); + clk_put(iclk); + fclk = iclk = NULL; + + return 0; +} + +static struct platform_driver ehci_atmel_driver = { + .probe = ehci_atmel_drv_probe, + .remove = __exit_p(ehci_atmel_drv_remove), + .shutdown = usb_hcd_platform_shutdown, + .driver.name = "atmel-ehci", +}; diff --git a/drivers/usb/host/ehci-au1xxx.c b/drivers/usb/host/ehci-au1xxx.c index 59d208d94d4e..ed77be76d6bb 100644 --- a/drivers/usb/host/ehci-au1xxx.c +++ b/drivers/usb/host/ehci-au1xxx.c @@ -199,10 +199,9 @@ static int ehci_hcd_au1xxx_drv_remove(struct platform_device *pdev) } #ifdef CONFIG_PM -static int ehci_hcd_au1xxx_drv_suspend(struct platform_device *pdev, - pm_message_t message) +static int ehci_hcd_au1xxx_drv_suspend(struct device *dev) { - struct usb_hcd *hcd = platform_get_drvdata(pdev); + struct usb_hcd *hcd = dev_get_drvdata(dev); struct ehci_hcd *ehci = hcd_to_ehci(hcd); unsigned long flags; int rc; @@ -229,12 +228,6 @@ static int ehci_hcd_au1xxx_drv_suspend(struct platform_device *pdev, ehci_writel(ehci, 0, &ehci->regs->intr_enable); (void)ehci_readl(ehci, &ehci->regs->intr_enable); - /* make sure snapshot being resumed re-enumerates everything */ - if (message.event == PM_EVENT_PRETHAW) { - ehci_halt(ehci); - ehci_reset(ehci); - } - clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); au1xxx_stop_ehc(); @@ -248,10 +241,9 @@ bail: return rc; } - -static int ehci_hcd_au1xxx_drv_resume(struct platform_device *pdev) +static int ehci_hcd_au1xxx_drv_resume(struct device *dev) { - struct usb_hcd *hcd = platform_get_drvdata(pdev); + struct usb_hcd *hcd = dev_get_drvdata(dev); struct ehci_hcd *ehci = hcd_to_ehci(hcd); au1xxx_start_ehc(); @@ -305,20 +297,25 @@ static int ehci_hcd_au1xxx_drv_resume(struct platform_device *pdev) return 0; } +static struct dev_pm_ops au1xxx_ehci_pmops = { + .suspend = ehci_hcd_au1xxx_drv_suspend, + .resume = ehci_hcd_au1xxx_drv_resume, +}; + +#define AU1XXX_EHCI_PMOPS &au1xxx_ehci_pmops + #else -#define ehci_hcd_au1xxx_drv_suspend NULL -#define ehci_hcd_au1xxx_drv_resume NULL +#define AU1XXX_EHCI_PMOPS NULL #endif static struct platform_driver ehci_hcd_au1xxx_driver = { .probe = ehci_hcd_au1xxx_drv_probe, .remove = ehci_hcd_au1xxx_drv_remove, .shutdown = usb_hcd_platform_shutdown, - .suspend = ehci_hcd_au1xxx_drv_suspend, - .resume = ehci_hcd_au1xxx_drv_resume, .driver = { .name = "au1xxx-ehci", .owner = THIS_MODULE, + .pm = AU1XXX_EHCI_PMOPS, } }; diff --git a/drivers/usb/host/ehci-dbg.c b/drivers/usb/host/ehci-dbg.c index 7f4ace73d44a..874d2000bf92 100644 --- a/drivers/usb/host/ehci-dbg.c +++ b/drivers/usb/host/ehci-dbg.c @@ -134,10 +134,11 @@ dbg_qtd (const char *label, struct ehci_hcd *ehci, struct ehci_qtd *qtd) static void __maybe_unused dbg_qh (const char *label, struct ehci_hcd *ehci, struct ehci_qh *qh) { + struct ehci_qh_hw *hw = qh->hw; + ehci_dbg (ehci, "%s qh %p n%08x info %x %x qtd %x\n", label, - qh, qh->hw_next, qh->hw_info1, qh->hw_info2, - qh->hw_current); - dbg_qtd ("overlay", ehci, (struct ehci_qtd *) &qh->hw_qtd_next); + qh, hw->hw_next, hw->hw_info1, hw->hw_info2, hw->hw_current); + dbg_qtd("overlay", ehci, (struct ehci_qtd *) &hw->hw_qtd_next); } static void __maybe_unused @@ -400,31 +401,32 @@ static void qh_lines ( char *next = *nextp; char mark; __le32 list_end = EHCI_LIST_END(ehci); + struct ehci_qh_hw *hw = qh->hw; - if (qh->hw_qtd_next == list_end) /* NEC does this */ + if (hw->hw_qtd_next == list_end) /* NEC does this */ mark = '@'; else - mark = token_mark(ehci, qh->hw_token); + mark = token_mark(ehci, hw->hw_token); if (mark == '/') { /* qh_alt_next controls qh advance? */ - if ((qh->hw_alt_next & QTD_MASK(ehci)) - == ehci->async->hw_alt_next) + if ((hw->hw_alt_next & QTD_MASK(ehci)) + == ehci->async->hw->hw_alt_next) mark = '#'; /* blocked */ - else if (qh->hw_alt_next == list_end) + else if (hw->hw_alt_next == list_end) mark = '.'; /* use hw_qtd_next */ /* else alt_next points to some other qtd */ } - scratch = hc32_to_cpup(ehci, &qh->hw_info1); - hw_curr = (mark == '*') ? hc32_to_cpup(ehci, &qh->hw_current) : 0; + scratch = hc32_to_cpup(ehci, &hw->hw_info1); + hw_curr = (mark == '*') ? hc32_to_cpup(ehci, &hw->hw_current) : 0; temp = scnprintf (next, size, "qh/%p dev%d %cs ep%d %08x %08x (%08x%c %s nak%d)", qh, scratch & 0x007f, speed_char (scratch), (scratch >> 8) & 0x000f, - scratch, hc32_to_cpup(ehci, &qh->hw_info2), - hc32_to_cpup(ehci, &qh->hw_token), mark, - (cpu_to_hc32(ehci, QTD_TOGGLE) & qh->hw_token) + scratch, hc32_to_cpup(ehci, &hw->hw_info2), + hc32_to_cpup(ehci, &hw->hw_token), mark, + (cpu_to_hc32(ehci, QTD_TOGGLE) & hw->hw_token) ? "data1" : "data0", - (hc32_to_cpup(ehci, &qh->hw_alt_next) >> 1) & 0x0f); + (hc32_to_cpup(ehci, &hw->hw_alt_next) >> 1) & 0x0f); size -= temp; next += temp; @@ -435,10 +437,10 @@ static void qh_lines ( mark = ' '; if (hw_curr == td->qtd_dma) mark = '*'; - else if (qh->hw_qtd_next == cpu_to_hc32(ehci, td->qtd_dma)) + else if (hw->hw_qtd_next == cpu_to_hc32(ehci, td->qtd_dma)) mark = '+'; else if (QTD_LENGTH (scratch)) { - if (td->hw_alt_next == ehci->async->hw_alt_next) + if (td->hw_alt_next == ehci->async->hw->hw_alt_next) mark = '#'; else if (td->hw_alt_next != list_end) mark = '/'; @@ -550,12 +552,15 @@ static ssize_t fill_periodic_buffer(struct debug_buffer *buf) next += temp; do { + struct ehci_qh_hw *hw; + switch (hc32_to_cpu(ehci, tag)) { case Q_TYPE_QH: + hw = p.qh->hw; temp = scnprintf (next, size, " qh%d-%04x/%p", p.qh->period, hc32_to_cpup(ehci, - &p.qh->hw_info2) + &hw->hw_info2) /* uframe masks */ & (QH_CMASK | QH_SMASK), p.qh); @@ -576,7 +581,7 @@ static ssize_t fill_periodic_buffer(struct debug_buffer *buf) /* show more info the first time around */ if (temp == seen_count) { u32 scratch = hc32_to_cpup(ehci, - &p.qh->hw_info1); + &hw->hw_info1); struct ehci_qtd *qtd; char *type = ""; @@ -609,7 +614,7 @@ static ssize_t fill_periodic_buffer(struct debug_buffer *buf) } else temp = 0; if (p.qh) { - tag = Q_NEXT_TYPE(ehci, p.qh->hw_next); + tag = Q_NEXT_TYPE(ehci, hw->hw_next); p = p.qh->qh_next; } break; @@ -879,8 +884,7 @@ static int debug_close(struct inode *inode, struct file *file) struct debug_buffer *buf = file->private_data; if (buf) { - if (buf->output_buf) - vfree(buf->output_buf); + vfree(buf->output_buf); kfree(buf); } diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index 11c627ce6022..9835e0713943 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -30,7 +30,6 @@ #include <linux/timer.h> #include <linux/list.h> #include <linux/interrupt.h> -#include <linux/reboot.h> #include <linux/usb.h> #include <linux/moduleparam.h> #include <linux/dma-mapping.h> @@ -127,6 +126,8 @@ timer_action(struct ehci_hcd *ehci, enum ehci_timer_action action) switch (action) { case TIMER_IO_WATCHDOG: + if (!ehci->need_io_watchdog) + return; t = EHCI_IO_JIFFIES; break; case TIMER_ASYNC_OFF: @@ -239,6 +240,11 @@ static int ehci_reset (struct ehci_hcd *ehci) int retval; u32 command = ehci_readl(ehci, &ehci->regs->command); + /* If the EHCI debug controller is active, special care must be + * taken before and after a host controller reset */ + if (ehci->debug && !dbgp_reset_prep()) + ehci->debug = NULL; + command |= CMD_RESET; dbg_cmd (ehci, "reset", command); ehci_writel(ehci, command, &ehci->regs->command); @@ -247,12 +253,21 @@ static int ehci_reset (struct ehci_hcd *ehci) retval = handshake (ehci, &ehci->regs->command, CMD_RESET, 0, 250 * 1000); + if (ehci->has_hostpc) { + ehci_writel(ehci, USBMODE_EX_HC | USBMODE_EX_VBPS, + (u32 __iomem *)(((u8 *)ehci->regs) + USBMODE_EX)); + ehci_writel(ehci, TXFIFO_DEFAULT, + (u32 __iomem *)(((u8 *)ehci->regs) + TXFILLTUNING)); + } if (retval) return retval; if (ehci_is_TDI(ehci)) tdi_reset (ehci); + if (ehci->debug) + dbgp_external_startup(); + return retval; } @@ -505,9 +520,14 @@ static int ehci_init(struct usb_hcd *hcd) u32 temp; int retval; u32 hcc_params; + struct ehci_qh_hw *hw; spin_lock_init(&ehci->lock); + /* + * keep io watchdog by default, those good HCDs could turn off it later + */ + ehci->need_io_watchdog = 1; init_timer(&ehci->watchdog); ehci->watchdog.function = ehci_watchdog; ehci->watchdog.data = (unsigned long) ehci; @@ -544,12 +564,13 @@ static int ehci_init(struct usb_hcd *hcd) * from automatically advancing to the next td after short reads. */ ehci->async->qh_next.qh = NULL; - ehci->async->hw_next = QH_NEXT(ehci, ehci->async->qh_dma); - ehci->async->hw_info1 = cpu_to_hc32(ehci, QH_HEAD); - ehci->async->hw_token = cpu_to_hc32(ehci, QTD_STS_HALT); - ehci->async->hw_qtd_next = EHCI_LIST_END(ehci); + hw = ehci->async->hw; + hw->hw_next = QH_NEXT(ehci, ehci->async->qh_dma); + hw->hw_info1 = cpu_to_hc32(ehci, QH_HEAD); + hw->hw_token = cpu_to_hc32(ehci, QTD_STS_HALT); + hw->hw_qtd_next = EHCI_LIST_END(ehci); ehci->async->qh_state = QH_STATE_LINKED; - ehci->async->hw_alt_next = QTD_NEXT(ehci, ehci->async->dummy->qtd_dma); + hw->hw_alt_next = QTD_NEXT(ehci, ehci->async->dummy->qtd_dma); /* clear interrupt enables, set irq latency */ if (log2_irq_thresh < 0 || log2_irq_thresh > 6) @@ -850,12 +871,18 @@ static void unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh) if (!HC_IS_RUNNING(ehci_to_hcd(ehci)->state) && ehci->reclaim) end_unlink_async(ehci); - /* if it's not linked then there's nothing to do */ - if (qh->qh_state != QH_STATE_LINKED) - ; + /* If the QH isn't linked then there's nothing we can do + * unless we were called during a giveback, in which case + * qh_completions() has to deal with it. + */ + if (qh->qh_state != QH_STATE_LINKED) { + if (qh->qh_state == QH_STATE_COMPLETING) + qh->needs_rescan = 1; + return; + } /* defer till later if busy */ - else if (ehci->reclaim) { + if (ehci->reclaim) { struct ehci_qh *last; for (last = ehci->reclaim; @@ -915,8 +942,9 @@ static int ehci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) break; switch (qh->qh_state) { case QH_STATE_LINKED: + case QH_STATE_COMPLETING: intr_deschedule (ehci, qh); - /* FALL THROUGH */ + break; case QH_STATE_IDLE: qh_completions (ehci, qh); break; @@ -925,23 +953,6 @@ static int ehci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) qh, qh->qh_state); goto done; } - - /* reschedule QH iff another request is queued */ - if (!list_empty (&qh->qtd_list) - && HC_IS_RUNNING (hcd->state)) { - rc = qh_schedule(ehci, qh); - - /* An error here likely indicates handshake failure - * or no space left in the schedule. Neither fault - * should happen often ... - * - * FIXME kill the now-dysfunctional queued urbs - */ - if (rc != 0) - ehci_err(ehci, - "can't reschedule qh %p, err %d", - qh, rc); - } break; case PIPE_ISOCHRONOUS: @@ -979,7 +990,7 @@ rescan: /* endpoints can be iso streams. for now, we don't * accelerate iso completions ... so spin a while. */ - if (qh->hw_info1 == 0) { + if (qh->hw->hw_info1 == 0) { ehci_vdbg (ehci, "iso delay\n"); goto idle_timeout; } @@ -988,6 +999,7 @@ rescan: qh->qh_state = QH_STATE_IDLE; switch (qh->qh_state) { case QH_STATE_LINKED: + case QH_STATE_COMPLETING: for (tmp = ehci->async->qh_next.qh; tmp && tmp != qh; tmp = tmp->qh_next.qh) @@ -1052,18 +1064,17 @@ ehci_endpoint_reset(struct usb_hcd *hcd, struct usb_host_endpoint *ep) usb_settoggle(qh->dev, epnum, is_out, 0); if (!list_empty(&qh->qtd_list)) { WARN_ONCE(1, "clear_halt for a busy endpoint\n"); - } else if (qh->qh_state == QH_STATE_LINKED) { + } else if (qh->qh_state == QH_STATE_LINKED || + qh->qh_state == QH_STATE_COMPLETING) { /* The toggle value in the QH can't be updated * while the QH is active. Unlink it now; * re-linking will call qh_refresh(). */ - if (eptype == USB_ENDPOINT_XFER_BULK) { + if (eptype == USB_ENDPOINT_XFER_BULK) unlink_async(ehci, qh); - } else { + else intr_deschedule(ehci, qh); - (void) qh_schedule(ehci, qh); - } } } spin_unlock_irqrestore(&ehci->lock, flags); @@ -1117,6 +1128,16 @@ MODULE_LICENSE ("GPL"); #define PLATFORM_DRIVER ixp4xx_ehci_driver #endif +#ifdef CONFIG_USB_W90X900_EHCI +#include "ehci-w90x900.c" +#define PLATFORM_DRIVER ehci_hcd_w90x900_driver +#endif + +#ifdef CONFIG_ARCH_AT91 +#include "ehci-atmel.c" +#define PLATFORM_DRIVER ehci_atmel_driver +#endif + #if !defined(PCI_DRIVER) && !defined(PLATFORM_DRIVER) && \ !defined(PS3_SYSTEM_BUS_DRIVER) && !defined(OF_PLATFORM_DRIVER) #error "missing bus glue for ehci-hcd" diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c index f46ad27c9a90..1b6f1c0e5cee 100644 --- a/drivers/usb/host/ehci-hub.c +++ b/drivers/usb/host/ehci-hub.c @@ -111,6 +111,7 @@ static int ehci_bus_suspend (struct usb_hcd *hcd) struct ehci_hcd *ehci = hcd_to_ehci (hcd); int port; int mask; + u32 __iomem *hostpc_reg = NULL; ehci_dbg(ehci, "suspend root hub\n"); @@ -142,6 +143,9 @@ static int ehci_bus_suspend (struct usb_hcd *hcd) u32 t1 = ehci_readl(ehci, reg) & ~PORT_RWC_BITS; u32 t2 = t1; + if (ehci->has_hostpc) + hostpc_reg = (u32 __iomem *)((u8 *)ehci->regs + + HOSTPC0 + 4 * (port & 0xff)); /* keep track of which ports we suspend */ if (t1 & PORT_OWNER) set_bit(port, &ehci->owned_ports); @@ -151,15 +155,37 @@ static int ehci_bus_suspend (struct usb_hcd *hcd) } /* enable remote wakeup on all ports */ - if (hcd->self.root_hub->do_remote_wakeup) - t2 |= PORT_WAKE_BITS; - else + if (hcd->self.root_hub->do_remote_wakeup) { + /* only enable appropriate wake bits, otherwise the + * hardware can not go phy low power mode. If a race + * condition happens here(connection change during bits + * set), the port change detection will finally fix it. + */ + if (t1 & PORT_CONNECT) { + t2 |= PORT_WKOC_E | PORT_WKDISC_E; + t2 &= ~PORT_WKCONN_E; + } else { + t2 |= PORT_WKOC_E | PORT_WKCONN_E; + t2 &= ~PORT_WKDISC_E; + } + } else t2 &= ~PORT_WAKE_BITS; if (t1 != t2) { ehci_vdbg (ehci, "port %d, %08x -> %08x\n", port + 1, t1, t2); ehci_writel(ehci, t2, reg); + if (hostpc_reg) { + u32 t3; + + msleep(5);/* 5ms for HCD enter low pwr mode */ + t3 = ehci_readl(ehci, hostpc_reg); + ehci_writel(ehci, t3 | HOSTPC_PHCD, hostpc_reg); + t3 = ehci_readl(ehci, hostpc_reg); + ehci_dbg(ehci, "Port%d phy low pwr mode %s\n", + port, (t3 & HOSTPC_PHCD) ? + "succeeded" : "failed"); + } } } @@ -183,6 +209,11 @@ static int ehci_bus_suspend (struct usb_hcd *hcd) ehci->next_statechange = jiffies + msecs_to_jiffies(10); spin_unlock_irq (&ehci->lock); + + /* ehci_work() may have re-enabled the watchdog timer, which we do not + * want, and so we must delete any pending watchdog timer events. + */ + del_timer_sync(&ehci->watchdog); return 0; } @@ -204,6 +235,13 @@ static int ehci_bus_resume (struct usb_hcd *hcd) return -ESHUTDOWN; } + if (unlikely(ehci->debug)) { + if (ehci->debug && !dbgp_reset_prep()) + ehci->debug = NULL; + else + dbgp_external_startup(); + } + /* Ideally and we've got a real resume here, and no port's power * was lost. (For PCI, that means Vaux was maintained.) But we * could instead be restoring a swsusp snapshot -- so that BIOS was @@ -563,7 +601,8 @@ static int ehci_hub_control ( int ports = HCS_N_PORTS (ehci->hcs_params); u32 __iomem *status_reg = &ehci->regs->port_status[ (wIndex & 0xff) - 1]; - u32 temp, status; + u32 __iomem *hostpc_reg = NULL; + u32 temp, temp1, status; unsigned long flags; int retval = 0; unsigned selector; @@ -575,6 +614,9 @@ static int ehci_hub_control ( * power, "this is the one", etc. EHCI spec supports this. */ + if (ehci->has_hostpc) + hostpc_reg = (u32 __iomem *)((u8 *)ehci->regs + + HOSTPC0 + 4 * ((wIndex & 0xff) - 1)); spin_lock_irqsave (&ehci->lock, flags); switch (typeReq) { case ClearHubFeature: @@ -773,7 +815,11 @@ static int ehci_hub_control ( if (temp & PORT_CONNECT) { status |= 1 << USB_PORT_FEAT_CONNECTION; // status may be from integrated TT - status |= ehci_port_speed(ehci, temp); + if (ehci->has_hostpc) { + temp1 = ehci_readl(ehci, hostpc_reg); + status |= ehci_port_speed(ehci, temp1); + } else + status |= ehci_port_speed(ehci, temp); } if (temp & PORT_PE) status |= 1 << USB_PORT_FEAT_ENABLE; @@ -816,6 +862,15 @@ static int ehci_hub_control ( case SetPortFeature: selector = wIndex >> 8; wIndex &= 0xff; + if (unlikely(ehci->debug)) { + /* If the debug port is active any port + * feature requests should get denied */ + if (wIndex == HCS_DEBUG_PORT(ehci->hcs_params) && + (readl(&ehci->debug->control) & DBGP_ENABLED)) { + retval = -ENODEV; + goto error_exit; + } + } if (!wIndex || wIndex > ports) goto error; wIndex--; @@ -832,6 +887,24 @@ static int ehci_hub_control ( || (temp & PORT_RESET) != 0) goto error; ehci_writel(ehci, temp | PORT_SUSPEND, status_reg); + /* After above check the port must be connected. + * Set appropriate bit thus could put phy into low power + * mode if we have hostpc feature + */ + if (hostpc_reg) { + temp &= ~PORT_WKCONN_E; + temp |= (PORT_WKDISC_E | PORT_WKOC_E); + ehci_writel(ehci, temp | PORT_SUSPEND, + status_reg); + msleep(5);/* 5ms for HCD enter low pwr mode */ + temp1 = ehci_readl(ehci, hostpc_reg); + ehci_writel(ehci, temp1 | HOSTPC_PHCD, + hostpc_reg); + temp1 = ehci_readl(ehci, hostpc_reg); + ehci_dbg(ehci, "Port%d phy low pwr mode %s\n", + wIndex, (temp1 & HOSTPC_PHCD) ? + "succeeded" : "failed"); + } set_bit(wIndex, &ehci->suspended_ports); break; case USB_PORT_FEAT_POWER: @@ -894,6 +967,7 @@ error: /* "stall" on error */ retval = -EPIPE; } +error_exit: spin_unlock_irqrestore (&ehci->lock, flags); return retval; } diff --git a/drivers/usb/host/ehci-mem.c b/drivers/usb/host/ehci-mem.c index 10d52919abbb..aeda96e0af67 100644 --- a/drivers/usb/host/ehci-mem.c +++ b/drivers/usb/host/ehci-mem.c @@ -75,7 +75,8 @@ static void qh_destroy(struct ehci_qh *qh) } if (qh->dummy) ehci_qtd_free (ehci, qh->dummy); - dma_pool_free (ehci->qh_pool, qh, qh->qh_dma); + dma_pool_free(ehci->qh_pool, qh->hw, qh->qh_dma); + kfree(qh); } static struct ehci_qh *ehci_qh_alloc (struct ehci_hcd *ehci, gfp_t flags) @@ -83,12 +84,14 @@ static struct ehci_qh *ehci_qh_alloc (struct ehci_hcd *ehci, gfp_t flags) struct ehci_qh *qh; dma_addr_t dma; - qh = (struct ehci_qh *) - dma_pool_alloc (ehci->qh_pool, flags, &dma); + qh = kzalloc(sizeof *qh, GFP_ATOMIC); if (!qh) - return qh; - - memset (qh, 0, sizeof *qh); + goto done; + qh->hw = (struct ehci_qh_hw *) + dma_pool_alloc(ehci->qh_pool, flags, &dma); + if (!qh->hw) + goto fail; + memset(qh->hw, 0, sizeof *qh->hw); qh->refcount = 1; qh->ehci = ehci; qh->qh_dma = dma; @@ -99,10 +102,15 @@ static struct ehci_qh *ehci_qh_alloc (struct ehci_hcd *ehci, gfp_t flags) qh->dummy = ehci_qtd_alloc (ehci, flags); if (qh->dummy == NULL) { ehci_dbg (ehci, "no dummy td\n"); - dma_pool_free (ehci->qh_pool, qh, qh->qh_dma); - qh = NULL; + goto fail1; } +done: return qh; +fail1: + dma_pool_free(ehci->qh_pool, qh->hw, qh->qh_dma); +fail: + kfree(qh); + return NULL; } /* to share a qh (cpu threads, or hc) */ @@ -180,7 +188,7 @@ static int ehci_mem_init (struct ehci_hcd *ehci, gfp_t flags) /* QHs for control/bulk/intr transfers */ ehci->qh_pool = dma_pool_create ("ehci_qh", ehci_to_hcd(ehci)->self.controller, - sizeof (struct ehci_qh), + sizeof(struct ehci_qh_hw), 32 /* byte alignment (for hw parts) */, 4096 /* can't cross 4K */); if (!ehci->qh_pool) { diff --git a/drivers/usb/host/ehci-pci.c b/drivers/usb/host/ehci-pci.c index b5b83c43898a..378861b9d79a 100644 --- a/drivers/usb/host/ehci-pci.c +++ b/drivers/usb/host/ehci-pci.c @@ -27,28 +27,8 @@ /* called after powerup, by probe or system-pm "wakeup" */ static int ehci_pci_reinit(struct ehci_hcd *ehci, struct pci_dev *pdev) { - u32 temp; int retval; - /* optional debug port, normally in the first BAR */ - temp = pci_find_capability(pdev, 0x0a); - if (temp) { - pci_read_config_dword(pdev, temp, &temp); - temp >>= 16; - if ((temp & (3 << 13)) == (1 << 13)) { - temp &= 0x1fff; - ehci->debug = ehci_to_hcd(ehci)->regs + temp; - temp = ehci_readl(ehci, &ehci->debug->control); - ehci_info(ehci, "debug port %d%s\n", - HCS_DEBUG_PORT(ehci->hcs_params), - (temp & DBGP_ENABLED) - ? " IN USE" - : ""); - if (!(temp & DBGP_ENABLED)) - ehci->debug = NULL; - } - } - /* we expect static quirk code to handle the "extended capabilities" * (currently just BIOS handoff) allowed starting with EHCI 0.96 */ @@ -129,6 +109,9 @@ static int ehci_pci_setup(struct usb_hcd *hcd) return retval; switch (pdev->vendor) { + case PCI_VENDOR_ID_INTEL: + ehci->need_io_watchdog = 0; + break; case PCI_VENDOR_ID_TDI: if (pdev->device == PCI_DEVICE_ID_TDI_EHCI) { hcd->has_tt = 1; @@ -192,6 +175,25 @@ static int ehci_pci_setup(struct usb_hcd *hcd) break; } + /* optional debug port, normally in the first BAR */ + temp = pci_find_capability(pdev, 0x0a); + if (temp) { + pci_read_config_dword(pdev, temp, &temp); + temp >>= 16; + if ((temp & (3 << 13)) == (1 << 13)) { + temp &= 0x1fff; + ehci->debug = ehci_to_hcd(ehci)->regs + temp; + temp = ehci_readl(ehci, &ehci->debug->control); + ehci_info(ehci, "debug port %d%s\n", + HCS_DEBUG_PORT(ehci->hcs_params), + (temp & DBGP_ENABLED) + ? " IN USE" + : ""); + if (!(temp & DBGP_ENABLED)) + ehci->debug = NULL; + } + } + ehci_reset(ehci); /* at least the Genesys GL880S needs fixup here */ diff --git a/drivers/usb/host/ehci-q.c b/drivers/usb/host/ehci-q.c index 7673554fa64d..00ad9ce392ed 100644 --- a/drivers/usb/host/ehci-q.c +++ b/drivers/usb/host/ehci-q.c @@ -87,31 +87,33 @@ qtd_fill(struct ehci_hcd *ehci, struct ehci_qtd *qtd, dma_addr_t buf, static inline void qh_update (struct ehci_hcd *ehci, struct ehci_qh *qh, struct ehci_qtd *qtd) { + struct ehci_qh_hw *hw = qh->hw; + /* writes to an active overlay are unsafe */ BUG_ON(qh->qh_state != QH_STATE_IDLE); - qh->hw_qtd_next = QTD_NEXT(ehci, qtd->qtd_dma); - qh->hw_alt_next = EHCI_LIST_END(ehci); + hw->hw_qtd_next = QTD_NEXT(ehci, qtd->qtd_dma); + hw->hw_alt_next = EHCI_LIST_END(ehci); /* Except for control endpoints, we make hardware maintain data * toggle (like OHCI) ... here (re)initialize the toggle in the QH, * and set the pseudo-toggle in udev. Only usb_clear_halt() will * ever clear it. */ - if (!(qh->hw_info1 & cpu_to_hc32(ehci, 1 << 14))) { + if (!(hw->hw_info1 & cpu_to_hc32(ehci, 1 << 14))) { unsigned is_out, epnum; is_out = !(qtd->hw_token & cpu_to_hc32(ehci, 1 << 8)); - epnum = (hc32_to_cpup(ehci, &qh->hw_info1) >> 8) & 0x0f; + epnum = (hc32_to_cpup(ehci, &hw->hw_info1) >> 8) & 0x0f; if (unlikely (!usb_gettoggle (qh->dev, epnum, is_out))) { - qh->hw_token &= ~cpu_to_hc32(ehci, QTD_TOGGLE); + hw->hw_token &= ~cpu_to_hc32(ehci, QTD_TOGGLE); usb_settoggle (qh->dev, epnum, is_out, 1); } } /* HC must see latest qtd and qh data before we clear ACTIVE+HALT */ wmb (); - qh->hw_token &= cpu_to_hc32(ehci, QTD_TOGGLE | QTD_STS_PING); + hw->hw_token &= cpu_to_hc32(ehci, QTD_TOGGLE | QTD_STS_PING); } /* if it weren't for a common silicon quirk (writing the dummy into the qh @@ -129,7 +131,7 @@ qh_refresh (struct ehci_hcd *ehci, struct ehci_qh *qh) qtd = list_entry (qh->qtd_list.next, struct ehci_qtd, qtd_list); /* first qtd may already be partially processed */ - if (cpu_to_hc32(ehci, qtd->qtd_dma) == qh->hw_current) + if (cpu_to_hc32(ehci, qtd->qtd_dma) == qh->hw->hw_current) qtd = NULL; } @@ -260,7 +262,7 @@ __acquires(ehci->lock) struct ehci_qh *qh = (struct ehci_qh *) urb->hcpriv; /* S-mask in a QH means it's an interrupt urb */ - if ((qh->hw_info2 & cpu_to_hc32(ehci, QH_SMASK)) != 0) { + if ((qh->hw->hw_info2 & cpu_to_hc32(ehci, QH_SMASK)) != 0) { /* ... update hc-wide periodic stats (for usbfs) */ ehci_to_hcd(ehci)->self.bandwidth_int_reqs--; @@ -297,7 +299,6 @@ __acquires(ehci->lock) static void start_unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh); static void unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh); -static void intr_deschedule (struct ehci_hcd *ehci, struct ehci_qh *qh); static int qh_schedule (struct ehci_hcd *ehci, struct ehci_qh *qh); /* @@ -308,13 +309,14 @@ static int qh_schedule (struct ehci_hcd *ehci, struct ehci_qh *qh); static unsigned qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh) { - struct ehci_qtd *last = NULL, *end = qh->dummy; + struct ehci_qtd *last, *end = qh->dummy; struct list_head *entry, *tmp; - int last_status = -EINPROGRESS; + int last_status; int stopped; unsigned count = 0; u8 state; - __le32 halt = HALT_BIT(ehci); + const __le32 halt = HALT_BIT(ehci); + struct ehci_qh_hw *hw = qh->hw; if (unlikely (list_empty (&qh->qtd_list))) return count; @@ -324,11 +326,20 @@ qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh) * they add urbs to this qh's queue or mark them for unlinking. * * NOTE: unlinking expects to be done in queue order. + * + * It's a bug for qh->qh_state to be anything other than + * QH_STATE_IDLE, unless our caller is scan_async() or + * scan_periodic(). */ state = qh->qh_state; qh->qh_state = QH_STATE_COMPLETING; stopped = (state == QH_STATE_IDLE); + rescan: + last = NULL; + last_status = -EINPROGRESS; + qh->needs_rescan = 0; + /* remove de-activated QTDs from front of queue. * after faults (including short reads), cleanup this urb * then let the queue advance. @@ -392,7 +403,8 @@ qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh) qtd->hw_token = cpu_to_hc32(ehci, token); wmb(); - qh->hw_token = cpu_to_hc32(ehci, token); + hw->hw_token = cpu_to_hc32(ehci, + token); goto retry_xacterr; } stopped = 1; @@ -435,8 +447,8 @@ qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh) /* qh unlinked; token in overlay may be most current */ if (state == QH_STATE_IDLE && cpu_to_hc32(ehci, qtd->qtd_dma) - == qh->hw_current) { - token = hc32_to_cpu(ehci, qh->hw_token); + == hw->hw_current) { + token = hc32_to_cpu(ehci, hw->hw_token); /* An unlink may leave an incomplete * async transaction in the TT buffer. @@ -449,9 +461,9 @@ qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh) * patch the qh later and so that completions can't * activate it while we "know" it's stopped. */ - if ((halt & qh->hw_token) == 0) { + if ((halt & hw->hw_token) == 0) { halt: - qh->hw_token |= halt; + hw->hw_token |= halt; wmb (); } } @@ -503,6 +515,21 @@ halt: ehci_qtd_free (ehci, last); } + /* Do we need to rescan for URBs dequeued during a giveback? */ + if (unlikely(qh->needs_rescan)) { + /* If the QH is already unlinked, do the rescan now. */ + if (state == QH_STATE_IDLE) + goto rescan; + + /* Otherwise we have to wait until the QH is fully unlinked. + * Our caller will start an unlink if qh->needs_rescan is + * set. But if an unlink has already started, nothing needs + * to be done. + */ + if (state != QH_STATE_LINKED) + qh->needs_rescan = 0; + } + /* restore original state; caller must unlink or relink */ qh->qh_state = state; @@ -510,7 +537,7 @@ halt: * it after fault cleanup, or recovering from silicon wrongly * overlaying the dummy qtd (which reduces DMA chatter). */ - if (stopped != 0 || qh->hw_qtd_next == EHCI_LIST_END(ehci)) { + if (stopped != 0 || hw->hw_qtd_next == EHCI_LIST_END(ehci)) { switch (state) { case QH_STATE_IDLE: qh_refresh(ehci, qh); @@ -527,12 +554,9 @@ halt: * That should be rare for interrupt transfers, * except maybe high bandwidth ... */ - if ((cpu_to_hc32(ehci, QH_SMASK) - & qh->hw_info2) != 0) { - intr_deschedule (ehci, qh); - (void) qh_schedule (ehci, qh); - } else - unlink_async (ehci, qh); + + /* Tell the caller to start an unlink */ + qh->needs_rescan = 1; break; /* otherwise, unlink already started */ } @@ -649,7 +673,7 @@ qh_urb_transaction ( * (this will usually be overridden later.) */ if (is_input) - qtd->hw_alt_next = ehci->async->hw_alt_next; + qtd->hw_alt_next = ehci->async->hw->hw_alt_next; /* qh makes control packets use qtd toggle; maybe switch it */ if ((maxpacket & (this_qtd_len + (maxpacket - 1))) == 0) @@ -744,6 +768,7 @@ qh_make ( int is_input, type; int maxp = 0; struct usb_tt *tt = urb->dev->tt; + struct ehci_qh_hw *hw; if (!qh) return qh; @@ -890,8 +915,9 @@ done: /* init as live, toggle clear, advance to dummy */ qh->qh_state = QH_STATE_IDLE; - qh->hw_info1 = cpu_to_hc32(ehci, info1); - qh->hw_info2 = cpu_to_hc32(ehci, info2); + hw = qh->hw; + hw->hw_info1 = cpu_to_hc32(ehci, info1); + hw->hw_info2 = cpu_to_hc32(ehci, info2); usb_settoggle (urb->dev, usb_pipeendpoint (urb->pipe), !is_input, 1); qh_refresh (ehci, qh); return qh; @@ -910,6 +936,8 @@ static void qh_link_async (struct ehci_hcd *ehci, struct ehci_qh *qh) if (unlikely(qh->clearing_tt)) return; + WARN_ON(qh->qh_state != QH_STATE_IDLE); + /* (re)start the async schedule? */ head = ehci->async; timer_action_done (ehci, TIMER_ASYNC_OFF); @@ -928,16 +956,15 @@ static void qh_link_async (struct ehci_hcd *ehci, struct ehci_qh *qh) } /* clear halt and/or toggle; and maybe recover from silicon quirk */ - if (qh->qh_state == QH_STATE_IDLE) - qh_refresh (ehci, qh); + qh_refresh(ehci, qh); /* splice right after start */ qh->qh_next = head->qh_next; - qh->hw_next = head->hw_next; + qh->hw->hw_next = head->hw->hw_next; wmb (); head->qh_next.qh = qh; - head->hw_next = dma; + head->hw->hw_next = dma; qh_get(qh); qh->xacterrs = 0; @@ -984,7 +1011,7 @@ static struct ehci_qh *qh_append_tds ( /* usb_reset_device() briefly reverts to address 0 */ if (usb_pipedevice (urb->pipe) == 0) - qh->hw_info1 &= ~qh_addr_mask; + qh->hw->hw_info1 &= ~qh_addr_mask; } /* just one way to queue requests: swap with the dummy qtd. @@ -1169,7 +1196,7 @@ static void start_unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh) while (prev->qh_next.qh != qh) prev = prev->qh_next.qh; - prev->hw_next = qh->hw_next; + prev->hw->hw_next = qh->hw->hw_next; prev->qh_next = qh->qh_next; wmb (); @@ -1214,6 +1241,8 @@ rescan: qh = qh_get (qh); qh->stamp = ehci->stamp; temp = qh_completions (ehci, qh); + if (qh->needs_rescan) + unlink_async(ehci, qh); qh_put (qh); if (temp != 0) { goto rescan; diff --git a/drivers/usb/host/ehci-sched.c b/drivers/usb/host/ehci-sched.c index edd61ee90323..b25cdea93a1f 100644 --- a/drivers/usb/host/ehci-sched.c +++ b/drivers/usb/host/ehci-sched.c @@ -60,6 +60,20 @@ periodic_next_shadow(struct ehci_hcd *ehci, union ehci_shadow *periodic, } } +static __hc32 * +shadow_next_periodic(struct ehci_hcd *ehci, union ehci_shadow *periodic, + __hc32 tag) +{ + switch (hc32_to_cpu(ehci, tag)) { + /* our ehci_shadow.qh is actually software part */ + case Q_TYPE_QH: + return &periodic->qh->hw->hw_next; + /* others are hw parts */ + default: + return periodic->hw_next; + } +} + /* caller must hold ehci->lock */ static void periodic_unlink (struct ehci_hcd *ehci, unsigned frame, void *ptr) { @@ -71,7 +85,8 @@ static void periodic_unlink (struct ehci_hcd *ehci, unsigned frame, void *ptr) while (here.ptr && here.ptr != ptr) { prev_p = periodic_next_shadow(ehci, prev_p, Q_NEXT_TYPE(ehci, *hw_p)); - hw_p = here.hw_next; + hw_p = shadow_next_periodic(ehci, &here, + Q_NEXT_TYPE(ehci, *hw_p)); here = *prev_p; } /* an interrupt entry (at list end) could have been shared */ @@ -83,7 +98,7 @@ static void periodic_unlink (struct ehci_hcd *ehci, unsigned frame, void *ptr) */ *prev_p = *periodic_next_shadow(ehci, &here, Q_NEXT_TYPE(ehci, *hw_p)); - *hw_p = *here.hw_next; + *hw_p = *shadow_next_periodic(ehci, &here, Q_NEXT_TYPE(ehci, *hw_p)); } /* how many of the uframe's 125 usecs are allocated? */ @@ -93,18 +108,20 @@ periodic_usecs (struct ehci_hcd *ehci, unsigned frame, unsigned uframe) __hc32 *hw_p = &ehci->periodic [frame]; union ehci_shadow *q = &ehci->pshadow [frame]; unsigned usecs = 0; + struct ehci_qh_hw *hw; while (q->ptr) { switch (hc32_to_cpu(ehci, Q_NEXT_TYPE(ehci, *hw_p))) { case Q_TYPE_QH: + hw = q->qh->hw; /* is it in the S-mask? */ - if (q->qh->hw_info2 & cpu_to_hc32(ehci, 1 << uframe)) + if (hw->hw_info2 & cpu_to_hc32(ehci, 1 << uframe)) usecs += q->qh->usecs; /* ... or C-mask? */ - if (q->qh->hw_info2 & cpu_to_hc32(ehci, + if (hw->hw_info2 & cpu_to_hc32(ehci, 1 << (8 + uframe))) usecs += q->qh->c_usecs; - hw_p = &q->qh->hw_next; + hw_p = &hw->hw_next; q = &q->qh->qh_next; break; // case Q_TYPE_FSTN: @@ -237,10 +254,10 @@ periodic_tt_usecs ( continue; case Q_TYPE_QH: if (same_tt(dev, q->qh->dev)) { - uf = tt_start_uframe(ehci, q->qh->hw_info2); + uf = tt_start_uframe(ehci, q->qh->hw->hw_info2); tt_usecs[uf] += q->qh->tt_usecs; } - hw_p = &q->qh->hw_next; + hw_p = &q->qh->hw->hw_next; q = &q->qh->qh_next; continue; case Q_TYPE_SITD: @@ -375,6 +392,7 @@ static int tt_no_collision ( for (; frame < ehci->periodic_size; frame += period) { union ehci_shadow here; __hc32 type; + struct ehci_qh_hw *hw; here = ehci->pshadow [frame]; type = Q_NEXT_TYPE(ehci, ehci->periodic [frame]); @@ -385,17 +403,18 @@ static int tt_no_collision ( here = here.itd->itd_next; continue; case Q_TYPE_QH: + hw = here.qh->hw; if (same_tt (dev, here.qh->dev)) { u32 mask; mask = hc32_to_cpu(ehci, - here.qh->hw_info2); + hw->hw_info2); /* "knows" no gap is needed */ mask |= mask >> 8; if (mask & uf_mask) break; } - type = Q_NEXT_TYPE(ehci, here.qh->hw_next); + type = Q_NEXT_TYPE(ehci, hw->hw_next); here = here.qh->qh_next; continue; case Q_TYPE_SITD: @@ -498,7 +517,8 @@ static int qh_link_periodic (struct ehci_hcd *ehci, struct ehci_qh *qh) dev_dbg (&qh->dev->dev, "link qh%d-%04x/%p start %d [%d/%d us]\n", - period, hc32_to_cpup(ehci, &qh->hw_info2) & (QH_CMASK | QH_SMASK), + period, hc32_to_cpup(ehci, &qh->hw->hw_info2) + & (QH_CMASK | QH_SMASK), qh, qh->start, qh->usecs, qh->c_usecs); /* high bandwidth, or otherwise every microframe */ @@ -517,7 +537,7 @@ static int qh_link_periodic (struct ehci_hcd *ehci, struct ehci_qh *qh) if (type == cpu_to_hc32(ehci, Q_TYPE_QH)) break; prev = periodic_next_shadow(ehci, prev, type); - hw_p = &here.qh->hw_next; + hw_p = shadow_next_periodic(ehci, &here, type); here = *prev; } @@ -528,14 +548,14 @@ static int qh_link_periodic (struct ehci_hcd *ehci, struct ehci_qh *qh) if (qh->period > here.qh->period) break; prev = &here.qh->qh_next; - hw_p = &here.qh->hw_next; + hw_p = &here.qh->hw->hw_next; here = *prev; } /* link in this qh, unless some earlier pass did that */ if (qh != here.qh) { qh->qh_next = here; if (here.qh) - qh->hw_next = *hw_p; + qh->hw->hw_next = *hw_p; wmb (); prev->qh = qh; *hw_p = QH_NEXT (ehci, qh->qh_dma); @@ -581,7 +601,7 @@ static int qh_unlink_periodic(struct ehci_hcd *ehci, struct ehci_qh *qh) dev_dbg (&qh->dev->dev, "unlink qh%d-%04x/%p start %d [%d/%d us]\n", qh->period, - hc32_to_cpup(ehci, &qh->hw_info2) & (QH_CMASK | QH_SMASK), + hc32_to_cpup(ehci, &qh->hw->hw_info2) & (QH_CMASK | QH_SMASK), qh, qh->start, qh->usecs, qh->c_usecs); /* qh->qh_next still "live" to HC */ @@ -595,7 +615,19 @@ static int qh_unlink_periodic(struct ehci_hcd *ehci, struct ehci_qh *qh) static void intr_deschedule (struct ehci_hcd *ehci, struct ehci_qh *qh) { - unsigned wait; + unsigned wait; + struct ehci_qh_hw *hw = qh->hw; + int rc; + + /* If the QH isn't linked then there's nothing we can do + * unless we were called during a giveback, in which case + * qh_completions() has to deal with it. + */ + if (qh->qh_state != QH_STATE_LINKED) { + if (qh->qh_state == QH_STATE_COMPLETING) + qh->needs_rescan = 1; + return; + } qh_unlink_periodic (ehci, qh); @@ -606,15 +638,33 @@ static void intr_deschedule (struct ehci_hcd *ehci, struct ehci_qh *qh) */ if (list_empty (&qh->qtd_list) || (cpu_to_hc32(ehci, QH_CMASK) - & qh->hw_info2) != 0) + & hw->hw_info2) != 0) wait = 2; else wait = 55; /* worst case: 3 * 1024 */ udelay (wait); qh->qh_state = QH_STATE_IDLE; - qh->hw_next = EHCI_LIST_END(ehci); + hw->hw_next = EHCI_LIST_END(ehci); wmb (); + + qh_completions(ehci, qh); + + /* reschedule QH iff another request is queued */ + if (!list_empty(&qh->qtd_list) && + HC_IS_RUNNING(ehci_to_hcd(ehci)->state)) { + rc = qh_schedule(ehci, qh); + + /* An error here likely indicates handshake failure + * or no space left in the schedule. Neither fault + * should happen often ... + * + * FIXME kill the now-dysfunctional queued urbs + */ + if (rc != 0) + ehci_err(ehci, "can't reschedule qh %p, err %d\n", + qh, rc); + } } /*-------------------------------------------------------------------------*/ @@ -739,14 +789,15 @@ static int qh_schedule(struct ehci_hcd *ehci, struct ehci_qh *qh) unsigned uframe; __hc32 c_mask; unsigned frame; /* 0..(qh->period - 1), or NO_FRAME */ + struct ehci_qh_hw *hw = qh->hw; qh_refresh(ehci, qh); - qh->hw_next = EHCI_LIST_END(ehci); + hw->hw_next = EHCI_LIST_END(ehci); frame = qh->start; /* reuse the previous schedule slots, if we can */ if (frame < qh->period) { - uframe = ffs(hc32_to_cpup(ehci, &qh->hw_info2) & QH_SMASK); + uframe = ffs(hc32_to_cpup(ehci, &hw->hw_info2) & QH_SMASK); status = check_intr_schedule (ehci, frame, --uframe, qh, &c_mask); } else { @@ -784,11 +835,11 @@ static int qh_schedule(struct ehci_hcd *ehci, struct ehci_qh *qh) qh->start = frame; /* reset S-frame and (maybe) C-frame masks */ - qh->hw_info2 &= cpu_to_hc32(ehci, ~(QH_CMASK | QH_SMASK)); - qh->hw_info2 |= qh->period + hw->hw_info2 &= cpu_to_hc32(ehci, ~(QH_CMASK | QH_SMASK)); + hw->hw_info2 |= qh->period ? cpu_to_hc32(ehci, 1 << uframe) : cpu_to_hc32(ehci, QH_SMASK); - qh->hw_info2 |= c_mask; + hw->hw_info2 |= c_mask; } else ehci_dbg (ehci, "reused qh %p schedule\n", qh); @@ -1349,6 +1400,10 @@ iso_stream_schedule ( goto fail; } + period = urb->interval; + if (!stream->highspeed) + period <<= 3; + now = ehci_readl(ehci, &ehci->regs->frame_index) % mod; /* when's the last uframe this urb could start? */ @@ -1366,14 +1421,15 @@ iso_stream_schedule ( /* Fell behind (by up to twice the slop amount)? */ if (start >= max - 2 * 8 * SCHEDULE_SLOP) - start += stream->interval * DIV_ROUND_UP( - max - start, stream->interval) - mod; + start += period * DIV_ROUND_UP( + max - start, period) - mod; /* Tried to schedule too far into the future? */ if (unlikely((start + sched->span) >= max)) { status = -EFBIG; goto fail; } + stream->next_uframe = start; goto ready; } @@ -1389,10 +1445,6 @@ iso_stream_schedule ( /* NOTE: assumes URB_ISO_ASAP, to limit complexity/bugs */ - period = urb->interval; - if (!stream->highspeed) - period <<= 3; - /* find a uframe slot with enough bandwidth */ for (; start < (stream->next_uframe + period); start++) { int enough_space; @@ -2188,10 +2240,11 @@ restart: case Q_TYPE_QH: /* handle any completions */ temp.qh = qh_get (q.qh); - type = Q_NEXT_TYPE(ehci, q.qh->hw_next); + type = Q_NEXT_TYPE(ehci, q.qh->hw->hw_next); q = q.qh->qh_next; modified = qh_completions (ehci, temp.qh); - if (unlikely (list_empty (&temp.qh->qtd_list))) + if (unlikely(list_empty(&temp.qh->qtd_list) || + temp.qh->needs_rescan)) intr_deschedule (ehci, temp.qh); qh_put (temp.qh); break; diff --git a/drivers/usb/host/ehci-w90x900.c b/drivers/usb/host/ehci-w90x900.c new file mode 100644 index 000000000000..cfa21ea20f82 --- /dev/null +++ b/drivers/usb/host/ehci-w90x900.c @@ -0,0 +1,181 @@ +/* + * linux/driver/usb/host/ehci-w90x900.c + * + * Copyright (c) 2008 Nuvoton technology corporation. + * + * Wan ZongShun <mcuos.com@gmail.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation;version 2 of the License. + * + */ + +#include <linux/platform_device.h> + +/*ebable phy0 and phy1 for w90p910*/ +#define ENPHY (0x01<<8) +#define PHY0_CTR (0xA4) +#define PHY1_CTR (0xA8) + +static int __devinit usb_w90x900_probe(const struct hc_driver *driver, + struct platform_device *pdev) +{ + struct usb_hcd *hcd; + struct ehci_hcd *ehci; + struct resource *res; + int retval = 0, irq; + unsigned long val; + + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + retval = -ENXIO; + goto err1; + } + + hcd = usb_create_hcd(driver, &pdev->dev, "w90x900 EHCI"); + if (!hcd) { + retval = -ENOMEM; + goto err1; + } + + hcd->rsrc_start = res->start; + hcd->rsrc_len = res->end - res->start + 1; + + if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) { + retval = -EBUSY; + goto err2; + } + + hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len); + if (hcd->regs == NULL) { + retval = -EFAULT; + goto err3; + } + + ehci = hcd_to_ehci(hcd); + ehci->caps = hcd->regs; + ehci->regs = hcd->regs + + HC_LENGTH(ehci_readl(ehci, &ehci->caps->hc_capbase)); + + /* enable PHY 0,1,the regs only apply to w90p910 + * 0xA4,0xA8 were offsets of PHY0 and PHY1 controller of + * w90p910 IC relative to ehci->regs. + */ + val = __raw_readl(ehci->regs+PHY0_CTR); + val |= ENPHY; + __raw_writel(val, ehci->regs+PHY0_CTR); + + val = __raw_readl(ehci->regs+PHY1_CTR); + val |= ENPHY; + __raw_writel(val, ehci->regs+PHY1_CTR); + + ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params); + ehci->sbrn = 0x20; + + irq = platform_get_irq(pdev, 0); + if (irq < 0) + goto err4; + + retval = usb_add_hcd(hcd, irq, IRQF_SHARED); + if (retval != 0) + goto err4; + + ehci_writel(ehci, 1, &ehci->regs->configured_flag); + + return retval; +err4: + iounmap(hcd->regs); +err3: + release_mem_region(hcd->rsrc_start, hcd->rsrc_len); +err2: + usb_put_hcd(hcd); +err1: + return retval; +} + +static +void usb_w90x900_remove(struct usb_hcd *hcd, struct platform_device *pdev) +{ + usb_remove_hcd(hcd); + iounmap(hcd->regs); + release_mem_region(hcd->rsrc_start, hcd->rsrc_len); + usb_put_hcd(hcd); +} + +static const struct hc_driver ehci_w90x900_hc_driver = { + .description = hcd_name, + .product_desc = "Nuvoton w90x900 EHCI Host Controller", + .hcd_priv_size = sizeof(struct ehci_hcd), + + /* + * generic hardware linkage + */ + .irq = ehci_irq, + .flags = HCD_USB2|HCD_MEMORY, + + /* + * basic lifecycle operations + */ + .reset = ehci_init, + .start = ehci_run, + + .stop = ehci_stop, + .shutdown = ehci_shutdown, + + /* + * managing i/o requests and associated device resources + */ + .urb_enqueue = ehci_urb_enqueue, + .urb_dequeue = ehci_urb_dequeue, + .endpoint_disable = ehci_endpoint_disable, + + /* + * scheduling support + */ + .get_frame_number = ehci_get_frame, + + /* + * root hub support + */ + .hub_status_data = ehci_hub_status_data, + .hub_control = ehci_hub_control, +#ifdef CONFIG_PM + .bus_suspend = ehci_bus_suspend, + .bus_resume = ehci_bus_resume, +#endif + .relinquish_port = ehci_relinquish_port, + .port_handed_over = ehci_port_handed_over, +}; + +static int __devinit ehci_w90x900_probe(struct platform_device *pdev) +{ + if (usb_disabled()) + return -ENODEV; + + return usb_w90x900_probe(&ehci_w90x900_hc_driver, pdev); +} + +static int __devexit ehci_w90x900_remove(struct platform_device *pdev) +{ + struct usb_hcd *hcd = platform_get_drvdata(pdev); + + usb_w90x900_remove(hcd, pdev); + + return 0; +} + +static struct platform_driver ehci_hcd_w90x900_driver = { + .probe = ehci_w90x900_probe, + .remove = __devexit_p(ehci_w90x900_remove), + .driver = { + .name = "w90x900-ehci", + .owner = THIS_MODULE, + }, +}; + +MODULE_AUTHOR("Wan ZongShun <mcuos.com@gmail.com>"); +MODULE_DESCRIPTION("w90p910 usb ehci driver!"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:w90p910-ehci"); diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h index 48b9e889a18b..064e76821ff5 100644 --- a/drivers/usb/host/ehci.h +++ b/drivers/usb/host/ehci.h @@ -126,6 +126,7 @@ struct ehci_hcd { /* one per controller */ unsigned big_endian_mmio:1; unsigned big_endian_desc:1; unsigned has_amcc_usb23:1; + unsigned need_io_watchdog:1; /* required for usb32 quirk */ #define OHCI_CTRL_HCFS (3 << 6) @@ -135,6 +136,7 @@ struct ehci_hcd { /* one per controller */ #define OHCI_HCCTRL_OFFSET 0x4 #define OHCI_HCCTRL_LEN 0x4 __hc32 *ohci_hcctrl_reg; + unsigned has_hostpc:1; u8 sbrn; /* packed release number */ @@ -298,8 +300,8 @@ union ehci_shadow { * These appear in both the async and (for interrupt) periodic schedules. */ -struct ehci_qh { - /* first part defined by EHCI spec */ +/* first part defined by EHCI spec */ +struct ehci_qh_hw { __hc32 hw_next; /* see EHCI 3.6.1 */ __hc32 hw_info1; /* see EHCI 3.6.2 */ #define QH_HEAD 0x00008000 @@ -317,7 +319,10 @@ struct ehci_qh { __hc32 hw_token; __hc32 hw_buf [5]; __hc32 hw_buf_hi [5]; +} __attribute__ ((aligned(32))); +struct ehci_qh { + struct ehci_qh_hw *hw; /* the rest is HCD-private */ dma_addr_t qh_dma; /* address of qh */ union ehci_shadow qh_next; /* ptr to qh; or periodic */ @@ -336,6 +341,7 @@ struct ehci_qh { u32 refcount; unsigned stamp; + u8 needs_rescan; /* Dequeue during giveback */ u8 qh_state; #define QH_STATE_LINKED 1 /* HC sees this */ #define QH_STATE_UNLINK 2 /* HC may still see this */ @@ -357,7 +363,7 @@ struct ehci_qh { struct usb_device *dev; /* access to TT */ unsigned clearing_tt:1; /* Clear-TT-Buf in progress */ -} __attribute__ ((aligned (32))); +}; /*-------------------------------------------------------------------------*/ @@ -544,7 +550,7 @@ static inline unsigned int ehci_port_speed(struct ehci_hcd *ehci, unsigned int portsc) { if (ehci_is_TDI(ehci)) { - switch ((portsc>>26)&3) { + switch ((portsc >> (ehci->has_hostpc ? 25 : 26)) & 3) { case 0: return 0; case 1: diff --git a/drivers/usb/host/isp1362-hcd.c b/drivers/usb/host/isp1362-hcd.c new file mode 100644 index 000000000000..5c774ab98252 --- /dev/null +++ b/drivers/usb/host/isp1362-hcd.c @@ -0,0 +1,2909 @@ +/* + * ISP1362 HCD (Host Controller Driver) for USB. + * + * Copyright (C) 2005 Lothar Wassmann <LW@KARO-electronics.de> + * + * Derived from the SL811 HCD, rewritten for ISP116x. + * Copyright (C) 2005 Olav Kongas <ok@artecdesign.ee> + * + * Portions: + * Copyright (C) 2004 Psion Teklogix (for NetBook PRO) + * Copyright (C) 2004 David Brownell + */ + +/* + * The ISP1362 chip requires a large delay (300ns and 462ns) between + * accesses to the address and data register. + * The following timing options exist: + * + * 1. Configure your memory controller to add such delays if it can (the best) + * 2. Implement platform-specific delay function possibly + * combined with configuring the memory controller; see + * include/linux/usb_isp1362.h for more info. + * 3. Use ndelay (easiest, poorest). + * + * Use the corresponding macros USE_PLATFORM_DELAY and USE_NDELAY in the + * platform specific section of isp1362.h to select the appropriate variant. + * + * Also note that according to the Philips "ISP1362 Errata" document + * Rev 1.00 from 27 May data corruption may occur when the #WR signal + * is reasserted (even with #CS deasserted) within 132ns after a + * write cycle to any controller register. If the hardware doesn't + * implement the recommended fix (gating the #WR with #CS) software + * must ensure that no further write cycle (not necessarily to the chip!) + * is issued by the CPU within this interval. + + * For PXA25x this can be ensured by using VLIO with the maximum + * recovery time (MSCx = 0x7f8c) with a memory clock of 99.53 MHz. + */ + +#ifdef CONFIG_USB_DEBUG +# define ISP1362_DEBUG +#else +# undef ISP1362_DEBUG +#endif + +/* + * The PXA255 UDC apparently doesn't handle GET_STATUS, GET_CONFIG and + * GET_INTERFACE requests correctly when the SETUP and DATA stages of the + * requests are carried out in separate frames. This will delay any SETUP + * packets until the start of the next frame so that this situation is + * unlikely to occur (and makes usbtest happy running with a PXA255 target + * device). + */ +#undef BUGGY_PXA2XX_UDC_USBTEST + +#undef PTD_TRACE +#undef URB_TRACE +#undef VERBOSE +#undef REGISTERS + +/* This enables a memory test on the ISP1362 chip memory to make sure the + * chip access timing is correct. + */ +#undef CHIP_BUFFER_TEST + +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/kernel.h> +#include <linux/delay.h> +#include <linux/ioport.h> +#include <linux/sched.h> +#include <linux/slab.h> +#include <linux/smp_lock.h> +#include <linux/errno.h> +#include <linux/init.h> +#include <linux/list.h> +#include <linux/interrupt.h> +#include <linux/usb.h> +#include <linux/usb/isp1362.h> +#include <linux/platform_device.h> +#include <linux/pm.h> +#include <linux/io.h> +#include <linux/bitops.h> + +#include <asm/irq.h> +#include <asm/system.h> +#include <asm/byteorder.h> +#include <asm/unaligned.h> + +static int dbg_level; +#ifdef ISP1362_DEBUG +module_param(dbg_level, int, 0644); +#else +module_param(dbg_level, int, 0); +#define STUB_DEBUG_FILE +#endif + +#include "../core/hcd.h" +#include "../core/usb.h" +#include "isp1362.h" + + +#define DRIVER_VERSION "2005-04-04" +#define DRIVER_DESC "ISP1362 USB Host Controller Driver" + +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); + +static const char hcd_name[] = "isp1362-hcd"; + +static void isp1362_hc_stop(struct usb_hcd *hcd); +static int isp1362_hc_start(struct usb_hcd *hcd); + +/*-------------------------------------------------------------------------*/ + +/* + * When called from the interrupthandler only isp1362_hcd->irqenb is modified, + * since the interrupt handler will write isp1362_hcd->irqenb to HCuPINT upon + * completion. + * We don't need a 'disable' counterpart, since interrupts will be disabled + * only by the interrupt handler. + */ +static inline void isp1362_enable_int(struct isp1362_hcd *isp1362_hcd, u16 mask) +{ + if ((isp1362_hcd->irqenb | mask) == isp1362_hcd->irqenb) + return; + if (mask & ~isp1362_hcd->irqenb) + isp1362_write_reg16(isp1362_hcd, HCuPINT, mask & ~isp1362_hcd->irqenb); + isp1362_hcd->irqenb |= mask; + if (isp1362_hcd->irq_active) + return; + isp1362_write_reg16(isp1362_hcd, HCuPINTENB, isp1362_hcd->irqenb); +} + +/*-------------------------------------------------------------------------*/ + +static inline struct isp1362_ep_queue *get_ptd_queue(struct isp1362_hcd *isp1362_hcd, + u16 offset) +{ + struct isp1362_ep_queue *epq = NULL; + + if (offset < isp1362_hcd->istl_queue[1].buf_start) + epq = &isp1362_hcd->istl_queue[0]; + else if (offset < isp1362_hcd->intl_queue.buf_start) + epq = &isp1362_hcd->istl_queue[1]; + else if (offset < isp1362_hcd->atl_queue.buf_start) + epq = &isp1362_hcd->intl_queue; + else if (offset < isp1362_hcd->atl_queue.buf_start + + isp1362_hcd->atl_queue.buf_size) + epq = &isp1362_hcd->atl_queue; + + if (epq) + DBG(1, "%s: PTD $%04x is on %s queue\n", __func__, offset, epq->name); + else + pr_warning("%s: invalid PTD $%04x\n", __func__, offset); + + return epq; +} + +static inline int get_ptd_offset(struct isp1362_ep_queue *epq, u8 index) +{ + int offset; + + if (index * epq->blk_size > epq->buf_size) { + pr_warning("%s: Bad %s index %d(%d)\n", __func__, epq->name, index, + epq->buf_size / epq->blk_size); + return -EINVAL; + } + offset = epq->buf_start + index * epq->blk_size; + DBG(3, "%s: %s PTD[%02x] # %04x\n", __func__, epq->name, index, offset); + + return offset; +} + +/*-------------------------------------------------------------------------*/ + +static inline u16 max_transfer_size(struct isp1362_ep_queue *epq, size_t size, + int mps) +{ + u16 xfer_size = min_t(size_t, MAX_XFER_SIZE, size); + + xfer_size = min_t(size_t, xfer_size, epq->buf_avail * epq->blk_size - PTD_HEADER_SIZE); + if (xfer_size < size && xfer_size % mps) + xfer_size -= xfer_size % mps; + + return xfer_size; +} + +static int claim_ptd_buffers(struct isp1362_ep_queue *epq, + struct isp1362_ep *ep, u16 len) +{ + int ptd_offset = -EINVAL; + int index; + int num_ptds = ((len + PTD_HEADER_SIZE - 1) / epq->blk_size) + 1; + int found = -1; + int last = -1; + + BUG_ON(len > epq->buf_size); + + if (!epq->buf_avail) + return -ENOMEM; + + if (ep->num_ptds) + pr_err("%s: %s len %d/%d num_ptds %d buf_map %08lx skip_map %08lx\n", __func__, + epq->name, len, epq->blk_size, num_ptds, epq->buf_map, epq->skip_map); + BUG_ON(ep->num_ptds != 0); + + for (index = 0; index <= epq->buf_count - num_ptds; index++) { + if (test_bit(index, &epq->buf_map)) + continue; + found = index; + for (last = index + 1; last < index + num_ptds; last++) { + if (test_bit(last, &epq->buf_map)) { + found = -1; + break; + } + } + if (found >= 0) + break; + } + if (found < 0) + return -EOVERFLOW; + + DBG(1, "%s: Found %d PTDs[%d] for %d/%d byte\n", __func__, + num_ptds, found, len, (int)(epq->blk_size - PTD_HEADER_SIZE)); + ptd_offset = get_ptd_offset(epq, found); + WARN_ON(ptd_offset < 0); + ep->ptd_offset = ptd_offset; + ep->num_ptds += num_ptds; + epq->buf_avail -= num_ptds; + BUG_ON(epq->buf_avail > epq->buf_count); + ep->ptd_index = found; + for (index = found; index < last; index++) + __set_bit(index, &epq->buf_map); + DBG(1, "%s: Done %s PTD[%d] $%04x, avail %d count %d claimed %d %08lx:%08lx\n", + __func__, epq->name, ep->ptd_index, ep->ptd_offset, + epq->buf_avail, epq->buf_count, num_ptds, epq->buf_map, epq->skip_map); + + return found; +} + +static inline void release_ptd_buffers(struct isp1362_ep_queue *epq, struct isp1362_ep *ep) +{ + int index = ep->ptd_index; + int last = ep->ptd_index + ep->num_ptds; + + if (last > epq->buf_count) + pr_err("%s: ep %p req %d len %d %s PTD[%d] $%04x num_ptds %d buf_count %d buf_avail %d buf_map %08lx skip_map %08lx\n", + __func__, ep, ep->num_req, ep->length, epq->name, ep->ptd_index, + ep->ptd_offset, ep->num_ptds, epq->buf_count, epq->buf_avail, + epq->buf_map, epq->skip_map); + BUG_ON(last > epq->buf_count); + + for (; index < last; index++) { + __clear_bit(index, &epq->buf_map); + __set_bit(index, &epq->skip_map); + } + epq->buf_avail += ep->num_ptds; + epq->ptd_count--; + + BUG_ON(epq->buf_avail > epq->buf_count); + BUG_ON(epq->ptd_count > epq->buf_count); + + DBG(1, "%s: Done %s PTDs $%04x released %d avail %d count %d\n", + __func__, epq->name, + ep->ptd_offset, ep->num_ptds, epq->buf_avail, epq->buf_count); + DBG(1, "%s: buf_map %08lx skip_map %08lx\n", __func__, + epq->buf_map, epq->skip_map); + + ep->num_ptds = 0; + ep->ptd_offset = -EINVAL; + ep->ptd_index = -EINVAL; +} + +/*-------------------------------------------------------------------------*/ + +/* + Set up PTD's. +*/ +static void prepare_ptd(struct isp1362_hcd *isp1362_hcd, struct urb *urb, + struct isp1362_ep *ep, struct isp1362_ep_queue *epq, + u16 fno) +{ + struct ptd *ptd; + int toggle; + int dir; + u16 len; + size_t buf_len = urb->transfer_buffer_length - urb->actual_length; + + DBG(3, "%s: %s ep %p\n", __func__, epq->name, ep); + + ptd = &ep->ptd; + + ep->data = (unsigned char *)urb->transfer_buffer + urb->actual_length; + + switch (ep->nextpid) { + case USB_PID_IN: + toggle = usb_gettoggle(urb->dev, ep->epnum, 0); + dir = PTD_DIR_IN; + if (usb_pipecontrol(urb->pipe)) { + len = min_t(size_t, ep->maxpacket, buf_len); + } else if (usb_pipeisoc(urb->pipe)) { + len = min_t(size_t, urb->iso_frame_desc[fno].length, MAX_XFER_SIZE); + ep->data = urb->transfer_buffer + urb->iso_frame_desc[fno].offset; + } else + len = max_transfer_size(epq, buf_len, ep->maxpacket); + DBG(1, "%s: IN len %d/%d/%d from URB\n", __func__, len, ep->maxpacket, + (int)buf_len); + break; + case USB_PID_OUT: + toggle = usb_gettoggle(urb->dev, ep->epnum, 1); + dir = PTD_DIR_OUT; + if (usb_pipecontrol(urb->pipe)) + len = min_t(size_t, ep->maxpacket, buf_len); + else if (usb_pipeisoc(urb->pipe)) + len = min_t(size_t, urb->iso_frame_desc[0].length, MAX_XFER_SIZE); + else + len = max_transfer_size(epq, buf_len, ep->maxpacket); + if (len == 0) + pr_info("%s: Sending ZERO packet: %d\n", __func__, + urb->transfer_flags & URB_ZERO_PACKET); + DBG(1, "%s: OUT len %d/%d/%d from URB\n", __func__, len, ep->maxpacket, + (int)buf_len); + break; + case USB_PID_SETUP: + toggle = 0; + dir = PTD_DIR_SETUP; + len = sizeof(struct usb_ctrlrequest); + DBG(1, "%s: SETUP len %d\n", __func__, len); + ep->data = urb->setup_packet; + break; + case USB_PID_ACK: + toggle = 1; + len = 0; + dir = (urb->transfer_buffer_length && usb_pipein(urb->pipe)) ? + PTD_DIR_OUT : PTD_DIR_IN; + DBG(1, "%s: ACK len %d\n", __func__, len); + break; + default: + toggle = dir = len = 0; + pr_err("%s@%d: ep->nextpid %02x\n", __func__, __LINE__, ep->nextpid); + BUG_ON(1); + } + + ep->length = len; + if (!len) + ep->data = NULL; + + ptd->count = PTD_CC_MSK | PTD_ACTIVE_MSK | PTD_TOGGLE(toggle); + ptd->mps = PTD_MPS(ep->maxpacket) | PTD_SPD(urb->dev->speed == USB_SPEED_LOW) | + PTD_EP(ep->epnum); + ptd->len = PTD_LEN(len) | PTD_DIR(dir); + ptd->faddr = PTD_FA(usb_pipedevice(urb->pipe)); + + if (usb_pipeint(urb->pipe)) { + ptd->faddr |= PTD_SF_INT(ep->branch); + ptd->faddr |= PTD_PR(ep->interval ? __ffs(ep->interval) : 0); + } + if (usb_pipeisoc(urb->pipe)) + ptd->faddr |= PTD_SF_ISO(fno); + + DBG(1, "%s: Finished\n", __func__); +} + +static void isp1362_write_ptd(struct isp1362_hcd *isp1362_hcd, struct isp1362_ep *ep, + struct isp1362_ep_queue *epq) +{ + struct ptd *ptd = &ep->ptd; + int len = PTD_GET_DIR(ptd) == PTD_DIR_IN ? 0 : ep->length; + + _BUG_ON(ep->ptd_offset < 0); + + prefetch(ptd); + isp1362_write_buffer(isp1362_hcd, ptd, ep->ptd_offset, PTD_HEADER_SIZE); + if (len) + isp1362_write_buffer(isp1362_hcd, ep->data, + ep->ptd_offset + PTD_HEADER_SIZE, len); + + dump_ptd(ptd); + dump_ptd_out_data(ptd, ep->data); +} + +static void isp1362_read_ptd(struct isp1362_hcd *isp1362_hcd, struct isp1362_ep *ep, + struct isp1362_ep_queue *epq) +{ + struct ptd *ptd = &ep->ptd; + int act_len; + + WARN_ON(list_empty(&ep->active)); + BUG_ON(ep->ptd_offset < 0); + + list_del_init(&ep->active); + DBG(1, "%s: ep %p removed from active list %p\n", __func__, ep, &epq->active); + + prefetchw(ptd); + isp1362_read_buffer(isp1362_hcd, ptd, ep->ptd_offset, PTD_HEADER_SIZE); + dump_ptd(ptd); + act_len = PTD_GET_COUNT(ptd); + if (PTD_GET_DIR(ptd) != PTD_DIR_IN || act_len == 0) + return; + if (act_len > ep->length) + pr_err("%s: ep %p PTD $%04x act_len %d ep->length %d\n", __func__, ep, + ep->ptd_offset, act_len, ep->length); + BUG_ON(act_len > ep->length); + /* Only transfer the amount of data that has actually been overwritten + * in the chip buffer. We don't want any data that doesn't belong to the + * transfer to leak out of the chip to the callers transfer buffer! + */ + prefetchw(ep->data); + isp1362_read_buffer(isp1362_hcd, ep->data, + ep->ptd_offset + PTD_HEADER_SIZE, act_len); + dump_ptd_in_data(ptd, ep->data); +} + +/* + * INT PTDs will stay in the chip until data is available. + * This function will remove a PTD from the chip when the URB is dequeued. + * Must be called with the spinlock held and IRQs disabled + */ +static void remove_ptd(struct isp1362_hcd *isp1362_hcd, struct isp1362_ep *ep) + +{ + int index; + struct isp1362_ep_queue *epq; + + DBG(1, "%s: ep %p PTD[%d] $%04x\n", __func__, ep, ep->ptd_index, ep->ptd_offset); + BUG_ON(ep->ptd_offset < 0); + + epq = get_ptd_queue(isp1362_hcd, ep->ptd_offset); + BUG_ON(!epq); + + /* put ep in remove_list for cleanup */ + WARN_ON(!list_empty(&ep->remove_list)); + list_add_tail(&ep->remove_list, &isp1362_hcd->remove_list); + /* let SOF interrupt handle the cleanup */ + isp1362_enable_int(isp1362_hcd, HCuPINT_SOF); + + index = ep->ptd_index; + if (index < 0) + /* ISO queues don't have SKIP registers */ + return; + + DBG(1, "%s: Disabling PTD[%02x] $%04x %08lx|%08x\n", __func__, + index, ep->ptd_offset, epq->skip_map, 1 << index); + + /* prevent further processing of PTD (will be effective after next SOF) */ + epq->skip_map |= 1 << index; + if (epq == &isp1362_hcd->atl_queue) { + DBG(2, "%s: ATLSKIP = %08x -> %08lx\n", __func__, + isp1362_read_reg32(isp1362_hcd, HCATLSKIP), epq->skip_map); + isp1362_write_reg32(isp1362_hcd, HCATLSKIP, epq->skip_map); + if (~epq->skip_map == 0) + isp1362_clr_mask16(isp1362_hcd, HCBUFSTAT, HCBUFSTAT_ATL_ACTIVE); + } else if (epq == &isp1362_hcd->intl_queue) { + DBG(2, "%s: INTLSKIP = %08x -> %08lx\n", __func__, + isp1362_read_reg32(isp1362_hcd, HCINTLSKIP), epq->skip_map); + isp1362_write_reg32(isp1362_hcd, HCINTLSKIP, epq->skip_map); + if (~epq->skip_map == 0) + isp1362_clr_mask16(isp1362_hcd, HCBUFSTAT, HCBUFSTAT_INTL_ACTIVE); + } +} + +/* + Take done or failed requests out of schedule. Give back + processed urbs. +*/ +static void finish_request(struct isp1362_hcd *isp1362_hcd, struct isp1362_ep *ep, + struct urb *urb, int status) + __releases(isp1362_hcd->lock) + __acquires(isp1362_hcd->lock) +{ + urb->hcpriv = NULL; + ep->error_count = 0; + + if (usb_pipecontrol(urb->pipe)) + ep->nextpid = USB_PID_SETUP; + + URB_DBG("%s: req %d FA %d ep%d%s %s: len %d/%d %s stat %d\n", __func__, + ep->num_req, usb_pipedevice(urb->pipe), + usb_pipeendpoint(urb->pipe), + !usb_pipein(urb->pipe) ? "out" : "in", + usb_pipecontrol(urb->pipe) ? "ctrl" : + usb_pipeint(urb->pipe) ? "int" : + usb_pipebulk(urb->pipe) ? "bulk" : + "iso", + urb->actual_length, urb->transfer_buffer_length, + !(urb->transfer_flags & URB_SHORT_NOT_OK) ? + "short_ok" : "", urb->status); + + + usb_hcd_unlink_urb_from_ep(isp1362_hcd_to_hcd(isp1362_hcd), urb); + spin_unlock(&isp1362_hcd->lock); + usb_hcd_giveback_urb(isp1362_hcd_to_hcd(isp1362_hcd), urb, status); + spin_lock(&isp1362_hcd->lock); + + /* take idle endpoints out of the schedule right away */ + if (!list_empty(&ep->hep->urb_list)) + return; + + /* async deschedule */ + if (!list_empty(&ep->schedule)) { + list_del_init(&ep->schedule); + return; + } + + + if (ep->interval) { + /* periodic deschedule */ + DBG(1, "deschedule qh%d/%p branch %d load %d bandwidth %d -> %d\n", ep->interval, + ep, ep->branch, ep->load, + isp1362_hcd->load[ep->branch], + isp1362_hcd->load[ep->branch] - ep->load); + isp1362_hcd->load[ep->branch] -= ep->load; + ep->branch = PERIODIC_SIZE; + } +} + +/* + * Analyze transfer results, handle partial transfers and errors +*/ +static void postproc_ep(struct isp1362_hcd *isp1362_hcd, struct isp1362_ep *ep) +{ + struct urb *urb = get_urb(ep); + struct usb_device *udev; + struct ptd *ptd; + int short_ok; + u16 len; + int urbstat = -EINPROGRESS; + u8 cc; + + DBG(2, "%s: ep %p req %d\n", __func__, ep, ep->num_req); + + udev = urb->dev; + ptd = &ep->ptd; + cc = PTD_GET_CC(ptd); + if (cc == PTD_NOTACCESSED) { + pr_err("%s: req %d PTD %p Untouched by ISP1362\n", __func__, + ep->num_req, ptd); + cc = PTD_DEVNOTRESP; + } + + short_ok = !(urb->transfer_flags & URB_SHORT_NOT_OK); + len = urb->transfer_buffer_length - urb->actual_length; + + /* Data underrun is special. For allowed underrun + we clear the error and continue as normal. For + forbidden underrun we finish the DATA stage + immediately while for control transfer, + we do a STATUS stage. + */ + if (cc == PTD_DATAUNDERRUN) { + if (short_ok) { + DBG(1, "%s: req %d Allowed data underrun short_%sok %d/%d/%d byte\n", + __func__, ep->num_req, short_ok ? "" : "not_", + PTD_GET_COUNT(ptd), ep->maxpacket, len); + cc = PTD_CC_NOERROR; + urbstat = 0; + } else { + DBG(1, "%s: req %d Data Underrun %s nextpid %02x short_%sok %d/%d/%d byte\n", + __func__, ep->num_req, + usb_pipein(urb->pipe) ? "IN" : "OUT", ep->nextpid, + short_ok ? "" : "not_", + PTD_GET_COUNT(ptd), ep->maxpacket, len); + if (usb_pipecontrol(urb->pipe)) { + ep->nextpid = USB_PID_ACK; + /* save the data underrun error code for later and + * procede with the status stage + */ + urb->actual_length += PTD_GET_COUNT(ptd); + BUG_ON(urb->actual_length > urb->transfer_buffer_length); + + if (urb->status == -EINPROGRESS) + urb->status = cc_to_error[PTD_DATAUNDERRUN]; + } else { + usb_settoggle(udev, ep->epnum, ep->nextpid == USB_PID_OUT, + PTD_GET_TOGGLE(ptd)); + urbstat = cc_to_error[PTD_DATAUNDERRUN]; + } + goto out; + } + } + + if (cc != PTD_CC_NOERROR) { + if (++ep->error_count >= 3 || cc == PTD_CC_STALL || cc == PTD_DATAOVERRUN) { + urbstat = cc_to_error[cc]; + DBG(1, "%s: req %d nextpid %02x, status %d, error %d, error_count %d\n", + __func__, ep->num_req, ep->nextpid, urbstat, cc, + ep->error_count); + } + goto out; + } + + switch (ep->nextpid) { + case USB_PID_OUT: + if (PTD_GET_COUNT(ptd) != ep->length) + pr_err("%s: count=%d len=%d\n", __func__, + PTD_GET_COUNT(ptd), ep->length); + BUG_ON(PTD_GET_COUNT(ptd) != ep->length); + urb->actual_length += ep->length; + BUG_ON(urb->actual_length > urb->transfer_buffer_length); + usb_settoggle(udev, ep->epnum, 1, PTD_GET_TOGGLE(ptd)); + if (urb->actual_length == urb->transfer_buffer_length) { + DBG(3, "%s: req %d xfer complete %d/%d status %d -> 0\n", __func__, + ep->num_req, len, ep->maxpacket, urbstat); + if (usb_pipecontrol(urb->pipe)) { + DBG(3, "%s: req %d %s Wait for ACK\n", __func__, + ep->num_req, + usb_pipein(urb->pipe) ? "IN" : "OUT"); + ep->nextpid = USB_PID_ACK; + } else { + if (len % ep->maxpacket || + !(urb->transfer_flags & URB_ZERO_PACKET)) { + urbstat = 0; + DBG(3, "%s: req %d URB %s status %d count %d/%d/%d\n", + __func__, ep->num_req, usb_pipein(urb->pipe) ? "IN" : "OUT", + urbstat, len, ep->maxpacket, urb->actual_length); + } + } + } + break; + case USB_PID_IN: + len = PTD_GET_COUNT(ptd); + BUG_ON(len > ep->length); + urb->actual_length += len; + BUG_ON(urb->actual_length > urb->transfer_buffer_length); + usb_settoggle(udev, ep->epnum, 0, PTD_GET_TOGGLE(ptd)); + /* if transfer completed or (allowed) data underrun */ + if ((urb->transfer_buffer_length == urb->actual_length) || + len % ep->maxpacket) { + DBG(3, "%s: req %d xfer complete %d/%d status %d -> 0\n", __func__, + ep->num_req, len, ep->maxpacket, urbstat); + if (usb_pipecontrol(urb->pipe)) { + DBG(3, "%s: req %d %s Wait for ACK\n", __func__, + ep->num_req, + usb_pipein(urb->pipe) ? "IN" : "OUT"); + ep->nextpid = USB_PID_ACK; + } else { + urbstat = 0; + DBG(3, "%s: req %d URB %s status %d count %d/%d/%d\n", + __func__, ep->num_req, usb_pipein(urb->pipe) ? "IN" : "OUT", + urbstat, len, ep->maxpacket, urb->actual_length); + } + } + break; + case USB_PID_SETUP: + if (urb->transfer_buffer_length == urb->actual_length) { + ep->nextpid = USB_PID_ACK; + } else if (usb_pipeout(urb->pipe)) { + usb_settoggle(udev, 0, 1, 1); + ep->nextpid = USB_PID_OUT; + } else { + usb_settoggle(udev, 0, 0, 1); + ep->nextpid = USB_PID_IN; + } + break; + case USB_PID_ACK: + DBG(3, "%s: req %d got ACK %d -> 0\n", __func__, ep->num_req, + urbstat); + WARN_ON(urbstat != -EINPROGRESS); + urbstat = 0; + ep->nextpid = 0; + break; + default: + BUG_ON(1); + } + + out: + if (urbstat != -EINPROGRESS) { + DBG(2, "%s: Finishing ep %p req %d urb %p status %d\n", __func__, + ep, ep->num_req, urb, urbstat); + finish_request(isp1362_hcd, ep, urb, urbstat); + } +} + +static void finish_unlinks(struct isp1362_hcd *isp1362_hcd) +{ + struct isp1362_ep *ep; + struct isp1362_ep *tmp; + + list_for_each_entry_safe(ep, tmp, &isp1362_hcd->remove_list, remove_list) { + struct isp1362_ep_queue *epq = + get_ptd_queue(isp1362_hcd, ep->ptd_offset); + int index = ep->ptd_index; + + BUG_ON(epq == NULL); + if (index >= 0) { + DBG(1, "%s: remove PTD[%d] $%04x\n", __func__, index, ep->ptd_offset); + BUG_ON(ep->num_ptds == 0); + release_ptd_buffers(epq, ep); + } + if (!list_empty(&ep->hep->urb_list)) { + struct urb *urb = get_urb(ep); + + DBG(1, "%s: Finishing req %d ep %p from remove_list\n", __func__, + ep->num_req, ep); + finish_request(isp1362_hcd, ep, urb, -ESHUTDOWN); + } + WARN_ON(list_empty(&ep->active)); + if (!list_empty(&ep->active)) { + list_del_init(&ep->active); + DBG(1, "%s: ep %p removed from active list\n", __func__, ep); + } + list_del_init(&ep->remove_list); + DBG(1, "%s: ep %p removed from remove_list\n", __func__, ep); + } + DBG(1, "%s: Done\n", __func__); +} + +static inline void enable_atl_transfers(struct isp1362_hcd *isp1362_hcd, int count) +{ + if (count > 0) { + if (count < isp1362_hcd->atl_queue.ptd_count) + isp1362_write_reg16(isp1362_hcd, HCATLDTC, count); + isp1362_enable_int(isp1362_hcd, HCuPINT_ATL); + isp1362_write_reg32(isp1362_hcd, HCATLSKIP, isp1362_hcd->atl_queue.skip_map); + isp1362_set_mask16(isp1362_hcd, HCBUFSTAT, HCBUFSTAT_ATL_ACTIVE); + } else + isp1362_enable_int(isp1362_hcd, HCuPINT_SOF); +} + +static inline void enable_intl_transfers(struct isp1362_hcd *isp1362_hcd) +{ + isp1362_enable_int(isp1362_hcd, HCuPINT_INTL); + isp1362_set_mask16(isp1362_hcd, HCBUFSTAT, HCBUFSTAT_INTL_ACTIVE); + isp1362_write_reg32(isp1362_hcd, HCINTLSKIP, isp1362_hcd->intl_queue.skip_map); +} + +static inline void enable_istl_transfers(struct isp1362_hcd *isp1362_hcd, int flip) +{ + isp1362_enable_int(isp1362_hcd, flip ? HCuPINT_ISTL1 : HCuPINT_ISTL0); + isp1362_set_mask16(isp1362_hcd, HCBUFSTAT, flip ? + HCBUFSTAT_ISTL1_FULL : HCBUFSTAT_ISTL0_FULL); +} + +static int submit_req(struct isp1362_hcd *isp1362_hcd, struct urb *urb, + struct isp1362_ep *ep, struct isp1362_ep_queue *epq) +{ + int index = epq->free_ptd; + + prepare_ptd(isp1362_hcd, urb, ep, epq, 0); + index = claim_ptd_buffers(epq, ep, ep->length); + if (index == -ENOMEM) { + DBG(1, "%s: req %d No free %s PTD available: %d, %08lx:%08lx\n", __func__, + ep->num_req, epq->name, ep->num_ptds, epq->buf_map, epq->skip_map); + return index; + } else if (index == -EOVERFLOW) { + DBG(1, "%s: req %d Not enough space for %d byte %s PTD %d %08lx:%08lx\n", + __func__, ep->num_req, ep->length, epq->name, ep->num_ptds, + epq->buf_map, epq->skip_map); + return index; + } else + BUG_ON(index < 0); + list_add_tail(&ep->active, &epq->active); + DBG(1, "%s: ep %p req %d len %d added to active list %p\n", __func__, + ep, ep->num_req, ep->length, &epq->active); + DBG(1, "%s: Submitting %s PTD $%04x for ep %p req %d\n", __func__, epq->name, + ep->ptd_offset, ep, ep->num_req); + isp1362_write_ptd(isp1362_hcd, ep, epq); + __clear_bit(ep->ptd_index, &epq->skip_map); + + return 0; +} + +static void start_atl_transfers(struct isp1362_hcd *isp1362_hcd) +{ + int ptd_count = 0; + struct isp1362_ep_queue *epq = &isp1362_hcd->atl_queue; + struct isp1362_ep *ep; + int defer = 0; + + if (atomic_read(&epq->finishing)) { + DBG(1, "%s: finish_transfers is active for %s\n", __func__, epq->name); + return; + } + + list_for_each_entry(ep, &isp1362_hcd->async, schedule) { + struct urb *urb = get_urb(ep); + int ret; + + if (!list_empty(&ep->active)) { + DBG(2, "%s: Skipping active %s ep %p\n", __func__, epq->name, ep); + continue; + } + + DBG(1, "%s: Processing %s ep %p req %d\n", __func__, epq->name, + ep, ep->num_req); + + ret = submit_req(isp1362_hcd, urb, ep, epq); + if (ret == -ENOMEM) { + defer = 1; + break; + } else if (ret == -EOVERFLOW) { + defer = 1; + continue; + } +#ifdef BUGGY_PXA2XX_UDC_USBTEST + defer = ep->nextpid == USB_PID_SETUP; +#endif + ptd_count++; + } + + /* Avoid starving of endpoints */ + if (isp1362_hcd->async.next != isp1362_hcd->async.prev) { + DBG(2, "%s: Cycling ASYNC schedule %d\n", __func__, ptd_count); + list_move(&isp1362_hcd->async, isp1362_hcd->async.next); + } + if (ptd_count || defer) + enable_atl_transfers(isp1362_hcd, defer ? 0 : ptd_count); + + epq->ptd_count += ptd_count; + if (epq->ptd_count > epq->stat_maxptds) { + epq->stat_maxptds = epq->ptd_count; + DBG(0, "%s: max_ptds: %d\n", __func__, epq->stat_maxptds); + } +} + +static void start_intl_transfers(struct isp1362_hcd *isp1362_hcd) +{ + int ptd_count = 0; + struct isp1362_ep_queue *epq = &isp1362_hcd->intl_queue; + struct isp1362_ep *ep; + + if (atomic_read(&epq->finishing)) { + DBG(1, "%s: finish_transfers is active for %s\n", __func__, epq->name); + return; + } + + list_for_each_entry(ep, &isp1362_hcd->periodic, schedule) { + struct urb *urb = get_urb(ep); + int ret; + + if (!list_empty(&ep->active)) { + DBG(1, "%s: Skipping active %s ep %p\n", __func__, + epq->name, ep); + continue; + } + + DBG(1, "%s: Processing %s ep %p req %d\n", __func__, + epq->name, ep, ep->num_req); + ret = submit_req(isp1362_hcd, urb, ep, epq); + if (ret == -ENOMEM) + break; + else if (ret == -EOVERFLOW) + continue; + ptd_count++; + } + + if (ptd_count) { + static int last_count; + + if (ptd_count != last_count) { + DBG(0, "%s: ptd_count: %d\n", __func__, ptd_count); + last_count = ptd_count; + } + enable_intl_transfers(isp1362_hcd); + } + + epq->ptd_count += ptd_count; + if (epq->ptd_count > epq->stat_maxptds) + epq->stat_maxptds = epq->ptd_count; +} + +static inline int next_ptd(struct isp1362_ep_queue *epq, struct isp1362_ep *ep) +{ + u16 ptd_offset = ep->ptd_offset; + int num_ptds = (ep->length + PTD_HEADER_SIZE + (epq->blk_size - 1)) / epq->blk_size; + + DBG(2, "%s: PTD offset $%04x + %04x => %d * %04x -> $%04x\n", __func__, ptd_offset, + ep->length, num_ptds, epq->blk_size, ptd_offset + num_ptds * epq->blk_size); + + ptd_offset += num_ptds * epq->blk_size; + if (ptd_offset < epq->buf_start + epq->buf_size) + return ptd_offset; + else + return -ENOMEM; +} + +static void start_iso_transfers(struct isp1362_hcd *isp1362_hcd) +{ + int ptd_count = 0; + int flip = isp1362_hcd->istl_flip; + struct isp1362_ep_queue *epq; + int ptd_offset; + struct isp1362_ep *ep; + struct isp1362_ep *tmp; + u16 fno = isp1362_read_reg32(isp1362_hcd, HCFMNUM); + + fill2: + epq = &isp1362_hcd->istl_queue[flip]; + if (atomic_read(&epq->finishing)) { + DBG(1, "%s: finish_transfers is active for %s\n", __func__, epq->name); + return; + } + + if (!list_empty(&epq->active)) + return; + + ptd_offset = epq->buf_start; + list_for_each_entry_safe(ep, tmp, &isp1362_hcd->isoc, schedule) { + struct urb *urb = get_urb(ep); + s16 diff = fno - (u16)urb->start_frame; + + DBG(1, "%s: Processing %s ep %p\n", __func__, epq->name, ep); + + if (diff > urb->number_of_packets) { + /* time frame for this URB has elapsed */ + finish_request(isp1362_hcd, ep, urb, -EOVERFLOW); + continue; + } else if (diff < -1) { + /* URB is not due in this frame or the next one. + * Comparing with '-1' instead of '0' accounts for double + * buffering in the ISP1362 which enables us to queue the PTD + * one frame ahead of time + */ + } else if (diff == -1) { + /* submit PTD's that are due in the next frame */ + prepare_ptd(isp1362_hcd, urb, ep, epq, fno); + if (ptd_offset + PTD_HEADER_SIZE + ep->length > + epq->buf_start + epq->buf_size) { + pr_err("%s: Not enough ISO buffer space for %d byte PTD\n", + __func__, ep->length); + continue; + } + ep->ptd_offset = ptd_offset; + list_add_tail(&ep->active, &epq->active); + + ptd_offset = next_ptd(epq, ep); + if (ptd_offset < 0) { + pr_warning("%s: req %d No more %s PTD buffers available\n", __func__, + ep->num_req, epq->name); + break; + } + } + } + list_for_each_entry(ep, &epq->active, active) { + if (epq->active.next == &ep->active) + ep->ptd.mps |= PTD_LAST_MSK; + isp1362_write_ptd(isp1362_hcd, ep, epq); + ptd_count++; + } + + if (ptd_count) + enable_istl_transfers(isp1362_hcd, flip); + + epq->ptd_count += ptd_count; + if (epq->ptd_count > epq->stat_maxptds) + epq->stat_maxptds = epq->ptd_count; + + /* check, whether the second ISTL buffer may also be filled */ + if (!(isp1362_read_reg16(isp1362_hcd, HCBUFSTAT) & + (flip ? HCBUFSTAT_ISTL0_FULL : HCBUFSTAT_ISTL1_FULL))) { + fno++; + ptd_count = 0; + flip = 1 - flip; + goto fill2; + } +} + +static void finish_transfers(struct isp1362_hcd *isp1362_hcd, unsigned long done_map, + struct isp1362_ep_queue *epq) +{ + struct isp1362_ep *ep; + struct isp1362_ep *tmp; + + if (list_empty(&epq->active)) { + DBG(1, "%s: Nothing to do for %s queue\n", __func__, epq->name); + return; + } + + DBG(1, "%s: Finishing %s transfers %08lx\n", __func__, epq->name, done_map); + + atomic_inc(&epq->finishing); + list_for_each_entry_safe(ep, tmp, &epq->active, active) { + int index = ep->ptd_index; + + DBG(1, "%s: Checking %s PTD[%02x] $%04x\n", __func__, epq->name, + index, ep->ptd_offset); + + BUG_ON(index < 0); + if (__test_and_clear_bit(index, &done_map)) { + isp1362_read_ptd(isp1362_hcd, ep, epq); + epq->free_ptd = index; + BUG_ON(ep->num_ptds == 0); + release_ptd_buffers(epq, ep); + + DBG(1, "%s: ep %p req %d removed from active list\n", __func__, + ep, ep->num_req); + if (!list_empty(&ep->remove_list)) { + list_del_init(&ep->remove_list); + DBG(1, "%s: ep %p removed from remove list\n", __func__, ep); + } + DBG(1, "%s: Postprocessing %s ep %p req %d\n", __func__, epq->name, + ep, ep->num_req); + postproc_ep(isp1362_hcd, ep); + } + if (!done_map) + break; + } + if (done_map) + pr_warning("%s: done_map not clear: %08lx:%08lx\n", __func__, done_map, + epq->skip_map); + atomic_dec(&epq->finishing); +} + +static void finish_iso_transfers(struct isp1362_hcd *isp1362_hcd, struct isp1362_ep_queue *epq) +{ + struct isp1362_ep *ep; + struct isp1362_ep *tmp; + + if (list_empty(&epq->active)) { + DBG(1, "%s: Nothing to do for %s queue\n", __func__, epq->name); + return; + } + + DBG(1, "%s: Finishing %s transfers\n", __func__, epq->name); + + atomic_inc(&epq->finishing); + list_for_each_entry_safe(ep, tmp, &epq->active, active) { + DBG(1, "%s: Checking PTD $%04x\n", __func__, ep->ptd_offset); + + isp1362_read_ptd(isp1362_hcd, ep, epq); + DBG(1, "%s: Postprocessing %s ep %p\n", __func__, epq->name, ep); + postproc_ep(isp1362_hcd, ep); + } + WARN_ON(epq->blk_size != 0); + atomic_dec(&epq->finishing); +} + +static irqreturn_t isp1362_irq(struct usb_hcd *hcd) +{ + int handled = 0; + struct isp1362_hcd *isp1362_hcd = hcd_to_isp1362_hcd(hcd); + u16 irqstat; + u16 svc_mask; + + spin_lock(&isp1362_hcd->lock); + + BUG_ON(isp1362_hcd->irq_active++); + + isp1362_write_reg16(isp1362_hcd, HCuPINTENB, 0); + + irqstat = isp1362_read_reg16(isp1362_hcd, HCuPINT); + DBG(3, "%s: got IRQ %04x:%04x\n", __func__, irqstat, isp1362_hcd->irqenb); + + /* only handle interrupts that are currently enabled */ + irqstat &= isp1362_hcd->irqenb; + isp1362_write_reg16(isp1362_hcd, HCuPINT, irqstat); + svc_mask = irqstat; + + if (irqstat & HCuPINT_SOF) { + isp1362_hcd->irqenb &= ~HCuPINT_SOF; + isp1362_hcd->irq_stat[ISP1362_INT_SOF]++; + handled = 1; + svc_mask &= ~HCuPINT_SOF; + DBG(3, "%s: SOF\n", __func__); + isp1362_hcd->fmindex = isp1362_read_reg32(isp1362_hcd, HCFMNUM); + if (!list_empty(&isp1362_hcd->remove_list)) + finish_unlinks(isp1362_hcd); + if (!list_empty(&isp1362_hcd->async) && !(irqstat & HCuPINT_ATL)) { + if (list_empty(&isp1362_hcd->atl_queue.active)) { + start_atl_transfers(isp1362_hcd); + } else { + isp1362_enable_int(isp1362_hcd, HCuPINT_ATL); + isp1362_write_reg32(isp1362_hcd, HCATLSKIP, + isp1362_hcd->atl_queue.skip_map); + isp1362_set_mask16(isp1362_hcd, HCBUFSTAT, HCBUFSTAT_ATL_ACTIVE); + } + } + } + + if (irqstat & HCuPINT_ISTL0) { + isp1362_hcd->irq_stat[ISP1362_INT_ISTL0]++; + handled = 1; + svc_mask &= ~HCuPINT_ISTL0; + isp1362_clr_mask16(isp1362_hcd, HCBUFSTAT, HCBUFSTAT_ISTL0_FULL); + DBG(1, "%s: ISTL0\n", __func__); + WARN_ON((int)!!isp1362_hcd->istl_flip); + WARN_ON(isp1362_read_reg16(isp1362_hcd, HCBUFSTAT) & + HCBUFSTAT_ISTL0_ACTIVE); + WARN_ON(!(isp1362_read_reg16(isp1362_hcd, HCBUFSTAT) & + HCBUFSTAT_ISTL0_DONE)); + isp1362_hcd->irqenb &= ~HCuPINT_ISTL0; + } + + if (irqstat & HCuPINT_ISTL1) { + isp1362_hcd->irq_stat[ISP1362_INT_ISTL1]++; + handled = 1; + svc_mask &= ~HCuPINT_ISTL1; + isp1362_clr_mask16(isp1362_hcd, HCBUFSTAT, HCBUFSTAT_ISTL1_FULL); + DBG(1, "%s: ISTL1\n", __func__); + WARN_ON(!(int)isp1362_hcd->istl_flip); + WARN_ON(isp1362_read_reg16(isp1362_hcd, HCBUFSTAT) & + HCBUFSTAT_ISTL1_ACTIVE); + WARN_ON(!(isp1362_read_reg16(isp1362_hcd, HCBUFSTAT) & + HCBUFSTAT_ISTL1_DONE)); + isp1362_hcd->irqenb &= ~HCuPINT_ISTL1; + } + + if (irqstat & (HCuPINT_ISTL0 | HCuPINT_ISTL1)) { + WARN_ON((irqstat & (HCuPINT_ISTL0 | HCuPINT_ISTL1)) == + (HCuPINT_ISTL0 | HCuPINT_ISTL1)); + finish_iso_transfers(isp1362_hcd, + &isp1362_hcd->istl_queue[isp1362_hcd->istl_flip]); + start_iso_transfers(isp1362_hcd); + isp1362_hcd->istl_flip = 1 - isp1362_hcd->istl_flip; + } + + if (irqstat & HCuPINT_INTL) { + u32 done_map = isp1362_read_reg32(isp1362_hcd, HCINTLDONE); + u32 skip_map = isp1362_read_reg32(isp1362_hcd, HCINTLSKIP); + isp1362_hcd->irq_stat[ISP1362_INT_INTL]++; + + DBG(2, "%s: INTL\n", __func__); + + svc_mask &= ~HCuPINT_INTL; + + isp1362_write_reg32(isp1362_hcd, HCINTLSKIP, skip_map | done_map); + if (~(done_map | skip_map) == 0) + /* All PTDs are finished, disable INTL processing entirely */ + isp1362_clr_mask16(isp1362_hcd, HCBUFSTAT, HCBUFSTAT_INTL_ACTIVE); + + handled = 1; + WARN_ON(!done_map); + if (done_map) { + DBG(3, "%s: INTL done_map %08x\n", __func__, done_map); + finish_transfers(isp1362_hcd, done_map, &isp1362_hcd->intl_queue); + start_intl_transfers(isp1362_hcd); + } + } + + if (irqstat & HCuPINT_ATL) { + u32 done_map = isp1362_read_reg32(isp1362_hcd, HCATLDONE); + u32 skip_map = isp1362_read_reg32(isp1362_hcd, HCATLSKIP); + isp1362_hcd->irq_stat[ISP1362_INT_ATL]++; + + DBG(2, "%s: ATL\n", __func__); + + svc_mask &= ~HCuPINT_ATL; + + isp1362_write_reg32(isp1362_hcd, HCATLSKIP, skip_map | done_map); + if (~(done_map | skip_map) == 0) + isp1362_clr_mask16(isp1362_hcd, HCBUFSTAT, HCBUFSTAT_ATL_ACTIVE); + if (done_map) { + DBG(3, "%s: ATL done_map %08x\n", __func__, done_map); + finish_transfers(isp1362_hcd, done_map, &isp1362_hcd->atl_queue); + start_atl_transfers(isp1362_hcd); + } + handled = 1; + } + + if (irqstat & HCuPINT_OPR) { + u32 intstat = isp1362_read_reg32(isp1362_hcd, HCINTSTAT); + isp1362_hcd->irq_stat[ISP1362_INT_OPR]++; + + svc_mask &= ~HCuPINT_OPR; + DBG(2, "%s: OPR %08x:%08x\n", __func__, intstat, isp1362_hcd->intenb); + intstat &= isp1362_hcd->intenb; + if (intstat & OHCI_INTR_UE) { + pr_err("Unrecoverable error\n"); + /* FIXME: do here reset or cleanup or whatever */ + } + if (intstat & OHCI_INTR_RHSC) { + isp1362_hcd->rhstatus = isp1362_read_reg32(isp1362_hcd, HCRHSTATUS); + isp1362_hcd->rhport[0] = isp1362_read_reg32(isp1362_hcd, HCRHPORT1); + isp1362_hcd->rhport[1] = isp1362_read_reg32(isp1362_hcd, HCRHPORT2); + } + if (intstat & OHCI_INTR_RD) { + pr_info("%s: RESUME DETECTED\n", __func__); + isp1362_show_reg(isp1362_hcd, HCCONTROL); + usb_hcd_resume_root_hub(hcd); + } + isp1362_write_reg32(isp1362_hcd, HCINTSTAT, intstat); + irqstat &= ~HCuPINT_OPR; + handled = 1; + } + + if (irqstat & HCuPINT_SUSP) { + isp1362_hcd->irq_stat[ISP1362_INT_SUSP]++; + handled = 1; + svc_mask &= ~HCuPINT_SUSP; + + pr_info("%s: SUSPEND IRQ\n", __func__); + } + + if (irqstat & HCuPINT_CLKRDY) { + isp1362_hcd->irq_stat[ISP1362_INT_CLKRDY]++; + handled = 1; + isp1362_hcd->irqenb &= ~HCuPINT_CLKRDY; + svc_mask &= ~HCuPINT_CLKRDY; + pr_info("%s: CLKRDY IRQ\n", __func__); + } + + if (svc_mask) + pr_err("%s: Unserviced interrupt(s) %04x\n", __func__, svc_mask); + + isp1362_write_reg16(isp1362_hcd, HCuPINTENB, isp1362_hcd->irqenb); + isp1362_hcd->irq_active--; + spin_unlock(&isp1362_hcd->lock); + + return IRQ_RETVAL(handled); +} + +/*-------------------------------------------------------------------------*/ + +#define MAX_PERIODIC_LOAD 900 /* out of 1000 usec */ +static int balance(struct isp1362_hcd *isp1362_hcd, u16 interval, u16 load) +{ + int i, branch = -ENOSPC; + + /* search for the least loaded schedule branch of that interval + * which has enough bandwidth left unreserved. + */ + for (i = 0; i < interval; i++) { + if (branch < 0 || isp1362_hcd->load[branch] > isp1362_hcd->load[i]) { + int j; + + for (j = i; j < PERIODIC_SIZE; j += interval) { + if ((isp1362_hcd->load[j] + load) > MAX_PERIODIC_LOAD) { + pr_err("%s: new load %d load[%02x] %d max %d\n", __func__, + load, j, isp1362_hcd->load[j], MAX_PERIODIC_LOAD); + break; + } + } + if (j < PERIODIC_SIZE) + continue; + branch = i; + } + } + return branch; +} + +/* NB! ALL the code above this point runs with isp1362_hcd->lock + held, irqs off +*/ + +/*-------------------------------------------------------------------------*/ + +static int isp1362_urb_enqueue(struct usb_hcd *hcd, + struct urb *urb, + gfp_t mem_flags) +{ + struct isp1362_hcd *isp1362_hcd = hcd_to_isp1362_hcd(hcd); + struct usb_device *udev = urb->dev; + unsigned int pipe = urb->pipe; + int is_out = !usb_pipein(pipe); + int type = usb_pipetype(pipe); + int epnum = usb_pipeendpoint(pipe); + struct usb_host_endpoint *hep = urb->ep; + struct isp1362_ep *ep = NULL; + unsigned long flags; + int retval = 0; + + DBG(3, "%s: urb %p\n", __func__, urb); + + if (type == PIPE_ISOCHRONOUS) { + pr_err("Isochronous transfers not supported\n"); + return -ENOSPC; + } + + URB_DBG("%s: FA %d ep%d%s %s: len %d %s%s\n", __func__, + usb_pipedevice(pipe), epnum, + is_out ? "out" : "in", + usb_pipecontrol(pipe) ? "ctrl" : + usb_pipeint(pipe) ? "int" : + usb_pipebulk(pipe) ? "bulk" : + "iso", + urb->transfer_buffer_length, + (urb->transfer_flags & URB_ZERO_PACKET) ? "ZERO_PACKET " : "", + !(urb->transfer_flags & URB_SHORT_NOT_OK) ? + "short_ok" : ""); + + /* avoid all allocations within spinlocks: request or endpoint */ + if (!hep->hcpriv) { + ep = kcalloc(1, sizeof *ep, mem_flags); + if (!ep) + return -ENOMEM; + } + spin_lock_irqsave(&isp1362_hcd->lock, flags); + + /* don't submit to a dead or disabled port */ + if (!((isp1362_hcd->rhport[0] | isp1362_hcd->rhport[1]) & + (1 << USB_PORT_FEAT_ENABLE)) || + !HC_IS_RUNNING(hcd->state)) { + kfree(ep); + retval = -ENODEV; + goto fail_not_linked; + } + + retval = usb_hcd_link_urb_to_ep(hcd, urb); + if (retval) { + kfree(ep); + goto fail_not_linked; + } + + if (hep->hcpriv) { + ep = hep->hcpriv; + } else { + INIT_LIST_HEAD(&ep->schedule); + INIT_LIST_HEAD(&ep->active); + INIT_LIST_HEAD(&ep->remove_list); + ep->udev = usb_get_dev(udev); + ep->hep = hep; + ep->epnum = epnum; + ep->maxpacket = usb_maxpacket(udev, urb->pipe, is_out); + ep->ptd_offset = -EINVAL; + ep->ptd_index = -EINVAL; + usb_settoggle(udev, epnum, is_out, 0); + + if (type == PIPE_CONTROL) + ep->nextpid = USB_PID_SETUP; + else if (is_out) + ep->nextpid = USB_PID_OUT; + else + ep->nextpid = USB_PID_IN; + + switch (type) { + case PIPE_ISOCHRONOUS: + case PIPE_INTERRUPT: + if (urb->interval > PERIODIC_SIZE) + urb->interval = PERIODIC_SIZE; + ep->interval = urb->interval; + ep->branch = PERIODIC_SIZE; + ep->load = usb_calc_bus_time(udev->speed, !is_out, + (type == PIPE_ISOCHRONOUS), + usb_maxpacket(udev, pipe, is_out)) / 1000; + break; + } + hep->hcpriv = ep; + } + ep->num_req = isp1362_hcd->req_serial++; + + /* maybe put endpoint into schedule */ + switch (type) { + case PIPE_CONTROL: + case PIPE_BULK: + if (list_empty(&ep->schedule)) { + DBG(1, "%s: Adding ep %p req %d to async schedule\n", + __func__, ep, ep->num_req); + list_add_tail(&ep->schedule, &isp1362_hcd->async); + } + break; + case PIPE_ISOCHRONOUS: + case PIPE_INTERRUPT: + urb->interval = ep->interval; + + /* urb submitted for already existing EP */ + if (ep->branch < PERIODIC_SIZE) + break; + + retval = balance(isp1362_hcd, ep->interval, ep->load); + if (retval < 0) { + pr_err("%s: balance returned %d\n", __func__, retval); + goto fail; + } + ep->branch = retval; + retval = 0; + isp1362_hcd->fmindex = isp1362_read_reg32(isp1362_hcd, HCFMNUM); + DBG(1, "%s: Current frame %04x branch %02x start_frame %04x(%04x)\n", + __func__, isp1362_hcd->fmindex, ep->branch, + ((isp1362_hcd->fmindex + PERIODIC_SIZE - 1) & + ~(PERIODIC_SIZE - 1)) + ep->branch, + (isp1362_hcd->fmindex & (PERIODIC_SIZE - 1)) + ep->branch); + + if (list_empty(&ep->schedule)) { + if (type == PIPE_ISOCHRONOUS) { + u16 frame = isp1362_hcd->fmindex; + + frame += max_t(u16, 8, ep->interval); + frame &= ~(ep->interval - 1); + frame |= ep->branch; + if (frame_before(frame, isp1362_hcd->fmindex)) + frame += ep->interval; + urb->start_frame = frame; + + DBG(1, "%s: Adding ep %p to isoc schedule\n", __func__, ep); + list_add_tail(&ep->schedule, &isp1362_hcd->isoc); + } else { + DBG(1, "%s: Adding ep %p to periodic schedule\n", __func__, ep); + list_add_tail(&ep->schedule, &isp1362_hcd->periodic); + } + } else + DBG(1, "%s: ep %p already scheduled\n", __func__, ep); + + DBG(2, "%s: load %d bandwidth %d -> %d\n", __func__, + ep->load / ep->interval, isp1362_hcd->load[ep->branch], + isp1362_hcd->load[ep->branch] + ep->load); + isp1362_hcd->load[ep->branch] += ep->load; + } + + urb->hcpriv = hep; + ALIGNSTAT(isp1362_hcd, urb->transfer_buffer); + + switch (type) { + case PIPE_CONTROL: + case PIPE_BULK: + start_atl_transfers(isp1362_hcd); + break; + case PIPE_INTERRUPT: + start_intl_transfers(isp1362_hcd); + break; + case PIPE_ISOCHRONOUS: + start_iso_transfers(isp1362_hcd); + break; + default: + BUG(); + } + fail: + if (retval) + usb_hcd_unlink_urb_from_ep(hcd, urb); + + + fail_not_linked: + spin_unlock_irqrestore(&isp1362_hcd->lock, flags); + if (retval) + DBG(0, "%s: urb %p failed with %d\n", __func__, urb, retval); + return retval; +} + +static int isp1362_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) +{ + struct isp1362_hcd *isp1362_hcd = hcd_to_isp1362_hcd(hcd); + struct usb_host_endpoint *hep; + unsigned long flags; + struct isp1362_ep *ep; + int retval = 0; + + DBG(3, "%s: urb %p\n", __func__, urb); + + spin_lock_irqsave(&isp1362_hcd->lock, flags); + retval = usb_hcd_check_unlink_urb(hcd, urb, status); + if (retval) + goto done; + + hep = urb->hcpriv; + + if (!hep) { + spin_unlock_irqrestore(&isp1362_hcd->lock, flags); + return -EIDRM; + } + + ep = hep->hcpriv; + if (ep) { + /* In front of queue? */ + if (ep->hep->urb_list.next == &urb->urb_list) { + if (!list_empty(&ep->active)) { + DBG(1, "%s: urb %p ep %p req %d active PTD[%d] $%04x\n", __func__, + urb, ep, ep->num_req, ep->ptd_index, ep->ptd_offset); + /* disable processing and queue PTD for removal */ + remove_ptd(isp1362_hcd, ep); + urb = NULL; + } + } + if (urb) { + DBG(1, "%s: Finishing ep %p req %d\n", __func__, ep, + ep->num_req); + finish_request(isp1362_hcd, ep, urb, status); + } else + DBG(1, "%s: urb %p active; wait4irq\n", __func__, urb); + } else { + pr_warning("%s: No EP in URB %p\n", __func__, urb); + retval = -EINVAL; + } +done: + spin_unlock_irqrestore(&isp1362_hcd->lock, flags); + + DBG(3, "%s: exit\n", __func__); + + return retval; +} + +static void isp1362_endpoint_disable(struct usb_hcd *hcd, struct usb_host_endpoint *hep) +{ + struct isp1362_ep *ep = hep->hcpriv; + struct isp1362_hcd *isp1362_hcd = hcd_to_isp1362_hcd(hcd); + unsigned long flags; + + DBG(1, "%s: ep %p\n", __func__, ep); + if (!ep) + return; + spin_lock_irqsave(&isp1362_hcd->lock, flags); + if (!list_empty(&hep->urb_list)) { + if (!list_empty(&ep->active) && list_empty(&ep->remove_list)) { + DBG(1, "%s: Removing ep %p req %d PTD[%d] $%04x\n", __func__, + ep, ep->num_req, ep->ptd_index, ep->ptd_offset); + remove_ptd(isp1362_hcd, ep); + pr_info("%s: Waiting for Interrupt to clean up\n", __func__); + } + } + spin_unlock_irqrestore(&isp1362_hcd->lock, flags); + /* Wait for interrupt to clear out active list */ + while (!list_empty(&ep->active)) + msleep(1); + + DBG(1, "%s: Freeing EP %p\n", __func__, ep); + + usb_put_dev(ep->udev); + kfree(ep); + hep->hcpriv = NULL; +} + +static int isp1362_get_frame(struct usb_hcd *hcd) +{ + struct isp1362_hcd *isp1362_hcd = hcd_to_isp1362_hcd(hcd); + u32 fmnum; + unsigned long flags; + + spin_lock_irqsave(&isp1362_hcd->lock, flags); + fmnum = isp1362_read_reg32(isp1362_hcd, HCFMNUM); + spin_unlock_irqrestore(&isp1362_hcd->lock, flags); + + return (int)fmnum; +} + +/*-------------------------------------------------------------------------*/ + +/* Adapted from ohci-hub.c */ +static int isp1362_hub_status_data(struct usb_hcd *hcd, char *buf) +{ + struct isp1362_hcd *isp1362_hcd = hcd_to_isp1362_hcd(hcd); + int ports, i, changed = 0; + unsigned long flags; + + if (!HC_IS_RUNNING(hcd->state)) + return -ESHUTDOWN; + + /* Report no status change now, if we are scheduled to be + called later */ + if (timer_pending(&hcd->rh_timer)) + return 0; + + ports = isp1362_hcd->rhdesca & RH_A_NDP; + BUG_ON(ports > 2); + + spin_lock_irqsave(&isp1362_hcd->lock, flags); + /* init status */ + if (isp1362_hcd->rhstatus & (RH_HS_LPSC | RH_HS_OCIC)) + buf[0] = changed = 1; + else + buf[0] = 0; + + for (i = 0; i < ports; i++) { + u32 status = isp1362_hcd->rhport[i]; + + if (status & (RH_PS_CSC | RH_PS_PESC | RH_PS_PSSC | + RH_PS_OCIC | RH_PS_PRSC)) { + changed = 1; + buf[0] |= 1 << (i + 1); + continue; + } + + if (!(status & RH_PS_CCS)) + continue; + } + spin_unlock_irqrestore(&isp1362_hcd->lock, flags); + return changed; +} + +static void isp1362_hub_descriptor(struct isp1362_hcd *isp1362_hcd, + struct usb_hub_descriptor *desc) +{ + u32 reg = isp1362_hcd->rhdesca; + + DBG(3, "%s: enter\n", __func__); + + desc->bDescriptorType = 0x29; + desc->bDescLength = 9; + desc->bHubContrCurrent = 0; + desc->bNbrPorts = reg & 0x3; + /* Power switching, device type, overcurrent. */ + desc->wHubCharacteristics = cpu_to_le16((reg >> 8) & 0x1f); + DBG(0, "%s: hubcharacteristics = %02x\n", __func__, cpu_to_le16((reg >> 8) & 0x1f)); + desc->bPwrOn2PwrGood = (reg >> 24) & 0xff; + /* two bitmaps: ports removable, and legacy PortPwrCtrlMask */ + desc->bitmap[0] = desc->bNbrPorts == 1 ? 1 << 1 : 3 << 1; + desc->bitmap[1] = ~0; + + DBG(3, "%s: exit\n", __func__); +} + +/* Adapted from ohci-hub.c */ +static int isp1362_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, + u16 wIndex, char *buf, u16 wLength) +{ + struct isp1362_hcd *isp1362_hcd = hcd_to_isp1362_hcd(hcd); + int retval = 0; + unsigned long flags; + unsigned long t1; + int ports = isp1362_hcd->rhdesca & RH_A_NDP; + u32 tmp = 0; + + switch (typeReq) { + case ClearHubFeature: + DBG(0, "ClearHubFeature: "); + switch (wValue) { + case C_HUB_OVER_CURRENT: + _DBG(0, "C_HUB_OVER_CURRENT\n"); + spin_lock_irqsave(&isp1362_hcd->lock, flags); + isp1362_write_reg32(isp1362_hcd, HCRHSTATUS, RH_HS_OCIC); + spin_unlock_irqrestore(&isp1362_hcd->lock, flags); + case C_HUB_LOCAL_POWER: + _DBG(0, "C_HUB_LOCAL_POWER\n"); + break; + default: + goto error; + } + break; + case SetHubFeature: + DBG(0, "SetHubFeature: "); + switch (wValue) { + case C_HUB_OVER_CURRENT: + case C_HUB_LOCAL_POWER: + _DBG(0, "C_HUB_OVER_CURRENT or C_HUB_LOCAL_POWER\n"); + break; + default: + goto error; + } + break; + case GetHubDescriptor: + DBG(0, "GetHubDescriptor\n"); + isp1362_hub_descriptor(isp1362_hcd, (struct usb_hub_descriptor *)buf); + break; + case GetHubStatus: + DBG(0, "GetHubStatus\n"); + put_unaligned(cpu_to_le32(0), (__le32 *) buf); + break; + case GetPortStatus: +#ifndef VERBOSE + DBG(0, "GetPortStatus\n"); +#endif + if (!wIndex || wIndex > ports) + goto error; + tmp = isp1362_hcd->rhport[--wIndex]; + put_unaligned(cpu_to_le32(tmp), (__le32 *) buf); + break; + case ClearPortFeature: + DBG(0, "ClearPortFeature: "); + if (!wIndex || wIndex > ports) + goto error; + wIndex--; + + switch (wValue) { + case USB_PORT_FEAT_ENABLE: + _DBG(0, "USB_PORT_FEAT_ENABLE\n"); + tmp = RH_PS_CCS; + break; + case USB_PORT_FEAT_C_ENABLE: + _DBG(0, "USB_PORT_FEAT_C_ENABLE\n"); + tmp = RH_PS_PESC; + break; + case USB_PORT_FEAT_SUSPEND: + _DBG(0, "USB_PORT_FEAT_SUSPEND\n"); + tmp = RH_PS_POCI; + break; + case USB_PORT_FEAT_C_SUSPEND: + _DBG(0, "USB_PORT_FEAT_C_SUSPEND\n"); + tmp = RH_PS_PSSC; + break; + case USB_PORT_FEAT_POWER: + _DBG(0, "USB_PORT_FEAT_POWER\n"); + tmp = RH_PS_LSDA; + + break; + case USB_PORT_FEAT_C_CONNECTION: + _DBG(0, "USB_PORT_FEAT_C_CONNECTION\n"); + tmp = RH_PS_CSC; + break; + case USB_PORT_FEAT_C_OVER_CURRENT: + _DBG(0, "USB_PORT_FEAT_C_OVER_CURRENT\n"); + tmp = RH_PS_OCIC; + break; + case USB_PORT_FEAT_C_RESET: + _DBG(0, "USB_PORT_FEAT_C_RESET\n"); + tmp = RH_PS_PRSC; + break; + default: + goto error; + } + + spin_lock_irqsave(&isp1362_hcd->lock, flags); + isp1362_write_reg32(isp1362_hcd, HCRHPORT1 + wIndex, tmp); + isp1362_hcd->rhport[wIndex] = + isp1362_read_reg32(isp1362_hcd, HCRHPORT1 + wIndex); + spin_unlock_irqrestore(&isp1362_hcd->lock, flags); + break; + case SetPortFeature: + DBG(0, "SetPortFeature: "); + if (!wIndex || wIndex > ports) + goto error; + wIndex--; + switch (wValue) { + case USB_PORT_FEAT_SUSPEND: + _DBG(0, "USB_PORT_FEAT_SUSPEND\n"); +#ifdef CONFIG_USB_OTG + if (ohci->hcd.self.otg_port == (wIndex + 1) && + ohci->hcd.self.b_hnp_enable) { + start_hnp(ohci); + break; + } +#endif + spin_lock_irqsave(&isp1362_hcd->lock, flags); + isp1362_write_reg32(isp1362_hcd, HCRHPORT1 + wIndex, RH_PS_PSS); + isp1362_hcd->rhport[wIndex] = + isp1362_read_reg32(isp1362_hcd, HCRHPORT1 + wIndex); + spin_unlock_irqrestore(&isp1362_hcd->lock, flags); + break; + case USB_PORT_FEAT_POWER: + _DBG(0, "USB_PORT_FEAT_POWER\n"); + spin_lock_irqsave(&isp1362_hcd->lock, flags); + isp1362_write_reg32(isp1362_hcd, HCRHPORT1 + wIndex, RH_PS_PPS); + isp1362_hcd->rhport[wIndex] = + isp1362_read_reg32(isp1362_hcd, HCRHPORT1 + wIndex); + spin_unlock_irqrestore(&isp1362_hcd->lock, flags); + break; + case USB_PORT_FEAT_RESET: + _DBG(0, "USB_PORT_FEAT_RESET\n"); + spin_lock_irqsave(&isp1362_hcd->lock, flags); + + t1 = jiffies + msecs_to_jiffies(USB_RESET_WIDTH); + while (time_before(jiffies, t1)) { + /* spin until any current reset finishes */ + for (;;) { + tmp = isp1362_read_reg32(isp1362_hcd, HCRHPORT1 + wIndex); + if (!(tmp & RH_PS_PRS)) + break; + udelay(500); + } + if (!(tmp & RH_PS_CCS)) + break; + /* Reset lasts 10ms (claims datasheet) */ + isp1362_write_reg32(isp1362_hcd, HCRHPORT1 + wIndex, (RH_PS_PRS)); + + spin_unlock_irqrestore(&isp1362_hcd->lock, flags); + msleep(10); + spin_lock_irqsave(&isp1362_hcd->lock, flags); + } + + isp1362_hcd->rhport[wIndex] = isp1362_read_reg32(isp1362_hcd, + HCRHPORT1 + wIndex); + spin_unlock_irqrestore(&isp1362_hcd->lock, flags); + break; + default: + goto error; + } + break; + + default: + error: + /* "protocol stall" on error */ + _DBG(0, "PROTOCOL STALL\n"); + retval = -EPIPE; + } + + return retval; +} + +#ifdef CONFIG_PM +static int isp1362_bus_suspend(struct usb_hcd *hcd) +{ + int status = 0; + struct isp1362_hcd *isp1362_hcd = hcd_to_isp1362_hcd(hcd); + unsigned long flags; + + if (time_before(jiffies, isp1362_hcd->next_statechange)) + msleep(5); + + spin_lock_irqsave(&isp1362_hcd->lock, flags); + + isp1362_hcd->hc_control = isp1362_read_reg32(isp1362_hcd, HCCONTROL); + switch (isp1362_hcd->hc_control & OHCI_CTRL_HCFS) { + case OHCI_USB_RESUME: + DBG(0, "%s: resume/suspend?\n", __func__); + isp1362_hcd->hc_control &= ~OHCI_CTRL_HCFS; + isp1362_hcd->hc_control |= OHCI_USB_RESET; + isp1362_write_reg32(isp1362_hcd, HCCONTROL, isp1362_hcd->hc_control); + /* FALL THROUGH */ + case OHCI_USB_RESET: + status = -EBUSY; + pr_warning("%s: needs reinit!\n", __func__); + goto done; + case OHCI_USB_SUSPEND: + pr_warning("%s: already suspended?\n", __func__); + goto done; + } + DBG(0, "%s: suspend root hub\n", __func__); + + /* First stop any processing */ + hcd->state = HC_STATE_QUIESCING; + if (!list_empty(&isp1362_hcd->atl_queue.active) || + !list_empty(&isp1362_hcd->intl_queue.active) || + !list_empty(&isp1362_hcd->istl_queue[0] .active) || + !list_empty(&isp1362_hcd->istl_queue[1] .active)) { + int limit; + + isp1362_write_reg32(isp1362_hcd, HCATLSKIP, ~0); + isp1362_write_reg32(isp1362_hcd, HCINTLSKIP, ~0); + isp1362_write_reg16(isp1362_hcd, HCBUFSTAT, 0); + isp1362_write_reg16(isp1362_hcd, HCuPINTENB, 0); + isp1362_write_reg32(isp1362_hcd, HCINTSTAT, OHCI_INTR_SF); + + DBG(0, "%s: stopping schedules ...\n", __func__); + limit = 2000; + while (limit > 0) { + udelay(250); + limit -= 250; + if (isp1362_read_reg32(isp1362_hcd, HCINTSTAT) & OHCI_INTR_SF) + break; + } + mdelay(7); + if (isp1362_read_reg16(isp1362_hcd, HCuPINT) & HCuPINT_ATL) { + u32 done_map = isp1362_read_reg32(isp1362_hcd, HCATLDONE); + finish_transfers(isp1362_hcd, done_map, &isp1362_hcd->atl_queue); + } + if (isp1362_read_reg16(isp1362_hcd, HCuPINT) & HCuPINT_INTL) { + u32 done_map = isp1362_read_reg32(isp1362_hcd, HCINTLDONE); + finish_transfers(isp1362_hcd, done_map, &isp1362_hcd->intl_queue); + } + if (isp1362_read_reg16(isp1362_hcd, HCuPINT) & HCuPINT_ISTL0) + finish_iso_transfers(isp1362_hcd, &isp1362_hcd->istl_queue[0]); + if (isp1362_read_reg16(isp1362_hcd, HCuPINT) & HCuPINT_ISTL1) + finish_iso_transfers(isp1362_hcd, &isp1362_hcd->istl_queue[1]); + } + DBG(0, "%s: HCINTSTAT: %08x\n", __func__, + isp1362_read_reg32(isp1362_hcd, HCINTSTAT)); + isp1362_write_reg32(isp1362_hcd, HCINTSTAT, + isp1362_read_reg32(isp1362_hcd, HCINTSTAT)); + + /* Suspend hub */ + isp1362_hcd->hc_control = OHCI_USB_SUSPEND; + isp1362_show_reg(isp1362_hcd, HCCONTROL); + isp1362_write_reg32(isp1362_hcd, HCCONTROL, isp1362_hcd->hc_control); + isp1362_show_reg(isp1362_hcd, HCCONTROL); + +#if 1 + isp1362_hcd->hc_control = isp1362_read_reg32(isp1362_hcd, HCCONTROL); + if ((isp1362_hcd->hc_control & OHCI_CTRL_HCFS) != OHCI_USB_SUSPEND) { + pr_err("%s: controller won't suspend %08x\n", __func__, + isp1362_hcd->hc_control); + status = -EBUSY; + } else +#endif + { + /* no resumes until devices finish suspending */ + isp1362_hcd->next_statechange = jiffies + msecs_to_jiffies(5); + } +done: + if (status == 0) { + hcd->state = HC_STATE_SUSPENDED; + DBG(0, "%s: HCD suspended: %08x\n", __func__, + isp1362_read_reg32(isp1362_hcd, HCCONTROL)); + } + spin_unlock_irqrestore(&isp1362_hcd->lock, flags); + return status; +} + +static int isp1362_bus_resume(struct usb_hcd *hcd) +{ + struct isp1362_hcd *isp1362_hcd = hcd_to_isp1362_hcd(hcd); + u32 port; + unsigned long flags; + int status = -EINPROGRESS; + + if (time_before(jiffies, isp1362_hcd->next_statechange)) + msleep(5); + + spin_lock_irqsave(&isp1362_hcd->lock, flags); + isp1362_hcd->hc_control = isp1362_read_reg32(isp1362_hcd, HCCONTROL); + pr_info("%s: HCCONTROL: %08x\n", __func__, isp1362_hcd->hc_control); + if (hcd->state == HC_STATE_RESUMING) { + pr_warning("%s: duplicate resume\n", __func__); + status = 0; + } else + switch (isp1362_hcd->hc_control & OHCI_CTRL_HCFS) { + case OHCI_USB_SUSPEND: + DBG(0, "%s: resume root hub\n", __func__); + isp1362_hcd->hc_control &= ~OHCI_CTRL_HCFS; + isp1362_hcd->hc_control |= OHCI_USB_RESUME; + isp1362_write_reg32(isp1362_hcd, HCCONTROL, isp1362_hcd->hc_control); + break; + case OHCI_USB_RESUME: + /* HCFS changes sometime after INTR_RD */ + DBG(0, "%s: remote wakeup\n", __func__); + break; + case OHCI_USB_OPER: + DBG(0, "%s: odd resume\n", __func__); + status = 0; + hcd->self.root_hub->dev.power.power_state = PMSG_ON; + break; + default: /* RESET, we lost power */ + DBG(0, "%s: root hub hardware reset\n", __func__); + status = -EBUSY; + } + spin_unlock_irqrestore(&isp1362_hcd->lock, flags); + if (status == -EBUSY) { + DBG(0, "%s: Restarting HC\n", __func__); + isp1362_hc_stop(hcd); + return isp1362_hc_start(hcd); + } + if (status != -EINPROGRESS) + return status; + spin_lock_irqsave(&isp1362_hcd->lock, flags); + port = isp1362_read_reg32(isp1362_hcd, HCRHDESCA) & RH_A_NDP; + while (port--) { + u32 stat = isp1362_read_reg32(isp1362_hcd, HCRHPORT1 + port); + + /* force global, not selective, resume */ + if (!(stat & RH_PS_PSS)) { + DBG(0, "%s: Not Resuming RH port %d\n", __func__, port); + continue; + } + DBG(0, "%s: Resuming RH port %d\n", __func__, port); + isp1362_write_reg32(isp1362_hcd, HCRHPORT1 + port, RH_PS_POCI); + } + spin_unlock_irqrestore(&isp1362_hcd->lock, flags); + + /* Some controllers (lucent) need extra-long delays */ + hcd->state = HC_STATE_RESUMING; + mdelay(20 /* usb 11.5.1.10 */ + 15); + + isp1362_hcd->hc_control = OHCI_USB_OPER; + spin_lock_irqsave(&isp1362_hcd->lock, flags); + isp1362_show_reg(isp1362_hcd, HCCONTROL); + isp1362_write_reg32(isp1362_hcd, HCCONTROL, isp1362_hcd->hc_control); + spin_unlock_irqrestore(&isp1362_hcd->lock, flags); + /* TRSMRCY */ + msleep(10); + + /* keep it alive for ~5x suspend + resume costs */ + isp1362_hcd->next_statechange = jiffies + msecs_to_jiffies(250); + + hcd->self.root_hub->dev.power.power_state = PMSG_ON; + hcd->state = HC_STATE_RUNNING; + return 0; +} +#else +#define isp1362_bus_suspend NULL +#define isp1362_bus_resume NULL +#endif + +/*-------------------------------------------------------------------------*/ + +#ifdef STUB_DEBUG_FILE + +static inline void create_debug_file(struct isp1362_hcd *isp1362_hcd) +{ +} +static inline void remove_debug_file(struct isp1362_hcd *isp1362_hcd) +{ +} + +#else + +#include <linux/proc_fs.h> +#include <linux/seq_file.h> + +static void dump_irq(struct seq_file *s, char *label, u16 mask) +{ + seq_printf(s, "%-15s %04x%s%s%s%s%s%s\n", label, mask, + mask & HCuPINT_CLKRDY ? " clkrdy" : "", + mask & HCuPINT_SUSP ? " susp" : "", + mask & HCuPINT_OPR ? " opr" : "", + mask & HCuPINT_EOT ? " eot" : "", + mask & HCuPINT_ATL ? " atl" : "", + mask & HCuPINT_SOF ? " sof" : ""); +} + +static void dump_int(struct seq_file *s, char *label, u32 mask) +{ + seq_printf(s, "%-15s %08x%s%s%s%s%s%s%s\n", label, mask, + mask & OHCI_INTR_MIE ? " MIE" : "", + mask & OHCI_INTR_RHSC ? " rhsc" : "", + mask & OHCI_INTR_FNO ? " fno" : "", + mask & OHCI_INTR_UE ? " ue" : "", + mask & OHCI_INTR_RD ? " rd" : "", + mask & OHCI_INTR_SF ? " sof" : "", + mask & OHCI_INTR_SO ? " so" : ""); +} + +static void dump_ctrl(struct seq_file *s, char *label, u32 mask) +{ + seq_printf(s, "%-15s %08x%s%s%s\n", label, mask, + mask & OHCI_CTRL_RWC ? " rwc" : "", + mask & OHCI_CTRL_RWE ? " rwe" : "", + ({ + char *hcfs; + switch (mask & OHCI_CTRL_HCFS) { + case OHCI_USB_OPER: + hcfs = " oper"; + break; + case OHCI_USB_RESET: + hcfs = " reset"; + break; + case OHCI_USB_RESUME: + hcfs = " resume"; + break; + case OHCI_USB_SUSPEND: + hcfs = " suspend"; + break; + default: + hcfs = " ?"; + } + hcfs; + })); +} + +static void dump_regs(struct seq_file *s, struct isp1362_hcd *isp1362_hcd) +{ + seq_printf(s, "HCREVISION [%02x] %08x\n", ISP1362_REG_NO(ISP1362_REG_HCREVISION), + isp1362_read_reg32(isp1362_hcd, HCREVISION)); + seq_printf(s, "HCCONTROL [%02x] %08x\n", ISP1362_REG_NO(ISP1362_REG_HCCONTROL), + isp1362_read_reg32(isp1362_hcd, HCCONTROL)); + seq_printf(s, "HCCMDSTAT [%02x] %08x\n", ISP1362_REG_NO(ISP1362_REG_HCCMDSTAT), + isp1362_read_reg32(isp1362_hcd, HCCMDSTAT)); + seq_printf(s, "HCINTSTAT [%02x] %08x\n", ISP1362_REG_NO(ISP1362_REG_HCINTSTAT), + isp1362_read_reg32(isp1362_hcd, HCINTSTAT)); + seq_printf(s, "HCINTENB [%02x] %08x\n", ISP1362_REG_NO(ISP1362_REG_HCINTENB), + isp1362_read_reg32(isp1362_hcd, HCINTENB)); + seq_printf(s, "HCFMINTVL [%02x] %08x\n", ISP1362_REG_NO(ISP1362_REG_HCFMINTVL), + isp1362_read_reg32(isp1362_hcd, HCFMINTVL)); + seq_printf(s, "HCFMREM [%02x] %08x\n", ISP1362_REG_NO(ISP1362_REG_HCFMREM), + isp1362_read_reg32(isp1362_hcd, HCFMREM)); + seq_printf(s, "HCFMNUM [%02x] %08x\n", ISP1362_REG_NO(ISP1362_REG_HCFMNUM), + isp1362_read_reg32(isp1362_hcd, HCFMNUM)); + seq_printf(s, "HCLSTHRESH [%02x] %08x\n", ISP1362_REG_NO(ISP1362_REG_HCLSTHRESH), + isp1362_read_reg32(isp1362_hcd, HCLSTHRESH)); + seq_printf(s, "HCRHDESCA [%02x] %08x\n", ISP1362_REG_NO(ISP1362_REG_HCRHDESCA), + isp1362_read_reg32(isp1362_hcd, HCRHDESCA)); + seq_printf(s, "HCRHDESCB [%02x] %08x\n", ISP1362_REG_NO(ISP1362_REG_HCRHDESCB), + isp1362_read_reg32(isp1362_hcd, HCRHDESCB)); + seq_printf(s, "HCRHSTATUS [%02x] %08x\n", ISP1362_REG_NO(ISP1362_REG_HCRHSTATUS), + isp1362_read_reg32(isp1362_hcd, HCRHSTATUS)); + seq_printf(s, "HCRHPORT1 [%02x] %08x\n", ISP1362_REG_NO(ISP1362_REG_HCRHPORT1), + isp1362_read_reg32(isp1362_hcd, HCRHPORT1)); + seq_printf(s, "HCRHPORT2 [%02x] %08x\n", ISP1362_REG_NO(ISP1362_REG_HCRHPORT2), + isp1362_read_reg32(isp1362_hcd, HCRHPORT2)); + seq_printf(s, "\n"); + seq_printf(s, "HCHWCFG [%02x] %04x\n", ISP1362_REG_NO(ISP1362_REG_HCHWCFG), + isp1362_read_reg16(isp1362_hcd, HCHWCFG)); + seq_printf(s, "HCDMACFG [%02x] %04x\n", ISP1362_REG_NO(ISP1362_REG_HCDMACFG), + isp1362_read_reg16(isp1362_hcd, HCDMACFG)); + seq_printf(s, "HCXFERCTR [%02x] %04x\n", ISP1362_REG_NO(ISP1362_REG_HCXFERCTR), + isp1362_read_reg16(isp1362_hcd, HCXFERCTR)); + seq_printf(s, "HCuPINT [%02x] %04x\n", ISP1362_REG_NO(ISP1362_REG_HCuPINT), + isp1362_read_reg16(isp1362_hcd, HCuPINT)); + seq_printf(s, "HCuPINTENB [%02x] %04x\n", ISP1362_REG_NO(ISP1362_REG_HCuPINTENB), + isp1362_read_reg16(isp1362_hcd, HCuPINTENB)); + seq_printf(s, "HCCHIPID [%02x] %04x\n", ISP1362_REG_NO(ISP1362_REG_HCCHIPID), + isp1362_read_reg16(isp1362_hcd, HCCHIPID)); + seq_printf(s, "HCSCRATCH [%02x] %04x\n", ISP1362_REG_NO(ISP1362_REG_HCSCRATCH), + isp1362_read_reg16(isp1362_hcd, HCSCRATCH)); + seq_printf(s, "HCBUFSTAT [%02x] %04x\n", ISP1362_REG_NO(ISP1362_REG_HCBUFSTAT), + isp1362_read_reg16(isp1362_hcd, HCBUFSTAT)); + seq_printf(s, "HCDIRADDR [%02x] %08x\n", ISP1362_REG_NO(ISP1362_REG_HCDIRADDR), + isp1362_read_reg32(isp1362_hcd, HCDIRADDR)); +#if 0 + seq_printf(s, "HCDIRDATA [%02x] %04x\n", ISP1362_REG_NO(HCDIRDATA), + isp1362_read_reg16(isp1362_hcd, HCDIRDATA)); +#endif + seq_printf(s, "HCISTLBUFSZ[%02x] %04x\n", ISP1362_REG_NO(ISP1362_REG_HCISTLBUFSZ), + isp1362_read_reg16(isp1362_hcd, HCISTLBUFSZ)); + seq_printf(s, "HCISTLRATE [%02x] %04x\n", ISP1362_REG_NO(ISP1362_REG_HCISTLRATE), + isp1362_read_reg16(isp1362_hcd, HCISTLRATE)); + seq_printf(s, "\n"); + seq_printf(s, "HCINTLBUFSZ[%02x] %04x\n", ISP1362_REG_NO(ISP1362_REG_HCINTLBUFSZ), + isp1362_read_reg16(isp1362_hcd, HCINTLBUFSZ)); + seq_printf(s, "HCINTLBLKSZ[%02x] %04x\n", ISP1362_REG_NO(ISP1362_REG_HCINTLBLKSZ), + isp1362_read_reg16(isp1362_hcd, HCINTLBLKSZ)); + seq_printf(s, "HCINTLDONE [%02x] %08x\n", ISP1362_REG_NO(ISP1362_REG_HCINTLDONE), + isp1362_read_reg32(isp1362_hcd, HCINTLDONE)); + seq_printf(s, "HCINTLSKIP [%02x] %08x\n", ISP1362_REG_NO(ISP1362_REG_HCINTLSKIP), + isp1362_read_reg32(isp1362_hcd, HCINTLSKIP)); + seq_printf(s, "HCINTLLAST [%02x] %08x\n", ISP1362_REG_NO(ISP1362_REG_HCINTLLAST), + isp1362_read_reg32(isp1362_hcd, HCINTLLAST)); + seq_printf(s, "HCINTLCURR [%02x] %04x\n", ISP1362_REG_NO(ISP1362_REG_HCINTLCURR), + isp1362_read_reg16(isp1362_hcd, HCINTLCURR)); + seq_printf(s, "\n"); + seq_printf(s, "HCATLBUFSZ [%02x] %04x\n", ISP1362_REG_NO(ISP1362_REG_HCATLBUFSZ), + isp1362_read_reg16(isp1362_hcd, HCATLBUFSZ)); + seq_printf(s, "HCATLBLKSZ [%02x] %04x\n", ISP1362_REG_NO(ISP1362_REG_HCATLBLKSZ), + isp1362_read_reg16(isp1362_hcd, HCATLBLKSZ)); +#if 0 + seq_printf(s, "HCATLDONE [%02x] %08x\n", ISP1362_REG_NO(ISP1362_REG_HCATLDONE), + isp1362_read_reg32(isp1362_hcd, HCATLDONE)); +#endif + seq_printf(s, "HCATLSKIP [%02x] %08x\n", ISP1362_REG_NO(ISP1362_REG_HCATLSKIP), + isp1362_read_reg32(isp1362_hcd, HCATLSKIP)); + seq_printf(s, "HCATLLAST [%02x] %08x\n", ISP1362_REG_NO(ISP1362_REG_HCATLLAST), + isp1362_read_reg32(isp1362_hcd, HCATLLAST)); + seq_printf(s, "HCATLCURR [%02x] %04x\n", ISP1362_REG_NO(ISP1362_REG_HCATLCURR), + isp1362_read_reg16(isp1362_hcd, HCATLCURR)); + seq_printf(s, "\n"); + seq_printf(s, "HCATLDTC [%02x] %04x\n", ISP1362_REG_NO(ISP1362_REG_HCATLDTC), + isp1362_read_reg16(isp1362_hcd, HCATLDTC)); + seq_printf(s, "HCATLDTCTO [%02x] %04x\n", ISP1362_REG_NO(ISP1362_REG_HCATLDTCTO), + isp1362_read_reg16(isp1362_hcd, HCATLDTCTO)); +} + +static int proc_isp1362_show(struct seq_file *s, void *unused) +{ + struct isp1362_hcd *isp1362_hcd = s->private; + struct isp1362_ep *ep; + int i; + + seq_printf(s, "%s\n%s version %s\n", + isp1362_hcd_to_hcd(isp1362_hcd)->product_desc, hcd_name, DRIVER_VERSION); + + /* collect statistics to help estimate potential win for + * DMA engines that care about alignment (PXA) + */ + seq_printf(s, "alignment: 16b/%ld 8b/%ld 4b/%ld 2b/%ld 1b/%ld\n", + isp1362_hcd->stat16, isp1362_hcd->stat8, isp1362_hcd->stat4, + isp1362_hcd->stat2, isp1362_hcd->stat1); + seq_printf(s, "max # ptds in ATL fifo: %d\n", isp1362_hcd->atl_queue.stat_maxptds); + seq_printf(s, "max # ptds in INTL fifo: %d\n", isp1362_hcd->intl_queue.stat_maxptds); + seq_printf(s, "max # ptds in ISTL fifo: %d\n", + max(isp1362_hcd->istl_queue[0] .stat_maxptds, + isp1362_hcd->istl_queue[1] .stat_maxptds)); + + /* FIXME: don't show the following in suspended state */ + spin_lock_irq(&isp1362_hcd->lock); + + dump_irq(s, "hc_irq_enable", isp1362_read_reg16(isp1362_hcd, HCuPINTENB)); + dump_irq(s, "hc_irq_status", isp1362_read_reg16(isp1362_hcd, HCuPINT)); + dump_int(s, "ohci_int_enable", isp1362_read_reg32(isp1362_hcd, HCINTENB)); + dump_int(s, "ohci_int_status", isp1362_read_reg32(isp1362_hcd, HCINTSTAT)); + dump_ctrl(s, "ohci_control", isp1362_read_reg32(isp1362_hcd, HCCONTROL)); + + for (i = 0; i < NUM_ISP1362_IRQS; i++) + if (isp1362_hcd->irq_stat[i]) + seq_printf(s, "%-15s: %d\n", + ISP1362_INT_NAME(i), isp1362_hcd->irq_stat[i]); + + dump_regs(s, isp1362_hcd); + list_for_each_entry(ep, &isp1362_hcd->async, schedule) { + struct urb *urb; + + seq_printf(s, "%p, ep%d%s, maxpacket %d:\n", ep, ep->epnum, + ({ + char *s; + switch (ep->nextpid) { + case USB_PID_IN: + s = "in"; + break; + case USB_PID_OUT: + s = "out"; + break; + case USB_PID_SETUP: + s = "setup"; + break; + case USB_PID_ACK: + s = "status"; + break; + default: + s = "?"; + break; + }; + s;}), ep->maxpacket) ; + list_for_each_entry(urb, &ep->hep->urb_list, urb_list) { + seq_printf(s, " urb%p, %d/%d\n", urb, + urb->actual_length, + urb->transfer_buffer_length); + } + } + if (!list_empty(&isp1362_hcd->async)) + seq_printf(s, "\n"); + dump_ptd_queue(&isp1362_hcd->atl_queue); + + seq_printf(s, "periodic size= %d\n", PERIODIC_SIZE); + + list_for_each_entry(ep, &isp1362_hcd->periodic, schedule) { + seq_printf(s, "branch:%2d load:%3d PTD[%d] $%04x:\n", ep->branch, + isp1362_hcd->load[ep->branch], ep->ptd_index, ep->ptd_offset); + + seq_printf(s, " %d/%p (%sdev%d ep%d%s max %d)\n", + ep->interval, ep, + (ep->udev->speed == USB_SPEED_FULL) ? "" : "ls ", + ep->udev->devnum, ep->epnum, + (ep->epnum == 0) ? "" : + ((ep->nextpid == USB_PID_IN) ? + "in" : "out"), ep->maxpacket); + } + dump_ptd_queue(&isp1362_hcd->intl_queue); + + seq_printf(s, "ISO:\n"); + + list_for_each_entry(ep, &isp1362_hcd->isoc, schedule) { + seq_printf(s, " %d/%p (%sdev%d ep%d%s max %d)\n", + ep->interval, ep, + (ep->udev->speed == USB_SPEED_FULL) ? "" : "ls ", + ep->udev->devnum, ep->epnum, + (ep->epnum == 0) ? "" : + ((ep->nextpid == USB_PID_IN) ? + "in" : "out"), ep->maxpacket); + } + + spin_unlock_irq(&isp1362_hcd->lock); + seq_printf(s, "\n"); + + return 0; +} + +static int proc_isp1362_open(struct inode *inode, struct file *file) +{ + return single_open(file, proc_isp1362_show, PDE(inode)->data); +} + +static const struct file_operations proc_ops = { + .open = proc_isp1362_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +/* expect just one isp1362_hcd per system */ +static const char proc_filename[] = "driver/isp1362"; + +static void create_debug_file(struct isp1362_hcd *isp1362_hcd) +{ + struct proc_dir_entry *pde; + + pde = create_proc_entry(proc_filename, 0, NULL); + if (pde == NULL) { + pr_warning("%s: Failed to create debug file '%s'\n", __func__, proc_filename); + return; + } + + pde->proc_fops = &proc_ops; + pde->data = isp1362_hcd; + isp1362_hcd->pde = pde; +} + +static void remove_debug_file(struct isp1362_hcd *isp1362_hcd) +{ + if (isp1362_hcd->pde) + remove_proc_entry(proc_filename, 0); +} + +#endif + +/*-------------------------------------------------------------------------*/ + +static void isp1362_sw_reset(struct isp1362_hcd *isp1362_hcd) +{ + int tmp = 20; + unsigned long flags; + + spin_lock_irqsave(&isp1362_hcd->lock, flags); + + isp1362_write_reg16(isp1362_hcd, HCSWRES, HCSWRES_MAGIC); + isp1362_write_reg32(isp1362_hcd, HCCMDSTAT, OHCI_HCR); + while (--tmp) { + mdelay(1); + if (!(isp1362_read_reg32(isp1362_hcd, HCCMDSTAT) & OHCI_HCR)) + break; + } + if (!tmp) + pr_err("Software reset timeout\n"); + spin_unlock_irqrestore(&isp1362_hcd->lock, flags); +} + +static int isp1362_mem_config(struct usb_hcd *hcd) +{ + struct isp1362_hcd *isp1362_hcd = hcd_to_isp1362_hcd(hcd); + unsigned long flags; + u32 total; + u16 istl_size = ISP1362_ISTL_BUFSIZE; + u16 intl_blksize = ISP1362_INTL_BLKSIZE + PTD_HEADER_SIZE; + u16 intl_size = ISP1362_INTL_BUFFERS * intl_blksize; + u16 atl_blksize = ISP1362_ATL_BLKSIZE + PTD_HEADER_SIZE; + u16 atl_buffers = (ISP1362_BUF_SIZE - (istl_size + intl_size)) / atl_blksize; + u16 atl_size; + int i; + + WARN_ON(istl_size & 3); + WARN_ON(atl_blksize & 3); + WARN_ON(intl_blksize & 3); + WARN_ON(atl_blksize < PTD_HEADER_SIZE); + WARN_ON(intl_blksize < PTD_HEADER_SIZE); + + BUG_ON((unsigned)ISP1362_INTL_BUFFERS > 32); + if (atl_buffers > 32) + atl_buffers = 32; + atl_size = atl_buffers * atl_blksize; + total = atl_size + intl_size + istl_size; + dev_info(hcd->self.controller, "ISP1362 Memory usage:\n"); + dev_info(hcd->self.controller, " ISTL: 2 * %4d: %4d @ $%04x:$%04x\n", + istl_size / 2, istl_size, 0, istl_size / 2); + dev_info(hcd->self.controller, " INTL: %4d * (%3lu+8): %4d @ $%04x\n", + ISP1362_INTL_BUFFERS, intl_blksize - PTD_HEADER_SIZE, + intl_size, istl_size); + dev_info(hcd->self.controller, " ATL : %4d * (%3lu+8): %4d @ $%04x\n", + atl_buffers, atl_blksize - PTD_HEADER_SIZE, + atl_size, istl_size + intl_size); + dev_info(hcd->self.controller, " USED/FREE: %4d %4d\n", total, + ISP1362_BUF_SIZE - total); + + if (total > ISP1362_BUF_SIZE) { + dev_err(hcd->self.controller, "%s: Memory requested: %d, available %d\n", + __func__, total, ISP1362_BUF_SIZE); + return -ENOMEM; + } + + total = istl_size + intl_size + atl_size; + spin_lock_irqsave(&isp1362_hcd->lock, flags); + + for (i = 0; i < 2; i++) { + isp1362_hcd->istl_queue[i].buf_start = i * istl_size / 2, + isp1362_hcd->istl_queue[i].buf_size = istl_size / 2; + isp1362_hcd->istl_queue[i].blk_size = 4; + INIT_LIST_HEAD(&isp1362_hcd->istl_queue[i].active); + snprintf(isp1362_hcd->istl_queue[i].name, + sizeof(isp1362_hcd->istl_queue[i].name), "ISTL%d", i); + DBG(3, "%s: %5s buf $%04x %d\n", __func__, + isp1362_hcd->istl_queue[i].name, + isp1362_hcd->istl_queue[i].buf_start, + isp1362_hcd->istl_queue[i].buf_size); + } + isp1362_write_reg16(isp1362_hcd, HCISTLBUFSZ, istl_size / 2); + + isp1362_hcd->intl_queue.buf_start = istl_size; + isp1362_hcd->intl_queue.buf_size = intl_size; + isp1362_hcd->intl_queue.buf_count = ISP1362_INTL_BUFFERS; + isp1362_hcd->intl_queue.blk_size = intl_blksize; + isp1362_hcd->intl_queue.buf_avail = isp1362_hcd->intl_queue.buf_count; + isp1362_hcd->intl_queue.skip_map = ~0; + INIT_LIST_HEAD(&isp1362_hcd->intl_queue.active); + + isp1362_write_reg16(isp1362_hcd, HCINTLBUFSZ, + isp1362_hcd->intl_queue.buf_size); + isp1362_write_reg16(isp1362_hcd, HCINTLBLKSZ, + isp1362_hcd->intl_queue.blk_size - PTD_HEADER_SIZE); + isp1362_write_reg32(isp1362_hcd, HCINTLSKIP, ~0); + isp1362_write_reg32(isp1362_hcd, HCINTLLAST, + 1 << (ISP1362_INTL_BUFFERS - 1)); + + isp1362_hcd->atl_queue.buf_start = istl_size + intl_size; + isp1362_hcd->atl_queue.buf_size = atl_size; + isp1362_hcd->atl_queue.buf_count = atl_buffers; + isp1362_hcd->atl_queue.blk_size = atl_blksize; + isp1362_hcd->atl_queue.buf_avail = isp1362_hcd->atl_queue.buf_count; + isp1362_hcd->atl_queue.skip_map = ~0; + INIT_LIST_HEAD(&isp1362_hcd->atl_queue.active); + + isp1362_write_reg16(isp1362_hcd, HCATLBUFSZ, + isp1362_hcd->atl_queue.buf_size); + isp1362_write_reg16(isp1362_hcd, HCATLBLKSZ, + isp1362_hcd->atl_queue.blk_size - PTD_HEADER_SIZE); + isp1362_write_reg32(isp1362_hcd, HCATLSKIP, ~0); + isp1362_write_reg32(isp1362_hcd, HCATLLAST, + 1 << (atl_buffers - 1)); + + snprintf(isp1362_hcd->atl_queue.name, + sizeof(isp1362_hcd->atl_queue.name), "ATL"); + snprintf(isp1362_hcd->intl_queue.name, + sizeof(isp1362_hcd->intl_queue.name), "INTL"); + DBG(3, "%s: %5s buf $%04x %2d * %4d = %4d\n", __func__, + isp1362_hcd->intl_queue.name, + isp1362_hcd->intl_queue.buf_start, + ISP1362_INTL_BUFFERS, isp1362_hcd->intl_queue.blk_size, + isp1362_hcd->intl_queue.buf_size); + DBG(3, "%s: %5s buf $%04x %2d * %4d = %4d\n", __func__, + isp1362_hcd->atl_queue.name, + isp1362_hcd->atl_queue.buf_start, + atl_buffers, isp1362_hcd->atl_queue.blk_size, + isp1362_hcd->atl_queue.buf_size); + + spin_unlock_irqrestore(&isp1362_hcd->lock, flags); + + return 0; +} + +static int isp1362_hc_reset(struct usb_hcd *hcd) +{ + int ret = 0; + struct isp1362_hcd *isp1362_hcd = hcd_to_isp1362_hcd(hcd); + unsigned long t; + unsigned long timeout = 100; + unsigned long flags; + int clkrdy = 0; + + pr_info("%s:\n", __func__); + + if (isp1362_hcd->board && isp1362_hcd->board->reset) { + isp1362_hcd->board->reset(hcd->self.controller, 1); + msleep(20); + if (isp1362_hcd->board->clock) + isp1362_hcd->board->clock(hcd->self.controller, 1); + isp1362_hcd->board->reset(hcd->self.controller, 0); + } else + isp1362_sw_reset(isp1362_hcd); + + /* chip has been reset. First we need to see a clock */ + t = jiffies + msecs_to_jiffies(timeout); + while (!clkrdy && time_before_eq(jiffies, t)) { + spin_lock_irqsave(&isp1362_hcd->lock, flags); + clkrdy = isp1362_read_reg16(isp1362_hcd, HCuPINT) & HCuPINT_CLKRDY; + spin_unlock_irqrestore(&isp1362_hcd->lock, flags); + if (!clkrdy) + msleep(4); + } + + spin_lock_irqsave(&isp1362_hcd->lock, flags); + isp1362_write_reg16(isp1362_hcd, HCuPINT, HCuPINT_CLKRDY); + spin_unlock_irqrestore(&isp1362_hcd->lock, flags); + if (!clkrdy) { + pr_err("Clock not ready after %lums\n", timeout); + ret = -ENODEV; + } + return ret; +} + +static void isp1362_hc_stop(struct usb_hcd *hcd) +{ + struct isp1362_hcd *isp1362_hcd = hcd_to_isp1362_hcd(hcd); + unsigned long flags; + u32 tmp; + + pr_info("%s:\n", __func__); + + del_timer_sync(&hcd->rh_timer); + + spin_lock_irqsave(&isp1362_hcd->lock, flags); + + isp1362_write_reg16(isp1362_hcd, HCuPINTENB, 0); + + /* Switch off power for all ports */ + tmp = isp1362_read_reg32(isp1362_hcd, HCRHDESCA); + tmp &= ~(RH_A_NPS | RH_A_PSM); + isp1362_write_reg32(isp1362_hcd, HCRHDESCA, tmp); + isp1362_write_reg32(isp1362_hcd, HCRHSTATUS, RH_HS_LPS); + + /* Reset the chip */ + if (isp1362_hcd->board && isp1362_hcd->board->reset) + isp1362_hcd->board->reset(hcd->self.controller, 1); + else + isp1362_sw_reset(isp1362_hcd); + + if (isp1362_hcd->board && isp1362_hcd->board->clock) + isp1362_hcd->board->clock(hcd->self.controller, 0); + + spin_unlock_irqrestore(&isp1362_hcd->lock, flags); +} + +#ifdef CHIP_BUFFER_TEST +static int isp1362_chip_test(struct isp1362_hcd *isp1362_hcd) +{ + int ret = 0; + u16 *ref; + unsigned long flags; + + ref = kmalloc(2 * ISP1362_BUF_SIZE, GFP_KERNEL); + if (ref) { + int offset; + u16 *tst = &ref[ISP1362_BUF_SIZE / 2]; + + for (offset = 0; offset < ISP1362_BUF_SIZE / 2; offset++) { + ref[offset] = ~offset; + tst[offset] = offset; + } + + for (offset = 0; offset < 4; offset++) { + int j; + + for (j = 0; j < 8; j++) { + spin_lock_irqsave(&isp1362_hcd->lock, flags); + isp1362_write_buffer(isp1362_hcd, (u8 *)ref + offset, 0, j); + isp1362_read_buffer(isp1362_hcd, (u8 *)tst + offset, 0, j); + spin_unlock_irqrestore(&isp1362_hcd->lock, flags); + + if (memcmp(ref, tst, j)) { + ret = -ENODEV; + pr_err("%s: memory check with %d byte offset %d failed\n", + __func__, j, offset); + dump_data((u8 *)ref + offset, j); + dump_data((u8 *)tst + offset, j); + } + } + } + + spin_lock_irqsave(&isp1362_hcd->lock, flags); + isp1362_write_buffer(isp1362_hcd, ref, 0, ISP1362_BUF_SIZE); + isp1362_read_buffer(isp1362_hcd, tst, 0, ISP1362_BUF_SIZE); + spin_unlock_irqrestore(&isp1362_hcd->lock, flags); + + if (memcmp(ref, tst, ISP1362_BUF_SIZE)) { + ret = -ENODEV; + pr_err("%s: memory check failed\n", __func__); + dump_data((u8 *)tst, ISP1362_BUF_SIZE / 2); + } + + for (offset = 0; offset < 256; offset++) { + int test_size = 0; + + yield(); + + memset(tst, 0, ISP1362_BUF_SIZE); + spin_lock_irqsave(&isp1362_hcd->lock, flags); + isp1362_write_buffer(isp1362_hcd, tst, 0, ISP1362_BUF_SIZE); + isp1362_read_buffer(isp1362_hcd, tst, 0, ISP1362_BUF_SIZE); + spin_unlock_irqrestore(&isp1362_hcd->lock, flags); + if (memcmp(tst, tst + (ISP1362_BUF_SIZE / (2 * sizeof(*tst))), + ISP1362_BUF_SIZE / 2)) { + pr_err("%s: Failed to clear buffer\n", __func__); + dump_data((u8 *)tst, ISP1362_BUF_SIZE); + break; + } + spin_lock_irqsave(&isp1362_hcd->lock, flags); + isp1362_write_buffer(isp1362_hcd, ref, offset * 2, PTD_HEADER_SIZE); + isp1362_write_buffer(isp1362_hcd, ref + PTD_HEADER_SIZE / sizeof(*ref), + offset * 2 + PTD_HEADER_SIZE, test_size); + isp1362_read_buffer(isp1362_hcd, tst, offset * 2, + PTD_HEADER_SIZE + test_size); + spin_unlock_irqrestore(&isp1362_hcd->lock, flags); + if (memcmp(ref, tst, PTD_HEADER_SIZE + test_size)) { + dump_data(((u8 *)ref) + offset, PTD_HEADER_SIZE + test_size); + dump_data((u8 *)tst, PTD_HEADER_SIZE + test_size); + spin_lock_irqsave(&isp1362_hcd->lock, flags); + isp1362_read_buffer(isp1362_hcd, tst, offset * 2, + PTD_HEADER_SIZE + test_size); + spin_unlock_irqrestore(&isp1362_hcd->lock, flags); + if (memcmp(ref, tst, PTD_HEADER_SIZE + test_size)) { + ret = -ENODEV; + pr_err("%s: memory check with offset %02x failed\n", + __func__, offset); + break; + } + pr_warning("%s: memory check with offset %02x ok after second read\n", + __func__, offset); + } + } + kfree(ref); + } + return ret; +} +#endif + +static int isp1362_hc_start(struct usb_hcd *hcd) +{ + int ret; + struct isp1362_hcd *isp1362_hcd = hcd_to_isp1362_hcd(hcd); + struct isp1362_platform_data *board = isp1362_hcd->board; + u16 hwcfg; + u16 chipid; + unsigned long flags; + + pr_info("%s:\n", __func__); + + spin_lock_irqsave(&isp1362_hcd->lock, flags); + chipid = isp1362_read_reg16(isp1362_hcd, HCCHIPID); + spin_unlock_irqrestore(&isp1362_hcd->lock, flags); + + if ((chipid & HCCHIPID_MASK) != HCCHIPID_MAGIC) { + pr_err("%s: Invalid chip ID %04x\n", __func__, chipid); + return -ENODEV; + } + +#ifdef CHIP_BUFFER_TEST + ret = isp1362_chip_test(isp1362_hcd); + if (ret) + return -ENODEV; +#endif + spin_lock_irqsave(&isp1362_hcd->lock, flags); + /* clear interrupt status and disable all interrupt sources */ + isp1362_write_reg16(isp1362_hcd, HCuPINT, 0xff); + isp1362_write_reg16(isp1362_hcd, HCuPINTENB, 0); + + /* HW conf */ + hwcfg = HCHWCFG_INT_ENABLE | HCHWCFG_DBWIDTH(1); + if (board->sel15Kres) + hwcfg |= HCHWCFG_PULLDOWN_DS2 | + ((MAX_ROOT_PORTS > 1) ? HCHWCFG_PULLDOWN_DS1 : 0); + if (board->clknotstop) + hwcfg |= HCHWCFG_CLKNOTSTOP; + if (board->oc_enable) + hwcfg |= HCHWCFG_ANALOG_OC; + if (board->int_act_high) + hwcfg |= HCHWCFG_INT_POL; + if (board->int_edge_triggered) + hwcfg |= HCHWCFG_INT_TRIGGER; + if (board->dreq_act_high) + hwcfg |= HCHWCFG_DREQ_POL; + if (board->dack_act_high) + hwcfg |= HCHWCFG_DACK_POL; + isp1362_write_reg16(isp1362_hcd, HCHWCFG, hwcfg); + isp1362_show_reg(isp1362_hcd, HCHWCFG); + isp1362_write_reg16(isp1362_hcd, HCDMACFG, 0); + spin_unlock_irqrestore(&isp1362_hcd->lock, flags); + + ret = isp1362_mem_config(hcd); + if (ret) + return ret; + + spin_lock_irqsave(&isp1362_hcd->lock, flags); + + /* Root hub conf */ + isp1362_hcd->rhdesca = 0; + if (board->no_power_switching) + isp1362_hcd->rhdesca |= RH_A_NPS; + if (board->power_switching_mode) + isp1362_hcd->rhdesca |= RH_A_PSM; + if (board->potpg) + isp1362_hcd->rhdesca |= (board->potpg << 24) & RH_A_POTPGT; + else + isp1362_hcd->rhdesca |= (25 << 24) & RH_A_POTPGT; + + isp1362_write_reg32(isp1362_hcd, HCRHDESCA, isp1362_hcd->rhdesca & ~RH_A_OCPM); + isp1362_write_reg32(isp1362_hcd, HCRHDESCA, isp1362_hcd->rhdesca | RH_A_OCPM); + isp1362_hcd->rhdesca = isp1362_read_reg32(isp1362_hcd, HCRHDESCA); + + isp1362_hcd->rhdescb = RH_B_PPCM; + isp1362_write_reg32(isp1362_hcd, HCRHDESCB, isp1362_hcd->rhdescb); + isp1362_hcd->rhdescb = isp1362_read_reg32(isp1362_hcd, HCRHDESCB); + + isp1362_read_reg32(isp1362_hcd, HCFMINTVL); + isp1362_write_reg32(isp1362_hcd, HCFMINTVL, (FSMP(FI) << 16) | FI); + isp1362_write_reg32(isp1362_hcd, HCLSTHRESH, LSTHRESH); + + spin_unlock_irqrestore(&isp1362_hcd->lock, flags); + + isp1362_hcd->hc_control = OHCI_USB_OPER; + hcd->state = HC_STATE_RUNNING; + + spin_lock_irqsave(&isp1362_hcd->lock, flags); + /* Set up interrupts */ + isp1362_hcd->intenb = OHCI_INTR_MIE | OHCI_INTR_RHSC | OHCI_INTR_UE; + isp1362_hcd->intenb |= OHCI_INTR_RD; + isp1362_hcd->irqenb = HCuPINT_OPR | HCuPINT_SUSP; + isp1362_write_reg32(isp1362_hcd, HCINTENB, isp1362_hcd->intenb); + isp1362_write_reg16(isp1362_hcd, HCuPINTENB, isp1362_hcd->irqenb); + + /* Go operational */ + isp1362_write_reg32(isp1362_hcd, HCCONTROL, isp1362_hcd->hc_control); + /* enable global power */ + isp1362_write_reg32(isp1362_hcd, HCRHSTATUS, RH_HS_LPSC | RH_HS_DRWE); + + spin_unlock_irqrestore(&isp1362_hcd->lock, flags); + + return 0; +} + +/*-------------------------------------------------------------------------*/ + +static struct hc_driver isp1362_hc_driver = { + .description = hcd_name, + .product_desc = "ISP1362 Host Controller", + .hcd_priv_size = sizeof(struct isp1362_hcd), + + .irq = isp1362_irq, + .flags = HCD_USB11 | HCD_MEMORY, + + .reset = isp1362_hc_reset, + .start = isp1362_hc_start, + .stop = isp1362_hc_stop, + + .urb_enqueue = isp1362_urb_enqueue, + .urb_dequeue = isp1362_urb_dequeue, + .endpoint_disable = isp1362_endpoint_disable, + + .get_frame_number = isp1362_get_frame, + + .hub_status_data = isp1362_hub_status_data, + .hub_control = isp1362_hub_control, + .bus_suspend = isp1362_bus_suspend, + .bus_resume = isp1362_bus_resume, +}; + +/*-------------------------------------------------------------------------*/ + +#define resource_len(r) (((r)->end - (r)->start) + 1) + +static int __devexit isp1362_remove(struct platform_device *pdev) +{ + struct usb_hcd *hcd = platform_get_drvdata(pdev); + struct isp1362_hcd *isp1362_hcd = hcd_to_isp1362_hcd(hcd); + struct resource *res; + + remove_debug_file(isp1362_hcd); + DBG(0, "%s: Removing HCD\n", __func__); + usb_remove_hcd(hcd); + + DBG(0, "%s: Unmapping data_reg @ %p\n", __func__, + isp1362_hcd->data_reg); + iounmap(isp1362_hcd->data_reg); + + DBG(0, "%s: Unmapping addr_reg @ %p\n", __func__, + isp1362_hcd->addr_reg); + iounmap(isp1362_hcd->addr_reg); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 1); + DBG(0, "%s: release mem_region: %08lx\n", __func__, (long unsigned int)res->start); + if (res) + release_mem_region(res->start, resource_len(res)); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + DBG(0, "%s: release mem_region: %08lx\n", __func__, (long unsigned int)res->start); + if (res) + release_mem_region(res->start, resource_len(res)); + + DBG(0, "%s: put_hcd\n", __func__); + usb_put_hcd(hcd); + DBG(0, "%s: Done\n", __func__); + + return 0; +} + +static int __init isp1362_probe(struct platform_device *pdev) +{ + struct usb_hcd *hcd; + struct isp1362_hcd *isp1362_hcd; + struct resource *addr, *data; + void __iomem *addr_reg; + void __iomem *data_reg; + int irq; + int retval = 0; + + /* basic sanity checks first. board-specific init logic should + * have initialized this the three resources and probably board + * specific platform_data. we don't probe for IRQs, and do only + * minimal sanity checking. + */ + if (pdev->num_resources < 3) { + retval = -ENODEV; + goto err1; + } + + data = platform_get_resource(pdev, IORESOURCE_MEM, 0); + addr = platform_get_resource(pdev, IORESOURCE_MEM, 1); + irq = platform_get_irq(pdev, 0); + if (!addr || !data || irq < 0) { + retval = -ENODEV; + goto err1; + } + +#ifdef CONFIG_USB_HCD_DMA + if (pdev->dev.dma_mask) { + struct resource *dma_res = platform_get_resource(pdev, IORESOURCE_MEM, 2); + + if (!dma_res) { + retval = -ENODEV; + goto err1; + } + isp1362_hcd->data_dma = dma_res->start; + isp1362_hcd->max_dma_size = resource_len(dma_res); + } +#else + if (pdev->dev.dma_mask) { + DBG(1, "won't do DMA"); + retval = -ENODEV; + goto err1; + } +#endif + + if (!request_mem_region(addr->start, resource_len(addr), hcd_name)) { + retval = -EBUSY; + goto err1; + } + addr_reg = ioremap(addr->start, resource_len(addr)); + if (addr_reg == NULL) { + retval = -ENOMEM; + goto err2; + } + + if (!request_mem_region(data->start, resource_len(data), hcd_name)) { + retval = -EBUSY; + goto err3; + } + data_reg = ioremap(data->start, resource_len(data)); + if (data_reg == NULL) { + retval = -ENOMEM; + goto err4; + } + + /* allocate and initialize hcd */ + hcd = usb_create_hcd(&isp1362_hc_driver, &pdev->dev, dev_name(&pdev->dev)); + if (!hcd) { + retval = -ENOMEM; + goto err5; + } + hcd->rsrc_start = data->start; + isp1362_hcd = hcd_to_isp1362_hcd(hcd); + isp1362_hcd->data_reg = data_reg; + isp1362_hcd->addr_reg = addr_reg; + + isp1362_hcd->next_statechange = jiffies; + spin_lock_init(&isp1362_hcd->lock); + INIT_LIST_HEAD(&isp1362_hcd->async); + INIT_LIST_HEAD(&isp1362_hcd->periodic); + INIT_LIST_HEAD(&isp1362_hcd->isoc); + INIT_LIST_HEAD(&isp1362_hcd->remove_list); + isp1362_hcd->board = pdev->dev.platform_data; +#if USE_PLATFORM_DELAY + if (!isp1362_hcd->board->delay) { + dev_err(hcd->self.controller, "No platform delay function given\n"); + retval = -ENODEV; + goto err6; + } +#endif + +#ifdef CONFIG_ARM + if (isp1362_hcd->board) + set_irq_type(irq, isp1362_hcd->board->int_act_high ? IRQT_RISING : IRQT_FALLING); +#endif + + retval = usb_add_hcd(hcd, irq, IRQF_TRIGGER_LOW | IRQF_DISABLED | IRQF_SHARED); + if (retval != 0) + goto err6; + pr_info("%s, irq %d\n", hcd->product_desc, irq); + + create_debug_file(isp1362_hcd); + + return 0; + + err6: + DBG(0, "%s: Freeing dev %p\n", __func__, isp1362_hcd); + usb_put_hcd(hcd); + err5: + DBG(0, "%s: Unmapping data_reg @ %p\n", __func__, data_reg); + iounmap(data_reg); + err4: + DBG(0, "%s: Releasing mem region %08lx\n", __func__, (long unsigned int)data->start); + release_mem_region(data->start, resource_len(data)); + err3: + DBG(0, "%s: Unmapping addr_reg @ %p\n", __func__, addr_reg); + iounmap(addr_reg); + err2: + DBG(0, "%s: Releasing mem region %08lx\n", __func__, (long unsigned int)addr->start); + release_mem_region(addr->start, resource_len(addr)); + err1: + pr_err("%s: init error, %d\n", __func__, retval); + + return retval; +} + +#ifdef CONFIG_PM +static int isp1362_suspend(struct platform_device *pdev, pm_message_t state) +{ + struct usb_hcd *hcd = platform_get_drvdata(pdev); + struct isp1362_hcd *isp1362_hcd = hcd_to_isp1362_hcd(hcd); + unsigned long flags; + int retval = 0; + + DBG(0, "%s: Suspending device\n", __func__); + + if (state.event == PM_EVENT_FREEZE) { + DBG(0, "%s: Suspending root hub\n", __func__); + retval = isp1362_bus_suspend(hcd); + } else { + DBG(0, "%s: Suspending RH ports\n", __func__); + spin_lock_irqsave(&isp1362_hcd->lock, flags); + isp1362_write_reg32(isp1362_hcd, HCRHSTATUS, RH_HS_LPS); + spin_unlock_irqrestore(&isp1362_hcd->lock, flags); + } + if (retval == 0) + pdev->dev.power.power_state = state; + return retval; +} + +static int isp1362_resume(struct platform_device *pdev) +{ + struct usb_hcd *hcd = platform_get_drvdata(pdev); + struct isp1362_hcd *isp1362_hcd = hcd_to_isp1362_hcd(hcd); + unsigned long flags; + + DBG(0, "%s: Resuming\n", __func__); + + if (pdev->dev.power.power_state.event == PM_EVENT_SUSPEND) { + DBG(0, "%s: Resume RH ports\n", __func__); + spin_lock_irqsave(&isp1362_hcd->lock, flags); + isp1362_write_reg32(isp1362_hcd, HCRHSTATUS, RH_HS_LPSC); + spin_unlock_irqrestore(&isp1362_hcd->lock, flags); + return 0; + } + + pdev->dev.power.power_state = PMSG_ON; + + return isp1362_bus_resume(isp1362_hcd_to_hcd(isp1362_hcd)); +} +#else +#define isp1362_suspend NULL +#define isp1362_resume NULL +#endif + +static struct platform_driver isp1362_driver = { + .probe = isp1362_probe, + .remove = __devexit_p(isp1362_remove), + + .suspend = isp1362_suspend, + .resume = isp1362_resume, + .driver = { + .name = (char *)hcd_name, + .owner = THIS_MODULE, + }, +}; + +/*-------------------------------------------------------------------------*/ + +static int __init isp1362_init(void) +{ + if (usb_disabled()) + return -ENODEV; + pr_info("driver %s, %s\n", hcd_name, DRIVER_VERSION); + return platform_driver_register(&isp1362_driver); +} +module_init(isp1362_init); + +static void __exit isp1362_cleanup(void) +{ + platform_driver_unregister(&isp1362_driver); +} +module_exit(isp1362_cleanup); diff --git a/drivers/usb/host/isp1362.h b/drivers/usb/host/isp1362.h new file mode 100644 index 000000000000..1a253ebf7e50 --- /dev/null +++ b/drivers/usb/host/isp1362.h @@ -0,0 +1,1079 @@ +/* + * ISP1362 HCD (Host Controller Driver) for USB. + * + * COPYRIGHT (C) by L. Wassmann <LW@KARO-electronics.de> + */ + +/* ------------------------------------------------------------------------- */ +/* + * Platform specific compile time options + */ +#if defined(CONFIG_ARCH_KARO) +#include <asm/arch/hardware.h> +#include <asm/arch/pxa-regs.h> +#include <asm/arch/karo.h> + +#define USE_32BIT 1 + + +/* These options are mutually eclusive */ +#define USE_PLATFORM_DELAY 1 +#define USE_NDELAY 0 +/* + * MAX_ROOT_PORTS: Number of downstream ports + * + * The chip has two USB ports, one of which can be configured as + * an USB device port, so the value of this constant is implementation + * specific. + */ +#define MAX_ROOT_PORTS 2 +#define DUMMY_DELAY_ACCESS do {} while (0) + +/* insert platform specific definitions for other machines here */ +#elif defined(CONFIG_BLACKFIN) + +#include <linux/io.h> +#define USE_32BIT 0 +#define MAX_ROOT_PORTS 2 +#define USE_PLATFORM_DELAY 0 +#define USE_NDELAY 1 + +#define DUMMY_DELAY_ACCESS \ + do { \ + bfin_read16(ASYNC_BANK0_BASE); \ + bfin_read16(ASYNC_BANK0_BASE); \ + bfin_read16(ASYNC_BANK0_BASE); \ + } while (0) + +#undef insw +#undef outsw + +#define insw delayed_insw +#define outsw delayed_outsw + +static inline void delayed_outsw(unsigned int addr, void *buf, int len) +{ + unsigned short *bp = (unsigned short *)buf; + while (len--) { + DUMMY_DELAY_ACCESS; + outw(*bp++, addr); + } +} + +static inline void delayed_insw(unsigned int addr, void *buf, int len) +{ + unsigned short *bp = (unsigned short *)buf; + while (len--) { + DUMMY_DELAY_ACCESS; + *bp++ = inw((void *)addr); + } +} + +#else + +#define MAX_ROOT_PORTS 2 + +#define USE_32BIT 0 + +/* These options are mutually eclusive */ +#define USE_PLATFORM_DELAY 0 +#define USE_NDELAY 0 + +#define DUMMY_DELAY_ACCESS do {} while (0) + +#endif + + +/* ------------------------------------------------------------------------- */ + +#define USB_RESET_WIDTH 50 +#define MAX_XFER_SIZE 1023 + +/* Buffer sizes */ +#define ISP1362_BUF_SIZE 4096 +#define ISP1362_ISTL_BUFSIZE 512 +#define ISP1362_INTL_BLKSIZE 64 +#define ISP1362_INTL_BUFFERS 16 +#define ISP1362_ATL_BLKSIZE 64 + +#define ISP1362_REG_WRITE_OFFSET 0x80 + +#ifdef ISP1362_DEBUG +typedef const unsigned int isp1362_reg_t; + +#define REG_WIDTH_16 0x000 +#define REG_WIDTH_32 0x100 +#define REG_WIDTH_MASK 0x100 +#define REG_NO_MASK 0x0ff + +#define REG_ACCESS_R 0x200 +#define REG_ACCESS_W 0x400 +#define REG_ACCESS_RW 0x600 +#define REG_ACCESS_MASK 0x600 + +#define ISP1362_REG_NO(r) ((r) & REG_NO_MASK) + +#define _BUG_ON(x) BUG_ON(x) +#define _WARN_ON(x) WARN_ON(x) + +#define ISP1362_REG(name, addr, width, rw) \ +static isp1362_reg_t ISP1362_REG_##name = ((addr) | (width) | (rw)) + +#define REG_ACCESS_TEST(r) BUG_ON(((r) & ISP1362_REG_WRITE_OFFSET) && !((r) & REG_ACCESS_W)) +#define REG_WIDTH_TEST(r, w) BUG_ON(((r) & REG_WIDTH_MASK) != (w)) +#else +typedef const unsigned char isp1362_reg_t; +#define ISP1362_REG_NO(r) (r) +#define _BUG_ON(x) do {} while (0) +#define _WARN_ON(x) do {} while (0) + +#define ISP1362_REG(name, addr, width, rw) \ +static isp1362_reg_t ISP1362_REG_##name = addr + +#define REG_ACCESS_TEST(r) do {} while (0) +#define REG_WIDTH_TEST(r, w) do {} while (0) +#endif + +/* OHCI compatible registers */ +/* + * Note: Some of the ISP1362 'OHCI' registers implement only + * a subset of the bits defined in the OHCI spec. + * + * Bitmasks for the individual bits of these registers are defined in "ohci.h" + */ +ISP1362_REG(HCREVISION, 0x00, REG_WIDTH_32, REG_ACCESS_R); +ISP1362_REG(HCCONTROL, 0x01, REG_WIDTH_32, REG_ACCESS_RW); +ISP1362_REG(HCCMDSTAT, 0x02, REG_WIDTH_32, REG_ACCESS_RW); +ISP1362_REG(HCINTSTAT, 0x03, REG_WIDTH_32, REG_ACCESS_RW); +ISP1362_REG(HCINTENB, 0x04, REG_WIDTH_32, REG_ACCESS_RW); +ISP1362_REG(HCINTDIS, 0x05, REG_WIDTH_32, REG_ACCESS_RW); +ISP1362_REG(HCFMINTVL, 0x0d, REG_WIDTH_32, REG_ACCESS_RW); +ISP1362_REG(HCFMREM, 0x0e, REG_WIDTH_32, REG_ACCESS_RW); +ISP1362_REG(HCFMNUM, 0x0f, REG_WIDTH_32, REG_ACCESS_RW); +ISP1362_REG(HCLSTHRESH, 0x11, REG_WIDTH_32, REG_ACCESS_RW); +ISP1362_REG(HCRHDESCA, 0x12, REG_WIDTH_32, REG_ACCESS_RW); +ISP1362_REG(HCRHDESCB, 0x13, REG_WIDTH_32, REG_ACCESS_RW); +ISP1362_REG(HCRHSTATUS, 0x14, REG_WIDTH_32, REG_ACCESS_RW); +ISP1362_REG(HCRHPORT1, 0x15, REG_WIDTH_32, REG_ACCESS_RW); +ISP1362_REG(HCRHPORT2, 0x16, REG_WIDTH_32, REG_ACCESS_RW); + +/* Philips ISP1362 specific registers */ +ISP1362_REG(HCHWCFG, 0x20, REG_WIDTH_16, REG_ACCESS_RW); +#define HCHWCFG_DISABLE_SUSPEND (1 << 15) +#define HCHWCFG_GLOBAL_PWRDOWN (1 << 14) +#define HCHWCFG_PULLDOWN_DS2 (1 << 13) +#define HCHWCFG_PULLDOWN_DS1 (1 << 12) +#define HCHWCFG_CLKNOTSTOP (1 << 11) +#define HCHWCFG_ANALOG_OC (1 << 10) +#define HCHWCFG_ONEINT (1 << 9) +#define HCHWCFG_DACK_MODE (1 << 8) +#define HCHWCFG_ONEDMA (1 << 7) +#define HCHWCFG_DACK_POL (1 << 6) +#define HCHWCFG_DREQ_POL (1 << 5) +#define HCHWCFG_DBWIDTH_MASK (0x03 << 3) +#define HCHWCFG_DBWIDTH(n) (((n) << 3) & HCHWCFG_DBWIDTH_MASK) +#define HCHWCFG_INT_POL (1 << 2) +#define HCHWCFG_INT_TRIGGER (1 << 1) +#define HCHWCFG_INT_ENABLE (1 << 0) + +ISP1362_REG(HCDMACFG, 0x21, REG_WIDTH_16, REG_ACCESS_RW); +#define HCDMACFG_CTR_ENABLE (1 << 7) +#define HCDMACFG_BURST_LEN_MASK (0x03 << 5) +#define HCDMACFG_BURST_LEN(n) (((n) << 5) & HCDMACFG_BURST_LEN_MASK) +#define HCDMACFG_BURST_LEN_1 HCDMACFG_BURST_LEN(0) +#define HCDMACFG_BURST_LEN_4 HCDMACFG_BURST_LEN(1) +#define HCDMACFG_BURST_LEN_8 HCDMACFG_BURST_LEN(2) +#define HCDMACFG_DMA_ENABLE (1 << 4) +#define HCDMACFG_BUF_TYPE_MASK (0x07 << 1) +#define HCDMACFG_BUF_TYPE(n) (((n) << 1) & HCDMACFG_BUF_TYPE_MASK) +#define HCDMACFG_BUF_ISTL0 HCDMACFG_BUF_TYPE(0) +#define HCDMACFG_BUF_ISTL1 HCDMACFG_BUF_TYPE(1) +#define HCDMACFG_BUF_INTL HCDMACFG_BUF_TYPE(2) +#define HCDMACFG_BUF_ATL HCDMACFG_BUF_TYPE(3) +#define HCDMACFG_BUF_DIRECT HCDMACFG_BUF_TYPE(4) +#define HCDMACFG_DMA_RW_SELECT (1 << 0) + +ISP1362_REG(HCXFERCTR, 0x22, REG_WIDTH_16, REG_ACCESS_RW); + +ISP1362_REG(HCuPINT, 0x24, REG_WIDTH_16, REG_ACCESS_RW); +#define HCuPINT_SOF (1 << 0) +#define HCuPINT_ISTL0 (1 << 1) +#define HCuPINT_ISTL1 (1 << 2) +#define HCuPINT_EOT (1 << 3) +#define HCuPINT_OPR (1 << 4) +#define HCuPINT_SUSP (1 << 5) +#define HCuPINT_CLKRDY (1 << 6) +#define HCuPINT_INTL (1 << 7) +#define HCuPINT_ATL (1 << 8) +#define HCuPINT_OTG (1 << 9) + +ISP1362_REG(HCuPINTENB, 0x25, REG_WIDTH_16, REG_ACCESS_RW); +/* same bit definitions apply as for HCuPINT */ + +ISP1362_REG(HCCHIPID, 0x27, REG_WIDTH_16, REG_ACCESS_R); +#define HCCHIPID_MASK 0xff00 +#define HCCHIPID_MAGIC 0x3600 + +ISP1362_REG(HCSCRATCH, 0x28, REG_WIDTH_16, REG_ACCESS_RW); + +ISP1362_REG(HCSWRES, 0x29, REG_WIDTH_16, REG_ACCESS_W); +#define HCSWRES_MAGIC 0x00f6 + +ISP1362_REG(HCBUFSTAT, 0x2c, REG_WIDTH_16, REG_ACCESS_RW); +#define HCBUFSTAT_ISTL0_FULL (1 << 0) +#define HCBUFSTAT_ISTL1_FULL (1 << 1) +#define HCBUFSTAT_INTL_ACTIVE (1 << 2) +#define HCBUFSTAT_ATL_ACTIVE (1 << 3) +#define HCBUFSTAT_RESET_HWPP (1 << 4) +#define HCBUFSTAT_ISTL0_ACTIVE (1 << 5) +#define HCBUFSTAT_ISTL1_ACTIVE (1 << 6) +#define HCBUFSTAT_ISTL0_DONE (1 << 8) +#define HCBUFSTAT_ISTL1_DONE (1 << 9) +#define HCBUFSTAT_PAIRED_PTDPP (1 << 10) + +ISP1362_REG(HCDIRADDR, 0x32, REG_WIDTH_32, REG_ACCESS_RW); +#define HCDIRADDR_ADDR_MASK 0x0000ffff +#define HCDIRADDR_ADDR(n) (((n) << 0) & HCDIRADDR_ADDR_MASK) +#define HCDIRADDR_COUNT_MASK 0xffff0000 +#define HCDIRADDR_COUNT(n) (((n) << 16) & HCDIRADDR_COUNT_MASK) +ISP1362_REG(HCDIRDATA, 0x45, REG_WIDTH_16, REG_ACCESS_RW); + +ISP1362_REG(HCISTLBUFSZ, 0x30, REG_WIDTH_16, REG_ACCESS_RW); +ISP1362_REG(HCISTL0PORT, 0x40, REG_WIDTH_16, REG_ACCESS_RW); +ISP1362_REG(HCISTL1PORT, 0x42, REG_WIDTH_16, REG_ACCESS_RW); +ISP1362_REG(HCISTLRATE, 0x47, REG_WIDTH_16, REG_ACCESS_RW); + +ISP1362_REG(HCINTLBUFSZ, 0x33, REG_WIDTH_16, REG_ACCESS_RW); +ISP1362_REG(HCINTLPORT, 0x43, REG_WIDTH_16, REG_ACCESS_RW); +ISP1362_REG(HCINTLBLKSZ, 0x53, REG_WIDTH_16, REG_ACCESS_RW); +ISP1362_REG(HCINTLDONE, 0x17, REG_WIDTH_32, REG_ACCESS_R); +ISP1362_REG(HCINTLSKIP, 0x18, REG_WIDTH_32, REG_ACCESS_RW); +ISP1362_REG(HCINTLLAST, 0x19, REG_WIDTH_32, REG_ACCESS_RW); +ISP1362_REG(HCINTLCURR, 0x1a, REG_WIDTH_16, REG_ACCESS_R); + +ISP1362_REG(HCATLBUFSZ, 0x34, REG_WIDTH_16, REG_ACCESS_RW); +ISP1362_REG(HCATLPORT, 0x44, REG_WIDTH_16, REG_ACCESS_RW); +ISP1362_REG(HCATLBLKSZ, 0x54, REG_WIDTH_16, REG_ACCESS_RW); +ISP1362_REG(HCATLDONE, 0x1b, REG_WIDTH_32, REG_ACCESS_R); +ISP1362_REG(HCATLSKIP, 0x1c, REG_WIDTH_32, REG_ACCESS_RW); +ISP1362_REG(HCATLLAST, 0x1d, REG_WIDTH_32, REG_ACCESS_RW); +ISP1362_REG(HCATLCURR, 0x1e, REG_WIDTH_16, REG_ACCESS_R); + +ISP1362_REG(HCATLDTC, 0x51, REG_WIDTH_16, REG_ACCESS_RW); +ISP1362_REG(HCATLDTCTO, 0x52, REG_WIDTH_16, REG_ACCESS_RW); + + +ISP1362_REG(OTGCONTROL, 0x62, REG_WIDTH_16, REG_ACCESS_RW); +ISP1362_REG(OTGSTATUS, 0x67, REG_WIDTH_16, REG_ACCESS_R); +ISP1362_REG(OTGINT, 0x68, REG_WIDTH_16, REG_ACCESS_RW); +ISP1362_REG(OTGINTENB, 0x69, REG_WIDTH_16, REG_ACCESS_RW); +ISP1362_REG(OTGTIMER, 0x6A, REG_WIDTH_16, REG_ACCESS_RW); +ISP1362_REG(OTGALTTMR, 0x6C, REG_WIDTH_16, REG_ACCESS_RW); + +/* Philips transfer descriptor, cpu-endian */ +struct ptd { + u16 count; +#define PTD_COUNT_MSK (0x3ff << 0) +#define PTD_TOGGLE_MSK (1 << 10) +#define PTD_ACTIVE_MSK (1 << 11) +#define PTD_CC_MSK (0xf << 12) + u16 mps; +#define PTD_MPS_MSK (0x3ff << 0) +#define PTD_SPD_MSK (1 << 10) +#define PTD_LAST_MSK (1 << 11) +#define PTD_EP_MSK (0xf << 12) + u16 len; +#define PTD_LEN_MSK (0x3ff << 0) +#define PTD_DIR_MSK (3 << 10) +#define PTD_DIR_SETUP (0) +#define PTD_DIR_OUT (1) +#define PTD_DIR_IN (2) + u16 faddr; +#define PTD_FA_MSK (0x7f << 0) +/* PTD Byte 7: [StartingFrame (if ISO PTD) | StartingFrame[0..4], PollingRate[0..2] (if INT PTD)] */ +#define PTD_SF_ISO_MSK (0xff << 8) +#define PTD_SF_INT_MSK (0x1f << 8) +#define PTD_PR_MSK (0x07 << 13) +} __attribute__ ((packed, aligned(2))); +#define PTD_HEADER_SIZE sizeof(struct ptd) + +/* ------------------------------------------------------------------------- */ +/* Copied from ohci.h: */ +/* + * Hardware transfer status codes -- CC from PTD + */ +#define PTD_CC_NOERROR 0x00 +#define PTD_CC_CRC 0x01 +#define PTD_CC_BITSTUFFING 0x02 +#define PTD_CC_DATATOGGLEM 0x03 +#define PTD_CC_STALL 0x04 +#define PTD_DEVNOTRESP 0x05 +#define PTD_PIDCHECKFAIL 0x06 +#define PTD_UNEXPECTEDPID 0x07 +#define PTD_DATAOVERRUN 0x08 +#define PTD_DATAUNDERRUN 0x09 + /* 0x0A, 0x0B reserved for hardware */ +#define PTD_BUFFEROVERRUN 0x0C +#define PTD_BUFFERUNDERRUN 0x0D + /* 0x0E, 0x0F reserved for HCD */ +#define PTD_NOTACCESSED 0x0F + + +/* map OHCI TD status codes (CC) to errno values */ +static const int cc_to_error[16] = { + /* No Error */ 0, + /* CRC Error */ -EILSEQ, + /* Bit Stuff */ -EPROTO, + /* Data Togg */ -EILSEQ, + /* Stall */ -EPIPE, + /* DevNotResp */ -ETIMEDOUT, + /* PIDCheck */ -EPROTO, + /* UnExpPID */ -EPROTO, + /* DataOver */ -EOVERFLOW, + /* DataUnder */ -EREMOTEIO, + /* (for hw) */ -EIO, + /* (for hw) */ -EIO, + /* BufferOver */ -ECOMM, + /* BuffUnder */ -ENOSR, + /* (for HCD) */ -EALREADY, + /* (for HCD) */ -EALREADY +}; + + +/* + * HcControl (control) register masks + */ +#define OHCI_CTRL_HCFS (3 << 6) /* host controller functional state */ +#define OHCI_CTRL_RWC (1 << 9) /* remote wakeup connected */ +#define OHCI_CTRL_RWE (1 << 10) /* remote wakeup enable */ + +/* pre-shifted values for HCFS */ +# define OHCI_USB_RESET (0 << 6) +# define OHCI_USB_RESUME (1 << 6) +# define OHCI_USB_OPER (2 << 6) +# define OHCI_USB_SUSPEND (3 << 6) + +/* + * HcCommandStatus (cmdstatus) register masks + */ +#define OHCI_HCR (1 << 0) /* host controller reset */ +#define OHCI_SOC (3 << 16) /* scheduling overrun count */ + +/* + * masks used with interrupt registers: + * HcInterruptStatus (intrstatus) + * HcInterruptEnable (intrenable) + * HcInterruptDisable (intrdisable) + */ +#define OHCI_INTR_SO (1 << 0) /* scheduling overrun */ +#define OHCI_INTR_WDH (1 << 1) /* writeback of done_head */ +#define OHCI_INTR_SF (1 << 2) /* start frame */ +#define OHCI_INTR_RD (1 << 3) /* resume detect */ +#define OHCI_INTR_UE (1 << 4) /* unrecoverable error */ +#define OHCI_INTR_FNO (1 << 5) /* frame number overflow */ +#define OHCI_INTR_RHSC (1 << 6) /* root hub status change */ +#define OHCI_INTR_OC (1 << 30) /* ownership change */ +#define OHCI_INTR_MIE (1 << 31) /* master interrupt enable */ + +/* roothub.portstatus [i] bits */ +#define RH_PS_CCS 0x00000001 /* current connect status */ +#define RH_PS_PES 0x00000002 /* port enable status*/ +#define RH_PS_PSS 0x00000004 /* port suspend status */ +#define RH_PS_POCI 0x00000008 /* port over current indicator */ +#define RH_PS_PRS 0x00000010 /* port reset status */ +#define RH_PS_PPS 0x00000100 /* port power status */ +#define RH_PS_LSDA 0x00000200 /* low speed device attached */ +#define RH_PS_CSC 0x00010000 /* connect status change */ +#define RH_PS_PESC 0x00020000 /* port enable status change */ +#define RH_PS_PSSC 0x00040000 /* port suspend status change */ +#define RH_PS_OCIC 0x00080000 /* over current indicator change */ +#define RH_PS_PRSC 0x00100000 /* port reset status change */ + +/* roothub.status bits */ +#define RH_HS_LPS 0x00000001 /* local power status */ +#define RH_HS_OCI 0x00000002 /* over current indicator */ +#define RH_HS_DRWE 0x00008000 /* device remote wakeup enable */ +#define RH_HS_LPSC 0x00010000 /* local power status change */ +#define RH_HS_OCIC 0x00020000 /* over current indicator change */ +#define RH_HS_CRWE 0x80000000 /* clear remote wakeup enable */ + +/* roothub.b masks */ +#define RH_B_DR 0x0000ffff /* device removable flags */ +#define RH_B_PPCM 0xffff0000 /* port power control mask */ + +/* roothub.a masks */ +#define RH_A_NDP (0xff << 0) /* number of downstream ports */ +#define RH_A_PSM (1 << 8) /* power switching mode */ +#define RH_A_NPS (1 << 9) /* no power switching */ +#define RH_A_DT (1 << 10) /* device type (mbz) */ +#define RH_A_OCPM (1 << 11) /* over current protection mode */ +#define RH_A_NOCP (1 << 12) /* no over current protection */ +#define RH_A_POTPGT (0xff << 24) /* power on to power good time */ + +#define FI 0x2edf /* 12000 bits per frame (-1) */ +#define FSMP(fi) (0x7fff & ((6 * ((fi) - 210)) / 7)) +#define LSTHRESH 0x628 /* lowspeed bit threshold */ + +/* ------------------------------------------------------------------------- */ + +/* PTD accessor macros. */ +#define PTD_GET_COUNT(p) (((p)->count & PTD_COUNT_MSK) >> 0) +#define PTD_COUNT(v) (((v) << 0) & PTD_COUNT_MSK) +#define PTD_GET_TOGGLE(p) (((p)->count & PTD_TOGGLE_MSK) >> 10) +#define PTD_TOGGLE(v) (((v) << 10) & PTD_TOGGLE_MSK) +#define PTD_GET_ACTIVE(p) (((p)->count & PTD_ACTIVE_MSK) >> 11) +#define PTD_ACTIVE(v) (((v) << 11) & PTD_ACTIVE_MSK) +#define PTD_GET_CC(p) (((p)->count & PTD_CC_MSK) >> 12) +#define PTD_CC(v) (((v) << 12) & PTD_CC_MSK) +#define PTD_GET_MPS(p) (((p)->mps & PTD_MPS_MSK) >> 0) +#define PTD_MPS(v) (((v) << 0) & PTD_MPS_MSK) +#define PTD_GET_SPD(p) (((p)->mps & PTD_SPD_MSK) >> 10) +#define PTD_SPD(v) (((v) << 10) & PTD_SPD_MSK) +#define PTD_GET_LAST(p) (((p)->mps & PTD_LAST_MSK) >> 11) +#define PTD_LAST(v) (((v) << 11) & PTD_LAST_MSK) +#define PTD_GET_EP(p) (((p)->mps & PTD_EP_MSK) >> 12) +#define PTD_EP(v) (((v) << 12) & PTD_EP_MSK) +#define PTD_GET_LEN(p) (((p)->len & PTD_LEN_MSK) >> 0) +#define PTD_LEN(v) (((v) << 0) & PTD_LEN_MSK) +#define PTD_GET_DIR(p) (((p)->len & PTD_DIR_MSK) >> 10) +#define PTD_DIR(v) (((v) << 10) & PTD_DIR_MSK) +#define PTD_GET_FA(p) (((p)->faddr & PTD_FA_MSK) >> 0) +#define PTD_FA(v) (((v) << 0) & PTD_FA_MSK) +#define PTD_GET_SF_INT(p) (((p)->faddr & PTD_SF_INT_MSK) >> 8) +#define PTD_SF_INT(v) (((v) << 8) & PTD_SF_INT_MSK) +#define PTD_GET_SF_ISO(p) (((p)->faddr & PTD_SF_ISO_MSK) >> 8) +#define PTD_SF_ISO(v) (((v) << 8) & PTD_SF_ISO_MSK) +#define PTD_GET_PR(p) (((p)->faddr & PTD_PR_MSK) >> 13) +#define PTD_PR(v) (((v) << 13) & PTD_PR_MSK) + +#define LOG2_PERIODIC_SIZE 5 /* arbitrary; this matches OHCI */ +#define PERIODIC_SIZE (1 << LOG2_PERIODIC_SIZE) + +struct isp1362_ep { + struct usb_host_endpoint *hep; + struct usb_device *udev; + + /* philips transfer descriptor */ + struct ptd ptd; + + u8 maxpacket; + u8 epnum; + u8 nextpid; + u16 error_count; + u16 length; /* of current packet */ + s16 ptd_offset; /* buffer offset in ISP1362 where + PTD has been stored + (for access thru HCDIRDATA) */ + int ptd_index; + int num_ptds; + void *data; /* to databuf */ + /* queue of active EPs (the ones transmitted to the chip) */ + struct list_head active; + + /* periodic schedule */ + u8 branch; + u16 interval; + u16 load; + u16 last_iso; + + /* async schedule */ + struct list_head schedule; /* list of all EPs that need processing */ + struct list_head remove_list; + int num_req; +}; + +struct isp1362_ep_queue { + struct list_head active; /* list of PTDs currently processed by HC */ + atomic_t finishing; + unsigned long buf_map; + unsigned long skip_map; + int free_ptd; + u16 buf_start; + u16 buf_size; + u16 blk_size; /* PTD buffer block size for ATL and INTL */ + u8 buf_count; + u8 buf_avail; + char name[16]; + + /* for statistical tracking */ + u8 stat_maxptds; /* Max # of ptds seen simultaneously in fifo */ + u8 ptd_count; /* number of ptds submitted to this queue */ +}; + +struct isp1362_hcd { + spinlock_t lock; + void __iomem *addr_reg; + void __iomem *data_reg; + + struct isp1362_platform_data *board; + + struct proc_dir_entry *pde; + unsigned long stat1, stat2, stat4, stat8, stat16; + + /* HC registers */ + u32 intenb; /* "OHCI" interrupts */ + u16 irqenb; /* uP interrupts */ + + /* Root hub registers */ + u32 rhdesca; + u32 rhdescb; + u32 rhstatus; + u32 rhport[MAX_ROOT_PORTS]; + unsigned long next_statechange; + + /* HC control reg shadow copy */ + u32 hc_control; + + /* async schedule: control, bulk */ + struct list_head async; + + /* periodic schedule: int */ + u16 load[PERIODIC_SIZE]; + struct list_head periodic; + u16 fmindex; + + /* periodic schedule: isochronous */ + struct list_head isoc; + int istl_flip:1; + int irq_active:1; + + /* Schedules for the current frame */ + struct isp1362_ep_queue atl_queue; + struct isp1362_ep_queue intl_queue; + struct isp1362_ep_queue istl_queue[2]; + + /* list of PTDs retrieved from HC */ + struct list_head remove_list; + enum { + ISP1362_INT_SOF, + ISP1362_INT_ISTL0, + ISP1362_INT_ISTL1, + ISP1362_INT_EOT, + ISP1362_INT_OPR, + ISP1362_INT_SUSP, + ISP1362_INT_CLKRDY, + ISP1362_INT_INTL, + ISP1362_INT_ATL, + ISP1362_INT_OTG, + NUM_ISP1362_IRQS + } IRQ_NAMES; + unsigned int irq_stat[NUM_ISP1362_IRQS]; + int req_serial; +}; + +static inline const char *ISP1362_INT_NAME(int n) +{ + switch (n) { + case ISP1362_INT_SOF: return "SOF"; + case ISP1362_INT_ISTL0: return "ISTL0"; + case ISP1362_INT_ISTL1: return "ISTL1"; + case ISP1362_INT_EOT: return "EOT"; + case ISP1362_INT_OPR: return "OPR"; + case ISP1362_INT_SUSP: return "SUSP"; + case ISP1362_INT_CLKRDY: return "CLKRDY"; + case ISP1362_INT_INTL: return "INTL"; + case ISP1362_INT_ATL: return "ATL"; + case ISP1362_INT_OTG: return "OTG"; + default: return "unknown"; + } +} + +static inline void ALIGNSTAT(struct isp1362_hcd *isp1362_hcd, void *ptr) +{ + unsigned long p = (unsigned long)ptr; + if (!(p & 0xf)) + isp1362_hcd->stat16++; + else if (!(p & 0x7)) + isp1362_hcd->stat8++; + else if (!(p & 0x3)) + isp1362_hcd->stat4++; + else if (!(p & 0x1)) + isp1362_hcd->stat2++; + else + isp1362_hcd->stat1++; +} + +static inline struct isp1362_hcd *hcd_to_isp1362_hcd(struct usb_hcd *hcd) +{ + return (struct isp1362_hcd *) (hcd->hcd_priv); +} + +static inline struct usb_hcd *isp1362_hcd_to_hcd(struct isp1362_hcd *isp1362_hcd) +{ + return container_of((void *)isp1362_hcd, struct usb_hcd, hcd_priv); +} + +#define frame_before(f1, f2) ((s16)((u16)f1 - (u16)f2) < 0) + +/* + * ISP1362 HW Interface + */ + +#ifdef ISP1362_DEBUG +#define DBG(level, fmt...) \ + do { \ + if (dbg_level > level) \ + pr_debug(fmt); \ + } while (0) +#define _DBG(level, fmt...) \ + do { \ + if (dbg_level > level) \ + printk(fmt); \ + } while (0) +#else +#define DBG(fmt...) do {} while (0) +#define _DBG DBG +#endif + +#ifdef VERBOSE +# define VDBG(fmt...) DBG(3, fmt) +#else +# define VDBG(fmt...) do {} while (0) +#endif + +#ifdef REGISTERS +# define RDBG(fmt...) DBG(1, fmt) +#else +# define RDBG(fmt...) do {} while (0) +#endif + +#ifdef URB_TRACE +#define URB_DBG(fmt...) DBG(0, fmt) +#else +#define URB_DBG(fmt...) do {} while (0) +#endif + + +#if USE_PLATFORM_DELAY +#if USE_NDELAY +#error USE_PLATFORM_DELAY and USE_NDELAY defined simultaneously. +#endif +#define isp1362_delay(h, d) (h)->board->delay(isp1362_hcd_to_hcd(h)->self.controller, d) +#elif USE_NDELAY +#define isp1362_delay(h, d) ndelay(d) +#else +#define isp1362_delay(h, d) do {} while (0) +#endif + +#define get_urb(ep) ({ \ + BUG_ON(list_empty(&ep->hep->urb_list)); \ + container_of(ep->hep->urb_list.next, struct urb, urb_list); \ +}) + +/* basic access functions for ISP1362 chip registers */ +/* NOTE: The contents of the address pointer register cannot be read back! The driver must ensure, + * that all register accesses are performed with interrupts disabled, since the interrupt + * handler has no way of restoring the previous state. + */ +static void isp1362_write_addr(struct isp1362_hcd *isp1362_hcd, isp1362_reg_t reg) +{ + /*_BUG_ON((reg & ISP1362_REG_WRITE_OFFSET) && !(reg & REG_ACCESS_W));*/ + REG_ACCESS_TEST(reg); + _BUG_ON(!irqs_disabled()); + DUMMY_DELAY_ACCESS; + writew(ISP1362_REG_NO(reg), isp1362_hcd->addr_reg); + DUMMY_DELAY_ACCESS; + isp1362_delay(isp1362_hcd, 1); +} + +static void isp1362_write_data16(struct isp1362_hcd *isp1362_hcd, u16 val) +{ + _BUG_ON(!irqs_disabled()); + DUMMY_DELAY_ACCESS; + writew(val, isp1362_hcd->data_reg); +} + +static u16 isp1362_read_data16(struct isp1362_hcd *isp1362_hcd) +{ + u16 val; + + _BUG_ON(!irqs_disabled()); + DUMMY_DELAY_ACCESS; + val = readw(isp1362_hcd->data_reg); + + return val; +} + +static void isp1362_write_data32(struct isp1362_hcd *isp1362_hcd, u32 val) +{ + _BUG_ON(!irqs_disabled()); +#if USE_32BIT + DUMMY_DELAY_ACCESS; + writel(val, isp1362_hcd->data_reg); +#else + DUMMY_DELAY_ACCESS; + writew((u16)val, isp1362_hcd->data_reg); + DUMMY_DELAY_ACCESS; + writew(val >> 16, isp1362_hcd->data_reg); +#endif +} + +static u32 isp1362_read_data32(struct isp1362_hcd *isp1362_hcd) +{ + u32 val; + + _BUG_ON(!irqs_disabled()); +#if USE_32BIT + DUMMY_DELAY_ACCESS; + val = readl(isp1362_hcd->data_reg); +#else + DUMMY_DELAY_ACCESS; + val = (u32)readw(isp1362_hcd->data_reg); + DUMMY_DELAY_ACCESS; + val |= (u32)readw(isp1362_hcd->data_reg) << 16; +#endif + return val; +} + +/* use readsw/writesw to access the fifo whenever possible */ +/* assume HCDIRDATA or XFERCTR & addr_reg have been set up */ +static void isp1362_read_fifo(struct isp1362_hcd *isp1362_hcd, void *buf, u16 len) +{ + u8 *dp = buf; + u16 data; + + if (!len) + return; + + _BUG_ON(!irqs_disabled()); + + RDBG("%s: Reading %d byte from fifo to mem @ %p\n", __func__, len, buf); +#if USE_32BIT + if (len >= 4) { + RDBG("%s: Using readsl for %d dwords\n", __func__, len >> 2); + readsl(isp1362_hcd->data_reg, dp, len >> 2); + dp += len & ~3; + len &= 3; + } +#endif + if (len >= 2) { + RDBG("%s: Using readsw for %d words\n", __func__, len >> 1); + insw((unsigned long)isp1362_hcd->data_reg, dp, len >> 1); + dp += len & ~1; + len &= 1; + } + + BUG_ON(len & ~1); + if (len > 0) { + data = isp1362_read_data16(isp1362_hcd); + RDBG("%s: Reading trailing byte %02x to mem @ %08x\n", __func__, + (u8)data, (u32)dp); + *dp = (u8)data; + } +} + +static void isp1362_write_fifo(struct isp1362_hcd *isp1362_hcd, void *buf, u16 len) +{ + u8 *dp = buf; + u16 data; + + if (!len) + return; + + if ((unsigned long)dp & 0x1) { + /* not aligned */ + for (; len > 1; len -= 2) { + data = *dp++; + data |= *dp++ << 8; + isp1362_write_data16(isp1362_hcd, data); + } + if (len) + isp1362_write_data16(isp1362_hcd, *dp); + return; + } + + _BUG_ON(!irqs_disabled()); + + RDBG("%s: Writing %d byte to fifo from memory @%p\n", __func__, len, buf); +#if USE_32BIT + if (len >= 4) { + RDBG("%s: Using writesl for %d dwords\n", __func__, len >> 2); + writesl(isp1362_hcd->data_reg, dp, len >> 2); + dp += len & ~3; + len &= 3; + } +#endif + if (len >= 2) { + RDBG("%s: Using writesw for %d words\n", __func__, len >> 1); + outsw((unsigned long)isp1362_hcd->data_reg, dp, len >> 1); + dp += len & ~1; + len &= 1; + } + + BUG_ON(len & ~1); + if (len > 0) { + /* finally write any trailing byte; we don't need to care + * about the high byte of the last word written + */ + data = (u16)*dp; + RDBG("%s: Sending trailing byte %02x from mem @ %08x\n", __func__, + data, (u32)dp); + isp1362_write_data16(isp1362_hcd, data); + } +} + +#define isp1362_read_reg16(d, r) ({ \ + u16 __v; \ + REG_WIDTH_TEST(ISP1362_REG_##r, REG_WIDTH_16); \ + isp1362_write_addr(d, ISP1362_REG_##r); \ + __v = isp1362_read_data16(d); \ + RDBG("%s: Read %04x from %s[%02x]\n", __func__, __v, #r, \ + ISP1362_REG_NO(ISP1362_REG_##r)); \ + __v; \ +}) + +#define isp1362_read_reg32(d, r) ({ \ + u32 __v; \ + REG_WIDTH_TEST(ISP1362_REG_##r, REG_WIDTH_32); \ + isp1362_write_addr(d, ISP1362_REG_##r); \ + __v = isp1362_read_data32(d); \ + RDBG("%s: Read %08x from %s[%02x]\n", __func__, __v, #r, \ + ISP1362_REG_NO(ISP1362_REG_##r)); \ + __v; \ +}) + +#define isp1362_write_reg16(d, r, v) { \ + REG_WIDTH_TEST(ISP1362_REG_##r, REG_WIDTH_16); \ + isp1362_write_addr(d, (ISP1362_REG_##r) | ISP1362_REG_WRITE_OFFSET); \ + isp1362_write_data16(d, (u16)(v)); \ + RDBG("%s: Wrote %04x to %s[%02x]\n", __func__, (u16)(v), #r, \ + ISP1362_REG_NO(ISP1362_REG_##r)); \ +} + +#define isp1362_write_reg32(d, r, v) { \ + REG_WIDTH_TEST(ISP1362_REG_##r, REG_WIDTH_32); \ + isp1362_write_addr(d, (ISP1362_REG_##r) | ISP1362_REG_WRITE_OFFSET); \ + isp1362_write_data32(d, (u32)(v)); \ + RDBG("%s: Wrote %08x to %s[%02x]\n", __func__, (u32)(v), #r, \ + ISP1362_REG_NO(ISP1362_REG_##r)); \ +} + +#define isp1362_set_mask16(d, r, m) { \ + u16 __v; \ + __v = isp1362_read_reg16(d, r); \ + if ((__v | m) != __v) \ + isp1362_write_reg16(d, r, __v | m); \ +} + +#define isp1362_clr_mask16(d, r, m) { \ + u16 __v; \ + __v = isp1362_read_reg16(d, r); \ + if ((__v & ~m) != __v) \ + isp1362_write_reg16(d, r, __v & ~m); \ +} + +#define isp1362_set_mask32(d, r, m) { \ + u32 __v; \ + __v = isp1362_read_reg32(d, r); \ + if ((__v | m) != __v) \ + isp1362_write_reg32(d, r, __v | m); \ +} + +#define isp1362_clr_mask32(d, r, m) { \ + u32 __v; \ + __v = isp1362_read_reg32(d, r); \ + if ((__v & ~m) != __v) \ + isp1362_write_reg32(d, r, __v & ~m); \ +} + +#ifdef ISP1362_DEBUG +#define isp1362_show_reg(d, r) { \ + if ((ISP1362_REG_##r & REG_WIDTH_MASK) == REG_WIDTH_32) \ + DBG(0, "%-12s[%02x]: %08x\n", #r, \ + ISP1362_REG_NO(ISP1362_REG_##r), isp1362_read_reg32(d, r)); \ + else \ + DBG(0, "%-12s[%02x]: %04x\n", #r, \ + ISP1362_REG_NO(ISP1362_REG_##r), isp1362_read_reg16(d, r)); \ +} +#else +#define isp1362_show_reg(d, r) do {} while (0) +#endif + +static void __attribute__((__unused__)) isp1362_show_regs(struct isp1362_hcd *isp1362_hcd) +{ + isp1362_show_reg(isp1362_hcd, HCREVISION); + isp1362_show_reg(isp1362_hcd, HCCONTROL); + isp1362_show_reg(isp1362_hcd, HCCMDSTAT); + isp1362_show_reg(isp1362_hcd, HCINTSTAT); + isp1362_show_reg(isp1362_hcd, HCINTENB); + isp1362_show_reg(isp1362_hcd, HCFMINTVL); + isp1362_show_reg(isp1362_hcd, HCFMREM); + isp1362_show_reg(isp1362_hcd, HCFMNUM); + isp1362_show_reg(isp1362_hcd, HCLSTHRESH); + isp1362_show_reg(isp1362_hcd, HCRHDESCA); + isp1362_show_reg(isp1362_hcd, HCRHDESCB); + isp1362_show_reg(isp1362_hcd, HCRHSTATUS); + isp1362_show_reg(isp1362_hcd, HCRHPORT1); + isp1362_show_reg(isp1362_hcd, HCRHPORT2); + + isp1362_show_reg(isp1362_hcd, HCHWCFG); + isp1362_show_reg(isp1362_hcd, HCDMACFG); + isp1362_show_reg(isp1362_hcd, HCXFERCTR); + isp1362_show_reg(isp1362_hcd, HCuPINT); + + if (in_interrupt()) + DBG(0, "%-12s[%02x]: %04x\n", "HCuPINTENB", + ISP1362_REG_NO(ISP1362_REG_HCuPINTENB), isp1362_hcd->irqenb); + else + isp1362_show_reg(isp1362_hcd, HCuPINTENB); + isp1362_show_reg(isp1362_hcd, HCCHIPID); + isp1362_show_reg(isp1362_hcd, HCSCRATCH); + isp1362_show_reg(isp1362_hcd, HCBUFSTAT); + isp1362_show_reg(isp1362_hcd, HCDIRADDR); + /* Access would advance fifo + * isp1362_show_reg(isp1362_hcd, HCDIRDATA); + */ + isp1362_show_reg(isp1362_hcd, HCISTLBUFSZ); + isp1362_show_reg(isp1362_hcd, HCISTLRATE); + isp1362_show_reg(isp1362_hcd, HCINTLBUFSZ); + isp1362_show_reg(isp1362_hcd, HCINTLBLKSZ); + isp1362_show_reg(isp1362_hcd, HCINTLDONE); + isp1362_show_reg(isp1362_hcd, HCINTLSKIP); + isp1362_show_reg(isp1362_hcd, HCINTLLAST); + isp1362_show_reg(isp1362_hcd, HCINTLCURR); + isp1362_show_reg(isp1362_hcd, HCATLBUFSZ); + isp1362_show_reg(isp1362_hcd, HCATLBLKSZ); + /* only valid after ATL_DONE interrupt + * isp1362_show_reg(isp1362_hcd, HCATLDONE); + */ + isp1362_show_reg(isp1362_hcd, HCATLSKIP); + isp1362_show_reg(isp1362_hcd, HCATLLAST); + isp1362_show_reg(isp1362_hcd, HCATLCURR); + isp1362_show_reg(isp1362_hcd, HCATLDTC); + isp1362_show_reg(isp1362_hcd, HCATLDTCTO); +} + +static void isp1362_write_diraddr(struct isp1362_hcd *isp1362_hcd, u16 offset, u16 len) +{ + _BUG_ON(offset & 1); + _BUG_ON(offset >= ISP1362_BUF_SIZE); + _BUG_ON(len > ISP1362_BUF_SIZE); + _BUG_ON(offset + len > ISP1362_BUF_SIZE); + len = (len + 1) & ~1; + + isp1362_clr_mask16(isp1362_hcd, HCDMACFG, HCDMACFG_CTR_ENABLE); + isp1362_write_reg32(isp1362_hcd, HCDIRADDR, + HCDIRADDR_ADDR(offset) | HCDIRADDR_COUNT(len)); +} + +static void isp1362_read_buffer(struct isp1362_hcd *isp1362_hcd, void *buf, u16 offset, int len) +{ + _BUG_ON(offset & 1); + + isp1362_write_diraddr(isp1362_hcd, offset, len); + + DBG(3, "%s: Reading %d byte from buffer @%04x to memory @ %p\n", + __func__, len, offset, buf); + + isp1362_write_reg16(isp1362_hcd, HCuPINT, HCuPINT_EOT); + _WARN_ON((isp1362_read_reg16(isp1362_hcd, HCuPINT) & HCuPINT_EOT)); + + isp1362_write_addr(isp1362_hcd, ISP1362_REG_HCDIRDATA); + + isp1362_read_fifo(isp1362_hcd, buf, len); + _WARN_ON(!(isp1362_read_reg16(isp1362_hcd, HCuPINT) & HCuPINT_EOT)); + isp1362_write_reg16(isp1362_hcd, HCuPINT, HCuPINT_EOT); + _WARN_ON((isp1362_read_reg16(isp1362_hcd, HCuPINT) & HCuPINT_EOT)); +} + +static void isp1362_write_buffer(struct isp1362_hcd *isp1362_hcd, void *buf, u16 offset, int len) +{ + _BUG_ON(offset & 1); + + isp1362_write_diraddr(isp1362_hcd, offset, len); + + DBG(3, "%s: Writing %d byte to buffer @%04x from memory @ %p\n", + __func__, len, offset, buf); + + isp1362_write_reg16(isp1362_hcd, HCuPINT, HCuPINT_EOT); + _WARN_ON((isp1362_read_reg16(isp1362_hcd, HCuPINT) & HCuPINT_EOT)); + + isp1362_write_addr(isp1362_hcd, ISP1362_REG_HCDIRDATA | ISP1362_REG_WRITE_OFFSET); + isp1362_write_fifo(isp1362_hcd, buf, len); + + _WARN_ON(!(isp1362_read_reg16(isp1362_hcd, HCuPINT) & HCuPINT_EOT)); + isp1362_write_reg16(isp1362_hcd, HCuPINT, HCuPINT_EOT); + _WARN_ON((isp1362_read_reg16(isp1362_hcd, HCuPINT) & HCuPINT_EOT)); +} + +static void __attribute__((unused)) dump_data(char *buf, int len) +{ + if (dbg_level > 0) { + int k; + int lf = 0; + + for (k = 0; k < len; ++k) { + if (!lf) + DBG(0, "%04x:", k); + printk(" %02x", ((u8 *) buf)[k]); + lf = 1; + if (!k) + continue; + if (k % 16 == 15) { + printk("\n"); + lf = 0; + continue; + } + if (k % 8 == 7) + printk(" "); + if (k % 4 == 3) + printk(" "); + } + if (lf) + printk("\n"); + } +} + +#if defined(ISP1362_DEBUG) && defined(PTD_TRACE) + +static void dump_ptd(struct ptd *ptd) +{ + DBG(0, "EP %p: CC=%x EP=%d DIR=%x CNT=%d LEN=%d MPS=%d TGL=%x ACT=%x FA=%d SPD=%x SF=%x PR=%x LST=%x\n", + container_of(ptd, struct isp1362_ep, ptd), + PTD_GET_CC(ptd), PTD_GET_EP(ptd), PTD_GET_DIR(ptd), + PTD_GET_COUNT(ptd), PTD_GET_LEN(ptd), PTD_GET_MPS(ptd), + PTD_GET_TOGGLE(ptd), PTD_GET_ACTIVE(ptd), PTD_GET_FA(ptd), + PTD_GET_SPD(ptd), PTD_GET_SF_INT(ptd), PTD_GET_PR(ptd), PTD_GET_LAST(ptd)); + DBG(0, " %04x %04x %04x %04x\n", ptd->count, ptd->mps, ptd->len, ptd->faddr); +} + +static void dump_ptd_out_data(struct ptd *ptd, u8 *buf) +{ + if (dbg_level > 0) { + if (PTD_GET_DIR(ptd) != PTD_DIR_IN && PTD_GET_LEN(ptd)) { + DBG(0, "--out->\n"); + dump_data(buf, PTD_GET_LEN(ptd)); + } + } +} + +static void dump_ptd_in_data(struct ptd *ptd, u8 *buf) +{ + if (dbg_level > 0) { + if (PTD_GET_DIR(ptd) == PTD_DIR_IN && PTD_GET_COUNT(ptd)) { + DBG(0, "<--in--\n"); + dump_data(buf, PTD_GET_COUNT(ptd)); + } + DBG(0, "-----\n"); + } +} + +static void dump_ptd_queue(struct isp1362_ep_queue *epq) +{ + struct isp1362_ep *ep; + int dbg = dbg_level; + + dbg_level = 1; + list_for_each_entry(ep, &epq->active, active) { + dump_ptd(&ep->ptd); + dump_data(ep->data, ep->length); + } + dbg_level = dbg; +} +#else +#define dump_ptd(ptd) do {} while (0) +#define dump_ptd_in_data(ptd, buf) do {} while (0) +#define dump_ptd_out_data(ptd, buf) do {} while (0) +#define dump_ptd_data(ptd, buf) do {} while (0) +#define dump_ptd_queue(epq) do {} while (0) +#endif diff --git a/drivers/usb/host/isp1760-hcd.c b/drivers/usb/host/isp1760-hcd.c index 15438469f21a..9600a58299db 100644 --- a/drivers/usb/host/isp1760-hcd.c +++ b/drivers/usb/host/isp1760-hcd.c @@ -386,6 +386,10 @@ static int isp1760_hc_setup(struct usb_hcd *hcd) hwmode |= HW_DACK_POL_HIGH; if (priv->devflags & ISP1760_FLAG_DREQ_POL_HIGH) hwmode |= HW_DREQ_POL_HIGH; + if (priv->devflags & ISP1760_FLAG_INTR_POL_HIGH) + hwmode |= HW_INTR_HIGH_ACT; + if (priv->devflags & ISP1760_FLAG_INTR_EDGE_TRIG) + hwmode |= HW_INTR_EDGE_TRIG; /* * We have to set this first in case we're in 16-bit mode. diff --git a/drivers/usb/host/isp1760-hcd.h b/drivers/usb/host/isp1760-hcd.h index 462f4943cb1b..6931ef5c9650 100644 --- a/drivers/usb/host/isp1760-hcd.h +++ b/drivers/usb/host/isp1760-hcd.h @@ -142,6 +142,8 @@ typedef void (packet_enqueue)(struct usb_hcd *hcd, struct isp1760_qh *qh, #define ISP1760_FLAG_DACK_POL_HIGH 0x00000010 /* DACK active high */ #define ISP1760_FLAG_DREQ_POL_HIGH 0x00000020 /* DREQ active high */ #define ISP1760_FLAG_ISP1761 0x00000040 /* Chip is ISP1761 */ +#define ISP1760_FLAG_INTR_POL_HIGH 0x00000080 /* Interrupt polarity active high */ +#define ISP1760_FLAG_INTR_EDGE_TRIG 0x00000100 /* Interrupt edge triggered */ /* chip memory management */ struct memory_chunk { diff --git a/drivers/usb/host/isp1760-if.c b/drivers/usb/host/isp1760-if.c index d4feebfc63bd..1c9f977a5c9c 100644 --- a/drivers/usb/host/isp1760-if.c +++ b/drivers/usb/host/isp1760-if.c @@ -3,6 +3,7 @@ * Currently there is support for * - OpenFirmware * - PCI + * - PDEV (generic platform device centralized driver model) * * (c) 2007 Sebastian Siewior <bigeasy@linutronix.de> * @@ -11,6 +12,7 @@ #include <linux/usb.h> #include <linux/io.h> #include <linux/platform_device.h> +#include <linux/usb/isp1760.h> #include "../core/hcd.h" #include "isp1760-hcd.h" @@ -308,6 +310,8 @@ static int __devinit isp1760_plat_probe(struct platform_device *pdev) struct resource *mem_res; struct resource *irq_res; resource_size_t mem_size; + struct isp1760_platform_data *priv = pdev->dev.platform_data; + unsigned int devflags = 0; unsigned long irqflags = IRQF_SHARED | IRQF_DISABLED; mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); @@ -330,8 +334,23 @@ static int __devinit isp1760_plat_probe(struct platform_device *pdev) } irqflags |= irq_res->flags & IRQF_TRIGGER_MASK; + if (priv) { + if (priv->is_isp1761) + devflags |= ISP1760_FLAG_ISP1761; + if (priv->bus_width_16) + devflags |= ISP1760_FLAG_BUS_WIDTH_16; + if (priv->port1_otg) + devflags |= ISP1760_FLAG_OTG_EN; + if (priv->analog_oc) + devflags |= ISP1760_FLAG_ANALOG_OC; + if (priv->dack_polarity_high) + devflags |= ISP1760_FLAG_DACK_POL_HIGH; + if (priv->dreq_polarity_high) + devflags |= ISP1760_FLAG_DREQ_POL_HIGH; + } + hcd = isp1760_register(mem_res->start, mem_size, irq_res->start, - irqflags, &pdev->dev, dev_name(&pdev->dev), 0); + irqflags, &pdev->dev, dev_name(&pdev->dev), devflags); if (IS_ERR(hcd)) { pr_warning("isp1760: Failed to register the HCD device\n"); ret = -ENODEV; diff --git a/drivers/usb/host/ohci-at91.c b/drivers/usb/host/ohci-at91.c index bb5e6f671578..7ccffcbe7b6f 100644 --- a/drivers/usb/host/ohci-at91.c +++ b/drivers/usb/host/ohci-at91.c @@ -148,7 +148,7 @@ static int usb_hcd_at91_probe(const struct hc_driver *driver, at91_start_hc(pdev); ohci_hcd_init(hcd_to_ohci(hcd)); - retval = usb_add_hcd(hcd, pdev->resource[1].start, IRQF_DISABLED); + retval = usb_add_hcd(hcd, pdev->resource[1].start, IRQF_SHARED); if (retval == 0) return retval; diff --git a/drivers/usb/host/ohci-au1xxx.c b/drivers/usb/host/ohci-au1xxx.c index 2ac4e022a13f..e4380082ebb1 100644 --- a/drivers/usb/host/ohci-au1xxx.c +++ b/drivers/usb/host/ohci-au1xxx.c @@ -248,10 +248,9 @@ static int ohci_hcd_au1xxx_drv_remove(struct platform_device *pdev) } #ifdef CONFIG_PM -static int ohci_hcd_au1xxx_drv_suspend(struct platform_device *pdev, - pm_message_t message) +static int ohci_hcd_au1xxx_drv_suspend(struct device *dev) { - struct usb_hcd *hcd = platform_get_drvdata(pdev); + struct usb_hcd *hcd = dev_get_drvdata(dev); struct ohci_hcd *ohci = hcd_to_ohci(hcd); unsigned long flags; int rc; @@ -274,10 +273,6 @@ static int ohci_hcd_au1xxx_drv_suspend(struct platform_device *pdev, ohci_writel(ohci, OHCI_INTR_MIE, &ohci->regs->intrdisable); (void)ohci_readl(ohci, &ohci->regs->intrdisable); - /* make sure snapshot being resumed re-enumerates everything */ - if (message.event == PM_EVENT_PRETHAW) - ohci_usb_reset(ohci); - clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); au1xxx_stop_ohc(); @@ -287,9 +282,9 @@ bail: return rc; } -static int ohci_hcd_au1xxx_drv_resume(struct platform_device *pdev) +static int ohci_hcd_au1xxx_drv_resume(struct device *dev) { - struct usb_hcd *hcd = platform_get_drvdata(pdev); + struct usb_hcd *hcd = dev_get_drvdata(dev); au1xxx_start_ohc(); @@ -298,20 +293,26 @@ static int ohci_hcd_au1xxx_drv_resume(struct platform_device *pdev) return 0; } + +static struct dev_pm_ops au1xxx_ohci_pmops = { + .suspend = ohci_hcd_au1xxx_drv_suspend, + .resume = ohci_hcd_au1xxx_drv_resume, +}; + +#define AU1XXX_OHCI_PMOPS &au1xxx_ohci_pmops + #else -#define ohci_hcd_au1xxx_drv_suspend NULL -#define ohci_hcd_au1xxx_drv_resume NULL +#define AU1XXX_OHCI_PMOPS NULL #endif static struct platform_driver ohci_hcd_au1xxx_driver = { .probe = ohci_hcd_au1xxx_drv_probe, .remove = ohci_hcd_au1xxx_drv_remove, .shutdown = usb_hcd_platform_shutdown, - .suspend = ohci_hcd_au1xxx_drv_suspend, - .resume = ohci_hcd_au1xxx_drv_resume, .driver = { .name = "au1xxx-ohci", .owner = THIS_MODULE, + .pm = AU1XXX_OHCI_PMOPS, }, }; diff --git a/drivers/usb/host/ohci-ep93xx.c b/drivers/usb/host/ohci-ep93xx.c index b0dbf4157d29..4e681613e7ae 100644 --- a/drivers/usb/host/ohci-ep93xx.c +++ b/drivers/usb/host/ohci-ep93xx.c @@ -188,7 +188,6 @@ static int ohci_hcd_ep93xx_drv_resume(struct platform_device *pdev) { struct usb_hcd *hcd = platform_get_drvdata(pdev); struct ohci_hcd *ohci = hcd_to_ohci(hcd); - int status; if (time_before(jiffies, ohci->next_statechange)) msleep(5); diff --git a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c index 58151687d351..24eb74781919 100644 --- a/drivers/usb/host/ohci-hcd.c +++ b/drivers/usb/host/ohci-hcd.c @@ -34,7 +34,6 @@ #include <linux/usb/otg.h> #include <linux/dma-mapping.h> #include <linux/dmapool.h> -#include <linux/reboot.h> #include <linux/workqueue.h> #include <linux/debugfs.h> @@ -88,6 +87,7 @@ static int ohci_restart (struct ohci_hcd *ohci); #ifdef CONFIG_PCI static void quirk_amd_pll(int state); static void amd_iso_dev_put(void); +static void sb800_prefetch(struct ohci_hcd *ohci, int on); #else static inline void quirk_amd_pll(int state) { @@ -97,6 +97,10 @@ static inline void amd_iso_dev_put(void) { return; } +static inline void sb800_prefetch(struct ohci_hcd *ohci, int on) +{ + return; +} #endif diff --git a/drivers/usb/host/ohci-pci.c b/drivers/usb/host/ohci-pci.c index d2ba04dd785e..b8a1148f248e 100644 --- a/drivers/usb/host/ohci-pci.c +++ b/drivers/usb/host/ohci-pci.c @@ -177,6 +177,13 @@ static int ohci_quirk_amd700(struct usb_hcd *hcd) return 0; pci_read_config_byte(amd_smbus_dev, PCI_REVISION_ID, &rev); + + /* SB800 needs pre-fetch fix */ + if ((rev >= 0x40) && (rev <= 0x4f)) { + ohci->flags |= OHCI_QUIRK_AMD_PREFETCH; + ohci_dbg(ohci, "enabled AMD prefetch quirk\n"); + } + if ((rev > 0x3b) || (rev < 0x30)) { pci_dev_put(amd_smbus_dev); amd_smbus_dev = NULL; @@ -262,6 +269,19 @@ static void amd_iso_dev_put(void) } +static void sb800_prefetch(struct ohci_hcd *ohci, int on) +{ + struct pci_dev *pdev; + u16 misc; + + pdev = to_pci_dev(ohci_to_hcd(ohci)->self.controller); + pci_read_config_word(pdev, 0x50, &misc); + if (on == 0) + pci_write_config_word(pdev, 0x50, misc & 0xfcff); + else + pci_write_config_word(pdev, 0x50, misc | 0x0300); +} + /* List of quirks for OHCI */ static const struct pci_device_id ohci_pci_quirks[] = { { diff --git a/drivers/usb/host/ohci-pxa27x.c b/drivers/usb/host/ohci-pxa27x.c index e44dc2cbca24..f1c06202fdf2 100644 --- a/drivers/usb/host/ohci-pxa27x.c +++ b/drivers/usb/host/ohci-pxa27x.c @@ -177,9 +177,13 @@ static inline void pxa27x_setup_hc(struct pxa27x_ohci *ohci, if (inf->flags & NO_OC_PROTECTION) uhcrhda |= UHCRHDA_NOCP; + else + uhcrhda &= ~UHCRHDA_NOCP; if (inf->flags & OC_MODE_PERPORT) uhcrhda |= UHCRHDA_OCPM; + else + uhcrhda &= ~UHCRHDA_OCPM; if (inf->power_on_delay) { uhcrhda &= ~UHCRHDA_POTPGT(0xff); @@ -477,38 +481,47 @@ static int ohci_hcd_pxa27x_drv_remove(struct platform_device *pdev) return 0; } -#ifdef CONFIG_PM -static int ohci_hcd_pxa27x_drv_suspend(struct platform_device *pdev, pm_message_t state) +#ifdef CONFIG_PM +static int ohci_hcd_pxa27x_drv_suspend(struct device *dev) { - struct usb_hcd *hcd = platform_get_drvdata(pdev); + struct usb_hcd *hcd = dev_get_drvdata(dev); struct pxa27x_ohci *ohci = to_pxa27x_ohci(hcd); if (time_before(jiffies, ohci->ohci.next_statechange)) msleep(5); ohci->ohci.next_statechange = jiffies; - pxa27x_stop_hc(ohci, &pdev->dev); + pxa27x_stop_hc(ohci, dev); hcd->state = HC_STATE_SUSPENDED; return 0; } -static int ohci_hcd_pxa27x_drv_resume(struct platform_device *pdev) +static int ohci_hcd_pxa27x_drv_resume(struct device *dev) { - struct usb_hcd *hcd = platform_get_drvdata(pdev); + struct usb_hcd *hcd = dev_get_drvdata(dev); struct pxa27x_ohci *ohci = to_pxa27x_ohci(hcd); + struct pxaohci_platform_data *inf = dev->platform_data; int status; if (time_before(jiffies, ohci->ohci.next_statechange)) msleep(5); ohci->ohci.next_statechange = jiffies; - if ((status = pxa27x_start_hc(ohci, &pdev->dev)) < 0) + if ((status = pxa27x_start_hc(ohci, dev)) < 0) return status; + /* Select Power Management Mode */ + pxa27x_ohci_select_pmm(ohci, inf->port_mode); + ohci_finish_controller_resume(hcd); return 0; } + +static struct dev_pm_ops ohci_hcd_pxa27x_pm_ops = { + .suspend = ohci_hcd_pxa27x_drv_suspend, + .resume = ohci_hcd_pxa27x_drv_resume, +}; #endif /* work with hotplug and coldplug */ @@ -518,13 +531,12 @@ static struct platform_driver ohci_hcd_pxa27x_driver = { .probe = ohci_hcd_pxa27x_drv_probe, .remove = ohci_hcd_pxa27x_drv_remove, .shutdown = usb_hcd_platform_shutdown, -#ifdef CONFIG_PM - .suspend = ohci_hcd_pxa27x_drv_suspend, - .resume = ohci_hcd_pxa27x_drv_resume, -#endif .driver = { .name = "pxa27x-ohci", .owner = THIS_MODULE, +#ifdef CONFIG_PM + .pm = &ohci_hcd_pxa27x_pm_ops, +#endif }, }; diff --git a/drivers/usb/host/ohci-q.c b/drivers/usb/host/ohci-q.c index 16fecb8ecc39..35288bcae0db 100644 --- a/drivers/usb/host/ohci-q.c +++ b/drivers/usb/host/ohci-q.c @@ -49,9 +49,12 @@ __acquires(ohci->lock) switch (usb_pipetype (urb->pipe)) { case PIPE_ISOCHRONOUS: ohci_to_hcd(ohci)->self.bandwidth_isoc_reqs--; - if (ohci_to_hcd(ohci)->self.bandwidth_isoc_reqs == 0 - && quirk_amdiso(ohci)) - quirk_amd_pll(1); + if (ohci_to_hcd(ohci)->self.bandwidth_isoc_reqs == 0) { + if (quirk_amdiso(ohci)) + quirk_amd_pll(1); + if (quirk_amdprefetch(ohci)) + sb800_prefetch(ohci, 0); + } break; case PIPE_INTERRUPT: ohci_to_hcd(ohci)->self.bandwidth_int_reqs--; @@ -680,9 +683,12 @@ static void td_submit_urb ( data + urb->iso_frame_desc [cnt].offset, urb->iso_frame_desc [cnt].length, urb, cnt); } - if (ohci_to_hcd(ohci)->self.bandwidth_isoc_reqs == 0 - && quirk_amdiso(ohci)) - quirk_amd_pll(0); + if (ohci_to_hcd(ohci)->self.bandwidth_isoc_reqs == 0) { + if (quirk_amdiso(ohci)) + quirk_amd_pll(0); + if (quirk_amdprefetch(ohci)) + sb800_prefetch(ohci, 1); + } periodic = ohci_to_hcd(ohci)->self.bandwidth_isoc_reqs++ == 0 && ohci_to_hcd(ohci)->self.bandwidth_int_reqs == 0; break; diff --git a/drivers/usb/host/ohci.h b/drivers/usb/host/ohci.h index 222011f6172c..5bf15fed0d9f 100644 --- a/drivers/usb/host/ohci.h +++ b/drivers/usb/host/ohci.h @@ -402,6 +402,7 @@ struct ohci_hcd { #define OHCI_QUIRK_FRAME_NO 0x80 /* no big endian frame_no shift */ #define OHCI_QUIRK_HUB_POWER 0x100 /* distrust firmware power/oc setup */ #define OHCI_QUIRK_AMD_ISO 0x200 /* ISO transfers*/ +#define OHCI_QUIRK_AMD_PREFETCH 0x400 /* pre-fetch for ISO transfer */ // there are also chip quirks/bugs in init logic struct work_struct nec_work; /* Worker for NEC quirk */ @@ -433,6 +434,10 @@ static inline int quirk_amdiso(struct ohci_hcd *ohci) { return ohci->flags & OHCI_QUIRK_AMD_ISO; } +static inline int quirk_amdprefetch(struct ohci_hcd *ohci) +{ + return ohci->flags & OHCI_QUIRK_AMD_PREFETCH; +} #else static inline int quirk_nec(struct ohci_hcd *ohci) { @@ -446,6 +451,10 @@ static inline int quirk_amdiso(struct ohci_hcd *ohci) { return 0; } +static inline int quirk_amdprefetch(struct ohci_hcd *ohci) +{ + return 0; +} #endif /* convert between an hcd pointer and the corresponding ohci_hcd */ diff --git a/drivers/usb/host/oxu210hp-hcd.c b/drivers/usb/host/oxu210hp-hcd.c index 5ac489ee3dab..50f57f468836 100644 --- a/drivers/usb/host/oxu210hp-hcd.c +++ b/drivers/usb/host/oxu210hp-hcd.c @@ -33,7 +33,6 @@ #include <linux/timer.h> #include <linux/list.h> #include <linux/interrupt.h> -#include <linux/reboot.h> #include <linux/usb.h> #include <linux/moduleparam.h> #include <linux/dma-mapping.h> diff --git a/drivers/usb/host/r8a66597-hcd.c b/drivers/usb/host/r8a66597-hcd.c index 749b53742828..e33d36256350 100644 --- a/drivers/usb/host/r8a66597-hcd.c +++ b/drivers/usb/host/r8a66597-hcd.c @@ -1003,19 +1003,20 @@ static void r8a66597_check_syssts(struct r8a66597 *r8a66597, int port, if (syssts == SE0) { r8a66597_write(r8a66597, ~ATTCH, get_intsts_reg(port)); r8a66597_bset(r8a66597, ATTCHE, get_intenb_reg(port)); - return; - } + } else { + if (syssts == FS_JSTS) + r8a66597_bset(r8a66597, HSE, get_syscfg_reg(port)); + else if (syssts == LS_JSTS) + r8a66597_bclr(r8a66597, HSE, get_syscfg_reg(port)); - if (syssts == FS_JSTS) - r8a66597_bset(r8a66597, HSE, get_syscfg_reg(port)); - else if (syssts == LS_JSTS) - r8a66597_bclr(r8a66597, HSE, get_syscfg_reg(port)); + r8a66597_write(r8a66597, ~DTCH, get_intsts_reg(port)); + r8a66597_bset(r8a66597, DTCHE, get_intenb_reg(port)); - r8a66597_write(r8a66597, ~DTCH, get_intsts_reg(port)); - r8a66597_bset(r8a66597, DTCHE, get_intenb_reg(port)); + if (r8a66597->bus_suspended) + usb_hcd_resume_root_hub(r8a66597_to_hcd(r8a66597)); + } - if (r8a66597->bus_suspended) - usb_hcd_resume_root_hub(r8a66597_to_hcd(r8a66597)); + usb_hcd_poll_rh_status(r8a66597_to_hcd(r8a66597)); } /* this function must be called with interrupt disabled */ @@ -1024,6 +1025,8 @@ static void r8a66597_usb_connect(struct r8a66597 *r8a66597, int port) u16 speed = get_rh_usb_speed(r8a66597, port); struct r8a66597_root_hub *rh = &r8a66597->root_hub[port]; + rh->port &= ~((1 << USB_PORT_FEAT_HIGHSPEED) | + (1 << USB_PORT_FEAT_LOWSPEED)); if (speed == HSMODE) rh->port |= (1 << USB_PORT_FEAT_HIGHSPEED); else if (speed == LSMODE) diff --git a/drivers/usb/host/sl811-hcd.c b/drivers/usb/host/sl811-hcd.c index a949259f18b9..5b22a4d1c9e4 100644 --- a/drivers/usb/host/sl811-hcd.c +++ b/drivers/usb/host/sl811-hcd.c @@ -719,8 +719,12 @@ retry: /* port status seems weird until after reset, so * force the reset and make khubd clean up later. */ - sl811->port1 |= (1 << USB_PORT_FEAT_C_CONNECTION) - | (1 << USB_PORT_FEAT_CONNECTION); + if (sl811->stat_insrmv & 1) + sl811->port1 |= 1 << USB_PORT_FEAT_CONNECTION; + else + sl811->port1 &= ~(1 << USB_PORT_FEAT_CONNECTION); + + sl811->port1 |= 1 << USB_PORT_FEAT_C_CONNECTION; } else if (irqstat & SL11H_INTMASK_RD) { if (sl811->port1 & (1 << USB_PORT_FEAT_SUSPEND)) { diff --git a/drivers/usb/host/uhci-q.c b/drivers/usb/host/uhci-q.c index 64e57bfe236b..acd582c02802 100644 --- a/drivers/usb/host/uhci-q.c +++ b/drivers/usb/host/uhci-q.c @@ -1422,7 +1422,6 @@ static int uhci_urb_enqueue(struct usb_hcd *hcd, goto err_submit_failed; /* Add this URB to the QH */ - urbp->qh = qh; list_add_tail(&urbp->node, &qh->queue); /* If the new URB is the first and only one on this QH then either diff --git a/drivers/usb/host/whci/asl.c b/drivers/usb/host/whci/asl.c index c2050785a819..562eba108816 100644 --- a/drivers/usb/host/whci/asl.c +++ b/drivers/usb/host/whci/asl.c @@ -115,6 +115,10 @@ static uint32_t process_qset(struct whc *whc, struct whc_qset *qset) if (status & QTD_STS_HALTED) { /* Ug, an error. */ process_halted_qtd(whc, qset, td); + /* A halted qTD always triggers an update + because the qset was either removed or + reactivated. */ + update |= WHC_UPDATE_UPDATED; goto done; } @@ -227,11 +231,21 @@ void scan_async_work(struct work_struct *work) /* * Now that the ASL is updated, complete the removal of any * removed qsets. + * + * If the qset was to be reset, do so and reinsert it into the + * ASL if it has pending transfers. */ spin_lock_irq(&whc->lock); list_for_each_entry_safe(qset, t, &whc->async_removed_list, list_node) { qset_remove_complete(whc, qset); + if (qset->reset) { + qset_reset(whc, qset); + if (!list_empty(&qset->stds)) { + asl_qset_insert_begin(whc, qset); + queue_work(whc->workqueue, &whc->async_work); + } + } } spin_unlock_irq(&whc->lock); @@ -267,7 +281,7 @@ int asl_urb_enqueue(struct whc *whc, struct urb *urb, gfp_t mem_flags) else err = qset_add_urb(whc, qset, urb, GFP_ATOMIC); if (!err) { - if (!qset->in_sw_list) + if (!qset->in_sw_list && !qset->remove) asl_qset_insert_begin(whc, qset); } else usb_hcd_unlink_urb_from_ep(&whc->wusbhc.usb_hcd, urb); @@ -295,6 +309,7 @@ int asl_urb_dequeue(struct whc *whc, struct urb *urb, int status) struct whc_urb *wurb = urb->hcpriv; struct whc_qset *qset = wurb->qset; struct whc_std *std, *t; + bool has_qtd = false; int ret; unsigned long flags; @@ -305,17 +320,21 @@ int asl_urb_dequeue(struct whc *whc, struct urb *urb, int status) goto out; list_for_each_entry_safe(std, t, &qset->stds, list_node) { - if (std->urb == urb) + if (std->urb == urb) { + if (std->qtd) + has_qtd = true; qset_free_std(whc, std); - else + } else std->qtd = NULL; /* so this std is re-added when the qset is */ } - asl_qset_remove(whc, qset); - wurb->status = status; - wurb->is_async = true; - queue_work(whc->workqueue, &wurb->dequeue_work); - + if (has_qtd) { + asl_qset_remove(whc, qset); + wurb->status = status; + wurb->is_async = true; + queue_work(whc->workqueue, &wurb->dequeue_work); + } else + qset_remove_urb(whc, qset, urb, status); out: spin_unlock_irqrestore(&whc->lock, flags); diff --git a/drivers/usb/host/whci/debug.c b/drivers/usb/host/whci/debug.c index cf2d45946c57..2273c815941f 100644 --- a/drivers/usb/host/whci/debug.c +++ b/drivers/usb/host/whci/debug.c @@ -134,7 +134,7 @@ static int pzl_open(struct inode *inode, struct file *file) return single_open(file, pzl_print, inode->i_private); } -static struct file_operations di_fops = { +static const struct file_operations di_fops = { .open = di_open, .read = seq_read, .llseek = seq_lseek, @@ -142,7 +142,7 @@ static struct file_operations di_fops = { .owner = THIS_MODULE, }; -static struct file_operations asl_fops = { +static const struct file_operations asl_fops = { .open = asl_open, .read = seq_read, .llseek = seq_lseek, @@ -150,7 +150,7 @@ static struct file_operations asl_fops = { .owner = THIS_MODULE, }; -static struct file_operations pzl_fops = { +static const struct file_operations pzl_fops = { .open = pzl_open, .read = seq_read, .llseek = seq_lseek, diff --git a/drivers/usb/host/whci/hcd.c b/drivers/usb/host/whci/hcd.c index e019a5058ab8..687b622a1612 100644 --- a/drivers/usb/host/whci/hcd.c +++ b/drivers/usb/host/whci/hcd.c @@ -192,19 +192,23 @@ static void whc_endpoint_reset(struct usb_hcd *usb_hcd, struct wusbhc *wusbhc = usb_hcd_to_wusbhc(usb_hcd); struct whc *whc = wusbhc_to_whc(wusbhc); struct whc_qset *qset; + unsigned long flags; + + spin_lock_irqsave(&whc->lock, flags); qset = ep->hcpriv; if (qset) { qset->remove = 1; + qset->reset = 1; if (usb_endpoint_xfer_bulk(&ep->desc) || usb_endpoint_xfer_control(&ep->desc)) queue_work(whc->workqueue, &whc->async_work); else queue_work(whc->workqueue, &whc->periodic_work); - - qset_reset(whc, qset); } + + spin_unlock_irqrestore(&whc->lock, flags); } diff --git a/drivers/usb/host/whci/pzl.c b/drivers/usb/host/whci/pzl.c index ff4ef9e910d9..0db3fb2dc03a 100644 --- a/drivers/usb/host/whci/pzl.c +++ b/drivers/usb/host/whci/pzl.c @@ -121,6 +121,10 @@ static enum whc_update pzl_process_qset(struct whc *whc, struct whc_qset *qset) if (status & QTD_STS_HALTED) { /* Ug, an error. */ process_halted_qtd(whc, qset, td); + /* A halted qTD always triggers an update + because the qset was either removed or + reactivated. */ + update |= WHC_UPDATE_UPDATED; goto done; } @@ -255,11 +259,21 @@ void scan_periodic_work(struct work_struct *work) /* * Now that the PZL is updated, complete the removal of any * removed qsets. + * + * If the qset was to be reset, do so and reinsert it into the + * PZL if it has pending transfers. */ spin_lock_irq(&whc->lock); list_for_each_entry_safe(qset, t, &whc->periodic_removed_list, list_node) { qset_remove_complete(whc, qset); + if (qset->reset) { + qset_reset(whc, qset); + if (!list_empty(&qset->stds)) { + qset_insert_in_sw_list(whc, qset); + queue_work(whc->workqueue, &whc->periodic_work); + } + } } spin_unlock_irq(&whc->lock); @@ -295,7 +309,7 @@ int pzl_urb_enqueue(struct whc *whc, struct urb *urb, gfp_t mem_flags) else err = qset_add_urb(whc, qset, urb, GFP_ATOMIC); if (!err) { - if (!qset->in_sw_list) + if (!qset->in_sw_list && !qset->remove) qset_insert_in_sw_list(whc, qset); } else usb_hcd_unlink_urb_from_ep(&whc->wusbhc.usb_hcd, urb); @@ -323,6 +337,7 @@ int pzl_urb_dequeue(struct whc *whc, struct urb *urb, int status) struct whc_urb *wurb = urb->hcpriv; struct whc_qset *qset = wurb->qset; struct whc_std *std, *t; + bool has_qtd = false; int ret; unsigned long flags; @@ -333,17 +348,22 @@ int pzl_urb_dequeue(struct whc *whc, struct urb *urb, int status) goto out; list_for_each_entry_safe(std, t, &qset->stds, list_node) { - if (std->urb == urb) + if (std->urb == urb) { + if (std->qtd) + has_qtd = true; qset_free_std(whc, std); - else + } else std->qtd = NULL; /* so this std is re-added when the qset is */ } - pzl_qset_remove(whc, qset); - wurb->status = status; - wurb->is_async = false; - queue_work(whc->workqueue, &wurb->dequeue_work); - + if (has_qtd) { + pzl_qset_remove(whc, qset); + update_pzl_hw_view(whc); + wurb->status = status; + wurb->is_async = false; + queue_work(whc->workqueue, &wurb->dequeue_work); + } else + qset_remove_urb(whc, qset, urb, status); out: spin_unlock_irqrestore(&whc->lock, flags); diff --git a/drivers/usb/host/whci/qset.c b/drivers/usb/host/whci/qset.c index 640b38fbd051..1b9dc1571570 100644 --- a/drivers/usb/host/whci/qset.c +++ b/drivers/usb/host/whci/qset.c @@ -103,7 +103,6 @@ static void qset_fill_qh(struct whc_qset *qset, struct urb *urb) void qset_clear(struct whc *whc, struct whc_qset *qset) { qset->td_start = qset->td_end = qset->ntds = 0; - qset->remove = 0; qset->qh.link = cpu_to_le32(QH_LINK_NTDS(8) | QH_LINK_T); qset->qh.status = qset->qh.status & QH_STATUS_SEQ_MASK; @@ -125,7 +124,7 @@ void qset_clear(struct whc *whc, struct whc_qset *qset) */ void qset_reset(struct whc *whc, struct whc_qset *qset) { - wait_for_completion(&qset->remove_complete); + qset->reset = 0; qset->qh.status &= ~QH_STATUS_SEQ_MASK; qset->qh.cur_window = cpu_to_le32((1 << qset->max_burst) - 1); @@ -156,6 +155,7 @@ struct whc_qset *get_qset(struct whc *whc, struct urb *urb, void qset_remove_complete(struct whc *whc, struct whc_qset *qset) { + qset->remove = 0; list_del_init(&qset->list_node); complete(&qset->remove_complete); } diff --git a/drivers/usb/host/whci/whci-hc.h b/drivers/usb/host/whci/whci-hc.h index 794dba0d0f0a..e8d0001605be 100644 --- a/drivers/usb/host/whci/whci-hc.h +++ b/drivers/usb/host/whci/whci-hc.h @@ -264,6 +264,7 @@ struct whc_qset { unsigned in_sw_list:1; unsigned in_hw_list:1; unsigned remove:1; + unsigned reset:1; struct urb *pause_after_urb; struct completion remove_complete; int max_burst; diff --git a/drivers/usb/host/xhci-dbg.c b/drivers/usb/host/xhci-dbg.c index 705e34324156..33128d52f212 100644 --- a/drivers/usb/host/xhci-dbg.c +++ b/drivers/usb/host/xhci-dbg.c @@ -413,7 +413,8 @@ void xhci_dbg_slot_ctx(struct xhci_hcd *xhci, struct xhci_container_ctx *ctx) int i; struct xhci_slot_ctx *slot_ctx = xhci_get_slot_ctx(xhci, ctx); - dma_addr_t dma = ctx->dma + ((unsigned long)slot_ctx - (unsigned long)ctx); + dma_addr_t dma = ctx->dma + + ((unsigned long)slot_ctx - (unsigned long)ctx->bytes); int csz = HCC_64BYTE_CONTEXT(xhci->hcc_params); xhci_dbg(xhci, "Slot Context:\n"); @@ -459,7 +460,7 @@ void xhci_dbg_ep_ctx(struct xhci_hcd *xhci, for (i = 0; i < last_ep_ctx; ++i) { struct xhci_ep_ctx *ep_ctx = xhci_get_ep_ctx(xhci, ctx, i); dma_addr_t dma = ctx->dma + - ((unsigned long)ep_ctx - (unsigned long)ctx); + ((unsigned long)ep_ctx - (unsigned long)ctx->bytes); xhci_dbg(xhci, "Endpoint %02d Context:\n", i); xhci_dbg(xhci, "@%p (virt) @%08llx (dma) %#08x - ep_info\n", diff --git a/drivers/usb/host/xhci-hcd.c b/drivers/usb/host/xhci-hcd.c index 816c39caca1c..932f99938481 100644 --- a/drivers/usb/host/xhci-hcd.c +++ b/drivers/usb/host/xhci-hcd.c @@ -22,12 +22,18 @@ #include <linux/irq.h> #include <linux/module.h> +#include <linux/moduleparam.h> #include "xhci.h" #define DRIVER_AUTHOR "Sarah Sharp" #define DRIVER_DESC "'eXtensible' Host Controller (xHC) Driver" +/* Some 0.95 hardware can't handle the chain bit on a Link TRB being cleared */ +static int link_quirk; +module_param(link_quirk, int, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(link_quirk, "Don't clear the chain bit on a link TRB"); + /* TODO: copied from ehci-hcd.c - can this be refactored? */ /* * handshake - spin reading hc until handshake completes or fails @@ -214,6 +220,12 @@ int xhci_init(struct usb_hcd *hcd) xhci_dbg(xhci, "xhci_init\n"); spin_lock_init(&xhci->lock); + if (link_quirk) { + xhci_dbg(xhci, "QUIRK: Not clearing Link TRB chain bits.\n"); + xhci->quirks |= XHCI_LINK_TRB_QUIRK; + } else { + xhci_dbg(xhci, "xHCI doesn't need link TRB QUIRK\n"); + } retval = xhci_mem_init(xhci, GFP_KERNEL); xhci_dbg(xhci, "Finished xhci_init\n"); @@ -323,6 +335,12 @@ void xhci_event_ring_work(unsigned long arg) spin_lock_irqsave(&xhci->lock, flags); temp = xhci_readl(xhci, &xhci->op_regs->status); xhci_dbg(xhci, "op reg status = 0x%x\n", temp); + if (temp == 0xffffffff) { + xhci_dbg(xhci, "HW died, polling stopped.\n"); + spin_unlock_irqrestore(&xhci->lock, flags); + return; + } + temp = xhci_readl(xhci, &xhci->ir_set->irq_pending); xhci_dbg(xhci, "ir_set 0 pending = 0x%x\n", temp); xhci_dbg(xhci, "No-op commands handled = %d\n", xhci->noops_handled); @@ -339,13 +357,14 @@ void xhci_event_ring_work(unsigned long arg) xhci_dbg_ring_ptrs(xhci, xhci->cmd_ring); xhci_dbg_cmd_ptrs(xhci); for (i = 0; i < MAX_HC_SLOTS; ++i) { - if (xhci->devs[i]) { - for (j = 0; j < 31; ++j) { - if (xhci->devs[i]->ep_rings[j]) { - xhci_dbg(xhci, "Dev %d endpoint ring %d:\n", i, j); - xhci_debug_segment(xhci, xhci->devs[i]->ep_rings[j]->deq_seg); - } - } + if (!xhci->devs[i]) + continue; + for (j = 0; j < 31; ++j) { + struct xhci_ring *ring = xhci->devs[i]->eps[j].ring; + if (!ring) + continue; + xhci_dbg(xhci, "Dev %d endpoint ring %d:\n", i, j); + xhci_debug_segment(xhci, ring->deq_seg); } } @@ -555,13 +574,22 @@ unsigned int xhci_get_endpoint_flag(struct usb_endpoint_descriptor *desc) return 1 << (xhci_get_endpoint_index(desc) + 1); } +/* Find the flag for this endpoint (for use in the control context). Use the + * endpoint index to create a bitmask. The slot context is bit 0, endpoint 0 is + * bit 1, etc. + */ +unsigned int xhci_get_endpoint_flag_from_index(unsigned int ep_index) +{ + return 1 << (ep_index + 1); +} + /* Compute the last valid endpoint context index. Basically, this is the * endpoint index plus one. For slot contexts with more than valid endpoint, * we find the most significant bit set in the added contexts flags. * e.g. ep 1 IN (with epnum 0x81) => added_ctxs = 0b1000 * fls(0b1000) = 4, but the endpoint context index is 3, so subtract one. */ -static inline unsigned int xhci_last_valid_endpoint(u32 added_ctxs) +unsigned int xhci_last_valid_endpoint(u32 added_ctxs) { return fls(added_ctxs) - 1; } @@ -589,6 +617,71 @@ int xhci_check_args(struct usb_hcd *hcd, struct usb_device *udev, return 1; } +static int xhci_configure_endpoint(struct xhci_hcd *xhci, + struct usb_device *udev, struct xhci_command *command, + bool ctx_change, bool must_succeed); + +/* + * Full speed devices may have a max packet size greater than 8 bytes, but the + * USB core doesn't know that until it reads the first 8 bytes of the + * descriptor. If the usb_device's max packet size changes after that point, + * we need to issue an evaluate context command and wait on it. + */ +static int xhci_check_maxpacket(struct xhci_hcd *xhci, unsigned int slot_id, + unsigned int ep_index, struct urb *urb) +{ + struct xhci_container_ctx *in_ctx; + struct xhci_container_ctx *out_ctx; + struct xhci_input_control_ctx *ctrl_ctx; + struct xhci_ep_ctx *ep_ctx; + int max_packet_size; + int hw_max_packet_size; + int ret = 0; + + out_ctx = xhci->devs[slot_id]->out_ctx; + ep_ctx = xhci_get_ep_ctx(xhci, out_ctx, ep_index); + hw_max_packet_size = MAX_PACKET_DECODED(ep_ctx->ep_info2); + max_packet_size = urb->dev->ep0.desc.wMaxPacketSize; + if (hw_max_packet_size != max_packet_size) { + xhci_dbg(xhci, "Max Packet Size for ep 0 changed.\n"); + xhci_dbg(xhci, "Max packet size in usb_device = %d\n", + max_packet_size); + xhci_dbg(xhci, "Max packet size in xHCI HW = %d\n", + hw_max_packet_size); + xhci_dbg(xhci, "Issuing evaluate context command.\n"); + + /* Set up the modified control endpoint 0 */ + xhci_endpoint_copy(xhci, xhci->devs[slot_id]->in_ctx, + xhci->devs[slot_id]->out_ctx, ep_index); + in_ctx = xhci->devs[slot_id]->in_ctx; + ep_ctx = xhci_get_ep_ctx(xhci, in_ctx, ep_index); + ep_ctx->ep_info2 &= ~MAX_PACKET_MASK; + ep_ctx->ep_info2 |= MAX_PACKET(max_packet_size); + + /* Set up the input context flags for the command */ + /* FIXME: This won't work if a non-default control endpoint + * changes max packet sizes. + */ + ctrl_ctx = xhci_get_input_control_ctx(xhci, in_ctx); + ctrl_ctx->add_flags = EP0_FLAG; + ctrl_ctx->drop_flags = 0; + + xhci_dbg(xhci, "Slot %d input context\n", slot_id); + xhci_dbg_ctx(xhci, in_ctx, ep_index); + xhci_dbg(xhci, "Slot %d output context\n", slot_id); + xhci_dbg_ctx(xhci, out_ctx, ep_index); + + ret = xhci_configure_endpoint(xhci, urb->dev, NULL, + true, false); + + /* Clean up the input context for later use by bandwidth + * functions. + */ + ctrl_ctx->add_flags = SLOT_FLAG; + } + return ret; +} + /* * non-error returns are a promise to giveback() the urb later * we drop ownership so next owner (or urb unlink) can get it @@ -600,13 +693,13 @@ int xhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flags) int ret = 0; unsigned int slot_id, ep_index; + if (!urb || xhci_check_args(hcd, urb->dev, urb->ep, true, __func__) <= 0) return -EINVAL; slot_id = urb->dev->slot_id; ep_index = xhci_get_endpoint_index(&urb->ep->desc); - spin_lock_irqsave(&xhci->lock, flags); if (!xhci->devs || !xhci->devs[slot_id]) { if (!in_interrupt()) dev_warn(&urb->dev->dev, "WARN: urb submitted for dev with no Slot ID\n"); @@ -619,19 +712,38 @@ int xhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flags) ret = -ESHUTDOWN; goto exit; } - if (usb_endpoint_xfer_control(&urb->ep->desc)) + if (usb_endpoint_xfer_control(&urb->ep->desc)) { + /* Check to see if the max packet size for the default control + * endpoint changed during FS device enumeration + */ + if (urb->dev->speed == USB_SPEED_FULL) { + ret = xhci_check_maxpacket(xhci, slot_id, + ep_index, urb); + if (ret < 0) + return ret; + } + /* We have a spinlock and interrupts disabled, so we must pass * atomic context to this function, which may allocate memory. */ + spin_lock_irqsave(&xhci->lock, flags); ret = xhci_queue_ctrl_tx(xhci, GFP_ATOMIC, urb, slot_id, ep_index); - else if (usb_endpoint_xfer_bulk(&urb->ep->desc)) + spin_unlock_irqrestore(&xhci->lock, flags); + } else if (usb_endpoint_xfer_bulk(&urb->ep->desc)) { + spin_lock_irqsave(&xhci->lock, flags); ret = xhci_queue_bulk_tx(xhci, GFP_ATOMIC, urb, slot_id, ep_index); - else + spin_unlock_irqrestore(&xhci->lock, flags); + } else if (usb_endpoint_xfer_int(&urb->ep->desc)) { + spin_lock_irqsave(&xhci->lock, flags); + ret = xhci_queue_intr_tx(xhci, GFP_ATOMIC, urb, + slot_id, ep_index); + spin_unlock_irqrestore(&xhci->lock, flags); + } else { ret = -EINVAL; + } exit: - spin_unlock_irqrestore(&xhci->lock, flags); return ret; } @@ -670,10 +782,12 @@ int xhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) { unsigned long flags; int ret; + u32 temp; struct xhci_hcd *xhci; struct xhci_td *td; unsigned int ep_index; struct xhci_ring *ep_ring; + struct xhci_virt_ep *ep; xhci = hcd_to_xhci(hcd); spin_lock_irqsave(&xhci->lock, flags); @@ -681,22 +795,34 @@ int xhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) ret = usb_hcd_check_unlink_urb(hcd, urb, status); if (ret || !urb->hcpriv) goto done; + temp = xhci_readl(xhci, &xhci->op_regs->status); + if (temp == 0xffffffff) { + xhci_dbg(xhci, "HW died, freeing TD.\n"); + td = (struct xhci_td *) urb->hcpriv; + + usb_hcd_unlink_urb_from_ep(hcd, urb); + spin_unlock_irqrestore(&xhci->lock, flags); + usb_hcd_giveback_urb(xhci_to_hcd(xhci), urb, -ESHUTDOWN); + kfree(td); + return ret; + } xhci_dbg(xhci, "Cancel URB %p\n", urb); xhci_dbg(xhci, "Event ring:\n"); xhci_debug_ring(xhci, xhci->event_ring); ep_index = xhci_get_endpoint_index(&urb->ep->desc); - ep_ring = xhci->devs[urb->dev->slot_id]->ep_rings[ep_index]; + ep = &xhci->devs[urb->dev->slot_id]->eps[ep_index]; + ep_ring = ep->ring; xhci_dbg(xhci, "Endpoint ring:\n"); xhci_debug_ring(xhci, ep_ring); td = (struct xhci_td *) urb->hcpriv; - ep_ring->cancels_pending++; - list_add_tail(&td->cancelled_td_list, &ep_ring->cancelled_td_list); + ep->cancels_pending++; + list_add_tail(&td->cancelled_td_list, &ep->cancelled_td_list); /* Queue a stop endpoint command, but only if this is * the first cancellation to be handled. */ - if (ep_ring->cancels_pending == 1) { + if (ep->cancels_pending == 1) { xhci_queue_stop_endpoint(xhci, urb->dev->slot_id, ep_index); xhci_ring_cmd_db(xhci); } @@ -769,7 +895,7 @@ int xhci_drop_endpoint(struct usb_hcd *hcd, struct usb_device *udev, ctrl_ctx->drop_flags |= drop_flag; new_drop_flags = ctrl_ctx->drop_flags; - ctrl_ctx->add_flags = ~drop_flag; + ctrl_ctx->add_flags &= ~drop_flag; new_add_flags = ctrl_ctx->add_flags; last_ctx = xhci_last_valid_endpoint(ctrl_ctx->add_flags); @@ -930,6 +1056,141 @@ static void xhci_zero_in_ctx(struct xhci_hcd *xhci, struct xhci_virt_device *vir } } +static int xhci_configure_endpoint_result(struct xhci_hcd *xhci, + struct usb_device *udev, int *cmd_status) +{ + int ret; + + switch (*cmd_status) { + case COMP_ENOMEM: + dev_warn(&udev->dev, "Not enough host controller resources " + "for new device state.\n"); + ret = -ENOMEM; + /* FIXME: can we allocate more resources for the HC? */ + break; + case COMP_BW_ERR: + dev_warn(&udev->dev, "Not enough bandwidth " + "for new device state.\n"); + ret = -ENOSPC; + /* FIXME: can we go back to the old state? */ + break; + case COMP_TRB_ERR: + /* the HCD set up something wrong */ + dev_warn(&udev->dev, "ERROR: Endpoint drop flag = 0, " + "add flag = 1, " + "and endpoint is not disabled.\n"); + ret = -EINVAL; + break; + case COMP_SUCCESS: + dev_dbg(&udev->dev, "Successful Endpoint Configure command\n"); + ret = 0; + break; + default: + xhci_err(xhci, "ERROR: unexpected command completion " + "code 0x%x.\n", *cmd_status); + ret = -EINVAL; + break; + } + return ret; +} + +static int xhci_evaluate_context_result(struct xhci_hcd *xhci, + struct usb_device *udev, int *cmd_status) +{ + int ret; + struct xhci_virt_device *virt_dev = xhci->devs[udev->slot_id]; + + switch (*cmd_status) { + case COMP_EINVAL: + dev_warn(&udev->dev, "WARN: xHCI driver setup invalid evaluate " + "context command.\n"); + ret = -EINVAL; + break; + case COMP_EBADSLT: + dev_warn(&udev->dev, "WARN: slot not enabled for" + "evaluate context command.\n"); + case COMP_CTX_STATE: + dev_warn(&udev->dev, "WARN: invalid context state for " + "evaluate context command.\n"); + xhci_dbg_ctx(xhci, virt_dev->out_ctx, 1); + ret = -EINVAL; + break; + case COMP_SUCCESS: + dev_dbg(&udev->dev, "Successful evaluate context command\n"); + ret = 0; + break; + default: + xhci_err(xhci, "ERROR: unexpected command completion " + "code 0x%x.\n", *cmd_status); + ret = -EINVAL; + break; + } + return ret; +} + +/* Issue a configure endpoint command or evaluate context command + * and wait for it to finish. + */ +static int xhci_configure_endpoint(struct xhci_hcd *xhci, + struct usb_device *udev, + struct xhci_command *command, + bool ctx_change, bool must_succeed) +{ + int ret; + int timeleft; + unsigned long flags; + struct xhci_container_ctx *in_ctx; + struct completion *cmd_completion; + int *cmd_status; + struct xhci_virt_device *virt_dev; + + spin_lock_irqsave(&xhci->lock, flags); + virt_dev = xhci->devs[udev->slot_id]; + if (command) { + in_ctx = command->in_ctx; + cmd_completion = command->completion; + cmd_status = &command->status; + command->command_trb = xhci->cmd_ring->enqueue; + list_add_tail(&command->cmd_list, &virt_dev->cmd_list); + } else { + in_ctx = virt_dev->in_ctx; + cmd_completion = &virt_dev->cmd_completion; + cmd_status = &virt_dev->cmd_status; + } + + if (!ctx_change) + ret = xhci_queue_configure_endpoint(xhci, in_ctx->dma, + udev->slot_id, must_succeed); + else + ret = xhci_queue_evaluate_context(xhci, in_ctx->dma, + udev->slot_id); + if (ret < 0) { + spin_unlock_irqrestore(&xhci->lock, flags); + xhci_dbg(xhci, "FIXME allocate a new ring segment\n"); + return -ENOMEM; + } + xhci_ring_cmd_db(xhci); + spin_unlock_irqrestore(&xhci->lock, flags); + + /* Wait for the configure endpoint command to complete */ + timeleft = wait_for_completion_interruptible_timeout( + cmd_completion, + USB_CTRL_SET_TIMEOUT); + if (timeleft <= 0) { + xhci_warn(xhci, "%s while waiting for %s command\n", + timeleft == 0 ? "Timeout" : "Signal", + ctx_change == 0 ? + "configure endpoint" : + "evaluate context"); + /* FIXME cancel the configure endpoint command */ + return -ETIME; + } + + if (!ctx_change) + return xhci_configure_endpoint_result(xhci, udev, cmd_status); + return xhci_evaluate_context_result(xhci, udev, cmd_status); +} + /* Called after one or more calls to xhci_add_endpoint() or * xhci_drop_endpoint(). If this call fails, the USB core is expected * to call xhci_reset_bandwidth(). @@ -944,8 +1205,6 @@ int xhci_check_bandwidth(struct usb_hcd *hcd, struct usb_device *udev) { int i; int ret = 0; - int timeleft; - unsigned long flags; struct xhci_hcd *xhci; struct xhci_virt_device *virt_dev; struct xhci_input_control_ctx *ctrl_ctx; @@ -975,56 +1234,8 @@ int xhci_check_bandwidth(struct usb_hcd *hcd, struct usb_device *udev) xhci_dbg_ctx(xhci, virt_dev->in_ctx, LAST_CTX_TO_EP_NUM(slot_ctx->dev_info)); - spin_lock_irqsave(&xhci->lock, flags); - ret = xhci_queue_configure_endpoint(xhci, virt_dev->in_ctx->dma, - udev->slot_id); - if (ret < 0) { - spin_unlock_irqrestore(&xhci->lock, flags); - xhci_dbg(xhci, "FIXME allocate a new ring segment\n"); - return -ENOMEM; - } - xhci_ring_cmd_db(xhci); - spin_unlock_irqrestore(&xhci->lock, flags); - - /* Wait for the configure endpoint command to complete */ - timeleft = wait_for_completion_interruptible_timeout( - &virt_dev->cmd_completion, - USB_CTRL_SET_TIMEOUT); - if (timeleft <= 0) { - xhci_warn(xhci, "%s while waiting for configure endpoint command\n", - timeleft == 0 ? "Timeout" : "Signal"); - /* FIXME cancel the configure endpoint command */ - return -ETIME; - } - - switch (virt_dev->cmd_status) { - case COMP_ENOMEM: - dev_warn(&udev->dev, "Not enough host controller resources " - "for new device state.\n"); - ret = -ENOMEM; - /* FIXME: can we allocate more resources for the HC? */ - break; - case COMP_BW_ERR: - dev_warn(&udev->dev, "Not enough bandwidth " - "for new device state.\n"); - ret = -ENOSPC; - /* FIXME: can we go back to the old state? */ - break; - case COMP_TRB_ERR: - /* the HCD set up something wrong */ - dev_warn(&udev->dev, "ERROR: Endpoint drop flag = 0, add flag = 1, " - "and endpoint is not disabled.\n"); - ret = -EINVAL; - break; - case COMP_SUCCESS: - dev_dbg(&udev->dev, "Successful Endpoint Configure command\n"); - break; - default: - xhci_err(xhci, "ERROR: unexpected command completion " - "code 0x%x.\n", virt_dev->cmd_status); - ret = -EINVAL; - break; - } + ret = xhci_configure_endpoint(xhci, udev, NULL, + false, false); if (ret) { /* Callee should call reset_bandwidth() */ return ret; @@ -1037,10 +1248,10 @@ int xhci_check_bandwidth(struct usb_hcd *hcd, struct usb_device *udev) xhci_zero_in_ctx(xhci, virt_dev); /* Free any old rings */ for (i = 1; i < 31; ++i) { - if (virt_dev->new_ep_rings[i]) { - xhci_ring_free(xhci, virt_dev->ep_rings[i]); - virt_dev->ep_rings[i] = virt_dev->new_ep_rings[i]; - virt_dev->new_ep_rings[i] = NULL; + if (virt_dev->eps[i].new_ring) { + xhci_ring_free(xhci, virt_dev->eps[i].ring); + virt_dev->eps[i].ring = virt_dev->eps[i].new_ring; + virt_dev->eps[i].new_ring = NULL; } } @@ -1067,14 +1278,93 @@ void xhci_reset_bandwidth(struct usb_hcd *hcd, struct usb_device *udev) virt_dev = xhci->devs[udev->slot_id]; /* Free any rings allocated for added endpoints */ for (i = 0; i < 31; ++i) { - if (virt_dev->new_ep_rings[i]) { - xhci_ring_free(xhci, virt_dev->new_ep_rings[i]); - virt_dev->new_ep_rings[i] = NULL; + if (virt_dev->eps[i].new_ring) { + xhci_ring_free(xhci, virt_dev->eps[i].new_ring); + virt_dev->eps[i].new_ring = NULL; } } xhci_zero_in_ctx(xhci, virt_dev); } +static void xhci_setup_input_ctx_for_config_ep(struct xhci_hcd *xhci, + struct xhci_container_ctx *in_ctx, + struct xhci_container_ctx *out_ctx, + u32 add_flags, u32 drop_flags) +{ + struct xhci_input_control_ctx *ctrl_ctx; + ctrl_ctx = xhci_get_input_control_ctx(xhci, in_ctx); + ctrl_ctx->add_flags = add_flags; + ctrl_ctx->drop_flags = drop_flags; + xhci_slot_copy(xhci, in_ctx, out_ctx); + ctrl_ctx->add_flags |= SLOT_FLAG; + + xhci_dbg(xhci, "Input Context:\n"); + xhci_dbg_ctx(xhci, in_ctx, xhci_last_valid_endpoint(add_flags)); +} + +void xhci_setup_input_ctx_for_quirk(struct xhci_hcd *xhci, + unsigned int slot_id, unsigned int ep_index, + struct xhci_dequeue_state *deq_state) +{ + struct xhci_container_ctx *in_ctx; + struct xhci_ep_ctx *ep_ctx; + u32 added_ctxs; + dma_addr_t addr; + + xhci_endpoint_copy(xhci, xhci->devs[slot_id]->in_ctx, + xhci->devs[slot_id]->out_ctx, ep_index); + in_ctx = xhci->devs[slot_id]->in_ctx; + ep_ctx = xhci_get_ep_ctx(xhci, in_ctx, ep_index); + addr = xhci_trb_virt_to_dma(deq_state->new_deq_seg, + deq_state->new_deq_ptr); + if (addr == 0) { + xhci_warn(xhci, "WARN Cannot submit config ep after " + "reset ep command\n"); + xhci_warn(xhci, "WARN deq seg = %p, deq ptr = %p\n", + deq_state->new_deq_seg, + deq_state->new_deq_ptr); + return; + } + ep_ctx->deq = addr | deq_state->new_cycle_state; + + added_ctxs = xhci_get_endpoint_flag_from_index(ep_index); + xhci_setup_input_ctx_for_config_ep(xhci, xhci->devs[slot_id]->in_ctx, + xhci->devs[slot_id]->out_ctx, added_ctxs, added_ctxs); +} + +void xhci_cleanup_stalled_ring(struct xhci_hcd *xhci, + struct usb_device *udev, unsigned int ep_index) +{ + struct xhci_dequeue_state deq_state; + struct xhci_virt_ep *ep; + + xhci_dbg(xhci, "Cleaning up stalled endpoint ring\n"); + ep = &xhci->devs[udev->slot_id]->eps[ep_index]; + /* We need to move the HW's dequeue pointer past this TD, + * or it will attempt to resend it on the next doorbell ring. + */ + xhci_find_new_dequeue_state(xhci, udev->slot_id, + ep_index, ep->stopped_td, + &deq_state); + + /* HW with the reset endpoint quirk will use the saved dequeue state to + * issue a configure endpoint command later. + */ + if (!(xhci->quirks & XHCI_RESET_EP_QUIRK)) { + xhci_dbg(xhci, "Queueing new dequeue state\n"); + xhci_queue_new_dequeue_state(xhci, udev->slot_id, + ep_index, &deq_state); + } else { + /* Better hope no one uses the input context between now and the + * reset endpoint completion! + */ + xhci_dbg(xhci, "Setting up input context for " + "configure endpoint command\n"); + xhci_setup_input_ctx_for_quirk(xhci, udev->slot_id, + ep_index, &deq_state); + } +} + /* Deal with stalled endpoints. The core should have sent the control message * to clear the halt condition. However, we need to make the xHCI hardware * reset its sequence number, since a device will expect a sequence number of @@ -1089,8 +1379,7 @@ void xhci_endpoint_reset(struct usb_hcd *hcd, unsigned int ep_index; unsigned long flags; int ret; - struct xhci_dequeue_state deq_state; - struct xhci_ring *ep_ring; + struct xhci_virt_ep *virt_ep; xhci = hcd_to_xhci(hcd); udev = (struct usb_device *) ep->hcpriv; @@ -1100,12 +1389,16 @@ void xhci_endpoint_reset(struct usb_hcd *hcd, if (!ep->hcpriv) return; ep_index = xhci_get_endpoint_index(&ep->desc); - ep_ring = xhci->devs[udev->slot_id]->ep_rings[ep_index]; - if (!ep_ring->stopped_td) { + virt_ep = &xhci->devs[udev->slot_id]->eps[ep_index]; + if (!virt_ep->stopped_td) { xhci_dbg(xhci, "Endpoint 0x%x not halted, refusing to reset.\n", ep->desc.bEndpointAddress); return; } + if (usb_endpoint_xfer_control(&ep->desc)) { + xhci_dbg(xhci, "Control endpoint stall already handled.\n"); + return; + } xhci_dbg(xhci, "Queueing reset endpoint command\n"); spin_lock_irqsave(&xhci->lock, flags); @@ -1116,17 +1409,8 @@ void xhci_endpoint_reset(struct usb_hcd *hcd, * command. Better hope that last command worked! */ if (!ret) { - xhci_dbg(xhci, "Cleaning up stalled endpoint ring\n"); - /* We need to move the HW's dequeue pointer past this TD, - * or it will attempt to resend it on the next doorbell ring. - */ - xhci_find_new_dequeue_state(xhci, udev->slot_id, - ep_index, ep_ring->stopped_td, &deq_state); - xhci_dbg(xhci, "Queueing new dequeue state\n"); - xhci_queue_new_dequeue_state(xhci, ep_ring, - udev->slot_id, - ep_index, &deq_state); - kfree(ep_ring->stopped_td); + xhci_cleanup_stalled_ring(xhci, udev, ep_index); + kfree(virt_ep->stopped_td); xhci_ring_cmd_db(xhci); } spin_unlock_irqrestore(&xhci->lock, flags); @@ -1144,11 +1428,20 @@ void xhci_free_dev(struct usb_hcd *hcd, struct usb_device *udev) { struct xhci_hcd *xhci = hcd_to_xhci(hcd); unsigned long flags; + u32 state; if (udev->slot_id == 0) return; spin_lock_irqsave(&xhci->lock, flags); + /* Don't disable the slot if the host controller is dead. */ + state = xhci_readl(xhci, &xhci->op_regs->status); + if (state == 0xffffffff) { + xhci_free_virt_device(xhci, udev->slot_id); + spin_unlock_irqrestore(&xhci->lock, flags); + return; + } + if (xhci_queue_slot_control(xhci, TRB_DISABLE_SLOT, udev->slot_id)) { spin_unlock_irqrestore(&xhci->lock, flags); xhci_dbg(xhci, "FIXME: allocate a command ring segment\n"); @@ -1328,6 +1621,88 @@ int xhci_address_device(struct usb_hcd *hcd, struct usb_device *udev) return 0; } +/* Once a hub descriptor is fetched for a device, we need to update the xHC's + * internal data structures for the device. + */ +int xhci_update_hub_device(struct usb_hcd *hcd, struct usb_device *hdev, + struct usb_tt *tt, gfp_t mem_flags) +{ + struct xhci_hcd *xhci = hcd_to_xhci(hcd); + struct xhci_virt_device *vdev; + struct xhci_command *config_cmd; + struct xhci_input_control_ctx *ctrl_ctx; + struct xhci_slot_ctx *slot_ctx; + unsigned long flags; + unsigned think_time; + int ret; + + /* Ignore root hubs */ + if (!hdev->parent) + return 0; + + vdev = xhci->devs[hdev->slot_id]; + if (!vdev) { + xhci_warn(xhci, "Cannot update hub desc for unknown device.\n"); + return -EINVAL; + } + config_cmd = xhci_alloc_command(xhci, true, mem_flags); + if (!config_cmd) { + xhci_dbg(xhci, "Could not allocate xHCI command structure.\n"); + return -ENOMEM; + } + + spin_lock_irqsave(&xhci->lock, flags); + xhci_slot_copy(xhci, config_cmd->in_ctx, vdev->out_ctx); + ctrl_ctx = xhci_get_input_control_ctx(xhci, config_cmd->in_ctx); + ctrl_ctx->add_flags |= SLOT_FLAG; + slot_ctx = xhci_get_slot_ctx(xhci, config_cmd->in_ctx); + slot_ctx->dev_info |= DEV_HUB; + if (tt->multi) + slot_ctx->dev_info |= DEV_MTT; + if (xhci->hci_version > 0x95) { + xhci_dbg(xhci, "xHCI version %x needs hub " + "TT think time and number of ports\n", + (unsigned int) xhci->hci_version); + slot_ctx->dev_info2 |= XHCI_MAX_PORTS(hdev->maxchild); + /* Set TT think time - convert from ns to FS bit times. + * 0 = 8 FS bit times, 1 = 16 FS bit times, + * 2 = 24 FS bit times, 3 = 32 FS bit times. + */ + think_time = tt->think_time; + if (think_time != 0) + think_time = (think_time / 666) - 1; + slot_ctx->tt_info |= TT_THINK_TIME(think_time); + } else { + xhci_dbg(xhci, "xHCI version %x doesn't need hub " + "TT think time or number of ports\n", + (unsigned int) xhci->hci_version); + } + slot_ctx->dev_state = 0; + spin_unlock_irqrestore(&xhci->lock, flags); + + xhci_dbg(xhci, "Set up %s for hub device.\n", + (xhci->hci_version > 0x95) ? + "configure endpoint" : "evaluate context"); + xhci_dbg(xhci, "Slot %u Input Context:\n", hdev->slot_id); + xhci_dbg_ctx(xhci, config_cmd->in_ctx, 0); + + /* Issue and wait for the configure endpoint or + * evaluate context command. + */ + if (xhci->hci_version > 0x95) + ret = xhci_configure_endpoint(xhci, hdev, config_cmd, + false, false); + else + ret = xhci_configure_endpoint(xhci, hdev, config_cmd, + true, false); + + xhci_dbg(xhci, "Slot %u Output Context:\n", hdev->slot_id); + xhci_dbg_ctx(xhci, vdev->out_ctx, 0); + + xhci_free_command(xhci, config_cmd); + return ret; +} + int xhci_get_frame(struct usb_hcd *hcd) { struct xhci_hcd *xhci = hcd_to_xhci(hcd); diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c index e6b9a1c6002d..b8fd270a8b0d 100644 --- a/drivers/usb/host/xhci-mem.c +++ b/drivers/usb/host/xhci-mem.c @@ -94,6 +94,9 @@ static void xhci_link_segments(struct xhci_hcd *xhci, struct xhci_segment *prev, val = prev->trbs[TRBS_PER_SEGMENT-1].link.control; val &= ~TRB_TYPE_BITMASK; val |= TRB_TYPE(TRB_LINK); + /* Always set the chain bit with 0.95 hardware */ + if (xhci_link_trb_quirk(xhci)) + val |= TRB_CHAIN; prev->trbs[TRBS_PER_SEGMENT-1].link.control = val; } xhci_dbg(xhci, "Linking segment 0x%llx to segment 0x%llx (DMA)\n", @@ -141,7 +144,6 @@ static struct xhci_ring *xhci_ring_alloc(struct xhci_hcd *xhci, return 0; INIT_LIST_HEAD(&ring->td_list); - INIT_LIST_HEAD(&ring->cancelled_td_list); if (num_segs == 0) return ring; @@ -262,8 +264,8 @@ void xhci_free_virt_device(struct xhci_hcd *xhci, int slot_id) return; for (i = 0; i < 31; ++i) - if (dev->ep_rings[i]) - xhci_ring_free(xhci, dev->ep_rings[i]); + if (dev->eps[i].ring) + xhci_ring_free(xhci, dev->eps[i].ring); if (dev->in_ctx) xhci_free_container_ctx(xhci, dev->in_ctx); @@ -278,6 +280,7 @@ int xhci_alloc_virt_device(struct xhci_hcd *xhci, int slot_id, struct usb_device *udev, gfp_t flags) { struct xhci_virt_device *dev; + int i; /* Slot ID 0 is reserved */ if (slot_id == 0 || xhci->devs[slot_id]) { @@ -306,12 +309,17 @@ int xhci_alloc_virt_device(struct xhci_hcd *xhci, int slot_id, xhci_dbg(xhci, "Slot %d input ctx = 0x%llx (dma)\n", slot_id, (unsigned long long)dev->in_ctx->dma); + /* Initialize the cancellation list for each endpoint */ + for (i = 0; i < 31; i++) + INIT_LIST_HEAD(&dev->eps[i].cancelled_td_list); + /* Allocate endpoint 0 ring */ - dev->ep_rings[0] = xhci_ring_alloc(xhci, 1, true, flags); - if (!dev->ep_rings[0]) + dev->eps[0].ring = xhci_ring_alloc(xhci, 1, true, flags); + if (!dev->eps[0].ring) goto fail; init_completion(&dev->cmd_completion); + INIT_LIST_HEAD(&dev->cmd_list); /* Point to output device context in dcbaa. */ xhci->dcbaa->dev_context_ptrs[slot_id] = dev->out_ctx->dma; @@ -352,9 +360,9 @@ int xhci_setup_addressable_virt_dev(struct xhci_hcd *xhci, struct usb_device *ud /* 3) Only the control endpoint is valid - one endpoint context */ slot_ctx->dev_info |= LAST_CTX(1); + slot_ctx->dev_info |= (u32) udev->route; switch (udev->speed) { case USB_SPEED_SUPER: - slot_ctx->dev_info |= (u32) udev->route; slot_ctx->dev_info |= (u32) SLOT_SPEED_SS; break; case USB_SPEED_HIGH: @@ -382,14 +390,12 @@ int xhci_setup_addressable_virt_dev(struct xhci_hcd *xhci, struct usb_device *ud xhci_dbg(xhci, "Set root hub portnum to %d\n", top_dev->portnum); /* Is this a LS/FS device under a HS hub? */ - /* - * FIXME: I don't think this is right, where does the TT info for the - * roothub or parent hub come from? - */ if ((udev->speed == USB_SPEED_LOW || udev->speed == USB_SPEED_FULL) && udev->tt) { slot_ctx->tt_info = udev->tt->hub->slot_id; slot_ctx->tt_info |= udev->ttport << 8; + if (udev->tt->multi) + slot_ctx->dev_info |= DEV_MTT; } xhci_dbg(xhci, "udev->tt = %p\n", udev->tt); xhci_dbg(xhci, "udev->ttport = 0x%x\n", udev->ttport); @@ -398,22 +404,35 @@ int xhci_setup_addressable_virt_dev(struct xhci_hcd *xhci, struct usb_device *ud /* Step 5 */ ep0_ctx->ep_info2 = EP_TYPE(CTRL_EP); /* - * See section 4.3 bullet 6: - * The default Max Packet size for ep0 is "8 bytes for a USB2 - * LS/FS/HS device or 512 bytes for a USB3 SS device" * XXX: Not sure about wireless USB devices. */ - if (udev->speed == USB_SPEED_SUPER) + switch (udev->speed) { + case USB_SPEED_SUPER: ep0_ctx->ep_info2 |= MAX_PACKET(512); - else + break; + case USB_SPEED_HIGH: + /* USB core guesses at a 64-byte max packet first for FS devices */ + case USB_SPEED_FULL: + ep0_ctx->ep_info2 |= MAX_PACKET(64); + break; + case USB_SPEED_LOW: ep0_ctx->ep_info2 |= MAX_PACKET(8); + break; + case USB_SPEED_VARIABLE: + xhci_dbg(xhci, "FIXME xHCI doesn't support wireless speeds\n"); + return -EINVAL; + break; + default: + /* New speed? */ + BUG(); + } /* EP 0 can handle "burst" sizes of 1, so Max Burst Size field is 0 */ ep0_ctx->ep_info2 |= MAX_BURST(0); ep0_ctx->ep_info2 |= ERROR_COUNT(3); ep0_ctx->deq = - dev->ep_rings[0]->first_seg->dma; - ep0_ctx->deq |= dev->ep_rings[0]->cycle_state; + dev->eps[0].ring->first_seg->dma; + ep0_ctx->deq |= dev->eps[0].ring->cycle_state; /* Steps 7 and 8 were done in xhci_alloc_virt_device() */ @@ -523,10 +542,11 @@ int xhci_endpoint_init(struct xhci_hcd *xhci, ep_ctx = xhci_get_ep_ctx(xhci, virt_dev->in_ctx, ep_index); /* Set up the endpoint ring */ - virt_dev->new_ep_rings[ep_index] = xhci_ring_alloc(xhci, 1, true, mem_flags); - if (!virt_dev->new_ep_rings[ep_index]) + virt_dev->eps[ep_index].new_ring = + xhci_ring_alloc(xhci, 1, true, mem_flags); + if (!virt_dev->eps[ep_index].new_ring) return -ENOMEM; - ep_ring = virt_dev->new_ep_rings[ep_index]; + ep_ring = virt_dev->eps[ep_index].new_ring; ep_ctx->deq = ep_ring->first_seg->dma | ep_ring->cycle_state; ep_ctx->ep_info = xhci_get_endpoint_interval(udev, ep); @@ -598,6 +618,48 @@ void xhci_endpoint_zero(struct xhci_hcd *xhci, */ } +/* Copy output xhci_ep_ctx to the input xhci_ep_ctx copy. + * Useful when you want to change one particular aspect of the endpoint and then + * issue a configure endpoint command. + */ +void xhci_endpoint_copy(struct xhci_hcd *xhci, + struct xhci_container_ctx *in_ctx, + struct xhci_container_ctx *out_ctx, + unsigned int ep_index) +{ + struct xhci_ep_ctx *out_ep_ctx; + struct xhci_ep_ctx *in_ep_ctx; + + out_ep_ctx = xhci_get_ep_ctx(xhci, out_ctx, ep_index); + in_ep_ctx = xhci_get_ep_ctx(xhci, in_ctx, ep_index); + + in_ep_ctx->ep_info = out_ep_ctx->ep_info; + in_ep_ctx->ep_info2 = out_ep_ctx->ep_info2; + in_ep_ctx->deq = out_ep_ctx->deq; + in_ep_ctx->tx_info = out_ep_ctx->tx_info; +} + +/* Copy output xhci_slot_ctx to the input xhci_slot_ctx. + * Useful when you want to change one particular aspect of the endpoint and then + * issue a configure endpoint command. Only the context entries field matters, + * but we'll copy the whole thing anyway. + */ +void xhci_slot_copy(struct xhci_hcd *xhci, + struct xhci_container_ctx *in_ctx, + struct xhci_container_ctx *out_ctx) +{ + struct xhci_slot_ctx *in_slot_ctx; + struct xhci_slot_ctx *out_slot_ctx; + + in_slot_ctx = xhci_get_slot_ctx(xhci, in_ctx); + out_slot_ctx = xhci_get_slot_ctx(xhci, out_ctx); + + in_slot_ctx->dev_info = out_slot_ctx->dev_info; + in_slot_ctx->dev_info2 = out_slot_ctx->dev_info2; + in_slot_ctx->tt_info = out_slot_ctx->tt_info; + in_slot_ctx->dev_state = out_slot_ctx->dev_state; +} + /* Set up the scratchpad buffer array and scratchpad buffers, if needed. */ static int scratchpad_alloc(struct xhci_hcd *xhci, gfp_t flags) { @@ -695,6 +757,44 @@ static void scratchpad_free(struct xhci_hcd *xhci) xhci->scratchpad = NULL; } +struct xhci_command *xhci_alloc_command(struct xhci_hcd *xhci, + bool allocate_completion, gfp_t mem_flags) +{ + struct xhci_command *command; + + command = kzalloc(sizeof(*command), mem_flags); + if (!command) + return NULL; + + command->in_ctx = + xhci_alloc_container_ctx(xhci, XHCI_CTX_TYPE_INPUT, mem_flags); + if (!command->in_ctx) + return NULL; + + if (allocate_completion) { + command->completion = + kzalloc(sizeof(struct completion), mem_flags); + if (!command->completion) { + xhci_free_container_ctx(xhci, command->in_ctx); + return NULL; + } + init_completion(command->completion); + } + + command->status = 0; + INIT_LIST_HEAD(&command->cmd_list); + return command; +} + +void xhci_free_command(struct xhci_hcd *xhci, + struct xhci_command *command) +{ + xhci_free_container_ctx(xhci, + command->in_ctx); + kfree(command->completion); + kfree(command); +} + void xhci_mem_cleanup(struct xhci_hcd *xhci) { struct pci_dev *pdev = to_pci_dev(xhci_to_hcd(xhci)->self.controller); @@ -702,9 +802,11 @@ void xhci_mem_cleanup(struct xhci_hcd *xhci) int i; /* Free the Event Ring Segment Table and the actual Event Ring */ - xhci_writel(xhci, 0, &xhci->ir_set->erst_size); - xhci_write_64(xhci, 0, &xhci->ir_set->erst_base); - xhci_write_64(xhci, 0, &xhci->ir_set->erst_dequeue); + if (xhci->ir_set) { + xhci_writel(xhci, 0, &xhci->ir_set->erst_size); + xhci_write_64(xhci, 0, &xhci->ir_set->erst_base); + xhci_write_64(xhci, 0, &xhci->ir_set->erst_dequeue); + } size = sizeof(struct xhci_erst_entry)*(xhci->erst.num_entries); if (xhci->erst.entries) pci_free_consistent(pdev, size, @@ -741,9 +843,9 @@ void xhci_mem_cleanup(struct xhci_hcd *xhci) xhci->dcbaa, xhci->dcbaa->dma); xhci->dcbaa = NULL; + scratchpad_free(xhci); xhci->page_size = 0; xhci->page_shift = 0; - scratchpad_free(xhci); } int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags) diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c index 592fe7e623f7..06595ec27bb7 100644 --- a/drivers/usb/host/xhci-pci.c +++ b/drivers/usb/host/xhci-pci.c @@ -24,6 +24,10 @@ #include "xhci.h" +/* Device for a quirk */ +#define PCI_VENDOR_ID_FRESCO_LOGIC 0x1b73 +#define PCI_DEVICE_ID_FRESCO_LOGIC_PDK 0x1000 + static const char hcd_name[] = "xhci_hcd"; /* called after powerup, by probe or system-pm "wakeup" */ @@ -59,9 +63,20 @@ static int xhci_pci_setup(struct usb_hcd *hcd) xhci->hcs_params1 = xhci_readl(xhci, &xhci->cap_regs->hcs_params1); xhci->hcs_params2 = xhci_readl(xhci, &xhci->cap_regs->hcs_params2); xhci->hcs_params3 = xhci_readl(xhci, &xhci->cap_regs->hcs_params3); + xhci->hcc_params = xhci_readl(xhci, &xhci->cap_regs->hc_capbase); + xhci->hci_version = HC_VERSION(xhci->hcc_params); xhci->hcc_params = xhci_readl(xhci, &xhci->cap_regs->hcc_params); xhci_print_registers(xhci); + /* Look for vendor-specific quirks */ + if (pdev->vendor == PCI_VENDOR_ID_FRESCO_LOGIC && + pdev->device == PCI_DEVICE_ID_FRESCO_LOGIC_PDK && + pdev->revision == 0x0) { + xhci->quirks |= XHCI_RESET_EP_QUIRK; + xhci_dbg(xhci, "QUIRK: Fresco Logic xHC needs configure" + " endpoint cmd after reset endpoint\n"); + } + /* Make sure the HC is halted. */ retval = xhci_halt(xhci); if (retval) @@ -121,6 +136,7 @@ static const struct hc_driver xhci_pci_hc_driver = { .check_bandwidth = xhci_check_bandwidth, .reset_bandwidth = xhci_reset_bandwidth, .address_device = xhci_address_device, + .update_hub_device = xhci_update_hub_device, /* * scheduling support diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index aa88a067148b..821b7b4709de 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -172,8 +172,9 @@ static void inc_deq(struct xhci_hcd *xhci, struct xhci_ring *ring, bool consumer * have their chain bit cleared (so that each Link TRB is a separate TD). * * Section 6.4.4.1 of the 0.95 spec says link TRBs cannot have the chain bit - * set, but other sections talk about dealing with the chain bit set. - * Assume section 6.4.4.1 is wrong, and the chain bit can be set in a Link TRB. + * set, but other sections talk about dealing with the chain bit set. This was + * fixed in the 0.96 specification errata, but we have to assume that all 0.95 + * xHCI hardware can't handle the chain bit being cleared on a link TRB. */ static void inc_enq(struct xhci_hcd *xhci, struct xhci_ring *ring, bool consumer) { @@ -191,8 +192,14 @@ static void inc_enq(struct xhci_hcd *xhci, struct xhci_ring *ring, bool consumer while (last_trb(xhci, ring, ring->enq_seg, next)) { if (!consumer) { if (ring != xhci->event_ring) { - next->link.control &= ~TRB_CHAIN; - next->link.control |= chain; + /* If we're not dealing with 0.95 hardware, + * carry over the chain bit of the previous TRB + * (which may mean the chain bit is cleared). + */ + if (!xhci_link_trb_quirk(xhci)) { + next->link.control &= ~TRB_CHAIN; + next->link.control |= chain; + } /* Give this link TRB to the hardware */ wmb(); if (next->link.control & TRB_CYCLE) @@ -289,16 +296,18 @@ static void ring_ep_doorbell(struct xhci_hcd *xhci, unsigned int slot_id, unsigned int ep_index) { - struct xhci_ring *ep_ring; + struct xhci_virt_ep *ep; + unsigned int ep_state; u32 field; __u32 __iomem *db_addr = &xhci->dba->doorbell[slot_id]; - ep_ring = xhci->devs[slot_id]->ep_rings[ep_index]; + ep = &xhci->devs[slot_id]->eps[ep_index]; + ep_state = ep->ep_state; /* Don't ring the doorbell for this endpoint if there are pending * cancellations because the we don't want to interrupt processing. */ - if (!ep_ring->cancels_pending && !(ep_ring->state & SET_DEQ_PENDING) - && !(ep_ring->state & EP_HALTED)) { + if (!ep->cancels_pending && !(ep_state & SET_DEQ_PENDING) + && !(ep_state & EP_HALTED)) { field = xhci_readl(xhci, db_addr) & DB_MASK; xhci_writel(xhci, field | EPI_TO_DB(ep_index), db_addr); /* Flush PCI posted writes - FIXME Matthew Wilcox says this @@ -354,7 +363,7 @@ void xhci_find_new_dequeue_state(struct xhci_hcd *xhci, struct xhci_td *cur_td, struct xhci_dequeue_state *state) { struct xhci_virt_device *dev = xhci->devs[slot_id]; - struct xhci_ring *ep_ring = dev->ep_rings[ep_index]; + struct xhci_ring *ep_ring = dev->eps[ep_index].ring; struct xhci_generic_trb *trb; struct xhci_ep_ctx *ep_ctx; dma_addr_t addr; @@ -362,7 +371,7 @@ void xhci_find_new_dequeue_state(struct xhci_hcd *xhci, state->new_cycle_state = 0; xhci_dbg(xhci, "Finding segment containing stopped TRB.\n"); state->new_deq_seg = find_trb_seg(cur_td->start_seg, - ep_ring->stopped_trb, + dev->eps[ep_index].stopped_trb, &state->new_cycle_state); if (!state->new_deq_seg) BUG(); @@ -442,9 +451,11 @@ static int queue_set_tr_deq(struct xhci_hcd *xhci, int slot_id, union xhci_trb *deq_ptr, u32 cycle_state); void xhci_queue_new_dequeue_state(struct xhci_hcd *xhci, - struct xhci_ring *ep_ring, unsigned int slot_id, - unsigned int ep_index, struct xhci_dequeue_state *deq_state) + unsigned int slot_id, unsigned int ep_index, + struct xhci_dequeue_state *deq_state) { + struct xhci_virt_ep *ep = &xhci->devs[slot_id]->eps[ep_index]; + xhci_dbg(xhci, "Set TR Deq Ptr cmd, new deq seg = %p (0x%llx dma), " "new deq ptr = %p (0x%llx dma), new cycle = %u\n", deq_state->new_deq_seg, @@ -461,8 +472,7 @@ void xhci_queue_new_dequeue_state(struct xhci_hcd *xhci, * if the ring is running, and ringing the doorbell starts the * ring running. */ - ep_ring->state |= SET_DEQ_PENDING; - xhci_ring_cmd_db(xhci); + ep->ep_state |= SET_DEQ_PENDING; } /* @@ -481,6 +491,7 @@ static void handle_stopped_endpoint(struct xhci_hcd *xhci, unsigned int slot_id; unsigned int ep_index; struct xhci_ring *ep_ring; + struct xhci_virt_ep *ep; struct list_head *entry; struct xhci_td *cur_td = 0; struct xhci_td *last_unlinked_td; @@ -493,9 +504,10 @@ static void handle_stopped_endpoint(struct xhci_hcd *xhci, memset(&deq_state, 0, sizeof(deq_state)); slot_id = TRB_TO_SLOT_ID(trb->generic.field[3]); ep_index = TRB_TO_EP_INDEX(trb->generic.field[3]); - ep_ring = xhci->devs[slot_id]->ep_rings[ep_index]; + ep = &xhci->devs[slot_id]->eps[ep_index]; + ep_ring = ep->ring; - if (list_empty(&ep_ring->cancelled_td_list)) + if (list_empty(&ep->cancelled_td_list)) return; /* Fix up the ep ring first, so HW stops executing cancelled TDs. @@ -503,7 +515,7 @@ static void handle_stopped_endpoint(struct xhci_hcd *xhci, * it. We're also in the event handler, so we can't get re-interrupted * if another Stop Endpoint command completes */ - list_for_each(entry, &ep_ring->cancelled_td_list) { + list_for_each(entry, &ep->cancelled_td_list) { cur_td = list_entry(entry, struct xhci_td, cancelled_td_list); xhci_dbg(xhci, "Cancelling TD starting at %p, 0x%llx (dma).\n", cur_td->first_trb, @@ -512,7 +524,7 @@ static void handle_stopped_endpoint(struct xhci_hcd *xhci, * If we stopped on the TD we need to cancel, then we have to * move the xHC endpoint ring dequeue pointer past this TD. */ - if (cur_td == ep_ring->stopped_td) + if (cur_td == ep->stopped_td) xhci_find_new_dequeue_state(xhci, slot_id, ep_index, cur_td, &deq_state); else @@ -523,14 +535,15 @@ static void handle_stopped_endpoint(struct xhci_hcd *xhci, * the cancelled TD list for URB completion later. */ list_del(&cur_td->td_list); - ep_ring->cancels_pending--; + ep->cancels_pending--; } last_unlinked_td = cur_td; /* If necessary, queue a Set Transfer Ring Dequeue Pointer command */ if (deq_state.new_deq_ptr && deq_state.new_deq_seg) { - xhci_queue_new_dequeue_state(xhci, ep_ring, + xhci_queue_new_dequeue_state(xhci, slot_id, ep_index, &deq_state); + xhci_ring_cmd_db(xhci); } else { /* Otherwise just ring the doorbell to restart the ring */ ring_ep_doorbell(xhci, slot_id, ep_index); @@ -543,7 +556,7 @@ static void handle_stopped_endpoint(struct xhci_hcd *xhci, * So stop when we've completed the URB for the last TD we unlinked. */ do { - cur_td = list_entry(ep_ring->cancelled_td_list.next, + cur_td = list_entry(ep->cancelled_td_list.next, struct xhci_td, cancelled_td_list); list_del(&cur_td->cancelled_td_list); @@ -590,7 +603,7 @@ static void handle_set_deq_completion(struct xhci_hcd *xhci, slot_id = TRB_TO_SLOT_ID(trb->generic.field[3]); ep_index = TRB_TO_EP_INDEX(trb->generic.field[3]); dev = xhci->devs[slot_id]; - ep_ring = dev->ep_rings[ep_index]; + ep_ring = dev->eps[ep_index].ring; ep_ctx = xhci_get_ep_ctx(xhci, dev->out_ctx, ep_index); slot_ctx = xhci_get_slot_ctx(xhci, dev->out_ctx); @@ -634,7 +647,7 @@ static void handle_set_deq_completion(struct xhci_hcd *xhci, ep_ctx->deq); } - ep_ring->state &= ~SET_DEQ_PENDING; + dev->eps[ep_index].ep_state &= ~SET_DEQ_PENDING; ring_ep_doorbell(xhci, slot_id, ep_index); } @@ -644,18 +657,60 @@ static void handle_reset_ep_completion(struct xhci_hcd *xhci, { int slot_id; unsigned int ep_index; + struct xhci_ring *ep_ring; slot_id = TRB_TO_SLOT_ID(trb->generic.field[3]); ep_index = TRB_TO_EP_INDEX(trb->generic.field[3]); + ep_ring = xhci->devs[slot_id]->eps[ep_index].ring; /* This command will only fail if the endpoint wasn't halted, * but we don't care. */ xhci_dbg(xhci, "Ignoring reset ep completion code of %u\n", (unsigned int) GET_COMP_CODE(event->status)); - /* Clear our internal halted state and restart the ring */ - xhci->devs[slot_id]->ep_rings[ep_index]->state &= ~EP_HALTED; - ring_ep_doorbell(xhci, slot_id, ep_index); + /* HW with the reset endpoint quirk needs to have a configure endpoint + * command complete before the endpoint can be used. Queue that here + * because the HW can't handle two commands being queued in a row. + */ + if (xhci->quirks & XHCI_RESET_EP_QUIRK) { + xhci_dbg(xhci, "Queueing configure endpoint command\n"); + xhci_queue_configure_endpoint(xhci, + xhci->devs[slot_id]->in_ctx->dma, slot_id, + false); + xhci_ring_cmd_db(xhci); + } else { + /* Clear our internal halted state and restart the ring */ + xhci->devs[slot_id]->eps[ep_index].ep_state &= ~EP_HALTED; + ring_ep_doorbell(xhci, slot_id, ep_index); + } +} + +/* Check to see if a command in the device's command queue matches this one. + * Signal the completion or free the command, and return 1. Return 0 if the + * completed command isn't at the head of the command list. + */ +static int handle_cmd_in_cmd_wait_list(struct xhci_hcd *xhci, + struct xhci_virt_device *virt_dev, + struct xhci_event_cmd *event) +{ + struct xhci_command *command; + + if (list_empty(&virt_dev->cmd_list)) + return 0; + + command = list_entry(virt_dev->cmd_list.next, + struct xhci_command, cmd_list); + if (xhci->cmd_ring->dequeue != command->command_trb) + return 0; + + command->status = + GET_COMP_CODE(event->status); + list_del(&command->cmd_list); + if (command->completion) + complete(command->completion); + else + xhci_free_command(xhci, command); + return 1; } static void handle_cmd_completion(struct xhci_hcd *xhci, @@ -664,6 +719,11 @@ static void handle_cmd_completion(struct xhci_hcd *xhci, int slot_id = TRB_TO_SLOT_ID(event->flags); u64 cmd_dma; dma_addr_t cmd_dequeue_dma; + struct xhci_input_control_ctx *ctrl_ctx; + struct xhci_virt_device *virt_dev; + unsigned int ep_index; + struct xhci_ring *ep_ring; + unsigned int ep_state; cmd_dma = event->cmd_trb; cmd_dequeue_dma = xhci_trb_virt_to_dma(xhci->cmd_ring->deq_seg, @@ -691,6 +751,47 @@ static void handle_cmd_completion(struct xhci_hcd *xhci, xhci_free_virt_device(xhci, slot_id); break; case TRB_TYPE(TRB_CONFIG_EP): + virt_dev = xhci->devs[slot_id]; + if (handle_cmd_in_cmd_wait_list(xhci, virt_dev, event)) + break; + /* + * Configure endpoint commands can come from the USB core + * configuration or alt setting changes, or because the HW + * needed an extra configure endpoint command after a reset + * endpoint command. In the latter case, the xHCI driver is + * not waiting on the configure endpoint command. + */ + ctrl_ctx = xhci_get_input_control_ctx(xhci, + virt_dev->in_ctx); + /* Input ctx add_flags are the endpoint index plus one */ + ep_index = xhci_last_valid_endpoint(ctrl_ctx->add_flags) - 1; + ep_ring = xhci->devs[slot_id]->eps[ep_index].ring; + if (!ep_ring) { + /* This must have been an initial configure endpoint */ + xhci->devs[slot_id]->cmd_status = + GET_COMP_CODE(event->status); + complete(&xhci->devs[slot_id]->cmd_completion); + break; + } + ep_state = xhci->devs[slot_id]->eps[ep_index].ep_state; + xhci_dbg(xhci, "Completed config ep cmd - last ep index = %d, " + "state = %d\n", ep_index, ep_state); + if (xhci->quirks & XHCI_RESET_EP_QUIRK && + ep_state & EP_HALTED) { + /* Clear our internal halted state and restart ring */ + xhci->devs[slot_id]->eps[ep_index].ep_state &= + ~EP_HALTED; + ring_ep_doorbell(xhci, slot_id, ep_index); + } else { + xhci->devs[slot_id]->cmd_status = + GET_COMP_CODE(event->status); + complete(&xhci->devs[slot_id]->cmd_completion); + } + break; + case TRB_TYPE(TRB_EVAL_CONTEXT): + virt_dev = xhci->devs[slot_id]; + if (handle_cmd_in_cmd_wait_list(xhci, virt_dev, event)) + break; xhci->devs[slot_id]->cmd_status = GET_COMP_CODE(event->status); complete(&xhci->devs[slot_id]->cmd_completion); break; @@ -763,9 +864,11 @@ static struct xhci_segment *trb_in_td( cur_seg = start_seg; do { + if (start_dma == 0) + return 0; /* We may get an event for a Link TRB in the middle of a TD */ end_seg_dma = xhci_trb_virt_to_dma(cur_seg, - &start_seg->trbs[TRBS_PER_SEGMENT - 1]); + &cur_seg->trbs[TRBS_PER_SEGMENT - 1]); /* If the end TRB isn't in this segment, this is set to 0 */ end_trb_dma = xhci_trb_virt_to_dma(cur_seg, end_trb); @@ -792,8 +895,9 @@ static struct xhci_segment *trb_in_td( } cur_seg = cur_seg->next; start_dma = xhci_trb_virt_to_dma(cur_seg, &cur_seg->trbs[0]); - } while (1); + } while (cur_seg != start_seg); + return 0; } /* @@ -805,7 +909,9 @@ static int handle_tx_event(struct xhci_hcd *xhci, struct xhci_transfer_event *event) { struct xhci_virt_device *xdev; + struct xhci_virt_ep *ep; struct xhci_ring *ep_ring; + unsigned int slot_id; int ep_index; struct xhci_td *td = 0; dma_addr_t event_dma; @@ -814,9 +920,11 @@ static int handle_tx_event(struct xhci_hcd *xhci, struct urb *urb = 0; int status = -EINPROGRESS; struct xhci_ep_ctx *ep_ctx; + u32 trb_comp_code; xhci_dbg(xhci, "In %s\n", __func__); - xdev = xhci->devs[TRB_TO_SLOT_ID(event->flags)]; + slot_id = TRB_TO_SLOT_ID(event->flags); + xdev = xhci->devs[slot_id]; if (!xdev) { xhci_err(xhci, "ERROR Transfer event pointed to bad slot\n"); return -ENODEV; @@ -825,7 +933,8 @@ static int handle_tx_event(struct xhci_hcd *xhci, /* Endpoint ID is 1 based, our index is zero based */ ep_index = TRB_TO_EP_ID(event->flags) - 1; xhci_dbg(xhci, "%s - ep index = %d\n", __func__, ep_index); - ep_ring = xdev->ep_rings[ep_index]; + ep = &xdev->eps[ep_index]; + ep_ring = ep->ring; ep_ctx = xhci_get_ep_ctx(xhci, xdev->out_ctx, ep_index); if (!ep_ring || (ep_ctx->ep_info & EP_STATE_MASK) == EP_STATE_DISABLED) { xhci_err(xhci, "ERROR Transfer event pointed to disabled endpoint\n"); @@ -870,7 +979,8 @@ static int handle_tx_event(struct xhci_hcd *xhci, (unsigned int) event->flags); /* Look for common error cases */ - switch (GET_COMP_CODE(event->transfer_len)) { + trb_comp_code = GET_COMP_CODE(event->transfer_len); + switch (trb_comp_code) { /* Skip codes that require special handling depending on * transfer type */ @@ -885,7 +995,7 @@ static int handle_tx_event(struct xhci_hcd *xhci, break; case COMP_STALL: xhci_warn(xhci, "WARN: Stalled endpoint\n"); - ep_ring->state |= EP_HALTED; + ep->ep_state |= EP_HALTED; status = -EPIPE; break; case COMP_TRB_ERR: @@ -913,7 +1023,7 @@ static int handle_tx_event(struct xhci_hcd *xhci, /* Was this a control transfer? */ if (usb_endpoint_xfer_control(&td->urb->ep->desc)) { xhci_debug_trb(xhci, xhci->event_ring->dequeue); - switch (GET_COMP_CODE(event->transfer_len)) { + switch (trb_comp_code) { case COMP_SUCCESS: if (event_trb == ep_ring->dequeue) { xhci_warn(xhci, "WARN: Success on ctrl setup TRB without IOC set??\n"); @@ -928,8 +1038,37 @@ static int handle_tx_event(struct xhci_hcd *xhci, break; case COMP_SHORT_TX: xhci_warn(xhci, "WARN: short transfer on control ep\n"); - status = -EREMOTEIO; + if (td->urb->transfer_flags & URB_SHORT_NOT_OK) + status = -EREMOTEIO; + else + status = 0; break; + case COMP_BABBLE: + /* The 0.96 spec says a babbling control endpoint + * is not halted. The 0.96 spec says it is. Some HW + * claims to be 0.95 compliant, but it halts the control + * endpoint anyway. Check if a babble halted the + * endpoint. + */ + if (ep_ctx->ep_info != EP_STATE_HALTED) + break; + /* else fall through */ + case COMP_STALL: + /* Did we transfer part of the data (middle) phase? */ + if (event_trb != ep_ring->dequeue && + event_trb != td->last_trb) + td->urb->actual_length = + td->urb->transfer_buffer_length + - TRB_LEN(event->transfer_len); + else + td->urb->actual_length = 0; + + ep->stopped_td = td; + ep->stopped_trb = event_trb; + xhci_queue_reset_ep(xhci, slot_id, ep_index); + xhci_cleanup_stalled_ring(xhci, td->urb->dev, ep_index); + xhci_ring_cmd_db(xhci); + goto td_cleanup; default: /* Others already handled above */ break; @@ -943,7 +1082,10 @@ static int handle_tx_event(struct xhci_hcd *xhci, if (event_trb == td->last_trb) { if (td->urb->actual_length != 0) { /* Don't overwrite a previously set error code */ - if (status == -EINPROGRESS || status == 0) + if ((status == -EINPROGRESS || + status == 0) && + (td->urb->transfer_flags + & URB_SHORT_NOT_OK)) /* Did we already see a short data stage? */ status = -EREMOTEIO; } else { @@ -952,7 +1094,7 @@ static int handle_tx_event(struct xhci_hcd *xhci, } } else { /* Maybe the event was for the data stage? */ - if (GET_COMP_CODE(event->transfer_len) != COMP_STOP_INVAL) { + if (trb_comp_code != COMP_STOP_INVAL) { /* We didn't stop on a link TRB in the middle */ td->urb->actual_length = td->urb->transfer_buffer_length - @@ -964,7 +1106,7 @@ static int handle_tx_event(struct xhci_hcd *xhci, } } } else { - switch (GET_COMP_CODE(event->transfer_len)) { + switch (trb_comp_code) { case COMP_SUCCESS: /* Double check that the HW transferred everything. */ if (event_trb != td->last_trb) { @@ -975,7 +1117,12 @@ static int handle_tx_event(struct xhci_hcd *xhci, else status = 0; } else { - xhci_dbg(xhci, "Successful bulk transfer!\n"); + if (usb_endpoint_xfer_bulk(&td->urb->ep->desc)) + xhci_dbg(xhci, "Successful bulk " + "transfer!\n"); + else + xhci_dbg(xhci, "Successful interrupt " + "transfer!\n"); status = 0; } break; @@ -1001,11 +1148,17 @@ static int handle_tx_event(struct xhci_hcd *xhci, td->urb->actual_length = td->urb->transfer_buffer_length - TRB_LEN(event->transfer_len); - if (td->urb->actual_length < 0) { + if (td->urb->transfer_buffer_length < + td->urb->actual_length) { xhci_warn(xhci, "HC gave bad length " "of %d bytes left\n", TRB_LEN(event->transfer_len)); td->urb->actual_length = 0; + if (td->urb->transfer_flags & + URB_SHORT_NOT_OK) + status = -EREMOTEIO; + else + status = 0; } /* Don't overwrite a previously set error code */ if (status == -EINPROGRESS) { @@ -1041,30 +1194,31 @@ static int handle_tx_event(struct xhci_hcd *xhci, /* If the ring didn't stop on a Link or No-op TRB, add * in the actual bytes transferred from the Normal TRB */ - if (GET_COMP_CODE(event->transfer_len) != COMP_STOP_INVAL) + if (trb_comp_code != COMP_STOP_INVAL) td->urb->actual_length += TRB_LEN(cur_trb->generic.field[2]) - TRB_LEN(event->transfer_len); } } - if (GET_COMP_CODE(event->transfer_len) == COMP_STOP_INVAL || - GET_COMP_CODE(event->transfer_len) == COMP_STOP) { + if (trb_comp_code == COMP_STOP_INVAL || + trb_comp_code == COMP_STOP) { /* The Endpoint Stop Command completion will take care of any * stopped TDs. A stopped TD may be restarted, so don't update * the ring dequeue pointer or take this TD off any lists yet. */ - ep_ring->stopped_td = td; - ep_ring->stopped_trb = event_trb; + ep->stopped_td = td; + ep->stopped_trb = event_trb; } else { - if (GET_COMP_CODE(event->transfer_len) == COMP_STALL) { + if (trb_comp_code == COMP_STALL || + trb_comp_code == COMP_BABBLE) { /* The transfer is completed from the driver's * perspective, but we need to issue a set dequeue * command for this stalled endpoint to move the dequeue * pointer past the TD. We can't do that here because * the halt condition must be cleared first. */ - ep_ring->stopped_td = td; - ep_ring->stopped_trb = event_trb; + ep->stopped_td = td; + ep->stopped_trb = event_trb; } else { /* Update ring dequeue pointer */ while (ep_ring->dequeue != td->last_trb) @@ -1072,16 +1226,41 @@ static int handle_tx_event(struct xhci_hcd *xhci, inc_deq(xhci, ep_ring, false); } +td_cleanup: /* Clean up the endpoint's TD list */ urb = td->urb; + /* Do one last check of the actual transfer length. + * If the host controller said we transferred more data than + * the buffer length, urb->actual_length will be a very big + * number (since it's unsigned). Play it safe and say we didn't + * transfer anything. + */ + if (urb->actual_length > urb->transfer_buffer_length) { + xhci_warn(xhci, "URB transfer length is wrong, " + "xHC issue? req. len = %u, " + "act. len = %u\n", + urb->transfer_buffer_length, + urb->actual_length); + urb->actual_length = 0; + if (td->urb->transfer_flags & URB_SHORT_NOT_OK) + status = -EREMOTEIO; + else + status = 0; + } list_del(&td->td_list); /* Was this TD slated to be cancelled but completed anyway? */ if (!list_empty(&td->cancelled_td_list)) { list_del(&td->cancelled_td_list); - ep_ring->cancels_pending--; + ep->cancels_pending--; } - /* Leave the TD around for the reset endpoint function to use */ - if (GET_COMP_CODE(event->transfer_len) != COMP_STALL) { + /* Leave the TD around for the reset endpoint function to use + * (but only if it's not a control endpoint, since we already + * queued the Set TR dequeue pointer command for stalled + * control endpoints). + */ + if (usb_endpoint_xfer_control(&urb->ep->desc) || + (trb_comp_code != COMP_STALL && + trb_comp_code != COMP_BABBLE)) { kfree(td); } urb->hcpriv = NULL; @@ -1094,7 +1273,7 @@ cleanup: if (urb) { usb_hcd_unlink_urb_from_ep(xhci_to_hcd(xhci), urb); xhci_dbg(xhci, "Giveback URB %p, len = %d, status = %d\n", - urb, td->urb->actual_length, status); + urb, urb->actual_length, status); spin_unlock(&xhci->lock); usb_hcd_giveback_urb(xhci_to_hcd(xhci), urb, status); spin_lock(&xhci->lock); @@ -1235,7 +1414,7 @@ static int prepare_transfer(struct xhci_hcd *xhci, { int ret; struct xhci_ep_ctx *ep_ctx = xhci_get_ep_ctx(xhci, xdev->out_ctx, ep_index); - ret = prepare_ring(xhci, xdev->ep_rings[ep_index], + ret = prepare_ring(xhci, xdev->eps[ep_index].ring, ep_ctx->ep_info & EP_STATE_MASK, num_trbs, mem_flags); if (ret) @@ -1255,9 +1434,9 @@ static int prepare_transfer(struct xhci_hcd *xhci, (*td)->urb = urb; urb->hcpriv = (void *) (*td); /* Add this TD to the tail of the endpoint ring's TD list */ - list_add_tail(&(*td)->td_list, &xdev->ep_rings[ep_index]->td_list); - (*td)->start_seg = xdev->ep_rings[ep_index]->enq_seg; - (*td)->first_trb = xdev->ep_rings[ep_index]->enqueue; + list_add_tail(&(*td)->td_list, &xdev->eps[ep_index].ring->td_list); + (*td)->start_seg = xdev->eps[ep_index].ring->enq_seg; + (*td)->first_trb = xdev->eps[ep_index].ring->enqueue; return 0; } @@ -1335,6 +1514,47 @@ static void giveback_first_trb(struct xhci_hcd *xhci, int slot_id, ring_ep_doorbell(xhci, slot_id, ep_index); } +/* + * xHCI uses normal TRBs for both bulk and interrupt. When the interrupt + * endpoint is to be serviced, the xHC will consume (at most) one TD. A TD + * (comprised of sg list entries) can take several service intervals to + * transmit. + */ +int xhci_queue_intr_tx(struct xhci_hcd *xhci, gfp_t mem_flags, + struct urb *urb, int slot_id, unsigned int ep_index) +{ + struct xhci_ep_ctx *ep_ctx = xhci_get_ep_ctx(xhci, + xhci->devs[slot_id]->out_ctx, ep_index); + int xhci_interval; + int ep_interval; + + xhci_interval = EP_INTERVAL_TO_UFRAMES(ep_ctx->ep_info); + ep_interval = urb->interval; + /* Convert to microframes */ + if (urb->dev->speed == USB_SPEED_LOW || + urb->dev->speed == USB_SPEED_FULL) + ep_interval *= 8; + /* FIXME change this to a warning and a suggestion to use the new API + * to set the polling interval (once the API is added). + */ + if (xhci_interval != ep_interval) { + if (!printk_ratelimit()) + dev_dbg(&urb->dev->dev, "Driver uses different interval" + " (%d microframe%s) than xHCI " + "(%d microframe%s)\n", + ep_interval, + ep_interval == 1 ? "" : "s", + xhci_interval, + xhci_interval == 1 ? "" : "s"); + urb->interval = xhci_interval; + /* Convert back to frames for LS/FS devices */ + if (urb->dev->speed == USB_SPEED_LOW || + urb->dev->speed == USB_SPEED_FULL) + urb->interval /= 8; + } + return xhci_queue_bulk_tx(xhci, GFP_ATOMIC, urb, slot_id, ep_index); +} + static int queue_bulk_sg_tx(struct xhci_hcd *xhci, gfp_t mem_flags, struct urb *urb, int slot_id, unsigned int ep_index) { @@ -1350,7 +1570,7 @@ static int queue_bulk_sg_tx(struct xhci_hcd *xhci, gfp_t mem_flags, struct xhci_generic_trb *start_trb; int start_cycle; - ep_ring = xhci->devs[slot_id]->ep_rings[ep_index]; + ep_ring = xhci->devs[slot_id]->eps[ep_index].ring; num_trbs = count_sg_trbs_needed(xhci, urb); num_sgs = urb->num_sgs; @@ -1483,7 +1703,7 @@ int xhci_queue_bulk_tx(struct xhci_hcd *xhci, gfp_t mem_flags, if (urb->sg) return queue_bulk_sg_tx(xhci, mem_flags, urb, slot_id, ep_index); - ep_ring = xhci->devs[slot_id]->ep_rings[ep_index]; + ep_ring = xhci->devs[slot_id]->eps[ep_index].ring; num_trbs = 0; /* How much data is (potentially) left before the 64KB boundary? */ @@ -1594,7 +1814,7 @@ int xhci_queue_ctrl_tx(struct xhci_hcd *xhci, gfp_t mem_flags, u32 field, length_field; struct xhci_td *td; - ep_ring = xhci->devs[slot_id]->ep_rings[ep_index]; + ep_ring = xhci->devs[slot_id]->eps[ep_index].ring; /* * Need to copy setup packet into setup TRB, so we can't use the setup @@ -1677,12 +1897,27 @@ int xhci_queue_ctrl_tx(struct xhci_hcd *xhci, gfp_t mem_flags, /**** Command Ring Operations ****/ -/* Generic function for queueing a command TRB on the command ring */ -static int queue_command(struct xhci_hcd *xhci, u32 field1, u32 field2, u32 field3, u32 field4) +/* Generic function for queueing a command TRB on the command ring. + * Check to make sure there's room on the command ring for one command TRB. + * Also check that there's room reserved for commands that must not fail. + * If this is a command that must not fail, meaning command_must_succeed = TRUE, + * then only check for the number of reserved spots. + * Don't decrement xhci->cmd_ring_reserved_trbs after we've queued the TRB + * because the command event handler may want to resubmit a failed command. + */ +static int queue_command(struct xhci_hcd *xhci, u32 field1, u32 field2, + u32 field3, u32 field4, bool command_must_succeed) { - if (!room_on_ring(xhci, xhci->cmd_ring, 1)) { + int reserved_trbs = xhci->cmd_ring_reserved_trbs; + if (!command_must_succeed) + reserved_trbs++; + + if (!room_on_ring(xhci, xhci->cmd_ring, reserved_trbs)) { if (!in_interrupt()) xhci_err(xhci, "ERR: No room for command on command ring\n"); + if (command_must_succeed) + xhci_err(xhci, "ERR: Reserved TRB counting for " + "unfailable commands failed.\n"); return -ENOMEM; } queue_trb(xhci, xhci->cmd_ring, false, field1, field2, field3, @@ -1693,7 +1928,7 @@ static int queue_command(struct xhci_hcd *xhci, u32 field1, u32 field2, u32 fiel /* Queue a no-op command on the command ring */ static int queue_cmd_noop(struct xhci_hcd *xhci) { - return queue_command(xhci, 0, 0, 0, TRB_TYPE(TRB_CMD_NOOP)); + return queue_command(xhci, 0, 0, 0, TRB_TYPE(TRB_CMD_NOOP), false); } /* @@ -1712,7 +1947,7 @@ void *xhci_setup_one_noop(struct xhci_hcd *xhci) int xhci_queue_slot_control(struct xhci_hcd *xhci, u32 trb_type, u32 slot_id) { return queue_command(xhci, 0, 0, 0, - TRB_TYPE(trb_type) | SLOT_ID_FOR_TRB(slot_id)); + TRB_TYPE(trb_type) | SLOT_ID_FOR_TRB(slot_id), false); } /* Queue an address device command TRB */ @@ -1721,16 +1956,28 @@ int xhci_queue_address_device(struct xhci_hcd *xhci, dma_addr_t in_ctx_ptr, { return queue_command(xhci, lower_32_bits(in_ctx_ptr), upper_32_bits(in_ctx_ptr), 0, - TRB_TYPE(TRB_ADDR_DEV) | SLOT_ID_FOR_TRB(slot_id)); + TRB_TYPE(TRB_ADDR_DEV) | SLOT_ID_FOR_TRB(slot_id), + false); } /* Queue a configure endpoint command TRB */ int xhci_queue_configure_endpoint(struct xhci_hcd *xhci, dma_addr_t in_ctx_ptr, + u32 slot_id, bool command_must_succeed) +{ + return queue_command(xhci, lower_32_bits(in_ctx_ptr), + upper_32_bits(in_ctx_ptr), 0, + TRB_TYPE(TRB_CONFIG_EP) | SLOT_ID_FOR_TRB(slot_id), + command_must_succeed); +} + +/* Queue an evaluate context command TRB */ +int xhci_queue_evaluate_context(struct xhci_hcd *xhci, dma_addr_t in_ctx_ptr, u32 slot_id) { return queue_command(xhci, lower_32_bits(in_ctx_ptr), upper_32_bits(in_ctx_ptr), 0, - TRB_TYPE(TRB_CONFIG_EP) | SLOT_ID_FOR_TRB(slot_id)); + TRB_TYPE(TRB_EVAL_CONTEXT) | SLOT_ID_FOR_TRB(slot_id), + false); } int xhci_queue_stop_endpoint(struct xhci_hcd *xhci, int slot_id, @@ -1741,7 +1988,7 @@ int xhci_queue_stop_endpoint(struct xhci_hcd *xhci, int slot_id, u32 type = TRB_TYPE(TRB_STOP_RING); return queue_command(xhci, 0, 0, 0, - trb_slot_id | trb_ep_index | type); + trb_slot_id | trb_ep_index | type, false); } /* Set Transfer Ring Dequeue Pointer command. @@ -1765,7 +2012,7 @@ static int queue_set_tr_deq(struct xhci_hcd *xhci, int slot_id, } return queue_command(xhci, lower_32_bits(addr) | cycle_state, upper_32_bits(addr), 0, - trb_slot_id | trb_ep_index | type); + trb_slot_id | trb_ep_index | type, false); } int xhci_queue_reset_ep(struct xhci_hcd *xhci, int slot_id, @@ -1775,5 +2022,6 @@ int xhci_queue_reset_ep(struct xhci_hcd *xhci, int slot_id, u32 trb_ep_index = EP_ID_FOR_TRB(ep_index); u32 type = TRB_TYPE(TRB_RESET_EP); - return queue_command(xhci, 0, 0, 0, trb_slot_id | trb_ep_index | type); + return queue_command(xhci, 0, 0, 0, trb_slot_id | trb_ep_index | type, + false); } diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index ffe1625d4e1b..4b254b6fa245 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -509,6 +509,8 @@ struct xhci_slot_ctx { #define MAX_EXIT (0xffff) /* Root hub port number that is needed to access the USB device */ #define ROOT_HUB_PORT(p) (((p) & 0xff) << 16) +/* Maximum number of ports under a hub device */ +#define XHCI_MAX_PORTS(p) (((p) & 0xff) << 24) /* tt_info bitmasks */ /* @@ -522,6 +524,7 @@ struct xhci_slot_ctx { * '0' if the device is not low or full speed. */ #define TT_PORT (0xff << 8) +#define TT_THINK_TIME(p) (((p) & 0x3) << 16) /* dev_state bitmasks */ /* USB device address - assigned by the HC */ @@ -581,6 +584,7 @@ struct xhci_ep_ctx { /* bit 15 is Linear Stream Array */ /* Interval - period between requests to an endpoint - 125u increments. */ #define EP_INTERVAL(p) ((p & 0xff) << 16) +#define EP_INTERVAL_TO_UFRAMES(p) (1 << (((p) >> 16) & 0xff)) /* ep_info2 bitmasks */ /* @@ -589,6 +593,7 @@ struct xhci_ep_ctx { */ #define FORCE_EVENT (0x1) #define ERROR_COUNT(p) (((p) & 0x3) << 1) +#define CTX_TO_EP_TYPE(p) (((p) >> 3) & 0x7) #define EP_TYPE(p) ((p) << 3) #define ISOC_OUT_EP 1 #define BULK_OUT_EP 2 @@ -601,6 +606,8 @@ struct xhci_ep_ctx { /* bit 7 is Host Initiate Disable - for disabling stream selection */ #define MAX_BURST(p) (((p)&0xff) << 8) #define MAX_PACKET(p) (((p)&0xffff) << 16) +#define MAX_PACKET_MASK (0xffff << 16) +#define MAX_PACKET_DECODED(p) (((p) >> 16) & 0xffff) /** @@ -616,11 +623,44 @@ struct xhci_input_control_ctx { u32 rsvd2[6]; }; +/* Represents everything that is needed to issue a command on the command ring. + * It's useful to pre-allocate these for commands that cannot fail due to + * out-of-memory errors, like freeing streams. + */ +struct xhci_command { + /* Input context for changing device state */ + struct xhci_container_ctx *in_ctx; + u32 status; + /* If completion is null, no one is waiting on this command + * and the structure can be freed after the command completes. + */ + struct completion *completion; + union xhci_trb *command_trb; + struct list_head cmd_list; +}; + /* drop context bitmasks */ #define DROP_EP(x) (0x1 << x) /* add context bitmasks */ #define ADD_EP(x) (0x1 << x) +struct xhci_virt_ep { + struct xhci_ring *ring; + /* Temporary storage in case the configure endpoint command fails and we + * have to restore the device state to the previous state + */ + struct xhci_ring *new_ring; + unsigned int ep_state; +#define SET_DEQ_PENDING (1 << 0) +#define EP_HALTED (1 << 1) + /* ---- Related to URB cancellation ---- */ + struct list_head cancelled_td_list; + unsigned int cancels_pending; + /* The TRB that was last reported in a stopped endpoint ring */ + union xhci_trb *stopped_trb; + struct xhci_td *stopped_td; +}; + struct xhci_virt_device { /* * Commands to the hardware are passed an "input context" that @@ -633,16 +673,11 @@ struct xhci_virt_device { struct xhci_container_ctx *out_ctx; /* Used for addressing devices and configuration changes */ struct xhci_container_ctx *in_ctx; - - /* FIXME when stream support is added */ - struct xhci_ring *ep_rings[31]; - /* Temporary storage in case the configure endpoint command fails and we - * have to restore the device state to the previous state - */ - struct xhci_ring *new_ep_rings[31]; + struct xhci_virt_ep eps[31]; struct completion cmd_completion; /* Status of the last command issued for this device */ u32 cmd_status; + struct list_head cmd_list; }; @@ -905,6 +940,8 @@ union xhci_trb { * It must also be greater than 16. */ #define TRBS_PER_SEGMENT 64 +/* Allow two commands + a link TRB, along with any reserved command TRBs */ +#define MAX_RSVD_CMD_TRBS (TRBS_PER_SEGMENT - 3) #define SEGMENT_SIZE (TRBS_PER_SEGMENT*16) /* TRB buffer pointers can't cross 64KB boundaries */ #define TRB_MAX_BUFF_SHIFT 16 @@ -926,6 +963,12 @@ struct xhci_td { union xhci_trb *last_trb; }; +struct xhci_dequeue_state { + struct xhci_segment *new_deq_seg; + union xhci_trb *new_deq_ptr; + int new_cycle_state; +}; + struct xhci_ring { struct xhci_segment *first_seg; union xhci_trb *enqueue; @@ -935,15 +978,6 @@ struct xhci_ring { struct xhci_segment *deq_seg; unsigned int deq_updates; struct list_head td_list; - /* ---- Related to URB cancellation ---- */ - struct list_head cancelled_td_list; - unsigned int cancels_pending; - unsigned int state; -#define SET_DEQ_PENDING (1 << 0) -#define EP_HALTED (1 << 1) - /* The TRB that was last reported in a stopped endpoint ring */ - union xhci_trb *stopped_trb; - struct xhci_td *stopped_td; /* * Write the cycle state into the TRB cycle field to give ownership of * the TRB to the host controller (if we are the producer), or to check @@ -952,12 +986,6 @@ struct xhci_ring { u32 cycle_state; }; -struct xhci_dequeue_state { - struct xhci_segment *new_deq_seg; - union xhci_trb *new_deq_ptr; - int new_cycle_state; -}; - struct xhci_erst_entry { /* 64-bit event ring segment address */ u64 seg_addr; @@ -1034,6 +1062,7 @@ struct xhci_hcd { /* data structures */ struct xhci_device_context_array *dcbaa; struct xhci_ring *cmd_ring; + unsigned int cmd_ring_reserved_trbs; struct xhci_ring *event_ring; struct xhci_erst erst; /* Scratchpad */ @@ -1058,6 +1087,9 @@ struct xhci_hcd { int noops_submitted; int noops_handled; int error_bitmask; + unsigned int quirks; +#define XHCI_LINK_TRB_QUIRK (1 << 0) +#define XHCI_RESET_EP_QUIRK (1 << 1) }; /* For testing purposes */ @@ -1136,6 +1168,13 @@ static inline void xhci_write_64(struct xhci_hcd *xhci, writel(val_hi, ptr + 1); } +static inline int xhci_link_trb_quirk(struct xhci_hcd *xhci) +{ + u32 temp = xhci_readl(xhci, &xhci->cap_regs->hc_capbase); + return ((HC_VERSION(temp) == 0x95) && + (xhci->quirks & XHCI_LINK_TRB_QUIRK)); +} + /* xHCI debugging */ void xhci_print_ir_set(struct xhci_hcd *xhci, struct xhci_intr_reg *ir_set, int set_num); void xhci_print_registers(struct xhci_hcd *xhci); @@ -1158,11 +1197,24 @@ int xhci_alloc_virt_device(struct xhci_hcd *xhci, int slot_id, struct usb_device int xhci_setup_addressable_virt_dev(struct xhci_hcd *xhci, struct usb_device *udev); unsigned int xhci_get_endpoint_index(struct usb_endpoint_descriptor *desc); unsigned int xhci_get_endpoint_flag(struct usb_endpoint_descriptor *desc); +unsigned int xhci_get_endpoint_flag_from_index(unsigned int ep_index); +unsigned int xhci_last_valid_endpoint(u32 added_ctxs); void xhci_endpoint_zero(struct xhci_hcd *xhci, struct xhci_virt_device *virt_dev, struct usb_host_endpoint *ep); +void xhci_endpoint_copy(struct xhci_hcd *xhci, + struct xhci_container_ctx *in_ctx, + struct xhci_container_ctx *out_ctx, + unsigned int ep_index); +void xhci_slot_copy(struct xhci_hcd *xhci, + struct xhci_container_ctx *in_ctx, + struct xhci_container_ctx *out_ctx); int xhci_endpoint_init(struct xhci_hcd *xhci, struct xhci_virt_device *virt_dev, struct usb_device *udev, struct usb_host_endpoint *ep, gfp_t mem_flags); void xhci_ring_free(struct xhci_hcd *xhci, struct xhci_ring *ring); +struct xhci_command *xhci_alloc_command(struct xhci_hcd *xhci, + bool allocate_completion, gfp_t mem_flags); +void xhci_free_command(struct xhci_hcd *xhci, + struct xhci_command *command); #ifdef CONFIG_PCI /* xHCI PCI glue */ @@ -1182,6 +1234,8 @@ irqreturn_t xhci_irq(struct usb_hcd *hcd); int xhci_alloc_dev(struct usb_hcd *hcd, struct usb_device *udev); void xhci_free_dev(struct usb_hcd *hcd, struct usb_device *udev); int xhci_address_device(struct usb_hcd *hcd, struct usb_device *udev); +int xhci_update_hub_device(struct usb_hcd *hcd, struct usb_device *hdev, + struct usb_tt *tt, gfp_t mem_flags); int xhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flags); int xhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status); int xhci_add_endpoint(struct usb_hcd *hcd, struct usb_device *udev, struct usb_host_endpoint *ep); @@ -1205,7 +1259,11 @@ int xhci_queue_ctrl_tx(struct xhci_hcd *xhci, gfp_t mem_flags, struct urb *urb, int slot_id, unsigned int ep_index); int xhci_queue_bulk_tx(struct xhci_hcd *xhci, gfp_t mem_flags, struct urb *urb, int slot_id, unsigned int ep_index); +int xhci_queue_intr_tx(struct xhci_hcd *xhci, gfp_t mem_flags, struct urb *urb, + int slot_id, unsigned int ep_index); int xhci_queue_configure_endpoint(struct xhci_hcd *xhci, dma_addr_t in_ctx_ptr, + u32 slot_id, bool command_must_succeed); +int xhci_queue_evaluate_context(struct xhci_hcd *xhci, dma_addr_t in_ctx_ptr, u32 slot_id); int xhci_queue_reset_ep(struct xhci_hcd *xhci, int slot_id, unsigned int ep_index); @@ -1213,8 +1271,13 @@ void xhci_find_new_dequeue_state(struct xhci_hcd *xhci, unsigned int slot_id, unsigned int ep_index, struct xhci_td *cur_td, struct xhci_dequeue_state *state); void xhci_queue_new_dequeue_state(struct xhci_hcd *xhci, - struct xhci_ring *ep_ring, unsigned int slot_id, - unsigned int ep_index, struct xhci_dequeue_state *deq_state); + unsigned int slot_id, unsigned int ep_index, + struct xhci_dequeue_state *deq_state); +void xhci_cleanup_stalled_ring(struct xhci_hcd *xhci, + struct usb_device *udev, unsigned int ep_index); +void xhci_queue_config_ep_quirk(struct xhci_hcd *xhci, + unsigned int slot_id, unsigned int ep_index, + struct xhci_dequeue_state *deq_state); /* xHCI roothub code */ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, u16 wIndex, diff --git a/drivers/usb/image/microtek.c b/drivers/usb/image/microtek.c index 4541dfcea88f..459a7287fe01 100644 --- a/drivers/usb/image/microtek.c +++ b/drivers/usb/image/microtek.c @@ -653,33 +653,6 @@ static struct scsi_host_template mts_scsi_host_template = { .max_sectors= 256, /* 128 K */ }; -struct vendor_product -{ - char* name; - enum - { - mts_sup_unknown=0, - mts_sup_alpha, - mts_sup_full - } - support_status; -} ; - - -/* These are taken from the msmUSB.inf file on the Windows driver CD */ -static const struct vendor_product mts_supported_products[] = -{ - { "Phantom 336CX", mts_sup_unknown}, - { "Phantom 336CX", mts_sup_unknown}, - { "Scanmaker X6", mts_sup_alpha}, - { "Phantom C6", mts_sup_unknown}, - { "Phantom 336CX", mts_sup_unknown}, - { "ScanMaker V6USL", mts_sup_unknown}, - { "ScanMaker V6USL", mts_sup_unknown}, - { "Scanmaker V6UL", mts_sup_unknown}, - { "Scanmaker V6UPL", mts_sup_alpha}, -}; - /* The entries of microtek_table must correspond, line-by-line to the entries of mts_supported_products[]. */ @@ -711,7 +684,6 @@ static int mts_usb_probe(struct usb_interface *intf, int err_retval = -ENOMEM; struct mts_desc * new_desc; - struct vendor_product const* p; struct usb_device *dev = interface_to_usbdev (intf); /* the current altsetting on the interface we're probing */ @@ -726,15 +698,6 @@ static int mts_usb_probe(struct usb_interface *intf, MTS_DEBUG_GOT_HERE(); - p = &mts_supported_products[id - mts_usb_ids]; - - MTS_DEBUG_GOT_HERE(); - - MTS_DEBUG( "found model %s\n", p->name ); - if ( p->support_status != mts_sup_full ) - MTS_MESSAGE( "model %s is not known to be fully supported, reports welcome!\n", - p->name ); - /* the current altsetting on the interface we're probing */ altsetting = intf->cur_altsetting; diff --git a/drivers/usb/misc/idmouse.c b/drivers/usb/misc/idmouse.c index 6da8887538c7..1337a9ce80b9 100644 --- a/drivers/usb/misc/idmouse.c +++ b/drivers/usb/misc/idmouse.c @@ -96,6 +96,8 @@ static int idmouse_probe(struct usb_interface *interface, const struct usb_device_id *id); static void idmouse_disconnect(struct usb_interface *interface); +static int idmouse_suspend(struct usb_interface *intf, pm_message_t message); +static int idmouse_resume(struct usb_interface *intf); /* file operation pointers */ static const struct file_operations idmouse_fops = { @@ -117,7 +119,11 @@ static struct usb_driver idmouse_driver = { .name = DRIVER_SHORT, .probe = idmouse_probe, .disconnect = idmouse_disconnect, + .suspend = idmouse_suspend, + .resume = idmouse_resume, + .reset_resume = idmouse_resume, .id_table = idmouse_table, + .supports_autosuspend = 1, }; static int idmouse_create_image(struct usb_idmouse *dev) @@ -197,6 +203,17 @@ reset: return result; } +/* PM operations are nops as this driver does IO only during open() */ +static int idmouse_suspend(struct usb_interface *intf, pm_message_t message) +{ + return 0; +} + +static int idmouse_resume(struct usb_interface *intf) +{ + return 0; +} + static inline void idmouse_delete(struct usb_idmouse *dev) { kfree(dev->bulk_in_buffer); @@ -235,9 +252,13 @@ static int idmouse_open(struct inode *inode, struct file *file) } else { /* create a new image and check for success */ + result = usb_autopm_get_interface(interface); + if (result) + goto error; result = idmouse_create_image (dev); if (result) goto error; + usb_autopm_put_interface(interface); /* increment our usage count for the driver */ ++dev->open; diff --git a/drivers/usb/misc/ldusb.c b/drivers/usb/misc/ldusb.c index ad4fb15b5dcb..90f130126c10 100644 --- a/drivers/usb/misc/ldusb.c +++ b/drivers/usb/misc/ldusb.c @@ -412,6 +412,9 @@ static unsigned int ld_usb_poll(struct file *file, poll_table *wait) dev = file->private_data; + if (!dev->intf) + return POLLERR | POLLHUP; + poll_wait(file, &dev->read_wait, wait); poll_wait(file, &dev->write_wait, wait); @@ -767,6 +770,9 @@ static void ld_usb_disconnect(struct usb_interface *intf) ld_usb_delete(dev); } else { dev->intf = NULL; + /* wake up pollers */ + wake_up_interruptible_all(&dev->read_wait); + wake_up_interruptible_all(&dev->write_wait); mutex_unlock(&dev->mutex); } diff --git a/drivers/usb/misc/legousbtower.c b/drivers/usb/misc/legousbtower.c index 97efeaec4d52..faa6d623de78 100644 --- a/drivers/usb/misc/legousbtower.c +++ b/drivers/usb/misc/legousbtower.c @@ -552,6 +552,9 @@ static unsigned int tower_poll (struct file *file, poll_table *wait) dev = file->private_data; + if (!dev->udev) + return POLLERR | POLLHUP; + poll_wait(file, &dev->read_wait, wait); poll_wait(file, &dev->write_wait, wait); @@ -1025,6 +1028,9 @@ static void tower_disconnect (struct usb_interface *interface) tower_delete (dev); } else { dev->udev = NULL; + /* wake up pollers */ + wake_up_interruptible_all(&dev->read_wait); + wake_up_interruptible_all(&dev->write_wait); mutex_unlock(&dev->lock); } diff --git a/drivers/usb/misc/rio500.c b/drivers/usb/misc/rio500.c index d645f3899fe1..32d0199d0c32 100644 --- a/drivers/usb/misc/rio500.c +++ b/drivers/usb/misc/rio500.c @@ -429,8 +429,7 @@ read_rio(struct file *file, char __user *buffer, size_t count, loff_t * ppos) return read_count; } -static struct -file_operations usb_rio_fops = { +static const struct file_operations usb_rio_fops = { .owner = THIS_MODULE, .read = read_rio, .write = write_rio, diff --git a/drivers/usb/misc/sisusbvga/sisusb.c b/drivers/usb/misc/sisusbvga/sisusb.c index b4ec716de7da..0025847743f3 100644 --- a/drivers/usb/misc/sisusbvga/sisusb.c +++ b/drivers/usb/misc/sisusbvga/sisusb.c @@ -79,14 +79,12 @@ sisusb_free_buffers(struct sisusb_usb_data *sisusb) for (i = 0; i < NUMOBUFS; i++) { if (sisusb->obuf[i]) { - usb_buffer_free(sisusb->sisusb_dev, sisusb->obufsize, - sisusb->obuf[i], sisusb->transfer_dma_out[i]); + kfree(sisusb->obuf[i]); sisusb->obuf[i] = NULL; } } if (sisusb->ibuf) { - usb_buffer_free(sisusb->sisusb_dev, sisusb->ibufsize, - sisusb->ibuf, sisusb->transfer_dma_in); + kfree(sisusb->ibuf); sisusb->ibuf = NULL; } } @@ -230,8 +228,7 @@ sisusb_bulk_completeout(struct urb *urb) static int sisusb_bulkout_msg(struct sisusb_usb_data *sisusb, int index, unsigned int pipe, void *data, - int len, int *actual_length, int timeout, unsigned int tflags, - dma_addr_t transfer_dma) + int len, int *actual_length, int timeout, unsigned int tflags) { struct urb *urb = sisusb->sisurbout[index]; int retval, byteswritten = 0; @@ -245,9 +242,6 @@ sisusb_bulkout_msg(struct sisusb_usb_data *sisusb, int index, unsigned int pipe, urb->transfer_flags |= tflags; urb->actual_length = 0; - if ((urb->transfer_dma = transfer_dma)) - urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; - /* Set up context */ sisusb->urbout_context[index].actual_length = (timeout) ? NULL : actual_length; @@ -297,8 +291,8 @@ sisusb_bulk_completein(struct urb *urb) } static int -sisusb_bulkin_msg(struct sisusb_usb_data *sisusb, unsigned int pipe, void *data, int len, - int *actual_length, int timeout, unsigned int tflags, dma_addr_t transfer_dma) +sisusb_bulkin_msg(struct sisusb_usb_data *sisusb, unsigned int pipe, void *data, + int len, int *actual_length, int timeout, unsigned int tflags) { struct urb *urb = sisusb->sisurbin; int retval, readbytes = 0; @@ -311,9 +305,6 @@ sisusb_bulkin_msg(struct sisusb_usb_data *sisusb, unsigned int pipe, void *data, urb->transfer_flags |= tflags; urb->actual_length = 0; - if ((urb->transfer_dma = transfer_dma)) - urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; - sisusb->completein = 0; retval = usb_submit_urb(urb, GFP_ATOMIC); if (retval == 0) { @@ -422,8 +413,7 @@ static int sisusb_send_bulk_msg(struct sisusb_usb_data *sisusb, int ep, int len, thispass, &transferred_len, async ? 0 : 5 * HZ, - tflags, - sisusb->transfer_dma_out[index]); + tflags); if (result == -ETIMEDOUT) { @@ -432,29 +422,16 @@ static int sisusb_send_bulk_msg(struct sisusb_usb_data *sisusb, int ep, int len, return -ETIME; continue; + } - } else if ((result == 0) && !async && transferred_len) { + if ((result == 0) && !async && transferred_len) { thispass -= transferred_len; - if (thispass) { - if (sisusb->transfer_dma_out) { - /* If DMA, copy remaining - * to beginning of buffer - */ - memcpy(buffer, - buffer + transferred_len, - thispass); - } else { - /* If not DMA, simply increase - * the pointer - */ - buffer += transferred_len; - } - } + buffer += transferred_len; } else break; - }; + } if (result) return result; @@ -530,8 +507,7 @@ static int sisusb_recv_bulk_msg(struct sisusb_usb_data *sisusb, int ep, int len, thispass, &transferred_len, 5 * HZ, - tflags, - sisusb->transfer_dma_in); + tflags); if (transferred_len) thispass = transferred_len; @@ -3132,8 +3108,7 @@ static int sisusb_probe(struct usb_interface *intf, /* Allocate buffers */ sisusb->ibufsize = SISUSB_IBUF_SIZE; - if (!(sisusb->ibuf = usb_buffer_alloc(dev, SISUSB_IBUF_SIZE, - GFP_KERNEL, &sisusb->transfer_dma_in))) { + if (!(sisusb->ibuf = kmalloc(SISUSB_IBUF_SIZE, GFP_KERNEL))) { dev_err(&sisusb->sisusb_dev->dev, "Failed to allocate memory for input buffer"); retval = -ENOMEM; goto error_2; @@ -3142,9 +3117,7 @@ static int sisusb_probe(struct usb_interface *intf, sisusb->numobufs = 0; sisusb->obufsize = SISUSB_OBUF_SIZE; for (i = 0; i < NUMOBUFS; i++) { - if (!(sisusb->obuf[i] = usb_buffer_alloc(dev, SISUSB_OBUF_SIZE, - GFP_KERNEL, - &sisusb->transfer_dma_out[i]))) { + if (!(sisusb->obuf[i] = kmalloc(SISUSB_OBUF_SIZE, GFP_KERNEL))) { if (i == 0) { dev_err(&sisusb->sisusb_dev->dev, "Failed to allocate memory for output buffer\n"); retval = -ENOMEM; diff --git a/drivers/usb/misc/sisusbvga/sisusb.h b/drivers/usb/misc/sisusbvga/sisusb.h index cf0b4a5883f6..55492a5930bd 100644 --- a/drivers/usb/misc/sisusbvga/sisusb.h +++ b/drivers/usb/misc/sisusbvga/sisusb.h @@ -123,8 +123,6 @@ struct sisusb_usb_data { int numobufs; /* number of obufs = number of out urbs */ char *obuf[NUMOBUFS], *ibuf; /* transfer buffers */ int obufsize, ibufsize; - dma_addr_t transfer_dma_out[NUMOBUFS]; - dma_addr_t transfer_dma_in; struct urb *sisurbout[NUMOBUFS]; struct urb *sisurbin; unsigned char urbstatus[NUMOBUFS]; diff --git a/drivers/usb/misc/sisusbvga/sisusb_init.c b/drivers/usb/misc/sisusbvga/sisusb_init.c index 273de5d0934e..0ab990744830 100644 --- a/drivers/usb/misc/sisusbvga/sisusb_init.c +++ b/drivers/usb/misc/sisusbvga/sisusb_init.c @@ -43,7 +43,6 @@ #include <linux/init.h> #include <linux/slab.h> #include <linux/spinlock.h> -#include <linux/kref.h> #include "sisusb.h" diff --git a/drivers/usb/misc/usblcd.c b/drivers/usb/misc/usblcd.c index 29092b8e59ce..4fb120357c55 100644 --- a/drivers/usb/misc/usblcd.c +++ b/drivers/usb/misc/usblcd.c @@ -313,7 +313,8 @@ static int lcd_probe(struct usb_interface *interface, const struct usb_device_id if (le16_to_cpu(dev->udev->descriptor.idProduct) != 0x0001) { dev_warn(&interface->dev, "USBLCD model not supported.\n"); - return -ENODEV; + retval = -ENODEV; + goto error; } /* set up the endpoint information */ diff --git a/drivers/usb/misc/usbsevseg.c b/drivers/usb/misc/usbsevseg.c index 28a6a3a09538..3db255537e79 100644 --- a/drivers/usb/misc/usbsevseg.c +++ b/drivers/usb/misc/usbsevseg.c @@ -38,6 +38,7 @@ static char *display_textmodes[] = {"raw", "hex", "ascii", NULL}; struct usb_sevsegdev { struct usb_device *udev; + struct usb_interface *intf; u8 powered; u8 mode_msb; @@ -46,6 +47,8 @@ struct usb_sevsegdev { u8 textmode; u8 text[MAXLEN]; u16 textlength; + + u8 shadow_power; /* for PM */ }; /* sysfs_streq can't replace this completely @@ -65,6 +68,12 @@ static void update_display_powered(struct usb_sevsegdev *mydev) { int rc; + if (!mydev->shadow_power && mydev->powered) { + rc = usb_autopm_get_interface(mydev->intf); + if (rc < 0) + return; + } + rc = usb_control_msg(mydev->udev, usb_sndctrlpipe(mydev->udev, 0), 0x12, @@ -76,12 +85,18 @@ static void update_display_powered(struct usb_sevsegdev *mydev) 2000); if (rc < 0) dev_dbg(&mydev->udev->dev, "power retval = %d\n", rc); + + if (mydev->shadow_power && !mydev->powered) + usb_autopm_put_interface(mydev->intf); } static void update_display_mode(struct usb_sevsegdev *mydev) { int rc; + if(mydev->shadow_power != 1) + return; + rc = usb_control_msg(mydev->udev, usb_sndctrlpipe(mydev->udev, 0), 0x12, @@ -96,14 +111,17 @@ static void update_display_mode(struct usb_sevsegdev *mydev) dev_dbg(&mydev->udev->dev, "mode retval = %d\n", rc); } -static void update_display_visual(struct usb_sevsegdev *mydev) +static void update_display_visual(struct usb_sevsegdev *mydev, gfp_t mf) { int rc; int i; unsigned char *buffer; u8 decimals = 0; - buffer = kzalloc(MAXLEN, GFP_KERNEL); + if(mydev->shadow_power != 1) + return; + + buffer = kzalloc(MAXLEN, mf); if (!buffer) { dev_err(&mydev->udev->dev, "out of memory\n"); return; @@ -163,7 +181,7 @@ static ssize_t set_attr_##name(struct device *dev, \ struct usb_sevsegdev *mydev = usb_get_intfdata(intf); \ \ mydev->name = simple_strtoul(buf, NULL, 10); \ - update_fcn(mydev); \ + update_fcn(mydev); \ \ return count; \ } \ @@ -194,7 +212,7 @@ static ssize_t set_attr_text(struct device *dev, if (end > 0) memcpy(mydev->text, buf, end); - update_display_visual(mydev); + update_display_visual(mydev, GFP_KERNEL); return count; } @@ -242,7 +260,7 @@ static ssize_t set_attr_decimals(struct device *dev, if (buf[i] == '1') mydev->decimals[end-1-i] = 1; - update_display_visual(mydev); + update_display_visual(mydev, GFP_KERNEL); return count; } @@ -286,7 +304,7 @@ static ssize_t set_attr_textmode(struct device *dev, for (i = 0; display_textmodes[i]; i++) { if (sysfs_streq(display_textmodes[i], buf)) { mydev->textmode = i; - update_display_visual(mydev); + update_display_visual(mydev, GFP_KERNEL); return count; } } @@ -330,6 +348,7 @@ static int sevseg_probe(struct usb_interface *interface, } mydev->udev = usb_get_dev(udev); + mydev->intf = interface; usb_set_intfdata(interface, mydev); /*set defaults */ @@ -364,11 +383,49 @@ static void sevseg_disconnect(struct usb_interface *interface) dev_info(&interface->dev, "USB 7 Segment now disconnected\n"); } +static int sevseg_suspend(struct usb_interface *intf, pm_message_t message) +{ + struct usb_sevsegdev *mydev; + + mydev = usb_get_intfdata(intf); + mydev->shadow_power = 0; + + return 0; +} + +static int sevseg_resume(struct usb_interface *intf) +{ + struct usb_sevsegdev *mydev; + + mydev = usb_get_intfdata(intf); + mydev->shadow_power = 1; + update_display_mode(mydev); + update_display_visual(mydev, GFP_NOIO); + + return 0; +} + +static int sevseg_reset_resume(struct usb_interface *intf) +{ + struct usb_sevsegdev *mydev; + + mydev = usb_get_intfdata(intf); + mydev->shadow_power = 1; + update_display_mode(mydev); + update_display_visual(mydev, GFP_NOIO); + + return 0; +} + static struct usb_driver sevseg_driver = { .name = "usbsevseg", .probe = sevseg_probe, .disconnect = sevseg_disconnect, + .suspend = sevseg_suspend, + .resume = sevseg_resume, + .reset_resume = sevseg_reset_resume, .id_table = id_table, + .supports_autosuspend = 1, }; static int __init usb_sevseg_init(void) diff --git a/drivers/usb/mon/Kconfig b/drivers/usb/mon/Kconfig index f28f350cd96a..635745f57fbd 100644 --- a/drivers/usb/mon/Kconfig +++ b/drivers/usb/mon/Kconfig @@ -5,11 +5,9 @@ config USB_MON tristate "USB Monitor" depends on USB - default y if USB=y - default m if USB=m help If you select this option, a component which captures the USB traffic between peripheral-specific drivers and HC drivers will be built. For more information, see <file:Documentation/usb/usbmon.txt>. - If unsure, say Y (if allowed), otherwise M. + If unsure, say Y, if allowed, otherwise M. diff --git a/drivers/usb/mon/Makefile b/drivers/usb/mon/Makefile index c6516b566731..384b198faa7c 100644 --- a/drivers/usb/mon/Makefile +++ b/drivers/usb/mon/Makefile @@ -2,6 +2,6 @@ # Makefile for USB monitor # -usbmon-objs := mon_main.o mon_stat.o mon_text.o mon_bin.o mon_dma.o +usbmon-objs := mon_main.o mon_stat.o mon_text.o mon_bin.o obj-$(CONFIG_USB_MON) += usbmon.o diff --git a/drivers/usb/mon/mon_bin.c b/drivers/usb/mon/mon_bin.c index 0f7a30b7d2d1..10f3205798e8 100644 --- a/drivers/usb/mon/mon_bin.c +++ b/drivers/usb/mon/mon_bin.c @@ -220,9 +220,8 @@ static void mon_free_buff(struct mon_pgmap *map, int npages); /* * This is a "chunked memcpy". It does not manipulate any counters. - * But it returns the new offset for repeated application. */ -unsigned int mon_copy_to_buff(const struct mon_reader_bin *this, +static void mon_copy_to_buff(const struct mon_reader_bin *this, unsigned int off, const unsigned char *from, unsigned int length) { unsigned int step_len; @@ -247,7 +246,6 @@ unsigned int mon_copy_to_buff(const struct mon_reader_bin *this, from += step_len; length -= step_len; } - return off; } /* @@ -350,12 +348,12 @@ static unsigned int mon_buff_area_alloc_contiguous(struct mon_reader_bin *rp, /* * Return a few (kilo-)bytes to the head of the buffer. - * This is used if a DMA fetch fails. + * This is used if a data fetch fails. */ static void mon_buff_area_shrink(struct mon_reader_bin *rp, unsigned int size) { - size = (size + PKT_ALIGN-1) & ~(PKT_ALIGN-1); + /* size &= ~(PKT_ALIGN-1); -- we're called with aligned size */ rp->b_cnt -= size; if (rp->b_in < size) rp->b_in += rp->b_size; @@ -400,15 +398,8 @@ static char mon_bin_get_data(const struct mon_reader_bin *rp, unsigned int offset, struct urb *urb, unsigned int length) { - if (urb->dev->bus->uses_dma && - (urb->transfer_flags & URB_NO_TRANSFER_DMA_MAP)) { - mon_dmapeek_vec(rp, offset, urb->transfer_dma, length); - return 0; - } - if (urb->transfer_buffer == NULL) return 'Z'; - mon_copy_to_buff(rp, offset, urb->transfer_buffer, length); return 0; } @@ -442,6 +433,7 @@ static void mon_bin_event(struct mon_reader_bin *rp, struct urb *urb, unsigned int urb_length; unsigned int offset; unsigned int length; + unsigned int delta; unsigned int ndesc, lendesc; unsigned char dir; struct mon_bin_hdr *ep; @@ -546,8 +538,10 @@ static void mon_bin_event(struct mon_reader_bin *rp, struct urb *urb, if (length != 0) { ep->flag_data = mon_bin_get_data(rp, offset, urb, length); if (ep->flag_data != 0) { /* Yes, it's 0x00, not '0' */ - ep->len_cap = 0; - mon_buff_area_shrink(rp, length); + delta = (ep->len_cap + PKT_ALIGN-1) & ~(PKT_ALIGN-1); + ep->len_cap -= length; + delta -= (ep->len_cap + PKT_ALIGN-1) & ~(PKT_ALIGN-1); + mon_buff_area_shrink(rp, delta); } } else { ep->flag_data = data_tag; @@ -635,7 +629,6 @@ static int mon_bin_open(struct inode *inode, struct file *file) spin_lock_init(&rp->b_lock); init_waitqueue_head(&rp->b_wait); mutex_init(&rp->fetch_lock); - rp->b_size = BUFF_DFL; size = sizeof(struct mon_pgmap) * (rp->b_size/CHUNK_SIZE); @@ -1184,7 +1177,7 @@ static int mon_bin_vma_fault(struct vm_area_struct *vma, struct vm_fault *vmf) return 0; } -static struct vm_operations_struct mon_bin_vm_ops = { +static const struct vm_operations_struct mon_bin_vm_ops = { .open = mon_bin_vma_open, .close = mon_bin_vma_close, .fault = mon_bin_vma_fault, diff --git a/drivers/usb/mon/mon_dma.c b/drivers/usb/mon/mon_dma.c deleted file mode 100644 index 140cc80bd2b1..000000000000 --- a/drivers/usb/mon/mon_dma.c +++ /dev/null @@ -1,95 +0,0 @@ -/* - * The USB Monitor, inspired by Dave Harding's USBMon. - * - * mon_dma.c: Library which snoops on DMA areas. - * - * Copyright (C) 2005 Pete Zaitcev (zaitcev@redhat.com) - */ -#include <linux/kernel.h> -#include <linux/list.h> -#include <linux/highmem.h> -#include <asm/page.h> - -#include <linux/usb.h> /* Only needed for declarations in usb_mon.h */ -#include "usb_mon.h" - -/* - * PC-compatibles, are, fortunately, sufficiently cache-coherent for this. - */ -#if defined(__i386__) || defined(__x86_64__) /* CONFIG_ARCH_I386 doesn't exit */ -#define MON_HAS_UNMAP 1 - -#define phys_to_page(phys) pfn_to_page((phys) >> PAGE_SHIFT) - -char mon_dmapeek(unsigned char *dst, dma_addr_t dma_addr, int len) -{ - struct page *pg; - unsigned long flags; - unsigned char *map; - unsigned char *ptr; - - /* - * On i386, a DMA handle is the "physical" address of a page. - * In other words, the bus address is equal to physical address. - * There is no IOMMU. - */ - pg = phys_to_page(dma_addr); - - /* - * We are called from hardware IRQs in case of callbacks. - * But we can be called from softirq or process context in case - * of submissions. In such case, we need to protect KM_IRQ0. - */ - local_irq_save(flags); - map = kmap_atomic(pg, KM_IRQ0); - ptr = map + (dma_addr & (PAGE_SIZE-1)); - memcpy(dst, ptr, len); - kunmap_atomic(map, KM_IRQ0); - local_irq_restore(flags); - return 0; -} - -void mon_dmapeek_vec(const struct mon_reader_bin *rp, - unsigned int offset, dma_addr_t dma_addr, unsigned int length) -{ - unsigned long flags; - unsigned int step_len; - struct page *pg; - unsigned char *map; - unsigned long page_off, page_len; - - local_irq_save(flags); - while (length) { - /* compute number of bytes we are going to copy in this page */ - step_len = length; - page_off = dma_addr & (PAGE_SIZE-1); - page_len = PAGE_SIZE - page_off; - if (page_len < step_len) - step_len = page_len; - - /* copy data and advance pointers */ - pg = phys_to_page(dma_addr); - map = kmap_atomic(pg, KM_IRQ0); - offset = mon_copy_to_buff(rp, offset, map + page_off, step_len); - kunmap_atomic(map, KM_IRQ0); - dma_addr += step_len; - length -= step_len; - } - local_irq_restore(flags); -} - -#endif /* __i386__ */ - -#ifndef MON_HAS_UNMAP -char mon_dmapeek(unsigned char *dst, dma_addr_t dma_addr, int len) -{ - return 'D'; -} - -void mon_dmapeek_vec(const struct mon_reader_bin *rp, - unsigned int offset, dma_addr_t dma_addr, unsigned int length) -{ - ; -} - -#endif /* MON_HAS_UNMAP */ diff --git a/drivers/usb/mon/mon_main.c b/drivers/usb/mon/mon_main.c index 5e0ab4201c00..e0c2db3b767b 100644 --- a/drivers/usb/mon/mon_main.c +++ b/drivers/usb/mon/mon_main.c @@ -361,7 +361,6 @@ static int __init mon_init(void) } // MOD_INC_USE_COUNT(which_module?); - mutex_lock(&usb_bus_list_lock); list_for_each_entry (ubus, &usb_bus_list, bus_list) { mon_bus_init(ubus); diff --git a/drivers/usb/mon/mon_text.c b/drivers/usb/mon/mon_text.c index a7eb4c99342c..9f1a9227ebe6 100644 --- a/drivers/usb/mon/mon_text.c +++ b/drivers/usb/mon/mon_text.c @@ -150,20 +150,6 @@ static inline char mon_text_get_data(struct mon_event_text *ep, struct urb *urb, return '>'; } - /* - * The check to see if it's safe to poke at data has an enormous - * number of corner cases, but it seems that the following is - * more or less safe. - * - * We do not even try to look at transfer_buffer, because it can - * contain non-NULL garbage in case the upper level promised to - * set DMA for the HCD. - */ - if (urb->dev->bus->uses_dma && - (urb->transfer_flags & URB_NO_TRANSFER_DMA_MAP)) { - return mon_dmapeek(ep->data, urb->transfer_dma, len); - } - if (urb->transfer_buffer == NULL) return 'Z'; /* '0' would be not as pretty. */ diff --git a/drivers/usb/mon/usb_mon.h b/drivers/usb/mon/usb_mon.h index f5d84ff8c101..df9a4df342c7 100644 --- a/drivers/usb/mon/usb_mon.h +++ b/drivers/usb/mon/usb_mon.h @@ -65,20 +65,6 @@ int __init mon_bin_init(void); void mon_bin_exit(void); /* - * DMA interface. - * - * XXX The vectored side needs a serious re-thinking. Abstracting vectors, - * like in Paolo's original patch, produces a double pkmap. We need an idea. -*/ -extern char mon_dmapeek(unsigned char *dst, dma_addr_t dma_addr, int len); - -struct mon_reader_bin; -extern void mon_dmapeek_vec(const struct mon_reader_bin *rp, - unsigned int offset, dma_addr_t dma_addr, unsigned int len); -extern unsigned int mon_copy_to_buff(const struct mon_reader_bin *rp, - unsigned int offset, const unsigned char *from, unsigned int len); - -/* */ extern struct mutex mon_lock; diff --git a/drivers/usb/musb/Kconfig b/drivers/usb/musb/Kconfig index 803adcb5ac1d..b84abd8ee8a5 100644 --- a/drivers/usb/musb/Kconfig +++ b/drivers/usb/musb/Kconfig @@ -8,8 +8,8 @@ comment "Enable Host or Gadget support to see Inventra options" # (M)HDRC = (Multipoint) Highspeed Dual-Role Controller config USB_MUSB_HDRC - depends on (USB || USB_GADGET) && HAVE_CLK - depends on !SUPERH + depends on (USB || USB_GADGET) + depends on (ARM || BLACKFIN) select NOP_USB_XCEIV if ARCH_DAVINCI select TWL4030_USB if MACH_OMAP_3430SDP select NOP_USB_XCEIV if MACH_OMAP3EVM diff --git a/drivers/usb/musb/blackfin.c b/drivers/usb/musb/blackfin.c index f2f66ebc7362..fcec87ea709e 100644 --- a/drivers/usb/musb/blackfin.c +++ b/drivers/usb/musb/blackfin.c @@ -14,7 +14,6 @@ #include <linux/slab.h> #include <linux/init.h> #include <linux/list.h> -#include <linux/clk.h> #include <linux/gpio.h> #include <linux/io.h> diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c index 1d26beddf2ca..3a61ddb62bd2 100644 --- a/drivers/usb/musb/musb_core.c +++ b/drivers/usb/musb/musb_core.c @@ -1850,6 +1850,10 @@ static void musb_free(struct musb *musb) dma_controller_destroy(c); } +#ifdef CONFIG_USB_MUSB_OTG + put_device(musb->xceiv->dev); +#endif + musb_writeb(musb->mregs, MUSB_DEVCTL, 0); musb_platform_exit(musb); musb_writeb(musb->mregs, MUSB_DEVCTL, 0); @@ -1859,10 +1863,6 @@ static void musb_free(struct musb *musb) clk_put(musb->clock); } -#ifdef CONFIG_USB_MUSB_OTG - put_device(musb->xceiv->dev); -#endif - #ifdef CONFIG_USB_MUSB_HDRC_HCD usb_put_hcd(musb_to_hcd(musb)); #else diff --git a/drivers/usb/musb/musb_core.h b/drivers/usb/musb/musb_core.h index 381d648a36b8..6aa5f22e5274 100644 --- a/drivers/usb/musb/musb_core.h +++ b/drivers/usb/musb/musb_core.h @@ -95,6 +95,13 @@ struct musb_ep; #endif #endif /* need MUSB gadget selection */ +#ifndef CONFIG_HAVE_CLK +/* Dummy stub for clk framework */ +#define clk_get(dev, id) NULL +#define clk_put(clock) do {} while (0) +#define clk_enable(clock) do {} while (0) +#define clk_disable(clock) do {} while (0) +#endif #ifdef CONFIG_PROC_FS #include <linux/fs.h> diff --git a/drivers/usb/musb/musb_regs.h b/drivers/usb/musb/musb_regs.h index fbfd3fd9ce1f..cc1d71b57d3c 100644 --- a/drivers/usb/musb/musb_regs.h +++ b/drivers/usb/musb/musb_regs.h @@ -439,15 +439,6 @@ static inline void musb_write_txhubport(void __iomem *mbase, u8 epnum, /* Not implemented - HW has seperate Tx/Rx FIFO */ #define MUSB_TXCSR_MODE 0x0000 -/* - * Dummy stub for clk framework, it will be removed - * until Blackfin supports clk framework - */ -#define clk_get(dev, id) NULL -#define clk_put(clock) do {} while (0) -#define clk_enable(clock) do {} while (0) -#define clk_disable(clock) do {} while (0) - static inline void musb_write_txfifosz(void __iomem *mbase, u8 c_size) { } diff --git a/drivers/usb/otg/isp1301_omap.c b/drivers/usb/otg/isp1301_omap.c index e0d56ef2bcb0..77a5f4188999 100644 --- a/drivers/usb/otg/isp1301_omap.c +++ b/drivers/usb/otg/isp1301_omap.c @@ -117,24 +117,7 @@ static void enable_vbus_draw(struct isp1301 *isp, unsigned mA) pr_debug(" VBUS %d mA error %d\n", mA, status); } -static void enable_vbus_source(struct isp1301 *isp) -{ - /* this board won't supply more than 8mA vbus power. - * some boards can switch a 100ma "unit load" (or more). - */ -} - - -/* products will deliver OTG messages with LEDs, GUI, etc */ -static inline void notresponding(struct isp1301 *isp) -{ - printk(KERN_NOTICE "OTG device not responding.\n"); -} - - -#endif - -#if defined(CONFIG_MACH_OMAP_H4) +#else static void enable_vbus_draw(struct isp1301 *isp, unsigned mA) { @@ -144,6 +127,8 @@ static void enable_vbus_draw(struct isp1301 *isp, unsigned mA) */ } +#endif + static void enable_vbus_source(struct isp1301 *isp) { /* this board won't supply more than 8mA vbus power. @@ -159,8 +144,6 @@ static inline void notresponding(struct isp1301 *isp) } -#endif - /*-------------------------------------------------------------------------*/ static struct i2c_driver isp1301_driver; diff --git a/drivers/usb/serial/aircable.c b/drivers/usb/serial/aircable.c index 2cbfab3716e5..b10ac8409411 100644 --- a/drivers/usb/serial/aircable.c +++ b/drivers/usb/serial/aircable.c @@ -554,13 +554,12 @@ static void aircable_throttle(struct tty_struct *tty) { struct usb_serial_port *port = tty->driver_data; struct aircable_private *priv = usb_get_serial_port_data(port); - unsigned long flags; dbg("%s - port %d", __func__, port->number); - spin_lock_irqsave(&priv->rx_lock, flags); + spin_lock_irq(&priv->rx_lock); priv->rx_flags |= THROTTLED; - spin_unlock_irqrestore(&priv->rx_lock, flags); + spin_unlock_irq(&priv->rx_lock); } /* Based on ftdi_sio.c unthrottle */ @@ -569,14 +568,13 @@ static void aircable_unthrottle(struct tty_struct *tty) struct usb_serial_port *port = tty->driver_data; struct aircable_private *priv = usb_get_serial_port_data(port); int actually_throttled; - unsigned long flags; dbg("%s - port %d", __func__, port->number); - spin_lock_irqsave(&priv->rx_lock, flags); + spin_lock_irq(&priv->rx_lock); actually_throttled = priv->rx_flags & ACTUALLY_THROTTLED; priv->rx_flags &= ~(THROTTLED | ACTUALLY_THROTTLED); - spin_unlock_irqrestore(&priv->rx_lock, flags); + spin_unlock_irq(&priv->rx_lock); if (actually_throttled) schedule_work(&priv->rx_work); diff --git a/drivers/usb/serial/ark3116.c b/drivers/usb/serial/ark3116.c index 5d25d3e52bf6..131e61adaaf7 100644 --- a/drivers/usb/serial/ark3116.c +++ b/drivers/usb/serial/ark3116.c @@ -31,10 +31,20 @@ static int debug; static struct usb_device_id id_table [] = { { USB_DEVICE(0x6547, 0x0232) }, + { USB_DEVICE(0x18ec, 0x3118) }, /* USB to IrDA adapter */ { }, }; MODULE_DEVICE_TABLE(usb, id_table); +static int is_irda(struct usb_serial *serial) +{ + struct usb_device *dev = serial->dev; + if (le16_to_cpu(dev->descriptor.idVendor) == 0x18ec && + le16_to_cpu(dev->descriptor.idProduct) == 0x3118) + return 1; + return 0; +} + static inline void ARK3116_SND(struct usb_serial *serial, int seq, __u8 request, __u8 requesttype, __u16 value, __u16 index) @@ -84,11 +94,21 @@ static int ark3116_attach(struct usb_serial *serial) return -ENOMEM; } + if (is_irda(serial)) + dbg("IrDA mode"); + /* 3 */ ARK3116_SND(serial, 3, 0xFE, 0x40, 0x0008, 0x0002); ARK3116_SND(serial, 4, 0xFE, 0x40, 0x0008, 0x0001); ARK3116_SND(serial, 5, 0xFE, 0x40, 0x0000, 0x0008); - ARK3116_SND(serial, 6, 0xFE, 0x40, 0x0000, 0x000B); + ARK3116_SND(serial, 6, 0xFE, 0x40, is_irda(serial) ? 0x0001 : 0x0000, + 0x000B); + + if (is_irda(serial)) { + ARK3116_SND(serial, 1001, 0xFE, 0x40, 0x0000, 0x000C); + ARK3116_SND(serial, 1002, 0xFE, 0x40, 0x0041, 0x000D); + ARK3116_SND(serial, 1003, 0xFE, 0x40, 0x0001, 0x000A); + } /* <-- seq7 */ ARK3116_RCV(serial, 7, 0xFE, 0xC0, 0x0000, 0x0003, 0x00, buf); @@ -125,6 +145,8 @@ static int ark3116_attach(struct usb_serial *serial) ARK3116_SND(serial, 147, 0xFE, 0x40, 0x0083, 0x0003); ARK3116_SND(serial, 148, 0xFE, 0x40, 0x0038, 0x0000); ARK3116_SND(serial, 149, 0xFE, 0x40, 0x0001, 0x0001); + if (is_irda(serial)) + ARK3116_SND(serial, 1004, 0xFE, 0x40, 0x0000, 0x0009); ARK3116_SND(serial, 150, 0xFE, 0x40, 0x0003, 0x0003); ARK3116_RCV(serial, 151, 0xFE, 0xC0, 0x0000, 0x0004, 0x03, buf); ARK3116_SND(serial, 152, 0xFE, 0x40, 0x0000, 0x0003); diff --git a/drivers/usb/serial/ch341.c b/drivers/usb/serial/ch341.c index 8c894a7d5dcf..59eff721fcc5 100644 --- a/drivers/usb/serial/ch341.c +++ b/drivers/usb/serial/ch341.c @@ -56,6 +56,18 @@ #define CH341_BAUDBASE_FACTOR 1532620800 #define CH341_BAUDBASE_DIVMAX 3 +/* Break support - the information used to implement this was gleaned from + * the Net/FreeBSD uchcom.c driver by Takanori Watanabe. Domo arigato. + */ + +#define CH341_REQ_WRITE_REG 0x9A +#define CH341_REQ_READ_REG 0x95 +#define CH341_REG_BREAK1 0x05 +#define CH341_REG_BREAK2 0x18 +#define CH341_NBREAK_BITS_REG1 0x01 +#define CH341_NBREAK_BITS_REG2 0x40 + + static int debug; static struct usb_device_id id_table [] = { @@ -373,6 +385,45 @@ static void ch341_set_termios(struct tty_struct *tty, */ } +static void ch341_break_ctl(struct tty_struct *tty, int break_state) +{ + const uint16_t ch341_break_reg = + CH341_REG_BREAK1 | ((uint16_t) CH341_REG_BREAK2 << 8); + struct usb_serial_port *port = tty->driver_data; + int r; + uint16_t reg_contents; + uint8_t break_reg[2]; + + dbg("%s()", __func__); + + r = ch341_control_in(port->serial->dev, CH341_REQ_READ_REG, + ch341_break_reg, 0, break_reg, sizeof(break_reg)); + if (r < 0) { + printk(KERN_WARNING "%s: USB control read error whilst getting" + " break register contents.\n", __FILE__); + return; + } + dbg("%s - initial ch341 break register contents - reg1: %x, reg2: %x", + __func__, break_reg[0], break_reg[1]); + if (break_state != 0) { + dbg("%s - Enter break state requested", __func__); + break_reg[0] &= ~CH341_NBREAK_BITS_REG1; + break_reg[1] &= ~CH341_NBREAK_BITS_REG2; + } else { + dbg("%s - Leave break state requested", __func__); + break_reg[0] |= CH341_NBREAK_BITS_REG1; + break_reg[1] |= CH341_NBREAK_BITS_REG2; + } + dbg("%s - New ch341 break register contents - reg1: %x, reg2: %x", + __func__, break_reg[0], break_reg[1]); + reg_contents = (uint16_t)break_reg[0] | ((uint16_t)break_reg[1] << 8); + r = ch341_control_out(port->serial->dev, CH341_REQ_WRITE_REG, + ch341_break_reg, reg_contents); + if (r < 0) + printk(KERN_WARNING "%s: USB control write error whilst setting" + " break register contents.\n", __FILE__); +} + static int ch341_tiocmset(struct tty_struct *tty, struct file *file, unsigned int set, unsigned int clear) { @@ -576,6 +627,7 @@ static struct usb_serial_driver ch341_device = { .close = ch341_close, .ioctl = ch341_ioctl, .set_termios = ch341_set_termios, + .break_ctl = ch341_break_ctl, .tiocmget = ch341_tiocmget, .tiocmset = ch341_tiocmset, .read_int_callback = ch341_read_int_callback, diff --git a/drivers/usb/serial/cp210x.c b/drivers/usb/serial/cp210x.c index 4a208fe85bc9..bd254ec97d14 100644 --- a/drivers/usb/serial/cp210x.c +++ b/drivers/usb/serial/cp210x.c @@ -50,6 +50,8 @@ static int cp210x_tiocmset_port(struct usb_serial_port *port, struct file *, static void cp210x_break_ctl(struct tty_struct *, int); static int cp210x_startup(struct usb_serial *); static void cp210x_disconnect(struct usb_serial *); +static void cp210x_dtr_rts(struct usb_serial_port *p, int on); +static int cp210x_carrier_raised(struct usb_serial_port *p); static int debug; @@ -113,6 +115,7 @@ static struct usb_device_id id_table [] = { { USB_DEVICE(0x166A, 0x0303) }, /* Clipsal 5500PCU C-Bus USB interface */ { USB_DEVICE(0x16D6, 0x0001) }, /* Jablotron serial interface */ { USB_DEVICE(0x18EF, 0xE00F) }, /* ELV USB-I2C-Interface */ + { USB_DEVICE(0x413C, 0x9500) }, /* DW700 GPS USB interface */ { } /* Terminating Entry */ }; @@ -142,6 +145,8 @@ static struct usb_serial_driver cp210x_device = { .tiocmset = cp210x_tiocmset, .attach = cp210x_startup, .disconnect = cp210x_disconnect, + .dtr_rts = cp210x_dtr_rts, + .carrier_raised = cp210x_carrier_raised }; /* Config request types */ @@ -745,6 +750,14 @@ static int cp210x_tiocmset_port(struct usb_serial_port *port, struct file *file, return cp210x_set_config(port, CP210X_SET_MHS, &control, 2); } +static void cp210x_dtr_rts(struct usb_serial_port *p, int on) +{ + if (on) + cp210x_tiocmset_port(p, NULL, TIOCM_DTR|TIOCM_RTS, 0); + else + cp210x_tiocmset_port(p, NULL, 0, TIOCM_DTR|TIOCM_RTS); +} + static int cp210x_tiocmget (struct tty_struct *tty, struct file *file) { struct usb_serial_port *port = tty->driver_data; @@ -767,6 +780,15 @@ static int cp210x_tiocmget (struct tty_struct *tty, struct file *file) return result; } +static int cp210x_carrier_raised(struct usb_serial_port *p) +{ + unsigned int control; + cp210x_get_config(p, CP210X_GET_MDMSTS, &control, 1); + if (control & CONTROL_DCD) + return 1; + return 0; +} + static void cp210x_break_ctl (struct tty_struct *tty, int break_state) { struct usb_serial_port *port = tty->driver_data; diff --git a/drivers/usb/serial/cypress_m8.c b/drivers/usb/serial/cypress_m8.c index e0a8b715f2f2..a591ebec0f89 100644 --- a/drivers/usb/serial/cypress_m8.c +++ b/drivers/usb/serial/cypress_m8.c @@ -1155,13 +1155,12 @@ static void cypress_throttle(struct tty_struct *tty) { struct usb_serial_port *port = tty->driver_data; struct cypress_private *priv = usb_get_serial_port_data(port); - unsigned long flags; dbg("%s - port %d", __func__, port->number); - spin_lock_irqsave(&priv->lock, flags); + spin_lock_irq(&priv->lock); priv->rx_flags = THROTTLED; - spin_unlock_irqrestore(&priv->lock, flags); + spin_unlock_irq(&priv->lock); } @@ -1170,14 +1169,13 @@ static void cypress_unthrottle(struct tty_struct *tty) struct usb_serial_port *port = tty->driver_data; struct cypress_private *priv = usb_get_serial_port_data(port); int actually_throttled, result; - unsigned long flags; dbg("%s - port %d", __func__, port->number); - spin_lock_irqsave(&priv->lock, flags); + spin_lock_irq(&priv->lock); actually_throttled = priv->rx_flags & ACTUALLY_THROTTLED; priv->rx_flags = 0; - spin_unlock_irqrestore(&priv->lock, flags); + spin_unlock_irq(&priv->lock); if (!priv->comm_is_ok) return; @@ -1185,7 +1183,7 @@ static void cypress_unthrottle(struct tty_struct *tty) if (actually_throttled) { port->interrupt_in_urb->dev = port->serial->dev; - result = usb_submit_urb(port->interrupt_in_urb, GFP_ATOMIC); + result = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL); if (result) { dev_err(&port->dev, "%s - failed submitting read urb, " "error %d\n", __func__, result); diff --git a/drivers/usb/serial/digi_acceleport.c b/drivers/usb/serial/digi_acceleport.c index ab3dd991586b..68e80be6b9e1 100644 --- a/drivers/usb/serial/digi_acceleport.c +++ b/drivers/usb/serial/digi_acceleport.c @@ -898,16 +898,16 @@ static void digi_rx_unthrottle(struct tty_struct *tty) spin_lock_irqsave(&priv->dp_port_lock, flags); - /* turn throttle off */ - priv->dp_throttled = 0; - priv->dp_throttle_restart = 0; - /* restart read chain */ if (priv->dp_throttle_restart) { port->read_urb->dev = port->serial->dev; ret = usb_submit_urb(port->read_urb, GFP_ATOMIC); } + /* turn throttle off */ + priv->dp_throttled = 0; + priv->dp_throttle_restart = 0; + spin_unlock_irqrestore(&priv->dp_port_lock, flags); if (ret) diff --git a/drivers/usb/serial/empeg.c b/drivers/usb/serial/empeg.c index 33c9e9cf9eb2..7dd0e3eadbe6 100644 --- a/drivers/usb/serial/empeg.c +++ b/drivers/usb/serial/empeg.c @@ -391,7 +391,7 @@ static void empeg_unthrottle(struct tty_struct *tty) dbg("%s - port %d", __func__, port->number); port->read_urb->dev = port->serial->dev; - result = usb_submit_urb(port->read_urb, GFP_ATOMIC); + result = usb_submit_urb(port->read_urb, GFP_KERNEL); if (result) dev_err(&port->dev, "%s - failed submitting read urb, error %d\n", diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c index 76a17f915eef..9c60d6d4908a 100644 --- a/drivers/usb/serial/ftdi_sio.c +++ b/drivers/usb/serial/ftdi_sio.c @@ -76,13 +76,7 @@ struct ftdi_private { unsigned long last_dtr_rts; /* saved modem control outputs */ wait_queue_head_t delta_msr_wait; /* Used for TIOCMIWAIT */ char prev_status, diff_status; /* Used for TIOCMIWAIT */ - __u8 rx_flags; /* receive state flags (throttling) */ - spinlock_t rx_lock; /* spinlock for receive state */ - struct delayed_work rx_work; struct usb_serial_port *port; - int rx_processed; - unsigned long rx_bytes; - __u16 interface; /* FT2232C, FT2232H or FT4232H port interface (0 for FT232/245) */ @@ -176,6 +170,9 @@ static struct usb_device_id id_table_combined [] = { { USB_DEVICE(FTDI_VID, FTDI_MICRO_CHAMELEON_PID) }, { USB_DEVICE(FTDI_VID, FTDI_RELAIS_PID) }, { USB_DEVICE(FTDI_VID, FTDI_OPENDCC_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_OPENDCC_SNIFFER_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_OPENDCC_THROTTLE_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_OPENDCC_GATEWAY_PID) }, { USB_DEVICE(INTERBIOMETRICS_VID, INTERBIOMETRICS_IOBOARD_PID) }, { USB_DEVICE(INTERBIOMETRICS_VID, INTERBIOMETRICS_MINI_IOBOARD_PID) }, { USB_DEVICE(FTDI_VID, FTDI_SPROG_II) }, @@ -694,6 +691,8 @@ static struct usb_device_id id_table_combined [] = { { USB_DEVICE(DE_VID, WHT_PID) }, { USB_DEVICE(ADI_VID, ADI_GNICE_PID), .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk }, + { USB_DEVICE(ADI_VID, ADI_GNICEPLUS_PID), + .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk }, { USB_DEVICE(JETI_VID, JETI_SPC1201_PID) }, { USB_DEVICE(MARVELL_VID, MARVELL_SHEEVAPLUG_PID), .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk }, @@ -702,6 +701,8 @@ static struct usb_device_id id_table_combined [] = { { USB_DEVICE(BAYER_VID, BAYER_CONTOUR_CABLE_PID) }, { USB_DEVICE(FTDI_VID, MARVELL_OPENRD_PID), .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk }, + { USB_DEVICE(FTDI_VID, HAMEG_HO820_PID) }, + { USB_DEVICE(FTDI_VID, HAMEG_HO870_PID) }, { }, /* Optional parameter entry */ { } /* Terminating entry */ }; @@ -730,10 +731,6 @@ static const char *ftdi_chip_name[] = { /* Constants for read urb and write urb */ #define BUFSZ 512 -/* rx_flags */ -#define THROTTLED 0x01 -#define ACTUALLY_THROTTLED 0x02 - /* Used for TIOCMIWAIT */ #define FTDI_STATUS_B0_MASK (FTDI_RS0_CTS | FTDI_RS0_DSR | FTDI_RS0_RI | FTDI_RS0_RLSD) #define FTDI_STATUS_B1_MASK (FTDI_RS_BI) @@ -756,7 +753,7 @@ static int ftdi_write_room(struct tty_struct *tty); static int ftdi_chars_in_buffer(struct tty_struct *tty); static void ftdi_write_bulk_callback(struct urb *urb); static void ftdi_read_bulk_callback(struct urb *urb); -static void ftdi_process_read(struct work_struct *work); +static void ftdi_process_read(struct usb_serial_port *port); static void ftdi_set_termios(struct tty_struct *tty, struct usb_serial_port *port, struct ktermios *old); static int ftdi_tiocmget(struct tty_struct *tty, struct file *file); @@ -1227,7 +1224,6 @@ static int set_serial_info(struct tty_struct *tty, (new_serial.flags & ASYNC_FLAGS)); priv->custom_divisor = new_serial.custom_divisor; - tty->low_latency = (priv->flags & ASYNC_LOW_LATENCY) ? 1 : 0; write_latency_timer(port); check_and_exit: @@ -1520,7 +1516,6 @@ static int ftdi_sio_port_probe(struct usb_serial_port *port) } kref_init(&priv->kref); - spin_lock_init(&priv->rx_lock); spin_lock_init(&priv->tx_lock); init_waitqueue_head(&priv->delta_msr_wait); /* This will push the characters through immediately rather @@ -1542,7 +1537,6 @@ static int ftdi_sio_port_probe(struct usb_serial_port *port) port->read_urb->transfer_buffer_length = BUFSZ; } - INIT_DELAYED_WORK(&priv->rx_work, ftdi_process_read); priv->port = port; /* Free port's existing write urb and transfer buffer. */ @@ -1679,6 +1673,26 @@ static int ftdi_sio_port_remove(struct usb_serial_port *port) return 0; } +static int ftdi_submit_read_urb(struct usb_serial_port *port, gfp_t mem_flags) +{ + struct urb *urb = port->read_urb; + struct usb_serial *serial = port->serial; + int result; + + usb_fill_bulk_urb(urb, serial->dev, + usb_rcvbulkpipe(serial->dev, + port->bulk_in_endpointAddress), + urb->transfer_buffer, + urb->transfer_buffer_length, + ftdi_read_bulk_callback, port); + result = usb_submit_urb(urb, mem_flags); + if (result) + dev_err(&port->dev, + "%s - failed submitting read urb, error %d\n", + __func__, result); + return result; +} + static int ftdi_open(struct tty_struct *tty, struct usb_serial_port *port) { /* ftdi_open */ struct usb_device *dev = port->serial->dev; @@ -1693,12 +1707,6 @@ static int ftdi_open(struct tty_struct *tty, struct usb_serial_port *port) spin_lock_irqsave(&priv->tx_lock, flags); priv->tx_bytes = 0; spin_unlock_irqrestore(&priv->tx_lock, flags); - spin_lock_irqsave(&priv->rx_lock, flags); - priv->rx_bytes = 0; - spin_unlock_irqrestore(&priv->rx_lock, flags); - - if (tty) - tty->low_latency = (priv->flags & ASYNC_LOW_LATENCY) ? 1 : 0; write_latency_timer(port); @@ -1718,23 +1726,14 @@ static int ftdi_open(struct tty_struct *tty, struct usb_serial_port *port) ftdi_set_termios(tty, port, tty->termios); /* Not throttled */ - spin_lock_irqsave(&priv->rx_lock, flags); - priv->rx_flags &= ~(THROTTLED | ACTUALLY_THROTTLED); - spin_unlock_irqrestore(&priv->rx_lock, flags); + spin_lock_irqsave(&port->lock, flags); + port->throttled = 0; + port->throttle_req = 0; + spin_unlock_irqrestore(&port->lock, flags); /* Start reading from the device */ - priv->rx_processed = 0; - usb_fill_bulk_urb(port->read_urb, dev, - usb_rcvbulkpipe(dev, port->bulk_in_endpointAddress), - port->read_urb->transfer_buffer, - port->read_urb->transfer_buffer_length, - ftdi_read_bulk_callback, port); - result = usb_submit_urb(port->read_urb, GFP_KERNEL); - if (result) - dev_err(&port->dev, - "%s - failed submitting read urb, error %d\n", - __func__, result); - else + result = ftdi_submit_read_urb(port, GFP_KERNEL); + if (!result) kref_get(&priv->kref); return result; @@ -1780,10 +1779,6 @@ static void ftdi_close(struct usb_serial_port *port) dbg("%s", __func__); - - /* cancel any scheduled reading */ - cancel_delayed_work_sync(&priv->rx_work); - /* shutdown our bulk read */ usb_kill_urb(port->read_urb); kref_put(&priv->kref, ftdi_sio_priv_release); @@ -2006,271 +2001,121 @@ static int ftdi_chars_in_buffer(struct tty_struct *tty) return buffered; } -static void ftdi_read_bulk_callback(struct urb *urb) +static int ftdi_process_packet(struct tty_struct *tty, + struct usb_serial_port *port, struct ftdi_private *priv, + char *packet, int len) { - struct usb_serial_port *port = urb->context; - struct tty_struct *tty; - struct ftdi_private *priv; - unsigned long countread; - unsigned long flags; - int status = urb->status; - - if (urb->number_of_packets > 0) { - dev_err(&port->dev, "%s transfer_buffer_length %d " - "actual_length %d number of packets %d\n", __func__, - urb->transfer_buffer_length, - urb->actual_length, urb->number_of_packets); - dev_err(&port->dev, "%s transfer_flags %x\n", __func__, - urb->transfer_flags); - } + int i; + char status; + char flag; + char *ch; dbg("%s - port %d", __func__, port->number); - if (port->port.count <= 0) - return; - - tty = tty_port_tty_get(&port->port); - if (!tty) { - dbg("%s - bad tty pointer - exiting", __func__); - return; + if (len < 2) { + dbg("malformed packet"); + return 0; } - priv = usb_get_serial_port_data(port); - if (!priv) { - dbg("%s - bad port private data pointer - exiting", __func__); - goto out; + /* Compare new line status to the old one, signal if different/ + N.B. packet may be processed more than once, but differences + are only processed once. */ + status = packet[0] & FTDI_STATUS_B0_MASK; + if (status != priv->prev_status) { + priv->diff_status |= status ^ priv->prev_status; + wake_up_interruptible(&priv->delta_msr_wait); + priv->prev_status = status; } - if (urb != port->read_urb) - dev_err(&port->dev, "%s - Not my urb!\n", __func__); - - if (status) { - /* This will happen at close every time so it is a dbg not an - err */ - dbg("(this is ok on close) nonzero read bulk status received: %d", status); - goto out; + /* + * Although the device uses a bitmask and hence can have multiple + * errors on a packet - the order here sets the priority the error is + * returned to the tty layer. + */ + flag = TTY_NORMAL; + if (packet[1] & FTDI_RS_OE) { + flag = TTY_OVERRUN; + dbg("OVERRRUN error"); + } + if (packet[1] & FTDI_RS_BI) { + flag = TTY_BREAK; + dbg("BREAK received"); + usb_serial_handle_break(port); + } + if (packet[1] & FTDI_RS_PE) { + flag = TTY_PARITY; + dbg("PARITY error"); + } + if (packet[1] & FTDI_RS_FE) { + flag = TTY_FRAME; + dbg("FRAMING error"); } - /* count data bytes, but not status bytes */ - countread = urb->actual_length; - countread -= 2 * DIV_ROUND_UP(countread, priv->max_packet_size); - spin_lock_irqsave(&priv->rx_lock, flags); - priv->rx_bytes += countread; - spin_unlock_irqrestore(&priv->rx_lock, flags); - - ftdi_process_read(&priv->rx_work.work); -out: - tty_kref_put(tty); -} /* ftdi_read_bulk_callback */ - + len -= 2; + if (!len) + return 0; /* status only */ + ch = packet + 2; + + if (!(port->console && port->sysrq) && flag == TTY_NORMAL) + tty_insert_flip_string(tty, ch, len); + else { + for (i = 0; i < len; i++, ch++) { + if (!usb_serial_handle_sysrq_char(tty, port, *ch)) + tty_insert_flip_char(tty, *ch, flag); + } + } + return len; +} -static void ftdi_process_read(struct work_struct *work) -{ /* ftdi_process_read */ - struct ftdi_private *priv = - container_of(work, struct ftdi_private, rx_work.work); - struct usb_serial_port *port = priv->port; - struct urb *urb; +static void ftdi_process_read(struct usb_serial_port *port) +{ + struct urb *urb = port->read_urb; struct tty_struct *tty; - char error_flag; - unsigned char *data; - + struct ftdi_private *priv = usb_get_serial_port_data(port); + char *data = (char *)urb->transfer_buffer; int i; - int result; - int need_flip; - int packet_offset; - unsigned long flags; - - dbg("%s - port %d", __func__, port->number); - - if (port->port.count <= 0) - return; + int len; + int count = 0; tty = tty_port_tty_get(&port->port); - if (!tty) { - dbg("%s - bad tty pointer - exiting", __func__); + if (!tty) return; - } - priv = usb_get_serial_port_data(port); - if (!priv) { - dbg("%s - bad port private data pointer - exiting", __func__); - goto out; - } - - urb = port->read_urb; - if (!urb) { - dbg("%s - bad read_urb pointer - exiting", __func__); - goto out; + for (i = 0; i < urb->actual_length; i += priv->max_packet_size) { + len = min_t(int, urb->actual_length - i, priv->max_packet_size); + count += ftdi_process_packet(tty, port, priv, &data[i], len); } - data = urb->transfer_buffer; - - if (priv->rx_processed) { - dbg("%s - already processed: %d bytes, %d remain", __func__, - priv->rx_processed, - urb->actual_length - priv->rx_processed); - } else { - /* The first two bytes of every read packet are status */ - if (urb->actual_length > 2) - usb_serial_debug_data(debug, &port->dev, __func__, - urb->actual_length, data); - else - dbg("Status only: %03oo %03oo", data[0], data[1]); - } - - - /* TO DO -- check for hung up line and handle appropriately: */ - /* send hangup */ - /* See acm.c - you do a tty_hangup - eg tty_hangup(tty) */ - /* if CD is dropped and the line is not CLOCAL then we should hangup */ - - need_flip = 0; - for (packet_offset = priv->rx_processed; - packet_offset < urb->actual_length; packet_offset += priv->max_packet_size) { - int length; - - /* Compare new line status to the old one, signal if different/ - N.B. packet may be processed more than once, but differences - are only processed once. */ - char new_status = data[packet_offset + 0] & - FTDI_STATUS_B0_MASK; - if (new_status != priv->prev_status) { - priv->diff_status |= - new_status ^ priv->prev_status; - wake_up_interruptible(&priv->delta_msr_wait); - priv->prev_status = new_status; - } - - length = min_t(u32, priv->max_packet_size, urb->actual_length-packet_offset)-2; - if (length < 0) { - dev_err(&port->dev, "%s - bad packet length: %d\n", - __func__, length+2); - length = 0; - } - - if (priv->rx_flags & THROTTLED) { - dbg("%s - throttled", __func__); - break; - } - if (tty_buffer_request_room(tty, length) < length) { - /* break out & wait for throttling/unthrottling to - happen */ - dbg("%s - receive room low", __func__); - break; - } - - /* Handle errors and break */ - error_flag = TTY_NORMAL; - /* Although the device uses a bitmask and hence can have - multiple errors on a packet - the order here sets the - priority the error is returned to the tty layer */ - - if (data[packet_offset+1] & FTDI_RS_OE) { - error_flag = TTY_OVERRUN; - dbg("OVERRRUN error"); - } - if (data[packet_offset+1] & FTDI_RS_BI) { - error_flag = TTY_BREAK; - dbg("BREAK received"); - usb_serial_handle_break(port); - } - if (data[packet_offset+1] & FTDI_RS_PE) { - error_flag = TTY_PARITY; - dbg("PARITY error"); - } - if (data[packet_offset+1] & FTDI_RS_FE) { - error_flag = TTY_FRAME; - dbg("FRAMING error"); - } - if (length > 0) { - for (i = 2; i < length+2; i++) { - /* Note that the error flag is duplicated for - every character received since we don't know - which character it applied to */ - if (!usb_serial_handle_sysrq_char(tty, port, - data[packet_offset + i])) - tty_insert_flip_char(tty, - data[packet_offset + i], - error_flag); - } - need_flip = 1; - } - -#ifdef NOT_CORRECT_BUT_KEEPING_IT_FOR_NOW - /* if a parity error is detected you get status packets forever - until a character is sent without a parity error. - This doesn't work well since the application receives a - never ending stream of bad data - even though new data - hasn't been sent. Therefore I (bill) have taken this out. - However - this might make sense for framing errors and so on - so I am leaving the code in for now. - */ - else { - if (error_flag != TTY_NORMAL) { - dbg("error_flag is not normal"); - /* In this case it is just status - if that is - an error send a bad character */ - if (tty->flip.count >= TTY_FLIPBUF_SIZE) - tty_flip_buffer_push(tty); - tty_insert_flip_char(tty, 0xff, error_flag); - need_flip = 1; - } - } -#endif - } /* "for(packet_offset=0..." */ - - /* Low latency */ - if (need_flip) + if (count) tty_flip_buffer_push(tty); + tty_kref_put(tty); +} - if (packet_offset < urb->actual_length) { - /* not completely processed - record progress */ - priv->rx_processed = packet_offset; - dbg("%s - incomplete, %d bytes processed, %d remain", - __func__, packet_offset, - urb->actual_length - packet_offset); - /* check if we were throttled while processing */ - spin_lock_irqsave(&priv->rx_lock, flags); - if (priv->rx_flags & THROTTLED) { - priv->rx_flags |= ACTUALLY_THROTTLED; - spin_unlock_irqrestore(&priv->rx_lock, flags); - dbg("%s - deferring remainder until unthrottled", - __func__); - goto out; - } - spin_unlock_irqrestore(&priv->rx_lock, flags); - /* if the port is closed stop trying to read */ - if (port->port.count > 0) - /* delay processing of remainder */ - schedule_delayed_work(&priv->rx_work, 1); - else - dbg("%s - port is closed", __func__); - goto out; - } - - /* urb is completely processed */ - priv->rx_processed = 0; +static void ftdi_read_bulk_callback(struct urb *urb) +{ + struct usb_serial_port *port = urb->context; + unsigned long flags; - /* if the port is closed stop trying to read */ - if (port->port.count > 0) { - /* Continue trying to always read */ - usb_fill_bulk_urb(port->read_urb, port->serial->dev, - usb_rcvbulkpipe(port->serial->dev, - port->bulk_in_endpointAddress), - port->read_urb->transfer_buffer, - port->read_urb->transfer_buffer_length, - ftdi_read_bulk_callback, port); + dbg("%s - port %d", __func__, port->number); - result = usb_submit_urb(port->read_urb, GFP_ATOMIC); - if (result) - dev_err(&port->dev, - "%s - failed resubmitting read urb, error %d\n", - __func__, result); + if (urb->status) { + dbg("%s - nonzero read bulk status received: %d", + __func__, urb->status); + return; } -out: - tty_kref_put(tty); -} /* ftdi_process_read */ + usb_serial_debug_data(debug, &port->dev, __func__, + urb->actual_length, urb->transfer_buffer); + ftdi_process_read(port); + + spin_lock_irqsave(&port->lock, flags); + port->throttled = port->throttle_req; + if (!port->throttled) { + spin_unlock_irqrestore(&port->lock, flags); + ftdi_submit_read_urb(port, GFP_ATOMIC); + } else + spin_unlock_irqrestore(&port->lock, flags); +} static void ftdi_break_ctl(struct tty_struct *tty, int break_state) { @@ -2602,33 +2447,31 @@ static int ftdi_ioctl(struct tty_struct *tty, struct file *file, static void ftdi_throttle(struct tty_struct *tty) { struct usb_serial_port *port = tty->driver_data; - struct ftdi_private *priv = usb_get_serial_port_data(port); unsigned long flags; dbg("%s - port %d", __func__, port->number); - spin_lock_irqsave(&priv->rx_lock, flags); - priv->rx_flags |= THROTTLED; - spin_unlock_irqrestore(&priv->rx_lock, flags); + spin_lock_irqsave(&port->lock, flags); + port->throttle_req = 1; + spin_unlock_irqrestore(&port->lock, flags); } - -static void ftdi_unthrottle(struct tty_struct *tty) +void ftdi_unthrottle(struct tty_struct *tty) { struct usb_serial_port *port = tty->driver_data; - struct ftdi_private *priv = usb_get_serial_port_data(port); - int actually_throttled; + int was_throttled; unsigned long flags; dbg("%s - port %d", __func__, port->number); - spin_lock_irqsave(&priv->rx_lock, flags); - actually_throttled = priv->rx_flags & ACTUALLY_THROTTLED; - priv->rx_flags &= ~(THROTTLED | ACTUALLY_THROTTLED); - spin_unlock_irqrestore(&priv->rx_lock, flags); + spin_lock_irqsave(&port->lock, flags); + was_throttled = port->throttled; + port->throttled = port->throttle_req = 0; + spin_unlock_irqrestore(&port->lock, flags); - if (actually_throttled) - schedule_delayed_work(&priv->rx_work, 0); + /* Resubmit urb if throttled and open. */ + if (was_throttled && test_bit(ASYNCB_INITIALIZED, &port->port.flags)) + ftdi_submit_read_urb(port, GFP_KERNEL); } static int __init ftdi_init(void) diff --git a/drivers/usb/serial/ftdi_sio.h b/drivers/usb/serial/ftdi_sio.h index 8c92b88166ae..6f31e0d71898 100644 --- a/drivers/usb/serial/ftdi_sio.h +++ b/drivers/usb/serial/ftdi_sio.h @@ -81,6 +81,9 @@ /* OpenDCC (www.opendcc.de) product id */ #define FTDI_OPENDCC_PID 0xBFD8 +#define FTDI_OPENDCC_SNIFFER_PID 0xBFD9 +#define FTDI_OPENDCC_THROTTLE_PID 0xBFDA +#define FTDI_OPENDCC_GATEWAY_PID 0xBFDB /* Sprog II (Andrew Crosland's SprogII DCC interface) */ #define FTDI_SPROG_II 0xF0C8 @@ -930,6 +933,7 @@ */ #define ADI_VID 0x0456 #define ADI_GNICE_PID 0xF000 +#define ADI_GNICEPLUS_PID 0xF001 /* * JETI SPECTROMETER SPECBOS 1201 @@ -968,6 +972,12 @@ #define MARVELL_OPENRD_PID 0x9e90 /* + * Hameg HO820 and HO870 interface (using VID 0x0403) + */ +#define HAMEG_HO820_PID 0xed74 +#define HAMEG_HO870_PID 0xed71 + +/* * BmRequestType: 1100 0000b * bRequest: FTDI_E2_READ * wValue: 0 diff --git a/drivers/usb/serial/garmin_gps.c b/drivers/usb/serial/garmin_gps.c index 20432d345529..5ac900e5a50e 100644 --- a/drivers/usb/serial/garmin_gps.c +++ b/drivers/usb/serial/garmin_gps.c @@ -1390,14 +1390,13 @@ static void garmin_throttle(struct tty_struct *tty) { struct usb_serial_port *port = tty->driver_data; struct garmin_data *garmin_data_p = usb_get_serial_port_data(port); - unsigned long flags; dbg("%s - port %d", __func__, port->number); /* set flag, data received will be put into a queue for later processing */ - spin_lock_irqsave(&garmin_data_p->lock, flags); + spin_lock_irq(&garmin_data_p->lock); garmin_data_p->flags |= FLAGS_QUEUING|FLAGS_THROTTLED; - spin_unlock_irqrestore(&garmin_data_p->lock, flags); + spin_unlock_irq(&garmin_data_p->lock); } @@ -1405,13 +1404,12 @@ static void garmin_unthrottle(struct tty_struct *tty) { struct usb_serial_port *port = tty->driver_data; struct garmin_data *garmin_data_p = usb_get_serial_port_data(port); - unsigned long flags; int status; dbg("%s - port %d", __func__, port->number); - spin_lock_irqsave(&garmin_data_p->lock, flags); + spin_lock_irq(&garmin_data_p->lock); garmin_data_p->flags &= ~FLAGS_THROTTLED; - spin_unlock_irqrestore(&garmin_data_p->lock, flags); + spin_unlock_irq(&garmin_data_p->lock); /* in native mode send queued data to tty, in serial mode nothing needs to be done here */ @@ -1419,7 +1417,7 @@ static void garmin_unthrottle(struct tty_struct *tty) garmin_flush_queue(garmin_data_p); if (0 != (garmin_data_p->flags & FLAGS_BULK_IN_ACTIVE)) { - status = usb_submit_urb(port->read_urb, GFP_ATOMIC); + status = usb_submit_urb(port->read_urb, GFP_KERNEL); if (status) dev_err(&port->dev, "%s - failed resubmitting read urb, error %d\n", diff --git a/drivers/usb/serial/generic.c b/drivers/usb/serial/generic.c index d9398e9f30ce..bbe005cefcfb 100644 --- a/drivers/usb/serial/generic.c +++ b/drivers/usb/serial/generic.c @@ -19,7 +19,7 @@ #include <linux/usb.h> #include <linux/usb/serial.h> #include <linux/uaccess.h> - +#include <linux/kfifo.h> static int debug; @@ -166,24 +166,6 @@ static void generic_cleanup(struct usb_serial_port *port) } } -int usb_serial_generic_resume(struct usb_serial *serial) -{ - struct usb_serial_port *port; - int i, c = 0, r; - - for (i = 0; i < serial->num_ports; i++) { - port = serial->port[i]; - if (port->port.count && port->read_urb) { - r = usb_submit_urb(port->read_urb, GFP_NOIO); - if (r < 0) - c++; - } - } - - return c ? -EIO : 0; -} -EXPORT_SYMBOL_GPL(usb_serial_generic_resume); - void usb_serial_generic_close(struct usb_serial_port *port) { dbg("%s - port %d", __func__, port->number); @@ -272,12 +254,81 @@ error_no_buffer: return bwrite; } +/** + * usb_serial_generic_write_start - kick off an URB write + * @port: Pointer to the &struct usb_serial_port data + * + * Returns the number of bytes queued on success. This will be zero if there + * was nothing to send. Otherwise, it returns a negative errno value + */ +static int usb_serial_generic_write_start(struct usb_serial_port *port) +{ + struct usb_serial *serial = port->serial; + unsigned char *data; + int result; + int count; + unsigned long flags; + bool start_io; + + /* Atomically determine whether we can and need to start a USB + * operation. */ + spin_lock_irqsave(&port->lock, flags); + if (port->write_urb_busy) + start_io = false; + else { + start_io = (__kfifo_len(port->write_fifo) != 0); + port->write_urb_busy = start_io; + } + spin_unlock_irqrestore(&port->lock, flags); + + if (!start_io) + return 0; + + data = port->write_urb->transfer_buffer; + count = kfifo_get(port->write_fifo, data, port->bulk_out_size); + usb_serial_debug_data(debug, &port->dev, __func__, count, data); + + /* set up our urb */ + usb_fill_bulk_urb(port->write_urb, serial->dev, + usb_sndbulkpipe(serial->dev, + port->bulk_out_endpointAddress), + port->write_urb->transfer_buffer, count, + ((serial->type->write_bulk_callback) ? + serial->type->write_bulk_callback : + usb_serial_generic_write_bulk_callback), + port); + + /* send the data out the bulk port */ + result = usb_submit_urb(port->write_urb, GFP_ATOMIC); + if (result) { + dev_err(&port->dev, + "%s - failed submitting write urb, error %d\n", + __func__, result); + /* don't have to grab the lock here, as we will + retry if != 0 */ + port->write_urb_busy = 0; + } else + result = count; + + return result; +} + +/** + * usb_serial_generic_write - generic write function for serial USB devices + * @tty: Pointer to &struct tty_struct for the device + * @port: Pointer to the &usb_serial_port structure for the device + * @buf: Pointer to the data to write + * @count: Number of bytes to write + * + * Returns the number of characters actually written, which may be anything + * from zero to @count. If an error occurs, it returns the negative errno + * value. + */ int usb_serial_generic_write(struct tty_struct *tty, struct usb_serial_port *port, const unsigned char *buf, int count) { struct usb_serial *serial = port->serial; int result; - unsigned char *data; dbg("%s - port %d", __func__, port->number); @@ -287,57 +338,20 @@ int usb_serial_generic_write(struct tty_struct *tty, } /* only do something if we have a bulk out endpoint */ - if (serial->num_bulk_out) { - unsigned long flags; - - if (serial->type->max_in_flight_urbs) - return usb_serial_multi_urb_write(tty, port, - buf, count); - - spin_lock_irqsave(&port->lock, flags); - if (port->write_urb_busy) { - spin_unlock_irqrestore(&port->lock, flags); - dbg("%s - already writing", __func__); - return 0; - } - port->write_urb_busy = 1; - spin_unlock_irqrestore(&port->lock, flags); - - count = (count > port->bulk_out_size) ? - port->bulk_out_size : count; - - memcpy(port->write_urb->transfer_buffer, buf, count); - data = port->write_urb->transfer_buffer; - usb_serial_debug_data(debug, &port->dev, __func__, count, data); + if (!serial->num_bulk_out) + return 0; - /* set up our urb */ - usb_fill_bulk_urb(port->write_urb, serial->dev, - usb_sndbulkpipe(serial->dev, - port->bulk_out_endpointAddress), - port->write_urb->transfer_buffer, count, - ((serial->type->write_bulk_callback) ? - serial->type->write_bulk_callback : - usb_serial_generic_write_bulk_callback), - port); + if (serial->type->max_in_flight_urbs) + return usb_serial_multi_urb_write(tty, port, + buf, count); - /* send the data out the bulk port */ - port->write_urb_busy = 1; - result = usb_submit_urb(port->write_urb, GFP_ATOMIC); - if (result) { - dev_err(&port->dev, - "%s - failed submitting write urb, error %d\n", - __func__, result); - /* don't have to grab the lock here, as we will - retry if != 0 */ - port->write_urb_busy = 0; - } else - result = count; + count = kfifo_put(port->write_fifo, buf, count); + result = usb_serial_generic_write_start(port); - return result; - } + if (result >= 0) + result = count; - /* no bulk out, so return 0 bytes written */ - return 0; + return result; } EXPORT_SYMBOL_GPL(usb_serial_generic_write); @@ -355,9 +369,8 @@ int usb_serial_generic_write_room(struct tty_struct *tty) room = port->bulk_out_size * (serial->type->max_in_flight_urbs - port->urbs_in_flight); - } else if (serial->num_bulk_out && !(port->write_urb_busy)) { - room = port->bulk_out_size; - } + } else if (serial->num_bulk_out) + room = port->write_fifo->size - __kfifo_len(port->write_fifo); spin_unlock_irqrestore(&port->lock, flags); dbg("%s - returns %d", __func__, room); @@ -377,11 +390,8 @@ int usb_serial_generic_chars_in_buffer(struct tty_struct *tty) spin_lock_irqsave(&port->lock, flags); chars = port->tx_bytes_flight; spin_unlock_irqrestore(&port->lock, flags); - } else if (serial->num_bulk_out) { - /* FIXME: Locking */ - if (port->write_urb_busy) - chars = port->write_urb->transfer_buffer_length; - } + } else if (serial->num_bulk_out) + chars = kfifo_len(port->write_fifo); dbg("%s - returns %d", __func__, chars); return chars; @@ -485,16 +495,23 @@ void usb_serial_generic_write_bulk_callback(struct urb *urb) if (port->urbs_in_flight < 0) port->urbs_in_flight = 0; spin_unlock_irqrestore(&port->lock, flags); + + if (status) { + dbg("%s - nonzero multi-urb write bulk status " + "received: %d", __func__, status); + return; + } } else { - /* Handle the case for single urb mode */ port->write_urb_busy = 0; - } - if (status) { - dbg("%s - nonzero write bulk status received: %d", - __func__, status); - return; + if (status) { + dbg("%s - nonzero multi-urb write bulk status " + "received: %d", __func__, status); + kfifo_reset(port->write_fifo); + } else + usb_serial_generic_write_start(port); } + usb_serial_port_softint(port); } EXPORT_SYMBOL_GPL(usb_serial_generic_write_bulk_callback); @@ -529,7 +546,7 @@ void usb_serial_generic_unthrottle(struct tty_struct *tty) if (was_throttled) { /* Resume reading from device */ - usb_serial_generic_resubmit_read_urb(port, GFP_KERNEL); + flush_and_resubmit_read_urb(port); } } @@ -559,6 +576,33 @@ int usb_serial_handle_break(struct usb_serial_port *port) } EXPORT_SYMBOL_GPL(usb_serial_handle_break); +int usb_serial_generic_resume(struct usb_serial *serial) +{ + struct usb_serial_port *port; + int i, c = 0, r; + + for (i = 0; i < serial->num_ports; i++) { + port = serial->port[i]; + if (!port->port.count) + continue; + + if (port->read_urb) { + r = usb_submit_urb(port->read_urb, GFP_NOIO); + if (r < 0) + c++; + } + + if (port->write_urb) { + r = usb_serial_generic_write_start(port); + if (r < 0) + c++; + } + } + + return c ? -EIO : 0; +} +EXPORT_SYMBOL_GPL(usb_serial_generic_resume); + void usb_serial_generic_disconnect(struct usb_serial *serial) { int i; diff --git a/drivers/usb/serial/ipaq.c b/drivers/usb/serial/ipaq.c index 24fcc64b837d..d6231c38813e 100644 --- a/drivers/usb/serial/ipaq.c +++ b/drivers/usb/serial/ipaq.c @@ -966,6 +966,15 @@ static int ipaq_calc_num_ports(struct usb_serial *serial) static int ipaq_startup(struct usb_serial *serial) { dbg("%s", __func__); + + /* Some of the devices in ipaq_id_table[] are composite, and we + * shouldn't bind to all the interfaces. This test will rule out + * some obviously invalid possibilities. + */ + if (serial->num_bulk_in < serial->num_ports || + serial->num_bulk_out < serial->num_ports) + return -ENODEV; + if (serial->dev->actconfig->desc.bConfigurationValue != 1) { /* * FIXME: HP iPaq rx3715, possibly others, have 1 config that diff --git a/drivers/usb/serial/iuu_phoenix.c b/drivers/usb/serial/iuu_phoenix.c index 6138c1cda35f..e6e02b178d2b 100644 --- a/drivers/usb/serial/iuu_phoenix.c +++ b/drivers/usb/serial/iuu_phoenix.c @@ -40,7 +40,7 @@ static int debug; /* * Version Information */ -#define DRIVER_VERSION "v0.10" +#define DRIVER_VERSION "v0.11" #define DRIVER_DESC "Infinity USB Unlimited Phoenix driver" static struct usb_device_id id_table[] = { @@ -64,6 +64,7 @@ static int cdmode = 1; static int iuu_cardin; static int iuu_cardout; static int xmas; +static int vcc_default = 5; static void read_rxcmd_callback(struct urb *urb); @@ -79,6 +80,7 @@ struct iuu_private { u8 *buf; /* used for initialize speed */ u8 *dbgbuf; /* debug buffer */ u8 len; + int vcc; /* vcc (either 3 or 5 V) */ }; @@ -114,6 +116,7 @@ static int iuu_startup(struct usb_serial *serial) kfree(priv); return -ENOMEM; } + priv->vcc = vcc_default; spin_lock_init(&priv->lock); init_waitqueue_head(&priv->delta_msr_wait); usb_set_serial_port_data(serial->port[0], priv); @@ -1009,11 +1012,7 @@ static void iuu_close(struct usb_serial_port *port) usb_kill_urb(port->write_urb); usb_kill_urb(port->read_urb); usb_kill_urb(port->interrupt_in_urb); - msleep(1000); - /* wait one second to free all buffers */ iuu_led(port, 0, 0, 0xF000, 0xFF); - msleep(1000); - usb_reset_device(port->serial->dev); } } @@ -1182,6 +1181,95 @@ static int iuu_open(struct tty_struct *tty, struct usb_serial_port *port) return result; } +/* how to change VCC */ +static int iuu_vcc_set(struct usb_serial_port *port, unsigned int vcc) +{ + int status; + u8 *buf; + + buf = kmalloc(5, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + dbg("%s - enter", __func__); + + buf[0] = IUU_SET_VCC; + buf[1] = vcc & 0xFF; + buf[2] = (vcc >> 8) & 0xFF; + buf[3] = (vcc >> 16) & 0xFF; + buf[4] = (vcc >> 24) & 0xFF; + + status = bulk_immediate(port, buf, 5); + kfree(buf); + + if (status != IUU_OPERATION_OK) + dbg("%s - vcc error status = %2x", __func__, status); + else + dbg("%s - vcc OK !", __func__); + + return status; +} + +/* + * Sysfs Attributes + */ + +static ssize_t show_vcc_mode(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct usb_serial_port *port = to_usb_serial_port(dev); + struct iuu_private *priv = usb_get_serial_port_data(port); + + return sprintf(buf, "%d\n", priv->vcc); +} + +static ssize_t store_vcc_mode(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct usb_serial_port *port = to_usb_serial_port(dev); + struct iuu_private *priv = usb_get_serial_port_data(port); + unsigned long v; + + if (strict_strtoul(buf, 10, &v)) { + dev_err(dev, "%s - vcc_mode: %s is not a unsigned long\n", + __func__, buf); + goto fail_store_vcc_mode; + } + + dbg("%s: setting vcc_mode = %ld", __func__, v); + + if ((v != 3) && (v != 5)) { + dev_err(dev, "%s - vcc_mode %ld is invalid\n", __func__, v); + } else { + iuu_vcc_set(port, v); + priv->vcc = v; + } +fail_store_vcc_mode: + return count; +} + +static DEVICE_ATTR(vcc_mode, S_IRUSR | S_IWUSR, show_vcc_mode, + store_vcc_mode); + +static int iuu_create_sysfs_attrs(struct usb_serial_port *port) +{ + dbg("%s", __func__); + + return device_create_file(&port->dev, &dev_attr_vcc_mode); +} + +static int iuu_remove_sysfs_attrs(struct usb_serial_port *port) +{ + dbg("%s", __func__); + + device_remove_file(&port->dev, &dev_attr_vcc_mode); + return 0; +} + +/* + * End Sysfs Attributes + */ + static struct usb_serial_driver iuu_device = { .driver = { .owner = THIS_MODULE, @@ -1189,6 +1277,8 @@ static struct usb_serial_driver iuu_device = { }, .id_table = id_table, .num_ports = 1, + .port_probe = iuu_create_sysfs_attrs, + .port_remove = iuu_remove_sysfs_attrs, .open = iuu_open, .close = iuu_close, .write = iuu_uart_write, @@ -1238,14 +1328,19 @@ module_param(debug, bool, S_IRUGO | S_IWUSR); MODULE_PARM_DESC(debug, "Debug enabled or not"); module_param(xmas, bool, S_IRUGO | S_IWUSR); -MODULE_PARM_DESC(xmas, "xmas color enabled or not"); +MODULE_PARM_DESC(xmas, "Xmas colors enabled or not"); module_param(boost, int, S_IRUGO | S_IWUSR); -MODULE_PARM_DESC(boost, "overclock boost percent 100 to 500"); +MODULE_PARM_DESC(boost, "Card overclock boost (in percent 100-500)"); module_param(clockmode, int, S_IRUGO | S_IWUSR); -MODULE_PARM_DESC(clockmode, "1=3Mhz579,2=3Mhz680,3=6Mhz"); +MODULE_PARM_DESC(clockmode, "Card clock mode (1=3.579 MHz, 2=3.680 MHz, " + "3=6 Mhz)"); module_param(cdmode, int, S_IRUGO | S_IWUSR); -MODULE_PARM_DESC(cdmode, "Card detect mode 0=none, 1=CD, 2=!CD, 3=DSR, " - "4=!DSR, 5=CTS, 6=!CTS, 7=RING, 8=!RING"); +MODULE_PARM_DESC(cdmode, "Card detect mode (0=none, 1=CD, 2=!CD, 3=DSR, " + "4=!DSR, 5=CTS, 6=!CTS, 7=RING, 8=!RING)"); + +module_param(vcc_default, int, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(vcc_default, "Set default VCC (either 3 for 3.3V or 5 " + "for 5V). Default to 5."); diff --git a/drivers/usb/serial/keyspan_pda.c b/drivers/usb/serial/keyspan_pda.c index 257c16cc6b2a..1296a097f5c3 100644 --- a/drivers/usb/serial/keyspan_pda.c +++ b/drivers/usb/serial/keyspan_pda.c @@ -290,7 +290,7 @@ static void keyspan_pda_rx_unthrottle(struct tty_struct *tty) /* just restart the receive interrupt URB */ dbg("keyspan_pda_rx_unthrottle port %d", port->number); port->interrupt_in_urb->dev = port->serial->dev; - if (usb_submit_urb(port->interrupt_in_urb, GFP_ATOMIC)) + if (usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL)) dbg(" usb_submit_urb(read urb) failed"); return; } diff --git a/drivers/usb/serial/kl5kusb105.c b/drivers/usb/serial/kl5kusb105.c index f7373371b137..3a7873806f46 100644 --- a/drivers/usb/serial/kl5kusb105.c +++ b/drivers/usb/serial/kl5kusb105.c @@ -951,7 +951,7 @@ static void klsi_105_unthrottle(struct tty_struct *tty) dbg("%s - port %d", __func__, port->number); port->read_urb->dev = port->serial->dev; - result = usb_submit_urb(port->read_urb, GFP_ATOMIC); + result = usb_submit_urb(port->read_urb, GFP_KERNEL); if (result) dev_err(&port->dev, "%s - failed submitting read urb, error %d\n", diff --git a/drivers/usb/serial/mct_u232.c b/drivers/usb/serial/mct_u232.c index ad4998bbf16f..cd009cb280a5 100644 --- a/drivers/usb/serial/mct_u232.c +++ b/drivers/usb/serial/mct_u232.c @@ -777,20 +777,19 @@ static void mct_u232_throttle(struct tty_struct *tty) { struct usb_serial_port *port = tty->driver_data; struct mct_u232_private *priv = usb_get_serial_port_data(port); - unsigned long flags; unsigned int control_state; dbg("%s - port %d", __func__, port->number); - spin_lock_irqsave(&priv->lock, flags); + spin_lock_irq(&priv->lock); priv->rx_flags |= THROTTLED; if (C_CRTSCTS(tty)) { priv->control_state &= ~TIOCM_RTS; control_state = priv->control_state; - spin_unlock_irqrestore(&priv->lock, flags); + spin_unlock_irq(&priv->lock); (void) mct_u232_set_modem_ctrl(port->serial, control_state); } else { - spin_unlock_irqrestore(&priv->lock, flags); + spin_unlock_irq(&priv->lock); } } @@ -799,20 +798,19 @@ static void mct_u232_unthrottle(struct tty_struct *tty) { struct usb_serial_port *port = tty->driver_data; struct mct_u232_private *priv = usb_get_serial_port_data(port); - unsigned long flags; unsigned int control_state; dbg("%s - port %d", __func__, port->number); - spin_lock_irqsave(&priv->lock, flags); + spin_lock_irq(&priv->lock); if ((priv->rx_flags & THROTTLED) && C_CRTSCTS(tty)) { priv->rx_flags &= ~THROTTLED; priv->control_state |= TIOCM_RTS; control_state = priv->control_state; - spin_unlock_irqrestore(&priv->lock, flags); + spin_unlock_irq(&priv->lock); (void) mct_u232_set_modem_ctrl(port->serial, control_state); } else { - spin_unlock_irqrestore(&priv->lock, flags); + spin_unlock_irq(&priv->lock); } } diff --git a/drivers/usb/serial/moto_modem.c b/drivers/usb/serial/moto_modem.c index b66b71ccd12b..99bd00f5188a 100644 --- a/drivers/usb/serial/moto_modem.c +++ b/drivers/usb/serial/moto_modem.c @@ -8,7 +8,7 @@ * published by the Free Software Foundation. * * {sigh} - * Mororola should be using the CDC ACM USB spec, but instead + * Motorola should be using the CDC ACM USB spec, but instead * they try to just "do their own thing"... This driver should handle a * few phones in which a basic "dumb serial connection" is needed to be * able to get a connection through to them. diff --git a/drivers/usb/serial/opticon.c b/drivers/usb/serial/opticon.c index 1085a577c5c1..80f59b6350cb 100644 --- a/drivers/usb/serial/opticon.c +++ b/drivers/usb/serial/opticon.c @@ -314,21 +314,24 @@ static void opticon_unthrottle(struct tty_struct *tty) struct usb_serial_port *port = tty->driver_data; struct opticon_private *priv = usb_get_serial_data(port->serial); unsigned long flags; - int result; + int result, was_throttled; dbg("%s - port %d", __func__, port->number); spin_lock_irqsave(&priv->lock, flags); priv->throttled = false; + was_throttled = priv->actually_throttled; priv->actually_throttled = false; spin_unlock_irqrestore(&priv->lock, flags); priv->bulk_read_urb->dev = port->serial->dev; - result = usb_submit_urb(priv->bulk_read_urb, GFP_ATOMIC); - if (result) - dev_err(&port->dev, - "%s - failed submitting read urb, error %d\n", + if (was_throttled) { + result = usb_submit_urb(priv->bulk_read_urb, GFP_ATOMIC); + if (result) + dev_err(&port->dev, + "%s - failed submitting read urb, error %d\n", __func__, result); + } } static int opticon_tiocmget(struct tty_struct *tty, struct file *file) diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c index fe47051dbef2..319aaf9725b3 100644 --- a/drivers/usb/serial/option.c +++ b/drivers/usb/serial/option.c @@ -165,6 +165,7 @@ static int option_resume(struct usb_serial *serial); #define HUAWEI_PRODUCT_E143D 0x143D #define HUAWEI_PRODUCT_E143E 0x143E #define HUAWEI_PRODUCT_E143F 0x143F +#define HUAWEI_PRODUCT_E14AC 0x14AC #define QUANTA_VENDOR_ID 0x0408 #define QUANTA_PRODUCT_Q101 0xEA02 @@ -291,6 +292,7 @@ static int option_resume(struct usb_serial *serial); #define TELIT_VENDOR_ID 0x1bc7 #define TELIT_PRODUCT_UC864E 0x1003 +#define TELIT_PRODUCT_UC864G 0x1004 /* ZTE PRODUCTS */ #define ZTE_VENDOR_ID 0x19d2 @@ -299,12 +301,14 @@ static int option_resume(struct usb_serial *serial); #define ZTE_PRODUCT_MF626 0x0031 #define ZTE_PRODUCT_CDMA_TECH 0xfffe #define ZTE_PRODUCT_AC8710 0xfff1 +#define ZTE_PRODUCT_AC2726 0xfff5 #define BENQ_VENDOR_ID 0x04a5 #define BENQ_PRODUCT_H10 0x4068 #define DLINK_VENDOR_ID 0x1186 #define DLINK_PRODUCT_DWM_652 0x3e04 +#define DLINK_PRODUCT_DWM_652_U5 0xce16 #define QISDA_VENDOR_ID 0x1da5 #define QISDA_PRODUCT_H21_4512 0x4512 @@ -312,10 +316,14 @@ static int option_resume(struct usb_serial *serial); #define QISDA_PRODUCT_H20_4515 0x4515 #define QISDA_PRODUCT_H20_4519 0x4519 +/* TLAYTECH PRODUCTS */ +#define TLAYTECH_VENDOR_ID 0x20B9 +#define TLAYTECH_PRODUCT_TEU800 0x1682 /* TOSHIBA PRODUCTS */ #define TOSHIBA_VENDOR_ID 0x0930 #define TOSHIBA_PRODUCT_HSDPA_MINICARD 0x1302 +#define TOSHIBA_PRODUCT_G450 0x0d45 #define ALINK_VENDOR_ID 0x1e0e #define ALINK_PRODUCT_3GU 0x9200 @@ -324,6 +332,9 @@ static int option_resume(struct usb_serial *serial); #define ALCATEL_VENDOR_ID 0x1bbb #define ALCATEL_PRODUCT_X060S 0x0000 +/* Airplus products */ +#define AIRPLUS_VENDOR_ID 0x1011 +#define AIRPLUS_PRODUCT_MCD650 0x3198 static struct usb_device_id option_ids[] = { { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_COLT) }, @@ -422,6 +433,7 @@ static struct usb_device_id option_ids[] = { { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E143D, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E143E, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E143F, 0xff, 0xff, 0xff) }, + { USB_DEVICE(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E14AC) }, { USB_DEVICE(AMOI_VENDOR_ID, AMOI_PRODUCT_9508) }, { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_V640) }, /* Novatel Merlin V640/XV620 */ { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_V620) }, /* Novatel Merlin V620/S620 */ @@ -502,6 +514,7 @@ static struct usb_device_id option_ids[] = { { USB_DEVICE(QUALCOMM_VENDOR_ID, 0x6613)}, /* Onda H600/ZTE MF330 */ { USB_DEVICE(MAXON_VENDOR_ID, 0x6280) }, /* BP3-USB & BP3-EXT HSDPA */ { USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_UC864E) }, + { USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_UC864G) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, ZTE_PRODUCT_MF622, 0xff, 0xff, 0xff) }, /* ZTE WCDMA products */ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0002, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0003, 0xff, 0xff, 0xff) }, @@ -571,16 +584,21 @@ static struct usb_device_id option_ids[] = { { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0073, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, ZTE_PRODUCT_CDMA_TECH, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, ZTE_PRODUCT_AC8710, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, ZTE_PRODUCT_AC2726, 0xff, 0xff, 0xff) }, { USB_DEVICE(BENQ_VENDOR_ID, BENQ_PRODUCT_H10) }, { USB_DEVICE(DLINK_VENDOR_ID, DLINK_PRODUCT_DWM_652) }, + { USB_DEVICE(ALINK_VENDOR_ID, DLINK_PRODUCT_DWM_652_U5) }, /* Yes, ALINK_VENDOR_ID */ { USB_DEVICE(QISDA_VENDOR_ID, QISDA_PRODUCT_H21_4512) }, { USB_DEVICE(QISDA_VENDOR_ID, QISDA_PRODUCT_H21_4523) }, { USB_DEVICE(QISDA_VENDOR_ID, QISDA_PRODUCT_H20_4515) }, { USB_DEVICE(QISDA_VENDOR_ID, QISDA_PRODUCT_H20_4519) }, + { USB_DEVICE(TOSHIBA_VENDOR_ID, TOSHIBA_PRODUCT_G450) }, { USB_DEVICE(TOSHIBA_VENDOR_ID, TOSHIBA_PRODUCT_HSDPA_MINICARD ) }, /* Toshiba 3G HSDPA == Novatel Expedite EU870D MiniCard */ { USB_DEVICE(ALINK_VENDOR_ID, 0x9000) }, { USB_DEVICE_AND_INTERFACE_INFO(ALINK_VENDOR_ID, ALINK_PRODUCT_3GU, 0xff, 0xff, 0xff) }, { USB_DEVICE(ALCATEL_VENDOR_ID, ALCATEL_PRODUCT_X060S) }, + { USB_DEVICE(AIRPLUS_VENDOR_ID, AIRPLUS_PRODUCT_MCD650) }, + { USB_DEVICE(TLAYTECH_VENDOR_ID, TLAYTECH_PRODUCT_TEU800) }, { } /* Terminating entry */ }; MODULE_DEVICE_TABLE(usb, option_ids); @@ -592,6 +610,7 @@ static struct usb_driver option_driver = { #ifdef CONFIG_PM .suspend = usb_serial_suspend, .resume = usb_serial_resume, + .supports_autosuspend = 1, #endif .id_table = option_ids, .no_dynamic_id = 1, @@ -639,6 +658,12 @@ static int debug; #define IN_BUFLEN 4096 #define OUT_BUFLEN 4096 +struct option_intf_private { + spinlock_t susp_lock; + unsigned int suspended:1; + int in_flight; +}; + struct option_port_private { /* Input endpoints and buffer for this port */ struct urb *in_urbs[N_IN_URB]; @@ -647,6 +672,8 @@ struct option_port_private { struct urb *out_urbs[N_OUT_URB]; u8 *out_buffer[N_OUT_URB]; unsigned long out_busy; /* Bit vector of URBs in use */ + int opened; + struct usb_anchor delayed; /* Settings for the port */ int rts_state; /* Handshaking pins (outputs) */ @@ -693,12 +720,17 @@ module_exit(option_exit); static int option_probe(struct usb_serial *serial, const struct usb_device_id *id) { + struct option_intf_private *data; /* D-Link DWM 652 still exposes CD-Rom emulation interface in modem mode */ if (serial->dev->descriptor.idVendor == DLINK_VENDOR_ID && serial->dev->descriptor.idProduct == DLINK_PRODUCT_DWM_652 && serial->interface->cur_altsetting->desc.bInterfaceClass == 0x8) return -ENODEV; + data = serial->private = kzalloc(sizeof(struct option_intf_private), GFP_KERNEL); + if (!data) + return -ENOMEM; + spin_lock_init(&data->susp_lock); return 0; } @@ -755,12 +787,15 @@ static int option_write(struct tty_struct *tty, struct usb_serial_port *port, const unsigned char *buf, int count) { struct option_port_private *portdata; + struct option_intf_private *intfdata; int i; int left, todo; struct urb *this_urb = NULL; /* spurious */ int err; + unsigned long flags; portdata = usb_get_serial_port_data(port); + intfdata = port->serial->private; dbg("%s: write (%d chars)", __func__, count); @@ -782,17 +817,33 @@ static int option_write(struct tty_struct *tty, struct usb_serial_port *port, dbg("%s: endpoint %d buf %d", __func__, usb_pipeendpoint(this_urb->pipe), i); + err = usb_autopm_get_interface_async(port->serial->interface); + if (err < 0) + break; + /* send the data */ memcpy(this_urb->transfer_buffer, buf, todo); this_urb->transfer_buffer_length = todo; - err = usb_submit_urb(this_urb, GFP_ATOMIC); - if (err) { - dbg("usb_submit_urb %p (write bulk) failed " - "(%d)", this_urb, err); - clear_bit(i, &portdata->out_busy); - continue; + spin_lock_irqsave(&intfdata->susp_lock, flags); + if (intfdata->suspended) { + usb_anchor_urb(this_urb, &portdata->delayed); + spin_unlock_irqrestore(&intfdata->susp_lock, flags); + } else { + intfdata->in_flight++; + spin_unlock_irqrestore(&intfdata->susp_lock, flags); + err = usb_submit_urb(this_urb, GFP_ATOMIC); + if (err) { + dbg("usb_submit_urb %p (write bulk) failed " + "(%d)", this_urb, err); + clear_bit(i, &portdata->out_busy); + spin_lock_irqsave(&intfdata->susp_lock, flags); + intfdata->in_flight--; + spin_unlock_irqrestore(&intfdata->susp_lock, flags); + continue; + } } + portdata->tx_start_time[i] = jiffies; buf += todo; left -= todo; @@ -836,7 +887,10 @@ static void option_indat_callback(struct urb *urb) if (err) printk(KERN_ERR "%s: resubmit read urb failed. " "(%d)", __func__, err); + else + usb_mark_last_busy(port->serial->dev); } + } return; } @@ -845,15 +899,21 @@ static void option_outdat_callback(struct urb *urb) { struct usb_serial_port *port; struct option_port_private *portdata; + struct option_intf_private *intfdata; int i; dbg("%s", __func__); port = urb->context; + intfdata = port->serial->private; usb_serial_port_softint(port); - + usb_autopm_put_interface_async(port->serial->interface); portdata = usb_get_serial_port_data(port); + spin_lock(&intfdata->susp_lock); + intfdata->in_flight--; + spin_unlock(&intfdata->susp_lock); + for (i = 0; i < N_OUT_URB; ++i) { if (portdata->out_urbs[i] == urb) { smp_mb__before_clear_bit(); @@ -963,10 +1023,13 @@ static int option_chars_in_buffer(struct tty_struct *tty) static int option_open(struct tty_struct *tty, struct usb_serial_port *port) { struct option_port_private *portdata; + struct option_intf_private *intfdata; + struct usb_serial *serial = port->serial; int i, err; struct urb *urb; portdata = usb_get_serial_port_data(port); + intfdata = serial->private; dbg("%s", __func__); @@ -985,6 +1048,12 @@ static int option_open(struct tty_struct *tty, struct usb_serial_port *port) option_send_setup(port); + serial->interface->needs_remote_wakeup = 1; + spin_lock_irq(&intfdata->susp_lock); + portdata->opened = 1; + spin_unlock_irq(&intfdata->susp_lock); + usb_autopm_put_interface(serial->interface); + return 0; } @@ -1009,16 +1078,23 @@ static void option_close(struct usb_serial_port *port) int i; struct usb_serial *serial = port->serial; struct option_port_private *portdata; + struct option_intf_private *intfdata = port->serial->private; dbg("%s", __func__); portdata = usb_get_serial_port_data(port); if (serial->dev) { /* Stop reading/writing urbs */ + spin_lock_irq(&intfdata->susp_lock); + portdata->opened = 0; + spin_unlock_irq(&intfdata->susp_lock); + for (i = 0; i < N_IN_URB; i++) usb_kill_urb(portdata->in_urbs[i]); for (i = 0; i < N_OUT_URB; i++) usb_kill_urb(portdata->out_urbs[i]); + usb_autopm_get_interface(serial->interface); + serial->interface->needs_remote_wakeup = 0; } } @@ -1123,6 +1199,7 @@ static int option_startup(struct usb_serial *serial) __func__, i); return 1; } + init_usb_anchor(&portdata->delayed); for (j = 0; j < N_IN_URB; j++) { buffer = (u8 *)__get_free_page(GFP_KERNEL); @@ -1225,18 +1302,52 @@ static void option_release(struct usb_serial *serial) #ifdef CONFIG_PM static int option_suspend(struct usb_serial *serial, pm_message_t message) { + struct option_intf_private *intfdata = serial->private; + int b; + dbg("%s entered", __func__); + + if (serial->dev->auto_pm) { + spin_lock_irq(&intfdata->susp_lock); + b = intfdata->in_flight; + spin_unlock_irq(&intfdata->susp_lock); + + if (b) + return -EBUSY; + } + + spin_lock_irq(&intfdata->susp_lock); + intfdata->suspended = 1; + spin_unlock_irq(&intfdata->susp_lock); stop_read_write_urbs(serial); return 0; } +static void play_delayed(struct usb_serial_port *port) +{ + struct option_intf_private *data; + struct option_port_private *portdata; + struct urb *urb; + int err; + + portdata = usb_get_serial_port_data(port); + data = port->serial->private; + while ((urb = usb_get_from_anchor(&portdata->delayed))) { + err = usb_submit_urb(urb, GFP_ATOMIC); + if (!err) + data->in_flight++; + } +} + static int option_resume(struct usb_serial *serial) { - int err, i, j; + int i, j; struct usb_serial_port *port; - struct urb *urb; + struct option_intf_private *intfdata = serial->private; struct option_port_private *portdata; + struct urb *urb; + int err = 0; dbg("%s entered", __func__); /* get the interrupt URBs resubmitted unconditionally */ @@ -1251,7 +1362,7 @@ static int option_resume(struct usb_serial *serial) if (err < 0) { err("%s: Error %d for interrupt URB of port%d", __func__, err, i); - return err; + goto err_out; } } @@ -1259,27 +1370,32 @@ static int option_resume(struct usb_serial *serial) /* walk all ports */ port = serial->port[i]; portdata = usb_get_serial_port_data(port); - mutex_lock(&port->mutex); /* skip closed ports */ - if (!port->port.count) { - mutex_unlock(&port->mutex); + spin_lock_irq(&intfdata->susp_lock); + if (!portdata->opened) { + spin_unlock_irq(&intfdata->susp_lock); continue; } for (j = 0; j < N_IN_URB; j++) { urb = portdata->in_urbs[j]; - err = usb_submit_urb(urb, GFP_NOIO); + err = usb_submit_urb(urb, GFP_ATOMIC); if (err < 0) { - mutex_unlock(&port->mutex); err("%s: Error %d for bulk URB %d", __func__, err, i); - return err; + spin_unlock_irq(&intfdata->susp_lock); + goto err_out; } } - mutex_unlock(&port->mutex); + play_delayed(port); + spin_unlock_irq(&intfdata->susp_lock); } - return 0; + spin_lock_irq(&intfdata->susp_lock); + intfdata->suspended = 0; + spin_unlock_irq(&intfdata->susp_lock); +err_out: + return err; } #endif diff --git a/drivers/usb/serial/oti6858.c b/drivers/usb/serial/oti6858.c index 0f4a70ce3823..c644e26394b4 100644 --- a/drivers/usb/serial/oti6858.c +++ b/drivers/usb/serial/oti6858.c @@ -288,7 +288,7 @@ static void setup_line(struct work_struct *work) dbg("%s(): submitting interrupt urb", __func__); port->interrupt_in_urb->dev = port->serial->dev; - result = usb_submit_urb(port->interrupt_in_urb, GFP_ATOMIC); + result = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL); if (result != 0) { dev_err(&port->dev, "%s(): usb_submit_urb() failed" " with error %d\n", __func__, result); @@ -335,7 +335,7 @@ void send_data(struct work_struct *work) dbg("%s(): submitting interrupt urb", __func__); port->interrupt_in_urb->dev = port->serial->dev; - result = usb_submit_urb(port->interrupt_in_urb, GFP_ATOMIC); + result = usb_submit_urb(port->interrupt_in_urb, GFP_NOIO); if (result != 0) { dev_err(&port->dev, "%s(): usb_submit_urb() failed" " with error %d\n", __func__, result); @@ -349,7 +349,7 @@ void send_data(struct work_struct *work) port->write_urb->transfer_buffer_length = count; port->write_urb->dev = port->serial->dev; - result = usb_submit_urb(port->write_urb, GFP_ATOMIC); + result = usb_submit_urb(port->write_urb, GFP_NOIO); if (result != 0) { dev_err(&port->dev, "%s(): usb_submit_urb() failed" " with error %d\n", __func__, result); diff --git a/drivers/usb/serial/pl2303.c b/drivers/usb/serial/pl2303.c index a63ea99936f7..9ec1a49e2362 100644 --- a/drivers/usb/serial/pl2303.c +++ b/drivers/usb/serial/pl2303.c @@ -96,6 +96,7 @@ static struct usb_device_id id_table [] = { { USB_DEVICE(HP_VENDOR_ID, HP_LD220_PRODUCT_ID) }, { USB_DEVICE(CRESSI_VENDOR_ID, CRESSI_EDY_PRODUCT_ID) }, { USB_DEVICE(SONY_VENDOR_ID, SONY_QN3USB_PRODUCT_ID) }, + { USB_DEVICE(SANWA_VENDOR_ID, SANWA_PRODUCT_ID) }, { } /* Terminating entry */ }; @@ -527,6 +528,12 @@ static void pl2303_set_termios(struct tty_struct *tty, int baud; int i; u8 control; + const int baud_sup[] = { 75, 150, 300, 600, 1200, 1800, 2400, 3600, + 4800, 7200, 9600, 14400, 19200, 28800, 38400, + 57600, 115200, 230400, 460800, 614400, + 921600, 1228800, 2457600, 3000000, 6000000 }; + int baud_floor, baud_ceil; + int k; dbg("%s - port %d", __func__, port->number); @@ -572,9 +579,39 @@ static void pl2303_set_termios(struct tty_struct *tty, dbg("%s - data bits = %d", __func__, buf[6]); } + /* For reference buf[0]:buf[3] baud rate value */ + /* NOTE: Only the values defined in baud_sup are supported ! + * => if unsupported values are set, the PL2303 seems to use + * 9600 baud (at least my PL2303X always does) + */ baud = tty_get_baud_rate(tty); - dbg("%s - baud = %d", __func__, baud); + dbg("%s - baud requested = %d", __func__, baud); if (baud) { + /* Set baudrate to nearest supported value */ + for (k=0; k<ARRAY_SIZE(baud_sup); k++) { + if (baud_sup[k] / baud) { + baud_ceil = baud_sup[k]; + if (k==0) { + baud = baud_ceil; + } else { + baud_floor = baud_sup[k-1]; + if ((baud_ceil % baud) + > (baud % baud_floor)) + baud = baud_floor; + else + baud = baud_ceil; + } + break; + } + } + if (baud > 1228800) { + /* type_0, type_1 only support up to 1228800 baud */ + if (priv->type != HX) + baud = 1228800; + else if (baud > 6000000) + baud = 6000000; + } + dbg("%s - baud set = %d", __func__, baud); buf[0] = baud & 0xff; buf[1] = (baud >> 8) & 0xff; buf[2] = (baud >> 16) & 0xff; @@ -585,8 +622,16 @@ static void pl2303_set_termios(struct tty_struct *tty, /* For reference buf[4]=1 is 1.5 stop bits */ /* For reference buf[4]=2 is 2 stop bits */ if (cflag & CSTOPB) { - buf[4] = 2; - dbg("%s - stop bits = 2", __func__); + /* NOTE: Comply with "real" UARTs / RS232: + * use 1.5 instead of 2 stop bits with 5 data bits + */ + if ((cflag & CSIZE) == CS5) { + buf[4] = 1; + dbg("%s - stop bits = 1.5", __func__); + } else { + buf[4] = 2; + dbg("%s - stop bits = 2", __func__); + } } else { buf[4] = 0; dbg("%s - stop bits = 1", __func__); @@ -599,11 +644,21 @@ static void pl2303_set_termios(struct tty_struct *tty, /* For reference buf[5]=3 is mark parity */ /* For reference buf[5]=4 is space parity */ if (cflag & PARODD) { - buf[5] = 1; - dbg("%s - parity = odd", __func__); + if (cflag & CMSPAR) { + buf[5] = 3; + dbg("%s - parity = mark", __func__); + } else { + buf[5] = 1; + dbg("%s - parity = odd", __func__); + } } else { - buf[5] = 2; - dbg("%s - parity = even", __func__); + if (cflag & CMSPAR) { + buf[5] = 4; + dbg("%s - parity = space", __func__); + } else { + buf[5] = 2; + dbg("%s - parity = even", __func__); + } } } else { buf[5] = 0; @@ -647,7 +702,7 @@ static void pl2303_set_termios(struct tty_struct *tty, pl2303_vendor_write(0x0, 0x0, serial); } - /* FIXME: Need to read back resulting baud rate */ + /* Save resulting baud rate */ if (baud) tty_encode_baud_rate(tty, baud, baud); @@ -991,13 +1046,15 @@ static void pl2303_push_data(struct tty_struct *tty, /* overrun is special, not associated with a char */ if (line_status & UART_OVERRUN_ERROR) tty_insert_flip_char(tty, 0, TTY_OVERRUN); - if (port->console && port->sysrq) { + + if (tty_flag == TTY_NORMAL && !(port->console && port->sysrq)) + tty_insert_flip_string(tty, data, urb->actual_length); + else { int i; for (i = 0; i < urb->actual_length; ++i) if (!usb_serial_handle_sysrq_char(tty, port, data[i])) tty_insert_flip_char(tty, data[i], tty_flag); - } else - tty_insert_flip_string(tty, data, urb->actual_length); + } tty_flip_buffer_push(tty); } diff --git a/drivers/usb/serial/pl2303.h b/drivers/usb/serial/pl2303.h index ee9505e1dd92..d640dc951568 100644 --- a/drivers/usb/serial/pl2303.h +++ b/drivers/usb/serial/pl2303.h @@ -130,3 +130,7 @@ /* Sony, USB data cable for CMD-Jxx mobile phones */ #define SONY_VENDOR_ID 0x054c #define SONY_QN3USB_PRODUCT_ID 0x0437 + +/* Sanwa KB-USB2 multimeter cable (ID: 11ad:0001) */ +#define SANWA_VENDOR_ID 0x11ad +#define SANWA_PRODUCT_ID 0x0001 diff --git a/drivers/usb/serial/sierra.c b/drivers/usb/serial/sierra.c index 55391bbe1230..5019325ba25d 100644 --- a/drivers/usb/serial/sierra.c +++ b/drivers/usb/serial/sierra.c @@ -17,7 +17,7 @@ Whom based his on the Keyspan driver by Hugh Blemings <hugh@blemings.org> */ -#define DRIVER_VERSION "v.1.3.7" +#define DRIVER_VERSION "v.1.3.8" #define DRIVER_AUTHOR "Kevin Lloyd, Elina Pasheva, Matthew Safar, Rory Filer" #define DRIVER_DESC "USB Driver for Sierra Wireless USB modems" @@ -51,6 +51,12 @@ struct sierra_iface_info { const u8 *ifaceinfo; /* pointer to the array holding the numbers */ }; +struct sierra_intf_private { + spinlock_t susp_lock; + unsigned int suspended:1; + int in_flight; +}; + static int sierra_set_power_state(struct usb_device *udev, __u16 swiState) { int result; @@ -144,6 +150,7 @@ static int sierra_probe(struct usb_serial *serial, { int result = 0; struct usb_device *udev; + struct sierra_intf_private *data; u8 ifnum; udev = serial->dev; @@ -171,6 +178,11 @@ static int sierra_probe(struct usb_serial *serial, return -ENODEV; } + data = serial->private = kzalloc(sizeof(struct sierra_intf_private), GFP_KERNEL); + if (!data) + return -ENOMEM; + spin_lock_init(&data->susp_lock); + return result; } @@ -261,13 +273,18 @@ static struct usb_driver sierra_driver = { .name = "sierra", .probe = usb_serial_probe, .disconnect = usb_serial_disconnect, + .suspend = usb_serial_suspend, + .resume = usb_serial_resume, .id_table = id_table, .no_dynamic_id = 1, + .supports_autosuspend = 1, }; struct sierra_port_private { spinlock_t lock; /* lock the structure */ int outstanding_urbs; /* number of out urbs in flight */ + struct usb_anchor active; + struct usb_anchor delayed; /* Input endpoints and buffers for this port */ struct urb *in_urbs[N_IN_URB]; @@ -279,6 +296,7 @@ struct sierra_port_private { int dsr_state; int dcd_state; int ri_state; + unsigned int opened:1; }; static int sierra_send_setup(struct usb_serial_port *port) @@ -287,6 +305,8 @@ static int sierra_send_setup(struct usb_serial_port *port) struct sierra_port_private *portdata; __u16 interface = 0; int val = 0; + int do_send = 0; + int retval; dev_dbg(&port->dev, "%s\n", __func__); @@ -305,10 +325,7 @@ static int sierra_send_setup(struct usb_serial_port *port) */ if (port->interrupt_in_urb) { /* send control message */ - return usb_control_msg(serial->dev, - usb_rcvctrlpipe(serial->dev, 0), - 0x22, 0x21, val, interface, - NULL, 0, USB_CTRL_SET_TIMEOUT); + do_send = 1; } } @@ -320,12 +337,18 @@ static int sierra_send_setup(struct usb_serial_port *port) interface = 1; else if (port->bulk_out_endpointAddress == 5) interface = 2; - return usb_control_msg(serial->dev, - usb_rcvctrlpipe(serial->dev, 0), - 0x22, 0x21, val, interface, - NULL, 0, USB_CTRL_SET_TIMEOUT); + + do_send = 1; } - return 0; + if (!do_send) + return 0; + + usb_autopm_get_interface(serial->interface); + retval = usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev, 0), + 0x22, 0x21, val, interface, NULL, 0, USB_CTRL_SET_TIMEOUT); + usb_autopm_put_interface(serial->interface); + + return retval; } static void sierra_set_termios(struct tty_struct *tty, @@ -390,21 +413,25 @@ static void sierra_outdat_callback(struct urb *urb) { struct usb_serial_port *port = urb->context; struct sierra_port_private *portdata = usb_get_serial_port_data(port); + struct sierra_intf_private *intfdata; int status = urb->status; - unsigned long flags; dev_dbg(&port->dev, "%s - port %d\n", __func__, port->number); + intfdata = port->serial->private; /* free up the transfer buffer, as usb_free_urb() does not do this */ kfree(urb->transfer_buffer); - + usb_autopm_put_interface_async(port->serial->interface); if (status) dev_dbg(&port->dev, "%s - nonzero write bulk status " "received: %d\n", __func__, status); - spin_lock_irqsave(&portdata->lock, flags); + spin_lock(&portdata->lock); --portdata->outstanding_urbs; - spin_unlock_irqrestore(&portdata->lock, flags); + spin_unlock(&portdata->lock); + spin_lock(&intfdata->susp_lock); + --intfdata->in_flight; + spin_unlock(&intfdata->susp_lock); usb_serial_port_softint(port); } @@ -414,6 +441,7 @@ static int sierra_write(struct tty_struct *tty, struct usb_serial_port *port, const unsigned char *buf, int count) { struct sierra_port_private *portdata = usb_get_serial_port_data(port); + struct sierra_intf_private *intfdata; struct usb_serial *serial = port->serial; unsigned long flags; unsigned char *buffer; @@ -426,9 +454,9 @@ static int sierra_write(struct tty_struct *tty, struct usb_serial_port *port, return 0; portdata = usb_get_serial_port_data(port); + intfdata = serial->private; dev_dbg(&port->dev, "%s: write (%zd bytes)\n", __func__, writesize); - spin_lock_irqsave(&portdata->lock, flags); dev_dbg(&port->dev, "%s - outstanding_urbs: %d\n", __func__, portdata->outstanding_urbs); @@ -442,6 +470,14 @@ static int sierra_write(struct tty_struct *tty, struct usb_serial_port *port, portdata->outstanding_urbs); spin_unlock_irqrestore(&portdata->lock, flags); + retval = usb_autopm_get_interface_async(serial->interface); + if (retval < 0) { + spin_lock_irqsave(&portdata->lock, flags); + portdata->outstanding_urbs--; + spin_unlock_irqrestore(&portdata->lock, flags); + goto error_simple; + } + buffer = kmalloc(writesize, GFP_ATOMIC); if (!buffer) { dev_err(&port->dev, "out of memory\n"); @@ -468,14 +504,29 @@ static int sierra_write(struct tty_struct *tty, struct usb_serial_port *port, /* Handle the need to send a zero length packet */ urb->transfer_flags |= URB_ZERO_PACKET; + spin_lock_irqsave(&intfdata->susp_lock, flags); + + if (intfdata->suspended) { + usb_anchor_urb(urb, &portdata->delayed); + spin_unlock_irqrestore(&intfdata->susp_lock, flags); + goto skip_power; + } else { + usb_anchor_urb(urb, &portdata->active); + } /* send it down the pipe */ retval = usb_submit_urb(urb, GFP_ATOMIC); if (retval) { + usb_unanchor_urb(urb); + spin_unlock_irqrestore(&intfdata->susp_lock, flags); dev_err(&port->dev, "%s - usb_submit_urb(write bulk) failed " "with status = %d\n", __func__, retval); goto error; + } else { + intfdata->in_flight++; + spin_unlock_irqrestore(&intfdata->susp_lock, flags); } +skip_power: /* we are done with this urb, so let the host driver * really free it when it is finished with it */ usb_free_urb(urb); @@ -491,6 +542,8 @@ error_no_buffer: dev_dbg(&port->dev, "%s - 2. outstanding_urbs: %d\n", __func__, portdata->outstanding_urbs); spin_unlock_irqrestore(&portdata->lock, flags); + usb_autopm_put_interface_async(serial->interface); +error_simple: return retval; } @@ -530,6 +583,7 @@ static void sierra_indat_callback(struct urb *urb) /* Resubmit urb so we continue receiving */ if (port->port.count && status != -ESHUTDOWN && status != -EPERM) { + usb_mark_last_busy(port->serial->dev); err = usb_submit_urb(urb, GFP_ATOMIC); if (err) dev_err(&port->dev, "resubmit read urb failed." @@ -591,6 +645,7 @@ static void sierra_instat_callback(struct urb *urb) /* Resubmit urb so we continue receiving IRQ data */ if (port->port.count && status != -ESHUTDOWN && status != -ENOENT) { + usb_mark_last_busy(serial->dev); urb->dev = serial->dev; err = usb_submit_urb(urb, GFP_ATOMIC); if (err) @@ -711,6 +766,8 @@ static void sierra_close(struct usb_serial_port *port) int i; struct usb_serial *serial = port->serial; struct sierra_port_private *portdata; + struct sierra_intf_private *intfdata = port->serial->private; + dev_dbg(&port->dev, "%s\n", __func__); portdata = usb_get_serial_port_data(port); @@ -720,9 +777,16 @@ static void sierra_close(struct usb_serial_port *port) if (serial->dev) { mutex_lock(&serial->disc_mutex); - if (!serial->disconnected) + if (!serial->disconnected) { + serial->interface->needs_remote_wakeup = 0; + usb_autopm_get_interface(serial->interface); sierra_send_setup(port); + } mutex_unlock(&serial->disc_mutex); + spin_lock_irq(&intfdata->susp_lock); + portdata->opened = 0; + spin_unlock_irq(&intfdata->susp_lock); + /* Stop reading urbs */ sierra_stop_rx_urbs(port); @@ -738,6 +802,7 @@ static int sierra_open(struct tty_struct *tty, struct usb_serial_port *port) { struct sierra_port_private *portdata; struct usb_serial *serial = port->serial; + struct sierra_intf_private *intfdata = serial->private; int i; int err; int endpoint; @@ -767,10 +832,18 @@ static int sierra_open(struct tty_struct *tty, struct usb_serial_port *port) if (err) { /* get rid of everything as in close */ sierra_close(port); + /* restore balance for autopm */ + usb_autopm_put_interface(serial->interface); return err; } sierra_send_setup(port); + serial->interface->needs_remote_wakeup = 1; + spin_lock_irq(&intfdata->susp_lock); + portdata->opened = 1; + spin_unlock_irq(&intfdata->susp_lock); + usb_autopm_put_interface(serial->interface); + return 0; } @@ -818,6 +891,8 @@ static int sierra_startup(struct usb_serial *serial) return -ENOMEM; } spin_lock_init(&portdata->lock); + init_usb_anchor(&portdata->active); + init_usb_anchor(&portdata->delayed); /* Set the port private data pointer */ usb_set_serial_port_data(port, portdata); } @@ -844,6 +919,87 @@ static void sierra_release(struct usb_serial *serial) } } +#ifdef CONFIG_PM +static void stop_read_write_urbs(struct usb_serial *serial) +{ + int i; + struct usb_serial_port *port; + struct sierra_port_private *portdata; + + /* Stop reading/writing urbs */ + for (i = 0; i < serial->num_ports; ++i) { + port = serial->port[i]; + portdata = usb_get_serial_port_data(port); + sierra_stop_rx_urbs(port); + usb_kill_anchored_urbs(&portdata->active); + } +} + +static int sierra_suspend(struct usb_serial *serial, pm_message_t message) +{ + struct sierra_intf_private *intfdata; + int b; + + if (serial->dev->auto_pm) { + intfdata = serial->private; + spin_lock_irq(&intfdata->susp_lock); + b = intfdata->in_flight; + + if (b) { + spin_unlock_irq(&intfdata->susp_lock); + return -EBUSY; + } else { + intfdata->suspended = 1; + spin_unlock_irq(&intfdata->susp_lock); + } + } + stop_read_write_urbs(serial); + + return 0; +} + +static int sierra_resume(struct usb_serial *serial) +{ + struct usb_serial_port *port; + struct sierra_intf_private *intfdata = serial->private; + struct sierra_port_private *portdata; + struct urb *urb; + int ec = 0; + int i, err; + + spin_lock_irq(&intfdata->susp_lock); + for (i = 0; i < serial->num_ports; i++) { + port = serial->port[i]; + portdata = usb_get_serial_port_data(port); + + while ((urb = usb_get_from_anchor(&portdata->delayed))) { + usb_anchor_urb(urb, &portdata->active); + intfdata->in_flight++; + err = usb_submit_urb(urb, GFP_ATOMIC); + if (err < 0) { + intfdata->in_flight--; + usb_unanchor_urb(urb); + usb_scuttle_anchored_urbs(&portdata->delayed); + break; + } + } + + if (portdata->opened) { + err = sierra_submit_rx_urbs(port, GFP_ATOMIC); + if (err) + ec++; + } + } + intfdata->suspended = 0; + spin_unlock_irq(&intfdata->susp_lock); + + return ec ? -EIO : 0; +} +#else +#define sierra_suspend NULL +#define sierra_resume NULL +#endif + static struct usb_serial_driver sierra_device = { .driver = { .owner = THIS_MODULE, @@ -864,6 +1020,8 @@ static struct usb_serial_driver sierra_device = { .tiocmset = sierra_tiocmset, .attach = sierra_startup, .release = sierra_release, + .suspend = sierra_suspend, + .resume = sierra_resume, .read_int_callback = sierra_instat_callback, }; diff --git a/drivers/usb/serial/symbolserial.c b/drivers/usb/serial/symbolserial.c index cb7e95f9fcbf..b282c0f2d8e5 100644 --- a/drivers/usb/serial/symbolserial.c +++ b/drivers/usb/serial/symbolserial.c @@ -165,34 +165,36 @@ static void symbol_throttle(struct tty_struct *tty) { struct usb_serial_port *port = tty->driver_data; struct symbol_private *priv = usb_get_serial_data(port->serial); - unsigned long flags; dbg("%s - port %d", __func__, port->number); - spin_lock_irqsave(&priv->lock, flags); + spin_lock_irq(&priv->lock); priv->throttled = true; - spin_unlock_irqrestore(&priv->lock, flags); + spin_unlock_irq(&priv->lock); } static void symbol_unthrottle(struct tty_struct *tty) { struct usb_serial_port *port = tty->driver_data; struct symbol_private *priv = usb_get_serial_data(port->serial); - unsigned long flags; int result; + bool was_throttled; dbg("%s - port %d", __func__, port->number); - spin_lock_irqsave(&priv->lock, flags); + spin_lock_irq(&priv->lock); priv->throttled = false; + was_throttled = priv->actually_throttled; priv->actually_throttled = false; - spin_unlock_irqrestore(&priv->lock, flags); + spin_unlock_irq(&priv->lock); priv->int_urb->dev = port->serial->dev; - result = usb_submit_urb(priv->int_urb, GFP_ATOMIC); - if (result) - dev_err(&port->dev, - "%s - failed submitting read urb, error %d\n", + if (was_throttled) { + result = usb_submit_urb(priv->int_urb, GFP_KERNEL); + if (result) + dev_err(&port->dev, + "%s - failed submitting read urb, error %d\n", __func__, result); + } } static int symbol_startup(struct usb_serial *serial) diff --git a/drivers/usb/serial/usb-serial.c b/drivers/usb/serial/usb-serial.c index 9d7ca4868d37..bd3fa7ff15b1 100644 --- a/drivers/usb/serial/usb-serial.c +++ b/drivers/usb/serial/usb-serial.c @@ -35,6 +35,7 @@ #include <linux/serial.h> #include <linux/usb.h> #include <linux/usb/serial.h> +#include <linux/kfifo.h> #include "pl2303.h" /* @@ -155,7 +156,8 @@ static void destroy_serial(struct kref *kref) if (serial->minor != SERIAL_TTY_NO_MINOR) return_serial(serial); - serial->type->release(serial); + if (serial->attached) + serial->type->release(serial); /* Now that nothing is using the ports, they can be freed */ for (i = 0; i < serial->num_port_pointers; ++i) { @@ -191,7 +193,7 @@ void usb_serial_put(struct usb_serial *serial) * This is the first place a new tty gets used. Hence this is where we * acquire references to the usb_serial structure and the driver module, * where we store a pointer to the port, and where we do an autoresume. - * All these actions are reversed in serial_release(). + * All these actions are reversed in serial_cleanup(). */ static int serial_install(struct tty_driver *driver, struct tty_struct *tty) { @@ -292,8 +294,6 @@ static int serial_open(struct tty_struct *tty, struct file *filp) static void serial_down(struct usb_serial_port *port) { struct usb_serial_driver *drv = port->serial->type; - struct usb_serial *serial; - struct module *owner; /* * The console is magical. Do not hang up the console hardware @@ -309,12 +309,8 @@ static void serial_down(struct usb_serial_port *port) return; mutex_lock(&port->mutex); - serial = port->serial; - owner = serial->type->driver.owner; - if (drv->close) drv->close(port); - mutex_unlock(&port->mutex); } @@ -344,15 +340,16 @@ static void serial_close(struct tty_struct *tty, struct file *filp) } /** - * serial_release - free resources post close/hangup + * serial_cleanup - free resources post close/hangup * @port: port to free up * * Do the resource freeing and refcount dropping for the port. * Avoid freeing the console. * - * Called when the last tty kref is dropped. + * Called asynchronously after the last tty kref is dropped, + * and the tty layer has already done the tty_shutdown(tty); */ -static void serial_release(struct tty_struct *tty) +static void serial_cleanup(struct tty_struct *tty) { struct usb_serial_port *port = tty->driver_data; struct usb_serial *serial; @@ -366,9 +363,6 @@ static void serial_release(struct tty_struct *tty) dbg("%s - port %d", __func__, port->number); - /* Standard shutdown processing */ - tty_shutdown(tty); - tty->driver_data = NULL; serial = port->serial; @@ -631,6 +625,8 @@ static void port_release(struct device *dev) usb_free_urb(port->write_urb); usb_free_urb(port->interrupt_in_urb); usb_free_urb(port->interrupt_out_urb); + if (!IS_ERR(port->write_fifo) && port->write_fifo) + kfifo_free(port->write_fifo); kfree(port->bulk_in_buffer); kfree(port->bulk_out_buffer); kfree(port->interrupt_in_buffer); @@ -970,6 +966,10 @@ int usb_serial_probe(struct usb_interface *interface, dev_err(&interface->dev, "No free urbs available\n"); goto probe_error; } + port->write_fifo = kfifo_alloc(PAGE_SIZE, GFP_KERNEL, + &port->lock); + if (IS_ERR(port->write_fifo)) + goto probe_error; buffer_size = le16_to_cpu(endpoint->wMaxPacketSize); port->bulk_out_size = buffer_size; port->bulk_out_endpointAddress = endpoint->bEndpointAddress; @@ -1060,12 +1060,15 @@ int usb_serial_probe(struct usb_interface *interface, module_put(type->driver.owner); if (retval < 0) goto probe_error; + serial->attached = 1; if (retval > 0) { /* quietly accept this device, but don't bind to a serial port as it's about to disappear */ serial->num_ports = 0; goto exit; } + } else { + serial->attached = 1; } if (get_free_serial(serial, num_ports, &minor) == NULL) { @@ -1163,15 +1166,21 @@ int usb_serial_suspend(struct usb_interface *intf, pm_message_t message) serial->suspending = 1; + if (serial->type->suspend) { + r = serial->type->suspend(serial, message); + if (r < 0) { + serial->suspending = 0; + goto err_out; + } + } + for (i = 0; i < serial->num_ports; ++i) { port = serial->port[i]; if (port) kill_traffic(port); } - if (serial->type->suspend) - r = serial->type->suspend(serial, message); - +err_out: return r; } EXPORT_SYMBOL(usb_serial_suspend); @@ -1205,7 +1214,7 @@ static const struct tty_operations serial_ops = { .chars_in_buffer = serial_chars_in_buffer, .tiocmget = serial_tiocmget, .tiocmset = serial_tiocmset, - .shutdown = serial_release, + .cleanup = serial_cleanup, .install = serial_install, .proc_fops = &serial_proc_fops, }; diff --git a/drivers/usb/serial/visor.c b/drivers/usb/serial/visor.c index 1aa5d20a5d99..ad1f9232292d 100644 --- a/drivers/usb/serial/visor.c +++ b/drivers/usb/serial/visor.c @@ -513,7 +513,8 @@ static void visor_read_bulk_callback(struct urb *urb) tty_kref_put(tty); } spin_lock(&priv->lock); - priv->bytes_in += available_room; + if (tty) + priv->bytes_in += available_room; } else { spin_lock(&priv->lock); @@ -582,12 +583,11 @@ static void visor_throttle(struct tty_struct *tty) { struct usb_serial_port *port = tty->driver_data; struct visor_private *priv = usb_get_serial_port_data(port); - unsigned long flags; dbg("%s - port %d", __func__, port->number); - spin_lock_irqsave(&priv->lock, flags); + spin_lock_irq(&priv->lock); priv->throttled = 1; - spin_unlock_irqrestore(&priv->lock, flags); + spin_unlock_irq(&priv->lock); } @@ -595,21 +595,23 @@ static void visor_unthrottle(struct tty_struct *tty) { struct usb_serial_port *port = tty->driver_data; struct visor_private *priv = usb_get_serial_port_data(port); - unsigned long flags; - int result; + int result, was_throttled; dbg("%s - port %d", __func__, port->number); - spin_lock_irqsave(&priv->lock, flags); + spin_lock_irq(&priv->lock); priv->throttled = 0; + was_throttled = priv->actually_throttled; priv->actually_throttled = 0; - spin_unlock_irqrestore(&priv->lock, flags); + spin_unlock_irq(&priv->lock); - port->read_urb->dev = port->serial->dev; - result = usb_submit_urb(port->read_urb, GFP_ATOMIC); - if (result) - dev_err(&port->dev, - "%s - failed submitting read urb, error %d\n", + if (was_throttled) { + port->read_urb->dev = port->serial->dev; + result = usb_submit_urb(port->read_urb, GFP_KERNEL); + if (result) + dev_err(&port->dev, + "%s - failed submitting read urb, error %d\n", __func__, result); + } } static int palm_os_3_probe(struct usb_serial *serial, diff --git a/drivers/usb/serial/whiteheat.c b/drivers/usb/serial/whiteheat.c index 62424eec33ec..1093d2eb046a 100644 --- a/drivers/usb/serial/whiteheat.c +++ b/drivers/usb/serial/whiteheat.c @@ -949,13 +949,12 @@ static void whiteheat_throttle(struct tty_struct *tty) { struct usb_serial_port *port = tty->driver_data; struct whiteheat_private *info = usb_get_serial_port_data(port); - unsigned long flags; dbg("%s - port %d", __func__, port->number); - spin_lock_irqsave(&info->lock, flags); + spin_lock_irq(&info->lock); info->flags |= THROTTLED; - spin_unlock_irqrestore(&info->lock, flags); + spin_unlock_irq(&info->lock); return; } @@ -966,14 +965,13 @@ static void whiteheat_unthrottle(struct tty_struct *tty) struct usb_serial_port *port = tty->driver_data; struct whiteheat_private *info = usb_get_serial_port_data(port); int actually_throttled; - unsigned long flags; dbg("%s - port %d", __func__, port->number); - spin_lock_irqsave(&info->lock, flags); + spin_lock_irq(&info->lock); actually_throttled = info->flags & ACTUALLY_THROTTLED; info->flags &= ~(THROTTLED | ACTUALLY_THROTTLED); - spin_unlock_irqrestore(&info->lock, flags); + spin_unlock_irq(&info->lock); if (actually_throttled) rx_data_softint(&info->rx_work); diff --git a/drivers/usb/storage/datafab.c b/drivers/usb/storage/datafab.c index 2b6e565262c2..ded836b02d7b 100644 --- a/drivers/usb/storage/datafab.c +++ b/drivers/usb/storage/datafab.c @@ -334,7 +334,7 @@ static int datafab_determine_lun(struct us_data *us, unsigned char *buf; int count = 0, rc; - if (!us || !info) + if (!info) return USB_STOR_TRANSPORT_ERROR; memcpy(command, scommand, 8); @@ -399,7 +399,7 @@ static int datafab_id_device(struct us_data *us, unsigned char *reply; int rc; - if (!us || !info) + if (!info) return USB_STOR_TRANSPORT_ERROR; if (info->lun == -1) { diff --git a/drivers/usb/storage/initializers.c b/drivers/usb/storage/initializers.c index ec17c96371af..105d900150c1 100644 --- a/drivers/usb/storage/initializers.c +++ b/drivers/usb/storage/initializers.c @@ -102,5 +102,5 @@ int usb_stor_huawei_e220_init(struct us_data *us) USB_TYPE_STANDARD | USB_RECIP_DEVICE, 0x01, 0x0, NULL, 0x0, 1000); US_DEBUGP("Huawei mode set result is %d\n", result); - return (result ? 0 : -ENODEV); + return 0; } diff --git a/drivers/usb/storage/jumpshot.c b/drivers/usb/storage/jumpshot.c index 1c69420e3acf..6168596c5ac6 100644 --- a/drivers/usb/storage/jumpshot.c +++ b/drivers/usb/storage/jumpshot.c @@ -335,7 +335,7 @@ static int jumpshot_id_device(struct us_data *us, unsigned char *reply; int rc; - if (!us || !info) + if (!info) return USB_STOR_TRANSPORT_ERROR; command[0] = 0xE0; diff --git a/drivers/usb/storage/onetouch.c b/drivers/usb/storage/onetouch.c index 380233bd6a39..80e65f29921c 100644 --- a/drivers/usb/storage/onetouch.c +++ b/drivers/usb/storage/onetouch.c @@ -163,7 +163,7 @@ static void usb_onetouch_pm_hook(struct us_data *us, int action) usb_kill_urb(onetouch->irq); break; case US_RESUME: - if (usb_submit_urb(onetouch->irq, GFP_KERNEL) != 0) + if (usb_submit_urb(onetouch->irq, GFP_NOIO) != 0) dev_err(&onetouch->irq->dev->dev, "usb_submit_urb failed\n"); break; diff --git a/drivers/usb/storage/transport.c b/drivers/usb/storage/transport.c index e20dc525d177..589f6b4404f0 100644 --- a/drivers/usb/storage/transport.c +++ b/drivers/usb/storage/transport.c @@ -696,7 +696,7 @@ void usb_stor_invoke_transport(struct scsi_cmnd *srb, struct us_data *us) /* device supports and needs bigger sense buffer */ if (us->fflags & US_FL_SANE_SENSE) sense_size = ~0; - +Retry_Sense: US_DEBUGP("Issuing auto-REQUEST_SENSE\n"); scsi_eh_prep_cmnd(srb, &ses, NULL, 0, sense_size); @@ -720,6 +720,21 @@ void usb_stor_invoke_transport(struct scsi_cmnd *srb, struct us_data *us) srb->result = DID_ABORT << 16; goto Handle_Errors; } + + /* Some devices claim to support larger sense but fail when + * trying to request it. When a transport failure happens + * using US_FS_SANE_SENSE, we always retry with a standard + * (small) sense request. This fixes some USB GSM modems + */ + if (temp_result == USB_STOR_TRANSPORT_FAILED && + (us->fflags & US_FL_SANE_SENSE) && + sense_size != US_SENSE_SIZE) { + US_DEBUGP("-- auto-sense failure, retry small sense\n"); + sense_size = US_SENSE_SIZE; + goto Retry_Sense; + } + + /* Other failures */ if (temp_result != USB_STOR_TRANSPORT_GOOD) { US_DEBUGP("-- auto-sense failure\n"); @@ -768,17 +783,32 @@ void usb_stor_invoke_transport(struct scsi_cmnd *srb, struct us_data *us) /* set the result so the higher layers expect this data */ srb->result = SAM_STAT_CHECK_CONDITION; - /* If things are really okay, then let's show that. Zero - * out the sense buffer so the higher layers won't realize - * we did an unsolicited auto-sense. */ - if (result == USB_STOR_TRANSPORT_GOOD && - /* Filemark 0, ignore EOM, ILI 0, no sense */ + /* We often get empty sense data. This could indicate that + * everything worked or that there was an unspecified + * problem. We have to decide which. + */ + if ( /* Filemark 0, ignore EOM, ILI 0, no sense */ (srb->sense_buffer[2] & 0xaf) == 0 && /* No ASC or ASCQ */ srb->sense_buffer[12] == 0 && srb->sense_buffer[13] == 0) { - srb->result = SAM_STAT_GOOD; - srb->sense_buffer[0] = 0x0; + + /* If things are really okay, then let's show that. + * Zero out the sense buffer so the higher layers + * won't realize we did an unsolicited auto-sense. + */ + if (result == USB_STOR_TRANSPORT_GOOD) { + srb->result = SAM_STAT_GOOD; + srb->sense_buffer[0] = 0x0; + + /* If there was a problem, report an unspecified + * hardware error to prevent the higher layers from + * entering an infinite retry loop. + */ + } else { + srb->result = DID_ERROR << 16; + srb->sense_buffer[2] = HARDWARE_ERROR; + } } } diff --git a/drivers/usb/storage/unusual_devs.h b/drivers/usb/storage/unusual_devs.h index 7477d411959f..d4f034ebaa8a 100644 --- a/drivers/usb/storage/unusual_devs.h +++ b/drivers/usb/storage/unusual_devs.h @@ -66,13 +66,6 @@ UNUSUAL_DEV( 0x03eb, 0x2002, 0x0100, 0x0100, US_SC_DEVICE, US_PR_DEVICE, NULL, US_FL_IGNORE_RESIDUE), -/* modified by Tobias Lorenz <tobias.lorenz@gmx.net> */ -UNUSUAL_DEV( 0x03ee, 0x6901, 0x0000, 0x0200, - "Mitsumi", - "USB FDD", - US_SC_DEVICE, US_PR_DEVICE, NULL, - US_FL_SINGLE_LUN ), - /* Reported by Rodolfo Quesada <rquesada@roqz.net> */ UNUSUAL_DEV( 0x03ee, 0x6906, 0x0003, 0x0003, "VIA Technologies Inc.", @@ -233,13 +226,6 @@ UNUSUAL_DEV( 0x0421, 0x0495, 0x0370, 0x0370, US_SC_DEVICE, US_PR_DEVICE, NULL, US_FL_MAX_SECTORS_64 ), -/* Reported by Olaf Hering <olh@suse.de> from novell bug #105878 */ -UNUSUAL_DEV( 0x0424, 0x0fdc, 0x0210, 0x0210, - "SMSC", - "FDC GOLD-2.30", - US_SC_DEVICE, US_PR_DEVICE, NULL, - US_FL_SINGLE_LUN ), - #ifdef NO_SDDR09 UNUSUAL_DEV( 0x0436, 0x0005, 0x0100, 0x0100, "Microtech", @@ -664,19 +650,13 @@ UNUSUAL_DEV( 0x055d, 0x2020, 0x0000, 0x0210, US_SC_DEVICE, US_PR_DEVICE, NULL, US_FL_SINGLE_LUN ), - +/* We keep this entry to force the transport; firmware 3.00 and later is ok. */ UNUSUAL_DEV( 0x057b, 0x0000, 0x0000, 0x0299, "Y-E Data", "Flashbuster-U", US_SC_DEVICE, US_PR_CB, NULL, US_FL_SINGLE_LUN), -UNUSUAL_DEV( 0x057b, 0x0000, 0x0300, 0x9999, - "Y-E Data", - "Flashbuster-U", - US_SC_DEVICE, US_PR_DEVICE, NULL, - US_FL_SINGLE_LUN), - /* Reported by Johann Cardon <johann.cardon@free.fr> * This entry is needed only because the device reports * bInterfaceClass = 0xff (vendor-specific) @@ -1843,6 +1823,13 @@ UNUSUAL_DEV( 0x4102, 0x1020, 0x0100, 0x0100, US_SC_DEVICE, US_PR_DEVICE, NULL, US_FL_IGNORE_RESIDUE ), +/* Reported by Sergey Pinaev <dfo@antex.ru> */ +UNUSUAL_DEV( 0x4102, 0x1059, 0x0000, 0x0000, + "iRiver", + "P7K", + US_SC_DEVICE, US_PR_DEVICE, NULL, + US_FL_MAX_SECTORS_64 ), + /* * David Härdeman <david@2gen.com> * The key makes the SCSI stack print confusing (but harmless) messages diff --git a/drivers/usb/usb-skeleton.c b/drivers/usb/usb-skeleton.c index 60ba631e99c2..b62f2bc064f6 100644 --- a/drivers/usb/usb-skeleton.c +++ b/drivers/usb/usb-skeleton.c @@ -18,7 +18,7 @@ #include <linux/slab.h> #include <linux/module.h> #include <linux/kref.h> -#include <asm/uaccess.h> +#include <linux/uaccess.h> #include <linux/usb.h> #include <linux/mutex.h> @@ -28,7 +28,7 @@ #define USB_SKEL_PRODUCT_ID 0xfff0 /* table of devices that work with this driver */ -static struct usb_device_id skel_table [] = { +static struct usb_device_id skel_table[] = { { USB_DEVICE(USB_SKEL_VENDOR_ID, USB_SKEL_PRODUCT_ID) }, { } /* Terminating entry */ }; @@ -52,15 +52,21 @@ struct usb_skel { struct usb_interface *interface; /* the interface for this device */ struct semaphore limit_sem; /* limiting the number of writes in progress */ struct usb_anchor submitted; /* in case we need to retract our submissions */ + struct urb *bulk_in_urb; /* the urb to read data with */ unsigned char *bulk_in_buffer; /* the buffer to receive data */ size_t bulk_in_size; /* the size of the receive buffer */ + size_t bulk_in_filled; /* number of bytes in the buffer */ + size_t bulk_in_copied; /* already copied to user space */ __u8 bulk_in_endpointAddr; /* the address of the bulk in endpoint */ __u8 bulk_out_endpointAddr; /* the address of the bulk out endpoint */ int errors; /* the last request tanked */ int open_count; /* count the number of openers */ + bool ongoing_read; /* a read is going on */ + bool processed_urb; /* indicates we haven't processed the urb */ spinlock_t err_lock; /* lock for errors */ struct kref kref; struct mutex io_mutex; /* synchronize I/O with disconnect */ + struct completion bulk_in_completion; /* to wait for an ongoing read */ }; #define to_skel_dev(d) container_of(d, struct usb_skel, kref) @@ -71,6 +77,7 @@ static void skel_delete(struct kref *kref) { struct usb_skel *dev = to_skel_dev(kref); + usb_free_urb(dev->bulk_in_urb); usb_put_dev(dev->udev); kfree(dev->bulk_in_buffer); kfree(dev); @@ -87,7 +94,7 @@ static int skel_open(struct inode *inode, struct file *file) interface = usb_find_interface(&skel_driver, subminor); if (!interface) { - err ("%s - error, can't find device for minor %d", + err("%s - error, can't find device for minor %d", __func__, subminor); retval = -ENODEV; goto exit; @@ -174,38 +181,190 @@ static int skel_flush(struct file *file, fl_owner_t id) return res; } -static ssize_t skel_read(struct file *file, char *buffer, size_t count, loff_t *ppos) +static void skel_read_bulk_callback(struct urb *urb) { struct usb_skel *dev; - int retval; - int bytes_read; + + dev = urb->context; + + spin_lock(&dev->err_lock); + /* sync/async unlink faults aren't errors */ + if (urb->status) { + if (!(urb->status == -ENOENT || + urb->status == -ECONNRESET || + urb->status == -ESHUTDOWN)) + err("%s - nonzero write bulk status received: %d", + __func__, urb->status); + + dev->errors = urb->status; + } else { + dev->bulk_in_filled = urb->actual_length; + } + dev->ongoing_read = 0; + spin_unlock(&dev->err_lock); + + complete(&dev->bulk_in_completion); +} + +static int skel_do_read_io(struct usb_skel *dev, size_t count) +{ + int rv; + + /* prepare a read */ + usb_fill_bulk_urb(dev->bulk_in_urb, + dev->udev, + usb_rcvbulkpipe(dev->udev, + dev->bulk_in_endpointAddr), + dev->bulk_in_buffer, + min(dev->bulk_in_size, count), + skel_read_bulk_callback, + dev); + /* tell everybody to leave the URB alone */ + spin_lock_irq(&dev->err_lock); + dev->ongoing_read = 1; + spin_unlock_irq(&dev->err_lock); + + /* do it */ + rv = usb_submit_urb(dev->bulk_in_urb, GFP_KERNEL); + if (rv < 0) { + err("%s - failed submitting read urb, error %d", + __func__, rv); + dev->bulk_in_filled = 0; + rv = (rv == -ENOMEM) ? rv : -EIO; + spin_lock_irq(&dev->err_lock); + dev->ongoing_read = 0; + spin_unlock_irq(&dev->err_lock); + } + + return rv; +} + +static ssize_t skel_read(struct file *file, char *buffer, size_t count, + loff_t *ppos) +{ + struct usb_skel *dev; + int rv; + bool ongoing_io; dev = (struct usb_skel *)file->private_data; - mutex_lock(&dev->io_mutex); + /* if we cannot read at all, return EOF */ + if (!dev->bulk_in_urb || !count) + return 0; + + /* no concurrent readers */ + rv = mutex_lock_interruptible(&dev->io_mutex); + if (rv < 0) + return rv; + if (!dev->interface) { /* disconnect() was called */ - retval = -ENODEV; + rv = -ENODEV; goto exit; } - /* do a blocking bulk read to get data from the device */ - retval = usb_bulk_msg(dev->udev, - usb_rcvbulkpipe(dev->udev, dev->bulk_in_endpointAddr), - dev->bulk_in_buffer, - min(dev->bulk_in_size, count), - &bytes_read, 10000); - - /* if the read was successful, copy the data to userspace */ - if (!retval) { - if (copy_to_user(buffer, dev->bulk_in_buffer, bytes_read)) - retval = -EFAULT; - else - retval = bytes_read; + /* if IO is under way, we must not touch things */ +retry: + spin_lock_irq(&dev->err_lock); + ongoing_io = dev->ongoing_read; + spin_unlock_irq(&dev->err_lock); + + if (ongoing_io) { + /* nonblocking IO shall not wait */ + if (file->f_flags & O_NONBLOCK) { + rv = -EAGAIN; + goto exit; + } + /* + * IO may take forever + * hence wait in an interruptible state + */ + rv = wait_for_completion_interruptible(&dev->bulk_in_completion); + if (rv < 0) + goto exit; + /* + * by waiting we also semiprocessed the urb + * we must finish now + */ + dev->bulk_in_copied = 0; + dev->processed_urb = 1; + } + + if (!dev->processed_urb) { + /* + * the URB hasn't been processed + * do it now + */ + wait_for_completion(&dev->bulk_in_completion); + dev->bulk_in_copied = 0; + dev->processed_urb = 1; + } + + /* errors must be reported */ + rv = dev->errors; + if (rv < 0) { + /* any error is reported once */ + dev->errors = 0; + /* to preserve notifications about reset */ + rv = (rv == -EPIPE) ? rv : -EIO; + /* no data to deliver */ + dev->bulk_in_filled = 0; + /* report it */ + goto exit; } + /* + * if the buffer is filled we may satisfy the read + * else we need to start IO + */ + + if (dev->bulk_in_filled) { + /* we had read data */ + size_t available = dev->bulk_in_filled - dev->bulk_in_copied; + size_t chunk = min(available, count); + + if (!available) { + /* + * all data has been used + * actual IO needs to be done + */ + rv = skel_do_read_io(dev, count); + if (rv < 0) + goto exit; + else + goto retry; + } + /* + * data is available + * chunk tells us how much shall be copied + */ + + if (copy_to_user(buffer, + dev->bulk_in_buffer + dev->bulk_in_copied, + chunk)) + rv = -EFAULT; + else + rv = chunk; + + dev->bulk_in_copied += chunk; + + /* + * if we are asked for more than we have, + * we start IO but don't wait + */ + if (available < count) + skel_do_read_io(dev, count - chunk); + } else { + /* no data in the buffer */ + rv = skel_do_read_io(dev, count); + if (rv < 0) + goto exit; + else if (!file->f_flags & O_NONBLOCK) + goto retry; + rv = -EAGAIN; + } exit: mutex_unlock(&dev->io_mutex); - return retval; + return rv; } static void skel_write_bulk_callback(struct urb *urb) @@ -216,7 +375,7 @@ static void skel_write_bulk_callback(struct urb *urb) /* sync/async unlink faults aren't errors */ if (urb->status) { - if(!(urb->status == -ENOENT || + if (!(urb->status == -ENOENT || urb->status == -ECONNRESET || urb->status == -ESHUTDOWN)) err("%s - nonzero write bulk status received: %d", @@ -233,7 +392,8 @@ static void skel_write_bulk_callback(struct urb *urb) up(&dev->limit_sem); } -static ssize_t skel_write(struct file *file, const char *user_buffer, size_t count, loff_t *ppos) +static ssize_t skel_write(struct file *file, const char *user_buffer, + size_t count, loff_t *ppos) { struct usb_skel *dev; int retval = 0; @@ -247,14 +407,25 @@ static ssize_t skel_write(struct file *file, const char *user_buffer, size_t cou if (count == 0) goto exit; - /* limit the number of URBs in flight to stop a user from using up all RAM */ - if (down_interruptible(&dev->limit_sem)) { - retval = -ERESTARTSYS; - goto exit; + /* + * limit the number of URBs in flight to stop a user from using up all + * RAM + */ + if (!file->f_flags & O_NONBLOCK) { + if (down_interruptible(&dev->limit_sem)) { + retval = -ERESTARTSYS; + goto exit; + } + } else { + if (down_trylock(&dev->limit_sem)) { + retval = -EAGAIN; + goto exit; + } } spin_lock_irq(&dev->err_lock); - if ((retval = dev->errors) < 0) { + retval = dev->errors; + if (retval < 0) { /* any error is reported once */ dev->errors = 0; /* to preserve notifications about reset */ @@ -271,7 +442,8 @@ static ssize_t skel_write(struct file *file, const char *user_buffer, size_t cou goto error; } - buf = usb_buffer_alloc(dev->udev, writesize, GFP_KERNEL, &urb->transfer_dma); + buf = usb_buffer_alloc(dev->udev, writesize, GFP_KERNEL, + &urb->transfer_dma); if (!buf) { retval = -ENOMEM; goto error; @@ -301,11 +473,15 @@ static ssize_t skel_write(struct file *file, const char *user_buffer, size_t cou retval = usb_submit_urb(urb, GFP_KERNEL); mutex_unlock(&dev->io_mutex); if (retval) { - err("%s - failed submitting write urb, error %d", __func__, retval); + err("%s - failed submitting write urb, error %d", __func__, + retval); goto error_unanchor; } - /* release our reference to this urb, the USB core will eventually free it entirely */ + /* + * release our reference to this urb, the USB core will eventually free + * it entirely + */ usb_free_urb(urb); @@ -343,7 +519,8 @@ static struct usb_class_driver skel_class = { .minor_base = USB_SKEL_MINOR_BASE, }; -static int skel_probe(struct usb_interface *interface, const struct usb_device_id *id) +static int skel_probe(struct usb_interface *interface, + const struct usb_device_id *id) { struct usb_skel *dev; struct usb_host_interface *iface_desc; @@ -363,6 +540,7 @@ static int skel_probe(struct usb_interface *interface, const struct usb_device_i mutex_init(&dev->io_mutex); spin_lock_init(&dev->err_lock); init_usb_anchor(&dev->submitted); + init_completion(&dev->bulk_in_completion); dev->udev = usb_get_dev(interface_to_usbdev(interface)); dev->interface = interface; @@ -384,6 +562,11 @@ static int skel_probe(struct usb_interface *interface, const struct usb_device_i err("Could not allocate bulk_in_buffer"); goto error; } + dev->bulk_in_urb = usb_alloc_urb(0, GFP_KERNEL); + if (!dev->bulk_in_urb) { + err("Could not allocate bulk_in_urb"); + goto error; + } } if (!dev->bulk_out_endpointAddr && @@ -453,6 +636,7 @@ static void skel_draw_down(struct usb_skel *dev) time = usb_wait_anchor_empty_timeout(&dev->submitted, 1000); if (!time) usb_kill_anchored_urbs(&dev->submitted); + usb_kill_urb(dev->bulk_in_urb); } static int skel_suspend(struct usb_interface *intf, pm_message_t message) @@ -465,7 +649,7 @@ static int skel_suspend(struct usb_interface *intf, pm_message_t message) return 0; } -static int skel_resume (struct usb_interface *intf) +static int skel_resume(struct usb_interface *intf) { return 0; } diff --git a/drivers/usb/wusbcore/security.c b/drivers/usb/wusbcore/security.c index b2f149fedcc5..4516c36436e6 100644 --- a/drivers/usb/wusbcore/security.c +++ b/drivers/usb/wusbcore/security.c @@ -200,35 +200,40 @@ int wusb_dev_sec_add(struct wusbhc *wusbhc, { int result, bytes, secd_size; struct device *dev = &usb_dev->dev; - struct usb_security_descriptor secd; + struct usb_security_descriptor *secd; const struct usb_encryption_descriptor *etd, *ccm1_etd = NULL; - void *secd_buf; const void *itr, *top; char buf[64]; + secd = kmalloc(sizeof(struct usb_security_descriptor), GFP_KERNEL); + if (secd == NULL) { + result = -ENOMEM; + goto out; + } + result = usb_get_descriptor(usb_dev, USB_DT_SECURITY, - 0, &secd, sizeof(secd)); + 0, secd, sizeof(struct usb_security_descriptor)); if (result < sizeof(secd)) { dev_err(dev, "Can't read security descriptor or " "not enough data: %d\n", result); - goto error_secd; + goto out; } - secd_size = le16_to_cpu(secd.wTotalLength); - secd_buf = kmalloc(secd_size, GFP_KERNEL); - if (secd_buf == NULL) { + secd_size = le16_to_cpu(secd->wTotalLength); + secd = krealloc(secd, secd_size, GFP_KERNEL); + if (secd == NULL) { dev_err(dev, "Can't allocate space for security descriptors\n"); - goto error_secd_alloc; + goto out; } result = usb_get_descriptor(usb_dev, USB_DT_SECURITY, - 0, secd_buf, secd_size); + 0, secd, secd_size); if (result < secd_size) { dev_err(dev, "Can't read security descriptor or " "not enough data: %d\n", result); - goto error_secd_all; + goto out; } bytes = 0; - itr = secd_buf + sizeof(secd); - top = secd_buf + result; + itr = &secd[1]; + top = (void *)secd + result; while (itr < top) { etd = itr; if (top - itr < sizeof(*etd)) { @@ -259,24 +264,16 @@ int wusb_dev_sec_add(struct wusbhc *wusbhc, dev_err(dev, "WUSB device doesn't support CCM1 encryption, " "can't use!\n"); result = -EINVAL; - goto error_no_ccm1; + goto out; } wusb_dev->ccm1_etd = *ccm1_etd; dev_dbg(dev, "supported encryption: %s; using %s (0x%02x/%02x)\n", buf, wusb_et_name(ccm1_etd->bEncryptionType), ccm1_etd->bEncryptionValue, ccm1_etd->bAuthKeyIndex); result = 0; - kfree(secd_buf); out: + kfree(secd); return result; - - -error_no_ccm1: -error_secd_all: - kfree(secd_buf); -error_secd_alloc: -error_secd: - goto out; } void wusb_dev_sec_rm(struct wusb_dev *wusb_dev) diff --git a/drivers/uwb/uwb-debug.c b/drivers/uwb/uwb-debug.c index 4a42993700c1..2eecec0c13c9 100644 --- a/drivers/uwb/uwb-debug.c +++ b/drivers/uwb/uwb-debug.c @@ -205,7 +205,7 @@ static ssize_t command_write(struct file *file, const char __user *buf, return ret < 0 ? ret : len; } -static struct file_operations command_fops = { +static const struct file_operations command_fops = { .open = command_open, .write = command_write, .read = NULL, @@ -255,7 +255,7 @@ static int reservations_open(struct inode *inode, struct file *file) return single_open(file, reservations_print, inode->i_private); } -static struct file_operations reservations_fops = { +static const struct file_operations reservations_fops = { .open = reservations_open, .read = seq_read, .llseek = seq_lseek, @@ -283,7 +283,7 @@ static int drp_avail_open(struct inode *inode, struct file *file) return single_open(file, drp_avail_print, inode->i_private); } -static struct file_operations drp_avail_fops = { +static const struct file_operations drp_avail_fops = { .open = drp_avail_open, .read = seq_read, .llseek = seq_lseek, diff --git a/drivers/uwb/whc-rc.c b/drivers/uwb/whc-rc.c index 1d9a6f54658e..01950c62dc8d 100644 --- a/drivers/uwb/whc-rc.c +++ b/drivers/uwb/whc-rc.c @@ -42,6 +42,7 @@ #include <linux/init.h> #include <linux/module.h> #include <linux/pci.h> +#include <linux/sched.h> #include <linux/dma-mapping.h> #include <linux/interrupt.h> #include <linux/workqueue.h> diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index 11af4cb8924e..188e1ba3b69f 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -1275,26 +1275,6 @@ config FB_MATROX_MAVEN painting procedures (the secondary head does not use acceleration engine). -config FB_MATROX_MULTIHEAD - bool "Multihead support" - depends on FB_MATROX - ---help--- - Say Y here if you have more than one (supported) Matrox device in - your computer and you want to use all of them for different monitors - ("multihead"). If you have only one device, you should say N because - the driver compiled with Y is larger and a bit slower, especially on - ia32 (ix86). - - If you said M to "Matrox unified accelerated driver" and N here, you - will still be able to use several Matrox devices simultaneously: - insert several instances of the module matroxfb into the kernel - with insmod, supplying the parameter "dev=N" where N is 0, 1, etc. - for the different Matrox devices. This method is slightly faster but - uses 40 KB of kernel memory per Matrox card. - - There is no need for enabling 'Matrox multihead support' if you have - only one Matrox card in the box. - config FB_RADEON tristate "ATI Radeon display support" depends on FB && PCI @@ -2041,6 +2021,17 @@ config FB_SH7760 and 8, 15 or 16 bpp color; 90 degrees clockwise display rotation for panels <= 320 pixel horizontal resolution. +config FB_DA8XX + tristate "DA8xx/OMAP-L1xx Framebuffer support" + depends on FB && ARCH_DAVINCI_DA8XX + select FB_CFB_FILLRECT + select FB_CFB_COPYAREA + select FB_CFB_IMAGEBLIT + ---help--- + This is the frame buffer device driver for the TI LCD controller + found on DA8xx/OMAP-L1xx SoCs. + If unsure, say N. + config FB_VIRTUAL tristate "Virtual Frame Buffer support (ONLY FOR TESTING!)" depends on FB @@ -2117,13 +2108,32 @@ config FB_MB862XX_LIME ---help--- Framebuffer support for Fujitsu Lime GDC on host CPU bus. +config FB_EP93XX + tristate "EP93XX frame buffer support" + depends on FB && ARCH_EP93XX + select FB_CFB_FILLRECT + select FB_CFB_COPYAREA + select FB_CFB_IMAGEBLIT + ---help--- + Framebuffer driver for the Cirrus Logic EP93XX series of processors. + This driver is also available as a module. The module will be called + ep93xx-fb. + config FB_PRE_INIT_FB bool "Don't reinitialize, use bootloader's GDC/Display configuration" - depends on FB_MB862XX_LIME + depends on FB && FB_MB862XX_LIME ---help--- Select this option if display contents should be inherited as set by the bootloader. +config FB_MSM + tristate + depends on FB && ARCH_MSM + select FB_CFB_FILLRECT + select FB_CFB_COPYAREA + select FB_CFB_IMAGEBLIT + default y + config FB_MX3 tristate "MX3 Framebuffer support" depends on FB && MX3_IPU diff --git a/drivers/video/Makefile b/drivers/video/Makefile index 01a819f47371..80232e124889 100644 --- a/drivers/video/Makefile +++ b/drivers/video/Makefile @@ -85,6 +85,7 @@ obj-$(CONFIG_FB_Q40) += q40fb.o obj-$(CONFIG_FB_TGA) += tgafb.o obj-$(CONFIG_FB_HP300) += hpfb.o obj-$(CONFIG_FB_G364) += g364fb.o +obj-$(CONFIG_FB_EP93XX) += ep93xx-fb.o obj-$(CONFIG_FB_SA1100) += sa1100fb.o obj-$(CONFIG_FB_HIT) += hitfb.o obj-$(CONFIG_FB_EPSON1355) += epson1355fb.o @@ -126,6 +127,7 @@ obj-$(CONFIG_FB_OMAP) += omap/ obj-$(CONFIG_XEN_FBDEV_FRONTEND) += xen-fbfront.o obj-$(CONFIG_FB_CARMINE) += carminefb.o obj-$(CONFIG_FB_MB862XX) += mb862xx/ +obj-$(CONFIG_FB_MSM) += msm/ # Platform or fallback drivers go here obj-$(CONFIG_FB_UVESA) += uvesafb.o @@ -136,6 +138,7 @@ obj-$(CONFIG_FB_OF) += offb.o obj-$(CONFIG_FB_BF54X_LQ043) += bf54x-lq043fb.o obj-$(CONFIG_FB_BFIN_T350MCQB) += bfin-t350mcqb-fb.o obj-$(CONFIG_FB_MX3) += mx3fb.o +obj-$(CONFIG_FB_DA8XX) += da8xx-fb.o # the test framebuffer is last obj-$(CONFIG_FB_VIRTUAL) += vfb.o diff --git a/drivers/video/atafb.c b/drivers/video/atafb.c index 8cd279be74e5..37624f74e88b 100644 --- a/drivers/video/atafb.c +++ b/drivers/video/atafb.c @@ -329,12 +329,6 @@ extern unsigned char fontdata_8x16[]; * * * perform fb specific mmap * * int (*fb_mmap)(struct fb_info *info, struct vm_area_struct *vma); - * - * * save current hardware state * - * void (*fb_save_state)(struct fb_info *info); - * - * * restore saved state * - * void (*fb_restore_state)(struct fb_info *info); * } ; */ diff --git a/drivers/video/atmel_lcdfb.c b/drivers/video/atmel_lcdfb.c index 2830ffd72976..d5e801076d33 100644 --- a/drivers/video/atmel_lcdfb.c +++ b/drivers/video/atmel_lcdfb.c @@ -484,6 +484,7 @@ static int atmel_lcdfb_set_par(struct fb_info *info) unsigned long value; unsigned long clk_value_khz; unsigned long bits_per_line; + unsigned long pix_factor = 2; might_sleep(); @@ -516,20 +517,24 @@ static int atmel_lcdfb_set_par(struct fb_info *info) /* Now, the LCDC core... */ /* Set pixel clock */ + if (cpu_is_at91sam9g45() && !cpu_is_at91sam9g45es()) + pix_factor = 1; + clk_value_khz = clk_get_rate(sinfo->lcdc_clk) / 1000; value = DIV_ROUND_UP(clk_value_khz, PICOS2KHZ(info->var.pixclock)); - if (value < 2) { + if (value < pix_factor) { dev_notice(info->device, "Bypassing pixel clock divider\n"); lcdc_writel(sinfo, ATMEL_LCDC_LCDCON1, ATMEL_LCDC_BYPASS); } else { - value = (value / 2) - 1; + value = (value / pix_factor) - 1; dev_dbg(info->device, " * programming CLKVAL = 0x%08lx\n", value); lcdc_writel(sinfo, ATMEL_LCDC_LCDCON1, value << ATMEL_LCDC_CLKVAL_OFFSET); - info->var.pixclock = KHZ2PICOS(clk_value_khz / (2 * (value + 1))); + info->var.pixclock = + KHZ2PICOS(clk_value_khz / (pix_factor * (value + 1))); dev_dbg(info->device, " updated pixclk: %lu KHz\n", PICOS2KHZ(info->var.pixclock)); } diff --git a/drivers/video/aty/atyfb_base.c b/drivers/video/aty/atyfb_base.c index 63d3739d43a8..913b4a47ae52 100644 --- a/drivers/video/aty/atyfb_base.c +++ b/drivers/video/aty/atyfb_base.c @@ -132,7 +132,7 @@ #endif #define PRINTKI(fmt, args...) printk(KERN_INFO "atyfb: " fmt, ## args) -#define PRINTKE(fmt, args...) printk(KERN_ERR "atyfb: " fmt, ## args) +#define PRINTKE(fmt, args...) printk(KERN_ERR "atyfb: " fmt, ## args) #if defined(CONFIG_PM) || defined(CONFIG_PMAC_BACKLIGHT) || \ defined (CONFIG_FB_ATY_GENERIC_LCD) || defined(CONFIG_FB_ATY_BACKLIGHT) @@ -188,24 +188,23 @@ u32 aty_ld_lcd(int index, const struct atyfb_par *par) */ static void ATIReduceRatio(int *Numerator, int *Denominator) { - int Multiplier, Divider, Remainder; + int Multiplier, Divider, Remainder; - Multiplier = *Numerator; - Divider = *Denominator; + Multiplier = *Numerator; + Divider = *Denominator; - while ((Remainder = Multiplier % Divider)) - { - Multiplier = Divider; - Divider = Remainder; - } + while ((Remainder = Multiplier % Divider)) { + Multiplier = Divider; + Divider = Remainder; + } - *Numerator /= Divider; - *Denominator /= Divider; + *Numerator /= Divider; + *Denominator /= Divider; } #endif - /* - * The Hardware parameters for each card - */ +/* + * The Hardware parameters for each card + */ struct pci_mmap_map { unsigned long voff; @@ -223,17 +222,19 @@ static struct fb_fix_screeninfo atyfb_fix __devinitdata = { .ypanstep = 1, }; - /* - * Frame buffer device API - */ +/* + * Frame buffer device API + */ static int atyfb_open(struct fb_info *info, int user); static int atyfb_release(struct fb_info *info, int user); -static int atyfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info); +static int atyfb_check_var(struct fb_var_screeninfo *var, + struct fb_info *info); static int atyfb_set_par(struct fb_info *info); static int atyfb_setcolreg(u_int regno, u_int red, u_int green, u_int blue, - u_int transp, struct fb_info *info); -static int atyfb_pan_display(struct fb_var_screeninfo *var, struct fb_info *info); + u_int transp, struct fb_info *info); +static int atyfb_pan_display(struct fb_var_screeninfo *var, + struct fb_info *info); static int atyfb_blank(int blank, struct fb_info *info); static int atyfb_ioctl(struct fb_info *info, u_int cmd, u_long arg); #ifdef __sparc__ @@ -241,9 +242,9 @@ static int atyfb_mmap(struct fb_info *info, struct vm_area_struct *vma); #endif static int atyfb_sync(struct fb_info *info); - /* - * Internal routines - */ +/* + * Internal routines + */ static int aty_init(struct fb_info *info); @@ -254,8 +255,11 @@ static int store_video_par(char *videopar, unsigned char m64_num); static void aty_get_crtc(const struct atyfb_par *par, struct crtc *crtc); static void aty_set_crtc(const struct atyfb_par *par, const struct crtc *crtc); -static int aty_var_to_crtc(const struct fb_info *info, const struct fb_var_screeninfo *var, struct crtc *crtc); -static int aty_crtc_to_var(const struct crtc *crtc, struct fb_var_screeninfo *var); +static int aty_var_to_crtc(const struct fb_info *info, + const struct fb_var_screeninfo *var, + struct crtc *crtc); +static int aty_crtc_to_var(const struct crtc *crtc, + struct fb_var_screeninfo *var); static void set_off_pitch(struct atyfb_par *par, const struct fb_info *info); #ifdef CONFIG_PPC static int read_aty_sense(const struct atyfb_par *par); @@ -264,9 +268,9 @@ static int read_aty_sense(const struct atyfb_par *par); static DEFINE_MUTEX(reboot_lock); static struct fb_info *reboot_info; - /* - * Interface used by the world - */ +/* + * Interface used by the world + */ static struct fb_var_screeninfo default_var = { /* 640x480, 60 Hz, Non-Interlaced (25.175 MHz dotclock) */ @@ -452,14 +456,14 @@ static int __devinit correct_chipset(struct atyfb_par *par) type = chip_id & CFG_CHIP_TYPE; rev = (chip_id & CFG_CHIP_REV) >> 24; - switch(par->pci_id) { + switch (par->pci_id) { #ifdef CONFIG_FB_ATY_GX case PCI_CHIP_MACH64GX: - if(type != 0x00d7) + if (type != 0x00d7) return -ENODEV; break; case PCI_CHIP_MACH64CX: - if(type != 0x0057) + if (type != 0x0057) return -ENODEV; break; #endif @@ -564,7 +568,8 @@ static char *aty_xl_ram[8] __devinitdata = { }; #endif /* CONFIG_FB_ATY_CT */ -static u32 atyfb_get_pixclock(struct fb_var_screeninfo *var, struct atyfb_par *par) +static u32 atyfb_get_pixclock(struct fb_var_screeninfo *var, + struct atyfb_par *par) { u32 pixclock = var->pixclock; #ifdef CONFIG_FB_ATY_GENERIC_LCD @@ -572,7 +577,7 @@ static u32 atyfb_get_pixclock(struct fb_var_screeninfo *var, struct atyfb_par *p par->pll.ct.xres = 0; if (par->lcd_table != 0) { lcd_on_off = aty_ld_lcd(LCD_GEN_CNTL, par); - if(lcd_on_off & LCD_ON) { + if (lcd_on_off & LCD_ON) { par->pll.ct.xres = var->xres; pixclock = par->lcd_pixclock; } @@ -584,7 +589,7 @@ static u32 atyfb_get_pixclock(struct fb_var_screeninfo *var, struct atyfb_par *p #if defined(CONFIG_PPC) /* - * Apple monitor sense + * Apple monitor sense */ static int __devinit read_aty_sense(const struct atyfb_par *par) @@ -625,16 +630,16 @@ static int __devinit read_aty_sense(const struct atyfb_par *par) /* ------------------------------------------------------------------------- */ /* - * CRTC programming + * CRTC programming */ static void aty_get_crtc(const struct atyfb_par *par, struct crtc *crtc) { #ifdef CONFIG_FB_ATY_GENERIC_LCD if (par->lcd_table != 0) { - if(!M64_HAS(LT_LCD_REGS)) { - crtc->lcd_index = aty_ld_le32(LCD_INDEX, par); - aty_st_le32(LCD_INDEX, crtc->lcd_index, par); + if (!M64_HAS(LT_LCD_REGS)) { + crtc->lcd_index = aty_ld_le32(LCD_INDEX, par); + aty_st_le32(LCD_INDEX, crtc->lcd_index, par); } crtc->lcd_config_panel = aty_ld_lcd(CNFG_PANEL, par); crtc->lcd_gen_cntl = aty_ld_lcd(LCD_GEN_CNTL, par); @@ -642,7 +647,7 @@ static void aty_get_crtc(const struct atyfb_par *par, struct crtc *crtc) /* switch to non shadow registers */ aty_st_lcd(LCD_GEN_CNTL, crtc->lcd_gen_cntl & - ~(CRTC_RW_SELECT | SHADOW_EN | SHADOW_RW_EN), par); + ~(CRTC_RW_SELECT | SHADOW_EN | SHADOW_RW_EN), par); /* save stretching */ crtc->horz_stretching = aty_ld_lcd(HORZ_STRETCHING, par); @@ -663,7 +668,7 @@ static void aty_get_crtc(const struct atyfb_par *par, struct crtc *crtc) if (par->lcd_table != 0) { /* switch to shadow registers */ aty_st_lcd(LCD_GEN_CNTL, (crtc->lcd_gen_cntl & ~CRTC_RW_SELECT) | - SHADOW_EN | SHADOW_RW_EN, par); + SHADOW_EN | SHADOW_RW_EN, par); crtc->shadow_h_tot_disp = aty_ld_le32(CRTC_H_TOTAL_DISP, par); crtc->shadow_h_sync_strt_wid = aty_ld_le32(CRTC_H_SYNC_STRT_WID, par); @@ -680,21 +685,20 @@ static void aty_set_crtc(const struct atyfb_par *par, const struct crtc *crtc) #ifdef CONFIG_FB_ATY_GENERIC_LCD if (par->lcd_table != 0) { /* stop CRTC */ - aty_st_le32(CRTC_GEN_CNTL, crtc->gen_cntl & ~(CRTC_EXT_DISP_EN | CRTC_EN), par); + aty_st_le32(CRTC_GEN_CNTL, crtc->gen_cntl & + ~(CRTC_EXT_DISP_EN | CRTC_EN), par); /* update non-shadow registers first */ aty_st_lcd(CNFG_PANEL, crtc->lcd_config_panel, par); aty_st_lcd(LCD_GEN_CNTL, crtc->lcd_gen_cntl & - ~(CRTC_RW_SELECT | SHADOW_EN | SHADOW_RW_EN), par); + ~(CRTC_RW_SELECT | SHADOW_EN | SHADOW_RW_EN), par); /* temporarily disable stretching */ - aty_st_lcd(HORZ_STRETCHING, - crtc->horz_stretching & - ~(HORZ_STRETCH_MODE | HORZ_STRETCH_EN), par); - aty_st_lcd(VERT_STRETCHING, - crtc->vert_stretching & - ~(VERT_STRETCH_RATIO1 | VERT_STRETCH_RATIO2 | - VERT_STRETCH_USE0 | VERT_STRETCH_EN), par); + aty_st_lcd(HORZ_STRETCHING, crtc->horz_stretching & + ~(HORZ_STRETCH_MODE | HORZ_STRETCH_EN), par); + aty_st_lcd(VERT_STRETCHING, crtc->vert_stretching & + ~(VERT_STRETCH_RATIO1 | VERT_STRETCH_RATIO2 | + VERT_STRETCH_USE0 | VERT_STRETCH_EN), par); } #endif /* turn off CRT */ @@ -702,17 +706,19 @@ static void aty_set_crtc(const struct atyfb_par *par, const struct crtc *crtc) DPRINTK("setting up CRTC\n"); DPRINTK("set primary CRT to %ix%i %c%c composite %c\n", - ((((crtc->h_tot_disp>>16) & 0xff) + 1)<<3), (((crtc->v_tot_disp>>16) & 0x7ff) + 1), - (crtc->h_sync_strt_wid & 0x200000)?'N':'P', (crtc->v_sync_strt_wid & 0x200000)?'N':'P', - (crtc->gen_cntl & CRTC_CSYNC_EN)?'P':'N'); - - DPRINTK("CRTC_H_TOTAL_DISP: %x\n",crtc->h_tot_disp); - DPRINTK("CRTC_H_SYNC_STRT_WID: %x\n",crtc->h_sync_strt_wid); - DPRINTK("CRTC_V_TOTAL_DISP: %x\n",crtc->v_tot_disp); - DPRINTK("CRTC_V_SYNC_STRT_WID: %x\n",crtc->v_sync_strt_wid); + ((((crtc->h_tot_disp >> 16) & 0xff) + 1) << 3), + (((crtc->v_tot_disp >> 16) & 0x7ff) + 1), + (crtc->h_sync_strt_wid & 0x200000) ? 'N' : 'P', + (crtc->v_sync_strt_wid & 0x200000) ? 'N' : 'P', + (crtc->gen_cntl & CRTC_CSYNC_EN) ? 'P' : 'N'); + + DPRINTK("CRTC_H_TOTAL_DISP: %x\n", crtc->h_tot_disp); + DPRINTK("CRTC_H_SYNC_STRT_WID: %x\n", crtc->h_sync_strt_wid); + DPRINTK("CRTC_V_TOTAL_DISP: %x\n", crtc->v_tot_disp); + DPRINTK("CRTC_V_SYNC_STRT_WID: %x\n", crtc->v_sync_strt_wid); DPRINTK("CRTC_OFF_PITCH: %x\n", crtc->off_pitch); DPRINTK("CRTC_VLINE_CRNT_VLINE: %x\n", crtc->vline_crnt_vline); - DPRINTK("CRTC_GEN_CNTL: %x\n",crtc->gen_cntl); + DPRINTK("CRTC_GEN_CNTL: %x\n", crtc->gen_cntl); aty_st_le32(CRTC_H_TOTAL_DISP, crtc->h_tot_disp, par); aty_st_le32(CRTC_H_SYNC_STRT_WID, crtc->h_sync_strt_wid, par); @@ -732,16 +738,22 @@ static void aty_set_crtc(const struct atyfb_par *par, const struct crtc *crtc) if (par->lcd_table != 0) { /* switch to shadow registers */ aty_st_lcd(LCD_GEN_CNTL, (crtc->lcd_gen_cntl & ~CRTC_RW_SELECT) | - (SHADOW_EN | SHADOW_RW_EN), par); + SHADOW_EN | SHADOW_RW_EN, par); DPRINTK("set shadow CRT to %ix%i %c%c\n", - ((((crtc->shadow_h_tot_disp>>16) & 0xff) + 1)<<3), (((crtc->shadow_v_tot_disp>>16) & 0x7ff) + 1), - (crtc->shadow_h_sync_strt_wid & 0x200000)?'N':'P', (crtc->shadow_v_sync_strt_wid & 0x200000)?'N':'P'); - - DPRINTK("SHADOW CRTC_H_TOTAL_DISP: %x\n", crtc->shadow_h_tot_disp); - DPRINTK("SHADOW CRTC_H_SYNC_STRT_WID: %x\n", crtc->shadow_h_sync_strt_wid); - DPRINTK("SHADOW CRTC_V_TOTAL_DISP: %x\n", crtc->shadow_v_tot_disp); - DPRINTK("SHADOW CRTC_V_SYNC_STRT_WID: %x\n", crtc->shadow_v_sync_strt_wid); + ((((crtc->shadow_h_tot_disp >> 16) & 0xff) + 1) << 3), + (((crtc->shadow_v_tot_disp >> 16) & 0x7ff) + 1), + (crtc->shadow_h_sync_strt_wid & 0x200000) ? 'N' : 'P', + (crtc->shadow_v_sync_strt_wid & 0x200000) ? 'N' : 'P'); + + DPRINTK("SHADOW CRTC_H_TOTAL_DISP: %x\n", + crtc->shadow_h_tot_disp); + DPRINTK("SHADOW CRTC_H_SYNC_STRT_WID: %x\n", + crtc->shadow_h_sync_strt_wid); + DPRINTK("SHADOW CRTC_V_TOTAL_DISP: %x\n", + crtc->shadow_v_tot_disp); + DPRINTK("SHADOW CRTC_V_SYNC_STRT_WID: %x\n", + crtc->shadow_v_sync_strt_wid); aty_st_le32(CRTC_H_TOTAL_DISP, crtc->shadow_h_tot_disp, par); aty_st_le32(CRTC_H_SYNC_STRT_WID, crtc->shadow_h_sync_strt_wid, par); @@ -752,16 +764,16 @@ static void aty_set_crtc(const struct atyfb_par *par, const struct crtc *crtc) DPRINTK("LCD_GEN_CNTL: %x\n", crtc->lcd_gen_cntl); DPRINTK("HORZ_STRETCHING: %x\n", crtc->horz_stretching); DPRINTK("VERT_STRETCHING: %x\n", crtc->vert_stretching); - if(!M64_HAS(LT_LCD_REGS)) - DPRINTK("EXT_VERT_STRETCH: %x\n", crtc->ext_vert_stretch); + if (!M64_HAS(LT_LCD_REGS)) + DPRINTK("EXT_VERT_STRETCH: %x\n", crtc->ext_vert_stretch); aty_st_lcd(LCD_GEN_CNTL, crtc->lcd_gen_cntl, par); aty_st_lcd(HORZ_STRETCHING, crtc->horz_stretching, par); aty_st_lcd(VERT_STRETCHING, crtc->vert_stretching, par); - if(!M64_HAS(LT_LCD_REGS)) { - aty_st_lcd(EXT_VERT_STRETCH, crtc->ext_vert_stretch, par); - aty_ld_le32(LCD_INDEX, par); - aty_st_le32(LCD_INDEX, crtc->lcd_index, par); + if (!M64_HAS(LT_LCD_REGS)) { + aty_st_lcd(EXT_VERT_STRETCH, crtc->ext_vert_stretch, par); + aty_ld_le32(LCD_INDEX, par); + aty_st_le32(LCD_INDEX, crtc->lcd_index, par); } } #endif /* CONFIG_FB_ATY_GENERIC_LCD */ @@ -779,7 +791,8 @@ static u32 calc_line_length(struct atyfb_par *par, u32 vxres, u32 bpp) } static int aty_var_to_crtc(const struct fb_info *info, - const struct fb_var_screeninfo *var, struct crtc *crtc) + const struct fb_var_screeninfo *var, + struct crtc *crtc) { struct atyfb_par *par = (struct atyfb_par *) info->par; u32 xres, yres, vxres, vyres, xoffset, yoffset, bpp; @@ -814,34 +827,32 @@ static int aty_var_to_crtc(const struct fb_info *info, if (bpp <= 8) { bpp = 8; pix_width = CRTC_PIX_WIDTH_8BPP; - dp_pix_width = - HOST_8BPP | SRC_8BPP | DST_8BPP | - BYTE_ORDER_LSB_TO_MSB; + dp_pix_width = HOST_8BPP | SRC_8BPP | DST_8BPP | + BYTE_ORDER_LSB_TO_MSB; dp_chain_mask = DP_CHAIN_8BPP; } else if (bpp <= 15) { bpp = 16; pix_width = CRTC_PIX_WIDTH_15BPP; dp_pix_width = HOST_15BPP | SRC_15BPP | DST_15BPP | - BYTE_ORDER_LSB_TO_MSB; + BYTE_ORDER_LSB_TO_MSB; dp_chain_mask = DP_CHAIN_15BPP; } else if (bpp <= 16) { bpp = 16; pix_width = CRTC_PIX_WIDTH_16BPP; dp_pix_width = HOST_16BPP | SRC_16BPP | DST_16BPP | - BYTE_ORDER_LSB_TO_MSB; + BYTE_ORDER_LSB_TO_MSB; dp_chain_mask = DP_CHAIN_16BPP; } else if (bpp <= 24 && M64_HAS(INTEGRATED)) { bpp = 24; pix_width = CRTC_PIX_WIDTH_24BPP; - dp_pix_width = - HOST_8BPP | SRC_8BPP | DST_8BPP | - BYTE_ORDER_LSB_TO_MSB; + dp_pix_width = HOST_8BPP | SRC_8BPP | DST_8BPP | + BYTE_ORDER_LSB_TO_MSB; dp_chain_mask = DP_CHAIN_24BPP; } else if (bpp <= 32) { bpp = 32; pix_width = CRTC_PIX_WIDTH_32BPP; dp_pix_width = HOST_32BPP | SRC_32BPP | DST_32BPP | - BYTE_ORDER_LSB_TO_MSB; + BYTE_ORDER_LSB_TO_MSB; dp_chain_mask = DP_CHAIN_32BPP; } else FAIL("invalid bpp"); @@ -854,9 +865,9 @@ static int aty_var_to_crtc(const struct fb_info *info, h_sync_pol = sync & FB_SYNC_HOR_HIGH_ACT ? 0 : 1; v_sync_pol = sync & FB_SYNC_VERT_HIGH_ACT ? 0 : 1; - if((xres > 1600) || (yres > 1200)) { + if ((xres > 1600) || (yres > 1200)) { FAIL("MACH64 chips are designed for max 1600x1200\n" - "select anoter resolution."); + "select anoter resolution."); } h_sync_strt = h_disp + var->right_margin; h_sync_end = h_sync_strt + var->hsync_len; @@ -869,11 +880,12 @@ static int aty_var_to_crtc(const struct fb_info *info, #ifdef CONFIG_FB_ATY_GENERIC_LCD if (par->lcd_table != 0) { - if(!M64_HAS(LT_LCD_REGS)) { - u32 lcd_index = aty_ld_le32(LCD_INDEX, par); - crtc->lcd_index = lcd_index & - ~(LCD_INDEX_MASK | LCD_DISPLAY_DIS | LCD_SRC_SEL | CRTC2_DISPLAY_DIS); - aty_st_le32(LCD_INDEX, lcd_index, par); + if (!M64_HAS(LT_LCD_REGS)) { + u32 lcd_index = aty_ld_le32(LCD_INDEX, par); + crtc->lcd_index = lcd_index & + ~(LCD_INDEX_MASK | LCD_DISPLAY_DIS | + LCD_SRC_SEL | CRTC2_DISPLAY_DIS); + aty_st_le32(LCD_INDEX, lcd_index, par); } if (!M64_HAS(MOBIL_BUS)) @@ -888,12 +900,14 @@ static int aty_var_to_crtc(const struct fb_info *info, USE_SHADOWED_ROWCUR | SHADOW_EN | SHADOW_RW_EN); crtc->lcd_gen_cntl |= DONT_SHADOW_VPAR | LOCK_8DOT; - if((crtc->lcd_gen_cntl & LCD_ON) && - ((xres > par->lcd_width) || (yres > par->lcd_height))) { - /* We cannot display the mode on the LCD. If the CRT is enabled - we can turn off the LCD. - If the CRT is off, it isn't a good idea to switch it on; we don't - know if one is connected. So it's better to fail then. + if ((crtc->lcd_gen_cntl & LCD_ON) && + ((xres > par->lcd_width) || (yres > par->lcd_height))) { + /* + * We cannot display the mode on the LCD. If the CRT is + * enabled we can turn off the LCD. + * If the CRT is off, it isn't a good idea to switch it + * on; we don't know if one is connected. So it's better + * to fail then. */ if (crtc->lcd_gen_cntl & CRT_ON) { if (!(var->activate & FB_ACTIVATE_TEST)) @@ -916,17 +930,18 @@ static int aty_var_to_crtc(const struct fb_info *info, vmode &= ~(FB_VMODE_DOUBLE | FB_VMODE_INTERLACED); - /* This is horror! When we simulate, say 640x480 on an 800x600 - LCD monitor, the CRTC should be programmed 800x600 values for - the non visible part, but 640x480 for the visible part. - This code has been tested on a laptop with it's 1400x1050 LCD - monitor and a conventional monitor both switched on. - Tested modes: 1280x1024, 1152x864, 1024x768, 800x600, - works with little glitches also with DOUBLESCAN modes + /* + * This is horror! When we simulate, say 640x480 on an 800x600 + * LCD monitor, the CRTC should be programmed 800x600 values for + * the non visible part, but 640x480 for the visible part. + * This code has been tested on a laptop with it's 1400x1050 LCD + * monitor and a conventional monitor both switched on. + * Tested modes: 1280x1024, 1152x864, 1024x768, 800x600, + * works with little glitches also with DOUBLESCAN modes */ if (yres < par->lcd_height) { VScan = par->lcd_height / yres; - if(VScan > 1) { + if (VScan > 1) { VScan = 2; vmode |= FB_VMODE_DOUBLE; } @@ -952,7 +967,7 @@ static int aty_var_to_crtc(const struct fb_info *info, FAIL_MAX("h_disp too large", h_disp, 0xff); FAIL_MAX("h_sync_strt too large", h_sync_strt, 0x1ff); /*FAIL_MAX("h_sync_wid too large", h_sync_wid, 0x1f);*/ - if(h_sync_wid > 0x1f) + if (h_sync_wid > 0x1f) h_sync_wid = 0x1f; FAIL_MAX("h_total too large", h_total, 0x1ff); @@ -978,7 +993,7 @@ static int aty_var_to_crtc(const struct fb_info *info, FAIL_MAX("v_disp too large", v_disp, 0x7ff); FAIL_MAX("v_sync_stsrt too large", v_sync_strt, 0x7ff); /*FAIL_MAX("v_sync_wid too large", v_sync_wid, 0x1f);*/ - if(v_sync_wid > 0x1f) + if (v_sync_wid > 0x1f) v_sync_wid = 0x1f; FAIL_MAX("v_total too large", v_total, 0x7ff); @@ -995,11 +1010,13 @@ static int aty_var_to_crtc(const struct fb_info *info, ((line_length / bpp) << 22); crtc->vline_crnt_vline = 0; - crtc->h_tot_disp = h_total | (h_disp<<16); - crtc->h_sync_strt_wid = (h_sync_strt & 0xff) | (h_sync_dly<<8) | - ((h_sync_strt & 0x100)<<4) | (h_sync_wid<<16) | (h_sync_pol<<21); - crtc->v_tot_disp = v_total | (v_disp<<16); - crtc->v_sync_strt_wid = v_sync_strt | (v_sync_wid<<16) | (v_sync_pol<<21); + crtc->h_tot_disp = h_total | (h_disp << 16); + crtc->h_sync_strt_wid = (h_sync_strt & 0xff) | (h_sync_dly << 8) | + ((h_sync_strt & 0x100) << 4) | (h_sync_wid << 16) | + (h_sync_pol << 21); + crtc->v_tot_disp = v_total | (v_disp << 16); + crtc->v_sync_strt_wid = v_sync_strt | (v_sync_wid << 16) | + (v_sync_pol << 21); /* crtc->gen_cntl = aty_ld_le32(CRTC_GEN_CNTL, par) & CRTC_PRESERVED_MASK; */ crtc->gen_cntl = CRTC_EXT_DISP_EN | CRTC_EN | pix_width | c_sync; @@ -1014,13 +1031,15 @@ static int aty_var_to_crtc(const struct fb_info *info, #ifdef CONFIG_FB_ATY_GENERIC_LCD if (par->lcd_table != 0) { vdisplay = yres; - if(vmode & FB_VMODE_DOUBLE) + if (vmode & FB_VMODE_DOUBLE) vdisplay <<= 1; crtc->gen_cntl &= ~(CRTC2_EN | CRTC2_PIX_WIDTH); crtc->lcd_gen_cntl &= ~(HORZ_DIVBY2_EN | DIS_HOR_CRT_DIVBY2 | - /*TVCLK_PM_EN | VCLK_DAC_PM_EN |*/ - USE_SHADOWED_VEND | USE_SHADOWED_ROWCUR | SHADOW_EN | SHADOW_RW_EN); - crtc->lcd_gen_cntl |= (DONT_SHADOW_VPAR/* | LOCK_8DOT*/); + /*TVCLK_PM_EN | VCLK_DAC_PM_EN |*/ + USE_SHADOWED_VEND | + USE_SHADOWED_ROWCUR | + SHADOW_EN | SHADOW_RW_EN); + crtc->lcd_gen_cntl |= DONT_SHADOW_VPAR/* | LOCK_8DOT*/; /* MOBILITY M1 tested, FIXME: LT */ crtc->horz_stretching = aty_ld_lcd(HORZ_STRETCHING, par); @@ -1028,28 +1047,32 @@ static int aty_var_to_crtc(const struct fb_info *info, crtc->ext_vert_stretch = aty_ld_lcd(EXT_VERT_STRETCH, par) & ~(AUTO_VERT_RATIO | VERT_STRETCH_MODE | VERT_STRETCH_RATIO3); - crtc->horz_stretching &= - ~(HORZ_STRETCH_RATIO | HORZ_STRETCH_LOOP | AUTO_HORZ_RATIO | - HORZ_STRETCH_MODE | HORZ_STRETCH_EN); + crtc->horz_stretching &= ~(HORZ_STRETCH_RATIO | + HORZ_STRETCH_LOOP | AUTO_HORZ_RATIO | + HORZ_STRETCH_MODE | HORZ_STRETCH_EN); if (xres < par->lcd_width && crtc->lcd_gen_cntl & LCD_ON) { do { /* - * The horizontal blender misbehaves when HDisplay is less than a - * a certain threshold (440 for a 1024-wide panel). It doesn't - * stretch such modes enough. Use pixel replication instead of - * blending to stretch modes that can be made to exactly fit the - * panel width. The undocumented "NoLCDBlend" option allows the - * pixel-replicated mode to be slightly wider or narrower than the - * panel width. It also causes a mode that is exactly half as wide - * as the panel to be pixel-replicated, rather than blended. - */ + * The horizontal blender misbehaves when + * HDisplay is less than a certain threshold + * (440 for a 1024-wide panel). It doesn't + * stretch such modes enough. Use pixel + * replication instead of blending to stretch + * modes that can be made to exactly fit the + * panel width. The undocumented "NoLCDBlend" + * option allows the pixel-replicated mode to + * be slightly wider or narrower than the + * panel width. It also causes a mode that is + * exactly half as wide as the panel to be + * pixel-replicated, rather than blended. + */ int HDisplay = xres & ~7; int nStretch = par->lcd_width / HDisplay; int Remainder = par->lcd_width % HDisplay; if ((!Remainder && ((nStretch > 2))) || - (((HDisplay * 16) / par->lcd_width) < 7)) { - static const char StretchLoops[] = {10, 12, 13, 15, 16}; + (((HDisplay * 16) / par->lcd_width) < 7)) { + static const char StretchLoops[] = { 10, 12, 13, 15, 16 }; int horz_stretch_loop = -1, BestRemainder; int Numerator = HDisplay, Denominator = par->lcd_width; int Index = 5; @@ -1098,12 +1121,12 @@ static int aty_var_to_crtc(const struct fb_info *info, (((vdisplay * (VERT_STRETCH_RATIO0 + 1)) / par->lcd_height) & VERT_STRETCH_RATIO0)); if (!M64_HAS(LT_LCD_REGS) && - xres <= (M64_HAS(MOBIL_BUS)?1024:800)) + xres <= (M64_HAS(MOBIL_BUS) ? 1024 : 800)) crtc->ext_vert_stretch |= VERT_STRETCH_MODE; } else { /* - * Don't use vertical blending if the mode is too wide or not - * vertically stretched. + * Don't use vertical blending if the mode is too wide + * or not vertically stretched. */ crtc->vert_stretching = 0; } @@ -1125,11 +1148,11 @@ static int aty_var_to_crtc(const struct fb_info *info, return 0; } -static int aty_crtc_to_var(const struct crtc *crtc, struct fb_var_screeninfo *var) +static int aty_crtc_to_var(const struct crtc *crtc, + struct fb_var_screeninfo *var) { u32 xres, yres, bpp, left, right, upper, lower, hslen, vslen, sync; - u32 h_total, h_disp, h_sync_strt, h_sync_dly, h_sync_wid, - h_sync_pol; + u32 h_total, h_disp, h_sync_strt, h_sync_dly, h_sync_wid, h_sync_pol; u32 v_total, v_disp, v_sync_strt, v_sync_wid, v_sync_pol, c_sync; u32 pix_width; u32 double_scan, interlace; @@ -1161,8 +1184,8 @@ static int aty_crtc_to_var(const struct crtc *crtc, struct fb_var_screeninfo *va lower = v_sync_strt - v_disp; vslen = v_sync_wid; sync = (h_sync_pol ? 0 : FB_SYNC_HOR_HIGH_ACT) | - (v_sync_pol ? 0 : FB_SYNC_VERT_HIGH_ACT) | - (c_sync ? FB_SYNC_COMP_HIGH_ACT : 0); + (v_sync_pol ? 0 : FB_SYNC_VERT_HIGH_ACT) | + (c_sync ? FB_SYNC_COMP_HIGH_ACT : 0); switch (pix_width) { #if 0 @@ -1252,20 +1275,21 @@ static int aty_crtc_to_var(const struct crtc *crtc, struct fb_var_screeninfo *va var->vsync_len = vslen; var->sync = sync; var->vmode = FB_VMODE_NONINTERLACED; - /* In double scan mode, the vertical parameters are doubled, so we need to - half them to get the right values. - In interlaced mode the values are already correct, so no correction is - necessary. + /* + * In double scan mode, the vertical parameters are doubled, + * so we need to halve them to get the right values. + * In interlaced mode the values are already correct, + * so no correction is necessary. */ if (interlace) var->vmode = FB_VMODE_INTERLACED; if (double_scan) { var->vmode = FB_VMODE_DOUBLE; - var->yres>>=1; - var->upper_margin>>=1; - var->lower_margin>>=1; - var->vsync_len>>=1; + var->yres >>= 1; + var->upper_margin >>= 1; + var->lower_margin >>= 1; + var->vsync_len >>= 1; } return 0; @@ -1286,7 +1310,8 @@ static int atyfb_set_par(struct fb_info *info) if (par->asleep) return 0; - if ((err = aty_var_to_crtc(info, var, &par->crtc))) + err = aty_var_to_crtc(info, var, &par->crtc); + if (err) return err; pixclock = atyfb_get_pixclock(var, par); @@ -1295,7 +1320,9 @@ static int atyfb_set_par(struct fb_info *info) PRINTKE("Invalid pixclock\n"); return -EINVAL; } else { - if((err = par->pll_ops->var_to_pll(info, pixclock, var->bits_per_pixel, &par->pll))) + err = par->pll_ops->var_to_pll(info, pixclock, + var->bits_per_pixel, &par->pll); + if (err) return err; } @@ -1313,22 +1340,23 @@ static int atyfb_set_par(struct fb_info *info) wait_for_idle(par); aty_set_crtc(par, &par->crtc); - par->dac_ops->set_dac(info, &par->pll, var->bits_per_pixel, par->accel_flags); + par->dac_ops->set_dac(info, &par->pll, + var->bits_per_pixel, par->accel_flags); par->pll_ops->set_pll(info, &par->pll); #ifdef DEBUG - if(par->pll_ops && par->pll_ops->pll_to_var) - pixclock_in_ps = par->pll_ops->pll_to_var(info, &(par->pll)); + if (par->pll_ops && par->pll_ops->pll_to_var) + pixclock_in_ps = par->pll_ops->pll_to_var(info, &par->pll); else pixclock_in_ps = 0; - if(0 == pixclock_in_ps) { + if (0 == pixclock_in_ps) { PRINTKE("ALERT ops->pll_to_var get 0\n"); pixclock_in_ps = pixclock; } memset(&debug, 0, sizeof(debug)); - if(!aty_crtc_to_var(&(par->crtc), &debug)) { + if (!aty_crtc_to_var(&par->crtc, &debug)) { u32 hSync, vRefresh; u32 h_disp, h_sync_strt, h_sync_end, h_total; u32 v_disp, v_sync_strt, v_sync_end, v_total; @@ -1344,16 +1372,20 @@ static int atyfb_set_par(struct fb_info *info) hSync = 1000000000 / (pixclock_in_ps * h_total); vRefresh = (hSync * 1000) / v_total; - if (par->crtc.gen_cntl & CRTC_INTERLACE_EN) - vRefresh *= 2; - if (par->crtc.gen_cntl & CRTC_DBL_SCAN_EN) - vRefresh /= 2; + if (par->crtc.gen_cntl & CRTC_INTERLACE_EN) + vRefresh *= 2; + if (par->crtc.gen_cntl & CRTC_DBL_SCAN_EN) + vRefresh /= 2; DPRINTK("atyfb_set_par\n"); - DPRINTK(" Set Visible Mode to %ix%i-%i\n", var->xres, var->yres, var->bits_per_pixel); - DPRINTK(" Virtual resolution %ix%i, pixclock_in_ps %i (calculated %i)\n", - var->xres_virtual, var->yres_virtual, pixclock, pixclock_in_ps); - DPRINTK(" Dot clock: %i MHz\n", 1000000 / pixclock_in_ps); + DPRINTK(" Set Visible Mode to %ix%i-%i\n", + var->xres, var->yres, var->bits_per_pixel); + DPRINTK(" Virtual resolution %ix%i, " + "pixclock_in_ps %i (calculated %i)\n", + var->xres_virtual, var->yres_virtual, + pixclock, pixclock_in_ps); + DPRINTK(" Dot clock: %i MHz\n", + 1000000 / pixclock_in_ps); DPRINTK(" Horizontal sync: %i kHz\n", hSync); DPRINTK(" Vertical refresh: %i Hz\n", vRefresh); DPRINTK(" x style: %i.%03i %i %i %i %i %i %i %i %i\n", @@ -1448,7 +1480,8 @@ static int atyfb_set_par(struct fb_info *info) base = 0x2000; printk("debug atyfb: Mach64 non-shadow register values:"); for (i = 0; i < 256; i = i+4) { - if(i%16 == 0) printk("\ndebug atyfb: 0x%04X: ", base + i); + if (i % 16 == 0) + printk("\ndebug atyfb: 0x%04X: ", base + i); printk(" %08X", aty_ld_le32(i, par)); } printk("\n\n"); @@ -1458,8 +1491,10 @@ static int atyfb_set_par(struct fb_info *info) base = 0x00; printk("debug atyfb: Mach64 PLL register values:"); for (i = 0; i < 64; i++) { - if(i%16 == 0) printk("\ndebug atyfb: 0x%02X: ", base + i); - if(i%4 == 0) printk(" "); + if (i % 16 == 0) + printk("\ndebug atyfb: 0x%02X: ", base + i); + if (i % 4 == 0) + printk(" "); printk("%02X", aty_ld_pll_ct(i, par)); } printk("\n\n"); @@ -1470,19 +1505,21 @@ static int atyfb_set_par(struct fb_info *info) /* LCD registers */ base = 0x00; printk("debug atyfb: LCD register values:"); - if(M64_HAS(LT_LCD_REGS)) { - for(i = 0; i <= POWER_MANAGEMENT; i++) { - if(i == EXT_VERT_STRETCH) - continue; - printk("\ndebug atyfb: 0x%04X: ", lt_lcd_regs[i]); - printk(" %08X", aty_ld_lcd(i, par)); - } - + if (M64_HAS(LT_LCD_REGS)) { + for (i = 0; i <= POWER_MANAGEMENT; i++) { + if (i == EXT_VERT_STRETCH) + continue; + printk("\ndebug atyfb: 0x%04X: ", + lt_lcd_regs[i]); + printk(" %08X", aty_ld_lcd(i, par)); + } } else { - for (i = 0; i < 64; i++) { - if(i%4 == 0) printk("\ndebug atyfb: 0x%02X: ", base + i); - printk(" %08X", aty_ld_lcd(i, par)); - } + for (i = 0; i < 64; i++) { + if (i % 4 == 0) + printk("\ndebug atyfb: 0x%02X: ", + base + i); + printk(" %08X", aty_ld_lcd(i, par)); + } } printk("\n\n"); } @@ -1500,9 +1537,10 @@ static int atyfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) union aty_pll pll; u32 pixclock; - memcpy(&pll, &(par->pll), sizeof(pll)); + memcpy(&pll, &par->pll, sizeof(pll)); - if((err = aty_var_to_crtc(info, var, &crtc))) + err = aty_var_to_crtc(info, var, &crtc); + if (err) return err; pixclock = atyfb_get_pixclock(var, par); @@ -1512,7 +1550,9 @@ static int atyfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) PRINTKE("Invalid pixclock\n"); return -EINVAL; } else { - if((err = par->pll_ops->var_to_pll(info, pixclock, var->bits_per_pixel, &pll))) + err = par->pll_ops->var_to_pll(info, pixclock, + var->bits_per_pixel, &pll); + if (err) return err; } @@ -1539,9 +1579,9 @@ static void set_off_pitch(struct atyfb_par *par, const struct fb_info *info) } - /* - * Open/Release the frame buffer device - */ +/* + * Open/Release the frame buffer device + */ static int atyfb_open(struct fb_info *info, int user) { @@ -1553,7 +1593,7 @@ static int atyfb_open(struct fb_info *info, int user) par->mmaped = 0; #endif } - return (0); + return 0; } static irqreturn_t aty_irq(int irq, void *dev_id) @@ -1568,7 +1608,8 @@ static irqreturn_t aty_irq(int irq, void *dev_id) if (int_cntl & CRTC_VBLANK_INT) { /* clear interrupt */ - aty_st_le32(CRTC_INT_CNTL, (int_cntl & CRTC_INT_EN_MASK) | CRTC_VBLANK_INT_AK, par); + aty_st_le32(CRTC_INT_CNTL, (int_cntl & CRTC_INT_EN_MASK) | + CRTC_VBLANK_INT_AK, par); par->vblank.count++; if (par->vblank.pan_display) { par->vblank.pan_display = 0; @@ -1603,9 +1644,11 @@ static int aty_enable_irq(struct atyfb_par *par, int reenable) spin_lock_irq(&par->int_lock); int_cntl = aty_ld_le32(CRTC_INT_CNTL, par) & CRTC_INT_EN_MASK; if (!(int_cntl & CRTC_VBLANK_INT_EN)) { - printk("atyfb: someone disabled IRQ [%08x]\n", int_cntl); + printk("atyfb: someone disabled IRQ [%08x]\n", + int_cntl); /* re-enable interrupt */ - aty_st_le32(CRTC_INT_CNTL, int_cntl | CRTC_VBLANK_INT_EN, par ); + aty_st_le32(CRTC_INT_CNTL, int_cntl | + CRTC_VBLANK_INT_EN, par); } spin_unlock_irq(&par->int_lock); } @@ -1625,7 +1668,7 @@ static int aty_disable_irq(struct atyfb_par *par) spin_lock_irq(&par->int_lock); int_cntl = aty_ld_le32(CRTC_INT_CNTL, par) & CRTC_INT_EN_MASK; /* disable interrupt */ - aty_st_le32(CRTC_INT_CNTL, int_cntl & ~CRTC_VBLANK_INT_EN, par ); + aty_st_le32(CRTC_INT_CNTL, int_cntl & ~CRTC_VBLANK_INT_EN, par); spin_unlock_irq(&par->int_lock); free_irq(par->irq, par); } @@ -1636,50 +1679,62 @@ static int aty_disable_irq(struct atyfb_par *par) static int atyfb_release(struct fb_info *info, int user) { struct atyfb_par *par = (struct atyfb_par *) info->par; - if (user) { - par->open--; - mdelay(1); - wait_for_idle(par); - if (!par->open) { #ifdef __sparc__ - int was_mmaped = par->mmaped; + int was_mmaped; +#endif - par->mmaped = 0; + if (!user) + return 0; - if (was_mmaped) { - struct fb_var_screeninfo var; + par->open--; + mdelay(1); + wait_for_idle(par); - /* Now reset the default display config, we have no - * idea what the program(s) which mmap'd the chip did - * to the configuration, nor whether it restored it - * correctly. - */ - var = default_var; - if (noaccel) - var.accel_flags &= ~FB_ACCELF_TEXT; - else - var.accel_flags |= FB_ACCELF_TEXT; - if (var.yres == var.yres_virtual) { - u32 videoram = (info->fix.smem_len - (PAGE_SIZE << 2)); - var.yres_virtual = ((videoram * 8) / var.bits_per_pixel) / var.xres_virtual; - if (var.yres_virtual < var.yres) - var.yres_virtual = var.yres; - } - } -#endif - aty_disable_irq(par); + if (par->open) + return 0; + +#ifdef __sparc__ + was_mmaped = par->mmaped; + + par->mmaped = 0; + + if (was_mmaped) { + struct fb_var_screeninfo var; + + /* + * Now reset the default display config, we have + * no idea what the program(s) which mmap'd the + * chip did to the configuration, nor whether it + * restored it correctly. + */ + var = default_var; + if (noaccel) + var.accel_flags &= ~FB_ACCELF_TEXT; + else + var.accel_flags |= FB_ACCELF_TEXT; + if (var.yres == var.yres_virtual) { + u32 videoram = (info->fix.smem_len - (PAGE_SIZE << 2)); + var.yres_virtual = + ((videoram * 8) / var.bits_per_pixel) / + var.xres_virtual; + if (var.yres_virtual < var.yres) + var.yres_virtual = var.yres; } } - return (0); +#endif + aty_disable_irq(par); + + return 0; } - /* - * Pan or Wrap the Display - * - * This call looks only at xoffset, yoffset and the FB_VMODE_YWRAP flag - */ +/* + * Pan or Wrap the Display + * + * This call looks only at xoffset, yoffset and the FB_VMODE_YWRAP flag + */ -static int atyfb_pan_display(struct fb_var_screeninfo *var, struct fb_info *info) +static int atyfb_pan_display(struct fb_var_screeninfo *var, + struct fb_info *info) { struct atyfb_par *par = (struct atyfb_par *) info->par; u32 xres, yres, xoffset, yoffset; @@ -1690,7 +1745,8 @@ static int atyfb_pan_display(struct fb_var_screeninfo *var, struct fb_info *info yres >>= 1; xoffset = (var->xoffset + 7) & ~7; yoffset = var->yoffset; - if (xoffset + xres > par->crtc.vxres || yoffset + yres > par->crtc.vyres) + if (xoffset + xres > par->crtc.vxres || + yoffset + yres > par->crtc.vyres) return -EINVAL; info->var.xoffset = xoffset; info->var.yoffset = yoffset; @@ -1727,10 +1783,10 @@ static int aty_waitforvblank(struct atyfb_par *par, u32 crtc) return ret; count = vbl->count; - ret = wait_event_interruptible_timeout(vbl->wait, count != vbl->count, HZ/10); - if (ret < 0) { + ret = wait_event_interruptible_timeout(vbl->wait, + count != vbl->count, HZ/10); + if (ret < 0) return ret; - } if (ret == 0) { aty_enable_irq(par, 1); return -ETIMEDOUT; @@ -1784,7 +1840,8 @@ static int atyfb_ioctl(struct fb_info *info, u_int cmd, u_long arg) fbtyp.fb_depth = info->var.bits_per_pixel; fbtyp.fb_cmsize = info->cmap.len; fbtyp.fb_size = info->fix.smem_len; - if (copy_to_user((struct fbtype __user *) arg, &fbtyp, sizeof(fbtyp))) + if (copy_to_user((struct fbtype __user *) arg, &fbtyp, + sizeof(fbtyp))) return -EFAULT; break; #endif /* __sparc__ */ @@ -1804,7 +1861,7 @@ static int atyfb_ioctl(struct fb_info *info, u_int cmd, u_long arg) case ATYIO_CLKR: if (M64_HAS(INTEGRATED)) { struct atyclk clk; - union aty_pll *pll = &(par->pll); + union aty_pll *pll = &par->pll; u32 dsp_config = pll->ct.dsp_config; u32 dsp_on_off = pll->ct.dsp_on_off; clk.ref_clk_per = par->ref_clk_per; @@ -1829,8 +1886,9 @@ static int atyfb_ioctl(struct fb_info *info, u_int cmd, u_long arg) case ATYIO_CLKW: if (M64_HAS(INTEGRATED)) { struct atyclk clk; - union aty_pll *pll = &(par->pll); - if (copy_from_user(&clk, (struct atyclk __user *) arg, sizeof(clk))) + union aty_pll *pll = &par->pll; + if (copy_from_user(&clk, (struct atyclk __user *) arg, + sizeof(clk))) return -EFAULT; par->ref_clk_per = clk.ref_clk_per; pll->ct.pll_ref_div = clk.pll_ref_div; @@ -1841,8 +1899,10 @@ static int atyfb_ioctl(struct fb_info *info, u_int cmd, u_long arg) pll->ct.vclk_fb_div = clk.vclk_fb_div; pll->ct.vclk_post_div_real = clk.vclk_post_div; pll->ct.dsp_config = (clk.dsp_xclks_per_row & 0x3fff) | - ((clk.dsp_loop_latency & 0xf)<<16)| ((clk.dsp_precision & 7)<<20); - pll->ct.dsp_on_off = (clk.dsp_off & 0x7ff) | ((clk.dsp_on & 0x7ff)<<16); + ((clk.dsp_loop_latency & 0xf) << 16) | + ((clk.dsp_precision & 7) << 20); + pll->ct.dsp_on_off = (clk.dsp_off & 0x7ff) | + ((clk.dsp_on & 0x7ff) << 16); /*aty_calc_pll_ct(info, &pll->ct);*/ aty_set_pll_ct(info, pll); } else @@ -1913,8 +1973,7 @@ static int atyfb_mmap(struct fb_info *info, struct vm_area_struct *vma) continue; map_size = par->mmap_map[i].size - (offset - start); - map_offset = - par->mmap_map[i].poff + (offset - start); + map_offset = par->mmap_map[i].poff + (offset - start); break; } if (!map_size) { @@ -1924,8 +1983,7 @@ static int atyfb_mmap(struct fb_info *info, struct vm_area_struct *vma) if (page + map_size > size) map_size = size - page; - pgprot_val(vma->vm_page_prot) &= - ~(par->mmap_map[i].prot_mask); + pgprot_val(vma->vm_page_prot) &= ~(par->mmap_map[i].prot_mask); pgprot_val(vma->vm_page_prot) |= par->mmap_map[i].prot_flag; if (remap_pfn_range(vma, vma->vm_start + page, @@ -2029,7 +2087,8 @@ static int atyfb_pci_suspend(struct pci_dev *pdev, pm_message_t state) par->asleep = 1; par->lock_blank = 1; - /* Because we may change PCI D state ourselves, we need to + /* + * Because we may change PCI D state ourselves, we need to * first save the config space content so the core can * restore it properly on resume. */ @@ -2080,7 +2139,8 @@ static int atyfb_pci_resume(struct pci_dev *pdev) acquire_console_sem(); - /* PCI state will have been restored by the core, so + /* + * PCI state will have been restored by the core, so * we should be in D0 now with our config space fully * restored */ @@ -2192,8 +2252,8 @@ static void aty_bl_init(struct atyfb_par *par) info->bl_dev = bd; fb_bl_default_curve(info, 0, - 0x3F * FB_BACKLIGHT_MAX / MAX_LEVEL, - 0xFF * FB_BACKLIGHT_MAX / MAX_LEVEL); + 0x3F * FB_BACKLIGHT_MAX / MAX_LEVEL, + 0xFF * FB_BACKLIGHT_MAX / MAX_LEVEL); bd->props.max_brightness = FB_BACKLIGHT_LEVELS - 1; bd->props.brightness = bd->props.max_brightness; @@ -2236,16 +2296,16 @@ static void __devinit aty_calc_mem_refresh(struct atyfb_par *par, int xclk) size = ARRAY_SIZE(ragepro_tbl); } - for (i=0; i < size; i++) { + for (i = 0; i < size; i++) { if (xclk < refresh_tbl[i]) - break; + break; } par->mem_refresh_rate = i; } - /* - * Initialisation - */ +/* + * Initialisation + */ static struct fb_info *fb_list = NULL; @@ -2375,8 +2435,10 @@ static int __devinit aty_init(struct fb_info *info) } #endif #ifdef CONFIG_PPC_PMAC - /* The Apple iBook1 uses non-standard memory frequencies. We detect it - * and set the frequency manually. */ + /* + * The Apple iBook1 uses non-standard memory frequencies. + * We detect it and set the frequency manually. + */ if (machine_is_compatible("PowerBook2,1")) { par->pll_limits.mclk = 70; par->pll_limits.xclk = 53; @@ -2421,13 +2483,14 @@ static int __devinit aty_init(struct fb_info *info) /* save previous video mode */ aty_get_crtc(par, &par->saved_crtc); - if(par->pll_ops->get_pll) + if (par->pll_ops->get_pll) par->pll_ops->get_pll(info, &par->saved_pll); par->mem_cntl = aty_ld_le32(MEM_CNTL, par); gtb_memsize = M64_HAS(GTB_DSP); if (gtb_memsize) - switch (par->mem_cntl & 0xF) { /* 0xF used instead of MEM_SIZE_ALIAS */ + /* 0xF used instead of MEM_SIZE_ALIAS */ + switch (par->mem_cntl & 0xF) { case MEM_SIZE_512K: info->fix.smem_len = 0x80000; break; @@ -2496,8 +2559,8 @@ static int __devinit aty_init(struct fb_info *info) } /* - * Reg Block 0 (CT-compatible block) is at mmio_start - * Reg Block 1 (multimedia extensions) is at mmio_start - 0x400 + * Reg Block 0 (CT-compatible block) is at mmio_start + * Reg Block 1 (multimedia extensions) is at mmio_start - 0x400 */ if (M64_HAS(GX)) { info->fix.mmio_len = 0x400; @@ -2516,84 +2579,98 @@ static int __devinit aty_init(struct fb_info *info) } PRINTKI("%d%c %s, %s MHz XTAL, %d MHz PLL, %d Mhz MCLK, %d MHz XCLK\n", - info->fix.smem_len == 0x80000 ? 512 : (info->fix.smem_len >> 20), - info->fix.smem_len == 0x80000 ? 'K' : 'M', ramname, xtal, par->pll_limits.pll_max, - par->pll_limits.mclk, par->pll_limits.xclk); + info->fix.smem_len == 0x80000 ? 512 : (info->fix.smem_len>>20), + info->fix.smem_len == 0x80000 ? 'K' : 'M', ramname, xtal, + par->pll_limits.pll_max, par->pll_limits.mclk, + par->pll_limits.xclk); #if defined(DEBUG) && defined(CONFIG_FB_ATY_CT) if (M64_HAS(INTEGRATED)) { int i; - printk("debug atyfb: BUS_CNTL DAC_CNTL MEM_CNTL EXT_MEM_CNTL CRTC_GEN_CNTL " - "DSP_CONFIG DSP_ON_OFF CLOCK_CNTL\n" - "debug atyfb: %08x %08x %08x %08x %08x %08x %08x %08x\n" + printk("debug atyfb: BUS_CNTL DAC_CNTL MEM_CNTL " + "EXT_MEM_CNTL CRTC_GEN_CNTL DSP_CONFIG " + "DSP_ON_OFF CLOCK_CNTL\n" + "debug atyfb: %08x %08x %08x " + "%08x %08x %08x " + "%08x %08x\n" "debug atyfb: PLL", - aty_ld_le32(BUS_CNTL, par), aty_ld_le32(DAC_CNTL, par), - aty_ld_le32(MEM_CNTL, par), aty_ld_le32(EXT_MEM_CNTL, par), - aty_ld_le32(CRTC_GEN_CNTL, par), aty_ld_le32(DSP_CONFIG, par), - aty_ld_le32(DSP_ON_OFF, par), aty_ld_le32(CLOCK_CNTL, par)); + aty_ld_le32(BUS_CNTL, par), + aty_ld_le32(DAC_CNTL, par), + aty_ld_le32(MEM_CNTL, par), + aty_ld_le32(EXT_MEM_CNTL, par), + aty_ld_le32(CRTC_GEN_CNTL, par), + aty_ld_le32(DSP_CONFIG, par), + aty_ld_le32(DSP_ON_OFF, par), + aty_ld_le32(CLOCK_CNTL, par)); for (i = 0; i < 40; i++) printk(" %02x", aty_ld_pll_ct(i, par)); printk("\n"); } #endif - if(par->pll_ops->init_pll) + if (par->pll_ops->init_pll) par->pll_ops->init_pll(info, &par->pll); if (par->pll_ops->resume_pll) par->pll_ops->resume_pll(info, &par->pll); /* - * Last page of 8 MB (4 MB on ISA) aperture is MMIO, - * unless the auxiliary register aperture is used. + * Last page of 8 MB (4 MB on ISA) aperture is MMIO, + * unless the auxiliary register aperture is used. */ - if (!par->aux_start && - (info->fix.smem_len == 0x800000 || (par->bus_type == ISA && info->fix.smem_len == 0x400000))) + (info->fix.smem_len == 0x800000 || + (par->bus_type == ISA && info->fix.smem_len == 0x400000))) info->fix.smem_len -= GUI_RESERVE; /* - * Disable register access through the linear aperture - * if the auxiliary aperture is used so we can access - * the full 8 MB of video RAM on 8 MB boards. + * Disable register access through the linear aperture + * if the auxiliary aperture is used so we can access + * the full 8 MB of video RAM on 8 MB boards. */ if (par->aux_start) - aty_st_le32(BUS_CNTL, aty_ld_le32(BUS_CNTL, par) | BUS_APER_REG_DIS, par); + aty_st_le32(BUS_CNTL, aty_ld_le32(BUS_CNTL, par) | + BUS_APER_REG_DIS, par); #ifdef CONFIG_MTRR par->mtrr_aper = -1; par->mtrr_reg = -1; if (!nomtrr) { /* Cover the whole resource. */ - par->mtrr_aper = mtrr_add(par->res_start, par->res_size, MTRR_TYPE_WRCOMB, 1); - if (par->mtrr_aper >= 0 && !par->aux_start) { + par->mtrr_aper = mtrr_add(par->res_start, par->res_size, + MTRR_TYPE_WRCOMB, 1); + if (par->mtrr_aper >= 0 && !par->aux_start) { /* Make a hole for mmio. */ - par->mtrr_reg = mtrr_add(par->res_start + 0x800000 - GUI_RESERVE, - GUI_RESERVE, MTRR_TYPE_UNCACHABLE, 1); + par->mtrr_reg = mtrr_add(par->res_start + 0x800000 - + GUI_RESERVE, GUI_RESERVE, + MTRR_TYPE_UNCACHABLE, 1); if (par->mtrr_reg < 0) { mtrr_del(par->mtrr_aper, 0, 0); par->mtrr_aper = -1; } - } + } } #endif info->fbops = &atyfb_ops; info->pseudo_palette = par->pseudo_palette; info->flags = FBINFO_DEFAULT | - FBINFO_HWACCEL_IMAGEBLIT | - FBINFO_HWACCEL_FILLRECT | - FBINFO_HWACCEL_COPYAREA | - FBINFO_HWACCEL_YPAN; + FBINFO_HWACCEL_IMAGEBLIT | + FBINFO_HWACCEL_FILLRECT | + FBINFO_HWACCEL_COPYAREA | + FBINFO_HWACCEL_YPAN; #ifdef CONFIG_PMAC_BACKLIGHT if (M64_HAS(G3_PB_1_1) && machine_is_compatible("PowerBook1,1")) { - /* these bits let the 101 powerbook wake up from sleep -- paulus */ - aty_st_lcd(POWER_MANAGEMENT, aty_ld_lcd(POWER_MANAGEMENT, par) - | (USE_F32KHZ | TRISTATE_MEM_EN), par); + /* + * these bits let the 101 powerbook + * wake up from sleep -- paulus + */ + aty_st_lcd(POWER_MANAGEMENT, aty_ld_lcd(POWER_MANAGEMENT, par) | + USE_F32KHZ | TRISTATE_MEM_EN, par); } else #endif if (M64_HAS(MOBIL_BUS) && backlight) { #ifdef CONFIG_FB_ATY_BACKLIGHT - aty_bl_init (par); + aty_bl_init(par); #endif } @@ -2601,8 +2678,8 @@ static int __devinit aty_init(struct fb_info *info) #ifdef CONFIG_PPC if (machine_is(powermac)) { /* - * FIXME: The NVRAM stuff should be put in a Mac-specific file, as it - * applies to all Mac video cards + * FIXME: The NVRAM stuff should be put in a Mac-specific file, + * as it applies to all Mac video cards */ if (mode) { if (mac_find_mode(&var, info, mode, 8)) @@ -2615,8 +2692,7 @@ static int __devinit aty_init(struct fb_info *info) default_vmode = VMODE_1024_768_60; else if (machine_is_compatible("iMac")) default_vmode = VMODE_1024_768_75; - else if (machine_is_compatible - ("PowerBook2,1")) + else if (machine_is_compatible("PowerBook2,1")) /* iBook with 800x600 LCD */ default_vmode = VMODE_800_600_60; else @@ -2630,7 +2706,7 @@ static int __devinit aty_init(struct fb_info *info) if (default_cmode < CMODE_8 || default_cmode > CMODE_32) default_cmode = CMODE_8; if (!mac_vmode_to_var(default_vmode, default_cmode, - &var)) + &var)) has_var = 1; } } @@ -2702,12 +2778,12 @@ aty_init_exit: #ifdef CONFIG_MTRR if (par->mtrr_reg >= 0) { - mtrr_del(par->mtrr_reg, 0, 0); - par->mtrr_reg = -1; + mtrr_del(par->mtrr_reg, 0, 0); + par->mtrr_reg = -1; } if (par->mtrr_aper >= 0) { - mtrr_del(par->mtrr_aper, 0, 0); - par->mtrr_aper = -1; + mtrr_del(par->mtrr_aper, 0, 0); + par->mtrr_aper = -1; } #endif return ret; @@ -2735,18 +2811,18 @@ static int __devinit store_video_par(char *video_str, unsigned char m64_num) phys_size[m64_num] = size; phys_guiregbase[m64_num] = guiregbase; PRINTKI("stored them all: $%08lX $%08lX $%08lX \n", vmembase, size, - guiregbase); + guiregbase); return 0; - mach64_invalid: + mach64_invalid: phys_vmembase[m64_num] = 0; return -1; } #endif /* CONFIG_ATARI */ - /* - * Blank the display. - */ +/* + * Blank the display. + */ static int atyfb_blank(int blank, struct fb_info *info) { @@ -2768,20 +2844,20 @@ static int atyfb_blank(int blank, struct fb_info *info) gen_cntl = aty_ld_le32(CRTC_GEN_CNTL, par); gen_cntl &= ~0x400004c; switch (blank) { - case FB_BLANK_UNBLANK: - break; - case FB_BLANK_NORMAL: - gen_cntl |= 0x4000040; - break; - case FB_BLANK_VSYNC_SUSPEND: - gen_cntl |= 0x4000048; - break; - case FB_BLANK_HSYNC_SUSPEND: - gen_cntl |= 0x4000044; - break; - case FB_BLANK_POWERDOWN: - gen_cntl |= 0x400004c; - break; + case FB_BLANK_UNBLANK: + break; + case FB_BLANK_NORMAL: + gen_cntl |= 0x4000040; + break; + case FB_BLANK_VSYNC_SUSPEND: + gen_cntl |= 0x4000048; + break; + case FB_BLANK_HSYNC_SUSPEND: + gen_cntl |= 0x4000044; + break; + case FB_BLANK_POWERDOWN: + gen_cntl |= 0x400004c; + break; } aty_st_le32(CRTC_GEN_CNTL, gen_cntl, par); @@ -2806,15 +2882,15 @@ static void aty_st_pal(u_int regno, u_int red, u_int green, u_int blue, aty_st_8(DAC_DATA, blue, par); } - /* - * Set a single color register. The values supplied are already - * rounded down to the hardware's capabilities (according to the - * entries in the var structure). Return != 0 for invalid regno. - * !! 4 & 8 = PSEUDO, > 8 = DIRECTCOLOR - */ +/* + * Set a single color register. The values supplied are already + * rounded down to the hardware's capabilities (according to the + * entries in the var structure). Return != 0 for invalid regno. + * !! 4 & 8 = PSEUDO, > 8 = DIRECTCOLOR + */ static int atyfb_setcolreg(u_int regno, u_int red, u_int green, u_int blue, - u_int transp, struct fb_info *info) + u_int transp, struct fb_info *info) { struct atyfb_par *par = (struct atyfb_par *) info->par; int i, depth; @@ -2868,16 +2944,15 @@ static int atyfb_setcolreg(u_int regno, u_int red, u_int green, u_int blue, if (depth == 16) { if (regno < 32) aty_st_pal(regno << 3, red, - par->palette[regno<<1].green, + par->palette[regno << 1].green, blue, par); - red = par->palette[regno>>1].red; - blue = par->palette[regno>>1].blue; + red = par->palette[regno >> 1].red; + blue = par->palette[regno >> 1].blue; regno <<= 2; } else if (depth == 15) { regno <<= 3; - for(i = 0; i < 8; i++) { - aty_st_pal(regno + i, red, green, blue, par); - } + for (i = 0; i < 8; i++) + aty_st_pal(regno + i, red, green, blue, par); } } aty_st_pal(regno, red, green, blue, par); @@ -2890,7 +2965,8 @@ static int atyfb_setcolreg(u_int regno, u_int red, u_int green, u_int blue, #ifdef __sparc__ static int __devinit atyfb_setup_sparc(struct pci_dev *pdev, - struct fb_info *info, unsigned long addr) + struct fb_info *info, + unsigned long addr) { struct atyfb_par *par = info->par; struct device_node *dp; @@ -2978,7 +3054,8 @@ static int __devinit atyfb_setup_sparc(struct pci_dev *pdev, j++; } - if((ret = correct_chipset(par))) + ret = correct_chipset(par); + if (ret) return ret; if (IS_XL(pdev->device)) { @@ -3108,28 +3185,28 @@ static void __devinit aty_init_lcd(struct atyfb_par *par, u32 bios_base) u32 driv_inf_tab, sig; u16 lcd_ofs; - /* To support an LCD panel, we should know it's dimensions and + /* + * To support an LCD panel, we should know it's dimensions and * it's desired pixel clock. * There are two ways to do it: * - Check the startup video mode and calculate the panel * size from it. This is unreliable. * - Read it from the driver information table in the video BIOS. - */ + */ /* Address of driver information table is at offset 0x78. */ driv_inf_tab = bios_base + *((u16 *)(bios_base+0x78)); /* Check for the driver information table signature. */ - sig = (*(u32 *)driv_inf_tab); + sig = *(u32 *)driv_inf_tab; if ((sig == 0x54504c24) || /* Rage LT pro */ - (sig == 0x544d5224) || /* Rage mobility */ - (sig == 0x54435824) || /* Rage XC */ - (sig == 0x544c5824)) { /* Rage XL */ + (sig == 0x544d5224) || /* Rage mobility */ + (sig == 0x54435824) || /* Rage XC */ + (sig == 0x544c5824)) { /* Rage XL */ PRINTKI("BIOS contains driver information table.\n"); - lcd_ofs = (*(u16 *)(driv_inf_tab + 10)); + lcd_ofs = *(u16 *)(driv_inf_tab + 10); par->lcd_table = 0; - if (lcd_ofs != 0) { + if (lcd_ofs != 0) par->lcd_table = bios_base + lcd_ofs; - } } if (par->lcd_table != 0) { @@ -3144,14 +3221,16 @@ static void __devinit aty_init_lcd(struct atyfb_par *par, u32 bios_base) u16 width, height, panel_type, refresh_rates; u16 *lcdmodeptr; u32 format; - u8 lcd_refresh_rates[16] = {50,56,60,67,70,72,75,76,85,90,100,120,140,150,160,200}; - /* The most important information is the panel size at + u8 lcd_refresh_rates[16] = { 50, 56, 60, 67, 70, 72, 75, 76, 85, + 90, 100, 120, 140, 150, 160, 200 }; + /* + * The most important information is the panel size at * offset 25 and 27, but there's some other nice information * which we print to the screen. */ id = *(u8 *)par->lcd_table; - strncpy(model,(char *)par->lcd_table+1,24); - model[23]=0; + strncpy(model, (char *)par->lcd_table+1, 24); + model[23] = 0; width = par->lcd_width = *(u16 *)(par->lcd_table+25); height = par->lcd_height = *(u16 *)(par->lcd_table+27); @@ -3164,7 +3243,7 @@ static void __devinit aty_init_lcd(struct atyfb_par *par, u32 bios_base) txtdual = "dual (split) "; else txtdual = ""; - tech = (panel_type>>2) & 63; + tech = (panel_type >> 2) & 63; switch (tech) { case 0: txtmonitor = "passive matrix"; @@ -3224,22 +3303,24 @@ static void __devinit aty_init_lcd(struct atyfb_par *par, u32 bios_base) } } PRINTKI("%s%s %s monitor detected: %s\n", - txtdual ,txtcolour, txtmonitor, model); + txtdual, txtcolour, txtmonitor, model); PRINTKI(" id=%d, %dx%d pixels, %s\n", id, width, height, txtformat); refresh_rates_buf[0] = 0; refresh_rates = *(u16 *)(par->lcd_table+62); m = 1; f = 0; - for (i=0;i<16;i++) { + for (i = 0; i < 16; i++) { if (refresh_rates & m) { if (f == 0) { - sprintf(strbuf, "%d", lcd_refresh_rates[i]); + sprintf(strbuf, "%d", + lcd_refresh_rates[i]); f++; } else { - sprintf(strbuf, ",%d", lcd_refresh_rates[i]); + sprintf(strbuf, ",%d", + lcd_refresh_rates[i]); } - strcat(refresh_rates_buf,strbuf); + strcat(refresh_rates_buf, strbuf); } m = m << 1; } @@ -3247,7 +3328,8 @@ static void __devinit aty_init_lcd(struct atyfb_par *par, u32 bios_base) PRINTKI(" supports refresh rates [%s], default %d Hz\n", refresh_rates_buf, lcd_refresh_rates[default_refresh_rate]); par->lcd_refreshrate = lcd_refresh_rates[default_refresh_rate]; - /* We now need to determine the crtc parameters for the + /* + * We now need to determine the crtc parameters for the * LCD monitor. This is tricky, because they are not stored * individually in the BIOS. Instead, the BIOS contains a * table of display modes that work for this monitor. @@ -3382,7 +3464,9 @@ static int __devinit init_from_bios(struct atyfb_par *par) } #endif /* __i386__ */ -static int __devinit atyfb_setup_generic(struct pci_dev *pdev, struct fb_info *info, unsigned long addr) +static int __devinit atyfb_setup_generic(struct pci_dev *pdev, + struct fb_info *info, + unsigned long addr) { struct atyfb_par *par = info->par; u16 tmp; @@ -3429,10 +3513,12 @@ static int __devinit atyfb_setup_generic(struct pci_dev *pdev, struct fb_info *i goto atyfb_setup_generic_fail; } - if((ret = correct_chipset(par))) + ret = correct_chipset(par); + if (ret) goto atyfb_setup_generic_fail; #ifdef __i386__ - if((ret = init_from_bios(par))) + ret = init_from_bios(par); + if (ret) goto atyfb_setup_generic_fail; #endif if (!(aty_ld_le32(CRTC_GEN_CNTL, par) & CRTC_EXT_DISP_EN)) @@ -3457,7 +3543,8 @@ atyfb_setup_generic_fail: #endif /* !__sparc__ */ -static int __devinit atyfb_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) +static int __devinit atyfb_pci_probe(struct pci_dev *pdev, + const struct pci_device_id *ent) { unsigned long addr, res_start, res_size; struct fb_info *info; @@ -3482,10 +3569,10 @@ static int __devinit atyfb_pci_probe(struct pci_dev *pdev, const struct pci_devi /* Reserve space */ res_start = rp->start; res_size = rp->end - rp->start + 1; - if (!request_mem_region (res_start, res_size, "atyfb")) + if (!request_mem_region(res_start, res_size, "atyfb")) return -EBUSY; - /* Allocate framebuffer */ + /* Allocate framebuffer */ info = framebuffer_alloc(sizeof(struct atyfb_par), &pdev->dev); if (!info) { PRINTKE("atyfb_pci_probe() can't alloc fb_info\n"); @@ -3573,7 +3660,8 @@ static int __init atyfb_atari_probe(void) for (m64_num = 0; m64_num < mach64_count; m64_num++) { if (!phys_vmembase[m64_num] || !phys_size[m64_num] || !phys_guiregbase[m64_num]) { - PRINTKI("phys_*[%d] parameters not set => returning early. \n", m64_num); + PRINTKI("phys_*[%d] parameters not set => " + "returning early. \n", m64_num); continue; } @@ -3589,8 +3677,8 @@ static int __init atyfb_atari_probe(void) par->irq = (unsigned int) -1; /* something invalid */ /* - * Map the video memory (physical address given) to somewhere in the - * kernel address space. + * Map the video memory (physical address given) + * to somewhere in the kernel address space. */ info->screen_base = ioremap(phys_vmembase[m64_num], phys_size[m64_num]); info->fix.smem_start = (unsigned long)info->screen_base; /* Fake! */ @@ -3661,12 +3749,12 @@ static void __devexit atyfb_remove(struct fb_info *info) #ifdef CONFIG_MTRR if (par->mtrr_reg >= 0) { - mtrr_del(par->mtrr_reg, 0, 0); - par->mtrr_reg = -1; + mtrr_del(par->mtrr_reg, 0, 0); + par->mtrr_reg = -1; } if (par->mtrr_aper >= 0) { - mtrr_del(par->mtrr_aper, 0, 0); - par->mtrr_aper = -1; + mtrr_del(par->mtrr_aper, 0, 0); + par->mtrr_aper = -1; } #endif #ifndef __sparc__ @@ -3900,29 +3988,29 @@ static const struct dmi_system_id atyfb_reboot_ids[] = { static int __init atyfb_init(void) { - int err1 = 1, err2 = 1; + int err1 = 1, err2 = 1; #ifndef MODULE - char *option = NULL; + char *option = NULL; - if (fb_get_options("atyfb", &option)) - return -ENODEV; - atyfb_setup(option); + if (fb_get_options("atyfb", &option)) + return -ENODEV; + atyfb_setup(option); #endif #ifdef CONFIG_PCI - err1 = pci_register_driver(&atyfb_driver); + err1 = pci_register_driver(&atyfb_driver); #endif #ifdef CONFIG_ATARI - err2 = atyfb_atari_probe(); + err2 = atyfb_atari_probe(); #endif - if (err1 && err2) - return -ENODEV; + if (err1 && err2) + return -ENODEV; - if (dmi_check_system(atyfb_reboot_ids)) - register_reboot_notifier(&atyfb_reboot_notifier); + if (dmi_check_system(atyfb_reboot_ids)) + register_reboot_notifier(&atyfb_reboot_notifier); - return 0; + return 0; } static void __exit atyfb_exit(void) @@ -3951,8 +4039,7 @@ MODULE_PARM_DESC(mclk, "int: override memory clock"); module_param(xclk, int, 0); MODULE_PARM_DESC(xclk, "int: override accelerated engine clock"); module_param(comp_sync, int, 0); -MODULE_PARM_DESC(comp_sync, - "Set composite sync signal to low (0) or high (1)"); +MODULE_PARM_DESC(comp_sync, "Set composite sync signal to low (0) or high (1)"); module_param(mode, charp, 0); MODULE_PARM_DESC(mode, "Specify resolution as \"<xres>x<yres>[-<bpp>][@<refresh>]\" "); #ifdef CONFIG_MTRR diff --git a/drivers/video/au1100fb.c b/drivers/video/au1100fb.c index 378f27745a1d..a699aab63820 100644 --- a/drivers/video/au1100fb.c +++ b/drivers/video/au1100fb.c @@ -715,8 +715,11 @@ int au1100fb_setup(char *options) } /* Mode option (only option that start with digit) */ else if (isdigit(this_opt[0])) { - mode = kmalloc(strlen(this_opt) + 1, GFP_KERNEL); - strncpy(mode, this_opt, strlen(this_opt) + 1); + mode = kstrdup(this_opt, GFP_KERNEL); + if (!mode) { + print_err("memory allocation failed"); + return -ENOMEM; + } } /* Unsupported option */ else { diff --git a/drivers/video/backlight/Kconfig b/drivers/video/backlight/Kconfig index 90861cd93165..09bfa9662e4d 100644 --- a/drivers/video/backlight/Kconfig +++ b/drivers/video/backlight/Kconfig @@ -31,6 +31,13 @@ config LCD_CORGI Say y here to support the LCD panels usually found on SHARP corgi (C7x0) and spitz (Cxx00) models. +config LCD_LMS283GF05 + tristate "Samsung LMS283GF05 LCD" + depends on LCD_CLASS_DEVICE && SPI_MASTER && GENERIC_GPIO + help + SPI driver for Samsung LMS283GF05. This provides basic support + for powering the LCD up/down through a sysfs interface. + config LCD_LTV350QV tristate "Samsung LTV350QV LCD Panel" depends on LCD_CLASS_DEVICE && SPI_MASTER @@ -229,3 +236,29 @@ config BACKLIGHT_SAHARA help If you have a Tabletkiosk Sahara Touch-iT, say y to enable the backlight driver. + +config BACKLIGHT_WM831X + tristate "WM831x PMIC Backlight Driver" + depends on BACKLIGHT_CLASS_DEVICE && MFD_WM831X + help + If you have a backlight driven by the ISINK and DCDC of a + WM831x PMIC say y to enable the backlight driver for it. + +config BACKLIGHT_ADX + tristate "Avionic Design Xanthos Backlight Driver" + depends on BACKLIGHT_CLASS_DEVICE && ARCH_PXA_ADX + default y + help + Say Y to enable the backlight driver on Avionic Design Xanthos-based + boards. + +config BACKLIGHT_ADP5520 + tristate "Backlight Driver for ADP5520/ADP5501 using WLED" + depends on BACKLIGHT_CLASS_DEVICE && PMIC_ADP5520 + help + If you have a LCD backlight connected to the BST/BL_SNK output of + ADP5520 or ADP5501, say Y here to enable this driver. + + To compile this driver as a module, choose M here: the module will + be called adp5520_bl. + diff --git a/drivers/video/backlight/Makefile b/drivers/video/backlight/Makefile index 4eb178c1d684..9a405548874c 100644 --- a/drivers/video/backlight/Makefile +++ b/drivers/video/backlight/Makefile @@ -3,6 +3,7 @@ obj-$(CONFIG_LCD_CLASS_DEVICE) += lcd.o obj-$(CONFIG_LCD_CORGI) += corgi_lcd.o obj-$(CONFIG_LCD_HP700) += jornada720_lcd.o +obj-$(CONFIG_LCD_LMS283GF05) += lms283gf05.o obj-$(CONFIG_LCD_LTV350QV) += ltv350qv.o obj-$(CONFIG_LCD_ILI9320) += ili9320.o obj-$(CONFIG_LCD_PLATFORM) += platform_lcd.o @@ -24,4 +25,7 @@ obj-$(CONFIG_BACKLIGHT_DA903X) += da903x_bl.o obj-$(CONFIG_BACKLIGHT_MBP_NVIDIA) += mbp_nvidia_bl.o obj-$(CONFIG_BACKLIGHT_TOSA) += tosa_bl.o obj-$(CONFIG_BACKLIGHT_SAHARA) += kb3886_bl.o +obj-$(CONFIG_BACKLIGHT_WM831X) += wm831x_bl.o +obj-$(CONFIG_BACKLIGHT_ADX) += adx_bl.o +obj-$(CONFIG_BACKLIGHT_ADP5520) += adp5520_bl.o diff --git a/drivers/video/backlight/adp5520_bl.c b/drivers/video/backlight/adp5520_bl.c new file mode 100644 index 000000000000..ad05da5ba3c7 --- /dev/null +++ b/drivers/video/backlight/adp5520_bl.c @@ -0,0 +1,377 @@ +/* + * Backlight driver for Analog Devices ADP5520/ADP5501 MFD PMICs + * + * Copyright 2009 Analog Devices Inc. + * + * Licensed under the GPL-2 or later. + */ + +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/platform_device.h> +#include <linux/fb.h> +#include <linux/backlight.h> +#include <linux/mfd/adp5520.h> + +struct adp5520_bl { + struct device *master; + struct adp5520_backlight_platfrom_data *pdata; + struct mutex lock; + unsigned long cached_daylight_max; + int id; + int current_brightness; +}; + +static int adp5520_bl_set(struct backlight_device *bl, int brightness) +{ + struct adp5520_bl *data = bl_get_data(bl); + struct device *master = data->master; + int ret = 0; + + if (data->pdata->en_ambl_sens) { + if ((brightness > 0) && (brightness < ADP5020_MAX_BRIGHTNESS)) { + /* Disable Ambient Light auto adjust */ + ret |= adp5520_clr_bits(master, BL_CONTROL, + BL_AUTO_ADJ); + ret |= adp5520_write(master, DAYLIGHT_MAX, brightness); + } else { + /* + * MAX_BRIGHTNESS -> Enable Ambient Light auto adjust + * restore daylight l3 sysfs brightness + */ + ret |= adp5520_write(master, DAYLIGHT_MAX, + data->cached_daylight_max); + ret |= adp5520_set_bits(master, BL_CONTROL, + BL_AUTO_ADJ); + } + } else { + ret |= adp5520_write(master, DAYLIGHT_MAX, brightness); + } + + if (data->current_brightness && brightness == 0) + ret |= adp5520_set_bits(master, + MODE_STATUS, DIM_EN); + else if (data->current_brightness == 0 && brightness) + ret |= adp5520_clr_bits(master, + MODE_STATUS, DIM_EN); + + if (!ret) + data->current_brightness = brightness; + + return ret; +} + +static int adp5520_bl_update_status(struct backlight_device *bl) +{ + int brightness = bl->props.brightness; + if (bl->props.power != FB_BLANK_UNBLANK) + brightness = 0; + + if (bl->props.fb_blank != FB_BLANK_UNBLANK) + brightness = 0; + + return adp5520_bl_set(bl, brightness); +} + +static int adp5520_bl_get_brightness(struct backlight_device *bl) +{ + struct adp5520_bl *data = bl_get_data(bl); + int error; + uint8_t reg_val; + + error = adp5520_read(data->master, BL_VALUE, ®_val); + + return error ? data->current_brightness : reg_val; +} + +static struct backlight_ops adp5520_bl_ops = { + .update_status = adp5520_bl_update_status, + .get_brightness = adp5520_bl_get_brightness, +}; + +static int adp5520_bl_setup(struct backlight_device *bl) +{ + struct adp5520_bl *data = bl_get_data(bl); + struct device *master = data->master; + struct adp5520_backlight_platfrom_data *pdata = data->pdata; + int ret = 0; + + ret |= adp5520_write(master, DAYLIGHT_MAX, pdata->l1_daylight_max); + ret |= adp5520_write(master, DAYLIGHT_DIM, pdata->l1_daylight_dim); + + if (pdata->en_ambl_sens) { + data->cached_daylight_max = pdata->l1_daylight_max; + ret |= adp5520_write(master, OFFICE_MAX, pdata->l2_office_max); + ret |= adp5520_write(master, OFFICE_DIM, pdata->l2_office_dim); + ret |= adp5520_write(master, DARK_MAX, pdata->l3_dark_max); + ret |= adp5520_write(master, DARK_DIM, pdata->l3_dark_dim); + ret |= adp5520_write(master, L2_TRIP, pdata->l2_trip); + ret |= adp5520_write(master, L2_HYS, pdata->l2_hyst); + ret |= adp5520_write(master, L3_TRIP, pdata->l3_trip); + ret |= adp5520_write(master, L3_HYS, pdata->l3_hyst); + ret |= adp5520_write(master, ALS_CMPR_CFG, + ALS_CMPR_CFG_VAL(pdata->abml_filt, L3_EN)); + } + + ret |= adp5520_write(master, BL_CONTROL, + BL_CTRL_VAL(pdata->fade_led_law, pdata->en_ambl_sens)); + + ret |= adp5520_write(master, BL_FADE, FADE_VAL(pdata->fade_in, + pdata->fade_out)); + + ret |= adp5520_set_bits(master, MODE_STATUS, BL_EN | DIM_EN); + + return ret; +} + +static ssize_t adp5520_show(struct device *dev, char *buf, int reg) +{ + struct adp5520_bl *data = dev_get_drvdata(dev); + int error; + uint8_t reg_val; + + mutex_lock(&data->lock); + error = adp5520_read(data->master, reg, ®_val); + mutex_unlock(&data->lock); + + return sprintf(buf, "%u\n", reg_val); +} + +static ssize_t adp5520_store(struct device *dev, const char *buf, + size_t count, int reg) +{ + struct adp5520_bl *data = dev_get_drvdata(dev); + unsigned long val; + int ret; + + ret = strict_strtoul(buf, 10, &val); + if (ret) + return ret; + + mutex_lock(&data->lock); + adp5520_write(data->master, reg, val); + mutex_unlock(&data->lock); + + return count; +} + +static ssize_t adp5520_bl_dark_max_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return adp5520_show(dev, buf, DARK_MAX); +} + +static ssize_t adp5520_bl_dark_max_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + return adp5520_store(dev, buf, count, DARK_MAX); +} +static DEVICE_ATTR(dark_max, 0664, adp5520_bl_dark_max_show, + adp5520_bl_dark_max_store); + +static ssize_t adp5520_bl_office_max_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return adp5520_show(dev, buf, OFFICE_MAX); +} + +static ssize_t adp5520_bl_office_max_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + return adp5520_store(dev, buf, count, OFFICE_MAX); +} +static DEVICE_ATTR(office_max, 0664, adp5520_bl_office_max_show, + adp5520_bl_office_max_store); + +static ssize_t adp5520_bl_daylight_max_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return adp5520_show(dev, buf, DAYLIGHT_MAX); +} + +static ssize_t adp5520_bl_daylight_max_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct adp5520_bl *data = dev_get_drvdata(dev); + + strict_strtoul(buf, 10, &data->cached_daylight_max); + return adp5520_store(dev, buf, count, DAYLIGHT_MAX); +} +static DEVICE_ATTR(daylight_max, 0664, adp5520_bl_daylight_max_show, + adp5520_bl_daylight_max_store); + +static ssize_t adp5520_bl_dark_dim_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return adp5520_show(dev, buf, DARK_DIM); +} + +static ssize_t adp5520_bl_dark_dim_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + return adp5520_store(dev, buf, count, DARK_DIM); +} +static DEVICE_ATTR(dark_dim, 0664, adp5520_bl_dark_dim_show, + adp5520_bl_dark_dim_store); + +static ssize_t adp5520_bl_office_dim_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return adp5520_show(dev, buf, OFFICE_DIM); +} + +static ssize_t adp5520_bl_office_dim_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + return adp5520_store(dev, buf, count, OFFICE_DIM); +} +static DEVICE_ATTR(office_dim, 0664, adp5520_bl_office_dim_show, + adp5520_bl_office_dim_store); + +static ssize_t adp5520_bl_daylight_dim_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return adp5520_show(dev, buf, DAYLIGHT_DIM); +} + +static ssize_t adp5520_bl_daylight_dim_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + return adp5520_store(dev, buf, count, DAYLIGHT_DIM); +} +static DEVICE_ATTR(daylight_dim, 0664, adp5520_bl_daylight_dim_show, + adp5520_bl_daylight_dim_store); + +static struct attribute *adp5520_bl_attributes[] = { + &dev_attr_dark_max.attr, + &dev_attr_dark_dim.attr, + &dev_attr_office_max.attr, + &dev_attr_office_dim.attr, + &dev_attr_daylight_max.attr, + &dev_attr_daylight_dim.attr, + NULL +}; + +static const struct attribute_group adp5520_bl_attr_group = { + .attrs = adp5520_bl_attributes, +}; + +static int __devinit adp5520_bl_probe(struct platform_device *pdev) +{ + struct backlight_device *bl; + struct adp5520_bl *data; + int ret = 0; + + data = kzalloc(sizeof(*data), GFP_KERNEL); + if (data == NULL) + return -ENOMEM; + + data->master = pdev->dev.parent; + data->pdata = pdev->dev.platform_data; + + if (data->pdata == NULL) { + dev_err(&pdev->dev, "missing platform data\n"); + kfree(data); + return -ENODEV; + } + + data->id = pdev->id; + data->current_brightness = 0; + + mutex_init(&data->lock); + + bl = backlight_device_register(pdev->name, data->master, + data, &adp5520_bl_ops); + if (IS_ERR(bl)) { + dev_err(&pdev->dev, "failed to register backlight\n"); + kfree(data); + return PTR_ERR(bl); + } + + bl->props.max_brightness = + bl->props.brightness = ADP5020_MAX_BRIGHTNESS; + + if (data->pdata->en_ambl_sens) + ret = sysfs_create_group(&bl->dev.kobj, + &adp5520_bl_attr_group); + + if (ret) { + dev_err(&pdev->dev, "failed to register sysfs\n"); + backlight_device_unregister(bl); + kfree(data); + } + + platform_set_drvdata(pdev, bl); + ret |= adp5520_bl_setup(bl); + backlight_update_status(bl); + + return ret; +} + +static int __devexit adp5520_bl_remove(struct platform_device *pdev) +{ + struct backlight_device *bl = platform_get_drvdata(pdev); + struct adp5520_bl *data = bl_get_data(bl); + + adp5520_clr_bits(data->master, MODE_STATUS, BL_EN); + + if (data->pdata->en_ambl_sens) + sysfs_remove_group(&bl->dev.kobj, + &adp5520_bl_attr_group); + + backlight_device_unregister(bl); + kfree(data); + + return 0; +} + +#ifdef CONFIG_PM +static int adp5520_bl_suspend(struct platform_device *pdev, + pm_message_t state) +{ + struct backlight_device *bl = platform_get_drvdata(pdev); + return adp5520_bl_set(bl, 0); +} + +static int adp5520_bl_resume(struct platform_device *pdev) +{ + struct backlight_device *bl = platform_get_drvdata(pdev); + + backlight_update_status(bl); + return 0; +} +#else +#define adp5520_bl_suspend NULL +#define adp5520_bl_resume NULL +#endif + +static struct platform_driver adp5520_bl_driver = { + .driver = { + .name = "adp5520-backlight", + .owner = THIS_MODULE, + }, + .probe = adp5520_bl_probe, + .remove = __devexit_p(adp5520_bl_remove), + .suspend = adp5520_bl_suspend, + .resume = adp5520_bl_resume, +}; + +static int __init adp5520_bl_init(void) +{ + return platform_driver_register(&adp5520_bl_driver); +} +module_init(adp5520_bl_init); + +static void __exit adp5520_bl_exit(void) +{ + platform_driver_unregister(&adp5520_bl_driver); +} +module_exit(adp5520_bl_exit); + +MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>"); +MODULE_DESCRIPTION("ADP5520(01) Backlight Driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:adp5520-backlight"); diff --git a/drivers/video/backlight/adx_bl.c b/drivers/video/backlight/adx_bl.c new file mode 100644 index 000000000000..2c3bdfc620b7 --- /dev/null +++ b/drivers/video/backlight/adx_bl.c @@ -0,0 +1,178 @@ +/* + * linux/drivers/video/backlight/adx.c + * + * Copyright (C) 2009 Avionic Design GmbH + * + * 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. + * + * Written by Thierry Reding <thierry.reding@avionic-design.de> + */ + +#include <linux/backlight.h> +#include <linux/fb.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/platform_device.h> + +/* register definitions */ +#define ADX_BACKLIGHT_CONTROL 0x00 +#define ADX_BACKLIGHT_CONTROL_ENABLE (1 << 0) +#define ADX_BACKLIGHT_BRIGHTNESS 0x08 +#define ADX_BACKLIGHT_STATUS 0x10 +#define ADX_BACKLIGHT_ERROR 0x18 + +struct adxbl { + void __iomem *base; +}; + +static int adx_backlight_update_status(struct backlight_device *bldev) +{ + struct adxbl *bl = bl_get_data(bldev); + u32 value; + + value = bldev->props.brightness; + writel(value, bl->base + ADX_BACKLIGHT_BRIGHTNESS); + + value = readl(bl->base + ADX_BACKLIGHT_CONTROL); + + if (bldev->props.state & BL_CORE_FBBLANK) + value &= ~ADX_BACKLIGHT_CONTROL_ENABLE; + else + value |= ADX_BACKLIGHT_CONTROL_ENABLE; + + writel(value, bl->base + ADX_BACKLIGHT_CONTROL); + + return 0; +} + +static int adx_backlight_get_brightness(struct backlight_device *bldev) +{ + struct adxbl *bl = bl_get_data(bldev); + u32 brightness; + + brightness = readl(bl->base + ADX_BACKLIGHT_BRIGHTNESS); + return brightness & 0xff; +} + +static int adx_backlight_check_fb(struct fb_info *fb) +{ + return 1; +} + +static struct backlight_ops adx_backlight_ops = { + .options = 0, + .update_status = adx_backlight_update_status, + .get_brightness = adx_backlight_get_brightness, + .check_fb = adx_backlight_check_fb, +}; + +static int __devinit adx_backlight_probe(struct platform_device *pdev) +{ + struct backlight_device *bldev; + struct resource *res; + struct adxbl *bl; + int ret = 0; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + ret = -ENXIO; + goto out; + } + + res = devm_request_mem_region(&pdev->dev, res->start, + resource_size(res), res->name); + if (!res) { + ret = -ENXIO; + goto out; + } + + bl = devm_kzalloc(&pdev->dev, sizeof(*bl), GFP_KERNEL); + if (!bl) { + ret = -ENOMEM; + goto out; + } + + bl->base = devm_ioremap_nocache(&pdev->dev, res->start, + resource_size(res)); + if (!bl->base) { + ret = -ENXIO; + goto out; + } + + bldev = backlight_device_register(dev_name(&pdev->dev), &pdev->dev, bl, + &adx_backlight_ops); + if (!bldev) { + ret = -ENOMEM; + goto out; + } + + bldev->props.max_brightness = 0xff; + bldev->props.brightness = 0xff; + bldev->props.power = FB_BLANK_UNBLANK; + + platform_set_drvdata(pdev, bldev); + +out: + return ret; +} + +static int __devexit adx_backlight_remove(struct platform_device *pdev) +{ + struct backlight_device *bldev; + int ret = 0; + + bldev = platform_get_drvdata(pdev); + bldev->props.power = FB_BLANK_UNBLANK; + bldev->props.brightness = 0xff; + backlight_update_status(bldev); + backlight_device_unregister(bldev); + platform_set_drvdata(pdev, NULL); + + return ret; +} + +#ifdef CONFIG_PM +static int adx_backlight_suspend(struct platform_device *pdev, + pm_message_t state) +{ + return 0; +} + +static int adx_backlight_resume(struct platform_device *pdev) +{ + return 0; +} +#else +#define adx_backlight_suspend NULL +#define adx_backlight_resume NULL +#endif + +static struct platform_driver adx_backlight_driver = { + .probe = adx_backlight_probe, + .remove = __devexit_p(adx_backlight_remove), + .suspend = adx_backlight_suspend, + .resume = adx_backlight_resume, + .driver = { + .name = "adx-backlight", + .owner = THIS_MODULE, + }, +}; + +static int __init adx_backlight_init(void) +{ + return platform_driver_register(&adx_backlight_driver); +} + +static void __exit adx_backlight_exit(void) +{ + platform_driver_unregister(&adx_backlight_driver); +} + +module_init(adx_backlight_init); +module_exit(adx_backlight_exit); + +MODULE_AUTHOR("Thierry Reding <thierry.reding@avionic-design.de>"); +MODULE_DESCRIPTION("Avionic Design Xanthos Backlight Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/video/backlight/backlight.c b/drivers/video/backlight/backlight.c index 157057c79ca3..6615ac7fa60a 100644 --- a/drivers/video/backlight/backlight.c +++ b/drivers/video/backlight/backlight.c @@ -73,6 +73,27 @@ static inline void backlight_unregister_fb(struct backlight_device *bd) } #endif /* CONFIG_FB */ +static void backlight_generate_event(struct backlight_device *bd, + enum backlight_update_reason reason) +{ + char *envp[2]; + + switch (reason) { + case BACKLIGHT_UPDATE_SYSFS: + envp[0] = "SOURCE=sysfs"; + break; + case BACKLIGHT_UPDATE_HOTKEY: + envp[0] = "SOURCE=hotkey"; + break; + default: + envp[0] = "SOURCE=unknown"; + break; + } + envp[1] = NULL; + kobject_uevent_env(&bd->dev.kobj, KOBJ_CHANGE, envp); + sysfs_notify(&bd->dev.kobj, NULL, "actual_brightness"); +} + static ssize_t backlight_show_power(struct device *dev, struct device_attribute *attr,char *buf) { @@ -142,6 +163,8 @@ static ssize_t backlight_store_brightness(struct device *dev, } mutex_unlock(&bd->ops_lock); + backlight_generate_event(bd, BACKLIGHT_UPDATE_SYSFS); + return rc; } @@ -214,6 +237,25 @@ static struct device_attribute bl_device_attributes[] = { }; /** + * backlight_force_update - tell the backlight subsystem that hardware state + * has changed + * @bd: the backlight device to update + * + * Updates the internal state of the backlight in response to a hardware event, + * and generate a uevent to notify userspace + */ +void backlight_force_update(struct backlight_device *bd, + enum backlight_update_reason reason) +{ + mutex_lock(&bd->ops_lock); + if (bd->ops && bd->ops->get_brightness) + bd->props.brightness = bd->ops->get_brightness(bd); + mutex_unlock(&bd->ops_lock); + backlight_generate_event(bd, reason); +} +EXPORT_SYMBOL(backlight_force_update); + +/** * backlight_device_register - create and register a new object of * backlight_device class. * @name: the name of the new object(must be the same as the name of the diff --git a/drivers/video/backlight/corgi_lcd.c b/drivers/video/backlight/corgi_lcd.c index f8a4bb20f41a..96774949cd30 100644 --- a/drivers/video/backlight/corgi_lcd.c +++ b/drivers/video/backlight/corgi_lcd.c @@ -433,8 +433,9 @@ static int corgi_bl_update_status(struct backlight_device *bd) if (corgibl_flags & CORGIBL_SUSPENDED) intensity = 0; - if (corgibl_flags & CORGIBL_BATTLOW) - intensity &= lcd->limit_mask; + + if ((corgibl_flags & CORGIBL_BATTLOW) && intensity > lcd->limit_mask) + intensity = lcd->limit_mask; return corgi_bl_set_intensity(lcd, intensity); } @@ -639,3 +640,4 @@ module_exit(corgi_lcd_exit); MODULE_DESCRIPTION("LCD and backlight driver for SHARP C7x0/Cxx00"); MODULE_AUTHOR("Eric Miao <eric.miao@marvell.com>"); MODULE_LICENSE("GPL"); +MODULE_ALIAS("spi:corgi-lcd"); diff --git a/drivers/video/backlight/da903x_bl.c b/drivers/video/backlight/da903x_bl.c index 93bb4340cc64..701a1081e199 100644 --- a/drivers/video/backlight/da903x_bl.c +++ b/drivers/video/backlight/da903x_bl.c @@ -154,34 +154,38 @@ static int da903x_backlight_remove(struct platform_device *pdev) } #ifdef CONFIG_PM -static int da903x_backlight_suspend(struct platform_device *pdev, - pm_message_t state) +static int da903x_backlight_suspend(struct device *dev) { + struct platform_device *pdev = to_platform_device(dev); struct backlight_device *bl = platform_get_drvdata(pdev); return da903x_backlight_set(bl, 0); } -static int da903x_backlight_resume(struct platform_device *pdev) +static int da903x_backlight_resume(struct device *dev) { + struct platform_device *pdev = to_platform_device(dev); struct backlight_device *bl = platform_get_drvdata(pdev); backlight_update_status(bl); return 0; } -#else -#define da903x_backlight_suspend NULL -#define da903x_backlight_resume NULL + +static struct dev_pm_ops da903x_backlight_pm_ops = { + .suspend = da903x_backlight_suspend, + .resume = da903x_backlight_resume, +}; #endif static struct platform_driver da903x_backlight_driver = { .driver = { .name = "da903x-backlight", .owner = THIS_MODULE, +#ifdef CONFIG_PM + .pm = &da903x_backlight_pm_ops, +#endif }, .probe = da903x_backlight_probe, .remove = da903x_backlight_remove, - .suspend = da903x_backlight_suspend, - .resume = da903x_backlight_resume, }; static int __init da903x_backlight_init(void) diff --git a/drivers/video/backlight/hp680_bl.c b/drivers/video/backlight/hp680_bl.c index 5be55a20d8c7..7fb4eefff80d 100644 --- a/drivers/video/backlight/hp680_bl.c +++ b/drivers/video/backlight/hp680_bl.c @@ -103,7 +103,7 @@ static struct backlight_ops hp680bl_ops = { .update_status = hp680bl_set_intensity, }; -static int __init hp680bl_probe(struct platform_device *pdev) +static int __devinit hp680bl_probe(struct platform_device *pdev) { struct backlight_device *bd; diff --git a/drivers/video/backlight/lcd.c b/drivers/video/backlight/lcd.c index b6449470106c..a482dd7b0311 100644 --- a/drivers/video/backlight/lcd.c +++ b/drivers/video/backlight/lcd.c @@ -56,7 +56,7 @@ static int fb_notifier_callback(struct notifier_block *self, static int lcd_register_fb(struct lcd_device *ld) { - memset(&ld->fb_notif, 0, sizeof(&ld->fb_notif)); + memset(&ld->fb_notif, 0, sizeof(ld->fb_notif)); ld->fb_notif.notifier_call = fb_notifier_callback; return fb_register_client(&ld->fb_notif); } diff --git a/drivers/video/backlight/lms283gf05.c b/drivers/video/backlight/lms283gf05.c new file mode 100644 index 000000000000..447b542a20ca --- /dev/null +++ b/drivers/video/backlight/lms283gf05.c @@ -0,0 +1,242 @@ +/* + * lms283gf05.c -- support for Samsung LMS283GF05 LCD + * + * Copyright (c) 2009 Marek Vasut <marek.vasut@gmail.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/device.h> +#include <linux/kernel.h> +#include <linux/delay.h> +#include <linux/gpio.h> +#include <linux/lcd.h> + +#include <linux/spi/spi.h> +#include <linux/spi/lms283gf05.h> + +struct lms283gf05_state { + struct spi_device *spi; + struct lcd_device *ld; +}; + +struct lms283gf05_seq { + unsigned char reg; + unsigned short value; + unsigned char delay; +}; + +/* Magic sequences supplied by manufacturer, for details refer to datasheet */ +static struct lms283gf05_seq disp_initseq[] = { + /* REG, VALUE, DELAY */ + { 0x07, 0x0000, 0 }, + { 0x13, 0x0000, 10 }, + + { 0x11, 0x3004, 0 }, + { 0x14, 0x200F, 0 }, + { 0x10, 0x1a20, 0 }, + { 0x13, 0x0040, 50 }, + + { 0x13, 0x0060, 0 }, + { 0x13, 0x0070, 200 }, + + { 0x01, 0x0127, 0 }, + { 0x02, 0x0700, 0 }, + { 0x03, 0x1030, 0 }, + { 0x08, 0x0208, 0 }, + { 0x0B, 0x0620, 0 }, + { 0x0C, 0x0110, 0 }, + { 0x30, 0x0120, 0 }, + { 0x31, 0x0127, 0 }, + { 0x32, 0x0000, 0 }, + { 0x33, 0x0503, 0 }, + { 0x34, 0x0727, 0 }, + { 0x35, 0x0124, 0 }, + { 0x36, 0x0706, 0 }, + { 0x37, 0x0701, 0 }, + { 0x38, 0x0F00, 0 }, + { 0x39, 0x0F00, 0 }, + { 0x40, 0x0000, 0 }, + { 0x41, 0x0000, 0 }, + { 0x42, 0x013f, 0 }, + { 0x43, 0x0000, 0 }, + { 0x44, 0x013f, 0 }, + { 0x45, 0x0000, 0 }, + { 0x46, 0xef00, 0 }, + { 0x47, 0x013f, 0 }, + { 0x48, 0x0000, 0 }, + { 0x07, 0x0015, 30 }, + + { 0x07, 0x0017, 0 }, + + { 0x20, 0x0000, 0 }, + { 0x21, 0x0000, 0 }, + { 0x22, 0x0000, 0 } +}; + +static struct lms283gf05_seq disp_pdwnseq[] = { + { 0x07, 0x0016, 30 }, + + { 0x07, 0x0004, 0 }, + { 0x10, 0x0220, 20 }, + + { 0x13, 0x0060, 50 }, + + { 0x13, 0x0040, 50 }, + + { 0x13, 0x0000, 0 }, + { 0x10, 0x0000, 0 } +}; + + +static void lms283gf05_reset(unsigned long gpio, bool inverted) +{ + gpio_set_value(gpio, !inverted); + mdelay(100); + gpio_set_value(gpio, inverted); + mdelay(20); + gpio_set_value(gpio, !inverted); + mdelay(20); +} + +static void lms283gf05_toggle(struct spi_device *spi, + struct lms283gf05_seq *seq, int sz) +{ + char buf[3]; + int i; + + for (i = 0; i < sz; i++) { + buf[0] = 0x74; + buf[1] = 0x00; + buf[2] = seq[i].reg; + spi_write(spi, buf, 3); + + buf[0] = 0x76; + buf[1] = seq[i].value >> 8; + buf[2] = seq[i].value & 0xff; + spi_write(spi, buf, 3); + + mdelay(seq[i].delay); + } +} + +static int lms283gf05_power_set(struct lcd_device *ld, int power) +{ + struct lms283gf05_state *st = lcd_get_data(ld); + struct spi_device *spi = st->spi; + struct lms283gf05_pdata *pdata = spi->dev.platform_data; + + if (power) { + if (pdata) + lms283gf05_reset(pdata->reset_gpio, + pdata->reset_inverted); + lms283gf05_toggle(spi, disp_initseq, ARRAY_SIZE(disp_initseq)); + } else { + lms283gf05_toggle(spi, disp_pdwnseq, ARRAY_SIZE(disp_pdwnseq)); + if (pdata) + gpio_set_value(pdata->reset_gpio, + pdata->reset_inverted); + } + + return 0; +} + +static struct lcd_ops lms_ops = { + .set_power = lms283gf05_power_set, + .get_power = NULL, +}; + +static int __devinit lms283gf05_probe(struct spi_device *spi) +{ + struct lms283gf05_state *st; + struct lms283gf05_pdata *pdata = spi->dev.platform_data; + struct lcd_device *ld; + int ret = 0; + + if (pdata != NULL) { + ret = gpio_request(pdata->reset_gpio, "LMS285GF05 RESET"); + if (ret) + return ret; + + ret = gpio_direction_output(pdata->reset_gpio, + !pdata->reset_inverted); + if (ret) + goto err; + } + + st = kzalloc(sizeof(struct lms283gf05_state), GFP_KERNEL); + if (st == NULL) { + dev_err(&spi->dev, "No memory for device state\n"); + ret = -ENOMEM; + goto err; + } + + ld = lcd_device_register("lms283gf05", &spi->dev, st, &lms_ops); + if (IS_ERR(ld)) { + ret = PTR_ERR(ld); + goto err2; + } + + st->spi = spi; + st->ld = ld; + + dev_set_drvdata(&spi->dev, st); + + /* kick in the LCD */ + if (pdata) + lms283gf05_reset(pdata->reset_gpio, pdata->reset_inverted); + lms283gf05_toggle(spi, disp_initseq, ARRAY_SIZE(disp_initseq)); + + return 0; + +err2: + kfree(st); +err: + if (pdata != NULL) + gpio_free(pdata->reset_gpio); + + return ret; +} + +static int __devexit lms283gf05_remove(struct spi_device *spi) +{ + struct lms283gf05_state *st = dev_get_drvdata(&spi->dev); + struct lms283gf05_pdata *pdata = st->spi->dev.platform_data; + + lcd_device_unregister(st->ld); + + if (pdata != NULL) + gpio_free(pdata->reset_gpio); + + kfree(st); + + return 0; +} + +static struct spi_driver lms283gf05_driver = { + .driver = { + .name = "lms283gf05", + .owner = THIS_MODULE, + }, + .probe = lms283gf05_probe, + .remove = __devexit_p(lms283gf05_remove), +}; + +static __init int lms283gf05_init(void) +{ + return spi_register_driver(&lms283gf05_driver); +} + +static __exit void lms283gf05_exit(void) +{ + spi_unregister_driver(&lms283gf05_driver); +} + +module_init(lms283gf05_init); +module_exit(lms283gf05_exit); + +MODULE_AUTHOR("Marek Vasut <marek.vasut@gmail.com>"); +MODULE_DESCRIPTION("LCD283GF05 LCD"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/video/backlight/ltv350qv.c b/drivers/video/backlight/ltv350qv.c index 2eb206bf73e6..4631ca8fa4a4 100644 --- a/drivers/video/backlight/ltv350qv.c +++ b/drivers/video/backlight/ltv350qv.c @@ -328,3 +328,4 @@ module_exit(ltv350qv_exit); MODULE_AUTHOR("Haavard Skinnemoen <hskinnemoen@atmel.com>"); MODULE_DESCRIPTION("Samsung LTV350QV LCD Driver"); MODULE_LICENSE("GPL"); +MODULE_ALIAS("spi:ltv350qv"); diff --git a/drivers/video/backlight/mbp_nvidia_bl.c b/drivers/video/backlight/mbp_nvidia_bl.c index 3bb4c0a50c62..9edb8d7c295f 100644 --- a/drivers/video/backlight/mbp_nvidia_bl.c +++ b/drivers/video/backlight/mbp_nvidia_bl.c @@ -166,6 +166,15 @@ static const struct dmi_system_id __initdata mbp_device_table[] = { }, { .callback = mbp_dmi_match, + .ident = "MacBookAir 1,1", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "MacBookAir1,1"), + }, + .driver_data = (void *)&intel_chipset_data, + }, + { + .callback = mbp_dmi_match, .ident = "MacBook 5,1", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc."), @@ -175,6 +184,15 @@ static const struct dmi_system_id __initdata mbp_device_table[] = { }, { .callback = mbp_dmi_match, + .ident = "MacBook 5,2", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "MacBook5,2"), + }, + .driver_data = (void *)&nvidia_chipset_data, + }, + { + .callback = mbp_dmi_match, .ident = "MacBookAir 2,1", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc."), @@ -191,6 +209,24 @@ static const struct dmi_system_id __initdata mbp_device_table[] = { }, .driver_data = (void *)&nvidia_chipset_data, }, + { + .callback = mbp_dmi_match, + .ident = "MacBookPro 5,2", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro5,2"), + }, + .driver_data = (void *)&nvidia_chipset_data, + }, + { + .callback = mbp_dmi_match, + .ident = "MacBookPro 5,5", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro5,5"), + }, + .driver_data = (void *)&nvidia_chipset_data, + }, { } }; diff --git a/drivers/video/backlight/tdo24m.c b/drivers/video/backlight/tdo24m.c index 51422fc4f606..bbfb502add67 100644 --- a/drivers/video/backlight/tdo24m.c +++ b/drivers/video/backlight/tdo24m.c @@ -472,3 +472,4 @@ module_exit(tdo24m_exit); MODULE_AUTHOR("Eric Miao <eric.miao@marvell.com>"); MODULE_DESCRIPTION("Driver for Toppoly TDO24M LCD Panel"); MODULE_LICENSE("GPL"); +MODULE_ALIAS("spi:tdo24m"); diff --git a/drivers/video/backlight/tosa_lcd.c b/drivers/video/backlight/tosa_lcd.c index b7fbc75a62fc..50ec17dfc517 100644 --- a/drivers/video/backlight/tosa_lcd.c +++ b/drivers/video/backlight/tosa_lcd.c @@ -300,4 +300,4 @@ module_exit(tosa_lcd_exit); MODULE_AUTHOR("Dmitry Baryshkov"); MODULE_LICENSE("GPL v2"); MODULE_DESCRIPTION("LCD/Backlight control for Sharp SL-6000 PDA"); - +MODULE_ALIAS("spi:tosa-lcd"); diff --git a/drivers/video/backlight/vgg2432a4.c b/drivers/video/backlight/vgg2432a4.c index 8e653b8a6f17..b49063c831e7 100644 --- a/drivers/video/backlight/vgg2432a4.c +++ b/drivers/video/backlight/vgg2432a4.c @@ -280,5 +280,4 @@ module_exit(vgg2432a4_exit); MODULE_AUTHOR("Ben Dooks <ben-linux@fluff.org>"); MODULE_DESCRIPTION("VGG2432A4 LCD Driver"); MODULE_LICENSE("GPL v2"); - - +MODULE_ALIAS("spi:VGG2432A4"); diff --git a/drivers/video/backlight/wm831x_bl.c b/drivers/video/backlight/wm831x_bl.c new file mode 100644 index 000000000000..467bdb7efb23 --- /dev/null +++ b/drivers/video/backlight/wm831x_bl.c @@ -0,0 +1,250 @@ +/* + * Backlight driver for Wolfson Microelectronics WM831x PMICs + * + * Copyright 2009 Wolfson Microelectonics plc + * + * 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/fb.h> +#include <linux/backlight.h> + +#include <linux/mfd/wm831x/core.h> +#include <linux/mfd/wm831x/pdata.h> +#include <linux/mfd/wm831x/regulator.h> + +struct wm831x_backlight_data { + struct wm831x *wm831x; + int isink_reg; + int current_brightness; +}; + +static int wm831x_backlight_set(struct backlight_device *bl, int brightness) +{ + struct wm831x_backlight_data *data = bl_get_data(bl); + struct wm831x *wm831x = data->wm831x; + int power_up = !data->current_brightness && brightness; + int power_down = data->current_brightness && !brightness; + int ret; + + if (power_up) { + /* Enable the ISINK */ + ret = wm831x_set_bits(wm831x, data->isink_reg, + WM831X_CS1_ENA, WM831X_CS1_ENA); + if (ret < 0) + goto err; + + /* Enable the DC-DC */ + ret = wm831x_set_bits(wm831x, WM831X_DCDC_ENABLE, + WM831X_DC4_ENA, WM831X_DC4_ENA); + if (ret < 0) + goto err; + } + + if (power_down) { + /* DCDC first */ + ret = wm831x_set_bits(wm831x, WM831X_DCDC_ENABLE, + WM831X_DC4_ENA, 0); + if (ret < 0) + goto err; + + /* ISINK */ + ret = wm831x_set_bits(wm831x, data->isink_reg, + WM831X_CS1_DRIVE | WM831X_CS1_ENA, 0); + if (ret < 0) + goto err; + } + + /* Set the new brightness */ + ret = wm831x_set_bits(wm831x, data->isink_reg, + WM831X_CS1_ISEL_MASK, brightness); + if (ret < 0) + goto err; + + if (power_up) { + /* Drive current through the ISINK */ + ret = wm831x_set_bits(wm831x, data->isink_reg, + WM831X_CS1_DRIVE, WM831X_CS1_DRIVE); + if (ret < 0) + return ret; + } + + data->current_brightness = brightness; + + return 0; + +err: + /* If we were in the middle of a power transition always shut down + * for safety. + */ + if (power_up || power_down) { + wm831x_set_bits(wm831x, WM831X_DCDC_ENABLE, WM831X_DC4_ENA, 0); + wm831x_set_bits(wm831x, data->isink_reg, WM831X_CS1_ENA, 0); + } + + return ret; +} + +static int wm831x_backlight_update_status(struct backlight_device *bl) +{ + int brightness = bl->props.brightness; + + if (bl->props.power != FB_BLANK_UNBLANK) + brightness = 0; + + if (bl->props.fb_blank != FB_BLANK_UNBLANK) + brightness = 0; + + if (bl->props.state & BL_CORE_SUSPENDED) + brightness = 0; + + return wm831x_backlight_set(bl, brightness); +} + +static int wm831x_backlight_get_brightness(struct backlight_device *bl) +{ + struct wm831x_backlight_data *data = bl_get_data(bl); + return data->current_brightness; +} + +static struct backlight_ops wm831x_backlight_ops = { + .options = BL_CORE_SUSPENDRESUME, + .update_status = wm831x_backlight_update_status, + .get_brightness = wm831x_backlight_get_brightness, +}; + +static int wm831x_backlight_probe(struct platform_device *pdev) +{ + struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent); + struct wm831x_pdata *wm831x_pdata; + struct wm831x_backlight_pdata *pdata; + struct wm831x_backlight_data *data; + struct backlight_device *bl; + int ret, i, max_isel, isink_reg, dcdc_cfg; + + /* We need platform data */ + if (pdev->dev.parent->platform_data) { + wm831x_pdata = pdev->dev.parent->platform_data; + pdata = wm831x_pdata->backlight; + } else { + pdata = NULL; + } + + if (!pdata) { + dev_err(&pdev->dev, "No platform data supplied\n"); + return -EINVAL; + } + + /* Figure out the maximum current we can use */ + for (i = 0; i < WM831X_ISINK_MAX_ISEL; i++) { + if (wm831x_isinkv_values[i] > pdata->max_uA) + break; + } + + if (i == 0) { + dev_err(&pdev->dev, "Invalid max_uA: %duA\n", pdata->max_uA); + return -EINVAL; + } + max_isel = i - 1; + + if (pdata->max_uA != wm831x_isinkv_values[max_isel]) + dev_warn(&pdev->dev, + "Maximum current is %duA not %duA as requested\n", + wm831x_isinkv_values[max_isel], pdata->max_uA); + + switch (pdata->isink) { + case 1: + isink_reg = WM831X_CURRENT_SINK_1; + dcdc_cfg = 0; + break; + case 2: + isink_reg = WM831X_CURRENT_SINK_2; + dcdc_cfg = WM831X_DC4_FBSRC; + break; + default: + dev_err(&pdev->dev, "Invalid ISINK %d\n", pdata->isink); + return -EINVAL; + } + + /* Configure the ISINK to use for feedback */ + ret = wm831x_reg_unlock(wm831x); + if (ret < 0) + return ret; + + ret = wm831x_set_bits(wm831x, WM831X_DC4_CONTROL, WM831X_DC4_FBSRC, + dcdc_cfg); + + wm831x_reg_lock(wm831x); + if (ret < 0) + return ret; + + data = kzalloc(sizeof(*data), GFP_KERNEL); + if (data == NULL) + return -ENOMEM; + + data->wm831x = wm831x; + data->current_brightness = 0; + data->isink_reg = isink_reg; + + bl = backlight_device_register("wm831x", &pdev->dev, + data, &wm831x_backlight_ops); + if (IS_ERR(bl)) { + dev_err(&pdev->dev, "failed to register backlight\n"); + kfree(data); + return PTR_ERR(bl); + } + + bl->props.max_brightness = max_isel; + bl->props.brightness = max_isel; + + platform_set_drvdata(pdev, bl); + + /* Disable the DCDC if it was started so we can bootstrap */ + wm831x_set_bits(wm831x, WM831X_DCDC_ENABLE, WM831X_DC4_ENA, 0); + + + backlight_update_status(bl); + + return 0; +} + +static int wm831x_backlight_remove(struct platform_device *pdev) +{ + struct backlight_device *bl = platform_get_drvdata(pdev); + struct wm831x_backlight_data *data = bl_get_data(bl); + + backlight_device_unregister(bl); + kfree(data); + return 0; +} + +static struct platform_driver wm831x_backlight_driver = { + .driver = { + .name = "wm831x-backlight", + .owner = THIS_MODULE, + }, + .probe = wm831x_backlight_probe, + .remove = wm831x_backlight_remove, +}; + +static int __init wm831x_backlight_init(void) +{ + return platform_driver_register(&wm831x_backlight_driver); +} +module_init(wm831x_backlight_init); + +static void __exit wm831x_backlight_exit(void) +{ + platform_driver_unregister(&wm831x_backlight_driver); +} +module_exit(wm831x_backlight_exit); + +MODULE_DESCRIPTION("Backlight Driver for WM831x PMICs"); +MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:wm831x-backlight"); diff --git a/drivers/video/console/bitblit.c b/drivers/video/console/bitblit.c index 69864b1b3f9e..6b7c8fbc5495 100644 --- a/drivers/video/console/bitblit.c +++ b/drivers/video/console/bitblit.c @@ -25,7 +25,7 @@ static inline void update_attr(u8 *dst, u8 *src, int attribute, struct vc_data *vc) { int i, offset = (vc->vc_font.height < 10) ? 1 : 2; - int width = (vc->vc_font.width + 7) >> 3; + int width = DIV_ROUND_UP(vc->vc_font.width, 8); unsigned int cellsize = vc->vc_font.height * width; u8 c; @@ -144,7 +144,7 @@ static void bit_putcs(struct vc_data *vc, struct fb_info *info, int fg, int bg) { struct fb_image image; - u32 width = (vc->vc_font.width + 7)/8; + u32 width = DIV_ROUND_UP(vc->vc_font.width, 8); u32 cellsize = width * vc->vc_font.height; u32 maxcnt = info->pixmap.size/cellsize; u32 scan_align = info->pixmap.scan_align - 1; @@ -173,7 +173,7 @@ static void bit_putcs(struct vc_data *vc, struct fb_info *info, cnt = count; image.width = vc->vc_font.width * cnt; - pitch = ((image.width + 7) >> 3) + scan_align; + pitch = DIV_ROUND_UP(image.width, 8) + scan_align; pitch &= ~scan_align; size = pitch * image.height + buf_align; size &= ~buf_align; @@ -239,7 +239,7 @@ static void bit_cursor(struct vc_data *vc, struct fb_info *info, int mode, struct fb_cursor cursor; struct fbcon_ops *ops = info->fbcon_par; unsigned short charmask = vc->vc_hi_font_mask ? 0x1ff : 0xff; - int w = (vc->vc_font.width + 7) >> 3, c; + int w = DIV_ROUND_UP(vc->vc_font.width, 8), c; int y = real_y(ops->p, vc->vc_y); int attribute, use_sw = (vc->vc_cursor_type & 0x10); int err = 1; diff --git a/drivers/video/console/fbcon.c b/drivers/video/console/fbcon.c index 3a44695b9c09..3681c6a88212 100644 --- a/drivers/video/console/fbcon.c +++ b/drivers/video/console/fbcon.c @@ -114,6 +114,7 @@ static int last_fb_vc = MAX_NR_CONSOLES - 1; static int fbcon_is_default = 1; static int fbcon_has_exited; static int primary_device = -1; +static int fbcon_has_console_bind; #ifdef CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY static int map_override; @@ -544,6 +545,8 @@ static int fbcon_takeover(int show_logo) con2fb_map[i] = -1; } info_idx = -1; + } else { + fbcon_has_console_bind = 1; } return err; @@ -725,7 +728,7 @@ static int con2fb_release_oldinfo(struct vc_data *vc, struct fb_info *oldinfo, int oldidx, int found) { struct fbcon_ops *ops = oldinfo->fbcon_par; - int err = 0; + int err = 0, ret; if (oldinfo->fbops->fb_release && oldinfo->fbops->fb_release(oldinfo, 0)) { @@ -752,8 +755,14 @@ static int con2fb_release_oldinfo(struct vc_data *vc, struct fb_info *oldinfo, newinfo in an undefined state. Thus, a call to fb_set_par() may be needed for the newinfo. */ - if (newinfo->fbops->fb_set_par) - newinfo->fbops->fb_set_par(newinfo); + if (newinfo->fbops->fb_set_par) { + ret = newinfo->fbops->fb_set_par(newinfo); + + if (ret) + printk(KERN_ERR "con2fb_release_oldinfo: " + "detected unhandled fb_set_par error, " + "error code %d\n", ret); + } } return err; @@ -763,11 +772,18 @@ static void con2fb_init_display(struct vc_data *vc, struct fb_info *info, int unit, int show_logo) { struct fbcon_ops *ops = info->fbcon_par; + int ret; ops->currcon = fg_console; - if (info->fbops->fb_set_par && !(ops->flags & FBCON_FLAGS_INIT)) - info->fbops->fb_set_par(info); + if (info->fbops->fb_set_par && !(ops->flags & FBCON_FLAGS_INIT)) { + ret = info->fbops->fb_set_par(info); + + if (ret) + printk(KERN_ERR "con2fb_init_display: detected " + "unhandled fb_set_par error, " + "error code %d\n", ret); + } ops->flags |= FBCON_FLAGS_INIT; ops->graphics = 0; @@ -1006,7 +1022,7 @@ static void fbcon_init(struct vc_data *vc, int init) struct vc_data *svc = *default_mode; struct display *t, *p = &fb_display[vc->vc_num]; int logo = 1, new_rows, new_cols, rows, cols, charcnt = 256; - int cap; + int cap, ret; if (info_idx == -1 || info == NULL) return; @@ -1092,8 +1108,15 @@ static void fbcon_init(struct vc_data *vc, int init) */ if (CON_IS_VISIBLE(vc) && vc->vc_mode == KD_TEXT) { if (info->fbops->fb_set_par && - !(ops->flags & FBCON_FLAGS_INIT)) - info->fbops->fb_set_par(info); + !(ops->flags & FBCON_FLAGS_INIT)) { + ret = info->fbops->fb_set_par(info); + + if (ret) + printk(KERN_ERR "fbcon_init: detected " + "unhandled fb_set_par error, " + "error code %d\n", ret); + } + ops->flags |= FBCON_FLAGS_INIT; } @@ -2119,7 +2142,7 @@ static int fbcon_switch(struct vc_data *vc) struct fbcon_ops *ops; struct display *p = &fb_display[vc->vc_num]; struct fb_var_screeninfo var; - int i, prev_console, charcnt = 256; + int i, ret, prev_console, charcnt = 256; info = registered_fb[con2fb_map[vc->vc_num]]; ops = info->fbcon_par; @@ -2174,8 +2197,14 @@ static int fbcon_switch(struct vc_data *vc) if (old_info != NULL && (old_info != info || info->flags & FBINFO_MISC_ALWAYS_SETPAR)) { - if (info->fbops->fb_set_par) - info->fbops->fb_set_par(info); + if (info->fbops->fb_set_par) { + ret = info->fbops->fb_set_par(info); + + if (ret) + printk(KERN_ERR "fbcon_switch: detected " + "unhandled fb_set_par error, " + "error code %d\n", ret); + } if (old_info != info) fbcon_del_cursor_timer(old_info); @@ -2282,14 +2311,11 @@ static int fbcon_blank(struct vc_data *vc, int blank, int mode_switch) ops->graphics = 1; if (!blank) { - if (info->fbops->fb_save_state) - info->fbops->fb_save_state(info); var.activate = FB_ACTIVATE_NOW | FB_ACTIVATE_FORCE; fb_set_var(info, &var); ops->graphics = 0; ops->var = info->var; - } else if (info->fbops->fb_restore_state) - info->fbops->fb_restore_state(info); + } } if (!fbcon_is_inactive(vc, info)) { @@ -2923,6 +2949,10 @@ static int fbcon_unbind(void) ret = unbind_con_driver(&fb_con, first_fb_vc, last_fb_vc, fbcon_is_default); + + if (!ret) + fbcon_has_console_bind = 0; + return ret; } #else @@ -2936,6 +2966,9 @@ static int fbcon_fb_unbind(int idx) { int i, new_idx = -1, ret = 0; + if (!fbcon_has_console_bind) + return 0; + for (i = first_fb_vc; i <= last_fb_vc; i++) { if (con2fb_map[i] != idx && con2fb_map[i] != -1) { diff --git a/drivers/video/console/newport_con.c b/drivers/video/console/newport_con.c index d31b203bf654..3772433c49d1 100644 --- a/drivers/video/console/newport_con.c +++ b/drivers/video/console/newport_con.c @@ -216,7 +216,7 @@ static void newport_get_screensize(void) } newport_xsize = newport_ysize = 0; - for (i = 0; linetable[i + 1] && (i < sizeof(linetable)); i += 2) { + for (i = 0; i < ARRAY_SIZE(linetable) - 1 && linetable[i + 1]; i += 2) { cols = 0; newport_vc2_set(npregs, VC2_IREG_RADDR, linetable[i]); npregs->set.dcbmode = (NPORT_DMODE_AVC2 | VC2_REGADDR_RAM | diff --git a/drivers/video/console/vgacon.c b/drivers/video/console/vgacon.c index 74e96cf83b7e..da55ccaf4d55 100644 --- a/drivers/video/console/vgacon.c +++ b/drivers/video/console/vgacon.c @@ -589,12 +589,14 @@ static void vgacon_init(struct vc_data *c, int init) static void vgacon_deinit(struct vc_data *c) { - /* When closing the last console, reset video origin */ - if (!--vgacon_uni_pagedir[1]) { + /* When closing the active console, reset video origin */ + if (CON_IS_VISIBLE(c)) { c->vc_visible_origin = vga_vram_base; vga_set_mem_top(c); - con_free_unimap(c); } + + if (!--vgacon_uni_pagedir[1]) + con_free_unimap(c); c->vc_uni_pagedir_loc = &c->vc_uni_pagedir; con_set_default_unimap(c); } diff --git a/drivers/video/da8xx-fb.c b/drivers/video/da8xx-fb.c new file mode 100644 index 000000000000..035d56835b75 --- /dev/null +++ b/drivers/video/da8xx-fb.c @@ -0,0 +1,889 @@ +/* + * Copyright (C) 2008-2009 MontaVista Software Inc. + * Copyright (C) 2008-2009 Texas Instruments Inc + * + * Based on the LCD driver for TI Avalanche processors written by + * Ajay Singh and Shalom Hai. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option)any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/fb.h> +#include <linux/dma-mapping.h> +#include <linux/device.h> +#include <linux/platform_device.h> +#include <linux/uaccess.h> +#include <linux/interrupt.h> +#include <linux/clk.h> +#include <video/da8xx-fb.h> + +#define DRIVER_NAME "da8xx_lcdc" + +/* LCD Status Register */ +#define LCD_END_OF_FRAME0 BIT(8) +#define LCD_FIFO_UNDERFLOW BIT(5) +#define LCD_SYNC_LOST BIT(2) + +/* LCD DMA Control Register */ +#define LCD_DMA_BURST_SIZE(x) ((x) << 4) +#define LCD_DMA_BURST_1 0x0 +#define LCD_DMA_BURST_2 0x1 +#define LCD_DMA_BURST_4 0x2 +#define LCD_DMA_BURST_8 0x3 +#define LCD_DMA_BURST_16 0x4 +#define LCD_END_OF_FRAME_INT_ENA BIT(2) +#define LCD_DUAL_FRAME_BUFFER_ENABLE BIT(0) + +/* LCD Control Register */ +#define LCD_CLK_DIVISOR(x) ((x) << 8) +#define LCD_RASTER_MODE 0x01 + +/* LCD Raster Control Register */ +#define LCD_PALETTE_LOAD_MODE(x) ((x) << 20) +#define PALETTE_AND_DATA 0x00 +#define PALETTE_ONLY 0x01 + +#define LCD_MONO_8BIT_MODE BIT(9) +#define LCD_RASTER_ORDER BIT(8) +#define LCD_TFT_MODE BIT(7) +#define LCD_UNDERFLOW_INT_ENA BIT(6) +#define LCD_MONOCHROME_MODE BIT(1) +#define LCD_RASTER_ENABLE BIT(0) +#define LCD_TFT_ALT_ENABLE BIT(23) +#define LCD_STN_565_ENABLE BIT(24) + +/* LCD Raster Timing 2 Register */ +#define LCD_AC_BIAS_TRANSITIONS_PER_INT(x) ((x) << 16) +#define LCD_AC_BIAS_FREQUENCY(x) ((x) << 8) +#define LCD_SYNC_CTRL BIT(25) +#define LCD_SYNC_EDGE BIT(24) +#define LCD_INVERT_PIXEL_CLOCK BIT(22) +#define LCD_INVERT_LINE_CLOCK BIT(21) +#define LCD_INVERT_FRAME_CLOCK BIT(20) + +/* LCD Block */ +#define LCD_CTRL_REG 0x4 +#define LCD_STAT_REG 0x8 +#define LCD_RASTER_CTRL_REG 0x28 +#define LCD_RASTER_TIMING_0_REG 0x2C +#define LCD_RASTER_TIMING_1_REG 0x30 +#define LCD_RASTER_TIMING_2_REG 0x34 +#define LCD_DMA_CTRL_REG 0x40 +#define LCD_DMA_FRM_BUF_BASE_ADDR_0_REG 0x44 +#define LCD_DMA_FRM_BUF_CEILING_ADDR_0_REG 0x48 + +#define WSI_TIMEOUT 50 +#define PALETTE_SIZE 256 +#define LEFT_MARGIN 64 +#define RIGHT_MARGIN 64 +#define UPPER_MARGIN 32 +#define LOWER_MARGIN 32 + +static resource_size_t da8xx_fb_reg_base; +static struct resource *lcdc_regs; + +static inline unsigned int lcdc_read(unsigned int addr) +{ + return (unsigned int)__raw_readl(da8xx_fb_reg_base + (addr)); +} + +static inline void lcdc_write(unsigned int val, unsigned int addr) +{ + __raw_writel(val, da8xx_fb_reg_base + (addr)); +} + +struct da8xx_fb_par { + resource_size_t p_palette_base; + unsigned char *v_palette_base; + struct clk *lcdc_clk; + int irq; + unsigned short pseudo_palette[16]; + unsigned int databuf_sz; + unsigned int palette_sz; +}; + +/* Variable Screen Information */ +static struct fb_var_screeninfo da8xx_fb_var __devinitdata = { + .xoffset = 0, + .yoffset = 0, + .transp = {0, 0, 0}, + .nonstd = 0, + .activate = 0, + .height = -1, + .width = -1, + .pixclock = 46666, /* 46us - AUO display */ + .accel_flags = 0, + .left_margin = LEFT_MARGIN, + .right_margin = RIGHT_MARGIN, + .upper_margin = UPPER_MARGIN, + .lower_margin = LOWER_MARGIN, + .sync = 0, + .vmode = FB_VMODE_NONINTERLACED +}; + +static struct fb_fix_screeninfo da8xx_fb_fix __devinitdata = { + .id = "DA8xx FB Drv", + .type = FB_TYPE_PACKED_PIXELS, + .type_aux = 0, + .visual = FB_VISUAL_PSEUDOCOLOR, + .xpanstep = 1, + .ypanstep = 1, + .ywrapstep = 1, + .accel = FB_ACCEL_NONE +}; + +struct da8xx_panel { + const char name[25]; /* Full name <vendor>_<model> */ + unsigned short width; + unsigned short height; + int hfp; /* Horizontal front porch */ + int hbp; /* Horizontal back porch */ + int hsw; /* Horizontal Sync Pulse Width */ + int vfp; /* Vertical front porch */ + int vbp; /* Vertical back porch */ + int vsw; /* Vertical Sync Pulse Width */ + int pxl_clk; /* Pixel clock */ + unsigned char invert_pxl_clk; /* Invert Pixel clock */ +}; + +static struct da8xx_panel known_lcd_panels[] = { + /* Sharp LCD035Q3DG01 */ + [0] = { + .name = "Sharp_LCD035Q3DG01", + .width = 320, + .height = 240, + .hfp = 8, + .hbp = 6, + .hsw = 0, + .vfp = 2, + .vbp = 2, + .vsw = 0, + .pxl_clk = 0x10, + .invert_pxl_clk = 1, + }, + /* Sharp LK043T1DG01 */ + [1] = { + .name = "Sharp_LK043T1DG01", + .width = 480, + .height = 272, + .hfp = 2, + .hbp = 2, + .hsw = 41, + .vfp = 2, + .vbp = 2, + .vsw = 10, + .pxl_clk = 0x12, + .invert_pxl_clk = 0, + }, +}; + +/* Disable the Raster Engine of the LCD Controller */ +static void lcd_disable_raster(struct da8xx_fb_par *par) +{ + u32 reg; + + reg = lcdc_read(LCD_RASTER_CTRL_REG); + if (reg & LCD_RASTER_ENABLE) + lcdc_write(reg & ~LCD_RASTER_ENABLE, LCD_RASTER_CTRL_REG); +} + +static void lcd_blit(int load_mode, struct da8xx_fb_par *par) +{ + u32 tmp = par->p_palette_base + par->databuf_sz - 4; + u32 reg; + + /* Update the databuf in the hw. */ + lcdc_write(par->p_palette_base, LCD_DMA_FRM_BUF_BASE_ADDR_0_REG); + lcdc_write(tmp, LCD_DMA_FRM_BUF_CEILING_ADDR_0_REG); + + /* Start the DMA. */ + reg = lcdc_read(LCD_RASTER_CTRL_REG); + reg &= ~(3 << 20); + if (load_mode == LOAD_DATA) + reg |= LCD_PALETTE_LOAD_MODE(PALETTE_AND_DATA); + else if (load_mode == LOAD_PALETTE) + reg |= LCD_PALETTE_LOAD_MODE(PALETTE_ONLY); + + lcdc_write(reg, LCD_RASTER_CTRL_REG); +} + +/* Configure the Burst Size of DMA */ +static int lcd_cfg_dma(int burst_size) +{ + u32 reg; + + reg = lcdc_read(LCD_DMA_CTRL_REG) & 0x00000001; + switch (burst_size) { + case 1: + reg |= LCD_DMA_BURST_SIZE(LCD_DMA_BURST_1); + break; + case 2: + reg |= LCD_DMA_BURST_SIZE(LCD_DMA_BURST_2); + break; + case 4: + reg |= LCD_DMA_BURST_SIZE(LCD_DMA_BURST_4); + break; + case 8: + reg |= LCD_DMA_BURST_SIZE(LCD_DMA_BURST_8); + break; + case 16: + reg |= LCD_DMA_BURST_SIZE(LCD_DMA_BURST_16); + break; + default: + return -EINVAL; + } + lcdc_write(reg, LCD_DMA_CTRL_REG); + + return 0; +} + +static void lcd_cfg_ac_bias(int period, int transitions_per_int) +{ + u32 reg; + + /* Set the AC Bias Period and Number of Transisitons per Interrupt */ + reg = lcdc_read(LCD_RASTER_TIMING_2_REG) & 0xFFF00000; + reg |= LCD_AC_BIAS_FREQUENCY(period) | + LCD_AC_BIAS_TRANSITIONS_PER_INT(transitions_per_int); + lcdc_write(reg, LCD_RASTER_TIMING_2_REG); +} + +static void lcd_cfg_horizontal_sync(int back_porch, int pulse_width, + int front_porch) +{ + u32 reg; + + reg = lcdc_read(LCD_RASTER_TIMING_0_REG) & 0xf; + reg |= ((back_porch & 0xff) << 24) + | ((front_porch & 0xff) << 16) + | ((pulse_width & 0x3f) << 10); + lcdc_write(reg, LCD_RASTER_TIMING_0_REG); +} + +static void lcd_cfg_vertical_sync(int back_porch, int pulse_width, + int front_porch) +{ + u32 reg; + + reg = lcdc_read(LCD_RASTER_TIMING_1_REG) & 0x3ff; + reg |= ((back_porch & 0xff) << 24) + | ((front_porch & 0xff) << 16) + | ((pulse_width & 0x3f) << 10); + lcdc_write(reg, LCD_RASTER_TIMING_1_REG); +} + +static int lcd_cfg_display(const struct lcd_ctrl_config *cfg) +{ + u32 reg; + + reg = lcdc_read(LCD_RASTER_CTRL_REG) & ~(LCD_TFT_MODE | + LCD_MONO_8BIT_MODE | + LCD_MONOCHROME_MODE); + + switch (cfg->p_disp_panel->panel_shade) { + case MONOCHROME: + reg |= LCD_MONOCHROME_MODE; + if (cfg->mono_8bit_mode) + reg |= LCD_MONO_8BIT_MODE; + break; + case COLOR_ACTIVE: + reg |= LCD_TFT_MODE; + if (cfg->tft_alt_mode) + reg |= LCD_TFT_ALT_ENABLE; + break; + + case COLOR_PASSIVE: + if (cfg->stn_565_mode) + reg |= LCD_STN_565_ENABLE; + break; + + default: + return -EINVAL; + } + + /* enable additional interrupts here */ + reg |= LCD_UNDERFLOW_INT_ENA; + + lcdc_write(reg, LCD_RASTER_CTRL_REG); + + reg = lcdc_read(LCD_RASTER_TIMING_2_REG); + + if (cfg->sync_ctrl) + reg |= LCD_SYNC_CTRL; + else + reg &= ~LCD_SYNC_CTRL; + + if (cfg->sync_edge) + reg |= LCD_SYNC_EDGE; + else + reg &= ~LCD_SYNC_EDGE; + + if (cfg->invert_line_clock) + reg |= LCD_INVERT_LINE_CLOCK; + else + reg &= ~LCD_INVERT_LINE_CLOCK; + + if (cfg->invert_frm_clock) + reg |= LCD_INVERT_FRAME_CLOCK; + else + reg &= ~LCD_INVERT_FRAME_CLOCK; + + lcdc_write(reg, LCD_RASTER_TIMING_2_REG); + + return 0; +} + +static int lcd_cfg_frame_buffer(struct da8xx_fb_par *par, u32 width, u32 height, + u32 bpp, u32 raster_order) +{ + u32 bpl, reg; + + /* Disable Dual Frame Buffer. */ + reg = lcdc_read(LCD_DMA_CTRL_REG); + lcdc_write(reg & ~LCD_DUAL_FRAME_BUFFER_ENABLE, + LCD_DMA_CTRL_REG); + /* Set the Panel Width */ + /* Pixels per line = (PPL + 1)*16 */ + /*0x3F in bits 4..9 gives max horisontal resolution = 1024 pixels*/ + width &= 0x3f0; + reg = lcdc_read(LCD_RASTER_TIMING_0_REG); + reg &= 0xfffffc00; + reg |= ((width >> 4) - 1) << 4; + lcdc_write(reg, LCD_RASTER_TIMING_0_REG); + + /* Set the Panel Height */ + reg = lcdc_read(LCD_RASTER_TIMING_1_REG); + reg = ((height - 1) & 0x3ff) | (reg & 0xfffffc00); + lcdc_write(reg, LCD_RASTER_TIMING_1_REG); + + /* Set the Raster Order of the Frame Buffer */ + reg = lcdc_read(LCD_RASTER_CTRL_REG) & ~(1 << 8); + if (raster_order) + reg |= LCD_RASTER_ORDER; + lcdc_write(reg, LCD_RASTER_CTRL_REG); + + switch (bpp) { + case 1: + case 2: + case 4: + case 16: + par->palette_sz = 16 * 2; + break; + + case 8: + par->palette_sz = 256 * 2; + break; + + default: + return -EINVAL; + } + + bpl = width * bpp / 8; + par->databuf_sz = height * bpl + par->palette_sz; + + return 0; +} + +static int fb_setcolreg(unsigned regno, unsigned red, unsigned green, + unsigned blue, unsigned transp, + struct fb_info *info) +{ + struct da8xx_fb_par *par = info->par; + unsigned short *palette = (unsigned short *)par->v_palette_base; + u_short pal; + + if (regno > 255) + return 1; + + if (info->fix.visual == FB_VISUAL_DIRECTCOLOR) + return 1; + + if (info->var.bits_per_pixel == 8) { + red >>= 4; + green >>= 8; + blue >>= 12; + + pal = (red & 0x0f00); + pal |= (green & 0x00f0); + pal |= (blue & 0x000f); + + palette[regno] = pal; + + } else if ((info->var.bits_per_pixel == 16) && regno < 16) { + red >>= (16 - info->var.red.length); + red <<= info->var.red.offset; + + green >>= (16 - info->var.green.length); + green <<= info->var.green.offset; + + blue >>= (16 - info->var.blue.length); + blue <<= info->var.blue.offset; + + par->pseudo_palette[regno] = red | green | blue; + + palette[0] = 0x4000; + } + + return 0; +} + +static void lcd_reset(struct da8xx_fb_par *par) +{ + /* Disable the Raster if previously Enabled */ + if (lcdc_read(LCD_RASTER_CTRL_REG) & LCD_RASTER_ENABLE) + lcd_disable_raster(par); + + /* DMA has to be disabled */ + lcdc_write(0, LCD_DMA_CTRL_REG); + lcdc_write(0, LCD_RASTER_CTRL_REG); +} + +static int lcd_init(struct da8xx_fb_par *par, const struct lcd_ctrl_config *cfg, + struct da8xx_panel *panel) +{ + u32 bpp; + int ret = 0; + + lcd_reset(par); + + /* Configure the LCD clock divisor. */ + lcdc_write(LCD_CLK_DIVISOR(panel->pxl_clk) | + (LCD_RASTER_MODE & 0x1), LCD_CTRL_REG); + + if (panel->invert_pxl_clk) + lcdc_write((lcdc_read(LCD_RASTER_TIMING_2_REG) | + LCD_INVERT_PIXEL_CLOCK), LCD_RASTER_TIMING_2_REG); + else + lcdc_write((lcdc_read(LCD_RASTER_TIMING_2_REG) & + ~LCD_INVERT_PIXEL_CLOCK), LCD_RASTER_TIMING_2_REG); + + /* Configure the DMA burst size. */ + ret = lcd_cfg_dma(cfg->dma_burst_sz); + if (ret < 0) + return ret; + + /* Configure the AC bias properties. */ + lcd_cfg_ac_bias(cfg->ac_bias, cfg->ac_bias_intrpt); + + /* Configure the vertical and horizontal sync properties. */ + lcd_cfg_vertical_sync(panel->vbp, panel->vsw, panel->vfp); + lcd_cfg_horizontal_sync(panel->hbp, panel->hsw, panel->hfp); + + /* Configure for disply */ + ret = lcd_cfg_display(cfg); + if (ret < 0) + return ret; + + if (QVGA != cfg->p_disp_panel->panel_type) + return -EINVAL; + + if (cfg->bpp <= cfg->p_disp_panel->max_bpp && + cfg->bpp >= cfg->p_disp_panel->min_bpp) + bpp = cfg->bpp; + else + bpp = cfg->p_disp_panel->max_bpp; + if (bpp == 12) + bpp = 16; + ret = lcd_cfg_frame_buffer(par, (unsigned int)panel->width, + (unsigned int)panel->height, bpp, + cfg->raster_order); + if (ret < 0) + return ret; + + /* Configure FDD */ + lcdc_write((lcdc_read(LCD_RASTER_CTRL_REG) & 0xfff00fff) | + (cfg->fdd << 12), LCD_RASTER_CTRL_REG); + + return 0; +} + +static irqreturn_t lcdc_irq_handler(int irq, void *arg) +{ + u32 stat = lcdc_read(LCD_STAT_REG); + u32 reg; + + if ((stat & LCD_SYNC_LOST) && (stat & LCD_FIFO_UNDERFLOW)) { + reg = lcdc_read(LCD_RASTER_CTRL_REG); + lcdc_write(reg & ~LCD_RASTER_ENABLE, LCD_RASTER_CTRL_REG); + lcdc_write(stat, LCD_STAT_REG); + lcdc_write(reg | LCD_RASTER_ENABLE, LCD_RASTER_CTRL_REG); + } else + lcdc_write(stat, LCD_STAT_REG); + + return IRQ_HANDLED; +} + +static int fb_check_var(struct fb_var_screeninfo *var, + struct fb_info *info) +{ + int err = 0; + + switch (var->bits_per_pixel) { + case 1: + case 8: + var->red.offset = 0; + var->red.length = 8; + var->green.offset = 0; + var->green.length = 8; + var->blue.offset = 0; + var->blue.length = 8; + var->transp.offset = 0; + var->transp.length = 0; + break; + case 4: + var->red.offset = 0; + var->red.length = 4; + var->green.offset = 0; + var->green.length = 4; + var->blue.offset = 0; + var->blue.length = 4; + var->transp.offset = 0; + var->transp.length = 0; + break; + case 16: /* RGB 565 */ + var->red.offset = 0; + var->red.length = 5; + var->green.offset = 5; + var->green.length = 6; + var->blue.offset = 11; + var->blue.length = 5; + var->transp.offset = 0; + var->transp.length = 0; + break; + default: + err = -EINVAL; + } + + var->red.msb_right = 0; + var->green.msb_right = 0; + var->blue.msb_right = 0; + var->transp.msb_right = 0; + return err; +} + +static int __devexit fb_remove(struct platform_device *dev) +{ + struct fb_info *info = dev_get_drvdata(&dev->dev); + + if (info) { + struct da8xx_fb_par *par = info->par; + + if (lcdc_read(LCD_RASTER_CTRL_REG) & LCD_RASTER_ENABLE) + lcd_disable_raster(par); + lcdc_write(0, LCD_RASTER_CTRL_REG); + + /* disable DMA */ + lcdc_write(0, LCD_DMA_CTRL_REG); + + unregister_framebuffer(info); + fb_dealloc_cmap(&info->cmap); + dma_free_coherent(NULL, par->databuf_sz + PAGE_SIZE, + info->screen_base, + info->fix.smem_start); + free_irq(par->irq, par); + clk_disable(par->lcdc_clk); + clk_put(par->lcdc_clk); + framebuffer_release(info); + iounmap((void __iomem *)da8xx_fb_reg_base); + release_mem_region(lcdc_regs->start, resource_size(lcdc_regs)); + + } + return 0; +} + +static int fb_ioctl(struct fb_info *info, unsigned int cmd, + unsigned long arg) +{ + struct lcd_sync_arg sync_arg; + + switch (cmd) { + case FBIOGET_CONTRAST: + case FBIOPUT_CONTRAST: + case FBIGET_BRIGHTNESS: + case FBIPUT_BRIGHTNESS: + case FBIGET_COLOR: + case FBIPUT_COLOR: + return -ENOTTY; + case FBIPUT_HSYNC: + if (copy_from_user(&sync_arg, (char *)arg, + sizeof(struct lcd_sync_arg))) + return -EFAULT; + lcd_cfg_horizontal_sync(sync_arg.back_porch, + sync_arg.pulse_width, + sync_arg.front_porch); + break; + case FBIPUT_VSYNC: + if (copy_from_user(&sync_arg, (char *)arg, + sizeof(struct lcd_sync_arg))) + return -EFAULT; + lcd_cfg_vertical_sync(sync_arg.back_porch, + sync_arg.pulse_width, + sync_arg.front_porch); + break; + default: + return -EINVAL; + } + return 0; +} + +static struct fb_ops da8xx_fb_ops = { + .owner = THIS_MODULE, + .fb_check_var = fb_check_var, + .fb_setcolreg = fb_setcolreg, + .fb_ioctl = fb_ioctl, + .fb_fillrect = cfb_fillrect, + .fb_copyarea = cfb_copyarea, + .fb_imageblit = cfb_imageblit, +}; + +static int __init fb_probe(struct platform_device *device) +{ + struct da8xx_lcdc_platform_data *fb_pdata = + device->dev.platform_data; + struct lcd_ctrl_config *lcd_cfg; + struct da8xx_panel *lcdc_info; + struct fb_info *da8xx_fb_info; + struct clk *fb_clk = NULL; + struct da8xx_fb_par *par; + resource_size_t len; + int ret, i; + + if (fb_pdata == NULL) { + dev_err(&device->dev, "Can not get platform data\n"); + return -ENOENT; + } + + lcdc_regs = platform_get_resource(device, IORESOURCE_MEM, 0); + if (!lcdc_regs) { + dev_err(&device->dev, + "Can not get memory resource for LCD controller\n"); + return -ENOENT; + } + + len = resource_size(lcdc_regs); + + lcdc_regs = request_mem_region(lcdc_regs->start, len, lcdc_regs->name); + if (!lcdc_regs) + return -EBUSY; + + da8xx_fb_reg_base = (resource_size_t)ioremap(lcdc_regs->start, len); + if (!da8xx_fb_reg_base) { + ret = -EBUSY; + goto err_request_mem; + } + + fb_clk = clk_get(&device->dev, NULL); + if (IS_ERR(fb_clk)) { + dev_err(&device->dev, "Can not get device clock\n"); + ret = -ENODEV; + goto err_ioremap; + } + ret = clk_enable(fb_clk); + if (ret) + goto err_clk_put; + + for (i = 0, lcdc_info = known_lcd_panels; + i < ARRAY_SIZE(known_lcd_panels); + i++, lcdc_info++) { + if (strcmp(fb_pdata->type, lcdc_info->name) == 0) + break; + } + + if (i == ARRAY_SIZE(known_lcd_panels)) { + dev_err(&device->dev, "GLCD: No valid panel found\n"); + ret = -ENODEV; + goto err_clk_disable; + } else + dev_info(&device->dev, "GLCD: Found %s panel\n", + fb_pdata->type); + + lcd_cfg = (struct lcd_ctrl_config *)fb_pdata->controller_data; + + da8xx_fb_info = framebuffer_alloc(sizeof(struct da8xx_fb_par), + &device->dev); + if (!da8xx_fb_info) { + dev_dbg(&device->dev, "Memory allocation failed for fb_info\n"); + ret = -ENOMEM; + goto err_clk_disable; + } + + par = da8xx_fb_info->par; + + if (lcd_init(par, lcd_cfg, lcdc_info) < 0) { + dev_err(&device->dev, "lcd_init failed\n"); + ret = -EFAULT; + goto err_release_fb; + } + + /* allocate frame buffer */ + da8xx_fb_info->screen_base = dma_alloc_coherent(NULL, + par->databuf_sz + PAGE_SIZE, + (resource_size_t *) + &da8xx_fb_info->fix.smem_start, + GFP_KERNEL | GFP_DMA); + + if (!da8xx_fb_info->screen_base) { + dev_err(&device->dev, + "GLCD: kmalloc for frame buffer failed\n"); + ret = -EINVAL; + goto err_release_fb; + } + + /* move palette base pointer by (PAGE_SIZE - palette_sz) bytes */ + par->v_palette_base = da8xx_fb_info->screen_base + + (PAGE_SIZE - par->palette_sz); + par->p_palette_base = da8xx_fb_info->fix.smem_start + + (PAGE_SIZE - par->palette_sz); + + /* the rest of the frame buffer is pixel data */ + da8xx_fb_fix.smem_start = par->p_palette_base + par->palette_sz; + da8xx_fb_fix.smem_len = par->databuf_sz - par->palette_sz; + da8xx_fb_fix.line_length = (lcdc_info->width * lcd_cfg->bpp) / 8; + + par->lcdc_clk = fb_clk; + + par->irq = platform_get_irq(device, 0); + if (par->irq < 0) { + ret = -ENOENT; + goto err_release_fb_mem; + } + + ret = request_irq(par->irq, lcdc_irq_handler, 0, DRIVER_NAME, par); + if (ret) + goto err_release_fb_mem; + + /* Initialize par */ + da8xx_fb_info->var.bits_per_pixel = lcd_cfg->bpp; + + da8xx_fb_var.xres = lcdc_info->width; + da8xx_fb_var.xres_virtual = lcdc_info->width; + + da8xx_fb_var.yres = lcdc_info->height; + da8xx_fb_var.yres_virtual = lcdc_info->height; + + da8xx_fb_var.grayscale = + lcd_cfg->p_disp_panel->panel_shade == MONOCHROME ? 1 : 0; + da8xx_fb_var.bits_per_pixel = lcd_cfg->bpp; + + da8xx_fb_var.hsync_len = lcdc_info->hsw; + da8xx_fb_var.vsync_len = lcdc_info->vsw; + + /* Initialize fbinfo */ + da8xx_fb_info->flags = FBINFO_FLAG_DEFAULT; + da8xx_fb_info->fix = da8xx_fb_fix; + da8xx_fb_info->var = da8xx_fb_var; + da8xx_fb_info->fbops = &da8xx_fb_ops; + da8xx_fb_info->pseudo_palette = par->pseudo_palette; + + ret = fb_alloc_cmap(&da8xx_fb_info->cmap, PALETTE_SIZE, 0); + if (ret) + goto err_free_irq; + + /* First palette_sz byte of the frame buffer is the palette */ + da8xx_fb_info->cmap.len = par->palette_sz; + + /* Flush the buffer to the screen. */ + lcd_blit(LOAD_DATA, par); + + /* initialize var_screeninfo */ + da8xx_fb_var.activate = FB_ACTIVATE_FORCE; + fb_set_var(da8xx_fb_info, &da8xx_fb_var); + + dev_set_drvdata(&device->dev, da8xx_fb_info); + /* Register the Frame Buffer */ + if (register_framebuffer(da8xx_fb_info) < 0) { + dev_err(&device->dev, + "GLCD: Frame Buffer Registration Failed!\n"); + ret = -EINVAL; + goto err_dealloc_cmap; + } + + /* enable raster engine */ + lcdc_write(lcdc_read(LCD_RASTER_CTRL_REG) | + LCD_RASTER_ENABLE, LCD_RASTER_CTRL_REG); + + return 0; + +err_dealloc_cmap: + fb_dealloc_cmap(&da8xx_fb_info->cmap); + +err_free_irq: + free_irq(par->irq, par); + +err_release_fb_mem: + dma_free_coherent(NULL, par->databuf_sz + PAGE_SIZE, + da8xx_fb_info->screen_base, + da8xx_fb_info->fix.smem_start); + +err_release_fb: + framebuffer_release(da8xx_fb_info); + +err_clk_disable: + clk_disable(fb_clk); + +err_clk_put: + clk_put(fb_clk); + +err_ioremap: + iounmap((void __iomem *)da8xx_fb_reg_base); + +err_request_mem: + release_mem_region(lcdc_regs->start, len); + + return ret; +} + +#ifdef CONFIG_PM +static int fb_suspend(struct platform_device *dev, pm_message_t state) +{ + return -EBUSY; +} +static int fb_resume(struct platform_device *dev) +{ + return -EBUSY; +} +#else +#define fb_suspend NULL +#define fb_resume NULL +#endif + +static struct platform_driver da8xx_fb_driver = { + .probe = fb_probe, + .remove = fb_remove, + .suspend = fb_suspend, + .resume = fb_resume, + .driver = { + .name = DRIVER_NAME, + .owner = THIS_MODULE, + }, +}; + +static int __init da8xx_fb_init(void) +{ + return platform_driver_register(&da8xx_fb_driver); +} + +static void __exit da8xx_fb_cleanup(void) +{ + platform_driver_unregister(&da8xx_fb_driver); +} + +module_init(da8xx_fb_init); +module_exit(da8xx_fb_cleanup); + +MODULE_DESCRIPTION("Framebuffer driver for TI da8xx/omap-l1xx"); +MODULE_AUTHOR("Texas Instruments"); +MODULE_LICENSE("GPL"); diff --git a/drivers/video/ep93xx-fb.c b/drivers/video/ep93xx-fb.c new file mode 100644 index 000000000000..bd9d46f95291 --- /dev/null +++ b/drivers/video/ep93xx-fb.c @@ -0,0 +1,646 @@ +/* + * linux/drivers/video/ep93xx-fb.c + * + * Framebuffer support for the EP93xx series. + * + * Copyright (C) 2007 Bluewater Systems Ltd + * Author: Ryan Mallon <ryan@bluewatersys.com> + * + * Copyright (c) 2009 H Hartley Sweeten <hsweeten@visionengravers.com> + * + * Based on the Cirrus Logic ep93xxfb driver, and various other ep93xxfb + * drivers. + * + * 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/platform_device.h> +#include <linux/dma-mapping.h> +#include <linux/clk.h> +#include <linux/fb.h> + +#include <mach/fb.h> + +/* Vertical Frame Timing Registers */ +#define EP93XXFB_VLINES_TOTAL 0x0000 /* SW locked */ +#define EP93XXFB_VSYNC 0x0004 /* SW locked */ +#define EP93XXFB_VACTIVE 0x0008 /* SW locked */ +#define EP93XXFB_VBLANK 0x0228 /* SW locked */ +#define EP93XXFB_VCLK 0x000c /* SW locked */ + +/* Horizontal Frame Timing Registers */ +#define EP93XXFB_HCLKS_TOTAL 0x0010 /* SW locked */ +#define EP93XXFB_HSYNC 0x0014 /* SW locked */ +#define EP93XXFB_HACTIVE 0x0018 /* SW locked */ +#define EP93XXFB_HBLANK 0x022c /* SW locked */ +#define EP93XXFB_HCLK 0x001c /* SW locked */ + +/* Frame Buffer Memory Configuration Registers */ +#define EP93XXFB_SCREEN_PAGE 0x0028 +#define EP93XXFB_SCREEN_HPAGE 0x002c +#define EP93XXFB_SCREEN_LINES 0x0030 +#define EP93XXFB_LINE_LENGTH 0x0034 +#define EP93XXFB_VLINE_STEP 0x0038 +#define EP93XXFB_LINE_CARRY 0x003c /* SW locked */ +#define EP93XXFB_EOL_OFFSET 0x0230 + +/* Other Video Registers */ +#define EP93XXFB_BRIGHTNESS 0x0020 +#define EP93XXFB_ATTRIBS 0x0024 /* SW locked */ +#define EP93XXFB_SWLOCK 0x007c /* SW locked */ +#define EP93XXFB_AC_RATE 0x0214 +#define EP93XXFB_FIFO_LEVEL 0x0234 +#define EP93XXFB_PIXELMODE 0x0054 +#define EP93XXFB_PIXELMODE_32BPP (0x7 << 0) +#define EP93XXFB_PIXELMODE_24BPP (0x6 << 0) +#define EP93XXFB_PIXELMODE_16BPP (0x4 << 0) +#define EP93XXFB_PIXELMODE_8BPP (0x2 << 0) +#define EP93XXFB_PIXELMODE_SHIFT_1P_24B (0x0 << 3) +#define EP93XXFB_PIXELMODE_SHIFT_1P_18B (0x1 << 3) +#define EP93XXFB_PIXELMODE_COLOR_LUT (0x0 << 10) +#define EP93XXFB_PIXELMODE_COLOR_888 (0x4 << 10) +#define EP93XXFB_PIXELMODE_COLOR_555 (0x5 << 10) +#define EP93XXFB_PARL_IF_OUT 0x0058 +#define EP93XXFB_PARL_IF_IN 0x005c + +/* Blink Control Registers */ +#define EP93XXFB_BLINK_RATE 0x0040 +#define EP93XXFB_BLINK_MASK 0x0044 +#define EP93XXFB_BLINK_PATTRN 0x0048 +#define EP93XXFB_PATTRN_MASK 0x004c +#define EP93XXFB_BKGRND_OFFSET 0x0050 + +/* Hardware Cursor Registers */ +#define EP93XXFB_CURSOR_ADR_START 0x0060 +#define EP93XXFB_CURSOR_ADR_RESET 0x0064 +#define EP93XXFB_CURSOR_SIZE 0x0068 +#define EP93XXFB_CURSOR_COLOR1 0x006c +#define EP93XXFB_CURSOR_COLOR2 0x0070 +#define EP93XXFB_CURSOR_BLINK_COLOR1 0x021c +#define EP93XXFB_CURSOR_BLINK_COLOR2 0x0220 +#define EP93XXFB_CURSOR_XY_LOC 0x0074 +#define EP93XXFB_CURSOR_DSCAN_HY_LOC 0x0078 +#define EP93XXFB_CURSOR_BLINK_RATE_CTRL 0x0224 + +/* LUT Registers */ +#define EP93XXFB_GRY_SCL_LUTR 0x0080 +#define EP93XXFB_GRY_SCL_LUTG 0x0280 +#define EP93XXFB_GRY_SCL_LUTB 0x0300 +#define EP93XXFB_LUT_SW_CONTROL 0x0218 +#define EP93XXFB_LUT_SW_CONTROL_SWTCH (1 << 0) +#define EP93XXFB_LUT_SW_CONTROL_SSTAT (1 << 1) +#define EP93XXFB_COLOR_LUT 0x0400 + +/* Video Signature Registers */ +#define EP93XXFB_VID_SIG_RSLT_VAL 0x0200 +#define EP93XXFB_VID_SIG_CTRL 0x0204 +#define EP93XXFB_VSIG 0x0208 +#define EP93XXFB_HSIG 0x020c +#define EP93XXFB_SIG_CLR_STR 0x0210 + +/* Minimum / Maximum resolutions supported */ +#define EP93XXFB_MIN_XRES 64 +#define EP93XXFB_MIN_YRES 64 +#define EP93XXFB_MAX_XRES 1024 +#define EP93XXFB_MAX_YRES 768 + +struct ep93xx_fbi { + struct ep93xxfb_mach_info *mach_info; + struct clk *clk; + struct resource *res; + void __iomem *mmio_base; + unsigned int pseudo_palette[256]; +}; + +static int check_screenpage_bug = 1; +module_param(check_screenpage_bug, int, 0644); +MODULE_PARM_DESC(check_screenpage_bug, + "Check for bit 27 screen page bug. Default = 1"); + +static inline unsigned int ep93xxfb_readl(struct ep93xx_fbi *fbi, + unsigned int off) +{ + return __raw_readl(fbi->mmio_base + off); +} + +static inline void ep93xxfb_writel(struct ep93xx_fbi *fbi, + unsigned int val, unsigned int off) +{ + __raw_writel(val, fbi->mmio_base + off); +} + +/* + * Write to one of the locked raster registers. + */ +static inline void ep93xxfb_out_locked(struct ep93xx_fbi *fbi, + unsigned int val, unsigned int reg) +{ + /* + * We don't need a lock or delay here since the raster register + * block will remain unlocked until the next access. + */ + ep93xxfb_writel(fbi, 0xaa, EP93XXFB_SWLOCK); + ep93xxfb_writel(fbi, val, reg); +} + +static void ep93xxfb_set_video_attribs(struct fb_info *info) +{ + struct ep93xx_fbi *fbi = info->par; + unsigned int attribs; + + attribs = EP93XXFB_ENABLE; + attribs |= fbi->mach_info->flags; + ep93xxfb_out_locked(fbi, attribs, EP93XXFB_ATTRIBS); +} + +static int ep93xxfb_set_pixelmode(struct fb_info *info) +{ + struct ep93xx_fbi *fbi = info->par; + unsigned int val; + + info->var.transp.offset = 0; + info->var.transp.length = 0; + + switch (info->var.bits_per_pixel) { + case 8: + val = EP93XXFB_PIXELMODE_8BPP | EP93XXFB_PIXELMODE_COLOR_LUT | + EP93XXFB_PIXELMODE_SHIFT_1P_18B; + + info->var.red.offset = 0; + info->var.red.length = 8; + info->var.green.offset = 0; + info->var.green.length = 8; + info->var.blue.offset = 0; + info->var.blue.length = 8; + info->fix.visual = FB_VISUAL_PSEUDOCOLOR; + break; + + case 16: + val = EP93XXFB_PIXELMODE_16BPP | EP93XXFB_PIXELMODE_COLOR_555 | + EP93XXFB_PIXELMODE_SHIFT_1P_18B; + + info->var.red.offset = 11; + info->var.red.length = 5; + info->var.green.offset = 5; + info->var.green.length = 6; + info->var.blue.offset = 0; + info->var.blue.length = 5; + info->fix.visual = FB_VISUAL_TRUECOLOR; + break; + + case 24: + val = EP93XXFB_PIXELMODE_24BPP | EP93XXFB_PIXELMODE_COLOR_888 | + EP93XXFB_PIXELMODE_SHIFT_1P_24B; + + info->var.red.offset = 16; + info->var.red.length = 8; + info->var.green.offset = 8; + info->var.green.length = 8; + info->var.blue.offset = 0; + info->var.blue.length = 8; + info->fix.visual = FB_VISUAL_TRUECOLOR; + break; + + case 32: + val = EP93XXFB_PIXELMODE_32BPP | EP93XXFB_PIXELMODE_COLOR_888 | + EP93XXFB_PIXELMODE_SHIFT_1P_24B; + + info->var.red.offset = 16; + info->var.red.length = 8; + info->var.green.offset = 8; + info->var.green.length = 8; + info->var.blue.offset = 0; + info->var.blue.length = 8; + info->fix.visual = FB_VISUAL_TRUECOLOR; + break; + + default: + return -EINVAL; + } + + ep93xxfb_writel(fbi, val, EP93XXFB_PIXELMODE); + return 0; +} + +static void ep93xxfb_set_timing(struct fb_info *info) +{ + struct ep93xx_fbi *fbi = info->par; + unsigned int vlines_total, hclks_total, start, stop; + + vlines_total = info->var.yres + info->var.upper_margin + + info->var.lower_margin + info->var.vsync_len - 1; + + hclks_total = info->var.xres + info->var.left_margin + + info->var.right_margin + info->var.hsync_len - 1; + + ep93xxfb_out_locked(fbi, vlines_total, EP93XXFB_VLINES_TOTAL); + ep93xxfb_out_locked(fbi, hclks_total, EP93XXFB_HCLKS_TOTAL); + + start = vlines_total; + stop = vlines_total - info->var.vsync_len; + ep93xxfb_out_locked(fbi, start | (stop << 16), EP93XXFB_VSYNC); + + start = vlines_total - info->var.vsync_len - info->var.upper_margin; + stop = info->var.lower_margin - 1; + ep93xxfb_out_locked(fbi, start | (stop << 16), EP93XXFB_VBLANK); + ep93xxfb_out_locked(fbi, start | (stop << 16), EP93XXFB_VACTIVE); + + start = vlines_total; + stop = vlines_total + 1; + ep93xxfb_out_locked(fbi, start | (stop << 16), EP93XXFB_VCLK); + + start = hclks_total; + stop = hclks_total - info->var.hsync_len; + ep93xxfb_out_locked(fbi, start | (stop << 16), EP93XXFB_HSYNC); + + start = hclks_total - info->var.hsync_len - info->var.left_margin; + stop = info->var.right_margin - 1; + ep93xxfb_out_locked(fbi, start | (stop << 16), EP93XXFB_HBLANK); + ep93xxfb_out_locked(fbi, start | (stop << 16), EP93XXFB_HACTIVE); + + start = hclks_total; + stop = hclks_total; + ep93xxfb_out_locked(fbi, start | (stop << 16), EP93XXFB_HCLK); + + ep93xxfb_out_locked(fbi, 0x0, EP93XXFB_LINE_CARRY); +} + +static int ep93xxfb_set_par(struct fb_info *info) +{ + struct ep93xx_fbi *fbi = info->par; + + clk_set_rate(fbi->clk, 1000 * PICOS2KHZ(info->var.pixclock)); + + ep93xxfb_set_timing(info); + + info->fix.line_length = info->var.xres_virtual * + info->var.bits_per_pixel / 8; + + ep93xxfb_writel(fbi, info->fix.smem_start, EP93XXFB_SCREEN_PAGE); + ep93xxfb_writel(fbi, info->var.yres - 1, EP93XXFB_SCREEN_LINES); + ep93xxfb_writel(fbi, ((info->var.xres * info->var.bits_per_pixel) + / 32) - 1, EP93XXFB_LINE_LENGTH); + ep93xxfb_writel(fbi, info->fix.line_length / 4, EP93XXFB_VLINE_STEP); + ep93xxfb_set_video_attribs(info); + return 0; +} + +static int ep93xxfb_check_var(struct fb_var_screeninfo *var, + struct fb_info *info) +{ + int err; + + err = ep93xxfb_set_pixelmode(info); + if (err) + return err; + + var->xres = max_t(unsigned int, var->xres, EP93XXFB_MIN_XRES); + var->xres = min_t(unsigned int, var->xres, EP93XXFB_MAX_XRES); + var->xres_virtual = max(var->xres_virtual, var->xres); + + var->yres = max_t(unsigned int, var->yres, EP93XXFB_MIN_YRES); + var->yres = min_t(unsigned int, var->yres, EP93XXFB_MAX_YRES); + var->yres_virtual = max(var->yres_virtual, var->yres); + + return 0; +} + +static int ep93xxfb_mmap(struct fb_info *info, struct vm_area_struct *vma) +{ + unsigned int offset = vma->vm_pgoff << PAGE_SHIFT; + + if (offset < info->fix.smem_len) { + return dma_mmap_writecombine(info->dev, vma, info->screen_base, + info->fix.smem_start, + info->fix.smem_len); + } + + return -EINVAL; +} + +static int ep93xxfb_blank(int blank_mode, struct fb_info *info) +{ + struct ep93xx_fbi *fbi = info->par; + unsigned int attribs = ep93xxfb_readl(fbi, EP93XXFB_ATTRIBS); + + if (blank_mode) { + if (fbi->mach_info->blank) + fbi->mach_info->blank(blank_mode, info); + ep93xxfb_out_locked(fbi, attribs & ~EP93XXFB_ENABLE, + EP93XXFB_ATTRIBS); + clk_disable(fbi->clk); + } else { + clk_enable(fbi->clk); + ep93xxfb_out_locked(fbi, attribs | EP93XXFB_ENABLE, + EP93XXFB_ATTRIBS); + if (fbi->mach_info->blank) + fbi->mach_info->blank(blank_mode, info); + } + + return 0; +} + +static inline int ep93xxfb_convert_color(int val, int width) +{ + return ((val << width) + 0x7fff - val) >> 16; +} + +static int ep93xxfb_setcolreg(unsigned int regno, unsigned int red, + unsigned int green, unsigned int blue, + unsigned int transp, struct fb_info *info) +{ + struct ep93xx_fbi *fbi = info->par; + unsigned int *pal = info->pseudo_palette; + unsigned int ctrl, i, rgb, lut_current, lut_stat; + + switch (info->fix.visual) { + case FB_VISUAL_PSEUDOCOLOR: + rgb = ((red & 0xff00) << 8) | (green & 0xff00) | + ((blue & 0xff00) >> 8); + + pal[regno] = rgb; + ep93xxfb_writel(fbi, rgb, (EP93XXFB_COLOR_LUT + (regno << 2))); + ctrl = ep93xxfb_readl(fbi, EP93XXFB_LUT_SW_CONTROL); + lut_stat = !!(ctrl & EP93XXFB_LUT_SW_CONTROL_SSTAT); + lut_current = !!(ctrl & EP93XXFB_LUT_SW_CONTROL_SWTCH); + + if (lut_stat == lut_current) { + for (i = 0; i < 256; i++) { + ep93xxfb_writel(fbi, pal[i], + EP93XXFB_COLOR_LUT + (i << 2)); + } + + ep93xxfb_writel(fbi, + ctrl ^ EP93XXFB_LUT_SW_CONTROL_SWTCH, + EP93XXFB_LUT_SW_CONTROL); + } + break; + + case FB_VISUAL_TRUECOLOR: + if (regno > 16) + return 1; + + red = ep93xxfb_convert_color(red, info->var.red.length); + green = ep93xxfb_convert_color(green, info->var.green.length); + blue = ep93xxfb_convert_color(blue, info->var.blue.length); + transp = ep93xxfb_convert_color(transp, + info->var.transp.length); + + pal[regno] = (red << info->var.red.offset) | + (green << info->var.green.offset) | + (blue << info->var.blue.offset) | + (transp << info->var.transp.offset); + break; + + default: + return 1; + } + + return 0; +} + +static struct fb_ops ep93xxfb_ops = { + .owner = THIS_MODULE, + .fb_check_var = ep93xxfb_check_var, + .fb_set_par = ep93xxfb_set_par, + .fb_blank = ep93xxfb_blank, + .fb_fillrect = cfb_fillrect, + .fb_copyarea = cfb_copyarea, + .fb_imageblit = cfb_imageblit, + .fb_setcolreg = ep93xxfb_setcolreg, + .fb_mmap = ep93xxfb_mmap, +}; + +static int __init ep93xxfb_calc_fbsize(struct ep93xxfb_mach_info *mach_info) +{ + int i, fb_size = 0; + + if (mach_info->num_modes == EP93XXFB_USE_MODEDB) { + fb_size = EP93XXFB_MAX_XRES * EP93XXFB_MAX_YRES * + mach_info->bpp / 8; + } else { + for (i = 0; i < mach_info->num_modes; i++) { + const struct fb_videomode *mode; + int size; + + mode = &mach_info->modes[i]; + size = mode->xres * mode->yres * mach_info->bpp / 8; + if (size > fb_size) + fb_size = size; + } + } + + return fb_size; +} + +static int __init ep93xxfb_alloc_videomem(struct fb_info *info) +{ + struct ep93xx_fbi *fbi = info->par; + char __iomem *virt_addr; + dma_addr_t phys_addr; + unsigned int fb_size; + + fb_size = ep93xxfb_calc_fbsize(fbi->mach_info); + virt_addr = dma_alloc_writecombine(info->dev, fb_size, + &phys_addr, GFP_KERNEL); + if (!virt_addr) + return -ENOMEM; + + /* + * There is a bug in the ep93xx framebuffer which causes problems + * if bit 27 of the physical address is set. + * See: http://marc.info/?l=linux-arm-kernel&m=110061245502000&w=2 + * There does not seem to be any offical errata for this, but I + * have confirmed the problem exists on my hardware (ep9315) at + * least. + */ + if (check_screenpage_bug && phys_addr & (1 << 27)) { + dev_err(info->dev, "ep93xx framebuffer bug. phys addr (0x%x) " + "has bit 27 set: cannot init framebuffer\n", + phys_addr); + + dma_free_coherent(info->dev, fb_size, virt_addr, phys_addr); + return -ENOMEM; + } + + info->fix.smem_start = phys_addr; + info->fix.smem_len = fb_size; + info->screen_base = virt_addr; + + return 0; +} + +static void ep93xxfb_dealloc_videomem(struct fb_info *info) +{ + if (info->screen_base) + dma_free_coherent(info->dev, info->fix.smem_len, + info->screen_base, info->fix.smem_start); +} + +static int __init ep93xxfb_probe(struct platform_device *pdev) +{ + struct ep93xxfb_mach_info *mach_info = pdev->dev.platform_data; + struct fb_info *info; + struct ep93xx_fbi *fbi; + struct resource *res; + char *video_mode; + int err; + + if (!mach_info) + return -EINVAL; + + info = framebuffer_alloc(sizeof(struct ep93xx_fbi), &pdev->dev); + if (!info) + return -ENOMEM; + + info->dev = &pdev->dev; + platform_set_drvdata(pdev, info); + fbi = info->par; + fbi->mach_info = mach_info; + + err = fb_alloc_cmap(&info->cmap, 256, 0); + if (err) + goto failed; + + err = ep93xxfb_alloc_videomem(info); + if (err) + goto failed; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + err = -ENXIO; + goto failed; + } + + res = request_mem_region(res->start, resource_size(res), pdev->name); + if (!res) { + err = -EBUSY; + goto failed; + } + + fbi->res = res; + fbi->mmio_base = ioremap(res->start, resource_size(res)); + if (!fbi->mmio_base) { + err = -ENXIO; + goto failed; + } + + strcpy(info->fix.id, pdev->name); + info->fbops = &ep93xxfb_ops; + info->fix.type = FB_TYPE_PACKED_PIXELS; + info->fix.accel = FB_ACCEL_NONE; + info->var.activate = FB_ACTIVATE_NOW; + info->var.vmode = FB_VMODE_NONINTERLACED; + info->flags = FBINFO_DEFAULT; + info->node = -1; + info->state = FBINFO_STATE_RUNNING; + info->pseudo_palette = &fbi->pseudo_palette; + + fb_get_options("ep93xx-fb", &video_mode); + err = fb_find_mode(&info->var, info, video_mode, + fbi->mach_info->modes, fbi->mach_info->num_modes, + fbi->mach_info->default_mode, fbi->mach_info->bpp); + if (err == 0) { + dev_err(info->dev, "No suitable video mode found\n"); + err = -EINVAL; + goto failed; + } + + if (mach_info->setup) { + err = mach_info->setup(pdev); + if (err) + return err; + } + + err = ep93xxfb_check_var(&info->var, info); + if (err) + goto failed; + + fbi->clk = clk_get(info->dev, NULL); + if (IS_ERR(fbi->clk)) { + err = PTR_ERR(fbi->clk); + fbi->clk = NULL; + goto failed; + } + + ep93xxfb_set_par(info); + clk_enable(fbi->clk); + + err = register_framebuffer(info); + if (err) + goto failed; + + dev_info(info->dev, "registered. Mode = %dx%d-%d\n", + info->var.xres, info->var.yres, info->var.bits_per_pixel); + return 0; + +failed: + if (fbi->clk) + clk_put(fbi->clk); + if (fbi->mmio_base) + iounmap(fbi->mmio_base); + if (fbi->res) + release_mem_region(fbi->res->start, resource_size(fbi->res)); + ep93xxfb_dealloc_videomem(info); + if (&info->cmap) + fb_dealloc_cmap(&info->cmap); + if (fbi->mach_info->teardown) + fbi->mach_info->teardown(pdev); + kfree(info); + platform_set_drvdata(pdev, NULL); + + return err; +} + +static int ep93xxfb_remove(struct platform_device *pdev) +{ + struct fb_info *info = platform_get_drvdata(pdev); + struct ep93xx_fbi *fbi = info->par; + + unregister_framebuffer(info); + clk_disable(fbi->clk); + clk_put(fbi->clk); + iounmap(fbi->mmio_base); + release_mem_region(fbi->res->start, resource_size(fbi->res)); + ep93xxfb_dealloc_videomem(info); + fb_dealloc_cmap(&info->cmap); + + if (fbi->mach_info->teardown) + fbi->mach_info->teardown(pdev); + + kfree(info); + platform_set_drvdata(pdev, NULL); + + return 0; +} + +static struct platform_driver ep93xxfb_driver = { + .probe = ep93xxfb_probe, + .remove = ep93xxfb_remove, + .driver = { + .name = "ep93xx-fb", + .owner = THIS_MODULE, + }, +}; + +static int __devinit ep93xxfb_init(void) +{ + return platform_driver_register(&ep93xxfb_driver); +} + +static void __exit ep93xxfb_exit(void) +{ + platform_driver_unregister(&ep93xxfb_driver); +} + +module_init(ep93xxfb_init); +module_exit(ep93xxfb_exit); + +MODULE_DESCRIPTION("EP93XX Framebuffer Driver"); +MODULE_ALIAS("platform:ep93xx-fb"); +MODULE_AUTHOR("Ryan Mallon <ryan&bluewatersys.com>, " + "H Hartley Sweeten <hsweeten@visionengravers.com"); +MODULE_LICENSE("GPL"); diff --git a/drivers/video/fb_defio.c b/drivers/video/fb_defio.c index 0a7a6679ee6e..c27ab1ed9604 100644 --- a/drivers/video/fb_defio.c +++ b/drivers/video/fb_defio.c @@ -125,7 +125,7 @@ page_already_added: return 0; } -static struct vm_operations_struct fb_deferred_io_vm_ops = { +static const struct vm_operations_struct fb_deferred_io_vm_ops = { .fault = fb_deferred_io_fault, .page_mkwrite = fb_deferred_io_mkwrite, }; diff --git a/drivers/video/fbmem.c b/drivers/video/fbmem.c index a85c818be945..99bbd282ce63 100644 --- a/drivers/video/fbmem.c +++ b/drivers/video/fbmem.c @@ -871,8 +871,8 @@ fb_pan_display(struct fb_info *info, struct fb_var_screeninfo *var) err = -EINVAL; if (err || !info->fbops->fb_pan_display || - var->yoffset + yres > info->var.yres_virtual || - var->xoffset + info->var.xres > info->var.xres_virtual) + var->yoffset > info->var.yres_virtual - yres || + var->xoffset > info->var.xres_virtual - info->var.xres) return -EINVAL; if ((err = info->fbops->fb_pan_display(var, info))) @@ -954,6 +954,7 @@ fb_set_var(struct fb_info *info, struct fb_var_screeninfo *var) goto done; if ((var->activate & FB_ACTIVATE_MASK) == FB_ACTIVATE_NOW) { + struct fb_var_screeninfo old_var; struct fb_videomode mode; if (info->fbops->fb_get_caps) { @@ -963,10 +964,20 @@ fb_set_var(struct fb_info *info, struct fb_var_screeninfo *var) goto done; } + old_var = info->var; info->var = *var; - if (info->fbops->fb_set_par) - info->fbops->fb_set_par(info); + if (info->fbops->fb_set_par) { + ret = info->fbops->fb_set_par(info); + + if (ret) { + info->var = old_var; + printk(KERN_WARNING "detected " + "fb_set_par error, " + "error code: %d\n", ret); + goto done; + } + } fb_pan_display(info, &info->var); fb_set_cmap(&info->cmap, info); @@ -1789,7 +1800,7 @@ static int __init video_setup(char *options) global = 1; } - if (!global && !strstr(options, "fb:")) { + if (!global && !strchr(options, ':')) { fb_mode_option = options; global = 1; } diff --git a/drivers/video/matrox/g450_pll.c b/drivers/video/matrox/g450_pll.c index d42346e7fdda..09f6e045d5be 100644 --- a/drivers/video/matrox/g450_pll.c +++ b/drivers/video/matrox/g450_pll.c @@ -25,16 +25,19 @@ static inline unsigned int g450_f2vco(unsigned char p, unsigned int fin) { return (p & 0x40) ? fin : fin << ((p & 3) + 1); } -static unsigned int g450_mnp2vco(CPMINFO unsigned int mnp) { +static unsigned int g450_mnp2vco(const struct matrox_fb_info *minfo, + unsigned int mnp) +{ unsigned int m, n; m = ((mnp >> 16) & 0x0FF) + 1; n = ((mnp >> 7) & 0x1FE) + 4; - return (ACCESS_FBINFO(features).pll.ref_freq * n + (m >> 1)) / m; + return (minfo->features.pll.ref_freq * n + (m >> 1)) / m; } -unsigned int g450_mnp2f(CPMINFO unsigned int mnp) { - return g450_vco2f(mnp, g450_mnp2vco(PMINFO mnp)); +unsigned int g450_mnp2f(const struct matrox_fb_info *minfo, unsigned int mnp) +{ + return g450_vco2f(mnp, g450_mnp2vco(minfo, mnp)); } static inline unsigned int pll_freq_delta(unsigned int f1, unsigned int f2) { @@ -49,7 +52,10 @@ static inline unsigned int pll_freq_delta(unsigned int f1, unsigned int f2) { #define NO_MORE_MNP 0x01FFFFFF #define G450_MNP_FREQBITS (0xFFFFFF43) /* do not mask high byte so we'll catch NO_MORE_MNP */ -static unsigned int g450_nextpll(CPMINFO const struct matrox_pll_limits* pi, unsigned int* fvco, unsigned int mnp) { +static unsigned int g450_nextpll(const struct matrox_fb_info *minfo, + const struct matrox_pll_limits *pi, + unsigned int *fvco, unsigned int mnp) +{ unsigned int m, n, p; unsigned int tvco = *fvco; @@ -90,12 +96,15 @@ static unsigned int g450_nextpll(CPMINFO const struct matrox_pll_limits* pi, uns } else { m--; } - n = ((tvco * (m+1) + ACCESS_FBINFO(features).pll.ref_freq) / (ACCESS_FBINFO(features).pll.ref_freq * 2)) - 2; + n = ((tvco * (m+1) + minfo->features.pll.ref_freq) / (minfo->features.pll.ref_freq * 2)) - 2; } while (n < 0x03 || n > 0x7A); return (m << 16) | (n << 8) | p; } -static unsigned int g450_firstpll(CPMINFO const struct matrox_pll_limits* pi, unsigned int* vco, unsigned int fout) { +static unsigned int g450_firstpll(const struct matrox_fb_info *minfo, + const struct matrox_pll_limits *pi, + unsigned int *vco, unsigned int fout) +{ unsigned int p; unsigned int vcomax; @@ -121,88 +130,94 @@ static unsigned int g450_firstpll(CPMINFO const struct matrox_pll_limits* pi, un } *vco = tvco; } - return g450_nextpll(PMINFO pi, vco, 0xFF0000 | p); + return g450_nextpll(minfo, pi, vco, 0xFF0000 | p); } -static inline unsigned int g450_setpll(CPMINFO unsigned int mnp, unsigned int pll) { +static inline unsigned int g450_setpll(const struct matrox_fb_info *minfo, + unsigned int mnp, unsigned int pll) +{ switch (pll) { case M_PIXEL_PLL_A: - matroxfb_DAC_out(PMINFO M1064_XPIXPLLAM, mnp >> 16); - matroxfb_DAC_out(PMINFO M1064_XPIXPLLAN, mnp >> 8); - matroxfb_DAC_out(PMINFO M1064_XPIXPLLAP, mnp); + matroxfb_DAC_out(minfo, M1064_XPIXPLLAM, mnp >> 16); + matroxfb_DAC_out(minfo, M1064_XPIXPLLAN, mnp >> 8); + matroxfb_DAC_out(minfo, M1064_XPIXPLLAP, mnp); return M1064_XPIXPLLSTAT; case M_PIXEL_PLL_B: - matroxfb_DAC_out(PMINFO M1064_XPIXPLLBM, mnp >> 16); - matroxfb_DAC_out(PMINFO M1064_XPIXPLLBN, mnp >> 8); - matroxfb_DAC_out(PMINFO M1064_XPIXPLLBP, mnp); + matroxfb_DAC_out(minfo, M1064_XPIXPLLBM, mnp >> 16); + matroxfb_DAC_out(minfo, M1064_XPIXPLLBN, mnp >> 8); + matroxfb_DAC_out(minfo, M1064_XPIXPLLBP, mnp); return M1064_XPIXPLLSTAT; case M_PIXEL_PLL_C: - matroxfb_DAC_out(PMINFO M1064_XPIXPLLCM, mnp >> 16); - matroxfb_DAC_out(PMINFO M1064_XPIXPLLCN, mnp >> 8); - matroxfb_DAC_out(PMINFO M1064_XPIXPLLCP, mnp); + matroxfb_DAC_out(minfo, M1064_XPIXPLLCM, mnp >> 16); + matroxfb_DAC_out(minfo, M1064_XPIXPLLCN, mnp >> 8); + matroxfb_DAC_out(minfo, M1064_XPIXPLLCP, mnp); return M1064_XPIXPLLSTAT; case M_SYSTEM_PLL: - matroxfb_DAC_out(PMINFO DAC1064_XSYSPLLM, mnp >> 16); - matroxfb_DAC_out(PMINFO DAC1064_XSYSPLLN, mnp >> 8); - matroxfb_DAC_out(PMINFO DAC1064_XSYSPLLP, mnp); + matroxfb_DAC_out(minfo, DAC1064_XSYSPLLM, mnp >> 16); + matroxfb_DAC_out(minfo, DAC1064_XSYSPLLN, mnp >> 8); + matroxfb_DAC_out(minfo, DAC1064_XSYSPLLP, mnp); return DAC1064_XSYSPLLSTAT; case M_VIDEO_PLL: - matroxfb_DAC_out(PMINFO M1064_XVIDPLLM, mnp >> 16); - matroxfb_DAC_out(PMINFO M1064_XVIDPLLN, mnp >> 8); - matroxfb_DAC_out(PMINFO M1064_XVIDPLLP, mnp); + matroxfb_DAC_out(minfo, M1064_XVIDPLLM, mnp >> 16); + matroxfb_DAC_out(minfo, M1064_XVIDPLLN, mnp >> 8); + matroxfb_DAC_out(minfo, M1064_XVIDPLLP, mnp); return M1064_XVIDPLLSTAT; } return 0; } -static inline unsigned int g450_cmppll(CPMINFO unsigned int mnp, unsigned int pll) { +static inline unsigned int g450_cmppll(const struct matrox_fb_info *minfo, + unsigned int mnp, unsigned int pll) +{ unsigned char m = mnp >> 16; unsigned char n = mnp >> 8; unsigned char p = mnp; switch (pll) { case M_PIXEL_PLL_A: - return (matroxfb_DAC_in(PMINFO M1064_XPIXPLLAM) != m || - matroxfb_DAC_in(PMINFO M1064_XPIXPLLAN) != n || - matroxfb_DAC_in(PMINFO M1064_XPIXPLLAP) != p); + return (matroxfb_DAC_in(minfo, M1064_XPIXPLLAM) != m || + matroxfb_DAC_in(minfo, M1064_XPIXPLLAN) != n || + matroxfb_DAC_in(minfo, M1064_XPIXPLLAP) != p); case M_PIXEL_PLL_B: - return (matroxfb_DAC_in(PMINFO M1064_XPIXPLLBM) != m || - matroxfb_DAC_in(PMINFO M1064_XPIXPLLBN) != n || - matroxfb_DAC_in(PMINFO M1064_XPIXPLLBP) != p); + return (matroxfb_DAC_in(minfo, M1064_XPIXPLLBM) != m || + matroxfb_DAC_in(minfo, M1064_XPIXPLLBN) != n || + matroxfb_DAC_in(minfo, M1064_XPIXPLLBP) != p); case M_PIXEL_PLL_C: - return (matroxfb_DAC_in(PMINFO M1064_XPIXPLLCM) != m || - matroxfb_DAC_in(PMINFO M1064_XPIXPLLCN) != n || - matroxfb_DAC_in(PMINFO M1064_XPIXPLLCP) != p); + return (matroxfb_DAC_in(minfo, M1064_XPIXPLLCM) != m || + matroxfb_DAC_in(minfo, M1064_XPIXPLLCN) != n || + matroxfb_DAC_in(minfo, M1064_XPIXPLLCP) != p); case M_SYSTEM_PLL: - return (matroxfb_DAC_in(PMINFO DAC1064_XSYSPLLM) != m || - matroxfb_DAC_in(PMINFO DAC1064_XSYSPLLN) != n || - matroxfb_DAC_in(PMINFO DAC1064_XSYSPLLP) != p); + return (matroxfb_DAC_in(minfo, DAC1064_XSYSPLLM) != m || + matroxfb_DAC_in(minfo, DAC1064_XSYSPLLN) != n || + matroxfb_DAC_in(minfo, DAC1064_XSYSPLLP) != p); case M_VIDEO_PLL: - return (matroxfb_DAC_in(PMINFO M1064_XVIDPLLM) != m || - matroxfb_DAC_in(PMINFO M1064_XVIDPLLN) != n || - matroxfb_DAC_in(PMINFO M1064_XVIDPLLP) != p); + return (matroxfb_DAC_in(minfo, M1064_XVIDPLLM) != m || + matroxfb_DAC_in(minfo, M1064_XVIDPLLN) != n || + matroxfb_DAC_in(minfo, M1064_XVIDPLLP) != p); } return 1; } -static inline int g450_isplllocked(CPMINFO unsigned int regidx) { +static inline int g450_isplllocked(const struct matrox_fb_info *minfo, + unsigned int regidx) +{ unsigned int j; for (j = 0; j < 1000; j++) { - if (matroxfb_DAC_in(PMINFO regidx) & 0x40) { + if (matroxfb_DAC_in(minfo, regidx) & 0x40) { unsigned int r = 0; int i; for (i = 0; i < 100; i++) { - r += matroxfb_DAC_in(PMINFO regidx) & 0x40; + r += matroxfb_DAC_in(minfo, regidx) & 0x40; } return r >= (90 * 0x40); } @@ -211,8 +226,10 @@ static inline int g450_isplllocked(CPMINFO unsigned int regidx) { return 0; } -static int g450_testpll(CPMINFO unsigned int mnp, unsigned int pll) { - return g450_isplllocked(PMINFO g450_setpll(PMINFO mnp, pll)); +static int g450_testpll(const struct matrox_fb_info *minfo, unsigned int mnp, + unsigned int pll) +{ + return g450_isplllocked(minfo, g450_setpll(minfo, mnp, pll)); } static void updatehwstate_clk(struct matrox_hw_state* hw, unsigned int mnp, unsigned int pll) { @@ -225,13 +242,19 @@ static void updatehwstate_clk(struct matrox_hw_state* hw, unsigned int mnp, unsi } } -void matroxfb_g450_setpll_cond(WPMINFO unsigned int mnp, unsigned int pll) { - if (g450_cmppll(PMINFO mnp, pll)) { - g450_setpll(PMINFO mnp, pll); +void matroxfb_g450_setpll_cond(struct matrox_fb_info *minfo, unsigned int mnp, + unsigned int pll) +{ + if (g450_cmppll(minfo, mnp, pll)) { + g450_setpll(minfo, mnp, pll); } } -static inline unsigned int g450_findworkingpll(WPMINFO unsigned int pll, unsigned int* mnparray, unsigned int mnpcount) { +static inline unsigned int g450_findworkingpll(struct matrox_fb_info *minfo, + unsigned int pll, + unsigned int *mnparray, + unsigned int mnpcount) +{ unsigned int found = 0; unsigned int idx; unsigned int mnpfound = mnparray[0]; @@ -255,22 +278,22 @@ static inline unsigned int g450_findworkingpll(WPMINFO unsigned int pll, unsigne while (sptr >= sarray) { unsigned int mnp = *sptr--; - if (g450_testpll(PMINFO mnp - 0x0300, pll) && - g450_testpll(PMINFO mnp + 0x0300, pll) && - g450_testpll(PMINFO mnp - 0x0200, pll) && - g450_testpll(PMINFO mnp + 0x0200, pll) && - g450_testpll(PMINFO mnp - 0x0100, pll) && - g450_testpll(PMINFO mnp + 0x0100, pll)) { - if (g450_testpll(PMINFO mnp, pll)) { + if (g450_testpll(minfo, mnp - 0x0300, pll) && + g450_testpll(minfo, mnp + 0x0300, pll) && + g450_testpll(minfo, mnp - 0x0200, pll) && + g450_testpll(minfo, mnp + 0x0200, pll) && + g450_testpll(minfo, mnp - 0x0100, pll) && + g450_testpll(minfo, mnp + 0x0100, pll)) { + if (g450_testpll(minfo, mnp, pll)) { return mnp; } - } else if (!found && g450_testpll(PMINFO mnp, pll)) { + } else if (!found && g450_testpll(minfo, mnp, pll)) { mnpfound = mnp; found = 1; } } } - g450_setpll(PMINFO mnpfound, pll); + g450_setpll(minfo, mnpfound, pll); return mnpfound; } @@ -283,7 +306,9 @@ static void g450_addcache(struct matrox_pll_cache* ci, unsigned int mnp_key, uns ci->data[0].mnp_value = mnp_value; } -static int g450_checkcache(WPMINFO struct matrox_pll_cache* ci, unsigned int mnp_key) { +static int g450_checkcache(struct matrox_fb_info *minfo, + struct matrox_pll_cache *ci, unsigned int mnp_key) +{ unsigned int i; mnp_key &= G450_MNP_FREQBITS; @@ -303,8 +328,10 @@ static int g450_checkcache(WPMINFO struct matrox_pll_cache* ci, unsigned int mnp return NO_MORE_MNP; } -static int __g450_setclk(WPMINFO unsigned int fout, unsigned int pll, - unsigned int* mnparray, unsigned int* deltaarray) { +static int __g450_setclk(struct matrox_fb_info *minfo, unsigned int fout, + unsigned int pll, unsigned int *mnparray, + unsigned int *deltaarray) +{ unsigned int mnpcount; unsigned int pixel_vco; const struct matrox_pll_limits* pi; @@ -321,19 +348,19 @@ static int __g450_setclk(WPMINFO unsigned int fout, unsigned int pll, matroxfb_DAC_lock_irqsave(flags); - xpwrctrl = matroxfb_DAC_in(PMINFO M1064_XPWRCTRL); - matroxfb_DAC_out(PMINFO M1064_XPWRCTRL, xpwrctrl & ~M1064_XPWRCTRL_PANELPDN); + xpwrctrl = matroxfb_DAC_in(minfo, M1064_XPWRCTRL); + matroxfb_DAC_out(minfo, M1064_XPWRCTRL, xpwrctrl & ~M1064_XPWRCTRL_PANELPDN); mga_outb(M_SEQ_INDEX, M_SEQ1); mga_outb(M_SEQ_DATA, mga_inb(M_SEQ_DATA) | M_SEQ1_SCROFF); - tmp = matroxfb_DAC_in(PMINFO M1064_XPIXCLKCTRL); + tmp = matroxfb_DAC_in(minfo, M1064_XPIXCLKCTRL); tmp |= M1064_XPIXCLKCTRL_DIS; if (!(tmp & M1064_XPIXCLKCTRL_PLL_UP)) { tmp |= M1064_XPIXCLKCTRL_PLL_UP; } - matroxfb_DAC_out(PMINFO M1064_XPIXCLKCTRL, tmp); + matroxfb_DAC_out(minfo, M1064_XPIXCLKCTRL, tmp); /* DVI PLL preferred for frequencies up to panel link max, standard PLL otherwise */ - if (fout >= MINFO->max_pixel_clock_panellink) + if (fout >= minfo->max_pixel_clock_panellink) tmp = 0; else tmp = M1064_XDVICLKCTRL_DVIDATAPATHSEL | @@ -341,8 +368,8 @@ static int __g450_setclk(WPMINFO unsigned int fout, unsigned int pll, M1064_XDVICLKCTRL_C1DVICLKEN | M1064_XDVICLKCTRL_DVILOOPCTL | M1064_XDVICLKCTRL_P1LOOPBWDTCTL; - matroxfb_DAC_out(PMINFO M1064_XDVICLKCTRL,tmp); - matroxfb_DAC_out(PMINFO M1064_XPWRCTRL, + matroxfb_DAC_out(minfo, M1064_XDVICLKCTRL, tmp); + matroxfb_DAC_out(minfo, M1064_XPWRCTRL, xpwrctrl); matroxfb_DAC_unlock_irqrestore(flags); @@ -363,20 +390,20 @@ static int __g450_setclk(WPMINFO unsigned int fout, unsigned int pll, } mga_outb(M_MISC_REG, misc); } - pi = &ACCESS_FBINFO(limits.pixel); - ci = &ACCESS_FBINFO(cache.pixel); + pi = &minfo->limits.pixel; + ci = &minfo->cache.pixel; break; case M_SYSTEM_PLL: { u_int32_t opt; - pci_read_config_dword(ACCESS_FBINFO(pcidev), PCI_OPTION_REG, &opt); + pci_read_config_dword(minfo->pcidev, PCI_OPTION_REG, &opt); if (!(opt & 0x20)) { - pci_write_config_dword(ACCESS_FBINFO(pcidev), PCI_OPTION_REG, opt | 0x20); + pci_write_config_dword(minfo->pcidev, PCI_OPTION_REG, opt | 0x20); } } - pi = &ACCESS_FBINFO(limits.system); - ci = &ACCESS_FBINFO(cache.system); + pi = &minfo->limits.system; + ci = &minfo->cache.system; break; case M_VIDEO_PLL: { @@ -385,18 +412,18 @@ static int __g450_setclk(WPMINFO unsigned int fout, unsigned int pll, unsigned long flags; matroxfb_DAC_lock_irqsave(flags); - tmp = matroxfb_DAC_in(PMINFO M1064_XPWRCTRL); + tmp = matroxfb_DAC_in(minfo, M1064_XPWRCTRL); if (!(tmp & 2)) { - matroxfb_DAC_out(PMINFO M1064_XPWRCTRL, tmp | 2); + matroxfb_DAC_out(minfo, M1064_XPWRCTRL, tmp | 2); } - mnp = matroxfb_DAC_in(PMINFO M1064_XPIXPLLCM) << 16; - mnp |= matroxfb_DAC_in(PMINFO M1064_XPIXPLLCN) << 8; - pixel_vco = g450_mnp2vco(PMINFO mnp); + mnp = matroxfb_DAC_in(minfo, M1064_XPIXPLLCM) << 16; + mnp |= matroxfb_DAC_in(minfo, M1064_XPIXPLLCN) << 8; + pixel_vco = g450_mnp2vco(minfo, mnp); matroxfb_DAC_unlock_irqrestore(flags); } - pi = &ACCESS_FBINFO(limits.video); - ci = &ACCESS_FBINFO(cache.video); + pi = &minfo->limits.video; + ci = &minfo->cache.video; break; default: return -EINVAL; @@ -407,12 +434,12 @@ static int __g450_setclk(WPMINFO unsigned int fout, unsigned int pll, unsigned int mnp; unsigned int xvco; - for(mnp = g450_firstpll(PMINFO pi, &xvco, fout); mnp != NO_MORE_MNP; mnp = g450_nextpll(PMINFO pi, &xvco, mnp)) { + for (mnp = g450_firstpll(minfo, pi, &xvco, fout); mnp != NO_MORE_MNP; mnp = g450_nextpll(minfo, pi, &xvco, mnp)) { unsigned int idx; unsigned int vco; unsigned int delta; - vco = g450_mnp2vco(PMINFO mnp); + vco = g450_mnp2vco(minfo, mnp); #if 0 if (pll == M_VIDEO_PLL) { unsigned int big, small; @@ -444,7 +471,7 @@ static int __g450_setclk(WPMINFO unsigned int fout, unsigned int pll, * (freqs near VCOmin aren't as stable) */ if (delta == deltaarray[idx-1] - && vco != g450_mnp2vco(PMINFO mnparray[idx-1]) + && vco != g450_mnp2vco(minfo, mnparray[idx-1]) && vco < (pi->vcomin * 17 / 16)) { break; } @@ -468,14 +495,14 @@ static int __g450_setclk(WPMINFO unsigned int fout, unsigned int pll, unsigned int mnp; matroxfb_DAC_lock_irqsave(flags); - mnp = g450_checkcache(PMINFO ci, mnparray[0]); + mnp = g450_checkcache(minfo, ci, mnparray[0]); if (mnp != NO_MORE_MNP) { - matroxfb_g450_setpll_cond(PMINFO mnp, pll); + matroxfb_g450_setpll_cond(minfo, mnp, pll); } else { - mnp = g450_findworkingpll(PMINFO pll, mnparray, mnpcount); + mnp = g450_findworkingpll(minfo, pll, mnparray, mnpcount); g450_addcache(ci, mnparray[0], mnp); } - updatehwstate_clk(&ACCESS_FBINFO(hw), mnp, pll); + updatehwstate_clk(&minfo->hw, mnp, pll); matroxfb_DAC_unlock_irqrestore(flags); return mnp; } @@ -485,14 +512,16 @@ static int __g450_setclk(WPMINFO unsigned int fout, unsigned int pll, * Currently there is 5(p) * 10(m) = 50 possible values. */ #define MNP_TABLE_SIZE 64 -int matroxfb_g450_setclk(WPMINFO unsigned int fout, unsigned int pll) { +int matroxfb_g450_setclk(struct matrox_fb_info *minfo, unsigned int fout, + unsigned int pll) +{ unsigned int* arr; arr = kmalloc(sizeof(*arr) * MNP_TABLE_SIZE * 2, GFP_KERNEL); if (arr) { int r; - r = __g450_setclk(PMINFO fout, pll, arr, arr + MNP_TABLE_SIZE); + r = __g450_setclk(minfo, fout, pll, arr, arr + MNP_TABLE_SIZE); kfree(arr); return r; } diff --git a/drivers/video/matrox/g450_pll.h b/drivers/video/matrox/g450_pll.h index c17ed74501e9..aac615d18440 100644 --- a/drivers/video/matrox/g450_pll.h +++ b/drivers/video/matrox/g450_pll.h @@ -3,8 +3,10 @@ #include "matroxfb_base.h" -int matroxfb_g450_setclk(WPMINFO unsigned int fout, unsigned int pll); -unsigned int g450_mnp2f(CPMINFO unsigned int mnp); -void matroxfb_g450_setpll_cond(WPMINFO unsigned int mnp, unsigned int pll); +int matroxfb_g450_setclk(struct matrox_fb_info *minfo, unsigned int fout, + unsigned int pll); +unsigned int g450_mnp2f(const struct matrox_fb_info *minfo, unsigned int mnp); +void matroxfb_g450_setpll_cond(struct matrox_fb_info *minfo, unsigned int mnp, + unsigned int pll); #endif /* __G450_PLL_H__ */ diff --git a/drivers/video/matrox/i2c-matroxfb.c b/drivers/video/matrox/i2c-matroxfb.c index c14e3e2212b3..f3728ab262f8 100644 --- a/drivers/video/matrox/i2c-matroxfb.c +++ b/drivers/video/matrox/i2c-matroxfb.c @@ -41,7 +41,7 @@ static int matroxfb_read_gpio(struct matrox_fb_info* minfo) { int v; matroxfb_DAC_lock_irqsave(flags); - v = matroxfb_DAC_in(PMINFO DAC_XGENIODATA); + v = matroxfb_DAC_in(minfo, DAC_XGENIODATA); matroxfb_DAC_unlock_irqrestore(flags); return v; } @@ -51,10 +51,10 @@ static void matroxfb_set_gpio(struct matrox_fb_info* minfo, int mask, int val) { int v; matroxfb_DAC_lock_irqsave(flags); - v = (matroxfb_DAC_in(PMINFO DAC_XGENIOCTRL) & mask) | val; - matroxfb_DAC_out(PMINFO DAC_XGENIOCTRL, v); + v = (matroxfb_DAC_in(minfo, DAC_XGENIOCTRL) & mask) | val; + matroxfb_DAC_out(minfo, DAC_XGENIOCTRL, v); /* We must reset GENIODATA very often... XFree plays with this register */ - matroxfb_DAC_out(PMINFO DAC_XGENIODATA, 0x00); + matroxfb_DAC_out(minfo, DAC_XGENIODATA, 0x00); matroxfb_DAC_unlock_irqrestore(flags); } @@ -112,7 +112,7 @@ static int i2c_bus_reg(struct i2c_bit_adapter* b, struct matrox_fb_info* minfo, i2c_set_adapdata(&b->adapter, b); b->adapter.class = class; b->adapter.algo_data = &b->bac; - b->adapter.dev.parent = &ACCESS_FBINFO(pcidev)->dev; + b->adapter.dev.parent = &minfo->pcidev->dev; b->bac = matrox_i2c_algo_template; b->bac.data = b; err = i2c_bit_add_bus(&b->adapter); @@ -149,11 +149,11 @@ static void* i2c_matroxfb_probe(struct matrox_fb_info* minfo) { return NULL; matroxfb_DAC_lock_irqsave(flags); - matroxfb_DAC_out(PMINFO DAC_XGENIODATA, 0xFF); - matroxfb_DAC_out(PMINFO DAC_XGENIOCTRL, 0x00); + matroxfb_DAC_out(minfo, DAC_XGENIODATA, 0xFF); + matroxfb_DAC_out(minfo, DAC_XGENIOCTRL, 0x00); matroxfb_DAC_unlock_irqrestore(flags); - switch (ACCESS_FBINFO(chip)) { + switch (minfo->chip) { case MGA_2064: case MGA_2164: err = i2c_bus_reg(&m2info->ddc1, minfo, @@ -168,7 +168,7 @@ static void* i2c_matroxfb_probe(struct matrox_fb_info* minfo) { } if (err) goto fail_ddc1; - if (ACCESS_FBINFO(devflags.dualhead)) { + if (minfo->devflags.dualhead) { err = i2c_bus_reg(&m2info->ddc2, minfo, DDC2_DATA, DDC2_CLK, "DDC:fb%u #1", I2C_CLASS_DDC); diff --git a/drivers/video/matrox/matroxfb_DAC1064.c b/drivers/video/matrox/matroxfb_DAC1064.c index a74e5da17aa0..f9fa0fd00292 100644 --- a/drivers/video/matrox/matroxfb_DAC1064.c +++ b/drivers/video/matrox/matroxfb_DAC1064.c @@ -33,7 +33,11 @@ #define DAC1064_OPT_MDIV2 0x00 #define DAC1064_OPT_RESERVED 0x10 -static void DAC1064_calcclock(CPMINFO unsigned int freq, unsigned int fmax, unsigned int* in, unsigned int* feed, unsigned int* post) { +static void DAC1064_calcclock(const struct matrox_fb_info *minfo, + unsigned int freq, unsigned int fmax, + unsigned int *in, unsigned int *feed, + unsigned int *post) +{ unsigned int fvco; unsigned int p; @@ -41,7 +45,7 @@ static void DAC1064_calcclock(CPMINFO unsigned int freq, unsigned int fmax, unsi /* only for devices older than G450 */ - fvco = PLL_calcclock(PMINFO freq, fmax, in, feed, &p); + fvco = PLL_calcclock(minfo, freq, fmax, in, feed, &p); p = (1 << p) - 1; if (fvco <= 100000) @@ -80,32 +84,35 @@ static const unsigned char MGA1064_DAC[] = { 0x00, 0x00, 0x00, 0xFF, 0xFF}; -static void DAC1064_setpclk(WPMINFO unsigned long fout) { +static void DAC1064_setpclk(struct matrox_fb_info *minfo, unsigned long fout) +{ unsigned int m, n, p; DBG(__func__) - DAC1064_calcclock(PMINFO fout, ACCESS_FBINFO(max_pixel_clock), &m, &n, &p); - ACCESS_FBINFO(hw).DACclk[0] = m; - ACCESS_FBINFO(hw).DACclk[1] = n; - ACCESS_FBINFO(hw).DACclk[2] = p; + DAC1064_calcclock(minfo, fout, minfo->max_pixel_clock, &m, &n, &p); + minfo->hw.DACclk[0] = m; + minfo->hw.DACclk[1] = n; + minfo->hw.DACclk[2] = p; } -static void DAC1064_setmclk(WPMINFO int oscinfo, unsigned long fmem) { +static void DAC1064_setmclk(struct matrox_fb_info *minfo, int oscinfo, + unsigned long fmem) +{ u_int32_t mx; - struct matrox_hw_state* hw = &ACCESS_FBINFO(hw); + struct matrox_hw_state *hw = &minfo->hw; DBG(__func__) - if (ACCESS_FBINFO(devflags.noinit)) { + if (minfo->devflags.noinit) { /* read MCLK and give up... */ - hw->DACclk[3] = inDAC1064(PMINFO DAC1064_XSYSPLLM); - hw->DACclk[4] = inDAC1064(PMINFO DAC1064_XSYSPLLN); - hw->DACclk[5] = inDAC1064(PMINFO DAC1064_XSYSPLLP); + hw->DACclk[3] = inDAC1064(minfo, DAC1064_XSYSPLLM); + hw->DACclk[4] = inDAC1064(minfo, DAC1064_XSYSPLLN); + hw->DACclk[5] = inDAC1064(minfo, DAC1064_XSYSPLLP); return; } mx = hw->MXoptionReg | 0x00000004; - pci_write_config_dword(ACCESS_FBINFO(pcidev), PCI_OPTION_REG, mx); + pci_write_config_dword(minfo->pcidev, PCI_OPTION_REG, mx); mx &= ~0x000000BB; if (oscinfo & DAC1064_OPT_GDIV1) mx |= 0x00000008; @@ -120,9 +127,9 @@ static void DAC1064_setmclk(WPMINFO int oscinfo, unsigned long fmem) { /* powerup system PLL, select PCI clock */ mx |= 0x00000020; - pci_write_config_dword(ACCESS_FBINFO(pcidev), PCI_OPTION_REG, mx); + pci_write_config_dword(minfo->pcidev, PCI_OPTION_REG, mx); mx &= ~0x00000004; - pci_write_config_dword(ACCESS_FBINFO(pcidev), PCI_OPTION_REG, mx); + pci_write_config_dword(minfo->pcidev, PCI_OPTION_REG, mx); /* !!! you must not access device if MCLK is not running !!! Doing so cause immediate PCI lockup :-( Maybe they should @@ -131,12 +138,12 @@ static void DAC1064_setmclk(WPMINFO int oscinfo, unsigned long fmem) { perfect... */ /* (bit 2 of PCI_OPTION_REG must be 0... and bits 0,1 must not select PLL... because of PLL can be stopped at this time) */ - DAC1064_calcclock(PMINFO fmem, ACCESS_FBINFO(max_pixel_clock), &m, &n, &p); - outDAC1064(PMINFO DAC1064_XSYSPLLM, hw->DACclk[3] = m); - outDAC1064(PMINFO DAC1064_XSYSPLLN, hw->DACclk[4] = n); - outDAC1064(PMINFO DAC1064_XSYSPLLP, hw->DACclk[5] = p); + DAC1064_calcclock(minfo, fmem, minfo->max_pixel_clock, &m, &n, &p); + outDAC1064(minfo, DAC1064_XSYSPLLM, hw->DACclk[3] = m); + outDAC1064(minfo, DAC1064_XSYSPLLN, hw->DACclk[4] = n); + outDAC1064(minfo, DAC1064_XSYSPLLP, hw->DACclk[5] = p); for (clk = 65536; clk; --clk) { - if (inDAC1064(PMINFO DAC1064_XSYSPLLSTAT) & 0x40) + if (inDAC1064(minfo, DAC1064_XSYSPLLSTAT) & 0x40) break; } if (!clk) @@ -147,29 +154,30 @@ static void DAC1064_setmclk(WPMINFO int oscinfo, unsigned long fmem) { /* select specified system clock source */ mx |= oscinfo & DAC1064_OPT_SCLK_MASK; } - pci_write_config_dword(ACCESS_FBINFO(pcidev), PCI_OPTION_REG, mx); + pci_write_config_dword(minfo->pcidev, PCI_OPTION_REG, mx); mx &= ~0x00000004; - pci_write_config_dword(ACCESS_FBINFO(pcidev), PCI_OPTION_REG, mx); + pci_write_config_dword(minfo->pcidev, PCI_OPTION_REG, mx); hw->MXoptionReg = mx; } #ifdef CONFIG_FB_MATROX_G -static void g450_set_plls(WPMINFO2) { +static void g450_set_plls(struct matrox_fb_info *minfo) +{ u_int32_t c2_ctl; unsigned int pxc; - struct matrox_hw_state* hw = &ACCESS_FBINFO(hw); + struct matrox_hw_state *hw = &minfo->hw; int pixelmnp; int videomnp; c2_ctl = hw->crtc2.ctl & ~0x4007; /* Clear PLL + enable for CRTC2 */ c2_ctl |= 0x0001; /* Enable CRTC2 */ hw->DACreg[POS1064_XPWRCTRL] &= ~0x02; /* Stop VIDEO PLL */ - pixelmnp = ACCESS_FBINFO(crtc1).mnp; - videomnp = ACCESS_FBINFO(crtc2).mnp; + pixelmnp = minfo->crtc1.mnp; + videomnp = minfo->crtc2.mnp; if (videomnp < 0) { c2_ctl &= ~0x0001; /* Disable CRTC2 */ hw->DACreg[POS1064_XPWRCTRL] &= ~0x10; /* Powerdown CRTC2 */ - } else if (ACCESS_FBINFO(crtc2).pixclock == ACCESS_FBINFO(features).pll.ref_freq) { + } else if (minfo->crtc2.pixclock == minfo->features.pll.ref_freq) { c2_ctl |= 0x4002; /* Use reference directly */ } else if (videomnp == pixelmnp) { c2_ctl |= 0x0004; /* Use pixel PLL */ @@ -184,27 +192,27 @@ static void g450_set_plls(WPMINFO2) { c2_ctl |= 0x0006; /* Use video PLL */ hw->DACreg[POS1064_XPWRCTRL] |= 0x02; - outDAC1064(PMINFO M1064_XPWRCTRL, hw->DACreg[POS1064_XPWRCTRL]); - matroxfb_g450_setpll_cond(PMINFO videomnp, M_VIDEO_PLL); + outDAC1064(minfo, M1064_XPWRCTRL, hw->DACreg[POS1064_XPWRCTRL]); + matroxfb_g450_setpll_cond(minfo, videomnp, M_VIDEO_PLL); } hw->DACreg[POS1064_XPIXCLKCTRL] &= ~M1064_XPIXCLKCTRL_PLL_UP; if (pixelmnp >= 0) { hw->DACreg[POS1064_XPIXCLKCTRL] |= M1064_XPIXCLKCTRL_PLL_UP; - outDAC1064(PMINFO M1064_XPIXCLKCTRL, hw->DACreg[POS1064_XPIXCLKCTRL]); - matroxfb_g450_setpll_cond(PMINFO pixelmnp, M_PIXEL_PLL_C); + outDAC1064(minfo, M1064_XPIXCLKCTRL, hw->DACreg[POS1064_XPIXCLKCTRL]); + matroxfb_g450_setpll_cond(minfo, pixelmnp, M_PIXEL_PLL_C); } if (c2_ctl != hw->crtc2.ctl) { hw->crtc2.ctl = c2_ctl; mga_outl(0x3C10, c2_ctl); } - pxc = ACCESS_FBINFO(crtc1).pixclock; - if (pxc == 0 || ACCESS_FBINFO(outputs[2]).src == MATROXFB_SRC_CRTC2) { - pxc = ACCESS_FBINFO(crtc2).pixclock; + pxc = minfo->crtc1.pixclock; + if (pxc == 0 || minfo->outputs[2].src == MATROXFB_SRC_CRTC2) { + pxc = minfo->crtc2.pixclock; } - if (ACCESS_FBINFO(chip) == MGA_G550) { + if (minfo->chip == MGA_G550) { if (pxc < 45000) { hw->DACreg[POS1064_XPANMODE] = 0x00; /* 0-50 */ } else if (pxc < 55000) { @@ -245,18 +253,19 @@ static void g450_set_plls(WPMINFO2) { } #endif -void DAC1064_global_init(WPMINFO2) { - struct matrox_hw_state* hw = &ACCESS_FBINFO(hw); +void DAC1064_global_init(struct matrox_fb_info *minfo) +{ + struct matrox_hw_state *hw = &minfo->hw; hw->DACreg[POS1064_XMISCCTRL] &= M1064_XMISCCTRL_DAC_WIDTHMASK; hw->DACreg[POS1064_XMISCCTRL] |= M1064_XMISCCTRL_LUT_EN; hw->DACreg[POS1064_XPIXCLKCTRL] = M1064_XPIXCLKCTRL_PLL_UP | M1064_XPIXCLKCTRL_EN | M1064_XPIXCLKCTRL_SRC_PLL; #ifdef CONFIG_FB_MATROX_G - if (ACCESS_FBINFO(devflags.g450dac)) { + if (minfo->devflags.g450dac) { hw->DACreg[POS1064_XPWRCTRL] = 0x1F; /* powerup everything */ hw->DACreg[POS1064_XOUTPUTCONN] = 0x00; /* disable outputs */ hw->DACreg[POS1064_XMISCCTRL] |= M1064_XMISCCTRL_DAC_EN; - switch (ACCESS_FBINFO(outputs[0]).src) { + switch (minfo->outputs[0].src) { case MATROXFB_SRC_CRTC1: case MATROXFB_SRC_CRTC2: hw->DACreg[POS1064_XOUTPUTCONN] |= 0x01; /* enable output; CRTC1/2 selection is in CRTC2 ctl */ @@ -265,12 +274,12 @@ void DAC1064_global_init(WPMINFO2) { hw->DACreg[POS1064_XMISCCTRL] &= ~M1064_XMISCCTRL_DAC_EN; break; } - switch (ACCESS_FBINFO(outputs[1]).src) { + switch (minfo->outputs[1].src) { case MATROXFB_SRC_CRTC1: hw->DACreg[POS1064_XOUTPUTCONN] |= 0x04; break; case MATROXFB_SRC_CRTC2: - if (ACCESS_FBINFO(outputs[1]).mode == MATROXFB_OUTPUT_MODE_MONITOR) { + if (minfo->outputs[1].mode == MATROXFB_OUTPUT_MODE_MONITOR) { hw->DACreg[POS1064_XOUTPUTCONN] |= 0x08; } else { hw->DACreg[POS1064_XOUTPUTCONN] |= 0x0C; @@ -280,7 +289,7 @@ void DAC1064_global_init(WPMINFO2) { hw->DACreg[POS1064_XPWRCTRL] &= ~0x01; /* Poweroff DAC2 */ break; } - switch (ACCESS_FBINFO(outputs[2]).src) { + switch (minfo->outputs[2].src) { case MATROXFB_SRC_CRTC1: hw->DACreg[POS1064_XOUTPUTCONN] |= 0x20; break; @@ -299,55 +308,57 @@ void DAC1064_global_init(WPMINFO2) { break; } /* Now set timming related variables... */ - g450_set_plls(PMINFO2); + g450_set_plls(minfo); } else #endif { - if (ACCESS_FBINFO(outputs[1]).src == MATROXFB_SRC_CRTC1) { + if (minfo->outputs[1].src == MATROXFB_SRC_CRTC1) { hw->DACreg[POS1064_XPIXCLKCTRL] = M1064_XPIXCLKCTRL_PLL_UP | M1064_XPIXCLKCTRL_EN | M1064_XPIXCLKCTRL_SRC_EXT; hw->DACreg[POS1064_XMISCCTRL] |= GX00_XMISCCTRL_MFC_MAFC | G400_XMISCCTRL_VDO_MAFC12; - } else if (ACCESS_FBINFO(outputs[1]).src == MATROXFB_SRC_CRTC2) { + } else if (minfo->outputs[1].src == MATROXFB_SRC_CRTC2) { hw->DACreg[POS1064_XMISCCTRL] |= GX00_XMISCCTRL_MFC_MAFC | G400_XMISCCTRL_VDO_C2_MAFC12; - } else if (ACCESS_FBINFO(outputs[2]).src == MATROXFB_SRC_CRTC1) + } else if (minfo->outputs[2].src == MATROXFB_SRC_CRTC1) hw->DACreg[POS1064_XMISCCTRL] |= GX00_XMISCCTRL_MFC_PANELLINK | G400_XMISCCTRL_VDO_MAFC12; else hw->DACreg[POS1064_XMISCCTRL] |= GX00_XMISCCTRL_MFC_DIS; - if (ACCESS_FBINFO(outputs[0]).src != MATROXFB_SRC_NONE) + if (minfo->outputs[0].src != MATROXFB_SRC_NONE) hw->DACreg[POS1064_XMISCCTRL] |= M1064_XMISCCTRL_DAC_EN; } } -void DAC1064_global_restore(WPMINFO2) { - struct matrox_hw_state* hw = &ACCESS_FBINFO(hw); - - outDAC1064(PMINFO M1064_XPIXCLKCTRL, hw->DACreg[POS1064_XPIXCLKCTRL]); - outDAC1064(PMINFO M1064_XMISCCTRL, hw->DACreg[POS1064_XMISCCTRL]); - if (ACCESS_FBINFO(devflags.accelerator) == FB_ACCEL_MATROX_MGAG400) { - outDAC1064(PMINFO 0x20, 0x04); - outDAC1064(PMINFO 0x1F, ACCESS_FBINFO(devflags.dfp_type)); - if (ACCESS_FBINFO(devflags.g450dac)) { - outDAC1064(PMINFO M1064_XSYNCCTRL, 0xCC); - outDAC1064(PMINFO M1064_XPWRCTRL, hw->DACreg[POS1064_XPWRCTRL]); - outDAC1064(PMINFO M1064_XPANMODE, hw->DACreg[POS1064_XPANMODE]); - outDAC1064(PMINFO M1064_XOUTPUTCONN, hw->DACreg[POS1064_XOUTPUTCONN]); +void DAC1064_global_restore(struct matrox_fb_info *minfo) +{ + struct matrox_hw_state *hw = &minfo->hw; + + outDAC1064(minfo, M1064_XPIXCLKCTRL, hw->DACreg[POS1064_XPIXCLKCTRL]); + outDAC1064(minfo, M1064_XMISCCTRL, hw->DACreg[POS1064_XMISCCTRL]); + if (minfo->devflags.accelerator == FB_ACCEL_MATROX_MGAG400) { + outDAC1064(minfo, 0x20, 0x04); + outDAC1064(minfo, 0x1F, minfo->devflags.dfp_type); + if (minfo->devflags.g450dac) { + outDAC1064(minfo, M1064_XSYNCCTRL, 0xCC); + outDAC1064(minfo, M1064_XPWRCTRL, hw->DACreg[POS1064_XPWRCTRL]); + outDAC1064(minfo, M1064_XPANMODE, hw->DACreg[POS1064_XPANMODE]); + outDAC1064(minfo, M1064_XOUTPUTCONN, hw->DACreg[POS1064_XOUTPUTCONN]); } } } -static int DAC1064_init_1(WPMINFO struct my_timming* m) { - struct matrox_hw_state* hw = &ACCESS_FBINFO(hw); +static int DAC1064_init_1(struct matrox_fb_info *minfo, struct my_timming *m) +{ + struct matrox_hw_state *hw = &minfo->hw; DBG(__func__) memcpy(hw->DACreg, MGA1064_DAC, sizeof(MGA1064_DAC_regs)); - switch (ACCESS_FBINFO(fbcon).var.bits_per_pixel) { + switch (minfo->fbcon.var.bits_per_pixel) { /* case 4: not supported by MGA1064 DAC */ case 8: hw->DACreg[POS1064_XMULCTRL] = M1064_XMULCTRL_DEPTH_8BPP | M1064_XMULCTRL_GRAPHICS_PALETIZED; break; case 16: - if (ACCESS_FBINFO(fbcon).var.green.length == 5) + if (minfo->fbcon.var.green.length == 5) hw->DACreg[POS1064_XMULCTRL] = M1064_XMULCTRL_DEPTH_15BPP_1BPP | M1064_XMULCTRL_GRAPHICS_PALETIZED; else hw->DACreg[POS1064_XMULCTRL] = M1064_XMULCTRL_DEPTH_16BPP | M1064_XMULCTRL_GRAPHICS_PALETIZED; @@ -361,22 +372,23 @@ static int DAC1064_init_1(WPMINFO struct my_timming* m) { default: return 1; /* unsupported depth */ } - hw->DACreg[POS1064_XVREFCTRL] = ACCESS_FBINFO(features.DAC1064.xvrefctrl); + hw->DACreg[POS1064_XVREFCTRL] = minfo->features.DAC1064.xvrefctrl; hw->DACreg[POS1064_XGENCTRL] &= ~M1064_XGENCTRL_SYNC_ON_GREEN_MASK; hw->DACreg[POS1064_XGENCTRL] |= (m->sync & FB_SYNC_ON_GREEN)?M1064_XGENCTRL_SYNC_ON_GREEN:M1064_XGENCTRL_NO_SYNC_ON_GREEN; hw->DACreg[POS1064_XCURADDL] = 0; hw->DACreg[POS1064_XCURADDH] = 0; - DAC1064_global_init(PMINFO2); + DAC1064_global_init(minfo); return 0; } -static int DAC1064_init_2(WPMINFO struct my_timming* m) { - struct matrox_hw_state* hw = &ACCESS_FBINFO(hw); +static int DAC1064_init_2(struct matrox_fb_info *minfo, struct my_timming *m) +{ + struct matrox_hw_state *hw = &minfo->hw; DBG(__func__) - if (ACCESS_FBINFO(fbcon).var.bits_per_pixel > 16) { /* 256 entries */ + if (minfo->fbcon.var.bits_per_pixel > 16) { /* 256 entries */ int i; for (i = 0; i < 256; i++) { @@ -384,8 +396,8 @@ static int DAC1064_init_2(WPMINFO struct my_timming* m) { hw->DACpal[i * 3 + 1] = i; hw->DACpal[i * 3 + 2] = i; } - } else if (ACCESS_FBINFO(fbcon).var.bits_per_pixel > 8) { - if (ACCESS_FBINFO(fbcon).var.green.length == 5) { /* 0..31, 128..159 */ + } else if (minfo->fbcon.var.bits_per_pixel > 8) { + if (minfo->fbcon.var.green.length == 5) { /* 0..31, 128..159 */ int i; for (i = 0; i < 32; i++) { @@ -413,8 +425,9 @@ static int DAC1064_init_2(WPMINFO struct my_timming* m) { return 0; } -static void DAC1064_restore_1(WPMINFO2) { - struct matrox_hw_state* hw = &ACCESS_FBINFO(hw); +static void DAC1064_restore_1(struct matrox_fb_info *minfo) +{ + struct matrox_hw_state *hw = &minfo->hw; CRITFLAGS @@ -422,28 +435,29 @@ static void DAC1064_restore_1(WPMINFO2) { CRITBEGIN - if ((inDAC1064(PMINFO DAC1064_XSYSPLLM) != hw->DACclk[3]) || - (inDAC1064(PMINFO DAC1064_XSYSPLLN) != hw->DACclk[4]) || - (inDAC1064(PMINFO DAC1064_XSYSPLLP) != hw->DACclk[5])) { - outDAC1064(PMINFO DAC1064_XSYSPLLM, hw->DACclk[3]); - outDAC1064(PMINFO DAC1064_XSYSPLLN, hw->DACclk[4]); - outDAC1064(PMINFO DAC1064_XSYSPLLP, hw->DACclk[5]); + if ((inDAC1064(minfo, DAC1064_XSYSPLLM) != hw->DACclk[3]) || + (inDAC1064(minfo, DAC1064_XSYSPLLN) != hw->DACclk[4]) || + (inDAC1064(minfo, DAC1064_XSYSPLLP) != hw->DACclk[5])) { + outDAC1064(minfo, DAC1064_XSYSPLLM, hw->DACclk[3]); + outDAC1064(minfo, DAC1064_XSYSPLLN, hw->DACclk[4]); + outDAC1064(minfo, DAC1064_XSYSPLLP, hw->DACclk[5]); } { unsigned int i; for (i = 0; i < sizeof(MGA1064_DAC_regs); i++) { if ((i != POS1064_XPIXCLKCTRL) && (i != POS1064_XMISCCTRL)) - outDAC1064(PMINFO MGA1064_DAC_regs[i], hw->DACreg[i]); + outDAC1064(minfo, MGA1064_DAC_regs[i], hw->DACreg[i]); } } - DAC1064_global_restore(PMINFO2); + DAC1064_global_restore(minfo); CRITEND }; -static void DAC1064_restore_2(WPMINFO2) { +static void DAC1064_restore_2(struct matrox_fb_info *minfo) +{ #ifdef DEBUG unsigned int i; #endif @@ -453,12 +467,12 @@ static void DAC1064_restore_2(WPMINFO2) { #ifdef DEBUG dprintk(KERN_DEBUG "DAC1064regs "); for (i = 0; i < sizeof(MGA1064_DAC_regs); i++) { - dprintk("R%02X=%02X ", MGA1064_DAC_regs[i], ACCESS_FBINFO(hw).DACreg[i]); + dprintk("R%02X=%02X ", MGA1064_DAC_regs[i], minfo->hw.DACreg[i]); if ((i & 0x7) == 0x7) dprintk(KERN_DEBUG "continuing... "); } dprintk(KERN_DEBUG "DAC1064clk "); for (i = 0; i < 6; i++) - dprintk("C%02X=%02X ", i, ACCESS_FBINFO(hw).DACclk[i]); + dprintk("C%02X=%02X ", i, minfo->hw.DACclk[i]); dprintk("\n"); #endif } @@ -470,14 +484,14 @@ static int m1064_compute(void* out, struct my_timming* m) { int tmout; CRITFLAGS - DAC1064_setpclk(PMINFO m->pixclock); + DAC1064_setpclk(minfo, m->pixclock); CRITBEGIN for (i = 0; i < 3; i++) - outDAC1064(PMINFO M1064_XPIXPLLCM + i, ACCESS_FBINFO(hw).DACclk[i]); + outDAC1064(minfo, M1064_XPIXPLLCM + i, minfo->hw.DACclk[i]); for (tmout = 500000; tmout; tmout--) { - if (inDAC1064(PMINFO M1064_XPIXPLLSTAT) & 0x40) + if (inDAC1064(minfo, M1064_XPIXPLLSTAT) & 0x40) break; udelay(10); }; @@ -500,9 +514,9 @@ static struct matrox_altout m1064 = { static int g450_compute(void* out, struct my_timming* m) { #define minfo ((struct matrox_fb_info*)out) if (m->mnp < 0) { - m->mnp = matroxfb_g450_setclk(PMINFO m->pixclock, (m->crtc == MATROXFB_SRC_CRTC1) ? M_PIXEL_PLL_C : M_VIDEO_PLL); + m->mnp = matroxfb_g450_setclk(minfo, m->pixclock, (m->crtc == MATROXFB_SRC_CRTC1) ? M_PIXEL_PLL_C : M_VIDEO_PLL); if (m->mnp >= 0) { - m->pixclock = g450_mnp2f(PMINFO m->mnp); + m->pixclock = g450_mnp2f(minfo, m->mnp); } } #undef minfo @@ -518,13 +532,14 @@ static struct matrox_altout g450out = { #endif /* NEED_DAC1064 */ #ifdef CONFIG_FB_MATROX_MYSTIQUE -static int MGA1064_init(WPMINFO struct my_timming* m) { - struct matrox_hw_state* hw = &ACCESS_FBINFO(hw); +static int MGA1064_init(struct matrox_fb_info *minfo, struct my_timming *m) +{ + struct matrox_hw_state *hw = &minfo->hw; DBG(__func__) - if (DAC1064_init_1(PMINFO m)) return 1; - if (matroxfb_vgaHWinit(PMINFO m)) return 1; + if (DAC1064_init_1(minfo, m)) return 1; + if (matroxfb_vgaHWinit(minfo, m)) return 1; hw->MiscOutReg = 0xCB; if (m->sync & FB_SYNC_HOR_HIGH_ACT) @@ -534,20 +549,21 @@ static int MGA1064_init(WPMINFO struct my_timming* m) { if (m->sync & FB_SYNC_COMP_HIGH_ACT) /* should be only FB_SYNC_COMP */ hw->CRTCEXT[3] |= 0x40; - if (DAC1064_init_2(PMINFO m)) return 1; + if (DAC1064_init_2(minfo, m)) return 1; return 0; } #endif #ifdef CONFIG_FB_MATROX_G -static int MGAG100_init(WPMINFO struct my_timming* m) { - struct matrox_hw_state* hw = &ACCESS_FBINFO(hw); +static int MGAG100_init(struct matrox_fb_info *minfo, struct my_timming *m) +{ + struct matrox_hw_state *hw = &minfo->hw; DBG(__func__) - if (DAC1064_init_1(PMINFO m)) return 1; + if (DAC1064_init_1(minfo, m)) return 1; hw->MXoptionReg &= ~0x2000; - if (matroxfb_vgaHWinit(PMINFO m)) return 1; + if (matroxfb_vgaHWinit(minfo, m)) return 1; hw->MiscOutReg = 0xEF; if (m->sync & FB_SYNC_HOR_HIGH_ACT) @@ -557,27 +573,28 @@ static int MGAG100_init(WPMINFO struct my_timming* m) { if (m->sync & FB_SYNC_COMP_HIGH_ACT) /* should be only FB_SYNC_COMP */ hw->CRTCEXT[3] |= 0x40; - if (DAC1064_init_2(PMINFO m)) return 1; + if (DAC1064_init_2(minfo, m)) return 1; return 0; } #endif /* G */ #ifdef CONFIG_FB_MATROX_MYSTIQUE -static void MGA1064_ramdac_init(WPMINFO2) { +static void MGA1064_ramdac_init(struct matrox_fb_info *minfo) +{ DBG(__func__) - /* ACCESS_FBINFO(features.DAC1064.vco_freq_min) = 120000; */ - ACCESS_FBINFO(features.pll.vco_freq_min) = 62000; - ACCESS_FBINFO(features.pll.ref_freq) = 14318; - ACCESS_FBINFO(features.pll.feed_div_min) = 100; - ACCESS_FBINFO(features.pll.feed_div_max) = 127; - ACCESS_FBINFO(features.pll.in_div_min) = 1; - ACCESS_FBINFO(features.pll.in_div_max) = 31; - ACCESS_FBINFO(features.pll.post_shift_max) = 3; - ACCESS_FBINFO(features.DAC1064.xvrefctrl) = DAC1064_XVREFCTRL_EXTERNAL; + /* minfo->features.DAC1064.vco_freq_min = 120000; */ + minfo->features.pll.vco_freq_min = 62000; + minfo->features.pll.ref_freq = 14318; + minfo->features.pll.feed_div_min = 100; + minfo->features.pll.feed_div_max = 127; + minfo->features.pll.in_div_min = 1; + minfo->features.pll.in_div_max = 31; + minfo->features.pll.post_shift_max = 3; + minfo->features.DAC1064.xvrefctrl = DAC1064_XVREFCTRL_EXTERNAL; /* maybe cmdline MCLK= ?, doc says gclk=44MHz, mclk=66MHz... it was 55/83 with old values */ - DAC1064_setmclk(PMINFO DAC1064_OPT_MDIV2 | DAC1064_OPT_GDIV3 | DAC1064_OPT_SCLK_PLL, 133333); + DAC1064_setmclk(minfo, DAC1064_OPT_MDIV2 | DAC1064_OPT_GDIV3 | DAC1064_OPT_SCLK_PLL, 133333); } #endif @@ -589,23 +606,25 @@ static int x7AF4 = 0x10; /* flags, maybe 0x10 = SDRAM, 0x00 = SGRAM??? */ static int def50 = 0; /* reg50, & 0x0F, & 0x3000 (only 0x0000, 0x1000, 0x2000 (0x3000 disallowed and treated as 0) */ #endif -static void MGAG100_progPixClock(CPMINFO int flags, int m, int n, int p) { +static void MGAG100_progPixClock(const struct matrox_fb_info *minfo, int flags, + int m, int n, int p) +{ int reg; int selClk; int clk; DBG(__func__) - outDAC1064(PMINFO M1064_XPIXCLKCTRL, inDAC1064(PMINFO M1064_XPIXCLKCTRL) | M1064_XPIXCLKCTRL_DIS | + outDAC1064(minfo, M1064_XPIXCLKCTRL, inDAC1064(minfo, M1064_XPIXCLKCTRL) | M1064_XPIXCLKCTRL_DIS | M1064_XPIXCLKCTRL_PLL_UP); switch (flags & 3) { case 0: reg = M1064_XPIXPLLAM; break; case 1: reg = M1064_XPIXPLLBM; break; default: reg = M1064_XPIXPLLCM; break; } - outDAC1064(PMINFO reg++, m); - outDAC1064(PMINFO reg++, n); - outDAC1064(PMINFO reg, p); + outDAC1064(minfo, reg++, m); + outDAC1064(minfo, reg++, n); + outDAC1064(minfo, reg, p); selClk = mga_inb(M_MISC_REG_READ) & ~0xC; /* there should be flags & 0x03 & case 0/1/else */ /* and we should first select source and after that we should wait for PLL */ @@ -617,61 +636,64 @@ static void MGAG100_progPixClock(CPMINFO int flags, int m, int n, int p) { } mga_outb(M_MISC_REG, selClk); for (clk = 500000; clk; clk--) { - if (inDAC1064(PMINFO M1064_XPIXPLLSTAT) & 0x40) + if (inDAC1064(minfo, M1064_XPIXPLLSTAT) & 0x40) break; udelay(10); }; if (!clk) printk(KERN_ERR "matroxfb: Pixel PLL%c not locked after usual time\n", (reg-M1064_XPIXPLLAM-2)/4 + 'A'); - selClk = inDAC1064(PMINFO M1064_XPIXCLKCTRL) & ~M1064_XPIXCLKCTRL_SRC_MASK; + selClk = inDAC1064(minfo, M1064_XPIXCLKCTRL) & ~M1064_XPIXCLKCTRL_SRC_MASK; switch (flags & 0x0C) { case 0x00: selClk |= M1064_XPIXCLKCTRL_SRC_PCI; break; case 0x04: selClk |= M1064_XPIXCLKCTRL_SRC_PLL; break; default: selClk |= M1064_XPIXCLKCTRL_SRC_EXT; break; } - outDAC1064(PMINFO M1064_XPIXCLKCTRL, selClk); - outDAC1064(PMINFO M1064_XPIXCLKCTRL, inDAC1064(PMINFO M1064_XPIXCLKCTRL) & ~M1064_XPIXCLKCTRL_DIS); + outDAC1064(minfo, M1064_XPIXCLKCTRL, selClk); + outDAC1064(minfo, M1064_XPIXCLKCTRL, inDAC1064(minfo, M1064_XPIXCLKCTRL) & ~M1064_XPIXCLKCTRL_DIS); } -static void MGAG100_setPixClock(CPMINFO int flags, int freq) { +static void MGAG100_setPixClock(const struct matrox_fb_info *minfo, int flags, + int freq) +{ unsigned int m, n, p; DBG(__func__) - DAC1064_calcclock(PMINFO freq, ACCESS_FBINFO(max_pixel_clock), &m, &n, &p); - MGAG100_progPixClock(PMINFO flags, m, n, p); + DAC1064_calcclock(minfo, freq, minfo->max_pixel_clock, &m, &n, &p); + MGAG100_progPixClock(minfo, flags, m, n, p); } #endif #ifdef CONFIG_FB_MATROX_MYSTIQUE -static int MGA1064_preinit(WPMINFO2) { +static int MGA1064_preinit(struct matrox_fb_info *minfo) +{ static const int vxres_mystique[] = { 512, 640, 768, 800, 832, 960, 1024, 1152, 1280, 1600, 1664, 1920, 2048, 0}; - struct matrox_hw_state* hw = &ACCESS_FBINFO(hw); + struct matrox_hw_state *hw = &minfo->hw; DBG(__func__) - /* ACCESS_FBINFO(capable.cfb4) = 0; ... preinitialized by 0 */ - ACCESS_FBINFO(capable.text) = 1; - ACCESS_FBINFO(capable.vxres) = vxres_mystique; + /* minfo->capable.cfb4 = 0; ... preinitialized by 0 */ + minfo->capable.text = 1; + minfo->capable.vxres = vxres_mystique; - ACCESS_FBINFO(outputs[0]).output = &m1064; - ACCESS_FBINFO(outputs[0]).src = ACCESS_FBINFO(outputs[0]).default_src; - ACCESS_FBINFO(outputs[0]).data = MINFO; - ACCESS_FBINFO(outputs[0]).mode = MATROXFB_OUTPUT_MODE_MONITOR; + minfo->outputs[0].output = &m1064; + minfo->outputs[0].src = minfo->outputs[0].default_src; + minfo->outputs[0].data = minfo; + minfo->outputs[0].mode = MATROXFB_OUTPUT_MODE_MONITOR; - if (ACCESS_FBINFO(devflags.noinit)) + if (minfo->devflags.noinit) return 0; /* do not modify settings */ hw->MXoptionReg &= 0xC0000100; hw->MXoptionReg |= 0x00094E20; - if (ACCESS_FBINFO(devflags.novga)) + if (minfo->devflags.novga) hw->MXoptionReg &= ~0x00000100; - if (ACCESS_FBINFO(devflags.nobios)) + if (minfo->devflags.nobios) hw->MXoptionReg &= ~0x40000000; - if (ACCESS_FBINFO(devflags.nopciretry)) + if (minfo->devflags.nopciretry) hw->MXoptionReg |= 0x20000000; - pci_write_config_dword(ACCESS_FBINFO(pcidev), PCI_OPTION_REG, hw->MXoptionReg); + pci_write_config_dword(minfo->pcidev, PCI_OPTION_REG, hw->MXoptionReg); mga_setr(M_SEQ_INDEX, 0x01, 0x20); mga_outl(M_CTLWTST, 0x00000000); udelay(200); @@ -681,101 +703,105 @@ static int MGA1064_preinit(WPMINFO2) { return 0; } -static void MGA1064_reset(WPMINFO2) { +static void MGA1064_reset(struct matrox_fb_info *minfo) +{ DBG(__func__); - MGA1064_ramdac_init(PMINFO2); + MGA1064_ramdac_init(minfo); } #endif #ifdef CONFIG_FB_MATROX_G -static void g450_mclk_init(WPMINFO2) { +static void g450_mclk_init(struct matrox_fb_info *minfo) +{ /* switch all clocks to PCI source */ - pci_write_config_dword(ACCESS_FBINFO(pcidev), PCI_OPTION_REG, ACCESS_FBINFO(hw).MXoptionReg | 4); - pci_write_config_dword(ACCESS_FBINFO(pcidev), PCI_OPTION3_REG, ACCESS_FBINFO(values).reg.opt3 & ~0x00300C03); - pci_write_config_dword(ACCESS_FBINFO(pcidev), PCI_OPTION_REG, ACCESS_FBINFO(hw).MXoptionReg); - - if (((ACCESS_FBINFO(values).reg.opt3 & 0x000003) == 0x000003) || - ((ACCESS_FBINFO(values).reg.opt3 & 0x000C00) == 0x000C00) || - ((ACCESS_FBINFO(values).reg.opt3 & 0x300000) == 0x300000)) { - matroxfb_g450_setclk(PMINFO ACCESS_FBINFO(values.pll.video), M_VIDEO_PLL); + pci_write_config_dword(minfo->pcidev, PCI_OPTION_REG, minfo->hw.MXoptionReg | 4); + pci_write_config_dword(minfo->pcidev, PCI_OPTION3_REG, minfo->values.reg.opt3 & ~0x00300C03); + pci_write_config_dword(minfo->pcidev, PCI_OPTION_REG, minfo->hw.MXoptionReg); + + if (((minfo->values.reg.opt3 & 0x000003) == 0x000003) || + ((minfo->values.reg.opt3 & 0x000C00) == 0x000C00) || + ((minfo->values.reg.opt3 & 0x300000) == 0x300000)) { + matroxfb_g450_setclk(minfo, minfo->values.pll.video, M_VIDEO_PLL); } else { unsigned long flags; unsigned int pwr; matroxfb_DAC_lock_irqsave(flags); - pwr = inDAC1064(PMINFO M1064_XPWRCTRL) & ~0x02; - outDAC1064(PMINFO M1064_XPWRCTRL, pwr); + pwr = inDAC1064(minfo, M1064_XPWRCTRL) & ~0x02; + outDAC1064(minfo, M1064_XPWRCTRL, pwr); matroxfb_DAC_unlock_irqrestore(flags); } - matroxfb_g450_setclk(PMINFO ACCESS_FBINFO(values.pll.system), M_SYSTEM_PLL); + matroxfb_g450_setclk(minfo, minfo->values.pll.system, M_SYSTEM_PLL); /* switch clocks to their real PLL source(s) */ - pci_write_config_dword(ACCESS_FBINFO(pcidev), PCI_OPTION_REG, ACCESS_FBINFO(hw).MXoptionReg | 4); - pci_write_config_dword(ACCESS_FBINFO(pcidev), PCI_OPTION3_REG, ACCESS_FBINFO(values).reg.opt3); - pci_write_config_dword(ACCESS_FBINFO(pcidev), PCI_OPTION_REG, ACCESS_FBINFO(hw).MXoptionReg); + pci_write_config_dword(minfo->pcidev, PCI_OPTION_REG, minfo->hw.MXoptionReg | 4); + pci_write_config_dword(minfo->pcidev, PCI_OPTION3_REG, minfo->values.reg.opt3); + pci_write_config_dword(minfo->pcidev, PCI_OPTION_REG, minfo->hw.MXoptionReg); } -static void g450_memory_init(WPMINFO2) { +static void g450_memory_init(struct matrox_fb_info *minfo) +{ /* disable memory refresh */ - ACCESS_FBINFO(hw).MXoptionReg &= ~0x001F8000; - pci_write_config_dword(ACCESS_FBINFO(pcidev), PCI_OPTION_REG, ACCESS_FBINFO(hw).MXoptionReg); + minfo->hw.MXoptionReg &= ~0x001F8000; + pci_write_config_dword(minfo->pcidev, PCI_OPTION_REG, minfo->hw.MXoptionReg); /* set memory interface parameters */ - ACCESS_FBINFO(hw).MXoptionReg &= ~0x00207E00; - ACCESS_FBINFO(hw).MXoptionReg |= 0x00207E00 & ACCESS_FBINFO(values).reg.opt; - pci_write_config_dword(ACCESS_FBINFO(pcidev), PCI_OPTION_REG, ACCESS_FBINFO(hw).MXoptionReg); - pci_write_config_dword(ACCESS_FBINFO(pcidev), PCI_OPTION2_REG, ACCESS_FBINFO(values).reg.opt2); + minfo->hw.MXoptionReg &= ~0x00207E00; + minfo->hw.MXoptionReg |= 0x00207E00 & minfo->values.reg.opt; + pci_write_config_dword(minfo->pcidev, PCI_OPTION_REG, minfo->hw.MXoptionReg); + pci_write_config_dword(minfo->pcidev, PCI_OPTION2_REG, minfo->values.reg.opt2); - mga_outl(M_CTLWTST, ACCESS_FBINFO(values).reg.mctlwtst); + mga_outl(M_CTLWTST, minfo->values.reg.mctlwtst); /* first set up memory interface with disabled memory interface clocks */ - pci_write_config_dword(ACCESS_FBINFO(pcidev), PCI_MEMMISC_REG, ACCESS_FBINFO(values).reg.memmisc & ~0x80000000U); - mga_outl(M_MEMRDBK, ACCESS_FBINFO(values).reg.memrdbk); - mga_outl(M_MACCESS, ACCESS_FBINFO(values).reg.maccess); + pci_write_config_dword(minfo->pcidev, PCI_MEMMISC_REG, minfo->values.reg.memmisc & ~0x80000000U); + mga_outl(M_MEMRDBK, minfo->values.reg.memrdbk); + mga_outl(M_MACCESS, minfo->values.reg.maccess); /* start memory clocks */ - pci_write_config_dword(ACCESS_FBINFO(pcidev), PCI_MEMMISC_REG, ACCESS_FBINFO(values).reg.memmisc | 0x80000000U); + pci_write_config_dword(minfo->pcidev, PCI_MEMMISC_REG, minfo->values.reg.memmisc | 0x80000000U); udelay(200); - if (ACCESS_FBINFO(values).memory.ddr && (!ACCESS_FBINFO(values).memory.emrswen || !ACCESS_FBINFO(values).memory.dll)) { - mga_outl(M_MEMRDBK, ACCESS_FBINFO(values).reg.memrdbk & ~0x1000); + if (minfo->values.memory.ddr && (!minfo->values.memory.emrswen || !minfo->values.memory.dll)) { + mga_outl(M_MEMRDBK, minfo->values.reg.memrdbk & ~0x1000); } - mga_outl(M_MACCESS, ACCESS_FBINFO(values).reg.maccess | 0x8000); + mga_outl(M_MACCESS, minfo->values.reg.maccess | 0x8000); udelay(200); - ACCESS_FBINFO(hw).MXoptionReg |= 0x001F8000 & ACCESS_FBINFO(values).reg.opt; - pci_write_config_dword(ACCESS_FBINFO(pcidev), PCI_OPTION_REG, ACCESS_FBINFO(hw).MXoptionReg); + minfo->hw.MXoptionReg |= 0x001F8000 & minfo->values.reg.opt; + pci_write_config_dword(minfo->pcidev, PCI_OPTION_REG, minfo->hw.MXoptionReg); /* value is written to memory chips only if old != new */ mga_outl(M_PLNWT, 0); mga_outl(M_PLNWT, ~0); - if (ACCESS_FBINFO(values).reg.mctlwtst != ACCESS_FBINFO(values).reg.mctlwtst_core) { - mga_outl(M_CTLWTST, ACCESS_FBINFO(values).reg.mctlwtst_core); + if (minfo->values.reg.mctlwtst != minfo->values.reg.mctlwtst_core) { + mga_outl(M_CTLWTST, minfo->values.reg.mctlwtst_core); } } -static void g450_preinit(WPMINFO2) { +static void g450_preinit(struct matrox_fb_info *minfo) +{ u_int32_t c2ctl; u_int8_t curctl; u_int8_t c1ctl; - /* ACCESS_FBINFO(hw).MXoptionReg = minfo->values.reg.opt; */ - ACCESS_FBINFO(hw).MXoptionReg &= 0xC0000100; - ACCESS_FBINFO(hw).MXoptionReg |= 0x00000020; - if (ACCESS_FBINFO(devflags.novga)) - ACCESS_FBINFO(hw).MXoptionReg &= ~0x00000100; - if (ACCESS_FBINFO(devflags.nobios)) - ACCESS_FBINFO(hw).MXoptionReg &= ~0x40000000; - if (ACCESS_FBINFO(devflags.nopciretry)) - ACCESS_FBINFO(hw).MXoptionReg |= 0x20000000; - ACCESS_FBINFO(hw).MXoptionReg |= ACCESS_FBINFO(values).reg.opt & 0x03400040; - pci_write_config_dword(ACCESS_FBINFO(pcidev), PCI_OPTION_REG, ACCESS_FBINFO(hw).MXoptionReg); + /* minfo->hw.MXoptionReg = minfo->values.reg.opt; */ + minfo->hw.MXoptionReg &= 0xC0000100; + minfo->hw.MXoptionReg |= 0x00000020; + if (minfo->devflags.novga) + minfo->hw.MXoptionReg &= ~0x00000100; + if (minfo->devflags.nobios) + minfo->hw.MXoptionReg &= ~0x40000000; + if (minfo->devflags.nopciretry) + minfo->hw.MXoptionReg |= 0x20000000; + minfo->hw.MXoptionReg |= minfo->values.reg.opt & 0x03400040; + pci_write_config_dword(minfo->pcidev, PCI_OPTION_REG, minfo->hw.MXoptionReg); /* Init system clocks */ @@ -783,24 +809,24 @@ static void g450_preinit(WPMINFO2) { c2ctl = mga_inl(M_C2CTL); mga_outl(M_C2CTL, c2ctl & ~1); /* stop cursor */ - curctl = inDAC1064(PMINFO M1064_XCURCTRL); - outDAC1064(PMINFO M1064_XCURCTRL, 0); + curctl = inDAC1064(minfo, M1064_XCURCTRL); + outDAC1064(minfo, M1064_XCURCTRL, 0); /* stop crtc1 */ c1ctl = mga_readr(M_SEQ_INDEX, 1); mga_setr(M_SEQ_INDEX, 1, c1ctl | 0x20); - g450_mclk_init(PMINFO2); - g450_memory_init(PMINFO2); + g450_mclk_init(minfo); + g450_memory_init(minfo); /* set legacy VGA clock sources for DOSEmu or VMware... */ - matroxfb_g450_setclk(PMINFO 25175, M_PIXEL_PLL_A); - matroxfb_g450_setclk(PMINFO 28322, M_PIXEL_PLL_B); + matroxfb_g450_setclk(minfo, 25175, M_PIXEL_PLL_A); + matroxfb_g450_setclk(minfo, 28322, M_PIXEL_PLL_B); /* restore crtc1 */ mga_setr(M_SEQ_INDEX, 1, c1ctl); /* restore cursor */ - outDAC1064(PMINFO M1064_XCURCTRL, curctl); + outDAC1064(minfo, M1064_XCURCTRL, curctl); /* restore crtc2 */ mga_outl(M_C2CTL, c2ctl); @@ -808,11 +834,12 @@ static void g450_preinit(WPMINFO2) { return; } -static int MGAG100_preinit(WPMINFO2) { +static int MGAG100_preinit(struct matrox_fb_info *minfo) +{ static const int vxres_g100[] = { 512, 640, 768, 800, 832, 960, 1024, 1152, 1280, 1600, 1664, 1920, 2048, 0}; - struct matrox_hw_state* hw = &ACCESS_FBINFO(hw); + struct matrox_hw_state *hw = &minfo->hw; u_int32_t reg50; #if 0 @@ -822,68 +849,68 @@ static int MGAG100_preinit(WPMINFO2) { DBG(__func__) /* there are some instabilities if in_div > 19 && vco < 61000 */ - if (ACCESS_FBINFO(devflags.g450dac)) { - ACCESS_FBINFO(features.pll.vco_freq_min) = 130000; /* my sample: >118 */ + if (minfo->devflags.g450dac) { + minfo->features.pll.vco_freq_min = 130000; /* my sample: >118 */ } else { - ACCESS_FBINFO(features.pll.vco_freq_min) = 62000; + minfo->features.pll.vco_freq_min = 62000; } - if (!ACCESS_FBINFO(features.pll.ref_freq)) { - ACCESS_FBINFO(features.pll.ref_freq) = 27000; + if (!minfo->features.pll.ref_freq) { + minfo->features.pll.ref_freq = 27000; } - ACCESS_FBINFO(features.pll.feed_div_min) = 7; - ACCESS_FBINFO(features.pll.feed_div_max) = 127; - ACCESS_FBINFO(features.pll.in_div_min) = 1; - ACCESS_FBINFO(features.pll.in_div_max) = 31; - ACCESS_FBINFO(features.pll.post_shift_max) = 3; - ACCESS_FBINFO(features.DAC1064.xvrefctrl) = DAC1064_XVREFCTRL_G100_DEFAULT; - /* ACCESS_FBINFO(capable.cfb4) = 0; ... preinitialized by 0 */ - ACCESS_FBINFO(capable.text) = 1; - ACCESS_FBINFO(capable.vxres) = vxres_g100; - ACCESS_FBINFO(capable.plnwt) = ACCESS_FBINFO(devflags.accelerator) == FB_ACCEL_MATROX_MGAG100 - ? ACCESS_FBINFO(devflags.sgram) : 1; + minfo->features.pll.feed_div_min = 7; + minfo->features.pll.feed_div_max = 127; + minfo->features.pll.in_div_min = 1; + minfo->features.pll.in_div_max = 31; + minfo->features.pll.post_shift_max = 3; + minfo->features.DAC1064.xvrefctrl = DAC1064_XVREFCTRL_G100_DEFAULT; + /* minfo->capable.cfb4 = 0; ... preinitialized by 0 */ + minfo->capable.text = 1; + minfo->capable.vxres = vxres_g100; + minfo->capable.plnwt = minfo->devflags.accelerator == FB_ACCEL_MATROX_MGAG100 + ? minfo->devflags.sgram : 1; #ifdef CONFIG_FB_MATROX_G - if (ACCESS_FBINFO(devflags.g450dac)) { - ACCESS_FBINFO(outputs[0]).output = &g450out; + if (minfo->devflags.g450dac) { + minfo->outputs[0].output = &g450out; } else #endif { - ACCESS_FBINFO(outputs[0]).output = &m1064; + minfo->outputs[0].output = &m1064; } - ACCESS_FBINFO(outputs[0]).src = ACCESS_FBINFO(outputs[0]).default_src; - ACCESS_FBINFO(outputs[0]).data = MINFO; - ACCESS_FBINFO(outputs[0]).mode = MATROXFB_OUTPUT_MODE_MONITOR; + minfo->outputs[0].src = minfo->outputs[0].default_src; + minfo->outputs[0].data = minfo; + minfo->outputs[0].mode = MATROXFB_OUTPUT_MODE_MONITOR; - if (ACCESS_FBINFO(devflags.g450dac)) { + if (minfo->devflags.g450dac) { /* we must do this always, BIOS does not do it for us and accelerator dies without it */ mga_outl(0x1C0C, 0); } - if (ACCESS_FBINFO(devflags.noinit)) + if (minfo->devflags.noinit) return 0; - if (ACCESS_FBINFO(devflags.g450dac)) { - g450_preinit(PMINFO2); + if (minfo->devflags.g450dac) { + g450_preinit(minfo); return 0; } hw->MXoptionReg &= 0xC0000100; hw->MXoptionReg |= 0x00000020; - if (ACCESS_FBINFO(devflags.novga)) + if (minfo->devflags.novga) hw->MXoptionReg &= ~0x00000100; - if (ACCESS_FBINFO(devflags.nobios)) + if (minfo->devflags.nobios) hw->MXoptionReg &= ~0x40000000; - if (ACCESS_FBINFO(devflags.nopciretry)) + if (minfo->devflags.nopciretry) hw->MXoptionReg |= 0x20000000; - pci_write_config_dword(ACCESS_FBINFO(pcidev), PCI_OPTION_REG, hw->MXoptionReg); - DAC1064_setmclk(PMINFO DAC1064_OPT_MDIV2 | DAC1064_OPT_GDIV3 | DAC1064_OPT_SCLK_PCI, 133333); + pci_write_config_dword(minfo->pcidev, PCI_OPTION_REG, hw->MXoptionReg); + DAC1064_setmclk(minfo, DAC1064_OPT_MDIV2 | DAC1064_OPT_GDIV3 | DAC1064_OPT_SCLK_PCI, 133333); - if (ACCESS_FBINFO(devflags.accelerator) == FB_ACCEL_MATROX_MGAG100) { - pci_read_config_dword(ACCESS_FBINFO(pcidev), PCI_OPTION2_REG, ®50); + if (minfo->devflags.accelerator == FB_ACCEL_MATROX_MGAG100) { + pci_read_config_dword(minfo->pcidev, PCI_OPTION2_REG, ®50); reg50 &= ~0x3000; - pci_write_config_dword(ACCESS_FBINFO(pcidev), PCI_OPTION2_REG, reg50); + pci_write_config_dword(minfo->pcidev, PCI_OPTION2_REG, reg50); hw->MXoptionReg |= 0x1080; - pci_write_config_dword(ACCESS_FBINFO(pcidev), PCI_OPTION_REG, hw->MXoptionReg); - mga_outl(M_CTLWTST, ACCESS_FBINFO(values).reg.mctlwtst); + pci_write_config_dword(minfo->pcidev, PCI_OPTION_REG, hw->MXoptionReg); + mga_outl(M_CTLWTST, minfo->values.reg.mctlwtst); udelay(100); mga_outb(0x1C05, 0x00); mga_outb(0x1C05, 0x80); @@ -893,68 +920,69 @@ static int MGAG100_preinit(WPMINFO2) { udelay(100); reg50 &= ~0xFF; reg50 |= 0x07; - pci_write_config_dword(ACCESS_FBINFO(pcidev), PCI_OPTION2_REG, reg50); + pci_write_config_dword(minfo->pcidev, PCI_OPTION2_REG, reg50); /* it should help with G100 */ mga_outb(M_GRAPHICS_INDEX, 6); mga_outb(M_GRAPHICS_DATA, (mga_inb(M_GRAPHICS_DATA) & 3) | 4); mga_setr(M_EXTVGA_INDEX, 0x03, 0x81); mga_setr(M_EXTVGA_INDEX, 0x04, 0x00); - mga_writeb(ACCESS_FBINFO(video.vbase), 0x0000, 0xAA); - mga_writeb(ACCESS_FBINFO(video.vbase), 0x0800, 0x55); - mga_writeb(ACCESS_FBINFO(video.vbase), 0x4000, 0x55); + mga_writeb(minfo->video.vbase, 0x0000, 0xAA); + mga_writeb(minfo->video.vbase, 0x0800, 0x55); + mga_writeb(minfo->video.vbase, 0x4000, 0x55); #if 0 - if (mga_readb(ACCESS_FBINFO(video.vbase), 0x0000) != 0xAA) { + if (mga_readb(minfo->video.vbase, 0x0000) != 0xAA) { hw->MXoptionReg &= ~0x1000; } #endif hw->MXoptionReg |= 0x00078020; - } else if (ACCESS_FBINFO(devflags.accelerator) == FB_ACCEL_MATROX_MGAG200) { - pci_read_config_dword(ACCESS_FBINFO(pcidev), PCI_OPTION2_REG, ®50); + } else if (minfo->devflags.accelerator == FB_ACCEL_MATROX_MGAG200) { + pci_read_config_dword(minfo->pcidev, PCI_OPTION2_REG, ®50); reg50 &= ~0x3000; - pci_write_config_dword(ACCESS_FBINFO(pcidev), PCI_OPTION2_REG, reg50); + pci_write_config_dword(minfo->pcidev, PCI_OPTION2_REG, reg50); - if (ACCESS_FBINFO(devflags.memtype) == -1) - hw->MXoptionReg |= ACCESS_FBINFO(values).reg.opt & 0x1C00; + if (minfo->devflags.memtype == -1) + hw->MXoptionReg |= minfo->values.reg.opt & 0x1C00; else - hw->MXoptionReg |= (ACCESS_FBINFO(devflags.memtype) & 7) << 10; - if (ACCESS_FBINFO(devflags.sgram)) + hw->MXoptionReg |= (minfo->devflags.memtype & 7) << 10; + if (minfo->devflags.sgram) hw->MXoptionReg |= 0x4000; - mga_outl(M_CTLWTST, ACCESS_FBINFO(values).reg.mctlwtst); - mga_outl(M_MEMRDBK, ACCESS_FBINFO(values).reg.memrdbk); + mga_outl(M_CTLWTST, minfo->values.reg.mctlwtst); + mga_outl(M_MEMRDBK, minfo->values.reg.memrdbk); udelay(200); mga_outl(M_MACCESS, 0x00000000); mga_outl(M_MACCESS, 0x00008000); udelay(100); - mga_outw(M_MEMRDBK, ACCESS_FBINFO(values).reg.memrdbk); + mga_outw(M_MEMRDBK, minfo->values.reg.memrdbk); hw->MXoptionReg |= 0x00078020; } else { - pci_read_config_dword(ACCESS_FBINFO(pcidev), PCI_OPTION2_REG, ®50); + pci_read_config_dword(minfo->pcidev, PCI_OPTION2_REG, ®50); reg50 &= ~0x00000100; reg50 |= 0x00000000; - pci_write_config_dword(ACCESS_FBINFO(pcidev), PCI_OPTION2_REG, reg50); + pci_write_config_dword(minfo->pcidev, PCI_OPTION2_REG, reg50); - if (ACCESS_FBINFO(devflags.memtype) == -1) - hw->MXoptionReg |= ACCESS_FBINFO(values).reg.opt & 0x1C00; + if (minfo->devflags.memtype == -1) + hw->MXoptionReg |= minfo->values.reg.opt & 0x1C00; else - hw->MXoptionReg |= (ACCESS_FBINFO(devflags.memtype) & 7) << 10; - if (ACCESS_FBINFO(devflags.sgram)) + hw->MXoptionReg |= (minfo->devflags.memtype & 7) << 10; + if (minfo->devflags.sgram) hw->MXoptionReg |= 0x4000; - mga_outl(M_CTLWTST, ACCESS_FBINFO(values).reg.mctlwtst); - mga_outl(M_MEMRDBK, ACCESS_FBINFO(values).reg.memrdbk); + mga_outl(M_CTLWTST, minfo->values.reg.mctlwtst); + mga_outl(M_MEMRDBK, minfo->values.reg.memrdbk); udelay(200); mga_outl(M_MACCESS, 0x00000000); mga_outl(M_MACCESS, 0x00008000); udelay(100); - mga_outl(M_MEMRDBK, ACCESS_FBINFO(values).reg.memrdbk); + mga_outl(M_MEMRDBK, minfo->values.reg.memrdbk); hw->MXoptionReg |= 0x00040020; } - pci_write_config_dword(ACCESS_FBINFO(pcidev), PCI_OPTION_REG, hw->MXoptionReg); + pci_write_config_dword(minfo->pcidev, PCI_OPTION_REG, hw->MXoptionReg); return 0; } -static void MGAG100_reset(WPMINFO2) { +static void MGAG100_reset(struct matrox_fb_info *minfo) +{ u_int8_t b; - struct matrox_hw_state* hw = &ACCESS_FBINFO(hw); + struct matrox_hw_state *hw = &minfo->hw; DBG(__func__) @@ -964,54 +992,55 @@ static void MGAG100_reset(WPMINFO2) { find 1014/22 (IBM/82351); /* if found and bridging Matrox, do some strange stuff */ pci_read_config_byte(ibm, PCI_SECONDARY_BUS, &b); - if (b == ACCESS_FBINFO(pcidev)->bus->number) { + if (b == minfo->pcidev->bus->number) { pci_write_config_byte(ibm, PCI_COMMAND+1, 0); /* disable back-to-back & SERR */ pci_write_config_byte(ibm, 0x41, 0xF4); /* ??? */ pci_write_config_byte(ibm, PCI_IO_BASE, 0xF0); /* ??? */ pci_write_config_byte(ibm, PCI_IO_LIMIT, 0x00); /* ??? */ } #endif - if (!ACCESS_FBINFO(devflags.noinit)) { + if (!minfo->devflags.noinit) { if (x7AF4 & 8) { hw->MXoptionReg |= 0x40; /* FIXME... */ - pci_write_config_dword(ACCESS_FBINFO(pcidev), PCI_OPTION_REG, hw->MXoptionReg); + pci_write_config_dword(minfo->pcidev, PCI_OPTION_REG, hw->MXoptionReg); } mga_setr(M_EXTVGA_INDEX, 0x06, 0x00); } } - if (ACCESS_FBINFO(devflags.g450dac)) { + if (minfo->devflags.g450dac) { /* either leave MCLK as is... or they were set in preinit */ - hw->DACclk[3] = inDAC1064(PMINFO DAC1064_XSYSPLLM); - hw->DACclk[4] = inDAC1064(PMINFO DAC1064_XSYSPLLN); - hw->DACclk[5] = inDAC1064(PMINFO DAC1064_XSYSPLLP); + hw->DACclk[3] = inDAC1064(minfo, DAC1064_XSYSPLLM); + hw->DACclk[4] = inDAC1064(minfo, DAC1064_XSYSPLLN); + hw->DACclk[5] = inDAC1064(minfo, DAC1064_XSYSPLLP); } else { - DAC1064_setmclk(PMINFO DAC1064_OPT_RESERVED | DAC1064_OPT_MDIV2 | DAC1064_OPT_GDIV1 | DAC1064_OPT_SCLK_PLL, 133333); + DAC1064_setmclk(minfo, DAC1064_OPT_RESERVED | DAC1064_OPT_MDIV2 | DAC1064_OPT_GDIV1 | DAC1064_OPT_SCLK_PLL, 133333); } - if (ACCESS_FBINFO(devflags.accelerator) == FB_ACCEL_MATROX_MGAG400) { - if (ACCESS_FBINFO(devflags.dfp_type) == -1) { - ACCESS_FBINFO(devflags.dfp_type) = inDAC1064(PMINFO 0x1F); + if (minfo->devflags.accelerator == FB_ACCEL_MATROX_MGAG400) { + if (minfo->devflags.dfp_type == -1) { + minfo->devflags.dfp_type = inDAC1064(minfo, 0x1F); } } - if (ACCESS_FBINFO(devflags.noinit)) + if (minfo->devflags.noinit) return; - if (ACCESS_FBINFO(devflags.g450dac)) { + if (minfo->devflags.g450dac) { } else { - MGAG100_setPixClock(PMINFO 4, 25175); - MGAG100_setPixClock(PMINFO 5, 28322); + MGAG100_setPixClock(minfo, 4, 25175); + MGAG100_setPixClock(minfo, 5, 28322); if (x7AF4 & 0x10) { - b = inDAC1064(PMINFO M1064_XGENIODATA) & ~1; - outDAC1064(PMINFO M1064_XGENIODATA, b); - b = inDAC1064(PMINFO M1064_XGENIOCTRL) | 1; - outDAC1064(PMINFO M1064_XGENIOCTRL, b); + b = inDAC1064(minfo, M1064_XGENIODATA) & ~1; + outDAC1064(minfo, M1064_XGENIODATA, b); + b = inDAC1064(minfo, M1064_XGENIOCTRL) | 1; + outDAC1064(minfo, M1064_XGENIOCTRL, b); } } } #endif #ifdef CONFIG_FB_MATROX_MYSTIQUE -static void MGA1064_restore(WPMINFO2) { +static void MGA1064_restore(struct matrox_fb_info *minfo) +{ int i; - struct matrox_hw_state* hw = &ACCESS_FBINFO(hw); + struct matrox_hw_state *hw = &minfo->hw; CRITFLAGS @@ -1019,25 +1048,26 @@ static void MGA1064_restore(WPMINFO2) { CRITBEGIN - pci_write_config_dword(ACCESS_FBINFO(pcidev), PCI_OPTION_REG, hw->MXoptionReg); + pci_write_config_dword(minfo->pcidev, PCI_OPTION_REG, hw->MXoptionReg); mga_outb(M_IEN, 0x00); mga_outb(M_CACHEFLUSH, 0x00); CRITEND - DAC1064_restore_1(PMINFO2); - matroxfb_vgaHWrestore(PMINFO2); - ACCESS_FBINFO(crtc1.panpos) = -1; + DAC1064_restore_1(minfo); + matroxfb_vgaHWrestore(minfo); + minfo->crtc1.panpos = -1; for (i = 0; i < 6; i++) mga_setr(M_EXTVGA_INDEX, i, hw->CRTCEXT[i]); - DAC1064_restore_2(PMINFO2); + DAC1064_restore_2(minfo); } #endif #ifdef CONFIG_FB_MATROX_G -static void MGAG100_restore(WPMINFO2) { +static void MGAG100_restore(struct matrox_fb_info *minfo) +{ int i; - struct matrox_hw_state* hw = &ACCESS_FBINFO(hw); + struct matrox_hw_state *hw = &minfo->hw; CRITFLAGS @@ -1045,19 +1075,17 @@ static void MGAG100_restore(WPMINFO2) { CRITBEGIN - pci_write_config_dword(ACCESS_FBINFO(pcidev), PCI_OPTION_REG, hw->MXoptionReg); + pci_write_config_dword(minfo->pcidev, PCI_OPTION_REG, hw->MXoptionReg); CRITEND - DAC1064_restore_1(PMINFO2); - matroxfb_vgaHWrestore(PMINFO2); -#ifdef CONFIG_FB_MATROX_32MB - if (ACCESS_FBINFO(devflags.support32MB)) + DAC1064_restore_1(minfo); + matroxfb_vgaHWrestore(minfo); + if (minfo->devflags.support32MB) mga_setr(M_EXTVGA_INDEX, 8, hw->CRTCEXT[8]); -#endif - ACCESS_FBINFO(crtc1.panpos) = -1; + minfo->crtc1.panpos = -1; for (i = 0; i < 6; i++) mga_setr(M_EXTVGA_INDEX, i, hw->CRTCEXT[i]); - DAC1064_restore_2(PMINFO2); + DAC1064_restore_2(minfo); } #endif diff --git a/drivers/video/matrox/matroxfb_DAC1064.h b/drivers/video/matrox/matroxfb_DAC1064.h index 7a98ce8043d7..c6ed7801efe2 100644 --- a/drivers/video/matrox/matroxfb_DAC1064.h +++ b/drivers/video/matrox/matroxfb_DAC1064.h @@ -11,8 +11,8 @@ extern struct matrox_switch matrox_mystique; extern struct matrox_switch matrox_G100; #endif #ifdef NEED_DAC1064 -void DAC1064_global_init(WPMINFO2); -void DAC1064_global_restore(WPMINFO2); +void DAC1064_global_init(struct matrox_fb_info *minfo); +void DAC1064_global_restore(struct matrox_fb_info *minfo); #endif #define M1064_INDEX 0x00 diff --git a/drivers/video/matrox/matroxfb_Ti3026.c b/drivers/video/matrox/matroxfb_Ti3026.c index 4e825112a601..835aaaae6b96 100644 --- a/drivers/video/matrox/matroxfb_Ti3026.c +++ b/drivers/video/matrox/matroxfb_Ti3026.c @@ -279,27 +279,31 @@ static const unsigned char MGADACbpp32[] = TVP3026_XCOLKEYCTRL_ZOOM1, 0x00, 0x00, TVP3026_XCURCTRL_DIS }; -static int Ti3026_calcclock(CPMINFO unsigned int freq, unsigned int fmax, int* in, int* feed, int* post) { +static int Ti3026_calcclock(const struct matrox_fb_info *minfo, + unsigned int freq, unsigned int fmax, int *in, + int *feed, int *post) +{ unsigned int fvco; unsigned int lin, lfeed, lpost; DBG(__func__) - fvco = PLL_calcclock(PMINFO freq, fmax, &lin, &lfeed, &lpost); + fvco = PLL_calcclock(minfo, freq, fmax, &lin, &lfeed, &lpost); fvco >>= (*post = lpost); *in = 64 - lin; *feed = 64 - lfeed; return fvco; } -static int Ti3026_setpclk(WPMINFO int clk) { +static int Ti3026_setpclk(struct matrox_fb_info *minfo, int clk) +{ unsigned int f_pll; unsigned int pixfeed, pixin, pixpost; - struct matrox_hw_state* hw = &ACCESS_FBINFO(hw); + struct matrox_hw_state *hw = &minfo->hw; DBG(__func__) - f_pll = Ti3026_calcclock(PMINFO clk, ACCESS_FBINFO(max_pixel_clock), &pixin, &pixfeed, &pixpost); + f_pll = Ti3026_calcclock(minfo, clk, minfo->max_pixel_clock, &pixin, &pixfeed, &pixpost); hw->DACclk[0] = pixin | 0xC0; hw->DACclk[1] = pixfeed; @@ -309,9 +313,9 @@ static int Ti3026_setpclk(WPMINFO int clk) { unsigned int loopfeed, loopin, looppost, loopdiv, z; unsigned int Bpp; - Bpp = ACCESS_FBINFO(curr.final_bppShift); + Bpp = minfo->curr.final_bppShift; - if (ACCESS_FBINFO(fbcon).var.bits_per_pixel == 24) { + if (minfo->fbcon.var.bits_per_pixel == 24) { loopfeed = 3; /* set lm to any possible value */ loopin = 3 * 32 / Bpp; } else { @@ -330,18 +334,18 @@ static int Ti3026_setpclk(WPMINFO int clk) { looppost = 3; loopdiv = z/16; } - if (ACCESS_FBINFO(fbcon).var.bits_per_pixel == 24) { + if (minfo->fbcon.var.bits_per_pixel == 24) { hw->DACclk[3] = ((65 - loopin) & 0x3F) | 0xC0; hw->DACclk[4] = (65 - loopfeed) | 0x80; - if (ACCESS_FBINFO(accel.ramdac_rev) > 0x20) { - if (isInterleave(MINFO)) + if (minfo->accel.ramdac_rev > 0x20) { + if (isInterleave(minfo)) hw->DACreg[POS3026_XLATCHCTRL] = TVP3026B_XLATCHCTRL_8_3; else { hw->DACclk[4] &= ~0xC0; hw->DACreg[POS3026_XLATCHCTRL] = TVP3026B_XLATCHCTRL_4_3; } } else { - if (isInterleave(MINFO)) + if (isInterleave(minfo)) ; /* default... */ else { hw->DACclk[4] ^= 0xC0; /* change from 0x80 to 0x40 */ @@ -349,7 +353,7 @@ static int Ti3026_setpclk(WPMINFO int clk) { } } hw->DACclk[5] = looppost | 0xF8; - if (ACCESS_FBINFO(devflags.mga_24bpp_fix)) + if (minfo->devflags.mga_24bpp_fix) hw->DACclk[5] ^= 0x40; } else { hw->DACclk[3] = ((65 - loopin) & 0x3F) | 0xC0; @@ -361,14 +365,15 @@ static int Ti3026_setpclk(WPMINFO int clk) { return 0; } -static int Ti3026_init(WPMINFO struct my_timming* m) { - u_int8_t muxctrl = isInterleave(MINFO) ? TVP3026_XMUXCTRL_MEMORY_64BIT : TVP3026_XMUXCTRL_MEMORY_32BIT; - struct matrox_hw_state* hw = &ACCESS_FBINFO(hw); +static int Ti3026_init(struct matrox_fb_info *minfo, struct my_timming *m) +{ + u_int8_t muxctrl = isInterleave(minfo) ? TVP3026_XMUXCTRL_MEMORY_64BIT : TVP3026_XMUXCTRL_MEMORY_32BIT; + struct matrox_hw_state *hw = &minfo->hw; DBG(__func__) memcpy(hw->DACreg, MGADACbpp32, sizeof(hw->DACreg)); - switch (ACCESS_FBINFO(fbcon).var.bits_per_pixel) { + switch (minfo->fbcon.var.bits_per_pixel) { case 4: hw->DACreg[POS3026_XLATCHCTRL] = TVP3026_XLATCHCTRL_16_1; /* or _8_1, they are same */ hw->DACreg[POS3026_XTRUECOLORCTRL] = TVP3026_XTRUECOLORCTRL_PSEUDOCOLOR; hw->DACreg[POS3026_XMUXCTRL] = muxctrl | TVP3026_XMUXCTRL_PIXEL_4BIT; @@ -383,7 +388,7 @@ static int Ti3026_init(WPMINFO struct my_timming* m) { break; case 16: /* XLATCHCTRL should be _4_1 / _2_1... Why is not? (_2_1 is used everytime) */ - hw->DACreg[POS3026_XTRUECOLORCTRL] = (ACCESS_FBINFO(fbcon).var.green.length == 5)? (TVP3026_XTRUECOLORCTRL_DIRECTCOLOR | TVP3026_XTRUECOLORCTRL_ORGB_1555 ) : (TVP3026_XTRUECOLORCTRL_DIRECTCOLOR | TVP3026_XTRUECOLORCTRL_RGB_565); + hw->DACreg[POS3026_XTRUECOLORCTRL] = (minfo->fbcon.var.green.length == 5) ? (TVP3026_XTRUECOLORCTRL_DIRECTCOLOR | TVP3026_XTRUECOLORCTRL_ORGB_1555) : (TVP3026_XTRUECOLORCTRL_DIRECTCOLOR | TVP3026_XTRUECOLORCTRL_RGB_565); hw->DACreg[POS3026_XMUXCTRL] = muxctrl | TVP3026_XMUXCTRL_PIXEL_16BIT; hw->DACreg[POS3026_XCLKCTRL] = TVP3026_XCLKCTRL_SRC_PLL | TVP3026_XCLKCTRL_DIV2; break; @@ -400,7 +405,7 @@ static int Ti3026_init(WPMINFO struct my_timming* m) { default: return 1; /* TODO: failed */ } - if (matroxfb_vgaHWinit(PMINFO m)) return 1; + if (matroxfb_vgaHWinit(minfo, m)) return 1; /* set SYNC */ hw->MiscOutReg = 0xCB; @@ -412,9 +417,9 @@ static int Ti3026_init(WPMINFO struct my_timming* m) { hw->DACreg[POS3026_XGENCTRL] |= TVP3026_XGENCTRL_SYNC_ON_GREEN; /* set DELAY */ - if (ACCESS_FBINFO(video.len) < 0x400000) + if (minfo->video.len < 0x400000) hw->CRTCEXT[3] |= 0x08; - else if (ACCESS_FBINFO(video.len) > 0x400000) + else if (minfo->video.len > 0x400000) hw->CRTCEXT[3] |= 0x10; /* set HWCURSOR */ @@ -426,14 +431,15 @@ static int Ti3026_init(WPMINFO struct my_timming* m) { /* set interleaving */ hw->MXoptionReg &= ~0x00001000; - if (isInterleave(MINFO)) hw->MXoptionReg |= 0x00001000; + if (isInterleave(minfo)) hw->MXoptionReg |= 0x00001000; /* set DAC */ - Ti3026_setpclk(PMINFO m->pixclock); + Ti3026_setpclk(minfo, m->pixclock); return 0; } -static void ti3026_setMCLK(WPMINFO int fout){ +static void ti3026_setMCLK(struct matrox_fb_info *minfo, int fout) +{ unsigned int f_pll; unsigned int pclk_m, pclk_n, pclk_p; unsigned int mclk_m, mclk_n, mclk_p; @@ -442,29 +448,29 @@ static void ti3026_setMCLK(WPMINFO int fout){ DBG(__func__) - f_pll = Ti3026_calcclock(PMINFO fout, ACCESS_FBINFO(max_pixel_clock), &mclk_n, &mclk_m, &mclk_p); + f_pll = Ti3026_calcclock(minfo, fout, minfo->max_pixel_clock, &mclk_n, &mclk_m, &mclk_p); /* save pclk */ - outTi3026(PMINFO TVP3026_XPLLADDR, 0xFC); - pclk_n = inTi3026(PMINFO TVP3026_XPIXPLLDATA); - outTi3026(PMINFO TVP3026_XPLLADDR, 0xFD); - pclk_m = inTi3026(PMINFO TVP3026_XPIXPLLDATA); - outTi3026(PMINFO TVP3026_XPLLADDR, 0xFE); - pclk_p = inTi3026(PMINFO TVP3026_XPIXPLLDATA); + outTi3026(minfo, TVP3026_XPLLADDR, 0xFC); + pclk_n = inTi3026(minfo, TVP3026_XPIXPLLDATA); + outTi3026(minfo, TVP3026_XPLLADDR, 0xFD); + pclk_m = inTi3026(minfo, TVP3026_XPIXPLLDATA); + outTi3026(minfo, TVP3026_XPLLADDR, 0xFE); + pclk_p = inTi3026(minfo, TVP3026_XPIXPLLDATA); /* stop pclk */ - outTi3026(PMINFO TVP3026_XPLLADDR, 0xFE); - outTi3026(PMINFO TVP3026_XPIXPLLDATA, 0x00); + outTi3026(minfo, TVP3026_XPLLADDR, 0xFE); + outTi3026(minfo, TVP3026_XPIXPLLDATA, 0x00); /* set pclk to new mclk */ - outTi3026(PMINFO TVP3026_XPLLADDR, 0xFC); - outTi3026(PMINFO TVP3026_XPIXPLLDATA, mclk_n | 0xC0); - outTi3026(PMINFO TVP3026_XPIXPLLDATA, mclk_m); - outTi3026(PMINFO TVP3026_XPIXPLLDATA, mclk_p | 0xB0); + outTi3026(minfo, TVP3026_XPLLADDR, 0xFC); + outTi3026(minfo, TVP3026_XPIXPLLDATA, mclk_n | 0xC0); + outTi3026(minfo, TVP3026_XPIXPLLDATA, mclk_m); + outTi3026(minfo, TVP3026_XPIXPLLDATA, mclk_p | 0xB0); /* wait for PLL to lock */ for (tmout = 500000; tmout; tmout--) { - if (inTi3026(PMINFO TVP3026_XPIXPLLDATA) & 0x40) + if (inTi3026(minfo, TVP3026_XPIXPLLDATA) & 0x40) break; udelay(10); }; @@ -472,23 +478,23 @@ static void ti3026_setMCLK(WPMINFO int fout){ printk(KERN_ERR "matroxfb: Temporary pixel PLL not locked after 5 secs\n"); /* output pclk on mclk pin */ - mclk_ctl = inTi3026(PMINFO TVP3026_XMEMPLLCTRL); - outTi3026(PMINFO TVP3026_XMEMPLLCTRL, mclk_ctl & 0xE7); - outTi3026(PMINFO TVP3026_XMEMPLLCTRL, (mclk_ctl & 0xE7) | TVP3026_XMEMPLLCTRL_STROBEMKC4); + mclk_ctl = inTi3026(minfo, TVP3026_XMEMPLLCTRL); + outTi3026(minfo, TVP3026_XMEMPLLCTRL, mclk_ctl & 0xE7); + outTi3026(minfo, TVP3026_XMEMPLLCTRL, (mclk_ctl & 0xE7) | TVP3026_XMEMPLLCTRL_STROBEMKC4); /* stop MCLK */ - outTi3026(PMINFO TVP3026_XPLLADDR, 0xFB); - outTi3026(PMINFO TVP3026_XMEMPLLDATA, 0x00); + outTi3026(minfo, TVP3026_XPLLADDR, 0xFB); + outTi3026(minfo, TVP3026_XMEMPLLDATA, 0x00); /* set mclk to new freq */ - outTi3026(PMINFO TVP3026_XPLLADDR, 0xF3); - outTi3026(PMINFO TVP3026_XMEMPLLDATA, mclk_n | 0xC0); - outTi3026(PMINFO TVP3026_XMEMPLLDATA, mclk_m); - outTi3026(PMINFO TVP3026_XMEMPLLDATA, mclk_p | 0xB0); + outTi3026(minfo, TVP3026_XPLLADDR, 0xF3); + outTi3026(minfo, TVP3026_XMEMPLLDATA, mclk_n | 0xC0); + outTi3026(minfo, TVP3026_XMEMPLLDATA, mclk_m); + outTi3026(minfo, TVP3026_XMEMPLLDATA, mclk_p | 0xB0); /* wait for PLL to lock */ for (tmout = 500000; tmout; tmout--) { - if (inTi3026(PMINFO TVP3026_XMEMPLLDATA) & 0x40) + if (inTi3026(minfo, TVP3026_XMEMPLLDATA) & 0x40) break; udelay(10); } @@ -496,7 +502,7 @@ static void ti3026_setMCLK(WPMINFO int fout){ printk(KERN_ERR "matroxfb: Memory PLL not locked after 5 secs\n"); f_pll = f_pll * 333 / (10000 << mclk_p); - if (isMilleniumII(MINFO)) { + if (isMilleniumII(minfo)) { rfhcnt = (f_pll - 128) / 256; if (rfhcnt > 15) rfhcnt = 15; @@ -505,26 +511,26 @@ static void ti3026_setMCLK(WPMINFO int fout){ if (rfhcnt > 15) rfhcnt = 0; } - ACCESS_FBINFO(hw).MXoptionReg = (ACCESS_FBINFO(hw).MXoptionReg & ~0x000F0000) | (rfhcnt << 16); - pci_write_config_dword(ACCESS_FBINFO(pcidev), PCI_OPTION_REG, ACCESS_FBINFO(hw).MXoptionReg); + minfo->hw.MXoptionReg = (minfo->hw.MXoptionReg & ~0x000F0000) | (rfhcnt << 16); + pci_write_config_dword(minfo->pcidev, PCI_OPTION_REG, minfo->hw.MXoptionReg); /* output MCLK to MCLK pin */ - outTi3026(PMINFO TVP3026_XMEMPLLCTRL, (mclk_ctl & 0xE7) | TVP3026_XMEMPLLCTRL_MCLK_MCLKPLL); - outTi3026(PMINFO TVP3026_XMEMPLLCTRL, (mclk_ctl ) | TVP3026_XMEMPLLCTRL_MCLK_MCLKPLL | TVP3026_XMEMPLLCTRL_STROBEMKC4); + outTi3026(minfo, TVP3026_XMEMPLLCTRL, (mclk_ctl & 0xE7) | TVP3026_XMEMPLLCTRL_MCLK_MCLKPLL); + outTi3026(minfo, TVP3026_XMEMPLLCTRL, (mclk_ctl ) | TVP3026_XMEMPLLCTRL_MCLK_MCLKPLL | TVP3026_XMEMPLLCTRL_STROBEMKC4); /* stop PCLK */ - outTi3026(PMINFO TVP3026_XPLLADDR, 0xFE); - outTi3026(PMINFO TVP3026_XPIXPLLDATA, 0x00); + outTi3026(minfo, TVP3026_XPLLADDR, 0xFE); + outTi3026(minfo, TVP3026_XPIXPLLDATA, 0x00); /* restore pclk */ - outTi3026(PMINFO TVP3026_XPLLADDR, 0xFC); - outTi3026(PMINFO TVP3026_XPIXPLLDATA, pclk_n); - outTi3026(PMINFO TVP3026_XPIXPLLDATA, pclk_m); - outTi3026(PMINFO TVP3026_XPIXPLLDATA, pclk_p); + outTi3026(minfo, TVP3026_XPLLADDR, 0xFC); + outTi3026(minfo, TVP3026_XPIXPLLDATA, pclk_n); + outTi3026(minfo, TVP3026_XPIXPLLDATA, pclk_m); + outTi3026(minfo, TVP3026_XPIXPLLDATA, pclk_p); /* wait for PLL to lock */ for (tmout = 500000; tmout; tmout--) { - if (inTi3026(PMINFO TVP3026_XPIXPLLDATA) & 0x40) + if (inTi3026(minfo, TVP3026_XPIXPLLDATA) & 0x40) break; udelay(10); } @@ -532,26 +538,27 @@ static void ti3026_setMCLK(WPMINFO int fout){ printk(KERN_ERR "matroxfb: Pixel PLL not locked after 5 secs\n"); } -static void ti3026_ramdac_init(WPMINFO2) { - +static void ti3026_ramdac_init(struct matrox_fb_info *minfo) +{ DBG(__func__) - ACCESS_FBINFO(features.pll.vco_freq_min) = 110000; - ACCESS_FBINFO(features.pll.ref_freq) = 114545; - ACCESS_FBINFO(features.pll.feed_div_min) = 2; - ACCESS_FBINFO(features.pll.feed_div_max) = 24; - ACCESS_FBINFO(features.pll.in_div_min) = 2; - ACCESS_FBINFO(features.pll.in_div_max) = 63; - ACCESS_FBINFO(features.pll.post_shift_max) = 3; - if (ACCESS_FBINFO(devflags.noinit)) + minfo->features.pll.vco_freq_min = 110000; + minfo->features.pll.ref_freq = 114545; + minfo->features.pll.feed_div_min = 2; + minfo->features.pll.feed_div_max = 24; + minfo->features.pll.in_div_min = 2; + minfo->features.pll.in_div_max = 63; + minfo->features.pll.post_shift_max = 3; + if (minfo->devflags.noinit) return; - ti3026_setMCLK(PMINFO 60000); + ti3026_setMCLK(minfo, 60000); } -static void Ti3026_restore(WPMINFO2) { +static void Ti3026_restore(struct matrox_fb_info *minfo) +{ int i; unsigned char progdac[6]; - struct matrox_hw_state* hw = &ACCESS_FBINFO(hw); + struct matrox_hw_state *hw = &minfo->hw; CRITFLAGS DBG(__func__) @@ -565,31 +572,31 @@ static void Ti3026_restore(WPMINFO2) { CRITBEGIN - pci_write_config_dword(ACCESS_FBINFO(pcidev), PCI_OPTION_REG, hw->MXoptionReg); + pci_write_config_dword(minfo->pcidev, PCI_OPTION_REG, hw->MXoptionReg); CRITEND - matroxfb_vgaHWrestore(PMINFO2); + matroxfb_vgaHWrestore(minfo); CRITBEGIN - ACCESS_FBINFO(crtc1.panpos) = -1; + minfo->crtc1.panpos = -1; for (i = 0; i < 6; i++) mga_setr(M_EXTVGA_INDEX, i, hw->CRTCEXT[i]); for (i = 0; i < 21; i++) { - outTi3026(PMINFO DACseq[i], hw->DACreg[i]); + outTi3026(minfo, DACseq[i], hw->DACreg[i]); } - outTi3026(PMINFO TVP3026_XPLLADDR, 0x00); - progdac[0] = inTi3026(PMINFO TVP3026_XPIXPLLDATA); - progdac[3] = inTi3026(PMINFO TVP3026_XLOOPPLLDATA); - outTi3026(PMINFO TVP3026_XPLLADDR, 0x15); - progdac[1] = inTi3026(PMINFO TVP3026_XPIXPLLDATA); - progdac[4] = inTi3026(PMINFO TVP3026_XLOOPPLLDATA); - outTi3026(PMINFO TVP3026_XPLLADDR, 0x2A); - progdac[2] = inTi3026(PMINFO TVP3026_XPIXPLLDATA); - progdac[5] = inTi3026(PMINFO TVP3026_XLOOPPLLDATA); + outTi3026(minfo, TVP3026_XPLLADDR, 0x00); + progdac[0] = inTi3026(minfo, TVP3026_XPIXPLLDATA); + progdac[3] = inTi3026(minfo, TVP3026_XLOOPPLLDATA); + outTi3026(minfo, TVP3026_XPLLADDR, 0x15); + progdac[1] = inTi3026(minfo, TVP3026_XPIXPLLDATA); + progdac[4] = inTi3026(minfo, TVP3026_XLOOPPLLDATA); + outTi3026(minfo, TVP3026_XPLLADDR, 0x2A); + progdac[2] = inTi3026(minfo, TVP3026_XPIXPLLDATA); + progdac[5] = inTi3026(minfo, TVP3026_XLOOPPLLDATA); CRITEND if (memcmp(hw->DACclk, progdac, 6)) { @@ -598,20 +605,20 @@ static void Ti3026_restore(WPMINFO2) { /* Maybe even we should call schedule() ? */ CRITBEGIN - outTi3026(PMINFO TVP3026_XCLKCTRL, hw->DACreg[POS3026_XCLKCTRL]); - outTi3026(PMINFO TVP3026_XPLLADDR, 0x2A); - outTi3026(PMINFO TVP3026_XLOOPPLLDATA, 0); - outTi3026(PMINFO TVP3026_XPIXPLLDATA, 0); + outTi3026(minfo, TVP3026_XCLKCTRL, hw->DACreg[POS3026_XCLKCTRL]); + outTi3026(minfo, TVP3026_XPLLADDR, 0x2A); + outTi3026(minfo, TVP3026_XLOOPPLLDATA, 0); + outTi3026(minfo, TVP3026_XPIXPLLDATA, 0); - outTi3026(PMINFO TVP3026_XPLLADDR, 0x00); + outTi3026(minfo, TVP3026_XPLLADDR, 0x00); for (i = 0; i < 3; i++) - outTi3026(PMINFO TVP3026_XPIXPLLDATA, hw->DACclk[i]); + outTi3026(minfo, TVP3026_XPIXPLLDATA, hw->DACclk[i]); /* wait for PLL only if PLL clock requested (always for PowerMode, never for VGA) */ if (hw->MiscOutReg & 0x08) { int tmout; - outTi3026(PMINFO TVP3026_XPLLADDR, 0x3F); + outTi3026(minfo, TVP3026_XPLLADDR, 0x3F); for (tmout = 500000; tmout; --tmout) { - if (inTi3026(PMINFO TVP3026_XPIXPLLDATA) & 0x40) + if (inTi3026(minfo, TVP3026_XPIXPLLDATA) & 0x40) break; udelay(10); } @@ -624,18 +631,18 @@ static void Ti3026_restore(WPMINFO2) { dprintk(KERN_INFO "PixelPLL: %d\n", 500000-tmout); CRITBEGIN } - outTi3026(PMINFO TVP3026_XMEMPLLCTRL, hw->DACreg[POS3026_XMEMPLLCTRL]); - outTi3026(PMINFO TVP3026_XPLLADDR, 0x00); + outTi3026(minfo, TVP3026_XMEMPLLCTRL, hw->DACreg[POS3026_XMEMPLLCTRL]); + outTi3026(minfo, TVP3026_XPLLADDR, 0x00); for (i = 3; i < 6; i++) - outTi3026(PMINFO TVP3026_XLOOPPLLDATA, hw->DACclk[i]); + outTi3026(minfo, TVP3026_XLOOPPLLDATA, hw->DACclk[i]); CRITEND if ((hw->MiscOutReg & 0x08) && ((hw->DACclk[5] & 0x80) == 0x80)) { int tmout; CRITBEGIN - outTi3026(PMINFO TVP3026_XPLLADDR, 0x3F); + outTi3026(minfo, TVP3026_XPLLADDR, 0x3F); for (tmout = 500000; tmout; --tmout) { - if (inTi3026(PMINFO TVP3026_XLOOPPLLDATA) & 0x40) + if (inTi3026(minfo, TVP3026_XLOOPPLLDATA) & 0x40) break; udelay(10); } @@ -660,65 +667,66 @@ static void Ti3026_restore(WPMINFO2) { #endif } -static void Ti3026_reset(WPMINFO2) { - +static void Ti3026_reset(struct matrox_fb_info *minfo) +{ DBG(__func__) - ti3026_ramdac_init(PMINFO2); + ti3026_ramdac_init(minfo); } static struct matrox_altout ti3026_output = { .name = "Primary output", }; -static int Ti3026_preinit(WPMINFO2) { +static int Ti3026_preinit(struct matrox_fb_info *minfo) +{ static const int vxres_mill2[] = { 512, 640, 768, 800, 832, 960, 1024, 1152, 1280, 1600, 1664, 1920, 2048, 0}; static const int vxres_mill1[] = { 640, 768, 800, 960, 1024, 1152, 1280, 1600, 1920, 2048, 0}; - struct matrox_hw_state* hw = &ACCESS_FBINFO(hw); + struct matrox_hw_state *hw = &minfo->hw; DBG(__func__) - ACCESS_FBINFO(millenium) = 1; - ACCESS_FBINFO(milleniumII) = (ACCESS_FBINFO(pcidev)->device != PCI_DEVICE_ID_MATROX_MIL); - ACCESS_FBINFO(capable.cfb4) = 1; - ACCESS_FBINFO(capable.text) = 1; /* isMilleniumII(MINFO); */ - ACCESS_FBINFO(capable.vxres) = isMilleniumII(MINFO)?vxres_mill2:vxres_mill1; + minfo->millenium = 1; + minfo->milleniumII = (minfo->pcidev->device != PCI_DEVICE_ID_MATROX_MIL); + minfo->capable.cfb4 = 1; + minfo->capable.text = 1; /* isMilleniumII(minfo); */ + minfo->capable.vxres = isMilleniumII(minfo) ? vxres_mill2 : vxres_mill1; - ACCESS_FBINFO(outputs[0]).data = MINFO; - ACCESS_FBINFO(outputs[0]).output = &ti3026_output; - ACCESS_FBINFO(outputs[0]).src = ACCESS_FBINFO(outputs[0]).default_src; - ACCESS_FBINFO(outputs[0]).mode = MATROXFB_OUTPUT_MODE_MONITOR; + minfo->outputs[0].data = minfo; + minfo->outputs[0].output = &ti3026_output; + minfo->outputs[0].src = minfo->outputs[0].default_src; + minfo->outputs[0].mode = MATROXFB_OUTPUT_MODE_MONITOR; - if (ACCESS_FBINFO(devflags.noinit)) + if (minfo->devflags.noinit) return 0; /* preserve VGA I/O, BIOS and PPC */ hw->MXoptionReg &= 0xC0000100; hw->MXoptionReg |= 0x002C0000; - if (ACCESS_FBINFO(devflags.novga)) + if (minfo->devflags.novga) hw->MXoptionReg &= ~0x00000100; - if (ACCESS_FBINFO(devflags.nobios)) + if (minfo->devflags.nobios) hw->MXoptionReg &= ~0x40000000; - if (ACCESS_FBINFO(devflags.nopciretry)) + if (minfo->devflags.nopciretry) hw->MXoptionReg |= 0x20000000; - pci_write_config_dword(ACCESS_FBINFO(pcidev), PCI_OPTION_REG, hw->MXoptionReg); + pci_write_config_dword(minfo->pcidev, PCI_OPTION_REG, hw->MXoptionReg); - ACCESS_FBINFO(accel.ramdac_rev) = inTi3026(PMINFO TVP3026_XSILICONREV); + minfo->accel.ramdac_rev = inTi3026(minfo, TVP3026_XSILICONREV); - outTi3026(PMINFO TVP3026_XCLKCTRL, TVP3026_XCLKCTRL_SRC_CLK0VGA | TVP3026_XCLKCTRL_CLKSTOPPED); - outTi3026(PMINFO TVP3026_XTRUECOLORCTRL, TVP3026_XTRUECOLORCTRL_PSEUDOCOLOR); - outTi3026(PMINFO TVP3026_XMUXCTRL, TVP3026_XMUXCTRL_VGA); + outTi3026(minfo, TVP3026_XCLKCTRL, TVP3026_XCLKCTRL_SRC_CLK0VGA | TVP3026_XCLKCTRL_CLKSTOPPED); + outTi3026(minfo, TVP3026_XTRUECOLORCTRL, TVP3026_XTRUECOLORCTRL_PSEUDOCOLOR); + outTi3026(minfo, TVP3026_XMUXCTRL, TVP3026_XMUXCTRL_VGA); - outTi3026(PMINFO TVP3026_XPLLADDR, 0x2A); - outTi3026(PMINFO TVP3026_XLOOPPLLDATA, 0x00); - outTi3026(PMINFO TVP3026_XPIXPLLDATA, 0x00); + outTi3026(minfo, TVP3026_XPLLADDR, 0x2A); + outTi3026(minfo, TVP3026_XLOOPPLLDATA, 0x00); + outTi3026(minfo, TVP3026_XPIXPLLDATA, 0x00); mga_outb(M_MISC_REG, 0x67); - outTi3026(PMINFO TVP3026_XMEMPLLCTRL, TVP3026_XMEMPLLCTRL_STROBEMKC4 | TVP3026_XMEMPLLCTRL_MCLK_MCLKPLL); + outTi3026(minfo, TVP3026_XMEMPLLCTRL, TVP3026_XMEMPLLCTRL_STROBEMKC4 | TVP3026_XMEMPLLCTRL_MCLK_MCLKPLL); mga_outl(M_RESET, 1); udelay(250); diff --git a/drivers/video/matrox/matroxfb_accel.c b/drivers/video/matrox/matroxfb_accel.c index 9c3aeee1cc4f..8335a6fe303e 100644 --- a/drivers/video/matrox/matroxfb_accel.c +++ b/drivers/video/matrox/matroxfb_accel.c @@ -81,7 +81,7 @@ #include "matroxfb_Ti3026.h" #include "matroxfb_misc.h" -#define curr_ydstorg(x) ACCESS_FBINFO2(x, curr.ydstorg.pixels) +#define curr_ydstorg(x) ((x)->curr.ydstorg.pixels) #define mga_ydstlen(y,l) mga_outl(M_YDSTLEN | M_EXEC, ((y) << 16) | (l)) @@ -107,7 +107,8 @@ static void matroxfb_imageblit(struct fb_info* info, const struct fb_image* imag static void matroxfb_cfb4_fillrect(struct fb_info* info, const struct fb_fillrect* rect); static void matroxfb_cfb4_copyarea(struct fb_info* info, const struct fb_copyarea* area); -void matrox_cfbX_init(WPMINFO2) { +void matrox_cfbX_init(struct matrox_fb_info *minfo) +{ u_int32_t maccess; u_int32_t mpitch; u_int32_t mopmode; @@ -115,59 +116,59 @@ void matrox_cfbX_init(WPMINFO2) { DBG(__func__) - mpitch = ACCESS_FBINFO(fbcon).var.xres_virtual; + mpitch = minfo->fbcon.var.xres_virtual; - ACCESS_FBINFO(fbops).fb_copyarea = cfb_copyarea; - ACCESS_FBINFO(fbops).fb_fillrect = cfb_fillrect; - ACCESS_FBINFO(fbops).fb_imageblit = cfb_imageblit; - ACCESS_FBINFO(fbops).fb_cursor = NULL; + minfo->fbops.fb_copyarea = cfb_copyarea; + minfo->fbops.fb_fillrect = cfb_fillrect; + minfo->fbops.fb_imageblit = cfb_imageblit; + minfo->fbops.fb_cursor = NULL; - accel = (ACCESS_FBINFO(fbcon).var.accel_flags & FB_ACCELF_TEXT) == FB_ACCELF_TEXT; + accel = (minfo->fbcon.var.accel_flags & FB_ACCELF_TEXT) == FB_ACCELF_TEXT; - switch (ACCESS_FBINFO(fbcon).var.bits_per_pixel) { + switch (minfo->fbcon.var.bits_per_pixel) { case 4: maccess = 0x00000000; /* accelerate as 8bpp video */ mpitch = (mpitch >> 1) | 0x8000; /* disable linearization */ mopmode = M_OPMODE_4BPP; - matrox_cfb4_pal(ACCESS_FBINFO(cmap)); + matrox_cfb4_pal(minfo->cmap); if (accel && !(mpitch & 1)) { - ACCESS_FBINFO(fbops).fb_copyarea = matroxfb_cfb4_copyarea; - ACCESS_FBINFO(fbops).fb_fillrect = matroxfb_cfb4_fillrect; + minfo->fbops.fb_copyarea = matroxfb_cfb4_copyarea; + minfo->fbops.fb_fillrect = matroxfb_cfb4_fillrect; } break; case 8: maccess = 0x00000000; mopmode = M_OPMODE_8BPP; - matrox_cfb8_pal(ACCESS_FBINFO(cmap)); + matrox_cfb8_pal(minfo->cmap); if (accel) { - ACCESS_FBINFO(fbops).fb_copyarea = matroxfb_copyarea; - ACCESS_FBINFO(fbops).fb_fillrect = matroxfb_fillrect; - ACCESS_FBINFO(fbops).fb_imageblit = matroxfb_imageblit; + minfo->fbops.fb_copyarea = matroxfb_copyarea; + minfo->fbops.fb_fillrect = matroxfb_fillrect; + minfo->fbops.fb_imageblit = matroxfb_imageblit; } break; - case 16: if (ACCESS_FBINFO(fbcon).var.green.length == 5) + case 16: if (minfo->fbcon.var.green.length == 5) maccess = 0xC0000001; else maccess = 0x40000001; mopmode = M_OPMODE_16BPP; if (accel) { - ACCESS_FBINFO(fbops).fb_copyarea = matroxfb_copyarea; - ACCESS_FBINFO(fbops).fb_fillrect = matroxfb_fillrect; - ACCESS_FBINFO(fbops).fb_imageblit = matroxfb_imageblit; + minfo->fbops.fb_copyarea = matroxfb_copyarea; + minfo->fbops.fb_fillrect = matroxfb_fillrect; + minfo->fbops.fb_imageblit = matroxfb_imageblit; } break; case 24: maccess = 0x00000003; mopmode = M_OPMODE_24BPP; if (accel) { - ACCESS_FBINFO(fbops).fb_copyarea = matroxfb_copyarea; - ACCESS_FBINFO(fbops).fb_fillrect = matroxfb_fillrect; - ACCESS_FBINFO(fbops).fb_imageblit = matroxfb_imageblit; + minfo->fbops.fb_copyarea = matroxfb_copyarea; + minfo->fbops.fb_fillrect = matroxfb_fillrect; + minfo->fbops.fb_imageblit = matroxfb_imageblit; } break; case 32: maccess = 0x00000002; mopmode = M_OPMODE_32BPP; if (accel) { - ACCESS_FBINFO(fbops).fb_copyarea = matroxfb_copyarea; - ACCESS_FBINFO(fbops).fb_fillrect = matroxfb_fillrect; - ACCESS_FBINFO(fbops).fb_imageblit = matroxfb_imageblit; + minfo->fbops.fb_copyarea = matroxfb_copyarea; + minfo->fbops.fb_fillrect = matroxfb_fillrect; + minfo->fbops.fb_imageblit = matroxfb_imageblit; } break; default: maccess = 0x00000000; @@ -176,10 +177,10 @@ void matrox_cfbX_init(WPMINFO2) { } mga_fifo(8); mga_outl(M_PITCH, mpitch); - mga_outl(M_YDSTORG, curr_ydstorg(MINFO)); - if (ACCESS_FBINFO(capable.plnwt)) + mga_outl(M_YDSTORG, curr_ydstorg(minfo)); + if (minfo->capable.plnwt) mga_outl(M_PLNWT, -1); - if (ACCESS_FBINFO(capable.srcorg)) { + if (minfo->capable.srcorg) { mga_outl(M_SRCORG, 0); mga_outl(M_DSTORG, 0); } @@ -188,14 +189,16 @@ void matrox_cfbX_init(WPMINFO2) { mga_outl(M_YTOP, 0); mga_outl(M_YBOT, 0x01FFFFFF); mga_outl(M_MACCESS, maccess); - ACCESS_FBINFO(accel.m_dwg_rect) = M_DWG_TRAP | M_DWG_SOLID | M_DWG_ARZERO | M_DWG_SGNZERO | M_DWG_SHIFTZERO; - if (isMilleniumII(MINFO)) ACCESS_FBINFO(accel.m_dwg_rect) |= M_DWG_TRANSC; - ACCESS_FBINFO(accel.m_opmode) = mopmode; + minfo->accel.m_dwg_rect = M_DWG_TRAP | M_DWG_SOLID | M_DWG_ARZERO | M_DWG_SGNZERO | M_DWG_SHIFTZERO; + if (isMilleniumII(minfo)) minfo->accel.m_dwg_rect |= M_DWG_TRANSC; + minfo->accel.m_opmode = mopmode; } EXPORT_SYMBOL(matrox_cfbX_init); -static void matrox_accel_bmove(WPMINFO int vxres, int sy, int sx, int dy, int dx, int height, int width) { +static void matrox_accel_bmove(struct matrox_fb_info *minfo, int vxres, int sy, + int sx, int dy, int dx, int height, int width) +{ int start, end; CRITFLAGS @@ -209,7 +212,7 @@ static void matrox_accel_bmove(WPMINFO int vxres, int sy, int sx, int dy, int dx M_DWG_BFCOL | M_DWG_REPLACE); mga_outl(M_AR5, vxres); width--; - start = sy*vxres+sx+curr_ydstorg(MINFO); + start = sy*vxres+sx+curr_ydstorg(minfo); end = start+width; } else { mga_fifo(3); @@ -217,7 +220,7 @@ static void matrox_accel_bmove(WPMINFO int vxres, int sy, int sx, int dy, int dx mga_outl(M_SGN, 5); mga_outl(M_AR5, -vxres); width--; - end = (sy+height-1)*vxres+sx+curr_ydstorg(MINFO); + end = (sy+height-1)*vxres+sx+curr_ydstorg(minfo); start = end+width; dy += height-1; } @@ -231,7 +234,10 @@ static void matrox_accel_bmove(WPMINFO int vxres, int sy, int sx, int dy, int dx CRITEND } -static void matrox_accel_bmove_lin(WPMINFO int vxres, int sy, int sx, int dy, int dx, int height, int width) { +static void matrox_accel_bmove_lin(struct matrox_fb_info *minfo, int vxres, + int sy, int sx, int dy, int dx, int height, + int width) +{ int start, end; CRITFLAGS @@ -245,7 +251,7 @@ static void matrox_accel_bmove_lin(WPMINFO int vxres, int sy, int sx, int dy, in M_DWG_BFCOL | M_DWG_REPLACE); mga_outl(M_AR5, vxres); width--; - start = sy*vxres+sx+curr_ydstorg(MINFO); + start = sy*vxres+sx+curr_ydstorg(minfo); end = start+width; } else { mga_fifo(3); @@ -253,7 +259,7 @@ static void matrox_accel_bmove_lin(WPMINFO int vxres, int sy, int sx, int dy, in mga_outl(M_SGN, 5); mga_outl(M_AR5, -vxres); width--; - end = (sy+height-1)*vxres+sx+curr_ydstorg(MINFO); + end = (sy+height-1)*vxres+sx+curr_ydstorg(minfo); start = end+width; dy += height-1; } @@ -269,22 +275,23 @@ static void matrox_accel_bmove_lin(WPMINFO int vxres, int sy, int sx, int dy, in } static void matroxfb_cfb4_copyarea(struct fb_info* info, const struct fb_copyarea* area) { - MINFO_FROM_INFO(info); + struct matrox_fb_info *minfo = info2minfo(info); if ((area->sx | area->dx | area->width) & 1) cfb_copyarea(info, area); else - matrox_accel_bmove_lin(PMINFO ACCESS_FBINFO(fbcon.var.xres_virtual) >> 1, area->sy, area->sx >> 1, area->dy, area->dx >> 1, area->height, area->width >> 1); + matrox_accel_bmove_lin(minfo, minfo->fbcon.var.xres_virtual >> 1, area->sy, area->sx >> 1, area->dy, area->dx >> 1, area->height, area->width >> 1); } static void matroxfb_copyarea(struct fb_info* info, const struct fb_copyarea* area) { - MINFO_FROM_INFO(info); + struct matrox_fb_info *minfo = info2minfo(info); - matrox_accel_bmove(PMINFO ACCESS_FBINFO(fbcon.var.xres_virtual), area->sy, area->sx, area->dy, area->dx, area->height, area->width); + matrox_accel_bmove(minfo, minfo->fbcon.var.xres_virtual, area->sy, area->sx, area->dy, area->dx, area->height, area->width); } -static void matroxfb_accel_clear(WPMINFO u_int32_t color, int sy, int sx, int height, - int width) { +static void matroxfb_accel_clear(struct matrox_fb_info *minfo, u_int32_t color, + int sy, int sx, int height, int width) +{ CRITFLAGS DBG(__func__) @@ -292,7 +299,7 @@ static void matroxfb_accel_clear(WPMINFO u_int32_t color, int sy, int sx, int he CRITBEGIN mga_fifo(5); - mga_outl(M_DWGCTL, ACCESS_FBINFO(accel.m_dwg_rect) | M_DWG_REPLACE); + mga_outl(M_DWGCTL, minfo->accel.m_dwg_rect | M_DWG_REPLACE); mga_outl(M_FCOL, color); mga_outl(M_FXBNDRY, ((sx + width) << 16) | sx); mga_ydstlen(sy, height); @@ -302,16 +309,18 @@ static void matroxfb_accel_clear(WPMINFO u_int32_t color, int sy, int sx, int he } static void matroxfb_fillrect(struct fb_info* info, const struct fb_fillrect* rect) { - MINFO_FROM_INFO(info); + struct matrox_fb_info *minfo = info2minfo(info); switch (rect->rop) { case ROP_COPY: - matroxfb_accel_clear(PMINFO ((u_int32_t*)info->pseudo_palette)[rect->color], rect->dy, rect->dx, rect->height, rect->width); + matroxfb_accel_clear(minfo, ((u_int32_t *)info->pseudo_palette)[rect->color], rect->dy, rect->dx, rect->height, rect->width); break; } } -static void matroxfb_cfb4_clear(WPMINFO u_int32_t bgx, int sy, int sx, int height, int width) { +static void matroxfb_cfb4_clear(struct matrox_fb_info *minfo, u_int32_t bgx, + int sy, int sx, int height, int width) +{ int whattodo; CRITFLAGS @@ -333,16 +342,16 @@ static void matroxfb_cfb4_clear(WPMINFO u_int32_t bgx, int sy, int sx, int heigh sx >>= 1; if (width) { mga_fifo(5); - mga_outl(M_DWGCTL, ACCESS_FBINFO(accel.m_dwg_rect) | M_DWG_REPLACE2); + mga_outl(M_DWGCTL, minfo->accel.m_dwg_rect | M_DWG_REPLACE2); mga_outl(M_FCOL, bgx); mga_outl(M_FXBNDRY, ((sx + width) << 16) | sx); - mga_outl(M_YDST, sy * ACCESS_FBINFO(fbcon).var.xres_virtual >> 6); + mga_outl(M_YDST, sy * minfo->fbcon.var.xres_virtual >> 6); mga_outl(M_LEN | M_EXEC, height); WaitTillIdle(); } if (whattodo) { - u_int32_t step = ACCESS_FBINFO(fbcon).var.xres_virtual >> 1; - vaddr_t vbase = ACCESS_FBINFO(video.vbase); + u_int32_t step = minfo->fbcon.var.xres_virtual >> 1; + vaddr_t vbase = minfo->video.vbase; if (whattodo & 1) { unsigned int uaddr = sy * step + sx - 1; u_int32_t loop; @@ -367,17 +376,19 @@ static void matroxfb_cfb4_clear(WPMINFO u_int32_t bgx, int sy, int sx, int heigh } static void matroxfb_cfb4_fillrect(struct fb_info* info, const struct fb_fillrect* rect) { - MINFO_FROM_INFO(info); + struct matrox_fb_info *minfo = info2minfo(info); switch (rect->rop) { case ROP_COPY: - matroxfb_cfb4_clear(PMINFO ((u_int32_t*)info->pseudo_palette)[rect->color], rect->dy, rect->dx, rect->height, rect->width); + matroxfb_cfb4_clear(minfo, ((u_int32_t *)info->pseudo_palette)[rect->color], rect->dy, rect->dx, rect->height, rect->width); break; } } -static void matroxfb_1bpp_imageblit(WPMINFO u_int32_t fgx, u_int32_t bgx, - const u_int8_t* chardata, int width, int height, int yy, int xx) { +static void matroxfb_1bpp_imageblit(struct matrox_fb_info *minfo, u_int32_t fgx, + u_int32_t bgx, const u_int8_t *chardata, + int width, int height, int yy, int xx) +{ u_int32_t step; u_int32_t ydstlen; u_int32_t xlen; @@ -412,7 +423,7 @@ static void matroxfb_1bpp_imageblit(WPMINFO u_int32_t fgx, u_int32_t bgx, mga_outl(M_FCOL, fgx); mga_outl(M_BCOL, bgx); fxbndry = ((xx + width - 1) << 16) | xx; - mmio = ACCESS_FBINFO(mmio.vbase); + mmio = minfo->mmio.vbase; mga_fifo(6); mga_writel(mmio, M_FXBNDRY, fxbndry); @@ -467,7 +478,7 @@ static void matroxfb_1bpp_imageblit(WPMINFO u_int32_t fgx, u_int32_t bgx, static void matroxfb_imageblit(struct fb_info* info, const struct fb_image* image) { - MINFO_FROM_INFO(info); + struct matrox_fb_info *minfo = info2minfo(info); DBG_HEAVY(__func__); @@ -476,7 +487,7 @@ static void matroxfb_imageblit(struct fb_info* info, const struct fb_image* imag fgx = ((u_int32_t*)info->pseudo_palette)[image->fg_color]; bgx = ((u_int32_t*)info->pseudo_palette)[image->bg_color]; - matroxfb_1bpp_imageblit(PMINFO fgx, bgx, image->data, image->width, image->height, image->dy, image->dx); + matroxfb_1bpp_imageblit(minfo, fgx, bgx, image->data, image->width, image->height, image->dy, image->dx); } else { /* Danger! image->depth is useless: logo painting code always passes framebuffer color depth here, although logo data are diff --git a/drivers/video/matrox/matroxfb_accel.h b/drivers/video/matrox/matroxfb_accel.h index f40c314b4c30..1e418e62c22d 100644 --- a/drivers/video/matrox/matroxfb_accel.h +++ b/drivers/video/matrox/matroxfb_accel.h @@ -3,6 +3,6 @@ #include "matroxfb_base.h" -void matrox_cfbX_init(WPMINFO2); +void matrox_cfbX_init(struct matrox_fb_info *minfo); #endif diff --git a/drivers/video/matrox/matroxfb_base.c b/drivers/video/matrox/matroxfb_base.c index 0c1049b308bf..7064fb4427b6 100644 --- a/drivers/video/matrox/matroxfb_base.c +++ b/drivers/video/matrox/matroxfb_base.c @@ -154,21 +154,22 @@ static struct fb_var_screeninfo vesafb_defined = { /* --------------------------------------------------------------------- */ -static void update_crtc2(WPMINFO unsigned int pos) { - struct matroxfb_dh_fb_info* info = ACCESS_FBINFO(crtc2.info); +static void update_crtc2(struct matrox_fb_info *minfo, unsigned int pos) +{ + struct matroxfb_dh_fb_info *info = minfo->crtc2.info; /* Make sure that displays are compatible */ - if (info && (info->fbcon.var.bits_per_pixel == ACCESS_FBINFO(fbcon).var.bits_per_pixel) - && (info->fbcon.var.xres_virtual == ACCESS_FBINFO(fbcon).var.xres_virtual) - && (info->fbcon.var.green.length == ACCESS_FBINFO(fbcon).var.green.length) + if (info && (info->fbcon.var.bits_per_pixel == minfo->fbcon.var.bits_per_pixel) + && (info->fbcon.var.xres_virtual == minfo->fbcon.var.xres_virtual) + && (info->fbcon.var.green.length == minfo->fbcon.var.green.length) ) { - switch (ACCESS_FBINFO(fbcon).var.bits_per_pixel) { + switch (minfo->fbcon.var.bits_per_pixel) { case 16: case 32: pos = pos * 8; if (info->interlaced) { mga_outl(0x3C2C, pos); - mga_outl(0x3C28, pos + ACCESS_FBINFO(fbcon).var.xres_virtual * ACCESS_FBINFO(fbcon).var.bits_per_pixel / 8); + mga_outl(0x3C28, pos + minfo->fbcon.var.xres_virtual * minfo->fbcon.var.bits_per_pixel / 8); } else { mga_outl(0x3C28, pos); } @@ -177,17 +178,18 @@ static void update_crtc2(WPMINFO unsigned int pos) { } } -static void matroxfb_crtc1_panpos(WPMINFO2) { - if (ACCESS_FBINFO(crtc1.panpos) >= 0) { +static void matroxfb_crtc1_panpos(struct matrox_fb_info *minfo) +{ + if (minfo->crtc1.panpos >= 0) { unsigned long flags; int panpos; matroxfb_DAC_lock_irqsave(flags); - panpos = ACCESS_FBINFO(crtc1.panpos); + panpos = minfo->crtc1.panpos; if (panpos >= 0) { unsigned int extvga_reg; - ACCESS_FBINFO(crtc1.panpos) = -1; /* No update pending anymore */ + minfo->crtc1.panpos = -1; /* No update pending anymore */ extvga_reg = mga_inb(M_EXTVGA_INDEX); mga_setr(M_EXTVGA_INDEX, 0x00, panpos); if (extvga_reg != 0x00) { @@ -202,39 +204,39 @@ static irqreturn_t matrox_irq(int irq, void *dev_id) { u_int32_t status; int handled = 0; - - MINFO_FROM(dev_id); + struct matrox_fb_info *minfo = dev_id; status = mga_inl(M_STATUS); if (status & 0x20) { mga_outl(M_ICLEAR, 0x20); - ACCESS_FBINFO(crtc1.vsync.cnt)++; - matroxfb_crtc1_panpos(PMINFO2); - wake_up_interruptible(&ACCESS_FBINFO(crtc1.vsync.wait)); + minfo->crtc1.vsync.cnt++; + matroxfb_crtc1_panpos(minfo); + wake_up_interruptible(&minfo->crtc1.vsync.wait); handled = 1; } if (status & 0x200) { mga_outl(M_ICLEAR, 0x200); - ACCESS_FBINFO(crtc2.vsync.cnt)++; - wake_up_interruptible(&ACCESS_FBINFO(crtc2.vsync.wait)); + minfo->crtc2.vsync.cnt++; + wake_up_interruptible(&minfo->crtc2.vsync.wait); handled = 1; } return IRQ_RETVAL(handled); } -int matroxfb_enable_irq(WPMINFO int reenable) { +int matroxfb_enable_irq(struct matrox_fb_info *minfo, int reenable) +{ u_int32_t bm; - if (ACCESS_FBINFO(devflags.accelerator) == FB_ACCEL_MATROX_MGAG400) + if (minfo->devflags.accelerator == FB_ACCEL_MATROX_MGAG400) bm = 0x220; else bm = 0x020; - if (!test_and_set_bit(0, &ACCESS_FBINFO(irq_flags))) { - if (request_irq(ACCESS_FBINFO(pcidev)->irq, matrox_irq, - IRQF_SHARED, "matroxfb", MINFO)) { - clear_bit(0, &ACCESS_FBINFO(irq_flags)); + if (!test_and_set_bit(0, &minfo->irq_flags)) { + if (request_irq(minfo->pcidev->irq, matrox_irq, + IRQF_SHARED, "matroxfb", minfo)) { + clear_bit(0, &minfo->irq_flags); return -EINVAL; } /* Clear any pending field interrupts */ @@ -252,37 +254,39 @@ int matroxfb_enable_irq(WPMINFO int reenable) { return 0; } -static void matroxfb_disable_irq(WPMINFO2) { - if (test_and_clear_bit(0, &ACCESS_FBINFO(irq_flags))) { +static void matroxfb_disable_irq(struct matrox_fb_info *minfo) +{ + if (test_and_clear_bit(0, &minfo->irq_flags)) { /* Flush pending pan-at-vbl request... */ - matroxfb_crtc1_panpos(PMINFO2); - if (ACCESS_FBINFO(devflags.accelerator) == FB_ACCEL_MATROX_MGAG400) + matroxfb_crtc1_panpos(minfo); + if (minfo->devflags.accelerator == FB_ACCEL_MATROX_MGAG400) mga_outl(M_IEN, mga_inl(M_IEN) & ~0x220); else mga_outl(M_IEN, mga_inl(M_IEN) & ~0x20); - free_irq(ACCESS_FBINFO(pcidev)->irq, MINFO); + free_irq(minfo->pcidev->irq, minfo); } } -int matroxfb_wait_for_sync(WPMINFO u_int32_t crtc) { +int matroxfb_wait_for_sync(struct matrox_fb_info *minfo, u_int32_t crtc) +{ struct matrox_vsync *vs; unsigned int cnt; int ret; switch (crtc) { case 0: - vs = &ACCESS_FBINFO(crtc1.vsync); + vs = &minfo->crtc1.vsync; break; case 1: - if (ACCESS_FBINFO(devflags.accelerator) != FB_ACCEL_MATROX_MGAG400) { + if (minfo->devflags.accelerator != FB_ACCEL_MATROX_MGAG400) { return -ENODEV; } - vs = &ACCESS_FBINFO(crtc2.vsync); + vs = &minfo->crtc2.vsync; break; default: return -ENODEV; } - ret = matroxfb_enable_irq(PMINFO 0); + ret = matroxfb_enable_irq(minfo, 0); if (ret) { return ret; } @@ -293,7 +297,7 @@ int matroxfb_wait_for_sync(WPMINFO u_int32_t crtc) { return ret; } if (ret == 0) { - matroxfb_enable_irq(PMINFO 1); + matroxfb_enable_irq(minfo, 1); return -ETIMEDOUT; } return 0; @@ -301,12 +305,12 @@ int matroxfb_wait_for_sync(WPMINFO u_int32_t crtc) { /* --------------------------------------------------------------------- */ -static void matrox_pan_var(WPMINFO struct fb_var_screeninfo *var) { +static void matrox_pan_var(struct matrox_fb_info *minfo, + struct fb_var_screeninfo *var) +{ unsigned int pos; unsigned short p0, p1, p2; -#ifdef CONFIG_FB_MATROX_32MB unsigned int p3; -#endif int vbl; unsigned long flags; @@ -314,47 +318,44 @@ static void matrox_pan_var(WPMINFO struct fb_var_screeninfo *var) { DBG(__func__) - if (ACCESS_FBINFO(dead)) + if (minfo->dead) return; - ACCESS_FBINFO(fbcon).var.xoffset = var->xoffset; - ACCESS_FBINFO(fbcon).var.yoffset = var->yoffset; - pos = (ACCESS_FBINFO(fbcon).var.yoffset * ACCESS_FBINFO(fbcon).var.xres_virtual + ACCESS_FBINFO(fbcon).var.xoffset) * ACCESS_FBINFO(curr.final_bppShift) / 32; - pos += ACCESS_FBINFO(curr.ydstorg.chunks); - p0 = ACCESS_FBINFO(hw).CRTC[0x0D] = pos & 0xFF; - p1 = ACCESS_FBINFO(hw).CRTC[0x0C] = (pos & 0xFF00) >> 8; - p2 = ACCESS_FBINFO(hw).CRTCEXT[0] = (ACCESS_FBINFO(hw).CRTCEXT[0] & 0xB0) | ((pos >> 16) & 0x0F) | ((pos >> 14) & 0x40); -#ifdef CONFIG_FB_MATROX_32MB - p3 = ACCESS_FBINFO(hw).CRTCEXT[8] = pos >> 21; -#endif + minfo->fbcon.var.xoffset = var->xoffset; + minfo->fbcon.var.yoffset = var->yoffset; + pos = (minfo->fbcon.var.yoffset * minfo->fbcon.var.xres_virtual + minfo->fbcon.var.xoffset) * minfo->curr.final_bppShift / 32; + pos += minfo->curr.ydstorg.chunks; + p0 = minfo->hw.CRTC[0x0D] = pos & 0xFF; + p1 = minfo->hw.CRTC[0x0C] = (pos & 0xFF00) >> 8; + p2 = minfo->hw.CRTCEXT[0] = (minfo->hw.CRTCEXT[0] & 0xB0) | ((pos >> 16) & 0x0F) | ((pos >> 14) & 0x40); + p3 = minfo->hw.CRTCEXT[8] = pos >> 21; /* FB_ACTIVATE_VBL and we can acquire interrupts? Honor FB_ACTIVATE_VBL then... */ - vbl = (var->activate & FB_ACTIVATE_VBL) && (matroxfb_enable_irq(PMINFO 0) == 0); + vbl = (var->activate & FB_ACTIVATE_VBL) && (matroxfb_enable_irq(minfo, 0) == 0); CRITBEGIN matroxfb_DAC_lock_irqsave(flags); mga_setr(M_CRTC_INDEX, 0x0D, p0); mga_setr(M_CRTC_INDEX, 0x0C, p1); -#ifdef CONFIG_FB_MATROX_32MB - if (ACCESS_FBINFO(devflags.support32MB)) + if (minfo->devflags.support32MB) mga_setr(M_EXTVGA_INDEX, 0x08, p3); -#endif if (vbl) { - ACCESS_FBINFO(crtc1.panpos) = p2; + minfo->crtc1.panpos = p2; } else { /* Abort any pending change */ - ACCESS_FBINFO(crtc1.panpos) = -1; + minfo->crtc1.panpos = -1; mga_setr(M_EXTVGA_INDEX, 0x00, p2); } matroxfb_DAC_unlock_irqrestore(flags); - update_crtc2(PMINFO pos); + update_crtc2(minfo, pos); CRITEND } -static void matroxfb_remove(WPMINFO int dummy) { +static void matroxfb_remove(struct matrox_fb_info *minfo, int dummy) +{ /* Currently we are holding big kernel lock on all dead & usecount updates. * Destroy everything after all users release it. Especially do not unregister * framebuffer and iounmap memory, neither fbmem nor fbcon-cfb* does not check @@ -363,25 +364,23 @@ static void matroxfb_remove(WPMINFO int dummy) { * write data without causing too much damage... */ - ACCESS_FBINFO(dead) = 1; - if (ACCESS_FBINFO(usecount)) { + minfo->dead = 1; + if (minfo->usecount) { /* destroy it later */ return; } - matroxfb_unregister_device(MINFO); - unregister_framebuffer(&ACCESS_FBINFO(fbcon)); - matroxfb_g450_shutdown(PMINFO2); + matroxfb_unregister_device(minfo); + unregister_framebuffer(&minfo->fbcon); + matroxfb_g450_shutdown(minfo); #ifdef CONFIG_MTRR - if (ACCESS_FBINFO(mtrr.vram_valid)) - mtrr_del(ACCESS_FBINFO(mtrr.vram), ACCESS_FBINFO(video.base), ACCESS_FBINFO(video.len)); + if (minfo->mtrr.vram_valid) + mtrr_del(minfo->mtrr.vram, minfo->video.base, minfo->video.len); #endif - mga_iounmap(ACCESS_FBINFO(mmio.vbase)); - mga_iounmap(ACCESS_FBINFO(video.vbase)); - release_mem_region(ACCESS_FBINFO(video.base), ACCESS_FBINFO(video.len_maximum)); - release_mem_region(ACCESS_FBINFO(mmio.base), 16384); -#ifdef CONFIG_FB_MATROX_MULTIHEAD + mga_iounmap(minfo->mmio.vbase); + mga_iounmap(minfo->video.vbase); + release_mem_region(minfo->video.base, minfo->video.len_maximum); + release_mem_region(minfo->mmio.base, 16384); kfree(minfo); -#endif } /* @@ -390,48 +389,50 @@ static void matroxfb_remove(WPMINFO int dummy) { static int matroxfb_open(struct fb_info *info, int user) { - MINFO_FROM_INFO(info); + struct matrox_fb_info *minfo = info2minfo(info); DBG_LOOP(__func__) - if (ACCESS_FBINFO(dead)) { + if (minfo->dead) { return -ENXIO; } - ACCESS_FBINFO(usecount)++; + minfo->usecount++; if (user) { - ACCESS_FBINFO(userusecount)++; + minfo->userusecount++; } return(0); } static int matroxfb_release(struct fb_info *info, int user) { - MINFO_FROM_INFO(info); + struct matrox_fb_info *minfo = info2minfo(info); DBG_LOOP(__func__) if (user) { - if (0 == --ACCESS_FBINFO(userusecount)) { - matroxfb_disable_irq(PMINFO2); + if (0 == --minfo->userusecount) { + matroxfb_disable_irq(minfo); } } - if (!(--ACCESS_FBINFO(usecount)) && ACCESS_FBINFO(dead)) { - matroxfb_remove(PMINFO 0); + if (!(--minfo->usecount) && minfo->dead) { + matroxfb_remove(minfo, 0); } return(0); } static int matroxfb_pan_display(struct fb_var_screeninfo *var, struct fb_info* info) { - MINFO_FROM_INFO(info); + struct matrox_fb_info *minfo = info2minfo(info); DBG(__func__) - matrox_pan_var(PMINFO var); + matrox_pan_var(minfo, var); return 0; } -static int matroxfb_get_final_bppShift(CPMINFO int bpp) { +static int matroxfb_get_final_bppShift(const struct matrox_fb_info *minfo, + int bpp) +{ int bppshft2; DBG(__func__) @@ -440,14 +441,16 @@ static int matroxfb_get_final_bppShift(CPMINFO int bpp) { if (!bppshft2) { return 8; } - if (isInterleave(MINFO)) + if (isInterleave(minfo)) bppshft2 >>= 1; - if (ACCESS_FBINFO(devflags.video64bits)) + if (minfo->devflags.video64bits) bppshft2 >>= 1; return bppshft2; } -static int matroxfb_test_and_set_rounding(CPMINFO int xres, int bpp) { +static int matroxfb_test_and_set_rounding(const struct matrox_fb_info *minfo, + int xres, int bpp) +{ int over; int rounding; @@ -465,11 +468,11 @@ static int matroxfb_test_and_set_rounding(CPMINFO int xres, int bpp) { break; default: rounding = 16; /* on G400, 16 really does not work */ - if (ACCESS_FBINFO(devflags.accelerator) == FB_ACCEL_MATROX_MGAG400) + if (minfo->devflags.accelerator == FB_ACCEL_MATROX_MGAG400) rounding = 32; break; } - if (isInterleave(MINFO)) { + if (isInterleave(minfo)) { rounding *= 2; } over = xres % rounding; @@ -478,7 +481,9 @@ static int matroxfb_test_and_set_rounding(CPMINFO int xres, int bpp) { return xres; } -static int matroxfb_pitch_adjust(CPMINFO int xres, int bpp) { +static int matroxfb_pitch_adjust(const struct matrox_fb_info *minfo, int xres, + int bpp) +{ const int* width; int xres_new; @@ -486,18 +491,18 @@ static int matroxfb_pitch_adjust(CPMINFO int xres, int bpp) { if (!bpp) return xres; - width = ACCESS_FBINFO(capable.vxres); + width = minfo->capable.vxres; - if (ACCESS_FBINFO(devflags.precise_width)) { + if (minfo->devflags.precise_width) { while (*width) { - if ((*width >= xres) && (matroxfb_test_and_set_rounding(PMINFO *width, bpp) == *width)) { + if ((*width >= xres) && (matroxfb_test_and_set_rounding(minfo, *width, bpp) == *width)) { break; } width++; } xres_new = *width; } else { - xres_new = matroxfb_test_and_set_rounding(PMINFO xres, bpp); + xres_new = matroxfb_test_and_set_rounding(minfo, xres, bpp); } return xres_new; } @@ -524,7 +529,10 @@ static int matroxfb_get_cmap_len(struct fb_var_screeninfo *var) { return 16; /* return something reasonable... or panic()? */ } -static int matroxfb_decode_var(CPMINFO struct fb_var_screeninfo *var, int *visual, int *video_cmap_len, unsigned int* ydstorg) { +static int matroxfb_decode_var(const struct matrox_fb_info *minfo, + struct fb_var_screeninfo *var, int *visual, + int *video_cmap_len, unsigned int* ydstorg) +{ struct RGBT { unsigned char bpp; struct { @@ -551,7 +559,7 @@ static int matroxfb_decode_var(CPMINFO struct fb_var_screeninfo *var, int *visua DBG(__func__) switch (bpp) { - case 4: if (!ACCESS_FBINFO(capable.cfb4)) return -EINVAL; + case 4: if (!minfo->capable.cfb4) return -EINVAL; break; case 8: break; case 16: break; @@ -560,13 +568,13 @@ static int matroxfb_decode_var(CPMINFO struct fb_var_screeninfo *var, int *visua default: return -EINVAL; } *ydstorg = 0; - vramlen = ACCESS_FBINFO(video.len_usable); + vramlen = minfo->video.len_usable; if (var->yres_virtual < var->yres) var->yres_virtual = var->yres; if (var->xres_virtual < var->xres) var->xres_virtual = var->xres; - var->xres_virtual = matroxfb_pitch_adjust(PMINFO var->xres_virtual, bpp); + var->xres_virtual = matroxfb_pitch_adjust(minfo, var->xres_virtual, bpp); memlen = var->xres_virtual * bpp * var->yres_virtual / 8; if (memlen > vramlen) { var->yres_virtual = vramlen * 8 / (var->xres_virtual * bpp); @@ -575,7 +583,7 @@ static int matroxfb_decode_var(CPMINFO struct fb_var_screeninfo *var, int *visua /* There is hardware bug that no line can cross 4MB boundary */ /* give up for CFB24, it is impossible to easy workaround it */ /* for other try to do something */ - if (!ACCESS_FBINFO(capable.cross4MB) && (memlen > 0x400000)) { + if (!minfo->capable.cross4MB && (memlen > 0x400000)) { if (bpp == 24) { /* sorry */ } else { @@ -644,9 +652,7 @@ static int matroxfb_setcolreg(unsigned regno, unsigned red, unsigned green, unsigned blue, unsigned transp, struct fb_info *fb_info) { -#ifdef CONFIG_FB_MATROX_MULTIHEAD struct matrox_fb_info* minfo = container_of(fb_info, struct matrox_fb_info, fbcon); -#endif DBG(__func__) @@ -657,20 +663,20 @@ static int matroxfb_setcolreg(unsigned regno, unsigned red, unsigned green, * != 0 for invalid regno. */ - if (regno >= ACCESS_FBINFO(curr.cmap_len)) + if (regno >= minfo->curr.cmap_len) return 1; - if (ACCESS_FBINFO(fbcon).var.grayscale) { + if (minfo->fbcon.var.grayscale) { /* gray = 0.30*R + 0.59*G + 0.11*B */ red = green = blue = (red * 77 + green * 151 + blue * 28) >> 8; } - red = CNVT_TOHW(red, ACCESS_FBINFO(fbcon).var.red.length); - green = CNVT_TOHW(green, ACCESS_FBINFO(fbcon).var.green.length); - blue = CNVT_TOHW(blue, ACCESS_FBINFO(fbcon).var.blue.length); - transp = CNVT_TOHW(transp, ACCESS_FBINFO(fbcon).var.transp.length); + red = CNVT_TOHW(red, minfo->fbcon.var.red.length); + green = CNVT_TOHW(green, minfo->fbcon.var.green.length); + blue = CNVT_TOHW(blue, minfo->fbcon.var.blue.length); + transp = CNVT_TOHW(transp, minfo->fbcon.var.transp.length); - switch (ACCESS_FBINFO(fbcon).var.bits_per_pixel) { + switch (minfo->fbcon.var.bits_per_pixel) { case 4: case 8: mga_outb(M_DAC_REG, regno); @@ -683,30 +689,30 @@ static int matroxfb_setcolreg(unsigned regno, unsigned red, unsigned green, break; { u_int16_t col = - (red << ACCESS_FBINFO(fbcon).var.red.offset) | - (green << ACCESS_FBINFO(fbcon).var.green.offset) | - (blue << ACCESS_FBINFO(fbcon).var.blue.offset) | - (transp << ACCESS_FBINFO(fbcon).var.transp.offset); /* for 1:5:5:5 */ - ACCESS_FBINFO(cmap[regno]) = col | (col << 16); + (red << minfo->fbcon.var.red.offset) | + (green << minfo->fbcon.var.green.offset) | + (blue << minfo->fbcon.var.blue.offset) | + (transp << minfo->fbcon.var.transp.offset); /* for 1:5:5:5 */ + minfo->cmap[regno] = col | (col << 16); } break; case 24: case 32: if (regno >= 16) break; - ACCESS_FBINFO(cmap[regno]) = - (red << ACCESS_FBINFO(fbcon).var.red.offset) | - (green << ACCESS_FBINFO(fbcon).var.green.offset) | - (blue << ACCESS_FBINFO(fbcon).var.blue.offset) | - (transp << ACCESS_FBINFO(fbcon).var.transp.offset); /* 8:8:8:8 */ + minfo->cmap[regno] = + (red << minfo->fbcon.var.red.offset) | + (green << minfo->fbcon.var.green.offset) | + (blue << minfo->fbcon.var.blue.offset) | + (transp << minfo->fbcon.var.transp.offset); /* 8:8:8:8 */ break; } return 0; } -static void matroxfb_init_fix(WPMINFO2) +static void matroxfb_init_fix(struct matrox_fb_info *minfo) { - struct fb_fix_screeninfo *fix = &ACCESS_FBINFO(fbcon).fix; + struct fb_fix_screeninfo *fix = &minfo->fbcon.fix; DBG(__func__) strcpy(fix->id,"MATROX"); @@ -714,20 +720,20 @@ static void matroxfb_init_fix(WPMINFO2) fix->xpanstep = 8; /* 8 for 8bpp, 4 for 16bpp, 2 for 32bpp */ fix->ypanstep = 1; fix->ywrapstep = 0; - fix->mmio_start = ACCESS_FBINFO(mmio.base); - fix->mmio_len = ACCESS_FBINFO(mmio.len); - fix->accel = ACCESS_FBINFO(devflags.accelerator); + fix->mmio_start = minfo->mmio.base; + fix->mmio_len = minfo->mmio.len; + fix->accel = minfo->devflags.accelerator; } -static void matroxfb_update_fix(WPMINFO2) +static void matroxfb_update_fix(struct matrox_fb_info *minfo) { - struct fb_fix_screeninfo *fix = &ACCESS_FBINFO(fbcon).fix; + struct fb_fix_screeninfo *fix = &minfo->fbcon.fix; DBG(__func__) - mutex_lock(&ACCESS_FBINFO(fbcon).mm_lock); - fix->smem_start = ACCESS_FBINFO(video.base) + ACCESS_FBINFO(curr.ydstorg.bytes); - fix->smem_len = ACCESS_FBINFO(video.len_usable) - ACCESS_FBINFO(curr.ydstorg.bytes); - mutex_unlock(&ACCESS_FBINFO(fbcon).mm_lock); + mutex_lock(&minfo->fbcon.mm_lock); + fix->smem_start = minfo->video.base + minfo->curr.ydstorg.bytes; + fix->smem_len = minfo->video.len_usable - minfo->curr.ydstorg.bytes; + mutex_unlock(&minfo->fbcon.mm_lock); } static int matroxfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) @@ -736,12 +742,12 @@ static int matroxfb_check_var(struct fb_var_screeninfo *var, struct fb_info *inf int visual; int cmap_len; unsigned int ydstorg; - MINFO_FROM_INFO(info); + struct matrox_fb_info *minfo = info2minfo(info); - if (ACCESS_FBINFO(dead)) { + if (minfo->dead) { return -ENXIO; } - if ((err = matroxfb_decode_var(PMINFO var, &visual, &cmap_len, &ydstorg)) != 0) + if ((err = matroxfb_decode_var(minfo, var, &visual, &cmap_len, &ydstorg)) != 0) return err; return 0; } @@ -753,35 +759,35 @@ static int matroxfb_set_par(struct fb_info *info) int cmap_len; unsigned int ydstorg; struct fb_var_screeninfo *var; - MINFO_FROM_INFO(info); + struct matrox_fb_info *minfo = info2minfo(info); DBG(__func__) - if (ACCESS_FBINFO(dead)) { + if (minfo->dead) { return -ENXIO; } var = &info->var; - if ((err = matroxfb_decode_var(PMINFO var, &visual, &cmap_len, &ydstorg)) != 0) + if ((err = matroxfb_decode_var(minfo, var, &visual, &cmap_len, &ydstorg)) != 0) return err; - ACCESS_FBINFO(fbcon.screen_base) = vaddr_va(ACCESS_FBINFO(video.vbase)) + ydstorg; - matroxfb_update_fix(PMINFO2); - ACCESS_FBINFO(fbcon).fix.visual = visual; - ACCESS_FBINFO(fbcon).fix.type = FB_TYPE_PACKED_PIXELS; - ACCESS_FBINFO(fbcon).fix.type_aux = 0; - ACCESS_FBINFO(fbcon).fix.line_length = (var->xres_virtual * var->bits_per_pixel) >> 3; + minfo->fbcon.screen_base = vaddr_va(minfo->video.vbase) + ydstorg; + matroxfb_update_fix(minfo); + minfo->fbcon.fix.visual = visual; + minfo->fbcon.fix.type = FB_TYPE_PACKED_PIXELS; + minfo->fbcon.fix.type_aux = 0; + minfo->fbcon.fix.line_length = (var->xres_virtual * var->bits_per_pixel) >> 3; { unsigned int pos; - ACCESS_FBINFO(curr.cmap_len) = cmap_len; - ydstorg += ACCESS_FBINFO(devflags.ydstorg); - ACCESS_FBINFO(curr.ydstorg.bytes) = ydstorg; - ACCESS_FBINFO(curr.ydstorg.chunks) = ydstorg >> (isInterleave(MINFO)?3:2); + minfo->curr.cmap_len = cmap_len; + ydstorg += minfo->devflags.ydstorg; + minfo->curr.ydstorg.bytes = ydstorg; + minfo->curr.ydstorg.chunks = ydstorg >> (isInterleave(minfo) ? 3 : 2); if (var->bits_per_pixel == 4) - ACCESS_FBINFO(curr.ydstorg.pixels) = ydstorg; + minfo->curr.ydstorg.pixels = ydstorg; else - ACCESS_FBINFO(curr.ydstorg.pixels) = (ydstorg * 8) / var->bits_per_pixel; - ACCESS_FBINFO(curr.final_bppShift) = matroxfb_get_final_bppShift(PMINFO var->bits_per_pixel); + minfo->curr.ydstorg.pixels = (ydstorg * 8) / var->bits_per_pixel; + minfo->curr.final_bppShift = matroxfb_get_final_bppShift(minfo, var->bits_per_pixel); { struct my_timming mt; struct matrox_hw_state* hw; int out; @@ -797,54 +803,55 @@ static int matroxfb_set_par(struct fb_info *info) default: mt.delay = 31 + 8; break; } - hw = &ACCESS_FBINFO(hw); + hw = &minfo->hw; - down_read(&ACCESS_FBINFO(altout).lock); + down_read(&minfo->altout.lock); for (out = 0; out < MATROXFB_MAX_OUTPUTS; out++) { - if (ACCESS_FBINFO(outputs[out]).src == MATROXFB_SRC_CRTC1 && - ACCESS_FBINFO(outputs[out]).output->compute) { - ACCESS_FBINFO(outputs[out]).output->compute(ACCESS_FBINFO(outputs[out]).data, &mt); + if (minfo->outputs[out].src == MATROXFB_SRC_CRTC1 && + minfo->outputs[out].output->compute) { + minfo->outputs[out].output->compute(minfo->outputs[out].data, &mt); } } - up_read(&ACCESS_FBINFO(altout).lock); - ACCESS_FBINFO(crtc1).pixclock = mt.pixclock; - ACCESS_FBINFO(crtc1).mnp = mt.mnp; - ACCESS_FBINFO(hw_switch->init(PMINFO &mt)); - pos = (var->yoffset * var->xres_virtual + var->xoffset) * ACCESS_FBINFO(curr.final_bppShift) / 32; - pos += ACCESS_FBINFO(curr.ydstorg.chunks); + up_read(&minfo->altout.lock); + minfo->crtc1.pixclock = mt.pixclock; + minfo->crtc1.mnp = mt.mnp; + minfo->hw_switch->init(minfo, &mt); + pos = (var->yoffset * var->xres_virtual + var->xoffset) * minfo->curr.final_bppShift / 32; + pos += minfo->curr.ydstorg.chunks; hw->CRTC[0x0D] = pos & 0xFF; hw->CRTC[0x0C] = (pos & 0xFF00) >> 8; hw->CRTCEXT[0] = (hw->CRTCEXT[0] & 0xF0) | ((pos >> 16) & 0x0F) | ((pos >> 14) & 0x40); hw->CRTCEXT[8] = pos >> 21; - ACCESS_FBINFO(hw_switch->restore(PMINFO2)); - update_crtc2(PMINFO pos); - down_read(&ACCESS_FBINFO(altout).lock); + minfo->hw_switch->restore(minfo); + update_crtc2(minfo, pos); + down_read(&minfo->altout.lock); for (out = 0; out < MATROXFB_MAX_OUTPUTS; out++) { - if (ACCESS_FBINFO(outputs[out]).src == MATROXFB_SRC_CRTC1 && - ACCESS_FBINFO(outputs[out]).output->program) { - ACCESS_FBINFO(outputs[out]).output->program(ACCESS_FBINFO(outputs[out]).data); + if (minfo->outputs[out].src == MATROXFB_SRC_CRTC1 && + minfo->outputs[out].output->program) { + minfo->outputs[out].output->program(minfo->outputs[out].data); } } for (out = 0; out < MATROXFB_MAX_OUTPUTS; out++) { - if (ACCESS_FBINFO(outputs[out]).src == MATROXFB_SRC_CRTC1 && - ACCESS_FBINFO(outputs[out]).output->start) { - ACCESS_FBINFO(outputs[out]).output->start(ACCESS_FBINFO(outputs[out]).data); + if (minfo->outputs[out].src == MATROXFB_SRC_CRTC1 && + minfo->outputs[out].output->start) { + minfo->outputs[out].output->start(minfo->outputs[out].data); } } - up_read(&ACCESS_FBINFO(altout).lock); - matrox_cfbX_init(PMINFO2); + up_read(&minfo->altout.lock); + matrox_cfbX_init(minfo); } } - ACCESS_FBINFO(initialized) = 1; + minfo->initialized = 1; return 0; } -static int matroxfb_get_vblank(WPMINFO struct fb_vblank *vblank) +static int matroxfb_get_vblank(struct matrox_fb_info *minfo, + struct fb_vblank *vblank) { unsigned int sts1; - matroxfb_enable_irq(PMINFO 0); + matroxfb_enable_irq(minfo, 0); memset(vblank, 0, sizeof(*vblank)); vblank->flags = FB_VBLANK_HAVE_VCOUNT | FB_VBLANK_HAVE_VSYNC | FB_VBLANK_HAVE_VBLANK | FB_VBLANK_HAVE_HBLANK; @@ -857,13 +864,13 @@ static int matroxfb_get_vblank(WPMINFO struct fb_vblank *vblank) vblank->flags |= FB_VBLANK_HBLANKING; if (sts1 & 8) vblank->flags |= FB_VBLANK_VSYNCING; - if (vblank->vcount >= ACCESS_FBINFO(fbcon).var.yres) + if (vblank->vcount >= minfo->fbcon.var.yres) vblank->flags |= FB_VBLANK_VBLANKING; - if (test_bit(0, &ACCESS_FBINFO(irq_flags))) { + if (test_bit(0, &minfo->irq_flags)) { vblank->flags |= FB_VBLANK_HAVE_COUNT; /* Only one writer, aligned int value... it should work without lock and without atomic_t */ - vblank->count = ACCESS_FBINFO(crtc1).vsync.cnt; + vblank->count = minfo->crtc1.vsync.cnt; } return 0; } @@ -876,11 +883,11 @@ static int matroxfb_ioctl(struct fb_info *info, unsigned int cmd, unsigned long arg) { void __user *argp = (void __user *)arg; - MINFO_FROM_INFO(info); + struct matrox_fb_info *minfo = info2minfo(info); DBG(__func__) - if (ACCESS_FBINFO(dead)) { + if (minfo->dead) { return -ENXIO; } @@ -890,7 +897,7 @@ static int matroxfb_ioctl(struct fb_info *info, struct fb_vblank vblank; int err; - err = matroxfb_get_vblank(PMINFO &vblank); + err = matroxfb_get_vblank(minfo, &vblank); if (err) return err; if (copy_to_user(argp, &vblank, sizeof(vblank))) @@ -904,7 +911,7 @@ static int matroxfb_ioctl(struct fb_info *info, if (get_user(crt, (u_int32_t __user *)arg)) return -EFAULT; - return matroxfb_wait_for_sync(PMINFO crt); + return matroxfb_wait_for_sync(minfo, crt); } case MATROXFB_SET_OUTPUT_MODE: { @@ -916,8 +923,8 @@ static int matroxfb_ioctl(struct fb_info *info, return -EFAULT; if (mom.output >= MATROXFB_MAX_OUTPUTS) return -ENXIO; - down_read(&ACCESS_FBINFO(altout.lock)); - oproc = ACCESS_FBINFO(outputs[mom.output]).output; + down_read(&minfo->altout.lock); + oproc = minfo->outputs[mom.output].output; if (!oproc) { val = -ENXIO; } else if (!oproc->verifymode) { @@ -927,18 +934,18 @@ static int matroxfb_ioctl(struct fb_info *info, val = -EINVAL; } } else { - val = oproc->verifymode(ACCESS_FBINFO(outputs[mom.output]).data, mom.mode); + val = oproc->verifymode(minfo->outputs[mom.output].data, mom.mode); } if (!val) { - if (ACCESS_FBINFO(outputs[mom.output]).mode != mom.mode) { - ACCESS_FBINFO(outputs[mom.output]).mode = mom.mode; + if (minfo->outputs[mom.output].mode != mom.mode) { + minfo->outputs[mom.output].mode = mom.mode; val = 1; } } - up_read(&ACCESS_FBINFO(altout.lock)); + up_read(&minfo->altout.lock); if (val != 1) return val; - switch (ACCESS_FBINFO(outputs[mom.output]).src) { + switch (minfo->outputs[mom.output].src) { case MATROXFB_SRC_CRTC1: matroxfb_set_par(info); break; @@ -946,11 +953,11 @@ static int matroxfb_ioctl(struct fb_info *info, { struct matroxfb_dh_fb_info* crtc2; - down_read(&ACCESS_FBINFO(crtc2.lock)); - crtc2 = ACCESS_FBINFO(crtc2.info); + down_read(&minfo->crtc2.lock); + crtc2 = minfo->crtc2.info; if (crtc2) crtc2->fbcon.fbops->fb_set_par(&crtc2->fbcon); - up_read(&ACCESS_FBINFO(crtc2.lock)); + up_read(&minfo->crtc2.lock); } break; } @@ -966,15 +973,15 @@ static int matroxfb_ioctl(struct fb_info *info, return -EFAULT; if (mom.output >= MATROXFB_MAX_OUTPUTS) return -ENXIO; - down_read(&ACCESS_FBINFO(altout.lock)); - oproc = ACCESS_FBINFO(outputs[mom.output]).output; + down_read(&minfo->altout.lock); + oproc = minfo->outputs[mom.output].output; if (!oproc) { val = -ENXIO; } else { - mom.mode = ACCESS_FBINFO(outputs[mom.output]).mode; + mom.mode = minfo->outputs[mom.output].mode; val = 0; } - up_read(&ACCESS_FBINFO(altout.lock)); + up_read(&minfo->altout.lock); if (val) return val; if (copy_to_user(argp, &mom, sizeof(mom))) @@ -993,9 +1000,9 @@ static int matroxfb_ioctl(struct fb_info *info, if (tmp & (1 << i)) { if (i >= MATROXFB_MAX_OUTPUTS) return -ENXIO; - if (!ACCESS_FBINFO(outputs[i]).output) + if (!minfo->outputs[i].output) return -ENXIO; - switch (ACCESS_FBINFO(outputs[i]).src) { + switch (minfo->outputs[i].src) { case MATROXFB_SRC_NONE: case MATROXFB_SRC_CRTC1: break; @@ -1004,12 +1011,12 @@ static int matroxfb_ioctl(struct fb_info *info, } } } - if (ACCESS_FBINFO(devflags.panellink)) { + if (minfo->devflags.panellink) { if (tmp & MATROXFB_OUTPUT_CONN_DFP) { if (tmp & MATROXFB_OUTPUT_CONN_SECONDARY) return -EINVAL; for (i = 0; i < MATROXFB_MAX_OUTPUTS; i++) { - if (ACCESS_FBINFO(outputs[i]).src == MATROXFB_SRC_CRTC2) { + if (minfo->outputs[i].src == MATROXFB_SRC_CRTC2) { return -EBUSY; } } @@ -1018,13 +1025,13 @@ static int matroxfb_ioctl(struct fb_info *info, changes = 0; for (i = 0; i < MATROXFB_MAX_OUTPUTS; i++) { if (tmp & (1 << i)) { - if (ACCESS_FBINFO(outputs[i]).src != MATROXFB_SRC_CRTC1) { + if (minfo->outputs[i].src != MATROXFB_SRC_CRTC1) { changes = 1; - ACCESS_FBINFO(outputs[i]).src = MATROXFB_SRC_CRTC1; + minfo->outputs[i].src = MATROXFB_SRC_CRTC1; } - } else if (ACCESS_FBINFO(outputs[i]).src == MATROXFB_SRC_CRTC1) { + } else if (minfo->outputs[i].src == MATROXFB_SRC_CRTC1) { changes = 1; - ACCESS_FBINFO(outputs[i]).src = MATROXFB_SRC_NONE; + minfo->outputs[i].src = MATROXFB_SRC_NONE; } } if (!changes) @@ -1038,7 +1045,7 @@ static int matroxfb_ioctl(struct fb_info *info, int i; for (i = 0; i < MATROXFB_MAX_OUTPUTS; i++) { - if (ACCESS_FBINFO(outputs[i]).src == MATROXFB_SRC_CRTC1) { + if (minfo->outputs[i].src == MATROXFB_SRC_CRTC1) { conn |= 1 << i; } } @@ -1052,8 +1059,8 @@ static int matroxfb_ioctl(struct fb_info *info, int i; for (i = 0; i < MATROXFB_MAX_OUTPUTS; i++) { - if (ACCESS_FBINFO(outputs[i]).output) { - switch (ACCESS_FBINFO(outputs[i]).src) { + if (minfo->outputs[i].output) { + switch (minfo->outputs[i].src) { case MATROXFB_SRC_NONE: case MATROXFB_SRC_CRTC1: conn |= 1 << i; @@ -1061,7 +1068,7 @@ static int matroxfb_ioctl(struct fb_info *info, } } } - if (ACCESS_FBINFO(devflags.panellink)) { + if (minfo->devflags.panellink) { if (conn & MATROXFB_OUTPUT_CONN_DFP) conn &= ~MATROXFB_OUTPUT_CONN_SECONDARY; if (conn & MATROXFB_OUTPUT_CONN_SECONDARY) @@ -1077,7 +1084,7 @@ static int matroxfb_ioctl(struct fb_info *info, int i; for (i = 0; i < MATROXFB_MAX_OUTPUTS; i++) { - if (ACCESS_FBINFO(outputs[i]).output) { + if (minfo->outputs[i].output) { conn |= 1 << i; } } @@ -1092,7 +1099,7 @@ static int matroxfb_ioctl(struct fb_info *info, memset(&r, 0, sizeof(r)); strcpy(r.driver, "matroxfb"); strcpy(r.card, "Matrox"); - sprintf(r.bus_info, "PCI:%s", pci_name(ACCESS_FBINFO(pcidev))); + sprintf(r.bus_info, "PCI:%s", pci_name(minfo->pcidev)); r.version = KERNEL_VERSION(1,0,0); r.capabilities = V4L2_CAP_VIDEO_OUTPUT; if (copy_to_user(argp, &r, sizeof(r))) @@ -1108,15 +1115,15 @@ static int matroxfb_ioctl(struct fb_info *info, if (copy_from_user(&qctrl, argp, sizeof(qctrl))) return -EFAULT; - down_read(&ACCESS_FBINFO(altout).lock); - if (!ACCESS_FBINFO(outputs[1]).output) { + down_read(&minfo->altout.lock); + if (!minfo->outputs[1].output) { err = -ENXIO; - } else if (ACCESS_FBINFO(outputs[1]).output->getqueryctrl) { - err = ACCESS_FBINFO(outputs[1]).output->getqueryctrl(ACCESS_FBINFO(outputs[1]).data, &qctrl); + } else if (minfo->outputs[1].output->getqueryctrl) { + err = minfo->outputs[1].output->getqueryctrl(minfo->outputs[1].data, &qctrl); } else { err = -EINVAL; } - up_read(&ACCESS_FBINFO(altout).lock); + up_read(&minfo->altout.lock); if (err >= 0 && copy_to_user(argp, &qctrl, sizeof(qctrl))) return -EFAULT; @@ -1130,15 +1137,15 @@ static int matroxfb_ioctl(struct fb_info *info, if (copy_from_user(&ctrl, argp, sizeof(ctrl))) return -EFAULT; - down_read(&ACCESS_FBINFO(altout).lock); - if (!ACCESS_FBINFO(outputs[1]).output) { + down_read(&minfo->altout.lock); + if (!minfo->outputs[1].output) { err = -ENXIO; - } else if (ACCESS_FBINFO(outputs[1]).output->getctrl) { - err = ACCESS_FBINFO(outputs[1]).output->getctrl(ACCESS_FBINFO(outputs[1]).data, &ctrl); + } else if (minfo->outputs[1].output->getctrl) { + err = minfo->outputs[1].output->getctrl(minfo->outputs[1].data, &ctrl); } else { err = -EINVAL; } - up_read(&ACCESS_FBINFO(altout).lock); + up_read(&minfo->altout.lock); if (err >= 0 && copy_to_user(argp, &ctrl, sizeof(ctrl))) return -EFAULT; @@ -1153,15 +1160,15 @@ static int matroxfb_ioctl(struct fb_info *info, if (copy_from_user(&ctrl, argp, sizeof(ctrl))) return -EFAULT; - down_read(&ACCESS_FBINFO(altout).lock); - if (!ACCESS_FBINFO(outputs[1]).output) { + down_read(&minfo->altout.lock); + if (!minfo->outputs[1].output) { err = -ENXIO; - } else if (ACCESS_FBINFO(outputs[1]).output->setctrl) { - err = ACCESS_FBINFO(outputs[1]).output->setctrl(ACCESS_FBINFO(outputs[1]).data, &ctrl); + } else if (minfo->outputs[1].output->setctrl) { + err = minfo->outputs[1].output->setctrl(minfo->outputs[1].data, &ctrl); } else { err = -EINVAL; } - up_read(&ACCESS_FBINFO(altout).lock); + up_read(&minfo->altout.lock); return err; } } @@ -1175,11 +1182,11 @@ static int matroxfb_blank(int blank, struct fb_info *info) int seq; int crtc; CRITFLAGS - MINFO_FROM_INFO(info); + struct matrox_fb_info *minfo = info2minfo(info); DBG(__func__) - if (ACCESS_FBINFO(dead)) + if (minfo->dead) return 1; switch (blank) { @@ -1281,7 +1288,9 @@ static char outputs[8]; /* "matrox:outputs:xxx" */ static char videomode[64]; /* "matrox:mode:xxxxx" or "matrox:xxxxx" */ #endif -static int matroxfb_getmemory(WPMINFO unsigned int maxSize, unsigned int *realSize){ +static int matroxfb_getmemory(struct matrox_fb_info *minfo, + unsigned int maxSize, unsigned int *realSize) +{ vaddr_t vm; unsigned int offs; unsigned int offs2; @@ -1291,7 +1300,7 @@ static int matroxfb_getmemory(WPMINFO unsigned int maxSize, unsigned int *realSi DBG(__func__) - vm = ACCESS_FBINFO(video.vbase); + vm = minfo->video.vbase; maxSize &= ~0x1FFFFF; /* must be X*2MB (really it must be 2 or X*4MB) */ /* at least 2MB */ if (maxSize < 0x0200000) return 0; @@ -1323,7 +1332,7 @@ static int matroxfb_getmemory(WPMINFO unsigned int maxSize, unsigned int *realSi *realSize = offs - 0x100000; #ifdef CONFIG_FB_MATROX_MILLENIUM - ACCESS_FBINFO(interleave) = !(!isMillenium(MINFO) || ((offs - 0x100000) & 0x3FFFFF)); + minfo->interleave = !(!isMillenium(minfo) || ((offs - 0x100000) & 0x3FFFFF)); #endif return 1; } @@ -1345,13 +1354,9 @@ static struct video_board vbMystique = {0x0800000, 0x0800000, FB_ACCEL_MATROX_M #ifdef CONFIG_FB_MATROX_G static struct video_board vbG100 = {0x0800000, 0x0800000, FB_ACCEL_MATROX_MGAG100, &matrox_G100}; static struct video_board vbG200 = {0x1000000, 0x1000000, FB_ACCEL_MATROX_MGAG200, &matrox_G100}; -#ifdef CONFIG_FB_MATROX_32MB /* from doc it looks like that accelerator can draw only to low 16MB :-( Direct accesses & displaying are OK for whole 32MB */ static struct video_board vbG400 = {0x2000000, 0x1000000, FB_ACCEL_MATROX_MGAG400, &matrox_G100}; -#else -static struct video_board vbG400 = {0x2000000, 0x1000000, FB_ACCEL_MATROX_MGAG400, &matrox_G100}; -#endif #endif #define DEVF_VIDEO64BIT 0x0001 @@ -1558,16 +1563,17 @@ static struct fb_videomode defaultmode = { static int hotplug = 0; -static void setDefaultOutputs(WPMINFO2) { +static void setDefaultOutputs(struct matrox_fb_info *minfo) +{ unsigned int i; const char* ptr; - ACCESS_FBINFO(outputs[0]).default_src = MATROXFB_SRC_CRTC1; - if (ACCESS_FBINFO(devflags.g450dac)) { - ACCESS_FBINFO(outputs[1]).default_src = MATROXFB_SRC_CRTC1; - ACCESS_FBINFO(outputs[2]).default_src = MATROXFB_SRC_CRTC1; + minfo->outputs[0].default_src = MATROXFB_SRC_CRTC1; + if (minfo->devflags.g450dac) { + minfo->outputs[1].default_src = MATROXFB_SRC_CRTC1; + minfo->outputs[2].default_src = MATROXFB_SRC_CRTC1; } else if (dfp) { - ACCESS_FBINFO(outputs[2]).default_src = MATROXFB_SRC_CRTC1; + minfo->outputs[2].default_src = MATROXFB_SRC_CRTC1; } ptr = outputs; for (i = 0; i < MATROXFB_MAX_OUTPUTS; i++) { @@ -1577,11 +1583,11 @@ static void setDefaultOutputs(WPMINFO2) { break; } if (c == '0') { - ACCESS_FBINFO(outputs[i]).default_src = MATROXFB_SRC_NONE; + minfo->outputs[i].default_src = MATROXFB_SRC_NONE; } else if (c == '1') { - ACCESS_FBINFO(outputs[i]).default_src = MATROXFB_SRC_CRTC1; - } else if (c == '2' && ACCESS_FBINFO(devflags.crtc2)) { - ACCESS_FBINFO(outputs[i]).default_src = MATROXFB_SRC_CRTC2; + minfo->outputs[i].default_src = MATROXFB_SRC_CRTC1; + } else if (c == '2' && minfo->devflags.crtc2) { + minfo->outputs[i].default_src = MATROXFB_SRC_CRTC2; } else { printk(KERN_ERR "matroxfb: Unknown outputs setting\n"); break; @@ -1591,7 +1597,8 @@ static void setDefaultOutputs(WPMINFO2) { outputs[0] = 0; } -static int initMatrox2(WPMINFO struct board* b){ +static int initMatrox2(struct matrox_fb_info *minfo, struct board *b) +{ unsigned long ctrlptr_phys = 0; unsigned long video_base_phys = 0; unsigned int memsize; @@ -1607,58 +1614,56 @@ static int initMatrox2(WPMINFO struct board* b){ /* set default values... */ vesafb_defined.accel_flags = FB_ACCELF_TEXT; - ACCESS_FBINFO(hw_switch) = b->base->lowlevel; - ACCESS_FBINFO(devflags.accelerator) = b->base->accelID; - ACCESS_FBINFO(max_pixel_clock) = b->maxclk; + minfo->hw_switch = b->base->lowlevel; + minfo->devflags.accelerator = b->base->accelID; + minfo->max_pixel_clock = b->maxclk; printk(KERN_INFO "matroxfb: Matrox %s detected\n", b->name); - ACCESS_FBINFO(capable.plnwt) = 1; - ACCESS_FBINFO(chip) = b->chip; - ACCESS_FBINFO(capable.srcorg) = b->flags & DEVF_SRCORG; - ACCESS_FBINFO(devflags.video64bits) = b->flags & DEVF_VIDEO64BIT; + minfo->capable.plnwt = 1; + minfo->chip = b->chip; + minfo->capable.srcorg = b->flags & DEVF_SRCORG; + minfo->devflags.video64bits = b->flags & DEVF_VIDEO64BIT; if (b->flags & DEVF_TEXT4B) { - ACCESS_FBINFO(devflags.vgastep) = 4; - ACCESS_FBINFO(devflags.textmode) = 4; - ACCESS_FBINFO(devflags.text_type_aux) = FB_AUX_TEXT_MGA_STEP16; + minfo->devflags.vgastep = 4; + minfo->devflags.textmode = 4; + minfo->devflags.text_type_aux = FB_AUX_TEXT_MGA_STEP16; } else if (b->flags & DEVF_TEXT16B) { - ACCESS_FBINFO(devflags.vgastep) = 16; - ACCESS_FBINFO(devflags.textmode) = 1; - ACCESS_FBINFO(devflags.text_type_aux) = FB_AUX_TEXT_MGA_STEP16; + minfo->devflags.vgastep = 16; + minfo->devflags.textmode = 1; + minfo->devflags.text_type_aux = FB_AUX_TEXT_MGA_STEP16; } else { - ACCESS_FBINFO(devflags.vgastep) = 8; - ACCESS_FBINFO(devflags.textmode) = 1; - ACCESS_FBINFO(devflags.text_type_aux) = FB_AUX_TEXT_MGA_STEP8; - } -#ifdef CONFIG_FB_MATROX_32MB - ACCESS_FBINFO(devflags.support32MB) = (b->flags & DEVF_SUPPORT32MB) != 0; -#endif - ACCESS_FBINFO(devflags.precise_width) = !(b->flags & DEVF_ANY_VXRES); - ACCESS_FBINFO(devflags.crtc2) = (b->flags & DEVF_CRTC2) != 0; - ACCESS_FBINFO(devflags.maven_capable) = (b->flags & DEVF_MAVEN_CAPABLE) != 0; - ACCESS_FBINFO(devflags.dualhead) = (b->flags & DEVF_DUALHEAD) != 0; - ACCESS_FBINFO(devflags.dfp_type) = dfp_type; - ACCESS_FBINFO(devflags.g450dac) = (b->flags & DEVF_G450DAC) != 0; - ACCESS_FBINFO(devflags.textstep) = ACCESS_FBINFO(devflags.vgastep) * ACCESS_FBINFO(devflags.textmode); - ACCESS_FBINFO(devflags.textvram) = 65536 / ACCESS_FBINFO(devflags.textmode); - setDefaultOutputs(PMINFO2); + minfo->devflags.vgastep = 8; + minfo->devflags.textmode = 1; + minfo->devflags.text_type_aux = FB_AUX_TEXT_MGA_STEP8; + } + minfo->devflags.support32MB = (b->flags & DEVF_SUPPORT32MB) != 0; + minfo->devflags.precise_width = !(b->flags & DEVF_ANY_VXRES); + minfo->devflags.crtc2 = (b->flags & DEVF_CRTC2) != 0; + minfo->devflags.maven_capable = (b->flags & DEVF_MAVEN_CAPABLE) != 0; + minfo->devflags.dualhead = (b->flags & DEVF_DUALHEAD) != 0; + minfo->devflags.dfp_type = dfp_type; + minfo->devflags.g450dac = (b->flags & DEVF_G450DAC) != 0; + minfo->devflags.textstep = minfo->devflags.vgastep * minfo->devflags.textmode; + minfo->devflags.textvram = 65536 / minfo->devflags.textmode; + setDefaultOutputs(minfo); if (b->flags & DEVF_PANELLINK_CAPABLE) { - ACCESS_FBINFO(outputs[2]).data = MINFO; - ACCESS_FBINFO(outputs[2]).output = &panellink_output; - ACCESS_FBINFO(outputs[2]).src = ACCESS_FBINFO(outputs[2]).default_src; - ACCESS_FBINFO(outputs[2]).mode = MATROXFB_OUTPUT_MODE_MONITOR; - ACCESS_FBINFO(devflags.panellink) = 1; + minfo->outputs[2].data = minfo; + minfo->outputs[2].output = &panellink_output; + minfo->outputs[2].src = minfo->outputs[2].default_src; + minfo->outputs[2].mode = MATROXFB_OUTPUT_MODE_MONITOR; + minfo->devflags.panellink = 1; } - if (ACCESS_FBINFO(capable.cross4MB) < 0) - ACCESS_FBINFO(capable.cross4MB) = b->flags & DEVF_CROSS4MB; + if (minfo->capable.cross4MB < 0) + minfo->capable.cross4MB = b->flags & DEVF_CROSS4MB; if (b->flags & DEVF_SWAPS) { - ctrlptr_phys = pci_resource_start(ACCESS_FBINFO(pcidev), 1); - video_base_phys = pci_resource_start(ACCESS_FBINFO(pcidev), 0); - ACCESS_FBINFO(devflags.fbResource) = PCI_BASE_ADDRESS_0; + ctrlptr_phys = pci_resource_start(minfo->pcidev, 1); + video_base_phys = pci_resource_start(minfo->pcidev, 0); + minfo->devflags.fbResource = PCI_BASE_ADDRESS_0; } else { - ctrlptr_phys = pci_resource_start(ACCESS_FBINFO(pcidev), 0); - video_base_phys = pci_resource_start(ACCESS_FBINFO(pcidev), 1); - ACCESS_FBINFO(devflags.fbResource) = PCI_BASE_ADDRESS_1; + ctrlptr_phys = pci_resource_start(minfo->pcidev, 0); + video_base_phys = pci_resource_start(minfo->pcidev, 1); + minfo->devflags.fbResource = PCI_BASE_ADDRESS_1; } err = -EINVAL; if (!ctrlptr_phys) { @@ -1676,7 +1681,7 @@ static int initMatrox2(WPMINFO struct board* b){ if (!request_mem_region(video_base_phys, memsize, "matroxfb FB")) { goto failCtrlMR; } - ACCESS_FBINFO(video.len_maximum) = memsize; + minfo->video.len_maximum = memsize; /* convert mem (autodetect k, M) */ if (mem < 1024) mem *= 1024; if (mem < 0x00100000) mem *= 1024; @@ -1684,14 +1689,14 @@ static int initMatrox2(WPMINFO struct board* b){ if (mem && (mem < memsize)) memsize = mem; err = -ENOMEM; - if (mga_ioremap(ctrlptr_phys, 16384, MGA_IOREMAP_MMIO, &ACCESS_FBINFO(mmio.vbase))) { + if (mga_ioremap(ctrlptr_phys, 16384, MGA_IOREMAP_MMIO, &minfo->mmio.vbase)) { printk(KERN_ERR "matroxfb: cannot ioremap(%lX, 16384), matroxfb disabled\n", ctrlptr_phys); goto failVideoMR; } - ACCESS_FBINFO(mmio.base) = ctrlptr_phys; - ACCESS_FBINFO(mmio.len) = 16384; - ACCESS_FBINFO(video.base) = video_base_phys; - if (mga_ioremap(video_base_phys, memsize, MGA_IOREMAP_FB, &ACCESS_FBINFO(video.vbase))) { + minfo->mmio.base = ctrlptr_phys; + minfo->mmio.len = 16384; + minfo->video.base = video_base_phys; + if (mga_ioremap(video_base_phys, memsize, MGA_IOREMAP_FB, &minfo->video.vbase)) { printk(KERN_ERR "matroxfb: cannot ioremap(%lX, %d), matroxfb disabled\n", video_base_phys, memsize); goto failCtrlIO; @@ -1700,63 +1705,63 @@ static int initMatrox2(WPMINFO struct board* b){ u_int32_t cmd; u_int32_t mga_option; - pci_read_config_dword(ACCESS_FBINFO(pcidev), PCI_OPTION_REG, &mga_option); - pci_read_config_dword(ACCESS_FBINFO(pcidev), PCI_COMMAND, &cmd); + pci_read_config_dword(minfo->pcidev, PCI_OPTION_REG, &mga_option); + pci_read_config_dword(minfo->pcidev, PCI_COMMAND, &cmd); mga_option &= 0x7FFFFFFF; /* clear BIG_ENDIAN */ mga_option |= MX_OPTION_BSWAP; /* disable palette snooping */ cmd &= ~PCI_COMMAND_VGA_PALETTE; if (pci_dev_present(intel_82437)) { - if (!(mga_option & 0x20000000) && !ACCESS_FBINFO(devflags.nopciretry)) { + if (!(mga_option & 0x20000000) && !minfo->devflags.nopciretry) { printk(KERN_WARNING "matroxfb: Disabling PCI retries due to i82437 present\n"); } mga_option |= 0x20000000; - ACCESS_FBINFO(devflags.nopciretry) = 1; + minfo->devflags.nopciretry = 1; } - pci_write_config_dword(ACCESS_FBINFO(pcidev), PCI_COMMAND, cmd); - pci_write_config_dword(ACCESS_FBINFO(pcidev), PCI_OPTION_REG, mga_option); - ACCESS_FBINFO(hw).MXoptionReg = mga_option; + pci_write_config_dword(minfo->pcidev, PCI_COMMAND, cmd); + pci_write_config_dword(minfo->pcidev, PCI_OPTION_REG, mga_option); + minfo->hw.MXoptionReg = mga_option; /* select non-DMA memory for PCI_MGA_DATA, otherwise dump of PCI cfg space can lock PCI bus */ /* maybe preinit() candidate, but it is same... for all devices... at this time... */ - pci_write_config_dword(ACCESS_FBINFO(pcidev), PCI_MGA_INDEX, 0x00003C00); + pci_write_config_dword(minfo->pcidev, PCI_MGA_INDEX, 0x00003C00); } err = -ENXIO; - matroxfb_read_pins(PMINFO2); - if (ACCESS_FBINFO(hw_switch)->preinit(PMINFO2)) { + matroxfb_read_pins(minfo); + if (minfo->hw_switch->preinit(minfo)) { goto failVideoIO; } err = -ENOMEM; - if (!matroxfb_getmemory(PMINFO memsize, &ACCESS_FBINFO(video.len)) || !ACCESS_FBINFO(video.len)) { + if (!matroxfb_getmemory(minfo, memsize, &minfo->video.len) || !minfo->video.len) { printk(KERN_ERR "matroxfb: cannot determine memory size\n"); goto failVideoIO; } - ACCESS_FBINFO(devflags.ydstorg) = 0; + minfo->devflags.ydstorg = 0; - ACCESS_FBINFO(video.base) = video_base_phys; - ACCESS_FBINFO(video.len_usable) = ACCESS_FBINFO(video.len); - if (ACCESS_FBINFO(video.len_usable) > b->base->maxdisplayable) - ACCESS_FBINFO(video.len_usable) = b->base->maxdisplayable; + minfo->video.base = video_base_phys; + minfo->video.len_usable = minfo->video.len; + if (minfo->video.len_usable > b->base->maxdisplayable) + minfo->video.len_usable = b->base->maxdisplayable; #ifdef CONFIG_MTRR if (mtrr) { - ACCESS_FBINFO(mtrr.vram) = mtrr_add(video_base_phys, ACCESS_FBINFO(video.len), MTRR_TYPE_WRCOMB, 1); - ACCESS_FBINFO(mtrr.vram_valid) = 1; + minfo->mtrr.vram = mtrr_add(video_base_phys, minfo->video.len, MTRR_TYPE_WRCOMB, 1); + minfo->mtrr.vram_valid = 1; printk(KERN_INFO "matroxfb: MTRR's turned on\n"); } #endif /* CONFIG_MTRR */ - if (!ACCESS_FBINFO(devflags.novga)) + if (!minfo->devflags.novga) request_region(0x3C0, 32, "matrox"); - matroxfb_g450_connect(PMINFO2); - ACCESS_FBINFO(hw_switch->reset(PMINFO2)); + matroxfb_g450_connect(minfo); + minfo->hw_switch->reset(minfo); - ACCESS_FBINFO(fbcon.monspecs.hfmin) = 0; - ACCESS_FBINFO(fbcon.monspecs.hfmax) = fh; - ACCESS_FBINFO(fbcon.monspecs.vfmin) = 0; - ACCESS_FBINFO(fbcon.monspecs.vfmax) = fv; - ACCESS_FBINFO(fbcon.monspecs.dpms) = 0; /* TBD */ + minfo->fbcon.monspecs.hfmin = 0; + minfo->fbcon.monspecs.hfmax = fh; + minfo->fbcon.monspecs.vfmin = 0; + minfo->fbcon.monspecs.vfmax = fv; + minfo->fbcon.monspecs.dpms = 0; /* TBD */ /* static settings */ vesafb_defined.red = colors[depth-1].red; @@ -1768,24 +1773,24 @@ static int initMatrox2(WPMINFO struct board* b){ if (noaccel) vesafb_defined.accel_flags &= ~FB_ACCELF_TEXT; - ACCESS_FBINFO(fbops) = matroxfb_ops; - ACCESS_FBINFO(fbcon.fbops) = &ACCESS_FBINFO(fbops); - ACCESS_FBINFO(fbcon.pseudo_palette) = ACCESS_FBINFO(cmap); + minfo->fbops = matroxfb_ops; + minfo->fbcon.fbops = &minfo->fbops; + minfo->fbcon.pseudo_palette = minfo->cmap; /* after __init time we are like module... no logo */ - ACCESS_FBINFO(fbcon.flags) = hotplug ? FBINFO_FLAG_MODULE : FBINFO_FLAG_DEFAULT; - ACCESS_FBINFO(fbcon.flags) |= FBINFO_PARTIAL_PAN_OK | /* Prefer panning for scroll under MC viewer/edit */ + minfo->fbcon.flags = hotplug ? FBINFO_FLAG_MODULE : FBINFO_FLAG_DEFAULT; + minfo->fbcon.flags |= FBINFO_PARTIAL_PAN_OK | /* Prefer panning for scroll under MC viewer/edit */ FBINFO_HWACCEL_COPYAREA | /* We have hw-assisted bmove */ FBINFO_HWACCEL_FILLRECT | /* And fillrect */ FBINFO_HWACCEL_IMAGEBLIT | /* And imageblit */ FBINFO_HWACCEL_XPAN | /* And we support both horizontal */ FBINFO_HWACCEL_YPAN; /* And vertical panning */ - ACCESS_FBINFO(video.len_usable) &= PAGE_MASK; - fb_alloc_cmap(&ACCESS_FBINFO(fbcon.cmap), 256, 1); + minfo->video.len_usable &= PAGE_MASK; + fb_alloc_cmap(&minfo->fbcon.cmap, 256, 1); #ifndef MODULE /* mode database is marked __init!!! */ if (!hotplug) { - fb_find_mode(&vesafb_defined, &ACCESS_FBINFO(fbcon), videomode[0]?videomode:NULL, + fb_find_mode(&vesafb_defined, &minfo->fbcon, videomode[0] ? videomode : NULL, NULL, 0, &defaultmode, vesafb_defined.bits_per_pixel); } #endif /* !MODULE */ @@ -1874,52 +1879,52 @@ static int initMatrox2(WPMINFO struct board* b){ vesafb_defined.yres_virtual = 65536; /* large enough to be INF, but small enough to yres_virtual * xres_virtual < 2^32 */ } - matroxfb_init_fix(PMINFO2); - ACCESS_FBINFO(fbcon.screen_base) = vaddr_va(ACCESS_FBINFO(video.vbase)); + matroxfb_init_fix(minfo); + minfo->fbcon.screen_base = vaddr_va(minfo->video.vbase); /* Normalize values (namely yres_virtual) */ - matroxfb_check_var(&vesafb_defined, &ACCESS_FBINFO(fbcon)); + matroxfb_check_var(&vesafb_defined, &minfo->fbcon); /* And put it into "current" var. Do NOT program hardware yet, or we'll not take over * vgacon correctly. fbcon_startup will call fb_set_par for us, WITHOUT check_var, * and unfortunately it will do it BEFORE vgacon contents is saved, so it won't work * anyway. But we at least tried... */ - ACCESS_FBINFO(fbcon.var) = vesafb_defined; + minfo->fbcon.var = vesafb_defined; err = -EINVAL; printk(KERN_INFO "matroxfb: %dx%dx%dbpp (virtual: %dx%d)\n", vesafb_defined.xres, vesafb_defined.yres, vesafb_defined.bits_per_pixel, vesafb_defined.xres_virtual, vesafb_defined.yres_virtual); printk(KERN_INFO "matroxfb: framebuffer at 0x%lX, mapped to 0x%p, size %d\n", - ACCESS_FBINFO(video.base), vaddr_va(ACCESS_FBINFO(video.vbase)), ACCESS_FBINFO(video.len)); + minfo->video.base, vaddr_va(minfo->video.vbase), minfo->video.len); /* We do not have to set currcon to 0... register_framebuffer do it for us on first console * and we do not want currcon == 0 for subsequent framebuffers */ - ACCESS_FBINFO(fbcon).device = &ACCESS_FBINFO(pcidev)->dev; - if (register_framebuffer(&ACCESS_FBINFO(fbcon)) < 0) { + minfo->fbcon.device = &minfo->pcidev->dev; + if (register_framebuffer(&minfo->fbcon) < 0) { goto failVideoIO; } printk("fb%d: %s frame buffer device\n", - ACCESS_FBINFO(fbcon.node), ACCESS_FBINFO(fbcon.fix.id)); + minfo->fbcon.node, minfo->fbcon.fix.id); /* there is no console on this fb... but we have to initialize hardware * until someone tells me what is proper thing to do */ - if (!ACCESS_FBINFO(initialized)) { + if (!minfo->initialized) { printk(KERN_INFO "fb%d: initializing hardware\n", - ACCESS_FBINFO(fbcon.node)); + minfo->fbcon.node); /* We have to use FB_ACTIVATE_FORCE, as we had to put vesafb_defined to the fbcon.var * already before, so register_framebuffer works correctly. */ vesafb_defined.activate |= FB_ACTIVATE_FORCE; - fb_set_var(&ACCESS_FBINFO(fbcon), &vesafb_defined); + fb_set_var(&minfo->fbcon, &vesafb_defined); } return 0; failVideoIO:; - matroxfb_g450_shutdown(PMINFO2); - mga_iounmap(ACCESS_FBINFO(video.vbase)); + matroxfb_g450_shutdown(minfo); + mga_iounmap(minfo->video.vbase); failCtrlIO:; - mga_iounmap(ACCESS_FBINFO(mmio.vbase)); + mga_iounmap(minfo->mmio.vbase); failVideoMR:; - release_mem_region(video_base_phys, ACCESS_FBINFO(video.len_maximum)); + release_mem_region(video_base_phys, minfo->video.len_maximum); failCtrlMR:; release_mem_region(ctrlptr_phys, 16384); fail:; @@ -1975,7 +1980,7 @@ void matroxfb_unregister_driver(struct matroxfb_driver* drv) { static void matroxfb_register_device(struct matrox_fb_info* minfo) { struct matroxfb_driver* drv; int i = 0; - list_add(&ACCESS_FBINFO(next_fb), &matroxfb_list); + list_add(&minfo->next_fb, &matroxfb_list); for (drv = matroxfb_driver_l(matroxfb_driver_list.next); drv != matroxfb_driver_l(&matroxfb_driver_list); drv = matroxfb_driver_l(drv->node.next)) { @@ -1995,7 +2000,7 @@ static void matroxfb_register_device(struct matrox_fb_info* minfo) { static void matroxfb_unregister_device(struct matrox_fb_info* minfo) { int i; - list_del(&ACCESS_FBINFO(next_fb)); + list_del(&minfo->next_fb); for (i = 0; i < minfo->drivers_count; i++) { struct matroxfb_driver* drv = minfo->drivers[i]; @@ -2011,9 +2016,6 @@ static int matroxfb_probe(struct pci_dev* pdev, const struct pci_device_id* dumm struct matrox_fb_info* minfo; int err; u_int32_t cmd; -#ifndef CONFIG_FB_MATROX_MULTIHEAD - static int registered = 0; -#endif DBG(__func__) svid = pdev->subsystem_vendor; @@ -2037,68 +2039,57 @@ static int matroxfb_probe(struct pci_dev* pdev, const struct pci_device_id* dumm return -1; } -#ifdef CONFIG_FB_MATROX_MULTIHEAD minfo = kmalloc(sizeof(*minfo), GFP_KERNEL); if (!minfo) return -1; -#else - if (registered) /* singlehead driver... */ - return -1; - minfo = &matroxfb_global_mxinfo; -#endif - memset(MINFO, 0, sizeof(*MINFO)); + memset(minfo, 0, sizeof(*minfo)); - ACCESS_FBINFO(pcidev) = pdev; - ACCESS_FBINFO(dead) = 0; - ACCESS_FBINFO(usecount) = 0; - ACCESS_FBINFO(userusecount) = 0; + minfo->pcidev = pdev; + minfo->dead = 0; + minfo->usecount = 0; + minfo->userusecount = 0; - pci_set_drvdata(pdev, MINFO); + pci_set_drvdata(pdev, minfo); /* DEVFLAGS */ - ACCESS_FBINFO(devflags.memtype) = memtype; + minfo->devflags.memtype = memtype; if (memtype != -1) noinit = 0; if (cmd & PCI_COMMAND_MEMORY) { - ACCESS_FBINFO(devflags.novga) = novga; - ACCESS_FBINFO(devflags.nobios) = nobios; - ACCESS_FBINFO(devflags.noinit) = noinit; + minfo->devflags.novga = novga; + minfo->devflags.nobios = nobios; + minfo->devflags.noinit = noinit; /* subsequent heads always needs initialization and must not enable BIOS */ novga = 1; nobios = 1; noinit = 0; } else { - ACCESS_FBINFO(devflags.novga) = 1; - ACCESS_FBINFO(devflags.nobios) = 1; - ACCESS_FBINFO(devflags.noinit) = 0; - } - - ACCESS_FBINFO(devflags.nopciretry) = no_pci_retry; - ACCESS_FBINFO(devflags.mga_24bpp_fix) = inv24; - ACCESS_FBINFO(devflags.precise_width) = option_precise_width; - ACCESS_FBINFO(devflags.sgram) = sgram; - ACCESS_FBINFO(capable.cross4MB) = cross4MB; - - spin_lock_init(&ACCESS_FBINFO(lock.DAC)); - spin_lock_init(&ACCESS_FBINFO(lock.accel)); - init_rwsem(&ACCESS_FBINFO(crtc2.lock)); - init_rwsem(&ACCESS_FBINFO(altout.lock)); - mutex_init(&ACCESS_FBINFO(fbcon).mm_lock); - ACCESS_FBINFO(irq_flags) = 0; - init_waitqueue_head(&ACCESS_FBINFO(crtc1.vsync.wait)); - init_waitqueue_head(&ACCESS_FBINFO(crtc2.vsync.wait)); - ACCESS_FBINFO(crtc1.panpos) = -1; - - err = initMatrox2(PMINFO b); + minfo->devflags.novga = 1; + minfo->devflags.nobios = 1; + minfo->devflags.noinit = 0; + } + + minfo->devflags.nopciretry = no_pci_retry; + minfo->devflags.mga_24bpp_fix = inv24; + minfo->devflags.precise_width = option_precise_width; + minfo->devflags.sgram = sgram; + minfo->capable.cross4MB = cross4MB; + + spin_lock_init(&minfo->lock.DAC); + spin_lock_init(&minfo->lock.accel); + init_rwsem(&minfo->crtc2.lock); + init_rwsem(&minfo->altout.lock); + mutex_init(&minfo->fbcon.mm_lock); + minfo->irq_flags = 0; + init_waitqueue_head(&minfo->crtc1.vsync.wait); + init_waitqueue_head(&minfo->crtc2.vsync.wait); + minfo->crtc1.panpos = -1; + + err = initMatrox2(minfo, b); if (!err) { -#ifndef CONFIG_FB_MATROX_MULTIHEAD - registered = 1; -#endif - matroxfb_register_device(MINFO); + matroxfb_register_device(minfo); return 0; } -#ifdef CONFIG_FB_MATROX_MULTIHEAD kfree(minfo); -#endif return -1; } @@ -2106,7 +2097,7 @@ static void pci_remove_matrox(struct pci_dev* pdev) { struct matrox_fb_info* minfo; minfo = pci_get_drvdata(pdev); - matroxfb_remove(PMINFO 1); + matroxfb_remove(minfo, 1); } static struct pci_device_id matroxfb_devices[] = { @@ -2510,13 +2501,8 @@ module_param(inv24, int, 0); MODULE_PARM_DESC(inv24, "Inverts clock polarity for 24bpp and loop frequency > 100MHz (default=do not invert polarity)"); module_param(inverse, int, 0); MODULE_PARM_DESC(inverse, "Inverse (0 or 1) (default=0)"); -#ifdef CONFIG_FB_MATROX_MULTIHEAD module_param(dev, int, 0); MODULE_PARM_DESC(dev, "Multihead support, attach to device ID (0..N) (default=all working)"); -#else -module_param(dev, int, 0); -MODULE_PARM_DESC(dev, "Multihead support, attach to device ID (0..N) (default=first working)"); -#endif module_param(vesa, int, 0); MODULE_PARM_DESC(vesa, "Startup videomode (0x000-0x1FF) (default=0x101)"); module_param(xres, int, 0); diff --git a/drivers/video/matrox/matroxfb_base.h b/drivers/video/matrox/matroxfb_base.h index 95883236c0cd..f3a4e15672d9 100644 --- a/drivers/video/matrox/matroxfb_base.h +++ b/drivers/video/matrox/matroxfb_base.h @@ -54,9 +54,6 @@ #include "../macmodes.h" #endif -/* always compile support for 32MB... It cost almost nothing */ -#define CONFIG_FB_MATROX_32MB - #ifdef MATROXFB_DEBUG #define DEBUG @@ -464,9 +461,7 @@ struct matrox_fb_info { int nopciretry; int noinit; int sgram; -#ifdef CONFIG_FB_MATROX_32MB int support32MB; -#endif int accelerator; int text_type_aux; @@ -524,47 +519,11 @@ struct matrox_fb_info { #define info2minfo(info) container_of(info, struct matrox_fb_info, fbcon) -#ifdef CONFIG_FB_MATROX_MULTIHEAD -#define ACCESS_FBINFO2(info, x) (info->x) -#define ACCESS_FBINFO(x) ACCESS_FBINFO2(minfo,x) - -#define MINFO minfo - -#define WPMINFO2 struct matrox_fb_info* minfo -#define WPMINFO WPMINFO2 , -#define CPMINFO2 const struct matrox_fb_info* minfo -#define CPMINFO CPMINFO2 , -#define PMINFO2 minfo -#define PMINFO PMINFO2 , - -#define MINFO_FROM(x) struct matrox_fb_info* minfo = x -#else - -extern struct matrox_fb_info matroxfb_global_mxinfo; - -#define ACCESS_FBINFO(x) (matroxfb_global_mxinfo.x) -#define ACCESS_FBINFO2(info, x) (matroxfb_global_mxinfo.x) - -#define MINFO (&matroxfb_global_mxinfo) - -#define WPMINFO2 void -#define WPMINFO -#define CPMINFO2 void -#define CPMINFO -#define PMINFO2 -#define PMINFO - -#define MINFO_FROM(x) - -#endif - -#define MINFO_FROM_INFO(x) MINFO_FROM(info2minfo(x)) - struct matrox_switch { - int (*preinit)(WPMINFO2); - void (*reset)(WPMINFO2); - int (*init)(WPMINFO struct my_timming*); - void (*restore)(WPMINFO2); + int (*preinit)(struct matrox_fb_info *minfo); + void (*reset)(struct matrox_fb_info *minfo); + int (*init)(struct matrox_fb_info *minfo, struct my_timming*); + void (*restore)(struct matrox_fb_info *minfo); }; struct matroxfb_driver { @@ -727,11 +686,11 @@ void matroxfb_unregister_driver(struct matroxfb_driver* drv); #endif #endif -#define mga_inb(addr) mga_readb(ACCESS_FBINFO(mmio.vbase), (addr)) -#define mga_inl(addr) mga_readl(ACCESS_FBINFO(mmio.vbase), (addr)) -#define mga_outb(addr,val) mga_writeb(ACCESS_FBINFO(mmio.vbase), (addr), (val)) -#define mga_outw(addr,val) mga_writew(ACCESS_FBINFO(mmio.vbase), (addr), (val)) -#define mga_outl(addr,val) mga_writel(ACCESS_FBINFO(mmio.vbase), (addr), (val)) +#define mga_inb(addr) mga_readb(minfo->mmio.vbase, (addr)) +#define mga_inl(addr) mga_readl(minfo->mmio.vbase, (addr)) +#define mga_outb(addr,val) mga_writeb(minfo->mmio.vbase, (addr), (val)) +#define mga_outw(addr,val) mga_writew(minfo->mmio.vbase, (addr), (val)) +#define mga_outl(addr,val) mga_writel(minfo->mmio.vbase, (addr), (val)) #define mga_readr(port,idx) (mga_outb((port),(idx)), mga_inb((port)+1)) #define mga_setr(addr,port,val) mga_outw(addr, ((val)<<8) | (port)) @@ -750,19 +709,20 @@ void matroxfb_unregister_driver(struct matroxfb_driver* drv); #define isMilleniumII(x) (0) #endif -#define matroxfb_DAC_lock() spin_lock(&ACCESS_FBINFO(lock.DAC)) -#define matroxfb_DAC_unlock() spin_unlock(&ACCESS_FBINFO(lock.DAC)) -#define matroxfb_DAC_lock_irqsave(flags) spin_lock_irqsave(&ACCESS_FBINFO(lock.DAC),flags) -#define matroxfb_DAC_unlock_irqrestore(flags) spin_unlock_irqrestore(&ACCESS_FBINFO(lock.DAC),flags) -extern void matroxfb_DAC_out(CPMINFO int reg, int val); -extern int matroxfb_DAC_in(CPMINFO int reg); +#define matroxfb_DAC_lock() spin_lock(&minfo->lock.DAC) +#define matroxfb_DAC_unlock() spin_unlock(&minfo->lock.DAC) +#define matroxfb_DAC_lock_irqsave(flags) spin_lock_irqsave(&minfo->lock.DAC, flags) +#define matroxfb_DAC_unlock_irqrestore(flags) spin_unlock_irqrestore(&minfo->lock.DAC, flags) +extern void matroxfb_DAC_out(const struct matrox_fb_info *minfo, int reg, + int val); +extern int matroxfb_DAC_in(const struct matrox_fb_info *minfo, int reg); extern void matroxfb_var2my(struct fb_var_screeninfo* fvsi, struct my_timming* mt); -extern int matroxfb_wait_for_sync(WPMINFO u_int32_t crtc); -extern int matroxfb_enable_irq(WPMINFO int reenable); +extern int matroxfb_wait_for_sync(struct matrox_fb_info *minfo, u_int32_t crtc); +extern int matroxfb_enable_irq(struct matrox_fb_info *minfo, int reenable); #ifdef MATROXFB_USE_SPINLOCKS -#define CRITBEGIN spin_lock_irqsave(&ACCESS_FBINFO(lock.accel), critflags); -#define CRITEND spin_unlock_irqrestore(&ACCESS_FBINFO(lock.accel), critflags); +#define CRITBEGIN spin_lock_irqsave(&minfo->lock.accel, critflags); +#define CRITEND spin_unlock_irqrestore(&minfo->lock.accel, critflags); #define CRITFLAGS unsigned long critflags; #else #define CRITBEGIN diff --git a/drivers/video/matrox/matroxfb_crtc2.c b/drivers/video/matrox/matroxfb_crtc2.c index ebcb5c6b4962..78414baa5a54 100644 --- a/drivers/video/matrox/matroxfb_crtc2.c +++ b/drivers/video/matrox/matroxfb_crtc2.c @@ -65,7 +65,7 @@ static void matroxfb_dh_restore(struct matroxfb_dh_fb_info* m2info, unsigned int pos) { u_int32_t tmp; u_int32_t datactl; - MINFO_FROM(m2info->primary_dev); + struct matrox_fb_info *minfo = m2info->primary_dev; switch (mode) { case 15: @@ -81,11 +81,11 @@ static void matroxfb_dh_restore(struct matroxfb_dh_fb_info* m2info, } tmp |= 0x00000001; /* enable CRTC2 */ datactl = 0; - if (ACCESS_FBINFO(outputs[1]).src == MATROXFB_SRC_CRTC2) { - if (ACCESS_FBINFO(devflags.g450dac)) { + if (minfo->outputs[1].src == MATROXFB_SRC_CRTC2) { + if (minfo->devflags.g450dac) { tmp |= 0x00000006; /* source from secondary pixel PLL */ /* no vidrst when in monitor mode */ - if (ACCESS_FBINFO(outputs[1]).mode != MATROXFB_OUTPUT_MODE_MONITOR) { + if (minfo->outputs[1].mode != MATROXFB_OUTPUT_MODE_MONITOR) { tmp |= 0xC0001000; /* Enable H/V vidrst */ } } else { @@ -93,11 +93,11 @@ static void matroxfb_dh_restore(struct matroxfb_dh_fb_info* m2info, tmp |= 0xC0000000; /* enable vvidrst & hvidrst */ /* MGA TVO is our clock source */ } - } else if (ACCESS_FBINFO(outputs[0]).src == MATROXFB_SRC_CRTC2) { + } else if (minfo->outputs[0].src == MATROXFB_SRC_CRTC2) { tmp |= 0x00000004; /* source from pixclock */ /* PIXPLL is our clock source */ } - if (ACCESS_FBINFO(outputs[0]).src == MATROXFB_SRC_CRTC2) { + if (minfo->outputs[0].src == MATROXFB_SRC_CRTC2) { tmp |= 0x00100000; /* connect CRTC2 to DAC */ } if (mt->interlaced) { @@ -146,7 +146,7 @@ static void matroxfb_dh_restore(struct matroxfb_dh_fb_info* m2info, } } mga_outl(0x3C10, tmp); - ACCESS_FBINFO(hw).crtc2.ctl = tmp; + minfo->hw.crtc2.ctl = tmp; tmp = mt->VDisplay << 16; /* line compare */ if (mt->sync & FB_SYNC_HOR_HIGH_ACT) @@ -157,10 +157,10 @@ static void matroxfb_dh_restore(struct matroxfb_dh_fb_info* m2info, } static void matroxfb_dh_disable(struct matroxfb_dh_fb_info* m2info) { - MINFO_FROM(m2info->primary_dev); + struct matrox_fb_info *minfo = m2info->primary_dev; mga_outl(0x3C10, 0x00000004); /* disable CRTC2, CRTC1->DAC1, PLL as clock source */ - ACCESS_FBINFO(hw).crtc2.ctl = 0x00000004; + minfo->hw.crtc2.ctl = 0x00000004; } static void matroxfb_dh_pan_var(struct matroxfb_dh_fb_info* m2info, @@ -168,7 +168,7 @@ static void matroxfb_dh_pan_var(struct matroxfb_dh_fb_info* m2info, unsigned int pos; unsigned int linelen; unsigned int pixelsize; - MINFO_FROM(m2info->primary_dev); + struct matrox_fb_info *minfo = m2info->primary_dev; m2info->fbcon.var.xoffset = var->xoffset; m2info->fbcon.var.yoffset = var->yoffset; @@ -260,15 +260,15 @@ static int matroxfb_dh_decode_var(struct matroxfb_dh_fb_info* m2info, static int matroxfb_dh_open(struct fb_info* info, int user) { #define m2info (container_of(info, struct matroxfb_dh_fb_info, fbcon)) - MINFO_FROM(m2info->primary_dev); + struct matrox_fb_info *minfo = m2info->primary_dev; - if (MINFO) { + if (minfo) { int err; - if (ACCESS_FBINFO(dead)) { + if (minfo->dead) { return -ENXIO; } - err = ACCESS_FBINFO(fbops).fb_open(&ACCESS_FBINFO(fbcon), user); + err = minfo->fbops.fb_open(&minfo->fbcon, user); if (err) { return err; } @@ -280,10 +280,10 @@ static int matroxfb_dh_open(struct fb_info* info, int user) { static int matroxfb_dh_release(struct fb_info* info, int user) { #define m2info (container_of(info, struct matroxfb_dh_fb_info, fbcon)) int err = 0; - MINFO_FROM(m2info->primary_dev); + struct matrox_fb_info *minfo = m2info->primary_dev; - if (MINFO) { - err = ACCESS_FBINFO(fbops).fb_release(&ACCESS_FBINFO(fbcon), user); + if (minfo) { + err = minfo->fbops.fb_release(&minfo->fbcon, user); } return err; #undef m2info @@ -326,7 +326,7 @@ static int matroxfb_dh_set_par(struct fb_info* info) { int mode; int err; struct fb_var_screeninfo* var = &info->var; - MINFO_FROM(m2info->primary_dev); + struct matrox_fb_info *minfo = m2info->primary_dev; if ((err = matroxfb_dh_decode_var(m2info, var, &visual, &cmap_len, &mode)) != 0) return err; @@ -352,39 +352,39 @@ static int matroxfb_dh_set_par(struct fb_info* info) { pos = (m2info->fbcon.var.yoffset * m2info->fbcon.var.xres_virtual + m2info->fbcon.var.xoffset) * m2info->fbcon.var.bits_per_pixel >> 3; pos += m2info->video.offbase; cnt = 0; - down_read(&ACCESS_FBINFO(altout).lock); + down_read(&minfo->altout.lock); for (out = 0; out < MATROXFB_MAX_OUTPUTS; out++) { - if (ACCESS_FBINFO(outputs[out]).src == MATROXFB_SRC_CRTC2) { + if (minfo->outputs[out].src == MATROXFB_SRC_CRTC2) { cnt++; - if (ACCESS_FBINFO(outputs[out]).output->compute) { - ACCESS_FBINFO(outputs[out]).output->compute(ACCESS_FBINFO(outputs[out]).data, &mt); + if (minfo->outputs[out].output->compute) { + minfo->outputs[out].output->compute(minfo->outputs[out].data, &mt); } } } - ACCESS_FBINFO(crtc2).pixclock = mt.pixclock; - ACCESS_FBINFO(crtc2).mnp = mt.mnp; - up_read(&ACCESS_FBINFO(altout).lock); + minfo->crtc2.pixclock = mt.pixclock; + minfo->crtc2.mnp = mt.mnp; + up_read(&minfo->altout.lock); if (cnt) { matroxfb_dh_restore(m2info, &mt, mode, pos); } else { matroxfb_dh_disable(m2info); } - DAC1064_global_init(PMINFO2); - DAC1064_global_restore(PMINFO2); - down_read(&ACCESS_FBINFO(altout).lock); + DAC1064_global_init(minfo); + DAC1064_global_restore(minfo); + down_read(&minfo->altout.lock); for (out = 0; out < MATROXFB_MAX_OUTPUTS; out++) { - if (ACCESS_FBINFO(outputs[out]).src == MATROXFB_SRC_CRTC2 && - ACCESS_FBINFO(outputs[out]).output->program) { - ACCESS_FBINFO(outputs[out]).output->program(ACCESS_FBINFO(outputs[out]).data); + if (minfo->outputs[out].src == MATROXFB_SRC_CRTC2 && + minfo->outputs[out].output->program) { + minfo->outputs[out].output->program(minfo->outputs[out].data); } } for (out = 0; out < MATROXFB_MAX_OUTPUTS; out++) { - if (ACCESS_FBINFO(outputs[out]).src == MATROXFB_SRC_CRTC2 && - ACCESS_FBINFO(outputs[out]).output->start) { - ACCESS_FBINFO(outputs[out]).output->start(ACCESS_FBINFO(outputs[out]).data); + if (minfo->outputs[out].src == MATROXFB_SRC_CRTC2 && + minfo->outputs[out].output->start) { + minfo->outputs[out].output->start(minfo->outputs[out].data); } } - up_read(&ACCESS_FBINFO(altout).lock); + up_read(&minfo->altout.lock); } m2info->initialized = 1; return 0; @@ -399,9 +399,9 @@ static int matroxfb_dh_pan_display(struct fb_var_screeninfo* var, struct fb_info } static int matroxfb_dh_get_vblank(const struct matroxfb_dh_fb_info* m2info, struct fb_vblank* vblank) { - MINFO_FROM(m2info->primary_dev); + struct matrox_fb_info *minfo = m2info->primary_dev; - matroxfb_enable_irq(PMINFO 0); + matroxfb_enable_irq(minfo, 0); memset(vblank, 0, sizeof(*vblank)); vblank->flags = FB_VBLANK_HAVE_VCOUNT | FB_VBLANK_HAVE_VBLANK; /* mask out reserved bits + field number (odd/even) */ @@ -409,11 +409,11 @@ static int matroxfb_dh_get_vblank(const struct matroxfb_dh_fb_info* m2info, stru /* compatibility stuff */ if (vblank->vcount >= m2info->fbcon.var.yres) vblank->flags |= FB_VBLANK_VBLANKING; - if (test_bit(0, &ACCESS_FBINFO(irq_flags))) { + if (test_bit(0, &minfo->irq_flags)) { vblank->flags |= FB_VBLANK_HAVE_COUNT; /* Only one writer, aligned int value... it should work without lock and without atomic_t */ - vblank->count = ACCESS_FBINFO(crtc2).vsync.cnt; + vblank->count = minfo->crtc2.vsync.cnt; } return 0; } @@ -423,7 +423,7 @@ static int matroxfb_dh_ioctl(struct fb_info *info, unsigned long arg) { #define m2info (container_of(info, struct matroxfb_dh_fb_info, fbcon)) - MINFO_FROM(m2info->primary_dev); + struct matrox_fb_info *minfo = m2info->primary_dev; DBG(__func__) @@ -449,13 +449,13 @@ static int matroxfb_dh_ioctl(struct fb_info *info, if (crt != 0) return -ENODEV; - return matroxfb_wait_for_sync(PMINFO 1); + return matroxfb_wait_for_sync(minfo, 1); } case MATROXFB_SET_OUTPUT_MODE: case MATROXFB_GET_OUTPUT_MODE: case MATROXFB_GET_ALL_OUTPUTS: { - return ACCESS_FBINFO(fbcon.fbops)->fb_ioctl(&ACCESS_FBINFO(fbcon), cmd, arg); + return minfo->fbcon.fbops->fb_ioctl(&minfo->fbcon, cmd, arg); } case MATROXFB_SET_OUTPUT_CONNECTION: { @@ -469,9 +469,9 @@ static int matroxfb_dh_ioctl(struct fb_info *info, if (tmp & (1 << out)) { if (out >= MATROXFB_MAX_OUTPUTS) return -ENXIO; - if (!ACCESS_FBINFO(outputs[out]).output) + if (!minfo->outputs[out].output) return -ENXIO; - switch (ACCESS_FBINFO(outputs[out]).src) { + switch (minfo->outputs[out].src) { case MATROXFB_SRC_NONE: case MATROXFB_SRC_CRTC2: break; @@ -480,22 +480,22 @@ static int matroxfb_dh_ioctl(struct fb_info *info, } } } - if (ACCESS_FBINFO(devflags.panellink)) { + if (minfo->devflags.panellink) { if (tmp & MATROXFB_OUTPUT_CONN_DFP) return -EINVAL; - if ((ACCESS_FBINFO(outputs[2]).src == MATROXFB_SRC_CRTC1) && tmp) + if ((minfo->outputs[2].src == MATROXFB_SRC_CRTC1) && tmp) return -EBUSY; } changes = 0; for (out = 0; out < MATROXFB_MAX_OUTPUTS; out++) { if (tmp & (1 << out)) { - if (ACCESS_FBINFO(outputs[out]).src != MATROXFB_SRC_CRTC2) { + if (minfo->outputs[out].src != MATROXFB_SRC_CRTC2) { changes = 1; - ACCESS_FBINFO(outputs[out]).src = MATROXFB_SRC_CRTC2; + minfo->outputs[out].src = MATROXFB_SRC_CRTC2; } - } else if (ACCESS_FBINFO(outputs[out]).src == MATROXFB_SRC_CRTC2) { + } else if (minfo->outputs[out].src == MATROXFB_SRC_CRTC2) { changes = 1; - ACCESS_FBINFO(outputs[out]).src = MATROXFB_SRC_NONE; + minfo->outputs[out].src = MATROXFB_SRC_NONE; } } if (!changes) @@ -509,7 +509,7 @@ static int matroxfb_dh_ioctl(struct fb_info *info, int out; for (out = 0; out < MATROXFB_MAX_OUTPUTS; out++) { - if (ACCESS_FBINFO(outputs[out]).src == MATROXFB_SRC_CRTC2) { + if (minfo->outputs[out].src == MATROXFB_SRC_CRTC2) { conn |= 1 << out; } } @@ -523,8 +523,8 @@ static int matroxfb_dh_ioctl(struct fb_info *info, int out; for (out = 0; out < MATROXFB_MAX_OUTPUTS; out++) { - if (ACCESS_FBINFO(outputs[out]).output) { - switch (ACCESS_FBINFO(outputs[out]).src) { + if (minfo->outputs[out].output) { + switch (minfo->outputs[out].src) { case MATROXFB_SRC_NONE: case MATROXFB_SRC_CRTC2: tmp |= 1 << out; @@ -532,9 +532,9 @@ static int matroxfb_dh_ioctl(struct fb_info *info, } } } - if (ACCESS_FBINFO(devflags.panellink)) { + if (minfo->devflags.panellink) { tmp &= ~MATROXFB_OUTPUT_CONN_DFP; - if (ACCESS_FBINFO(outputs[2]).src == MATROXFB_SRC_CRTC1) { + if (minfo->outputs[2].src == MATROXFB_SRC_CRTC1) { tmp = 0; } } @@ -595,7 +595,9 @@ static struct fb_var_screeninfo matroxfb_dh_defined = { 0, {0,0,0,0,0} }; -static int matroxfb_dh_regit(CPMINFO struct matroxfb_dh_fb_info* m2info) { +static int matroxfb_dh_regit(const struct matrox_fb_info *minfo, + struct matroxfb_dh_fb_info *m2info) +{ #define minfo (m2info->primary_dev) void* oldcrtc2; @@ -611,21 +613,21 @@ static int matroxfb_dh_regit(CPMINFO struct matroxfb_dh_fb_info* m2info) { if (mem < 64*1024) mem *= 1024; mem &= ~0x00000FFF; /* PAGE_MASK? */ - if (ACCESS_FBINFO(video.len_usable) + mem <= ACCESS_FBINFO(video.len)) - m2info->video.offbase = ACCESS_FBINFO(video.len) - mem; - else if (ACCESS_FBINFO(video.len) < mem) { + if (minfo->video.len_usable + mem <= minfo->video.len) + m2info->video.offbase = minfo->video.len - mem; + else if (minfo->video.len < mem) { return -ENOMEM; } else { /* check yres on first head... */ m2info->video.borrowed = mem; - ACCESS_FBINFO(video.len_usable) -= mem; - m2info->video.offbase = ACCESS_FBINFO(video.len_usable); + minfo->video.len_usable -= mem; + m2info->video.offbase = minfo->video.len_usable; } - m2info->video.base = ACCESS_FBINFO(video.base) + m2info->video.offbase; + m2info->video.base = minfo->video.base + m2info->video.offbase; m2info->video.len = m2info->video.len_usable = m2info->video.len_maximum = mem; - m2info->video.vbase.vaddr = vaddr_va(ACCESS_FBINFO(video.vbase)) + m2info->video.offbase; - m2info->mmio.base = ACCESS_FBINFO(mmio.base); - m2info->mmio.vbase = ACCESS_FBINFO(mmio.vbase); - m2info->mmio.len = ACCESS_FBINFO(mmio.len); + m2info->video.vbase.vaddr = vaddr_va(minfo->video.vbase) + m2info->video.offbase; + m2info->mmio.base = minfo->mmio.base; + m2info->mmio.vbase = minfo->mmio.vbase; + m2info->mmio.len = minfo->mmio.len; matroxfb_dh_init_fix(m2info); if (register_framebuffer(&m2info->fbcon)) { @@ -633,10 +635,10 @@ static int matroxfb_dh_regit(CPMINFO struct matroxfb_dh_fb_info* m2info) { } if (!m2info->initialized) fb_set_var(&m2info->fbcon, &matroxfb_dh_defined); - down_write(&ACCESS_FBINFO(crtc2.lock)); - oldcrtc2 = ACCESS_FBINFO(crtc2.info); - ACCESS_FBINFO(crtc2.info) = m2info; - up_write(&ACCESS_FBINFO(crtc2.lock)); + down_write(&minfo->crtc2.lock); + oldcrtc2 = minfo->crtc2.info; + minfo->crtc2.info = m2info; + up_write(&minfo->crtc2.lock); if (oldcrtc2) { printk(KERN_ERR "matroxfb_crtc2: Internal consistency check failed: crtc2 already present: %p\n", oldcrtc2); @@ -649,12 +651,12 @@ static int matroxfb_dh_regit(CPMINFO struct matroxfb_dh_fb_info* m2info) { static int matroxfb_dh_registerfb(struct matroxfb_dh_fb_info* m2info) { #define minfo (m2info->primary_dev) - if (matroxfb_dh_regit(PMINFO m2info)) { + if (matroxfb_dh_regit(minfo, m2info)) { printk(KERN_ERR "matroxfb_crtc2: secondary head failed to register\n"); return -1; } printk(KERN_INFO "matroxfb_crtc2: secondary head of fb%u was registered as fb%u\n", - ACCESS_FBINFO(fbcon.node), m2info->fbcon.node); + minfo->fbcon.node, m2info->fbcon.node); m2info->fbcon_registered = 1; return 0; #undef minfo @@ -666,11 +668,11 @@ static void matroxfb_dh_deregisterfb(struct matroxfb_dh_fb_info* m2info) { int id; struct matroxfb_dh_fb_info* crtc2; - down_write(&ACCESS_FBINFO(crtc2.lock)); - crtc2 = ACCESS_FBINFO(crtc2.info); + down_write(&minfo->crtc2.lock); + crtc2 = minfo->crtc2.info; if (crtc2 == m2info) - ACCESS_FBINFO(crtc2.info) = NULL; - up_write(&ACCESS_FBINFO(crtc2.lock)); + minfo->crtc2.info = NULL; + up_write(&minfo->crtc2.lock); if (crtc2 != m2info) { printk(KERN_ERR "matroxfb_crtc2: Internal consistency check failed: crtc2 mismatch at unload: %p != %p\n", crtc2, m2info); @@ -680,7 +682,7 @@ static void matroxfb_dh_deregisterfb(struct matroxfb_dh_fb_info* m2info) { id = m2info->fbcon.node; unregister_framebuffer(&m2info->fbcon); /* return memory back to primary head */ - ACCESS_FBINFO(video.len_usable) += m2info->video.borrowed; + minfo->video.len_usable += m2info->video.borrowed; printk(KERN_INFO "matroxfb_crtc2: fb%u unregistered\n", id); m2info->fbcon_registered = 0; } @@ -691,14 +693,14 @@ static void* matroxfb_crtc2_probe(struct matrox_fb_info* minfo) { struct matroxfb_dh_fb_info* m2info; /* hardware is CRTC2 incapable... */ - if (!ACCESS_FBINFO(devflags.crtc2)) + if (!minfo->devflags.crtc2) return NULL; m2info = kzalloc(sizeof(*m2info), GFP_KERNEL); if (!m2info) { printk(KERN_ERR "matroxfb_crtc2: Not enough memory for CRTC2 control structs\n"); return NULL; } - m2info->primary_dev = MINFO; + m2info->primary_dev = minfo; if (matroxfb_dh_registerfb(m2info)) { kfree(m2info); printk(KERN_ERR "matroxfb_crtc2: CRTC2 framebuffer failed to register\n"); diff --git a/drivers/video/matrox/matroxfb_g450.c b/drivers/video/matrox/matroxfb_g450.c index 6209a761f674..cff0546ea6fd 100644 --- a/drivers/video/matrox/matroxfb_g450.c +++ b/drivers/video/matrox/matroxfb_g450.c @@ -80,52 +80,59 @@ static int get_ctrl_id(__u32 v4l2_id) { return -EINVAL; } -static inline int* get_ctrl_ptr(WPMINFO unsigned int idx) { - return (int*)((char*)MINFO + g450_controls[idx].control); +static inline int *get_ctrl_ptr(struct matrox_fb_info *minfo, unsigned int idx) +{ + return (int*)((char*)minfo + g450_controls[idx].control); } -static void tvo_fill_defaults(WPMINFO2) { +static void tvo_fill_defaults(struct matrox_fb_info *minfo) +{ unsigned int i; for (i = 0; i < G450CTRLS; i++) { - *get_ctrl_ptr(PMINFO i) = g450_controls[i].desc.default_value; + *get_ctrl_ptr(minfo, i) = g450_controls[i].desc.default_value; } } -static int cve2_get_reg(WPMINFO int reg) { +static int cve2_get_reg(struct matrox_fb_info *minfo, int reg) +{ unsigned long flags; int val; matroxfb_DAC_lock_irqsave(flags); - matroxfb_DAC_out(PMINFO 0x87, reg); - val = matroxfb_DAC_in(PMINFO 0x88); + matroxfb_DAC_out(minfo, 0x87, reg); + val = matroxfb_DAC_in(minfo, 0x88); matroxfb_DAC_unlock_irqrestore(flags); return val; } -static void cve2_set_reg(WPMINFO int reg, int val) { +static void cve2_set_reg(struct matrox_fb_info *minfo, int reg, int val) +{ unsigned long flags; matroxfb_DAC_lock_irqsave(flags); - matroxfb_DAC_out(PMINFO 0x87, reg); - matroxfb_DAC_out(PMINFO 0x88, val); + matroxfb_DAC_out(minfo, 0x87, reg); + matroxfb_DAC_out(minfo, 0x88, val); matroxfb_DAC_unlock_irqrestore(flags); } -static void cve2_set_reg10(WPMINFO int reg, int val) { +static void cve2_set_reg10(struct matrox_fb_info *minfo, int reg, int val) +{ unsigned long flags; matroxfb_DAC_lock_irqsave(flags); - matroxfb_DAC_out(PMINFO 0x87, reg); - matroxfb_DAC_out(PMINFO 0x88, val >> 2); - matroxfb_DAC_out(PMINFO 0x87, reg + 1); - matroxfb_DAC_out(PMINFO 0x88, val & 3); + matroxfb_DAC_out(minfo, 0x87, reg); + matroxfb_DAC_out(minfo, 0x88, val >> 2); + matroxfb_DAC_out(minfo, 0x87, reg + 1); + matroxfb_DAC_out(minfo, 0x88, val & 3); matroxfb_DAC_unlock_irqrestore(flags); } -static void g450_compute_bwlevel(CPMINFO int *bl, int *wl) { - const int b = ACCESS_FBINFO(altout.tvo_params.brightness) + BLMIN; - const int c = ACCESS_FBINFO(altout.tvo_params.contrast); +static void g450_compute_bwlevel(const struct matrox_fb_info *minfo, int *bl, + int *wl) +{ + const int b = minfo->altout.tvo_params.brightness + BLMIN; + const int c = minfo->altout.tvo_params.contrast; *bl = max(b - c, BLMIN); *wl = min(b + c, WLMAX); @@ -154,7 +161,7 @@ static int g450_query_ctrl(void* md, struct v4l2_queryctrl *p) { static int g450_set_ctrl(void* md, struct v4l2_control *p) { int i; - MINFO_FROM(md); + struct matrox_fb_info *minfo = md; i = get_ctrl_id(p->id); if (i < 0) return -EINVAL; @@ -162,7 +169,7 @@ static int g450_set_ctrl(void* md, struct v4l2_control *p) { /* * Check if changed. */ - if (p->value == *get_ctrl_ptr(PMINFO i)) return 0; + if (p->value == *get_ctrl_ptr(minfo, i)) return 0; /* * Check limits. @@ -173,31 +180,31 @@ static int g450_set_ctrl(void* md, struct v4l2_control *p) { /* * Store new value. */ - *get_ctrl_ptr(PMINFO i) = p->value; + *get_ctrl_ptr(minfo, i) = p->value; switch (p->id) { case V4L2_CID_BRIGHTNESS: case V4L2_CID_CONTRAST: { int blacklevel, whitelevel; - g450_compute_bwlevel(PMINFO &blacklevel, &whitelevel); - cve2_set_reg10(PMINFO 0x0e, blacklevel); - cve2_set_reg10(PMINFO 0x1e, whitelevel); + g450_compute_bwlevel(minfo, &blacklevel, &whitelevel); + cve2_set_reg10(minfo, 0x0e, blacklevel); + cve2_set_reg10(minfo, 0x1e, whitelevel); } break; case V4L2_CID_SATURATION: - cve2_set_reg(PMINFO 0x20, p->value); - cve2_set_reg(PMINFO 0x22, p->value); + cve2_set_reg(minfo, 0x20, p->value); + cve2_set_reg(minfo, 0x22, p->value); break; case V4L2_CID_HUE: - cve2_set_reg(PMINFO 0x25, p->value); + cve2_set_reg(minfo, 0x25, p->value); break; case MATROXFB_CID_TESTOUT: { - unsigned char val = cve2_get_reg (PMINFO 0x05); + unsigned char val = cve2_get_reg(minfo, 0x05); if (p->value) val |= 0x02; else val &= ~0x02; - cve2_set_reg(PMINFO 0x05, val); + cve2_set_reg(minfo, 0x05, val); } break; } @@ -208,11 +215,11 @@ static int g450_set_ctrl(void* md, struct v4l2_control *p) { static int g450_get_ctrl(void* md, struct v4l2_control *p) { int i; - MINFO_FROM(md); + struct matrox_fb_info *minfo = md; i = get_ctrl_id(p->id); if (i < 0) return -EINVAL; - p->value = *get_ctrl_ptr(PMINFO i); + p->value = *get_ctrl_ptr(minfo, i); return 0; } @@ -226,7 +233,9 @@ struct output_desc { unsigned int v_total; }; -static void computeRegs(WPMINFO struct mavenregs* r, struct my_timming* mt, const struct output_desc* outd) { +static void computeRegs(struct matrox_fb_info *minfo, struct mavenregs *r, + struct my_timming *mt, const struct output_desc *outd) +{ u_int32_t chromasc; u_int32_t hlen; u_int32_t hsl; @@ -251,10 +260,10 @@ static void computeRegs(WPMINFO struct mavenregs* r, struct my_timming* mt, cons dprintk(KERN_DEBUG "Want %u kHz pixclock\n", (unsigned int)piic); - mnp = matroxfb_g450_setclk(PMINFO piic, M_VIDEO_PLL); + mnp = matroxfb_g450_setclk(minfo, piic, M_VIDEO_PLL); mt->mnp = mnp; - mt->pixclock = g450_mnp2f(PMINFO mnp); + mt->pixclock = g450_mnp2f(minfo, mnp); dprintk(KERN_DEBUG "MNP=%08X\n", mnp); @@ -490,65 +499,67 @@ static void cve2_init_TVdata(int norm, struct mavenregs* data, const struct outp return; } -#define LR(x) cve2_set_reg(PMINFO (x), m->regs[(x)]) -static void cve2_init_TV(WPMINFO const struct mavenregs* m) { +#define LR(x) cve2_set_reg(minfo, (x), m->regs[(x)]) +static void cve2_init_TV(struct matrox_fb_info *minfo, + const struct mavenregs *m) +{ int i; LR(0x80); LR(0x82); LR(0x83); LR(0x84); LR(0x85); - cve2_set_reg(PMINFO 0x3E, 0x01); + cve2_set_reg(minfo, 0x3E, 0x01); for (i = 0; i < 0x3E; i++) { LR(i); } - cve2_set_reg(PMINFO 0x3E, 0x00); + cve2_set_reg(minfo, 0x3E, 0x00); } static int matroxfb_g450_compute(void* md, struct my_timming* mt) { - MINFO_FROM(md); + struct matrox_fb_info *minfo = md; - dprintk(KERN_DEBUG "Computing, mode=%u\n", ACCESS_FBINFO(outputs[1]).mode); + dprintk(KERN_DEBUG "Computing, mode=%u\n", minfo->outputs[1].mode); if (mt->crtc == MATROXFB_SRC_CRTC2 && - ACCESS_FBINFO(outputs[1]).mode != MATROXFB_OUTPUT_MODE_MONITOR) { + minfo->outputs[1].mode != MATROXFB_OUTPUT_MODE_MONITOR) { const struct output_desc* outd; - cve2_init_TVdata(ACCESS_FBINFO(outputs[1]).mode, &ACCESS_FBINFO(hw).maven, &outd); + cve2_init_TVdata(minfo->outputs[1].mode, &minfo->hw.maven, &outd); { int blacklevel, whitelevel; - g450_compute_bwlevel(PMINFO &blacklevel, &whitelevel); - ACCESS_FBINFO(hw).maven.regs[0x0E] = blacklevel >> 2; - ACCESS_FBINFO(hw).maven.regs[0x0F] = blacklevel & 3; - ACCESS_FBINFO(hw).maven.regs[0x1E] = whitelevel >> 2; - ACCESS_FBINFO(hw).maven.regs[0x1F] = whitelevel & 3; + g450_compute_bwlevel(minfo, &blacklevel, &whitelevel); + minfo->hw.maven.regs[0x0E] = blacklevel >> 2; + minfo->hw.maven.regs[0x0F] = blacklevel & 3; + minfo->hw.maven.regs[0x1E] = whitelevel >> 2; + minfo->hw.maven.regs[0x1F] = whitelevel & 3; - ACCESS_FBINFO(hw).maven.regs[0x20] = - ACCESS_FBINFO(hw).maven.regs[0x22] = ACCESS_FBINFO(altout.tvo_params.saturation); + minfo->hw.maven.regs[0x20] = + minfo->hw.maven.regs[0x22] = minfo->altout.tvo_params.saturation; - ACCESS_FBINFO(hw).maven.regs[0x25] = ACCESS_FBINFO(altout.tvo_params.hue); + minfo->hw.maven.regs[0x25] = minfo->altout.tvo_params.hue; - if (ACCESS_FBINFO(altout.tvo_params.testout)) { - ACCESS_FBINFO(hw).maven.regs[0x05] |= 0x02; + if (minfo->altout.tvo_params.testout) { + minfo->hw.maven.regs[0x05] |= 0x02; } } - computeRegs(PMINFO &ACCESS_FBINFO(hw).maven, mt, outd); + computeRegs(minfo, &minfo->hw.maven, mt, outd); } else if (mt->mnp < 0) { /* We must program clocks before CRTC2, otherwise interlaced mode startup may fail */ - mt->mnp = matroxfb_g450_setclk(PMINFO mt->pixclock, (mt->crtc == MATROXFB_SRC_CRTC1) ? M_PIXEL_PLL_C : M_VIDEO_PLL); - mt->pixclock = g450_mnp2f(PMINFO mt->mnp); + mt->mnp = matroxfb_g450_setclk(minfo, mt->pixclock, (mt->crtc == MATROXFB_SRC_CRTC1) ? M_PIXEL_PLL_C : M_VIDEO_PLL); + mt->pixclock = g450_mnp2f(minfo, mt->mnp); } dprintk(KERN_DEBUG "Pixclock = %u\n", mt->pixclock); return 0; } static int matroxfb_g450_program(void* md) { - MINFO_FROM(md); + struct matrox_fb_info *minfo = md; - if (ACCESS_FBINFO(outputs[1]).mode != MATROXFB_OUTPUT_MODE_MONITOR) { - cve2_init_TV(PMINFO &ACCESS_FBINFO(hw).maven); + if (minfo->outputs[1].mode != MATROXFB_OUTPUT_MODE_MONITOR) { + cve2_init_TV(minfo, &minfo->hw.maven); } return 0; } @@ -564,11 +575,11 @@ static int matroxfb_g450_verify_mode(void* md, u_int32_t arg) { } static int g450_dvi_compute(void* md, struct my_timming* mt) { - MINFO_FROM(md); + struct matrox_fb_info *minfo = md; if (mt->mnp < 0) { - mt->mnp = matroxfb_g450_setclk(PMINFO mt->pixclock, (mt->crtc == MATROXFB_SRC_CRTC1) ? M_PIXEL_PLL_C : M_VIDEO_PLL); - mt->pixclock = g450_mnp2f(PMINFO mt->mnp); + mt->mnp = matroxfb_g450_setclk(minfo, mt->pixclock, (mt->crtc == MATROXFB_SRC_CRTC1) ? M_PIXEL_PLL_C : M_VIDEO_PLL); + mt->pixclock = g450_mnp2f(minfo, mt->mnp); } return 0; } @@ -588,34 +599,36 @@ static struct matrox_altout matroxfb_g450_dvi = { .compute = g450_dvi_compute, }; -void matroxfb_g450_connect(WPMINFO2) { - if (ACCESS_FBINFO(devflags.g450dac)) { - down_write(&ACCESS_FBINFO(altout.lock)); - tvo_fill_defaults(PMINFO2); - ACCESS_FBINFO(outputs[1]).src = ACCESS_FBINFO(outputs[1]).default_src; - ACCESS_FBINFO(outputs[1]).data = MINFO; - ACCESS_FBINFO(outputs[1]).output = &matroxfb_g450_altout; - ACCESS_FBINFO(outputs[1]).mode = MATROXFB_OUTPUT_MODE_MONITOR; - ACCESS_FBINFO(outputs[2]).src = ACCESS_FBINFO(outputs[2]).default_src; - ACCESS_FBINFO(outputs[2]).data = MINFO; - ACCESS_FBINFO(outputs[2]).output = &matroxfb_g450_dvi; - ACCESS_FBINFO(outputs[2]).mode = MATROXFB_OUTPUT_MODE_MONITOR; - up_write(&ACCESS_FBINFO(altout.lock)); +void matroxfb_g450_connect(struct matrox_fb_info *minfo) +{ + if (minfo->devflags.g450dac) { + down_write(&minfo->altout.lock); + tvo_fill_defaults(minfo); + minfo->outputs[1].src = minfo->outputs[1].default_src; + minfo->outputs[1].data = minfo; + minfo->outputs[1].output = &matroxfb_g450_altout; + minfo->outputs[1].mode = MATROXFB_OUTPUT_MODE_MONITOR; + minfo->outputs[2].src = minfo->outputs[2].default_src; + minfo->outputs[2].data = minfo; + minfo->outputs[2].output = &matroxfb_g450_dvi; + minfo->outputs[2].mode = MATROXFB_OUTPUT_MODE_MONITOR; + up_write(&minfo->altout.lock); } } -void matroxfb_g450_shutdown(WPMINFO2) { - if (ACCESS_FBINFO(devflags.g450dac)) { - down_write(&ACCESS_FBINFO(altout.lock)); - ACCESS_FBINFO(outputs[1]).src = MATROXFB_SRC_NONE; - ACCESS_FBINFO(outputs[1]).output = NULL; - ACCESS_FBINFO(outputs[1]).data = NULL; - ACCESS_FBINFO(outputs[1]).mode = MATROXFB_OUTPUT_MODE_MONITOR; - ACCESS_FBINFO(outputs[2]).src = MATROXFB_SRC_NONE; - ACCESS_FBINFO(outputs[2]).output = NULL; - ACCESS_FBINFO(outputs[2]).data = NULL; - ACCESS_FBINFO(outputs[2]).mode = MATROXFB_OUTPUT_MODE_MONITOR; - up_write(&ACCESS_FBINFO(altout.lock)); +void matroxfb_g450_shutdown(struct matrox_fb_info *minfo) +{ + if (minfo->devflags.g450dac) { + down_write(&minfo->altout.lock); + minfo->outputs[1].src = MATROXFB_SRC_NONE; + minfo->outputs[1].output = NULL; + minfo->outputs[1].data = NULL; + minfo->outputs[1].mode = MATROXFB_OUTPUT_MODE_MONITOR; + minfo->outputs[2].src = MATROXFB_SRC_NONE; + minfo->outputs[2].output = NULL; + minfo->outputs[2].data = NULL; + minfo->outputs[2].mode = MATROXFB_OUTPUT_MODE_MONITOR; + up_write(&minfo->altout.lock); } } diff --git a/drivers/video/matrox/matroxfb_g450.h b/drivers/video/matrox/matroxfb_g450.h index a0822a6033e5..3a3e654444b8 100644 --- a/drivers/video/matrox/matroxfb_g450.h +++ b/drivers/video/matrox/matroxfb_g450.h @@ -4,11 +4,11 @@ #include "matroxfb_base.h" #ifdef CONFIG_FB_MATROX_G -void matroxfb_g450_connect(WPMINFO2); -void matroxfb_g450_shutdown(WPMINFO2); +void matroxfb_g450_connect(struct matrox_fb_info *minfo); +void matroxfb_g450_shutdown(struct matrox_fb_info *minfo); #else -static inline void matroxfb_g450_connect(WPMINFO2) { }; -static inline void matroxfb_g450_shutdown(WPMINFO2) { }; +static inline void matroxfb_g450_connect(struct matrox_fb_info *minfo) { }; +static inline void matroxfb_g450_shutdown(struct matrox_fb_info *minfo) { }; #endif #endif /* __MATROXFB_G450_H__ */ diff --git a/drivers/video/matrox/matroxfb_maven.c b/drivers/video/matrox/matroxfb_maven.c index 042408a8c631..91af9159111f 100644 --- a/drivers/video/matrox/matroxfb_maven.c +++ b/drivers/video/matrox/matroxfb_maven.c @@ -458,9 +458,9 @@ static void maven_init_TVdata(const struct maven_data* md, struct mavenregs* dat 0x00, /* 3E written multiple times */ 0x00, /* never written */ }, MATROXFB_OUTPUT_MODE_NTSC, 525, 60 }; - MINFO_FROM(md->primary_head); + struct matrox_fb_info *minfo = md->primary_head; - if (ACCESS_FBINFO(outputs[1]).mode == MATROXFB_OUTPUT_MODE_PAL) + if (minfo->outputs[1].mode == MATROXFB_OUTPUT_MODE_PAL) *data = palregs; else *data = ntscregs; @@ -496,11 +496,11 @@ static void maven_init_TVdata(const struct maven_data* md, struct mavenregs* dat /* Set saturation */ { data->regs[0x20] = - data->regs[0x22] = ACCESS_FBINFO(altout.tvo_params.saturation); + data->regs[0x22] = minfo->altout.tvo_params.saturation; } /* Set HUE */ - data->regs[0x25] = ACCESS_FBINFO(altout.tvo_params.hue); + data->regs[0x25] = minfo->altout.tvo_params.hue; return; } @@ -741,9 +741,9 @@ static inline int maven_compute_timming(struct maven_data* md, struct mavenregs* m) { unsigned int tmpi; unsigned int a, bv, c; - MINFO_FROM(md->primary_head); + struct matrox_fb_info *minfo = md->primary_head; - m->mode = ACCESS_FBINFO(outputs[1]).mode; + m->mode = minfo->outputs[1].mode; if (m->mode != MATROXFB_OUTPUT_MODE_MONITOR) { unsigned int lmargin; unsigned int umargin; @@ -1132,7 +1132,7 @@ static int maven_get_control (struct maven_data* md, static int maven_out_compute(void* md, struct my_timming* mt) { #define mdinfo ((struct maven_data*)md) #define minfo (mdinfo->primary_head) - return maven_compute_timming(md, mt, &ACCESS_FBINFO(hw).maven); + return maven_compute_timming(md, mt, &minfo->hw.maven); #undef minfo #undef mdinfo } @@ -1140,7 +1140,7 @@ static int maven_out_compute(void* md, struct my_timming* mt) { static int maven_out_program(void* md) { #define mdinfo ((struct maven_data*)md) #define minfo (mdinfo->primary_head) - return maven_program_timming(md, &ACCESS_FBINFO(hw).maven); + return maven_program_timming(md, &minfo->hw.maven); #undef minfo #undef mdinfo } @@ -1184,16 +1184,18 @@ static struct matrox_altout maven_altout = { static int maven_init_client(struct i2c_client* clnt) { struct maven_data* md = i2c_get_clientdata(clnt); - MINFO_FROM(container_of(clnt->adapter, struct i2c_bit_adapter, adapter)->minfo); + struct matrox_fb_info *minfo = container_of(clnt->adapter, + struct i2c_bit_adapter, + adapter)->minfo; - md->primary_head = MINFO; + md->primary_head = minfo; md->client = clnt; - down_write(&ACCESS_FBINFO(altout.lock)); - ACCESS_FBINFO(outputs[1]).output = &maven_altout; - ACCESS_FBINFO(outputs[1]).src = ACCESS_FBINFO(outputs[1]).default_src; - ACCESS_FBINFO(outputs[1]).data = md; - ACCESS_FBINFO(outputs[1]).mode = MATROXFB_OUTPUT_MODE_MONITOR; - up_write(&ACCESS_FBINFO(altout.lock)); + down_write(&minfo->altout.lock); + minfo->outputs[1].output = &maven_altout; + minfo->outputs[1].src = minfo->outputs[1].default_src; + minfo->outputs[1].data = md; + minfo->outputs[1].mode = MATROXFB_OUTPUT_MODE_MONITOR; + up_write(&minfo->altout.lock); if (maven_get_reg(clnt, 0xB2) < 0x14) { md->version = MGATVO_B; /* Tweak some things for this old chip */ @@ -1218,14 +1220,14 @@ static int maven_shutdown_client(struct i2c_client* clnt) { struct maven_data* md = i2c_get_clientdata(clnt); if (md->primary_head) { - MINFO_FROM(md->primary_head); - - down_write(&ACCESS_FBINFO(altout.lock)); - ACCESS_FBINFO(outputs[1]).src = MATROXFB_SRC_NONE; - ACCESS_FBINFO(outputs[1]).output = NULL; - ACCESS_FBINFO(outputs[1]).data = NULL; - ACCESS_FBINFO(outputs[1]).mode = MATROXFB_OUTPUT_MODE_MONITOR; - up_write(&ACCESS_FBINFO(altout.lock)); + struct matrox_fb_info *minfo = md->primary_head; + + down_write(&minfo->altout.lock); + minfo->outputs[1].src = MATROXFB_SRC_NONE; + minfo->outputs[1].output = NULL; + minfo->outputs[1].data = NULL; + minfo->outputs[1].mode = MATROXFB_OUTPUT_MODE_MONITOR; + up_write(&minfo->altout.lock); md->primary_head = NULL; } return 0; diff --git a/drivers/video/matrox/matroxfb_misc.c b/drivers/video/matrox/matroxfb_misc.c index 5b5f072fc1a8..9948ca2a3046 100644 --- a/drivers/video/matrox/matroxfb_misc.c +++ b/drivers/video/matrox/matroxfb_misc.c @@ -89,13 +89,15 @@ #include <linux/interrupt.h> #include <linux/matroxfb.h> -void matroxfb_DAC_out(CPMINFO int reg, int val) { +void matroxfb_DAC_out(const struct matrox_fb_info *minfo, int reg, int val) +{ DBG_REG(__func__) mga_outb(M_RAMDAC_BASE+M_X_INDEX, reg); mga_outb(M_RAMDAC_BASE+M_X_DATAREG, val); } -int matroxfb_DAC_in(CPMINFO int reg) { +int matroxfb_DAC_in(const struct matrox_fb_info *minfo, int reg) +{ DBG_REG(__func__) mga_outb(M_RAMDAC_BASE+M_X_INDEX, reg); return mga_inb(M_RAMDAC_BASE+M_X_DATAREG); @@ -184,13 +186,14 @@ int matroxfb_PLL_calcclock(const struct matrox_pll_features* pll, unsigned int f return bestvco; } -int matroxfb_vgaHWinit(WPMINFO struct my_timming* m) { +int matroxfb_vgaHWinit(struct matrox_fb_info *minfo, struct my_timming *m) +{ unsigned int hd, hs, he, hbe, ht; unsigned int vd, vs, ve, vt, lc; unsigned int wd; unsigned int divider; int i; - struct matrox_hw_state * const hw = &ACCESS_FBINFO(hw); + struct matrox_hw_state * const hw = &minfo->hw; DBG(__func__) @@ -240,7 +243,7 @@ int matroxfb_vgaHWinit(WPMINFO struct my_timming* m) { /* standard timmings are in 8pixels, but for interleaved we cannot */ /* do it for 4bpp (because of (4bpp >> 1(interleaved))/4 == 0) */ /* using 16 or more pixels per unit can save us */ - divider = ACCESS_FBINFO(curr.final_bppShift); + divider = minfo->curr.final_bppShift; while (divider & 3) { hd >>= 1; hs >>= 1; @@ -270,7 +273,7 @@ int matroxfb_vgaHWinit(WPMINFO struct my_timming* m) { if (((ht & 0x07) == 0x06) || ((ht & 0x0F) == 0x04)) ht++; hbe = ht; - wd = ACCESS_FBINFO(fbcon).var.xres_virtual * ACCESS_FBINFO(curr.final_bppShift) / 64; + wd = minfo->fbcon.var.xres_virtual * minfo->curr.final_bppShift / 64; hw->CRTCEXT[0] = 0; hw->CRTCEXT[5] = 0; @@ -287,7 +290,7 @@ int matroxfb_vgaHWinit(WPMINFO struct my_timming* m) { ((hs & 0x100) >> 6) | /* sync start */ (hbe & 0x040); /* end hor. blanking */ /* FIXME: Enable vidrst only on G400, and only if TV-out is used */ - if (ACCESS_FBINFO(outputs[1]).src == MATROXFB_SRC_CRTC1) + if (minfo->outputs[1].src == MATROXFB_SRC_CRTC1) hw->CRTCEXT[1] |= 0x88; /* enable horizontal and vertical vidrst */ hw->CRTCEXT[2] = ((vt & 0xC00) >> 10) | ((vd & 0x400) >> 8) | /* disp end */ @@ -331,9 +334,10 @@ int matroxfb_vgaHWinit(WPMINFO struct my_timming* m) { return 0; }; -void matroxfb_vgaHWrestore(WPMINFO2) { +void matroxfb_vgaHWrestore(struct matrox_fb_info *minfo) +{ int i; - struct matrox_hw_state * const hw = &ACCESS_FBINFO(hw); + struct matrox_hw_state * const hw = &minfo->hw; CRITFLAGS DBG(__func__) @@ -522,7 +526,9 @@ static void parse_bios(unsigned char __iomem* vbios, struct matrox_bios* bd) { #endif } -static int parse_pins1(WPMINFO const struct matrox_bios* bd) { +static int parse_pins1(struct matrox_fb_info *minfo, + const struct matrox_bios *bd) +{ unsigned int maxdac; switch (bd->pins[22]) { @@ -533,173 +539,188 @@ static int parse_pins1(WPMINFO const struct matrox_bios* bd) { if (get_unaligned_le16(bd->pins + 24)) { maxdac = get_unaligned_le16(bd->pins + 24) * 10; } - MINFO->limits.pixel.vcomax = maxdac; - MINFO->values.pll.system = get_unaligned_le16(bd->pins + 28) ? + minfo->limits.pixel.vcomax = maxdac; + minfo->values.pll.system = get_unaligned_le16(bd->pins + 28) ? get_unaligned_le16(bd->pins + 28) * 10 : 50000; /* ignore 4MB, 8MB, module clocks */ - MINFO->features.pll.ref_freq = 14318; - MINFO->values.reg.mctlwtst = 0x00030101; + minfo->features.pll.ref_freq = 14318; + minfo->values.reg.mctlwtst = 0x00030101; return 0; } -static void default_pins1(WPMINFO2) { +static void default_pins1(struct matrox_fb_info *minfo) +{ /* Millennium */ - MINFO->limits.pixel.vcomax = 220000; - MINFO->values.pll.system = 50000; - MINFO->features.pll.ref_freq = 14318; - MINFO->values.reg.mctlwtst = 0x00030101; + minfo->limits.pixel.vcomax = 220000; + minfo->values.pll.system = 50000; + minfo->features.pll.ref_freq = 14318; + minfo->values.reg.mctlwtst = 0x00030101; } -static int parse_pins2(WPMINFO const struct matrox_bios* bd) { - MINFO->limits.pixel.vcomax = - MINFO->limits.system.vcomax = (bd->pins[41] == 0xFF) ? 230000 : ((bd->pins[41] + 100) * 1000); - MINFO->values.reg.mctlwtst = ((bd->pins[51] & 0x01) ? 0x00000001 : 0) | +static int parse_pins2(struct matrox_fb_info *minfo, + const struct matrox_bios *bd) +{ + minfo->limits.pixel.vcomax = + minfo->limits.system.vcomax = (bd->pins[41] == 0xFF) ? 230000 : ((bd->pins[41] + 100) * 1000); + minfo->values.reg.mctlwtst = ((bd->pins[51] & 0x01) ? 0x00000001 : 0) | ((bd->pins[51] & 0x02) ? 0x00000100 : 0) | ((bd->pins[51] & 0x04) ? 0x00010000 : 0) | ((bd->pins[51] & 0x08) ? 0x00020000 : 0); - MINFO->values.pll.system = (bd->pins[43] == 0xFF) ? 50000 : ((bd->pins[43] + 100) * 1000); - MINFO->features.pll.ref_freq = 14318; + minfo->values.pll.system = (bd->pins[43] == 0xFF) ? 50000 : ((bd->pins[43] + 100) * 1000); + minfo->features.pll.ref_freq = 14318; return 0; } -static void default_pins2(WPMINFO2) { +static void default_pins2(struct matrox_fb_info *minfo) +{ /* Millennium II, Mystique */ - MINFO->limits.pixel.vcomax = - MINFO->limits.system.vcomax = 230000; - MINFO->values.reg.mctlwtst = 0x00030101; - MINFO->values.pll.system = 50000; - MINFO->features.pll.ref_freq = 14318; + minfo->limits.pixel.vcomax = + minfo->limits.system.vcomax = 230000; + minfo->values.reg.mctlwtst = 0x00030101; + minfo->values.pll.system = 50000; + minfo->features.pll.ref_freq = 14318; } -static int parse_pins3(WPMINFO const struct matrox_bios* bd) { - MINFO->limits.pixel.vcomax = - MINFO->limits.system.vcomax = (bd->pins[36] == 0xFF) ? 230000 : ((bd->pins[36] + 100) * 1000); - MINFO->values.reg.mctlwtst = get_unaligned_le32(bd->pins + 48) == 0xFFFFFFFF ? +static int parse_pins3(struct matrox_fb_info *minfo, + const struct matrox_bios *bd) +{ + minfo->limits.pixel.vcomax = + minfo->limits.system.vcomax = (bd->pins[36] == 0xFF) ? 230000 : ((bd->pins[36] + 100) * 1000); + minfo->values.reg.mctlwtst = get_unaligned_le32(bd->pins + 48) == 0xFFFFFFFF ? 0x01250A21 : get_unaligned_le32(bd->pins + 48); /* memory config */ - MINFO->values.reg.memrdbk = ((bd->pins[57] << 21) & 0x1E000000) | + minfo->values.reg.memrdbk = ((bd->pins[57] << 21) & 0x1E000000) | ((bd->pins[57] << 22) & 0x00C00000) | ((bd->pins[56] << 1) & 0x000001E0) | ( bd->pins[56] & 0x0000000F); - MINFO->values.reg.opt = (bd->pins[54] & 7) << 10; - MINFO->values.reg.opt2 = bd->pins[58] << 12; - MINFO->features.pll.ref_freq = (bd->pins[52] & 0x20) ? 14318 : 27000; + minfo->values.reg.opt = (bd->pins[54] & 7) << 10; + minfo->values.reg.opt2 = bd->pins[58] << 12; + minfo->features.pll.ref_freq = (bd->pins[52] & 0x20) ? 14318 : 27000; return 0; } -static void default_pins3(WPMINFO2) { +static void default_pins3(struct matrox_fb_info *minfo) +{ /* G100, G200 */ - MINFO->limits.pixel.vcomax = - MINFO->limits.system.vcomax = 230000; - MINFO->values.reg.mctlwtst = 0x01250A21; - MINFO->values.reg.memrdbk = 0x00000000; - MINFO->values.reg.opt = 0x00000C00; - MINFO->values.reg.opt2 = 0x00000000; - MINFO->features.pll.ref_freq = 27000; + minfo->limits.pixel.vcomax = + minfo->limits.system.vcomax = 230000; + minfo->values.reg.mctlwtst = 0x01250A21; + minfo->values.reg.memrdbk = 0x00000000; + minfo->values.reg.opt = 0x00000C00; + minfo->values.reg.opt2 = 0x00000000; + minfo->features.pll.ref_freq = 27000; } -static int parse_pins4(WPMINFO const struct matrox_bios* bd) { - MINFO->limits.pixel.vcomax = (bd->pins[ 39] == 0xFF) ? 230000 : bd->pins[ 39] * 4000; - MINFO->limits.system.vcomax = (bd->pins[ 38] == 0xFF) ? MINFO->limits.pixel.vcomax : bd->pins[ 38] * 4000; - MINFO->values.reg.mctlwtst = get_unaligned_le32(bd->pins + 71); - MINFO->values.reg.memrdbk = ((bd->pins[87] << 21) & 0x1E000000) | +static int parse_pins4(struct matrox_fb_info *minfo, + const struct matrox_bios *bd) +{ + minfo->limits.pixel.vcomax = (bd->pins[ 39] == 0xFF) ? 230000 : bd->pins[ 39] * 4000; + minfo->limits.system.vcomax = (bd->pins[ 38] == 0xFF) ? minfo->limits.pixel.vcomax : bd->pins[ 38] * 4000; + minfo->values.reg.mctlwtst = get_unaligned_le32(bd->pins + 71); + minfo->values.reg.memrdbk = ((bd->pins[87] << 21) & 0x1E000000) | ((bd->pins[87] << 22) & 0x00C00000) | ((bd->pins[86] << 1) & 0x000001E0) | ( bd->pins[86] & 0x0000000F); - MINFO->values.reg.opt = ((bd->pins[53] << 15) & 0x00400000) | + minfo->values.reg.opt = ((bd->pins[53] << 15) & 0x00400000) | ((bd->pins[53] << 22) & 0x10000000) | ((bd->pins[53] << 7) & 0x00001C00); - MINFO->values.reg.opt3 = get_unaligned_le32(bd->pins + 67); - MINFO->values.pll.system = (bd->pins[ 65] == 0xFF) ? 200000 : bd->pins[ 65] * 4000; - MINFO->features.pll.ref_freq = (bd->pins[ 92] & 0x01) ? 14318 : 27000; + minfo->values.reg.opt3 = get_unaligned_le32(bd->pins + 67); + minfo->values.pll.system = (bd->pins[ 65] == 0xFF) ? 200000 : bd->pins[ 65] * 4000; + minfo->features.pll.ref_freq = (bd->pins[ 92] & 0x01) ? 14318 : 27000; return 0; } -static void default_pins4(WPMINFO2) { +static void default_pins4(struct matrox_fb_info *minfo) +{ /* G400 */ - MINFO->limits.pixel.vcomax = - MINFO->limits.system.vcomax = 252000; - MINFO->values.reg.mctlwtst = 0x04A450A1; - MINFO->values.reg.memrdbk = 0x000000E7; - MINFO->values.reg.opt = 0x10000400; - MINFO->values.reg.opt3 = 0x0190A419; - MINFO->values.pll.system = 200000; - MINFO->features.pll.ref_freq = 27000; + minfo->limits.pixel.vcomax = + minfo->limits.system.vcomax = 252000; + minfo->values.reg.mctlwtst = 0x04A450A1; + minfo->values.reg.memrdbk = 0x000000E7; + minfo->values.reg.opt = 0x10000400; + minfo->values.reg.opt3 = 0x0190A419; + minfo->values.pll.system = 200000; + minfo->features.pll.ref_freq = 27000; } -static int parse_pins5(WPMINFO const struct matrox_bios* bd) { +static int parse_pins5(struct matrox_fb_info *minfo, + const struct matrox_bios *bd) +{ unsigned int mult; mult = bd->pins[4]?8000:6000; - MINFO->limits.pixel.vcomax = (bd->pins[ 38] == 0xFF) ? 600000 : bd->pins[ 38] * mult; - MINFO->limits.system.vcomax = (bd->pins[ 36] == 0xFF) ? MINFO->limits.pixel.vcomax : bd->pins[ 36] * mult; - MINFO->limits.video.vcomax = (bd->pins[ 37] == 0xFF) ? MINFO->limits.system.vcomax : bd->pins[ 37] * mult; - MINFO->limits.pixel.vcomin = (bd->pins[123] == 0xFF) ? 256000 : bd->pins[123] * mult; - MINFO->limits.system.vcomin = (bd->pins[121] == 0xFF) ? MINFO->limits.pixel.vcomin : bd->pins[121] * mult; - MINFO->limits.video.vcomin = (bd->pins[122] == 0xFF) ? MINFO->limits.system.vcomin : bd->pins[122] * mult; - MINFO->values.pll.system = - MINFO->values.pll.video = (bd->pins[ 92] == 0xFF) ? 284000 : bd->pins[ 92] * 4000; - MINFO->values.reg.opt = get_unaligned_le32(bd->pins + 48); - MINFO->values.reg.opt2 = get_unaligned_le32(bd->pins + 52); - MINFO->values.reg.opt3 = get_unaligned_le32(bd->pins + 94); - MINFO->values.reg.mctlwtst = get_unaligned_le32(bd->pins + 98); - MINFO->values.reg.memmisc = get_unaligned_le32(bd->pins + 102); - MINFO->values.reg.memrdbk = get_unaligned_le32(bd->pins + 106); - MINFO->features.pll.ref_freq = (bd->pins[110] & 0x01) ? 14318 : 27000; - MINFO->values.memory.ddr = (bd->pins[114] & 0x60) == 0x20; - MINFO->values.memory.dll = (bd->pins[115] & 0x02) != 0; - MINFO->values.memory.emrswen = (bd->pins[115] & 0x01) != 0; - MINFO->values.reg.maccess = MINFO->values.memory.emrswen ? 0x00004000 : 0x00000000; + minfo->limits.pixel.vcomax = (bd->pins[ 38] == 0xFF) ? 600000 : bd->pins[ 38] * mult; + minfo->limits.system.vcomax = (bd->pins[ 36] == 0xFF) ? minfo->limits.pixel.vcomax : bd->pins[ 36] * mult; + minfo->limits.video.vcomax = (bd->pins[ 37] == 0xFF) ? minfo->limits.system.vcomax : bd->pins[ 37] * mult; + minfo->limits.pixel.vcomin = (bd->pins[123] == 0xFF) ? 256000 : bd->pins[123] * mult; + minfo->limits.system.vcomin = (bd->pins[121] == 0xFF) ? minfo->limits.pixel.vcomin : bd->pins[121] * mult; + minfo->limits.video.vcomin = (bd->pins[122] == 0xFF) ? minfo->limits.system.vcomin : bd->pins[122] * mult; + minfo->values.pll.system = + minfo->values.pll.video = (bd->pins[ 92] == 0xFF) ? 284000 : bd->pins[ 92] * 4000; + minfo->values.reg.opt = get_unaligned_le32(bd->pins + 48); + minfo->values.reg.opt2 = get_unaligned_le32(bd->pins + 52); + minfo->values.reg.opt3 = get_unaligned_le32(bd->pins + 94); + minfo->values.reg.mctlwtst = get_unaligned_le32(bd->pins + 98); + minfo->values.reg.memmisc = get_unaligned_le32(bd->pins + 102); + minfo->values.reg.memrdbk = get_unaligned_le32(bd->pins + 106); + minfo->features.pll.ref_freq = (bd->pins[110] & 0x01) ? 14318 : 27000; + minfo->values.memory.ddr = (bd->pins[114] & 0x60) == 0x20; + minfo->values.memory.dll = (bd->pins[115] & 0x02) != 0; + minfo->values.memory.emrswen = (bd->pins[115] & 0x01) != 0; + minfo->values.reg.maccess = minfo->values.memory.emrswen ? 0x00004000 : 0x00000000; if (bd->pins[115] & 4) { - MINFO->values.reg.mctlwtst_core = MINFO->values.reg.mctlwtst; + minfo->values.reg.mctlwtst_core = minfo->values.reg.mctlwtst; } else { u_int32_t wtst_xlat[] = { 0, 1, 5, 6, 7, 5, 2, 3 }; - MINFO->values.reg.mctlwtst_core = (MINFO->values.reg.mctlwtst & ~7) | - wtst_xlat[MINFO->values.reg.mctlwtst & 7]; + minfo->values.reg.mctlwtst_core = (minfo->values.reg.mctlwtst & ~7) | + wtst_xlat[minfo->values.reg.mctlwtst & 7]; } - MINFO->max_pixel_clock_panellink = bd->pins[47] * 4000; + minfo->max_pixel_clock_panellink = bd->pins[47] * 4000; return 0; } -static void default_pins5(WPMINFO2) { +static void default_pins5(struct matrox_fb_info *minfo) +{ /* Mine 16MB G450 with SDRAM DDR */ - MINFO->limits.pixel.vcomax = - MINFO->limits.system.vcomax = - MINFO->limits.video.vcomax = 600000; - MINFO->limits.pixel.vcomin = - MINFO->limits.system.vcomin = - MINFO->limits.video.vcomin = 256000; - MINFO->values.pll.system = - MINFO->values.pll.video = 284000; - MINFO->values.reg.opt = 0x404A1160; - MINFO->values.reg.opt2 = 0x0000AC00; - MINFO->values.reg.opt3 = 0x0090A409; - MINFO->values.reg.mctlwtst_core = - MINFO->values.reg.mctlwtst = 0x0C81462B; - MINFO->values.reg.memmisc = 0x80000004; - MINFO->values.reg.memrdbk = 0x01001103; - MINFO->features.pll.ref_freq = 27000; - MINFO->values.memory.ddr = 1; - MINFO->values.memory.dll = 1; - MINFO->values.memory.emrswen = 1; - MINFO->values.reg.maccess = 0x00004000; + minfo->limits.pixel.vcomax = + minfo->limits.system.vcomax = + minfo->limits.video.vcomax = 600000; + minfo->limits.pixel.vcomin = + minfo->limits.system.vcomin = + minfo->limits.video.vcomin = 256000; + minfo->values.pll.system = + minfo->values.pll.video = 284000; + minfo->values.reg.opt = 0x404A1160; + minfo->values.reg.opt2 = 0x0000AC00; + minfo->values.reg.opt3 = 0x0090A409; + minfo->values.reg.mctlwtst_core = + minfo->values.reg.mctlwtst = 0x0C81462B; + minfo->values.reg.memmisc = 0x80000004; + minfo->values.reg.memrdbk = 0x01001103; + minfo->features.pll.ref_freq = 27000; + minfo->values.memory.ddr = 1; + minfo->values.memory.dll = 1; + minfo->values.memory.emrswen = 1; + minfo->values.reg.maccess = 0x00004000; } -static int matroxfb_set_limits(WPMINFO const struct matrox_bios* bd) { +static int matroxfb_set_limits(struct matrox_fb_info *minfo, + const struct matrox_bios *bd) +{ unsigned int pins_version; static const unsigned int pinslen[] = { 64, 64, 64, 128, 128 }; - switch (ACCESS_FBINFO(chip)) { - case MGA_2064: default_pins1(PMINFO2); break; + switch (minfo->chip) { + case MGA_2064: default_pins1(minfo); break; case MGA_2164: case MGA_1064: - case MGA_1164: default_pins2(PMINFO2); break; + case MGA_1164: default_pins2(minfo); break; case MGA_G100: - case MGA_G200: default_pins3(PMINFO2); break; - case MGA_G400: default_pins4(PMINFO2); break; + case MGA_G200: default_pins3(minfo); break; + case MGA_G400: default_pins4(minfo); break; case MGA_G450: - case MGA_G550: default_pins5(PMINFO2); break; + case MGA_G550: default_pins5(minfo); break; } if (!bd->bios_valid) { printk(KERN_INFO "matroxfb: Your Matrox device does not have BIOS\n"); @@ -724,38 +745,39 @@ static int matroxfb_set_limits(WPMINFO const struct matrox_bios* bd) { } switch (pins_version) { case 1: - return parse_pins1(PMINFO bd); + return parse_pins1(minfo, bd); case 2: - return parse_pins2(PMINFO bd); + return parse_pins2(minfo, bd); case 3: - return parse_pins3(PMINFO bd); + return parse_pins3(minfo, bd); case 4: - return parse_pins4(PMINFO bd); + return parse_pins4(minfo, bd); case 5: - return parse_pins5(PMINFO bd); + return parse_pins5(minfo, bd); default: printk(KERN_DEBUG "matroxfb: Powerup info version %u is not yet supported\n", pins_version); return -1; } } -void matroxfb_read_pins(WPMINFO2) { +void matroxfb_read_pins(struct matrox_fb_info *minfo) +{ u32 opt; u32 biosbase; u32 fbbase; - struct pci_dev* pdev = ACCESS_FBINFO(pcidev); + struct pci_dev *pdev = minfo->pcidev; - memset(&ACCESS_FBINFO(bios), 0, sizeof(ACCESS_FBINFO(bios))); + memset(&minfo->bios, 0, sizeof(minfo->bios)); pci_read_config_dword(pdev, PCI_OPTION_REG, &opt); pci_write_config_dword(pdev, PCI_OPTION_REG, opt | PCI_OPTION_ENABLE_ROM); pci_read_config_dword(pdev, PCI_ROM_ADDRESS, &biosbase); - pci_read_config_dword(pdev, ACCESS_FBINFO(devflags.fbResource), &fbbase); + pci_read_config_dword(pdev, minfo->devflags.fbResource, &fbbase); pci_write_config_dword(pdev, PCI_ROM_ADDRESS, (fbbase & PCI_ROM_ADDRESS_MASK) | PCI_ROM_ADDRESS_ENABLE); - parse_bios(vaddr_va(ACCESS_FBINFO(video).vbase), &ACCESS_FBINFO(bios)); + parse_bios(vaddr_va(minfo->video.vbase), &minfo->bios); pci_write_config_dword(pdev, PCI_ROM_ADDRESS, biosbase); pci_write_config_dword(pdev, PCI_OPTION_REG, opt); #ifdef CONFIG_X86 - if (!ACCESS_FBINFO(bios).bios_valid) { + if (!minfo->bios.bios_valid) { unsigned char __iomem* b; b = ioremap(0x000C0000, 65536); @@ -769,25 +791,21 @@ void matroxfb_read_pins(WPMINFO2) { printk(KERN_INFO "matroxfb: Legacy BIOS is for %04X:%04X, while this device is %04X:%04X\n", ven, dev, pdev->vendor, pdev->device); } else { - parse_bios(b, &ACCESS_FBINFO(bios)); + parse_bios(b, &minfo->bios); } iounmap(b); } } #endif - matroxfb_set_limits(PMINFO &ACCESS_FBINFO(bios)); + matroxfb_set_limits(minfo, &minfo->bios); printk(KERN_INFO "PInS memtype = %u\n", - (ACCESS_FBINFO(values).reg.opt & 0x1C00) >> 10); + (minfo->values.reg.opt & 0x1C00) >> 10); } EXPORT_SYMBOL(matroxfb_DAC_in); EXPORT_SYMBOL(matroxfb_DAC_out); EXPORT_SYMBOL(matroxfb_var2my); EXPORT_SYMBOL(matroxfb_PLL_calcclock); -#ifndef CONFIG_FB_MATROX_MULTIHEAD -struct matrox_fb_info matroxfb_global_mxinfo; -EXPORT_SYMBOL(matroxfb_global_mxinfo); -#endif EXPORT_SYMBOL(matroxfb_vgaHWinit); /* DAC1064, Ti3026 */ EXPORT_SYMBOL(matroxfb_vgaHWrestore); /* DAC1064, Ti3026 */ EXPORT_SYMBOL(matroxfb_read_pins); diff --git a/drivers/video/matrox/matroxfb_misc.h b/drivers/video/matrox/matroxfb_misc.h index cb62cc0ead96..351c823f1f74 100644 --- a/drivers/video/matrox/matroxfb_misc.h +++ b/drivers/video/matrox/matroxfb_misc.h @@ -6,13 +6,16 @@ /* also for modules */ int matroxfb_PLL_calcclock(const struct matrox_pll_features* pll, unsigned int freq, unsigned int fmax, unsigned int* in, unsigned int* feed, unsigned int* post); -static inline int PLL_calcclock(CPMINFO unsigned int freq, unsigned int fmax, - unsigned int* in, unsigned int* feed, unsigned int* post) { - return matroxfb_PLL_calcclock(&ACCESS_FBINFO(features.pll), freq, fmax, in, feed, post); +static inline int PLL_calcclock(const struct matrox_fb_info *minfo, + unsigned int freq, unsigned int fmax, + unsigned int *in, unsigned int *feed, + unsigned int *post) +{ + return matroxfb_PLL_calcclock(&minfo->features.pll, freq, fmax, in, feed, post); } -int matroxfb_vgaHWinit(WPMINFO struct my_timming* m); -void matroxfb_vgaHWrestore(WPMINFO2); -void matroxfb_read_pins(WPMINFO2); +int matroxfb_vgaHWinit(struct matrox_fb_info *minfo, struct my_timming* m); +void matroxfb_vgaHWrestore(struct matrox_fb_info *minfo); +void matroxfb_read_pins(struct matrox_fb_info *minfo); #endif /* __MATROXFB_MISC_H__ */ diff --git a/drivers/video/msm/Makefile b/drivers/video/msm/Makefile new file mode 100644 index 000000000000..802d6ae523fb --- /dev/null +++ b/drivers/video/msm/Makefile @@ -0,0 +1,19 @@ + +# core framebuffer +# +obj-y := msm_fb.o + +# MDP DMA/PPP engine +# +obj-y += mdp.o mdp_scale_tables.o mdp_ppp.o + +# MDDI interface +# +obj-y += mddi.o + +# MDDI client/panel drivers +# +obj-y += mddi_client_dummy.o +obj-y += mddi_client_toshiba.o +obj-y += mddi_client_nt35399.o + diff --git a/drivers/video/msm/mddi.c b/drivers/video/msm/mddi.c new file mode 100644 index 000000000000..474421fe79a6 --- /dev/null +++ b/drivers/video/msm/mddi.c @@ -0,0 +1,827 @@ +/* + * MSM MDDI Transport + * + * Copyright (C) 2007 Google Incorporated + * Copyright (C) 2007 QUALCOMM Incorporated + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/dma-mapping.h> +#include <linux/interrupt.h> +#include <linux/platform_device.h> +#include <linux/delay.h> +#include <linux/spinlock.h> +#include <linux/clk.h> +#include <linux/io.h> +#include <linux/sched.h> +#include <mach/msm_iomap.h> +#include <mach/irqs.h> +#include <mach/board.h> +#include <mach/msm_fb.h> +#include "mddi_hw.h" + +#define FLAG_DISABLE_HIBERNATION 0x0001 +#define FLAG_HAVE_CAPS 0x0002 +#define FLAG_HAS_VSYNC_IRQ 0x0004 +#define FLAG_HAVE_STATUS 0x0008 + +#define CMD_GET_CLIENT_CAP 0x0601 +#define CMD_GET_CLIENT_STATUS 0x0602 + +union mddi_rev { + unsigned char raw[MDDI_REV_BUFFER_SIZE]; + struct mddi_rev_packet hdr; + struct mddi_client_status status; + struct mddi_client_caps caps; + struct mddi_register_access reg; +}; + +struct reg_read_info { + struct completion done; + uint32_t reg; + uint32_t status; + uint32_t result; +}; + +struct mddi_info { + uint16_t flags; + uint16_t version; + char __iomem *base; + int irq; + struct clk *clk; + struct msm_mddi_client_data client_data; + + /* buffer for rev encap packets */ + void *rev_data; + dma_addr_t rev_addr; + struct mddi_llentry *reg_write_data; + dma_addr_t reg_write_addr; + struct mddi_llentry *reg_read_data; + dma_addr_t reg_read_addr; + size_t rev_data_curr; + + spinlock_t int_lock; + uint32_t int_enable; + uint32_t got_int; + wait_queue_head_t int_wait; + + struct mutex reg_write_lock; + struct mutex reg_read_lock; + struct reg_read_info *reg_read; + + struct mddi_client_caps caps; + struct mddi_client_status status; + + void (*power_client)(struct msm_mddi_client_data *, int); + + /* client device published to bind us to the + * appropriate mddi_client driver + */ + char client_name[20]; + + struct platform_device client_pdev; +}; + +static void mddi_init_rev_encap(struct mddi_info *mddi); + +#define mddi_readl(r) readl(mddi->base + (MDDI_##r)) +#define mddi_writel(v, r) writel((v), mddi->base + (MDDI_##r)) + +void mddi_activate_link(struct msm_mddi_client_data *cdata) +{ + struct mddi_info *mddi = container_of(cdata, struct mddi_info, + client_data); + + mddi_writel(MDDI_CMD_LINK_ACTIVE, CMD); +} + +static void mddi_handle_link_list_done(struct mddi_info *mddi) +{ +} + +static void mddi_reset_rev_encap_ptr(struct mddi_info *mddi) +{ + printk(KERN_INFO "mddi: resetting rev ptr\n"); + mddi->rev_data_curr = 0; + mddi_writel(mddi->rev_addr, REV_PTR); + mddi_writel(mddi->rev_addr, REV_PTR); + mddi_writel(MDDI_CMD_FORCE_NEW_REV_PTR, CMD); +} + +static void mddi_handle_rev_data(struct mddi_info *mddi, union mddi_rev *rev) +{ + int i; + struct reg_read_info *ri; + + if ((rev->hdr.length <= MDDI_REV_BUFFER_SIZE - 2) && + (rev->hdr.length >= sizeof(struct mddi_rev_packet) - 2)) { + + switch (rev->hdr.type) { + case TYPE_CLIENT_CAPS: + memcpy(&mddi->caps, &rev->caps, + sizeof(struct mddi_client_caps)); + mddi->flags |= FLAG_HAVE_CAPS; + wake_up(&mddi->int_wait); + break; + case TYPE_CLIENT_STATUS: + memcpy(&mddi->status, &rev->status, + sizeof(struct mddi_client_status)); + mddi->flags |= FLAG_HAVE_STATUS; + wake_up(&mddi->int_wait); + break; + case TYPE_REGISTER_ACCESS: + ri = mddi->reg_read; + if (ri == 0) { + printk(KERN_INFO "rev: got reg %x = %x without " + " pending read\n", + rev->reg.register_address, + rev->reg.register_data_list); + break; + } + if (ri->reg != rev->reg.register_address) { + printk(KERN_INFO "rev: got reg %x = %x for " + "wrong register, expected " + "%x\n", + rev->reg.register_address, + rev->reg.register_data_list, ri->reg); + break; + } + mddi->reg_read = NULL; + ri->status = 0; + ri->result = rev->reg.register_data_list; + complete(&ri->done); + break; + default: + printk(KERN_INFO "rev: unknown reverse packet: " + "len=%04x type=%04x CURR_REV_PTR=%x\n", + rev->hdr.length, rev->hdr.type, + mddi_readl(CURR_REV_PTR)); + for (i = 0; i < rev->hdr.length + 2; i++) { + if ((i % 16) == 0) + printk(KERN_INFO "\n"); + printk(KERN_INFO " %02x", rev->raw[i]); + } + printk(KERN_INFO "\n"); + mddi_reset_rev_encap_ptr(mddi); + } + } else { + printk(KERN_INFO "bad rev length, %d, CURR_REV_PTR %x\n", + rev->hdr.length, mddi_readl(CURR_REV_PTR)); + mddi_reset_rev_encap_ptr(mddi); + } +} + +static void mddi_wait_interrupt(struct mddi_info *mddi, uint32_t intmask); + +static void mddi_handle_rev_data_avail(struct mddi_info *mddi) +{ + union mddi_rev *rev = mddi->rev_data; + uint32_t rev_data_count; + uint32_t rev_crc_err_count; + int i; + struct reg_read_info *ri; + size_t prev_offset; + uint16_t length; + + union mddi_rev *crev = mddi->rev_data + mddi->rev_data_curr; + + /* clear the interrupt */ + mddi_writel(MDDI_INT_REV_DATA_AVAIL, INT); + rev_data_count = mddi_readl(REV_PKT_CNT); + rev_crc_err_count = mddi_readl(REV_CRC_ERR); + if (rev_data_count > 1) + printk(KERN_INFO "rev_data_count %d\n", rev_data_count); + + if (rev_crc_err_count) { + printk(KERN_INFO "rev_crc_err_count %d, INT %x\n", + rev_crc_err_count, mddi_readl(INT)); + ri = mddi->reg_read; + if (ri == 0) { + printk(KERN_INFO "rev: got crc error without pending " + "read\n"); + } else { + mddi->reg_read = NULL; + ri->status = -EIO; + ri->result = -1; + complete(&ri->done); + } + } + + if (rev_data_count == 0) + return; + + prev_offset = mddi->rev_data_curr; + + length = *((uint8_t *)mddi->rev_data + mddi->rev_data_curr); + mddi->rev_data_curr++; + if (mddi->rev_data_curr == MDDI_REV_BUFFER_SIZE) + mddi->rev_data_curr = 0; + length += *((uint8_t *)mddi->rev_data + mddi->rev_data_curr) << 8; + mddi->rev_data_curr += 1 + length; + if (mddi->rev_data_curr >= MDDI_REV_BUFFER_SIZE) + mddi->rev_data_curr = + mddi->rev_data_curr % MDDI_REV_BUFFER_SIZE; + + if (length > MDDI_REV_BUFFER_SIZE - 2) { + printk(KERN_INFO "mddi: rev data length greater than buffer" + "size\n"); + mddi_reset_rev_encap_ptr(mddi); + return; + } + + if (prev_offset + 2 + length >= MDDI_REV_BUFFER_SIZE) { + union mddi_rev tmprev; + size_t rem = MDDI_REV_BUFFER_SIZE - prev_offset; + memcpy(&tmprev.raw[0], mddi->rev_data + prev_offset, rem); + memcpy(&tmprev.raw[rem], mddi->rev_data, 2 + length - rem); + mddi_handle_rev_data(mddi, &tmprev); + } else { + mddi_handle_rev_data(mddi, crev); + } + + if (prev_offset < MDDI_REV_BUFFER_SIZE / 2 && + mddi->rev_data_curr >= MDDI_REV_BUFFER_SIZE / 2) { + mddi_writel(mddi->rev_addr, REV_PTR); + } +} + +static irqreturn_t mddi_isr(int irq, void *data) +{ + struct msm_mddi_client_data *cdata = data; + struct mddi_info *mddi = container_of(cdata, struct mddi_info, + client_data); + uint32_t active, status; + + spin_lock(&mddi->int_lock); + + active = mddi_readl(INT); + status = mddi_readl(STAT); + + mddi_writel(active, INT); + + /* ignore any interrupts we have disabled */ + active &= mddi->int_enable; + + mddi->got_int |= active; + wake_up(&mddi->int_wait); + + if (active & MDDI_INT_PRI_LINK_LIST_DONE) { + mddi->int_enable &= (~MDDI_INT_PRI_LINK_LIST_DONE); + mddi_handle_link_list_done(mddi); + } + if (active & MDDI_INT_REV_DATA_AVAIL) + mddi_handle_rev_data_avail(mddi); + + if (active & ~MDDI_INT_NEED_CLEAR) + mddi->int_enable &= ~(active & ~MDDI_INT_NEED_CLEAR); + + if (active & MDDI_INT_LINK_ACTIVE) { + mddi->int_enable &= (~MDDI_INT_LINK_ACTIVE); + mddi->int_enable |= MDDI_INT_IN_HIBERNATION; + } + + if (active & MDDI_INT_IN_HIBERNATION) { + mddi->int_enable &= (~MDDI_INT_IN_HIBERNATION); + mddi->int_enable |= MDDI_INT_LINK_ACTIVE; + } + + mddi_writel(mddi->int_enable, INTEN); + spin_unlock(&mddi->int_lock); + + return IRQ_HANDLED; +} + +static long mddi_wait_interrupt_timeout(struct mddi_info *mddi, + uint32_t intmask, int timeout) +{ + unsigned long irq_flags; + + spin_lock_irqsave(&mddi->int_lock, irq_flags); + mddi->got_int &= ~intmask; + mddi->int_enable |= intmask; + mddi_writel(mddi->int_enable, INTEN); + spin_unlock_irqrestore(&mddi->int_lock, irq_flags); + return wait_event_timeout(mddi->int_wait, mddi->got_int & intmask, + timeout); +} + +static void mddi_wait_interrupt(struct mddi_info *mddi, uint32_t intmask) +{ + if (mddi_wait_interrupt_timeout(mddi, intmask, HZ/10) == 0) + printk(KERN_INFO KERN_ERR "mddi_wait_interrupt %d, timeout " + "waiting for %x, INT = %x, STAT = %x gotint = %x\n", + current->pid, intmask, mddi_readl(INT), mddi_readl(STAT), + mddi->got_int); +} + +static void mddi_init_rev_encap(struct mddi_info *mddi) +{ + memset(mddi->rev_data, 0xee, MDDI_REV_BUFFER_SIZE); + mddi_writel(mddi->rev_addr, REV_PTR); + mddi_writel(MDDI_CMD_FORCE_NEW_REV_PTR, CMD); + mddi_wait_interrupt(mddi, MDDI_INT_NO_CMD_PKTS_PEND); +} + +void mddi_set_auto_hibernate(struct msm_mddi_client_data *cdata, int on) +{ + struct mddi_info *mddi = container_of(cdata, struct mddi_info, + client_data); + mddi_writel(MDDI_CMD_POWERDOWN, CMD); + mddi_wait_interrupt(mddi, MDDI_INT_IN_HIBERNATION); + mddi_writel(MDDI_CMD_HIBERNATE | !!on, CMD); + mddi_wait_interrupt(mddi, MDDI_INT_NO_CMD_PKTS_PEND); +} + + +static uint16_t mddi_init_registers(struct mddi_info *mddi) +{ + mddi_writel(0x0001, VERSION); + mddi_writel(MDDI_HOST_BYTES_PER_SUBFRAME, BPS); + mddi_writel(0x0003, SPM); /* subframes per media */ + mddi_writel(0x0005, TA1_LEN); + mddi_writel(MDDI_HOST_TA2_LEN, TA2_LEN); + mddi_writel(0x0096, DRIVE_HI); + /* 0x32 normal, 0x50 for Toshiba display */ + mddi_writel(0x0050, DRIVE_LO); + mddi_writel(0x003C, DISP_WAKE); /* wakeup counter */ + mddi_writel(MDDI_HOST_REV_RATE_DIV, REV_RATE_DIV); + + mddi_writel(MDDI_REV_BUFFER_SIZE, REV_SIZE); + mddi_writel(MDDI_MAX_REV_PKT_SIZE, REV_ENCAP_SZ); + + /* disable periodic rev encap */ + mddi_writel(MDDI_CMD_PERIODIC_REV_ENCAP, CMD); + mddi_wait_interrupt(mddi, MDDI_INT_NO_CMD_PKTS_PEND); + + if (mddi_readl(PAD_CTL) == 0) { + /* If we are turning on band gap, need to wait 5us before + * turning on the rest of the PAD */ + mddi_writel(0x08000, PAD_CTL); + udelay(5); + } + + /* Recommendation from PAD hw team */ + mddi_writel(0xa850f, PAD_CTL); + + + /* Need an even number for counts */ + mddi_writel(0x60006, DRIVER_START_CNT); + + mddi_set_auto_hibernate(&mddi->client_data, 0); + + mddi_writel(MDDI_CMD_DISP_IGNORE, CMD); + mddi_wait_interrupt(mddi, MDDI_INT_NO_CMD_PKTS_PEND); + + mddi_init_rev_encap(mddi); + return mddi_readl(CORE_VER) & 0xffff; +} + +static void mddi_suspend(struct msm_mddi_client_data *cdata) +{ + struct mddi_info *mddi = container_of(cdata, struct mddi_info, + client_data); + /* turn off the client */ + if (mddi->power_client) + mddi->power_client(&mddi->client_data, 0); + /* turn off the link */ + mddi_writel(MDDI_CMD_RESET, CMD); + mddi_wait_interrupt(mddi, MDDI_INT_NO_CMD_PKTS_PEND); + /* turn off the clock */ + clk_disable(mddi->clk); +} + +static void mddi_resume(struct msm_mddi_client_data *cdata) +{ + struct mddi_info *mddi = container_of(cdata, struct mddi_info, + client_data); + mddi_set_auto_hibernate(&mddi->client_data, 0); + /* turn on the client */ + if (mddi->power_client) + mddi->power_client(&mddi->client_data, 1); + /* turn on the clock */ + clk_enable(mddi->clk); + /* set up the local registers */ + mddi->rev_data_curr = 0; + mddi_init_registers(mddi); + mddi_writel(mddi->int_enable, INTEN); + mddi_writel(MDDI_CMD_LINK_ACTIVE, CMD); + mddi_writel(MDDI_CMD_SEND_RTD, CMD); + mddi_wait_interrupt(mddi, MDDI_INT_NO_CMD_PKTS_PEND); + mddi_set_auto_hibernate(&mddi->client_data, 1); +} + +static int __init mddi_get_client_caps(struct mddi_info *mddi) +{ + int i, j; + + /* clear any stale interrupts */ + mddi_writel(0xffffffff, INT); + + mddi->int_enable = MDDI_INT_LINK_ACTIVE | + MDDI_INT_IN_HIBERNATION | + MDDI_INT_PRI_LINK_LIST_DONE | + MDDI_INT_REV_DATA_AVAIL | + MDDI_INT_REV_OVERFLOW | + MDDI_INT_REV_OVERWRITE | + MDDI_INT_RTD_FAILURE; + mddi_writel(mddi->int_enable, INTEN); + + mddi_writel(MDDI_CMD_LINK_ACTIVE, CMD); + mddi_wait_interrupt(mddi, MDDI_INT_NO_CMD_PKTS_PEND); + + for (j = 0; j < 3; j++) { + /* the toshiba vga panel does not respond to get + * caps unless you SEND_RTD, but the first SEND_RTD + * will fail... + */ + for (i = 0; i < 4; i++) { + uint32_t stat; + + mddi_writel(MDDI_CMD_SEND_RTD, CMD); + mddi_wait_interrupt(mddi, MDDI_INT_NO_CMD_PKTS_PEND); + stat = mddi_readl(STAT); + printk(KERN_INFO "mddi cmd send rtd: int %x, stat %x, " + "rtd val %x\n", mddi_readl(INT), stat, + mddi_readl(RTD_VAL)); + if ((stat & MDDI_STAT_RTD_MEAS_FAIL) == 0) + break; + msleep(1); + } + + mddi_writel(CMD_GET_CLIENT_CAP, CMD); + mddi_wait_interrupt(mddi, MDDI_INT_NO_CMD_PKTS_PEND); + wait_event_timeout(mddi->int_wait, mddi->flags & FLAG_HAVE_CAPS, + HZ / 100); + + if (mddi->flags & FLAG_HAVE_CAPS) + break; + printk(KERN_INFO KERN_ERR "mddi_init, timeout waiting for " + "caps\n"); + } + return mddi->flags & FLAG_HAVE_CAPS; +} + +/* link must be active when this is called */ +int mddi_check_status(struct mddi_info *mddi) +{ + int ret = -1, retry = 3; + mutex_lock(&mddi->reg_read_lock); + mddi_writel(MDDI_CMD_PERIODIC_REV_ENCAP | 1, CMD); + mddi_wait_interrupt(mddi, MDDI_INT_NO_CMD_PKTS_PEND); + + do { + mddi->flags &= ~FLAG_HAVE_STATUS; + mddi_writel(CMD_GET_CLIENT_STATUS, CMD); + mddi_wait_interrupt(mddi, MDDI_INT_NO_CMD_PKTS_PEND); + wait_event_timeout(mddi->int_wait, + mddi->flags & FLAG_HAVE_STATUS, + HZ / 100); + + if (mddi->flags & FLAG_HAVE_STATUS) { + if (mddi->status.crc_error_count) + printk(KERN_INFO "mddi status: crc_error " + "count: %d\n", + mddi->status.crc_error_count); + else + ret = 0; + break; + } else + printk(KERN_INFO "mddi status: failed to get client " + "status\n"); + mddi_writel(MDDI_CMD_SEND_RTD, CMD); + mddi_wait_interrupt(mddi, MDDI_INT_NO_CMD_PKTS_PEND); + } while (--retry); + + mddi_writel(MDDI_CMD_PERIODIC_REV_ENCAP | 0, CMD); + mddi_wait_interrupt(mddi, MDDI_INT_NO_CMD_PKTS_PEND); + mutex_unlock(&mddi->reg_read_lock); + return ret; +} + + +void mddi_remote_write(struct msm_mddi_client_data *cdata, uint32_t val, + uint32_t reg) +{ + struct mddi_info *mddi = container_of(cdata, struct mddi_info, + client_data); + struct mddi_llentry *ll; + struct mddi_register_access *ra; + + mutex_lock(&mddi->reg_write_lock); + + ll = mddi->reg_write_data; + + ra = &(ll->u.r); + ra->length = 14 + 4; + ra->type = TYPE_REGISTER_ACCESS; + ra->client_id = 0; + ra->read_write_info = MDDI_WRITE | 1; + ra->crc16 = 0; + + ra->register_address = reg; + ra->register_data_list = val; + + ll->flags = 1; + ll->header_count = 14; + ll->data_count = 4; + ll->data = mddi->reg_write_addr + offsetof(struct mddi_llentry, + u.r.register_data_list); + ll->next = 0; + ll->reserved = 0; + + mddi_writel(mddi->reg_write_addr, PRI_PTR); + + mddi_wait_interrupt(mddi, MDDI_INT_PRI_LINK_LIST_DONE); + mutex_unlock(&mddi->reg_write_lock); +} + +uint32_t mddi_remote_read(struct msm_mddi_client_data *cdata, uint32_t reg) +{ + struct mddi_info *mddi = container_of(cdata, struct mddi_info, + client_data); + struct mddi_llentry *ll; + struct mddi_register_access *ra; + struct reg_read_info ri; + unsigned s; + int retry_count = 2; + unsigned long irq_flags; + + mutex_lock(&mddi->reg_read_lock); + + ll = mddi->reg_read_data; + + ra = &(ll->u.r); + ra->length = 14; + ra->type = TYPE_REGISTER_ACCESS; + ra->client_id = 0; + ra->read_write_info = MDDI_READ | 1; + ra->crc16 = 0; + + ra->register_address = reg; + + ll->flags = 0x11; + ll->header_count = 14; + ll->data_count = 0; + ll->data = 0; + ll->next = 0; + ll->reserved = 0; + + s = mddi_readl(STAT); + + ri.reg = reg; + ri.status = -1; + + do { + init_completion(&ri.done); + mddi->reg_read = &ri; + mddi_writel(mddi->reg_read_addr, PRI_PTR); + + mddi_wait_interrupt(mddi, MDDI_INT_PRI_LINK_LIST_DONE); + + /* Enable Periodic Reverse Encapsulation. */ + mddi_writel(MDDI_CMD_PERIODIC_REV_ENCAP | 1, CMD); + mddi_wait_interrupt(mddi, MDDI_INT_NO_CMD_PKTS_PEND); + if (wait_for_completion_timeout(&ri.done, HZ/10) == 0 && + !ri.done.done) { + printk(KERN_INFO "mddi_remote_read(%x) timeout " + "(%d %d %d)\n", + reg, ri.status, ri.result, ri.done.done); + spin_lock_irqsave(&mddi->int_lock, irq_flags); + mddi->reg_read = NULL; + spin_unlock_irqrestore(&mddi->int_lock, irq_flags); + ri.status = -1; + ri.result = -1; + } + if (ri.status == 0) + break; + + mddi_writel(MDDI_CMD_SEND_RTD, CMD); + mddi_writel(MDDI_CMD_LINK_ACTIVE, CMD); + mddi_wait_interrupt(mddi, MDDI_INT_NO_CMD_PKTS_PEND); + printk(KERN_INFO "mddi_remote_read: failed, sent " + "MDDI_CMD_SEND_RTD: int %x, stat %x, rtd val %x " + "curr_rev_ptr %x\n", mddi_readl(INT), mddi_readl(STAT), + mddi_readl(RTD_VAL), mddi_readl(CURR_REV_PTR)); + } while (retry_count-- > 0); + /* Disable Periodic Reverse Encapsulation. */ + mddi_writel(MDDI_CMD_PERIODIC_REV_ENCAP | 0, CMD); + mddi_wait_interrupt(mddi, MDDI_INT_NO_CMD_PKTS_PEND); + mddi->reg_read = NULL; + mutex_unlock(&mddi->reg_read_lock); + return ri.result; +} + +static struct mddi_info mddi_info[2]; + +static int __init mddi_clk_setup(struct platform_device *pdev, + struct mddi_info *mddi, + unsigned long clk_rate) +{ + int ret; + + /* set up the clocks */ + mddi->clk = clk_get(&pdev->dev, "mddi_clk"); + if (IS_ERR(mddi->clk)) { + printk(KERN_INFO "mddi: failed to get clock\n"); + return PTR_ERR(mddi->clk); + } + ret = clk_enable(mddi->clk); + if (ret) + goto fail; + ret = clk_set_rate(mddi->clk, clk_rate); + if (ret) + goto fail; + return 0; + +fail: + clk_put(mddi->clk); + return ret; +} + +static int __init mddi_rev_data_setup(struct mddi_info *mddi) +{ + void *dma; + dma_addr_t dma_addr; + + /* set up dma buffer */ + dma = dma_alloc_coherent(NULL, 0x1000, &dma_addr, GFP_KERNEL); + if (dma == 0) + return -ENOMEM; + mddi->rev_data = dma; + mddi->rev_data_curr = 0; + mddi->rev_addr = dma_addr; + mddi->reg_write_data = dma + MDDI_REV_BUFFER_SIZE; + mddi->reg_write_addr = dma_addr + MDDI_REV_BUFFER_SIZE; + mddi->reg_read_data = mddi->reg_write_data + 1; + mddi->reg_read_addr = mddi->reg_write_addr + + sizeof(*mddi->reg_write_data); + return 0; +} + +static int __init mddi_probe(struct platform_device *pdev) +{ + struct msm_mddi_platform_data *pdata = pdev->dev.platform_data; + struct mddi_info *mddi = &mddi_info[pdev->id]; + struct resource *resource; + int ret, i; + + resource = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!resource) { + printk(KERN_ERR "mddi: no associated mem resource!\n"); + return -ENOMEM; + } + mddi->base = ioremap(resource->start, resource->end - resource->start); + if (!mddi->base) { + printk(KERN_ERR "mddi: failed to remap base!\n"); + ret = -EINVAL; + goto error_ioremap; + } + resource = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (!resource) { + printk(KERN_ERR "mddi: no associated irq resource!\n"); + ret = -EINVAL; + goto error_get_irq_resource; + } + mddi->irq = resource->start; + printk(KERN_INFO "mddi: init() base=0x%p irq=%d\n", mddi->base, + mddi->irq); + mddi->power_client = pdata->power_client; + + mutex_init(&mddi->reg_write_lock); + mutex_init(&mddi->reg_read_lock); + spin_lock_init(&mddi->int_lock); + init_waitqueue_head(&mddi->int_wait); + + ret = mddi_clk_setup(pdev, mddi, pdata->clk_rate); + if (ret) { + printk(KERN_ERR "mddi: failed to setup clock!\n"); + goto error_clk_setup; + } + + ret = mddi_rev_data_setup(mddi); + if (ret) { + printk(KERN_ERR "mddi: failed to setup rev data!\n"); + goto error_rev_data; + } + + mddi->int_enable = 0; + mddi_writel(mddi->int_enable, INTEN); + ret = request_irq(mddi->irq, mddi_isr, IRQF_DISABLED, "mddi", + &mddi->client_data); + if (ret) { + printk(KERN_ERR "mddi: failed to request enable irq!\n"); + goto error_request_irq; + } + + /* turn on the mddi client bridge chip */ + if (mddi->power_client) + mddi->power_client(&mddi->client_data, 1); + + /* initialize the mddi registers */ + mddi_set_auto_hibernate(&mddi->client_data, 0); + mddi_writel(MDDI_CMD_RESET, CMD); + mddi_wait_interrupt(mddi, MDDI_INT_NO_CMD_PKTS_PEND); + mddi->version = mddi_init_registers(mddi); + if (mddi->version < 0x20) { + printk(KERN_ERR "mddi: unsupported version 0x%x\n", + mddi->version); + ret = -ENODEV; + goto error_mddi_version; + } + + /* read the capabilities off the client */ + if (!mddi_get_client_caps(mddi)) { + printk(KERN_INFO "mddi: no client found\n"); + /* power down the panel */ + mddi_writel(MDDI_CMD_POWERDOWN, CMD); + printk(KERN_INFO "mddi powerdown: stat %x\n", mddi_readl(STAT)); + msleep(100); + printk(KERN_INFO "mddi powerdown: stat %x\n", mddi_readl(STAT)); + return 0; + } + mddi_set_auto_hibernate(&mddi->client_data, 1); + + if (mddi->caps.Mfr_Name == 0 && mddi->caps.Product_Code == 0) + pdata->fixup(&mddi->caps.Mfr_Name, &mddi->caps.Product_Code); + + mddi->client_pdev.id = 0; + for (i = 0; i < pdata->num_clients; i++) { + if (pdata->client_platform_data[i].product_id == + (mddi->caps.Mfr_Name << 16 | mddi->caps.Product_Code)) { + mddi->client_data.private_client_data = + pdata->client_platform_data[i].client_data; + mddi->client_pdev.name = + pdata->client_platform_data[i].name; + mddi->client_pdev.id = + pdata->client_platform_data[i].id; + /* XXX: possibly set clock */ + break; + } + } + + if (i >= pdata->num_clients) + mddi->client_pdev.name = "mddi_c_dummy"; + printk(KERN_INFO "mddi: registering panel %s\n", + mddi->client_pdev.name); + + mddi->client_data.suspend = mddi_suspend; + mddi->client_data.resume = mddi_resume; + mddi->client_data.activate_link = mddi_activate_link; + mddi->client_data.remote_write = mddi_remote_write; + mddi->client_data.remote_read = mddi_remote_read; + mddi->client_data.auto_hibernate = mddi_set_auto_hibernate; + mddi->client_data.fb_resource = pdata->fb_resource; + if (pdev->id == 0) + mddi->client_data.interface_type = MSM_MDDI_PMDH_INTERFACE; + else if (pdev->id == 1) + mddi->client_data.interface_type = MSM_MDDI_EMDH_INTERFACE; + else { + printk(KERN_ERR "mddi: can not determine interface %d!\n", + pdev->id); + ret = -EINVAL; + goto error_mddi_interface; + } + + mddi->client_pdev.dev.platform_data = &mddi->client_data; + printk(KERN_INFO "mddi: publish: %s\n", mddi->client_name); + platform_device_register(&mddi->client_pdev); + return 0; + +error_mddi_interface: +error_mddi_version: + free_irq(mddi->irq, 0); +error_request_irq: + dma_free_coherent(NULL, 0x1000, mddi->rev_data, mddi->rev_addr); +error_rev_data: +error_clk_setup: +error_get_irq_resource: + iounmap(mddi->base); +error_ioremap: + + printk(KERN_INFO "mddi: mddi_init() failed (%d)\n", ret); + return ret; +} + + +static struct platform_driver mddi_driver = { + .probe = mddi_probe, + .driver = { .name = "msm_mddi" }, +}; + +static int __init _mddi_init(void) +{ + return platform_driver_register(&mddi_driver); +} + +module_init(_mddi_init); diff --git a/drivers/video/msm/mddi_client_dummy.c b/drivers/video/msm/mddi_client_dummy.c new file mode 100644 index 000000000000..ebbae87885b6 --- /dev/null +++ b/drivers/video/msm/mddi_client_dummy.c @@ -0,0 +1,97 @@ +/* drivers/video/msm_fb/mddi_client_dummy.c + * + * Support for "dummy" mddi client devices which require no + * special initialization code. + * + * Copyright (C) 2007 Google Incorporated + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/platform_device.h> + +#include <mach/msm_fb.h> + +struct panel_info { + struct platform_device pdev; + struct msm_panel_data panel_data; +}; + +static int mddi_dummy_suspend(struct msm_panel_data *panel_data) +{ + return 0; +} + +static int mddi_dummy_resume(struct msm_panel_data *panel_data) +{ + return 0; +} + +static int mddi_dummy_blank(struct msm_panel_data *panel_data) +{ + return 0; +} + +static int mddi_dummy_unblank(struct msm_panel_data *panel_data) +{ + return 0; +} + +static int mddi_dummy_probe(struct platform_device *pdev) +{ + struct msm_mddi_client_data *client_data = pdev->dev.platform_data; + struct panel_info *panel = + kzalloc(sizeof(struct panel_info), GFP_KERNEL); + int ret; + if (!panel) + return -ENOMEM; + platform_set_drvdata(pdev, panel); + panel->panel_data.suspend = mddi_dummy_suspend; + panel->panel_data.resume = mddi_dummy_resume; + panel->panel_data.blank = mddi_dummy_blank; + panel->panel_data.unblank = mddi_dummy_unblank; + panel->panel_data.caps = MSMFB_CAP_PARTIAL_UPDATES; + panel->pdev.name = "msm_panel"; + panel->pdev.id = pdev->id; + platform_device_add_resources(&panel->pdev, + client_data->fb_resource, 1); + panel->panel_data.fb_data = client_data->private_client_data; + panel->pdev.dev.platform_data = &panel->panel_data; + ret = platform_device_register(&panel->pdev); + if (ret) { + kfree(panel); + return ret; + } + return 0; +} + +static int mddi_dummy_remove(struct platform_device *pdev) +{ + struct panel_info *panel = platform_get_drvdata(pdev); + kfree(panel); + return 0; +} + +static struct platform_driver mddi_client_dummy = { + .probe = mddi_dummy_probe, + .remove = mddi_dummy_remove, + .driver = { .name = "mddi_c_dummy" }, +}; + +static int __init mddi_client_dummy_init(void) +{ + platform_driver_register(&mddi_client_dummy); + return 0; +} + +module_init(mddi_client_dummy_init); + diff --git a/drivers/video/msm/mddi_client_nt35399.c b/drivers/video/msm/mddi_client_nt35399.c new file mode 100644 index 000000000000..c9e9349451cb --- /dev/null +++ b/drivers/video/msm/mddi_client_nt35399.c @@ -0,0 +1,256 @@ +/* drivers/video/msm_fb/mddi_client_nt35399.c + * + * Support for Novatek NT35399 MDDI client of Sapphire + * + * Copyright (C) 2008 HTC Incorporated + * Author: Solomon Chiu (solomon_chiu@htc.com) + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/platform_device.h> +#include <linux/interrupt.h> +#include <linux/sched.h> +#include <linux/gpio.h> +#include <mach/msm_fb.h> + +static DECLARE_WAIT_QUEUE_HEAD(nt35399_vsync_wait); + +struct panel_info { + struct msm_mddi_client_data *client_data; + struct platform_device pdev; + struct msm_panel_data panel_data; + struct msmfb_callback *fb_callback; + struct work_struct panel_work; + struct workqueue_struct *fb_wq; + int nt35399_got_int; +}; + +static void +nt35399_request_vsync(struct msm_panel_data *panel_data, + struct msmfb_callback *callback) +{ + struct panel_info *panel = container_of(panel_data, struct panel_info, + panel_data); + struct msm_mddi_client_data *client_data = panel->client_data; + + panel->fb_callback = callback; + if (panel->nt35399_got_int) { + panel->nt35399_got_int = 0; + client_data->activate_link(client_data); /* clears interrupt */ + } +} + +static void nt35399_wait_vsync(struct msm_panel_data *panel_data) +{ + struct panel_info *panel = container_of(panel_data, struct panel_info, + panel_data); + struct msm_mddi_client_data *client_data = panel->client_data; + + if (panel->nt35399_got_int) { + panel->nt35399_got_int = 0; + client_data->activate_link(client_data); /* clears interrupt */ + } + + if (wait_event_timeout(nt35399_vsync_wait, panel->nt35399_got_int, + HZ/2) == 0) + printk(KERN_ERR "timeout waiting for VSYNC\n"); + + panel->nt35399_got_int = 0; + /* interrupt clears when screen dma starts */ +} + +static int nt35399_suspend(struct msm_panel_data *panel_data) +{ + struct panel_info *panel = container_of(panel_data, struct panel_info, + panel_data); + struct msm_mddi_client_data *client_data = panel->client_data; + + struct msm_mddi_bridge_platform_data *bridge_data = + client_data->private_client_data; + int ret; + + ret = bridge_data->uninit(bridge_data, client_data); + if (ret) { + printk(KERN_INFO "mddi nt35399 client: non zero return from " + "uninit\n"); + return ret; + } + client_data->suspend(client_data); + return 0; +} + +static int nt35399_resume(struct msm_panel_data *panel_data) +{ + struct panel_info *panel = container_of(panel_data, struct panel_info, + panel_data); + struct msm_mddi_client_data *client_data = panel->client_data; + + struct msm_mddi_bridge_platform_data *bridge_data = + client_data->private_client_data; + int ret; + + client_data->resume(client_data); + ret = bridge_data->init(bridge_data, client_data); + if (ret) + return ret; + return 0; +} + +static int nt35399_blank(struct msm_panel_data *panel_data) +{ + struct panel_info *panel = container_of(panel_data, struct panel_info, + panel_data); + struct msm_mddi_client_data *client_data = panel->client_data; + struct msm_mddi_bridge_platform_data *bridge_data = + client_data->private_client_data; + + return bridge_data->blank(bridge_data, client_data); +} + +static int nt35399_unblank(struct msm_panel_data *panel_data) +{ + struct panel_info *panel = container_of(panel_data, struct panel_info, + panel_data); + struct msm_mddi_client_data *client_data = panel->client_data; + struct msm_mddi_bridge_platform_data *bridge_data = + client_data->private_client_data; + + return bridge_data->unblank(bridge_data, client_data); +} + +irqreturn_t nt35399_vsync_interrupt(int irq, void *data) +{ + struct panel_info *panel = data; + + panel->nt35399_got_int = 1; + + if (panel->fb_callback) { + panel->fb_callback->func(panel->fb_callback); + panel->fb_callback = NULL; + } + + wake_up(&nt35399_vsync_wait); + + return IRQ_HANDLED; +} + +static int setup_vsync(struct panel_info *panel, int init) +{ + int ret; + int gpio = 97; + unsigned int irq; + + if (!init) { + ret = 0; + goto uninit; + } + ret = gpio_request(gpio, "vsync"); + if (ret) + goto err_request_gpio_failed; + + ret = gpio_direction_input(gpio); + if (ret) + goto err_gpio_direction_input_failed; + + ret = irq = gpio_to_irq(gpio); + if (ret < 0) + goto err_get_irq_num_failed; + + ret = request_irq(irq, nt35399_vsync_interrupt, IRQF_TRIGGER_RISING, + "vsync", panel); + if (ret) + goto err_request_irq_failed; + + printk(KERN_INFO "vsync on gpio %d now %d\n", + gpio, gpio_get_value(gpio)); + return 0; + +uninit: + free_irq(gpio_to_irq(gpio), panel->client_data); +err_request_irq_failed: +err_get_irq_num_failed: +err_gpio_direction_input_failed: + gpio_free(gpio); +err_request_gpio_failed: + return ret; +} + +static int mddi_nt35399_probe(struct platform_device *pdev) +{ + struct msm_mddi_client_data *client_data = pdev->dev.platform_data; + struct msm_mddi_bridge_platform_data *bridge_data = + client_data->private_client_data; + + int ret; + + struct panel_info *panel = kzalloc(sizeof(struct panel_info), + GFP_KERNEL); + + printk(KERN_DEBUG "%s: enter.\n", __func__); + + if (!panel) + return -ENOMEM; + platform_set_drvdata(pdev, panel); + + ret = setup_vsync(panel, 1); + if (ret) { + dev_err(&pdev->dev, "mddi_nt35399_setup_vsync failed\n"); + return ret; + } + + panel->client_data = client_data; + panel->panel_data.suspend = nt35399_suspend; + panel->panel_data.resume = nt35399_resume; + panel->panel_data.wait_vsync = nt35399_wait_vsync; + panel->panel_data.request_vsync = nt35399_request_vsync; + panel->panel_data.blank = nt35399_blank; + panel->panel_data.unblank = nt35399_unblank; + panel->panel_data.fb_data = &bridge_data->fb_data; + panel->panel_data.caps = 0; + + panel->pdev.name = "msm_panel"; + panel->pdev.id = pdev->id; + panel->pdev.resource = client_data->fb_resource; + panel->pdev.num_resources = 1; + panel->pdev.dev.platform_data = &panel->panel_data; + + if (bridge_data->init) + bridge_data->init(bridge_data, client_data); + + platform_device_register(&panel->pdev); + + return 0; +} + +static int mddi_nt35399_remove(struct platform_device *pdev) +{ + struct panel_info *panel = platform_get_drvdata(pdev); + + setup_vsync(panel, 0); + kfree(panel); + return 0; +} + +static struct platform_driver mddi_client_0bda_8a47 = { + .probe = mddi_nt35399_probe, + .remove = mddi_nt35399_remove, + .driver = { .name = "mddi_c_0bda_8a47" }, +}; + +static int __init mddi_client_nt35399_init(void) +{ + return platform_driver_register(&mddi_client_0bda_8a47); +} + +module_init(mddi_client_nt35399_init); + diff --git a/drivers/video/msm/mddi_client_toshiba.c b/drivers/video/msm/mddi_client_toshiba.c new file mode 100644 index 000000000000..71048e78f7f0 --- /dev/null +++ b/drivers/video/msm/mddi_client_toshiba.c @@ -0,0 +1,284 @@ +/* drivers/video/msm_fb/mddi_client_toshiba.c + * + * Support for Toshiba TC358720XBG mddi client devices which require no + * special initialization code. + * + * Copyright (C) 2007 Google Incorporated + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/platform_device.h> +#include <linux/interrupt.h> +#include <linux/gpio.h> +#include <linux/sched.h> +#include <mach/msm_fb.h> + + +#define LCD_CONTROL_BLOCK_BASE 0x110000 +#define CMN (LCD_CONTROL_BLOCK_BASE|0x10) +#define INTFLG (LCD_CONTROL_BLOCK_BASE|0x18) +#define HCYCLE (LCD_CONTROL_BLOCK_BASE|0x34) +#define HDE_START (LCD_CONTROL_BLOCK_BASE|0x3C) +#define VPOS (LCD_CONTROL_BLOCK_BASE|0xC0) +#define MPLFBUF (LCD_CONTROL_BLOCK_BASE|0x20) +#define WAKEUP (LCD_CONTROL_BLOCK_BASE|0x54) +#define WSYN_DLY (LCD_CONTROL_BLOCK_BASE|0x58) +#define REGENB (LCD_CONTROL_BLOCK_BASE|0x5C) + +#define BASE5 0x150000 +#define BASE6 0x160000 +#define BASE7 0x170000 + +#define GPIOIEV (BASE5 + 0x10) +#define GPIOIE (BASE5 + 0x14) +#define GPIORIS (BASE5 + 0x18) +#define GPIOMIS (BASE5 + 0x1C) +#define GPIOIC (BASE5 + 0x20) + +#define INTMASK (BASE6 + 0x0C) +#define INTMASK_VWAKEOUT (1U << 0) +#define INTMASK_VWAKEOUT_ACTIVE_LOW (1U << 8) +#define GPIOSEL (BASE7 + 0x00) +#define GPIOSEL_VWAKEINT (1U << 0) + +static DECLARE_WAIT_QUEUE_HEAD(toshiba_vsync_wait); + +struct panel_info { + struct msm_mddi_client_data *client_data; + struct platform_device pdev; + struct msm_panel_data panel_data; + struct msmfb_callback *toshiba_callback; + int toshiba_got_int; +}; + + +static void toshiba_request_vsync(struct msm_panel_data *panel_data, + struct msmfb_callback *callback) +{ + struct panel_info *panel = container_of(panel_data, struct panel_info, + panel_data); + struct msm_mddi_client_data *client_data = panel->client_data; + + panel->toshiba_callback = callback; + if (panel->toshiba_got_int) { + panel->toshiba_got_int = 0; + client_data->activate_link(client_data); + } +} + +static void toshiba_clear_vsync(struct msm_panel_data *panel_data) +{ + struct panel_info *panel = container_of(panel_data, struct panel_info, + panel_data); + struct msm_mddi_client_data *client_data = panel->client_data; + + client_data->activate_link(client_data); +} + +static void toshiba_wait_vsync(struct msm_panel_data *panel_data) +{ + struct panel_info *panel = container_of(panel_data, struct panel_info, + panel_data); + struct msm_mddi_client_data *client_data = panel->client_data; + + if (panel->toshiba_got_int) { + panel->toshiba_got_int = 0; + client_data->activate_link(client_data); /* clears interrupt */ + } + if (wait_event_timeout(toshiba_vsync_wait, panel->toshiba_got_int, + HZ/2) == 0) + printk(KERN_ERR "timeout waiting for VSYNC\n"); + panel->toshiba_got_int = 0; + /* interrupt clears when screen dma starts */ +} + +static int toshiba_suspend(struct msm_panel_data *panel_data) +{ + struct panel_info *panel = container_of(panel_data, struct panel_info, + panel_data); + struct msm_mddi_client_data *client_data = panel->client_data; + + struct msm_mddi_bridge_platform_data *bridge_data = + client_data->private_client_data; + int ret; + + ret = bridge_data->uninit(bridge_data, client_data); + if (ret) { + printk(KERN_INFO "mddi toshiba client: non zero return from " + "uninit\n"); + return ret; + } + client_data->suspend(client_data); + return 0; +} + +static int toshiba_resume(struct msm_panel_data *panel_data) +{ + struct panel_info *panel = container_of(panel_data, struct panel_info, + panel_data); + struct msm_mddi_client_data *client_data = panel->client_data; + + struct msm_mddi_bridge_platform_data *bridge_data = + client_data->private_client_data; + int ret; + + client_data->resume(client_data); + ret = bridge_data->init(bridge_data, client_data); + if (ret) + return ret; + return 0; +} + +static int toshiba_blank(struct msm_panel_data *panel_data) +{ + struct panel_info *panel = container_of(panel_data, struct panel_info, + panel_data); + struct msm_mddi_client_data *client_data = panel->client_data; + struct msm_mddi_bridge_platform_data *bridge_data = + client_data->private_client_data; + + return bridge_data->blank(bridge_data, client_data); +} + +static int toshiba_unblank(struct msm_panel_data *panel_data) +{ + struct panel_info *panel = container_of(panel_data, struct panel_info, + panel_data); + struct msm_mddi_client_data *client_data = panel->client_data; + struct msm_mddi_bridge_platform_data *bridge_data = + client_data->private_client_data; + + return bridge_data->unblank(bridge_data, client_data); +} + +irqreturn_t toshiba_vsync_interrupt(int irq, void *data) +{ + struct panel_info *panel = data; + + panel->toshiba_got_int = 1; + if (panel->toshiba_callback) { + panel->toshiba_callback->func(panel->toshiba_callback); + panel->toshiba_callback = 0; + } + wake_up(&toshiba_vsync_wait); + return IRQ_HANDLED; +} + +static int setup_vsync(struct panel_info *panel, + int init) +{ + int ret; + int gpio = 97; + unsigned int irq; + + if (!init) { + ret = 0; + goto uninit; + } + ret = gpio_request(gpio, "vsync"); + if (ret) + goto err_request_gpio_failed; + + ret = gpio_direction_input(gpio); + if (ret) + goto err_gpio_direction_input_failed; + + ret = irq = gpio_to_irq(gpio); + if (ret < 0) + goto err_get_irq_num_failed; + + ret = request_irq(irq, toshiba_vsync_interrupt, IRQF_TRIGGER_RISING, + "vsync", panel); + if (ret) + goto err_request_irq_failed; + printk(KERN_INFO "vsync on gpio %d now %d\n", + gpio, gpio_get_value(gpio)); + return 0; + +uninit: + free_irq(gpio_to_irq(gpio), panel); +err_request_irq_failed: +err_get_irq_num_failed: +err_gpio_direction_input_failed: + gpio_free(gpio); +err_request_gpio_failed: + return ret; +} + +static int mddi_toshiba_probe(struct platform_device *pdev) +{ + int ret; + struct msm_mddi_client_data *client_data = pdev->dev.platform_data; + struct msm_mddi_bridge_platform_data *bridge_data = + client_data->private_client_data; + struct panel_info *panel = + kzalloc(sizeof(struct panel_info), GFP_KERNEL); + if (!panel) + return -ENOMEM; + platform_set_drvdata(pdev, panel); + + /* mddi_remote_write(mddi, 0, WAKEUP); */ + client_data->remote_write(client_data, GPIOSEL_VWAKEINT, GPIOSEL); + client_data->remote_write(client_data, INTMASK_VWAKEOUT, INTMASK); + + ret = setup_vsync(panel, 1); + if (ret) { + dev_err(&pdev->dev, "mddi_bridge_setup_vsync failed\n"); + return ret; + } + + panel->client_data = client_data; + panel->panel_data.suspend = toshiba_suspend; + panel->panel_data.resume = toshiba_resume; + panel->panel_data.wait_vsync = toshiba_wait_vsync; + panel->panel_data.request_vsync = toshiba_request_vsync; + panel->panel_data.clear_vsync = toshiba_clear_vsync; + panel->panel_data.blank = toshiba_blank; + panel->panel_data.unblank = toshiba_unblank; + panel->panel_data.fb_data = &bridge_data->fb_data; + panel->panel_data.caps = MSMFB_CAP_PARTIAL_UPDATES; + + panel->pdev.name = "msm_panel"; + panel->pdev.id = pdev->id; + panel->pdev.resource = client_data->fb_resource; + panel->pdev.num_resources = 1; + panel->pdev.dev.platform_data = &panel->panel_data; + bridge_data->init(bridge_data, client_data); + platform_device_register(&panel->pdev); + + return 0; +} + +static int mddi_toshiba_remove(struct platform_device *pdev) +{ + struct panel_info *panel = platform_get_drvdata(pdev); + + setup_vsync(panel, 0); + kfree(panel); + return 0; +} + +static struct platform_driver mddi_client_d263_0000 = { + .probe = mddi_toshiba_probe, + .remove = mddi_toshiba_remove, + .driver = { .name = "mddi_c_d263_0000" }, +}; + +static int __init mddi_client_toshiba_init(void) +{ + platform_driver_register(&mddi_client_d263_0000); + return 0; +} + +module_init(mddi_client_toshiba_init); + diff --git a/drivers/video/msm/mddi_hw.h b/drivers/video/msm/mddi_hw.h new file mode 100644 index 000000000000..45cc01fc1e7f --- /dev/null +++ b/drivers/video/msm/mddi_hw.h @@ -0,0 +1,305 @@ +/* drivers/video/msm_fb/mddi_hw.h + * + * MSM MDDI Hardware Registers and Structures + * + * Copyright (C) 2007 QUALCOMM Incorporated + * Copyright (C) 2007 Google Incorporated + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef _MDDI_HW_H_ +#define _MDDI_HW_H_ + +#include <linux/types.h> + +#define MDDI_CMD 0x0000 +#define MDDI_VERSION 0x0004 +#define MDDI_PRI_PTR 0x0008 +#define MDDI_SEC_PTR 0x000c +#define MDDI_BPS 0x0010 +#define MDDI_SPM 0x0014 +#define MDDI_INT 0x0018 +#define MDDI_INTEN 0x001c +#define MDDI_REV_PTR 0x0020 +#define MDDI_REV_SIZE 0x0024 +#define MDDI_STAT 0x0028 +#define MDDI_REV_RATE_DIV 0x002c +#define MDDI_REV_CRC_ERR 0x0030 +#define MDDI_TA1_LEN 0x0034 +#define MDDI_TA2_LEN 0x0038 +#define MDDI_TEST_BUS 0x003c +#define MDDI_TEST 0x0040 +#define MDDI_REV_PKT_CNT 0x0044 +#define MDDI_DRIVE_HI 0x0048 +#define MDDI_DRIVE_LO 0x004c +#define MDDI_DISP_WAKE 0x0050 +#define MDDI_REV_ENCAP_SZ 0x0054 +#define MDDI_RTD_VAL 0x0058 +#define MDDI_PAD_CTL 0x0068 +#define MDDI_DRIVER_START_CNT 0x006c +#define MDDI_NEXT_PRI_PTR 0x0070 +#define MDDI_NEXT_SEC_PTR 0x0074 +#define MDDI_MISR_CTL 0x0078 +#define MDDI_MISR_DATA 0x007c +#define MDDI_SF_CNT 0x0080 +#define MDDI_MF_CNT 0x0084 +#define MDDI_CURR_REV_PTR 0x0088 +#define MDDI_CORE_VER 0x008c + +#define MDDI_INT_PRI_PTR_READ 0x0001 +#define MDDI_INT_SEC_PTR_READ 0x0002 +#define MDDI_INT_REV_DATA_AVAIL 0x0004 +#define MDDI_INT_DISP_REQ 0x0008 +#define MDDI_INT_PRI_UNDERFLOW 0x0010 +#define MDDI_INT_SEC_UNDERFLOW 0x0020 +#define MDDI_INT_REV_OVERFLOW 0x0040 +#define MDDI_INT_CRC_ERROR 0x0080 +#define MDDI_INT_MDDI_IN 0x0100 +#define MDDI_INT_PRI_OVERWRITE 0x0200 +#define MDDI_INT_SEC_OVERWRITE 0x0400 +#define MDDI_INT_REV_OVERWRITE 0x0800 +#define MDDI_INT_DMA_FAILURE 0x1000 +#define MDDI_INT_LINK_ACTIVE 0x2000 +#define MDDI_INT_IN_HIBERNATION 0x4000 +#define MDDI_INT_PRI_LINK_LIST_DONE 0x8000 +#define MDDI_INT_SEC_LINK_LIST_DONE 0x10000 +#define MDDI_INT_NO_CMD_PKTS_PEND 0x20000 +#define MDDI_INT_RTD_FAILURE 0x40000 +#define MDDI_INT_REV_PKT_RECEIVED 0x80000 +#define MDDI_INT_REV_PKTS_AVAIL 0x100000 + +#define MDDI_INT_NEED_CLEAR ( \ + MDDI_INT_REV_DATA_AVAIL | \ + MDDI_INT_PRI_UNDERFLOW | \ + MDDI_INT_SEC_UNDERFLOW | \ + MDDI_INT_REV_OVERFLOW | \ + MDDI_INT_CRC_ERROR | \ + MDDI_INT_REV_PKT_RECEIVED) + + +#define MDDI_STAT_LINK_ACTIVE 0x0001 +#define MDDI_STAT_NEW_REV_PTR 0x0002 +#define MDDI_STAT_NEW_PRI_PTR 0x0004 +#define MDDI_STAT_NEW_SEC_PTR 0x0008 +#define MDDI_STAT_IN_HIBERNATION 0x0010 +#define MDDI_STAT_PRI_LINK_LIST_DONE 0x0020 +#define MDDI_STAT_SEC_LINK_LIST_DONE 0x0040 +#define MDDI_STAT_PENDING_TIMING_PKT 0x0080 +#define MDDI_STAT_PENDING_REV_ENCAP 0x0100 +#define MDDI_STAT_PENDING_POWERDOWN 0x0200 +#define MDDI_STAT_RTD_MEAS_FAIL 0x0800 +#define MDDI_STAT_CLIENT_WAKEUP_REQ 0x1000 + + +#define MDDI_CMD_POWERDOWN 0x0100 +#define MDDI_CMD_POWERUP 0x0200 +#define MDDI_CMD_HIBERNATE 0x0300 +#define MDDI_CMD_RESET 0x0400 +#define MDDI_CMD_DISP_IGNORE 0x0501 +#define MDDI_CMD_DISP_LISTEN 0x0500 +#define MDDI_CMD_SEND_REV_ENCAP 0x0600 +#define MDDI_CMD_GET_CLIENT_CAP 0x0601 +#define MDDI_CMD_GET_CLIENT_STATUS 0x0602 +#define MDDI_CMD_SEND_RTD 0x0700 +#define MDDI_CMD_LINK_ACTIVE 0x0900 +#define MDDI_CMD_PERIODIC_REV_ENCAP 0x0A00 +#define MDDI_CMD_FORCE_NEW_REV_PTR 0x0C00 + + + +#define MDDI_VIDEO_REV_PKT_SIZE 0x40 +#define MDDI_CLIENT_CAPABILITY_REV_PKT_SIZE 0x60 +#define MDDI_MAX_REV_PKT_SIZE 0x60 + +/* #define MDDI_REV_BUFFER_SIZE 128 */ +#define MDDI_REV_BUFFER_SIZE (MDDI_MAX_REV_PKT_SIZE * 4) + +/* MDP sends 256 pixel packets, so lower value hibernates more without + * significantly increasing latency of waiting for next subframe */ +#define MDDI_HOST_BYTES_PER_SUBFRAME 0x3C00 +#define MDDI_HOST_TA2_LEN 0x000c +#define MDDI_HOST_REV_RATE_DIV 0x0002 + + +struct __attribute__((packed)) mddi_rev_packet { + uint16_t length; + uint16_t type; + uint16_t client_id; +}; + +struct __attribute__((packed)) mddi_client_status { + uint16_t length; + uint16_t type; + uint16_t client_id; + uint16_t reverse_link_request; /* bytes needed in rev encap message */ + uint8_t crc_error_count; + uint8_t capability_change; + uint16_t graphics_busy_flags; + uint16_t crc16; +}; + +struct __attribute__((packed)) mddi_client_caps { + uint16_t length; /* length, exclusive of this field */ + uint16_t type; /* 66 */ + uint16_t client_id; + + uint16_t Protocol_Version; + uint16_t Minimum_Protocol_Version; + uint16_t Data_Rate_Capability; + uint8_t Interface_Type_Capability; + uint8_t Number_of_Alt_Displays; + uint16_t PostCal_Data_Rate; + uint16_t Bitmap_Width; + uint16_t Bitmap_Height; + uint16_t Display_Window_Width; + uint16_t Display_Window_Height; + uint32_t Color_Map_Size; + uint16_t Color_Map_RGB_Width; + uint16_t RGB_Capability; + uint8_t Monochrome_Capability; + uint8_t Reserved_1; + uint16_t Y_Cb_Cr_Capability; + uint16_t Bayer_Capability; + uint16_t Alpha_Cursor_Image_Planes; + uint32_t Client_Feature_Capability_Indicators; + uint8_t Maximum_Video_Frame_Rate_Capability; + uint8_t Minimum_Video_Frame_Rate_Capability; + uint16_t Minimum_Sub_frame_Rate; + uint16_t Audio_Buffer_Depth; + uint16_t Audio_Channel_Capability; + uint16_t Audio_Sample_Rate_Capability; + uint8_t Audio_Sample_Resolution; + uint8_t Mic_Audio_Sample_Resolution; + uint16_t Mic_Sample_Rate_Capability; + uint8_t Keyboard_Data_Format; + uint8_t pointing_device_data_format; + uint16_t content_protection_type; + uint16_t Mfr_Name; + uint16_t Product_Code; + uint16_t Reserved_3; + uint32_t Serial_Number; + uint8_t Week_of_Manufacture; + uint8_t Year_of_Manufacture; + + uint16_t crc16; +} mddi_client_capability_type; + + +struct __attribute__((packed)) mddi_video_stream { + uint16_t length; + uint16_t type; /* 16 */ + uint16_t client_id; /* 0 */ + + uint16_t video_data_format_descriptor; +/* format of each pixel in the Pixel Data in the present stream in the + * present packet. + * If bits [15:13] = 000 monochrome + * If bits [15:13] = 001 color pixels (palette). + * If bits [15:13] = 010 color pixels in raw RGB + * If bits [15:13] = 011 data in 4:2:2 Y Cb Cr format + * If bits [15:13] = 100 Bayer pixels + */ + + uint16_t pixel_data_attributes; +/* interpreted as follows: + * Bits [1:0] = 11 pixel data is displayed to both eyes + * Bits [1:0] = 10 pixel data is routed to the left eye only. + * Bits [1:0] = 01 pixel data is routed to the right eye only. + * Bits [1:0] = 00 pixel data is routed to the alternate display. + * Bit 2 is 0 Pixel Data is in the standard progressive format. + * Bit 2 is 1 Pixel Data is in interlace format. + * Bit 3 is 0 Pixel Data is in the standard progressive format. + * Bit 3 is 1 Pixel Data is in alternate pixel format. + * Bit 4 is 0 Pixel Data is to or from the display frame buffer. + * Bit 4 is 1 Pixel Data is to or from the camera. + * Bit 5 is 0 pixel data contains the next consecutive row of pixels. + * Bit 5 is 1 X Left Edge, Y Top Edge, X Right Edge, Y Bottom Edge, + * X Start, and Y Start parameters are not defined and + * shall be ignored by the client. + * Bits [7:6] = 01 Pixel data is written to the offline image buffer. + * Bits [7:6] = 00 Pixel data is written to the buffer to refresh display. + * Bits [7:6] = 11 Pixel data is written to all image buffers. + * Bits [7:6] = 10 Invalid. Reserved for future use. + * Bits 8 through 11 alternate display number. + * Bits 12 through 14 are reserved for future use and shall be set to zero. + * Bit 15 is 1 the row of pixels is the last row of pixels in a frame. + */ + + uint16_t x_left_edge; + uint16_t y_top_edge; + /* X,Y coordinate of the top left edge of the screen window */ + + uint16_t x_right_edge; + uint16_t y_bottom_edge; + /* X,Y coordinate of the bottom right edge of the window being + * updated. */ + + uint16_t x_start; + uint16_t y_start; + /* (X Start, Y Start) is the first pixel in the Pixel Data field + * below. */ + + uint16_t pixel_count; + /* number of pixels in the Pixel Data field below. */ + + uint16_t parameter_CRC; + /* 16-bit CRC of all bytes from the Packet Length to the Pixel Count. */ + + uint16_t reserved; + /* 16-bit variable to make structure align on 4 byte boundary */ +}; + +#define TYPE_VIDEO_STREAM 16 +#define TYPE_CLIENT_CAPS 66 +#define TYPE_REGISTER_ACCESS 146 +#define TYPE_CLIENT_STATUS 70 + +struct __attribute__((packed)) mddi_register_access { + uint16_t length; + uint16_t type; /* 146 */ + uint16_t client_id; + + uint16_t read_write_info; + /* Bits 13:0 a 14-bit unsigned integer that specifies the number of + * 32-bit Register Data List items to be transferred in the + * Register Data List field. + * Bits[15:14] = 00 Write to register(s); + * Bits[15:14] = 10 Read from register(s); + * Bits[15:14] = 11 Response to a Read. + * Bits[15:14] = 01 this value is reserved for future use. */ +#define MDDI_WRITE (0 << 14) +#define MDDI_READ (2 << 14) +#define MDDI_READ_RESP (3 << 14) + + uint32_t register_address; + /* the register address that is to be written to or read from. */ + + uint16_t crc16; + + uint32_t register_data_list; + /* list of 4-byte register data values for/from client registers */ +}; + +struct __attribute__((packed)) mddi_llentry { + uint16_t flags; + uint16_t header_count; + uint16_t data_count; + dma_addr_t data; /* 32 bit */ + struct mddi_llentry *next; + uint16_t reserved; + union { + struct mddi_video_stream v; + struct mddi_register_access r; + uint32_t _[12]; + } u; +}; + +#endif diff --git a/drivers/video/msm/mdp.c b/drivers/video/msm/mdp.c new file mode 100644 index 000000000000..6c519e2fa2b7 --- /dev/null +++ b/drivers/video/msm/mdp.c @@ -0,0 +1,520 @@ +/* drivers/video/msm_fb/mdp.c + * + * MSM MDP Interface (used by framebuffer core) + * + * Copyright (C) 2007 QUALCOMM Incorporated + * Copyright (C) 2007 Google Incorporated + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/kernel.h> +#include <linux/fb.h> +#include <linux/msm_mdp.h> +#include <linux/interrupt.h> +#include <linux/wait.h> +#include <linux/clk.h> +#include <linux/file.h> +#include <linux/major.h> + +#include <mach/msm_iomap.h> +#include <mach/msm_fb.h> +#include <linux/platform_device.h> + +#include "mdp_hw.h" + +struct class *mdp_class; + +#define MDP_CMD_DEBUG_ACCESS_BASE (0x10000) + +static uint16_t mdp_default_ccs[] = { + 0x254, 0x000, 0x331, 0x254, 0xF38, 0xE61, 0x254, 0x409, 0x000, + 0x010, 0x080, 0x080 +}; + +static DECLARE_WAIT_QUEUE_HEAD(mdp_dma2_waitqueue); +static DECLARE_WAIT_QUEUE_HEAD(mdp_ppp_waitqueue); +static struct msmfb_callback *dma_callback; +static struct clk *clk; +static unsigned int mdp_irq_mask; +static DEFINE_SPINLOCK(mdp_lock); +DEFINE_MUTEX(mdp_mutex); + +static int enable_mdp_irq(struct mdp_info *mdp, uint32_t mask) +{ + unsigned long irq_flags; + int ret = 0; + + BUG_ON(!mask); + + spin_lock_irqsave(&mdp_lock, irq_flags); + /* if the mask bits are already set return an error, this interrupt + * is already enabled */ + if (mdp_irq_mask & mask) { + printk(KERN_ERR "mdp irq already on already on %x %x\n", + mdp_irq_mask, mask); + ret = -1; + } + /* if the mdp irq is not already enabled enable it */ + if (!mdp_irq_mask) { + if (clk) + clk_enable(clk); + enable_irq(mdp->irq); + } + + /* update the irq mask to reflect the fact that the interrupt is + * enabled */ + mdp_irq_mask |= mask; + spin_unlock_irqrestore(&mdp_lock, irq_flags); + return ret; +} + +static int locked_disable_mdp_irq(struct mdp_info *mdp, uint32_t mask) +{ + /* this interrupt is already disabled! */ + if (!(mdp_irq_mask & mask)) { + printk(KERN_ERR "mdp irq already off %x %x\n", + mdp_irq_mask, mask); + return -1; + } + /* update the irq mask to reflect the fact that the interrupt is + * disabled */ + mdp_irq_mask &= ~(mask); + /* if no one is waiting on the interrupt, disable it */ + if (!mdp_irq_mask) { + disable_irq(mdp->irq); + if (clk) + clk_disable(clk); + } + return 0; +} + +static int disable_mdp_irq(struct mdp_info *mdp, uint32_t mask) +{ + unsigned long irq_flags; + int ret; + + spin_lock_irqsave(&mdp_lock, irq_flags); + ret = locked_disable_mdp_irq(mdp, mask); + spin_unlock_irqrestore(&mdp_lock, irq_flags); + return ret; +} + +static irqreturn_t mdp_isr(int irq, void *data) +{ + uint32_t status; + unsigned long irq_flags; + struct mdp_info *mdp = data; + + spin_lock_irqsave(&mdp_lock, irq_flags); + + status = mdp_readl(mdp, MDP_INTR_STATUS); + mdp_writel(mdp, status, MDP_INTR_CLEAR); + + status &= mdp_irq_mask; + if (status & DL0_DMA2_TERM_DONE) { + if (dma_callback) { + dma_callback->func(dma_callback); + dma_callback = NULL; + } + wake_up(&mdp_dma2_waitqueue); + } + + if (status & DL0_ROI_DONE) + wake_up(&mdp_ppp_waitqueue); + + if (status) + locked_disable_mdp_irq(mdp, status); + + spin_unlock_irqrestore(&mdp_lock, irq_flags); + return IRQ_HANDLED; +} + +static uint32_t mdp_check_mask(uint32_t mask) +{ + uint32_t ret; + unsigned long irq_flags; + + spin_lock_irqsave(&mdp_lock, irq_flags); + ret = mdp_irq_mask & mask; + spin_unlock_irqrestore(&mdp_lock, irq_flags); + return ret; +} + +static int mdp_wait(struct mdp_info *mdp, uint32_t mask, wait_queue_head_t *wq) +{ + int ret = 0; + unsigned long irq_flags; + + wait_event_timeout(*wq, !mdp_check_mask(mask), HZ); + + spin_lock_irqsave(&mdp_lock, irq_flags); + if (mdp_irq_mask & mask) { + locked_disable_mdp_irq(mdp, mask); + printk(KERN_WARNING "timeout waiting for mdp to complete %x\n", + mask); + ret = -ETIMEDOUT; + } + spin_unlock_irqrestore(&mdp_lock, irq_flags); + + return ret; +} + +void mdp_dma_wait(struct mdp_device *mdp_dev) +{ +#define MDP_MAX_TIMEOUTS 20 + static int timeout_count; + struct mdp_info *mdp = container_of(mdp_dev, struct mdp_info, mdp_dev); + + if (mdp_wait(mdp, DL0_DMA2_TERM_DONE, &mdp_dma2_waitqueue) == -ETIMEDOUT) + timeout_count++; + else + timeout_count = 0; + + if (timeout_count > MDP_MAX_TIMEOUTS) { + printk(KERN_ERR "mdp: dma failed %d times, somethings wrong!\n", + MDP_MAX_TIMEOUTS); + BUG(); + } +} + +static int mdp_ppp_wait(struct mdp_info *mdp) +{ + return mdp_wait(mdp, DL0_ROI_DONE, &mdp_ppp_waitqueue); +} + +void mdp_dma_to_mddi(struct mdp_info *mdp, uint32_t addr, uint32_t stride, + uint32_t width, uint32_t height, uint32_t x, uint32_t y, + struct msmfb_callback *callback) +{ + uint32_t dma2_cfg; + uint16_t ld_param = 0; /* 0=PRIM, 1=SECD, 2=EXT */ + + if (enable_mdp_irq(mdp, DL0_DMA2_TERM_DONE)) { + printk(KERN_ERR "mdp_dma_to_mddi: busy\n"); + return; + } + + dma_callback = callback; + + dma2_cfg = DMA_PACK_TIGHT | + DMA_PACK_ALIGN_LSB | + DMA_PACK_PATTERN_RGB | + DMA_OUT_SEL_AHB | + DMA_IBUF_NONCONTIGUOUS; + + dma2_cfg |= DMA_IBUF_FORMAT_RGB565; + + dma2_cfg |= DMA_OUT_SEL_MDDI; + + dma2_cfg |= DMA_MDDI_DMAOUT_LCD_SEL_PRIMARY; + + dma2_cfg |= DMA_DITHER_EN; + + /* setup size, address, and stride */ + mdp_writel(mdp, (height << 16) | (width), + MDP_CMD_DEBUG_ACCESS_BASE + 0x0184); + mdp_writel(mdp, addr, MDP_CMD_DEBUG_ACCESS_BASE + 0x0188); + mdp_writel(mdp, stride, MDP_CMD_DEBUG_ACCESS_BASE + 0x018C); + + /* 666 18BPP */ + dma2_cfg |= DMA_DSTC0G_6BITS | DMA_DSTC1B_6BITS | DMA_DSTC2R_6BITS; + + /* set y & x offset and MDDI transaction parameters */ + mdp_writel(mdp, (y << 16) | (x), MDP_CMD_DEBUG_ACCESS_BASE + 0x0194); + mdp_writel(mdp, ld_param, MDP_CMD_DEBUG_ACCESS_BASE + 0x01a0); + mdp_writel(mdp, (MDDI_VDO_PACKET_DESC << 16) | MDDI_VDO_PACKET_PRIM, + MDP_CMD_DEBUG_ACCESS_BASE + 0x01a4); + + mdp_writel(mdp, dma2_cfg, MDP_CMD_DEBUG_ACCESS_BASE + 0x0180); + + /* start DMA2 */ + mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x0044); +} + +void mdp_dma(struct mdp_device *mdp_dev, uint32_t addr, uint32_t stride, + uint32_t width, uint32_t height, uint32_t x, uint32_t y, + struct msmfb_callback *callback, int interface) +{ + struct mdp_info *mdp = container_of(mdp_dev, struct mdp_info, mdp_dev); + + if (interface == MSM_MDDI_PMDH_INTERFACE) { + mdp_dma_to_mddi(mdp, addr, stride, width, height, x, y, + callback); + } +} + +int get_img(struct mdp_img *img, struct fb_info *info, + unsigned long *start, unsigned long *len, + struct file **filep) +{ + int put_needed, ret = 0; + struct file *file; + unsigned long vstart; + + file = fget_light(img->memory_id, &put_needed); + if (file == NULL) + return -1; + + if (MAJOR(file->f_dentry->d_inode->i_rdev) == FB_MAJOR) { + *start = info->fix.smem_start; + *len = info->fix.smem_len; + } else + ret = -1; + fput_light(file, put_needed); + + return ret; +} + +void put_img(struct file *src_file, struct file *dst_file) +{ +} + +int mdp_blit(struct mdp_device *mdp_dev, struct fb_info *fb, + struct mdp_blit_req *req) +{ + int ret; + unsigned long src_start = 0, src_len = 0, dst_start = 0, dst_len = 0; + struct mdp_info *mdp = container_of(mdp_dev, struct mdp_info, mdp_dev); + struct file *src_file = 0, *dst_file = 0; + + /* WORKAROUND FOR HARDWARE BUG IN BG TILE FETCH */ + if (unlikely(req->src_rect.h == 0 || + req->src_rect.w == 0)) { + printk(KERN_ERR "mpd_ppp: src img of zero size!\n"); + return -EINVAL; + } + if (unlikely(req->dst_rect.h == 0 || + req->dst_rect.w == 0)) + return -EINVAL; + + /* do this first so that if this fails, the caller can always + * safely call put_img */ + if (unlikely(get_img(&req->src, fb, &src_start, &src_len, &src_file))) { + printk(KERN_ERR "mpd_ppp: could not retrieve src image from " + "memory\n"); + return -EINVAL; + } + + if (unlikely(get_img(&req->dst, fb, &dst_start, &dst_len, &dst_file))) { + printk(KERN_ERR "mpd_ppp: could not retrieve dst image from " + "memory\n"); + return -EINVAL; + } + mutex_lock(&mdp_mutex); + + /* transp_masking unimplemented */ + req->transp_mask = MDP_TRANSP_NOP; + if (unlikely((req->transp_mask != MDP_TRANSP_NOP || + req->alpha != MDP_ALPHA_NOP || + HAS_ALPHA(req->src.format)) && + (req->flags & MDP_ROT_90 && + req->dst_rect.w <= 16 && req->dst_rect.h >= 16))) { + int i; + unsigned int tiles = req->dst_rect.h / 16; + unsigned int remainder = req->dst_rect.h % 16; + req->src_rect.w = 16*req->src_rect.w / req->dst_rect.h; + req->dst_rect.h = 16; + for (i = 0; i < tiles; i++) { + enable_mdp_irq(mdp, DL0_ROI_DONE); + ret = mdp_ppp_blit(mdp, req, src_file, src_start, + src_len, dst_file, dst_start, + dst_len); + if (ret) + goto err_bad_blit; + ret = mdp_ppp_wait(mdp); + if (ret) + goto err_wait_failed; + req->dst_rect.y += 16; + req->src_rect.x += req->src_rect.w; + } + if (!remainder) + goto end; + req->src_rect.w = remainder*req->src_rect.w / req->dst_rect.h; + req->dst_rect.h = remainder; + } + enable_mdp_irq(mdp, DL0_ROI_DONE); + ret = mdp_ppp_blit(mdp, req, src_file, src_start, src_len, dst_file, + dst_start, + dst_len); + if (ret) + goto err_bad_blit; + ret = mdp_ppp_wait(mdp); + if (ret) + goto err_wait_failed; +end: + put_img(src_file, dst_file); + mutex_unlock(&mdp_mutex); + return 0; +err_bad_blit: + disable_mdp_irq(mdp, DL0_ROI_DONE); +err_wait_failed: + put_img(src_file, dst_file); + mutex_unlock(&mdp_mutex); + return ret; +} + +void mdp_set_grp_disp(struct mdp_device *mdp_dev, unsigned disp_id) +{ + struct mdp_info *mdp = container_of(mdp_dev, struct mdp_info, mdp_dev); + + disp_id &= 0xf; + mdp_writel(mdp, disp_id, MDP_FULL_BYPASS_WORD43); +} + +int register_mdp_client(struct class_interface *cint) +{ + if (!mdp_class) { + pr_err("mdp: no mdp_class when registering mdp client\n"); + return -ENODEV; + } + cint->class = mdp_class; + return class_interface_register(cint); +} + +#include "mdp_csc_table.h" +#include "mdp_scale_tables.h" + +int mdp_probe(struct platform_device *pdev) +{ + struct resource *resource; + int ret; + int n; + struct mdp_info *mdp; + + resource = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!resource) { + pr_err("mdp: can not get mdp mem resource!\n"); + return -ENOMEM; + } + + mdp = kzalloc(sizeof(struct mdp_info), GFP_KERNEL); + if (!mdp) + return -ENOMEM; + + mdp->irq = platform_get_irq(pdev, 0); + if (mdp->irq < 0) { + pr_err("mdp: can not get mdp irq\n"); + ret = mdp->irq; + goto error_get_irq; + } + + mdp->base = ioremap(resource->start, + resource->end - resource->start); + if (mdp->base == 0) { + printk(KERN_ERR "msmfb: cannot allocate mdp regs!\n"); + ret = -ENOMEM; + goto error_ioremap; + } + + mdp->mdp_dev.dma = mdp_dma; + mdp->mdp_dev.dma_wait = mdp_dma_wait; + mdp->mdp_dev.blit = mdp_blit; + mdp->mdp_dev.set_grp_disp = mdp_set_grp_disp; + + clk = clk_get(&pdev->dev, "mdp_clk"); + if (IS_ERR(clk)) { + printk(KERN_INFO "mdp: failed to get mdp clk"); + return PTR_ERR(clk); + } + + ret = request_irq(mdp->irq, mdp_isr, IRQF_DISABLED, "msm_mdp", mdp); + if (ret) + goto error_request_irq; + disable_irq(mdp->irq); + mdp_irq_mask = 0; + + /* debug interface write access */ + mdp_writel(mdp, 1, 0x60); + + mdp_writel(mdp, MDP_ANY_INTR_MASK, MDP_INTR_ENABLE); + mdp_writel(mdp, 1, MDP_EBI2_PORTMAP_MODE); + + mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x01f8); + mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x01fc); + + for (n = 0; n < ARRAY_SIZE(csc_table); n++) + mdp_writel(mdp, csc_table[n].val, csc_table[n].reg); + + /* clear up unused fg/main registers */ + /* comp.plane 2&3 ystride */ + mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x0120); + + /* unpacked pattern */ + mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x012c); + mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x0130); + mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x0134); + mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x0158); + mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x015c); + mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x0160); + mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x0170); + mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x0174); + mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x017c); + + /* comp.plane 2 & 3 */ + mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x0114); + mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x0118); + + /* clear unused bg registers */ + mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x01c8); + mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x01d0); + mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x01dc); + mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x01e0); + mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x01e4); + + for (n = 0; n < ARRAY_SIZE(mdp_upscale_table); n++) + mdp_writel(mdp, mdp_upscale_table[n].val, + mdp_upscale_table[n].reg); + + for (n = 0; n < 9; n++) + mdp_writel(mdp, mdp_default_ccs[n], 0x40440 + 4 * n); + mdp_writel(mdp, mdp_default_ccs[9], 0x40500 + 4 * 0); + mdp_writel(mdp, mdp_default_ccs[10], 0x40500 + 4 * 0); + mdp_writel(mdp, mdp_default_ccs[11], 0x40500 + 4 * 0); + + /* register mdp device */ + mdp->mdp_dev.dev.parent = &pdev->dev; + mdp->mdp_dev.dev.class = mdp_class; + + /* if you can remove the platform device you'd have to implement + * this: + mdp_dev.release = mdp_class; */ + + ret = device_register(&mdp->mdp_dev.dev); + if (ret) + goto error_device_register; + return 0; + +error_device_register: + free_irq(mdp->irq, mdp); +error_request_irq: + iounmap(mdp->base); +error_get_irq: +error_ioremap: + kfree(mdp); + return ret; +} + +static struct platform_driver msm_mdp_driver = { + .probe = mdp_probe, + .driver = {.name = "msm_mdp"}, +}; + +static int __init mdp_init(void) +{ + mdp_class = class_create(THIS_MODULE, "msm_mdp"); + if (IS_ERR(mdp_class)) { + printk(KERN_ERR "Error creating mdp class\n"); + return PTR_ERR(mdp_class); + } + return platform_driver_register(&msm_mdp_driver); +} + +subsys_initcall(mdp_init); diff --git a/drivers/video/msm/mdp_csc_table.h b/drivers/video/msm/mdp_csc_table.h new file mode 100644 index 000000000000..d1cde30ead52 --- /dev/null +++ b/drivers/video/msm/mdp_csc_table.h @@ -0,0 +1,582 @@ +/* drivers/video/msm_fb/mdp_csc_table.h + * + * Copyright (C) 2007 QUALCOMM Incorporated + * Copyright (C) 2007 Google Incorporated + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +static struct { + uint32_t reg; + uint32_t val; +} csc_table[] = { + { 0x40400, 0x83 }, + { 0x40404, 0x102 }, + { 0x40408, 0x32 }, + { 0x4040c, 0xffffffb5 }, + { 0x40410, 0xffffff6c }, + { 0x40414, 0xe1 }, + { 0x40418, 0xe1 }, + { 0x4041c, 0xffffff45 }, + { 0x40420, 0xffffffdc }, + { 0x40440, 0x254 }, + { 0x40444, 0x0 }, + { 0x40448, 0x331 }, + { 0x4044c, 0x254 }, + { 0x40450, 0xffffff38 }, + { 0x40454, 0xfffffe61 }, + { 0x40458, 0x254 }, + { 0x4045c, 0x409 }, + { 0x40460, 0x0 }, + { 0x40480, 0x5d }, + { 0x40484, 0x13a }, + { 0x40488, 0x20 }, + { 0x4048c, 0xffffffcd }, + { 0x40490, 0xffffff54 }, + { 0x40494, 0xe1 }, + { 0x40498, 0xe1 }, + { 0x4049c, 0xffffff35 }, + { 0x404a0, 0xffffffec }, + { 0x404c0, 0x254 }, + { 0x404c4, 0x0 }, + { 0x404c8, 0x396 }, + { 0x404cc, 0x254 }, + { 0x404d0, 0xffffff94 }, + { 0x404d4, 0xfffffef0 }, + { 0x404d8, 0x254 }, + { 0x404dc, 0x43a }, + { 0x404e0, 0x0 }, + { 0x40500, 0x10 }, + { 0x40504, 0x80 }, + { 0x40508, 0x80 }, + { 0x40540, 0x10 }, + { 0x40544, 0x80 }, + { 0x40548, 0x80 }, + { 0x40580, 0x10 }, + { 0x40584, 0xeb }, + { 0x40588, 0x10 }, + { 0x4058c, 0xf0 }, + { 0x405c0, 0x10 }, + { 0x405c4, 0xeb }, + { 0x405c8, 0x10 }, + { 0x405cc, 0xf0 }, + { 0x40800, 0x0 }, + { 0x40804, 0x151515 }, + { 0x40808, 0x1d1d1d }, + { 0x4080c, 0x232323 }, + { 0x40810, 0x272727 }, + { 0x40814, 0x2b2b2b }, + { 0x40818, 0x2f2f2f }, + { 0x4081c, 0x333333 }, + { 0x40820, 0x363636 }, + { 0x40824, 0x393939 }, + { 0x40828, 0x3b3b3b }, + { 0x4082c, 0x3e3e3e }, + { 0x40830, 0x404040 }, + { 0x40834, 0x434343 }, + { 0x40838, 0x454545 }, + { 0x4083c, 0x474747 }, + { 0x40840, 0x494949 }, + { 0x40844, 0x4b4b4b }, + { 0x40848, 0x4d4d4d }, + { 0x4084c, 0x4f4f4f }, + { 0x40850, 0x515151 }, + { 0x40854, 0x535353 }, + { 0x40858, 0x555555 }, + { 0x4085c, 0x565656 }, + { 0x40860, 0x585858 }, + { 0x40864, 0x5a5a5a }, + { 0x40868, 0x5b5b5b }, + { 0x4086c, 0x5d5d5d }, + { 0x40870, 0x5e5e5e }, + { 0x40874, 0x606060 }, + { 0x40878, 0x616161 }, + { 0x4087c, 0x636363 }, + { 0x40880, 0x646464 }, + { 0x40884, 0x666666 }, + { 0x40888, 0x676767 }, + { 0x4088c, 0x686868 }, + { 0x40890, 0x6a6a6a }, + { 0x40894, 0x6b6b6b }, + { 0x40898, 0x6c6c6c }, + { 0x4089c, 0x6e6e6e }, + { 0x408a0, 0x6f6f6f }, + { 0x408a4, 0x707070 }, + { 0x408a8, 0x717171 }, + { 0x408ac, 0x727272 }, + { 0x408b0, 0x747474 }, + { 0x408b4, 0x757575 }, + { 0x408b8, 0x767676 }, + { 0x408bc, 0x777777 }, + { 0x408c0, 0x787878 }, + { 0x408c4, 0x797979 }, + { 0x408c8, 0x7a7a7a }, + { 0x408cc, 0x7c7c7c }, + { 0x408d0, 0x7d7d7d }, + { 0x408d4, 0x7e7e7e }, + { 0x408d8, 0x7f7f7f }, + { 0x408dc, 0x808080 }, + { 0x408e0, 0x818181 }, + { 0x408e4, 0x828282 }, + { 0x408e8, 0x838383 }, + { 0x408ec, 0x848484 }, + { 0x408f0, 0x858585 }, + { 0x408f4, 0x868686 }, + { 0x408f8, 0x878787 }, + { 0x408fc, 0x888888 }, + { 0x40900, 0x898989 }, + { 0x40904, 0x8a8a8a }, + { 0x40908, 0x8b8b8b }, + { 0x4090c, 0x8c8c8c }, + { 0x40910, 0x8d8d8d }, + { 0x40914, 0x8e8e8e }, + { 0x40918, 0x8f8f8f }, + { 0x4091c, 0x8f8f8f }, + { 0x40920, 0x909090 }, + { 0x40924, 0x919191 }, + { 0x40928, 0x929292 }, + { 0x4092c, 0x939393 }, + { 0x40930, 0x949494 }, + { 0x40934, 0x959595 }, + { 0x40938, 0x969696 }, + { 0x4093c, 0x969696 }, + { 0x40940, 0x979797 }, + { 0x40944, 0x989898 }, + { 0x40948, 0x999999 }, + { 0x4094c, 0x9a9a9a }, + { 0x40950, 0x9b9b9b }, + { 0x40954, 0x9c9c9c }, + { 0x40958, 0x9c9c9c }, + { 0x4095c, 0x9d9d9d }, + { 0x40960, 0x9e9e9e }, + { 0x40964, 0x9f9f9f }, + { 0x40968, 0xa0a0a0 }, + { 0x4096c, 0xa0a0a0 }, + { 0x40970, 0xa1a1a1 }, + { 0x40974, 0xa2a2a2 }, + { 0x40978, 0xa3a3a3 }, + { 0x4097c, 0xa4a4a4 }, + { 0x40980, 0xa4a4a4 }, + { 0x40984, 0xa5a5a5 }, + { 0x40988, 0xa6a6a6 }, + { 0x4098c, 0xa7a7a7 }, + { 0x40990, 0xa7a7a7 }, + { 0x40994, 0xa8a8a8 }, + { 0x40998, 0xa9a9a9 }, + { 0x4099c, 0xaaaaaa }, + { 0x409a0, 0xaaaaaa }, + { 0x409a4, 0xababab }, + { 0x409a8, 0xacacac }, + { 0x409ac, 0xadadad }, + { 0x409b0, 0xadadad }, + { 0x409b4, 0xaeaeae }, + { 0x409b8, 0xafafaf }, + { 0x409bc, 0xafafaf }, + { 0x409c0, 0xb0b0b0 }, + { 0x409c4, 0xb1b1b1 }, + { 0x409c8, 0xb2b2b2 }, + { 0x409cc, 0xb2b2b2 }, + { 0x409d0, 0xb3b3b3 }, + { 0x409d4, 0xb4b4b4 }, + { 0x409d8, 0xb4b4b4 }, + { 0x409dc, 0xb5b5b5 }, + { 0x409e0, 0xb6b6b6 }, + { 0x409e4, 0xb6b6b6 }, + { 0x409e8, 0xb7b7b7 }, + { 0x409ec, 0xb8b8b8 }, + { 0x409f0, 0xb8b8b8 }, + { 0x409f4, 0xb9b9b9 }, + { 0x409f8, 0xbababa }, + { 0x409fc, 0xbababa }, + { 0x40a00, 0xbbbbbb }, + { 0x40a04, 0xbcbcbc }, + { 0x40a08, 0xbcbcbc }, + { 0x40a0c, 0xbdbdbd }, + { 0x40a10, 0xbebebe }, + { 0x40a14, 0xbebebe }, + { 0x40a18, 0xbfbfbf }, + { 0x40a1c, 0xc0c0c0 }, + { 0x40a20, 0xc0c0c0 }, + { 0x40a24, 0xc1c1c1 }, + { 0x40a28, 0xc1c1c1 }, + { 0x40a2c, 0xc2c2c2 }, + { 0x40a30, 0xc3c3c3 }, + { 0x40a34, 0xc3c3c3 }, + { 0x40a38, 0xc4c4c4 }, + { 0x40a3c, 0xc5c5c5 }, + { 0x40a40, 0xc5c5c5 }, + { 0x40a44, 0xc6c6c6 }, + { 0x40a48, 0xc6c6c6 }, + { 0x40a4c, 0xc7c7c7 }, + { 0x40a50, 0xc8c8c8 }, + { 0x40a54, 0xc8c8c8 }, + { 0x40a58, 0xc9c9c9 }, + { 0x40a5c, 0xc9c9c9 }, + { 0x40a60, 0xcacaca }, + { 0x40a64, 0xcbcbcb }, + { 0x40a68, 0xcbcbcb }, + { 0x40a6c, 0xcccccc }, + { 0x40a70, 0xcccccc }, + { 0x40a74, 0xcdcdcd }, + { 0x40a78, 0xcecece }, + { 0x40a7c, 0xcecece }, + { 0x40a80, 0xcfcfcf }, + { 0x40a84, 0xcfcfcf }, + { 0x40a88, 0xd0d0d0 }, + { 0x40a8c, 0xd0d0d0 }, + { 0x40a90, 0xd1d1d1 }, + { 0x40a94, 0xd2d2d2 }, + { 0x40a98, 0xd2d2d2 }, + { 0x40a9c, 0xd3d3d3 }, + { 0x40aa0, 0xd3d3d3 }, + { 0x40aa4, 0xd4d4d4 }, + { 0x40aa8, 0xd4d4d4 }, + { 0x40aac, 0xd5d5d5 }, + { 0x40ab0, 0xd6d6d6 }, + { 0x40ab4, 0xd6d6d6 }, + { 0x40ab8, 0xd7d7d7 }, + { 0x40abc, 0xd7d7d7 }, + { 0x40ac0, 0xd8d8d8 }, + { 0x40ac4, 0xd8d8d8 }, + { 0x40ac8, 0xd9d9d9 }, + { 0x40acc, 0xd9d9d9 }, + { 0x40ad0, 0xdadada }, + { 0x40ad4, 0xdbdbdb }, + { 0x40ad8, 0xdbdbdb }, + { 0x40adc, 0xdcdcdc }, + { 0x40ae0, 0xdcdcdc }, + { 0x40ae4, 0xdddddd }, + { 0x40ae8, 0xdddddd }, + { 0x40aec, 0xdedede }, + { 0x40af0, 0xdedede }, + { 0x40af4, 0xdfdfdf }, + { 0x40af8, 0xdfdfdf }, + { 0x40afc, 0xe0e0e0 }, + { 0x40b00, 0xe0e0e0 }, + { 0x40b04, 0xe1e1e1 }, + { 0x40b08, 0xe1e1e1 }, + { 0x40b0c, 0xe2e2e2 }, + { 0x40b10, 0xe3e3e3 }, + { 0x40b14, 0xe3e3e3 }, + { 0x40b18, 0xe4e4e4 }, + { 0x40b1c, 0xe4e4e4 }, + { 0x40b20, 0xe5e5e5 }, + { 0x40b24, 0xe5e5e5 }, + { 0x40b28, 0xe6e6e6 }, + { 0x40b2c, 0xe6e6e6 }, + { 0x40b30, 0xe7e7e7 }, + { 0x40b34, 0xe7e7e7 }, + { 0x40b38, 0xe8e8e8 }, + { 0x40b3c, 0xe8e8e8 }, + { 0x40b40, 0xe9e9e9 }, + { 0x40b44, 0xe9e9e9 }, + { 0x40b48, 0xeaeaea }, + { 0x40b4c, 0xeaeaea }, + { 0x40b50, 0xebebeb }, + { 0x40b54, 0xebebeb }, + { 0x40b58, 0xececec }, + { 0x40b5c, 0xececec }, + { 0x40b60, 0xededed }, + { 0x40b64, 0xededed }, + { 0x40b68, 0xeeeeee }, + { 0x40b6c, 0xeeeeee }, + { 0x40b70, 0xefefef }, + { 0x40b74, 0xefefef }, + { 0x40b78, 0xf0f0f0 }, + { 0x40b7c, 0xf0f0f0 }, + { 0x40b80, 0xf1f1f1 }, + { 0x40b84, 0xf1f1f1 }, + { 0x40b88, 0xf2f2f2 }, + { 0x40b8c, 0xf2f2f2 }, + { 0x40b90, 0xf2f2f2 }, + { 0x40b94, 0xf3f3f3 }, + { 0x40b98, 0xf3f3f3 }, + { 0x40b9c, 0xf4f4f4 }, + { 0x40ba0, 0xf4f4f4 }, + { 0x40ba4, 0xf5f5f5 }, + { 0x40ba8, 0xf5f5f5 }, + { 0x40bac, 0xf6f6f6 }, + { 0x40bb0, 0xf6f6f6 }, + { 0x40bb4, 0xf7f7f7 }, + { 0x40bb8, 0xf7f7f7 }, + { 0x40bbc, 0xf8f8f8 }, + { 0x40bc0, 0xf8f8f8 }, + { 0x40bc4, 0xf9f9f9 }, + { 0x40bc8, 0xf9f9f9 }, + { 0x40bcc, 0xfafafa }, + { 0x40bd0, 0xfafafa }, + { 0x40bd4, 0xfafafa }, + { 0x40bd8, 0xfbfbfb }, + { 0x40bdc, 0xfbfbfb }, + { 0x40be0, 0xfcfcfc }, + { 0x40be4, 0xfcfcfc }, + { 0x40be8, 0xfdfdfd }, + { 0x40bec, 0xfdfdfd }, + { 0x40bf0, 0xfefefe }, + { 0x40bf4, 0xfefefe }, + { 0x40bf8, 0xffffff }, + { 0x40bfc, 0xffffff }, + { 0x40c00, 0x0 }, + { 0x40c04, 0x0 }, + { 0x40c08, 0x0 }, + { 0x40c0c, 0x0 }, + { 0x40c10, 0x0 }, + { 0x40c14, 0x0 }, + { 0x40c18, 0x0 }, + { 0x40c1c, 0x0 }, + { 0x40c20, 0x0 }, + { 0x40c24, 0x0 }, + { 0x40c28, 0x0 }, + { 0x40c2c, 0x0 }, + { 0x40c30, 0x0 }, + { 0x40c34, 0x0 }, + { 0x40c38, 0x0 }, + { 0x40c3c, 0x0 }, + { 0x40c40, 0x10101 }, + { 0x40c44, 0x10101 }, + { 0x40c48, 0x10101 }, + { 0x40c4c, 0x10101 }, + { 0x40c50, 0x10101 }, + { 0x40c54, 0x10101 }, + { 0x40c58, 0x10101 }, + { 0x40c5c, 0x10101 }, + { 0x40c60, 0x10101 }, + { 0x40c64, 0x10101 }, + { 0x40c68, 0x20202 }, + { 0x40c6c, 0x20202 }, + { 0x40c70, 0x20202 }, + { 0x40c74, 0x20202 }, + { 0x40c78, 0x20202 }, + { 0x40c7c, 0x20202 }, + { 0x40c80, 0x30303 }, + { 0x40c84, 0x30303 }, + { 0x40c88, 0x30303 }, + { 0x40c8c, 0x30303 }, + { 0x40c90, 0x30303 }, + { 0x40c94, 0x40404 }, + { 0x40c98, 0x40404 }, + { 0x40c9c, 0x40404 }, + { 0x40ca0, 0x40404 }, + { 0x40ca4, 0x40404 }, + { 0x40ca8, 0x50505 }, + { 0x40cac, 0x50505 }, + { 0x40cb0, 0x50505 }, + { 0x40cb4, 0x50505 }, + { 0x40cb8, 0x60606 }, + { 0x40cbc, 0x60606 }, + { 0x40cc0, 0x60606 }, + { 0x40cc4, 0x70707 }, + { 0x40cc8, 0x70707 }, + { 0x40ccc, 0x70707 }, + { 0x40cd0, 0x70707 }, + { 0x40cd4, 0x80808 }, + { 0x40cd8, 0x80808 }, + { 0x40cdc, 0x80808 }, + { 0x40ce0, 0x90909 }, + { 0x40ce4, 0x90909 }, + { 0x40ce8, 0xa0a0a }, + { 0x40cec, 0xa0a0a }, + { 0x40cf0, 0xa0a0a }, + { 0x40cf4, 0xb0b0b }, + { 0x40cf8, 0xb0b0b }, + { 0x40cfc, 0xb0b0b }, + { 0x40d00, 0xc0c0c }, + { 0x40d04, 0xc0c0c }, + { 0x40d08, 0xd0d0d }, + { 0x40d0c, 0xd0d0d }, + { 0x40d10, 0xe0e0e }, + { 0x40d14, 0xe0e0e }, + { 0x40d18, 0xe0e0e }, + { 0x40d1c, 0xf0f0f }, + { 0x40d20, 0xf0f0f }, + { 0x40d24, 0x101010 }, + { 0x40d28, 0x101010 }, + { 0x40d2c, 0x111111 }, + { 0x40d30, 0x111111 }, + { 0x40d34, 0x121212 }, + { 0x40d38, 0x121212 }, + { 0x40d3c, 0x131313 }, + { 0x40d40, 0x131313 }, + { 0x40d44, 0x141414 }, + { 0x40d48, 0x151515 }, + { 0x40d4c, 0x151515 }, + { 0x40d50, 0x161616 }, + { 0x40d54, 0x161616 }, + { 0x40d58, 0x171717 }, + { 0x40d5c, 0x171717 }, + { 0x40d60, 0x181818 }, + { 0x40d64, 0x191919 }, + { 0x40d68, 0x191919 }, + { 0x40d6c, 0x1a1a1a }, + { 0x40d70, 0x1b1b1b }, + { 0x40d74, 0x1b1b1b }, + { 0x40d78, 0x1c1c1c }, + { 0x40d7c, 0x1c1c1c }, + { 0x40d80, 0x1d1d1d }, + { 0x40d84, 0x1e1e1e }, + { 0x40d88, 0x1f1f1f }, + { 0x40d8c, 0x1f1f1f }, + { 0x40d90, 0x202020 }, + { 0x40d94, 0x212121 }, + { 0x40d98, 0x212121 }, + { 0x40d9c, 0x222222 }, + { 0x40da0, 0x232323 }, + { 0x40da4, 0x242424 }, + { 0x40da8, 0x242424 }, + { 0x40dac, 0x252525 }, + { 0x40db0, 0x262626 }, + { 0x40db4, 0x272727 }, + { 0x40db8, 0x272727 }, + { 0x40dbc, 0x282828 }, + { 0x40dc0, 0x292929 }, + { 0x40dc4, 0x2a2a2a }, + { 0x40dc8, 0x2b2b2b }, + { 0x40dcc, 0x2c2c2c }, + { 0x40dd0, 0x2c2c2c }, + { 0x40dd4, 0x2d2d2d }, + { 0x40dd8, 0x2e2e2e }, + { 0x40ddc, 0x2f2f2f }, + { 0x40de0, 0x303030 }, + { 0x40de4, 0x313131 }, + { 0x40de8, 0x323232 }, + { 0x40dec, 0x333333 }, + { 0x40df0, 0x333333 }, + { 0x40df4, 0x343434 }, + { 0x40df8, 0x353535 }, + { 0x40dfc, 0x363636 }, + { 0x40e00, 0x373737 }, + { 0x40e04, 0x383838 }, + { 0x40e08, 0x393939 }, + { 0x40e0c, 0x3a3a3a }, + { 0x40e10, 0x3b3b3b }, + { 0x40e14, 0x3c3c3c }, + { 0x40e18, 0x3d3d3d }, + { 0x40e1c, 0x3e3e3e }, + { 0x40e20, 0x3f3f3f }, + { 0x40e24, 0x404040 }, + { 0x40e28, 0x414141 }, + { 0x40e2c, 0x424242 }, + { 0x40e30, 0x434343 }, + { 0x40e34, 0x444444 }, + { 0x40e38, 0x464646 }, + { 0x40e3c, 0x474747 }, + { 0x40e40, 0x484848 }, + { 0x40e44, 0x494949 }, + { 0x40e48, 0x4a4a4a }, + { 0x40e4c, 0x4b4b4b }, + { 0x40e50, 0x4c4c4c }, + { 0x40e54, 0x4d4d4d }, + { 0x40e58, 0x4f4f4f }, + { 0x40e5c, 0x505050 }, + { 0x40e60, 0x515151 }, + { 0x40e64, 0x525252 }, + { 0x40e68, 0x535353 }, + { 0x40e6c, 0x545454 }, + { 0x40e70, 0x565656 }, + { 0x40e74, 0x575757 }, + { 0x40e78, 0x585858 }, + { 0x40e7c, 0x595959 }, + { 0x40e80, 0x5b5b5b }, + { 0x40e84, 0x5c5c5c }, + { 0x40e88, 0x5d5d5d }, + { 0x40e8c, 0x5e5e5e }, + { 0x40e90, 0x606060 }, + { 0x40e94, 0x616161 }, + { 0x40e98, 0x626262 }, + { 0x40e9c, 0x646464 }, + { 0x40ea0, 0x656565 }, + { 0x40ea4, 0x666666 }, + { 0x40ea8, 0x686868 }, + { 0x40eac, 0x696969 }, + { 0x40eb0, 0x6a6a6a }, + { 0x40eb4, 0x6c6c6c }, + { 0x40eb8, 0x6d6d6d }, + { 0x40ebc, 0x6f6f6f }, + { 0x40ec0, 0x707070 }, + { 0x40ec4, 0x717171 }, + { 0x40ec8, 0x737373 }, + { 0x40ecc, 0x747474 }, + { 0x40ed0, 0x767676 }, + { 0x40ed4, 0x777777 }, + { 0x40ed8, 0x797979 }, + { 0x40edc, 0x7a7a7a }, + { 0x40ee0, 0x7c7c7c }, + { 0x40ee4, 0x7d7d7d }, + { 0x40ee8, 0x7f7f7f }, + { 0x40eec, 0x808080 }, + { 0x40ef0, 0x828282 }, + { 0x40ef4, 0x838383 }, + { 0x40ef8, 0x858585 }, + { 0x40efc, 0x868686 }, + { 0x40f00, 0x888888 }, + { 0x40f04, 0x898989 }, + { 0x40f08, 0x8b8b8b }, + { 0x40f0c, 0x8d8d8d }, + { 0x40f10, 0x8e8e8e }, + { 0x40f14, 0x909090 }, + { 0x40f18, 0x919191 }, + { 0x40f1c, 0x939393 }, + { 0x40f20, 0x959595 }, + { 0x40f24, 0x969696 }, + { 0x40f28, 0x989898 }, + { 0x40f2c, 0x9a9a9a }, + { 0x40f30, 0x9b9b9b }, + { 0x40f34, 0x9d9d9d }, + { 0x40f38, 0x9f9f9f }, + { 0x40f3c, 0xa1a1a1 }, + { 0x40f40, 0xa2a2a2 }, + { 0x40f44, 0xa4a4a4 }, + { 0x40f48, 0xa6a6a6 }, + { 0x40f4c, 0xa7a7a7 }, + { 0x40f50, 0xa9a9a9 }, + { 0x40f54, 0xababab }, + { 0x40f58, 0xadadad }, + { 0x40f5c, 0xafafaf }, + { 0x40f60, 0xb0b0b0 }, + { 0x40f64, 0xb2b2b2 }, + { 0x40f68, 0xb4b4b4 }, + { 0x40f6c, 0xb6b6b6 }, + { 0x40f70, 0xb8b8b8 }, + { 0x40f74, 0xbababa }, + { 0x40f78, 0xbbbbbb }, + { 0x40f7c, 0xbdbdbd }, + { 0x40f80, 0xbfbfbf }, + { 0x40f84, 0xc1c1c1 }, + { 0x40f88, 0xc3c3c3 }, + { 0x40f8c, 0xc5c5c5 }, + { 0x40f90, 0xc7c7c7 }, + { 0x40f94, 0xc9c9c9 }, + { 0x40f98, 0xcbcbcb }, + { 0x40f9c, 0xcdcdcd }, + { 0x40fa0, 0xcfcfcf }, + { 0x40fa4, 0xd1d1d1 }, + { 0x40fa8, 0xd3d3d3 }, + { 0x40fac, 0xd5d5d5 }, + { 0x40fb0, 0xd7d7d7 }, + { 0x40fb4, 0xd9d9d9 }, + { 0x40fb8, 0xdbdbdb }, + { 0x40fbc, 0xdddddd }, + { 0x40fc0, 0xdfdfdf }, + { 0x40fc4, 0xe1e1e1 }, + { 0x40fc8, 0xe3e3e3 }, + { 0x40fcc, 0xe5e5e5 }, + { 0x40fd0, 0xe7e7e7 }, + { 0x40fd4, 0xe9e9e9 }, + { 0x40fd8, 0xebebeb }, + { 0x40fdc, 0xeeeeee }, + { 0x40fe0, 0xf0f0f0 }, + { 0x40fe4, 0xf2f2f2 }, + { 0x40fe8, 0xf4f4f4 }, + { 0x40fec, 0xf6f6f6 }, + { 0x40ff0, 0xf8f8f8 }, + { 0x40ff4, 0xfbfbfb }, + { 0x40ff8, 0xfdfdfd }, + { 0x40ffc, 0xffffff }, +}; diff --git a/drivers/video/msm/mdp_hw.h b/drivers/video/msm/mdp_hw.h new file mode 100644 index 000000000000..4e3deb4e592b --- /dev/null +++ b/drivers/video/msm/mdp_hw.h @@ -0,0 +1,621 @@ +/* drivers/video/msm_fb/mdp_hw.h + * + * Copyright (C) 2007 QUALCOMM Incorporated + * Copyright (C) 2007 Google Incorporated + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#ifndef _MDP_HW_H_ +#define _MDP_HW_H_ + +#include <mach/msm_iomap.h> +#include <mach/msm_fb.h> + +struct mdp_info { + struct mdp_device mdp_dev; + char * __iomem base; + int irq; +}; +struct mdp_blit_req; +struct mdp_device; +int mdp_ppp_blit(const struct mdp_info *mdp, struct mdp_blit_req *req, + struct file *src_file, unsigned long src_start, + unsigned long src_len, struct file *dst_file, + unsigned long dst_start, unsigned long dst_len); +#define mdp_writel(mdp, value, offset) writel(value, mdp->base + offset) +#define mdp_readl(mdp, offset) readl(mdp->base + offset) + +#define MDP_SYNC_CONFIG_0 (0x00000) +#define MDP_SYNC_CONFIG_1 (0x00004) +#define MDP_SYNC_CONFIG_2 (0x00008) +#define MDP_SYNC_STATUS_0 (0x0000c) +#define MDP_SYNC_STATUS_1 (0x00010) +#define MDP_SYNC_STATUS_2 (0x00014) +#define MDP_SYNC_THRESH_0 (0x00018) +#define MDP_SYNC_THRESH_1 (0x0001c) +#define MDP_INTR_ENABLE (0x00020) +#define MDP_INTR_STATUS (0x00024) +#define MDP_INTR_CLEAR (0x00028) +#define MDP_DISPLAY0_START (0x00030) +#define MDP_DISPLAY1_START (0x00034) +#define MDP_DISPLAY_STATUS (0x00038) +#define MDP_EBI2_LCD0 (0x0003c) +#define MDP_EBI2_LCD1 (0x00040) +#define MDP_DISPLAY0_ADDR (0x00054) +#define MDP_DISPLAY1_ADDR (0x00058) +#define MDP_EBI2_PORTMAP_MODE (0x0005c) +#define MDP_MODE (0x00060) +#define MDP_TV_OUT_STATUS (0x00064) +#define MDP_HW_VERSION (0x00070) +#define MDP_SW_RESET (0x00074) +#define MDP_AXI_ERROR_MASTER_STOP (0x00078) +#define MDP_SEL_CLK_OR_HCLK_TEST_BUS (0x0007c) +#define MDP_PRIMARY_VSYNC_OUT_CTRL (0x00080) +#define MDP_SECONDARY_VSYNC_OUT_CTRL (0x00084) +#define MDP_EXTERNAL_VSYNC_OUT_CTRL (0x00088) +#define MDP_VSYNC_CTRL (0x0008c) +#define MDP_CGC_EN (0x00100) +#define MDP_CMD_STATUS (0x10008) +#define MDP_PROFILE_EN (0x10010) +#define MDP_PROFILE_COUNT (0x10014) +#define MDP_DMA_START (0x10044) +#define MDP_FULL_BYPASS_WORD0 (0x10100) +#define MDP_FULL_BYPASS_WORD1 (0x10104) +#define MDP_COMMAND_CONFIG (0x10104) +#define MDP_FULL_BYPASS_WORD2 (0x10108) +#define MDP_FULL_BYPASS_WORD3 (0x1010c) +#define MDP_FULL_BYPASS_WORD4 (0x10110) +#define MDP_FULL_BYPASS_WORD6 (0x10118) +#define MDP_FULL_BYPASS_WORD7 (0x1011c) +#define MDP_FULL_BYPASS_WORD8 (0x10120) +#define MDP_FULL_BYPASS_WORD9 (0x10124) +#define MDP_PPP_SOURCE_CONFIG (0x10124) +#define MDP_FULL_BYPASS_WORD10 (0x10128) +#define MDP_FULL_BYPASS_WORD11 (0x1012c) +#define MDP_FULL_BYPASS_WORD12 (0x10130) +#define MDP_FULL_BYPASS_WORD13 (0x10134) +#define MDP_FULL_BYPASS_WORD14 (0x10138) +#define MDP_PPP_OPERATION_CONFIG (0x10138) +#define MDP_FULL_BYPASS_WORD15 (0x1013c) +#define MDP_FULL_BYPASS_WORD16 (0x10140) +#define MDP_FULL_BYPASS_WORD17 (0x10144) +#define MDP_FULL_BYPASS_WORD18 (0x10148) +#define MDP_FULL_BYPASS_WORD19 (0x1014c) +#define MDP_FULL_BYPASS_WORD20 (0x10150) +#define MDP_PPP_DESTINATION_CONFIG (0x10150) +#define MDP_FULL_BYPASS_WORD21 (0x10154) +#define MDP_FULL_BYPASS_WORD22 (0x10158) +#define MDP_FULL_BYPASS_WORD23 (0x1015c) +#define MDP_FULL_BYPASS_WORD24 (0x10160) +#define MDP_FULL_BYPASS_WORD25 (0x10164) +#define MDP_FULL_BYPASS_WORD26 (0x10168) +#define MDP_FULL_BYPASS_WORD27 (0x1016c) +#define MDP_FULL_BYPASS_WORD29 (0x10174) +#define MDP_FULL_BYPASS_WORD30 (0x10178) +#define MDP_FULL_BYPASS_WORD31 (0x1017c) +#define MDP_FULL_BYPASS_WORD32 (0x10180) +#define MDP_DMA_CONFIG (0x10180) +#define MDP_FULL_BYPASS_WORD33 (0x10184) +#define MDP_FULL_BYPASS_WORD34 (0x10188) +#define MDP_FULL_BYPASS_WORD35 (0x1018c) +#define MDP_FULL_BYPASS_WORD37 (0x10194) +#define MDP_FULL_BYPASS_WORD39 (0x1019c) +#define MDP_FULL_BYPASS_WORD40 (0x101a0) +#define MDP_FULL_BYPASS_WORD41 (0x101a4) +#define MDP_FULL_BYPASS_WORD43 (0x101ac) +#define MDP_FULL_BYPASS_WORD46 (0x101b8) +#define MDP_FULL_BYPASS_WORD47 (0x101bc) +#define MDP_FULL_BYPASS_WORD48 (0x101c0) +#define MDP_FULL_BYPASS_WORD49 (0x101c4) +#define MDP_FULL_BYPASS_WORD50 (0x101c8) +#define MDP_FULL_BYPASS_WORD51 (0x101cc) +#define MDP_FULL_BYPASS_WORD52 (0x101d0) +#define MDP_FULL_BYPASS_WORD53 (0x101d4) +#define MDP_FULL_BYPASS_WORD54 (0x101d8) +#define MDP_FULL_BYPASS_WORD55 (0x101dc) +#define MDP_FULL_BYPASS_WORD56 (0x101e0) +#define MDP_FULL_BYPASS_WORD57 (0x101e4) +#define MDP_FULL_BYPASS_WORD58 (0x101e8) +#define MDP_FULL_BYPASS_WORD59 (0x101ec) +#define MDP_FULL_BYPASS_WORD60 (0x101f0) +#define MDP_VSYNC_THRESHOLD (0x101f0) +#define MDP_FULL_BYPASS_WORD61 (0x101f4) +#define MDP_FULL_BYPASS_WORD62 (0x101f8) +#define MDP_FULL_BYPASS_WORD63 (0x101fc) +#define MDP_TFETCH_TEST_MODE (0x20004) +#define MDP_TFETCH_STATUS (0x20008) +#define MDP_TFETCH_TILE_COUNT (0x20010) +#define MDP_TFETCH_FETCH_COUNT (0x20014) +#define MDP_TFETCH_CONSTANT_COLOR (0x20040) +#define MDP_CSC_BYPASS (0x40004) +#define MDP_SCALE_COEFF_LSB (0x5fffc) +#define MDP_TV_OUT_CTL (0xc0000) +#define MDP_TV_OUT_FIR_COEFF (0xc0004) +#define MDP_TV_OUT_BUF_ADDR (0xc0008) +#define MDP_TV_OUT_CC_DATA (0xc000c) +#define MDP_TV_OUT_SOBEL (0xc0010) +#define MDP_TV_OUT_Y_CLAMP (0xc0018) +#define MDP_TV_OUT_CB_CLAMP (0xc001c) +#define MDP_TV_OUT_CR_CLAMP (0xc0020) +#define MDP_TEST_MODE_CLK (0xd0000) +#define MDP_TEST_MISR_RESET_CLK (0xd0004) +#define MDP_TEST_EXPORT_MISR_CLK (0xd0008) +#define MDP_TEST_MISR_CURR_VAL_CLK (0xd000c) +#define MDP_TEST_MODE_HCLK (0xd0100) +#define MDP_TEST_MISR_RESET_HCLK (0xd0104) +#define MDP_TEST_EXPORT_MISR_HCLK (0xd0108) +#define MDP_TEST_MISR_CURR_VAL_HCLK (0xd010c) +#define MDP_TEST_MODE_DCLK (0xd0200) +#define MDP_TEST_MISR_RESET_DCLK (0xd0204) +#define MDP_TEST_EXPORT_MISR_DCLK (0xd0208) +#define MDP_TEST_MISR_CURR_VAL_DCLK (0xd020c) +#define MDP_TEST_CAPTURED_DCLK (0xd0210) +#define MDP_TEST_MISR_CAPT_VAL_DCLK (0xd0214) +#define MDP_LCDC_CTL (0xe0000) +#define MDP_LCDC_HSYNC_CTL (0xe0004) +#define MDP_LCDC_VSYNC_CTL (0xe0008) +#define MDP_LCDC_ACTIVE_HCTL (0xe000c) +#define MDP_LCDC_ACTIVE_VCTL (0xe0010) +#define MDP_LCDC_BORDER_CLR (0xe0014) +#define MDP_LCDC_H_BLANK (0xe0018) +#define MDP_LCDC_V_BLANK (0xe001c) +#define MDP_LCDC_UNDERFLOW_CLR (0xe0020) +#define MDP_LCDC_HSYNC_SKEW (0xe0024) +#define MDP_LCDC_TEST_CTL (0xe0028) +#define MDP_LCDC_LINE_IRQ (0xe002c) +#define MDP_LCDC_CTL_POLARITY (0xe0030) +#define MDP_LCDC_DMA_CONFIG (0xe1000) +#define MDP_LCDC_DMA_SIZE (0xe1004) +#define MDP_LCDC_DMA_IBUF_ADDR (0xe1008) +#define MDP_LCDC_DMA_IBUF_Y_STRIDE (0xe100c) + + +#define MDP_DMA2_TERM 0x1 +#define MDP_DMA3_TERM 0x2 +#define MDP_PPP_TERM 0x3 + +/* MDP_INTR_ENABLE */ +#define DL0_ROI_DONE (1<<0) +#define DL1_ROI_DONE (1<<1) +#define DL0_DMA2_TERM_DONE (1<<2) +#define DL1_DMA2_TERM_DONE (1<<3) +#define DL0_PPP_TERM_DONE (1<<4) +#define DL1_PPP_TERM_DONE (1<<5) +#define TV_OUT_DMA3_DONE (1<<6) +#define TV_ENC_UNDERRUN (1<<7) +#define DL0_FETCH_DONE (1<<11) +#define DL1_FETCH_DONE (1<<12) + +#define MDP_PPP_BUSY_STATUS (DL0_ROI_DONE| \ + DL1_ROI_DONE| \ + DL0_PPP_TERM_DONE| \ + DL1_PPP_TERM_DONE) + +#define MDP_ANY_INTR_MASK (DL0_ROI_DONE| \ + DL1_ROI_DONE| \ + DL0_DMA2_TERM_DONE| \ + DL1_DMA2_TERM_DONE| \ + DL0_PPP_TERM_DONE| \ + DL1_PPP_TERM_DONE| \ + DL0_FETCH_DONE| \ + DL1_FETCH_DONE| \ + TV_ENC_UNDERRUN) + +#define MDP_TOP_LUMA 16 +#define MDP_TOP_CHROMA 0 +#define MDP_BOTTOM_LUMA 19 +#define MDP_BOTTOM_CHROMA 3 +#define MDP_LEFT_LUMA 22 +#define MDP_LEFT_CHROMA 6 +#define MDP_RIGHT_LUMA 25 +#define MDP_RIGHT_CHROMA 9 + +#define CLR_G 0x0 +#define CLR_B 0x1 +#define CLR_R 0x2 +#define CLR_ALPHA 0x3 + +#define CLR_Y CLR_G +#define CLR_CB CLR_B +#define CLR_CR CLR_R + +/* from lsb to msb */ +#define MDP_GET_PACK_PATTERN(a, x, y, z, bit) \ + (((a)<<(bit*3))|((x)<<(bit*2))|((y)<<bit)|(z)) + +/* MDP_SYNC_CONFIG_0/1/2 */ +#define MDP_SYNCFG_HGT_LOC 22 +#define MDP_SYNCFG_VSYNC_EXT_EN (1<<21) +#define MDP_SYNCFG_VSYNC_INT_EN (1<<20) + +/* MDP_SYNC_THRESH_0 */ +#define MDP_PRIM_BELOW_LOC 0 +#define MDP_PRIM_ABOVE_LOC 8 + +/* MDP_{PRIMARY,SECONDARY,EXTERNAL}_VSYNC_OUT_CRL */ +#define VSYNC_PULSE_EN (1<<31) +#define VSYNC_PULSE_INV (1<<30) + +/* MDP_VSYNC_CTRL */ +#define DISP0_VSYNC_MAP_VSYNC0 0 +#define DISP0_VSYNC_MAP_VSYNC1 (1<<0) +#define DISP0_VSYNC_MAP_VSYNC2 ((1<<0)|(1<<1)) + +#define DISP1_VSYNC_MAP_VSYNC0 0 +#define DISP1_VSYNC_MAP_VSYNC1 (1<<2) +#define DISP1_VSYNC_MAP_VSYNC2 ((1<<2)|(1<<3)) + +#define PRIMARY_LCD_SYNC_EN (1<<4) +#define PRIMARY_LCD_SYNC_DISABLE 0 + +#define SECONDARY_LCD_SYNC_EN (1<<5) +#define SECONDARY_LCD_SYNC_DISABLE 0 + +#define EXTERNAL_LCD_SYNC_EN (1<<6) +#define EXTERNAL_LCD_SYNC_DISABLE 0 + +/* MDP_VSYNC_THRESHOLD / MDP_FULL_BYPASS_WORD60 */ +#define VSYNC_THRESHOLD_ABOVE_LOC 0 +#define VSYNC_THRESHOLD_BELOW_LOC 16 +#define VSYNC_ANTI_TEAR_EN (1<<31) + +/* MDP_COMMAND_CONFIG / MDP_FULL_BYPASS_WORD1 */ +#define MDP_CMD_DBGBUS_EN (1<<0) + +/* MDP_PPP_SOURCE_CONFIG / MDP_FULL_BYPASS_WORD9&53 */ +#define PPP_SRC_C0G_8BIT ((1<<1)|(1<<0)) +#define PPP_SRC_C1B_8BIT ((1<<3)|(1<<2)) +#define PPP_SRC_C2R_8BIT ((1<<5)|(1<<4)) +#define PPP_SRC_C3A_8BIT ((1<<7)|(1<<6)) + +#define PPP_SRC_C0G_6BIT (1<<1) +#define PPP_SRC_C1B_6BIT (1<<3) +#define PPP_SRC_C2R_6BIT (1<<5) + +#define PPP_SRC_C0G_5BIT (1<<0) +#define PPP_SRC_C1B_5BIT (1<<2) +#define PPP_SRC_C2R_5BIT (1<<4) + +#define PPP_SRC_C3ALPHA_EN (1<<8) + +#define PPP_SRC_BPP_1BYTES 0 +#define PPP_SRC_BPP_2BYTES (1<<9) +#define PPP_SRC_BPP_3BYTES (1<<10) +#define PPP_SRC_BPP_4BYTES ((1<<10)|(1<<9)) + +#define PPP_SRC_BPP_ROI_ODD_X (1<<11) +#define PPP_SRC_BPP_ROI_ODD_Y (1<<12) +#define PPP_SRC_INTERLVD_2COMPONENTS (1<<13) +#define PPP_SRC_INTERLVD_3COMPONENTS (1<<14) +#define PPP_SRC_INTERLVD_4COMPONENTS ((1<<14)|(1<<13)) + + +/* RGB666 unpack format +** TIGHT means R6+G6+B6 together +** LOOSE means R6+2 +G6+2+ B6+2 (with MSB) +** or 2+R6 +2+G6 +2+B6 (with LSB) +*/ +#define PPP_SRC_PACK_TIGHT (1<<17) +#define PPP_SRC_PACK_LOOSE 0 +#define PPP_SRC_PACK_ALIGN_LSB 0 +#define PPP_SRC_PACK_ALIGN_MSB (1<<18) + +#define PPP_SRC_PLANE_INTERLVD 0 +#define PPP_SRC_PLANE_PSEUDOPLNR (1<<20) + +#define PPP_SRC_WMV9_MODE (1<<21) + +/* MDP_PPP_OPERATION_CONFIG / MDP_FULL_BYPASS_WORD14 */ +#define PPP_OP_SCALE_X_ON (1<<0) +#define PPP_OP_SCALE_Y_ON (1<<1) + +#define PPP_OP_CONVERT_RGB2YCBCR 0 +#define PPP_OP_CONVERT_YCBCR2RGB (1<<2) +#define PPP_OP_CONVERT_ON (1<<3) + +#define PPP_OP_CONVERT_MATRIX_PRIMARY 0 +#define PPP_OP_CONVERT_MATRIX_SECONDARY (1<<4) + +#define PPP_OP_LUT_C0_ON (1<<5) +#define PPP_OP_LUT_C1_ON (1<<6) +#define PPP_OP_LUT_C2_ON (1<<7) + +/* rotate or blend enable */ +#define PPP_OP_ROT_ON (1<<8) + +#define PPP_OP_ROT_90 (1<<9) +#define PPP_OP_FLIP_LR (1<<10) +#define PPP_OP_FLIP_UD (1<<11) + +#define PPP_OP_BLEND_ON (1<<12) + +#define PPP_OP_BLEND_SRCPIXEL_ALPHA 0 +#define PPP_OP_BLEND_DSTPIXEL_ALPHA (1<<13) +#define PPP_OP_BLEND_CONSTANT_ALPHA (1<<14) +#define PPP_OP_BLEND_SRCPIXEL_TRANSP ((1<<13)|(1<<14)) + +#define PPP_OP_BLEND_ALPHA_BLEND_NORMAL 0 +#define PPP_OP_BLEND_ALPHA_BLEND_REVERSE (1<<15) + +#define PPP_OP_DITHER_EN (1<<16) + +#define PPP_OP_COLOR_SPACE_RGB 0 +#define PPP_OP_COLOR_SPACE_YCBCR (1<<17) + +#define PPP_OP_SRC_CHROMA_RGB 0 +#define PPP_OP_SRC_CHROMA_H2V1 (1<<18) +#define PPP_OP_SRC_CHROMA_H1V2 (1<<19) +#define PPP_OP_SRC_CHROMA_420 ((1<<18)|(1<<19)) +#define PPP_OP_SRC_CHROMA_COSITE 0 +#define PPP_OP_SRC_CHROMA_OFFSITE (1<<20) + +#define PPP_OP_DST_CHROMA_RGB 0 +#define PPP_OP_DST_CHROMA_H2V1 (1<<21) +#define PPP_OP_DST_CHROMA_H1V2 (1<<22) +#define PPP_OP_DST_CHROMA_420 ((1<<21)|(1<<22)) +#define PPP_OP_DST_CHROMA_COSITE 0 +#define PPP_OP_DST_CHROMA_OFFSITE (1<<23) + +#define PPP_BLEND_ALPHA_TRANSP (1<<24) + +#define PPP_OP_BG_CHROMA_RGB 0 +#define PPP_OP_BG_CHROMA_H2V1 (1<<25) +#define PPP_OP_BG_CHROMA_H1V2 (1<<26) +#define PPP_OP_BG_CHROMA_420 ((1<<25)|(1<<26)) +#define PPP_OP_BG_CHROMA_SITE_COSITE 0 +#define PPP_OP_BG_CHROMA_SITE_OFFSITE (1<<27) + +/* MDP_PPP_DESTINATION_CONFIG / MDP_FULL_BYPASS_WORD20 */ +#define PPP_DST_C0G_8BIT ((1<<0)|(1<<1)) +#define PPP_DST_C1B_8BIT ((1<<3)|(1<<2)) +#define PPP_DST_C2R_8BIT ((1<<5)|(1<<4)) +#define PPP_DST_C3A_8BIT ((1<<7)|(1<<6)) + +#define PPP_DST_C0G_6BIT (1<<1) +#define PPP_DST_C1B_6BIT (1<<3) +#define PPP_DST_C2R_6BIT (1<<5) + +#define PPP_DST_C0G_5BIT (1<<0) +#define PPP_DST_C1B_5BIT (1<<2) +#define PPP_DST_C2R_5BIT (1<<4) + +#define PPP_DST_C3A_8BIT ((1<<7)|(1<<6)) +#define PPP_DST_C3ALPHA_EN (1<<8) + +#define PPP_DST_INTERLVD_2COMPONENTS (1<<9) +#define PPP_DST_INTERLVD_3COMPONENTS (1<<10) +#define PPP_DST_INTERLVD_4COMPONENTS ((1<<10)|(1<<9)) +#define PPP_DST_INTERLVD_6COMPONENTS ((1<<11)|(1<<9)) + +#define PPP_DST_PACK_LOOSE 0 +#define PPP_DST_PACK_TIGHT (1<<13) +#define PPP_DST_PACK_ALIGN_LSB 0 +#define PPP_DST_PACK_ALIGN_MSB (1<<14) + +#define PPP_DST_OUT_SEL_AXI 0 +#define PPP_DST_OUT_SEL_MDDI (1<<15) + +#define PPP_DST_BPP_2BYTES (1<<16) +#define PPP_DST_BPP_3BYTES (1<<17) +#define PPP_DST_BPP_4BYTES ((1<<17)|(1<<16)) + +#define PPP_DST_PLANE_INTERLVD 0 +#define PPP_DST_PLANE_PLANAR (1<<18) +#define PPP_DST_PLANE_PSEUDOPLNR (1<<19) + +#define PPP_DST_TO_TV (1<<20) + +#define PPP_DST_MDDI_PRIMARY 0 +#define PPP_DST_MDDI_SECONDARY (1<<21) +#define PPP_DST_MDDI_EXTERNAL (1<<22) + +/* image configurations by image type */ +#define PPP_CFG_MDP_RGB_565(dir) (PPP_##dir##_C2R_5BIT | \ + PPP_##dir##_C0G_6BIT | \ + PPP_##dir##_C1B_5BIT | \ + PPP_##dir##_BPP_2BYTES | \ + PPP_##dir##_INTERLVD_3COMPONENTS | \ + PPP_##dir##_PACK_TIGHT | \ + PPP_##dir##_PACK_ALIGN_LSB | \ + PPP_##dir##_PLANE_INTERLVD) + +#define PPP_CFG_MDP_RGB_888(dir) (PPP_##dir##_C2R_8BIT | \ + PPP_##dir##_C0G_8BIT | \ + PPP_##dir##_C1B_8BIT | \ + PPP_##dir##_BPP_3BYTES | \ + PPP_##dir##_INTERLVD_3COMPONENTS | \ + PPP_##dir##_PACK_TIGHT | \ + PPP_##dir##_PACK_ALIGN_LSB | \ + PPP_##dir##_PLANE_INTERLVD) + +#define PPP_CFG_MDP_ARGB_8888(dir) (PPP_##dir##_C2R_8BIT | \ + PPP_##dir##_C0G_8BIT | \ + PPP_##dir##_C1B_8BIT | \ + PPP_##dir##_C3A_8BIT | \ + PPP_##dir##_C3ALPHA_EN | \ + PPP_##dir##_BPP_4BYTES | \ + PPP_##dir##_INTERLVD_4COMPONENTS | \ + PPP_##dir##_PACK_TIGHT | \ + PPP_##dir##_PACK_ALIGN_LSB | \ + PPP_##dir##_PLANE_INTERLVD) + +#define PPP_CFG_MDP_XRGB_8888(dir) PPP_CFG_MDP_ARGB_8888(dir) +#define PPP_CFG_MDP_RGBA_8888(dir) PPP_CFG_MDP_ARGB_8888(dir) +#define PPP_CFG_MDP_BGRA_8888(dir) PPP_CFG_MDP_ARGB_8888(dir) + +#define PPP_CFG_MDP_Y_CBCR_H2V2(dir) (PPP_##dir##_C2R_8BIT | \ + PPP_##dir##_C0G_8BIT | \ + PPP_##dir##_C1B_8BIT | \ + PPP_##dir##_C3A_8BIT | \ + PPP_##dir##_BPP_2BYTES | \ + PPP_##dir##_INTERLVD_2COMPONENTS | \ + PPP_##dir##_PACK_TIGHT | \ + PPP_##dir##_PACK_ALIGN_LSB | \ + PPP_##dir##_PLANE_PSEUDOPLNR) + +#define PPP_CFG_MDP_Y_CRCB_H2V2(dir) PPP_CFG_MDP_Y_CBCR_H2V2(dir) + +#define PPP_CFG_MDP_YCRYCB_H2V1(dir) (PPP_##dir##_C2R_8BIT | \ + PPP_##dir##_C0G_8BIT | \ + PPP_##dir##_C1B_8BIT | \ + PPP_##dir##_C3A_8BIT | \ + PPP_##dir##_BPP_2BYTES | \ + PPP_##dir##_INTERLVD_4COMPONENTS | \ + PPP_##dir##_PACK_TIGHT | \ + PPP_##dir##_PACK_ALIGN_LSB |\ + PPP_##dir##_PLANE_INTERLVD) + +#define PPP_CFG_MDP_Y_CBCR_H2V1(dir) (PPP_##dir##_C2R_8BIT | \ + PPP_##dir##_C0G_8BIT | \ + PPP_##dir##_C1B_8BIT | \ + PPP_##dir##_C3A_8BIT | \ + PPP_##dir##_BPP_2BYTES | \ + PPP_##dir##_INTERLVD_2COMPONENTS | \ + PPP_##dir##_PACK_TIGHT | \ + PPP_##dir##_PACK_ALIGN_LSB | \ + PPP_##dir##_PLANE_PSEUDOPLNR) + +#define PPP_CFG_MDP_Y_CRCB_H2V1(dir) PPP_CFG_MDP_Y_CBCR_H2V1(dir) + +#define PPP_PACK_PATTERN_MDP_RGB_565 \ + MDP_GET_PACK_PATTERN(0, CLR_R, CLR_G, CLR_B, 8) +#define PPP_PACK_PATTERN_MDP_RGB_888 PPP_PACK_PATTERN_MDP_RGB_565 +#define PPP_PACK_PATTERN_MDP_XRGB_8888 \ + MDP_GET_PACK_PATTERN(CLR_ALPHA, CLR_R, CLR_G, CLR_B, 8) +#define PPP_PACK_PATTERN_MDP_ARGB_8888 PPP_PACK_PATTERN_MDP_XRGB_8888 +#define PPP_PACK_PATTERN_MDP_RGBA_8888 \ + MDP_GET_PACK_PATTERN(CLR_ALPHA, CLR_B, CLR_G, CLR_R, 8) +#define PPP_PACK_PATTERN_MDP_BGRA_8888 \ + MDP_GET_PACK_PATTERN(CLR_ALPHA, CLR_R, CLR_G, CLR_B, 8) +#define PPP_PACK_PATTERN_MDP_Y_CBCR_H2V1 \ + MDP_GET_PACK_PATTERN(0, 0, CLR_CB, CLR_CR, 8) +#define PPP_PACK_PATTERN_MDP_Y_CBCR_H2V2 PPP_PACK_PATTERN_MDP_Y_CBCR_H2V1 +#define PPP_PACK_PATTERN_MDP_Y_CRCB_H2V1 \ + MDP_GET_PACK_PATTERN(0, 0, CLR_CR, CLR_CB, 8) +#define PPP_PACK_PATTERN_MDP_Y_CRCB_H2V2 PPP_PACK_PATTERN_MDP_Y_CRCB_H2V1 +#define PPP_PACK_PATTERN_MDP_YCRYCB_H2V1 \ + MDP_GET_PACK_PATTERN(CLR_Y, CLR_R, CLR_Y, CLR_B, 8) + +#define PPP_CHROMA_SAMP_MDP_RGB_565(dir) PPP_OP_##dir##_CHROMA_RGB +#define PPP_CHROMA_SAMP_MDP_RGB_888(dir) PPP_OP_##dir##_CHROMA_RGB +#define PPP_CHROMA_SAMP_MDP_XRGB_8888(dir) PPP_OP_##dir##_CHROMA_RGB +#define PPP_CHROMA_SAMP_MDP_ARGB_8888(dir) PPP_OP_##dir##_CHROMA_RGB +#define PPP_CHROMA_SAMP_MDP_RGBA_8888(dir) PPP_OP_##dir##_CHROMA_RGB +#define PPP_CHROMA_SAMP_MDP_BGRA_8888(dir) PPP_OP_##dir##_CHROMA_RGB +#define PPP_CHROMA_SAMP_MDP_Y_CBCR_H2V1(dir) PPP_OP_##dir##_CHROMA_H2V1 +#define PPP_CHROMA_SAMP_MDP_Y_CBCR_H2V2(dir) PPP_OP_##dir##_CHROMA_420 +#define PPP_CHROMA_SAMP_MDP_Y_CRCB_H2V1(dir) PPP_OP_##dir##_CHROMA_H2V1 +#define PPP_CHROMA_SAMP_MDP_Y_CRCB_H2V2(dir) PPP_OP_##dir##_CHROMA_420 +#define PPP_CHROMA_SAMP_MDP_YCRYCB_H2V1(dir) PPP_OP_##dir##_CHROMA_H2V1 + +/* Helpful array generation macros */ +#define PPP_ARRAY0(name) \ + [MDP_RGB_565] = PPP_##name##_MDP_RGB_565,\ + [MDP_RGB_888] = PPP_##name##_MDP_RGB_888,\ + [MDP_XRGB_8888] = PPP_##name##_MDP_XRGB_8888,\ + [MDP_ARGB_8888] = PPP_##name##_MDP_ARGB_8888,\ + [MDP_RGBA_8888] = PPP_##name##_MDP_RGBA_8888,\ + [MDP_BGRA_8888] = PPP_##name##_MDP_BGRA_8888,\ + [MDP_Y_CBCR_H2V1] = PPP_##name##_MDP_Y_CBCR_H2V1,\ + [MDP_Y_CBCR_H2V2] = PPP_##name##_MDP_Y_CBCR_H2V2,\ + [MDP_Y_CRCB_H2V1] = PPP_##name##_MDP_Y_CRCB_H2V1,\ + [MDP_Y_CRCB_H2V2] = PPP_##name##_MDP_Y_CRCB_H2V2,\ + [MDP_YCRYCB_H2V1] = PPP_##name##_MDP_YCRYCB_H2V1 + +#define PPP_ARRAY1(name, dir) \ + [MDP_RGB_565] = PPP_##name##_MDP_RGB_565(dir),\ + [MDP_RGB_888] = PPP_##name##_MDP_RGB_888(dir),\ + [MDP_XRGB_8888] = PPP_##name##_MDP_XRGB_8888(dir),\ + [MDP_ARGB_8888] = PPP_##name##_MDP_ARGB_8888(dir),\ + [MDP_RGBA_8888] = PPP_##name##_MDP_RGBA_8888(dir),\ + [MDP_BGRA_8888] = PPP_##name##_MDP_BGRA_8888(dir),\ + [MDP_Y_CBCR_H2V1] = PPP_##name##_MDP_Y_CBCR_H2V1(dir),\ + [MDP_Y_CBCR_H2V2] = PPP_##name##_MDP_Y_CBCR_H2V2(dir),\ + [MDP_Y_CRCB_H2V1] = PPP_##name##_MDP_Y_CRCB_H2V1(dir),\ + [MDP_Y_CRCB_H2V2] = PPP_##name##_MDP_Y_CRCB_H2V2(dir),\ + [MDP_YCRYCB_H2V1] = PPP_##name##_MDP_YCRYCB_H2V1(dir) + +#define IS_YCRCB(img) ((img == MDP_Y_CRCB_H2V2) | (img == MDP_Y_CBCR_H2V2) | \ + (img == MDP_Y_CRCB_H2V1) | (img == MDP_Y_CBCR_H2V1) | \ + (img == MDP_YCRYCB_H2V1)) +#define IS_RGB(img) ((img == MDP_RGB_565) | (img == MDP_RGB_888) | \ + (img == MDP_ARGB_8888) | (img == MDP_RGBA_8888) | \ + (img == MDP_XRGB_8888) | (img == MDP_BGRA_8888)) +#define HAS_ALPHA(img) ((img == MDP_ARGB_8888) | (img == MDP_RGBA_8888) | \ + (img == MDP_BGRA_8888)) + +#define IS_PSEUDOPLNR(img) ((img == MDP_Y_CRCB_H2V2) | \ + (img == MDP_Y_CBCR_H2V2) | \ + (img == MDP_Y_CRCB_H2V1) | \ + (img == MDP_Y_CBCR_H2V1)) + +/* Mappings from addr to purpose */ +#define PPP_ADDR_SRC_ROI MDP_FULL_BYPASS_WORD2 +#define PPP_ADDR_SRC0 MDP_FULL_BYPASS_WORD3 +#define PPP_ADDR_SRC1 MDP_FULL_BYPASS_WORD4 +#define PPP_ADDR_SRC_YSTRIDE MDP_FULL_BYPASS_WORD7 +#define PPP_ADDR_SRC_CFG MDP_FULL_BYPASS_WORD9 +#define PPP_ADDR_SRC_PACK_PATTERN MDP_FULL_BYPASS_WORD10 +#define PPP_ADDR_OPERATION MDP_FULL_BYPASS_WORD14 +#define PPP_ADDR_PHASEX_INIT MDP_FULL_BYPASS_WORD15 +#define PPP_ADDR_PHASEY_INIT MDP_FULL_BYPASS_WORD16 +#define PPP_ADDR_PHASEX_STEP MDP_FULL_BYPASS_WORD17 +#define PPP_ADDR_PHASEY_STEP MDP_FULL_BYPASS_WORD18 +#define PPP_ADDR_ALPHA_TRANSP MDP_FULL_BYPASS_WORD19 +#define PPP_ADDR_DST_CFG MDP_FULL_BYPASS_WORD20 +#define PPP_ADDR_DST_PACK_PATTERN MDP_FULL_BYPASS_WORD21 +#define PPP_ADDR_DST_ROI MDP_FULL_BYPASS_WORD25 +#define PPP_ADDR_DST0 MDP_FULL_BYPASS_WORD26 +#define PPP_ADDR_DST1 MDP_FULL_BYPASS_WORD27 +#define PPP_ADDR_DST_YSTRIDE MDP_FULL_BYPASS_WORD30 +#define PPP_ADDR_EDGE MDP_FULL_BYPASS_WORD46 +#define PPP_ADDR_BG0 MDP_FULL_BYPASS_WORD48 +#define PPP_ADDR_BG1 MDP_FULL_BYPASS_WORD49 +#define PPP_ADDR_BG_YSTRIDE MDP_FULL_BYPASS_WORD51 +#define PPP_ADDR_BG_CFG MDP_FULL_BYPASS_WORD53 +#define PPP_ADDR_BG_PACK_PATTERN MDP_FULL_BYPASS_WORD54 + +/* MDP_DMA_CONFIG / MDP_FULL_BYPASS_WORD32 */ +#define DMA_DSTC0G_6BITS (1<<1) +#define DMA_DSTC1B_6BITS (1<<3) +#define DMA_DSTC2R_6BITS (1<<5) +#define DMA_DSTC0G_5BITS (1<<0) +#define DMA_DSTC1B_5BITS (1<<2) +#define DMA_DSTC2R_5BITS (1<<4) + +#define DMA_PACK_TIGHT (1<<6) +#define DMA_PACK_LOOSE 0 +#define DMA_PACK_ALIGN_LSB 0 +#define DMA_PACK_ALIGN_MSB (1<<7) +#define DMA_PACK_PATTERN_RGB \ + (MDP_GET_PACK_PATTERN(0, CLR_R, CLR_G, CLR_B, 2)<<8) + +#define DMA_OUT_SEL_AHB 0 +#define DMA_OUT_SEL_MDDI (1<<14) +#define DMA_AHBM_LCD_SEL_PRIMARY 0 +#define DMA_AHBM_LCD_SEL_SECONDARY (1<<15) +#define DMA_IBUF_C3ALPHA_EN (1<<16) +#define DMA_DITHER_EN (1<<17) + +#define DMA_MDDI_DMAOUT_LCD_SEL_PRIMARY 0 +#define DMA_MDDI_DMAOUT_LCD_SEL_SECONDARY (1<<18) +#define DMA_MDDI_DMAOUT_LCD_SEL_EXTERNAL (1<<19) + +#define DMA_IBUF_FORMAT_RGB565 (1<<20) +#define DMA_IBUF_FORMAT_RGB888_OR_ARGB8888 0 + +#define DMA_IBUF_NONCONTIGUOUS (1<<21) + +/* MDDI REGISTER ? */ +#define MDDI_VDO_PACKET_DESC 0x5666 +#define MDDI_VDO_PACKET_PRIM 0xC3 +#define MDDI_VDO_PACKET_SECD 0xC0 + +#endif diff --git a/drivers/video/msm/mdp_ppp.c b/drivers/video/msm/mdp_ppp.c new file mode 100644 index 000000000000..4ff001f4cbbd --- /dev/null +++ b/drivers/video/msm/mdp_ppp.c @@ -0,0 +1,730 @@ +/* drivers/video/msm/mdp_ppp.c + * + * Copyright (C) 2007 QUALCOMM Incorporated + * Copyright (C) 2007 Google Incorporated + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#include <linux/fb.h> +#include <linux/file.h> +#include <linux/delay.h> +#include <linux/msm_mdp.h> +#include <mach/msm_fb.h> + +#include "mdp_hw.h" +#include "mdp_scale_tables.h" + +#define DLOG(x...) do {} while (0) + +#define MDP_DOWNSCALE_BLUR (MDP_DOWNSCALE_MAX + 1) +static int downscale_y_table = MDP_DOWNSCALE_MAX; +static int downscale_x_table = MDP_DOWNSCALE_MAX; + +struct mdp_regs { + uint32_t src0; + uint32_t src1; + uint32_t dst0; + uint32_t dst1; + uint32_t src_cfg; + uint32_t dst_cfg; + uint32_t src_pack; + uint32_t dst_pack; + uint32_t src_rect; + uint32_t dst_rect; + uint32_t src_ystride; + uint32_t dst_ystride; + uint32_t op; + uint32_t src_bpp; + uint32_t dst_bpp; + uint32_t edge; + uint32_t phasex_init; + uint32_t phasey_init; + uint32_t phasex_step; + uint32_t phasey_step; +}; + +static uint32_t pack_pattern[] = { + PPP_ARRAY0(PACK_PATTERN) +}; + +static uint32_t src_img_cfg[] = { + PPP_ARRAY1(CFG, SRC) +}; + +static uint32_t dst_img_cfg[] = { + PPP_ARRAY1(CFG, DST) +}; + +static uint32_t bytes_per_pixel[] = { + [MDP_RGB_565] = 2, + [MDP_RGB_888] = 3, + [MDP_XRGB_8888] = 4, + [MDP_ARGB_8888] = 4, + [MDP_RGBA_8888] = 4, + [MDP_BGRA_8888] = 4, + [MDP_Y_CBCR_H2V1] = 1, + [MDP_Y_CBCR_H2V2] = 1, + [MDP_Y_CRCB_H2V1] = 1, + [MDP_Y_CRCB_H2V2] = 1, + [MDP_YCRYCB_H2V1] = 2 +}; + +static uint32_t dst_op_chroma[] = { + PPP_ARRAY1(CHROMA_SAMP, DST) +}; + +static uint32_t src_op_chroma[] = { + PPP_ARRAY1(CHROMA_SAMP, SRC) +}; + +static uint32_t bg_op_chroma[] = { + PPP_ARRAY1(CHROMA_SAMP, BG) +}; + +static void rotate_dst_addr_x(struct mdp_blit_req *req, struct mdp_regs *regs) +{ + regs->dst0 += (req->dst_rect.w - + min((uint32_t)16, req->dst_rect.w)) * regs->dst_bpp; + regs->dst1 += (req->dst_rect.w - + min((uint32_t)16, req->dst_rect.w)) * regs->dst_bpp; +} + +static void rotate_dst_addr_y(struct mdp_blit_req *req, struct mdp_regs *regs) +{ + regs->dst0 += (req->dst_rect.h - + min((uint32_t)16, req->dst_rect.h)) * + regs->dst_ystride; + regs->dst1 += (req->dst_rect.h - + min((uint32_t)16, req->dst_rect.h)) * + regs->dst_ystride; +} + +static void blit_rotate(struct mdp_blit_req *req, + struct mdp_regs *regs) +{ + if (req->flags == MDP_ROT_NOP) + return; + + regs->op |= PPP_OP_ROT_ON; + if ((req->flags & MDP_ROT_90 || req->flags & MDP_FLIP_LR) && + !(req->flags & MDP_ROT_90 && req->flags & MDP_FLIP_LR)) + rotate_dst_addr_x(req, regs); + if (req->flags & MDP_ROT_90) + regs->op |= PPP_OP_ROT_90; + if (req->flags & MDP_FLIP_UD) { + regs->op |= PPP_OP_FLIP_UD; + rotate_dst_addr_y(req, regs); + } + if (req->flags & MDP_FLIP_LR) + regs->op |= PPP_OP_FLIP_LR; +} + +static void blit_convert(struct mdp_blit_req *req, struct mdp_regs *regs) +{ + if (req->src.format == req->dst.format) + return; + if (IS_RGB(req->src.format) && IS_YCRCB(req->dst.format)) { + regs->op |= PPP_OP_CONVERT_RGB2YCBCR | PPP_OP_CONVERT_ON; + } else if (IS_YCRCB(req->src.format) && IS_RGB(req->dst.format)) { + regs->op |= PPP_OP_CONVERT_YCBCR2RGB | PPP_OP_CONVERT_ON; + if (req->dst.format == MDP_RGB_565) + regs->op |= PPP_OP_CONVERT_MATRIX_SECONDARY; + } +} + +#define GET_BIT_RANGE(value, high, low) \ + (((1 << (high - low + 1)) - 1) & (value >> low)) +static uint32_t transp_convert(struct mdp_blit_req *req) +{ + uint32_t transp = 0; + if (req->src.format == MDP_RGB_565) { + /* pad each value to 8 bits by copying the high bits into the + * low end, convert RGB to RBG by switching low 2 components */ + transp |= ((GET_BIT_RANGE(req->transp_mask, 15, 11) << 3) | + (GET_BIT_RANGE(req->transp_mask, 15, 13))) << 16; + + transp |= ((GET_BIT_RANGE(req->transp_mask, 4, 0) << 3) | + (GET_BIT_RANGE(req->transp_mask, 4, 2))) << 8; + + transp |= (GET_BIT_RANGE(req->transp_mask, 10, 5) << 2) | + (GET_BIT_RANGE(req->transp_mask, 10, 9)); + } else { + /* convert RGB to RBG */ + transp |= (GET_BIT_RANGE(req->transp_mask, 15, 8)) | + (GET_BIT_RANGE(req->transp_mask, 23, 16) << 16) | + (GET_BIT_RANGE(req->transp_mask, 7, 0) << 8); + } + return transp; +} +#undef GET_BIT_RANGE + +static void blit_blend(struct mdp_blit_req *req, struct mdp_regs *regs) +{ + /* TRANSP BLEND */ + if (req->transp_mask != MDP_TRANSP_NOP) { + req->transp_mask = transp_convert(req); + if (req->alpha != MDP_ALPHA_NOP) { + /* use blended transparancy mode + * pixel = (src == transp) ? dst : blend + * blend is combo of blend_eq_sel and + * blend_alpha_sel */ + regs->op |= PPP_OP_ROT_ON | PPP_OP_BLEND_ON | + PPP_OP_BLEND_ALPHA_BLEND_NORMAL | + PPP_OP_BLEND_CONSTANT_ALPHA | + PPP_BLEND_ALPHA_TRANSP; + } else { + /* simple transparancy mode + * pixel = (src == transp) ? dst : src */ + regs->op |= PPP_OP_ROT_ON | PPP_OP_BLEND_ON | + PPP_OP_BLEND_SRCPIXEL_TRANSP; + } + } + + req->alpha &= 0xff; + /* ALPHA BLEND */ + if (HAS_ALPHA(req->src.format)) { + regs->op |= PPP_OP_ROT_ON | PPP_OP_BLEND_ON | + PPP_OP_BLEND_SRCPIXEL_ALPHA; + } else if (req->alpha < MDP_ALPHA_NOP) { + /* just blend by alpha */ + regs->op |= PPP_OP_ROT_ON | PPP_OP_BLEND_ON | + PPP_OP_BLEND_ALPHA_BLEND_NORMAL | + PPP_OP_BLEND_CONSTANT_ALPHA; + } + + regs->op |= bg_op_chroma[req->dst.format]; +} + +#define ONE_HALF (1LL << 32) +#define ONE (1LL << 33) +#define TWO (2LL << 33) +#define THREE (3LL << 33) +#define FRAC_MASK (ONE - 1) +#define INT_MASK (~FRAC_MASK) + +static int scale_params(uint32_t dim_in, uint32_t dim_out, uint32_t origin, + uint32_t *phase_init, uint32_t *phase_step) +{ + /* to improve precicsion calculations are done in U31.33 and converted + * to U3.29 at the end */ + int64_t k1, k2, k3, k4, tmp; + uint64_t n, d, os, os_p, od, od_p, oreq; + unsigned rpa = 0; + int64_t ip64, delta; + + if (dim_out % 3 == 0) + rpa = !(dim_in % (dim_out / 3)); + + n = ((uint64_t)dim_out) << 34; + d = dim_in; + if (!d) + return -1; + do_div(n, d); + k3 = (n + 1) >> 1; + if ((k3 >> 4) < (1LL << 27) || (k3 >> 4) > (1LL << 31)) { + DLOG("crap bad scale\n"); + return -1; + } + n = ((uint64_t)dim_in) << 34; + d = (uint64_t)dim_out; + if (!d) + return -1; + do_div(n, d); + k1 = (n + 1) >> 1; + k2 = (k1 - ONE) >> 1; + + *phase_init = (int)(k2 >> 4); + k4 = (k3 - ONE) >> 1; + + if (rpa) { + os = ((uint64_t)origin << 33) - ONE_HALF; + tmp = (dim_out * os) + ONE_HALF; + if (!dim_in) + return -1; + do_div(tmp, dim_in); + od = tmp - ONE_HALF; + } else { + os = ((uint64_t)origin << 1) - 1; + od = (((k3 * os) >> 1) + k4); + } + + od_p = od & INT_MASK; + if (od_p != od) + od_p += ONE; + + if (rpa) { + tmp = (dim_in * od_p) + ONE_HALF; + if (!dim_in) + return -1; + do_div(tmp, dim_in); + os_p = tmp - ONE_HALF; + } else { + os_p = ((k1 * (od_p >> 33)) + k2); + } + + oreq = (os_p & INT_MASK) - ONE; + + ip64 = os_p - oreq; + delta = ((int64_t)(origin) << 33) - oreq; + ip64 -= delta; + /* limit to valid range before the left shift */ + delta = (ip64 & (1LL << 63)) ? 4 : -4; + delta <<= 33; + while (abs((int)(ip64 >> 33)) > 4) + ip64 += delta; + *phase_init = (int)(ip64 >> 4); + *phase_step = (uint32_t)(k1 >> 4); + return 0; +} + +static void load_scale_table(const struct mdp_info *mdp, + struct mdp_table_entry *table, int len) +{ + int i; + for (i = 0; i < len; i++) + mdp_writel(mdp, table[i].val, table[i].reg); +} + +enum { +IMG_LEFT, +IMG_RIGHT, +IMG_TOP, +IMG_BOTTOM, +}; + +static void get_edge_info(uint32_t src, uint32_t src_coord, uint32_t dst, + uint32_t *interp1, uint32_t *interp2, + uint32_t *repeat1, uint32_t *repeat2) { + if (src > 3 * dst) { + *interp1 = 0; + *interp2 = src - 1; + *repeat1 = 0; + *repeat2 = 0; + } else if (src == 3 * dst) { + *interp1 = 0; + *interp2 = src; + *repeat1 = 0; + *repeat2 = 1; + } else if (src > dst && src < 3 * dst) { + *interp1 = -1; + *interp2 = src; + *repeat1 = 1; + *repeat2 = 1; + } else if (src == dst) { + *interp1 = -1; + *interp2 = src + 1; + *repeat1 = 1; + *repeat2 = 2; + } else { + *interp1 = -2; + *interp2 = src + 1; + *repeat1 = 2; + *repeat2 = 2; + } + *interp1 += src_coord; + *interp2 += src_coord; +} + +static int get_edge_cond(struct mdp_blit_req *req, struct mdp_regs *regs) +{ + int32_t luma_interp[4]; + int32_t luma_repeat[4]; + int32_t chroma_interp[4]; + int32_t chroma_bound[4]; + int32_t chroma_repeat[4]; + uint32_t dst_w, dst_h; + + memset(&luma_interp, 0, sizeof(int32_t) * 4); + memset(&luma_repeat, 0, sizeof(int32_t) * 4); + memset(&chroma_interp, 0, sizeof(int32_t) * 4); + memset(&chroma_bound, 0, sizeof(int32_t) * 4); + memset(&chroma_repeat, 0, sizeof(int32_t) * 4); + regs->edge = 0; + + if (req->flags & MDP_ROT_90) { + dst_w = req->dst_rect.h; + dst_h = req->dst_rect.w; + } else { + dst_w = req->dst_rect.w; + dst_h = req->dst_rect.h; + } + + if (regs->op & (PPP_OP_SCALE_Y_ON | PPP_OP_SCALE_X_ON)) { + get_edge_info(req->src_rect.h, req->src_rect.y, dst_h, + &luma_interp[IMG_TOP], &luma_interp[IMG_BOTTOM], + &luma_repeat[IMG_TOP], &luma_repeat[IMG_BOTTOM]); + get_edge_info(req->src_rect.w, req->src_rect.x, dst_w, + &luma_interp[IMG_LEFT], &luma_interp[IMG_RIGHT], + &luma_repeat[IMG_LEFT], &luma_repeat[IMG_RIGHT]); + } else { + luma_interp[IMG_LEFT] = req->src_rect.x; + luma_interp[IMG_RIGHT] = req->src_rect.x + req->src_rect.w - 1; + luma_interp[IMG_TOP] = req->src_rect.y; + luma_interp[IMG_BOTTOM] = req->src_rect.y + req->src_rect.h - 1; + luma_repeat[IMG_LEFT] = 0; + luma_repeat[IMG_TOP] = 0; + luma_repeat[IMG_RIGHT] = 0; + luma_repeat[IMG_BOTTOM] = 0; + } + + chroma_interp[IMG_LEFT] = luma_interp[IMG_LEFT]; + chroma_interp[IMG_RIGHT] = luma_interp[IMG_RIGHT]; + chroma_interp[IMG_TOP] = luma_interp[IMG_TOP]; + chroma_interp[IMG_BOTTOM] = luma_interp[IMG_BOTTOM]; + + chroma_bound[IMG_LEFT] = req->src_rect.x; + chroma_bound[IMG_RIGHT] = req->src_rect.x + req->src_rect.w - 1; + chroma_bound[IMG_TOP] = req->src_rect.y; + chroma_bound[IMG_BOTTOM] = req->src_rect.y + req->src_rect.h - 1; + + if (IS_YCRCB(req->src.format)) { + chroma_interp[IMG_LEFT] = chroma_interp[IMG_LEFT] >> 1; + chroma_interp[IMG_RIGHT] = (chroma_interp[IMG_RIGHT] + 1) >> 1; + + chroma_bound[IMG_LEFT] = chroma_bound[IMG_LEFT] >> 1; + chroma_bound[IMG_RIGHT] = chroma_bound[IMG_RIGHT] >> 1; + } + + if (req->src.format == MDP_Y_CBCR_H2V2 || + req->src.format == MDP_Y_CRCB_H2V2) { + chroma_interp[IMG_TOP] = (chroma_interp[IMG_TOP] - 1) >> 1; + chroma_interp[IMG_BOTTOM] = (chroma_interp[IMG_BOTTOM] + 1) + >> 1; + chroma_bound[IMG_TOP] = (chroma_bound[IMG_TOP] + 1) >> 1; + chroma_bound[IMG_BOTTOM] = chroma_bound[IMG_BOTTOM] >> 1; + } + + chroma_repeat[IMG_LEFT] = chroma_bound[IMG_LEFT] - + chroma_interp[IMG_LEFT]; + chroma_repeat[IMG_RIGHT] = chroma_interp[IMG_RIGHT] - + chroma_bound[IMG_RIGHT]; + chroma_repeat[IMG_TOP] = chroma_bound[IMG_TOP] - + chroma_interp[IMG_TOP]; + chroma_repeat[IMG_BOTTOM] = chroma_interp[IMG_BOTTOM] - + chroma_bound[IMG_BOTTOM]; + + if (chroma_repeat[IMG_LEFT] < 0 || chroma_repeat[IMG_LEFT] > 3 || + chroma_repeat[IMG_RIGHT] < 0 || chroma_repeat[IMG_RIGHT] > 3 || + chroma_repeat[IMG_TOP] < 0 || chroma_repeat[IMG_TOP] > 3 || + chroma_repeat[IMG_BOTTOM] < 0 || chroma_repeat[IMG_BOTTOM] > 3 || + luma_repeat[IMG_LEFT] < 0 || luma_repeat[IMG_LEFT] > 3 || + luma_repeat[IMG_RIGHT] < 0 || luma_repeat[IMG_RIGHT] > 3 || + luma_repeat[IMG_TOP] < 0 || luma_repeat[IMG_TOP] > 3 || + luma_repeat[IMG_BOTTOM] < 0 || luma_repeat[IMG_BOTTOM] > 3) + return -1; + + regs->edge |= (chroma_repeat[IMG_LEFT] & 3) << MDP_LEFT_CHROMA; + regs->edge |= (chroma_repeat[IMG_RIGHT] & 3) << MDP_RIGHT_CHROMA; + regs->edge |= (chroma_repeat[IMG_TOP] & 3) << MDP_TOP_CHROMA; + regs->edge |= (chroma_repeat[IMG_BOTTOM] & 3) << MDP_BOTTOM_CHROMA; + regs->edge |= (luma_repeat[IMG_LEFT] & 3) << MDP_LEFT_LUMA; + regs->edge |= (luma_repeat[IMG_RIGHT] & 3) << MDP_RIGHT_LUMA; + regs->edge |= (luma_repeat[IMG_TOP] & 3) << MDP_TOP_LUMA; + regs->edge |= (luma_repeat[IMG_BOTTOM] & 3) << MDP_BOTTOM_LUMA; + return 0; +} + +static int blit_scale(const struct mdp_info *mdp, struct mdp_blit_req *req, + struct mdp_regs *regs) +{ + uint32_t phase_init_x, phase_init_y, phase_step_x, phase_step_y; + uint32_t scale_factor_x, scale_factor_y; + uint32_t downscale; + uint32_t dst_w, dst_h; + + if (req->flags & MDP_ROT_90) { + dst_w = req->dst_rect.h; + dst_h = req->dst_rect.w; + } else { + dst_w = req->dst_rect.w; + dst_h = req->dst_rect.h; + } + if ((req->src_rect.w == dst_w) && (req->src_rect.h == dst_h) && + !(req->flags & MDP_BLUR)) { + regs->phasex_init = 0; + regs->phasey_init = 0; + regs->phasex_step = 0; + regs->phasey_step = 0; + return 0; + } + + if (scale_params(req->src_rect.w, dst_w, 1, &phase_init_x, + &phase_step_x) || + scale_params(req->src_rect.h, dst_h, 1, &phase_init_y, + &phase_step_y)) + return -1; + + scale_factor_x = (dst_w * 10) / req->src_rect.w; + scale_factor_y = (dst_h * 10) / req->src_rect.h; + + if (scale_factor_x > 8) + downscale = MDP_DOWNSCALE_PT8TO1; + else if (scale_factor_x > 6) + downscale = MDP_DOWNSCALE_PT6TOPT8; + else if (scale_factor_x > 4) + downscale = MDP_DOWNSCALE_PT4TOPT6; + else + downscale = MDP_DOWNSCALE_PT2TOPT4; + if (downscale != downscale_x_table) { + load_scale_table(mdp, mdp_downscale_x_table[downscale], 64); + downscale_x_table = downscale; + } + + if (scale_factor_y > 8) + downscale = MDP_DOWNSCALE_PT8TO1; + else if (scale_factor_y > 6) + downscale = MDP_DOWNSCALE_PT6TOPT8; + else if (scale_factor_y > 4) + downscale = MDP_DOWNSCALE_PT4TOPT6; + else + downscale = MDP_DOWNSCALE_PT2TOPT4; + if (downscale != downscale_y_table) { + load_scale_table(mdp, mdp_downscale_y_table[downscale], 64); + downscale_y_table = downscale; + } + + regs->phasex_init = phase_init_x; + regs->phasey_init = phase_init_y; + regs->phasex_step = phase_step_x; + regs->phasey_step = phase_step_y; + regs->op |= (PPP_OP_SCALE_Y_ON | PPP_OP_SCALE_X_ON); + return 0; + +} + +static void blit_blur(const struct mdp_info *mdp, struct mdp_blit_req *req, + struct mdp_regs *regs) +{ + if (!(req->flags & MDP_BLUR)) + return; + + if (!(downscale_x_table == MDP_DOWNSCALE_BLUR && + downscale_y_table == MDP_DOWNSCALE_BLUR)) { + load_scale_table(mdp, mdp_gaussian_blur_table, 128); + downscale_x_table = MDP_DOWNSCALE_BLUR; + downscale_y_table = MDP_DOWNSCALE_BLUR; + } + + regs->op |= (PPP_OP_SCALE_Y_ON | PPP_OP_SCALE_X_ON); +} + + +#define IMG_LEN(rect_h, w, rect_w, bpp) (((rect_h) * w) * bpp) + +#define Y_TO_CRCB_RATIO(format) \ + ((format == MDP_Y_CBCR_H2V2 || format == MDP_Y_CRCB_H2V2) ? 2 :\ + (format == MDP_Y_CBCR_H2V1 || format == MDP_Y_CRCB_H2V1) ? 1 : 1) + +static void get_len(struct mdp_img *img, struct mdp_rect *rect, uint32_t bpp, + uint32_t *len0, uint32_t *len1) +{ + *len0 = IMG_LEN(rect->h, img->width, rect->w, bpp); + if (IS_PSEUDOPLNR(img->format)) + *len1 = *len0/Y_TO_CRCB_RATIO(img->format); + else + *len1 = 0; +} + +static int valid_src_dst(unsigned long src_start, unsigned long src_len, + unsigned long dst_start, unsigned long dst_len, + struct mdp_blit_req *req, struct mdp_regs *regs) +{ + unsigned long src_min_ok = src_start; + unsigned long src_max_ok = src_start + src_len; + unsigned long dst_min_ok = dst_start; + unsigned long dst_max_ok = dst_start + dst_len; + uint32_t src0_len, src1_len, dst0_len, dst1_len; + get_len(&req->src, &req->src_rect, regs->src_bpp, &src0_len, + &src1_len); + get_len(&req->dst, &req->dst_rect, regs->dst_bpp, &dst0_len, + &dst1_len); + + if (regs->src0 < src_min_ok || regs->src0 > src_max_ok || + regs->src0 + src0_len > src_max_ok) { + DLOG("invalid_src %x %x %lx %lx\n", regs->src0, + src0_len, src_min_ok, src_max_ok); + return 0; + } + if (regs->src_cfg & PPP_SRC_PLANE_PSEUDOPLNR) { + if (regs->src1 < src_min_ok || regs->src1 > src_max_ok || + regs->src1 + src1_len > src_max_ok) { + DLOG("invalid_src1"); + return 0; + } + } + if (regs->dst0 < dst_min_ok || regs->dst0 > dst_max_ok || + regs->dst0 + dst0_len > dst_max_ok) { + DLOG("invalid_dst"); + return 0; + } + if (regs->dst_cfg & PPP_SRC_PLANE_PSEUDOPLNR) { + if (regs->dst1 < dst_min_ok || regs->dst1 > dst_max_ok || + regs->dst1 + dst1_len > dst_max_ok) { + DLOG("invalid_dst1"); + return 0; + } + } + return 1; +} + + +static void flush_imgs(struct mdp_blit_req *req, struct mdp_regs *regs, + struct file *src_file, struct file *dst_file) +{ +} + +static void get_chroma_addr(struct mdp_img *img, struct mdp_rect *rect, + uint32_t base, uint32_t bpp, uint32_t cfg, + uint32_t *addr, uint32_t *ystride) +{ + uint32_t compress_v = Y_TO_CRCB_RATIO(img->format); + uint32_t compress_h = 2; + uint32_t offset; + + if (IS_PSEUDOPLNR(img->format)) { + offset = (rect->x / compress_h) * compress_h; + offset += rect->y == 0 ? 0 : + ((rect->y + 1) / compress_v) * img->width; + *addr = base + (img->width * img->height * bpp); + *addr += offset * bpp; + *ystride |= *ystride << 16; + } else { + *addr = 0; + } +} + +static int send_blit(const struct mdp_info *mdp, struct mdp_blit_req *req, + struct mdp_regs *regs, struct file *src_file, + struct file *dst_file) +{ + mdp_writel(mdp, 1, 0x060); + mdp_writel(mdp, regs->src_rect, PPP_ADDR_SRC_ROI); + mdp_writel(mdp, regs->src0, PPP_ADDR_SRC0); + mdp_writel(mdp, regs->src1, PPP_ADDR_SRC1); + mdp_writel(mdp, regs->src_ystride, PPP_ADDR_SRC_YSTRIDE); + mdp_writel(mdp, regs->src_cfg, PPP_ADDR_SRC_CFG); + mdp_writel(mdp, regs->src_pack, PPP_ADDR_SRC_PACK_PATTERN); + + mdp_writel(mdp, regs->op, PPP_ADDR_OPERATION); + mdp_writel(mdp, regs->phasex_init, PPP_ADDR_PHASEX_INIT); + mdp_writel(mdp, regs->phasey_init, PPP_ADDR_PHASEY_INIT); + mdp_writel(mdp, regs->phasex_step, PPP_ADDR_PHASEX_STEP); + mdp_writel(mdp, regs->phasey_step, PPP_ADDR_PHASEY_STEP); + + mdp_writel(mdp, (req->alpha << 24) | (req->transp_mask & 0xffffff), + PPP_ADDR_ALPHA_TRANSP); + + mdp_writel(mdp, regs->dst_cfg, PPP_ADDR_DST_CFG); + mdp_writel(mdp, regs->dst_pack, PPP_ADDR_DST_PACK_PATTERN); + mdp_writel(mdp, regs->dst_rect, PPP_ADDR_DST_ROI); + mdp_writel(mdp, regs->dst0, PPP_ADDR_DST0); + mdp_writel(mdp, regs->dst1, PPP_ADDR_DST1); + mdp_writel(mdp, regs->dst_ystride, PPP_ADDR_DST_YSTRIDE); + + mdp_writel(mdp, regs->edge, PPP_ADDR_EDGE); + if (regs->op & PPP_OP_BLEND_ON) { + mdp_writel(mdp, regs->dst0, PPP_ADDR_BG0); + mdp_writel(mdp, regs->dst1, PPP_ADDR_BG1); + mdp_writel(mdp, regs->dst_ystride, PPP_ADDR_BG_YSTRIDE); + mdp_writel(mdp, src_img_cfg[req->dst.format], PPP_ADDR_BG_CFG); + mdp_writel(mdp, pack_pattern[req->dst.format], + PPP_ADDR_BG_PACK_PATTERN); + } + flush_imgs(req, regs, src_file, dst_file); + mdp_writel(mdp, 0x1000, MDP_DISPLAY0_START); + return 0; +} + +int mdp_ppp_blit(const struct mdp_info *mdp, struct mdp_blit_req *req, + struct file *src_file, unsigned long src_start, unsigned long src_len, + struct file *dst_file, unsigned long dst_start, unsigned long dst_len) +{ + struct mdp_regs regs = {0}; + + if (unlikely(req->src.format >= MDP_IMGTYPE_LIMIT || + req->dst.format >= MDP_IMGTYPE_LIMIT)) { + printk(KERN_ERR "mpd_ppp: img is of wrong format\n"); + return -EINVAL; + } + + if (unlikely(req->src_rect.x > req->src.width || + req->src_rect.y > req->src.height || + req->dst_rect.x > req->dst.width || + req->dst_rect.y > req->dst.height)) { + printk(KERN_ERR "mpd_ppp: img rect is outside of img!\n"); + return -EINVAL; + } + + /* set the src image configuration */ + regs.src_cfg = src_img_cfg[req->src.format]; + regs.src_cfg |= (req->src_rect.x & 0x1) ? PPP_SRC_BPP_ROI_ODD_X : 0; + regs.src_cfg |= (req->src_rect.y & 0x1) ? PPP_SRC_BPP_ROI_ODD_Y : 0; + regs.src_rect = (req->src_rect.h << 16) | req->src_rect.w; + regs.src_pack = pack_pattern[req->src.format]; + + /* set the dest image configuration */ + regs.dst_cfg = dst_img_cfg[req->dst.format] | PPP_DST_OUT_SEL_AXI; + regs.dst_rect = (req->dst_rect.h << 16) | req->dst_rect.w; + regs.dst_pack = pack_pattern[req->dst.format]; + + /* set src, bpp, start pixel and ystride */ + regs.src_bpp = bytes_per_pixel[req->src.format]; + regs.src0 = src_start + req->src.offset; + regs.src_ystride = req->src.width * regs.src_bpp; + get_chroma_addr(&req->src, &req->src_rect, regs.src0, regs.src_bpp, + regs.src_cfg, ®s.src1, ®s.src_ystride); + regs.src0 += (req->src_rect.x + (req->src_rect.y * req->src.width)) * + regs.src_bpp; + + /* set dst, bpp, start pixel and ystride */ + regs.dst_bpp = bytes_per_pixel[req->dst.format]; + regs.dst0 = dst_start + req->dst.offset; + regs.dst_ystride = req->dst.width * regs.dst_bpp; + get_chroma_addr(&req->dst, &req->dst_rect, regs.dst0, regs.dst_bpp, + regs.dst_cfg, ®s.dst1, ®s.dst_ystride); + regs.dst0 += (req->dst_rect.x + (req->dst_rect.y * req->dst.width)) * + regs.dst_bpp; + + if (!valid_src_dst(src_start, src_len, dst_start, dst_len, req, + ®s)) { + printk(KERN_ERR "mpd_ppp: final src or dst location is " + "invalid, are you trying to make an image too large " + "or to place it outside the screen?\n"); + return -EINVAL; + } + + /* set up operation register */ + regs.op = 0; + blit_rotate(req, ®s); + blit_convert(req, ®s); + if (req->flags & MDP_DITHER) + regs.op |= PPP_OP_DITHER_EN; + blit_blend(req, ®s); + if (blit_scale(mdp, req, ®s)) { + printk(KERN_ERR "mpd_ppp: error computing scale for img.\n"); + return -EINVAL; + } + blit_blur(mdp, req, ®s); + regs.op |= dst_op_chroma[req->dst.format] | + src_op_chroma[req->src.format]; + + /* if the image is YCRYCB, the x and w must be even */ + if (unlikely(req->src.format == MDP_YCRYCB_H2V1)) { + req->src_rect.x = req->src_rect.x & (~0x1); + req->src_rect.w = req->src_rect.w & (~0x1); + req->dst_rect.x = req->dst_rect.x & (~0x1); + req->dst_rect.w = req->dst_rect.w & (~0x1); + } + if (get_edge_cond(req, ®s)) + return -EINVAL; + + send_blit(mdp, req, ®s, src_file, dst_file); + return 0; +} diff --git a/drivers/video/msm/mdp_scale_tables.c b/drivers/video/msm/mdp_scale_tables.c new file mode 100644 index 000000000000..604783b2e17c --- /dev/null +++ b/drivers/video/msm/mdp_scale_tables.c @@ -0,0 +1,766 @@ +/* drivers/video/msm_fb/mdp_scale_tables.c + * + * Copyright (C) 2007 QUALCOMM Incorporated + * Copyright (C) 2007 Google Incorporated + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "mdp_scale_tables.h" +#include "mdp_hw.h" + +struct mdp_table_entry mdp_upscale_table[] = { + { 0x5fffc, 0x0 }, + { 0x50200, 0x7fc00000 }, + { 0x5fffc, 0xff80000d }, + { 0x50204, 0x7ec003f9 }, + { 0x5fffc, 0xfec0001c }, + { 0x50208, 0x7d4003f3 }, + { 0x5fffc, 0xfe40002b }, + { 0x5020c, 0x7b8003ed }, + { 0x5fffc, 0xfd80003c }, + { 0x50210, 0x794003e8 }, + { 0x5fffc, 0xfcc0004d }, + { 0x50214, 0x76c003e4 }, + { 0x5fffc, 0xfc40005f }, + { 0x50218, 0x73c003e0 }, + { 0x5fffc, 0xfb800071 }, + { 0x5021c, 0x708003de }, + { 0x5fffc, 0xfac00085 }, + { 0x50220, 0x6d0003db }, + { 0x5fffc, 0xfa000098 }, + { 0x50224, 0x698003d9 }, + { 0x5fffc, 0xf98000ac }, + { 0x50228, 0x654003d8 }, + { 0x5fffc, 0xf8c000c1 }, + { 0x5022c, 0x610003d7 }, + { 0x5fffc, 0xf84000d5 }, + { 0x50230, 0x5c8003d7 }, + { 0x5fffc, 0xf7c000e9 }, + { 0x50234, 0x580003d7 }, + { 0x5fffc, 0xf74000fd }, + { 0x50238, 0x534003d8 }, + { 0x5fffc, 0xf6c00112 }, + { 0x5023c, 0x4e8003d8 }, + { 0x5fffc, 0xf6800126 }, + { 0x50240, 0x494003da }, + { 0x5fffc, 0xf600013a }, + { 0x50244, 0x448003db }, + { 0x5fffc, 0xf600014d }, + { 0x50248, 0x3f4003dd }, + { 0x5fffc, 0xf5c00160 }, + { 0x5024c, 0x3a4003df }, + { 0x5fffc, 0xf5c00172 }, + { 0x50250, 0x354003e1 }, + { 0x5fffc, 0xf5c00184 }, + { 0x50254, 0x304003e3 }, + { 0x5fffc, 0xf6000195 }, + { 0x50258, 0x2b0003e6 }, + { 0x5fffc, 0xf64001a6 }, + { 0x5025c, 0x260003e8 }, + { 0x5fffc, 0xf6c001b4 }, + { 0x50260, 0x214003eb }, + { 0x5fffc, 0xf78001c2 }, + { 0x50264, 0x1c4003ee }, + { 0x5fffc, 0xf80001cf }, + { 0x50268, 0x17c003f1 }, + { 0x5fffc, 0xf90001db }, + { 0x5026c, 0x134003f3 }, + { 0x5fffc, 0xfa0001e5 }, + { 0x50270, 0xf0003f6 }, + { 0x5fffc, 0xfb4001ee }, + { 0x50274, 0xac003f9 }, + { 0x5fffc, 0xfcc001f5 }, + { 0x50278, 0x70003fb }, + { 0x5fffc, 0xfe4001fb }, + { 0x5027c, 0x34003fe }, +}; + +static struct mdp_table_entry mdp_downscale_x_table_PT2TOPT4[] = { + { 0x5fffc, 0x740008c }, + { 0x50280, 0x33800088 }, + { 0x5fffc, 0x800008e }, + { 0x50284, 0x33400084 }, + { 0x5fffc, 0x8400092 }, + { 0x50288, 0x33000080 }, + { 0x5fffc, 0x9000094 }, + { 0x5028c, 0x3300007b }, + { 0x5fffc, 0x9c00098 }, + { 0x50290, 0x32400077 }, + { 0x5fffc, 0xa40009b }, + { 0x50294, 0x32000073 }, + { 0x5fffc, 0xb00009d }, + { 0x50298, 0x31c0006f }, + { 0x5fffc, 0xbc000a0 }, + { 0x5029c, 0x3140006b }, + { 0x5fffc, 0xc8000a2 }, + { 0x502a0, 0x31000067 }, + { 0x5fffc, 0xd8000a5 }, + { 0x502a4, 0x30800062 }, + { 0x5fffc, 0xe4000a8 }, + { 0x502a8, 0x2fc0005f }, + { 0x5fffc, 0xec000aa }, + { 0x502ac, 0x2fc0005b }, + { 0x5fffc, 0xf8000ad }, + { 0x502b0, 0x2f400057 }, + { 0x5fffc, 0x108000b0 }, + { 0x502b4, 0x2e400054 }, + { 0x5fffc, 0x114000b2 }, + { 0x502b8, 0x2e000050 }, + { 0x5fffc, 0x124000b4 }, + { 0x502bc, 0x2d80004c }, + { 0x5fffc, 0x130000b6 }, + { 0x502c0, 0x2d000049 }, + { 0x5fffc, 0x140000b8 }, + { 0x502c4, 0x2c800045 }, + { 0x5fffc, 0x150000b9 }, + { 0x502c8, 0x2c000042 }, + { 0x5fffc, 0x15c000bd }, + { 0x502cc, 0x2b40003e }, + { 0x5fffc, 0x16c000bf }, + { 0x502d0, 0x2a80003b }, + { 0x5fffc, 0x17c000bf }, + { 0x502d4, 0x2a000039 }, + { 0x5fffc, 0x188000c2 }, + { 0x502d8, 0x29400036 }, + { 0x5fffc, 0x19c000c4 }, + { 0x502dc, 0x28800032 }, + { 0x5fffc, 0x1ac000c5 }, + { 0x502e0, 0x2800002f }, + { 0x5fffc, 0x1bc000c7 }, + { 0x502e4, 0x2740002c }, + { 0x5fffc, 0x1cc000c8 }, + { 0x502e8, 0x26c00029 }, + { 0x5fffc, 0x1dc000c9 }, + { 0x502ec, 0x26000027 }, + { 0x5fffc, 0x1ec000cc }, + { 0x502f0, 0x25000024 }, + { 0x5fffc, 0x200000cc }, + { 0x502f4, 0x24800021 }, + { 0x5fffc, 0x210000cd }, + { 0x502f8, 0x23800020 }, + { 0x5fffc, 0x220000ce }, + { 0x502fc, 0x2300001d }, +}; + +static struct mdp_table_entry mdp_downscale_x_table_PT4TOPT6[] = { + { 0x5fffc, 0x740008c }, + { 0x50280, 0x33800088 }, + { 0x5fffc, 0x800008e }, + { 0x50284, 0x33400084 }, + { 0x5fffc, 0x8400092 }, + { 0x50288, 0x33000080 }, + { 0x5fffc, 0x9000094 }, + { 0x5028c, 0x3300007b }, + { 0x5fffc, 0x9c00098 }, + { 0x50290, 0x32400077 }, + { 0x5fffc, 0xa40009b }, + { 0x50294, 0x32000073 }, + { 0x5fffc, 0xb00009d }, + { 0x50298, 0x31c0006f }, + { 0x5fffc, 0xbc000a0 }, + { 0x5029c, 0x3140006b }, + { 0x5fffc, 0xc8000a2 }, + { 0x502a0, 0x31000067 }, + { 0x5fffc, 0xd8000a5 }, + { 0x502a4, 0x30800062 }, + { 0x5fffc, 0xe4000a8 }, + { 0x502a8, 0x2fc0005f }, + { 0x5fffc, 0xec000aa }, + { 0x502ac, 0x2fc0005b }, + { 0x5fffc, 0xf8000ad }, + { 0x502b0, 0x2f400057 }, + { 0x5fffc, 0x108000b0 }, + { 0x502b4, 0x2e400054 }, + { 0x5fffc, 0x114000b2 }, + { 0x502b8, 0x2e000050 }, + { 0x5fffc, 0x124000b4 }, + { 0x502bc, 0x2d80004c }, + { 0x5fffc, 0x130000b6 }, + { 0x502c0, 0x2d000049 }, + { 0x5fffc, 0x140000b8 }, + { 0x502c4, 0x2c800045 }, + { 0x5fffc, 0x150000b9 }, + { 0x502c8, 0x2c000042 }, + { 0x5fffc, 0x15c000bd }, + { 0x502cc, 0x2b40003e }, + { 0x5fffc, 0x16c000bf }, + { 0x502d0, 0x2a80003b }, + { 0x5fffc, 0x17c000bf }, + { 0x502d4, 0x2a000039 }, + { 0x5fffc, 0x188000c2 }, + { 0x502d8, 0x29400036 }, + { 0x5fffc, 0x19c000c4 }, + { 0x502dc, 0x28800032 }, + { 0x5fffc, 0x1ac000c5 }, + { 0x502e0, 0x2800002f }, + { 0x5fffc, 0x1bc000c7 }, + { 0x502e4, 0x2740002c }, + { 0x5fffc, 0x1cc000c8 }, + { 0x502e8, 0x26c00029 }, + { 0x5fffc, 0x1dc000c9 }, + { 0x502ec, 0x26000027 }, + { 0x5fffc, 0x1ec000cc }, + { 0x502f0, 0x25000024 }, + { 0x5fffc, 0x200000cc }, + { 0x502f4, 0x24800021 }, + { 0x5fffc, 0x210000cd }, + { 0x502f8, 0x23800020 }, + { 0x5fffc, 0x220000ce }, + { 0x502fc, 0x2300001d }, +}; + +static struct mdp_table_entry mdp_downscale_x_table_PT6TOPT8[] = { + { 0x5fffc, 0xfe000070 }, + { 0x50280, 0x4bc00068 }, + { 0x5fffc, 0xfe000078 }, + { 0x50284, 0x4bc00060 }, + { 0x5fffc, 0xfe000080 }, + { 0x50288, 0x4b800059 }, + { 0x5fffc, 0xfe000089 }, + { 0x5028c, 0x4b000052 }, + { 0x5fffc, 0xfe400091 }, + { 0x50290, 0x4a80004b }, + { 0x5fffc, 0xfe40009a }, + { 0x50294, 0x4a000044 }, + { 0x5fffc, 0xfe8000a3 }, + { 0x50298, 0x4940003d }, + { 0x5fffc, 0xfec000ac }, + { 0x5029c, 0x48400037 }, + { 0x5fffc, 0xff0000b4 }, + { 0x502a0, 0x47800031 }, + { 0x5fffc, 0xff8000bd }, + { 0x502a4, 0x4640002b }, + { 0x5fffc, 0xc5 }, + { 0x502a8, 0x45000026 }, + { 0x5fffc, 0x8000ce }, + { 0x502ac, 0x43800021 }, + { 0x5fffc, 0x10000d6 }, + { 0x502b0, 0x4240001c }, + { 0x5fffc, 0x18000df }, + { 0x502b4, 0x40800018 }, + { 0x5fffc, 0x24000e6 }, + { 0x502b8, 0x3f000014 }, + { 0x5fffc, 0x30000ee }, + { 0x502bc, 0x3d400010 }, + { 0x5fffc, 0x40000f5 }, + { 0x502c0, 0x3b80000c }, + { 0x5fffc, 0x50000fc }, + { 0x502c4, 0x39800009 }, + { 0x5fffc, 0x6000102 }, + { 0x502c8, 0x37c00006 }, + { 0x5fffc, 0x7000109 }, + { 0x502cc, 0x35800004 }, + { 0x5fffc, 0x840010e }, + { 0x502d0, 0x33800002 }, + { 0x5fffc, 0x9800114 }, + { 0x502d4, 0x31400000 }, + { 0x5fffc, 0xac00119 }, + { 0x502d8, 0x2f4003fe }, + { 0x5fffc, 0xc40011e }, + { 0x502dc, 0x2d0003fc }, + { 0x5fffc, 0xdc00121 }, + { 0x502e0, 0x2b0003fb }, + { 0x5fffc, 0xf400125 }, + { 0x502e4, 0x28c003fa }, + { 0x5fffc, 0x11000128 }, + { 0x502e8, 0x268003f9 }, + { 0x5fffc, 0x12c0012a }, + { 0x502ec, 0x244003f9 }, + { 0x5fffc, 0x1480012c }, + { 0x502f0, 0x224003f8 }, + { 0x5fffc, 0x1640012e }, + { 0x502f4, 0x200003f8 }, + { 0x5fffc, 0x1800012f }, + { 0x502f8, 0x1e0003f8 }, + { 0x5fffc, 0x1a00012f }, + { 0x502fc, 0x1c0003f8 }, +}; + +static struct mdp_table_entry mdp_downscale_x_table_PT8TO1[] = { + { 0x5fffc, 0x0 }, + { 0x50280, 0x7fc00000 }, + { 0x5fffc, 0xff80000d }, + { 0x50284, 0x7ec003f9 }, + { 0x5fffc, 0xfec0001c }, + { 0x50288, 0x7d4003f3 }, + { 0x5fffc, 0xfe40002b }, + { 0x5028c, 0x7b8003ed }, + { 0x5fffc, 0xfd80003c }, + { 0x50290, 0x794003e8 }, + { 0x5fffc, 0xfcc0004d }, + { 0x50294, 0x76c003e4 }, + { 0x5fffc, 0xfc40005f }, + { 0x50298, 0x73c003e0 }, + { 0x5fffc, 0xfb800071 }, + { 0x5029c, 0x708003de }, + { 0x5fffc, 0xfac00085 }, + { 0x502a0, 0x6d0003db }, + { 0x5fffc, 0xfa000098 }, + { 0x502a4, 0x698003d9 }, + { 0x5fffc, 0xf98000ac }, + { 0x502a8, 0x654003d8 }, + { 0x5fffc, 0xf8c000c1 }, + { 0x502ac, 0x610003d7 }, + { 0x5fffc, 0xf84000d5 }, + { 0x502b0, 0x5c8003d7 }, + { 0x5fffc, 0xf7c000e9 }, + { 0x502b4, 0x580003d7 }, + { 0x5fffc, 0xf74000fd }, + { 0x502b8, 0x534003d8 }, + { 0x5fffc, 0xf6c00112 }, + { 0x502bc, 0x4e8003d8 }, + { 0x5fffc, 0xf6800126 }, + { 0x502c0, 0x494003da }, + { 0x5fffc, 0xf600013a }, + { 0x502c4, 0x448003db }, + { 0x5fffc, 0xf600014d }, + { 0x502c8, 0x3f4003dd }, + { 0x5fffc, 0xf5c00160 }, + { 0x502cc, 0x3a4003df }, + { 0x5fffc, 0xf5c00172 }, + { 0x502d0, 0x354003e1 }, + { 0x5fffc, 0xf5c00184 }, + { 0x502d4, 0x304003e3 }, + { 0x5fffc, 0xf6000195 }, + { 0x502d8, 0x2b0003e6 }, + { 0x5fffc, 0xf64001a6 }, + { 0x502dc, 0x260003e8 }, + { 0x5fffc, 0xf6c001b4 }, + { 0x502e0, 0x214003eb }, + { 0x5fffc, 0xf78001c2 }, + { 0x502e4, 0x1c4003ee }, + { 0x5fffc, 0xf80001cf }, + { 0x502e8, 0x17c003f1 }, + { 0x5fffc, 0xf90001db }, + { 0x502ec, 0x134003f3 }, + { 0x5fffc, 0xfa0001e5 }, + { 0x502f0, 0xf0003f6 }, + { 0x5fffc, 0xfb4001ee }, + { 0x502f4, 0xac003f9 }, + { 0x5fffc, 0xfcc001f5 }, + { 0x502f8, 0x70003fb }, + { 0x5fffc, 0xfe4001fb }, + { 0x502fc, 0x34003fe }, +}; + +struct mdp_table_entry *mdp_downscale_x_table[MDP_DOWNSCALE_MAX] = { + [MDP_DOWNSCALE_PT2TOPT4] = mdp_downscale_x_table_PT2TOPT4, + [MDP_DOWNSCALE_PT4TOPT6] = mdp_downscale_x_table_PT4TOPT6, + [MDP_DOWNSCALE_PT6TOPT8] = mdp_downscale_x_table_PT6TOPT8, + [MDP_DOWNSCALE_PT8TO1] = mdp_downscale_x_table_PT8TO1, +}; + +static struct mdp_table_entry mdp_downscale_y_table_PT2TOPT4[] = { + { 0x5fffc, 0x740008c }, + { 0x50300, 0x33800088 }, + { 0x5fffc, 0x800008e }, + { 0x50304, 0x33400084 }, + { 0x5fffc, 0x8400092 }, + { 0x50308, 0x33000080 }, + { 0x5fffc, 0x9000094 }, + { 0x5030c, 0x3300007b }, + { 0x5fffc, 0x9c00098 }, + { 0x50310, 0x32400077 }, + { 0x5fffc, 0xa40009b }, + { 0x50314, 0x32000073 }, + { 0x5fffc, 0xb00009d }, + { 0x50318, 0x31c0006f }, + { 0x5fffc, 0xbc000a0 }, + { 0x5031c, 0x3140006b }, + { 0x5fffc, 0xc8000a2 }, + { 0x50320, 0x31000067 }, + { 0x5fffc, 0xd8000a5 }, + { 0x50324, 0x30800062 }, + { 0x5fffc, 0xe4000a8 }, + { 0x50328, 0x2fc0005f }, + { 0x5fffc, 0xec000aa }, + { 0x5032c, 0x2fc0005b }, + { 0x5fffc, 0xf8000ad }, + { 0x50330, 0x2f400057 }, + { 0x5fffc, 0x108000b0 }, + { 0x50334, 0x2e400054 }, + { 0x5fffc, 0x114000b2 }, + { 0x50338, 0x2e000050 }, + { 0x5fffc, 0x124000b4 }, + { 0x5033c, 0x2d80004c }, + { 0x5fffc, 0x130000b6 }, + { 0x50340, 0x2d000049 }, + { 0x5fffc, 0x140000b8 }, + { 0x50344, 0x2c800045 }, + { 0x5fffc, 0x150000b9 }, + { 0x50348, 0x2c000042 }, + { 0x5fffc, 0x15c000bd }, + { 0x5034c, 0x2b40003e }, + { 0x5fffc, 0x16c000bf }, + { 0x50350, 0x2a80003b }, + { 0x5fffc, 0x17c000bf }, + { 0x50354, 0x2a000039 }, + { 0x5fffc, 0x188000c2 }, + { 0x50358, 0x29400036 }, + { 0x5fffc, 0x19c000c4 }, + { 0x5035c, 0x28800032 }, + { 0x5fffc, 0x1ac000c5 }, + { 0x50360, 0x2800002f }, + { 0x5fffc, 0x1bc000c7 }, + { 0x50364, 0x2740002c }, + { 0x5fffc, 0x1cc000c8 }, + { 0x50368, 0x26c00029 }, + { 0x5fffc, 0x1dc000c9 }, + { 0x5036c, 0x26000027 }, + { 0x5fffc, 0x1ec000cc }, + { 0x50370, 0x25000024 }, + { 0x5fffc, 0x200000cc }, + { 0x50374, 0x24800021 }, + { 0x5fffc, 0x210000cd }, + { 0x50378, 0x23800020 }, + { 0x5fffc, 0x220000ce }, + { 0x5037c, 0x2300001d }, +}; + +static struct mdp_table_entry mdp_downscale_y_table_PT4TOPT6[] = { + { 0x5fffc, 0x740008c }, + { 0x50300, 0x33800088 }, + { 0x5fffc, 0x800008e }, + { 0x50304, 0x33400084 }, + { 0x5fffc, 0x8400092 }, + { 0x50308, 0x33000080 }, + { 0x5fffc, 0x9000094 }, + { 0x5030c, 0x3300007b }, + { 0x5fffc, 0x9c00098 }, + { 0x50310, 0x32400077 }, + { 0x5fffc, 0xa40009b }, + { 0x50314, 0x32000073 }, + { 0x5fffc, 0xb00009d }, + { 0x50318, 0x31c0006f }, + { 0x5fffc, 0xbc000a0 }, + { 0x5031c, 0x3140006b }, + { 0x5fffc, 0xc8000a2 }, + { 0x50320, 0x31000067 }, + { 0x5fffc, 0xd8000a5 }, + { 0x50324, 0x30800062 }, + { 0x5fffc, 0xe4000a8 }, + { 0x50328, 0x2fc0005f }, + { 0x5fffc, 0xec000aa }, + { 0x5032c, 0x2fc0005b }, + { 0x5fffc, 0xf8000ad }, + { 0x50330, 0x2f400057 }, + { 0x5fffc, 0x108000b0 }, + { 0x50334, 0x2e400054 }, + { 0x5fffc, 0x114000b2 }, + { 0x50338, 0x2e000050 }, + { 0x5fffc, 0x124000b4 }, + { 0x5033c, 0x2d80004c }, + { 0x5fffc, 0x130000b6 }, + { 0x50340, 0x2d000049 }, + { 0x5fffc, 0x140000b8 }, + { 0x50344, 0x2c800045 }, + { 0x5fffc, 0x150000b9 }, + { 0x50348, 0x2c000042 }, + { 0x5fffc, 0x15c000bd }, + { 0x5034c, 0x2b40003e }, + { 0x5fffc, 0x16c000bf }, + { 0x50350, 0x2a80003b }, + { 0x5fffc, 0x17c000bf }, + { 0x50354, 0x2a000039 }, + { 0x5fffc, 0x188000c2 }, + { 0x50358, 0x29400036 }, + { 0x5fffc, 0x19c000c4 }, + { 0x5035c, 0x28800032 }, + { 0x5fffc, 0x1ac000c5 }, + { 0x50360, 0x2800002f }, + { 0x5fffc, 0x1bc000c7 }, + { 0x50364, 0x2740002c }, + { 0x5fffc, 0x1cc000c8 }, + { 0x50368, 0x26c00029 }, + { 0x5fffc, 0x1dc000c9 }, + { 0x5036c, 0x26000027 }, + { 0x5fffc, 0x1ec000cc }, + { 0x50370, 0x25000024 }, + { 0x5fffc, 0x200000cc }, + { 0x50374, 0x24800021 }, + { 0x5fffc, 0x210000cd }, + { 0x50378, 0x23800020 }, + { 0x5fffc, 0x220000ce }, + { 0x5037c, 0x2300001d }, +}; + +static struct mdp_table_entry mdp_downscale_y_table_PT6TOPT8[] = { + { 0x5fffc, 0xfe000070 }, + { 0x50300, 0x4bc00068 }, + { 0x5fffc, 0xfe000078 }, + { 0x50304, 0x4bc00060 }, + { 0x5fffc, 0xfe000080 }, + { 0x50308, 0x4b800059 }, + { 0x5fffc, 0xfe000089 }, + { 0x5030c, 0x4b000052 }, + { 0x5fffc, 0xfe400091 }, + { 0x50310, 0x4a80004b }, + { 0x5fffc, 0xfe40009a }, + { 0x50314, 0x4a000044 }, + { 0x5fffc, 0xfe8000a3 }, + { 0x50318, 0x4940003d }, + { 0x5fffc, 0xfec000ac }, + { 0x5031c, 0x48400037 }, + { 0x5fffc, 0xff0000b4 }, + { 0x50320, 0x47800031 }, + { 0x5fffc, 0xff8000bd }, + { 0x50324, 0x4640002b }, + { 0x5fffc, 0xc5 }, + { 0x50328, 0x45000026 }, + { 0x5fffc, 0x8000ce }, + { 0x5032c, 0x43800021 }, + { 0x5fffc, 0x10000d6 }, + { 0x50330, 0x4240001c }, + { 0x5fffc, 0x18000df }, + { 0x50334, 0x40800018 }, + { 0x5fffc, 0x24000e6 }, + { 0x50338, 0x3f000014 }, + { 0x5fffc, 0x30000ee }, + { 0x5033c, 0x3d400010 }, + { 0x5fffc, 0x40000f5 }, + { 0x50340, 0x3b80000c }, + { 0x5fffc, 0x50000fc }, + { 0x50344, 0x39800009 }, + { 0x5fffc, 0x6000102 }, + { 0x50348, 0x37c00006 }, + { 0x5fffc, 0x7000109 }, + { 0x5034c, 0x35800004 }, + { 0x5fffc, 0x840010e }, + { 0x50350, 0x33800002 }, + { 0x5fffc, 0x9800114 }, + { 0x50354, 0x31400000 }, + { 0x5fffc, 0xac00119 }, + { 0x50358, 0x2f4003fe }, + { 0x5fffc, 0xc40011e }, + { 0x5035c, 0x2d0003fc }, + { 0x5fffc, 0xdc00121 }, + { 0x50360, 0x2b0003fb }, + { 0x5fffc, 0xf400125 }, + { 0x50364, 0x28c003fa }, + { 0x5fffc, 0x11000128 }, + { 0x50368, 0x268003f9 }, + { 0x5fffc, 0x12c0012a }, + { 0x5036c, 0x244003f9 }, + { 0x5fffc, 0x1480012c }, + { 0x50370, 0x224003f8 }, + { 0x5fffc, 0x1640012e }, + { 0x50374, 0x200003f8 }, + { 0x5fffc, 0x1800012f }, + { 0x50378, 0x1e0003f8 }, + { 0x5fffc, 0x1a00012f }, + { 0x5037c, 0x1c0003f8 }, +}; + +static struct mdp_table_entry mdp_downscale_y_table_PT8TO1[] = { + { 0x5fffc, 0x0 }, + { 0x50300, 0x7fc00000 }, + { 0x5fffc, 0xff80000d }, + { 0x50304, 0x7ec003f9 }, + { 0x5fffc, 0xfec0001c }, + { 0x50308, 0x7d4003f3 }, + { 0x5fffc, 0xfe40002b }, + { 0x5030c, 0x7b8003ed }, + { 0x5fffc, 0xfd80003c }, + { 0x50310, 0x794003e8 }, + { 0x5fffc, 0xfcc0004d }, + { 0x50314, 0x76c003e4 }, + { 0x5fffc, 0xfc40005f }, + { 0x50318, 0x73c003e0 }, + { 0x5fffc, 0xfb800071 }, + { 0x5031c, 0x708003de }, + { 0x5fffc, 0xfac00085 }, + { 0x50320, 0x6d0003db }, + { 0x5fffc, 0xfa000098 }, + { 0x50324, 0x698003d9 }, + { 0x5fffc, 0xf98000ac }, + { 0x50328, 0x654003d8 }, + { 0x5fffc, 0xf8c000c1 }, + { 0x5032c, 0x610003d7 }, + { 0x5fffc, 0xf84000d5 }, + { 0x50330, 0x5c8003d7 }, + { 0x5fffc, 0xf7c000e9 }, + { 0x50334, 0x580003d7 }, + { 0x5fffc, 0xf74000fd }, + { 0x50338, 0x534003d8 }, + { 0x5fffc, 0xf6c00112 }, + { 0x5033c, 0x4e8003d8 }, + { 0x5fffc, 0xf6800126 }, + { 0x50340, 0x494003da }, + { 0x5fffc, 0xf600013a }, + { 0x50344, 0x448003db }, + { 0x5fffc, 0xf600014d }, + { 0x50348, 0x3f4003dd }, + { 0x5fffc, 0xf5c00160 }, + { 0x5034c, 0x3a4003df }, + { 0x5fffc, 0xf5c00172 }, + { 0x50350, 0x354003e1 }, + { 0x5fffc, 0xf5c00184 }, + { 0x50354, 0x304003e3 }, + { 0x5fffc, 0xf6000195 }, + { 0x50358, 0x2b0003e6 }, + { 0x5fffc, 0xf64001a6 }, + { 0x5035c, 0x260003e8 }, + { 0x5fffc, 0xf6c001b4 }, + { 0x50360, 0x214003eb }, + { 0x5fffc, 0xf78001c2 }, + { 0x50364, 0x1c4003ee }, + { 0x5fffc, 0xf80001cf }, + { 0x50368, 0x17c003f1 }, + { 0x5fffc, 0xf90001db }, + { 0x5036c, 0x134003f3 }, + { 0x5fffc, 0xfa0001e5 }, + { 0x50370, 0xf0003f6 }, + { 0x5fffc, 0xfb4001ee }, + { 0x50374, 0xac003f9 }, + { 0x5fffc, 0xfcc001f5 }, + { 0x50378, 0x70003fb }, + { 0x5fffc, 0xfe4001fb }, + { 0x5037c, 0x34003fe }, +}; + +struct mdp_table_entry *mdp_downscale_y_table[MDP_DOWNSCALE_MAX] = { + [MDP_DOWNSCALE_PT2TOPT4] = mdp_downscale_y_table_PT2TOPT4, + [MDP_DOWNSCALE_PT4TOPT6] = mdp_downscale_y_table_PT4TOPT6, + [MDP_DOWNSCALE_PT6TOPT8] = mdp_downscale_y_table_PT6TOPT8, + [MDP_DOWNSCALE_PT8TO1] = mdp_downscale_y_table_PT8TO1, +}; + +struct mdp_table_entry mdp_gaussian_blur_table[] = { + /* max variance */ + { 0x5fffc, 0x20000080 }, + { 0x50280, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x50284, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x50288, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x5028c, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x50290, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x50294, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x50298, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x5029c, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x502a0, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x502a4, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x502a8, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x502ac, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x502b0, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x502b4, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x502b8, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x502bc, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x502c0, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x502c4, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x502c8, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x502cc, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x502d0, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x502d4, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x502d8, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x502dc, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x502e0, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x502e4, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x502e8, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x502ec, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x502f0, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x502f4, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x502f8, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x502fc, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x50300, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x50304, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x50308, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x5030c, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x50310, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x50314, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x50318, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x5031c, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x50320, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x50324, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x50328, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x5032c, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x50330, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x50334, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x50338, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x5033c, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x50340, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x50344, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x50348, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x5034c, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x50350, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x50354, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x50358, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x5035c, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x50360, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x50364, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x50368, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x5036c, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x50370, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x50374, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x50378, 0x20000080 }, + { 0x5fffc, 0x20000080 }, + { 0x5037c, 0x20000080 }, +}; diff --git a/drivers/video/msm/mdp_scale_tables.h b/drivers/video/msm/mdp_scale_tables.h new file mode 100644 index 000000000000..34077b1af603 --- /dev/null +++ b/drivers/video/msm/mdp_scale_tables.h @@ -0,0 +1,38 @@ +/* drivers/video/msm_fb/mdp_scale_tables.h + * + * Copyright (C) 2007 QUALCOMM Incorporated + * Copyright (C) 2007 Google Incorporated + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#ifndef _MDP_SCALE_TABLES_H_ +#define _MDP_SCALE_TABLES_H_ + +#include <linux/types.h> +struct mdp_table_entry { + uint32_t reg; + uint32_t val; +}; + +extern struct mdp_table_entry mdp_upscale_table[64]; + +enum { + MDP_DOWNSCALE_PT2TOPT4, + MDP_DOWNSCALE_PT4TOPT6, + MDP_DOWNSCALE_PT6TOPT8, + MDP_DOWNSCALE_PT8TO1, + MDP_DOWNSCALE_MAX, +}; + +extern struct mdp_table_entry *mdp_downscale_x_table[MDP_DOWNSCALE_MAX]; +extern struct mdp_table_entry *mdp_downscale_y_table[MDP_DOWNSCALE_MAX]; +extern struct mdp_table_entry mdp_gaussian_blur_table[]; + +#endif diff --git a/drivers/video/msm/msm_fb.c b/drivers/video/msm/msm_fb.c new file mode 100644 index 000000000000..49101dda45ee --- /dev/null +++ b/drivers/video/msm/msm_fb.c @@ -0,0 +1,636 @@ +/* drivers/video/msm/msm_fb.c + * + * Core MSM framebuffer driver. + * + * Copyright (C) 2007 Google Incorporated + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/platform_device.h> +#include <linux/module.h> +#include <linux/fb.h> +#include <linux/delay.h> + +#include <linux/freezer.h> +#include <linux/wait.h> +#include <linux/msm_mdp.h> +#include <linux/io.h> +#include <linux/uaccess.h> +#include <mach/msm_fb.h> +#include <mach/board.h> +#include <linux/workqueue.h> +#include <linux/clk.h> +#include <linux/debugfs.h> +#include <linux/dma-mapping.h> + +#define PRINT_FPS 0 +#define PRINT_BLIT_TIME 0 + +#define SLEEPING 0x4 +#define UPDATING 0x3 +#define FULL_UPDATE_DONE 0x2 +#define WAKING 0x1 +#define AWAKE 0x0 + +#define NONE 0 +#define SUSPEND_RESUME 0x1 +#define FPS 0x2 +#define BLIT_TIME 0x4 +#define SHOW_UPDATES 0x8 + +#define DLOG(mask, fmt, args...) \ +do { \ + if (msmfb_debug_mask & mask) \ + printk(KERN_INFO "msmfb: "fmt, ##args); \ +} while (0) + +static int msmfb_debug_mask; +module_param_named(msmfb_debug_mask, msmfb_debug_mask, int, + S_IRUGO | S_IWUSR | S_IWGRP); + +struct mdp_device *mdp; + +struct msmfb_info { + struct fb_info *fb; + struct msm_panel_data *panel; + int xres; + int yres; + unsigned output_format; + unsigned yoffset; + unsigned frame_requested; + unsigned frame_done; + int sleeping; + unsigned update_frame; + struct { + int left; + int top; + int eright; /* exclusive */ + int ebottom; /* exclusive */ + } update_info; + char *black; + + spinlock_t update_lock; + struct mutex panel_init_lock; + wait_queue_head_t frame_wq; + struct workqueue_struct *resume_workqueue; + struct work_struct resume_work; + struct msmfb_callback dma_callback; + struct msmfb_callback vsync_callback; + struct hrtimer fake_vsync; + ktime_t vsync_request_time; +}; + +static int msmfb_open(struct fb_info *info, int user) +{ + return 0; +} + +static int msmfb_release(struct fb_info *info, int user) +{ + return 0; +} + +/* Called from dma interrupt handler, must not sleep */ +static void msmfb_handle_dma_interrupt(struct msmfb_callback *callback) +{ + unsigned long irq_flags; + struct msmfb_info *msmfb = container_of(callback, struct msmfb_info, + dma_callback); + + spin_lock_irqsave(&msmfb->update_lock, irq_flags); + msmfb->frame_done = msmfb->frame_requested; + if (msmfb->sleeping == UPDATING && + msmfb->frame_done == msmfb->update_frame) { + DLOG(SUSPEND_RESUME, "full update completed\n"); + queue_work(msmfb->resume_workqueue, &msmfb->resume_work); + } + spin_unlock_irqrestore(&msmfb->update_lock, irq_flags); + wake_up(&msmfb->frame_wq); +} + +static int msmfb_start_dma(struct msmfb_info *msmfb) +{ + uint32_t x, y, w, h; + unsigned addr; + unsigned long irq_flags; + uint32_t yoffset; + s64 time_since_request; + struct msm_panel_data *panel = msmfb->panel; + + spin_lock_irqsave(&msmfb->update_lock, irq_flags); + time_since_request = ktime_to_ns(ktime_sub(ktime_get(), + msmfb->vsync_request_time)); + if (time_since_request > 20 * NSEC_PER_MSEC) { + uint32_t us; + us = do_div(time_since_request, NSEC_PER_MSEC) / NSEC_PER_USEC; + printk(KERN_WARNING "msmfb_start_dma %lld.%03u ms after vsync " + "request\n", time_since_request, us); + } + if (msmfb->frame_done == msmfb->frame_requested) { + spin_unlock_irqrestore(&msmfb->update_lock, irq_flags); + return -1; + } + if (msmfb->sleeping == SLEEPING) { + DLOG(SUSPEND_RESUME, "tried to start dma while asleep\n"); + spin_unlock_irqrestore(&msmfb->update_lock, irq_flags); + return -1; + } + x = msmfb->update_info.left; + y = msmfb->update_info.top; + w = msmfb->update_info.eright - x; + h = msmfb->update_info.ebottom - y; + yoffset = msmfb->yoffset; + msmfb->update_info.left = msmfb->xres + 1; + msmfb->update_info.top = msmfb->yres + 1; + msmfb->update_info.eright = 0; + msmfb->update_info.ebottom = 0; + if (unlikely(w > msmfb->xres || h > msmfb->yres || + w == 0 || h == 0)) { + printk(KERN_INFO "invalid update: %d %d %d " + "%d\n", x, y, w, h); + msmfb->frame_done = msmfb->frame_requested; + goto error; + } + spin_unlock_irqrestore(&msmfb->update_lock, irq_flags); + + addr = ((msmfb->xres * (yoffset + y) + x) * 2); + mdp->dma(mdp, addr + msmfb->fb->fix.smem_start, + msmfb->xres * 2, w, h, x, y, &msmfb->dma_callback, + panel->interface_type); + return 0; +error: + spin_unlock_irqrestore(&msmfb->update_lock, irq_flags); + /* some clients need to clear their vsync interrupt */ + if (panel->clear_vsync) + panel->clear_vsync(panel); + wake_up(&msmfb->frame_wq); + return 0; +} + +/* Called from esync interrupt handler, must not sleep */ +static void msmfb_handle_vsync_interrupt(struct msmfb_callback *callback) +{ + struct msmfb_info *msmfb = container_of(callback, struct msmfb_info, + vsync_callback); + msmfb_start_dma(msmfb); +} + +static enum hrtimer_restart msmfb_fake_vsync(struct hrtimer *timer) +{ + struct msmfb_info *msmfb = container_of(timer, struct msmfb_info, + fake_vsync); + msmfb_start_dma(msmfb); + return HRTIMER_NORESTART; +} + +static void msmfb_pan_update(struct fb_info *info, uint32_t left, uint32_t top, + uint32_t eright, uint32_t ebottom, + uint32_t yoffset, int pan_display) +{ + struct msmfb_info *msmfb = info->par; + struct msm_panel_data *panel = msmfb->panel; + unsigned long irq_flags; + int sleeping; + int retry = 1; + + DLOG(SHOW_UPDATES, "update %d %d %d %d %d %d\n", + left, top, eright, ebottom, yoffset, pan_display); +restart: + spin_lock_irqsave(&msmfb->update_lock, irq_flags); + + /* if we are sleeping, on a pan_display wait 10ms (to throttle back + * drawing otherwise return */ + if (msmfb->sleeping == SLEEPING) { + DLOG(SUSPEND_RESUME, "drawing while asleep\n"); + spin_unlock_irqrestore(&msmfb->update_lock, irq_flags); + if (pan_display) + wait_event_interruptible_timeout(msmfb->frame_wq, + msmfb->sleeping != SLEEPING, HZ/10); + return; + } + + sleeping = msmfb->sleeping; + /* on a full update, if the last frame has not completed, wait for it */ + if (pan_display && (msmfb->frame_requested != msmfb->frame_done || + sleeping == UPDATING)) { + int ret; + spin_unlock_irqrestore(&msmfb->update_lock, irq_flags); + ret = wait_event_interruptible_timeout(msmfb->frame_wq, + msmfb->frame_done == msmfb->frame_requested && + msmfb->sleeping != UPDATING, 5 * HZ); + if (ret <= 0 && (msmfb->frame_requested != msmfb->frame_done || + msmfb->sleeping == UPDATING)) { + if (retry && panel->request_vsync && + (sleeping == AWAKE)) { + panel->request_vsync(panel, + &msmfb->vsync_callback); + retry = 0; + printk(KERN_WARNING "msmfb_pan_display timeout " + "rerequest vsync\n"); + } else { + printk(KERN_WARNING "msmfb_pan_display timeout " + "waiting for frame start, %d %d\n", + msmfb->frame_requested, + msmfb->frame_done); + return; + } + } + goto restart; + } + + + msmfb->frame_requested++; + /* if necessary, update the y offset, if this is the + * first full update on resume, set the sleeping state */ + if (pan_display) { + msmfb->yoffset = yoffset; + if (left == 0 && top == 0 && eright == info->var.xres && + ebottom == info->var.yres) { + if (sleeping == WAKING) { + msmfb->update_frame = msmfb->frame_requested; + DLOG(SUSPEND_RESUME, "full update starting\n"); + msmfb->sleeping = UPDATING; + } + } + } + + /* set the update request */ + if (left < msmfb->update_info.left) + msmfb->update_info.left = left; + if (top < msmfb->update_info.top) + msmfb->update_info.top = top; + if (eright > msmfb->update_info.eright) + msmfb->update_info.eright = eright; + if (ebottom > msmfb->update_info.ebottom) + msmfb->update_info.ebottom = ebottom; + DLOG(SHOW_UPDATES, "update queued %d %d %d %d %d\n", + msmfb->update_info.left, msmfb->update_info.top, + msmfb->update_info.eright, msmfb->update_info.ebottom, + msmfb->yoffset); + spin_unlock_irqrestore(&msmfb->update_lock, irq_flags); + + /* if the panel is all the way on wait for vsync, otherwise sleep + * for 16 ms (long enough for the dma to panel) and then begin dma */ + msmfb->vsync_request_time = ktime_get(); + if (panel->request_vsync && (sleeping == AWAKE)) { + panel->request_vsync(panel, &msmfb->vsync_callback); + } else { + if (!hrtimer_active(&msmfb->fake_vsync)) { + hrtimer_start(&msmfb->fake_vsync, + ktime_set(0, NSEC_PER_SEC/60), + HRTIMER_MODE_REL); + } + } +} + +static void msmfb_update(struct fb_info *info, uint32_t left, uint32_t top, + uint32_t eright, uint32_t ebottom) +{ + msmfb_pan_update(info, left, top, eright, ebottom, 0, 0); +} + +static void power_on_panel(struct work_struct *work) +{ + struct msmfb_info *msmfb = + container_of(work, struct msmfb_info, resume_work); + struct msm_panel_data *panel = msmfb->panel; + unsigned long irq_flags; + + mutex_lock(&msmfb->panel_init_lock); + DLOG(SUSPEND_RESUME, "turning on panel\n"); + if (msmfb->sleeping == UPDATING) { + if (panel->unblank(panel)) { + printk(KERN_INFO "msmfb: panel unblank failed," + "not starting drawing\n"); + goto error; + } + spin_lock_irqsave(&msmfb->update_lock, irq_flags); + msmfb->sleeping = AWAKE; + wake_up(&msmfb->frame_wq); + spin_unlock_irqrestore(&msmfb->update_lock, irq_flags); + } +error: + mutex_unlock(&msmfb->panel_init_lock); +} + + +static int msmfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) +{ + if ((var->xres != info->var.xres) || + (var->yres != info->var.yres) || + (var->xres_virtual != info->var.xres_virtual) || + (var->yres_virtual != info->var.yres_virtual) || + (var->xoffset != info->var.xoffset) || + (var->bits_per_pixel != info->var.bits_per_pixel) || + (var->grayscale != info->var.grayscale)) + return -EINVAL; + return 0; +} + +int msmfb_pan_display(struct fb_var_screeninfo *var, struct fb_info *info) +{ + struct msmfb_info *msmfb = info->par; + struct msm_panel_data *panel = msmfb->panel; + + /* "UPDT" */ + if ((panel->caps & MSMFB_CAP_PARTIAL_UPDATES) && + (var->reserved[0] == 0x54445055)) { + msmfb_pan_update(info, var->reserved[1] & 0xffff, + var->reserved[1] >> 16, + var->reserved[2] & 0xffff, + var->reserved[2] >> 16, var->yoffset, 1); + } else { + msmfb_pan_update(info, 0, 0, info->var.xres, info->var.yres, + var->yoffset, 1); + } + return 0; +} + +static void msmfb_fillrect(struct fb_info *p, const struct fb_fillrect *rect) +{ + cfb_fillrect(p, rect); + msmfb_update(p, rect->dx, rect->dy, rect->dx + rect->width, + rect->dy + rect->height); +} + +static void msmfb_copyarea(struct fb_info *p, const struct fb_copyarea *area) +{ + cfb_copyarea(p, area); + msmfb_update(p, area->dx, area->dy, area->dx + area->width, + area->dy + area->height); +} + +static void msmfb_imageblit(struct fb_info *p, const struct fb_image *image) +{ + cfb_imageblit(p, image); + msmfb_update(p, image->dx, image->dy, image->dx + image->width, + image->dy + image->height); +} + + +static int msmfb_blit(struct fb_info *info, + void __user *p) +{ + struct mdp_blit_req req; + struct mdp_blit_req_list req_list; + int i; + int ret; + + if (copy_from_user(&req_list, p, sizeof(req_list))) + return -EFAULT; + + for (i = 0; i < req_list.count; i++) { + struct mdp_blit_req_list *list = + (struct mdp_blit_req_list *)p; + if (copy_from_user(&req, &list->req[i], sizeof(req))) + return -EFAULT; + ret = mdp->blit(mdp, info, &req); + if (ret) + return ret; + } + return 0; +} + + +DEFINE_MUTEX(mdp_ppp_lock); + +static int msmfb_ioctl(struct fb_info *p, unsigned int cmd, unsigned long arg) +{ + void __user *argp = (void __user *)arg; + int ret; + + switch (cmd) { + case MSMFB_GRP_DISP: + mdp->set_grp_disp(mdp, arg); + break; + case MSMFB_BLIT: + ret = msmfb_blit(p, argp); + if (ret) + return ret; + break; + default: + printk(KERN_INFO "msmfb unknown ioctl: %d\n", cmd); + return -EINVAL; + } + return 0; +} + +static struct fb_ops msmfb_ops = { + .owner = THIS_MODULE, + .fb_open = msmfb_open, + .fb_release = msmfb_release, + .fb_check_var = msmfb_check_var, + .fb_pan_display = msmfb_pan_display, + .fb_fillrect = msmfb_fillrect, + .fb_copyarea = msmfb_copyarea, + .fb_imageblit = msmfb_imageblit, + .fb_ioctl = msmfb_ioctl, +}; + +static unsigned PP[16]; + + + +#define BITS_PER_PIXEL 16 + +static void setup_fb_info(struct msmfb_info *msmfb) +{ + struct fb_info *fb_info = msmfb->fb; + int r; + + /* finish setting up the fb_info struct */ + strncpy(fb_info->fix.id, "msmfb", 16); + fb_info->fix.ypanstep = 1; + + fb_info->fbops = &msmfb_ops; + fb_info->flags = FBINFO_DEFAULT; + + fb_info->fix.type = FB_TYPE_PACKED_PIXELS; + fb_info->fix.visual = FB_VISUAL_TRUECOLOR; + fb_info->fix.line_length = msmfb->xres * 2; + + fb_info->var.xres = msmfb->xres; + fb_info->var.yres = msmfb->yres; + fb_info->var.width = msmfb->panel->fb_data->width; + fb_info->var.height = msmfb->panel->fb_data->height; + fb_info->var.xres_virtual = msmfb->xres; + fb_info->var.yres_virtual = msmfb->yres * 2; + fb_info->var.bits_per_pixel = BITS_PER_PIXEL; + fb_info->var.accel_flags = 0; + + fb_info->var.yoffset = 0; + + if (msmfb->panel->caps & MSMFB_CAP_PARTIAL_UPDATES) { + fb_info->var.reserved[0] = 0x54445055; + fb_info->var.reserved[1] = 0; + fb_info->var.reserved[2] = (uint16_t)msmfb->xres | + ((uint32_t)msmfb->yres << 16); + } + + fb_info->var.red.offset = 11; + fb_info->var.red.length = 5; + fb_info->var.red.msb_right = 0; + fb_info->var.green.offset = 5; + fb_info->var.green.length = 6; + fb_info->var.green.msb_right = 0; + fb_info->var.blue.offset = 0; + fb_info->var.blue.length = 5; + fb_info->var.blue.msb_right = 0; + + r = fb_alloc_cmap(&fb_info->cmap, 16, 0); + fb_info->pseudo_palette = PP; + + PP[0] = 0; + for (r = 1; r < 16; r++) + PP[r] = 0xffffffff; +} + +static int setup_fbmem(struct msmfb_info *msmfb, struct platform_device *pdev) +{ + struct fb_info *fb = msmfb->fb; + struct resource *resource; + unsigned long size = msmfb->xres * msmfb->yres * + (BITS_PER_PIXEL >> 3) * 2; + unsigned char *fbram; + + /* board file might have attached a resource describing an fb */ + resource = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!resource) + return -EINVAL; + + /* check the resource is large enough to fit the fb */ + if (resource->end - resource->start < size) { + printk(KERN_ERR "allocated resource is too small for " + "fb\n"); + return -ENOMEM; + } + fb->fix.smem_start = resource->start; + fb->fix.smem_len = resource->end - resource->start; + fbram = ioremap(resource->start, + resource->end - resource->start); + if (fbram == 0) { + printk(KERN_ERR "msmfb: cannot allocate fbram!\n"); + return -ENOMEM; + } + fb->screen_base = fbram; + return 0; +} + +static int msmfb_probe(struct platform_device *pdev) +{ + struct fb_info *fb; + struct msmfb_info *msmfb; + struct msm_panel_data *panel = pdev->dev.platform_data; + int ret; + + if (!panel) { + pr_err("msmfb_probe: no platform data\n"); + return -EINVAL; + } + if (!panel->fb_data) { + pr_err("msmfb_probe: no fb_data\n"); + return -EINVAL; + } + + fb = framebuffer_alloc(sizeof(struct msmfb_info), &pdev->dev); + if (!fb) + return -ENOMEM; + msmfb = fb->par; + msmfb->fb = fb; + msmfb->panel = panel; + msmfb->xres = panel->fb_data->xres; + msmfb->yres = panel->fb_data->yres; + + ret = setup_fbmem(msmfb, pdev); + if (ret) + goto error_setup_fbmem; + + setup_fb_info(msmfb); + + spin_lock_init(&msmfb->update_lock); + mutex_init(&msmfb->panel_init_lock); + init_waitqueue_head(&msmfb->frame_wq); + msmfb->resume_workqueue = create_workqueue("panel_on"); + if (msmfb->resume_workqueue == NULL) { + printk(KERN_ERR "failed to create panel_on workqueue\n"); + ret = -ENOMEM; + goto error_create_workqueue; + } + INIT_WORK(&msmfb->resume_work, power_on_panel); + msmfb->black = kzalloc(msmfb->fb->var.bits_per_pixel*msmfb->xres, + GFP_KERNEL); + + printk(KERN_INFO "msmfb_probe() installing %d x %d panel\n", + msmfb->xres, msmfb->yres); + + msmfb->dma_callback.func = msmfb_handle_dma_interrupt; + msmfb->vsync_callback.func = msmfb_handle_vsync_interrupt; + hrtimer_init(&msmfb->fake_vsync, CLOCK_MONOTONIC, + HRTIMER_MODE_REL); + + + msmfb->fake_vsync.function = msmfb_fake_vsync; + + ret = register_framebuffer(fb); + if (ret) + goto error_register_framebuffer; + + msmfb->sleeping = WAKING; + + return 0; + +error_register_framebuffer: + destroy_workqueue(msmfb->resume_workqueue); +error_create_workqueue: + iounmap(fb->screen_base); +error_setup_fbmem: + framebuffer_release(msmfb->fb); + return ret; +} + +static struct platform_driver msm_panel_driver = { + /* need to write remove */ + .probe = msmfb_probe, + .driver = {.name = "msm_panel"}, +}; + + +static int msmfb_add_mdp_device(struct device *dev, + struct class_interface *class_intf) +{ + /* might need locking if mulitple mdp devices */ + if (mdp) + return 0; + mdp = container_of(dev, struct mdp_device, dev); + return platform_driver_register(&msm_panel_driver); +} + +static void msmfb_remove_mdp_device(struct device *dev, + struct class_interface *class_intf) +{ + /* might need locking if mulitple mdp devices */ + if (dev != &mdp->dev) + return; + platform_driver_unregister(&msm_panel_driver); + mdp = NULL; +} + +static struct class_interface msm_fb_interface = { + .add_dev = &msmfb_add_mdp_device, + .remove_dev = &msmfb_remove_mdp_device, +}; + +static int __init msmfb_init(void) +{ + return register_mdp_client(&msm_fb_interface); +} + +module_init(msmfb_init); diff --git a/drivers/video/omap/Kconfig b/drivers/video/omap/Kconfig index 44408850e2eb..551e3e9c4cbe 100644 --- a/drivers/video/omap/Kconfig +++ b/drivers/video/omap/Kconfig @@ -7,6 +7,69 @@ config FB_OMAP help Frame buffer driver for OMAP based boards. +config FB_OMAP_LCD_VGA + bool "Use LCD in VGA mode" + depends on MACH_OMAP_3430SDP || MACH_OMAP_LDP + +choice + depends on FB_OMAP && MACH_OVERO + prompt "Screen resolution" + default FB_OMAP_079M3R + help + Selected desired screen resolution + +config FB_OMAP_031M3R + boolean "640 x 480 @ 60 Hz Reduced blanking" + +config FB_OMAP_048M3R + boolean "800 x 600 @ 60 Hz Reduced blanking" + +config FB_OMAP_079M3R + boolean "1024 x 768 @ 60 Hz Reduced blanking" + +config FB_OMAP_092M9R + boolean "1280 x 720 @ 60 Hz Reduced blanking" + +endchoice + +config FB_OMAP_LCDC_EXTERNAL + bool "External LCD controller support" + depends on FB_OMAP + help + Say Y here, if you want to have support for boards with an + external LCD controller connected to the SoSSI/RFBI interface. + +config FB_OMAP_LCDC_HWA742 + bool "Epson HWA742 LCD controller support" + depends on FB_OMAP && FB_OMAP_LCDC_EXTERNAL + help + Say Y here if you want to have support for the external + Epson HWA742 LCD controller. + +config FB_OMAP_LCDC_BLIZZARD + bool "Epson Blizzard LCD controller support" + depends on FB_OMAP && FB_OMAP_LCDC_EXTERNAL + help + Say Y here if you want to have support for the external + Epson Blizzard LCD controller. + +config FB_OMAP_MANUAL_UPDATE + bool "Default to manual update mode" + depends on FB_OMAP && FB_OMAP_LCDC_EXTERNAL + help + Say Y here, if your user-space applications are capable of + notifying the frame buffer driver when a change has occured in + the frame buffer content and thus a reload of the image data to + the external frame buffer is required. If unsure, say N. + +config FB_OMAP_LCD_MIPID + bool "MIPI DBI-C/DCS compatible LCD support" + depends on FB_OMAP && SPI_MASTER + help + Say Y here if you want to have support for LCDs compatible with + the Mobile Industry Processor Interface DBI-C/DCS + specification. (Supported LCDs: Philips LPH8923, Sharp LS041Y3) + config FB_OMAP_BOOTLOADER_INIT bool "Check bootloader initialization" depends on FB_OMAP @@ -36,23 +99,4 @@ config FB_OMAP_DMA_TUNE answer yes. Answer no if you have a dedicated video memory, or don't use any of the accelerated features. -config FB_OMAP_LCDC_EXTERNAL - bool "External LCD controller support" - depends on FB_OMAP - help - Say Y here, if you want to have support for boards with an - external LCD controller connected to the SoSSI/RFBI interface. - -config FB_OMAP_LCDC_HWA742 - bool "Epson HWA742 LCD controller support" - depends on FB_OMAP && FB_OMAP_LCDC_EXTERNAL - help - Say Y here if you want to have support for the external - Epson HWA742 LCD controller. -config FB_OMAP_LCDC_BLIZZARD - bool "Epson Blizzard LCD controller support" - depends on FB_OMAP && FB_OMAP_LCDC_EXTERNAL - help - Say Y here if you want to have support for the external - Epson Blizzard LCD controller. diff --git a/drivers/video/omap/Makefile b/drivers/video/omap/Makefile index ed13889c1162..b63b198d1f03 100644 --- a/drivers/video/omap/Makefile +++ b/drivers/video/omap/Makefile @@ -8,6 +8,7 @@ objs-yy := omapfb_main.o objs-y$(CONFIG_ARCH_OMAP1) += lcdc.o objs-y$(CONFIG_ARCH_OMAP2) += dispc.o +objs-y$(CONFIG_ARCH_OMAP3) += dispc.o objs-$(CONFIG_ARCH_OMAP1)$(CONFIG_FB_OMAP_LCDC_EXTERNAL) += sossi.o objs-$(CONFIG_ARCH_OMAP2)$(CONFIG_FB_OMAP_LCDC_EXTERNAL) += rfbi.o @@ -15,6 +16,7 @@ objs-$(CONFIG_ARCH_OMAP2)$(CONFIG_FB_OMAP_LCDC_EXTERNAL) += rfbi.o objs-y$(CONFIG_FB_OMAP_LCDC_HWA742) += hwa742.o objs-y$(CONFIG_FB_OMAP_LCDC_BLIZZARD) += blizzard.o +objs-y$(CONFIG_MACH_AMS_DELTA) += lcd_ams_delta.o objs-y$(CONFIG_MACH_OMAP_H4) += lcd_h4.o objs-y$(CONFIG_MACH_OMAP_H3) += lcd_h3.o objs-y$(CONFIG_MACH_OMAP_PALMTE) += lcd_palmte.o @@ -24,5 +26,15 @@ objs-$(CONFIG_ARCH_OMAP16XX)$(CONFIG_MACH_OMAP_INNOVATOR) += lcd_inn1610.o objs-$(CONFIG_ARCH_OMAP15XX)$(CONFIG_MACH_OMAP_INNOVATOR) += lcd_inn1510.o objs-y$(CONFIG_MACH_OMAP_OSK) += lcd_osk.o +objs-y$(CONFIG_MACH_OMAP_APOLLON) += lcd_apollon.o +objs-y$(CONFIG_MACH_OMAP_2430SDP) += lcd_2430sdp.o +objs-y$(CONFIG_MACH_OMAP_3430SDP) += lcd_2430sdp.o +objs-y$(CONFIG_MACH_OMAP_LDP) += lcd_ldp.o +objs-y$(CONFIG_MACH_OMAP2EVM) += lcd_omap2evm.o +objs-y$(CONFIG_MACH_OMAP3EVM) += lcd_omap3evm.o +objs-y$(CONFIG_MACH_OMAP3_BEAGLE) += lcd_omap3beagle.o +objs-y$(CONFIG_FB_OMAP_LCD_MIPID) += lcd_mipid.o +objs-y$(CONFIG_MACH_OVERO) += lcd_overo.o + omapfb-objs := $(objs-yy) diff --git a/drivers/video/omap/blizzard.c b/drivers/video/omap/blizzard.c index 9dfcf39d3367..70dadf9d2334 100644 --- a/drivers/video/omap/blizzard.c +++ b/drivers/video/omap/blizzard.c @@ -44,6 +44,7 @@ #define BLIZZARD_CLK_SRC 0x0e #define BLIZZARD_MEM_BANK0_ACTIVATE 0x10 #define BLIZZARD_MEM_BANK0_STATUS 0x14 +#define BLIZZARD_PANEL_CONFIGURATION 0x28 #define BLIZZARD_HDISP 0x2a #define BLIZZARD_HNDP 0x2c #define BLIZZARD_VDISP0 0x2e @@ -92,7 +93,7 @@ struct blizzard_reg_list { }; /* These need to be saved / restored separately from the rest. */ -static struct blizzard_reg_list blizzard_pll_regs[] = { +static const struct blizzard_reg_list blizzard_pll_regs[] = { { .start = 0x04, /* Don't save PLL ctrl (0x0C) */ .end = 0x0a, @@ -103,7 +104,7 @@ static struct blizzard_reg_list blizzard_pll_regs[] = { }, }; -static struct blizzard_reg_list blizzard_gen_regs[] = { +static const struct blizzard_reg_list blizzard_gen_regs[] = { { .start = 0x18, /* SDRAM control */ .end = 0x20, @@ -162,6 +163,10 @@ struct blizzard_struct { int vid_scaled; int last_color_mode; int zoom_on; + int zoom_area_gx1; + int zoom_area_gx2; + int zoom_area_gy1; + int zoom_area_gy2; int screen_width; int screen_height; unsigned te_connected:1; @@ -186,7 +191,7 @@ struct blizzard_struct { struct omapfb_device *fbdev; struct lcd_ctrl_extif *extif; - struct lcd_ctrl *int_ctrl; + const struct lcd_ctrl *int_ctrl; void (*power_up)(struct device *dev); void (*power_down)(struct device *dev); @@ -513,6 +518,13 @@ static int do_full_screen_update(struct blizzard_request *req) return REQ_PENDING; } +static int check_1d_intersect(int a1, int a2, int b1, int b2) +{ + if (a2 <= b1 || b2 <= a1) + return 0; + return 1; +} + /* Setup all planes with an overlapping area with the update window. */ static int do_partial_update(struct blizzard_request *req, int plane, int x, int y, int w, int h, @@ -525,6 +537,7 @@ static int do_partial_update(struct blizzard_request *req, int plane, int color_mode; int flags; int zoom_off; + int have_zoom_for_this_update = 0; /* Global coordinates, relative to pixel 0,0 of the LCD */ gx1 = x + blizzard.plane[plane].pos_x; @@ -544,10 +557,6 @@ static int do_partial_update(struct blizzard_request *req, int plane, gx2_out = gx1_out + w_out; gy2_out = gy1_out + h_out; } - zoom_off = blizzard.zoom_on && gx1 == 0 && gy1 == 0 && - w == blizzard.screen_width && h == blizzard.screen_height; - blizzard.zoom_on = (!zoom_off && blizzard.zoom_on) || - (w < w_out || h < h_out); for (i = 0; i < OMAPFB_PLANE_NUM; i++) { struct plane_info *p = &blizzard.plane[i]; @@ -653,8 +662,49 @@ static int do_partial_update(struct blizzard_request *req, int plane, else disable_tearsync(); + if ((gx2_out - gx1_out) != (gx2 - gx1) || + (gy2_out - gy1_out) != (gy2 - gy1)) + have_zoom_for_this_update = 1; + + /* 'background' type of screen update (as opposed to 'destructive') + can be used to disable scaling if scaling is active */ + zoom_off = blizzard.zoom_on && !have_zoom_for_this_update && + (gx1_out == 0) && (gx2_out == blizzard.screen_width) && + (gy1_out == 0) && (gy2_out == blizzard.screen_height) && + (gx1 == 0) && (gy1 == 0); + + if (blizzard.zoom_on && !have_zoom_for_this_update && !zoom_off && + check_1d_intersect(blizzard.zoom_area_gx1, blizzard.zoom_area_gx2, + gx1_out, gx2_out) && + check_1d_intersect(blizzard.zoom_area_gy1, blizzard.zoom_area_gy2, + gy1_out, gy2_out)) { + /* Previous screen update was using scaling, current update + * is not using it. Additionally, current screen update is + * going to overlap with the scaled area. Scaling needs to be + * disabled in order to avoid 'magnifying glass' effect. + * Dummy setup of background window can be used for this. + */ + set_window_regs(0, 0, blizzard.screen_width, + blizzard.screen_height, + 0, 0, blizzard.screen_width, + blizzard.screen_height, + BLIZZARD_COLOR_RGB565, 1, flags); + blizzard.zoom_on = 0; + } + + /* remember scaling settings if we have scaled update */ + if (have_zoom_for_this_update) { + blizzard.zoom_on = 1; + blizzard.zoom_area_gx1 = gx1_out; + blizzard.zoom_area_gx2 = gx2_out; + blizzard.zoom_area_gy1 = gy1_out; + blizzard.zoom_area_gy2 = gy2_out; + } + set_window_regs(gx1, gy1, gx2, gy2, gx1_out, gy1_out, gx2_out, gy2_out, color_mode, zoom_off, flags); + if (zoom_off) + blizzard.zoom_on = 0; blizzard.extif->set_bits_per_cycle(16); /* set_window_regs has left the register index at the right @@ -908,6 +958,35 @@ static int blizzard_set_scale(int plane, int orig_w, int orig_h, return 0; } +static int blizzard_set_rotate(int angle) +{ + u32 l; + + l = blizzard_read_reg(BLIZZARD_PANEL_CONFIGURATION); + l &= ~0x03; + + switch (angle) { + case 0: + l = l | 0x00; + break; + case 90: + l = l | 0x03; + break; + case 180: + l = l | 0x02; + break; + case 270: + l = l | 0x01; + break; + default: + return -EINVAL; + } + + blizzard_write_reg(BLIZZARD_PANEL_CONFIGURATION, l); + + return 0; +} + static int blizzard_enable_plane(int plane, int enable) { if (enable) @@ -1285,14 +1364,15 @@ static void blizzard_get_caps(int plane, struct omapfb_caps *caps) caps->ctrl |= OMAPFB_CAPS_MANUAL_UPDATE | OMAPFB_CAPS_WINDOW_PIXEL_DOUBLE | OMAPFB_CAPS_WINDOW_SCALE | - OMAPFB_CAPS_WINDOW_OVERLAY; + OMAPFB_CAPS_WINDOW_OVERLAY | + OMAPFB_CAPS_WINDOW_ROTATE; if (blizzard.te_connected) caps->ctrl |= OMAPFB_CAPS_TEARSYNC; caps->wnd_color |= (1 << OMAPFB_COLOR_RGB565) | (1 << OMAPFB_COLOR_YUV420); } -static void _save_regs(struct blizzard_reg_list *list, int cnt) +static void _save_regs(const struct blizzard_reg_list *list, int cnt) { int i; @@ -1303,7 +1383,7 @@ static void _save_regs(struct blizzard_reg_list *list, int cnt) } } -static void _restore_regs(struct blizzard_reg_list *list, int cnt) +static void _restore_regs(const struct blizzard_reg_list *list, int cnt) { int i; @@ -1560,6 +1640,7 @@ struct lcd_ctrl blizzard_ctrl = { .setup_plane = blizzard_setup_plane, .set_scale = blizzard_set_scale, .enable_plane = blizzard_enable_plane, + .set_rotate = blizzard_set_rotate, .update_window = blizzard_update_window_async, .sync = blizzard_sync, .suspend = blizzard_suspend, diff --git a/drivers/video/omap/dispc.c b/drivers/video/omap/dispc.c index 915439dc05a0..f16e42154229 100644 --- a/drivers/video/omap/dispc.c +++ b/drivers/video/omap/dispc.c @@ -155,6 +155,8 @@ struct resmap { unsigned long *map; }; +#define MAX_IRQ_HANDLERS 4 + static struct { void __iomem *base; @@ -167,9 +169,11 @@ static struct { int ext_mode; - unsigned long enabled_irqs; - void (*irq_callback)(void *); - void *irq_callback_data; + struct { + u32 irq_mask; + void (*callback)(void *); + void *data; + } irq_handlers[MAX_IRQ_HANDLERS]; struct completion frame_done; int fir_hinc[OMAPFB_PLANE_NUM]; @@ -286,7 +290,7 @@ static void setup_plane_fifo(int plane, int ext_mode) BUG_ON(plane > 2); l = dispc_read_reg(fsz_reg[plane]); - l &= FLD_MASK(0, 9); + l &= FLD_MASK(0, 11); if (ext_mode) { low = l * 3 / 4; high = l; @@ -294,7 +298,7 @@ static void setup_plane_fifo(int plane, int ext_mode) low = l / 4; high = l * 3 / 4; } - MOD_REG_FLD(ftrs_reg[plane], FLD_MASK(16, 9) | FLD_MASK(0, 9), + MOD_REG_FLD(ftrs_reg[plane], FLD_MASK(16, 12) | FLD_MASK(0, 12), (high << 16) | low); } @@ -809,57 +813,74 @@ static void set_lcd_timings(void) panel->pixel_clock = fck / lck_div / pck_div / 1000; } -int omap_dispc_request_irq(void (*callback)(void *data), void *data) +static void recalc_irq_mask(void) { - int r = 0; + int i; + unsigned long irq_mask = DISPC_IRQ_MASK_ERROR; - BUG_ON(callback == NULL); + for (i = 0; i < MAX_IRQ_HANDLERS; i++) { + if (!dispc.irq_handlers[i].callback) + continue; - if (dispc.irq_callback) - r = -EBUSY; - else { - dispc.irq_callback = callback; - dispc.irq_callback_data = data; + irq_mask |= dispc.irq_handlers[i].irq_mask; } - return r; -} -EXPORT_SYMBOL(omap_dispc_request_irq); - -void omap_dispc_enable_irqs(int irq_mask) -{ enable_lcd_clocks(1); - dispc.enabled_irqs = irq_mask; - irq_mask |= DISPC_IRQ_MASK_ERROR; MOD_REG_FLD(DISPC_IRQENABLE, 0x7fff, irq_mask); enable_lcd_clocks(0); } -EXPORT_SYMBOL(omap_dispc_enable_irqs); -void omap_dispc_disable_irqs(int irq_mask) +int omap_dispc_request_irq(unsigned long irq_mask, void (*callback)(void *data), + void *data) { - enable_lcd_clocks(1); - dispc.enabled_irqs &= ~irq_mask; - irq_mask &= ~DISPC_IRQ_MASK_ERROR; - MOD_REG_FLD(DISPC_IRQENABLE, 0x7fff, irq_mask); - enable_lcd_clocks(0); + int i; + + BUG_ON(callback == NULL); + + for (i = 0; i < MAX_IRQ_HANDLERS; i++) { + if (dispc.irq_handlers[i].callback) + continue; + + dispc.irq_handlers[i].irq_mask = irq_mask; + dispc.irq_handlers[i].callback = callback; + dispc.irq_handlers[i].data = data; + recalc_irq_mask(); + + return 0; + } + + return -EBUSY; } -EXPORT_SYMBOL(omap_dispc_disable_irqs); +EXPORT_SYMBOL(omap_dispc_request_irq); -void omap_dispc_free_irq(void) +void omap_dispc_free_irq(unsigned long irq_mask, void (*callback)(void *data), + void *data) { - enable_lcd_clocks(1); - omap_dispc_disable_irqs(DISPC_IRQ_MASK_ALL); - dispc.irq_callback = NULL; - dispc.irq_callback_data = NULL; - enable_lcd_clocks(0); + int i; + + for (i = 0; i < MAX_IRQ_HANDLERS; i++) { + if (dispc.irq_handlers[i].callback == callback && + dispc.irq_handlers[i].data == data) { + dispc.irq_handlers[i].irq_mask = 0; + dispc.irq_handlers[i].callback = NULL; + dispc.irq_handlers[i].data = NULL; + recalc_irq_mask(); + return; + } + } + + BUG(); } EXPORT_SYMBOL(omap_dispc_free_irq); static irqreturn_t omap_dispc_irq_handler(int irq, void *dev) { - u32 stat = dispc_read_reg(DISPC_IRQSTATUS); + u32 stat; + int i = 0; + + enable_lcd_clocks(1); + stat = dispc_read_reg(DISPC_IRQSTATUS); if (stat & DISPC_IRQ_FRAMEMASK) complete(&dispc.frame_done); @@ -870,11 +891,17 @@ static irqreturn_t omap_dispc_irq_handler(int irq, void *dev) } } - if ((stat & dispc.enabled_irqs) && dispc.irq_callback) - dispc.irq_callback(dispc.irq_callback_data); + for (i = 0; i < MAX_IRQ_HANDLERS; i++) { + if (unlikely(dispc.irq_handlers[i].callback && + (stat & dispc.irq_handlers[i].irq_mask))) + dispc.irq_handlers[i].callback( + dispc.irq_handlers[i].data); + } dispc_write_reg(DISPC_IRQSTATUS, stat); + enable_lcd_clocks(0); + return IRQ_HANDLED; } @@ -913,18 +940,13 @@ static void put_dss_clocks(void) static void enable_lcd_clocks(int enable) { - if (enable) + if (enable) { + clk_enable(dispc.dss_ick); clk_enable(dispc.dss1_fck); - else + } else { clk_disable(dispc.dss1_fck); -} - -static void enable_interface_clocks(int enable) -{ - if (enable) - clk_enable(dispc.dss_ick); - else clk_disable(dispc.dss_ick); + } } static void enable_digit_clocks(int enable) @@ -1013,7 +1035,7 @@ static void mmap_user_close(struct vm_area_struct *vma) atomic_dec(&dispc.map_count[plane]); } -static struct vm_operations_struct mmap_user_ops = { +static const struct vm_operations_struct mmap_user_ops = { .open = mmap_user_open, .close = mmap_user_close, }; @@ -1365,7 +1387,6 @@ static int omap_dispc_init(struct omapfb_device *fbdev, int ext_mode, if ((r = get_dss_clocks()) < 0) goto fail0; - enable_interface_clocks(1); enable_lcd_clocks(1); #ifdef CONFIG_FB_OMAP_BOOTLOADER_INIT @@ -1396,10 +1417,10 @@ static int omap_dispc_init(struct omapfb_device *fbdev, int ext_mode, enable_digit_clocks(0); } - /* Enable smart idle and autoidle */ - l = dispc_read_reg(DISPC_CONTROL); + /* Enable smart standby/idle, autoidle and wakeup */ + l = dispc_read_reg(DISPC_SYSCONFIG); l &= ~((3 << 12) | (3 << 3)); - l |= (2 << 12) | (2 << 3) | (1 << 0); + l |= (2 << 12) | (2 << 3) | (1 << 2) | (1 << 0); dispc_write_reg(DISPC_SYSCONFIG, l); omap_writel(1 << 0, DSS_BASE + DSS_SYSCONFIG); @@ -1409,10 +1430,9 @@ static int omap_dispc_init(struct omapfb_device *fbdev, int ext_mode, dispc_write_reg(DISPC_CONFIG, l); l = dispc_read_reg(DISPC_IRQSTATUS); - dispc_write_reg(l, DISPC_IRQSTATUS); + dispc_write_reg(DISPC_IRQSTATUS, l); - /* Enable those that we handle always */ - omap_dispc_enable_irqs(DISPC_IRQ_FRAMEMASK); + recalc_irq_mask(); if ((r = request_irq(INT_24XX_DSS_IRQ, omap_dispc_irq_handler, 0, MODULE_NAME, fbdev)) < 0) { @@ -1469,7 +1489,6 @@ fail2: free_irq(INT_24XX_DSS_IRQ, fbdev); fail1: enable_lcd_clocks(0); - enable_interface_clocks(0); put_dss_clocks(); fail0: iounmap(dispc.base); @@ -1487,7 +1506,6 @@ static void omap_dispc_cleanup(void) cleanup_fbmem(); free_palette_ram(); free_irq(INT_24XX_DSS_IRQ, dispc.fbdev); - enable_interface_clocks(0); put_dss_clocks(); iounmap(dispc.base); } diff --git a/drivers/video/omap/dispc.h b/drivers/video/omap/dispc.h index ef720a78f6d5..c15ea77f0604 100644 --- a/drivers/video/omap/dispc.h +++ b/drivers/video/omap/dispc.h @@ -37,9 +37,10 @@ extern void omap_dispc_set_lcd_size(int width, int height); extern void omap_dispc_enable_lcd_out(int enable); extern void omap_dispc_enable_digit_out(int enable); -extern int omap_dispc_request_irq(void (*callback)(void *data), void *data); -extern void omap_dispc_free_irq(void); +extern int omap_dispc_request_irq(unsigned long irq_mask, + void (*callback)(void *data), void *data); +extern void omap_dispc_free_irq(unsigned long irq_mask, + void (*callback)(void *data), void *data); extern const struct lcd_ctrl omap2_int_ctrl; - #endif diff --git a/drivers/video/omap/hwa742.c b/drivers/video/omap/hwa742.c index 5d4f34887a22..ca51583ec98a 100644 --- a/drivers/video/omap/hwa742.c +++ b/drivers/video/omap/hwa742.c @@ -131,7 +131,7 @@ struct { struct omapfb_device *fbdev; struct lcd_ctrl_extif *extif; - struct lcd_ctrl *int_ctrl; + const struct lcd_ctrl *int_ctrl; struct clk *sys_ck; } hwa742; diff --git a/drivers/video/omap/lcd_2430sdp.c b/drivers/video/omap/lcd_2430sdp.c new file mode 100644 index 000000000000..393712b6f369 --- /dev/null +++ b/drivers/video/omap/lcd_2430sdp.c @@ -0,0 +1,202 @@ +/* + * LCD panel support for the TI 2430SDP board + * + * Copyright (C) 2007 MontaVista + * Author: Hunyue Yau <hyau@mvista.com> + * + * Derived from drivers/video/omap/lcd-apollon.c + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/delay.h> +#include <linux/gpio.h> +#include <linux/i2c/twl4030.h> + +#include <mach/mux.h> +#include <mach/omapfb.h> +#include <asm/mach-types.h> + +#define SDP2430_LCD_PANEL_BACKLIGHT_GPIO 91 +#define SDP2430_LCD_PANEL_ENABLE_GPIO 154 +#define SDP3430_LCD_PANEL_BACKLIGHT_GPIO 24 +#define SDP3430_LCD_PANEL_ENABLE_GPIO 28 + +static unsigned backlight_gpio; +static unsigned enable_gpio; + +#define LCD_PIXCLOCK_MAX 5400 /* freq 5.4 MHz */ +#define PM_RECEIVER TWL4030_MODULE_PM_RECEIVER +#define ENABLE_VAUX2_DEDICATED 0x09 +#define ENABLE_VAUX2_DEV_GRP 0x20 +#define ENABLE_VAUX3_DEDICATED 0x03 +#define ENABLE_VAUX3_DEV_GRP 0x20 + +#define ENABLE_VPLL2_DEDICATED 0x05 +#define ENABLE_VPLL2_DEV_GRP 0xE0 +#define TWL4030_VPLL2_DEV_GRP 0x33 +#define TWL4030_VPLL2_DEDICATED 0x36 + +#define t2_out(c, r, v) twl4030_i2c_write_u8(c, r, v) + + +static int sdp2430_panel_init(struct lcd_panel *panel, + struct omapfb_device *fbdev) +{ + if (machine_is_omap_3430sdp()) { + enable_gpio = SDP3430_LCD_PANEL_ENABLE_GPIO; + backlight_gpio = SDP3430_LCD_PANEL_BACKLIGHT_GPIO; + } else { + enable_gpio = SDP2430_LCD_PANEL_ENABLE_GPIO; + backlight_gpio = SDP2430_LCD_PANEL_BACKLIGHT_GPIO; + } + + gpio_request(enable_gpio, "LCD enable"); /* LCD panel */ + gpio_request(backlight_gpio, "LCD bl"); /* LCD backlight */ + gpio_direction_output(enable_gpio, 0); + gpio_direction_output(backlight_gpio, 0); + + return 0; +} + +static void sdp2430_panel_cleanup(struct lcd_panel *panel) +{ + gpio_free(backlight_gpio); + gpio_free(enable_gpio); +} + +static int sdp2430_panel_enable(struct lcd_panel *panel) +{ + u8 ded_val, ded_reg; + u8 grp_val, grp_reg; + + if (machine_is_omap_3430sdp()) { + ded_reg = TWL4030_VAUX3_DEDICATED; + ded_val = ENABLE_VAUX3_DEDICATED; + grp_reg = TWL4030_VAUX3_DEV_GRP; + grp_val = ENABLE_VAUX3_DEV_GRP; + + if (omap_rev() > OMAP3430_REV_ES1_0) { + t2_out(PM_RECEIVER, ENABLE_VPLL2_DEDICATED, + TWL4030_VPLL2_DEDICATED); + t2_out(PM_RECEIVER, ENABLE_VPLL2_DEV_GRP, + TWL4030_VPLL2_DEV_GRP); + } + } else { + ded_reg = TWL4030_VAUX2_DEDICATED; + ded_val = ENABLE_VAUX2_DEDICATED; + grp_reg = TWL4030_VAUX2_DEV_GRP; + grp_val = ENABLE_VAUX2_DEV_GRP; + } + + gpio_set_value(enable_gpio, 1); + gpio_set_value(backlight_gpio, 1); + + if (0 != t2_out(PM_RECEIVER, ded_val, ded_reg)) + return -EIO; + if (0 != t2_out(PM_RECEIVER, grp_val, grp_reg)) + return -EIO; + + return 0; +} + +static void sdp2430_panel_disable(struct lcd_panel *panel) +{ + gpio_set_value(enable_gpio, 0); + gpio_set_value(backlight_gpio, 0); + if (omap_rev() > OMAP3430_REV_ES1_0) { + t2_out(PM_RECEIVER, 0x0, TWL4030_VPLL2_DEDICATED); + t2_out(PM_RECEIVER, 0x0, TWL4030_VPLL2_DEV_GRP); + msleep(4); + } +} + +static unsigned long sdp2430_panel_get_caps(struct lcd_panel *panel) +{ + return 0; +} + +struct lcd_panel sdp2430_panel = { + .name = "sdp2430", + .config = OMAP_LCDC_PANEL_TFT | OMAP_LCDC_INV_VSYNC | + OMAP_LCDC_INV_HSYNC, + + .bpp = 16, + .data_lines = 16, + .x_res = 240, + .y_res = 320, + .hsw = 3, /* hsync_len (4) - 1 */ + .hfp = 3, /* right_margin (4) - 1 */ + .hbp = 39, /* left_margin (40) - 1 */ + .vsw = 1, /* vsync_len (2) - 1 */ + .vfp = 2, /* lower_margin */ + .vbp = 7, /* upper_margin (8) - 1 */ + + .pixel_clock = LCD_PIXCLOCK_MAX, + + .init = sdp2430_panel_init, + .cleanup = sdp2430_panel_cleanup, + .enable = sdp2430_panel_enable, + .disable = sdp2430_panel_disable, + .get_caps = sdp2430_panel_get_caps, +}; + +static int sdp2430_panel_probe(struct platform_device *pdev) +{ + omapfb_register_panel(&sdp2430_panel); + return 0; +} + +static int sdp2430_panel_remove(struct platform_device *pdev) +{ + return 0; +} + +static int sdp2430_panel_suspend(struct platform_device *pdev, + pm_message_t mesg) +{ + return 0; +} + +static int sdp2430_panel_resume(struct platform_device *pdev) +{ + return 0; +} + +struct platform_driver sdp2430_panel_driver = { + .probe = sdp2430_panel_probe, + .remove = sdp2430_panel_remove, + .suspend = sdp2430_panel_suspend, + .resume = sdp2430_panel_resume, + .driver = { + .name = "sdp2430_lcd", + .owner = THIS_MODULE, + }, +}; + +static int __init sdp2430_panel_drv_init(void) +{ + return platform_driver_register(&sdp2430_panel_driver); +} + +static void __exit sdp2430_panel_drv_exit(void) +{ + platform_driver_unregister(&sdp2430_panel_driver); +} + +module_init(sdp2430_panel_drv_init); +module_exit(sdp2430_panel_drv_exit); diff --git a/drivers/video/omap/lcd_ams_delta.c b/drivers/video/omap/lcd_ams_delta.c new file mode 100644 index 000000000000..1f7439955e02 --- /dev/null +++ b/drivers/video/omap/lcd_ams_delta.c @@ -0,0 +1,137 @@ +/* + * Based on drivers/video/omap/lcd_inn1510.c + * + * LCD panel support for the Amstrad E3 (Delta) videophone. + * + * Copyright (C) 2006 Jonathan McDowell <noodles@earth.li> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/io.h> +#include <linux/delay.h> + +#include <mach/board-ams-delta.h> +#include <mach/hardware.h> +#include <mach/omapfb.h> + +#define AMS_DELTA_DEFAULT_CONTRAST 112 + +static int ams_delta_panel_init(struct lcd_panel *panel, + struct omapfb_device *fbdev) +{ + return 0; +} + +static void ams_delta_panel_cleanup(struct lcd_panel *panel) +{ +} + +static int ams_delta_panel_enable(struct lcd_panel *panel) +{ + ams_delta_latch2_write(AMS_DELTA_LATCH2_LCD_NDISP, + AMS_DELTA_LATCH2_LCD_NDISP); + ams_delta_latch2_write(AMS_DELTA_LATCH2_LCD_VBLEN, + AMS_DELTA_LATCH2_LCD_VBLEN); + + omap_writeb(1, OMAP_PWL_CLK_ENABLE); + omap_writeb(AMS_DELTA_DEFAULT_CONTRAST, OMAP_PWL_ENABLE); + + return 0; +} + +static void ams_delta_panel_disable(struct lcd_panel *panel) +{ + ams_delta_latch2_write(AMS_DELTA_LATCH2_LCD_VBLEN, 0); + ams_delta_latch2_write(AMS_DELTA_LATCH2_LCD_NDISP, 0); +} + +static unsigned long ams_delta_panel_get_caps(struct lcd_panel *panel) +{ + return 0; +} + +static struct lcd_panel ams_delta_panel = { + .name = "ams-delta", + .config = 0, + + .bpp = 12, + .data_lines = 16, + .x_res = 480, + .y_res = 320, + .pixel_clock = 4687, + .hsw = 3, + .hfp = 1, + .hbp = 1, + .vsw = 1, + .vfp = 0, + .vbp = 0, + .pcd = 0, + .acb = 37, + + .init = ams_delta_panel_init, + .cleanup = ams_delta_panel_cleanup, + .enable = ams_delta_panel_enable, + .disable = ams_delta_panel_disable, + .get_caps = ams_delta_panel_get_caps, +}; + +static int ams_delta_panel_probe(struct platform_device *pdev) +{ + omapfb_register_panel(&ams_delta_panel); + return 0; +} + +static int ams_delta_panel_remove(struct platform_device *pdev) +{ + return 0; +} + +static int ams_delta_panel_suspend(struct platform_device *pdev, + pm_message_t mesg) +{ + return 0; +} + +static int ams_delta_panel_resume(struct platform_device *pdev) +{ + return 0; +} + +struct platform_driver ams_delta_panel_driver = { + .probe = ams_delta_panel_probe, + .remove = ams_delta_panel_remove, + .suspend = ams_delta_panel_suspend, + .resume = ams_delta_panel_resume, + .driver = { + .name = "lcd_ams_delta", + .owner = THIS_MODULE, + }, +}; + +static int ams_delta_panel_drv_init(void) +{ + return platform_driver_register(&ams_delta_panel_driver); +} + +static void ams_delta_panel_drv_cleanup(void) +{ + platform_driver_unregister(&ams_delta_panel_driver); +} + +module_init(ams_delta_panel_drv_init); +module_exit(ams_delta_panel_drv_cleanup); diff --git a/drivers/video/omap/lcd_apollon.c b/drivers/video/omap/lcd_apollon.c new file mode 100644 index 000000000000..626ae3a532ff --- /dev/null +++ b/drivers/video/omap/lcd_apollon.c @@ -0,0 +1,138 @@ +/* + * LCD panel support for the Samsung OMAP2 Apollon board + * + * Copyright (C) 2005,2006 Samsung Electronics + * Author: Kyungmin Park <kyungmin.park@samsung.com> + * + * Derived from drivers/video/omap/lcd-h4.c + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include <linux/module.h> +#include <linux/platform_device.h> + +#include <mach/gpio.h> +#include <mach/mux.h> +#include <mach/omapfb.h> + +/* #define USE_35INCH_LCD 1 */ + +static int apollon_panel_init(struct lcd_panel *panel, + struct omapfb_device *fbdev) +{ + /* configure LCD PWR_EN */ + omap_cfg_reg(M21_242X_GPIO11); + return 0; +} + +static void apollon_panel_cleanup(struct lcd_panel *panel) +{ +} + +static int apollon_panel_enable(struct lcd_panel *panel) +{ + return 0; +} + +static void apollon_panel_disable(struct lcd_panel *panel) +{ +} + +static unsigned long apollon_panel_get_caps(struct lcd_panel *panel) +{ + return 0; +} + +struct lcd_panel apollon_panel = { + .name = "apollon", + .config = OMAP_LCDC_PANEL_TFT | OMAP_LCDC_INV_VSYNC | + OMAP_LCDC_INV_HSYNC, + + .bpp = 16, + .data_lines = 18, +#ifdef USE_35INCH_LCD + .x_res = 240, + .y_res = 320, + .hsw = 2, + .hfp = 3, + .hbp = 9, + .vsw = 4, + .vfp = 3, + .vbp = 5, +#else + .x_res = 480, + .y_res = 272, + .hsw = 41, + .hfp = 2, + .hbp = 2, + .vsw = 10, + .vfp = 2, + .vbp = 2, +#endif + .pixel_clock = 6250, + + .init = apollon_panel_init, + .cleanup = apollon_panel_cleanup, + .enable = apollon_panel_enable, + .disable = apollon_panel_disable, + .get_caps = apollon_panel_get_caps, +}; + +static int apollon_panel_probe(struct platform_device *pdev) +{ + omapfb_register_panel(&apollon_panel); + return 0; +} + +static int apollon_panel_remove(struct platform_device *pdev) +{ + return 0; +} + +static int apollon_panel_suspend(struct platform_device *pdev, + pm_message_t mesg) +{ + return 0; +} + +static int apollon_panel_resume(struct platform_device *pdev) +{ + return 0; +} + +struct platform_driver apollon_panel_driver = { + .probe = apollon_panel_probe, + .remove = apollon_panel_remove, + .suspend = apollon_panel_suspend, + .resume = apollon_panel_resume, + .driver = { + .name = "apollon_lcd", + .owner = THIS_MODULE, + }, +}; + +static int __init apollon_panel_drv_init(void) +{ + return platform_driver_register(&apollon_panel_driver); +} + +static void __exit apollon_panel_drv_exit(void) +{ + platform_driver_unregister(&apollon_panel_driver); +} + +module_init(apollon_panel_drv_init); +module_exit(apollon_panel_drv_exit); diff --git a/drivers/video/omap/lcd_ldp.c b/drivers/video/omap/lcd_ldp.c new file mode 100644 index 000000000000..dbfe8974fb94 --- /dev/null +++ b/drivers/video/omap/lcd_ldp.c @@ -0,0 +1,200 @@ +/* + * LCD panel support for the TI LDP board + * + * Copyright (C) 2007 WindRiver + * Author: Stanley Miao <stanley.miao@windriver.com> + * + * Derived from drivers/video/omap/lcd-2430sdp.c + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/delay.h> +#include <linux/i2c/twl4030.h> + +#include <mach/gpio.h> +#include <mach/mux.h> +#include <mach/omapfb.h> +#include <asm/mach-types.h> + +#define LCD_PANEL_BACKLIGHT_GPIO (15 + OMAP_MAX_GPIO_LINES) +#define LCD_PANEL_ENABLE_GPIO (7 + OMAP_MAX_GPIO_LINES) + +#define LCD_PANEL_RESET_GPIO 55 +#define LCD_PANEL_QVGA_GPIO 56 + +#ifdef CONFIG_FB_OMAP_LCD_VGA +#define LCD_XRES 480 +#define LCD_YRES 640 +#define LCD_PIXCLOCK_MAX 41700 +#else +#define LCD_XRES 240 +#define LCD_YRES 320 +#define LCD_PIXCLOCK_MAX 185186 +#endif + +#define PM_RECEIVER TWL4030_MODULE_PM_RECEIVER +#define ENABLE_VAUX2_DEDICATED 0x09 +#define ENABLE_VAUX2_DEV_GRP 0x20 +#define ENABLE_VAUX3_DEDICATED 0x03 +#define ENABLE_VAUX3_DEV_GRP 0x20 + +#define ENABLE_VPLL2_DEDICATED 0x05 +#define ENABLE_VPLL2_DEV_GRP 0xE0 +#define TWL4030_VPLL2_DEV_GRP 0x33 +#define TWL4030_VPLL2_DEDICATED 0x36 + +#define t2_out(c, r, v) twl4030_i2c_write_u8(c, r, v) + + +static int ldp_panel_init(struct lcd_panel *panel, + struct omapfb_device *fbdev) +{ + gpio_request(LCD_PANEL_RESET_GPIO, "lcd reset"); + gpio_request(LCD_PANEL_QVGA_GPIO, "lcd qvga"); + gpio_request(LCD_PANEL_ENABLE_GPIO, "lcd panel"); + gpio_request(LCD_PANEL_BACKLIGHT_GPIO, "lcd backlight"); + + gpio_direction_output(LCD_PANEL_QVGA_GPIO, 0); + gpio_direction_output(LCD_PANEL_RESET_GPIO, 0); + gpio_direction_output(LCD_PANEL_ENABLE_GPIO, 0); + gpio_direction_output(LCD_PANEL_BACKLIGHT_GPIO, 0); + +#ifdef CONFIG_FB_OMAP_LCD_VGA + gpio_set_value(LCD_PANEL_QVGA_GPIO, 0); +#else + gpio_set_value(LCD_PANEL_QVGA_GPIO, 1); +#endif + gpio_set_value(LCD_PANEL_RESET_GPIO, 1); + + return 0; +} + +static void ldp_panel_cleanup(struct lcd_panel *panel) +{ + gpio_free(LCD_PANEL_BACKLIGHT_GPIO); + gpio_free(LCD_PANEL_ENABLE_GPIO); + gpio_free(LCD_PANEL_QVGA_GPIO); + gpio_free(LCD_PANEL_RESET_GPIO); +} + +static int ldp_panel_enable(struct lcd_panel *panel) +{ + if (0 != t2_out(PM_RECEIVER, ENABLE_VPLL2_DEDICATED, + TWL4030_VPLL2_DEDICATED)) + return -EIO; + if (0 != t2_out(PM_RECEIVER, ENABLE_VPLL2_DEV_GRP, + TWL4030_VPLL2_DEV_GRP)) + return -EIO; + + gpio_direction_output(LCD_PANEL_ENABLE_GPIO, 1); + gpio_direction_output(LCD_PANEL_BACKLIGHT_GPIO, 1); + + if (0 != t2_out(PM_RECEIVER, ENABLE_VAUX3_DEDICATED, + TWL4030_VAUX3_DEDICATED)) + return -EIO; + if (0 != t2_out(PM_RECEIVER, ENABLE_VAUX3_DEV_GRP, + TWL4030_VAUX3_DEV_GRP)) + return -EIO; + + return 0; +} + +static void ldp_panel_disable(struct lcd_panel *panel) +{ + gpio_direction_output(LCD_PANEL_ENABLE_GPIO, 0); + gpio_direction_output(LCD_PANEL_BACKLIGHT_GPIO, 0); + + t2_out(PM_RECEIVER, 0x0, TWL4030_VPLL2_DEDICATED); + t2_out(PM_RECEIVER, 0x0, TWL4030_VPLL2_DEV_GRP); + msleep(4); +} + +static unsigned long ldp_panel_get_caps(struct lcd_panel *panel) +{ + return 0; +} + +struct lcd_panel ldp_panel = { + .name = "ldp", + .config = OMAP_LCDC_PANEL_TFT | OMAP_LCDC_INV_VSYNC | + OMAP_LCDC_INV_HSYNC, + + .bpp = 16, + .data_lines = 18, + .x_res = LCD_XRES, + .y_res = LCD_YRES, + .hsw = 3, /* hsync_len (4) - 1 */ + .hfp = 3, /* right_margin (4) - 1 */ + .hbp = 39, /* left_margin (40) - 1 */ + .vsw = 1, /* vsync_len (2) - 1 */ + .vfp = 2, /* lower_margin */ + .vbp = 7, /* upper_margin (8) - 1 */ + + .pixel_clock = LCD_PIXCLOCK_MAX, + + .init = ldp_panel_init, + .cleanup = ldp_panel_cleanup, + .enable = ldp_panel_enable, + .disable = ldp_panel_disable, + .get_caps = ldp_panel_get_caps, +}; + +static int ldp_panel_probe(struct platform_device *pdev) +{ + omapfb_register_panel(&ldp_panel); + return 0; +} + +static int ldp_panel_remove(struct platform_device *pdev) +{ + return 0; +} + +static int ldp_panel_suspend(struct platform_device *pdev, pm_message_t mesg) +{ + return 0; +} + +static int ldp_panel_resume(struct platform_device *pdev) +{ + return 0; +} + +struct platform_driver ldp_panel_driver = { + .probe = ldp_panel_probe, + .remove = ldp_panel_remove, + .suspend = ldp_panel_suspend, + .resume = ldp_panel_resume, + .driver = { + .name = "ldp_lcd", + .owner = THIS_MODULE, + }, +}; + +static int __init ldp_panel_drv_init(void) +{ + return platform_driver_register(&ldp_panel_driver); +} + +static void __exit ldp_panel_drv_exit(void) +{ + platform_driver_unregister(&ldp_panel_driver); +} + +module_init(ldp_panel_drv_init); +module_exit(ldp_panel_drv_exit); diff --git a/drivers/video/omap/lcd_mipid.c b/drivers/video/omap/lcd_mipid.c new file mode 100644 index 000000000000..918ee8934196 --- /dev/null +++ b/drivers/video/omap/lcd_mipid.c @@ -0,0 +1,625 @@ +/* + * LCD driver for MIPI DBI-C / DCS compatible LCDs + * + * Copyright (C) 2006 Nokia Corporation + * Author: Imre Deak <imre.deak@nokia.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ +#include <linux/device.h> +#include <linux/delay.h> +#include <linux/workqueue.h> +#include <linux/spi/spi.h> + +#include <mach/omapfb.h> +#include <mach/lcd_mipid.h> + +#define MIPID_MODULE_NAME "lcd_mipid" + +#define MIPID_CMD_READ_DISP_ID 0x04 +#define MIPID_CMD_READ_RED 0x06 +#define MIPID_CMD_READ_GREEN 0x07 +#define MIPID_CMD_READ_BLUE 0x08 +#define MIPID_CMD_READ_DISP_STATUS 0x09 +#define MIPID_CMD_RDDSDR 0x0F +#define MIPID_CMD_SLEEP_IN 0x10 +#define MIPID_CMD_SLEEP_OUT 0x11 +#define MIPID_CMD_DISP_OFF 0x28 +#define MIPID_CMD_DISP_ON 0x29 + +#define MIPID_ESD_CHECK_PERIOD msecs_to_jiffies(5000) + +#define to_mipid_device(p) container_of(p, struct mipid_device, \ + panel) +struct mipid_device { + int enabled; + int revision; + unsigned int saved_bklight_level; + unsigned long hw_guard_end; /* next value of jiffies + when we can issue the + next sleep in/out command */ + unsigned long hw_guard_wait; /* max guard time in jiffies */ + + struct omapfb_device *fbdev; + struct spi_device *spi; + struct mutex mutex; + struct lcd_panel panel; + + struct workqueue_struct *esd_wq; + struct delayed_work esd_work; + void (*esd_check)(struct mipid_device *m); +}; + +static void mipid_transfer(struct mipid_device *md, int cmd, const u8 *wbuf, + int wlen, u8 *rbuf, int rlen) +{ + struct spi_message m; + struct spi_transfer *x, xfer[4]; + u16 w; + int r; + + BUG_ON(md->spi == NULL); + + spi_message_init(&m); + + memset(xfer, 0, sizeof(xfer)); + x = &xfer[0]; + + cmd &= 0xff; + x->tx_buf = &cmd; + x->bits_per_word = 9; + x->len = 2; + spi_message_add_tail(x, &m); + + if (wlen) { + x++; + x->tx_buf = wbuf; + x->len = wlen; + x->bits_per_word = 9; + spi_message_add_tail(x, &m); + } + + if (rlen) { + x++; + x->rx_buf = &w; + x->len = 1; + spi_message_add_tail(x, &m); + + if (rlen > 1) { + /* Arrange for the extra clock before the first + * data bit. + */ + x->bits_per_word = 9; + x->len = 2; + + x++; + x->rx_buf = &rbuf[1]; + x->len = rlen - 1; + spi_message_add_tail(x, &m); + } + } + + r = spi_sync(md->spi, &m); + if (r < 0) + dev_dbg(&md->spi->dev, "spi_sync %d\n", r); + + if (rlen) + rbuf[0] = w & 0xff; +} + +static inline void mipid_cmd(struct mipid_device *md, int cmd) +{ + mipid_transfer(md, cmd, NULL, 0, NULL, 0); +} + +static inline void mipid_write(struct mipid_device *md, + int reg, const u8 *buf, int len) +{ + mipid_transfer(md, reg, buf, len, NULL, 0); +} + +static inline void mipid_read(struct mipid_device *md, + int reg, u8 *buf, int len) +{ + mipid_transfer(md, reg, NULL, 0, buf, len); +} + +static void set_data_lines(struct mipid_device *md, int data_lines) +{ + u16 par; + + switch (data_lines) { + case 16: + par = 0x150; + break; + case 18: + par = 0x160; + break; + case 24: + par = 0x170; + break; + } + mipid_write(md, 0x3a, (u8 *)&par, 2); +} + +static void send_init_string(struct mipid_device *md) +{ + u16 initpar[] = { 0x0102, 0x0100, 0x0100 }; + + mipid_write(md, 0xc2, (u8 *)initpar, sizeof(initpar)); + set_data_lines(md, md->panel.data_lines); +} + +static void hw_guard_start(struct mipid_device *md, int guard_msec) +{ + md->hw_guard_wait = msecs_to_jiffies(guard_msec); + md->hw_guard_end = jiffies + md->hw_guard_wait; +} + +static void hw_guard_wait(struct mipid_device *md) +{ + unsigned long wait = md->hw_guard_end - jiffies; + + if ((long)wait > 0 && wait <= md->hw_guard_wait) { + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(wait); + } +} + +static void set_sleep_mode(struct mipid_device *md, int on) +{ + int cmd, sleep_time = 50; + + if (on) + cmd = MIPID_CMD_SLEEP_IN; + else + cmd = MIPID_CMD_SLEEP_OUT; + hw_guard_wait(md); + mipid_cmd(md, cmd); + hw_guard_start(md, 120); + /* + * When we enable the panel, it seems we _have_ to sleep + * 120 ms before sending the init string. When disabling the + * panel we'll sleep for the duration of 2 frames, so that the + * controller can still provide the PCLK,HS,VS signals. + */ + if (!on) + sleep_time = 120; + msleep(sleep_time); +} + +static void set_display_state(struct mipid_device *md, int enabled) +{ + int cmd = enabled ? MIPID_CMD_DISP_ON : MIPID_CMD_DISP_OFF; + + mipid_cmd(md, cmd); +} + +static int mipid_set_bklight_level(struct lcd_panel *panel, unsigned int level) +{ + struct mipid_device *md = to_mipid_device(panel); + struct mipid_platform_data *pd = md->spi->dev.platform_data; + + if (pd->get_bklight_max == NULL || pd->set_bklight_level == NULL) + return -ENODEV; + if (level > pd->get_bklight_max(pd)) + return -EINVAL; + if (!md->enabled) { + md->saved_bklight_level = level; + return 0; + } + pd->set_bklight_level(pd, level); + + return 0; +} + +static unsigned int mipid_get_bklight_level(struct lcd_panel *panel) +{ + struct mipid_device *md = to_mipid_device(panel); + struct mipid_platform_data *pd = md->spi->dev.platform_data; + + if (pd->get_bklight_level == NULL) + return -ENODEV; + return pd->get_bklight_level(pd); +} + +static unsigned int mipid_get_bklight_max(struct lcd_panel *panel) +{ + struct mipid_device *md = to_mipid_device(panel); + struct mipid_platform_data *pd = md->spi->dev.platform_data; + + if (pd->get_bklight_max == NULL) + return -ENODEV; + + return pd->get_bklight_max(pd); +} + +static unsigned long mipid_get_caps(struct lcd_panel *panel) +{ + return OMAPFB_CAPS_SET_BACKLIGHT; +} + +static u16 read_first_pixel(struct mipid_device *md) +{ + u16 pixel; + u8 red, green, blue; + + mutex_lock(&md->mutex); + mipid_read(md, MIPID_CMD_READ_RED, &red, 1); + mipid_read(md, MIPID_CMD_READ_GREEN, &green, 1); + mipid_read(md, MIPID_CMD_READ_BLUE, &blue, 1); + mutex_unlock(&md->mutex); + + switch (md->panel.data_lines) { + case 16: + pixel = ((red >> 1) << 11) | (green << 5) | (blue >> 1); + break; + case 24: + /* 24 bit -> 16 bit */ + pixel = ((red >> 3) << 11) | ((green >> 2) << 5) | + (blue >> 3); + break; + default: + pixel = 0; + BUG(); + } + + return pixel; +} + +static int mipid_run_test(struct lcd_panel *panel, int test_num) +{ + struct mipid_device *md = to_mipid_device(panel); + static const u16 test_values[4] = { + 0x0000, 0xffff, 0xaaaa, 0x5555, + }; + int i; + + if (test_num != MIPID_TEST_RGB_LINES) + return MIPID_TEST_INVALID; + + for (i = 0; i < ARRAY_SIZE(test_values); i++) { + int delay; + unsigned long tmo; + + omapfb_write_first_pixel(md->fbdev, test_values[i]); + tmo = jiffies + msecs_to_jiffies(100); + delay = 25; + while (1) { + u16 pixel; + + msleep(delay); + pixel = read_first_pixel(md); + if (pixel == test_values[i]) + break; + if (time_after(jiffies, tmo)) { + dev_err(&md->spi->dev, + "MIPI LCD RGB I/F test failed: " + "expecting %04x, got %04x\n", + test_values[i], pixel); + return MIPID_TEST_FAILED; + } + delay = 10; + } + } + + return 0; +} + +static void ls041y3_esd_recover(struct mipid_device *md) +{ + dev_err(&md->spi->dev, "performing LCD ESD recovery\n"); + set_sleep_mode(md, 1); + set_sleep_mode(md, 0); +} + +static void ls041y3_esd_check_mode1(struct mipid_device *md) +{ + u8 state1, state2; + + mipid_read(md, MIPID_CMD_RDDSDR, &state1, 1); + set_sleep_mode(md, 0); + mipid_read(md, MIPID_CMD_RDDSDR, &state2, 1); + dev_dbg(&md->spi->dev, "ESD mode 1 state1 %02x state2 %02x\n", + state1, state2); + /* Each sleep out command will trigger a self diagnostic and flip + * Bit6 if the test passes. + */ + if (!((state1 ^ state2) & (1 << 6))) + ls041y3_esd_recover(md); +} + +static void ls041y3_esd_check_mode2(struct mipid_device *md) +{ + int i; + u8 rbuf[2]; + static const struct { + int cmd; + int wlen; + u16 wbuf[3]; + } *rd, rd_ctrl[7] = { + { 0xb0, 4, { 0x0101, 0x01fe, } }, + { 0xb1, 4, { 0x01de, 0x0121, } }, + { 0xc2, 4, { 0x0100, 0x0100, } }, + { 0xbd, 2, { 0x0100, } }, + { 0xc2, 4, { 0x01fc, 0x0103, } }, + { 0xb4, 0, }, + { 0x00, 0, }, + }; + + rd = rd_ctrl; + for (i = 0; i < 3; i++, rd++) + mipid_write(md, rd->cmd, (u8 *)rd->wbuf, rd->wlen); + + udelay(10); + mipid_read(md, rd->cmd, rbuf, 2); + rd++; + + for (i = 0; i < 3; i++, rd++) { + udelay(10); + mipid_write(md, rd->cmd, (u8 *)rd->wbuf, rd->wlen); + } + + dev_dbg(&md->spi->dev, "ESD mode 2 state %02x\n", rbuf[1]); + if (rbuf[1] == 0x00) + ls041y3_esd_recover(md); +} + +static void ls041y3_esd_check(struct mipid_device *md) +{ + ls041y3_esd_check_mode1(md); + if (md->revision >= 0x88) + ls041y3_esd_check_mode2(md); +} + +static void mipid_esd_start_check(struct mipid_device *md) +{ + if (md->esd_check != NULL) + queue_delayed_work(md->esd_wq, &md->esd_work, + MIPID_ESD_CHECK_PERIOD); +} + +static void mipid_esd_stop_check(struct mipid_device *md) +{ + if (md->esd_check != NULL) + cancel_rearming_delayed_workqueue(md->esd_wq, &md->esd_work); +} + +static void mipid_esd_work(struct work_struct *work) +{ + struct mipid_device *md = container_of(work, struct mipid_device, + esd_work.work); + + mutex_lock(&md->mutex); + md->esd_check(md); + mutex_unlock(&md->mutex); + mipid_esd_start_check(md); +} + +static int mipid_enable(struct lcd_panel *panel) +{ + struct mipid_device *md = to_mipid_device(panel); + + mutex_lock(&md->mutex); + + if (md->enabled) { + mutex_unlock(&md->mutex); + return 0; + } + set_sleep_mode(md, 0); + md->enabled = 1; + send_init_string(md); + set_display_state(md, 1); + mipid_set_bklight_level(panel, md->saved_bklight_level); + mipid_esd_start_check(md); + + mutex_unlock(&md->mutex); + return 0; +} + +static void mipid_disable(struct lcd_panel *panel) +{ + struct mipid_device *md = to_mipid_device(panel); + + /* + * A final ESD work might be called before returning, + * so do this without holding the lock. + */ + mipid_esd_stop_check(md); + mutex_lock(&md->mutex); + + if (!md->enabled) { + mutex_unlock(&md->mutex); + return; + } + md->saved_bklight_level = mipid_get_bklight_level(panel); + mipid_set_bklight_level(panel, 0); + set_display_state(md, 0); + set_sleep_mode(md, 1); + md->enabled = 0; + + mutex_unlock(&md->mutex); +} + +static int panel_enabled(struct mipid_device *md) +{ + u32 disp_status; + int enabled; + + mipid_read(md, MIPID_CMD_READ_DISP_STATUS, (u8 *)&disp_status, 4); + disp_status = __be32_to_cpu(disp_status); + enabled = (disp_status & (1 << 17)) && (disp_status & (1 << 10)); + dev_dbg(&md->spi->dev, + "LCD panel %senabled by bootloader (status 0x%04x)\n", + enabled ? "" : "not ", disp_status); + return enabled; +} + +static int mipid_init(struct lcd_panel *panel, + struct omapfb_device *fbdev) +{ + struct mipid_device *md = to_mipid_device(panel); + + md->fbdev = fbdev; + md->esd_wq = create_singlethread_workqueue("mipid_esd"); + if (md->esd_wq == NULL) { + dev_err(&md->spi->dev, "can't create ESD workqueue\n"); + return -ENOMEM; + } + INIT_DELAYED_WORK(&md->esd_work, mipid_esd_work); + mutex_init(&md->mutex); + + md->enabled = panel_enabled(md); + + if (md->enabled) + mipid_esd_start_check(md); + else + md->saved_bklight_level = mipid_get_bklight_level(panel); + + return 0; +} + +static void mipid_cleanup(struct lcd_panel *panel) +{ + struct mipid_device *md = to_mipid_device(panel); + + if (md->enabled) + mipid_esd_stop_check(md); + destroy_workqueue(md->esd_wq); +} + +static struct lcd_panel mipid_panel = { + .config = OMAP_LCDC_PANEL_TFT, + + .bpp = 16, + .x_res = 800, + .y_res = 480, + .pixel_clock = 21940, + .hsw = 50, + .hfp = 20, + .hbp = 15, + .vsw = 2, + .vfp = 1, + .vbp = 3, + + .init = mipid_init, + .cleanup = mipid_cleanup, + .enable = mipid_enable, + .disable = mipid_disable, + .get_caps = mipid_get_caps, + .set_bklight_level = mipid_set_bklight_level, + .get_bklight_level = mipid_get_bklight_level, + .get_bklight_max = mipid_get_bklight_max, + .run_test = mipid_run_test, +}; + +static int mipid_detect(struct mipid_device *md) +{ + struct mipid_platform_data *pdata; + u8 display_id[3]; + + pdata = md->spi->dev.platform_data; + if (pdata == NULL) { + dev_err(&md->spi->dev, "missing platform data\n"); + return -ENOENT; + } + + mipid_read(md, MIPID_CMD_READ_DISP_ID, display_id, 3); + dev_dbg(&md->spi->dev, "MIPI display ID: %02x%02x%02x\n", + display_id[0], display_id[1], display_id[2]); + + switch (display_id[0]) { + case 0x45: + md->panel.name = "lph8923"; + break; + case 0x83: + md->panel.name = "ls041y3"; + md->esd_check = ls041y3_esd_check; + break; + default: + md->panel.name = "unknown"; + dev_err(&md->spi->dev, "invalid display ID\n"); + return -ENODEV; + } + + md->revision = display_id[1]; + md->panel.data_lines = pdata->data_lines; + pr_info("omapfb: %s rev %02x LCD detected, %d data lines\n", + md->panel.name, md->revision, md->panel.data_lines); + + return 0; +} + +static int mipid_spi_probe(struct spi_device *spi) +{ + struct mipid_device *md; + int r; + + md = kzalloc(sizeof(*md), GFP_KERNEL); + if (md == NULL) { + dev_err(&spi->dev, "out of memory\n"); + return -ENOMEM; + } + + spi->mode = SPI_MODE_0; + md->spi = spi; + dev_set_drvdata(&spi->dev, md); + md->panel = mipid_panel; + + r = mipid_detect(md); + if (r < 0) + return r; + + omapfb_register_panel(&md->panel); + + return 0; +} + +static int mipid_spi_remove(struct spi_device *spi) +{ + struct mipid_device *md = dev_get_drvdata(&spi->dev); + + mipid_disable(&md->panel); + kfree(md); + + return 0; +} + +static struct spi_driver mipid_spi_driver = { + .driver = { + .name = MIPID_MODULE_NAME, + .bus = &spi_bus_type, + .owner = THIS_MODULE, + }, + .probe = mipid_spi_probe, + .remove = __devexit_p(mipid_spi_remove), +}; + +static int mipid_drv_init(void) +{ + spi_register_driver(&mipid_spi_driver); + + return 0; +} +module_init(mipid_drv_init); + +static void mipid_drv_cleanup(void) +{ + spi_unregister_driver(&mipid_spi_driver); +} +module_exit(mipid_drv_cleanup); + +MODULE_DESCRIPTION("MIPI display driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/video/omap/lcd_omap2evm.c b/drivers/video/omap/lcd_omap2evm.c new file mode 100644 index 000000000000..7a2bbe2ecec3 --- /dev/null +++ b/drivers/video/omap/lcd_omap2evm.c @@ -0,0 +1,191 @@ +/* + * LCD panel support for the MISTRAL OMAP2EVM board + * + * Author: Arun C <arunedarath@mistralsolutions.com> + * + * Derived from drivers/video/omap/lcd_omap3evm.c + * Derived from drivers/video/omap/lcd-apollon.c + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/gpio.h> +#include <linux/i2c/twl4030.h> + +#include <mach/mux.h> +#include <mach/omapfb.h> +#include <asm/mach-types.h> + +#define LCD_PANEL_ENABLE_GPIO 154 +#define LCD_PANEL_LR 128 +#define LCD_PANEL_UD 129 +#define LCD_PANEL_INI 152 +#define LCD_PANEL_QVGA 148 +#define LCD_PANEL_RESB 153 + +#define TWL_LED_LEDEN 0x00 +#define TWL_PWMA_PWMAON 0x00 +#define TWL_PWMA_PWMAOFF 0x01 + +static unsigned int bklight_level; + +static int omap2evm_panel_init(struct lcd_panel *panel, + struct omapfb_device *fbdev) +{ + gpio_request(LCD_PANEL_ENABLE_GPIO, "LCD enable"); + gpio_request(LCD_PANEL_LR, "LCD lr"); + gpio_request(LCD_PANEL_UD, "LCD ud"); + gpio_request(LCD_PANEL_INI, "LCD ini"); + gpio_request(LCD_PANEL_QVGA, "LCD qvga"); + gpio_request(LCD_PANEL_RESB, "LCD resb"); + + gpio_direction_output(LCD_PANEL_ENABLE_GPIO, 1); + gpio_direction_output(LCD_PANEL_RESB, 1); + gpio_direction_output(LCD_PANEL_INI, 1); + gpio_direction_output(LCD_PANEL_QVGA, 0); + gpio_direction_output(LCD_PANEL_LR, 1); + gpio_direction_output(LCD_PANEL_UD, 1); + + twl4030_i2c_write_u8(TWL4030_MODULE_LED, 0x11, TWL_LED_LEDEN); + twl4030_i2c_write_u8(TWL4030_MODULE_PWMA, 0x01, TWL_PWMA_PWMAON); + twl4030_i2c_write_u8(TWL4030_MODULE_PWMA, 0x02, TWL_PWMA_PWMAOFF); + bklight_level = 100; + + return 0; +} + +static void omap2evm_panel_cleanup(struct lcd_panel *panel) +{ + gpio_free(LCD_PANEL_RESB); + gpio_free(LCD_PANEL_QVGA); + gpio_free(LCD_PANEL_INI); + gpio_free(LCD_PANEL_UD); + gpio_free(LCD_PANEL_LR); + gpio_free(LCD_PANEL_ENABLE_GPIO); +} + +static int omap2evm_panel_enable(struct lcd_panel *panel) +{ + gpio_set_value(LCD_PANEL_ENABLE_GPIO, 0); + return 0; +} + +static void omap2evm_panel_disable(struct lcd_panel *panel) +{ + gpio_set_value(LCD_PANEL_ENABLE_GPIO, 1); +} + +static unsigned long omap2evm_panel_get_caps(struct lcd_panel *panel) +{ + return 0; +} + +static int omap2evm_bklight_setlevel(struct lcd_panel *panel, + unsigned int level) +{ + u8 c; + if ((level >= 0) && (level <= 100)) { + c = (125 * (100 - level)) / 100 + 2; + twl4030_i2c_write_u8(TWL4030_MODULE_PWMA, c, TWL_PWMA_PWMAOFF); + bklight_level = level; + } + return 0; +} + +static unsigned int omap2evm_bklight_getlevel(struct lcd_panel *panel) +{ + return bklight_level; +} + +static unsigned int omap2evm_bklight_getmaxlevel(struct lcd_panel *panel) +{ + return 100; +} + +struct lcd_panel omap2evm_panel = { + .name = "omap2evm", + .config = OMAP_LCDC_PANEL_TFT | OMAP_LCDC_INV_VSYNC | + OMAP_LCDC_INV_HSYNC, + + .bpp = 16, + .data_lines = 18, + .x_res = 480, + .y_res = 640, + .hsw = 3, + .hfp = 0, + .hbp = 28, + .vsw = 2, + .vfp = 1, + .vbp = 0, + + .pixel_clock = 20000, + + .init = omap2evm_panel_init, + .cleanup = omap2evm_panel_cleanup, + .enable = omap2evm_panel_enable, + .disable = omap2evm_panel_disable, + .get_caps = omap2evm_panel_get_caps, + .set_bklight_level = omap2evm_bklight_setlevel, + .get_bklight_level = omap2evm_bklight_getlevel, + .get_bklight_max = omap2evm_bklight_getmaxlevel, +}; + +static int omap2evm_panel_probe(struct platform_device *pdev) +{ + omapfb_register_panel(&omap2evm_panel); + return 0; +} + +static int omap2evm_panel_remove(struct platform_device *pdev) +{ + return 0; +} + +static int omap2evm_panel_suspend(struct platform_device *pdev, + pm_message_t mesg) +{ + return 0; +} + +static int omap2evm_panel_resume(struct platform_device *pdev) +{ + return 0; +} + +struct platform_driver omap2evm_panel_driver = { + .probe = omap2evm_panel_probe, + .remove = omap2evm_panel_remove, + .suspend = omap2evm_panel_suspend, + .resume = omap2evm_panel_resume, + .driver = { + .name = "omap2evm_lcd", + .owner = THIS_MODULE, + }, +}; + +static int __init omap2evm_panel_drv_init(void) +{ + return platform_driver_register(&omap2evm_panel_driver); +} + +static void __exit omap2evm_panel_drv_exit(void) +{ + platform_driver_unregister(&omap2evm_panel_driver); +} + +module_init(omap2evm_panel_drv_init); +module_exit(omap2evm_panel_drv_exit); diff --git a/drivers/video/omap/lcd_omap3beagle.c b/drivers/video/omap/lcd_omap3beagle.c new file mode 100644 index 000000000000..4011910123bf --- /dev/null +++ b/drivers/video/omap/lcd_omap3beagle.c @@ -0,0 +1,130 @@ +/* + * LCD panel support for the TI OMAP3 Beagle board + * + * Author: Koen Kooi <koen@openembedded.org> + * + * Derived from drivers/video/omap/lcd-omap3evm.c + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/gpio.h> +#include <linux/i2c/twl4030.h> + +#include <mach/mux.h> +#include <mach/omapfb.h> +#include <asm/mach-types.h> + +#define LCD_PANEL_ENABLE_GPIO 170 + +static int omap3beagle_panel_init(struct lcd_panel *panel, + struct omapfb_device *fbdev) +{ + gpio_request(LCD_PANEL_ENABLE_GPIO, "LCD enable"); + return 0; +} + +static void omap3beagle_panel_cleanup(struct lcd_panel *panel) +{ + gpio_free(LCD_PANEL_ENABLE_GPIO); +} + +static int omap3beagle_panel_enable(struct lcd_panel *panel) +{ + gpio_set_value(LCD_PANEL_ENABLE_GPIO, 1); + return 0; +} + +static void omap3beagle_panel_disable(struct lcd_panel *panel) +{ + gpio_set_value(LCD_PANEL_ENABLE_GPIO, 0); +} + +static unsigned long omap3beagle_panel_get_caps(struct lcd_panel *panel) +{ + return 0; +} + +struct lcd_panel omap3beagle_panel = { + .name = "omap3beagle", + .config = OMAP_LCDC_PANEL_TFT, + + .bpp = 16, + .data_lines = 24, + .x_res = 1024, + .y_res = 768, + .hsw = 3, /* hsync_len (4) - 1 */ + .hfp = 3, /* right_margin (4) - 1 */ + .hbp = 39, /* left_margin (40) - 1 */ + .vsw = 1, /* vsync_len (2) - 1 */ + .vfp = 2, /* lower_margin */ + .vbp = 7, /* upper_margin (8) - 1 */ + + .pixel_clock = 64000, + + .init = omap3beagle_panel_init, + .cleanup = omap3beagle_panel_cleanup, + .enable = omap3beagle_panel_enable, + .disable = omap3beagle_panel_disable, + .get_caps = omap3beagle_panel_get_caps, +}; + +static int omap3beagle_panel_probe(struct platform_device *pdev) +{ + omapfb_register_panel(&omap3beagle_panel); + return 0; +} + +static int omap3beagle_panel_remove(struct platform_device *pdev) +{ + return 0; +} + +static int omap3beagle_panel_suspend(struct platform_device *pdev, + pm_message_t mesg) +{ + return 0; +} + +static int omap3beagle_panel_resume(struct platform_device *pdev) +{ + return 0; +} + +struct platform_driver omap3beagle_panel_driver = { + .probe = omap3beagle_panel_probe, + .remove = omap3beagle_panel_remove, + .suspend = omap3beagle_panel_suspend, + .resume = omap3beagle_panel_resume, + .driver = { + .name = "omap3beagle_lcd", + .owner = THIS_MODULE, + }, +}; + +static int __init omap3beagle_panel_drv_init(void) +{ + return platform_driver_register(&omap3beagle_panel_driver); +} + +static void __exit omap3beagle_panel_drv_exit(void) +{ + platform_driver_unregister(&omap3beagle_panel_driver); +} + +module_init(omap3beagle_panel_drv_init); +module_exit(omap3beagle_panel_drv_exit); diff --git a/drivers/video/omap/lcd_omap3evm.c b/drivers/video/omap/lcd_omap3evm.c new file mode 100644 index 000000000000..b6a4c2c57a2f --- /dev/null +++ b/drivers/video/omap/lcd_omap3evm.c @@ -0,0 +1,192 @@ +/* + * LCD panel support for the TI OMAP3 EVM board + * + * Author: Steve Sakoman <steve@sakoman.com> + * + * Derived from drivers/video/omap/lcd-apollon.c + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/gpio.h> +#include <linux/i2c/twl4030.h> + +#include <mach/mux.h> +#include <mach/omapfb.h> +#include <asm/mach-types.h> + +#define LCD_PANEL_ENABLE_GPIO 153 +#define LCD_PANEL_LR 2 +#define LCD_PANEL_UD 3 +#define LCD_PANEL_INI 152 +#define LCD_PANEL_QVGA 154 +#define LCD_PANEL_RESB 155 + +#define ENABLE_VDAC_DEDICATED 0x03 +#define ENABLE_VDAC_DEV_GRP 0x20 +#define ENABLE_VPLL2_DEDICATED 0x05 +#define ENABLE_VPLL2_DEV_GRP 0xE0 + +#define TWL_LED_LEDEN 0x00 +#define TWL_PWMA_PWMAON 0x00 +#define TWL_PWMA_PWMAOFF 0x01 + +static unsigned int bklight_level; + +static int omap3evm_panel_init(struct lcd_panel *panel, + struct omapfb_device *fbdev) +{ + gpio_request(LCD_PANEL_LR, "LCD lr"); + gpio_request(LCD_PANEL_UD, "LCD ud"); + gpio_request(LCD_PANEL_INI, "LCD ini"); + gpio_request(LCD_PANEL_RESB, "LCD resb"); + gpio_request(LCD_PANEL_QVGA, "LCD qvga"); + + gpio_direction_output(LCD_PANEL_RESB, 1); + gpio_direction_output(LCD_PANEL_INI, 1); + gpio_direction_output(LCD_PANEL_QVGA, 0); + gpio_direction_output(LCD_PANEL_LR, 1); + gpio_direction_output(LCD_PANEL_UD, 1); + + twl4030_i2c_write_u8(TWL4030_MODULE_LED, 0x11, TWL_LED_LEDEN); + twl4030_i2c_write_u8(TWL4030_MODULE_PWMA, 0x01, TWL_PWMA_PWMAON); + twl4030_i2c_write_u8(TWL4030_MODULE_PWMA, 0x02, TWL_PWMA_PWMAOFF); + bklight_level = 100; + + return 0; +} + +static void omap3evm_panel_cleanup(struct lcd_panel *panel) +{ + gpio_free(LCD_PANEL_QVGA); + gpio_free(LCD_PANEL_RESB); + gpio_free(LCD_PANEL_INI); + gpio_free(LCD_PANEL_UD); + gpio_free(LCD_PANEL_LR); +} + +static int omap3evm_panel_enable(struct lcd_panel *panel) +{ + gpio_set_value(LCD_PANEL_ENABLE_GPIO, 0); + return 0; +} + +static void omap3evm_panel_disable(struct lcd_panel *panel) +{ + gpio_set_value(LCD_PANEL_ENABLE_GPIO, 1); +} + +static unsigned long omap3evm_panel_get_caps(struct lcd_panel *panel) +{ + return 0; +} + +static int omap3evm_bklight_setlevel(struct lcd_panel *panel, + unsigned int level) +{ + u8 c; + if ((level >= 0) && (level <= 100)) { + c = (125 * (100 - level)) / 100 + 2; + twl4030_i2c_write_u8(TWL4030_MODULE_PWMA, c, TWL_PWMA_PWMAOFF); + bklight_level = level; + } + return 0; +} + +static unsigned int omap3evm_bklight_getlevel(struct lcd_panel *panel) +{ + return bklight_level; +} + +static unsigned int omap3evm_bklight_getmaxlevel(struct lcd_panel *panel) +{ + return 100; +} + +struct lcd_panel omap3evm_panel = { + .name = "omap3evm", + .config = OMAP_LCDC_PANEL_TFT | OMAP_LCDC_INV_VSYNC | + OMAP_LCDC_INV_HSYNC, + + .bpp = 16, + .data_lines = 18, + .x_res = 480, + .y_res = 640, + .hsw = 3, /* hsync_len (4) - 1 */ + .hfp = 3, /* right_margin (4) - 1 */ + .hbp = 39, /* left_margin (40) - 1 */ + .vsw = 1, /* vsync_len (2) - 1 */ + .vfp = 2, /* lower_margin */ + .vbp = 7, /* upper_margin (8) - 1 */ + + .pixel_clock = 26000, + + .init = omap3evm_panel_init, + .cleanup = omap3evm_panel_cleanup, + .enable = omap3evm_panel_enable, + .disable = omap3evm_panel_disable, + .get_caps = omap3evm_panel_get_caps, + .set_bklight_level = omap3evm_bklight_setlevel, + .get_bklight_level = omap3evm_bklight_getlevel, + .get_bklight_max = omap3evm_bklight_getmaxlevel, +}; + +static int omap3evm_panel_probe(struct platform_device *pdev) +{ + omapfb_register_panel(&omap3evm_panel); + return 0; +} + +static int omap3evm_panel_remove(struct platform_device *pdev) +{ + return 0; +} + +static int omap3evm_panel_suspend(struct platform_device *pdev, + pm_message_t mesg) +{ + return 0; +} + +static int omap3evm_panel_resume(struct platform_device *pdev) +{ + return 0; +} + +struct platform_driver omap3evm_panel_driver = { + .probe = omap3evm_panel_probe, + .remove = omap3evm_panel_remove, + .suspend = omap3evm_panel_suspend, + .resume = omap3evm_panel_resume, + .driver = { + .name = "omap3evm_lcd", + .owner = THIS_MODULE, + }, +}; + +static int __init omap3evm_panel_drv_init(void) +{ + return platform_driver_register(&omap3evm_panel_driver); +} + +static void __exit omap3evm_panel_drv_exit(void) +{ + platform_driver_unregister(&omap3evm_panel_driver); +} + +module_init(omap3evm_panel_drv_init); +module_exit(omap3evm_panel_drv_exit); diff --git a/drivers/video/omap/lcd_overo.c b/drivers/video/omap/lcd_overo.c new file mode 100644 index 000000000000..2bc5c9268e5e --- /dev/null +++ b/drivers/video/omap/lcd_overo.c @@ -0,0 +1,179 @@ +/* + * LCD panel support for the Gumstix Overo + * + * Author: Steve Sakoman <steve@sakoman.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/i2c/twl4030.h> + +#include <mach/gpio.h> +#include <mach/mux.h> +#include <mach/omapfb.h> +#include <asm/mach-types.h> + +#define LCD_ENABLE 144 + +static int overo_panel_init(struct lcd_panel *panel, + struct omapfb_device *fbdev) +{ + if ((gpio_request(LCD_ENABLE, "LCD_ENABLE") == 0) && + (gpio_direction_output(LCD_ENABLE, 1) == 0)) + gpio_export(LCD_ENABLE, 0); + else + printk(KERN_ERR "could not obtain gpio for LCD_ENABLE\n"); + + return 0; +} + +static void overo_panel_cleanup(struct lcd_panel *panel) +{ + gpio_free(LCD_ENABLE); +} + +static int overo_panel_enable(struct lcd_panel *panel) +{ + gpio_set_value(LCD_ENABLE, 1); + return 0; +} + +static void overo_panel_disable(struct lcd_panel *panel) +{ + gpio_set_value(LCD_ENABLE, 0); +} + +static unsigned long overo_panel_get_caps(struct lcd_panel *panel) +{ + return 0; +} + +struct lcd_panel overo_panel = { + .name = "overo", + .config = OMAP_LCDC_PANEL_TFT, + .bpp = 16, + .data_lines = 24, + +#if defined CONFIG_FB_OMAP_031M3R + + /* 640 x 480 @ 60 Hz Reduced blanking VESA CVT 0.31M3-R */ + .x_res = 640, + .y_res = 480, + .hfp = 48, + .hsw = 32, + .hbp = 80, + .vfp = 3, + .vsw = 4, + .vbp = 7, + .pixel_clock = 23500, + +#elif defined CONFIG_FB_OMAP_048M3R + + /* 800 x 600 @ 60 Hz Reduced blanking VESA CVT 0.48M3-R */ + .x_res = 800, + .y_res = 600, + .hfp = 48, + .hsw = 32, + .hbp = 80, + .vfp = 3, + .vsw = 4, + .vbp = 11, + .pixel_clock = 35500, + +#elif defined CONFIG_FB_OMAP_079M3R + + /* 1024 x 768 @ 60 Hz Reduced blanking VESA CVT 0.79M3-R */ + .x_res = 1024, + .y_res = 768, + .hfp = 48, + .hsw = 32, + .hbp = 80, + .vfp = 3, + .vsw = 4, + .vbp = 15, + .pixel_clock = 56000, + +#elif defined CONFIG_FB_OMAP_092M9R + + /* 1280 x 720 @ 60 Hz Reduced blanking VESA CVT 0.92M9-R */ + .x_res = 1280, + .y_res = 720, + .hfp = 48, + .hsw = 32, + .hbp = 80, + .vfp = 3, + .vsw = 5, + .vbp = 13, + .pixel_clock = 64000, + +#else + + /* use 640 x 480 if no config option */ + /* 640 x 480 @ 60 Hz Reduced blanking VESA CVT 0.31M3-R */ + .x_res = 640, + .y_res = 480, + .hfp = 48, + .hsw = 32, + .hbp = 80, + .vfp = 3, + .vsw = 4, + .vbp = 7, + .pixel_clock = 23500, + +#endif + + .init = overo_panel_init, + .cleanup = overo_panel_cleanup, + .enable = overo_panel_enable, + .disable = overo_panel_disable, + .get_caps = overo_panel_get_caps, +}; + +static int overo_panel_probe(struct platform_device *pdev) +{ + omapfb_register_panel(&overo_panel); + return 0; +} + +static int overo_panel_remove(struct platform_device *pdev) +{ + /* omapfb does not have unregister_panel */ + return 0; +} + +static struct platform_driver overo_panel_driver = { + .probe = overo_panel_probe, + .remove = overo_panel_remove, + .driver = { + .name = "overo_lcd", + .owner = THIS_MODULE, + }, +}; + +static int __init overo_panel_drv_init(void) +{ + return platform_driver_register(&overo_panel_driver); +} + +static void __exit overo_panel_drv_exit(void) +{ + platform_driver_unregister(&overo_panel_driver); +} + +module_init(overo_panel_drv_init); +module_exit(overo_panel_drv_exit); diff --git a/drivers/video/omap/omapfb_main.c b/drivers/video/omap/omapfb_main.c index 8862233d57b6..0d0c8c8b9b56 100644 --- a/drivers/video/omap/omapfb_main.c +++ b/drivers/video/omap/omapfb_main.c @@ -67,6 +67,7 @@ static struct caps_table_struct ctrl_caps[] = { { OMAPFB_CAPS_WINDOW_PIXEL_DOUBLE, "pixel double window" }, { OMAPFB_CAPS_WINDOW_SCALE, "scale window" }, { OMAPFB_CAPS_WINDOW_OVERLAY, "overlay window" }, + { OMAPFB_CAPS_WINDOW_ROTATE, "rotate window" }, { OMAPFB_CAPS_SET_BACKLIGHT, "backlight setting" }, }; @@ -215,6 +216,15 @@ static int ctrl_change_mode(struct fb_info *fbi) offset, var->xres_virtual, plane->info.pos_x, plane->info.pos_y, var->xres, var->yres, plane->color_mode); + if (r < 0) + return r; + + if (fbdev->ctrl->set_rotate != NULL) { + r = fbdev->ctrl->set_rotate(var->rotate); + if (r < 0) + return r; + } + if (fbdev->ctrl->set_scale != NULL) r = fbdev->ctrl->set_scale(plane->idx, var->xres, var->yres, @@ -383,7 +393,7 @@ static void omapfb_sync(struct fb_info *fbi) * Set fb_info.fix fields and also updates fbdev. * When calling this fb_info.var must be set up already. */ -static void set_fb_fix(struct fb_info *fbi) +static void set_fb_fix(struct fb_info *fbi, int from_init) { struct fb_fix_screeninfo *fix = &fbi->fix; struct fb_var_screeninfo *var = &fbi->var; @@ -393,10 +403,16 @@ static void set_fb_fix(struct fb_info *fbi) rg = &plane->fbdev->mem_desc.region[plane->idx]; fbi->screen_base = rg->vaddr; - mutex_lock(&fbi->mm_lock); - fix->smem_start = rg->paddr; - fix->smem_len = rg->size; - mutex_unlock(&fbi->mm_lock); + + if (!from_init) { + mutex_lock(&fbi->mm_lock); + fix->smem_start = rg->paddr; + fix->smem_len = rg->size; + mutex_unlock(&fbi->mm_lock); + } else { + fix->smem_start = rg->paddr; + fix->smem_len = rg->size; + } fix->type = FB_TYPE_PACKED_PIXELS; bpp = var->bits_per_pixel; @@ -554,7 +570,6 @@ static int set_fb_var(struct fb_info *fbi, var->xoffset = var->xres_virtual - var->xres; if (var->yres + var->yoffset > var->yres_virtual) var->yoffset = var->yres_virtual - var->yres; - line_size = var->xres * bpp / 8; if (plane->color_mode == OMAPFB_COLOR_RGB444) { var->red.offset = 8; var->red.length = 4; @@ -600,7 +615,7 @@ static void omapfb_rotate(struct fb_info *fbi, int rotate) struct omapfb_device *fbdev = plane->fbdev; omapfb_rqueue_lock(fbdev); - if (cpu_is_omap15xx() && rotate != fbi->var.rotate) { + if (rotate != fbi->var.rotate) { struct fb_var_screeninfo *new_var = &fbdev->new_var; memcpy(new_var, &fbi->var, sizeof(*new_var)); @@ -695,7 +710,7 @@ static int omapfb_set_par(struct fb_info *fbi) int r = 0; omapfb_rqueue_lock(fbdev); - set_fb_fix(fbi); + set_fb_fix(fbi, 0); r = ctrl_change_mode(fbi); omapfb_rqueue_unlock(fbdev); @@ -707,28 +722,42 @@ int omapfb_update_window_async(struct fb_info *fbi, void (*callback)(void *), void *callback_data) { + int xres, yres; struct omapfb_plane_struct *plane = fbi->par; struct omapfb_device *fbdev = plane->fbdev; - struct fb_var_screeninfo *var; + struct fb_var_screeninfo *var = &fbi->var; - var = &fbi->var; - if (win->x >= var->xres || win->y >= var->yres || - win->out_x > var->xres || win->out_y >= var->yres) + switch (var->rotate) { + case 0: + case 180: + xres = fbdev->panel->x_res; + yres = fbdev->panel->y_res; + break; + case 90: + case 270: + xres = fbdev->panel->y_res; + yres = fbdev->panel->x_res; + break; + default: + return -EINVAL; + } + + if (win->x >= xres || win->y >= yres || + win->out_x > xres || win->out_y > yres) return -EINVAL; if (!fbdev->ctrl->update_window || fbdev->ctrl->get_update_mode() != OMAPFB_MANUAL_UPDATE) return -ENODEV; - if (win->x + win->width >= var->xres) - win->width = var->xres - win->x; - if (win->y + win->height >= var->yres) - win->height = var->yres - win->y; - /* The out sizes should be cropped to the LCD size */ - if (win->out_x + win->out_width > fbdev->panel->x_res) - win->out_width = fbdev->panel->x_res - win->out_x; - if (win->out_y + win->out_height > fbdev->panel->y_res) - win->out_height = fbdev->panel->y_res - win->out_y; + if (win->x + win->width > xres) + win->width = xres - win->x; + if (win->y + win->height > yres) + win->height = yres - win->y; + if (win->out_x + win->out_width > xres) + win->out_width = xres - win->out_x; + if (win->out_y + win->out_height > yres) + win->out_height = yres - win->out_y; if (!win->width || !win->height || !win->out_width || !win->out_height) return 0; @@ -881,7 +910,7 @@ static int omapfb_setup_mem(struct fb_info *fbi, struct omapfb_mem_info *mi) if (old_size != size) { if (size) { memcpy(&fbi->var, new_var, sizeof(fbi->var)); - set_fb_fix(fbi); + set_fb_fix(fbi, 0); } else { /* * Set these explicitly to indicate that the @@ -1481,7 +1510,7 @@ static int fbinfo_init(struct omapfb_device *fbdev, struct fb_info *info) var->bits_per_pixel = fbdev->panel->bpp; set_fb_var(info, var); - set_fb_fix(info); + set_fb_fix(info, 1); r = fb_alloc_cmap(&info->cmap, 16, 0); if (r != 0) @@ -1699,8 +1728,8 @@ static int omapfb_do_probe(struct platform_device *pdev, pr_info("omapfb: configured for panel %s\n", fbdev->panel->name); - def_vxres = def_vxres ? : fbdev->panel->x_res; - def_vyres = def_vyres ? : fbdev->panel->y_res; + def_vxres = def_vxres ? def_vxres : fbdev->panel->x_res; + def_vyres = def_vyres ? def_vyres : fbdev->panel->y_res; init_state++; @@ -1822,8 +1851,8 @@ static int omapfb_suspend(struct platform_device *pdev, pm_message_t mesg) { struct omapfb_device *fbdev = platform_get_drvdata(pdev); - omapfb_blank(FB_BLANK_POWERDOWN, fbdev->fb_info[0]); - + if (fbdev != NULL) + omapfb_blank(FB_BLANK_POWERDOWN, fbdev->fb_info[0]); return 0; } @@ -1832,7 +1861,8 @@ static int omapfb_resume(struct platform_device *pdev) { struct omapfb_device *fbdev = platform_get_drvdata(pdev); - omapfb_blank(FB_BLANK_UNBLANK, fbdev->fb_info[0]); + if (fbdev != NULL) + omapfb_blank(FB_BLANK_UNBLANK, fbdev->fb_info[0]); return 0; } diff --git a/drivers/video/omap/rfbi.c b/drivers/video/omap/rfbi.c index 9332d6ca6456..ee01e84e19c1 100644 --- a/drivers/video/omap/rfbi.c +++ b/drivers/video/omap/rfbi.c @@ -57,6 +57,7 @@ #define DISPC_BASE 0x48050400 #define DISPC_CONTROL 0x0040 +#define DISPC_IRQ_FRAMEMASK 0x0001 static struct { void __iomem *base; @@ -553,7 +554,9 @@ static int rfbi_init(struct omapfb_device *fbdev) l = (0x01 << 2); rfbi_write_reg(RFBI_CONTROL, l); - if ((r = omap_dispc_request_irq(rfbi_dma_callback, NULL)) < 0) { + r = omap_dispc_request_irq(DISPC_IRQ_FRAMEMASK, rfbi_dma_callback, + NULL); + if (r < 0) { dev_err(fbdev->dev, "can't get DISPC irq\n"); rfbi_enable_clocks(0); return r; @@ -570,7 +573,7 @@ static int rfbi_init(struct omapfb_device *fbdev) static void rfbi_cleanup(void) { - omap_dispc_free_irq(); + omap_dispc_free_irq(DISPC_IRQ_FRAMEMASK, rfbi_dma_callback, NULL); rfbi_put_clocks(); iounmap(rfbi.base); } diff --git a/drivers/video/platinumfb.c b/drivers/video/platinumfb.c index bacfabd9ce16..0a366d86f08e 100644 --- a/drivers/video/platinumfb.c +++ b/drivers/video/platinumfb.c @@ -223,10 +223,14 @@ static int platinumfb_setcolreg(u_int regno, u_int red, u_int green, u_int blue, static inline int platinum_vram_reqd(int video_mode, int color_mode) { - return vmode_attrs[video_mode-1].vres * - (vmode_attrs[video_mode-1].hres * (1<<color_mode) + - ((video_mode == VMODE_832_624_75) && - (color_mode > CMODE_8)) ? 0x10 : 0x20) + 0x1000; + int baseval = vmode_attrs[video_mode-1].hres * (1<<color_mode); + + if ((video_mode == VMODE_832_624_75) && (color_mode > CMODE_8)) + baseval += 0x10; + else + baseval += 0x20; + + return vmode_attrs[video_mode-1].vres * baseval + 0x1000; } #define STORE_D2(a, d) { \ diff --git a/drivers/video/pxafb.c b/drivers/video/pxafb.c index 6506117c134b..1820c4a24434 100644 --- a/drivers/video/pxafb.c +++ b/drivers/video/pxafb.c @@ -1638,24 +1638,26 @@ pxafb_freq_policy(struct notifier_block *nb, unsigned long val, void *data) * Power management hooks. Note that we won't be called from IRQ context, * unlike the blank functions above, so we may sleep. */ -static int pxafb_suspend(struct platform_device *dev, pm_message_t state) +static int pxafb_suspend(struct device *dev) { - struct pxafb_info *fbi = platform_get_drvdata(dev); + struct pxafb_info *fbi = dev_get_drvdata(dev); set_ctrlr_state(fbi, C_DISABLE_PM); return 0; } -static int pxafb_resume(struct platform_device *dev) +static int pxafb_resume(struct device *dev) { - struct pxafb_info *fbi = platform_get_drvdata(dev); + struct pxafb_info *fbi = dev_get_drvdata(dev); set_ctrlr_state(fbi, C_ENABLE_PM); return 0; } -#else -#define pxafb_suspend NULL -#define pxafb_resume NULL + +static struct dev_pm_ops pxafb_pm_ops = { + .suspend = pxafb_suspend, + .resume = pxafb_resume, +}; #endif static int __devinit pxafb_init_video_memory(struct pxafb_info *fbi) @@ -2081,6 +2083,9 @@ static int __devinit pxafb_probe(struct platform_device *dev) goto failed; } + if (cpu_is_pxa3xx() && inf->acceleration_enabled) + fbi->fb.fix.accel = FB_ACCEL_PXA3XX; + fbi->backlight_power = inf->pxafb_backlight_power; fbi->lcd_power = inf->pxafb_lcd_power; @@ -2091,14 +2096,14 @@ static int __devinit pxafb_probe(struct platform_device *dev) goto failed_fbi; } - r = request_mem_region(r->start, r->end - r->start + 1, dev->name); + r = request_mem_region(r->start, resource_size(r), dev->name); if (r == NULL) { dev_err(&dev->dev, "failed to request I/O memory\n"); ret = -EBUSY; goto failed_fbi; } - fbi->mmio_base = ioremap(r->start, r->end - r->start + 1); + fbi->mmio_base = ioremap(r->start, resource_size(r)); if (fbi->mmio_base == NULL) { dev_err(&dev->dev, "failed to map I/O memory\n"); ret = -EBUSY; @@ -2197,7 +2202,7 @@ failed_free_dma: failed_free_io: iounmap(fbi->mmio_base); failed_free_res: - release_mem_region(r->start, r->end - r->start + 1); + release_mem_region(r->start, resource_size(r)); failed_fbi: clk_put(fbi->clk); platform_set_drvdata(dev, NULL); @@ -2237,7 +2242,7 @@ static int __devexit pxafb_remove(struct platform_device *dev) iounmap(fbi->mmio_base); r = platform_get_resource(dev, IORESOURCE_MEM, 0); - release_mem_region(r->start, r->end - r->start + 1); + release_mem_region(r->start, resource_size(r)); clk_put(fbi->clk); kfree(fbi); @@ -2248,11 +2253,12 @@ static int __devexit pxafb_remove(struct platform_device *dev) static struct platform_driver pxafb_driver = { .probe = pxafb_probe, .remove = __devexit_p(pxafb_remove), - .suspend = pxafb_suspend, - .resume = pxafb_resume, .driver = { .owner = THIS_MODULE, .name = "pxa2xx-fb", +#ifdef CONFIG_PM + .pm = &pxafb_pm_ops, +#endif }, }; diff --git a/drivers/video/s3c-fb.c b/drivers/video/s3c-fb.c index 5a72083dc67c..adf9632c6b1f 100644 --- a/drivers/video/s3c-fb.c +++ b/drivers/video/s3c-fb.c @@ -1036,7 +1036,7 @@ static int s3c_fb_resume(struct platform_device *pdev) static struct platform_driver s3c_fb_driver = { .probe = s3c_fb_probe, - .remove = s3c_fb_remove, + .remove = __devexit_p(s3c_fb_remove), .suspend = s3c_fb_suspend, .resume = s3c_fb_resume, .driver = { diff --git a/drivers/video/s3c2410fb.c b/drivers/video/s3c2410fb.c index 5ffca2adc6a8..aac661225c78 100644 --- a/drivers/video/s3c2410fb.c +++ b/drivers/video/s3c2410fb.c @@ -369,7 +369,9 @@ static void s3c2410fb_activate_var(struct fb_info *info) void __iomem *regs = fbi->io; int type = fbi->regs.lcdcon1 & S3C2410_LCDCON1_TFT; struct fb_var_screeninfo *var = &info->var; - int clkdiv = s3c2410fb_calc_pixclk(fbi, var->pixclock) / 2; + int clkdiv; + + clkdiv = DIV_ROUND_UP(s3c2410fb_calc_pixclk(fbi, var->pixclock), 2); dprintk("%s: var->xres = %d\n", __func__, var->xres); dprintk("%s: var->yres = %d\n", __func__, var->yres); diff --git a/drivers/video/savage/savagefb_driver.c b/drivers/video/savage/savagefb_driver.c index 37b135d5d12e..842d157e1025 100644 --- a/drivers/video/savage/savagefb_driver.c +++ b/drivers/video/savage/savagefb_driver.c @@ -1565,7 +1565,7 @@ static int savagefb_blank(int blank, struct fb_info *info) vga_out8(0x3c5, sr8, par); vga_out8(0x3c4, 0x0d, par); srd = vga_in8(0x3c5, par); - srd &= 0x03; + srd &= 0x50; switch (blank) { case FB_BLANK_UNBLANK: @@ -1606,22 +1606,6 @@ static int savagefb_blank(int blank, struct fb_info *info) return (blank == FB_BLANK_NORMAL) ? 1 : 0; } -static void savagefb_save_state(struct fb_info *info) -{ - struct savagefb_par *par = info->par; - - savage_get_default_par(par, &par->save); -} - -static void savagefb_restore_state(struct fb_info *info) -{ - struct savagefb_par *par = info->par; - - savagefb_blank(FB_BLANK_POWERDOWN, info); - savage_set_default_par(par, &par->save); - savagefb_blank(FB_BLANK_UNBLANK, info); -} - static int savagefb_open(struct fb_info *info, int user) { struct savagefb_par *par = info->par; @@ -1667,8 +1651,6 @@ static struct fb_ops savagefb_ops = { .fb_setcolreg = savagefb_setcolreg, .fb_pan_display = savagefb_pan_display, .fb_blank = savagefb_blank, - .fb_save_state = savagefb_save_state, - .fb_restore_state = savagefb_restore_state, #if defined(CONFIG_FB_SAVAGE_ACCEL) .fb_fillrect = savagefb_fillrect, .fb_copyarea = savagefb_copyarea, diff --git a/drivers/video/sis/sis_main.c b/drivers/video/sis/sis_main.c index 4a067f0d0ceb..a4e05e4d7501 100644 --- a/drivers/video/sis/sis_main.c +++ b/drivers/video/sis/sis_main.c @@ -698,8 +698,8 @@ sisfb_search_refresh_rate(struct sis_video_info *ivideo, unsigned int rate, int rate, sisfb_vrate[i].refresh); ivideo->rate_idx = sisfb_vrate[i].idx; ivideo->refresh_rate = sisfb_vrate[i].refresh; - } else if(((rate - sisfb_vrate[i-1].refresh) <= 2) - && (sisfb_vrate[i].idx != 1)) { + } else if((sisfb_vrate[i].idx != 1) && + ((rate - sisfb_vrate[i-1].refresh) <= 2)) { DPRINTK("sisfb: Adjusting rate from %d down to %d\n", rate, sisfb_vrate[i-1].refresh); ivideo->rate_idx = sisfb_vrate[i-1].idx; diff --git a/drivers/video/sis/vstruct.h b/drivers/video/sis/vstruct.h index 705c85360526..bef4aae388d0 100644 --- a/drivers/video/sis/vstruct.h +++ b/drivers/video/sis/vstruct.h @@ -342,7 +342,7 @@ struct SiS_Private unsigned short SiS_RY4COE; unsigned short SiS_LCDHDES; unsigned short SiS_LCDVDES; - unsigned short SiS_DDC_Port; + SISIOADDRESS SiS_DDC_Port; unsigned short SiS_DDC_Index; unsigned short SiS_DDC_Data; unsigned short SiS_DDC_NData; diff --git a/drivers/video/tmiofb.c b/drivers/video/tmiofb.c index a1eb0862255b..6913fe168c25 100644 --- a/drivers/video/tmiofb.c +++ b/drivers/video/tmiofb.c @@ -974,7 +974,7 @@ static int tmiofb_resume(struct platform_device *dev) { struct fb_info *info = platform_get_drvdata(dev); struct mfd_cell *cell = dev->dev.platform_data; - int retval; + int retval = 0; acquire_console_sem(); diff --git a/drivers/video/uvesafb.c b/drivers/video/uvesafb.c index e98baf6916b8..54fbb2995a5f 100644 --- a/drivers/video/uvesafb.c +++ b/drivers/video/uvesafb.c @@ -67,11 +67,14 @@ static DEFINE_MUTEX(uvfb_lock); * find the kernel part of the task struct, copy the registers and * the buffer contents and then complete the task. */ -static void uvesafb_cn_callback(struct cn_msg *msg) +static void uvesafb_cn_callback(struct cn_msg *msg, struct netlink_skb_parms *nsp) { struct uvesafb_task *utask; struct uvesafb_ktask *task; + if (!cap_raised(nsp->eff_cap, CAP_SYS_ADMIN)) + return; + if (msg->seq >= UVESAFB_TASKS_MAX) return; @@ -1408,23 +1411,6 @@ static int uvesafb_check_var(struct fb_var_screeninfo *var, return 0; } -static void uvesafb_save_state(struct fb_info *info) -{ - struct uvesafb_par *par = info->par; - - if (par->vbe_state_saved) - kfree(par->vbe_state_saved); - - par->vbe_state_saved = uvesafb_vbe_state_save(par); -} - -static void uvesafb_restore_state(struct fb_info *info) -{ - struct uvesafb_par *par = info->par; - - uvesafb_vbe_state_restore(par, par->vbe_state_saved); -} - static struct fb_ops uvesafb_ops = { .owner = THIS_MODULE, .fb_open = uvesafb_open, @@ -1438,8 +1424,6 @@ static struct fb_ops uvesafb_ops = { .fb_imageblit = cfb_imageblit, .fb_check_var = uvesafb_check_var, .fb_set_par = uvesafb_set_par, - .fb_save_state = uvesafb_save_state, - .fb_restore_state = uvesafb_restore_state, }; static void __devinit uvesafb_init_info(struct fb_info *info, @@ -1456,15 +1440,6 @@ static void __devinit uvesafb_init_info(struct fb_info *info, info->fix.ypanstep = par->ypan ? 1 : 0; info->fix.ywrapstep = (par->ypan > 1) ? 1 : 0; - /* - * If we were unable to get the state buffer size, disable - * functions for saving and restoring the hardware state. - */ - if (par->vbe_state_size == 0) { - info->fbops->fb_save_state = NULL; - info->fbops->fb_restore_state = NULL; - } - /* Disable blanking if the user requested so. */ if (!blank) info->fbops->fb_blank = NULL; diff --git a/drivers/video/via/accel.c b/drivers/video/via/accel.c index 45c54bfe99bb..9d4f3a49ba4a 100644 --- a/drivers/video/via/accel.c +++ b/drivers/video/via/accel.c @@ -20,229 +20,430 @@ */ #include "global.h" -void viafb_init_accel(void) +static int hw_bitblt_1(void __iomem *engine, u8 op, u32 width, u32 height, + u8 dst_bpp, u32 dst_addr, u32 dst_pitch, u32 dst_x, u32 dst_y, + u32 *src_mem, u32 src_addr, u32 src_pitch, u32 src_x, u32 src_y, + u32 fg_color, u32 bg_color, u8 fill_rop) { - viaparinfo->fbmem_free -= CURSOR_SIZE; - viaparinfo->cursor_start = viaparinfo->fbmem_free; - viaparinfo->fbmem_used += CURSOR_SIZE; + u32 ge_cmd = 0, tmp, i; - /* Reverse 8*1024 memory space for cursor image */ - viaparinfo->fbmem_free -= (CURSOR_SIZE + VQ_SIZE); - viaparinfo->VQ_start = viaparinfo->fbmem_free; - viaparinfo->VQ_end = viaparinfo->VQ_start + VQ_SIZE - 1; - viaparinfo->fbmem_used += (CURSOR_SIZE + VQ_SIZE); } - -void viafb_init_2d_engine(void) -{ - u32 dwVQStartAddr, dwVQEndAddr; - u32 dwVQLen, dwVQStartL, dwVQEndL, dwVQStartEndH; - - /* init 2D engine regs to reset 2D engine */ - writel(0x0, viaparinfo->io_virt + VIA_REG_GEMODE); - writel(0x0, viaparinfo->io_virt + VIA_REG_SRCPOS); - writel(0x0, viaparinfo->io_virt + VIA_REG_DSTPOS); - writel(0x0, viaparinfo->io_virt + VIA_REG_DIMENSION); - writel(0x0, viaparinfo->io_virt + VIA_REG_PATADDR); - writel(0x0, viaparinfo->io_virt + VIA_REG_FGCOLOR); - writel(0x0, viaparinfo->io_virt + VIA_REG_BGCOLOR); - writel(0x0, viaparinfo->io_virt + VIA_REG_CLIPTL); - writel(0x0, viaparinfo->io_virt + VIA_REG_CLIPBR); - writel(0x0, viaparinfo->io_virt + VIA_REG_OFFSET); - writel(0x0, viaparinfo->io_virt + VIA_REG_KEYCONTROL); - writel(0x0, viaparinfo->io_virt + VIA_REG_SRCBASE); - writel(0x0, viaparinfo->io_virt + VIA_REG_DSTBASE); - writel(0x0, viaparinfo->io_virt + VIA_REG_PITCH); - writel(0x0, viaparinfo->io_virt + VIA_REG_MONOPAT1); - - /* Init AGP and VQ regs */ - switch (viaparinfo->chip_info->gfx_chip_name) { - case UNICHROME_K8M890: - case UNICHROME_P4M900: - writel(0x00100000, viaparinfo->io_virt + VIA_REG_CR_TRANSET); - writel(0x680A0000, viaparinfo->io_virt + VIA_REG_CR_TRANSPACE); - writel(0x02000000, viaparinfo->io_virt + VIA_REG_CR_TRANSPACE); - break; + if (!op || op > 3) { + printk(KERN_WARNING "hw_bitblt_1: Invalid operation: %d\n", op); + return -EINVAL; + } - default: - writel(0x00100000, viaparinfo->io_virt + VIA_REG_TRANSET); - writel(0x00000000, viaparinfo->io_virt + VIA_REG_TRANSPACE); - writel(0x00333004, viaparinfo->io_virt + VIA_REG_TRANSPACE); - writel(0x60000000, viaparinfo->io_virt + VIA_REG_TRANSPACE); - writel(0x61000000, viaparinfo->io_virt + VIA_REG_TRANSPACE); - writel(0x62000000, viaparinfo->io_virt + VIA_REG_TRANSPACE); - writel(0x63000000, viaparinfo->io_virt + VIA_REG_TRANSPACE); - writel(0x64000000, viaparinfo->io_virt + VIA_REG_TRANSPACE); - writel(0x7D000000, viaparinfo->io_virt + VIA_REG_TRANSPACE); - - writel(0xFE020000, viaparinfo->io_virt + VIA_REG_TRANSET); - writel(0x00000000, viaparinfo->io_virt + VIA_REG_TRANSPACE); - break; + if (op != VIA_BITBLT_FILL && !src_mem && src_addr == dst_addr) { + if (src_x < dst_x) { + ge_cmd |= 0x00008000; + src_x += width - 1; + dst_x += width - 1; + } + if (src_y < dst_y) { + ge_cmd |= 0x00004000; + src_y += height - 1; + dst_y += height - 1; + } } - if (viaparinfo->VQ_start != 0) { - /* Enable VQ */ - dwVQStartAddr = viaparinfo->VQ_start; - dwVQEndAddr = viaparinfo->VQ_end; - - dwVQStartL = 0x50000000 | (dwVQStartAddr & 0xFFFFFF); - dwVQEndL = 0x51000000 | (dwVQEndAddr & 0xFFFFFF); - dwVQStartEndH = 0x52000000 | - ((dwVQStartAddr & 0xFF000000) >> 24) | - ((dwVQEndAddr & 0xFF000000) >> 16); - dwVQLen = 0x53000000 | (VQ_SIZE >> 3); - switch (viaparinfo->chip_info->gfx_chip_name) { - case UNICHROME_K8M890: - case UNICHROME_P4M900: - dwVQStartL |= 0x20000000; - dwVQEndL |= 0x20000000; - dwVQStartEndH |= 0x20000000; - dwVQLen |= 0x20000000; + + if (op == VIA_BITBLT_FILL) { + switch (fill_rop) { + case 0x00: /* blackness */ + case 0x5A: /* pattern inversion */ + case 0xF0: /* pattern copy */ + case 0xFF: /* whiteness */ break; default: - break; + printk(KERN_WARNING "hw_bitblt_1: Invalid fill rop: " + "%u\n", fill_rop); + return -EINVAL; } + } - switch (viaparinfo->chip_info->gfx_chip_name) { - case UNICHROME_K8M890: - case UNICHROME_P4M900: - writel(0x00100000, - viaparinfo->io_virt + VIA_REG_CR_TRANSET); - writel(dwVQStartEndH, - viaparinfo->io_virt + VIA_REG_CR_TRANSPACE); - writel(dwVQStartL, - viaparinfo->io_virt + VIA_REG_CR_TRANSPACE); - writel(dwVQEndL, - viaparinfo->io_virt + VIA_REG_CR_TRANSPACE); - writel(dwVQLen, - viaparinfo->io_virt + VIA_REG_CR_TRANSPACE); - writel(0x74301001, - viaparinfo->io_virt + VIA_REG_CR_TRANSPACE); - writel(0x00000000, - viaparinfo->io_virt + VIA_REG_CR_TRANSPACE); - break; - default: - writel(0x00FE0000, - viaparinfo->io_virt + VIA_REG_TRANSET); - writel(0x080003FE, - viaparinfo->io_virt + VIA_REG_TRANSPACE); - writel(0x0A00027C, - viaparinfo->io_virt + VIA_REG_TRANSPACE); - writel(0x0B000260, - viaparinfo->io_virt + VIA_REG_TRANSPACE); - writel(0x0C000274, - viaparinfo->io_virt + VIA_REG_TRANSPACE); - writel(0x0D000264, - viaparinfo->io_virt + VIA_REG_TRANSPACE); - writel(0x0E000000, - viaparinfo->io_virt + VIA_REG_TRANSPACE); - writel(0x0F000020, - viaparinfo->io_virt + VIA_REG_TRANSPACE); - writel(0x1000027E, - viaparinfo->io_virt + VIA_REG_TRANSPACE); - writel(0x110002FE, - viaparinfo->io_virt + VIA_REG_TRANSPACE); - writel(0x200F0060, - viaparinfo->io_virt + VIA_REG_TRANSPACE); - - writel(0x00000006, - viaparinfo->io_virt + VIA_REG_TRANSPACE); - writel(0x40008C0F, - viaparinfo->io_virt + VIA_REG_TRANSPACE); - writel(0x44000000, - viaparinfo->io_virt + VIA_REG_TRANSPACE); - writel(0x45080C04, - viaparinfo->io_virt + VIA_REG_TRANSPACE); - writel(0x46800408, - viaparinfo->io_virt + VIA_REG_TRANSPACE); - - writel(dwVQStartEndH, - viaparinfo->io_virt + VIA_REG_TRANSPACE); - writel(dwVQStartL, - viaparinfo->io_virt + VIA_REG_TRANSPACE); - writel(dwVQEndL, - viaparinfo->io_virt + VIA_REG_TRANSPACE); - writel(dwVQLen, - viaparinfo->io_virt + VIA_REG_TRANSPACE); - break; + switch (dst_bpp) { + case 8: + tmp = 0x00000000; + break; + case 16: + tmp = 0x00000100; + break; + case 32: + tmp = 0x00000300; + break; + default: + printk(KERN_WARNING "hw_bitblt_1: Unsupported bpp %d\n", + dst_bpp); + return -EINVAL; + } + writel(tmp, engine + 0x04); + + if (op != VIA_BITBLT_FILL) { + if (src_x & (op == VIA_BITBLT_MONO ? 0xFFFF8000 : 0xFFFFF000) + || src_y & 0xFFFFF000) { + printk(KERN_WARNING "hw_bitblt_1: Unsupported source " + "x/y %d %d\n", src_x, src_y); + return -EINVAL; } - } else { - /* Disable VQ */ - switch (viaparinfo->chip_info->gfx_chip_name) { - case UNICHROME_K8M890: - case UNICHROME_P4M900: - writel(0x00100000, - viaparinfo->io_virt + VIA_REG_CR_TRANSET); - writel(0x74301000, - viaparinfo->io_virt + VIA_REG_CR_TRANSPACE); - break; - default: - writel(0x00FE0000, - viaparinfo->io_virt + VIA_REG_TRANSET); - writel(0x00000004, - viaparinfo->io_virt + VIA_REG_TRANSPACE); - writel(0x40008C0F, - viaparinfo->io_virt + VIA_REG_TRANSPACE); - writel(0x44000000, - viaparinfo->io_virt + VIA_REG_TRANSPACE); - writel(0x45080C04, - viaparinfo->io_virt + VIA_REG_TRANSPACE); - writel(0x46800408, - viaparinfo->io_virt + VIA_REG_TRANSPACE); - break; + tmp = src_x | (src_y << 16); + writel(tmp, engine + 0x08); + } + + if (dst_x & 0xFFFFF000 || dst_y & 0xFFFFF000) { + printk(KERN_WARNING "hw_bitblt_1: Unsupported destination x/y " + "%d %d\n", dst_x, dst_y); + return -EINVAL; + } + tmp = dst_x | (dst_y << 16); + writel(tmp, engine + 0x0C); + + if ((width - 1) & 0xFFFFF000 || (height - 1) & 0xFFFFF000) { + printk(KERN_WARNING "hw_bitblt_1: Unsupported width/height " + "%d %d\n", width, height); + return -EINVAL; + } + tmp = (width - 1) | ((height - 1) << 16); + writel(tmp, engine + 0x10); + + if (op != VIA_BITBLT_COLOR) + writel(fg_color, engine + 0x18); + + if (op == VIA_BITBLT_MONO) + writel(bg_color, engine + 0x1C); + + if (op != VIA_BITBLT_FILL) { + tmp = src_mem ? 0 : src_addr; + if (dst_addr & 0xE0000007) { + printk(KERN_WARNING "hw_bitblt_1: Unsupported source " + "address %X\n", tmp); + return -EINVAL; } + tmp >>= 3; + writel(tmp, engine + 0x30); + } + + if (dst_addr & 0xE0000007) { + printk(KERN_WARNING "hw_bitblt_1: Unsupported destination " + "address %X\n", dst_addr); + return -EINVAL; } + tmp = dst_addr >> 3; + writel(tmp, engine + 0x34); - viafb_set_2d_color_depth(viaparinfo->bpp); + if (op == VIA_BITBLT_FILL) + tmp = 0; + else + tmp = src_pitch; + if (tmp & 0xFFFFC007 || dst_pitch & 0xFFFFC007) { + printk(KERN_WARNING "hw_bitblt_1: Unsupported pitch %X %X\n", + tmp, dst_pitch); + return -EINVAL; + } + tmp = (tmp >> 3) | (dst_pitch << (16 - 3)); + writel(tmp, engine + 0x38); + + if (op == VIA_BITBLT_FILL) + ge_cmd |= fill_rop << 24 | 0x00002000 | 0x00000001; + else { + ge_cmd |= 0xCC000000; /* ROP=SRCCOPY */ + if (src_mem) + ge_cmd |= 0x00000040; + if (op == VIA_BITBLT_MONO) + ge_cmd |= 0x00000002 | 0x00000100 | 0x00020000; + else + ge_cmd |= 0x00000001; + } + writel(ge_cmd, engine); - writel(0x0, viaparinfo->io_virt + VIA_REG_SRCBASE); - writel(0x0, viaparinfo->io_virt + VIA_REG_DSTBASE); + if (op == VIA_BITBLT_FILL || !src_mem) + return 0; - writel(VIA_PITCH_ENABLE | - (((viaparinfo->hres * - viaparinfo->bpp >> 3) >> 3) | (((viaparinfo->hres * - viaparinfo-> - bpp >> 3) >> 3) << 16)), - viaparinfo->io_virt + VIA_REG_PITCH); + tmp = (width * height * (op == VIA_BITBLT_MONO ? 1 : (dst_bpp >> 3)) + + 3) >> 2; + + for (i = 0; i < tmp; i++) + writel(src_mem[i], engine + VIA_MMIO_BLTBASE); + + return 0; } -void viafb_set_2d_color_depth(int bpp) +static int hw_bitblt_2(void __iomem *engine, u8 op, u32 width, u32 height, + u8 dst_bpp, u32 dst_addr, u32 dst_pitch, u32 dst_x, u32 dst_y, + u32 *src_mem, u32 src_addr, u32 src_pitch, u32 src_x, u32 src_y, + u32 fg_color, u32 bg_color, u8 fill_rop) { - u32 dwGEMode; + u32 ge_cmd = 0, tmp, i; + + if (!op || op > 3) { + printk(KERN_WARNING "hw_bitblt_2: Invalid operation: %d\n", op); + return -EINVAL; + } - dwGEMode = readl(viaparinfo->io_virt + 0x04) & 0xFFFFFCFF; + if (op != VIA_BITBLT_FILL && !src_mem && src_addr == dst_addr) { + if (src_x < dst_x) { + ge_cmd |= 0x00008000; + src_x += width - 1; + dst_x += width - 1; + } + if (src_y < dst_y) { + ge_cmd |= 0x00004000; + src_y += height - 1; + dst_y += height - 1; + } + } - switch (bpp) { + if (op == VIA_BITBLT_FILL) { + switch (fill_rop) { + case 0x00: /* blackness */ + case 0x5A: /* pattern inversion */ + case 0xF0: /* pattern copy */ + case 0xFF: /* whiteness */ + break; + default: + printk(KERN_WARNING "hw_bitblt_2: Invalid fill rop: " + "%u\n", fill_rop); + return -EINVAL; + } + } + + switch (dst_bpp) { + case 8: + tmp = 0x00000000; + break; case 16: - dwGEMode |= VIA_GEM_16bpp; + tmp = 0x00000100; break; case 32: - dwGEMode |= VIA_GEM_32bpp; + tmp = 0x00000300; break; default: - dwGEMode |= VIA_GEM_8bpp; - break; + printk(KERN_WARNING "hw_bitblt_2: Unsupported bpp %d\n", + dst_bpp); + return -EINVAL; + } + writel(tmp, engine + 0x04); + + if (op == VIA_BITBLT_FILL) + tmp = 0; + else + tmp = src_pitch; + if (tmp & 0xFFFFC007 || dst_pitch & 0xFFFFC007) { + printk(KERN_WARNING "hw_bitblt_2: Unsupported pitch %X %X\n", + tmp, dst_pitch); + return -EINVAL; + } + tmp = (tmp >> 3) | (dst_pitch << (16 - 3)); + writel(tmp, engine + 0x08); + + if ((width - 1) & 0xFFFFF000 || (height - 1) & 0xFFFFF000) { + printk(KERN_WARNING "hw_bitblt_2: Unsupported width/height " + "%d %d\n", width, height); + return -EINVAL; + } + tmp = (width - 1) | ((height - 1) << 16); + writel(tmp, engine + 0x0C); + + if (dst_x & 0xFFFFF000 || dst_y & 0xFFFFF000) { + printk(KERN_WARNING "hw_bitblt_2: Unsupported destination x/y " + "%d %d\n", dst_x, dst_y); + return -EINVAL; + } + tmp = dst_x | (dst_y << 16); + writel(tmp, engine + 0x10); + + if (dst_addr & 0xE0000007) { + printk(KERN_WARNING "hw_bitblt_2: Unsupported destination " + "address %X\n", dst_addr); + return -EINVAL; + } + tmp = dst_addr >> 3; + writel(tmp, engine + 0x14); + + if (op != VIA_BITBLT_FILL) { + if (src_x & (op == VIA_BITBLT_MONO ? 0xFFFF8000 : 0xFFFFF000) + || src_y & 0xFFFFF000) { + printk(KERN_WARNING "hw_bitblt_2: Unsupported source " + "x/y %d %d\n", src_x, src_y); + return -EINVAL; + } + tmp = src_x | (src_y << 16); + writel(tmp, engine + 0x18); + + tmp = src_mem ? 0 : src_addr; + if (dst_addr & 0xE0000007) { + printk(KERN_WARNING "hw_bitblt_2: Unsupported source " + "address %X\n", tmp); + return -EINVAL; + } + tmp >>= 3; + writel(tmp, engine + 0x1C); } - /* Set BPP and Pitch */ - writel(dwGEMode, viaparinfo->io_virt + VIA_REG_GEMODE); + if (op != VIA_BITBLT_COLOR) + writel(fg_color, engine + 0x4C); + + if (op == VIA_BITBLT_MONO) + writel(bg_color, engine + 0x50); + + if (op == VIA_BITBLT_FILL) + ge_cmd |= fill_rop << 24 | 0x00002000 | 0x00000001; + else { + ge_cmd |= 0xCC000000; /* ROP=SRCCOPY */ + if (src_mem) + ge_cmd |= 0x00000040; + if (op == VIA_BITBLT_MONO) + ge_cmd |= 0x00000002 | 0x00000100 | 0x00020000; + else + ge_cmd |= 0x00000001; + } + writel(ge_cmd, engine); + + if (op == VIA_BITBLT_FILL || !src_mem) + return 0; + + tmp = (width * height * (op == VIA_BITBLT_MONO ? 1 : (dst_bpp >> 3)) + + 3) >> 2; + + for (i = 0; i < tmp; i++) + writel(src_mem[i], engine + VIA_MMIO_BLTBASE); + + return 0; } -void viafb_hw_cursor_init(void) +int viafb_init_engine(struct fb_info *info) { + struct viafb_par *viapar = info->par; + void __iomem *engine; + u32 vq_start_addr, vq_end_addr, vq_start_low, vq_end_low, vq_high, + vq_len, chip_name = viapar->shared->chip_info.gfx_chip_name; + + engine = ioremap_nocache(info->fix.mmio_start, info->fix.mmio_len); + viapar->shared->engine_mmio = engine; + if (!engine) { + printk(KERN_WARNING "viafb_init_accel: ioremap failed, " + "hardware acceleration disabled\n"); + return -ENOMEM; + } + + switch (chip_name) { + case UNICHROME_CLE266: + case UNICHROME_K400: + case UNICHROME_K800: + case UNICHROME_PM800: + case UNICHROME_CN700: + case UNICHROME_CX700: + case UNICHROME_CN750: + case UNICHROME_K8M890: + case UNICHROME_P4M890: + case UNICHROME_P4M900: + viapar->shared->hw_bitblt = hw_bitblt_1; + break; + case UNICHROME_VX800: + case UNICHROME_VX855: + viapar->shared->hw_bitblt = hw_bitblt_2; + break; + default: + viapar->shared->hw_bitblt = NULL; + } + + viapar->fbmem_free -= CURSOR_SIZE; + viapar->shared->cursor_vram_addr = viapar->fbmem_free; + viapar->fbmem_used += CURSOR_SIZE; + + viapar->fbmem_free -= VQ_SIZE; + viapar->shared->vq_vram_addr = viapar->fbmem_free; + viapar->fbmem_used += VQ_SIZE; + + /* Init AGP and VQ regs */ + switch (chip_name) { + case UNICHROME_K8M890: + case UNICHROME_P4M900: + writel(0x00100000, engine + VIA_REG_CR_TRANSET); + writel(0x680A0000, engine + VIA_REG_CR_TRANSPACE); + writel(0x02000000, engine + VIA_REG_CR_TRANSPACE); + break; + + default: + writel(0x00100000, engine + VIA_REG_TRANSET); + writel(0x00000000, engine + VIA_REG_TRANSPACE); + writel(0x00333004, engine + VIA_REG_TRANSPACE); + writel(0x60000000, engine + VIA_REG_TRANSPACE); + writel(0x61000000, engine + VIA_REG_TRANSPACE); + writel(0x62000000, engine + VIA_REG_TRANSPACE); + writel(0x63000000, engine + VIA_REG_TRANSPACE); + writel(0x64000000, engine + VIA_REG_TRANSPACE); + writel(0x7D000000, engine + VIA_REG_TRANSPACE); + + writel(0xFE020000, engine + VIA_REG_TRANSET); + writel(0x00000000, engine + VIA_REG_TRANSPACE); + break; + } + + /* Enable VQ */ + vq_start_addr = viapar->shared->vq_vram_addr; + vq_end_addr = viapar->shared->vq_vram_addr + VQ_SIZE - 1; + + vq_start_low = 0x50000000 | (vq_start_addr & 0xFFFFFF); + vq_end_low = 0x51000000 | (vq_end_addr & 0xFFFFFF); + vq_high = 0x52000000 | ((vq_start_addr & 0xFF000000) >> 24) | + ((vq_end_addr & 0xFF000000) >> 16); + vq_len = 0x53000000 | (VQ_SIZE >> 3); + + switch (chip_name) { + case UNICHROME_K8M890: + case UNICHROME_P4M900: + vq_start_low |= 0x20000000; + vq_end_low |= 0x20000000; + vq_high |= 0x20000000; + vq_len |= 0x20000000; + + writel(0x00100000, engine + VIA_REG_CR_TRANSET); + writel(vq_high, engine + VIA_REG_CR_TRANSPACE); + writel(vq_start_low, engine + VIA_REG_CR_TRANSPACE); + writel(vq_end_low, engine + VIA_REG_CR_TRANSPACE); + writel(vq_len, engine + VIA_REG_CR_TRANSPACE); + writel(0x74301001, engine + VIA_REG_CR_TRANSPACE); + writel(0x00000000, engine + VIA_REG_CR_TRANSPACE); + break; + default: + writel(0x00FE0000, engine + VIA_REG_TRANSET); + writel(0x080003FE, engine + VIA_REG_TRANSPACE); + writel(0x0A00027C, engine + VIA_REG_TRANSPACE); + writel(0x0B000260, engine + VIA_REG_TRANSPACE); + writel(0x0C000274, engine + VIA_REG_TRANSPACE); + writel(0x0D000264, engine + VIA_REG_TRANSPACE); + writel(0x0E000000, engine + VIA_REG_TRANSPACE); + writel(0x0F000020, engine + VIA_REG_TRANSPACE); + writel(0x1000027E, engine + VIA_REG_TRANSPACE); + writel(0x110002FE, engine + VIA_REG_TRANSPACE); + writel(0x200F0060, engine + VIA_REG_TRANSPACE); + + writel(0x00000006, engine + VIA_REG_TRANSPACE); + writel(0x40008C0F, engine + VIA_REG_TRANSPACE); + writel(0x44000000, engine + VIA_REG_TRANSPACE); + writel(0x45080C04, engine + VIA_REG_TRANSPACE); + writel(0x46800408, engine + VIA_REG_TRANSPACE); + + writel(vq_high, engine + VIA_REG_TRANSPACE); + writel(vq_start_low, engine + VIA_REG_TRANSPACE); + writel(vq_end_low, engine + VIA_REG_TRANSPACE); + writel(vq_len, engine + VIA_REG_TRANSPACE); + break; + } + /* Set Cursor Image Base Address */ - writel(viaparinfo->cursor_start, - viaparinfo->io_virt + VIA_REG_CURSOR_MODE); - writel(0x0, viaparinfo->io_virt + VIA_REG_CURSOR_POS); - writel(0x0, viaparinfo->io_virt + VIA_REG_CURSOR_ORG); - writel(0x0, viaparinfo->io_virt + VIA_REG_CURSOR_BG); - writel(0x0, viaparinfo->io_virt + VIA_REG_CURSOR_FG); + writel(viapar->shared->cursor_vram_addr, engine + VIA_REG_CURSOR_MODE); + writel(0x0, engine + VIA_REG_CURSOR_POS); + writel(0x0, engine + VIA_REG_CURSOR_ORG); + writel(0x0, engine + VIA_REG_CURSOR_BG); + writel(0x0, engine + VIA_REG_CURSOR_FG); + return 0; } void viafb_show_hw_cursor(struct fb_info *info, int Status) { - u32 temp; - u32 iga_path = ((struct viafb_par *)(info->par))->iga_path; + struct viafb_par *viapar = info->par; + u32 temp, iga_path = viapar->iga_path; - temp = readl(viaparinfo->io_virt + VIA_REG_CURSOR_MODE); + temp = readl(viapar->shared->engine_mmio + VIA_REG_CURSOR_MODE); switch (Status) { case HW_Cursor_ON: temp |= 0x1; @@ -259,25 +460,27 @@ void viafb_show_hw_cursor(struct fb_info *info, int Status) default: temp &= 0x7FFFFFFF; } - writel(temp, viaparinfo->io_virt + VIA_REG_CURSOR_MODE); + writel(temp, viapar->shared->engine_mmio + VIA_REG_CURSOR_MODE); } -int viafb_wait_engine_idle(void) +void viafb_wait_engine_idle(struct fb_info *info) { + struct viafb_par *viapar = info->par; int loop = 0; - while (!(readl(viaparinfo->io_virt + VIA_REG_STATUS) & + while (!(readl(viapar->shared->engine_mmio + VIA_REG_STATUS) & VIA_VR_QUEUE_BUSY) && (loop < MAXLOOP)) { loop++; cpu_relax(); } - while ((readl(viaparinfo->io_virt + VIA_REG_STATUS) & + while ((readl(viapar->shared->engine_mmio + VIA_REG_STATUS) & (VIA_CMD_RGTR_BUSY | VIA_2D_ENG_BUSY | VIA_3D_ENG_BUSY)) && (loop < MAXLOOP)) { loop++; cpu_relax(); } - return loop >= MAXLOOP; + if (loop >= MAXLOOP) + printk(KERN_ERR "viafb_wait_engine_idle: not syncing\n"); } diff --git a/drivers/video/via/accel.h b/drivers/video/via/accel.h index 29bf854e8ccf..615c84ad0f01 100644 --- a/drivers/video/via/accel.h +++ b/drivers/video/via/accel.h @@ -159,11 +159,12 @@ #define MAXLOOP 0xFFFFFF -void viafb_init_accel(void); -void viafb_init_2d_engine(void); -void set_2d_color_depth(int); -void viafb_hw_cursor_init(void); -void viafb_show_hw_cursor(struct fb_info *info, int Status); int -viafb_wait_engine_idle(void); void viafb_set_2d_color_depth(int bpp); +#define VIA_BITBLT_COLOR 1 +#define VIA_BITBLT_MONO 2 +#define VIA_BITBLT_FILL 3 + +int viafb_init_engine(struct fb_info *info); +void viafb_show_hw_cursor(struct fb_info *info, int Status); +void viafb_wait_engine_idle(struct fb_info *info); #endif /* __ACCEL_H__ */ diff --git a/drivers/video/via/chip.h b/drivers/video/via/chip.h index dde95edc387a..474f428aea92 100644 --- a/drivers/video/via/chip.h +++ b/drivers/video/via/chip.h @@ -68,6 +68,9 @@ #define UNICHROME_VX800 11 #define UNICHROME_VX800_DID 0x1122 +#define UNICHROME_VX855 12 +#define UNICHROME_VX855_DID 0x5122 + /**************************************************/ /* Definition TMDS Trasmitter Information */ /**************************************************/ @@ -122,7 +125,6 @@ struct lvds_chip_information { struct chip_information { int gfx_chip_name; int gfx_chip_revision; - int chip_on_slot; struct tmds_chip_information tmds_chip_info; struct lvds_chip_information lvds_chip_info; struct lvds_chip_information lvds_chip_info2; diff --git a/drivers/video/via/dvi.c b/drivers/video/via/dvi.c index d6965447ca69..c5c32b6b6e6c 100644 --- a/drivers/video/via/dvi.c +++ b/drivers/video/via/dvi.c @@ -160,7 +160,7 @@ int viafb_tmds_trasmitter_identify(void) static void tmds_register_write(int index, u8 data) { - viaparinfo->i2c_stuff.i2c_port = + viaparinfo->shared->i2c_stuff.i2c_port = viaparinfo->chip_info->tmds_chip_info.i2c_port; viafb_i2c_writebyte(viaparinfo->chip_info->tmds_chip_info. @@ -172,7 +172,7 @@ static int tmds_register_read(int index) { u8 data; - viaparinfo->i2c_stuff.i2c_port = + viaparinfo->shared->i2c_stuff.i2c_port = viaparinfo->chip_info->tmds_chip_info.i2c_port; viafb_i2c_readbyte((u8) viaparinfo->chip_info-> tmds_chip_info.tmds_chip_slave_addr, @@ -182,7 +182,7 @@ static int tmds_register_read(int index) static int tmds_register_read_bytes(int index, u8 *buff, int buff_len) { - viaparinfo->i2c_stuff.i2c_port = + viaparinfo->shared->i2c_stuff.i2c_port = viaparinfo->chip_info->tmds_chip_info.i2c_port; viafb_i2c_readbytes((u8) viaparinfo->chip_info->tmds_chip_info. tmds_chip_slave_addr, (u8) index, buff, buff_len); diff --git a/drivers/video/via/global.c b/drivers/video/via/global.c index 468be2425af3..b675cdbb03ad 100644 --- a/drivers/video/via/global.c +++ b/drivers/video/via/global.c @@ -32,7 +32,6 @@ int viafb_lcd_dsp_method = LCD_EXPANDSION; int viafb_lcd_mode = LCD_OPENLDI; int viafb_bpp = 32; int viafb_bpp1 = 32; -int viafb_accel = 1; int viafb_CRT_ON = 1; int viafb_DVI_ON; int viafb_LCD_ON ; @@ -46,13 +45,11 @@ int viafb_hotplug_refresh = 60; unsigned int viafb_second_offset; int viafb_second_size; int viafb_primary_dev = None_Device; -void __iomem *viafb_FB_MM; unsigned int viafb_second_xres = 640; unsigned int viafb_second_yres = 480; unsigned int viafb_second_virtual_xres; unsigned int viafb_second_virtual_yres; int viafb_lcd_panel_id = LCD_PANEL_ID_MAXIMUM + 1; -struct fb_cursor viacursor; struct fb_info *viafbinfo; struct fb_info *viafbinfo1; struct viafb_par *viaparinfo; diff --git a/drivers/video/via/global.h b/drivers/video/via/global.h index 7543d5f7e309..d69d0ca99c2f 100644 --- a/drivers/video/via/global.h +++ b/drivers/video/via/global.h @@ -77,8 +77,6 @@ extern int viafb_hotplug_Yres; extern int viafb_hotplug_bpp; extern int viafb_hotplug_refresh; extern int viafb_primary_dev; -extern void __iomem *viafb_FB_MM; -extern struct fb_cursor viacursor; extern unsigned int viafb_second_xres; extern unsigned int viafb_second_yres; diff --git a/drivers/video/via/hw.c b/drivers/video/via/hw.c index c8960003f47d..3e083ff67ae2 100644 --- a/drivers/video/via/hw.c +++ b/drivers/video/via/hw.c @@ -21,125 +21,143 @@ #include "global.h" -static const struct pci_device_id_info pciidlist[] = { - {PCI_VIA_VENDOR_ID, UNICHROME_CLE266_DID, UNICHROME_CLE266}, - {PCI_VIA_VENDOR_ID, UNICHROME_PM800_DID, UNICHROME_PM800}, - {PCI_VIA_VENDOR_ID, UNICHROME_K400_DID, UNICHROME_K400}, - {PCI_VIA_VENDOR_ID, UNICHROME_K800_DID, UNICHROME_K800}, - {PCI_VIA_VENDOR_ID, UNICHROME_CN700_DID, UNICHROME_CN700}, - {PCI_VIA_VENDOR_ID, UNICHROME_P4M890_DID, UNICHROME_P4M890}, - {PCI_VIA_VENDOR_ID, UNICHROME_K8M890_DID, UNICHROME_K8M890}, - {PCI_VIA_VENDOR_ID, UNICHROME_CX700_DID, UNICHROME_CX700}, - {PCI_VIA_VENDOR_ID, UNICHROME_P4M900_DID, UNICHROME_P4M900}, - {PCI_VIA_VENDOR_ID, UNICHROME_CN750_DID, UNICHROME_CN750}, - {PCI_VIA_VENDOR_ID, UNICHROME_VX800_DID, UNICHROME_VX800}, - {0, 0, 0} -}; - -struct offset offset_reg = { - /* IGA1 Offset Register */ - {IGA1_OFFSET_REG_NUM, {{CR13, 0, 7}, {CR35, 5, 7} } }, - /* IGA2 Offset Register */ - {IGA2_OFFSET_REG_NUM, {{CR66, 0, 7}, {CR67, 0, 1} } } -}; - static struct pll_map pll_value[] = { - {CLK_25_175M, CLE266_PLL_25_175M, K800_PLL_25_175M, CX700_25_175M}, - {CLK_29_581M, CLE266_PLL_29_581M, K800_PLL_29_581M, CX700_29_581M}, - {CLK_26_880M, CLE266_PLL_26_880M, K800_PLL_26_880M, CX700_26_880M}, - {CLK_31_490M, CLE266_PLL_31_490M, K800_PLL_31_490M, CX700_31_490M}, - {CLK_31_500M, CLE266_PLL_31_500M, K800_PLL_31_500M, CX700_31_500M}, - {CLK_31_728M, CLE266_PLL_31_728M, K800_PLL_31_728M, CX700_31_728M}, - {CLK_32_668M, CLE266_PLL_32_668M, K800_PLL_32_668M, CX700_32_668M}, - {CLK_36_000M, CLE266_PLL_36_000M, K800_PLL_36_000M, CX700_36_000M}, - {CLK_40_000M, CLE266_PLL_40_000M, K800_PLL_40_000M, CX700_40_000M}, - {CLK_41_291M, CLE266_PLL_41_291M, K800_PLL_41_291M, CX700_41_291M}, - {CLK_43_163M, CLE266_PLL_43_163M, K800_PLL_43_163M, CX700_43_163M}, - {CLK_45_250M, CLE266_PLL_45_250M, K800_PLL_45_250M, CX700_45_250M}, - {CLK_46_000M, CLE266_PLL_46_000M, K800_PLL_46_000M, CX700_46_000M}, - {CLK_46_996M, CLE266_PLL_46_996M, K800_PLL_46_996M, CX700_46_996M}, - {CLK_48_000M, CLE266_PLL_48_000M, K800_PLL_48_000M, CX700_48_000M}, - {CLK_48_875M, CLE266_PLL_48_875M, K800_PLL_48_875M, CX700_48_875M}, - {CLK_49_500M, CLE266_PLL_49_500M, K800_PLL_49_500M, CX700_49_500M}, - {CLK_52_406M, CLE266_PLL_52_406M, K800_PLL_52_406M, CX700_52_406M}, - {CLK_52_977M, CLE266_PLL_52_977M, K800_PLL_52_977M, CX700_52_977M}, - {CLK_56_250M, CLE266_PLL_56_250M, K800_PLL_56_250M, CX700_56_250M}, - {CLK_60_466M, CLE266_PLL_60_466M, K800_PLL_60_466M, CX700_60_466M}, - {CLK_61_500M, CLE266_PLL_61_500M, K800_PLL_61_500M, CX700_61_500M}, - {CLK_65_000M, CLE266_PLL_65_000M, K800_PLL_65_000M, CX700_65_000M}, - {CLK_65_178M, CLE266_PLL_65_178M, K800_PLL_65_178M, CX700_65_178M}, - {CLK_66_750M, CLE266_PLL_66_750M, K800_PLL_66_750M, CX700_66_750M}, - {CLK_68_179M, CLE266_PLL_68_179M, K800_PLL_68_179M, CX700_68_179M}, - {CLK_69_924M, CLE266_PLL_69_924M, K800_PLL_69_924M, CX700_69_924M}, - {CLK_70_159M, CLE266_PLL_70_159M, K800_PLL_70_159M, CX700_70_159M}, - {CLK_72_000M, CLE266_PLL_72_000M, K800_PLL_72_000M, CX700_72_000M}, - {CLK_78_750M, CLE266_PLL_78_750M, K800_PLL_78_750M, CX700_78_750M}, - {CLK_80_136M, CLE266_PLL_80_136M, K800_PLL_80_136M, CX700_80_136M}, - {CLK_83_375M, CLE266_PLL_83_375M, K800_PLL_83_375M, CX700_83_375M}, - {CLK_83_950M, CLE266_PLL_83_950M, K800_PLL_83_950M, CX700_83_950M}, - {CLK_84_750M, CLE266_PLL_84_750M, K800_PLL_84_750M, CX700_84_750M}, - {CLK_85_860M, CLE266_PLL_85_860M, K800_PLL_85_860M, CX700_85_860M}, - {CLK_88_750M, CLE266_PLL_88_750M, K800_PLL_88_750M, CX700_88_750M}, - {CLK_94_500M, CLE266_PLL_94_500M, K800_PLL_94_500M, CX700_94_500M}, - {CLK_97_750M, CLE266_PLL_97_750M, K800_PLL_97_750M, CX700_97_750M}, + {CLK_25_175M, CLE266_PLL_25_175M, K800_PLL_25_175M, + CX700_25_175M, VX855_25_175M}, + {CLK_29_581M, CLE266_PLL_29_581M, K800_PLL_29_581M, + CX700_29_581M, VX855_29_581M}, + {CLK_26_880M, CLE266_PLL_26_880M, K800_PLL_26_880M, + CX700_26_880M, VX855_26_880M}, + {CLK_31_490M, CLE266_PLL_31_490M, K800_PLL_31_490M, + CX700_31_490M, VX855_31_490M}, + {CLK_31_500M, CLE266_PLL_31_500M, K800_PLL_31_500M, + CX700_31_500M, VX855_31_500M}, + {CLK_31_728M, CLE266_PLL_31_728M, K800_PLL_31_728M, + CX700_31_728M, VX855_31_728M}, + {CLK_32_668M, CLE266_PLL_32_668M, K800_PLL_32_668M, + CX700_32_668M, VX855_32_668M}, + {CLK_36_000M, CLE266_PLL_36_000M, K800_PLL_36_000M, + CX700_36_000M, VX855_36_000M}, + {CLK_40_000M, CLE266_PLL_40_000M, K800_PLL_40_000M, + CX700_40_000M, VX855_40_000M}, + {CLK_41_291M, CLE266_PLL_41_291M, K800_PLL_41_291M, + CX700_41_291M, VX855_41_291M}, + {CLK_43_163M, CLE266_PLL_43_163M, K800_PLL_43_163M, + CX700_43_163M, VX855_43_163M}, + {CLK_45_250M, CLE266_PLL_45_250M, K800_PLL_45_250M, + CX700_45_250M, VX855_45_250M}, + {CLK_46_000M, CLE266_PLL_46_000M, K800_PLL_46_000M, + CX700_46_000M, VX855_46_000M}, + {CLK_46_996M, CLE266_PLL_46_996M, K800_PLL_46_996M, + CX700_46_996M, VX855_46_996M}, + {CLK_48_000M, CLE266_PLL_48_000M, K800_PLL_48_000M, + CX700_48_000M, VX855_48_000M}, + {CLK_48_875M, CLE266_PLL_48_875M, K800_PLL_48_875M, + CX700_48_875M, VX855_48_875M}, + {CLK_49_500M, CLE266_PLL_49_500M, K800_PLL_49_500M, + CX700_49_500M, VX855_49_500M}, + {CLK_52_406M, CLE266_PLL_52_406M, K800_PLL_52_406M, + CX700_52_406M, VX855_52_406M}, + {CLK_52_977M, CLE266_PLL_52_977M, K800_PLL_52_977M, + CX700_52_977M, VX855_52_977M}, + {CLK_56_250M, CLE266_PLL_56_250M, K800_PLL_56_250M, + CX700_56_250M, VX855_56_250M}, + {CLK_60_466M, CLE266_PLL_60_466M, K800_PLL_60_466M, + CX700_60_466M, VX855_60_466M}, + {CLK_61_500M, CLE266_PLL_61_500M, K800_PLL_61_500M, + CX700_61_500M, VX855_61_500M}, + {CLK_65_000M, CLE266_PLL_65_000M, K800_PLL_65_000M, + CX700_65_000M, VX855_65_000M}, + {CLK_65_178M, CLE266_PLL_65_178M, K800_PLL_65_178M, + CX700_65_178M, VX855_65_178M}, + {CLK_66_750M, CLE266_PLL_66_750M, K800_PLL_66_750M, + CX700_66_750M, VX855_66_750M}, + {CLK_68_179M, CLE266_PLL_68_179M, K800_PLL_68_179M, + CX700_68_179M, VX855_68_179M}, + {CLK_69_924M, CLE266_PLL_69_924M, K800_PLL_69_924M, + CX700_69_924M, VX855_69_924M}, + {CLK_70_159M, CLE266_PLL_70_159M, K800_PLL_70_159M, + CX700_70_159M, VX855_70_159M}, + {CLK_72_000M, CLE266_PLL_72_000M, K800_PLL_72_000M, + CX700_72_000M, VX855_72_000M}, + {CLK_78_750M, CLE266_PLL_78_750M, K800_PLL_78_750M, + CX700_78_750M, VX855_78_750M}, + {CLK_80_136M, CLE266_PLL_80_136M, K800_PLL_80_136M, + CX700_80_136M, VX855_80_136M}, + {CLK_83_375M, CLE266_PLL_83_375M, K800_PLL_83_375M, + CX700_83_375M, VX855_83_375M}, + {CLK_83_950M, CLE266_PLL_83_950M, K800_PLL_83_950M, + CX700_83_950M, VX855_83_950M}, + {CLK_84_750M, CLE266_PLL_84_750M, K800_PLL_84_750M, + CX700_84_750M, VX855_84_750M}, + {CLK_85_860M, CLE266_PLL_85_860M, K800_PLL_85_860M, + CX700_85_860M, VX855_85_860M}, + {CLK_88_750M, CLE266_PLL_88_750M, K800_PLL_88_750M, + CX700_88_750M, VX855_88_750M}, + {CLK_94_500M, CLE266_PLL_94_500M, K800_PLL_94_500M, + CX700_94_500M, VX855_94_500M}, + {CLK_97_750M, CLE266_PLL_97_750M, K800_PLL_97_750M, + CX700_97_750M, VX855_97_750M}, {CLK_101_000M, CLE266_PLL_101_000M, K800_PLL_101_000M, - CX700_101_000M}, + CX700_101_000M, VX855_101_000M}, {CLK_106_500M, CLE266_PLL_106_500M, K800_PLL_106_500M, - CX700_106_500M}, + CX700_106_500M, VX855_106_500M}, {CLK_108_000M, CLE266_PLL_108_000M, K800_PLL_108_000M, - CX700_108_000M}, + CX700_108_000M, VX855_108_000M}, {CLK_113_309M, CLE266_PLL_113_309M, K800_PLL_113_309M, - CX700_113_309M}, + CX700_113_309M, VX855_113_309M}, {CLK_118_840M, CLE266_PLL_118_840M, K800_PLL_118_840M, - CX700_118_840M}, + CX700_118_840M, VX855_118_840M}, {CLK_119_000M, CLE266_PLL_119_000M, K800_PLL_119_000M, - CX700_119_000M}, + CX700_119_000M, VX855_119_000M}, {CLK_121_750M, CLE266_PLL_121_750M, K800_PLL_121_750M, - CX700_121_750M}, + CX700_121_750M, 0}, {CLK_125_104M, CLE266_PLL_125_104M, K800_PLL_125_104M, - CX700_125_104M}, + CX700_125_104M, 0}, {CLK_133_308M, CLE266_PLL_133_308M, K800_PLL_133_308M, - CX700_133_308M}, + CX700_133_308M, 0}, {CLK_135_000M, CLE266_PLL_135_000M, K800_PLL_135_000M, - CX700_135_000M}, + CX700_135_000M, VX855_135_000M}, {CLK_136_700M, CLE266_PLL_136_700M, K800_PLL_136_700M, - CX700_136_700M}, + CX700_136_700M, VX855_136_700M}, {CLK_138_400M, CLE266_PLL_138_400M, K800_PLL_138_400M, - CX700_138_400M}, + CX700_138_400M, VX855_138_400M}, {CLK_146_760M, CLE266_PLL_146_760M, K800_PLL_146_760M, - CX700_146_760M}, + CX700_146_760M, VX855_146_760M}, {CLK_153_920M, CLE266_PLL_153_920M, K800_PLL_153_920M, - CX700_153_920M}, + CX700_153_920M, VX855_153_920M}, {CLK_156_000M, CLE266_PLL_156_000M, K800_PLL_156_000M, - CX700_156_000M}, + CX700_156_000M, VX855_156_000M}, {CLK_157_500M, CLE266_PLL_157_500M, K800_PLL_157_500M, - CX700_157_500M}, + CX700_157_500M, VX855_157_500M}, {CLK_162_000M, CLE266_PLL_162_000M, K800_PLL_162_000M, - CX700_162_000M}, + CX700_162_000M, VX855_162_000M}, {CLK_187_000M, CLE266_PLL_187_000M, K800_PLL_187_000M, - CX700_187_000M}, + CX700_187_000M, VX855_187_000M}, {CLK_193_295M, CLE266_PLL_193_295M, K800_PLL_193_295M, - CX700_193_295M}, + CX700_193_295M, VX855_193_295M}, {CLK_202_500M, CLE266_PLL_202_500M, K800_PLL_202_500M, - CX700_202_500M}, + CX700_202_500M, VX855_202_500M}, {CLK_204_000M, CLE266_PLL_204_000M, K800_PLL_204_000M, - CX700_204_000M}, + CX700_204_000M, VX855_204_000M}, {CLK_218_500M, CLE266_PLL_218_500M, K800_PLL_218_500M, - CX700_218_500M}, + CX700_218_500M, VX855_218_500M}, {CLK_234_000M, CLE266_PLL_234_000M, K800_PLL_234_000M, - CX700_234_000M}, + CX700_234_000M, VX855_234_000M}, {CLK_267_250M, CLE266_PLL_267_250M, K800_PLL_267_250M, - CX700_267_250M}, + CX700_267_250M, VX855_267_250M}, {CLK_297_500M, CLE266_PLL_297_500M, K800_PLL_297_500M, - CX700_297_500M}, - {CLK_74_481M, CLE266_PLL_74_481M, K800_PLL_74_481M, CX700_74_481M}, + CX700_297_500M, VX855_297_500M}, + {CLK_74_481M, CLE266_PLL_74_481M, K800_PLL_74_481M, + CX700_74_481M, VX855_74_481M}, {CLK_172_798M, CLE266_PLL_172_798M, K800_PLL_172_798M, - CX700_172_798M}, + CX700_172_798M, VX855_172_798M}, {CLK_122_614M, CLE266_PLL_122_614M, K800_PLL_122_614M, - CX700_122_614M}, - {CLK_74_270M, CLE266_PLL_74_270M, K800_PLL_74_270M, CX700_74_270M}, + CX700_122_614M, VX855_122_614M}, + {CLK_74_270M, CLE266_PLL_74_270M, K800_PLL_74_270M, + CX700_74_270M, 0}, {CLK_148_500M, CLE266_PLL_148_500M, K800_PLL_148_500M, - CX700_148_500M} + CX700_148_500M, VX855_148_500M} }; static struct fifo_depth_select display_fifo_depth_reg = { @@ -508,7 +526,8 @@ static void set_dvi_output_path(int set_iga, int output_interface); static void set_lcd_output_path(int set_iga, int output_interface); static int search_mode_setting(int ModeInfoIndex); static void load_fix_bit_crtc_reg(void); -static void init_gfx_chip_info(void); +static void init_gfx_chip_info(struct pci_dev *pdev, + const struct pci_device_id *pdi); static void init_tmds_chip_info(void); static void init_lvds_chip_info(void); static void device_screen_off(void); @@ -518,7 +537,6 @@ static void device_off(void); static void device_on(void); static void enable_second_display_channel(void); static void disable_second_display_channel(void); -static int get_fb_size_from_pci(void); void viafb_write_reg(u8 index, u16 io_port, u8 data) { @@ -629,70 +647,43 @@ void viafb_set_iga_path(void) } } -void viafb_set_start_addr(void) +void viafb_set_primary_address(u32 addr) { - unsigned long offset = 0, tmp = 0, size = 0; - unsigned long length; - - DEBUG_MSG(KERN_INFO "viafb_set_start_addr!\n"); - viafb_unlock_crt(); - /* update starting address of IGA1 */ - viafb_write_reg(CR0C, VIACR, 0x00); /*initial starting address */ - viafb_write_reg(CR0D, VIACR, 0x00); - viafb_write_reg(CR34, VIACR, 0x00); - viafb_write_reg_mask(CR48, VIACR, 0x00, 0x1F); - - if (viafb_dual_fb) { - viaparinfo->iga_path = IGA1; - viaparinfo1->iga_path = IGA2; - } - - if (viafb_SAMM_ON == 1) { - if (!viafb_dual_fb) { - if (viafb_second_size) - size = viafb_second_size * 1024 * 1024; - else - size = 8 * 1024 * 1024; - } else { + DEBUG_MSG(KERN_DEBUG "viafb_set_primary_address(0x%08X)\n", addr); + viafb_write_reg(CR0D, VIACR, addr & 0xFF); + viafb_write_reg(CR0C, VIACR, (addr >> 8) & 0xFF); + viafb_write_reg(CR34, VIACR, (addr >> 16) & 0xFF); + viafb_write_reg_mask(CR48, VIACR, (addr >> 24) & 0x1F, 0x1F); +} - size = viaparinfo1->memsize; - } - offset = viafb_second_offset; - DEBUG_MSG(KERN_INFO - "viafb_second_size=%lx, second start_adddress=%lx\n", - size, offset); - } - if (viafb_SAMM_ON == 1) { - offset = offset >> 3; - - tmp = viafb_read_reg(VIACR, 0x62) & 0x01; - tmp |= (offset & 0x7F) << 1; - viafb_write_reg(CR62, VIACR, tmp); - viafb_write_reg(CR63, VIACR, ((offset & 0x7F80) >> 7)); - viafb_write_reg(CR64, VIACR, ((offset & 0x7F8000) >> 15)); - viafb_write_reg(CRA3, VIACR, ((offset & 0x3800000) >> 23)); - } else { - /* update starting address */ - viafb_write_reg(CR62, VIACR, 0x00); - viafb_write_reg(CR63, VIACR, 0x00); - viafb_write_reg(CR64, VIACR, 0x00); - viafb_write_reg(CRA3, VIACR, 0x00); - } +void viafb_set_secondary_address(u32 addr) +{ + DEBUG_MSG(KERN_DEBUG "viafb_set_secondary_address(0x%08X)\n", addr); + /* secondary display supports only quadword aligned memory */ + viafb_write_reg_mask(CR62, VIACR, (addr >> 2) & 0xFE, 0xFE); + viafb_write_reg(CR63, VIACR, (addr >> 10) & 0xFF); + viafb_write_reg(CR64, VIACR, (addr >> 18) & 0xFF); + viafb_write_reg_mask(CRA3, VIACR, (addr >> 26) & 0x07, 0x07); +} - if (viafb_SAMM_ON == 1) { - if (viafb_accel) { - if (!viafb_dual_fb) - length = size - viaparinfo->fbmem_used; - else - length = size - viaparinfo1->fbmem_used; - } else - length = size; - offset = (unsigned long)(void *)viafb_FB_MM + - viafb_second_offset; - memset((void *)offset, 0, length); - } +void viafb_set_primary_pitch(u32 pitch) +{ + DEBUG_MSG(KERN_DEBUG "viafb_set_primary_pitch(0x%08X)\n", pitch); + /* spec does not say that first adapter skips 3 bits but old + * code did it and seems to be reasonable in analogy to 2nd adapter + */ + pitch = pitch >> 3; + viafb_write_reg(0x13, VIACR, pitch & 0xFF); + viafb_write_reg_mask(0x35, VIACR, (pitch >> (8 - 5)) & 0xE0, 0xE0); +} - viafb_lock_crt(); +void viafb_set_secondary_pitch(u32 pitch) +{ + DEBUG_MSG(KERN_DEBUG "viafb_set_secondary_pitch(0x%08X)\n", pitch); + pitch = pitch >> 3; + viafb_write_reg(0x66, VIACR, pitch & 0xFF); + viafb_write_reg_mask(0x67, VIACR, (pitch >> 8) & 0x03, 0x03); + viafb_write_reg_mask(0x71, VIACR, (pitch >> (10 - 7)) & 0x80, 0x80); } void viafb_set_output_path(int device, int set_iga, int output_interface) @@ -1123,30 +1114,6 @@ void viafb_write_regx(struct io_reg RegTable[], int ItemNum) } } -void viafb_load_offset_reg(int h_addr, int bpp_byte, int set_iga) -{ - int reg_value; - int viafb_load_reg_num; - struct io_register *reg; - - switch (set_iga) { - case IGA1_IGA2: - case IGA1: - reg_value = IGA1_OFFSET_FORMULA(h_addr, bpp_byte); - viafb_load_reg_num = offset_reg.iga1_offset_reg.reg_num; - reg = offset_reg.iga1_offset_reg.reg; - viafb_load_reg(reg_value, viafb_load_reg_num, reg, VIACR); - if (set_iga == IGA1) - break; - case IGA2: - reg_value = IGA2_OFFSET_FORMULA(h_addr, bpp_byte); - viafb_load_reg_num = offset_reg.iga2_offset_reg.reg_num; - reg = offset_reg.iga2_offset_reg.reg; - viafb_load_reg(reg_value, viafb_load_reg_num, reg, VIACR); - break; - } -} - void viafb_load_fetch_count_reg(int h_addr, int bpp_byte, int set_iga) { int reg_value; @@ -1277,6 +1244,15 @@ void viafb_load_FIFO_reg(int set_iga, int hor_active, int ver_active) VX800_IGA1_DISPLAY_QUEUE_EXPIRE_NUM; } + if (viaparinfo->chip_info->gfx_chip_name == UNICHROME_VX855) { + iga1_fifo_max_depth = VX855_IGA1_FIFO_MAX_DEPTH; + iga1_fifo_threshold = VX855_IGA1_FIFO_THRESHOLD; + iga1_fifo_high_threshold = + VX855_IGA1_FIFO_HIGH_THRESHOLD; + iga1_display_queue_expire_num = + VX855_IGA1_DISPLAY_QUEUE_EXPIRE_NUM; + } + /* Set Display FIFO Depath Select */ reg_value = IGA1_FIFO_DEPTH_SELECT_FORMULA(iga1_fifo_max_depth); viafb_load_reg_num = @@ -1408,6 +1384,15 @@ void viafb_load_FIFO_reg(int set_iga, int hor_active, int ver_active) VX800_IGA2_DISPLAY_QUEUE_EXPIRE_NUM; } + if (viaparinfo->chip_info->gfx_chip_name == UNICHROME_VX855) { + iga2_fifo_max_depth = VX855_IGA2_FIFO_MAX_DEPTH; + iga2_fifo_threshold = VX855_IGA2_FIFO_THRESHOLD; + iga2_fifo_high_threshold = + VX855_IGA2_FIFO_HIGH_THRESHOLD; + iga2_display_queue_expire_num = + VX855_IGA2_DISPLAY_QUEUE_EXPIRE_NUM; + } + if (viaparinfo->chip_info->gfx_chip_name == UNICHROME_K800) { /* Set Display FIFO Depath Select */ reg_value = @@ -1496,6 +1481,8 @@ u32 viafb_get_clk_value(int clk) case UNICHROME_P4M900: case UNICHROME_VX800: return pll_value[i].cx700_pll; + case UNICHROME_VX855: + return pll_value[i].vx855_pll; } } } @@ -1529,6 +1516,7 @@ void viafb_set_vclock(u32 CLK, int set_iga) case UNICHROME_P4M890: case UNICHROME_P4M900: case UNICHROME_VX800: + case UNICHROME_VX855: viafb_write_reg(SR44, VIASR, CLK / 0x10000); DEBUG_MSG(KERN_INFO "\nSR44=%x", CLK / 0x10000); viafb_write_reg(SR45, VIASR, (CLK & 0xFFFF) / 0x100); @@ -1557,6 +1545,7 @@ void viafb_set_vclock(u32 CLK, int set_iga) case UNICHROME_P4M890: case UNICHROME_P4M900: case UNICHROME_VX800: + case UNICHROME_VX855: viafb_write_reg(SR4A, VIASR, CLK / 0x10000); viafb_write_reg(SR4B, VIASR, (CLK & 0xFFFF) / 0x100); viafb_write_reg(SR4C, VIASR, CLK % 0x100); @@ -1916,7 +1905,6 @@ void viafb_fill_crtc_timing(struct crt_mode_table *crt_table, load_fix_bit_crtc_reg(); viafb_lock_crt(); viafb_write_reg_mask(CR17, VIACR, 0x80, BIT7); - viafb_load_offset_reg(h_addr, bpp_byte, set_iga); viafb_load_fetch_count_reg(h_addr, bpp_byte, set_iga); /* load FIFO */ @@ -1933,9 +1921,10 @@ void viafb_fill_crtc_timing(struct crt_mode_table *crt_table, } -void viafb_init_chip_info(void) +void viafb_init_chip_info(struct pci_dev *pdev, + const struct pci_device_id *pdi) { - init_gfx_chip_info(); + init_gfx_chip_info(pdev, pdi); init_tmds_chip_info(); init_lvds_chip_info(); @@ -2008,24 +1997,12 @@ void viafb_update_device_setting(int hres, int vres, } } -static void init_gfx_chip_info(void) +static void init_gfx_chip_info(struct pci_dev *pdev, + const struct pci_device_id *pdi) { - struct pci_dev *pdev = NULL; - u32 i; u8 tmp; - /* Indentify GFX Chip Name */ - for (i = 0; pciidlist[i].vendor != 0; i++) { - pdev = pci_get_device(pciidlist[i].vendor, - pciidlist[i].device, 0); - if (pdev) - break; - } - - if (!pciidlist[i].vendor) - return ; - - viaparinfo->chip_info->gfx_chip_name = pciidlist[i].chip_index; + viaparinfo->chip_info->gfx_chip_name = pdi->driver_data; /* Check revision of CLE266 Chip */ if (viaparinfo->chip_info->gfx_chip_name == UNICHROME_CLE266) { @@ -2056,8 +2033,6 @@ static void init_gfx_chip_info(void) CX700_REVISION_700; } } - - pci_dev_put(pdev); } static void init_tmds_chip_info(void) @@ -2271,11 +2246,12 @@ int viafb_setmode(int vmode_index, int hor_res, int ver_res, int video_bpp, break; case UNICHROME_CX700: - viafb_write_regx(CX700_ModeXregs, NUM_TOTAL_CX700_ModeXregs); - case UNICHROME_VX800: - viafb_write_regx(VX800_ModeXregs, NUM_TOTAL_VX800_ModeXregs); + viafb_write_regx(CX700_ModeXregs, NUM_TOTAL_CX700_ModeXregs); + break; + case UNICHROME_VX855: + viafb_write_regx(VX855_ModeXregs, NUM_TOTAL_VX855_ModeXregs); break; } @@ -2291,7 +2267,8 @@ int viafb_setmode(int vmode_index, int hor_res, int ver_res, int video_bpp, outb(VPIT.SR[i - 1], VIASR + 1); } - viafb_set_start_addr(); + viafb_set_primary_address(0); + viafb_set_secondary_address(viafb_SAMM_ON ? viafb_second_offset : 0); viafb_set_iga_path(); /* Write CRTC */ @@ -2371,6 +2348,9 @@ int viafb_setmode(int vmode_index, int hor_res, int ver_res, int video_bpp, } } + viafb_set_primary_pitch(viafbinfo->fix.line_length); + viafb_set_secondary_pitch(viafb_dual_fb ? viafbinfo1->fix.line_length + : viafbinfo->fix.line_length); /* Update Refresh Rate Setting */ /* Clear On Screen */ @@ -2545,38 +2525,6 @@ void viafb_crt_enable(void) viafb_write_reg_mask(CR36, VIACR, 0x0, BIT5 + BIT4); } -void viafb_get_mmio_info(unsigned long *mmio_base, - unsigned long *mmio_len) -{ - struct pci_dev *pdev = NULL; - u32 vendor, device; - u32 i; - - for (i = 0; pciidlist[i].vendor != 0; i++) - if (viaparinfo->chip_info->gfx_chip_name == - pciidlist[i].chip_index) - break; - - if (!pciidlist[i].vendor) - return ; - - vendor = pciidlist[i].vendor; - device = pciidlist[i].device; - - pdev = pci_get_device(vendor, device, NULL); - - if (!pdev) { - *mmio_base = 0; - *mmio_len = 0; - return ; - } - - *mmio_base = pci_resource_start(pdev, 1); - *mmio_len = pci_resource_len(pdev, 1); - - pci_dev_put(pdev); -} - static void enable_second_display_channel(void) { /* to enable second display channel. */ @@ -2593,44 +2541,7 @@ static void disable_second_display_channel(void) viafb_write_reg_mask(CR6A, VIACR, BIT6, BIT6); } -void viafb_get_fb_info(unsigned int *fb_base, unsigned int *fb_len) -{ - struct pci_dev *pdev = NULL; - u32 vendor, device; - u32 i; - - for (i = 0; pciidlist[i].vendor != 0; i++) - if (viaparinfo->chip_info->gfx_chip_name == - pciidlist[i].chip_index) - break; - - if (!pciidlist[i].vendor) - return ; - - vendor = pciidlist[i].vendor; - device = pciidlist[i].device; - - pdev = pci_get_device(vendor, device, NULL); - - if (!pdev) { - *fb_base = viafb_read_reg(VIASR, SR30) << 24; - *fb_len = viafb_get_memsize(); - DEBUG_MSG(KERN_INFO "Get FB info from SR30!\n"); - DEBUG_MSG(KERN_INFO "fb_base = %08x\n", *fb_base); - DEBUG_MSG(KERN_INFO "fb_len = %08x\n", *fb_len); - return ; - } - - *fb_base = (unsigned int)pci_resource_start(pdev, 0); - *fb_len = get_fb_size_from_pci(); - DEBUG_MSG(KERN_INFO "Get FB info from PCI system!\n"); - DEBUG_MSG(KERN_INFO "fb_base = %08x\n", *fb_base); - DEBUG_MSG(KERN_INFO "fb_len = %08x\n", *fb_len); - - pci_dev_put(pdev); -} - -static int get_fb_size_from_pci(void) +int viafb_get_fb_size_from_pci(void) { unsigned long configid, deviceid, FBSize = 0; int VideoMemSize; @@ -2656,6 +2567,7 @@ static int get_fb_size_from_pci(void) case P4M890_FUNCTION3: case P4M900_FUNCTION3: case VX800_FUNCTION3: + case VX855_FUNCTION3: /*case CN750_FUNCTION3: */ outl(configid + 0xA0, (unsigned long)0xCF8); FBSize = inl((unsigned long)0xCFC); @@ -2719,6 +2631,10 @@ static int get_fb_size_from_pci(void) VideoMemSize = (256 << 20); /*256M */ break; + case 0x00007000: /* Only on VX855/875 */ + VideoMemSize = (512 << 20); /*512M */ + break; + default: VideoMemSize = (32 << 20); /*32M */ break; @@ -2788,24 +2704,6 @@ void viafb_set_dpa_gfx(int output_interface, struct GFX_DPA_SETTING\ } } -void viafb_memory_pitch_patch(struct fb_info *info) -{ - if (info->var.xres != info->var.xres_virtual) { - viafb_load_offset_reg(info->var.xres_virtual, - info->var.bits_per_pixel >> 3, IGA1); - - if (viafb_SAMM_ON) { - viafb_load_offset_reg(viafb_second_virtual_xres, - viafb_bpp1 >> 3, - IGA2); - } else { - viafb_load_offset_reg(info->var.xres_virtual, - info->var.bits_per_pixel >> 3, IGA2); - } - - } -} - /*According var's xres, yres fill var's other timing information*/ void viafb_fill_var_timing_info(struct fb_var_screeninfo *var, int refresh, int mode_index) diff --git a/drivers/video/via/hw.h b/drivers/video/via/hw.h index 6ff38fa8569a..b874d952b446 100644 --- a/drivers/video/via/hw.h +++ b/drivers/video/via/hw.h @@ -147,14 +147,8 @@ is reserved, so it may have problem to set 1600x1200 on IGA2. */ /* location: {CR5F,0,4} */ #define IGA2_VER_SYNC_END_REG_NUM 1 -/* Define Offset and Fetch Count Register*/ +/* Define Fetch Count Register*/ -/* location: {CR13,0,7},{CR35,5,7} */ -#define IGA1_OFFSET_REG_NUM 2 -/* 8 bytes alignment. */ -#define IGA1_OFFSER_ALIGN_BYTE 8 -/* x: H resolution, y: color depth */ -#define IGA1_OFFSET_FORMULA(x, y) ((x*y)/IGA1_OFFSER_ALIGN_BYTE) /* location: {SR1C,0,7},{SR1D,0,1} */ #define IGA1_FETCH_COUNT_REG_NUM 2 /* 16 bytes alignment. */ @@ -164,11 +158,6 @@ is reserved, so it may have problem to set 1600x1200 on IGA2. */ #define IGA1_FETCH_COUNT_FORMULA(x, y) \ (((x*y)/IGA1_FETCH_COUNT_ALIGN_BYTE) + IGA1_FETCH_COUNT_PATCH_VALUE) -/* location: {CR66,0,7},{CR67,0,1} */ -#define IGA2_OFFSET_REG_NUM 2 -#define IGA2_OFFSET_ALIGN_BYTE 8 -/* x: H resolution, y: color depth */ -#define IGA2_OFFSET_FORMULA(x, y) ((x*y)/IGA2_OFFSET_ALIGN_BYTE) /* location: {CR65,0,7},{CR67,2,3} */ #define IGA2_FETCH_COUNT_REG_NUM 2 #define IGA2_FETCH_COUNT_ALIGN_BYTE 16 @@ -335,6 +324,17 @@ is reserved, so it may have problem to set 1600x1200 on IGA2. */ /* location: {CR94,0,6} */ #define VX800_IGA2_DISPLAY_QUEUE_EXPIRE_NUM 128 +/* For VT3409 */ +#define VX855_IGA1_FIFO_MAX_DEPTH 400 +#define VX855_IGA1_FIFO_THRESHOLD 320 +#define VX855_IGA1_FIFO_HIGH_THRESHOLD 320 +#define VX855_IGA1_DISPLAY_QUEUE_EXPIRE_NUM 160 + +#define VX855_IGA2_FIFO_MAX_DEPTH 200 +#define VX855_IGA2_FIFO_THRESHOLD 160 +#define VX855_IGA2_FIFO_HIGH_THRESHOLD 160 +#define VX855_IGA2_DISPLAY_QUEUE_EXPIRE_NUM 320 + #define IGA1_FIFO_DEPTH_SELECT_REG_NUM 1 #define IGA1_FIFO_THRESHOLD_REG_NUM 2 #define IGA1_FIFO_HIGH_THRESHOLD_REG_NUM 2 @@ -617,23 +617,6 @@ struct iga2_ver_sync_end { struct io_register reg[IGA2_VER_SYNC_END_REG_NUM]; }; -/* IGA1 Offset Register */ -struct iga1_offset { - int reg_num; - struct io_register reg[IGA1_OFFSET_REG_NUM]; -}; - -/* IGA2 Offset Register */ -struct iga2_offset { - int reg_num; - struct io_register reg[IGA2_OFFSET_REG_NUM]; -}; - -struct offset { - struct iga1_offset iga1_offset_reg; - struct iga2_offset iga2_offset_reg; -}; - /* IGA1 Fetch Count Register */ struct iga1_fetch_count { int reg_num; @@ -716,6 +699,7 @@ struct pll_map { u32 cle266_pll; u32 k800_pll; u32 cx700_pll; + u32 vx855_pll; }; struct rgbLUT { @@ -860,6 +844,8 @@ struct iga2_crtc_timing { #define P4M900_FUNCTION3 0x3364 /* VT3353 chipset*/ #define VX800_FUNCTION3 0x3353 +/* VT3409 chipset*/ +#define VX855_FUNCTION3 0x3409 #define NUM_TOTAL_PLL_TABLE ARRAY_SIZE(pll_value) @@ -883,7 +869,6 @@ extern int viafb_dual_fb; extern int viafb_LCD2_ON; extern int viafb_LCD_ON; extern int viafb_DVI_ON; -extern int viafb_accel; extern int viafb_hotplug; void viafb_write_reg_mask(u8 index, int io_port, u8 data, u8 mask); @@ -904,7 +889,6 @@ void viafb_write_reg(u8 index, u16 io_port, u8 data); u8 viafb_read_reg(int io_port, u8 index); void viafb_lock_crt(void); void viafb_unlock_crt(void); -void viafb_load_offset_reg(int h_addr, int bpp_byte, int set_iga); void viafb_load_fetch_count_reg(int h_addr, int bpp_byte, int set_iga); void viafb_write_regx(struct io_reg RegTable[], int ItemNum); struct VideoModeTable *viafb_get_modetbl_pointer(int Index); @@ -917,17 +901,20 @@ void viafb_set_dpa_gfx(int output_interface, struct GFX_DPA_SETTING\ int viafb_setmode(int vmode_index, int hor_res, int ver_res, int video_bpp, int vmode_index1, int hor_res1, int ver_res1, int video_bpp1); -void viafb_init_chip_info(void); +void viafb_init_chip_info(struct pci_dev *pdev, + const struct pci_device_id *pdi); void viafb_init_dac(int set_iga); int viafb_get_pixclock(int hres, int vres, int vmode_refresh); int viafb_get_refresh(int hres, int vres, u32 float_refresh); void viafb_update_device_setting(int hres, int vres, int bpp, int vmode_refresh, int flag); -void viafb_get_mmio_info(unsigned long *mmio_base, - unsigned long *mmio_len); +int viafb_get_fb_size_from_pci(void); void viafb_set_iga_path(void); -void viafb_set_start_addr(void); +void viafb_set_primary_address(u32 addr); +void viafb_set_secondary_address(u32 addr); +void viafb_set_primary_pitch(u32 pitch); +void viafb_set_secondary_pitch(u32 pitch); void viafb_get_fb_info(unsigned int *fb_base, unsigned int *fb_len); #endif /* __HW_H__ */ diff --git a/drivers/video/via/ioctl.h b/drivers/video/via/ioctl.h index 842fe30b9868..de899807eade 100644 --- a/drivers/video/via/ioctl.h +++ b/drivers/video/via/ioctl.h @@ -50,8 +50,6 @@ #define VIAFB_GET_GAMMA_LUT 0x56494124 #define VIAFB_SET_GAMMA_LUT 0x56494125 #define VIAFB_GET_GAMMA_SUPPORT_STATE 0x56494126 -#define VIAFB_SET_VIDEO_DEVICE 0x56494127 -#define VIAFB_GET_VIDEO_DEVICE 0x56494128 #define VIAFB_SET_SECOND_MODE 0x56494129 #define VIAFB_SYNC_SURFACE 0x56494130 #define VIAFB_GET_DRIVER_CAPS 0x56494131 @@ -179,9 +177,7 @@ struct viafb_ioctl_setting { unsigned short second_dev_bpp; /* Indicate which device are primary display device. */ unsigned int primary_device; - /* Indicate which device will show video. only valid in duoview mode */ - unsigned int video_device_status; - unsigned int struct_reserved[34]; + unsigned int struct_reserved[35]; struct viafb_ioctl_lcd_attribute lcd_attributes; }; diff --git a/drivers/video/via/lcd.c b/drivers/video/via/lcd.c index 78c6b3387947..e3e597f937a5 100644 --- a/drivers/video/via/lcd.c +++ b/drivers/video/via/lcd.c @@ -207,13 +207,13 @@ static bool lvds_identify_integratedlvds(void) int viafb_lvds_trasmitter_identify(void) { - viaparinfo->i2c_stuff.i2c_port = I2CPORTINDEX; + viaparinfo->shared->i2c_stuff.i2c_port = I2CPORTINDEX; if (viafb_lvds_identify_vt1636()) { viaparinfo->chip_info->lvds_chip_info.i2c_port = I2CPORTINDEX; DEBUG_MSG(KERN_INFO "Found VIA VT1636 LVDS on port i2c 0x31 \n"); } else { - viaparinfo->i2c_stuff.i2c_port = GPIOPORTINDEX; + viaparinfo->shared->i2c_stuff.i2c_port = GPIOPORTINDEX; if (viafb_lvds_identify_vt1636()) { viaparinfo->chip_info->lvds_chip_info.i2c_port = GPIOPORTINDEX; @@ -470,7 +470,7 @@ static int lvds_register_read(int index) { u8 data; - viaparinfo->i2c_stuff.i2c_port = GPIOPORTINDEX; + viaparinfo->shared->i2c_stuff.i2c_port = GPIOPORTINDEX; viafb_i2c_readbyte((u8) viaparinfo->chip_info-> lvds_chip_info.lvds_chip_slave_addr, (u8) index, &data); @@ -952,13 +952,10 @@ void viafb_lcd_set_mode(struct crt_mode_table *mode_crt_table, int video_index = plvds_setting_info->lcd_panel_size; int set_iga = plvds_setting_info->iga_path; int mode_bpp = plvds_setting_info->bpp; - int viafb_load_reg_num = 0; - int reg_value = 0; int set_hres, set_vres; int panel_hres, panel_vres; u32 pll_D_N; int offset; - struct io_register *reg = NULL; struct display_timing mode_crt_reg, panel_crt_reg; struct crt_mode_table *panel_crt_table = NULL; struct VideoModeTable *vmode_tbl = NULL; @@ -1038,16 +1035,11 @@ void viafb_lcd_set_mode(struct crt_mode_table *mode_crt_table, } /* Offset for simultaneous */ - reg_value = offset; - viafb_load_reg_num = offset_reg.iga2_offset_reg.reg_num; - reg = offset_reg.iga2_offset_reg.reg; - viafb_load_reg(reg_value, viafb_load_reg_num, reg, VIACR); + viafb_set_secondary_pitch(offset << 3); DEBUG_MSG(KERN_INFO "viafb_load_reg!!\n"); viafb_load_fetch_count_reg(set_hres, 4, IGA2); /* Fetch count for simultaneous */ } else { /* SAMM */ - /* Offset for IGA2 only */ - viafb_load_offset_reg(set_hres, mode_bpp / 8, set_iga); /* Fetch count for IGA2 only */ viafb_load_fetch_count_reg(set_hres, mode_bpp / 8, set_iga); diff --git a/drivers/video/via/share.h b/drivers/video/via/share.h index 2e1254da9c8c..7cd03e2a1275 100644 --- a/drivers/video/via/share.h +++ b/drivers/video/via/share.h @@ -167,6 +167,10 @@ #define SR4B 0x4B #define SR4C 0x4C #define SR52 0x52 +#define SR57 0x57 +#define SR58 0x58 +#define SR59 0x59 +#define SR5D 0x5D #define SR5E 0x5E #define SR65 0x65 @@ -966,6 +970,100 @@ #define CX700_297_500M 0x00CE0403 #define CX700_122_614M 0x00870802 +/* PLL for VX855 */ +#define VX855_22_000M 0x007B1005 +#define VX855_25_175M 0x008D1005 +#define VX855_26_719M 0x00961005 +#define VX855_26_880M 0x00961005 +#define VX855_27_000M 0x00971005 +#define VX855_29_581M 0x00A51005 +#define VX855_29_829M 0x00641003 +#define VX855_31_490M 0x00B01005 +#define VX855_31_500M 0x00B01005 +#define VX855_31_728M 0x008E1004 +#define VX855_32_668M 0x00921004 +#define VX855_36_000M 0x00A11004 +#define VX855_40_000M 0x00700C05 +#define VX855_41_291M 0x00730C05 +#define VX855_43_163M 0x00790C05 +#define VX855_45_250M 0x007F0C05 /* 45.46MHz */ +#define VX855_46_000M 0x00670C04 +#define VX855_46_996M 0x00690C04 +#define VX855_48_000M 0x00860C05 +#define VX855_48_875M 0x00890C05 +#define VX855_49_500M 0x00530C03 +#define VX855_52_406M 0x00580C03 +#define VX855_52_977M 0x00940C05 +#define VX855_56_250M 0x009D0C05 +#define VX855_60_466M 0x00A90C05 +#define VX855_61_500M 0x00AC0C05 +#define VX855_65_000M 0x006D0C03 +#define VX855_65_178M 0x00B60C05 +#define VX855_66_750M 0x00700C03 /*67.116MHz */ +#define VX855_67_295M 0x00BC0C05 +#define VX855_68_179M 0x00BF0C05 +#define VX855_68_369M 0x00BF0C05 +#define VX855_69_924M 0x00C30C05 +#define VX855_70_159M 0x00C30C05 +#define VX855_72_000M 0x00A10C04 +#define VX855_73_023M 0x00CC0C05 +#define VX855_74_481M 0x00D10C05 +#define VX855_78_750M 0x006E0805 +#define VX855_79_466M 0x006F0805 +#define VX855_80_136M 0x00700805 +#define VX855_81_627M 0x00720805 +#define VX855_83_375M 0x00750805 +#define VX855_83_527M 0x00750805 +#define VX855_83_950M 0x00750805 +#define VX855_84_537M 0x00760805 +#define VX855_84_750M 0x00760805 /* 84.537Mhz */ +#define VX855_85_500M 0x00760805 /* 85.909080 MHz*/ +#define VX855_85_860M 0x00760805 +#define VX855_85_909M 0x00760805 +#define VX855_88_750M 0x007C0805 +#define VX855_89_489M 0x007D0805 +#define VX855_94_500M 0x00840805 +#define VX855_96_648M 0x00870805 +#define VX855_97_750M 0x00890805 +#define VX855_101_000M 0x008D0805 +#define VX855_106_500M 0x00950805 +#define VX855_108_000M 0x00970805 +#define VX855_110_125M 0x00990805 +#define VX855_112_000M 0x009D0805 +#define VX855_113_309M 0x009F0805 +#define VX855_115_000M 0x00A10805 +#define VX855_118_840M 0x00A60805 +#define VX855_119_000M 0x00A70805 +#define VX855_121_750M 0x00AA0805 /* 121.704MHz */ +#define VX855_122_614M 0x00AC0805 +#define VX855_126_266M 0x00B10805 +#define VX855_130_250M 0x00B60805 /* 130.250 */ +#define VX855_135_000M 0x00BD0805 +#define VX855_136_700M 0x00BF0805 +#define VX855_137_750M 0x00C10805 +#define VX855_138_400M 0x00C20805 +#define VX855_144_300M 0x00CA0805 +#define VX855_146_760M 0x00CE0805 +#define VX855_148_500M 0x00D00805 +#define VX855_153_920M 0x00540402 +#define VX855_156_000M 0x006C0405 +#define VX855_156_867M 0x006E0405 +#define VX855_157_500M 0x006E0405 +#define VX855_162_000M 0x00710405 +#define VX855_172_798M 0x00790405 +#define VX855_187_000M 0x00830405 +#define VX855_193_295M 0x00870405 +#define VX855_202_500M 0x008E0405 +#define VX855_204_000M 0x008F0405 +#define VX855_218_500M 0x00990405 +#define VX855_229_500M 0x00A10405 +#define VX855_234_000M 0x00A40405 +#define VX855_267_250M 0x00BB0405 +#define VX855_297_500M 0x00D00405 +#define VX855_339_500M 0x00770005 +#define VX855_340_772M 0x00770005 + + /* Definition CRTC Timing Index */ #define H_TOTAL_INDEX 0 #define H_ADDR_INDEX 1 diff --git a/drivers/video/via/via_i2c.c b/drivers/video/via/via_i2c.c index 0f3ed4eb236d..15543e968248 100644 --- a/drivers/video/via/via_i2c.c +++ b/drivers/video/via/via_i2c.c @@ -97,7 +97,7 @@ int viafb_i2c_readbyte(u8 slave_addr, u8 index, u8 *pdata) mm1[0] = index; msgs[0].len = 1; msgs[1].len = 1; msgs[0].buf = mm1; msgs[1].buf = pdata; - i2c_transfer(&viaparinfo->i2c_stuff.adapter, msgs, 2); + i2c_transfer(&viaparinfo->shared->i2c_stuff.adapter, msgs, 2); return 0; } @@ -111,7 +111,7 @@ int viafb_i2c_writebyte(u8 slave_addr, u8 index, u8 data) msgs.addr = slave_addr / 2; msgs.len = 2; msgs.buf = msg; - return i2c_transfer(&viaparinfo->i2c_stuff.adapter, &msgs, 1); + return i2c_transfer(&viaparinfo->shared->i2c_stuff.adapter, &msgs, 1); } int viafb_i2c_readbytes(u8 slave_addr, u8 index, u8 *buff, int buff_len) @@ -125,53 +125,53 @@ int viafb_i2c_readbytes(u8 slave_addr, u8 index, u8 *buff, int buff_len) mm1[0] = index; msgs[0].len = 1; msgs[1].len = buff_len; msgs[0].buf = mm1; msgs[1].buf = buff; - i2c_transfer(&viaparinfo->i2c_stuff.adapter, msgs, 2); + i2c_transfer(&viaparinfo->shared->i2c_stuff.adapter, msgs, 2); return 0; } int viafb_create_i2c_bus(void *viapar) { int ret; - struct viafb_par *par = (struct viafb_par *)viapar; - - strcpy(par->i2c_stuff.adapter.name, "via_i2c"); - par->i2c_stuff.i2c_port = 0x0; - par->i2c_stuff.adapter.owner = THIS_MODULE; - par->i2c_stuff.adapter.id = 0x01FFFF; - par->i2c_stuff.adapter.class = 0; - par->i2c_stuff.adapter.algo_data = &par->i2c_stuff.algo; - par->i2c_stuff.adapter.dev.parent = NULL; - par->i2c_stuff.algo.setsda = via_i2c_setsda; - par->i2c_stuff.algo.setscl = via_i2c_setscl; - par->i2c_stuff.algo.getsda = via_i2c_getsda; - par->i2c_stuff.algo.getscl = via_i2c_getscl; - par->i2c_stuff.algo.udelay = 40; - par->i2c_stuff.algo.timeout = 20; - par->i2c_stuff.algo.data = &par->i2c_stuff; - - i2c_set_adapdata(&par->i2c_stuff.adapter, &par->i2c_stuff); + struct via_i2c_stuff *i2c_stuff = + &((struct viafb_par *)viapar)->shared->i2c_stuff; + + strcpy(i2c_stuff->adapter.name, "via_i2c"); + i2c_stuff->i2c_port = 0x0; + i2c_stuff->adapter.owner = THIS_MODULE; + i2c_stuff->adapter.id = 0x01FFFF; + i2c_stuff->adapter.class = 0; + i2c_stuff->adapter.algo_data = &i2c_stuff->algo; + i2c_stuff->adapter.dev.parent = NULL; + i2c_stuff->algo.setsda = via_i2c_setsda; + i2c_stuff->algo.setscl = via_i2c_setscl; + i2c_stuff->algo.getsda = via_i2c_getsda; + i2c_stuff->algo.getscl = via_i2c_getscl; + i2c_stuff->algo.udelay = 40; + i2c_stuff->algo.timeout = 20; + i2c_stuff->algo.data = i2c_stuff; + + i2c_set_adapdata(&i2c_stuff->adapter, i2c_stuff); /* Raise SCL and SDA */ - par->i2c_stuff.i2c_port = I2CPORTINDEX; - via_i2c_setsda(&par->i2c_stuff, 1); - via_i2c_setscl(&par->i2c_stuff, 1); + i2c_stuff->i2c_port = I2CPORTINDEX; + via_i2c_setsda(i2c_stuff, 1); + via_i2c_setscl(i2c_stuff, 1); - par->i2c_stuff.i2c_port = GPIOPORTINDEX; - via_i2c_setsda(&par->i2c_stuff, 1); - via_i2c_setscl(&par->i2c_stuff, 1); + i2c_stuff->i2c_port = GPIOPORTINDEX; + via_i2c_setsda(i2c_stuff, 1); + via_i2c_setscl(i2c_stuff, 1); udelay(20); - ret = i2c_bit_add_bus(&par->i2c_stuff.adapter); + ret = i2c_bit_add_bus(&i2c_stuff->adapter); if (ret == 0) - DEBUG_MSG("I2C bus %s registered.\n", - par->i2c_stuff.adapter.name); + DEBUG_MSG("I2C bus %s registered.\n", i2c_stuff->adapter.name); else DEBUG_MSG("Failed to register I2C bus %s.\n", - par->i2c_stuff.adapter.name); + i2c_stuff->adapter.name); return ret; } void viafb_delete_i2c_buss(void *par) { - i2c_del_adapter(&((struct viafb_par *)par)->i2c_stuff.adapter); + i2c_del_adapter(&((struct viafb_par *)par)->shared->i2c_stuff.adapter); } diff --git a/drivers/video/via/viafbdev.c b/drivers/video/via/viafbdev.c index 72833f3334b5..56ec696e8afa 100644 --- a/drivers/video/via/viafbdev.c +++ b/drivers/video/via/viafbdev.c @@ -20,11 +20,12 @@ */ #include <linux/module.h> +#include <linux/seq_file.h> +#include <linux/stat.h> #define _MASTER_FILE #include "global.h" -static int MAX_CURS = 32; static struct fb_var_screeninfo default_var; static char *viafb_name = "Via"; static u32 pseudo_pal[17]; @@ -33,12 +34,11 @@ static u32 pseudo_pal[17]; static char *viafb_mode = "640x480"; static char *viafb_mode1 = "640x480"; +static int viafb_accel = 1; + /* Added for specifying active devices.*/ char *viafb_active_dev = ""; -/* Added for specifying video on devices.*/ -char *viafb_video_dev = ""; - /*Added for specify lcd output port*/ char *viafb_lcd_port = ""; char *viafb_dvi_port = ""; @@ -50,71 +50,20 @@ static void apply_second_mode_setting(struct fb_var_screeninfo *sec_var); static void retrieve_device_setting(struct viafb_ioctl_setting *setting_info); -static void viafb_set_video_device(u32 video_dev_info); -static void viafb_get_video_device(u32 *video_dev_info); - -/* Mode information */ -static const struct viafb_modeinfo viafb_modentry[] = { - {480, 640, VIA_RES_480X640}, - {640, 480, VIA_RES_640X480}, - {800, 480, VIA_RES_800X480}, - {800, 600, VIA_RES_800X600}, - {1024, 768, VIA_RES_1024X768}, - {1152, 864, VIA_RES_1152X864}, - {1280, 1024, VIA_RES_1280X1024}, - {1600, 1200, VIA_RES_1600X1200}, - {1440, 1050, VIA_RES_1440X1050}, - {1280, 768, VIA_RES_1280X768,}, - {1280, 800, VIA_RES_1280X800}, - {1280, 960, VIA_RES_1280X960}, - {1920, 1440, VIA_RES_1920X1440}, - {848, 480, VIA_RES_848X480}, - {1400, 1050, VIA_RES_1400X1050}, - {720, 480, VIA_RES_720X480}, - {720, 576, VIA_RES_720X576}, - {1024, 512, VIA_RES_1024X512}, - {1024, 576, VIA_RES_1024X576}, - {1024, 600, VIA_RES_1024X600}, - {1280, 720, VIA_RES_1280X720}, - {1920, 1080, VIA_RES_1920X1080}, - {1366, 768, VIA_RES_1368X768}, - {1680, 1050, VIA_RES_1680X1050}, - {960, 600, VIA_RES_960X600}, - {1000, 600, VIA_RES_1000X600}, - {1024, 576, VIA_RES_1024X576}, - {1024, 600, VIA_RES_1024X600}, - {1088, 612, VIA_RES_1088X612}, - {1152, 720, VIA_RES_1152X720}, - {1200, 720, VIA_RES_1200X720}, - {1280, 600, VIA_RES_1280X600}, - {1360, 768, VIA_RES_1360X768}, - {1440, 900, VIA_RES_1440X900}, - {1600, 900, VIA_RES_1600X900}, - {1600, 1024, VIA_RES_1600X1024}, - {1792, 1344, VIA_RES_1792X1344}, - {1856, 1392, VIA_RES_1856X1392}, - {1920, 1200, VIA_RES_1920X1200}, - {2048, 1536, VIA_RES_2048X1536}, - {0, 0, VIA_RES_INVALID} -}; static struct fb_ops viafb_ops; -static int viafb_update_fix(struct fb_fix_screeninfo *fix, struct fb_info *info) -{ - struct viafb_par *ppar; - ppar = info->par; - - DEBUG_MSG(KERN_INFO "viafb_update_fix!\n"); - fix->visual = - ppar->bpp == 8 ? FB_VISUAL_PSEUDOCOLOR : FB_VISUAL_TRUECOLOR; - fix->line_length = ppar->linelength; +static void viafb_update_fix(struct fb_info *info) +{ + u32 bpp = info->var.bits_per_pixel; - return 0; + info->fix.visual = + bpp == 8 ? FB_VISUAL_PSEUDOCOLOR : FB_VISUAL_TRUECOLOR; + info->fix.line_length = + ((info->var.xres_virtual + 7) & ~7) * bpp / 8; } - static void viafb_setup_fixinfo(struct fb_fix_screeninfo *fix, struct viafb_par *viaparinfo) { @@ -123,8 +72,6 @@ static void viafb_setup_fixinfo(struct fb_fix_screeninfo *fix, fix->smem_start = viaparinfo->fbmem; fix->smem_len = viaparinfo->fbmem_free; - fix->mmio_start = viaparinfo->mmio_base; - fix->mmio_len = viaparinfo->mmio_len; fix->type = FB_TYPE_PACKED_PIXELS; fix->type_aux = 0; @@ -147,28 +94,12 @@ static int viafb_release(struct fb_info *info, int user) return 0; } -static void viafb_update_viafb_par(struct fb_info *info) -{ - struct viafb_par *ppar; - - ppar = info->par; - ppar->bpp = info->var.bits_per_pixel; - ppar->linelength = ((info->var.xres_virtual + 7) & ~7) * ppar->bpp / 8; - ppar->hres = info->var.xres; - ppar->vres = info->var.yres; - ppar->xoffset = info->var.xoffset; - ppar->yoffset = info->var.yoffset; -} - static int viafb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) { int vmode_index, htotal, vtotal; - struct viafb_par *ppar; + struct viafb_par *ppar = info->par; u32 long_refresh; - struct viafb_par *p_viafb_par; - ppar = info->par; - DEBUG_MSG(KERN_INFO "viafb_check_var!\n"); /* Sanity check */ @@ -212,23 +143,21 @@ static int viafb_check_var(struct fb_var_screeninfo *var, /* Adjust var according to our driver's own table */ viafb_fill_var_timing_info(var, viafb_refresh, vmode_index); - - /* This is indeed a patch for VT3353 */ - if (!info->par) - return -1; - p_viafb_par = (struct viafb_par *)info->par; - if (p_viafb_par->chip_info->gfx_chip_name == UNICHROME_VX800) - var->accel_flags = 0; + if (info->var.accel_flags & FB_ACCELF_TEXT && + !ppar->shared->engine_mmio) + info->var.accel_flags = 0; return 0; } static int viafb_set_par(struct fb_info *info) { + struct viafb_par *viapar = info->par; int vmode_index; int vmode_index1 = 0; DEBUG_MSG(KERN_INFO "viafb_set_par!\n"); + viapar->depth = fb_get_color_depth(&info->var, &info->fix); viafb_update_device_setting(info->var.xres, info->var.yres, info->var.bits_per_pixel, viafb_refresh, 0); @@ -252,21 +181,12 @@ static int viafb_set_par(struct fb_info *info) info->var.bits_per_pixel, vmode_index1, viafb_second_xres, viafb_second_yres, viafb_bpp1); - /*We should set memory offset according virtual_x */ - /*Fix me:put this function into viafb_setmode */ - viafb_memory_pitch_patch(info); - - /* Update ***fb_par information */ - viafb_update_viafb_par(info); - - /* Update other fixed information */ - viafb_update_fix(&info->fix, info); + viafb_update_fix(info); viafb_bpp = info->var.bits_per_pixel; - /* Update viafb_accel, it is necessary to our 2D accelerate */ - viafb_accel = info->var.accel_flags; - - if (viafb_accel) - viafb_set_2d_color_depth(info->var.bits_per_pixel); + if (info->var.accel_flags & FB_ACCELF_TEXT) + info->flags &= ~FBINFO_HWACCEL_DISABLED; + else + info->flags |= FBINFO_HWACCEL_DISABLED; } return 0; @@ -503,12 +423,7 @@ static int viafb_pan_display(struct fb_var_screeninfo *var, var->bits_per_pixel / 16; DEBUG_MSG(KERN_INFO "\nviafb_pan_display,offset =%d ", offset); - - viafb_write_reg_mask(0x48, 0x3d4, ((offset >> 24) & 0x3), 0x3); - viafb_write_reg_mask(0x34, 0x3d4, ((offset >> 16) & 0xff), 0xff); - viafb_write_reg_mask(0x0c, 0x3d4, ((offset >> 8) & 0xff), 0xff); - viafb_write_reg_mask(0x0d, 0x3d4, (offset & 0xff), 0xff); - + viafb_set_primary_address(offset); return 0; } @@ -560,7 +475,6 @@ static int viafb_ioctl(struct fb_info *info, u_int cmd, u_long arg) u32 __user *argp = (u32 __user *) arg; u32 gpu32; - u32 video_dev_info = 0; DEBUG_MSG(KERN_INFO "viafb_ioctl: 0x%X !!\n", cmd); memset(&u, 0, sizeof(u)); @@ -792,15 +706,6 @@ static int viafb_ioctl(struct fb_info *info, u_int cmd, u_long arg) if (put_user(state_info, argp)) return -EFAULT; break; - case VIAFB_SET_VIDEO_DEVICE: - get_user(video_dev_info, argp); - viafb_set_video_device(video_dev_info); - break; - case VIAFB_GET_VIDEO_DEVICE: - viafb_get_video_device(&video_dev_info); - if (put_user(video_dev_info, argp)) - return -EFAULT; - break; case VIAFB_SYNC_SURFACE: DEBUG_MSG(KERN_INFO "lobo VIAFB_SYNC_SURFACE\n"); break; @@ -866,10 +771,12 @@ static int viafb_ioctl(struct fb_info *info, u_int cmd, u_long arg) static void viafb_fillrect(struct fb_info *info, const struct fb_fillrect *rect) { - u32 col = 0, rop = 0; - int pitch; + struct viafb_par *viapar = info->par; + struct viafb_shared *shared = viapar->shared; + u32 fg_color; + u8 rop; - if (!viafb_accel) { + if (info->flags & FBINFO_HWACCEL_DISABLED || !shared->hw_bitblt) { cfb_fillrect(info, rect); return; } @@ -877,68 +784,31 @@ static void viafb_fillrect(struct fb_info *info, if (!rect->width || !rect->height) return; - switch (rect->rop) { - case ROP_XOR: + if (info->fix.visual == FB_VISUAL_TRUECOLOR) + fg_color = ((u32 *)info->pseudo_palette)[rect->color]; + else + fg_color = rect->color; + + if (rect->rop == ROP_XOR) rop = 0x5A; - break; - case ROP_COPY: - default: + else rop = 0xF0; - break; - } - - switch (info->var.bits_per_pixel) { - case 8: - col = rect->color; - break; - case 16: - col = ((u32 *) (info->pseudo_palette))[rect->color]; - break; - case 32: - col = ((u32 *) (info->pseudo_palette))[rect->color]; - break; - } - - /* BitBlt Source Address */ - writel(0x0, viaparinfo->io_virt + VIA_REG_SRCPOS); - /* Source Base Address */ - writel(0x0, viaparinfo->io_virt + VIA_REG_SRCBASE); - /* Destination Base Address */ - writel(((unsigned long) (info->screen_base) - - (unsigned long) viafb_FB_MM) >> 3, - viaparinfo->io_virt + VIA_REG_DSTBASE); - /* Pitch */ - pitch = (info->var.xres_virtual + 7) & ~7; - writel(VIA_PITCH_ENABLE | - (((pitch * - info->var.bits_per_pixel >> 3) >> 3) | - (((pitch * info-> - var.bits_per_pixel >> 3) >> 3) << 16)), - viaparinfo->io_virt + VIA_REG_PITCH); - /* BitBlt Destination Address */ - writel(((rect->dy << 16) | rect->dx), - viaparinfo->io_virt + VIA_REG_DSTPOS); - /* Dimension: width & height */ - writel((((rect->height - 1) << 16) | (rect->width - 1)), - viaparinfo->io_virt + VIA_REG_DIMENSION); - /* Forground color or Destination color */ - writel(col, viaparinfo->io_virt + VIA_REG_FGCOLOR); - /* GE Command */ - writel((0x01 | 0x2000 | (rop << 24)), - viaparinfo->io_virt + VIA_REG_GECMD); + DEBUG_MSG(KERN_DEBUG "viafb 2D engine: fillrect\n"); + if (shared->hw_bitblt(shared->engine_mmio, VIA_BITBLT_FILL, + rect->width, rect->height, info->var.bits_per_pixel, + viapar->vram_addr, info->fix.line_length, rect->dx, rect->dy, + NULL, 0, 0, 0, 0, fg_color, 0, rop)) + cfb_fillrect(info, rect); } static void viafb_copyarea(struct fb_info *info, const struct fb_copyarea *area) { - u32 dy = area->dy, sy = area->sy, direction = 0x0; - u32 sx = area->sx, dx = area->dx, width = area->width; - int pitch; - - DEBUG_MSG(KERN_INFO "viafb_copyarea!!\n"); + struct viafb_par *viapar = info->par; + struct viafb_shared *shared = viapar->shared; - if (!viafb_accel) { + if (info->flags & FBINFO_HWACCEL_DISABLED || !shared->hw_bitblt) { cfb_copyarea(info, area); return; } @@ -946,263 +816,148 @@ static void viafb_copyarea(struct fb_info *info, if (!area->width || !area->height) return; - if (sy < dy) { - dy += area->height - 1; - sy += area->height - 1; - direction |= 0x4000; - } - - if (sx < dx) { - dx += width - 1; - sx += width - 1; - direction |= 0x8000; - } - - /* Source Base Address */ - writel(((unsigned long) (info->screen_base) - - (unsigned long) viafb_FB_MM) >> 3, - viaparinfo->io_virt + VIA_REG_SRCBASE); - /* Destination Base Address */ - writel(((unsigned long) (info->screen_base) - - (unsigned long) viafb_FB_MM) >> 3, - viaparinfo->io_virt + VIA_REG_DSTBASE); - /* Pitch */ - pitch = (info->var.xres_virtual + 7) & ~7; - /* VIA_PITCH_ENABLE can be omitted now. */ - writel(VIA_PITCH_ENABLE | - (((pitch * - info->var.bits_per_pixel >> 3) >> 3) | (((pitch * - info->var. - bits_per_pixel - >> 3) >> 3) - << 16)), - viaparinfo->io_virt + VIA_REG_PITCH); - /* BitBlt Source Address */ - writel(((sy << 16) | sx), viaparinfo->io_virt + VIA_REG_SRCPOS); - /* BitBlt Destination Address */ - writel(((dy << 16) | dx), viaparinfo->io_virt + VIA_REG_DSTPOS); - /* Dimension: width & height */ - writel((((area->height - 1) << 16) | (area->width - 1)), - viaparinfo->io_virt + VIA_REG_DIMENSION); - /* GE Command */ - writel((0x01 | direction | (0xCC << 24)), - viaparinfo->io_virt + VIA_REG_GECMD); - + DEBUG_MSG(KERN_DEBUG "viafb 2D engine: copyarea\n"); + if (shared->hw_bitblt(shared->engine_mmio, VIA_BITBLT_COLOR, + area->width, area->height, info->var.bits_per_pixel, + viapar->vram_addr, info->fix.line_length, area->dx, area->dy, + NULL, viapar->vram_addr, info->fix.line_length, + area->sx, area->sy, 0, 0, 0)) + cfb_copyarea(info, area); } static void viafb_imageblit(struct fb_info *info, const struct fb_image *image) { - u32 size, bg_col = 0, fg_col = 0, *udata; - int i; - int pitch; + struct viafb_par *viapar = info->par; + struct viafb_shared *shared = viapar->shared; + u32 fg_color = 0, bg_color = 0; + u8 op; - if (!viafb_accel) { + if (info->flags & FBINFO_HWACCEL_DISABLED || !shared->hw_bitblt || + (image->depth != 1 && image->depth != viapar->depth)) { cfb_imageblit(info, image); return; } - udata = (u32 *) image->data; - - switch (info->var.bits_per_pixel) { - case 8: - bg_col = image->bg_color; - fg_col = image->fg_color; - break; - case 16: - bg_col = ((u32 *) (info->pseudo_palette))[image->bg_color]; - fg_col = ((u32 *) (info->pseudo_palette))[image->fg_color]; - break; - case 32: - bg_col = ((u32 *) (info->pseudo_palette))[image->bg_color]; - fg_col = ((u32 *) (info->pseudo_palette))[image->fg_color]; - break; - } - size = image->width * image->height; - - /* Source Base Address */ - writel(0x0, viaparinfo->io_virt + VIA_REG_SRCBASE); - /* Destination Base Address */ - writel(((unsigned long) (info->screen_base) - - (unsigned long) viafb_FB_MM) >> 3, - viaparinfo->io_virt + VIA_REG_DSTBASE); - /* Pitch */ - pitch = (info->var.xres_virtual + 7) & ~7; - writel(VIA_PITCH_ENABLE | - (((pitch * - info->var.bits_per_pixel >> 3) >> 3) | (((pitch * - info->var. - bits_per_pixel - >> 3) >> 3) - << 16)), - viaparinfo->io_virt + VIA_REG_PITCH); - /* BitBlt Source Address */ - writel(0x0, viaparinfo->io_virt + VIA_REG_SRCPOS); - /* BitBlt Destination Address */ - writel(((image->dy << 16) | image->dx), - viaparinfo->io_virt + VIA_REG_DSTPOS); - /* Dimension: width & height */ - writel((((image->height - 1) << 16) | (image->width - 1)), - viaparinfo->io_virt + VIA_REG_DIMENSION); - /* fb color */ - writel(fg_col, viaparinfo->io_virt + VIA_REG_FGCOLOR); - /* bg color */ - writel(bg_col, viaparinfo->io_virt + VIA_REG_BGCOLOR); - /* GE Command */ - writel(0xCC020142, viaparinfo->io_virt + VIA_REG_GECMD); - - for (i = 0; i < size / 4; i++) { - writel(*udata, viaparinfo->io_virt + VIA_MMIO_BLTBASE); - udata++; - } + if (image->depth == 1) { + op = VIA_BITBLT_MONO; + if (info->fix.visual == FB_VISUAL_TRUECOLOR) { + fg_color = + ((u32 *)info->pseudo_palette)[image->fg_color]; + bg_color = + ((u32 *)info->pseudo_palette)[image->bg_color]; + } else { + fg_color = image->fg_color; + bg_color = image->bg_color; + } + } else + op = VIA_BITBLT_COLOR; + DEBUG_MSG(KERN_DEBUG "viafb 2D engine: imageblit\n"); + if (shared->hw_bitblt(shared->engine_mmio, op, + image->width, image->height, info->var.bits_per_pixel, + viapar->vram_addr, info->fix.line_length, image->dx, image->dy, + (u32 *)image->data, 0, 0, 0, 0, fg_color, bg_color, 0)) + cfb_imageblit(info, image); } static int viafb_cursor(struct fb_info *info, struct fb_cursor *cursor) { - u32 temp, xx, yy, bg_col = 0, fg_col = 0; - int i, j = 0; - static int hw_cursor; - struct viafb_par *p_viafb_par; - - if (viafb_accel) - hw_cursor = 1; - - if (!viafb_accel) { - if (hw_cursor) { - viafb_show_hw_cursor(info, HW_Cursor_OFF); - hw_cursor = 0; - } - return -ENODEV; - } + struct viafb_par *viapar = info->par; + void __iomem *engine = viapar->shared->engine_mmio; + u32 temp, xx, yy, bg_color = 0, fg_color = 0, + chip_name = viapar->shared->chip_info.gfx_chip_name; + int i, j = 0, cur_size = 64; - if ((((struct viafb_par *)(info->par))->iga_path == IGA2) - && (viaparinfo->chip_info->gfx_chip_name == UNICHROME_CLE266)) + if (info->flags & FBINFO_HWACCEL_DISABLED || info != viafbinfo) return -ENODEV; - /* When duoview and using lcd , use soft cursor */ - if (viafb_LCD_ON || ((struct viafb_par *)(info->par))->duoview) + if (chip_name == UNICHROME_CLE266 && viapar->iga_path == IGA2) return -ENODEV; viafb_show_hw_cursor(info, HW_Cursor_OFF); - viacursor = *cursor; if (cursor->set & FB_CUR_SETHOT) { - viacursor.hot = cursor->hot; - temp = ((viacursor.hot.x) << 16) + viacursor.hot.y; - writel(temp, viaparinfo->io_virt + VIA_REG_CURSOR_ORG); + temp = (cursor->hot.x << 16) + cursor->hot.y; + writel(temp, engine + VIA_REG_CURSOR_ORG); } if (cursor->set & FB_CUR_SETPOS) { - viacursor.image.dx = cursor->image.dx; - viacursor.image.dy = cursor->image.dy; yy = cursor->image.dy - info->var.yoffset; xx = cursor->image.dx - info->var.xoffset; temp = yy & 0xFFFF; temp |= (xx << 16); - writel(temp, viaparinfo->io_virt + VIA_REG_CURSOR_POS); + writel(temp, engine + VIA_REG_CURSOR_POS); } - if (cursor->set & FB_CUR_SETSIZE) { - temp = readl(viaparinfo->io_virt + VIA_REG_CURSOR_MODE); + if (cursor->image.width <= 32 && cursor->image.height <= 32) + cur_size = 32; + else if (cursor->image.width <= 64 && cursor->image.height <= 64) + cur_size = 64; + else { + printk(KERN_WARNING "viafb_cursor: The cursor is too large " + "%dx%d", cursor->image.width, cursor->image.height); + return -ENXIO; + } - if ((cursor->image.width <= 32) - && (cursor->image.height <= 32)) { - MAX_CURS = 32; + if (cursor->set & FB_CUR_SETSIZE) { + temp = readl(engine + VIA_REG_CURSOR_MODE); + if (cur_size == 32) temp |= 0x2; - } else if ((cursor->image.width <= 64) - && (cursor->image.height <= 64)) { - MAX_CURS = 64; - temp &= 0xFFFFFFFD; - } else { - DEBUG_MSG(KERN_INFO - "The cursor image is biger than 64x64 bits...\n"); - return -ENXIO; - } - writel(temp, viaparinfo->io_virt + VIA_REG_CURSOR_MODE); + else + temp &= ~0x2; - viacursor.image.height = cursor->image.height; - viacursor.image.width = cursor->image.width; + writel(temp, engine + VIA_REG_CURSOR_MODE); } if (cursor->set & FB_CUR_SETCMAP) { - viacursor.image.fg_color = cursor->image.fg_color; - viacursor.image.bg_color = cursor->image.bg_color; - - switch (info->var.bits_per_pixel) { - case 8: - case 16: - case 32: - bg_col = - (0xFF << 24) | - (((info->cmap.red)[viacursor.image.bg_color] & - 0xFF00) << 8) | - ((info->cmap.green)[viacursor.image.bg_color] & - 0xFF00) | - (((info->cmap.blue)[viacursor.image.bg_color] & - 0xFF00) >> 8); - fg_col = - (0xFF << 24) | - (((info->cmap.red)[viacursor.image.fg_color] & - 0xFF00) << 8) | - ((info->cmap.green)[viacursor.image.fg_color] & - 0xFF00) | - (((info->cmap.blue)[viacursor.image.fg_color] & - 0xFF00) >> 8); - break; - default: - return 0; - } - - /* This is indeed a patch for VT3324/VT3353 */ - if (!info->par) - return 0; - p_viafb_par = (struct viafb_par *)info->par; - - if ((p_viafb_par->chip_info->gfx_chip_name == - UNICHROME_CX700) || - ((p_viafb_par->chip_info->gfx_chip_name == - UNICHROME_VX800))) { - bg_col = - (((info->cmap.red)[viacursor.image.bg_color] & - 0xFFC0) << 14) | - (((info->cmap.green)[viacursor.image.bg_color] & - 0xFFC0) << 4) | - (((info->cmap.blue)[viacursor.image.bg_color] & - 0xFFC0) >> 6); - fg_col = - (((info->cmap.red)[viacursor.image.fg_color] & - 0xFFC0) << 14) | - (((info->cmap.green)[viacursor.image.fg_color] & - 0xFFC0) << 4) | - (((info->cmap.blue)[viacursor.image.fg_color] & - 0xFFC0) >> 6); + fg_color = cursor->image.fg_color; + bg_color = cursor->image.bg_color; + if (chip_name == UNICHROME_CX700 || + chip_name == UNICHROME_VX800 || + chip_name == UNICHROME_VX855) { + fg_color = + ((info->cmap.red[fg_color] & 0xFFC0) << 14) | + ((info->cmap.green[fg_color] & 0xFFC0) << 4) | + ((info->cmap.blue[fg_color] & 0xFFC0) >> 6); + bg_color = + ((info->cmap.red[bg_color] & 0xFFC0) << 14) | + ((info->cmap.green[bg_color] & 0xFFC0) << 4) | + ((info->cmap.blue[bg_color] & 0xFFC0) >> 6); + } else { + fg_color = + ((info->cmap.red[fg_color] & 0xFF00) << 8) | + (info->cmap.green[fg_color] & 0xFF00) | + ((info->cmap.blue[fg_color] & 0xFF00) >> 8); + bg_color = + ((info->cmap.red[bg_color] & 0xFF00) << 8) | + (info->cmap.green[bg_color] & 0xFF00) | + ((info->cmap.blue[bg_color] & 0xFF00) >> 8); } - writel(bg_col, viaparinfo->io_virt + VIA_REG_CURSOR_BG); - writel(fg_col, viaparinfo->io_virt + VIA_REG_CURSOR_FG); + writel(bg_color, engine + VIA_REG_CURSOR_BG); + writel(fg_color, engine + VIA_REG_CURSOR_FG); } if (cursor->set & FB_CUR_SETSHAPE) { struct { - u8 data[CURSOR_SIZE / 8]; - u32 bak[CURSOR_SIZE / 32]; + u8 data[CURSOR_SIZE]; + u32 bak[CURSOR_SIZE / 4]; } *cr_data = kzalloc(sizeof(*cr_data), GFP_ATOMIC); - int size = - ((viacursor.image.width + 7) >> 3) * - viacursor.image.height; + int size = ((cursor->image.width + 7) >> 3) * + cursor->image.height; - if (cr_data == NULL) - goto out; + if (!cr_data) + return -ENOMEM; - if (MAX_CURS == 32) { - for (i = 0; i < (CURSOR_SIZE / 32); i++) { + if (cur_size == 32) { + for (i = 0; i < (CURSOR_SIZE / 4); i++) { cr_data->bak[i] = 0x0; cr_data->bak[i + 1] = 0xFFFFFFFF; i += 1; } - } else if (MAX_CURS == 64) { - for (i = 0; i < (CURSOR_SIZE / 32); i++) { + } else { + for (i = 0; i < (CURSOR_SIZE / 4); i++) { cr_data->bak[i] = 0x0; cr_data->bak[i + 1] = 0x0; cr_data->bak[i + 2] = 0xFFFFFFFF; @@ -1211,27 +966,27 @@ static int viafb_cursor(struct fb_info *info, struct fb_cursor *cursor) } } - switch (viacursor.rop) { + switch (cursor->rop) { case ROP_XOR: for (i = 0; i < size; i++) - cr_data->data[i] = viacursor.mask[i]; + cr_data->data[i] = cursor->mask[i]; break; case ROP_COPY: for (i = 0; i < size; i++) - cr_data->data[i] = viacursor.mask[i]; + cr_data->data[i] = cursor->mask[i]; break; default: break; } - if (MAX_CURS == 32) { + if (cur_size == 32) { for (i = 0; i < size; i++) { cr_data->bak[j] = (u32) cr_data->data[i]; cr_data->bak[j + 1] = ~cr_data->bak[j]; j += 2; } - } else if (MAX_CURS == 64) { + } else { for (i = 0; i < size; i++) { cr_data->bak[j] = (u32) cr_data->data[i]; cr_data->bak[j + 1] = 0x0; @@ -1241,14 +996,12 @@ static int viafb_cursor(struct fb_info *info, struct fb_cursor *cursor) } } - memcpy(((struct viafb_par *)(info->par))->fbmem_virt + - ((struct viafb_par *)(info->par))->cursor_start, - cr_data->bak, CURSOR_SIZE); -out: + memcpy_toio(viafbinfo->screen_base + viapar->shared-> + cursor_vram_addr, cr_data->bak, CURSOR_SIZE); kfree(cr_data); } - if (viacursor.enable) + if (cursor->enable) viafb_show_hw_cursor(info, HW_Cursor_ON); return 0; @@ -1256,8 +1009,8 @@ out: static int viafb_sync(struct fb_info *info) { - if (viafb_accel) - viafb_wait_engine_idle(); + if (!(info->flags & FBINFO_HWACCEL_DISABLED)) + viafb_wait_engine_idle(info); return 0; } @@ -1266,12 +1019,16 @@ int viafb_get_mode_index(int hres, int vres) u32 i; DEBUG_MSG(KERN_INFO "viafb_get_mode_index!\n"); - for (i = 0; viafb_modentry[i].mode_index != VIA_RES_INVALID; i++) - if (viafb_modentry[i].xres == hres && - viafb_modentry[i].yres == vres) + for (i = 0; i < NUM_TOTAL_MODETABLE; i++) + if (CLE266Modes[i].mode_array && + CLE266Modes[i].crtc[0].crtc.hor_addr == hres && + CLE266Modes[i].crtc[0].crtc.ver_addr == vres) break; - return viafb_modentry[i].mode_index; + if (i == NUM_TOTAL_MODETABLE) + return VIA_RES_INVALID; + + return CLE266Modes[i].ModeIndex; } static void check_available_device_to_enable(int device_id) @@ -1375,36 +1132,11 @@ static void viafb_set_device(struct device_t active_dev) viafb_SAMM_ON = active_dev.samm; viafb_primary_dev = active_dev.primary_dev; - viafb_set_start_addr(); + viafb_set_primary_address(0); + viafb_set_secondary_address(viafb_SAMM_ON ? viafb_second_offset : 0); viafb_set_iga_path(); } -static void viafb_set_video_device(u32 video_dev_info) -{ - viaparinfo->video_on_crt = STATE_OFF; - viaparinfo->video_on_dvi = STATE_OFF; - viaparinfo->video_on_lcd = STATE_OFF; - - /* Check available device to enable: */ - if ((video_dev_info & CRT_Device) == CRT_Device) - viaparinfo->video_on_crt = STATE_ON; - else if ((video_dev_info & DVI_Device) == DVI_Device) - viaparinfo->video_on_dvi = STATE_ON; - else if ((video_dev_info & LCD_Device) == LCD_Device) - viaparinfo->video_on_lcd = STATE_ON; -} - -static void viafb_get_video_device(u32 *video_dev_info) -{ - *video_dev_info = None_Device; - if (viaparinfo->video_on_crt == STATE_ON) - *video_dev_info |= CRT_Device; - else if (viaparinfo->video_on_dvi == STATE_ON) - *video_dev_info |= DVI_Device; - else if (viaparinfo->video_on_lcd == STATE_ON) - *video_dev_info |= LCD_Device; -} - static int get_primary_device(void) { int primary_device = 0; @@ -1446,18 +1178,6 @@ static int get_primary_device(void) return primary_device; } -static u8 is_duoview(void) -{ - if (0 == viafb_SAMM_ON) { - if (viafb_LCD_ON + viafb_LCD2_ON + - viafb_DVI_ON + viafb_CRT_ON == 2) - return true; - return false; - } else { - return false; - } -} - static void apply_second_mode_setting(struct fb_var_screeninfo *sec_var) { @@ -1559,14 +1279,13 @@ static int apply_device_setting(struct viafb_ioctl_setting setting_info, if (viafb_SAMM_ON) viafb_primary_dev = setting_info.primary_device; - viafb_set_start_addr(); + viafb_set_primary_address(0); + viafb_set_secondary_address(viafb_SAMM_ON ? viafb_second_offset : 0); viafb_set_iga_path(); } need_set_mode = 1; } - viaparinfo->duoview = is_duoview(); - if (!need_set_mode) { ; } else { @@ -1589,18 +1308,6 @@ static void retrieve_device_setting(struct viafb_ioctl_setting setting_info->device_status |= LCD_Device; if (viafb_LCD2_ON == 1) setting_info->device_status |= LCD2_Device; - if ((viaparinfo->video_on_crt == 1) && (viafb_CRT_ON == 1)) { - setting_info->video_device_status = - viaparinfo->crt_setting_info->iga_path; - } else if ((viaparinfo->video_on_dvi == 1) && (viafb_DVI_ON == 1)) { - setting_info->video_device_status = - viaparinfo->tmds_setting_info->iga_path; - } else if ((viaparinfo->video_on_lcd == 1) && (viafb_LCD_ON == 1)) { - setting_info->video_device_status = - viaparinfo->lvds_setting_info->iga_path; - } else { - setting_info->video_device_status = 0; - } setting_info->samm_status = viafb_SAMM_ON; setting_info->primary_device = get_primary_device(); @@ -1687,25 +1394,6 @@ static void parse_active_dev(void) viafb_CRT_ON = STATE_ON; viafb_SAMM_ON = STATE_OFF; } - viaparinfo->duoview = is_duoview(); -} - -static void parse_video_dev(void) -{ - viaparinfo->video_on_crt = STATE_OFF; - viaparinfo->video_on_dvi = STATE_OFF; - viaparinfo->video_on_lcd = STATE_OFF; - - if (!strncmp(viafb_video_dev, "CRT", 3)) { - /* Video on CRT */ - viaparinfo->video_on_crt = STATE_ON; - } else if (!strncmp(viafb_video_dev, "DVI", 3)) { - /* Video on DVI */ - viaparinfo->video_on_dvi = STATE_ON; - } else if (!strncmp(viafb_video_dev, "LCD", 3)) { - /* Video on LCD */ - viaparinfo->video_on_lcd = STATE_ON; - } } static int parse_port(char *opt_str, int *output_interface) @@ -1754,10 +1442,8 @@ static void parse_dvi_port(void) * DVP1Driving, DFPHigh, DFPLow CR96, SR2A[5], SR1B[1], SR2A[4], SR1E[2], * CR9B, SR65, CR97, CR99 */ -static int viafb_dvp0_proc_read(char *buf, char **start, off_t offset, -int count, int *eof, void *data) +static int viafb_dvp0_proc_show(struct seq_file *m, void *v) { - int len = 0; u8 dvp0_data_dri = 0, dvp0_clk_dri = 0, dvp0 = 0; dvp0_data_dri = (viafb_read_reg(VIASR, SR2A) & BIT5) >> 4 | @@ -1766,13 +1452,17 @@ int count, int *eof, void *data) (viafb_read_reg(VIASR, SR2A) & BIT4) >> 3 | (viafb_read_reg(VIASR, SR1E) & BIT2) >> 2; dvp0 = viafb_read_reg(VIACR, CR96) & 0x0f; - len += - sprintf(buf + len, "%x %x %x\n", dvp0, dvp0_data_dri, dvp0_clk_dri); - *eof = 1; /*Inform kernel end of data */ - return len; + seq_printf(m, "%x %x %x\n", dvp0, dvp0_data_dri, dvp0_clk_dri); + return 0; } -static int viafb_dvp0_proc_write(struct file *file, - const char __user *buffer, unsigned long count, void *data) + +static int viafb_dvp0_proc_open(struct inode *inode, struct file *file) +{ + return single_open(file, viafb_dvp0_proc_show, NULL); +} + +static ssize_t viafb_dvp0_proc_write(struct file *file, + const char __user *buffer, size_t count, loff_t *pos) { char buf[20], *value, *pbuf; u8 reg_val = 0; @@ -1816,21 +1506,33 @@ static int viafb_dvp0_proc_write(struct file *file, } return count; } -static int viafb_dvp1_proc_read(char *buf, char **start, off_t offset, - int count, int *eof, void *data) + +static const struct file_operations viafb_dvp0_proc_fops = { + .owner = THIS_MODULE, + .open = viafb_dvp0_proc_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, + .write = viafb_dvp0_proc_write, +}; + +static int viafb_dvp1_proc_show(struct seq_file *m, void *v) { - int len = 0; u8 dvp1 = 0, dvp1_data_dri = 0, dvp1_clk_dri = 0; dvp1 = viafb_read_reg(VIACR, CR9B) & 0x0f; dvp1_data_dri = (viafb_read_reg(VIASR, SR65) & 0x0c) >> 2; dvp1_clk_dri = viafb_read_reg(VIASR, SR65) & 0x03; - len += - sprintf(buf + len, "%x %x %x\n", dvp1, dvp1_data_dri, dvp1_clk_dri); - *eof = 1; /*Inform kernel end of data */ - return len; + seq_printf(m, "%x %x %x\n", dvp1, dvp1_data_dri, dvp1_clk_dri); + return 0; } -static int viafb_dvp1_proc_write(struct file *file, - const char __user *buffer, unsigned long count, void *data) + +static int viafb_dvp1_proc_open(struct inode *inode, struct file *file) +{ + return single_open(file, viafb_dvp1_proc_show, NULL); +} + +static ssize_t viafb_dvp1_proc_write(struct file *file, + const char __user *buffer, size_t count, loff_t *pos) { char buf[20], *value, *pbuf; u8 reg_val = 0; @@ -1869,18 +1571,30 @@ static int viafb_dvp1_proc_write(struct file *file, return count; } -static int viafb_dfph_proc_read(char *buf, char **start, off_t offset, - int count, int *eof, void *data) +static const struct file_operations viafb_dvp1_proc_fops = { + .owner = THIS_MODULE, + .open = viafb_dvp1_proc_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, + .write = viafb_dvp1_proc_write, +}; + +static int viafb_dfph_proc_show(struct seq_file *m, void *v) { - int len = 0; u8 dfp_high = 0; dfp_high = viafb_read_reg(VIACR, CR97) & 0x0f; - len += sprintf(buf + len, "%x\n", dfp_high); - *eof = 1; /*Inform kernel end of data */ - return len; + seq_printf(m, "%x\n", dfp_high); + return 0; } -static int viafb_dfph_proc_write(struct file *file, - const char __user *buffer, unsigned long count, void *data) + +static int viafb_dfph_proc_open(struct inode *inode, struct file *file) +{ + return single_open(file, viafb_dfph_proc_show, NULL); +} + +static ssize_t viafb_dfph_proc_write(struct file *file, + const char __user *buffer, size_t count, loff_t *pos) { char buf[20]; u8 reg_val = 0; @@ -1895,18 +1609,31 @@ static int viafb_dfph_proc_write(struct file *file, viafb_write_reg_mask(CR97, VIACR, reg_val, 0x0f); return count; } -static int viafb_dfpl_proc_read(char *buf, char **start, off_t offset, - int count, int *eof, void *data) + +static const struct file_operations viafb_dfph_proc_fops = { + .owner = THIS_MODULE, + .open = viafb_dfph_proc_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, + .write = viafb_dfph_proc_write, +}; + +static int viafb_dfpl_proc_show(struct seq_file *m, void *v) { - int len = 0; u8 dfp_low = 0; dfp_low = viafb_read_reg(VIACR, CR99) & 0x0f; - len += sprintf(buf + len, "%x\n", dfp_low); - *eof = 1; /*Inform kernel end of data */ - return len; + seq_printf(m, "%x\n", dfp_low); + return 0; } -static int viafb_dfpl_proc_write(struct file *file, - const char __user *buffer, unsigned long count, void *data) + +static int viafb_dfpl_proc_open(struct inode *inode, struct file *file) +{ + return single_open(file, viafb_dfpl_proc_show, NULL); +} + +static ssize_t viafb_dfpl_proc_write(struct file *file, + const char __user *buffer, size_t count, loff_t *pos) { char buf[20]; u8 reg_val = 0; @@ -1921,10 +1648,18 @@ static int viafb_dfpl_proc_write(struct file *file, viafb_write_reg_mask(CR99, VIACR, reg_val, 0x0f); return count; } -static int viafb_vt1636_proc_read(char *buf, char **start, - off_t offset, int count, int *eof, void *data) + +static const struct file_operations viafb_dfpl_proc_fops = { + .owner = THIS_MODULE, + .open = viafb_dfpl_proc_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, + .write = viafb_dfpl_proc_write, +}; + +static int viafb_vt1636_proc_show(struct seq_file *m, void *v) { - int len = 0; u8 vt1636_08 = 0, vt1636_09 = 0; switch (viaparinfo->chip_info->lvds_chip_info.lvds_chip_name) { case VT1636_LVDS: @@ -1934,7 +1669,7 @@ static int viafb_vt1636_proc_read(char *buf, char **start, vt1636_09 = viafb_gpio_i2c_read_lvds(viaparinfo->lvds_setting_info, &viaparinfo->chip_info->lvds_chip_info, 0x09) & 0x1f; - len += sprintf(buf + len, "%x %x\n", vt1636_08, vt1636_09); + seq_printf(m, "%x %x\n", vt1636_08, vt1636_09); break; default: break; @@ -1947,16 +1682,21 @@ static int viafb_vt1636_proc_read(char *buf, char **start, vt1636_09 = viafb_gpio_i2c_read_lvds(viaparinfo->lvds_setting_info2, &viaparinfo->chip_info->lvds_chip_info2, 0x09) & 0x1f; - len += sprintf(buf + len, " %x %x\n", vt1636_08, vt1636_09); + seq_printf(m, " %x %x\n", vt1636_08, vt1636_09); break; default: break; } - *eof = 1; /*Inform kernel end of data */ - return len; + return 0; } -static int viafb_vt1636_proc_write(struct file *file, - const char __user *buffer, unsigned long count, void *data) + +static int viafb_vt1636_proc_open(struct inode *inode, struct file *file) +{ + return single_open(file, viafb_vt1636_proc_show, NULL); +} + +static ssize_t viafb_vt1636_proc_write(struct file *file, + const char __user *buffer, size_t count, loff_t *pos) { char buf[30], *value, *pbuf; struct IODATA reg_val; @@ -2045,39 +1785,27 @@ static int viafb_vt1636_proc_write(struct file *file, return count; } +static const struct file_operations viafb_vt1636_proc_fops = { + .owner = THIS_MODULE, + .open = viafb_vt1636_proc_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, + .write = viafb_vt1636_proc_write, +}; + static void viafb_init_proc(struct proc_dir_entry **viafb_entry) { - struct proc_dir_entry *entry; *viafb_entry = proc_mkdir("viafb", NULL); if (viafb_entry) { - entry = create_proc_entry("dvp0", 0, *viafb_entry); - if (entry) { - entry->read_proc = viafb_dvp0_proc_read; - entry->write_proc = viafb_dvp0_proc_write; - } - entry = create_proc_entry("dvp1", 0, *viafb_entry); - if (entry) { - entry->read_proc = viafb_dvp1_proc_read; - entry->write_proc = viafb_dvp1_proc_write; - } - entry = create_proc_entry("dfph", 0, *viafb_entry); - if (entry) { - entry->read_proc = viafb_dfph_proc_read; - entry->write_proc = viafb_dfph_proc_write; - } - entry = create_proc_entry("dfpl", 0, *viafb_entry); - if (entry) { - entry->read_proc = viafb_dfpl_proc_read; - entry->write_proc = viafb_dfpl_proc_write; - } + proc_create("dvp0", 0, *viafb_entry, &viafb_dvp0_proc_fops); + proc_create("dvp1", 0, *viafb_entry, &viafb_dvp1_proc_fops); + proc_create("dfph", 0, *viafb_entry, &viafb_dfph_proc_fops); + proc_create("dfpl", 0, *viafb_entry, &viafb_dfpl_proc_fops); if (VT1636_LVDS == viaparinfo->chip_info->lvds_chip_info. lvds_chip_name || VT1636_LVDS == viaparinfo->chip_info->lvds_chip_info2.lvds_chip_name) { - entry = create_proc_entry("vt1636", 0, *viafb_entry); - if (entry) { - entry->read_proc = viafb_vt1636_proc_read; - entry->write_proc = viafb_vt1636_proc_write; - } + proc_create("vt1636", 0, *viafb_entry, &viafb_vt1636_proc_fops); } } @@ -2094,51 +1822,61 @@ static void viafb_remove_proc(struct proc_dir_entry *viafb_entry) remove_proc_entry("viafb", NULL); } -static int __devinit via_pci_probe(void) +static void parse_mode(const char *str, u32 *xres, u32 *yres) { - unsigned long default_xres, default_yres; - char *tmpc, *tmpm; - char *tmpc_sec, *tmpm_sec; + char *ptr; + + *xres = simple_strtoul(str, &ptr, 10); + if (ptr[0] != 'x') + goto out_default; + + *yres = simple_strtoul(&ptr[1], &ptr, 10); + if (ptr[0]) + goto out_default; + + return; + +out_default: + printk(KERN_WARNING "viafb received invalid mode string: %s\n", str); + *xres = 640; + *yres = 480; +} + +static int __devinit via_pci_probe(struct pci_dev *pdev, + const struct pci_device_id *ent) +{ + u32 default_xres, default_yres; int vmode_index; - u32 tmds_length, lvds_length, crt_length, chip_length, viafb_par_length; + u32 viafb_par_length; DEBUG_MSG(KERN_INFO "VIAFB PCI Probe!!\n"); viafb_par_length = ALIGN(sizeof(struct viafb_par), BITS_PER_LONG/8); - tmds_length = ALIGN(sizeof(struct tmds_setting_information), - BITS_PER_LONG/8); - lvds_length = ALIGN(sizeof(struct lvds_setting_information), - BITS_PER_LONG/8); - crt_length = ALIGN(sizeof(struct lvds_setting_information), - BITS_PER_LONG/8); - chip_length = ALIGN(sizeof(struct chip_information), BITS_PER_LONG/8); /* Allocate fb_info and ***_par here, also including some other needed * variables */ - viafbinfo = framebuffer_alloc(viafb_par_length + 2 * lvds_length + - tmds_length + crt_length + chip_length, NULL); + viafbinfo = framebuffer_alloc(viafb_par_length + + ALIGN(sizeof(struct viafb_shared), BITS_PER_LONG/8), + &pdev->dev); if (!viafbinfo) { printk(KERN_ERR"Could not allocate memory for viafb_info.\n"); return -ENODEV; } viaparinfo = (struct viafb_par *)viafbinfo->par; - viaparinfo->tmds_setting_info = (struct tmds_setting_information *) - ((unsigned long)viaparinfo + viafb_par_length); - viaparinfo->lvds_setting_info = (struct lvds_setting_information *) - ((unsigned long)viaparinfo->tmds_setting_info + tmds_length); - viaparinfo->lvds_setting_info2 = (struct lvds_setting_information *) - ((unsigned long)viaparinfo->lvds_setting_info + lvds_length); - viaparinfo->crt_setting_info = (struct crt_setting_information *) - ((unsigned long)viaparinfo->lvds_setting_info2 + lvds_length); - viaparinfo->chip_info = (struct chip_information *) - ((unsigned long)viaparinfo->crt_setting_info + crt_length); + viaparinfo->shared = viafbinfo->par + viafb_par_length; + viaparinfo->vram_addr = 0; + viaparinfo->tmds_setting_info = &viaparinfo->shared->tmds_setting_info; + viaparinfo->lvds_setting_info = &viaparinfo->shared->lvds_setting_info; + viaparinfo->lvds_setting_info2 = + &viaparinfo->shared->lvds_setting_info2; + viaparinfo->crt_setting_info = &viaparinfo->shared->crt_setting_info; + viaparinfo->chip_info = &viaparinfo->shared->chip_info; if (viafb_dual_fb) viafb_SAMM_ON = 1; parse_active_dev(); - parse_video_dev(); parse_lcd_port(); parse_dvi_port(); @@ -2149,32 +1887,32 @@ static int __devinit via_pci_probe(void) /* Set up I2C bus stuff */ viafb_create_i2c_bus(viaparinfo); - viafb_init_chip_info(); - viafb_get_fb_info(&viaparinfo->fbmem, &viaparinfo->memsize); + viafb_init_chip_info(pdev, ent); + viaparinfo->fbmem = pci_resource_start(pdev, 0); + viaparinfo->memsize = viafb_get_fb_size_from_pci(); viaparinfo->fbmem_free = viaparinfo->memsize; viaparinfo->fbmem_used = 0; - viaparinfo->fbmem_virt = ioremap_nocache(viaparinfo->fbmem, + viafbinfo->screen_base = ioremap_nocache(viaparinfo->fbmem, viaparinfo->memsize); - viafbinfo->screen_base = (char *)viaparinfo->fbmem_virt; - - if (!viaparinfo->fbmem_virt) { + if (!viafbinfo->screen_base) { printk(KERN_INFO "ioremap failed\n"); - return -1; + return -ENOMEM; } - viafb_get_mmio_info(&viaparinfo->mmio_base, &viaparinfo->mmio_len); - viaparinfo->io_virt = ioremap_nocache(viaparinfo->mmio_base, - viaparinfo->mmio_len); - + viafbinfo->fix.mmio_start = pci_resource_start(pdev, 1); + viafbinfo->fix.mmio_len = pci_resource_len(pdev, 1); viafbinfo->node = 0; viafbinfo->fbops = &viafb_ops; viafbinfo->flags = FBINFO_DEFAULT | FBINFO_HWACCEL_YPAN; viafbinfo->pseudo_palette = pseudo_pal; - if (viafb_accel) { - viafb_init_accel(); - viafb_init_2d_engine(); - viafb_hw_cursor_init(); + if (viafb_accel && !viafb_init_engine(viafbinfo)) { + viafbinfo->flags |= FBINFO_HWACCEL_COPYAREA | + FBINFO_HWACCEL_FILLRECT | FBINFO_HWACCEL_IMAGEBLIT; + default_var.accel_flags = FB_ACCELF_TEXT; + } else { + viafbinfo->flags |= FBINFO_HWACCEL_DISABLED; + default_var.accel_flags = 0; } if (viafb_second_size && (viafb_second_size < 8)) { @@ -2186,27 +1924,14 @@ static int __devinit via_pci_probe(void) viafb_second_size * 1024 * 1024; } - viafb_FB_MM = viaparinfo->fbmem_virt; - tmpm = viafb_mode; - tmpc = strsep(&tmpm, "x"); - strict_strtoul(tmpc, 0, &default_xres); - strict_strtoul(tmpm, 0, &default_yres); - + parse_mode(viafb_mode, &default_xres, &default_yres); vmode_index = viafb_get_mode_index(default_xres, default_yres); DEBUG_MSG(KERN_INFO "0->index=%d\n", vmode_index); if (viafb_SAMM_ON == 1) { - if (strcmp(viafb_mode, viafb_mode1)) { - tmpm_sec = viafb_mode1; - tmpc_sec = strsep(&tmpm_sec, "x"); - strict_strtoul(tmpc_sec, 0, - (unsigned long *)&viafb_second_xres); - strict_strtoul(tmpm_sec, 0, - (unsigned long *)&viafb_second_yres); - } else { - viafb_second_xres = default_xres; - viafb_second_yres = default_yres; - } + parse_mode(viafb_mode1, &viafb_second_xres, + &viafb_second_yres); + if (0 == viafb_second_virtual_xres) { switch (viafb_second_xres) { case 1400: @@ -2256,18 +1981,9 @@ static int __devinit via_pci_probe(void) default_var.lower_margin = 4; default_var.hsync_len = default_var.left_margin; default_var.vsync_len = 4; - default_var.accel_flags = 0; - - if (viafb_accel) { - viafbinfo->flags |= - (FBINFO_HWACCEL_COPYAREA | FBINFO_HWACCEL_FILLRECT | - FBINFO_HWACCEL_IMAGEBLIT); - default_var.accel_flags |= FB_ACCELF_TEXT; - } else - viafbinfo->flags |= FBINFO_HWACCEL_DISABLED; if (viafb_dual_fb) { - viafbinfo1 = framebuffer_alloc(viafb_par_length, NULL); + viafbinfo1 = framebuffer_alloc(viafb_par_length, &pdev->dev); if (!viafbinfo1) { printk(KERN_ERR "allocate the second framebuffer struct error\n"); @@ -2276,11 +1992,10 @@ static int __devinit via_pci_probe(void) } viaparinfo1 = viafbinfo1->par; memcpy(viaparinfo1, viaparinfo, viafb_par_length); + viaparinfo1->vram_addr = viafb_second_offset; viaparinfo1->memsize = viaparinfo->memsize - viafb_second_offset; viaparinfo->memsize = viafb_second_offset; - viaparinfo1->fbmem_virt = viaparinfo->fbmem_virt + - viafb_second_offset; viaparinfo1->fbmem = viaparinfo->fbmem + viafb_second_offset; viaparinfo1->fbmem_used = viaparinfo->fbmem_used; @@ -2288,20 +2003,13 @@ static int __devinit via_pci_probe(void) viaparinfo1->fbmem_used; viaparinfo->fbmem_free = viaparinfo->memsize; viaparinfo->fbmem_used = 0; - if (viafb_accel) { - viaparinfo1->cursor_start = - viaparinfo->cursor_start - viafb_second_offset; - viaparinfo1->VQ_start = viaparinfo->VQ_start - - viafb_second_offset; - viaparinfo1->VQ_end = viaparinfo->VQ_end - - viafb_second_offset; - } + viaparinfo->iga_path = IGA1; + viaparinfo1->iga_path = IGA2; memcpy(viafbinfo1, viafbinfo, sizeof(struct fb_info)); + viafbinfo1->par = viaparinfo1; viafbinfo1->screen_base = viafbinfo->screen_base + viafb_second_offset; - viafbinfo1->fix.smem_start = viaparinfo1->fbmem; - viafbinfo1->fix.smem_len = viaparinfo1->fbmem_free; default_var.xres = viafb_second_xres; default_var.yres = viafb_second_yres; @@ -2323,15 +2031,17 @@ static int __devinit via_pci_probe(void) viafb_setup_fixinfo(&viafbinfo1->fix, viaparinfo1); viafb_check_var(&default_var, viafbinfo1); viafbinfo1->var = default_var; - viafb_update_viafb_par(viafbinfo); - viafb_update_fix(&viafbinfo1->fix, viafbinfo1); + viafb_update_fix(viafbinfo1); + viaparinfo1->depth = fb_get_color_depth(&viafbinfo1->var, + &viafbinfo1->fix); } viafb_setup_fixinfo(&viafbinfo->fix, viaparinfo); viafb_check_var(&default_var, viafbinfo); viafbinfo->var = default_var; - viafb_update_viafb_par(viafbinfo); - viafb_update_fix(&viafbinfo->fix, viafbinfo); + viafb_update_fix(viafbinfo); + viaparinfo->depth = fb_get_color_depth(&viafbinfo->var, + &viafbinfo->fix); default_var.activate = FB_ACTIVATE_NOW; fb_alloc_cmap(&viafbinfo->cmap, 256, 0); @@ -2353,20 +2063,20 @@ static int __devinit via_pci_probe(void) viafbinfo->node, viafbinfo->fix.id, default_var.xres, default_var.yres, default_var.bits_per_pixel); - viafb_init_proc(&viaparinfo->proc_entry); + viafb_init_proc(&viaparinfo->shared->proc_entry); viafb_init_dac(IGA2); return 0; } -static void __devexit via_pci_remove(void) +static void __devexit via_pci_remove(struct pci_dev *pdev) { DEBUG_MSG(KERN_INFO "via_pci_remove!\n"); fb_dealloc_cmap(&viafbinfo->cmap); unregister_framebuffer(viafbinfo); if (viafb_dual_fb) unregister_framebuffer(viafbinfo1); - iounmap((void *)viaparinfo->fbmem_virt); - iounmap(viaparinfo->io_virt); + iounmap((void *)viafbinfo->screen_base); + iounmap(viaparinfo->shared->engine_mmio); viafb_delete_i2c_buss(viaparinfo); @@ -2374,7 +2084,7 @@ static void __devexit via_pci_remove(void) if (viafb_dual_fb) framebuffer_release(viafbinfo1); - viafb_remove_proc(viaparinfo->proc_entry); + viafb_remove_proc(viaparinfo->shared->proc_entry); } #ifndef MODULE @@ -2441,8 +2151,6 @@ static int __init viafb_setup(char *options) else if (!strncmp(this_opt, "viafb_lcd_mode=", 15)) strict_strtoul(this_opt + 15, 0, (unsigned long *)&viafb_lcd_mode); - else if (!strncmp(this_opt, "viafb_video_dev=", 16)) - viafb_video_dev = kstrdup(this_opt + 16, GFP_KERNEL); else if (!strncmp(this_opt, "viafb_lcd_port=", 15)) viafb_lcd_port = kstrdup(this_opt + 15, GFP_KERNEL); else if (!strncmp(this_opt, "viafb_dvi_port=", 15)) @@ -2452,6 +2160,40 @@ static int __init viafb_setup(char *options) } #endif +static struct pci_device_id viafb_pci_table[] __devinitdata = { + { PCI_DEVICE(PCI_VENDOR_ID_VIA, UNICHROME_CLE266_DID), + .driver_data = UNICHROME_CLE266 }, + { PCI_DEVICE(PCI_VENDOR_ID_VIA, UNICHROME_PM800_DID), + .driver_data = UNICHROME_PM800 }, + { PCI_DEVICE(PCI_VENDOR_ID_VIA, UNICHROME_K400_DID), + .driver_data = UNICHROME_K400 }, + { PCI_DEVICE(PCI_VENDOR_ID_VIA, UNICHROME_K800_DID), + .driver_data = UNICHROME_K800 }, + { PCI_DEVICE(PCI_VENDOR_ID_VIA, UNICHROME_P4M890_DID), + .driver_data = UNICHROME_CN700 }, + { PCI_DEVICE(PCI_VENDOR_ID_VIA, UNICHROME_K8M890_DID), + .driver_data = UNICHROME_K8M890 }, + { PCI_DEVICE(PCI_VENDOR_ID_VIA, UNICHROME_CX700_DID), + .driver_data = UNICHROME_CX700 }, + { PCI_DEVICE(PCI_VENDOR_ID_VIA, UNICHROME_P4M900_DID), + .driver_data = UNICHROME_P4M900 }, + { PCI_DEVICE(PCI_VENDOR_ID_VIA, UNICHROME_CN750_DID), + .driver_data = UNICHROME_CN750 }, + { PCI_DEVICE(PCI_VENDOR_ID_VIA, UNICHROME_VX800_DID), + .driver_data = UNICHROME_VX800 }, + { PCI_DEVICE(PCI_VENDOR_ID_VIA, UNICHROME_VX855_DID), + .driver_data = UNICHROME_VX855 }, + { } +}; +MODULE_DEVICE_TABLE(pci, viafb_pci_table); + +static struct pci_driver viafb_driver = { + .name = "viafb", + .id_table = viafb_pci_table, + .probe = via_pci_probe, + .remove = __devexit_p(via_pci_remove), +}; + static int __init viafb_init(void) { #ifndef MODULE @@ -2463,13 +2205,13 @@ static int __init viafb_init(void) printk(KERN_INFO "VIA Graphics Intergration Chipset framebuffer %d.%d initializing\n", VERSION_MAJOR, VERSION_MINOR); - return via_pci_probe(); + return pci_register_driver(&viafb_driver); } static void __exit viafb_exit(void) { DEBUG_MSG(KERN_INFO "viafb_exit!\n"); - via_pci_remove(); + pci_unregister_driver(&viafb_driver); } static struct fb_ops viafb_ops = { @@ -2494,82 +2236,79 @@ module_init(viafb_init); module_exit(viafb_exit); #ifdef MODULE -module_param(viafb_memsize, int, 0); +module_param(viafb_memsize, int, S_IRUSR); -module_param(viafb_mode, charp, 0); +module_param(viafb_mode, charp, S_IRUSR); MODULE_PARM_DESC(viafb_mode, "Set resolution (default=640x480)"); -module_param(viafb_mode1, charp, 0); +module_param(viafb_mode1, charp, S_IRUSR); MODULE_PARM_DESC(viafb_mode1, "Set resolution (default=640x480)"); -module_param(viafb_bpp, int, 0); +module_param(viafb_bpp, int, S_IRUSR); MODULE_PARM_DESC(viafb_bpp, "Set color depth (default=32bpp)"); -module_param(viafb_bpp1, int, 0); +module_param(viafb_bpp1, int, S_IRUSR); MODULE_PARM_DESC(viafb_bpp1, "Set color depth (default=32bpp)"); -module_param(viafb_refresh, int, 0); +module_param(viafb_refresh, int, S_IRUSR); MODULE_PARM_DESC(viafb_refresh, "Set CRT viafb_refresh rate (default = 60)"); -module_param(viafb_refresh1, int, 0); +module_param(viafb_refresh1, int, S_IRUSR); MODULE_PARM_DESC(viafb_refresh1, "Set CRT refresh rate (default = 60)"); -module_param(viafb_lcd_panel_id, int, 0); +module_param(viafb_lcd_panel_id, int, S_IRUSR); MODULE_PARM_DESC(viafb_lcd_panel_id, "Set Flat Panel type(Default=1024x768)"); -module_param(viafb_lcd_dsp_method, int, 0); +module_param(viafb_lcd_dsp_method, int, S_IRUSR); MODULE_PARM_DESC(viafb_lcd_dsp_method, "Set Flat Panel display scaling method.(Default=Expandsion)"); -module_param(viafb_SAMM_ON, int, 0); +module_param(viafb_SAMM_ON, int, S_IRUSR); MODULE_PARM_DESC(viafb_SAMM_ON, "Turn on/off flag of SAMM(Default=OFF)"); -module_param(viafb_accel, int, 0); +module_param(viafb_accel, int, S_IRUSR); MODULE_PARM_DESC(viafb_accel, - "Set 2D Hardware Acceleration.(Default = OFF)"); + "Set 2D Hardware Acceleration: 0 = OFF, 1 = ON (default)"); -module_param(viafb_active_dev, charp, 0); +module_param(viafb_active_dev, charp, S_IRUSR); MODULE_PARM_DESC(viafb_active_dev, "Specify active devices."); -module_param(viafb_display_hardware_layout, int, 0); +module_param(viafb_display_hardware_layout, int, S_IRUSR); MODULE_PARM_DESC(viafb_display_hardware_layout, "Display Hardware Layout (LCD Only, DVI Only...,etc)"); -module_param(viafb_second_size, int, 0); +module_param(viafb_second_size, int, S_IRUSR); MODULE_PARM_DESC(viafb_second_size, "Set secondary device memory size"); -module_param(viafb_dual_fb, int, 0); +module_param(viafb_dual_fb, int, S_IRUSR); MODULE_PARM_DESC(viafb_dual_fb, "Turn on/off flag of dual framebuffer devices.(Default = OFF)"); -module_param(viafb_platform_epia_dvi, int, 0); +module_param(viafb_platform_epia_dvi, int, S_IRUSR); MODULE_PARM_DESC(viafb_platform_epia_dvi, "Turn on/off flag of DVI devices on EPIA board.(Default = OFF)"); -module_param(viafb_device_lcd_dualedge, int, 0); +module_param(viafb_device_lcd_dualedge, int, S_IRUSR); MODULE_PARM_DESC(viafb_device_lcd_dualedge, "Turn on/off flag of dual edge panel.(Default = OFF)"); -module_param(viafb_bus_width, int, 0); +module_param(viafb_bus_width, int, S_IRUSR); MODULE_PARM_DESC(viafb_bus_width, "Set bus width of panel.(Default = 12)"); -module_param(viafb_lcd_mode, int, 0); +module_param(viafb_lcd_mode, int, S_IRUSR); MODULE_PARM_DESC(viafb_lcd_mode, "Set Flat Panel mode(Default=OPENLDI)"); -module_param(viafb_video_dev, charp, 0); -MODULE_PARM_DESC(viafb_video_dev, "Specify video devices."); - -module_param(viafb_lcd_port, charp, 0); +module_param(viafb_lcd_port, charp, S_IRUSR); MODULE_PARM_DESC(viafb_lcd_port, "Specify LCD output port."); -module_param(viafb_dvi_port, charp, 0); +module_param(viafb_dvi_port, charp, S_IRUSR); MODULE_PARM_DESC(viafb_dvi_port, "Specify DVI output port."); MODULE_LICENSE("GPL"); diff --git a/drivers/video/via/viafbdev.h b/drivers/video/via/viafbdev.h index 227b000feb38..0c94d2441922 100644 --- a/drivers/video/via/viafbdev.h +++ b/drivers/video/via/viafbdev.h @@ -37,51 +37,50 @@ #define VERSION_OS 0 /* 0: for 32 bits OS, 1: for 64 bits OS */ #define VERSION_MINOR 4 +struct viafb_shared { + struct proc_dir_entry *proc_entry; /*viafb proc entry */ + + /* I2C stuff */ + struct via_i2c_stuff i2c_stuff; + + /* All the information will be needed to set engine */ + struct tmds_setting_information tmds_setting_info; + struct crt_setting_information crt_setting_info; + struct lvds_setting_information lvds_setting_info; + struct lvds_setting_information lvds_setting_info2; + struct chip_information chip_info; + + /* hardware acceleration stuff */ + void __iomem *engine_mmio; + u32 cursor_vram_addr; + u32 vq_vram_addr; /* virtual queue address in video ram */ + int (*hw_bitblt)(void __iomem *engine, u8 op, u32 width, u32 height, + u8 dst_bpp, u32 dst_addr, u32 dst_pitch, u32 dst_x, u32 dst_y, + u32 *src_mem, u32 src_addr, u32 src_pitch, u32 src_x, u32 src_y, + u32 fg_color, u32 bg_color, u8 fill_rop); +}; + struct viafb_par { - int bpp; - int hres; - int vres; - int linelength; - u32 xoffset; - u32 yoffset; - - void __iomem *fbmem_virt; /*framebuffer virtual memory address */ - void __iomem *io_virt; /*iospace virtual memory address */ + u8 depth; + u32 vram_addr; + unsigned int fbmem; /*framebuffer physical memory address */ unsigned int memsize; /*size of fbmem */ - unsigned int io; /*io space address */ - unsigned long mmio_base; /*mmio base address */ - unsigned long mmio_len; /*mmio base length */ u32 fbmem_free; /* Free FB memory */ u32 fbmem_used; /* Use FB memory size */ - u32 cursor_start; /* Cursor Start Address */ - u32 VQ_start; /* Virtual Queue Start Address */ - u32 VQ_end; /* Virtual Queue End Address */ u32 iga_path; - struct proc_dir_entry *proc_entry; /*viafb proc entry */ - u8 duoview; /*Is working in duoview mode? */ - /* I2C stuff */ - struct via_i2c_stuff i2c_stuff; + struct viafb_shared *shared; /* All the information will be needed to set engine */ + /* depreciated, use the ones in shared directly */ struct tmds_setting_information *tmds_setting_info; struct crt_setting_information *crt_setting_info; struct lvds_setting_information *lvds_setting_info; struct lvds_setting_information *lvds_setting_info2; struct chip_information *chip_info; - - /* some information related to video playing */ - int video_on_crt; - int video_on_dvi; - int video_on_lcd; - -}; -struct viafb_modeinfo { - u32 xres; - u32 yres; - int mode_index; }; + extern unsigned int viafb_second_virtual_yres; extern unsigned int viafb_second_virtual_xres; extern unsigned int viafb_second_offset; @@ -91,14 +90,12 @@ extern int viafb_dual_fb; extern int viafb_LCD2_ON; extern int viafb_LCD_ON; extern int viafb_DVI_ON; -extern int viafb_accel; extern int viafb_hotplug; extern int viafb_memsize; extern int strict_strtoul(const char *cp, unsigned int base, unsigned long *res); -void viafb_memory_pitch_patch(struct fb_info *info); void viafb_fill_var_timing_info(struct fb_var_screeninfo *var, int refresh, int mode_index); int viafb_get_mode_index(int hres, int vres); diff --git a/drivers/video/via/viamode.c b/drivers/video/via/viamode.c index 6dcf583a837d..b74f8a67923c 100644 --- a/drivers/video/via/viamode.c +++ b/drivers/video/via/viamode.c @@ -100,12 +100,8 @@ struct io_reg CN400_ModeXregs[] = { {VIASR, SR10, 0xFF, 0x01}, {VIACR, CR0F, 0xFF, 0x00}, /* Cursor Localtion Low */ {VIACR, CR32, 0xFF, 0x00}, {VIACR, CR33, 0xFF, 0x00}, -{VIACR, CR34, 0xFF, 0x00}, {VIACR, CR35, 0xFF, 0x00}, {VIACR, CR36, 0x08, 0x00}, -{VIACR, CR62, 0xFF, 0x00}, /* Secondary Display Starting Address */ -{VIACR, CR63, 0xFF, 0x00}, /* Secondary Display Starting Address */ -{VIACR, CR64, 0xFF, 0x00}, /* Secondary Display Starting Address */ {VIACR, CR69, 0xFF, 0x00}, {VIACR, CR6A, 0xFF, 0x40}, {VIACR, CR6B, 0xFF, 0x00}, @@ -159,16 +155,12 @@ struct io_reg CN700_ModeXregs[] = { {VIASR, SR10, 0xFF, 0x01}, {VIASR, CR30, 0xFF, 0x04}, {VIACR, CR32, 0xFF, 0x00}, {VIACR, CR33, 0x7F, 0x00}, -{VIACR, CR34, 0xFF, 0x00}, {VIACR, CR35, 0xFF, 0x00}, {VIACR, CR36, 0xFF, 0x31}, {VIACR, CR41, 0xFF, 0x80}, {VIACR, CR42, 0xFF, 0x00}, {VIACR, CR55, 0x80, 0x00}, {VIACR, CR5D, 0x80, 0x00}, /*Horizontal Retrace Start bit[11] should be 0*/ -{VIACR, CR62, 0xFF, 0x00}, /* Secondary Display Starting Address */ -{VIACR, CR63, 0xFF, 0x00}, /* Secondary Display Starting Address */ -{VIACR, CR64, 0xFF, 0x00}, /* Secondary Display Starting Address */ {VIACR, CR68, 0xFF, 0x67}, /* Default FIFO For IGA2 */ {VIACR, CR69, 0xFF, 0x00}, {VIACR, CR6A, 0xFD, 0x40}, @@ -233,9 +225,6 @@ struct io_reg KM400_ModeXregs[] = { {VIACR, CR55, 0x80, 0x00}, {VIACR, CR5D, 0x80, 0x00}, {VIACR, CR36, 0xFF, 0x01}, /* Power Mangement 3 */ - {VIACR, CR62, 0xFF, 0x00}, /* Secondary Display Starting Address */ - {VIACR, CR63, 0xFF, 0x00}, /* Secondary Display Starting Address */ - {VIACR, CR64, 0xFF, 0x00}, /* Secondary Display Starting Address */ {VIACR, CR68, 0xFF, 0x67}, /* Default FIFO For IGA2 */ {VIACR, CR6A, 0x20, 0x20}, /* Extended FIFO On */ {VIACR, CR7A, 0xFF, 0x01}, /* LCD Scaling Parameter 1 */ @@ -285,14 +274,9 @@ struct io_reg CX700_ModeXregs[] = { {VIASR, SR10, 0xFF, 0x01}, {VIACR, CR0F, 0xFF, 0x00}, /* Cursor Localtion Low */ {VIACR, CR32, 0xFF, 0x00}, {VIACR, CR33, 0xFF, 0x00}, -{VIACR, CR34, 0xFF, 0x00}, {VIACR, CR35, 0xFF, 0x00}, {VIACR, CR36, 0x08, 0x00}, {VIACR, CR47, 0xC8, 0x00}, /* Clear VCK Plus. */ -{VIACR, CR62, 0xFF, 0x00}, /* Secondary Display Starting Address */ -{VIACR, CR63, 0xFF, 0x00}, /* Secondary Display Starting Address */ -{VIACR, CR64, 0xFF, 0x00}, /* Secondary Display Starting Address */ -{VIACR, CRA3, 0xFF, 0x00}, /* Secondary Display Starting Address */ {VIACR, CR69, 0xFF, 0x00}, {VIACR, CR6A, 0xFF, 0x40}, {VIACR, CR6B, 0xFF, 0x00}, @@ -325,69 +309,61 @@ struct io_reg CX700_ModeXregs[] = { {VIASR, SR10, 0xFF, 0x01}, {VIACR, CR96, 0xFF, 0x00}, {VIACR, CR97, 0xFF, 0x00}, {VIACR, CR99, 0xFF, 0x00}, -{VIACR, CR9B, 0xFF, 0x00}, -{VIACR, CRD2, 0xFF, 0xFF} /* TMDS/LVDS control register. */ +{VIACR, CR9B, 0xFF, 0x00} }; -/* For VT3353: Common Setting for Video Mode */ -struct io_reg VX800_ModeXregs[] = { {VIASR, SR10, 0xFF, 0x01}, +struct io_reg VX855_ModeXregs[] = { +{VIASR, SR10, 0xFF, 0x01}, {VIASR, SR15, 0x02, 0x02}, {VIASR, SR16, 0xBF, 0x08}, {VIASR, SR17, 0xFF, 0x1F}, {VIASR, SR18, 0xFF, 0x4E}, {VIASR, SR1A, 0xFB, 0x08}, {VIASR, SR1B, 0xFF, 0xF0}, -{VIASR, SR1E, 0xFF, 0x01}, -{VIASR, SR2A, 0xFF, 0x00}, +{VIASR, SR1E, 0x07, 0x01}, +{VIASR, SR2A, 0xF0, 0x00}, +{VIASR, SR58, 0xFF, 0x00}, +{VIASR, SR59, 0xFF, 0x00}, {VIASR, SR2D, 0xFF, 0xFF}, /* VCK and LCK PLL power on. */ +{VIACR, CR09, 0xFF, 0x00}, /* Initial CR09=0*/ +{VIACR, CR11, 0x8F, 0x00}, /* IGA1 initial Vertical end */ +{VIACR, CR17, 0x7F, 0x00}, /* IGA1 CRT Mode control init */ {VIACR, CR0A, 0xFF, 0x1E}, /* Cursor Start */ {VIACR, CR0B, 0xFF, 0x00}, /* Cursor End */ {VIACR, CR0E, 0xFF, 0x00}, /* Cursor Location High */ {VIACR, CR0F, 0xFF, 0x00}, /* Cursor Localtion Low */ {VIACR, CR32, 0xFF, 0x00}, -{VIACR, CR33, 0xFF, 0x00}, -{VIACR, CR34, 0xFF, 0x00}, +{VIACR, CR33, 0x7F, 0x00}, {VIACR, CR35, 0xFF, 0x00}, {VIACR, CR36, 0x08, 0x00}, -{VIACR, CR47, 0xC8, 0x00}, /* Clear VCK Plus. */ -{VIACR, CR62, 0xFF, 0x00}, /* Secondary Display Starting Address */ -{VIACR, CR63, 0xFF, 0x00}, /* Secondary Display Starting Address */ -{VIACR, CR64, 0xFF, 0x00}, /* Secondary Display Starting Address */ -{VIACR, CRA3, 0xFF, 0x00}, /* Secondary Display Starting Address */ {VIACR, CR69, 0xFF, 0x00}, -{VIACR, CR6A, 0xFF, 0x40}, +{VIACR, CR6A, 0xFD, 0x60}, {VIACR, CR6B, 0xFF, 0x00}, {VIACR, CR6C, 0xFF, 0x00}, -{VIACR, CR7A, 0xFF, 0x01}, /* LCD Scaling Parameter 1 */ -{VIACR, CR7B, 0xFF, 0x02}, /* LCD Scaling Parameter 2 */ -{VIACR, CR7C, 0xFF, 0x03}, /* LCD Scaling Parameter 3 */ -{VIACR, CR7D, 0xFF, 0x04}, /* LCD Scaling Parameter 4 */ -{VIACR, CR7E, 0xFF, 0x07}, /* LCD Scaling Parameter 5 */ -{VIACR, CR7F, 0xFF, 0x0A}, /* LCD Scaling Parameter 6 */ -{VIACR, CR80, 0xFF, 0x0D}, /* LCD Scaling Parameter 7 */ -{VIACR, CR81, 0xFF, 0x13}, /* LCD Scaling Parameter 8 */ -{VIACR, CR82, 0xFF, 0x16}, /* LCD Scaling Parameter 9 */ -{VIACR, CR83, 0xFF, 0x19}, /* LCD Scaling Parameter 10 */ -{VIACR, CR84, 0xFF, 0x1C}, /* LCD Scaling Parameter 11 */ -{VIACR, CR85, 0xFF, 0x1D}, /* LCD Scaling Parameter 12 */ -{VIACR, CR86, 0xFF, 0x1E}, /* LCD Scaling Parameter 13 */ -{VIACR, CR87, 0xFF, 0x1F}, /* LCD Scaling Parameter 14 */ -{VIACR, CR88, 0xFF, 0x40}, /* LCD Panel Type */ -{VIACR, CR89, 0xFF, 0x00}, /* LCD Timing Control 0 */ -{VIACR, CR8A, 0xFF, 0x88}, /* LCD Timing Control 1 */ -{VIACR, CRD4, 0xFF, 0x81}, /* Second power sequence control */ -{VIACR, CR8B, 0xFF, 0x5D}, /* LCD Power Sequence Control 0 */ -{VIACR, CR8C, 0xFF, 0x2B}, /* LCD Power Sequence Control 1 */ -{VIACR, CR8D, 0xFF, 0x6F}, /* LCD Power Sequence Control 2 */ -{VIACR, CR8E, 0xFF, 0x2B}, /* LCD Power Sequence Control 3 */ -{VIACR, CR8F, 0xFF, 0x01}, /* LCD Power Sequence Control 4 */ -{VIACR, CR90, 0xFF, 0x01}, /* LCD Power Sequence Control 5 */ -{VIACR, CR91, 0xFF, 0x80}, /* 24/12 bit LVDS Data off */ +{VIACR, CR7A, 0xFF, 0x01}, /* LCD Scaling Parameter 1 */ +{VIACR, CR7B, 0xFF, 0x02}, /* LCD Scaling Parameter 2 */ +{VIACR, CR7C, 0xFF, 0x03}, /* LCD Scaling Parameter 3 */ +{VIACR, CR7D, 0xFF, 0x04}, /* LCD Scaling Parameter 4 */ +{VIACR, CR7E, 0xFF, 0x07}, /* LCD Scaling Parameter 5 */ +{VIACR, CR7F, 0xFF, 0x0A}, /* LCD Scaling Parameter 6 */ +{VIACR, CR80, 0xFF, 0x0D}, /* LCD Scaling Parameter 7 */ +{VIACR, CR81, 0xFF, 0x13}, /* LCD Scaling Parameter 8 */ +{VIACR, CR82, 0xFF, 0x16}, /* LCD Scaling Parameter 9 */ +{VIACR, CR83, 0xFF, 0x19}, /* LCD Scaling Parameter 10 */ +{VIACR, CR84, 0xFF, 0x1C}, /* LCD Scaling Parameter 11 */ +{VIACR, CR85, 0xFF, 0x1D}, /* LCD Scaling Parameter 12 */ +{VIACR, CR86, 0xFF, 0x1E}, /* LCD Scaling Parameter 13 */ +{VIACR, CR87, 0xFF, 0x1F}, /* LCD Scaling Parameter 14 */ +{VIACR, CR88, 0xFF, 0x40}, /* LCD Panel Type */ +{VIACR, CR89, 0xFF, 0x00}, /* LCD Timing Control 0 */ +{VIACR, CR8A, 0xFF, 0x88}, /* LCD Timing Control 1 */ +{VIACR, CRD4, 0xFF, 0x81}, /* Second power sequence control */ +{VIACR, CR91, 0xFF, 0x80}, /* 24/12 bit LVDS Data off */ {VIACR, CR96, 0xFF, 0x00}, {VIACR, CR97, 0xFF, 0x00}, {VIACR, CR99, 0xFF, 0x00}, {VIACR, CR9B, 0xFF, 0x00}, -{VIACR, CRD2, 0xFF, 0xFF} /* TMDS/LVDS control register. */ +{VIACR, CRD2, 0xFF, 0xFF} /* TMDS/LVDS control register. */ }; /* Video Mode Table */ @@ -401,7 +377,6 @@ struct io_reg CLE266_ModeXregs[] = { {VIASR, SR1E, 0xF0, 0x00}, {VIASR, SR1A, 0xFB, 0x08}, {VIACR, CR32, 0xFF, 0x00}, -{VIACR, CR34, 0xFF, 0x00}, {VIACR, CR35, 0xFF, 0x00}, {VIACR, CR36, 0x08, 0x00}, {VIACR, CR6A, 0xFF, 0x80}, @@ -1084,3 +1059,14 @@ struct VideoModeTable CEA_HDMI_Modes[] = { {VIA_RES_1280X720, CEAM1280x720, ARRAY_SIZE(CEAM1280x720)}, {VIA_RES_1920X1080, CEAM1920x1080, ARRAY_SIZE(CEAM1920x1080)} }; + +int NUM_TOTAL_RES_MAP_REFRESH = ARRAY_SIZE(res_map_refresh_tbl); +int NUM_TOTAL_CEA_MODES = ARRAY_SIZE(CEA_HDMI_Modes); +int NUM_TOTAL_CN400_ModeXregs = ARRAY_SIZE(CN400_ModeXregs); +int NUM_TOTAL_CN700_ModeXregs = ARRAY_SIZE(CN700_ModeXregs); +int NUM_TOTAL_KM400_ModeXregs = ARRAY_SIZE(KM400_ModeXregs); +int NUM_TOTAL_CX700_ModeXregs = ARRAY_SIZE(CX700_ModeXregs); +int NUM_TOTAL_VX855_ModeXregs = ARRAY_SIZE(VX855_ModeXregs); +int NUM_TOTAL_CLE266_ModeXregs = ARRAY_SIZE(CLE266_ModeXregs); +int NUM_TOTAL_PATCH_MODE = ARRAY_SIZE(res_patch_table); +int NUM_TOTAL_MODETABLE = ARRAY_SIZE(CLE266Modes); diff --git a/drivers/video/via/viamode.h b/drivers/video/via/viamode.h index 1a5de50a23a2..a9d6554fabdf 100644 --- a/drivers/video/via/viamode.h +++ b/drivers/video/via/viamode.h @@ -50,128 +50,35 @@ struct res_map_refresh { int vmode_refresh; }; -#define NUM_TOTAL_RES_MAP_REFRESH ARRAY_SIZE(res_map_refresh_tbl) -#define NUM_TOTAL_CEA_MODES ARRAY_SIZE(CEA_HDMI_Modes) -#define NUM_TOTAL_CN400_ModeXregs ARRAY_SIZE(CN400_ModeXregs) -#define NUM_TOTAL_CN700_ModeXregs ARRAY_SIZE(CN700_ModeXregs) -#define NUM_TOTAL_KM400_ModeXregs ARRAY_SIZE(KM400_ModeXregs) -#define NUM_TOTAL_CX700_ModeXregs ARRAY_SIZE(CX700_ModeXregs) -#define NUM_TOTAL_VX800_ModeXregs ARRAY_SIZE(VX800_ModeXregs) -#define NUM_TOTAL_CLE266_ModeXregs ARRAY_SIZE(CLE266_ModeXregs) -#define NUM_TOTAL_PATCH_MODE ARRAY_SIZE(res_patch_table) -#define NUM_TOTAL_MODETABLE ARRAY_SIZE(CLE266Modes) +extern int NUM_TOTAL_RES_MAP_REFRESH; +extern int NUM_TOTAL_CEA_MODES; +extern int NUM_TOTAL_CN400_ModeXregs; +extern int NUM_TOTAL_CN700_ModeXregs; +extern int NUM_TOTAL_KM400_ModeXregs; +extern int NUM_TOTAL_CX700_ModeXregs; +extern int NUM_TOTAL_VX855_ModeXregs; +extern int NUM_TOTAL_CLE266_ModeXregs; +extern int NUM_TOTAL_PATCH_MODE; +extern int NUM_TOTAL_MODETABLE; /********************/ /* Mode Table */ /********************/ -/* 480x640 */ -extern struct crt_mode_table CRTM480x640[1]; -/* 640x480*/ -extern struct crt_mode_table CRTM640x480[5]; -/*720x480 (GTF)*/ -extern struct crt_mode_table CRTM720x480[1]; -/*720x576 (GTF)*/ -extern struct crt_mode_table CRTM720x576[1]; -/* 800x480 (CVT) */ -extern struct crt_mode_table CRTM800x480[1]; -/* 800x600*/ -extern struct crt_mode_table CRTM800x600[5]; -/* 848x480 (CVT) */ -extern struct crt_mode_table CRTM848x480[1]; -/*856x480 (GTF) convert to 852x480*/ -extern struct crt_mode_table CRTM852x480[1]; -/*1024x512 (GTF)*/ -extern struct crt_mode_table CRTM1024x512[1]; -/* 1024x600*/ -extern struct crt_mode_table CRTM1024x600[1]; -/* 1024x768*/ -extern struct crt_mode_table CRTM1024x768[4]; -/* 1152x864*/ -extern struct crt_mode_table CRTM1152x864[1]; -/* 1280x720 (HDMI 720P)*/ -extern struct crt_mode_table CRTM1280x720[2]; -/*1280x768 (GTF)*/ -extern struct crt_mode_table CRTM1280x768[2]; -/* 1280x800 (CVT) */ -extern struct crt_mode_table CRTM1280x800[1]; -/*1280x960*/ -extern struct crt_mode_table CRTM1280x960[1]; -/* 1280x1024*/ -extern struct crt_mode_table CRTM1280x1024[3]; -/* 1368x768 (GTF) */ -extern struct crt_mode_table CRTM1368x768[1]; -/*1440x1050 (GTF)*/ -extern struct crt_mode_table CRTM1440x1050[1]; -/* 1600x1200*/ -extern struct crt_mode_table CRTM1600x1200[2]; -/* 1680x1050 (CVT) */ -extern struct crt_mode_table CRTM1680x1050[2]; -/* 1680x1050 (CVT Reduce Blanking) */ -extern struct crt_mode_table CRTM1680x1050_RB[1]; -/* 1920x1080 (CVT)*/ -extern struct crt_mode_table CRTM1920x1080[1]; -/* 1920x1080 (CVT with Reduce Blanking) */ -extern struct crt_mode_table CRTM1920x1080_RB[1]; -/* 1920x1440*/ -extern struct crt_mode_table CRTM1920x1440[2]; -/* 1400x1050 (CVT) */ -extern struct crt_mode_table CRTM1400x1050[2]; -/* 1400x1050 (CVT Reduce Blanking) */ -extern struct crt_mode_table CRTM1400x1050_RB[1]; -/* 960x600 (CVT) */ -extern struct crt_mode_table CRTM960x600[1]; -/* 1000x600 (GTF) */ -extern struct crt_mode_table CRTM1000x600[1]; -/* 1024x576 (GTF) */ -extern struct crt_mode_table CRTM1024x576[1]; -/* 1088x612 (CVT) */ -extern struct crt_mode_table CRTM1088x612[1]; -/* 1152x720 (CVT) */ -extern struct crt_mode_table CRTM1152x720[1]; -/* 1200x720 (GTF) */ -extern struct crt_mode_table CRTM1200x720[1]; -/* 1280x600 (GTF) */ -extern struct crt_mode_table CRTM1280x600[1]; -/* 1360x768 (CVT) */ -extern struct crt_mode_table CRTM1360x768[1]; -/* 1360x768 (CVT Reduce Blanking) */ -extern struct crt_mode_table CRTM1360x768_RB[1]; -/* 1366x768 (GTF) */ -extern struct crt_mode_table CRTM1366x768[2]; -/* 1440x900 (CVT) */ -extern struct crt_mode_table CRTM1440x900[2]; -/* 1440x900 (CVT Reduce Blanking) */ -extern struct crt_mode_table CRTM1440x900_RB[1]; -/* 1600x900 (CVT) */ -extern struct crt_mode_table CRTM1600x900[1]; -/* 1600x900 (CVT Reduce Blanking) */ -extern struct crt_mode_table CRTM1600x900_RB[1]; -/* 1600x1024 (GTF) */ -extern struct crt_mode_table CRTM1600x1024[1]; -/* 1792x1344 (DMT) */ -extern struct crt_mode_table CRTM1792x1344[1]; -/* 1856x1392 (DMT) */ -extern struct crt_mode_table CRTM1856x1392[1]; -/* 1920x1200 (CVT) */ -extern struct crt_mode_table CRTM1920x1200[1]; -/* 1920x1200 (CVT with Reduce Blanking) */ -extern struct crt_mode_table CRTM1920x1200_RB[1]; -/* 2048x1536 (CVT) */ -extern struct crt_mode_table CRTM2048x1536[1]; -extern struct VideoModeTable CLE266Modes[47]; -extern struct crt_mode_table CEAM1280x720[1]; -extern struct crt_mode_table CEAM1920x1080[1]; -extern struct VideoModeTable CEA_HDMI_Modes[2]; +extern struct VideoModeTable CLE266Modes[]; +extern struct crt_mode_table CEAM1280x720[]; +extern struct crt_mode_table CEAM1920x1080[]; +extern struct VideoModeTable CEA_HDMI_Modes[]; -extern struct res_map_refresh res_map_refresh_tbl[61]; -extern struct io_reg CN400_ModeXregs[52]; -extern struct io_reg CN700_ModeXregs[66]; -extern struct io_reg KM400_ModeXregs[55]; -extern struct io_reg CX700_ModeXregs[58]; -extern struct io_reg VX800_ModeXregs[58]; -extern struct io_reg CLE266_ModeXregs[32]; -extern struct io_reg PM1024x768[2]; -extern struct patch_table res_patch_table[1]; +extern struct res_map_refresh res_map_refresh_tbl[]; +extern struct io_reg CN400_ModeXregs[]; +extern struct io_reg CN700_ModeXregs[]; +extern struct io_reg KM400_ModeXregs[]; +extern struct io_reg CX700_ModeXregs[]; +extern struct io_reg VX800_ModeXregs[]; +extern struct io_reg VX855_ModeXregs[]; +extern struct io_reg CLE266_ModeXregs[]; +extern struct io_reg PM1024x768[]; +extern struct patch_table res_patch_table[]; extern struct VPITTable VPIT; #endif /* __VIAMODE_H__ */ diff --git a/drivers/video/via/vt1636.c b/drivers/video/via/vt1636.c index 322a9f993550..a6b37494e79a 100644 --- a/drivers/video/via/vt1636.c +++ b/drivers/video/via/vt1636.c @@ -27,7 +27,7 @@ u8 viafb_gpio_i2c_read_lvds(struct lvds_setting_information { u8 data; - viaparinfo->i2c_stuff.i2c_port = plvds_chip_info->i2c_port; + viaparinfo->shared->i2c_stuff.i2c_port = plvds_chip_info->i2c_port; viafb_i2c_readbyte(plvds_chip_info->lvds_chip_slave_addr, index, &data); return data; @@ -39,7 +39,7 @@ void viafb_gpio_i2c_write_mask_lvds(struct lvds_setting_information { int index, data; - viaparinfo->i2c_stuff.i2c_port = plvds_chip_info->i2c_port; + viaparinfo->shared->i2c_stuff.i2c_port = plvds_chip_info->i2c_port; index = io_data.Index; data = viafb_gpio_i2c_read_lvds(plvds_setting_info, plvds_chip_info, diff --git a/drivers/virtio/virtio_balloon.c b/drivers/virtio/virtio_balloon.c index 26b278264796..9dd588042880 100644 --- a/drivers/virtio/virtio_balloon.c +++ b/drivers/virtio/virtio_balloon.c @@ -84,7 +84,7 @@ static void tell_host(struct virtio_balloon *vb, struct virtqueue *vq) init_completion(&vb->acked); /* We should always be able to add one buffer to an empty queue. */ - if (vq->vq_ops->add_buf(vq, &sg, 1, 0, vb) != 0) + if (vq->vq_ops->add_buf(vq, &sg, 1, 0, vb) < 0) BUG(); vq->vq_ops->kick(vq); @@ -247,7 +247,7 @@ out: return err; } -static void virtballoon_remove(struct virtio_device *vdev) +static void __devexit virtballoon_remove(struct virtio_device *vdev) { struct virtio_balloon *vb = vdev->priv; diff --git a/drivers/virtio/virtio_pci.c b/drivers/virtio/virtio_pci.c index 248e00ec4dc1..28d9cf7cf72f 100644 --- a/drivers/virtio/virtio_pci.c +++ b/drivers/virtio/virtio_pci.c @@ -84,7 +84,7 @@ struct virtio_pci_vq_info struct list_head node; /* MSI-X vector (or none) */ - unsigned vector; + unsigned msix_vector; }; /* Qumranet donated their vendor ID for devices 0x1000 thru 0x10FF. */ @@ -280,25 +280,14 @@ static void vp_free_vectors(struct virtio_device *vdev) vp_dev->msix_entries = NULL; } -static int vp_request_vectors(struct virtio_device *vdev, int nvectors, - bool per_vq_vectors) +static int vp_request_msix_vectors(struct virtio_device *vdev, int nvectors, + bool per_vq_vectors) { struct virtio_pci_device *vp_dev = to_vp_device(vdev); const char *name = dev_name(&vp_dev->vdev.dev); unsigned i, v; int err = -ENOMEM; - if (!nvectors) { - /* Can't allocate MSI-X vectors, use regular interrupt */ - vp_dev->msix_vectors = 0; - err = request_irq(vp_dev->pci_dev->irq, vp_interrupt, - IRQF_SHARED, name, vp_dev); - if (err) - return err; - vp_dev->intx_enabled = 1; - return 0; - } - vp_dev->msix_entries = kmalloc(nvectors * sizeof *vp_dev->msix_entries, GFP_KERNEL); if (!vp_dev->msix_entries) @@ -311,6 +300,7 @@ static int vp_request_vectors(struct virtio_device *vdev, int nvectors, for (i = 0; i < nvectors; ++i) vp_dev->msix_entries[i].entry = i; + /* pci_enable_msix returns positive if we can't get this many. */ err = pci_enable_msix(vp_dev->pci_dev, vp_dev->msix_entries, nvectors); if (err > 0) err = -ENOSPC; @@ -356,10 +346,22 @@ error: return err; } -static struct virtqueue *vp_find_vq(struct virtio_device *vdev, unsigned index, - void (*callback)(struct virtqueue *vq), - const char *name, - u16 vector) +static int vp_request_intx(struct virtio_device *vdev) +{ + int err; + struct virtio_pci_device *vp_dev = to_vp_device(vdev); + + err = request_irq(vp_dev->pci_dev->irq, vp_interrupt, + IRQF_SHARED, dev_name(&vdev->dev), vp_dev); + if (!err) + vp_dev->intx_enabled = 1; + return err; +} + +static struct virtqueue *setup_vq(struct virtio_device *vdev, unsigned index, + void (*callback)(struct virtqueue *vq), + const char *name, + u16 msix_vec) { struct virtio_pci_device *vp_dev = to_vp_device(vdev); struct virtio_pci_vq_info *info; @@ -384,7 +386,7 @@ static struct virtqueue *vp_find_vq(struct virtio_device *vdev, unsigned index, info->queue_index = index; info->num = num; - info->vector = vector; + info->msix_vector = msix_vec; size = PAGE_ALIGN(vring_size(num, VIRTIO_PCI_VRING_ALIGN)); info->queue = alloc_pages_exact(size, GFP_KERNEL|__GFP_ZERO); @@ -408,10 +410,10 @@ static struct virtqueue *vp_find_vq(struct virtio_device *vdev, unsigned index, vq->priv = info; info->vq = vq; - if (vector != VIRTIO_MSI_NO_VECTOR) { - iowrite16(vector, vp_dev->ioaddr + VIRTIO_MSI_QUEUE_VECTOR); - vector = ioread16(vp_dev->ioaddr + VIRTIO_MSI_QUEUE_VECTOR); - if (vector == VIRTIO_MSI_NO_VECTOR) { + if (msix_vec != VIRTIO_MSI_NO_VECTOR) { + iowrite16(msix_vec, vp_dev->ioaddr + VIRTIO_MSI_QUEUE_VECTOR); + msix_vec = ioread16(vp_dev->ioaddr + VIRTIO_MSI_QUEUE_VECTOR); + if (msix_vec == VIRTIO_MSI_NO_VECTOR) { err = -EBUSY; goto out_assign; } @@ -472,7 +474,8 @@ static void vp_del_vqs(struct virtio_device *vdev) list_for_each_entry_safe(vq, n, &vdev->vqs, list) { info = vq->priv; if (vp_dev->per_vq_vectors) - free_irq(vp_dev->msix_entries[info->vector].vector, vq); + free_irq(vp_dev->msix_entries[info->msix_vector].vector, + vq); vp_del_vq(vq); } vp_dev->per_vq_vectors = false; @@ -484,42 +487,65 @@ static int vp_try_to_find_vqs(struct virtio_device *vdev, unsigned nvqs, struct virtqueue *vqs[], vq_callback_t *callbacks[], const char *names[], - int nvectors, + bool use_msix, bool per_vq_vectors) { struct virtio_pci_device *vp_dev = to_vp_device(vdev); - u16 vector; - int i, err, allocated_vectors; + u16 msix_vec; + int i, err, nvectors, allocated_vectors; - err = vp_request_vectors(vdev, nvectors, per_vq_vectors); - if (err) - goto error_request; + if (!use_msix) { + /* Old style: one normal interrupt for change and all vqs. */ + err = vp_request_intx(vdev); + if (err) + goto error_request; + } else { + if (per_vq_vectors) { + /* Best option: one for change interrupt, one per vq. */ + nvectors = 1; + for (i = 0; i < nvqs; ++i) + if (callbacks[i]) + ++nvectors; + } else { + /* Second best: one for change, shared for all vqs. */ + nvectors = 2; + } + + err = vp_request_msix_vectors(vdev, nvectors, per_vq_vectors); + if (err) + goto error_request; + } vp_dev->per_vq_vectors = per_vq_vectors; allocated_vectors = vp_dev->msix_used_vectors; for (i = 0; i < nvqs; ++i) { if (!callbacks[i] || !vp_dev->msix_enabled) - vector = VIRTIO_MSI_NO_VECTOR; + msix_vec = VIRTIO_MSI_NO_VECTOR; else if (vp_dev->per_vq_vectors) - vector = allocated_vectors++; + msix_vec = allocated_vectors++; else - vector = VP_MSIX_VQ_VECTOR; - vqs[i] = vp_find_vq(vdev, i, callbacks[i], names[i], vector); + msix_vec = VP_MSIX_VQ_VECTOR; + vqs[i] = setup_vq(vdev, i, callbacks[i], names[i], msix_vec); if (IS_ERR(vqs[i])) { err = PTR_ERR(vqs[i]); goto error_find; } + + if (!vp_dev->per_vq_vectors || msix_vec == VIRTIO_MSI_NO_VECTOR) + continue; + /* allocate per-vq irq if available and necessary */ - if (vp_dev->per_vq_vectors && vector != VIRTIO_MSI_NO_VECTOR) { - snprintf(vp_dev->msix_names[vector], sizeof *vp_dev->msix_names, - "%s-%s", dev_name(&vp_dev->vdev.dev), names[i]); - err = request_irq(vp_dev->msix_entries[vector].vector, - vring_interrupt, 0, - vp_dev->msix_names[vector], vqs[i]); - if (err) { - vp_del_vq(vqs[i]); - goto error_find; - } + snprintf(vp_dev->msix_names[msix_vec], + sizeof *vp_dev->msix_names, + "%s-%s", + dev_name(&vp_dev->vdev.dev), names[i]); + err = request_irq(vp_dev->msix_entries[msix_vec].vector, + vring_interrupt, 0, + vp_dev->msix_names[msix_vec], + vqs[i]); + if (err) { + vp_del_vq(vqs[i]); + goto error_find; } } return 0; @@ -537,28 +563,20 @@ static int vp_find_vqs(struct virtio_device *vdev, unsigned nvqs, vq_callback_t *callbacks[], const char *names[]) { - int vectors = 0; - int i, uninitialized_var(err); - - /* How many vectors would we like? */ - for (i = 0; i < nvqs; ++i) - if (callbacks[i]) - ++vectors; + int err; - /* We want at most one vector per queue and one for config changes. */ - err = vp_try_to_find_vqs(vdev, nvqs, vqs, callbacks, names, - vectors + 1, true); + /* Try MSI-X with one vector per queue. */ + err = vp_try_to_find_vqs(vdev, nvqs, vqs, callbacks, names, true, true); if (!err) return 0; - /* Fallback to separate vectors for config and a shared for queues. */ + /* Fallback: MSI-X with one vector for config, one shared for queues. */ err = vp_try_to_find_vqs(vdev, nvqs, vqs, callbacks, names, - 2, false); + true, false); if (!err) return 0; /* Finally fall back to regular interrupts. */ - err = vp_try_to_find_vqs(vdev, nvqs, vqs, callbacks, names, - 0, false); - return err; + return vp_try_to_find_vqs(vdev, nvqs, vqs, callbacks, names, + false, false); } static struct virtio_config_ops virtio_pci_config_ops = { diff --git a/drivers/virtio/virtio_ring.c b/drivers/virtio/virtio_ring.c index a882f2606515..fbd2ecde93e4 100644 --- a/drivers/virtio/virtio_ring.c +++ b/drivers/virtio/virtio_ring.c @@ -208,7 +208,11 @@ add_head: pr_debug("Added buffer head %i to %p\n", head, vq); END_USE(vq); - return 0; + + /* If we're indirect, we can fit many (assuming not OOM). */ + if (vq->indirect) + return vq->num_free ? vq->vring.num : 0; + return vq->num_free; } static void vring_kick(struct virtqueue *_vq) @@ -281,6 +285,9 @@ static void *vring_get_buf(struct virtqueue *_vq, unsigned int *len) return NULL; } + /* Only get used array entries after they have been exposed by host. */ + rmb(); + i = vq->vring.used->ring[vq->last_used_idx%vq->vring.num].id; *len = vq->vring.used->ring[vq->last_used_idx%vq->vring.num].len; diff --git a/drivers/vlynq/vlynq.c b/drivers/vlynq/vlynq.c index f05d2a368367..9554ad5f9af7 100644 --- a/drivers/vlynq/vlynq.c +++ b/drivers/vlynq/vlynq.c @@ -28,7 +28,6 @@ #include <linux/errno.h> #include <linux/platform_device.h> #include <linux/interrupt.h> -#include <linux/device.h> #include <linux/delay.h> #include <linux/io.h> @@ -703,7 +702,7 @@ static int vlynq_probe(struct platform_device *pdev) dev->mem_start = mem_res->start; dev->mem_end = mem_res->end; - len = regs_res->end - regs_res->start; + len = resource_size(regs_res); if (!request_mem_region(regs_res->start, len, dev_name(&dev->dev))) { printk(KERN_ERR "%s: Can't request vlynq registers\n", dev_name(&dev->dev)); diff --git a/drivers/w1/masters/ds2482.c b/drivers/w1/masters/ds2482.c index df52cb355f7d..406caa6a71cb 100644 --- a/drivers/w1/masters/ds2482.c +++ b/drivers/w1/masters/ds2482.c @@ -24,19 +24,6 @@ #include "../w1_int.h" /** - * Address is selected using 2 pins, resulting in 4 possible addresses. - * 0x18, 0x19, 0x1a, 0x1b - * However, the chip cannot be detected without doing an i2c write, - * so use the force module parameter. - */ -static const unsigned short normal_i2c[] = { I2C_CLIENT_END }; - -/** - * Insmod parameters - */ -I2C_CLIENT_INSMOD_1(ds2482); - -/** * The DS2482 registers - there are 3 registers that are addressed by a read * pointer. The read pointer is set by the last command executed. * @@ -96,8 +83,6 @@ static const u8 ds2482_chan_rd[8] = static int ds2482_probe(struct i2c_client *client, const struct i2c_device_id *id); -static int ds2482_detect(struct i2c_client *client, int kind, - struct i2c_board_info *info); static int ds2482_remove(struct i2c_client *client); @@ -117,8 +102,6 @@ static struct i2c_driver ds2482_driver = { .probe = ds2482_probe, .remove = ds2482_remove, .id_table = ds2482_id, - .detect = ds2482_detect, - .address_data = &addr_data, }; /* @@ -425,19 +408,6 @@ static u8 ds2482_w1_reset_bus(void *data) } -static int ds2482_detect(struct i2c_client *client, int kind, - struct i2c_board_info *info) -{ - if (!i2c_check_functionality(client->adapter, - I2C_FUNC_SMBUS_WRITE_BYTE_DATA | - I2C_FUNC_SMBUS_BYTE)) - return -ENODEV; - - strlcpy(info->type, "ds2482", I2C_NAME_SIZE); - - return 0; -} - static int ds2482_probe(struct i2c_client *client, const struct i2c_device_id *id) { @@ -446,6 +416,11 @@ static int ds2482_probe(struct i2c_client *client, int temp1; int idx; + if (!i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_WRITE_BYTE_DATA | + I2C_FUNC_SMBUS_BYTE)) + return -ENODEV; + if (!(data = kzalloc(sizeof(struct ds2482_data), GFP_KERNEL))) { err = -ENOMEM; goto exit; diff --git a/drivers/w1/w1_netlink.c b/drivers/w1/w1_netlink.c index 52ccb3d3a963..45c126fea31d 100644 --- a/drivers/w1/w1_netlink.c +++ b/drivers/w1/w1_netlink.c @@ -306,7 +306,7 @@ static int w1_netlink_send_error(struct cn_msg *rcmsg, struct w1_netlink_msg *rm return error; } -static void w1_cn_callback(struct cn_msg *msg) +static void w1_cn_callback(struct cn_msg *msg, struct netlink_skb_parms *nsp) { struct w1_netlink_msg *m = (struct w1_netlink_msg *)(msg + 1); struct w1_netlink_cmd *cmd; diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index ff3eb8ff6bd7..3711b888d482 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -282,6 +282,13 @@ config NUC900_WATCHDOG To compile this driver as a module, choose M here: the module will be called nuc900_wdt. +config ADX_WATCHDOG + tristate "Avionic Design Xanthos watchdog" + depends on ARCH_PXA_ADX + help + Say Y here if you want support for the watchdog timer on Avionic + Design Xanthos boards. + # AVR32 Architecture config AT32AP700X_WDT diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile index 348b3b862c99..699199b1baa6 100644 --- a/drivers/watchdog/Makefile +++ b/drivers/watchdog/Makefile @@ -45,6 +45,7 @@ obj-$(CONFIG_ORION_WATCHDOG) += orion_wdt.o obj-$(CONFIG_COH901327_WATCHDOG) += coh901327_wdt.o obj-$(CONFIG_STMP3XXX_WATCHDOG) += stmp3xxx_wdt.o obj-$(CONFIG_NUC900_WATCHDOG) += nuc900_wdt.o +obj-$(CONFIG_ADX_WATCHDOG) += adx_wdt.o # AVR32 Architecture obj-$(CONFIG_AT32AP700X_WDT) += at32ap700x_wdt.o diff --git a/drivers/watchdog/adx_wdt.c b/drivers/watchdog/adx_wdt.c new file mode 100644 index 000000000000..77afb0acc500 --- /dev/null +++ b/drivers/watchdog/adx_wdt.c @@ -0,0 +1,354 @@ +/* + * Copyright (C) 2008-2009 Avionic Design GmbH + * + * 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/fs.h> +#include <linux/io.h> +#include <linux/miscdevice.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/types.h> +#include <linux/uaccess.h> +#include <linux/watchdog.h> + +#define WATCHDOG_NAME "adx-wdt" + +/* register offsets */ +#define ADX_WDT_CONTROL 0x00 +#define ADX_WDT_CONTROL_ENABLE (1 << 0) +#define ADX_WDT_CONTROL_nRESET (1 << 1) +#define ADX_WDT_TIMEOUT 0x08 + +static struct platform_device *adx_wdt_dev; +static unsigned long driver_open; + +#define WDT_STATE_STOP 0 +#define WDT_STATE_START 1 + +struct adx_wdt { + void __iomem *base; + unsigned long timeout; + unsigned int state; + unsigned int wake; + spinlock_t lock; +}; + +static struct watchdog_info adx_wdt_info = { + .identity = "Avionic Design Xanthos Watchdog", + .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING, +}; + +static void adx_wdt_start_locked(struct adx_wdt *wdt) +{ + u32 ctrl; + + ctrl = readl(wdt->base + ADX_WDT_CONTROL); + ctrl |= ADX_WDT_CONTROL_ENABLE; + writel(ctrl, wdt->base + ADX_WDT_CONTROL); + wdt->state = WDT_STATE_START; +} + +static void adx_wdt_start(struct adx_wdt *wdt) +{ + unsigned long flags; + + spin_lock_irqsave(&wdt->lock, flags); + adx_wdt_start_locked(wdt); + spin_unlock_irqrestore(&wdt->lock, flags); +} + +static void adx_wdt_stop_locked(struct adx_wdt *wdt) +{ + u32 ctrl; + + ctrl = readl(wdt->base + ADX_WDT_CONTROL); + ctrl &= ~ADX_WDT_CONTROL_ENABLE; + writel(ctrl, wdt->base + ADX_WDT_CONTROL); + wdt->state = WDT_STATE_STOP; +} + +static void adx_wdt_stop(struct adx_wdt *wdt) +{ + unsigned long flags; + + spin_lock_irqsave(&wdt->lock, flags); + adx_wdt_stop_locked(wdt); + spin_unlock_irqrestore(&wdt->lock, flags); +} + +static void adx_wdt_set_timeout(struct adx_wdt *wdt, unsigned long seconds) +{ + unsigned long timeout = seconds * 1000; + unsigned long flags; + unsigned int state; + + spin_lock_irqsave(&wdt->lock, flags); + state = wdt->state; + adx_wdt_stop_locked(wdt); + writel(timeout, wdt->base + ADX_WDT_TIMEOUT); + + if (state == WDT_STATE_START) + adx_wdt_start_locked(wdt); + + wdt->timeout = timeout; + spin_unlock_irqrestore(&wdt->lock, flags); +} + +static void adx_wdt_get_timeout(struct adx_wdt *wdt, unsigned long *seconds) +{ + *seconds = wdt->timeout / 1000; +} + +static void adx_wdt_keepalive(struct adx_wdt *wdt) +{ + unsigned long flags; + + spin_lock_irqsave(&wdt->lock, flags); + writel(wdt->timeout, wdt->base + ADX_WDT_TIMEOUT); + spin_unlock_irqrestore(&wdt->lock, flags); +} + +static int adx_wdt_open(struct inode *inode, struct file *file) +{ + struct adx_wdt *wdt = platform_get_drvdata(adx_wdt_dev); + + if (test_and_set_bit(0, &driver_open)) + return -EBUSY; + + file->private_data = wdt; + adx_wdt_set_timeout(wdt, 30); + adx_wdt_start(wdt); + + return nonseekable_open(inode, file); +} + +static int adx_wdt_release(struct inode *inode, struct file *file) +{ + struct adx_wdt *wdt = file->private_data; + + adx_wdt_stop(wdt); + clear_bit(0, &driver_open); + + return 0; +} + +static long adx_wdt_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + struct adx_wdt *wdt = file->private_data; + void __user *argp = (void __user *)arg; + unsigned long __user *p = argp; + unsigned long seconds = 0; + unsigned int options; + long ret = -EINVAL; + + switch (cmd) { + case WDIOC_GETSUPPORT: + if (copy_to_user(argp, &adx_wdt_info, sizeof(adx_wdt_info))) + return -EFAULT; + else + return 0; + + case WDIOC_GETSTATUS: + case WDIOC_GETBOOTSTATUS: + return put_user(0, p); + + case WDIOC_KEEPALIVE: + adx_wdt_keepalive(wdt); + return 0; + + case WDIOC_SETTIMEOUT: + if (get_user(seconds, p)) + return -EFAULT; + + adx_wdt_set_timeout(wdt, seconds); + + /* fallthrough */ + case WDIOC_GETTIMEOUT: + adx_wdt_get_timeout(wdt, &seconds); + return put_user(seconds, p); + + case WDIOC_SETOPTIONS: + if (copy_from_user(&options, argp, sizeof(options))) + return -EFAULT; + + if (options & WDIOS_DISABLECARD) { + adx_wdt_stop(wdt); + ret = 0; + } + + if (options & WDIOS_ENABLECARD) { + adx_wdt_start(wdt); + ret = 0; + } + + return ret; + + default: + break; + } + + return -ENOTTY; +} + +static ssize_t adx_wdt_write(struct file *file, const char __user *data, + size_t len, loff_t *ppos) +{ + struct adx_wdt *wdt = file->private_data; + + if (len) + adx_wdt_keepalive(wdt); + + return len; +} + +static const struct file_operations adx_wdt_fops = { + .owner = THIS_MODULE, + .llseek = no_llseek, + .open = adx_wdt_open, + .release = adx_wdt_release, + .unlocked_ioctl = adx_wdt_ioctl, + .write = adx_wdt_write, +}; + +static struct miscdevice adx_wdt_miscdev = { + .minor = WATCHDOG_MINOR, + .name = "watchdog", + .fops = &adx_wdt_fops, +}; + +static int __devinit adx_wdt_probe(struct platform_device *pdev) +{ + struct resource *res; + struct adx_wdt *wdt; + int ret = 0; + u32 ctrl; + + wdt = devm_kzalloc(&pdev->dev, sizeof(*wdt), GFP_KERNEL); + if (!wdt) { + dev_err(&pdev->dev, "cannot allocate WDT structure\n"); + return -ENOMEM; + } + + spin_lock_init(&wdt->lock); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "cannot obtain I/O memory region\n"); + return -ENXIO; + } + + res = devm_request_mem_region(&pdev->dev, res->start, + res->end - res->start + 1, res->name); + if (!res) { + dev_err(&pdev->dev, "cannot request I/O memory region\n"); + return -ENXIO; + } + + wdt->base = devm_ioremap_nocache(&pdev->dev, res->start, + res->end - res->start + 1); + if (!wdt->base) { + dev_err(&pdev->dev, "cannot remap I/O memory region\n"); + return -ENXIO; + } + + /* disable watchdog and reboot on timeout */ + ctrl = readl(wdt->base + ADX_WDT_CONTROL); + ctrl &= ~ADX_WDT_CONTROL_ENABLE; + ctrl &= ~ADX_WDT_CONTROL_nRESET; + writel(ctrl, wdt->base + ADX_WDT_CONTROL); + + platform_set_drvdata(pdev, wdt); + adx_wdt_dev = pdev; + + ret = misc_register(&adx_wdt_miscdev); + if (ret) { + dev_err(&pdev->dev, "cannot register miscdev on minor %d " + "(err=%d)\n", WATCHDOG_MINOR, ret); + return ret; + } + + return 0; +} + +static int __devexit adx_wdt_remove(struct platform_device *pdev) +{ + struct adx_wdt *wdt = platform_get_drvdata(pdev); + + misc_deregister(&adx_wdt_miscdev); + adx_wdt_stop(wdt); + platform_set_drvdata(pdev, NULL); + + return 0; +} + +static void adx_wdt_shutdown(struct platform_device *pdev) +{ + struct adx_wdt *wdt = platform_get_drvdata(pdev); + adx_wdt_stop(wdt); +} + +#ifdef CONFIG_PM +static int adx_wdt_suspend(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct adx_wdt *wdt = platform_get_drvdata(pdev); + + wdt->wake = (wdt->state == WDT_STATE_START) ? 1 : 0; + adx_wdt_stop(wdt); + + return 0; +} + +static int adx_wdt_resume(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct adx_wdt *wdt = platform_get_drvdata(pdev); + + if (wdt->wake) + adx_wdt_start(wdt); + + return 0; +} + +static struct dev_pm_ops adx_wdt_pm_ops = { + .suspend = adx_wdt_suspend, + .resume = adx_wdt_resume, +}; + +# define ADX_WDT_PM_OPS (&adx_wdt_pm_ops) +#else +# define ADX_WDT_PM_OPS NULL +#endif + +static struct platform_driver adx_wdt_driver = { + .probe = adx_wdt_probe, + .remove = __devexit_p(adx_wdt_remove), + .shutdown = adx_wdt_shutdown, + .driver = { + .name = WATCHDOG_NAME, + .owner = THIS_MODULE, + .pm = ADX_WDT_PM_OPS, + }, +}; + +static int __init adx_wdt_init(void) +{ + return platform_driver_register(&adx_wdt_driver); +} + +static void __exit adx_wdt_exit(void) +{ + platform_driver_unregister(&adx_wdt_driver); +} + +module_init(adx_wdt_init); +module_exit(adx_wdt_exit); + +MODULE_DESCRIPTION("Avionic Design Xanthos Watchdog Driver"); +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Thierry Reding <thierry.reding@avionic-design.de>"); +MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); diff --git a/drivers/watchdog/riowd.c b/drivers/watchdog/riowd.c index 1e8f02f440e6..d3c824dc2358 100644 --- a/drivers/watchdog/riowd.c +++ b/drivers/watchdog/riowd.c @@ -206,7 +206,7 @@ static int __devinit riowd_probe(struct of_device *op, dev_set_drvdata(&op->dev, p); riowd_device = p; - err = 0; + return 0; out_iounmap: of_iounmap(&op->resource[0], p->regs, 2); diff --git a/drivers/watchdog/sbc_fitpc2_wdt.c b/drivers/watchdog/sbc_fitpc2_wdt.c index 852ca1977917..91430a89107c 100644 --- a/drivers/watchdog/sbc_fitpc2_wdt.c +++ b/drivers/watchdog/sbc_fitpc2_wdt.c @@ -227,7 +227,7 @@ static int __init fitpc2_wdt_init(void) } err = misc_register(&fitpc2_wdt_miscdev); - if (!err) { + if (err) { pr_err("cannot register miscdev on minor=%d (err=%d)\n", WATCHDOG_MINOR, err); goto err_margin; diff --git a/drivers/xen/xenfs/xenbus.c b/drivers/xen/xenfs/xenbus.c index a9592d981b10..6c4269b836b7 100644 --- a/drivers/xen/xenfs/xenbus.c +++ b/drivers/xen/xenfs/xenbus.c @@ -43,6 +43,7 @@ #include <linux/fs.h> #include <linux/poll.h> #include <linux/mutex.h> +#include <linux/sched.h> #include <linux/spinlock.h> #include <linux/mount.h> #include <linux/pagemap.h> |